[
  {
    "path": ".buckconfig",
    "content": "[cells]\ngh_facebook_folly = .\nshim_DISABLED = shim\n\n[cell_aliases]\nroot = gh_facebook_folly\n\n[oss]\ninternal_cell = fbcode\nproject_dirs = folly\n\n[buck2]\ndirectories_to_allow_relative_paths = //folly\n"
  },
  {
    "path": ".buckconfig.d/common.buckconfig",
    "content": "[cells]\nprelude = prelude\nnone = none\n\n[cell_aliases]\nconfig = prelude\novr_config = prelude\nbazel_skylib = gh_facebook_buck2_shims_meta\nbuck = gh_facebook_buck2_shims_meta\nfbcode = gh_facebook_buck2_shims_meta\nfbcode_macros = gh_facebook_buck2_shims_meta\nfbsource = gh_facebook_buck2_shims_meta\nshim = gh_facebook_buck2_shims_meta\ntoolchains = gh_facebook_buck2_shims_meta\n\n[external_cells]\nprelude = bundled\n\n[build]\nexecution_platforms = prelude//platforms:default\n\n[parser]\ntarget_platform_detector_spec = target:root//...->prelude//platforms:default target:shim//...->prelude//platforms:default\n"
  },
  {
    "path": ".buckroot",
    "content": ""
  },
  {
    "path": ".devcontainer/Containerfile",
    "content": "FROM mcr.microsoft.com/devcontainers/base:jammy\n\nRUN apt-get update \\\n    && apt-get upgrade -y \\\n    && apt-get install -y --no-install-recommends \\\n        watchman \\\n    && apt-get clean \\\n    && rm -rf /var/lib/apt/lists/*\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n\t\"name\": \"Ubuntu\",\n\t\"build\": {\n\t\t\"cacheFrom\": \"ghcr.io/marinatedconcrete/config-devcontainer\",\n\t\t\"dockerfile\": \"Containerfile\"\n\t},\n\t\"customizations\": {\n\t\t\"vscode\": {\n\t\t\t\"extensions\": [\n\t\t\t\t\"GitHub.vscode-github-actions\"\n\t\t\t]\n\t\t}\n\t},\n\t\"features\": {\n\t\t\"ghcr.io/facebook/devcontainers/features/dotslash:1\": {}\n\t}\n}\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "---\nversion: 2\nupdates:\n - package-ecosystem: \"devcontainers\"\n   directory: \"/\"\n   schedule:\n     interval: weekly\n"
  },
  {
    "path": ".github/scripts/bad_targets",
    "content": "# This is a list of bad targets that do not build in OSS. Ideally this list goes away\n# with time.\nroot//folly/debugging/exception_tracer:exception_counter\nroot//folly/debugging/exception_tracer:exception_tracer\nroot//folly/debugging/exception_tracer:exception_tracer_callbacks\nroot//folly/debugging/exception_tracer:smart_exception_stack_trace_hooks\nroot//folly/debugging/exception_tracer:smart_exception_tracer\nroot//folly/debugging/symbolizer/tool:folly-addr2line\nroot//folly/debugging/symbolizer/tool:libFollySegFault.so\nroot//folly/docs/examples/folly:baton_demo\nroot//folly/docs/examples/folly:cancellation_callback_demo\nroot//folly/docs/examples/folly:cancellation_source_demo\nroot//folly/docs/examples/folly:cancellation_token_demo\nroot//folly/docs/examples/folly:dynamic_converter_demo\nroot//folly/docs/examples/folly:dynamic_demo\nroot//folly/docs/examples/folly:executor_guide\nroot//folly/docs/examples/folly:file_demo\nroot//folly/docs/examples/folly:format_demo\nroot//folly/docs/examples/folly:function_demo\nroot//folly/docs/examples/folly:ipaddress_demo\nroot//folly/docs/examples/folly:likely_demo\nroot//folly/docs/examples/folly:map_util_demo\nroot//folly/docs/examples/folly:scope_guard_demo\nroot//folly/docs/examples/folly:scope_guard2_demo\nroot//folly/docs/examples/folly:scoped_event_base_thread_demo\nroot//folly/docs/examples/folly:scoped_event_base_thread2_demo\nroot//folly/docs/examples/folly:synchronized_demo\nroot//folly/docs/examples/folly/container:array_demo\nroot//folly/docs/examples/folly/dynamic:array_demo\nroot//folly/docs/examples/folly/dynamic:object_demo\nroot//folly/docs/examples/folly/coro:async_scope_demo\nroot//folly/docs/examples/folly/coro:cancellable_async_scope_demo\nroot//folly/docs/examples/folly/coro:detach_on_cancel_demo\nroot//folly/docs/examples/folly/coro:promise_demo\nroot//folly/docs/examples/folly/coro:retry_demo\nroot//folly/docs/examples/folly/coro:task_demo\nroot//folly/docs/examples/folly/coro:with_cancellation_demo\nroot//folly/docs/examples/folly/hash:hash_demo\nroot//folly/docs/examples/folly/io:i_o_buf_demo\nroot//folly/logging/example:example\nroot//folly/python:executor_lib\nroot//folly/python:fibers_lib\nroot//folly/python:iobuf_lib\nroot//folly/tool:benchmark_compare\n"
  },
  {
    "path": ".github/scripts/buck_build_and_test.sh",
    "content": "#!/bin/bash\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nTARGETS_FILE=$(mktemp)\n./buck2 targets //... | grep -F -v -f .github/scripts/bad_targets | grep -v test >\"$TARGETS_FILE\"\n\n./buck2 build @\"$TARGETS_FILE\"\n# ./buck2 test @\"$TARGETS_FILE\"\n"
  },
  {
    "path": ".github/workflows/TagIt.yml",
    "content": "on:\n  push:\n    tags:\n      # Only match TagIt tags, which always start with this prefix\n      - 'v20*'\n\nname: TagIt\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    permissions:\n      contents: write  # for actions/create-release to create a release\n    name: Release\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n      - name: Archive project\n        id: archive_project\n        run: |\n          FILE_NAME=${GITHUB_REPOSITORY#*/}-${GITHUB_REF##*/}\n          git archive ${{ github.ref }} -o ${FILE_NAME}.zip\n          git archive ${{ github.ref }} -o ${FILE_NAME}.tar.gz\n          echo \"file_name=${FILE_NAME}\" >> $GITHUB_OUTPUT\n      - name: Compute digests\n        id: compute_digests\n        run: |\n          echo \"tgz_256=$(openssl dgst -sha256 ${{ steps.archive_project.outputs.file_name }}.tar.gz)\" >> $GITHUB_OUTPUT\n          echo \"tgz_512=$(openssl dgst -sha512 ${{ steps.archive_project.outputs.file_name }}.tar.gz)\" >> $GITHUB_OUTPUT\n          echo \"zip_256=$(openssl dgst -sha256 ${{ steps.archive_project.outputs.file_name }}.zip)\" >> $GITHUB_OUTPUT\n          echo \"zip_512=$(openssl dgst -sha512 ${{ steps.archive_project.outputs.file_name }}.zip)\" >> $GITHUB_OUTPUT\n      - name: Create Release\n        id: create_release\n        uses: actions/create-release@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ github.ref }}\n          release_name: ${{ github.ref }}\n          body: |\n            Automated release from TagIt\n            <details>\n              <summary>File Hashes</summary>\n              <ul>\n                <li>${{ steps.compute_digests.outputs.zip_256 }}</li>\n                <li>${{ steps.compute_digests.outputs.zip_512 }}</li>\n                <li>${{ steps.compute_digests.outputs.tgz_256 }}</li>\n                <li>${{ steps.compute_digests.outputs.tgz_512 }}</li>\n              </ul>\n            </details>\n          draft: false\n          prerelease: false\n      - name: Upload zip\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_release.outputs.upload_url }}\n          asset_path: ./${{ steps.archive_project.outputs.file_name }}.zip\n          asset_name: ${{ steps.archive_project.outputs.file_name }}.zip\n          asset_content_type: application/zip\n      - name: Upload tar.gz\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_release.outputs.upload_url }}\n          asset_path: ./${{ steps.archive_project.outputs.file_name }}.tar.gz\n          asset_name: ${{ steps.archive_project.outputs.file_name }}.tar.gz\n          asset_content_type: application/gzip\n"
  },
  {
    "path": ".github/workflows/devcontainer.yml",
    "content": "---\nname: Development Container-Based CI\non:\n  pull_request:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\n\njobs:\n  build:\n    permissions:\n      contents: read\n      packages: write\n    runs-on: ${{ matrix.runsOn }}\n    strategy:\n      matrix:\n        runsOn:\n          - ubuntu-24.04-arm\n          - ubuntu-latest\n    steps:\n      - name: Checkout Repo\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      - name: Login to GitHub Container Registry\n        uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.repository_owner }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - name: Set up Buildx\n        uses: docker/setup-buildx-action@v3\n        with:\n          driver: docker-container\n      - if: ${{ github.event_name != 'pull_request' }}\n        id: cache_to_helper\n        run: echo \"cacheTo=ghcr.io/${{ github.repository_owner }}/devcontainers/folly\" >> $GITHUB_OUTPUT\n      - name: Build devcontainer image\n        uses: devcontainers/ci@8bf61b26e9c3a98f69cb6ce2f88d24ff59b785c6 # v0.3.1900000417\n        with:\n          cacheFrom: ghcr.io/${{ github.repository_owner }}/devcontainers/folly\n          cacheTo: ${{ steps.cache_to_helper.outputs.cacheTo }}\n          env: |\n            CI=1\n          imageName: ghcr.io/${{ github.repository_owner }}/devcontainers/folly\n          push: filter\n          refFilterForPush: refs/heads/main\n"
  },
  {
    "path": ".github/workflows/getdeps_linux.yml",
    "content": "# This file was @generated by getdeps.py\n\nname: linux\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\npermissions:\n  contents: read  #  to fetch code (actions/checkout)\n\njobs:\n  build:\n    runs-on: ubuntu-22.04\n    steps:\n    - uses: actions/checkout@v6\n    - name: Show disk space at start\n      run: df -h\n    - name: Free up disk space\n      run: sudo rm -rf /usr/local/lib/android\n    - name: Show disk space after freeing up\n      run: df -h\n    - name: Update system package info\n      run: sudo --preserve-env=http_proxy apt-get update\n    - name: Install system deps\n      run: sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive folly && sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive patchelf\n    - id: paths\n      name: Query paths\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages query-paths --recursive --src-dir=. folly  >> \"$GITHUB_OUTPUT\"\n    - name: Fetch boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests boost\n    - name: Fetch libaio\n      if: ${{ steps.paths.outputs.libaio_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libaio\n    - name: Fetch ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests ninja\n    - name: Fetch cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests cmake\n    - name: Fetch double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests double-conversion\n    - name: Fetch fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fast_float\n    - name: Fetch fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fmt\n    - name: Fetch gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests gflags\n    - name: Fetch glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests glog\n    - name: Fetch googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests googletest\n    - name: Fetch libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libdwarf\n    - name: Fetch libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libevent\n    - name: Fetch zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zlib\n    - name: Fetch lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests lz4\n    - name: Fetch snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests snappy\n    - name: Fetch zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zstd\n    - name: Fetch autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests autoconf\n    - name: Fetch automake\n      if: ${{ steps.paths.outputs.automake_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests automake\n    - name: Fetch libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libtool\n    - name: Fetch libiberty\n      if: ${{ steps.paths.outputs.libiberty_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libiberty\n    - name: Fetch libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libsodium\n    - name: Fetch libunwind\n      if: ${{ steps.paths.outputs.libunwind_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libunwind\n    - name: Fetch xz\n      if: ${{ steps.paths.outputs.xz_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests xz\n    - name: Restore boost from cache\n      id: restore_boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Build boost\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests boost\n    - name: Save boost to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Restore libaio from cache\n      id: restore_libaio\n      if: ${{ steps.paths.outputs.libaio_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libaio_INSTALL }}\n       key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install\n    - name: Build libaio\n      if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libaio\n    - name: Save libaio to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libaio_INSTALL }}\n       key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install\n    - name: Restore ninja from cache\n      id: restore_ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Build ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests ninja\n    - name: Save ninja to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Restore cmake from cache\n      id: restore_cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Build cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests cmake\n    - name: Save cmake to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Restore double-conversion from cache\n      id: restore_double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Build double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests double-conversion\n    - name: Save double-conversion to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Restore fast_float from cache\n      id: restore_fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Build fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fast_float\n    - name: Save fast_float to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Restore fmt from cache\n      id: restore_fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Build fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fmt\n    - name: Save fmt to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Restore gflags from cache\n      id: restore_gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Build gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests gflags\n    - name: Save gflags to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Restore glog from cache\n      id: restore_glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Build glog\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests glog\n    - name: Save glog to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Restore googletest from cache\n      id: restore_googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Build googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests googletest\n    - name: Save googletest to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Restore libdwarf from cache\n      id: restore_libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Build libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libdwarf\n    - name: Save libdwarf to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Restore libevent from cache\n      id: restore_libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Build libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libevent\n    - name: Save libevent to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Restore zlib from cache\n      id: restore_zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.zlib_INSTALL }}\n       key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install\n    - name: Build zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests zlib\n    - name: Save zlib to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.zlib_INSTALL }}\n       key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install\n    - name: Restore lz4 from cache\n      id: restore_lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Build lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests lz4\n    - name: Save lz4 to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Restore snappy from cache\n      id: restore_snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Build snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests snappy\n    - name: Save snappy to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Restore zstd from cache\n      id: restore_zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Build zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests zstd\n    - name: Save zstd to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Restore autoconf from cache\n      id: restore_autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.autoconf_INSTALL }}\n       key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install\n    - name: Build autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests autoconf\n    - name: Save autoconf to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.autoconf_INSTALL }}\n       key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install\n    - name: Restore automake from cache\n      id: restore_automake\n      if: ${{ steps.paths.outputs.automake_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.automake_INSTALL }}\n       key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install\n    - name: Build automake\n      if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests automake\n    - name: Save automake to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.automake_INSTALL }}\n       key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install\n    - name: Restore libtool from cache\n      id: restore_libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libtool_INSTALL }}\n       key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install\n    - name: Build libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libtool\n    - name: Save libtool to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libtool_INSTALL }}\n       key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install\n    - name: Restore libiberty from cache\n      id: restore_libiberty\n      if: ${{ steps.paths.outputs.libiberty_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libiberty_INSTALL }}\n       key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install\n    - name: Build libiberty\n      if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libiberty\n    - name: Save libiberty to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libiberty_INSTALL }}\n       key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install\n    - name: Restore libsodium from cache\n      id: restore_libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Build libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libsodium\n    - name: Save libsodium to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Restore libunwind from cache\n      id: restore_libunwind\n      if: ${{ steps.paths.outputs.libunwind_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libunwind_INSTALL }}\n       key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install\n    - name: Build libunwind\n      if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libunwind\n    - name: Save libunwind to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libunwind_INSTALL }}\n       key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install\n    - name: Restore xz from cache\n      id: restore_xz\n      if: ${{ steps.paths.outputs.xz_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.xz_INSTALL }}\n       key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install\n    - name: Build xz\n      if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests xz\n    - name: Save xz to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.xz_INSTALL }}\n       key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install\n    - name: Build folly\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --src-dir=. folly --project-install-prefix folly:/usr/local\n    - name: Copy artifacts\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fixup-dyn-deps --strip --src-dir=. folly _artifacts/linux --project-install-prefix folly:/usr/local --final-install-prefix /usr/local\n    - uses: actions/upload-artifact@v6\n      with:\n        name: folly\n        path: _artifacts\n    - name: Test folly\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages test --src-dir=. folly --project-install-prefix folly:/usr/local\n    - name: Show disk space at end\n      if: always()\n      run: df -h\n"
  },
  {
    "path": ".github/workflows/getdeps_mac.yml",
    "content": "# This file was @generated by getdeps.py\n\nname: mac\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\npermissions:\n  contents: read  #  to fetch code (actions/checkout)\n\njobs:\n  build:\n    runs-on: macOS-latest\n    steps:\n    - uses: actions/checkout@v6\n    - name: Show disk space at start\n      run: df -h\n    - name: Free up disk space\n      run: sudo rm -rf /usr/local/lib/android\n    - name: Show disk space after freeing up\n      run: df -h\n    - name: Install system deps\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive folly\n    - id: paths\n      name: Query paths\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages query-paths --recursive --src-dir=. folly  >> \"$GITHUB_OUTPUT\"\n    - name: Fetch boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests boost\n    - name: Fetch openssl\n      if: ${{ steps.paths.outputs.openssl_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests openssl\n    - name: Fetch ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests ninja\n    - name: Fetch cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests cmake\n    - name: Fetch double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests double-conversion\n    - name: Fetch fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fast_float\n    - name: Fetch fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fmt\n    - name: Fetch gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests gflags\n    - name: Fetch glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests glog\n    - name: Fetch googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests googletest\n    - name: Fetch libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libdwarf\n    - name: Fetch libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libevent\n    - name: Fetch lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests lz4\n    - name: Fetch snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests snappy\n    - name: Fetch zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zstd\n    - name: Fetch autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests autoconf\n    - name: Fetch automake\n      if: ${{ steps.paths.outputs.automake_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests automake\n    - name: Fetch libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libtool\n    - name: Fetch libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libsodium\n    - name: Fetch xz\n      if: ${{ steps.paths.outputs.xz_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests xz\n    - name: Restore boost from cache\n      id: restore_boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Build boost\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests boost\n    - name: Save boost to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Restore openssl from cache\n      id: restore_openssl\n      if: ${{ steps.paths.outputs.openssl_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.openssl_INSTALL }}\n       key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install\n    - name: Build openssl\n      if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests openssl\n    - name: Save openssl to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.openssl_INSTALL }}\n       key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install\n    - name: Restore ninja from cache\n      id: restore_ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Build ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests ninja\n    - name: Save ninja to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Restore cmake from cache\n      id: restore_cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Build cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests cmake\n    - name: Save cmake to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Restore double-conversion from cache\n      id: restore_double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Build double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests double-conversion\n    - name: Save double-conversion to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Restore fast_float from cache\n      id: restore_fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Build fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fast_float\n    - name: Save fast_float to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Restore fmt from cache\n      id: restore_fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Build fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fmt\n    - name: Save fmt to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Restore gflags from cache\n      id: restore_gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Build gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests gflags\n    - name: Save gflags to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Restore glog from cache\n      id: restore_glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Build glog\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests glog\n    - name: Save glog to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Restore googletest from cache\n      id: restore_googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Build googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests googletest\n    - name: Save googletest to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Restore libdwarf from cache\n      id: restore_libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Build libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libdwarf\n    - name: Save libdwarf to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Restore libevent from cache\n      id: restore_libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Build libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libevent\n    - name: Save libevent to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Restore lz4 from cache\n      id: restore_lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Build lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests lz4\n    - name: Save lz4 to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Restore snappy from cache\n      id: restore_snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Build snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests snappy\n    - name: Save snappy to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Restore zstd from cache\n      id: restore_zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Build zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests zstd\n    - name: Save zstd to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Restore autoconf from cache\n      id: restore_autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.autoconf_INSTALL }}\n       key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install\n    - name: Build autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests autoconf\n    - name: Save autoconf to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.autoconf_INSTALL }}\n       key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install\n    - name: Restore automake from cache\n      id: restore_automake\n      if: ${{ steps.paths.outputs.automake_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.automake_INSTALL }}\n       key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install\n    - name: Build automake\n      if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests automake\n    - name: Save automake to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.automake_INSTALL }}\n       key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install\n    - name: Restore libtool from cache\n      id: restore_libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libtool_INSTALL }}\n       key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install\n    - name: Build libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libtool\n    - name: Save libtool to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libtool_INSTALL }}\n       key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install\n    - name: Restore libsodium from cache\n      id: restore_libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Build libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libsodium\n    - name: Save libsodium to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Restore xz from cache\n      id: restore_xz\n      if: ${{ steps.paths.outputs.xz_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.xz_INSTALL }}\n       key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install\n    - name: Build xz\n      if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests xz\n    - name: Save xz to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.xz_INSTALL }}\n       key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install\n    - name: Build folly\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --src-dir=. folly --project-install-prefix folly:/usr/local\n    - name: Copy artifacts\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fixup-dyn-deps --src-dir=. folly _artifacts/mac --project-install-prefix folly:/usr/local --final-install-prefix /usr/local\n    - uses: actions/upload-artifact@v6\n      with:\n        name: folly\n        path: _artifacts\n    - name: Test folly\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages test --src-dir=. folly --project-install-prefix folly:/usr/local\n    - name: Show disk space at end\n      if: always()\n      run: df -h\n"
  },
  {
    "path": ".github/workflows/getdeps_shared-libs_linux.yml",
    "content": "# This file was @generated by getdeps.py\n\nname: Shared Libs Linux\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\npermissions:\n  contents: read  #  to fetch code (actions/checkout)\n\njobs:\n  build:\n    runs-on: ubuntu-22.04\n    steps:\n    - uses: actions/checkout@v6\n    - name: Show disk space at start\n      run: df -h\n    - name: Free up disk space\n      run: sudo rm -rf /usr/local/lib/android\n    - name: Show disk space after freeing up\n      run: df -h\n    - name: Update system package info\n      run: sudo --preserve-env=http_proxy apt-get update\n    - name: Install system deps\n      run: sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive folly && sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive patchelf\n    - id: paths\n      name: Query paths\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages query-paths --recursive --src-dir=. folly  >> \"$GITHUB_OUTPUT\"\n    - name: Fetch boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests boost\n    - name: Fetch libaio\n      if: ${{ steps.paths.outputs.libaio_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libaio\n    - name: Fetch ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests ninja\n    - name: Fetch cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests cmake\n    - name: Fetch double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests double-conversion\n    - name: Fetch fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fast_float\n    - name: Fetch fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fmt\n    - name: Fetch gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests gflags\n    - name: Fetch glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests glog\n    - name: Fetch googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests googletest\n    - name: Fetch libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libdwarf\n    - name: Fetch libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libevent\n    - name: Fetch zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zlib\n    - name: Fetch lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests lz4\n    - name: Fetch snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests snappy\n    - name: Fetch zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zstd\n    - name: Fetch autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests autoconf\n    - name: Fetch automake\n      if: ${{ steps.paths.outputs.automake_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests automake\n    - name: Fetch libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libtool\n    - name: Fetch libiberty\n      if: ${{ steps.paths.outputs.libiberty_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libiberty\n    - name: Fetch libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libsodium\n    - name: Fetch libunwind\n      if: ${{ steps.paths.outputs.libunwind_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libunwind\n    - name: Fetch xz\n      if: ${{ steps.paths.outputs.xz_SOURCE }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests xz\n    - name: Restore boost from cache\n      id: restore_boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Build boost\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests boost\n    - name: Save boost to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Restore libaio from cache\n      id: restore_libaio\n      if: ${{ steps.paths.outputs.libaio_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libaio_INSTALL }}\n       key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install\n    - name: Build libaio\n      if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libaio\n    - name: Save libaio to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libaio_INSTALL }}\n       key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install\n    - name: Restore ninja from cache\n      id: restore_ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Build ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests ninja\n    - name: Save ninja to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Restore cmake from cache\n      id: restore_cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Build cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests cmake\n    - name: Save cmake to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Restore double-conversion from cache\n      id: restore_double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Build double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests double-conversion\n    - name: Save double-conversion to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Restore fast_float from cache\n      id: restore_fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Build fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests fast_float\n    - name: Save fast_float to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Restore fmt from cache\n      id: restore_fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Build fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests fmt\n    - name: Save fmt to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Restore gflags from cache\n      id: restore_gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Build gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests gflags\n    - name: Save gflags to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Restore glog from cache\n      id: restore_glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Build glog\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests glog\n    - name: Save glog to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Restore googletest from cache\n      id: restore_googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Build googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests googletest\n    - name: Save googletest to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Restore libdwarf from cache\n      id: restore_libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Build libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libdwarf\n    - name: Save libdwarf to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Restore libevent from cache\n      id: restore_libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Build libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libevent\n    - name: Save libevent to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Restore zlib from cache\n      id: restore_zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.zlib_INSTALL }}\n       key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install\n    - name: Build zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests zlib\n    - name: Save zlib to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.zlib_INSTALL }}\n       key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install\n    - name: Restore lz4 from cache\n      id: restore_lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Build lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests lz4\n    - name: Save lz4 to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Restore snappy from cache\n      id: restore_snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Build snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests snappy\n    - name: Save snappy to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Restore zstd from cache\n      id: restore_zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Build zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests zstd\n    - name: Save zstd to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Restore autoconf from cache\n      id: restore_autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.autoconf_INSTALL }}\n       key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install\n    - name: Build autoconf\n      if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests autoconf\n    - name: Save autoconf to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.autoconf_INSTALL }}\n       key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install\n    - name: Restore automake from cache\n      id: restore_automake\n      if: ${{ steps.paths.outputs.automake_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.automake_INSTALL }}\n       key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install\n    - name: Build automake\n      if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests automake\n    - name: Save automake to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.automake_INSTALL }}\n       key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install\n    - name: Restore libtool from cache\n      id: restore_libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libtool_INSTALL }}\n       key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install\n    - name: Build libtool\n      if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libtool\n    - name: Save libtool to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libtool_INSTALL }}\n       key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install\n    - name: Restore libiberty from cache\n      id: restore_libiberty\n      if: ${{ steps.paths.outputs.libiberty_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libiberty_INSTALL }}\n       key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install\n    - name: Build libiberty\n      if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libiberty\n    - name: Save libiberty to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libiberty_INSTALL }}\n       key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install\n    - name: Restore libsodium from cache\n      id: restore_libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Build libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libsodium\n    - name: Save libsodium to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Restore libunwind from cache\n      id: restore_libunwind\n      if: ${{ steps.paths.outputs.libunwind_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libunwind_INSTALL }}\n       key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install\n    - name: Build libunwind\n      if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libunwind\n    - name: Save libunwind to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libunwind_INSTALL }}\n       key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install\n    - name: Restore xz from cache\n      id: restore_xz\n      if: ${{ steps.paths.outputs.xz_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.xz_INSTALL }}\n       key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install\n    - name: Build xz\n      if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests xz\n    - name: Save xz to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.xz_INSTALL }}\n       key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install\n    - name: Build folly\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --src-dir=. folly --project-install-prefix folly:/usr/local\n    - name: Copy artifacts\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fixup-dyn-deps --strip --src-dir=. folly _artifacts/linux --project-install-prefix folly:/usr/local --final-install-prefix /usr/local\n    - uses: actions/upload-artifact@v6\n      with:\n        name: folly\n        path: _artifacts\n    - name: Test folly\n      run: python3 build/fbcode_builder/getdeps.py --allow-system-packages test --shared-libs --src-dir=. folly --project-install-prefix folly:/usr/local\n    - name: Show disk space at end\n      if: always()\n      run: df -h\n"
  },
  {
    "path": ".github/workflows/getdeps_windows.yml",
    "content": "# This file was @generated by getdeps.py\n\nname: windows\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\npermissions:\n  contents: read  #  to fetch code (actions/checkout)\n\njobs:\n  build:\n    runs-on: windows-2022\n    steps:\n    - name: Export boost environment\n      run: \"echo BOOST_ROOT=%BOOST_ROOT_1_83_0% >> %GITHUB_ENV%\"\n      shell: cmd\n    - name: Fix Git config\n      run: >\n        git config --system core.longpaths true &&\n        git config --system core.autocrlf false &&\n        git config --system core.symlinks true\n      shell: cmd\n    - uses: actions/checkout@v6\n    - id: paths\n      name: Query paths\n      run: python build/fbcode_builder/getdeps.py query-paths --recursive --src-dir=. folly  >> $env:GITHUB_OUTPUT\n      shell: pwsh\n    - name: Fetch boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests boost\n    - name: Fetch libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests libsodium\n    - name: Fetch ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests ninja\n    - name: Fetch cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests cmake\n    - name: Fetch double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests double-conversion\n    - name: Fetch fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests fast_float\n    - name: Fetch fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests fmt\n    - name: Fetch gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests gflags\n    - name: Fetch glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests glog\n    - name: Fetch googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests googletest\n    - name: Fetch libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests libdwarf\n    - name: Fetch lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests lz4\n    - name: Fetch jom\n      if: ${{ steps.paths.outputs.jom_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests jom\n    - name: Fetch perl\n      if: ${{ steps.paths.outputs.perl_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests perl\n    - name: Fetch openssl\n      if: ${{ steps.paths.outputs.openssl_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests openssl\n    - name: Fetch snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests snappy\n    - name: Fetch zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests zlib\n    - name: Fetch zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests zstd\n    - name: Fetch libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      run: python build/fbcode_builder/getdeps.py fetch --no-tests libevent\n    - name: Restore boost from cache\n      id: restore_boost\n      if: ${{ steps.paths.outputs.boost_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Build boost\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests boost\n    - name: Save boost to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.boost_INSTALL }}\n       key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install\n    - name: Restore libsodium from cache\n      id: restore_libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Build libsodium\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libsodium\n    - name: Save libsodium to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libsodium_INSTALL }}\n       key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install\n    - name: Restore ninja from cache\n      id: restore_ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Build ninja\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests ninja\n    - name: Save ninja to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.ninja_INSTALL }}\n       key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install\n    - name: Restore cmake from cache\n      id: restore_cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Build cmake\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests cmake\n    - name: Save cmake to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.cmake_INSTALL }}\n       key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install\n    - name: Restore double-conversion from cache\n      id: restore_double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Build double-conversion\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests double-conversion\n    - name: Save double-conversion to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.double-conversion_INSTALL }}\n       key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install\n    - name: Restore fast_float from cache\n      id: restore_fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Build fast_float\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests fast_float\n    - name: Save fast_float to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fast_float_INSTALL }}\n       key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install\n    - name: Restore fmt from cache\n      id: restore_fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Build fmt\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests fmt\n    - name: Save fmt to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.fmt_INSTALL }}\n       key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install\n    - name: Restore gflags from cache\n      id: restore_gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Build gflags\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests gflags\n    - name: Save gflags to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.gflags_INSTALL }}\n       key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install\n    - name: Restore glog from cache\n      id: restore_glog\n      if: ${{ steps.paths.outputs.glog_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Build glog\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests glog\n    - name: Save glog to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.glog_INSTALL }}\n       key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install\n    - name: Restore googletest from cache\n      id: restore_googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Build googletest\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests googletest\n    - name: Save googletest to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.googletest_INSTALL }}\n       key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install\n    - name: Restore libdwarf from cache\n      id: restore_libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Build libdwarf\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libdwarf\n    - name: Save libdwarf to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libdwarf_INSTALL }}\n       key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install\n    - name: Restore lz4 from cache\n      id: restore_lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Build lz4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests lz4\n    - name: Save lz4 to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.lz4_INSTALL }}\n       key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install\n    - name: Restore jom from cache\n      id: restore_jom\n      if: ${{ steps.paths.outputs.jom_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.jom_INSTALL }}\n       key: ${{ steps.paths.outputs.jom_CACHE_KEY }}-install\n    - name: Build jom\n      if: ${{ steps.paths.outputs.jom_SOURCE && ! steps.restore_jom.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests jom\n    - name: Save jom to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.jom_SOURCE && ! steps.restore_jom.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.jom_INSTALL }}\n       key: ${{ steps.paths.outputs.jom_CACHE_KEY }}-install\n    - name: Restore perl from cache\n      id: restore_perl\n      if: ${{ steps.paths.outputs.perl_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.perl_INSTALL }}\n       key: ${{ steps.paths.outputs.perl_CACHE_KEY }}-install\n    - name: Build perl\n      if: ${{ steps.paths.outputs.perl_SOURCE && ! steps.restore_perl.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests perl\n    - name: Save perl to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.perl_SOURCE && ! steps.restore_perl.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.perl_INSTALL }}\n       key: ${{ steps.paths.outputs.perl_CACHE_KEY }}-install\n    - name: Restore openssl from cache\n      id: restore_openssl\n      if: ${{ steps.paths.outputs.openssl_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.openssl_INSTALL }}\n       key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install\n    - name: Build openssl\n      if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests openssl\n    - name: Save openssl to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.openssl_INSTALL }}\n       key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install\n    - name: Restore snappy from cache\n      id: restore_snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Build snappy\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests snappy\n    - name: Save snappy to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.snappy_INSTALL }}\n       key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install\n    - name: Restore zlib from cache\n      id: restore_zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.zlib_INSTALL }}\n       key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install\n    - name: Build zlib\n      if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests zlib\n    - name: Save zlib to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.zlib_INSTALL }}\n       key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install\n    - name: Restore zstd from cache\n      id: restore_zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Build zstd\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests zstd\n    - name: Save zstd to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.zstd_INSTALL }}\n       key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install\n    - name: Restore libevent from cache\n      id: restore_libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE }}\n      uses: actions/cache/restore@v4\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Build libevent\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libevent\n    - name: Save libevent to cache\n      uses: actions/cache/save@v4\n      if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}\n      with:\n       path: ${{ steps.paths.outputs.libevent_INSTALL }}\n       key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install\n    - name: Build folly\n      run: python build/fbcode_builder/getdeps.py build --src-dir=. folly\n    - name: Copy artifacts\n      run: python build/fbcode_builder/getdeps.py fixup-dyn-deps --src-dir=. folly _artifacts/windows --final-install-prefix /usr/local\n    - uses: actions/upload-artifact@v6\n      with:\n        name: folly\n        path: _artifacts\n    - name: Test folly\n      run: python build/fbcode_builder/getdeps.py test --src-dir=. folly\n"
  },
  {
    "path": ".github/workflows/oss-build-and-test.yml",
    "content": "name: Buck build and test\non: [push, pull_request, workflow_dispatch]\njobs:\n  get-toolchains-to-install:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: 'true'\n      - uses: facebook/install-dotslash@latest\n      - name: get_buck_graph\n        run: |\n          BUCK_GRAPH=$(./buck2 cquery ... --output-attribute '^buck.type$|^name$')\n          echo \"$BUCK_GRAPH\" > buck_graph_results.json\n        shell: bash\n      - name: Check if rust_binary\n        id: check_rust\n        run: |\n          OUTPUT=$(cat buck_graph_results.json)\n          if [[ \"$OUTPUT\" == *\"rust_binary\"* ]]; then\n            echo \"uses_rust=true\" >> $GITHUB_ENV\n          fi\n        shell: bash\n      - name: Check if cxx_binary\n        id: check_cxx\n        run: |\n          OUTPUT=$(cat buck_graph_results.json)\n          if [[ \"$OUTPUT\" == *\"cxx_binary\"* ]]; then\n            echo \"uses_cxx=true\" >> $GITHUB_ENV\n          fi\n        shell: bash\n      - name: Check if ocaml_binary\n        id: check_ocaml\n        run: |\n          OUTPUT=$(cat buck_graph_results.json)\n          if [[ \"$OUTPUT\" == *\"ocaml_binary\"* ]]; then\n            echo \"uses_ocaml=true\" >> $GITHUB_ENV\n          fi\n        shell: bash\n      - name: Check if python_binary\n        id: check_python\n        run: |\n          OUTPUT=$(cat buck_graph_results.json)\n          if [[ \"$OUTPUT\" == *\"python_binary\"* ]]; then\n            echo \"uses_python=true\" >> $GITHUB_ENV\n          fi\n        shell: bash\n    outputs:\n      uses_rust: ${{ env.uses_rust }}\n      uses_cxx: ${{ env.uses_cxx }}\n      uses_ocaml: ${{env.uses_ocaml}}\n      uses_python: ${{env.uses_python}}\n\n  ubuntu-os-buck-build-and-test:\n      needs: get-toolchains-to-install\n      runs-on: ubuntu-latest\n      steps:\n        - uses: actions/checkout@v4\n          with:\n            submodules: 'true'\n        - run: sudo apt-get update\n          shell: bash\n        - uses: facebook/install-dotslash@latest\n        - name: Install Rust toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_rust == 'true'\n          uses: dtolnay/rust-toolchain@stable\n        - name: Install C++ toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_cxx == 'true'\n          run: |\n            sudo apt-get install cmake llvm cppcheck python3-pip\n            sudo pip3 install conan==1.*\n          shell: bash\n        - name: Install OCaml toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_ocaml == 'true'\n          uses: ocaml/setup-ocaml@v2\n          with:\n            ocaml-compiler: \"5.1\"\n        - name: Install Python toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_python == 'true'\n          uses: actions/setup-python@v5\n          with:\n            python-version: '3.10'\n        - name: buck2 build and test\n          run: bash ./.github/scripts/buck_build_and_test.sh\n  windows-os-buck-build-and-test:\n      needs: get-toolchains-to-install\n      runs-on: windows-latest\n      steps:\n        - uses: actions/checkout@v4\n          with:\n            submodules: 'true'\n        - uses: facebook/install-dotslash@latest\n        - name: Install Rust toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_rust == 'true'\n          uses: dtolnay/rust-toolchain@stable\n        - name: Install C++ toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_cxx == 'true'\n          run: |\n            choco install llvm cmake conan cppcheck -y\n            if ($LASTEXITCODE -eq 3010) { $LASTEXITCODE = 0 }\n          shell: pwsh\n        - name: Install OCaml toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_ocaml == 'true'\n          uses: ocaml/setup-ocaml@v2\n          with:\n            ocaml-compiler: \"4.12.0\"\n        - name: Install Python toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_python == 'true'\n          uses: actions/setup-python@v5\n          with:\n            python-version: '3.10'\n        - name: buck2 build and test\n          run: bash ./.github/scripts/buck_build_and_test.sh\n  mac-os-buck-build-and-test:\n      needs: get-toolchains-to-install\n      runs-on: macos-latest\n      steps:\n        - uses: actions/checkout@v4\n          with:\n            submodules: 'true'\n        - uses: facebook/install-dotslash@latest\n        - name: Install Rust toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_rust == 'true'\n          uses: dtolnay/rust-toolchain@stable\n        - name: Install C++ toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_cxx == 'true'\n          run: |\n            brew install cmake llvm cppcheck python3 conan@1\n          shell: bash\n        - name: Install OCaml toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_ocaml == 'true'\n          uses: ocaml/setup-ocaml@v2\n          with:\n            ocaml-compiler: \"5.1\"\n        - name: Install Python toolchain\n          if: needs.get-toolchains-to-install.outputs.uses_python == 'true'\n          uses: actions/setup-python@v5\n          with:\n            python-version: '3.10'\n        - name: Install homebrew deps\n          run: |\n            BUCK_GRAPH=$(./buck2 cquery \"attrregexfilter(labels, 'third-party:homebrew:', deps(//...))\" --json --output-attribute=labels)\n            HOMEBREW_PACKAGES=$(echo $BUCK_GRAPH | jq '[.[] | .labels] | flatten | unique | map(select(contains(\"third-party:homebrew:\")) | sub(\"third-party:homebrew:\"; \"\")) | .[] | @text')\n            echo $HOMEBREW_PACKAGES\n            echo $HOMEBREW_PACKAGES | xargs brew install pkg-config\n        - name: buck2 build and test\n          run: bash ./.github/scripts/buck_build_and_test.sh\n"
  },
  {
    "path": ".gitignore",
    "content": "*.o\n*.lo\n*.la\n.dirstamp\nMakefile\nMakefile.in\n.libs\n.deps\nstamp-h1\nfolly-config.h\n_configs.sed\naclocal.m4\nautom4te.cache\nbuild-aux\nlibtool\nfolly/test/gtest\nfolly/folly-config.h\nfolly/**/test/*_benchmark\nfolly/**/test/*.log\nfolly/**/test/*_test\nfolly/**/test/*_test_using_jemalloc\nfolly/**/test/*.trs\nfolly/config.*\nfolly/configure\nfolly/logging/example/logging_example\nfolly/libfolly.pc\nfolly/m4/libtool.m4\nfolly/m4/ltoptions.m4\nfolly/m4/ltsugar.m4\nfolly/m4/ltversion.m4\nfolly/m4/lt~obsolete.m4\nfolly/generate_fingerprint_tables\nfolly/FingerprintTables.cpp\n_build\n# Ignore all files generated by Buck2\nbuck-out/\n"
  },
  {
    "path": ".projectid",
    "content": "folly\n"
  },
  {
    "path": "BUCK",
    "content": "load(\"@fbcode_macros//build_defs:native_rules.bzl\", \"buck_genrule\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nbuck_genrule(\n    name = \"folly-config.h\",\n    srcs = {file: file for file in glob([\n               \"CMake/*\",\n               \"build/fbcode_builder/CMake/*\",\n           ])} |\n           {\"CMakeLists.txt\": \"CMakeListsForBuck2.txt\"},\n    out = \"folly-config.h\",\n    cmd = \"cmake . && mv folly/folly-config.h $OUT\",\n    default_target_platform = \"prelude//platforms:default\",\n    labels = [\n        \"third-party:fedora:cmake\",\n        \"third-party:homebrew:cmake\",\n        \"third-party:ubuntu:cmake\",\n    ],\n    remote = False,\n)\n"
  },
  {
    "path": "CMake/FindCython.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Find Cython\n#\n# This module sets the following variables:\n# - Cython_FOUND\n# - CYTHON_EXE\n# - CYTHON_VERSION_STRING\n#\nfind_program(CYTHON_EXE\n             NAMES cython cython3)\nif (CYTHON_EXE)\n  execute_process(COMMAND ${CYTHON_EXE} --version\n                  RESULT_VARIABLE _cython_retcode\n                  OUTPUT_VARIABLE _cython_output\n                  ERROR_VARIABLE _cython_output\n                  OUTPUT_STRIP_TRAILING_WHITESPACE)\n\n  if (${_cython_retcode} EQUAL 0)\n    separate_arguments(_cython_output)\n    list(GET _cython_output -1 CYTHON_VERSION_STRING)\n    message(STATUS \"Found Cython Version ${CYTHON_VERSION_STRING}\")\n  else ()\n    message(STATUS \"Failed to get Cython version\")\n  endif ()\nelse ()\n  message(STATUS \"Cython not found\")\nendif ()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  Cython\n  REQUIRED_VARS CYTHON_EXE CYTHON_VERSION_STRING\n  VERSION_VAR CYTHON_VERSION_STRING\n)\n"
  },
  {
    "path": "CMake/FindFastFloat.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#\n# - Try to find fast_float library\n# This will define\n# FASTFLOAT_FOUND\n# FASTFLOAT_INCLUDE_DIR\n#\n\nfind_path(FASTFLOAT_INCLUDE_DIR NAMES fast_float/fast_float.h)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    FastFloat DEFAULT_MSG\n    FASTFLOAT_INCLUDE_DIR\n)\n\nmark_as_advanced(FASTFLOAT_INCLUDE_DIR)\n"
  },
  {
    "path": "CMake/FindFmt.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfind_path(LIBFMT_INCLUDE_DIR fmt/core.h)\nmark_as_advanced(LIBFMT_INCLUDE_DIR)\n\nfind_library(LIBFMT_LIBRARY NAMES fmt fmtd)\nmark_as_advanced(LIBFMT_LIBRARY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    LIBFMT\n    DEFAULT_MSG\n    LIBFMT_LIBRARY LIBFMT_INCLUDE_DIR)\n\nif(LIBFMT_FOUND)\n    set(LIBFMT_LIBRARIES ${LIBFMT_LIBRARY})\n    set(LIBFMT_INCLUDE_DIRS ${LIBFMT_INCLUDE_DIR})\n    message(STATUS \"Found {fmt}: ${LIBFMT_LIBRARY}\")\n    add_library(fmt::fmt UNKNOWN IMPORTED)\n    set_target_properties(\n      fmt::fmt PROPERTIES\n      INTERFACE_INCLUDE_DIRECTORIES \"${LIBFMT_INCLUDE_DIR}\"\n    )\n    set_target_properties(\n      fmt::fmt PROPERTIES\n      IMPORTED_LINK_INTERFACE_LANGUAGES \"C\"\n      IMPORTED_LOCATION \"${LIBFMT_LIBRARY}\"\n    )\nendif()\n\n"
  },
  {
    "path": "CMake/FindLZ4.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Finds liblz4.\n#\n# This module defines:\n# LZ4_FOUND\n# LZ4_INCLUDE_DIR\n# LZ4_LIBRARY\n#\n\nfind_path(LZ4_INCLUDE_DIR NAMES lz4.h)\n\nfind_library(LZ4_LIBRARY_DEBUG NAMES lz4d)\nfind_library(LZ4_LIBRARY_RELEASE NAMES lz4)\n\ninclude(SelectLibraryConfigurations)\nSELECT_LIBRARY_CONFIGURATIONS(LZ4)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    LZ4 DEFAULT_MSG\n    LZ4_LIBRARY LZ4_INCLUDE_DIR\n)\n\nif (LZ4_FOUND)\n    message(STATUS \"Found LZ4: ${LZ4_LIBRARY}\")\nendif()\n\nmark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY)\n"
  },
  {
    "path": "CMake/FindLibAIO.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfind_path(LIBAIO_INCLUDE_DIR NAMES libaio.h)\nmark_as_advanced(LIBAIO_INCLUDE_DIR)\n\nfind_library(LIBAIO_LIBRARY NAMES aio)\nmark_as_advanced(LIBAIO_LIBRARY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n  LIBAIO\n  REQUIRED_VARS LIBAIO_LIBRARY LIBAIO_INCLUDE_DIR)\n\nif(LIBAIO_FOUND)\n  set(LIBAIO_LIBRARIES ${LIBAIO_LIBRARY})\n  set(LIBAIO_INCLUDE_DIRS ${LIBAIO_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "CMake/FindLibDwarf.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# dwarf.h is typically installed in a libdwarf/ subdirectory on Debian-style\n# Linux distributions.  It is not installed in a libdwarf/ subdirectory on Mac\n# systems when installed with Homebrew.  Newer homebrew installations install\n# it in libdwarf-0.  Search for it in all locations.\nfind_path(LIBDWARF_INCLUDE_DIR NAMES dwarf.h PATH_SUFFIXES libdwarf libdwarf-0)\nmark_as_advanced(LIBDWARF_INCLUDE_DIR)\n\nfind_library(LIBDWARF_LIBRARY NAMES dwarf)\nmark_as_advanced(LIBDWARF_LIBRARY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n  LIBDWARF\n  REQUIRED_VARS LIBDWARF_LIBRARY LIBDWARF_INCLUDE_DIR)\n\nif(LIBDWARF_FOUND)\n  set(LIBDWARF_LIBRARIES ${LIBDWARF_LIBRARY})\n  set(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "CMake/FindLibUring.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfind_path(LIBURING_INCLUDE_DIR NAMES liburing.h)\nmark_as_advanced(LIBURING_INCLUDE_DIR)\n\nfind_library(LIBURING_LIBRARY NAMES uring)\nmark_as_advanced(LIBURING_LIBRARY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n        LIBURING\n        REQUIRED_VARS LIBURING_LIBRARY LIBURING_INCLUDE_DIR)\n\nif(LIBURING_FOUND)\n  set(LIBURING_LIBRARIES ${LIBURING_LIBRARY})\n  set(LIBURING_INCLUDE_DIRS ${LIBURING_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "CMake/FindLibiberty.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfind_path(LIBIBERTY_INCLUDE_DIR NAMES libiberty.h PATH_SUFFIXES libiberty)\nmark_as_advanced(LIBIBERTY_INCLUDE_DIR)\n\nfind_library(LIBIBERTY_LIBRARY NAMES iberty)\nmark_as_advanced(LIBIBERTY_LIBRARY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n  LIBIBERTY\n  REQUIRED_VARS LIBIBERTY_LIBRARY LIBIBERTY_INCLUDE_DIR)\n\nif(LIBIBERTY_FOUND)\n  set(LIBIBERTY_LIBRARIES ${LIBIBERTY_LIBRARY})\n  set(LIBIBERTY_INCLUDE_DIRS ${LIBIBERTY_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "CMake/FindLibsodium.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfind_path(LIBSODIUM_INCLUDE_DIR NAMES sodium.h)\nmark_as_advanced(LIBSODIUM_INCLUDE_DIR)\n\nfind_library(LIBSODIUM_LIBRARY NAMES sodium)\nmark_as_advanced(LIBSODIUM_LIBRARY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n  LIBSODIUM\n  REQUIRED_VARS LIBSODIUM_LIBRARY LIBSODIUM_INCLUDE_DIR)\n\nif(LIBSODIUM_FOUND)\n  set(LIBSODIUM_LIBRARIES ${LIBSODIUM_LIBRARY})\n  set(LIBSODIUM_INCLUDE_DIRS ${LIBSODIUM_INCLUDE_DIR})\n  message(STATUS \"Found Libsodium: ${LIBSODIUM_LIBRARY}\")\nendif()\n"
  },
  {
    "path": "CMake/FindSnappy.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Find the Snappy libraries\n#\n# This module defines:\n# SNAPPY_FOUND\n# SNAPPY_INCLUDE_DIR\n# SNAPPY_LIBRARY\n\nfind_path(SNAPPY_INCLUDE_DIR NAMES snappy.h)\n\nfind_library(SNAPPY_LIBRARY_DEBUG NAMES snappyd)\nfind_library(SNAPPY_LIBRARY_RELEASE NAMES snappy)\n\ninclude(SelectLibraryConfigurations)\nSELECT_LIBRARY_CONFIGURATIONS(SNAPPY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    SNAPPY DEFAULT_MSG\n    SNAPPY_LIBRARY SNAPPY_INCLUDE_DIR\n)\n\nmark_as_advanced(SNAPPY_INCLUDE_DIR SNAPPY_LIBRARY)\n"
  },
  {
    "path": "CMake/FindZstd.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#\n# - Try to find Facebook zstd library\n# This will define\n# ZSTD_FOUND\n# ZSTD_INCLUDE_DIR\n# ZSTD_LIBRARY\n#\n\nfind_path(ZSTD_INCLUDE_DIR NAMES zstd.h)\n\nfind_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)\nfind_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)\n\ninclude(SelectLibraryConfigurations)\nSELECT_LIBRARY_CONFIGURATIONS(ZSTD)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    ZSTD DEFAULT_MSG\n    ZSTD_LIBRARY ZSTD_INCLUDE_DIR\n)\n\nif (ZSTD_FOUND)\n    message(STATUS \"Found Zstd: ${ZSTD_LIBRARY}\")\nendif()\n\nmark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)\n"
  },
  {
    "path": "CMake/FollyCompilerMSVC.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Some additional configuration options.\noption(MSVC_ENABLE_ALL_WARNINGS \"If enabled, pass /Wall to the compiler.\" ON)\noption(MSVC_ENABLE_DEBUG_INLINING \"If enabled, enable inlining in the debug configuration. This allows /Zc:inline to be far more effective.\" OFF)\noption(MSVC_ENABLE_FAST_LINK \"If enabled, pass /DEBUG:FASTLINK to the linker. This makes linking faster, but the gtest integration for Visual Studio can't currently handle the .pdbs generated.\" OFF)\noption(MSVC_ENABLE_LEAN_AND_MEAN_WINDOWS \"If enabled, define WIN32_LEAN_AND_MEAN to include a smaller subset of Windows.h\" ON)\noption(MSVC_ENABLE_LTCG \"If enabled, use Link Time Code Generation for Release builds.\" OFF)\noption(MSVC_ENABLE_PARALLEL_BUILD \"If enabled, build multiple source files in parallel.\" ON)\noption(MSVC_ENABLE_STATIC_ANALYSIS \"If enabled, do more complex static analysis and generate warnings appropriately.\" OFF)\noption(MSVC_USE_STATIC_RUNTIME \"If enabled, build against the static, rather than the dynamic, runtime.\" OFF)\noption(MSVC_SUPPRESS_BOOST_CONFIG_OUTDATED \"If enabled, suppress Boost's warnings about the config being out of date.\" ON)\n\n# Alas, option() doesn't support string values.\nset(MSVC_FAVORED_ARCHITECTURE \"blend\" CACHE STRING \"One of 'blend', 'AMD64', 'INTEL64', or 'ATOM'. This tells the compiler to generate code optimized to run best on the specified architecture.\")\n# Add a pretty drop-down selector for these values when using the GUI.\nset_property(\n  CACHE MSVC_FAVORED_ARCHITECTURE\n  PROPERTY STRINGS\n    blend\n    AMD64\n    ATOM\n    INTEL64\n)\n# Validate, and then add the favored architecture.\nif (NOT MSVC_FAVORED_ARCHITECTURE STREQUAL \"blend\" AND NOT MSVC_FAVORED_ARCHITECTURE STREQUAL \"AMD64\" AND NOT MSVC_FAVORED_ARCHITECTURE STREQUAL \"INTEL64\" AND NOT MSVC_FAVORED_ARCHITECTURE STREQUAL \"ATOM\")\n  message(FATAL_ERROR \"MSVC_FAVORED_ARCHITECTURE must be set to one of exactly, 'blend', 'AMD64', 'INTEL64', or 'ATOM'! Got '${MSVC_FAVORED_ARCHITECTURE}' instead!\")\nendif()\n\nset(MSVC_LANGUAGE_VERSION \"c++20\" CACHE STRING \"One of 'c++20', or 'c++latest'. This determines which version of C++ to compile as.\")\nset_property(\n  CACHE MSVC_LANGUAGE_VERSION\n  PROPERTY STRINGS\n    \"c++20\"\n    \"c++latest\"\n)\n\n############################################################\n# We need to adjust a couple of the default option sets.\n############################################################\n\n# If the static runtime is requested, we have to\n# overwrite some of CMake's defaults.\nif (MSVC_USE_STATIC_RUNTIME)\n  foreach(flag_var\n      CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n      CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO\n      CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE\n      CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)\n    if (${flag_var} MATCHES \"/MD\")\n      string(REGEX REPLACE \"/MD\" \"/MT\" ${flag_var} \"${${flag_var}}\")\n    endif()\n  endforeach()\nendif()\n\n# The Ninja generator doesn't de-dup the exception mode flag, so remove the\n# default flag so that MSVC doesn't warn about it on every single file.\nif (\"${CMAKE_GENERATOR}\" STREQUAL \"Ninja\")\n  foreach(flag_var\n      CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n      CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO\n      CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE\n      CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)\n    if (${flag_var} MATCHES \"/EHsc\")\n      string(REGEX REPLACE \"/EHsc\" \"\" ${flag_var} \"${${flag_var}}\")\n    endif()\n  endforeach()\nendif()\n\n# In order for /Zc:inline, which speeds up the build significantly, to work\n# we need to remove the /Ob0 parameter that CMake adds by default, because that\n# would normally disable all inlining.\nforeach(flag_var CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG)\n  if (${flag_var} MATCHES \"/Ob0\")\n    string(REGEX REPLACE \"/Ob0\" \"\" ${flag_var} \"${${flag_var}}\")\n  endif()\nendforeach()\n\n# When building with Ninja, or with /MP enabled, there is the potential\n# for multiple processes to need to lock the same pdb file.\n# The /FS option (which is implicitly enabled by /MP) is widely believed\n# to be the solution for this, but even with /FS enabled the problem can\n# still randomly occur.\n# https://stackoverflow.com/a/58020501/149111 suggests that /Z7 should be\n# used; rather than placing the debug info into a .pdb file it embeds it\n# into the object files in a similar way to gcc/clang which should reduce\n# contention and potentially make the build faster... but at the cost of\n# larger object files\nforeach(flag_var CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG)\n  if (${flag_var} MATCHES \"/Zi\")\n    string(REGEX REPLACE \"/Zi\" \"/Z7\" ${flag_var} \"${${flag_var}}\")\n  endif()\nendforeach()\n\n# Apply the option set for Folly to the specified target.\nfunction(apply_folly_compile_options_to_target THETARGET)\n  # The general options passed:\n  target_compile_options(${THETARGET}\n    PUBLIC\n      /EHs # Don't catch structured exceptions with catch (...)\n      /GF # There are bugs with constexpr StringPiece when string pooling is disabled.\n      /Zc:referenceBinding # Disallow temporaries from binding to non-const lvalue references.\n      /Zc:rvalueCast # Enforce the standard rules for explicit type conversion.\n      /Zc:implicitNoexcept # Enable implicit noexcept specifications where required, such as destructors.\n      /Zc:strictStrings # Don't allow conversion from a string literal to mutable characters.\n      /Zc:threadSafeInit # Enable thread-safe function-local statics initialization.\n      /Zc:throwingNew # Assume operator new throws on failure.\n\n      /permissive- # Be mean, don't allow bad non-standard stuff (C++/CLI, __declspec, etc. are all left intact).\n      /std:${MSVC_LANGUAGE_VERSION} # Build in the requested version of C++\n      /utf-8 # fmt needs unicode support, which requires compiling with /utf-8\n\n    PRIVATE\n      /bigobj # Support objects with > 65k sections. Needed due to templates.\n      /favor:${MSVC_FAVORED_ARCHITECTURE} # Architecture to prefer when generating code.\n      /Zc:inline # Have the compiler eliminate unreferenced COMDAT functions and data before emitting the object file.\n\n      $<$<BOOL:${MSVC_ENABLE_ALL_WARNINGS}>:/Wall> # Enable all warnings if requested.\n      $<$<BOOL:${MSVC_ENABLE_PARALLEL_BUILD}>:/MP> # Enable multi-processor compilation if requested.\n      $<$<BOOL:${MSVC_ENABLE_STATIC_ANALYSIS}>:/analyze> # Enable static analysis if requested.\n\n      # Debug builds\n      $<$<CONFIG:DEBUG>:\n        /Gy- # Disable function level linking.\n\n        $<$<BOOL:${MSVC_ENABLE_DEBUG_INLINING}>:/Ob2> # Add /Ob2 if allowing inlining in debug mode.\n      >\n\n      # Non-debug builds\n      $<$<NOT:$<CONFIG:DEBUG>>:\n        /Gw # Optimize global data. (-fdata-sections)\n        /Gy # Enable function level linking. (-ffunction-sections)\n        /Qpar # Enable parallel code generation.\n        /Oi # Enable intrinsic functions.\n        /Ot # Favor fast code.\n\n        $<$<BOOL:${MSVC_ENABLE_LTCG}>:/GL> # Enable link time code generation.\n      >\n  )\n\n  target_compile_options(${THETARGET}\n    PUBLIC\n      /wd4191 # 'type cast' unsafe conversion of function pointers\n      /wd4291 # no matching operator delete found\n      /wd4309 # '=' truncation of constant value\n      /wd4310 # cast truncates constant value\n      /wd4366 # result of unary '&' operator may be unaligned\n      /wd4587 # behavior change; constructor no longer implicitly called\n      /wd4592 # symbol will be dynamically initialized (implementation limitation)\n      /wd4628 # digraphs not supported with -Ze\n      /wd4723 # potential divide by 0\n      /wd4724 # potential mod by 0\n      /wd4868 # compiler may not enforce left-to-right evaluation order\n      /wd4996 # user deprecated\n\n      # The warnings that are disabled:\n      /wd4068 # Unknown pragma.\n      /wd4091 # 'typedef' ignored on left of '' when no variable is declared.\n      /wd4146 # Unary minus applied to unsigned type, result still unsigned.\n      /wd4800 # Values being forced to bool, this happens many places, and is a \"performance warning\".\n\n      # NOTE: glog/logging.h:1116 change to `size_t pcount() const { return size_t(pptr() - pbase()); }`\n      # NOTE: gmock/gmock-spec-builders.h:1177 change to `*static_cast<const Action<F>*>(untyped_actions_[size_t(count - 1)]) :`\n      # NOTE: gmock/gmock-spec-builders.h:1749 change to `const size_t count = untyped_expectations_.size();`\n      # NOTE: gmock/gmock-spec-builders.h:1754 change to `for (size_t i = 0; i < count; i++) {`\n      # NOTE: gtest/gtest-printers.h:173 change to `const internal::BiggestInt kBigInt = internal::BiggestInt(value);`\n      # NOTE: gtest/internal/gtest-internal.h:890 add `GTEST_DISABLE_MSC_WARNINGS_PUSH_(4365)`\n      # NOTE: gtest/internal/gtest-internal.h:894 ass `GTEST_DISABLE_MSC_WARNINGS_POP_()`\n      # NOTE: boost/crc.hpp:578 change to `{ return static_cast<unsigned char>(x ^ rem); }`\n      # NOTE: boost/regex/v4/match_results.hpp:126 change to `return m_subs[size_type(sub)].length();`\n      # NOTE: boost/regex/v4/match_results.hpp:226 change to `return m_subs[size_type(sub)];`\n      # NOTE: boost/date_time/adjust_functors.hpp:67 change to `origDayOfMonth_ = short(ymd.day);`\n      # NOTE: boost/date_time/adjust_functors.hpp:75 change to `wrap_int2 wi(short(ymd.month));`\n      # NOTE: boost/date_time/adjust_functors.hpp:82 change to `day_type resultingEndOfMonthDay(cal_type::end_of_month_day(static_cast<unsigned short>(year), static_cast<unsigned short>(wi.as_int())));`\n      # NOTE: boost/date_time/adjust_functors.hpp:85 change to `return date_type(static_cast<unsigned short>(year), static_cast<unsigned short>(wi.as_int()), resultingEndOfMonthDay) - d;`\n      # NOTE: boost/date_time/adjust_functors.hpp:87 change to `day_type dayOfMonth = static_cast<unsigned short>(origDayOfMonth_);`\n      # NOTE: boost/date_time/adjust_functors.hpp:91 change to `return date_type(static_cast<unsigned short>(year), static_cast<unsigned short>(wi.as_int()), dayOfMonth) - d;`\n      # NOTE: boost/date_time/adjust_functors.hpp:98 change to `origDayOfMonth_ = short(ymd.day);`\n      # NOTE: boost/date_time/adjust_functors.hpp:106 change to `wrap_int2 wi(short(ymd.month));`\n      # NOTE: boost/date_time/adjust_functors.hpp:111 change to `day_type resultingEndOfMonthDay(cal_type::end_of_month_day(static_cast<unsigned short>(year), static_cast<unsigned short>(wi.as_int())));`\n      # NOTE: boost/date_time/adjust_functors.hpp:114 change to `return date_type(static_cast<unsigned short>(year), static_cast<unsigned short>(wi.as_int()), resultingEndOfMonthDay) - d;`\n      # NOTE: boost/date_time/adjust_functors.hpp:116 change to `day_type dayOfMonth = static_cast<unsigned short>(origDayOfMonth_);`\n      # NOTE: boost/date_time/adjust_functors.hpp:120 change to `return date_type(static_cast<unsigned short>(year), static_cast<unsigned short>(wi.as_int()), dayOfMonth) - d;`\n      # NOTE: boost/date_time/gregorian_calendar.ipp:81 change to `unsigned long  d = static_cast<unsigned long>(ymd.day + ((153*m + 2)/5) + 365*y + (y/4) - (y/100) + (y/400) - 32045);`\n      # NOTE: boost/date_time/gregorian/greg_date.hpp:122 change to `unsigned short eom_day =  gregorian_calendar::end_of_month_day(ymd.year, ymd.month);`\n      # NOTE: boost/thread/future.hpp:1050 change to `locks[std::ptrdiff_t(i)]=BOOST_THREAD_MAKE_RV_REF(boost::unique_lock<boost::mutex>(futures[i].future_->mutex));`\n      # NOTE: boost/thread/future.hpp:1063 change to `locks[std::ptrdiff_t(i)].unlock();`\n      # NOTE: boost/thread/win32/basic_recursive_mutex.hpp:47 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`\n      # NOTE: boost/thread/win32/basic_recursive_mutex.hpp:53 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`\n      # NOTE: boost/thread/win32/basic_recursive_mutex.hpp:64 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`\n      # NOTE: boost/thread/win32/basic_recursive_mutex.hpp:78 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`\n      # NOTE: boost/thread/win32/basic_recursive_mutex.hpp:84 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`\n      # NOTE: boost/thread/win32/condition_variable.hpp:79 change to `detail::win32::ReleaseSemaphore(semaphore,long(count_to_release),0);`\n      # NOTE: boost/thread/win32/condition_variable.hpp:84 change to `release(unsigned(detail::interlocked_read_acquire(&waiters)));`\n      # NOTE: boost/algorithm/string/detail/classification.hpp:85 change to `std::size_t Size=std::size_t(::boost::distance(Range));`\n      /wd4018 # Signed/unsigned mismatch.\n      /wd4365 # Signed/unsigned mismatch.\n      /wd4388 # Signed/unsigned mismatch on relative comparison operator.\n      /wd4389 # Signed/unsigned mismatch on equality comparison operator.\n\n      # TODO:\n      /wd4100 # Unreferenced formal parameter.\n      /wd4459 # Declaration of parameter hides global declaration.\n      /wd4505 # Unreferenced local function has been removed.\n      /wd4701 # Potentially uninitialized local variable used.\n      /wd4702 # Unreachable code.\n\n      # These warnings are disabled because we've\n      # enabled all warnings. If all warnings are\n      # not enabled, we still need to disable them\n      # for consuming libs.\n      /wd4061 # Enum value not handled by a case in a switch on an enum. This isn't very helpful because it is produced even if a default statement is present.\n      /wd4127 # Conditional expression is constant.\n      /wd4200 # Non-standard extension, zero sized array.\n      /wd4201 # Non-standard extension used: nameless struct/union.\n      /wd4296 # '<' Expression is always false.\n      /wd4316 # Object allocated on the heap may not be aligned to 128.\n      /wd4324 # Structure was padded due to alignment specifier.\n      /wd4355 # 'this' used in base member initializer list.\n      /wd4371 # Layout of class may have changed due to fixes in packing.\n      /wd4435 # Object layout under /vd2 will change due to virtual base.\n      /wd4514 # Unreferenced inline function has been removed. (caused by /Zc:inline)\n      /wd4548 # Expression before comma has no effect. I wouldn't disable this normally, but malloc.h triggers this warning.\n      /wd4571 # Semantics of catch(...) changed in VC 7.1\n      /wd4574 # ifdef'd macro was defined to 0.\n      /wd4582 # Constructor is not implicitly called.\n      /wd4583 # Destructor is not implicitly called.\n      /wd4619 # Invalid warning number used in #pragma warning.\n      /wd4623 # Default constructor was implicitly defined as deleted.\n      /wd4625 # Copy constructor was implicitly defined as deleted.\n      /wd4626 # Assignment operator was implicitly defined as deleted.\n      /wd4643 # Forward declaring standard library types is not permitted.\n      /wd4647 # Behavior change in __is_pod.\n      /wd4668 # Macro was not defined, replacing with 0.\n      /wd4706 # Assignment within conditional expression.\n      /wd4710 # Function was not inlined.\n      /wd4711 # Function was selected for automated inlining.\n      /wd4714 # Function marked as __forceinline not inlined.\n      /wd4820 # Padding added after data member.\n      /wd5026 # Move constructor was implicitly defined as deleted.\n      /wd5027 # Move assignment operator was implicitly defined as deleted.\n      /wd5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file. This is needed because of how boost does things.\n      /wd5045 # Compiler will insert Spectre mitigation for memory load if /Qspectre switch is specified.\n\n      # Warnings to treat as errors:\n      /we4099 # Mixed use of struct and class on same type names.\n      /we4129 # Unknown escape sequence. This is usually caused by incorrect escaping.\n      /we4566 # Character cannot be represented in current charset. This is remidied by prefixing string with \"u8\".\n\n    PRIVATE\n      # Warnings disabled for /analyze\n      $<$<BOOL:${MSVC_ENABLE_STATIC_ANALYSIS}>:\n        /wd6001 # Using uninitialized memory. This is disabled because it is wrong 99% of the time.\n        /wd6011 # Dereferencing potentially NULL pointer.\n        /wd6031 # Return value ignored.\n        /wd6235 # (<non-zero constant> || <expression>) is always a non-zero constant.\n        /wd6237 # (<zero> && <expression>) is always zero. <expression> is never evaluated and may have side effects.\n        /wd6239 # (<non-zero constant> && <expression>) always evaluates to the result of <expression>.\n        /wd6240 # (<expression> && <non-zero constant>) always evaluates to the result of <expression>.\n        /wd6246 # Local declaration hides declaration of same name in outer scope.\n        /wd6248 # Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object. This is done by one of the boost headers.\n        /wd6255 # _alloca indicates failure by raising a stack overflow exception.\n        /wd6262 # Function uses more than x bytes of stack space.\n        /wd6271 # Extra parameter passed to format function. The analysis pass doesn't recognize %j or %z, even though the runtime does.\n        /wd6285 # (<non-zero constant> || <non-zero constant>) is always true.\n        /wd6297 # 32-bit value is shifted then cast to 64-bits. The places this occurs never use more than 32 bits.\n        /wd6308 # Realloc might return null pointer: assigning null pointer to '<name>', which is passed as an argument to 'realloc', will cause the original memory to leak.\n        /wd6326 # Potential comparison of a constant with another constant.\n        /wd6330 # Unsigned/signed mismatch when passed as a parameter.\n        /wd6340 # Mismatch on sign when passed as format string value.\n        /wd6387 # '<value>' could be '0': This does not adhere to the specification for a function.\n        /wd28182 # Dereferencing NULL pointer. '<value>' contains the same NULL value as '<expression>'.\n        /wd28251 # Inconsistent annotation for function. This is because we only annotate the declaration and not the definition.\n        /wd28278 # Function appears with no prototype in scope.\n      >\n  )\n\n  # And the extra defines:\n  target_compile_definitions(${THETARGET}\n    PUBLIC\n      _CRT_NONSTDC_NO_WARNINGS # Don't deprecate posix names of functions.\n      _CRT_SECURE_NO_WARNINGS # Don't deprecate the non _s versions of various standard library functions, because safety is for chumps.\n      _SCL_SECURE_NO_WARNINGS # Don't deprecate the non _s versions of various standard library functions, because safety is for chumps.\n      _STL_EXTRA_DISABLED_WARNINGS=4774\\ 4987\n\n      $<$<BOOL:${MSVC_ENABLE_CPP_LATEST}>:_HAS_AUTO_PTR_ETC=1> # We're building in C++ 17 or greater mode, but certain dependencies (Boost) still have dependencies on unary_function and binary_function, so we have to make sure not to remove them.\n      $<$<BOOL:${MSVC_ENABLE_LEAN_AND_MEAN_WINDOWS}>:WIN32_LEAN_AND_MEAN> # Don't include most of Windows.h\n      $<$<BOOL:${MSVC_SUPPRESS_BOOST_CONFIG_OUTDATED}>:BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE> # MSVC moves faster than boost, so add a quick way to disable the messages.\n  )\n\n  # Ignore a warning about an object file not defining any symbols,\n  # these are known, and we don't care.\n  set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY STATIC_LIBRARY_FLAGS \" /ignore:4221\")\n\n  # The options to pass to the linker:\n  set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG \" /INCREMENTAL\") # Do incremental linking.\n  if (NOT $<TARGET_PROPERTY:${THETARGET},TYPE> STREQUAL \"STATIC_LIBRARY\")\n    set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG \" /OPT:NOREF\") # No unreferenced data elimination.\n    set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG \" /OPT:NOICF\") # No Identical COMDAT folding.\n\n    set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE \" /OPT:REF\") # Remove unreferenced functions and data.\n    set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE \" /OPT:ICF\") # Identical COMDAT folding.\n  endif()\n\n  if (MSVC_ENABLE_FAST_LINK)\n    set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG \" /DEBUG:FASTLINK\") # Generate a partial PDB file that simply references the original object and library files.\n  endif()\n\n  # Add /GL to the compiler, and /LTCG to the linker\n  # if link time code generation is enabled.\n  if (MSVC_ENABLE_LTCG)\n    set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE \" /LTCG\")\n  endif()\nendfunction()\n\nlist(APPEND FOLLY_LINK_LIBRARIES Iphlpapi.lib Ws2_32.lib)\n"
  },
  {
    "path": "CMake/FollyCompilerUnix.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset(CMAKE_CXX_FLAGS_COMMON \"-g -Wall -Wextra\")\nset(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_COMMON}\")\nset(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_COMMON} -O3\")\n\nlist(APPEND CMAKE_REQUIRED_DEFINITIONS \"-D_GNU_SOURCE\")\nfunction(apply_folly_compile_options_to_target THETARGET)\n  target_compile_definitions(${THETARGET}\n    PRIVATE\n      _REENTRANT\n      _GNU_SOURCE\n  )\n  target_compile_options(${THETARGET}\n    PRIVATE\n      -g\n      -finput-charset=UTF-8\n      -fsigned-char\n      -Wall\n      -Wno-deprecated\n      -Wno-deprecated-declarations\n      -Wno-sign-compare\n      -Wno-unused\n      -Wuninitialized\n      -Wunused-label\n      -Wunused-result\n      ${FOLLY_CXX_FLAGS}\n  )\nendfunction()\n"
  },
  {
    "path": "CMake/FollyConfigChecks.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ninclude(CheckCXXSourceCompiles)\ninclude(CheckCXXSourceRuns)\ninclude(CheckFunctionExists)\ninclude(CheckIncludeFileCXX)\ninclude(CheckSymbolExists)\ninclude(CheckTypeSize)\ninclude(CheckCXXCompilerFlag)\n\nif (CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n  list(APPEND FOLLY_CXX_FLAGS -Wno-psabi)\nendif()\n\nif (CMAKE_SYSTEM_NAME STREQUAL \"FreeBSD\")\n  CHECK_INCLUDE_FILE_CXX(malloc_np.h FOLLY_USE_JEMALLOC)\nelse()\n  CHECK_INCLUDE_FILE_CXX(jemalloc/jemalloc.h FOLLY_USE_JEMALLOC)\nendif()\n\nif(NOT CMAKE_SYSTEM_NAME STREQUAL \"Windows\")\n  # clang only rejects unknown warning flags if -Werror=unknown-warning-option\n  # is also specified.\n  check_cxx_compiler_flag(\n    -Werror=unknown-warning-option\n    COMPILER_HAS_UNKNOWN_WARNING_OPTION)\n  if (COMPILER_HAS_UNKNOWN_WARNING_OPTION)\n    set(CMAKE_REQUIRED_FLAGS\n      \"${CMAKE_REQUIRED_FLAGS} -Werror=unknown-warning-option\")\n  endif()\n\n  check_cxx_compiler_flag(-Wshadow-local COMPILER_HAS_W_SHADOW_LOCAL)\n  check_cxx_compiler_flag(\n    -Wshadow-compatible-local\n    COMPILER_HAS_W_SHADOW_COMPATIBLE_LOCAL)\n  if (COMPILER_HAS_W_SHADOW_LOCAL AND COMPILER_HAS_W_SHADOW_COMPATIBLE_LOCAL)\n    set(FOLLY_HAVE_SHADOW_LOCAL_WARNINGS ON)\n    list(APPEND FOLLY_CXX_FLAGS -Wshadow-compatible-local)\n  endif()\n\n  check_cxx_compiler_flag(\n      -Wnullability-completeness\n      COMPILER_HAS_W_NULLABILITY_COMPLETENESS)\n  if (COMPILER_HAS_W_NULLABILITY_COMPLETENESS)\n    list(APPEND FOLLY_CXX_FLAGS -Wno-nullability-completeness)\n  endif()\n\n  check_cxx_compiler_flag(\n      -Winconsistent-missing-override\n      COMPILER_HAS_W_INCONSISTENT_MISSING_OVERRIDE)\n  if (COMPILER_HAS_W_INCONSISTENT_MISSING_OVERRIDE)\n    list(APPEND FOLLY_CXX_FLAGS -Wno-inconsistent-missing-override)\n  endif()\n\n  check_cxx_compiler_flag(-fopenmp COMPILER_HAS_F_OPENMP)\n  if (COMPILER_HAS_F_OPENMP)\n      list(APPEND FOLLY_CXX_FLAGS -fopenmp)\n  endif()\nendif()\n\nset(FOLLY_ORIGINAL_CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS}\")\nstring(REGEX REPLACE\n  \"-std=(c|gnu)\\\\+\\\\+..\"\n  \"\"\n  CMAKE_REQUIRED_FLAGS\n  \"${CMAKE_REQUIRED_FLAGS}\")\n\ncheck_symbol_exists(pthread_atfork pthread.h FOLLY_HAVE_PTHREAD_ATFORK)\n\nlist(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)\ncheck_symbol_exists(accept4 sys/socket.h FOLLY_HAVE_ACCEPT4)\ncheck_symbol_exists(getrandom sys/random.h FOLLY_HAVE_GETRANDOM)\ncheck_symbol_exists(preadv sys/uio.h FOLLY_HAVE_PREADV)\ncheck_symbol_exists(pwritev sys/uio.h FOLLY_HAVE_PWRITEV)\ncheck_symbol_exists(clock_gettime time.h FOLLY_HAVE_CLOCK_GETTIME)\ncheck_symbol_exists(pipe2 unistd.h FOLLY_HAVE_PIPE2)\n\ncheck_function_exists(malloc_usable_size FOLLY_HAVE_MALLOC_USABLE_SIZE)\n\nset(CMAKE_REQUIRED_FLAGS \"${FOLLY_ORIGINAL_CMAKE_REQUIRED_FLAGS}\")\n\ncheck_cxx_source_compiles(\"\n  #pragma GCC diagnostic error \\\"-Wattributes\\\"\n  extern \\\"C\\\" void (*test_ifunc(void))() { return 0; }\n  void func() __attribute__((ifunc(\\\"test_ifunc\\\")));\n  int main() { return 0; }\"\n  FOLLY_HAVE_IFUNC\n)\ncheck_cxx_source_runs(\"\n  int main(int, char**) {\n    char buf[64] = {0};\n    unsigned long *ptr = (unsigned long *)(buf + 1);\n    *ptr = 0xdeadbeef;\n    return (*ptr & 0xff) == 0xef ? 0 : 1;\n  }\"\n  FOLLY_HAVE_UNALIGNED_ACCESS\n)\ncheck_cxx_source_compiles(\"\n  int main(int argc, char** argv) {\n    unsigned size = argc;\n    char data[size];\n    return 0;\n  }\"\n  FOLLY_HAVE_VLA\n)\ncheck_cxx_source_runs(\"\n  extern \\\"C\\\" int folly_example_undefined_weak_symbol() __attribute__((weak));\n  int main(int argc, char** argv) {\n    auto f = folly_example_undefined_weak_symbol; // null pointer\n    return f ? f() : 0; // must compile, link, and run with null pointer\n  }\"\n  FOLLY_HAVE_WEAK_SYMBOLS\n)\ncheck_cxx_source_runs(\"\n  #include <dlfcn.h>\n  int main() {\n    void *h = dlopen(\\\"linux-vdso.so.1\\\", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);\n    if (h == nullptr) {\n      return -1;\n    }\n    dlclose(h);\n    return 0;\n  }\"\n  FOLLY_HAVE_LINUX_VDSO\n)\n\ncheck_cxx_source_runs(\"\n  #include <cstddef>\n  #include <cwchar>\n  int main(int argc, char** argv) {\n    return wcstol(L\\\"01\\\", nullptr, 10) == 1 ? 0 : 1;\n  }\"\n  FOLLY_HAVE_WCHAR_SUPPORT\n)\n\ncheck_cxx_source_compiles(\"\n  #include <ext/random>\n  int main(int argc, char** argv) {\n    __gnu_cxx::sfmt19937 rng;\n    return 0;\n  }\"\n  FOLLY_HAVE_EXTRANDOM_SFMT19937\n)\n\ncheck_cxx_source_runs(\"\n  #include <stdarg.h>\n  #include <stdio.h>\n\n  int call_vsnprintf(const char* fmt, ...) {\n    char buf[256];\n    va_list ap;\n    va_start(ap, fmt);\n    int result = vsnprintf(buf, sizeof(buf), fmt, ap);\n    va_end(ap);\n    return result;\n  }\n\n  int main(int argc, char** argv) {\n    return call_vsnprintf(\\\"%\\\", 1) < 0 ? 0 : 1;\n  }\"\n  HAVE_VSNPRINTF_ERRORS\n)\n"
  },
  {
    "path": "CMake/FollyFunctions.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfunction(auto_sources RETURN_VALUE PATTERN SOURCE_SUBDIRS)\n  if (\"${SOURCE_SUBDIRS}\" STREQUAL \"RECURSE\")\n    SET(PATH \".\")\n    if (${ARGC} EQUAL 4)\n      list(GET ARGV 3 PATH)\n    endif ()\n  endif()\n\n  if (\"${SOURCE_SUBDIRS}\" STREQUAL \"RECURSE\")\n    unset(${RETURN_VALUE})\n    file(GLOB SUBDIR_FILES \"${PATH}/${PATTERN}\")\n    list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})\n\n    file(GLOB subdirs RELATIVE ${PATH} ${PATH}/*)\n\n    foreach(DIR ${subdirs})\n      if (IS_DIRECTORY ${PATH}/${DIR})\n        if (NOT \"${DIR}\" STREQUAL \"CMakeFiles\")\n          file(GLOB_RECURSE SUBDIR_FILES \"${PATH}/${DIR}/${PATTERN}\")\n          list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})\n        endif()\n      endif()\n    endforeach()\n  else()\n    file(GLOB ${RETURN_VALUE} \"${PATTERN}\")\n\n    foreach (PATH ${SOURCE_SUBDIRS})\n      file(GLOB SUBDIR_FILES \"${PATH}/${PATTERN}\")\n      list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})\n    endforeach()\n  endif ()\n\n  set(${RETURN_VALUE} ${${RETURN_VALUE}} PARENT_SCOPE)\nendfunction(auto_sources)\n\n# Remove all files matching a set of patterns, and,\n# optionally, not matching a second set of patterns,\n# from a set of lists.\n#\n# Example:\n# This will remove all files in the CPP_SOURCES list\n# matching \"/test/\" or \"Test.cpp$\", but not matching\n# \"BobTest.cpp$\".\n# REMOVE_MATCHES_FROM_LISTS(CPP_SOURCES MATCHES \"/test/\" \"Test.cpp$\" IGNORE_MATCHES \"BobTest.cpp$\")\n#\n# Parameters:\n#\n# [...]:\n# The names of the lists to remove matches from.\n#\n# [MATCHES ...]:\n# The matches to remove from the lists.\n#\n# [IGNORE_MATCHES ...]:\n# The matches not to remove, even if they match\n# the main set of matches to remove.\nfunction(REMOVE_MATCHES_FROM_LISTS)\n  set(LISTS_TO_SEARCH)\n  set(MATCHES_TO_REMOVE)\n  set(MATCHES_TO_IGNORE)\n  set(argumentState 0)\n  foreach (arg ${ARGN})\n    if (\"x${arg}\" STREQUAL \"xMATCHES\")\n      set(argumentState 1)\n    elseif (\"x${arg}\" STREQUAL \"xIGNORE_MATCHES\")\n      set(argumentState 2)\n    elseif (argumentState EQUAL 0)\n      list(APPEND LISTS_TO_SEARCH ${arg})\n    elseif (argumentState EQUAL 1)\n      list(APPEND MATCHES_TO_REMOVE ${arg})\n    elseif (argumentState EQUAL 2)\n      list(APPEND MATCHES_TO_IGNORE ${arg})\n    else()\n      message(FATAL_ERROR \"Unknown argument state!\")\n    endif()\n  endforeach()\n\n  foreach (theList ${LISTS_TO_SEARCH})\n    foreach (entry ${${theList}})\n      foreach (match ${MATCHES_TO_REMOVE})\n        if (${entry} MATCHES ${match})\n          set(SHOULD_IGNORE OFF)\n          foreach (ign ${MATCHES_TO_IGNORE})\n            if (${entry} MATCHES ${ign})\n              set(SHOULD_IGNORE ON)\n              break()\n            endif()\n          endforeach()\n\n          if (NOT SHOULD_IGNORE)\n            list(REMOVE_ITEM ${theList} ${entry})\n          endif()\n        endif()\n      endforeach()\n    endforeach()\n    set(${theList} ${${theList}} PARENT_SCOPE)\n  endforeach()\nendfunction()\n\n# Automatically create source_group directives for the sources passed in.\nfunction(auto_source_group rootName rootDir)\n  file(TO_CMAKE_PATH \"${rootDir}\" rootDir)\n  string(LENGTH \"${rootDir}\" rootDirLength)\n  set(sourceGroups)\n  foreach (fil ${ARGN})\n    file(TO_CMAKE_PATH \"${fil}\" filePath)\n    string(FIND \"${filePath}\" \"/\" rIdx REVERSE)\n    if (rIdx EQUAL -1)\n      message(FATAL_ERROR \"Unable to locate the final forward slash in '${filePath}'!\")\n    endif()\n    string(SUBSTRING \"${filePath}\" 0 ${rIdx} filePath)\n\n    string(LENGTH \"${filePath}\" filePathLength)\n    string(FIND \"${filePath}\" \"${rootDir}\" rIdx)\n    if (rIdx EQUAL 0)\n      math(EXPR filePathLength \"${filePathLength} - ${rootDirLength}\")\n      string(SUBSTRING \"${filePath}\" ${rootDirLength} ${filePathLength} fileGroup)\n\n      string(REPLACE \"/\" \"\\\\\" fileGroup \"${fileGroup}\")\n      set(fileGroup \"\\\\${rootName}${fileGroup}\")\n\n      list(FIND sourceGroups \"${fileGroup}\" rIdx)\n      if (rIdx EQUAL -1)\n        list(APPEND sourceGroups \"${fileGroup}\")\n        source_group(\"${fileGroup}\" REGULAR_EXPRESSION \"${filePath}/[^/.]+.(cpp|h)$\")\n      endif()\n    endif()\n  endforeach()\nendfunction()\n\n# CMake is a pain and doesn't have an easy way to install only the files\n# we actually included in our build :(\nfunction(auto_install_files rootName rootDir)\n  file(TO_CMAKE_PATH \"${rootDir}\" rootDir)\n  string(LENGTH \"${rootDir}\" rootDirLength)\n  set(sourceGroups)\n  foreach (fil ${ARGN})\n    file(TO_CMAKE_PATH \"${fil}\" filePath)\n    string(FIND \"${filePath}\" \"/\" rIdx REVERSE)\n    if (rIdx EQUAL -1)\n      message(FATAL_ERROR \"Unable to locate the final forward slash in '${filePath}'!\")\n    endif()\n    string(SUBSTRING \"${filePath}\" 0 ${rIdx} filePath)\n\n    string(LENGTH \"${filePath}\" filePathLength)\n    string(FIND \"${filePath}\" \"${rootDir}\" rIdx)\n    if (rIdx EQUAL 0)\n      math(EXPR filePathLength \"${filePathLength} - ${rootDirLength}\")\n      string(SUBSTRING \"${filePath}\" ${rootDirLength} ${filePathLength} fileGroup)\n      install(FILES ${fil}\n              DESTINATION ${INCLUDE_INSTALL_DIR}/${rootName}${fileGroup})\n    endif()\n  endforeach()\nendfunction()\n\nfunction(folly_define_tests)\n  set(directory_count 0)\n  set(test_count 0)\n  set(currentArg 0)\n  while (currentArg LESS ${ARGC})\n    if (\"x${ARGV${currentArg}}\" STREQUAL \"xDIRECTORY\")\n      math(EXPR currentArg \"${currentArg} + 1\")\n      if (NOT currentArg LESS ${ARGC})\n        message(FATAL_ERROR \"Expected base directory!\")\n      endif()\n\n      set(cur_dir ${directory_count})\n      math(EXPR directory_count \"${directory_count} + 1\")\n      set(directory_${cur_dir}_name \"${ARGV${currentArg}}\")\n      # We need a single list of sources to get source_group to work nicely.\n      set(directory_${cur_dir}_source_list)\n\n      math(EXPR currentArg \"${currentArg} + 1\")\n      while (currentArg LESS ${ARGC})\n        if (\"x${ARGV${currentArg}}\" STREQUAL \"xDIRECTORY\")\n          break()\n        elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xTEST\" OR\n                \"x${ARGV${currentArg}}\" STREQUAL \"xBENCHMARK\")\n          set(cur_test ${test_count})\n          math(EXPR test_count \"${test_count} + 1\")\n\n          set(test_${cur_test}_is_benchmark $<STREQUAL:\"x${ARGV${currentArg}}\",\"xBENCHMARK\">)\n\n          math(EXPR currentArg \"${currentArg} + 1\")\n          if (NOT currentArg LESS ${ARGC})\n            message(FATAL_ERROR \"Expected test name!\")\n          endif()\n\n          set(test_${cur_test}_name \"${ARGV${currentArg}}\")\n          math(EXPR currentArg \"${currentArg} + 1\")\n          set(test_${cur_test}_directory ${cur_dir})\n          set(test_${cur_test}_content_dir)\n          set(test_${cur_test}_headers)\n          set(test_${cur_test}_sources)\n          set(test_${cur_test}_tag)\n\n          set(argumentState 0)\n          while (currentArg LESS ${ARGC})\n            if (\"x${ARGV${currentArg}}\" STREQUAL \"xHEADERS\")\n              set(argumentState 1)\n            elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xSOURCES\")\n              set(argumentState 2)\n            elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xCONTENT_DIR\")\n              math(EXPR currentArg \"${currentArg} + 1\")\n              if (NOT currentArg LESS ${ARGC})\n                message(FATAL_ERROR \"Expected content directory name!\")\n              endif()\n              set(test_${cur_test}_content_dir \"${ARGV${currentArg}}\")\n            elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xTEST\" OR\n                    \"x${ARGV${currentArg}}\" STREQUAL \"xBENCHMARK\" OR\n                    \"x${ARGV${currentArg}}\" STREQUAL \"xDIRECTORY\")\n              break()\n            elseif (argumentState EQUAL 0)\n              if (\"x${ARGV${currentArg}}\" STREQUAL \"xBROKEN\")\n                list(APPEND test_${cur_test}_tag \"BROKEN\")\n              elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xHANGING\")\n                list(APPEND test_${cur_test}_tag \"HANGING\")\n              elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xSLOW\")\n                list(APPEND test_${cur_test}_tag \"SLOW\")\n              elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xWINDOWS_DISABLED\")\n                list(APPEND test_${cur_test}_tag \"WINDOWS_DISABLED\")\n              elseif (\"x${ARGV${currentArg}}\" STREQUAL \"xAPPLE_DISABLED\")\n                list(APPEND test_${cur_test}_tag \"APPLE_DISABLED\")\n              else()\n                message(FATAL_ERROR \"Unknown test tag '${ARGV${currentArg}}'!\")\n              endif()\n            elseif (argumentState EQUAL 1)\n              list(APPEND test_${cur_test}_headers\n                \"${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}\"\n              )\n            elseif (argumentState EQUAL 2)\n              list(APPEND test_${cur_test}_sources\n                \"${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}\"\n              )\n            else()\n              message(FATAL_ERROR \"Unknown argument state!\")\n            endif()\n            math(EXPR currentArg \"${currentArg} + 1\")\n          endwhile()\n\n          list(APPEND directory_${cur_dir}_source_list\n            ${test_${cur_test}_sources} ${test_${cur_test}_headers})\n        else()\n          message(FATAL_ERROR \"Unknown argument inside directory '${ARGV${currentArg}}'!\")\n        endif()\n      endwhile()\n    else()\n      message(FATAL_ERROR \"Unknown argument '${ARGV${currentArg}}'!\")\n    endif()\n  endwhile()\n\n  set(cur_dir 0)\n  while (cur_dir LESS directory_count)\n    source_group(\"\" FILES ${directory_${cur_dir}_source_list})\n    math(EXPR cur_dir \"${cur_dir} + 1\")\n  endwhile()\n\n  set(cur_test 0)\n  while (cur_test LESS test_count)\n    set(cur_test_name ${test_${cur_test}_name})\n    set(cur_dir_name ${directory_${test_${cur_test}_directory}_name})\n    if (\"BROKEN\" IN_LIST test_${cur_test}_tag AND NOT BUILD_BROKEN_TESTS)\n      message(\"Skipping broken test ${cur_dir_name}${cur_test_name}, enable with BUILD_BROKEN_TESTS\")\n    elseif (\"SLOW\" IN_LIST test_${cur_test}_tag AND NOT BUILD_SLOW_TESTS)\n      message(\"Skipping slow test ${cur_dir_name}${cur_test_name}, enable with BUILD_SLOW_TESTS\")\n    elseif (\"HANGING\" IN_LIST test_${cur_test}_tag AND NOT BUILD_HANGING_TESTS)\n      message(\"Skipping hanging test ${cur_dir_name}${cur_test_name}, enable with BUILD_HANGING_TESTS\")\n    elseif (\"WINDOWS_DISABLED\" IN_LIST test_${cur_test}_tag AND WIN32 AND NOT BUILD_WINDOWS_DISABLED)\n      message(\"Skipping windows disabled test ${cur_dir_name}${cur_test_name}, enable with BUILD_WINDOWS_DISABLED\")\n    elseif (\"APPLE_DISABLED\" IN_LIST test_${cur_test}_tag AND APPLE AND NOT BUILD_APPLE_DISABLED)\n      message(\"Skipping apple disabled test ${cur_dir_name}${cur_test_name}, enable with BUILD_APPLE_DISABLED\")\n    elseif (${test_${cur_test}_is_benchmark} AND NOT BUILD_BENCHMARKS)\n      message(\"Skipping benchmark ${cur_dir_name}${cur_test_name}, enable with BUILD_BENCHMARKS\")\n    else()\n      add_executable(${cur_test_name}\n        ${test_${cur_test}_headers}\n        ${test_${cur_test}_sources}\n      )\n      if (NOT ${test_${cur_test}_is_benchmark})\n        if (HAVE_CMAKE_GTEST)\n          # If we have CMake's built-in gtest support use it to add each test\n          # function as a separate test.\n          gtest_add_tests(TARGET ${cur_test_name}\n                          WORKING_DIRECTORY \"${TOP_DIR}\"\n                          TEST_PREFIX \"${cur_test_name}.\"\n                          TEST_LIST test_cases)\n          set_tests_properties(${test_cases} PROPERTIES TIMEOUT 120)\n        else()\n          # Otherwise add each test executable as a single test.\n          add_test(\n            NAME ${cur_test_name}\n            COMMAND ${cur_test_name}\n            WORKING_DIRECTORY \"${TOP_DIR}\"\n          )\n          set_tests_properties(${cur_test_name} PROPERTIES TIMEOUT 120)\n        endif()\n      endif()\n      if (NOT \"x${test_${cur_test}_content_dir}\" STREQUAL \"x\")\n        # Copy the content directory to the output directory tree so that\n        # tests can be run easily from Visual Studio without having to change\n        # the working directory for each test individually.\n        file(\n          COPY \"${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}\"\n          DESTINATION \"${CMAKE_CURRENT_BINARY_DIR}/folly/${cur_dir_name}${test_${cur_test}_content_dir}\"\n        )\n        add_custom_command(TARGET ${cur_test_name} POST_BUILD COMMAND\n          ${CMAKE_COMMAND} ARGS -E copy_directory\n            \"${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}\"\n            \"$<TARGET_FILE_DIR:${cur_test_name}>/folly/${cur_dir_name}${test_${cur_test}_content_dir}\"\n          COMMENT \"Copying test content for ${cur_test_name}\" VERBATIM\n        )\n      endif()\n      # Strip the tailing test directory name for the folder name.\n      string(REPLACE \"test/\" \"\" test_dir_name \"${cur_dir_name}\")\n      set_property(TARGET ${cur_test_name} PROPERTY FOLDER \"Tests/${test_dir_name}\")\n      target_link_libraries(${cur_test_name} PRIVATE folly_test_support)\n      apply_folly_compile_options_to_target(${cur_test_name})\n    endif()\n    math(EXPR cur_test \"${cur_test} + 1\")\n  endwhile()\nendfunction()\n\n# Initialize global property to track all granular component targets\ndefine_property(GLOBAL PROPERTY FOLLY_COMPONENT_TARGETS\n  BRIEF_DOCS \"List of all folly component OBJECT targets\"\n  FULL_DOCS \"Used to aggregate all component targets into the monolithic folly library\"\n)\nset_property(GLOBAL PROPERTY FOLLY_COMPONENT_TARGETS \"\")\n\n# Track deferred dependencies to be linked after all targets are created\n# Each entry is: \"target|visibility|dep1,dep2,dep3\"\ndefine_property(GLOBAL PROPERTY FOLLY_DEFERRED_DEPS\n  BRIEF_DOCS \"List of deferred dependency specifications\"\n  FULL_DOCS \"Format: target|PUBLIC|PRIVATE|dep1,dep2,...\"\n)\nset_property(GLOBAL PROPERTY FOLLY_DEFERRED_DEPS \"\")\n\n# Track INTERFACE targets that need to link to monolithic folly (for shared builds)\ndefine_property(GLOBAL PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS\n  BRIEF_DOCS \"List of granular INTERFACE targets for shared builds\"\n  FULL_DOCS \"These targets will be linked to the monolithic folly library\"\n)\nset_property(GLOBAL PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS \"\")\n\n# Helper to add a folly library target\n# Creates:\n#   1. OBJECT library (${name}_obj) - for composition into monolithic target\n#   2. STATIC library (${name}) - individual .a file for granular linking\n# Usage:\n#   folly_add_library(\n#     NAME iobuf                    # Target suffix (full name: folly_io_iobuf)\n#     TARGET_NAME follybenchmark    # Optional: override computed target name\n#     SRCS Cursor.cpp IOBuf.cpp     # Source files (optional if using AUTO_SOURCES)\n#     HEADERS Cursor.h IOBuf.h      # Header files\n#     AUTO_SOURCES                  # Use auto_sources() instead of explicit SRCS\n#     DEPS folly_lang_bits          # Private dependencies (internal folly targets)\n#     EXPORTED_DEPS folly_range     # Public dependencies (propagated to users)\n#     EXTERNAL_DEPS ${LIBSODIUM_LIBRARIES}  # External library dependencies\n#     EXTERNAL_INCLUDE_DIRS ${LIBSODIUM_INCLUDE_DIRS}  # External include directories\n#     COMPILE_OPTIONS -mpclmul      # Optional compile options for source files\n#     EXCLUDE_FROM_MONOLITH         # Don't include in monolithic folly library\n#   )\nfunction(folly_add_library)\n  cmake_parse_arguments(\n    FOLLY_LIB\n    \"AUTO_SOURCES;EXCLUDE_FROM_MONOLITH\"            # Options\n    \"NAME;TARGET_NAME\"                              # Single-value args\n    \"SRCS;HEADERS;DEPS;EXPORTED_DEPS;EXTERNAL_DEPS;EXTERNAL_INCLUDE_DIRS;COMPILE_OPTIONS\"  # Multi-value args\n    ${ARGN}\n  )\n\n  # Use explicit TARGET_NAME if provided, otherwise compute from directory\n  if(FOLLY_LIB_TARGET_NAME)\n    set(_target_name \"${FOLLY_LIB_TARGET_NAME}\")\n  else()\n    # Compute target name from current directory relative to FOLLY_DIR\n    # e.g., folly/io → folly_io, folly/io/async → folly_io_async\n    file(RELATIVE_PATH _rel_path \"${FOLLY_DIR}\" \"${CMAKE_CURRENT_SOURCE_DIR}\")\n    if(_rel_path STREQUAL \"\")\n      set(_target_name \"folly_${FOLLY_LIB_NAME}\")\n    else()\n      string(REPLACE \"/\" \"_\" _prefix \"${_rel_path}\")\n      set(_target_name \"folly_${_prefix}_${FOLLY_LIB_NAME}\")\n    endif()\n  endif()\n\n  # Handle source collection\n  if(FOLLY_LIB_AUTO_SOURCES)\n    auto_sources(_srcs \"*.cpp\" \"\" \"${CMAKE_CURRENT_SOURCE_DIR}\")\n    REMOVE_MATCHES_FROM_LISTS(_srcs MATCHES \"test/\" \"Test.cpp$\" \"Benchmark.cpp$\")\n  else()\n    set(_srcs ${FOLLY_LIB_SRCS})\n  endif()\n\n  # Skip if no sources (header-only library)\n  list(LENGTH _srcs _src_count)\n  if(_src_count EQUAL 0)\n    # Header-only: create INTERFACE library\n    add_library(${_target_name} INTERFACE)\n    target_include_directories(${_target_name}\n      INTERFACE\n        $<BUILD_INTERFACE:${TOP_DIR}>\n        $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>\n        $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>\n    )\n    target_link_libraries(${_target_name}\n      INTERFACE folly_deps ${FOLLY_LIB_EXPORTED_DEPS} ${FOLLY_LIB_EXTERNAL_DEPS}\n    )\n    # Track external deps for monolithic library (header-only deps are used transitively)\n    if(FOLLY_LIB_EXTERNAL_DEPS)\n      set_property(GLOBAL APPEND PROPERTY FOLLY_MONOLITHIC_EXTERNAL_DEPS ${FOLLY_LIB_EXTERNAL_DEPS})\n    endif()\n    install(\n      TARGETS ${_target_name}\n      EXPORT folly\n    )\n    add_library(Folly::${_target_name} ALIAS ${_target_name})\n    return()\n  endif()\n\n  # 1. Create OBJECT library (for composition into monolithic target)\n  set(_obj_target \"${_target_name}_obj\")\n  add_library(${_obj_target} OBJECT ${_srcs} ${FOLLY_LIB_HEADERS})\n  set_property(TARGET ${_obj_target} PROPERTY VERSION ${PACKAGE_VERSION})\n  if(BUILD_SHARED_LIBS)\n    set_property(TARGET ${_obj_target} PROPERTY POSITION_INDEPENDENT_CODE ON)\n  endif()\n\n  target_include_directories(${_obj_target}\n    PUBLIC\n      $<BUILD_INTERFACE:${TOP_DIR}>\n      $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>\n      $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>\n  )\n  apply_folly_compile_options_to_target(${_obj_target})\n\n  # Apply optional compile options\n  if(FOLLY_LIB_COMPILE_OPTIONS)\n    target_compile_options(${_obj_target} PRIVATE ${FOLLY_LIB_COMPILE_OPTIONS})\n  endif()\n\n  # Link dependencies on OBJECT library\n  # External deps via folly_deps are always available\n  # Internal folly deps are deferred until all targets exist\n  target_link_libraries(${_obj_target}\n    PUBLIC folly_deps\n  )\n\n  # Link external dependencies (e.g., libsodium, openssl) directly\n  if(FOLLY_LIB_EXTERNAL_DEPS)\n    target_link_libraries(${_obj_target}\n      PUBLIC ${FOLLY_LIB_EXTERNAL_DEPS}\n    )\n  endif()\n\n  # Add external include directories (e.g., libsodium include dirs)\n  if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)\n    target_include_directories(${_obj_target}\n      PUBLIC ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}\n    )\n  endif()\n\n  # Defer internal folly dependencies until all targets are created\n  # (Only for static builds - for shared builds, OBJECT targets are bundled into monolithic folly)\n  if(NOT BUILD_SHARED_LIBS)\n    if(FOLLY_LIB_EXPORTED_DEPS)\n      # Join deps with comma, store as \"target|PUBLIC|dep1,dep2,...\"\n      list(JOIN FOLLY_LIB_EXPORTED_DEPS \",\" _deps_str)\n      set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS\n        \"${_obj_target}|PUBLIC|${_deps_str}\"\n      )\n    endif()\n    if(FOLLY_LIB_DEPS)\n      list(JOIN FOLLY_LIB_DEPS \",\" _deps_str)\n      set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS\n        \"${_obj_target}|PRIVATE|${_deps_str}\"\n      )\n    endif()\n  endif()\n\n  # Track OBJECT target for monolithic aggregation (unless excluded)\n  if(NOT FOLLY_LIB_EXCLUDE_FROM_MONOLITH)\n    set_property(GLOBAL APPEND PROPERTY FOLLY_COMPONENT_TARGETS ${_obj_target})\n    # Track external deps for the monolithic library\n    if(FOLLY_LIB_EXTERNAL_DEPS)\n      set_property(GLOBAL APPEND PROPERTY FOLLY_MONOLITHIC_EXTERNAL_DEPS ${FOLLY_LIB_EXTERNAL_DEPS})\n    endif()\n  endif()\n\n  # 2. Create the granular library target\n  if(BUILD_SHARED_LIBS AND NOT FOLLY_LIB_EXCLUDE_FROM_MONOLITH)\n    # For shared builds: create INTERFACE library that will link to monolithic folly\n    # This avoids duplicating symbols between granular and monolithic libraries\n    add_library(${_target_name} INTERFACE)\n\n    target_include_directories(${_target_name}\n      INTERFACE\n        $<BUILD_INTERFACE:${TOP_DIR}>\n        $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>\n        $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>\n    )\n\n    # Add external include directories for INTERFACE target\n    if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)\n      target_include_directories(${_target_name}\n        INTERFACE ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}\n      )\n    endif()\n\n    # Track this target to link to folly after monolithic library is created\n    set_property(GLOBAL APPEND PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS ${_target_name})\n\n    # Install the INTERFACE library\n    install(\n      TARGETS ${_target_name}\n      EXPORT folly\n    )\n  elseif(BUILD_SHARED_LIBS AND FOLLY_LIB_EXCLUDE_FROM_MONOLITH)\n    # For excluded targets in shared builds: create SHARED library with actual code\n    # These are NOT in the monolithic folly, so they need their own implementation\n    add_library(${_target_name} SHARED $<TARGET_OBJECTS:${_obj_target}>)\n    set_property(TARGET ${_target_name} PROPERTY VERSION ${PACKAGE_VERSION})\n\n    target_include_directories(${_target_name}\n      PUBLIC\n        $<BUILD_INTERFACE:${TOP_DIR}>\n        $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>\n        $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>\n    )\n\n    # Link to folly_deps (external dependencies)\n    target_link_libraries(${_target_name}\n      PUBLIC folly_deps\n    )\n\n    # Link external dependencies\n    if(FOLLY_LIB_EXTERNAL_DEPS)\n      target_link_libraries(${_target_name}\n        PUBLIC ${FOLLY_LIB_EXTERNAL_DEPS}\n      )\n    endif()\n\n    # Add external include directories\n    if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)\n      target_include_directories(${_target_name}\n        PUBLIC ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}\n      )\n    endif()\n\n    # Defer linking to folly (created later by folly_create_monolithic_library)\n    # Also defer internal folly dependencies\n    set(_all_deps \"folly\")\n    if(FOLLY_LIB_EXPORTED_DEPS)\n      list(APPEND _all_deps ${FOLLY_LIB_EXPORTED_DEPS})\n    endif()\n    list(JOIN _all_deps \",\" _deps_str)\n    set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS\n      \"${_target_name}|PUBLIC|${_deps_str}\"\n    )\n    if(FOLLY_LIB_DEPS)\n      list(JOIN FOLLY_LIB_DEPS \",\" _deps_str)\n      set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS\n        \"${_target_name}|PRIVATE|${_deps_str}\"\n      )\n    endif()\n\n    # Install the SHARED library\n    install(\n      TARGETS ${_target_name}\n      EXPORT folly\n      LIBRARY DESTINATION ${LIB_INSTALL_DIR}\n      ARCHIVE DESTINATION ${LIB_INSTALL_DIR}\n    )\n  else()\n    # For static builds: create STATIC library (individual .a file for granular linking)\n    add_library(${_target_name} STATIC $<TARGET_OBJECTS:${_obj_target}>)\n    set_property(TARGET ${_target_name} PROPERTY VERSION ${PACKAGE_VERSION})\n\n    target_include_directories(${_target_name}\n      PUBLIC\n        $<BUILD_INTERFACE:${TOP_DIR}>\n        $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>\n        $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>\n    )\n\n    # Link external dependencies on STATIC library\n    # Internal folly deps are deferred (see above)\n    target_link_libraries(${_target_name}\n      PUBLIC folly_deps\n    )\n\n    # Link external dependencies (e.g., libsodium, openssl) directly\n    if(FOLLY_LIB_EXTERNAL_DEPS)\n      target_link_libraries(${_target_name}\n        PUBLIC ${FOLLY_LIB_EXTERNAL_DEPS}\n      )\n    endif()\n\n    # Add external include directories for STATIC target\n    if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)\n      target_include_directories(${_target_name}\n        PUBLIC ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}\n      )\n    endif()\n\n    # Defer internal folly dependencies for STATIC library too\n    if(FOLLY_LIB_EXPORTED_DEPS)\n      list(JOIN FOLLY_LIB_EXPORTED_DEPS \",\" _deps_str)\n      set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS\n        \"${_target_name}|PUBLIC|${_deps_str}\"\n      )\n    endif()\n    if(FOLLY_LIB_DEPS)\n      list(JOIN FOLLY_LIB_DEPS \",\" _deps_str)\n      set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS\n        \"${_target_name}|PRIVATE|${_deps_str}\"\n      )\n    endif()\n\n    # Install the STATIC library\n    install(\n      TARGETS ${_target_name}\n      EXPORT folly\n      LIBRARY DESTINATION ${LIB_INSTALL_DIR}\n      ARCHIVE DESTINATION ${LIB_INSTALL_DIR}\n    )\n  endif()\n\n  # Create alias for the library\n  add_library(Folly::${_target_name} ALIAS ${_target_name})\nendfunction()\n\n# Resolve all deferred dependencies after all targets have been created\n# Call this after all add_subdirectory() calls\nfunction(folly_resolve_deferred_dependencies)\n  # Allow linking targets defined in other directories\n  cmake_policy(SET CMP0079 NEW)\n\n  get_property(_deferred_deps GLOBAL PROPERTY FOLLY_DEFERRED_DEPS)\n\n  foreach(_spec IN LISTS _deferred_deps)\n    # Parse the spec: \"target|visibility|dep1,dep2,...\"\n    string(REPLACE \"|\" \";\" _parts \"${_spec}\")\n    list(LENGTH _parts _len)\n    if(_len LESS 3)\n      continue()\n    endif()\n\n    list(GET _parts 0 _target)\n    list(GET _parts 1 _visibility)\n    list(GET _parts 2 _deps_str)\n\n    # Split deps by comma\n    string(REPLACE \",\" \";\" _deps \"${_deps_str}\")\n\n    # Filter to only existing targets (skip deps that weren't generated)\n    set(_valid_deps \"\")\n    foreach(_dep IN LISTS _deps)\n      if(TARGET ${_dep})\n        list(APPEND _valid_deps ${_dep})\n      endif()\n    endforeach()\n\n    if(_valid_deps)\n      target_link_libraries(${_target} ${_visibility} ${_valid_deps})\n    endif()\n  endforeach()\nendfunction()\n\n# Create the monolithic folly library from all component OBJECT libraries\n# Call this after all add_subdirectory() calls and folly_resolve_deferred_dependencies()\nfunction(folly_create_monolithic_library)\n  get_property(_component_targets GLOBAL PROPERTY FOLLY_COMPONENT_TARGETS)\n\n  # Collect all object files from component targets\n  set(_all_objects)\n  foreach(_target IN LISTS _component_targets)\n    list(APPEND _all_objects $<TARGET_OBJECTS:${_target}>)\n  endforeach()\n\n  # Create the monolithic library\n  add_library(folly ${_all_objects})\n  if(BUILD_SHARED_LIBS)\n    set_property(TARGET folly PROPERTY POSITION_INDEPENDENT_CODE ON)\n  endif()\n  set_property(TARGET folly PROPERTY VERSION ${PACKAGE_VERSION})\n\n  apply_folly_compile_options_to_target(folly)\n  target_compile_features(folly INTERFACE cxx_generic_lambdas)\n\n  target_include_directories(folly\n    PUBLIC\n      $<BUILD_INTERFACE:${TOP_DIR}>\n      $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>\n      $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>\n  )\n\n  target_link_libraries(folly PUBLIC folly_deps)\n\n  # Link all external dependencies that were tracked from component targets\n  get_property(_external_deps GLOBAL PROPERTY FOLLY_MONOLITHIC_EXTERNAL_DEPS)\n  if(_external_deps)\n    list(REMOVE_DUPLICATES _external_deps)\n    target_link_libraries(folly PUBLIC ${_external_deps})\n  endif()\n\n  # Create alias for consistency\n  add_library(Folly::folly ALIAS folly)\n\n  # For shared builds: link all granular INTERFACE targets to the monolithic library\n  if(BUILD_SHARED_LIBS)\n    # CMP0079: target_link_libraries allows use with targets in other directories\n    cmake_policy(SET CMP0079 NEW)\n    get_property(_interface_targets GLOBAL PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS)\n    foreach(_target IN LISTS _interface_targets)\n      target_link_libraries(${_target} INTERFACE folly)\n    endforeach()\n  endif()\nendfunction()\n"
  },
  {
    "path": "CMake/GenPkgConfig.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Generate variables that can be used to help emit a pkg-config file\n# using configure_file().\n#\n# Usage: gen_pkgconfig_vars(VAR_PREFIX target)\n#\n# This will set two variables in the caller scope:\n# ${VAR_PREFIX}_CFLAGS: set to the compile flags computed from the specified\n#   target\n# ${VAR_PREFIX}_PRIVATE_LIBS: set to the linker flags needed for static\n#   linking computed from the specified target\nfunction(gen_pkgconfig_vars)\n  if (NOT ${ARGC} EQUAL 2)\n    message(FATAL_ERROR \"gen_pkgconfig_vars() requires exactly 2 arguments\")\n  endif()\n  set(var_prefix \"${ARGV0}\")\n  set(target \"${ARGV1}\")\n\n  get_target_property(target_cflags \"${target}\" INTERFACE_COMPILE_OPTIONS)\n  if(target_cflags)\n    list(APPEND cflags \"${target_cflags}\")\n  endif()\n  get_target_property(\n    target_inc_dirs \"${target}\" INTERFACE_INCLUDE_DIRECTORIES)\n  if(target_inc_dirs)\n    list(APPEND include_dirs \"${target_inc_dirs}\")\n  endif()\n  get_target_property(target_defns \"${target}\" INTERFACE_COMPILE_DEFINITIONS)\n  if(target_defns)\n    list(APPEND definitions \"${target_defns}\")\n  endif()\n\n  # The INTERFACE_LINK_LIBRARIES list is unfortunately somewhat awkward to\n  # process.  Entries in this list may be any of\n  # - target names\n  # - absolute paths to a library file\n  # - plain library names that need \"-l\" prepended\n  # - other linker flags starting with \"-\"\n  #\n  # Walk through each entry and transform it into the desired arguments\n  get_target_property(link_libs \"${target}\" INTERFACE_LINK_LIBRARIES)\n  if(link_libs)\n    foreach(lib_arg IN LISTS link_libs)\n      if(TARGET \"${lib_arg}\")\n        # Add any compile options specified in the targets\n        # INTERFACE_COMPILE_OPTIONS.  We don't need to process its\n        # INTERFACE_LINK_LIBRARIES property, since our INTERFACE_LINK_LIBRARIES\n        # will already include its entries transitively.\n        get_target_property(lib_cflags \"${lib_arg}\" INTERFACE_COMPILE_OPTIONS)\n        if(lib_cflags)\n          list(APPEND cflags \"${lib_cflags}\")\n        endif()\n        get_target_property(lib_defs \"${lib_arg}\"\n          INTERFACE_COMPILE_DEFINITIONS)\n        if(lib_defs)\n          list(APPEND definitions \"${lib_defs}\")\n        endif()\n      elseif(lib_arg MATCHES \"^[-/]\")\n        list(APPEND private_libs \"${lib_arg}\")\n      else()\n        list(APPEND private_libs \"-l${lib_arg}\")\n      endif()\n    endforeach()\n  endif()\n\n  list(APPEND cflags \"${CMAKE_REQUIRED_FLAGS}\")\n  if(definitions)\n    list(REMOVE_DUPLICATES definitions)\n    foreach(def_arg IN LISTS definitions)\n      list(APPEND cflags \"-D${def_arg}\")\n    endforeach()\n  endif()\n  if(include_dirs)\n    list(REMOVE_DUPLICATES include_dirs)\n    foreach(inc_dir IN LISTS include_dirs)\n      list(APPEND cflags \"-I${inc_dir}\")\n    endforeach()\n  endif()\n\n  # Set the output variables\n  string(REPLACE \";\" \" \" cflags \"${cflags}\")\n  string(REPLACE \";\" \" \" private_libs \"${private_libs}\")\n\n  # Since CMake 3.18 FindThreads may include a generator expression requiring\n  # a target, which gets propagated to us through INTERFACE_COMPILE_OPTIONS.\n  # Before CMake 3.19 there's no way to solve this in a general way, so we\n  # work around the specific case. See #1414 and CMake bug #21074.\n  if(CMAKE_VERSION VERSION_LESS 3.19)\n    string(REPLACE\n      \"<COMPILE_LANG_AND_ID:CUDA,NVIDIA>\" \"<COMPILE_LANGUAGE:CUDA>\"\n      cflags \"${cflags}\"\n    )\n\n  endif()\n  # patch for fmt's generator expression\n  if (MSVC)\n    # fmt 11.0.3 and above\n    string(REPLACE \"$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>\" \"/utf-8\" cflags \"${cflags}\")\n    # fmt 11.0.2 and below\n    string(REPLACE \"$<$<COMPILE_LANGUAGE:CXX>:/utf-8>\" \"/utf-8\" cflags \"${cflags}\")\n  endif()\n\n  set(\"${var_prefix}_CFLAGS\" \"${cflags}\" PARENT_SCOPE)\n  set(\"${var_prefix}_PRIVATE_LIBS\" \"${private_libs}\" PARENT_SCOPE)\nendfunction()\n"
  },
  {
    "path": "CMake/folly-config.cmake.in",
    "content": "# CMake configuration file for folly\n#\n# This provides the Folly::folly target, which you can depend on by adding it\n# to your target_link_libraries().\n#\n# It also defines the following variables, although using these directly is not\n# necessary if you use the Folly::folly target instead.\n#  FOLLY_INCLUDE_DIR\n#  FOLLY_LIBRARIES\n\n@PACKAGE_INIT@\n\ninclude(CMakeFindDependencyMacro)\n\nset_and_check(FOLLY_INCLUDE_DIRS \"@PACKAGE_INCLUDE_INSTALL_DIR@\")\nset_and_check(FOLLY_INCLUDE_DIR \"@PACKAGE_INCLUDE_INSTALL_DIR@\")\nset_and_check(FOLLY_CMAKE_DIR \"@PACKAGE_CMAKE_INSTALL_DIR@\")\n\n# find_dependency() ends up changing PACKAGE_PREFIX_DIR, so save\n# folly's prefix directory in the FOLLY_PREFIX_DIR variable\nset(FOLLY_PREFIX_DIR \"${PACKAGE_PREFIX_DIR}\")\n\n# Find glog before loading targets, since targets reference the glog::glog target\nif(NOT TARGET glog::glog)\n  find_package(Glog QUIET)\nendif()\n\n# Include the folly-targets.cmake file, which is generated from our CMake rules\nif (NOT TARGET Folly::folly)\n  include(\"${FOLLY_CMAKE_DIR}/folly-targets.cmake\")\nendif()\n\n# Set FOLLY_LIBRARIES from our Folly::folly target\nset(FOLLY_LIBRARIES Folly::folly)\n\n# Find folly's dependencies\nfind_dependency(fmt)\n\nset(Boost_USE_STATIC_LIBS \"@FOLLY_BOOST_LINK_STATIC@\")\nfind_package(Boost 1.69.0 REQUIRED\n  COMPONENTS\n    context\n    filesystem\n    program_options\n    regex\n    thread\n)\n\nif (NOT folly_FIND_QUIETLY)\n  message(STATUS \"Found folly: ${FOLLY_PREFIX_DIR}\")\nendif()\n"
  },
  {
    "path": "CMake/folly-config.h.cmake",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#ifdef __APPLE__\n#include <TargetConditionals.h> // @manual\n#endif\n\n#if !defined(FOLLY_MOBILE)\n#if defined(__ANDROID__) || \\\n    (defined(__APPLE__) &&  \\\n     (TARGET_IPHONE_SIMULATOR || TARGET_OS_SIMULATOR || TARGET_OS_IPHONE))\n#define FOLLY_MOBILE 1\n#else\n#define FOLLY_MOBILE 0\n#endif\n#endif // FOLLY_MOBILE\n\n#cmakedefine01 FOLLY_HAVE_PTHREAD\n#cmakedefine FOLLY_HAVE_PTHREAD_ATFORK 1\n\n#cmakedefine FOLLY_HAVE_LIBGFLAGS 1\n\n#cmakedefine FOLLY_HAVE_LIBGLOG 1\n\n#cmakedefine FOLLY_USE_JEMALLOC 1\n\n#if __has_include(<features.h>)\n#include <features.h>\n#endif\n\n#cmakedefine FOLLY_HAVE_ACCEPT4 1\n#cmakedefine01 FOLLY_HAVE_GETRANDOM\n#cmakedefine FOLLY_HAVE_PREADV 1\n#cmakedefine FOLLY_HAVE_PWRITEV 1\n#cmakedefine01 FOLLY_HAVE_CLOCK_GETTIME\n#cmakedefine FOLLY_HAVE_PIPE2 1\n\n#cmakedefine FOLLY_HAVE_IFUNC 1\n#cmakedefine FOLLY_HAVE_UNALIGNED_ACCESS 1\n#cmakedefine FOLLY_HAVE_VLA 1\n#cmakedefine01 FOLLY_HAVE_WEAK_SYMBOLS\n#cmakedefine FOLLY_HAVE_LINUX_VDSO 1\n#cmakedefine FOLLY_HAVE_MALLOC_USABLE_SIZE 1\n#cmakedefine FOLLY_HAVE_INT128_T 1\n#cmakedefine FOLLY_HAVE_WCHAR_SUPPORT 1\n#cmakedefine FOLLY_HAVE_EXTRANDOM_SFMT19937 1\n#cmakedefine HAVE_VSNPRINTF_ERRORS 1\n\n#cmakedefine FOLLY_HAVE_LIBUNWIND 1\n#cmakedefine01 FOLLY_HAVE_DWARF\n#cmakedefine01 FOLLY_HAVE_ELF\n#cmakedefine FOLLY_HAVE_SWAPCONTEXT 1\n#cmakedefine FOLLY_HAVE_BACKTRACE 1\n#cmakedefine FOLLY_USE_SYMBOLIZER 1\n#define FOLLY_DEMANGLE_MAX_SYMBOL_SIZE 1024\n\n#cmakedefine FOLLY_HAVE_SHADOW_LOCAL_WARNINGS 1\n\n#cmakedefine01 FOLLY_HAVE_LIBLZ4\n#cmakedefine01 FOLLY_HAVE_LIBLZMA\n#cmakedefine01 FOLLY_HAVE_LIBSNAPPY\n#cmakedefine01 FOLLY_HAVE_LIBZ\n#cmakedefine01 FOLLY_HAVE_LIBZSTD\n#cmakedefine01 FOLLY_HAVE_LIBBZ2\n\n#cmakedefine01 FOLLY_LIBRARY_SANITIZE_ADDRESS\n\n#cmakedefine FOLLY_SUPPORT_SHARED_LIBRARY 1\n\n#cmakedefine01 FOLLY_HAVE_LIBRT\n\n#cmakedefine01 FOLLY_HAVE_VSOCK\n"
  },
  {
    "path": "CMake/folly-deps.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ninclude(CheckCXXSourceCompiles)\ninclude(CheckCXXSymbolExists)\ninclude(CheckIncludeFileCXX)\ninclude(CheckFunctionExists)\ninclude(CMakePushCheckState)\n\nset(\n  BOOST_LINK_STATIC \"auto\"\n  CACHE STRING\n  \"Whether to link against boost statically or dynamically.\"\n)\nif(\"${BOOST_LINK_STATIC}\" STREQUAL \"auto\")\n  # Default to linking boost statically on Windows with MSVC\n  if(MSVC)\n    set(FOLLY_BOOST_LINK_STATIC ON)\n  else()\n    set(FOLLY_BOOST_LINK_STATIC OFF)\n  endif()\nelse()\n  set(FOLLY_BOOST_LINK_STATIC \"${BOOST_LINK_STATIC}\")\nendif()\nset(Boost_USE_STATIC_LIBS \"${FOLLY_BOOST_LINK_STATIC}\")\n\n# Note: We find these components so the CMake targets exist, but we don't\n# link them globally. Targets that need specific Boost libraries should\n# add them to their EXTERNAL_DEPS (e.g., Boost::regex, Boost::context).\n# Boost::thread is needed by Windows pthread compatibility layer.\nset(FOLLY_BOOST_COMPONENTS\n    context\n    filesystem\n    program_options\n    regex\n)\nif(WIN32)\n  list(APPEND FOLLY_BOOST_COMPONENTS thread)\nendif()\n\nfind_package(Boost 1.69.0 REQUIRED\n  COMPONENTS\n    ${FOLLY_BOOST_COMPONENTS}\n)\n# Only add include directories globally, not libraries\n# Per-target Boost dependencies are specified via EXTERNAL_DEPS\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS})\n\nfind_package(DoubleConversion MODULE REQUIRED)\nlist(APPEND FOLLY_LINK_LIBRARIES ${DOUBLE_CONVERSION_LIBRARY})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${DOUBLE_CONVERSION_INCLUDE_DIR})\n\nfind_package(FastFloat MODULE REQUIRED)\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${FASTFLOAT_INCLUDE_DIR})\n\nfind_package(Gflags MODULE)\nset(FOLLY_HAVE_LIBGFLAGS ${LIBGFLAGS_FOUND})\nif(LIBGFLAGS_FOUND)\n  list(APPEND FOLLY_LINK_LIBRARIES ${LIBGFLAGS_LIBRARY})\n  list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBGFLAGS_INCLUDE_DIR})\n  set(FOLLY_LIBGFLAGS_LIBRARY ${LIBGFLAGS_LIBRARY})\n  set(FOLLY_LIBGFLAGS_INCLUDE ${LIBGFLAGS_INCLUDE_DIR})\nendif()\n\nfind_package(Glog MODULE)\nset(FOLLY_HAVE_LIBGLOG ${GLOG_FOUND})\nlist(APPEND FOLLY_LINK_LIBRARIES glog::glog)\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${GLOG_INCLUDE_DIR})\n# Glog 0.7+ requires GLOG_USE_GLOG_EXPORT to be defined so that headers\n# include glog/export.h which defines GLOG_EXPORT.\nif (EXISTS \"${GLOG_INCLUDE_DIR}/glog/export.h\")\n  list(APPEND FOLLY_CXX_FLAGS -DGLOG_USE_GLOG_EXPORT)\nendif()\n\nfind_package(LibEvent MODULE REQUIRED)\nlist(APPEND FOLLY_LINK_LIBRARIES ${LIBEVENT_LIB})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBEVENT_INCLUDE_DIR})\n\nfind_package(ZLIB MODULE)\nset(FOLLY_HAVE_LIBZ ${ZLIB_FOUND})\nif (ZLIB_FOUND)\n  list(APPEND FOLLY_INCLUDE_DIRECTORIES ${ZLIB_INCLUDE_DIRS})\n  list(APPEND FOLLY_LINK_LIBRARIES ${ZLIB_LIBRARIES})\n  list(APPEND CMAKE_REQUIRED_LIBRARIES ${ZLIB_LIBRARIES})\nendif()\n\nfind_package(OpenSSL 1.1.1 MODULE REQUIRED)\nlist(APPEND FOLLY_LINK_LIBRARIES ${OPENSSL_LIBRARIES})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})\nlist(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})\nlist(APPEND CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})\nlist(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})\nlist(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})\nif (ZLIB_FOUND)\n    list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${ZLIB_LIBRARIES})\nendif()\n\nfind_package(BZip2 MODULE)\nset(FOLLY_HAVE_LIBBZ2 ${BZIP2_FOUND})\nif (BZIP2_FOUND)\n  list(APPEND FOLLY_INCLUDE_DIRECTORIES ${BZIP2_INCLUDE_DIRS})\n  list(APPEND FOLLY_LINK_LIBRARIES ${BZIP2_LIBRARIES})\nendif()\n\nfind_package(LibLZMA MODULE)\nset(FOLLY_HAVE_LIBLZMA ${LIBLZMA_FOUND})\nif (LIBLZMA_FOUND)\n  list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBLZMA_INCLUDE_DIRS})\n  list(APPEND FOLLY_LINK_LIBRARIES ${LIBLZMA_LIBRARIES})\nendif()\n\nfind_package(LZ4 MODULE)\nset(FOLLY_HAVE_LIBLZ4 ${LZ4_FOUND})\nif (LZ4_FOUND)\n  list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LZ4_INCLUDE_DIR})\n  list(APPEND FOLLY_LINK_LIBRARIES ${LZ4_LIBRARY})\nendif()\n\nfind_package(Zstd MODULE)\nset(FOLLY_HAVE_LIBZSTD ${ZSTD_FOUND})\nif(ZSTD_FOUND)\n  list(APPEND FOLLY_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR})\n  list(APPEND FOLLY_LINK_LIBRARIES ${ZSTD_LIBRARY})\nendif()\n\nfind_package(Snappy MODULE)\nset(FOLLY_HAVE_LIBSNAPPY ${SNAPPY_FOUND})\nif (SNAPPY_FOUND)\n  list(APPEND FOLLY_INCLUDE_DIRECTORIES ${SNAPPY_INCLUDE_DIR})\n  list(APPEND FOLLY_LINK_LIBRARIES ${SNAPPY_LIBRARY})\nendif()\n\nfind_package(LibDwarf)\nlist(APPEND FOLLY_LINK_LIBRARIES ${LIBDWARF_LIBRARIES})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBDWARF_INCLUDE_DIRS})\n\nfind_package(Libiberty)\nlist(APPEND FOLLY_LINK_LIBRARIES ${LIBIBERTY_LIBRARIES})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBIBERTY_INCLUDE_DIRS})\n\nfind_package(LibAIO)\nlist(APPEND FOLLY_LINK_LIBRARIES ${LIBAIO_LIBRARIES})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBAIO_INCLUDE_DIRS})\n\nfind_package(LibUring)\nlist(APPEND FOLLY_LINK_LIBRARIES ${LIBURING_LIBRARIES})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBURING_INCLUDE_DIRS})\n\nfind_package(Libsodium)\nlist(APPEND FOLLY_LINK_LIBRARIES ${LIBSODIUM_LIBRARIES})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBSODIUM_INCLUDE_DIRS})\n\nlist(APPEND FOLLY_LINK_LIBRARIES ${CMAKE_DL_LIBS})\nlist(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS})\n\nif (PYTHON_EXTENSIONS)\n  find_package(Python3 COMPONENTS Interpreter Development REQUIRED)\n  find_package(Cython 0.26 REQUIRED)\nendif ()\n\nfind_package(LibUnwind)\nlist(APPEND FOLLY_LINK_LIBRARIES ${LIBUNWIND_LIBRARIES})\nlist(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBUNWIND_INCLUDE_DIRS})\nif (LIBUNWIND_FOUND)\n  set(FOLLY_HAVE_LIBUNWIND ON)\nendif()\nif (CMAKE_SYSTEM_NAME MATCHES \"FreeBSD\")\n  list(APPEND FOLLY_LINK_LIBRARIES \"execinfo\")\nendif ()\n\ncmake_push_check_state()\nset(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE)\ncheck_cxx_symbol_exists(swapcontext ucontext.h FOLLY_HAVE_SWAPCONTEXT)\ncmake_pop_check_state()\n\nset(FOLLY_USE_SYMBOLIZER OFF)\nCHECK_INCLUDE_FILE_CXX(elf.h FOLLY_HAVE_ELF)\nfind_package(Backtrace)\n\nset(FOLLY_HAVE_BACKTRACE ${Backtrace_FOUND})\nset(FOLLY_HAVE_DWARF ${LIBDWARF_FOUND})\nif (NOT WIN32 AND NOT APPLE)\n  set(FOLLY_USE_SYMBOLIZER ON)\nendif()\nmessage(STATUS \"Setting FOLLY_USE_SYMBOLIZER: ${FOLLY_USE_SYMBOLIZER}\")\nmessage(STATUS \"Setting FOLLY_HAVE_ELF: ${FOLLY_HAVE_ELF}\")\nmessage(STATUS \"Setting FOLLY_HAVE_DWARF: ${FOLLY_HAVE_DWARF}\")\n\n# Using clang with libstdc++ requires explicitly linking against libatomic\ncheck_cxx_source_compiles(\"\n  #include <atomic>\n  int main(int argc, char** argv) {\n    std::atomic<uint8_t> a1;\n    std::atomic<uint16_t> a2;\n    std::atomic<uint32_t> a4;\n    std::atomic<uint64_t> a8;\n    struct Test { bool val; };\n    std::atomic<Test> s;\n    return a1++ + a2++ + a4++ + a8++ + unsigned(s.is_lock_free());\n  }\"\n  FOLLY_CPP_ATOMIC_BUILTIN\n)\nif(NOT FOLLY_CPP_ATOMIC_BUILTIN)\n  list(APPEND CMAKE_REQUIRED_LIBRARIES atomic)\n  list(APPEND FOLLY_LINK_LIBRARIES atomic)\n  set(ATOMIC_LIBRARY \"atomic\")\n  check_cxx_source_compiles(\"\n    #include <atomic>\n    int main(int argc, char** argv) {\n      std::atomic<uint8_t> a1;\n      std::atomic<uint16_t> a2;\n      std::atomic<uint32_t> a4;\n      std::atomic<uint64_t> a8;\n      struct Test { bool val; };\n      std::atomic<Test> s;\n      return a1++ + a2++ + a4++ + a8++ + unsigned(s.is_lock_free());\n    }\"\n    FOLLY_CPP_ATOMIC_WITH_LIBATOMIC\n  )\n  if (NOT FOLLY_CPP_ATOMIC_WITH_LIBATOMIC)\n    message(\n      FATAL_ERROR \"unable to link C++ std::atomic code: you may need \\\n      to install GNU libatomic\"\n    )\n  endif()\nendif()\n\ncheck_cxx_source_compiles(\"\n  #include <type_traits>\n  #if _GLIBCXX_RELEASE\n  int main() {}\n  #endif\"\n  FOLLY_STDLIB_LIBSTDCXX\n)\ncheck_cxx_source_compiles(\"\n  #include <type_traits>\n  #if _GLIBCXX_RELEASE >= 9\n  int main() {}\n  #endif\"\n  FOLLY_STDLIB_LIBSTDCXX_GE_9\n)\ncheck_cxx_source_compiles(\"\n  #include <type_traits>\n  #if _LIBCPP_VERSION\n  int main() {}\n  #endif\"\n  FOLLY_STDLIB_LIBCXX\n)\ncheck_cxx_source_compiles(\"\n  #include <type_traits>\n  #if _LIBCPP_VERSION >= 9000\n  int main() {}\n  #endif\"\n  FOLLY_STDLIB_LIBCXX_GE_9\n)\ncheck_cxx_source_compiles(\"\n  #include <type_traits>\n  #if _CPPLIB_VER\n  int main() {}\n  #endif\"\n  FOLLY_STDLIB_LIBCPP\n)\n\nif (APPLE)\n  list (APPEND CMAKE_REQUIRED_LIBRARIES c++abi)\n  list (APPEND FOLLY_LINK_LIBRARIES c++abi)\nendif ()\n\nif (FOLLY_STDLIB_LIBSTDCXX AND NOT FOLLY_STDLIB_LIBSTDCXX_GE_9)\n  list (APPEND CMAKE_REQUIRED_LIBRARIES stdc++fs)\n  list (APPEND FOLLY_LINK_LIBRARIES stdc++fs)\nendif()\nif (FOLLY_STDLIB_LIBCXX AND NOT FOLLY_STDLIB_LIBCXX_GE_9)\n  list (APPEND CMAKE_REQUIRED_LIBRARIES c++fs)\n  list (APPEND FOLLY_LINK_LIBRARIES c++fs)\nendif ()\n\noption(\n  FOLLY_LIBRARY_SANITIZE_ADDRESS\n  \"Build folly with Address Sanitizer enabled.\"\n  OFF\n)\n\nif ($ENV{WITH_ASAN})\n  message(STATUS \"ENV WITH_ASAN is set\")\n  set (FOLLY_LIBRARY_SANITIZE_ADDRESS ON)\nendif()\n\nif (FOLLY_LIBRARY_SANITIZE_ADDRESS)\n  if (\"${CMAKE_CXX_COMPILER_ID}\" MATCHES GNU)\n    set(FOLLY_LIBRARY_SANITIZE_ADDRESS ON)\n    set(FOLLY_ASAN_FLAGS -fsanitize=address,undefined)\n    list(APPEND FOLLY_CXX_FLAGS ${FOLLY_ASAN_FLAGS})\n    # All of the functions in folly/detail/Sse.cpp are intended to be compiled\n    # with ASAN disabled.  They are marked with attributes to disable the\n    # sanitizer, but even so, gcc fails to compile them for some reason when\n    # sanitization is enabled on the compile line.\n    set_source_files_properties(\n      \"${PROJECT_SOURCE_DIR}/folly/detail/Sse.cpp\"\n      PROPERTIES COMPILE_FLAGS -fno-sanitize=address,undefined\n    )\n  elseif (\"${CMAKE_CXX_COMPILER_ID}\" MATCHES Clang)\n    set(FOLLY_LIBRARY_SANITIZE_ADDRESS ON)\n    set(\n      FOLLY_ASAN_FLAGS\n      -fno-common\n      -fsanitize=address,undefined,integer,nullability\n      -fno-sanitize=unsigned-integer-overflow\n    )\n    list(APPEND FOLLY_CXX_FLAGS ${FOLLY_ASAN_FLAGS})\n  endif()\nendif()\n\nadd_library(folly_deps INTERFACE)\n\nfind_package(fmt CONFIG)\nif (NOT DEFINED fmt_CONFIG)\n    # Fallback on a normal search on the current system\n    find_package(Fmt MODULE REQUIRED)\nendif()\ntarget_link_libraries(folly_deps INTERFACE fmt::fmt)\n\nlist(REMOVE_DUPLICATES FOLLY_INCLUDE_DIRECTORIES)\nif(NOT \"${CMAKE_SOURCE_DIR}\" STREQUAL \"${PROJECT_SOURCE_DIR}\")\n  # When consumed via add_subdirectory/FetchContent, wrap each include\n  # directory in BUILD_INTERFACE so absolute build-tree paths don't leak\n  # into the parent project's install-time INTERFACE_INCLUDE_DIRECTORIES.\n  foreach(_dir IN LISTS FOLLY_INCLUDE_DIRECTORIES)\n    target_include_directories(folly_deps INTERFACE $<BUILD_INTERFACE:${_dir}>)\n  endforeach()\nelse()\n  target_include_directories(folly_deps INTERFACE ${FOLLY_INCLUDE_DIRECTORIES})\nendif()\ntarget_link_libraries(folly_deps INTERFACE\n  ${FOLLY_LINK_LIBRARIES}\n  ${FOLLY_SHINY_DEPENDENCIES}\n  ${FOLLY_ASAN_FLAGS}\n)\n"
  },
  {
    "path": "CMake/libfolly.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=@CMAKE_INSTALL_PREFIX@\nlibdir=${exec_prefix}/@LIB_INSTALL_DIR@\nincludedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@\n\nName: libfolly\nDescription: Facebook (Folly) C++ library\nVersion: @PACKAGE_VERSION@\nCflags: -I${includedir} @FOLLY_PKGCONFIG_CFLAGS@\nLibs: -L${libdir} -lfolly\nLibs.private: @FOLLY_PKGCONFIG_PRIVATE_LIBS@\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ncmake_minimum_required(VERSION 3.5 FATAL_ERROR)\n# We use the GoogleTest module if it is available (only in CMake 3.9+)\n# It requires CMP0054 and CMP0057 to be enabled.\nif (POLICY CMP0054)\n  cmake_policy(SET CMP0054 NEW)\nendif()\nif (POLICY CMP0057)\n  cmake_policy(SET CMP0057 NEW)\nendif()\n\n# CMP0075 Include file check macros honor CMAKE_REQUIRED_LIBRARIES\nif(POLICY CMP0075)\n  cmake_policy(SET CMP0075 NEW)\nendif()\n\n# CMP0167 The FindBoost module is removed\nif(POLICY CMP0167)\n  cmake_policy(SET CMP0167 NEW)\nendif()\n\n# includes\nset(CMAKE_MODULE_PATH\n  \"${CMAKE_CURRENT_SOURCE_DIR}/CMake\"\n  # for in-fbsource builds\n  \"${CMAKE_CURRENT_SOURCE_DIR}/../opensource/fbcode_builder/CMake\"\n  # For shipit-transformed builds\n  \"${CMAKE_CURRENT_SOURCE_DIR}/build/fbcode_builder/CMake\"\n  ${CMAKE_MODULE_PATH})\n\n# package information\nset(PACKAGE_NAME      \"folly\")\nif (NOT DEFINED PACKAGE_VERSION)\n  set(PACKAGE_VERSION   \"0.58.0-dev\")\nendif()\nset(PACKAGE_STRING    \"${PACKAGE_NAME} ${PACKAGE_VERSION}\")\nset(PACKAGE_TARNAME   \"${PACKAGE_NAME}-${PACKAGE_VERSION}\")\nset(PACKAGE_BUGREPORT \"https://github.com/facebook/folly/issues\")\n\n# 150+ tests in the root folder anyone? No? I didn't think so.\nset_property(GLOBAL PROPERTY USE_FOLDERS ON)\n\nproject(${PACKAGE_NAME} CXX C ASM)\n\n# Set for FetchContent to skip find_package(folly)\nset(folly_SOURCE_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\"\n  CACHE INTERNAL \"folly source directory\")\n\nset(INCLUDE_INSTALL_DIR include CACHE STRING\n    \"The subdirectory where header files should be installed\")\nset(LIB_INSTALL_DIR lib CACHE STRING\n    \"The subdirectory where libraries should be installed\")\nset(BIN_INSTALL_DIR bin CACHE STRING\n    \"The subdirectory where binaries should be installed\")\nset(CMAKE_INSTALL_DIR lib/cmake/folly CACHE STRING\n    \"The subdirectory where CMake package config files should be installed\")\n\noption(BUILD_SHARED_LIBS\n  \"If enabled, build folly as a shared library.  \\\n  This is generally discouraged, since folly does not commit to having \\\n  a stable ABI.\"\n  OFF\n)\noption(PYTHON_EXTENSIONS\n  \"Build Python Bindings for Folly, requires Cython and (BUILD_SHARED_LIBS=ON)\"\n  OFF\n)\n# Python extensions require shared libraries for proper symbol resolution\nif (PYTHON_EXTENSIONS)\n  set(BUILD_SHARED_LIBS ON CACHE BOOL\n    \"Forced ON because PYTHON_EXTENSIONS requires shared libraries\" FORCE)\nendif()\nset(PYTHON_PACKAGE_INSTALL_DIR \"\" CACHE STRING\n  \"Installation directory for folly Python packages. \\\n  If empty, CMAKE_INSTALL_PREFIX will be used. \\\n  No effect if PYTHON_EXTENSIONS=OFF.\"\n)\n# Mark BUILD_SHARED_LIBS as an \"advanced\" option, since enabling it\n# is generally discouraged.\nmark_as_advanced(BUILD_SHARED_LIBS)\nset(FOLLY_SUPPORT_SHARED_LIBRARY \"${BUILD_SHARED_LIBS}\")\n\ninclude(FBBuildOptions)\nfb_activate_static_library_option()\n\nif(NOT CMAKE_CXX_STANDARD)\n  set(CMAKE_CXX_STANDARD 20)\n  set(CMAKE_CXX_STANDARD_REQUIRED ON)\n  message(STATUS \"setting C++ standard to C++${CMAKE_CXX_STANDARD}\")\nendif()\n\nif(NOT DEFINED IS_X86_64_ARCH AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES \"x86_64|AMD64\")\n  set(IS_X86_64_ARCH TRUE)\nelse()\n  set(IS_X86_64_ARCH FALSE)\nendif()\n\nif(NOT DEFINED IS_AARCH64_ARCH AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES \"aarch64\")\n  set(IS_AARCH64_ARCH TRUE)\nelse()\n  set(IS_AARCH64_ARCH FALSE)\nendif()\n\nif(CMAKE_SYSTEM_NAME STREQUAL \"Windows\")\n  # Check target architecture\n  if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)\n    message(FATAL_ERROR \"Folly requires a 64bit target architecture.\")\n  endif()\n\n  if (MSVC_VERSION LESS 1900)\n    message(\n      FATAL_ERROR\n      \"This build script only supports building Folly on 64-bit Windows with \"\n      \"at least Visual Studio 2017. \"\n      \"MSVC version '${MSVC_VERSION}' is not supported.\"\n    )\n  endif()\nendif()\n\nif(NOT DEFINED FOLLY_HAVE_INT128_T)\n  include(CheckTypeSize)\n  check_type_size(\"__int128_t\" FOLLY_INT128_T_SIZE)\n  if(HAVE_FOLLY_INT128_T_SIZE)\n    set(FOLLY_HAVE_INT128_T TRUE)\n  endif()\nendif()\n\nset(TOP_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\")\nset(FOLLY_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/folly\")\nset(\n  FOLLY_DIR_PREFIXES\n  \"${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_CURRENT_BINARY_DIR}\"\n)\n# https://gitlab.kitware.com/cmake/cmake/-/issues/18580#note_1405108\nstring(REGEX REPLACE \"(.)\" \"\\\\\\\\\\\\1\" FOLLY_DIR_REGEX_ESCAPED \"${FOLLY_DIR}\")\n\ninclude(GNUInstallDirs)\n\nset(CMAKE_THREAD_PREFER_PTHREAD ON)\nset(THREADS_PREFER_PTHREAD_FLAG ON)\nfind_package(Threads REQUIRED)\nset(FOLLY_HAVE_PTHREAD \"${CMAKE_USE_PTHREADS_INIT}\")\nlist(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads)\nlist(APPEND FOLLY_LINK_LIBRARIES Threads::Threads)\n\nif(MSVC)\n  include(FollyCompilerMSVC)\nelse()\n  include(FollyCompilerUnix)\nendif()\ninclude(FollyFunctions)\n\ninclude(folly-deps) # Find the required packages\ninclude(FollyConfigChecks)\nconfigure_file(\n  ${CMAKE_CURRENT_SOURCE_DIR}/CMake/folly-config.h.cmake\n  ${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h\n)\n\n# Define FOLLY_XLOG_STRIP_PREFIXES when compiling our sources so that\n# folly/logging will automatically choose the correct log category names,\n# using only the relative portion of the source file name inside the\n# folly repository.\nset_property(\n  DIRECTORY\n  ${CMAKE_CURRENT_SOURCE_DIR}\n  APPEND\n  PROPERTY\n  COMPILE_DEFINITIONS\n  \"FOLLY_XLOG_STRIP_PREFIXES=\\\"${CMAKE_SOURCE_DIR}:${CMAKE_BINARY_DIR}\\\"\"\n)\n\n# Collect headers for installation (still needed for install step)\nauto_sources(hfiles \"*.h\" \"RECURSE\" \"${FOLLY_DIR}\")\nREMOVE_MATCHES_FROM_LISTS(hfiles\n  MATCHES\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/build/\"\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/docs/examples/\"\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/logging/example/\"\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/(.*/)?test/\"\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/(.*/)?tool/\"\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/facebook/\"\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/rust/\"\n    \"^${FOLLY_DIR_REGEX_ESCAPED}/ext/buck2/\"\n)\n# Add test utility headers that are part of public API\nlist(APPEND hfiles\n  ${FOLLY_DIR}/container/test/F14TestUtil.h\n  ${FOLLY_DIR}/container/test/TrackingTypes.h\n  ${FOLLY_DIR}/io/async/test/AsyncSSLSocketTest.h\n  ${FOLLY_DIR}/io/async/test/AsyncSocketTest.h\n  ${FOLLY_DIR}/io/async/test/AsyncSocketTest2.h\n  ${FOLLY_DIR}/io/async/test/BlockingSocket.h\n  ${FOLLY_DIR}/io/async/test/CallbackStateEnum.h\n  ${FOLLY_DIR}/io/async/test/ConnCallback.h\n  ${FOLLY_DIR}/io/async/test/MockAsyncSocket.h\n  ${FOLLY_DIR}/io/async/test/MockAsyncServerSocket.h\n  ${FOLLY_DIR}/io/async/test/MockAsyncSSLSocket.h\n  ${FOLLY_DIR}/io/async/test/MockAsyncTransport.h\n  ${FOLLY_DIR}/io/async/test/MockAsyncUDPSocket.h\n  ${FOLLY_DIR}/io/async/test/MockTimeoutManager.h\n  ${FOLLY_DIR}/io/async/test/ScopedBoundPort.h\n  ${FOLLY_DIR}/io/async/test/SocketPair.h\n  ${FOLLY_DIR}/io/async/test/TFOUtil.h\n  ${FOLLY_DIR}/io/async/test/TestSSLServer.h\n  ${FOLLY_DIR}/io/async/test/TimeUtil.h\n  ${FOLLY_DIR}/io/async/test/UndelayedDestruction.h\n  ${FOLLY_DIR}/io/async/test/Util.h\n  ${FOLLY_DIR}/synchronization/test/Semaphore.h\n  ${FOLLY_DIR}/test/DeterministicSchedule.h\n  ${FOLLY_DIR}/test/TestUtils.h\n)\n\n# Generate pkg-config variables from folly_deps before we add our own\n# build/install-time include directory generator expressions\ninclude(GenPkgConfig)\ngen_pkgconfig_vars(FOLLY_PKGCONFIG folly_deps)\n\ntarget_include_directories(folly_deps\n  BEFORE\n  INTERFACE\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>\n)\ntarget_include_directories(folly_deps\n  INTERFACE\n    $<INSTALL_INTERFACE:include>\n)\n\n# =============================================================================\n# Build granular libraries from subdirectory CMakeLists.txt files\n# Sources are compiled ONCE via OBJECT libraries, then reused for both\n# granular .a files and the monolithic libfolly.a\n# =============================================================================\n\n# Check for architecture-specific compiler flags needed by subdirectories\ninclude(CheckCXXCompilerFlag)\ncheck_cxx_compiler_flag(-mpclmul COMPILER_HAS_M_PCLMUL)\n\n# GCC coroutines support\nif (CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n  check_cxx_compiler_flag(-fcoroutines COMPILER_HAS_F_COROUTINES)\n  if (COMPILER_HAS_F_COROUTINES)\n    message(STATUS \"GCC has C++ coroutines support, enabling for Folly.\")\n    add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fcoroutines>)\n  else()\n    message(STATUS \"GCC lacks C++ coroutines, disabling Folly coroutines.\")\n  endif()\nendif()\n\nadd_subdirectory(folly)\n\n# Create monolithic folly library from all component OBJECT libraries\nfolly_create_monolithic_library()\n\n# Resolve deferred dependencies (must be after monolithic library is created)\nfolly_resolve_deferred_dependencies()\n\n# Define FOLLY_CERTS_DIR for the granular test_util target\n# This points to the SSL test certificates used by TestSSLServer and related tests\nif(TARGET folly_testing_test_util)\n  target_compile_definitions(folly_testing_test_util INTERFACE\n    FOLLY_CERTS_DIR=\"${FOLLY_DIR}/io/async/test/certs\"\n  )\nendif()\n\nset(FOLLY_INSTALL_TARGETS folly folly_deps)\n\n# Test utilities exported for use by downstream projects\nadd_library(folly_test_util\n  ${FOLLY_DIR}/test/DeterministicSchedule.cpp\n  ${FOLLY_DIR}/json/JsonTestUtil.cpp\n)\ntarget_compile_definitions(folly_test_util PUBLIC\n  FOLLY_CERTS_DIR=\"${FOLLY_DIR}/io/async/test/certs\"\n)\nset_property(TARGET folly_test_util PROPERTY VERSION ${PACKAGE_VERSION})\ntarget_link_libraries(folly_test_util\n  PUBLIC\n    folly\n    ${LIBGMOCK_LIBRARIES}\n)\napply_folly_compile_options_to_target(folly_test_util)\nlist(APPEND FOLLY_INSTALL_TARGETS folly_test_util)\n\ninstall(TARGETS ${FOLLY_INSTALL_TARGETS}\n  EXPORT folly\n  RUNTIME DESTINATION bin\n  LIBRARY DESTINATION ${LIB_INSTALL_DIR}\n  ARCHIVE DESTINATION ${LIB_INSTALL_DIR})\n\nauto_install_files(folly ${FOLLY_DIR}\n  ${hfiles}\n)\ninstall(\n  FILES ${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h\n  DESTINATION ${INCLUDE_INSTALL_DIR}/folly\n  COMPONENT dev\n)\n\n# Generate the folly-config.cmake file for installation so that\n# downstream projects that use on folly can easily depend on it in their CMake\n# files using \"find_package(folly CONFIG)\"\ninclude(CMakePackageConfigHelpers)\nconfigure_package_config_file(\n  CMake/folly-config.cmake.in\n  folly-config.cmake\n  INSTALL_DESTINATION ${CMAKE_INSTALL_DIR}\n  PATH_VARS\n    INCLUDE_INSTALL_DIR\n    CMAKE_INSTALL_DIR\n)\ninstall(\n  FILES ${CMAKE_CURRENT_BINARY_DIR}/folly-config.cmake\n  DESTINATION ${CMAKE_INSTALL_DIR}\n  COMPONENT dev\n)\ninstall(\n  EXPORT folly\n  DESTINATION ${CMAKE_INSTALL_DIR}\n  NAMESPACE Folly::\n  FILE folly-targets.cmake\n  COMPONENT dev\n)\n\n# Generate a pkg-config file so that downstream projects that don't use\n# CMake can depend on folly using pkg-config.\nconfigure_file(\n  ${CMAKE_CURRENT_SOURCE_DIR}/CMake/libfolly.pc.in\n  ${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc.gen\n  @ONLY\n)\n\n# Specify target to allow resolving generator expressions requiring\n# a target for CMake >=3.19. See #1414.\n# VERSION_GREATER_EQUAL isn't available before CMake 3.7.\nif(NOT CMAKE_VERSION VERSION_LESS 3.19)\n  set(target_arg TARGET folly_deps)\nendif()\n\nfile(\n  GENERATE\n  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc\n  INPUT ${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc.gen\n  ${target_arg}\n)\ninstall(\n  FILES ${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc\n  DESTINATION ${LIB_INSTALL_DIR}/pkgconfig\n  COMPONENT dev\n)\n\noption(BUILD_TESTS \"If enabled, compile the tests.\" OFF)\noption(BUILD_BENCHMARKS \"If enabled, compile the benchmarks.\" OFF)\noption(BUILD_BROKEN_TESTS \"If enabled, compile tests that are known to be broken.\" OFF)\noption(BUILD_HANGING_TESTS \"If enabled, compile tests that are known to hang.\" OFF)\noption(BUILD_SLOW_TESTS \"If enabled, compile tests that take a while to run in debug mode.\" OFF)\nif (BUILD_TESTS OR BUILD_BENCHMARKS)\n  option(USE_CMAKE_GOOGLE_TEST_INTEGRATION \"If enabled, use the google test integration included in CMake.\" ON)\n  find_package(GMock MODULE REQUIRED)\n  find_package(GTest MODULE REQUIRED)\n  if (USE_CMAKE_GOOGLE_TEST_INTEGRATION)\n    include(GoogleTest OPTIONAL RESULT_VARIABLE HAVE_CMAKE_GTEST)\n    enable_testing()\n  else()\n    set(HAVE_CMAKE_GTEST OFF)\n  endif()\n\n  # The ThreadLocalTest code uses a helper shared library for one of its tests.\n  # This can only be built if folly itself was built as a shared library.\n  if (BUILD_SHARED_LIBS)\n    add_library(thread_local_test_lib MODULE\n      ${FOLLY_DIR}/test/ThreadLocalTestLib.cpp\n    )\n    set_target_properties(thread_local_test_lib PROPERTIES PREFIX \"\")\n    apply_folly_compile_options_to_target(thread_local_test_lib)\n    target_link_libraries(thread_local_test_lib PUBLIC folly)\n    target_include_directories(\n      thread_local_test_lib\n      PUBLIC ${CMAKE_CURRENT_BINARY_DIR})\n  endif()\n\n  add_library(folly_test_support\n    ${FOLLY_DIR}/test/common/TestMain.cpp\n    ${FOLLY_DIR}/test/FBVectorTestUtil.cpp\n    ${FOLLY_DIR}/test/DeterministicSchedule.cpp\n    ${FOLLY_DIR}/test/DeterministicSchedule.h\n    ${FOLLY_DIR}/test/SingletonTestStructs.cpp\n    ${FOLLY_DIR}/test/SocketAddressTestHelper.cpp\n    ${FOLLY_DIR}/test/SocketAddressTestHelper.h\n    ${FOLLY_DIR}/compression/test/CodingTestUtils.cpp\n    ${FOLLY_DIR}/container/test/F14TestUtil.h\n    ${FOLLY_DIR}/container/test/TrackingTypes.h\n    ${FOLLY_DIR}/futures/test/TestExecutor.cpp\n    ${FOLLY_DIR}/futures/test/TestExecutor.h\n    ${FOLLY_DIR}/io/async/test/BlockingSocket.h\n    ${FOLLY_DIR}/io/async/test/CallbackStateEnum.h\n    ${FOLLY_DIR}/io/async/test/ConnCallback.h\n    ${FOLLY_DIR}/io/async/test/MockAsyncServerSocket.h\n    ${FOLLY_DIR}/io/async/test/MockAsyncSocket.h\n    ${FOLLY_DIR}/io/async/test/MockAsyncSSLSocket.h\n    ${FOLLY_DIR}/io/async/test/MockAsyncTransport.h\n    ${FOLLY_DIR}/io/async/test/MockAsyncUDPSocket.h\n    ${FOLLY_DIR}/io/async/test/MockTimeoutManager.h\n    ${FOLLY_DIR}/io/async/test/ScopedBoundPort.cpp\n    ${FOLLY_DIR}/io/async/test/ScopedBoundPort.h\n    ${FOLLY_DIR}/io/async/test/SocketPair.cpp\n    ${FOLLY_DIR}/io/async/test/SocketPair.h\n    ${FOLLY_DIR}/io/async/test/SSLUtil.cpp\n    ${FOLLY_DIR}/io/async/test/SSLUtil.h\n    ${FOLLY_DIR}/io/async/test/TFOUtil.cpp\n    ${FOLLY_DIR}/io/async/test/TFOUtil.h\n    ${FOLLY_DIR}/io/async/test/TestSSLServer.cpp\n    ${FOLLY_DIR}/io/async/test/TestSSLServer.h\n    ${FOLLY_DIR}/io/async/test/TimeUtil.cpp\n    ${FOLLY_DIR}/io/async/test/TimeUtil.h\n    ${FOLLY_DIR}/io/async/test/UndelayedDestruction.h\n    ${FOLLY_DIR}/io/async/test/Util.h\n    ${FOLLY_DIR}/logging/test/ConfigHelpers.cpp\n    ${FOLLY_DIR}/logging/test/ConfigHelpers.h\n    ${FOLLY_DIR}/logging/test/TestLogHandler.cpp\n    ${FOLLY_DIR}/logging/test/TestLogHandler.h\n  )\n  target_compile_definitions(folly_test_support\n    PUBLIC\n      ${LIBGMOCK_DEFINES}\n  )\n  target_include_directories(folly_test_support\n    SYSTEM\n    PUBLIC\n      ${LIBGMOCK_INCLUDE_DIR}\n      ${GTEST_INCLUDE_DIRS}\n  )\n  target_link_libraries(folly_test_support\n    PUBLIC\n      follybenchmark\n      folly\n      ${LIBGMOCK_LIBRARIES}\n      glog::glog\n  )\n  apply_folly_compile_options_to_target(folly_test_support)\n\n  folly_define_tests(\n    DIRECTORY algorithm/simd/detail/test/\n      TEST algorithm_simd_detail_simd_any_of_test SOURCES SimdAnyOfTest.cpp\n      TEST algorithm_simd_detail_simd_for_each_test SOURCES SimdForEachTest.cpp\n      TEST algorithm_simd_detail_simd_traits_test SOURCES TraitsTest.cpp\n      TEST algorithm_simd_detail_unroll_utils_test SOURCES UnrollUtilsTest.cpp\n\n    DIRECTORY algorithm/simd/test/\n      TEST algorithm_simd_contains_test SOURCES ContainsTest.cpp\n      TEST algorithm_simd_find_first_of_test SOURCES find_first_of_test.cpp\n      TEST algorithm_simd_find_fixed_test SOURCES FindFixedTest.cpp\n      TEST algorithm_simd_movemask_test SOURCES MovemaskTest.cpp\n\n    DIRECTORY chrono/test/\n      TEST chrono_conv_test WINDOWS_DISABLED\n        SOURCES ConvTest.cpp\n\n    DIRECTORY cli/test/\n      TEST cli_args_test SOURCES ArgsTest.cpp\n\n    DIRECTORY codec/test/\n      TEST codec_hex_test SOURCES hex_test.cpp\n      TEST codec_uuid_test SOURCES UuidTest.cpp\n\n    DIRECTORY compression/test/\n      TEST compression_compression_test SLOW SOURCES CompressionTest.cpp\n      TEST compression_quotient_multiset_test SOURCES QuotientMultiSetTest.cpp\n      TEST compression_select64_test SOURCES Select64Test.cpp\n\n    DIRECTORY compression/elias_fano/test/\n      TEST compression_alias_fano_bit_vector_coding_test\n        SOURCES BitVectorCodingTest.cpp\n      TEST compression_alias_fano_elias_fano_test\n        SOURCES EliasFanoCodingTest.cpp\n\n    DIRECTORY container/test/\n      TEST container_access_test SOURCES AccessTest.cpp\n      TEST container_array_test SOURCES ArrayTest.cpp\n      BENCHMARK container_bit_iterator_bench SOURCES BitIteratorBench.cpp\n      TEST container_bit_iterator_test SOURCES BitIteratorTest.cpp\n      TEST container_enumerate_test SOURCES EnumerateTest.cpp\n      BENCHMARK container_evicting_cache_map_bench\n        SOURCES EvictingCacheMapBench.cpp\n      TEST container_evicting_cache_map_test SOURCES EvictingCacheMapTest.cpp\n      TEST container_f14_fwd_test SOURCES F14FwdTest.cpp\n      TEST container_f14_map_test SOURCES F14MapTest.cpp\n      TEST container_f14_set_test SOURCES F14SetTest.cpp\n      BENCHMARK container_fbvector_benchmark\n        SOURCES FBVectorBenchmark.cpp\n        HEADERS FBVectorBenchmarks.cpp.h\n      TEST container_fbvector_test SOURCES FBVectorTest.cpp\n      BENCHMARK container_foreach_benchmark SOURCES ForeachBenchmark.cpp\n      TEST container_foreach_test SOURCES ForeachTest.cpp\n      BENCHMARK container_hash_maps_bench SOURCES HashMapsBench.cpp\n      TEST container_heap_vector_types_test SOURCES heap_vector_types_test.cpp\n      TEST container_map_util_test WINDOWS_DISABLED SOURCES MapUtilTest.cpp\n      TEST container_merge_test SOURCES MergeTest.cpp\n      TEST container_regex_match_cache_test SOURCES RegexMatchCacheTest.cpp\n      TEST container_small_vector_test WINDOWS_DISABLED\n        SOURCES small_vector_test.cpp\n      TEST container_sorted_vector_types_test SOURCES sorted_vector_test.cpp\n      TEST container_span_test SOURCES span_test.cpp\n      TEST container_std_bitset_test SOURCES StdBitsetTest.cpp\n      BENCHMARK container_sparse_byte_set_benchmark\n        SOURCES SparseByteSetBenchmark.cpp\n      TEST container_sparse_byte_set_test SOURCES SparseByteSetTest.cpp\n      TEST container_util_test SOURCES UtilTest.cpp\n\n    DIRECTORY concurrency/container/test/\n      TEST concurrency_container_lock_free_ring_buffer_test\n        SOURCES LockFreeRingBufferTest.cpp\n\n    DIRECTORY concurrency/test/\n      TEST concurrency_atomic_shared_ptr_test SOURCES AtomicSharedPtrTest.cpp\n      TEST concurrency_cache_locality_test WINDOWS_DISABLED\n        SOURCES CacheLocalityTest.cpp\n      TEST concurrency_core_cached_shared_ptr_test\n        SOURCES CoreCachedSharedPtrTest.cpp\n      BENCHMARK concurrency_concurrent_hash_map_bench WINDOWS_DISABLED\n        SOURCES ConcurrentHashMapBench.cpp\n      TEST concurrency_concurrent_hash_map_stress_test SLOW WINDOWS_DISABLED\n        SOURCES ConcurrentHashMapStressTest.cpp\n      TEST concurrency_concurrent_hash_map_test WINDOWS_DISABLED\n        SOURCES ConcurrentHashMapTest.cpp\n      TEST concurrency_dynamic_bounded_queue_test WINDOWS_DISABLED\n        SOURCES DynamicBoundedQueueTest.cpp\n      TEST concurrency_priority_unbounded_queue_set_test\n        SOURCES PriorityUnboundedQueueSetTest.cpp\n      BENCHMARK concurrency_thread_cached_synchronized_bench\n        SOURCES ThreadCachedSynchronizedBench.cpp\n      TEST concurrency_thread_cached_synchronized_test\n        SOURCES ThreadCachedSynchronizedTest.cpp\n      TEST concurrency_unbounded_queue_test SOURCES UnboundedQueueTest.cpp\n\n    DIRECTORY detail/test/\n      TEST detail_simple_simd_string_utils_test\n        SOURCES SimpleSimdStringUtilsTest.cpp\n      TEST detail_split_string_simd_test WINDOWS_DISABLED\n        SOURCES SplitStringSimdTest.cpp\n      TEST detail_static_singleton_manager_test\n        SOURCES StaticSingletonManagerTest.cpp\n      TEST detail_type_list_test SOURCES TypeListTest.cpp\n\n    DIRECTORY detail/base64_detail/test/\n      TEST detail_base64_detail_test WINDOWS_DISABLED\n      SOURCES\n        Base64AgainstScalarTest.cpp\n        Base64PlatformTest.cpp\n        Base64SpecialCasesTest.cpp\n\n    DIRECTORY executors/test/\n      TEST executors_async_helpers_test SOURCES AsyncTest.cpp\n      TEST executors_codel_test WINDOWS_DISABLED SOURCES CodelTest.cpp\n      BENCHMARK executors_edf_thread_pool_executor_benchmark\n        SOURCES EDFThreadPoolExecutorBenchmark.cpp\n      TEST executors_executor_test SOURCES ExecutorTest.cpp\n      TEST executors_fiber_io_executor_test SOURCES FiberIOExecutorTest.cpp\n      # FunctionSchedulerTest has a lot of timing-dependent checks,\n      # and tends to fail on heavily loaded systems.\n      TEST executors_function_scheduler_test BROKEN\n        SOURCES FunctionSchedulerTest.cpp\n      TEST executors_global_executor_test SOURCES GlobalExecutorTest.cpp\n      TEST executors_serial_executor_test SOURCES SerialExecutorTest.cpp\n      # Fails in ThreadPoolExecutorTest.RequestContext:719 data2 != nullptr\n      TEST executors_thread_pool_executor_test BROKEN WINDOWS_DISABLED\n        SOURCES ThreadPoolExecutorTest.cpp\n      TEST executors_threaded_executor_test SOURCES ThreadedExecutorTest.cpp\n      TEST executors_timed_drivable_executor_test\n        SOURCES TimedDrivableExecutorTest.cpp\n\n    DIRECTORY executors/task_queue/test/\n      TEST executors_task_queue_priority_unbounded_blocking_queue_test\n        SOURCES PriorityUnboundedBlockingQueueTest.cpp\n      BENCHMARK executors_task_queue_unbounded_blocking_queue_bench\n        SOURCES UnboundedBlockingQueueBench.cpp\n      TEST executors_task_queue_unbounded_blocking_queue_test\n        SOURCES UnboundedBlockingQueueTest.cpp\n\n    #DIRECTORY experimental/test/\n      #TEST nested_command_line_app_test SOURCES NestedCommandLineAppTest.cpp\n      #TEST program_options_test SOURCES ProgramOptionsTest.cpp\n      # Depends on liburcu\n      #TEST read_mostly_shared_ptr_test SOURCES ReadMostlySharedPtrTest.cpp\n      #TEST ref_count_test SOURCES RefCountTest.cpp\n\n    #DIRECTORY experimental/io/test/\n\n    DIRECTORY external/farmhash/test/\n      TEST external_farmhash_farmhash_test SOURCES farmhash_test.cpp\n\n    DIRECTORY logging/test/\n      TEST logging_async_file_writer_test WINDOWS_DISABLED\n        SOURCES AsyncFileWriterTest.cpp\n      TEST logging_autotimer_test SOURCES AutoTimerTest.cpp\n      TEST logging_config_parser_test SOURCES ConfigParserTest.cpp\n      TEST logging_config_update_test SOURCES ConfigUpdateTest.cpp\n      TEST logging_file_handler_factory_test WINDOWS_DISABLED\n        SOURCES FileHandlerFactoryTest.cpp\n      TEST logging_glog_formatter_test SOURCES GlogFormatterTest.cpp\n      TEST logging_immediate_file_writer_test\n        SOURCES ImmediateFileWriterTest.cpp\n      TEST logging_log_category_test SOURCES LogCategoryTest.cpp\n      TEST logging_logger_db_test SOURCES LoggerDBTest.cpp\n      TEST logging_logger_test WINDOWS_DISABLED SOURCES LoggerTest.cpp\n      TEST logging_log_level_test SOURCES LogLevelTest.cpp\n      TEST logging_log_message_test SOURCES LogMessageTest.cpp\n      TEST logging_log_name_test SOURCES LogNameTest.cpp\n      TEST logging_log_stream_test SOURCES LogStreamTest.cpp\n      TEST logging_rate_limiter_test SOURCES RateLimiterTest.cpp\n      TEST logging_standard_log_handler_test SOURCES StandardLogHandlerTest.cpp\n      TEST logging_xlog_test WINDOWS_DISABLED\n        HEADERS\n          XlogHeader1.h\n          XlogHeader2.h\n        SOURCES\n          XlogFile1.cpp\n          XlogFile2.cpp\n          XlogTest.cpp\n\n    DIRECTORY fibers/test/\n      # FiberManager swapWithException fails with segfault\n      BENCHMARK fibers_fibers_benchmark WINDOWS_DISABLED\n        SOURCES FibersBenchmark.cpp\n      TEST fibers_fibers_test BROKEN SOURCES FibersTest.cpp\n\n    DIRECTORY functional/test/\n      TEST functional_apply_tuple_test WINDOWS_DISABLED\n        SOURCES ApplyTupleTest.cpp\n      TEST functional_invoke_test WINDOWS_DISABLED\n        SOURCES InvokeTest.cpp\n      TEST functional_partial_test SOURCES PartialTest.cpp\n      TEST functional_protocol_test SOURCES protocol_test.cpp\n      TEST functional_traits_test SOURCES traits_test.cpp\n\n    DIRECTORY futures/test/\n      TEST futures_barrier_test SOURCES BarrierTest.cpp\n      TEST futures_callback_lifetime_test SOURCES CallbackLifetimeTest.cpp\n      TEST futures_collect_test SOURCES CollectTest.cpp\n      TEST futures_context_test SOURCES ContextTest.cpp\n      TEST futures_core_test SOURCES CoreTest.cpp\n      TEST futures_ensure_test SOURCES EnsureTest.cpp\n      TEST futures_filter_test SOURCES FilterTest.cpp\n      TEST futures_future_splitter_test SOURCES FutureSplitterTest.cpp\n      BENCHMARK futures_benchmark WINDOWS_DISABLED\n        SOURCES Benchmark.cpp\n      TEST futures_future_test WINDOWS_DISABLED\n        SOURCES FutureTest.cpp\n      TEST futures_header_compile_test SOURCES HeaderCompileTest.cpp\n      TEST futures_interrupt_test SOURCES InterruptTest.cpp\n      TEST futures_map_test SOURCES MapTest.cpp\n      TEST futures_non_copyable_lambda_test SOURCES NonCopyableLambdaTest.cpp\n      TEST futures_poll_test SOURCES PollTest.cpp\n      TEST futures_promise_test SOURCES PromiseTest.cpp\n      TEST futures_reduce_test SOURCES ReduceTest.cpp\n      TEST futures_retrying_test SOURCES RetryingTest.cpp\n      TEST futures_self_destruct_test SOURCES SelfDestructTest.cpp\n      TEST futures_shared_promise_test SOURCES SharedPromiseTest.cpp\n      TEST futures_test_executor_test SOURCES TestExecutorTest.cpp\n      TEST futures_then_compile_test\n        HEADERS\n          ThenCompileTest.h\n        SOURCES\n          ThenCompileTest.cpp\n      TEST futures_then_test SOURCES ThenTest.cpp\n      TEST futures_timekeeper_test WINDOWS_DISABLED SOURCES TimekeeperTest.cpp\n      TEST futures_times_test SOURCES TimesTest.cpp\n      TEST futures_unwrap_test SOURCES UnwrapTest.cpp\n      TEST futures_via_test SOURCES ViaTest.cpp\n      TEST futures_wait_test SOURCES WaitTest.cpp\n      TEST futures_when_test SOURCES WhenTest.cpp\n      TEST futures_while_do_test SOURCES WhileDoTest.cpp\n      TEST futures_will_equal_test SOURCES WillEqualTest.cpp\n      TEST futures_window_test WINDOWS_DISABLED\n        SOURCES WindowTest.cpp\n\n    DIRECTORY gen/test/\n      # MSVC bug can't resolve initializer_list constructor properly\n      TEST gen_base_test WINDOWS_DISABLED SOURCES BaseTest.cpp\n      TEST gen_combine_test SOURCES CombineTest.cpp\n      BENCHMARK gen_parallel_map_benchmark SOURCES ParallelMapBenchmark.cpp\n      TEST gen_parallel_map_test SOURCES ParallelMapTest.cpp\n      BENCHMARK gen_parallel_benchmark SOURCES ParallelBenchmark.cpp\n      TEST gen_parallel_test SOURCES ParallelTest.cpp\n\n    DIRECTORY hash/test/\n      BENCHMARK hash_checksum_benchmark SOURCES ChecksumBenchmark.cpp\n      # builds, but tests fail on MSVC cmake build\n      TEST hash_checksum_test WINDOWS_DISABLED\n        SOURCES ChecksumTest.cpp\n      TEST hash_farm_hash_test SOURCES FarmHashTest.cpp\n      BENCHMARK hash_hash_benchmark WINDOWS_DISABLED\n        SOURCES HashBenchmark.cpp\n      TEST hash_hash_test WINDOWS_DISABLED\n        SOURCES HashTest.cpp\n      TEST hash_spooky_hash_v1_test SOURCES SpookyHashV1Test.cpp\n      TEST hash_spooky_hash_v2_test SOURCES SpookyHashV2Test.cpp\n      TEST hash_traits_test SOURCES traits_test.cpp\n      TEST hash_unique_hash_key_test SOURCES UniqueHashKeyTest.cpp\n\n    DIRECTORY io/test/\n      TEST io_fs_util_test SOURCES FsUtilTest.cpp\n      BENCHMARK io_iobuf_benchmark WINDOWS_DISABLED SOURCES IOBufBenchmark.cpp\n      TEST io_iobuf_test WINDOWS_DISABLED SOURCES IOBufTest.cpp\n      TEST io_iobuf_cursor_test SOURCES IOBufCursorTest.cpp\n      TEST io_iobuf_queue_test SOURCES IOBufQueueTest.cpp\n      TEST io_record_io_test WINDOWS_DISABLED SOURCES RecordIOTest.cpp\n      TEST io_shutdown_socket_set_test HANGING\n        SOURCES ShutdownSocketSetTest.cpp\n      TEST io_socket_option_value_test HANGING\n        SOURCES SocketOptionValueTest.cpp\n\n    DIRECTORY io/async/test/\n      # A number of tests in the async_test binary are unfortunately flaky.\n      # When run under Travis CI a number of the tests also hang (it looks\n      # like they do not get expected socket accept events, causing them\n      # to never break out of their event loops).\n      TEST io_async_async_test BROKEN\n        CONTENT_DIR certs/\n        HEADERS\n          AsyncSocketTest.h\n          AsyncSSLSocketTest.h\n        SOURCES\n          AsyncPipeTest.cpp\n          AsyncSocketExceptionTest.cpp\n          AsyncSocketTest.cpp\n          AsyncSocketTest2.cpp\n          AsyncSSLSocketTest.cpp\n          AsyncSSLSocketTest2.cpp\n          AsyncSSLSocketWriteTest.cpp\n          AsyncTransportTest.cpp\n      # This is disabled because it depends on things that don't exist\n      # on Windows.\n      # TODO: Refactor EventHandlerTest to not use eventfd so it can work on Mac OS X.\n      #TEST io_async_event_handler_test WINDOWS_DISABLED SOURCES EventHandlerTest.cpp\n      TEST io_async_async_timeout_test SOURCES AsyncTimeoutTest.cpp\n      TEST io_async_async_udp_socket_test APPLE_DISABLED WINDOWS_DISABLED\n        SOURCES AsyncUDPSocketTest.cpp\n      TEST io_async_delayed_destruction_test SOURCES DelayedDestructionTest.cpp\n      TEST io_async_delayed_destruction_base_test\n        SOURCES DelayedDestructionBaseTest.cpp\n      TEST io_async_destructor_check_test SOURCES DestructorCheckTest.cpp\n      # Fails with gtest macro error\n      BENCHMARK io_async_event_base_benchmark SOURCES EventBaseBenchmark.cpp\n      TEST io_async_event_base_test BROKEN SOURCES EventBaseTest.cpp\n      TEST io_async_event_base_local_test WINDOWS_DISABLED\n        SOURCES EventBaseLocalTest.cpp\n      TEST io_async_hh_wheel_timer_test SOURCES HHWheelTimerTest.cpp\n      TEST io_async_hh_wheel_timer_slow_tests SLOW\n        SOURCES HHWheelTimerSlowTests.cpp\n      TEST io_async_notification_queue_test WINDOWS_DISABLED\n        SOURCES NotificationQueueTest.cpp\n      BENCHMARK io_async_request_context_benchmark WINDOWS_DISABLED\n        SOURCES RequestContextBenchmark.cpp\n        HEADERS RequestContextHelper.h\n      TEST io_async_request_context_test WINDOWS_DISABLED\n        SOURCES RequestContextTest.cpp\n      TEST io_async_scoped_event_base_thread_test WINDOWS_DISABLED\n        SOURCES ScopedEventBaseThreadTest.cpp\n      TEST io_async_ssl_session_test\n        CONTENT_DIR certs/\n        SOURCES SSLSessionTest.cpp\n      TEST io_async_write_chain_async_transport_wrapper_test\n        SOURCES WriteChainAsyncTransportWrapperTest.cpp\n\n    DIRECTORY io/async/ssl/test/\n      TEST io_async_ssl_ssl_errors_test SOURCES SSLErrorsTest.cpp\n\n    DIRECTORY lang/test/\n      TEST lang_align_test SOURCES AlignTest.cpp\n      TEST lang_aligned_test SOURCES AlignedTest.cpp\n      TEST lang_badge_test SOURCES BadgeTest.cpp\n      TEST lang_bits_class_test SOURCES BitsClassTest.cpp\n      TEST lang_bits_test SOURCES BitsTest.cpp\n      TEST lang_c_string_test SOURCES CStringTest.cpp\n      TEST lang_cast_test SOURCES CastTest.cpp\n      TEST lang_checked_math_test SOURCES CheckedMathTest.cpp\n      TEST lang_exception_test SOURCES ExceptionTest.cpp\n      TEST lang_extern_test SOURCES ExternTest.cpp\n      TEST lang_ordering_test SOURCES OrderingTest.cpp\n      TEST lang_pretty_test SOURCES PrettyTest.cpp\n      TEST lang_propagate_const_test SOURCES PropagateConstTest.cpp\n      TEST lang_r_value_reference_wrapper_test WINDOWS_DISABLED\n        SOURCES RValueReferenceWrapperTest.cpp\n      TEST lang_safe_assert_test SOURCES SafeAssertTest.cpp\n      TEST lang_switch_test SOURCES SwitchTest.cpp\n      BENCHMARK lang_to_ascii_benchmark SOURCES ToAsciiBench.cpp\n      TEST lang_to_ascii_test SOURCES ToAsciiTest.cpp\n      TEST lang_type_info_test SOURCES TypeInfoTest.cpp\n\n    DIRECTORY memory/test/\n      TEST memory_arena_test WINDOWS_DISABLED SOURCES ArenaTest.cpp\n      TEST memory_reentrant_allocator_test WINDOWS_DISABLED\n        SOURCES ReentrantAllocatorTest.cpp\n      TEST memory_shared_from_this_ptr_test\n        SOURCES shared_from_this_ptr_test.cpp\n      TEST memory_thread_cached_arena_test WINDOWS_DISABLED\n        SOURCES ThreadCachedArenaTest.cpp\n      TEST memory_mallctl_helper_test SOURCES MallctlHelperTest.cpp\n      TEST memory_uninitialized_memory_hacks_test\n        SOURCES UninitializedMemoryHacksTest.cpp\n\n    DIRECTORY net/detail/test/\n      TEST net_detail_socket_file_descriptor_map_test\n        SOURCES SocketFileDescriptorMapTest.cpp\n\n    DIRECTORY portability/test/\n      TEST portability_constexpr_test SOURCES ConstexprTest.cpp\n      TEST portability_filesystem_test SOURCES FilesystemTest.cpp\n      TEST portability_libgen_test SOURCES LibgenTest.cpp\n      TEST portability_openssl_portability_test\n        SOURCES OpenSSLPortabilityTest.cpp\n      TEST portability_pthread_test SOURCES PThreadTest.cpp\n      TEST portability_time_test WINDOWS_DISABLED\n        SOURCES TimeTest.cpp\n\n    DIRECTORY ssl/test/\n      TEST ssl_openssl_hash_test SOURCES OpenSSLHashTest.cpp\n\n    DIRECTORY stats/test/\n      TEST stats_buffered_stat_test SOURCES BufferedStatTest.cpp\n      BENCHMARK stats_digest_builder_benchmark\n        SOURCES DigestBuilderBenchmark.cpp\n      TEST stats_digest_builder_test SOURCES DigestBuilderTest.cpp\n      BENCHMARK stats_histogram_benchmark SOURCES HistogramBenchmark.cpp\n      TEST stats_histogram_test SOURCES HistogramTest.cpp\n      BENCHMARK stats_quantile_histogram_benchmark\n        SOURCES QuantileHistogramBenchmark.cpp\n      TEST stats_quantile_estimator_test SOURCES QuantileEstimatorTest.cpp\n      TEST stats_sliding_window_test SOURCES SlidingWindowTest.cpp\n      BENCHMARK stats_tdigest_benchmark SOURCES TDigestBenchmark.cpp\n      TEST stats_tdigest_test SOURCES TDigestTest.cpp\n      TEST stats_timeseries_histogram_test SOURCES TimeseriesHistogramTest.cpp\n      TEST stats_timeseries_test SOURCES TimeSeriesTest.cpp\n\n    DIRECTORY synchronization/test/\n      TEST synchronization_atomic_util_test SOURCES AtomicUtilTest.cpp\n      TEST synchronization_atomic_struct_test SOURCES AtomicStructTest.cpp\n      BENCHMARK synchronization_baton_benchmark SOURCES BatonBenchmark.cpp\n      TEST synchronization_baton_test SOURCES BatonTest.cpp\n      TEST synchronization_call_once_test SOURCES CallOnceTest.cpp\n      TEST synchronization_event_count_test SOURCES EventCountTest.cpp\n      BENCHMARK synchronization_hazptr_bench SOURCES HazptrBench.cpp\n      TEST synchronization_hazptr_test SOURCES HazptrTest.cpp\n      BENCHMARK synchronization_lifo_sem_bench WINDOWS_DISABLED\n        SOURCES LifoSemBench.cpp\n      TEST synchronization_lifo_sem_test WINDOWS_DISABLED\n        SOURCES LifoSemTests.cpp\n      TEST synchronization_relaxed_atomic_test WINDOWS_DISABLED\n        SOURCES RelaxedAtomicTest.cpp\n      TEST synchronization_rw_spin_lock_test SOURCES RWSpinLockTest.cpp\n      TEST synchronization_semaphore_test WINDOWS_DISABLED\n        SOURCES SemaphoreTest.cpp\n      BENCHMARK synchronization_small_locks_benchmark\n        SOURCES SmallLocksBenchmark.cpp\n      TEST synchronization_small_locks_test SOURCES SmallLocksTest.cpp\n\n    DIRECTORY synchronization/detail/test/\n      TEST synchronization_detail_inline_function_ref_test\n        SOURCES InlineFunctionRefTest.cpp\n\n    DIRECTORY system/test/\n      TEST system_at_fork_test WINDOWS_DISABLED SOURCES AtForkTest.cpp\n      TEST system_memory_mapping_test SOURCES MemoryMappingTest.cpp\n      TEST system_shell_test SOURCES ShellTest.cpp\n      #TEST system_subprocess_test SOURCES SubprocessTest.cpp\n      TEST system_thread_id_test SOURCES ThreadIdTest.cpp\n      TEST system_thread_name_test WINDOWS_DISABLED\n        SOURCES ThreadNameTest.cpp\n\n    DIRECTORY test/\n      TEST ahm_int_stress_test SOURCES AHMIntStressTest.cpp\n      TEST arena_smartptr_test SOURCES ArenaSmartPtrTest.cpp\n      BENCHMARK ascii_case_insensitive_benchmark\n        SOURCES AsciiCaseInsensitiveBenchmark.cpp\n      TEST ascii_check_test SOURCES AsciiCaseInsensitiveTest.cpp\n      TEST atomic_hash_array_test SOURCES AtomicHashArrayTest.cpp\n      TEST atomic_hash_map_test HANGING\n        SOURCES AtomicHashMapTest.cpp\n      TEST atomic_linked_list_test SOURCES AtomicLinkedListTest.cpp\n      TEST atomic_unordered_map_test SOURCES AtomicUnorderedMapTest.cpp\n      TEST base64_test SOURCES base64_test.cpp\n      TEST buffered_atomic_test SOURCES BufferedAtomicTest.cpp\n      TEST cancellation_token_test SOURCES CancellationTokenTest.cpp\n      TEST chrono_test SOURCES ChronoTest.cpp\n      TEST clock_gettime_wrappers_test SOURCES ClockGettimeWrappersTest.cpp\n      TEST concurrent_bit_set_test SOURCES ConcurrentBitSetTest.cpp\n      BENCHMARK concurrent_skip_list_benchmark\n        SOURCES ConcurrentSkipListBenchmark.cpp\n      TEST concurrent_skip_list_test SOURCES ConcurrentSkipListTest.cpp\n      # Builds but fails test on constexpr_exp_floating std::exp(471.L)\n      TEST constexpr_math_test WINDOWS_DISABLED\n        SOURCES ConstexprMathTest.cpp\n      TEST conv_test SOURCES ConvTest.cpp\n      TEST cpu_id_test SOURCES CpuIdTest.cpp\n      TEST demangle_test SOURCES DemangleTest.cpp\n      TEST deterministic_schedule_test SOURCES DeterministicScheduleTest.cpp\n      TEST discriminated_ptr_test SOURCES DiscriminatedPtrTest.cpp\n      TEST endian_test SOURCES EndianTest.cpp\n      TEST exception_string_test BROKEN SOURCES ExceptionStringTest.cpp\n      TEST exception_test SOURCES ExceptionTest.cpp\n      BENCHMARK exception_wrapper_benchmark WINDOWS_DISABLED\n        SOURCES ExceptionWrapperBenchmark.cpp\n      TEST exception_wrapper_test WINDOWS_DISABLED\n        SOURCES ExceptionWrapperTest.cpp\n      TEST expected_coroutines_test SOURCES ExpectedCoroutinesTest.cpp\n      TEST expected_test WINDOWS_DISABLED\n        SOURCES ExpectedTest.cpp\n      BENCHMARK fbstring_benchmark WINDOWS_DISABLED\n        SOURCES FBStringBenchmark.cpp\n        HEADERS FBStringTestBenchmarks.cpp.h\n      TEST fbstring_test WINDOWS_DISABLED SOURCES FBStringTest.cpp\n      TEST file_test SOURCES FileTest.cpp\n      # Open-source linux build can't handle running this.\n      #TEST file_lock_test SOURCES FileLockTest.cpp\n      TEST file_util_test SOURCES FileUtilTest.cpp\n      BENCHMARK fingerprint_benchmark SOURCES FingerprintBenchmark.cpp\n      TEST fingerprint_test SOURCES FingerprintTest.cpp\n      TEST fixed_string_test SOURCES FixedStringTest.cpp\n      TEST fmt_utility_test SOURCES FmtUtilityTest.cpp\n      TEST format_other_test SOURCES FormatOtherTest.cpp\n      BENCHMARK format_benchmark SOURCES FormatBenchmark.cpp\n      TEST format_test SOURCES FormatTest.cpp\n      TEST function_test BROKEN\n        SOURCES FunctionTest.cpp\n      BENCHMARK function_ref_benchmark SOURCES FunctionRefBenchmark.cpp\n      TEST function_ref_test SOURCES FunctionRefTest.cpp\n      TEST futex_test SOURCES FutexTest.cpp\n      TEST glog_test SOURCES GLogTest.cpp\n      TEST group_varint_test SOURCES GroupVarintTest.cpp\n      TEST group_varint_test_ssse3 SOURCES GroupVarintTest.cpp\n      TEST indestructible_test SOURCES IndestructibleTest.cpp\n      TEST indexed_mem_pool_test BROKEN\n        SOURCES IndexedMemPoolTest.cpp\n      TEST iterators_test SOURCES IteratorsTest.cpp\n      TEST lazy_test SOURCES LazyTest.cpp\n      TEST locks_test SOURCES SpinLockTest.cpp\n      TEST math_test SOURCES MathTest.cpp\n      BENCHMARK memcpy_benchmark WINDOWS_DISABLED SOURCES MemcpyBenchmark.cpp\n      TEST memcpy_test SOURCES MemcpyTest.cpp\n      TEST memory_idler_test SOURCES MemoryIdlerTest.cpp\n      TEST memory_test WINDOWS_DISABLED\n        SOURCES MemoryTest.cpp\n      BENCHMARK memset_benchmark WINDOWS_DISABLED SOURCES MemsetBenchmark.cpp\n      TEST move_wrapper_test SOURCES MoveWrapperTest.cpp\n      TEST mpmc_pipeline_test SOURCES MPMCPipelineTest.cpp\n      TEST mpmc_queue_test SLOW\n        SOURCES MPMCQueueTest.cpp\n      TEST network_address_test HANGING\n        SOURCES\n          IPAddressTest.cpp\n          MacAddressTest.cpp\n          SocketAddressTest.cpp\n      TEST optional_coroutines_test SOURCES OptionalCoroutinesTest.cpp\n      TEST optional_test SOURCES OptionalTest.cpp\n      TEST packed_sync_ptr_test HANGING\n        SOURCES PackedSyncPtrTest.cpp\n      TEST padded_test SOURCES PaddedTest.cpp\n      #TEST poly_test SOURCES PolyTest.cpp\n      TEST portability_test SOURCES PortabilityTest.cpp\n      # Turns out this benchmark uses Linux only API for setting CPU affinity i.e. cpu_set_t.\n      BENCHMARK producer_consumer_queue_benchmark APPLE_DISABLED WINDOWS_DISABLED\n        SOURCES ProducerConsumerQueueBenchmark.cpp\n      TEST producer_consumer_queue_test SLOW\n        SOURCES ProducerConsumerQueueTest.cpp\n      BENCHMARK range_find_benchmark SOURCES RangeFindBenchmark.cpp\n      BENCHMARK random_benchmark SOURCES RandomBenchmark.cpp\n      TEST random_test SOURCES RandomTest.cpp\n      TEST range_test SOURCES RangeTest.cpp\n      TEST replaceable_test WINDOWS_DISABLED SOURCES ReplaceableTest.cpp\n      TEST scope_guard_test WINDOWS_DISABLED SOURCES ScopeGuardTest.cpp\n      # Heavily dependent on drand and srand48\n      #TEST shared_mutex_test SOURCES SharedMutexTest.cpp\n      # SingletonTest requires Subprocess\n      #TEST singleton_test SOURCES SingletonTest.cpp\n      TEST singleton_double_registration_test BROKEN SOURCES\n        SingletonDoubleRegistration.cpp\n      TEST singleton_test_global SOURCES SingletonTestGlobal.cpp\n      TEST singleton_thread_local_test BROKEN SOURCES\n        SingletonThreadLocalTest.cpp\n        SingletonThreadLocalTestOverload.cpp\n      TEST spin_lock_test SOURCES SpinLockTest.cpp\n      BENCHMARK string_benchmark WINDOWS_DISABLED SOURCES StringBenchmark.cpp\n      TEST string_test WINDOWS_DISABLED SOURCES StringTest.cpp\n      BENCHMARK string_to_float_benchmark SOURCES StringToFloatBenchmark.cpp\n      BENCHMARK synchronized_benchmark WINDOWS_DISABLED\n        SOURCES SynchronizedBenchmark.cpp\n      TEST synchronized_test WINDOWS_DISABLED\n        SOURCES SynchronizedTest.cpp\n      TEST thread_cached_int_test WINDOWS_DISABLED\n        SOURCES ThreadCachedIntTest.cpp\n      TEST thread_local_test WINDOWS_DISABLED SOURCES ThreadLocalTest.cpp\n      TEST timeout_queue_test SOURCES TimeoutQueueTest.cpp\n      TEST token_bucket_test SOURCES TokenBucketTest.cpp\n      TEST traits_test SOURCES TraitsTest.cpp\n      TEST try_test WINDOWS_DISABLED SOURCES TryTest.cpp\n      TEST unicode_test SOURCES UnicodeTest.cpp\n      TEST unit_test SOURCES UnitTest.cpp\n      BENCHMARK uri_benchmark SOURCES UriBenchmark.cpp\n      TEST uri_test SOURCES UriTest.cpp\n      TEST utf8_string_test SOURCES UTF8StringTest.cpp\n      TEST utility_test SOURCES UtilityTest.cpp\n      TEST varint_test SOURCES VarintTest.cpp\n\n    DIRECTORY test/function_benchmark/\n      BENCHMARK function_benchmark\n        SOURCES main.cpp benchmark_impl.cpp test_functions.cpp\n\n    DIRECTORY testing/test/\n      TEST testing_test_util_test SOURCES TestUtilTest.cpp\n\n    DIRECTORY tracing/test/\n      TEST static_tracepoint_section_test\n        SOURCES StaticTracepointSectionTest.cpp\n\n    DIRECTORY json/test/\n      TEST json_dynamic_converter_test SOURCES DynamicConverterTest.cpp\n      TEST json_dynamic_other_test SOURCES DynamicOtherTest.cpp\n      TEST json_dynamic_parser_test SOURCES DynamicParserTest.cpp\n      TEST json_dynamic_test SOURCES DynamicTest.cpp\n      # MSVC Preprocessor stringizing raw string literals bug\n      TEST json_json_test WINDOWS_DISABLED SOURCES JsonTest.cpp\n      BENCHMARK json_json_benchmark SOURCES JsonBenchmark.cpp\n      TEST json_json_other_test SOURCES JsonOtherTest.cpp\n      TEST json_json_patch_test SOURCES json_patch_test.cpp\n      TEST json_json_pointer_test SOURCES json_pointer_test.cpp\n      TEST json_json_schema_test SOURCES JSONSchemaTest.cpp\n  )\n\n  if (${LIBSODIUM_FOUND})\n    folly_define_tests(\n      DIRECTORY crypto/test/\n        TEST crypto_blake2xb_test SOURCES Blake2xbTest.cpp\n        BENCHMARK crypto_lt_hash_benchmark SOURCES LtHashBenchmark.cpp\n        TEST crypto_lt_hash_test SOURCES LtHashTest.cpp\n    )\n  endif()\n\n  if (${LIBAIO_FOUND})\n    folly_define_tests(\n      DIRECTORY io/async/test/\n        TEST io_async_async_io_test\n          SOURCES\n            AsyncIOTest.cpp\n            AsyncBaseTestLib.cpp\n            IoTestTempFileUtil.cpp\n    )\n  endif()\n\n  get_target_property(pic folly POSITION_INDEPENDENT_CODE)\n  if (pic)\n    add_library(singleton_thread_local_overload\n      SHARED ${FOLLY_DIR}/test/SingletonThreadLocalTestOverload.cpp)\n    apply_folly_compile_options_to_target(singleton_thread_local_overload)\n    set_target_properties(singleton_thread_local_overload PROPERTIES\n      PREFIX \"\"\n      LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/folly/test\n    )\n    target_link_libraries(singleton_thread_local_overload PRIVATE folly)\n    folly_define_tests(\n      DIRECTORY test/\n        TEST singleton_thread_local_test SOURCES SingletonThreadLocalTest.cpp\n    )\n  endif()\nendif()\n"
  },
  {
    "path": "CMakeListsForBuck2.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ncmake_minimum_required(VERSION 3.5 FATAL_ERROR)\n# We use the GoogleTest module if it is available (only in CMake 3.9+)\n# It requires CMP0054 and CMP0057 to be enabled.\nif (POLICY CMP0054)\n  cmake_policy(SET CMP0054 NEW)\nendif()\nif (POLICY CMP0057)\n  cmake_policy(SET CMP0057 NEW)\nendif()\n\n# CMP0075 Include file check macros honor CMAKE_REQUIRED_LIBRARIES\nif(POLICY CMP0075)\n  cmake_policy(SET CMP0075 NEW)\nendif()\n\n# includes\nset(CMAKE_MODULE_PATH\n  \"${CMAKE_CURRENT_SOURCE_DIR}/CMake\"\n  # for in-fbsource builds\n  \"${CMAKE_CURRENT_SOURCE_DIR}/../opensource/fbcode_builder/CMake\"\n  # For shipit-transformed builds\n  \"${CMAKE_CURRENT_SOURCE_DIR}/build/fbcode_builder/CMake\"\n  ${CMAKE_MODULE_PATH})\n\n# package information\nset(PACKAGE_NAME      \"folly\")\nif (NOT DEFINED PACKAGE_VERSION)\n  set(PACKAGE_VERSION   \"0.58.0-dev\")\nendif()\nset(PACKAGE_STRING    \"${PACKAGE_NAME} ${PACKAGE_VERSION}\")\nset(PACKAGE_TARNAME   \"${PACKAGE_NAME}-${PACKAGE_VERSION}\")\nset(PACKAGE_BUGREPORT \"https://github.com/facebook/folly/issues\")\n\n# 150+ tests in the root folder anyone? No? I didn't think so.\nset_property(GLOBAL PROPERTY USE_FOLDERS ON)\n\nproject(${PACKAGE_NAME} CXX C ASM)\n\nset(INCLUDE_INSTALL_DIR include CACHE STRING\n    \"The subdirectory where header files should be installed\")\nset(LIB_INSTALL_DIR lib CACHE STRING\n    \"The subdirectory where libraries should be installed\")\nset(BIN_INSTALL_DIR bin CACHE STRING\n    \"The subdirectory where binaries should be installed\")\nset(CMAKE_INSTALL_DIR lib/cmake/folly CACHE STRING\n    \"The subdirectory where CMake package config files should be installed\")\n\noption(BUILD_SHARED_LIBS\n  \"If enabled, build folly as a shared library.  \\\n  This is generally discouraged, since folly does not commit to having \\\n  a stable ABI.\"\n  OFF\n)\n# Mark BUILD_SHARED_LIBS as an \"advanced\" option, since enabling it\n# is generally discouraged.\nmark_as_advanced(BUILD_SHARED_LIBS)\nset(FOLLY_SUPPORT_SHARED_LIBRARY \"${BUILD_SHARED_LIBS}\")\n\ninclude(FBBuildOptions)\nfb_activate_static_library_option()\n\nif(NOT CMAKE_CXX_STANDARD)\n  set(CMAKE_CXX_STANDARD 20)\n  set(CMAKE_CXX_STANDARD_REQUIRED ON)\n  message(STATUS \"setting C++ standard to C++${CMAKE_CXX_STANDARD}\")\nendif()\n\nif(NOT DEFINED IS_X86_64_ARCH AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES \"x86_64|AMD64\")\n  set(IS_X86_64_ARCH TRUE)\nelse()\n  set(IS_X86_64_ARCH FALSE)\nendif()\n\nif(CMAKE_SYSTEM_NAME STREQUAL \"Windows\")\n  # Check target architecture\n  if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)\n    message(FATAL_ERROR \"Folly requires a 64bit target architecture.\")\n  endif()\n\n  if (MSVC_VERSION LESS 1900)\n    message(\n      FATAL_ERROR\n      \"This build script only supports building Folly on 64-bit Windows with \"\n      \"at least Visual Studio 2017. \"\n      \"MSVC version '${MSVC_VERSION}' is not supported.\"\n    )\n  endif()\nendif()\n\nset(TOP_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\")\nset(FOLLY_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/folly\")\nset(\n  FOLLY_DIR_PREFIXES\n  \"${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_CURRENT_BINARY_DIR}\"\n)\n\ninclude(GNUInstallDirs)\n\nset(CMAKE_THREAD_PREFER_PTHREAD ON)\nset(THREADS_PREFER_PTHREAD_FLAG ON)\nfind_package(Threads REQUIRED)\nset(FOLLY_HAVE_PTHREAD \"${CMAKE_USE_PTHREADS_INIT}\")\nlist(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads)\nlist(APPEND FOLLY_LINK_LIBRARIES Threads::Threads)\n\nif(MSVC)\n  include(FollyCompilerMSVC)\nelse()\n  include(FollyCompilerUnix)\nendif()\ninclude(FollyFunctions)\n\ninclude(folly-deps) # Find the required packages\ninclude(FollyConfigChecks)\nconfigure_file(\n  ${CMAKE_CURRENT_SOURCE_DIR}/CMake/folly-config.h.cmake\n  ${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h\n)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to make participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies within all project spaces, and it also applies when\nan individual is representing the project or its community in public spaces.\nExamples of representing a project or community include using an official\nproject e-mail address, posting via an official social media account, or acting\nas an appointed representative at an online or offline event. Representation of\na project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at <opensource-conduct@fb.com>. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Folly\nWe want to make contributing to this project as easy and transparent as\npossible.\n\n## Code of Conduct\nThe code of conduct is described in [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).\n\n## Pull Requests\nWe actively welcome your pull requests.\n\n1. Fork the repo and create your branch from `master`.\n2. If you've added code that should be tested, add tests.\n3. If you've changed APIs, update the documentation.\n4. Ensure the test suite passes.\n5. If you haven't already, complete the Contributor License Agreement (\"CLA\").\n\n## Contributor License Agreement (\"CLA\")\nIn order to accept your pull request, we need you to submit a CLA. You only need\nto do this once to work on any of Facebook's open source projects.\n\nComplete your CLA here: <https://code.facebook.com/cla>\n\n## Issues\nWe use GitHub issues to track public bugs. Please ensure your description is\nclear and has sufficient instructions to be able to reproduce the issue.\n\nFacebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe\ndisclosure of security bugs. In those cases, please go through the process\noutlined on that page and do not file a public issue.\n\n## License\nBy contributing to folly, you agree that your contributions will be licensed\nunder the LICENSE file in the root directory of this source tree.\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n\nFiles in folly/external/farmhash licensed as follows\n\n    Copyright (c) 2014 Google, Inc.\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in\n    all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n    THE SOFTWARE.\n"
  },
  {
    "path": "PACKAGE",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nload(\"@shim//:cfg.bzl\", \"SHIM_ALIASES\", \"set_cfg_constructor\", \"get_shim_modifiers\")\nload(\"@prelude//cfg/modifier:set_cfg_modifiers.bzl\", \"set_cfg_modifiers\")\n\n# Activate cfg modifiers from CLI / PACKAGE / targets\nset_cfg_constructor(SHIM_ALIASES)\n\nmodifiers = get_shim_modifiers()\nset_cfg_modifiers(modifiers)\n"
  },
  {
    "path": "README.md",
    "content": "Folly: Facebook Open-source Library\n===================================\n\n# What is `folly`?\n\n<img src=\"static/logo.svg\" alt=\"Logo Folly\" width=\"15%\" align=\"right\" />\n\nFolly (an acronym loosely after Facebook Open Source Library) is a\nlibrary of C++20 components designed with practicality and efficiency\nin mind. **Folly contains a variety of core library components used extensively\nat Facebook**. In particular, it's often a dependency of Facebook's other\nopen source C++ efforts and place where those projects can share code.\n\nIt complements (as opposed to competing against) offerings\nsuch as Boost and of course `std`. In fact, we embark on defining our\nown component only when something we need is either not available, or\ndoes not meet the needed performance profile. We endeavor to remove\nthings from folly if or when `std` or Boost obsoletes them.\n\nPerformance concerns permeate much of Folly, sometimes leading to\ndesigns that are more idiosyncratic than they would otherwise be (see\ne.g. `PackedSyncPtr.h`, `SmallLocks.h`). Good performance at large\nscale is a unifying theme in all of Folly.\n\n## Check it out in the intro video\n[![Explain Like I’m 5: Folly](https://img.youtube.com/vi/Wr_IfOICYSs/0.jpg)](https://www.youtube.com/watch?v=Wr_IfOICYSs)\n\n# Logical Design\n\nFolly is a collection of relatively independent components, some as\nsimple as a few symbols. There is no restriction on internal\ndependencies, meaning that a given folly module may use any other\nfolly components.\n\nAll symbols are defined in the top-level namespace `folly`, except of\ncourse macros. Macro names are ALL_UPPERCASE and should be prefixed\nwith `FOLLY_`. Namespace `folly` defines other internal namespaces\nsuch as `internal` or `detail`. User code should not depend on symbols\nin those namespaces.\n\n# Physical Design\n\nAt the top level Folly uses the classic \"stuttering\" scheme\n`folly/folly` used by Boost and others. The first directory serves as\nan installation root of the library (with possible versioning a la\n`folly-1.0/`), and the second is to distinguish the library when\nincluding files, e.g. `#include <folly/FBString.h>`.\n\nThe directory structure is flat (mimicking the namespace structure),\ni.e. we don't have an elaborate directory hierarchy (it is possible\nthis will change in future versions). The subdirectory `experimental`\ncontains files that are used inside folly and possibly at Facebook but\nnot considered stable enough for client use. Your code should not use\nfiles in `folly/experimental` lest it may break when you update Folly.\n\nThe `folly/folly/test` subdirectory includes the unittests for all\ncomponents, usually named `ComponentXyzTest.cpp` for each\n`ComponentXyz.*`. The `folly/folly/docs` directory contains\ndocumentation.\n\n# What's in it?\n\nBecause of folly's fairly flat structure, the best way to see what's in it\nis to look at the headers in [top level `folly/` directory](https://github.com/facebook/folly/tree/main/folly). You can also\ncheck the [`docs` folder](folly/docs) for documentation, starting with the\n[overview](folly/docs/Overview.md).\n\nFolly is published on GitHub at https://github.com/facebook/folly.\n\n# Build Notes\n\nBecause folly does not provide any ABI compatibility guarantees from commit to\ncommit, we generally recommend building folly as a static library.\n\nfolly supports gcc (5.1+), clang, or MSVC. It should run on Linux (x86-32,\nx86-64, and ARM), iOS, macOS, and Windows (x86-64). The CMake build is only\ntested on some of these platforms; at a minimum, we aim to support macOS and\nLinux (on the latest Ubuntu LTS release or newer.)\n\n## `getdeps.py`\n\nThis script is used by many of Meta's OSS tools.  It will download and build all of the necessary dependencies first, and will then invoke cmake etc to build folly.  This will help ensure that you build with relevant versions of all of the dependent libraries, taking into account what versions are installed locally on your system.\n\nIt's written in python so you'll need python3.6 or later on your PATH.  It works on Linux, macOS and Windows.\n\nThe settings for folly's cmake build are held in its getdeps manifest `build/fbcode_builder/manifests/folly`, which you can edit locally if desired.\n\n### Dependencies\n\nIf on Linux or MacOS (with homebrew installed) you can install system dependencies to save building them:\n\n    # Clone the repo\n    git clone https://github.com/facebook/folly\n    # Install dependencies\n    cd folly\n    sudo ./build/fbcode_builder/getdeps.py install-system-deps --recursive\n\nIf you'd like to see the packages before installing them:\n\n    ./build/fbcode_builder/getdeps.py install-system-deps --dry-run --recursive\n\nOn other platforms or if on Linux and without system dependencies `getdeps.py` will mostly download and build them for you during the build step.\n\nSome of the dependencies `getdeps.py` uses and installs are:\n\n  * a version of boost compiled with C++14 support.\n  * googletest is required to build and run folly's tests.\n\n### Build\n\nThis script will download and build all of the necessary dependencies first,\nand will then invoke cmake etc to build folly.  This will help ensure that you build with relevant versions of all of the dependent libraries, taking into account what versions are installed locally on your system.\n\n`getdeps.py` currently requires python 3.6+ to be on your path.\n\n`getdeps.py` will invoke cmake etc.\n\n    # Clone the repo\n    git clone https://github.com/facebook/folly\n    cd folly\n    # Build, using system dependencies if available\n    python3 ./build/fbcode_builder/getdeps.py --allow-system-packages build\n\nIt puts output in its scratch area:\n\n  * `installed/folly/lib/libfolly.a`: Library\n\nYou can also specify a `--scratch-path` argument to control\nthe location of the scratch directory used for the build. You can find the default scratch install location from logs or with `python3 ./build/fbcode_builder/getdeps.py show-inst-dir`.\n\nThere are also\n`--install-dir` and `--install-prefix` arguments to provide some more\nfine-grained control of the installation directories. However, given that\nfolly provides no compatibility guarantees between commits we generally\nrecommend building and installing the libraries to a temporary location, and\nthen pointing your project's build at this temporary location, rather than\ninstalling folly in the traditional system installation directories. e.g., if you are building with CMake you can use the `CMAKE_PREFIX_PATH` variable to allow CMake to find folly in this temporary installation directory when\nbuilding your project.\n\nIf you want to invoke `cmake` again to iterate, there is a helpful `run_cmake.py` script output in the scratch build directory.  You can find the scratch build directory from logs or with `python3 ./build/fbcode_builder/getdeps.py show-build-dir`.\n\n### Run tests\n\nBy default `getdeps.py` will build the tests for folly. To run them:\n\n    cd folly\n    python3 ./build/fbcode_builder/getdeps.py --allow-system-packages test\n\n### `build.sh`/`build.bat` wrapper\n\n`build.sh` can be used on Linux and MacOS, on Windows use\nthe `build.bat` script instead. Its a wrapper around `getdeps.py`.\n\n## Build with cmake directly\n\nIf you don't want to let getdeps invoke cmake for you then by default, building the tests is disabled as part of the CMake `all` target.\nTo build the tests, specify `-DBUILD_TESTS=ON` to CMake at configure time.\n\nNB if you want to invoke `cmake` again to iterate on a `getdeps.py` build, there is a helpful `run_cmake.py` script output in the scratch-path build directory. You can find the scratch build directory from logs or with `python3 ./build/fbcode_builder/getdeps.py show-build-dir`.\n\nRunning tests with ctests also works if you cd to the build dir, e.g.\n`(cd $(python3 ./build/fbcode_builder/getdeps.py show-build-dir) && ctest)`\n\n### Finding dependencies in non-default locations\n\nIf you have boost, gtest, or other dependencies installed in a non-default\nlocation, you can use the `CMAKE_INCLUDE_PATH` and `CMAKE_LIBRARY_PATH`\nvariables to make CMAKE look also look for header files and libraries in\nnon-standard locations.  For example, to also search the directories\n`/alt/include/path1` and `/alt/include/path2` for header files and the\ndirectories `/alt/lib/path1` and `/alt/lib/path2` for libraries, you can invoke\n`cmake` as follows:\n\n```\ncmake \\\n  -DCMAKE_INCLUDE_PATH=/alt/include/path1:/alt/include/path2 \\\n  -DCMAKE_LIBRARY_PATH=/alt/lib/path1:/alt/lib/path2 ...\n```\n\n## Ubuntu LTS, CentOS Stream, Fedora\n\nUse the `getdeps.py` approach above. We test in CI on Ubuntu LTS, and occasionally on other distros.\n\nIf you find the set of system packages is not quite right for your chosen distro, you can specify distro version specific overrides in the dependency manifests (e.g. https://github.com/facebook/folly/blob/main/build/fbcode_builder/manifests/boost ). You could probably make it work on most recent Ubuntu/Debian or Fedora/Redhat derived distributions.\n\nAt time of writing (Dec 2021) there is a build break on GCC 11.x based systems in lang_badge_test.  If you don't need badge functionality you can work around by commenting it out from CMakeLists.txt (unfortunately fbthrift does need it)\n\n## Windows (Vcpkg)\n\nNote that many tests are disabled for folly Windows builds, you can see them in the log from the cmake configure step, or by looking for WINDOWS_DISABLED in `CMakeLists.txt`\n\nThat said, `getdeps.py` builds work on Windows and are tested in CI.\n\nIf you prefer, you can try Vcpkg. folly is available in [Vcpkg](https://github.com/Microsoft/vcpkg#vcpkg) and releases may be built via `vcpkg install folly:x64-windows`.\n\nYou may also use `vcpkg install folly:x64-windows --head` to build against `main`.\n\n## macOS\n\n`getdeps.py` builds work on macOS and are tested in CI, however if you prefer, you can try one of the macOS package managers\n\n### Homebrew\n\nfolly is available as a Formula and releases may be built via `brew install folly`.\n\nYou may also use `folly/build/bootstrap-osx-homebrew.sh` to build against `main`:\n\n```\n  ./folly/build/bootstrap-osx-homebrew.sh\n```\n\nThis will create a build directory `_build` in the top-level.\n\n### MacPorts\n\nInstall the required packages from MacPorts:\n\n```\n  sudo port install \\\n    boost \\\n    cmake \\\n    gflags \\\n    git \\\n    google-glog \\\n    libevent \\\n    libtool \\\n    lz4 \\\n    lzma \\\n    openssl \\\n    snappy \\\n    xz \\\n    zlib\n```\n\nDownload and install double-conversion:\n\n```\n  git clone https://github.com/google/double-conversion.git\n  cd double-conversion\n  cmake -DBUILD_SHARED_LIBS=ON .\n  make\n  sudo make install\n```\n\nDownload and install folly with the parameters listed below:\n\n```\n  git clone https://github.com/facebook/folly.git\n  cd folly\n  mkdir _build\n  cd _build\n  cmake ..\n  make\n  sudo make install\n```\n"
  },
  {
    "path": "buck2",
    "content": "#!/usr/bin/env dotslash\n\n{\n  \"name\": \"buck2\",\n  \"platforms\": {\n    \"macos-aarch64\": {\n      \"size\": 31815315,\n      \"hash\": \"blake3\",\n      \"digest\": \"65a9c2296163b9d0a3e0f6ae36923444c79d5abcc7160e88cdce56d1646e12ca\",\n      \"format\": \"zst\",\n      \"path\": \"buck2-aarch64-apple-darwin\",\n      \"providers\": [\n        {\n          \"url\": \"https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-aarch64-apple-darwin.zst\"\n        }\n      ]\n    },\n    \"windows-aarch64\": {\n      \"size\": 27939642,\n      \"hash\": \"blake3\",\n      \"digest\": \"64bc1eeecdb17aca4cd4cc3e2e878ee5f86d95993c891232add0374bb575304e\",\n      \"format\": \"zst\",\n      \"path\": \"buck2-aarch64-pc-windows-msvc.exe\",\n      \"providers\": [\n        {\n          \"url\": \"https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-aarch64-pc-windows-msvc.exe.zst\"\n        }\n      ]\n    },\n    \"linux-aarch64\": {\n      \"size\": 34861909,\n      \"hash\": \"blake3\",\n      \"digest\": \"c94bc9096171d9c06e478646ce2fc21ab64cf0d9028aa9341de1fec075f54402\",\n      \"format\": \"zst\",\n      \"path\": \"buck2-aarch64-unknown-linux-musl\",\n      \"providers\": [\n        {\n          \"url\": \"https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-aarch64-unknown-linux-musl.zst\"\n        }\n      ]\n    },\n    \"linux-riscv64\": {\n      \"size\": 37112472,\n      \"hash\": \"blake3\",\n      \"digest\": \"0f64fbe707fd43504c353bb633b64e8f6b64884c50ebbde116f197a465d9d04a\",\n      \"format\": \"zst\",\n      \"path\": \"buck2-riscv64gc-unknown-linux-gnu\",\n      \"providers\": [\n        {\n          \"url\": \"https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-riscv64gc-unknown-linux-gnu.zst\"\n        }\n      ]\n    },\n    \"macos-x86_64\": {\n      \"size\": 34156976,\n      \"hash\": \"blake3\",\n      \"digest\": \"a158bd24817ece46ce43e700818704182421ad95a0b2031c2e2a9e6d0fcf29a7\",\n      \"format\": \"zst\",\n      \"path\": \"buck2-x86_64-apple-darwin\",\n      \"providers\": [\n        {\n          \"url\": \"https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-x86_64-apple-darwin.zst\"\n        }\n      ]\n    },\n    \"windows-x86_64\": {\n      \"size\": 29670925,\n      \"hash\": \"blake3\",\n      \"digest\": \"a6522f77c3c9680539df265fc85c8ac98888fa4bb8971e0c43e4b08cc46f3c4a\",\n      \"format\": \"zst\",\n      \"path\": \"buck2-x86_64-pc-windows-msvc.exe\",\n      \"providers\": [\n        {\n          \"url\": \"https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-x86_64-pc-windows-msvc.exe.zst\"\n        }\n      ]\n    },\n    \"linux-x86_64\": {\n      \"size\": 36453720,\n      \"hash\": \"blake3\",\n      \"digest\": \"61bc5de2f2847fef8a4aa80c6d0b29352e1cce667adbe7d07b79c5ced44d7561\",\n      \"format\": \"zst\",\n      \"path\": \"buck2-x86_64-unknown-linux-musl\",\n      \"providers\": [\n        {\n          \"url\": \"https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-x86_64-unknown-linux-musl.zst\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "build/buck2/README.md",
    "content": "# Easy buck2 builds for Facebook projects\n\nThis directory contains buck2 targets designed to simplify buck2 builds of\nMeta open source projects.\n\nThe most notable target is `//build/buck2/install_deps`, which will attempt to\ndiscover and install necessary third party packages from apt / dnf / etc.\nSee the \"repos\" directory for the currently supported platforms.\n\n## Deployment\n\nThis directory is copied literally into a number of different Facebook open\nsource repositories.  Any change made to code in this directory will be\nautomatically be replicated by our open source tooling into all GitHub hosted\nrepositories that use `buck2`.  Typically this directory is copied\ninto the open source repositories as `build/buck2/`.\n"
  },
  {
    "path": "build/buck2/install_deps/BUCK",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the LICENSE file found in the root\n# directory of this source tree.\nload(\"@fbcode_macros//build_defs:native_rules.bzl\", \"buck_sh_binary\")\n\noncall(\"open_source\")\n\nbuck_sh_binary(\n    name = \"install_deps\",\n    main = \"install_deps.sh\",\n    resources = glob([\"repos/*\"]),\n)\n"
  },
  {
    "path": "build/buck2/install_deps/install_deps.sh",
    "content": "#!/bin/sh\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the LICENSE file found in the root\n# directory of this source tree.\n\nif [ -z \"$INSTALL_COMMAND\" ]; then\n  if [ -f /etc/os-release ]; then\n    . /etc/os-release;\n  fi\n\n  if command -v brew >/dev/null; then\n    ID=\"homebrew\";\n  fi\n\n  if [ -f \"$BUCK_DEFAULT_RUNTIME_RESOURCES/repos/$ID\" ]; then\n    # shellcheck disable=SC1090\n    . \"$BUCK_DEFAULT_RUNTIME_RESOURCES/repos/$ID\";\n  else\n    echo \"Unable to determine platform id / install commands\";\n    return 1;\n  fi\nfi\n\nif [ -z \"${BUCK2_COMMAND}\" ]; then\n  if command -v buck2 >/dev/null; then\n    BUCK2_COMMAND=\"buck2\"\n  elif command -v dotslash >/dev/null && [ -f ./buck2 ]; then\n    BUCK2_COMMAND=\"dotslash ./buck2\"\n  else\n    echo \"Unable to determine buck2 command\";\n    return 1;\n  fi\nfi\n\n__confirm() {\n  echo \"Press \\\"y\\\" to continue\"\n  read -r REPLY\n  expr \"X$REPLY\" : '^X[Yy]$' >/dev/null\n}\n\nPKG_FILE=$(mktemp /tmp/buck2-install-pkgs.XXXXXX)\n\nif ! command -v jq >/dev/null; then\n  echo \"Failed to find jq command, attempting to install with\"\n  echo\n  echo \"$INSTALL_COMMAND\" jq\n  echo\n  if __confirm; then\n    eval \"$INSTALL_COMMAND jq\"\n  else\n    echo \"Not confirmed, exiting\";\n    exit 1\n  fi\nfi\n\neval \"$BUCK2_COMMAND cquery 'kind(system_packages, deps(//...))' \\\\\n    --output-attribute=packages --modifier $ID --json 2>/dev/null \\\\\n  | jq -r '.[].packages[]' \\\\\n  | sort \\\\\n  | uniq \\\\\n  > $PKG_FILE\"\n\necho \"About to install the project dependencies with the following command:\"\necho\neval \"cat $PKG_FILE | xargs echo $INSTALL_COMMAND\"\necho\nif __confirm; then\n  eval \"cat $PKG_FILE | xargs -r $INSTALL_COMMAND\"\nelse\n  echo \"Not installing dependencies\"\nfi\n\nrm \"$PKG_FILE\"\n"
  },
  {
    "path": "build/buck2/install_deps/repos/fedora",
    "content": "INSTALL_COMMAND=\"sudo -E dnf install -y\"\n"
  },
  {
    "path": "build/buck2/install_deps/repos/homebrew",
    "content": "INSTALL_COMMAND=\"brew install\"\n"
  },
  {
    "path": "build/buck2/install_deps/repos/ubuntu",
    "content": "INSTALL_COMMAND=\"sudo -E apt-get install -y\"\n"
  },
  {
    "path": "build/fbcode_builder/.gitignore",
    "content": "# Facebook-internal CI builds don't have write permission outside of the\n# source tree, so we install all projects into this directory.\n/facebook_ci\n__pycache__/\n*.pyc\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBBuildOptions.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\nfunction (fb_activate_static_library_option)\n  option(USE_STATIC_DEPS_ON_UNIX\n    \"If enabled, use static dependencies on unix systems. This is generally discouraged.\"\n    OFF\n  )\n  # Mark USE_STATIC_DEPS_ON_UNIX as an \"advanced\" option, since enabling it\n  # is generally discouraged.\n  mark_as_advanced(USE_STATIC_DEPS_ON_UNIX)\n\n  if(UNIX AND USE_STATIC_DEPS_ON_UNIX)\n    SET(CMAKE_FIND_LIBRARY_SUFFIXES \".a\" PARENT_SCOPE)\n  endif()\n\n  option(PREFER_STATIC_DEPS_ON_UNIX\n    \"If enabled, use static dependencies on unix systems as possible as we can. This is generally discouraged.\"\n    OFF\n  )\n  # Mark PREFER_STATIC_DEPS_ON_UNIX as an \"advanced\" option, since enabling it\n  # is generally discouraged.\n  mark_as_advanced(PREFER_STATIC_DEPS_ON_UNIX)\n\n  if(UNIX AND PREFER_STATIC_DEPS_ON_UNIX)\n    SET(CMAKE_FIND_LIBRARY_SUFFIXES \".a\" \".so\" PARENT_SCOPE)\n  endif()\nendfunction()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBCMakeParseArgs.cmake",
    "content": "#\n# Copyright (c) Facebook, Inc. and its affiliates.\n#\n# Helper function for parsing arguments to a CMake function.\n#\n# This function is very similar to CMake's built-in cmake_parse_arguments()\n# function, with some improvements:\n# - This function correctly handles empty arguments.  (cmake_parse_arguments()\n#   ignores empty arguments.)\n# - If a multi-value argument is specified more than once, the subsequent\n#   arguments are appended to the original list rather than replacing it.  e.g.\n#   if \"SOURCES\" is a multi-value argument, and the argument list contains\n#   \"SOURCES a b c SOURCES x y z\" then the resulting value for SOURCES will be\n#   \"a;b;c;x;y;z\" rather than \"x;y;z\"\n# - This function errors out by default on unrecognized arguments.  You can\n#   pass in an extra \"ALLOW_UNPARSED_ARGS\" argument to make it behave like\n#   cmake_parse_arguments(), and return the unparsed arguments in a\n#   <prefix>_UNPARSED_ARGUMENTS variable instead.\n#\n# It does look like cmake_parse_arguments() handled empty arguments correctly\n# from CMake 3.0 through 3.3, but it seems like this was probably broken when\n# it was turned into a built-in function in CMake 3.4.  Here is discussion and\n# patches that fixed this behavior prior to CMake 3.0:\n# https://cmake.org/pipermail/cmake-developers/2013-November/020607.html\n#\n# The one downside to this function over the built-in cmake_parse_arguments()\n# is that I don't think we can achieve the PARSE_ARGV behavior in a non-builtin\n# function, so we can't properly handle arguments that contain \";\".  CMake will\n# treat the \";\" characters as list element separators, and treat it as multiple\n# separate arguments.\n#\nfunction(fb_cmake_parse_args PREFIX OPTIONS ONE_VALUE_ARGS MULTI_VALUE_ARGS ARGS)\n  foreach(option IN LISTS ARGN)\n    if (\"${option}\" STREQUAL \"ALLOW_UNPARSED_ARGS\")\n      set(ALLOW_UNPARSED_ARGS TRUE)\n    else()\n      message(\n        FATAL_ERROR\n        \"unknown optional argument for fb_cmake_parse_args(): ${option}\"\n      )\n    endif()\n  endforeach()\n\n  # Define all options as FALSE in the parent scope to start with\n  foreach(var_name IN LISTS OPTIONS)\n    set(\"${PREFIX}_${var_name}\" \"FALSE\" PARENT_SCOPE)\n  endforeach()\n\n  # TODO: We aren't extremely strict about error checking for one-value\n  # arguments here.  e.g., we don't complain if a one-value argument is\n  # followed by another option/one-value/multi-value name rather than an\n  # argument.  We also don't complain if a one-value argument is the last\n  # argument and isn't followed by a value.\n\n  list(APPEND all_args ${ONE_VALUE_ARGS})\n  list(APPEND all_args ${MULTI_VALUE_ARGS})\n  set(current_variable)\n  set(unparsed_args)\n  foreach(arg IN LISTS ARGS)\n    list(FIND OPTIONS \"${arg}\" opt_index)\n    if(\"${opt_index}\" EQUAL -1)\n      list(FIND all_args \"${arg}\" arg_index)\n      if(\"${arg_index}\" EQUAL -1)\n        # This argument does not match an argument name,\n        # must be an argument value\n        if(\"${current_variable}\" STREQUAL \"\")\n          list(APPEND unparsed_args \"${arg}\")\n        else()\n          # Ugh, CMake lists have a pretty fundamental flaw: they cannot\n          # distinguish between an empty list and a list with a single empty\n          # element.  We track our own SEEN_VALUES_arg setting to help\n          # distinguish this and behave properly here.\n          if (\"${SEEN_${current_variable}}\" AND \"${${current_variable}}\" STREQUAL \"\")\n            set(\"${current_variable}\" \";${arg}\")\n          else()\n            list(APPEND \"${current_variable}\" \"${arg}\")\n          endif()\n          set(\"SEEN_${current_variable}\" TRUE)\n        endif()\n      else()\n        # We found a single- or multi-value argument name\n        set(current_variable \"VALUES_${arg}\")\n        set(\"SEEN_${arg}\" TRUE)\n      endif()\n    else()\n      # We found an option variable\n      set(\"${PREFIX}_${arg}\" \"TRUE\" PARENT_SCOPE)\n      set(current_variable)\n    endif()\n  endforeach()\n\n  foreach(arg_name IN LISTS ONE_VALUE_ARGS)\n    if(NOT \"${SEEN_${arg_name}}\")\n      unset(\"${PREFIX}_${arg_name}\" PARENT_SCOPE)\n    elseif(NOT \"${SEEN_VALUES_${arg_name}}\")\n      # If the argument was seen but a value wasn't specified, error out.\n      # We require exactly one value to be specified.\n      message(\n        FATAL_ERROR \"argument ${arg_name} was specified without a value\"\n      )\n    else()\n      list(LENGTH \"VALUES_${arg_name}\" num_args)\n      if(\"${num_args}\" EQUAL 0)\n        # We know an argument was specified and that we called list(APPEND).\n        # If CMake thinks the list is empty that means there is really a single\n        # empty element in the list.\n        set(\"${PREFIX}_${arg_name}\" \"\" PARENT_SCOPE)\n      elseif(\"${num_args}\" EQUAL 1)\n        list(GET \"VALUES_${arg_name}\" 0 arg_value)\n        set(\"${PREFIX}_${arg_name}\" \"${arg_value}\" PARENT_SCOPE)\n      else()\n        message(\n          FATAL_ERROR \"too many arguments specified for ${arg_name}: \"\n          \"${VALUES_${arg_name}}\"\n        )\n      endif()\n    endif()\n  endforeach()\n\n  foreach(arg_name IN LISTS MULTI_VALUE_ARGS)\n    # If this argument name was never seen, then unset the parent scope\n    if (NOT \"${SEEN_${arg_name}}\")\n      unset(\"${PREFIX}_${arg_name}\" PARENT_SCOPE)\n    else()\n      # TODO: Our caller still won't be able to distinguish between an empty\n      # list and a list with a single empty element.  We can tell which is\n      # which, but CMake lists don't make it easy to show this to our caller.\n      set(\"${PREFIX}_${arg_name}\" \"${VALUES_${arg_name}}\" PARENT_SCOPE)\n    endif()\n  endforeach()\n\n  # By default we fatal out on unparsed arguments, but return them to the\n  # caller if ALLOW_UNPARSED_ARGS was specified.\n  if (DEFINED unparsed_args)\n    if (\"${ALLOW_UNPARSED_ARGS}\")\n      set(\"${PREFIX}_UNPARSED_ARGUMENTS\" \"${unparsed_args}\" PARENT_SCOPE)\n    else()\n      message(FATAL_ERROR \"unrecognized arguments: ${unparsed_args}\")\n    endif()\n  endif()\nendfunction()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBCompilerSettings.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\n# This file applies common compiler settings that are shared across\n# a number of Facebook opensource projects.\n# Please use caution and your best judgement before making changes\n# to these shared compiler settings in order to avoid accidentally\n# breaking a build in another project!\n\nif (WIN32)\n  include(FBCompilerSettingsMSVC)\nelse()\n  include(FBCompilerSettingsUnix)\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBCompilerSettingsMSVC.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\n# This file applies common compiler settings that are shared across\n# a number of Facebook opensource projects.\n# Please use caution and your best judgement before making changes\n# to these shared compiler settings in order to avoid accidentally\n# breaking a build in another project!\n\nadd_compile_options(\n  /wd4250 # 'class1' : inherits 'class2::member' via dominance\n  /Zc:preprocessor # Enable conforming preprocessor for __VA_OPT__ support\n)\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBCompilerSettingsUnix.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\n# This file applies common compiler settings that are shared across\n# a number of Facebook opensource projects.\n# Please use caution and your best judgement before making changes\n# to these shared compiler settings in order to avoid accidentally\n# breaking a build in another project!\n\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -g -Wall -Wextra -Wno-deprecated -Wno-deprecated-declarations\")\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBPythonBinary.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\ninclude(FBCMakeParseArgs)\n\n#\n# This file contains helper functions for building self-executing Python\n# binaries.\n#\n# This is somewhat different than typical python installation with\n# distutils/pip/virtualenv/etc.  We primarily want to build a standalone\n# executable, isolated from other Python packages on the system.  We don't want\n# to install files into the standard library python paths.  This is more\n# similar to PEX (https://github.com/pantsbuild/pex) and XAR\n# (https://github.com/facebookincubator/xar).  (In the future it would be nice\n# to update this code to also support directly generating XAR files if XAR is\n# available.)\n#\n# We also want to be able to easily define \"libraries\" of python files that can\n# be shared and re-used between these standalone python executables, and can be\n# shared across projects in different repositories.  This means that we do need\n# a way to \"install\" libraries so that they are visible to CMake builds in\n# other repositories, without actually installing them in the standard python\n# library paths.\n#\n\n# If the caller has not already found Python, do so now.\n# If we fail to find python now we won't fail immediately, but\n# add_fb_python_executable() or add_fb_python_library() will fatal out if they\n# are used.\nif(NOT TARGET Python3::Interpreter)\n  # CMake 3.12+ ships with a FindPython3.cmake module.  Try using it first.\n  # We find with QUIET here, since otherwise this generates some noisy warnings\n  # on versions of CMake before 3.12\n  if (WIN32)\n    # On Windows we need both the Interpreter as well as the Development\n    # libraries.\n    find_package(Python3 COMPONENTS Interpreter Development QUIET)\n  else()\n    find_package(Python3 COMPONENTS Interpreter QUIET)\n  endif()\n  if(Python3_Interpreter_FOUND)\n    message(STATUS \"Found Python 3: ${Python3_EXECUTABLE}\")\n  else()\n    # Try with the FindPythonInterp.cmake module available in older CMake\n    # versions.  Check to see if the caller has already searched for this\n    # themselves first.\n    if(NOT PYTHONINTERP_FOUND)\n      set(Python_ADDITIONAL_VERSIONS 3 3.6 3.5 3.4 3.3 3.2 3.1)\n      find_package(PythonInterp)\n      # TODO: On Windows we require the Python libraries as well.\n      # We currently do not search for them on this code path.\n      # For now we require building with CMake 3.12+ on Windows, so that the\n      # FindPython3 code path above is available.\n    endif()\n    if(PYTHONINTERP_FOUND)\n      if(\"${PYTHON_VERSION_MAJOR}\" GREATER_EQUAL 3)\n        set(Python3_EXECUTABLE \"${PYTHON_EXECUTABLE}\")\n        add_custom_target(Python3::Interpreter)\n      else()\n        string(\n          CONCAT FBPY_FIND_PYTHON_ERR\n          \"found Python ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}, \"\n          \"but need Python 3\"\n        )\n      endif()\n    endif()\n  endif()\nendif()\n\n# Find our helper program.\n# We typically install this in the same directory as this .cmake file.\nfind_program(\n  FB_MAKE_PYTHON_ARCHIVE \"make_fbpy_archive.py\"\n  PATHS ${CMAKE_MODULE_PATH}\n)\nset(FB_PY_TEST_MAIN \"${CMAKE_CURRENT_LIST_DIR}/fb_py_test_main.py\")\nset(\n  FB_PY_TEST_DISCOVER_SCRIPT\n  \"${CMAKE_CURRENT_LIST_DIR}/FBPythonTestAddTests.cmake\"\n)\nset(\n  FB_PY_WIN_MAIN_C\n  \"${CMAKE_CURRENT_LIST_DIR}/fb_py_win_main.c\"\n)\n\n# An option to control the default installation location for\n# install_fb_python_library().  This is relative to ${CMAKE_INSTALL_PREFIX}\nset(\n  FBPY_LIB_INSTALL_DIR \"lib/fb-py-libs\" CACHE STRING\n  \"The subdirectory where FB python libraries should be installed\"\n)\n\n#\n# Build a self-executing python binary.\n#\n# This accepts the same arguments as add_fb_python_library().\n#\n# In addition, a MAIN_MODULE argument is accepted.  This argument specifies\n# which module should be started as the __main__ module when the executable is\n# run.  If left unspecified, a __main__.py script must be present in the\n# manifest.\n#\nfunction(add_fb_python_executable TARGET)\n  fb_py_check_available()\n\n  # Parse the arguments\n  set(one_value_args BASE_DIR NAMESPACE MAIN_MODULE TYPE)\n  set(multi_value_args SOURCES DEPENDS)\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n  fb_py_process_default_args(ARG_NAMESPACE ARG_BASE_DIR)\n\n  # Use add_fb_python_library() to perform most of our source handling\n  add_fb_python_library(\n    \"${TARGET}.main_lib\"\n    BASE_DIR \"${ARG_BASE_DIR}\"\n    NAMESPACE \"${ARG_NAMESPACE}\"\n    SOURCES ${ARG_SOURCES}\n    DEPENDS ${ARG_DEPENDS}\n  )\n\n  set(\n    manifest_files\n    \"$<TARGET_PROPERTY:${TARGET}.main_lib.py_lib,INTERFACE_INCLUDE_DIRECTORIES>\"\n  )\n  set(\n    source_files\n    \"$<TARGET_PROPERTY:${TARGET}.main_lib.py_lib,INTERFACE_SOURCES>\"\n  )\n\n  # The command to build the executable archive.\n  #\n  # If we are using CMake 3.8+ we can use COMMAND_EXPAND_LISTS.\n  # CMP0067 isn't really the policy we care about, but seems like the best way\n  # to check if we are running 3.8+.\n  if (POLICY CMP0067)\n    set(extra_cmd_params COMMAND_EXPAND_LISTS)\n    set(make_py_args \"${manifest_files}\")\n  else()\n    set(extra_cmd_params)\n    set(make_py_args --manifest-separator \"::\" \"$<JOIN:${manifest_files},::>\")\n  endif()\n\n  set(output_file \"${TARGET}${CMAKE_EXECUTABLE_SUFFIX}\")\n  if(WIN32)\n    set(zipapp_output \"${TARGET}.py_zipapp\")\n  else()\n    set(zipapp_output \"${output_file}\")\n  endif()\n  set(zipapp_output_file \"${zipapp_output}\")\n\n  set(is_dir_output FALSE)\n  if(DEFINED ARG_TYPE)\n    list(APPEND make_py_args \"--type\" \"${ARG_TYPE}\")\n    if (\"${ARG_TYPE}\" STREQUAL \"dir\")\n      set(is_dir_output TRUE)\n      # CMake doesn't really seem to like having a directory specified as an\n      # output; specify the __main__.py file as the output instead.\n      set(zipapp_output_file \"${zipapp_output}/__main__.py\")\n      # Update output_file to match zipapp_output_file for dir type\n      set(output_file \"${zipapp_output_file}\")\n      list(APPEND\n        extra_cmd_params\n        COMMAND \"${CMAKE_COMMAND}\" -E remove_directory \"${zipapp_output}\"\n      )\n    endif()\n  endif()\n\n  if(DEFINED ARG_MAIN_MODULE)\n    list(APPEND make_py_args \"--main\" \"${ARG_MAIN_MODULE}\")\n  endif()\n\n  add_custom_command(\n    OUTPUT \"${zipapp_output_file}\"\n    ${extra_cmd_params}\n    COMMAND\n      \"${Python3_EXECUTABLE}\" \"${FB_MAKE_PYTHON_ARCHIVE}\"\n      -o \"${zipapp_output}\"\n      ${make_py_args}\n    DEPENDS\n      ${source_files}\n      \"${TARGET}.main_lib.py_sources_built\"\n      \"${FB_MAKE_PYTHON_ARCHIVE}\"\n  )\n\n  if(WIN32)\n    if(is_dir_output)\n      # TODO: generate a main executable that will invoke Python3\n      # with the correct main module inside the output directory\n    else()\n      add_executable(\"${TARGET}.winmain\" \"${FB_PY_WIN_MAIN_C}\")\n      target_link_libraries(\"${TARGET}.winmain\" Python3::Python)\n      # The Python3::Python target doesn't seem to be set up completely\n      # correctly on Windows for some reason, and we have to explicitly add\n      # ${Python3_LIBRARY_DIRS} to the target link directories.\n      target_link_directories(\n        \"${TARGET}.winmain\"\n        PUBLIC ${Python3_LIBRARY_DIRS}\n      )\n      add_custom_command(\n        OUTPUT \"${output_file}\"\n        DEPENDS \"${TARGET}.winmain\" \"${zipapp_output_file}\"\n        COMMAND\n          \"cmd.exe\" \"/c\" \"copy\" \"/b\"\n          \"${TARGET}.winmain${CMAKE_EXECUTABLE_SUFFIX}+${zipapp_output}\"\n          \"${output_file}\"\n      )\n    endif()\n  endif()\n\n  # Add an \"ALL\" target that depends on force ${TARGET},\n  # so that ${TARGET} will be included in the default list of build targets.\n  add_custom_target(\"${TARGET}.GEN_PY_EXE\" ALL DEPENDS \"${output_file}\")\n\n  # Allow resolving the executable path for the target that we generate\n  # via a generator expression like:\n  # \"WATCHMAN_WAIT_PATH=$<TARGET_PROPERTY:watchman-wait.GEN_PY_EXE,EXECUTABLE>\"\n  set_property(TARGET \"${TARGET}.GEN_PY_EXE\"\n      PROPERTY EXECUTABLE \"${CMAKE_CURRENT_BINARY_DIR}/${output_file}\")\nendfunction()\n\n# Define a python unittest executable.\n# The executable is built using add_fb_python_executable and has the\n# following differences:\n#\n# Each of the source files specified in SOURCES will be imported\n# and have unittest discovery performed upon them.\n# Those sources will be imported in the top level namespace.\n#\n# The ENV argument allows specifying a list of \"KEY=VALUE\"\n# pairs that will be used by the test runner to set up the environment\n# in the child process prior to running the test.  This is useful for\n# passing additional configuration to the test.\nfunction(add_fb_python_unittest TARGET)\n  # Parse the arguments\n  set(multi_value_args SOURCES DEPENDS ENV PROPERTIES)\n  set(\n    one_value_args\n    WORKING_DIRECTORY BASE_DIR NAMESPACE TEST_LIST DISCOVERY_TIMEOUT TYPE\n  )\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n  fb_py_process_default_args(ARG_NAMESPACE ARG_BASE_DIR)\n  if(NOT ARG_WORKING_DIRECTORY)\n    # Default the working directory to the current binary directory.\n    # This matches the default behavior of add_test() and other standard\n    # test functions like gtest_discover_tests()\n    set(ARG_WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")\n  endif()\n  if(NOT ARG_TEST_LIST)\n    set(ARG_TEST_LIST \"${TARGET}_TESTS\")\n  endif()\n  if(NOT ARG_DISCOVERY_TIMEOUT)\n    set(ARG_DISCOVERY_TIMEOUT 5)\n  endif()\n\n  # Tell our test program the list of modules to scan for tests.\n  # We scan all modules directly listed in our SOURCES argument, and skip\n  # modules that came from dependencies in the DEPENDS list.\n  #\n  # This is written into a __test_modules__.py module that the test runner\n  # will look at.\n  set(\n    test_modules_path\n    \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_test_modules.py\"\n  )\n  file(WRITE \"${test_modules_path}\" \"TEST_MODULES = [\\n\")\n  string(REPLACE \".\" \"/\" namespace_dir \"${ARG_NAMESPACE}\")\n  if (NOT \"${namespace_dir}\" STREQUAL \"\")\n    set(namespace_dir \"${namespace_dir}/\")\n  endif()\n  set(test_modules)\n  foreach(src_path IN LISTS ARG_SOURCES)\n    fb_py_compute_dest_path(\n      abs_source dest_path\n      \"${src_path}\" \"${namespace_dir}\" \"${ARG_BASE_DIR}\"\n    )\n    string(REPLACE \"/\" \".\" module_name \"${dest_path}\")\n    string(REGEX REPLACE \"\\\\.py$\" \"\" module_name \"${module_name}\")\n    list(APPEND test_modules \"${module_name}\")\n    file(APPEND \"${test_modules_path}\" \"  '${module_name}',\\n\")\n  endforeach()\n  file(APPEND \"${test_modules_path}\" \"]\\n\")\n\n  # The __main__ is provided by our runner wrapper/bootstrap\n  list(APPEND ARG_SOURCES \"${FB_PY_TEST_MAIN}=__main__.py\")\n  list(APPEND ARG_SOURCES \"${test_modules_path}=__test_modules__.py\")\n\n  if(NOT DEFINED ARG_TYPE)\n    set(ARG_TYPE \"zipapp\")\n  endif()\n\n  add_fb_python_executable(\n    \"${TARGET}\"\n    TYPE \"${ARG_TYPE}\"\n    NAMESPACE \"${ARG_NAMESPACE}\"\n    BASE_DIR \"${ARG_BASE_DIR}\"\n    SOURCES ${ARG_SOURCES}\n    DEPENDS ${ARG_DEPENDS}\n  )\n\n  # Run test discovery after the test executable is built.\n  # This logic is based on the code for gtest_discover_tests()\n  set(ctest_file_base \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET}\")\n  set(ctest_include_file \"${ctest_file_base}_include.cmake\")\n  set(ctest_tests_file \"${ctest_file_base}_tests.cmake\")\n  add_custom_command(\n    TARGET \"${TARGET}.GEN_PY_EXE\" POST_BUILD\n    BYPRODUCTS \"${ctest_tests_file}\"\n    COMMAND\n      \"${CMAKE_COMMAND}\"\n      -D \"TEST_TARGET=${TARGET}\"\n      -D \"TEST_INTERPRETER=${Python3_EXECUTABLE}\"\n      -D \"TEST_ENV=${ARG_ENV}\"\n      -D \"TEST_EXECUTABLE=$<TARGET_PROPERTY:${TARGET}.GEN_PY_EXE,EXECUTABLE>\"\n      -D \"TEST_WORKING_DIR=${ARG_WORKING_DIRECTORY}\"\n      -D \"TEST_LIST=${ARG_TEST_LIST}\"\n      -D \"TEST_PREFIX=${TARGET}::\"\n      -D \"TEST_PROPERTIES=${ARG_PROPERTIES}\"\n      -D \"CTEST_FILE=${ctest_tests_file}\"\n      -P \"${FB_PY_TEST_DISCOVER_SCRIPT}\"\n    VERBATIM\n  )\n\n  file(\n    WRITE \"${ctest_include_file}\"\n    \"if(EXISTS \\\"${ctest_tests_file}\\\")\\n\"\n    \"  include(\\\"${ctest_tests_file}\\\")\\n\"\n    \"else()\\n\"\n    \"  add_test(\\\"${TARGET}_NOT_BUILT\\\" \\\"${TARGET}_NOT_BUILT\\\")\\n\"\n    \"endif()\\n\"\n  )\n  set_property(\n    DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES\n    \"${ctest_include_file}\"\n  )\nendfunction()\n\n#\n# Define a python library.\n#\n# If you want to install a python library generated from this rule note that\n# you need to use install_fb_python_library() rather than CMake's built-in\n# install() function.  This will make it available for other downstream\n# projects to use in their add_fb_python_executable() and\n# add_fb_python_library() calls.  (You do still need to use `install(EXPORT)`\n# later to install the CMake exports.)\n#\n# Parameters:\n# - BASE_DIR <dir>:\n#   The base directory path to strip off from each source path.  All source\n#   files must be inside this directory.  If not specified it defaults to\n#   ${CMAKE_CURRENT_SOURCE_DIR}.\n# - NAMESPACE <namespace>:\n#   The destination namespace where these files should be installed in python\n#   binaries.  If not specified, this defaults to the current relative path of\n#   ${CMAKE_CURRENT_SOURCE_DIR} inside ${CMAKE_SOURCE_DIR}.  e.g., a python\n#   library defined in the directory repo_root/foo/bar will use a default\n#   namespace of \"foo.bar\"\n# - SOURCES <src1> <...>:\n#   The python source files.\n#   You may optionally specify as source using the form: PATH=ALIAS where\n#   PATH is a relative path in the source tree and ALIAS is the relative\n#   path into which PATH should be rewritten.  This is useful for mapping\n#   an executable script to the main module in a python executable.\n#   e.g.: `python/bin/watchman-wait=__main__.py`\n# - DEPENDS <target1> <...>:\n#   Other python libraries that this one depends on.\n# - INSTALL_DIR <dir>:\n#   The directory where this library should be installed.\n#   install_fb_python_library() must still be called later to perform the\n#   installation.  If a relative path is given it will be treated relative to\n#   ${CMAKE_INSTALL_PREFIX}\n#\n# CMake is unfortunately pretty crappy at being able to define custom build\n# rules & behaviors.  It doesn't support transitive property propagation\n# between custom targets; only the built-in add_executable() and add_library()\n# targets support transitive properties.\n#\n# We hack around this janky CMake behavior by (ab)using interface libraries to\n# propagate some of the data we want between targets, without actually\n# generating a C library.\n#\n# add_fb_python_library(SOMELIB) generates the following things:\n# - An INTERFACE library rule named SOMELIB.py_lib which tracks some\n#   information about transitive dependencies:\n#   - the transitive set of source files in the INTERFACE_SOURCES property\n#   - the transitive set of manifest files that this library depends on in\n#     the INTERFACE_INCLUDE_DIRECTORIES property.\n# - A custom command that generates a SOMELIB.manifest file.\n#   This file contains the mapping of source files to desired destination\n#   locations in executables that depend on this library.  This manifest file\n#   will then be read at build-time in order to build executables.\n#\nfunction(add_fb_python_library LIB_NAME)\n  fb_py_check_available()\n\n  # Parse the arguments\n  # We use fb_cmake_parse_args() rather than cmake_parse_arguments() since\n  # cmake_parse_arguments() does not handle empty arguments, and it is common\n  # for callers to want to specify an empty NAMESPACE parameter.\n  set(one_value_args BASE_DIR NAMESPACE INSTALL_DIR)\n  set(multi_value_args SOURCES DEPENDS)\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n  fb_py_process_default_args(ARG_NAMESPACE ARG_BASE_DIR)\n\n  string(REPLACE \".\" \"/\" namespace_dir \"${ARG_NAMESPACE}\")\n  if (NOT \"${namespace_dir}\" STREQUAL \"\")\n    set(namespace_dir \"${namespace_dir}/\")\n  endif()\n\n  if(NOT DEFINED ARG_INSTALL_DIR)\n    set(install_dir \"${FBPY_LIB_INSTALL_DIR}/\")\n  elseif(\"${ARG_INSTALL_DIR}\" STREQUAL \"\")\n    set(install_dir \"\")\n  else()\n    set(install_dir \"${ARG_INSTALL_DIR}/\")\n  endif()\n\n  # message(STATUS \"fb py library ${LIB_NAME}: \"\n  #         \"NS=${namespace_dir} BASE=${ARG_BASE_DIR}\")\n\n  # TODO: In the future it would be nice to support pre-compiling the source\n  # files.  We could emit a rule to compile each source file and emit a\n  # .pyc/.pyo file here, and then have the manifest reference the pyc/pyo\n  # files.\n\n  # Define a library target to help pass around information about the library,\n  # and propagate dependency information.\n  #\n  # CMake make a lot of assumptions that libraries are C++ libraries.  To help\n  # avoid confusion we name our target \"${LIB_NAME}.py_lib\" rather than just\n  # \"${LIB_NAME}\".  This helps avoid confusion if callers try to use\n  # \"${LIB_NAME}\" on their own as a target name.  (e.g., attempting to install\n  # it directly with install(TARGETS) won't work.  Callers must use\n  # install_fb_python_library() instead.)\n  add_library(\"${LIB_NAME}.py_lib\" INTERFACE)\n\n  # Emit the manifest file.\n  #\n  # We write the manifest file to a temporary path first, then copy it with\n  # configure_file(COPYONLY).  This is necessary to get CMake to understand\n  # that \"${manifest_path}\" is generated by the CMake configure phase,\n  # and allow using it as a dependency for add_custom_command().\n  # (https://gitlab.kitware.com/cmake/cmake/issues/16367)\n  set(manifest_path \"${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}.manifest\") \n  set(tmp_manifest \"${manifest_path}.tmp\")\n  file(WRITE \"${tmp_manifest}\" \"FBPY_MANIFEST 1\\n\")\n  set(abs_sources)\n  foreach(src_path IN LISTS ARG_SOURCES)\n    fb_py_compute_dest_path(\n      abs_source dest_path\n      \"${src_path}\" \"${namespace_dir}\" \"${ARG_BASE_DIR}\"\n    )\n    list(APPEND abs_sources \"${abs_source}\")\n    target_sources(\n      \"${LIB_NAME}.py_lib\" INTERFACE\n      \"$<BUILD_INTERFACE:${abs_source}>\"\n      \"$<INSTALL_INTERFACE:${install_dir}${LIB_NAME}/${dest_path}>\"\n    )\n    file(\n      APPEND \"${tmp_manifest}\"\n      \"${abs_source} :: ${dest_path}\\n\"\n    )\n  endforeach()\n  configure_file(\"${tmp_manifest}\" \"${manifest_path}\" COPYONLY)\n\n  target_include_directories(\n    \"${LIB_NAME}.py_lib\" INTERFACE\n    \"$<BUILD_INTERFACE:${manifest_path}>\"\n    \"$<INSTALL_INTERFACE:${install_dir}${LIB_NAME}.manifest>\"\n  )\n\n  # Add a target that depends on all of the source files.\n  # This is needed in case some of the source files are generated.  This will\n  # ensure that these source files are brought up-to-date before we build\n  # any python binaries that depend on this library.\n  add_custom_target(\"${LIB_NAME}.py_sources_built\" DEPENDS ${abs_sources})\n  add_dependencies(\"${LIB_NAME}.py_lib\" \"${LIB_NAME}.py_sources_built\")\n\n  # Hook up library dependencies, and also make the *.py_sources_built target\n  # depend on the sources for all of our dependencies also being up-to-date.\n  foreach(dep IN LISTS ARG_DEPENDS)\n    target_link_libraries(\"${LIB_NAME}.py_lib\" INTERFACE \"${dep}.py_lib\")\n\n    # Mark that our .py_sources_built target depends on each our our dependent\n    # libraries.  This serves two functions:\n    # - This causes CMake to generate an error message if one of the\n    #   dependencies is never defined.  The target_link_libraries() call above\n    #   won't complain if one of the dependencies doesn't exist (since it is\n    #   intended to allow passing in file names for plain library files rather\n    #   than just targets).\n    # - It ensures that sources for our dependencies are built before any\n    #   executable that depends on us.  Note that we depend on \"${dep}.py_lib\"\n    #   rather than \"${dep}.py_sources_built\" for this purpose because the\n    #   \".py_sources_built\" target won't be available for imported targets.\n    add_dependencies(\"${LIB_NAME}.py_sources_built\" \"${dep}.py_lib\")\n  endforeach()\n\n  # Add a custom command to help with library installation, in case\n  # install_fb_python_library() is called later for this library.\n  # add_custom_command() only works with file dependencies defined in the same\n  # CMakeLists.txt file, so we want to make sure this is defined here, rather\n  # then where install_fb_python_library() is called.\n  # This command won't be run by default, but will only be run if it is needed\n  # by a subsequent install_fb_python_library() call.\n  #\n  # This command copies the library contents into the build directory.\n  # It would be nicer if we could skip this intermediate copy, and just run\n  # make_fbpy_archive.py at install time to copy them directly to the desired\n  # installation directory.  Unfortunately this is difficult to do, and seems\n  # to interfere with some of the CMake code that wants to generate a manifest\n  # of installed files.\n  set(build_install_dir \"${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}.lib_install\")\n  add_custom_command(\n    OUTPUT\n      \"${build_install_dir}/${LIB_NAME}.manifest\"\n    COMMAND \"${CMAKE_COMMAND}\" -E remove_directory \"${build_install_dir}\"\n    COMMAND\n      \"${Python3_EXECUTABLE}\" \"${FB_MAKE_PYTHON_ARCHIVE}\" --type lib-install\n      --install-dir \"${LIB_NAME}\"\n      -o \"${build_install_dir}/${LIB_NAME}\" \"${manifest_path}\"\n    DEPENDS\n      \"${abs_sources}\"\n      \"${manifest_path}\"\n      \"${FB_MAKE_PYTHON_ARCHIVE}\"\n  )\n  add_custom_target(\n    \"${LIB_NAME}.py_lib_install\"\n    DEPENDS \"${build_install_dir}/${LIB_NAME}.manifest\"\n  )\n\n  # Set some properties to pass through the install paths to\n  # install_fb_python_library()\n  #\n  # Passing through ${build_install_dir} allows install_fb_python_library()\n  # to work even if used from a different CMakeLists.txt file than where\n  # add_fb_python_library() was called (i.e. such that\n  # ${CMAKE_CURRENT_BINARY_DIR} is different between the two calls).\n  set(abs_install_dir \"${install_dir}\")\n  if(NOT IS_ABSOLUTE \"${abs_install_dir}\")\n    set(abs_install_dir \"${CMAKE_INSTALL_PREFIX}/${abs_install_dir}\")\n  endif()\n  string(REGEX REPLACE \"/$\" \"\" abs_install_dir \"${abs_install_dir}\")\n  set_target_properties(\n    \"${LIB_NAME}.py_lib_install\"\n    PROPERTIES\n    INSTALL_DIR \"${abs_install_dir}\"\n    BUILD_INSTALL_DIR \"${build_install_dir}\"\n  )\nendfunction()\n\n#\n# Install an FB-style packaged python binary.\n#\n# - DESTINATION <export-name>:\n#   Associate the installed target files with the given export-name.\n#\nfunction(install_fb_python_executable TARGET)\n  # Parse the arguments\n  set(one_value_args DESTINATION)\n  set(multi_value_args)\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n\n  if(NOT DEFINED ARG_DESTINATION)\n    set(ARG_DESTINATION bin)\n  endif()\n\n  install(\n    PROGRAMS \"$<TARGET_PROPERTY:${TARGET}.GEN_PY_EXE,EXECUTABLE>\"\n    DESTINATION \"${ARG_DESTINATION}\"\n  )\nendfunction()\n\n#\n# Install a python library.\n#\n# - EXPORT <export-name>:\n#   Associate the installed target files with the given export-name.\n#\n# Note that unlike the built-in CMake install() function we do not accept a\n# DESTINATION parameter.  Instead, use the INSTALL_DIR parameter to\n# add_fb_python_library() to set the installation location.\n#\nfunction(install_fb_python_library LIB_NAME)\n  set(one_value_args EXPORT)\n  fb_cmake_parse_args(ARG \"\" \"${one_value_args}\" \"\" \"${ARGN}\")\n\n  # Export our \"${LIB_NAME}.py_lib\" target so that it will be available to\n  # downstream projects in our installed CMake config files.\n  if(DEFINED ARG_EXPORT)\n    install(TARGETS \"${LIB_NAME}.py_lib\" EXPORT \"${ARG_EXPORT}\")\n  endif()\n\n  # add_fb_python_library() emits a .py_lib_install target that will prepare\n  # the installation directory.  However, it isn't part of the \"ALL\" target and\n  # therefore isn't built by default.\n  #\n  # Make sure the ALL target depends on it now.  We have to do this by\n  # introducing yet another custom target.\n  # Add it as a dependency to the ALL target now.\n  add_custom_target(\"${LIB_NAME}.py_lib_install_all\" ALL)\n  add_dependencies(\n    \"${LIB_NAME}.py_lib_install_all\" \"${LIB_NAME}.py_lib_install\"\n  )\n\n  # Copy the intermediate install directory generated at build time into\n  # the desired install location.\n  get_target_property(dest_dir \"${LIB_NAME}.py_lib_install\" \"INSTALL_DIR\")\n  get_target_property(\n    build_install_dir \"${LIB_NAME}.py_lib_install\" \"BUILD_INSTALL_DIR\"\n  )\n  install(\n    DIRECTORY \"${build_install_dir}/${LIB_NAME}\"\n    DESTINATION \"${dest_dir}\"\n  )\n  install(\n    FILES \"${build_install_dir}/${LIB_NAME}.manifest\"\n    DESTINATION \"${dest_dir}\"\n  )\nendfunction()\n\n# Helper macro to process the BASE_DIR and NAMESPACE arguments for\n# add_fb_python_executable() and add_fb_python_executable()\nmacro(fb_py_process_default_args NAMESPACE_VAR BASE_DIR_VAR)\n  # If the namespace was not specified, default to the relative path to the\n  # current directory (starting from the repository root).\n  if(NOT DEFINED \"${NAMESPACE_VAR}\")\n    file(\n      RELATIVE_PATH \"${NAMESPACE_VAR}\"\n      \"${CMAKE_SOURCE_DIR}\"\n      \"${CMAKE_CURRENT_SOURCE_DIR}\"\n    )\n  endif()\n\n  if(NOT DEFINED \"${BASE_DIR_VAR}\")\n    # If the base directory was not specified, default to the current directory\n    set(\"${BASE_DIR_VAR}\" \"${CMAKE_CURRENT_SOURCE_DIR}\")\n  else()\n    # If the base directory was specified, always convert it to an\n    # absolute path.\n    get_filename_component(\"${BASE_DIR_VAR}\" \"${${BASE_DIR_VAR}}\" ABSOLUTE)\n  endif()\nendmacro()\n\nfunction(fb_py_check_available)\n  # Make sure that Python 3 and our make_fbpy_archive.py helper script are\n  # available.\n  if(NOT Python3_EXECUTABLE)\n    if(FBPY_FIND_PYTHON_ERR)\n      message(FATAL_ERROR \"Unable to find Python 3: ${FBPY_FIND_PYTHON_ERR}\")\n    else()\n      message(FATAL_ERROR \"Unable to find Python 3\")\n    endif()\n  endif()\n\n  if (NOT FB_MAKE_PYTHON_ARCHIVE)\n    message(\n      FATAL_ERROR \"unable to find make_fbpy_archive.py helper program (it \"\n      \"should be located in the same directory as FBPythonBinary.cmake)\"\n    )\n  endif()\nendfunction()\n\nfunction(\n    fb_py_compute_dest_path\n    src_path_output dest_path_output src_path namespace_dir base_dir\n)\n  if(\"${src_path}\" MATCHES \"=\")\n    # We want to split the string on the `=` sign, but cmake doesn't\n    # provide much in the way of helpers for this, so we rewrite the\n    # `=` sign to `;` so that we can treat it as a cmake list and\n    # then index into the components\n    string(REPLACE \"=\" \";\" src_path_list \"${src_path}\")\n    list(GET src_path_list 0 src_path)\n    # Note that we ignore the `namespace_dir` in the alias case\n    # in order to allow aliasing a source to the top level `__main__.py`\n    # filename.\n    list(GET src_path_list 1 dest_path)\n  else()\n    unset(dest_path)\n  endif()\n\n  get_filename_component(abs_source \"${src_path}\" ABSOLUTE)\n  if(NOT DEFINED dest_path)\n    file(RELATIVE_PATH rel_src \"${ARG_BASE_DIR}\" \"${abs_source}\")\n    if(\"${rel_src}\" MATCHES \"^../\")\n      message(\n        FATAL_ERROR \"${LIB_NAME}: source file \\\"${abs_source}\\\" is not inside \"\n        \"the base directory ${ARG_BASE_DIR}\"\n      )\n    endif()\n    set(dest_path \"${namespace_dir}${rel_src}\")\n  endif()\n\n  set(\"${src_path_output}\" \"${abs_source}\" PARENT_SCOPE)\n  set(\"${dest_path_output}\" \"${dest_path}\" PARENT_SCOPE)\nendfunction()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBPythonTestAddTests.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\n# Add a command to be emitted to the CTest file\nset(ctest_script)\nfunction(add_command CMD)\n  set(escaped_args \"\")\n  foreach(arg ${ARGN})\n    # Escape all arguments using \"Bracket Argument\" syntax\n    # We could skip this for argument that don't contain any special\n    # characters if we wanted to make the output slightly more human-friendly.\n    set(escaped_args \"${escaped_args} [==[${arg}]==]\")\n  endforeach()\n  set(ctest_script \"${ctest_script}${CMD}(${escaped_args})\\n\" PARENT_SCOPE)\nendfunction()\n\nif(NOT EXISTS \"${TEST_EXECUTABLE}\")\n  message(FATAL_ERROR \"Test executable does not exist: ${TEST_EXECUTABLE}\")\nendif()\nexecute_process(\n  COMMAND ${CMAKE_COMMAND} -E env ${TEST_ENV} \"${TEST_INTERPRETER}\" \"${TEST_EXECUTABLE}\" --list-tests\n  WORKING_DIRECTORY \"${TEST_WORKING_DIR}\"\n  OUTPUT_VARIABLE output\n  RESULT_VARIABLE result\n)\nif(NOT \"${result}\" EQUAL 0)\n  string(REPLACE \"\\n\" \"\\n  \" output \"${output}\")\n  message(\n    FATAL_ERROR\n    \"Error running test executable: ${TEST_EXECUTABLE}\\n\"\n    \"Output:\\n\"\n    \"  ${output}\\n\"\n  )\nendif()\n\n# Parse output\nstring(REPLACE \"\\n\" \";\" tests_list \"${output}\")\nforeach(test_name ${tests_list})\n  add_command(\n    add_test\n    \"${TEST_PREFIX}${test_name}\"\n    ${CMAKE_COMMAND} -E env ${TEST_ENV}\n    \"${TEST_INTERPRETER}\" \"${TEST_EXECUTABLE}\" \"${test_name}\"\n  )\n  add_command(\n    set_tests_properties\n    \"${TEST_PREFIX}${test_name}\"\n    PROPERTIES\n    WORKING_DIRECTORY \"${TEST_WORKING_DIR}\"\n    ${TEST_PROPERTIES}\n  )\nendforeach()\n\n# Set a list of discovered tests in the parent scope, in case users\n# want access to this list as a CMake variable\nif(TEST_LIST)\n  add_command(set ${TEST_LIST} ${tests_list})\nendif()\n\nfile(WRITE \"${CTEST_FILE}\" \"${ctest_script}\")\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBThriftCppLibrary.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\ninclude(FBCMakeParseArgs)\n\n# Generate a C++ library from a thrift file\n#\n# Parameters:\n# - SERVICES <svc1> [<svc2> ...]\n#   The names of the services defined in the thrift file.\n# - DEPENDS <dep1> [<dep2> ...]\n#   A list of other thrift C++ libraries that this library depends on.\n# - OPTIONS <opt1> [<opt2> ...]\n#   A list of options to pass to the thrift compiler.\n# - INCLUDE_DIR <path>\n#   The sub-directory where generated headers will be installed.\n#   Defaults to \"include\" if not specified.  The caller must still call\n#   install() to install the thrift library if desired.\n# - THRIFT_INCLUDE_DIR <path>\n#   The sub-directory where generated headers will be installed.\n#   Defaults to \"${INCLUDE_DIR}/thrift-files\" if not specified.\n#   The caller must still call install() to install the thrift library if\n#   desired.\nfunction(add_fbthrift_cpp_library LIB_NAME THRIFT_FILE)\n  # Parse the arguments\n  set(one_value_args INCLUDE_DIR THRIFT_INCLUDE_DIR)\n  set(multi_value_args SERVICES DEPENDS OPTIONS)\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n  if(NOT DEFINED ARG_INCLUDE_DIR)\n    set(ARG_INCLUDE_DIR \"include\")\n  endif()\n  if(NOT DEFINED ARG_THRIFT_INCLUDE_DIR)\n    set(ARG_THRIFT_INCLUDE_DIR \"${ARG_INCLUDE_DIR}/thrift-files\")\n  endif()\n\n  get_filename_component(base ${THRIFT_FILE} NAME_WE)\n  get_filename_component(\n    output_dir\n    ${CMAKE_CURRENT_BINARY_DIR}/${THRIFT_FILE}\n    DIRECTORY\n  )\n\n  # Generate relative paths in #includes\n  file(\n    RELATIVE_PATH include_prefix\n    \"${CMAKE_SOURCE_DIR}\"\n    \"${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}\"\n  )\n  get_filename_component(include_prefix ${include_prefix} DIRECTORY)\n\n  if (NOT \"${include_prefix}\" STREQUAL \"\")\n    list(APPEND ARG_OPTIONS \"include_prefix=${include_prefix}\")\n  endif()\n  # CMake 3.12 is finally getting a list(JOIN) function, but until then\n  # treating the list as a string and replacing the semicolons is good enough.\n  string(REPLACE \";\" \",\" GEN_ARG_STR \"${ARG_OPTIONS}\")\n\n  # Compute the list of generated files\n  list(APPEND generated_headers\n    \"${output_dir}/gen-cpp2/${base}_constants.h\"\n    \"${output_dir}/gen-cpp2/${base}_types.h\"\n    \"${output_dir}/gen-cpp2/${base}_types.tcc\"\n    \"${output_dir}/gen-cpp2/${base}_types_custom_protocol.h\"\n    \"${output_dir}/gen-cpp2/${base}_metadata.h\"\n  )\n  list(APPEND generated_sources\n    \"${output_dir}/gen-cpp2/${base}_constants.cpp\"\n    \"${output_dir}/gen-cpp2/${base}_data.h\"\n    \"${output_dir}/gen-cpp2/${base}_data.cpp\"\n    \"${output_dir}/gen-cpp2/${base}_types.cpp\"\n    \"${output_dir}/gen-cpp2/${base}_types_binary.cpp\"\n    \"${output_dir}/gen-cpp2/${base}_types_compact.cpp\"\n    \"${output_dir}/gen-cpp2/${base}_types_serialization.cpp\"\n    \"${output_dir}/gen-cpp2/${base}_metadata.cpp\"\n  )\n  foreach(service IN LISTS ARG_SERVICES)\n    list(APPEND generated_headers\n      \"${output_dir}/gen-cpp2/${service}.h\"\n      \"${output_dir}/gen-cpp2/${service}.tcc\"\n      \"${output_dir}/gen-cpp2/${service}AsyncClient.h\"\n      \"${output_dir}/gen-cpp2/${service}_custom_protocol.h\"\n    )\n    list(APPEND generated_sources\n      \"${output_dir}/gen-cpp2/${service}.cpp\"\n      \"${output_dir}/gen-cpp2/${service}AsyncClient.cpp\"\n      \"${output_dir}/gen-cpp2/${service}_processmap_binary.cpp\"\n      \"${output_dir}/gen-cpp2/${service}_processmap_compact.cpp\"\n    )\n  endforeach()\n\n  # This generator expression gets the list of include directories required\n  # for all of our dependencies.\n  # It requires using COMMAND_EXPAND_LISTS in the add_custom_command() call\n  # below.  COMMAND_EXPAND_LISTS is only available in CMake 3.8+\n  # If we really had to support older versions of CMake we would probably need\n  # to use a wrapper script around the thrift compiler that could take the\n  # include list as a single argument and split it up before invoking the\n  # thrift compiler.\n  if (NOT POLICY CMP0067)\n    message(FATAL_ERROR \"add_fbthrift_cpp_library() requires CMake 3.8+\")\n  endif()\n  set(\n    thrift_include_options\n    \"-I;$<JOIN:$<TARGET_PROPERTY:${LIB_NAME}.thrift_includes,INTERFACE_INCLUDE_DIRECTORIES>,;-I;>\"\n  )\n\n  # Emit the rule to run the thrift compiler\n  add_custom_command(\n    OUTPUT\n      ${generated_headers}\n      ${generated_sources}\n    COMMAND_EXPAND_LISTS\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E make_directory \"${output_dir}\"\n    COMMAND\n      \"${FBTHRIFT_COMPILER}\"\n      --legacy-strict\n      --gen \"mstch_cpp2:${GEN_ARG_STR}\"\n      \"${thrift_include_options}\"\n      -I \"${FBTHRIFT_INCLUDE_DIR}\"\n      -o \"${output_dir}\"\n      \"${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}\"\n    WORKING_DIRECTORY\n      \"${CMAKE_BINARY_DIR}\"\n    MAIN_DEPENDENCY\n      \"${THRIFT_FILE}\"\n    DEPENDS\n      ${ARG_DEPENDS}\n      \"${FBTHRIFT_COMPILER}\"\n  )\n\n  # Now emit the library rule to compile the sources\n  if (BUILD_SHARED_LIBS)\n    set(LIB_TYPE SHARED)\n  else ()\n    set(LIB_TYPE STATIC)\n  endif ()\n\n  add_library(\n    \"${LIB_NAME}\" ${LIB_TYPE}\n    ${generated_sources}\n  )\n\n  target_include_directories(\n    \"${LIB_NAME}\"\n    PUBLIC\n      \"$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>\"\n      \"$<INSTALL_INTERFACE:${ARG_INCLUDE_DIR}>\"\n      ${Xxhash_INCLUDE_DIR}\n  )\n  target_link_libraries(\n    \"${LIB_NAME}\"\n    PUBLIC\n      ${ARG_DEPENDS}\n      FBThrift::thriftcpp2\n      Folly::folly\n      mvfst::mvfst_server_async_tran\n      mvfst::mvfst_server\n      ${Xxhash_LIBRARY}\n  )\n\n  # Add ${generated_headers} to the PUBLIC_HEADER property for ${LIB_NAME}\n  #\n  # This allows callers to install it using\n  # \"install(TARGETS ${LIB_NAME} PUBLIC_HEADER)\"\n  # However, note that CMake's PUBLIC_HEADER behavior is rather inflexible,\n  # and does have any way to preserve header directory structure.  Callers\n  # must be careful to use the correct PUBLIC_HEADER DESTINATION parameter\n  # when doing this, to put the files the correct directory themselves.\n  # We define a HEADER_INSTALL_DIR property with the include directory prefix,\n  # so typically callers should specify the PUBLIC_HEADER DESTINATION as\n  # \"$<TARGET_PROPERTY:${LIB_NAME},HEADER_INSTALL_DIR>\"\n  set_property(\n    TARGET \"${LIB_NAME}\"\n    PROPERTY PUBLIC_HEADER ${generated_headers}\n  )\n\n  # Define a dummy interface library to help propagate the thrift include\n  # directories between dependencies.\n  add_library(\"${LIB_NAME}.thrift_includes\" INTERFACE)\n  target_include_directories(\n    \"${LIB_NAME}.thrift_includes\"\n    INTERFACE\n      \"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>\"\n      \"$<INSTALL_INTERFACE:${ARG_THRIFT_INCLUDE_DIR}>\"\n  )\n  foreach(dep IN LISTS ARG_DEPENDS)\n    target_link_libraries(\n      \"${LIB_NAME}.thrift_includes\"\n      INTERFACE \"${dep}.thrift_includes\"\n    )\n  endforeach()\n\n  set_target_properties(\n    \"${LIB_NAME}\"\n    PROPERTIES\n      EXPORT_PROPERTIES \"THRIFT_INSTALL_DIR\"\n      THRIFT_INSTALL_DIR \"${ARG_THRIFT_INCLUDE_DIR}/${include_prefix}\"\n      HEADER_INSTALL_DIR \"${ARG_INCLUDE_DIR}/${include_prefix}/gen-cpp2\"\n  )\nendfunction()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBThriftLibrary.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\ninclude(FBCMakeParseArgs)\ninclude(FBThriftPyLibrary)\ninclude(FBThriftCppLibrary)\n\n#\n# add_fbthrift_library()\n#\n# This is a convenience function that generates thrift libraries for multiple\n# languages.\n#\n# For example:\n#   add_fbthrift_library(\n#     foo foo.thrift\n#     LANGUAGES cpp py\n#     SERVICES Foo\n#     DEPENDS bar)\n#\n# will be expanded into two separate calls:\n#\n# add_fbthrift_cpp_library(foo_cpp foo.thrift SERVICES Foo DEPENDS bar_cpp)\n# add_fbthrift_py_library(foo_py foo.thrift SERVICES Foo DEPENDS bar_py)\n#\nfunction(add_fbthrift_library LIB_NAME THRIFT_FILE)\n  # Parse the arguments\n  set(one_value_args PY_NAMESPACE INCLUDE_DIR THRIFT_INCLUDE_DIR)\n  set(multi_value_args SERVICES DEPENDS LANGUAGES CPP_OPTIONS PY_OPTIONS)\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n\n  if(NOT DEFINED ARG_INCLUDE_DIR)\n    set(ARG_INCLUDE_DIR \"include\")\n  endif()\n  if(NOT DEFINED ARG_THRIFT_INCLUDE_DIR)\n    set(ARG_THRIFT_INCLUDE_DIR \"${ARG_INCLUDE_DIR}/thrift-files\")\n  endif()\n\n  # CMake 3.12+ adds list(TRANSFORM) which would be nice to use here, but for\n  # now we still want to support older versions of CMake.\n  set(CPP_DEPENDS)\n  set(PY_DEPENDS)\n  foreach(dep IN LISTS ARG_DEPENDS)\n    list(APPEND CPP_DEPENDS \"${dep}_cpp\")\n    list(APPEND PY_DEPENDS \"${dep}_py\")\n  endforeach()\n\n  foreach(lang IN LISTS ARG_LANGUAGES)\n    if (\"${lang}\" STREQUAL \"cpp\")\n      add_fbthrift_cpp_library(\n        \"${LIB_NAME}_cpp\" \"${THRIFT_FILE}\"\n        SERVICES ${ARG_SERVICES}\n        DEPENDS ${CPP_DEPENDS}\n        OPTIONS ${ARG_CPP_OPTIONS}\n        INCLUDE_DIR \"${ARG_INCLUDE_DIR}\"\n        THRIFT_INCLUDE_DIR \"${ARG_THRIFT_INCLUDE_DIR}\"\n      )\n    elseif (\"${lang}\" STREQUAL \"py\" OR \"${lang}\" STREQUAL \"python\")\n      if (DEFINED ARG_PY_NAMESPACE)\n        set(namespace_args NAMESPACE \"${ARG_PY_NAMESPACE}\")\n      endif()\n      add_fbthrift_py_library(\n        \"${LIB_NAME}_py\" \"${THRIFT_FILE}\"\n        SERVICES ${ARG_SERVICES}\n        ${namespace_args}\n        DEPENDS ${PY_DEPENDS}\n        OPTIONS ${ARG_PY_OPTIONS}\n        THRIFT_INCLUDE_DIR \"${ARG_THRIFT_INCLUDE_DIR}\"\n      )\n    else()\n      message(\n        FATAL_ERROR \"unknown language for thrift library ${LIB_NAME}: ${lang}\"\n      )\n    endif()\n  endforeach()\nendfunction()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FBThriftPyLibrary.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n\ninclude(FBCMakeParseArgs)\ninclude(FBPythonBinary)\n\n# Generate a Python library from a thrift file\nfunction(add_fbthrift_py_library LIB_NAME THRIFT_FILE)\n  # Parse the arguments\n  set(one_value_args NAMESPACE THRIFT_INCLUDE_DIR)\n  set(multi_value_args SERVICES DEPENDS OPTIONS)\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n\n  if(NOT DEFINED ARG_THRIFT_INCLUDE_DIR)\n    set(ARG_THRIFT_INCLUDE_DIR \"include/thrift-files\")\n  endif()\n\n  get_filename_component(base ${THRIFT_FILE} NAME_WE)\n  set(output_dir \"${CMAKE_CURRENT_BINARY_DIR}/${THRIFT_FILE}-py\")\n\n  # Parse the namespace value\n  if (NOT DEFINED ARG_NAMESPACE)\n    set(ARG_NAMESPACE \"${base}\")\n  endif()\n\n  string(REPLACE \".\" \"/\" namespace_dir \"${ARG_NAMESPACE}\")\n  set(py_output_dir \"${output_dir}/gen-py/${namespace_dir}\")\n  list(APPEND generated_sources\n    \"${py_output_dir}/__init__.py\"\n    \"${py_output_dir}/ttypes.py\"\n    \"${py_output_dir}/constants.py\"\n  )\n  foreach(service IN LISTS ARG_SERVICES)\n    list(APPEND generated_sources\n      ${py_output_dir}/${service}.py\n    )\n  endforeach()\n\n  # Define a dummy interface library to help propagate the thrift include\n  # directories between dependencies.\n  add_library(\"${LIB_NAME}.thrift_includes\" INTERFACE)\n  target_include_directories(\n    \"${LIB_NAME}.thrift_includes\"\n    INTERFACE\n      \"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>\"\n      \"$<INSTALL_INTERFACE:${ARG_THRIFT_INCLUDE_DIR}>\"\n  )\n  foreach(dep IN LISTS ARG_DEPENDS)\n    target_link_libraries(\n      \"${LIB_NAME}.thrift_includes\"\n      INTERFACE \"${dep}.thrift_includes\"\n    )\n  endforeach()\n\n  # This generator expression gets the list of include directories required\n  # for all of our dependencies.\n  # It requires using COMMAND_EXPAND_LISTS in the add_custom_command() call\n  # below.  COMMAND_EXPAND_LISTS is only available in CMake 3.8+\n  # If we really had to support older versions of CMake we would probably need\n  # to use a wrapper script around the thrift compiler that could take the\n  # include list as a single argument and split it up before invoking the\n  # thrift compiler.\n  if (NOT POLICY CMP0067)\n    message(FATAL_ERROR \"add_fbthrift_py_library() requires CMake 3.8+\")\n  endif()\n  set(\n    thrift_include_options\n    \"-I;$<JOIN:$<TARGET_PROPERTY:${LIB_NAME}.thrift_includes,INTERFACE_INCLUDE_DIRECTORIES>,;-I;>\"\n  )\n\n  # Always force generation of \"new-style\" python classes for Python 2\n  list(APPEND ARG_OPTIONS \"new_style\")\n  # CMake 3.12 is finally getting a list(JOIN) function, but until then\n  # treating the list as a string and replacing the semicolons is good enough.\n  string(REPLACE \";\" \",\" GEN_ARG_STR \"${ARG_OPTIONS}\")\n\n  # Emit the rule to run the thrift compiler\n  add_custom_command(\n    OUTPUT\n      ${generated_sources}\n    COMMAND_EXPAND_LISTS\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E make_directory \"${output_dir}\"\n    COMMAND\n      \"${FBTHRIFT_COMPILER}\"\n      --legacy-strict\n      --gen \"py:${GEN_ARG_STR}\"\n      \"${thrift_include_options}\"\n      -o \"${output_dir}\"\n      \"${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}\"\n    WORKING_DIRECTORY\n      \"${CMAKE_BINARY_DIR}\"\n    MAIN_DEPENDENCY\n      \"${THRIFT_FILE}\"\n    DEPENDS\n      \"${FBTHRIFT_COMPILER}\"\n  )\n\n  # We always want to pass the namespace as \"\" to this call:\n  # thrift will already emit the files with the desired namespace prefix under\n  # gen-py.  We don't want add_fb_python_library() to prepend the namespace a\n  # second time.\n  add_fb_python_library(\n    \"${LIB_NAME}\"\n    BASE_DIR \"${output_dir}/gen-py\"\n    NAMESPACE \"\"\n    SOURCES ${generated_sources}\n    DEPENDS ${ARG_DEPENDS} FBThrift::thrift_py\n  )\nendfunction()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindCares.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n# All rights reserved.\n#\n# This source code is licensed under the BSD-style license found in the\n# LICENSE file in the root directory of this source tree.\n\nfind_path(CARES_INCLUDE_DIR NAMES ares.h)\nfind_library(CARES_LIBRARIES NAMES cares)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(Cares DEFAULT_MSG CARES_LIBRARIES CARES_INCLUDE_DIR)\n\nmark_as_advanced(\n  CARES_LIBRARIES\n  CARES_INCLUDE_DIR\n)\n\nif(NOT TARGET cares)\n    if(\"${CARES_LIBRARIES}\" MATCHES \".*.a$\")\n    add_library(cares STATIC IMPORTED)\n    else()\n    add_library(cares SHARED IMPORTED)\n    endif()\n    set_target_properties(\n        cares\n        PROPERTIES\n            IMPORTED_LOCATION ${CARES_LIBRARIES}\n            INTERFACE_INCLUDE_DIRECTORIES ${CARES_INCLUDE_DIR}\n    )\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindDoubleConversion.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n\n# Finds libdouble-conversion.\n#\n# This module defines:\n# DOUBLE_CONVERSION_INCLUDE_DIR\n# DOUBLE_CONVERSION_LIBRARY\n#\n\nfind_path(DOUBLE_CONVERSION_INCLUDE_DIR double-conversion/double-conversion.h)\nfind_library(DOUBLE_CONVERSION_LIBRARY NAMES double-conversion)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  DoubleConversion\n  DEFAULT_MSG\n  DOUBLE_CONVERSION_LIBRARY DOUBLE_CONVERSION_INCLUDE_DIR)\n\nmark_as_advanced(DOUBLE_CONVERSION_INCLUDE_DIR DOUBLE_CONVERSION_LIBRARY)\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindGMock.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n# Find libgmock\n#\n#  LIBGMOCK_DEFINES     - List of defines when using libgmock.\n#  LIBGMOCK_INCLUDE_DIR - where to find gmock/gmock.h, etc.\n#  LIBGMOCK_LIBRARIES   - List of libraries when using libgmock.\n#  LIBGMOCK_FOUND       - True if libgmock found.\n\nIF (LIBGMOCK_INCLUDE_DIR)\n  # Already in cache, be silent\n  SET(LIBGMOCK_FIND_QUIETLY TRUE)\nENDIF ()\n\nfind_package(GTest CONFIG QUIET)\nif (TARGET GTest::gmock)\n  get_target_property(LIBGMOCK_DEFINES GTest::gtest INTERFACE_COMPILE_DEFINITIONS)\n  if (NOT ${LIBGMOCK_DEFINES})\n    # Explicitly set to empty string if not found to avoid it being\n    # set to NOTFOUND and breaking compilation\n    set(LIBGMOCK_DEFINES \"\")\n  endif()\n  get_target_property(LIBGMOCK_INCLUDE_DIR GTest::gtest INTERFACE_INCLUDE_DIRECTORIES)\n  set(LIBGMOCK_LIBRARIES GTest::gmock_main GTest::gmock GTest::gtest)\n  set(LIBGMOCK_FOUND ON)\n  message(STATUS \"Found gmock via config, defines=${LIBGMOCK_DEFINES}, include=${LIBGMOCK_INCLUDE_DIR}, libs=${LIBGMOCK_LIBRARIES}\")\nelse()\n\n  FIND_PATH(LIBGMOCK_INCLUDE_DIR gmock/gmock.h)\n\n  FIND_LIBRARY(LIBGMOCK_MAIN_LIBRARY_DEBUG NAMES gmock_maind)\n  FIND_LIBRARY(LIBGMOCK_MAIN_LIBRARY_RELEASE NAMES gmock_main)\n  FIND_LIBRARY(LIBGMOCK_LIBRARY_DEBUG NAMES gmockd)\n  FIND_LIBRARY(LIBGMOCK_LIBRARY_RELEASE NAMES gmock)\n  FIND_LIBRARY(LIBGTEST_LIBRARY_DEBUG NAMES gtestd)\n  FIND_LIBRARY(LIBGTEST_LIBRARY_RELEASE NAMES gtest)\n\n  find_package(Threads REQUIRED)\n  INCLUDE(SelectLibraryConfigurations)\n  SELECT_LIBRARY_CONFIGURATIONS(LIBGMOCK_MAIN)\n  SELECT_LIBRARY_CONFIGURATIONS(LIBGMOCK)\n  SELECT_LIBRARY_CONFIGURATIONS(LIBGTEST)\n\n  set(LIBGMOCK_LIBRARIES\n    ${LIBGMOCK_MAIN_LIBRARY}\n    ${LIBGMOCK_LIBRARY}\n    ${LIBGTEST_LIBRARY}\n    Threads::Threads\n  )\n\n  if(CMAKE_SYSTEM_NAME STREQUAL \"Windows\")\n    # The GTEST_LINKED_AS_SHARED_LIBRARY macro must be set properly on Windows.\n    #\n    # There isn't currently an easy way to determine if a library was compiled as\n    # a shared library on Windows, so just assume we've been built against a\n    # shared build of gmock for now.\n    SET(LIBGMOCK_DEFINES \"GTEST_LINKED_AS_SHARED_LIBRARY=1\" CACHE STRING \"\")\n  endif()\n\n  # handle the QUIETLY and REQUIRED arguments and set LIBGMOCK_FOUND to TRUE if\n  # all listed variables are TRUE\n  INCLUDE(FindPackageHandleStandardArgs)\n  FIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    GMock\n    DEFAULT_MSG\n    LIBGMOCK_MAIN_LIBRARY\n    LIBGMOCK_LIBRARY\n    LIBGTEST_LIBRARY\n    LIBGMOCK_LIBRARIES\n    LIBGMOCK_INCLUDE_DIR\n  )\n\n  MARK_AS_ADVANCED(\n    LIBGMOCK_DEFINES\n    LIBGMOCK_MAIN_LIBRARY\n    LIBGMOCK_LIBRARY\n    LIBGTEST_LIBRARY\n    LIBGMOCK_LIBRARIES\n    LIBGMOCK_INCLUDE_DIR\n  )\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindGflags.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n# Find libgflags.\n# There's a lot of compatibility cruft going on in here, both\n# to deal with changes across the FB consumers of this and also\n# to deal with variances in behavior of cmake itself.\n#\n# Since this file is named FindGflags.cmake the cmake convention\n# is for the module to export both GFLAGS_FOUND and Gflags_FOUND.\n# The convention expected by consumers is that we export the\n# following variables, even though these do not match the cmake\n# conventions:\n#\n#  LIBGFLAGS_INCLUDE_DIR - where to find gflags/gflags.h, etc.\n#  LIBGFLAGS_LIBRARY     - List of libraries when using libgflags.\n#  LIBGFLAGS_FOUND       - True if libgflags found.\n#\n# We need to be able to locate gflags both from an installed\n# cmake config file and just from the raw headers and libs, so\n# test for the former and then the latter, and then stick\n# the results together and export them into the variables\n# listed above.\n#\n# For forwards compatibility, we export the following variables:\n#\n#  gflags_INCLUDE_DIR - where to find gflags/gflags.h, etc.\n#  gflags_TARGET / GFLAGS_TARGET / gflags_LIBRARIES\n#                     - List of libraries when using libgflags.\n#  gflags_FOUND       - True if libgflags found.\n#\n\nIF (LIBGFLAGS_INCLUDE_DIR)\n  # Already in cache, be silent\n  SET(Gflags_FIND_QUIETLY TRUE)\nENDIF ()\n\nfind_package(gflags CONFIG QUIET)\nif (gflags_FOUND)\n  if (NOT Gflags_FIND_QUIETLY)\n    message(STATUS \"Found gflags from package config ${gflags_CONFIG}\")\n  endif()\n  # Re-export the config-specified libs with our local names\n  set(LIBGFLAGS_LIBRARY ${gflags_LIBRARIES})\n  set(LIBGFLAGS_INCLUDE_DIR ${gflags_INCLUDE_DIR})\n  if(NOT EXISTS \"${gflags_INCLUDE_DIR}\")\n    # The gflags-devel RPM on recent RedHat-based systems is somewhat broken.\n    # RedHat symlinks /lib64 to /usr/lib64, and this breaks some of the\n    # relative path computation performed in gflags-config.cmake.  The package\n    # config file ends up being found via /lib64, but the relative path\n    # computation it does only works if it was found in /usr/lib64.\n    # If gflags_INCLUDE_DIR does not actually exist, simply default it to\n    # /usr/include on these systems.\n    set(LIBGFLAGS_INCLUDE_DIR \"/usr/include\")\n    set(GFLAGS_INCLUDE_DIR \"/usr/include\")\n  endif()\n  set(LIBGFLAGS_FOUND ${gflags_FOUND})\n  # cmake module compat\n  set(GFLAGS_FOUND ${gflags_FOUND})\n  set(Gflags_FOUND ${gflags_FOUND})\nelse()\n  FIND_PATH(LIBGFLAGS_INCLUDE_DIR gflags/gflags.h)\n\n  FIND_LIBRARY(LIBGFLAGS_LIBRARY_DEBUG NAMES gflagsd gflags_staticd)\n  FIND_LIBRARY(LIBGFLAGS_LIBRARY_RELEASE NAMES gflags gflags_static)\n\n  INCLUDE(SelectLibraryConfigurations)\n  SELECT_LIBRARY_CONFIGURATIONS(LIBGFLAGS)\n\n  # handle the QUIETLY and REQUIRED arguments and set LIBGFLAGS_FOUND to TRUE if\n  # all listed variables are TRUE\n  INCLUDE(FindPackageHandleStandardArgs)\n  FIND_PACKAGE_HANDLE_STANDARD_ARGS(gflags DEFAULT_MSG LIBGFLAGS_LIBRARY LIBGFLAGS_INCLUDE_DIR)\n  # cmake module compat\n  set(Gflags_FOUND ${GFLAGS_FOUND})\n  # compat with some existing FindGflags consumers\n  set(LIBGFLAGS_FOUND ${GFLAGS_FOUND})\n\n  # Compat with the gflags CONFIG based detection\n  set(gflags_FOUND ${GFLAGS_FOUND})\n  set(gflags_INCLUDE_DIR ${LIBGFLAGS_INCLUDE_DIR})\n  set(gflags_LIBRARIES ${LIBGFLAGS_LIBRARY})\n  set(GFLAGS_TARGET ${LIBGFLAGS_LIBRARY})\n  set(gflags_TARGET ${LIBGFLAGS_LIBRARY})\n\n  MARK_AS_ADVANCED(LIBGFLAGS_LIBRARY LIBGFLAGS_INCLUDE_DIR)\nendif()\n\n# Compat with the gflags CONFIG based detection\nif (LIBGFLAGS_FOUND AND NOT TARGET gflags)\n  add_library(gflags UNKNOWN IMPORTED)\n  if(TARGET gflags-shared)\n    # If the installed gflags CMake package config defines a gflags-shared\n    # target but not gflags, just make the gflags target that we define\n    # depend on the gflags-shared target.\n    target_link_libraries(gflags INTERFACE gflags-shared)\n    # Export LIBGFLAGS_LIBRARY as the gflags-shared target in this case.\n    set(LIBGFLAGS_LIBRARY gflags-shared)\n  else()\n    set_target_properties(\n      gflags\n      PROPERTIES\n        IMPORTED_LINK_INTERFACE_LANGUAGES \"C\"\n        IMPORTED_LOCATION \"${LIBGFLAGS_LIBRARY}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${LIBGFLAGS_INCLUDE_DIR}\"\n    )\n  endif()\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindGlog.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n# - Try to find Glog\n# Once done, this will define\n#\n# GLOG_FOUND - system has Glog\n# GLOG_INCLUDE_DIRS - the Glog include directories\n# GLOG_LIBRARIES - link these to use Glog\n\ninclude(FindPackageHandleStandardArgs)\ninclude(SelectLibraryConfigurations)\n\nfind_library(GLOG_LIBRARY_RELEASE glog\n  PATHS ${GLOG_LIBRARYDIR})\nfind_library(GLOG_LIBRARY_DEBUG glogd\n  PATHS ${GLOG_LIBRARYDIR})\n\nfind_path(GLOG_INCLUDE_DIR glog/logging.h\n  PATHS ${GLOG_INCLUDEDIR})\n\nselect_library_configurations(GLOG)\n\nfind_package_handle_standard_args(Glog DEFAULT_MSG\n  GLOG_LIBRARY\n  GLOG_INCLUDE_DIR)\n\nmark_as_advanced(\n  GLOG_LIBRARY\n  GLOG_INCLUDE_DIR)\n\nset(GLOG_LIBRARIES ${GLOG_LIBRARY})\nset(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR})\n\nif (NOT TARGET glog::glog)\n  add_library(glog::glog UNKNOWN IMPORTED)\n  set_target_properties(glog::glog PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${GLOG_INCLUDE_DIRS}\")\n  set_target_properties(glog::glog PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES \"C\" IMPORTED_LOCATION \"${GLOG_LIBRARIES}\")\n  set_target_properties(glog::glog PROPERTIES\n    INTERFACE_COMPILE_DEFINITIONS \"GLOG_USE_GLOG_EXPORT\")\n\n  find_package(Gflags)\n  if(GFLAGS_FOUND)\n    message(STATUS \"Found gflags as a dependency of glog::glog, include=${LIBGFLAGS_INCLUDE_DIR}, libs=${LIBGFLAGS_LIBRARY}\")\n    set_property(TARGET glog::glog APPEND PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES ${LIBGFLAGS_LIBRARY})\n  endif()\n\n  find_package(LibUnwind)\n  if(LIBUNWIND_FOUND)\n    message(STATUS \"Found LibUnwind as a dependency of glog::glog, include=${LIBUNWIND_INCLUDE_DIR}, libs=${LIBUNWIND_LIBRARY}\")\n    set_property(TARGET glog::glog APPEND PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES ${LIBUNWIND_LIBRARY})\n  endif()\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindLMDB.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This software may be used and distributed according to the terms of the\n# GNU General Public License version 2.\n\nfind_library(LMDB_LIBRARIES NAMES lmdb liblmdb)\nmark_as_advanced(LMDB_LIBRARIES)\n\nfind_path(LMDB_INCLUDE_DIR NAMES  lmdb.h)\nmark_as_advanced(LMDB_INCLUDE_DIR)\n\nfind_package_handle_standard_args(\n     LMDB\n     REQUIRED_VARS LMDB_LIBRARIES LMDB_INCLUDE_DIR)\n\nif(LMDB_FOUND)\n  set(LMDB_LIBRARIES ${LMDB_LIBRARIES})\n  set(LMDB_INCLUDE_DIR, ${LMDB_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindLibEvent.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n# - Find LibEvent (a cross event library)\n# This module defines\n# LIBEVENT_INCLUDE_DIR, where to find LibEvent headers\n# LIBEVENT_LIB, LibEvent libraries\n# LibEvent_FOUND, If false, do not try to use libevent\n\nset(LibEvent_EXTRA_PREFIXES /usr/local /opt/local \"$ENV{HOME}\")\nforeach(prefix ${LibEvent_EXTRA_PREFIXES})\n  list(APPEND LibEvent_INCLUDE_PATHS \"${prefix}/include\")\n  list(APPEND LibEvent_LIB_PATHS \"${prefix}/lib\")\nendforeach()\n\nfind_package(Libevent CONFIG QUIET)\nif (TARGET event)\n  # Re-export the config under our own names\n\n  # Somewhat gross, but some vcpkg installed libevents have a relative\n  # `include` path exported into LIBEVENT_INCLUDE_DIRS, which triggers\n  # a cmake error because it resolves to the `include` dir within the\n  # folly repo, which is not something cmake allows to be in the\n  # INTERFACE_INCLUDE_DIRECTORIES.  Thankfully on such a system the\n  # actual include directory is already part of the global include\n  # directories, so we can just skip it.\n  if (NOT \"${LIBEVENT_INCLUDE_DIRS}\" STREQUAL \"include\")\n    set(LIBEVENT_INCLUDE_DIR ${LIBEVENT_INCLUDE_DIRS})\n  else()\n    set(LIBEVENT_INCLUDE_DIR)\n  endif()\n\n  # Unfortunately, with a bare target name `event`, downstream consumers\n  # of the package that depends on `Libevent` located via CONFIG end\n  # up exporting just a bare `event` in their libraries.  This is problematic\n  # because this in interpreted as just `-levent` with no library path.\n  # When libevent is not installed in the default installation prefix\n  # this results in linker errors.\n  # To resolve this, we ask cmake to lookup the full path to the library\n  # and use that instead.\n  cmake_policy(PUSH)\n  if(POLICY CMP0026)\n    # Allow reading the LOCATION property\n    cmake_policy(SET CMP0026 OLD)\n  endif()\n  get_target_property(LIBEVENT_LIB event LOCATION)\n  cmake_policy(POP)\n\n  set(LibEvent_FOUND ${Libevent_FOUND})\n  if (NOT LibEvent_FIND_QUIETLY)\n    message(STATUS \"Found libevent from package config include=${LIBEVENT_INCLUDE_DIRS} lib=${LIBEVENT_LIB}\")\n  endif()\nelse()\n  find_path(LIBEVENT_INCLUDE_DIR event.h PATHS ${LibEvent_INCLUDE_PATHS})\n  find_library(LIBEVENT_LIB NAMES event PATHS ${LibEvent_LIB_PATHS})\n\n  if (LIBEVENT_LIB AND LIBEVENT_INCLUDE_DIR)\n    set(LibEvent_FOUND TRUE)\n    set(LIBEVENT_LIB ${LIBEVENT_LIB})\n  else ()\n    set(LibEvent_FOUND FALSE)\n  endif ()\n\n  if (LibEvent_FOUND)\n    if (NOT LibEvent_FIND_QUIETLY)\n      message(STATUS \"Found libevent: ${LIBEVENT_LIB}\")\n    endif ()\n  else ()\n    if (LibEvent_FIND_REQUIRED)\n      message(FATAL_ERROR \"Could NOT find libevent.\")\n    endif ()\n    message(STATUS \"libevent NOT found.\")\n  endif ()\n\n  mark_as_advanced(\n    LIBEVENT_LIB\n    LIBEVENT_INCLUDE_DIR\n  )\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindLibUnwind.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ninclude(FindPackageHandleStandardArgs)\n\n# Prefer pkg-config: picks up transitive deps (e.g. lzma, zlib) that\n# static libunwind needs but a bare find_library would miss.\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_check_modules(PC_LIBUNWIND QUIET libunwind)\nendif()\n\nif(PC_LIBUNWIND_FOUND)\n  find_path(LIBUNWIND_INCLUDE_DIR NAMES libunwind.h\n    HINTS ${PC_LIBUNWIND_INCLUDE_DIRS}\n    PATH_SUFFIXES libunwind)\n  mark_as_advanced(LIBUNWIND_INCLUDE_DIR)\n\n  # Resolve each library from the static set (Libs + Libs.private) to a\n  # full path.  This gives the linker everything it needs for a fully-static\n  # link without leaking imported targets through cmake exports.\n  set(LIBUNWIND_LIBRARIES \"\")\n  foreach(_lib IN LISTS PC_LIBUNWIND_STATIC_LIBRARIES)\n    find_library(_libunwind_dep_${_lib} NAMES ${_lib}\n      HINTS ${PC_LIBUNWIND_STATIC_LIBRARY_DIRS})\n    if(_libunwind_dep_${_lib})\n      list(APPEND LIBUNWIND_LIBRARIES ${_libunwind_dep_${_lib}})\n    else()\n      # Fall back to bare name; the linker will resolve -l<name>.\n      list(APPEND LIBUNWIND_LIBRARIES ${_lib})\n    endif()\n    unset(_libunwind_dep_${_lib} CACHE)\n  endforeach()\n  set(LIBUNWIND_LIBRARY \"${LIBUNWIND_LIBRARIES}\")\n\n  FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUnwind\n    REQUIRED_VARS LIBUNWIND_LIBRARIES LIBUNWIND_INCLUDE_DIR)\nelse()\n  # Fallback for systems without pkg-config.\n  # When using prepackaged LLVM libunwind on Ubuntu, its includes are\n  # installed in a subdirectory.\n  find_path(LIBUNWIND_INCLUDE_DIR NAMES libunwind.h PATH_SUFFIXES libunwind)\n  mark_as_advanced(LIBUNWIND_INCLUDE_DIR)\n\n  find_library(LIBUNWIND_LIBRARY NAMES unwind)\n  mark_as_advanced(LIBUNWIND_LIBRARY)\n\n  FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUnwind\n    REQUIRED_VARS LIBUNWIND_LIBRARY LIBUNWIND_INCLUDE_DIR)\nendif()\n\nif(LibUnwind_FOUND)\n  if(NOT LIBUNWIND_LIBRARIES)\n    set(LIBUNWIND_LIBRARIES ${LIBUNWIND_LIBRARY})\n  endif()\n  set(LIBUNWIND_INCLUDE_DIRS ${LIBUNWIND_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindLibiberty.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfind_path(LIBIBERTY_INCLUDE_DIR NAMES libiberty.h PATH_SUFFIXES libiberty)\nmark_as_advanced(LIBIBERTY_INCLUDE_DIR)\n\nfind_library(LIBIBERTY_LIBRARY NAMES iberty)\nmark_as_advanced(LIBIBERTY_LIBRARY)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n  LIBIBERTY\n  REQUIRED_VARS LIBIBERTY_LIBRARY LIBIBERTY_INCLUDE_DIR)\n\nif(LIBIBERTY_FOUND)\n  set(LIBIBERTY_LIBRARIES ${LIBIBERTY_LIBRARY})\n  set(LIBIBERTY_INCLUDE_DIRS ${LIBIBERTY_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindPCRE.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\ninclude(FindPackageHandleStandardArgs)\nfind_path(PCRE_INCLUDE_DIR NAMES pcre.h)\nfind_library(PCRE_LIBRARY NAMES pcre)\nfind_package_handle_standard_args(\n  PCRE\n  DEFAULT_MSG\n  PCRE_LIBRARY\n  PCRE_INCLUDE_DIR\n)\nmark_as_advanced(PCRE_INCLUDE_DIR PCRE_LIBRARY)\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindPCRE2.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\ninclude(FindPackageHandleStandardArgs)\nfind_path(PCRE2_INCLUDE_DIR NAMES pcre2.h)\nfind_library(PCRE2_LIBRARY NAMES pcre2-8)\nfind_package_handle_standard_args(\n  PCRE2\n  DEFAULT_MSG\n  PCRE2_LIBRARY\n  PCRE2_INCLUDE_DIR\n)\nset(PCRE2_DEFINES \"PCRE2_CODE_UNIT_WIDTH=8\")\nmark_as_advanced(PCRE2_INCLUDE_DIR PCRE2_LIBRARY PCRE2_DEFINES)\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindRe2.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n#\n# This software may be used and distributed according to the terms of the\n# GNU General Public License version 2.\n\nfind_library(RE2_LIBRARY re2)\nmark_as_advanced(RE2_LIBRARY)\n\nfind_path(RE2_INCLUDE_DIR NAMES re2/re2.h)\nmark_as_advanced(RE2_INCLUDE_DIR)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n     RE2\n     REQUIRED_VARS RE2_LIBRARY RE2_INCLUDE_DIR)\n\nif(RE2_FOUND)\n  set(RE2_LIBRARY ${RE2_LIBRARY})\n  set(RE2_INCLUDE_DIR, ${RE2_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindSodium.cmake",
    "content": "# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>\n#\n# To the extent possible under law, the author(s) have dedicated all\n# copyright and related and neighboring rights to this software to the\n# public domain worldwide. This software is distributed without any warranty.\n#\n# You should have received a copy of the CC0 Public Domain Dedication\n# along with this software. If not, see\n#\n#     http://creativecommons.org/publicdomain/zero/1.0/\n#\n########################################################################\n# Tries to find the local libsodium installation.\n#\n# On Windows the sodium_DIR environment variable is used as a default\n# hint which can be overridden by setting the corresponding cmake variable.\n#\n# Once done the following variables will be defined:\n#\n#   sodium_FOUND\n#   sodium_INCLUDE_DIR\n#   sodium_LIBRARY_DEBUG\n#   sodium_LIBRARY_RELEASE\n#\n#\n# Furthermore an imported \"sodium\" target is created.\n#\n\nif (CMAKE_C_COMPILER_ID STREQUAL \"GNU\"\n    OR CMAKE_C_COMPILER_ID STREQUAL \"Clang\")\n    set(_GCC_COMPATIBLE 1)\nendif()\n\n# static library option\nif (NOT DEFINED sodium_USE_STATIC_LIBS)\n    option(sodium_USE_STATIC_LIBS \"enable to statically link against sodium\" OFF)\nendif()\nif(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))\n    unset(sodium_LIBRARY CACHE)\n    unset(sodium_LIBRARY_DEBUG CACHE)\n    unset(sodium_LIBRARY_RELEASE CACHE)\n    unset(sodium_DLL_DEBUG CACHE)\n    unset(sodium_DLL_RELEASE CACHE)\n    set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL \"internal change tracking variable\")\nendif()\n\n\n########################################################################\n# UNIX\nif (UNIX)\n    # import pkg-config\n    find_package(PkgConfig QUIET)\n    if (PKG_CONFIG_FOUND)\n        pkg_check_modules(sodium_PKG QUIET libsodium)\n    endif()\n\n    if(sodium_USE_STATIC_LIBS)\n        foreach(_libname ${sodium_PKG_STATIC_LIBRARIES})\n            if (NOT _libname MATCHES \"^lib.*\\\\.a$\") # ignore strings already ending with .a\n                list(INSERT sodium_PKG_STATIC_LIBRARIES 0 \"lib${_libname}.a\")\n            endif()\n        endforeach()\n        list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES)\n\n        # if pkgconfig for libsodium doesn't provide\n        # static lib info, then override PKG_STATIC here..\n        if (NOT sodium_PKG_STATIC_FOUND)\n            set(sodium_PKG_STATIC_LIBRARIES libsodium.a)\n        endif()\n\n        set(XPREFIX sodium_PKG_STATIC)\n    else()\n        if (NOT sodium_PKG_FOUND)\n            set(sodium_PKG_LIBRARIES sodium)\n        endif()\n\n        set(XPREFIX sodium_PKG)\n    endif()\n\n    find_path(sodium_INCLUDE_DIR sodium.h\n        HINTS ${${XPREFIX}_INCLUDE_DIRS}\n    )\n    find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES}\n        HINTS ${${XPREFIX}_LIBRARY_DIRS}\n    )\n    find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES}\n        HINTS ${${XPREFIX}_LIBRARY_DIRS}\n    )\n\n\n########################################################################\n# Windows\nelseif (WIN32)\n    set(sodium_DIR \"$ENV{sodium_DIR}\" CACHE FILEPATH \"sodium install directory\")\n    mark_as_advanced(sodium_DIR)\n\n    find_path(sodium_INCLUDE_DIR sodium.h\n        HINTS ${sodium_DIR}\n        PATH_SUFFIXES include\n    )\n\n    if (MSVC)\n        # detect target architecture\n        file(WRITE \"${CMAKE_CURRENT_BINARY_DIR}/arch.cpp\" [=[\n            #if defined _M_IX86\n            #error ARCH_VALUE x86_32\n            #elif defined _M_X64\n            #error ARCH_VALUE x86_64\n            #endif\n            #error ARCH_VALUE unknown\n        ]=])\n        try_compile(_UNUSED_VAR \"${CMAKE_CURRENT_BINARY_DIR}\" \"${CMAKE_CURRENT_BINARY_DIR}/arch.cpp\"\n            OUTPUT_VARIABLE _COMPILATION_LOG\n        )\n        string(REGEX REPLACE \".*ARCH_VALUE ([a-zA-Z0-9_]+).*\" \"\\\\1\" _TARGET_ARCH \"${_COMPILATION_LOG}\")\n\n        # construct library path\n        if (_TARGET_ARCH STREQUAL \"x86_32\")\n            string(APPEND _PLATFORM_PATH \"Win32\")\n        elseif(_TARGET_ARCH STREQUAL \"x86_64\")\n            string(APPEND _PLATFORM_PATH \"x64\")\n        else()\n            message(FATAL_ERROR \"the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.\")\n        endif()\n        string(APPEND _PLATFORM_PATH \"/$$CONFIG$$\")\n\n        if (MSVC_VERSION LESS 1900)\n            math(EXPR _VS_VERSION \"${MSVC_VERSION} / 10 - 60\")\n        else()\n            math(EXPR _VS_VERSION \"${MSVC_VERSION} / 10 - 50\")\n        endif()\n        string(APPEND _PLATFORM_PATH \"/v${_VS_VERSION}\")\n\n        if (sodium_USE_STATIC_LIBS)\n            string(APPEND _PLATFORM_PATH \"/static\")\n        else()\n            string(APPEND _PLATFORM_PATH \"/dynamic\")\n        endif()\n\n        string(REPLACE \"$$CONFIG$$\" \"Debug\" _DEBUG_PATH_SUFFIX \"${_PLATFORM_PATH}\")\n        string(REPLACE \"$$CONFIG$$\" \"Release\" _RELEASE_PATH_SUFFIX \"${_PLATFORM_PATH}\")\n\n        find_library(sodium_LIBRARY_DEBUG libsodium.lib\n            HINTS ${sodium_DIR}\n            PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}\n        )\n        find_library(sodium_LIBRARY_RELEASE libsodium.lib\n            HINTS ${sodium_DIR}\n            PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}\n        )\n        if (NOT sodium_USE_STATIC_LIBS)\n            set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})\n            set(CMAKE_FIND_LIBRARY_SUFFIXES \".dll\")\n            find_library(sodium_DLL_DEBUG libsodium\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}\n            )\n            find_library(sodium_DLL_RELEASE libsodium\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}\n            )\n            set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})\n        endif()\n\n    elseif(_GCC_COMPATIBLE)\n        if (sodium_USE_STATIC_LIBS)\n            find_library(sodium_LIBRARY_DEBUG libsodium.a\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES lib\n            )\n            find_library(sodium_LIBRARY_RELEASE libsodium.a\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES lib\n            )\n        else()\n            find_library(sodium_LIBRARY_DEBUG libsodium.dll.a\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES lib\n            )\n            find_library(sodium_LIBRARY_RELEASE libsodium.dll.a\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES lib\n            )\n\n            file(GLOB _DLL\n                LIST_DIRECTORIES false\n                RELATIVE \"${sodium_DIR}/bin\"\n                \"${sodium_DIR}/bin/libsodium*.dll\"\n            )\n            find_library(sodium_DLL_DEBUG ${_DLL} libsodium\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES bin\n            )\n            find_library(sodium_DLL_RELEASE ${_DLL} libsodium\n                HINTS ${sodium_DIR}\n                PATH_SUFFIXES bin\n            )\n        endif()\n    else()\n        message(FATAL_ERROR \"this platform is not supported by FindSodium.cmake\")\n    endif()\n\n\n########################################################################\n# unsupported\nelse()\n    message(FATAL_ERROR \"this platform is not supported by FindSodium.cmake\")\nendif()\n\n\n########################################################################\n# common stuff\n\n# extract sodium version\nif (sodium_INCLUDE_DIR)\n    set(_VERSION_HEADER \"${_INCLUDE_DIR}/sodium/version.h\")\n    if (EXISTS _VERSION_HEADER)\n        file(READ \"${_VERSION_HEADER}\" _VERSION_HEADER_CONTENT)\n        string(REGEX REPLACE \".*#[ \\t]*define[ \\t]*SODIUM_VERSION_STRING[ \\t]*\\\"([^\\n]*)\\\".*\" \"\\\\1\"\n            sodium_VERSION \"${_VERSION_HEADER_CONTENT}\")\n        set(sodium_VERSION \"${sodium_VERSION}\" PARENT_SCOPE)\n    endif()\nendif()\n\n# communicate results\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n    Sodium # The name must be either uppercase or match the filename case.\n    REQUIRED_VARS\n        sodium_LIBRARY_RELEASE\n        sodium_LIBRARY_DEBUG\n        sodium_INCLUDE_DIR\n    VERSION_VAR\n        sodium_VERSION\n)\n\nif(Sodium_FOUND)\n    set(sodium_LIBRARIES\n        optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG})\nendif()\n\n# mark file paths as advanced\nmark_as_advanced(sodium_INCLUDE_DIR)\nmark_as_advanced(sodium_LIBRARY_DEBUG)\nmark_as_advanced(sodium_LIBRARY_RELEASE)\nif (WIN32)\n    mark_as_advanced(sodium_DLL_DEBUG)\n    mark_as_advanced(sodium_DLL_RELEASE)\nendif()\n\n# create imported target\nif(sodium_USE_STATIC_LIBS)\n    set(_LIB_TYPE STATIC)\nelse()\n    set(_LIB_TYPE SHARED)\nendif()\n\nif(NOT TARGET sodium)\n    add_library(sodium ${_LIB_TYPE} IMPORTED)\nendif()\n\nset_target_properties(sodium PROPERTIES\n    INTERFACE_INCLUDE_DIRECTORIES \"${sodium_INCLUDE_DIR}\"\n    IMPORTED_LINK_INTERFACE_LANGUAGES \"C\"\n)\n\nif (sodium_USE_STATIC_LIBS)\n    set_target_properties(sodium PROPERTIES\n        INTERFACE_COMPILE_DEFINITIONS \"SODIUM_STATIC\"\n        IMPORTED_LOCATION \"${sodium_LIBRARY_RELEASE}\"\n        IMPORTED_LOCATION_DEBUG \"${sodium_LIBRARY_DEBUG}\"\n    )\nelse()\n    if (UNIX)\n        set_target_properties(sodium PROPERTIES\n            IMPORTED_LOCATION \"${sodium_LIBRARY_RELEASE}\"\n            IMPORTED_LOCATION_DEBUG \"${sodium_LIBRARY_DEBUG}\"\n        )\n    elseif (WIN32)\n        set_target_properties(sodium PROPERTIES\n            IMPORTED_IMPLIB \"${sodium_LIBRARY_RELEASE}\"\n            IMPORTED_IMPLIB_DEBUG \"${sodium_LIBRARY_DEBUG}\"\n        )\n        if (NOT (sodium_DLL_DEBUG MATCHES \".*-NOTFOUND\"))\n            set_target_properties(sodium PROPERTIES\n                IMPORTED_LOCATION_DEBUG \"${sodium_DLL_DEBUG}\"\n            )\n        endif()\n        if (NOT (sodium_DLL_RELEASE MATCHES \".*-NOTFOUND\"))\n            set_target_properties(sodium PROPERTIES\n                IMPORTED_LOCATION_RELWITHDEBINFO \"${sodium_DLL_RELEASE}\"\n                IMPORTED_LOCATION_MINSIZEREL \"${sodium_DLL_RELEASE}\"\n                IMPORTED_LOCATION_RELEASE \"${sodium_DLL_RELEASE}\"\n            )\n        endif()\n    endif()\nendif()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindXxhash.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#\n# - Try to find Facebook xxhash library\n# This will define\n# Xxhash_FOUND\n# Xxhash_INCLUDE_DIR\n# Xxhash_LIBRARY\n#\n\nfind_path(Xxhash_INCLUDE_DIR NAMES xxhash.h)\n\nfind_library(Xxhash_LIBRARY_RELEASE NAMES xxhash)\n\ninclude(SelectLibraryConfigurations)\nSELECT_LIBRARY_CONFIGURATIONS(Xxhash)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    Xxhash DEFAULT_MSG\n    Xxhash_LIBRARY Xxhash_INCLUDE_DIR\n)\n\nif (Xxhash_FOUND)\n  message(STATUS \"Found xxhash: ${Xxhash_LIBRARY}\")\nendif()\n\nmark_as_advanced(Xxhash_INCLUDE_DIR Xxhash_LIBRARY)\n"
  },
  {
    "path": "build/fbcode_builder/CMake/FindZstd.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n#\n# - Try to find Facebook zstd library\n# This will define\n# ZSTD_FOUND\n# ZSTD_INCLUDE_DIR\n# ZSTD_LIBRARY\n#\n\nfind_path(ZSTD_INCLUDE_DIR NAMES zstd.h)\n\nfind_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)\nfind_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)\n\ninclude(SelectLibraryConfigurations)\nSELECT_LIBRARY_CONFIGURATIONS(ZSTD)\n\ninclude(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    Zstd DEFAULT_MSG\n    ZSTD_LIBRARY ZSTD_INCLUDE_DIR\n)\n\nif (ZSTD_FOUND)\n    message(STATUS \"Found Zstd: ${ZSTD_LIBRARY}\")\nendif()\n\nmark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)\n"
  },
  {
    "path": "build/fbcode_builder/CMake/Findibverbs.cmake",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Find the ibverbs libraries\n#\n# The following variables are optionally searched for defaults\n#  IBVERBS_ROOT_DIR: Base directory where all ibverbs components are found\n#  IBVERBS_INCLUDE_DIR: Directory where ibverbs headers are found\n#  IBVERBS_LIB_DIR: Directory where ibverbs libraries are found\n\n# The following are set after configuration is done:\n#  IBVERBS_FOUND\n#  IBVERBS_INCLUDE_DIRS\n#  IBVERBS_LIBRARIES\n#  IBVERBS_VERSION\n\nfind_path(IBVERBS_INCLUDE_DIRS\n  NAMES infiniband/verbs.h\n  HINTS\n  ${IBVERBS_INCLUDE_DIR}\n  ${IBVERBS_ROOT_DIR}\n  ${IBVERBS_ROOT_DIR}/include)\n\nfind_library(IBVERBS_LIBRARIES\n  NAMES ibverbs\n  HINTS\n  ${IBVERBS_LIB_DIR}\n  ${IBVERBS_ROOT_DIR}\n  ${IBVERBS_ROOT_DIR}/lib)\n\n# Try to determine the rdma-core version\nif(IBVERBS_INCLUDE_DIRS AND IBVERBS_LIBRARIES)\n  # First try using pkg-config if available\n  find_package(PkgConfig QUIET)\n  if(PKG_CONFIG_FOUND)\n    pkg_check_modules(PC_RDMA_CORE QUIET rdma-core)\n    if(PC_RDMA_CORE_VERSION)\n      set(IBVERBS_VERSION ${PC_RDMA_CORE_VERSION})\n    endif()\n  endif()\n\n  # If pkg-config didn't work, try to extract version from library filename\n  # According to rdma-core Documentation/versioning.md:\n  # Library filename format:\n  #   libibverbs.so.SONAME.ABI.PACKAGE_VERSION_MAIN[.PACKAGE_VERSION_BRANCH]\n  # Where:\n  #   - SONAME: Major version (1st field)\n  #   - ABI: ABI version number (2nd field)\n  #   - PACKAGE_VERSION_MAIN: Main package version (3rd field)\n  #   - PACKAGE_VERSION_BRANCH: Optional counter for branched stable\n  #     releases (4th field, part of PACKAGE_VERSION)\n  # Example: libibverbs.so.1.14.57.0 → SONAME=1, ABI=14,\n  #   PACKAGE_VERSION=57.0\n  if(NOT IBVERBS_VERSION)\n    # Get the real path of the library (follows symlinks)\n    get_filename_component(IBVERBS_REAL_PATH \"${IBVERBS_LIBRARIES}\" REALPATH)\n    get_filename_component(IBVERBS_LIB_NAME \"${IBVERBS_REAL_PATH}\" NAME)\n\n    # Extract version from filename\n    if(IBVERBS_LIB_NAME MATCHES\n       \"libibverbs\\\\.so\\\\.([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)\")\n      # Four-component version: PACKAGE_VERSION_MAIN.PACKAGE_VERSION_BRANCH\n      set(IBVERBS_VERSION_MAJOR ${CMAKE_MATCH_3})\n      set(IBVERBS_VERSION_MINOR ${CMAKE_MATCH_4})\n      set(IBVERBS_VERSION \"${IBVERBS_VERSION_MAJOR}.${IBVERBS_VERSION_MINOR}\")\n    elseif(IBVERBS_LIB_NAME MATCHES\n           \"libibverbs\\\\.so\\\\.([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)\")\n      # Three-component version: PACKAGE_VERSION_MAIN only\n      set(IBVERBS_VERSION_MAJOR ${CMAKE_MATCH_3})\n      set(IBVERBS_VERSION \"${IBVERBS_VERSION_MAJOR}.0\")\n    else()\n      # If we can't parse the filename, set to empty string\n      # Feature detection will be done in CMakeLists.txt\n      set(IBVERBS_VERSION \"\")\n    endif()\n  endif()\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(ibverbs\n  REQUIRED_VARS IBVERBS_INCLUDE_DIRS IBVERBS_LIBRARIES\n  VERSION_VAR IBVERBS_VERSION)\nmark_as_advanced(IBVERBS_INCLUDE_DIRS IBVERBS_LIBRARIES IBVERBS_VERSION)\n"
  },
  {
    "path": "build/fbcode_builder/CMake/RustStaticLibrary.cmake",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n\ninclude(FBCMakeParseArgs)\n\nset(\n  USE_CARGO_VENDOR AUTO CACHE STRING\n  \"Download Rust Crates from an internally vendored location\"\n)\nset_property(CACHE USE_CARGO_VENDOR PROPERTY STRINGS AUTO ON OFF)\n\nset(\n  GENERATE_CARGO_VENDOR_CONFIG AUTO CACHE STRING\n  \"Whether to generate Rust cargo vendor config or use existing\"\n)\nset_property(CACHE GENERATE_CARGO_VENDOR_CONFIG PROPERTY STRINGS AUTO ON OFF)\n\nset(RUST_VENDORED_CRATES_DIR \"$ENV{RUST_VENDORED_CRATES_DIR}\")\n\nif(\"${USE_CARGO_VENDOR}\" STREQUAL \"AUTO\")\n  if(EXISTS \"${RUST_VENDORED_CRATES_DIR}\")\n    set(USE_CARGO_VENDOR ON)\n  else()\n    set(USE_CARGO_VENDOR OFF)\n  endif()\nendif()\n\nif(\"${GENERATE_CARGO_VENDOR_CONFIG}\" STREQUAL \"AUTO\")\n  set(GENERATE_CARGO_VENDOR_CONFIG \"${USE_CARGO_VENDOR}\")\nendif()\n\nif(GENERATE_CARGO_VENDOR_CONFIG)\n  if(NOT EXISTS \"${RUST_VENDORED_CRATES_DIR}\")\n    message(\n      FATAL \"vendored rust crates not present: \"\n      \"${RUST_VENDORED_CRATES_DIR}\"\n    )\n  endif()\n\n  set(RUST_CARGO_HOME \"${CMAKE_BINARY_DIR}/_cargo_home\")\n  file(MAKE_DIRECTORY \"${RUST_CARGO_HOME}\")\n\n  file(\n    TO_NATIVE_PATH \"${RUST_VENDORED_CRATES_DIR}\"\n    ESCAPED_RUST_VENDORED_CRATES_DIR\n  )\n  string(\n    REPLACE \"\\\\\" \"\\\\\\\\\"\n    ESCAPED_RUST_VENDORED_CRATES_DIR\n    \"${ESCAPED_RUST_VENDORED_CRATES_DIR}\"\n  )\n  file(\n    WRITE \"${RUST_CARGO_HOME}/config\"\n    \"[source.crates-io]\\n\"\n    \"replace-with = \\\"vendored-sources\\\"\\n\"\n    \"\\n\"\n    \"[source.vendored-sources]\\n\"\n    \"directory = \\\"${ESCAPED_RUST_VENDORED_CRATES_DIR}\\\"\\n\"\n  )\nendif()\n\nfind_program(CARGO_COMMAND cargo REQUIRED)\n\n# Cargo is a build system in itself, and thus will try to take advantage of all\n# the cores on the system. Unfortunately, this conflicts with Ninja, since it\n# also tries to utilize all the cores. This can lead to a system that is\n# completely overloaded with compile jobs to the point where nothing else can\n# be achieved on the system.\n#\n# Let's inform Ninja of this fact so it won't try to spawn other jobs while\n# Rust being compiled.\nset_property(GLOBAL APPEND PROPERTY JOB_POOLS rust_job_pool=1)\n\n# This function creates an interface library target based on the static library\n# built by Cargo. It will call Cargo to build a staticlib and generate a CMake\n# interface library with it.\n#\n# This function requires `find_package(Python COMPONENTS Interpreter)`.\n#\n# You need to set `lib:crate-type = [\"staticlib\"]` in your Cargo.toml to make\n# Cargo build static library.\n#\n# ```cmake\n# rust_static_library(<TARGET> [CRATE <CRATE_NAME>] [FEATURES <FEATURE_NAME>] [USE_CXX_INCLUDE])\n# ```\n#\n# Parameters:\n# - TARGET:\n#   Name of the target name. This function will create an interface library\n#   target with this name.\n# - CRATE_NAME:\n#   Name of the crate. This parameter is optional. If unspecified, it will\n#   fallback to `${TARGET}`.\n# - FEATURE_NAME:\n#   Name of the Rust feature to enable.\n# - USE_CXX_INCLUDE:\n#   Include cxx.rs include path in `${TARGET}` INTERFACE.\n#\n# This function creates two targets:\n# - \"${TARGET}\": an interface library target contains the static library built\n#   from Cargo.\n# - \"${TARGET}.cargo\": an internal custom target that invokes Cargo.\n#\n# If you are going to use this static library from C/C++, you will need to\n# write header files for the library (or generate with cbindgen) and bind these\n# headers with the interface library.\n#\nfunction(rust_static_library TARGET)\n  fb_cmake_parse_args(ARG \"USE_CXX_INCLUDE\" \"CRATE;FEATURES\" \"\" \"${ARGN}\")\n\n  if(DEFINED ARG_CRATE)\n    set(crate_name \"${ARG_CRATE}\")\n  else()\n    set(crate_name \"${TARGET}\")\n  endif()\n  if(DEFINED ARG_FEATURES)\n    set(features --features ${ARG_FEATURES})\n  else()\n    set(features )\n  endif()\n\n  set(cargo_target \"${TARGET}.cargo\")\n  set(target_dir $<IF:$<CONFIG:Debug>,debug,release>)\n  set(staticlib_name \"${CMAKE_STATIC_LIBRARY_PREFIX}${crate_name}${CMAKE_STATIC_LIBRARY_SUFFIX}\")\n  set(rust_staticlib \"${CMAKE_CURRENT_BINARY_DIR}/${target_dir}/${staticlib_name}\")\n\n  if(DEFINED ARG_FEATURES)\n    set(cargo_flags build $<IF:$<CONFIG:Debug>,,--release> -p ${crate_name} --features ${ARG_FEATURES} --config fbcode_build=false)\n  else()\n    set(cargo_flags build $<IF:$<CONFIG:Debug>,,--release> -p ${crate_name} --config fbcode_build=false)\n  endif()\n  if(USE_CARGO_VENDOR)\n    set(extra_cargo_env \"CARGO_HOME=${RUST_CARGO_HOME}\")\n    set(cargo_flags ${cargo_flags})\n  endif()\n\n  add_custom_target(\n    ${cargo_target}\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E remove -f \"${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock\"\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E env\n      \"CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}\"\n      ${extra_cargo_env}\n      ${CARGO_COMMAND}\n      ${cargo_flags}\n    COMMENT \"Building Rust crate '${crate_name}'...\"\n    JOB_POOL rust_job_pool\n    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n    BYPRODUCTS\n      \"${CMAKE_CURRENT_BINARY_DIR}/debug/${staticlib_name}\"\n      \"${CMAKE_CURRENT_BINARY_DIR}/release/${staticlib_name}\"\n  )\n\n  add_library(${TARGET} INTERFACE)\n  add_dependencies(${TARGET} ${cargo_target})\n  set_target_properties(\n    ${TARGET}\n    PROPERTIES\n      INTERFACE_STATICLIB_OUTPUT_PATH \"${rust_staticlib}\"\n      INTERFACE_INSTALL_LIBNAME\n        \"${CMAKE_STATIC_LIBRARY_PREFIX}${crate_name}_rs${CMAKE_STATIC_LIBRARY_SUFFIX}\"\n  )\n\n  if(DEFINED ARG_USE_CXX_INCLUDE)\n    target_include_directories(\n      ${TARGET}\n      INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/\n    )\n  endif()\n\n  target_link_libraries(\n    ${TARGET}\n    INTERFACE \"$<BUILD_INTERFACE:${rust_staticlib}>\"\n  )\nendfunction()\n\n# This function instructs CMake to define a target that will use `cargo build`\n# to build a bin crate referenced by the Cargo.toml file in the current source\n# directory.\n# It accepts a single `TARGET` parameter which will be passed as the package\n# name to `cargo build -p TARGET`. If binary has different name as package,\n# use optional flag BINARY_NAME to override it.\n# It also accepts a `FEATURES` parameter if you want to enable certain features\n# in your Rust binary.\n# The CMake target will be registered to build by default as part of the\n# ALL target.\nfunction(rust_executable TARGET)\n  fb_cmake_parse_args(ARG \"\" \"BINARY_NAME;FEATURES\" \"\" \"${ARGN}\")\n\n  set(crate_name \"${TARGET}\")\n  set(cargo_target \"${TARGET}.cargo\")\n  set(target_dir $<IF:$<CONFIG:Debug>,debug,release>)\n\n  if(DEFINED ARG_BINARY_NAME)\n    set(executable_name \"${ARG_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}\")\n  else()\n    set(executable_name \"${crate_name}${CMAKE_EXECUTABLE_SUFFIX}\")\n  endif()\n  if(DEFINED ARG_FEATURES)\n    set(features --features ${ARG_FEATURES})\n  else()\n    set(features )\n  endif()\n\n  if(DEFINED ARG_FEATURES)\n    set(cargo_flags build $<IF:$<CONFIG:Debug>,,--release> -p ${crate_name} --features ${ARG_FEATURES})\n  else()\n    set(cargo_flags build $<IF:$<CONFIG:Debug>,,--release> -p ${crate_name})\n  endif()\n  if(USE_CARGO_VENDOR)\n    set(extra_cargo_env \"CARGO_HOME=${RUST_CARGO_HOME}\")\n    set(cargo_flags ${cargo_flags})\n  endif()\n\n  add_custom_target(\n    ${cargo_target}\n    ALL\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E remove -f \"${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock\"\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E env\n      \"CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}\"\n      ${extra_cargo_env}\n      ${CARGO_COMMAND}\n      ${cargo_flags}\n    COMMENT \"Building Rust executable '${crate_name}'...\"\n    JOB_POOL rust_job_pool\n    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n    BYPRODUCTS\n      \"${CMAKE_CURRENT_BINARY_DIR}/debug/${executable_name}\"\n      \"${CMAKE_CURRENT_BINARY_DIR}/release/${executable_name}\"\n  )\n\n  set_property(TARGET \"${cargo_target}\"\n      PROPERTY EXECUTABLE \"${CMAKE_CURRENT_BINARY_DIR}/${target_dir}/${executable_name}\")\nendfunction()\n\n# This function can be used to install the executable generated by a prior\n# call to the `rust_executable` function.\n# It requires a `TARGET` parameter to identify the target to be installed,\n# and an optional `DESTINATION` parameter to specify the installation\n# directory.  If DESTINATION is not specified then the `bin` directory\n# will be assumed.\nfunction(install_rust_executable TARGET)\n  # Parse the arguments\n  set(one_value_args DESTINATION)\n  set(multi_value_args)\n  fb_cmake_parse_args(\n    ARG \"\" \"${one_value_args}\" \"${multi_value_args}\" \"${ARGN}\"\n  )\n\n  if(NOT DEFINED ARG_DESTINATION)\n    set(ARG_DESTINATION bin)\n  endif()\n\n  get_target_property(foo \"${TARGET}.cargo\" EXECUTABLE)\n\n  install(\n    PROGRAMS \"${foo}\"\n    DESTINATION \"${ARG_DESTINATION}\"\n  )\nendfunction()\n\n# This function installs the interface target generated from the function\n# `rust_static_library`. Use this function if you want to export your Rust\n# target to external CMake targets.\n#\n# ```cmake\n# install_rust_static_library(\n#   <TARGET>\n#   INSTALL_DIR <INSTALL_DIR>\n#   [EXPORT <EXPORT_NAME>]\n# )\n# ```\n#\n# Parameters:\n# - TARGET: Name of the Rust static library target.\n# - EXPORT_NAME: Name of the exported target.\n# - INSTALL_DIR: Path to the directory where this library will be installed.\n#\nfunction(install_rust_static_library TARGET)\n  fb_cmake_parse_args(ARG \"\" \"EXPORT;INSTALL_DIR\" \"\" \"${ARGN}\")\n\n  get_property(\n    staticlib_output_path\n    TARGET \"${TARGET}\"\n    PROPERTY INTERFACE_STATICLIB_OUTPUT_PATH\n  )\n  get_property(\n    staticlib_output_name\n    TARGET \"${TARGET}\"\n    PROPERTY INTERFACE_INSTALL_LIBNAME\n  )\n\n  if(NOT DEFINED staticlib_output_path)\n    message(FATAL_ERROR \"Not a rust_static_library target.\")\n  endif()\n\n  if(NOT DEFINED ARG_INSTALL_DIR)\n    message(FATAL_ERROR \"Missing required argument.\")\n  endif()\n\n  if(DEFINED ARG_EXPORT)\n    set(install_export_args EXPORT \"${ARG_EXPORT}\")\n  endif()\n\n  set(install_interface_dir \"${ARG_INSTALL_DIR}\")\n  if(NOT IS_ABSOLUTE \"${install_interface_dir}\")\n    set(install_interface_dir \"\\${_IMPORT_PREFIX}/${install_interface_dir}\")\n  endif()\n\n  target_link_libraries(\n    ${TARGET} INTERFACE\n    \"$<INSTALL_INTERFACE:${install_interface_dir}/${staticlib_output_name}>\"\n  )\n  install(\n    TARGETS ${TARGET}\n    ${install_export_args}\n    LIBRARY DESTINATION ${ARG_INSTALL_DIR}\n  )\n  install(\n    FILES ${staticlib_output_path}\n    RENAME ${staticlib_output_name}\n    DESTINATION ${ARG_INSTALL_DIR}\n  )\nendfunction()\n\n# This function creates C++ bindings using the [cxx] crate.\n#\n# Original function found here: https://github.com/corrosion-rs/corrosion/blob/master/cmake/Corrosion.cmake#L1390\n# Simplified for use as part of RustStaticLibrary module. License below.\n#\n# MIT License\n#\n# Copyright (c) 2018 Andrew Gaspar\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in all\n# copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n#\n# The rules approximately do the following:\n# - Check which version of `cxx` the Rust crate depends on.\n# - Check if the exact same version of `cxxbridge-cmd` is installed\n# - If not, create a rule to build the exact same version of `cxxbridge-cmd`.\n# - Create rules to run `cxxbridge` and generate\n#   - The `rust/cxx.h` header\n#   - A header and source file for the specified CXX_BRIDGE_FILE.\n# - The generated sources (and header include directories) are added to the\n#   `${TARGET}` CMake library target.\n#\n# ```cmake\n# rust_cxx_bridge(<TARGET> <CXX_BRIDGE_FILE> [CRATE <CRATE_NAME>] [LIBS <LIBNAMES>])\n# ```\n#\n# Parameters:\n# - TARGET:\n#   Name of the target name. The target that the bridge will be included with.\n# - CXX_BRIDGE_FILE:\n#   Name of the file that include the cxxbridge (e.g., \"src/ffi.rs\").\n# - CRATE_NAME:\n#   Name of the crate. This parameter is optional. If unspecified, it will\n#   fallback to `${TARGET}`.\n# - LIBS <lib1> [<lib2> ...]:\n#   A list of libraries that this library depends on.\n#\nfunction(rust_cxx_bridge TARGET CXX_BRIDGE_FILE)\n  fb_cmake_parse_args(ARG \"\" \"CRATE\" \"LIBS\" \"${ARGN}\")\n\n  if(DEFINED ARG_CRATE)\n    set(crate_name \"${ARG_CRATE}\")\n  else()\n    set(crate_name \"${TARGET}\")\n  endif()\n\n  if(USE_CARGO_VENDOR)\n    set(extra_cargo_env \"CARGO_HOME=${RUST_CARGO_HOME}\")\n  endif()\n\n  execute_process(\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E env\n      ${extra_cargo_env}\n      \"${CARGO_COMMAND}\" tree -i cxx --depth=0\n    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n    RESULT_VARIABLE cxx_version_result\n    OUTPUT_VARIABLE cxx_version_output\n  )\n\n  if(NOT \"${cxx_version_result}\" EQUAL \"0\")\n    message(FATAL_ERROR \"Crate ${crate_name} does not depend on cxx.\")\n  endif()\n  if(cxx_version_output MATCHES \"cxx v([0-9]+.[0-9]+.[0-9]+)\")\n    set(cxx_required_version \"${CMAKE_MATCH_1}\")\n  else()\n    message(\n      FATAL_ERROR\n      \"Failed to parse cxx version from cargo tree output: `cxx_version_output`\")\n  endif()\n\n  # First check if a suitable version of cxxbridge is installed\n  find_program(INSTALLED_CXXBRIDGE cxxbridge PATHS \"$ENV{HOME}/.cargo/bin/\")\n  mark_as_advanced(INSTALLED_CXXBRIDGE)\n  if(INSTALLED_CXXBRIDGE)\n    execute_process(\n      COMMAND \"${INSTALLED_CXXBRIDGE}\" --version\n      OUTPUT_VARIABLE cxxbridge_version_output\n    )\n    if(cxxbridge_version_output MATCHES \"cxxbridge ([0-9]+.[0-9]+.[0-9]+)\")\n      set(cxxbridge_version \"${CMAKE_MATCH_1}\")\n    else()\n      set(cxxbridge_version \"\")\n    endif()\n  endif()\n\n  set(cxxbridge \"\")\n  if(cxxbridge_version)\n    if(cxxbridge_version VERSION_EQUAL cxx_required_version)\n      set(cxxbridge \"${INSTALLED_CXXBRIDGE}\")\n      if(NOT TARGET \"cxxbridge_v${cxx_required_version}\")\n        # Add an empty target.\n        add_custom_target(\"cxxbridge_v${cxx_required_version}\")\n      endif()\n    endif()\n  endif()\n\n  # No suitable version of cxxbridge was installed,\n  # so use custom target to install correct version.\n  if(NOT cxxbridge)\n    if(NOT TARGET \"cxxbridge_v${cxx_required_version}\")\n      add_custom_command(\n        OUTPUT\n          \"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge\"\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E make_directory\n          \"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}\"\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E remove -f \"${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock\"\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E env\n          ${extra_cargo_env}\n          \"${CARGO_COMMAND}\" install cxxbridge-cmd\n          --version \"${cxx_required_version}\"\n          --root \"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}\"\n          --quiet\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E remove -f \"${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock\"\n        COMMENT \"Installing cxxbridge (version ${cxx_required_version})\"\n      )\n      add_custom_target(\n        \"cxxbridge_v${cxx_required_version}\"\n        DEPENDS \"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge\"\n      )\n    endif()\n    set(\n      cxxbridge\n      \"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge\"\n    )\n  endif()\n\n  add_library(${crate_name} STATIC)\n  target_include_directories(\n    ${crate_name}\n    PUBLIC\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>\n    $<INSTALL_INTERFACE:include>\n  )\n  target_link_libraries(\n    ${crate_name}\n    PUBLIC\n    ${ARG_LIBS}\n  )\n\n  file(MAKE_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/rust\")\n  add_custom_command(\n    OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h\"\n    COMMAND\n      \"${cxxbridge}\" --header --output \"${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h\"\n    DEPENDS \"cxxbridge_v${cxx_required_version}\"\n    COMMENT \"Generating rust/cxx.h header\"\n  )\n\n  get_filename_component(filename_component ${CXX_BRIDGE_FILE} NAME)\n  get_filename_component(directory_component ${CXX_BRIDGE_FILE} DIRECTORY)\n  set(directory \"\")\n  if(directory_component)\n    set(directory \"${directory_component}\")\n  endif()\n\n  set(cxx_header ${directory}/${filename_component}.h)\n  set(cxx_source ${directory}/${filename_component}.cc)\n  set(rust_source_path \"${CMAKE_CURRENT_SOURCE_DIR}/${CXX_BRIDGE_FILE}\")\n\n  file(\n    MAKE_DIRECTORY\n    \"${CMAKE_CURRENT_BINARY_DIR}/${directory_component}\"\n  )\n\n  add_custom_command(\n    OUTPUT\n      \"${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}\"\n      \"${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}\"\n    COMMAND\n      ${cxxbridge} ${rust_source_path}\n        --cfg fbcode_build=false\n        --header\n        --output \"${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}\"\n    COMMAND\n      ${cxxbridge} ${rust_source_path}\n        --cfg fbcode_build=false\n        --output \"${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}\"\n        --include \"${cxx_header}\"\n    DEPENDS \"cxxbridge_v${cxx_required_version}\" \"${rust_source_path}\"\n    COMMENT \"Generating cxx bindings for crate ${crate_name}\"\n  )\n\n  target_sources(\n    ${crate_name}\n    PRIVATE\n    \"${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}\"\n    \"${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h\"\n    \"${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}\"\n  )\nendfunction()\n"
  },
  {
    "path": "build/fbcode_builder/CMake/fb_py_test_main.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright (c) Facebook, Inc. and its affiliates.\n#\n\"\"\"\nThis file contains the main module code for Python test programs.\n\"\"\"\n\n\nimport contextlib\nimport ctypes\nimport fnmatch\nimport json\nimport logging\nimport optparse\nimport os\nimport platform\nimport re\nimport sys\nimport tempfile\nimport time\nimport traceback\nimport unittest\nimport warnings\nfrom importlib.machinery import PathFinder\n\n\ntry:\n    from StringIO import StringIO\nexcept ImportError:\n    from io import StringIO\ntry:\n    import coverage\nexcept ImportError:\n    coverage = None  # type: ignore\ntry:\n    from importlib.machinery import SourceFileLoader\nexcept ImportError:\n    SourceFileLoader = None  # type: ignore\n\n\nclass get_cpu_instr_counter:\n    def read(self):\n        # TODO\n        return 0\n\n\nEXIT_CODE_SUCCESS = 0\nEXIT_CODE_TEST_FAILURE = 70\n\n\nclass TestStatus:\n\n    ABORTED = \"FAILURE\"\n    PASSED = \"SUCCESS\"\n    FAILED = \"FAILURE\"\n    EXPECTED_FAILURE = \"SUCCESS\"\n    UNEXPECTED_SUCCESS = \"FAILURE\"\n    SKIPPED = \"ASSUMPTION_VIOLATION\"\n\n\nclass PathMatcher:\n    def __init__(self, include_patterns, omit_patterns):\n        self.include_patterns = include_patterns\n        self.omit_patterns = omit_patterns\n\n    def omit(self, path):\n        \"\"\"\n        Omit iff matches any of the omit_patterns or the include patterns are\n        not empty and none is matched\n        \"\"\"\n        path = os.path.realpath(path)\n        return any(fnmatch.fnmatch(path, p) for p in self.omit_patterns) or (\n            self.include_patterns\n            and not any(fnmatch.fnmatch(path, p) for p in self.include_patterns)\n        )\n\n    def include(self, path):\n        return not self.omit(path)\n\n\nclass DebugWipeFinder(PathFinder):\n    \"\"\"\n    PEP 302 finder that uses a DebugWipeLoader for all files which do not need\n    coverage\n    \"\"\"\n\n    def __init__(self, matcher):\n        self.matcher = matcher\n\n    def find_spec(self, fullname, path=None, target=None):\n        spec = super().find_spec(fullname, path=path, target=target)\n        if spec is None or spec.origin is None:\n            return None\n        if not spec.origin.endswith(\".py\"):\n            return None\n        if self.matcher.include(spec.origin):\n            return None\n\n        class PyVarObject(ctypes.Structure):\n            _fields_ = [\n                (\"ob_refcnt\", ctypes.c_long),\n                (\"ob_type\", ctypes.c_void_p),\n                (\"ob_size\", ctypes.c_ulong),\n            ]\n\n        class DebugWipeLoader(SourceFileLoader):\n            \"\"\"\n            PEP302 loader that zeros out debug information before execution\n            \"\"\"\n\n            def get_code(self, fullname):\n                code = super().get_code(fullname)\n                if code:\n                    # Ideally we'd do\n                    # code.co_lnotab = b''\n                    # But code objects are READONLY. Not to worry though; we'll\n                    # directly modify CPython's object\n                    code_impl = PyVarObject.from_address(id(code.co_lnotab))\n                    code_impl.ob_size = 0\n                return code\n\n        if isinstance(spec.loader, SourceFileLoader):\n            spec.loader = DebugWipeLoader(fullname, spec.origin)\n        return spec\n\n\ndef optimize_for_coverage(cov, include_patterns, omit_patterns):\n    \"\"\"\n    We get better performance if we zero out debug information for files which\n    we're not interested in. Only available in CPython 3.3+\n    \"\"\"\n    matcher = PathMatcher(include_patterns, omit_patterns)\n    if SourceFileLoader and platform.python_implementation() == \"CPython\":\n        sys.meta_path.insert(0, DebugWipeFinder(matcher))\n\n\nclass TeeStream:\n    def __init__(self, *streams):\n        self._streams = streams\n\n    def write(self, data):\n        for stream in self._streams:\n            stream.write(data)\n\n    def flush(self):\n        for stream in self._streams:\n            stream.flush()\n\n    def isatty(self):\n        return False\n\n\nclass CallbackStream:\n    def __init__(self, callback, bytes_callback=None, orig=None):\n        self._callback = callback\n        self._fileno = orig.fileno() if orig else None\n\n        # Python 3 APIs:\n        # - `encoding` is a string holding the encoding name\n        # - `errors` is a string holding the error-handling mode for encoding\n        # - `buffer` should look like an io.BufferedIOBase object\n\n        self.errors = orig.errors if orig else None\n        if bytes_callback:\n            # those members are only on the io.TextIOWrapper\n            self.encoding = orig.encoding if orig else \"UTF-8\"\n            self.buffer = CallbackStream(bytes_callback, orig=orig)\n\n    def write(self, data):\n        self._callback(data)\n\n    def flush(self):\n        pass\n\n    def isatty(self):\n        return False\n\n    def fileno(self):\n        return self._fileno\n\n\nclass BuckTestResult(unittest.TextTestResult):\n    \"\"\"\n    Our own TestResult class that outputs data in a format that can be easily\n    parsed by buck's test runner.\n    \"\"\"\n\n    _instr_counter = get_cpu_instr_counter()\n\n    def __init__(\n        self, stream, descriptions, verbosity, show_output, main_program, suite\n    ):\n        super(BuckTestResult, self).__init__(stream, descriptions, verbosity)\n        self._main_program = main_program\n        self._suite = suite\n        self._results = []\n        self._current_test = None\n        self._saved_stdout = sys.stdout\n        self._saved_stderr = sys.stderr\n        self._show_output = show_output\n\n    def getResults(self):\n        return self._results\n\n    def startTest(self, test):\n        super(BuckTestResult, self).startTest(test)\n\n        # Pass in the real stdout and stderr filenos.  We can't really do much\n        # here to intercept callers who directly operate on these fileno\n        # objects.\n        sys.stdout = CallbackStream(\n            self.addStdout, self.addStdoutBytes, orig=sys.stdout\n        )\n        sys.stderr = CallbackStream(\n            self.addStderr, self.addStderrBytes, orig=sys.stderr\n        )\n        self._current_test = test\n        self._test_start_time = time.time()\n        self._current_status = TestStatus.ABORTED\n        self._messages = []\n        self._stacktrace = None\n        self._stdout = \"\"\n        self._stderr = \"\"\n        self._start_instr_count = self._instr_counter.read()\n\n    def _find_next_test(self, suite):\n        \"\"\"\n        Find the next test that has not been run.\n        \"\"\"\n\n        for test in suite:\n\n            # We identify test suites by test that are iterable (as is done in\n            # the builtin python test harness).  If we see one, recurse on it.\n            if hasattr(test, \"__iter__\"):\n                test = self._find_next_test(test)\n\n            # The builtin python test harness sets test references to `None`\n            # after they have run, so we know we've found the next test up\n            # if it's not `None`.\n            if test is not None:\n                return test\n\n    def stopTest(self, test):\n        sys.stdout = self._saved_stdout\n        sys.stderr = self._saved_stderr\n\n        super(BuckTestResult, self).stopTest(test)\n\n        # If a failure occurred during module/class setup, then this \"test\" may\n        # actually be a `_ErrorHolder`, which doesn't contain explicit info\n        # about the upcoming test.  Since we really only care about the test\n        # name field (i.e. `_testMethodName`), we use that to detect an actual\n        # test cases, and fall back to looking the test up from the suite\n        # otherwise.\n        if not hasattr(test, \"_testMethodName\"):\n            test = self._find_next_test(self._suite)\n\n        result = {\n            \"testCaseName\": \"{0}.{1}\".format(\n                test.__class__.__module__, test.__class__.__name__\n            ),\n            \"testCase\": test._testMethodName,\n            \"type\": self._current_status,\n            \"time\": int((time.time() - self._test_start_time) * 1000),\n            \"message\": os.linesep.join(self._messages),\n            \"stacktrace\": self._stacktrace,\n            \"stdOut\": self._stdout,\n            \"stdErr\": self._stderr,\n        }\n\n        # TestPilot supports an instruction count field.\n        if \"TEST_PILOT\" in os.environ:\n            result[\"instrCount\"] = (\n                int(self._instr_counter.read() - self._start_instr_count),\n            )\n\n        self._results.append(result)\n        self._current_test = None\n\n    def stopTestRun(self):\n        cov = self._main_program.get_coverage()\n        if cov is not None:\n            self._results.append({\"coverage\": cov})\n\n    @contextlib.contextmanager\n    def _withTest(self, test):\n        self.startTest(test)\n        yield\n        self.stopTest(test)\n\n    def _setStatus(self, test, status, message=None, stacktrace=None):\n        assert test == self._current_test\n        self._current_status = status\n        self._stacktrace = stacktrace\n        if message is not None:\n            if message.endswith(os.linesep):\n                message = message[:-1]\n            self._messages.append(message)\n\n    def setStatus(self, test, status, message=None, stacktrace=None):\n        # addError() may be called outside of a test if one of the shared\n        # fixtures (setUpClass/tearDownClass/setUpModule/tearDownModule)\n        # throws an error.\n        #\n        # In this case, create a fake test result to record the error.\n        if self._current_test is None:\n            with self._withTest(test):\n                self._setStatus(test, status, message, stacktrace)\n        else:\n            self._setStatus(test, status, message, stacktrace)\n\n    def setException(self, test, status, excinfo):\n        exctype, value, tb = excinfo\n        self.setStatus(\n            test,\n            status,\n            \"{0}: {1}\".format(exctype.__name__, value),\n            \"\".join(traceback.format_tb(tb)),\n        )\n\n    def addSuccess(self, test):\n        super(BuckTestResult, self).addSuccess(test)\n        self.setStatus(test, TestStatus.PASSED)\n\n    def addError(self, test, err):\n        super(BuckTestResult, self).addError(test, err)\n        self.setException(test, TestStatus.ABORTED, err)\n\n    def addFailure(self, test, err):\n        super(BuckTestResult, self).addFailure(test, err)\n        self.setException(test, TestStatus.FAILED, err)\n\n    def addSkip(self, test, reason):\n        super(BuckTestResult, self).addSkip(test, reason)\n        self.setStatus(test, TestStatus.SKIPPED, \"Skipped: %s\" % (reason,))\n\n    def addExpectedFailure(self, test, err):\n        super(BuckTestResult, self).addExpectedFailure(test, err)\n        self.setException(test, TestStatus.EXPECTED_FAILURE, err)\n\n    def addUnexpectedSuccess(self, test):\n        super(BuckTestResult, self).addUnexpectedSuccess(test)\n        self.setStatus(test, TestStatus.UNEXPECTED_SUCCESS, \"Unexpected success\")\n\n    def addStdout(self, val):\n        self._stdout += val\n        if self._show_output:\n            self._saved_stdout.write(val)\n            self._saved_stdout.flush()\n\n    def addStdoutBytes(self, val):\n        string = val.decode(\"utf-8\", errors=\"backslashreplace\")\n        self.addStdout(string)\n\n    def addStderr(self, val):\n        self._stderr += val\n        if self._show_output:\n            self._saved_stderr.write(val)\n            self._saved_stderr.flush()\n\n    def addStderrBytes(self, val):\n        string = val.decode(\"utf-8\", errors=\"backslashreplace\")\n        self.addStderr(string)\n\n\nclass BuckTestRunner(unittest.TextTestRunner):\n    def __init__(self, main_program, suite, show_output=True, **kwargs):\n        super(BuckTestRunner, self).__init__(**kwargs)\n        self.show_output = show_output\n        self._main_program = main_program\n        self._suite = suite\n\n    def _makeResult(self):\n        return BuckTestResult(\n            self.stream,\n            self.descriptions,\n            self.verbosity,\n            self.show_output,\n            self._main_program,\n            self._suite,\n        )\n\n\ndef _format_test_name(test_class, attrname):\n    return \"{0}.{1}.{2}\".format(test_class.__module__, test_class.__name__, attrname)\n\n\nclass StderrLogHandler(logging.StreamHandler):\n    \"\"\"\n    This class is very similar to logging.StreamHandler, except that it\n    always uses the current sys.stderr object.\n\n    StreamHandler caches the current sys.stderr object when it is constructed.\n    This makes it behave poorly in unit tests, which may replace sys.stderr\n    with a StringIO buffer during tests.  The StreamHandler will continue using\n    the old sys.stderr object instead of the desired StringIO buffer.\n    \"\"\"\n\n    def __init__(self):\n        logging.Handler.__init__(self)\n\n    @property\n    def stream(self):\n        return sys.stderr\n\n\nclass RegexTestLoader(unittest.TestLoader):\n    def __init__(self, regex=None):\n        self.regex = regex\n        super(RegexTestLoader, self).__init__()\n\n    def getTestCaseNames(self, testCaseClass):\n        \"\"\"\n        Return a sorted sequence of method names found within testCaseClass\n        \"\"\"\n\n        testFnNames = super(RegexTestLoader, self).getTestCaseNames(testCaseClass)\n        if self.regex is None:\n            return testFnNames\n        robj = re.compile(self.regex)\n        matched = []\n        for attrname in testFnNames:\n            fullname = _format_test_name(testCaseClass, attrname)\n            if robj.search(fullname):\n                matched.append(attrname)\n        return matched\n\n\nclass Loader:\n\n    suiteClass = unittest.TestSuite\n\n    def __init__(self, modules, regex=None):\n        self.modules = modules\n        self.regex = regex\n\n    def load_all(self):\n        loader = RegexTestLoader(self.regex)\n        test_suite = self.suiteClass()\n        for module_name in self.modules:\n            __import__(module_name, level=0)\n            module = sys.modules[module_name]\n            module_suite = loader.loadTestsFromModule(module)\n            test_suite.addTest(module_suite)\n        return test_suite\n\n    def load_args(self, args):\n        loader = RegexTestLoader(self.regex)\n\n        suites = []\n        for arg in args:\n            suite = loader.loadTestsFromName(arg)\n            # loadTestsFromName() can only process names that refer to\n            # individual test functions or modules.  It can't process package\n            # names.  If there were no module/function matches, check to see if\n            # this looks like a package name.\n            if suite.countTestCases() != 0:\n                suites.append(suite)\n                continue\n\n            # Load all modules whose name is <arg>.<something>\n            prefix = arg + \".\"\n            for module in self.modules:\n                if module.startswith(prefix):\n                    suite = loader.loadTestsFromName(module)\n                    suites.append(suite)\n\n        return loader.suiteClass(suites)\n\n\n_COVERAGE_INI = \"\"\"\\\n[report]\nexclude_lines =\n    pragma: no cover\n    pragma: nocover\n    pragma:.*no${PLATFORM}\n    pragma:.*no${PY_IMPL}${PY_MAJOR}${PY_MINOR}\n    pragma:.*no${PY_IMPL}${PY_MAJOR}\n    pragma:.*nopy${PY_MAJOR}\n    pragma:.*nopy${PY_MAJOR}${PY_MINOR}\n\"\"\"\n\n\nclass MainProgram:\n    \"\"\"\n    This class implements the main program.  It can be subclassed by\n    users who wish to customize some parts of the main program.\n    (Adding additional command line options, customizing test loading, etc.)\n    \"\"\"\n\n    DEFAULT_VERBOSITY = 2\n\n    def __init__(self, argv):\n        self.init_option_parser()\n        self.parse_options(argv)\n        self.setup_logging()\n\n    def init_option_parser(self):\n        usage = \"%prog [options] [TEST] ...\"\n        op = optparse.OptionParser(usage=usage, add_help_option=False)\n        self.option_parser = op\n\n        op.add_option(\n            \"--hide-output\",\n            dest=\"show_output\",\n            action=\"store_false\",\n            default=True,\n            help=\"Suppress data that tests print to stdout/stderr, and only \"\n            \"show it if the test fails.\",\n        )\n        op.add_option(\n            \"-o\",\n            \"--output\",\n            help=\"Write results to a file in a JSON format to be read by Buck\",\n        )\n        op.add_option(\n            \"-f\",\n            \"--failfast\",\n            action=\"store_true\",\n            default=False,\n            help=\"Stop after the first failure\",\n        )\n        op.add_option(\n            \"-l\",\n            \"--list-tests\",\n            action=\"store_true\",\n            dest=\"list\",\n            default=False,\n            help=\"List tests and exit\",\n        )\n        op.add_option(\n            \"-r\",\n            \"--regex\",\n            default=None,\n            help=\"Regex to apply to tests, to only run those tests\",\n        )\n        op.add_option(\n            \"--collect-coverage\",\n            action=\"store_true\",\n            default=False,\n            help=\"Collect test coverage information\",\n        )\n        op.add_option(\n            \"--coverage-include\",\n            default=\"*\",\n            help='File globs to include in converage (split by \",\")',\n        )\n        op.add_option(\n            \"--coverage-omit\",\n            default=\"\",\n            help='File globs to omit from converage (split by \",\")',\n        )\n        op.add_option(\n            \"--logger\",\n            action=\"append\",\n            metavar=\"<category>=<level>\",\n            default=[],\n            help=\"Configure log levels for specific logger categories\",\n        )\n        op.add_option(\n            \"-q\",\n            \"--quiet\",\n            action=\"count\",\n            default=0,\n            help=\"Decrease the verbosity (may be specified multiple times)\",\n        )\n        op.add_option(\n            \"-v\",\n            \"--verbosity\",\n            action=\"count\",\n            default=self.DEFAULT_VERBOSITY,\n            help=\"Increase the verbosity (may be specified multiple times)\",\n        )\n        op.add_option(\n            \"-?\", \"--help\", action=\"help\", help=\"Show this help message and exit\"\n        )\n\n    def parse_options(self, argv):\n        self.options, self.test_args = self.option_parser.parse_args(argv[1:])\n        self.options.verbosity -= self.options.quiet\n\n        if self.options.collect_coverage and coverage is None:\n            self.option_parser.error(\"coverage module is not available\")\n        self.options.coverage_include = self.options.coverage_include.split(\",\")\n        if self.options.coverage_omit == \"\":\n            self.options.coverage_omit = []\n        else:\n            self.options.coverage_omit = self.options.coverage_omit.split(\",\")\n\n    def setup_logging(self):\n        # Configure the root logger to log at INFO level.\n        # This is similar to logging.basicConfig(), but uses our\n        # StderrLogHandler instead of a StreamHandler.\n        fmt = logging.Formatter(\"%(pathname)s:%(lineno)s: %(message)s\")\n        log_handler = StderrLogHandler()\n        log_handler.setFormatter(fmt)\n        root_logger = logging.getLogger()\n        root_logger.addHandler(log_handler)\n        root_logger.setLevel(logging.INFO)\n\n        level_names = {\n            \"debug\": logging.DEBUG,\n            \"info\": logging.INFO,\n            \"warn\": logging.WARNING,\n            \"warning\": logging.WARNING,\n            \"error\": logging.ERROR,\n            \"critical\": logging.CRITICAL,\n            \"fatal\": logging.FATAL,\n        }\n\n        for value in self.options.logger:\n            parts = value.rsplit(\"=\", 1)\n            if len(parts) != 2:\n                self.option_parser.error(\n                    \"--logger argument must be of the \"\n                    \"form <name>=<level>: %s\" % value\n                )\n            name = parts[0]\n            level_name = parts[1].lower()\n            level = level_names.get(level_name)\n            if level is None:\n                self.option_parser.error(\n                    \"invalid log level %r for log \" \"category %s\" % (parts[1], name)\n                )\n            logging.getLogger(name).setLevel(level)\n\n    def create_loader(self):\n        import __test_modules__\n\n        return Loader(__test_modules__.TEST_MODULES, self.options.regex)\n\n    def load_tests(self):\n        loader = self.create_loader()\n        if self.options.collect_coverage:\n            self.start_coverage()\n            include = self.options.coverage_include\n            omit = self.options.coverage_omit\n            if include and \"*\" not in include:\n                optimize_for_coverage(self.cov, include, omit)\n\n        if self.test_args:\n            suite = loader.load_args(self.test_args)\n        else:\n            suite = loader.load_all()\n        if self.options.collect_coverage:\n            self.cov.start()\n        return suite\n\n    def get_tests(self, test_suite):\n        tests = []\n\n        for test in test_suite:\n            if isinstance(test, unittest.TestSuite):\n                tests.extend(self.get_tests(test))\n            else:\n                tests.append(test)\n\n        return tests\n\n    def run(self):\n        test_suite = self.load_tests()\n\n        if self.options.list:\n            for test in self.get_tests(test_suite):\n                method_name = getattr(test, \"_testMethodName\", \"\")\n                name = _format_test_name(test.__class__, method_name)\n                print(name)\n            return EXIT_CODE_SUCCESS\n        else:\n            result = self.run_tests(test_suite)\n            if self.options.output is not None:\n                with open(self.options.output, \"w\") as f:\n                    json.dump(result.getResults(), f, indent=4, sort_keys=True)\n            if not result.wasSuccessful():\n                return EXIT_CODE_TEST_FAILURE\n            return EXIT_CODE_SUCCESS\n\n    def run_tests(self, test_suite):\n        # Install a signal handler to catch Ctrl-C and display the results\n        # (but only if running >2.6).\n        if sys.version_info[0] > 2 or sys.version_info[1] > 6:\n            unittest.installHandler()\n\n        # Run the tests\n        runner = BuckTestRunner(\n            self,\n            test_suite,\n            verbosity=self.options.verbosity,\n            show_output=self.options.show_output,\n        )\n        result = runner.run(test_suite)\n\n        if self.options.collect_coverage and self.options.show_output:\n            self.cov.stop()\n            try:\n                self.cov.report(file=sys.stdout)\n            except coverage.misc.CoverageException:\n                print(\"No lines were covered, potentially restricted by file filters\")\n\n        return result\n\n    def get_abbr_impl(self):\n        \"\"\"Return abbreviated implementation name.\"\"\"\n        impl = platform.python_implementation()\n        if impl == \"PyPy\":\n            return \"pp\"\n        elif impl == \"Jython\":\n            return \"jy\"\n        elif impl == \"IronPython\":\n            return \"ip\"\n        elif impl == \"CPython\":\n            return \"cp\"\n        else:\n            raise RuntimeError(\"unknown python runtime\")\n\n    def start_coverage(self):\n        if not self.options.collect_coverage:\n            return\n\n        with tempfile.NamedTemporaryFile(\"w\", delete=False) as coverage_ini:\n            coverage_ini.write(_COVERAGE_INI)\n            self._coverage_ini_path = coverage_ini.name\n\n        # Keep the original working dir in case tests use os.chdir\n        self._original_working_dir = os.getcwd()\n\n        # for coverage config ignores by platform/python version\n        os.environ[\"PLATFORM\"] = sys.platform\n        os.environ[\"PY_IMPL\"] = self.get_abbr_impl()\n        os.environ[\"PY_MAJOR\"] = str(sys.version_info.major)\n        os.environ[\"PY_MINOR\"] = str(sys.version_info.minor)\n\n        self.cov = coverage.Coverage(\n            include=self.options.coverage_include,\n            omit=self.options.coverage_omit,\n            config_file=coverage_ini.name,\n        )\n        self.cov.erase()\n        self.cov.start()\n\n    def get_coverage(self):\n        if not self.options.collect_coverage:\n            return None\n\n        try:\n            os.remove(self._coverage_ini_path)\n        except OSError:\n            pass  # Better to litter than to fail the test\n\n        # Switch back to the original working directory.\n        os.chdir(self._original_working_dir)\n\n        result = {}\n\n        self.cov.stop()\n\n        try:\n            f = StringIO()\n            self.cov.report(file=f)\n            lines = f.getvalue().split(\"\\n\")\n        except coverage.misc.CoverageException:\n            # Nothing was covered. That's fine by us\n            return result\n\n        # N.B.: the format of the coverage library's output differs\n        # depending on whether one or more files are in the results\n        for line in lines[2:]:\n            if line.strip(\"-\") == \"\":\n                break\n            r = line.split()[0]\n            analysis = self.cov.analysis2(r)\n            covString = self.convert_to_diff_cov_str(analysis)\n            if covString:\n                result[r] = covString\n\n        return result\n\n    def convert_to_diff_cov_str(self, analysis):\n        # Info on the format of analysis:\n        # http://nedbatchelder.com/code/coverage/api.html\n        if not analysis:\n            return None\n        numLines = max(\n            analysis[1][-1] if len(analysis[1]) else 0,\n            analysis[2][-1] if len(analysis[2]) else 0,\n            analysis[3][-1] if len(analysis[3]) else 0,\n        )\n        lines = [\"N\"] * numLines\n        for l in analysis[1]:\n            lines[l - 1] = \"C\"\n        for l in analysis[2]:\n            lines[l - 1] = \"X\"\n        for l in analysis[3]:\n            lines[l - 1] = \"U\"\n        return \"\".join(lines)\n\n\ndef main(argv):\n    return MainProgram(sys.argv).run()\n\n\nif __name__ == \"__main__\":\n    sys.exit(main(sys.argv))\n"
  },
  {
    "path": "build/fbcode_builder/CMake/fb_py_win_main.c",
    "content": "// Copyright (c) Facebook, Inc. and its affiliates.\n\n#define WIN32_LEAN_AND_MEAN\n\n#include <Windows.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#define PATH_SIZE 32768\n\ntypedef int (*Py_Main)(int, wchar_t**);\n\nint locate_py_main(int argc, wchar_t** argv) {\n  /*\n   * We have to dynamically locate Python3.dll because we may be loading a\n   * Python native module while running. If that module is built with a\n   * different Python version, we will end up a DLL import error. To resolve\n   * this, we can either ship an embedded version of Python with us or\n   * dynamically look up existing Python distribution installed on user's\n   * machine. This way, we should be able to get a consistent version of\n   * Python3.dll and .pyd modules.\n   */\n  HINSTANCE python_dll;\n  Py_Main pymain;\n\n  python_dll =\n      LoadLibraryExW(L\"python3.dll\", NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);\n\n  int returncode = 0;\n  if (python_dll != NULL) {\n    pymain = (Py_Main)GetProcAddress(python_dll, \"Py_Main\");\n\n    if (pymain != NULL) {\n      returncode = (pymain)(argc, argv);\n    } else {\n      fprintf(stderr, \"error: %d unable to load Py_Main\\n\", GetLastError());\n    }\n\n    FreeLibrary(python_dll);\n  } else {\n    fprintf(stderr, \"error: %d unable to locate python3.dll\\n\", GetLastError());\n    return 1;\n  }\n  return returncode;\n}\n\nint wmain() {\n  /*\n   * This executable will be prepended to the start of a Python ZIP archive.\n   * Python will be able to directly execute the ZIP archive, so we simply\n   * need to tell Py_Main() to run our own file.  Duplicate the argument list\n   * and add our file name to the beginning to tell Python what file to invoke.\n   */\n  wchar_t** pyargv = malloc(sizeof(wchar_t*) * (__argc + 1));\n  if (!pyargv) {\n    fprintf(stderr, \"error: failed to allocate argument vector\\n\");\n    return 1;\n  }\n\n  /* Py_Main wants the wide character version of the argv so we pull those\n   * values from the global __wargv array that has been prepared by MSVCRT.\n   *\n   * In order for the zipapp to run we need to insert an extra argument in\n   * the front of the argument vector that points to ourselves.\n   *\n   * An additional complication is that, depending on who prepared the argument\n   * string used to start our process, the computed __wargv[0] can be a simple\n   * shell word like `watchman-wait` which is normally resolved together with\n   * the PATH by the shell.\n   * That unresolved path isn't sufficient to start the zipapp on windows;\n   * we need the fully qualified path.\n   *\n   * Given:\n   * __wargv == {\"watchman-wait\", \"-h\"}\n   *\n   * we want to pass the following to Py_Main:\n   *\n   * {\n   *   \"z:\\build\\watchman\\python\\watchman-wait.exe\",\n   *   \"z:\\build\\watchman\\python\\watchman-wait.exe\",\n   *   \"-h\"\n   * }\n   */\n  wchar_t full_path_to_argv0[PATH_SIZE];\n  DWORD len = GetModuleFileNameW(NULL, full_path_to_argv0, PATH_SIZE);\n  if (len == 0 ||\n      len == PATH_SIZE && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {\n    fprintf(\n        stderr,\n        \"error: %d while retrieving full path to this executable\\n\",\n        GetLastError());\n    return 1;\n  }\n\n  for (int n = 1; n < __argc; ++n) {\n    pyargv[n + 1] = __wargv[n];\n  }\n  pyargv[0] = full_path_to_argv0;\n  pyargv[1] = full_path_to_argv0;\n\n  return locate_py_main(__argc + 1, pyargv);\n}\n"
  },
  {
    "path": "build/fbcode_builder/CMake/make_fbpy_archive.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (c) Facebook, Inc. and its affiliates.\n#\nimport argparse\nimport collections\nimport errno\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport zipapp\n\nMANIFEST_SEPARATOR = \" :: \"\nMANIFEST_HEADER_V1 = \"FBPY_MANIFEST 1\\n\"\n\n\nclass UsageError(Exception):\n    def __init__(self, message):\n        self.message = message\n\n    def __str__(self):\n        return self.message\n\n\nclass BadManifestError(UsageError):\n    def __init__(self, path, line_num, message):\n        full_msg = \"%s:%s: %s\" % (path, line_num, message)\n        super().__init__(full_msg)\n        self.path = path\n        self.line_num = line_num\n        self.raw_message = message\n\n\nPathInfo = collections.namedtuple(\n    \"PathInfo\", (\"src\", \"dest\", \"manifest_path\", \"manifest_line\")\n)\n\n\ndef parse_manifest(manifest, path_map):\n    bad_prefix = \"..\" + os.path.sep\n    manifest_dir = os.path.dirname(manifest)\n    with open(manifest, \"r\") as f:\n        line_num = 1\n        line = f.readline()\n        if line != MANIFEST_HEADER_V1:\n            raise BadManifestError(\n                manifest, line_num, \"Unexpected manifest file header\"\n            )\n\n        for line in f:\n            line_num += 1\n            if line.startswith(\"#\"):\n                continue\n            line = line.rstrip(\"\\n\")\n            parts = line.split(MANIFEST_SEPARATOR)\n            if len(parts) != 2:\n                msg = \"line must be of the form SRC %s DEST\" % MANIFEST_SEPARATOR\n                raise BadManifestError(manifest, line_num, msg)\n            src, dest = parts\n            dest = os.path.normpath(dest)\n            if dest.startswith(bad_prefix):\n                msg = \"destination path starts with %s: %s\" % (bad_prefix, dest)\n                raise BadManifestError(manifest, line_num, msg)\n\n            if not os.path.isabs(src):\n                src = os.path.normpath(os.path.join(manifest_dir, src))\n\n            if dest in path_map:\n                prev_info = path_map[dest]\n                msg = (\n                    \"multiple source paths specified for destination \"\n                    \"path %s.  Previous source was %s from %s:%s\"\n                    % (\n                        dest,\n                        prev_info.src,\n                        prev_info.manifest_path,\n                        prev_info.manifest_line,\n                    )\n                )\n                raise BadManifestError(manifest, line_num, msg)\n\n            info = PathInfo(\n                src=src,\n                dest=dest,\n                manifest_path=manifest,\n                manifest_line=line_num,\n            )\n            path_map[dest] = info\n\n\ndef populate_install_tree(inst_dir, path_map):\n    os.mkdir(inst_dir)\n    dest_dirs = {\"\": False}\n\n    def make_dest_dir(path):\n        if path in dest_dirs:\n            return\n        parent = os.path.dirname(path)\n        make_dest_dir(parent)\n        abs_path = os.path.join(inst_dir, path)\n        os.mkdir(abs_path)\n        dest_dirs[path] = False\n\n    def install_file(info):\n        dir_name, base_name = os.path.split(info.dest)\n        make_dest_dir(dir_name)\n        if base_name == \"__init__.py\":\n            dest_dirs[dir_name] = True\n        abs_dest = os.path.join(inst_dir, info.dest)\n        shutil.copy2(info.src, abs_dest)\n\n    # Copy all of the destination files\n    for info in path_map.values():\n        install_file(info)\n\n    # Create __init__ files in any directories that don't have them.\n    for dir_path, has_init in dest_dirs.items():\n        if has_init:\n            continue\n        init_path = os.path.join(inst_dir, dir_path, \"__init__.py\")\n        with open(init_path, \"w\"):\n            pass\n\n\ndef build_pex(args, path_map):\n    \"\"\"Create a self executing python binary using the PEX tool\n\n    This type of Python binary is more complex as it requires a third-party tool,\n    but it does support native language extensions (.so/.dll files).\n    \"\"\"\n    dest_dir = os.path.dirname(args.output)\n    with tempfile.TemporaryDirectory(prefix=\"make_fbpy.\", dir=dest_dir) as tmpdir:\n        inst_dir = os.path.join(tmpdir, \"tree\")\n        populate_install_tree(inst_dir, path_map)\n\n        if os.path.exists(os.path.join(inst_dir, \"__main__.py\")):\n            os.rename(\n                os.path.join(inst_dir, \"__main__.py\"),\n                os.path.join(inst_dir, \"main.py\"),\n            )\n            args.main = \"main\"\n\n        tmp_output = os.path.abspath(os.path.join(tmpdir, \"output.exe\"))\n        subprocess.check_call(\n            [\"pex\"]\n            + [\"--output-file\", tmp_output]\n            + [\"--python\", args.python]\n            + [\"--sources-directory\", inst_dir]\n            + [\"-e\", args.main]\n        )\n\n        os.replace(tmp_output, args.output)\n\n\ndef build_zipapp(args, path_map):\n    \"\"\"Create a self executing python binary using Python 3's built-in\n    zipapp module.\n\n    This type of Python binary is relatively simple, as zipapp is part of the\n    standard library, but it does not support native language extensions\n    (.so/.dll files).\n    \"\"\"\n    dest_dir = os.path.dirname(args.output)\n    with tempfile.TemporaryDirectory(prefix=\"make_fbpy.\", dir=dest_dir) as tmpdir:\n        inst_dir = os.path.join(tmpdir, \"tree\")\n        populate_install_tree(inst_dir, path_map)\n\n        tmp_output = os.path.join(tmpdir, \"output.exe\")\n        zipapp.create_archive(\n            inst_dir, target=tmp_output, interpreter=args.python, main=args.main\n        )\n        os.replace(tmp_output, args.output)\n\n\ndef create_main_module(args, inst_dir, path_map):\n    if not args.main:\n        assert \"__main__.py\" in path_map\n        return\n\n    dest_path = os.path.join(inst_dir, \"__main__.py\")\n    main_module, main_fn = args.main.split(\":\")\n    main_contents = \"\"\"\\\n#!{python}\n\nif __name__ == \"__main__\":\n    import {main_module}\n    {main_module}.{main_fn}()\n\"\"\".format(\n        python=args.python, main_module=main_module, main_fn=main_fn\n    )\n    with open(dest_path, \"w\") as f:\n        f.write(main_contents)\n    os.chmod(dest_path, 0o755)\n\n\ndef build_install_dir(args, path_map):\n    \"\"\"Create a directory that contains all of the sources, with a __main__\n    module to run the program.\n    \"\"\"\n    # Populate a temporary directory first, then rename to the destination\n    # location.  This ensures that we don't ever leave a halfway-built\n    # directory behind at the output path if something goes wrong.\n    dest_dir = os.path.dirname(args.output)\n    with tempfile.TemporaryDirectory(prefix=\"make_fbpy.\", dir=dest_dir) as tmpdir:\n        inst_dir = os.path.join(tmpdir, \"tree\")\n        populate_install_tree(inst_dir, path_map)\n        create_main_module(args, inst_dir, path_map)\n        os.rename(inst_dir, args.output)\n\n\ndef ensure_directory(path):\n    try:\n        os.makedirs(path)\n    except OSError as ex:\n        if ex.errno != errno.EEXIST:\n            raise\n\n\ndef install_library(args, path_map):\n    \"\"\"Create an installation directory a python library.\"\"\"\n    out_dir = args.output\n    out_manifest = args.output + \".manifest\"\n\n    install_dir = args.install_dir\n    if not install_dir:\n        install_dir = out_dir\n\n    os.makedirs(out_dir)\n    with open(out_manifest, \"w\") as manifest:\n        manifest.write(MANIFEST_HEADER_V1)\n        for info in path_map.values():\n            abs_dest = os.path.join(out_dir, info.dest)\n            ensure_directory(os.path.dirname(abs_dest))\n            print(\"copy %r --> %r\" % (info.src, abs_dest))\n            shutil.copy2(info.src, abs_dest)\n            installed_dest = os.path.join(install_dir, info.dest)\n            manifest.write(\"%s%s%s\\n\" % (installed_dest, MANIFEST_SEPARATOR, info.dest))\n\n\ndef parse_manifests(args):\n    # Process args.manifest_separator to help support older versions of CMake\n    if args.manifest_separator:\n        manifests = []\n        for manifest_arg in args.manifests:\n            split_arg = manifest_arg.split(args.manifest_separator)\n            manifests.extend(split_arg)\n        args.manifests = manifests\n\n    path_map = {}\n    for manifest in args.manifests:\n        parse_manifest(manifest, path_map)\n\n    return path_map\n\n\ndef check_main_module(args, path_map):\n    # Translate an empty string in the --main argument to None,\n    # just to allow the CMake logic to be slightly simpler and pass in an\n    # empty string when it really wants the default __main__.py module to be\n    # used.\n    if args.main == \"\":\n        args.main = None\n\n    if args.type == \"lib-install\":\n        if args.main is not None:\n            raise UsageError(\"cannot specify a --main argument with --type=lib-install\")\n        return\n\n    main_info = path_map.get(\"__main__.py\")\n    if args.main:\n        if main_info is not None:\n            msg = (\n                \"specified an explicit main module with --main, \"\n                \"but the file listing already includes __main__.py\"\n            )\n            raise BadManifestError(\n                main_info.manifest_path, main_info.manifest_line, msg\n            )\n        parts = args.main.split(\":\")\n        if len(parts) != 2:\n            raise UsageError(\n                \"argument to --main must be of the form MODULE:CALLABLE \"\n                \"(received %s)\" % (args.main,)\n            )\n    else:\n        if main_info is None:\n            raise UsageError(\n                \"no main module specified with --main, \"\n                \"and no __main__.py module present\"\n            )\n\n\nBUILD_TYPES = {\n    \"pex\": build_pex,\n    \"zipapp\": build_zipapp,\n    \"dir\": build_install_dir,\n    \"lib-install\": install_library,\n}\n\n\ndef main():\n    ap = argparse.ArgumentParser()\n    ap.add_argument(\"-o\", \"--output\", required=True, help=\"The output file path\")\n    ap.add_argument(\n        \"--install-dir\",\n        help=\"When used with --type=lib-install, this parameter specifies the \"\n        \"final location where the library where be installed.  This can be \"\n        \"used to generate the library in one directory first, when you plan \"\n        \"to move or copy it to another final location later.\",\n    )\n    ap.add_argument(\n        \"--manifest-separator\",\n        help=\"Split manifest arguments around this separator.  This is used \"\n        \"to support older versions of CMake that cannot supply the manifests \"\n        \"as separate arguments.\",\n    )\n    ap.add_argument(\n        \"--main\",\n        help=\"The main module to run, specified as <module>:<callable>.  \"\n        \"This must be specified if and only if the archive does not contain \"\n        \"a __main__.py file.\",\n    )\n    ap.add_argument(\n        \"--python\",\n        help=\"Explicitly specify the python interpreter to use for the \" \"executable.\",\n    )\n    ap.add_argument(\n        \"--type\", choices=BUILD_TYPES.keys(), help=\"The type of output to build.\"\n    )\n    ap.add_argument(\n        \"manifests\",\n        nargs=\"+\",\n        help=\"The manifest files specifying how to construct the archive\",\n    )\n    args = ap.parse_args()\n\n    if args.python is None:\n        args.python = sys.executable\n\n    if args.type is None:\n        # In the future we might want different default output types\n        # for different platforms.\n        args.type = \"zipapp\"\n    build_fn = BUILD_TYPES[args.type]\n\n    try:\n        path_map = parse_manifests(args)\n        check_main_module(args, path_map)\n    except UsageError as ex:\n        print(\"error: %s\" % (ex,), file=sys.stderr)\n        sys.exit(1)\n\n    build_fn(args, path_map)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "build/fbcode_builder/LICENSE",
    "content": "MIT License\n\nCopyright (c) Facebook, Inc. and its affiliates.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "build/fbcode_builder/README.md",
    "content": "# Easy builds for Facebook projects\n\nThis directory contains tools designed to simplify continuous-integration\n(and other builds) of Facebook open source projects.  In particular, this helps\nmanage builds for cross-project dependencies.\n\nThe main entry point is the `getdeps.py` script.  This script has several\nsubcommands, but the most notable is the `build` command.  This will download\nand build all dependencies for a project, and then build the project itself.\n\n## Deployment\n\nThis directory is copied literally into a number of different Facebook open\nsource repositories.  Any change made to code in this directory will be\nautomatically be replicated by our open source tooling into all GitHub hosted\nrepositories that use `fbcode_builder`.  Typically this directory is copied\ninto the open source repositories as `build/fbcode_builder/`.\n\n\n# Project Configuration Files\n\nThe `manifests` subdirectory contains configuration files for many different\nprojects, describing how to build each project.  These files also list\ndependencies between projects, enabling `getdeps.py` to build all dependencies\nfor a project before building the project itself.\n\n\n# Shared CMake utilities\n\nSince this directory is copied into many Facebook open source repositories,\nit is also used to help share some CMake utility files across projects.  The\n`CMake/` subdirectory contains a number of `.cmake` files that are shared by\nthe CMake-based build systems across several different projects.\n\n\n# Older Build Scripts\n\nThis directory also still contains a handful of older build scripts that\npre-date the current `getdeps.py` build system.  Most of the other `.py` files\nin this top directory, apart from `getdeps.py` itself, are from this older\nbuild system.  This older system is only used by a few remaining projects, and\nnew projects should generally use the newer `getdeps.py` script, by adding a\nnew configuration file in the `manifests/` subdirectory.\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/__init__.py",
    "content": "# pyre-strict\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/builder.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\nimport glob\nimport json\nimport os\nimport os.path\nimport pathlib\nimport re\nimport shutil\nimport stat\nimport subprocess\nimport sys\nimport typing\nfrom collections.abc import Callable, Sequence\nfrom shlex import quote as shellquote\n\nfrom .copytree import rmtree_more, simple_copytree\nfrom .dyndeps import create_dyn_dep_munger\nfrom .envfuncs import add_path_entry, Env, path_search\nfrom .fetcher import copy_if_different, is_public_commit\nfrom .runcmd import make_memory_limit_preexec_fn, run_cmd\n\nif typing.TYPE_CHECKING:\n    from .buildopts import BuildOptions\n    from .dyndeps import DepBase\n    from .load import ManifestLoader\n    from .manifest import ManifestContext, ManifestParser\n\n\nclass BuilderBase:\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str | None,\n        inst_dir: str,\n        env: Env | None = None,\n        final_install_prefix: str | None = None,\n    ) -> None:\n        self.env: Env = Env()\n        if env:\n            # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got\n            #  `Env`.\n            self.env.update(env)\n\n        subdir: str | None = manifest.get(\"build\", \"subdir\", ctx=ctx)\n        if subdir:\n            src_dir = os.path.join(src_dir, subdir)\n\n        self.patchfile: str | None = manifest.get(\"build\", \"patchfile\", ctx=ctx)\n        self.patchfile_opts: str = (\n            manifest.get(\"build\", \"patchfile_opts\", ctx=ctx) or \"\"\n        )\n        self.ctx: ManifestContext = ctx\n        self.src_dir: str = src_dir\n        self.build_dir: str = build_dir or src_dir\n        self.inst_dir: str = inst_dir\n        self.build_opts: BuildOptions = build_opts\n        self.manifest: ManifestParser = manifest\n        self.final_install_prefix: str | None = final_install_prefix\n        self.loader: ManifestLoader = loader\n        self.dep_manifests: list[ManifestParser] = dep_manifests\n        self.install_dirs: list[str] = [\n            loader.get_project_install_dir(m) for m in dep_manifests\n        ]\n\n    def _get_cmd_prefix(self) -> list[str]:\n        if self.build_opts.is_windows():\n            vcvarsall = self.build_opts.get_vcvars_path()\n            if vcvarsall is not None:\n                # Since it sets rather a large number of variables we mildly abuse\n                # the cmd quoting rules to assemble a command that calls the script\n                # to prep the environment and then triggers the actual command that\n                # we wanted to run.\n\n                # Due to changes in vscrsall.bat, it now reports an ERRORLEVEL of 1\n                # even when succeeding. This occurs when an extension is not present.\n                # To continue, we must ignore the ERRORLEVEL returned. We do this by\n                # wrapping the call in a batch file that always succeeds.\n                wrapper = os.path.join(self.build_dir, \"succeed.bat\")\n                with open(wrapper, \"w\") as f:\n                    f.write(\"@echo off\\n\")\n                    f.write(f'call \"{vcvarsall}\" amd64\\n')\n                    f.write(\"set ERRORLEVEL=0\\n\")\n                    f.write(\"exit /b 0\\n\")\n                return [wrapper, \"&&\"]\n        return []\n\n    def _check_cmd(self, cmd: list[str], **kwargs: object) -> None:\n        \"\"\"Run the command and abort on failure\"\"\"\n        # pyre-fixme[6]: For 2nd argument expected `Optional[Env]` but got `object`.\n        # pyre-fixme[6]: For 2nd argument expected `Optional[str]` but got `object`.\n        # pyre-fixme[6]: For 2nd argument expected `bool` but got `object`.\n        rc = self._run_cmd(cmd, **kwargs)\n        if rc != 0:\n            raise RuntimeError(f\"Failure exit code {rc} for command {cmd}\")\n\n    def _run_cmd(\n        self,\n        cmd: list[str],\n        cwd: str | None = None,\n        env: Env | None = None,\n        use_cmd_prefix: bool = True,\n        allow_fail: bool = False,\n        preexec_fn: Callable[[], None] | None = None,\n    ) -> int:\n        if env:\n            e = self.env.copy()\n            # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got\n            #  `Env`.\n            e.update(env)\n            env = e\n        else:\n            env = self.env\n\n        if use_cmd_prefix:\n            cmd_prefix = self._get_cmd_prefix()\n            if cmd_prefix:\n                cmd = cmd_prefix + cmd\n\n        log_file = os.path.join(self.build_dir, \"getdeps_build.log\")\n        return run_cmd(\n            cmd=cmd,\n            env=env,\n            cwd=cwd or self.build_dir,\n            log_file=log_file,\n            allow_fail=allow_fail,\n            preexec_fn=preexec_fn,\n        )\n\n    def _reconfigure(self, reconfigure: bool) -> bool:\n        if self.build_dir is not None:\n            if not os.path.isdir(self.build_dir):\n                os.makedirs(self.build_dir)\n                reconfigure = True\n        return reconfigure\n\n    def _apply_patchfile(self) -> None:\n        if self.patchfile is None:\n            return\n        patched_sentinel_file = pathlib.Path(self.src_dir + \"/.getdeps_patched\")\n        if patched_sentinel_file.exists():\n            return\n        old_wd = os.getcwd()\n        os.chdir(self.src_dir)\n        # Apply patches from the git repo root so paths resolve correctly\n        # even when src_dir is a subdirectory of the repo.\n        try:\n            git_root = subprocess.check_output(\n                [\"git\", \"rev-parse\", \"--show-toplevel\"], text=True\n            ).strip()\n            os.chdir(git_root)\n        except subprocess.CalledProcessError:\n            pass  # not a git repo, stay in src_dir\n        print(f\"Patching {self.manifest.name} with {self.patchfile} in {os.getcwd()}\")\n        patchfile = os.path.join(\n            self.build_opts.fbcode_builder_dir,\n            \"patches\",\n            # pyre-fixme[6]: For 3rd argument expected `Union[PathLike[str], str]`\n            #  but got `Optional[str]`.\n            self.patchfile,\n        )\n        patchcmd = [\"git\", \"apply\", \"--ignore-space-change\"]\n        if self.patchfile_opts:\n            patchcmd.append(self.patchfile_opts)\n        try:\n            subprocess.check_call(patchcmd + [patchfile])\n        except subprocess.CalledProcessError:\n            raise ValueError(f\"Failed to apply patch to {self.manifest.name}\")\n        os.chdir(old_wd)\n        patched_sentinel_file.touch()\n\n    def prepare(self, reconfigure: bool) -> None:\n        print(\"Preparing %s...\" % self.manifest.name)\n        reconfigure = self._reconfigure(reconfigure)\n        self._apply_patchfile()\n        self._prepare(reconfigure=reconfigure)\n\n    def debug(self, reconfigure: bool) -> None:\n        reconfigure = self._reconfigure(reconfigure)\n        self._apply_patchfile()\n        self._prepare(reconfigure=reconfigure)\n        env = self._compute_env()\n        print(\"Starting a shell in %s, ^D to exit...\" % self.build_dir)\n        # TODO: print the command to run the build\n        shell = [\"powershell.exe\"] if sys.platform == \"win32\" else [\"/bin/sh\", \"-i\"]\n        self._run_cmd(shell, cwd=self.build_dir, env=env)\n\n    def printenv(self, reconfigure: bool) -> None:\n        \"\"\"print the environment in a shell sourcable format\"\"\"\n        reconfigure = self._reconfigure(reconfigure)\n        self._apply_patchfile()\n        self._prepare(reconfigure=reconfigure)\n        env = self._compute_env(env=Env(src={}))\n        prefix = \"export \"\n        sep = \":\"\n        expand = \"$\"\n        expandpost = \"\"\n        if self.build_opts.is_windows():\n            prefix = \"SET \"\n            sep = \";\"\n            expand = \"%\"\n            expandpost = \"%\"\n        for k, v in sorted(env.items()):\n            existing = os.environ.get(k, None)\n            if k.endswith(\"PATH\") and existing:\n                v = shellquote(v) + sep + f\"{expand}{k}{expandpost}\"\n            else:\n                v = shellquote(v)\n            print(\"%s%s=%s\" % (prefix, k, v))\n\n    def build(self, reconfigure: bool) -> None:\n        print(\"Building %s...\" % self.manifest.name)\n        reconfigure = self._reconfigure(reconfigure)\n        self._apply_patchfile()\n        self._prepare(reconfigure=reconfigure)\n        self._build(reconfigure=reconfigure)\n\n        if self.build_opts.free_up_disk:\n            # don't clean --src-dir=. case as user may want to build again or run tests on the build\n            if self.src_dir.startswith(self.build_opts.scratch_dir) and os.path.isdir(\n                self.build_dir\n            ):\n                if os.path.islink(self.build_dir):\n                    os.remove(self.build_dir)\n                else:\n                    rmtree_more(self.build_dir)\n        elif self.build_opts.is_windows():\n            # On Windows, emit a wrapper script that can be used to run build artifacts\n            # directly from the build directory, without installing them.  On Windows $PATH\n            # needs to be updated to include all of the directories containing the runtime\n            # library dependencies in order to run the binaries.\n            script_path = self.get_dev_run_script_path()\n            dep_munger = create_dyn_dep_munger(\n                self.build_opts, self._compute_env(), self.install_dirs\n            )\n            dep_dirs = self.get_dev_run_extra_path_dirs(dep_munger)\n            # pyre-fixme[16]: Optional type has no attribute `emit_dev_run_script`.\n            dep_munger.emit_dev_run_script(script_path, dep_dirs)\n\n    @property\n    def _job_weight_mib(self) -> int:\n        # This is a hack, but we don't have a \"defaults manifest\" that we can\n        # customize per platform.\n        # TODO: Introduce some sort of defaults config that can select by\n        # platform, just like manifest contexts.\n        if sys.platform.startswith(\"freebsd\"):\n            # clang on FreeBSD is quite memory-efficient.\n            default_job_weight = 512\n        else:\n            # 1.5 GiB is a lot to assume, but it's typical of Facebook-style C++.\n            # Some manifests are even heavier and should override.\n            default_job_weight = 1536\n        return int(\n            self.manifest.get(\n                \"build\", \"job_weight_mib\", str(default_job_weight), ctx=self.ctx\n            )\n        )\n\n    @property\n    def num_jobs(self) -> int:\n        return self.build_opts.get_num_jobs(self._job_weight_mib)\n\n    @property\n    def memory_limit_preexec_fn(self) -> Callable[[], None] | None:\n        \"\"\"Return a preexec_fn that caps per-process virtual memory.\n\n        Uses the same job_weight_mib that controls parallelism, so the memory\n        limit is consistent with the parallelism budget.\n        \"\"\"\n        return make_memory_limit_preexec_fn(self._job_weight_mib)\n\n    def run_tests(\n        self,\n        schedule_type: str,\n        owner: str | None,\n        test_filter: str | None,\n        test_exclude: str | None,\n        retry: int,\n        no_testpilot: bool,\n        timeout: int | None = None,\n    ) -> None:\n        \"\"\"Execute any tests that we know how to run.  If they fail,\n        raise an exception.\"\"\"\n        pass\n\n    def _prepare(self, reconfigure: bool) -> None:\n        \"\"\"Prepare the build. Useful when need to generate config,\n        but builder is not the primary build system.\n        e.g. cargo when called from cmake\"\"\"\n        pass\n\n    def _build(self, reconfigure: bool) -> None:\n        \"\"\"Perform the build.\n        reconfigure will be set to true if the fetcher determined\n        that the sources have changed in such a way that the build\n        system needs to regenerate its rules.\"\"\"\n        pass\n\n    def _compute_env(self, env: Env | None = None) -> Env:\n        if env is None:\n            env = self.env\n        # CMAKE_PREFIX_PATH is only respected when passed through the\n        # environment, so we construct an appropriate path to pass down\n        return self.build_opts.compute_env_for_install_dirs(\n            self.loader,\n            self.dep_manifests,\n            self.ctx,\n            env=env,\n            manifest=self.manifest,\n        )\n\n    def get_dev_run_script_path(self) -> str:\n        assert self.build_opts.is_windows()\n        return os.path.join(self.build_dir, \"run.ps1\")\n\n    def get_dev_run_extra_path_dirs(\n        self, dep_munger: DepBase | None = None\n    ) -> list[str]:\n        assert self.build_opts.is_windows()\n        if dep_munger is None:\n            dep_munger = create_dyn_dep_munger(\n                self.build_opts, self._compute_env(), self.install_dirs\n            )\n        # pyre-fixme[16]: Optional type has no attribute `compute_dependency_paths`.\n        return dep_munger.compute_dependency_paths(self.build_dir)\n\n\nclass MakeBuilder(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n        build_args: list[str] | None,\n        install_args: list[str] | None,\n        test_args: list[str] | None,\n    ) -> None:\n        super(MakeBuilder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n        self.build_args: list[str] = build_args or []\n        self.install_args: list[str] = install_args or []\n        self.test_args: list[str] | None = test_args\n\n    @property\n    def _make_binary(self) -> str | None:\n        return self.manifest.get(\"build\", \"make_binary\", \"make\", ctx=self.ctx)\n\n    def _get_prefix(self) -> list[str]:\n        return [\"PREFIX=\" + self.inst_dir, \"prefix=\" + self.inst_dir]\n\n    def _build(self, reconfigure: bool) -> None:\n\n        env = self._compute_env()\n\n        # Need to ensure that PREFIX is set prior to install because\n        # libbpf uses it when generating its pkg-config file.\n        # The lowercase prefix is used by some projects.\n        cmd = (\n            [self._make_binary, \"-j%s\" % self.num_jobs]\n            + self.build_args\n            + self._get_prefix()\n        )\n        # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n        #  `List[Optional[str]]`.\n        self._check_cmd(cmd, env=env)\n\n        install_cmd = [self._make_binary] + self.install_args + self._get_prefix()\n        # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n        #  `List[Optional[str]]`.\n        self._check_cmd(install_cmd, env=env)\n\n        # bz2's Makefile doesn't install its .so properly\n        if self.manifest and self.manifest.name == \"bz2\":\n            libdir = os.path.join(self.inst_dir, \"lib\")\n            srcpattern = os.path.join(self.src_dir, \"lib*.so.*\")\n            print(f\"copying to {libdir} from {srcpattern}\")\n            for file in glob.glob(srcpattern):\n                shutil.copy(file, libdir)\n\n    def run_tests(\n        self,\n        schedule_type: str,\n        owner: str | None,\n        test_filter: str | None,\n        test_exclude: str | None,\n        retry: int,\n        no_testpilot: bool,\n        timeout: int | None = None,\n    ) -> None:\n        if not self.test_args:\n            return\n\n        env = self._compute_env()\n        if test_filter:\n            env[\"GETDEPS_TEST_FILTER\"] = test_filter\n        else:\n            env[\"GETDEPS_TEST_FILTER\"] = \"\"\n\n        if retry:\n            # pyre-fixme[6]: Expected `str` but got `int`.\n            env[\"GETDEPS_TEST_RETRY\"] = retry\n        else:\n            # pyre-fixme[6]: Expected `str` but got `int`.\n            env[\"GETDEPS_TEST_RETRY\"] = 0\n\n        if timeout is not None:\n            env[\"GETDEPS_TEST_TIMEOUT\"] = str(timeout)\n\n        cmd = (\n            [self._make_binary, \"-j%s\" % self.num_jobs]\n            # pyre-fixme[58]: `+` is not supported for operand types\n            #  `list[Optional[str]]` and `Optional[list[str]]`.\n            + self.test_args\n            + self._get_prefix()\n        )\n        # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n        #  `List[Optional[str]]`.\n        self._check_cmd(cmd, allow_fail=False, env=env)\n\n\nclass CMakeBootStrapBuilder(MakeBuilder):\n    def _build(self, reconfigure: bool) -> None:\n        self._check_cmd(\n            [\n                \"./bootstrap\",\n                \"--prefix=\" + self.inst_dir,\n                f\"--parallel={self.num_jobs}\",\n            ]\n        )\n        super(CMakeBootStrapBuilder, self)._build(reconfigure)\n\n\nclass AutoconfBuilder(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n        args: list[str] | None,\n        conf_env_args: dict[str, list[str]] | None,\n    ) -> None:\n        super(AutoconfBuilder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n        self.args: list[str] = args or []\n        if (\n            not build_opts.shared_libs\n            and \"--disable-shared\" not in self.args\n            and \"--enable-shared\" not in self.args\n        ):\n            self.args.append(\"--disable-shared\")\n        self.conf_env_args: dict[str, list[str]] = conf_env_args or {}\n\n    @property\n    def _make_binary(self) -> str | None:\n        return self.manifest.get(\"build\", \"make_binary\", \"make\", ctx=self.ctx)\n\n    def _build(self, reconfigure: bool) -> None:\n        configure_path = os.path.join(self.src_dir, \"configure\")\n        autogen_path = os.path.join(self.src_dir, \"autogen.sh\")\n\n        env = self._compute_env()\n\n        # Some configure scripts need additional env values passed derived from cmds\n        for k, cmd_args in self.conf_env_args.items():\n            out = (\n                subprocess.check_output(cmd_args, env=dict(env.items()))\n                .decode(\"utf-8\")\n                .strip()\n            )\n            if out:\n                env.set(k, out)\n\n        if not os.path.exists(configure_path):\n            print(\"%s doesn't exist, so reconfiguring\" % configure_path)\n            # This libtoolize call is a bit gross; the issue is that\n            # `autoreconf` as invoked by libsodium's `autogen.sh` doesn't\n            # seem to realize that it should invoke libtoolize and then\n            # error out when the configure script references a libtool\n            # related symbol.\n            self._check_cmd([\"libtoolize\"], cwd=self.src_dir, env=env)\n\n            # We generally prefer to call the `autogen.sh` script provided\n            # by the project on the basis that it may know more than plain\n            # autoreconf does.\n            if os.path.exists(autogen_path):\n                self._check_cmd([\"bash\", autogen_path], cwd=self.src_dir, env=env)\n            else:\n                self._check_cmd([\"autoreconf\", \"-ivf\"], cwd=self.src_dir, env=env)\n        configure_cmd = [configure_path, \"--prefix=\" + self.inst_dir] + self.args\n        self._check_cmd(configure_cmd, env=env)\n        only_install = self.manifest.get(\"build\", \"only_install\", ctx=self.ctx)\n        if not only_install or only_install.lower() == \"false\":\n            # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n            #  `List[Optional[str]]`.\n            self._check_cmd([self._make_binary, \"-j%s\" % self.num_jobs], env=env)\n        # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n        #  `List[Union[str, None, str]]`.\n        self._check_cmd([self._make_binary, \"install\"], env=env)\n\n\nclass Iproute2Builder(BuilderBase):\n    # ./configure --prefix does not work for iproute2.\n    # Thus, explicitly copy sources from src_dir to build_dir, build,\n    # and then install to inst_dir using DESTDIR\n    # lastly, also copy include from build_dir to inst_dir\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n    ) -> None:\n        super(Iproute2Builder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n\n    def _build(self, reconfigure: bool) -> None:\n        configure_path = os.path.join(self.src_dir, \"configure\")\n        env = self.env.copy()\n        self._check_cmd([configure_path], env=env)\n        shutil.rmtree(self.build_dir)\n        shutil.copytree(self.src_dir, self.build_dir)\n        self._check_cmd([\"make\", \"-j%s\" % self.num_jobs], env=env)\n        install_cmd = [\"make\", \"install\", \"DESTDIR=\" + self.inst_dir]\n\n        for d in [\"include\", \"lib\"]:\n            if not os.path.isdir(os.path.join(self.inst_dir, d)):\n                shutil.copytree(\n                    os.path.join(self.build_dir, d), os.path.join(self.inst_dir, d)\n                )\n\n        self._check_cmd(install_cmd, env=env)\n\n\nclass MesonBuilder(BuilderBase):\n    # MesonBuilder assumes that meson build tool has already been installed on\n    # the machine.\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n    ) -> None:\n        super(MesonBuilder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n\n    def _build(self, reconfigure: bool) -> None:\n        env = self._compute_env()\n        # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.\n        meson: str | None = path_search(env, \"meson\")\n        if meson is None:\n            raise Exception(\"Failed to find Meson\")\n\n        setup_args = self.manifest.get_section_as_args(\"meson.setup_args\", self.ctx)\n\n        # Meson builds typically require setup, compile, and install steps.\n        # During this setup step we ensure that the static library is built and\n        # the prefix is empty.\n        self._check_cmd(\n            [\n                meson,\n                \"setup\",\n            ]\n            + setup_args\n            + [\n                self.build_dir,\n                self.src_dir,\n            ]\n        )\n\n        # Compile step needs to satisfy the build directory that was previously\n        # prepared during setup.\n        self._check_cmd([meson, \"compile\", \"-C\", self.build_dir])\n\n        # Install step\n        self._check_cmd(\n            [meson, \"install\", \"-C\", self.build_dir, \"--destdir\", self.inst_dir]\n        )\n\n\nclass CMakeBuilder(BuilderBase):\n    MANUAL_BUILD_SCRIPT = \"\"\"\\\n#!{sys.executable}\n\n\nimport argparse\nimport subprocess\nimport sys\n\nCMAKE = {cmake!r}\nCTEST = {ctest!r}\nSRC_DIR = {src_dir!r}\nBUILD_DIR = {build_dir!r}\nINSTALL_DIR = {install_dir!r}\nCMD_PREFIX = {cmd_prefix!r}\nCMAKE_ENV = {env_str}\nCMAKE_DEFINE_ARGS = {define_args_str}\n\n\ndef get_jobs_argument(num_jobs_arg: int) -> str:\n    if num_jobs_arg > 0:\n        return \"-j\" + str(num_jobs_arg)\n\n    import multiprocessing\n    num_jobs = multiprocessing.cpu_count() // 2\n    return \"-j\" + str(num_jobs)\n\n\ndef main():\n    ap = argparse.ArgumentParser()\n    ap.add_argument(\n      \"cmake_args\",\n      nargs=argparse.REMAINDER,\n      help='Any extra arguments after an \"--\" argument will be passed '\n      \"directly to CMake.\"\n    )\n    ap.add_argument(\n      \"--mode\",\n      choices=[\"configure\", \"build\", \"install\", \"test\"],\n      default=\"configure\",\n      help=\"The mode to run: configure, build, or install.  \"\n      \"Defaults to configure\",\n    )\n    ap.add_argument(\n      \"--build\",\n      action=\"store_const\",\n      const=\"build\",\n      dest=\"mode\",\n      help=\"An alias for --mode=build\",\n    )\n    ap.add_argument(\n      \"-j\",\n      \"--num-jobs\",\n      action=\"store\",\n      type=int,\n      default=0,\n      help=\"Run the build or tests with the specified number of parallel jobs\",\n    )\n    ap.add_argument(\n      \"--install\",\n      action=\"store_const\",\n      const=\"install\",\n      dest=\"mode\",\n      help=\"An alias for --mode=install\",\n    )\n    ap.add_argument(\n      \"--test\",\n      action=\"store_const\",\n      const=\"test\",\n      dest=\"mode\",\n      help=\"An alias for --mode=test\",\n    )\n    args = ap.parse_args()\n\n    # Strip off a leading \"--\" from the additional CMake arguments\n    if args.cmake_args and args.cmake_args[0] == \"--\":\n        args.cmake_args = args.cmake_args[1:]\n\n    env = CMAKE_ENV\n\n    if args.mode == \"configure\":\n        full_cmd = CMD_PREFIX + [CMAKE, SRC_DIR] + CMAKE_DEFINE_ARGS + args.cmake_args\n    elif args.mode in (\"build\", \"install\"):\n        target = \"all\" if args.mode == \"build\" else \"install\"\n        full_cmd = CMD_PREFIX + [\n                CMAKE,\n                \"--build\",\n                BUILD_DIR,\n                \"--target\",\n                target,\n                \"--config\",\n                \"{build_type}\",\n                get_jobs_argument(args.num_jobs),\n        ] + args.cmake_args\n    elif args.mode == \"test\":\n        full_cmd = CMD_PREFIX + [\n            {dev_run_script}CTEST,\n            \"--output-on-failure\",\n            get_jobs_argument(args.num_jobs),\n        ] + args.cmake_args\n    else:\n        ap.error(\"unknown invocation mode: %s\" % (args.mode,))\n\n    cmd_str = \" \".join(full_cmd)\n    print(\"Running: %r\" % (cmd_str,))\n    proc = subprocess.run(full_cmd, env=env, cwd=BUILD_DIR)\n    sys.exit(proc.returncode)\n\n\nif __name__ == \"__main__\":\n    main()\n\"\"\"\n\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n        defines: dict[str, str] | None,\n        final_install_prefix: str | None = None,\n        extra_cmake_defines: dict[str, str] | None = None,\n        cmake_targets: list[str] | None = None,\n    ) -> None:\n        super(CMakeBuilder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n            final_install_prefix=final_install_prefix,\n        )\n        self.defines: dict[str, str] = defines or {}\n        if extra_cmake_defines:\n            self.defines.update(extra_cmake_defines)\n        self.cmake_targets: list[str] = cmake_targets or [\"install\"]\n\n        if build_opts.is_windows():\n            try:\n                from .facebook.vcvarsall import extra_vc_cmake_defines\n            except ImportError:\n                pass\n            else:\n                self.defines.update(extra_vc_cmake_defines)\n\n        self.loader = loader\n        if build_opts.shared_libs:\n            self.defines[\"BUILD_SHARED_LIBS\"] = \"ON\"\n            self.defines[\"BOOST_LINK_STATIC\"] = \"OFF\"\n\n    def _invalidate_cache(self) -> None:\n        for name in [\n            \"CMakeCache.txt\",\n            \"CMakeFiles/CMakeError.log\",\n            \"CMakeFiles/CMakeOutput.log\",\n        ]:\n            name = os.path.join(self.build_dir, name)\n            if os.path.isdir(name):\n                shutil.rmtree(name)\n            elif os.path.exists(name):\n                os.unlink(name)\n\n    def _needs_reconfigure(self) -> bool:\n        for name in [\"CMakeCache.txt\", \"build.ninja\"]:\n            name = os.path.join(self.build_dir, name)\n            if not os.path.exists(name):\n                return True\n        return False\n\n    def _write_build_script(self, **kwargs: object) -> None:\n        # pyre-fixme[16]: `object` has no attribute `items`.\n        env_lines = [\"    {!r}: {!r},\".format(k, v) for k, v in kwargs[\"env\"].items()]\n        kwargs[\"env_str\"] = \"\\n\".join([\"{\"] + env_lines + [\"}\"])\n\n        if self.build_opts.is_windows():\n            kwargs[\"dev_run_script\"] = '\"powershell.exe\", {!r}, '.format(\n                self.get_dev_run_script_path()\n            )\n        else:\n            kwargs[\"dev_run_script\"] = \"\"\n\n        define_arg_lines = [\"[\"]\n        # pyre-fixme[16]: `object` has no attribute `__iter__`.\n        for arg in kwargs[\"define_args\"]:\n            # Replace the CMAKE_INSTALL_PREFIX argument to use the INSTALL_DIR\n            # variable that we define in the MANUAL_BUILD_SCRIPT code.\n            if arg.startswith(\"-DCMAKE_INSTALL_PREFIX=\"):\n                value = \"    {!r}.format(INSTALL_DIR),\".format(\n                    \"-DCMAKE_INSTALL_PREFIX={}\"\n                )\n            else:\n                value = \"    {!r},\".format(arg)\n            define_arg_lines.append(value)\n        define_arg_lines.append(\"]\")\n        kwargs[\"define_args_str\"] = \"\\n\".join(define_arg_lines)\n\n        # In order to make it easier for developers to manually run builds for\n        # CMake-based projects, write out some build scripts that can be used to invoke\n        # CMake manually.\n        build_script_path = os.path.join(self.build_dir, \"run_cmake.py\")\n        script_contents = self.MANUAL_BUILD_SCRIPT.format(**kwargs)\n        with open(build_script_path, \"wb\") as f:\n            f.write(script_contents.encode())\n        os.chmod(build_script_path, 0o755)\n\n    def _compute_cmake_define_args(self, env: Env) -> list[str]:\n        defines = {\n            \"CMAKE_INSTALL_PREFIX\": self.final_install_prefix or self.inst_dir,\n            \"BUILD_SHARED_LIBS\": \"OFF\",\n            # Some of the deps (rsocket) default to UBSAN enabled if left\n            # unspecified.  Some of the deps fail to compile in release mode\n            # due to warning->error promotion.  RelWithDebInfo is the happy\n            # medium.\n            \"CMAKE_BUILD_TYPE\": self.build_opts.build_type,\n        }\n\n        if \"SANDCASTLE\" not in os.environ:\n            # We sometimes see intermittent ccache related breakages on some\n            # of the FB internal CI hosts, so we prefer to disable ccache\n            # when running in that environment.\n            # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got\n            #  `Env`.\n            ccache = path_search(env, \"ccache\")\n            if ccache:\n                defines[\"CMAKE_CXX_COMPILER_LAUNCHER\"] = ccache\n        else:\n            # rocksdb does its own probing for ccache.\n            # Ensure that it is disabled on sandcastle\n            env[\"CCACHE_DISABLE\"] = \"1\"\n            # Some sandcastle hosts have broken ccache related dirs, and\n            # even though we've asked for it to be disabled ccache is\n            # still invoked by rocksdb's cmake.\n            # Redirect its config directory to somewhere that is guaranteed\n            # fresh to us, and that won't have any ccache data inside.\n            env[\"CCACHE_DIR\"] = f\"{self.build_opts.scratch_dir}/ccache\"\n\n        if \"GITHUB_ACTIONS\" in os.environ and self.build_opts.is_windows():\n            # GitHub actions: the host has both gcc and msvc installed, and\n            # the default behavior of cmake is to prefer gcc.\n            # Instruct cmake that we want it to use cl.exe; this is important\n            # because Boost prefers cl.exe and the mismatch results in cmake\n            # with gcc not being able to find boost built with cl.exe.\n            defines[\"CMAKE_C_COMPILER\"] = \"cl.exe\"\n            defines[\"CMAKE_CXX_COMPILER\"] = \"cl.exe\"\n\n        if self.build_opts.is_darwin():\n            # Try to persuade cmake to set the rpath to match the lib\n            # dirs of the dependencies.  This isn't automatic, and to\n            # make things more interesting, cmake uses `;` as the path\n            # separator, so translate the runtime path to something\n            # that cmake will parse\n            defines[\"CMAKE_INSTALL_RPATH\"] = \";\".join(\n                # pyre-fixme[16]: Optional type has no attribute `split`.\n                env.get(\"DYLD_LIBRARY_PATH\", \"\").split(\":\")\n            )\n            # Tell cmake that we want to set the rpath in the tree\n            # at build time.  Without this the rpath is only set\n            # at the moment that the binaries are installed.  That\n            # default is problematic for example when using the\n            # gtest integration in cmake which runs the built test\n            # executables during the build to discover the set of\n            # tests.\n            defines[\"CMAKE_BUILD_WITH_INSTALL_RPATH\"] = \"ON\"\n\n        defines.update(self.defines)\n        define_args = [\"-D%s=%s\" % (k, v) for (k, v) in defines.items()]\n\n        # if self.build_opts.is_windows():\n        #    define_args += [\"-G\", \"Visual Studio 15 2017 Win64\"]\n        define_args += [\"-G\", \"Ninja\"]\n\n        return define_args\n\n    def _run_include_rewriter(self) -> None:\n        \"\"\"Run include path rewriting on source files before building.\"\"\"\n        from .include_rewriter import rewrite_includes_from_manifest\n\n        print(f\"Rewriting include paths for {self.manifest.name}...\")\n        try:\n            modified_count = rewrite_includes_from_manifest(\n                self.manifest, self.ctx, self.src_dir, verbose=True\n            )\n            if modified_count > 0:\n                print(f\"Successfully modified {modified_count} files\")\n            else:\n                print(\"No files needed modification\")\n        except Exception as e:\n            print(f\"Warning: Include path rewriting failed: {e}\")\n            # Don't fail the build for include rewriting issues\n\n    def _build(self, reconfigure: bool) -> None:\n        # Check if include rewriting is enabled\n        rewrite_includes: str | None = self.manifest.get(\n            \"build\", \"rewrite_includes\", \"false\", ctx=self.ctx\n        )\n        # pyre-fixme[16]: Optional type has no attribute `lower`.\n        if rewrite_includes.lower() == \"true\":\n            self._run_include_rewriter()\n\n        reconfigure = reconfigure or self._needs_reconfigure()\n\n        env = self._compute_env()\n        if not self.build_opts.is_windows() and self.final_install_prefix:\n            env[\"DESTDIR\"] = self.inst_dir\n\n        # Resolve the cmake that we installed\n        # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.\n        cmake = path_search(env, \"cmake\")\n        if cmake is None:\n            raise Exception(\"Failed to find CMake\")\n\n        if self.build_opts.is_windows():\n            checkdir = self.src_dir\n            if os.path.exists(checkdir):\n                children = os.listdir(checkdir)\n                print(f\"Building from source {checkdir} contents: {children}\")\n            else:\n                print(f\"Source {checkdir} not found\")\n\n        if reconfigure:\n            define_args = self._compute_cmake_define_args(env)\n            self._write_build_script(\n                cmd_prefix=self._get_cmd_prefix(),\n                cmake=cmake,\n                # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but\n                #  got `Env`.\n                ctest=path_search(env, \"ctest\"),\n                env=env,\n                define_args=define_args,\n                src_dir=self.src_dir,\n                build_dir=self.build_dir,\n                install_dir=self.inst_dir,\n                sys=sys,\n                build_type=self.build_opts.build_type,\n            )\n\n            self._invalidate_cache()\n            self._check_cmd([cmake, self.src_dir] + define_args, env=env)\n\n        self._check_cmd(\n            # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n            #  `List[Optional[str]]`.\n            [cmake, \"--build\", self.build_dir, \"--target\"]\n            + self.cmake_targets\n            + [\n                \"--config\",\n                self.build_opts.build_type,\n                \"-j\",\n                str(self.num_jobs),\n            ],\n            env=env,\n            preexec_fn=self.memory_limit_preexec_fn,\n        )\n\n    def _build_targets(self, targets: Sequence[str]) -> None:\n        \"\"\"Build one or more cmake targets in parallel.\n\n        Args:\n            targets: Sequence of target names (strings) to build\n        \"\"\"\n        if not targets:\n            return\n\n        env = self._compute_env()\n        # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.\n        cmake = path_search(env, \"cmake\")\n        if cmake is None:\n            raise RuntimeError(\"unable to find cmake\")\n\n        # Build all targets in a single cmake invocation for better parallelism\n        cmd = [\n            cmake,\n            \"--build\",\n            self.build_dir,\n        ]\n\n        # Add all targets\n        for target in targets:\n            cmd.extend([\"--target\", target])\n\n        cmd.extend(\n            # pyre-fixme[6]: For 1st argument expected `Iterable[str]` but got\n            #  `Iterable[Union[str, str, None, str]]`.\n            [\n                \"--config\",\n                self.build_opts.build_type,\n                \"-j\",\n                str(self.num_jobs),\n            ]\n        )\n\n        self._check_cmd(cmd, env=env, preexec_fn=self.memory_limit_preexec_fn)\n\n    def _get_missing_test_executables(\n        self, test_filter: str | None, env: Env, ctest: str | None\n    ) -> set[str]:\n        \"\"\"Discover which test executables are missing for the given filter.\n        Returns a set of missing executable basenames (without path).\"\"\"\n        if ctest is None:\n            return set()\n\n        # Run ctest -N (show tests without running) with the filter to see which tests match\n        cmd = [ctest, \"-N\"]\n        if test_filter:\n            cmd += [\"-R\", test_filter]\n\n        try:\n            output = subprocess.check_output(\n                cmd,\n                env=dict(env.items()),\n                cwd=self.build_dir,\n                stderr=subprocess.STDOUT,\n                text=True,\n            )\n        except subprocess.CalledProcessError as e:\n            # If ctest fails, it might be because executables don't exist yet\n            # Parse the error output to find the missing executables\n            output = e.output\n\n        # Parse output to find missing executable paths\n        # Look for lines like \"Could not find executable /path/to/test_binary\"\n        missing_executables = set()\n        for line in output.split(\"\\n\"):\n            match = re.search(r\"Could not find executable (.+)\", line)\n            if match:\n                exe_path = match.group(1)\n                exe_name = os.path.basename(exe_path)\n                missing_executables.add(exe_name)\n\n        return missing_executables\n\n    def run_tests(\n        self,\n        schedule_type: str,\n        owner: str | None,\n        test_filter: str | None,\n        test_exclude: str | None,\n        retry: int,\n        no_testpilot: bool,\n        timeout: int | None = None,\n    ) -> None:\n        env = self._compute_env()\n        # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.\n        ctest: str | None = path_search(env, \"ctest\")\n        # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.\n        cmake = path_search(env, \"cmake\")\n\n        # Build only the missing test executables needed for the given filter.\n        # This is especially important for LocalDirFetcher projects (like fboss)\n        # where the build marker gets removed when building specific cmake targets.\n        missing_test_executables = self._get_missing_test_executables(\n            test_filter, env, ctest\n        )\n        if missing_test_executables:\n            sorted_executables = sorted(missing_test_executables)\n            print(f\"Building missing test executables: {', '.join(sorted_executables)}\")\n            # Build all missing executables in one cmake invocation for better parallelism\n            self._build_targets(sorted_executables)\n\n        def require_command(path: str | None, name: str) -> str:\n            if path is None:\n                raise RuntimeError(\"unable to find command `{}`\".format(name))\n            return path\n\n        # On Windows, we also need to update $PATH to include the directories that\n        # contain runtime library dependencies.  This is not needed on other platforms\n        # since CMake will emit RPATH properly in the binary so they can find these\n        # dependencies.\n        if self.build_opts.is_windows():\n            path_entries = self.get_dev_run_extra_path_dirs()\n            path = env.get(\"PATH\")\n            if path:\n                path_entries.insert(0, path)\n            env[\"PATH\"] = \";\".join(path_entries)\n\n        # Don't use the cmd_prefix when running tests.  This is vcvarsall.bat on\n        # Windows.  vcvarsall.bat is only needed for the build, not tests.  It\n        # unfortunately fails if invoked with a long PATH environment variable when\n        # running the tests.\n        use_cmd_prefix = False\n\n        def get_property(\n            test: dict[str, object], propname: str, defval: object = None\n        ) -> object:\n            \"\"\"extracts a named property from a cmake test info json blob.\n            The properties look like:\n            [{\"name\": \"WORKING_DIRECTORY\"},\n             {\"value\": \"something\"}]\n            We assume that it is invalid for the same named property to be\n            listed more than once.\n            \"\"\"\n            props = test.get(\"properties\", [])\n            # pyre-fixme[16]: `object` has no attribute `__iter__`.\n            for p in props:\n                if p.get(\"name\", None) == propname:\n                    return p.get(\"value\", defval)\n            return defval\n\n        # pyre-fixme[53]: Captured variable `cmake` is not annotated.\n        # pyre-fixme[53]: Captured variable `env` is not annotated.\n        def list_tests() -> list[dict[str, object]]:\n            output = subprocess.check_output(\n                [require_command(ctest, \"ctest\"), \"--show-only=json-v1\"],\n                env=env,\n                cwd=self.build_dir,\n            )\n            try:\n                data = json.loads(output.decode(\"utf-8\"))\n            except ValueError as exc:\n                raise Exception(\n                    \"Failed to decode cmake test info using %s: %s.  Output was: %r\"\n                    % (ctest, str(exc), output)\n                )\n\n            tests = []\n            machine_suffix = self.build_opts.host_type.as_tuple_string()\n            for test in data[\"tests\"]:\n                working_dir = get_property(test, \"WORKING_DIRECTORY\")\n                labels = []\n                machine_suffix = self.build_opts.host_type.as_tuple_string()\n                labels.append(\"tpx-fb-test-type=3\")\n                labels.append(\"tpx_test_config::buildsystem=getdeps\")\n                labels.append(\"tpx_test_config::platform={}\".format(machine_suffix))\n\n                if get_property(test, \"DISABLED\"):\n                    labels.append(\"disabled\")\n                command = test[\"command\"]\n                if working_dir:\n                    command = [\n                        require_command(cmake, \"cmake\"),\n                        \"-E\",\n                        \"chdir\",\n                        working_dir,\n                    ] + command\n\n                tests.append(\n                    {\n                        \"type\": \"custom\",\n                        \"target\": \"%s-%s-getdeps-%s\"\n                        % (self.manifest.name, test[\"name\"], machine_suffix),\n                        \"command\": command,\n                        \"labels\": labels,\n                        \"env\": {},\n                        \"required_paths\": [],\n                        \"contacts\": [],\n                        \"cwd\": os.getcwd(),\n                    }\n                )\n            return tests\n\n        discover_like_continuous = False\n        if schedule_type == \"continuous\" or (\n            schedule_type == \"base_retry\" and is_public_commit(self.build_opts)\n        ):\n            discover_like_continuous = True\n\n        if discover_like_continuous or schedule_type == \"testwarden\":\n            # for continuous and testwarden runs, disabling retry can give up\n            # better signals for flaky tests.\n            retry = 0\n\n        tpx = None\n        try:\n            from .facebook.testinfra import start_run\n\n            # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got\n            #  `Env`.\n            tpx = path_search(env, \"tpx\")\n        except ImportError:\n            # internal testinfra not available\n            pass\n\n        if tpx and not no_testpilot:\n            import os\n\n            buck_test_info = list_tests()\n\n            buck_test_info_name = os.path.join(self.build_dir, \".buck-test-info.json\")\n            with open(buck_test_info_name, \"w\") as f:\n                json.dump(buck_test_info, f)\n\n            env.set(\"http_proxy\", \"\")\n            env.set(\"https_proxy\", \"\")\n            runs = []\n\n            with start_run(env[\"FBSOURCE_HASH\"]) as run_id:\n                testpilot_args = [\n                    tpx,\n                    \"--force-local-execution\",\n                    \"--buck-test-info\",\n                    buck_test_info_name,\n                    \"--retry=%d\" % retry,\n                    \"-j=%s\" % str(self.num_jobs),\n                    \"--print-long-results\",\n                ]\n\n                if owner:\n                    testpilot_args += [\"--contacts\", owner]\n\n                if env:\n                    testpilot_args.append(\"--env\")\n                    testpilot_args.extend(f\"{key}={val}\" for key, val in env.items())\n\n                if run_id is not None:\n                    testpilot_args += [\"--run-id\", run_id]\n\n                if timeout is not None:\n                    testpilot_args += [\"--timeout\", str(timeout)]\n\n                if test_filter:\n                    testpilot_args += [\"--\", test_filter]\n\n                if schedule_type == \"diff\":\n                    runs.append([\"--collection\", \"oss-diff\", \"--purpose\", \"diff\"])\n                elif discover_like_continuous:\n                    runs.append(\n                        [\n                            \"--tag-new-tests\",\n                            \"--collection\",\n                            \"oss-continuous\",\n                            \"--purpose\",\n                            \"continuous\",\n                        ]\n                    )\n                elif schedule_type == \"testwarden\":\n                    # One run to assess new tests\n                    runs.append(\n                        [\n                            \"--tag-new-tests\",\n                            \"--collection\",\n                            \"oss-new-test-stress\",\n                            \"--stress-runs\",\n                            \"10\",\n                            \"--purpose\",\n                            \"stress-run-new-test\",\n                        ]\n                    )\n                    # And another for existing tests\n                    runs.append(\n                        [\n                            \"--tag-new-tests\",\n                            \"--collection\",\n                            \"oss-existing-test-stress\",\n                            \"--stress-runs\",\n                            \"10\",\n                            \"--purpose\",\n                            \"stress-run\",\n                        ]\n                    )\n                else:\n                    runs.append([])\n\n                for run in runs:\n                    # FIXME: What is this trying to accomplish? Should it fail on first or >=1 errors?\n                    self._run_cmd(\n                        testpilot_args + run,\n                        cwd=self.build_opts.fbcode_builder_dir,\n                        env=env,\n                        use_cmd_prefix=use_cmd_prefix,\n                    )\n        else:\n            args = [\n                require_command(ctest, \"ctest\"),\n                \"--output-on-failure\",\n                \"-j\",\n                str(self.num_jobs),\n            ]\n            if test_filter:\n                args += [\"-R\", test_filter]\n            if test_exclude:\n                args += [\"--exclude-regex\", test_exclude]\n            if timeout is not None:\n                args += [\"--timeout\", str(timeout)]\n\n            count: int = 0\n            retcode: int | None = -1\n            while count <= retry:\n                # FIXME: What is this trying to accomplish? Should it fail on first or >=1 errors?\n                retcode = self._check_cmd(\n                    args, env=env, use_cmd_prefix=use_cmd_prefix, allow_fail=True\n                )\n\n                if retcode == 0:\n                    break\n                if count == 0:\n                    # Only add this option in the second run.\n                    args += [\"--rerun-failed\"]\n                count += 1\n            if retcode is not None and retcode != 0:\n                # Allow except clause in getdeps.main to catch and exit gracefully\n                # This allows non-testpilot runs to fail through the same logic as failed testpilot runs, which may become handy in case if post test processing is needed in the future\n                raise subprocess.CalledProcessError(retcode, args)\n\n\nclass NinjaBootstrap(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        build_dir: str,\n        src_dir: str,\n        inst_dir: str,\n    ) -> None:\n        super(NinjaBootstrap, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n\n    def _build(self, reconfigure: bool) -> None:\n        self._check_cmd(\n            [sys.executable, \"configure.py\", \"--bootstrap\"], cwd=self.src_dir\n        )\n        src_ninja = os.path.join(self.src_dir, \"ninja\")\n        dest_ninja = os.path.join(self.inst_dir, \"bin/ninja\")\n        bin_dir = os.path.dirname(dest_ninja)\n        if not os.path.exists(bin_dir):\n            os.makedirs(bin_dir)\n        shutil.copyfile(src_ninja, dest_ninja)\n        shutil.copymode(src_ninja, dest_ninja)\n\n\nclass OpenSSLBuilder(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        build_dir: str,\n        src_dir: str,\n        inst_dir: str,\n    ) -> None:\n        super(OpenSSLBuilder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n\n    def _build(self, reconfigure: bool) -> None:\n        configure = os.path.join(self.src_dir, \"Configure\")\n\n        # prefer to resolve the perl that we installed from\n        # our manifest on windows, but fall back to the system\n        # path on eg: darwin\n        env = self.env.copy()\n        for m in self.dep_manifests:\n            bindir = os.path.join(self.loader.get_project_install_dir(m), \"bin\")\n            add_path_entry(env, \"PATH\", bindir, append=False)\n\n        # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.\n        perl = typing.cast(str, path_search(env, \"perl\", \"perl\"))\n\n        make_j_args = []\n        extra_args = []\n        if self.build_opts.is_windows():\n            # jom is compatible with nmake, adds the /j argument for parallel build\n            make = \"jom.exe\"\n            make_j_args = [\"/j%s\" % self.num_jobs]\n            args = [\"VC-WIN64A-masm\", \"-utf-8\"]\n            # fixes \"if multiple CL.EXE write to the same .PDB file, please use /FS\"\n            extra_args = [\"/FS\"]\n        elif self.build_opts.is_darwin():\n            make = \"make\"\n            make_j_args = [\"-j%s\" % self.num_jobs]\n            args = (\n                [\"darwin64-x86_64-cc\"]\n                if not self.build_opts.is_arm()\n                else [\"darwin64-arm64-cc\"]\n            )\n        elif self.build_opts.is_linux():\n            make = \"make\"\n            make_j_args = [\"-j%s\" % self.num_jobs]\n            args = (\n                [\"linux-x86_64\"] if not self.build_opts.is_arm() else [\"linux-aarch64\"]\n            )\n        else:\n            raise Exception(\"don't know how to build openssl for %r\" % self.ctx)\n\n        self._check_cmd(\n            [\n                perl,\n                configure,\n                \"--prefix=%s\" % self.inst_dir,\n                \"--openssldir=%s\" % self.inst_dir,\n            ]\n            + args\n            + [\n                \"enable-static-engine\",\n                \"enable-capieng\",\n                \"no-makedepend\",\n                \"no-unit-test\",\n                \"no-tests\",\n            ]\n            + extra_args\n        )\n        # show the config produced\n        self._check_cmd([perl, \"configdata.pm\", \"--dump\"], env=env)\n        make_build = [make] + make_j_args\n        self._check_cmd(make_build, env=env)\n        make_install = [make, \"install_sw\", \"install_ssldirs\"]\n        self._check_cmd(make_install, env=env)\n\n\nclass Boost(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n        b2_args: list[str],\n    ) -> None:\n        children = os.listdir(src_dir)\n        assert len(children) == 1, \"expected a single directory entry: %r\" % (children,)\n        boost_src = children[0]\n        assert boost_src.startswith(\"boost\")\n        src_dir = os.path.join(src_dir, children[0])\n        super(Boost, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n        self.b2_args: list[str] = b2_args\n\n    def _build(self, reconfigure: bool) -> None:\n        env = self._compute_env()\n        linkage: list[str] = [\"static\"]\n        if self.build_opts.is_windows() or self.build_opts.shared_libs:\n            linkage.append(\"shared\")\n\n        args = []\n        if self.build_opts.is_darwin():\n            clang = subprocess.check_output([\"xcrun\", \"--find\", \"clang\"])\n            user_config = os.path.join(self.build_dir, \"project-config.jam\")\n            with open(user_config, \"w\") as jamfile:\n                jamfile.write(\"using clang : : %s ;\\n\" % clang.decode().strip())\n            args.append(\"--user-config=%s\" % user_config)\n\n        for link in linkage:\n            bootstrap_args = self.manifest.get_section_as_args(\n                \"bootstrap.args\", self.ctx\n            )\n            if self.build_opts.is_windows():\n                bootstrap = os.path.join(self.src_dir, \"bootstrap.bat\")\n                self._check_cmd([bootstrap] + bootstrap_args, cwd=self.src_dir, env=env)\n                args += [\"address-model=64\"]\n            else:\n                bootstrap = os.path.join(self.src_dir, \"bootstrap.sh\")\n                self._check_cmd(\n                    [bootstrap, \"--prefix=%s\" % self.inst_dir] + bootstrap_args,\n                    cwd=self.src_dir,\n                    env=env,\n                )\n\n            b2 = os.path.join(self.src_dir, \"b2\")\n            self._check_cmd(\n                [\n                    b2,\n                    \"-j%s\" % self.num_jobs,\n                    \"--prefix=%s\" % self.inst_dir,\n                    \"--builddir=%s\" % self.build_dir,\n                ]\n                + args\n                + self.b2_args\n                + [\n                    \"link=%s\" % link,\n                    \"runtime-link=shared\",\n                    \"variant=release\",\n                    \"threading=multi\",\n                    \"debug-symbols=on\",\n                    \"visibility=global\",\n                    \"-d2\",\n                    \"install\",\n                ],\n                cwd=self.src_dir,\n                env=env,\n            )\n\n\nclass NopBuilder(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        inst_dir: str,\n    ) -> None:\n        super(NopBuilder, self).__init__(\n            loader, dep_manifests, build_opts, ctx, manifest, src_dir, None, inst_dir\n        )\n\n    def build(self, reconfigure: bool) -> None:\n        print(\"Installing %s -> %s\" % (self.src_dir, self.inst_dir))\n        parent = os.path.dirname(self.inst_dir)\n        if not os.path.exists(parent):\n            os.makedirs(parent)\n\n        install_files = self.manifest.get_section_as_ordered_pairs(\n            \"install.files\", self.ctx\n        )\n        if install_files:\n            for src_name, dest_name in self.manifest.get_section_as_ordered_pairs(\n                \"install.files\", self.ctx\n            ):\n                # pyre-fixme[6]: For 2nd argument expected `Union[PathLike[str],\n                #  str]` but got `Optional[str]`.\n                full_dest = os.path.join(self.inst_dir, dest_name)\n                full_src = os.path.join(self.src_dir, src_name)\n\n                dest_parent = os.path.dirname(full_dest)\n                if not os.path.exists(dest_parent):\n                    os.makedirs(dest_parent)\n                if os.path.isdir(full_src):\n                    if not os.path.exists(full_dest):\n                        simple_copytree(full_src, full_dest)\n                else:\n                    shutil.copyfile(full_src, full_dest)\n                    shutil.copymode(full_src, full_dest)\n                    # This is a bit gross, but the mac ninja.zip doesn't\n                    # give ninja execute permissions, so force them on\n                    # for things that look like they live in a bin dir\n                    # pyre-fixme[6]: For 1st argument expected `PathLike[AnyStr]`\n                    #  but got `Optional[str]`.\n                    if os.path.dirname(dest_name) == \"bin\":\n                        st = os.lstat(full_dest)\n                        os.chmod(full_dest, st.st_mode | stat.S_IXUSR)\n        else:\n            if not os.path.exists(self.inst_dir):\n                simple_copytree(self.src_dir, self.inst_dir)\n\n\nclass SetupPyBuilder(BuilderBase):\n    def _build(self, reconfigure: bool) -> None:\n        env = self._compute_env()\n\n        setup_env = self.manifest.get_section_as_dict(\"setup-py.env\", self.ctx)\n        for key, value in setup_env.items():\n            # pyre-fixme[6]: For 2nd argument expected `str` but got `Optional[str]`.\n            env[key] = value\n\n        setup_py_path = os.path.join(self.src_dir, \"setup.py\")\n\n        if not os.path.exists(setup_py_path):\n            raise RuntimeError(f\"setup.py script not found at {setup_py_path}\")\n\n        self._check_cmd(\n            # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n            #  `List[Union[str, None, str]]`.\n            # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got\n            #  `Env`.\n            [path_search(env, \"python3\"), setup_py_path, \"install\"],\n            cwd=self.src_dir,\n            env=env,\n        )\n\n        # Create the installation directory if it doesn't exist\n        os.makedirs(self.inst_dir, exist_ok=True)\n\n        # Mark the project as built\n        with open(os.path.join(self.inst_dir, \".built-by-getdeps\"), \"w\") as f:\n            f.write(\"built\")\n\n    def run_tests(\n        self,\n        schedule_type: str,\n        owner: str | None,\n        test_filter: str | None,\n        test_exclude: str | None,\n        retry: int,\n        no_testpilot: bool,\n        timeout: int | None = None,\n    ) -> None:\n        # setup.py actually no longer has a standard command for running tests.\n        # Instead we let manifest files specify an arbitrary Python file to run\n        # as a test.\n\n        # Get the test command from the manifest\n        python_script = self.manifest.get(\n            \"setup-py.test\", \"python_script\", ctx=self.ctx\n        )\n        if not python_script:\n            print(f\"No test script specified for {self.manifest.name}\")\n            return\n\n        # Run the command\n        env = self._compute_env()\n        self._check_cmd([\"python3\", python_script], cwd=self.src_dir, env=env)\n\n\nclass SqliteBuilder(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n    ) -> None:\n        super(SqliteBuilder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n\n    def _build(self, reconfigure: bool) -> None:\n        for f in [\"sqlite3.c\", \"sqlite3.h\", \"sqlite3ext.h\"]:\n            src = os.path.join(self.src_dir, f)\n            dest = os.path.join(self.build_dir, f)\n            copy_if_different(src, dest)\n\n        cmake_lists = \"\"\"\ncmake_minimum_required(VERSION 3.5 FATAL_ERROR)\nproject(sqlite3 C)\nadd_library(sqlite3 STATIC sqlite3.c)\n# These options are taken from the defaults in Makefile.msc in\n# the sqlite distribution\ntarget_compile_definitions(sqlite3 PRIVATE\n    -DSQLITE_ENABLE_COLUMN_METADATA=1\n    -DSQLITE_ENABLE_FTS3=1\n    -DSQLITE_ENABLE_RTREE=1\n    -DSQLITE_ENABLE_GEOPOLY=1\n    -DSQLITE_ENABLE_JSON1=1\n    -DSQLITE_ENABLE_STMTVTAB=1\n    -DSQLITE_ENABLE_DBPAGE_VTAB=1\n    -DSQLITE_ENABLE_DBSTAT_VTAB=1\n    -DSQLITE_INTROSPECTION_PRAGMAS=1\n    -DSQLITE_ENABLE_DESERIALIZE=1\n)\ninstall(TARGETS sqlite3)\ninstall(FILES sqlite3.h sqlite3ext.h DESTINATION include)\n            \"\"\"\n\n        with open(os.path.join(self.build_dir, \"CMakeLists.txt\"), \"w\") as f:\n            f.write(cmake_lists)\n\n        defines = {\n            \"CMAKE_INSTALL_PREFIX\": self.inst_dir,\n            \"BUILD_SHARED_LIBS\": \"ON\" if self.build_opts.shared_libs else \"OFF\",\n            \"CMAKE_BUILD_TYPE\": \"RelWithDebInfo\",\n        }\n        define_args = [\"-D%s=%s\" % (k, v) for (k, v) in defines.items()]\n        define_args += [\"-G\", \"Ninja\"]\n\n        env = self._compute_env()\n\n        # Resolve the cmake that we installed\n        # pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.\n        cmake = path_search(env, \"cmake\")\n\n        # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n        #  `List[Optional[str]]`.\n        self._check_cmd([cmake, self.build_dir] + define_args, env=env)\n        self._check_cmd(\n            # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n            #  `List[Union[str, str, str, str, str, None, str]]`.\n            [\n                cmake,\n                \"--build\",\n                self.build_dir,\n                \"--target\",\n                \"install\",\n                \"--config\",\n                self.build_opts.build_type,\n                \"-j\",\n                str(self.num_jobs),\n            ],\n            env=env,\n        )\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/buildopts.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\nfrom __future__ import annotations\n\nimport argparse\nimport errno\nimport glob\nimport ntpath\nimport os\nimport subprocess\nimport sys\nimport tempfile\nimport typing\nfrom collections.abc import Mapping\n\nfrom .copytree import containing_repo_type\nfrom .envfuncs import add_flag, add_path_entry, Env\nfrom .fetcher import get_fbsource_repo_data, homebrew_package_prefix\nfrom .manifest import ContextGenerator\nfrom .platform import get_available_ram, HostType, is_windows\n\nif typing.TYPE_CHECKING:\n    from .load import ManifestLoader\n    from .manifest import ManifestContext, ManifestParser\n\n\nGITBASH_TMP: str = \"c:\\\\tools\\\\fb.gitbash\\\\tmp\"\n\n\ndef detect_project(path: str) -> tuple[str | None, str | None]:\n    repo_type, repo_root = containing_repo_type(path)\n    if repo_type is None:\n        return None, None\n\n    # Look for a .projectid file.  If it exists, read the project name from it.\n    # pyre-fixme[6]: For 1st argument expected `LiteralString` but got `Optional[str]`.\n    project_id_path = os.path.join(repo_root, \".projectid\")\n    try:\n        with open(project_id_path, \"r\") as f:\n            project_name = f.read().strip()\n            return repo_root, project_name\n    except EnvironmentError as ex:\n        if ex.errno != errno.ENOENT:\n            raise\n\n    return repo_root, None\n\n\nclass BuildOptions:\n    def __init__(\n        self,\n        fbcode_builder_dir: str,\n        scratch_dir: str,\n        host_type: HostType,\n        install_dir: str | None = None,\n        num_jobs: int = 0,\n        use_shipit: bool = False,\n        vcvars_path: str | None = None,\n        allow_system_packages: bool = False,\n        lfs_path: str | None = None,\n        shared_libs: bool = False,\n        facebook_internal: bool | None = None,\n        free_up_disk: bool = False,\n        build_type: str | None = None,\n    ) -> None:\n        \"\"\"fbcode_builder_dir - the path to either the in-fbsource fbcode_builder dir,\n                             or for shipit-transformed repos, the build dir that\n                             has been mapped into that dir.\n        scratch_dir - a place where we can store repos and build bits.\n                      This path should be stable across runs and ideally\n                      should not be in the repo of the project being built,\n                      but that is ultimately where we generally fall back\n                      for builds outside of FB\n        install_dir - where the project will ultimately be installed\n        num_jobs - the level of concurrency to use while building\n        use_shipit - use real shipit instead of the simple shipit transformer\n        vcvars_path - Path to external VS toolchain's vsvarsall.bat\n        shared_libs - whether to build shared libraries\n        free_up_disk - take extra actions to save runner disk space\n        build_type - CMAKE_BUILD_TYPE, used by cmake and cargo builders\n        \"\"\"\n\n        if not install_dir:\n            install_dir = os.path.join(scratch_dir, \"installed\")\n\n        self.project_hashes: str | None = None\n        for p in [\"../deps/github_hashes\", \"../project_hashes\"]:\n            hashes = os.path.join(fbcode_builder_dir, p)\n            if os.path.exists(hashes):\n                self.project_hashes = hashes\n                break\n\n        # Detect what repository and project we are being run from.\n        # pyre-fixme[4]: Attribute must be annotated.\n        self.repo_root, self.repo_project = detect_project(os.getcwd())\n\n        # If we are running from an fbsource repository, set self.fbsource_dir\n        # to allow the ShipIt-based fetchers to use it.\n        if self.repo_project == \"fbsource\":\n            self.fbsource_dir: str | None = self.repo_root\n        else:\n            self.fbsource_dir = None\n\n        if facebook_internal is None:\n            if self.fbsource_dir:\n                facebook_internal = True\n            else:\n                facebook_internal = False\n\n        self.facebook_internal: bool = facebook_internal\n        self.specified_num_jobs: int = num_jobs\n        self.scratch_dir: str = scratch_dir\n        self.install_dir: str = install_dir\n        self.fbcode_builder_dir: str = fbcode_builder_dir\n        self.host_type: HostType = host_type\n        self.use_shipit: bool = use_shipit\n        self.allow_system_packages: bool = allow_system_packages\n        self.lfs_path: str | None = lfs_path\n        self.shared_libs: bool = shared_libs\n        self.free_up_disk: bool = free_up_disk\n        self.build_type: str | None = build_type\n\n        lib_path: str | None = None\n        if self.is_darwin():\n            lib_path = \"DYLD_LIBRARY_PATH\"\n        elif self.is_linux():\n            lib_path = \"LD_LIBRARY_PATH\"\n        elif self.is_windows():\n            lib_path = \"PATH\"\n        else:\n            lib_path = None\n        self.lib_path: str | None = lib_path\n\n        if vcvars_path is None and is_windows():\n\n            try:\n                # Allow a site-specific vcvarsall path.\n                from .facebook.vcvarsall import build_default_vcvarsall\n            except ImportError:\n                vcvarsall: list[str] = []\n            else:\n                vcvarsall = (\n                    build_default_vcvarsall(self.fbsource_dir)\n                    if self.fbsource_dir is not None\n                    else []\n                )\n\n            # On Windows, the compiler is not available in the PATH by\n            # default so we need to run the vcvarsall script to populate the\n            # environment. We use a glob to find some version of this script\n            # as deployed with Visual Studio.\n            if len(vcvarsall) == 0:\n                # check the 64 bit installs\n                for year in [\"2022\"]:\n                    vcvarsall += glob.glob(\n                        os.path.join(\n                            os.environ.get(\"ProgramFiles\", \"C:\\\\Program Files\"),\n                            \"Microsoft Visual Studio\",\n                            year,\n                            \"*\",\n                            \"VC\",\n                            \"Auxiliary\",\n                            \"Build\",\n                            \"vcvarsall.bat\",\n                        )\n                    )\n\n                # then the 32 bit ones\n                for year in [\"2022\", \"2019\", \"2017\"]:\n                    vcvarsall += glob.glob(\n                        os.path.join(\n                            os.environ[\"ProgramFiles(x86)\"],\n                            \"Microsoft Visual Studio\",\n                            year,\n                            \"*\",\n                            \"VC\",\n                            \"Auxiliary\",\n                            \"Build\",\n                            \"vcvarsall.bat\",\n                        )\n                    )\n            if len(vcvarsall) == 0:\n                raise Exception(\n                    \"Could not find vcvarsall.bat. Please install Visual Studio.\"\n                )\n            vcvars_path = vcvarsall[0]\n            print(f\"Using vcvarsall.bat from {vcvars_path}\", file=sys.stderr)\n\n        self.vcvars_path: str | None = vcvars_path\n\n    @property\n    def manifests_dir(self) -> str:\n        return os.path.join(self.fbcode_builder_dir, \"manifests\")\n\n    def is_darwin(self) -> bool:\n        return self.host_type.is_darwin()\n\n    def is_windows(self) -> bool:\n        return self.host_type.is_windows()\n\n    def is_arm(self) -> bool:\n        return self.host_type.is_arm()\n\n    def get_vcvars_path(self) -> str | None:\n        return self.vcvars_path\n\n    def is_linux(self) -> bool:\n        return self.host_type.is_linux()\n\n    def is_freebsd(self) -> bool:\n        return self.host_type.is_freebsd()\n\n    def get_num_jobs(self, job_weight: int) -> int:\n        \"\"\"Given an estimated job_weight in MiB, compute a reasonable concurrency limit.\"\"\"\n        if self.specified_num_jobs:\n            return self.specified_num_jobs\n\n        available_ram = get_available_ram()\n\n        import multiprocessing\n\n        return max(1, min(multiprocessing.cpu_count(), available_ram // job_weight))\n\n    def get_context_generator(\n        self, host_tuple: str | HostType | None = None\n    ) -> ContextGenerator:\n        \"\"\"Create a manifest ContextGenerator for the specified target platform.\"\"\"\n        if host_tuple is None:\n            host_type = self.host_type\n        elif isinstance(host_tuple, HostType):\n            host_type = host_tuple\n        else:\n            host_type = HostType.from_tuple_string(host_tuple)\n\n        return ContextGenerator(\n            {\n                \"os\": host_type.ostype,\n                \"distro\": host_type.distro,\n                \"distro_vers\": host_type.distrovers,\n                \"fb\": \"on\" if self.facebook_internal else \"off\",\n                \"fbsource\": \"on\" if self.fbsource_dir else \"off\",\n                \"test\": \"off\",\n                \"shared_libs\": \"on\" if self.shared_libs else \"off\",\n            }\n        )\n\n    def compute_env_for_install_dirs(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        ctx: ManifestContext,\n        env: Env | None = None,\n        manifest: ManifestParser | None = None,\n    ) -> Env:  # noqa: C901\n        if env is not None:\n            env = env.copy()\n        else:\n            env = Env()\n\n        env[\"GETDEPS_BUILD_DIR\"] = os.path.join(self.scratch_dir, \"build\")\n        env[\"GETDEPS_INSTALL_DIR\"] = self.install_dir\n\n        # Python setuptools attempts to discover a local MSVC for\n        # building Python extensions. On Windows, getdeps already\n        # supports invoking a vcvarsall prior to compilation.\n        #\n        # Tell setuptools to bypass its own search. This fixes a bug\n        # where setuptools would fail when run from CMake on GitHub\n        # Actions with the inscrutable message 'error: Microsoft\n        # Visual C++ 14.0 is required. Get it with \"Build Tools for\n        # Visual Studio\"'. I suspect the actual error is that the\n        # environment or PATH is overflowing.\n        #\n        # For extra credit, someone could patch setuptools to\n        # propagate the actual error message from vcvarsall, because\n        # often it does not mean Visual C++ is not available.\n        #\n        # Related discussions:\n        # - https://github.com/pypa/setuptools/issues/2028\n        # - https://github.com/pypa/setuptools/issues/2307\n        # - https://developercommunity.visualstudio.com/t/error-microsoft-visual-c-140-is-required/409173\n        # - https://github.com/OpenMS/OpenMS/pull/4779\n        # - https://github.com/actions/virtual-environments/issues/1484\n\n        if self.is_windows() and self.get_vcvars_path():\n            env[\"DISTUTILS_USE_SDK\"] = \"1\"\n\n        # On macOS we need to set `SDKROOT` when we use clang for system\n        # header files.\n        if self.is_darwin() and \"SDKROOT\" not in env:\n            sdkroot = subprocess.check_output([\"xcrun\", \"--show-sdk-path\"])\n            env[\"SDKROOT\"] = sdkroot.decode().strip()\n\n        if (\n            self.is_darwin()\n            and self.allow_system_packages\n            and self.host_type.get_package_manager() == \"homebrew\"\n            and manifest\n            and manifest.resolved_system_packages\n        ):\n            # Homebrew packages may not be on the default PATHs\n            brew_packages = manifest.resolved_system_packages.get(\"homebrew\", [])\n            for p in brew_packages:\n                found = self.add_homebrew_package_to_env(p, env)\n                # Try extra hard to find openssl, needed with homebrew on macOS\n                if found and p.startswith(\"openssl\"):\n                    candidate = homebrew_package_prefix(\"openssl@1.1\")\n                    # pyre-fixme[6]: For 1st argument expected\n                    #  `Union[PathLike[bytes], PathLike[str], bytes, int, str]` but got\n                    #  `Optional[str]`.\n                    if os.path.exists(candidate):\n                        # pyre-fixme[6]: For 2nd argument expected `str` but got\n                        #  `Optional[str]`.\n                        os.environ[\"OPENSSL_ROOT_DIR\"] = candidate\n                        env[\"OPENSSL_ROOT_DIR\"] = os.environ[\"OPENSSL_ROOT_DIR\"]\n\n        if self.fbsource_dir:\n            env[\"YARN_YARN_OFFLINE_MIRROR\"] = os.path.join(\n                self.fbsource_dir, \"xplat/third-party/yarn/offline-mirror\"\n            )\n            yarn_exe = \"yarn.bat\" if self.is_windows() else \"yarn\"\n            env[\"YARN_PATH\"] = os.path.join(\n                # pyre-fixme[6]: For 1st argument expected `LiteralString` but got\n                #  `Optional[str]`.\n                self.fbsource_dir,\n                \"xplat/third-party/yarn/\",\n                yarn_exe,\n            )\n            node_exe = \"node-win-x64.exe\" if self.is_windows() else \"node\"\n            env[\"NODE_BIN\"] = os.path.join(\n                # pyre-fixme[6]: For 1st argument expected `LiteralString` but got\n                #  `Optional[str]`.\n                self.fbsource_dir,\n                \"xplat/third-party/node/bin/\",\n                node_exe,\n            )\n            env[\"RUST_VENDORED_CRATES_DIR\"] = os.path.join(\n                # pyre-fixme[6]: For 1st argument expected `LiteralString` but got\n                #  `Optional[str]`.\n                self.fbsource_dir,\n                \"third-party/rust/vendor\",\n            )\n            hash_data = get_fbsource_repo_data(self)\n            env[\"FBSOURCE_HASH\"] = hash_data.hash\n            env[\"FBSOURCE_DATE\"] = hash_data.date\n\n        # reverse as we are prepending to the PATHs\n        for m in reversed(dep_manifests):\n            is_direct_dep = (\n                manifest is not None and m.name in manifest.get_dependencies(ctx)\n            )\n            d = loader.get_project_install_dir(m)\n            if os.path.exists(d):\n                self.add_prefix_to_env(\n                    d,\n                    env,\n                    append=False,\n                    is_direct_dep=is_direct_dep,\n                )\n\n        # Linux is always system openssl\n        system_openssl: bool = self.is_linux()\n\n        # For other systems lets see if package is requested\n        if not system_openssl and manifest and manifest.resolved_system_packages:\n            for _pkg_type, pkgs in manifest.resolved_system_packages.items():\n                for p in pkgs:\n                    if p.startswith(\"openssl\") or p.startswith(\"libssl\"):\n                        system_openssl = True\n                        break\n\n        # Let openssl know to pick up the system certs if present\n        if system_openssl or \"OPENSSL_DIR\" in env:\n            for system_ssl_cfg in [\"/etc/pki/tls\", \"/etc/ssl\"]:\n                if os.path.isdir(system_ssl_cfg):\n                    cert_dir = system_ssl_cfg + \"/certs\"\n                    if os.path.isdir(cert_dir):\n                        env[\"SSL_CERT_DIR\"] = cert_dir\n                    cert_file = system_ssl_cfg + \"/cert.pem\"\n                    if os.path.isfile(cert_file):\n                        env[\"SSL_CERT_FILE\"] = cert_file\n\n        return env\n\n    def add_homebrew_package_to_env(self, package: str, env: Env) -> bool:\n        prefix = homebrew_package_prefix(package)\n        if prefix and os.path.exists(prefix):\n            return self.add_prefix_to_env(\n                prefix, env, append=False, add_library_path=True\n            )\n        return False\n\n    def add_prefix_to_env(\n        self,\n        d: str,\n        env: Env,\n        append: bool = True,\n        add_library_path: bool = False,\n        is_direct_dep: bool = False,\n    ) -> bool:  # noqa: C901\n        bindir: str = os.path.join(d, \"bin\")\n        found: bool = False\n        has_pkgconfig: bool = False\n        pkgconfig: str = os.path.join(d, \"lib\", \"pkgconfig\")\n        if os.path.exists(pkgconfig):\n            found = True\n            has_pkgconfig = True\n            add_path_entry(env, \"PKG_CONFIG_PATH\", pkgconfig, append=append)\n\n        pkgconfig = os.path.join(d, \"lib64\", \"pkgconfig\")\n        if os.path.exists(pkgconfig):\n            found = True\n            has_pkgconfig = True\n            add_path_entry(env, \"PKG_CONFIG_PATH\", pkgconfig, append=append)\n\n        add_path_entry(env, \"CMAKE_PREFIX_PATH\", d, append=append)\n\n        # Tell the thrift compiler about includes it needs to consider\n        thriftdir: str = os.path.join(d, \"include\", \"thrift-files\")\n        if os.path.exists(thriftdir):\n            found = True\n            add_path_entry(env, \"THRIFT_INCLUDE_PATH\", thriftdir, append=append)\n\n        # module detection for python is old fashioned and needs flags\n        includedir: str = os.path.join(d, \"include\")\n        if os.path.exists(includedir):\n            found = True\n            ncursesincludedir: str = os.path.join(d, \"include\", \"ncurses\")\n            if os.path.exists(ncursesincludedir):\n                add_path_entry(env, \"C_INCLUDE_PATH\", ncursesincludedir, append=append)\n                add_flag(env, \"CPPFLAGS\", f\"-I{includedir}\", append=append)\n                add_flag(env, \"CPPFLAGS\", f\"-I{ncursesincludedir}\", append=append)\n            elif \"/bz2-\" in d:\n                add_flag(env, \"CPPFLAGS\", f\"-I{includedir}\", append=append)\n            # For non-pkgconfig projects Cabal has no way to find the includes or\n            # libraries, so we provide a set of extra Cabal flags in the env\n            if not has_pkgconfig and is_direct_dep:\n                add_flag(\n                    env,\n                    \"GETDEPS_CABAL_FLAGS\",\n                    f\"--extra-include-dirs={includedir}\",\n                    append=append,\n                )\n\n            # The thrift compiler's built-in includes are installed directly to the include dir\n            includethriftdir: str = os.path.join(d, \"include\", \"thrift\")\n            if os.path.exists(includethriftdir):\n                add_path_entry(env, \"THRIFT_INCLUDE_PATH\", includedir, append=append)\n\n        # Map from FB python manifests to PYTHONPATH\n        pydir: str = os.path.join(d, \"lib\", \"fb-py-libs\")\n        if os.path.exists(pydir):\n            found = True\n            manifest_ext: str = \".manifest\"\n            pymanifestfiles: list[str] = [\n                f\n                for f in os.listdir(pydir)\n                if f.endswith(manifest_ext) and os.path.isfile(os.path.join(pydir, f))\n            ]\n            for f in pymanifestfiles:\n                subdir = f[: -len(manifest_ext)]\n                add_path_entry(\n                    env, \"PYTHONPATH\", os.path.join(pydir, subdir), append=append\n                )\n\n        # Allow resolving shared objects built earlier (eg: zstd\n        # doesn't include the full path to the dylib in its linkage\n        # so we need to give it an assist)\n        if self.lib_path:\n            for lib in [\"lib\", \"lib64\"]:\n                libdir: str = os.path.join(d, lib)\n                if os.path.exists(libdir):\n                    found = True\n                    # pyre-fixme[6]: For 2nd argument expected `str` but got\n                    #  `Optional[str]`.\n                    add_path_entry(env, self.lib_path, libdir, append=append)\n                    # module detection for python is old fashioned and needs flags\n                    if \"/ncurses-\" in d:\n                        add_flag(env, \"LDFLAGS\", f\"-L{libdir}\", append=append)\n                    elif \"/bz2-\" in d:\n                        add_flag(env, \"LDFLAGS\", f\"-L{libdir}\", append=append)\n                    if add_library_path:\n                        add_path_entry(env, \"LIBRARY_PATH\", libdir, append=append)\n                    if not has_pkgconfig and is_direct_dep:\n                        add_flag(\n                            env,\n                            \"GETDEPS_CABAL_FLAGS\",\n                            f\"--extra-lib-dirs={libdir}\",\n                            append=append,\n                        )\n\n        # Allow resolving binaries (eg: cmake, ninja) and dlls\n        # built by earlier steps\n        if os.path.exists(bindir):\n            found = True\n            add_path_entry(env, \"PATH\", bindir, append=append)\n\n        # If rustc is present in the `bin` directory, set RUSTC to prevent\n        # cargo uses the rustc installed in the system.\n        if self.is_windows():\n            cargo_path: str = os.path.join(bindir, \"cargo.exe\")\n            rustc_path: str = os.path.join(bindir, \"rustc.exe\")\n            rustdoc_path: str = os.path.join(bindir, \"rustdoc.exe\")\n        else:\n            cargo_path = os.path.join(bindir, \"cargo\")\n            rustc_path = os.path.join(bindir, \"rustc\")\n            rustdoc_path = os.path.join(bindir, \"rustdoc\")\n\n        if os.path.isfile(rustc_path):\n            env[\"CARGO_BIN\"] = cargo_path\n            env[\"RUSTC\"] = rustc_path\n            env[\"RUSTDOC\"] = rustdoc_path\n\n        openssl_include: str = os.path.join(d, \"include\", \"openssl\")\n        if os.path.isdir(openssl_include) and any(\n            os.path.isfile(os.path.join(d, \"lib\", libcrypto))\n            for libcrypto in (\"libcrypto.lib\", \"libcrypto.so\", \"libcrypto.a\")\n        ):\n            # This must be the openssl library, let Rust know about it\n            env[\"OPENSSL_DIR\"] = d\n\n        return found\n\n\ndef list_win32_subst_letters() -> dict[str, str]:\n    output = subprocess.check_output([\"subst\"]).decode(\"utf-8\")\n    # The output is a set of lines like: `F:\\: => C:\\open\\some\\where`\n    lines = output.strip().split(\"\\r\\n\")\n    mapping: dict[str, str] = {}\n    for line in lines:\n        fields = line.split(\": => \")\n        if len(fields) != 2:\n            continue\n        letter = fields[0]\n        path = fields[1]\n        mapping[letter] = path\n\n    return mapping\n\n\ndef find_existing_win32_subst_for_path(\n    path: str,\n    subst_mapping: Mapping[str, str],\n) -> str | None:\n    path = ntpath.normcase(ntpath.normpath(path))\n    for letter, target in subst_mapping.items():\n        if ntpath.normcase(target) == path:\n            return letter\n    return None\n\n\ndef find_unused_drive_letter() -> str | None:\n    import ctypes\n\n    buffer_len = 256\n    blen = ctypes.c_uint(buffer_len)\n    rv = ctypes.c_uint()\n    bufs = ctypes.create_string_buffer(buffer_len)\n    # pyre-fixme[16]: Module `ctypes` has no attribute `windll`.\n    rv = ctypes.windll.kernel32.GetLogicalDriveStringsA(blen, bufs)\n    if rv > buffer_len:\n        raise Exception(\"GetLogicalDriveStringsA result too large for buffer\")\n    nul = \"\\x00\".encode(\"ascii\")\n\n    used: list[str] = [\n        drive.decode(\"ascii\")[0] for drive in bufs.raw.strip(nul).split(nul)\n    ]\n    possible: list[str] = [c for c in \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"]\n    available: list[str] = sorted(list(set(possible) - set(used)))\n    if len(available) == 0:\n        return None\n    # Prefer to assign later letters rather than earlier letters\n    return available[-1]\n\n\ndef map_subst_path(path: str) -> str:\n    \"\"\"find a short drive letter mapping for a path\"\"\"\n    for _attempt in range(0, 24):\n        drive = find_existing_win32_subst_for_path(\n            path, subst_mapping=list_win32_subst_letters()\n        )\n        if drive:\n            return drive\n        available = find_unused_drive_letter()\n        if available is None:\n            raise Exception(\n                (\n                    \"unable to make shorter subst mapping for %s; \"\n                    \"no available drive letters\"\n                )\n                % path\n            )\n\n        # Try to set up a subst mapping; note that we may be racing with\n        # other processes on the same host, so this may not succeed.\n        try:\n            subprocess.check_call([\"subst\", \"%s:\" % available, path])\n            subst = \"%s:\\\\\" % available\n            print(\"Mapped scratch dir %s -> %s\" % (path, subst), file=sys.stderr)\n            return subst\n        except Exception:\n            print(\"Failed to map %s -> %s\" % (available, path), file=sys.stderr)\n\n    raise Exception(\"failed to set up a subst path for %s\" % path)\n\n\ndef _check_host_type(args: argparse.Namespace, host_type: HostType | None) -> HostType:\n    if host_type is None:\n        host_tuple_string: str | None = getattr(args, \"host_type\", None)\n        if host_tuple_string:\n            host_type = HostType.from_tuple_string(host_tuple_string)\n        else:\n            host_type = HostType()\n\n    assert isinstance(host_type, HostType)\n    return host_type\n\n\ndef setup_build_options(\n    args: argparse.Namespace, host_type: HostType | None = None\n) -> BuildOptions:\n    \"\"\"Create a BuildOptions object based on the arguments\"\"\"\n\n    fbcode_builder_dir: str = os.path.dirname(\n        os.path.dirname(os.path.abspath(__file__))\n    )\n    scratch_dir: str | None = args.scratch_path\n    if not scratch_dir:\n        # TODO: `mkscratch` doesn't currently know how best to place things on\n        # sandcastle, so whip up something reasonable-ish\n        if \"SANDCASTLE\" in os.environ:\n            if \"DISK_TEMP\" not in os.environ:\n                raise Exception(\n                    (\n                        \"I need DISK_TEMP to be set in the sandcastle environment \"\n                        \"so that I can store build products somewhere sane\"\n                    )\n                )\n\n            disk_temp: str = os.environ[\"DISK_TEMP\"]\n            if is_windows():\n                # force use gitbash tmp dir for windows, as its less likely to have a tmp cleaner\n                # that removes extracted prior dated source files\n                os.makedirs(GITBASH_TMP, exist_ok=True)\n                print(\n                    f\"Using {GITBASH_TMP} instead of DISK_TEMP {disk_temp} for scratch dir\",\n                    file=sys.stderr,\n                )\n                disk_temp = GITBASH_TMP\n\n            scratch_dir = os.path.join(disk_temp, \"fbcode_builder_getdeps\")\n        if not scratch_dir:\n            try:\n                scratch_dir = (\n                    subprocess.check_output(\n                        [\"mkscratch\", \"path\", \"--subdir\", \"fbcode_builder_getdeps\"]\n                    )\n                    .strip()\n                    .decode(\"utf-8\")\n                )\n            except OSError as exc:\n                if exc.errno != errno.ENOENT:\n                    # A legit failure; don't fall back, surface the error\n                    raise\n                # This system doesn't have mkscratch so we fall back to\n                # something local.\n                munged: str = fbcode_builder_dir.replace(\"Z\", \"zZ\")\n                for s in [\"/\", \"\\\\\", \":\"]:\n                    munged = munged.replace(s, \"Z\")\n\n                if is_windows() and os.path.isdir(\"c:/open\"):\n                    temp: str = \"c:/open/scratch\"\n                else:\n                    temp = tempfile.gettempdir()\n\n                scratch_dir = os.path.join(temp, \"fbcode_builder_getdeps-%s\" % munged)\n                if not is_windows() and os.geteuid() == 0:\n                    # Running as root; in the case where someone runs\n                    # sudo getdeps.py install-system-deps\n                    # and then runs as build without privs, we want to avoid creating\n                    # a scratch dir that the second stage cannot write to.\n                    # So we generate a different path if we are root.\n                    scratch_dir += \"-root\"\n\n        if not os.path.exists(scratch_dir):\n            os.makedirs(scratch_dir)\n\n        if is_windows():\n            subst = map_subst_path(scratch_dir)\n            scratch_dir = subst\n    else:\n        if not os.path.exists(scratch_dir):\n            os.makedirs(scratch_dir)\n\n    # Make sure we normalize the scratch path.  This path is used as part of the hash\n    # computation for detecting if projects have been updated, so we need to always\n    # use the exact same string to refer to a given directory.\n    # But! realpath in some combinations of Windows/Python3 versions can expand the\n    # drive substitutions on Windows, so avoid that!\n    if not is_windows():\n        scratch_dir = os.path.realpath(scratch_dir)\n\n    # Save these args passed by the user in an env variable, so it\n    # can be used while hashing this build.\n    os.environ[\"GETDEPS_CMAKE_DEFINES\"] = getattr(args, \"extra_cmake_defines\", \"\") or \"\"\n\n    host_type = _check_host_type(args, host_type)\n\n    build_args: dict[str, object] = {\n        k: v\n        for (k, v) in vars(args).items()\n        if k\n        in {\n            \"num_jobs\",\n            \"use_shipit\",\n            \"vcvars_path\",\n            \"allow_system_packages\",\n            \"lfs_path\",\n            \"shared_libs\",\n            \"free_up_disk\",\n            \"build_type\",\n        }\n    }\n\n    return BuildOptions(\n        fbcode_builder_dir,\n        scratch_dir,\n        host_type,\n        install_dir=args.install_prefix,\n        facebook_internal=args.facebook_internal,\n        # pyre-fixme[6]: For 6th argument expected `Optional[str]` but got `object`.\n        # pyre-fixme[6]: For 6th argument expected `bool` but got `object`.\n        # pyre-fixme[6]: For 6th argument expected `int` but got `object`.\n        **build_args,\n    )\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/cache.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\n\nclass ArtifactCache:\n    \"\"\"The ArtifactCache is a small abstraction that allows caching\n    named things in some external storage mechanism.\n    The primary use case is for storing the build products on CI\n    systems to accelerate the build\"\"\"\n\n    def download_to_file(self, name: str, dest_file_name: str) -> bool:\n        \"\"\"If `name` exists in the cache, download it and place it\n        in the specified `dest_file_name` location on the filesystem.\n        If a transient issue was encountered a TransientFailure shall\n        be raised.\n        If `name` doesn't exist in the cache `False` shall be returned.\n        If `dest_file_name` was successfully updated `True` shall be\n        returned.\n        All other conditions shall raise an appropriate exception.\"\"\"\n        return False\n\n    def upload_from_file(self, name: str, source_file_name: str) -> None:\n        \"\"\"Causes `name` to be populated in the cache by uploading\n        the contents of `source_file_name` to the storage system.\n        If a transient issue was encountered a TransientFailure shall\n        be raised.\n        If the upload failed for some other reason, an appropriate\n        exception shall be raised.\"\"\"\n        pass\n\n\ndef create_cache() -> ArtifactCache | None:\n    \"\"\"This function is monkey patchable to provide an actual\n    implementation\"\"\"\n    return None\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/cargo.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\nfrom __future__ import annotations\n\nimport os\nimport re\nimport shutil\nimport sys\nimport typing\n\nfrom .builder import BuilderBase\nfrom .copytree import rmtree_more, simple_copytree\n\nif typing.TYPE_CHECKING:\n    from .buildopts import BuildOptions\n    from .load import ManifestLoader\n    from .manifest import ManifestContext, ManifestParser\n\n\nclass CargoBuilder(BuilderBase):\n    def __init__(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],  # manifests of dependencies\n        build_opts: BuildOptions,\n        ctx: ManifestContext,\n        manifest: ManifestParser,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n        build_doc: bool,\n        workspace_dir: str | None,\n        manifests_to_build: str | None,\n        cargo_config_file: str | None,\n    ) -> None:\n        super(CargoBuilder, self).__init__(\n            loader,\n            dep_manifests,\n            build_opts,\n            ctx,\n            manifest,\n            src_dir,\n            build_dir,\n            inst_dir,\n        )\n        self.build_doc = build_doc\n        self.ws_dir: str | None = workspace_dir\n        # pyre-fixme[8]: Attribute has type `Optional[List[str]]`; used as\n        #  `Union[None, List[str], str]`.\n        self.manifests_to_build: list[str] | None = (\n            manifests_to_build and manifests_to_build.split(\",\")\n        )\n        self.loader: ManifestLoader = loader\n        self.cargo_config_file_subdir: str | None = cargo_config_file\n\n    def run_cargo(\n        self,\n        install_dirs: list[str],\n        operation: str,\n        args: list[str] | None = None,\n    ) -> None:\n        args = args or []\n        env = self._compute_env()\n        # Enable using nightly features with stable compiler\n        env[\"RUSTC_BOOTSTRAP\"] = \"1\"\n        env[\"LIBZ_SYS_STATIC\"] = \"1\"\n        cmd = [\n            \"cargo\",\n            operation,\n            \"--workspace\",\n            \"-j%s\" % self.num_jobs,\n        ] + args\n        self._check_cmd(cmd, cwd=self.workspace_dir(), env=env)\n\n    def build_source_dir(self) -> str:\n        return os.path.join(self.build_dir, \"source\")\n\n    def workspace_dir(self) -> str:\n        return os.path.join(self.build_source_dir(), self.ws_dir or \"\")\n\n    def manifest_dir(self, manifest: str) -> str:\n        return os.path.join(self.build_source_dir(), manifest)\n\n    def recreate_dir(self, src: str, dst: str) -> None:\n        if os.path.isdir(dst):\n            if os.path.islink(dst):\n                os.remove(dst)\n            else:\n                rmtree_more(dst)\n        simple_copytree(src, dst)\n\n    def recreate_linked_dir(self, src: str, dst: str) -> None:\n        if os.path.isdir(dst):\n            if os.path.islink(dst):\n                os.remove(dst)\n            elif os.path.isdir(dst):\n                shutil.rmtree(dst)\n        os.symlink(src, dst)\n\n    def cargo_config_file(self) -> str:\n        build_source_dir = self.build_dir\n        if self.cargo_config_file_subdir:\n            return os.path.join(build_source_dir, self.cargo_config_file_subdir)\n        else:\n            return os.path.join(build_source_dir, \".cargo\", \"config.toml\")\n\n    def _create_cargo_config(self) -> dict[str, dict[str, str]]:\n        cargo_config_file = self.cargo_config_file()\n        cargo_config_dir = os.path.dirname(cargo_config_file)\n        if not os.path.isdir(cargo_config_dir):\n            os.mkdir(cargo_config_dir)\n\n        dep_to_git = self._resolve_dep_to_git()\n\n        if os.path.isfile(cargo_config_file):\n            with open(cargo_config_file, \"r\") as f:\n                print(f\"Reading {cargo_config_file}\", file=sys.stderr)\n                cargo_content = f.read()\n        else:\n            cargo_content = \"\"\n\n        new_content = cargo_content\n        if \"# Generated by getdeps.py\" not in cargo_content:\n            new_content += \"\"\"\\\n# Generated by getdeps.py\n[build]\ntarget-dir = '''{}'''\n\n[profile.dev]\ndebug = false\nincremental = false\n\n[profile.release]\nopt-level = \"{}\"\n\"\"\".format(\n                self.build_dir.replace(\"\\\\\", \"\\\\\\\\\"),\n                \"z\" if self.build_opts.build_type == \"MinSizeRel\" else \"s\",\n            )\n\n        # Point to vendored sources from getdeps manifests\n        for _dep, git_conf in dep_to_git.items():\n            if \"cargo_vendored_sources\" in git_conf:\n                vendored_dir = git_conf[\"cargo_vendored_sources\"].replace(\"\\\\\", \"\\\\\\\\\")\n                override = (\n                    f'[source.\"{git_conf[\"repo_url\"]}\"]\\ndirectory = \"{vendored_dir}\"\\n'\n                )\n                if override not in cargo_content:\n                    new_content += override\n\n            if self.build_opts.fbsource_dir:\n                # Point to vendored crates.io if possible\n                try:\n                    from .facebook.rust import vendored_crates\n\n                    new_content = vendored_crates(\n                        self.build_opts.fbsource_dir, new_content\n                    )\n                except ImportError:\n                    # This FB internal module isn't shippped to github,\n                    # so just rely on cargo downloading crates on it's own\n                    pass\n\n        if new_content != cargo_content:\n            with open(cargo_config_file, \"w\") as f:\n                print(\n                    f\"Writing cargo config for {self.manifest.name} to {cargo_config_file}\",\n                    file=sys.stderr,\n                )\n                f.write(new_content)\n\n        return dep_to_git\n\n    def _prepare(self, reconfigure: bool) -> None:\n        build_source_dir = self.build_source_dir()\n        self.recreate_dir(self.src_dir, build_source_dir)\n\n        dep_to_git = self._create_cargo_config()\n\n        if self.ws_dir is not None:\n            self._patchup_workspace(dep_to_git)\n\n    def _build(self, reconfigure: bool) -> None:\n        # _prepare has been run already. Actually do the build\n        build_source_dir = self.build_source_dir()\n\n        build_args = [\n            \"--artifact-dir\",\n            os.path.join(self.inst_dir, \"bin\"),\n            \"-Zunstable-options\",\n        ]\n\n        if self.build_opts.build_type != \"Debug\":\n            build_args.append(\"--release\")\n\n        if self.manifests_to_build is None:\n            self.run_cargo(\n                self.install_dirs,\n                \"build\",\n                build_args,\n            )\n        else:\n            # pyre-fixme[16]: Optional type has no attribute `__iter__`.\n            for manifest in self.manifests_to_build:\n                self.run_cargo(\n                    self.install_dirs,\n                    \"build\",\n                    build_args\n                    + [\n                        \"--manifest-path\",\n                        self.manifest_dir(manifest),\n                    ],\n                )\n\n        self.recreate_linked_dir(\n            build_source_dir, os.path.join(self.inst_dir, \"source\")\n        )\n\n    def run_tests(\n        self,\n        schedule_type: str,\n        owner: str | None,\n        test_filter: str | None,\n        test_exclude: str | None,\n        retry: int,\n        no_testpilot: bool,\n        timeout: int | None = None,\n    ) -> None:\n        build_args: list[str] = []\n        if self.build_opts.build_type != \"Debug\":\n            build_args.append(\"--release\")\n\n        if test_filter:\n            filter_args = [\"--\", test_filter]\n        else:\n            filter_args = []\n\n        if self.manifests_to_build is None:\n            self.run_cargo(self.install_dirs, \"test\", build_args + filter_args)\n            if self.build_doc and not filter_args:\n                self.run_cargo(self.install_dirs, \"doc\", [\"--no-deps\"])\n        else:\n            # pyre-fixme[16]: Optional type has no attribute `__iter__`.\n            for manifest in self.manifests_to_build:\n                margs = [\"--manifest-path\", self.manifest_dir(manifest)]\n                self.run_cargo(\n                    self.install_dirs, \"test\", build_args + filter_args + margs\n                )\n                if self.build_doc and not filter_args:\n                    self.run_cargo(self.install_dirs, \"doc\", [\"--no-deps\"] + margs)\n\n    def _patchup_workspace(self, dep_to_git: dict[str, dict[str, str]]) -> None:\n        \"\"\"\n        This method makes some assumptions about the state of the project and\n        its cargo dependendies:\n        1. Crates from cargo dependencies can be extracted from Cargo.toml files\n           using _extract_crates function. It is using a heuristic so check its\n           code to understand how it is done.\n        2. The extracted cargo dependencies crates can be found in the\n           dependency's install dir using _resolve_crate_to_path function\n           which again is using a heuristic.\n\n        Notice that many things might go wrong here. E.g. if someone depends\n        on another getdeps crate by writing in their Cargo.toml file:\n\n            my-rename-of-crate = { package = \"crate\", git = \"...\" }\n\n        they can count themselves lucky because the code will raise an\n        Exception. There might be more cases where the code will silently pass\n        producing bad results.\n        \"\"\"\n        workspace_dir = self.workspace_dir()\n        git_url_to_crates_and_paths = self._resolve_config(dep_to_git)\n        if git_url_to_crates_and_paths:\n            patch_cargo = os.path.join(workspace_dir, \"Cargo.toml\")\n            if os.path.isfile(patch_cargo):\n                with open(patch_cargo, \"r\") as f:\n                    manifest_content = f.read()\n            else:\n                manifest_content = \"\"\n\n            new_content = manifest_content\n            if \"[package]\" not in manifest_content:\n                # A fake manifest has to be crated to change the virtual\n                # manifest into a non-virtual. The virtual manifests are limited\n                # in many ways and the inability to define patches on them is\n                # one. Check https://github.com/rust-lang/cargo/issues/4934 to\n                # see if it is resolved.\n                null_file = \"/dev/null\"\n                if self.build_opts.is_windows():\n                    null_file = \"nul\"\n                new_content += f\"\"\"\n[package]\nname = \"fake_manifest_of_{self.manifest.name}\"\nversion = \"0.0.0\"\n\n[lib]\npath = \"{null_file}\"\n\"\"\"\n            config: list[str] = []\n            for git_url, crates_to_patch_path in git_url_to_crates_and_paths.items():\n                crates_patches = [\n                    '{} = {{ path = \"{}\" }}'.format(\n                        crate,\n                        crates_to_patch_path[crate].replace(\"\\\\\", \"\\\\\\\\\"),\n                    )\n                    for crate in sorted(crates_to_patch_path.keys())\n                ]\n                patch_key = f'[patch.\"{git_url}\"]'\n                if patch_key not in manifest_content:\n                    config.append(f\"\\n{patch_key}\\n\" + \"\\n\".join(crates_patches))\n            new_content += \"\\n\".join(config)\n            if new_content != manifest_content:\n                with open(patch_cargo, \"w\") as f:\n                    print(\n                        f\"writing patch to {patch_cargo}\",\n                        file=sys.stderr,\n                    )\n                    f.write(new_content)\n\n    def _resolve_config(\n        self, dep_to_git: dict[str, dict[str, str]]\n    ) -> dict[str, dict[str, str]]:\n        \"\"\"\n        Returns a configuration to be put inside root Cargo.toml file which\n        patches the dependencies git code with local getdeps versions.\n        See https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section\n        \"\"\"\n        dep_to_crates = self._resolve_dep_to_crates(self.build_source_dir(), dep_to_git)\n\n        git_url_to_crates_and_paths: dict[str, dict[str, str]] = {}\n        for dep_name in sorted(dep_to_git.keys()):\n            git_conf = dep_to_git[dep_name]\n            req_crates = sorted(dep_to_crates.get(dep_name, []))\n            if not req_crates:\n                continue  # nothing to patch, move along\n\n            git_url = git_conf.get(\"repo_url\", None)\n            crate_source_map = git_conf[\"crate_source_map\"]\n            if git_url and crate_source_map:\n                crates_to_patch_path = git_url_to_crates_and_paths.get(git_url, {})\n                for c in req_crates:\n                    if c in crate_source_map and c not in crates_to_patch_path:\n                        # pyre-fixme[6]: For 1st argument expected `Union[slice[Any,\n                        #  Any, Any], SupportsIndex]` but got `str`.\n                        crates_to_patch_path[c] = crate_source_map[c]\n                        print(\n                            f\"{self.manifest.name}: Patching crate {c} via virtual manifest in {self.workspace_dir()}\",\n                            file=sys.stderr,\n                        )\n                if crates_to_patch_path:\n                    git_url_to_crates_and_paths[git_url] = crates_to_patch_path\n\n        return git_url_to_crates_and_paths\n\n    def _resolve_dep_to_git(self) -> dict[str, dict[str, str]]:\n        \"\"\"\n        For each direct dependency of the currently build manifest check if it\n        is also cargo-builded and if yes then extract it's git configs and\n        install dir\n        \"\"\"\n        dependencies = self.manifest.get_dependencies(self.ctx)\n        if not dependencies:\n            return {}\n\n        dep_to_git: dict[str, dict[str, str]] = {}\n        for dep in dependencies:\n            dep_manifest = self.loader.load_manifest(dep)\n            dep_builder = dep_manifest.get(\"build\", \"builder\", ctx=self.ctx)\n\n            dep_cargo_conf = dep_manifest.get_section_as_dict(\"cargo\", self.ctx)\n            dep_crate_map = dep_manifest.get_section_as_dict(\"crate.pathmap\", self.ctx)\n\n            if (\n                not (dep_crate_map or dep_cargo_conf)\n                and dep_builder not in [\"cargo\"]\n                or dep == \"rust\"\n            ):\n                # This dependency has no cargo rust content so ignore it.\n                # The \"rust\" dependency is an exception since it contains the\n                # toolchain.\n                continue\n\n            git_conf = dep_manifest.get_section_as_dict(\"git\", self.ctx)\n            if dep != \"rust\" and \"repo_url\" not in git_conf:\n                raise Exception(\n                    f\"{dep}: A cargo dependency requires git.repo_url to be defined.\"\n                )\n\n            if dep_builder == \"cargo\":\n                dep_source_dir = self.loader.get_project_install_dir(dep_manifest)\n                dep_source_dir = os.path.join(dep_source_dir, \"source\")\n            else:\n                fetcher = self.loader.create_fetcher(dep_manifest)\n                dep_source_dir = fetcher.get_src_dir()\n\n            crate_source_map: dict[str, str] = {}\n            if dep_crate_map:\n                for crate, subpath in dep_crate_map.items():\n                    if crate not in crate_source_map:\n                        if self.build_opts.is_windows():\n                            # pyre-fixme[16]: Optional type has no attribute `replace`.\n                            subpath = subpath.replace(\"/\", \"\\\\\")\n                        crate_path = os.path.join(dep_source_dir, subpath)\n                        print(\n                            f\"{self.manifest.name}: Mapped crate {crate} to dep {dep} dir {crate_path}\",\n                            file=sys.stderr,\n                        )\n                        crate_source_map[crate] = crate_path\n            elif dep_cargo_conf:\n                # We don't know what crates are defined buy the dep, look for them\n                search_pattern = re.compile('\\\\[package\\\\]\\nname = \"(.*)\"')\n                for crate_root, _, files in os.walk(dep_source_dir):\n                    if \"Cargo.toml\" in files:\n                        with open(os.path.join(crate_root, \"Cargo.toml\"), \"r\") as f:\n                            content = f.read()\n                            match = search_pattern.search(content)\n                            if match:\n                                crate = match.group(1)\n                                if crate:\n                                    print(\n                                        f\"{self.manifest.name}: Discovered crate {crate} in dep {dep} dir {crate_root}\",\n                                        file=sys.stderr,\n                                    )\n                                    crate_source_map[crate] = crate_root\n\n            # pyre-fixme[6]: For 2nd argument expected `Optional[str]` but got\n            #  `Dict[str, str]`.\n            git_conf[\"crate_source_map\"] = crate_source_map\n\n            if not dep_crate_map and dep_cargo_conf:\n                dep_cargo_dir = self.loader.get_project_build_dir(dep_manifest)\n                dep_cargo_dir = os.path.join(dep_cargo_dir, \"source\")\n                dep_ws_dir = dep_cargo_conf.get(\"workspace_dir\", None)\n                if dep_ws_dir:\n                    dep_cargo_dir = os.path.join(dep_cargo_dir, dep_ws_dir)\n                git_conf[\"cargo_vendored_sources\"] = dep_cargo_dir\n\n            # pyre-fixme[6]: For 2nd argument expected `Dict[str, str]` but got\n            #  `Dict[str, Optional[str]]`.\n            dep_to_git[dep] = git_conf\n        return dep_to_git\n\n    def _resolve_dep_to_crates(\n        self,\n        build_source_dir: str,\n        dep_to_git: dict[str, dict[str, str]],\n    ) -> dict[str, set[str]]:\n        \"\"\"\n        This function traverse the build_source_dir in search of Cargo.toml\n        files, extracts the crate names from them using _extract_crates\n        function and returns a merged result containing crate names per\n        dependency name from all Cargo.toml files in the project.\n        \"\"\"\n        if not dep_to_git:\n            return {}  # no deps, so don't waste time traversing files\n\n        dep_to_crates: dict[str, set[str]] = {}\n\n        # First populate explicit crate paths from dependencies\n        for name, git_conf in dep_to_git.items():\n            # pyre-fixme[16]: `str` has no attribute `keys`.\n            crates = git_conf[\"crate_source_map\"].keys()\n            if crates:\n                dep_to_crates.setdefault(name, set()).update(crates)\n\n        # Now find from Cargo.tomls\n        for root, _, files in os.walk(build_source_dir):\n            for f in files:\n                if f == \"Cargo.toml\":\n                    more_dep_to_crates = CargoBuilder._extract_crates_used(\n                        os.path.join(root, f), dep_to_git\n                    )\n                    for dep_name, crates in more_dep_to_crates.items():\n                        existing_crates = dep_to_crates.get(dep_name, set())\n                        for c in crates:\n                            if c not in existing_crates:\n                                print(\n                                    f\"Patch {self.manifest.name} uses {dep_name} crate {crates}\",\n                                    file=sys.stderr,\n                                )\n                                existing_crates.add(c)\n                        # pyre-fixme[61]: `name` is undefined, or not always defined.\n                        dep_to_crates.setdefault(name, set()).update(existing_crates)\n        return dep_to_crates\n\n    @staticmethod\n    def _extract_crates_used(\n        cargo_toml_file: str,\n        dep_to_git: dict[str, dict[str, str]],\n    ) -> dict[str, set[str]]:\n        \"\"\"\n        This functions reads content of provided cargo toml file and extracts\n        crate names per each dependency. The extraction is done by a heuristic\n        so it might be incorrect.\n        \"\"\"\n        deps_to_crates: dict[str, set[str]] = {}\n        with open(cargo_toml_file, \"r\") as f:\n            for line in f.readlines():\n                if line.startswith(\"#\") or \"git = \" not in line:\n                    continue  # filter out commented lines and ones without git deps\n                for dep_name, conf in dep_to_git.items():\n                    # Only redirect deps that point to git URLS\n                    if 'git = \"{}\"'.format(conf[\"repo_url\"]) in line:\n                        pkg_template = ' package = \"'\n                        if pkg_template in line:\n                            crate_name, _, _ = line.partition(pkg_template)[\n                                2\n                            ].partition('\"')\n                        else:\n                            crate_name, _, _ = line.partition(\"=\")\n                        deps_to_crates.setdefault(dep_name, set()).add(\n                            crate_name.strip()\n                        )\n        return deps_to_crates\n\n    def _resolve_crate_to_path(\n        self,\n        crate: str,\n        crate_source_map: dict[str, str],\n    ) -> str:\n        \"\"\"\n        Tries to find <crate> in source_dir by searching a [package]\n        keyword followed by name = \"<crate>\".\n        \"\"\"\n        search_pattern = '[package]\\nname = \"{}\"'.format(crate)\n\n        for _crate, crate_source_dir in crate_source_map.items():\n            for crate_root, _, files in os.walk(crate_source_dir):\n                if \"Cargo.toml\" in files:\n                    with open(os.path.join(crate_root, \"Cargo.toml\"), \"r\") as f:\n                        content = f.read()\n                        if search_pattern in content:\n                            return crate_root\n\n        raise Exception(\n            f\"{self.manifest.name}: Failed to find dep crate {crate} in paths {crate_source_map}\"\n        )\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/copytree.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\nimport os\nimport shutil\nimport stat\nimport subprocess\nfrom collections.abc import Callable\n\nfrom .platform import is_windows\nfrom .runcmd import run_cmd\n\n\nPREFETCHED_DIRS: set[str] = set()\n\n\ndef containing_repo_type(path: str) -> tuple[str | None, str | None]:\n    while True:\n        if os.path.exists(os.path.join(path, \".git\")):\n            return (\"git\", path)\n        if os.path.exists(os.path.join(path, \".hg\")):\n            return (\"hg\", path)\n\n        parent = os.path.dirname(path)\n        if parent == path:\n            return None, None\n        path = parent\n\n\ndef find_eden_root(dirpath: str) -> str | None:\n    \"\"\"If the specified directory is inside an EdenFS checkout, returns\n    the canonical absolute path to the root of that checkout.\n\n    Returns None if the specified directory is not in an EdenFS checkout.\n    \"\"\"\n    if is_windows():\n        repo_type, repo_root = containing_repo_type(dirpath)\n        if repo_root is not None:\n            if os.path.exists(os.path.join(repo_root, \".eden\", \"config\")):\n                return repo_root\n        return None\n\n    try:\n        return os.readlink(os.path.join(dirpath, \".eden\", \"root\"))\n    except OSError:\n        return None\n\n\ndef prefetch_dir_if_eden(dirpath: str) -> None:\n    \"\"\"After an amend/rebase, Eden may need to fetch a large number\n    of trees from the servers.  The simplistic single threaded walk\n    performed by copytree makes this more expensive than is desirable\n    so we help accelerate things by performing a prefetch on the\n    source directory\"\"\"\n    global PREFETCHED_DIRS\n    if dirpath in PREFETCHED_DIRS:\n        return\n    root = find_eden_root(dirpath)\n    if root is None:\n        return\n    glob = f\"{os.path.relpath(dirpath, root).replace(os.sep, '/')}/**\"\n    print(f\"Prefetching {glob}\")\n    subprocess.call([\"edenfsctl\", \"prefetch\", \"--repo\", root, glob, \"--background\"])\n    PREFETCHED_DIRS.add(dirpath)\n\n\ndef simple_copytree(src_dir: str, dest_dir: str, symlinks: bool = False) -> str:\n    \"\"\"A simple version of shutil.copytree() that can delegate to native tools if faster\"\"\"\n    if is_windows():\n        os.makedirs(dest_dir, exist_ok=True)\n        cmd = [\n            \"robocopy.exe\",\n            src_dir,\n            dest_dir,\n            # copy directories, including empty ones\n            \"/E\",\n            # Ignore Extra files in destination\n            \"/XX\",\n            # enable parallel copy\n            \"/MT\",\n            # be quiet\n            \"/NFL\",\n            \"/NDL\",\n            \"/NJH\",\n            \"/NJS\",\n            \"/NP\",\n        ]\n        if symlinks:\n            cmd.append(\"/SL\")\n        # robocopy exits with code 1 if it copied ok, hence allow_fail\n        # https://learn.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/return-codes-used-robocopy-utility\n        exit_code = run_cmd(cmd, allow_fail=True)\n        if exit_code > 1:\n            raise subprocess.CalledProcessError(exit_code, cmd)\n        return dest_dir\n    else:\n        return shutil.copytree(src_dir, dest_dir, symlinks=symlinks)\n\n\ndef _remove_readonly_and_try_again(\n    func: Callable[..., object],\n    path: str,\n    # pyre-fixme[24]: Generic type `type` expects 1 type parameter, use\n    #  `typing.Type[<base type>]` to avoid runtime subscripting errors.\n    exc_info: tuple[type, BaseException, object],\n) -> None:\n    \"\"\"\n    Error handler for shutil.rmtree.\n    If the error is due to an access error (read only file)\n    it attempts to add write permission and then retries the operation.\n    Any other failure propagates.\n    \"\"\"\n    # exc_info is a tuple (exc_type, exc_value, traceback)\n    exc_type = exc_info[0]\n    if exc_type is PermissionError:\n        os.chmod(path, stat.S_IWRITE)\n        # Retry the original function (os.remove or os.rmdir)\n        try:\n            func(path)\n        except Exception:\n            # If it still fails, the original exception from func() will propagate\n            raise\n    else:\n        # If the error is not a PermissionError, re-raise the original exception\n        raise exc_info[1]\n\n\ndef rmtree_more(path: str) -> None:\n    \"\"\"Wrapper around shutil.rmtree() that makes it remove readonly files as well.\n    Useful when git on windows decides to make some files readonly on checkout\"\"\"\n    shutil.rmtree(path, onerror=_remove_readonly_and_try_again)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/dyndeps.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\nfrom __future__ import annotations\n\nimport errno\nimport glob\nimport os\nimport re\nimport shlex\nimport shutil\nimport stat\nimport subprocess\nimport sys\nimport typing\nfrom collections.abc import Generator\nfrom struct import unpack\n\nif typing.TYPE_CHECKING:\n    from .buildopts import BuildOptions\n    from .envfuncs import Env\n\nOBJECT_SUBDIRS: tuple[str, ...] = (\"bin\", \"lib\", \"lib64\")\n\n\ndef copyfile(src: str, dest: str) -> None:\n    shutil.copyfile(src, dest)\n    shutil.copymode(src, dest)\n\n\nclass DepBase:\n    def __init__(\n        self,\n        buildopts: BuildOptions,\n        env: Env,\n        install_dirs: list[str],\n        strip: bool,\n    ) -> None:\n        self.buildopts: BuildOptions = buildopts\n        self.env: Env = env\n        self.install_dirs: list[str] = install_dirs\n        self.strip: bool = strip\n\n        # Deduplicates dependency processing. Keyed on the library\n        # destination path.\n        self.processed_deps: set[str] = set()\n\n        self.munged_lib_dir: str = \"\"\n\n    def list_dynamic_deps(self, objfile: str) -> list[str]:\n        raise RuntimeError(\"list_dynamic_deps not implemented\")\n\n    def interesting_dep(self, d: str) -> bool:\n        return True\n\n    # final_install_prefix must be the equivalent path to `destdir` on the\n    # installed system.  For example, if destdir is `/tmp/RANDOM/usr/local' which\n    # is intended to map to `/usr/local` in the install image, then\n    # final_install_prefix='/usr/local'.\n    # If left unspecified, destdir will be used.\n    def process_deps(\n        self, destdir: str, final_install_prefix: str | None = None\n    ) -> None:\n        if self.buildopts.is_windows():\n            lib_dir = \"bin\"\n        else:\n            lib_dir = \"lib\"\n        self.munged_lib_dir = os.path.join(destdir, lib_dir)\n\n        final_lib_dir: str = os.path.join(final_install_prefix or destdir, lib_dir)\n\n        if not os.path.isdir(self.munged_lib_dir):\n            os.makedirs(self.munged_lib_dir)\n\n        # Look only at the things that got installed in the leaf package,\n        # which will be the last entry in the install dirs list\n        inst_dir: str = self.install_dirs[-1]\n        print(\"Process deps under %s\" % inst_dir, file=sys.stderr)\n\n        for dir in OBJECT_SUBDIRS:\n            src_dir: str = os.path.join(inst_dir, dir)\n            if not os.path.isdir(src_dir):\n                continue\n            dest_dir: str = os.path.join(destdir, dir)\n            if not os.path.exists(dest_dir):\n                os.makedirs(dest_dir)\n\n            for objfile in self.list_objs_in_dir(src_dir):\n                print(\"Consider %s/%s\" % (dir, objfile))\n                dest_obj: str = os.path.join(dest_dir, objfile)\n                copyfile(os.path.join(src_dir, objfile), dest_obj)\n                self.munge_in_place(dest_obj, final_lib_dir)\n\n    def find_all_dependencies(self, build_dir: str) -> list[str]:\n        all_deps: set[str] = set()\n        for objfile in self.list_objs_in_dir(\n            build_dir, recurse=True, output_prefix=build_dir\n        ):\n            for d in self.list_dynamic_deps(objfile):\n                all_deps.add(d)\n\n        interesting_deps: set[str] = {d for d in all_deps if self.interesting_dep(d)}\n        dep_paths: list[str] = []\n        for dep in interesting_deps:\n            dep_path: str | None = self.resolve_loader_path(dep)\n            if dep_path:\n                dep_paths.append(dep_path)\n\n        return dep_paths\n\n    def munge_in_place(self, objfile: str, final_lib_dir: str) -> None:\n        print(\"Munging %s\" % objfile)\n        for d in self.list_dynamic_deps(objfile):\n            if not self.interesting_dep(d):\n                continue\n\n            # Resolve this dep: does it exist in any of our installation\n            # directories?  If so, then it is a candidate for processing\n            dep: str | None = self.resolve_loader_path(d)\n            if dep:\n                dest_dep: str = os.path.join(self.munged_lib_dir, os.path.basename(dep))\n                print(\"dep: %s -> %s\" % (d, dest_dep))\n                if dest_dep in self.processed_deps:\n                    # A previous dependency with the same name has already\n                    # been installed at dest_dep, so there is no need to copy\n                    # or munge the dependency again.\n                    # TODO: audit that both source paths have the same inode number\n                    pass\n                else:\n                    self.processed_deps.add(dest_dep)\n                    copyfile(dep, dest_dep)\n                    self.munge_in_place(dest_dep, final_lib_dir)\n\n                self.rewrite_dep(objfile, d, dep, dest_dep, final_lib_dir)\n\n        if self.strip:\n            self.strip_debug_info(objfile)\n\n    def rewrite_dep(\n        self,\n        objfile: str,\n        depname: str,\n        old_dep: str,\n        new_dep: str,\n        final_lib_dir: str,\n    ) -> None:\n        raise RuntimeError(\"rewrite_dep not implemented\")\n\n    def resolve_loader_path(self, dep: str) -> str | None:\n        if os.path.isabs(dep):\n            return dep\n        d: str = os.path.basename(dep)\n        for inst_dir in self.install_dirs:\n            for libdir in OBJECT_SUBDIRS:\n                candidate: str = os.path.join(inst_dir, libdir, d)\n                if os.path.exists(candidate):\n                    return candidate\n        return None\n\n    def list_objs_in_dir(\n        self, dir: str, recurse: bool = False, output_prefix: str = \"\"\n    ) -> Generator[str, None, None]:\n        for entry in os.listdir(dir):\n            entry_path: str = os.path.join(dir, entry)\n            st: os.stat_result = os.lstat(entry_path)\n            if stat.S_ISREG(st.st_mode):\n                if self.is_objfile(entry_path):\n                    relative_result: str = os.path.join(output_prefix, entry)\n                    yield os.path.normcase(relative_result)\n            elif recurse and stat.S_ISDIR(st.st_mode):\n                child_prefix: str = os.path.join(output_prefix, entry)\n                for result in self.list_objs_in_dir(\n                    entry_path, recurse=recurse, output_prefix=child_prefix\n                ):\n                    yield result\n\n    def is_objfile(self, objfile: str) -> bool:\n        return True\n\n    def strip_debug_info(self, objfile: str) -> None:\n        \"\"\"override this to define how to remove debug information\n        from an object file\"\"\"\n        pass\n\n    def check_call_verbose(self, args: list[str]) -> None:\n        print(\" \".join(map(shlex.quote, args)))\n        subprocess.check_call(args)\n\n\nclass WinDeps(DepBase):\n    def __init__(\n        self,\n        buildopts: BuildOptions,\n        env: Env,\n        install_dirs: list[str],\n        strip: bool,\n    ) -> None:\n        super(WinDeps, self).__init__(buildopts, env, install_dirs, strip)\n        self.dumpbin: str = self.find_dumpbin()\n\n    def find_dumpbin(self) -> str:\n        # Looking for dumpbin in the following hardcoded paths.\n        # The registry option to find the install dir doesn't work anymore.\n        globs: list[str] = [\n            (\n                \"C:/Program Files/\"\n                \"Microsoft Visual Studio/\"\n                \"*/*/VC/Tools/\"\n                \"MSVC/*/bin/Hostx64/x64/dumpbin.exe\"\n            ),\n            (\n                \"C:/Program Files (x86)/\"\n                \"Microsoft Visual Studio/\"\n                \"*/*/VC/Tools/\"\n                \"MSVC/*/bin/Hostx64/x64/dumpbin.exe\"\n            ),\n            (\n                \"C:/Program Files (x86)/\"\n                \"Common Files/\"\n                \"Microsoft/Visual C++ for Python/*/\"\n                \"VC/bin/dumpbin.exe\"\n            ),\n            (\"c:/Program Files (x86)/Microsoft Visual Studio */VC/bin/dumpbin.exe\"),\n            (\n                \"C:/Program Files/Microsoft Visual Studio/*/Professional/VC/Tools/MSVC/*/bin/HostX64/x64/dumpbin.exe\"\n            ),\n        ]\n        for pattern in globs:\n            for exe in glob.glob(pattern):\n                return exe\n\n        raise RuntimeError(\"could not find dumpbin.exe\")\n\n    # pyre-fixme[14]: `list_dynamic_deps` overrides method defined in `DepBase`\n    #  inconsistently.\n    def list_dynamic_deps(self, exe: str) -> list[str]:\n        deps: list[str] = []\n        print(\"Resolve deps for %s\" % exe)\n        output: str = subprocess.check_output(\n            [self.dumpbin, \"/nologo\", \"/dependents\", exe]\n        ).decode(\"utf-8\")\n\n        lines: list[str] = output.split(\"\\n\")\n        for line in lines:\n            m: re.Match[str] | None = re.match(\"\\\\s+(\\\\S+.dll)\", line, re.IGNORECASE)\n            if m:\n                deps.append(m.group(1).lower())\n\n        return deps\n\n    def rewrite_dep(\n        self,\n        objfile: str,\n        depname: str,\n        old_dep: str,\n        new_dep: str,\n        final_lib_dir: str,\n    ) -> None:\n        # We can't rewrite on windows, but we will\n        # place the deps alongside the exe so that\n        # they end up in the search path\n        pass\n\n    # These are the Windows system dll, which we don't want to copy while\n    # packaging.\n    SYSTEM_DLLS: set[str] = set(  # noqa: C405\n        [\n            \"advapi32.dll\",\n            \"dbghelp.dll\",\n            \"kernel32.dll\",\n            \"msvcp140.dll\",\n            \"vcruntime140.dll\",\n            \"ws2_32.dll\",\n            \"ntdll.dll\",\n            \"shlwapi.dll\",\n        ]\n    )\n\n    def interesting_dep(self, d: str) -> bool:\n        if \"api-ms-win-crt\" in d:\n            return False\n        if d in self.SYSTEM_DLLS:\n            return False\n        return True\n\n    def is_objfile(self, objfile: str) -> bool:\n        if not os.path.isfile(objfile):\n            return False\n        if objfile.lower().endswith(\".exe\"):\n            return True\n        return False\n\n    def emit_dev_run_script(self, script_path: str, dep_dirs: list[str]) -> None:\n        \"\"\"Emit a script that can be used to run build artifacts directly from the\n        build directory, without installing them.\n\n        The dep_dirs parameter should be a list of paths that need to be added to $PATH.\n        This can be computed by calling compute_dependency_paths() or\n        compute_dependency_paths_fast().\n\n        This is only necessary on Windows, which does not have RPATH, and instead\n        requires the $PATH environment variable be updated in order to find the proper\n        library dependencies.\n        \"\"\"\n        contents: str = self._get_dev_run_script_contents(dep_dirs)\n        with open(script_path, \"w\") as f:\n            f.write(contents)\n\n    def compute_dependency_paths(self, build_dir: str) -> list[str]:\n        \"\"\"Return a list of all directories that need to be added to $PATH to ensure\n        that library dependencies can be found correctly.  This is computed by scanning\n        binaries to determine exactly the right list of dependencies.\n\n        The compute_dependency_paths_fast() is a alternative function that runs faster\n        but may return additional extraneous paths.\n        \"\"\"\n        dep_dirs: set[str] = set()\n        # Find paths by scanning the binaries.\n        for dep in self.find_all_dependencies(build_dir):\n            dep_dirs.add(os.path.dirname(dep))\n\n        dep_dirs.update(self.read_custom_dep_dirs(build_dir))\n        return sorted(dep_dirs)\n\n    def compute_dependency_paths_fast(self, build_dir: str) -> list[str]:\n        \"\"\"Similar to compute_dependency_paths(), but rather than actually scanning\n        binaries, just add all library paths from the specified installation\n        directories.  This is much faster than scanning the binaries, but may result in\n        more paths being returned than actually necessary.\n        \"\"\"\n        dep_dirs: set[str] = set()\n        for inst_dir in self.install_dirs:\n            for subdir in OBJECT_SUBDIRS:\n                path: str = os.path.join(inst_dir, subdir)\n                if os.path.exists(path):\n                    dep_dirs.add(path)\n\n        dep_dirs.update(self.read_custom_dep_dirs(build_dir))\n        return sorted(dep_dirs)\n\n    def read_custom_dep_dirs(self, build_dir: str) -> set[str]:\n        # The build system may also have included libraries from other locations that\n        # we might not be able to find normally in find_all_dependencies().\n        # To handle this situation we support reading additional library paths\n        # from a LIBRARY_DEP_DIRS.txt file that may have been generated in the build\n        # output directory.\n        dep_dirs: set[str] = set()\n        try:\n            explicit_dep_dirs_path: str = os.path.join(\n                build_dir, \"LIBRARY_DEP_DIRS.txt\"\n            )\n            with open(explicit_dep_dirs_path, \"r\") as f:\n                for line in f.read().splitlines():\n                    dep_dirs.add(line)\n        except OSError as ex:\n            if ex.errno != errno.ENOENT:\n                raise\n\n        return dep_dirs\n\n    def _get_dev_run_script_contents(self, path_dirs: list[str]) -> str:\n        path_entries: list[str] = [\"$env:PATH\"] + path_dirs\n        path_str: str = \";\".join(path_entries)\n        return \"\"\"\\\n$orig_env = $env:PATH\n$env:PATH = \"{path_str}\"\n\ntry {{\n    $cmd_args = $args[1..$args.length]\n    & $args[0] @cmd_args\n}} finally {{\n    $env:PATH = $orig_env\n}}\n\"\"\".format(\n            path_str=path_str\n        )\n\n\nclass ElfDeps(DepBase):\n    def __init__(\n        self,\n        buildopts: BuildOptions,\n        env: Env,\n        install_dirs: list[str],\n        strip: bool,\n    ) -> None:\n        super(ElfDeps, self).__init__(buildopts, env, install_dirs, strip)\n\n        # We need patchelf to rewrite deps, so ensure that it is built...\n        args: list[str] = [sys.executable, sys.argv[0]]\n        if buildopts.allow_system_packages:\n            args.append(\"--allow-system-packages\")\n        subprocess.check_call(args + [\"build\", \"patchelf\"])\n\n        # ... and that we know where it lives\n        patchelf_install: str = os.fsdecode(\n            subprocess.check_output(args + [\"show-inst-dir\", \"patchelf\"]).strip()\n        )\n        if not patchelf_install:\n            # its a system package, so we assume it is in the path\n            patchelf_install = \"patchelf\"\n        else:\n            patchelf_install = os.path.join(patchelf_install, \"bin\", \"patchelf\")\n        self.patchelf: str = patchelf_install\n\n    def list_dynamic_deps(self, objfile: str) -> list[str]:\n        out: str = (\n            subprocess.check_output(\n                [self.patchelf, \"--print-needed\", objfile], env=dict(self.env.items())\n            )\n            .decode(\"utf-8\")\n            .strip()\n        )\n        lines: list[str] = out.split(\"\\n\")\n        return lines\n\n    def rewrite_dep(\n        self,\n        objfile: str,\n        depname: str,\n        old_dep: str,\n        new_dep: str,\n        final_lib_dir: str,\n    ) -> None:\n        final_dep: str = os.path.join(\n            final_lib_dir,\n            os.path.relpath(new_dep, self.munged_lib_dir),\n        )\n        self.check_call_verbose(\n            [self.patchelf, \"--replace-needed\", depname, final_dep, objfile]\n        )\n\n    def is_objfile(self, objfile: str) -> bool:\n        if not os.path.isfile(objfile):\n            return False\n        with open(objfile, \"rb\") as f:\n            # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header\n            magic: bytes = f.read(4)\n            return magic == b\"\\x7fELF\"\n\n    def strip_debug_info(self, objfile: str) -> None:\n        self.check_call_verbose([\"strip\", objfile])\n\n\n# MACH-O magic number\nMACH_MAGIC: int = 0xFEEDFACF\n\n\nclass MachDeps(DepBase):\n    def interesting_dep(self, d: str) -> bool:\n        if d.startswith(\"/usr/lib/\") or d.startswith(\"/System/\"):\n            return False\n        return True\n\n    def is_objfile(self, objfile: str) -> bool:\n        if not os.path.isfile(objfile):\n            return False\n        with open(objfile, \"rb\") as f:\n            # mach stores the magic number in native endianness,\n            # so unpack as native here and compare\n            header: bytes = f.read(4)\n            if len(header) != 4:\n                return False\n            magic: int = unpack(\"I\", header)[0]\n            return magic == MACH_MAGIC\n\n    def list_dynamic_deps(self, objfile: str) -> list[str]:\n        if not self.interesting_dep(objfile):\n            return []\n        out: str = (\n            subprocess.check_output(\n                [\"otool\", \"-L\", objfile], env=dict(self.env.items())\n            )\n            .decode(\"utf-8\")\n            .strip()\n        )\n        lines: list[str] = out.split(\"\\n\")\n        deps: list[str] = []\n        for line in lines:\n            m: re.Match[str] | None = re.match(\"\\t(\\\\S+)\\\\s\", line)\n            if m:\n                if os.path.basename(m.group(1)) != os.path.basename(objfile):\n                    deps.append(os.path.normcase(m.group(1)))\n        return deps\n\n    def rewrite_dep(\n        self,\n        objfile: str,\n        depname: str,\n        old_dep: str,\n        new_dep: str,\n        final_lib_dir: str,\n    ) -> None:\n        if objfile.endswith(\".dylib\"):\n            # Erase the original location from the id of the shared\n            # object.  It doesn't appear to hurt to retain it, but\n            # it does look weird, so let's rewrite it to be sure.\n            self.check_call_verbose(\n                [\"install_name_tool\", \"-id\", os.path.basename(objfile), objfile]\n            )\n        final_dep: str = os.path.join(\n            final_lib_dir,\n            os.path.relpath(new_dep, self.munged_lib_dir),\n        )\n\n        self.check_call_verbose(\n            [\"install_name_tool\", \"-change\", depname, final_dep, objfile]\n        )\n\n\ndef create_dyn_dep_munger(\n    buildopts: BuildOptions,\n    env: Env,\n    install_dirs: list[str],\n    strip: bool = False,\n) -> DepBase | None:\n    if buildopts.is_linux():\n        return ElfDeps(buildopts, env, install_dirs, strip)\n    if buildopts.is_darwin():\n        return MachDeps(buildopts, env, install_dirs, strip)\n    if buildopts.is_windows():\n        return WinDeps(buildopts, env, install_dirs, strip)\n    if buildopts.is_freebsd():\n        return ElfDeps(buildopts, env, install_dirs, strip)\n    return None\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/envfuncs.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\nimport os\nimport shlex\nimport sys\nfrom collections.abc import ItemsView, Iterator, KeysView, Mapping, ValuesView\n\n\nclass Env:\n    def __init__(self, src: Mapping[str, str] | None = None) -> None:\n        self._dict: dict[str, str] = {}\n        if src is None:\n            self.update(os.environ)\n        else:\n            self.update(src)\n\n    def update(self, src: Mapping[str, str]) -> None:\n        for k, v in src.items():\n            self.set(k, v)\n\n    def copy(self) -> Env:\n        return Env(self._dict)\n\n    def _key(self, key: str) -> str | None:\n        # The `str` cast may not appear to be needed, but without it we run\n        # into issues when passing the environment to subprocess.  The main\n        # issue is that in python2 `os.environ` (which is the initial source\n        # of data for the environment) uses byte based strings, but this\n        # project uses `unicode_literals`.  `subprocess` will raise an error\n        # if the environment that it is passed has a mixture of byte and\n        # unicode strings.\n        # It is simplest to force everything to be `str` for the sake of\n        # consistency.\n        key = str(key)\n        if sys.platform.startswith(\"win\"):\n            # Windows env var names are case insensitive but case preserving.\n            # An implementation of PAR files on windows gets confused if\n            # the env block contains keys with conflicting case, so make a\n            # pass over the contents to remove any.\n            # While this O(n) scan is technically expensive and gross, it\n            # is practically not a problem because the volume of calls is\n            # relatively low and the cost of manipulating the env is dwarfed\n            # by the cost of spawning a process on windows.  In addition,\n            # since the processes that we run are expensive anyway, this\n            # overhead is not the worst thing to worry about.\n            for k in list(self._dict.keys()):\n                if str(k).lower() == key.lower():\n                    return k\n        elif key in self._dict:\n            return key\n        return None\n\n    def get(self, key: str, defval: str | None = None) -> str | None:\n        resolved_key = self._key(key)\n        if resolved_key is None:\n            return defval\n        return self._dict[resolved_key]\n\n    def __getitem__(self, key: str) -> str:\n        val = self.get(key)\n        if val is None:\n            raise KeyError(key)\n        return val\n\n    def unset(self, key: str) -> None:\n        if key is None:\n            raise KeyError(\"attempting to unset env[None]\")\n\n        resolved_key = self._key(key)\n        if resolved_key:\n            del self._dict[resolved_key]\n\n    def __delitem__(self, key: str) -> None:\n        self.unset(key)\n\n    def __repr__(self) -> str:\n        return repr(self._dict)\n\n    def set(self, key: str, value: str) -> None:\n        if key is None:\n            raise KeyError(\"attempting to assign env[None] = %r\" % value)\n\n        if value is None:\n            raise ValueError(\"attempting to assign env[%s] = None\" % key)\n\n        # The `str` conversion is important to avoid triggering errors\n        # with subprocess if we pass in a unicode value; see commentary\n        # in the `_key` method.\n        key = str(key)\n        value = str(value)\n\n        # The `unset` call is necessary on windows where the keys are\n        # case insensitive.   Since this dict is case sensitive, simply\n        # assigning the value to the new key is not sufficient to remove\n        # the old value.  The `unset` call knows how to match keys and\n        # remove any potential duplicates.\n        self.unset(key)\n        self._dict[key] = value\n\n    def __setitem__(self, key: str, value: str) -> None:\n        self.set(key, value)\n\n    def __iter__(self) -> Iterator[str]:\n        return self._dict.__iter__()\n\n    def __len__(self) -> int:\n        return len(self._dict)\n\n    def keys(self) -> KeysView[str]:\n        return self._dict.keys()\n\n    def values(self) -> ValuesView[str]:\n        return self._dict.values()\n\n    def items(self) -> ItemsView[str, str]:\n        return self._dict.items()\n\n\ndef add_path_entry(\n    env: Env, name: str, item: str, append: bool = True, separator: str = os.pathsep\n) -> None:\n    \"\"\"Cause `item` to be added to the path style env var named\n    `name` held in the `env` dict.  `append` specifies whether\n    the item is added to the end (the default) or should be\n    prepended if `name` already exists.\"\"\"\n    val = env.get(name, \"\")\n    if val is not None and len(val) > 0:\n        val_list = val.split(separator)\n    else:\n        val_list = []\n    if append:\n        val_list.append(item)\n    else:\n        val_list.insert(0, item)\n    env.set(name, separator.join(val_list))\n\n\ndef add_flag(env: Env, name: str, flag: str, append: bool = True) -> None:\n    \"\"\"Cause `flag` to be added to the CXXFLAGS-style env var named\n    `name` held in the `env` dict.  `append` specifies whether the\n    flag is added to the end (the default) or should be prepended if\n    `name` already exists.\"\"\"\n    val = shlex.split(env.get(name, \"\") or \"\")\n    if append:\n        val.append(flag)\n    else:\n        val.insert(0, flag)\n    env.set(name, \" \".join(val))\n\n\n_path_search_cache: dict[object, str | None] = {}\n_not_found: object = object()\n\n\ndef tpx_path() -> str:\n    return \"xplat/testinfra/tpx/ctp.tpx\"\n\n\ndef path_search(\n    env: Mapping[str, str], exename: str, defval: str | None = None\n) -> str | None:\n    \"\"\"Search for exename in the PATH specified in env.\n    exename is eg: `ninja` and this function knows to append a .exe\n    to the end on windows.\n    Returns the path to the exe if found, or None if either no\n    PATH is set in env or no executable is found.\"\"\"\n\n    path = env.get(\"PATH\", None)\n    if path is None:\n        return defval\n\n    # The project hash computation code searches for C++ compilers (g++, clang, etc)\n    # repeatedly.  Cache the result so we don't end up searching for these over and over\n    # again.\n    cache_key = (path, exename)\n    result = _path_search_cache.get(cache_key, _not_found)\n    if result is _not_found:\n        result = _perform_path_search(path, exename)\n        _path_search_cache[cache_key] = result\n    # pyre-fixme[7]: Expected `Optional[str]` but got `Optional[object]`.\n    return result\n\n\ndef _perform_path_search(path: str, exename: str) -> str | None:\n    is_win = sys.platform.startswith(\"win\")\n    if is_win:\n        exename = \"%s.exe\" % exename\n\n    for bindir in path.split(os.pathsep):\n        full_name = os.path.join(bindir, exename)\n        if os.path.exists(full_name) and os.path.isfile(full_name):\n            if not is_win and not os.access(full_name, os.X_OK):\n                continue\n            return full_name\n\n    return None\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/errors.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\n\nclass TransientFailure(Exception):\n    \"\"\"Raising this error causes getdeps to return with an error code\n    that Sandcastle will consider to be a retryable transient\n    infrastructure error\"\"\"\n\n    pass\n\n\nclass ManifestNotFound(Exception):\n    def __init__(self, manifest_name: str) -> None:\n        super(Exception, self).__init__(\"Unable to find manifest '%s'\" % manifest_name)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/expr.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\nimport re\nimport shlex\nfrom collections.abc import Callable\n\n\ndef parse_expr(expr_text: str, valid_variables: set[str]) -> ExprNode:\n    \"\"\"parses the simple criteria expression syntax used in\n    dependency specifications.\n    Returns an ExprNode instance that can be evaluated like this:\n\n    ```\n    expr = parse_expr(\"os=windows\")\n    ok = expr.eval({\n        \"os\": \"windows\"\n    })\n    ```\n\n    Whitespace is allowed between tokens.  The following terms\n    are recognized:\n\n    KEY = VALUE   # Evaluates to True if ctx[KEY] == VALUE\n    not(EXPR)     # Evaluates to True if EXPR evaluates to False\n                  # and vice versa\n    all(EXPR1, EXPR2, ...) # Evaluates True if all of the supplied\n                           # EXPR's also evaluate True\n    any(EXPR1, EXPR2, ...) # Evaluates True if any of the supplied\n                           # EXPR's also evaluate True, False if\n                           # none of them evaluated true.\n    \"\"\"\n\n    p = Parser(expr_text, valid_variables)\n    return p.parse()\n\n\nclass ExprNode:\n    def eval(self, ctx: dict[str, str | None]) -> bool:\n        return False\n\n\nclass TrueExpr(ExprNode):\n    def eval(self, ctx: dict[str, str | None]) -> bool:\n        return True\n\n    def __str__(self) -> str:\n        return \"true\"\n\n\nclass NotExpr(ExprNode):\n    def __init__(self, node: ExprNode) -> None:\n        self._node: ExprNode = node\n\n    def eval(self, ctx: dict[str, str | None]) -> bool:\n        return not self._node.eval(ctx)\n\n    def __str__(self) -> str:\n        return \"not(%s)\" % self._node\n\n\nclass AllExpr(ExprNode):\n    def __init__(self, nodes: list[ExprNode]) -> None:\n        self._nodes: list[ExprNode] = nodes\n\n    def eval(self, ctx: dict[str, str | None]) -> bool:\n        for node in self._nodes:\n            if not node.eval(ctx):\n                return False\n        return True\n\n    def __str__(self) -> str:\n        items: list[str] = []\n        for node in self._nodes:\n            items.append(str(node))\n        return \"all(%s)\" % \",\".join(items)\n\n\nclass AnyExpr(ExprNode):\n    def __init__(self, nodes: list[ExprNode]) -> None:\n        self._nodes: list[ExprNode] = nodes\n\n    def eval(self, ctx: dict[str, str | None]) -> bool:\n        for node in self._nodes:\n            if node.eval(ctx):\n                return True\n        return False\n\n    def __str__(self) -> str:\n        items: list[str] = []\n        for node in self._nodes:\n            items.append(str(node))\n        return \"any(%s)\" % \",\".join(items)\n\n\nclass EqualExpr(ExprNode):\n    def __init__(self, key: str, value: str) -> None:\n        self._key: str = key\n        self._value: str = value\n\n    def eval(self, ctx: dict[str, str | None]) -> bool:\n        return ctx.get(self._key) == self._value\n\n    def __str__(self) -> str:\n        return \"%s=%s\" % (self._key, self._value)\n\n\nclass Parser:\n    def __init__(self, text: str, valid_variables: set[str]) -> None:\n        self.text: str = text\n        self.lex: shlex.shlex = shlex.shlex(text)\n        self.valid_variables: set[str] = valid_variables\n\n    def parse(self) -> ExprNode:\n        expr = self.top()\n        garbage = self.lex.get_token()\n        if garbage != \"\":\n            raise Exception(\n                \"Unexpected token %s after EqualExpr in %s\" % (garbage, self.text)\n            )\n        return expr\n\n    def top(self) -> ExprNode:\n        name = self.ident()\n        op = self.lex.get_token()\n\n        if op == \"(\":\n            parsers: dict[str, Callable[[], ExprNode]] = {\n                \"not\": self.parse_not,\n                \"any\": self.parse_any,\n                \"all\": self.parse_all,\n            }\n            func = parsers.get(name)\n            if not func:\n                raise Exception(\"invalid term %s in %s\" % (name, self.text))\n            return func()\n\n        if op == \"=\":\n            if name not in self.valid_variables:\n                raise Exception(\"unknown variable %r in expression\" % (name,))\n            # remove shell quote from value so can test things with period in them, e.g \"18.04\"\n            token = self.lex.get_token()\n            if token is None:\n                raise Exception(\"unexpected end of expression in %s\" % self.text)\n            unquoted = \" \".join(shlex.split(token))\n            return EqualExpr(name, unquoted)\n\n        raise Exception(\n            \"Unexpected token sequence '%s %s' in %s\" % (name, op, self.text)\n        )\n\n    def ident(self) -> str:\n        ident = self.lex.get_token()\n        if ident is None or not re.match(\"[a-zA-Z]+\", ident):\n            raise Exception(\"expected identifier found %s\" % ident)\n        return ident\n\n    def parse_not(self) -> NotExpr:\n        node = self.top()\n        expr = NotExpr(node)\n        tok = self.lex.get_token()\n        if tok != \")\":\n            raise Exception(\"expected ')' found %s\" % tok)\n        return expr\n\n    def parse_any(self) -> AnyExpr:\n        nodes: list[ExprNode] = []\n        while True:\n            nodes.append(self.top())\n            tok = self.lex.get_token()\n            if tok == \")\":\n                break\n            if tok != \",\":\n                raise Exception(\"expected ',' or ')' but found %s\" % tok)\n        return AnyExpr(nodes)\n\n    def parse_all(self) -> AllExpr:\n        nodes: list[ExprNode] = []\n        while True:\n            nodes.append(self.top())\n            tok = self.lex.get_token()\n            if tok == \")\":\n                break\n            if tok != \",\":\n                raise Exception(\"expected ',' or ')' but found %s\" % tok)\n        return AllExpr(nodes)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/fetcher.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\nfrom __future__ import annotations\n\n# pyre-strict\n\nimport errno\nimport hashlib\nimport os\nimport random\nimport re\nimport shlex\nimport shutil\nimport stat\nimport subprocess\nimport sys\nimport tarfile\nimport time\nimport zipfile\nfrom abc import ABC, abstractmethod\nfrom collections.abc import Iterator\nfrom datetime import datetime\nfrom typing import NamedTuple, TYPE_CHECKING\nfrom urllib.parse import urlparse\nfrom urllib.request import Request, urlopen\n\nfrom .copytree import prefetch_dir_if_eden\nfrom .envfuncs import Env\nfrom .errors import TransientFailure\nfrom .platform import HostType, is_windows\nfrom .runcmd import run_cmd\n\nif TYPE_CHECKING:\n    from .buildopts import BuildOptions\n    from .manifest import ManifestContext, ManifestParser\n\n\ndef file_name_is_cmake_file(file_name: str) -> bool:\n    file_name = file_name.lower()\n    base = os.path.basename(file_name)\n    return (\n        base.endswith(\".cmake\")\n        or base.endswith(\".cmake.in\")\n        or base == \"cmakelists.txt\"\n    )\n\n\nclass ChangeStatus:\n    \"\"\"Indicates the nature of changes that happened while updating\n    the source directory.  There are two broad uses:\n    * When extracting archives for third party software we want to\n      know that we did something (eg: we either extracted code or\n      we didn't do anything)\n    * For 1st party code where we use shipit to transform the code,\n      we want to know if we changed anything so that we can perform\n      a build, but we generally want to be a little more nuanced\n      and be able to distinguish between just changing a source file\n      and whether we might need to reconfigure the build system.\n    \"\"\"\n\n    def __init__(self, all_changed: bool = False) -> None:\n        \"\"\"Construct a ChangeStatus object.  The default is to create\n        a status that indicates no changes, but passing all_changed=True\n        will create one that indicates that everything changed\"\"\"\n        if all_changed:\n            self.source_files: int = 1\n            self.make_files: int = 1\n        else:\n            self.source_files: int = 0\n            self.make_files: int = 0\n\n    def record_change(self, file_name: str) -> None:\n        \"\"\"Used by the shipit fetcher to record changes as it updates\n        files in the destination.  If the file name might be one used\n        in the cmake build system that we use for 1st party code, then\n        record that as a \"make file\" change.  We could broaden this\n        to match any file used by various build systems, but it is\n        only really useful for our internal cmake stuff at this time.\n        If the file isn't a build file and is under the `fbcode_builder`\n        dir then we don't class that as an interesting change that we\n        might need to rebuild, so we ignore it.\n        Otherwise we record the file as a source file change.\"\"\"\n\n        file_name = file_name.lower()\n        if file_name_is_cmake_file(file_name):\n            self.make_files += 1\n        elif \"/fbcode_builder/cmake\" in file_name:\n            self.source_files += 1\n        elif \"/fbcode_builder/\" not in file_name:\n            self.source_files += 1\n\n    def sources_changed(self) -> bool:\n        \"\"\"Returns true if any source files were changed during\n        an update operation.  This will typically be used to decide\n        that the build system to be run on the source dir in an\n        incremental mode\"\"\"\n        return self.source_files > 0\n\n    def build_changed(self) -> bool:\n        \"\"\"Returns true if any build files were changed during\n        an update operation.  This will typically be used to decidfe\n        that the build system should be reconfigured and re-run\n        as a full build\"\"\"\n        return self.make_files > 0\n\n\nclass Fetcher(ABC):\n    \"\"\"The Fetcher is responsible for fetching and extracting the\n    sources for project.  The Fetcher instance defines where the\n    extracted data resides and reports this to the consumer via\n    its `get_src_dir` method.\"\"\"\n\n    def update(self) -> ChangeStatus:\n        \"\"\"Brings the src dir up to date, ideally minimizing\n        changes so that a subsequent build doesn't over-build.\n        Returns a ChangeStatus object that helps the caller to\n        understand the nature of the changes required during\n        the update.\"\"\"\n        return ChangeStatus()\n\n    @abstractmethod\n    def clean(self) -> None:\n        \"\"\"Reverts any changes that might have been made to\n        the src dir\"\"\"\n        pass\n\n    @abstractmethod\n    def hash(self) -> str:\n        \"\"\"Returns a hash that identifies the version of the code in the\n        working copy.  For a git repo this is commit hash for the working\n        copy.  For other Fetchers this should relate to the version of\n        the code in the src dir.  The intent is that if a manifest\n        changes the version/rev of a project that the hash be different.\n        Importantly, this should be computable without actually fetching\n        the code, as we want this to factor into a hash used to download\n        a pre-built version of the code, without having to first download\n        and extract its sources (eg: boost on windows is pretty painful).\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def get_src_dir(self) -> str:\n        \"\"\"Returns the source directory that the project was\n        extracted into\"\"\"\n        pass\n\n\nclass LocalDirFetcher:\n    \"\"\"This class exists to override the normal fetching behavior, and\n    use an explicit user-specified directory for the project sources.\n\n    This fetcher cannot update or track changes.  It always reports that the\n    project has changed, forcing it to always be built.\"\"\"\n\n    def __init__(self, path: str) -> None:\n        self.path: str = os.path.realpath(path)\n\n    def update(self) -> ChangeStatus:\n        return ChangeStatus(all_changed=True)\n\n    def hash(self) -> str:\n        return \"0\" * 40\n\n    def get_src_dir(self) -> str:\n        return self.path\n\n    def clean(self) -> None:\n        pass\n\n\nclass SystemPackageFetcher:\n    def __init__(\n        self, build_options: BuildOptions, packages: dict[str, list[str]]\n    ) -> None:\n        self.manager: str | None = build_options.host_type.get_package_manager()\n        # pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.\n        self.packages: list[str] | None = packages.get(self.manager)\n        self.host_type: HostType = build_options.host_type\n        if self.packages:\n            self.installed: bool | None = None\n        else:\n            self.installed = False\n\n    def packages_are_installed(self) -> bool:\n        if self.installed is not None:\n            return self.installed\n\n        cmd = None\n        if self.manager == \"rpm\":\n            # pyre-fixme[6]: For 1st argument expected\n            #  `pyre_extensions.PyreReadOnly[Iterable[SupportsRichComparisonT]]` but\n            #  got `Optional[List[str]]`.\n            cmd = [\"rpm\", \"-q\"] + sorted(self.packages)\n        elif self.manager == \"deb\":\n            # pyre-fixme[6]: For 1st argument expected\n            #  `pyre_extensions.PyreReadOnly[Iterable[SupportsRichComparisonT]]` but\n            #  got `Optional[List[str]]`.\n            cmd = [\"dpkg\", \"-s\"] + sorted(self.packages)\n        elif self.manager == \"homebrew\":\n            # pyre-fixme[6]: For 1st argument expected\n            #  `pyre_extensions.PyreReadOnly[Iterable[SupportsRichComparisonT]]` but\n            #  got `Optional[List[str]]`.\n            cmd = [\"brew\", \"ls\", \"--versions\"] + sorted(self.packages)\n\n        if cmd:\n            proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n            if proc.returncode == 0:\n                # captured as binary as we will hash this later\n                # pyre-fixme[8]: Attribute has type `Optional[bool]`; used as `bytes`.\n                self.installed = proc.stdout\n            else:\n                # Need all packages to be present to consider us installed\n                self.installed = False\n\n        else:\n            self.installed = False\n\n        return bool(self.installed)\n\n    def update(self) -> ChangeStatus:\n        assert self.installed\n        return ChangeStatus(all_changed=False)\n\n    def hash(self) -> str:\n        if self.packages_are_installed():\n            return hashlib.sha256(self.installed).hexdigest()\n        else:\n            return \"0\" * 40\n\n    def get_src_dir(self) -> None:\n        return None\n\n\nclass PreinstalledNopFetcher(SystemPackageFetcher):\n    def __init__(self) -> None:\n        self.installed = True\n\n\nclass GitFetcher(Fetcher):\n    DEFAULT_DEPTH = 1\n\n    def __init__(\n        self,\n        build_options: BuildOptions,\n        manifest: ManifestParser,\n        repo_url: str,\n        rev: str,\n        depth: int,\n        branch: str,\n    ) -> None:\n        # Extract the host/path portions of the URL and generate a flattened\n        # directory name.  eg:\n        # github.com/facebook/folly.git -> github.com-facebook-folly.git\n        url = urlparse(repo_url)\n        directory = \"%s%s%s\" % (url.netloc, url.path, branch if branch else \"\")\n        for s in [\"/\", \"\\\\\", \":\"]:\n            directory = directory.replace(s, \"-\")\n\n        # Place it in a repos dir in the scratch space\n        repos_dir = os.path.join(build_options.scratch_dir, \"repos\")\n        if not os.path.exists(repos_dir):\n            os.makedirs(repos_dir)\n        self.repo_dir: str = os.path.join(repos_dir, directory)\n\n        if not rev and build_options.project_hashes:\n            hash_file = os.path.join(\n                build_options.project_hashes,\n                re.sub(\"\\\\.git$\", \"-rev.txt\", url.path[1:]),\n            )\n            if os.path.exists(hash_file):\n                with open(hash_file, \"r\") as f:\n                    data = f.read()\n                    m = re.match(\"Subproject commit ([a-fA-F0-9]{40})\", data)\n                    if not m:\n                        raise Exception(\"Failed to parse rev from %s\" % hash_file)\n                    rev = m.group(1)\n                    print(\n                        \"Using pinned rev %s for %s\" % (rev, repo_url), file=sys.stderr\n                    )\n\n        self.rev: str = rev or branch or \"main\"\n        self.origin_repo: str = repo_url\n        self.manifest: ManifestParser = manifest\n        self.depth: int = depth if depth else GitFetcher.DEFAULT_DEPTH\n        self.branch: str = branch\n\n    def _update(self) -> ChangeStatus:\n        current_hash = (\n            subprocess.check_output([\"git\", \"rev-parse\", \"HEAD\"], cwd=self.repo_dir)\n            .strip()\n            .decode(\"utf-8\")\n        )\n        target_hash = (\n            subprocess.check_output([\"git\", \"rev-parse\", self.rev], cwd=self.repo_dir)\n            .strip()\n            .decode(\"utf-8\")\n        )\n        if target_hash == current_hash:\n            # It's up to date, so there are no changes.  This doesn't detect eg:\n            # if origin/main moved and rev='main', but that's ok for our purposes;\n            # we should be using explicit hashes or eg: a stable branch for the cases\n            # that we care about, and it isn't unreasonable to require that the user\n            # explicitly perform a clean build if those have moved.  For the most\n            # part we prefer that folks build using a release tarball from github\n            # rather than use the git protocol, as it is generally a bit quicker\n            # to fetch and easier to hash and verify tarball downloads.\n            return ChangeStatus()\n\n        print(\"Updating %s -> %s\" % (self.repo_dir, self.rev))\n        run_cmd([\"git\", \"fetch\", \"origin\", self.rev], cwd=self.repo_dir)\n        run_cmd([\"git\", \"checkout\", self.rev], cwd=self.repo_dir)\n        run_cmd([\"git\", \"submodule\", \"update\", \"--init\"], cwd=self.repo_dir)\n\n        return ChangeStatus(True)\n\n    def update(self) -> ChangeStatus:\n        if os.path.exists(self.repo_dir):\n            return self._update()\n        self._clone()\n        return ChangeStatus(True)\n\n    def _clone(self) -> None:\n        print(\"Cloning %s...\" % self.origin_repo)\n        # The basename/dirname stuff allows us to dance around issues where\n        # eg: this python process is native win32, but the git.exe is cygwin\n        # or msys and doesn't like the absolute windows path that we'd otherwise\n        # pass to it.  Careful use of cwd helps avoid headaches with cygpath.\n        cmd = [\n            \"git\",\n            \"clone\",\n            \"--depth=\" + str(self.depth),\n        ]\n        if self.branch:\n            cmd.append(\"--branch=\" + self.branch)\n        cmd += [\n            \"--\",\n            self.origin_repo,\n            os.path.basename(self.repo_dir),\n        ]\n        run_cmd(cmd, cwd=os.path.dirname(self.repo_dir))\n        self._update()\n\n    def clean(self) -> None:\n        if os.path.exists(self.repo_dir):\n            run_cmd([\"git\", \"clean\", \"-fxd\"], cwd=self.repo_dir)\n\n    def hash(self) -> str:\n        return self.rev\n\n    def get_src_dir(self) -> str:\n        return self.repo_dir\n\n\ndef does_file_need_update(\n    src_name: str, src_st: os.stat_result, dest_name: str\n) -> bool:\n    try:\n        target_st = os.lstat(dest_name)\n    except OSError as exc:\n        if exc.errno != errno.ENOENT:\n            raise\n        return True\n\n    if src_st.st_size != target_st.st_size:\n        return True\n\n    if stat.S_IFMT(src_st.st_mode) != stat.S_IFMT(target_st.st_mode):\n        return True\n    if stat.S_ISLNK(src_st.st_mode):\n        return os.readlink(src_name) != os.readlink(dest_name)\n    if not stat.S_ISREG(src_st.st_mode):\n        return True\n\n    # They might have the same content; compare.\n    with open(src_name, \"rb\") as sf, open(dest_name, \"rb\") as df:\n        chunk_size = 8192\n        while True:\n            src_data = sf.read(chunk_size)\n            dest_data = df.read(chunk_size)\n            if src_data != dest_data:\n                return True\n            if len(src_data) < chunk_size:\n                # EOF\n                break\n    return False\n\n\ndef copy_if_different(src_name: str, dest_name: str) -> bool:\n    \"\"\"Copy src_name -> dest_name, but only touch dest_name\n    if src_name is different from dest_name, making this a\n    more build system friendly way to copy.\"\"\"\n    src_st = os.lstat(src_name)\n    if not does_file_need_update(src_name, src_st, dest_name):\n        return False\n\n    dest_parent = os.path.dirname(dest_name)\n    if not os.path.exists(dest_parent):\n        os.makedirs(dest_parent)\n    if stat.S_ISLNK(src_st.st_mode):\n        try:\n            os.unlink(dest_name)\n        except OSError as exc:\n            if exc.errno != errno.ENOENT:\n                raise\n        target = os.readlink(src_name)\n        os.symlink(target, dest_name)\n    else:\n        shutil.copy2(src_name, dest_name)\n\n    return True\n\n\ndef filter_strip_marker(dest_name: str, marker: str) -> None:\n    \"\"\"Strip lines/blocks tagged with the given marker from a file.\"\"\"\n    try:\n        with open(dest_name, \"r\") as f:\n            content = f.read()\n    except (UnicodeDecodeError, PermissionError):\n        return\n\n    if marker not in content:\n        return\n\n    escaped = re.escape(marker)\n    block_re = re.compile(\n        r\"[^\\n]*\" + escaped + r\"-start[^\\n]*\\n.*?[^\\n]*\" + escaped + r\"-end[^\\n]*\\n?\",\n        re.DOTALL,\n    )\n    line_re = re.compile(r\".*\" + escaped + r\".*\\n?\")\n\n    filtered = block_re.sub(\"\", content)\n    filtered = line_re.sub(\"\", filtered)\n    if filtered != content:\n        with open(dest_name, \"w\") as f:\n            f.write(filtered)\n\n\ndef list_files_under_dir_newer_than_timestamp(\n    dir_to_scan: str, ts: int\n) -> Iterator[str]:\n    for root, _dirs, files in os.walk(dir_to_scan):\n        for src_file in files:\n            full_name = os.path.join(root, src_file)\n            st = os.lstat(full_name)\n            if st.st_mtime > ts:\n                yield full_name\n\n\nclass ShipitPathMap:\n    def __init__(self) -> None:\n        self.roots: list[str] = []\n        self.mapping: list[str] = []\n        self.exclusion: list[str] = []\n        self.strip_marker: str = \"@fb-only\"\n\n    def add_mapping(self, fbsource_dir: str, target_dir: str) -> None:\n        \"\"\"Add a posix path or pattern.  We cannot normpath the input\n        here because that would change the paths from posix to windows\n        form and break the logic throughout this class.\"\"\"\n        self.roots.append(fbsource_dir)\n        # pyre-fixme[6]: For 1st argument expected `str` but got `Tuple[str, str]`.\n        self.mapping.append((fbsource_dir, target_dir))\n\n    def add_exclusion(self, pattern: str) -> None:\n        # pyre-fixme[6]: For 1st argument expected `str` but got `Pattern[str]`.\n        self.exclusion.append(re.compile(pattern))\n\n    def _minimize_roots(self) -> None:\n        \"\"\"compute the de-duplicated set of roots within fbsource.\n        We take the shortest common directory prefix to make this\n        determination\"\"\"\n        self.roots.sort(key=len)\n        minimized = []\n\n        for r in self.roots:\n            add_this_entry = True\n            for existing in minimized:\n                if r.startswith(existing + \"/\"):\n                    add_this_entry = False\n                    break\n            if add_this_entry:\n                minimized.append(r)\n\n        self.roots = minimized\n\n    def _sort_mapping(self) -> None:\n        self.mapping.sort(reverse=True, key=lambda x: len(x[0]))\n\n    def _map_name(self, norm_name: str, dest_root: str) -> str | None:\n        if norm_name.endswith(\".pyc\") or norm_name.endswith(\".swp\"):\n            # Ignore some incidental garbage while iterating\n            return None\n\n        for excl in self.exclusion:\n            # pyre-fixme[16]: `str` has no attribute `match`.\n            if excl.match(norm_name):\n                return None\n\n        for src_name, dest_name in self.mapping:\n            if norm_name == src_name or norm_name.startswith(src_name + \"/\"):\n                rel_name = os.path.relpath(norm_name, src_name)\n                # We can have \".\" as a component of some paths, depending\n                # on the contents of the shipit transformation section.\n                # normpath doesn't always remove `.` as the final component\n                # of the path, which be problematic when we later mkdir\n                # the dirname of the path that we return.  Take care to avoid\n                # returning a path with a `.` in it.\n                rel_name = os.path.normpath(rel_name)\n                if dest_name == \".\":\n                    return os.path.normpath(os.path.join(dest_root, rel_name))\n                dest_name = os.path.normpath(dest_name)\n                return os.path.normpath(os.path.join(dest_root, dest_name, rel_name))\n\n        raise Exception(\"%s did not match any rules\" % norm_name)\n\n    def mirror(self, fbsource_root: str, dest_root: str) -> ChangeStatus:\n        self._minimize_roots()\n        self._sort_mapping()\n\n        change_status = ChangeStatus()\n\n        # Record the full set of files that should be in the tree\n        full_file_list = set()\n\n        if sys.platform == \"win32\":\n            # Let's not assume st_dev has a consistent value on Windows.\n            def st_dev(path: str) -> int:\n                return 1\n\n        else:\n\n            def st_dev(path: str) -> int:\n                return os.lstat(path).st_dev\n\n        for fbsource_subdir in self.roots:\n            dir_to_mirror = os.path.join(fbsource_root, fbsource_subdir)\n            root_dev = st_dev(dir_to_mirror)\n            prefetch_dir_if_eden(dir_to_mirror)\n            if not os.path.exists(dir_to_mirror):\n                raise Exception(\n                    \"%s doesn't exist; check your sparse profile!\" % dir_to_mirror\n                )\n            update_count = 0\n            for root, dirs, files in os.walk(dir_to_mirror):\n                dirs[:] = [d for d in dirs if root_dev == st_dev(os.path.join(root, d))]\n\n                for src_file in files:\n                    full_name = os.path.join(root, src_file)\n                    rel_name = os.path.relpath(full_name, fbsource_root)\n                    norm_name = rel_name.replace(\"\\\\\", \"/\")\n\n                    target_name = self._map_name(norm_name, dest_root)\n                    if target_name:\n                        full_file_list.add(target_name)\n                        if copy_if_different(full_name, target_name):\n                            filter_strip_marker(target_name, self.strip_marker)\n                            change_status.record_change(target_name)\n                            if update_count < 10:\n                                print(\"Updated %s -> %s\" % (full_name, target_name))\n                            elif update_count == 10:\n                                print(\"...\")\n                            update_count += 1\n            if update_count:\n                print(\"Updated %s for %s\" % (update_count, fbsource_subdir))\n\n        # Compare the list of previously shipped files; if a file is\n        # in the old list but not the new list then it has been\n        # removed from the source and should be removed from the\n        # destination.\n        # Why don't we simply create this list by walking dest_root?\n        # Some builds currently have to be in-source builds and\n        # may legitimately need to keep some state in the source tree :-/\n        installed_name = os.path.join(dest_root, \".shipit_shipped\")\n        if os.path.exists(installed_name):\n            with open(installed_name, \"rb\") as f:\n                for name in f.read().decode(\"utf-8\").splitlines():\n                    name = name.strip()\n                    if name not in full_file_list:\n                        print(\"Remove %s\" % name)\n                        os.unlink(name)\n                        change_status.record_change(name)\n\n        with open(installed_name, \"wb\") as f:\n            for name in sorted(full_file_list):\n                f.write((\"%s\\n\" % name).encode(\"utf-8\"))\n\n        return change_status\n\n\nclass FbsourceRepoData(NamedTuple):\n    hash: str\n    date: str\n\n\nFBSOURCE_REPO_DATA: dict[str, FbsourceRepoData] = {}\n\n\ndef get_fbsource_repo_data(build_options: BuildOptions) -> FbsourceRepoData:\n    \"\"\"Returns the commit metadata for the fbsource repo.\n    Since we may have multiple first party projects to\n    hash, and because we don't mutate the repo, we cache\n    this hash in a global.\"\"\"\n    # pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.\n    cached_data = FBSOURCE_REPO_DATA.get(build_options.fbsource_dir)\n    if cached_data:\n        return cached_data\n\n    if \"GETDEPS_HG_REPO_DATA\" in os.environ:\n        log_data = os.environ[\"GETDEPS_HG_REPO_DATA\"]\n    else:\n        cmd = [\"hg\", \"log\", \"-r.\", \"-T{node}\\n{date|hgdate}\"]\n        env = Env()\n        env.set(\"HGPLAIN\", \"1\")\n        log_data = subprocess.check_output(\n            cmd, cwd=build_options.fbsource_dir, env=dict(env.items())\n        ).decode(\"ascii\")\n\n    (hash, datestr) = log_data.split(\"\\n\")\n\n    # datestr is like \"seconds fractionalseconds\"\n    # We want \"20200324.113140\"\n    (unixtime, _fractional) = datestr.split(\" \")\n    date = datetime.fromtimestamp(int(unixtime)).strftime(\"%Y%m%d.%H%M%S\")\n    cached_data = FbsourceRepoData(hash=hash, date=date)\n\n    # pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.\n    FBSOURCE_REPO_DATA[build_options.fbsource_dir] = cached_data\n\n    return cached_data\n\n\ndef is_public_commit(build_options: BuildOptions) -> bool:  # noqa: C901\n    \"\"\"Check if the current commit is public (shipped/will be shipped to remote).\n\n    Works across git, sapling (sl), and hg repositories:\n    - For hg/sapling: Uses 'phase' command to check if commit is public\n    - For git: Checks if commit exists in remote branches\n\n    Returns True if public, False if draft/local-only or on error (conservative).\n    \"\"\"\n    # Use fbsource_dir if available (Meta internal), otherwise fall back to repo_root\n    repo_dir = build_options.fbsource_dir or build_options.repo_root\n    if not repo_dir:\n        # No repository detected, be conservative\n        return False\n\n    env = Env()\n    env.set(\"HGPLAIN\", \"1\")\n    env_dict = dict(env.items())\n\n    try:\n        # Try hg/sapling phase command first (works for both hg and sl)\n        # Try 'sl' first as it's the preferred tool at Meta\n        for cmd in [[\"sl\", \"phase\", \"-r\", \".\"], [\"hg\", \"phase\", \"-r\", \".\"]]:\n            try:\n                output = (\n                    subprocess.check_output(\n                        cmd, cwd=repo_dir, env=env_dict, stderr=subprocess.DEVNULL\n                    )\n                    .decode(\"ascii\")\n                    .strip()\n                )\n                # Output format: \"hash: public\" or \"hash: draft\"\n                return \"public\" in output\n            except (subprocess.CalledProcessError, FileNotFoundError):\n                continue\n\n        # Try git if hg/sl didn't work\n        try:\n            # Detect the default branch for origin remote\n            default_branch = None\n            try:\n                # Get the symbolic ref for origin/HEAD to find default branch\n                output = (\n                    subprocess.check_output(\n                        [\"git\", \"symbolic-ref\", \"refs/remotes/origin/HEAD\"],\n                        cwd=repo_dir,\n                        stderr=subprocess.DEVNULL,\n                    )\n                    .decode(\"ascii\")\n                    .strip()\n                )\n                # Output format: \"refs/remotes/origin/main\"\n                if output.startswith(\"refs/remotes/\"):\n                    default_branch = output\n            except subprocess.CalledProcessError:\n                # If symbolic-ref fails, fall back to common names\n                pass\n\n            # Build list of branches to check\n            branches_to_check = []\n            if default_branch:\n                branches_to_check.append(default_branch)\n            # Also try common defaults as fallback\n            branches_to_check.extend([\"origin/main\", \"origin/master\"])\n\n            # Check if HEAD is an ancestor of any of these branches\n            for branch in branches_to_check:\n                try:\n                    subprocess.check_output(\n                        [\"git\", \"merge-base\", \"--is-ancestor\", \"HEAD\", branch],\n                        cwd=repo_dir,\n                        stderr=subprocess.DEVNULL,\n                    )\n                    # If command succeeds (exit 0), HEAD is an ancestor of the branch\n                    return True\n                except subprocess.CalledProcessError:\n                    # Not an ancestor of this branch, try next\n                    continue\n            # HEAD is not in any default branch\n            return False\n        except FileNotFoundError:\n            pass\n\n        # If all VCS commands failed, be conservative and don't upload\n        return False\n\n    except Exception:\n        # On any unexpected error, be conservative and don't upload\n        return False\n\n\nclass SimpleShipitTransformerFetcher(Fetcher):\n    def __init__(\n        self,\n        build_options: BuildOptions,\n        manifest: ManifestParser,\n        ctx: ManifestContext,\n    ) -> None:\n        self.build_options: BuildOptions = build_options\n        self.manifest: ManifestParser = manifest\n        self.repo_dir: str = os.path.join(\n            build_options.scratch_dir, \"shipit\", manifest.name\n        )\n        self.ctx: ManifestContext = ctx\n\n    def clean(self) -> None:\n        if os.path.exists(self.repo_dir):\n            shutil.rmtree(self.repo_dir)\n\n    def update(self) -> ChangeStatus:\n        mapping = ShipitPathMap()\n        for src, dest in self.manifest.get_section_as_ordered_pairs(\n            \"shipit.pathmap\", self.ctx\n        ):\n            # pyre-fixme[6]: For 2nd argument expected `str` but got `Optional[str]`.\n            mapping.add_mapping(src, dest)\n        if self.manifest.shipit_fbcode_builder:\n            mapping.add_mapping(\n                \"fbcode/opensource/fbcode_builder\", \"build/fbcode_builder\"\n            )\n        for pattern in self.manifest.get_section_as_args(\"shipit.strip\", self.ctx):\n            mapping.add_exclusion(pattern)\n\n        # pyre-fixme[8]: Attribute has type `str`; used as `Optional[str]`.\n        mapping.strip_marker = self.manifest.shipit_strip_marker\n\n        # pyre-fixme[6]: In call `ShipitPathMap.mirror`, for 1st positional argument, expected `str` but got `Optional[str]`\n        return mapping.mirror(self.build_options.fbsource_dir, self.repo_dir)\n\n    def hash(self) -> str:\n        # We return a fixed non-hash string for in-fbsource builds.\n        # We're relying on the `update` logic to correctly invalidate\n        # the build in the case that files have changed.\n        return \"fbsource\"\n\n    def get_src_dir(self) -> str:\n        return self.repo_dir\n\n\nclass SubFetcher(Fetcher):\n    \"\"\"Fetcher for a project with subprojects\"\"\"\n\n    def __init__(self, base: Fetcher, subs: list[tuple[Fetcher, str]]) -> None:\n        self.base: Fetcher = base\n        self.subs: list[tuple[Fetcher, str]] = subs\n\n    def update(self) -> ChangeStatus:\n        base = self.base.update()\n        changed = base.build_changed() or base.sources_changed()\n        for fetcher, dir in self.subs:\n            stat = fetcher.update()\n            if stat.build_changed() or stat.sources_changed():\n                changed = True\n            link = self.base.get_src_dir() + \"/\" + dir\n            if not os.path.exists(link):\n                os.symlink(fetcher.get_src_dir(), link)\n        return ChangeStatus(changed)\n\n    def clean(self) -> None:\n        self.base.clean()\n        for fetcher, _ in self.subs:\n            fetcher.clean()\n\n    def hash(self) -> str:\n        my_hash = self.base.hash()\n        for fetcher, _ in self.subs:\n            my_hash += fetcher.hash()\n        return my_hash\n\n    def get_src_dir(self) -> str:\n        return self.base.get_src_dir()\n\n\nclass ShipitTransformerFetcher(Fetcher):\n    @classmethod\n    def _shipit_paths(cls, build_options: BuildOptions) -> list[str]:\n        www_path = [\"/var/www/scripts/opensource/codesync\"]\n        if build_options.fbsource_dir:\n            fbcode_path = [\n                os.path.join(\n                    build_options.fbsource_dir,\n                    \"fbcode/opensource/codesync/codesync-cli/codesync\",\n                )\n            ]\n        else:\n            fbcode_path = []\n        return www_path + fbcode_path\n\n    def __init__(\n        self, build_options: BuildOptions, project_name: str, external_branch: str\n    ) -> None:\n        self.build_options: BuildOptions = build_options\n        self.project_name: str = project_name\n        self.external_branch: str = external_branch\n        self.repo_dir: str = os.path.join(\n            build_options.scratch_dir, \"shipit\", project_name\n        )\n        self.shipit: str | None = None\n        for path in ShipitTransformerFetcher._shipit_paths(build_options):\n            if os.path.exists(path):\n                self.shipit = path\n                break\n\n    def update(self) -> ChangeStatus:\n        if os.path.exists(self.repo_dir):\n            return ChangeStatus()\n        self.run_shipit()\n        return ChangeStatus(True)\n\n    def clean(self) -> None:\n        if os.path.exists(self.repo_dir):\n            shutil.rmtree(self.repo_dir)\n\n    @classmethod\n    def available(cls, build_options: BuildOptions) -> bool:\n        return any(\n            os.path.exists(path)\n            for path in ShipitTransformerFetcher._shipit_paths(build_options)\n        )\n\n    def run_shipit(self) -> None:\n        tmp_path = self.repo_dir + \".new\"\n        try:\n            if os.path.exists(tmp_path):\n                shutil.rmtree(tmp_path)\n            os.makedirs(os.path.dirname(tmp_path), exist_ok=True)\n            cmd = [\n                self.shipit,\n                \"shipit\",\n                \"--project=\" + self.project_name,\n                \"--create-new-repo\",\n                # pyre-fixme[58]: `+` is not supported for operand types `str` and\n                #  `Optional[str]`.\n                \"--source-repo-dir=\" + self.build_options.fbsource_dir,\n                \"--source-branch=.\",\n                \"--skip-source-init\",\n                \"--skip-source-pull\",\n                \"--skip-source-clean\",\n                \"--skip-push\",\n                \"--destination-use-anonymous-https\",\n                \"--create-new-repo-output-path=\" + tmp_path,\n            ]\n            if self.external_branch:\n                cmd += [\n                    f\"--external-branch={self.external_branch}\",\n                ]\n\n            # Run shipit\n            # pyre-fixme[6]: For 1st argument expected `List[str]` but got\n            #  `List[Optional[str]]`.\n            run_cmd(cmd)\n\n            # Remove the .git directory from the repository it generated.\n            # There is no need to commit this.\n            repo_git_dir = os.path.join(tmp_path, \".git\")\n            shutil.rmtree(repo_git_dir)\n            os.rename(tmp_path, self.repo_dir)\n        except Exception:\n            # Clean up after a failed extraction\n            if os.path.exists(tmp_path):\n                shutil.rmtree(tmp_path)\n            self.clean()\n            raise\n\n    def hash(self) -> str:\n        # We return a fixed non-hash string for in-fbsource builds.\n        return \"fbsource\"\n\n    def get_src_dir(self) -> str:\n        return self.repo_dir\n\n\ndef download_url_to_file_with_progress(url: str, file_name: str) -> None:\n    print(\"Download with %s -> %s ...\" % (url, file_name))\n\n    class Progress:\n        last_report: float = 0\n\n        def write_update(self, total: int, amount: int) -> None:\n            if total == -1:\n                total = \"(Unknown)\"\n\n            if sys.stdout.isatty():\n                sys.stdout.write(\"\\r downloading %s of %s \" % (amount, total))\n            else:\n                # When logging to CI logs, avoid spamming the logs and print\n                # status every few seconds\n                now = time.time()\n                if now - self.last_report > 5:\n                    sys.stdout.write(\".. %s of %s \" % (amount, total))\n                    self.last_report = now\n            sys.stdout.flush()\n\n        def progress_pycurl(\n            self, total: float, amount: float, _uploadtotal: float, _uploadamount: float\n        ) -> None:\n            self.write_update(total, amount)\n\n    progress = Progress()\n    start = time.time()\n    try:\n        if os.environ.get(\"GETDEPS_USE_WGET\") is not None:\n            procargs = (\n                [\n                    \"wget\",\n                ]\n                + os.environ.get(\"GETDEPS_WGET_ARGS\", \"\").split()\n                + [\n                    \"-O\",\n                    file_name,\n                    url,\n                ]\n            )\n            subprocess.run(procargs, capture_output=True)\n            headers = None\n\n        elif os.environ.get(\"GETDEPS_USE_LIBCURL\") is not None:\n            import pycurl\n\n            with open(file_name, \"wb\") as f:\n                c = pycurl.Curl()\n                c.setopt(pycurl.URL, url)\n                c.setopt(pycurl.WRITEDATA, f)\n                # display progress\n                c.setopt(pycurl.NOPROGRESS, False)\n                c.setopt(pycurl.XFERINFOFUNCTION, progress.progress_pycurl)\n                c.perform()\n                c.close()\n            headers = None\n        else:\n            try:\n                req_header = {\"Accept\": \"application/*\"}\n                res = urlopen(Request(url, None, req_header))\n                chunk_size = 8192  # urlretrieve uses this value\n                headers = res.headers\n                content_length = res.headers.get(\"Content-Length\")\n                total = int(content_length.strip()) if content_length else -1\n                amount = 0\n                with open(file_name, \"wb\") as f:\n                    chunk = res.read(chunk_size)\n                    while chunk:\n                        f.write(chunk)\n                        amount += len(chunk)\n                        progress.write_update(total, amount)\n                        chunk = res.read(chunk_size)\n            except (OSError, IOError) as exc:  # noqa: B014\n                # Downloading from within Meta's network needs to use a proxy.\n                if shutil.which(\"fwdproxy-config\") is None:\n                    print(\n                        \"Note: Could not find Meta-specific fallback 'fwdproxy-config'. \"\n                        \"If you are working externally, you can ignore this message.\"\n                    )\n                    raise\n\n                print(\"Default download failed, retrying with curl and fwdproxy...\")\n                cmd = f\"curl -L $(fwdproxy-config curl) -o {shlex.quote(file_name)} {shlex.quote(url)}\"\n                print(f\"Running command: {cmd}\")\n                result = subprocess.run(cmd, shell=True, capture_output=True)\n                if result.returncode != 0:\n                    raise TransientFailure(\n                        f\"Failed to download {url} to {file_name}: {exc} (fwdproxy fallback failed: {result.stderr.decode()})\"\n                    )\n                headers = None\n    except (OSError, IOError) as exc:  # noqa: B014\n        raise TransientFailure(\n            \"Failed to download %s to %s: %s\" % (url, file_name, str(exc))\n        )\n\n    end = time.time()\n    sys.stdout.write(\" [Complete in %f seconds]\\n\" % (end - start))\n    sys.stdout.flush()\n    if headers is not None:\n        print(f\"{headers}\")\n\n\nclass ArchiveFetcher(Fetcher):\n    def __init__(\n        self,\n        build_options: BuildOptions,\n        manifest: ManifestParser,\n        url: str,\n        sha256: str,\n    ) -> None:\n        self.manifest: ManifestParser = manifest\n        self.url: str = url\n        self.sha256: str = sha256\n        self.build_options: BuildOptions = build_options\n\n        parsed_url = urlparse(self.url)\n        basename = \"%s-%s\" % (manifest.name, os.path.basename(parsed_url.path))\n        self.file_name: str = os.path.join(\n            build_options.scratch_dir, \"downloads\", basename\n        )\n        self.src_dir: str = os.path.join(\n            build_options.scratch_dir, \"extracted\", basename\n        )\n        self.hash_file: str = self.src_dir + \".hash\"\n\n    def _verify_hash(self) -> None:\n        h = hashlib.sha256()\n        with open(self.file_name, \"rb\") as f:\n            while True:\n                block = f.read(8192)\n                if not block:\n                    break\n                h.update(block)\n        digest = h.hexdigest()\n        if digest != self.sha256:\n            os.unlink(self.file_name)\n            raise Exception(\n                \"%s: expected sha256 %s but got %s\" % (self.url, self.sha256, digest)\n            )\n\n    def _download_dir(self) -> str:\n        \"\"\"returns the download dir, creating it if it doesn't already exist\"\"\"\n        download_dir = os.path.dirname(self.file_name)\n        if not os.path.exists(download_dir):\n            os.makedirs(download_dir)\n        return download_dir\n\n    def _download(self) -> None:\n        self._download_dir()\n        max_attempts = 5\n        delay = 1\n        for attempt in range(max_attempts):\n            try:\n                download_url_to_file_with_progress(self.url, self.file_name)\n                break\n            except TransientFailure as tf:\n                if attempt < max_attempts - 1:\n                    delay *= 2\n                    delay_with_jitter = delay * (1 + random.random() * 0.1)\n                    time.sleep(min(delay_with_jitter, 10))\n                else:\n                    print(f\"Failed after retries: {tf}\")\n                    raise\n        self._verify_hash()\n\n    def clean(self) -> None:\n        if os.path.exists(self.src_dir):\n            shutil.rmtree(self.src_dir)\n\n    def update(self) -> ChangeStatus:\n        try:\n            with open(self.hash_file, \"r\") as f:\n                saved_hash = f.read().strip()\n                if saved_hash == self.sha256 and os.path.exists(self.src_dir):\n                    # Everything is up to date\n                    return ChangeStatus()\n                print(\n                    \"saved hash %s doesn't match expected hash %s, re-validating\"\n                    % (saved_hash, self.sha256)\n                )\n                os.unlink(self.hash_file)\n        except EnvironmentError:\n            pass\n\n        # If we got here we know the contents of src_dir are either missing\n        # or wrong, so blow away whatever happened to be there first.\n        if os.path.exists(self.src_dir):\n            shutil.rmtree(self.src_dir)\n\n        # If we already have a file here, make sure it looks legit before\n        # proceeding: any errors and we just remove it and re-download\n        if os.path.exists(self.file_name):\n            try:\n                self._verify_hash()\n            except Exception:\n                if os.path.exists(self.file_name):\n                    os.unlink(self.file_name)\n\n        if not os.path.exists(self.file_name):\n            self._download()\n            self._verify_hash()\n\n        if tarfile.is_tarfile(self.file_name):\n            opener = tarfile.open\n        elif zipfile.is_zipfile(self.file_name):\n            opener = zipfile.ZipFile\n        else:\n            raise Exception(\"don't know how to extract %s\" % self.file_name)\n        os.makedirs(self.src_dir)\n        print(\"Extract %s -> %s\" % (self.file_name, self.src_dir))\n        if is_windows():\n            # Ensure that we don't fall over when dealing with long paths\n            # on windows\n            src = r\"\\\\?\\%s\" % os.path.normpath(self.src_dir)\n        else:\n            src = self.src_dir\n\n        with opener(self.file_name) as t:\n            # The `str` here is necessary to ensure that we don't pass a unicode\n            # object down to tarfile.extractall on python2.  When extracting\n            # the boost tarball it makes some assumptions and tries to convert\n            # a non-ascii path to ascii and throws.\n            src = str(src)\n            t.extractall(src)\n\n        if is_windows():\n            subdir = self.manifest.get(\"build\", \"subdir\")\n            checkdir = src\n            if subdir:\n                checkdir = src + \"\\\\\" + subdir\n            if os.path.exists(checkdir):\n                children = os.listdir(checkdir)\n                print(f\"Extracted to {checkdir} contents: {children}\")\n\n        with open(self.hash_file, \"w\") as f:\n            f.write(self.sha256)\n\n        return ChangeStatus(True)\n\n    def hash(self) -> str:\n        return self.sha256\n\n    def get_src_dir(self) -> str:\n        return self.src_dir\n\n\ndef homebrew_package_prefix(package: str) -> str | None:\n    cmd = [\"brew\", \"--prefix\", package]\n    try:\n        proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    except FileNotFoundError:\n        return None\n\n    if proc.returncode == 0:\n        return proc.stdout.decode(\"utf-8\").rstrip()\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/include_rewriter.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\nfrom __future__ import annotations\n\n\"\"\"\nInclude Path Rewriter for getdeps\n\nThis module provides functionality to rewrite #include statements in C++ files\nto handle differences between fbcode and open source project structures.\n\"\"\"\n\nimport os\nimport re\nimport typing\nfrom pathlib import Path\nfrom typing import Any\n\nif typing.TYPE_CHECKING:\n    from .manifest import ManifestParser\n\n\nclass IncludePathRewriter:\n    \"\"\"Rewrites #include paths in C++ source files based on path mappings.\"\"\"\n\n    # C++ file extensions to process\n    CPP_EXTENSIONS: set[str] = {\n        \".cpp\",\n        \".cc\",\n        \".cxx\",\n        \".c\",\n        \".h\",\n        \".hpp\",\n        \".hxx\",\n        \".tcc\",\n        \".inc\",\n    }\n\n    def __init__(self, mappings: list[tuple[str, str]], verbose: bool = False) -> None:\n        \"\"\"\n        Initialize the rewriter with path mappings.\n\n        Args:\n            mappings: List of (old_path_prefix, new_path_prefix) tuples\n            verbose: Enable verbose output\n        \"\"\"\n        self.mappings: list[tuple[str, str]] = mappings\n        self.verbose: bool = verbose\n\n        # Compile regex patterns for efficiency\n        self.patterns: list[tuple[re.Pattern[str], str, str]] = []\n        for old_prefix, new_prefix in mappings:\n            # Match both quoted and angle bracket includes\n            # Pattern matches: #include \"old_prefix/rest\" or #include <old_prefix/rest>\n            pattern = re.compile(\n                r'(#\\s*include\\s*[<\"])(' + re.escape(old_prefix) + r'/[^\">]+)([\">])',\n                re.MULTILINE,\n            )\n            self.patterns.append((pattern, old_prefix, new_prefix))\n\n    def rewrite_file(self, file_path: Path, dry_run: bool = False) -> bool:\n        \"\"\"\n        Rewrite includes in a single file.\n\n        Args:\n            file_path: Path to the file to process\n            dry_run: If True, don't actually modify files\n\n        Returns:\n            True if file was modified, False otherwise\n        \"\"\"\n        try:\n            with open(file_path, \"r\", encoding=\"utf-8\") as f:\n                original_content: str = f.read()\n        except (IOError, UnicodeDecodeError) as e:\n            if self.verbose:\n                print(f\"Warning: Could not read {file_path}: {e}\")\n            return False\n\n        modified_content: str = original_content\n        changes_made: bool = False\n\n        for pattern, old_prefix, new_prefix in self.patterns:\n\n            def make_replace_func(\n                old_prefix: str, new_prefix: str\n            ) -> typing.Callable[[re.Match[str]], str]:\n                def replace_func(match: re.Match[str]) -> str:\n                    nonlocal changes_made\n                    prefix: str = match.group(1)  # #include [<\"]\n                    full_path: str = match.group(2)  # full path\n                    suffix: str = match.group(3)  # [\">]\n\n                    # Replace the old prefix with new prefix\n                    new_path: str = full_path.replace(old_prefix, new_prefix, 1)\n\n                    if self.verbose and not changes_made:\n                        print(f\"  {full_path} -> {new_path}\")\n\n                    changes_made = True\n                    return f\"{prefix}{new_path}{suffix}\"\n\n                return replace_func\n\n            modified_content = pattern.sub(\n                make_replace_func(old_prefix, new_prefix), modified_content\n            )\n\n        if changes_made and not dry_run:\n            try:\n                with open(file_path, \"w\", encoding=\"utf-8\") as f:\n                    f.write(modified_content)\n                if self.verbose:\n                    print(f\"Modified: {file_path}\")\n            except IOError as e:\n                print(f\"Error: Could not write {file_path}: {e}\")\n                return False\n        elif changes_made and dry_run:\n            if self.verbose:\n                print(f\"Would modify: {file_path}\")\n\n        return changes_made\n\n    def process_directory(self, source_dir: Path, dry_run: bool = False) -> int:\n        \"\"\"\n        Process all C++ files in a directory recursively.\n\n        Args:\n            source_dir: Root directory to process\n            dry_run: If True, don't actually modify files\n\n        Returns:\n            Number of files modified\n        \"\"\"\n        if not source_dir.exists():\n            if self.verbose:\n                print(f\"Warning: Directory {source_dir} does not exist\")\n            return 0\n\n        modified_count: int = 0\n        processed_count: int = 0\n\n        for root, dirs, files in os.walk(source_dir):\n            # Skip hidden directories and common build directories\n            dirs[:] = [\n                d\n                for d in dirs\n                if not d.startswith(\".\")\n                and d not in {\"build\", \"_build\", \"__pycache__\", \"CMakeFiles\"}\n            ]\n\n            for file in files:\n                file_path: Path = Path(root) / file\n\n                # Only process C++ files\n                if file_path.suffix.lower() not in self.CPP_EXTENSIONS:\n                    continue\n\n                processed_count += 1\n                if self.verbose:\n                    print(f\"Processing: {file_path}\")\n\n                if self.rewrite_file(file_path, dry_run):\n                    modified_count += 1\n\n        if self.verbose or modified_count > 0:\n            print(f\"Processed {processed_count} files, modified {modified_count} files\")\n        return modified_count\n\n\ndef rewrite_includes_from_manifest(\n    manifest: ManifestParser, ctx: Any, source_dir: str, verbose: bool = False\n) -> int:\n    \"\"\"\n    Rewrite includes using mappings from a manifest file.\n\n    Args:\n        manifest: The manifest object containing shipit.pathmap section\n        ctx: The manifest context\n        source_dir: Directory containing source files to process\n        verbose: Enable verbose output\n\n    Returns:\n        Number of files modified\n    \"\"\"\n    mappings: list[tuple[str, str]] = []\n\n    # Get mappings from the manifest's shipit.pathmap section\n    for src, dest in manifest.get_section_as_ordered_pairs(\"shipit.pathmap\", ctx):\n        # Remove fbcode/ or xplat/ prefixes from src since they won't appear in #include statements\n        if src.startswith(\"fbcode/\"):\n            src = src[len(\"fbcode/\") :]\n        elif src.startswith(\"xplat/\"):\n            src = src[len(\"xplat/\") :]\n        # pyre-fixme[6]: For 1st argument expected `Tuple[str, str]` but got\n        #  `Tuple[str, Optional[str]]`.\n        mappings.append((src, dest))\n\n    if not mappings:\n        if verbose:\n            print(\"No include path mappings found in manifest\")\n        return 0\n\n    if verbose:\n        print(\"Include path mappings:\")\n        for old_path, new_path in mappings:\n            print(f\"  {old_path} -> {new_path}\")\n\n    rewriter: IncludePathRewriter = IncludePathRewriter(mappings, verbose)\n    return rewriter.process_directory(Path(source_dir), dry_run=False)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/load.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\nfrom __future__ import annotations\n\nimport base64\nimport copy\nimport hashlib\nimport os\nimport typing\nfrom collections.abc import Iterator\n\nfrom . import fetcher\nfrom .envfuncs import path_search\nfrom .errors import ManifestNotFound\nfrom .manifest import ManifestParser\n\nif typing.TYPE_CHECKING:\n    from .buildopts import BuildOptions\n    from .manifest import ContextGenerator, ManifestContext\n\n\nclass Loader:\n    \"\"\"The loader allows our tests to patch the load operation\"\"\"\n\n    def _list_manifests(self, build_opts: BuildOptions) -> Iterator[str]:\n        \"\"\"Returns a generator that iterates all the available manifests\"\"\"\n        for path, _, files in os.walk(build_opts.manifests_dir):\n            for name in files:\n                # skip hidden files\n                if name.startswith(\".\"):\n                    continue\n\n                yield os.path.join(path, name)\n\n    def _load_manifest(self, path: str) -> ManifestParser:\n        return ManifestParser(path)\n\n    def load_project(\n        self, build_opts: BuildOptions, project_name: str\n    ) -> ManifestParser:\n        if \"/\" in project_name or \"\\\\\" in project_name:\n            # Assume this is a path already\n            return ManifestParser(project_name)\n\n        for manifest in self._list_manifests(build_opts):\n            if os.path.basename(manifest) == project_name:\n                return ManifestParser(manifest)\n\n        raise ManifestNotFound(project_name)\n\n    def load_all(self, build_opts: BuildOptions) -> dict[str, ManifestParser]:\n        manifests_by_name: dict[str, ManifestParser] = {}\n\n        for manifest in self._list_manifests(build_opts):\n            m = self._load_manifest(manifest)\n\n            if m.name in manifests_by_name:\n                raise Exception(\"found duplicate manifest '%s'\" % m.name)\n\n            manifests_by_name[m.name] = m\n\n        return manifests_by_name\n\n\nclass ResourceLoader(Loader):\n    def __init__(self, namespace: str, manifests_dir: str) -> None:\n        self.namespace: str = namespace\n        self.manifests_dir: str = manifests_dir\n\n    def _list_manifests(self, build_opts: BuildOptions) -> Iterator[str]:\n        import pkg_resources\n\n        dirs: list[str] = [self.manifests_dir]\n\n        while dirs:\n            current = dirs.pop(0)\n            for name in pkg_resources.resource_listdir(self.namespace, current):\n                path = \"%s/%s\" % (current, name)\n\n                if pkg_resources.resource_isdir(self.namespace, path):\n                    dirs.append(path)\n                else:\n                    yield \"%s/%s\" % (current, name)\n\n    def _find_manifest(self, project_name: str) -> str:\n        # pyre-fixme[20]: Call `ResourceLoader._list_manifests` expects argument `build_opts`.\n        for name in self._list_manifests():\n            if name.endswith(\"/%s\" % project_name):\n                return name\n\n        raise ManifestNotFound(project_name)\n\n    def _load_manifest(self, path: str) -> ManifestParser:\n        import pkg_resources\n\n        contents = pkg_resources.resource_string(self.namespace, path).decode(\"utf8\")\n        return ManifestParser(file_name=path, fp=contents)\n\n    def load_project(\n        self, build_opts: BuildOptions, project_name: str\n    ) -> ManifestParser:\n        project_name = self._find_manifest(project_name)\n        # pyre-fixme[16]: `ResourceLoader` has no attribute `_load_resource_manifest`.\n        return self._load_resource_manifest(project_name)\n\n\nLOADER: Loader = Loader()\n\n\ndef patch_loader(namespace: str, manifests_dir: str = \"manifests\") -> None:\n    global LOADER\n    LOADER = ResourceLoader(namespace, manifests_dir)\n\n\ndef load_project(build_opts: BuildOptions, project_name: str) -> ManifestParser:\n    \"\"\"given the name of a project or a path to a manifest file,\n    load up the ManifestParser instance for it and return it\"\"\"\n    return LOADER.load_project(build_opts, project_name)\n\n\ndef load_all_manifests(build_opts: BuildOptions) -> dict[str, ManifestParser]:\n    return LOADER.load_all(build_opts)\n\n\nclass ManifestLoader:\n    \"\"\"ManifestLoader stores information about project manifest relationships for a\n    given set of (build options + platform) configuration.\n\n    The ManifestLoader class primarily serves as a location to cache project dependency\n    relationships and project hash values for this build configuration.\n    \"\"\"\n\n    def __init__(\n        self, build_opts: BuildOptions, ctx_gen: ContextGenerator | None = None\n    ) -> None:\n        self._loader: Loader = LOADER\n        self.build_opts: BuildOptions = build_opts\n        if ctx_gen is None:\n            self.ctx_gen: ContextGenerator = self.build_opts.get_context_generator()\n        else:\n            self.ctx_gen = ctx_gen\n\n        self.manifests_by_name: dict[str, ManifestParser] = {}\n        self._loaded_all: bool = False\n        self._project_hashes: dict[str, str] = {}\n        self._fetcher_overrides: dict[str, fetcher.LocalDirFetcher] = {}\n        self._build_dir_overrides: dict[str, str] = {}\n        self._install_dir_overrides: dict[str, str] = {}\n        self._install_prefix_overrides: dict[str, str] = {}\n\n    def load_manifest(self, name: str) -> ManifestParser:\n        manifest = self.manifests_by_name.get(name)\n        if manifest is None:\n            manifest = self._loader.load_project(self.build_opts, name)\n            self.manifests_by_name[name] = manifest\n        return manifest\n\n    def load_all_manifests(self) -> dict[str, ManifestParser]:\n        if not self._loaded_all:\n            all_manifests_by_name = self._loader.load_all(self.build_opts)\n            if self.manifests_by_name:\n                # To help ensure that we only ever have a single manifest object for a\n                # given project, and that it can't change once we have loaded it,\n                # only update our mapping for projects that weren't already loaded.\n                for name, manifest in all_manifests_by_name.items():\n                    self.manifests_by_name.setdefault(name, manifest)\n            else:\n                self.manifests_by_name = all_manifests_by_name\n            self._loaded_all = True\n\n        return self.manifests_by_name\n\n    def dependencies_of(self, manifest: ManifestParser) -> list[ManifestParser]:\n        \"\"\"Returns the dependencies of the given project, not including the project itself, in topological order.\"\"\"\n        return [\n            dep\n            for dep in self.manifests_in_dependency_order(manifest)\n            if dep != manifest\n        ]\n\n    def manifests_in_dependency_order(\n        self, manifest: ManifestParser | None = None\n    ) -> list[ManifestParser]:\n        \"\"\"Compute all dependencies of the specified project.  Returns a list of the\n        dependencies plus the project itself, in topologically sorted order.\n\n        Each entry in the returned list only depends on projects that appear before it\n        in the list.\n\n        If the input manifest is None, the dependencies for all currently loaded\n        projects will be computed.  i.e., if you call load_all_manifests() followed by\n        manifests_in_dependency_order() this will return a global dependency ordering of\n        all projects.\"\"\"\n        # The list of deps that have been fully processed\n        seen: set[str] = set()\n        # The list of deps which have yet to be evaluated.  This\n        # can potentially contain duplicates.\n        if manifest is None:\n            deps: list[ManifestParser] = list(self.manifests_by_name.values())\n        else:\n            assert manifest.name in self.manifests_by_name\n            deps = [manifest]\n        # The list of manifests in dependency order\n        dep_order: list[ManifestParser] = []\n        system_packages: dict[str, list[str]] = {}\n\n        while len(deps) > 0:\n            m = deps.pop(0)\n            if m.name in seen:\n                continue\n\n            # Consider its deps, if any.\n            # We sort them for increased determinism; we'll produce\n            # a correct order even if they aren't sorted, but we prefer\n            # to produce the same order regardless of how they are listed\n            # in the project manifest files.\n            ctx: ManifestContext = self.ctx_gen.get_context(m.name)\n            dep_list: list[str] = m.get_dependencies(ctx)\n\n            dep_count: int = 0\n            for dep_name in dep_list:\n                # If we're not sure whether it is done, queue it up\n                if dep_name not in seen:\n                    dep = self.manifests_by_name.get(dep_name)\n                    if dep is None:\n                        dep = self._loader.load_project(self.build_opts, dep_name)\n                        self.manifests_by_name[dep.name] = dep\n\n                    deps.append(dep)\n                    dep_count += 1\n\n            if dep_count > 0:\n                # If we queued anything, re-queue this item, as it depends\n                # those new item(s) and their transitive deps.\n                deps.append(m)\n                continue\n\n            # Its deps are done, so we can emit it\n            seen.add(m.name)\n            # Capture system packages as we may need to set PATHs to then later\n            if (\n                self.build_opts.allow_system_packages\n                and self.build_opts.host_type.get_package_manager()\n            ):\n                packages: dict[str, list[str]] = m.get_required_system_packages(ctx)\n                for pkg_type, v in packages.items():\n                    merged: list[str] = system_packages.get(pkg_type, [])\n                    if v not in merged:\n                        merged += v\n                    system_packages[pkg_type] = merged\n                # A manifest depends on all system packages in it dependencies as well\n                # pyre-fixme[8]: Attribute has type `Dict[str, str]`; used as\n                #  `Dict[str, List[str]]`.\n                m.resolved_system_packages = copy.copy(system_packages)\n            dep_order.append(m)\n\n        return dep_order\n\n    def set_project_src_dir(self, project_name: str, path: str) -> None:\n        self._fetcher_overrides[project_name] = fetcher.LocalDirFetcher(path)\n\n    def set_project_build_dir(self, project_name: str, path: str) -> None:\n        self._build_dir_overrides[project_name] = path\n\n    def set_project_install_dir(self, project_name: str, path: str) -> None:\n        self._install_dir_overrides[project_name] = path\n\n    def set_project_install_prefix(self, project_name: str, path: str) -> None:\n        self._install_prefix_overrides[project_name] = path\n\n    def create_fetcher(\n        self, manifest: ManifestParser\n    ) -> fetcher.Fetcher | fetcher.LocalDirFetcher:\n        override = self._fetcher_overrides.get(manifest.name)\n        if override is not None:\n            return override\n\n        ctx: ManifestContext = self.ctx_gen.get_context(manifest.name)\n        return manifest.create_fetcher(self.build_opts, self, ctx)\n\n    def get_project_hash(self, manifest: ManifestParser) -> str:\n        h = self._project_hashes.get(manifest.name)\n        if h is None:\n            h = self._compute_project_hash(manifest)\n            self._project_hashes[manifest.name] = h\n        return h\n\n    def _compute_project_hash(self, manifest: ManifestParser) -> str:\n        \"\"\"This recursive function computes a hash for a given manifest.\n        The hash takes into account some environmental factors on the\n        host machine and includes the hashes of its dependencies.\n        No caching of the computation is performed, which is theoretically\n        wasteful but the computation is fast enough that it is not required\n        to cache across multiple invocations.\"\"\"\n        ctx: ManifestContext = self.ctx_gen.get_context(manifest.name)\n\n        hasher = hashlib.sha256()\n        # Some environmental and configuration things matter\n        env: dict[str, str | None] = {}\n        env[\"install_dir\"] = self.build_opts.install_dir\n        env[\"scratch_dir\"] = self.build_opts.scratch_dir\n        env[\"vcvars_path\"] = self.build_opts.vcvars_path\n        env[\"os\"] = self.build_opts.host_type.ostype\n        env[\"distro\"] = self.build_opts.host_type.distro\n        env[\"distro_vers\"] = self.build_opts.host_type.distrovers\n        env[\"shared_libs\"] = str(self.build_opts.shared_libs)\n        for name in [\n            \"CXXFLAGS\",\n            \"CPPFLAGS\",\n            \"LDFLAGS\",\n            \"CXX\",\n            \"CC\",\n            \"GETDEPS_CMAKE_DEFINES\",\n        ]:\n            env[name] = os.environ.get(name)\n        for tool in [\"cc\", \"c++\", \"gcc\", \"g++\", \"clang\", \"clang++\"]:\n            env[\"tool-%s\" % tool] = path_search(os.environ, tool)\n        for name in manifest.get_section_as_args(\"depends.environment\", ctx):\n            env[name] = os.environ.get(name)\n\n        fetcher_inst: fetcher.Fetcher | fetcher.LocalDirFetcher = self.create_fetcher(\n            manifest\n        )\n        env[\"fetcher.hash\"] = fetcher_inst.hash()\n\n        for name in sorted(env.keys()):\n            hasher.update(name.encode(\"utf-8\"))\n            value = env.get(name)\n            if value is not None:\n                try:\n                    hasher.update(value.encode(\"utf-8\"))\n                except AttributeError as exc:\n                    raise AttributeError(\"name=%r, value=%r: %s\" % (name, value, exc))\n\n        manifest.update_hash(hasher, ctx)\n\n        # If a patchfile is specified, include its contents in the hash\n        patchfile: str | None = manifest.get(\"build\", \"patchfile\", ctx=ctx)\n        if patchfile:\n            patchfile_path: str = os.path.join(\n                self.build_opts.fbcode_builder_dir, \"patches\", patchfile\n            )\n            if os.path.exists(patchfile_path):\n                with open(patchfile_path, \"rb\") as f:\n                    hasher.update(f.read())\n\n        dep_list: list[str] = manifest.get_dependencies(ctx)\n        for dep in dep_list:\n            dep_manifest: ManifestParser = self.load_manifest(dep)\n            dep_hash: str = self.get_project_hash(dep_manifest)\n            hasher.update(dep_hash.encode(\"utf-8\"))\n\n        # Use base64 to represent the hash, rather than the simple hex digest,\n        # so that the string is shorter.  Use the URL-safe encoding so that\n        # the hash can also be safely used as a filename component.\n        h: str = base64.urlsafe_b64encode(hasher.digest()).decode(\"ascii\")\n        # ... and because cmd.exe is troublesome with `=` signs, nerf those.\n        # They tend to be padding characters at the end anyway, so we can\n        # safely discard them.\n        h = h.replace(\"=\", \"\")\n\n        return h\n\n    def _get_project_dir_name(self, manifest: ManifestParser) -> str:\n        if manifest.is_first_party_project():\n            return manifest.name\n        else:\n            project_hash: str = self.get_project_hash(manifest)\n            return \"%s-%s\" % (manifest.name, project_hash)\n\n    def get_project_install_dir(self, manifest: ManifestParser) -> str:\n        override = self._install_dir_overrides.get(manifest.name)\n        if override:\n            return override\n\n        project_dir_name: str = self._get_project_dir_name(manifest)\n        return os.path.join(self.build_opts.install_dir, project_dir_name)\n\n    def get_project_build_dir(self, manifest: ManifestParser) -> str:\n        override = self._build_dir_overrides.get(manifest.name)\n        if override:\n            return override\n\n        project_dir_name: str = self._get_project_dir_name(manifest)\n        return os.path.join(self.build_opts.scratch_dir, \"build\", project_dir_name)\n\n    def get_project_install_prefix(self, manifest: ManifestParser) -> str | None:\n        return self._install_prefix_overrides.get(manifest.name)\n\n    def get_project_install_dir_respecting_install_prefix(\n        self, manifest: ManifestParser\n    ) -> str:\n        inst_dir: str = self.get_project_install_dir(manifest)\n        prefix: str | None = self.get_project_install_prefix(manifest)\n        if prefix:\n            return inst_dir + prefix\n        return inst_dir\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/manifest.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\nimport configparser\nimport hashlib\nimport io\nimport os\nimport sys\nimport typing\n\nfrom .builder import (\n    AutoconfBuilder,\n    Boost,\n    CMakeBootStrapBuilder,\n    CMakeBuilder,\n    Iproute2Builder,\n    MakeBuilder,\n    MesonBuilder,\n    NinjaBootstrap,\n    NopBuilder,\n    OpenSSLBuilder,\n    SetupPyBuilder,\n    SqliteBuilder,\n)\nfrom .cargo import CargoBuilder\nfrom .expr import ExprNode, parse_expr\nfrom .fetcher import (\n    ArchiveFetcher,\n    GitFetcher,\n    PreinstalledNopFetcher,\n    ShipitTransformerFetcher,\n    SimpleShipitTransformerFetcher,\n    SubFetcher,\n    SystemPackageFetcher,\n)\nfrom .py_wheel_builder import PythonWheelBuilder\n\nif typing.TYPE_CHECKING:\n    from .builder import BuilderBase\n    from .buildopts import BuildOptions\n    from .fetcher import Fetcher\n    from .load import ManifestLoader\n\nREQUIRED: str = \"REQUIRED\"\nOPTIONAL: str = \"OPTIONAL\"\n\nSCHEMA: dict[str, dict[str, object]] = {\n    \"manifest\": {\n        \"optional_section\": False,\n        \"fields\": {\n            \"name\": REQUIRED,\n            \"fbsource_path\": OPTIONAL,\n            \"shipit_project\": OPTIONAL,\n            \"shipit_fbcode_builder\": OPTIONAL,\n            \"use_shipit\": OPTIONAL,\n            \"shipit_external_branch\": OPTIONAL,\n            \"shipit_strip_marker\": OPTIONAL,\n        },\n    },\n    \"dependencies\": {\"optional_section\": True, \"allow_values\": False},\n    \"depends.environment\": {\"optional_section\": True},\n    \"git\": {\n        \"optional_section\": True,\n        \"fields\": {\n            \"repo_url\": REQUIRED,\n            \"rev\": OPTIONAL,\n            \"depth\": OPTIONAL,\n            \"branch\": OPTIONAL,\n        },\n    },\n    \"download\": {\n        \"optional_section\": True,\n        \"fields\": {\"url\": REQUIRED, \"sha256\": REQUIRED},\n    },\n    \"build\": {\n        \"optional_section\": True,\n        \"fields\": {\n            \"builder\": REQUIRED,\n            \"subdir\": OPTIONAL,\n            \"make_binary\": OPTIONAL,\n            \"build_in_src_dir\": OPTIONAL,\n            \"only_install\": OPTIONAL,\n            \"job_weight_mib\": OPTIONAL,\n            \"patchfile\": OPTIONAL,\n            \"patchfile_opts\": OPTIONAL,\n            \"rewrite_includes\": OPTIONAL,\n        },\n    },\n    \"msbuild\": {\"optional_section\": True, \"fields\": {\"project\": REQUIRED}},\n    \"cargo\": {\n        \"optional_section\": True,\n        \"fields\": {\n            \"build_doc\": OPTIONAL,\n            \"workspace_dir\": OPTIONAL,\n            \"manifests_to_build\": OPTIONAL,\n            # Where to write cargo config (defaults to build_dir/.cargo/config.toml)\n            \"cargo_config_file\": OPTIONAL,\n        },\n    },\n    \"github.actions\": {\n        \"optional_section\": True,\n        \"fields\": {\n            \"run_tests\": OPTIONAL,\n            \"required_locales\": OPTIONAL,\n            \"rust_version\": OPTIONAL,\n            \"build_type\": OPTIONAL,\n        },\n    },\n    \"crate.pathmap\": {\"optional_section\": True},\n    \"cmake.defines\": {\"optional_section\": True},\n    \"autoconf.args\": {\"optional_section\": True},\n    \"autoconf.envcmd.LDFLAGS\": {\"optional_section\": True},\n    \"rpms\": {\"optional_section\": True},\n    \"debs\": {\"optional_section\": True},\n    \"homebrew\": {\"optional_section\": True},\n    \"pps\": {\"optional_section\": True},\n    \"preinstalled.env\": {\"optional_section\": True},\n    \"bootstrap.args\": {\"optional_section\": True},\n    \"b2.args\": {\"optional_section\": True},\n    \"make.build_args\": {\"optional_section\": True},\n    \"make.install_args\": {\"optional_section\": True},\n    \"make.test_args\": {\"optional_section\": True},\n    \"meson.setup_args\": {\"optional_section\": True},\n    \"header-only\": {\"optional_section\": True, \"fields\": {\"includedir\": REQUIRED}},\n    \"shipit.pathmap\": {\"optional_section\": True},\n    \"shipit.strip\": {\"optional_section\": True},\n    \"install.files\": {\"optional_section\": True},\n    \"subprojects\": {\"optional_section\": True},\n    # fb-only\n    \"sandcastle\": {\"optional_section\": True, \"fields\": {\"run_tests\": OPTIONAL}},\n    \"setup-py.test\": {\"optional_section\": True, \"fields\": {\"python_script\": REQUIRED}},\n    \"setup-py.env\": {\"optional_section\": True},\n}\n\n# These sections are allowed to vary for different platforms\n# using the expression syntax to enable/disable sections\nALLOWED_EXPR_SECTIONS: list[str] = [\n    \"autoconf.args\",\n    \"autoconf.envcmd.LDFLAGS\",\n    \"build\",\n    \"cmake.defines\",\n    \"dependencies\",\n    \"make.build_args\",\n    \"make.install_args\",\n    \"bootstrap.args\",\n    \"b2.args\",\n    \"download\",\n    \"git\",\n    \"install.files\",\n    \"rpms\",\n    \"debs\",\n    \"shipit.pathmap\",\n    \"shipit.strip\",\n    \"homebrew\",\n    \"github.actions\",\n    \"pps\",\n]\n\n\ndef parse_conditional_section_name(name: str, section_def: str) -> ExprNode:\n    expr = name[len(section_def) + 1 :]\n    return parse_expr(expr, ManifestContext.ALLOWED_VARIABLES)\n\n\ndef validate_allowed_fields(\n    file_name: str,\n    section: str,\n    config: configparser.RawConfigParser,\n    allowed_fields: dict[str, str],\n) -> None:\n    for field in config.options(section):\n        if not allowed_fields.get(field):\n            raise Exception(\n                (\"manifest file %s section '%s' contains \" \"unknown field '%s'\")\n                % (file_name, section, field)\n            )\n\n    for field in allowed_fields:\n        if allowed_fields[field] == REQUIRED and not config.has_option(section, field):\n            raise Exception(\n                (\"manifest file %s section '%s' is missing \" \"required field '%s'\")\n                % (file_name, section, field)\n            )\n\n\ndef validate_allow_values(\n    file_name: str, section: str, config: configparser.RawConfigParser\n) -> None:\n    for field in config.options(section):\n        value = config.get(section, field)\n        if value is not None:\n            raise Exception(\n                (\n                    \"manifest file %s section '%s' has '%s = %s' but \"\n                    \"this section doesn't allow specifying values \"\n                    \"for its entries\"\n                )\n                % (file_name, section, field, value)\n            )\n\n\ndef validate_section(\n    file_name: str, section: str, config: configparser.RawConfigParser\n) -> str:\n    section_def = SCHEMA.get(section)\n    if not section_def:\n        for name in ALLOWED_EXPR_SECTIONS:\n            if section.startswith(name + \".\"):\n                # Verify that the conditional parses, but discard it\n                try:\n                    parse_conditional_section_name(section, name)\n                except Exception as exc:\n                    raise Exception(\n                        (\"manifest file %s section '%s' has invalid \" \"conditional: %s\")\n                        % (file_name, section, str(exc))\n                    )\n                section_def = SCHEMA.get(name)\n                canonical_section_name = name\n                break\n        if not section_def:\n            raise Exception(\n                \"manifest file %s contains unknown section '%s'\" % (file_name, section)\n            )\n    else:\n        canonical_section_name = section\n\n    allowed_fields = section_def.get(\"fields\")\n    if allowed_fields:\n        # pyre-ignore[6]: Expected `dict[str, str]` but got `object`.\n        validate_allowed_fields(file_name, section, config, allowed_fields)\n    elif not section_def.get(\"allow_values\", True):\n        validate_allow_values(file_name, section, config)\n    # pyre-fixme[61]: `canonical_section_name` is undefined, or not always defined.\n    return canonical_section_name\n\n\nclass ManifestParser:\n    def __init__(self, file_name: str, fp: str | typing.IO[str] | None = None) -> None:\n        # allow_no_value enables listing parameters in the\n        # autoconf.args section one per line\n        config = configparser.RawConfigParser(allow_no_value=True)\n        config.optionxform = str  # type: ignore[assignment]  # make it case sensitive\n        if fp is None:\n            with open(file_name, \"r\") as fp:\n                config.read_file(fp)\n        elif isinstance(fp, type(\"\")):\n            # For testing purposes, parse from a string (str\n            # or unicode)\n            config.read_file(io.StringIO(fp))\n        else:\n            config.read_file(fp)\n\n        # validate against the schema\n        seen_sections: set[str] = set()\n\n        for section in config.sections():\n            seen_sections.add(validate_section(file_name, section, config))\n\n        for section in SCHEMA.keys():\n            section_def = SCHEMA[section]\n            if (\n                not section_def.get(\"optional_section\", False)\n                and section not in seen_sections\n            ):\n                raise Exception(\n                    \"manifest file %s is missing required section %s\"\n                    % (file_name, section)\n                )\n\n        self._config: configparser.RawConfigParser = config\n        self.name: str = config.get(\"manifest\", \"name\")\n        self.fbsource_path: str | None = self.get(\"manifest\", \"fbsource_path\")\n        self.shipit_project: str | None = self.get(\"manifest\", \"shipit_project\")\n        self.shipit_fbcode_builder: str | None = self.get(\n            \"manifest\", \"shipit_fbcode_builder\"\n        )\n        self.resolved_system_packages: dict[str, str] = {}\n        self.shipit_strip_marker: str | None = self.get(\n            \"manifest\", \"shipit_strip_marker\", defval=\"@fb-only\"\n        )\n\n        if self.name != os.path.basename(file_name):\n            raise Exception(\n                \"filename of the manifest '%s' does not match the manifest name '%s'\"\n                % (file_name, self.name)\n            )\n\n        if \".\" in self.name:\n            raise Exception(\n                f\"manifest name ({self.name}) must not contain the '.' character (it is incompatible with github actions)\"\n            )\n\n    def get(\n        self,\n        section: str,\n        key: str,\n        defval: str | None = None,\n        ctx: ManifestContext | dict[str, str | None] | None = None,\n    ) -> str | None:\n        ctx = ctx or {}\n\n        for s in self._config.sections():\n            if s == section:\n                if self._config.has_option(s, key):\n                    return self._config.get(s, key)\n                return defval\n\n            if s.startswith(section + \".\"):\n                expr = parse_conditional_section_name(s, section)\n                # pyre-fixme[6]: For 1st argument expected `Dict[str,\n                #  Optional[str]]` but got `Union[Dict[str, Optional[str]],\n                #  ManifestContext]`.\n                if not expr.eval(ctx):\n                    continue\n\n                if self._config.has_option(s, key):\n                    return self._config.get(s, key)\n\n        return defval\n\n    def get_dependencies(self, ctx: ManifestContext) -> list[str]:\n        dep_list = list(self.get_section_as_dict(\"dependencies\", ctx).keys())\n        dep_list.sort()\n        builder = self.get(\"build\", \"builder\", ctx=ctx)\n        if builder in (\"cmake\", \"python-wheel\"):\n            dep_list.insert(0, \"cmake\")\n        elif builder == \"autoconf\" and self.name not in (\n            \"autoconf\",\n            \"libtool\",\n            \"automake\",\n        ):\n            # they need libtool and its deps (automake, autoconf) so add\n            # those as deps (but obviously not if we're building those\n            # projects themselves)\n            dep_list.insert(0, \"libtool\")\n\n        return dep_list\n\n    def get_section_as_args(\n        self,\n        section: str,\n        ctx: ManifestContext | dict[str, str | None] | None = None,\n    ) -> list[str]:\n        \"\"\"Intended for use with the make.[build_args/install_args] and\n        autoconf.args sections, this method collects the entries and returns an\n        array of strings.\n        If the manifest contains conditional sections, ctx is used to\n        evaluate the condition and merge in the values.\n        \"\"\"\n        args: list[str] = []\n        ctx = ctx or {}\n\n        for s in self._config.sections():\n            if s != section:\n                if not s.startswith(section + \".\"):\n                    continue\n                expr = parse_conditional_section_name(s, section)\n                # pyre-fixme[6]: For 1st argument expected `Dict[str,\n                #  Optional[str]]` but got `Union[Dict[str, Optional[str]],\n                #  ManifestContext]`.\n                if not expr.eval(ctx):\n                    continue\n            for field in self._config.options(s):\n                value = self._config.get(s, field)\n                if value is None:\n                    args.append(field)\n                else:\n                    args.append(\"%s=%s\" % (field, value))\n        return args\n\n    def get_section_as_ordered_pairs(\n        self,\n        section: str,\n        ctx: ManifestContext | dict[str, str | None] | None = None,\n    ) -> list[tuple[str, str | None]]:\n        \"\"\"Used for eg: shipit.pathmap which has strong\n        ordering requirements\"\"\"\n        res: list[tuple[str, str | None]] = []\n        ctx = ctx or {}\n\n        for s in self._config.sections():\n            if s != section:\n                if not s.startswith(section + \".\"):\n                    continue\n                expr = parse_conditional_section_name(s, section)\n                # pyre-fixme[6]: For 1st argument expected `Dict[str,\n                #  Optional[str]]` but got `Union[Dict[str, Optional[str]],\n                #  ManifestContext]`.\n                if not expr.eval(ctx):\n                    continue\n\n            for key in self._config.options(s):\n                value = self._config.get(s, key)\n                res.append((key, value))\n        return res\n\n    def get_section_as_dict(\n        self,\n        section: str,\n        ctx: ManifestContext | dict[str, str | None] | None,\n    ) -> dict[str, str | None]:\n        d: dict[str, str | None] = {}\n\n        for s in self._config.sections():\n            if s != section:\n                if not s.startswith(section + \".\"):\n                    continue\n                expr = parse_conditional_section_name(s, section)\n                # pyre-fixme[6]: For 1st argument expected `Dict[str,\n                #  Optional[str]]` but got `Union[None, Dict[str, Optional[str]],\n                #  ManifestContext]`.\n                if not expr.eval(ctx):\n                    continue\n            for field in self._config.options(s):\n                value = self._config.get(s, field)\n                d[field] = value\n        return d\n\n    def update_hash(self, hasher: hashlib._Hash, ctx: ManifestContext) -> None:\n        \"\"\"Compute a hash over the configuration for the given\n        context.  The goal is for the hash to change if the config\n        for that context changes, but not if a change is made to\n        the config only for a different platform than that expressed\n        by ctx.  The hash is intended to be used to help invalidate\n        a future cache for the third party build products.\n        The hasher argument is a hash object returned from hashlib.\"\"\"\n        for section in sorted(SCHEMA.keys()):\n            hasher.update(section.encode(\"utf-8\"))\n\n            # Note: at the time of writing, nothing in the implementation\n            # relies on keys in any config section being ordered.\n            # In theory we could have conflicting flags in different\n            # config sections and later flags override earlier flags.\n            # For the purposes of computing a hash we're not super\n            # concerned about this: manifest changes should be rare\n            # enough and we'd rather that this trigger an invalidation\n            # than strive for a cache hit at this time.\n            pairs = self.get_section_as_ordered_pairs(section, ctx)\n            pairs.sort(key=lambda pair: pair[0])\n            for key, value in pairs:\n                hasher.update(key.encode(\"utf-8\"))\n                if value is not None:\n                    hasher.update(value.encode(\"utf-8\"))\n\n    def is_first_party_project(self) -> bool:\n        \"\"\"returns true if this is an FB first-party project\"\"\"\n        return self.shipit_project is not None\n\n    def get_required_system_packages(\n        self, ctx: ManifestContext\n    ) -> dict[str, list[str]]:\n        \"\"\"Returns dictionary of packager system -> list of packages\"\"\"\n        return {\n            \"rpm\": self.get_section_as_args(\"rpms\", ctx),\n            \"deb\": self.get_section_as_args(\"debs\", ctx),\n            \"homebrew\": self.get_section_as_args(\"homebrew\", ctx),\n            \"pacman-package\": self.get_section_as_args(\"pps\", ctx),\n        }\n\n    def _is_satisfied_by_preinstalled_environment(self, ctx: ManifestContext) -> bool:\n        envs = self.get_section_as_args(\"preinstalled.env\", ctx)\n        if not envs:\n            return False\n        for key in envs:\n            val = os.environ.get(key, None)\n            print(\n                f\"Testing ENV[{key}]: {repr(val)}\",\n                file=sys.stderr,\n            )\n            if val is None:\n                return False\n            if len(val) == 0:\n                return False\n\n        return True\n\n    def get_repo_url(self, ctx: ManifestContext) -> str | None:\n        return self.get(\"git\", \"repo_url\", ctx=ctx)\n\n    def _create_fetcher(\n        self, build_options: BuildOptions, ctx: ManifestContext\n    ) -> Fetcher:\n        real_shipit_available = ShipitTransformerFetcher.available(build_options)\n        use_real_shipit = real_shipit_available and (\n            build_options.use_shipit\n            or self.get(\"manifest\", \"use_shipit\", defval=\"false\", ctx=ctx) == \"true\"\n        )\n        if (\n            not use_real_shipit\n            and self.fbsource_path\n            and build_options.fbsource_dir\n            and self.shipit_project\n        ):\n            return SimpleShipitTransformerFetcher(build_options, self, ctx)\n\n        if (\n            self.fbsource_path\n            and build_options.fbsource_dir\n            and self.shipit_project\n            and real_shipit_available\n        ):\n            # We can use the code from fbsource\n            return ShipitTransformerFetcher(\n                build_options,\n                self.shipit_project,\n                # pyre-fixme[6]: For 3rd argument expected `str` but got\n                #  `Optional[str]`.\n                self.get(\"manifest\", \"shipit_external_branch\"),\n            )\n\n        # If both of these are None, the package can only be coming from\n        # preinstalled toolchain or system packages\n        repo_url = self.get_repo_url(ctx)\n        url = self.get(\"download\", \"url\", ctx=ctx)\n\n        # Can we satisfy this dep with system packages?\n        if (repo_url is None and url is None) or build_options.allow_system_packages:\n            if self._is_satisfied_by_preinstalled_environment(ctx):\n                # pyre-fixme[7]: Expected `Fetcher` but got `PreinstalledNopFetcher`.\n                return PreinstalledNopFetcher()\n\n            if build_options.host_type.get_package_manager():\n                packages = self.get_required_system_packages(ctx)\n                package_fetcher = SystemPackageFetcher(build_options, packages)\n                if package_fetcher.packages_are_installed():\n                    # pyre-fixme[7]: Expected `Fetcher` but got `SystemPackageFetcher`.\n                    return package_fetcher\n\n        if repo_url:\n            rev = self.get(\"git\", \"rev\")\n            depth = self.get(\"git\", \"depth\")\n            branch = self.get(\"git\", \"branch\")\n            # pyre-fixme[6]: For 4th argument expected `str` but got `Optional[str]`.\n            # pyre-fixme[6]: For 5th argument expected `int` but got `Optional[str]`.\n            # pyre-fixme[6]: For 6th argument expected `str` but got `Optional[str]`.\n            return GitFetcher(build_options, self, repo_url, rev, depth, branch)\n\n        if url:\n            # We need to defer this import until now to avoid triggering\n            # a cycle when the facebook/__init__.py is loaded.\n            try:\n                from .facebook.lfs import LFSCachingArchiveFetcher\n\n                return LFSCachingArchiveFetcher(\n                    build_options,\n                    self,\n                    url,\n                    # pyre-fixme[6]: For 4th argument expected `str` but got\n                    #  `Optional[str]`.\n                    self.get(\"download\", \"sha256\", ctx=ctx),\n                )\n            except ImportError:\n                # This FB internal module isn't shippped to github,\n                # so just use its base class\n                return ArchiveFetcher(\n                    build_options,\n                    self,\n                    url,\n                    # pyre-fixme[6]: For 4th argument expected `str` but got\n                    #  `Optional[str]`.\n                    self.get(\"download\", \"sha256\", ctx=ctx),\n                )\n\n        raise KeyError(\n            f\"project {self.name} has no fetcher configuration or system packages matching {ctx} - have you run `getdeps.py install-system-deps --recursive`?\"\n        )\n\n    def create_fetcher(\n        self,\n        build_options: BuildOptions,\n        loader: ManifestLoader,\n        ctx: ManifestContext,\n    ) -> Fetcher:\n        fetcher = self._create_fetcher(build_options, ctx)\n        subprojects = self.get_section_as_ordered_pairs(\"subprojects\", ctx)\n        if subprojects:\n            subs: list[tuple[Fetcher, str | None]] = []\n            for project, subdir in subprojects:\n                submanifest = loader.load_manifest(project)\n                subfetcher = submanifest.create_fetcher(build_options, loader, ctx)\n                subs.append((subfetcher, subdir))\n            # pyre-fixme[6]: For 2nd argument expected `List[Tuple[Fetcher, str]]`\n            #  but got `List[Tuple[Fetcher, Optional[str]]]`.\n            return SubFetcher(fetcher, subs)\n        else:\n            return fetcher\n\n    def get_builder_name(self, ctx: ManifestContext) -> str:\n        builder = self.get(\"build\", \"builder\", ctx=ctx)\n        if not builder:\n            raise Exception(\"project %s has no builder for %r\" % (self.name, ctx))\n        return builder\n\n    def create_builder(  # noqa:C901\n        self,\n        build_options: BuildOptions,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n        ctx: ManifestContext,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        final_install_prefix: str | None = None,\n        extra_cmake_defines: dict[str, str] | None = None,\n        cmake_targets: list[str] | None = None,\n        extra_b2_args: list[str] | None = None,\n    ) -> BuilderBase:\n        builder = self.get_builder_name(ctx)\n        build_in_src_dir = self.get(\"build\", \"build_in_src_dir\", \"false\", ctx=ctx)\n        if build_in_src_dir == \"true\":\n            # Some scripts don't work when they are configured and build in\n            # a different directory than source (or when the build directory\n            # is not a subdir of source).\n            build_dir = src_dir\n            subdir = self.get(\"build\", \"subdir\", None, ctx=ctx)\n            if subdir is not None:\n                build_dir = os.path.join(build_dir, subdir)\n            print(\"build_dir is %s\" % build_dir)  # just to quiet lint\n\n        if builder == \"make\" or builder == \"cmakebootstrap\":\n            build_args = self.get_section_as_args(\"make.build_args\", ctx)\n            install_args = self.get_section_as_args(\"make.install_args\", ctx)\n            test_args = self.get_section_as_args(\"make.test_args\", ctx)\n            if builder == \"cmakebootstrap\":\n                return CMakeBootStrapBuilder(\n                    loader,\n                    dep_manifests,\n                    build_options,\n                    ctx,\n                    self,\n                    src_dir,\n                    # pyre-fixme[6]: For 7th argument expected `str` but got `None`.\n                    None,\n                    inst_dir,\n                    build_args,\n                    install_args,\n                    test_args,\n                )\n            else:\n                return MakeBuilder(\n                    loader,\n                    dep_manifests,\n                    build_options,\n                    ctx,\n                    self,\n                    src_dir,\n                    # pyre-fixme[6]: For 7th argument expected `str` but got `None`.\n                    None,\n                    inst_dir,\n                    build_args,\n                    install_args,\n                    test_args,\n                )\n\n        if builder == \"autoconf\":\n            args = self.get_section_as_args(\"autoconf.args\", ctx)\n            conf_env_args: dict[str, list[str]] = {}\n            ldflags_cmd = self.get_section_as_args(\"autoconf.envcmd.LDFLAGS\", ctx)\n            if ldflags_cmd:\n                conf_env_args[\"LDFLAGS\"] = ldflags_cmd\n            return AutoconfBuilder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n                args,\n                conf_env_args,\n            )\n\n        if builder == \"boost\":\n            args = self.get_section_as_args(\"b2.args\", ctx)\n            if extra_b2_args is not None:\n                args += extra_b2_args\n            return Boost(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n                args,\n            )\n\n        if builder == \"cmake\":\n            defines = self.get_section_as_dict(\"cmake.defines\", ctx)\n            return CMakeBuilder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n                # pyre-fixme[6]: For 9th argument expected `Optional[Dict[str,\n                #  str]]` but got `Dict[str, Optional[str]]`.\n                defines,\n                final_install_prefix,\n                extra_cmake_defines,\n                cmake_targets,\n            )\n\n        if builder == \"python-wheel\":\n            return PythonWheelBuilder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n            )\n\n        if builder == \"sqlite\":\n            return SqliteBuilder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n            )\n\n        if builder == \"ninja_bootstrap\":\n            return NinjaBootstrap(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                build_dir,\n                src_dir,\n                inst_dir,\n            )\n\n        if builder == \"nop\":\n            return NopBuilder(\n                loader, dep_manifests, build_options, ctx, self, src_dir, inst_dir\n            )\n\n        if builder == \"openssl\":\n            return OpenSSLBuilder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                build_dir,\n                src_dir,\n                inst_dir,\n            )\n\n        if builder == \"iproute2\":\n            return Iproute2Builder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n            )\n\n        if builder == \"meson\":\n            return MesonBuilder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n            )\n\n        if builder == \"setup-py\":\n            return SetupPyBuilder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                self,\n                src_dir,\n                build_dir,\n                inst_dir,\n            )\n\n        if builder == \"cargo\":\n            return self.create_cargo_builder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                src_dir,\n                build_dir,\n                inst_dir,\n            )\n\n        raise KeyError(\"project %s has no known builder\" % (self.name))\n\n    def create_prepare_builders(\n        self,\n        build_options: BuildOptions,\n        ctx: ManifestContext,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n    ) -> list[BuilderBase]:\n        \"\"\"Create builders that have a prepare step run, e.g. to write config files\"\"\"\n        prepare_builders: list[BuilderBase] = []\n        builder = self.get_builder_name(ctx)\n        cargo = self.get_section_as_dict(\"cargo\", ctx)\n        if not builder == \"cargo\" and cargo:\n            cargo_builder = self.create_cargo_builder(\n                loader,\n                dep_manifests,\n                build_options,\n                ctx,\n                src_dir,\n                build_dir,\n                inst_dir,\n            )\n            prepare_builders.append(cargo_builder)\n        return prepare_builders\n\n    def create_cargo_builder(\n        self,\n        loader: ManifestLoader,\n        dep_manifests: list[ManifestParser],\n        build_options: BuildOptions,\n        ctx: ManifestContext,\n        src_dir: str,\n        build_dir: str,\n        inst_dir: str,\n    ) -> CargoBuilder:\n        # pyre-fixme[6]: For 3rd argument expected `Optional[str]` but got `bool`.\n        build_doc = self.get(\"cargo\", \"build_doc\", False, ctx)\n        workspace_dir = self.get(\"cargo\", \"workspace_dir\", None, ctx)\n        manifests_to_build = self.get(\"cargo\", \"manifests_to_build\", None, ctx)\n        cargo_config_file = self.get(\"cargo\", \"cargo_config_file\", None, ctx)\n        return CargoBuilder(\n            loader,\n            dep_manifests,\n            build_options,\n            ctx,\n            self,\n            src_dir,\n            build_dir,\n            inst_dir,\n            # pyre-fixme[6]: For 9th argument expected `bool` but got `Optional[str]`.\n            build_doc,\n            workspace_dir,\n            manifests_to_build,\n            cargo_config_file,\n        )\n\n\nclass ManifestContext:\n    \"\"\"ProjectContext contains a dictionary of values to use when evaluating boolean\n    expressions in a project manifest.\n\n    This object should be passed as the `ctx` parameter in ManifestParser.get() calls.\n    \"\"\"\n\n    ALLOWED_VARIABLES: set[str] = {\n        \"os\",\n        \"distro\",\n        \"distro_vers\",\n        \"fb\",\n        \"fbsource\",\n        \"test\",\n        \"shared_libs\",\n    }\n\n    def __init__(self, ctx_dict: dict[str, str | None]) -> None:\n        assert set(ctx_dict.keys()) == self.ALLOWED_VARIABLES\n        self.ctx_dict: dict[str, str | None] = ctx_dict\n\n    def get(self, key: str) -> str | None:\n        return self.ctx_dict[key]\n\n    def set(self, key: str, value: str | None) -> None:\n        assert key in self.ALLOWED_VARIABLES\n        self.ctx_dict[key] = value\n\n    def copy(self) -> ManifestContext:\n        return ManifestContext(dict(self.ctx_dict))\n\n    def __str__(self) -> str:\n        s = \", \".join(\n            \"%s=%s\" % (key, value) for key, value in sorted(self.ctx_dict.items())\n        )\n        return \"{\" + s + \"}\"\n\n\nclass ContextGenerator:\n    \"\"\"ContextGenerator allows creating ManifestContext objects on a per-project basis.\n    This allows us to evaluate different projects with slightly different contexts.\n\n    For instance, this can be used to only enable tests for some projects.\"\"\"\n\n    def __init__(self, default_ctx: dict[str, str | None]) -> None:\n        self.default_ctx: ManifestContext = ManifestContext(default_ctx)\n        self.ctx_by_project: dict[str, ManifestContext] = {}\n\n    def set_value_for_project(\n        self, project_name: str, key: str, value: str | None\n    ) -> None:\n        project_ctx = self.ctx_by_project.get(project_name)\n        if project_ctx is None:\n            project_ctx = self.default_ctx.copy()\n            self.ctx_by_project[project_name] = project_ctx\n        project_ctx.set(key, value)\n\n    def set_value_for_all_projects(self, key: str, value: str | None) -> None:\n        self.default_ctx.set(key, value)\n        for ctx in self.ctx_by_project.values():\n            ctx.set(key, value)\n\n    def get_context(self, project_name: str) -> ManifestContext:\n        return self.ctx_by_project.get(project_name, self.default_ctx)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/platform.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\nfrom __future__ import annotations\n\nimport os\nimport platform\nimport re\nimport shlex\nimport sys\n\n\ndef is_windows() -> bool:\n    \"\"\"Returns true if the system we are currently running on\n    is a Windows system\"\"\"\n    return sys.platform.startswith(\"win\")\n\n\ndef get_linux_type() -> tuple[str | None, str | None, str | None]:\n    try:\n        with open(\"/etc/os-release\") as f:\n            data = f.read()\n    except EnvironmentError:\n        return (None, None, None)\n\n    os_vars: dict[str, str] = {}\n    for line in data.splitlines():\n        parts = line.split(\"=\", 1)\n        if len(parts) != 2:\n            continue\n        key = parts[0].strip()\n        value_parts = shlex.split(parts[1].strip())\n        if not value_parts:\n            value = \"\"\n        else:\n            value = value_parts[0]\n        os_vars[key] = value\n\n    name = os_vars.get(\"NAME\")\n    if name:\n        name = name.lower()\n        name = re.sub(\"linux\", \"\", name)\n        name = name.strip().replace(\" \", \"_\")\n\n    version_id = os_vars.get(\"VERSION_ID\")\n    if version_id:\n        version_id = version_id.lower()\n\n    return \"linux\", name, version_id\n\n\n# Ideally we'd use a common library like `psutil` to read system information,\n# but getdeps can't take third-party dependencies.\n\n\ndef _get_available_ram_linux() -> int:\n    # TODO: Ideally, this function would inspect the current cgroup for any\n    # limits, rather than solely relying on system RAM.\n\n    meminfo_path = \"/proc/meminfo\"\n    try:\n        with open(meminfo_path) as f:\n            for line in f:\n                try:\n                    key, value = line.split(\":\", 1)\n                except ValueError:\n                    continue\n                suffix = \" kB\\n\"\n                if key == \"MemAvailable\" and value.endswith(suffix):\n                    value = value[: -len(suffix)]\n                    try:\n                        return int(value) // 1024\n                    except ValueError:\n                        continue\n    except OSError:\n        print(\"error opening {}\".format(meminfo_path), end=\"\", file=sys.stderr)\n    else:\n        print(\n            \"{} had no valid MemAvailable\".format(meminfo_path), end=\"\", file=sys.stderr\n        )\n\n    guess = 8\n    print(\", guessing {} GiB\".format(guess), file=sys.stderr)\n    return guess * 1024\n\n\ndef _get_available_ram_macos() -> int:\n    import ctypes.util\n\n    libc = ctypes.CDLL(ctypes.util.find_library(\"libc\"), use_errno=True)\n    sysctlbyname = libc.sysctlbyname\n    sysctlbyname.restype = ctypes.c_int\n    sysctlbyname.argtypes = [\n        ctypes.c_char_p,\n        ctypes.c_void_p,\n        ctypes.POINTER(ctypes.c_size_t),\n        ctypes.c_void_p,\n        ctypes.c_size_t,\n    ]\n    # TODO: There may be some way to approximate an availability\n    # metric, but just use total RAM for now.\n    memsize = ctypes.c_int64()\n    memsizesize = ctypes.c_size_t(8)\n    res = sysctlbyname(\n        b\"hw.memsize\", ctypes.byref(memsize), ctypes.byref(memsizesize), None, 0\n    )\n    if res != 0:\n        raise NotImplementedError(\n            f\"failed to retrieve hw.memsize sysctl: {ctypes.get_errno()}\"\n        )\n    return memsize.value // (1024 * 1024)\n\n\ndef _get_available_ram_windows() -> int:\n    import ctypes\n\n    DWORD = ctypes.c_uint32\n    QWORD = ctypes.c_uint64\n\n    class MEMORYSTATUSEX(ctypes.Structure):\n        _fields_ = [\n            (\"dwLength\", DWORD),\n            (\"dwMemoryLoad\", DWORD),\n            (\"ullTotalPhys\", QWORD),\n            (\"ullAvailPhys\", QWORD),\n            (\"ullTotalPageFile\", QWORD),\n            (\"ullAvailPageFile\", QWORD),\n            (\"ullTotalVirtual\", QWORD),\n            (\"ullAvailVirtual\", QWORD),\n            (\"ullExtendedVirtual\", QWORD),\n        ]\n\n    ms = MEMORYSTATUSEX()\n    ms.dwLength = ctypes.sizeof(ms)\n    # pyre-ignore[16]\n    res = ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(ms))\n    if res == 0:\n        raise NotImplementedError(\"error calling GlobalMemoryStatusEx\")\n\n    # This is fuzzy, but AvailPhys is too conservative, and AvailTotal is too\n    # aggressive, so average the two. It's okay for builds to use some swap.\n    return (ms.ullAvailPhys + ms.ullTotalPhys) // (2 * 1024 * 1024)\n\n\ndef _get_available_ram_freebsd() -> int:\n    import ctypes.util\n\n    libc = ctypes.CDLL(ctypes.util.find_library(\"libc\"), use_errno=True)\n    sysctlbyname = libc.sysctlbyname\n    sysctlbyname.restype = ctypes.c_int\n    sysctlbyname.argtypes = [\n        ctypes.c_char_p,\n        ctypes.c_void_p,\n        ctypes.POINTER(ctypes.c_size_t),\n        ctypes.c_void_p,\n        ctypes.c_size_t,\n    ]\n    # hw.usermem is pretty close to what we want.\n    memsize = ctypes.c_int64()\n    memsizesize = ctypes.c_size_t(8)\n    res = sysctlbyname(\n        b\"hw.usermem\", ctypes.byref(memsize), ctypes.byref(memsizesize), None, 0\n    )\n    if res != 0:\n        raise NotImplementedError(\n            f\"failed to retrieve hw.memsize sysctl: {ctypes.get_errno()}\"\n        )\n    return memsize.value // (1024 * 1024)\n\n\ndef get_available_ram() -> int:\n    \"\"\"\n    Returns a platform-appropriate available RAM metric in MiB.\n    \"\"\"\n    if sys.platform == \"linux\":\n        return _get_available_ram_linux()\n    elif sys.platform == \"darwin\":\n        return _get_available_ram_macos()\n    elif sys.platform == \"win32\":\n        return _get_available_ram_windows()\n    elif sys.platform.startswith(\"freebsd\"):\n        return _get_available_ram_freebsd()\n    else:\n        raise NotImplementedError(\n            f\"platform {sys.platform} does not have an implementation of get_available_ram\"\n        )\n\n\ndef is_current_host_arm() -> bool:\n    if sys.platform.startswith(\"darwin\"):\n        # platform.machine() can be fooled by rosetta for python < 3.9.2\n        return \"ARM64\" in os.uname().version\n    else:\n        machine = platform.machine().lower()\n        return \"arm\" in machine or \"aarch\" in machine\n\n\nclass HostType:\n    def __init__(\n        self,\n        ostype: str | None = None,\n        distro: str | None = None,\n        distrovers: str | None = None,\n    ) -> None:\n        # Maybe we should allow callers to indicate whether this machine uses\n        # an ARM architecture, but we need to change HostType serialization\n        # and deserialization in that case and hunt down anywhere that is\n        # persisting that serialized data.\n        isarm = False\n\n        if ostype is None:\n            distro = None\n            distrovers = None\n            if sys.platform.startswith(\"linux\"):\n                ostype, distro, distrovers = get_linux_type()\n            elif sys.platform.startswith(\"darwin\"):\n                ostype = \"darwin\"\n            elif is_windows():\n                ostype = \"windows\"\n                distrovers = str(sys.getwindowsversion().major)\n            elif sys.platform.startswith(\"freebsd\"):\n                ostype = \"freebsd\"\n            else:\n                ostype = sys.platform\n\n            isarm = is_current_host_arm()\n\n        # The operating system type\n        self.ostype: str | None = ostype\n        # The distribution, if applicable\n        self.distro: str | None = distro\n        # The OS/distro version if known\n        self.distrovers: str | None = distrovers\n        # Does the CPU use an ARM architecture? ARM includes Apple Silicon\n        # Macs as well as other ARM systems that might be running Linux or\n        # something.\n        self.isarm: bool = isarm\n\n    def is_windows(self) -> bool:\n        return self.ostype == \"windows\"\n\n    # is_arm is kinda half implemented at the moment. This method is only\n    # intended to be used when HostType represents information about the\n    # current machine we are running on.\n    # When HostType is being used to enumerate platform types (represent\n    # information about machine types that we may or may not be running on)\n    # the result could be nonsense (under the current implementation its always\n    # false.)\n    def is_arm(self) -> bool:\n        return self.isarm\n\n    def is_darwin(self) -> bool:\n        return self.ostype == \"darwin\"\n\n    def is_linux(self) -> bool:\n        return self.ostype == \"linux\"\n\n    def is_freebsd(self) -> bool:\n        return self.ostype == \"freebsd\"\n\n    def as_tuple_string(self) -> str:\n        return \"%s-%s-%s\" % (\n            self.ostype,\n            self.distro or \"none\",\n            self.distrovers or \"none\",\n        )\n\n    def get_package_manager(self) -> str | None:\n        if not self.is_linux() and not self.is_darwin():\n            return None\n        if self.is_darwin():\n            return \"homebrew\"\n        if self.distro in (\"fedora\", \"centos\", \"centos_stream\", \"rocky\"):\n            return \"rpm\"\n        if self.distro is not None and self.distro.startswith(\n            (\"debian\", \"ubuntu\", \"pop!_os\", \"mint\")\n        ):\n            return \"deb\"\n        if self.distro == \"arch\":\n            return \"pacman-package\"\n        return None\n\n    @staticmethod\n    def from_tuple_string(s: str) -> HostType:\n        ostype, distro, distrovers = s.split(\"-\")\n        return HostType(ostype=ostype, distro=distro, distrovers=distrovers)\n\n    def __eq__(self, b: object) -> bool:\n        if not isinstance(b, HostType):\n            return False\n        return (\n            self.ostype == b.ostype\n            and self.distro == b.distro\n            and self.distrovers == b.distrovers\n        )\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/py_wheel_builder.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\nfrom __future__ import annotations\n\nimport codecs\nimport collections\nimport email\nimport email.message\nimport os\nimport re\nimport stat\n\nfrom .builder import BuilderBase, CMakeBuilder\n\n\nWheelNameInfo = collections.namedtuple(\n    \"WheelNameInfo\", (\"distribution\", \"version\", \"build\", \"python\", \"abi\", \"platform\")\n)\n\nCMAKE_HEADER = \"\"\"\ncmake_minimum_required(VERSION 3.8)\n\nproject(\"{manifest_name}\" LANGUAGES C)\n\nset(CMAKE_MODULE_PATH\n  \"{cmake_dir}\"\n  ${{CMAKE_MODULE_PATH}}\n)\ninclude(FBPythonBinary)\n\nset(CMAKE_INSTALL_DIR lib/cmake/{manifest_name} CACHE STRING\n    \"The subdirectory where CMake package config files should be installed\")\n\"\"\"\n\nCMAKE_FOOTER = \"\"\"\ninstall_fb_python_library({lib_name} EXPORT all)\ninstall(\n  EXPORT all\n  FILE {manifest_name}-targets.cmake\n  NAMESPACE {namespace}::\n  DESTINATION ${{CMAKE_INSTALL_DIR}}\n)\n\ninclude(CMakePackageConfigHelpers)\nconfigure_package_config_file(\n  ${{CMAKE_BINARY_DIR}}/{manifest_name}-config.cmake.in\n  {manifest_name}-config.cmake\n  INSTALL_DESTINATION ${{CMAKE_INSTALL_DIR}}\n  PATH_VARS\n    CMAKE_INSTALL_DIR\n)\ninstall(\n  FILES ${{CMAKE_CURRENT_BINARY_DIR}}/{manifest_name}-config.cmake\n  DESTINATION ${{CMAKE_INSTALL_DIR}}\n)\n\"\"\"\n\nCMAKE_CONFIG_FILE = \"\"\"\n@PACKAGE_INIT@\n\ninclude(CMakeFindDependencyMacro)\n\nset_and_check({upper_name}_CMAKE_DIR \"@PACKAGE_CMAKE_INSTALL_DIR@\")\n\nif (NOT TARGET {namespace}::{lib_name})\n  include(\"${{{upper_name}_CMAKE_DIR}}/{manifest_name}-targets.cmake\")\nendif()\n\nset({upper_name}_LIBRARIES {namespace}::{lib_name})\n\n{find_dependency_lines}\n\nif (NOT {manifest_name}_FIND_QUIETLY)\n  message(STATUS \"Found {manifest_name}: ${{PACKAGE_PREFIX_DIR}}\")\nendif()\n\"\"\"\n\n\n# Note: for now we are manually manipulating the wheel packet contents.\n# The wheel format is documented here:\n# https://www.python.org/dev/peps/pep-0491/#file-format\n#\n# We currently aren't particularly smart about correctly handling the full wheel\n# functionality, but this is good enough to handle simple pure-python wheels,\n# which is the main thing we care about right now.\n#\n# We could potentially use pip to install the wheel to a temporary location and\n# then copy its \"installed\" files, but this has its own set of complications.\n# This would require pip to already be installed and available, and we would\n# need to correctly find the right version of pip or pip3 to use.\n# If we did ever want to go down that path, we would probably want to use\n# something like the following pip3 command:\n#   pip3 --isolated install --no-cache-dir --no-index --system \\\n#       --target <install_dir> <wheel_file>\nclass PythonWheelBuilder(BuilderBase):\n    \"\"\"This Builder can take Python wheel archives and install them as python libraries\n    that can be used by add_fb_python_library()/add_fb_python_executable() CMake rules.\n    \"\"\"\n\n    # pyre-fixme[13]: Attribute `dist_info_dir` is never initialized.\n    dist_info_dir: str\n    # pyre-fixme[13]: Attribute `template_format_dict` is never initialized.\n    template_format_dict: dict[str, str]\n\n    def _build(self, reconfigure: bool) -> None:\n        # When we are invoked, self.src_dir contains the unpacked wheel contents.\n        #\n        # Since a wheel file is just a zip file, the Fetcher code recognizes it as such\n        # and goes ahead and unpacks it.  (We could disable that Fetcher behavior in the\n        # future if we ever wanted to, say if we wanted to call pip here.)\n        wheel_name = self._parse_wheel_name()\n        name_version_prefix = \"-\".join((wheel_name.distribution, wheel_name.version))\n        dist_info_name = name_version_prefix + \".dist-info\"\n        data_dir_name = name_version_prefix + \".data\"\n        self.dist_info_dir = os.path.join(self.src_dir, dist_info_name)\n        wheel_metadata = self._read_wheel_metadata(wheel_name)\n\n        # Check that we can understand the wheel version.\n        # We don't really care about wheel_metadata[\"Root-Is-Purelib\"] since\n        # we are generating our own standalone python archives rather than installing\n        # into site-packages.\n        version = wheel_metadata[\"Wheel-Version\"]\n        if not version.startswith(\"1.\"):\n            raise Exception(\"unsupported wheel version %s\" % (version,))\n\n        # Add a find_dependency() call for each of our dependencies.\n        # The dependencies are also listed in the wheel METADATA file, but it is simpler\n        # to pull this directly from the getdeps manifest.\n        dep_list = sorted(\n            self.manifest.get_section_as_dict(\"dependencies\", self.ctx).keys()\n        )\n        find_dependency_lines = [\"find_dependency({})\".format(dep) for dep in dep_list]\n\n        getdeps_cmake_dir = os.path.join(\n            os.path.dirname(os.path.dirname(__file__)), \"CMake\"\n        )\n        self.template_format_dict = {\n            # Note that CMake files always uses forward slash separators in path names,\n            # even on Windows.  Therefore replace path separators here.\n            \"cmake_dir\": _to_cmake_path(getdeps_cmake_dir),\n            \"lib_name\": self.manifest.name,\n            \"manifest_name\": self.manifest.name,\n            \"namespace\": self.manifest.name,\n            \"upper_name\": self.manifest.name.upper().replace(\"-\", \"_\"),\n            \"find_dependency_lines\": \"\\n\".join(find_dependency_lines),\n        }\n\n        # Find sources from the root directory\n        path_mapping: dict[str, str] = {}\n        for entry in os.listdir(self.src_dir):\n            if entry == data_dir_name:\n                continue\n            self._add_sources(path_mapping, os.path.join(self.src_dir, entry), entry)\n\n        # Files under the .data directory also need to be installed in the correct\n        # locations\n        if os.path.exists(data_dir_name):\n            # TODO: process the subdirectories of data_dir_name\n            # This isn't implemented yet since for now we have only needed dependencies\n            # on some simple pure Python wheels, so I haven't tested against wheels with\n            # additional files in the .data directory.\n            raise Exception(\n                \"handling of the subdirectories inside %s is not implemented yet\"\n                % data_dir_name\n            )\n\n        # Emit CMake files\n        self._write_cmakelists(path_mapping, dep_list)\n        self._write_cmake_config_template()\n\n        # Run the build\n        self._run_cmake_build(reconfigure)\n\n    def _run_cmake_build(self, reconfigure: bool) -> None:\n        cmake_builder = CMakeBuilder(\n            loader=self.loader,\n            dep_manifests=self.dep_manifests,\n            build_opts=self.build_opts,\n            ctx=self.ctx,\n            manifest=self.manifest,\n            # Note that we intentionally supply src_dir=build_dir,\n            # since we wrote out our generated CMakeLists.txt in the build directory\n            src_dir=self.build_dir,\n            build_dir=self.build_dir,\n            inst_dir=self.inst_dir,\n            defines={},\n            final_install_prefix=None,\n        )\n        cmake_builder.build(reconfigure=reconfigure)\n\n    def _write_cmakelists(\n        self, path_mapping: dict[str, str], dependencies: list[str]\n    ) -> None:\n        cmake_path = os.path.join(self.build_dir, \"CMakeLists.txt\")\n        with open(cmake_path, \"w\") as f:\n            f.write(CMAKE_HEADER.format(**self.template_format_dict))\n            for dep in dependencies:\n                f.write(\"find_package({0} REQUIRED)\\n\".format(dep))\n\n            f.write(\n                \"add_fb_python_library({lib_name}\\n\".format(**self.template_format_dict)\n            )\n            f.write('  BASE_DIR \"%s\"\\n' % _to_cmake_path(self.src_dir))\n            f.write(\"  SOURCES\\n\")\n            for src_path, install_path in path_mapping.items():\n                f.write(\n                    '    \"%s=%s\"\\n'\n                    % (_to_cmake_path(src_path), _to_cmake_path(install_path))\n                )\n            if dependencies:\n                f.write(\"  DEPENDS\\n\")\n                for dep in dependencies:\n                    f.write('    \"{0}::{0}\"\\n'.format(dep))\n            f.write(\")\\n\")\n\n            f.write(CMAKE_FOOTER.format(**self.template_format_dict))\n\n    def _write_cmake_config_template(self) -> None:\n        config_path_name = self.manifest.name + \"-config.cmake.in\"\n        output_path = os.path.join(self.build_dir, config_path_name)\n\n        with open(output_path, \"w\") as f:\n            f.write(CMAKE_CONFIG_FILE.format(**self.template_format_dict))\n\n    def _add_sources(\n        self, path_mapping: dict[str, str], src_path: str, install_path: str\n    ) -> None:\n        s = os.lstat(src_path)\n        if not stat.S_ISDIR(s.st_mode):\n            path_mapping[src_path] = install_path\n            return\n\n        for entry in os.listdir(src_path):\n            self._add_sources(\n                path_mapping,\n                os.path.join(src_path, entry),\n                os.path.join(install_path, entry),\n            )\n\n    def _parse_wheel_name(self) -> WheelNameInfo:\n        # The ArchiveFetcher prepends \"manifest_name-\", so strip that off first.\n        wheel_name = os.path.basename(self.src_dir)\n        prefix = self.manifest.name + \"-\"\n        if not wheel_name.startswith(prefix):\n            raise Exception(\n                \"expected wheel source directory to be of the form %s-NAME.whl\"\n                % (prefix,)\n            )\n        wheel_name = wheel_name[len(prefix) :]\n\n        wheel_name_re = re.compile(\n            r\"(?P<distribution>[^-]+)\"\n            r\"-(?P<version>\\d+[^-]*)\"\n            r\"(-(?P<build>\\d+[^-]*))?\"\n            r\"-(?P<python>\\w+\\d+(\\.\\w+\\d+)*)\"\n            r\"-(?P<abi>\\w+)\"\n            r\"-(?P<platform>\\w+(\\.\\w+)*)\"\n            r\"\\.whl\"\n        )\n        match = wheel_name_re.match(wheel_name)\n        if not match:\n            raise Exception(\n                \"bad python wheel name %s: expected to have the form \"\n                \"DISTRIBUTION-VERSION-[-BUILD]-PYTAG-ABI-PLATFORM\"\n            )\n\n        return WheelNameInfo(\n            distribution=match.group(\"distribution\"),\n            version=match.group(\"version\"),\n            build=match.group(\"build\"),\n            python=match.group(\"python\"),\n            abi=match.group(\"abi\"),\n            platform=match.group(\"platform\"),\n        )\n\n    # pyre-fixme[24]: Generic type `email.message.Message` expects 2 type parameters.\n    def _read_wheel_metadata(self, wheel_name: WheelNameInfo) -> email.message.Message:\n        metadata_path = os.path.join(self.dist_info_dir, \"WHEEL\")\n        with codecs.open(metadata_path, \"r\", encoding=\"utf-8\") as f:\n            return email.message_from_file(f)\n\n\ndef _to_cmake_path(path: str) -> str:\n    # CMake always uses forward slashes to separate paths in CMakeLists.txt files,\n    # even on Windows.  It treats backslashes as character escapes, so using\n    # backslashes in the path will cause problems.  Therefore replace all path\n    # separators with forward slashes to make sure the paths are correct on Windows.\n    # e.g. \"C:\\foo\\bar.txt\" becomes \"C:/foo/bar.txt\"\n    return path.replace(os.path.sep, \"/\")\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/runcmd.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\nimport os\nimport select\nimport subprocess\nimport sys\nfrom collections.abc import Callable\nfrom shlex import quote as shellquote\n\nfrom .envfuncs import Env\nfrom .platform import is_windows\n\n\nclass RunCommandError(Exception):\n    pass\n\n\ndef make_memory_limit_preexec_fn(\n    job_weight_mib: int,\n) -> Callable[[], None] | None:\n    \"\"\"Create a preexec_fn that sets a per-process virtual memory limit.\n\n    When getdeps spawns build commands (cmake -> ninja -> N compiler processes),\n    the parallelism is computed from available RAM divided by job_weight_mib.\n    However, there is no enforcement of that budget: if a compiler or linker\n    process exceeds its expected memory usage, the system can run out of RAM\n    and the Linux OOM killer may terminate arbitrary processes — including the\n    user's shell or terminal.\n\n    This function returns a callable suitable for subprocess.Popen's preexec_fn\n    parameter. It runs in each child process after fork() but before exec(),\n    setting RLIMIT_AS (virtual address space limit) so that a runaway process\n    gets a failed allocation (std::bad_alloc / ENOMEM) instead of triggering\n    the OOM killer. The limit is inherited by all descendant processes (ninja,\n    compiler invocations, etc.).\n\n    The per-process limit is set to job_weight_mib * 10. The 10x multiplier\n    accounts for the fact that RLIMIT_AS caps virtual address space, which is\n    typically 2-4x larger than resident (physical) memory for C++ compilers\n    due to memory-mapped files, shared libraries, and address space reservations\n    that don't consume physical RAM. The multiplier is intentionally generous:\n    the goal is a safety net that catches genuine runaways before the OOM killer\n    fires, not a tight per-job budget.\n\n    Only applies on Linux, where the OOM killer is the problem. Returns None\n    on other platforms.\n    \"\"\"\n    if sys.platform != \"linux\":\n        return None\n\n    # Each job is budgeted job_weight_mib of physical RAM. Virtual address\n    # space is typically 2-4x RSS. Use 10x as a generous safety net: tight\n    # enough to stop a runaway process before the OOM killer fires, but loose\n    # enough to avoid false positives from normal virtual memory overhead.\n    limit_bytes: int = job_weight_mib * 10 * 1024 * 1024\n\n    def _set_memory_limit() -> None:\n        import resource\n\n        resource.setrlimit(resource.RLIMIT_AS, (limit_bytes, limit_bytes))\n\n    return _set_memory_limit\n\n\ndef _print_env_diff(env: Env, log_fn: Callable[[str], None]) -> None:\n    current_keys = set(os.environ.keys())\n    wanted_env = set(env.keys())\n\n    unset_keys = current_keys.difference(wanted_env)\n    for k in sorted(unset_keys):\n        log_fn(\"+ unset %s\\n\" % k)\n\n    added_keys = wanted_env.difference(current_keys)\n    for k in wanted_env.intersection(current_keys):\n        if os.environ[k] != env[k]:\n            added_keys.add(k)\n\n    for k in sorted(added_keys):\n        if (\"PATH\" in k) and (os.pathsep in env[k]):\n            log_fn(\"+ %s=\\\\\\n\" % k)\n            for elem in env[k].split(os.pathsep):\n                log_fn(\"+      %s%s\\\\\\n\" % (shellquote(elem), os.pathsep))\n        else:\n            log_fn(\"+ %s=%s \\\\\\n\" % (k, shellquote(env[k])))\n\n\ndef check_cmd(\n    cmd: list[str],\n    env: Env | None = None,\n    cwd: str | None = None,\n    allow_fail: bool = False,\n    log_file: str | None = None,\n) -> None:\n    \"\"\"Run the command and abort on failure\"\"\"\n    rc = run_cmd(cmd, env=env, cwd=cwd, allow_fail=allow_fail, log_file=log_file)\n    if rc != 0:\n        raise RuntimeError(f\"Failure exit code {rc} for command {cmd}\")\n\n\ndef run_cmd(\n    cmd: list[str],\n    env: Env | None = None,\n    cwd: str | None = None,\n    allow_fail: bool = False,\n    log_file: str | None = None,\n    preexec_fn: Callable[[], None] | None = None,\n) -> int:\n    def log_to_stdout(msg: str) -> None:\n        sys.stdout.buffer.write(msg.encode(errors=\"surrogateescape\"))\n\n    if log_file is not None:\n        with open(log_file, \"a\", encoding=\"utf-8\", errors=\"surrogateescape\") as log:\n\n            # pyre-fixme[53]: Captured variable `log` is not annotated.\n            def log_function(msg: str) -> None:\n                log.write(msg)\n                log_to_stdout(msg)\n\n            return _run_cmd(\n                cmd,\n                env=env,\n                cwd=cwd,\n                allow_fail=allow_fail,\n                log_fn=log_function,\n                preexec_fn=preexec_fn,\n            )\n    else:\n        return _run_cmd(\n            cmd,\n            env=env,\n            cwd=cwd,\n            allow_fail=allow_fail,\n            log_fn=log_to_stdout,\n            preexec_fn=preexec_fn,\n        )\n\n\ndef _run_cmd(\n    cmd: list[str],\n    env: Env | None,\n    cwd: str | None,\n    allow_fail: bool,\n    log_fn: Callable[[str], None],\n    preexec_fn: Callable[[], None] | None = None,\n) -> int:\n    log_fn(\"---\\n\")\n    try:\n        cmd_str = \" \\\\\\n+      \".join(shellquote(arg) for arg in cmd)\n    except TypeError:\n        # eg: one of the elements is None\n        raise RunCommandError(\"problem quoting cmd: %r\" % cmd)\n\n    if env:\n        assert isinstance(env, Env)\n        _print_env_diff(env, log_fn)\n\n        # Convert from our Env type to a regular dict.\n        # This is needed because python3 looks up b'PATH' and 'PATH'\n        # and emits an error if both are present.  In our Env type\n        # we'll return the same value for both requests, but we don't\n        # have duplicate potentially conflicting values which is the\n        # spirit of the check.\n        env_dict: dict[str, str] | None = dict(env.items())\n    else:\n        env_dict = None\n\n    if cwd:\n        log_fn(\"+ cd %s && \\\\\\n\" % shellquote(cwd))\n        # Our long path escape sequence may confuse cmd.exe, so if the cwd\n        # is short enough, strip that off.\n        if is_windows() and (len(cwd) < 250) and cwd.startswith(\"\\\\\\\\?\\\\\"):\n            cwd = cwd[4:]\n\n    log_fn(\"+ %s\\n\" % cmd_str)\n\n    isinteractive = os.isatty(sys.stdout.fileno())\n    if isinteractive:\n        stdout = None\n        sys.stdout.buffer.flush()\n    else:\n        stdout = subprocess.PIPE\n\n    try:\n        p = subprocess.Popen(\n            cmd,\n            env=env_dict,\n            cwd=cwd,\n            stdout=stdout,\n            stderr=subprocess.STDOUT,\n            preexec_fn=preexec_fn,\n        )\n    except (TypeError, ValueError, OSError) as exc:\n        log_fn(\"error running `%s`: %s\" % (cmd_str, exc))\n        raise RunCommandError(\n            \"%s while running `%s` with env=%r\\nos.environ=%r\"\n            % (str(exc), cmd_str, env_dict, os.environ)\n        )\n\n    if not isinteractive:\n        _pipe_output(p, log_fn)\n\n    p.wait()\n    if p.returncode != 0 and not allow_fail:\n        raise subprocess.CalledProcessError(p.returncode, cmd)\n\n    return p.returncode\n\n\nif hasattr(select, \"poll\"):\n\n    def _pipe_output(p: subprocess.Popen[bytes], log_fn: Callable[[str], None]) -> None:\n        \"\"\"Read output from p.stdout and call log_fn() with each chunk of data as it\n        becomes available.\"\"\"\n        # Perform non-blocking reads\n        import fcntl\n\n        assert p.stdout is not None\n        fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)\n        poll = select.poll()\n        poll.register(p.stdout.fileno(), select.POLLIN)\n\n        buffer_size = 4096\n        while True:\n            poll.poll()\n            data = p.stdout.read(buffer_size)\n            if not data:\n                break\n            # log_fn() accepts arguments as str (binary in Python 2, unicode in\n            # Python 3).  In Python 3 the subprocess output will be plain bytes,\n            # and need to be decoded.\n            if not isinstance(data, str):\n                data = data.decode(\"utf-8\", errors=\"surrogateescape\")\n            log_fn(data)\n\nelse:\n\n    def _pipe_output(p: subprocess.Popen[bytes], log_fn: Callable[[str], None]) -> None:\n        \"\"\"Read output from p.stdout and call log_fn() with each chunk of data as it\n        becomes available.\"\"\"\n        # Perform blocking reads.  Use a smaller buffer size to avoid blocking\n        # for very long when data is available.\n        assert p.stdout is not None\n        buffer_size = 64\n        while True:\n            # pyre-fixme[16]: Optional type has no attribute `read`.\n            data = p.stdout.read(buffer_size)\n            if not data:\n                break\n            # log_fn() accepts arguments as str (binary in Python 2, unicode in\n            # Python 3).  In Python 3 the subprocess output will be plain bytes,\n            # and need to be decoded.\n            if not isinstance(data, str):\n                data = data.decode(\"utf-8\", errors=\"surrogateescape\")\n            log_fn(data)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/subcmd.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\nfrom __future__ import annotations\n\nimport argparse\nfrom collections.abc import Callable\n\n\nclass SubCmd:\n    NAME: str | None = None\n    HELP: str | None = None\n\n    def run(self, args: argparse.Namespace) -> int:\n        \"\"\"perform the command\"\"\"\n        return 0\n\n    def setup_parser(self, parser: argparse.ArgumentParser) -> None:\n        # Subclasses should override setup_parser() if they have any\n        # command line options or arguments.\n        pass\n\n\nCmdTable: list[type[SubCmd]] = []\n\n\ndef add_subcommands(\n    parser: argparse._SubParsersAction[argparse.ArgumentParser],\n    common_args: argparse.ArgumentParser,\n    cmd_table: list[type[SubCmd]] = CmdTable,\n) -> None:\n    \"\"\"Register parsers for the defined commands with the provided parser\"\"\"\n    for cls in cmd_table:\n        command = cls()\n        command_parser = parser.add_parser(\n            # pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.\n            command.NAME,\n            help=command.HELP,\n            parents=[common_args],\n        )\n        command.setup_parser(command_parser)\n        command_parser.set_defaults(func=command.run)\n\n\ndef cmd(\n    name: str,\n    help: str | None = None,\n    cmd_table: list[type[SubCmd]] = CmdTable,\n) -> Callable[[type[SubCmd]], type[SubCmd]]:\n    \"\"\"\n    @cmd() is a decorator that can be used to help define Subcmd instances\n\n    Example usage:\n\n        @subcmd('list', 'Show the result list')\n        class ListCmd(Subcmd):\n            def run(self, args):\n                # Perform the command actions here...\n                pass\n    \"\"\"\n\n    def wrapper(cls: type[SubCmd]) -> type[SubCmd]:\n        class SubclassedCmd(cls):\n            NAME = name\n            HELP = help\n\n        # pyre-fixme[6]: For 1st argument expected `Type[SubCmd]` but got\n        #  `Type[SubclassedCmd]`.\n        # pyre-fixme[16]: Callable `cmd` has no attribute `wrapper`.\n        cmd_table.append(SubclassedCmd)\n        # pyre-fixme[7]: Expected `Type[SubCmd]` but got `Type[SubclassedCmd]`.\n        return SubclassedCmd\n\n    return wrapper\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/expr_test.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-unsafe\n\n\nimport unittest\n\nfrom ..expr import parse_expr\n\n\nclass ExprTest(unittest.TestCase):\n    def test_equal(self) -> None:\n        valid_variables = {\"foo\", \"some_var\", \"another_var\"}\n        e = parse_expr(\"foo=bar\", valid_variables)\n        self.assertTrue(e.eval({\"foo\": \"bar\"}))\n        self.assertFalse(e.eval({\"foo\": \"not-bar\"}))\n        self.assertFalse(e.eval({\"not-foo\": \"bar\"}))\n\n    def test_not_equal(self) -> None:\n        valid_variables = {\"foo\"}\n        e = parse_expr(\"not(foo=bar)\", valid_variables)\n        self.assertFalse(e.eval({\"foo\": \"bar\"}))\n        self.assertTrue(e.eval({\"foo\": \"not-bar\"}))\n\n    def test_bad_not(self) -> None:\n        valid_variables = {\"foo\"}\n        with self.assertRaises(Exception):\n            parse_expr(\"foo=not(bar)\", valid_variables)\n\n    def test_bad_variable(self) -> None:\n        valid_variables = {\"bar\"}\n        with self.assertRaises(Exception):\n            parse_expr(\"foo=bar\", valid_variables)\n\n    def test_all(self) -> None:\n        valid_variables = {\"foo\", \"baz\"}\n        e = parse_expr(\"all(foo = bar, baz = qux)\", valid_variables)\n        self.assertTrue(e.eval({\"foo\": \"bar\", \"baz\": \"qux\"}))\n        self.assertFalse(e.eval({\"foo\": \"bar\", \"baz\": \"nope\"}))\n        self.assertFalse(e.eval({\"foo\": \"nope\", \"baz\": \"nope\"}))\n\n    def test_any(self) -> None:\n        valid_variables = {\"foo\", \"baz\"}\n        e = parse_expr(\"any(foo = bar, baz = qux)\", valid_variables)\n        self.assertTrue(e.eval({\"foo\": \"bar\", \"baz\": \"qux\"}))\n        self.assertTrue(e.eval({\"foo\": \"bar\", \"baz\": \"nope\"}))\n        self.assertFalse(e.eval({\"foo\": \"nope\", \"baz\": \"nope\"}))\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/fixtures/duplicate/foo",
    "content": "[manifest]\nname = foo\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/fixtures/duplicate/subdir/foo",
    "content": "[manifest]\nname = foo\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/manifest_test.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-unsafe\n\n\nimport sys\nimport unittest\n\nfrom ..load import load_all_manifests, patch_loader\nfrom ..manifest import ManifestParser\n\n\nclass ManifestTest(unittest.TestCase):\n    def test_missing_section(self) -> None:\n        with self.assertRaisesRegex(\n            Exception, \"manifest file test is missing required section manifest\"\n        ):\n            ManifestParser(\"test\", \"\")\n\n    def test_missing_name(self) -> None:\n        with self.assertRaisesRegex(\n            Exception,\n            \"manifest file test section 'manifest' is missing required field 'name'\",\n        ):\n            ManifestParser(\n                \"test\",\n                \"\"\"\n[manifest]\n\"\"\",\n            )\n\n    def test_minimal(self) -> None:\n        p = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\n\"\"\",\n        )\n        self.assertEqual(p.name, \"test\")\n        self.assertEqual(p.fbsource_path, None)\n\n    def test_minimal_with_fbsource_path(self) -> None:\n        p = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\nfbsource_path = fbcode/wat\n\"\"\",\n        )\n        self.assertEqual(p.name, \"test\")\n        self.assertEqual(p.fbsource_path, \"fbcode/wat\")\n\n    def test_unknown_field(self) -> None:\n        with self.assertRaisesRegex(\n            Exception,\n            (\n                \"manifest file test section 'manifest' contains \"\n                \"unknown field 'invalid.field'\"\n            ),\n        ):\n            ManifestParser(\n                \"test\",\n                \"\"\"\n[manifest]\nname = test\ninvalid.field = woot\n\"\"\",\n            )\n\n    def test_invalid_section_name(self) -> None:\n        with self.assertRaisesRegex(\n            Exception, \"manifest file test contains unknown section 'invalid.section'\"\n        ):\n            ManifestParser(\n                \"test\",\n                \"\"\"\n[manifest]\nname = test\n\n[invalid.section]\nfoo = bar\n\"\"\",\n            )\n\n    def test_value_in_dependencies_section(self) -> None:\n        with self.assertRaisesRegex(\n            Exception,\n            (\n                \"manifest file test section 'dependencies' has \"\n                \"'foo = bar' but this section doesn't allow \"\n                \"specifying values for its entries\"\n            ),\n        ):\n            ManifestParser(\n                \"test\",\n                \"\"\"\n[manifest]\nname = test\n\n[dependencies]\nfoo = bar\n\"\"\",\n            )\n\n    def test_invalid_conditional_section_name(self) -> None:\n        with self.assertRaisesRegex(\n            Exception,\n            (\n                \"manifest file test section 'dependencies.=' \"\n                \"has invalid conditional: expected \"\n                \"identifier found =\"\n            ),\n        ):\n            ManifestParser(\n                \"test\",\n                \"\"\"\n[manifest]\nname = test\n\n[dependencies.=]\n\"\"\",\n            )\n\n    def test_section_as_args(self) -> None:\n        p = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\n\n[dependencies]\na\nb\nc\n\n[dependencies.test=on]\nfoo\n\"\"\",\n        )\n        self.assertEqual(p.get_section_as_args(\"dependencies\"), [\"a\", \"b\", \"c\"])\n        self.assertEqual(\n            p.get_section_as_args(\"dependencies\", {\"test\": \"off\"}), [\"a\", \"b\", \"c\"]\n        )\n        self.assertEqual(\n            p.get_section_as_args(\"dependencies\", {\"test\": \"on\"}),\n            [\"a\", \"b\", \"c\", \"foo\"],\n        )\n\n        p2 = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\n\n[autoconf.args]\n--prefix=/foo\n--with-woot\n\"\"\",\n        )\n        self.assertEqual(\n            p2.get_section_as_args(\"autoconf.args\"), [\"--prefix=/foo\", \"--with-woot\"]\n        )\n\n    def test_section_as_dict(self) -> None:\n        p = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\n\n[cmake.defines]\nfoo = bar\n\n[cmake.defines.test=on]\nfoo = baz\n\"\"\",\n        )\n        self.assertEqual(p.get_section_as_dict(\"cmake.defines\", {}), {\"foo\": \"bar\"})\n        self.assertEqual(\n            p.get_section_as_dict(\"cmake.defines\", {\"test\": \"on\"}), {\"foo\": \"baz\"}\n        )\n\n        p2 = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\n\n[cmake.defines.test=on]\nfoo = baz\n\n[cmake.defines]\nfoo = bar\n\"\"\",\n        )\n        self.assertEqual(\n            p2.get_section_as_dict(\"cmake.defines\", {\"test\": \"on\"}),\n            {\"foo\": \"bar\"},\n            msg=\"sections cascade in the order they appear in the manifest\",\n        )\n\n    def test_parse_common_manifests(self) -> None:\n        patch_loader(__name__)\n        # pyre-fixme[6]: For 1st argument expected `BuildOptions` but got `None`.\n        manifests = load_all_manifests(None)\n        self.assertNotEqual(0, len(manifests), msg=\"parsed some number of manifests\")\n\n    def test_mismatch_name(self) -> None:\n        with self.assertRaisesRegex(\n            Exception,\n            \"filename of the manifest 'foo' does not match the manifest name 'bar'\",\n        ):\n            ManifestParser(\n                \"foo\",\n                \"\"\"\n[manifest]\nname = bar\n\"\"\",\n            )\n\n    def test_duplicate_manifest(self) -> None:\n        patch_loader(__name__, \"fixtures/duplicate\")\n\n        with self.assertRaisesRegex(Exception, \"found duplicate manifest 'foo'\"):\n            # pyre-fixme[6]: For 1st argument expected `BuildOptions` but got `None`.\n            load_all_manifests(None)\n\n    if sys.version_info < (3, 2):\n\n        def assertRaisesRegex(self, *args, **kwargs):\n            return self.assertRaisesRegex(*args, **kwargs)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/platform_test.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-unsafe\n\n\nimport unittest\n\nfrom ..platform import HostType\n\n\nclass PlatformTest(unittest.TestCase):\n    def test_create(self) -> None:\n        p = HostType()\n        self.assertNotEqual(p.ostype, None, msg=\"probed and returned something\")\n\n        tuple_string = p.as_tuple_string()\n        round_trip = HostType.from_tuple_string(tuple_string)\n        self.assertEqual(round_trip, p)\n\n    def test_rendering_of_none(self) -> None:\n        p = HostType(ostype=\"foo\")\n        self.assertEqual(p.as_tuple_string(), \"foo-none-none\")\n\n    def test_is_methods(self) -> None:\n        p = HostType(ostype=\"windows\")\n        self.assertTrue(p.is_windows())\n        self.assertFalse(p.is_darwin())\n        self.assertFalse(p.is_linux())\n\n        p = HostType(ostype=\"darwin\")\n        self.assertFalse(p.is_windows())\n        self.assertTrue(p.is_darwin())\n        self.assertFalse(p.is_linux())\n\n        p = HostType(ostype=\"linux\")\n        self.assertFalse(p.is_windows())\n        self.assertFalse(p.is_darwin())\n        self.assertTrue(p.is_linux())\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/retry_test.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-unsafe\n\n\nimport unittest\nfrom unittest.mock import call, MagicMock, patch\n\nfrom ..buildopts import BuildOptions\nfrom ..errors import TransientFailure\nfrom ..fetcher import ArchiveFetcher\nfrom ..manifest import ManifestParser\n\n\nclass RetryTest(unittest.TestCase):\n    def _get_build_opts(self) -> BuildOptions:\n        mock_build_opts = MagicMock(spec=BuildOptions)\n        mock_build_opts.scratch_dir = \"/path/to/scratch_dir\"\n        return mock_build_opts\n\n    def _get_manifest(self) -> ManifestParser:\n        mock_manifest_parser = MagicMock(spec=ManifestParser)\n        mock_manifest_parser.name = \"mock_manifest_parser\"\n        return mock_manifest_parser\n\n    def _get_archive_fetcher(self) -> ArchiveFetcher:\n        return ArchiveFetcher(\n            build_options=self._get_build_opts(),\n            manifest=self._get_manifest(),\n            url=\"https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz\",\n            sha256=\"896d76ff65c88f5fd9e42f90d152b0579049158a163431dd77cdc57748b1d7b0\",\n        )\n\n    @patch(\"os.makedirs\")\n    @patch(\"os.environ.get\")\n    @patch(\"time.sleep\")\n    @patch(\"subprocess.run\")\n    def test_no_retries(\n        self, mock_run, mock_sleep, mock_os_environ_get, mock_makedirs\n    ) -> None:\n        def custom_makedirs(path, exist_ok=False):\n            return None\n\n        def custom_get(key, default=None):\n            if key == \"GETDEPS_USE_WGET\":\n                return \"1\"\n            elif key == \"GETDEPS_WGET_ARGS\":\n                return \"\"\n            else:\n                return None\n\n        mock_makedirs.side_effect = custom_makedirs\n        mock_os_environ_get.side_effect = custom_get\n        mock_sleep.side_effect = None\n        fetcher = self._get_archive_fetcher()\n        fetcher._verify_hash = MagicMock(return_value=None)\n        fetcher._download()\n        mock_sleep.assert_has_calls([], any_order=False)\n        mock_run.assert_called_once_with(\n            [\n                \"wget\",\n                \"-O\",\n                \"/path/to/scratch_dir/downloads/mock_manifest_parser-v256.7.tar.gz\",\n                \"https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz\",\n            ],\n            capture_output=True,\n        )\n\n    @patch(\"random.random\")\n    @patch(\"os.makedirs\")\n    @patch(\"os.environ.get\")\n    @patch(\"time.sleep\")\n    @patch(\"subprocess.run\")\n    def test_retries(\n        self, mock_run, mock_sleep, mock_os_environ_get, mock_makedirs, mock_random\n    ) -> None:\n        def custom_makedirs(path, exist_ok=False):\n            return None\n\n        def custom_get(key, default=None):\n            if key == \"GETDEPS_USE_WGET\":\n                return \"1\"\n            elif key == \"GETDEPS_WGET_ARGS\":\n                return \"\"\n            else:\n                return None\n\n        mock_random.return_value = 0\n\n        mock_run.side_effect = [\n            IOError(\"<urlopen error [Errno 104] Connection reset by peer>\"),\n            IOError(\"<urlopen error [Errno 104] Connection reset by peer>\"),\n            None,\n        ]\n        mock_makedirs.side_effect = custom_makedirs\n        mock_os_environ_get.side_effect = custom_get\n        mock_sleep.side_effect = None\n        fetcher = self._get_archive_fetcher()\n        fetcher._verify_hash = MagicMock(return_value=None)\n        fetcher._download()\n        mock_sleep.assert_has_calls([call(2), call(4)], any_order=False)\n        calls = [\n            call(\n                [\n                    \"wget\",\n                    \"-O\",\n                    \"/path/to/scratch_dir/downloads/mock_manifest_parser-v256.7.tar.gz\",\n                    \"https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz\",\n                ],\n                capture_output=True,\n            ),\n        ] * 3\n\n        mock_run.assert_has_calls(calls, any_order=False)\n\n    @patch(\"random.random\")\n    @patch(\"os.makedirs\")\n    @patch(\"os.environ.get\")\n    @patch(\"time.sleep\")\n    @patch(\"subprocess.run\")\n    def test_all_retries(\n        self, mock_run, mock_sleep, mock_os_environ_get, mock_makedirs, mock_random\n    ) -> None:\n        def custom_makedirs(path, exist_ok=False):\n            return None\n\n        def custom_get(key, default=None):\n            if key == \"GETDEPS_USE_WGET\":\n                return \"1\"\n            elif key == \"GETDEPS_WGET_ARGS\":\n                return \"\"\n            else:\n                return None\n\n        mock_random.return_value = 0\n\n        mock_run.side_effect = IOError(\n            \"<urlopen error [Errno 104] Connection reset by peer>\"\n        )\n        mock_makedirs.side_effect = custom_makedirs\n        mock_os_environ_get.side_effect = custom_get\n        mock_sleep.side_effect = None\n        fetcher = self._get_archive_fetcher()\n        fetcher._verify_hash = MagicMock(return_value=None)\n        with self.assertRaises(TransientFailure):\n            fetcher._download()\n        mock_sleep.assert_has_calls(\n            [call(2), call(4), call(8), call(10)], any_order=False\n        )\n        calls = [\n            call(\n                [\n                    \"wget\",\n                    \"-O\",\n                    \"/path/to/scratch_dir/downloads/mock_manifest_parser-v256.7.tar.gz\",\n                    \"https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz\",\n                ],\n                capture_output=True,\n            ),\n        ] * 5\n\n        mock_run.assert_has_calls(calls, any_order=False)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/scratch_test.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-unsafe\n\n\nimport unittest\n\nfrom ..buildopts import find_existing_win32_subst_for_path\n\n\nclass Win32SubstTest(unittest.TestCase):\n    def test_no_existing_subst(self) -> None:\n        self.assertIsNone(\n            find_existing_win32_subst_for_path(\n                r\"C:\\users\\alice\\appdata\\local\\temp\\fbcode_builder_getdeps\",\n                subst_mapping={},\n            )\n        )\n        self.assertIsNone(\n            find_existing_win32_subst_for_path(\n                r\"C:\\users\\alice\\appdata\\local\\temp\\fbcode_builder_getdeps\",\n                subst_mapping={\"X:\\\\\": r\"C:\\users\\alice\\appdata\\local\\temp\\other\"},\n            )\n        )\n\n    def test_exact_match_returns_drive_path(self) -> None:\n        self.assertEqual(\n            find_existing_win32_subst_for_path(\n                r\"C:\\temp\\fbcode_builder_getdeps\",\n                subst_mapping={\"X:\\\\\": r\"C:\\temp\\fbcode_builder_getdeps\"},\n            ),\n            \"X:\\\\\",\n        )\n        self.assertEqual(\n            find_existing_win32_subst_for_path(\n                r\"C:/temp/fbcode_builder_getdeps\",\n                subst_mapping={\"X:\\\\\": r\"C:/temp/fbcode_builder_getdeps\"},\n            ),\n            \"X:\\\\\",\n        )\n\n    def test_multiple_exact_matches_returns_arbitrary_drive_path(self) -> None:\n        self.assertIn(\n            find_existing_win32_subst_for_path(\n                r\"C:\\temp\\fbcode_builder_getdeps\",\n                subst_mapping={\n                    \"X:\\\\\": r\"C:\\temp\\fbcode_builder_getdeps\",\n                    \"Y:\\\\\": r\"C:\\temp\\fbcode_builder_getdeps\",\n                    \"Z:\\\\\": r\"C:\\temp\\fbcode_builder_getdeps\",\n                },\n            ),\n            (\"X:\\\\\", \"Y:\\\\\", \"Z:\\\\\"),\n        )\n\n    def test_drive_letter_is_case_insensitive(self) -> None:\n        self.assertEqual(\n            find_existing_win32_subst_for_path(\n                r\"C:\\temp\\fbcode_builder_getdeps\",\n                subst_mapping={\"X:\\\\\": r\"c:\\temp\\fbcode_builder_getdeps\"},\n            ),\n            \"X:\\\\\",\n        )\n\n    def test_path_components_are_case_insensitive(self) -> None:\n        self.assertEqual(\n            find_existing_win32_subst_for_path(\n                r\"C:\\TEMP\\FBCODE_builder_getdeps\",\n                subst_mapping={\"X:\\\\\": r\"C:\\temp\\fbcode_builder_getdeps\"},\n            ),\n            \"X:\\\\\",\n        )\n        self.assertEqual(\n            find_existing_win32_subst_for_path(\n                r\"C:\\temp\\fbcode_builder_getdeps\",\n                subst_mapping={\"X:\\\\\": r\"C:\\TEMP\\FBCODE_builder_getdeps\"},\n            ),\n            \"X:\\\\\",\n        )\n"
  },
  {
    "path": "build/fbcode_builder/getdeps/test/strip_marker_test.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\n# pyre-strict\n\n\nimport os\nimport tempfile\nimport unittest\n\nfrom ..fetcher import filter_strip_marker\nfrom ..manifest import ManifestParser\n\n\nclass ManifestStripMarkerTest(unittest.TestCase):\n    def test_default_strip_marker(self) -> None:\n        p = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\n\"\"\",\n        )\n        self.assertEqual(p.shipit_strip_marker, \"@fb-only\")\n\n    def test_custom_strip_marker(self) -> None:\n        p = ManifestParser(\n            \"test\",\n            \"\"\"\n[manifest]\nname = test\nshipit_strip_marker = @oss-disable\n\"\"\",\n        )\n        self.assertEqual(p.shipit_strip_marker, \"@oss-disable\")\n\n\nclass FilterStripMarkerTest(unittest.TestCase):\n    def _write_temp(self, content: str) -> str:\n        fd, path = tempfile.mkstemp(suffix=\".txt\")\n        os.close(fd)\n        with open(path, \"w\") as f:\n            f.write(content)\n        return path\n\n    def _read(self, path: str) -> str:\n        with open(path, \"r\") as f:\n            return f.read()\n\n    def test_single_line_removal(self) -> None:\n        path = self._write_temp(\"keep this\\nremove this @fb-only\\nkeep this too\\n\")\n        try:\n            filter_strip_marker(path, \"@fb-only\")\n            self.assertEqual(self._read(path), \"keep this\\nkeep this too\\n\")\n        finally:\n            os.unlink(path)\n\n    def test_block_removal(self) -> None:\n        content = (\n            \"before\\n\"\n            \"// @fb-only-start\\n\"\n            \"secret stuff\\n\"\n            \"more secret\\n\"\n            \"// @fb-only-end\\n\"\n            \"after\\n\"\n        )\n        path = self._write_temp(content)\n        try:\n            filter_strip_marker(path, \"@fb-only\")\n            self.assertEqual(self._read(path), \"before\\nafter\\n\")\n        finally:\n            os.unlink(path)\n\n    def test_no_marker_present_no_change(self) -> None:\n        original = \"nothing special here\\njust plain code\\n\"\n        path = self._write_temp(original)\n        try:\n            filter_strip_marker(path, \"@fb-only\")\n            self.assertEqual(self._read(path), original)\n        finally:\n            os.unlink(path)\n\n    def test_custom_marker_single_line(self) -> None:\n        content = \"keep\\nremove @oss-disable\\nkeep too\\n\"\n        path = self._write_temp(content)\n        try:\n            filter_strip_marker(path, \"@oss-disable\")\n            self.assertEqual(self._read(path), \"keep\\nkeep too\\n\")\n        finally:\n            os.unlink(path)\n\n    def test_custom_marker_block(self) -> None:\n        content = (\n            \"before\\n\"\n            \"# @oss-disable-start\\n\"\n            \"internal only\\n\"\n            \"# @oss-disable-end\\n\"\n            \"after\\n\"\n        )\n        path = self._write_temp(content)\n        try:\n            filter_strip_marker(path, \"@oss-disable\")\n            self.assertEqual(self._read(path), \"before\\nafter\\n\")\n        finally:\n            os.unlink(path)\n\n    def test_custom_marker_ignores_default(self) -> None:\n        \"\"\"When using a custom marker, @fb-only lines should be kept.\"\"\"\n        content = \"keep @fb-only\\nremove @oss-disable\\nplain\\n\"\n        path = self._write_temp(content)\n        try:\n            filter_strip_marker(path, \"@oss-disable\")\n            self.assertEqual(self._read(path), \"keep @fb-only\\nplain\\n\")\n        finally:\n            os.unlink(path)\n\n    def test_mixed_single_and_block(self) -> None:\n        content = (\n            \"line1\\n\"\n            \"line2 @fb-only\\n\"\n            \"line3\\n\"\n            \"// @fb-only-start\\n\"\n            \"block content\\n\"\n            \"// @fb-only-end\\n\"\n            \"line4\\n\"\n        )\n        path = self._write_temp(content)\n        try:\n            filter_strip_marker(path, \"@fb-only\")\n            self.assertEqual(self._read(path), \"line1\\nline3\\nline4\\n\")\n        finally:\n            os.unlink(path)\n\n    def test_marker_with_regex_metacharacters(self) -> None:\n        \"\"\"Markers containing regex metacharacters should be escaped properly.\"\"\"\n        content = \"keep\\nremove @fb.only\\nkeep too\\n\"\n        path = self._write_temp(content)\n        try:\n            # With proper escaping, the dot is literal, not a wildcard\n            filter_strip_marker(path, \"@fb.only\")\n            self.assertEqual(self._read(path), \"keep\\nkeep too\\n\")\n        finally:\n            os.unlink(path)\n\n    def test_binary_file_skipped(self) -> None:\n        \"\"\"Binary files that can't be decoded as UTF-8 should be skipped.\"\"\"\n        fd, path = tempfile.mkstemp(suffix=\".bin\")\n        os.close(fd)\n        binary_content = b\"\\x80\\x81\\x82\\xff\\xfe\"\n        with open(path, \"wb\") as f:\n            f.write(binary_content)\n        try:\n            filter_strip_marker(path, \"@fb-only\")\n            with open(path, \"rb\") as f:\n                self.assertEqual(f.read(), binary_content)\n        finally:\n            os.unlink(path)\n"
  },
  {
    "path": "build/fbcode_builder/getdeps.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n\nimport argparse\nimport json\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport tarfile\nimport tempfile\n\n# We don't import cache.create_cache directly as the facebook\n# specific import below may monkey patch it, and we want to\n# observe the patched version of this function!\nimport getdeps.cache as cache_module\nfrom getdeps.buildopts import setup_build_options\nfrom getdeps.dyndeps import create_dyn_dep_munger\nfrom getdeps.errors import TransientFailure\nfrom getdeps.fetcher import (\n    file_name_is_cmake_file,\n    is_public_commit,\n    list_files_under_dir_newer_than_timestamp,\n    SystemPackageFetcher,\n)\nfrom getdeps.load import ManifestLoader\nfrom getdeps.manifest import ManifestParser\nfrom getdeps.platform import HostType\nfrom getdeps.runcmd import check_cmd\nfrom getdeps.subcmd import add_subcommands, cmd, SubCmd\n\ntry:\n    import getdeps.facebook  # noqa: F401\nexcept ImportError:\n    # we don't ship the facebook specific subdir,\n    # so allow that to fail silently\n    pass\n\n\nsys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), \"getdeps\"))\n\n\nclass UsageError(Exception):\n    pass\n\n\n# Shared argument definition for --build-type used by multiple commands\nBUILD_TYPE_ARG = {\n    \"help\": \"Set the build type explicitly: Debug (unoptimized, debug symbols), RelWithDebInfo (optimized with debug symbols, default), MinSizeRel (size-optimized, no debug), or Release (optimized, no debug).\",\n    \"choices\": [\"Debug\", \"Release\", \"RelWithDebInfo\", \"MinSizeRel\"],\n    \"action\": \"store\",\n    \"default\": \"RelWithDebInfo\",\n}\n\n\n@cmd(\"validate-manifest\", \"parse a manifest and validate that it is correct\")\nclass ValidateManifest(SubCmd):\n    def run(self, args):\n        try:\n            ManifestParser(file_name=args.file_name)\n            print(\"OK\", file=sys.stderr)\n            return 0\n        except Exception as exc:\n            print(\"ERROR: %s\" % str(exc), file=sys.stderr)\n            return 1\n\n    def setup_parser(self, parser):\n        parser.add_argument(\"file_name\", help=\"path to the manifest file\")\n\n\n@cmd(\"show-host-type\", \"outputs the host type tuple for the host machine\")\nclass ShowHostType(SubCmd):\n    def run(self, args):\n        host = HostType()\n        print(\"%s\" % host.as_tuple_string())\n        return 0\n\n\nclass ProjectCmdBase(SubCmd):\n    def run(self, args):\n        opts = setup_build_options(args)\n\n        if args.current_project is not None:\n            opts.repo_project = args.current_project\n        if args.project is None:\n            if opts.repo_project is None:\n                raise UsageError(\n                    \"no project name specified, and no .projectid file found\"\n                )\n            if opts.repo_project == \"fbsource\":\n                # The fbsource repository is a little special.  There is no project\n                # manifest file for it.  A specific project must always be explicitly\n                # specified when building from fbsource.\n                raise UsageError(\n                    \"no project name specified (required when building in fbsource)\"\n                )\n            args.project = opts.repo_project\n\n        ctx_gen = opts.get_context_generator()\n        if args.test_dependencies:\n            ctx_gen.set_value_for_all_projects(\"test\", \"on\")\n        if args.enable_tests:\n            ctx_gen.set_value_for_project(args.project, \"test\", \"on\")\n        else:\n            ctx_gen.set_value_for_project(args.project, \"test\", \"off\")\n\n        if opts.shared_libs:\n            ctx_gen.set_value_for_all_projects(\"shared_libs\", \"on\")\n\n        loader = ManifestLoader(opts, ctx_gen)\n        self.process_project_dir_arguments(args, loader)\n\n        manifest = loader.load_manifest(args.project)\n\n        return self.run_project_cmd(args, loader, manifest)\n\n    def process_project_dir_arguments(self, args, loader):\n        def parse_project_arg(arg, arg_type):\n            parts = arg.split(\":\")\n            if len(parts) == 2:\n                project, path = parts\n            elif len(parts) == 1:\n                project = args.project\n                path = parts[0]\n            # On Windows path contains colon, e.g. C:\\open\n            elif os.name == \"nt\" and len(parts) == 3:\n                project = parts[0]\n                path = parts[1] + \":\" + parts[2]\n            else:\n                raise UsageError(\n                    \"invalid %s argument; too many ':' characters: %s\" % (arg_type, arg)\n                )\n\n            return project, os.path.abspath(path)\n\n        # If we are currently running from a project repository,\n        # use the current repository for the project sources.\n        build_opts = loader.build_opts\n        if build_opts.repo_project is not None and build_opts.repo_root is not None:\n            loader.set_project_src_dir(build_opts.repo_project, build_opts.repo_root)\n\n        for arg in args.src_dir:\n            project, path = parse_project_arg(arg, \"--src-dir\")\n            loader.set_project_src_dir(project, path)\n\n        for arg in args.build_dir:\n            project, path = parse_project_arg(arg, \"--build-dir\")\n            loader.set_project_build_dir(project, path)\n\n        for arg in args.install_dir:\n            project, path = parse_project_arg(arg, \"--install-dir\")\n            loader.set_project_install_dir(project, path)\n\n        for arg in args.project_install_prefix:\n            project, path = parse_project_arg(arg, \"--install-prefix\")\n            loader.set_project_install_prefix(project, path)\n\n    def setup_parser(self, parser):\n        parser.add_argument(\n            \"project\",\n            nargs=\"?\",\n            help=(\n                \"name of the project or path to a manifest \"\n                \"file describing the project\"\n            ),\n        )\n        parser.add_argument(\n            \"--no-tests\",\n            action=\"store_false\",\n            dest=\"enable_tests\",\n            default=True,\n            help=\"Disable building tests for this project.\",\n        )\n        parser.add_argument(\n            \"--test-dependencies\",\n            action=\"store_true\",\n            help=\"Enable building tests for dependencies as well.\",\n        )\n        parser.add_argument(\n            \"--current-project\",\n            help=\"Specify the name of the fbcode_builder manifest file for the \"\n            \"current repository.  If not specified, the code will attempt to find \"\n            \"this in a .projectid file in the repository root.\",\n        )\n        parser.add_argument(\n            \"--src-dir\",\n            default=[],\n            action=\"append\",\n            help=\"Specify a local directory to use for the project source, \"\n            \"rather than fetching it.\",\n        )\n        parser.add_argument(\n            \"--build-dir\",\n            default=[],\n            action=\"append\",\n            help=\"Explicitly specify the build directory to use for the \"\n            \"project, instead of the default location in the scratch path. \"\n            \"This only affects the project specified, and not its dependencies.\",\n        )\n        parser.add_argument(\n            \"--install-dir\",\n            default=[],\n            action=\"append\",\n            help=\"Explicitly specify the install directory to use for the \"\n            \"project, instead of the default location in the scratch path. \"\n            \"This only affects the project specified, and not its dependencies.\",\n        )\n        parser.add_argument(\n            \"--project-install-prefix\",\n            default=[],\n            action=\"append\",\n            help=\"Specify the final deployment installation path for a project\",\n        )\n\n        self.setup_project_cmd_parser(parser)\n\n    def setup_project_cmd_parser(self, parser):\n        pass\n\n    def create_builder(self, loader, manifest):\n        fetcher = loader.create_fetcher(manifest)\n        src_dir = fetcher.get_src_dir()\n        ctx = loader.ctx_gen.get_context(manifest.name)\n        build_dir = loader.get_project_build_dir(manifest)\n        inst_dir = loader.get_project_install_dir(manifest)\n        return manifest.create_builder(\n            loader.build_opts,\n            src_dir,\n            build_dir,\n            inst_dir,\n            ctx,\n            loader,\n            loader.dependencies_of(manifest),\n        )\n\n    def check_built(self, loader, manifest):\n        built_marker = os.path.join(\n            loader.get_project_install_dir(manifest), \".built-by-getdeps\"\n        )\n        return os.path.exists(built_marker)\n\n\nclass CachedProject:\n    \"\"\"A helper that allows calling the cache logic for a project\n    from both the build and the fetch code\"\"\"\n\n    def __init__(self, cache, loader, m):\n        self.m = m\n        self.inst_dir = loader.get_project_install_dir(m)\n        self.project_hash = loader.get_project_hash(m)\n        self.ctx = loader.ctx_gen.get_context(m.name)\n        self.loader = loader\n        self.cache = cache\n\n        self.cache_key = \"-\".join(\n            (\n                m.name,\n                self.ctx.get(\"os\"),\n                self.ctx.get(\"distro\") or \"none\",\n                self.ctx.get(\"distro_vers\") or \"none\",\n                self.project_hash,\n            )\n        )\n        self.cache_file_name = self.cache_key + \"-buildcache.tgz\"\n\n    def is_cacheable(self):\n        \"\"\"We only cache third party projects\"\"\"\n        return self.cache and self.m.shipit_project is None\n\n    def was_cached(self):\n        cached_marker = os.path.join(self.inst_dir, \".getdeps-cached-build\")\n        return os.path.exists(cached_marker)\n\n    def download(self):\n        if self.is_cacheable() and not os.path.exists(self.inst_dir):\n            print(\"check cache for %s\" % self.cache_file_name)\n            dl_dir = os.path.join(self.loader.build_opts.scratch_dir, \"downloads\")\n            if not os.path.exists(dl_dir):\n                os.makedirs(dl_dir)\n            try:\n                target_file_name = os.path.join(dl_dir, self.cache_file_name)\n                if self.cache.download_to_file(self.cache_file_name, target_file_name):\n                    tf = tarfile.open(target_file_name, \"r\")\n                    print(\n                        \"Extracting %s -> %s...\" % (self.cache_file_name, self.inst_dir)\n                    )\n                    tf.extractall(self.inst_dir)\n\n                    cached_marker = os.path.join(self.inst_dir, \".getdeps-cached-build\")\n                    with open(cached_marker, \"w\") as f:\n                        f.write(\"\\n\")\n\n                    return True\n            except Exception as exc:\n                print(\"%s\" % str(exc))\n\n        return False\n\n    def upload(self):\n        if self.is_cacheable():\n            # We can prepare an archive and stick it in LFS\n            tempdir = tempfile.mkdtemp()\n            tarfilename = os.path.join(tempdir, self.cache_file_name)\n            print(\"Archiving for cache: %s...\" % tarfilename)\n            tf = tarfile.open(tarfilename, \"w:gz\")\n            tf.add(self.inst_dir, arcname=\".\")\n            tf.close()\n            try:\n                self.cache.upload_from_file(self.cache_file_name, tarfilename)\n            except Exception as exc:\n                print(\n                    \"Failed to upload to cache (%s), continue anyway\" % str(exc),\n                    file=sys.stderr,\n                )\n            shutil.rmtree(tempdir)\n\n\n@cmd(\"fetch\", \"fetch the code for a given project\")\nclass FetchCmd(ProjectCmdBase):\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--recursive\",\n            help=\"fetch the transitive deps also\",\n            action=\"store_true\",\n            default=False,\n        )\n        parser.add_argument(\n            \"--host-type\",\n            help=(\n                \"When recursively fetching, fetch deps for \"\n                \"this host type rather than the current system\"\n            ),\n        )\n\n    def run_project_cmd(self, args, loader, manifest):\n        if args.recursive:\n            projects = loader.manifests_in_dependency_order()\n        else:\n            projects = [manifest]\n\n        cache = cache_module.create_cache()\n        for m in projects:\n            fetcher = loader.create_fetcher(m)\n            if isinstance(fetcher, SystemPackageFetcher):\n                # We are guaranteed that if the fetcher is set to\n                # SystemPackageFetcher then this item is completely\n                # satisfied by the appropriate system packages\n                continue\n            cached_project = CachedProject(cache, loader, m)\n            if cached_project.download():\n                continue\n\n            inst_dir = loader.get_project_install_dir(m)\n            built_marker = os.path.join(inst_dir, \".built-by-getdeps\")\n            if os.path.exists(built_marker):\n                with open(built_marker, \"r\") as f:\n                    built_hash = f.read().strip()\n\n                project_hash = loader.get_project_hash(m)\n                if built_hash == project_hash:\n                    continue\n\n            # We need to fetch the sources\n            fetcher.update()\n\n\n@cmd(\"install-system-deps\", \"Install system packages to satisfy the deps for a project\")\nclass InstallSysDepsCmd(ProjectCmdBase):\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--recursive\",\n            help=\"install the transitive deps also\",\n            action=\"store_true\",\n            default=False,\n        )\n        parser.add_argument(\n            \"--dry-run\",\n            action=\"store_true\",\n            default=False,\n            help=\"Don't install, just print the commands specs we would run\",\n        )\n        parser.add_argument(\n            \"--os-type\",\n            help=\"Filter to just this OS type to run\",\n            choices=[\"linux\", \"darwin\", \"windows\", \"pacman-package\"],\n            action=\"store\",\n            dest=\"ostype\",\n            default=None,\n        )\n        parser.add_argument(\n            \"--distro\",\n            help=\"Filter to just this distro to run\",\n            choices=[\"ubuntu\", \"centos_stream\"],\n            action=\"store\",\n            dest=\"distro\",\n            default=None,\n        )\n        parser.add_argument(\n            \"--distro-version\",\n            help=\"Filter to just this distro version\",\n            action=\"store\",\n            dest=\"distrovers\",\n            default=None,\n        )\n\n    def run_project_cmd(self, args, loader, manifest):\n        if args.recursive:\n            projects = loader.manifests_in_dependency_order()\n        else:\n            projects = [manifest]\n\n        rebuild_ctx_gen = False\n        if args.ostype:\n            loader.build_opts.host_type.ostype = args.ostype\n            loader.build_opts.host_type.distro = None\n            loader.build_opts.host_type.distrovers = None\n            rebuild_ctx_gen = True\n\n        if args.distro:\n            loader.build_opts.host_type.distro = args.distro\n            loader.build_opts.host_type.distrovers = None\n            rebuild_ctx_gen = True\n\n        if args.distrovers:\n            loader.build_opts.host_type.distrovers = args.distrovers\n            rebuild_ctx_gen = True\n\n        if rebuild_ctx_gen:\n            loader.ctx_gen = loader.build_opts.get_context_generator()\n\n        manager = loader.build_opts.host_type.get_package_manager()\n\n        all_packages = {}\n        for m in projects:\n            ctx = loader.ctx_gen.get_context(m.name)\n            packages = m.get_required_system_packages(ctx)\n            for k, v in packages.items():\n                merged = all_packages.get(k, [])\n                merged += v\n                all_packages[k] = merged\n\n        cmd_argss = []\n        if manager == \"rpm\":\n            packages = sorted(set(all_packages[\"rpm\"]))\n            if packages:\n                cmd_argss.append(\n                    [\"sudo\", \"dnf\", \"install\", \"-y\", \"--skip-broken\"] + packages\n                )\n        elif manager == \"deb\":\n            packages = sorted(set(all_packages[\"deb\"]))\n            if packages:\n                cmd_argss.append(\n                    [\n                        \"sudo\",\n                        \"--preserve-env=http_proxy\",\n                        \"apt-get\",\n                        \"install\",\n                        \"-y\",\n                    ]\n                    + packages\n                )\n                cmd_argss.append([\"pip\", \"install\", \"pex\"])\n        elif manager == \"homebrew\":\n            packages = sorted(set(all_packages[\"homebrew\"]))\n            if packages:\n                cmd_argss.append([\"brew\", \"install\"] + packages)\n        elif manager == \"pacman-package\":\n            packages = sorted(list(set(all_packages[\"pacman-package\"])))\n            if packages:\n                cmd_argss.append([\"pacman\", \"-S\"] + packages)\n        else:\n            host_tuple = loader.build_opts.host_type.as_tuple_string()\n            print(\n                f\"I don't know how to install any packages on this system {host_tuple}\"\n            )\n            return\n\n        for cmd_args in cmd_argss:\n            if args.dry_run:\n                print(\" \".join(cmd_args))\n            else:\n                check_cmd(cmd_args)\n        else:\n            print(\"no packages to install\")\n\n\n@cmd(\"list-deps\", \"lists the transitive deps for a given project\")\nclass ListDepsCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        for m in loader.manifests_in_dependency_order():\n            print(m.name)\n        return 0\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--host-type\",\n            help=(\n                \"Produce the list for the specified host type, \"\n                \"rather than that of the current system\"\n            ),\n        )\n\n\ndef clean_dirs(opts):\n    for d in [\"build\", \"installed\", \"extracted\", \"shipit\"]:\n        d = os.path.join(opts.scratch_dir, d)\n        print(\"Cleaning %s...\" % d)\n        if os.path.exists(d):\n            shutil.rmtree(d)\n\n\n@cmd(\"clean\", \"clean up the scratch dir\")\nclass CleanCmd(SubCmd):\n    def run(self, args):\n        opts = setup_build_options(args)\n        clean_dirs(opts)\n\n\n@cmd(\"show-scratch-dir\", \"show the scratch dir\")\nclass ShowScratchDirCmd(SubCmd):\n    def run(self, args):\n        opts = setup_build_options(args)\n        print(opts.scratch_dir)\n\n\n@cmd(\"show-build-dir\", \"print the build dir for a given project\")\nclass ShowBuildDirCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        if args.recursive:\n            manifests = loader.manifests_in_dependency_order()\n        else:\n            manifests = [manifest]\n\n        for m in manifests:\n            inst_dir = loader.get_project_build_dir(m)\n            print(inst_dir)\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--recursive\",\n            help=\"print the transitive deps also\",\n            action=\"store_true\",\n            default=False,\n        )\n\n\n@cmd(\"show-inst-dir\", \"print the installation dir for a given project\")\nclass ShowInstDirCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        if args.recursive:\n            manifests = loader.manifests_in_dependency_order()\n        else:\n            manifests = [manifest]\n\n        for m in manifests:\n            fetcher = loader.create_fetcher(m)\n            if isinstance(fetcher, SystemPackageFetcher):\n                # We are guaranteed that if the fetcher is set to\n                # SystemPackageFetcher then this item is completely\n                # satisfied by the appropriate system packages\n                continue\n            inst_dir = loader.get_project_install_dir_respecting_install_prefix(m)\n            print(inst_dir)\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--recursive\",\n            help=\"print the transitive deps also\",\n            action=\"store_true\",\n            default=False,\n        )\n\n\n@cmd(\"query-paths\", \"print the paths for tooling to use\")\nclass QueryPathsCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        if args.recursive:\n            manifests = loader.manifests_in_dependency_order()\n        else:\n            manifests = [manifest]\n\n        cache = cache_module.create_cache()\n        for m in manifests:\n            fetcher = loader.create_fetcher(m)\n            if isinstance(fetcher, SystemPackageFetcher):\n                # We are guaranteed that if the fetcher is set to\n                # SystemPackageFetcher then this item is completely\n                # satisfied by the appropriate system packages\n                continue\n            src_dir = fetcher.get_src_dir()\n            print(f\"{m.name}_SOURCE={src_dir}\")\n            inst_dir = loader.get_project_install_dir_respecting_install_prefix(m)\n            print(f\"{m.name}_INSTALL={inst_dir}\")\n            cached_project = CachedProject(cache, loader, m)\n            print(f\"{m.name}_CACHE_KEY={cached_project.cache_key}\")\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--recursive\",\n            help=\"print the transitive deps also\",\n            action=\"store_true\",\n            default=False,\n        )\n\n\n@cmd(\"show-source-dir\", \"print the source dir for a given project\")\nclass ShowSourceDirCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        if args.recursive:\n            manifests = loader.manifests_in_dependency_order()\n        else:\n            manifests = [manifest]\n\n        for m in manifests:\n            fetcher = loader.create_fetcher(m)\n            print(fetcher.get_src_dir())\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--recursive\",\n            help=\"print the transitive deps also\",\n            action=\"store_true\",\n            default=False,\n        )\n\n\n@cmd(\"build\", \"build a given project\")\nclass BuildCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        if args.clean:\n            clean_dirs(loader.build_opts)\n\n        print(\"Building on %s\" % loader.ctx_gen.get_context(args.project))\n        projects = loader.manifests_in_dependency_order()\n\n        cache = cache_module.create_cache() if args.use_build_cache else None\n\n        dep_manifests = []\n\n        for m in projects:\n            dep_manifests.append(m)\n\n            fetcher = loader.create_fetcher(m)\n\n            if args.build_skip_lfs_download and hasattr(fetcher, \"skip_lfs_download\"):\n                print(\"skipping lfs download for %s\" % m.name)\n                fetcher.skip_lfs_download()\n\n            if isinstance(fetcher, SystemPackageFetcher):\n                # We are guaranteed that if the fetcher is set to\n                # SystemPackageFetcher then this item is completely\n                # satisfied by the appropriate system packages\n                continue\n\n            if args.clean:\n                fetcher.clean()\n\n            build_dir = loader.get_project_build_dir(m)\n            inst_dir = loader.get_project_install_dir(m)\n\n            if (\n                m == manifest\n                and not args.only_deps\n                or m != manifest\n                and not args.no_deps\n            ):\n                print(\"Assessing %s...\" % m.name)\n                project_hash = loader.get_project_hash(m)\n                ctx = loader.ctx_gen.get_context(m.name)\n                built_marker = os.path.join(inst_dir, \".built-by-getdeps\")\n\n                cached_project = CachedProject(cache, loader, m)\n\n                reconfigure, sources_changed = self.compute_source_change_status(\n                    cached_project, fetcher, m, built_marker, project_hash\n                )\n\n                if os.path.exists(built_marker) and not cached_project.was_cached():\n                    # We've previously built this. We may need to reconfigure if\n                    # our deps have changed, so let's check them.\n                    dep_reconfigure, dep_build = self.compute_dep_change_status(\n                        m, built_marker, loader\n                    )\n                    if dep_reconfigure:\n                        reconfigure = True\n                    if dep_build:\n                        sources_changed = True\n\n                extra_cmake_defines = (\n                    json.loads(args.extra_cmake_defines)\n                    if args.extra_cmake_defines\n                    else {}\n                )\n\n                extra_b2_args = args.extra_b2_args or []\n                cmake_targets = args.cmake_target or [\"install\"]\n\n                if sources_changed or reconfigure or not os.path.exists(built_marker):\n                    if os.path.exists(built_marker):\n                        os.unlink(built_marker)\n                    src_dir = fetcher.get_src_dir()\n                    # Prepare builders write out config before the main builder runs\n                    prepare_builders = m.create_prepare_builders(\n                        loader.build_opts,\n                        ctx,\n                        src_dir,\n                        build_dir,\n                        inst_dir,\n                        loader,\n                        dep_manifests,\n                    )\n                    for preparer in prepare_builders:\n                        preparer.prepare(reconfigure=reconfigure)\n\n                    builder = m.create_builder(\n                        loader.build_opts,\n                        src_dir,\n                        build_dir,\n                        inst_dir,\n                        ctx,\n                        loader,\n                        dep_manifests,\n                        final_install_prefix=loader.get_project_install_prefix(m),\n                        extra_cmake_defines=extra_cmake_defines,\n                        cmake_targets=(cmake_targets if m == manifest else [\"install\"]),\n                        extra_b2_args=extra_b2_args,\n                    )\n                    builder.build(reconfigure=reconfigure)\n\n                    # If we are building the project (not dependency) and a specific\n                    # cmake_target (not 'install') has been requested, then we don't\n                    # set the built_marker. This allows subsequent runs of getdeps.py\n                    # for the project to run with different cmake_targets to trigger\n                    # cmake\n                    has_built_marker = False\n                    if not (m == manifest and \"install\" not in cmake_targets):\n                        os.makedirs(os.path.dirname(built_marker), exist_ok=True)\n                        with open(built_marker, \"w\") as f:\n                            f.write(project_hash)\n                            has_built_marker = True\n\n                    # Only populate the cache from continuous build runs, and\n                    # only if we have a built_marker.\n                    if not args.skip_upload and has_built_marker:\n                        if args.schedule_type == \"continuous\":\n                            cached_project.upload()\n                        elif args.schedule_type == \"base_retry\":\n                            # Check if on public commit before uploading\n                            if is_public_commit(loader.build_opts):\n                                cached_project.upload()\n                elif args.verbose:\n                    print(\"found good %s\" % built_marker)\n\n    def compute_dep_change_status(self, m, built_marker, loader):\n        reconfigure = False\n        sources_changed = False\n        st = os.lstat(built_marker)\n\n        ctx = loader.ctx_gen.get_context(m.name)\n        dep_list = m.get_dependencies(ctx)\n        for dep in dep_list:\n            if reconfigure and sources_changed:\n                break\n\n            dep_manifest = loader.load_manifest(dep)\n            dep_root = loader.get_project_install_dir(dep_manifest)\n            for dep_file in list_files_under_dir_newer_than_timestamp(\n                dep_root, st.st_mtime\n            ):\n                if os.path.basename(dep_file) == \".built-by-getdeps\":\n                    continue\n                if file_name_is_cmake_file(dep_file):\n                    if not reconfigure:\n                        reconfigure = True\n                        print(\n                            f\"Will reconfigure cmake because {dep_file} is newer than {built_marker}\"\n                        )\n                else:\n                    if not sources_changed:\n                        sources_changed = True\n                        print(\n                            f\"Will run build because {dep_file} is newer than {built_marker}\"\n                        )\n\n                if reconfigure and sources_changed:\n                    break\n\n        return reconfigure, sources_changed\n\n    def compute_source_change_status(\n        self, cached_project, fetcher, m, built_marker, project_hash\n    ):\n        reconfigure = False\n        sources_changed = False\n        if cached_project.download():\n            if not os.path.exists(built_marker):\n                fetcher.update()\n        else:\n            check_fetcher = True\n            if os.path.exists(built_marker):\n                check_fetcher = False\n                with open(built_marker, \"r\") as f:\n                    built_hash = f.read().strip()\n                if built_hash == project_hash:\n                    if cached_project.is_cacheable():\n                        # We can blindly trust the build status\n                        reconfigure = False\n                        sources_changed = False\n                    else:\n                        # Otherwise, we may have changed the source, so let's\n                        # check in with the fetcher layer\n                        check_fetcher = True\n                else:\n                    # Some kind of inconsistency with a prior build,\n                    # let's run it again to be sure\n                    os.unlink(built_marker)\n                    reconfigure = True\n                    sources_changed = True\n                    # While we don't need to consult the fetcher for the\n                    # status in this case, we may still need to have eg: shipit\n                    # run in order to have a correct source tree.\n                    fetcher.update()\n\n            if check_fetcher:\n                change_status = fetcher.update()\n                reconfigure = change_status.build_changed()\n                sources_changed = change_status.sources_changed()\n\n        return reconfigure, sources_changed\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--clean\",\n            action=\"store_true\",\n            default=False,\n            help=(\n                \"Clean up the build and installation area prior to building, \"\n                \"causing the projects to be built from scratch\"\n            ),\n        )\n        parser.add_argument(\n            \"--no-deps\",\n            action=\"store_true\",\n            default=False,\n            help=(\n                \"Only build the named project, not its deps. \"\n                \"This is most useful after you've built all of the deps, \"\n                \"and helps to avoid waiting for relatively \"\n                \"slow up-to-date-ness checks\"\n            ),\n        )\n        parser.add_argument(\n            \"--only-deps\",\n            action=\"store_true\",\n            default=False,\n            help=(\n                \"Only build the named project's deps. \"\n                \"This is most useful when you want to separate out building \"\n                \"of all of the deps and your project\"\n            ),\n        )\n        parser.add_argument(\n            \"--no-build-cache\",\n            action=\"store_false\",\n            default=True,\n            dest=\"use_build_cache\",\n            help=\"Do not attempt to use the build cache.\",\n        )\n        parser.add_argument(\n            \"--cmake-target\",\n            help=(\"Repeatable argument that specifies targets for cmake build.\"),\n            default=[],\n            action=\"append\",\n        )\n        parser.add_argument(\n            \"--extra-b2-args\",\n            help=(\n                \"Repeatable argument that contains extra arguments to pass \"\n                \"to b2, which compiles boost. \"\n                \"e.g.: 'cxxflags=-fPIC' 'cflags=-fPIC'\"\n            ),\n            action=\"append\",\n        )\n        parser.add_argument(\n            \"--free-up-disk\",\n            help=\"Remove unused tools and clean up intermediate files if possible to maximise space for the build\",\n            action=\"store_true\",\n            default=False,\n        )\n        parser.add_argument(\"--build-type\", **BUILD_TYPE_ARG)\n\n\n@cmd(\"fixup-dyn-deps\", \"Adjusts dynamic dependencies for packaging purposes\")\nclass FixupDeps(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        projects = loader.manifests_in_dependency_order()\n\n        # Accumulate the install directories so that the build steps\n        # can find their dep installation\n        install_dirs = []\n        dep_manifests = []\n\n        for m in projects:\n            inst_dir = loader.get_project_install_dir_respecting_install_prefix(m)\n            install_dirs.append(inst_dir)\n            dep_manifests.append(m)\n\n            if m == manifest:\n                ctx = loader.ctx_gen.get_context(m.name)\n                env = loader.build_opts.compute_env_for_install_dirs(\n                    loader, dep_manifests, ctx\n                )\n                dep_munger = create_dyn_dep_munger(\n                    loader.build_opts, env, install_dirs, args.strip\n                )\n                if dep_munger is None:\n                    print(f\"dynamic dependency fixups not supported on {sys.platform}\")\n                else:\n                    dep_munger.process_deps(args.destdir, args.final_install_prefix)\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\"destdir\", help=\"Where to copy the fixed up executables\")\n        parser.add_argument(\n            \"--final-install-prefix\", help=\"specify the final installation prefix\"\n        )\n        parser.add_argument(\n            \"--strip\",\n            action=\"store_true\",\n            default=False,\n            help=\"Strip debug info while processing executables\",\n        )\n\n\n@cmd(\"test\", \"test a given project\")\nclass TestCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        if not self.check_built(loader, manifest):\n            print(\"project %s has not been built\" % manifest.name)\n            return 1\n        return self.create_builder(loader, manifest).run_tests(\n            schedule_type=args.schedule_type,\n            owner=args.test_owner,\n            test_filter=args.filter,\n            test_exclude=args.exclude,\n            retry=args.retry,\n            no_testpilot=args.no_testpilot,\n            timeout=args.timeout,\n        )\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\"--test-owner\", help=\"Owner for testpilot\")\n        parser.add_argument(\"--filter\", help=\"Only run the tests matching the regex\")\n        parser.add_argument(\"--exclude\", help=\"Exclude tests matching the regex\")\n        parser.add_argument(\n            \"--retry\",\n            type=int,\n            default=3,\n            help=\"Number of immediate retries for failed tests \"\n            \"(noop in continuous and testwarden runs)\",\n        )\n        parser.add_argument(\n            \"--no-testpilot\",\n            help=\"Do not use Test Pilot even when available\",\n            action=\"store_true\",\n        )\n        parser.add_argument(\n            \"--timeout\",\n            type=int,\n            default=None,\n            help=\"Timeout in seconds for each individual test\",\n        )\n        parser.add_argument(\"--build-type\", **BUILD_TYPE_ARG)\n\n\n@cmd(\n    \"debug\",\n    \"start a shell in the given project's build dir with the correct environment for running the build\",\n)\nclass DebugCmd(ProjectCmdBase):\n    def run_project_cmd(self, args, loader, manifest):\n        self.create_builder(loader, manifest).debug(reconfigure=False)\n\n\n@cmd(\n    \"env\",\n    \"print the environment in a shell sourceable format\",\n)\nclass EnvCmd(ProjectCmdBase):\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--os-type\",\n            help=\"Filter to just this OS type to run\",\n            choices=[\"linux\", \"darwin\", \"windows\"],\n            action=\"store\",\n            dest=\"ostype\",\n            default=None,\n        )\n\n    def run_project_cmd(self, args, loader, manifest):\n        if args.ostype:\n            loader.build_opts.host_type.ostype = args.ostype\n        self.create_builder(loader, manifest).printenv(reconfigure=False)\n\n\n@cmd(\"generate-github-actions\", \"generate a GitHub actions configuration\")\nclass GenerateGitHubActionsCmd(ProjectCmdBase):\n    RUN_ON_ALL = \"\"\" [push, pull_request]\"\"\"\n\n    WORKFLOW_DISPATCH_TMATE = \"\"\"\n  workflow_dispatch:\n    inputs:\n      tmate_enabled:\n        description: 'Start a tmate SSH session on failure'\n        required: false\n        default: false\n        type: boolean\"\"\"\n\n    def run_project_cmd(self, args, loader, manifest):\n        platforms = [\n            HostType(\"linux\", \"ubuntu\", \"24\"),\n            HostType(\"darwin\", None, None),\n            HostType(\"windows\", None, None),\n        ]\n\n        for p in platforms:\n            if args.os_types and p.ostype not in args.os_types:\n                continue\n            self.write_job_for_platform(p, args)\n\n    def get_run_on(self, args):\n        if args.run_on_all_branches:\n            return (\n                \"\"\"\n  push:\n  pull_request:\"\"\"\n                + self.WORKFLOW_DISPATCH_TMATE\n            )\n        if args.cron:\n            if args.cron == \"never\":\n                return \" {}\"\n            elif args.cron == \"workflow_dispatch\":\n                return self.WORKFLOW_DISPATCH_TMATE\n            else:\n                return (\n                    f\"\"\"\n  schedule:\n    - cron: '{args.cron}'\"\"\"\n                    + self.WORKFLOW_DISPATCH_TMATE\n                )\n\n        return (\n            f\"\"\"\n  push:\n    branches:\n    - {args.main_branch}\n  pull_request:\n    branches:\n    - {args.main_branch}\"\"\"\n            + self.WORKFLOW_DISPATCH_TMATE\n        )\n\n    # TODO: Break up complex function\n    def write_job_for_platform(self, platform, args):  # noqa: C901\n        build_opts = setup_build_options(args, platform)\n        ctx_gen = build_opts.get_context_generator()\n        if args.enable_tests:\n            ctx_gen.set_value_for_project(args.project, \"test\", \"on\")\n        else:\n            ctx_gen.set_value_for_project(args.project, \"test\", \"off\")\n        loader = ManifestLoader(build_opts, ctx_gen)\n        self.process_project_dir_arguments(args, loader)\n        manifest = loader.load_manifest(args.project)\n        manifest_ctx = loader.ctx_gen.get_context(manifest.name)\n        run_tests = (\n            args.enable_tests\n            and manifest.get(\"github.actions\", \"run_tests\", ctx=manifest_ctx) != \"off\"\n        )\n        rust_version = (\n            manifest.get(\"github.actions\", \"rust_version\", ctx=manifest_ctx) or \"stable\"\n        )\n\n        override_build_type = args.build_type or manifest.get(\n            \"github.actions\", \"build_type\", ctx=manifest_ctx\n        )\n        if run_tests:\n            manifest_ctx.set(\"test\", \"on\")\n        run_on = self.get_run_on(args)\n\n        tests_arg = \"--no-tests \"\n        if run_tests:\n            tests_arg = \"\"\n\n        # Some projects don't do anything \"useful\" as a leaf project, only\n        # as a dep for a leaf project. Check for those here; we don't want\n        # to waste the effort scheduling them on CI.\n        # We do this by looking at the builder type in the manifest file\n        # rather than creating a builder and checking its type because we\n        # don't know enough to create the full builder instance here.\n        builder_name = manifest.get(\"build\", \"builder\", ctx=manifest_ctx)\n        if builder_name == \"nop\":\n            return None\n\n        # We want to be sure that we're running things with python 3\n        # but python versioning is honestly a bit of a frustrating mess.\n        # `python` may be version 2 or version 3 depending on the system.\n        # python3 may not be a thing at all!\n        # Assume an optimistic default\n        py3 = \"python3\"\n\n        if build_opts.is_linux():\n            artifacts = \"linux\"\n            if args.runs_on:\n                runs_on = args.runs_on\n            else:\n                runs_on = f\"ubuntu-{args.ubuntu_version}\"\n                if args.cpu_cores:\n                    runs_on = f\"{args.cpu_cores}-core-ubuntu-{args.ubuntu_version}\"\n        elif build_opts.is_windows():\n            artifacts = \"windows\"\n            if args.runs_on:\n                runs_on = args.runs_on\n            else:\n                runs_on = \"windows-2022\"\n            # The windows runners are python 3 by default; python2.exe\n            # is available if needed.\n            py3 = \"python\"\n        else:\n            artifacts = \"mac\"\n            if args.runs_on:\n                runs_on = args.runs_on\n            else:\n                runs_on = \"macOS-latest\"\n\n        os.makedirs(args.output_dir, exist_ok=True)\n\n        job_file_prefix = \"getdeps_\"\n        if args.job_file_prefix:\n            job_file_prefix = args.job_file_prefix\n\n        output_file = os.path.join(args.output_dir, f\"{job_file_prefix}{artifacts}.yml\")\n\n        if args.job_name_prefix:\n            job_name = args.job_name_prefix + artifacts.capitalize()\n        else:\n            job_name = artifacts\n\n        with open(output_file, \"w\") as out:\n            # Deliberate line break here because the @ and the generated\n            # symbols are meaningful to our internal tooling when they\n            # appear in a single token\n            out.write(\"# This file was @\")\n            out.write(\"generated by getdeps.py\\n\")\n            out.write(\n                f\"\"\"\nname: {job_name}\n\non:{run_on}\n\npermissions:\n  contents: read  #  to fetch code (actions/checkout)\n\njobs:\n\"\"\"\n            )\n\n            getdepscmd = f\"{py3} build/fbcode_builder/getdeps.py\"\n\n            out.write(\"  build:\\n\")\n            out.write(\"    runs-on: %s\\n\" % runs_on)\n            out.write(\"    steps:\\n\")\n\n            if build_opts.is_windows():\n                # cmake relies on BOOST_ROOT but GH deliberately don't set it in order\n                # to avoid versioning issues:\n                # https://github.com/actions/virtual-environments/issues/319\n                # Instead, set the version we think we need; this is effectively\n                # coupled with the boost manifest\n                # This is the unusual syntax for setting an env var for the rest of\n                # the steps in a workflow:\n                # https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/\n                out.write(\"    - name: Export boost environment\\n\")\n                out.write(\n                    '      run: \"echo BOOST_ROOT=%BOOST_ROOT_1_83_0% >> %GITHUB_ENV%\"\\n'\n                )\n                out.write(\"      shell: cmd\\n\")\n\n                out.write(\"    - name: Fix Git config\\n\")\n                out.write(\"      run: >\\n\")\n                out.write(\"        git config --system core.longpaths true &&\\n\")\n                out.write(\"        git config --system core.autocrlf false &&\\n\")\n                # cxx crate needs symlinks enabled\n                out.write(\"        git config --system core.symlinks true\\n\")\n                # && is not supported on default windows powershell, so use cmd\n                out.write(\"      shell: cmd\\n\")\n\n            out.write(\"    - uses: actions/checkout@v6\\n\")\n\n            build_type_arg = \"\"\n            if override_build_type:\n                build_type_arg = f\"--build-type {override_build_type} \"\n\n            if args.shared_libs:\n                build_type_arg += \"--shared-libs \"\n\n            if build_opts.free_up_disk:\n                free_up_disk = \"--free-up-disk \"\n                if not build_opts.is_windows():\n                    out.write(\"    - name: Show disk space at start\\n\")\n                    out.write(\"      run: df -h\\n\")\n                    # remove the unused github supplied android dev tools\n                    out.write(\"    - name: Free up disk space\\n\")\n                    out.write(\"      run: sudo rm -rf /usr/local/lib/android\\n\")\n                    out.write(\"    - name: Show disk space after freeing up\\n\")\n                    out.write(\"      run: df -h\\n\")\n            else:\n                free_up_disk = \"\"\n\n            allow_sys_arg = \"\"\n            if (\n                build_opts.allow_system_packages\n                and build_opts.host_type.get_package_manager()\n            ):\n                sudo_arg = \"sudo --preserve-env=http_proxy \"\n                allow_sys_arg = \" --allow-system-packages\"\n                if build_opts.host_type.get_package_manager() == \"deb\":\n                    out.write(\"    - name: Update system package info\\n\")\n                    out.write(f\"      run: {sudo_arg}apt-get update\\n\")\n\n                out.write(\"    - name: Install system deps\\n\")\n                if build_opts.is_darwin():\n                    # brew is installed as regular user\n                    sudo_arg = \"\"\n\n                system_deps_cmd = f\"{sudo_arg}{getdepscmd}{allow_sys_arg} install-system-deps {tests_arg}--recursive {manifest.name}\"\n                if build_opts.is_linux() or build_opts.is_freebsd():\n                    system_deps_cmd += f\" && {sudo_arg}{getdepscmd}{allow_sys_arg} install-system-deps {tests_arg}--recursive patchelf\"\n                out.write(f\"      run: {system_deps_cmd}\\n\")\n\n                required_locales = manifest.get(\n                    \"github.actions\", \"required_locales\", ctx=manifest_ctx\n                )\n                if (\n                    build_opts.host_type.get_package_manager() == \"deb\"\n                    and required_locales\n                ):\n                    # ubuntu doesn't include this by default\n                    out.write(\"    - name: Install locale-gen\\n\")\n                    out.write(f\"      run: {sudo_arg}apt-get install locales\\n\")\n                    for loc in required_locales.split():\n                        out.write(f\"    - name: Ensure {loc} locale present\\n\")\n                        out.write(f\"      run: {sudo_arg}locale-gen {loc}\\n\")\n\n            out.write(\"    - id: paths\\n\")\n            out.write(\"      name: Query paths\\n\")\n            if build_opts.is_windows():\n                out.write(\n                    f\"      run: {getdepscmd}{allow_sys_arg} query-paths {tests_arg}--recursive --src-dir=. {manifest.name}  >> $env:GITHUB_OUTPUT\\n\"\n                )\n                out.write(\"      shell: pwsh\\n\")\n            else:\n                out.write(\n                    f'      run: {getdepscmd}{allow_sys_arg} query-paths {tests_arg}--recursive --src-dir=. {manifest.name}  >> \"$GITHUB_OUTPUT\"\\n'\n                )\n\n            projects = loader.manifests_in_dependency_order()\n\n            main_repo_url = manifest.get_repo_url(manifest_ctx)\n            has_same_repo_dep = False\n\n            # Add the rust dep which doesn't have a manifest\n            for m in projects:\n                if m == manifest:\n                    continue\n                mbuilder_name = m.get(\"build\", \"builder\", ctx=manifest_ctx)\n                if (\n                    m.name == \"rust\"\n                    or builder_name == \"cargo\"\n                    or mbuilder_name == \"cargo\"\n                ):\n                    out.write(f\"    - name: Install Rust {rust_version.capitalize()}\\n\")\n                    out.write(f\"      uses: dtolnay/rust-toolchain@{rust_version}\\n\")\n                    break\n\n            # Normal deps that have manifests\n            for m in projects:\n                if m == manifest or m.name == \"rust\":\n                    continue\n                ctx = loader.ctx_gen.get_context(m.name)\n                if m.get_repo_url(ctx) != main_repo_url:\n                    out.write(\"    - name: Fetch %s\\n\" % m.name)\n                    out.write(\n                        f\"      if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\\n\"\n                    )\n                    out.write(\n                        f\"      run: {getdepscmd}{allow_sys_arg} fetch --no-tests {m.name}\\n\"\n                    )\n\n            for m in projects:\n                if m == manifest or m.name == \"rust\":\n                    continue\n                src_dir_arg = \"\"\n                ctx = loader.ctx_gen.get_context(m.name)\n                if main_repo_url and m.get_repo_url(ctx) == main_repo_url:\n                    # Its in the same repo, so src-dir is also .\n                    src_dir_arg = \"--src-dir=. \"\n                    has_same_repo_dep = True\n\n                if args.use_build_cache and not src_dir_arg:\n                    out.write(f\"    - name: Restore {m.name} from cache\\n\")\n                    out.write(f\"      id: restore_{m.name}\\n\")\n                    # only need to restore if would build it\n                    out.write(\n                        f\"      if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\\n\"\n                    )\n                    out.write(\"      uses: actions/cache/restore@v4\\n\")\n                    out.write(\"      with:\\n\")\n                    out.write(\n                        f\"       path: ${{{{ steps.paths.outputs.{m.name}_INSTALL }}}}\\n\"\n                    )\n                    out.write(\n                        f\"       key: ${{{{ steps.paths.outputs.{m.name}_CACHE_KEY }}}}-install\\n\"\n                    )\n\n                out.write(\"    - name: Build %s\\n\" % m.name)\n                if not src_dir_arg:\n                    if args.use_build_cache:\n                        out.write(\n                            f\"      if: ${{{{ steps.paths.outputs.{m.name}_SOURCE && ! steps.restore_{m.name}.outputs.cache-hit }}}}\\n\"\n                        )\n                    else:\n                        out.write(\n                            f\"      if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\\n\"\n                        )\n                out.write(\n                    f\"      run: {getdepscmd}{allow_sys_arg} build {build_type_arg}{src_dir_arg}{free_up_disk}--no-tests {m.name}\\n\"\n                )\n\n                if args.use_build_cache and not src_dir_arg:\n                    out.write(f\"    - name: Save {m.name} to cache\\n\")\n                    out.write(\"      uses: actions/cache/save@v4\\n\")\n                    out.write(\n                        f\"      if: ${{{{ steps.paths.outputs.{m.name}_SOURCE && ! steps.restore_{m.name}.outputs.cache-hit }}}}\\n\"\n                    )\n                    out.write(\"      with:\\n\")\n                    out.write(\n                        f\"       path: ${{{{ steps.paths.outputs.{m.name}_INSTALL }}}}\\n\"\n                    )\n                    out.write(\n                        f\"       key: ${{{{ steps.paths.outputs.{m.name}_CACHE_KEY }}}}-install\\n\"\n                    )\n\n            out.write(\"    - name: Build %s\\n\" % manifest.name)\n\n            project_prefix = \"\"\n            if not build_opts.is_windows():\n                prefix = loader.get_project_install_prefix(manifest) or \"/usr/local\"\n                project_prefix = \" --project-install-prefix %s:%s\" % (\n                    manifest.name,\n                    prefix,\n                )\n\n            # If we have dep from same repo, we already built it and don't want to rebuild it again\n            no_deps_arg = \"\"\n            if has_same_repo_dep:\n                no_deps_arg = \"--no-deps \"\n\n            out.write(\n                f\"      run: {getdepscmd}{allow_sys_arg} build {build_type_arg}{tests_arg}{no_deps_arg}--src-dir=. {manifest.name}{project_prefix}\\n\"\n            )\n\n            out.write(\"    - name: Copy artifacts\\n\")\n            if build_opts.is_linux():\n                # Strip debug info from the binaries, but only on linux.\n                # While the `strip` utility is also available on macOS,\n                # attempting to strip there results in an error.\n                # The `strip` utility is not available on Windows.\n                strip = \" --strip\"\n            else:\n                strip = \"\"\n\n            out.write(\n                f\"      run: {getdepscmd}{allow_sys_arg} fixup-dyn-deps{strip} \"\n                f\"--src-dir=. {manifest.name} _artifacts/{artifacts}{project_prefix} \"\n                f\"--final-install-prefix /usr/local\\n\"\n            )\n\n            out.write(\"    - uses: actions/upload-artifact@v6\\n\")\n            out.write(\"      with:\\n\")\n            out.write(\"        name: %s\\n\" % manifest.name)\n            out.write(\"        path: _artifacts\\n\")\n\n            if run_tests:\n                num_jobs_arg = \"\"\n                if args.num_jobs:\n                    num_jobs_arg = f\"--num-jobs {args.num_jobs} \"\n\n                out.write(\"    - name: Test %s\\n\" % manifest.name)\n                out.write(\n                    f\"      run: {getdepscmd}{allow_sys_arg} test {build_type_arg}{num_jobs_arg}--src-dir=. {manifest.name}{project_prefix}\\n\"\n                )\n            if build_opts.free_up_disk and not build_opts.is_windows():\n                out.write(\"    - name: Show disk space at end\\n\")\n                out.write(\"      if: always()\\n\")\n                out.write(\"      run: df -h\\n\")\n\n            out.write(\"    - name: Setup tmate session\\n\")\n            out.write(\n                \"      if: failure() && github.event_name == 'workflow_dispatch' && inputs.tmate_enabled\\n\"\n            )\n            out.write(\"      uses: mxschmitt/action-tmate@v3\\n\")\n\n    def setup_project_cmd_parser(self, parser):\n        parser.add_argument(\n            \"--disallow-system-packages\",\n            help=\"Disallow satisfying third party deps from installed system packages\",\n            action=\"store_true\",\n            default=False,\n        )\n        parser.add_argument(\n            \"--output-dir\", help=\"The directory that will contain the yml files\"\n        )\n        parser.add_argument(\n            \"--run-on-all-branches\",\n            action=\"store_true\",\n            help=\"Allow CI to fire on all branches - Handy for testing\",\n        )\n        parser.add_argument(\n            \"--ubuntu-version\", default=\"24.04\", help=\"Version of Ubuntu to use\"\n        )\n        parser.add_argument(\n            \"--cpu-cores\",\n            help=\"Number of CPU cores to use (applicable for Linux OS)\",\n        )\n        parser.add_argument(\n            \"--runs-on\",\n            help=\"Allow specifying explicit runs-on: for github actions\",\n        )\n        parser.add_argument(\n            \"--cron\",\n            help=\"Specify that the job runs on a cron schedule instead of on pushes. Pass never to disable the action.\",\n        )\n        parser.add_argument(\n            \"--main-branch\",\n            default=\"main\",\n            help=\"Main branch to trigger GitHub Action on\",\n        )\n        parser.add_argument(\n            \"--os-type\",\n            help=\"Filter to just this OS type to run\",\n            choices=[\"linux\", \"darwin\", \"windows\"],\n            action=\"append\",\n            dest=\"os_types\",\n            default=[],\n        )\n        parser.add_argument(\n            \"--job-file-prefix\",\n            type=str,\n            help=\"add a prefix to all job file names\",\n            default=None,\n        )\n        parser.add_argument(\n            \"--job-name-prefix\",\n            type=str,\n            help=\"add a prefix to all job names\",\n            default=None,\n        )\n        parser.add_argument(\n            \"--free-up-disk\",\n            help=\"Remove unused tools and clean up intermediate files if possible to maximise space for the build\",\n            action=\"store_true\",\n            default=False,\n        )\n        parser.add_argument(\"--build-type\", **BUILD_TYPE_ARG)\n        parser.add_argument(\n            \"--no-build-cache\",\n            action=\"store_false\",\n            default=True,\n            dest=\"use_build_cache\",\n            help=\"Do not attempt to use the build cache.\",\n        )\n\n\ndef get_arg_var_name(args):\n    for arg in args:\n        if arg.startswith(\"--\"):\n            return arg[2:].replace(\"-\", \"_\")\n\n    raise Exception(\"unable to determine argument variable name from %r\" % (args,))\n\n\ndef parse_args():\n    # We want to allow common arguments to be specified either before or after\n    # the subcommand name.  In order to do this we add them to the main parser\n    # and to subcommand parsers.  In order for this to work, we need to tell\n    # argparse that the default value is SUPPRESS, so that the default values\n    # from the subparser arguments won't override values set by the user from\n    # the main parser.  We maintain our own list of desired defaults in the\n    # common_defaults dictionary, and manually set those if the argument wasn't\n    # present at all.\n    common_args = argparse.ArgumentParser(add_help=False)\n    common_defaults = {}\n\n    def add_common_arg(*args, **kwargs):\n        var_name = get_arg_var_name(args)\n        default_value = kwargs.pop(\"default\", None)\n        common_defaults[var_name] = default_value\n        kwargs[\"default\"] = argparse.SUPPRESS\n        common_args.add_argument(*args, **kwargs)\n\n    add_common_arg(\"--scratch-path\", help=\"Where to maintain checkouts and build dirs\")\n    add_common_arg(\n        \"--vcvars-path\", default=None, help=\"Path to the vcvarsall.bat on Windows.\"\n    )\n    add_common_arg(\n        \"--install-prefix\",\n        help=(\n            \"Where the final build products will be installed \"\n            \"(default is [scratch-path]/installed)\"\n        ),\n    )\n    add_common_arg(\n        \"--num-jobs\",\n        type=int,\n        help=(\n            \"Number of concurrent jobs to use while building. \"\n            \"(default=number of cpu cores)\"\n        ),\n    )\n    add_common_arg(\n        \"--use-shipit\",\n        help=\"use the real ShipIt instead of the simple shipit transformer\",\n        action=\"store_true\",\n        default=False,\n    )\n    add_common_arg(\n        \"--facebook-internal\",\n        help=\"Setup the build context as an FB internal build\",\n        action=\"store_true\",\n        default=None,\n    )\n    add_common_arg(\n        \"--no-facebook-internal\",\n        help=\"Perform a non-FB internal build, even when in an fbsource repository\",\n        action=\"store_false\",\n        dest=\"facebook_internal\",\n    )\n    add_common_arg(\n        \"--shared-libs\",\n        help=\"Build shared libraries if possible\",\n        action=\"store_true\",\n        default=False,\n    )\n    add_common_arg(\n        \"--extra-cmake-defines\",\n        help=(\n            \"Input json map that contains extra cmake defines to be used \"\n            \"when compiling the current project and all its deps. \"\n            'e.g: \\'{\"CMAKE_CXX_FLAGS\": \"--bla\"}\\''\n        ),\n    )\n    add_common_arg(\n        \"--allow-system-packages\",\n        help=\"Allow satisfying third party deps from installed system packages\",\n        action=\"store_true\",\n        default=False,\n    )\n    add_common_arg(\n        \"-v\",\n        \"--verbose\",\n        help=\"Print more output\",\n        action=\"store_true\",\n        default=False,\n    )\n    add_common_arg(\n        \"-su\",\n        \"--skip-upload\",\n        help=\"skip upload steps\",\n        action=\"store_true\",\n        default=False,\n    )\n    add_common_arg(\n        \"--lfs-path\",\n        help=\"Provide a parent directory for lfs when fbsource is unavailable\",\n        default=None,\n    )\n    add_common_arg(\n        \"--build-skip-lfs-download\",\n        action=\"store_true\",\n        default=False,\n        help=(\n            \"Download from the URL, rather than LFS. This is useful \"\n            \"in cases where the upstream project has uploaded a new \"\n            \"version of the archive with a different hash\"\n        ),\n    )\n    add_common_arg(\n        \"--schedule-type\",\n        nargs=\"?\",\n        help=\"Indicates how the build was activated\",\n    )\n\n    ap = argparse.ArgumentParser(\n        description=\"Get and build dependencies and projects\", parents=[common_args]\n    )\n    sub = ap.add_subparsers(\n        # metavar suppresses the long and ugly default list of subcommands on a\n        # single line.  We still render the nicer list below where we would\n        # have shown the nasty one.\n        metavar=\"\",\n        title=\"Available commands\",\n        help=\"\",\n    )\n\n    add_subcommands(sub, common_args)\n\n    args = ap.parse_args()\n    for var_name, default_value in common_defaults.items():\n        if not hasattr(args, var_name):\n            setattr(args, var_name, default_value)\n\n    return ap, args\n\n\ndef main():\n    ap, args = parse_args()\n    if getattr(args, \"func\", None) is None:\n        ap.print_help()\n        return 0\n    try:\n        return args.func(args)\n    except UsageError as exc:\n        ap.error(str(exc))\n        return 1\n    except TransientFailure as exc:\n        print(\"TransientFailure: %s\" % str(exc))\n        # This return code is treated as a retryable transient infrastructure\n        # error by Facebook's internal CI, rather than eg: a build or code\n        # related error that needs to be fixed before progress can be made.\n        return 128\n    except subprocess.CalledProcessError as exc:\n        print(\"%s\" % str(exc), file=sys.stderr)\n        print(\"!! Failed\", file=sys.stderr)\n        return 1\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "build/fbcode_builder/manifests/CLI11",
    "content": "[manifest]\nname = CLI11\n\n[download]\nurl = https://github.com/CLIUtils/CLI11/archive/v2.0.0.tar.gz\nsha256 = 2c672f17bf56e8e6223a3bfb74055a946fa7b1ff376510371902adb9cb0ab6a3\n\n[build]\nbuilder = cmake\nsubdir = CLI11-2.0.0\n\n[cmake.defines]\nCLI11_BUILD_TESTS = OFF\nCLI11_BUILD_EXAMPLES = OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/autoconf",
    "content": "[manifest]\nname = autoconf\n\n[debs]\nautoconf\n\n[homebrew]\nautoconf\n\n[rpms]\nautoconf\n\n[pps]\nautoconf\n\n[download]\nurl = https://ftpmirror.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz\nsha256 = 954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969\n\n[build]\nbuilder = autoconf\nsubdir = autoconf-2.69\n"
  },
  {
    "path": "build/fbcode_builder/manifests/automake",
    "content": "[manifest]\nname = automake\n\n[homebrew]\nautomake\n\n[debs]\nautomake\n\n[rpms]\nautomake\n\n[pps]\nautomake\n\n[download]\nurl = https://ftpmirror.gnu.org/gnu/automake/automake-1.16.1.tar.gz\nsha256 = 608a97523f97db32f1f5d5615c98ca69326ced2054c9f82e65bade7fc4c9dea8\n\n[build]\nbuilder = autoconf\nsubdir = automake-1.16.1\n\n[dependencies]\nautoconf\n"
  },
  {
    "path": "build/fbcode_builder/manifests/benchmark",
    "content": "[manifest]\nname = benchmark\n\n[download]\nurl = https://github.com/google/benchmark/archive/refs/tags/v1.8.0.tar.gz\nsha256 = ea2e94c24ddf6594d15c711c06ccd4486434d9cf3eca954e2af8a20c88f9f172\n\n[build]\nbuilder = cmake\nsubdir = benchmark-1.8.0/\n\n[cmake.defines]\nBENCHMARK_ENABLE_TESTING=OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/blake3",
    "content": "[manifest]\nname = blake3\n\n[download]\nurl = https://github.com/BLAKE3-team/BLAKE3/archive/refs/tags/1.5.1.tar.gz\nsha256 = 822cd37f70152e5985433d2c50c8f6b2ec83aaf11aa31be9fe71486a91744f37\n\n[build]\nbuilder = cmake\nsubdir = BLAKE3-1.5.1/c\n"
  },
  {
    "path": "build/fbcode_builder/manifests/boost",
    "content": "[manifest]\nname = boost\n\n[download.not(os=windows)]\nurl = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.tar.gz\nsha256 = c0685b68dd44cc46574cce86c4e17c0f611b15e195be9848dfd0769a0a207628\n\n[download.os=windows]\nurl = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.zip\nsha256 = c86bd9d9eef795b4b0d3802279419fde5221922805b073b9bd822edecb1ca28e\n\n[preinstalled.env]\n# Here we list the acceptable versions that cmake needs a hint to find\nBOOST_ROOT_1_69_0\nBOOST_ROOT_1_83_0\n\n[debs]\nlibboost-all-dev\n\n[homebrew]\nboost\n# Boost cmake detection on homebrew adds this as requirement: https://github.com/Homebrew/homebrew-core/issues/67427#issuecomment-754187345\nicu4c\n\n[pps]\nboost\n\n[rpms.all(distro=centos_stream,distro_vers=8)]\nboost169\nboost169-math\nboost169-test\nboost169-fiber\nboost169-graph\nboost169-log\nboost169-openmpi\nboost169-timer\nboost169-chrono\nboost169-locale\nboost169-thread\nboost169-atomic\nboost169-random\nboost169-static\nboost169-contract\nboost169-date-time\nboost169-iostreams\nboost169-container\nboost169-coroutine\nboost169-filesystem\nboost169-system\nboost169-stacktrace\nboost169-regex\nboost169-devel\nboost169-context\nboost169-python3-devel\nboost169-type_erasure\nboost169-wave\nboost169-python3\nboost169-serialization\nboost169-program-options\n\n[rpms.distro=fedora]\nboost-devel\nboost-static\n\n[build]\nbuilder = boost\njob_weight_mib = 512\npatchfile = boost_1_83_0.patch\n\n[b2.args]\n--with-atomic\n--with-chrono\n--with-container\n--with-context\n--with-contract\n--with-coroutine\n--with-date_time\n--with-exception\n--with-fiber\n--with-filesystem\n--with-graph\n--with-graph_parallel\n--with-iostreams\n--with-locale\n--with-log\n--with-math\n--with-mpi\n--with-program_options\n--with-python\n--with-random\n--with-regex\n--with-serialization\n--with-stacktrace\n--with-system\n--with-test\n--with-thread\n--with-timer\n--with-type_erasure\n\n[bootstrap.args.os=darwin]\n# Not really gcc, but CI puts a broken clang in the PATH, and saying gcc\n# here selects the correct one from Xcode.\n--with-toolset=gcc\n\n[b2.args.os=linux]\n# RHEL hardened gcc is not compatible with PCH\n# https://bugzilla.redhat.com/show_bug.cgi?id=1806545\npch=off\n\n[b2.args.os=darwin]\ntoolset=clang\n# Since Xcode 15.3 std::piecewise_construct is only visible in C++17 and later modes\ncxxflags=\"-DBOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT=0\"\n\n[b2.args.all(os=windows,fb=on)]\ntoolset=msvc-14.3\n"
  },
  {
    "path": "build/fbcode_builder/manifests/boost-python",
    "content": "[manifest]\nname = boost-python\n\n[download.not(os=windows)]\nurl = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.tar.gz\nsha256 = c0685b68dd44cc46574cce86c4e17c0f611b15e195be9848dfd0769a0a207628\n\n[download.os=windows]\nurl = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.zip\nsha256 = c86bd9d9eef795b4b0d3802279419fde5221922805b073b9bd822edecb1ca28e\n\n[preinstalled.env]\n# Here we list the acceptable versions that cmake needs a hint to find\nBOOST_ROOT_1_69_0\nBOOST_ROOT_1_83_0\n\n[homebrew]\nboost\n# Boost cmake detection on homebrew adds this as requirement: https://github.com/Homebrew/homebrew-core/issues/67427#issuecomment-754187345\nicu4c\n\n[pps]\nboost\n\n[rpms.all(distro=centos_stream,distro_vers=8)]\nboost169\nboost169-math\nboost169-test\nboost169-fiber\nboost169-graph\nboost169-log\nboost169-openmpi\nboost169-timer\nboost169-chrono\nboost169-locale\nboost169-thread\nboost169-atomic\nboost169-random\nboost169-static\nboost169-contract\nboost169-date-time\nboost169-iostreams\nboost169-container\nboost169-coroutine\nboost169-filesystem\nboost169-system\nboost169-stacktrace\nboost169-regex\nboost169-devel\nboost169-context\nboost169-python3-devel\nboost169-type_erasure\nboost169-wave\nboost169-python3\nboost169-serialization\nboost169-program-options\n\n[rpms.distro=fedora]\nboost-devel\nboost-static\n\n[build]\nbuilder = boost\njob_weight_mib = 512\npatchfile = boost_1_83_0.patch\n\n[build.not(os=linux)]\nbuilder = nop\n\n[b2.args]\n--with-atomic\n--with-chrono\n--with-container\n--with-context\n--with-contract\n--with-coroutine\n--with-date_time\n--with-exception\n--with-fiber\n--with-filesystem\n--with-graph\n--with-graph_parallel\n--with-iostreams\n--with-locale\n--with-log\n--with-math\n--with-mpi\n--with-program_options\n--with-python\n--with-random\n--with-regex\n--with-serialization\n--with-stacktrace\n--with-system\n--with-test\n--with-thread\n--with-timer\n--with-type_erasure\n\n[bootstrap.args.os=darwin]\n# Not really gcc, but CI puts a broken clang in the PATH, and saying gcc\n# here selects the correct one from Xcode.\n--with-toolset=gcc\n\n[b2.args.os=linux]\n# RHEL hardened gcc is not compatible with PCH\n# https://bugzilla.redhat.com/show_bug.cgi?id=1806545\npch=off\n# Python extensions need -fPIC for static library linking into shared objects\ncxxflags=\"-fPIC\"\n\n[b2.args.os=darwin]\ntoolset=clang\n# Since Xcode 15.3 std::piecewise_construct is only visible in C++17 and later modes\ncxxflags=\"-DBOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT=0\"\n\n[b2.args.all(os=windows,fb=on)]\ntoolset=msvc-14.3\n"
  },
  {
    "path": "build/fbcode_builder/manifests/bz2",
    "content": "[manifest]\nname = bz2\n\n[debs]\nlibbz2-dev\nbzip2\n\n[homebrew]\nbzip2\n\n[rpms]\nbzip2-devel\nbzip2\n\n[download]\nurl = https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz\nsha256 = ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269\n\n[build.not(os=windows)]\nbuilder = make\nsubdir = bzip2-1.0.8\n\n[make.build_args.os=linux]\n# python bz2 support on linux needs dynamic library\n-f\nMakefile-libbz2_so\n\n[make.install_args]\ninstall\n\n[build.os=windows]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/c-ares",
    "content": "[manifest]\nname = c-ares\n\n[download]\nurl = https://github.com/c-ares/c-ares/releases/download/v1.34.6/c-ares-1.34.6.tar.gz\nsha256 = 912dd7cc3b3e8a79c52fd7fb9c0f4ecf0aaa73e45efda880266a2d6e26b84ef5\n\n[build]\nbuilder = cmake\nsubdir = c-ares-1.34.6\n\n[cmake.defines]\nCARES_STATIC = ON\n\n[cmake.defines.shared_libs=off]\nCARES_SHARED = OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/cabal",
    "content": "[manifest]\nname = cabal\n\n[download.os=linux]\nurl = https://downloads.haskell.org/~cabal/cabal-install-3.6.2.0/cabal-install-3.6.2.0-x86_64-linux-deb10.tar.xz\nsha256 = 4759b56e9257e02f29fa374a6b25d6cb2f9d80c7e3a55d4f678a8e570925641c\n\n[build]\nbuilder = nop\n\n[install.files]\ncabal = bin/cabal\n"
  },
  {
    "path": "build/fbcode_builder/manifests/cachelib",
    "content": "[manifest]\nname = cachelib\nfbsource_path = fbcode/cachelib\nshipit_project = cachelib\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/cachelib.git\n\n[build]\nbuilder = cmake\nsubdir = cachelib\njob_weight_mib = 2048\n\n[dependencies]\nzlib\nfizz\nfmt\nfolly\nfbthrift\ngoogletest\nsparsemap\nwangle\nzstd\nmvfst\nnuma\nlibaio\nmagic_enum\n# cachelib also depends on openssl but since the latter requires a platform-\n# specific configuration we rely on the folly manifest to provide this\n# dependency to avoid duplication.\n\n[dependencies.all(distro=centos_stream,distro_vers=9)]\ngcc14\n\n[cmake.defines.all(distro=centos_stream,distro_vers=9)]\nCMAKE_C_COMPILER=/opt/rh/gcc-toolset-14/root/usr/bin/gcc\nCMAKE_CXX_COMPILER=/opt/rh/gcc-toolset-14/root/usr/bin/g++\n\n[shipit.pathmap]\nfbcode/cachelib = cachelib\nfbcode/cachelib/public_tld = .\n\n[shipit.strip]\n^fbcode/cachelib/examples(/|$)\n^fbcode/cachelib/facebook(/|$)\n^fbcode/cachelib/public_tld/website/docs/facebook(/|$)\n^fbcode/cachelib/public_tld/website/node_modules(/|$)\n^fbcode/cachelib/public_tld/website/build(/|$)\n"
  },
  {
    "path": "build/fbcode_builder/manifests/cinderx-3_14",
    "content": "[manifest]\nname = cinderx-3_14\nfbsource_path = fbcode/cinderx\nshipit_project = facebookincubator/cinderx\n\n[git]\nrepo_url = https://github.com/facebookincubator/cinderx.git\n\n[build.os=linux]\nbuilder = setup-py\n\n[build.not(os=linux)]\nbuilder = nop\n\n[dependencies]\npython-setuptools\npython-3_14\n\n[shipit.pathmap]\nfbcode/cinderx = cinderx\nfbcode/cinderx/oss_toplevel = .\n\n[setup-py.test]\npython_script = cinderx/PythonLib/test_cinderx/test_oss_quick.py\n\n[setup-py.env]\nCINDERX_ENABLE_PGO=1\nCINDERX_ENABLE_LTO=1\n"
  },
  {
    "path": "build/fbcode_builder/manifests/cinderx-main",
    "content": "# For building CinderX against CPython main.\n# Note that externally this can be broken  because in that environment we will\n# be checking out the head of the CPython repo. However CinderX is only built\n# and tested against our internal copy of CPython which updates ~daily, and so\n# may be behind CPython head.\n\n[manifest]\nname = cinderx-main\nfbsource_path = fbcode/cinderx\nshipit_project = facebookincubator/cinderx\n\n[git]\nrepo_url = https://github.com/facebookincubator/cinderx.git\n\n[build.os=linux]\nbuilder = setup-py\n\n[build.not(os=linux)]\nbuilder = nop\n\n[dependencies]\npython-setuptools\npython-main\n\n[shipit.pathmap]\nfbcode/cinderx = cinderx\nfbcode/cinderx/oss_toplevel = .\n\n[setup-py.test]\npython_script = cinderx/PythonLib/test_cinderx/test_oss_quick.py\n\n[setup-py.env]\nCINDERX_ENABLE_PGO=1\nCINDERX_ENABLE_LTO=1\n"
  },
  {
    "path": "build/fbcode_builder/manifests/clang",
    "content": "[manifest]\nname = clang\n\n[rpms]\nclang15-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/clang19",
    "content": "[manifest]\nname = clang19\n\n[debs.os=linux]\nclang-19\n\n[rpms.os=linux]\nclang\n"
  },
  {
    "path": "build/fbcode_builder/manifests/cmake",
    "content": "[manifest]\nname = cmake\n\n[homebrew]\ncmake\n\n# 18.04 cmake is too old\n[debs.not(all(distro=ubuntu,distro_vers=\"18.04\"))]\ncmake\n\n[rpms]\ncmake\n\n[pps]\ncmake\n\n[dependencies]\nninja\n\n[download.os=windows]\nurl = https://github.com/Kitware/CMake/releases/download/v3.20.4/cmake-3.20.4-windows-x86_64.zip\nsha256 = 965d2f001c3ca807d288f2b6b15c42b25579a0e73ef12c2a72c95f4c69123638\n\n[download.os=darwin]\nurl = https://github.com/Kitware/CMake/releases/download/v3.20.4/cmake-3.20.4-macos-universal.tar.gz\nsha256 = df90016635e3183834143c6d94607f0804fe9762f7cc6032f6a4afd7c19cd43b\n\n[download.any(os=linux,os=freebsd)]\nurl = https://github.com/Kitware/CMake/releases/download/v3.20.4/cmake-3.20.4.tar.gz\nsha256 = 87a4060298f2c6bb09d479de1400bc78195a5b55a65622a7dceeb3d1090a1b16\n\n[build.os=windows]\nbuilder = nop\nsubdir = cmake-3.20.4-windows-x86_64\n\n[build.os=darwin]\nbuilder = nop\nsubdir = cmake-3.20.4-macos-universal\n\n[install.files.os=darwin]\nCMake.app/Contents/bin = bin\nCMake.app/Contents/share = share\n\n[build.any(os=linux,os=freebsd)]\nbuilder = cmakebootstrap\nsubdir = cmake-3.20.4\n\n[make.install_args.any(os=linux,os=freebsd)]\ninstall\n"
  },
  {
    "path": "build/fbcode_builder/manifests/cpptoml",
    "content": "[manifest]\nname = cpptoml\n\n[homebrew]\ncpptoml\n\n[download]\nurl = https://github.com/chadaustin/cpptoml/archive/refs/tags/v0.1.2.tar.gz\nsha256 = beda37e94f9746874436c8090c045fd80ae6f8a51f7c668c932a2b110a4fc277\n\n[build]\nbuilder = cmake\nsubdir = cpptoml-0.1.2\n\n[cmake.defines.os=freebsd]\nENABLE_LIBCXX=NO\n"
  },
  {
    "path": "build/fbcode_builder/manifests/double-conversion",
    "content": "[manifest]\nname = double-conversion\n\n[download]\nurl = https://github.com/google/double-conversion/archive/v3.1.4.tar.gz\nsha256 = 95004b65e43fefc6100f337a25da27bb99b9ef8d4071a36a33b5e83eb1f82021\n\n[homebrew]\ndouble-conversion\n\n[debs]\nlibdouble-conversion-dev\n\n[rpms]\ndouble-conversion\ndouble-conversion-devel\n\n[pps]\ndouble-conversion\n\n[build]\nbuilder = cmake\nsubdir = double-conversion-3.1.4\n"
  },
  {
    "path": "build/fbcode_builder/manifests/double-conversion-python",
    "content": "[manifest]\nname = double-conversion-python\n\n[download]\nurl = https://github.com/google/double-conversion/archive/v3.1.4.tar.gz\nsha256 = 95004b65e43fefc6100f337a25da27bb99b9ef8d4071a36a33b5e83eb1f82021\n\n[homebrew]\ndouble-conversion\n\n[debs]\nlibdouble-conversion-dev\n\n[rpms]\ndouble-conversion\ndouble-conversion-devel\n\n[pps]\ndouble-conversion\n\n[build]\nbuilder = cmake\nsubdir = double-conversion-3.1.4\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines]\nCMAKE_POSITION_INDEPENDENT_CODE=ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/eden",
    "content": "[manifest]\nname = eden\nfbsource_path = fbcode/eden\nshipit_project = eden\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/sapling.git\n\n[github.actions]\nrun_tests = off\n\n[sandcastle]\nrun_tests = off\n\n[build]\nbuilder = cmake\n\n[dependencies]\nblake3\ngoogletest\nfolly\nfbthrift\nfb303\ncpptoml\nrocksdb\nre2\nlibgit2\npexpect\npython-psutil\npython-toml\npython-filelock\nedencommon\nrust-shed\n\n[dependencies.fbsource=on]\nrust\n\n# macOS ships with sqlite3, and some of the core system\n# frameworks require that that version be linked rather\n# than the one we might build for ourselves here, so we\n# skip building it on macos.\n[dependencies.not(os=darwin)]\nsqlite3\n\n[dependencies.os=darwin]\nosxfuse\n\n[dependencies.not(os=windows)]\n# TODO: teach getdeps to compile curl on Windows.\n# Enabling curl on Windows requires us to find a way to compile libcurl with\n# msvc.\nlibcurl\n# Added so that OSS doesn't see system \"python\" which is python 2 on darwin and some linux\npython\n# TODO: teach getdeps to compile lmdb on Windows.\nlmdb\n\n[dependencies.test=on]\n# sapling CLI is needed to run the tests\nsapling\n\n[shipit.pathmap.fb=on]\n# for internal builds that use getdeps\nfbcode/fb303 = fb303\nfbcode/common/rust/shed = common/rust/shed\nfbcode/thrift/lib/cpp = thrift/lib/cpp\nfbcode/thrift/lib/cpp2 = thrift/lib/cpp2\nfbcode/thrift/lib/java = thrift/lib/java\nfbcode/thrift/lib/py = thrift/lib/py\nfbcode/thrift/lib/python = thrift/lib/python\nfbcode/thrift/lib/rust = thrift/lib/rust\n\n[shipit.pathmap]\n# Map hostcaps for now as eden C++ includes its .h. Rust-shed should install it\nfbcode/common/rust/shed/hostcaps = common/rust/shed/hostcaps\nfbcode/configerator/structs/scm/hg = configerator/structs/scm/hg\nfbcode/eden/oss = .\nfbcode/eden = eden\nfbcode/tools/lfs = tools/lfs\n\n[shipit.pathmap.fb=off]\nfbcode/eden/fs/public_autocargo = eden/fs\nfbcode/eden/scm/public_autocargo = eden/scm\nfbcode/common/rust/shed/hostcaps/public_cargo = common/rust/shed/hostcaps\nfbcode/configerator/structs/scm/hg/public_autocargo = configerator/structs/scm/hg\n\n[shipit.strip]\n^fbcode/eden/addons/.*$\n^fbcode/eden/fs/eden-config\\.h$\n^fbcode/eden/fs/py/eden/config\\.py$\n^fbcode/eden/hg-server/.*$\n^fbcode/eden/mononoke/(?!lfs_protocol)\n^fbcode/eden/scm/build/.*$\n^fbcode/eden/scm/lib/third-party/rust/.*/Cargo.toml$\n^fbcode/eden/website/.*$\n^fbcode/eden/.*/\\.cargo/.*$\n/Cargo\\.lock$\n\\.pyc$\n\n[shipit.strip.fb=off]\n^fbcode/common/rust/shed(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/configerator/structs/scm/hg(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/eden/fs(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/eden/scm(?!/public_autocargo|/saplingnative).*/Cargo\\.toml$\n^.*/facebook/.*$\n^.*/fb/.*$\n\n[cmake.defines.all(fb=on,os=windows)]\nENABLE_GIT=OFF\nINSTALL_PYTHON_LIB=ON\n\n[cmake.defines.all(not(fb=on),os=windows)]\nENABLE_GIT=OFF\n\n[cmake.defines.fbsource=on]\nUSE_CARGO_VENDOR=ON\n\n[cmake.defines.fb=on]\nIS_FB_BUILD=ON\n\n[depends.environment]\nEDEN_VERSION_OVERRIDE\n"
  },
  {
    "path": "build/fbcode_builder/manifests/edencommon",
    "content": "[manifest]\nname = edencommon\nfbsource_path = fbcode/eden/common\nshipit_project = edencommon\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebookexperimental/edencommon.git\n\n[build]\nbuilder = cmake\n\n[dependencies]\nfbthrift\nfb303\nfmt\nfolly\ngflags\nglog\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\n\n[shipit.pathmap]\nfbcode/eden/common = eden/common\nfbcode/eden/common/oss = .\n\n[shipit.strip]\n@README.facebook@\n"
  },
  {
    "path": "build/fbcode_builder/manifests/exprtk",
    "content": "[manifest]\nname = exprtk\n\n[download]\nurl = https://github.com/ArashPartow/exprtk/archive/refs/tags/0.0.1.tar.gz\nsha256 = fb72791c88ae3b3426e14fdad630027715682584daf56b973569718c56e33f28\n\n[build.not(os=windows)]\nbuilder = nop\nsubdir = exprtk-0.0.1\n\n[install.files]\nexprtk.hpp = exprtk.hpp\n\n[dependencies]\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fast_float",
    "content": "[manifest]\nname = fast_float\n\n[download]\nurl = https://github.com/fastfloat/fast_float/archive/refs/tags/v8.0.0.tar.gz\nsha256 = f312f2dc34c61e665f4b132c0307d6f70ad9420185fa831911bc24408acf625d\n\n[build]\nbuilder = cmake\nsubdir = fast_float-8.0.0\n\n[cmake.defines]\nFASTFLOAT_TEST = OFF\nFASTFLOAT_SANITIZE = OFF\n\n[debs.not(all(distro=ubuntu,any(distro_vers=\"18.04\",distro_vers=\"20.04\",distro_vers=\"22.04\",distro_vers=\"24.04\")))]\nlibfast-float-dev\n\n[rpms.distro=fedora]\nfast_float-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fatal",
    "content": "[manifest]\nname = fatal\nfbsource_path = fbcode/fatal\nshipit_project = fatal\n\n[git]\nrepo_url = https://github.com/facebook/fatal.git\n\n[shipit.pathmap]\nfbcode/fatal = fatal\nfbcode/fatal/public_tld = .\n\n[build]\nbuilder = nop\nsubdir = .\n\n[install.files]\nfatal/portability.h = fatal/portability.h\nfatal/preprocessor.h = fatal/preprocessor.h\nfatal/container = fatal/container\nfatal/functional = fatal/functional\nfatal/math = fatal/math\nfatal/string = fatal/string\nfatal/type = fatal/type\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fb303",
    "content": "[manifest]\nname = fb303\nfbsource_path = fbcode/fb303\nshipit_project = fb303\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/fb303.git\n\n[cargo]\ncargo_config_file = source/fb303/thrift/.cargo/config.toml\n\n[crate.pathmap]\nfb303_core = fb303/thrift/rust\n\n[build]\nbuilder = cmake\n\n[dependencies]\nfolly\ngflags\nglog\nfbthrift\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\n\n[shipit.pathmap]\nfbcode/fb303/github = .\nfbcode/fb303/public_autocargo = fb303\nfbcode/fb303 = fb303\n\n[shipit.strip]\n^fbcode/fb303/(?!public_autocargo).+/Cargo\\.toml$\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fboss",
    "content": "[manifest]\nname = fboss\nfbsource_path = fbcode/fboss\nshipit_project = fboss\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/fboss.git\n\n[build.os=linux]\nbuilder = cmake\n# fboss files take a lot of RAM to compile.\njob_weight_mib = 3072\n\n[build.not(os=linux)]\nbuilder = nop\n\n[dependencies]\nfolly\nfb303\nwangle\nfizz\nmvfst\nfmt\nlibsodium\ngoogletest\nzstd\nfatal\nfbthrift\niproute2\nlibusb\nlibcurl\nlibnl\nlibsai\nre2\npython\nyaml-cpp\nlibyaml\nCLI11\nexprtk\nnlohmann-json\nlibgpiod\nsystemd\nrange-v3\ntabulate\ngcc12\npython-pyyaml\n\n# ShipitPathMap always assume to use directory to fetch the source code.\n# If you need to sync the files to fboss github repo, please make changes in\n# configerator/source/opensource/shipit_config/facebook/fboss.cconf\n[shipit.pathmap]\nfbcode/fboss/github = .\nfbcode/fboss/common = common\nfbcode/fboss = fboss\n# NOTE: Although this directory has other thrift files might not be ready for\n# opensource, this pathmap should only be used internally `getdeps.py fetch`\nfbcode/configerator/structs/neteng/fboss/thrift = configerator/structs/neteng/fboss/thrift\n\n[shipit.strip]\n^fbcode/fboss/github/docs/.*\n^fbcode/fboss/oss/.*\n^fbcode/fboss/github/.github/.*\n^fbcode/fboss/github/fboss-image/.*\n^fbcode/fboss/github/.pre-commit-config.yaml\n^fbcode/fboss/github/requirements-dev.txt\n^fbcode/fboss/.llms/.*\n\n[sandcastle]\nrun_tests = off\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fbthrift",
    "content": "[manifest]\nname = fbthrift\nfbsource_path = xplat/thrift\nshipit_project = fbthrift\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/fbthrift.git\n\n[cargo]\ncargo_config_file =  source/thrift/lib/rust/.cargo/config.toml\n\n[crate.pathmap]\nfbthrift = thrift/lib/rust\n\n[build]\nbuilder = cmake\njob_weight_mib = 2048\n\n[cmake.defines.all(not(os=windows),test=on)]\nenable_tests=ON\n\n[cmake.defines.any(os=windows,test=off)]\nenable_tests=OFF\n\n[dependencies]\nfizz\nfmt\nfolly\ngoogletest\nlibsodium\nwangle\nzstd\nmvfst\nxxhash\n# Thrift also depends on openssl but since the latter requires a platform-\n# specific configuration we rely on the folly manifest to provide this\n# dependency to avoid duplication.\n\n[shipit.pathmap]\nxplat/thrift/public_tld = .\nxplat/thrift = thrift\n\n[shipit.strip]\n^xplat/thrift/thrift-config\\.h$\n^xplat/thrift/perf/canary.py$\n^xplat/thrift/perf/loadtest.py$\n^xplat/thrift/.castle/.*\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fbthrift-python",
    "content": "[manifest]\nname = fbthrift-python\nfbsource_path = xplat/thrift\nshipit_project = fbthrift\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/fbthrift.git\n\n[cargo]\ncargo_config_file =  source/thrift/lib/rust/.cargo/config.toml\n\n[crate.pathmap]\nfbthrift = thrift/lib/rust\n\n[build]\nbuilder = cmake\njob_weight_mib = 2048\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines.all(not(os=windows),test=on)]\nenable_tests=ON\n\n[cmake.defines.any(os=windows,test=off)]\nenable_tests=OFF\n\n[cmake.defines.os=linux]\nthrift_python=ON\nenable_tests=ON\n\n[dependencies]\nfizz-python\nfmt-python\nfolly-python\ngoogletest\nlibsodium\nwangle-python\nzstd-python\nmvfst-python\nxxhash\n# Thrift also depends on openssl but since the latter requires a platform-\n# specific configuration we rely on the folly manifest to provide this\n# dependency to avoid duplication.\n\n[dependencies.os=linux]\nlibaio-python\nlibevent-python\n\n[shipit.pathmap]\nxplat/thrift/public_tld = .\nxplat/thrift = thrift\n\n[shipit.strip]\n^xplat/thrift/thrift-config\\.h$\n^xplat/thrift/perf/canary.py$\n^xplat/thrift/perf/loadtest.py$\n^xplat/thrift/.castle/.*\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fizz",
    "content": "[manifest]\nname = fizz\nfbsource_path = fbcode/fizz\nshipit_project = fizz\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebookincubator/fizz.git\n\n[build]\nbuilder = cmake\nsubdir = fizz\n\n[cmake.defines]\nBUILD_EXAMPLES = OFF\n\n[cmake.defines.test=on]\nBUILD_TESTS = ON\n\n[cmake.defines.all(os=windows, test=on)]\nBUILD_TESTS = OFF\n\n[cmake.defines.test=off]\nBUILD_TESTS = OFF\n\n[dependencies]\nfolly\nliboqs\nlibsodium\nzlib\nzstd\n\n[dependencies.all(test=on, not(os=windows))]\ngoogletest\n\n[shipit.pathmap]\nfbcode/fizz/public_tld = .\nfbcode/fizz = fizz\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fizz-python",
    "content": "[manifest]\nname = fizz-python\nfbsource_path = fbcode/fizz\nshipit_project = fizz\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebookincubator/fizz.git\n\n[build]\nbuilder = cmake\nsubdir = fizz\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines]\nBUILD_EXAMPLES = OFF\n\n[cmake.defines.os=linux]\nCMAKE_POSITION_INDEPENDENT_CODE = ON\nBUILD_SHARED_LIBS = ON\n\n[cmake.defines.test=on]\nBUILD_TESTS = ON\n\n[cmake.defines.all(os=windows, test=on)]\nBUILD_TESTS = OFF\n\n[cmake.defines.test=off]\nBUILD_TESTS = OFF\n\n[dependencies]\nfolly-python\nliboqs\nlibsodium\nzlib-python\nzstd-python\n\n[dependencies.all(test=on, not(os=windows))]\ngoogletest\n\n[shipit.pathmap]\nfbcode/fizz/public_tld = .\nfbcode/fizz = fizz\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fmt",
    "content": "[manifest]\nname = fmt\n\n[download]\nurl = https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz\nsha256 = ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea\n\n[build]\nbuilder = cmake\nsubdir = fmt-12.1.0\n\n[cmake.defines]\nFMT_TEST = OFF\nFMT_DOC = OFF\n\n[homebrew]\nfmt\n\n[rpms.distro=fedora]\nfmt-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/fmt-python",
    "content": "[manifest]\nname = fmt-python\n\n[download]\nurl = https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz\nsha256 = ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea\n\n[build]\nbuilder = cmake\nsubdir = fmt-12.1.0\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines]\nFMT_TEST = OFF\nFMT_DOC = OFF\n# Build as shared library so Python extensions can find fmt symbols at runtime\n# (fmt uses -fvisibility=hidden, so static linking leaves symbols unexported)\nBUILD_SHARED_LIBS = ON\n\n[homebrew]\nfmt\n\n[rpms.distro=fedora]\nfmt-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/folly",
    "content": "[manifest]\nname = folly\nfbsource_path = fbcode/folly\nshipit_project = folly\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/folly.git\n\n[build]\nbuilder = cmake\njob_weight_mib = 1024\n\n[dependencies]\ngflags\nglog\ngoogletest\nboost\nlibdwarf\nlibevent\nlibsodium\ndouble-conversion\nfast_float\nfmt\nlz4\nsnappy\nzstd\n# no openssl or zlib in the linux case, why?\n# these are usually installed on the system\n# and are the easiest system deps to pull in.\n# In the future we want to be able to express\n# that a system dep is sufficient in the manifest\n# for eg: openssl and zlib, but for now we don't\n# have it.\n\n# macOS doesn't expose the openssl api so we need\n# to build our own.\n[dependencies.os=darwin]\nopenssl\n\n# Windows has neither openssl nor zlib, so we get\n# to provide both\n[dependencies.os=windows]\nopenssl\nzlib\n\n[dependencies.os=linux]\nlibaio\nlibiberty\nlibunwind\n\n# xz depends on autoconf which does not build on\n# Windows\n[dependencies.not(os=windows)]\nxz\n\n[shipit.pathmap]\nfbcode/folly/public_tld = .\nfbcode/folly = folly\n\n[shipit.strip]\n^fbcode/folly/folly-config\\.h$\n^fbcode/folly/public_tld/build/facebook_.*\n\n[cmake.defines]\nBUILD_SHARED_LIBS=OFF\n\n[cmake.defines.not(os=windows)]\nBOOST_LINK_STATIC=ON\n\n[cmake.defines.os=freebsd]\nLIBDWARF_FOUND=NO\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\nBUILD_BENCHMARKS=OFF\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\nBUILD_BENCHMARKS=OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/folly-python",
    "content": "[manifest]\nname = folly-python\nfbsource_path = fbcode/folly\nshipit_project = folly\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/folly.git\n\n[build]\nbuilder = cmake\njob_weight_mib = 1024\n\n[build.not(os=linux)]\nbuilder = nop\n\n[dependencies]\ngflags\nglog\ngoogletest\nboost-python\nlibdwarf-python\nlibevent-python\nlibsodium\ndouble-conversion-python\nfast_float\nfmt-python\nlz4-python\nsnappy\nzstd-python\n# no openssl or zlib in the linux case, why?\n# these are usually installed on the system\n# and are the easiest system deps to pull in.\n# In the future we want to be able to express\n# that a system dep is sufficient in the manifest\n# for eg: openssl and zlib, but for now we don't\n# have it.\n\n# macOS doesn't expose the openssl api so we need\n# to build our own.\n[dependencies.os=darwin]\nopenssl\n\n# Windows has neither openssl nor zlib, so we get\n# to provide both\n[dependencies.os=windows]\nopenssl\nzlib\n\n[dependencies.os=linux]\nlibaio-python\nlibiberty-python\nlibunwind\n\n# xz depends on autoconf which does not build on\n# Windows\n[dependencies.not(os=windows)]\nxz\n\n[shipit.pathmap]\nfbcode/folly/public_tld = .\nfbcode/folly = folly\n\n[shipit.strip]\n^fbcode/folly/folly-config\\.h$\n^fbcode/folly/public_tld/build/facebook_.*\n\n[cmake.defines.os=linux]\nPYTHON_EXTENSIONS=ON\nBUILD_SHARED_LIBS=ON\n\n[cmake.defines.not(os=windows)]\nBOOST_LINK_STATIC=ON\n\n[cmake.defines.os=freebsd]\nLIBDWARF_FOUND=NO\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\nBUILD_BENCHMARKS=OFF\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\nBUILD_BENCHMARKS=OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/gcc12",
    "content": "[manifest]\nname = gcc12\n\n[rpms.all(distro=centos_stream,distro_vers=9)]\ngcc-toolset-12\n"
  },
  {
    "path": "build/fbcode_builder/manifests/gcc14",
    "content": "[manifest]\nname = gcc14\n\n[rpms.all(distro=centos_stream,distro_vers=9)]\ngcc-toolset-14\n"
  },
  {
    "path": "build/fbcode_builder/manifests/gflags",
    "content": "[manifest]\nname = gflags\n\n[download]\nurl = https://github.com/gflags/gflags/archive/v2.2.2.tar.gz\nsha256 = 34af2f15cf7367513b352bdcd2493ab14ce43692d2dcd9dfc499492966c64dcf\n\n[build]\nbuilder = cmake\nsubdir = gflags-2.2.2\n\n[cmake.defines]\nBUILD_SHARED_LIBS = ON\nBUILD_STATIC_LIBS = ON\n#BUILD_gflags_nothreads_LIB = OFF\nBUILD_gflags_LIB = ON\n\n[homebrew]\ngflags\n\n[debs]\nlibgflags-dev\n\n[rpms.distro=fedora]\ngflags-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/ghc",
    "content": "[manifest]\nname = ghc\n\n[download.os=linux]\nurl = https://downloads.haskell.org/~ghc/9.2.8/ghc-9.2.8-x86_64-fedora27-linux.tar.xz\nsha256 = 845f63cd365317bb764d81025554a2527dbe315d6fa268c9859e21b911bf2d3c\n\n[build]\nbuilder = autoconf\nsubdir = ghc-9.2.8\nbuild_in_src_dir = true\nonly_install = true\n\n[make.install_args]\ninstall\n"
  },
  {
    "path": "build/fbcode_builder/manifests/git-lfs",
    "content": "[manifest]\nname = git-lfs\n\n[rpms]\ngit-lfs\n\n[debs]\ngit-lfs\n\n[homebrew]\ngit-lfs\n\n# only used from system packages currently\n[build]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/glean",
    "content": "[manifest]\nname = glean\nfbsource_path = fbcode/glean\nshipit_project = facebookincubator/Glean\nuse_shipit = true\n\n[shipit.pathmap]\n# These are only used by target determinator to trigger builds, the\n# real path mappings are in the ShipIt config.\nfbcode/glean = glean\nfbcode/common/hs = hsthrift\n\n[subprojects]\nhsthrift = hsthrift\n\n[dependencies]\ncabal\nghc\ngflags\nglog\nfolly\nrocksdb\nxxhash\nllvm\nclang\nre2\n\n[build]\nbuilder = make\n\n[make.build_args]\nsetup-folly\nsetup-folly-version\ncabal-update\nall\nglean-hie\nglass\nglean-clang\nEXTRA_GHC_OPTS=-j4 +RTS -A32m -n4m -RTS\nCABAL_CONFIG_FLAGS=-f-hack-tests -f-typescript-tests -f-python-tests -f-dotnet-tests -f-go-tests -f-rust-tests -f-java-lsif-tests -f-flow-tests -f-bundled-folly\n\n[make.install_args]\ninstall\n\n[make.test_args]\ntest\nEXTRA_GHC_OPTS=-j4 +RTS -A32m -n4m -RTS\nCABAL_CONFIG_FLAGS=-f-hack-tests -f-typescript-tests -f-python-tests -f-dotnet-tests -f-go-tests -f-rust-tests -f-java-lsif-tests -f-flow-tests -f-bundled-folly\n"
  },
  {
    "path": "build/fbcode_builder/manifests/glog",
    "content": "[manifest]\nname = glog\n\n[download]\nurl = https://github.com/google/glog/archive/v0.5.0.tar.gz\nsha256 = eede71f28371bf39aa69b45de23b329d37214016e2055269b3b5e7cfd40b59f5\n\n[build]\nbuilder = cmake\nsubdir = glog-0.5.0\n\n[dependencies]\ngflags\n\n[cmake.defines]\nBUILD_SHARED_LIBS=ON\nBUILD_TESTING=NO\nWITH_PKGCONFIG=ON\n\n[cmake.defines.os=freebsd]\nHAVE_TR1_UNORDERED_MAP=OFF\nHAVE_TR1_UNORDERED_SET=OFF\n\n[homebrew]\nglog\n\n# on ubuntu glog brings in liblzma-dev, which in turn breaks watchman tests\n[debs.not(distro=ubuntu)]\nlibgoogle-glog-dev\n\n[rpms.distro=fedora]\nglog-devel\n\n"
  },
  {
    "path": "build/fbcode_builder/manifests/googletest",
    "content": "[manifest]\nname = googletest\n\n[download]\nurl = https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz\nsha256 = 65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c\n\n[build]\nbuilder = cmake\nsubdir = googletest-1.17.0\n\n[cmake.defines]\n# Everything else defaults to the shared runtime, so tell gtest that\n# it should not use its choice of the static runtime\ngtest_force_shared_crt=ON\n\n[cmake.defines.os=windows]\nBUILD_SHARED_LIBS=ON\n\n[homebrew]\ngoogletest\n\n# packaged googletest is too old\n[debs.not(all(distro=ubuntu,any(distro_vers=\"18.04\",distro_vers=\"20.04\",distro_vers=\"22.04\")))]\nlibgtest-dev\nlibgmock-dev\n\n[rpms.distro=fedora]\ngmock-devel\ngtest-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/gperf",
    "content": "[manifest]\nname = gperf\n\n[download]\nurl = https://ftpmirror.gnu.org/gnu/gperf/gperf-3.1.tar.gz\nsha256 = 588546b945bba4b70b6a3a616e80b4ab466e3f33024a352fc2198112cdbb3ae2\n\n[build.not(os=windows)]\nbuilder = autoconf\nsubdir = gperf-3.1\n\n[build.os=windows]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/hexdump",
    "content": "[manifest]\nname = hexdump\n\n[rpms]\nutil-linux\n\n[debs]\nbsdmainutils\n\n# only used from system packages currently\n[build]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/hsthrift",
    "content": "[manifest]\nname = hsthrift\nfbsource_path = fbcode/common/hs\nshipit_project = facebookincubator/hsthrift\nuse_shipit = true\n\n[shipit.pathmap]\n# These are only used by target determinator to trigger builds, the\n# real path mappings are in the ShipIt config.\nfbcode/common/hs = .\n\n[dependencies]\ncabal\nghc\ngflags\nglog\nfolly\nfbthrift\nwangle\nfizz\nboost\n\n[build]\nbuilder = make\n\n[make.build_args]\nsetup-folly\nsetup-meta\ncabal-update\nall\n\n[make.install_args]\ninstall\n\n[make.test_args]\ntest\n"
  },
  {
    "path": "build/fbcode_builder/manifests/iproute2",
    "content": "[manifest]\nname = iproute2\n\n[download]\nurl = https://mirrors.edge.kernel.org/pub/linux/utils/net/iproute2/iproute2-4.12.0.tar.gz\nsha256 = 46612a1e2d01bb31932557bccdb1b8618cae9a439dfffc08ef35ed8e197f14ce\n\n[build.os=linux]\nbuilder = iproute2\nsubdir = iproute2-4.12.0\npatchfile = iproute2_oss.patch\n\n[build.not(os=linux)]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/jom",
    "content": "# jom is compatible with MSVC nmake, but adds the /j<number of jobs> argment which \n# speeds up openssl build a lot\n[manifest]\nname = jom\n\n# see https://download.qt.io/official_releases/jom/changelog.txt for latest version\n[download.os=windows]\nurl = https://download.qt.io/official_releases/jom/jom_1_1_4.zip\nsha256 = d533c1ef49214229681e90196ed2094691e8c4a0a0bef0b2c901debcb562682b\n\n[build.os=windows]\nbuilder = nop\n\n[install.files.os=windows]\n. = bin\n"
  },
  {
    "path": "build/fbcode_builder/manifests/jq",
    "content": "[manifest]\nname = jq\n\n[rpms.distro=fedora]\njq\n\n[homebrew]\njq\n\n[download.not(os=windows)]\n# we use jq-1.7+ to get fix for number truncation https://github.com/jqlang/jq/pull/1752\nurl = https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-1.7.1.tar.gz\nsha256 = 478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2\n\n[build.not(os=windows)]\nbuilder = autoconf\nsubdir = jq-1.7.1\n\n[build.os=windows]\nbuilder = nop\n\n[autoconf.args]\n# This argument turns off some developers tool and it is recommended in jq's\n# README\n--disable-maintainer-mode\n"
  },
  {
    "path": "build/fbcode_builder/manifests/katran",
    "content": "[manifest]\nname = katran\nfbsource_path = fbcode/katran\nshipit_project = katran\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebookincubator/katran.git\n\n[build.not(os=linux)]\nbuilder = nop\n\n[build.os=linux]\nbuilder = cmake\nsubdir = .\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\n\n[dependencies]\nfolly\nfizz\nlibbpf\nlibmnl\nzlib\ngoogletest\nfmt\n\n[debs]\nlibssl-dev\n\n[shipit.pathmap]\nfbcode/katran/public_root = .\nfbcode/katran = katran\n\n[shipit.strip]\n^fbcode/katran/facebook\n^fbcode/katran/OSS_SYNC\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libaio",
    "content": "[manifest]\nname = libaio\n\n[debs]\nlibaio-dev\n\n[rpms.distro=centos_stream]\nlibaio-devel\n\n[download]\nurl = https://pagure.io/libaio/archive/libaio-0.3.113/libaio-libaio-0.3.113.tar.gz\nsha256 = 716c7059703247344eb066b54ecbc3ca2134f0103307192e6c2b7dab5f9528ab\n\n[build]\nbuilder = make\nsubdir = libaio-libaio-0.3.113\n\n[make.build_args.shared_libs=off]\nENABLE_SHARED=0\n\n[make.install_args]\ninstall\n\n[make.install_args.shared_libs=off]\nENABLE_SHARED=0\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libaio-python",
    "content": "[manifest]\nname = libaio-python\n\n[debs]\nlibaio-dev\n\n[rpms.distro=centos_stream]\nlibaio-devel\n\n[download]\nurl = https://pagure.io/libaio/archive/libaio-0.3.113/libaio-libaio-0.3.113.tar.gz\nsha256 = 716c7059703247344eb066b54ecbc3ca2134f0103307192e6c2b7dab5f9528ab\n\n[build]\nbuilder = make\nsubdir = libaio-libaio-0.3.113\n\n[build.not(os=linux)]\nbuilder = nop\n\n[make.build_args]\nCFLAGS=-fPIC -O2\n\n[make.install_args]\ninstall\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libbpf",
    "content": "[manifest]\nname = libbpf\n\n[download]\nurl = https://github.com/libbpf/libbpf/archive/refs/tags/v1.6.2.tar.gz\nsha256 = 16f31349c70764cba8e0fad3725cc9f52f6cf952554326aa0229daaa21ef4fbd\n\n# BPF only builds on linux, so make it a NOP on other platforms\n[build.not(os=linux)]\nbuilder = nop\n\n[build.os=linux]\nbuilder = make\nsubdir = libbpf-1.6.2/src\n\n[make.build_args]\nBUILD_STATIC_ONLY=y\n\n# libbpf-0.3 requires uapi headers >= 5.8\n[make.install_args]\ninstall\ninstall_uapi_headers\nBUILD_STATIC_ONLY=y\n\n[dependencies]\nlibelf\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libcurl",
    "content": "[manifest]\nname = libcurl\n\n[rpms]\nlibcurl-devel\nlibcurl-minimal\n\n[debs]\nlibcurl4-openssl-dev\n\n[pps]\nlibcurl-gnutls\n\n[download]\nurl = https://curl.haxx.se/download/curl-7.65.1.tar.gz\nsha256 = 821aeb78421375f70e55381c9ad2474bf279fc454b791b7e95fc83562951c690\n\n[dependencies]\nnghttp2\n\n# We use system OpenSSL on Linux (see folly's manifest for details)\n[dependencies.not(os=linux)]\nopenssl\n\n[build.not(os=windows)]\nbuilder = autoconf\nsubdir = curl-7.65.1\n\n[autoconf.args]\n# fboss (which added the libcurl dep) doesn't need ldap so it is disabled here.\n# if someone in the future wants to add ldap for something else, it won't hurt\n# fboss. However, that would require adding an ldap manifest.\n#\n# For the same reason, we disable libssh2 and libidn2 which aren't really used\n# but would require adding manifests if we don't disable them.\n--disable-ldap\n--without-libssh2\n--without-libidn2\n\n[build.os=windows]\nbuilder = cmake\nsubdir = curl-7.65.1\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libdwarf",
    "content": "[manifest]\nname = libdwarf\n\n[rpms]\nlibdwarf-devel\nlibdwarf\n\n[debs]\nlibdwarf-dev\n\n[homebrew]\ndwarfutils\n\n[download]\nurl = https://www.prevanders.net/libdwarf-0.9.2.tar.xz\nsha256 = 22b66d06831a76f6a062126cdcad3fcc58540b89a1acb23c99f8861f50999ec3\n\n[build]\nbuilder = cmake\nsubdir = libdwarf-0.9.2\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libdwarf-python",
    "content": "[manifest]\nname = libdwarf-python\n\n[rpms]\nlibdwarf-devel\nlibdwarf\n\n[debs]\nlibdwarf-dev\n\n[homebrew]\ndwarfutils\n\n[download]\nurl = https://www.prevanders.net/libdwarf-0.9.2.tar.xz\nsha256 = 22b66d06831a76f6a062126cdcad3fcc58540b89a1acb23c99f8861f50999ec3\n\n[build]\nbuilder = cmake\nsubdir = libdwarf-0.9.2\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines]\nCMAKE_POSITION_INDEPENDENT_CODE=ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libelf",
    "content": "[manifest]\nname = libelf\n\n[rpms]\nelfutils-libelf-devel-static\n\n[debs]\nlibelf-dev\n\n[pps]\nlibelf\n\n[download]\nurl = https://sourceware.org/elfutils/ftp/0.193/elfutils-0.193.tar.bz2\nsha256 = 7857f44b624f4d8d421df851aaae7b1402cfe6bcdd2d8049f15fc07d3dde7635\n\n# libelf only makes sense on linux, so make it a NOP on other platforms\n[build.not(os=linux)]\nbuilder = nop\n\n[build.os=linux]\nbuilder = autoconf\nsubdir = elfutils-0.193\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libevent",
    "content": "[manifest]\nname = libevent\n\n[debs]\nlibevent-dev\n\n[homebrew]\nlibevent\n\n[rpms]\nlibevent-devel\n\n[pps]\nlibevent\n\n# Note that the CMakeLists.txt file is present only in\n# git repo and not in the release tarball, so take care\n# to use the github generated source tarball rather than\n# the explicitly uploaded source tarball\n[download]\nurl = https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz\nsha256 = 92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb\n\n[build]\nbuilder = cmake\nsubdir = libevent-2.1.12-stable\n\n[cmake.defines]\nEVENT__DISABLE_TESTS = ON\nEVENT__DISABLE_BENCHMARK = ON\nEVENT__DISABLE_SAMPLES = ON\nEVENT__DISABLE_REGRESS = ON\n\n[cmake.defines.shared_libs=on]\nEVENT__BUILD_SHARED_LIBRARIES = ON\n\n[cmake.defines.shared_libs=off]\nEVENT__LIBRARY_TYPE = STATIC\n\n[cmake.defines.os=windows]\nEVENT__LIBRARY_TYPE = STATIC\n\n[dependencies.not(any(os=linux, os=freebsd))]\nopenssl\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libevent-python",
    "content": "[manifest]\nname = libevent-python\n\n# NOTE: System packages (debs, rpms) removed because they don't include\n# LibeventConfig.cmake which is required by find_package(Libevent REQUIRED CONFIG).\n# Building from source ensures CMake config files are present.\n\n[homebrew]\nlibevent\n\n[pps]\nlibevent\n\n# Note that the CMakeLists.txt file is present only in\n# git repo and not in the release tarball, so take care\n# to use the github generated source tarball rather than\n# the explicitly uploaded source tarball\n[download]\nurl = https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz\nsha256 = 92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb\n\n[build]\nbuilder = cmake\nsubdir = libevent-2.1.12-stable\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines]\nEVENT__DISABLE_TESTS = ON\nEVENT__DISABLE_BENCHMARK = ON\nEVENT__DISABLE_SAMPLES = ON\nEVENT__DISABLE_REGRESS = ON\nCMAKE_POSITION_INDEPENDENT_CODE=ON\n\n[cmake.defines.shared_libs=on]\nEVENT__BUILD_SHARED_LIBRARIES = ON\n\n[cmake.defines.os=windows]\nEVENT__LIBRARY_TYPE = STATIC\n\n[dependencies.not(any(os=linux, os=freebsd))]\nopenssl\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libffi",
    "content": "[manifest]\nname = libffi\n\n[debs]\nlibffi-dev\n\n[homebrew]\nlibffi\n\n[rpms]\nlibffi-devel\nlibffi\n\n[pps]\nlibffi\n\n[download]\nurl = https://github.com/libffi/libffi/releases/download/v3.4.2/libffi-3.4.2.tar.gz\nsha256 = 540fb721619a6aba3bdeef7d940d8e9e0e6d2c193595bc243241b77ff9e93620\n\n[build]\nbuilder = autoconf\nsubdir = libffi-3.4.2\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libgit2",
    "content": "[manifest]\nname = libgit2\n\n[homebrew]\nlibgit2\n\n[rpms]\nlibgit2-devel\n\n[pps]\nlibgit2\n\n# Ubuntu 18.04 libgit2 has clash with libcurl4-openssl-dev as it depends on\n# libcurl4-gnutls-dev.  Should be ok from 20.04 again\n# There is a description at https://github.com/r-hub/sysreqsdb/issues/77\n[debs.not(all(distro=ubuntu,distro_vers=\"18.04\"))]\nlibgit2-dev\n\n[download]\nurl = https://github.com/libgit2/libgit2/archive/v0.28.1.tar.gz\nsha256 = 0ca11048795b0d6338f2e57717370208c2c97ad66c6d5eac0c97a8827d13936b\n\n[build]\nbuilder = cmake\nsubdir = libgit2-0.28.1\n\n[cmake.defines]\n# Could turn this on if we also wanted to add a manifest for libssh2\nUSE_SSH = OFF\nBUILD_CLAR = OFF\n# Have to build shared to work around annoying problems with cmake\n# mis-parsing the frameworks required to link this on macos :-/\nBUILD_SHARED_LIBS = ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libgpiod",
    "content": "[manifest]\nname = libgpiod\n\n[download]\nurl = https://cdn.kernel.org/pub/software/libs/libgpiod/libgpiod-1.6.tar.xz\nsha256 = 62908023d59e8cbb9137ddd14deec50ced862d8f9b8749f288d3dbe7967151ef\n\n[build]\nbuilder = autoconf\nsubdir = libgpiod-1.6\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libiberty",
    "content": "[manifest]\nname = libiberty\n\n[rpms]\nbinutils-devel\nbinutils\n\n[debs.not(all(distro=ubuntu,distro_vers=\"24.04\"))]\nbinutils-dev\n\n[debs.all(distro=ubuntu,distro_vers=\"24.04\")]\nbinutils-x86-64-linux-gnu\n\n[download]\nurl = https://ftpmirror.gnu.org/gnu/binutils/binutils-2.43.tar.xz\nsha256 = b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365\n\n[dependencies]\nzlib\n\n[build]\nbuilder = autoconf\nsubdir = binutils-2.43/libiberty\npatchfile = libiberty_install_pic_lib.patch\n\n# only build the parts needed for demangling\n# as we still want to use system linker and assembler etc\n[autoconf.args]\n--enable-install-libiberty\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libiberty-python",
    "content": "[manifest]\nname = libiberty-python\n\n[rpms]\nbinutils-devel\nbinutils\n\n[debs.not(all(distro=ubuntu,distro_vers=\"24.04\"))]\nbinutils-dev\n\n[debs.all(distro=ubuntu,distro_vers=\"24.04\")]\nbinutils-x86-64-linux-gnu\n\n[download]\nurl = https://ftpmirror.gnu.org/gnu/binutils/binutils-2.43.tar.xz\nsha256 = b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365\n\n[dependencies]\nzlib-python\n\n[build]\nbuilder = autoconf\nsubdir = binutils-2.43/libiberty\npatchfile = libiberty_install_pic_lib.patch\n\n[build.not(os=linux)]\nbuilder = nop\n\n# only build the parts needed for demangling\n# as we still want to use system linker and assembler etc\n[autoconf.args]\n--enable-install-libiberty\nCFLAGS=-fPIC -O2\nPICFLAG=-fPIC\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libibverbs",
    "content": "[manifest]\nname = libibverbs\n\n[debs]\nlibibverbs-dev\nrdma-core\n\n[rpms]\nlibibverbs\nrdma-core-devel\n\n[download]\nurl = https://github.com/linux-rdma/rdma-core/releases/download/v60.0/rdma-core-60.0.tar.gz\nsha256 = 9b1b892e4eaaaa5dfbade07a290fbf5079e39117724fa1ef80d0ad78839328de\n\n[build]\nbuilder = cmake\nsubdir = rdma-core-60.0\n\n[dependencies]\nlibnl\n\n[cmake.defines]\nNO_MAN_PAGES=1\nNO_PYVERBS=1\nENABLE_RESOLVE_NEIGH=0\n# Use absolute short path for runtime dir to avoid Unix socket path length limit (108 chars)\nCMAKE_INSTALL_RUNDIR=/tmp/ibacm\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libmnl",
    "content": "[manifest]\nname = libmnl\n\n[rpms]\nlibmnl-devel\n\n# all centos 8 distros are missing this,\n# but its in fedora so may be back in a later version\n[rpms.not(all(any(distro=centos_stream,distro=centos),distro_vers=8))]\nlibmnl-static\n\n[debs]\nlibmnl-dev\n\n[pps]\nlibmnl\n\n[download]\nurl = https://www.netfilter.org/pub/libmnl/libmnl-1.0.4.tar.bz2\nsha256 = 171f89699f286a5854b72b91d06e8f8e3683064c5901fb09d954a9ab6f551f81\n\n[build.os=linux]\nbuilder = autoconf\nsubdir = libmnl-1.0.4\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libnl",
    "content": "[manifest]\nname = libnl\n\n[rpms]\nlibnl3-devel\nlibnl3\n\n[debs]\nlibnl-3-dev\nlibnl-route-3-dev\n\n[pps]\nlibnl\n\n[download]\nurl = https://github.com/thom311/libnl/releases/download/libnl3_2_25/libnl-3.2.25.tar.gz\nsha256 = 8beb7590674957b931de6b7f81c530b85dc7c1ad8fbda015398bc1e8d1ce8ec5\n\n[build.os=linux]\nbuilder = autoconf\nsubdir = libnl-3.2.25\n"
  },
  {
    "path": "build/fbcode_builder/manifests/liboqs",
    "content": "[manifest]\nname = liboqs\n\n[download]\nurl = https://github.com/open-quantum-safe/liboqs/archive/refs/tags/0.12.0.tar.gz\nsha256 = df999915204eb1eba311d89e83d1edd3a514d5a07374745d6a9e5b2dd0d59c08\n\n[build]\nbuilder = cmake\nsubdir = liboqs-0.12.0\n\n[cmake.defines]\nOQS_MINIMAL_BUILD = KEM_kyber_512;KEM_kyber_768;KEM_kyber_1024;KEM_ml_kem_512;KEM_ml_kem_768;KEM_ml_kem_1024\n\n[dependencies]\nopenssl\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libsai",
    "content": "[manifest]\nname = libsai\n\n[download]\nurl = https://github.com/opencomputeproject/SAI/archive/v1.16.3.tar.gz\nsha256 = 5c89cdb6b2e4f1b42ced6b78d43d06d22434ddbf423cdc551f7c2001f12e63d9\n\n[build]\nbuilder = nop\nsubdir = SAI-1.16.3\n\n[install.files]\ninc = include\nexperimental = experimental\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libsodium",
    "content": "[manifest]\nname = libsodium\n\n[debs]\nlibsodium-dev\n\n[homebrew]\nlibsodium\n\n[rpms]\nlibsodium-devel\nlibsodium-static\n\n[pps]\nlibsodium\n\n[download.not(os=windows)]\nurl = https://github.com/jedisct1/libsodium/releases/download/1.0.20-RELEASE/libsodium-1.0.20.tar.gz\nsha256 = ebb65ef6ca439333c2bb41a0c1990587288da07f6c7fd07cb3a18cc18d30ce19\n\n[build.not(os=windows)]\nbuilder = autoconf\nsubdir = libsodium-1.0.20\n\n[download.os=windows]\nurl = https://github.com/jedisct1/libsodium/releases/download/1.0.20-RELEASE/libsodium-1.0.20-msvc.zip\nsha256 = 2ff97f9e3f5b341bdc808e698057bea1ae454f99e29ff6f9b62e14d0eb1b1baa\n\n[build.os=windows]\nbuilder = nop\n\n[install.files.os=windows]\nlibsodium/x64/Release/v143/dynamic/libsodium.dll = bin/libsodium.dll\nlibsodium/x64/Release/v143/dynamic/libsodium.lib = lib/libsodium.lib\nlibsodium/x64/Release/v143/dynamic/libsodium.exp = lib/libsodium.exp\nlibsodium/x64/Release/v143/dynamic/libsodium.pdb = lib/libsodium.pdb\nlibsodium/include = include\n\n[autoconf.args]\n--with-pic\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libunwind",
    "content": "[manifest]\nname = libunwind\n\n[rpms]\nlibunwind-devel\nlibunwind\n\n# on ubuntu this brings in liblzma-dev, which in turn breaks watchman tests\n[debs.not(distro=ubuntu)]\nlibunwind-dev\n\n# The current libunwind v1.8.1 release has compiler issues with aarch64 (https://github.com/libunwind/libunwind/issues/702).\n# This more recent libunwind version (based on the latest commit, not a release version) got it fixed.\n[download]\nurl = https://github.com/libunwind/libunwind/archive/f081cf42917bdd5c428b77850b473f31f81767cf.tar.gz\nsha256 = 4ff5c335c02d225491d6c885db827fb5fa505fee4e68b4d7e866efc0087e7264\n\n[build]\nbuilder = autoconf\nsubdir = libunwind-f081cf42917bdd5c428b77850b473f31f81767cf\n\n[autoconf.args]\n--with-pic\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libusb",
    "content": "[manifest]\nname = libusb\n\n[debs]\nlibusb-1.0-0-dev\n\n[homebrew]\nlibusb\n\n[rpms]\nlibusb-devel\nlibusb\n\n[pps]\nlibusb\n\n[download]\nurl = https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2\nsha256 = 75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157\n\n[build.os=linux]\nbuilder = autoconf\nsubdir = libusb-1.0.22\n\n[autoconf.args]\n# fboss (which added the libusb dep) doesn't need udev so it is disabled here.\n# if someone in the future wants to add udev for something else, it won't hurt\n# fboss.\n--disable-udev\n"
  },
  {
    "path": "build/fbcode_builder/manifests/libyaml",
    "content": "[manifest]\nname = libyaml\n\n[download]\nurl = https://pyyaml.org/download/libyaml/yaml-0.1.7.tar.gz\nsha256 = 8088e457264a98ba451a90b8661fcb4f9d6f478f7265d48322a196cec2480729\n\n[build.os=linux]\nbuilder = autoconf\nsubdir = yaml-0.1.7\n\n[build.not(os=linux)]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/llvm",
    "content": "[manifest]\nname = llvm\n\n[rpms]\nllvm15-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/lmdb",
    "content": "[manifest]\nname = lmdb\n\n[build]\nbuilder = make\nsubdir = lmdb-LMDB_0.9.31/libraries/liblmdb\n\n[download]\nurl = https://github.com/LMDB/lmdb/archive/refs/tags/LMDB_0.9.31.tar.gz\nsha256 = dd70a8c67807b3b8532b3e987b0a4e998962ecc28643e1af5ec77696b081c9b0\n\n[make.build_args]\nBUILD_STATIC_ONLY=y\n\n[make.install_args]\ninstall\nBUILD_STATIC_ONLY=y\n"
  },
  {
    "path": "build/fbcode_builder/manifests/lz4",
    "content": "[manifest]\nname = lz4\n\n[homebrew]\nlz4\n\n[rpms]\nlz4-devel\n# centos 8 and centos_stream 9 are missing this rpm\n[rpms.not(any(all(distro=centos,distro_vers=8),all(distro=centos_stream,distro_vers=9)))]\nlz4-static\n\n[debs]\nliblz4-dev\n\n[pps]\nlz4\n\n[download]\nurl = https://github.com/lz4/lz4/releases/download/v1.10.0/lz4-1.10.0.tar.gz\nsha256 = 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b\n\n[build]\nbuilder = cmake\nsubdir = lz4-1.10.0/build/cmake\n"
  },
  {
    "path": "build/fbcode_builder/manifests/lz4-python",
    "content": "[manifest]\nname = lz4-python\n\n[homebrew]\nlz4\n\n[rpms]\nlz4-devel\n# centos 8 and centos_stream 9 are missing this rpm\n[rpms.not(any(all(distro=centos,distro_vers=8),all(distro=centos_stream,distro_vers=9)))]\nlz4-static\n\n[debs]\nliblz4-dev\n\n[pps]\nlz4\n\n[download]\nurl = https://github.com/lz4/lz4/releases/download/v1.10.0/lz4-1.10.0.tar.gz\nsha256 = 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b\n\n[build]\nbuilder = cmake\nsubdir = lz4-1.10.0/build/cmake\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines]\nCMAKE_POSITION_INDEPENDENT_CODE=ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/magic_enum",
    "content": "[manifest]\nname = magic_enum\n\n[download]\nurl = https://github.com/Neargye/magic_enum/releases/download/v0.9.7/magic_enum-v0.9.7.tar.gz\nsha256 = c047bc7ca0b76752168140e7ae9a4a30d72bf6530c196fdfbf5105a39d40cc46\n\n[build]\nbuilder = cmake\n\n[cmake.defines]\nMAGIC_ENUM_OPT_BUILD_EXAMPLES = OFF\nMAGIC_ENUM_OPT_BUILD_TESTS = OFF\nMAGIC_ENUM_OPT_INSTALL = ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/mcrouter",
    "content": "[manifest]\nname = mcrouter\nfbsource_path = fbcode/mcrouter\nshipit_project = mcrouter\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/mcrouter.git\n\n[dependencies]\nfolly\nwangle\nfizz\nfbthrift\nmvfst\nragel\ngflags\nglog\nboost\nlibevent\nopenssl\nzlib\ndouble-conversion\n\n[shipit.pathmap]\nfbcode/mcrouter/public_tld = .\nfbcode/mcrouter = mcrouter\n\n[shipit.strip]\n^fbcode/mcrouter/(.*/)?facebook/\n\n[build]\nbuilder = cmake\n\n[cmake.defines]\nBUILD_SHARED_LIBS=OFF\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/mononoke",
    "content": "[manifest]\nname = mononoke\nfbsource_path = fbcode/eden\nshipit_project = eden\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/sapling.git\n\n[build.not(os=windows)]\nbuilder = cargo\n\n[build.os=windows]\n# building Mononoke on windows is not supported\nbuilder = nop\n\n[cargo]\nbuild_doc = true\nworkspace_dir = eden/mononoke\n\n[github.actions]\nrust_version = 1.91\nbuild_type = MinSizeRel\n\n[shipit.pathmap]\nfbcode/configerator/structs/scm/hg = configerator/structs/scm/hg\nfbcode/configerator/structs/scm/hg/public_autocargo = configerator/structs/scm/hg\nfbcode/configerator/structs/scm/mononoke/public_autocargo = configerator/structs/scm/mononoke\nfbcode/configerator/structs/scm/mononoke = configerator/structs/scm/mononoke\nfbcode/eden/oss = .\nfbcode/eden = eden\nfbcode/eden/fs/public_autocargo = eden/fs\nfbcode/eden/mononoke/public_autocargo = eden/mononoke\nfbcode/eden/scm/public_autocargo = eden/scm\nfbcode/tools/lfs = tools/lfs\ntools/rust/ossconfigs = .\n\n[shipit.strip]\n^fbcode/configerator/structs/scm/hg(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/configerator/structs/scm/mononoke(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/eden/fs(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/eden/scm/lib/third-party/rust/.*/Cargo\\.toml$\n^fbcode/eden/mononoke(?!/public_autocargo).*/Cargo\\.toml$\n# strip other scm code  unrelated to mononoke to prevent triggering unnecessary checks\n^fbcode/eden(?!/mononoke|/scm/(lib|public_autocargo))/.*$\n^.*/facebook/.*$\n^.*/fb/.*$\n\n[dependencies]\nfb303\nfbthrift\nrust-shed\n\n[dependencies.fb=on]\nrust\n"
  },
  {
    "path": "build/fbcode_builder/manifests/mononoke_integration",
    "content": "[manifest]\nname = mononoke_integration\nfbsource_path = fbcode/eden\nshipit_project = eden\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/sapling.git\n\n[build.not(os=windows)]\nbuilder = make\nsubdir = eden/mononoke/tests/integration\n\n[build.os=windows]\n# building Mononoke on windows is not supported\nbuilder = nop\n\n[make.build_args]\nbuild-getdeps\n\n[make.install_args]\ninstall-getdeps\n\n[make.test_args]\ntest-getdeps\n\n[shipit.pathmap]\nfbcode/eden/mononoke/tests/integration = eden/mononoke/tests/integration\n\n[shipit.strip]\n^.*/facebook/.*$\n^.*/fb/.*$\n\n[dependencies]\ngit-lfs\njq\nmononoke\nnmap\npython\npython-click\nripgrep\nsapling\ntree\nzstd\n\n[dependencies.os=linux]\nsqlite3\n"
  },
  {
    "path": "build/fbcode_builder/manifests/moxygen",
    "content": "[manifest]\nname = moxygen\nfbsource_path = fbcode/moxygen\nshipit_project = moxygen\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebookexperimental/moxygen.git\n\n[build.os=windows]\nbuilder = nop\n\n[build]\nbuilder = cmake\nsubdir = .\njob_weight_mib = 3072\nrewrite_includes = true\n\n[cmake.defines.test=on]\nBUILD_TESTS = ON\n\n[cmake.defines.test=off]\nBUILD_TESTS = OFF\n\n[dependencies]\nfolly\nfizz\nwangle\nmvfst\nproxygen\n\n[dependencies.test=on]\ngoogletest\n\n[shipit.pathmap]\nfbcode/ti/experimental/moxygen/project_root = .\nfbcode/ti/experimental/moxygen = moxygen\n"
  },
  {
    "path": "build/fbcode_builder/manifests/mvfst",
    "content": "[manifest]\nname = mvfst\nfbsource_path = fbcode/quic\nshipit_project = mvfst\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/mvfst.git\n\n[build]\nbuilder = cmake\nsubdir = .\n\n[cmake.defines.test=on]\nBUILD_TESTS = ON\n\n[cmake.defines.all(os=windows, test=on)]\nBUILD_TESTS = OFF\n\n[cmake.defines.test=off]\nBUILD_TESTS = OFF\n\n[dependencies]\nfolly\nfizz\n\n[dependencies.all(test=on, not(os=windows))]\ngoogletest\n\n[shipit.pathmap]\nfbcode/quic/public_root = .\nfbcode/quic = quic\n"
  },
  {
    "path": "build/fbcode_builder/manifests/mvfst-python",
    "content": "[manifest]\nname = mvfst-python\nfbsource_path = fbcode/quic\nshipit_project = mvfst\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/mvfst.git\n\n[build]\nbuilder = cmake\nsubdir = .\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines.test=on]\nBUILD_TESTS = ON\n\n[cmake.defines.all(os=windows, test=on)]\nBUILD_TESTS = OFF\n\n[cmake.defines.test=off]\nBUILD_TESTS = OFF\n\n[cmake.defines.os=linux]\nCMAKE_POSITION_INDEPENDENT_CODE = ON\nBUILD_SHARED_LIBS = ON\n\n[dependencies]\nfolly-python\nfizz-python\n\n[dependencies.all(test=on, not(os=windows))]\ngoogletest\n\n[shipit.pathmap]\nfbcode/quic/public_root = .\nfbcode/quic = quic\n"
  },
  {
    "path": "build/fbcode_builder/manifests/ncurses",
    "content": "[manifest]\nname = ncurses\n\n[debs]\nlibncurses-dev\n\n[homebrew]\nncurses\n\n[rpms]\nncurses-devel\n\n[download]\nurl = https://ftpmirror.gnu.org/gnu/ncurses/ncurses-6.3.tar.gz\nsha256 = 97fc51ac2b085d4cde31ef4d2c3122c21abc217e9090a43a30fc5ec21684e059\n\n[build.not(os=windows)]\nbuilder = autoconf\nsubdir = ncurses-6.3\n\n[autoconf.args]\n--without-cxx-binding\n--without-ada\n\n[autoconf.args.os=linux]\n--enable-shared\n--with-shared\n\n[build.os=windows]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/nghttp2",
    "content": "[manifest]\nname = nghttp2\n\n[rpms]\nlibnghttp2-devel\nlibnghttp2\n\n[debs]\nlibnghttp2-dev\n\n[pps]\nlibnghttp2\n\n[download]\nurl = https://github.com/nghttp2/nghttp2/releases/download/v1.47.0/nghttp2-1.47.0.tar.gz\nsha256 = 62f50f0e9fc479e48b34e1526df8dd2e94136de4c426b7680048181606832b7c\n\n[build]\nbuilder = autoconf\nsubdir = nghttp2-1.47.0\n\n[autoconf.args]\n--enable-lib-only\n--disable-dependency-tracking\n"
  },
  {
    "path": "build/fbcode_builder/manifests/ninja",
    "content": "[manifest]\nname = ninja\n\n[debs]\nninja-build\n\n[homebrew]\nninja\n\n[rpms]\nninja-build\n\n[pps]\nninja\n\n[download.os=windows]\nurl = https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-win.zip\nsha256 = f550fec705b6d6ff58f2db3c374c2277a37691678d6aba463adcbb129108467a\n\n[build.os=windows]\nbuilder = nop\n\n[install.files.os=windows]\nninja.exe = bin/ninja.exe\n\n[download.not(os=windows)]\nurl = https://github.com/ninja-build/ninja/archive/v1.12.1.tar.gz\nsha256 = 821bdff48a3f683bc4bb3b6f0b5fe7b2d647cf65d52aeb63328c91a6c6df285a\n\n[build.not(os=windows)]\nbuilder = ninja_bootstrap\nsubdir = ninja-1.12.1"
  },
  {
    "path": "build/fbcode_builder/manifests/nlohmann-json",
    "content": "[manifest]\nname = nlohmann-json\n\n[download]\nurl = https://github.com/nlohmann/json/archive/refs/tags/v3.10.5.tar.gz\nsha256 = 5daca6ca216495edf89d167f808d1d03c4a4d929cef7da5e10f135ae1540c7e4\n\n[dependencies]\n\n[build]\nbuilder = cmake\nsubdir = json-3.10.5\n"
  },
  {
    "path": "build/fbcode_builder/manifests/nmap",
    "content": "[manifest]\nname = nmap\n\n[rpms]\nnmap\nnmap-ncat\n\n[debs]\nnmap\n\n# 18.04 combines ncat into the nmap package, newer need the separate one\n[debs.not(all(distro=ubuntu,distro_vers=\"18.04\"))]\nncat\n\n[download.not(os=windows)]\nurl = https://api.github.com/repos/nmap/nmap/tarball/ef8213a36c2e89233c806753a57b5cd473605408\nsha256 = eda39e5a8ef4964fac7db16abf91cc11ff568eac0fa2d680b0bfa33b0ed71f4a\n\n[build.not(os=windows)]\nbuilder = autoconf\nsubdir = nmap-nmap-ef8213a\nbuild_in_src_dir = true\n\n[build.os=windows]\nbuilder = nop\n\n[autoconf.args]\n# Without this option the build was filing to find some third party libraries\n# that we don't need\nenable_rdma=no\n"
  },
  {
    "path": "build/fbcode_builder/manifests/numa",
    "content": "[manifest]\nname = numa\n\n[download]\nurl = https://github.com/numactl/numactl/releases/download/v2.0.19/numactl-2.0.19.tar.gz\nsha256 = f2672a0381cb59196e9c246bf8bcc43d5568bc457700a697f1a1df762b9af884\n\n[build]\nbuilder = autoconf\nsubdir = numactl-2.0.19\n\n[rpms.distro=centos_stream]\nnumactl-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/openr",
    "content": "[manifest]\nname = openr\nfbsource_path = facebook/openr\nshipit_project = openr\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/openr.git\n\n[build.os=linux]\nbuilder = cmake\n# openr files take a lot of RAM to compile.\njob_weight_mib = 3072\n\n[build.not(os=linux)]\n# boost.fiber is required and that is not available on macos.\nbuilder = nop\n\n[dependencies]\nboost\nfb303\nfbthrift\nfolly\ngoogletest\nre2\nrange-v3\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\nADD_ROOT_TESTS=OFF\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\n\n\n[shipit.pathmap]\nfbcode/openr = openr\nfbcode/openr/public_tld = .\n"
  },
  {
    "path": "build/fbcode_builder/manifests/openssl",
    "content": "[manifest]\nname = openssl\n\n[debs]\nlibssl-dev\n\n[homebrew]\nopenssl\n# on homebrew need the matching curl and ca-\n\n[rpms]\nopenssl\nopenssl-devel\nopenssl-libs\n\n[pps]\nopenssl\n\n# no need to download on the systems where we always use the system libs\n[download.not(any(os=linux, os=freebsd))]\n# match the openssl version packages in ubuntu LTS folly current supports\nurl = https://www.openssl.org/source/openssl-3.0.15.tar.gz\nsha256 = 23c666d0edf20f14249b3d8f0368acaee9ab585b09e1de82107c66e1f3ec9533\n\n# We use the system openssl on these platforms even without --allow-system-packages\n[build.any(os=linux, os=freebsd)]\nbuilder = nop\n\n[build.not(any(os=linux, os=freebsd))]\nbuilder = openssl\nsubdir = openssl-3.0.15\n\n[dependencies.os=windows]\njom\nperl\n"
  },
  {
    "path": "build/fbcode_builder/manifests/osxfuse",
    "content": "[manifest]\nname = osxfuse\n\n[download]\nurl = https://github.com/osxfuse/osxfuse/archive/osxfuse-3.8.3.tar.gz\nsha256 = 93bab6731bdfe8dc1ef069483437270ce7fe5a370f933d40d8d0ef09ba846c0c\n\n[build]\nbuilder = nop\n\n[install.files]\nosxfuse-osxfuse-3.8.3/common = include\n"
  },
  {
    "path": "build/fbcode_builder/manifests/patchelf",
    "content": "[manifest]\nname = patchelf\n\n[rpms]\npatchelf\n\n[debs]\npatchelf\n\n[pps]\npatchelf\n\n[download]\nurl = https://github.com/NixOS/patchelf/archive/0.10.tar.gz\nsha256 = b3cb6bdedcef5607ce34a350cf0b182eb979f8f7bc31eae55a93a70a3f020d13\n\n[build]\nbuilder = autoconf\nsubdir = patchelf-0.10\n\n"
  },
  {
    "path": "build/fbcode_builder/manifests/pcre2",
    "content": "[manifest]\nname = pcre2\n\n[homebrew]\npcre2\n\n[rpms]\npcre2-devel\npcre-static\n\n[debs]\nlibpcre2-dev\n\n[download]\nurl = https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.40/pcre2-10.40.tar.bz2\nsha256 = 14e4b83c4783933dc17e964318e6324f7cae1bc75d8f3c79bc6969f00c159d68\n\n[build]\nbuilder = cmake\nsubdir = pcre2-10.40\n"
  },
  {
    "path": "build/fbcode_builder/manifests/perl",
    "content": "[manifest]\nname = perl\n\n[download.os=windows]\nurl = https://strawberryperl.com/download/5.28.1.1/strawberry-perl-5.28.1.1-64bit-portable.zip\nsha256 = 935c95ba096fa11c4e1b5188732e3832d330a2a79e9882ab7ba8460ddbca810d\n\n[build.os=windows]\nbuilder = nop\nsubdir = perl\n"
  },
  {
    "path": "build/fbcode_builder/manifests/pexpect",
    "content": "[manifest]\nname = pexpect\n\n[download]\nurl = https://files.pythonhosted.org/packages/0e/3e/377007e3f36ec42f1b84ec322ee12141a9e10d808312e5738f52f80a232c/pexpect-4.7.0-py2.py3-none-any.whl\nsha256 = 2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1\n\n[build]\nbuilder = python-wheel\n\n[dependencies]\npython-ptyprocess\n"
  },
  {
    "path": "build/fbcode_builder/manifests/proxygen",
    "content": "[manifest]\nname = proxygen\nfbsource_path = fbcode/proxygen\nshipit_project = proxygen\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/proxygen.git\n\n[build.os=windows]\nbuilder = nop\n\n[build]\nbuilder = cmake\nsubdir = .\njob_weight_mib = 3072\n\n[cmake.defines.test=on]\nBUILD_TESTS = ON\n\n[cmake.defines.test=off]\nBUILD_TESTS = OFF\n\n[dependencies]\nzlib\ngperf\nfolly\nfizz\nwangle\nmvfst\nc-ares\n\n[dependencies.test=on]\ngoogletest\n\n[shipit.pathmap]\nfbcode/proxygen/public_tld = .\nfbcode/proxygen = proxygen\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python",
    "content": "[manifest]\nname = python\n\n[homebrew]\npython@3.10\n\n# sapling needs match statements with arrive in python 3.12 in centos 10\n[rpms.not(all(distro=centos_stream,distro_vers=9))]\npython3\npython3-devel\n\n# Centos Stream 9 default python is 3.9, sapling needs 3.10+\n[rpms.all(distro=centos_stream,distro_vers=9)]\npython3.12\npython3.12-devel\n\n# sapling needs match statements with arrive in python 3.10 in ubuntu 22.04\n[debs.not(all(distro=ubuntu,any(distro_vers=\"18.04\",distro_vers=\"20.04\")))]\npython3-all-dev\n\n[pps]\npython3\n\n[download]\nurl = https://www.python.org/ftp/python/3.10.19/Python-3.10.19.tgz\nsha256 = a078fb2d7a216071ebbe2e34b5f5355dd6b6e9b0cd1bacc4a41c63990c5a0eec\n\n[build]\nbuilder = autoconf\nsubdir = Python-3.10.19\n\n[autoconf.args]\n--enable-shared\n--with-ensurepip=install\n\n# python's pkg-config libffi detection is broken\n# See https://bugs.python.org/issue34823 for clearest description\n# and pending PR https://github.com/python/cpython/pull/20451\n# The documented workaround requires an environment variable derived from\n# pkg-config to be passed into its configure step\n[autoconf.envcmd.LDFLAGS]\npkg-config\n--libs-only-L\nlibffi\n\n[dependencies]\nlibffi\n# eden tests expect the python bz2 support\nbz2\n# eden tests expect the python curses support\nncurses\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-3_14",
    "content": "# This is primarily to support CinderX's CI, so it's not heavily configured.\n\n[manifest]\nname = python-3_14\n\n[download]\nurl = https://github.com/python/cpython/archive/refs/tags/v3.14.3.tar.gz\nsha256 = f229a232052ae318d2fc8eb0aca4a02d631e7e1a8790ef1f9b65e1632743a469\n\n[build]\nbuilder = autoconf\nsubdir = cpython-3.14.3\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-click",
    "content": "[manifest]\nname = python-click\n\n[download]\nurl = https://files.pythonhosted.org/packages/d2/3d/fa76db83bf75c4f8d338c2fd15c8d33fdd7ad23a9b5e57eb6c5de26b430e/click-7.1.2-py2.py3-none-any.whl\nsha256 = dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc\n\n[build]\nbuilder = python-wheel\n\n[rpms]\npython3-click\n\n[debs]\npython3-click\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-filelock",
    "content": "[manifest]\nname = python-filelock\n\n[download]\nurl = https://files.pythonhosted.org/packages/31/24/ee722b92f23b9ebd87783e893a75352c048bbbc1f67dce0d63b58b46cb48/filelock-3.3.2-py3-none-any.whl\nsha256 = bb2a1c717df74c48a2d00ed625e5a66f8572a3a30baacb7657add1d7bac4097b\n\n[build]\nbuilder = python-wheel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-main",
    "content": "# This is primarily to support CinderX's CI, so it's not heavily configured.\n\n[manifest]\nname = python-main\nfbsource_path = third-party/python/main/pristine\n# We don't actually have a shipit project for python-main, but we use getdeps\n# built-in shipit implementation which just needs a shipit.pathmap.\nshipit_project = dummy-name\n\n\n[git]\nrepo_url = https://github.com/python/cpython.git\n\n[shipit.pathmap]\nthird-party/python/main/pristine = .\n\n[build]\nbuilder = autoconf\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-psutil",
    "content": "[manifest]\nname = python-psutil\n\n[download]\nurl = https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl\nsha256 = 4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34\n\n[build]\nbuilder = python-wheel\n\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-ptyprocess",
    "content": "[manifest]\nname = python-ptyprocess\n\n[download]\nurl = https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl\nsha256 = d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f\n\n[build]\nbuilder = python-wheel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-pyyaml",
    "content": "[manifest]\nname = python-pyyaml\n\n[download]\nurl = https://files.pythonhosted.org/packages/25/a2/b725b61ac76a75583ae7104b3209f75ea44b13cfd026aa535ece22b7f22e/PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl\nsha256 = 22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6\n\n[build]\nbuilder = python-wheel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-setuptools",
    "content": "[manifest]\nname = python-setuptools\n\n[download]\nurl = https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl\nsha256 = 062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922\n\n[build]\nbuilder = python-wheel\n\n[rpms]\npython3-setuptools\n\n# Centos Stream 9 default python is 3.9, sapling needs 3.10+\n[rpms.all(distro=centos_stream,distro_vers=9)]\npython3.12-setuptools\n\n[homebrew]\npython-setuptools\n\n[debs]\npython3-setuptools\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-setuptools-69",
    "content": "[manifest]\nname = python-setuptools-69\n\n[download]\nurl = https://files.pythonhosted.org/packages/c0/7a/3da654f49c95d0cc6e9549a855b5818e66a917e852ec608e77550c8dc08b/setuptools-69.1.1-py3-none-any.whl\nsha256 = 02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56\n\n[build]\nbuilder = python-wheel\n\n[rpms]\npython3-setuptools\n\n[homebrew]\npython-setuptools\n\n[debs]\npython3-setuptools\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-six",
    "content": "[manifest]\nname = python-six\n\n[download]\nurl = https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl\nsha256 = 3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c\n\n[build]\nbuilder = python-wheel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/python-toml",
    "content": "[manifest]\nname = python-toml\n\n[download]\nurl = https://files.pythonhosted.org/packages/a2/12/ced7105d2de62fa7c8fb5fce92cc4ce66b57c95fb875e9318dba7f8c5db0/toml-0.10.0-py2.py3-none-any.whl\nsha256 = 235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e\n\n[build]\nbuilder = python-wheel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/ragel",
    "content": "[manifest]\nname = ragel\n\n[debs]\nragel\n\n[homebrew]\nragel\n\n[rpms]\nragel\n\n[download]\nurl = https://www.colm.net/files/ragel/ragel-6.10.tar.gz\nsha256 = 5f156edb65d20b856d638dd9ee2dfb43285914d9aa2b6ec779dac0270cd56c3f\n\n[build]\nbuilder = autoconf\nsubdir = ragel-6.10\n"
  },
  {
    "path": "build/fbcode_builder/manifests/range-v3",
    "content": "[manifest]\nname = range-v3\n\n[download]\nurl = https://github.com/ericniebler/range-v3/archive/refs/tags/0.11.0.tar.gz\nsha256 = 376376615dbba43d3bef75aa590931431ecb49eb36d07bb726a19f680c75e20c \n\n\n[build]\nbuilder = cmake\nsubdir = range-v3-0.11.0\n\n[cmake.defines]\nRANGE_V3_EXAMPLES=OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/rdma-core",
    "content": "[manifest]\nname = rdma-core\n\n[debs]\nrdma-core\n\n[rpms]\nrdma-core-devel\n"
  },
  {
    "path": "build/fbcode_builder/manifests/re2",
    "content": "[manifest]\nname = re2\n\n[homebrew]\nre2\n\n[debs]\nlibre2-dev\n\n[rpms]\nre2\nre2-devel\n\n[pps]\nre2\n\n[download]\nurl = https://github.com/google/re2/archive/2020-11-01.tar.gz\nsha256 = 8903cc66c9d34c72e2bc91722288ebc7e3ec37787ecfef44d204b2d6281954d7\n\n[build]\nbuilder = cmake\nsubdir = re2-2020-11-01\n"
  },
  {
    "path": "build/fbcode_builder/manifests/rebalancer",
    "content": "[manifest]\nname = rebalancer\nfbsource_path = fbcode/algopt/rebalancer/\nshipit_project = rebalancer\nshipit_fbcode_builder = true\nuse_shipit = true\n\n[git]\n# To git clone on devserver, setup fwdproxy:\n# https://www.internalfb.com/wiki/Open_Source/Maintain_a_FB_OSS_Project/Devserver_GitHub_Access/\nrepo_url = https://github.com/facebookincubator/rebalancer.git\nbranch = main\n\n[build]\nbuilder = cmake\n\n[dependencies]\nboost\nfbthrift\nfizz\nfmt\nfolly\ngflags\nglog\ngoogletest\nxxhash\n\n[dependencies.os=linux]\nclang19\n\n[cmake.defines.os=linux]\nCMAKE_C_COMPILER=clang-19\nCMAKE_CXX_COMPILER=clang++-19\n\n[cmake.defines.os=darwin]\nREBALANCER_USE_SCIP=0\n\n[shipit.strip]\n^.*/fb/.*$\n\n[shipit.pathmap]\nfbcode/algopt/rebalancer/oss_root = .\nfbcode/algopt/rebalancer = algopt/rebalancer\nfbcode/algopt/lp = algopt/lp\n\n[dependencies.all(distro=centos_stream,distro_vers=9)]\nclang19\n\n[cmake.defines.all(distro=centos_stream,distro_vers=9)]\nCMAKE_C_COMPILER=clang-19\nCMAKE_CXX_COMPILER=clang++-19\n"
  },
  {
    "path": "build/fbcode_builder/manifests/ripgrep",
    "content": "[manifest]\nname = ripgrep\n\n[rpms]\nripgrep\n\n[debs]\nripgrep\n\n[homebrew]\nripgrep\n\n# only used from system packages currently\n[build]\nbuilder = nop\n"
  },
  {
    "path": "build/fbcode_builder/manifests/rocksdb",
    "content": "[manifest]\nname = rocksdb\n\n[download]\nurl = https://github.com/facebook/rocksdb/archive/refs/tags/v8.7.3.zip\nsha256 = 36c06b61dc167f2455990d60dd88d734b73aa8c4dfc095243efd0243834c6cd3\n\n[dependencies]\nlz4\nsnappy\n\n[build]\nbuilder = cmake\nsubdir = rocksdb-8.7.3\n\n[cmake.defines]\nWITH_SNAPPY=ON\nWITH_LZ4=ON\nWITH_TESTS=OFF\nWITH_BENCHMARK_TOOLS=OFF\n# We get relocation errors with the static gflags lib,\n# and there's no clear way to make it pick the shared gflags\n# so just turn it off.\nWITH_GFLAGS=OFF\n# Disable the use of -Werror\nFAIL_ON_WARNINGS = OFF\n\n[cmake.defines.os=windows]\nROCKSDB_INSTALL_ON_WINDOWS=ON\n# RocksDB hard codes the paths to the snappy libs to something\n# that doesn't exist; ignoring the usual cmake rules.  As a result,\n# we can't build it with snappy without either patching rocksdb or\n# without introducing more complex logic to the build system to\n# connect the snappy build outputs to rocksdb's custom logic here.\n# Let's just turn it off on windows.\nWITH_SNAPPY=OFF\nWITH_LZ4=ON\nROCKSDB_SKIP_THIRDPARTY=ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/rust-shed",
    "content": "[manifest]\nname = rust-shed\nfbsource_path = fbcode/common/rust/shed\nshipit_project = rust-shed\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebookexperimental/rust-shed.git\n\n[build]\nbuilder = cargo\n\n[cargo]\nbuild_doc = true\nworkspace_dir =\n\n[shipit.pathmap]\nfbcode/common/rust/shed = shed\nfbcode/common/rust/shed/public_autocargo = shed\nfbcode/common/rust/shed/public_tld = .\ntools/rust/ossconfigs = .\n\n[shipit.strip]\n^fbcode/common/rust/shed/(?!public_autocargo|public_tld).+/Cargo\\.toml$\n\n[dependencies]\nfbthrift\nfb303\n\n# We use the system openssl on linux\n[dependencies.not(os=linux)]\nopenssl\n\n[dependencies.fbsource=on]\nrust\n"
  },
  {
    "path": "build/fbcode_builder/manifests/sapling",
    "content": "[manifest]\nname = sapling\nfbsource_path = fbcode/eden\nshipit_project = eden\nshipit_fbcode_builder = true\n\n[github.actions]\nrequired_locales = en_US.UTF-8\n\n[git]\nrepo_url = https://github.com/facebook/sapling.git\n\n[build.not(os=windows)]\nbuilder = make\nsubdir = eden/scm\n\n[build.os=windows]\n# For now the biggest blocker is missing \"make\" on windows, but there are bound\n# to be more\nbuilder = nop\n\n[make.build_args]\ngetdepsbuild\n\n[make.install_args]\ninstall-getdeps\n\n[make.test_args]\ntest-getdeps\n\n[shipit.pathmap]\nfbcode/configerator/structs/scm/hg = configerator/structs/scm/hg\nfbcode/configerator/structs/scm/hg/public_autocargo = configerator/structs/scm/hg\nfbcode/eden/oss = .\nfbcode/eden = eden\nfbcode/eden/fs/public_autocargo = eden/fs\nfbcode/eden/mononoke/public_autocargo = eden/mononoke\nfbcode/eden/scm/public_autocargo = eden/scm\nfbcode/tools/lfs = tools/lfs\n\n[shipit.strip]\n^fbcode/configerator/structs/scm/hg(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/eden/addons/.*$\n^fbcode/eden/fs/eden-config\\.h$\n^fbcode/eden/fs/py/eden/config\\.py$\n^fbcode/eden/hg-server/.*$\n^fbcode/eden/fs(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/eden/mononoke(?!/public_autocargo).*/Cargo\\.toml$\n^fbcode/eden/scm(?!/public_autocargo|/edenscmnative/bindings).*/Cargo\\.toml$\n^fbcode/eden/scm/build/.*$\n^fbcode/eden/website/.*$\n^fbcode/eden/.*/\\.cargo/.*$\n^.*/facebook/.*$\n^.*/fb/.*$\n/Cargo\\.lock$\n\\.pyc$\n\n[dependencies]\nfb303\nfbthrift\nrust-shed\n\n[dependencies.all(test=on,not(os=darwin))]\nhexdump\n\n[dependencies.not(os=windows)]\npython\npython-setuptools\n\n# We use the system openssl on linux\n[dependencies.not(os=linux)]\nopenssl\n\n[dependencies.fbsource=on]\nrust\n"
  },
  {
    "path": "build/fbcode_builder/manifests/snappy",
    "content": "[manifest]\nname = snappy\n\n[homebrew]\nsnappy\n\n[debs]\nlibsnappy-dev\n\n[rpms]\nsnappy-devel\n\n[pps]\nsnappy\n\n[download]\nurl = https://github.com/google/snappy/archive/1.1.7.tar.gz\nsha256 = 3dfa02e873ff51a11ee02b9ca391807f0c8ea0529a4924afa645fbf97163f9d4\n\n[build]\nbuilder = cmake\nsubdir = snappy-1.1.7\n\n[cmake.defines]\nSNAPPY_BUILD_TESTS = OFF\n\n# Avoid problems like `relocation R_X86_64_PC32 against symbol` on ELF systems\n# when linking rocksdb, which builds PIC even when building a static lib\n[cmake.defines.os=linux]\nBUILD_SHARED_LIBS = ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/sparsemap",
    "content": "[manifest]\nname = sparsemap\n\n[download]\nurl = https://github.com/Tessil/sparse-map/archive/refs/tags/v0.6.2.tar.gz\nsha256 = 7020c21e8752e59d72e37456cd80000e18671c803890a3e55ae36b295eba99f6\n\n[build]\nbuilder = cmake\nsubdir = sparse-map-0.6.2/\n"
  },
  {
    "path": "build/fbcode_builder/manifests/sqlite3",
    "content": "[manifest]\nname = sqlite3\n\n[debs]\nlibsqlite3-dev\nsqlite3\n\n[homebrew]\nsqlite\n\n[rpms]\nsqlite-devel\nsqlite-libs\nsqlite\n\n[pps]\nsqlite3\n\n[download]\nurl = https://sqlite.org/2019/sqlite-amalgamation-3280000.zip\nsha256 = d02fc4e95cfef672b45052e221617a050b7f2e20103661cda88387349a9b1327\n\n[dependencies]\ncmake\nninja\n\n[build]\nbuilder = sqlite\nsubdir = sqlite-amalgamation-3280000\n"
  },
  {
    "path": "build/fbcode_builder/manifests/systemd",
    "content": "[manifest]\nname = systemd\n\n[rpms]\nsystemd\nsystemd-devel\n\n\n[download]\nurl = https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz\nsha256 = 896d76ff65c88f5fd9e42f90d152b0579049158a163431dd77cdc57748b1d7b0\n\n[build.os=linux]\nbuilder = meson\nsubdir = systemd-256.7\n\n[meson.setup_args]\n-Dstatic-libsystemd=true\n-Dprefix=/\n"
  },
  {
    "path": "build/fbcode_builder/manifests/tabulate",
    "content": "[manifest]\nname = tabulate\n\n[download]\nurl = https://github.com/p-ranav/tabulate/archive/refs/tags/v1.5.tar.gz\nsha256 = 16b289f46306283544bb593f4601e80d6ea51248fde52e910cc569ef08eba3fb\n\n[build]\nbuilder = cmake\nsubdir = tabulate-1.5\n\n[cmake.defines]\ntabulate_BUILD_TESTS = OFF\ntabulate_BUILD_SAMPLES = OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/tree",
    "content": "[manifest]\nname = tree\n\n[debs]\ntree\n\n[homebrew]\ntree\n\n[rpms]\ntree\n\n[download.os=linux]\nurl = https://salsa.debian.org/debian/tree-packaging/-/archive/debian/1.8.0-1/tree-packaging-debian-1.8.0-1.tar.gz\nsha256 = a841eee1d52bfd64a48f54caab9937b9bd92935055c48885c4ab1ae4dab7fae5\n\n[download.os=darwin]\n# The official package of tree source requires users of non-Linux platform to\n# comment/uncomment certain lines in the Makefile to build for their platform.\n# Besauce getdeps.py doesn't have that functionality we just use this custom\n# fork of tree which has proper lines uncommented for a OSX build\nurl = https://github.com/lukaspiatkowski/tree-command/archive/debian/1.8.0-1-macos.tar.gz\nsha256 = 9cbe889553d95cf5a2791dd0743795d46a3c092c5bba691769c0e5c52e11229e\n\n[build.os=linux]\nbuilder = make\nsubdir = tree-packaging-debian-1.8.0-1\n\n[build.os=darwin]\nbuilder = make\nsubdir = tree-command-debian-1.8.0-1-macos\n\n[build.os=windows]\nbuilder = nop\n\n[make.install_args]\ninstall\n"
  },
  {
    "path": "build/fbcode_builder/manifests/wangle",
    "content": "[manifest]\nname = wangle\nfbsource_path = fbcode/wangle\nshipit_project = wangle\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/wangle.git\n\n[build]\nbuilder = cmake\nsubdir = wangle\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\n\n[dependencies]\nfolly\ngoogletest\nfizz\n\n[shipit.pathmap]\nfbcode/wangle/public_tld = .\nfbcode/wangle = wangle\n"
  },
  {
    "path": "build/fbcode_builder/manifests/wangle-python",
    "content": "[manifest]\nname = wangle-python\nfbsource_path = fbcode/wangle\nshipit_project = wangle\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/wangle.git\n\n[build]\nbuilder = cmake\nsubdir = wangle\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines.test=on]\nBUILD_TESTS=ON\n\n[cmake.defines.test=off]\nBUILD_TESTS=OFF\n\n[cmake.defines.os=linux]\nCMAKE_POSITION_INDEPENDENT_CODE = ON\nBUILD_SHARED_LIBS = ON\n\n[dependencies]\nfolly-python\ngoogletest\nfizz-python\n\n[shipit.pathmap]\nfbcode/wangle/public_tld = .\nfbcode/wangle = wangle\n"
  },
  {
    "path": "build/fbcode_builder/manifests/watchman",
    "content": "[manifest]\nname = watchman\nfbsource_path = fbcode/watchman\nshipit_project = watchman\nshipit_fbcode_builder = true\n\n[git]\nrepo_url = https://github.com/facebook/watchman.git\n\n[build]\nbuilder = cmake\n\n[dependencies]\nboost\ncpptoml\nedencommon\nfb303\nfbthrift\nfolly\npcre2\ngoogletest\npython-setuptools-69\n\n[dependencies.fbsource=on]\nrust\n\n[shipit.pathmap]\nfbcode/watchman = watchman\nfbcode/watchman/oss = .\nfbcode/eden/fs = eden/fs\n\n[shipit.strip]\n^fbcode/eden/fs/(?!.*\\.thrift|service/shipit_test_file\\.txt)\n\n[cmake.defines.fb=on]\nENABLE_EDEN_SUPPORT=ON\nIS_FB_BUILD=ON\n\n# FB macos specific settings\n[cmake.defines.all(fb=on,os=darwin)]\n# this path is coupled with the FB internal watchman-osx.spec\nWATCHMAN_STATE_DIR=/opt/facebook/watchman/var/run/watchman\n# tell cmake not to try to create /opt/facebook/...\nINSTALL_WATCHMAN_STATE_DIR=OFF\nUSE_SYS_PYTHON=OFF\n\n[depends.environment]\nWATCHMAN_VERSION_OVERRIDE\n"
  },
  {
    "path": "build/fbcode_builder/manifests/xxhash",
    "content": "[manifest]\nname = xxhash\n\n[download]\nurl = https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.2.tar.gz\nsha256 = baee0c6afd4f03165de7a4e67988d16f0f2b257b51d0e3cb91909302a26a79c4\n\n[rpms]\nxxhash-devel\n\n[debs]\nlibxxhash-dev\nxxhash\n\n[homebrew]\nxxhash\n\n[build]\nbuilder = cmake\nsubdir = xxHash-0.8.2/cmake_unofficial\n\n[cmake.defines]\nCMAKE_POSITION_INDEPENDENT_CODE = ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/xz",
    "content": "[manifest]\nname = xz\n\n# ubuntu's package causes watchman's tests to hang\n[debs.not(distro=ubuntu)]\nliblzma-dev\n\n[homebrew]\nxz\n\n[rpms]\nxz-devel\n\n[download]\nurl = https://tukaani.org/xz/xz-5.2.5.tar.gz\nsha256 = f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10\n\n[build]\nbuilder = autoconf\nsubdir = xz-5.2.5\n\n[autoconf.args]\n--with-pic\n"
  },
  {
    "path": "build/fbcode_builder/manifests/yaml-cpp",
    "content": "[manifest]\nname = yaml-cpp\n\n[download]\nurl = https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-0.6.2.tar.gz\nsha256 = e4d8560e163c3d875fd5d9e5542b5fd5bec810febdcba61481fe5fc4e6b1fd05\n\n[build.os=linux]\nbuilder = cmake\nsubdir = yaml-cpp-yaml-cpp-0.6.2\n\n[build.not(os=linux)]\nbuilder = nop\n\n[dependencies]\nboost\ngoogletest\n\n[cmake.defines]\nYAML_CPP_BUILD_TESTS=OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/zlib",
    "content": "[manifest]\nname = zlib\n\n[debs]\nzlib1g-dev\n\n[homebrew]\nzlib\n\n[rpms.not(distro=fedora)]\nzlib-devel\nzlib-static\n\n[rpms.distro=fedora]\nzlib-ng-compat-devel\nzlib-ng-compat-static\n\n[pps]\nzlib\n\n[download]\nurl = https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz\nsha256 = 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23\n\n[build]\nbuilder = cmake\nsubdir = zlib-1.3.1\npatchfile = zlib_dont_build_more_than_needed.patch\n"
  },
  {
    "path": "build/fbcode_builder/manifests/zlib-python",
    "content": "[manifest]\nname = zlib-python\n\n[debs]\nzlib1g-dev\n\n[homebrew]\nzlib\n\n[rpms.not(distro=fedora)]\nzlib-devel\nzlib-static\n\n[rpms.distro=fedora]\nzlib-ng-compat-devel\nzlib-ng-compat-static\n\n[pps]\nzlib\n\n[download]\nurl = https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz\nsha256 = 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23\n\n[build]\nbuilder = cmake\nsubdir = zlib-1.3.1\npatchfile = zlib_dont_build_more_than_needed.patch\n\n[build.not(os=linux)]\nbuilder = nop\n\n[cmake.defines]\nCMAKE_POSITION_INDEPENDENT_CODE=ON\n"
  },
  {
    "path": "build/fbcode_builder/manifests/zstd",
    "content": "[manifest]\nname = zstd\n\n[homebrew]\nzstd\n\n# 18.04 zstd is too old\n[debs.not(all(distro=ubuntu,distro_vers=\"18.04\"))]\nlibclang-dev\nlibzstd-dev\nzstd\n\n[rpms]\nlibzstd-devel\nlibzstd\n\n[pps]\nzstd\n\n[download]\nurl = https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-1.5.5.tar.gz\nsha256 = 9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4\n\n[build]\nbuilder = cmake\nsubdir = zstd-1.5.5/build/cmake\n\n# The zstd cmake build explicitly sets the install name\n# for the shared library in such a way that cmake discards\n# the path to the library from the install_name, rendering\n# the library non-resolvable during the build.  The short\n# term solution for this is just to link static on macos.\n#\n# And while we're at it, let's just always link statically.\n[cmake.defines]\nZSTD_BUILD_SHARED = OFF\n"
  },
  {
    "path": "build/fbcode_builder/manifests/zstd-python",
    "content": "[manifest]\nname = zstd-python\n\n[homebrew]\nzstd\n\n# 18.04 zstd is too old\n[debs.not(all(distro=ubuntu,distro_vers=\"18.04\"))]\nlibclang-dev\nlibzstd-dev\nzstd\n\n[rpms]\nlibzstd-devel\nlibzstd\n\n[pps]\nzstd\n\n[download]\nurl = https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-1.5.5.tar.gz\nsha256 = 9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4\n\n[build]\nbuilder = cmake\nsubdir = zstd-1.5.5/build/cmake\n\n[build.not(os=linux)]\nbuilder = nop\n\n# The zstd cmake build explicitly sets the install name\n# for the shared library in such a way that cmake discards\n# the path to the library from the install_name, rendering\n# the library non-resolvable during the build.  The short\n# term solution for this is just to link static on macos.\n#\n# And while we're at it, let's just always link statically.\n[cmake.defines]\nZSTD_BUILD_SHARED = OFF\nCMAKE_POSITION_INDEPENDENT_CODE=ON\n"
  },
  {
    "path": "build/fbcode_builder/patches/boost_1_83_0.patch",
    "content": "diff --git a/boost/serialization/strong_typedef.hpp b/boost/serialization/strong_typedef.hpp\n--- a/boost/serialization/strong_typedef.hpp\n+++ b/boost/serialization/strong_typedef.hpp\n@@ -44,6 +44,7 @@\n     operator const T&() const {return t;}                                                                        \\\n     operator T&() {return t;}                                                                                    \\\n     bool operator==(const D& rhs) const {return t == rhs.t;}                                                     \\\n+    bool operator==(const T& lhs) const {return t == lhs;}                                                       \\\n     bool operator<(const D& rhs) const {return t < rhs.t;}                                                       \\\n };\n \ndiff --git a/tools/build/src/tools/msvc.jam b/tools/build/src/tools/msvc.jam\n--- a/tools/build/src/tools/msvc.jam\n+++ b/tools/build/src/tools/msvc.jam\n@@ -1137,6 +1137,14 @@\n         }\n         else\n         {\n+            if [ MATCH \"(14.4)\" : $(version) ]\n+            {\n+                if $(.debug-configuration)\n+                {\n+                    ECHO \"notice: [generate-setup-cmd] $(version) is 14.4x\" ;\n+                }\n+                parent = [ path.native [ path.join  $(parent) \"..\\\\..\\\\..\\\\..\\\\..\\\\Auxiliary\\\\Build\" ] ] ;\n+            }\n             if [ MATCH \"(14.3)\" : $(version) ]\n             {\n                 if $(.debug-configuration)\n"
  },
  {
    "path": "build/fbcode_builder/patches/iproute2_oss.patch",
    "content": "diff --git a/bridge/fdb.c b/bridge/fdb.c\n--- a/bridge/fdb.c\n+++ b/bridge/fdb.c\n@@ -31,7 +31,7 @@\n\n static unsigned int filter_index, filter_vlan, filter_state;\n\n-json_writer_t *jw_global;\n+static json_writer_t *jw_global;\n\n static void usage(void)\n {\ndiff --git a/ip/ipmroute.c b/ip/ipmroute.c\n--- a/ip/ipmroute.c\n+++ b/ip/ipmroute.c\n@@ -44,7 +44,7 @@\n        exit(-1);\n }\n\n-struct rtfilter {\n+static struct rtfilter {\n        int tb;\n        int af;\n        int iif;\ndiff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c\n--- a/ip/xfrm_monitor.c\n+++ b/ip/xfrm_monitor.c\n@@ -34,7 +34,7 @@\n #include \"ip_common.h\"\n\n static void usage(void) __attribute__((noreturn));\n-int listen_all_nsid;\n+static int listen_all_nsid;\n\n static void usage(void)\n {\n"
  },
  {
    "path": "build/fbcode_builder/patches/libiberty_install_pic_lib.patch",
    "content": "diff --git a/Makefile.in b/Makefile.in\nindex b77a41c..cbe71fe 100644\n--- a/Makefile.in\n+++ b/Makefile.in\n@@ -389,7 +389,7 @@ MULTIOSDIR = `$(CC) $(CFLAGS) -print-multi-os-directory`\n install_to_libdir: all\n        if test -n \"${target_header_dir}\"; then \\\n                ${mkinstalldirs} $(DESTDIR)$(libdir)/$(MULTIOSDIR); \\\n-               $(INSTALL_DATA) $(TARGETLIB) $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB)n; \\\n+               $(INSTALL_DATA) pic/$(TARGETLIB) $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB)n; \\\n                ( cd $(DESTDIR)$(libdir)/$(MULTIOSDIR) ; chmod 644 $(TARGETLIB)n ;$(RANLIB) $(TARGETLIB)n ); \\\n                mv -f $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB)n $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB); \\\n                case \"${target_header_dir}\" in \\\n"
  },
  {
    "path": "build/fbcode_builder/patches/zlib_dont_build_more_than_needed.patch",
    "content": "diff -Naur ../zlib-1.3.1/CMakeLists.txt ./CMakeLists.txt\n--- ../zlib-1.3.1/CMakeLists.txt\t2024-01-22 10:32:37.000000000 -0800\n+++ ./CMakeLists.txt\t2024-01-23 13:14:09.870289968 -0800\n@@ -149,10 +149,8 @@\n     set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)\n endif(MINGW)\n \n-add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})\n+add_library(zlib ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})\n target_include_directories(zlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})\n-add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})\n-target_include_directories(zlibstatic PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})\n set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)\n set_target_properties(zlib PROPERTIES SOVERSION 1)\n \n@@ -169,7 +167,7 @@\n \n if(UNIX)\n     # On unix-like platforms the library is almost always called libz\n-   set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z)\n+   set_target_properties(zlib PROPERTIES OUTPUT_NAME z)\n    if(NOT APPLE AND NOT(CMAKE_SYSTEM_NAME STREQUAL AIX))\n      set_target_properties(zlib PROPERTIES LINK_FLAGS \"-Wl,--version-script,\\\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\\\"\")\n    endif()\n@@ -179,7 +177,7 @@\n endif()\n \n if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL )\n-    install(TARGETS zlib zlibstatic\n+    install(TARGETS zlib\n         RUNTIME DESTINATION \"${INSTALL_BIN_DIR}\"\n         ARCHIVE DESTINATION \"${INSTALL_LIB_DIR}\"\n         LIBRARY DESTINATION \"${INSTALL_LIB_DIR}\" )\n "
  },
  {
    "path": "build.bat",
    "content": "@REM Copyright (c) Meta Platforms, Inc. and affiliates.\n@REM\n@REM Licensed under the Apache License, Version 2.0 (the \"License\");\n@REM you may not use this file except in compliance with the License.\n@REM You may obtain a copy of the License at\n@REM\n@REM     http://www.apache.org/licenses/LICENSE-2.0\n@REM\n@REM Unless required by applicable law or agreed to in writing, software\n@REM distributed under the License is distributed on an \"AS IS\" BASIS,\n@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@REM See the License for the specific language governing permissions and\n@REM limitations under the License.\n\n@echo off\nSETLOCAL\nif exist %~dp0\\..\\..\\opensource\\fbcode_builder\\getdeps.py (\n  set GETDEPS=%~dp0\\..\\..\\opensource\\fbcode_builder\\getdeps.py\n) else if exist %~dp0\\build\\fbcode_builder\\getdeps.py (\n  set GETDEPS=%~dp0\\build\\fbcode_builder\\getdeps.py\n) else (\n  echo \"error: unable to find getdeps.py\"\n  exit 1\n)\n\npython3.exe %GETDEPS% build folly %*\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/bash\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This script is just a simple wrapper around the\n# build/fbcode_builder/getdeps.py script.\n#\n# Feel free to invoke getdeps.py directly to have more control over the build.\n\nSCRIPT_DIR=$(dirname \"${BASH_SOURCE[0]}\")\nGETDEPS_PATHS=(\n    \"$SCRIPT_DIR/build/fbcode_builder/getdeps.py\"\n    \"$SCRIPT_DIR/../../opensource/fbcode_builder/getdeps.py\"\n)\nfor getdeps in \"${GETDEPS_PATHS[@]}\"; do\n    if [[ -x \"$getdeps\" ]]; then\n        exec \"$getdeps\" build folly \"$@\"\n    fi\ndone\necho \"Could not find getdeps.py\" >&2\nexit 1\n"
  },
  {
    "path": "folly/.clang-format",
    "content": "# The canary clang-format config file.\n#\n# Changes from head:\n# - AllowAllParametersOfDeclarationOnNextLine: Safe\n# - AllowShortFunctionsOnASingleLine: Safe\n# - IncludeIsMainRegex: May cause errors, as it may reorder includes.\n# - IncludeCategories: May cause errors, as it may reorder includes.\n---\nAccessModifierOffset: -1\nAlignAfterOpenBracket: AlwaysBreak\nAlignConsecutiveMacros: false\nAlignConsecutiveAssignments: false\nAlignConsecutiveBitFields: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Left\nAlignOperands: DontAlign\nAlignTrailingComments: false\nAllowAllArgumentsOnNextLine: true\nAllowAllConstructorInitializersOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortEnumsOnASingleLine: true\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortLambdasOnASingleLine: All\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: true\nAlwaysBreakTemplateDeclarations: Yes\nBinPackArguments: false\nBinPackParameters: false\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Attach\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: false\nColumnLimit: 80\nCommentPragmas: '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDeriveLineEnding: true\nDerivePointerAlignment: false\nDisableFormat: false\nFixNamespaceComments: true\nForEachMacros:\n  - FOR_EACH\n  - FOR_EACH_R\n  - FOR_EACH_RANGE\nIncludeBlocks: Preserve\nIncludeCategories:\n  - Regex:           '^<(sodium|Python)\\.h>$'\n    Priority:        3\n  - Regex:           '^<((arpa|net|netinet|sys|linux)\\/)?[-_[:alnum:]]+\\.h>$'\n    Priority:        1\n  - Regex:           '^<[\\/-_[:alnum:]]+>$'\n    Priority:        2\n  - Regex:           '^<((bistro|caffe2|cachelib|fatal|fb303|faiss|fbmeshd|fboss|fbzmq|fizz|folly|gloo|hphp|logdevice|magma|mcrouter|openr|proxygen|quic|rsocket|scape|squangle|tensorpipe|thrift|wangle|wdt|yarpl)\\/).*>$'\n    Priority:        4\n  - Regex:           '^<.*>$'\n    Priority:        3\n  - Regex:           '.*'\n    Priority:        5\nIncludeIsMainRegex: '((Test|Tests|_test|Bench|Benchmark|Performance)[0-9]*)?$'\nIndentCaseLabels: true\nIndentCaseBlocks: false\nIndentGotoLabels: true\nIndentPPDirectives: None\nIndentExternBlock: AfterExternBlock\nIndentWidth: 2\nIndentWrappedFunctionNames: false\nInsertTrailingCommas: None\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nMacroBlockBegin: ''\nMacroBlockEnd: ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 2\nObjCBreakBeforeNestedBlockParam: true\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: false\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 1\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyIndentedWhitespace: 10\nPenaltyReturnTypeOnItsOwnLine: 200\nPointerAlignment: Left\nReflowComments: true\nSortIncludes: true\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyBlock: false\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles: false\nSpacesInConditionalStatement: false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nSpaceBeforeSquareBrackets: false\nStandard: Latest\nTabWidth: 8\nUseCRLF: false\nUseTab: Never\n...\n"
  },
  {
    "path": "folly/AtomicHashArray-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_ATOMICHASHARRAY_H_\n#error \"This should only be included by AtomicHashArray.h\"\n#endif\n\n#include <type_traits>\n\n#include <folly/detail/AtomicHashUtils.h>\n#include <folly/detail/Iterators.h>\n#include <folly/lang/Bits.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\n// AtomicHashArray private constructor --\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\nAtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::\n    AtomicHashArray(\n        size_t capacity,\n        KeyT emptyKey,\n        KeyT lockedKey,\n        KeyT erasedKey,\n        double _maxLoadFactor,\n        uint32_t cacheSize)\n    : capacity_(capacity),\n      maxEntries_(size_t(_maxLoadFactor * capacity_ + 0.5)),\n      kEmptyKey_(emptyKey),\n      kLockedKey_(lockedKey),\n      kErasedKey_(erasedKey),\n      kAnchorMask_(nextPowTwo(capacity_) - 1),\n      numEntries_(0, cacheSize),\n      numPendingEntries_(0, cacheSize),\n      isFull_(0),\n      numErases_(0) {\n  if (capacity == 0) {\n    throw_exception<std::invalid_argument>(\"capacity\");\n  }\n}\n\n/*\n * findInternal --\n *\n *   Sets ret.second to value found and ret.index to index\n *   of key and returns true, or if key does not exist returns false and\n *   ret.index is set to capacity_.\n */\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\ntemplate <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn>\ntypename AtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::SimpleRetT\nAtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::findInternal(const LookupKeyT key_in) {\n  checkLegalKeyIfKey<LookupKeyT>(key_in);\n\n  for (size_t idx = keyToAnchorIdx<LookupKeyT, LookupHashFcn>(key_in),\n              numProbes = 0;\n       ;\n       idx = ProbeFcn()(idx, numProbes, capacity_)) {\n    const KeyT key = acquireLoadKey(cells_[idx]);\n    if (FOLLY_LIKELY(LookupEqualFcn()(key, key_in))) {\n      return SimpleRetT(idx, true);\n    }\n    if (FOLLY_UNLIKELY(key == kEmptyKey_)) {\n      // if we hit an empty element, this key does not exist\n      return SimpleRetT(capacity_, false);\n    }\n    // NOTE: the way we count numProbes must be same in find(), insert(),\n    // and erase(). Otherwise it may break probing.\n    ++numProbes;\n    if (FOLLY_UNLIKELY(numProbes >= capacity_)) {\n      // probed every cell...fail\n      return SimpleRetT(capacity_, false);\n    }\n  }\n}\n\n/*\n * insertInternal --\n *\n *   Returns false on failure due to key collision or full.\n *   Also sets ret.index to the index of the key.  If the map is full, sets\n *   ret.index = capacity_.  Also sets ret.second to cell value, thus if insert\n *   successful this will be what we just inserted, if there is a key collision\n *   this will be the previously inserted value, and if the map is full it is\n *   default.\n */\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\ntemplate <\n    typename LookupKeyT,\n    typename LookupHashFcn,\n    typename LookupEqualFcn,\n    typename LookupKeyToKeyFcn,\n    typename... ArgTs>\ntypename AtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::SimpleRetT\nAtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::insertInternal(LookupKeyT key_in, ArgTs&&... vCtorArgs) {\n  const short NO_NEW_INSERTS = 1;\n  const short NO_PENDING_INSERTS = 2;\n  checkLegalKeyIfKey<LookupKeyT>(key_in);\n\n  size_t idx = keyToAnchorIdx<LookupKeyT, LookupHashFcn>(key_in);\n  size_t numProbes = 0;\n  for (;;) {\n    DCHECK_LT(idx, capacity_);\n    value_type* cell = &cells_[idx];\n    if (relaxedLoadKey(*cell) == kEmptyKey_) {\n      // NOTE: isFull_ is set based on numEntries_.readFast(), so it's\n      // possible to insert more than maxEntries_ entries. However, it's not\n      // possible to insert past capacity_.\n      ++numPendingEntries_;\n      if (isFull_.load(std::memory_order_acquire)) {\n        --numPendingEntries_;\n\n        // Before deciding whether this insert succeeded, this thread needs to\n        // wait until no other thread can add a new entry.\n\n        // Correctness assumes isFull_ is true at this point. If\n        // another thread now does ++numPendingEntries_, we expect it\n        // to pass the isFull_.load() test above. (It shouldn't insert\n        // a new entry.)\n        detail::atomic_hash_spin_wait([&] {\n          return (isFull_.load(std::memory_order_acquire) !=\n                  NO_PENDING_INSERTS) &&\n              (numPendingEntries_.readFull() != 0);\n        });\n        isFull_.store(NO_PENDING_INSERTS, std::memory_order_release);\n\n        if (relaxedLoadKey(*cell) == kEmptyKey_) {\n          // Don't insert past max load factor\n          return SimpleRetT(capacity_, false);\n        }\n      } else {\n        // An unallocated cell. Try once to lock it. If we succeed, insert here.\n        // If we fail, fall through to comparison below; maybe the insert that\n        // just beat us was for this very key....\n        if (tryLockCell(cell)) {\n          KeyT key_new;\n          // Write the value - done before unlocking\n          try {\n            key_new = LookupKeyToKeyFcn()(key_in);\n            typedef\n                typename std::remove_const<LookupKeyT>::type LookupKeyTNoConst;\n            constexpr bool kAlreadyChecked =\n                std::is_same<KeyT, LookupKeyTNoConst>::value;\n            if (!kAlreadyChecked) {\n              checkLegalKeyIfKey(key_new);\n            }\n            DCHECK(relaxedLoadKey(*cell) == kLockedKey_);\n            // A const mapped_type is only constant once constructed, so cast\n            // away any const for the placement new here.\n            using mapped = typename std::remove_const<mapped_type>::type;\n            new (const_cast<mapped*>(&cell->second))\n                ValueT(std::forward<ArgTs>(vCtorArgs)...);\n            unlockCell(cell, key_new); // Sets the new key\n          } catch (...) {\n            // Transition back to empty key---requires handling\n            // locked->empty below.\n            unlockCell(cell, kEmptyKey_);\n            --numPendingEntries_;\n            throw;\n          }\n          // An erase() can race here and delete right after our insertion\n          // Direct comparison rather than EqualFcn ok here\n          // (we just inserted it)\n          DCHECK(\n              relaxedLoadKey(*cell) == key_new ||\n              relaxedLoadKey(*cell) == kErasedKey_);\n          --numPendingEntries_;\n          ++numEntries_; // This is a thread cached atomic increment :)\n          if (numEntries_.readFast() >= maxEntries_) {\n            isFull_.store(NO_NEW_INSERTS, std::memory_order_relaxed);\n          }\n          return SimpleRetT(idx, true);\n        }\n        --numPendingEntries_;\n      }\n    }\n    DCHECK(relaxedLoadKey(*cell) != kEmptyKey_);\n    if (kLockedKey_ == acquireLoadKey(*cell)) {\n      detail::atomic_hash_spin_wait([&] {\n        return kLockedKey_ == acquireLoadKey(*cell);\n      });\n    }\n\n    const KeyT thisKey = acquireLoadKey(*cell);\n    if (LookupEqualFcn()(thisKey, key_in)) {\n      // Found an existing entry for our key, but we don't overwrite the\n      // previous value.\n      return SimpleRetT(idx, false);\n    } else if (thisKey == kEmptyKey_ || thisKey == kLockedKey_) {\n      // We need to try again (i.e., don't increment numProbes or\n      // advance idx): this case can happen if the constructor for\n      // ValueT threw for this very cell (the rethrow block above).\n      continue;\n    }\n\n    // NOTE: the way we count numProbes must be same in find(),\n    // insert(), and erase(). Otherwise it may break probing.\n    ++numProbes;\n    if (FOLLY_UNLIKELY(numProbes >= capacity_)) {\n      // probed every cell...fail\n      return SimpleRetT(capacity_, false);\n    }\n\n    idx = ProbeFcn()(idx, numProbes, capacity_);\n  }\n}\n\n/*\n * erase --\n *\n *   This will attempt to erase the given key key_in if the key is found. It\n *   returns 1 iff the key was located and marked as erased, and 0 otherwise.\n *\n *   Memory is not freed or reclaimed by erase, i.e. the cell containing the\n *   erased key will never be reused. If there's an associated value, we won't\n *   touch it either.\n */\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\nsize_t AtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::erase(KeyT key_in) {\n  CHECK_NE(key_in, kEmptyKey_);\n  CHECK_NE(key_in, kLockedKey_);\n  CHECK_NE(key_in, kErasedKey_);\n\n  for (size_t idx = keyToAnchorIdx(key_in), numProbes = 0;;\n       idx = ProbeFcn()(idx, numProbes, capacity_)) {\n    DCHECK_LT(idx, capacity_);\n    value_type* cell = &cells_[idx];\n    KeyT currentKey = acquireLoadKey(*cell);\n    if (currentKey == kEmptyKey_ || currentKey == kLockedKey_) {\n      // If we hit an empty (or locked) element, this key does not exist. This\n      // is similar to how it's handled in find().\n      return 0;\n    }\n    if (EqualFcn()(currentKey, key_in)) {\n      // Found an existing entry for our key, attempt to mark it erased.\n      // Some other thread may have erased our key, but this is ok.\n      KeyT expect = currentKey;\n      if (cellKeyPtr(*cell)->compare_exchange_strong(expect, kErasedKey_)) {\n        numErases_.fetch_add(1, std::memory_order_relaxed);\n\n        // Even if there's a value in the cell, we won't delete (or even\n        // default construct) it because some other thread may be accessing it.\n        // Locking it meanwhile won't work either since another thread may be\n        // holding a pointer to it.\n\n        // We found the key and successfully erased it.\n        return 1;\n      }\n      // If another thread succeeds in erasing our key, we'll stop our search.\n      return 0;\n    }\n\n    // NOTE: the way we count numProbes must be same in find(), insert(),\n    // and erase(). Otherwise it may break probing.\n    ++numProbes;\n    if (FOLLY_UNLIKELY(numProbes >= capacity_)) {\n      // probed every cell...fail\n      return 0;\n    }\n  }\n}\n\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\ntypename AtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::SmartPtr\nAtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::create(size_t maxSize, const Config& c) {\n  CHECK_LE(c.maxLoadFactor, 1.0);\n  CHECK_GT(c.maxLoadFactor, 0.0);\n  CHECK_NE(c.emptyKey, c.lockedKey);\n  size_t capacity = size_t(maxSize / c.maxLoadFactor);\n  size_t sz = sizeof(AtomicHashArray) + sizeof(value_type) * capacity;\n\n  auto const mem = Allocator().allocate(sz);\n  try {\n    new (mem) AtomicHashArray(\n        capacity,\n        c.emptyKey,\n        c.lockedKey,\n        c.erasedKey,\n        c.maxLoadFactor,\n        c.entryCountThreadCacheSize);\n  } catch (...) {\n    Allocator().deallocate(mem, sz);\n    throw;\n  }\n\n  SmartPtr map(static_cast<AtomicHashArray*>((void*)mem));\n\n  /*\n   * Mark all cells as empty.\n   *\n   * Note: we're bending the rules a little here accessing the key\n   * element in our cells even though the cell object has not been\n   * constructed, and casting them to atomic objects (see cellKeyPtr).\n   * (Also, in fact we never actually invoke the value_type\n   * constructor.)  This is in order to avoid needing to default\n   * construct a bunch of value_type when we first start up: if you\n   * have an expensive default constructor for the value type this can\n   * noticeably speed construction time for an AHA.\n   */\n  FOR_EACH_RANGE (i, 0, map->capacity_) {\n    cellKeyPtr(map->cells_[i])\n        ->store(map->kEmptyKey_, std::memory_order_relaxed);\n  }\n  return map;\n}\n\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\nvoid AtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::destroy(AtomicHashArray* p) {\n  assert(p);\n\n  size_t sz = sizeof(AtomicHashArray) + sizeof(value_type) * p->capacity_;\n\n  FOR_EACH_RANGE (i, 0, p->capacity_) {\n    if (p->cells_[i].first != p->kEmptyKey_) {\n      p->cells_[i].~value_type();\n    }\n  }\n  p->~AtomicHashArray();\n\n  Allocator().deallocate((char*)p, sz);\n}\n\n// clear -- clears all keys and values in the map and resets all counters\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\nvoid AtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::clear() {\n  FOR_EACH_RANGE (i, 0, capacity_) {\n    if (cells_[i].first != kEmptyKey_) {\n      cells_[i].~value_type();\n      *const_cast<KeyT*>(&cells_[i].first) = kEmptyKey_;\n    }\n    CHECK(cells_[i].first == kEmptyKey_);\n  }\n  numEntries_.set(0);\n  numPendingEntries_.set(0);\n  isFull_.store(0, std::memory_order_relaxed);\n  numErases_.store(0, std::memory_order_relaxed);\n}\n\n// Iterator implementation\n\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\ntemplate <class ContT, class IterVal>\nstruct AtomicHashArray<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::aha_iterator\n    : detail::IteratorFacade<\n          aha_iterator<ContT, IterVal>,\n          IterVal,\n          std::forward_iterator_tag> {\n  explicit aha_iterator() : aha_(nullptr) {}\n\n  // Conversion ctor for interoperability between const_iterator and\n  // iterator.  The enable_if<> magic keeps us well-behaved for\n  // is_convertible<> (v. the iterator_facade documentation).\n  template <class OtherContT, class OtherVal>\n  aha_iterator(\n      const aha_iterator<OtherContT, OtherVal>& o,\n      typename std::enable_if<\n          std::is_convertible<OtherVal*, IterVal*>::value>::type* = nullptr)\n      : aha_(o.aha_), offset_(o.offset_) {}\n\n  explicit aha_iterator(ContT* array, size_t offset)\n      : aha_(array), offset_(offset) {}\n\n  // Returns unique index that can be used with findAt().\n  // WARNING: The following function will fail silently for hashtable\n  // with capacity > 2^32\n  uint32_t getIndex() const { return offset_; }\n\n  void advancePastEmpty() {\n    while (offset_ < aha_->capacity_ && !isValid()) {\n      ++offset_;\n    }\n  }\n\n private:\n  friend class AtomicHashArray;\n  friend class detail::\n      IteratorFacade<aha_iterator, IterVal, std::forward_iterator_tag>;\n\n  void increment() {\n    ++offset_;\n    advancePastEmpty();\n  }\n\n  bool equal(const aha_iterator& o) const {\n    return aha_ == o.aha_ && offset_ == o.offset_;\n  }\n\n  IterVal& dereference() const { return aha_->cells_[offset_]; }\n\n  bool isValid() const {\n    KeyT key = acquireLoadKey(aha_->cells_[offset_]);\n    return key != aha_->kEmptyKey_ && key != aha_->kLockedKey_ &&\n        key != aha_->kErasedKey_;\n  }\n\n private:\n  ContT* aha_;\n  size_t offset_;\n}; // aha_iterator\n\n} // namespace folly\n"
  },
  {
    "path": "folly/AtomicHashArray.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n *  AtomicHashArray is the building block for AtomicHashMap.  It provides the\n *  core lock-free functionality, but is limited by the fact that it cannot\n *  grow past its initialization size and is a little more awkward (no public\n *  constructor, for example).  If you're confident that you won't run out of\n *  space, don't mind the awkwardness, and really need bare-metal performance,\n *  feel free to use AHA directly.\n *\n *  Check out AtomicHashMap.h for more thorough documentation on perf and\n *  general pros and cons relative to other hash maps.\n *\n */\n\n#pragma once\n#define FOLLY_ATOMICHASHARRAY_H_\n\n#include <atomic>\n\n#include <folly/ThreadCachedInt.h>\n#include <folly/Utility.h>\n#include <folly/hash/Hash.h>\n\nnamespace folly {\n\nstruct AtomicHashArrayLinearProbeFcn {\n  inline size_t operator()(\n      size_t idx, size_t /* numProbes */, size_t capacity) const {\n    idx += 1; // linear probing\n\n    // Avoid modulus because it's slow\n    return FOLLY_LIKELY(idx < capacity) ? idx : (idx - capacity);\n  }\n};\n\nstruct AtomicHashArrayQuadraticProbeFcn {\n  inline size_t operator()(\n      size_t idx, size_t numProbes, size_t capacity) const {\n    idx += numProbes; // quadratic probing\n\n    // Avoid modulus because it's slow\n    return FOLLY_LIKELY(idx < capacity) ? idx : (idx - capacity);\n  }\n};\n\n// Enables specializing checkLegalKey without specializing its class.\nnamespace detail {\ntemplate <typename NotKeyT, typename KeyT>\ninline void checkLegalKeyIfKeyTImpl(\n    NotKeyT /* ignored */,\n    KeyT /* emptyKey */,\n    KeyT /* lockedKey */,\n    KeyT /* erasedKey */) {}\n\ntemplate <typename KeyT>\ninline void checkLegalKeyIfKeyTImpl(\n    KeyT key_in, KeyT emptyKey, KeyT lockedKey, KeyT erasedKey) {\n  DCHECK_NE(key_in, emptyKey);\n  DCHECK_NE(key_in, lockedKey);\n  DCHECK_NE(key_in, erasedKey);\n}\n} // namespace detail\n\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn = std::hash<KeyT>,\n    class EqualFcn = std::equal_to<KeyT>,\n    class Allocator = std::allocator<char>,\n    class ProbeFcn = AtomicHashArrayLinearProbeFcn,\n    class KeyConvertFcn = Identity>\nclass AtomicHashMap;\n\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn = std::hash<KeyT>,\n    class EqualFcn = std::equal_to<KeyT>,\n    class Allocator = std::allocator<char>,\n    class ProbeFcn = AtomicHashArrayLinearProbeFcn,\n    class KeyConvertFcn = Identity>\nclass AtomicHashArray {\n  static_assert(\n      (std::is_convertible<KeyT, int32_t>::value ||\n       std::is_convertible<KeyT, int64_t>::value ||\n       std::is_convertible<KeyT, const void*>::value),\n      \"You are trying to use AtomicHashArray with disallowed key \"\n      \"types.  You must use atomically compare-and-swappable integer \"\n      \"keys, or a different container class.\");\n\n public:\n  using key_type = KeyT;\n  using mapped_type = ValueT;\n  using hasher = HashFcn;\n  using key_equal = EqualFcn;\n  using key_convert = KeyConvertFcn;\n  using value_type = std::pair<const KeyT, ValueT>;\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n  using pointer = value_type*;\n  using const_pointer = const value_type*;\n\n  const size_t capacity_;\n  const size_t maxEntries_;\n  const KeyT kEmptyKey_;\n  const KeyT kLockedKey_;\n  const KeyT kErasedKey_;\n\n  template <class ContT, class IterVal>\n  struct aha_iterator;\n\n  using const_iterator = aha_iterator<const AtomicHashArray, const value_type>;\n  using iterator = aha_iterator<AtomicHashArray, value_type>;\n\n  // You really shouldn't need this if you use the SmartPtr provided by create,\n  // but if you really want to do something crazy like stick the released\n  // pointer into a DiscriminatedPtr or something, you'll need this to clean up\n  // after yourself.\n  static void destroy(AtomicHashArray*);\n\n private:\n  const size_t kAnchorMask_;\n\n  struct Deleter {\n    void operator()(AtomicHashArray* ptr) { AtomicHashArray::destroy(ptr); }\n  };\n\n public:\n  using SmartPtr = std::unique_ptr<AtomicHashArray, Deleter>;\n\n  /*\n   * create --\n   *\n   *   Creates AtomicHashArray objects.  Use instead of constructor/destructor.\n   *\n   *   We do things this way in order to avoid the perf penalty of a second\n   *   pointer indirection when composing these into AtomicHashMap, which needs\n   *   to store an array of pointers so that it can perform atomic operations on\n   *   them when growing.\n   *\n   *   Instead of a mess of arguments, we take a max size and a Config struct to\n   *   simulate named ctor parameters.  The Config struct has sensible defaults\n   *   for everything, but is overloaded - if you specify a positive capacity,\n   *   that will be used directly instead of computing it based on\n   *   maxLoadFactor.\n   *\n   *   Create returns an AHA::SmartPtr which is a unique_ptr with a custom\n   *   deleter to make sure everything is cleaned up properly.\n   */\n  struct Config {\n    KeyT emptyKey;\n    KeyT lockedKey;\n    KeyT erasedKey;\n    double maxLoadFactor;\n    double growthFactor;\n    uint32_t entryCountThreadCacheSize;\n    size_t capacity; // if positive, overrides maxLoadFactor\n\n    //  Cannot have constexpr ctor because some compilers rightly complain.\n    Config()\n        : emptyKey((KeyT)-1),\n          lockedKey((KeyT)-2),\n          erasedKey((KeyT)-3),\n          maxLoadFactor(0.8),\n          growthFactor(-1),\n          entryCountThreadCacheSize(1000),\n          capacity(0) {}\n  };\n\n  //  Cannot have pre-instantiated const Config instance because of SIOF.\n  static SmartPtr create(size_t maxSize, const Config& c = Config());\n\n  /*\n   * find --\n   *\n   *\n   *   Returns the iterator to the element if found, otherwise end().\n   *\n   *   As an optional feature, the type of the key to look up (LookupKeyT) is\n   *   allowed to be different from the type of keys actually stored (KeyT).\n   *\n   *   This enables use cases where materializing the key is costly and usually\n   *   redundant, e.g., canonicalizing/interning a set of strings and being able\n   *   to look up by StringPiece. To use this feature, LookupHashFcn must take\n   *   a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first\n   *   and second parameter, respectively.\n   *\n   *   See folly/test/ArrayHashArrayTest.cpp for sample usage.\n   */\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal>\n  iterator find(LookupKeyT k) {\n    return iterator(\n        this, findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k).idx);\n  }\n\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal>\n  const_iterator find(LookupKeyT k) const {\n    return const_cast<AtomicHashArray*>(this)\n        ->find<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k);\n  }\n\n  /*\n   * insert --\n   *\n   *   Returns a pair with iterator to the element at r.first and bool success.\n   *   Retrieve the index with ret.first.getIndex().\n   *\n   *   Fails on key collision (does not overwrite) or if map becomes\n   *   full, at which point no element is inserted, iterator is set to end(),\n   *   and success is set false.  On collisions, success is set false, but the\n   *   iterator is set to the existing entry.\n   */\n  std::pair<iterator, bool> insert(const value_type& r) {\n    return emplace(r.first, r.second);\n  }\n  std::pair<iterator, bool> insert(value_type&& r) {\n    return emplace(r.first, std::move(r.second));\n  }\n\n  /*\n   * emplace --\n   *\n   *   Same contract as insert(), but performs in-place construction\n   *   of the value type using the specified arguments.\n   *\n   *   Also, like find(), this method optionally allows 'key_in' to have a type\n   *   different from that stored in the table; see find(). If and only if no\n   *   equal key is already present, this method converts 'key_in' to a key of\n   *   type KeyT using the provided LookupKeyToKeyFcn.\n   */\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal,\n      typename LookupKeyToKeyFcn = key_convert,\n      typename... ArgTs>\n  std::pair<iterator, bool> emplace(LookupKeyT key_in, ArgTs&&... vCtorArgs) {\n    SimpleRetT ret = insertInternal<\n        LookupKeyT,\n        LookupHashFcn,\n        LookupEqualFcn,\n        LookupKeyToKeyFcn>(key_in, std::forward<ArgTs>(vCtorArgs)...);\n    return std::make_pair(iterator(this, ret.idx), ret.success);\n  }\n\n  // returns the number of elements erased - should never exceed 1\n  size_t erase(KeyT k);\n\n  // clears all keys and values in the map and resets all counters.  Not thread\n  // safe.\n  void clear();\n\n  // Exact number of elements in the map - note that readFull() acquires a\n  // mutex.  See folly/ThreadCachedInt.h for more details.\n  size_t size() const {\n    return numEntries_.readFull() - numErases_.load(std::memory_order_relaxed);\n  }\n\n  bool empty() const { return size() == 0; }\n\n  iterator begin() {\n    iterator it(this, 0);\n    it.advancePastEmpty();\n    return it;\n  }\n  const_iterator begin() const {\n    const_iterator it(this, 0);\n    it.advancePastEmpty();\n    return it;\n  }\n\n  iterator end() { return iterator(this, capacity_); }\n  const_iterator end() const { return const_iterator(this, capacity_); }\n\n  // See AtomicHashMap::findAt - access elements directly\n  // WARNING: The following 2 functions will fail silently for hashtable\n  // with capacity > 2^32\n  iterator findAt(uint32_t idx) {\n    DCHECK_LT(idx, capacity_);\n    return iterator(this, idx);\n  }\n  const_iterator findAt(uint32_t idx) const {\n    return const_cast<AtomicHashArray*>(this)->findAt(idx);\n  }\n\n  iterator makeIter(size_t idx) { return iterator(this, idx); }\n  const_iterator makeIter(size_t idx) const {\n    return const_iterator(this, idx);\n  }\n\n  // The max load factor allowed for this map\n  double maxLoadFactor() const { return ((double)maxEntries_) / capacity_; }\n\n  void setEntryCountThreadCacheSize(uint32_t newSize) {\n    numEntries_.setCacheSize(newSize);\n    numPendingEntries_.setCacheSize(newSize);\n  }\n\n  uint32_t getEntryCountThreadCacheSize() const {\n    return numEntries_.getCacheSize();\n  }\n\n  /* Private data and helper functions... */\n\n private:\n  friend class AtomicHashMap<\n      KeyT,\n      ValueT,\n      HashFcn,\n      EqualFcn,\n      Allocator,\n      ProbeFcn>;\n\n  struct SimpleRetT {\n    size_t idx;\n    bool success;\n    SimpleRetT(size_t i, bool s) : idx(i), success(s) {}\n    SimpleRetT() = default;\n  };\n\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal,\n      typename LookupKeyToKeyFcn = Identity,\n      typename... ArgTs>\n  SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... vCtorArgs);\n\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal>\n  SimpleRetT findInternal(const LookupKeyT key);\n\n  template <typename MaybeKeyT>\n  void checkLegalKeyIfKey(MaybeKeyT key) {\n    detail::checkLegalKeyIfKeyTImpl(key, kEmptyKey_, kLockedKey_, kErasedKey_);\n  }\n\n  static std::atomic<KeyT>* cellKeyPtr(const value_type& r) {\n    // We need some illegal casting here in order to actually store\n    // our value_type as a std::pair<const,>.  But a little bit of\n    // undefined behavior never hurt anyone ...\n    static_assert(\n        sizeof(std::atomic<KeyT>) == sizeof(KeyT),\n        \"std::atomic is implemented in an unexpected way for AHM\");\n    return const_cast<std::atomic<KeyT>*>(\n        reinterpret_cast<std::atomic<KeyT> const*>(&r.first));\n  }\n\n  static KeyT relaxedLoadKey(const value_type& r) {\n    return cellKeyPtr(r)->load(std::memory_order_relaxed);\n  }\n\n  static KeyT acquireLoadKey(const value_type& r) {\n    return cellKeyPtr(r)->load(std::memory_order_acquire);\n  }\n\n  // Fun with thread local storage - atomic increment is expensive\n  // (relatively), so we accumulate in the thread cache and periodically\n  // flush to the actual variable, and walk through the unflushed counts when\n  // reading the value, so be careful of calling size() too frequently.  This\n  // increases insertion throughput several times over while keeping the count\n  // accurate.\n  ThreadCachedInt<uint64_t> numEntries_; // Successful key inserts\n  ThreadCachedInt<uint64_t> numPendingEntries_; // Used by insertInternal\n  std::atomic<int64_t> isFull_; // Used by insertInternal\n  std::atomic<int64_t> numErases_; // Successful key erases\n\n  value_type cells_[0]; // This must be the last field of this class\n\n  // Force constructor/destructor private since create/destroy should be\n  // used externally instead\n  AtomicHashArray(\n      size_t capacity,\n      KeyT emptyKey,\n      KeyT lockedKey,\n      KeyT erasedKey,\n      double maxLoadFactor,\n      uint32_t cacheSize);\n\n  AtomicHashArray(const AtomicHashArray&) = delete;\n  AtomicHashArray& operator=(const AtomicHashArray&) = delete;\n\n  ~AtomicHashArray() = default;\n\n  inline void unlockCell(value_type* const cell, KeyT newKey) {\n    cellKeyPtr(*cell)->store(newKey, std::memory_order_release);\n  }\n\n  inline bool tryLockCell(value_type* const cell) {\n    KeyT expect = kEmptyKey_;\n    return cellKeyPtr(*cell)->compare_exchange_strong(\n        expect, kLockedKey_, std::memory_order_acq_rel);\n  }\n\n  template <class LookupKeyT = key_type, class LookupHashFcn = hasher>\n  inline size_t keyToAnchorIdx(const LookupKeyT k) const {\n    const size_t hashVal = LookupHashFcn()(k);\n    const size_t probe = hashVal & kAnchorMask_;\n    return FOLLY_LIKELY(probe < capacity_) ? probe : hashVal % capacity_;\n  }\n\n}; // AtomicHashArray\n\n} // namespace folly\n\n#include <folly/AtomicHashArray-inl.h>\n"
  },
  {
    "path": "folly/AtomicHashMap-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_ATOMICHASHMAP_H_\n#error \"This should only be included by AtomicHashMap.h\"\n#endif\n\n#include <folly/detail/AtomicHashUtils.h>\n#include <folly/detail/Iterators.h>\n\n#include <type_traits>\n\nnamespace folly {\n\n// AtomicHashMap constructor -- Atomic wrapper that allows growth\n// This class has a lot of overhead (184 Bytes) so only use for big maps\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::AtomicHashMap(size_t finalSizeEst, const Config& config)\n    : kGrowthFrac_(\n          config.growthFactor < 0\n              ? 1.0f - config.maxLoadFactor\n              : config.growthFactor) {\n  CHECK(config.maxLoadFactor > 0.0f && config.maxLoadFactor < 1.0f);\n  subMaps_[0].store(\n      SubMap::create(finalSizeEst, config).release(),\n      std::memory_order_relaxed);\n  auto subMapCount = kNumSubMaps_;\n  FOR_EACH_RANGE (i, 1, subMapCount) {\n    subMaps_[i].store(nullptr, std::memory_order_relaxed);\n  }\n  numMapsAllocated_.store(1, std::memory_order_relaxed);\n}\n\n// emplace --\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntemplate <\n    typename LookupKeyT,\n    typename LookupHashFcn,\n    typename LookupEqualFcn,\n    typename LookupKeyToKeyFcn,\n    typename... ArgTs>\nstd::pair<\n    typename AtomicHashMap<\n        KeyT,\n        ValueT,\n        HashFcn,\n        EqualFcn,\n        Allocator,\n        ProbeFcn,\n        KeyConvertFcn>::iterator,\n    bool>\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::emplace(LookupKeyT k, ArgTs&&... vCtorArgs) {\n  SimpleRetT ret = insertInternal<\n      LookupKeyT,\n      LookupHashFcn,\n      LookupEqualFcn,\n      LookupKeyToKeyFcn>(k, std::forward<ArgTs>(vCtorArgs)...);\n  SubMap* subMap = subMaps_[ret.i].load(std::memory_order_relaxed);\n  return std::make_pair(\n      iterator(this, ret.i, subMap->makeIter(ret.j)), ret.success);\n}\n\n// insertInternal -- Allocates new sub maps as existing ones fill up.\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntemplate <\n    typename LookupKeyT,\n    typename LookupHashFcn,\n    typename LookupEqualFcn,\n    typename LookupKeyToKeyFcn,\n    typename... ArgTs>\ntypename AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::SimpleRetT\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::insertInternal(LookupKeyT key, ArgTs&&... vCtorArgs) {\nbeginInsertInternal:\n  auto nextMapIdx = // this maintains our state\n      numMapsAllocated_.load(std::memory_order_acquire);\n  typename SubMap::SimpleRetT ret;\n  FOR_EACH_RANGE (i, 0, nextMapIdx) {\n    // insert in each map successively.  If one succeeds, we're done!\n    SubMap* subMap = subMaps_[i].load(std::memory_order_relaxed);\n    ret = subMap->template insertInternal<\n        LookupKeyT,\n        LookupHashFcn,\n        LookupEqualFcn,\n        LookupKeyToKeyFcn>(key, std::forward<ArgTs>(vCtorArgs)...);\n    if (ret.idx == subMap->capacity_) {\n      continue; // map is full, so try the next one\n    }\n    // Either collision or success - insert in either case\n    return SimpleRetT(i, ret.idx, ret.success);\n  }\n\n  // If we made it this far, all maps are full and we need to try to allocate\n  // the next one.\n\n  SubMap* primarySubMap = subMaps_[0].load(std::memory_order_relaxed);\n  if (nextMapIdx >= kNumSubMaps_ ||\n      primarySubMap->capacity_ * kGrowthFrac_ < 1.0) {\n    // Can't allocate any more sub maps.\n    throw AtomicHashMapFullError();\n  }\n\n  if (tryLockMap(nextMapIdx)) {\n    // Alloc a new map and shove it in.  We can change whatever\n    // we want because other threads are waiting on us...\n    size_t numCellsAllocated =\n        (size_t)(primarySubMap->capacity_ *\n                 std::pow(1.0 + kGrowthFrac_, nextMapIdx - 1));\n    size_t newSize = size_t(numCellsAllocated * kGrowthFrac_);\n    DCHECK(\n        subMaps_[nextMapIdx].load(std::memory_order_relaxed) ==\n        (SubMap*)kLockedPtr_);\n    // create a new map using the settings stored in the first map\n\n    Config config;\n    config.emptyKey = primarySubMap->kEmptyKey_;\n    config.lockedKey = primarySubMap->kLockedKey_;\n    config.erasedKey = primarySubMap->kErasedKey_;\n    config.maxLoadFactor = primarySubMap->maxLoadFactor();\n    config.entryCountThreadCacheSize =\n        primarySubMap->getEntryCountThreadCacheSize();\n    subMaps_[nextMapIdx].store(\n        SubMap::create(newSize, config).release(), std::memory_order_relaxed);\n\n    // Publish the map to other threads.\n    numMapsAllocated_.fetch_add(1, std::memory_order_release);\n    DCHECK_EQ(\n        nextMapIdx + 1, numMapsAllocated_.load(std::memory_order_relaxed));\n  } else {\n    // If we lost the race, we'll have to wait for the next map to get\n    // allocated before doing any insertion here.\n    detail::atomic_hash_spin_wait([&] {\n      return nextMapIdx >= numMapsAllocated_.load(std::memory_order_acquire);\n    });\n  }\n\n  // Relaxed is ok here because either we just created this map, or we\n  // just did a spin wait with an acquire load on numMapsAllocated_.\n  SubMap* loadedMap = subMaps_[nextMapIdx].load(std::memory_order_relaxed);\n  DCHECK(loadedMap && loadedMap != (SubMap*)kLockedPtr_);\n  ret = loadedMap->insertInternal(key, std::forward<ArgTs>(vCtorArgs)...);\n  if (ret.idx != loadedMap->capacity_) {\n    return SimpleRetT(nextMapIdx, ret.idx, ret.success);\n  }\n  // We took way too long and the new map is already full...try again from\n  // the top (this should pretty much never happen).\n  goto beginInsertInternal;\n}\n\n// find --\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntemplate <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn>\ntypename AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::iterator\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::find(LookupKeyT k) {\n  SimpleRetT ret = findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k);\n  if (!ret.success) {\n    return end();\n  }\n  SubMap* subMap = subMaps_[ret.i].load(std::memory_order_relaxed);\n  return iterator(this, ret.i, subMap->makeIter(ret.j));\n}\n\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntemplate <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn>\ntypename AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::const_iterator\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::find(LookupKeyT k) const {\n  return const_cast<AtomicHashMap*>(this)\n      ->find<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k);\n}\n\n// findInternal --\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntemplate <class LookupKeyT, class LookupHashFcn, class LookupEqualFcn>\ntypename AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::SimpleRetT\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::findInternal(const LookupKeyT k) const {\n  SubMap* const primaryMap = subMaps_[0].load(std::memory_order_relaxed);\n  typename SubMap::SimpleRetT ret =\n      primaryMap\n          ->template findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(k);\n  if (FOLLY_LIKELY(ret.idx != primaryMap->capacity_)) {\n    return SimpleRetT(0, ret.idx, ret.success);\n  }\n  const unsigned int numMaps =\n      numMapsAllocated_.load(std::memory_order_acquire);\n  FOR_EACH_RANGE (i, 1, numMaps) {\n    // Check each map successively.  If one succeeds, we're done!\n    SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed);\n    ret =\n        thisMap\n            ->template findInternal<LookupKeyT, LookupHashFcn, LookupEqualFcn>(\n                k);\n    if (FOLLY_LIKELY(ret.idx != thisMap->capacity_)) {\n      return SimpleRetT(i, ret.idx, ret.success);\n    }\n  }\n  // Didn't find our key...\n  return SimpleRetT(numMaps, 0, false);\n}\n\n// findAtInternal -- see encodeIndex() for details.\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntypename AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::SimpleRetT\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::findAtInternal(uint32_t idx) const {\n  uint32_t subMapIdx, subMapOffset;\n  if (idx & kSecondaryMapBit_) {\n    // idx falls in a secondary map\n    idx &= ~kSecondaryMapBit_; // unset secondary bit\n    subMapIdx = idx >> kSubMapIndexShift_;\n    DCHECK_LT(subMapIdx, numMapsAllocated_.load(std::memory_order_relaxed));\n    subMapOffset = idx & kSubMapIndexMask_;\n  } else {\n    // idx falls in primary map\n    subMapIdx = 0;\n    subMapOffset = idx;\n  }\n  return SimpleRetT(subMapIdx, subMapOffset, true);\n}\n\n// erase --\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntypename AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::size_type\nAtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::erase(const KeyT k) {\n  int const numMaps = numMapsAllocated_.load(std::memory_order_acquire);\n  FOR_EACH_RANGE (i, 0, numMaps) {\n    // Check each map successively.  If one succeeds, we're done!\n    if (subMaps_[i].load(std::memory_order_relaxed)->erase(k)) {\n      return 1;\n    }\n  }\n  // Didn't find our key...\n  return 0;\n}\n\n// capacity -- summation of capacities of all submaps\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\nsize_t AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::capacity() const {\n  size_t totalCap(0);\n  int const numMaps = numMapsAllocated_.load(std::memory_order_acquire);\n  FOR_EACH_RANGE (i, 0, numMaps) {\n    totalCap += subMaps_[i].load(std::memory_order_relaxed)->capacity_;\n  }\n  return totalCap;\n}\n\n// spaceRemaining --\n// number of new insertions until current submaps are all at max load\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\nsize_t AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::spaceRemaining() const {\n  size_t spaceRem(0);\n  int const numMaps = numMapsAllocated_.load(std::memory_order_acquire);\n  FOR_EACH_RANGE (i, 0, numMaps) {\n    SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed);\n    spaceRem +=\n        std::max(0, thisMap->maxEntries_ - &thisMap->numEntries_.readFull());\n  }\n  return spaceRem;\n}\n\n// clear -- Wipes all keys and values from primary map and destroys\n// all secondary maps.  Not thread safe.\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\nvoid AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::clear() {\n  subMaps_[0].load(std::memory_order_relaxed)->clear();\n  int const numMaps = numMapsAllocated_.load(std::memory_order_relaxed);\n  FOR_EACH_RANGE (i, 1, numMaps) {\n    SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed);\n    DCHECK(thisMap);\n    SubMap::destroy(thisMap);\n    subMaps_[i].store(nullptr, std::memory_order_relaxed);\n  }\n  numMapsAllocated_.store(1, std::memory_order_relaxed);\n}\n\n// size --\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\nsize_t AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::size() const {\n  size_t totalSize(0);\n  int const numMaps = numMapsAllocated_.load(std::memory_order_acquire);\n  FOR_EACH_RANGE (i, 0, numMaps) {\n    totalSize += subMaps_[i].load(std::memory_order_relaxed)->size();\n  }\n  return totalSize;\n}\n\n// encodeIndex -- Encode the submap index and offset into return.\n// index_ret must be pre-populated with the submap offset.\n//\n// We leave index_ret untouched when referring to the primary map\n// so it can be as large as possible (31 data bits).  Max size of\n// secondary maps is limited by what can fit in the low 27 bits.\n//\n// Returns the following bit-encoded data in index_ret:\n//   if subMap == 0 (primary map) =>\n//     bit(s)          value\n//         31              0\n//       0-30  submap offset (index_ret input)\n//\n//   if subMap > 0 (secondary maps) =>\n//     bit(s)          value\n//         31              1\n//      27-30   which subMap\n//       0-26  subMap offset (index_ret input)\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ninline uint32_t AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::encodeIndex(uint32_t subMap, uint32_t offset) {\n  DCHECK_EQ(offset & kSecondaryMapBit_, 0); // offset can't be too big\n  if (subMap == 0) {\n    return offset;\n  }\n  // Make sure subMap isn't too big\n  DCHECK_EQ(subMap >> kNumSubMapBits_, 0);\n  // Make sure subMap bits of offset are clear\n  DCHECK_EQ(offset & (~kSubMapIndexMask_ | kSecondaryMapBit_), 0);\n\n  // Set high-order bits to encode which submap this index belongs to\n  return offset | (subMap << kSubMapIndexShift_) | kSecondaryMapBit_;\n}\n\n// Iterator implementation\n\ntemplate <\n    typename KeyT,\n    typename ValueT,\n    typename HashFcn,\n    typename EqualFcn,\n    typename Allocator,\n    typename ProbeFcn,\n    typename KeyConvertFcn>\ntemplate <class ContT, class IterVal, class SubIt>\nstruct AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    ProbeFcn,\n    KeyConvertFcn>::ahm_iterator\n    : detail::IteratorFacade<\n          ahm_iterator<ContT, IterVal, SubIt>,\n          IterVal,\n          std::forward_iterator_tag> {\n  explicit ahm_iterator() : ahm_(nullptr) {}\n\n  // Conversion ctor for interoperability between const_iterator and\n  // iterator.  The enable_if<> magic keeps us well-behaved for\n  // is_convertible<> (v. the iterator_facade documentation).\n  template <class OtherContT, class OtherVal, class OtherSubIt>\n  ahm_iterator(\n      const ahm_iterator<OtherContT, OtherVal, OtherSubIt>& o,\n      typename std::enable_if<\n          std::is_convertible<OtherSubIt, SubIt>::value>::type* = nullptr)\n      : ahm_(o.ahm_), subMap_(o.subMap_), subIt_(o.subIt_) {}\n\n  /*\n   * Returns the unique index that can be used for access directly\n   * into the data storage.\n   */\n  uint32_t getIndex() const {\n    CHECK(!isEnd());\n    return ahm_->encodeIndex(subMap_, subIt_.getIndex());\n  }\n\n private:\n  friend class AtomicHashMap;\n  explicit ahm_iterator(ContT* ahm, uint32_t subMap, const SubIt& subIt)\n      : ahm_(ahm), subMap_(subMap), subIt_(subIt) {}\n\n  friend class detail::\n      IteratorFacade<ahm_iterator, IterVal, std::forward_iterator_tag>;\n\n  void increment() {\n    CHECK(!isEnd());\n    ++subIt_;\n    checkAdvanceToNextSubmap();\n  }\n\n  bool equal(const ahm_iterator& other) const {\n    if (ahm_ != other.ahm_) {\n      return false;\n    }\n\n    if (isEnd() || other.isEnd()) {\n      return isEnd() == other.isEnd();\n    }\n\n    return subMap_ == other.subMap_ && subIt_ == other.subIt_;\n  }\n\n  IterVal& dereference() const { return *subIt_; }\n\n  bool isEnd() const { return ahm_ == nullptr; }\n\n  void checkAdvanceToNextSubmap() {\n    if (isEnd()) {\n      return;\n    }\n\n    SubMap* thisMap = ahm_->subMaps_[subMap_].load(std::memory_order_relaxed);\n    while (subIt_ == thisMap->end()) {\n      // This sub iterator is done, advance to next one\n      if (subMap_ + 1 <\n          ahm_->numMapsAllocated_.load(std::memory_order_acquire)) {\n        ++subMap_;\n        thisMap = ahm_->subMaps_[subMap_].load(std::memory_order_relaxed);\n        subIt_ = thisMap->begin();\n      } else {\n        ahm_ = nullptr;\n        return;\n      }\n    }\n  }\n\n private:\n  ContT* ahm_;\n  uint32_t subMap_;\n  SubIt subIt_;\n}; // ahm_iterator\n\n} // namespace folly\n"
  },
  {
    "path": "folly/AtomicHashMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * AtomicHashMap --\n *\n * A high-performance concurrent hash map with int32_t or int64_t keys. Supports\n * insert, find(key), findAt(index), erase(key), size, and more.  Memory cannot\n * be freed or reclaimed by erase.  Can grow to a maximum of about 18 times the\n * initial capacity, but performance degrades linearly with growth. Can also be\n * used as an object store with unique 32-bit references directly into the\n * internal storage (retrieved with iterator::getIndex()).\n *\n * Advantages:\n *    - High-performance (~2-4x tbb::concurrent_hash_map in heavily\n *      multi-threaded environments).\n *    - Efficient memory usage if initial capacity is not over estimated\n *      (especially for small keys and values).\n *    - Good fragmentation properties (only allocates in large slabs which can\n *      be reused with clear() and never move).\n *    - Can generate unique, long-lived 32-bit references for efficient lookup\n *      (see findAt()).\n *\n * Disadvantages:\n *    - Keys must be native int32_t or int64_t, or explicitly converted.\n *    - Must be able to specify unique empty, locked, and erased keys\n *    - Performance degrades linearly as size grows beyond initialization\n *      capacity.\n *    - Max size limit of ~18x initial size (dependent on max load factor).\n *    - Memory is not freed or reclaimed by erase.\n *\n * Usage and Operation Details:\n *   Simple performance/memory tradeoff with maxLoadFactor.  Higher load factors\n *   give better memory utilization but probe lengths increase, reducing\n *   performance.\n *\n * Implementation and Performance Details:\n *   AHArray is a fixed size contiguous block of value_type cells.  When\n *   writing a cell, the key is locked while the rest of the record is\n *   written.  Once done, the cell is unlocked by setting the key.  find()\n *   is completely wait-free and doesn't require any non-relaxed atomic\n *   operations.  AHA cannot grow beyond initialization capacity, but is\n *   faster because of reduced data indirection.\n *\n *   AHMap is a wrapper around AHArray sub-maps that allows growth and provides\n *   an interface closer to the STL UnorderedAssociativeContainer concept. These\n *   sub-maps are allocated on the fly and are processed in series, so the more\n *   there are (from growing past initial capacity), the worse the performance.\n *\n *   Insert returns false if there is a key collision and throws if the max size\n *   of the map is exceeded.\n *\n *   Benchmark performance with 8 simultaneous threads processing 1 million\n *   unique <int64_t, int64_t> entries on a 4-core, 2.5 GHz machine:\n *\n *     Load Factor   Mem Efficiency   usec/Insert   usec/Find\n *         50%             50%           0.19         0.05\n *         85%             85%           0.20         0.06\n *         90%             90%           0.23         0.08\n *         95%             95%           0.27         0.10\n *\n *   See folly/tests/AtomicHashMapTest.cpp for more benchmarks.\n */\n\n#pragma once\n#define FOLLY_ATOMICHASHMAP_H_\n\n#include <atomic>\n#include <functional>\n#include <stdexcept>\n\n#include <folly/AtomicHashArray.h>\n#include <folly/CPortability.h>\n#include <folly/Likely.h>\n#include <folly/ThreadCachedInt.h>\n#include <folly/container/Foreach.h>\n#include <folly/hash/Hash.h>\n\nnamespace folly {\n\n/*\n * AtomicHashMap provides an interface somewhat similar to the\n * UnorderedAssociativeContainer concept in C++.  This does not\n * exactly match this concept (or even the basic Container concept),\n * because of some restrictions imposed by our datastructure.\n *\n * Specific differences (there are quite a few):\n *\n * - Efficiently thread safe for inserts (main point of this stuff),\n *   wait-free for lookups.\n *\n * - You can erase from this container, but the cell containing the key will\n *   not be free or reclaimed.\n *\n * - You can erase everything by calling clear() (and you must guarantee only\n *   one thread can be using the container to do that).\n *\n * - We aren't DefaultConstructible, CopyConstructible, Assignable, or\n *   EqualityComparable.  (Most of these are probably not something\n *   you actually want to do with this anyway.)\n *\n * - We don't support the various bucket functions, rehash(),\n *   reserve(), or equal_range().  Also no constructors taking\n *   iterators, although this could change.\n *\n * - Several insertion functions, notably operator[], are not\n *   implemented.  It is a little too easy to misuse these functions\n *   with this container, where part of the point is that when an\n *   insertion happens for a new key, it will atomically have the\n *   desired value.\n *\n * - The map has no templated insert() taking an iterator range, but\n *   we do provide an insert(key, value).  The latter seems more\n *   frequently useful for this container (to avoid sprinkling\n *   make_pair everywhere), and providing both can lead to some gross\n *   template error messages.\n *\n * - The Allocator must not be stateful (a new instance will be spun up for\n *   each allocation), and its allocate() method must take a raw number of\n *   bytes.\n *\n * - KeyT must be a 32 bit or 64 bit atomic integer type, and you must\n *   define special 'locked' and 'empty' key values in the ctor\n *\n * - We don't take the Hash function object as an instance in the\n *   constructor.\n *\n */\n\n// Thrown when insertion fails due to running out of space for\n// submaps.\nstruct FOLLY_EXPORT AtomicHashMapFullError : std::runtime_error {\n  explicit AtomicHashMapFullError()\n      : std::runtime_error(\"AtomicHashMap is full\") {}\n};\n\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn,\n    class EqualFcn,\n    class Allocator,\n    class ProbeFcn,\n    class KeyConvertFcn>\nclass AtomicHashMap {\n  typedef AtomicHashArray<\n      KeyT,\n      ValueT,\n      HashFcn,\n      EqualFcn,\n      Allocator,\n      ProbeFcn,\n      KeyConvertFcn>\n      SubMap;\n\n public:\n  typedef KeyT key_type;\n  typedef ValueT mapped_type;\n  typedef std::pair<const KeyT, ValueT> value_type;\n  typedef HashFcn hasher;\n  typedef EqualFcn key_equal;\n  typedef KeyConvertFcn key_convert;\n  typedef value_type* pointer;\n  typedef value_type& reference;\n  typedef const value_type& const_reference;\n  typedef std::ptrdiff_t difference_type;\n  typedef std::size_t size_type;\n  typedef typename SubMap::Config Config;\n\n  template <class ContT, class IterVal, class SubIt>\n  struct ahm_iterator;\n\n  typedef ahm_iterator<\n      const AtomicHashMap,\n      const value_type,\n      typename SubMap::const_iterator>\n      const_iterator;\n  typedef ahm_iterator<AtomicHashMap, value_type, typename SubMap::iterator>\n      iterator;\n\n public:\n  const float kGrowthFrac_; // How much to grow when we run out of capacity.\n\n  // The constructor takes a finalSizeEst which is the optimal\n  // number of elements to maximize space utilization and performance,\n  // and a Config object to specify more advanced options.\n  explicit AtomicHashMap(size_t finalSizeEst, const Config& c = Config());\n\n  AtomicHashMap(const AtomicHashMap&) = delete;\n  AtomicHashMap& operator=(const AtomicHashMap&) = delete;\n\n  ~AtomicHashMap() {\n    const unsigned int numMaps =\n        numMapsAllocated_.load(std::memory_order_relaxed);\n    FOR_EACH_RANGE (i, 0, numMaps) {\n      SubMap* thisMap = subMaps_[i].load(std::memory_order_relaxed);\n      DCHECK(thisMap);\n      SubMap::destroy(thisMap);\n    }\n  }\n\n  key_equal key_eq() const { return key_equal(); }\n  hasher hash_function() const { return hasher(); }\n\n  /*\n   * insert --\n   *\n   *   Returns a pair with iterator to the element at r.first and\n   *   success.  Retrieve the index with ret.first.getIndex().\n   *\n   *   Does not overwrite on key collision, but returns an iterator to\n   *   the existing element (since this could due to a race with\n   *   another thread, it is often important to check this return\n   *   value).\n   *\n   *   Allocates new sub maps as the existing ones become full.  If\n   *   all sub maps are full, no element is inserted, and\n   *   AtomicHashMapFullError is thrown.\n   */\n  std::pair<iterator, bool> insert(const value_type& r) {\n    return emplace(r.first, r.second);\n  }\n  std::pair<iterator, bool> insert(key_type k, const mapped_type& v) {\n    return emplace(k, v);\n  }\n  std::pair<iterator, bool> insert(value_type&& r) {\n    return emplace(r.first, std::move(r.second));\n  }\n  std::pair<iterator, bool> insert(key_type k, mapped_type&& v) {\n    return emplace(k, std::move(v));\n  }\n\n  /*\n   * emplace --\n   *\n   *   Same contract as insert(), but performs in-place construction\n   *   of the value type using the specified arguments.\n   *\n   *   Also, like find(), this method optionally allows 'key_in' to have a type\n   *   different from that stored in the table; see find(). If and only if no\n   *   equal key is already present, this method converts 'key_in' to a key of\n   *   type KeyT using the provided LookupKeyToKeyFcn.\n   */\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal,\n      typename LookupKeyToKeyFcn = key_convert,\n      typename... ArgTs>\n  std::pair<iterator, bool> emplace(LookupKeyT k, ArgTs&&... vCtorArg);\n\n  /*\n   * find --\n   *\n   *   Returns the iterator to the element if found, otherwise end().\n   *\n   *   As an optional feature, the type of the key to look up (LookupKeyT) is\n   *   allowed to be different from the type of keys actually stored (KeyT).\n   *\n   *   This enables use cases where materializing the key is costly and usually\n   *   redundant, e.g., canonicalizing/interning a set of strings and being able\n   *   to look up by StringPiece. To use this feature, LookupHashFcn must take\n   *   a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first\n   *   and second parameter, respectively.\n   *\n   *   See folly/test/ArrayHashMapTest.cpp for sample usage.\n   */\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal>\n  iterator find(LookupKeyT k);\n\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal>\n  const_iterator find(LookupKeyT k) const;\n\n  /*\n   * erase --\n   *\n   *   Erases key k from the map\n   *\n   *   Returns 1 iff the key is found and erased, and 0 otherwise.\n   */\n  size_type erase(key_type k);\n\n  /*\n   * clear --\n   *\n   *   Wipes all keys and values from primary map and destroys all secondary\n   *   maps.  Primary map remains allocated and thus the memory can be reused\n   *   in place.  Not thread safe.\n   *\n   */\n  void clear();\n\n  /*\n   * size --\n   *\n   *  Returns the exact size of the map.  Note this is not as cheap as typical\n   *  size() implementations because, for each AtomicHashArray in this AHM, we\n   *  need to grab a lock and accumulate the values from all the thread local\n   *  counters.  See folly/ThreadCachedInt.h for more details.\n   */\n  size_t size() const;\n\n  bool empty() const { return size() == 0; }\n\n  size_type count(key_type k) const { return find(k) == end() ? 0 : 1; }\n\n  /*\n   * findAt --\n   *\n   *   Returns an iterator into the map.\n   *\n   *   idx should only be an unmodified value returned by calling getIndex() on\n   *   a valid iterator returned by find() or insert(). If idx is invalid you\n   *   have a bug and the process aborts.\n   */\n  iterator findAt(uint32_t idx) {\n    SimpleRetT ret = findAtInternal(idx);\n    DCHECK_LT(ret.i, numSubMaps());\n    return iterator(\n        this,\n        ret.i,\n        subMaps_[ret.i].load(std::memory_order_relaxed)->makeIter(ret.j));\n  }\n  const_iterator findAt(uint32_t idx) const {\n    return const_cast<AtomicHashMap*>(this)->findAt(idx);\n  }\n\n  // Total capacity - summation of capacities of all submaps.\n  size_t capacity() const;\n\n  // Number of new insertions until current submaps are all at max load factor.\n  size_t spaceRemaining() const;\n\n  void setEntryCountThreadCacheSize(int32_t newSize) {\n    const int numMaps = numMapsAllocated_.load(std::memory_order_acquire);\n    for (int i = 0; i < numMaps; ++i) {\n      SubMap* map = subMaps_[i].load(std::memory_order_relaxed);\n      map->setEntryCountThreadCacheSize(newSize);\n    }\n  }\n\n  // Number of sub maps allocated so far to implement this map.  The more there\n  // are, the worse the performance.\n  int numSubMaps() const {\n    return numMapsAllocated_.load(std::memory_order_acquire);\n  }\n\n  iterator begin() {\n    iterator it(this, 0, subMaps_[0].load(std::memory_order_relaxed)->begin());\n    it.checkAdvanceToNextSubmap();\n    return it;\n  }\n\n  const_iterator begin() const {\n    const_iterator it(\n        this, 0, subMaps_[0].load(std::memory_order_relaxed)->begin());\n    it.checkAdvanceToNextSubmap();\n    return it;\n  }\n\n  iterator end() { return iterator(); }\n\n  const_iterator end() const { return const_iterator(); }\n\n  /* Advanced functions for direct access: */\n\n  inline uint32_t recToIdx(const value_type& r, bool mayInsert = true) {\n    SimpleRetT ret =\n        mayInsert ? insertInternal(r.first, r.second) : findInternal(r.first);\n    return encodeIndex(ret.i, ret.j);\n  }\n\n  inline uint32_t recToIdx(value_type&& r, bool mayInsert = true) {\n    SimpleRetT ret = mayInsert\n        ? insertInternal(r.first, std::move(r.second))\n        : findInternal(r.first);\n    return encodeIndex(ret.i, ret.j);\n  }\n\n  inline uint32_t recToIdx(\n      key_type k, const mapped_type& v, bool mayInsert = true) {\n    SimpleRetT ret = mayInsert ? insertInternal(k, v) : findInternal(k);\n    return encodeIndex(ret.i, ret.j);\n  }\n\n  inline uint32_t recToIdx(key_type k, mapped_type&& v, bool mayInsert = true) {\n    SimpleRetT ret =\n        mayInsert ? insertInternal(k, std::move(v)) : findInternal(k);\n    return encodeIndex(ret.i, ret.j);\n  }\n\n  inline uint32_t keyToIdx(const KeyT k, bool mayInsert = false) {\n    return recToIdx(value_type(k), mayInsert);\n  }\n\n  inline const value_type& idxToRec(uint32_t idx) const {\n    SimpleRetT ret = findAtInternal(idx);\n    return subMaps_[ret.i].load(std::memory_order_relaxed)->idxToRec(ret.j);\n  }\n\n  /* Private data and helper functions... */\n\n private:\n  // This limits primary submap size to 2^31 ~= 2 billion, secondary submap\n  // size to 2^(32 - kNumSubMapBits_ - 1) = 2^27 ~= 130 million, and num subMaps\n  // to 2^kNumSubMapBits_ = 16.\n  static const uint32_t kNumSubMapBits_ = 4;\n  static const uint32_t kSecondaryMapBit_ = 1u << 31; // Highest bit\n  static const uint32_t kSubMapIndexShift_ = 32 - kNumSubMapBits_ - 1;\n  static const uint32_t kSubMapIndexMask_ = (1 << kSubMapIndexShift_) - 1;\n  static const uint32_t kNumSubMaps_ = 1 << kNumSubMapBits_;\n  static const uintptr_t kLockedPtr_ = 0x88ULL << 48; // invalid pointer\n\n  struct SimpleRetT {\n    size_t j;\n    uint32_t i;\n    bool success;\n    SimpleRetT(uint32_t ii, size_t jj, bool s) : j(jj), i(ii), success(s) {}\n    SimpleRetT() = default;\n  };\n\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal,\n      typename LookupKeyToKeyFcn = key_convert,\n      typename... ArgTs>\n  SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... value);\n\n  template <\n      typename LookupKeyT = key_type,\n      typename LookupHashFcn = hasher,\n      typename LookupEqualFcn = key_equal>\n  SimpleRetT findInternal(const LookupKeyT k) const;\n\n  SimpleRetT findAtInternal(uint32_t idx) const;\n\n  std::atomic<SubMap*> subMaps_[kNumSubMaps_];\n  std::atomic<uint32_t> numMapsAllocated_;\n\n  inline bool tryLockMap(unsigned int idx) {\n    SubMap* val = nullptr;\n    return subMaps_[idx].compare_exchange_strong(\n        val, (SubMap*)kLockedPtr_, std::memory_order_acquire);\n  }\n\n  static inline uint32_t encodeIndex(uint32_t subMap, uint32_t subMapIdx);\n\n}; // AtomicHashMap\n\ntemplate <\n    class KeyT,\n    class ValueT,\n    class HashFcn = std::hash<KeyT>,\n    class EqualFcn = std::equal_to<KeyT>,\n    class Allocator = std::allocator<char>>\nusing QuadraticProbingAtomicHashMap = AtomicHashMap<\n    KeyT,\n    ValueT,\n    HashFcn,\n    EqualFcn,\n    Allocator,\n    AtomicHashArrayQuadraticProbeFcn>;\n} // namespace folly\n\n#include <folly/AtomicHashMap-inl.h>\n"
  },
  {
    "path": "folly/AtomicIntrusiveLinkedList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <utility>\n\nnamespace folly {\n\n/**\n * A very simple atomic single-linked list primitive.\n *\n * Usage:\n *\n * class MyClass {\n *   AtomicIntrusiveLinkedListHook<MyClass> hook_;\n * }\n *\n * AtomicIntrusiveLinkedList<MyClass, &MyClass::hook_> list;\n * list.insert(&a);\n * list.sweep([] (MyClass* c) { doSomething(c); }\n */\ntemplate <class T>\nstruct AtomicIntrusiveLinkedListHook {\n  T* next{nullptr};\n};\n\ntemplate <class T, AtomicIntrusiveLinkedListHook<T> T::* HookMember>\nclass AtomicIntrusiveLinkedList {\n public:\n  AtomicIntrusiveLinkedList() {}\n\n  AtomicIntrusiveLinkedList(const AtomicIntrusiveLinkedList&) = delete;\n  AtomicIntrusiveLinkedList& operator=(const AtomicIntrusiveLinkedList&) =\n      delete;\n\n  AtomicIntrusiveLinkedList(AtomicIntrusiveLinkedList&& other) noexcept\n      : head_(other.head_.exchange(nullptr, std::memory_order_acq_rel)) {}\n\n  // Absent because would be too error-prone to use correctly because of\n  // the requirement that lists are empty upon destruction.\n  AtomicIntrusiveLinkedList& operator=(\n      AtomicIntrusiveLinkedList&& other) noexcept = delete;\n\n  /**\n   * Move the currently held elements to a new list.\n   * The current list becomes empty, but concurrent threads\n   * might still add new elements to it.\n   *\n   * Equivalent to calling a move constructor, but more linter-friendly\n   * in case you still need the old list.\n   */\n  AtomicIntrusiveLinkedList spliceAll() { return std::move(*this); }\n\n  /**\n   * Move-assign the current list to `other`, then reverse-sweep\n   * the old list with the provided callback `func`.\n   *\n   * A safe replacement for the move assignment operator, which is absent\n   * because of the resource leak concerns.\n   */\n  template <typename F>\n  void reverseSweepAndAssign(AtomicIntrusiveLinkedList&& other, F&& func) {\n    auto otherHead = other.head_.exchange(nullptr, std::memory_order_acq_rel);\n    auto head = head_.exchange(otherHead, std::memory_order_acq_rel);\n    unlinkAll(head, std::forward<F>(func));\n  }\n\n  /**\n   * Note: The list must be empty on destruction.\n   */\n  ~AtomicIntrusiveLinkedList() { assert(empty()); }\n\n  /**\n   * Returns the current head of the list.\n   *\n   * WARNING: The returned pointer might not be valid if the list\n   * is modified concurrently!\n   */\n  T* unsafeHead() const { return head_.load(std::memory_order_acquire); }\n\n  /**\n   * Returns true if the list is empty.\n   *\n   * WARNING: This method's return value is only valid for a snapshot\n   * of the state, it might become stale as soon as it's returned.\n   */\n  bool empty() const { return unsafeHead() == nullptr; }\n\n  /**\n   * Atomically insert t at the head of the list.\n   * @return True if the inserted element is the only one in the list\n   *         after the call.\n   */\n  bool insertHead(T* t) {\n    assert(next(t) == nullptr);\n\n    auto oldHead = head_.load(std::memory_order_relaxed);\n    do {\n      next(t) = oldHead;\n      /* oldHead is updated by the call below.\n\n         NOTE: we don't use next(t) instead of oldHead directly due to\n         compiler bugs (GCC prior to 4.8.3 (bug 60272), clang (bug 18899),\n         MSVC (bug 819819); source:\n         http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange */\n    } while (!head_.compare_exchange_weak(\n        oldHead, t, std::memory_order_release, std::memory_order_relaxed));\n\n    return oldHead == nullptr;\n  }\n\n  /**\n   * Replaces the head with nullptr,\n   * and calls func() on the removed elements in the order from tail to head.\n   * Returns false if the list was empty.\n   */\n  template <typename F>\n  bool sweepOnce(F&& func) {\n    if (auto head = head_.exchange(nullptr, std::memory_order_acq_rel)) {\n      auto rhead = reverse(head);\n      unlinkAll(rhead, std::forward<F>(func));\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * Repeatedly replaces the head with nullptr,\n   * and calls func() on the removed elements in the order from tail to head.\n   * Stops when the list is empty.\n   */\n  template <typename F>\n  void sweep(F&& func) {\n    while (sweepOnce(func)) {\n    }\n  }\n\n  /**\n   * Similar to sweep() but calls func() on elements in LIFO order.\n   *\n   * func() is called for all elements in the list at the moment\n   * reverseSweep() is called.  Unlike sweep() it does not loop to ensure the\n   * list is empty at some point after the last invocation.  This way callers\n   * can reason about the ordering: elements inserted since the last call to\n   * reverseSweep() will be provided in LIFO order.\n   *\n   * Example: if elements are inserted in the order 1-2-3, the callback is\n   * invoked 3-2-1.  If the callback moves elements onto a stack, popping off\n   * the stack will produce the original insertion order 1-2-3.\n   */\n  template <typename F>\n  void reverseSweep(F&& func) {\n    // We don't loop like sweep() does because the overall order of callbacks\n    // would be strand-wise LIFO which is meaningless to callers.\n    auto head = head_.exchange(nullptr, std::memory_order_acq_rel);\n    unlinkAll(head, std::forward<F>(func));\n  }\n\n private:\n  std::atomic<T*> head_{nullptr};\n\n  static T*& next(T* t) { return (t->*HookMember).next; }\n\n  /* Reverses a linked list, returning the pointer to the new head\n     (old tail) */\n  static T* reverse(T* head) {\n    T* rhead = nullptr;\n    while (head != nullptr) {\n      auto t = head;\n      head = next(t);\n      next(t) = rhead;\n      rhead = t;\n    }\n    return rhead;\n  }\n\n  /* Unlinks all elements in the linked list fragment pointed to by `head',\n   * calling func() on every element */\n  template <typename F>\n  static void unlinkAll(T* head, F&& func) {\n    while (head != nullptr) {\n      auto t = head;\n      head = next(t);\n      next(t) = nullptr;\n      func(t);\n    }\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/AtomicLinkedList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/AtomicIntrusiveLinkedList.h>\n#include <folly/Memory.h>\n\nnamespace folly {\n\n/**\n * A very simple atomic single-linked list primitive.\n *\n * Usage:\n *\n * AtomicLinkedList<MyClass> list;\n * list.insert(a);\n * list.sweep([] (MyClass& c) { doSomething(c); }\n */\n\ntemplate <class T>\nclass AtomicLinkedList {\n public:\n  AtomicLinkedList() {}\n  AtomicLinkedList(const AtomicLinkedList&) = delete;\n  AtomicLinkedList& operator=(const AtomicLinkedList&) = delete;\n  AtomicLinkedList(AtomicLinkedList&& other) noexcept = default;\n  AtomicLinkedList& operator=(AtomicLinkedList&& other) noexcept {\n    list_.reverseSweepAndAssign(std::move(other.list_), [](Wrapper* node) {\n      delete node;\n    });\n    return *this;\n  }\n\n  ~AtomicLinkedList() {\n    sweep([](T&&) {});\n  }\n\n  bool empty() const { return list_.empty(); }\n\n  /**\n   * Atomically insert t at the head of the list.\n   * @return True if the inserted element is the only one in the list\n   *         after the call.\n   */\n  bool insertHead(T t) {\n    auto wrapper = std::make_unique<Wrapper>(std::move(t));\n\n    return list_.insertHead(wrapper.release());\n  }\n\n  /**\n   * Repeatedly pops element from head,\n   * and calls func() on the removed elements in the order from tail to head.\n   * Stops when the list is empty.\n   */\n  template <typename F>\n  void sweep(F&& func) {\n    list_.sweep([&](Wrapper* wrapperPtr) mutable {\n      std::unique_ptr<Wrapper> wrapper(wrapperPtr);\n\n      func(std::move(wrapper->data));\n    });\n  }\n\n  /**\n   * Sweeps the list a single time, as a single point in time swap with the\n   * current contents of the list.\n   *\n   * Unlike sweep() it does not loop to ensure the list is empty at some point\n   * after the last invocation.\n   *\n   * Returns false if the list is empty.\n   */\n  template <typename F>\n  bool sweepOnce(F&& func) {\n    return list_.sweepOnce([&](Wrapper* wrappedPtr) {\n      std::unique_ptr<Wrapper> wrapper(wrappedPtr);\n      func(std::move(wrapper->data));\n    });\n  }\n\n  /**\n   * Similar to sweep() but calls func() on elements in LIFO order.\n   *\n   * func() is called for all elements in the list at the moment\n   * reverseSweep() is called.  Unlike sweep() it does not loop to ensure the\n   * list is empty at some point after the last invocation.  This way callers\n   * can reason about the ordering: elements inserted since the last call to\n   * reverseSweep() will be provided in LIFO order.\n   *\n   * Example: if elements are inserted in the order 1-2-3, the callback is\n   * invoked 3-2-1.  If the callback moves elements onto a stack, popping off\n   * the stack will produce the original insertion order 1-2-3.\n   */\n  template <typename F>\n  void reverseSweep(F&& func) {\n    list_.reverseSweep([&](Wrapper* wrapperPtr) mutable {\n      std::unique_ptr<Wrapper> wrapper(wrapperPtr);\n\n      func(std::move(wrapper->data));\n    });\n  }\n\n private:\n  struct Wrapper {\n    explicit Wrapper(T&& t) : data(std::move(t)) {}\n\n    AtomicIntrusiveLinkedListHook<Wrapper> hook;\n    T data;\n  };\n  AtomicIntrusiveLinkedList<Wrapper, &Wrapper::hook> list_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/AtomicUnorderedMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstdint>\n#include <functional>\n#include <limits>\n#include <stdexcept>\n#include <system_error>\n#include <type_traits>\n\n#include <folly/Conv.h>\n#include <folly/Likely.h>\n#include <folly/Random.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/detail/AtomicUnorderedMapUtils.h>\n#include <folly/lang/Bits.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\n/// You're probably reading this because you are looking for an\n/// AtomicUnorderedMap<K,V> that is fully general, highly concurrent (for\n/// reads, writes, and iteration), and makes no performance compromises.\n/// We haven't figured that one out yet.  What you will find here is a\n/// hash table implementation that sacrifices generality so that it can\n/// give you all of the other things.\n///\n/// LIMITATIONS:\n///\n/// * Insert only (*) - the only write operation supported directly by\n///   AtomicUnorderedInsertMap is findOrConstruct.  There is a (*) because\n///   values aren't moved, so you can roll your own concurrency control for\n///   in-place updates of values (see MutableData and MutableAtom below),\n///   but the hash table itself doesn't help you.\n///\n/// * No resizing - you must specify the capacity up front, and once\n///   the hash map gets full you won't be able to insert.  Insert\n///   performance will degrade once the load factor is high.  Insert is\n///   O(1/(1-actual_load_factor)).  Note that this is a pretty strong\n///   limitation, because you can't remove existing keys.\n///\n/// * 2^30 maximum default capacity - by default AtomicUnorderedInsertMap\n///   uses uint32_t internal indexes (and steals 2 bits), limiting you\n///   to about a billion entries.  If you need more you can fill in all\n///   of the template params so you change IndexType to uint64_t, or you\n///   can use AtomicUnorderedInsertMap64.  64-bit indexes will increase\n///   the space over of the map, of course.\n///\n/// WHAT YOU GET IN EXCHANGE:\n///\n/// * Arbitrary key and value types - any K and V that can be used in a\n///   std::unordered_map can be used here.  In fact, the key and value\n///   types don't even have to be copyable or moveable!\n///\n/// * Keys and values in the map won't be moved - it is safe to keep\n///   pointers or references to the keys and values in the map, because\n///   they are never moved or destroyed (until the map itself is destroyed).\n///\n/// * Iterators are never invalidated - writes don't invalidate iterators,\n///   so you can scan and insert in parallel.\n///\n/// * Fast wait-free reads - reads are usually only a single cache miss,\n///   even when the hash table is very large.  Wait-freedom means that\n///   you won't see latency outliers even in the face of concurrent writes.\n///\n/// * Lock-free insert - writes proceed in parallel.  If a thread in the\n///   middle of a write is unlucky and gets suspended, it doesn't block\n///   anybody else.\n///\n/// COMMENTS ON INSERT-ONLY\n///\n/// This map provides wait-free linearizable reads and lock-free\n/// linearizable inserts.  Inserted values won't be moved, but no\n/// concurrency control is provided for safely updating them.  To remind\n/// you of that fact they are only provided in const form.  This is the\n/// only simple safe thing to do while preserving something like the normal\n/// std::map iteration form, which requires that iteration be exposed\n/// via std::pair (and prevents encapsulation of access to the value).\n///\n/// There are a couple of reasonable policies for doing in-place\n/// concurrency control on the values.  I am hoping that the policy can\n/// be injected via the value type or an extra template param, to keep\n/// the core AtomicUnorderedInsertMap insert-only:\n///\n///   CONST: this is the currently implemented strategy, which is simple,\n///   performant, and not that expressive.  You can always put in a value\n///   with a mutable field (see MutableAtom below), but that doesn't look\n///   as pretty as it should.\n///\n///   ATOMIC: for integers and integer-size trivially copyable structs\n///   (via an adapter like tao/queues/AtomicStruct) the value can be a\n///   std::atomic and read and written atomically.\n///\n///   SEQ-LOCK: attach a counter incremented before and after write.\n///   Writers serialize by using CAS to make an even->odd transition,\n///   then odd->even after the write.  Readers grab the value with memcpy,\n///   checking sequence value before and after.  Readers retry until they\n///   see an even sequence number that doesn't change.  This works for\n///   larger structs, but still requires memcpy to be equivalent to copy\n///   assignment, and it is no longer lock-free.  It scales very well,\n///   because the readers are still invisible (no cache line writes).\n///\n///   LOCK: folly's SharedMutex would be a good choice here.\n///\n/// MEMORY ALLOCATION\n///\n/// Underlying memory is allocated as a big anonymous chunk. If the\n/// SkipKeyValueDeletion template param is true then deletion of the map\n/// consists of deallocating the backing memory, which is much faster than\n/// destructing all of the keys and values.  Feel free to override if\n/// std::is_trivial_destructor isn't recognizing the triviality of your\n/// destructors.\ntemplate <\n    typename Key,\n    typename Value,\n    typename Hash = std::hash<Key>,\n    typename KeyEqual = std::equal_to<Key>,\n    bool SkipKeyValueDeletion =\n        (std::is_trivially_destructible<Key>::value &&\n         std::is_trivially_destructible<Value>::value),\n    template <typename> class Atom = std::atomic,\n    typename IndexType = uint32_t,\n    typename Allocator = folly::detail::MallocAlloc>\n\nstruct AtomicUnorderedInsertMap {\n  using key_type = Key;\n  using mapped_type = Value;\n  using value_type = std::pair<Key, Value>;\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n  using hasher = Hash;\n  using key_equal = KeyEqual;\n  using const_reference = const value_type&;\n\n  struct ConstIterator {\n    ConstIterator(const AtomicUnorderedInsertMap& owner, IndexType slot)\n        : owner_(owner), slot_(slot) {}\n\n    ConstIterator(const ConstIterator&) = default;\n    ConstIterator& operator=(const ConstIterator&) = default;\n\n    const value_type& operator*() const {\n      return *owner_.slots_[slot_].keyValue();\n    }\n\n    const value_type* operator->() const {\n      return owner_.slots_[slot_].keyValue();\n    }\n\n    // pre-increment\n    const ConstIterator& operator++() {\n      while (slot_ > 0) {\n        --slot_;\n        if (owner_.slots_[slot_].state() == LINKED) {\n          break;\n        }\n      }\n      return *this;\n    }\n\n    // post-increment\n    ConstIterator operator++(int /* dummy */) {\n      auto prev = *this;\n      ++*this;\n      return prev;\n    }\n\n    bool operator==(const ConstIterator& rhs) const {\n      return slot_ == rhs.slot_;\n    }\n    bool operator!=(const ConstIterator& rhs) const { return !(*this == rhs); }\n\n   private:\n    const AtomicUnorderedInsertMap& owner_;\n    IndexType slot_;\n  };\n  using const_iterator = ConstIterator;\n\n  friend ConstIterator;\n\n  /// Constructs a map that will support the insertion of maxSize key-value\n  /// pairs without exceeding the max load factor.  Load factors of greater\n  /// than 1 are not supported, and once the actual load factor of the\n  /// map approaches 1 the insert performance will suffer.  The capacity\n  /// is limited to 2^30 (about a billion) for the default IndexType,\n  /// beyond which we will throw invalid_argument.\n  explicit AtomicUnorderedInsertMap(\n      size_t maxSize,\n      float maxLoadFactor = 0.8f,\n      const Allocator& alloc = Allocator())\n      : allocator_(alloc) {\n    size_t capacity = size_t(maxSize / std::min(1.0f, maxLoadFactor) + 128);\n    size_t avail = size_t{1} << (8 * sizeof(IndexType) - 2);\n    if (capacity > avail && maxSize < avail) {\n      // we'll do our best\n      capacity = avail;\n    }\n    if (capacity < maxSize || capacity > avail) {\n      throw std::invalid_argument(\n          \"AtomicUnorderedInsertMap capacity must fit in IndexType with 2 bits \"\n          \"left over\");\n    }\n\n    numSlots_ = capacity;\n    slotMask_ = folly::nextPowTwo(capacity * 4) - 1;\n    allocRequested_ = sizeof(Slot) * capacity;\n    slots_ = reinterpret_cast<Slot*>(allocator_.allocate(allocRequested_));\n    zeroFillSlots();\n    // mark the zero-th slot as in-use but not valid, since that happens\n    // to be our nil value\n    slots_[0].stateUpdate(EMPTY, CONSTRUCTING);\n  }\n\n  ~AtomicUnorderedInsertMap() {\n    if (!SkipKeyValueDeletion) {\n      for (size_t i = 1; i < numSlots_; ++i) {\n        slots_[i].~Slot();\n      }\n    }\n    allocator_.deallocate(reinterpret_cast<char*>(slots_), allocRequested_);\n  }\n\n  /// Searches for the key, returning (iter,false) if it is found.\n  /// If it is not found calls the functor Func with a void* argument\n  /// that is raw storage suitable for placement construction of a Value\n  /// (see raw_value_type), then returns (iter,true).  May call Func and\n  /// then return (iter,false) if there are other concurrent writes, in\n  /// which case the newly constructed value will be immediately destroyed.\n  ///\n  /// This function does not block other readers or writers.  If there\n  /// are other concurrent writes, many parallel calls to func may happen\n  /// and only the first one to complete will win.  The values constructed\n  /// by the other calls to func will be destroyed.\n  ///\n  /// Usage:\n  ///\n  ///  AtomicUnorderedInsertMap<std::string,std::string> memo;\n  ///\n  ///  auto value = memo.findOrConstruct(key, [=](void* raw) {\n  ///    new (raw) std::string(computation(key));\n  ///  })->first;\n  template <typename Func>\n  std::pair<const_iterator, bool> findOrConstruct(const Key& key, Func&& func) {\n    auto const slot = keyToSlotIdx(key);\n    auto prev = slots_[slot].headAndState_.load(std::memory_order_acquire);\n\n    auto existing = find(key, slot);\n    if (existing != 0) {\n      return std::make_pair(ConstIterator(*this, existing), false);\n    }\n\n    // The copying of key and the calling of func and find can throw exceptions.\n    // Nothing else in this function can throw an exception. In the event of an\n    // exception, deallocate as if the KV was beaten in a concurrent addition.\n    const auto idx = allocateNear(slot);\n    auto guardSlot = folly::makeGuard([&] {\n      slots_[idx].stateUpdate(CONSTRUCTING, EMPTY);\n    });\n    value_type* addr = slots_[idx].keyValue();\n    new (static_cast<void*>(std::addressof(addr->first))) Key(key);\n    auto guardKey = folly::makeGuard([&] { addr->first.~Key(); });\n    new (static_cast<void*>(std::addressof(addr->second))) Value(func());\n    auto guardMapped = folly::makeGuard([&] { addr->second.~Value(); });\n\n    while (true) {\n      slots_[idx].next_ = prev >> 2;\n\n      // we can merge the head update and the CONSTRUCTING -> LINKED update\n      // into a single CAS if slot == idx (which should happen often)\n      auto after = idx << 2;\n      if (slot == idx) {\n        after += LINKED;\n      } else {\n        after += (prev & 3);\n      }\n\n      if (slots_[slot].headAndState_.compare_exchange_strong(prev, after)) {\n        // success\n        if (idx != slot) {\n          slots_[idx].stateUpdate(CONSTRUCTING, LINKED);\n        }\n        guardMapped.dismiss();\n        guardKey.dismiss();\n        guardSlot.dismiss();\n        return std::make_pair(ConstIterator(*this, idx), true);\n      }\n      // compare_exchange_strong updates its first arg on failure, so\n      // there is no need to reread prev\n\n      existing = find(key, slot);\n      if (existing != 0) {\n        // our allocated key and value are no longer needed\n        // and so the guards expire and invoke the cleanups\n        return std::make_pair(ConstIterator(*this, existing), false);\n      }\n    }\n  }\n\n  /// This isn't really emplace, but it is what we need to test.\n  /// Eventually we can duplicate all of the std::pair constructor\n  /// forms, including a recursive tuple forwarding template\n  /// http://functionalcpp.wordpress.com/2013/08/28/tuple-forwarding/).\n  template <class K, class V>\n  std::pair<const_iterator, bool> emplace(const K& key, V&& value) {\n    return findOrConstruct(key, [&] { return Value(std::forward<V>(value)); });\n  }\n\n  const_iterator find(const Key& key) const {\n    return ConstIterator(*this, find(key, keyToSlotIdx(key)));\n  }\n\n  const_iterator cbegin() const {\n    IndexType slot = numSlots_ - 1;\n    while (slot > 0 && slots_[slot].state() != LINKED) {\n      --slot;\n    }\n    return ConstIterator(*this, slot);\n  }\n  const_iterator begin() const { return cbegin(); }\n\n  const_iterator cend() const { return ConstIterator(*this, 0); }\n  const_iterator end() const { return cend(); }\n\n private:\n  enum : IndexType {\n    kMaxAllocationTries = 1000, // after this we throw\n  };\n\n  enum BucketState : IndexType {\n    EMPTY = 0,\n    CONSTRUCTING = 1,\n    LINKED = 2,\n  };\n\n  /// Lock-free insertion is easiest by prepending to collision chains.\n  /// A large chaining hash table takes two cache misses instead of\n  /// one, however.  Our solution is to colocate the bucket storage and\n  /// the head storage, so that even though we are traversing chains we\n  /// are likely to stay within the same cache line.  Just make sure to\n  /// traverse head before looking at any keys.  This strategy gives us\n  /// 32 bit pointers and fast iteration.\n  struct Slot {\n    /// The bottom two bits are the BucketState, the rest is the index\n    /// of the first bucket for the chain whose keys map to this slot.\n    /// When things are going well the head usually links to this slot,\n    /// but that doesn't always have to happen.\n    Atom<IndexType> headAndState_;\n\n    /// The next bucket in the chain\n    IndexType next_;\n\n    /// Key and Value\n    aligned_storage_for_t<value_type> raw_;\n\n    ~Slot() {\n      auto s = state();\n      assert(s == EMPTY || s == LINKED);\n      if (s == LINKED) {\n        keyValue()->second.~Value();\n        keyValue()->first.~Key();\n      }\n    }\n\n    BucketState state() const {\n      return BucketState(headAndState_.load(std::memory_order_acquire) & 3);\n    }\n\n    void stateUpdate(BucketState before, BucketState after) {\n      assert(state() == before);\n      headAndState_ += (after - before);\n    }\n\n    value_type* keyValue() {\n      assert(state() != EMPTY);\n      return static_cast<value_type*>(static_cast<void*>(&raw_));\n    }\n\n    const value_type* keyValue() const {\n      assert(state() != EMPTY);\n      return static_cast<const value_type*>(static_cast<const void*>(&raw_));\n    }\n  };\n\n  // We manually manage the slot memory so we can bypass initialization\n  // (by getting a zero-filled mmap chunk) and optionally destruction of\n  // the slots\n\n  size_t allocRequested_;\n  size_t numSlots_;\n\n  /// tricky, see keyToSlotIdx\n  size_t slotMask_;\n\n  Allocator allocator_;\n  Slot* slots_;\n\n  IndexType keyToSlotIdx(const Key& key) const {\n    size_t h = hasher()(key);\n    h &= slotMask_;\n    while (h >= numSlots_) {\n      h -= numSlots_;\n    }\n    return h;\n  }\n\n  IndexType find(const Key& key, IndexType slot) const {\n    KeyEqual ke = {};\n    auto hs = slots_[slot].headAndState_.load(std::memory_order_acquire);\n    for (slot = hs >> 2; slot != 0; slot = slots_[slot].next_) {\n      if (ke(key, slots_[slot].keyValue()->first)) {\n        return slot;\n      }\n    }\n    return 0;\n  }\n\n  /// Allocates a slot and returns its index.  Tries to put it near\n  /// slots_[start].\n  IndexType allocateNear(IndexType start) {\n    for (IndexType tries = 0; tries < kMaxAllocationTries; ++tries) {\n      auto slot = allocationAttempt(start, tries);\n      auto prev = slots_[slot].headAndState_.load(std::memory_order_acquire);\n      if ((prev & 3) == EMPTY &&\n          slots_[slot].headAndState_.compare_exchange_strong(\n              prev, prev + CONSTRUCTING - EMPTY)) {\n        return slot;\n      }\n    }\n    throw std::bad_alloc();\n  }\n\n  /// Returns the slot we should attempt to allocate after tries failed\n  /// tries, starting from the specified slot.  This is pulled out so we\n  /// can specialize it differently during deterministic testing\n  IndexType allocationAttempt(IndexType start, IndexType tries) const {\n    if (FOLLY_LIKELY(tries < 8 && start + tries < numSlots_)) {\n      return IndexType(start + tries);\n    } else {\n      IndexType rv;\n      if (sizeof(IndexType) <= 4) {\n        rv = IndexType(folly::Random::rand32(numSlots_));\n      } else {\n        rv = IndexType(folly::Random::rand64(numSlots_));\n      }\n      assert(rv < numSlots_);\n      return rv;\n    }\n  }\n\n  void zeroFillSlots() {\n    using folly::detail::GivesZeroFilledMemory;\n    if (!GivesZeroFilledMemory<Allocator>::value) {\n      memset(static_cast<void*>(slots_), 0, allocRequested_);\n    }\n  }\n};\n\n/// AtomicUnorderedInsertMap64 is just a type alias that makes it easier\n/// to select a 64 bit slot index type.  Use this if you need a capacity\n/// bigger than 2^30 (about a billion).  This increases memory overheads,\n/// obviously.\ntemplate <\n    typename Key,\n    typename Value,\n    typename Hash = std::hash<Key>,\n    typename KeyEqual = std::equal_to<Key>,\n    bool SkipKeyValueDeletion =\n        (std::is_trivially_destructible<Key>::value &&\n         std::is_trivially_destructible<Value>::value),\n    template <typename> class Atom = std::atomic,\n    typename Allocator = folly::detail::MallocAlloc>\nusing AtomicUnorderedInsertMap64 = AtomicUnorderedInsertMap<\n    Key,\n    Value,\n    Hash,\n    KeyEqual,\n    SkipKeyValueDeletion,\n    Atom,\n    uint64_t,\n    Allocator>;\n\n/// MutableAtom is a tiny wrapper that gives you the option of atomically\n/// updating values inserted into an AtomicUnorderedInsertMap<K,\n/// MutableAtom<V>>.  This relies on AtomicUnorderedInsertMap's guarantee\n/// that it doesn't move values.\ntemplate <typename T, template <typename> class Atom = std::atomic>\nstruct MutableAtom {\n  mutable Atom<T> data;\n\n  explicit MutableAtom(const T& init) : data(init) {}\n};\n\n/// MutableData is a tiny wrapper that gives you the option of using an\n/// external concurrency control mechanism to updating values inserted\n/// into an AtomicUnorderedInsertMap.\ntemplate <typename T>\nstruct MutableData {\n  mutable T data;\n  explicit MutableData(const T& init) : data(init) {}\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/BUCK",
    "content": "load(\"@bazel_skylib//lib:selects.bzl\", \"selects\")\nload(\"@fbcode_macros//build_defs:auto_headers.bzl\", \"AutoHeaders\")\nload(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs:fb_native_wrapper.bzl\", \"fb_native\")\nload(\"@fbsource//tools/build_defs:fb_xplat_cxx_library.bzl\", \"fb_xplat_cxx_library\")\nload(\"@fbsource//tools/build_defs:fb_xplat_cxx_test.bzl\", \"fb_xplat_cxx_test\")\nload(\n    \"@fbsource//tools/build_defs:platform_defs.bzl\",\n    \"ALL_APPLE_SDKS\",\n    \"ANDROID\",\n    \"APPLE\",\n    \"CXX\",\n    \"FBCODE\",\n    \"IOS\",\n    \"MACOSX\",\n    \"WINDOWS\",\n)\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \":defs.bzl\",\n    \"CXXFLAGS\",\n    \"DEFAULT_APPLE_SDKS\",\n    \"FBANDROID_CPPFLAGS\",\n    \"FBANDROID_CXXFLAGS\",\n    \"FBOBJC_CXXFLAGS\",\n    \"cpp_flags\",\n    \"folly_xplat_cxx_test\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nCPPFLAGS = cpp_flags()\n\nfb_dirsync_cpp_library(\n    name = \"memory\",\n    headers = [\"Memory.h\"],\n    fbobjc_complete_nullability = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":constexpr_math\",\n        \":likely\",\n        \":portability\",\n        \":traits\",\n        \":utility\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:align\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:thunk\",\n        \"//folly/memory:malloc\",\n        \"//folly/portability:config\",\n        \"//folly/portability:constexpr\",\n        \"//folly/portability:malloc\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"observer_container\",\n    headers = [\n        \"ObserverContainer.h\",\n    ],\n    fbobjc_complete_nullability = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":constructor_callback_list\",\n        \":function\",\n        \":optional\",\n        \":scope_guard\",\n        \":small_vector\",\n        \"//folly/io/async:destructor_check\",\n        \"//folly/lang:switch\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"operation_cancelled\",\n    headers = [\"OperationCancelled.h\"],\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"optional\",\n    headers = [\"Optional.h\"],\n    # Without modules, the `-Wextra` flag added in BUILD_MODE.bzl only affected\n    # the cpp files under `folly`, but with modules it also ends up getting\n    # baked into the PCM files of header units.\n    #\n    # This is a workaround specifically for the comparison operators of\n    # `Optional`, since those trigger `-Wsign-compare` warnings too often.\n    compiler_flags = select({\n        \"DEFAULT\": [\"-Wno-error=sign-compare\"],\n        \"ovr_config//compiler:cl\": [\"/wd4018\"],\n    }),\n    fbobjc_complete_nullability = True,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":portability\",\n        \":traits\",\n        \":utility\",\n        \"//folly/coro:coroutine\",\n        \"//folly/hash:traits\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"benchmark_util\",\n    srcs = [\n        \"BenchmarkUtil.cpp\",\n    ],\n    headers = [\n        \"BenchmarkUtil.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly/lang:align\",\n        \"//folly/portability:sched\",\n        \"//folly/portability:windows\",\n        \"//folly/system/arch:x86\",\n    ],\n    exported_deps = [\n        \":portability\",\n        \"//folly/lang:hint\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"benchmark\",\n    srcs = [\n        \"Benchmark.cpp\",\n        # Colocated here to avoid circular dep (Adaptive needs Benchmark types).\n        \"detail/BenchmarkAdaptive.cpp\",\n    ],\n    headers = [\n        \"Benchmark.h\",\n        \"detail/BenchmarkAdaptive.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":file_util\",\n        \":map_util\",\n        \":overload\",\n        \":random\",\n        \":string\",\n        \"//folly/detail:perf_scoped\",\n        \"//folly/json:dynamic\",\n        \"//folly/stats:streaming_stats\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":benchmark_util\",\n        \":portability\",\n        \":preprocessor\",\n        \":range\",\n        \":scope_guard\",\n        \":traits\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:hint\",\n        \"//folly/portability:gflags\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_regex\"),\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"scope_guard\",\n    srcs = [\"ScopeGuard.cpp\"],\n    headers = [\"ScopeGuard.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":portability\",\n        \":preprocessor\",\n        \":utility\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:uncaught_exceptions\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"synchronized\",\n    headers = [\"Synchronized.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":function\",\n        \":likely\",\n        \":preprocessor\",\n        \":shared_mutex\",\n        \":traits\",\n        \":utility\",\n        \"//folly/container:foreach\",\n        \"//folly/functional:apply_tuple\",\n        \"//folly/synchronization:lock\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"singleton\",\n    srcs = [\n        \"Singleton.cpp\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//compiler:clang\": [\n            \"-Wno-invalid-noreturn\",\n        ],\n    }),\n    cxx_deps = [\n        \"//xplat/third-party/linker_lib:dl\",\n        \"//third-party/toolchains:rt\",\n    ],\n    exported_preprocessor_flags = select({\n        \"//xplat/folly/buck_config:folly-singleton-schedule-at-exit-disabled\": [\n            \"-DFOLLY_SINGLETON_SKIP_SCHEDULE_ATEXIT=1\",\n        ],\n        \"DEFAULT\": [],\n    }) + select({\n        # Symbolizer is not available for all platforms, disable it for now.\n        \"DEFAULT\": [\"-DFOLLY_HAVE_DWARF=0\"],\n        \"ovr_config//os:linux\": [],\n    }),\n    fbandroid_deps = [\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n    raw_headers = [\n        \"Singleton-inl.h\",\n        \"Singleton.h\",\n        # Instead of depending on experimental_symbolizer_symbolizer, reference always-included headers here.\n        \"debugging/symbolizer/Dwarf.h\",\n        \"debugging/symbolizer/DwarfImpl.h\",\n        \"debugging/symbolizer/DwarfLineNumberVM.h\",\n        \"debugging/symbolizer/DwarfSection.h\",\n        \"debugging/symbolizer/DwarfUtil.h\",\n        \"debugging/symbolizer/Elf.h\",\n        \"debugging/symbolizer/Elf-inl.h\",\n        \"debugging/symbolizer/ElfCache.h\",\n        \"debugging/symbolizer/Symbolizer.h\",\n        \"debugging/symbolizer/SymbolizedFrame.h\",\n        \"debugging/symbolizer/SymbolizePrinter.h\",\n        \"debugging/symbolizer/StackTrace.h\",\n        \"experimental/symbolizer/Dwarf.h\",\n        \"experimental/symbolizer/DwarfImpl.h\",\n        \"experimental/symbolizer/DwarfLineNumberVM.h\",\n        \"experimental/symbolizer/DwarfSection.h\",\n        \"experimental/symbolizer/DwarfUtil.h\",\n        \"experimental/symbolizer/Elf.h\",\n        \"experimental/symbolizer/Elf-inl.h\",\n        \"experimental/symbolizer/ElfCache.h\",\n        \"experimental/symbolizer/Symbolizer.h\",\n        \"experimental/symbolizer/SymbolizedFrame.h\",\n        \"experimental/symbolizer/SymbolizePrinter.h\",\n        \"experimental/symbolizer/StackTrace.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:fmt_compile\",\n        \"fbsource//xplat/folly/system:at_fork\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/portability:config\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:unistd\",\n        \"fbsource//xplat/folly/synchronization:baton\",\n        \"//third-party/double-conversion:double-conversion\",\n        \"//third-party/fmt:fmt\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:cancellation_token\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:demangle\",\n        \"//xplat/folly:exception\",\n        \"//xplat/folly:executor\",\n        \"//xplat/folly:fbstring\",\n        \"//xplat/folly:file_util\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly:synchronized\",\n        \"//xplat/folly/concurrency:core_cached_shared_ptr\",\n        \"//xplat/folly/concurrency/memory:read_mostly_shared_ptr\",\n        \"//xplat/folly/container:evicting_cache_map\",\n        \"//xplat/folly/detail:singleton\",\n        \"//xplat/folly/detail:static_singleton_manager\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/memory:reentrant_allocator\",\n        \"//xplat/folly/memory:sanitize_leak\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux\": [\"//xplat/folly/experimental/symbolizer:symbolizer\"],\n    }),\n)\n\nfb_dirsync_cpp_library(\n    name = \"thread_local\",\n    headers = [\"ThreadLocal.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":likely\",\n        \":portability\",\n        \":scope_guard\",\n        \":shared_mutex\",\n        \"//folly/detail:thread_local_detail\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"singleton_thread_local\",\n    srcs = [\"SingletonThreadLocal.cpp\"],\n    headers = [\"SingletonThreadLocal.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":scope_guard\",\n        \":thread_local\",\n        \"//folly/detail:iterators\",\n        \"//folly/detail:singleton\",\n        \"//folly/detail:unique_instance\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:hint\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"random\",\n    srcs = [\n        \"Random.cpp\",\n    ],\n    headers = [\n        \"Random.h\",\n        \"Random-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":cpp_attributes\",\n        \":singleton_thread_local\",\n        \":thread_local\",\n        \"//folly/detail:file_util_detail\",\n        \"//folly/portability:config\",\n        \"//folly/portability:sys_time\",\n        \"//folly/portability:unistd\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n    exported_deps = [\n        \":portability\",\n        \":traits\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:bits\",\n        \"//folly/random:xoshiro256pp\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:windows\": [\"fbsource//third-party/toolchains/win:advapi32.lib\"],\n    }),\n)\n\nfb_dirsync_cpp_library(\n    name = \"file\",\n    srcs = [\"File.cpp\"],\n    headers = [\"File.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":exception\",\n        \":file_util\",\n        \":scope_guard\",\n        \"//folly/portability:fmt_compile\",\n        \"//folly/portability:sys_file\",\n    ],\n    exported_deps = [\n        \":exception_wrapper\",\n        \":expected\",\n        \":file_util\",  # @manual\n        \":portability\",\n        \":range\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"file_util\",\n    srcs = [\"FileUtil.cpp\"],\n    headers = [\"FileUtil.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/detail:file_util_detail\",\n        \"//folly/detail:file_util_vector_detail\",\n        \"//folly/net:net_ops\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:stdlib\",\n        \"//folly/portability:sys_file\",\n        \"//folly/portability:sys_stat\",\n    ],\n    exported_deps = [\n        \":portability\",\n        \":range\",\n        \":scope_guard\",\n        \"//folly/net:network_socket\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:sys_uio\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"executor\",\n    srcs = [\n        \"Executor.cpp\",\n    ],\n    force_static = select({\n        \"DEFAULT\": False,\n        \"ovr_config//build_mode:arvr_mode\": select({\n            \"DEFAULT\": False,\n            \"ovr_config//os:macos\": True,\n        }),\n    }),\n    raw_headers = [\n        \"Executor.h\",\n    ],\n    exported_deps = [\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:exception_string\",\n        \"//xplat/folly:function\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"subprocess\",\n    srcs = [\n        \"Subprocess.cpp\",\n    ],\n    cxx_deps = [\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n    fbandroid_deps = [\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n    fbandroid_use_host_platform = True,\n    force_static = False,\n    platforms = (CXX, ANDROID, APPLE),\n    raw_headers = [\n        \"Subprocess.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:dirent\",\n        \":conv\",\n        \":logging_logging\",\n        \":scope_guard\",\n        \":string\",\n        \"//third-party/boost:boost_range\",\n        \"//xplat/folly/lang:assume\",\n        \"//xplat/folly/portability:fcntl\",\n        \"//xplat/folly/portability:sockets\",\n        \"//xplat/folly/portability:stdlib\",\n        \"//xplat/folly/portability:sys_syscall\",\n        \"//xplat/folly/portability:unistd\",\n        \"//xplat/folly/system:at_fork\",\n        \"//xplat/folly/system:shell\",\n    ],\n    exported_deps = [\n        \":exception\",\n        \":file\",\n        \":file_util\",\n        \":function\",\n        \":gen_string\",\n        \":io_iobuf\",\n        \":map_util\",\n        \":optional\",\n        \":portability\",\n        \":range\",\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_container\",\n        \"//xplat/folly/container:span\",\n        \"//xplat/folly/portability:sys_resource\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"uri\",\n    srcs = [\n        \"Uri.cpp\",\n    ],\n    force_static = False,\n    raw_headers = [\n        \"Uri.h\",\n        \"Uri-inl.h\",\n    ],\n    deps = [\n        \"//third-party/boost:boost_regex\",\n        \"//third-party/glog:glog\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"//third-party/boost:boost_regex\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:string\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"range\",\n    headers = [\"Range.h\"],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    fbobjc_complete_nullability = True,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \":cpu_id\",\n        \":likely\",\n        \":portability\",\n        \":traits\",\n        \"//folly/detail:range_common\",\n        \"//folly/detail:range_simd\",\n        \"//folly/hash:spooky_hash_v2\",\n        \"//folly/lang:c_string\",\n        \"//folly/lang:exception\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fbvector\",\n    headers = [\n        \"FBVector.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container:fbvector\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fixed_string\",\n    headers = [\"FixedString.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":constexpr_math\",\n        \":portability\",\n        \":range\",\n        \":utility\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:ordering\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fbstring\",\n    headers = [\"FBString.h\"],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \":c_portability\",\n        \":cpp_attributes\",\n        \":likely\",\n        \":portability\",\n        \":traits\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:checked_math\",\n        \"//folly/lang:exception\",\n        \"//folly/memory:malloc\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"format\",\n    srcs = [\n        \"Format.cpp\",\n    ],\n    headers = [\n        \"Format.h\",\n        \"Format-inl.h\",\n        \"FormatArg.h\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":constexpr_math\",\n        \"//folly/container:array\",\n    ],\n    exported_deps = [\n        \":c_portability\",\n        \":conv\",\n        \":exception\",\n        \":format_traits\",\n        \":likely\",\n        \":map_util\",\n        \":portability\",\n        \":range\",\n        \":string\",\n        \":traits\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:to_ascii\",\n        \"//folly/portability:windows\",\n    ],\n    external_deps = [\n        \"double_conversion\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"string\",\n    srcs = [\n        \"String.cpp\",\n    ],\n    headers = [\n        \"String.h\",\n        \"String-inl.h\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/container:array\",\n    ],\n    exported_deps = [\n        \":conv\",\n        \":cpp_attributes\",\n        \":exception_string\",\n        \":optional\",\n        \":portability\",\n        \":range\",\n        \":scope_guard\",\n        \":traits\",\n        \":unit\",\n        \"//folly/container:reserve\",\n        \"//folly/detail:simple_simd_string_utils\",\n        \"//folly/detail:split_string_simd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"bits\",\n    headers = [\"Bits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\"//folly/lang:bits\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"hash\",\n    headers = [\"Hash.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/hash:hash\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_detail_checksum_detail\",\n    exported_deps = [\"//xplat/folly/hash/detail:checksum_detail\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_checksum\",\n    exported_deps = [\"//xplat/folly/hash:checksum\"],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"conv\",\n    srcs = [\n        \"Conv.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    export_header_unit = \"include\",\n    export_header_unit_filter = [\"folly/Conv.h\"],\n    fbobjc_complete_nullability = True,\n    raw_headers = [\n        \"Conv.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/fast_float:fast_float\",\n        \"//xplat/folly/lang:safe_assert\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:math\",\n        \"//third-party/double-conversion:double-conversion\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:demangle\",\n        \"//xplat/folly:expected\",\n        \"//xplat/folly:fbstring\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:unit\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/lang:pretty\",\n        \"//xplat/folly/lang:to_ascii\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"dynamic\",\n    apple_sdks = DEFAULT_APPLE_SDKS,\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    export_header_unit = \"include\",\n    export_header_unit_filter = [\".*\"],\n    fbobjc_complete_nullability = True,\n    raw_headers = [\n        \"DynamicConverter.h\",\n        \"dynamic.h\",\n        \"dynamic-inl.h\",\n        \"json.h\",\n    ],\n    deps = [\n        \"//xplat/folly:utility\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/portability:constexpr\",\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_algorithm\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:expected\",\n        \"//xplat/folly:format\",\n        \"//xplat/folly:function\",\n        \"//xplat/folly:json_pointer\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:unicode\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/container:access\",\n        \"//xplat/folly/container:enumerate\",\n        \"//xplat/folly/container:f14_hash\",\n        \"//xplat/folly/detail:iterators\",\n        \"//xplat/folly/json:dynamic\",\n        \"//xplat/folly/lang:assume\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"f14_hash\",\n    deps = [\n        \"//xplat/folly/container:f14_hash\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"indestructible\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"Indestructible.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:utility\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"io_iobuf\",\n    exported_deps = [\n        \"//xplat/folly/io:iobuf\",\n    ],\n)\n\n# xplat/folly/test aliases\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"json_test_util\",\n    platforms = (CXX, ANDROID, APPLE, WINDOWS, FBCODE),\n    raw_headers = [\n        \"test/JsonTestUtil.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/json:json_test_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"json_mock_util\",\n    exported_headers = [\n        \"test/JsonMockUtil.h\",\n    ],\n    platforms = (CXX, ANDROID, APPLE, WINDOWS, FBCODE),\n    exported_deps = [\n        \"//xplat/folly/json:json_mock_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"comparison_operator_test_util\",\n    exported_headers = [\n        \"test/ComparisonOperatorTestUtil.h\",\n    ],\n    platforms = (CXX, ANDROID, APPLE, WINDOWS, FBCODE),\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"cpu_id\",\n    headers = [\"CpuId.h\"],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":portability\",\n        \"//folly/system/arch:x86\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"cpp_attributes\",\n    headers = [\"CppAttributes.h\"],\n    apple_sdks = ALL_APPLE_SDKS,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":portability\"],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"config\",\n    apple_sdks = ALL_APPLE_SDKS,\n    exported_preprocessor_flags = CPPFLAGS,\n    fbandroid_exported_preprocessor_flags = FBANDROID_CPPFLAGS,\n    raw_headers = [\n        \"folly-config.h\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"constexpr_math\",\n    headers = [\"ConstexprMath.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":portability\",\n        \"//folly/lang:checked_math\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"constructor_callback_list\",\n    headers = [\n        \"ConstructorCallbackList.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \":format\",\n        \":function\",\n        \":shared_mutex\",\n        \"//folly/detail:static_singleton_manager\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"math\",\n    headers = [\"Math.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"move_wrapper\",\n    headers = [\"MoveWrapper.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"likely\",\n    headers = [\"Likely.h\"],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/lang:builtin\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"unit\",\n    headers = [\"Unit.h\"],\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"c_portability\",\n    headers = [\"CPortability.h\"],\n    apple_sdks = ALL_APPLE_SDKS,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/portability:config\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"preprocessor\",\n    headers = [\"Preprocessor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":c_portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"portability\",\n    headers = [\"Portability.h\"],\n    apple_sdks = ALL_APPLE_SDKS,\n    fbobjc_complete_nullability = True,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":c_portability\",\n        \"//folly/portability:config\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"traits\",\n    headers = [\"Traits.h\"],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":portability\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"utility\",\n    headers = [\n        \"Utility.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":c_portability\",\n        \":portability\",\n        \":traits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"function\",\n    headers = [\"Function.h\"],\n    fbobjc_complete_nullability = True,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":cpp_attributes\",\n        \":portability\",\n        \":traits\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:align\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:new\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"atomic_hash_map\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"AtomicHashMap.h\",\n        \"AtomicHashMap-inl.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"//xplat/folly:atomic_hash_array\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:thread_cached_int\",\n        \"//xplat/folly/container:foreach\",\n        \"//xplat/folly/detail:atomic_hash_utils\",\n        \"//xplat/folly/detail:iterators\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"atomic_linked_list\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"AtomicIntrusiveLinkedList.h\",\n        \"AtomicLinkedList.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:memory\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"chrono\",\n    headers = [\"Chrono.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":portability\",\n        \"//folly/lang:exception\",\n        \"//folly/portability:time\",\n    ],\n)\n\nfb_native.constraint_setting(\n    name = \"folly_demangle_macos_use_iberty_constraint\",\n)\n\nfb_native.constraint_value(\n    name = \"folly_demangle_macos_disable_iberty\",\n    constraint_setting = \":folly_demangle_macos_use_iberty_constraint\",\n    visibility = [\"PUBLIC\"],\n)\n\nfb_native.config_setting(\n    name = \"folly_demangle_macos_disable_iberty_config\",\n    constraint_values = [\n        \"ovr_config//os/constraints:macos\",\n        \":folly_demangle_macos_disable_iberty\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"demangle\",\n    srcs = [\"Demangle.cpp\"],\n    headers = [\"Demangle.h\"],\n    compiler_flags = selects.with_or({\n        (\n            \"ovr_config//os:android\"\n        ): [\n            \"-Wno-error=deprecated-dynamic-exception-spec\",\n            \"-fno-omit-frame-pointer\",\n        ],\n        \"DEFAULT\": [\"-fno-omit-frame-pointer\"],\n    }) + select({\n        \"DEFAULT\": [],\n        # x86_64-conda-linux-gnu-c++ (conda-forge gcc 11.4.0-13) 11.4.0\n        # fails to compile Demangle.cpp because of unused static functions despite\n        # using [[maybe_unused]] attribute.\n        \"ovr_config//distro/constraints:conda\": [\"-Wno-unused-function\"],\n    }),\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":c_portability\",\n        \":utility\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:c_string\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"antlir//antlir/distro:build-for-distro\": [\"third-party//binutils:iberty\"],\n        \"fbsource//xplat/folly:folly_demangle_macos_disable_iberty_config\": [],\n        \"ovr_config//os:linux\": [\"fbsource//third-party/binutils:iberty-demangle\"],\n        \"ovr_config//os:macos\": [\"fbsource//third-party/binutils:iberty-demangle\"],\n        \"ovr_config//runtime:fbcode\": [\"third-party//binutils:iberty\"],\n    }),\n    exported_deps = [\n        \":fbstring\",\n        \"//folly/portability:config\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"exception\",\n    headers = [\"Exception.h\"],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":conv\",\n        \":fbstring\",\n        \":likely\",\n        \":portability\",\n        \"//folly/portability:sys_types\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_string\",\n    srcs = [\n        \"ExceptionString.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"ExceptionString.h\",\n    ],\n    deps = [\n        \"//xplat/folly:demangle\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/lang:type_info\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:fbstring\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_wrapper\",\n    srcs = [\n        \"ExceptionWrapper.cpp\",\n    ],\n    raw_headers = [\n        \"ExceptionWrapper.h\",\n        \"ExceptionWrapper-inl.h\",\n    ],\n    exported_deps = [\n        \":c_portability\",\n        \":cpp_attributes\",\n        \":demangle\",\n        \":exception_string\",\n        \":fbstring\",\n        \":portability\",\n        \":traits\",\n        \":utility\",\n        \"//xplat/folly/functional:traits\",\n        \"//xplat/folly/lang:assume\",\n        \"//xplat/folly/lang:exception\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"expected\",\n    headers = [\"Expected.h\"],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":c_portability\",\n        \":cpp_attributes\",\n        \":likely\",\n        \":portability\",\n        \":preprocessor\",\n        \":traits\",\n        \":unit\",\n        \":utility\",\n        \"//folly/coro:coroutine\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:hint\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"fmt_utility\",\n    srcs = [\n        \"FmtUtility.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"FmtUtility.h\",\n    ],\n    deps = [\n        \"//xplat/folly:range\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly/ssl:openssl_hash\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:cpp_attributes\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"format_traits\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"FormatTraits.h\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"glog\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"GLog.h\",\n    ],\n    exported_deps = [\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:likely\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"indexed_mem_pool\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"IndexedMemPool.h\",\n    ],\n    exported_deps = [\n        \":portability\",\n        \"//xplat/folly/concurrency:cache_locality\",\n        \"//xplat/folly/portability:sys_mman\",\n        \"//xplat/folly/portability:unistd\",\n        \"//xplat/folly/synchronization:atomic_struct\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"intrusive_list\",\n    headers = [\n        \"IntrusiveList.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container:intrusive_list\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"io_shutdown_socket_set\",\n    exported_deps = [\n        \"//xplat/folly/io:shutdown_socket_set\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"io_socket_option_map\",\n    exported_deps = [\n        \"//xplat/folly/io:socket_option_map\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"map_util\",\n    headers = [\n        \"MapUtil.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container:map_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"micro_lock\",\n    srcs = [\n        \"MicroLock.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"MicroLock.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/synchronization:atomic_notification\",\n        \"fbsource//xplat/folly/synchronization:atomic_ref\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:utility\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"mpmc_queue\",\n    headers = [\"MPMCQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":traits\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/detail:turn_sequencer\",\n        \"//folly/lang:exception\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"network_address\",\n    srcs = [\n        \"IPAddress.cpp\",\n        \"IPAddressV4.cpp\",\n        \"IPAddressV6.cpp\",\n        \"MacAddress.cpp\",\n        \"SocketAddress.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"IPAddress.h\",\n        \"IPAddressException.h\",\n        \"IPAddressV4.h\",\n        \"IPAddressV6.h\",\n        \"MacAddress.h\",\n        \"SocketAddress.h\",\n    ],\n    windows_exported_linker_flags = [\n        \"iphlpapi.lib\",\n    ],\n    deps = [\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:small_vector\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/portability:sockets\",\n        \"//third-party/boost:boost\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:constexpr_math\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:cpp_attributes\",\n        \"//xplat/folly:exception\",\n        \"//xplat/folly:expected\",\n        \"//xplat/folly:fbstring\",\n        \"//xplat/folly:format\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly:unit\",\n        \"//xplat/folly/detail:ip_address\",\n        \"//xplat/folly/detail:ip_address_source\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/net:net_ops\",\n        \"//xplat/folly/net:network_socket\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"overload\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"Overload.h\",\n    ],\n    exported_deps = [\n        \":portability\",\n        \":traits\",\n        \"//xplat/folly/functional:invoke\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"shared_mutex\",\n    srcs = [\n        \"SharedMutex.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"SharedMutex.h\",\n    ],\n    deps = [\n        \"//xplat/folly:indestructible\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/synchronization:lock\",\n        \"fbsource//xplat/folly/synchronization:relaxed_atomic\",\n        \"fbsource//xplat/folly/synchronization:sanitize_thread\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly/chrono:hardware\",\n        \"//xplat/folly/concurrency:cache_locality\",\n        \"//xplat/folly/detail:futex\",\n        \"//xplat/folly/system:thread_id\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"small_vector\",\n    headers = [\n        \"small_vector.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container:small_vector\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"sorted_vector_types\",\n    headers = [\n        \"sorted_vector_types.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container:sorted_vector_types\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"spin_lock\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"SpinLock.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"//xplat/folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"stop_watch\",\n    headers = [\"stop_watch.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":chrono\",\n        \":utility\",\n        \"//folly/portability:time\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"varint\",\n    headers = [\"Varint.h\"],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":conv\",\n        \":expected\",\n        \":likely\",\n        \":portability\",\n        \":range\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"external_farmhash_farmhash\",\n    srcs = [\n        \"external/farmhash/farmhash.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"external/farmhash/farmhash.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:config\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = fb_xplat_cxx_library,\n    name = \"futures\",\n    header_namespace = \"\",\n    apple_sdks = DEFAULT_APPLE_SDKS,\n    compiler_flags = CXXFLAGS,\n    fbandroid_compiler_flags = FBANDROID_CXXFLAGS,\n    fbandroid_deps = [\n        \"//xplat/third-party/linker_lib:atomic\",\n    ],\n    fbobjc_compiler_flags = FBOBJC_CXXFLAGS,\n    labels = [\n        # This library \"re-exports\" symbols in :futures_core. Depslint doesn't\n        # support the concept of target-reexports (nor do we know the best\n        # design for such a feature), so we just exclude this target from being\n        # removed.\n        \"depslint_never_remove\",\n        # This is a legacy \"umbrella\" target\n        \"legacy_target\",\n    ],\n    platforms = (ANDROID, APPLE, CXX, WINDOWS),\n    public_include_directories = [\n        \"..\",\n    ],\n    soname = \"libfolly-futures.$(ext)\",\n    visibility = [\"PUBLIC\"],\n    windows_clang_compiler_flags_override = [\n        \"-Wno-deprecated-declarations\",\n    ],\n    windows_msvc_compiler_flags_override = [\n        \"/EHsc\",\n    ],\n    windows_preferred_linkage = \"static\",\n    deps = [\n        \"//xplat/folly:memory\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/boost:boost\",\n        \"fbsource//third-party/double-conversion:double-conversion\",\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//xplat/folly:atomic_hash_map\",\n        \"fbsource//xplat/folly:atomic_linked_list\",\n        \"fbsource//xplat/folly:c_portability\",\n        \"fbsource//xplat/folly:config\",\n        \"fbsource//xplat/folly:constexpr_math\",\n        \"fbsource//xplat/folly:conv\",\n        \"fbsource//xplat/folly:cpp_attributes\",\n        \"fbsource//xplat/folly:demangle\",\n        \"fbsource//xplat/folly:dynamic\",\n        \"fbsource//xplat/folly:exception\",\n        \"fbsource//xplat/folly:exception_string\",\n        \"fbsource//xplat/folly:executor\",\n        \"fbsource//xplat/folly:expected\",\n        \"fbsource//xplat/folly:fbstring\",\n        \"fbsource//xplat/folly:fbvector\",\n        \"fbsource//xplat/folly:file\",\n        \"fbsource//xplat/folly:format\",\n        \"fbsource//xplat/folly:format_traits\",\n        \"fbsource//xplat/folly:function\",\n        \"fbsource//xplat/folly:glog\",\n        \"fbsource//xplat/folly:indestructible\",\n        \"fbsource//xplat/folly:intrusive_list\",\n        \"fbsource//xplat/folly:likely\",\n        \"fbsource//xplat/folly:math\",\n        \"fbsource//xplat/folly:micro_lock\",\n        \"fbsource//xplat/folly:move_wrapper\",\n        \"fbsource//xplat/folly:optional\",\n        \"fbsource//xplat/folly:overload\",\n        \"fbsource//xplat/folly:portability\",\n        \"fbsource//xplat/folly:preprocessor\",\n        \"fbsource//xplat/folly:range\",\n        \"fbsource//xplat/folly:scope_guard\",\n        \"fbsource//xplat/folly:shared_mutex\",\n        \"fbsource//xplat/folly:singleton_thread_local\",\n        \"fbsource//xplat/folly:small_vector\",\n        \"fbsource//xplat/folly:sorted_vector_types\",\n        \"fbsource//xplat/folly:spin_lock\",\n        \"fbsource//xplat/folly:thread_local\",\n        \"fbsource//xplat/folly:traits\",\n        \"fbsource//xplat/folly:unit\",\n        \"fbsource//xplat/folly/concurrency:cache_locality\",\n        \"fbsource//xplat/folly/container:array\",\n        \"fbsource//xplat/folly/container:enumerate\",\n        \"fbsource//xplat/folly/container:f14_hash\",\n        \"fbsource//xplat/folly/container:foreach\",\n        \"fbsource//xplat/folly/container:heap_vector_types\",\n        \"fbsource//xplat/folly/container:iterator\",\n        \"fbsource//xplat/folly/container:sparse_byte_set\",\n        \"fbsource//xplat/folly/detail:futex\",\n        \"fbsource//xplat/folly/functional:apply_tuple\",\n        \"fbsource//xplat/folly/functional:invoke\",\n        \"fbsource//xplat/folly/functional:partial\",\n        \"fbsource//xplat/folly/futures:portability\",\n        \"fbsource//xplat/folly/futures/detail:core\",\n        \"fbsource//xplat/folly/futures/detail:types\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/lang:bits\",\n        \"fbsource//xplat/folly/lang:checked_math\",\n        \"fbsource//xplat/folly/lang:exception\",\n        \"fbsource//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"fbsource//xplat/folly/lang:uncaught_exceptions\",\n        \"fbsource//xplat/folly/memory:arena\",\n        \"fbsource//xplat/folly/memory:malloc\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:atomic\",\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"fbsource//xplat/folly/portability:dirent\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:iovec\",\n        \"fbsource//xplat/folly/portability:math\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:stdlib\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/portability:sys_stat\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:sys_time\",\n        \"fbsource//xplat/folly/portability:syslog\",\n        \"fbsource//xplat/folly/portability:time\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \"fbsource//xplat/folly/synchronization:call_once\",\n        \"fbsource//xplat/folly/synchronization:micro_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:pico_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"fbsource//xplat/folly/system:thread_id\",\n        \"fbsource//xplat/folly/system:thread_name\",\n        \"fbsource//xplat/third-party/linker_lib:atomic\",\n        \":futures_barrier\",\n        \":futures_core\",\n        \":futures_future_splitter\",\n        \":futures_shared_promise\",\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_system\",\n        \"//xplat/folly:singleton\",\n        \"//xplat/folly:try\",\n        \"//xplat/folly/detail:turn_sequencer\",\n        \"//xplat/folly/executors:executor_with_priority\",\n        \"//xplat/folly/executors:inline_executor\",\n        \"//xplat/folly/executors:manual_executor\",\n        \"//xplat/folly/executors:queued_immediate_executor\",\n        \"//xplat/folly/executors:timed_drivable_executor\",\n        \"//xplat/folly/experimental/coro:traits\",\n        \"//xplat/folly/io/async:async_base\",\n        # Deps merged from :extended_light\n        \"//xplat/folly:chrono\",\n        \"//xplat/folly:indexed_mem_pool\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly:mpmc_queue\",\n        \"//xplat/folly:random\",\n        \"//xplat/folly:synchronized\",\n        \"//xplat/folly/concurrency:concurrent_hash_map\",\n        \"//xplat/folly/concurrency:priority_unbounded_queue_set\",\n        \"//xplat/folly/concurrency:unbounded_queue\",\n        \"//xplat/folly/container:bit_iterator\",\n        \"//xplat/folly/container:evicting_cache_map\",\n        \"//xplat/folly/detail:socket_fast_open\",\n        \"//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"//xplat/folly/executors:global_executor\",\n        \"//xplat/folly/executors:io_thread_pool_executor\",\n        \"//xplat/folly/executors:serial_executor\",\n        \"//xplat/folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"//xplat/folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"//xplat/folly/executors/task_queue:unbounded_blocking_queue\",\n        \"//xplat/folly/io/async:async_signal_handler\",\n        \"//xplat/folly/io/async:async_socket_base\",\n        \"//xplat/folly/io/async:async_socket_exception\",\n        \"//xplat/folly/io/async:async_udp_server_socket\",\n        \"//xplat/folly/io/async:async_udp_socket\",\n        \"//xplat/folly/io/async:destructor_check\",\n        \"//xplat/folly/io/async/ssl:tls_definitions\",\n        \"//xplat/folly/portability:event\",\n        \"//xplat/folly/synchronization:hazptr\",\n        \"//xplat/folly/synchronization:lifo_sem\",\n        \"//xplat/folly/tracing:scoped_trace_section\",\n        \"//xplat/folly/tracing:static_tracepoint\",\n        \"//xplat/third-party/event:event\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"test_test_utils\",\n    raw_headers = [\n        \"test/TestUtils.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:exception_string\",\n        \"//xplat/folly:fbstring\",\n        \"//xplat/folly:fixed_string\",\n        \"//xplat/folly:range\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"test_socket_address_test_helper\",\n    srcs = [\n        \"test/SocketAddressTestHelper.cpp\",\n    ],\n    raw_headers = [\n        \"test/SocketAddressTestHelper.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:sockets\",\n        \"//xplat/folly/net:tcpinfo_dispatcher\",\n    ],\n)\n\n# Folly test targets\nnon_fbcode_target(\n    _kind = fb_xplat_cxx_library,\n    name = \"test-headers\",\n    header_namespace = \"\",\n    apple_sdks = DEFAULT_APPLE_SDKS,\n    force_static = True,\n    platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS),\n    public_include_directories = [\n        \"..\",\n    ],\n    visibility = [\"PUBLIC\"],\n    deps = [\n        \":memory\",\n        \"//xplat/folly/container/test:f14_test_util\",\n        \"//xplat/folly/container/test:tracking_types\",\n        \"//xplat/folly/experimental/flat_combining/test:flat_combining_examples\",\n        \"//xplat/folly/experimental/flat_combining/test:flat_combining_test_helpers\",\n        \"//xplat/folly/tracing/test:static_tracepoint_test_module\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = fb_xplat_cxx_library,\n    name = \"test-util\",\n    header_namespace = \"\",\n    apple_sdks = DEFAULT_APPLE_SDKS,\n    compiler_flags = CXXFLAGS + [\n        \"-Wno-narrowing\",\n    ],\n    fbandroid_compiler_flags = FBANDROID_CXXFLAGS,\n    fbandroid_deps = [\n        \"//xplat/third-party/linker_lib:atomic\",\n    ],\n    fbobjc_compiler_flags = FBOBJC_CXXFLAGS,\n    platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS),\n    visibility = [\"PUBLIC\"],\n    deps = [\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_regex\",\n        \"//xplat/folly:memory\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/boost:boost\",\n        \"fbsource//third-party/double-conversion:double-conversion\",\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//xplat/folly:atomic_hash_map\",\n        \"fbsource//xplat/folly:atomic_linked_list\",\n        \"fbsource//xplat/folly:c_portability\",\n        \"fbsource//xplat/folly:chrono\",\n        \"fbsource//xplat/folly:config\",\n        \"fbsource//xplat/folly:constexpr_math\",\n        \"fbsource//xplat/folly:conv\",\n        \"fbsource//xplat/folly:cpp_attributes\",\n        \"fbsource//xplat/folly:demangle\",\n        \"fbsource//xplat/folly:dynamic\",\n        \"fbsource//xplat/folly:exception\",\n        \"fbsource//xplat/folly:exception_string\",\n        \"fbsource//xplat/folly:executor\",\n        \"fbsource//xplat/folly:expected\",\n        \"fbsource//xplat/folly:fbstring\",\n        \"fbsource//xplat/folly:fbvector\",\n        \"fbsource//xplat/folly:file\",\n        \"fbsource//xplat/folly:format\",\n        \"fbsource//xplat/folly:format_traits\",\n        \"fbsource//xplat/folly:function\",\n        \"fbsource//xplat/folly:glog\",\n        \"fbsource//xplat/folly:indestructible\",\n        \"fbsource//xplat/folly:indexed_mem_pool\",\n        \"fbsource//xplat/folly:intrusive_list\",\n        \"fbsource//xplat/folly:likely\",\n        \"fbsource//xplat/folly:math\",\n        \"fbsource//xplat/folly:memory\",\n        \"fbsource//xplat/folly:micro_lock\",\n        \"fbsource//xplat/folly:move_wrapper\",\n        \"fbsource//xplat/folly:mpmc_queue\",\n        \"fbsource//xplat/folly:observer_container\",\n        \"fbsource//xplat/folly:optional\",\n        \"fbsource//xplat/folly:overload\",\n        \"fbsource//xplat/folly:portability\",\n        \"fbsource//xplat/folly:preprocessor\",\n        \"fbsource//xplat/folly:random\",\n        \"fbsource//xplat/folly:range\",\n        \"fbsource//xplat/folly:scope_guard\",\n        \"fbsource//xplat/folly:shared_mutex\",\n        \"fbsource//xplat/folly:singleton\",\n        \"fbsource//xplat/folly:singleton_thread_local\",\n        \"fbsource//xplat/folly:small_vector\",\n        \"fbsource//xplat/folly:sorted_vector_types\",\n        \"fbsource//xplat/folly:spin_lock\",\n        \"fbsource//xplat/folly:synchronized\",\n        \"fbsource//xplat/folly:thread_local\",\n        \"fbsource//xplat/folly:traits\",\n        \"fbsource//xplat/folly:unit\",\n        \"fbsource//xplat/folly/concurrency:cache_locality\",\n        \"fbsource//xplat/folly/concurrency:concurrent_hash_map\",\n        \"fbsource//xplat/folly/concurrency:priority_unbounded_queue_set\",\n        \"fbsource//xplat/folly/concurrency:unbounded_queue\",\n        \"fbsource//xplat/folly/container:array\",\n        \"fbsource//xplat/folly/container:bit_iterator\",\n        \"fbsource//xplat/folly/container:enumerate\",\n        \"fbsource//xplat/folly/container:evicting_cache_map\",\n        \"fbsource//xplat/folly/container:f14_hash\",\n        \"fbsource//xplat/folly/container:foreach\",\n        \"fbsource//xplat/folly/container:heap_vector_types\",\n        \"fbsource//xplat/folly/container:iterator\",\n        \"fbsource//xplat/folly/container:sparse_byte_set\",\n        \"fbsource//xplat/folly/detail:futex\",\n        \"fbsource//xplat/folly/detail:socket_fast_open\",\n        \"fbsource//xplat/folly/detail:turn_sequencer\",\n        \"fbsource//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:global_executor\",\n        \"fbsource//xplat/folly/executors:inline_executor\",\n        \"fbsource//xplat/folly/executors:io_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:serial_executor\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/functional:apply_tuple\",\n        \"fbsource//xplat/folly/functional:invoke\",\n        \"fbsource//xplat/folly/functional:partial\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:typed_io_buf\",\n        \"fbsource//xplat/folly/io/async:async_base\",\n        \"fbsource//xplat/folly/io/async:async_pipe\",\n        \"fbsource//xplat/folly/io/async:async_signal_handler\",\n        \"fbsource//xplat/folly/io/async:async_socket\",\n        \"fbsource//xplat/folly/io/async:async_socket_base\",\n        \"fbsource//xplat/folly/io/async:async_socket_exception\",\n        \"fbsource//xplat/folly/io/async:async_ssl_socket\",\n        \"fbsource//xplat/folly/io/async:async_transport\",\n        \"fbsource//xplat/folly/io/async:async_transport_certificate\",\n        \"fbsource//xplat/folly/io/async:async_udp_server_socket\",\n        \"fbsource//xplat/folly/io/async:async_udp_socket\",\n        \"fbsource//xplat/folly/io/async:decorated_async_transport_wrapper\",\n        \"fbsource//xplat/folly/io/async:destructor_check\",\n        \"fbsource//xplat/folly/io/async:scoped_event_base_thread\",\n        \"fbsource//xplat/folly/io/async:ssl_context\",\n        \"fbsource//xplat/folly/io/async:ssl_options\",\n        \"fbsource//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"fbsource//xplat/folly/io/async/ssl:openssl_utils\",\n        \"fbsource//xplat/folly/io/async/ssl:ssl_errors\",\n        \"fbsource//xplat/folly/io/async/ssl:tls_definitions\",\n        \"fbsource//xplat/folly/lang:bits\",\n        \"fbsource//xplat/folly/lang:checked_math\",\n        \"fbsource//xplat/folly/lang:exception\",\n        \"fbsource//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"fbsource//xplat/folly/lang:uncaught_exceptions\",\n        \"fbsource//xplat/folly/logging:init\",\n        \"fbsource//xplat/folly/logging:init_weak\",\n        \"fbsource//xplat/folly/logging:log_handler\",\n        \"fbsource//xplat/folly/logging:log_level\",\n        \"fbsource//xplat/folly/logging:log_name\",\n        \"fbsource//xplat/folly/logging:logging\",\n        \"fbsource//xplat/folly/logging:rate_limiter\",\n        \"fbsource//xplat/folly/memory:arena\",\n        \"fbsource//xplat/folly/memory:malloc\",\n        \"fbsource//xplat/folly/net:tcpinfo\",\n        \"fbsource//xplat/folly/net:tcpinfo_dispatcher\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:atomic\",\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"fbsource//xplat/folly/portability:dirent\",\n        \"fbsource//xplat/folly/portability:event\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:iovec\",\n        \"fbsource//xplat/folly/portability:math\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:stdlib\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/portability:sys_stat\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:sys_time\",\n        \"fbsource//xplat/folly/portability:syslog\",\n        \"fbsource//xplat/folly/portability:time\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \"fbsource//xplat/folly/ssl:openssl_cert_utils\",\n        \"fbsource//xplat/folly/ssl:openssl_hash\",\n        \"fbsource//xplat/folly/ssl:openssl_ptr_types\",\n        \"fbsource//xplat/folly/ssl:ssl_session\",\n        \"fbsource//xplat/folly/ssl:ssl_session_manager\",\n        \"fbsource//xplat/folly/synchronization:call_once\",\n        \"fbsource//xplat/folly/synchronization:hazptr\",\n        \"fbsource//xplat/folly/synchronization:lifo_sem\",\n        \"fbsource//xplat/folly/synchronization:micro_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:pico_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:rcu\",\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"fbsource//xplat/folly/system:thread_id\",\n        \"fbsource//xplat/folly/system:thread_name\",\n        \"fbsource//xplat/folly/tracing:scoped_trace_section\",\n        \"fbsource//xplat/folly/tracing:static_tracepoint\",\n        \"fbsource//xplat/third-party/event:event\",\n        \"fbsource//xplat/third-party/linker_lib:atomic\",\n        \"fbsource//xplat/third-party/openssl:crypto\",\n        \"fbsource//xplat/third-party/openssl:ssl\",\n        \"//third-party/boost:boost_filesystem\",\n        \"//third-party/boost:boost_system\",\n        \"//xplat/folly:discriminated_ptr\",\n        \"//xplat/folly:stop_watch\",\n        \"//xplat/folly/detail:discriminated_ptr_detail\",\n        \"//xplat/folly/fibers:core\",\n        \"//xplat/folly/fibers:event_base_loop_controller\",\n        \"//xplat/folly/fibers:fiber_manager_map\",\n        \"//xplat/folly/io/async:server_socket\",\n    ],\n)\n\nFOLLY_TEST_SRCS = [\n    \"test/ApplyTupleTest.cpp\",\n    \"test/CallOnceTest.cpp\",\n    \"test/ConvTest.cpp\",\n    # \"test/CpuIdTest.cpp\",\n    \"test/DemangleTest.cpp\",\n    \"test/DynamicConverterTest.cpp\",\n    \"test/FBStringTest.cpp\",\n    \"test/FBVectorTestUtil.cpp\",\n    \"test/FBVectorTest.cpp\",\n    \"test/ForeachTest.cpp\",\n    \"test/FormatTest.cpp\",\n    \"test/FunctionTest.cpp\",\n    \"test/HashTest.cpp\",\n    \"test/LoggingTest.cpp\",\n    \"test/MacAddressTest.cpp\",\n    \"test/MathTest.cpp\",\n    \"test/MoveWrapperTest.cpp\",\n    \"test/OptionalTest.cpp\",\n    \"test/PortabilityTest.cpp\",\n    # \"test/RandomTest.cpp\",\n    # \"test/RangeTest.cpp\",\n    # \"test/ScopeGuardTest.cpp\",\n    \"test/SmallLocksTest.cpp\",\n    \"test/StringTest.cpp\",\n    \"test/TraitsTest.cpp\",\n]\n\nFOLLY_EXTENDED_TEST_SRCS = [\n    \"test/AtomicStructTest.cpp\",\n    \"test/CacheLocalityTest.cpp\",\n    \"test/EvictingCacheMapTest.cpp\",\n    # \"test/ExceptionWrapperTest.cpp\",\n    \"test/MemoryIdlerTest.cpp\",\n]\n\nnon_fbcode_target(\n    _kind = fb_xplat_cxx_test,\n    name = \"folly-test\",\n    srcs = glob(FOLLY_TEST_SRCS),\n    compiler_flags = CXXFLAGS + [\n        \"-Wno-unused-private-field\",\n    ],\n    contacts = [\"oncall+folly@xmail.facebook.com\"],\n    fbandroid_compiler_flags = FBANDROID_CXXFLAGS,\n    fbandroid_cpu_suffixes = [\n        \"64bit\",\n        None,\n    ],\n    fbobjc_compiler_flags = FBOBJC_CXXFLAGS + [\n        \"-Wno-format\",\n        \"-Wno-missing-prototypes\",\n        \"-Wno-shadow\",\n    ],\n    platforms = (ANDROID, APPLE, CXX, WINDOWS),\n    preprocessor_flags = CPPFLAGS + [\n        \"-DFOLLY_SKIP_AS_FAILURE=0\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux\": FBANDROID_CPPFLAGS,\n    }),\n    use_instrumentation_test = True,\n    deps = [\n        \"fbsource//third-party/boost:boost\",\n        \"fbsource//third-party/boost:boost_filesystem\",\n        \"fbsource//third-party/boost:boost_system\",\n        \"fbsource//third-party/double-conversion:double-conversion\",\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//xplat/folly:atomic_hash_map\",\n        \"fbsource//xplat/folly:atomic_linked_list\",\n        \"fbsource//xplat/folly:c_portability\",\n        \"fbsource//xplat/folly:chrono\",\n        \"fbsource//xplat/folly:config\",\n        \"fbsource//xplat/folly:constexpr_math\",\n        \"fbsource//xplat/folly:conv\",\n        \"fbsource//xplat/folly:cpp_attributes\",\n        \"fbsource//xplat/folly:demangle\",\n        \"fbsource//xplat/folly:discriminated_ptr\",\n        \"fbsource//xplat/folly:dynamic\",\n        \"fbsource//xplat/folly:exception\",\n        \"fbsource//xplat/folly:exception_string\",\n        \"fbsource//xplat/folly:executor\",\n        \"fbsource//xplat/folly:expected\",\n        \"fbsource//xplat/folly:fbstring\",\n        \"fbsource//xplat/folly:fbvector\",\n        \"fbsource//xplat/folly:file\",\n        \"fbsource//xplat/folly:format\",\n        \"fbsource//xplat/folly:format_traits\",\n        \"fbsource//xplat/folly:function\",\n        \"fbsource//xplat/folly:glog\",\n        \"fbsource//xplat/folly:indestructible\",\n        \"fbsource//xplat/folly:indexed_mem_pool\",\n        \"fbsource//xplat/folly:intrusive_list\",\n        \"fbsource//xplat/folly:likely\",\n        \"fbsource//xplat/folly:math\",\n        \"fbsource//xplat/folly:memory\",\n        \"fbsource//xplat/folly:micro_lock\",\n        \"fbsource//xplat/folly:move_wrapper\",\n        \"fbsource//xplat/folly:mpmc_queue\",\n        \"fbsource//xplat/folly:observer_container\",\n        \"fbsource//xplat/folly:optional\",\n        \"fbsource//xplat/folly:overload\",\n        \"fbsource//xplat/folly:portability\",\n        \"fbsource//xplat/folly:preprocessor\",\n        \"fbsource//xplat/folly:random\",\n        \"fbsource//xplat/folly:range\",\n        \"fbsource//xplat/folly:scope_guard\",\n        \"fbsource//xplat/folly:shared_mutex\",\n        \"fbsource//xplat/folly:singleton\",\n        \"fbsource//xplat/folly:singleton_thread_local\",\n        \"fbsource//xplat/folly:small_vector\",\n        \"fbsource//xplat/folly:sorted_vector_types\",\n        \"fbsource//xplat/folly:spin_lock\",\n        \"fbsource//xplat/folly:stop_watch\",\n        \"fbsource//xplat/folly:synchronized\",\n        \"fbsource//xplat/folly:thread_local\",\n        \"fbsource//xplat/folly:traits\",\n        \"fbsource//xplat/folly:unit\",\n        \"fbsource//xplat/folly/concurrency:cache_locality\",\n        \"fbsource//xplat/folly/concurrency:concurrent_hash_map\",\n        \"fbsource//xplat/folly/concurrency:priority_unbounded_queue_set\",\n        \"fbsource//xplat/folly/concurrency:unbounded_queue\",\n        \"fbsource//xplat/folly/container:array\",\n        \"fbsource//xplat/folly/container:bit_iterator\",\n        \"fbsource//xplat/folly/container:enumerate\",\n        \"fbsource//xplat/folly/container:evicting_cache_map\",\n        \"fbsource//xplat/folly/container:f14_hash\",\n        \"fbsource//xplat/folly/container:foreach\",\n        \"fbsource//xplat/folly/container:heap_vector_types\",\n        \"fbsource//xplat/folly/container:iterator\",\n        \"fbsource//xplat/folly/container:sparse_byte_set\",\n        \"fbsource//xplat/folly/detail:discriminated_ptr_detail\",\n        \"fbsource//xplat/folly/detail:futex\",\n        \"fbsource//xplat/folly/detail:socket_fast_open\",\n        \"fbsource//xplat/folly/detail:turn_sequencer\",\n        \"fbsource//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:global_executor\",\n        \"fbsource//xplat/folly/executors:inline_executor\",\n        \"fbsource//xplat/folly/executors:io_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:serial_executor\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/fibers:core\",\n        \"fbsource//xplat/folly/fibers:event_base_loop_controller\",\n        \"fbsource//xplat/folly/fibers:fiber_manager_map\",\n        \"fbsource//xplat/folly/functional:apply_tuple\",\n        \"fbsource//xplat/folly/functional:invoke\",\n        \"fbsource//xplat/folly/functional:partial\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:typed_io_buf\",\n        \"fbsource//xplat/folly/io/async:async_base\",\n        \"fbsource//xplat/folly/io/async:async_pipe\",\n        \"fbsource//xplat/folly/io/async:async_signal_handler\",\n        \"fbsource//xplat/folly/io/async:async_socket\",\n        \"fbsource//xplat/folly/io/async:async_socket_base\",\n        \"fbsource//xplat/folly/io/async:async_socket_exception\",\n        \"fbsource//xplat/folly/io/async:async_ssl_socket\",\n        \"fbsource//xplat/folly/io/async:async_transport\",\n        \"fbsource//xplat/folly/io/async:async_transport_certificate\",\n        \"fbsource//xplat/folly/io/async:async_udp_server_socket\",\n        \"fbsource//xplat/folly/io/async:async_udp_socket\",\n        \"fbsource//xplat/folly/io/async:decorated_async_transport_wrapper\",\n        \"fbsource//xplat/folly/io/async:destructor_check\",\n        \"fbsource//xplat/folly/io/async:scoped_event_base_thread\",\n        \"fbsource//xplat/folly/io/async:server_socket\",\n        \"fbsource//xplat/folly/io/async:ssl_context\",\n        \"fbsource//xplat/folly/io/async:ssl_options\",\n        \"fbsource//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"fbsource//xplat/folly/io/async/ssl:openssl_utils\",\n        \"fbsource//xplat/folly/io/async/ssl:ssl_errors\",\n        \"fbsource//xplat/folly/io/async/ssl:tls_definitions\",\n        \"fbsource//xplat/folly/lang:bits\",\n        \"fbsource//xplat/folly/lang:checked_math\",\n        \"fbsource//xplat/folly/lang:exception\",\n        \"fbsource//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"fbsource//xplat/folly/lang:uncaught_exceptions\",\n        \"fbsource//xplat/folly/logging:init\",\n        \"fbsource//xplat/folly/logging:init_weak\",\n        \"fbsource//xplat/folly/logging:log_handler\",\n        \"fbsource//xplat/folly/logging:log_level\",\n        \"fbsource//xplat/folly/logging:log_name\",\n        \"fbsource//xplat/folly/logging:logging\",\n        \"fbsource//xplat/folly/logging:rate_limiter\",\n        \"fbsource//xplat/folly/memory:arena\",\n        \"fbsource//xplat/folly/memory:malloc\",\n        \"fbsource//xplat/folly/net:tcpinfo\",\n        \"fbsource//xplat/folly/net:tcpinfo_dispatcher\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:atomic\",\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"fbsource//xplat/folly/portability:dirent\",\n        \"fbsource//xplat/folly/portability:event\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:iovec\",\n        \"fbsource//xplat/folly/portability:math\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:stdlib\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/portability:sys_stat\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:sys_time\",\n        \"fbsource//xplat/folly/portability:syslog\",\n        \"fbsource//xplat/folly/portability:time\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \"fbsource//xplat/folly/ssl:openssl_cert_utils\",\n        \"fbsource//xplat/folly/ssl:openssl_hash\",\n        \"fbsource//xplat/folly/ssl:openssl_ptr_types\",\n        \"fbsource//xplat/folly/ssl:ssl_session\",\n        \"fbsource//xplat/folly/ssl:ssl_session_manager\",\n        \"fbsource//xplat/folly/synchronization:call_once\",\n        \"fbsource//xplat/folly/synchronization:hazptr\",\n        \"fbsource//xplat/folly/synchronization:lifo_sem\",\n        \"fbsource//xplat/folly/synchronization:micro_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:pico_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:rcu\",\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"fbsource//xplat/folly/system:thread_id\",\n        \"fbsource//xplat/folly/system:thread_name\",\n        \"fbsource//xplat/folly/tracing:scoped_trace_section\",\n        \"fbsource//xplat/folly/tracing:static_tracepoint\",\n        \"fbsource//xplat/third-party/event:event\",\n        \"fbsource//xplat/third-party/linker_lib:atomic\",\n        \"fbsource//xplat/third-party/openssl:crypto\",\n        \"fbsource//xplat/third-party/openssl:ssl\",\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_regex\",\n        \"//third-party/googletest:gmock_main\",\n        \"//xplat/folly:expected\",\n        \"//xplat/folly:fixed_string\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:test-headers\",\n        \"//xplat/folly/lang:keep\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"folly-extended-test\",\n    srcs = glob(FOLLY_EXTENDED_TEST_SRCS),\n    compiler_flags = CXXFLAGS,\n    fbandroid_compiler_flags = FBANDROID_CXXFLAGS,\n    fbobjc_compiler_flags = FBOBJC_CXXFLAGS,\n    platforms = (ANDROID, APPLE, CXX, WINDOWS),\n    preprocessor_flags = CPPFLAGS + select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux\": FBANDROID_CPPFLAGS,\n    }),\n    use_instrumentation_test = True,\n    deps = [\n        \"fbsource//third-party/boost:boost\",\n        \"fbsource//third-party/boost:boost_filesystem\",\n        \"fbsource//third-party/boost:boost_system\",\n        \"fbsource//third-party/double-conversion:double-conversion\",\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//xplat/folly:atomic_hash_map\",\n        \"fbsource//xplat/folly:atomic_linked_list\",\n        \"fbsource//xplat/folly:c_portability\",\n        \"fbsource//xplat/folly:chrono\",\n        \"fbsource//xplat/folly:config\",\n        \"fbsource//xplat/folly:constexpr_math\",\n        \"fbsource//xplat/folly:conv\",\n        \"fbsource//xplat/folly:cpp_attributes\",\n        \"fbsource//xplat/folly:demangle\",\n        \"fbsource//xplat/folly:discriminated_ptr\",\n        \"fbsource//xplat/folly:dynamic\",\n        \"fbsource//xplat/folly:exception\",\n        \"fbsource//xplat/folly:exception_string\",\n        \"fbsource//xplat/folly:executor\",\n        \"fbsource//xplat/folly:expected\",\n        \"fbsource//xplat/folly:fbstring\",\n        \"fbsource//xplat/folly:fbvector\",\n        \"fbsource//xplat/folly:file\",\n        \"fbsource//xplat/folly:format\",\n        \"fbsource//xplat/folly:format_traits\",\n        \"fbsource//xplat/folly:function\",\n        \"fbsource//xplat/folly:glog\",\n        \"fbsource//xplat/folly:indestructible\",\n        \"fbsource//xplat/folly:indexed_mem_pool\",\n        \"fbsource//xplat/folly:intrusive_list\",\n        \"fbsource//xplat/folly:likely\",\n        \"fbsource//xplat/folly:math\",\n        \"fbsource//xplat/folly:memory\",\n        \"fbsource//xplat/folly:micro_lock\",\n        \"fbsource//xplat/folly:move_wrapper\",\n        \"fbsource//xplat/folly:mpmc_queue\",\n        \"fbsource//xplat/folly:observer_container\",\n        \"fbsource//xplat/folly:optional\",\n        \"fbsource//xplat/folly:overload\",\n        \"fbsource//xplat/folly:portability\",\n        \"fbsource//xplat/folly:preprocessor\",\n        \"fbsource//xplat/folly:random\",\n        \"fbsource//xplat/folly:range\",\n        \"fbsource//xplat/folly:scope_guard\",\n        \"fbsource//xplat/folly:shared_mutex\",\n        \"fbsource//xplat/folly:singleton\",\n        \"fbsource//xplat/folly:singleton_thread_local\",\n        \"fbsource//xplat/folly:small_vector\",\n        \"fbsource//xplat/folly:sorted_vector_types\",\n        \"fbsource//xplat/folly:spin_lock\",\n        \"fbsource//xplat/folly:stop_watch\",\n        \"fbsource//xplat/folly:synchronized\",\n        \"fbsource//xplat/folly:thread_local\",\n        \"fbsource//xplat/folly:traits\",\n        \"fbsource//xplat/folly:unit\",\n        \"fbsource//xplat/folly/concurrency:cache_locality\",\n        \"fbsource//xplat/folly/concurrency:concurrent_hash_map\",\n        \"fbsource//xplat/folly/concurrency:priority_unbounded_queue_set\",\n        \"fbsource//xplat/folly/concurrency:unbounded_queue\",\n        \"fbsource//xplat/folly/container:array\",\n        \"fbsource//xplat/folly/container:bit_iterator\",\n        \"fbsource//xplat/folly/container:enumerate\",\n        \"fbsource//xplat/folly/container:evicting_cache_map\",\n        \"fbsource//xplat/folly/container:f14_hash\",\n        \"fbsource//xplat/folly/container:foreach\",\n        \"fbsource//xplat/folly/container:heap_vector_types\",\n        \"fbsource//xplat/folly/container:iterator\",\n        \"fbsource//xplat/folly/container:sparse_byte_set\",\n        \"fbsource//xplat/folly/detail:discriminated_ptr_detail\",\n        \"fbsource//xplat/folly/detail:futex\",\n        \"fbsource//xplat/folly/detail:socket_fast_open\",\n        \"fbsource//xplat/folly/detail:turn_sequencer\",\n        \"fbsource//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:global_executor\",\n        \"fbsource//xplat/folly/executors:inline_executor\",\n        \"fbsource//xplat/folly/executors:io_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:serial_executor\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/fibers:core\",\n        \"fbsource//xplat/folly/fibers:event_base_loop_controller\",\n        \"fbsource//xplat/folly/fibers:fiber_manager_map\",\n        \"fbsource//xplat/folly/functional:apply_tuple\",\n        \"fbsource//xplat/folly/functional:invoke\",\n        \"fbsource//xplat/folly/functional:partial\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:typed_io_buf\",\n        \"fbsource//xplat/folly/io/async:async_base\",\n        \"fbsource//xplat/folly/io/async:async_pipe\",\n        \"fbsource//xplat/folly/io/async:async_signal_handler\",\n        \"fbsource//xplat/folly/io/async:async_socket\",\n        \"fbsource//xplat/folly/io/async:async_socket_base\",\n        \"fbsource//xplat/folly/io/async:async_socket_exception\",\n        \"fbsource//xplat/folly/io/async:async_ssl_socket\",\n        \"fbsource//xplat/folly/io/async:async_transport\",\n        \"fbsource//xplat/folly/io/async:async_transport_certificate\",\n        \"fbsource//xplat/folly/io/async:async_udp_server_socket\",\n        \"fbsource//xplat/folly/io/async:async_udp_socket\",\n        \"fbsource//xplat/folly/io/async:decorated_async_transport_wrapper\",\n        \"fbsource//xplat/folly/io/async:destructor_check\",\n        \"fbsource//xplat/folly/io/async:scoped_event_base_thread\",\n        \"fbsource//xplat/folly/io/async:server_socket\",\n        \"fbsource//xplat/folly/io/async:ssl_context\",\n        \"fbsource//xplat/folly/io/async:ssl_options\",\n        \"fbsource//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"fbsource//xplat/folly/io/async/ssl:openssl_utils\",\n        \"fbsource//xplat/folly/io/async/ssl:ssl_errors\",\n        \"fbsource//xplat/folly/io/async/ssl:tls_definitions\",\n        \"fbsource//xplat/folly/lang:bits\",\n        \"fbsource//xplat/folly/lang:checked_math\",\n        \"fbsource//xplat/folly/lang:exception\",\n        \"fbsource//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"fbsource//xplat/folly/lang:uncaught_exceptions\",\n        \"fbsource//xplat/folly/logging:init\",\n        \"fbsource//xplat/folly/logging:init_weak\",\n        \"fbsource//xplat/folly/logging:log_handler\",\n        \"fbsource//xplat/folly/logging:log_level\",\n        \"fbsource//xplat/folly/logging:log_name\",\n        \"fbsource//xplat/folly/logging:logging\",\n        \"fbsource//xplat/folly/logging:rate_limiter\",\n        \"fbsource//xplat/folly/memory:arena\",\n        \"fbsource//xplat/folly/memory:malloc\",\n        \"fbsource//xplat/folly/net:tcpinfo\",\n        \"fbsource//xplat/folly/net:tcpinfo_dispatcher\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:atomic\",\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"fbsource//xplat/folly/portability:dirent\",\n        \"fbsource//xplat/folly/portability:event\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:iovec\",\n        \"fbsource//xplat/folly/portability:math\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:stdlib\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/portability:sys_stat\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:sys_time\",\n        \"fbsource//xplat/folly/portability:syslog\",\n        \"fbsource//xplat/folly/portability:time\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \"fbsource//xplat/folly/ssl:openssl_cert_utils\",\n        \"fbsource//xplat/folly/ssl:openssl_hash\",\n        \"fbsource//xplat/folly/ssl:openssl_ptr_types\",\n        \"fbsource//xplat/folly/ssl:ssl_session\",\n        \"fbsource//xplat/folly/ssl:ssl_session_manager\",\n        \"fbsource//xplat/folly/synchronization:call_once\",\n        \"fbsource//xplat/folly/synchronization:hazptr\",\n        \"fbsource//xplat/folly/synchronization:lifo_sem\",\n        \"fbsource//xplat/folly/synchronization:micro_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:pico_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:rcu\",\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"fbsource//xplat/folly/system:thread_id\",\n        \"fbsource//xplat/folly/system:thread_name\",\n        \"fbsource//xplat/folly/tracing:scoped_trace_section\",\n        \"fbsource//xplat/folly/tracing:static_tracepoint\",\n        \"fbsource//xplat/third-party/event:event\",\n        \"fbsource//xplat/third-party/linker_lib:atomic\",\n        \"fbsource//xplat/third-party/openssl:crypto\",\n        \"fbsource//xplat/third-party/openssl:ssl\",\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_regex\",\n        \"//third-party/googletest:gmock_main\",\n        \"//xplat/folly:test-headers\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_symbolizer_stack_trace\",\n    exported_deps = [\"//xplat/folly/experimental/symbolizer:stack_trace\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_detail_double_radix_sort\",\n    exported_deps = [\"//xplat/folly/stats/detail:double_radix_sort\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_detail_types\",\n    exported_deps = [\"//xplat/folly/futures/detail:types\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_detail_bucket\",\n    exported_deps = [\"//xplat/folly/stats/detail:bucket\"],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"timeout_queue\",\n    srcs = [\n        \"TimeoutQueue.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"TimeoutQueue.h\",\n    ],\n    deps = [\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_multi_index\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"try\",\n    srcs = [\n        \"Try.cpp\",\n    ],\n    headers = [\n        \"Try.h\",\n        \"Try-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":exception_wrapper\",\n        \":likely\",\n        \":memory\",\n        \":portability\",\n        \":unit\",\n        \":utility\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"thread_cached_int\",\n    headers = [\"ThreadCachedInt.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":likely\",\n        \":thread_local\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"cancellation_token\",\n    srcs = [\"CancellationToken.cpp\"],\n    headers = [\n        \"CancellationToken.h\",\n        \"CancellationToken-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":optional\",\n        \"//folly:scope_guard\",\n        \"//folly/lang:new\",\n        \"//folly/portability:memory\",\n        \"//folly/synchronization/detail:sleeper\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":cpp_attributes\",\n        \":function\",\n        \":operation_cancelled\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_bucketed_time_series\",\n    exported_deps = [\"//xplat/folly/stats:bucketed_time_series\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_histogram\",\n    exported_deps = [\"//xplat/folly/stats:histogram\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_streaming_stats\",\n    exported_deps = [\"//xplat/folly/stats:streaming_stats\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_base\",\n    apple_sdks = DEFAULT_APPLE_SDKS,\n    exported_deps = [\"//xplat/folly/gen:base\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_file\",\n    exported_deps = [\"//xplat/folly/gen:file\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_string\",\n    exported_deps = [\"//xplat/folly/gen:string\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_core\",\n    exported_deps = [\"//xplat/folly/gen:core\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"io_global_shutdown_socket_set\",\n    exported_deps = [\n        \"//xplat/folly/io:global_shutdown_socket_set\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_function_scheduler\",\n    exported_deps = [\n        \"//xplat/folly/experimental:function_scheduler\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_spooky_hash_v1\",\n    exported_deps = [\"//xplat/folly/hash:spooky_hash_v1\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_traits\",\n    exported_deps = [\"//xplat/folly/hash:traits\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_murmur_hash\",\n    exported_deps = [\"//xplat/folly/hash:murmur_hash\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"poly_exception\",\n    headers = [\"PolyException.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_farm_hash\",\n    exported_deps = [\"//xplat/folly/hash:farm_hash\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_init_weak\",\n    exported_deps = [\n        \"//xplat/folly/logging:init_weak\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_portability\",\n    exported_deps = [\"//xplat/folly/futures:portability\"],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"clock_gettime_wrappers\",\n    srcs = [\n        \"ClockGettimeWrappers.cpp\",\n    ],\n    cxx_deps = [\"//third-party/toolchains:rt\"],\n    raw_headers = [\n        \"ClockGettimeWrappers.h\",\n    ],\n    deps = [\n        \":c_portability\",\n        \":likely\",\n        \"//xplat/folly/portability:time\",\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_spooky_hash_v2\",\n    exported_deps = [\"//xplat/folly/hash:spooky_hash_v2\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"concurrent_bit_set\",\n    headers = [\"ConcurrentBitSet.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":portability\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"padded\",\n    headers = [\"Padded.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":portability\",\n        \":traits\",\n        \"//folly/functional:invoke\",\n        \"//folly/portability:sys_types\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_istream\",\n    exported_deps = [\"//xplat/folly/gen:istream\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"replaceable\",\n    headers = [\n        \"Replaceable.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":portability\",\n        \":traits\",\n        \":utility\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_rate_limiter\",\n    exported_deps = [\n        \"//xplat/folly/logging:rate_limiter\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_glog_bridge\",\n    exported_deps = [\n        \"//xplat/folly/logging:glog_bridge\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"rw_spin_lock\",\n    headers = [\"RWSpinLock.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\"//folly/synchronization:rw_spin_lock\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_detail_sliding_window\",\n    exported_deps = [\"//xplat/folly/stats/detail:sliding_window\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fingerprint\",\n    srcs = [\"Fingerprint.cpp\"],\n    headers = [\"Fingerprint.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":portability\",\n        \"//folly/detail:fingerprint_polynomial\",\n    ],\n    exported_deps = [\n        \":range\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"micro_spin_lock\",\n    headers = [\"MicroSpinLock.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\"//folly/synchronization:micro_spin_lock\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_tdigest\",\n    exported_deps = [\"//xplat/folly/stats:tdigest\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"discriminated_ptr\",\n    headers = [\"DiscriminatedPtr.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":likely\",\n        \":portability\",\n        \"//folly/detail:discriminated_ptr_detail\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"group_varint\",\n    srcs = [\n        \"GroupVarint.cpp\",\n    ],\n    headers = [\"GroupVarint.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/container:array\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":portability\",\n        \":range\",\n        \"//folly/detail:group_varint_detail\",\n        \"//folly/lang:bits\",\n        \"//folly/portability:builtins\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_log_name\",\n    exported_deps = [\n        \"//xplat/folly/logging:log_name\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lazy\",\n    headers = [\"Lazy.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":optional\",\n        \"//folly/functional:invoke\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"hash_hash\",\n    exported_deps = [\"//xplat/folly/hash:hash\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"utf8_string\",\n    headers = [\n        \"UTF8String.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":range\",\n    ],\n    exported_external_deps = [\n        (\"boost\", None, \"boost_regex\"),\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"utf8_string_icu\",\n    apple_sdks = (IOS, MACOSX),\n    platforms = (CXX, ANDROID, APPLE),\n    raw_headers = [\n        \"UTF8String.h\",\n    ],\n    deps = [\n        \":range\",\n        \"//third-party/boost:boost_regex_icu\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_threaded_repeating_function_runner\",\n    exported_deps = [\n        \"//xplat/folly/experimental:threaded_repeating_function_runner\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"poly\",\n    headers = [\n        \"Poly.h\",\n        \"Poly-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":c_portability\",\n        \":cpp_attributes\",\n        \":poly_exception\",\n        \":traits\",\n        \"//folly/detail:poly_detail\",\n        \"//folly/detail:typelist\",\n        \"//folly/lang:assume\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"unicode\",\n    srcs = [\"Unicode.cpp\"],\n    headers = [\"Unicode.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":conv\",\n    ],\n    exported_deps = [\n        \"//folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_log_level\",\n    exported_deps = [\n        \"//xplat/folly/logging:log_level\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"init_init\",\n    exported_deps = [\"//xplat/folly/init:init\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"init_phase\",\n    exported_deps = [\"//xplat/folly/init:phase\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_init\",\n    exported_deps = [\n        \"//xplat/folly/logging:init\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_logging\",\n    exported_deps = [\n        \"//xplat/folly/logging:logging\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_log_handler\",\n    exported_deps = [\n        \"//xplat/folly/logging:log_handler\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"io_typed_io_buf\",\n    exported_deps = [\n        \"//xplat/folly/io:typed_io_buf\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_multi_level_time_series\",\n    exported_deps = [\"//xplat/folly/stats:multi_level_time_series\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_symbolizer_detail_debug\",\n    exported_deps = [\"//xplat/folly/experimental/symbolizer/detail:debug\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_symbolizer_line_reader\",\n    exported_deps = [\"//xplat/folly/experimental/symbolizer:line_reader\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_timeseries_histogram\",\n    exported_deps = [\"//xplat/folly/stats:timeseries_histogram\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_symbolizer_elf\",\n    exported_deps = [\"//xplat/folly/experimental/symbolizer:elf\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"token_bucket\",\n    headers = [\"TokenBucket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":constexpr_math\",\n        \":likely\",\n        \":optional\",\n        \"//folly/concurrency:cache_locality\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"producer_consumer_queue\",\n    headers = [\"ProducerConsumerQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/concurrency:cache_locality\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_symbolizer_elf_cache\",\n    exported_deps = [\"//xplat/folly/experimental/symbolizer:elf_cache\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_symbolizer_dwarf\",\n    exported_deps = [\"//xplat/folly/experimental/symbolizer:dwarf\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_event_count\",\n    exported_deps = [\n        \"//xplat/folly/experimental:event_count\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"packed_sync_ptr\",\n    headers = [\"PackedSyncPtr.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":portability\",\n        \"//folly/synchronization:small_locks\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_combine\",\n    exported_deps = [\"//xplat/folly/gen:combine\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"synchronized_ptr\",\n    headers = [\"SynchronizedPtr.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":synchronized\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_parallel\",\n    exported_deps = [\"//xplat/folly/gen:parallel\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_digest_builder\",\n    exported_deps = [\"//xplat/folly/stats:digest_builder\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_file_handler_factory\",\n    exported_deps = [\n        \"//xplat/folly/logging:file_handler_factory\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"logging_example_lib\",\n    exported_deps = [\n        \"//xplat/folly/logging/example:lib\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"mpmc_pipeline\",\n    headers = [\"MPMCPipeline.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":portability\",\n        \"//folly/detail:mpmc_pipeline_detail\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"gen_parallel_map\",\n    exported_deps = [\"//xplat/folly/gen:parallel_map\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"concurrent_lazy\",\n    headers = [\"ConcurrentLazy.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/functional:invoke\",\n        \"//folly/synchronization:delayed_init\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"concurrent_skip_list\",\n    headers = [\n        \"ConcurrentSkipList.h\",\n        \"ConcurrentSkipList-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":constexpr_math\",\n        \":likely\",\n        \":memory\",\n        \":thread_local\",\n        \"//folly/detail:iterators\",\n        \"//folly/synchronization:micro_spin_lock\",\n    ],\n    exported_external_deps = [\n        (\"boost\", None, \"boost_random\"),\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_detail_buffered_stat\",\n    exported_deps = [\"//xplat/folly/stats/detail:buffered_stat\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_settings_settings\",\n    exported_deps = [\n        \"//xplat/folly/experimental/settings:settings\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"default_keep_alive_executor\",\n    headers = [\n        \"DefaultKeepAliveExecutor.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":executor\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"virtual_executor\",\n    headers = [\n        \"VirtualExecutor.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/executors:virtual_executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"atomic_hash_array\",\n    headers = [\n        \"AtomicHashArray.h\",\n        \"AtomicHashArray-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":thread_cached_int\",\n        \":utility\",\n        \"//folly/detail:atomic_hash_utils\",\n        \"//folly/detail:iterators\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"experimental_flat_combining_priority_queue\",\n    exported_deps = [\n        \"//xplat/folly/experimental:flat_combining_priority_queue\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"atomic_unordered_map\",\n    headers = [\"AtomicUnorderedMap.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":conv\",\n        \":likely\",\n        \":random\",\n        \":scope_guard\",\n        \":traits\",\n        \"//folly/detail:atomic_unordered_map_utils\",\n        \"//folly/lang:bits\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_detail_core\",\n    exported_deps = [\"//xplat/folly/futures/detail:core\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"stats_quantile_estimator\",\n    exported_deps = [\"//xplat/folly/stats:quantile_estimator\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"io_record_io\",\n    exported_deps = [\n        \"//xplat/folly/io:record_io\",\n    ],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_core\",\n    exported_deps = [\"//xplat/folly/futures:core\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_manual_timekeeper\",\n    exported_deps = [\"//xplat/folly/futures:manual_timekeeper\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_barrier\",\n    exported_deps = [\"//xplat/folly/futures:barrier\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_shared_promise\",\n    exported_deps = [\"//xplat/folly/futures:shared_promise\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_future_splitter\",\n    exported_deps = [\"//xplat/folly/futures:future_splitter\"],\n)\n\nnon_fbcode_target(\n    # @shim\n    _kind = folly_xplat_library,\n    name = \"futures_futures\",\n    exported_deps = [\"//xplat/folly/futures:futures\"],\n)\n\n# This adds the symbol __folly_memcpy through FollyMemcpy.h, but does not\n# replace the default memcpy.\n# NOTE: for mobile, this is a no-op. we have this here for symmetry with fbcode\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"memcpy\",\n    srcs = [\n        \"FollyMemcpy.cpp\",\n        \"memcpy.S\",\n    ],\n    fbcode_compiler_flags_override = [],\n    raw_headers = [\"FollyMemcpy.h\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64\",\n    headers = [\"base64.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":c_portability\",\n        \":portability\",\n        \"//folly/detail/base64_detail:base64_api\",\n        \"//folly/detail/base64_detail:base64_common\",\n        \"//folly/lang:exception\",\n        \"//folly/memory:uninitialized_memory_hacks\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"maybe_managed_ptr\",\n    headers = [\"MaybeManagedPtr.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [],\n    exported_deps = [],\n)\n\n# !!!! fbcode/folly/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\n######################################################################\n# Libraries\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"atomic_hash_map\",\n    headers = [\n        \"AtomicHashMap.h\",\n        \"AtomicHashMap-inl.h\",\n    ],\n    exported_deps = [\n        \":atomic_hash_array\",\n        \":c_portability\",\n        \":likely\",\n        \":thread_cached_int\",\n        \"//folly/container:foreach\",\n        \"//folly/detail:atomic_hash_utils\",\n        \"//folly/detail:iterators\",\n        \"//folly/hash:hash\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"atomic_linked_list\",\n    headers = [\n        \"AtomicIntrusiveLinkedList.h\",\n        \"AtomicLinkedList.h\",\n    ],\n    exported_deps = [\":memory\"],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"clock_gettime_wrappers\",\n    srcs = [\"ClockGettimeWrappers.cpp\"],\n    headers = [\"ClockGettimeWrappers.h\"],\n    deps = [\n        \":likely\",\n    ],\n    exported_deps = [\n        \"//folly/portability:time\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n    ],\n    exported_external_deps = [\n        (\"glibc\", None, \"rt\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"config\",\n    # @fb-only[end= ]: headers = [\"folly-config.h\"],\n    propagated_pp_flags = select({\n        \"DEFAULT\": [],\n        # The `liblz4.so` in the default conda channel doesn't *publicly* export\n        # symbols used for fast reset support (although they are availabel as\n        # \"internal\" symbols and would link statically).\n        \"ovr_config//distro:conda\": [\n            \"-DFOLLY_USE_LZ4_FAST_RESET=0\",\n        ],\n    }),\n    headers = [\"//:folly-config.h\"], # @oss-only\n    labels = [\"oss_dependency\"], # @oss-only\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"conv\",\n    srcs = [\"Conv.cpp\"],\n    headers = [\"Conv.h\"],\n    undefined_symbols = True,  # TODO(T23121628): fix deps and remove\n    deps = [\n        \"fbsource//third-party/fast_float:fast_float\",\n        \"//folly/lang:safe_assert\",\n    ],\n    exported_deps = [\n        \":c_portability\",\n        \":demangle\",\n        \":expected\",\n        \":fbstring\",\n        \":likely\",\n        \":portability\",\n        \":range\",\n        \":traits\",\n        \":unit\",\n        \":utility\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:pretty\",\n        \"//folly/lang:to_ascii\",\n        \"//folly/portability:math\",\n    ],\n    exported_external_deps = [\n        \"double_conversion\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"dynamic\",\n    headers = [\n        \"DynamicConverter.h\",\n        \"dynamic.h\",\n        \"dynamic-inl.h\",\n        \"json.h\",\n    ],\n    exported_deps = [\n        \"//folly/json:dynamic\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_string\",\n    srcs = [\"ExceptionString.cpp\"],\n    headers = [\"ExceptionString.h\"],\n    deps = [\n        \":demangle\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:type_info\",\n    ],\n    exported_deps = [\n        \":fbstring\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_wrapper\",\n    srcs = [\"ExceptionWrapper.cpp\"],\n    headers = [\n        \"ExceptionWrapper.h\",\n        \"ExceptionWrapper-inl.h\",\n    ],\n    undefined_symbols = True,  # TODO(T23121628): fix deps and remove\n    exported_deps = [\n        \":c_portability\",\n        \":cpp_attributes\",\n        \":demangle\",\n        \":exception_string\",\n        \":fbstring\",\n        \":portability\",\n        \":traits\",\n        \":utility\",\n        \"//folly/functional:traits\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"executor\",\n    srcs = [\"Executor.cpp\"],\n    headers = [\n        \"Executor.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":exception_string\",\n        \":portability\",\n    ],\n    exported_deps = [\n        \":function\",\n        \":optional\",\n        \":range\",\n        \":utility\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"fmt_utility\",\n    srcs = [\n        \"FmtUtility.cpp\",\n    ],\n    headers = [\n        \"FmtUtility.h\",\n    ],\n    deps = [\n        \":range\",\n        \":string\",\n        \"//folly/ssl:openssl_hash\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \":cpp_attributes\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"format_traits\",\n    headers = [\"FormatTraits.h\"],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"indestructible\",\n    headers = [\"Indestructible.h\"],\n    exported_deps = [\n        \":traits\",\n        \":utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"indexed_mem_pool\",\n    headers = [\"IndexedMemPool.h\"],\n    exported_deps = [\n        \":portability\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:unistd\",\n        \"//folly/synchronization:atomic_struct\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    # @shim\n    name = \"json\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":dynamic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"json_pointer\",\n    headers = [\n        \"json_pointer.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/json:json_pointer\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"json_patch\",\n    headers = [\n        \"json_patch.h\",\n    ],\n    exported_deps = [\n        \"//folly/json:json_patch\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"glog\",\n    headers = [\"GLog.h\"],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":likely\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memset-impl\",\n    srcs = [\n        \"FollyMemset.cpp\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"memset.S\",\n        ],\n        \"ovr_config//os:linux-arm64\": [\n            \"memset_select_aarch64.cpp\",\n        ],\n    }),\n    auto_headers = AutoHeaders.NONE,\n    headers = [],\n    modular_headers = False,\n    preprocessor_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mavx2\",\n        ],\n    }),\n    exported_deps = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux-arm64\": [\n            \"//folly/external/aor:memset_aarch64\",  # @manual\n        ],\n    }),\n)\n\n# This exports the symbol __folly_memset to C++ via a header.\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memset\",\n    headers = [\"FollyMemset.h\"],\n    exported_deps = [\n        \":memset-impl\",  # @manual\n    ],\n)\n\n# This overrides the libc memset with __folly_memset.\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memset-use\",\n    srcs = [\n        \"FollyMemset.cpp\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"memset.S\",\n        ],\n        \"ovr_config//os:linux-arm64\": [\n            \"memset_select_aarch64.cpp\",\n        ],\n    }),\n    auto_headers = AutoHeaders.NONE,\n    headers = [],\n    link_whole = True,  # Set link_whole to force linker to use __folly_memset\n    modular_headers = False,\n    preprocessor_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:arm64\": [\n            \"-DFOLLY_MEMSET_IS_MEMSET\",\n        ],\n        \"ovr_config//cpu:x86_64\": [\n            \"-DFOLLY_MEMSET_IS_MEMSET\",\n            \"-mavx2\",\n        ],\n    }),\n    exported_deps = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux-arm64\": [\n            \"//folly/external/aor:memset_aarch64-use\",  # @manual\n        ],\n    }),\n)\n\n# This adds the symbol __folly_memcpy but does not replace the default memcpy.\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memcpy-impl\",\n    srcs = [\n        \"FollyMemcpy.cpp\",\n    ] + select({\n        \"ovr_config//cpu:x86_64\": [\n            \"memcpy.S\",\n        ],\n        \"ovr_config//os:linux-arm64\": [\n            \"memcpy_select_aarch64.cpp\",\n        ],\n    }),\n    auto_headers = AutoHeaders.NONE,\n    headers = [],\n    modular_headers = False,\n    preprocessor_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mavx2\",\n        ],\n    }),\n    exported_deps = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux-arm64\": [\n            \"//folly/external/aor:memcpy_aarch64\",  # @manual\n        ],\n    }),\n)\n\n# This exports the symbol __folly_memcpy to C++ via a header.\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memcpy\",\n    headers = [\"FollyMemcpy.h\"],\n    exported_deps = [\n        \":memcpy-impl\",  # @manual\n    ],\n)\n\n# This overrides the libc memcpy with __folly_memcpy.\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memcpy-use\",\n    srcs = [\n        \"FollyMemcpy.cpp\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"memcpy.S\",\n        ],\n        \"ovr_config//os:linux-arm64\": [\n            \"memcpy_select_aarch64.cpp\",\n        ],\n    }),\n    auto_headers = AutoHeaders.NONE,\n    headers = [],\n    link_whole = True,  # Set link_whole to force linker to use __folly_memcpy\n    modular_headers = False,\n    preprocessor_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:arm64\": [\n            \"-DFOLLY_MEMCPY_IS_MEMCPY\",\n        ],\n        \"ovr_config//cpu:x86_64\": [\n            \"-DFOLLY_MEMCPY_IS_MEMCPY\",\n            \"-mavx2\",\n            \"-march=haswell\",\n        ],\n    }),\n    exported_deps = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux-arm64\": [\n            \"//folly/external/aor:memcpy_aarch64-use\",  # @manual\n        ],\n    }),\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"micro_lock\",\n    srcs = [\"MicroLock.cpp\"],\n    headers = [\"MicroLock.h\"],\n    deps = [\n        \"//folly/portability:asm\",\n    ],\n    exported_deps = [\n        \":optional\",\n        \":portability\",\n        \":utility\",\n        \"//folly/synchronization:atomic_notification\",\n        \"//folly/synchronization:atomic_ref\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"network_address\",\n    srcs = [\n        \"IPAddress.cpp\",\n        \"IPAddressV4.cpp\",\n        \"IPAddressV6.cpp\",\n        \"MacAddress.cpp\",\n        \"SocketAddress.cpp\",\n    ],\n    headers = [\n        \"IPAddress.h\",\n        \"IPAddressException.h\",\n        \"IPAddressV4.h\",\n        \"IPAddressV6.h\",\n        \"MacAddress.h\",\n        \"SocketAddress.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \":exception\",\n        \":format\",\n        \":scope_guard\",\n        \":small_vector\",\n        \":string\",\n        \"//folly/detail:ip_address_source\",\n        \"//folly/net:net_ops\",\n    ],\n    exported_deps = [\n        \":c_portability\",\n        \":constexpr_math\",\n        \":conv\",\n        \":expected\",\n        \":fbstring\",\n        \":optional\",\n        \":portability\",\n        \":range\",\n        \":unit\",\n        \"//folly/detail:ip_address\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:exception\",\n        \"//folly/net:network_socket\",\n        \"//folly/portability:config\",\n        \"//folly/portability:sockets\",\n    ],\n    external_deps = [\n        \"boost\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"overload\",\n    headers = [\"Overload.h\"],\n    exported_deps = [\n        \":portability\",\n        \":traits\",\n        \"//folly/functional:invoke\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"shared_mutex\",\n    srcs = [\"SharedMutex.cpp\"],\n    headers = [\"SharedMutex.h\"],\n    supports_python_dlopen = True,\n    deps = [\n        \":indestructible\",\n        \"//folly/lang:exception\",\n        \"//folly/portability:sys_resource\",\n    ],\n    exported_deps = [\n        \":c_portability\",\n        \":cpp_attributes\",\n        \":likely\",\n        \"//folly/chrono:hardware\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/detail:futex\",\n        \"//folly/portability:asm\",\n        \"//folly/synchronization:lock\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/synchronization:sanitize_thread\",\n        \"//folly/system:thread_id\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"singleton\",\n    srcs = [\n        \"Singleton.cpp\",\n    ],\n    headers = [\n        \"Singleton.h\",\n        \"Singleton-inl.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \":demangle\",\n        \":scope_guard\",\n        \"//folly/experimental/symbolizer:symbolizer\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/portability:config\",\n        \"//folly/portability:fmt_compile\",\n        \"//folly/system:at_fork\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":cancellation_token\",\n        \":exception\",\n        \":executor\",\n        \":memory\",\n        \":synchronized\",\n        \"//folly/concurrency:core_cached_shared_ptr\",\n        \"//folly/concurrency/memory:read_mostly_shared_ptr\",\n        \"//folly/detail:singleton\",\n        \"//folly/detail:static_singleton_manager\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:exception\",\n        \"//folly/memory:sanitize_leak\",\n        \"//folly/synchronization:baton\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n        (\"glibc\", None, \"rt\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"spin_lock\",\n    headers = [\n        \"SpinLock.h\",\n    ],\n    exported_deps = [\n        \":portability\",\n        \"//folly/synchronization:small_locks\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"subprocess\",\n    srcs = [\"Subprocess.cpp\"],\n    headers = [\"Subprocess.h\"],\n    deps = [\n        \":conv\",\n        \":scope_guard\",\n        \":string\",\n        \"//folly/lang:assume\",\n        \"//folly/logging:logging\",\n        \"//folly/portability:dirent\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:stdlib\",\n        \"//folly/portability:sys_syscall\",\n        \"//folly/portability:unistd\",\n        \"//folly/system:at_fork\",\n        \"//folly/system:shell\",\n    ],\n    exported_deps = [\n        \":exception\",\n        \":file\",\n        \":file_util\",\n        \":function\",\n        \":map_util\",\n        \":optional\",\n        \":portability\",\n        \":range\",\n        \"//folly/container:span\",\n        \"//folly/gen:string\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:sys_resource\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_range\"),\n        (\"glibc\", None, \"dl\"),\n    ],\n    exported_external_deps = [\n        \"boost\",\n        (\"boost\", None, \"boost_container\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"timeout_queue\",\n    srcs = [\"TimeoutQueue.cpp\"],\n    headers = [\"TimeoutQueue.h\"],\n    exported_external_deps = [\n        \"boost\",\n        (\"boost\", None, \"boost_multi_index\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"uri\",\n    srcs = [\n        \"Uri.cpp\",\n    ],\n    headers = [\n        \"Uri.h\",\n        \"Uri-inl.h\",\n    ],\n    exported_deps = [\n        \":conv\",\n        \":expected\",\n        \":string\",\n        \"//folly/hash:hash\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_regex\"),\n    ],\n)\n\n# For things that would go in c++ stdlib <utility>:\n\nfb_native.export_file(\n    name = \"src-tree\",\n    src = \".\",\n    mode = \"reference\",\n    visibility = [\"PUBLIC\"],\n)\n\nfb_native.export_file(\n    name = \"support/gdb.py\",\n    src = \"support/gdb.py\",\n    visibility = [\"PUBLIC\"],\n)\n\nfb_native.export_file(\n    name = \"fibers/scripts/gdb.py\",\n    src = \"fibers/scripts/gdb.py\",\n    visibility = [\"PUBLIC\"],\n)\n\nfb_native.export_file(\n    name = \"coro/scripts/co_bt.py\",\n    src = \"coro/scripts/co_bt.py\",\n    visibility = [\"PUBLIC\"],\n)\n"
  },
  {
    "path": "folly/BUILD_MODE.bzl",
    "content": "\"\"\" build mode definitions for folly \"\"\"\n\nload(\"@fbcode//:BUILD_MODE.bzl\", get_parent_modes = \"get_empty_modes\")\nload(\"@fbcode_macros//build_defs:create_build_mode.bzl\", \"extend_build_modes\")\n\n_extra_cflags = [\n    \"-Wsign-compare\",\n    \"-Wunused-parameter\",\n]\n\n_extra_cxxflags = [\n]\n\n_extra_clang_flags = [\n    \"-Wconditional-uninitialized\",\n    \"-Wconstant-conversion\",\n    \"-Wdeprecated-declarations\",\n    \"-Wextra\",\n    \"-Wextra-semi\",\n    \"-Wexceptions\",\n    \"-Wfloat-conversion\",\n    \"-Wgnu-conditional-omitted-operand\",\n    \"-Wheader-hygiene\",\n    \"-Wimplicit-fallthrough\",\n    \"-Wmismatched-tags\",\n    \"-Wmissing-braces\",\n    \"-Wno-error=missing-noreturn\",  # Disabled for LLVM-21 migration; tracked in T251075049\n    \"-Wshadow\",\n    \"-Wshift-sign-overflow\",\n    \"-Wsometimes-uninitialized\",\n    \"-Wuninitialized\",\n    \"-Wuninitialized-const-reference\",\n    \"-Wunused-const-variable\",\n    \"-Wunused-exception-parameter\",\n    \"-Wunused-function\",\n    \"-Wunused-lambda-capture\",\n    \"-Wunused-value\",\n    \"-Wunused-variable\",\n    \"-Wswitch-enum\",\n]\n\n_extra_gcc_flags = [\n    \"-Wdeprecated-declarations\",\n    \"-Wmaybe-uninitialized\",\n    \"-Wmissing-braces\",\n    \"-Wshadow\",\n    \"-Wuninitialized\",\n    \"-Wunused-but-set-variable\",\n]\n\n_extra_asan_options = {\n    \"detect_leaks\": \"1\",\n    \"detect_odr_violation\": \"2\",\n    \"handle_segv\": \"1\",\n}\n\n_modes = extend_build_modes(\n    get_parent_modes(),\n    asan_options = _extra_asan_options,\n    c_flags = _extra_cflags,\n    clang_flags = _extra_clang_flags,\n    cxx_flags = _extra_cxxflags,\n    gcc_flags = _extra_gcc_flags,\n)\n\ndef get_modes():\n    \"\"\" Return modes for this file \"\"\"\n    return _modes\n"
  },
  {
    "path": "folly/Benchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n\n#include <algorithm>\n#include <cmath>\n#include <cstring>\n#include <iostream>\n#include <limits>\n#include <map>\n#include <memory>\n#include <numeric>\n#include <sstream>\n#include <utility>\n#include <vector>\n\n#include <folly/FileUtil.h>\n#include <folly/MapUtil.h>\n#include <folly/Overload.h>\n#include <folly/String.h>\n#include <folly/detail/BenchmarkAdaptive.h>\n#include <folly/detail/PerfScoped.h>\n#include <folly/json/json.h>\n\n// This needs to be at the end because some versions end up including\n// Windows.h without defining NOMINMAX, which breaks uses\n// of `std::numeric_limits<T>::max()`. We explicitly define NOMINMAX here\n// explicitly instead.\n#define NOMINMAX 1\n#include <boost/regex.hpp>\n\nusing namespace std;\n\n// === For benchmark+test combos that gate on `FLAGS_benchmark` ===\n\nFOLLY_GFLAGS_DEFINE_bool(benchmark, false, \"Run benchmarks.\");\n\n// === Mode ===\n\nFOLLY_GFLAGS_DEFINE_string(\n    bm_mode,\n    \"best-of\",\n    \"'best-of' (default) or 'adaptive'. \"\n    \"Adaptive interleaves samples across all benchmarks to cancel \"\n    \"correlated noise, and runs until the target percentile estimate \"\n    \"is both stable and precise. \"\n    \"Set --bm_max_secs=20-30 for reliable results. \"\n    \"See BenchmarkAdaptive.md.\");\n\n// === Benchmark selection ===\n\nFOLLY_GFLAGS_DEFINE_string(\n    bm_regex, \"\", \"Only run benchmarks whose names match this regex.\");\n\nFOLLY_GFLAGS_DEFINE_string(\n    bm_file_regex,\n    \"\",\n    \"Only run benchmarks whose source filenames match this regex.\");\n\nFOLLY_GFLAGS_DEFINE_bool(\n    bm_list, false, \"Print benchmark names and exit without running.\");\n\n// === Adaptive: convergence target ===\n\nFOLLY_GFLAGS_DEFINE_double(\n    bm_target_percentile,\n    33.3,\n    \"Adaptive: which percentile of per-slice iteration timings to report. \"\n    \"Default 33.3 approximates the median of good runs, rejecting transient \"\n    \"slowdowns. Higher values (e.g. 90) capture tail behavior. \"\n    \"For comparison, best-of mode always reports the minimum (p0).\");\n\nFOLLY_GFLAGS_DEFINE_double(\n    bm_target_precision_pct,\n    0.4,\n    \"Adaptive: measurement precision target. A benchmark converges \"\n    \"when the 95% confidence interval around the target percentile \"\n    \"is narrower than this percentage of the estimate. E.g. 0.4 \"\n    \"means the CI for a 100ns benchmark must be under 0.4ns wide. \"\n    \"Tighter values (e.g. 0.1) need longer --bm_max_secs or a \"\n    \"quieter system.\");\n\n// === How long to measure ===\n\nFOLLY_GFLAGS_DEFINE_int32(\n    bm_max_secs,\n    1,\n    \"Maximum seconds per benchmark. Adaptive: set to 20-30 for \"\n    \"robust results on noisy systems. Default 1s is for quick \"\n    \"iteration.\");\n\nFOLLY_GFLAGS_DEFINE_double(\n    bm_min_secs,\n    0.1,\n    \"Adaptive: minimum seconds per benchmark before it can converge. \"\n    \"Ensures enough elapsed time to observe system jitter.\");\n\nFOLLY_GFLAGS_DEFINE_uint32(\n    bm_min_samples,\n    20,\n    \"Adaptive: minimum samples per benchmark before it can converge. \"\n    \"Ensures enough data points for stable percentile and CI \"\n    \"estimates.\");\n\n// Best-of mode only:\n\nFOLLY_GFLAGS_DEFINE_int32(\n    bm_min_iters,\n    1,\n    \"Best-of: minimum iterations per measurement. The inner loop \"\n    \"doubles from this until duration exceeds --bm_slice_usec.\");\n\nFOLLY_GFLAGS_DEFINE_int64(\n    bm_max_iters, 1 << 30, \"Best-of: maximum iterations per measurement.\");\n\nFOLLY_GFLAGS_DEFINE_uint32(\n    bm_max_trials,\n    1000,\n    \"Best-of: maximum number of measurement trials (epochs). \"\n    \"The best (minimum-time) trial is reported.\");\n\n// === Measurement slice duration ===\n\nFOLLY_GFLAGS_DEFINE_int64(\n    bm_slice_usec,\n    1000,\n    \"Duration in microseconds of each contiguous measurement slice. \"\n    \"Values below 1000 risk harness interference affecting results.\");\n\nFOLLY_GFLAGS_DEFINE_int64(\n    bm_min_usec,\n    1000,\n    \"Deprecated: use --bm_slice_usec (same meaning, same units).\");\n\n// === Instrumentation & diagnostics (mode-agnostic) ===\n\nFOLLY_GFLAGS_DEFINE_bool(\n    bm_warm_up_iteration,\n    false,\n    \"Run one iteration of each benchmark before measuring, to warm \"\n    \"caches and trigger lazy initialization. Automatically enabled \"\n    \"when --bm_perf_args is set.\");\n\n#if FOLLY_PERF_IS_SUPPORTED\nFOLLY_GFLAGS_DEFINE_string(\n    bm_perf_args,\n    \"\",\n    \"Attach `perf` during measurement (skips the first iteration \"\n    \"for setup). Example: --bm_perf_args=\\\"record -g\\\"\");\n#endif\n\nFOLLY_GFLAGS_DEFINE_bool(\n    bm_verbose,\n    false,\n    \"Log more diagnostic details: convergence progress and baseline \"\n    \"stats (adaptive), measurement phases (best-of).\");\n\nFOLLY_GFLAGS_DEFINE_bool(\n    bm_quiet, false, \"Silence non-actionable diagnostics.\");\n\n// === Output & comparison (mode-agnostic) ===\n\nFOLLY_GFLAGS_DEFINE_bool(json, false, \"Print results in JSON format.\");\n\nFOLLY_GFLAGS_DEFINE_string(\n    bm_json_verbose,\n    \"\",\n    \"Write verbose JSON to this file (for BenchmarkCompare or \"\n    \"--bm_relative_to). Written regardless of --json.\");\n\nFOLLY_GFLAGS_DEFINE_string(\n    bm_relative_to,\n    \"\",\n    \"Print results relative to a previous JSON dump \"\n    \"(produced by --bm_json_verbose).\");\n\nFOLLY_GFLAGS_DEFINE_uint32(\n    bm_result_width_chars, 76, \"Width of the results table in characters.\");\n\n// === Profiling with constant iterations (best-of mode only) ===\n\nFOLLY_GFLAGS_DEFINE_bool(\n    bm_profile,\n    false,\n    \"Best-of: run each benchmark with a fixed iteration count \"\n    \"(--bm_profile_iters) for external profiling. Results will be \"\n    \"jittery -- not for measurement.\");\n\nFOLLY_GFLAGS_DEFINE_int64(\n    bm_profile_iters,\n    1000,\n    \"Best-of: number of iterations when --bm_profile is set.\");\n\n// === Avoid ===\n\nFOLLY_GFLAGS_DEFINE_bool(\n    bm_estimate_time,\n    false,\n    \"Best-of: alternative measurement strategy that reports the \"\n    \"geometric mean of p25-p75 latencies. Slower than best-of mode, \"\n    \"with unclear benefits. \"\n    \"Prefer --bm_mode=adaptive for noise-robust measurements.\");\n\nnamespace folly {\nnamespace detail {\n\nBenchmarkingState<std::chrono::high_resolution_clock>& globalBenchmarkState() {\n  static detail::BenchmarkingState<std::chrono::high_resolution_clock> state;\n  return state;\n}\n\n} // namespace detail\n\nusing BenchmarkFun = std::function<detail::TimeIterData(unsigned int)>;\n\n#define FB_FOLLY_GLOBAL_BENCHMARK_BASELINE fbFollyGlobalBenchmarkBaseline\n#define FB_FOLLY_GLOBAL_BENCHMARK_SUSPENDER_BASELINE \\\n  fbFollyGlobalBenchmarkSuspenderBaseline\n#define FB_STRINGIZE_X2(x) FOLLY_PP_STRINGIZE(x)\n\nconstexpr const char kGlobalBenchmarkBaseline[] =\n    FB_STRINGIZE_X2(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE);\n\nconstexpr const char kGlobalBenchmarkSuspenderBaseline[] =\n    FB_STRINGIZE_X2(FB_FOLLY_GLOBAL_BENCHMARK_SUSPENDER_BASELINE);\n\n// Add the global baseline\nBENCHMARK(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE) {\n#ifdef _MSC_VER\n  _ReadWriteBarrier();\n#else\n  asm volatile(\"\");\n#endif\n}\n\n// Add the suspender overhead baseline\nBENCHMARK(FB_FOLLY_GLOBAL_BENCHMARK_SUSPENDER_BASELINE) {\n  BENCHMARK_SUSPEND {}\n}\n\n#undef FB_STRINGIZE_X2\n#undef FB_FOLLY_GLOBAL_BENCHMARK_BASELINE\n\nstatic std::pair<double, UserCounters> runBenchmarkGetNSPerIteration(\n    const BenchmarkFun& fun, const double globalBaseline, int64_t sliceUsec) {\n  using std::chrono::duration_cast;\n  using std::chrono::high_resolution_clock;\n  using std::chrono::microseconds;\n  using std::chrono::nanoseconds;\n  using std::chrono::seconds;\n\n  // They key here is accuracy; too low numbers means the accuracy was\n  // coarse. We up the ante until we get to at least minNanoseconds\n  // timings.\n  static_assert(\n      std::is_same<high_resolution_clock::duration, nanoseconds>::value,\n      \"High resolution clock must be nanosecond resolution.\");\n  // We choose a minimum minimum (sic) of 100,000 nanoseconds, but if\n  // the clock resolution is worse than that, it will be larger. In\n  // essence we're aiming at making the quantization noise 0.01%.\n  const auto minNanoseconds =\n      std::max<nanoseconds>(nanoseconds(100000), microseconds(sliceUsec));\n\n  // We establish a total time budget as we don't want a measurement\n  // to take too long. This will curtail the number of actual trials.\n  const auto timeBudget = seconds(FLAGS_bm_max_secs);\n  auto global = high_resolution_clock::now();\n\n  std::vector<std::pair<double, UserCounters>> trialResults(\n      FLAGS_bm_max_trials);\n  size_t actualTrials = 0;\n\n  // We do measurements in several trials (epochs) and take the minimum, to\n  // account for jitter.\n  for (; actualTrials < FLAGS_bm_max_trials; ++actualTrials) {\n    const auto maxIters = uint32_t(FLAGS_bm_max_iters);\n    for (auto n = uint32_t(FLAGS_bm_min_iters); n < maxIters; n *= 2) {\n      detail::TimeIterData timeIterData = fun(static_cast<unsigned int>(n));\n      if (timeIterData.duration < minNanoseconds) {\n        continue;\n      }\n      // We got an accurate enough timing, done. But only save if\n      // smaller than the current result.\n      auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);\n      trialResults[actualTrials] = std::make_pair(\n          max(0.0, double(nsecs.count()) / timeIterData.niter - globalBaseline),\n          std::move(timeIterData.userCounters));\n      // Done with the current trial, we got a meaningful timing.\n      break;\n    }\n    auto now = high_resolution_clock::now();\n    if (now - global >= timeBudget) {\n      // No more time budget available.\n      ++actualTrials;\n      break;\n    }\n  }\n\n  // Current state of the art: get the minimum. After some\n  // experimentation, it seems taking the minimum is the best.\n  auto iter = min_element(\n      trialResults.begin(),\n      trialResults.begin() + actualTrials,\n      [](const auto& a, const auto& b) { return a.first < b.first; });\n\n  // If the benchmark was basically drowned in baseline noise, it's\n  // possible it became negative.\n  return std::make_pair(max(0.0, iter->first), iter->second);\n}\n\nstatic std::pair<double, UserCounters> runBenchmarkGetNSPerIterationEstimate(\n    const BenchmarkFun& fun, const double globalBaseline) {\n  using std::chrono::duration_cast;\n  using std::chrono::high_resolution_clock;\n  using std::chrono::microseconds;\n  using std::chrono::nanoseconds;\n  using std::chrono::seconds;\n  using TrialResultType = std::pair<double, UserCounters>;\n\n  // They key here is accuracy; too low numbers means the accuracy was\n  // coarse. We up the ante until we get to at least minNanoseconds\n  // timings.\n  static_assert(\n      std::is_same<high_resolution_clock::duration, nanoseconds>::value,\n      \"High resolution clock must be nanosecond resolution.\");\n\n  // Estimate single iteration running time for 1 sec\n  double estPerIter = 0.0; // Estimated nanosec per iteration\n  auto estStart = high_resolution_clock::now();\n  const auto estBudget = seconds(1);\n  for (auto n = 1; n < 1000; n *= 2) {\n    detail::TimeIterData timeIterData = fun(static_cast<unsigned int>(n));\n    auto now = high_resolution_clock::now();\n    auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);\n    estPerIter = double(nsecs.count() - globalBaseline) / n;\n    if (now - estStart > estBudget) {\n      break;\n    }\n  }\n  // Can't estimate running time, so make it a baseline\n  if (estPerIter <= 0.0) {\n    estPerIter = globalBaseline;\n  }\n\n  // We do measurements in several trials (epochs) to account for jitter.\n  size_t actualTrials = 0;\n  const unsigned int estimateCount = to_integral(max(1.0, 5e+7 / estPerIter));\n  std::vector<TrialResultType> trialResults(FLAGS_bm_max_trials);\n  const auto maxRunTime = seconds(max(5, FLAGS_bm_max_secs));\n  auto globalStart = high_resolution_clock::now();\n\n  // Run benchmark up to trial times with at least 0.5 sec each\n  // Or until we run out of allowed time (max(5, FLAGS_bm_max_secs))\n  for (size_t tryId = 0; tryId < FLAGS_bm_max_trials; tryId++) {\n    detail::TimeIterData timeIterData = fun(estimateCount);\n    auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);\n\n    if (nsecs.count() > globalBaseline) {\n      auto nsecIter =\n          double(nsecs.count() - globalBaseline) / timeIterData.niter;\n      trialResults[actualTrials++] =\n          std::make_pair(nsecIter, std::move(timeIterData.userCounters));\n    }\n    // Check if we are out of time quota\n    auto now = high_resolution_clock::now();\n    if (now - globalStart > maxRunTime) {\n      break;\n    }\n  }\n\n  // Sort results by running time\n  std::sort(\n      trialResults.begin(),\n      trialResults.begin() + actualTrials,\n      [](const TrialResultType& a, const TrialResultType& b) {\n        return a.first < b.first;\n      });\n\n  const auto getPercentile = [](size_t count, double p) -> size_t {\n    return static_cast<size_t>(count * p);\n  };\n\n  const size_t trialP25 = getPercentile(actualTrials, 0.25);\n  const size_t trialP75 = getPercentile(actualTrials, 0.75);\n  if (trialP75 - trialP25 == 0) {\n    // Use first trial results if p75 == p25.\n    return std::make_pair(trialResults[0].first, trialResults[0].second);\n  }\n\n  double geomeanNsec = 0.0;\n  for (size_t tryId = trialP25; tryId < trialP75; tryId++) {\n    geomeanNsec += std::log(trialResults[tryId].first);\n  }\n  geomeanNsec = std::exp(geomeanNsec / (1.0 * (trialP75 - trialP25)));\n\n  return std::make_pair(\n      geomeanNsec, trialResults[trialP25 + (trialP75 - trialP25) / 2].second);\n}\n\nstatic std::pair<double, UserCounters> runProfilingGetNSPerIteration(\n    const BenchmarkFun& fun, const double globalBaseline) {\n  using std::chrono::duration_cast;\n  using std::chrono::high_resolution_clock;\n  using std::chrono::nanoseconds;\n\n  // They key here is accuracy; too low numbers means the accuracy was\n  // coarse. We up the ante until we get to at least minNanoseconds\n  // timings.\n  static_assert(\n      std::is_same<high_resolution_clock::duration, nanoseconds>::value,\n      \"High resolution clock must be nanosecond resolution.\");\n\n  // This is a very simple measurement with a single epoch\n  // and should be used only for profiling purposes\n  detail::TimeIterData timeIterData = fun(FLAGS_bm_profile_iters);\n\n  auto nsecs = duration_cast<nanoseconds>(timeIterData.duration);\n  auto nsecIter = double(nsecs.count()) / timeIterData.niter - globalBaseline;\n  return std::make_pair(nsecIter, std::move(timeIterData.userCounters));\n}\n\nstruct ScaleInfo {\n  double boundary;\n  const char* suffix;\n};\n\nstatic const ScaleInfo kTimeSuffixes[]{\n    {365.25 * 24 * 3600, \"years\"},\n    {24 * 3600, \"days\"},\n    {3600, \"hr\"},\n    {60, \"min\"},\n    {1, \"s\"},\n    {1E-3, \"ms\"},\n    {1E-6, \"us\"},\n    {1E-9, \"ns\"},\n    {1E-12, \"ps\"},\n    {1E-15, \"fs\"},\n    {0, nullptr},\n};\n\nstatic const ScaleInfo kMetricSuffixes[]{\n    {1E24, \"Y\"}, // yotta\n    {1E21, \"Z\"}, // zetta\n    {1E18, \"X\"}, // \"exa\" written with suffix 'X' so as to not create\n                 //   confusion with scientific notation\n    {1E15, \"P\"}, // peta\n    {1E12, \"T\"}, // terra\n    {1E9, \"G\"}, // giga\n    {1E6, \"M\"}, // mega\n    {1E3, \"K\"}, // kilo\n    {1, \"\"},\n    {1E-3, \"m\"}, // milli\n    {1E-6, \"u\"}, // micro\n    {1E-9, \"n\"}, // nano\n    {1E-12, \"p\"}, // pico\n    {1E-15, \"f\"}, // femto\n    {1E-18, \"a\"}, // atto\n    {1E-21, \"z\"}, // zepto\n    {1E-24, \"y\"}, // yocto\n    {0, nullptr},\n};\n\nstatic string humanReadable(\n    double n, unsigned int decimals, const ScaleInfo* scales) {\n  if (std::isinf(n) || std::isnan(n)) {\n    return folly::to<string>(n);\n  }\n\n  const double absValue = fabs(n);\n  const ScaleInfo* scale = scales;\n  while (absValue < scale[0].boundary && scale[1].suffix != nullptr) {\n    ++scale;\n  }\n\n  const double scaledValue = n / scale->boundary;\n  return stringPrintf(\"%.*f%s\", decimals, scaledValue, scale->suffix);\n}\n\nnamespace detail {\nstring readableTime(double n, unsigned int decimals) {\n  return humanReadable(n, decimals, kTimeSuffixes);\n}\n} // namespace detail\n\nstatic string metricReadable(double n, unsigned int decimals) {\n  return humanReadable(n, decimals, kMetricSuffixes);\n}\n\nnamespace {\n\nconstexpr std::string_view kUnitHeaders = \"relative  time/iter   iters/s\";\nconstexpr std::string_view kUnitHeadersPadding = \"     \";\n\nstd::string headerContents(std::string_view file, size_t columns) {\n  const size_t maxFileNameChars =\n      columns - kUnitHeaders.size() - kUnitHeadersPadding.size();\n  std::string fname(file);\n  if (fname.size() > maxFileNameChars) {\n    constexpr std::string_view overflowFilePrefix = \"[...]\";\n    fname.erase(0, fname.size() - maxFileNameChars);\n    fname.replace(0, overflowFilePrefix.size(), overflowFilePrefix);\n  }\n  return stringPrintf(\n      \"%-.*s%*s%*s\",\n      static_cast<int>(fname.size()),\n      fname.c_str(),\n      static_cast<int>(kUnitHeadersPadding.size()),\n      kUnitHeadersPadding.data(),\n      static_cast<int>(kUnitHeaders.size()),\n      kUnitHeaders.data());\n}\n\nclass BenchmarkResultsPrinter {\n public:\n  explicit BenchmarkResultsPrinter(\n      std::set<std::string> counterNames = {},\n      std::ostream* os = &std::cout,\n      std::string_view indent = \"\",\n      size_t columnsAdjust = 0)\n      : counterNames_(std::move(counterNames)),\n        namesLength_{std::accumulate(\n            counterNames_.begin(),\n            counterNames_.end(),\n            size_t{0},\n            [](size_t acc, auto&& name) { return acc + 2 + name.length(); })},\n        os_(os),\n        indent_(indent),\n        columns_(FLAGS_bm_result_width_chars + namesLength_ - columnsAdjust) {}\n\n  void separator(char pad) { line(string(columns_, pad)); }\n\n  void header(std::string_view file) {\n    separator('=');\n    std::string h = headerContents(file, columns_ - namesLength_);\n    for (auto const& name : counterNames_) {\n      h += \"  \";\n      h += name;\n    }\n    line(h);\n    separator('=');\n  }\n\n  void print(\n      const vector<detail::BenchmarkResult>& data,\n      const std::vector<std::string>& annotations = {}) {\n    for (size_t i = 0; i < data.size(); ++i) {\n      auto& datum = data[i];\n      auto file = datum.file;\n      if (file != lastFile_) {\n        header(file);\n        lastFile_ = file;\n      }\n\n      string s = datum.name;\n      if (s == \"-\") {\n        separator('-');\n        continue;\n      }\n      if (s[0] == '\"') {\n        // Strips quote characters from the beginning and end of the name.\n        line(s.substr(1, s.length() - 2));\n        continue;\n      }\n      bool useBaseline = false;\n      // '%' indicates a relative benchmark.\n      if (s[0] == '%') {\n        s.erase(0, 1);\n        useBaseline = isBaselineSet();\n      } else {\n        baselineNsPerIter_ = datum.timeInNs;\n        useBaseline = false;\n      }\n      s.resize(columns_ - namesLength_ - kUnitHeaders.size(), ' ');\n\n      const auto nsPerIter = datum.timeInNs;\n      const auto secPerIter = nsPerIter / 1E9;\n      const auto itersPerSec = (secPerIter == 0)\n          ? std::numeric_limits<double>::infinity()\n          : (1 / secPerIter);\n      std::string row;\n      if (!useBaseline) {\n        row = stringPrintf(\n            \"%*s%8.8s  %9.9s  %8.8s\",\n            static_cast<int>(s.size()),\n            s.c_str(),\n            \"\", // Padding for \"relative\" header.\n            detail::readableTime(secPerIter, 2).c_str(),\n            metricReadable(itersPerSec, 2).c_str());\n      } else {\n        row = stringPrintf(\n            \"%*s%#7.5g%%  %9.9s  %8.8s\",\n            static_cast<int>(s.size()),\n            s.c_str(),\n            baselineNsPerIter_ / nsPerIter * 100.0,\n            detail::readableTime(secPerIter, 2).c_str(),\n            metricReadable(itersPerSec, 2).c_str());\n      }\n      for (auto const& name : counterNames_) {\n        if (auto ptr = folly::get_ptr(datum.counters, name)) {\n          switch (ptr->type) {\n            // UserMetrics constructed as precision_values avoid the\n            // implicit cast from long to double when formatting the output\n            case UserMetric::Type::TIME:\n              folly::variant_match(ptr->value, [&](auto value) {\n                row += stringPrintf(\n                    \"  %*s\",\n                    int(name.length()),\n                    detail::readableTime(value, 2).c_str());\n              });\n              break;\n            case UserMetric::Type::METRIC:\n              folly::variant_match(ptr->value, [&](auto value) {\n                row += stringPrintf(\n                    \"  %*s\",\n                    int(name.length()),\n                    metricReadable(value, 2).c_str());\n              });\n              break;\n            case UserMetric::Type::CUSTOM:\n            default:\n              folly::variant_match(ptr->value, [&](auto value) {\n                row += stringPrintf(\n                    \"  %*\" PRId64,\n                    int(name.length()),\n                    static_cast<int64_t>(value));\n              });\n          }\n        } else {\n          row += stringPrintf(\"  %*s\", int(name.length()), \"NaN\");\n        }\n      }\n      if (i < annotations.size() && !annotations[i].empty()) {\n        row += indent_;\n        row += detail::kANSIBoldYellow;\n        row += annotations[i];\n        row += detail::kANSIReset;\n      }\n      line(row);\n    }\n  }\n\n private:\n  bool isBaselineSet() {\n    return baselineNsPerIter_ != numeric_limits<double>::max();\n  }\n\n  void line(std::string_view s) { *os_ << indent_ << s << std::endl; }\n\n  std::set<std::string> counterNames_;\n  size_t namesLength_{0};\n  std::ostream* os_;\n  std::string indent_;\n  size_t columns_{0};\n  double baselineNsPerIter_{numeric_limits<double>::max()};\n  string lastFile_;\n};\n} // namespace\n\nstatic void printBenchmarkResultsAsJson(\n    const vector<detail::BenchmarkResult>& data) {\n  dynamic d = dynamic::object;\n  for (auto& datum : data) {\n    d[datum.name] = datum.timeInNs * 1000.;\n  }\n\n  printf(\"%s\\n\", toPrettyJson(d).c_str());\n}\n\nvoid benchmarkResultsToDynamic(\n    const vector<detail::BenchmarkResult>& data, dynamic& out) {\n  out = dynamic::array;\n  for (auto& datum : data) {\n    if (!datum.counters.empty()) {\n      dynamic obj = dynamic::object;\n      for (auto& counter : datum.counters) {\n        dynamic counterInfo = dynamic::object;\n        folly::variant_match(counter.second.value, [&](auto value) {\n          counterInfo[\"value\"] = value;\n        });\n        counterInfo[\"type\"] = static_cast<int>(counter.second.type);\n        obj[counter.first] = counterInfo;\n      }\n      out.push_back(\n          dynamic::array(datum.file, datum.name, datum.timeInNs, obj));\n    } else {\n      out.push_back(dynamic::array(datum.file, datum.name, datum.timeInNs));\n    }\n  }\n}\n\nvoid benchmarkResultsFromDynamic(\n    const dynamic& d, vector<detail::BenchmarkResult>& results) {\n  for (auto& datum : d) {\n    results.push_back(\n        {datum[0].asString(),\n         datum[1].asString(),\n         datum[2].asDouble(),\n         UserCounters{}});\n  }\n}\n\nstatic pair<StringPiece, StringPiece> resultKey(\n    const detail::BenchmarkResult& result) {\n  return pair<StringPiece, StringPiece>(result.file, result.name);\n}\n\nvoid printResultComparison(\n    const vector<detail::BenchmarkResult>& base,\n    const vector<detail::BenchmarkResult>& test) {\n  map<pair<StringPiece, StringPiece>, double> baselines;\n\n  for (auto& baseResult : base) {\n    baselines[resultKey(baseResult)] = baseResult.timeInNs;\n  }\n\n  // Width available\n  const size_t columns = FLAGS_bm_result_width_chars;\n\n  auto sep = [&](char pad) { puts(string(columns, pad).c_str()); };\n\n  auto header = [&](const string_view& file) {\n    sep('=');\n    printf(\"%s\\n\", headerContents(file, columns).c_str());\n    sep('=');\n  };\n\n  string lastFile;\n\n  for (auto& datum : test) {\n    folly::Optional<double> baseline =\n        folly::get_optional(baselines, resultKey(datum));\n    auto file = datum.file;\n    if (file != lastFile) {\n      // New file starting\n      header(file);\n      lastFile = file;\n    }\n\n    string s = datum.name;\n    if (s == \"-\") {\n      sep('-');\n      continue;\n    }\n    if (s[0] == '%') {\n      s.erase(0, 1);\n    }\n    s.resize(columns - 29, ' ');\n    auto nsPerIter = datum.timeInNs;\n    auto secPerIter = nsPerIter / 1E9;\n    auto itersPerSec = (secPerIter == 0)\n        ? std::numeric_limits<double>::infinity()\n        : (1 / secPerIter);\n    if (!baseline) {\n      // Print without baseline\n      printf(\n          \"%*s           %9s  %7s\\n\",\n          static_cast<int>(s.size()),\n          s.c_str(),\n          detail::readableTime(secPerIter, 2).c_str(),\n          metricReadable(itersPerSec, 2).c_str());\n    } else {\n      // Print with baseline\n      auto rel = *baseline / nsPerIter * 100.0;\n      printf(\n          \"%*s %7.2f%%  %9s  %7s\\n\",\n          static_cast<int>(s.size()),\n          s.c_str(),\n          rel,\n          detail::readableTime(secPerIter, 2).c_str(),\n          metricReadable(itersPerSec, 2).c_str());\n    }\n  }\n  sep('=');\n}\n\nvoid checkRunMode() {\n  if (folly::kIsDebug || folly::kIsSanitize) {\n    std::cerr\n        << detail::kANSIBoldYellow << \"WARNING: \" << detail::kANSIReset\n        << \"Benchmark running \"\n        << (folly::kIsDebug ? \"in DEBUG mode\" : \"with SANITIZERS\") << std::endl;\n  }\n}\n\nnamespace {\n\nstruct BenchmarksToRun {\n  const detail::BenchmarkRegistration* baseline = nullptr;\n  const detail::BenchmarkRegistration* suspenderBaseline = nullptr;\n  std::vector<const detail::BenchmarkRegistration*> benchmarks;\n  std::vector<size_t> separatorsAfter;\n};\n\nvoid addSeparator(BenchmarksToRun& res) {\n  size_t separatorAfter = res.benchmarks.size();\n  if (separatorAfter == 0) {\n    return;\n  }\n  if (res.separatorsAfter.empty() ||\n      res.separatorsAfter.back() != (separatorAfter - 1)) {\n    res.separatorsAfter.push_back(res.benchmarks.size() - 1);\n  }\n}\n\nBenchmarksToRun selectBenchmarksToRun(\n    const std::vector<detail::BenchmarkRegistration>& benchmarks) {\n  BenchmarksToRun res;\n\n  folly::Optional<boost::regex> bmRegex;\n  folly::Optional<boost::regex> bmFileRegex;\n\n  res.benchmarks.reserve(benchmarks.size());\n\n  if (!FLAGS_bm_regex.empty()) {\n    bmRegex.emplace(FLAGS_bm_regex);\n  }\n\n  if (!FLAGS_bm_file_regex.empty()) {\n    bmFileRegex.emplace(FLAGS_bm_file_regex);\n  }\n\n  for (auto& bm : benchmarks) {\n    if (bm.name == \"-\") {\n      addSeparator(res);\n      continue;\n    }\n\n    if (bm.name == kGlobalBenchmarkBaseline) {\n      res.baseline = &bm;\n      continue;\n    }\n\n    if (bm.name == kGlobalBenchmarkSuspenderBaseline) {\n      res.suspenderBaseline = &bm;\n      continue;\n    }\n\n    bool matchedName = !bmRegex || boost::regex_search(bm.name, *bmRegex);\n    bool matchedFile =\n        !bmFileRegex || boost::regex_search(bm.file, *bmFileRegex);\n\n    if (matchedName && matchedFile) {\n      res.benchmarks.push_back(&bm);\n    }\n  }\n\n  if (bmRegex) {\n    res.separatorsAfter.clear();\n  }\n\n  CHECK(res.baseline);\n\n  return res;\n}\n\nvoid maybeRunWarmUpIteration(const BenchmarksToRun& toRun) {\n  bool shouldRun = FLAGS_bm_warm_up_iteration;\n\n#if FOLLY_PERF_IS_SUPPORTED\n  shouldRun = shouldRun || !FLAGS_bm_perf_args.empty();\n#endif\n\n  if (!shouldRun) {\n    return;\n  }\n\n  if (FLAGS_bm_verbose) {\n    LOG(INFO) << \"Running warmup for all benchmarks...\";\n  }\n  for (const auto* bm : toRun.benchmarks) {\n    bm->func(1);\n  }\n  if (FLAGS_bm_verbose) {\n    LOG(INFO) << \"Warmup complete\";\n  }\n}\n\nclass ShouldDrawLineTracker {\n public:\n  explicit ShouldDrawLineTracker(const std::vector<size_t>& separatorsAfter)\n      : separatorsAfter_(&separatorsAfter) {}\n\n  bool operator()() {\n    std::size_t i = curI_++;\n    if (drawAfterI_ >= separatorsAfter_->size()) {\n      return false;\n    }\n    std::size_t nextToDrawAfter = (*separatorsAfter_)[drawAfterI_];\n    if (i == nextToDrawAfter) {\n      ++drawAfterI_;\n      return true;\n    }\n    return false;\n  }\n\n private:\n  const std::vector<std::size_t>* separatorsAfter_;\n  std::size_t curI_ = 0;\n  std::size_t drawAfterI_ = 0;\n};\n\n// Helper for adaptive mode\nauto runAdaptiveMode(\n    auto* printer, const BenchmarksToRun& toRun, int64_t sliceUsec) {\n  auto rrResult = detail::runBenchmarksAdaptive(\n      toRun.benchmarks,\n      toRun.baseline->func,\n      toRun.suspenderBaseline->func,\n      {.sliceUsec = sliceUsec,\n       .targetPercentile = FLAGS_bm_target_percentile,\n       .targetPrecisionPct = FLAGS_bm_target_precision_pct,\n       .minSamples = static_cast<size_t>(FLAGS_bm_min_samples),\n       .minSecs = FLAGS_bm_min_secs,\n       .maxSecs = FLAGS_bm_max_secs,\n       .verbose = FLAGS_bm_verbose,\n       .quiet = FLAGS_bm_quiet});\n\n  if (printer != nullptr) {\n    ShouldDrawLineTracker lineTracker(toRun.separatorsAfter);\n    for (const auto& r : rrResult.results) {\n      printer->print({{r.file, r.name, r.timeInNs, r.counters}});\n      if (lineTracker()) {\n        printer->separator('-');\n      }\n    }\n  }\n\n  return std::pair{std::set<std::string>{}, std::move(rrResult.results)};\n}\n\n// Returns true when a user overrode a gflag on the command line.\nbool userSetGflag([[maybe_unused]] const char* name) {\n#if FOLLY_HAVE_LIBGFLAGS && __has_include(<gflags/gflags.h>)\n  return !gflags::GetCommandLineFlagInfoOrDie(name).is_default;\n#else\n  // No libgflags means we just have global vars, without CLI args.\n  // Falling back to false is fine since we only use this for warnings.\n  return false;\n#endif\n}\n\n// Check that no mode-incompatible flags were explicitly set.\nvoid validateFlagCombinations() {\n  // Log a user-facing error and exit without a stack trace.\n  auto fatal = [](const std::string& msg) {\n    LOG(ERROR) << detail::kANSIBoldRed << msg << detail::kANSIReset;\n    exit(1);\n  };\n\n  if (FLAGS_bm_mode != \"best-of\" && FLAGS_bm_mode != \"adaptive\") {\n    fatal(\n        fmt::format(\n            \"Unknown --bm_mode='{}'. Must be 'best-of' or 'adaptive'.\",\n            FLAGS_bm_mode));\n  }\n\n  if (FLAGS_bm_mode == \"adaptive\") {\n    if (userSetGflag(\"bm_min_iters\")) {\n      fatal(\"--bm_min_iters is only useful in --bm_mode=best-of.\");\n    }\n    if (userSetGflag(\"bm_max_iters\")) {\n      fatal(\"--bm_max_iters is only useful in --bm_mode=best-of.\");\n    }\n    if (userSetGflag(\"bm_max_trials\")) {\n      fatal(\"--bm_max_trials is only useful in --bm_mode=best-of.\");\n    }\n    if (userSetGflag(\"bm_estimate_time\")) {\n      fatal(\n          \"--bm_estimate_time is incompatible with adaptive mode. \"\n          \"Adaptive already targets a configurable percentile \"\n          \"(--bm_target_percentile).\");\n    }\n    if (userSetGflag(\"bm_profile\")) {\n      fatal(\n          \"--bm_profile is not supported in adaptive mode. \"\n          \"Use --bm_perf_args to attach perf in any mode.\");\n    }\n  } else {\n    // Best-of mode\n    if (userSetGflag(\"bm_target_percentile\")) {\n      fatal(\"--bm_target_percentile requires --bm_mode=adaptive.\");\n    }\n    if (userSetGflag(\"bm_target_precision_pct\")) {\n      fatal(\"--bm_target_precision_pct requires --bm_mode=adaptive.\");\n    }\n    if (userSetGflag(\"bm_min_secs\")) {\n      fatal(\"--bm_min_secs requires --bm_mode=adaptive.\");\n    }\n    if (userSetGflag(\"bm_min_samples\")) {\n      fatal(\"--bm_min_samples requires --bm_mode=adaptive.\");\n    }\n    if (FLAGS_bm_estimate_time && !FLAGS_bm_quiet) {\n      LOG(WARNING)\n          << detail::kANSIBoldYellow\n          << \"--bm_estimate_time is slow, with odd semantics (geometric mean \"\n          << \"of p25-p75). Consider --bm_mode=adaptive (BenchmarkAdaptive.md).\"\n          << detail::kANSIReset;\n    }\n  }\n}\n\n// Resolve `--bm_slice_usec` / `--bm_min_usec` (deprecated alias).\nint64_t resolveSliceUsec() {\n  bool minUsecSet = userSetGflag(\"bm_min_usec\");\n  bool sliceUsecSet = userSetGflag(\"bm_slice_usec\");\n  if (minUsecSet && sliceUsecSet) {\n    LOG(ERROR)\n        << detail::kANSIBoldRed\n        << \"Cannot set both --bm_min_usec and --bm_slice_usec. \"\n        << \"Use --bm_slice_usec only (--bm_min_usec is deprecated).\"\n        << detail::kANSIReset;\n    exit(1);\n  }\n  if (minUsecSet) {\n    LOG(ERROR) << detail::kANSIBoldRed << \"--bm_min_usec is deprecated; \"\n               << \"use --bm_slice_usec instead.\" << detail::kANSIReset;\n    return FLAGS_bm_min_usec;\n  }\n  return FLAGS_bm_slice_usec;\n}\n\nstd::pair<std::set<std::string>, std::vector<detail::BenchmarkResult>>\nrunBenchmarksWithPrinterImpl(\n    BenchmarkResultsPrinter* FOLLY_NULLABLE printer,\n    const BenchmarksToRun& toRun) {\n  vector<detail::BenchmarkResult> results;\n  results.reserve(toRun.benchmarks.size());\n\n  auto const sliceUsec = resolveSliceUsec();\n  if (sliceUsec < 1000) {\n    LOG(WARNING) << detail::kANSIBoldYellow << \"--bm_slice_usec=\" << sliceUsec\n                 << \" is below 1000; benchmark harness overhead may \"\n                 << \"interfere with results.\" << detail::kANSIReset;\n  }\n\n  validateFlagCombinations();\n\n  // PLEASE KEEP QUIET. MEASUREMENTS IN PROGRESS.\n\n  // Adaptive mode: interleaved sampling with paired correction for both\n  // baseline and suspender overhead. Does NOT set suspenderOverhead static;\n  // instead uses late correction based on suspensionCount.\n  if (FLAGS_bm_mode == \"adaptive\") {\n    return runAdaptiveMode(printer, toRun, sliceUsec);\n  }\n  // Encourage users to try `adaptive` when `--bm_mode` was not passed.\n  if (!userSetGflag(\"bm_mode\")) {\n    std::cerr\n        << \"NOTE: Default may change to \" << detail::kANSIBold\n        << \"faster & more robust `--bm_mode=adaptive`.\" << detail::kANSIReset\n        << \"\\n\"\n        << \"      Details in BenchmarkAdaptive.md.\\n\"\n        << \"Pass `--bm_mode=best-of` to keep current behavior -- report trial \"\n        << \"with lowest\\n\"\n        << \"iteration cost, each `1-2 * bm_slice_usec` long, repeated per \"\n        << \"benchmark up to\\n\"\n        << \"`bm_max_trials` or `bm_max_secs`.\\n\";\n  }\n\n  // Best-of mode: measure suspender overhead upfront and set static for\n  // immediate correction in tally().\n  if (FLAGS_bm_verbose) {\n    LOG(INFO) << \"Measuring suspenderBaseline...\";\n  }\n  auto const globalSuspenderBaseline = runBenchmarkGetNSPerIteration(\n      toRun.suspenderBaseline->func, 0, sliceUsec);\n\n  BenchmarkSuspender::suspenderOverhead = chrono::nanoseconds(\n      static_cast<chrono::high_resolution_clock::rep>(\n          globalSuspenderBaseline.first));\n\n  if (FLAGS_bm_verbose) {\n    LOG(INFO) << \"suspenderOverhead=\" << globalSuspenderBaseline.first\n              << \" ns/iter\";\n  }\n\n  if (FLAGS_bm_verbose) {\n    LOG(INFO) << \"Measuring globalBaseline...\";\n  }\n  auto const globalBaseline =\n      runBenchmarkGetNSPerIteration(toRun.baseline->func, 0, sliceUsec);\n  if (FLAGS_bm_verbose) {\n    LOG(INFO) << \"globalBaseline=\" << globalBaseline.first << \" ns/iter\";\n  }\n\n  std::set<std::string> counterNames;\n  ShouldDrawLineTracker shouldDrawLineTracker(toRun.separatorsAfter);\n  for (std::size_t i = 0; i != toRun.benchmarks.size(); ++i) {\n    std::pair<double, UserCounters> elapsed;\n    const detail::BenchmarkRegistration& bm = *toRun.benchmarks[i];\n    bool shouldDrawLineAfter = shouldDrawLineTracker();\n\n    if (FLAGS_bm_profile) {\n      elapsed = runProfilingGetNSPerIteration(bm.func, globalBaseline.first);\n    } else {\n      elapsed = FLAGS_bm_estimate_time\n          ? runBenchmarkGetNSPerIterationEstimate(bm.func, globalBaseline.first)\n          : runBenchmarkGetNSPerIteration(\n                bm.func, globalBaseline.first, sliceUsec);\n    }\n\n    // if customized user counters is used, it cannot print the result in real\n    // time as it needs to run all cases first to know the complete set of\n    // counters have been used, then the header can be printed out properly\n    if (printer != nullptr) {\n      printer->print({{bm.file, bm.name, elapsed.first, elapsed.second}});\n      if (shouldDrawLineAfter) {\n        printer->separator('-');\n      }\n    }\n    results.push_back({bm.file, bm.name, elapsed.first, elapsed.second});\n\n    // get all counter names\n    for (auto const& kv : elapsed.second) {\n      counterNames.insert(kv.first);\n    }\n  }\n\n  // MEASUREMENTS DONE.\n\n  return std::make_pair(std::move(counterNames), std::move(results));\n}\n\nstd::vector<detail::BenchmarkResult> resultsFromFile(\n    const std::string& filename) {\n  std::string content;\n  readFile(filename.c_str(), content);\n  std::vector<detail::BenchmarkResult> ret;\n  if (!content.empty()) {\n    benchmarkResultsFromDynamic(parseJson(content), ret);\n  }\n  return ret;\n}\n\nbool writeResultsToFile(\n    const std::vector<detail::BenchmarkResult>& results,\n    const std::string& filename) {\n  dynamic d;\n  benchmarkResultsToDynamic(results, d);\n  return writeFile(toPrettyJson(d), filename.c_str());\n}\n\n} // namespace\n\nnamespace detail {\n\nstd::ostream& operator<<(std::ostream& os, const BenchmarkResult& x) {\n  folly::dynamic r;\n  benchmarkResultsToDynamic({x}, r);\n  return os << r[0];\n}\n\nbool operator==(const BenchmarkResult& x, const BenchmarkResult& y) {\n  auto xtime = static_cast<std::uint64_t>(x.timeInNs * 1000);\n  auto ytime = static_cast<std::uint64_t>(y.timeInNs * 1000);\n  return x.name == y.name && x.file == y.file && xtime == ytime &&\n      x.counters == y.counters;\n}\n\nstd::chrono::high_resolution_clock::duration BenchmarkSuspenderBase::timeSpent;\nstd::chrono::high_resolution_clock::duration\n    BenchmarkSuspenderBase::suspenderOverhead;\nsize_t BenchmarkSuspenderBase::suspensionCount;\n\nvoid BenchmarkingStateBase::addBenchmarkImpl(\n    std::string file, std::string name, BenchmarkFun fun, bool useCounter) {\n  std::lock_guard guard(mutex_);\n  benchmarks_.push_back(\n      {std::move(file), std::move(name), std::move(fun), useCounter});\n}\n\nbool BenchmarkingStateBase::useCounters() const {\n  std::lock_guard guard(mutex_);\n  return std::any_of(\n      benchmarks_.begin(), benchmarks_.end(), [](const auto& bm) {\n        return bm.useCounter;\n      });\n}\n\nstd::vector<std::string> BenchmarkingStateBase::getBenchmarkList() {\n  std::vector<std::string> bmNames;\n  auto toRun = selectBenchmarksToRun(benchmarks_);\n  bmNames.reserve(toRun.benchmarks.size());\n  for (auto benchmarkRegistration : toRun.benchmarks) {\n    bmNames.push_back(benchmarkRegistration->name);\n  }\n\n  return bmNames;\n}\n\n// static\nfolly::StringPiece BenchmarkingStateBase::getGlobalBaselineNameForTests() {\n  return kGlobalBenchmarkBaseline;\n}\n\nfolly::StringPiece\nBenchmarkingStateBase::getGlobalSuspenderBaselineNameForTests() {\n  return kGlobalBenchmarkSuspenderBaseline;\n}\n\nPerfScoped BenchmarkingStateBase::doSetUpPerfScoped(\n    const std::vector<std::string>& args) const {\n  return PerfScoped{args};\n}\n\nPerfScoped BenchmarkingStateBase::setUpPerfScoped() const {\n  std::vector<std::string> perfArgs;\n#if FOLLY_PERF_IS_SUPPORTED\n  folly::split(' ', FLAGS_bm_perf_args, perfArgs, true);\n#endif\n  if (perfArgs.empty()) {\n    return PerfScoped{};\n  }\n  return doSetUpPerfScoped(perfArgs);\n}\n\ntemplate <typename Printer>\nstd::pair<std::set<std::string>, std::vector<BenchmarkResult>>\nBenchmarkingStateBase::runBenchmarksWithPrinter(Printer* printer) const {\n  if (FLAGS_bm_verbose) {\n    LOG(INFO) << \"Benchmark run starting...\";\n  }\n  std::lock_guard guard(mutex_);\n  BenchmarksToRun toRun = selectBenchmarksToRun(benchmarks_);\n  maybeRunWarmUpIteration(toRun);\n\n  detail::PerfScoped perf = setUpPerfScoped();\n  return runBenchmarksWithPrinterImpl(printer, toRun);\n}\n\nstd::vector<BenchmarkResult> BenchmarkingStateBase::runBenchmarksWithResults()\n    const {\n  return runBenchmarksWithPrinter(\n             static_cast<BenchmarkResultsPrinter*>(nullptr))\n      .second;\n}\n\nstd::vector<BenchmarkResult> runBenchmarksWithResults() {\n  return globalBenchmarkState().runBenchmarksWithResults();\n}\n\nstd::string benchmarkResultsToString(\n    const std::vector<BenchmarkResult>& results,\n    std::string_view indent,\n    const std::vector<std::string>& annotations) {\n  size_t maxAnnotationWidth = 0;\n  for (const auto& a : annotations) {\n    if (!a.empty()) {\n      maxAnnotationWidth =\n          std::max(maxAnnotationWidth, indent.size() + a.size());\n    }\n  }\n  std::set<std::string> counterNames;\n  for (const auto& r : results) {\n    for (const auto& [key, _] : r.counters) {\n      counterNames.insert(key);\n    }\n  }\n  std::ostringstream oss;\n  BenchmarkResultsPrinter printer(\n      std::move(counterNames), &oss, indent, maxAnnotationWidth);\n  printer.print(results, annotations);\n  printer.separator('=');\n  return oss.str();\n}\n\n} // namespace detail\n\nvoid runBenchmarks() {\n  auto& state = detail::globalBenchmarkState();\n\n  if (FLAGS_bm_list) {\n    auto bmNames = state.getBenchmarkList();\n    for (auto testName : bmNames) {\n      std::cout << testName << std::endl;\n    }\n    return;\n  }\n\n  if (FLAGS_bm_profile) {\n    printf(\n        \"WARNING: Running with constant number of iterations. Results might be jittery.\\n\");\n  }\n\n  if (FLAGS_bm_min_iters >= FLAGS_bm_max_iters) {\n    std::cerr << \"WARNING: bm_min_iters > bm_max_iters; increasing the max\"\n              << std::endl;\n    FLAGS_bm_max_iters = FLAGS_bm_min_iters + 1;\n  }\n\n  checkRunMode();\n\n  BenchmarkResultsPrinter printer;\n  bool useCounter = state.useCounters();\n\n  // PLEASE KEEP QUIET. MEASUREMENTS IN PROGRESS.\n\n  const bool shouldPrintInline =\n      FLAGS_bm_relative_to.empty() && !FLAGS_json && !useCounter;\n  auto benchmarkResults =\n      state.runBenchmarksWithPrinter(shouldPrintInline ? &printer : nullptr);\n\n  // PLEASE MAKE NOISE. MEASUREMENTS DONE.\n\n  if (FLAGS_json) {\n    printBenchmarkResultsAsJson(benchmarkResults.second);\n  } else if (!FLAGS_bm_relative_to.empty()) {\n    printResultComparison(\n        resultsFromFile(FLAGS_bm_relative_to), benchmarkResults.second);\n  } else if (!shouldPrintInline) {\n    printer = BenchmarkResultsPrinter{std::move(benchmarkResults.first)};\n    printer.print(benchmarkResults.second);\n    printer.separator('=');\n  }\n\n  if (!FLAGS_bm_json_verbose.empty()) {\n    writeResultsToFile(benchmarkResults.second, FLAGS_bm_json_verbose);\n  }\n\n  checkRunMode();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Benchmark.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/BenchmarkUtil.h>\n#include <folly/Portability.h>\n#include <folly/Preprocessor.h> // for FB_ANONYMOUS_VARIABLE\n#include <folly/Range.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Hint.h>\n#include <folly/portability/GFlags.h>\n\n#include <cassert>\n#include <chrono>\n#include <functional>\n#include <iosfwd>\n#include <limits>\n#include <mutex>\n#include <set>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <variant>\n\n#include <boost/function_types/function_arity.hpp>\n#include <glog/logging.h>\n\nFOLLY_GFLAGS_DECLARE_bool(benchmark);\nFOLLY_GFLAGS_DECLARE_bool(bm_quiet);\nFOLLY_GFLAGS_DECLARE_uint32(bm_result_width_chars);\nFOLLY_GFLAGS_DECLARE_int32(bm_min_iters);\nFOLLY_GFLAGS_DECLARE_int64(bm_max_iters);\n\nnamespace folly {\n\n/**\n * Runs all benchmarks defined. Usually put in main().\n */\nvoid runBenchmarks();\n\n/**\n * Runs all benchmarks defined if and only if the --benchmark flag has\n * been passed to the program. Usually put in main().\n */\ninline bool runBenchmarksOnFlag() {\n  if (FLAGS_benchmark) {\n    runBenchmarks();\n  }\n  return FLAGS_benchmark;\n}\n\nclass UserMetric {\n public:\n  enum class Type { CUSTOM, TIME, METRIC };\n  std::variant<int64_t, double> value;\n  Type type{Type::CUSTOM};\n\n  UserMetric() = default;\n  /* implicit */ UserMetric(int64_t val, Type typ = Type::CUSTOM)\n      : value(val), type(typ) {}\n\n  // Allow users to provide precision values\n  template <\n      typename T,\n      typename = std::enable_if_t<std::is_floating_point_v<T>>>\n  explicit UserMetric(T precision_val, Type typ = Type::CUSTOM)\n      : value(convert_helper(precision_val)), type(typ) {}\n\n  friend bool operator==(const UserMetric& x, const UserMetric& y) {\n    return x.value == y.value && x.type == y.type;\n  }\n  friend bool operator!=(const UserMetric& x, const UserMetric& y) {\n    return !(x == y);\n  }\n\n private:\n  double convert_helper(double val) { return val; }\n};\n\nusing UserCounters = std::unordered_map<std::string, UserMetric>;\n\nnamespace detail {\nstruct TimeIterData {\n  std::chrono::high_resolution_clock::duration duration;\n  unsigned int niter;\n  UserCounters userCounters;\n  size_t suspensionCount = 0;\n};\n\nusing BenchmarkFun = std::function<TimeIterData(unsigned int)>;\n\nstruct BenchmarkRegistration {\n  std::string file;\n  std::string name;\n  BenchmarkFun func;\n  bool useCounter = false;\n};\n\nstruct BenchmarkResult {\n  std::string file;\n  std::string name;\n  double timeInNs;\n  UserCounters counters;\n\n  friend std::ostream& operator<<(std::ostream&, const BenchmarkResult&);\n\n  friend bool operator==(const BenchmarkResult&, const BenchmarkResult&);\n  friend bool operator!=(const BenchmarkResult& x, const BenchmarkResult& y) {\n    return !(x == y);\n  }\n};\n\nstruct BenchmarkSuspenderBase {\n  /**\n   * Accumulates time spent outside benchmark.\n   */\n  static std::chrono::high_resolution_clock::duration timeSpent;\n  /**\n   * Overhead per suspension (set once at startup for best-of mode,\n   * left at 0 for adaptive mode which does late correction).\n   */\n  static std::chrono::high_resolution_clock::duration suspenderOverhead;\n  /**\n   * Number of suspensions in the current benchmark invocation.\n   * Reset before each benchmark call, used for late overhead correction.\n   */\n  static size_t suspensionCount;\n};\n\ntemplate <typename Clock>\nstruct BenchmarkSuspender : BenchmarkSuspenderBase {\n  using TimePoint = std::chrono::high_resolution_clock::time_point;\n  using Duration = std::chrono::high_resolution_clock::duration;\n\n  struct DismissedTag {};\n  static inline constexpr DismissedTag Dismissed{};\n\n  BenchmarkSuspender() : start(Clock::now()) {}\n\n  explicit BenchmarkSuspender(DismissedTag) : start(TimePoint{}) {}\n\n  BenchmarkSuspender(const BenchmarkSuspender&) = delete;\n  BenchmarkSuspender(BenchmarkSuspender&& rhs) noexcept {\n    start = rhs.start;\n    rhs.start = {};\n  }\n\n  BenchmarkSuspender& operator=(const BenchmarkSuspender&) = delete;\n  BenchmarkSuspender& operator=(BenchmarkSuspender&& rhs) noexcept {\n    if (start != TimePoint{}) {\n      tally();\n    }\n    start = rhs.start;\n    rhs.start = {};\n    return *this;\n  }\n\n  ~BenchmarkSuspender() {\n    if (start != TimePoint{}) {\n      tally();\n    }\n  }\n\n  void dismiss() {\n    assert(start != TimePoint{});\n    tally();\n    start = {};\n  }\n\n  void rehire() {\n    assert(start == TimePoint{});\n    start = Clock::now();\n  }\n\n  template <class F>\n  auto dismissing(F f) -> invoke_result_t<F> {\n    SCOPE_EXIT {\n      rehire();\n    };\n    dismiss();\n    return f();\n  }\n\n  /**\n   * This is for use inside of if-conditions, used in BENCHMARK macros.\n   * If-conditions bypass the explicit on operator bool.\n   */\n  explicit operator bool() const { return false; }\n\n private:\n  void tally() {\n    auto end = Clock::now();\n    timeSpent += (end - start) + suspenderOverhead;\n    ++suspensionCount;\n    start = end;\n  }\n\n  TimePoint start;\n};\n\nclass PerfScoped;\n\nclass BenchmarkingStateBase {\n public:\n  template <typename Printer>\n  std::pair<std::set<std::string>, std::vector<BenchmarkResult>>\n  runBenchmarksWithPrinter(Printer* printer) const;\n\n  std::vector<BenchmarkResult> runBenchmarksWithResults() const;\n\n  static folly::StringPiece getGlobalBaselineNameForTests();\n  static folly::StringPiece getGlobalSuspenderBaselineNameForTests();\n\n  bool useCounters() const;\n\n  void addBenchmarkImpl(\n      std::string file, std::string name, BenchmarkFun, bool useCounter);\n\n  std::vector<std::string> getBenchmarkList();\n\n protected:\n  // There is no need for this virtual but we overcome a check\n  virtual ~BenchmarkingStateBase() = default;\n\n  PerfScoped setUpPerfScoped() const;\n\n  // virtual for purely testing purposes.\n  virtual PerfScoped doSetUpPerfScoped(\n      const std::vector<std::string>& args) const;\n\n  mutable std::mutex mutex_;\n  std::vector<BenchmarkRegistration> benchmarks_;\n};\n\ntemplate <typename Clock>\nclass BenchmarkingState : public BenchmarkingStateBase {\n public:\n  template <typename Lambda>\n  typename std::enable_if<folly::is_invocable_v<Lambda, unsigned>>::type\n  addBenchmark(std::string file, std::string name, Lambda&& lambda) {\n    auto execute = [=](unsigned int times) {\n      BenchmarkSuspender<Clock>::timeSpent = {};\n      BenchmarkSuspender<Clock>::suspensionCount = 0;\n      unsigned int niter;\n\n      // CORE MEASUREMENT STARTS\n      auto start = Clock::now();\n      niter = lambda(times);\n      auto end = Clock::now();\n      // CORE MEASUREMENT ENDS\n      return detail::TimeIterData{\n          (end - start) - BenchmarkSuspender<Clock>::timeSpent,\n          niter,\n          UserCounters{},\n          BenchmarkSuspender<Clock>::suspensionCount};\n    };\n\n    this->addBenchmarkImpl(\n        std::move(file), std::move(name), detail::BenchmarkFun(execute), false);\n  }\n\n  template <typename Lambda>\n  typename std::enable_if<folly::is_invocable_v<Lambda>>::type addBenchmark(\n      std::string file, std::string name, Lambda&& lambda) {\n    addBenchmark(std::move(file), std::move(name), [=](unsigned int times) {\n      unsigned int niter = 0;\n      while (times-- > 0) {\n        niter += lambda();\n      }\n      return niter;\n    });\n  }\n\n  template <typename Lambda>\n  typename std::enable_if<\n      folly::is_invocable_v<Lambda, UserCounters&, unsigned>>::type\n  addBenchmark(std::string file, std::string name, Lambda&& lambda) {\n    auto execute = [=](unsigned int times) {\n      BenchmarkSuspender<Clock>::timeSpent = {};\n      BenchmarkSuspender<Clock>::suspensionCount = 0;\n      unsigned int niter;\n\n      // CORE MEASUREMENT STARTS\n      auto start = std::chrono::high_resolution_clock::now();\n      UserCounters counters;\n      niter = lambda(counters, times);\n      auto end = std::chrono::high_resolution_clock::now();\n      // CORE MEASUREMENT ENDS\n      return detail::TimeIterData{\n          (end - start) - BenchmarkSuspender<Clock>::timeSpent,\n          niter,\n          counters,\n          BenchmarkSuspender<Clock>::suspensionCount};\n    };\n\n    this->addBenchmarkImpl(\n        std::move(file),\n        std::move(name),\n        std::function<detail::TimeIterData(unsigned int)>(execute),\n        true);\n  }\n\n  template <typename Lambda>\n  typename std::enable_if<folly::is_invocable_v<Lambda, UserCounters&>>::type\n  addBenchmark(std::string file, std::string name, Lambda&& lambda) {\n    addBenchmark(\n        std::move(file),\n        std::move(name),\n        [=](UserCounters& counters, unsigned int times) {\n          unsigned int niter = 0;\n          while (times-- > 0) {\n            niter += lambda(counters);\n          }\n          return niter;\n        });\n  }\n};\n\nBenchmarkingState<std::chrono::high_resolution_clock>& globalBenchmarkState();\n\n/**\n * Runs all benchmarks defined in the program, doesn't print by default.\n * Usually used when customized printing of results is desired.\n */\nstd::vector<BenchmarkResult> runBenchmarksWithResults();\n\n// Format benchmark results as a human-readable table string.\n// Shares formatting logic with the default results printer.\n// `annotations[i]` is appended to the corresponding row.\nstd::string benchmarkResultsToString(\n    const std::vector<BenchmarkResult>& results,\n    std::string_view indent = \"\",\n    const std::vector<std::string>& annotations = {});\n\n// Format a time value (in seconds) as a human-readable string, e.g. \"8.5us\".\nstd::string readableTime(double n, unsigned int decimals);\n\n/**\n * Adds a benchmark wrapped in a std::function.\n * Was designed to only be used internally but, unfortunately,\n * is not.\n */\ninline void addBenchmarkImpl(\n    std::string file, std::string name, BenchmarkFun f, bool useCounter) {\n  globalBenchmarkState().addBenchmarkImpl(\n      std::move(file), std::move(name), std::move(f), useCounter);\n}\n\n} // namespace detail\n\n/**\n * Supporting type for BENCHMARK_SUSPEND defined below.\n */\nstruct BenchmarkSuspender\n    : detail::BenchmarkSuspender<std::chrono::high_resolution_clock> {\n  using Impl = detail::BenchmarkSuspender<std::chrono::high_resolution_clock>;\n  using Impl::Impl;\n};\n\n/**\n * Adds a benchmark. Usually not called directly but instead through\n * the macro BENCHMARK defined below.\n * The lambda function involved can have one of the following forms:\n *  * take zero parameters, and the benchmark calls it repeatedly\n *  * take exactly one parameter of type unsigned, and the benchmark\n *    uses it with counter semantics (iteration occurs inside the\n *    function).\n *  * 2 versions of the above cases but also accept UserCounters& as\n *    as their first parameter.\n */\ntemplate <typename Lambda>\nvoid addBenchmark(std::string file, std::string name, Lambda&& lambda) {\n  detail::globalBenchmarkState().addBenchmark(\n      std::move(file), std::move(name), lambda);\n}\n\nstruct dynamic;\n\nvoid benchmarkResultsToDynamic(\n    const std::vector<detail::BenchmarkResult>& data, dynamic&);\n\nvoid benchmarkResultsFromDynamic(\n    const dynamic&, std::vector<detail::BenchmarkResult>&);\n\nvoid printResultComparison(\n    const std::vector<detail::BenchmarkResult>& base,\n    const std::vector<detail::BenchmarkResult>& test);\n\n} // namespace folly\n\n/**\n * Introduces a benchmark function. Used internally, see BENCHMARK and\n * friends below.\n */\n\nFOLLY_PUSH_WARNING\nFOLLY_CLANG_DISABLE_WARNING(\"-Wglobal-constructors\")\n\n#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName) \\\n  static void funName(paramType);                                     \\\n  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(           \\\n      follyBenchmarkUnused) =                                         \\\n      (::folly::addBenchmark(                                         \\\n           __FILE__,                                                  \\\n           stringName,                                                \\\n           [](paramType paramName) -> unsigned {                      \\\n             funName(paramName);                                      \\\n             return rv;                                               \\\n           }),                                                        \\\n       true);                                                         \\\n  static void funName(paramType paramName)\n\n#define BENCHMARK_IMPL_COUNTERS(                                             \\\n    funName, stringName, counters, rv, paramType, paramName)                 \\\n  static void funName(                                                       \\\n      ::folly::UserCounters& FOLLY_PP_DETAIL_APPEND_VA_ARG(paramType));      \\\n  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(                  \\\n      follyBenchmarkUnused) =                                                \\\n      (::folly::addBenchmark(                                                \\\n           __FILE__,                                                         \\\n           stringName,                                                       \\\n           [](::folly::UserCounters& counters FOLLY_PP_DETAIL_APPEND_VA_ARG( \\\n               paramType paramName)) -> unsigned {                           \\\n             funName(counters FOLLY_PP_DETAIL_APPEND_VA_ARG(paramName));     \\\n             return rv;                                                      \\\n           }),                                                               \\\n       true);                                                                \\\n  static void funName(                                                       \\\n      [[maybe_unused]] ::folly::UserCounters& counters                       \\\n          FOLLY_PP_DETAIL_APPEND_VA_ARG(paramType paramName))\n\n/**\n * Introduces a benchmark function with support for returning the actual\n * number of iterations. Used internally, see BENCHMARK_MULTI and friends\n * below.\n */\n#define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \\\n  static unsigned funName(paramType);                                   \\\n  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(             \\\n      follyBenchmarkUnused) =                                           \\\n      (::folly::addBenchmark(                                           \\\n           __FILE__,                                                    \\\n           stringName,                                                  \\\n           [](paramType paramName) { return funName(paramName); }),     \\\n       true);                                                           \\\n  static unsigned funName(paramType paramName)\n\nFOLLY_POP_WARNING\n\n/**\n * Introduces a benchmark function. Use with either one or two arguments.\n * The first is the name of the benchmark. Use something descriptive, such\n * as insertVectorBegin. The second argument may be missing, or could be a\n * symbolic counter. The counter dictates how many internal iteration the\n * benchmark does. Example:\n *\n * BENCHMARK(vectorPushBack) {\n *   vector<int> v;\n *   v.push_back(42);\n * }\n *\n * BENCHMARK(insertVectorBegin, iters) {\n *   vector<int> v;\n *   for (unsigned int i = 0; i < iters; ++i) {\n *     v.insert(v.begin(), 42);\n *   }\n * }\n */\n#define BENCHMARK(name, ...)                   \\\n  BENCHMARK_IMPL(                              \\\n      name,                                    \\\n      FOLLY_PP_STRINGIZE(name),                \\\n      FB_ARG_2_OR_1(1, ##__VA_ARGS__),         \\\n      FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \\\n      __VA_ARGS__)\n\n/**\n * Allow users to record customized counter during benchmarking,\n * there will be one extra column showing in the output result for each counter\n *\n * BENCHMARK_COUNTERS(insertVectorBegin, counters, iters) {\n *   vector<int> v;\n *   for (unsigned int i = 0; i < iters; ++i) {\n *     v.insert(v.begin(), 42);\n *   }\n *   BENCHMARK_SUSPEND {\n *      counters[\"foo\"] = 10;\n *   }\n * }\n */\n#define BENCHMARK_COUNTERS(name, counters, ...) \\\n  BENCHMARK_IMPL_COUNTERS(                      \\\n      name,                                     \\\n      FOLLY_PP_STRINGIZE(name),                 \\\n      counters,                                 \\\n      FB_ARG_2_OR_1(1, ##__VA_ARGS__),          \\\n      FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__),  \\\n      __VA_ARGS__)\n/**\n * Like BENCHMARK above, but allows the user to return the actual\n * number of iterations executed in the function body. This can be\n * useful if the benchmark function doesn't know upfront how many\n * iterations it's going to run or if it runs through a certain\n * number of test cases, e.g.:\n *\n * BENCHMARK_MULTI(benchmarkSomething) {\n *   std::vector<int> testCases { 0, 1, 1, 2, 3, 5 };\n *   for (int c : testCases) {\n *     doSomething(c);\n *   }\n *   return testCases.size();\n * }\n */\n#define BENCHMARK_MULTI(name, ...)             \\\n  BENCHMARK_MULTI_IMPL(                        \\\n      name,                                    \\\n      FOLLY_PP_STRINGIZE(name),                \\\n      FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \\\n      __VA_ARGS__)\n\n/**\n * Defines a benchmark that passes a parameter to another one. This is\n * common for benchmarks that need a \"problem size\" in addition to\n * \"number of iterations\". Consider:\n *\n * void pushBack(uint32_t n, size_t initialSize) {\n *   vector<int> v;\n *   BENCHMARK_SUSPEND {\n *     v.resize(initialSize);\n *   }\n *   for (uint32_t i = 0; i < n; ++i) {\n *    v.push_back(i);\n *   }\n * }\n * BENCHMARK_PARAM(pushBack, 0)\n * BENCHMARK_PARAM(pushBack, 1000)\n * BENCHMARK_PARAM(pushBack, 1000000)\n *\n * The benchmark above estimates the speed of push_back at different\n * initial sizes of the vector. The framework will pass 0, 1000, and\n * 1000000 for initialSize, and the iteration count for n.\n */\n#define BENCHMARK_PARAM(name, param) BENCHMARK_NAMED_PARAM(name, param, param)\n\n/**\n * Like BENCHMARK_PARAM, but passes a UserCounters& parameter.\n */\n#define BENCHMARK_COUNTERS_PARAM(name, param) \\\n  BENCHMARK_COUNTERS_NAMED_PARAM(name, param, param)\n\n/**\n * Same as BENCHMARK_PARAM, but allows one to return the actual number of\n * iterations that have been run.\n */\n#define BENCHMARK_PARAM_MULTI(name, param) \\\n  BENCHMARK_NAMED_PARAM_MULTI(name, param, param)\n\n/*\n * Like BENCHMARK_PARAM(), but allows a custom name to be specified for each\n * parameter, rather than using the parameter value.\n *\n * Useful when the parameter value is not a valid token for string pasting,\n * of when you want to specify multiple parameter arguments.\n *\n * For example:\n *\n * void addValue(uint32_t n, int64_t bucketSize, int64_t min, int64_t max) {\n *   Histogram<int64_t> hist(bucketSize, min, max);\n *   int64_t num = min;\n *   for (uint32_t i = 0; i < n; ++i) {\n *     hist.addValue(num);\n *     ++num;\n *     if (num > max) { num = min; }\n *   }\n * }\n *\n * BENCHMARK_NAMED_PARAM(addValue, 0_to_100, 1, 0, 100)\n * BENCHMARK_NAMED_PARAM(addValue, 0_to_1000, 10, 0, 1000)\n * BENCHMARK_NAMED_PARAM(addValue, 5k_to_20k, 250, 5000, 20000)\n */\n#define BENCHMARK_NAMED_PARAM(name, param_name, ...)                   \\\n  BENCHMARK_IMPL(                                                      \\\n      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),             \\\n      FOLLY_PP_STRINGIZE(name) \"(\" FOLLY_PP_STRINGIZE(param_name) \")\", \\\n      iters,                                                           \\\n      unsigned,                                                        \\\n      iters) {                                                         \\\n    name(iters, ##__VA_ARGS__);                                        \\\n  }\n\n/**\n * Like BENCHMARK_NAMED_PARAM, but passes a UserCounters& parameter to the\n * benchmark function for recording custom counters.\n */\n#define BENCHMARK_COUNTERS_NAMED_PARAM(name, param_name, ...)          \\\n  BENCHMARK_IMPL_COUNTERS(                                             \\\n      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),             \\\n      FOLLY_PP_STRINGIZE(name) \"(\" FOLLY_PP_STRINGIZE(param_name) \")\", \\\n      counters,                                                        \\\n      iters,                                                           \\\n      unsigned,                                                        \\\n      iters) {                                                         \\\n    name(counters, iters, ##__VA_ARGS__);                              \\\n  }\n\n/**\n * Same as BENCHMARK_NAMED_PARAM, but allows one to return the actual number\n * of iterations that have been run.\n */\n#define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...)             \\\n  BENCHMARK_MULTI_IMPL(                                                \\\n      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),             \\\n      FOLLY_PP_STRINGIZE(name) \"(\" FOLLY_PP_STRINGIZE(param_name) \")\", \\\n      unsigned,                                                        \\\n      iters) {                                                         \\\n    return name(iters, ##__VA_ARGS__);                                 \\\n  }\n\n/**\n * Just like BENCHMARK, but prints the time relative to a\n * baseline. The baseline is the most recent BENCHMARK() seen in\n * the current scope. Example:\n *\n * // This is the baseline\n * BENCHMARK(insertVectorBegin, n) {\n *   vector<int> v;\n *   for (unsigned int i = 0; i < n; ++i) {\n *     v.insert(v.begin(), 42);\n *   }\n * }\n *\n * BENCHMARK_RELATIVE(insertListBegin, n) {\n *   list<int> s;\n *   for (unsigned int i = 0; i < n; ++i) {\n *     s.insert(s.begin(), 42);\n *   }\n * }\n *\n * Any number of relative benchmark can be associated with a\n * baseline. Another BENCHMARK() occurrence effectively establishes a\n * new baseline.\n */\n#define BENCHMARK_RELATIVE(name, ...)          \\\n  BENCHMARK_IMPL(                              \\\n      name,                                    \\\n      \"%\" FOLLY_PP_STRINGIZE(name),            \\\n      FB_ARG_2_OR_1(1, ##__VA_ARGS__),         \\\n      FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \\\n      __VA_ARGS__)\n\n#define BENCHMARK_COUNTERS_RELATIVE(name, counters, ...) \\\n  BENCHMARK_IMPL_COUNTERS(                               \\\n      name,                                              \\\n      \"%\" FOLLY_PP_STRINGIZE(name),                      \\\n      counters,                                          \\\n      FB_ARG_2_OR_1(1, ##__VA_ARGS__),                   \\\n      FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__),           \\\n      __VA_ARGS__)\n/**\n * Same as BENCHMARK_RELATIVE, but allows one to return the actual number\n * of iterations that have been run.\n */\n#define BENCHMARK_RELATIVE_MULTI(name, ...)    \\\n  BENCHMARK_MULTI_IMPL(                        \\\n      name,                                    \\\n      \"%\" FOLLY_PP_STRINGIZE(name),            \\\n      FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \\\n      __VA_ARGS__)\n\n/**\n * A combination of BENCHMARK_RELATIVE and BENCHMARK_PARAM.\n */\n#define BENCHMARK_RELATIVE_PARAM(name, param) \\\n  BENCHMARK_RELATIVE_NAMED_PARAM(name, param, param)\n\n/**\n * Same as BENCHMARK_RELATIVE_PARAM, but allows one to return the actual\n * number of iterations that have been run.\n */\n#define BENCHMARK_RELATIVE_PARAM_MULTI(name, param) \\\n  BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param, param)\n\n/**\n * A combination of BENCHMARK_RELATIVE and BENCHMARK_NAMED_PARAM.\n */\n#define BENCHMARK_RELATIVE_NAMED_PARAM(name, param_name, ...)              \\\n  BENCHMARK_IMPL(                                                          \\\n      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),                 \\\n      \"%\" FOLLY_PP_STRINGIZE(name) \"(\" FOLLY_PP_STRINGIZE(param_name) \")\", \\\n      iters,                                                               \\\n      unsigned,                                                            \\\n      iters) {                                                             \\\n    name(iters, ##__VA_ARGS__);                                            \\\n  }\n\n/**\n * Same as BENCHMARK_RELATIVE_NAMED_PARAM, but allows one to return the\n * actual number of iterations that have been run.\n */\n#define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...)        \\\n  BENCHMARK_MULTI_IMPL(                                                    \\\n      FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),                 \\\n      \"%\" FOLLY_PP_STRINGIZE(name) \"(\" FOLLY_PP_STRINGIZE(param_name) \")\", \\\n      unsigned,                                                            \\\n      iters) {                                                             \\\n    return name(iters, ##__VA_ARGS__);                                     \\\n  }\n\n/**\n * Draws a line of dashes.\n */\n#define BENCHMARK_DRAW_LINE()                                                \\\n  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(                  \\\n      follyBenchmarkUnused) =                                                \\\n      (::folly::addBenchmark(__FILE__, \"-\", []() -> unsigned { return 0; }), \\\n       true)\n\n/**\n * Prints arbitrary text.\n */\n#define BENCHMARK_DRAW_TEXT(text)                                              \\\n  [[maybe_unused]] static const bool FB_ANONYMOUS_VARIABLE(                    \\\n      follyBenchmarkUnused) =                                                  \\\n      (::folly::addBenchmark(__FILE__, #text, []() -> unsigned { return 0; }), \\\n       true)\n\n/**\n * Allows execution of code that doesn't count toward the benchmark's\n * time budget. Example:\n *\n * BENCHMARK_START_GROUP(insertVectorBegin, n) {\n *   vector<int> v;\n *   BENCHMARK_SUSPEND {\n *     v.reserve(n);\n *   }\n *   for (unsigned int i = 0; i < n; ++i) {\n *     v.insert(v.begin(), 42);\n *   }\n * }\n */\n#define BENCHMARK_SUSPEND                             \\\n  if (auto FB_ANONYMOUS_VARIABLE(BENCHMARK_SUSPEND) = \\\n          ::folly::BenchmarkSuspender()) {            \\\n  } else\n"
  },
  {
    "path": "folly/BenchmarkUtil.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/BenchmarkUtil.h>\n\n#include <array> // @manual\n#include <filesystem>\n#include <fstream>\n#include <string>\n\n#include <fmt/format.h>\n\n#include <folly/lang/Align.h>\n#include <folly/portability/Sched.h>\n#include <folly/portability/Windows.h>\n#include <folly/system/arch/x86.h>\n\n#if defined(__APPLE__) || defined(__FreeBSD__)\n#include <sys/sysctl.h>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\nsize_t bm_llc_size_fallback() {\n#if defined(__linux__)\n\n  /// sysctl is deprecated on Linux; walk sysfs\n\n  int cpu = sched_getcpu();\n  if (cpu < 0) {\n    return 0;\n  }\n\n  auto dir = std::filesystem::path(\n      fmt::format(\"/sys/devices/system/cpu/cpu{}/cache\", cpu));\n\n  size_t size = 0;\n  for (const auto& entry : std::filesystem::directory_iterator(dir)) {\n    if (entry.path().filename().native().starts_with(\"index\")) {\n      std::ifstream size_ifs(entry.path() / \"size\");\n      std::string size_str;\n      size_ifs >> size_str;\n      size_t size_num = std::stoul(size_str);\n      if (size_str.back() == 'K') {\n        size_num *= 1024;\n      } else if (size_str.back() == 'M') {\n        size_num *= 1024 * 1024;\n      }\n      size = std::max(size, size_num);\n    }\n  }\n\n  return size;\n\n#elif defined(__APPLE__) || defined(__FreeBSD__)\n\n  auto names = std::array{\n      \"hw.l3cachesize\", // FreeBSD, Intel Mac\n      \"hw.l2cachesize\", // Apple Silicon\n  };\n  for (auto name : names) {\n    uint64_t size = 0;\n    size_t len = sizeof(size);\n    if (sysctlbyname(name, &size, &len, NULL, 0) == 0 && size > 0) {\n      return size;\n    }\n  }\n\n  return 0;\n\n#elif defined(_WIN32)\n\n  DWORD length = 0;\n  GetLogicalProcessorInformation(nullptr, &length);\n  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || length == 0) {\n    return 0;\n  }\n  if (length % sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) != 0) {\n    return 0;\n  }\n\n  size_t const count = length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);\n  std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> infos(count);\n  if (!GetLogicalProcessorInformation(infos.data(), &length)) {\n    return 0;\n  }\n\n  size_t size = 0;\n  for (auto const& info : infos) {\n    if (info.Relationship == RelationCache) {\n      size = std::max<size_t>(size, info.Cache.Size);\n    }\n  }\n  return size;\n\n#else\n\n  return 0;\n\n#endif\n}\n\n} // namespace detail\n\nsize_t bm_llc_size() {\n  return kIsArchX86 || kIsArchAmd64\n      ? folly::x86_cpuid_get_llc_cache_info().cache_size()\n      : detail::bm_llc_size_fallback();\n}\n\nvoid bm_llc_evict(size_t value) {\n  constexpr auto step =\n      folly::hardware_constructive_interference_size / sizeof(size_t);\n\n  constexpr auto words_per_line =\n      folly::hardware_constructive_interference_size / step;\n\n  static auto const llc_size = bm_llc_size();\n\n  auto const llc_lines =\n      llc_size / folly::hardware_constructive_interference_size;\n\n  static std::unique_ptr<size_t volatile[]> const vec{\n      new size_t[llc_lines * words_per_line]};\n\n  // do N passes over the region, each time touching one word per line\n  for (size_t j = 0; j < step; ++j) {\n    for (size_t i = 0; i < llc_lines; i += step) {\n      vec[i * words_per_line + j] = value;\n    }\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/BenchmarkUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/lang/Hint.h>\n\nnamespace folly {\n\n/**\n * Call doNotOptimizeAway(var) to ensure that var will be computed even\n * post-optimization.  Use it for variables that are computed during\n * benchmarking but otherwise are useless. The compiler tends to do a\n * good job at eliminating unused variables, and this function fools it\n * into thinking var is in fact needed.\n *\n * Call makeUnpredictable(var) when you don't want the optimizer to use\n * its knowledge of var to shape the following code.  This is useful\n * when constant propagation or power reduction is possible during your\n * benchmark but not in real use cases.\n */\n\ntemplate <class T>\nFOLLY_ALWAYS_INLINE void doNotOptimizeAway(const T& datum) {\n  compiler_must_not_elide(datum);\n}\n\ntemplate <typename T>\nFOLLY_ALWAYS_INLINE void makeUnpredictable(T& datum) {\n  compiler_must_not_predict(datum);\n}\n\nnamespace detail {\nsize_t bm_llc_size_fallback();\n}\n\n/// bm_llc_size\n///\n/// Calculates the size of the LLC (Last Level Cache, typically L3 on x86-64).\nsize_t bm_llc_size();\n\n/// bm_llc_evict\n///\n/// Write to a large block of memory and evict whatever cache lines might\n/// currently be resident in any levels of cache.\nvoid bm_llc_evict(size_t value);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Bits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/lang/Bits.h> // @shim\n"
  },
  {
    "path": "folly/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME atomic_hash_array\n  HEADERS\n    AtomicHashArray-inl.h\n    AtomicHashArray.h\n  EXPORTED_DEPS\n    folly_detail_atomic_hash_utils\n    folly_detail_iterators\n    folly_hash_hash\n    folly_lang_bits\n    folly_lang_exception\n    folly_thread_cached_int\n    folly_utility\n)\n\nfolly_add_library(\n  NAME atomic_hash_map\n  HEADERS\n    AtomicHashMap-inl.h\n    AtomicHashMap.h\n  EXPORTED_DEPS\n    folly_atomic_hash_array\n    folly_c_portability\n    folly_container_foreach\n    folly_detail_atomic_hash_utils\n    folly_detail_iterators\n    folly_hash_hash\n    folly_likely\n    folly_thread_cached_int\n)\n\nfolly_add_library(\n  NAME atomic_linked_list\n  HEADERS\n    AtomicIntrusiveLinkedList.h\n    AtomicLinkedList.h\n  EXPORTED_DEPS\n    folly_memory\n)\n\nfolly_add_library(\n  NAME atomic_unordered_map\n  HEADERS\n    AtomicUnorderedMap.h\n  EXPORTED_DEPS\n    folly_conv\n    folly_detail_atomic_unordered_map_utils\n    folly_lang_bits\n    folly_likely\n    folly_portability_sys_mman\n    folly_portability_unistd\n    folly_random\n    folly_scope_guard\n    folly_traits\n)\n\nfolly_add_library(\n  NAME base64\n  HEADERS\n    base64.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_detail_base64_detail_base64_api\n    folly_detail_base64_detail_base64_common\n    folly_lang_exception\n    folly_memory_uninitialized_memory_hacks\n    folly_portability\n)\n\nfolly_add_library(\n  NAME benchmark\n  TARGET_NAME follybenchmark\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    Benchmark.cpp\n    detail/BenchmarkAdaptive.cpp\n  HEADERS\n    Benchmark.h\n    detail/BenchmarkAdaptive.h\n  DEPS\n    folly_detail_perf_scoped\n    folly_file_util\n    folly_json_dynamic\n    folly_map_util\n    folly_overload\n    folly_random\n    folly_stats_streaming_stats\n    folly_string\n  EXPORTED_DEPS\n    folly_benchmark_util\n    folly_functional_invoke\n    folly_lang_hint\n    folly_portability\n    folly_portability_gflags\n    folly_preprocessor\n    folly_range\n    folly_scope_guard\n    folly_traits\n  EXTERNAL_DEPS\n    Boost::headers\n    Boost::regex\n)\n\nfolly_add_library(\n  NAME benchmark_util\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    BenchmarkUtil.cpp\n  HEADERS\n    BenchmarkUtil.h\n  DEPS\n    folly_lang_align\n    folly_portability_sched\n    folly_portability_windows\n    folly_system_arch_x86\n  EXPORTED_DEPS\n    folly_lang_hint\n    folly_portability\n)\n\nfolly_add_library(\n  NAME bits\n  HEADERS\n    Bits.h\n  EXPORTED_DEPS\n    folly_lang_bits\n)\n\nfolly_add_library(\n  NAME c_portability\n  HEADERS\n    CPortability.h\n  EXPORTED_DEPS\n    folly_portability_config\n)\n\nfolly_add_library(\n  NAME cancellation_token\n  SRCS\n    CancellationToken.cpp\n  HEADERS\n    CancellationToken-inl.h\n    CancellationToken.h\n  DEPS\n    folly_lang_new\n    folly_optional\n    folly_portability_memory\n    folly_scope_guard\n    folly_synchronization_detail_sleeper\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_function\n    folly_operation_cancelled\n)\n\nfolly_add_library(\n  NAME chrono\n  HEADERS\n    Chrono.h\n  EXPORTED_DEPS\n    folly_lang_exception\n    folly_portability\n    folly_portability_time\n)\n\nfolly_add_library(\n  NAME clock_gettime_wrappers\n  SRCS\n    ClockGettimeWrappers.cpp\n  HEADERS\n    ClockGettimeWrappers.h\n  DEPS\n    folly_likely\n  EXPORTED_DEPS\n    folly_portability_time\n)\n\nfolly_add_library(\n  NAME concurrent_bit_set\n  HEADERS\n    ConcurrentBitSet.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME concurrent_lazy\n  HEADERS\n    ConcurrentLazy.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_synchronization_delayed_init\n)\n\nfolly_add_library(\n  NAME concurrent_skip_list\n  HEADERS\n    ConcurrentSkipList-inl.h\n    ConcurrentSkipList.h\n  EXPORTED_DEPS\n    folly_constexpr_math\n    folly_detail_iterators\n    folly_likely\n    folly_memory\n    folly_synchronization_micro_spin_lock\n    folly_thread_local\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME config\n  HEADERS\n    folly-config.h\n)\n\nfolly_add_library(\n  NAME constexpr_math\n  HEADERS\n    ConstexprMath.h\n  EXPORTED_DEPS\n    folly_lang_checked_math\n    folly_portability\n    folly_portability_constexpr\n)\n\nfolly_add_library(\n  NAME constructor_callback_list\n  HEADERS\n    ConstructorCallbackList.h\n  EXPORTED_DEPS\n    folly_detail_static_singleton_manager\n    folly_format\n    folly_function\n    folly_shared_mutex\n)\n\nfolly_add_library(\n  NAME conv\n  SRCS\n    Conv.cpp\n  HEADERS\n    Conv.h\n  DEPS\n    folly_lang_safe_assert\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_demangle\n    folly_expected\n    folly_fbstring\n    folly_lang_exception\n    folly_lang_pretty\n    folly_lang_to_ascii\n    folly_likely\n    folly_portability\n    folly_portability_math\n    folly_range\n    folly_traits\n    folly_unit\n    folly_utility\n)\n\nfolly_add_library(\n  NAME cpp_attributes\n  HEADERS\n    CppAttributes.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME cpu_id\n  HEADERS\n    CpuId.h\n  EXPORTED_DEPS\n    folly_portability\n    folly_system_arch_x86\n)\n\nfolly_add_library(\n  NAME default_keep_alive_executor\n  HEADERS\n    DefaultKeepAliveExecutor.h\n  EXPORTED_DEPS\n    folly_executor\n    folly_executors_sequenced_executor\n    folly_synchronization_baton\n)\n\nfolly_add_library(\n  NAME demangle\n  SRCS\n    Demangle.cpp\n  HEADERS\n    Demangle.h\n  DEPS\n    folly_c_portability\n    folly_functional_invoke\n    folly_lang_c_string\n    folly_utility\n  EXPORTED_DEPS\n    folly_fbstring\n    folly_portability_config\n)\n\nfolly_add_library(\n  NAME discriminated_ptr\n  HEADERS\n    DiscriminatedPtr.h\n  EXPORTED_DEPS\n    folly_detail_discriminated_ptr_detail\n    folly_likely\n    folly_portability\n)\n\nfolly_add_library(\n  NAME dynamic\n  HEADERS\n    DynamicConverter.h\n    dynamic-inl.h\n    dynamic.h\n    json.h\n  EXPORTED_DEPS\n    folly_json_dynamic\n)\n\nfolly_add_library(\n  NAME exception\n  HEADERS\n    Exception.h\n  EXPORTED_DEPS\n    folly_conv\n    folly_fbstring\n    folly_likely\n    folly_portability\n    folly_portability_sys_types\n)\n\nfolly_add_library(\n  NAME exception_string\n  SRCS\n    ExceptionString.cpp\n  HEADERS\n    ExceptionString.h\n  DEPS\n    folly_demangle\n    folly_lang_exception\n    folly_lang_type_info\n  EXPORTED_DEPS\n    folly_fbstring\n)\n\nfolly_add_library(\n  NAME exception_wrapper\n  SRCS\n    ExceptionWrapper.cpp\n  HEADERS\n    ExceptionWrapper-inl.h\n    ExceptionWrapper.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_cpp_attributes\n    folly_demangle\n    folly_exception_string\n    folly_fbstring\n    folly_functional_traits\n    folly_lang_assume\n    folly_lang_exception\n    folly_portability\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME executor\n  SRCS\n    Executor.cpp\n  HEADERS\n    Executor.h\n  DEPS\n    folly_exception_string\n    folly_portability\n  EXPORTED_DEPS\n    folly_function\n    folly_lang_exception\n    folly_optional\n    folly_range\n    folly_utility\n)\n\nfolly_add_library(\n  NAME expected\n  HEADERS\n    Expected.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_coro_coroutine\n    folly_cpp_attributes\n    folly_lang_exception\n    folly_lang_hint\n    folly_likely\n    folly_portability\n    folly_preprocessor\n    folly_traits\n    folly_unit\n    folly_utility\n)\n\nfolly_add_library(\n  NAME fbstring\n  HEADERS\n    FBString.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_cpp_attributes\n    folly_hash_hash\n    folly_lang_assume\n    folly_lang_checked_math\n    folly_lang_exception\n    folly_likely\n    folly_memory_malloc\n    folly_portability\n    folly_traits\n)\n\nfolly_add_library(\n  NAME fbvector\n  HEADERS\n    FBVector.h\n  EXPORTED_DEPS\n    folly_container_fbvector\n)\n\nfolly_add_library(\n  NAME file\n  SRCS\n    File.cpp\n  HEADERS\n    File.h\n  DEPS\n    folly_exception\n    folly_file_util\n    folly_portability_fmt_compile\n    folly_portability_sys_file\n    folly_scope_guard\n  EXPORTED_DEPS\n    folly_exception_wrapper\n    folly_expected\n    folly_file_util\n    folly_portability\n    folly_portability_fcntl\n    folly_portability_unistd\n    folly_range\n)\n\nfolly_add_library(\n  NAME file_util\n  SRCS\n    FileUtil.cpp\n  HEADERS\n    FileUtil.h\n  DEPS\n    folly_detail_file_util_detail\n    folly_detail_file_util_vector_detail\n    folly_net_net_ops\n    folly_portability_sockets\n    folly_portability_stdlib\n    folly_portability_sys_file\n    folly_portability_sys_stat\n  EXPORTED_DEPS\n    folly_net_network_socket\n    folly_portability\n    folly_portability_fcntl\n    folly_portability_sys_uio\n    folly_portability_unistd\n    folly_range\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME fingerprint\n  SRCS\n    Fingerprint.cpp\n  HEADERS\n    Fingerprint.h\n  DEPS\n    folly_detail_fingerprint_polynomial\n    folly_portability\n  EXPORTED_DEPS\n    folly_range\n)\n\nfolly_add_library(\n  NAME fixed_string\n  HEADERS\n    FixedString.h\n  EXPORTED_DEPS\n    folly_constexpr_math\n    folly_lang_exception\n    folly_lang_ordering\n    folly_portability\n    folly_portability_constexpr\n    folly_range\n    folly_utility\n)\n\nfolly_add_library(\n  NAME fmt_utility\n  SRCS\n    FmtUtility.cpp\n  HEADERS\n    FmtUtility.h\n  DEPS\n    folly_range\n    folly_ssl_openssl_hash\n    folly_string\n  EXPORTED_DEPS\n    folly_cpp_attributes\n)\n\nfolly_add_library(\n  NAME format\n  SRCS\n    Format.cpp\n  HEADERS\n    Format-inl.h\n    Format.h\n    FormatArg.h\n  DEPS\n    folly_constexpr_math\n    folly_container_array\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_conv\n    folly_exception\n    folly_format_traits\n    folly_lang_exception\n    folly_lang_to_ascii\n    folly_likely\n    folly_map_util\n    folly_portability\n    folly_portability_windows\n    folly_range\n    folly_string\n    folly_traits\n)\n\nfolly_add_library(\n  NAME format_traits\n  HEADERS\n    FormatTraits.h\n)\n\nfolly_add_library(\n  NAME function\n  HEADERS\n    Function.h\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_functional_invoke\n    folly_lang_align\n    folly_lang_exception\n    folly_lang_new\n    folly_portability\n    folly_traits\n)\n\nfolly_add_library(\n  NAME glog\n  HEADERS\n    GLog.h\n  EXPORTED_DEPS\n    folly_likely\n)\n\nfolly_add_library(\n  NAME group_varint\n  SRCS\n    GroupVarint.cpp\n  HEADERS\n    GroupVarint.h\n  DEPS\n    folly_container_array\n  EXPORTED_DEPS\n    folly_detail_group_varint_detail\n    folly_lang_bits\n    folly_portability\n    folly_portability_builtins\n    folly_range\n)\n\nfolly_add_library(\n  NAME hash\n  HEADERS\n    Hash.h\n  EXPORTED_DEPS\n    folly_hash_hash\n)\n\nfolly_add_library(\n  NAME indestructible\n  HEADERS\n    Indestructible.h\n  EXPORTED_DEPS\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME indexed_mem_pool\n  HEADERS\n    IndexedMemPool.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_portability\n    folly_portability_sys_mman\n    folly_portability_unistd\n    folly_synchronization_atomic_struct\n)\n\nfolly_add_library(\n  NAME intrusive_list\n  HEADERS\n    IntrusiveList.h\n  EXPORTED_DEPS\n    folly_container_intrusive_list\n)\n\nfolly_add_library(\n  NAME json\n  EXPORTED_DEPS\n    folly_dynamic\n)\n\nfolly_add_library(\n  NAME json_patch\n  HEADERS\n    json_patch.h\n  EXPORTED_DEPS\n    folly_json_json_patch\n)\n\nfolly_add_library(\n  NAME json_pointer\n  HEADERS\n    json_pointer.h\n  EXPORTED_DEPS\n    folly_json_json_pointer\n)\n\nfolly_add_library(\n  NAME lazy\n  HEADERS\n    Lazy.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_optional\n)\n\nfolly_add_library(\n  NAME likely\n  HEADERS\n    Likely.h\n  EXPORTED_DEPS\n    folly_lang_builtin\n)\n\nfolly_add_library(\n  NAME map_util\n  HEADERS\n    MapUtil.h\n  EXPORTED_DEPS\n    folly_container_map_util\n)\n\nfolly_add_library(\n  NAME math\n  HEADERS\n    Math.h\n)\n\nfolly_add_library(\n  NAME maybe_managed_ptr\n  HEADERS\n    MaybeManagedPtr.h\n)\n\nfolly_add_library(\n  NAME memcpy\n  HEADERS\n    FollyMemcpy.h\n  EXPORTED_DEPS\n    folly_memcpy-impl\n)\n\nfolly_add_library(\n  NAME memcpy-impl\n  SRCS\n    FollyMemcpy.cpp\n)\n\nfolly_add_library(\n  NAME memcpy-use\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    FollyMemcpy.cpp\n)\n\n# x86 assembly memcpy implementation (not supported on MSVC)\nif (IS_X86_64_ARCH AND NOT MSVC)\n  folly_add_library(\n    NAME memcpy_x86\n    SRCS\n      memcpy.S\n  )\n  set_property(\n    TARGET folly_memcpy_x86_obj\n    APPEND PROPERTY COMPILE_OPTIONS \"-x\" \"assembler-with-cpp\"\n  )\nendif()\n\nfolly_add_library(\n  NAME memory\n  HEADERS\n    Memory.h\n  EXPORTED_DEPS\n    folly_constexpr_math\n    folly_functional_invoke\n    folly_lang_align\n    folly_lang_exception\n    folly_lang_thunk\n    folly_likely\n    folly_memory_malloc\n    folly_portability\n    folly_portability_config\n    folly_portability_constexpr\n    folly_portability_malloc\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME memset\n  HEADERS\n    FollyMemset.h\n  EXPORTED_DEPS\n    folly_memset-impl\n)\n\nfolly_add_library(\n  NAME memset-impl\n  SRCS\n    FollyMemset.cpp\n)\n\nfolly_add_library(\n  NAME memset-use\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    FollyMemset.cpp\n)\n\nfolly_add_library(\n  NAME micro_lock\n  SRCS\n    MicroLock.cpp\n  HEADERS\n    MicroLock.h\n  DEPS\n    folly_portability_asm\n  EXPORTED_DEPS\n    folly_optional\n    folly_portability\n    folly_synchronization_atomic_notification\n    folly_synchronization_atomic_ref\n    folly_utility\n)\n\nfolly_add_library(\n  NAME micro_spin_lock\n  HEADERS\n    MicroSpinLock.h\n  EXPORTED_DEPS\n    folly_synchronization_micro_spin_lock\n)\n\nfolly_add_library(\n  NAME move_wrapper\n  HEADERS\n    MoveWrapper.h\n)\n\nfolly_add_library(\n  NAME mpmc_pipeline\n  HEADERS\n    MPMCPipeline.h\n  EXPORTED_DEPS\n    folly_detail_mpmc_pipeline_detail\n    folly_portability\n)\n\nfolly_add_library(\n  NAME mpmc_queue\n  HEADERS\n    MPMCQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_detail_turn_sequencer\n    folly_lang_exception\n    folly_portability_unistd\n    folly_traits\n)\n\nfolly_add_library(\n  NAME network_address\n  SRCS\n    IPAddress.cpp\n    IPAddressV4.cpp\n    IPAddressV6.cpp\n    MacAddress.cpp\n    SocketAddress.cpp\n  HEADERS\n    IPAddress.h\n    IPAddressException.h\n    IPAddressV4.h\n    IPAddressV6.h\n    MacAddress.h\n    SocketAddress.h\n  DEPS\n    folly_detail_ip_address_source\n    folly_exception\n    folly_format\n    folly_net_net_ops\n    folly_scope_guard\n    folly_small_vector\n    folly_string\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_constexpr_math\n    folly_conv\n    folly_detail_ip_address\n    folly_expected\n    folly_fbstring\n    folly_hash_hash\n    folly_lang_bits\n    folly_lang_exception\n    folly_net_network_socket\n    folly_optional\n    folly_portability\n    folly_portability_config\n    folly_portability_sockets\n    folly_range\n    folly_unit\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME observer_container\n  HEADERS\n    ObserverContainer.h\n  EXPORTED_DEPS\n    folly_constructor_callback_list\n    folly_function\n    folly_io_async_destructor_check\n    folly_lang_switch\n    folly_optional\n    folly_scope_guard\n    folly_small_vector\n)\n\nfolly_add_library(\n  NAME operation_cancelled\n  HEADERS\n    OperationCancelled.h\n)\n\nfolly_add_library(\n  NAME optional\n  HEADERS\n    Optional.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_hash_traits\n    folly_lang_exception\n    folly_portability\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME overload\n  HEADERS\n    Overload.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_portability\n    folly_traits\n)\n\nfolly_add_library(\n  NAME packed_sync_ptr\n  HEADERS\n    PackedSyncPtr.h\n  EXPORTED_DEPS\n    folly_portability\n    folly_synchronization_small_locks\n)\n\nfolly_add_library(\n  NAME padded\n  HEADERS\n    Padded.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_portability\n    folly_portability_sys_types\n    folly_traits\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME poly\n  HEADERS\n    Poly-inl.h\n    Poly.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_cpp_attributes\n    folly_detail_poly_detail\n    folly_detail_typelist\n    folly_lang_assume\n    folly_poly_exception\n    folly_traits\n)\n\nfolly_add_library(\n  NAME poly_exception\n  HEADERS\n    PolyException.h\n  EXPORTED_DEPS\n    folly_c_portability\n)\n\nfolly_add_library(\n  NAME portability\n  HEADERS\n    Portability.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_portability_config\n)\n\nfolly_add_library(\n  NAME preprocessor\n  HEADERS\n    Preprocessor.h\n  EXPORTED_DEPS\n    folly_c_portability\n)\n\nfolly_add_library(\n  NAME producer_consumer_queue\n  HEADERS\n    ProducerConsumerQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n)\n\nfolly_add_library(\n  NAME random\n  SRCS\n    Random.cpp\n  HEADERS\n    Random-inl.h\n    Random.h\n  DEPS\n    folly_cpp_attributes\n    folly_detail_file_util_detail\n    folly_portability_config\n    folly_portability_sys_time\n    folly_portability_unistd\n    folly_singleton_thread_local\n    folly_synchronization_relaxed_atomic\n    folly_thread_local\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_lang_bits\n    folly_portability\n    folly_random_xoshiro256pp\n    folly_traits\n)\n\nfolly_add_library(\n  NAME range\n  HEADERS\n    Range.h\n  EXPORTED_DEPS\n    folly_cpu_id\n    folly_detail_range_common\n    folly_detail_range_simd\n    folly_hash_spooky_hash_v2\n    folly_lang_c_string\n    folly_lang_exception\n    folly_likely\n    folly_portability\n    folly_portability_constexpr\n    folly_traits\n)\n\nfolly_add_library(\n  NAME replaceable\n  HEADERS\n    Replaceable.h\n  EXPORTED_DEPS\n    folly_portability\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME rw_spin_lock\n  HEADERS\n    RWSpinLock.h\n  EXPORTED_DEPS\n    folly_synchronization_rw_spin_lock\n)\n\nfolly_add_library(\n  NAME scope_guard\n  SRCS\n    ScopeGuard.cpp\n  HEADERS\n    ScopeGuard.h\n  EXPORTED_DEPS\n    folly_lang_exception\n    folly_lang_uncaught_exceptions\n    folly_portability\n    folly_preprocessor\n    folly_utility\n)\n\nfolly_add_library(\n  NAME shared_mutex\n  SRCS\n    SharedMutex.cpp\n  HEADERS\n    SharedMutex.h\n  DEPS\n    folly_indestructible\n    folly_lang_exception\n    folly_portability_sys_resource\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_chrono_hardware\n    folly_concurrency_cache_locality\n    folly_cpp_attributes\n    folly_detail_futex\n    folly_likely\n    folly_portability_asm\n    folly_synchronization_lock\n    folly_synchronization_relaxed_atomic\n    folly_synchronization_sanitize_thread\n    folly_system_thread_id\n)\n\nfolly_add_library(\n  NAME singleton\n  SRCS\n    Singleton.cpp\n  HEADERS\n    Singleton-inl.h\n    Singleton.h\n  DEPS\n    folly_demangle\n    folly_experimental_symbolizer_symbolizer\n    folly_lang_safe_assert\n    folly_portability_config\n    folly_portability_fmt_compile\n    folly_scope_guard\n    folly_system_at_fork\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_concurrency_core_cached_shared_ptr\n    folly_concurrency_memory_read_mostly_shared_ptr\n    folly_detail_singleton\n    folly_detail_static_singleton_manager\n    folly_exception\n    folly_executor\n    folly_hash_hash\n    folly_lang_exception\n    folly_memory\n    folly_memory_sanitize_leak\n    folly_synchronization_baton\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME singleton_thread_local\n  SRCS\n    SingletonThreadLocal.cpp\n  HEADERS\n    SingletonThreadLocal.h\n  EXPORTED_DEPS\n    folly_detail_iterators\n    folly_detail_singleton\n    folly_detail_unique_instance\n    folly_functional_invoke\n    folly_lang_hint\n    folly_scope_guard\n    folly_thread_local\n)\n\nfolly_add_library(\n  NAME small_vector\n  HEADERS\n    small_vector.h\n  EXPORTED_DEPS\n    folly_container_small_vector\n)\n\nfolly_add_library(\n  NAME sorted_vector_types\n  HEADERS\n    sorted_vector_types.h\n  EXPORTED_DEPS\n    folly_container_sorted_vector_types\n)\n\nfolly_add_library(\n  NAME spin_lock\n  HEADERS\n    SpinLock.h\n  EXPORTED_DEPS\n    folly_portability\n    folly_synchronization_small_locks\n)\n\nfolly_add_library(\n  NAME stop_watch\n  HEADERS\n    stop_watch.h\n  EXPORTED_DEPS\n    folly_chrono\n    folly_portability_time\n    folly_utility\n)\n\nfolly_add_library(\n  NAME string\n  SRCS\n    String.cpp\n  HEADERS\n    String-inl.h\n    String.h\n  DEPS\n    folly_container_array\n  EXPORTED_DEPS\n    folly_container_reserve\n    folly_conv\n    folly_cpp_attributes\n    folly_detail_simple_simd_string_utils\n    folly_detail_split_string_simd\n    folly_exception_string\n    folly_optional\n    folly_portability\n    folly_range\n    folly_scope_guard\n    folly_traits\n    folly_unit\n)\n\n# subprocess is not supported on Windows\nif(NOT WIN32)\n  folly_add_library(\n    NAME subprocess\n    SRCS\n      Subprocess.cpp\n    HEADERS\n      Subprocess.h\n    DEPS\n      folly_conv\n      folly_lang_assume\n      folly_logging_logging\n      folly_portability_dirent\n      folly_portability_fcntl\n      folly_portability_sockets\n      folly_portability_stdlib\n      folly_portability_sys_syscall\n      folly_portability_unistd\n      folly_scope_guard\n      folly_string\n      folly_system_at_fork\n      folly_system_shell\n    EXPORTED_DEPS\n      folly_container_span\n      folly_exception\n      folly_file\n      folly_file_util\n      folly_function\n      folly_gen_string\n      folly_io_iobuf\n      folly_map_util\n      folly_optional\n      folly_portability\n      folly_portability_sys_resource\n      folly_range\n    EXTERNAL_DEPS\n      Boost::headers\n  )\nendif()\n\nfolly_add_library(\n  NAME synchronized\n  HEADERS\n    Synchronized.h\n  EXPORTED_DEPS\n    folly_container_foreach\n    folly_function\n    folly_functional_apply_tuple\n    folly_likely\n    folly_preprocessor\n    folly_shared_mutex\n    folly_synchronization_lock\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME synchronized_ptr\n  HEADERS\n    SynchronizedPtr.h\n  EXPORTED_DEPS\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME thread_cached_int\n  HEADERS\n    ThreadCachedInt.h\n  EXPORTED_DEPS\n    folly_likely\n    folly_thread_local\n)\n\nfolly_add_library(\n  NAME thread_local\n  HEADERS\n    ThreadLocal.h\n  EXPORTED_DEPS\n    folly_detail_thread_local_detail\n    folly_likely\n    folly_portability\n    folly_scope_guard\n    folly_shared_mutex\n)\n\nfolly_add_library(\n  NAME timeout_queue\n  SRCS\n    TimeoutQueue.cpp\n  HEADERS\n    TimeoutQueue.h\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME token_bucket\n  HEADERS\n    TokenBucket.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_constexpr_math\n    folly_likely\n    folly_optional\n)\n\nfolly_add_library(\n  NAME traits\n  HEADERS\n    Traits.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME try\n  SRCS\n    Try.cpp\n  HEADERS\n    Try-inl.h\n    Try.h\n  EXPORTED_DEPS\n    folly_exception_wrapper\n    folly_functional_invoke\n    folly_lang_exception\n    folly_likely\n    folly_memory\n    folly_portability\n    folly_unit\n    folly_utility\n)\n\nfolly_add_library(\n  NAME unicode\n  SRCS\n    Unicode.cpp\n  HEADERS\n    Unicode.h\n  DEPS\n    folly_conv\n  EXPORTED_DEPS\n    folly_lang_exception\n)\n\nfolly_add_library(\n  NAME unit\n  HEADERS\n    Unit.h\n)\n\nfolly_add_library(\n  NAME uri\n  SRCS\n    Uri.cpp\n  HEADERS\n    Uri-inl.h\n    Uri.h\n  EXPORTED_DEPS\n    folly_conv\n    folly_expected\n    folly_hash_hash\n    folly_string\n  EXTERNAL_DEPS\n    Boost::regex\n)\n\nfolly_add_library(\n  NAME utf8_string\n  HEADERS\n    UTF8String.h\n  EXPORTED_DEPS\n    folly_range\n  EXTERNAL_DEPS\n    Boost::regex\n)\n\nfolly_add_library(\n  NAME utility\n  HEADERS\n    Utility.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_portability\n    folly_traits\n)\n\nfolly_add_library(\n  NAME varint\n  HEADERS\n    Varint.h\n  EXPORTED_DEPS\n    folly_conv\n    folly_expected\n    folly_likely\n    folly_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME virtual_executor\n  HEADERS\n    VirtualExecutor.h\n  EXPORTED_DEPS\n    folly_executors_virtual_executor\n)\n\nadd_subdirectory(algorithm)\nadd_subdirectory(channels)\nadd_subdirectory(chrono)\nadd_subdirectory(cli)\nadd_subdirectory(codec)\nadd_subdirectory(compression)\nadd_subdirectory(concurrency)\nadd_subdirectory(container)\nadd_subdirectory(coro)\nadd_subdirectory(crypto)\nadd_subdirectory(debugging)\nadd_subdirectory(detail)\nadd_subdirectory(executors)\nadd_subdirectory(experimental)\nadd_subdirectory(ext)\nadd_subdirectory(external)\nadd_subdirectory(fibers)\nadd_subdirectory(functional)\nadd_subdirectory(futures)\nadd_subdirectory(gen)\nadd_subdirectory(hash)\nadd_subdirectory(init)\nadd_subdirectory(io)\nadd_subdirectory(json)\nadd_subdirectory(lang)\nadd_subdirectory(logging)\nadd_subdirectory(memory)\nadd_subdirectory(net)\nadd_subdirectory(observer)\nadd_subdirectory(poly)\nadd_subdirectory(portability)\nif (PYTHON_EXTENSIONS)\n  add_subdirectory(python)\nendif()\nadd_subdirectory(random)\nadd_subdirectory(result)\nadd_subdirectory(settings)\nadd_subdirectory(ssl)\nadd_subdirectory(stats)\nadd_subdirectory(synchronization)\nadd_subdirectory(system)\nadd_subdirectory(testing)\nadd_subdirectory(tracing)\n\n# Resolve deferred dependencies now that all targets exist\nfolly_resolve_deferred_dependencies()\n"
  },
  {
    "path": "folly/CODING_GUIDELINES.md",
    "content": "# Folly Coding Guidelines\n\n\n## The Spirit of Folly\n\nFolly is a collection of core libraries, which engineers depend on.\nTherefore, the primary requirements when writing Folly code are:\n\n1. Create reusable components. Do one job, and do it well.\n2. Be complete. Missing APIs create friction for users.\n3. Have strong unit tests. Uphold Folly's reputation of reliability.\n4. Have documentation. Not everyone's an expert.\n\n\n## The realities of Folly\n\nAs an open-source library, there are a few necessary requirements:\n\n1. Define symbols in namespace `folly`. Prefix macros with `FOLLY_`, and C symbols with `folly_`.\n2. Put library-private symbols in subnamespace `detail` (e.g. `folly::detail::XYZ`)\n3. Restrict dependencies to (a) the standard library, and (b) other Folly libraries.\n   Some exceptions apply, such as select Boost components.\n4. Be portable. Notably:\n   - Use `throw_exception` from `folly/lang/Exception.h` instead of `throw`\n\n\nFolly only reimplements existing standard or boost libraries when there is good reason to.\nWhen improving upon an already-existing library:\n\n1. Strive to be a drop-in replacement:\n   - Have the same APIs\n   - Have the same guarantees\n   - Have the same naming convention\n\n\n## Coding style\n\nFolly follows Meta's C++ Coding conventions. When changing a file that has non-standard\nstyle, consistency takes priority.\n\nSome Folly files, based on standard classes, use naming conventions from the C++ standard:\n`snake_case` instead of `camelCase`. If you are faced with this situation, use your judgement to\ndecide which casing to use.\n\n\n## Documentation style\n\nFollow the [Google developer documentation style guide](https://developers.google.com/style). Highlights:\n\n- Use standard American spelling and punctuation.\n- Use second person: \"you\" rather than \"we.\"\n- Use active voice: make clear who's performing the action.\n- Be conversational and friendly.\n"
  },
  {
    "path": "folly/CPortability.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n/* These definitions are in a separate file so that they\n * may be included from C- as well as C++-based projects. */\n\n#include <folly/portability/Config.h>\n\n/**\n * Portable version check.\n */\n#ifndef __GNUC_PREREQ\n#if defined __GNUC__ && defined __GNUC_MINOR__\n/* nolint */\n#define __GNUC_PREREQ(maj, min) \\\n  ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))\n#else\n/* nolint */\n#define __GNUC_PREREQ(maj, min) 0\n#endif\n#endif\n\n// portable version check for clang\n#ifndef __CLANG_PREREQ\n#if defined __clang__ && defined __clang_major__ && defined __clang_minor__\n/* nolint */\n#define __CLANG_PREREQ(maj, min) \\\n  ((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min))\n#else\n/* nolint */\n#define __CLANG_PREREQ(maj, min) 0\n#endif\n#endif\n\n/// FOLLY_GLIBC_PREREQ\n#if !defined(__GLIBC__)\n#define FOLLY_GLIBC_PREREQ(maj, min) 0\n#else\n#define FOLLY_GLIBC_PREREQ(maj, min) \\\n  (__GLIBC__ > (maj)) || (__GLIBC__ == (maj) && __GLIBC_MINOR__ >= (min))\n#endif\n\n#if defined(__has_builtin)\n#define FOLLY_HAS_BUILTIN(...) __has_builtin(__VA_ARGS__)\n#else\n#define FOLLY_HAS_BUILTIN(...) 0\n#endif\n\n#if defined(__has_feature)\n#define FOLLY_HAS_FEATURE(...) __has_feature(__VA_ARGS__)\n#else\n#define FOLLY_HAS_FEATURE(...) 0\n#endif\n\n#if defined(__has_warning)\n#define FOLLY_HAS_WARNING(...) __has_warning(__VA_ARGS__)\n#else\n#define FOLLY_HAS_WARNING(...) 0\n#endif\n\n/* FOLLY_SANITIZE_ADDRESS is defined to 1 if the current compilation unit\n * is being compiled with ASAN or HWASAN enabled.\n *\n * Beware when using this macro in a header file: this macro may change values\n * across compilation units if some libraries are built with ASAN/HWASAN enabled\n * and some built with ASAN/HWSAN disabled. For instance, this may occur, if\n * folly itself was compiled without ASAN/HWSAN but a downstream project that\n * uses folly is compiling with ASAN/HWSAN enabled.\n *\n * Use FOLLY_LIBRARY_SANITIZE_ADDRESS (defined in folly-config.h) to check if\n * folly itself was compiled with ASAN enabled.\n */\n#ifndef FOLLY_SANITIZE_ADDRESS\n#if FOLLY_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__) || \\\n    FOLLY_HAS_FEATURE(hwaddress_sanitizer)\n#define FOLLY_SANITIZE_ADDRESS 1\n#endif\n#endif\n\n/* Define attribute wrapper for function attribute used to disable\n * address sanitizer instrumentation. Unfortunately, this attribute\n * has issues when inlining is used, so disable that as well. */\n#ifdef FOLLY_SANITIZE_ADDRESS\n#if defined(__clang__)\n#if __has_attribute(__no_sanitize__)\n#define FOLLY_DISABLE_ADDRESS_SANITIZER                     \\\n  __attribute__((__no_sanitize__(\"address\"), __noinline__)) \\\n  __attribute__((__no_sanitize__(\"hwaddress\"), __noinline__))\n#elif __has_attribute(__no_address_safety_analysis__)\n#define FOLLY_DISABLE_ADDRESS_SANITIZER \\\n  __attribute__((__no_address_safety_analysis__, __noinline__))\n#elif __has_attribute(__no_sanitize_address__)\n#define FOLLY_DISABLE_ADDRESS_SANITIZER \\\n  __attribute__((__no_sanitize_address__, __noinline__))\n#endif\n#elif defined(__GNUC__)\n#define FOLLY_DISABLE_ADDRESS_SANITIZER \\\n  __attribute__((__no_address_safety_analysis__, __noinline__))\n#elif defined(_MSC_VER)\n#define FOLLY_DISABLE_ADDRESS_SANITIZER __declspec(no_sanitize_address)\n#endif\n#endif\n#ifndef FOLLY_DISABLE_ADDRESS_SANITIZER\n#define FOLLY_DISABLE_ADDRESS_SANITIZER\n#endif\n\n/* Define a convenience macro to test when thread sanitizer is being used\n * across the different compilers (e.g. clang, gcc) */\n#ifndef FOLLY_SANITIZE_THREAD\n#if FOLLY_HAS_FEATURE(thread_sanitizer) || defined(__SANITIZE_THREAD__)\n#define FOLLY_SANITIZE_THREAD 1\n#endif\n#endif\n\n#ifdef FOLLY_SANITIZE_THREAD\n#define FOLLY_DISABLE_THREAD_SANITIZER \\\n  __attribute__((no_sanitize_thread, noinline))\n#else\n#define FOLLY_DISABLE_THREAD_SANITIZER\n#endif\n\n/**\n * Define a convenience macro to test when memory sanitizer is being used\n * across the different compilers (e.g. clang, gcc)\n */\n#ifndef FOLLY_SANITIZE_MEMORY\n#if FOLLY_HAS_FEATURE(memory_sanitizer) || defined(__SANITIZE_MEMORY__)\n#define FOLLY_SANITIZE_MEMORY 1\n#endif\n#endif\n\n#ifdef FOLLY_SANITIZE_MEMORY\n#define FOLLY_DISABLE_MEMORY_SANITIZER \\\n  __attribute__((no_sanitize_memory, noinline))\n#else\n#define FOLLY_DISABLE_MEMORY_SANITIZER\n#endif\n\n/**\n * Define a convenience macro to test when dataflow sanitizer is being used\n * across the different compilers (e.g. clang, gcc)\n */\n#ifndef FOLLY_SANITIZE_DATAFLOW\n#if FOLLY_HAS_FEATURE(dataflow_sanitizer) || defined(__SANITIZE_DATAFLOW__)\n#define FOLLY_SANITIZE_DATAFLOW 1\n#endif\n#endif\n\n#ifdef FOLLY_SANITIZE_DATAFLOW\n#define FOLLY_DISABLE_DATAFLOW_SANITIZER \\\n  __attribute__((no_sanitize_dataflow, noinline))\n#else\n#define FOLLY_DISABLE_DATAFLOW_SANITIZER\n#endif\n\n/**\n * Define a convenience macro to test when undefined-behavior sanitizer is being\n * used across the different compilers (e.g. clang, gcc)\n */\n#ifndef FOLLY_SANITIZE_UNDEFINED_BEHAVIOR\n#if FOLLY_HAS_FEATURE(undefined_behavior_sanitizer) || \\\n    defined(__SANITIZER_UNDEFINED__)\n#define FOLLY_SANITIZE_UNDEFINED_BEHAVIOR(...) 1\n#endif\n#endif\n\n#ifdef FOLLY_SANITIZE_UNDEFINED_BEHAVIOR\n#define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...) \\\n  __attribute__((no_sanitize(__VA_ARGS__)))\n#else\n#define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...)\n#endif\n\n/**\n * Define a convenience macro to test when ASAN, UBSAN, TSAN or MSAN sanitizer\n * are being used\n */\n#ifndef FOLLY_SANITIZE\n#if defined(FOLLY_SANITIZE_ADDRESS) || defined(FOLLY_SANITIZE_THREAD) ||  \\\n    defined(FOLLY_SANITIZE_MEMORY) || defined(FOLLY_SANITIZE_DATAFLOW) || \\\n    defined(FOLLY_SANITIZE_UNDEFINED_BEHAVIOR)\n#define FOLLY_SANITIZE 1\n#endif\n#endif\n\n#define FOLLY_DISABLE_SANITIZERS  \\\n  FOLLY_DISABLE_ADDRESS_SANITIZER \\\n  FOLLY_DISABLE_THREAD_SANITIZER  \\\n  FOLLY_DISABLE_MEMORY_SANITIZER  \\\n  FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(\"undefined\")\n\n/**\n * Macro for marking functions as having public visibility.\n */\n#if defined(__GNUC__)\n#define FOLLY_EXPORT __attribute__((__visibility__(\"default\")))\n#else\n#define FOLLY_EXPORT\n#endif\n\n// noinline\n#ifdef _MSC_VER\n#define FOLLY_NOINLINE __declspec(noinline)\n#elif defined(__HIP_PLATFORM_HCC__)\n// HIP software stack defines its own __noinline__ macro.\n#define FOLLY_NOINLINE __attribute__((noinline))\n#elif defined(__GNUC__)\n#define FOLLY_NOINLINE __attribute__((__noinline__))\n#else\n#define FOLLY_NOINLINE\n#endif\n\n// always inline\n#ifdef _MSC_VER\n#define FOLLY_ALWAYS_INLINE __forceinline\n#elif defined(__GNUC__)\n#define FOLLY_ALWAYS_INLINE inline __attribute__((__always_inline__))\n#else\n#define FOLLY_ALWAYS_INLINE inline\n#endif\n\n// attribute hidden\n#if defined(_MSC_VER)\n#define FOLLY_ATTR_VISIBILITY_HIDDEN\n#elif defined(__GNUC__)\n#define FOLLY_ATTR_VISIBILITY_HIDDEN __attribute__((__visibility__(\"hidden\")))\n#else\n#define FOLLY_ATTR_VISIBILITY_HIDDEN\n#endif\n\n// An attribute for marking symbols as weak, if supported\n#if FOLLY_HAVE_WEAK_SYMBOLS\n#define FOLLY_ATTR_WEAK __attribute__((__weak__))\n#else\n#define FOLLY_ATTR_WEAK\n#endif\n\n#if defined(__has_attribute)\n#if __has_attribute(weak)\n#define FOLLY_ATTR_WEAK_SYMBOLS_COMPILE_TIME __attribute__((__weak__))\n#else\n#define FOLLY_ATTR_WEAK_SYMBOLS_COMPILE_TIME\n#endif\n#else\n#define FOLLY_ATTR_WEAK_SYMBOLS_COMPILE_TIME\n#endif\n\n// Microsoft ABI version (can be overridden manually if necessary)\n#ifndef FOLLY_MICROSOFT_ABI_VER\n#ifdef _MSC_VER\n#define FOLLY_MICROSOFT_ABI_VER _MSC_VER\n#endif\n#endif\n\n// FOLLY_NAME_RESOLVABLE\n//\n// An attribute that marks a function or variable as needing to be resolvable\n// by name. This generally is needed if inline assembly refers to the variable\n// by string name.\n#ifdef __roar__\n#define FOLLY_NAME_RESOLVABLE __attribute__((roar_resolvable_by_name))\n#else\n#define FOLLY_NAME_RESOLVABLE\n#endif\n\n//  FOLLY_ERASE\n//\n//  A conceptual attribute/syntax combo for erasing a function from the build\n//  artifacts and forcing all call-sites to inline the callee, at least as far\n//  as each compiler supports.\n//\n//  Semantically includes the inline specifier.\n#define FOLLY_ERASE FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN\n\n//  FOLLY_ERASE_NOINLINE\n//\n//  Like FOLLY_ERASE, but also noinline. The naming similarity with FOLLY_ERASE\n//  is specifically desirable.\n#define FOLLY_ERASE_NOINLINE FOLLY_NOINLINE FOLLY_ATTR_VISIBILITY_HIDDEN\n\n//  FOLLY_ERASE_HACK_GCC\n//\n//  Equivalent to FOLLY_ERASE, but without hiding under gcc. Useful when applied\n//  to a function which may sometimes be hidden separately, for example by being\n//  declared in an anonymous namespace, since in such cases with -Wattributes\n//  enabled, gcc would emit: 'visibility' attribute ignored.\n//\n//  Semantically includes the inline specifier.\n#if defined(__GNUC__) && !defined(__clang__)\n#define FOLLY_ERASE_HACK_GCC FOLLY_ALWAYS_INLINE\n#else\n#define FOLLY_ERASE_HACK_GCC FOLLY_ERASE\n#endif\n\n//  FOLLY_ERASE_TRYCATCH\n//\n//  Equivalent to FOLLY_ERASE, but for code which might contain explicit\n//  exception handling. Has the effect of FOLLY_ERASE, except under MSVC which\n//  warns about __forceinline when functions contain exception handling.\n//\n//  Semantically includes the inline specifier.\n#ifdef _MSC_VER\n#define FOLLY_ERASE_TRYCATCH inline\n#else\n#define FOLLY_ERASE_TRYCATCH FOLLY_ERASE\n#endif\n\n// Generalize warning push/pop.\n#if defined(__GNUC__) || defined(__clang__)\n// Clang & GCC\n#define FOLLY_PUSH_WARNING _Pragma(\"GCC diagnostic push\")\n#define FOLLY_POP_WARNING _Pragma(\"GCC diagnostic pop\")\n#define FOLLY_GNU_ENABLE_WARNING_INTERNAL2(warningName) #warningName\n#define FOLLY_GNU_DISABLE_WARNING_INTERNAL2(warningName) #warningName\n#define FOLLY_GNU_ENABLE_ERROR_INTERNAL2(warningName) #warningName\n#define FOLLY_GNU_DISABLE_WARNING(warningName) \\\n  _Pragma(                                     \\\n      FOLLY_GNU_DISABLE_WARNING_INTERNAL2(GCC diagnostic ignored warningName))\n#define FOLLY_GNU_ENABLE_WARNING(warningName) \\\n  _Pragma(                                    \\\n      FOLLY_GNU_ENABLE_WARNING_INTERNAL2(GCC diagnostic warning warningName))\n#define FOLLY_GNU_ENABLE_ERROR(warningName) \\\n  _Pragma(FOLLY_GNU_ENABLE_ERROR_INTERNAL2(GCC diagnostic error warningName))\n#ifdef __clang__\n#define FOLLY_CLANG_DISABLE_WARNING(warningName) \\\n  FOLLY_GNU_DISABLE_WARNING(warningName)\n#define FOLLY_GCC_DISABLE_WARNING(warningName)\n#else\n#define FOLLY_CLANG_DISABLE_WARNING(warningName)\n#define FOLLY_GCC_DISABLE_WARNING(warningName) \\\n  FOLLY_GNU_DISABLE_WARNING(warningName)\n#endif\n#define FOLLY_MSVC_DISABLE_WARNING(warningNumber)\n#elif defined(_MSC_VER)\n#define FOLLY_PUSH_WARNING __pragma(warning(push))\n#define FOLLY_POP_WARNING __pragma(warning(pop))\n// Disable the GCC warnings.\n#define FOLLY_GNU_ENABLE_WARNING(warningName)\n#define FOLLY_GNU_DISABLE_WARNING(warningName)\n#define FOLLY_GNU_ENABLE_ERROR(warningName)\n#define FOLLY_GCC_DISABLE_WARNING(warningName)\n#define FOLLY_CLANG_DISABLE_WARNING(warningName)\n#define FOLLY_MSVC_DISABLE_WARNING(warningNumber) \\\n  __pragma(warning(disable : warningNumber))\n#else\n#define FOLLY_PUSH_WARNING\n#define FOLLY_POP_WARNING\n#define FOLLY_GNU_ENABLE_WARNING(warningName)\n#define FOLLY_GNU_DISABLE_WARNING(warningName)\n#define FOLLY_GNU_ENABLE_ERROR(warningName)\n#define FOLLY_GCC_DISABLE_WARNING(warningName)\n#define FOLLY_CLANG_DISABLE_WARNING(warningName)\n#define FOLLY_MSVC_DISABLE_WARNING(warningNumber)\n#endif\n\n#ifdef FOLLY_HAVE_SHADOW_LOCAL_WARNINGS\n#define FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS            \\\n  FOLLY_GNU_DISABLE_WARNING(\"-Wshadow-compatible-local\") \\\n  FOLLY_GNU_DISABLE_WARNING(\"-Wshadow-local\")            \\\n  FOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n#else\n#define FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS /* empty */\n#endif\n\n#if defined(_MSC_VER)\n#define FOLLY_MSVC_DECLSPEC(...) __declspec(__VA_ARGS__)\n#else\n#define FOLLY_MSVC_DECLSPEC(...)\n#endif\n"
  },
  {
    "path": "folly/CancellationToken-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <array>\n#include <cstdint>\n#include <limits>\n#include <tuple>\n#include <utility>\n\n#include <glog/logging.h>\n\nnamespace folly {\n\nnamespace detail {\n\nstruct MergingCancellationStateTag {};\n\n// Internal cancellation state object.\nclass CancellationState {\n public:\n  [[nodiscard]] static CancellationStateSourcePtr create();\n\n protected:\n  // Constructed initially with a CancellationSource reference count of 1.\n  CancellationState() noexcept;\n  // Constructed initially with a CancellationToken reference count of 1.\n  explicit CancellationState(MergingCancellationStateTag) noexcept;\n\n  virtual ~CancellationState();\n\n  friend struct CancellationStateTokenDeleter;\n  friend struct CancellationStateSourceDeleter;\n\n  void removeTokenReference() noexcept;\n  void removeSourceReference() noexcept;\n\n public:\n  [[nodiscard]] CancellationStateTokenPtr addTokenReference() noexcept;\n\n  [[nodiscard]] CancellationStateSourcePtr addSourceReference() noexcept;\n\n  bool tryAddCallback(\n      CancellationCallback* callback,\n      bool incrementRefCountIfSuccessful) noexcept;\n\n  void removeCallback(CancellationCallback* callback) noexcept;\n\n  bool isCancellationRequested() const noexcept;\n  bool canBeCancelled() const noexcept;\n\n  // Request cancellation.\n  // Return 'true' if cancellation had already been requested.\n  // Return 'false' if this was the first thread to request\n  // cancellation.\n  bool requestCancellation() noexcept;\n\n private:\n  void lock() noexcept;\n  void unlock() noexcept;\n  void unlockAndIncrementTokenCount() noexcept;\n  void unlockAndDecrementTokenCount() noexcept;\n  bool tryLockAndCancelUnlessCancelled() noexcept;\n\n  template <typename Predicate>\n  bool tryLock(Predicate predicate) noexcept;\n\n  static bool canBeCancelled(std::uint64_t state) noexcept;\n  static bool isCancellationRequested(std::uint64_t state) noexcept;\n  static bool isLocked(std::uint64_t state) noexcept;\n\n  static constexpr std::uint64_t kCancellationRequestedFlag = 1;\n  static constexpr std::uint64_t kLockedFlag = 2;\n  static constexpr std::uint64_t kMergingFlag = 4;\n  static constexpr std::uint64_t kTokenReferenceCountIncrement = 8;\n  static constexpr std::uint64_t kSourceReferenceCountIncrement =\n      std::uint64_t(1) << 34u;\n  static constexpr std::uint64_t kTokenReferenceCountMask =\n      (kSourceReferenceCountIncrement - 1u) -\n      (kTokenReferenceCountIncrement - 1u);\n  static constexpr std::uint64_t kSourceReferenceCountMask =\n      std::numeric_limits<std::uint64_t>::max() -\n      (kSourceReferenceCountIncrement - 1u);\n\n  // Bit 0 - Cancellation Requested\n  // Bit 1 - Locked Flag\n  // Bit 2 - MergingCancellationState Flag\n  // Bits 3-33  - Token reference count (max ~2 billion)\n  // Bits 34-63 - Source reference count (max ~1 billion)\n  std::atomic<std::uint64_t> state_;\n  CancellationCallback* head_{nullptr};\n  std::thread::id signallingThreadId_;\n};\n\ntemplate <typename... Data>\nclass CancellationStateWithData : public CancellationState {\n  template <typename... Args>\n  CancellationStateWithData(Args&&... data);\n\n public:\n  template <typename... Args>\n  [[nodiscard]] static std::\n      pair<CancellationStateSourcePtr, std::tuple<Data...>*>\n      create(Args&&... data);\n\n private:\n  std::tuple<Data...> data_;\n};\n\ninline void CancellationStateTokenDeleter::operator()(\n    CancellationState* state) noexcept {\n  state->removeTokenReference();\n}\n\ninline void CancellationStateSourceDeleter::operator()(\n    CancellationState* state) noexcept {\n  state->removeSourceReference();\n}\n\n} // namespace detail\n\ninline CancellationToken::CancellationToken(\n    const CancellationToken& other) noexcept\n    : state_() {\n  if (other.state_) {\n    state_ = other.state_->addTokenReference();\n  }\n}\n\ninline CancellationToken::CancellationToken(CancellationToken&& other) noexcept\n    : state_(std::move(other.state_)) {}\n\ninline CancellationToken& CancellationToken::operator=(\n    const CancellationToken& other) noexcept {\n  if (state_ != other.state_) {\n    CancellationToken temp{other};\n    swap(temp);\n  }\n  return *this;\n}\n\ninline CancellationToken& CancellationToken::operator=(\n    CancellationToken&& other) noexcept {\n  state_ = std::move(other.state_);\n  return *this;\n}\n\ninline bool CancellationToken::isCancellationRequested() const noexcept {\n  return state_ != nullptr && state_->isCancellationRequested();\n}\n\ninline bool CancellationToken::canBeCancelled() const noexcept {\n  return state_ != nullptr && state_->canBeCancelled();\n}\n\ninline void CancellationToken::swap(CancellationToken& other) noexcept {\n  std::swap(state_, other.state_);\n}\n\ninline CancellationToken::CancellationToken(\n    detail::CancellationStateTokenPtr state) noexcept\n    : state_(std::move(state)) {}\n\ninline bool operator==(\n    const CancellationToken& a, const CancellationToken& b) noexcept {\n  return a.state_ == b.state_;\n}\n\ninline bool operator!=(\n    const CancellationToken& a, const CancellationToken& b) noexcept {\n  return !(a == b);\n}\n\ninline CancellationSource::CancellationSource()\n    : state_(detail::CancellationState::create()) {}\n\ninline CancellationSource::CancellationSource(\n    const CancellationSource& other) noexcept\n    : state_() {\n  if (other.state_) {\n    state_ = other.state_->addSourceReference();\n  }\n}\n\ninline CancellationSource::CancellationSource(\n    CancellationSource&& other) noexcept\n    : state_(std::move(other.state_)) {}\n\ninline CancellationSource& CancellationSource::operator=(\n    const CancellationSource& other) noexcept {\n  if (state_ != other.state_) {\n    CancellationSource temp{other};\n    swap(temp);\n  }\n  return *this;\n}\n\ninline CancellationSource& CancellationSource::operator=(\n    CancellationSource&& other) noexcept {\n  state_ = std::move(other.state_);\n  return *this;\n}\n\ninline CancellationSource CancellationSource::invalid() noexcept {\n  return CancellationSource{detail::CancellationStateSourcePtr{}};\n}\n\ninline bool CancellationSource::isCancellationRequested() const noexcept {\n  return state_ != nullptr && state_->isCancellationRequested();\n}\n\ninline bool CancellationSource::canBeCancelled() const noexcept {\n  return state_ != nullptr;\n}\n\ninline CancellationToken CancellationSource::getToken() const noexcept {\n  if (state_ != nullptr) {\n    return CancellationToken{state_->addTokenReference()};\n  }\n  return CancellationToken{};\n}\n\ninline bool CancellationSource::requestCancellation() const noexcept {\n  if (state_ != nullptr) {\n    return state_->requestCancellation();\n  }\n  return false;\n}\n\ninline void CancellationSource::swap(CancellationSource& other) noexcept {\n  std::swap(state_, other.state_);\n}\n\ninline CancellationSource::CancellationSource(\n    detail::CancellationStateSourcePtr&& state) noexcept\n    : state_(std::move(state)) {}\n\ntemplate <\n    typename Callable,\n    std::enable_if_t<\n        std::is_constructible<CancellationCallback::VoidFunction, Callable>::\n            value,\n        int>>\ninline CancellationCallback::CancellationCallback(\n    CancellationToken&& ct, Callable&& callable)\n    : next_(nullptr),\n      prevNext_(nullptr),\n      state_(nullptr),\n      callback_(static_cast<Callable&&>(callable)),\n      destructorHasRunInsideCallback_(nullptr),\n      callbackCompleted_(false) {\n  if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, false)) {\n    state_ = ct.state_.release();\n  }\n}\n\ntemplate <\n    typename Callable,\n    std::enable_if_t<\n        std::is_constructible<CancellationCallback::VoidFunction, Callable>::\n            value,\n        int>>\ninline CancellationCallback::CancellationCallback(\n    const CancellationToken& ct, Callable&& callable)\n    : next_(nullptr),\n      prevNext_(nullptr),\n      state_(nullptr),\n      callback_(static_cast<Callable&&>(callable)),\n      destructorHasRunInsideCallback_(nullptr),\n      callbackCompleted_(false) {\n  if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, true)) {\n    state_ = ct.state_.get();\n  }\n}\n\ninline CancellationCallback::~CancellationCallback() {\n  if (state_ != nullptr) {\n    state_->removeCallback(this);\n  }\n}\n\ninline void CancellationCallback::invokeCallback() noexcept {\n  // Invoke within a noexcept context so that we std::terminate() if it throws.\n  callback_();\n}\n\nnamespace detail {\n\ninline CancellationStateSourcePtr CancellationState::create() {\n  return CancellationStateSourcePtr{new CancellationState()};\n}\n\ninline CancellationState::CancellationState() noexcept\n    : state_(kSourceReferenceCountIncrement) {}\ninline CancellationState::CancellationState(\n    MergingCancellationStateTag) noexcept\n    : state_(kTokenReferenceCountIncrement | kMergingFlag) {}\n\ninline CancellationStateTokenPtr\nCancellationState::addTokenReference() noexcept {\n  state_.fetch_add(kTokenReferenceCountIncrement, std::memory_order_relaxed);\n  return CancellationStateTokenPtr{this};\n}\n\n// This `alignas()` is here because \"create\" will going to allocate this\n// back-to-back with our `CancellationCallback` array.\nclass alignas(CancellationState) alignas(CancellationCallback)\n    MergingCancellationState : public CancellationState {\n  // There's a noticeable perf gain from specializing the constructors\n  struct CopyTag {};\n  struct MoveTag {};\n  struct CopyMoveTag {};\n\n  explicit MergingCancellationState();\n\n  CancellationCallback* callbackEnd_; // byte after the last callback\n\n public:\n  // Ctors are a private implementation detail of the create*() factory funcs\n  explicit MergingCancellationState(\n      CopyTag, size_t nCopy, const CancellationToken** copyToks);\n  explicit MergingCancellationState(\n      MoveTag, size_t nMove, CancellationToken** moveToks);\n  explicit MergingCancellationState(\n      CopyMoveTag,\n      size_t nCopy,\n      const CancellationToken** copyToks,\n      size_t nMove,\n      CancellationToken** moveToks);\n  ~MergingCancellationState() override;\n\n  static CancellationStateTokenPtr createCopy(\n      size_t nCopy, const CancellationToken** copyToks);\n  static CancellationStateTokenPtr createMove(\n      size_t nMove, CancellationToken** moveToks);\n  static CancellationStateTokenPtr createCopyMove(\n      size_t nCopy,\n      const CancellationToken** copyToks,\n      size_t nMove,\n      CancellationToken** moveToks);\n  void destroy() noexcept;\n};\n\ninline void CancellationState::removeTokenReference() noexcept {\n  const auto oldState = state_.fetch_sub(\n      kTokenReferenceCountIncrement, std::memory_order_acq_rel);\n  DCHECK(\n      (oldState & kTokenReferenceCountMask) >= kTokenReferenceCountIncrement);\n  if (oldState < (2 * kTokenReferenceCountIncrement)) {\n    if (oldState & kMergingFlag) {\n      static_cast<MergingCancellationState*>(this)->destroy();\n    } else {\n      delete this;\n    }\n  }\n}\n\ninline CancellationStateSourcePtr\nCancellationState::addSourceReference() noexcept {\n  state_.fetch_add(kSourceReferenceCountIncrement, std::memory_order_relaxed);\n  return CancellationStateSourcePtr{this};\n}\n\ninline void CancellationState::removeSourceReference() noexcept {\n  const auto oldState = state_.fetch_sub(\n      kSourceReferenceCountIncrement, std::memory_order_acq_rel);\n  DCHECK(\n      (oldState & kSourceReferenceCountMask) >= kSourceReferenceCountIncrement);\n  if (oldState <\n      (kSourceReferenceCountIncrement + kTokenReferenceCountIncrement)) {\n    // No \"free()\" branch because \"merging\" state has no source pointers.\n    DCHECK(!(oldState & kMergingFlag));\n    delete this;\n  }\n}\n\ninline bool CancellationState::isCancellationRequested() const noexcept {\n  return isCancellationRequested(state_.load(std::memory_order_acquire));\n}\n\ninline bool CancellationState::canBeCancelled() const noexcept {\n  return canBeCancelled(state_.load(std::memory_order_acquire));\n}\n\ninline bool CancellationState::canBeCancelled(std::uint64_t state) noexcept {\n  // Can be cancelled if there is at least one CancellationSource ref-count\n  // or if cancellation has been requested.\n  return (state >= kSourceReferenceCountIncrement) ||\n      (state & kMergingFlag) != 0 || isCancellationRequested(state);\n}\n\ninline bool CancellationState::isCancellationRequested(\n    std::uint64_t state) noexcept {\n  return (state & kCancellationRequestedFlag) != 0;\n}\n\ninline bool CancellationState::isLocked(std::uint64_t state) noexcept {\n  return (state & kLockedFlag) != 0;\n}\n\ntemplate <typename... Data>\nstruct WithDataTag {};\n\ntemplate <typename... Data>\ntemplate <typename... Args>\nCancellationStateWithData<Data...>::CancellationStateWithData(Args&&... data)\n    : data_(std::forward<Args>(data)...) {}\n\ntemplate <typename... Data>\ntemplate <typename... Args>\nstd::pair<CancellationStateSourcePtr, std::tuple<Data...>*>\nCancellationStateWithData<Data...>::create(Args&&... data) {\n  auto* state =\n      new CancellationStateWithData<Data...>(std::forward<Args>(data)...);\n  return {CancellationStateSourcePtr{state}, &state->data_};\n}\n\n} // namespace detail\n\ntemplate <typename... Data, typename... Args>\nstd::pair<CancellationSource, std::tuple<Data...>*> CancellationSource::create(\n    detail::WithDataTag<Data...>, Args&&... data) {\n  auto cancellationStateWithData =\n      detail::CancellationStateWithData<Data...>::create(\n          std::forward<Args>(data)...);\n  return {\n      CancellationSource{std::move(cancellationStateWithData.first)},\n      cancellationStateWithData.second};\n}\n\ntemplate <typename... Ts>\nCancellationToken cancellation_token_merge_fn::operator()(\n    Ts&&... tokens) const {\n  if constexpr (sizeof...(Ts) == 0) {\n    return CancellationToken();\n  } else if constexpr (sizeof...(Ts) == 1) {\n    return (tokens, ...);\n  } else {\n    constexpr size_t N = sizeof...(Ts);\n    constexpr size_t NCopy =\n        ((std::is_reference_v<Ts> || std::is_const_v<Ts>)+...);\n    std::array<const CancellationToken*, NCopy> copyToks;\n    std::array<CancellationToken*, N - NCopy> moveToks;\n    const detail::CancellationState* prevState = nullptr;\n    size_t copyIdx = 0, moveIdx = 0;\n    (\n        [&] {\n          constexpr bool mustCopy =\n              std::is_reference_v<Ts> || std::is_const_v<Ts>;\n          auto* state = tokens.state_.get();\n          if (!state) {\n            return; // Omit empties\n          }\n          if (state == prevState) {\n            if constexpr (!mustCopy) {\n              // Move out the input token, although we didn't use it.  The\n              // goal is to make deduplication non-observable by the user.\n              std::exchange(tokens, {});\n            }\n            return; // Omit adjacent duplicates\n          }\n          prevState = state;\n          if constexpr (mustCopy) {\n            copyToks[copyIdx++] = &tokens;\n          } else {\n            moveToks[moveIdx++] = &tokens;\n          }\n        }(),\n        ...);\n\n    size_t n = copyIdx + moveIdx;\n    if (n == 0) {\n      return CancellationToken();\n    } else if (n == 1) {\n      if (moveIdx) { // A ternary would have type `const CT*` and NOT move!\n        return std::move(*moveToks[0]);\n      }\n      return *copyToks[0];\n    } else if constexpr (NCopy == N) {\n      return CancellationToken(\n          detail::MergingCancellationState::createCopy(\n              copyIdx, copyToks.data()));\n    } else if constexpr (NCopy == 0) {\n      return CancellationToken(\n          detail::MergingCancellationState::createMove(\n              moveIdx, moveToks.data()));\n    } else {\n      return CancellationToken(\n          detail::MergingCancellationState::createCopyMove(\n              copyIdx, copyToks.data(), moveIdx, moveToks.data()));\n    }\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/CancellationToken.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/CancellationToken.h>\n#include <folly/Optional.h>\n#include <folly/ScopeGuard.h>\n#include <folly/lang/New.h>\n#include <folly/portability/Memory.h>\n#include <folly/synchronization/detail/Sleeper.h>\n\n#include <glog/logging.h>\n\n#include <new>\n#include <thread>\n#include <tuple>\n\nnamespace folly {\nnamespace detail {\n\nCancellationState::~CancellationState() {\n  DCHECK(head_ == nullptr);\n  DCHECK(!isLocked(state_.load(std::memory_order_relaxed)));\n  DCHECK(\n      state_.load(std::memory_order_relaxed) < kTokenReferenceCountIncrement);\n}\n\nbool CancellationState::tryAddCallback(\n    CancellationCallback* callback,\n    bool incrementRefCountIfSuccessful) noexcept {\n  // Try to acquire the lock, but abandon trying to acquire the lock if\n  // cancellation has already been requested (we can just immediately invoke\n  // the callback) or if cancellation can never be requested (we can just\n  // skip registration).\n  if (!tryLock([callback](std::uint64_t oldState) noexcept {\n        if (isCancellationRequested(oldState)) {\n          callback->invokeCallback();\n          return false;\n        }\n        return canBeCancelled(oldState);\n      })) {\n    return false;\n  }\n\n  // We've acquired the lock and cancellation has not yet been requested.\n  // Push this callback onto the head of the list.\n  if (head_ != nullptr) {\n    head_->prevNext_ = &callback->next_;\n  }\n  callback->next_ = head_;\n  callback->prevNext_ = &head_;\n  head_ = callback;\n\n  if (incrementRefCountIfSuccessful) {\n    // Combine multiple atomic operations into a single atomic operation.\n    unlockAndIncrementTokenCount();\n  } else {\n    unlock();\n  }\n\n  // Successfully added the callback.\n  return true;\n}\n\nvoid CancellationState::removeCallback(\n    CancellationCallback* callback) noexcept {\n  DCHECK(callback != nullptr);\n\n  lock();\n\n  if (callback->prevNext_ != nullptr) {\n    // Still registered in the list => not yet executed.\n    // Just remove it from the list.\n    *callback->prevNext_ = callback->next_;\n    if (callback->next_ != nullptr) {\n      callback->next_->prevNext_ = callback->prevNext_;\n    }\n\n    unlockAndDecrementTokenCount();\n    return;\n  }\n\n  unlock();\n\n  // Callback has either already executed or is executing concurrently on\n  // another thread.\n\n  if (signallingThreadId_ == std::this_thread::get_id()) {\n    // Callback executed on this thread or is still currently executing\n    // and is deregistering itself from within the callback.\n    if (callback->destructorHasRunInsideCallback_ != nullptr) {\n      // Currently inside the callback, let the requestCancellation() method\n      // know the object is about to be destructed and that it should\n      // not try to access the object when the callback returns.\n      *callback->destructorHasRunInsideCallback_ = true;\n    }\n  } else {\n    // Callback is currently executing on another thread, block until it\n    // finishes executing.\n    folly::detail::Sleeper sleeper;\n    while (!callback->callbackCompleted_.load(std::memory_order_acquire)) {\n      sleeper.wait();\n    }\n  }\n\n  removeTokenReference();\n}\n\nbool CancellationState::requestCancellation() noexcept {\n  if (!tryLockAndCancelUnlessCancelled()) {\n    // Was already marked as cancelled\n    return true;\n  }\n\n  // This thread marked as cancelled and acquired the lock\n\n  signallingThreadId_ = std::this_thread::get_id();\n\n  while (head_ != nullptr) {\n    // Dequeue the first item on the queue.\n    CancellationCallback* callback = head_;\n    head_ = callback->next_;\n    const bool anyMore = head_ != nullptr;\n    if (anyMore) {\n      head_->prevNext_ = &head_;\n    }\n    // Mark this item as removed from the list.\n    callback->prevNext_ = nullptr;\n\n    // Don't hold the lock while executing the callback\n    // as we don't want to block other threads from\n    // deregistering callbacks.\n    unlock();\n\n    // TRICKY: Need to store a flag on the stack here that the callback\n    // can use to signal that the destructor was executed inline\n    // during the call.\n    // If the destructor was executed inline then it's not safe to\n    // dereference 'callback' after 'invokeCallback()' returns.\n    // If the destructor runs on some other thread then the other\n    // thread will block waiting for this thread to signal that the\n    // callback has finished executing.\n    bool destructorHasRunInsideCallback = false;\n    callback->destructorHasRunInsideCallback_ = &destructorHasRunInsideCallback;\n\n    callback->invokeCallback();\n\n    if (!destructorHasRunInsideCallback) {\n      callback->destructorHasRunInsideCallback_ = nullptr;\n      callback->callbackCompleted_.store(true, std::memory_order_release);\n    }\n\n    if (!anyMore) {\n      // This was the last item in the queue when we dequeued it.\n      // No more items should be added to the queue after we have\n      // marked the state as cancelled, only removed from the queue.\n      // Avoid acquiring/releasing the lock in this case.\n      return false;\n    }\n\n    lock();\n  }\n\n  unlock();\n\n  return false;\n}\n\nvoid CancellationState::lock() noexcept {\n  folly::detail::Sleeper sleeper;\n  std::uint64_t oldState = state_.load(std::memory_order_relaxed);\n  do {\n    while (isLocked(oldState)) {\n      sleeper.wait();\n      oldState = state_.load(std::memory_order_relaxed);\n    }\n  } while (!state_.compare_exchange_weak(\n      oldState,\n      oldState | kLockedFlag,\n      std::memory_order_acquire,\n      std::memory_order_relaxed));\n}\n\nvoid CancellationState::unlock() noexcept {\n  state_.fetch_sub(kLockedFlag, std::memory_order_release);\n}\n\nvoid CancellationState::unlockAndIncrementTokenCount() noexcept {\n  state_.fetch_sub(\n      kLockedFlag - kTokenReferenceCountIncrement, std::memory_order_release);\n}\n\nvoid CancellationState::unlockAndDecrementTokenCount() noexcept {\n  auto oldState = state_.fetch_sub(\n      kLockedFlag + kTokenReferenceCountIncrement, std::memory_order_acq_rel);\n  if (oldState < (kLockedFlag + 2 * kTokenReferenceCountIncrement)) {\n    // `MergedTokenDestroyedViaCallback` shows how this is triggered.\n    if (UNLIKELY(oldState & kMergingFlag)) {\n      static_cast<MergingCancellationState*>(this)->destroy();\n    } else {\n      delete this;\n    }\n  }\n}\n\nbool CancellationState::tryLockAndCancelUnlessCancelled() noexcept {\n  folly::detail::Sleeper sleeper;\n  std::uint64_t oldState = state_.load(std::memory_order_acquire);\n  while (true) {\n    if (isCancellationRequested(oldState)) {\n      return false;\n    } else if (isLocked(oldState)) {\n      sleeper.wait();\n      oldState = state_.load(std::memory_order_acquire);\n    } else if (state_.compare_exchange_weak(\n                   oldState,\n                   oldState | kLockedFlag | kCancellationRequestedFlag,\n                   std::memory_order_acq_rel,\n                   std::memory_order_acquire)) {\n      return true;\n    }\n  }\n}\n\ntemplate <typename Predicate>\nbool CancellationState::tryLock(Predicate predicate) noexcept {\n  folly::detail::Sleeper sleeper;\n  std::uint64_t oldState = state_.load(std::memory_order_acquire);\n  while (true) {\n    if (!predicate(oldState)) {\n      return false;\n    } else if (isLocked(oldState)) {\n      sleeper.wait();\n      oldState = state_.load(std::memory_order_acquire);\n    } else if (state_.compare_exchange_weak(\n                   oldState,\n                   oldState | kLockedFlag,\n                   std::memory_order_acquire)) {\n      return true;\n    }\n  }\n}\n\n// CTOR EXCEPTION SAFETY: In case the `CancellationCallback` ctors below\n// throw, we increment `callbackEnd_` as we go.  This ensures that the dtor\n// unwinds only the ctors that succeeded.\n\nMergingCancellationState::MergingCancellationState()\n    : CancellationState(MergingCancellationStateTag{}),\n      callbackEnd_(reinterpret_cast<CancellationCallback*>(this + 1)) {}\n\nMergingCancellationState::MergingCancellationState(\n    CopyTag, size_t nCopy, const CancellationToken** copyToks)\n    : MergingCancellationState() {\n  for (size_t i = 0; i < nCopy; ++i, ++callbackEnd_) {\n    new (callbackEnd_) CancellationCallback(*copyToks[i], [this] {\n      requestCancellation();\n    });\n  }\n}\n\nMergingCancellationState::MergingCancellationState(\n    MoveTag, size_t nMove, CancellationToken** moveToks)\n    : MergingCancellationState() {\n  for (size_t i = 0; i < nMove; ++i, ++callbackEnd_) {\n    new (callbackEnd_) CancellationCallback(std::move(*moveToks[i]), [this] {\n      requestCancellation();\n    });\n  }\n}\n\nMergingCancellationState::MergingCancellationState(\n    CopyMoveTag,\n    size_t nCopy,\n    const CancellationToken** copyToks,\n    size_t nMove,\n    CancellationToken** moveToks)\n    : MergingCancellationState(CopyTag{}, nCopy, copyToks) {\n  for (size_t i = 0; i < nMove; ++i, ++callbackEnd_) {\n    new (callbackEnd_) CancellationCallback(std::move(*moveToks[i]), [this] {\n      requestCancellation();\n    });\n  }\n}\n\nnamespace {\ntemplate <typename... Args>\nauto allocAndConstructMergingState(size_t n, Args&&... ctorArgs) {\n  DCHECK_GE(n, 2); // `unlockAndDecrementTokenCount` assumes this\n  // The merging state uses `alignas` -- this makes the offset math easier.\n  static_assert(\n      alignof(MergingCancellationState) >= alignof(CancellationCallback));\n  // Future: If either type needs extended alignment, you must (1) use aligned\n  // `folly::operator_new`, (2) update the alignment math here and in `destroy`.\n  static_assert(alignof(MergingCancellationState) <= alignof(std::max_align_t));\n  static_assert(alignof(CancellationCallback) <= alignof(std::max_align_t));\n  void* p = operator_new( // fundamental alignment suffices per above\n      sizeof(MergingCancellationState) + n * sizeof(CancellationCallback));\n  // Free memory if the ctor throws. NB: Sized `delete` isn't worth it here.\n  auto guard = makeGuard(std::bind(operator_delete, p));\n  auto res = CancellationStateTokenPtr{\n      new (p) MergingCancellationState(std::forward<Args>(ctorArgs)...)};\n  guard.dismiss();\n  return res;\n}\n} // namespace\n\nCancellationStateTokenPtr MergingCancellationState::createCopy(\n    size_t nCopy, const CancellationToken** copyToks) {\n  return allocAndConstructMergingState(nCopy, CopyTag{}, nCopy, copyToks);\n}\nCancellationStateTokenPtr MergingCancellationState::createMove(\n    size_t nMove, CancellationToken** moveToks) {\n  return allocAndConstructMergingState(nMove, MoveTag{}, nMove, moveToks);\n}\nCancellationStateTokenPtr MergingCancellationState::createCopyMove(\n    size_t nCopy,\n    const CancellationToken** copyToks,\n    size_t nMove,\n    CancellationToken** moveToks) {\n  return allocAndConstructMergingState(\n      nCopy + nMove, CopyMoveTag{}, nCopy, copyToks, nMove, moveToks);\n}\n\nMergingCancellationState::~MergingCancellationState() {\n  // Arrays are expected to be destroyed in reverse order (although today's\n  // `MergeCancellationState` specific ctor does not require it).\n  auto callbackStart = reinterpret_cast<CancellationCallback*>(this + 1);\n  while (callbackEnd_ > callbackStart) {\n    (--callbackEnd_)->~CancellationCallback();\n  }\n}\n\nvoid MergingCancellationState::destroy() noexcept {\n  // `MergingCancellationState::create` used `operator_new` + in-place `new`\n  auto allocSize = reinterpret_cast<std::byte*>(callbackEnd_) -\n      reinterpret_cast<std::byte*>(this);\n  this->~MergingCancellationState();\n  operator_delete(this, allocSize); // Sized `delete` might be 1-2ns faster\n}\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/CancellationToken.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CppAttributes.h>\n#include <folly/Function.h>\n#include <folly/OperationCancelled.h>\n\n#include <atomic>\n#include <memory>\n#include <thread>\n#include <type_traits>\n\nnamespace folly {\n\nclass CancellationCallback;\nclass CancellationSource;\nstruct cancellation_token_merge_fn;\n\nnamespace detail {\nclass CancellationState;\nstruct CancellationStateTokenDeleter {\n  void operator()(CancellationState*) noexcept;\n};\nstruct CancellationStateSourceDeleter {\n  void operator()(CancellationState*) noexcept;\n};\nusing CancellationStateTokenPtr =\n    std::unique_ptr<CancellationState, CancellationStateTokenDeleter>;\nusing CancellationStateSourcePtr =\n    std::unique_ptr<CancellationState, CancellationStateSourceDeleter>;\ntemplate <typename...>\nstruct WithDataTag;\n} // namespace detail\n\n/**\n * A CancellationToken is an object that can be passed into an function or\n * operation that allows the caller to later request that the operation be\n * cancelled.\n *\n * A CancellationToken object can be obtained by calling the .getToken()\n * method on a CancellationSource or by copying another CancellationToken\n * object. All CancellationToken objects obtained from the same original\n * CancellationSource object all reference the same underlying cancellation\n * state and will all be cancelled together.\n *\n * If your function needs to be cancellable but does not need to request\n * cancellation then you should take a CancellationToken as a parameter.\n * If your function needs to be able to request cancellation then you\n * should instead take a CancellationSource as a parameter.\n *\n * @refcode folly/docs/examples/folly/CancellationToken.cpp\n * @class folly::CancellationToken\n */\nclass CancellationToken {\n public:\n  /**\n   * Constructs to a token that can never be cancelled.\n   *\n   * Pass a default-constructed CancellationToken into an operation that\n   * you never intend to cancel. These objects are very cheap to create.\n   */\n  CancellationToken() noexcept = default;\n\n  /// Construct a copy of the token that shares the same underlying state.\n  CancellationToken(const CancellationToken& other) noexcept;\n\n  /// Construct a token by moving the underlying state\n  CancellationToken(CancellationToken&& other) noexcept;\n\n  CancellationToken& operator=(const CancellationToken& other) noexcept;\n  CancellationToken& operator=(CancellationToken&& other) noexcept;\n\n  /**\n   * Query whether someone has called .requestCancellation() on an instance\n   * of CancellationSource object associated with this CancellationToken.\n   */\n  bool isCancellationRequested() const noexcept;\n\n  /**\n   * Query whether this CancellationToken can ever have cancellation requested\n   * on it.\n   *\n   * This will return false if the CancellationToken is not associated with a\n   * CancellationSource object. eg. because the CancellationToken was\n   * default-constructed, has been moved-from or because the last\n   * CancellationSource object associated with the underlying cancellation state\n   * has been destroyed and the operation has not yet been cancelled and so\n   * never will be.\n   *\n   * Implementations of operations may be able to take more efficient code-paths\n   * if they know they can never be cancelled.\n   */\n  bool canBeCancelled() const noexcept;\n\n  /**\n   * Swaps the underlying state of the cancellation token with the token that is\n   * passed-in.\n   */\n  void swap(CancellationToken& other) noexcept;\n\n  friend bool operator==(\n      const CancellationToken& a, const CancellationToken& b) noexcept;\n\n private:\n  friend class CancellationCallback;\n  friend class CancellationSource;\n  friend struct cancellation_token_merge_fn;\n\n  explicit CancellationToken(detail::CancellationStateTokenPtr state) noexcept;\n\n  detail::CancellationStateTokenPtr state_;\n};\n\nbool operator==(\n    const CancellationToken& a, const CancellationToken& b) noexcept;\nbool operator!=(\n    const CancellationToken& a, const CancellationToken& b) noexcept;\n\n/**\n * A CancellationSource object provides the ability to request cancellation of\n * operations that an associated CancellationToken was passed to.\n *\n * @refcode folly/docs/examples/folly/CancellationSource.cpp\n * @class folly::CancellationSource\n */\n// Example usage:\n//   CancellationSource cs;\n//   Future<void> f = startSomeOperation(cs.getToken());\n//\n//   // Later...\n//   cs.requestCancellation();\nclass CancellationSource {\n public:\n  /// Construct to a new, independent cancellation source.\n  CancellationSource();\n\n  /**\n   * Construct a new reference to the same underlying cancellation state.\n   *\n   * Either the original or the new copy can be used to request cancellation\n   * of associated work.\n   */\n  CancellationSource(const CancellationSource& other) noexcept;\n\n  /**\n   * This leaves 'other' in an empty state where 'requestCancellation()' is a\n   * no-op and 'canBeCancelled()' returns false.\n   */\n  CancellationSource(CancellationSource&& other) noexcept;\n\n  CancellationSource& operator=(const CancellationSource& other) noexcept;\n  CancellationSource& operator=(CancellationSource&& other) noexcept;\n\n  /**\n   * Construct a CancellationSource that cannot be cancelled.\n   *\n   * This factory function can be used to obtain a CancellationSource that\n   * is equivalent to a moved-from CancellationSource object without needing\n   * to allocate any shared-state.\n   */\n  static CancellationSource invalid() noexcept;\n\n  /**\n   * Query if cancellation has already been requested on this CancellationSource\n   * or any other CancellationSource object copied from the same original\n   * CancellationSource object.\n   */\n  bool isCancellationRequested() const noexcept;\n\n  /**\n   * Query if cancellation can be requested through this CancellationSource\n   * object. This will only return false if the CancellationSource object has\n   * been moved-from.\n   */\n  bool canBeCancelled() const noexcept;\n\n  /**\n   * Obtain a CancellationToken linked to this CancellationSource.\n   *\n   * This token can be passed into cancellable operations to allow the caller\n   * to later request cancellation of that operation.\n   */\n  CancellationToken getToken() const noexcept;\n\n  /**\n   * Request cancellation of work associated with this CancellationSource.\n   *\n   * This will ensure subsequent calls to isCancellationRequested() on any\n   * CancellationSource or CancellationToken object associated with the same\n   * underlying cancellation state to return true.\n   *\n   * If this is the first call to requestCancellation() on any\n   * CancellationSource object with the same underlying state then this call\n   * will also execute the callbacks associated with any CancellationCallback\n   * objects that were constructed with an associated CancellationToken.\n   *\n   * Note that it is possible that another thread may be concurrently\n   * registering a callback with CancellationCallback. This method guarantees\n   * that either this thread will see the callback registration and will\n   * ensure that the callback is called, or the CancellationCallback constructor\n   * will see the cancellation-requested signal and will execute the callback\n   * inline inside the constructor.\n   *\n   * Returns the previous state of 'isCancellationRequested()'. i.e.\n   *  - 'true' if cancellation had previously been requested.\n   *  - 'false' if this was the first call to request cancellation.\n   */\n  bool requestCancellation() const noexcept;\n\n  /**\n   * Swaps the underlying state of the cancellation source with the source that\n   * is passed-in.\n   *\n   * @param other The other cancellation source to copy the underlying state\n   * from.\n   */\n  void swap(CancellationSource& other) noexcept;\n\n  friend bool operator==(\n      const CancellationSource& a, const CancellationSource& b) noexcept;\n\n  /**\n   * Returns a pair of <CancellationSource, Data> where the underlying state is\n   * created using the arguments that is passed-in.\n   */\n  template <typename... Data, typename... Args>\n  static std::pair<CancellationSource, std::tuple<Data...>*> create(\n      detail::WithDataTag<Data...>, Args&&...);\n\n private:\n  explicit CancellationSource(\n      detail::CancellationStateSourcePtr&& state) noexcept;\n\n  detail::CancellationStateSourcePtr state_;\n};\n\nbool operator==(\n    const CancellationSource& a, const CancellationSource& b) noexcept;\nbool operator!=(\n    const CancellationSource& a, const CancellationSource& b) noexcept;\n\n/**\n * A CancellationCallback object registers the callback with the specified\n * CancellationToken such that the callback will be\n * executed if the corresponding CancellationSource object has the\n * requestCancellation() method called on it.\n *\n * If the CancellationToken object already had cancellation requested\n * then the callback will be executed inline on the current thread before\n * the constructor returns. Otherwise, the callback will be executed on\n * in the execution context of the first thread to call requestCancellation()\n * on a corresponding CancellationSource.\n *\n * The callback object must not throw any unhandled exceptions. Doing so\n * will result in the program terminating via std::terminate().\n *\n * A CancellationCallback object is neither copyable nor movable.\n *\n * @refcode folly/docs/examples/folly/CancellationCallback.cpp\n * @class folly::CancellationCallback\n */\nclass CancellationCallback {\n  using VoidFunction = folly::Function<void()>;\n\n public:\n  template <\n      typename Callable,\n      std::enable_if_t<\n          std::is_constructible<VoidFunction, Callable>::value,\n          int> = 0>\n  CancellationCallback(CancellationToken&& ct, Callable&& callable);\n  template <\n      typename Callable,\n      std::enable_if_t<\n          std::is_constructible<VoidFunction, Callable>::value,\n          int> = 0>\n  CancellationCallback(const CancellationToken& ct, Callable&& callable);\n\n  /**\n   * Deregisters the callback from the CancellationToken.\n   *\n   * If cancellation has been requested concurrently on another thread and the\n   * callback is currently executing then the destructor will block until after\n   * the callback has returned (otherwise it might be left with a dangling\n   * reference).\n   *\n   * You should generally try to implement your callback functions to be lock\n   * free to avoid deadlocks between the callback executing and the\n   * CancellationCallback destructor trying to deregister the callback.\n   *\n   * If the callback has not started executing yet then the callback will be\n   * deregistered from the CancellationToken before the destructor completes.\n   *\n   * Once the destructor returns you can be guaranteed that the callback will\n   * not be called by a subsequent call to 'requestCancellation()' on a\n   * CancellationSource associated with the CancellationToken passed to the\n   * constructor.\n   */\n  ~CancellationCallback();\n\n  // Not copyable/movable\n  CancellationCallback(const CancellationCallback&) = delete;\n  CancellationCallback(CancellationCallback&&) = delete;\n  CancellationCallback& operator=(const CancellationCallback&) = delete;\n  CancellationCallback& operator=(CancellationCallback&&) = delete;\n\n private:\n  friend class detail::CancellationState;\n\n  void invokeCallback() noexcept;\n\n  CancellationCallback* next_;\n\n  // Pointer to the pointer that points to this node in the linked list.\n  // This could be the 'next_' of a previous CancellationCallback or could\n  // be the 'head_' pointer of the CancellationState.\n  // If this node is inserted in the list then this will be non-null.\n  CancellationCallback** prevNext_;\n\n  detail::CancellationState* state_;\n  VoidFunction callback_;\n\n  // Pointer to a flag stored on the stack of the caller to invokeCallback()\n  // that is used to indicate to the caller of invokeCallback() that the\n  // destructor has run and it is no longer valid to access the callback\n  // object.\n  bool* destructorHasRunInsideCallback_;\n\n  // Flag used to signal that the callback has completed executing on another\n  // thread and it is now safe to exit the destructor.\n  std::atomic<bool> callbackCompleted_;\n};\n\n/**\n * Obtain a CancellationToken linked to any number of other\n * CancellationTokens.\n *\n * This token will have cancellation requested when any of the passed-in\n * tokens do.\n * This token is cancellable if any of the passed-in tokens are at the time of\n * construction.\n *\n * Example:\n *   CancellationSource a,b;\n *   auto c = cancellation_token_merge(a.getToken(), b.getToken());\n */\nstruct cancellation_token_merge_fn {\n  template <typename... Ts>\n  CancellationToken operator()(Ts&&... tokens) const;\n};\ninline constexpr cancellation_token_merge_fn cancellation_token_merge{};\n\n} // namespace folly\n\n#include <folly/CancellationToken-inl.h>\n"
  },
  {
    "path": "folly/Chrono.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <ctime>\n#include <stdexcept>\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/lang/Exception.h>\n#include <folly/portability/Time.h>\n\nnamespace folly {\nnamespace chrono {\n\n/* using override */ using std::chrono::abs;\n/* using override */ using std::chrono::ceil;\n/* using override */ using std::chrono::floor;\n/* using override */ using std::chrono::round;\n\n//  steady_clock_spec\n//\n//  All clocks with this spec share epoch and tick rate.\nstruct steady_clock_spec {};\n\n//  system_clock_spec\n//\n//  All clocks with this spec share epoch and tick rate.\nstruct system_clock_spec {};\n\n//  clock_traits\n//\n//  Detects and reexports per-clock traits.\n//\n//  Specializeable for clocks for which trait detection fails..\ntemplate <typename Clock>\nstruct clock_traits {\n private:\n  template <typename C>\n  using detect_spec_ = typename C::folly_spec;\n\n public:\n  using spec = detected_or_t<void, detect_spec_, Clock>;\n};\n\ntemplate <>\nstruct clock_traits<std::chrono::steady_clock> {\n  using spec = steady_clock_spec;\n};\ntemplate <>\nstruct clock_traits<std::chrono::system_clock> {\n  using spec = system_clock_spec;\n};\n\nstruct coarse_steady_clock {\n  using folly_spec = steady_clock_spec;\n\n  using duration = std::chrono::steady_clock::duration;\n  using rep = duration::rep;\n  using period = duration::period;\n  using time_point = std::chrono::time_point<coarse_steady_clock>;\n  constexpr static bool is_steady = true;\n\n  static time_point now() noexcept {\n#ifndef CLOCK_MONOTONIC_COARSE\n    auto time = std::chrono::steady_clock::now().time_since_epoch();\n#else\n    timespec ts;\n    int ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);\n    if (kIsDebug && (ret != 0)) {\n      throw_exception<std::runtime_error>(\n          \"Error using CLOCK_MONOTONIC_COARSE.\");\n    }\n    auto time =\n        std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec);\n#endif\n    return time_point(std::chrono::duration_cast<duration>(time));\n  }\n};\n\nstruct coarse_system_clock {\n  using folly_spec = system_clock_spec;\n\n  using duration = std::chrono::system_clock::duration;\n  using rep = duration::rep;\n  using period = duration::period;\n  using time_point = std::chrono::time_point<coarse_system_clock>;\n  constexpr static bool is_steady = false;\n\n  static time_point now() noexcept {\n#ifndef CLOCK_REALTIME_COARSE\n    auto time = std::chrono::system_clock::now().time_since_epoch();\n#else\n    timespec ts;\n    int ret = clock_gettime(CLOCK_REALTIME_COARSE, &ts);\n    if (kIsDebug && (ret != 0)) {\n      throw_exception<std::runtime_error>(\"Error using CLOCK_REALTIME_COARSE.\");\n    }\n    auto time =\n        std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec);\n#endif\n    return time_point(std::chrono::duration_cast<duration>(time));\n  }\n\n  static std::time_t to_time_t(const time_point& t) noexcept {\n    auto d = t.time_since_epoch();\n    return std::chrono::duration_cast<std::chrono::seconds>(d).count();\n  }\n\n  static time_point from_time_t(std::time_t t) noexcept {\n    return time_point(\n        std::chrono::duration_cast<duration>(std::chrono::seconds(t)));\n  }\n};\n\n} // namespace chrono\n} // namespace folly\n"
  },
  {
    "path": "folly/ClockGettimeWrappers.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ClockGettimeWrappers.h>\n\n#include <folly/Likely.h>\n#include <folly/portability/Time.h>\n\n#include <chrono>\n\n#include <ctime>\n\n#ifndef _WIN32\n#define _GNU_SOURCE 1\n#include <dlfcn.h>\n#endif\n\nnamespace folly {\nnamespace chrono {\n\nstatic int64_t clock_gettime_ns_fallback(clockid_t clock) {\n  struct timespec ts;\n  int r = clock_gettime(clock, &ts);\n  if (FOLLY_UNLIKELY(r != 0)) {\n    // Mimic what __clock_gettime_ns does (even though this can be a legit\n    // value).\n    return -1;\n  }\n  std::chrono::nanoseconds result =\n      std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec);\n  return result.count();\n}\n\n// Initialize with default behavior, which we might override on Linux hosts\n// with VDSO support.\nint (*clock_gettime)(clockid_t, timespec* ts) = &::clock_gettime;\nint64_t (*clock_gettime_ns)(clockid_t) = &clock_gettime_ns_fallback;\n\n// In MSAN mode use glibc's versions as they are intercepted by the MSAN\n// runtime which properly tracks memory initialization.\n#if defined(FOLLY_HAVE_LINUX_VDSO) && !defined(FOLLY_SANITIZE_MEMORY)\n\nnamespace {\n\nstruct VdsoInitializer {\n  VdsoInitializer() {\n    m_handle = dlopen(\"linux-vdso.so.1\", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);\n    if (!m_handle) {\n      return;\n    }\n\n    void* p = dlsym(m_handle, \"__vdso_clock_gettime\");\n    if (p) {\n      folly::chrono::clock_gettime = (int (*)(clockid_t, timespec*))p;\n    }\n    p = dlsym(m_handle, \"__vdso_clock_gettime_ns\");\n    if (p) {\n      folly::chrono::clock_gettime_ns = (int64_t (*)(clockid_t))p;\n    }\n  }\n\n  ~VdsoInitializer() {\n    if (m_handle) {\n      clock_gettime = &::clock_gettime;\n      clock_gettime_ns = &clock_gettime_ns_fallback;\n      dlclose(m_handle);\n    }\n  }\n\n private:\n  void* m_handle;\n};\n\nconst VdsoInitializer vdso_initializer;\n} // namespace\n\n#endif\n} // namespace chrono\n} // namespace folly\n"
  },
  {
    "path": "folly/ClockGettimeWrappers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/portability/Time.h>\n\n#include <time.h>\n\nnamespace folly {\nnamespace chrono {\n\nextern int (*clock_gettime)(clockid_t, timespec* ts);\nextern int64_t (*clock_gettime_ns)(clockid_t);\n} // namespace chrono\n} // namespace folly\n"
  },
  {
    "path": "folly/ConcurrentBitSet.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <atomic>\n#include <cassert>\n#include <cstddef>\n#include <limits>\n\n#include <folly/Portability.h>\n\nnamespace folly {\n\n/**\n * An atomic bitset of fixed size (specified at compile time).\n *\n * Formerly known as AtomicBitSet. It was renamed while fixing a bug\n * to avoid any silent breakages during run time.\n */\ntemplate <size_t N>\nclass ConcurrentBitSet {\n public:\n  /**\n   * Construct a ConcurrentBitSet; all bits are initially false.\n   */\n  ConcurrentBitSet();\n\n  ConcurrentBitSet(const ConcurrentBitSet&) = delete;\n  ConcurrentBitSet& operator=(const ConcurrentBitSet&) = delete;\n\n  /**\n   * Set bit idx to true, using the given memory order. Returns the\n   * previous value of the bit.\n   *\n   * Note that the operation is a read-modify-write operation due to the use\n   * of fetch_or.\n   */\n  bool set(size_t idx, std::memory_order order = std::memory_order_seq_cst);\n\n  /**\n   * Set bit idx to false, using the given memory order. Returns the\n   * previous value of the bit.\n   *\n   * Note that the operation is a read-modify-write operation due to the use\n   * of fetch_and.\n   */\n  bool reset(size_t idx, std::memory_order order = std::memory_order_seq_cst);\n\n  /**\n   * Set bit idx to the given value, using the given memory order. Returns\n   * the previous value of the bit.\n   *\n   * Note that the operation is a read-modify-write operation due to the use\n   * of fetch_and or fetch_or.\n   *\n   * Yes, this is an overload of set(), to keep as close to std::bitset's\n   * interface as possible.\n   */\n  bool set(\n      size_t idx,\n      bool value,\n      std::memory_order order = std::memory_order_seq_cst);\n\n  /**\n   * Read bit idx.\n   */\n  bool test(\n      size_t idx, std::memory_order order = std::memory_order_seq_cst) const;\n\n  /**\n   * Same as test() with the default memory order.\n   */\n  bool operator[](size_t idx) const;\n\n  /**\n   * Return the size of the bitset.\n   */\n  constexpr size_t size() const { return N; }\n\n private:\n  // Pick the largest lock-free type available\n#if (ATOMIC_LLONG_LOCK_FREE == 2)\n  using BlockType = unsigned long long;\n#elif (ATOMIC_LONG_LOCK_FREE == 2)\n  typedef unsigned long BlockType;\n#else\n  // Even if not lock free, what can we do?\n  typedef unsigned int BlockType;\n#endif\n  using AtomicBlockType = std::atomic<BlockType>;\n\n  static constexpr size_t kBitsPerBlock =\n      std::numeric_limits<BlockType>::digits;\n\n  static constexpr size_t blockIndex(size_t bit) { return bit / kBitsPerBlock; }\n\n  static constexpr size_t bitOffset(size_t bit) { return bit % kBitsPerBlock; }\n\n  // avoid casts\n  static constexpr BlockType kOne = 1;\n  static constexpr size_t kNumBlocks = (N + kBitsPerBlock - 1) / kBitsPerBlock;\n  std::array<AtomicBlockType, kNumBlocks> data_;\n};\n\n// value-initialize to zero\ntemplate <size_t N>\ninline ConcurrentBitSet<N>::ConcurrentBitSet() : data_() {}\n\ntemplate <size_t N>\ninline bool ConcurrentBitSet<N>::set(size_t idx, std::memory_order order) {\n  assert(idx < N);\n  BlockType mask = kOne << bitOffset(idx);\n  return data_[blockIndex(idx)].fetch_or(mask, order) & mask;\n}\n\ntemplate <size_t N>\ninline bool ConcurrentBitSet<N>::reset(size_t idx, std::memory_order order) {\n  assert(idx < N);\n  BlockType mask = kOne << bitOffset(idx);\n  return data_[blockIndex(idx)].fetch_and(~mask, order) & mask;\n}\n\ntemplate <size_t N>\ninline bool ConcurrentBitSet<N>::set(\n    size_t idx, bool value, std::memory_order order) {\n  return value ? set(idx, order) : reset(idx, order);\n}\n\ntemplate <size_t N>\ninline bool ConcurrentBitSet<N>::test(\n    size_t idx, std::memory_order order) const {\n  assert(idx < N);\n  BlockType mask = kOne << bitOffset(idx);\n  return data_[blockIndex(idx)].load(order) & mask;\n}\n\ntemplate <size_t N>\ninline bool ConcurrentBitSet<N>::operator[](size_t idx) const {\n  return test(idx);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ConcurrentLazy.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include <folly/functional/Invoke.h>\n#include <folly/synchronization/DelayedInit.h>\n\nnamespace folly {\n\n/*\n * ConcurrentLazy is for thread-safe, delayed initialization of a value. This\n * combines the benefits of both `folly::Lazy` and `folly::DelayedInit` to\n * compute the value, once, at access time.\n *\n * There are a few differences between the non-concurrent Lazy, most notably:\n *\n *   - this only safely initializes the value; thread-safety of the underlying\n *     value is left up to the caller.\n *   - the underlying types are not copyable or moveable, which means that this\n *     type is also not copyable or moveable.\n *\n * Otherwise, all design considerations from `folly::Lazy` are reflected here.\n */\n\ntemplate <class Ctor>\nstruct ConcurrentLazy {\n  using result_type = invoke_result_t<Ctor>;\n\n  static_assert(\n      !std::is_const<Ctor>::value, \"Func should not be a const-qualified type\");\n  static_assert(\n      !std::is_reference<Ctor>::value, \"Func should not be a reference type\");\n\n  template <\n      typename F,\n      std::enable_if_t<std::is_constructible_v<Ctor, F>, int> = 0>\n  explicit ConcurrentLazy(F&& f) noexcept(\n      std::is_nothrow_constructible_v<Ctor, F>)\n      : ctor_(static_cast<F&&>(f)) {}\n\n  const result_type& operator()() const {\n    return value_.try_emplace_with(std::ref(ctor_));\n  }\n\n  result_type& operator()() { return value_.try_emplace_with(std::ref(ctor_)); }\n\n private:\n  mutable folly::DelayedInit<result_type> value_;\n  mutable Ctor ctor_;\n};\n\ntemplate <class Func>\nConcurrentLazy<remove_cvref_t<Func>> concurrent_lazy(Func&& func) {\n  return ConcurrentLazy<remove_cvref_t<Func>>(static_cast<Func&&>(func));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ConcurrentSkipList-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <climits>\n#include <cmath>\n#include <memory>\n#include <mutex>\n#include <type_traits>\n#include <vector>\n\n#include <boost/random.hpp>\n#include <glog/logging.h>\n\n#include <folly/ConstexprMath.h>\n#include <folly/CppAttributes.h>\n#include <folly/Memory.h>\n#include <folly/ThreadLocal.h>\n#include <folly/synchronization/MicroSpinLock.h>\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename ValT, typename NodeT>\nclass csl_iterator;\n\ntemplate <typename T>\nclass SkipListNode {\n  enum : uint16_t {\n    IS_HEAD_NODE = 1,\n    MARKED_FOR_REMOVAL = (1 << 1),\n    FULLY_LINKED = (1 << 2),\n  };\n\n public:\n  using value_type = T;\n\n  SkipListNode(const SkipListNode&) = delete;\n  SkipListNode& operator=(const SkipListNode&) = delete;\n\n  template <\n      typename NodeAlloc,\n      typename U,\n      typename =\n          typename std::enable_if<std::is_convertible<U, T>::value>::type>\n  static SkipListNode* create(\n      NodeAlloc& alloc, int height, U&& data, bool isHead = false) {\n    DCHECK(height >= 1 && height < 64) << height;\n\n    size_t size =\n        sizeof(SkipListNode) + height * sizeof(std::atomic<SkipListNode*>);\n    auto storage = std::allocator_traits<NodeAlloc>::allocate(alloc, size);\n    // do placement new\n    return new (storage)\n        SkipListNode(uint8_t(height), std::forward<U>(data), isHead);\n  }\n\n  template <typename NodeAlloc>\n  static void destroy(NodeAlloc& alloc, SkipListNode* node) {\n    size_t size = sizeof(SkipListNode) +\n        node->height_ * sizeof(std::atomic<SkipListNode*>);\n    node->~SkipListNode();\n    std::allocator_traits<NodeAlloc>::deallocate(\n        alloc, typename std::allocator_traits<NodeAlloc>::pointer(node), size);\n  }\n\n  template <typename NodeAlloc>\n  struct DestroyIsNoOp\n      : StrictConjunction<\n            AllocatorHasTrivialDeallocate<NodeAlloc>,\n            std::is_trivially_destructible<SkipListNode>> {};\n\n  // copy the head node to a new head node assuming lock acquired\n  SkipListNode* copyHead(SkipListNode* node) {\n    DCHECK(node != nullptr && height_ > node->height_);\n    setFlags(node->getFlags());\n    for (uint8_t i = 0; i < node->height_; ++i) {\n      setSkip(i, node->skip(i));\n    }\n    return this;\n  }\n\n  inline SkipListNode* skip(int layer) const {\n    DCHECK_LT(layer, height_);\n    return skip_[layer].load(std::memory_order_acquire);\n  }\n\n  // next valid node as in the linked list\n  SkipListNode* next() {\n    SkipListNode* node;\n    for (node = skip(0); (node != nullptr && node->markedForRemoval());\n         node = node->skip(0)) {\n    }\n    return node;\n  }\n\n  void setSkip(uint8_t h, SkipListNode* next) {\n    DCHECK_LT(h, height_);\n    skip_[h].store(next, std::memory_order_release);\n  }\n\n  value_type& data() { return data_; }\n  const value_type& data() const { return data_; }\n  int maxLayer() const { return height_ - 1; }\n  int height() const { return height_; }\n\n  std::unique_lock<MicroSpinLock> acquireGuard() {\n    return std::unique_lock<MicroSpinLock>(spinLock_);\n  }\n\n  bool fullyLinked() const { return getFlags() & FULLY_LINKED; }\n  bool markedForRemoval() const { return getFlags() & MARKED_FOR_REMOVAL; }\n  bool isHeadNode() const { return getFlags() & IS_HEAD_NODE; }\n\n  void setIsHeadNode() { setFlags(uint16_t(getFlags() | IS_HEAD_NODE)); }\n  void setFullyLinked() { setFlags(uint16_t(getFlags() | FULLY_LINKED)); }\n  void setMarkedForRemoval() {\n    setFlags(uint16_t(getFlags() | MARKED_FOR_REMOVAL));\n  }\n\n private:\n  // Note! this can only be called from create() as a placement new.\n  template <typename U>\n  SkipListNode(uint8_t height, U&& data, bool isHead)\n      : height_(height), data_(std::forward<U>(data)) {\n    spinLock_.init();\n    setFlags(0);\n    if (isHead) {\n      setIsHeadNode();\n    }\n    // need to explicitly init the dynamic atomic pointer array\n    for (uint8_t i = 0; i < height_; ++i) {\n      new (&skip_[i]) std::atomic<SkipListNode*>(nullptr);\n    }\n  }\n\n  ~SkipListNode() {\n    for (uint8_t i = 0; i < height_; ++i) {\n      skip_[i].~atomic();\n    }\n  }\n\n  uint16_t getFlags() const { return flags_.load(std::memory_order_acquire); }\n  void setFlags(uint16_t flags) {\n    flags_.store(flags, std::memory_order_release);\n  }\n\n  // TODO(xliu): on x86_64, it's possible to squeeze these into\n  // skip_[0] to maybe save 8 bytes depending on the data alignments.\n  // NOTE: currently this is x86_64 only anyway, due to the\n  // MicroSpinLock.\n  std::atomic<uint16_t> flags_;\n  const uint8_t height_;\n  MicroSpinLock spinLock_;\n\n  value_type data_;\n\n  std::atomic<SkipListNode*> skip_[0];\n};\n\nclass SkipListRandomHeight {\n  enum { kMaxHeight = 64 };\n\n public:\n  // make it a singleton.\n  static SkipListRandomHeight* instance() {\n    static SkipListRandomHeight instance_;\n    return &instance_;\n  }\n\n  int getHeight(int maxHeight) const {\n    DCHECK_LE(maxHeight, kMaxHeight) << \"max height too big!\";\n    double p = randomProb();\n    for (int i = 0; i < maxHeight; ++i) {\n      if (p < lookupTable_[i]) {\n        return i + 1;\n      }\n    }\n    return maxHeight;\n  }\n\n  size_t getSizeLimit(int height) const {\n    DCHECK_LT(height, kMaxHeight);\n    return sizeLimitTable_[height];\n  }\n\n private:\n  SkipListRandomHeight() { initLookupTable(); }\n\n  void initLookupTable() {\n    // set skip prob = 1/E\n    static const double kProbInv = exp(1);\n    static const double kProb = 1.0 / kProbInv;\n    static const size_t kMaxSizeLimit = std::numeric_limits<size_t>::max();\n\n    double sizeLimit = 1;\n    double p = lookupTable_[0] = (1 - kProb);\n    sizeLimitTable_[0] = 1;\n    for (int i = 1; i < kMaxHeight - 1; ++i) {\n      p *= kProb;\n      sizeLimit *= kProbInv;\n      lookupTable_[i] = lookupTable_[i - 1] + p;\n      sizeLimitTable_[i] = folly::constexpr_clamp_cast<size_t>(sizeLimit);\n    }\n    lookupTable_[kMaxHeight - 1] = 1;\n    sizeLimitTable_[kMaxHeight - 1] = kMaxSizeLimit;\n  }\n\n  static double randomProb() {\n    static ThreadLocal<boost::lagged_fibonacci2281> rng_;\n    return (*rng_)();\n  }\n\n  double lookupTable_[kMaxHeight];\n  size_t sizeLimitTable_[kMaxHeight];\n};\n\ntemplate <typename NodeType, typename NodeAlloc, typename = void>\nclass NodeRecycler;\n\ntemplate <typename NodeType, typename NodeAlloc>\nclass NodeRecycler<\n    NodeType,\n    NodeAlloc,\n    typename std::enable_if<\n        !NodeType::template DestroyIsNoOp<NodeAlloc>::value>::type> {\n public:\n  explicit NodeRecycler(const NodeAlloc& alloc)\n      : refs_(0), dirty_(false), alloc_(alloc) {\n    lock_.init();\n  }\n\n  explicit NodeRecycler() : refs_(0), dirty_(false) { lock_.init(); }\n\n  ~NodeRecycler() {\n    CHECK_EQ(refs(), 0);\n    if (nodes_) {\n      for (auto& node : *nodes_) {\n        NodeType::destroy(alloc_, node);\n      }\n    }\n  }\n\n  void add(NodeType* node) {\n    std::lock_guard g(lock_);\n    if (nodes_.get() == nullptr) {\n      nodes_ = std::make_unique<std::vector<NodeType*>>(1, node);\n    } else {\n      nodes_->push_back(node);\n    }\n    DCHECK_GT(refs(), 0);\n    dirty_.store(true, std::memory_order_relaxed);\n  }\n\n  int addRef() { return refs_.fetch_add(1, std::memory_order_acq_rel); }\n\n  int releaseRef() {\n    // This if statement is purely an optimization. It's possible that this\n    // misses an opportunity to delete, but that's OK, we'll try again at\n    // the next opportunity. It does not harm the thread safety. For this\n    // reason, we can use relaxed loads to make the decision.\n    if (!dirty_.load(std::memory_order_relaxed) || refs() > 1) {\n      return refs_.fetch_add(-1, std::memory_order_acq_rel);\n    }\n\n    std::unique_ptr<std::vector<NodeType*>> newNodes;\n    int ret;\n    {\n      // The order at which we lock, add, swap, is very important for\n      // correctness.\n      std::lock_guard g(lock_);\n      ret = refs_.fetch_add(-1, std::memory_order_acq_rel);\n      if (ret == 1) {\n        // When releasing the last reference, it is safe to remove all the\n        // current nodes in the recycler, as we already acquired the lock here\n        // so no more new nodes can be added, even though new accessors may be\n        // added after this.\n        newNodes.swap(nodes_);\n        dirty_.store(false, std::memory_order_relaxed);\n      }\n    }\n    // TODO(xliu) should we spawn a thread to do this when there are large\n    // number of nodes in the recycler?\n    if (newNodes) {\n      for (auto& node : *newNodes) {\n        NodeType::destroy(alloc_, node);\n      }\n    }\n    return ret;\n  }\n\n  NodeAlloc& alloc() { return alloc_; }\n\n private:\n  int refs() const { return refs_.load(std::memory_order_relaxed); }\n\n  std::unique_ptr<std::vector<NodeType*>> nodes_;\n  std::atomic<int32_t> refs_; // current number of visitors to the list\n  std::atomic<bool> dirty_; // whether *nodes_ is non-empty\n  MicroSpinLock lock_; // protects access to *nodes_\n  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] NodeAlloc alloc_;\n};\n\n// In case of arena allocator, no recycling is necessary, and it's possible\n// to save on ConcurrentSkipList size.\ntemplate <typename NodeType, typename NodeAlloc>\nclass NodeRecycler<\n    NodeType,\n    NodeAlloc,\n    typename std::enable_if<\n        NodeType::template DestroyIsNoOp<NodeAlloc>::value>::type> {\n public:\n  explicit NodeRecycler(const NodeAlloc& alloc) : alloc_(alloc) {}\n\n  void addRef() {}\n  void releaseRef() {}\n\n  void add(NodeType* /* node */) {}\n\n  NodeAlloc& alloc() { return alloc_; }\n\n private:\n  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] NodeAlloc alloc_;\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/ConcurrentSkipList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// A concurrent skip list (CSL) implementation.\n// Ref: http://www.cs.tau.ac.il/~shanir/nir-pubs-web/Papers/OPODIS2006-BA.pdf\n\n/*\n\nThis implements a sorted associative container that supports only\nunique keys.  (Similar to std::set.)\n\nFeatures:\n\n  1. Small memory overhead: ~40% less memory overhead compared with\n     std::set (1.6 words per node versus 3). It has an minimum of 4\n     words (7 words if there nodes got deleted) per-list overhead\n     though.\n\n  2. Read accesses (count, find iterator, skipper) are lock-free and\n     mostly wait-free (the only wait a reader may need to do is when\n     the node it is visiting is in a pending stage, i.e. deleting,\n     adding and not fully linked).  Write accesses (remove, add) need\n     to acquire locks, but locks are local to the predecessor nodes\n     and/or successor nodes.\n\n  3. Good high contention performance, comparable single-thread\n     performance.  In the multithreaded case (12 workers), CSL tested\n     10x faster than a RWSpinLocked std::set for an averaged sized\n     list (1K - 1M nodes).\n\n     Comparable read performance to std::set when single threaded,\n     especially when the list size is large, and scales better to\n     larger lists: when the size is small, CSL can be 20-50% slower on\n     find()/contains().  As the size gets large (> 1M elements),\n     find()/contains() can be 30% faster.\n\n     Iterating through a skiplist is similar to iterating through a\n     linked list, thus is much (2-6x) faster than on a std::set\n     (tree-based).  This is especially true for short lists due to\n     better cache locality.  Based on that, it's also faster to\n     intersect two skiplists.\n\n  4. Lazy removal with GC support.  The removed nodes get deleted when\n     the last Accessor to the skiplist is destroyed.\n\nCaveats:\n\n  1. Write operations are usually 30% slower than std::set in a single\n     threaded environment.\n\n  2. Need to have a head node for each list, which has a 4 word\n     overhead.\n\n  3. When the list is quite small (< 1000 elements), single threaded\n     benchmarks show CSL can be 10x slower than std:set.\n\n  4. The interface requires using an Accessor to access the skiplist.\n    (See below.)\n\n  5. Currently x64 only, due to use of MicroSpinLock.\n\n  6. Freed nodes will not be reclaimed as long as there are ongoing\n     uses of the list.\n\nSample usage:\n\n     typedef ConcurrentSkipList<int> SkipListT;\n     shared_ptr<SkipListT> sl(SkipListT::createInstance(init_head_height);\n     {\n       // It's usually good practice to hold an accessor only during\n       // its necessary life cycle (but not in a tight loop as\n       // Accessor creation incurs ref-counting overhead).\n       //\n       // Holding it longer delays garbage-collecting the deleted\n       // nodes in the list.\n       SkipListT::Accessor accessor(sl);\n       accessor.insert(23);\n       accessor.erase(2);\n       for (auto &elem : accessor) {\n         // use elem to access data\n       }\n       ... ...\n     }\n\n Another useful type is the Skipper accessor.  This is useful if you\n want to skip to locations in the way std::lower_bound() works,\n i.e. it can be used for going through the list by skipping to the\n node no less than a specified key.  The Skipper keeps its location as\n state, which makes it convenient for things like implementing\n intersection of two sets efficiently, as it can start from the last\n visited position.\n\n     {\n       SkipListT::Accessor accessor(sl);\n       SkipListT::Skipper skipper(accessor);\n       skipper.to(30);\n       if (skipper) {\n         CHECK_LE(30, *skipper);\n       }\n       ...  ...\n       // GC may happen when the accessor gets destructed.\n     }\n*/\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <limits>\n#include <memory>\n#include <type_traits>\n\n#include <glog/logging.h>\n\n#include <folly/ConcurrentSkipList-inl.h>\n#include <folly/Likely.h>\n#include <folly/Memory.h>\n#include <folly/detail/Iterators.h>\n#include <folly/synchronization/MicroSpinLock.h>\n\nnamespace folly {\n\ntemplate <\n    typename T,\n    typename Comp = std::less<T>,\n    // All nodes are allocated using provided SysAllocator,\n    // it should be thread-safe.\n    typename NodeAlloc = SysAllocator<char>,\n    int MAX_HEIGHT = 24>\nclass ConcurrentSkipList {\n  // MAX_HEIGHT needs to be at least 2 to suppress compiler\n  // warnings/errors (Werror=uninitialized triggered due to preds_[1]\n  // being treated as a scalar in the compiler).\n  static_assert(\n      MAX_HEIGHT >= 2 && MAX_HEIGHT < 64,\n      \"MAX_HEIGHT can only be in the range of [2, 64)\");\n  using ScopedLocker = std::unique_lock<folly::MicroSpinLock>;\n  using SkipListType = ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>;\n\n public:\n  using NodeType = detail::SkipListNode<T>;\n  using value_type = T;\n  using key_type = T;\n\n  using iterator = detail::csl_iterator<value_type, NodeType>;\n  using const_iterator = detail::csl_iterator<const value_type, NodeType>;\n\n  class Accessor;\n  class Skipper;\n\n  explicit ConcurrentSkipList(int height, const NodeAlloc& alloc)\n      : recycler_(alloc),\n        head_(NodeType::create(recycler_.alloc(), height, value_type(), true)) {\n  }\n\n  explicit ConcurrentSkipList(int height)\n      : recycler_(),\n        head_(NodeType::create(recycler_.alloc(), height, value_type(), true)) {\n  }\n\n  // Convenience function to get an Accessor to a new instance.\n  static Accessor create(int height, const NodeAlloc& alloc) {\n    return Accessor(createInstance(height, alloc));\n  }\n\n  static Accessor create(int height = 1) {\n    return Accessor(createInstance(height));\n  }\n\n  // Create a shared_ptr skiplist object with initial head height.\n  static std::shared_ptr<SkipListType> createInstance(\n      int height, const NodeAlloc& alloc) {\n    return std::make_shared<ConcurrentSkipList>(height, alloc);\n  }\n\n  static std::shared_ptr<SkipListType> createInstance(int height = 1) {\n    return std::make_shared<ConcurrentSkipList>(height);\n  }\n\n  size_t size() const { return size_.load(std::memory_order_relaxed); }\n  bool empty() const { return size() == 0; }\n\n  //===================================================================\n  // Below are implementation details.\n  // Please see ConcurrentSkipList::Accessor for stdlib-like APIs.\n  //===================================================================\n\n  ~ConcurrentSkipList() {\n    if constexpr (NodeType::template DestroyIsNoOp<NodeAlloc>::value) {\n      // Avoid traversing the list if using arena allocator.\n      return;\n    }\n    for (NodeType* current = head_.load(std::memory_order_relaxed); current;) {\n      NodeType* tmp = current->skip(0);\n      NodeType::destroy(recycler_.alloc(), current);\n      current = tmp;\n    }\n  }\n\n private:\n  static bool greater(const value_type& data, const NodeType* node) {\n    return node && Comp()(node->data(), data);\n  }\n\n  static bool less(const value_type& data, const NodeType* node) {\n    return (node == nullptr) || Comp()(data, node->data());\n  }\n\n  static int findInsertionPoint(\n      NodeType* cur,\n      int cur_layer,\n      const value_type& data,\n      NodeType* preds[],\n      NodeType* succs[]) {\n    int foundLayer = -1;\n    NodeType* pred = cur;\n    NodeType* foundNode = nullptr;\n    for (int layer = cur_layer; layer >= 0; --layer) {\n      NodeType* node = pred->skip(layer);\n      while (greater(data, node)) {\n        pred = node;\n        node = node->skip(layer);\n      }\n      if (foundLayer == -1 && !less(data, node)) { // the two keys equal\n        foundLayer = layer;\n        foundNode = node;\n      }\n      preds[layer] = pred;\n\n      // if found, succs[0..foundLayer] need to point to the cached foundNode,\n      // as foundNode might be deleted at the same time thus pred->skip() can\n      // return nullptr or another node.\n      succs[layer] = foundNode ? foundNode : node;\n    }\n    return foundLayer;\n  }\n\n  int height() const { return head_.load(std::memory_order_acquire)->height(); }\n\n  int maxLayer() const { return height() - 1; }\n\n  size_t incrementSize(int delta) {\n    return size_.fetch_add(delta, std::memory_order_relaxed) + delta;\n  }\n\n  // Returns the node if found, nullptr otherwise.\n  NodeType* find(const value_type& data) {\n    auto ret = findNode(data);\n    if (ret.second && !ret.first->markedForRemoval()) {\n      return ret.first;\n    }\n    return nullptr;\n  }\n\n  // lock all the necessary nodes for changing (adding or removing) the list.\n  // returns true if all the lock acquired successfully and the related nodes\n  // are all validate (not in certain pending states), false otherwise.\n  bool lockNodesForChange(\n      int nodeHeight,\n      ScopedLocker guards[MAX_HEIGHT],\n      NodeType* preds[MAX_HEIGHT],\n      NodeType* succs[MAX_HEIGHT],\n      bool adding = true) {\n    NodeType *pred, *succ, *prevPred = nullptr;\n    bool valid = true;\n    for (int layer = 0; valid && layer < nodeHeight; ++layer) {\n      pred = preds[layer];\n      DCHECK(pred != nullptr)\n          << \"layer=\" << layer << \" height=\" << height()\n          << \" nodeheight=\" << nodeHeight;\n      succ = succs[layer];\n      if (pred != prevPred) {\n        guards[layer] = pred->acquireGuard();\n        prevPred = pred;\n      }\n      valid = !pred->markedForRemoval() &&\n          pred->skip(layer) == succ; // check again after locking\n\n      if (adding) { // when adding a node, the succ shouldn't be going away\n        valid = valid && (succ == nullptr || !succ->markedForRemoval());\n      }\n    }\n\n    return valid;\n  }\n\n  // Returns a paired value:\n  //   pair.first always stores the pointer to the node with the same input key.\n  //     It could be either the newly added data, or the existed data in the\n  //     list with the same key.\n  //   pair.second stores whether the data is added successfully:\n  //     0 means not added, otherwise returns the new size.\n  template <typename U>\n  std::pair<NodeType*, size_t> addOrGetData(U&& data) {\n    NodeType *preds[MAX_HEIGHT], *succs[MAX_HEIGHT];\n    NodeType* newNode;\n    size_t newSize;\n    while (true) {\n      int max_layer = 0;\n      int layer = findInsertionPointGetMaxLayer(data, preds, succs, &max_layer);\n\n      if (layer >= 0) {\n        NodeType* nodeFound = succs[layer];\n        DCHECK(nodeFound != nullptr);\n        if (nodeFound->markedForRemoval()) {\n          continue; // if it's getting deleted retry finding node.\n        }\n        // wait until fully linked.\n        while (FOLLY_UNLIKELY(!nodeFound->fullyLinked())) {\n        }\n        return std::make_pair(nodeFound, 0);\n      }\n\n      // need to capped at the original height -- the real height may have grown\n      int nodeHeight =\n          detail::SkipListRandomHeight::instance()->getHeight(max_layer + 1);\n\n      ScopedLocker guards[MAX_HEIGHT];\n      if (!lockNodesForChange(nodeHeight, guards, preds, succs)) {\n        continue; // give up the locks and retry until all valid\n      }\n\n      // locks acquired and all valid, need to modify the links under the locks.\n      newNode = NodeType::create(\n          recycler_.alloc(), nodeHeight, std::forward<U>(data));\n      for (int k = 0; k < nodeHeight; ++k) {\n        newNode->setSkip(k, succs[k]);\n        preds[k]->setSkip(k, newNode);\n      }\n\n      newNode->setFullyLinked();\n      newSize = incrementSize(1);\n      break;\n    }\n\n    int hgt = height();\n    size_t sizeLimit =\n        detail::SkipListRandomHeight::instance()->getSizeLimit(hgt);\n\n    if (hgt < MAX_HEIGHT && newSize > sizeLimit) {\n      growHeight(hgt + 1);\n    }\n    CHECK_GT(newSize, 0);\n    return std::make_pair(newNode, newSize);\n  }\n\n  bool remove(const value_type& data) {\n    NodeType* nodeToDelete = nullptr;\n    ScopedLocker nodeGuard;\n    bool isMarked = false;\n    int nodeHeight = 0;\n    NodeType *preds[MAX_HEIGHT], *succs[MAX_HEIGHT];\n\n    while (true) {\n      int max_layer = 0;\n      int layer = findInsertionPointGetMaxLayer(data, preds, succs, &max_layer);\n      if (!isMarked && (layer < 0 || !okToDelete(succs[layer], layer))) {\n        return false;\n      }\n\n      if (!isMarked) {\n        nodeToDelete = succs[layer];\n        nodeHeight = nodeToDelete->height();\n        nodeGuard = nodeToDelete->acquireGuard();\n        if (nodeToDelete->markedForRemoval()) {\n          return false;\n        }\n        nodeToDelete->setMarkedForRemoval();\n        isMarked = true;\n      }\n\n      // acquire pred locks from bottom layer up\n      ScopedLocker guards[MAX_HEIGHT];\n      if (!lockNodesForChange(nodeHeight, guards, preds, succs, false)) {\n        continue; // this will unlock all the locks\n      }\n\n      for (int k = nodeHeight - 1; k >= 0; --k) {\n        preds[k]->setSkip(k, nodeToDelete->skip(k));\n      }\n\n      incrementSize(-1);\n      break;\n    }\n    recycle(nodeToDelete);\n    return true;\n  }\n\n  const value_type* first() const {\n    auto node = head_.load(std::memory_order_acquire)->skip(0);\n    return node ? &node->data() : nullptr;\n  }\n\n  const value_type* last() const {\n    NodeType* pred = head_.load(std::memory_order_acquire);\n    NodeType* node = nullptr;\n    for (int layer = maxLayer(); layer >= 0; --layer) {\n      do {\n        node = pred->skip(layer);\n        if (node) {\n          pred = node;\n        }\n      } while (node != nullptr);\n    }\n    return pred == head_.load(std::memory_order_relaxed)\n        ? nullptr\n        : &pred->data();\n  }\n\n  static bool okToDelete(NodeType* candidate, int layer) {\n    DCHECK(candidate != nullptr);\n    return candidate->fullyLinked() && candidate->maxLayer() == layer &&\n        !candidate->markedForRemoval();\n  }\n\n  // find node for insertion/deleting\n  int findInsertionPointGetMaxLayer(\n      const value_type& data,\n      NodeType* preds[],\n      NodeType* succs[],\n      int* max_layer) const {\n    *max_layer = maxLayer();\n    return findInsertionPoint(\n        head_.load(std::memory_order_acquire), *max_layer, data, preds, succs);\n  }\n\n  // Find node for access. Returns a paired values:\n  // pair.first = the first node that no-less than data value\n  // pair.second = 1 when the data value is founded, or 0 otherwise.\n  // This is like lower_bound, but not exact: we could have the node marked for\n  // removal so still need to check that.\n  std::pair<NodeType*, int> findNode(const value_type& data) const {\n    return findNodeDownRight(data);\n  }\n\n  // Find node by first stepping down then stepping right. Based on benchmark\n  // results, this is slightly faster than findNodeRightDown for better\n  // locality on the skipping pointers.\n  std::pair<NodeType*, int> findNodeDownRight(const value_type& data) const {\n    NodeType* pred = head_.load(std::memory_order_acquire);\n    int ht = pred->height();\n    NodeType* node = nullptr;\n\n    bool found = false;\n    while (!found) {\n      // stepping down\n      for (; ht > 0 && less(data, node = pred->skip(ht - 1)); --ht) {\n      }\n      if (ht == 0) {\n        return std::make_pair(node, 0); // not found\n      }\n      // node <= data now, but we need to fix up ht\n      --ht;\n\n      // stepping right\n      while (greater(data, node)) {\n        pred = node;\n        node = node->skip(ht);\n      }\n      found = !less(data, node);\n    }\n    return std::make_pair(node, found);\n  }\n\n  // find node by first stepping right then stepping down.\n  // We still keep this for reference purposes.\n  std::pair<NodeType*, int> findNodeRightDown(const value_type& data) const {\n    NodeType* pred = head_.load(std::memory_order_acquire);\n    NodeType* node = nullptr;\n    auto top = maxLayer();\n    int found = 0;\n    for (int layer = top; !found && layer >= 0; --layer) {\n      node = pred->skip(layer);\n      while (greater(data, node)) {\n        pred = node;\n        node = node->skip(layer);\n      }\n      found = !less(data, node);\n    }\n    return std::make_pair(node, found);\n  }\n\n  NodeType* lower_bound(const value_type& data) const {\n    auto node = findNode(data).first;\n    while (node != nullptr && node->markedForRemoval()) {\n      node = node->skip(0);\n    }\n    return node;\n  }\n\n  void growHeight(int height) {\n    NodeType* oldHead = head_.load(std::memory_order_acquire);\n    if (oldHead->height() >= height) { // someone else already did this\n      return;\n    }\n\n    NodeType* newHead =\n        NodeType::create(recycler_.alloc(), height, value_type(), true);\n\n    { // need to guard the head node in case others are adding/removing\n      // nodes linked to the head.\n      ScopedLocker g = oldHead->acquireGuard();\n      newHead->copyHead(oldHead);\n      NodeType* expected = oldHead;\n      if (!head_.compare_exchange_strong(\n              expected, newHead, std::memory_order_release)) {\n        // if someone has already done the swap, just return.\n        NodeType::destroy(recycler_.alloc(), newHead);\n        return;\n      }\n      oldHead->setMarkedForRemoval();\n    }\n    recycle(oldHead);\n  }\n\n  void recycle(NodeType* node) { recycler_.add(node); }\n\n  detail::NodeRecycler<NodeType, NodeAlloc> recycler_;\n  std::atomic<NodeType*> head_;\n  std::atomic<size_t> size_{0};\n};\n\ntemplate <typename T, typename Comp, typename NodeAlloc, int MAX_HEIGHT>\nclass ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Accessor {\n  using NodeType = detail::SkipListNode<T>;\n  using SkipListType = ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>;\n\n public:\n  using value_type = T;\n  using key_type = T;\n  using reference = T&;\n  using pointer = T*;\n  using const_reference = const T&;\n  using const_pointer = const T*;\n  using size_type = size_t;\n  using key_compare = Comp;\n  using value_compare = Comp;\n\n  using iterator = typename SkipListType::iterator;\n  using const_iterator = typename SkipListType::const_iterator;\n  using Skipper = typename SkipListType::Skipper;\n\n  explicit Accessor(std::shared_ptr<ConcurrentSkipList> skip_list)\n      : slHolder_(std::move(skip_list)) {\n    sl_ = slHolder_.get();\n    DCHECK(sl_ != nullptr);\n    sl_->recycler_.addRef();\n  }\n\n  // Unsafe initializer: the caller assumes the responsibility to keep\n  // skip_list valid during the whole life cycle of the Accessor.\n  explicit Accessor(ConcurrentSkipList* skip_list) : sl_(skip_list) {\n    DCHECK(sl_ != nullptr);\n    sl_->recycler_.addRef();\n  }\n\n  Accessor(const Accessor& accessor)\n      : sl_(accessor.sl_), slHolder_(accessor.slHolder_) {\n    sl_->recycler_.addRef();\n  }\n\n  Accessor& operator=(const Accessor& accessor) {\n    if (this != &accessor) {\n      slHolder_ = accessor.slHolder_;\n      sl_->recycler_.releaseRef();\n      sl_ = accessor.sl_;\n      sl_->recycler_.addRef();\n    }\n    return *this;\n  }\n\n  ~Accessor() { sl_->recycler_.releaseRef(); }\n\n  bool empty() const { return sl_->size() == 0; }\n  size_t size() const { return sl_->size(); }\n  size_type max_size() const { return std::numeric_limits<size_type>::max(); }\n\n  // returns end() if the value is not in the list, otherwise returns an\n  // iterator pointing to the data, and it's guaranteed that the data is valid\n  // as far as the Accessor is hold.\n  iterator find(const key_type& value) { return iterator(sl_->find(value)); }\n  const_iterator find(const key_type& value) const {\n    return iterator(sl_->find(value));\n  }\n  size_type count(const key_type& data) const { return contains(data); }\n\n  iterator begin() const {\n    NodeType* head = sl_->head_.load(std::memory_order_acquire);\n    return iterator(head->next());\n  }\n  iterator end() const { return iterator(nullptr); }\n  const_iterator cbegin() const { return begin(); }\n  const_iterator cend() const { return end(); }\n\n  template <\n      typename U,\n      typename =\n          typename std::enable_if<std::is_convertible<U, T>::value>::type>\n  std::pair<iterator, bool> insert(U&& data) {\n    auto ret = sl_->addOrGetData(std::forward<U>(data));\n    return std::make_pair(iterator(ret.first), ret.second);\n  }\n  size_t erase(const key_type& data) { return remove(data); }\n\n  iterator lower_bound(const key_type& data) const {\n    return iterator(sl_->lower_bound(data));\n  }\n\n  size_t height() const { return sl_->height(); }\n\n  // first() returns pointer to the first element in the skiplist, or\n  // nullptr if empty.\n  //\n  // last() returns the pointer to the last element in the skiplist,\n  // nullptr if list is empty.\n  //\n  // Note: As concurrent writing can happen, first() is not\n  //   guaranteed to be the min_element() in the list. Similarly\n  //   last() is not guaranteed to be the max_element(), and both of them can\n  //   be invalid (i.e. nullptr), so we name them differently from front() and\n  //   tail() here.\n  const key_type* first() const { return sl_->first(); }\n  const key_type* last() const { return sl_->last(); }\n\n  // Try to remove the last element in the skip list.\n  //\n  // Returns true if we removed it, false if either the list is empty\n  // or a race condition happened (i.e. the used-to-be last element\n  // was already removed by another thread).\n  bool pop_back() {\n    auto last = sl_->last();\n    return last ? sl_->remove(*last) : false;\n  }\n\n  std::pair<key_type*, bool> addOrGetData(const key_type& data) {\n    auto ret = sl_->addOrGetData(data);\n    return std::make_pair(&ret.first->data(), ret.second);\n  }\n\n  SkipListType* skiplist() const { return sl_; }\n\n  // legacy interfaces\n  // TODO:(xliu) remove these.\n  // Returns true if the node is added successfully, false if not, i.e. the\n  // node with the same key already existed in the list.\n  bool contains(const key_type& data) const { return sl_->find(data); }\n  bool add(const key_type& data) { return sl_->addOrGetData(data).second; }\n  bool remove(const key_type& data) { return sl_->remove(data); }\n\n private:\n  SkipListType* sl_;\n  std::shared_ptr<SkipListType> slHolder_;\n};\n\n// implements forward iterator concept.\ntemplate <typename ValT, typename NodeT>\nclass detail::csl_iterator\n    : public detail::IteratorFacade<\n          csl_iterator<ValT, NodeT>,\n          ValT,\n          std::forward_iterator_tag> {\n public:\n  using value_type = ValT;\n  using reference = value_type&;\n  using pointer = value_type*;\n  using difference_type = ptrdiff_t;\n\n  explicit csl_iterator(NodeT* node = nullptr) : node_(node) {}\n\n  template <typename OtherVal, typename OtherNode>\n  csl_iterator(\n      const csl_iterator<OtherVal, OtherNode>& other,\n      typename std::enable_if<\n          std::is_convertible<OtherVal*, ValT*>::value>::type* = nullptr)\n      : node_(other.node_) {}\n\n  size_t nodeSize() const {\n    return node_ == nullptr\n        ? 0\n        : node_->height() * sizeof(NodeT*) + sizeof(*this);\n  }\n\n  bool good() const { return node_ != nullptr; }\n\n private:\n  template <class, class>\n  friend class csl_iterator;\n  friend class detail::\n      IteratorFacade<csl_iterator, ValT, std::forward_iterator_tag>;\n\n  void increment() { node_ = node_->next(); }\n  bool equal(const csl_iterator& other) const { return node_ == other.node_; }\n  value_type& dereference() const { return node_->data(); }\n\n  NodeT* node_;\n};\n\n// Skipper interface\ntemplate <typename T, typename Comp, typename NodeAlloc, int MAX_HEIGHT>\nclass ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>::Skipper {\n  using NodeType = detail::SkipListNode<T>;\n  using SkipListType = ConcurrentSkipList<T, Comp, NodeAlloc, MAX_HEIGHT>;\n  using Accessor = typename SkipListType::Accessor;\n\n public:\n  using value_type = T;\n  using reference = T&;\n  using pointer = T*;\n  using difference_type = ptrdiff_t;\n\n  Skipper(std::shared_ptr<SkipListType> skipList)\n      : accessor_(std::move(skipList)) {\n    init();\n  }\n\n  Skipper(const Accessor& accessor) : accessor_(accessor) { init(); }\n\n  void init() {\n    // need to cache the head node\n    NodeType* head_node = head();\n    headHeight_ = head_node->height();\n    for (int i = 0; i < headHeight_; ++i) {\n      preds_[i] = head_node;\n      succs_[i] = head_node->skip(i);\n    }\n    int max_layer = maxLayer();\n    for (int i = 0; i < max_layer; ++i) {\n      hints_[i] = uint8_t(i + 1);\n    }\n    hints_[max_layer] = max_layer;\n  }\n\n  // advance to the next node in the list.\n  Skipper& operator++() {\n    preds_[0] = succs_[0];\n    succs_[0] = preds_[0]->skip(0);\n    int height = curHeight();\n    for (int i = 1; i < height && preds_[0] == succs_[i]; ++i) {\n      preds_[i] = succs_[i];\n      succs_[i] = preds_[i]->skip(i);\n    }\n    return *this;\n  }\n\n  Accessor& accessor() { return accessor_; }\n  const Accessor& accessor() const { return accessor_; }\n\n  bool good() const { return succs_[0] != nullptr; }\n\n  int maxLayer() const { return headHeight_ - 1; }\n\n  int curHeight() const {\n    // need to cap the height to the cached head height, as the current node\n    // might be some newly inserted node and also during the time period the\n    // head height may have grown.\n    return succs_[0] ? std::min(headHeight_, succs_[0]->height()) : 0;\n  }\n\n  const value_type& data() const {\n    DCHECK(succs_[0] != nullptr);\n    return succs_[0]->data();\n  }\n\n  value_type& operator*() const {\n    DCHECK(succs_[0] != nullptr);\n    return succs_[0]->data();\n  }\n\n  value_type* operator->() const {\n    DCHECK(succs_[0] != nullptr);\n    return &succs_[0]->data();\n  }\n\n  /*\n   * Skip to the position whose data is no less than the parameter.\n   * (I.e. the lower_bound).\n   *\n   * Returns true if the data is found, false otherwise.\n   */\n  bool to(const value_type& data) {\n    int layer = curHeight() - 1;\n    if (layer < 0) {\n      return false; // reaches the end of the list\n    }\n\n    int lyr = hints_[layer];\n    int max_layer = maxLayer();\n    while (SkipListType::greater(data, succs_[lyr]) && lyr < max_layer) {\n      ++lyr;\n    }\n    hints_[layer] = lyr; // update the hint\n\n    int foundLayer = SkipListType::findInsertionPoint(\n        preds_[lyr], lyr, data, preds_, succs_);\n    if (foundLayer < 0) {\n      return false;\n    }\n\n    DCHECK(succs_[0] != nullptr)\n        << \"lyr=\" << lyr << \"; max_layer=\" << max_layer;\n    return !succs_[0]->markedForRemoval();\n  }\n\n private:\n  NodeType* head() const {\n    return accessor_.skiplist()->head_.load(std::memory_order_acquire);\n  }\n\n  Accessor accessor_;\n  int headHeight_;\n  NodeType *succs_[MAX_HEIGHT], *preds_[MAX_HEIGHT];\n  uint8_t hints_[MAX_HEIGHT];\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ConstexprMath.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <limits>\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/lang/CheckedMath.h>\n#include <folly/portability/Constexpr.h>\n\nnamespace folly {\n\n/// numbers\n///\n/// mimic: std::numbers, C++20 (partial)\nnamespace numbers {\n\nnamespace detail {\ntemplate <typename T>\nusing enable_if_floating_t =\n    std::enable_if_t<std::is_floating_point<T>::value, T>;\n}\n\n/// e_v\n///\n/// mimic: std::numbers::e_v, C++20\ntemplate <typename T>\ninline constexpr T e_v = detail::enable_if_floating_t<T>(\n    2.71828182845904523536028747135266249775724709369995L);\n\n/// ln2_v\n///\n/// mimic: std::numbers::ln2_v, C++20\ntemplate <typename T>\ninline constexpr T ln2_v = detail::enable_if_floating_t<T>(\n    0.69314718055994530941723212145817656807550013436025L);\n\n/// e\n///\n/// mimic: std::numbers::e, C++20\ninline constexpr double e = e_v<double>;\n\n/// ln2\n///\n/// mimic: std::numbers::ln2, C++20\ninline constexpr double ln2 = ln2_v<double>;\n\n} // namespace numbers\n\n/// floating_point_integral_constant\n///\n/// Like std::integral_constant but for floating-point types holding integral\n/// values representable in an integral type.\ntemplate <typename T, typename S, S Value>\nstruct floating_point_integral_constant {\n  using value_type = T;\n  static constexpr value_type value = static_cast<value_type>(Value);\n  constexpr operator value_type() const noexcept { return value; }\n  constexpr value_type operator()() const noexcept { return value; }\n};\n\n//  ----\n\nnamespace detail {\n\ntemplate <typename T>\nconstexpr size_t constexpr_iterated_squares_desc_size_(T const base) {\n  using lim = std::numeric_limits<T>;\n  size_t s = 1;\n  auto r = base;\n  while (r <= lim::max() / r) {\n    ++s;\n    r *= r;\n  }\n  return s;\n}\n\n} // namespace detail\n\n/// constexpr_iterated_squares_desc_size_v\n///\n/// Effectively calculates: floor(log(max_exponent)/log(base))\n///\n/// For use with constexpr_iterated_squares_desc below.\ntemplate <typename Base>\ninline constexpr size_t constexpr_iterated_squares_desc_size_v =\n    detail::constexpr_iterated_squares_desc_size_(Base::value);\n\n/// constexpr_iterated_squares_desc\n///\n/// A constexpr scaling array of integer powers-of-powers-of-two, descending,\n/// with the associated powers-of-two.\n///\n/// scaling = [..., {8, b^8}, {4, b^4}, {2, b^2}, {1, b^1}] for b = base\n///\n/// Includes select constexpr scaling algorithms based on the scaling array.\n///\n/// The scaling array and the scaling algorithms are general-purpose, if niche.\n/// They may be used by other constexpr math functions (floating-point) either\n/// to improve runtime performance or to improve numerical approximations.\n///\n/// Some compilers fail to support passing some types as non-type template\n/// params. In particular, long double is not universally supported. Therefore,\n/// this utility takes its base as a type rather than as a value. For floating-\n/// point integral bases, that is, bases of floating-point type but of integral\n/// value, floating_point_integral_constant is the easiest parameterization.\ntemplate <typename T, std::size_t Size>\nstruct constexpr_iterated_squares_desc {\n  static_assert(Size > 0, \"requires non-zero size\");\n\n  using size_type = decltype(Size);\n  using base_type = T;\n\n  struct item_type {\n    size_type power;\n    base_type scale;\n  };\n\n  static constexpr size_type size = Size;\n  base_type base;\n  item_type scaling[size];\n\n private:\n  using lim = std::numeric_limits<base_type>;\n\n  static_assert(\n      lim::max_exponent < std::numeric_limits<size_type>::max(),\n      \"size_type too small for base_type\");\n\n public:\n  explicit constexpr constexpr_iterated_squares_desc(base_type r) noexcept\n      : base{r}, scaling{} {\n    assert(size <= detail::constexpr_iterated_squares_desc_size_(base));\n    size_type i = 0;\n    size_type p = 1;\n    while (true) { // a for-loop might cause multiplication overflow below\n      scaling[size - 1 - i] = {p, r};\n      if (++i == size) {\n        break;\n      }\n      p *= 2;\n      r *= r;\n    }\n  }\n\n  /// shrink\n  ///\n  /// Returns scaling params of the form:\n  ///   item_type{power, scale} with scale = base ^ power\n  /// With power the smallest nonnegative integer such that:\n  ///   abs(num) / scale <= max\n  constexpr item_type shrink(base_type const num, base_type const max) const {\n    assert(max > base_type(0));\n    auto const rmax = max / base;\n    auto const snum = num < base_type(0) ? -num : num;\n    auto power = size_type(0);\n    auto scale = base_type(1);\n    if (!(snum / scale <= max)) {\n      for (auto const& i : scaling) {\n        auto const next = scale * i.scale;\n        auto const div = snum / next;\n        if (div <= rmax) {\n          continue;\n        }\n        power += i.power;\n        scale = next;\n        if (div <= max) {\n          break;\n        }\n      }\n    }\n    assert(snum / scale <= max);\n    return {power, scale};\n  }\n\n  /// growth\n  ///\n  /// Returns scaling params of the form:\n  ///   item_type{power, scale} with scale = base ^ power\n  /// With power the smallest nonnegative integer such that:\n  ///   abs(num) * scale >= min\n  constexpr item_type growth(base_type const num, base_type const min) const {\n    assert(min > base_type(0));\n    auto const rmin = min * base;\n    auto const snum = num < base_type(0) ? -num : num;\n    auto power = size_type(0);\n    auto scale = base_type(1);\n    if (!(snum * scale >= min)) {\n      for (auto const& i : scaling) {\n        auto const next = scale * i.scale;\n        auto const mul = snum * next;\n        if (mul >= rmin) {\n          continue;\n        }\n        power += i.power;\n        scale = next;\n        if (mul >= min) {\n          break;\n        }\n      }\n    }\n    assert(snum * scale >= min);\n    return {power, scale};\n  }\n};\n\n/// constexpr_iterated_squares_desc_v\n///\n/// An instance of constexpr_iterated_squares_desc of max size with the given\n/// base.\ntemplate <typename Base>\ninline constexpr auto constexpr_iterated_squares_desc_v =\n    constexpr_iterated_squares_desc<\n        typename Base::value_type,\n        constexpr_iterated_squares_desc_size_v<Base>>{Base::value};\n\n/// constexpr_iterated_squares_desc_2_v\n///\n/// An alias for constexpr_iterated_squares_desc_v with base 2, which is the\n/// most common base to use with iterated-squares.\ntemplate <typename T>\nconstexpr auto& constexpr_iterated_squares_desc_2_v =\n    constexpr_iterated_squares_desc_v<\n        floating_point_integral_constant<T, int, 2>>;\n\n// TLDR: Prefer using operator< for ordering. And when\n// a and b are equivalent objects, we return b to make\n// sorting stable.\n// See http://stepanovpapers.com/notes.pdf for details.\ntemplate <typename T, typename... Ts>\nconstexpr T constexpr_max(T a, Ts... ts) {\n  T list[] = {ts..., a}; // 0-length arrays are illegal\n  for (auto i = 0u; i < sizeof...(Ts); ++i) {\n    a = list[i] < a ? a : list[i];\n  }\n  return a;\n}\n\n// When a and b are equivalent objects, we return a to\n// make sorting stable.\ntemplate <typename T, typename... Ts>\nconstexpr T constexpr_min(T a, Ts... ts) {\n  T list[] = {ts..., a}; // 0-length arrays are illegal\n  for (auto i = 0u; i < sizeof...(Ts); ++i) {\n    a = list[i] < a ? list[i] : a;\n  }\n  return a;\n}\n\ntemplate <typename T, typename Less>\nconstexpr T const& constexpr_clamp(\n    T const& v, T const& lo, T const& hi, Less less) {\n  T const& a = less(v, lo) ? lo : v;\n  T const& b = less(hi, a) ? hi : a;\n  return b;\n}\ntemplate <typename T>\nconstexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) {\n  return constexpr_clamp(v, lo, hi, std::less<T>{});\n}\n\ntemplate <typename T>\nconstexpr bool constexpr_isnan(T const t) {\n  return t != t; // NOLINT\n}\n\nnamespace detail {\n\ntemplate <typename T, typename = void>\nstruct constexpr_abs_helper {};\n\ntemplate <typename T>\nstruct constexpr_abs_helper<\n    T,\n    typename std::enable_if<std::is_floating_point<T>::value>::type> {\n  static constexpr T go(T t) { return t < static_cast<T>(0) ? -t : t; }\n};\n\ntemplate <typename T>\nstruct constexpr_abs_helper<\n    T,\n    typename std::enable_if<\n        std::is_integral<T>::value && !std::is_same<T, bool>::value &&\n        std::is_unsigned<T>::value>::type> {\n  static constexpr T go(T t) { return t; }\n};\n\ntemplate <typename T>\nstruct constexpr_abs_helper<\n    T,\n    typename std::enable_if<\n        std::is_integral<T>::value && !std::is_same<T, bool>::value &&\n        std::is_signed<T>::value>::type> {\n  static constexpr typename std::make_unsigned<T>::type go(T t) {\n    return typename std::make_unsigned<T>::type(t < static_cast<T>(0) ? -t : t);\n  }\n};\n\n} // namespace detail\n\ntemplate <typename T>\nconstexpr auto constexpr_abs(T t)\n    -> decltype(detail::constexpr_abs_helper<T>::go(t)) {\n  return detail::constexpr_abs_helper<T>::go(t);\n}\n\nnamespace detail {\n\ntemplate <typename T>\nconstexpr T constexpr_log2_(T a, T e) {\n  return e == T(1) ? a : constexpr_log2_(a + T(1), e / T(2));\n}\n\ntemplate <typename T>\nconstexpr T constexpr_log2_ceil_(T l2, T t) {\n  return l2 + T(T(1) << l2 < t ? 1 : 0);\n}\n\n} // namespace detail\n\ntemplate <typename T>\nconstexpr T constexpr_log2(T t) {\n  return detail::constexpr_log2_(T(0), t);\n}\n\ntemplate <typename T>\nconstexpr T constexpr_log2_ceil(T t) {\n  return detail::constexpr_log2_ceil_(constexpr_log2(t), t);\n}\n\n/// constexpr_trunc\n///\n/// mimic: std::trunc (C++23)\ntemplate <\n    typename T,\n    std::enable_if_t<std::is_floating_point<T>::value, int> = 0>\nconstexpr T constexpr_trunc(T const t) {\n  using lim = std::numeric_limits<T>;\n  using int_type = std::uintmax_t;\n  using int_lim = std::numeric_limits<int_type>;\n  static_assert(lim::radix == 2, \"non-binary radix\");\n  static_assert(lim::digits <= int_lim::digits, \"overwide mantissa\");\n  constexpr auto bound = static_cast<T>(std::uintmax_t(1) << (lim::digits - 1));\n  auto const neg = !constexpr_isnan(t) && t < T(0);\n  auto const s = neg ? -t : t;\n  if (constexpr_isnan(t) || t == T(0) || !(s < bound)) {\n    return t;\n  }\n  if (s < T(1)) {\n    return neg ? -T(0) : T(0);\n  }\n  auto const r = static_cast<T>(static_cast<int_type>(s));\n  return neg ? -r : r;\n}\n\ntemplate <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>\nconstexpr T constexpr_trunc(T const t) {\n  return t;\n}\n\n/// constexpr_round\n///\n/// mimic: std::round (C++23)\ntemplate <typename T>\nconstexpr T constexpr_round(T const t) {\n  constexpr auto half = T(1) / T(2);\n  auto const same = constexpr_isnan(t) || t == T(0);\n  return same ? t : constexpr_trunc(t < T(0) ? t - half : t + half);\n}\n\n/// constexpr_floor\n///\n/// mimic: std::floor (C++23)\ntemplate <typename T>\nconstexpr T constexpr_floor(T const t) {\n  auto const s = constexpr_trunc(t);\n  return t < s ? s - T(1) : s;\n}\n\n/// constexpr_ceil\n///\n/// mimic: std::ceil (C++23)\ntemplate <typename T>\nconstexpr T constexpr_ceil(T const t) {\n  auto const s = constexpr_trunc(t);\n  return s < t ? s + T(1) : s;\n}\n\n/// constexpr_ceil\n///\n/// The least integer at least t that round divides.\ntemplate <typename T>\nconstexpr T constexpr_ceil(T t, T round) {\n  return round == T(0)\n      ? t\n      : ((t + (t <= T(0) ? T(0) : round - T(1))) / round) * round;\n}\n\n/// constexpr_mult\n///\n/// Multiply two values, allowing for constexpr floating-point overflow to\n/// infinity.\ntemplate <typename T>\nconstexpr T constexpr_mult(T const a, T const b) {\n  using lim = std::numeric_limits<T>;\n  if (constexpr_isnan(a) || constexpr_isnan(b)) {\n    return constexpr_isnan(a) ? a : b;\n  }\n  if (std::is_floating_point<T>::value) {\n    constexpr auto inf = lim::infinity();\n    auto const ax = constexpr_abs(a);\n    auto const bx = constexpr_abs(b);\n    if ((ax == T(0) && bx == inf) || (bx == T(0) && ax == inf)) {\n      return lim::quiet_NaN();\n    }\n    // floating-point multiplication overflow, ie where multiplication of two\n    // finite values overflows to infinity of either sign, is not constexpr per\n    // gcc\n    // floating-point division overflow, ie where division of two finite values\n    // overflows to infinity of either sign, is not constexpr per gcc\n    // floating-point division by zero is not constexpr per any compiler, but we\n    // use it in the checks for the other two conditions\n    if (ax != inf && bx != inf && T(1) < bx && lim::max() / bx < ax) {\n      auto const a_neg = static_cast<bool>(a < T(0));\n      auto const b_neg = static_cast<bool>(b < T(0));\n      auto const sign = a_neg == b_neg ? T(1) : T(-1);\n      return sign * inf;\n    }\n  }\n  return a * b;\n}\n\nnamespace detail {\n\ntemplate <\n    typename T,\n    typename E,\n    std::enable_if_t<std::is_signed<E>::value, int> = 1>\nconstexpr T constexpr_ipow(T const base, E const exp) {\n  if (std::is_floating_point<T>::value) {\n    if (exp < E(0)) {\n      return T(1) / constexpr_ipow(base, -exp);\n    }\n    if (exp == E(0)) {\n      return T(1);\n    }\n    if (constexpr_isnan(base)) {\n      return base;\n    }\n  }\n  assert(!(exp < E(0)) && \"negative exponent with integral base\");\n  if (exp == E(0)) {\n    return T(1);\n  }\n  if (exp == E(1)) {\n    return base;\n  }\n  auto const hexp = constexpr_trunc(exp / E(2));\n  auto const div = constexpr_ipow(base, hexp);\n  auto const rem = hexp * E(2) == exp ? T(1) : base;\n  return constexpr_mult(constexpr_mult(div, div), rem);\n}\n\ntemplate <\n    typename T,\n    typename E,\n    std::enable_if_t<std::is_unsigned<E>::value, int> = 1>\nconstexpr T constexpr_ipow(T const base, E const exp) {\n  if (std::is_floating_point<T>::value) {\n    if (exp == E(0)) {\n      return T(1);\n    }\n    if (constexpr_isnan(base)) {\n      return base;\n    }\n  }\n  if (exp == E(0)) {\n    return T(1);\n  }\n  if (exp == E(1)) {\n    return base;\n  }\n  auto const hexp = constexpr_trunc(exp / E(2));\n  auto const div = constexpr_ipow(base, hexp);\n  auto const rem = hexp * E(2) == exp ? T(1) : base;\n  return constexpr_mult(constexpr_mult(div, div), rem);\n}\n\n} // namespace detail\n\n/// constexpr_exp\n///\n/// Calculates an approximation of the mathematical function exp(num). Usable in\n/// constant evaluations. Like std::exp, which becomes constexpr in C++26.\n///\n/// The integer overload uses iterated squaring and multiplication. The\n/// floating-point overload naively evaluates the taylor series of exp(num)\n/// until approximate convergence.\n///\n/// mimic: std::exp (C++23, C++26)\ntemplate <\n    typename T,\n    typename N,\n    std::enable_if_t<\n        std::is_floating_point<T>::value && std::is_integral<N>::value &&\n            !std::is_same<N, bool>::value,\n        int> = 0>\nconstexpr T constexpr_exp(N const power) {\n  auto const npower = constexpr_abs(power);\n  auto const result = detail::constexpr_ipow(numbers::e_v<T>, npower);\n  return power < N(0) ? T(1) / result : result;\n}\ntemplate <\n    typename N,\n    std::enable_if_t<\n        std::is_integral<N>::value && !std::is_same<N, bool>::value,\n        int> = 0>\nconstexpr double constexpr_exp(N const power) {\n  return constexpr_exp<double>(power);\n}\ntemplate <\n    typename T,\n    std::enable_if_t<std::is_floating_point<T>::value, int> = 0>\nconstexpr T constexpr_exp(T const power) {\n  using lim = std::numeric_limits<T>;\n\n  // edge cases\n  if (constexpr_isnan(power)) {\n    return power;\n  }\n  if (power == -lim::infinity()) {\n    return +T(0);\n  }\n  if (power == +lim::infinity()) {\n    return power;\n  }\n\n  // convergence works better with positive powers since signs do not alternate\n  auto const abspower = constexpr_abs(power);\n  // convergence must short-circuit when terms grow to floating-point infinity\n  auto const bound = T(1) < abspower ? lim::max() / abspower : lim::infinity();\n\n  // term #index = power * coeff\n  auto index = size_t(0);\n  auto term = T(1);\n  // result = sum of terms\n  auto result = T(1);\n  // sum the terms until ~convergence\n  while (!(constexpr_abs(term) < lim::epsilon())) {\n    if (bound < term) {\n      return power < T(0) ? T(0) : lim::infinity();\n    }\n    index += 1;\n    term = term * abspower / index;\n    result += term;\n  }\n  return power < T(0) ? T(1) / result : result;\n}\n\n/// constexpr_log\n///\n/// Calculates an approximation of the natural logarithm ln(num).\n///\n/// The implementation uses a quickly-converging, high-precision iterative\n/// technique as described in:\n///   https://en.wikipedia.org/wiki/Natural_logarithm#High_precision\n///\n/// The technique works best with numbers that are close enough to 1, so the\n/// implementation uses a quick shrink/growth technique as described in:\n///   https://en.wikipedia.org/wiki/Natural_logarithm#Efficient_computation\ntemplate <\n    typename T,\n    std::enable_if_t<std::is_floating_point<T>::value, int> = 0>\nconstexpr T constexpr_log(T const num) {\n  using lim = std::numeric_limits<T>;\n  constexpr auto& isq = constexpr_iterated_squares_desc_2_v<T>;\n\n  // edge cases\n  if (constexpr_isnan(num)) {\n    return num;\n  }\n  if (num < T(0)) {\n    return lim::quiet_NaN();\n  }\n  if (num == T(0)) {\n    return -lim::infinity();\n  }\n  if (num == lim::infinity()) {\n    return num;\n  }\n\n  // compression\n  auto const shrink = isq.shrink(num, isq.base);\n  auto const growth = isq.growth(num, T(1));\n  auto const scaled = num * growth.scale / shrink.scale;\n  assert(scaled <= isq.base);\n  assert(scaled >= T(1));\n\n  auto sum = T(0);\n  auto delta = T(2);\n  while (constexpr_abs(delta) >= lim::epsilon()) {\n    auto expterm = constexpr_exp(sum);\n    delta = T(2) * (scaled - expterm) / (scaled + expterm);\n    sum += delta;\n  }\n  auto const ln2 = numbers::ln2_v<T>;\n  return sum - growth.power * ln2 + shrink.power * ln2;\n}\n\n/// constexpr_pow\n///\n/// Calculates an approximation of the value of base raised to the exponent exp.\n///\n/// The implementation uses iterated squaring and multiplication for the integer\n/// part of the exponent and uses the identity x^y = exp(y * log(x)) for the\n/// fractional part of the exponent.\n///\n/// Notes:\n/// * Forbids base of +0 or -0 with finite non-positive exponent: in part since\n///   the plausible infinite result would be sensitive to the sign of the zero;\n///   and in part since std::pow would be required or permitted to raise error\n///   div-by-zero.\n/// * Forbids finite negative base with finite non-integer exponent: in part\n///   since std::pow would be required to raise error invalid.\n///\n/// mimic: std::pow (C++26)\ntemplate <\n    typename T,\n    typename E,\n    std::enable_if_t<\n        std::is_integral<E>::value && !std::is_same<E, bool>::value,\n        int> = 0>\nconstexpr T constexpr_pow(T const base, E const exp) {\n  return detail::constexpr_ipow(base, exp);\n}\ntemplate <\n    typename T,\n    std::enable_if_t<std::is_floating_point<T>::value, int> = 0>\nconstexpr T constexpr_pow(T const base, T const exp) {\n  using lim = std::numeric_limits<T>;\n\n  // edge cases\n  if (exp == T(0)) {\n    return T(1);\n  }\n  if (constexpr_isnan(base)) {\n    return base;\n  }\n  if (exp == lim::infinity() || exp == -lim::infinity()) {\n    auto const abase = constexpr_abs(base);\n    if (abase < T(1)) {\n      return exp == lim::infinity() ? T(0) : lim::infinity();\n    }\n    if (T(1) < abase) {\n      return exp == lim::infinity() ? lim::infinity() : T(0);\n    }\n    return T(1);\n  }\n  if (base == T(1)) {\n    return base;\n  }\n  if (constexpr_isnan(exp)) {\n    return exp;\n  }\n  assert(base != T(0) || exp > T(0)); // error div-by-zero\n  if (base == lim::infinity()) {\n    return exp < T(0) ? T(0) : lim::infinity();\n  }\n  if (base == -lim::infinity()) {\n    auto const oddi = //\n        exp == constexpr_trunc(exp) &&\n        exp != constexpr_trunc(exp / T(2)) * T(2);\n    return (oddi ? -T(1) : T(1)) * (exp < T(0) ? T(0) : lim::infinity());\n  }\n  if (base == T(0)) {\n    auto const oddi = //\n        exp == constexpr_trunc(exp) &&\n        exp != constexpr_trunc(exp / T(2)) * T(2);\n    return oddi ? base : T(0);\n  }\n  if (exp < T(0)) {\n    return T(1) / constexpr_pow(base, -exp);\n  }\n\n  // as an identity: x^y = exp(y * log(x)); but calculation is imprecise ... so,\n  // for better precision, split the calculation into its integral-power and its\n  // fractional-power components\n  // as a cost, the complexity of constexpr_ipow here is logarithmic in y, i.e.,\n  // linear in the logarithm of y, which can be prohibitive\n  auto const exp_trunc = constexpr_trunc(exp);\n  assert(T(0) < base || exp == exp_trunc); // error invalid\n  auto const exp_fract = exp - exp_trunc;\n  auto const anyi = exp_fract == T(0);\n  return constexpr_mult(\n      detail::constexpr_ipow(base, exp_trunc),\n      anyi ? T(1) : constexpr_exp(exp_fract * constexpr_log(base)));\n}\n\n/// constexpr_find_last_set\n///\n/// Return the 1-based index of the most significant bit which is set.\n/// For x > 0, constexpr_find_last_set(x) == 1 + floor(log2(x)).\ntemplate <typename T>\nconstexpr std::size_t constexpr_find_last_set(T const t) {\n  using U = std::make_unsigned_t<T>;\n  return t == T(0) ? 0 : 1 + constexpr_log2(static_cast<U>(t));\n}\n\nnamespace detail {\ntemplate <typename U>\nconstexpr std::size_t constexpr_find_first_set_(\n    std::size_t s, std::size_t a, U const u) {\n  return s == 0\n      ? a\n      : constexpr_find_first_set_(\n            s / 2, a + s * bool((u >> a) % (U(1) << s) == U(0)), u);\n}\n} // namespace detail\n\n/// constexpr_find_first_set\n///\n/// Return the 1-based index of the least significant bit which is set.\n/// For x > 0, the exponent in the largest power of two which does not divide x.\ntemplate <typename T>\nconstexpr std::size_t constexpr_find_first_set(T t) {\n  using U = std::make_unsigned_t<T>;\n  using size = std::integral_constant<std::size_t, sizeof(T) * 4>;\n  return t == T(0)\n      ? 0\n      : 1 + detail::constexpr_find_first_set_(size{}, 0, static_cast<U>(t));\n}\n\ntemplate <typename T>\nconstexpr T constexpr_add_overflow_clamped(T a, T b) {\n  using L = std::numeric_limits<T>;\n  using M = std::intmax_t;\n  static_assert(\n      !std::is_integral<T>::value || sizeof(T) <= sizeof(M),\n      \"Integral type too large!\");\n  if (!folly::is_constant_evaluated_or(true)) {\n    if constexpr (std::is_integral_v<T>) {\n      T ret{};\n      if (FOLLY_UNLIKELY(!checked_add(&ret, a, b))) {\n        if constexpr (std::is_signed_v<T>) {\n          // Could be either overflow or underflow for signed types.\n          // Can only be underflow if both inputs are negative.\n          if (a < 0 && b < 0) {\n            return L::min();\n          }\n        }\n        return L::max();\n      }\n      return ret;\n    }\n  }\n  // clang-format off\n  return\n    // don't do anything special for non-integral types.\n    !std::is_integral<T>::value ? a + b :\n    // for narrow integral types, just convert to intmax_t.\n    sizeof(T) < sizeof(M)\n      ? T(constexpr_clamp(\n          static_cast<M>(a) + static_cast<M>(b),\n          static_cast<M>(L::min()),\n          static_cast<M>(L::max()))) :\n    // when a >= 0, cannot add more than `MAX - a` onto a.\n    !(a < 0) ? a + constexpr_min(b, T(L::max() - a)) :\n    // a < 0 && b >= 0, `a + b` will always be in valid range of type T.\n    !(b < 0) ? a + b :\n    // a < 0 && b < 0, keep the result >= MIN.\n              a + constexpr_max(b, T(L::min() - a));\n  // clang-format on\n}\n\ntemplate <typename T>\nconstexpr T constexpr_sub_overflow_clamped(T a, T b) {\n  using L = std::numeric_limits<T>;\n  using M = std::intmax_t;\n  static_assert(\n      !std::is_integral<T>::value || sizeof(T) <= sizeof(M),\n      \"Integral type too large!\");\n  // clang-format off\n  return\n    // don't do anything special for non-integral types.\n    !std::is_integral<T>::value ? a - b :\n    // for unsigned type, keep result >= 0.\n    std::is_unsigned<T>::value ? (a < b ? 0 : a - b) :\n    // for narrow signed integral types, just convert to intmax_t.\n    sizeof(T) < sizeof(M)\n      ? T(constexpr_clamp(\n          static_cast<M>(a) - static_cast<M>(b),\n          static_cast<M>(L::min()),\n          static_cast<M>(L::max()))) :\n    // (a >= 0 && b >= 0) || (a < 0 && b < 0), `a - b` will always be valid.\n    (a < 0) == (b < 0) ? a - b :\n    // MIN < b, so `-b` should be in valid range (-MAX <= -b <= MAX),\n    // convert subtraction to addition.\n    L::min() < b ? constexpr_add_overflow_clamped(a, T(-b)) :\n    // -b = -MIN = (MAX + 1) and a <= -1, result is in valid range.\n    a < 0 ? a - b :\n    // -b = -MIN = (MAX + 1) and a >= 0, result > MAX.\n            L::max();\n  // clang-format on\n}\n\n// clamp_cast<> provides sane numeric conversions from float point numbers to\n// integral numbers, and between different types of integral numbers. It helps\n// to avoid unexpected bugs introduced by bad conversion, and undefined behavior\n// like overflow when casting float point numbers to integral numbers.\n//\n// When doing clamp_cast<Dst>(value), if `value` is in valid range of Dst,\n// it will give correct result in Dst, equal to `value`.\n//\n// If `value` is outside the representable range of Dst, it will be clamped to\n// MAX or MIN in Dst, instead of being undefined behavior.\n//\n// Float NaNs are converted to 0 in integral type.\n//\n// Here's some comparison with static_cast<>:\n// (with FB-internal gcc-5-glibc-2.23 toolchain)\n//\n// static_cast<int32_t>(NaN) = 6\n// clamp_cast<int32_t>(NaN) = 0\n//\n// static_cast<int32_t>(9999999999.0f) = -348639895\n// clamp_cast<int32_t>(9999999999.0f) = 2147483647\n//\n// static_cast<int32_t>(2147483647.0f) = -348639895\n// clamp_cast<int32_t>(2147483647.0f) = 2147483647\n//\n// static_cast<uint32_t>(4294967295.0f) = 0\n// clamp_cast<uint32_t>(4294967295.0f) = 4294967295\n//\n// static_cast<uint32_t>(-1) = 4294967295\n// clamp_cast<uint32_t>(-1) = 0\n//\n// static_cast<int16_t>(32768u) = -32768\n// clamp_cast<int16_t>(32768u) = 32767\n\ntemplate <typename Dst, typename Src>\nconstexpr typename std::enable_if<std::is_integral<Src>::value, Dst>::type\nconstexpr_clamp_cast(Src src) {\n  static_assert(\n      std::is_integral<Dst>::value && sizeof(Dst) <= sizeof(int64_t),\n      \"constexpr_clamp_cast can only cast into integral type (up to 64bit)\");\n\n  using L = std::numeric_limits<Dst>;\n  // clang-format off\n  return\n    // Check if Src and Dst have same signedness.\n    std::is_signed<Src>::value == std::is_signed<Dst>::value\n    ? (\n      // Src and Dst have same signedness. If sizeof(Src) <= sizeof(Dst),\n      // we can safely convert Src to Dst without any loss of accuracy.\n      sizeof(Src) <= sizeof(Dst) ? Dst(src) :\n      // If Src is larger in size, we need to clamp it to valid range in Dst.\n      Dst(constexpr_clamp(src, Src(L::min()), Src(L::max()))))\n    // Src and Dst have different signedness.\n    // Check if it's signed -> unsigned cast.\n    : std::is_signed<Src>::value && std::is_unsigned<Dst>::value\n    ? (\n      // If src < 0, the result should be 0.\n      src < 0 ? Dst(0) :\n      // Otherwise, src >= 0. If src can fit into Dst, we can safely cast it\n      // without loss of accuracy.\n      sizeof(Src) <= sizeof(Dst) ? Dst(src) :\n      // If Src is larger in size than Dst, we need to ensure the result is\n      // at most Dst MAX.\n      Dst(constexpr_min(src, Src(L::max()))))\n    // It's unsigned -> signed cast.\n    : (\n      // Since Src is unsigned, and Dst is signed, Src can fit into Dst only\n      // when sizeof(Src) < sizeof(Dst).\n      sizeof(Src) < sizeof(Dst) ? Dst(src) :\n      // If Src does not fit into Dst, we need to ensure the result is at most\n      // Dst MAX.\n      Dst(constexpr_min(src, Src(L::max()))));\n  // clang-format on\n}\n\nnamespace detail {\n// Upper/lower bound values that could be accurately represented in both\n// integral and float point types.\nconstexpr double kClampCastLowerBoundDoubleToInt64F = -9223372036854774784.0;\nconstexpr double kClampCastUpperBoundDoubleToInt64F = 9223372036854774784.0;\nconstexpr double kClampCastUpperBoundDoubleToUInt64F = 18446744073709549568.0;\n\nconstexpr float kClampCastLowerBoundFloatToInt32F = -2147483520.0f;\nconstexpr float kClampCastUpperBoundFloatToInt32F = 2147483520.0f;\nconstexpr float kClampCastUpperBoundFloatToUInt32F = 4294967040.0f;\n\n// This works the same as constexpr_clamp, but the comparison are done in Src\n// to prevent any implicit promotions.\ntemplate <typename D, typename S>\nconstexpr D constexpr_clamp_cast_helper(S src, S sl, S su, D dl, D du) {\n  return src < sl ? dl : (src > su ? du : D(src));\n}\n} // namespace detail\n\ntemplate <typename Dst, typename Src>\nconstexpr typename std::enable_if<std::is_floating_point<Src>::value, Dst>::type\nconstexpr_clamp_cast(Src src) {\n  static_assert(\n      std::is_integral<Dst>::value && sizeof(Dst) <= sizeof(int64_t),\n      \"constexpr_clamp_cast can only cast into integral type (up to 64bit)\");\n\n  using L = std::numeric_limits<Dst>;\n  // clang-format off\n  return\n    // Special case: cast NaN into 0.\n    constexpr_isnan(src) ? Dst(0) :\n    // using `sizeof(Src) > sizeof(Dst)` as a heuristic that Dst can be\n    // represented in Src without loss of accuracy.\n    // see: https://en.wikipedia.org/wiki/Floating-point_arithmetic\n    sizeof(Src) > sizeof(Dst) ?\n      detail::constexpr_clamp_cast_helper(\n          src, Src(L::min()), Src(L::max()), L::min(), L::max()) :\n    // sizeof(Src) < sizeof(Dst) only happens when doing cast of\n    // 32bit float -> u/int64_t.\n    // Losslessly promote float into double, change into double -> u/int64_t.\n    sizeof(Src) < sizeof(Dst) ? (\n      src >= 0.0\n      ? constexpr_clamp_cast<Dst>(\n            constexpr_clamp_cast<std::uint64_t>(double(src)))\n      : constexpr_clamp_cast<Dst>(\n            constexpr_clamp_cast<std::int64_t>(double(src)))) :\n    // The following are for sizeof(Src) == sizeof(Dst).\n    std::is_same<Src, double>::value && std::is_same<Dst, int64_t>::value ?\n      detail::constexpr_clamp_cast_helper(\n          double(src),\n          detail::kClampCastLowerBoundDoubleToInt64F,\n          detail::kClampCastUpperBoundDoubleToInt64F,\n          L::min(),\n          L::max()) :\n    std::is_same<Src, double>::value && std::is_same<Dst, uint64_t>::value ?\n      detail::constexpr_clamp_cast_helper(\n          double(src),\n          0.0,\n          detail::kClampCastUpperBoundDoubleToUInt64F,\n          L::min(),\n          L::max()) :\n    std::is_same<Src, float>::value && std::is_same<Dst, int32_t>::value ?\n      detail::constexpr_clamp_cast_helper(\n          float(src),\n          detail::kClampCastLowerBoundFloatToInt32F,\n          detail::kClampCastUpperBoundFloatToInt32F,\n          L::min(),\n          L::max()) :\n      detail::constexpr_clamp_cast_helper(\n          float(src),\n          0.0f,\n          detail::kClampCastUpperBoundFloatToUInt32F,\n          L::min(),\n          L::max());\n  // clang-format on\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ConstructorCallbackList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <array>\n#include <atomic>\n#include <iterator>\n#include <memory>\n#include <stdexcept>\n#include <fmt/format.h>\n#include <folly/detail/StaticSingletonManager.h>\n\n#include <folly/Format.h>\n#include <folly/Function.h>\n#include <folly/SharedMutex.h>\n\nnamespace folly {\n\n// A mixin to register and issue callbacks every time a class constructor is\n// invoked\n//\n// For example:\n// #include <folly/ConstructorCallbackList.h>\n//\n// class Foo {\n//    ...\n//   private:\n//    ...\n//    // add this member last to minimize partially constructed errors\n//    ConstructorCallbackList<Foo> constructorCB_{this};\n// }\n//\n// int main() {\n//   auto cb = [](Foo * f) {\n//    std::cout << \"New Foo\" << f << std::endl;\n//   };\n//   ConstructorCallbackList<Foo>::addCallback(cb);\n//   Foo f{}; // will call callback, print to stdout\n// }\n//\n// This code is designed to be light weight so as to mixin to many\n// places with low overhead.\n//\n// NOTE: The callback is triggered with a *partially* constructed object.\n// This implies that that callback code can only access members that are\n// constructed *before* the ConstructorCallbackList object.  Also, at the time\n// of the callback, none of the Foo() constructor code will have run.\n// Per the example above,\n// the best practice is to place the ConstructorCallbackList declaration last\n// in the parent class.  This will minimize the amount of uninitialized\n// data in the Foo instance, but will not eliminate it unless it has a trivial\n// constructor.\n//\n// Implementation/Overhead Notes:\n//\n// By design, adding ConstructorCallbackList to an object should be very\n// light weight.  From a memory context, this adds 1 byte of memory to the\n// parent class. From a CPU/performance perspective, the constructor does a load\n// of an atomic int and the cost of the actual callbacks themselves.  So if this\n// is put in place and only used infrequently, e.g., during debugging,\n// this cost should be quite small.\n//\n// A compile-time static array is used intentionally over a dynamic one for\n// two reasons: (1) a dynamic array seems to require a proper lock in\n// the constructor which would exceed our perf target, and (2) having a\n// finite array provides some sanity checking on the number of callbacks\n// that can be registered.\n\ntemplate <class T, std::size_t MaxCallbacks = 4>\nclass ConstructorCallbackList {\n public:\n  static constexpr std::size_t kMaxCallbacks = MaxCallbacks;\n\n  using This = ConstructorCallbackList<T, MaxCallbacks>;\n  using Callback = folly::Function<void(T*)>;\n  using CallbackArray = std::array<typename This::Callback, MaxCallbacks>;\n\n  explicit ConstructorCallbackList(T* t) {\n    // This code depends on the C++ standard where values that are\n    // initialized to zero (\"Zero Initiation\") are initialized before any more\n    // complex static pre-main() dynamic initialization - see\n    // https://en.cppreference.com/w/cpp/language/initialization) for\n    // more details.\n    //\n    // This assumption prevents a subtle initialization race condition\n    // where something could call this code pre-main() before\n    // numCallbacks_ was set to zero, and thus prevents issuing\n    // callbacks on garbage data.\n\n    auto nCBs = This::global().numCallbacks_.load(std::memory_order_acquire);\n\n    // fire callbacks to inform listeners about the new constructor\n    /****\n     * We don't need the full lock here, just the atomic int to tell us\n     * how far into the array to go/how many callbacks are registered\n     *\n     * NOTE that nCBs > 0 will always imply that callbacks_ is non-nullptr\n     */\n    for (size_t i = 0; i < nCBs; i++) {\n      (This::global().callbacks_)[i](t);\n    }\n  }\n\n  /**\n   * Add a callback to the static class that will fire every time\n   * someone creates a new one.\n   *\n   * Implement this as a static array of callbacks rather than a dynamic\n   * vector to avoid nasty race conditions on resize, startup and shutdown.\n   *\n   * Implement this with functions rather than an observer pattern classes\n   * to avoid race conditions on shutdown\n   *\n   * Intentionally don't implement removeConstructorCallbackList to simplify\n   * implementation (e.g., just the counter is atomic rather than the whole\n   * array) and thus reduce computational cost.\n   *\n   * @throw std::length_error() if this callback would exceed our max\n   */\n  static void addCallback(Callback cb) {\n    // Ensure that a single callback is added at a time\n    std::lock_guard g(This::global().mutex_);\n    auto idx = This::global().numCallbacks_.load(std::memory_order_acquire);\n\n    if (idx >= (This::global().callbacks_).size()) {\n      throw std::length_error(\n          fmt::format(\"Too many callbacks - max {}\", MaxCallbacks));\n    }\n    (This::global().callbacks_)[idx] = std::move(cb);\n    // Only increment numCallbacks_ after fully initializing the array\n    // entry. This step makes the new array entry visible to other threads.\n    This::global().numCallbacks_.store(idx + 1, std::memory_order_release);\n  }\n\n private:\n  // use createGlobal to avoid races on shutdown\n  struct GlobalStorage {\n    mutable folly::SharedMutex mutex_;\n    This::CallbackArray callbacks_{};\n    std::atomic<size_t> numCallbacks_{0};\n  };\n  static auto& global() {\n    return folly::detail::createGlobal<GlobalStorage, void>();\n  }\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/Conv.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Conv.h>\n\n#include <array>\n#include <istream>\n\n#include <folly/lang/SafeAssert.h>\n\n#include <fast_float/fast_float.h>\n\nnamespace folly {\nnamespace detail {\n\nnamespace {\n\n/**\n * Finds the first non-digit in a string. The number of digits\n * searched depends on the precision of the Tgt integral. Assumes the\n * string starts with NO whitespace and NO sign.\n *\n * The semantics of the routine is:\n *   for (;; ++b) {\n *     if (b >= e || !isdigit(*b)) return b;\n *   }\n *\n *  Complete unrolling marks bottom-line (i.e. entire conversion)\n *  improvements of 20%.\n */\ninline const char* findFirstNonDigit(const char* b, const char* e) {\n  for (; b < e; ++b) {\n    auto const c = static_cast<unsigned>(*b) - '0';\n    if (c >= 10) {\n      break;\n    }\n  }\n  return b;\n}\n\n// Maximum value of number when represented as a string\ntemplate <class T>\nstruct MaxString {\n  static const char* const value;\n};\n\ntemplate <>\nconst char* const MaxString<uint8_t>::value = \"255\";\ntemplate <>\nconst char* const MaxString<uint16_t>::value = \"65535\";\ntemplate <>\nconst char* const MaxString<uint32_t>::value = \"4294967295\";\n#if __SIZEOF_LONG__ == 4\ntemplate <>\nconst char* const MaxString<unsigned long>::value = \"4294967295\";\n#else\ntemplate <>\nconst char* const MaxString<unsigned long>::value = \"18446744073709551615\";\n#endif\nstatic_assert(\n    sizeof(unsigned long) >= 4,\n    \"Wrong value for MaxString<unsigned long>::value,\"\n    \" please update.\");\ntemplate <>\nconst char* const MaxString<unsigned long long>::value = \"18446744073709551615\";\nstatic_assert(\n    sizeof(unsigned long long) >= 8,\n    \"Wrong value for MaxString<unsigned long long>::value\"\n    \", please update.\");\n\n#if FOLLY_HAVE_INT128_T\ntemplate <>\nconst char* const MaxString<__uint128_t>::value =\n    \"340282366920938463463374607431768211455\";\n#endif\n\n/*\n * Lookup tables that converts from a decimal character value to an integral\n * binary value, shifted by a decimal \"shift\" multiplier.\n * For all character values in the range '0'..'9', the table at those\n * index locations returns the actual decimal value shifted by the multiplier.\n * For all other values, the lookup table returns an invalid OOR value.\n */\n// Out-of-range flag value, larger than the largest value that can fit in\n// four decimal bytes (9999), but four of these added up together should\n// still not overflow uint16_t.\nconstexpr int32_t OOR = 10000;\n\nalignas(16) constexpr uint16_t shift1[] = {\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  10\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  20\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  30\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0,   1, //  40\n    2,   3,   4,   5,   6,   7,   8,   9,   OOR, OOR,\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  60\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  70\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  80\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  90\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240\n    OOR, OOR, OOR, OOR, OOR, OOR // 250\n};\n\nalignas(16) constexpr uint16_t shift10[] = {\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  10\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  20\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  30\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0,   10, //  40\n    20,  30,  40,  50,  60,  70,  80,  90,  OOR, OOR,\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  60\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  70\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  80\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  90\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240\n    OOR, OOR, OOR, OOR, OOR, OOR // 250\n};\n\nalignas(16) constexpr uint16_t shift100[] = {\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 0-9\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  10\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  20\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  30\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, 0,   100, //  40\n    200, 300, 400, 500, 600, 700, 800, 900, OOR, OOR,\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  60\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  70\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  80\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, //  90\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 100\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 110\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 120\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 130\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 140\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 150\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 160\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 170\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 180\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 190\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 200\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 210\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 220\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 230\n    OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, OOR, // 240\n    OOR, OOR, OOR, OOR, OOR, OOR // 250\n};\n\nalignas(16) constexpr uint16_t shift1000[] = {\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 0-9\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, //  10\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, //  20\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, //  30\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  0,   1000, //  40\n    2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, OOR, OOR,\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, //  60\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, //  70\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, //  80\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, //  90\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 100\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 110\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 120\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 130\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 140\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 150\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 160\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 170\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 180\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 190\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 200\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 210\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 220\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 230\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR,  OOR, OOR, // 240\n    OOR,  OOR,  OOR,  OOR,  OOR,  OOR // 250\n};\n\nstruct ErrorString {\n  const char* string;\n  bool quote;\n};\n\n// Keep this in sync with ConversionCode in Conv.h\nconstexpr const std::array<\n    ErrorString,\n    static_cast<std::size_t>(ConversionCode::NUM_ERROR_CODES)>\n    kErrorStrings{{\n        // SUCCESS\n        {\"Success\", true},\n        // EMPTY_INPUT_STRING\n        {\"Empty input string\", true},\n        // NO_DIGITS\n        {\"No digits found in input string\", true},\n        // BOOL_OVERFLOW\n        {\"Integer overflow when parsing bool (must be 0 or 1)\", true},\n        // BOOL_INVALID_VALUE\n        {\"Invalid value for bool\", true},\n        // NON_DIGIT_CHAR\n        {\"Non-digit character found\", true},\n        // INVALID_LEADING_CHAR\n        {\"Invalid leading character\", true},\n        // POSITIVE_OVERFLOW\n        {\"Overflow during conversion\", true},\n        // NEGATIVE_OVERFLOW\n        {\"Negative overflow during conversion\", true},\n        // STRING_TO_FLOAT_ERROR\n        {\"Unable to convert string to floating point value\", true},\n        // NON_WHITESPACE_AFTER_END\n        {\"Non-whitespace character found after end of conversion\", true},\n        // ARITH_POSITIVE_OVERFLOW\n        {\"Overflow during arithmetic conversion\", false},\n        // ARITH_NEGATIVE_OVERFLOW\n        {\"Negative overflow during arithmetic conversion\", false},\n        // ARITH_LOSS_OF_PRECISION\n        {\"Loss of precision during arithmetic conversion\", false},\n        // SPLIT_ERROR,\n        {\"Unexpected number of fields resulting from a split\", true},\n        // CUSTOM,\n        {\"Custom conversion failed\", true},\n    }};\n\n// Check if ASCII is really ASCII\nusing IsAscii =\n    std::bool_constant<'A' == 65 && 'Z' == 90 && 'a' == 97 && 'z' == 122>;\n\n// The code in this file that uses tolower() really only cares about\n// 7-bit ASCII characters, so we can take a nice shortcut here.\ninline char tolower_ascii(char in) {\n  return IsAscii::value ? in | 0x20 : char(std::tolower(in));\n}\n\ninline bool bool_str_cmp(const char** b, size_t len, const char* value) {\n  // Can't use strncasecmp, since we want to ensure that the full value matches\n  const char* p = *b;\n  const char* e = *b + len;\n  const char* v = value;\n  while (*v != '\\0') {\n    if (p == e || tolower_ascii(*p) != *v) { // value is already lowercase\n      return false;\n    }\n    ++p;\n    ++v;\n  }\n\n  *b = p;\n  return true;\n}\n\n} // namespace\n\nExpected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept {\n  auto b = src->begin(), e = src->end();\n  for (;; ++b) {\n    if (b >= e) {\n      return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);\n    }\n    if ((*b < '\\t' || *b > '\\r') && *b != ' ') {\n      break;\n    }\n  }\n\n  bool result;\n  auto len = size_t(e - b);\n  switch (*b) {\n    case '0':\n    case '1': {\n      result = false;\n      for (; b < e && isdigit(*b); ++b) {\n        if (result || (*b != '0' && *b != '1')) {\n          return makeUnexpected(ConversionCode::BOOL_OVERFLOW);\n        }\n        result = (*b == '1');\n      }\n      break;\n    }\n    case 'y':\n    case 'Y':\n      result = true;\n      if (!bool_str_cmp(&b, len, \"yes\")) {\n        ++b; // accept the single 'y' character\n      }\n      break;\n    case 'n':\n    case 'N':\n      result = false;\n      if (!bool_str_cmp(&b, len, \"no\")) {\n        ++b;\n      }\n      break;\n    case 't':\n    case 'T':\n      result = true;\n      if (!bool_str_cmp(&b, len, \"true\")) {\n        ++b;\n      }\n      break;\n    case 'f':\n    case 'F':\n      result = false;\n      if (!bool_str_cmp(&b, len, \"false\")) {\n        ++b;\n      }\n      break;\n    case 'o':\n    case 'O':\n      if (bool_str_cmp(&b, len, \"on\")) {\n        result = true;\n      } else if (bool_str_cmp(&b, len, \"off\")) {\n        result = false;\n      } else {\n        return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE);\n      }\n      break;\n    default:\n      return makeUnexpected(ConversionCode::BOOL_INVALID_VALUE);\n  }\n\n  src->assign(b, e);\n\n  return result;\n}\n\n/// Uses `fast_float::from_chars` to convert from string to an integer.\ntemplate <class Tgt>\nExpected<Tgt, ConversionCode> str_to_floating_fast_float_from_chars(\n    StringPiece* src) noexcept {\n  if (src->empty()) {\n    return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);\n  }\n\n  // move through leading whitespace characters\n  auto* e = src->end();\n  auto* b = std::find_if_not(src->begin(), e, [](char c) {\n    return (c >= '\\t' && c <= '\\r') || c == ' ';\n  });\n  if (b == e) {\n    return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);\n  }\n\n  Tgt result;\n  fast_float::parse_options options{\n      fast_float::chars_format::general |\n      fast_float::chars_format::allow_leading_plus};\n  auto [ptr, ec] = fast_float::from_chars_advanced(b, e, result, options);\n  bool isOutOfRange{ec == std::errc::result_out_of_range};\n  bool isOk{ec == std::errc()};\n  if (!isOk && !isOutOfRange) {\n    return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR);\n  }\n\n  auto numMatchedChars = ptr - src->data();\n  src->advance(numMatchedChars);\n  return result;\n}\n\ntemplate Expected<float, ConversionCode>\nstr_to_floating_fast_float_from_chars<float>(StringPiece* src) noexcept;\ntemplate Expected<double, ConversionCode>\nstr_to_floating_fast_float_from_chars<double>(StringPiece* src) noexcept;\n\n/**\n * StringPiece to double, with progress information. Alters the\n * StringPiece parameter to munch the already-parsed characters.\n */\ntemplate <class Tgt>\nExpected<Tgt, ConversionCode> str_to_floating(StringPiece* src) noexcept {\n  return detail::str_to_floating_fast_float_from_chars<Tgt>(src);\n}\n\ntemplate Expected<float, ConversionCode> str_to_floating<float>(\n    StringPiece* src) noexcept;\ntemplate Expected<double, ConversionCode> str_to_floating<double>(\n    StringPiece* src) noexcept;\n\nnamespace {\n\n/**\n * This class takes care of additional processing needed for signed values,\n * like leading sign character and overflow checks.\n */\ntemplate <typename T, bool IsSigned = is_signed_v<T>>\nclass SignedValueHandler;\n\ntemplate <typename T>\nclass SignedValueHandler<T, true> {\n public:\n  ConversionCode init(const char*& b) {\n    negative_ = false;\n    if (!std::isdigit(*b)) {\n      if (*b == '-') {\n        negative_ = true;\n      } else if (FOLLY_UNLIKELY(*b != '+')) {\n        return ConversionCode::INVALID_LEADING_CHAR;\n      }\n      ++b;\n    }\n    return ConversionCode::SUCCESS;\n  }\n\n  ConversionCode overflow() {\n    return negative_\n        ? ConversionCode::NEGATIVE_OVERFLOW\n        : ConversionCode::POSITIVE_OVERFLOW;\n  }\n\n  template <typename U>\n  Expected<T, ConversionCode> finalize(U value) {\n    T rv;\n    if (negative_) {\n      FOLLY_PUSH_WARNING\n      FOLLY_MSVC_DISABLE_WARNING(4146)\n\n      // unary minus operator applied to unsigned type, result still unsigned\n      rv = T(-value);\n\n      FOLLY_POP_WARNING\n\n      if (FOLLY_UNLIKELY(rv > 0)) {\n        return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n      }\n    } else {\n      rv = T(value);\n      if (FOLLY_UNLIKELY(rv < 0)) {\n        return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);\n      }\n    }\n    return rv;\n  }\n\n private:\n  bool negative_;\n};\n\n// For unsigned types, we don't need any extra processing\ntemplate <typename T>\nclass SignedValueHandler<T, false> {\n public:\n  ConversionCode init(const char*&) { return ConversionCode::SUCCESS; }\n\n  ConversionCode overflow() { return ConversionCode::POSITIVE_OVERFLOW; }\n\n  Expected<T, ConversionCode> finalize(T value) { return value; }\n};\n\n} // namespace\n\n/**\n * String represented as a pair of pointers to char to signed/unsigned\n * integrals. Assumes NO whitespace before or after, and also that the\n * string is composed entirely of digits (and an optional sign only for\n * signed types). String may be empty, in which case digits_to returns\n * an appropriate error.\n */\ntemplate <class Tgt>\ninline Expected<Tgt, ConversionCode> digits_to(\n    const char* b, const char* const e) noexcept {\n  using UT = make_unsigned_t<Tgt>;\n  assert(b <= e);\n\n  SignedValueHandler<Tgt> sgn;\n\n  auto err = sgn.init(b);\n  if (FOLLY_UNLIKELY(err != ConversionCode::SUCCESS)) {\n    return makeUnexpected(err);\n  }\n\n  auto size = size_t(e - b);\n\n  /* Although the string is entirely made of digits, we still need to\n   * check for overflow.\n   */\n  if (size > std::numeric_limits<UT>::digits10) {\n    // Leading zeros?\n    if (b < e && *b == '0') {\n      for (++b;; ++b) {\n        if (b == e) {\n          return Tgt(0); // just zeros, e.g. \"0000\"\n        }\n        if (*b != '0') {\n          size = size_t(e - b);\n          break;\n        }\n      }\n    }\n    if (size > std::numeric_limits<UT>::digits10 &&\n        (size != std::numeric_limits<UT>::digits10 + 1 ||\n         strncmp(b, MaxString<UT>::value, size) > 0)) {\n      return makeUnexpected(sgn.overflow());\n    }\n  }\n\n  // Here we know that the number won't overflow when\n  // converted. Proceed without checks.\n\n  UT result = 0;\n\n  for (; e - b >= 4; b += 4) {\n    result *= UT(10000);\n    const int32_t r0 = shift1000[static_cast<size_t>(b[0])];\n    const int32_t r1 = shift100[static_cast<size_t>(b[1])];\n    const int32_t r2 = shift10[static_cast<size_t>(b[2])];\n    const int32_t r3 = shift1[static_cast<size_t>(b[3])];\n    const auto sum = r0 + r1 + r2 + r3;\n    if (sum >= OOR) {\n      goto outOfRange;\n    }\n    result += UT(sum);\n  }\n\n  switch (e - b) {\n    case 3: {\n      const int32_t r0 = shift100[static_cast<size_t>(b[0])];\n      const int32_t r1 = shift10[static_cast<size_t>(b[1])];\n      const int32_t r2 = shift1[static_cast<size_t>(b[2])];\n      const auto sum = r0 + r1 + r2;\n      if (sum >= OOR) {\n        goto outOfRange;\n      }\n      result = UT(1000 * result + sum);\n      break;\n    }\n    case 2: {\n      const int32_t r0 = shift10[static_cast<size_t>(b[0])];\n      const int32_t r1 = shift1[static_cast<size_t>(b[1])];\n      const auto sum = r0 + r1;\n      if (sum >= OOR) {\n        goto outOfRange;\n      }\n      result = UT(100 * result + sum);\n      break;\n    }\n    case 1: {\n      const int32_t sum = shift1[static_cast<size_t>(b[0])];\n      if (sum >= OOR) {\n        goto outOfRange;\n      }\n      result = UT(10 * result + sum);\n      break;\n    }\n    default:\n      assert(b == e);\n      if (size == 0) {\n        return makeUnexpected(ConversionCode::NO_DIGITS);\n      }\n      break;\n  }\n\n  return sgn.finalize(result);\n\noutOfRange:\n  return makeUnexpected(ConversionCode::NON_DIGIT_CHAR);\n}\n\ntemplate Expected<char, ConversionCode> digits_to<char>(\n    const char*, const char*) noexcept;\ntemplate Expected<signed char, ConversionCode> digits_to<signed char>(\n    const char*, const char*) noexcept;\ntemplate Expected<unsigned char, ConversionCode> digits_to<unsigned char>(\n    const char*, const char*) noexcept;\n\ntemplate Expected<short, ConversionCode> digits_to<short>(\n    const char*, const char*) noexcept;\ntemplate Expected<unsigned short, ConversionCode> digits_to<unsigned short>(\n    const char*, const char*) noexcept;\n\ntemplate Expected<int, ConversionCode> digits_to<int>(\n    const char*, const char*) noexcept;\ntemplate Expected<unsigned int, ConversionCode> digits_to<unsigned int>(\n    const char*, const char*) noexcept;\n\ntemplate Expected<long, ConversionCode> digits_to<long>(\n    const char*, const char*) noexcept;\ntemplate Expected<unsigned long, ConversionCode> digits_to<unsigned long>(\n    const char*, const char*) noexcept;\n\ntemplate Expected<long long, ConversionCode> digits_to<long long>(\n    const char*, const char*) noexcept;\ntemplate Expected<unsigned long long, ConversionCode>\ndigits_to<unsigned long long>(const char*, const char*) noexcept;\n\n#if FOLLY_HAVE_INT128_T\ntemplate Expected<__int128, ConversionCode> digits_to<__int128>(\n    const char*, const char*) noexcept;\ntemplate Expected<unsigned __int128, ConversionCode>\ndigits_to<unsigned __int128>(const char*, const char*) noexcept;\n#endif\n\n/**\n * StringPiece to integrals, with progress information. Alters the\n * StringPiece parameter to munch the already-parsed characters.\n */\ntemplate <class Tgt>\nExpected<Tgt, ConversionCode> str_to_integral(StringPiece* src) noexcept {\n  using UT = make_unsigned_t<Tgt>;\n\n  auto b = src->data(), past = src->data() + src->size();\n\n  for (;; ++b) {\n    if (FOLLY_UNLIKELY(b >= past)) {\n      return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);\n    }\n    if ((*b < '\\t' || *b > '\\r') && *b != ' ') {\n      break;\n    }\n  }\n\n  SignedValueHandler<Tgt> sgn;\n  auto err = sgn.init(b);\n\n  if (FOLLY_UNLIKELY(err != ConversionCode::SUCCESS)) {\n    return makeUnexpected(err);\n  }\n  if (is_signed_v<Tgt> && FOLLY_UNLIKELY(b >= past)) {\n    return makeUnexpected(ConversionCode::NO_DIGITS);\n  }\n  if (FOLLY_UNLIKELY(!isdigit(*b))) {\n    return makeUnexpected(ConversionCode::NON_DIGIT_CHAR);\n  }\n\n  auto m = findFirstNonDigit(b + 1, past);\n\n  auto tmp = digits_to<UT>(b, m);\n\n  if (FOLLY_UNLIKELY(!tmp.hasValue())) {\n    return makeUnexpected(\n        tmp.error() == ConversionCode::POSITIVE_OVERFLOW\n            ? sgn.overflow()\n            : tmp.error());\n  }\n\n  auto res = sgn.finalize(tmp.value());\n\n  if (res.hasValue()) {\n    src->advance(size_t(m - src->data()));\n  }\n\n  return res;\n}\n\ntemplate Expected<char, ConversionCode> str_to_integral<char>(\n    StringPiece* src) noexcept;\ntemplate Expected<signed char, ConversionCode> str_to_integral<signed char>(\n    StringPiece* src) noexcept;\ntemplate Expected<unsigned char, ConversionCode> str_to_integral<unsigned char>(\n    StringPiece* src) noexcept;\n\ntemplate Expected<short, ConversionCode> str_to_integral<short>(\n    StringPiece* src) noexcept;\ntemplate Expected<unsigned short, ConversionCode>\nstr_to_integral<unsigned short>(StringPiece* src) noexcept;\n\ntemplate Expected<int, ConversionCode> str_to_integral<int>(\n    StringPiece* src) noexcept;\ntemplate Expected<unsigned int, ConversionCode> str_to_integral<unsigned int>(\n    StringPiece* src) noexcept;\n\ntemplate Expected<long, ConversionCode> str_to_integral<long>(\n    StringPiece* src) noexcept;\ntemplate Expected<unsigned long, ConversionCode> str_to_integral<unsigned long>(\n    StringPiece* src) noexcept;\n\ntemplate Expected<long long, ConversionCode> str_to_integral<long long>(\n    StringPiece* src) noexcept;\ntemplate Expected<unsigned long long, ConversionCode>\nstr_to_integral<unsigned long long>(StringPiece* src) noexcept;\n\n#if FOLLY_HAVE_INT128_T\ntemplate Expected<__int128, ConversionCode> str_to_integral<__int128>(\n    StringPiece* src) noexcept;\ntemplate Expected<unsigned __int128, ConversionCode>\nstr_to_integral<unsigned __int128>(StringPiece* src) noexcept;\n#endif\n} // namespace detail\n\nConversionError makeConversionError(ConversionCode code, StringPiece input) {\n  using namespace detail;\n  static_assert(\n      std::is_unsigned<std::underlying_type<ConversionCode>::type>::value,\n      \"ConversionCode should be unsigned\");\n  auto index = static_cast<std::size_t>(code);\n  FOLLY_SAFE_CHECK(index < kErrorStrings.size(), \"code=\", uint64_t(index));\n  const ErrorString& err = kErrorStrings[index];\n  if (code == ConversionCode::EMPTY_INPUT_STRING && input.empty()) {\n    return {err.string, code};\n  }\n  std::string tmp(err.string);\n  tmp.append(\": \");\n  if (err.quote) {\n    tmp.append(1, '\"');\n  }\n  if (!input.empty()) {\n    tmp.append(input.data(), input.size());\n  }\n  if (err.quote) {\n    tmp.append(1, '\"');\n  }\n  return {tmp, code};\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Conv.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_conv\n//\n\n/**\n * Conv provides the ubiquitous method `to<TargetType>(source)`, along with\n * a few other generic interfaces for converting objects to and from\n * string-like types (std::string, fbstring, StringPiece), as well as\n * range-checked conversions between numeric and enum types. The mechanisms are\n * extensible, so that user-specified types can add folly::to support.\n *\n *     folly::to<std::string>(123)\n *     // \"123\"\n *\n *******************************************************************************\n * ## TYPE -> STRING CONVERSIONS\n *******************************************************************************\n * You can call the `to<std::string>` or `to<fbstring>`. These are variadic\n * functions that convert their arguments to strings, and concatenate them to\n * form a result. So, for example,\n *\n *     auto str = to<std::string>(123, \"456\", 789);\n *\n * Sets str to `\"123456789\"`.\n *\n * In addition to just concatenating the arguments, related functions can\n * delimit them with some string: `toDelim<std::string>(\",\", \"123\", 456, \"789\")`\n * will return the string `\"123,456,789\"`.\n *\n * toAppend does not return a string; instead, it takes a pointer to a string as\n * its last argument, and appends the result of the concatenation into it:\n *     std::string str = \"123\";\n *     toAppend(456, \"789\", &str); // Now str is \"123456789\".\n *\n * The toAppendFit function acts like toAppend, but it precalculates the size\n * required to perform the append operation, and reserves that space in the\n * output string before actually inserting its arguments. This can sometimes\n * save on string expansion, but beware: appending to the same string many times\n * with toAppendFit is likely a pessimization, since it will resize the string\n * once per append.\n *\n * The combination of the append and delim variants also exist: toAppendDelim\n * and toAppendDelimFit are defined, with the obvious semantics.\n *\n *******************************************************************************\n * ## STRING -> TYPE CONVERSIONS\n *******************************************************************************\n * Going in the other direction, and parsing a string into a C++ type, is also\n * supported:\n *     to<int>(\"123\"); // Returns 123.\n *\n * Out of range (e.g. `to<std::uint8_t>(\"1000\")`), or invalidly formatted (e.g.\n * `to<int>(\"four\")`) inputs will throw. If throw-on-error is undesirable (for\n * instance: you're dealing with untrusted input, and want to protect yourself\n * from users sending you down a very slow exception-throwing path), you can use\n * `tryTo<T>`, which will return an `Expected<T, ConversionCode>`.\n *\n * There are overloads of to() and tryTo() that take a `StringPiece*`. These\n * parse out a type from the beginning of a string, and modify the passed-in\n * StringPiece to indicate the portion of the string not consumed.\n *\n *******************************************************************************\n * ## NUMERIC / ENUM CONVERSIONS\n *******************************************************************************\n * Conv also supports a `to<T>(S)` overload, where T and S are numeric or enum\n * types, that checks to see that the target type can represent its argument,\n * and will throw if it cannot. This includes cases where a floating point to\n * integral conversion is attempted on a value with a non-zero fractional\n * component, and integral to floating point conversions that would lose\n * precision. Enum conversions are range-checked for the underlying type of the\n * enum, but there is no check that the input value is a valid choice of enum\n * value.\n *\n *******************************************************************************\n * ## CUSTOM TYPE CONVERSIONS\n *******************************************************************************\n * Users may customize the string conversion functionality for their own data\n * types. The key functions you should implement are:\n *     // Two functions to allow conversion to your type from a string.\n *     Expected<StringPiece, ConversionCode> parseTo(folly::StringPiece in,\n *         YourType& out);\n *     YourErrorType makeConversionError(YourErrorType in, StringPiece in);\n *     // Two functions to allow conversion from your type to a string.\n *     template <class String>\n *   void toAppend(const YourType& in, String* out);\n *       size_t estimateSpaceNeeded(const YourType& in);\n *\n * These are documented below, inline.\n *\n * @file Conv.h\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cassert>\n#include <cctype>\n#include <climits>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <stdexcept>\n#include <string>\n#include <system_error>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#if __has_include(<charconv>)\n#include <charconv>\n#endif\n\n#include <double-conversion/double-conversion.h> // V8 JavaScript implementation\n\n#include <folly/CPortability.h>\n\n#include <folly/Demangle.h>\n#include <folly/Expected.h>\n#include <folly/FBString.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/Traits.h>\n#include <folly/Unit.h>\n#include <folly/Utility.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Pretty.h>\n#include <folly/lang/ToAscii.h>\n#include <folly/portability/Math.h>\n\nnamespace folly {\n\n// Keep this in sync with kErrorStrings in Conv.cpp\nenum class ConversionCode : unsigned char {\n  SUCCESS,\n  EMPTY_INPUT_STRING,\n  NO_DIGITS,\n  BOOL_OVERFLOW,\n  BOOL_INVALID_VALUE,\n  NON_DIGIT_CHAR,\n  INVALID_LEADING_CHAR,\n  POSITIVE_OVERFLOW,\n  NEGATIVE_OVERFLOW,\n  STRING_TO_FLOAT_ERROR,\n  NON_WHITESPACE_AFTER_END,\n  ARITH_POSITIVE_OVERFLOW,\n  ARITH_NEGATIVE_OVERFLOW,\n  ARITH_LOSS_OF_PRECISION,\n  SPLIT_ERROR,\n  CUSTOM,\n  NUM_ERROR_CODES, // has to be the last entry\n};\n\nstruct FOLLY_EXPORT ConversionErrorBase : std::range_error {\n  using std::range_error::range_error;\n};\n\nclass FOLLY_EXPORT ConversionError : public ConversionErrorBase {\n public:\n  ConversionError(const std::string& str, ConversionCode code)\n      : ConversionErrorBase(str), code_(code) {}\n\n  ConversionError(const char* str, ConversionCode code)\n      : ConversionErrorBase(str), code_(code) {}\n\n  ConversionCode errorCode() const { return code_; }\n\n private:\n  ConversionCode code_;\n};\n\n/**\n * Custom Error Translation\n *\n * Your overloaded parseTo() function can return a custom error code on failure.\n * ::folly::to() will call makeConversionError to translate that error code into\n * an object to throw. makeConversionError is found by argument-dependent\n * lookup. It should have this signature:\n *\n * namespace other_namespace {\n * enum YourErrorCode { BAD_ERROR, WORSE_ERROR };\n *\n * struct YourConversionError : ConversionErrorBase {\n *   YourConversionError(const char* what) : ConversionErrorBase(what) {}\n * };\n *\n * YourConversionError\n * makeConversionError(YourErrorCode code, ::folly::StringPiece sp) {\n *   ...\n *   return YourConversionError(messageString);\n * }\n */\nConversionError makeConversionError(ConversionCode code, StringPiece input);\n\nnamespace detail {\n/**\n * Enforce that the suffix following a number is made up only of whitespace.\n */\ninline ConversionCode enforceWhitespaceErr(StringPiece sp) {\n  for (auto c : sp) {\n    if (FOLLY_UNLIKELY(!std::isspace(c))) {\n      return ConversionCode::NON_WHITESPACE_AFTER_END;\n    }\n  }\n  return ConversionCode::SUCCESS;\n}\n\n/**\n * Keep this implementation around for prettyToDouble().\n */\ninline void enforceWhitespace(StringPiece sp) {\n  auto err = enforceWhitespaceErr(sp);\n  if (err != ConversionCode::SUCCESS) {\n    throw_exception(makeConversionError(err, sp));\n  }\n}\n} // namespace detail\n\n/**\n * @overloadbrief to, but return an Expected\n *\n * The identity conversion function.\n * tryTo<T>(T) returns itself for all types T.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_same<Tgt, typename std::decay<Src>::type>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(Src&& value) noexcept {\n  return static_cast<Src&&>(value);\n}\n\n/**\n * @overloadbrief Convert from one type to another.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_same<Tgt, typename std::decay<Src>::type>::value,\n    Tgt>::type\nto(Src&& value) {\n  return static_cast<Src&&>(value);\n}\n\n/**\n * Arithmetic to boolean\n */\n\n/**\n * Unchecked conversion from arithmetic to boolean. This is different from the\n * other arithmetic conversions because we use the C convention of treating any\n * non-zero value as true, instead of range checking.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    is_arithmetic_v<Src> && !std::is_same<Tgt, Src>::value &&\n        std::is_same<Tgt, bool>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const Src& value) noexcept {\n  return value != Src();\n}\n\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    is_arithmetic_v<Src> && !std::is_same<Tgt, Src>::value &&\n        std::is_same<Tgt, bool>::value,\n    Tgt>::type\nto(const Src& value) {\n  return value != Src();\n}\n\n/**\n * Anything to string\n */\n\nnamespace detail {\n\ntemplate <class... T>\nusing LastElement = type_pack_element_t<sizeof...(T) - 1, T...>;\n\n#ifdef _MSC_VER\n// MSVC can't quite figure out the LastElementImpl::call() stuff\n// in the base implementation, so we have to use tuples instead,\n// which result in significantly more templates being compiled,\n// though the runtime performance is the same.\n\ntemplate <typename... Ts, typename R = LastElement<Ts...>>\nconst R& getLastElement(const Ts&... ts) {\n  return std::get<sizeof...(Ts) - 1>(std::forward_as_tuple(ts...));\n}\n\ninline void getLastElement() {}\n#else\ntemplate <typename...>\nstruct LastElementImpl;\ntemplate <>\nstruct LastElementImpl<> {\n  static void call() {}\n};\ntemplate <typename Ign, typename... Igns>\nstruct LastElementImpl<Ign, Igns...> {\n  template <typename Last>\n  static const Last& call(Igns..., const Last& last) {\n    return last;\n  }\n};\n\ntemplate <typename... Ts, typename R = LastElement<Ts...>>\nconst R& getLastElement(const Ts&... ts) {\n  return LastElementImpl<Ignored<Ts>...>::call(ts...);\n}\n#endif\n\n} // namespace detail\n\n/**\n * Conversions from integral types to string types.\n */\n\n#if FOLLY_HAVE_INT128_T\nnamespace detail {\n\ntemplate <typename IntegerType>\nconstexpr unsigned int digitsEnough() {\n  // digits10 returns the number of decimal digits that this type can represent,\n  // not the number of characters required for the max value, so we need to add\n  // one. ex: char digits10 returns 2, because 256-999 cannot be represented,\n  // but we need 3.\n  auto const digits10 = std::numeric_limits<IntegerType>::digits10;\n  return static_cast<unsigned int>(digits10) + 1;\n}\n\ninline size_t unsafeTelescope128(char* outb, char* oute, unsigned __int128 x) {\n  using Usrc = unsigned __int128;\n\n  // Decompose the input into at most 3 components using the largest power-of-10\n  // base that fits in a 64-bit unsigned integer, and then convert the\n  // components using 64-bit arithmetic and concatenate them.\n  constexpr static auto kBase = UINT64_C(10'000'000'000'000'000'000);\n  constexpr static size_t kBaseDigits = 19;\n\n  size_t p = 0;\n  const auto leading = [&](Usrc v) {\n    assert(v >> 64 == 0);\n    p = detail::to_ascii_with_route<10, to_ascii_alphabet_lower>(\n        outb, oute, static_cast<uint64_t>(v));\n  };\n  const auto append = [&](uint64_t v) {\n    assert(v < kBase);\n    assert(outb + p + kBaseDigits <= oute);\n    auto v64 = static_cast<uint64_t>(v);\n    detail::to_ascii_with_route<10, to_ascii_alphabet_lower>(\n        outb + p, kBaseDigits, v64);\n    p += kBaseDigits;\n  };\n\n  if (x >> 64 > 0) {\n    const auto rem = static_cast<uint64_t>(x % kBase);\n    x /= kBase;\n\n    if (x >> 64 > 0) {\n      const auto rem2 = static_cast<uint64_t>(x % kBase);\n      x /= kBase;\n\n      leading(x);\n      append(rem2);\n      append(rem);\n      return p;\n    }\n\n    leading(x);\n    append(rem);\n    return p;\n  }\n\n  leading(x);\n  return p;\n}\n\n} // namespace detail\n#endif\n\n/**\n * @overloadbrief Appends conversion to string.\n *\n * A single char gets appended.\n */\ntemplate <class Tgt>\nvoid toAppend(char value, Tgt* result) {\n  *result += value;\n}\n\n/**\n * @overloadbrief Estimates the number of characters in a value's string\n * representation.\n */\ntemplate <class T>\nconstexpr typename std::enable_if<std::is_same<T, char>::value, size_t>::type\nestimateSpaceNeeded(T) {\n  return 1;\n}\n\ntemplate <size_t N>\nconstexpr size_t estimateSpaceNeeded(const char (&)[N]) {\n  return N;\n}\n\n/**\n * Everything implicitly convertible to const char* gets appended.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_convertible<Src, const char*>::value &&\n    IsSomeString<Tgt>::value>::type\ntoAppend(Src value, Tgt* result) {\n  // Treat null pointers like an empty string, as in:\n  // operator<<(std::ostream&, const char*).\n  if (const char* c = value) {\n    result->append(c);\n  }\n}\n\ntemplate <class Src>\ntypename std::enable_if<std::is_convertible<Src, const char*>::value, size_t>::\n    type\n    estimateSpaceNeeded(Src value) {\n  const char* c = value;\n  return c ? std::strlen(c) : 0;\n}\n\ntemplate <class Src>\ntypename std::enable_if<IsSomeString<Src>::value, size_t>::type\nestimateSpaceNeeded(Src const& value) {\n  return value.size();\n}\n\ntemplate <class Src>\ntypename std::enable_if<\n    std::is_convertible<Src, folly::StringPiece>::value &&\n        !IsSomeString<Src>::value &&\n        !std::is_convertible<Src, const char*>::value,\n    size_t>::type\nestimateSpaceNeeded(Src value) {\n  return folly::StringPiece(value).size();\n}\n\ntemplate <>\ninline size_t estimateSpaceNeeded(std::nullptr_t /* value */) {\n  return 0;\n}\n\ntemplate <class Src>\ntypename std::enable_if<\n    std::is_pointer<Src>::value &&\n        IsSomeString<std::remove_pointer<Src>>::value,\n    size_t>::type\nestimateSpaceNeeded(Src value) {\n  return value->size();\n}\n\n/**\n * Strings get appended, too.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    IsSomeString<Src>::value && IsSomeString<Tgt>::value>::type\ntoAppend(const Src& value, Tgt* result) {\n  result->append(value);\n}\n\n/**\n * and StringPiece objects too\n */\ntemplate <class Tgt>\ntypename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(\n    StringPiece value, Tgt* result) {\n  result->append(value.data(), value.size());\n}\n\n/**\n * There's no implicit conversion from fbstring to other string types,\n * so make a specialization.\n */\ntemplate <class Tgt>\ntypename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(\n    const fbstring& value, Tgt* result) {\n  result->append(value.data(), value.size());\n}\n\n#if FOLLY_HAVE_INT128_T\n/**\n * Special handling for 128 bit integers.\n */\n\ntemplate <class Tgt>\nvoid toAppend(__int128 value, Tgt* result) {\n  using Usrc = unsigned __int128;\n  char buffer[detail::digitsEnough<unsigned __int128>() + 1];\n  const auto oute = buffer + sizeof(buffer);\n  size_t p;\n\n  if (value < 0) {\n    buffer[0] = '-';\n    p = 1 + detail::unsafeTelescope128(buffer + 1, oute, -Usrc(value));\n  } else {\n    p = detail::unsafeTelescope128(buffer, oute, value);\n  }\n\n  result->append(buffer, p);\n}\n\ntemplate <class Tgt>\nvoid toAppend(unsigned __int128 value, Tgt* result) {\n  char buffer[detail::digitsEnough<unsigned __int128>()];\n  size_t p = detail::unsafeTelescope128(buffer, buffer + sizeof(buffer), value);\n  result->append(buffer, p);\n}\n\ntemplate <class T>\nconstexpr\n    typename std::enable_if<std::is_same<T, __int128>::value, size_t>::type\n    estimateSpaceNeeded(T) {\n  return detail::digitsEnough<__int128>();\n}\n\ntemplate <class T>\nconstexpr typename std::\n    enable_if<std::is_same<T, unsigned __int128>::value, size_t>::type\n    estimateSpaceNeeded(T) {\n  return detail::digitsEnough<unsigned __int128>();\n}\n\n#endif\n\n/**\n * int32_t and int64_t to string (by appending) go through here. The\n * result is APPENDED to a preexisting string passed as the second\n * parameter. This should be efficient with fbstring because fbstring\n * incurs no dynamic allocation below 23 bytes and no number has more\n * than 22 bytes in its textual representation (20 for digits, one for\n * sign, one for the terminating 0).\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    is_integral_v<Src> && is_signed_v<Src> && IsSomeString<Tgt>::value &&\n    sizeof(Src) >= 4>::type\ntoAppend(Src value, Tgt* result) {\n  char buffer[to_ascii_size_max_decimal<uint64_t>];\n  auto uvalue = value < 0\n      ? ~static_cast<uint64_t>(value) + 1\n      : static_cast<uint64_t>(value);\n  if (value < 0) {\n    result->push_back('-');\n  }\n  result->append(buffer, to_ascii_decimal(buffer, uvalue));\n}\n\ntemplate <class Src>\ntypename std::enable_if<\n    is_integral_v<Src> && is_signed_v<Src> && sizeof(Src) >= 4 &&\n        sizeof(Src) < 16,\n    size_t>::type\nestimateSpaceNeeded(Src value) {\n  auto uvalue = value < 0\n      ? ~static_cast<uint64_t>(value) + 1\n      : static_cast<uint64_t>(value);\n  return size_t(value < 0) + to_ascii_size_decimal(uvalue);\n}\n\n/**\n * As above, but for uint32_t and uint64_t.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    is_integral_v<Src> && !is_signed_v<Src> && IsSomeString<Tgt>::value &&\n    sizeof(Src) >= 4>::type\ntoAppend(Src value, Tgt* result) {\n  char buffer[to_ascii_size_max_decimal<uint64_t>];\n  result->append(buffer, to_ascii_decimal(buffer, value));\n}\n\ntemplate <class Src>\ntypename std::enable_if<\n    is_integral_v<Src> && !is_signed_v<Src> && sizeof(Src) >= 4 &&\n        sizeof(Src) < 16,\n    size_t>::type\nestimateSpaceNeeded(Src value) {\n  return to_ascii_size_decimal(value);\n}\n\n/**\n * All small signed and unsigned integers to string go through 32-bit\n * types int32_t and uint32_t, respectively.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    is_integral_v<Src> && IsSomeString<Tgt>::value && sizeof(Src) < 4>::type\ntoAppend(Src value, Tgt* result) {\n  using Intermediate =\n      typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type;\n  toAppend<Tgt>(static_cast<Intermediate>(value), result);\n}\n\ntemplate <class Src>\ntypename std::enable_if<\n    is_integral_v<Src> && sizeof(Src) < 4 && !std::is_same<Src, char>::value,\n    size_t>::type\nestimateSpaceNeeded(Src value) {\n  using Intermediate =\n      typename std::conditional<is_signed_v<Src>, int64_t, uint64_t>::type;\n  return estimateSpaceNeeded(static_cast<Intermediate>(value));\n}\n\n/**\n * Enumerated values get appended as integers.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_enum<Src>::value && IsSomeString<Tgt>::value>::type\ntoAppend(Src value, Tgt* result) {\n  toAppend(to_underlying(value), result);\n}\n\ntemplate <class Src>\ntypename std::enable_if<std::is_enum<Src>::value, size_t>::type\nestimateSpaceNeeded(Src value) {\n  return estimateSpaceNeeded(to_underlying(value));\n}\n\n/**\n * Conversions from floating-point types to string types.\n */\n\n/// Operating mode for the floating point type version of\n/// `folly::ToAppend`. This is modeled after\n/// `double_conversion::DoubleToStringConverter::DtoaMode`.\n/// Dtoa is an acronym for Double to ASCII.\nenum class DtoaMode {\n  /// Outputs the shortest representation of a `double`.\n  /// The output is either in decimal or exponential notation; which ever is\n  /// shortest.\n  SHORTEST,\n  /// Outputs the shortest representation of a `float`.\n  /// This outputs in either decimal or exponential notation, which ever is\n  /// shortest.\n  SHORTEST_SINGLE,\n  /// Outputs fixed precision after the decimal point. Similar to\n  /// `printf`'s %f.\n  /// The output is in decimal notation.\n  /// Use the `numDigits` parameter to specify the precision.\n  FIXED,\n  /// Outputs with a precision that is independent of the decimal point.\n  /// The outputs is either decimal or exponential notation, depending on the\n  /// value and the precision.\n  /// Similar to `printf`'s %g formatting.\n  /// Use the `numDigits` parameter to specify the precision.\n  PRECISION,\n};\n\n/// Flags for the floating point type version of `folly::ToAppend`.\n/// This is modeled after `double_conversion::DoubleToStringConverter::Flags`.\n/// Dtoa is an acronym for Double to ASCII.\n/// This enum is used to store bit wise flags, so a variable of this type may be\n/// a bitwise combination of these definitions.\nenum class DtoaFlags {\n  NO_FLAGS = 0,\n  /// Emits a plus sign for positive exponents. e.g., 1.2e+3\n  EMIT_POSITIVE_EXPONENT_SIGN = 1,\n  /// Emits a trailing decimal point. e.g., 123.\n  EMIT_TRAILING_DECIMAL_POINT = 2,\n  /// Emits a trailing decimal point. e.g., 123.0\n  /// Requires `EMIT_TRAILING_DECIMAL_POINT` to be set.\n  EMIT_TRAILING_ZERO_AFTER_POINT = 4,\n  /// -0.0 outputs as 0.0\n  UNIQUE_ZERO = 8,\n  /// Trailing zeros are removed from the fractional portion\n  /// of the result in precision mode. Matches `printf`'s %g.\n  /// When `EMIT_TRAILING_ZERO_AFTER_POINT` is also given, one trailing zero is\n  /// preserved.\n  NO_TRAILING_ZERO = 16,\n};\n\nconstexpr DtoaFlags operator|(DtoaFlags a, DtoaFlags b) {\n  return static_cast<DtoaFlags>(to_underlying(a) | to_underlying(b));\n}\n\nconstexpr DtoaFlags operator&(DtoaFlags a, DtoaFlags b) {\n  return static_cast<DtoaFlags>(to_underlying(a) & to_underlying(b));\n}\n\nnamespace detail {\nconstexpr int kConvMaxDecimalInShortestLow = -6;\n/// 10^kConvMaxDecimalInShortestLow. Replace with constexpr std::pow in C++26.\nconstexpr double kConvMaxDecimalInShortestLowValue = 0.000001;\nconstexpr int kConvMaxDecimalInShortestHigh = 21;\n/// 10^kConvMaxDecimalInShortestHigh. Replace with constexpr std::pow in C++26.\nconstexpr double kConvMaxDecimalInShortestHighValue =\n    1'000'000'000'000'000'000'000.0;\nconstexpr int kBase10MaximalLength = 17;\n\nconstexpr int kConvMaxFixedDigitsAfterPoint =\n    double_conversion::DoubleToStringConverter::kMaxFixedDigitsAfterPoint;\nconstexpr int kConvMaxPrecisionDigits =\n    double_conversion::DoubleToStringConverter::kMaxPrecisionDigits;\n\n/// Converts `DtoaMode` to\n/// `double_conversion::DoubleToStringConverter::DtoaMode`.\n/// This is temporary until\n/// `double_conversion::DoubleToStringConverter::DtoaMode` is removed.\nconstexpr double_conversion::DoubleToStringConverter::DtoaMode convert(\n    DtoaMode mode) {\n  switch (mode) {\n    case DtoaMode::SHORTEST:\n      return double_conversion::DoubleToStringConverter::SHORTEST;\n    case DtoaMode::SHORTEST_SINGLE:\n      return double_conversion::DoubleToStringConverter::SHORTEST_SINGLE;\n    case DtoaMode::FIXED:\n      return double_conversion::DoubleToStringConverter::FIXED;\n    case DtoaMode::PRECISION:\n      return double_conversion::DoubleToStringConverter::PRECISION;\n    default: /* unexpected */\n      assert(false);\n      // Default to PRECISION per existing behavior.\n      return double_conversion::DoubleToStringConverter::PRECISION;\n  }\n}\n\n/// Converts `DtoaFlags` to\n/// `double_conversion::DoubleToStringConverter::DtoaFlags`.\n/// This is temporary until\n/// `double_conversion::DoubleToStringConverter::DtoaFlags` is removed.\nconstexpr double_conversion::DoubleToStringConverter::Flags convert(\n    DtoaFlags flags) {\n  return static_cast<double_conversion::DoubleToStringConverter::Flags>(flags);\n}\n} // namespace detail\n\n/**\n * `numDigits` is only used with `FIXED` && `PRECISION`.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type\ntoAppend(\n    Src value,\n    Tgt* result,\n    DtoaMode mode,\n    unsigned int numDigits,\n    DtoaFlags flags = DtoaFlags::NO_FLAGS) {\n  double_conversion::DoubleToStringConverter::Flags dcFlags =\n      detail::convert(flags);\n  double_conversion::DoubleToStringConverter conv(\n      dcFlags,\n      \"Infinity\",\n      \"NaN\",\n      'E',\n      detail::kConvMaxDecimalInShortestLow,\n      detail::kConvMaxDecimalInShortestHigh,\n      6, // max leading padding zeros\n      1); // max trailing padding zeros\n  char buffer[256];\n  double_conversion::StringBuilder builder(buffer, sizeof(buffer));\n  double_conversion::DoubleToStringConverter::DtoaMode dcMode =\n      detail::convert(mode);\n  FOLLY_PUSH_WARNING\n  FOLLY_CLANG_DISABLE_WARNING(\"-Wcovered-switch-default\")\n  switch (dcMode) {\n    case double_conversion::DoubleToStringConverter::SHORTEST:\n      conv.ToShortest(value, &builder);\n      break;\n    case double_conversion::DoubleToStringConverter::SHORTEST_SINGLE:\n      conv.ToShortestSingle(static_cast<float>(value), &builder);\n      break;\n    case double_conversion::DoubleToStringConverter::FIXED:\n      conv.ToFixed(value, int(numDigits), &builder);\n      break;\n    case double_conversion::DoubleToStringConverter::PRECISION:\n    default:\n      assert(dcMode == double_conversion::DoubleToStringConverter::PRECISION);\n      conv.ToPrecision(value, int(numDigits), &builder);\n      break;\n  }\n  FOLLY_POP_WARNING\n  const size_t length = size_t(builder.position());\n  builder.Finalize();\n  result->append(buffer, length);\n}\n\n/**\n * As above, but for floating point\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_floating_point<Src>::value && IsSomeString<Tgt>::value>::type\ntoAppend(Src value, Tgt* result) {\n  toAppend(value, result, DtoaMode::SHORTEST, 0);\n}\n\n/**\n * Upper bound of the length of the output from\n * DoubleToStringConverter::ToShortest(double, StringBuilder*),\n * as used in toAppend(double, string*).\n */\ntemplate <class Src>\ntypename std::enable_if<std::is_floating_point<Src>::value, size_t>::type\nestimateSpaceNeeded(Src value) {\n  // kBase10MaximalLength is 17. We add 1 for decimal point,\n  // e.g. 10.0/9 is 17 digits and 18 characters, including the decimal point.\n  constexpr int kMaxMantissaSpace = detail::kBase10MaximalLength + 1;\n  // strlen(\"E-\") + digits10(numeric_limits<double>::max_exponent10)\n  constexpr int kMaxExponentSpace = 2 + 3;\n  static const int kMaxPositiveSpace = std::max({\n      // E.g. 1.1111111111111111E-100.\n      kMaxMantissaSpace + kMaxExponentSpace,\n      // E.g. 0.000001.1111111111111111, if kConvMaxDecimalInShortestLow is -6.\n      kMaxMantissaSpace - detail::kConvMaxDecimalInShortestLow,\n      // If kConvMaxDecimalInShortestHigh is 21, then 1e21 is the smallest\n      // number > 1 which ToShortest outputs in exponential notation,\n      // so 21 is the longest non-exponential number > 1.\n      detail::kConvMaxDecimalInShortestHigh,\n  });\n  return size_t(\n      kMaxPositiveSpace +\n      (value < 0 ? 1 : 0)); // +1 for minus sign, if negative\n}\n\ntemplate <class Src>\nconstexpr typename std::enable_if<\n    !std::is_fundamental<Src>::value &&\n#if FOLLY_HAVE_INT128_T\n        // On OSX 10.10, is_fundamental<__int128> is false :-O\n        !std::is_same<__int128, Src>::value &&\n        !std::is_same<unsigned __int128, Src>::value &&\n#endif\n        !IsSomeString<Src>::value &&\n        !std::is_convertible<Src, const char*>::value &&\n        !std::is_convertible<Src, StringPiece>::value &&\n        !std::is_enum<Src>::value,\n    size_t>::type\nestimateSpaceNeeded(const Src&) {\n  return sizeof(Src) + 1; // dumbest best effort ever?\n}\n\n#ifndef DOXYGEN_SHOULD_SKIP_THIS\nnamespace detail {\n\ntemplate <typename>\nstruct EstimateSpaceToReserveAll;\ntemplate <size_t... I>\nstruct EstimateSpaceToReserveAll<std::index_sequence<I...>> {\n  template <bool Tag, typename T>\n  FOLLY_ERASE static constexpr size_t one(const T& v) {\n    if constexpr (!Tag) {\n      return 0;\n    } else {\n      return estimateSpaceNeeded(v);\n    }\n  }\n\n  template <class... T>\n  static size_t call(const T&... v) {\n    const size_t sizes[] = {one<(I + 1 < sizeof...(I))>(v)...};\n    size_t size = 0;\n    for (const auto s : sizes) {\n      size += s;\n    }\n    return size;\n  }\n};\n\ntemplate <class O>\nvoid reserveInTarget(const O& o) {\n  (void)o;\n}\ntemplate <class T, class O>\nvoid reserveInTarget(const T& v, const O& o) {\n  o->reserve(estimateSpaceNeeded(v));\n}\ntemplate <class T0, class T1, class... Ts>\nvoid reserveInTarget(const T0& v0, const T1& v1, const Ts&... vs) {\n  using seq = std::index_sequence_for<T0, T1, Ts...>;\n  getLastElement(vs...)->reserve(\n      EstimateSpaceToReserveAll<seq>::call(v0, v1, vs...));\n}\n\ntemplate <class Delimiter, class... Ts>\nvoid reserveInTargetDelim(const Delimiter& d, const Ts&... vs) {\n  static_assert(sizeof...(vs) >= 2, \"Needs at least 2 args\");\n  using seq = std::index_sequence_for<Ts...>;\n  size_t fordelim = (sizeof...(vs) - 2) * estimateSpaceNeeded(d);\n  getLastElement(vs...)->reserve(\n      fordelim + EstimateSpaceToReserveAll<seq>::call(vs...));\n}\n\ntemplate <typename>\nstruct ToAppendStrImplAll;\ntemplate <size_t... I>\nstruct ToAppendStrImplAll<std::index_sequence<I...>> {\n  template <bool Tag, class T, class Tgt>\n  FOLLY_ERASE static void one(const T& v, Tgt* result) {\n    if constexpr (Tag) {\n      toAppend(v, result);\n    }\n  }\n\n  template <class... T>\n  static void call(const T&... v) {\n    auto r = getLastElement(v...);\n    ((one<I + 1 < sizeof...(T)>(v, r)), ...);\n  }\n};\n\ntemplate <typename>\nstruct ToAppendDelimStrImplAll;\ntemplate <size_t... I>\nstruct ToAppendDelimStrImplAll<std::index_sequence<I...>> {\n  template <size_t Tag, class Delimiter, class T, class Tgt>\n  FOLLY_ERASE static void one(const Delimiter& d, const T& v, Tgt* result) {\n    if constexpr (Tag >= 1) {\n      toAppend(v, result);\n    }\n    if constexpr (Tag >= 2) {\n      toAppend(d, result);\n    }\n  }\n\n  template <class Delimiter, class... T>\n  static void call(const Delimiter& d, const T&... v) {\n    static_assert(sizeof...(I) > 0);\n    constexpr size_t N = sizeof...(I) - 1;\n    auto r = detail::getLastElement(v...);\n    ((one<(N - I < 2 ? N - I : 2)>(d, v, r)), ...);\n  }\n};\ntemplate <\n    class Delimiter,\n    class T,\n    class... Ts,\n    std::enable_if_t<\n        sizeof...(Ts) >= 2 &&\n            IsSomeString<typename std::remove_pointer<\n                detail::LastElement<Ts...>>::type>::value,\n        int> = 0>\nvoid toAppendDelimStrImpl(const Delimiter& delim, const T& v, const Ts&... vs) {\n  using seq = std::index_sequence_for<T, Ts...>;\n  ToAppendDelimStrImplAll<seq>::call(delim, v, vs...);\n}\n} // namespace detail\n#endif\n\n/**\n * Variadic conversion to string. Appends each element in turn.\n * If we have two or more things to append, we will not reserve\n * the space for them and will depend on strings exponential growth.\n * If you just append once consider using toAppendFit which reserves\n * the space needed (but does not have exponential as a result).\n *\n * Custom implementations of toAppend() can be provided in the same namespace as\n * the type to customize printing. estimateSpaceNeed() may also be provided to\n * avoid reallocations in toAppendFit():\n *\n * namespace other_namespace {\n *\n * template <class String>\n * void toAppend(const OtherType&, String* out);\n *\n * // optional\n * size_t estimateSpaceNeeded(const OtherType&);\n *\n * }\n */\ntemplate <\n    class... Ts,\n    std::enable_if_t<\n        sizeof...(Ts) >= 3 &&\n            IsSomeString<typename std::remove_pointer<\n                detail::LastElement<Ts...>>::type>::value,\n        int> = 0>\nvoid toAppend(const Ts&... vs) {\n  using seq = std::index_sequence_for<Ts...>;\n  detail::ToAppendStrImplAll<seq>::call(vs...);\n}\n\n/**\n * @overloadbrief toAppend, but pre-allocate the exact amount of space required.\n *\n * Special version of the call that preallocates exactly as much memory\n * as need for arguments to be stored in target. This means we are\n * not doing exponential growth when we append. If you are using it\n * in a loop you are aiming at your foot with a big perf-destroying\n * bazooka.\n * On the other hand if you are appending to a string once, this\n * will probably save a few calls to malloc.\n */\ntemplate <\n    class... Ts,\n    std::enable_if_t<\n        IsSomeString<typename std::remove_pointer<\n            detail::LastElement<Ts...>>::type>::value,\n        int> = 0>\nvoid toAppendFit(const Ts&... vs) {\n  ::folly::detail::reserveInTarget(vs...);\n  toAppend(vs...);\n}\n\ntemplate <class Ts>\nvoid toAppendFit(const Ts&) {}\n\n/**\n * Variadic base case: do nothing.\n */\ntemplate <class Tgt>\ntypename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(\n    Tgt* /* result */) {}\n\n/**\n * @overloadbrief Use a specified delimiter between appendees.\n *\n * Variadic base case: do nothing.\n */\ntemplate <class Delimiter, class Tgt>\ntypename std::enable_if<IsSomeString<Tgt>::value>::type toAppendDelim(\n    const Delimiter& /* delim */, Tgt* /* result */) {}\n\n/**\n * 1 element: same as toAppend.\n */\ntemplate <class Delimiter, class T, class Tgt>\ntypename std::enable_if<IsSomeString<Tgt>::value>::type toAppendDelim(\n    const Delimiter& /* delim */, const T& v, Tgt* tgt) {\n  toAppend(v, tgt);\n}\n\n/**\n * Append to string with a delimiter in between elements. Check out\n * comments for toAppend for details about memory allocation.\n */\ntemplate <\n    class Delimiter,\n    class... Ts,\n    std::enable_if_t<\n        sizeof...(Ts) >= 3 &&\n            IsSomeString<typename std::remove_pointer<\n                detail::LastElement<Ts...>>::type>::value,\n        int> = 0>\nvoid toAppendDelim(const Delimiter& delim, const Ts&... vs) {\n  detail::toAppendDelimStrImpl(delim, vs...);\n}\n\n/**\n * @overloadbrief toAppend with custom delimiter and exact pre-allocation.\n *\n * Detail in comment for toAppendFit\n */\ntemplate <\n    class Delimiter,\n    class... Ts,\n    std::enable_if_t<\n        IsSomeString<typename std::remove_pointer<\n            detail::LastElement<Ts...>>::type>::value,\n        int> = 0>\nvoid toAppendDelimFit(const Delimiter& delim, const Ts&... vs) {\n  detail::reserveInTargetDelim(delim, vs...);\n  toAppendDelim(delim, vs...);\n}\n\ntemplate <class De, class Ts>\nvoid toAppendDelimFit(const De&, const Ts&) {}\n\n/**\n * to<SomeString>(v1, v2, ...) uses toAppend() (see below) as back-end\n * for all types.\n */\ntemplate <\n    class Tgt,\n    class... Ts,\n    std::enable_if_t<\n        IsSomeString<Tgt>::value &&\n            (sizeof...(Ts) != 1 ||\n             !std::is_same<Tgt, detail::LastElement<void, Ts...>>::value),\n        int> = 0>\nTgt to(const Ts&... vs) {\n  Tgt result;\n  toAppendFit(vs..., &result);\n  return result;\n}\n\n/**\n * Special version of to<SomeString> for floating point. When calling\n * folly::to<SomeString>(double), generic implementation above will\n * firstly reserve 24 (or 25 when negative value) bytes. This will\n * introduce a malloc call for most mainstream string implementations.\n *\n * But for most cases, a floating point doesn't need 24 (or 25) bytes to\n * be converted as a string.\n *\n * This special version will not do string reserve.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    IsSomeString<Tgt>::value && std::is_floating_point<Src>::value,\n    Tgt>::type\nto(Src value) {\n  Tgt result;\n  toAppend(value, &result);\n  return result;\n}\n\n/**\n * @overloadbrief Like `to`, but uses a custom delimiter.\n *\n * toDelim<SomeString>(SomeString str) returns itself.\n */\ntemplate <class Tgt, class Delim, class Src>\ntypename std::enable_if<\n    IsSomeString<Tgt>::value &&\n        std::is_same<Tgt, typename std::decay<Src>::type>::value,\n    Tgt>::type\ntoDelim(const Delim& /* delim */, Src&& value) {\n  return static_cast<Src&&>(value);\n}\n\n/**\n * toDelim<SomeString>(delim, v1, v2, ...) uses toAppendDelim() as\n * back-end for all types.\n */\ntemplate <\n    class Tgt,\n    class Delim,\n    class... Ts,\n    std::enable_if_t<\n        IsSomeString<Tgt>::value &&\n            (sizeof...(Ts) != 1 ||\n             !std::is_same<Tgt, detail::LastElement<void, Ts...>>::value),\n        int> = 0>\nTgt toDelim(const Delim& delim, const Ts&... vs) {\n  Tgt result;\n  toAppendDelimFit(delim, vs..., &result);\n  return result;\n}\n\n/**\n * Conversions from string types to integral types.\n */\n\nnamespace detail {\n\nExpected<bool, ConversionCode> str_to_bool(StringPiece* src) noexcept;\n\ntemplate <typename T>\nExpected<T, ConversionCode> str_to_floating(StringPiece* src) noexcept;\n\nextern template Expected<float, ConversionCode> str_to_floating<float>(\n    StringPiece* src) noexcept;\nextern template Expected<double, ConversionCode> str_to_floating<double>(\n    StringPiece* src) noexcept;\n\ntemplate <typename T>\nExpected<T, ConversionCode> str_to_floating_fast_float_from_chars(\n    StringPiece* src) noexcept;\n\nextern template Expected<float, ConversionCode>\nstr_to_floating_fast_float_from_chars<float>(StringPiece* src) noexcept;\nextern template Expected<double, ConversionCode>\nstr_to_floating_fast_float_from_chars<double>(StringPiece* src) noexcept;\n\ntemplate <class Tgt>\nExpected<Tgt, ConversionCode> digits_to(const char* b, const char* e) noexcept;\n\nextern template Expected<char, ConversionCode> digits_to<char>(\n    const char*, const char*) noexcept;\nextern template Expected<signed char, ConversionCode> digits_to<signed char>(\n    const char*, const char*) noexcept;\nextern template Expected<unsigned char, ConversionCode>\ndigits_to<unsigned char>(const char*, const char*) noexcept;\n\nextern template Expected<short, ConversionCode> digits_to<short>(\n    const char*, const char*) noexcept;\nextern template Expected<unsigned short, ConversionCode>\ndigits_to<unsigned short>(const char*, const char*) noexcept;\n\nextern template Expected<int, ConversionCode> digits_to<int>(\n    const char*, const char*) noexcept;\nextern template Expected<unsigned int, ConversionCode> digits_to<unsigned int>(\n    const char*, const char*) noexcept;\n\nextern template Expected<long, ConversionCode> digits_to<long>(\n    const char*, const char*) noexcept;\nextern template Expected<unsigned long, ConversionCode>\ndigits_to<unsigned long>(const char*, const char*) noexcept;\n\nextern template Expected<long long, ConversionCode> digits_to<long long>(\n    const char*, const char*) noexcept;\nextern template Expected<unsigned long long, ConversionCode>\ndigits_to<unsigned long long>(const char*, const char*) noexcept;\n\n#if FOLLY_HAVE_INT128_T\nextern template Expected<__int128, ConversionCode> digits_to<__int128>(\n    const char*, const char*) noexcept;\nextern template Expected<unsigned __int128, ConversionCode>\ndigits_to<unsigned __int128>(const char*, const char*) noexcept;\n#endif\n\ntemplate <class T>\nExpected<T, ConversionCode> str_to_integral(StringPiece* src) noexcept;\n\nextern template Expected<char, ConversionCode> str_to_integral<char>(\n    StringPiece* src) noexcept;\nextern template Expected<signed char, ConversionCode>\nstr_to_integral<signed char>(StringPiece* src) noexcept;\nextern template Expected<unsigned char, ConversionCode>\nstr_to_integral<unsigned char>(StringPiece* src) noexcept;\n\nextern template Expected<short, ConversionCode> str_to_integral<short>(\n    StringPiece* src) noexcept;\nextern template Expected<unsigned short, ConversionCode>\nstr_to_integral<unsigned short>(StringPiece* src) noexcept;\n\nextern template Expected<int, ConversionCode> str_to_integral<int>(\n    StringPiece* src) noexcept;\nextern template Expected<unsigned int, ConversionCode>\nstr_to_integral<unsigned int>(StringPiece* src) noexcept;\n\nextern template Expected<long, ConversionCode> str_to_integral<long>(\n    StringPiece* src) noexcept;\nextern template Expected<unsigned long, ConversionCode>\nstr_to_integral<unsigned long>(StringPiece* src) noexcept;\n\nextern template Expected<long long, ConversionCode> str_to_integral<long long>(\n    StringPiece* src) noexcept;\nextern template Expected<unsigned long long, ConversionCode>\nstr_to_integral<unsigned long long>(StringPiece* src) noexcept;\n\n#if FOLLY_HAVE_INT128_T\nextern template Expected<__int128, ConversionCode> str_to_integral<__int128>(\n    StringPiece* src) noexcept;\nextern template Expected<unsigned __int128, ConversionCode>\nstr_to_integral<unsigned __int128>(StringPiece* src) noexcept;\n#endif\n\ntemplate <typename T>\ntypename std::enable_if<\n    std::is_same<T, bool>::value,\n    Expected<T, ConversionCode>>::type convertTo(StringPiece* src) noexcept {\n  return str_to_bool(src);\n}\n\ntemplate <typename T>\ntypename std::enable_if<\n    std::is_floating_point<T>::value,\n    Expected<T, ConversionCode>>::type\nconvertTo(StringPiece* src) noexcept {\n  return str_to_floating<T>(src);\n}\n\ntemplate <typename T>\ntypename std::enable_if<\n    is_integral_v<T> && !std::is_same<T, bool>::value,\n    Expected<T, ConversionCode>>::type\nconvertTo(StringPiece* src) noexcept {\n  return str_to_integral<T>(src);\n}\n\n} // namespace detail\n\n/**\n * String represented as a pair of pointers to char to unsigned\n * integrals. Assumes NO whitespace before or after.\n */\ntemplate <typename Tgt>\ntypename std::enable_if<\n    is_integral_v<Tgt> && !std::is_same<Tgt, bool>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const char* b, const char* e) noexcept {\n  return detail::digits_to<Tgt>(b, e);\n}\n\ntemplate <typename Tgt>\ntypename std::enable_if< //\n    is_integral_v<Tgt> && !std::is_same<Tgt, bool>::value,\n    Tgt>::type\nto(const char* b, const char* e) {\n  return tryTo<Tgt>(b, e).thenOrThrow(identity, [=](ConversionCode code) {\n    return makeConversionError(code, StringPiece(b, e));\n  });\n}\n\n/**\n * Conversions from string types to arithmetic types.\n */\n\n/**\n * Parsing strings to numeric types.\n */\ntemplate <typename Tgt>\n[[nodiscard]] inline typename std::enable_if< //\n    is_arithmetic_v<Tgt>,\n    Expected<StringPiece, ConversionCode>>::type\nparseTo(StringPiece src, Tgt& out) {\n  return detail::convertTo<Tgt>(&src).then([&](Tgt res) {\n    return void(out = res), src;\n  });\n}\n\n/**\n * Integral / Floating Point to integral / Floating Point\n */\n\nnamespace detail {\n\n/**\n * Bool to integral/float doesn't need any special checks, and this\n * overload means we aren't trying to see if a bool is less than\n * an integer.\n */\ntemplate <class Tgt>\ntypename std::enable_if<\n    !std::is_same<Tgt, bool>::value &&\n        (is_integral_v<Tgt> || std::is_floating_point<Tgt>::value),\n    Expected<Tgt, ConversionCode>>::type\nconvertTo(const bool& value) noexcept {\n  return static_cast<Tgt>(value ? 1 : 0);\n}\n\n/**\n * Checked conversion from integral to integral. The checks are only\n * performed when meaningful, e.g. conversion from int to long goes\n * unchecked.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    is_integral_v<Src> && !std::is_same<Tgt, Src>::value &&\n        !std::is_same<Tgt, bool>::value && is_integral_v<Tgt>,\n    Expected<Tgt, ConversionCode>>::type\nconvertTo(const Src& value) noexcept {\n  if constexpr (\n      make_unsigned_t<Tgt>(std::numeric_limits<Tgt>::max()) <\n      make_unsigned_t<Src>(std::numeric_limits<Src>::max())) {\n    if (greater_than<Tgt, std::numeric_limits<Tgt>::max()>(value)) {\n      return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW);\n    }\n  }\n  if constexpr (\n      is_signed_v<Src> && (!is_signed_v<Tgt> || sizeof(Src) > sizeof(Tgt))) {\n    if (less_than<Tgt, std::numeric_limits<Tgt>::min()>(value)) {\n      return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW);\n    }\n  }\n  return static_cast<Tgt>(value);\n}\n\n/**\n * Checked conversion from floating to floating. The checks are only\n * performed when meaningful, e.g. conversion from float to double goes\n * unchecked.\n */\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_floating_point<Tgt>::value && std::is_floating_point<Src>::value &&\n        !std::is_same<Tgt, Src>::value,\n    Expected<Tgt, ConversionCode>>::type\nconvertTo(const Src& value) noexcept {\n  if (FOLLY_UNLIKELY(std::isinf(value))) {\n    return static_cast<Tgt>(value);\n  }\n  if constexpr (\n      std::numeric_limits<Tgt>::max() < std::numeric_limits<Src>::max()) {\n    if (value > std::numeric_limits<Tgt>::max()) {\n      return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW);\n    }\n    if (value < std::numeric_limits<Tgt>::lowest()) {\n      return makeUnexpected(ConversionCode::ARITH_NEGATIVE_OVERFLOW);\n    }\n  }\n  return static_cast<Tgt>(value);\n}\n\n/**\n * Check if a floating point value can safely be converted to an\n * integer value without triggering undefined behaviour.\n */\ntemplate <typename Tgt, typename Src>\ninline typename std::enable_if<\n    std::is_floating_point<Src>::value && is_integral_v<Tgt> &&\n        !std::is_same<Tgt, bool>::value,\n    bool>::type\ncheckConversion(const Src& value) {\n  constexpr Src tgtMaxAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::max());\n  constexpr Src tgtMinAsSrc = static_cast<Src>(std::numeric_limits<Tgt>::min());\n  // NOTE: The following two comparisons also handle the case where value is\n  // NaN, as all comparisons with NaN are false.\n  if (!(value < tgtMaxAsSrc)) {\n    if (!(value <= tgtMaxAsSrc)) {\n      return false;\n    }\n    const Src mmax = folly::nextafter(tgtMaxAsSrc, Src());\n    if (static_cast<Tgt>(value - mmax) >\n        std::numeric_limits<Tgt>::max() - static_cast<Tgt>(mmax)) {\n      return false;\n    }\n  } else if (value <= tgtMinAsSrc) {\n    if (value < tgtMinAsSrc) {\n      return false;\n    }\n    const Src mmin = folly::nextafter(tgtMinAsSrc, Src());\n    if (static_cast<Tgt>(value - mmin) <\n        std::numeric_limits<Tgt>::min() - static_cast<Tgt>(mmin)) {\n      return false;\n    }\n  }\n  return true;\n}\n\n// Integers can always safely be converted to floating point values\ntemplate <typename Tgt, typename Src>\nconstexpr typename std::enable_if<\n    is_integral_v<Src> && std::is_floating_point<Tgt>::value,\n    bool>::type\ncheckConversion(const Src&) {\n  return true;\n}\n\n// Also, floating point values can always be safely converted to bool\n// Per the standard, any floating point value that is not zero will yield true\ntemplate <typename Tgt, typename Src>\nconstexpr typename std::enable_if<\n    std::is_floating_point<Src>::value && std::is_same<Tgt, bool>::value,\n    bool>::type\ncheckConversion(const Src&) {\n  return true;\n}\n\n/**\n * Checked conversion from integral to floating point and back. The\n * result must be convertible back to the source type without loss of\n * precision. This seems Draconian but sometimes is what's needed, and\n * complements existing routines nicely. For various rounding\n * routines, see <math>.\n */\ntemplate <typename Tgt, typename Src>\ntypename std::enable_if<\n    (is_integral_v<Src> && std::is_floating_point<Tgt>::value) ||\n        (std::is_floating_point<Src>::value && is_integral_v<Tgt>),\n    Expected<Tgt, ConversionCode>>::type\nconvertTo(const Src& value) noexcept {\n  if (FOLLY_LIKELY(checkConversion<Tgt>(value))) {\n    Tgt result = static_cast<Tgt>(value);\n    if (FOLLY_LIKELY(checkConversion<Src>(result))) {\n      Src witness = static_cast<Src>(result);\n      if (FOLLY_LIKELY(value == witness)) {\n        return result;\n      }\n    }\n  }\n  return makeUnexpected(ConversionCode::ARITH_LOSS_OF_PRECISION);\n}\n\ntemplate <typename Tgt, typename Src>\ninline std::string errorValue(const Src& value) {\n  return to<std::string>(\"(\", pretty_name<Tgt>(), \") \", value);\n}\n\ntemplate <typename Tgt, typename Src>\nusing IsArithToArith = std::bool_constant<\n    !std::is_same<Tgt, Src>::value && !std::is_same<Tgt, bool>::value &&\n    is_arithmetic_v<Src> && is_arithmetic_v<Tgt>>;\n\n} // namespace detail\n\ntemplate <typename Tgt, typename Src>\ntypename std::enable_if<\n    detail::IsArithToArith<Tgt, Src>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const Src& value) noexcept {\n  return detail::convertTo<Tgt>(value);\n}\n\ntemplate <typename Tgt, typename Src>\ntypename std::enable_if<detail::IsArithToArith<Tgt, Src>::value, Tgt>::type to(\n    const Src& value) {\n  return tryTo<Tgt>(value).thenOrThrow(identity, [&](ConversionCode e) {\n    return makeConversionError(e, detail::errorValue<Tgt>(value));\n  });\n}\n\n/**\n * Custom Conversions\n *\n * Any type can be used with folly::to by implementing parseTo. The\n * implementation should be provided in the namespace of the type to facilitate\n * argument-dependent lookup:\n *\n * namespace other_namespace {\n * ::folly::Expected<::folly::StringPiece, SomeErrorCode>\n *   parseTo(::folly::StringPiece, OtherType&) noexcept;\n * }\n */\ntemplate <class T>\n[[nodiscard]] typename std::enable_if<\n    std::is_enum<T>::value,\n    Expected<StringPiece, ConversionCode>>::type\nparseTo(StringPiece in, T& out) noexcept {\n  typename std::underlying_type<T>::type tmp{};\n  auto restOrError = parseTo(in, tmp);\n  out = static_cast<T>(tmp); // Harmless if parseTo fails\n  return restOrError;\n}\n\n[[nodiscard]]\ninline Expected<StringPiece, ConversionCode> parseTo(\n    StringPiece in, StringPiece& out) noexcept {\n  out = in;\n  return StringPiece{in.end(), in.end()};\n}\n\nnamespace detail {\n\ntemplate <class Str>\nFOLLY_ERASE Expected<StringPiece, ConversionCode> parseToStr(\n    StringPiece in, Str& out) {\n  out.clear();\n  out.append(in.data(), in.size()); // TODO try/catch?\n  return StringPiece{in.end(), in.end()};\n}\n\n} // namespace detail\n\n[[nodiscard]]\ninline Expected<StringPiece, ConversionCode> parseTo(\n    StringPiece in, std::string& out) {\n  return detail::parseToStr(in, out);\n}\n\n[[nodiscard]]\ninline Expected<StringPiece, ConversionCode> parseTo(\n    StringPiece in, std::string_view& out) {\n  out = std::string_view(in.data(), in.size());\n  return StringPiece{in.end(), in.end()};\n}\n\n[[nodiscard]]\ninline Expected<StringPiece, ConversionCode> parseTo(\n    StringPiece in, fbstring& out) {\n  return detail::parseToStr(in, out);\n}\n\ntemplate <class Str>\n[[nodiscard]] inline typename std::enable_if<\n    IsSomeString<Str>::value,\n    Expected<StringPiece, ConversionCode>>::type\nparseTo(StringPiece in, Str& out) {\n  return detail::parseToStr(in, out);\n}\n\nnamespace detail {\ntemplate <typename Tgt>\nusing ParseToResult = decltype(parseTo(StringPiece{}, std::declval<Tgt&>()));\n\nstruct CheckTrailingSpace {\n  Expected<Unit, ConversionCode> operator()(StringPiece sp) const {\n    auto e = enforceWhitespaceErr(sp);\n    if (FOLLY_UNLIKELY(e != ConversionCode::SUCCESS)) {\n      return makeUnexpected(e);\n    }\n    return unit;\n  }\n};\n\ntemplate <class Error>\nstruct ReturnUnit {\n  template <class T>\n  constexpr Expected<Unit, Error> operator()(T&&) const {\n    return unit;\n  }\n};\n\n// Older versions of the parseTo customization point threw on error and\n// returned void. Handle that.\ntemplate <class Tgt>\ninline typename std::enable_if<\n    std::is_void<ParseToResult<Tgt>>::value,\n    Expected<StringPiece, ConversionCode>>::type\nparseToWrap(StringPiece sp, Tgt& out) {\n  parseTo(sp, out);\n  return StringPiece(sp.end(), sp.end());\n}\n\ntemplate <class Tgt>\ninline typename std::enable_if<\n    !std::is_void<ParseToResult<Tgt>>::value,\n    ParseToResult<Tgt>>::type\nparseToWrap(StringPiece sp, Tgt& out) {\n  return parseTo(sp, out);\n}\n\ntemplate <typename Tgt>\nusing ParseToError = ExpectedErrorType<decltype(detail::parseToWrap(\n    StringPiece{}, std::declval<Tgt&>()))>;\n\n} // namespace detail\n\n/**\n * String or StringPiece to target conversion. Accepts leading and trailing\n * whitespace, but no non-space trailing characters.\n */\n\ntemplate <class Tgt>\ninline typename std::enable_if<\n    !std::is_same<StringPiece, Tgt>::value,\n    Expected<Tgt, detail::ParseToError<Tgt>>>::type\ntryTo(StringPiece src) noexcept {\n  Tgt result{};\n  using Error = detail::ParseToError<Tgt>;\n  using Check = typename std::conditional<\n      is_arithmetic_v<Tgt>,\n      detail::CheckTrailingSpace,\n      detail::ReturnUnit<Error>>::type;\n  return parseTo(src, result).then(Check(), [&](Unit) {\n    return std::move(result);\n  });\n}\n\ntemplate <class Tgt, class Src>\ninline typename std::enable_if<\n    IsSomeString<Src>::value && !std::is_same<StringPiece, Tgt>::value,\n    Tgt>::type\nto(Src const& src) {\n  return to<Tgt>(StringPiece(src.data(), src.size()));\n}\n\ntemplate <class Tgt>\ninline\n    typename std::enable_if<!std::is_same<StringPiece, Tgt>::value, Tgt>::type\n    to(StringPiece src) {\n  Tgt result{};\n  using Error = detail::ParseToError<Tgt>;\n  using Check = typename std::conditional<\n      is_arithmetic_v<Tgt>,\n      detail::CheckTrailingSpace,\n      detail::ReturnUnit<Error>>::type;\n  auto tmp = detail::parseToWrap(src, result);\n  return tmp\n      .thenOrThrow(\n          Check(),\n          [&](Error e) { throw_exception(makeConversionError(e, src)); })\n      .thenOrThrow(\n          [&](Unit) { return std::move(result); },\n          [&](Error e) {\n            throw_exception(makeConversionError(e, tmp.value()));\n          });\n}\n\n/**\n * tryTo/to that take the strings by pointer so the caller gets information\n * about how much of the string was consumed by the conversion. These do not\n * check for trailing whitespace.\n */\ntemplate <class Tgt>\nExpected<Tgt, detail::ParseToError<Tgt>> tryTo(StringPiece* src) noexcept {\n  Tgt result;\n  return parseTo(*src, result).then([&, src](StringPiece sp) -> Tgt {\n    *src = sp;\n    return std::move(result);\n  });\n}\n\ntemplate <class Tgt>\nTgt to(StringPiece* src) {\n  Tgt result{};\n  using Error = detail::ParseToError<Tgt>;\n  return parseTo(*src, result)\n      .thenOrThrow(\n          [&, src](StringPiece sp) -> Tgt {\n            *src = sp;\n            return std::move(result);\n          },\n          [=](Error e) { return makeConversionError(e, *src); });\n}\n\n/**\n * Enum to anything and back\n */\n\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value &&\n        !std::is_convertible<Tgt, StringPiece>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const Src& value) noexcept {\n  return tryTo<Tgt>(to_underlying(value));\n}\n\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    !std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value &&\n        !std::is_same<Src, Tgt>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const Src& value) noexcept {\n  using I = typename std::underlying_type<Tgt>::type;\n  return tryTo<I>(value).then([](I i) { return static_cast<Tgt>(i); });\n}\n\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    std::is_enum<Src>::value && !std::is_same<Src, Tgt>::value &&\n        !std::is_convertible<Tgt, StringPiece>::value,\n    Tgt>::type\nto(const Src& value) {\n  return to<Tgt>(to_underlying(value));\n}\n\ntemplate <class Tgt, class Src>\ntypename std::enable_if<\n    !std::is_convertible<Src, StringPiece>::value && std::is_enum<Tgt>::value &&\n        !std::is_same<Src, Tgt>::value,\n    Tgt>::type\nto(const Src& value) {\n  return static_cast<Tgt>(to<typename std::underlying_type<Tgt>::type>(value));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/CppAttributes.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * GCC compatible wrappers around clang attributes.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n\n#ifndef __has_attribute\n#define FOLLY_HAS_ATTRIBUTE(x) 0\n#else\n#define FOLLY_HAS_ATTRIBUTE(x) __has_attribute(x)\n#endif\n\n#ifndef __has_cpp_attribute\n#define FOLLY_HAS_CPP_ATTRIBUTE(x) 0\n#else\n#define FOLLY_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)\n#endif\n\n#ifndef __has_extension\n#define FOLLY_HAS_EXTENSION(x) 0\n#else\n#define FOLLY_HAS_EXTENSION(x) __has_extension(x)\n#endif\n\n/**\n * Nullable indicates that a return value or a parameter may be a `nullptr`,\n * e.g.\n *\n * int* FOLLY_NULLABLE foo(int* a, int* FOLLY_NULLABLE b) {\n *   if (*a > 0) {  // safe dereference\n *     return nullptr;\n *   }\n *   if (*b < 0) {  // unsafe dereference\n *     return *a;\n *   }\n *   if (b != nullptr && *b == 1) {  // safe checked dereference\n *     return new int(1);\n *   }\n *   return nullptr;\n * }\n *\n * Ignores Clang's -Wnullability-extension since it correctly handles the case\n * where the extension is not present.\n */\n#if FOLLY_HAS_EXTENSION(nullability)\n#define FOLLY_NULLABLE                                   \\\n  FOLLY_PUSH_WARNING                                     \\\n  FOLLY_CLANG_DISABLE_WARNING(\"-Wnullability-extension\") \\\n  _Nullable FOLLY_POP_WARNING\n#define FOLLY_NONNULL                                    \\\n  FOLLY_PUSH_WARNING                                     \\\n  FOLLY_CLANG_DISABLE_WARNING(\"-Wnullability-extension\") \\\n  _Nonnull FOLLY_POP_WARNING\n#else\n#define FOLLY_NULLABLE\n#define FOLLY_NONNULL\n#endif\n\n/**\n * \"Cold\" indicates to the compiler that a function is only expected to be\n * called from unlikely code paths. It can affect decisions made by the\n * optimizer both when processing the function body and when analyzing\n * call-sites.\n */\n#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::cold)\n#define FOLLY_ATTR_GNU_COLD gnu::cold\n#else\n#define FOLLY_ATTR_GNU_COLD\n#endif\n\n/// FOLLY_ATTR_MAYBE_UNUSED_IF_NDEBUG\n///\n/// When defined(NDEBUG), expands to maybe_unused; otherwise, expands to empty.\n/// Useful for marking variables that are used, in the sense checked for by the\n/// attribute maybe_unused, only in debug builds.\n#if defined(NDEBUG)\n#define FOLLY_ATTR_MAYBE_UNUSED_IF_NDEBUG maybe_unused\n#else\n#define FOLLY_ATTR_MAYBE_UNUSED_IF_NDEBUG\n#endif\n\n/**\n *  no_unique_address indicates that a member variable can be optimized to\n * occupy no space, rather than the minimum 1-byte used by default.\n *\n *  class Empty {};\n *\n *  class NonEmpty1 {\n *    [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] Empty e;\n *    int f;\n *  };\n *\n *  class NonEmpty2 {\n *    Empty e;\n *    int f;\n *  };\n *\n *  sizeof(NonEmpty1); // may be == sizeof(int)\n *  sizeof(NonEmpty2); // must be > sizeof(int)\n */\n#if FOLLY_HAS_CPP_ATTRIBUTE(no_unique_address)\n#define FOLLY_ATTR_NO_UNIQUE_ADDRESS no_unique_address\n#elif FOLLY_HAS_CPP_ATTRIBUTE(msvc::no_unique_address)\n#define FOLLY_ATTR_NO_UNIQUE_ADDRESS msvc::no_unique_address\n#else\n#define FOLLY_ATTR_NO_UNIQUE_ADDRESS\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::no_destroy)\n#define FOLLY_ATTR_CLANG_NO_DESTROY clang::no_destroy\n#else\n#define FOLLY_ATTR_CLANG_NO_DESTROY\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::uninitialized)\n#define FOLLY_ATTR_CLANG_UNINITIALIZED clang::uninitialized\n#else\n#define FOLLY_ATTR_CLANG_UNINITIALIZED\n#endif\n\n/**\n * Accesses to objects with types with this attribute are not subjected to\n * type-based alias analysis, but are instead assumed to be able to alias any\n * other type of objects, just like the char type.\n */\n#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::may_alias)\n#define FOLLY_ATTR_GNU_MAY_ALIAS gnu::may_alias\n#else\n#define FOLLY_ATTR_GNU_MAY_ALIAS\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::pure)\n#define FOLLY_ATTR_GNU_PURE gnu::pure\n#else\n#define FOLLY_ATTR_GNU_PURE\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::preserve_most)\n#define FOLLY_ATTR_CLANG_PRESERVE_MOST clang::preserve_most\n#else\n#define FOLLY_ATTR_CLANG_PRESERVE_MOST\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::preserve_all)\n#define FOLLY_ATTR_CLANG_PRESERVE_ALL clang::preserve_all\n#else\n#define FOLLY_ATTR_CLANG_PRESERVE_ALL\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::used)\n#define FOLLY_ATTR_GNU_USED gnu::used\n#else\n#define FOLLY_ATTR_GNU_USED\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::retain)\n#define FOLLY_ATTR_GNU_RETAIN gnu::retain\n#else\n#define FOLLY_ATTR_GNU_RETAIN\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::noclone)\n#define FOLLY_ATTR_GNU_NOCLONE gnu::noclone\n#else\n#define FOLLY_ATTR_GNU_NOCLONE\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(gnu::flatten)\n#define FOLLY_ATTR_GNU_FLATTEN gnu::flatten\n#else\n#define FOLLY_ATTR_GNU_FLATTEN\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::lifetimebound)\n#define FOLLY_ATTR_CLANG_LIFETIMEBOUND clang::lifetimebound\n#else\n#define FOLLY_ATTR_CLANG_LIFETIMEBOUND\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::coro_await_elidable)\n#define FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE clang::coro_await_elidable\n#else\n#define FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::coro_await_elidable_argument)\n#define FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT \\\n  clang::coro_await_elidable_argument\n#else\n#define FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT\n#endif\n\n#if FOLLY_HAS_CPP_ATTRIBUTE(clang::no_thread_safety_analysis)\n#define FOLLY_ATTR_CLANG_NO_THREAD_SAFETY_ANALYSIS \\\n  clang::no_thread_safety_analysis\n#else\n#define FOLLY_ATTR_CLANG_NO_THREAD_SAFETY_ANALYSIS\n#endif\n"
  },
  {
    "path": "folly/CpuId.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <cstring>\n\n#include <folly/Portability.h>\n#include <folly/system/arch/x86.h>\n\nnamespace folly {\n\n/**\n * Identification of an Intel CPU.\n * Supports CPUID feature flags (EAX=1) and extended features (EAX=7, ECX=0).\n * Values from\n * http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html\n */\nclass CpuId {\n public:\n  // Always inline in order for this to be usable from a __ifunc__.\n  // In shared library mode, a __ifunc__ runs at relocation time, while the\n  // PLT hasn't been fully populated yet; thus, ifuncs cannot use symbols\n  // with potentially external linkage. (This issue is less likely in opt\n  // mode since inlining happens more likely, and it doesn't happen for\n  // statically linked binaries which don't depend on the PLT)\n  FOLLY_ALWAYS_INLINE CpuId() {\n    unsigned int reg[4];\n    x86_cpuid(reg, 0);\n    vendor_[0] = reg[1];\n    vendor_[1] = reg[3];\n    vendor_[2] = reg[2];\n    const int n = reg[0];\n    if (n >= 1) {\n      x86_cpuid(reg, 1);\n      f1c_ = reg[2];\n      f1d_ = reg[3];\n    }\n    if (n >= 7) {\n      x86_cpuid(reg, 7);\n      f7b_ = reg[1];\n      f7c_ = reg[2];\n      f7d_ = reg[3];\n    }\n  }\n\n#define FOLLY_DETAIL_CPUID_X(name, r, bit) \\\n  FOLLY_ALWAYS_INLINE bool name() const { return ((r) & (1U << bit)) != 0; }\n\n// cpuid(1): Processor Info and Feature Bits.\n#define FOLLY_DETAIL_CPUID_C(name, bit) FOLLY_DETAIL_CPUID_X(name, f1c_, bit)\n  FOLLY_DETAIL_CPUID_C(sse3, 0)\n  FOLLY_DETAIL_CPUID_C(pclmuldq, 1)\n  FOLLY_DETAIL_CPUID_C(dtes64, 2)\n  FOLLY_DETAIL_CPUID_C(monitor, 3)\n  FOLLY_DETAIL_CPUID_C(dscpl, 4)\n  FOLLY_DETAIL_CPUID_C(vmx, 5)\n  FOLLY_DETAIL_CPUID_C(smx, 6)\n  FOLLY_DETAIL_CPUID_C(eist, 7)\n  FOLLY_DETAIL_CPUID_C(tm2, 8)\n  FOLLY_DETAIL_CPUID_C(ssse3, 9)\n  FOLLY_DETAIL_CPUID_C(cnxtid, 10)\n  FOLLY_DETAIL_CPUID_C(fma, 12)\n  FOLLY_DETAIL_CPUID_C(cx16, 13)\n  FOLLY_DETAIL_CPUID_C(xtpr, 14)\n  FOLLY_DETAIL_CPUID_C(pdcm, 15)\n  FOLLY_DETAIL_CPUID_C(pcid, 17)\n  FOLLY_DETAIL_CPUID_C(dca, 18)\n  FOLLY_DETAIL_CPUID_C(sse41, 19)\n  FOLLY_DETAIL_CPUID_C(sse42, 20)\n  FOLLY_DETAIL_CPUID_C(x2apic, 21)\n  FOLLY_DETAIL_CPUID_C(movbe, 22)\n  FOLLY_DETAIL_CPUID_C(popcnt, 23)\n  FOLLY_DETAIL_CPUID_C(tscdeadline, 24)\n  FOLLY_DETAIL_CPUID_C(aes, 25)\n  FOLLY_DETAIL_CPUID_C(xsave, 26)\n  FOLLY_DETAIL_CPUID_C(osxsave, 27)\n  FOLLY_DETAIL_CPUID_C(avx, 28)\n  FOLLY_DETAIL_CPUID_C(f16c, 29)\n  FOLLY_DETAIL_CPUID_C(rdrand, 30)\n#undef FOLLY_DETAIL_CPUID_C\n#define FOLLY_DETAIL_CPUID_D(name, bit) FOLLY_DETAIL_CPUID_X(name, f1d_, bit)\n  FOLLY_DETAIL_CPUID_D(fpu, 0)\n  FOLLY_DETAIL_CPUID_D(vme, 1)\n  FOLLY_DETAIL_CPUID_D(de, 2)\n  FOLLY_DETAIL_CPUID_D(pse, 3)\n  FOLLY_DETAIL_CPUID_D(tsc, 4)\n  FOLLY_DETAIL_CPUID_D(msr, 5)\n  FOLLY_DETAIL_CPUID_D(pae, 6)\n  FOLLY_DETAIL_CPUID_D(mce, 7)\n  FOLLY_DETAIL_CPUID_D(cx8, 8)\n  FOLLY_DETAIL_CPUID_D(apic, 9)\n  FOLLY_DETAIL_CPUID_D(sep, 11)\n  FOLLY_DETAIL_CPUID_D(mtrr, 12)\n  FOLLY_DETAIL_CPUID_D(pge, 13)\n  FOLLY_DETAIL_CPUID_D(mca, 14)\n  FOLLY_DETAIL_CPUID_D(cmov, 15)\n  FOLLY_DETAIL_CPUID_D(pat, 16)\n  FOLLY_DETAIL_CPUID_D(pse36, 17)\n  FOLLY_DETAIL_CPUID_D(psn, 18)\n  FOLLY_DETAIL_CPUID_D(clfsh, 19)\n  FOLLY_DETAIL_CPUID_D(ds, 21)\n  FOLLY_DETAIL_CPUID_D(acpi, 22)\n  FOLLY_DETAIL_CPUID_D(mmx, 23)\n  FOLLY_DETAIL_CPUID_D(fxsr, 24)\n  FOLLY_DETAIL_CPUID_D(sse, 25)\n  FOLLY_DETAIL_CPUID_D(sse2, 26)\n  FOLLY_DETAIL_CPUID_D(ss, 27)\n  FOLLY_DETAIL_CPUID_D(htt, 28)\n  FOLLY_DETAIL_CPUID_D(tm, 29)\n  FOLLY_DETAIL_CPUID_D(pbe, 31)\n#undef FOLLY_DETAIL_CPUID_D\n\n  // cpuid(7): Extended Features.\n#define FOLLY_DETAIL_CPUID_B(name, bit) FOLLY_DETAIL_CPUID_X(name, f7b_, bit)\n  FOLLY_DETAIL_CPUID_B(bmi1, 3)\n  FOLLY_DETAIL_CPUID_B(hle, 4)\n  FOLLY_DETAIL_CPUID_B(avx2, 5)\n  FOLLY_DETAIL_CPUID_B(smep, 7)\n  FOLLY_DETAIL_CPUID_B(bmi2, 8)\n  FOLLY_DETAIL_CPUID_B(erms, 9)\n  FOLLY_DETAIL_CPUID_B(invpcid, 10)\n  FOLLY_DETAIL_CPUID_B(rtm, 11)\n  FOLLY_DETAIL_CPUID_B(mpx, 14)\n  FOLLY_DETAIL_CPUID_B(avx512f, 16)\n  FOLLY_DETAIL_CPUID_B(avx512dq, 17)\n  FOLLY_DETAIL_CPUID_B(rdseed, 18)\n  FOLLY_DETAIL_CPUID_B(adx, 19)\n  FOLLY_DETAIL_CPUID_B(smap, 20)\n  FOLLY_DETAIL_CPUID_B(avx512ifma, 21)\n  FOLLY_DETAIL_CPUID_B(pcommit, 22)\n  FOLLY_DETAIL_CPUID_B(clflushopt, 23)\n  FOLLY_DETAIL_CPUID_B(clwb, 24)\n  FOLLY_DETAIL_CPUID_B(avx512pf, 26)\n  FOLLY_DETAIL_CPUID_B(avx512er, 27)\n  FOLLY_DETAIL_CPUID_B(avx512cd, 28)\n  FOLLY_DETAIL_CPUID_B(sha, 29)\n  FOLLY_DETAIL_CPUID_B(avx512bw, 30)\n  FOLLY_DETAIL_CPUID_B(avx512vl, 31)\n#undef FOLLY_DETAIL_CPUID_B\n#define FOLLY_DETAIL_CPUID_C(name, bit) FOLLY_DETAIL_CPUID_X(name, f7c_, bit)\n  FOLLY_DETAIL_CPUID_C(prefetchwt1, 0)\n  FOLLY_DETAIL_CPUID_C(avx512vbmi, 1)\n  FOLLY_DETAIL_CPUID_C(avx512vbmi2, 6)\n  FOLLY_DETAIL_CPUID_C(vaes, 9)\n  FOLLY_DETAIL_CPUID_C(vpclmulqdq, 10)\n  FOLLY_DETAIL_CPUID_C(avx512vnni, 11)\n  FOLLY_DETAIL_CPUID_C(avx512bitalg, 12)\n  FOLLY_DETAIL_CPUID_C(avx512vpopcntdq, 14)\n  FOLLY_DETAIL_CPUID_C(rdpid, 22)\n#undef FOLLY_DETAIL_CPUID_C\n#define FOLLY_DETAIL_CPUID_D(name, bit) FOLLY_DETAIL_CPUID_X(name, f7d_, bit)\n  FOLLY_DETAIL_CPUID_D(avx5124vnniw, 2)\n  FOLLY_DETAIL_CPUID_D(avx5124fmaps, 3)\n  FOLLY_DETAIL_CPUID_D(avx512vp2intersect, 8)\n  FOLLY_DETAIL_CPUID_D(amxbf16, 22)\n  FOLLY_DETAIL_CPUID_D(avx512fp16, 23)\n  FOLLY_DETAIL_CPUID_D(amxtile, 24)\n  FOLLY_DETAIL_CPUID_D(amxint8, 25)\n#undef FOLLY_DETAIL_CPUID_D\n\n#undef FOLLY_DETAIL_CPUID_X\n\n#define FOLLY_DETAIL_VENDOR(name, str)                         \\\n  FOLLY_ALWAYS_INLINE bool vendor_##name() const {             \\\n    /* Size of str should be 12 + NUL terminator. */           \\\n    static_assert(sizeof(str) == 13, \"Bad CPU Vendor string\"); \\\n    /* Just as with the main CpuId call above, this can also   \\\n    still be in an __ifunc__, so no function calls :( */       \\\n    return memcmp(&vendor_[0], &str[0], 12) == 0;              \\\n  }\n\n  FOLLY_DETAIL_VENDOR(intel, \"GenuineIntel\")\n  FOLLY_DETAIL_VENDOR(amd, \"AuthenticAMD\")\n\n#undef FOLLY_DETAIL_VENDOR\n\n private:\n  uint32_t vendor_[3] = {0};\n  uint32_t f1c_ = 0;\n  uint32_t f1d_ = 0;\n  uint32_t f7b_ = 0;\n  uint32_t f7c_ = 0;\n  uint32_t f7d_ = 0;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/DefaultKeepAliveExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <future>\n\n#include <glog/logging.h>\n\n#include <folly/Executor.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/synchronization/Baton.h>\n\nnamespace folly {\n\n/// An Executor accepts units of work with add(), which should be\n/// threadsafe.\nclass DefaultKeepAliveExecutor : public virtual Executor {\n public:\n  virtual ~DefaultKeepAliveExecutor() override { DCHECK(!keepAlive_); }\n\n  template <typename ExecutorT>\n  static auto getWeakRef(ExecutorT& executor) {\n    static_assert(\n        std::is_base_of<DefaultKeepAliveExecutor, ExecutorT>::value,\n        \"getWeakRef only works for folly::DefaultKeepAliveExecutor implementations.\");\n    using WeakRefExecutorType = std::conditional_t<\n        std::is_base_of<SequencedExecutor, ExecutorT>::value,\n        SequencedExecutor,\n        Executor>;\n    return WeakRef<WeakRefExecutorType>::create(\n        executor.controlBlock_, &executor);\n  }\n\n  folly::Executor::KeepAlive<> weakRef() { return getWeakRef(*this); }\n\n  class WeakRefExecutor : public virtual Executor {};\n\n protected:\n  void joinKeepAlive() {\n    DCHECK(keepAlive_);\n    keepAlive_.reset();\n    keepAliveReleaseBaton_.wait();\n  }\n\n  void joinAndResetKeepAlive() {\n    joinKeepAlive();\n    auto keepAliveCount =\n        controlBlock_->keepAliveCount_.exchange(1, std::memory_order_relaxed);\n    DCHECK_EQ(keepAliveCount, 0);\n    keepAliveReleaseBaton_.reset();\n    keepAlive_ = makeKeepAlive(this);\n  }\n\n private:\n  struct ControlBlock {\n    std::atomic<ptrdiff_t> keepAliveCount_{1};\n  };\n\n  template <typename ExecutorT = Executor>\n  class WeakRef : public virtual ExecutorT, public virtual WeakRefExecutor {\n   public:\n    static folly::Executor::KeepAlive<ExecutorT> create(\n        std::shared_ptr<ControlBlock> controlBlock, ExecutorT* executor) {\n      return makeKeepAlive(new WeakRef(std::move(controlBlock), executor));\n    }\n\n    void add(Func f) override {\n      if (auto executor = lock()) {\n        executor->add(std::move(f));\n      }\n    }\n\n    void addWithPriority(Func f, int8_t priority) override {\n      if (auto executor = lock()) {\n        executor->addWithPriority(std::move(f), priority);\n      }\n    }\n\n    virtual uint8_t getNumPriorities() const override { return numPriorities_; }\n\n   private:\n    WeakRef(std::shared_ptr<ControlBlock> controlBlock, ExecutorT* executor)\n        : controlBlock_(std::move(controlBlock)),\n          executor_(executor),\n          numPriorities_(executor->getNumPriorities()) {}\n\n    bool keepAliveAcquire() noexcept override {\n      auto keepAliveCount =\n          keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n      // We should never increment from 0\n      DCHECK(keepAliveCount > 0);\n      return true;\n    }\n\n    void keepAliveRelease() noexcept override {\n      auto keepAliveCount =\n          keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);\n      DCHECK(keepAliveCount >= 1);\n\n      if (keepAliveCount == 1) {\n        delete this;\n      }\n    }\n\n    folly::Executor::KeepAlive<ExecutorT> lock() {\n      auto controlBlock =\n          controlBlock_->keepAliveCount_.load(std::memory_order_relaxed);\n      do {\n        if (controlBlock == 0) {\n          return {};\n        }\n      } while (!controlBlock_->keepAliveCount_.compare_exchange_weak(\n          controlBlock,\n          controlBlock + 1,\n          std::memory_order_release,\n          std::memory_order_relaxed));\n\n      return makeKeepAlive<ExecutorT>(executor_);\n    }\n\n    std::atomic<size_t> keepAliveCount_{1};\n\n    std::shared_ptr<ControlBlock> controlBlock_;\n    ExecutorT* executor_;\n\n    uint8_t numPriorities_;\n  };\n\n  bool keepAliveAcquire() noexcept override {\n    auto keepAliveCount =\n        controlBlock_->keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    // We should never increment from 0\n    DCHECK(keepAliveCount > 0);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    auto keepAliveCount =\n        controlBlock_->keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);\n    DCHECK(keepAliveCount >= 1);\n\n    if (keepAliveCount == 1) {\n      keepAliveReleaseBaton_.post(); // std::memory_order_release\n    }\n  }\n\n  std::shared_ptr<ControlBlock> controlBlock_{std::make_shared<ControlBlock>()};\n  Baton<> keepAliveReleaseBaton_;\n  KeepAlive<DefaultKeepAliveExecutor> keepAlive_{makeKeepAlive(this)};\n};\n\ntemplate <typename ExecutorT>\nauto getWeakRef(ExecutorT& executor) {\n  static_assert(\n      std::is_base_of<DefaultKeepAliveExecutor, ExecutorT>::value,\n      \"getWeakRef only works for folly::DefaultKeepAliveExecutor implementations.\");\n  return DefaultKeepAliveExecutor::getWeakRef(executor);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Demangle.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Demangle.h>\n\n#include <algorithm>\n#include <cstring>\n\n#include <folly/CPortability.h>\n#include <folly/Utility.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/CString.h>\n\n#if __has_include(<cxxabi.h>)\n#include <cxxabi.h> // @donotremove\n#endif\n\n//  The headers <libiberty.h> (binutils) and <string.h> (glibc) both declare the\n//  symbol basename. Unfortunately, the declarations are different. So including\n//  both headers in the same translation unit fails due to the two conflicting\n//  declarations. Since <demangle.h> includes <libiberty.h> we must be careful.\n#if __has_include(<demangle.h>)\n#pragma push_macro(\"HAVE_DECL_BASENAME\")\n#define HAVE_DECL_BASENAME 1\n#include <demangle.h> // @manual\n#pragma pop_macro(\"HAVE_DECL_BASENAME\")\n#endif\n\n//  try to find cxxabi demangle\n//\n//  prefer using a weakref\n\n#if __has_include(<cxxabi.h>)\n\n[[gnu::weakref(\"__cxa_demangle\")]] static char* cxxabi_demangle(\n    char const*, char*, size_t*, int*);\n\n#else // __has_include(<cxxabi.h>)\n\nstatic constexpr auto cxxabi_demangle = static_cast<char* (*)(...)>(nullptr);\n\n#endif // __has_include(<cxxabi.h>)\n\n//  try to find liberty demangle\n//\n//  cannot use a weak symbol since it may be the only referenced symbol in\n//  liberty\n//\n//  in contrast with cxxabi, where there are certainly other referenced symbols\n//\n//  for rust_demangle_callback, detect its declaration in the header\n\n#if __has_include(<demangle.h>)\n\nnamespace {\nstruct poison {};\n\nFOLLY_PUSH_WARNING\nFOLLY_GCC_DISABLE_WARNING(\"-Wunused-function\")\n[[maybe_unused]] void rust_demangle_callback(poison);\nFOLLY_POP_WARNING\n\n[[maybe_unused]] FOLLY_ERASE int rust_demangle_callback_fallback(\n    const char*, int, demangle_callbackref, void*) {\n  return 0;\n}\n\nFOLLY_CREATE_QUAL_INVOKER(\n    invoke_rust_demangle_primary, ::rust_demangle_callback);\nFOLLY_CREATE_QUAL_INVOKER(\n    invoke_rust_demangle_fallback, rust_demangle_callback_fallback);\n\nusing invoke_rust_demangle_fn = folly::invoke_first_match<\n    invoke_rust_demangle_primary,\n    invoke_rust_demangle_fallback>;\nconstexpr invoke_rust_demangle_fn invoke_rust_demangle;\n\nint call_rust_demangle_callback(\n    const char* mangled, int options, demangle_callbackref cb, void* opaque) {\n  return invoke_rust_demangle(mangled, options, cb, opaque);\n}\n\n} // namespace\n\nusing liberty_demangle_t = int(const char*, int, demangle_callbackref, void*);\n\nstatic constexpr liberty_demangle_t* liberty_cplus_demangle =\n    cplus_demangle_v3_callback;\nstatic constexpr liberty_demangle_t* liberty_rust_demangle =\n    call_rust_demangle_callback;\n\n#if defined(DMGL_NO_RECURSE_LIMIT)\nstatic constexpr auto liberty_demangle_options_no_recurse_limit =\n    DMGL_NO_RECURSE_LIMIT;\n#else\nstatic constexpr auto liberty_demangle_options_no_recurse_limit = 0;\n#endif\n\nstatic constexpr auto liberty_demangle_options = //\n    DMGL_PARAMS | DMGL_ANSI | DMGL_TYPES | //\n    liberty_demangle_options_no_recurse_limit;\n\n#else // __has_include(<demangle.h>)\n\nusing liberty_demangle_t = int(...);\n\nstatic constexpr liberty_demangle_t* liberty_cplus_demangle = nullptr;\nstatic constexpr liberty_demangle_t* liberty_rust_demangle = nullptr;\nstatic constexpr auto liberty_demangle_options = 0;\n\n#endif // __has_include(<demangle.h>)\n\n//  implementations\n\nnamespace folly {\n\nbool demangle_build_has_cxxabi() noexcept {\n  return to_bool(cxxabi_demangle);\n}\nbool demangle_build_has_liberty() noexcept {\n  bool vals[] = {\n      to_bool(liberty_cplus_demangle),\n      to_bool(liberty_rust_demangle),\n  };\n  return std::all_of(std::begin(vals), std::end(vals), folly::identity);\n}\n\nnamespace {\nvoid demangleStringCallback(const char* str, size_t size, void* p) {\n  fbstring* demangle = static_cast<fbstring*>(p);\n\n  demangle->append(str, size);\n}\n} // namespace\n\nfbstring demangle(const char* name) {\n  if (!name) {\n    return fbstring();\n  }\n\n  if (demangle_max_symbol_size) {\n    // GCC's __cxa_demangle() uses on-stack data structures for the\n    // parser state which are linear in the number of components of the\n    // symbol. For extremely long symbols, this can cause a stack\n    // overflow. We set an arbitrary symbol length limit above which we\n    // just return the mangled name.\n    size_t mangledLen = strlen(name);\n    if (mangledLen > demangle_max_symbol_size) {\n      return fbstring(name, mangledLen);\n    }\n  }\n\n  if (folly::demangle_build_has_liberty()) {\n    liberty_demangle_t* funcs[] = {\n        liberty_rust_demangle,\n        liberty_cplus_demangle,\n    };\n\n    for (auto func : funcs) {\n      fbstring demangled;\n\n      // Unlike most library functions, this returns 1 on success and 0 on\n      // failure\n      int success = func(\n          name, liberty_demangle_options, demangleStringCallback, &demangled);\n      if (success && !demangled.empty()) {\n        return demangled;\n      }\n    }\n  }\n\n  if (cxxabi_demangle) {\n    int status;\n    size_t len = 0;\n    // malloc() memory for the demangled type name\n    char* demangled = cxxabi_demangle(name, nullptr, &len, &status);\n    if (status != 0) {\n      return name;\n    }\n    // len is the length of the buffer (including NUL terminator and maybe\n    // other junk)\n    return fbstring(\n        demangled, strlen(demangled), len, AcquireMallocatedString());\n  } else {\n    return fbstring(name);\n  }\n}\n\nnamespace {\n\nstruct DemangleBuf {\n  char* dest;\n  size_t remaining;\n  size_t total;\n};\n\nvoid demangleBufCallback(const char* str, size_t size, void* p) {\n  DemangleBuf* buf = static_cast<DemangleBuf*>(p);\n  size_t n = std::min(buf->remaining, size);\n  memcpy(buf->dest, str, n);\n  buf->dest += n;\n  buf->remaining -= n;\n  buf->total += size;\n}\n\n} // namespace\n\nsize_t demangle(const char* name, char* out, size_t outSize) {\n  if (demangle_max_symbol_size) {\n    size_t mangledLen = strlen(name);\n    if (mangledLen > demangle_max_symbol_size) {\n      if (outSize) {\n        size_t n = std::min(mangledLen, outSize - 1);\n        memcpy(out, name, n);\n        out[n] = '\\0';\n      }\n      return mangledLen;\n    }\n  }\n\n  if (folly::demangle_build_has_liberty()) {\n    liberty_demangle_t* funcs[] = {\n        liberty_rust_demangle,\n        liberty_cplus_demangle,\n    };\n\n    for (auto func : funcs) {\n      DemangleBuf dbuf;\n      dbuf.dest = out;\n      dbuf.remaining = outSize ? outSize - 1 : 0; // leave room for null term\n      dbuf.total = 0;\n\n      // Unlike most library functions, this returns 1 on success and 0 on\n      // failure\n      int success =\n          func(name, liberty_demangle_options, demangleBufCallback, &dbuf);\n      if (success) {\n        if (outSize != 0) {\n          *dbuf.dest = '\\0';\n        }\n        return dbuf.total;\n      }\n    }\n  }\n\n  // fallback - just return original\n  return folly::strlcpy(out, name, outSize);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Demangle.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/FBString.h>\n#include <folly/portability/Config.h>\n\nnamespace folly {\n\ninline constexpr size_t demangle_max_symbol_size =\n#if defined(FOLLY_DEMANGLE_MAX_SYMBOL_SIZE)\n    FOLLY_DEMANGLE_MAX_SYMBOL_SIZE;\n#else\n    0;\n#endif\n\nbool demangle_build_has_cxxabi() noexcept;\nbool demangle_build_has_liberty() noexcept;\n\n/**\n * Return the demangled (prettified) version of a C++ type.\n *\n * This function tries to produce a human-readable type, but the type name will\n * be returned unchanged in case of error or if demangling isn't supported on\n * your system.\n *\n * Use for debugging -- do not rely on demangle() returning anything useful.\n *\n * This function may allocate memory (and therefore throw std::bad_alloc).\n */\nfbstring demangle(const char* name);\ninline fbstring demangle(const std::type_info& type) {\n  return demangle(type.name());\n}\n\n/**\n * Return the demangled (prettified) version of a C++ type in a user-provided\n * buffer.\n *\n * The semantics are the same as for snprintf or strlcpy: bufSize is the size\n * of the buffer, the string is always null-terminated, and the return value is\n * the number of characters (not including the null terminator) that would have\n * been written if the buffer was big enough. (So a return value >= bufSize\n * indicates that the output was truncated)\n *\n * This function does not allocate memory and is async-signal-safe.\n *\n * Note that the underlying function for the fbstring-returning demangle is\n * somewhat standard (abi::__cxa_demangle, which uses malloc), the underlying\n * function for this version is less so (cplus_demangle_v3_callback from\n * libiberty), so it is possible for the fbstring version to work, while this\n * version returns the original, mangled name.\n */\nsize_t demangle(const char* name, char* out, size_t outSize);\ninline size_t demangle(const std::type_info& type, char* buf, size_t bufSize) {\n  return demangle(type.name(), buf, bufSize);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/DiscriminatedPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Discriminated pointer: Type-safe pointer to one of several types.\n *\n * Similar to std::variant, but has no space overhead over a raw pointer, as\n * it relies on the fact that (on x86_64) there are 16 unused bits in a\n * pointer.\n */\n\n#pragma once\n\n#include <limits>\n#include <stdexcept>\n\n#include <glog/logging.h>\n\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/detail/DiscriminatedPtrDetail.h>\n\n#if !FOLLY_X64 && !FOLLY_AARCH64 && !FOLLY_PPC64 && !FOLLY_RISCV64\n#error \"DiscriminatedPtr is x64, arm64, ppc64 and riscv64 specific code.\"\n#endif\n\nnamespace folly {\n\n/**\n * Discriminated pointer.\n *\n * Given a list of types, a DiscriminatedPtr<Types...> may point to an object\n * of one of the given types, or may be empty.  DiscriminatedPtr is type-safe:\n * you may only get a pointer to the type that you put in, otherwise get\n * throws an exception (and get_nothrow returns nullptr)\n *\n * This pointer does not do any kind of lifetime management -- it's not a\n * \"smart\" pointer.  You are responsible for deallocating any memory used\n * to hold pointees, if necessary.\n */\ntemplate <typename... Types>\nclass DiscriminatedPtr {\n  // <, not <=, as our indexes are 1-based (0 means \"empty\")\n  static_assert(\n      sizeof...(Types) < std::numeric_limits<uint16_t>::max(),\n      \"too many types\");\n\n public:\n  /**\n   * Create an empty DiscriminatedPtr.\n   */\n  DiscriminatedPtr() : data_(0) {}\n\n  /**\n   * Create a DiscriminatedPtr that points to an object of type T.\n   * Fails at compile time if T is not a valid type (listed in Types)\n   */\n  template <typename T>\n  explicit DiscriminatedPtr(T* ptr) {\n    set(ptr, typeIndex<T>());\n  }\n\n  /**\n   * Set this DiscriminatedPtr to point to an object of type T.\n   * Fails at compile time if T is not a valid type (listed in Types)\n   */\n  template <typename T>\n  void set(T* ptr) {\n    set(ptr, typeIndex<T>());\n  }\n\n  /**\n   * Get a pointer to the object that this DiscriminatedPtr points to, if it is\n   * of type T.  Fails at compile time if T is not a valid type (listed in\n   * Types), and returns nullptr if this DiscriminatedPtr is empty or points to\n   * an object of a different type.\n   */\n  template <typename T>\n  T* get_nothrow() noexcept {\n    void* p = FOLLY_LIKELY(hasType<T>()) ? ptr() : nullptr;\n    return static_cast<T*>(p);\n  }\n\n  template <typename T>\n  const T* get_nothrow() const noexcept {\n    const void* p = FOLLY_LIKELY(hasType<T>()) ? ptr() : nullptr;\n    return static_cast<const T*>(p);\n  }\n\n  /**\n   * Get a pointer to the object that this DiscriminatedPtr points to, if it is\n   * of type T.  Fails at compile time if T is not a valid type (listed in\n   * Types), and throws std::invalid_argument if this DiscriminatedPtr is empty\n   * or points to an object of a different type.\n   */\n  template <typename T>\n  T* get() {\n    if (FOLLY_UNLIKELY(!hasType<T>())) {\n      throw std::invalid_argument(\"Invalid type\");\n    }\n    return static_cast<T*>(ptr());\n  }\n\n  template <typename T>\n  const T* get() const {\n    if (FOLLY_UNLIKELY(!hasType<T>())) {\n      throw std::invalid_argument(\"Invalid type\");\n    }\n    return static_cast<const T*>(ptr());\n  }\n\n  /**\n   * Return true iff this DiscriminatedPtr is empty.\n   */\n  bool empty() const { return index() == 0; }\n\n  /**\n   * Return true iff the object pointed by this DiscriminatedPtr has type T,\n   * false otherwise.  Fails at compile time if T is not a valid type (listed\n   * in Types...)\n   */\n  template <typename T>\n  bool hasType() const {\n    return index() == typeIndex<T>();\n  }\n\n  /**\n   * Clear this DiscriminatedPtr, making it empty.\n   */\n  void clear() { data_ = 0; }\n\n  /**\n   * Assignment operator from a pointer of type T.\n   */\n  template <typename T>\n  DiscriminatedPtr& operator=(T* ptr) {\n    set(ptr);\n    return *this;\n  }\n\n  /**\n   * Apply a visitor to this object, calling the appropriate overload for\n   * the type currently stored in DiscriminatedPtr.  Throws invalid_argument\n   * if the DiscriminatedPtr is empty.\n   *\n   * The visitor must meet the following requirements:\n   *\n   * - The visitor must allow invocation as a function by overloading\n   *   operator(), unambiguously accepting all values of type T* (or const T*)\n   *   for all T in Types...\n   * - All operations of the function object on T* (or const T*) must\n   *   return the same type (or a static_assert will fire).\n   */\n  template <typename V>\n  _t<dptr_detail::VisitorResult<V, Types...>> apply(V&& visitor) {\n    size_t n = index();\n    if (n == 0) {\n      throw std::invalid_argument(\"Empty DiscriminatedPtr\");\n    }\n    constexpr dptr_detail::ApplyVisitor<Types...> call;\n    return call(n, visitor, ptr());\n  }\n\n  template <typename V>\n  _t<dptr_detail::ConstVisitorResult<V, Types...>> apply(V&& visitor) const {\n    size_t n = index();\n    if (n == 0) {\n      throw std::invalid_argument(\"Empty DiscriminatedPtr\");\n    }\n    constexpr dptr_detail::ApplyConstVisitor<Types...> call;\n    return call(n, visitor, ptr());\n  }\n\n  /**\n   * Get the 1-based type index of the type currently stored in this pointer.\n   * Returns 0 if the pointer is empty.\n   */\n  size_t index() const { return data_ >> 48; }\n\n private:\n  /**\n   * Get the 1-based type index of T in Types.\n   */\n  template <typename T>\n  uint16_t typeIndex() const {\n    constexpr auto idx = type_pack_find_v<T, Types...>;\n    static_assert(idx < sizeof...(Types));\n    return uint16_t(idx + 1);\n  }\n  void* ptr() const {\n    return reinterpret_cast<void*>(data_ & ((1ULL << 48) - 1));\n  }\n\n  void set(void* p, uint16_t v) {\n    uintptr_t ip = reinterpret_cast<uintptr_t>(p);\n    CHECK(!(ip >> 48));\n    ip |= static_cast<uintptr_t>(v) << 48;\n    data_ = ip;\n  }\n\n  /**\n   * We store a pointer in the least significant 48 bits of data_, and a type\n   * index (0 = empty, or 1-based index in Types) in the most significant 16\n   * bits.  We rely on the fact that pointers have their most significant 16\n   * bits clear on x86_64.\n   */\n  uintptr_t data_;\n};\n\ntemplate <typename Visitor, typename... Args>\ndecltype(auto) apply_visitor(\n    Visitor&& visitor, const DiscriminatedPtr<Args...>& variant) {\n  return variant.apply(std::forward<Visitor>(visitor));\n}\n\ntemplate <typename Visitor, typename... Args>\ndecltype(auto) apply_visitor(\n    Visitor&& visitor, DiscriminatedPtr<Args...>& variant) {\n  return variant.apply(std::forward<Visitor>(visitor));\n}\n\ntemplate <typename Visitor, typename... Args>\ndecltype(auto) apply_visitor(\n    Visitor&& visitor, DiscriminatedPtr<Args...>&& variant) {\n  return variant.apply(std::forward<Visitor>(visitor));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/DynamicConverter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/json/DynamicConverter.h> // @shim\n"
  },
  {
    "path": "folly/Exception.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <errno.h>\n\n#include <cstdio>\n#include <stdexcept>\n#include <system_error>\n\n#include <folly/Conv.h>\n#include <folly/FBString.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/portability/SysTypes.h>\n\nnamespace folly {\n\n// Various helpers to throw appropriate std::system_error exceptions from C\n// library errors (returned in errno, as positive return values (many POSIX\n// functions), or as negative return values (Linux syscalls))\n//\n// The *Explicit functions take an explicit value for errno.\n\n// On linux and similar platforms the value of `errno` is a mixture of\n// POSIX-`errno`-domain error codes and per-OS extended error codes. So the\n// most appropriate category to use is `system_category`.\n//\n// On Windows `system_category` means codes that can be returned by Win32 API\n// `GetLastError` and codes from the `errno`-domain must be reported as\n// `generic_category`.\ninline const std::error_category& errorCategoryForErrnoDomain() noexcept {\n  if (kIsWindows) {\n    return std::generic_category();\n  }\n  return std::system_category();\n}\n\ninline std::system_error makeSystemErrorExplicit(int err, const char* msg) {\n  return std::system_error(err, errorCategoryForErrnoDomain(), msg);\n}\n\ntemplate <class... Args>\nstd::system_error makeSystemErrorExplicit(int err, Args&&... args) {\n  return makeSystemErrorExplicit(\n      err, to<fbstring>(std::forward<Args>(args)...).c_str());\n}\n\ninline std::system_error makeSystemError(const char* msg) {\n  return makeSystemErrorExplicit(errno, msg);\n}\n\ntemplate <class... Args>\nstd::system_error makeSystemError(Args&&... args) {\n  return makeSystemErrorExplicit(errno, std::forward<Args>(args)...);\n}\n\n// Helper to throw std::system_error\n[[noreturn]] inline void throwSystemErrorExplicit(int err, const char* msg) {\n  throw_exception(makeSystemErrorExplicit(err, msg));\n}\n\ntemplate <class... Args>\n[[noreturn]] void throwSystemErrorExplicit(int err, Args&&... args) {\n  throw_exception(makeSystemErrorExplicit(err, std::forward<Args>(args)...));\n}\n\n// Helper to throw std::system_error from errno and components of a string\ntemplate <class... Args>\n[[noreturn]] void throwSystemError(Args&&... args) {\n  throwSystemErrorExplicit(errno, std::forward<Args>(args)...);\n}\n\n// Check a Posix return code (0 on success, error number on error), throw\n// on error.\ntemplate <class... Args>\nvoid checkPosixError(int err, Args&&... args) {\n  if (FOLLY_UNLIKELY(err != 0)) {\n    throwSystemErrorExplicit(err, std::forward<Args>(args)...);\n  }\n}\n\n// Check a Linux kernel-style return code (>= 0 on success, negative error\n// number on error), throw on error.\ntemplate <class... Args>\nvoid checkKernelError(ssize_t ret, Args&&... args) {\n  if (FOLLY_UNLIKELY(ret < 0)) {\n    throwSystemErrorExplicit(int(-ret), std::forward<Args>(args)...);\n  }\n}\n\n// Check a traditional Unix return code (-1 and sets errno on error), throw\n// on error.\ntemplate <class... Args>\nvoid checkUnixError(ssize_t ret, Args&&... args) {\n  if (FOLLY_UNLIKELY(ret == -1)) {\n    throwSystemError(std::forward<Args>(args)...);\n  }\n}\n\ntemplate <class... Args>\nvoid checkUnixErrorExplicit(ssize_t ret, int savedErrno, Args&&... args) {\n  if (FOLLY_UNLIKELY(ret == -1)) {\n    throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...);\n  }\n}\n\n// Check the return code from a fopen-style function (returns a non-nullptr\n// FILE* on success, nullptr on error, sets errno).  Works with fopen, fdopen,\n// freopen, tmpfile, etc.\ntemplate <class... Args>\nvoid checkFopenError(FILE* fp, Args&&... args) {\n  if (FOLLY_UNLIKELY(!fp)) {\n    throwSystemError(std::forward<Args>(args)...);\n  }\n}\n\ntemplate <class... Args>\nvoid checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) {\n  if (FOLLY_UNLIKELY(!fp)) {\n    throwSystemErrorExplicit(savedErrno, std::forward<Args>(args)...);\n  }\n}\n\n/**\n * If cond is not true, raise an exception of type E.  E must have a ctor that\n * works with const char* (a description of the failure).\n */\n#define CHECK_THROW(cond, E)                       \\\n  do {                                             \\\n    if (!(cond)) {                                 \\\n      folly::throw_exception<E>(                   \\\n          \"Check failed: \" #cond \", in \" __FILE__  \\\n          \":\" FOLLY_PP_STRINGIZE_MACRO(__LINE__)); \\\n    }                                              \\\n  } while (0)\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ExceptionString.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ExceptionString.h>\n\n#include <utility>\n\n#include <folly/Demangle.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/TypeInfo.h>\n\nnamespace folly {\n\nnamespace {\n\nfbstring exception_string_type(std::type_info const* ti) {\n  return ti ? demangle(*ti) : \"<unknown exception>\";\n}\n\n} // namespace\n\n/**\n * Debug string for an exception: include type and what(), if\n * defined.\n */\nfbstring exceptionStr(std::exception const& e) {\n  auto prefix = exception_string_type(folly::type_info_of(e));\n  return std::move(prefix) + \": \" + e.what();\n}\n\nfbstring exceptionStr(std::exception_ptr const& ep) {\n  if (auto ex = exception_ptr_get_object<std::exception>(ep)) {\n    return exceptionStr(*ex);\n  }\n  return exception_string_type(exception_ptr_get_type(ep));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ExceptionString.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <exception>\n\n#include <folly/FBString.h>\n\nnamespace folly {\n\n/**\n * Debug string for an exception: include type and what(), if\n * defined.\n */\nfbstring exceptionStr(std::exception const& e);\n\nfbstring exceptionStr(std::exception_ptr const& ep);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ExceptionWrapper-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\nnamespace folly {\n\nstruct exception_wrapper::with_exception_from_fn_ {\n  struct impl_var_ {\n    template <typename>\n    using apply = void;\n  };\n  struct impl_arg_ {\n    template <typename F>\n    using apply = function_arguments_element_t<0, F>;\n  };\n  struct impl_bye_;\n  template <typename Sig, std::size_t NArgs = function_arguments_size_v<Sig>>\n  using impl_ = conditional_t<\n      function_is_variadic_v<Sig>,\n      impl_var_,\n      conditional_t<NArgs == 1, impl_arg_, impl_bye_>>;\n\n  template <typename T>\n  using member_ = typename member_pointer_traits<T>::member_type;\n\n  template <typename Void, typename>\n  struct arg_type_;\n  template <class Sig>\n  struct arg_type_<std::enable_if_t<std::is_function<Sig>::value>, Sig> {\n    using type = typename impl_<Sig>::template apply<Sig>;\n  };\n  template <class Ptr>\n  struct arg_type_<std::enable_if_t<std::is_pointer<Ptr>::value>, Ptr>\n      : arg_type_<void, std::remove_pointer_t<Ptr>> {};\n  template <class Obj>\n  struct arg_type_<void_t<decltype(&Obj::operator())>, Obj>\n      : arg_type_<void, member_<decltype(&Obj::operator())>> {};\n\n  // void if Fn is a variadic callable; otherwise the first arg type\n  template <typename, typename Fn>\n  using apply = _t<arg_type_<void, Fn>>;\n};\n\nstruct exception_wrapper::with_exception_from_ex_ {\n  template <typename Ex, typename>\n  using apply = Ex;\n};\n\ninline exception_wrapper::exception_wrapper(exception_wrapper&& that) noexcept\n    : ptr_{detail::extract_exception_ptr(std::move(that.ptr_))} {}\n\ninline exception_wrapper::exception_wrapper(\n    std::exception_ptr const& ptr) noexcept\n    : ptr_{ptr} {}\n\ninline exception_wrapper::exception_wrapper(std::exception_ptr&& ptr) noexcept\n    : ptr_{detail::extract_exception_ptr(std::move(ptr))} {}\n\ntemplate <\n    class Ex,\n    class Ex_,\n    FOLLY_REQUIRES_DEF(\n        Conjunction<\n            exception_wrapper::IsStdException<Ex_>,\n            exception_wrapper::IsRegularExceptionType<Ex_>>::value)>\ninline exception_wrapper::exception_wrapper(Ex&& ex)\n    : ptr_{make_exception_ptr_with(std::in_place, std::forward<Ex>(ex))} {}\n\ntemplate <\n    class Ex,\n    class Ex_,\n    FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex_>::value)>\ninline exception_wrapper::exception_wrapper(std::in_place_t, Ex&& ex)\n    : ptr_{make_exception_ptr_with(std::in_place, std::forward<Ex>(ex))} {}\n\ntemplate <\n    class Ex,\n    typename... As,\n    FOLLY_REQUIRES_DEF(exception_wrapper::IsRegularExceptionType<Ex>::value)>\ninline exception_wrapper::exception_wrapper(\n    std::in_place_type_t<Ex>, As&&... as)\n    : ptr_{make_exception_ptr_with(\n          std::in_place_type<Ex>, std::forward<As>(as)...)} {}\n\ninline exception_wrapper& exception_wrapper::operator=(\n    exception_wrapper&& that) noexcept {\n  // assume relocatability on all platforms\n  constexpr auto sz = sizeof(std::exception_ptr);\n  std::exception_ptr tmp;\n  std::memcpy(static_cast<void*>(&tmp), &ptr_, sz);\n  std::memcpy(static_cast<void*>(&ptr_), &that.ptr_, sz);\n  std::memset(static_cast<void*>(&that.ptr_), 0, sz);\n  return *this;\n}\n\ninline void exception_wrapper::swap(exception_wrapper& that) noexcept {\n  // assume relocatability on all platforms\n  constexpr auto sz = sizeof(std::exception_ptr);\n  aligned_storage_for_t<std::exception_ptr> storage;\n  std::memcpy(&storage, &ptr_, sz);\n  std::memcpy(static_cast<void*>(&ptr_), &that.ptr_, sz);\n  std::memcpy(static_cast<void*>(&that.ptr_), &storage, sz);\n}\n\ninline exception_wrapper::operator bool() const noexcept {\n  return !!ptr_;\n}\n\ninline bool exception_wrapper::operator!() const noexcept {\n  return !ptr_;\n}\n\ninline void exception_wrapper::reset() {\n  ptr_ = {};\n}\n\ninline bool exception_wrapper::has_exception_ptr() const noexcept {\n  return !!ptr_;\n}\n\ninline std::exception* exception_wrapper::get_mutable_exception()\n    const noexcept {\n  return exception_ptr_get_object<std::exception>(ptr_);\n}\ninline std::exception const* exception_wrapper::get_exception() const noexcept {\n  return exception_ptr_get_object<std::exception>(ptr_);\n}\n\ntemplate <typename Ex>\ninline Ex* exception_wrapper::get_mutable_exception() const noexcept {\n  return exception_ptr_get_object_hint<Ex>(ptr_);\n}\n\ntemplate <typename Ex>\ninline Ex const* exception_wrapper::get_exception() const noexcept {\n  return exception_ptr_get_object_hint<Ex>(ptr_);\n}\n\ninline std::exception_ptr exception_wrapper::to_exception_ptr()\n    const& noexcept {\n  return ptr_;\n}\n\ninline std::exception_ptr& exception_wrapper::exception_ptr() & noexcept {\n  return ptr_;\n}\ninline std::exception_ptr const& exception_wrapper::exception_ptr()\n    const& noexcept {\n  return ptr_;\n}\ninline std::exception_ptr&& exception_wrapper::exception_ptr() && noexcept {\n  return std::move(ptr_);\n}\ninline std::exception_ptr const&& exception_wrapper::exception_ptr()\n    const&& noexcept {\n  return std::move(ptr_);\n}\n\ninline std::type_info const* exception_wrapper::type() const noexcept {\n  return exception_ptr_get_type(ptr_);\n}\n\ninline folly::fbstring exception_wrapper::what() const {\n  if (auto e = get_exception()) {\n    return class_name() + \": \" + e->what();\n  }\n  return class_name();\n}\n\ninline folly::fbstring exception_wrapper::class_name() const {\n  auto const* const ti = type();\n  return !*this ? \"\" : !ti ? \"<unknown>\" : folly::demangle(*ti);\n}\n\ntemplate <class Ex>\ninline bool exception_wrapper::is_compatible_with() const noexcept {\n  return exception_ptr_get_object<Ex>(ptr_);\n}\n\n[[noreturn]] inline void exception_wrapper::throw_exception() const {\n  ptr_ ? std::rethrow_exception(ptr_) : onNoExceptionError(__func__);\n}\n\ntemplate <class Ex>\n[[noreturn]] inline void exception_wrapper::throw_with_nested(Ex&& ex) const {\n  try {\n    throw_exception();\n  } catch (...) {\n    std::throw_with_nested(std::forward<Ex>(ex));\n  }\n}\n\ntemplate <class This, class Fn>\ninline bool exception_wrapper::with_exception_(This&, Fn fn_, tag_t<void>) {\n  return void(fn_()), true;\n}\n\ntemplate <class This, class Fn, typename Ex>\ninline bool exception_wrapper::with_exception_(This& this_, Fn fn_, tag_t<Ex>) {\n  if constexpr (std::is_const_v<Ex>) {\n    auto ptr = this_.template get_exception<remove_cvref_t<Ex>>();\n    return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true);\n  } else {\n    auto ptr = this_.template get_mutable_exception<remove_cvref_t<Ex>>();\n    return ptr && (void(fn_(static_cast<Ex&>(*ptr))), true);\n  }\n}\n\ntemplate <class Ex, class This, class Fn>\ninline bool exception_wrapper::with_exception_(This& this_, Fn fn_) {\n  using from_fn = with_exception_from_fn_;\n  using from_ex = with_exception_from_ex_;\n  using from = conditional_t<std::is_void<Ex>::value, from_fn, from_ex>;\n  using type = typename from::template apply<Ex, Fn>;\n  return with_exception_(this_, std::move(fn_), tag<type>);\n}\n\ntemplate <class This, class... CatchFns>\ninline void exception_wrapper::handle_(\n    This& this_, char const* name, CatchFns&... fns) {\n  if (!this_) {\n    onNoExceptionError(name);\n  }\n  if (!(with_exception_<void>(this_, fns) || ...)) {\n    this_.throw_exception();\n  }\n}\n\ntemplate <class Ex, class Fn>\ninline bool exception_wrapper::with_exception(Fn fn) {\n  return with_exception_<Ex>(*this, std::move(fn));\n}\ntemplate <class Ex, class Fn>\ninline bool exception_wrapper::with_exception(Fn fn) const {\n  return with_exception_<Ex const>(*this, std::move(fn));\n}\n\ntemplate <class... CatchFns>\ninline void exception_wrapper::handle(CatchFns... fns) {\n  handle_(*this, __func__, fns...);\n}\ntemplate <class... CatchFns>\ninline void exception_wrapper::handle(CatchFns... fns) const {\n  handle_(*this, __func__, fns...);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ExceptionWrapper.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ExceptionWrapper.h>\n\n#include <iostream>\n\nnamespace folly {\n\n[[noreturn]] void exception_wrapper::onNoExceptionError(\n    char const* const name) {\n  std::ios_base::Init ioinit_; // ensure std::cerr is alive\n  std::cerr << \"Cannot use `\" << name\n            << \"` with an empty folly::exception_wrapper\" << std::endl;\n  std::terminate();\n}\n\nfbstring exceptionStr(exception_wrapper const& ew) {\n  return ew.what();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ExceptionWrapper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstdint>\n#include <exception>\n#include <iosfwd>\n#include <memory>\n#include <new>\n#include <type_traits>\n#include <typeinfo>\n#include <utility>\n#include <fmt/format.h>\n\n#include <folly/CPortability.h>\n#include <folly/CppAttributes.h>\n#include <folly/Demangle.h>\n#include <folly/ExceptionString.h>\n#include <folly/FBString.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/functional/traits.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\n#define FOLLY_REQUIRES_DEF(...) \\\n  std::enable_if_t<static_cast<bool>(__VA_ARGS__), long>\n\n#define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__\n\n//! Throwing exceptions can be a convenient way to handle errors. Storing\n//! exceptions in an `exception_ptr` makes it easy to handle exceptions in a\n//! different thread or at a later time. `exception_ptr` can also be used in a\n//! very generic result/exception wrapper.\n//!\n//! However, inspecting exceptions through the `exception_ptr` interface, namely\n//! through `rethrow_exception`, is expensive. This is a wrapper interface which\n//! offers faster inspection.\n//!\n//! \\par Example usage:\n//! \\code\n//! exception_wrapper globalExceptionWrapper;\n//!\n//! // Thread1\n//! void doSomethingCrazy() {\n//!   int rc = doSomethingCrazyWithLameReturnCodes();\n//!   if (rc == NAILED_IT) {\n//!     globalExceptionWrapper = exception_wrapper();\n//!   } else if (rc == FACE_PLANT) {\n//!     globalExceptionWrapper = make_exception_wrapper<FacePlantException>();\n//!   } else if (rc == FAIL_WHALE) {\n//!     globalExceptionWrapper = make_exception_wrapper<FailWhaleException>();\n//!   }\n//! }\n//!\n//! // Thread2: Exceptions are ok!\n//! void processResult() {\n//!   try {\n//!     globalExceptionWrapper.throw_exception();\n//!   } catch (const FacePlantException& e) {\n//!     LOG(ERROR) << \"FACEPLANT!\";\n//!   } catch (const FailWhaleException& e) {\n//!     LOG(ERROR) << \"FAILWHALE!\";\n//!   }\n//! }\n//!\n//! // Thread2: Exceptions are bad!\n//! void processResult() {\n//!   globalExceptionWrapper.handle(\n//!       [&](FacePlantException& faceplant) {\n//!         LOG(ERROR) << \"FACEPLANT\";\n//!       },\n//!       [&](FailWhaleException& failwhale) {\n//!         LOG(ERROR) << \"FAILWHALE!\";\n//!       },\n//!       [](...) {\n//!         LOG(FATAL) << \"Unrecognized exception\";\n//!       });\n//! }\n//! \\endcode\nclass exception_wrapper final {\n private:\n  struct with_exception_from_fn_;\n  struct with_exception_from_ex_;\n\n  [[noreturn]] static void onNoExceptionError(char const* name);\n\n  template <class Ex>\n  using IsStdException = std::is_base_of<std::exception, std::decay_t<Ex>>;\n\n  std::exception_ptr ptr_;\n\n  template <class T>\n  struct IsRegularExceptionType\n      : StrictConjunction<\n            std::is_copy_constructible<T>,\n            Negation<std::is_base_of<exception_wrapper, T>>,\n            Negation<std::is_abstract<T>>> {};\n\n  template <class This, class Fn>\n  static bool with_exception_(This& this_, Fn fn_, tag_t<void>);\n\n  template <class This, class Fn, typename Ex>\n  static bool with_exception_(This& this_, Fn fn_, tag_t<Ex>);\n\n  template <class Ex, class This, class Fn>\n  static bool with_exception_(This& this_, Fn fn_);\n\n  template <class This, class... CatchFns>\n  static void handle_(This& this_, char const* name, CatchFns&... fns);\n\n public:\n  //! Default-constructs an empty `exception_wrapper`\n  //! \\post `type() == nullptr`\n  exception_wrapper() noexcept {}\n\n  //! Move-constructs an `exception_wrapper`\n  //! \\post `*this` contains the value of `that` prior to the move\n  //! \\post `that.type() == nullptr`\n  exception_wrapper(exception_wrapper&& that) noexcept;\n\n  //! Copy-constructs an `exception_wrapper`\n  //! \\post `*this` contains a copy of `that`, and `that` is unmodified\n  //! \\post `type() == that.type()`\n  exception_wrapper(exception_wrapper const& that) = default;\n\n  //! Move-assigns an `exception_wrapper`\n  //! \\pre `this != &that`\n  //! \\post `*this` contains the value of `that` prior to the move\n  //! \\post `that.type() == nullptr`\n  exception_wrapper& operator=(exception_wrapper&& that) noexcept;\n\n  //! Copy-assigns an `exception_wrapper`\n  //! \\post `*this` contains a copy of `that`, and `that` is unmodified\n  //! \\post `type() == that.type()`\n  exception_wrapper& operator=(exception_wrapper const& that) = default;\n\n  //! \\post `!ptr || bool(*this)`\n  explicit exception_wrapper(std::exception_ptr const& ptr) noexcept;\n  explicit exception_wrapper(std::exception_ptr&& ptr) noexcept;\n\n  //! \\pre `typeid(ex) == typeid(typename decay<Ex>::type)`\n  //! \\post `bool(*this)`\n  //! \\post `type() == &typeid(ex)`\n  //! \\note Exceptions of types derived from `std::exception` can be implicitly\n  //!     converted to an `exception_wrapper`.\n  template <\n      class Ex,\n      class Ex_ = std::decay_t<Ex>,\n      FOLLY_REQUIRES(\n          Conjunction<IsStdException<Ex_>, IsRegularExceptionType<Ex_>>::value)>\n  /* implicit */ exception_wrapper(Ex&& ex);\n\n  //! \\pre `typeid(ex) == typeid(typename decay<Ex>::type)`\n  //! \\post `bool(*this)`\n  //! \\post `type() == &typeid(ex)`\n  //! \\note Exceptions of types not derived from `std::exception` can still be\n  //!     used to construct an `exception_wrapper`, but you must specify\n  //!     `std::in_place` as the first parameter.\n  template <\n      class Ex,\n      class Ex_ = std::decay_t<Ex>,\n      FOLLY_REQUIRES(IsRegularExceptionType<Ex_>::value)>\n  exception_wrapper(std::in_place_t, Ex&& ex);\n\n  template <\n      class Ex,\n      typename... As,\n      FOLLY_REQUIRES(IsRegularExceptionType<Ex>::value)>\n  exception_wrapper(std::in_place_type_t<Ex>, As&&... as);\n\n  //! Swaps the value of `*this` with the value of `that`\n  void swap(exception_wrapper& that) noexcept;\n\n  //! \\return `true` if `*this` is holding an exception.\n  explicit operator bool() const noexcept;\n\n  //! \\return `!bool(*this)`\n  bool operator!() const noexcept;\n\n  //! Make this `exception_wrapper` empty\n  //! \\post `!*this`\n  void reset();\n\n  //! \\return `true` if this `exception_wrapper` holds an exception.\n  bool has_exception_ptr() const noexcept;\n\n  //! \\return a pointer to the `std::exception` held by `*this`, if it holds\n  //!     one; otherwise, returns `nullptr`.\n  std::exception* get_mutable_exception() const noexcept;\n  std::exception const* get_exception() const noexcept;\n\n  //! \\returns a pointer to the `Ex` held by `*this`, if it holds an object\n  //!     whose type `From` permits `std::is_convertible<From*, Ex*>`;\n  //!     otherwise, returns `nullptr`.\n  //!\n  //! `folly::get_exception<Ex>(ew)` is identical, but avoids the \"dependent\n  //! template\" wart, and supports inspecting other types.\n  //!\n  //! This is most efficient when `Ex` matches the exact stored type, or when\n  //! the type alias `Ex::folly_get_exception_hint_types` has a good hint.\n  //! \\overload\n  template <typename Ex>\n  Ex* get_mutable_exception() const noexcept;\n  template <typename Ex>\n  Ex const* get_exception() const noexcept;\n\n  //! \\return A `std::exception_ptr` that references the exception held by\n  //!     `*this`.\n  std::exception_ptr to_exception_ptr() const& noexcept;\n  // NB: Can add this back, if a good use-case arises.\n  std::exception_ptr to_exception_ptr() && = delete;\n\n  std::exception_ptr& exception_ptr() & noexcept;\n  std::exception_ptr const& exception_ptr() const& noexcept;\n  std::exception_ptr&& exception_ptr() && noexcept;\n  std::exception_ptr const&& exception_ptr() const&& noexcept;\n\n  //! \\return `true` if the wrappers point to the same exception object\n  friend inline bool operator==(\n      exception_wrapper const& lhs, exception_wrapper const& rhs) noexcept {\n    return lhs.ptr_ == rhs.ptr_;\n  }\n\n  //! Returns the `typeid` of the wrapped exception object. If there is no\n  //!     wrapped exception object, returns `nullptr`.\n  std::type_info const* type() const noexcept;\n\n  //! \\return If `get_exception() != nullptr`, `class_name() + \": \" +\n  //!     get_exception()->what()`; otherwise, `class_name()`.\n  folly::fbstring what() const;\n\n  //! \\return If `!*this`, the empty string; otherwise, if `!type()`, text that\n  //!     is not a class name; otherwise, the demangling of `type()->name()`.\n  folly::fbstring class_name() const;\n\n  //! \\tparam Ex The expression type to check for compatibility with.\n  //! \\return `true` if and only if `*this` wraps an exception that would be\n  //!     caught with a `catch(Ex const&)` clause.\n  //! \\note If `*this` is empty, this function returns `false`.\n  template <class Ex>\n  bool is_compatible_with() const noexcept;\n\n  //! Throws the wrapped expression.\n  //! \\pre `bool(*this)`\n  [[noreturn]] void throw_exception() const;\n\n  //! Terminates the process with the wrapped expression.\n  [[noreturn]] void terminate_with() const noexcept { throw_exception(); }\n\n  //! Throws the wrapped expression nested into another exception.\n  //! \\pre `bool(*this)`\n  //! \\param ex Exception in *this will be thrown nested into ex;\n  //      see std::throw_with_nested() for details on this semantic.\n  template <class Ex>\n  [[noreturn]] void throw_with_nested(Ex&& ex) const;\n\n  //! Call `fn` with the wrapped exception (if any), if `fn` can accept it.\n  //! \\par Example\n  //! \\code\n  //! exception_wrapper ew{std::runtime_error(\"goodbye cruel world\")};\n  //!\n  //! assert( ew.with_exception([](std::runtime_error& e){/*...*/}) );\n  //!\n  //! assert( !ew.with_exception([](int& e){/*...*/}) );\n  //!\n  //! assert( !exception_wrapper{}.with_exception([](int& e){/*...*/}) );\n  //! \\endcode\n  //! \\tparam Ex Optionally, the type of the exception that `fn` accepts.\n  //! \\tparam Fn The type of a monomorphic function object.\n  //! \\param fn A function object to call with the wrapped exception\n  //! \\return `true` if and only if `fn` was called.\n  //! \\note Optionally, you may explicitly specify the type of the exception\n  //!     that `fn` expects, as in\n  //! \\code\n  //! ew.with_exception<std::runtime_error>([](auto&& e) { /*...*/; });\n  //! \\endcode\n  //! \\note The handler is not invoked with an active exception.\n  //!     **Do not try to rethrow the exception with `throw;` from within your\n  //!     handler -- that is, a throw expression with no operand.** This may\n  //!     cause your process to terminate. (It is perfectly ok to throw from\n  //!     a handler so long as you specify the exception to throw, as in\n  //!     `throw e;`.)\n  template <class Ex = void const, class Fn>\n  bool with_exception(Fn fn);\n  //! \\overload\n  template <class Ex = void const, class Fn>\n  bool with_exception(Fn fn) const;\n\n  //! Handle the wrapped expression as if with a series of `catch` clauses,\n  //!     propagating the exception if no handler matches.\n  //! \\par Example\n  //! \\code\n  //! exception_wrapper ew{std::runtime_error(\"goodbye cruel world\")};\n  //!\n  //! ew.handle(\n  //!   [&](std::logic_error const& e) {\n  //!      LOG(DFATAL) << \"ruh roh\";\n  //!      ew.throw_exception(); // rethrow the active exception without\n  //!                           // slicing it. Will not be caught by other\n  //!                           // handlers in this call.\n  //!   },\n  //!   [&](std::exception const& e) {\n  //!      LOG(ERROR) << ew.what();\n  //!   });\n  //! \\endcode\n  //! In the above example, any exception _not_ derived from `std::exception`\n  //!     will be propagated. To specify a catch-all clause, pass a lambda that\n  //!     takes a C-style ellipses, as in:\n  //! \\code\n  //! ew.handle(/*...* /, [](...) { /* handle unknown exception */ } )\n  //! \\endcode\n  //! \\pre `!*this`\n  //! \\tparam CatchFns A pack of unary monomorphic function object types.\n  //! \\param fns A pack of unary monomorphic function objects to be treated as\n  //!     an ordered list of potential exception handlers.\n  //! \\note The handlers are not invoked with an active exception.\n  //!     **Do not try to rethrow the exception with `throw;` from within your\n  //!     handler -- that is, a throw expression with no operand.** This may\n  //!     cause your process to terminate. (It is perfectly ok to throw from\n  //!     a handler so long as you specify the exception to throw, as in\n  //!     `throw e;`.)\n  template <class... CatchFns>\n  void handle(CatchFns... fns);\n  //! \\overload\n  template <class... CatchFns>\n  void handle(CatchFns... fns) const;\n\n  // Implement the `folly::get_exception<Ex>(ew)` protocol\n  template <typename Ex>\n  Ex const* get_exception(get_exception_tag_t) const noexcept {\n    return get_exception<Ex>();\n  }\n  template <typename Ex>\n  Ex* get_mutable_exception(get_exception_tag_t) const noexcept {\n    return get_mutable_exception<Ex>();\n  }\n};\n\n/**\n * \\return An `exception_wrapper` that wraps an instance of type `Ex`\n *     that has been constructed with arguments `std::forward<As>(as)...`.\n */\ntemplate <class Ex, typename... As>\nexception_wrapper make_exception_wrapper(As&&... as) {\n  return exception_wrapper{std::in_place_type<Ex>, std::forward<As>(as)...};\n}\n\n/**\n * Inserts `ew.what()` into the ostream `sout`.\n * \\return `sout`\n */\ntemplate <class Ch>\nstd::basic_ostream<Ch>& operator<<(\n    std::basic_ostream<Ch>& sout, exception_wrapper const& ew) {\n  sout << ew.class_name();\n  if (auto e = ew.get_exception()) {\n    sout << \": \" << e->what();\n  }\n  return sout;\n}\n\n/**\n * Swaps the value of `a` with the value of `b`.\n */\ninline void swap(exception_wrapper& a, exception_wrapper& b) noexcept {\n  a.swap(b);\n}\n\n// For consistency with exceptionStr() functions in ExceptionString.h\nfbstring exceptionStr(exception_wrapper const& ew);\n\n//! `try_and_catch` is a convenience for `try {} catch(...) {}`` that returns an\n//! `exception_wrapper` with the thrown exception, if any.\ntemplate <typename F>\nexception_wrapper try_and_catch(F&& fn) noexcept {\n  auto x = [&] { return void(static_cast<F&&>(fn)()), std::exception_ptr{}; };\n  return exception_wrapper{catch_exception(x, current_exception)};\n}\n\n/**\n * A convenience shorthand for `exception_wrapper(current_exception())`.\n */\ninline exception_wrapper current_exception_wrapper() noexcept {\n  return exception_wrapper{current_exception()};\n}\n\n} // namespace folly\n\ntemplate <>\nstruct fmt::formatter<folly::exception_wrapper> : formatter<folly::fbstring> {\n  template <typename Context>\n  auto format(const folly::exception_wrapper& ew, Context& ctx) const {\n    return formatter<folly::fbstring>::format(ew.what(), ctx);\n  }\n};\n\n#include <folly/ExceptionWrapper-inl.h>\n\n#undef FOLLY_REQUIRES\n#undef FOLLY_REQUIRES_DEF\n"
  },
  {
    "path": "folly/Executor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Executor.h>\n\n#include <stdexcept>\n\n#include <glog/logging.h>\n\n#include <folly/ExceptionString.h>\n#include <folly/Portability.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nvoid Executor::invokeCatchingExnsLog(char const* const prefix) noexcept {\n  auto ep = current_exception();\n  LOG(ERROR) << prefix << \" threw unhandled \" << exceptionStr(ep);\n}\n\nvoid Executor::addWithPriority(Func, int8_t /* priority */) {\n  throw std::runtime_error(\n      \"addWithPriority() is not implemented for this Executor\");\n}\n\nbool Executor::keepAliveAcquire() noexcept {\n  return false;\n}\n\nvoid Executor::keepAliveRelease() noexcept {\n  LOG(FATAL) << __func__ << \"() should not be called for folly::Executor types \"\n             << \"which do not override keepAliveAcquire()\";\n}\n\n// Base case of permitting with no termination to avoid nullptr tests\nstatic ExecutorBlockingList emptyList{nullptr, {false, false, nullptr, {}}};\n\nthread_local ExecutorBlockingList* executor_blocking_list = &emptyList;\n\nOptional<ExecutorBlockingContext> getExecutorBlockingContext() noexcept {\n  return //\n      kIsMobile || !executor_blocking_list->curr.forbid ? none : //\n      make_optional(executor_blocking_list->curr);\n}\n\nExecutorBlockingGuard::ExecutorBlockingGuard(PermitTag) noexcept {\n  if (!kIsMobile) {\n    list_ = *executor_blocking_list;\n    list_.prev = executor_blocking_list;\n    list_.curr.forbid = false;\n    // Do not overwrite tag or executor pointer\n    executor_blocking_list = &list_;\n  }\n}\n\nExecutorBlockingGuard::ExecutorBlockingGuard(\n    TrackTag, Executor* ex, StringPiece tag) noexcept {\n  if (!kIsMobile) {\n    list_ = *executor_blocking_list;\n    list_.prev = executor_blocking_list;\n    list_.curr.forbid = true;\n    list_.curr.ex = ex;\n    // If no string was provided, maintain the parent string to keep some\n    // information\n    if (!tag.empty()) {\n      list_.curr.tag = tag;\n    }\n    executor_blocking_list = &list_;\n  }\n}\n\nExecutorBlockingGuard::ExecutorBlockingGuard(\n    ProhibitTag, Executor* ex, StringPiece tag) noexcept {\n  if (!kIsMobile) {\n    list_ = *executor_blocking_list;\n    list_.prev = executor_blocking_list;\n    list_.curr.forbid = true;\n    list_.curr.ex = ex;\n    list_.curr.allowTerminationOnBlocking = true;\n    // If no string was provided, maintain the parent string to keep some\n    // information\n    if (!tag.empty()) {\n      list_.curr.tag = tag;\n    }\n    executor_blocking_list = &list_;\n  }\n}\n\nExecutorBlockingGuard::~ExecutorBlockingGuard() {\n  if (!kIsMobile) {\n    if (executor_blocking_list != &list_) {\n      terminate_with<std::logic_error>(\"dtor mismatch\");\n    }\n    executor_blocking_list = list_.prev;\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Executor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <climits>\n#include <utility>\n\n#include <folly/Function.h>\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/Utility.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nusing Func = Function<void()>;\n\nnamespace detail {\n\nclass ExecutorKeepAliveBase {\n public:\n  //  A dummy keep-alive is a keep-alive to an executor which does not support\n  //  the keep-alive mechanism.\n  static constexpr uintptr_t kDummyFlag = uintptr_t(1) << 0;\n\n  //  An alias keep-alive is a keep-alive to an executor to which there is\n  //  known to be another keep-alive whose lifetime surrounds the lifetime of\n  //  the alias.\n  static constexpr uintptr_t kAliasFlag = uintptr_t(1) << 1;\n\n  static constexpr uintptr_t kFlagMask = kDummyFlag | kAliasFlag;\n  static constexpr uintptr_t kExecutorMask = ~kFlagMask;\n};\n\n} // namespace detail\n\nclass Executor;\n\n/**\n * `ExecutorKeepAlive` is a safe pointer to an `Executor`.\n *\n * For any `Executor` that supports keep-alive functionality, its destructor\n * will block until all the `ExecutorKeepAlive` objects associated with that\n * executor are destroyed.  For executors that don't support the keep-alive\n * functionality, `ExecutorKeepAlive` doesn't provide such protection.\n *\n * `ExecutorKeepAlive` should *always* be used instead of `Executor*`.\n * `ExecutorKeepAlive` can be implicitly constructed from `Executor*`.\n *\n * The `getKeepAliveToken()` helper can be used to construct a keep-alive in\n * templated code if you need to preserve the original executor type.\n */\ntemplate <typename ExecutorT = Executor>\nclass ExecutorKeepAlive : private detail::ExecutorKeepAliveBase {\n public:\n  using KeepAliveFunc = Function<void(ExecutorKeepAlive&&)>;\n\n  ExecutorKeepAlive() = default;\n\n  ~ExecutorKeepAlive() {\n    static_assert(\n        std::is_standard_layout<ExecutorKeepAlive>::value, \"standard-layout\");\n    static_assert(sizeof(ExecutorKeepAlive) == sizeof(void*), \"pointer size\");\n    static_assert(\n        alignof(ExecutorKeepAlive) == alignof(void*), \"pointer align\");\n\n    reset();\n  }\n\n  ExecutorKeepAlive(ExecutorKeepAlive&& other) noexcept\n      : storage_(std::exchange(other.storage_, 0)) {}\n\n  ExecutorKeepAlive(const ExecutorKeepAlive& other) noexcept;\n\n  template <\n      typename OtherExecutor,\n      typename = typename std::enable_if<\n          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>\n  /* implicit */ ExecutorKeepAlive(\n      ExecutorKeepAlive<OtherExecutor>&& other) noexcept\n      : ExecutorKeepAlive(other.get(), other.storage_ & kFlagMask) {\n    other.storage_ = 0;\n  }\n\n  template <\n      typename OtherExecutor,\n      typename = typename std::enable_if<\n          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>\n  /* implicit */ ExecutorKeepAlive(\n      const ExecutorKeepAlive<OtherExecutor>& other) noexcept;\n\n  /* implicit */ ExecutorKeepAlive(ExecutorT* executor);\n\n  ExecutorKeepAlive& operator=(ExecutorKeepAlive&& other) noexcept {\n    reset();\n    storage_ = std::exchange(other.storage_, 0);\n    return *this;\n  }\n\n  ExecutorKeepAlive& operator=(ExecutorKeepAlive const& other) {\n    return operator=(folly::copy(other));\n  }\n\n  template <\n      typename OtherExecutor,\n      typename = typename std::enable_if<\n          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>\n  ExecutorKeepAlive& operator=(\n      ExecutorKeepAlive<OtherExecutor>&& other) noexcept {\n    return *this = ExecutorKeepAlive(std::move(other));\n  }\n\n  template <\n      typename OtherExecutor,\n      typename = typename std::enable_if<\n          std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type>\n  ExecutorKeepAlive& operator=(const ExecutorKeepAlive<OtherExecutor>& other) {\n    return *this = ExecutorKeepAlive(other);\n  }\n\n  void reset() noexcept;\n\n  explicit operator bool() const { return storage_; }\n\n  ExecutorT* get() const {\n    return reinterpret_cast<ExecutorT*>(storage_ & kExecutorMask);\n  }\n\n  ExecutorT& operator*() const { return *get(); }\n\n  ExecutorT* operator->() const { return get(); }\n\n  ExecutorKeepAlive copy() const;\n\n  ExecutorKeepAlive get_alias() const {\n    return ExecutorKeepAlive(storage_ | kAliasFlag);\n  }\n\n  template <class KAF>\n  void add(KAF&& f) && {\n    static_assert(\n        is_invocable<KAF, ExecutorKeepAlive&&>::value,\n        \"Parameter to add must be void(ExecutorKeepAlive&&)>\");\n    auto ex = get();\n    ex->add([ka = std::move(*this), f_2 = std::forward<KAF>(f)]() mutable {\n      f_2(std::move(ka));\n    });\n  }\n\n private:\n  friend class Executor;\n  template <typename OtherExecutor>\n  friend class ExecutorKeepAlive;\n\n  ExecutorKeepAlive(ExecutorT* executor, uintptr_t flags) noexcept\n      : storage_(reinterpret_cast<uintptr_t>(executor) | flags) {\n    assert(executor);\n    assert(!(reinterpret_cast<uintptr_t>(executor) & ~kExecutorMask));\n    assert(!(flags & kExecutorMask));\n  }\n\n  explicit ExecutorKeepAlive(uintptr_t storage) noexcept : storage_(storage) {}\n\n  //  Combined storage for the executor pointer and for all flags.\n  uintptr_t storage_{reinterpret_cast<uintptr_t>(nullptr)};\n};\n\n/// An Executor accepts units of work with add(), which should be\n/// threadsafe.\nclass Executor {\n public:\n  virtual ~Executor() = default;\n\n  /// Enqueue a function to be executed by this executor. This and all\n  /// variants must be threadsafe.\n  virtual void add(Func) = 0;\n\n  /// Enqueue a function with a given priority, where 0 is the medium priority\n  /// This is up to the implementation to enforce\n  virtual void addWithPriority(Func, int8_t priority);\n\n  virtual uint8_t getNumPriorities() const { return 1; }\n\n  static constexpr int8_t LO_PRI = SCHAR_MIN;\n  static constexpr int8_t MID_PRI = 0;\n  static constexpr int8_t HI_PRI = SCHAR_MAX;\n\n  // Compatibility shim.  Cannot be forward-declared, unlike\n  // `ExecutorKeepAlive`.\n  template <typename ExecutorT = Executor>\n  using KeepAlive = ExecutorKeepAlive<ExecutorT>;\n\n  template <typename ExecutorT>\n  static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) {\n    static_assert(\n        std::is_base_of<Executor, ExecutorT>::value,\n        \"getKeepAliveToken only works for folly::Executor implementations.\");\n    if (!executor) {\n      return {};\n    }\n    folly::Executor* executorPtr = executor;\n    if (executorPtr->keepAliveAcquire()) {\n      return makeKeepAlive<ExecutorT>(executor);\n    }\n    return makeKeepAliveDummy<ExecutorT>(executor);\n  }\n\n  template <typename ExecutorT>\n  static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) {\n    static_assert(\n        std::is_base_of<Executor, ExecutorT>::value,\n        \"getKeepAliveToken only works for folly::Executor implementations.\");\n    return getKeepAliveToken(&executor);\n  }\n\n  template <typename F>\n  FOLLY_ERASE static void invokeCatchingExns(char const* p, F f) noexcept {\n    catch_exception(f, invokeCatchingExnsLog, p);\n  }\n\n protected:\n  template <typename>\n  friend class ExecutorKeepAlive;\n\n  /**\n   * Returns true if the KeepAlive is constructed from an executor that does\n   * not support the keep alive ref-counting functionality\n   */\n  template <typename ExecutorT>\n  static bool isKeepAliveDummy(const KeepAlive<ExecutorT>& keepAlive) {\n    return keepAlive.storage_ & KeepAlive<ExecutorT>::kDummyFlag;\n  }\n\n  static bool keepAliveAcquire(Executor* executor) {\n    return executor->keepAliveAcquire();\n  }\n  static void keepAliveRelease(Executor* executor) {\n    return executor->keepAliveRelease();\n  }\n\n  // Acquire a keep alive token. Should return false if keep-alive mechanism\n  // is not supported.\n  virtual bool keepAliveAcquire() noexcept;\n  // Release a keep alive token previously acquired by keepAliveAcquire().\n  // Will never be called if keepAliveAcquire() returns false.\n  virtual void keepAliveRelease() noexcept;\n\n  template <typename ExecutorT>\n  static KeepAlive<ExecutorT> makeKeepAlive(ExecutorT* executor) {\n    static_assert(\n        std::is_base_of<Executor, ExecutorT>::value,\n        \"makeKeepAlive only works for folly::Executor implementations.\");\n    return KeepAlive<ExecutorT>{executor, uintptr_t(0)};\n  }\n\n private:\n  static void invokeCatchingExnsLog(char const* prefix) noexcept;\n\n  template <typename ExecutorT>\n  static KeepAlive<ExecutorT> makeKeepAliveDummy(ExecutorT* executor) {\n    static_assert(\n        std::is_base_of<Executor, ExecutorT>::value,\n        \"makeKeepAliveDummy only works for folly::Executor implementations.\");\n    return KeepAlive<ExecutorT>{executor, KeepAlive<ExecutorT>::kDummyFlag};\n  }\n};\n\ntemplate <typename ExecutorT>\nExecutorKeepAlive<ExecutorT>::ExecutorKeepAlive(\n    const ExecutorKeepAlive<ExecutorT>& other) noexcept\n    : ExecutorKeepAlive(Executor::getKeepAliveToken(other.get())) {}\n\ntemplate <typename ExecutorT>\ntemplate <typename OtherExecutor, typename>\nExecutorKeepAlive<ExecutorT>::ExecutorKeepAlive(\n    const ExecutorKeepAlive<OtherExecutor>& other) noexcept\n    : ExecutorKeepAlive(Executor::getKeepAliveToken(other.get())) {}\n\ntemplate <typename ExecutorT>\nExecutorKeepAlive<ExecutorT>::ExecutorKeepAlive(ExecutorT* executor) {\n  *this = Executor::getKeepAliveToken(executor);\n}\n\ntemplate <typename ExecutorT>\nvoid ExecutorKeepAlive<ExecutorT>::reset() noexcept {\n  if (Executor* executor = get()) {\n    auto const flags = std::exchange(storage_, 0) & kFlagMask;\n    if (!(flags & (kDummyFlag | kAliasFlag))) {\n      executor->keepAliveRelease();\n    }\n  }\n}\n\ntemplate <typename ExecutorT>\nExecutorKeepAlive<ExecutorT> ExecutorKeepAlive<ExecutorT>::copy() const {\n  return Executor::isKeepAliveDummy(*this) //\n      ? Executor::makeKeepAliveDummy(get())\n      : Executor::getKeepAliveToken(get());\n}\n\n/// Returns a keep-alive token which guarantees that Executor will keep\n/// processing tasks until the token is released (if supported by Executor).\n/// KeepAlive always contains a valid pointer to an Executor.\ntemplate <typename ExecutorT>\nExecutor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) {\n  static_assert(\n      std::is_base_of<Executor, ExecutorT>::value,\n      \"getKeepAliveToken only works for folly::Executor implementations.\");\n  return Executor::getKeepAliveToken(executor);\n}\n\ntemplate <typename ExecutorT>\nExecutor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) {\n  static_assert(\n      std::is_base_of<Executor, ExecutorT>::value,\n      \"getKeepAliveToken only works for folly::Executor implementations.\");\n  return getKeepAliveToken(&executor);\n}\n\ntemplate <typename ExecutorT>\nExecutor::KeepAlive<ExecutorT> getKeepAliveToken(\n    Executor::KeepAlive<ExecutorT>& ka) {\n  return ka.copy();\n}\n\nstruct ExecutorBlockingContext {\n  bool forbid;\n  bool allowTerminationOnBlocking;\n  Executor* ex = nullptr;\n  StringPiece tag;\n};\nstatic_assert(\n    std::is_standard_layout<ExecutorBlockingContext>::value,\n    \"non-standard layout\");\n\nstruct ExecutorBlockingList {\n  ExecutorBlockingList* prev;\n  ExecutorBlockingContext curr;\n};\nstatic_assert(\n    std::is_standard_layout<ExecutorBlockingList>::value,\n    \"non-standard layout\");\n\nclass ExecutorBlockingGuard {\n public:\n  struct PermitTag {};\n  struct TrackTag {};\n  struct ProhibitTag {};\n\n  ~ExecutorBlockingGuard();\n  ExecutorBlockingGuard() = delete;\n\n  explicit ExecutorBlockingGuard(PermitTag) noexcept;\n  explicit ExecutorBlockingGuard(\n      TrackTag, Executor* ex, StringPiece tag) noexcept;\n  explicit ExecutorBlockingGuard(\n      ProhibitTag, Executor* ex, StringPiece tag) noexcept;\n\n  ExecutorBlockingGuard(ExecutorBlockingGuard&&) = delete;\n  ExecutorBlockingGuard(ExecutorBlockingGuard const&) = delete;\n\n  ExecutorBlockingGuard& operator=(ExecutorBlockingGuard const&) = delete;\n  ExecutorBlockingGuard& operator=(ExecutorBlockingGuard&&) = delete;\n\n private:\n  ExecutorBlockingList list_;\n};\n\nOptional<ExecutorBlockingContext> getExecutorBlockingContext() noexcept;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Expected.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Like folly::Optional, but can store a value *or* an error.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstddef>\n#include <initializer_list>\n#include <new>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <folly/CPortability.h>\n#include <folly/CppAttributes.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Preprocessor.h>\n#include <folly/Traits.h>\n#include <folly/Unit.h>\n#include <folly/Utility.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Hint.h>\n\n#define FOLLY_EXPECTED_ID(X) FB_CONCATENATE(FB_CONCATENATE(Folly, X), __LINE__)\n\n#define FOLLY_REQUIRES_IMPL(...)                                            \\\n  bool FOLLY_EXPECTED_ID(Requires) = false,                                 \\\n       typename std::enable_if<                                             \\\n           (FOLLY_EXPECTED_ID(Requires) || static_cast<bool>(__VA_ARGS__)), \\\n           int>::type = 0\n\n#define FOLLY_REQUIRES_TRAILING(...) , FOLLY_REQUIRES_IMPL(__VA_ARGS__)\n\n#define FOLLY_REQUIRES(...) template <FOLLY_REQUIRES_IMPL(__VA_ARGS__)>\n\nnamespace folly {\n\nnamespace expected_detail {\nnamespace expected_detail_ExpectedHelper {\nstruct ExpectedHelper;\n}\n/* using override */ using expected_detail_ExpectedHelper::ExpectedHelper;\n} // namespace expected_detail\n\n/**\n * Unexpected - a helper type used to disambiguate the construction of\n * Expected objects in the error state.\n */\ntemplate <class Error>\nclass [[nodiscard]] Unexpected final {\n  template <class E>\n  friend class Unexpected;\n  template <class V, class E>\n  friend class Expected;\n  friend struct expected_detail::ExpectedHelper;\n\n public:\n  /**\n   * Constructors\n   */\n  Unexpected() = default;\n  Unexpected(const Unexpected&) = default;\n  Unexpected(Unexpected&&) = default;\n  Unexpected& operator=(const Unexpected&) = default;\n  Unexpected& operator=(Unexpected&&) = default;\n  [[FOLLY_ATTR_GNU_COLD]] constexpr /* implicit */ Unexpected(const Error& err)\n      : error_(err) {}\n  [[FOLLY_ATTR_GNU_COLD]] constexpr /* implicit */ Unexpected(Error&& err)\n      : error_(std::move(err)) {}\n\n  template <class Other FOLLY_REQUIRES_TRAILING(\n      std::is_constructible<Error, Other&&>::value)>\n  constexpr /* implicit */ Unexpected(Unexpected<Other> that)\n      : error_(std::move(that.error())) {}\n\n  /**\n   * Assignment\n   */\n  template <class Other FOLLY_REQUIRES_TRAILING(\n      std::is_assignable<Error&, Other&&>::value)>\n  Unexpected& operator=(Unexpected<Other> that) {\n    error_ = std::move(that.error());\n  }\n\n  /**\n   * Observers\n   */\n  Error& error() & noexcept { return error_; }\n  const Error& error() const& noexcept { return error_; }\n  Error&& error() && noexcept { return std::move(error_); }\n  const Error&& error() const&& noexcept { return std::move(error_); }\n\n private:\n  Error error_;\n};\n\ntemplate <\n    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>\ninline bool operator==(\n    const Unexpected<Error>& lhs, const Unexpected<Error>& rhs) {\n  return lhs.error() == rhs.error();\n}\n\ntemplate <\n    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>\ninline bool operator!=(\n    const Unexpected<Error>& lhs, const Unexpected<Error>& rhs) {\n  return !(lhs == rhs);\n}\n\n/**\n * For constructing an Unexpected object from an error code. Unexpected objects\n * are implicitly convertible to Expected object in the error state. Usage is\n * as follows:\n *\n * enum class MyErrorCode { BAD_ERROR, WORSE_ERROR };\n * Expected<int, MyErrorCode> myAPI() {\n *   int i = // ...;\n *   return i ? makeExpected<MyErrorCode>(i)\n *            : makeUnexpected(MyErrorCode::BAD_ERROR);\n * }\n */\ntemplate <class Error>\nconstexpr Unexpected<typename std::decay<Error>::type> makeUnexpected(\n    Error&& err) {\n  return Unexpected<typename std::decay<Error>::type>{\n      static_cast<Error&&>(err)};\n}\n\ntemplate <class Error>\nclass FOLLY_EXPORT BadExpectedAccess;\n\n/**\n * A common base type for exceptions thrown by Expected when the caller\n * erroneously requests a value.\n */\ntemplate <>\nclass FOLLY_EXPORT BadExpectedAccess<void> : public std::exception {\n public:\n  explicit BadExpectedAccess() noexcept = default;\n  BadExpectedAccess(BadExpectedAccess const&) {}\n  BadExpectedAccess& operator=(BadExpectedAccess const&) { return *this; }\n\n  char const* what() const noexcept override { return \"bad expected access\"; }\n};\n\n/**\n * An exception type thrown by Expected on catastrophic logic errors, i.e., when\n * the caller tries to access the value within an Expected but when the Expected\n * instead contains an error.\n */\ntemplate <class Error>\nclass FOLLY_EXPORT BadExpectedAccess : public BadExpectedAccess<void> {\n public:\n  explicit BadExpectedAccess(Error error)\n      : error_{static_cast<Error&&>(error)} {}\n\n  /**\n   * The error code that was held by the Expected object when the caller\n   * erroneously requested the value.\n   */\n  Error& error() & { return error_; }\n  Error const& error() const& { return error_; }\n  Error&& error() && { return static_cast<Error&&>(error_); }\n  Error const&& error() const&& { return static_cast<Error const&&>(error_); }\n\n private:\n  Error error_;\n};\n\n/**\n * Forward declarations\n */\ntemplate <class Value, class Error>\nclass Expected;\n\ntemplate <class Error, class Value>\n[[nodiscard]] constexpr Expected<typename std::decay<Value>::type, Error>\nmakeExpected(Value&&);\n\n/**\n * Alias for an Expected type's associated value_type\n */\ntemplate <class Expected>\nusing ExpectedValueType =\n    typename std::remove_reference<Expected>::type::value_type;\n\n/**\n * Alias for an Expected type's associated error_type\n */\ntemplate <class Expected>\nusing ExpectedErrorType =\n    typename std::remove_reference<Expected>::type::error_type;\n\n// Details...\nnamespace expected_detail {\n\ntemplate <typename Value, typename Error>\nstruct Promise;\ntemplate <typename Value, typename Error>\nstruct PromiseReturn;\n\ntemplate <template <class...> class Trait, class... Ts>\nusing StrictAllOf = StrictConjunction<Trait<Ts>...>;\n\ntemplate <class T>\nusing IsCopyable = StrictConjunction<\n    std::is_copy_constructible<T>,\n    std::is_copy_assignable<T>>;\n\ntemplate <class T>\nusing IsMovable = StrictConjunction<\n    std::is_move_constructible<T>,\n    std::is_move_assignable<T>>;\n\ntemplate <class T>\nusing IsNothrowCopyable = StrictConjunction<\n    std::is_nothrow_copy_constructible<T>,\n    std::is_nothrow_copy_assignable<T>>;\n\ntemplate <class T>\nusing IsNothrowMovable = StrictConjunction<\n    std::is_nothrow_move_constructible<T>,\n    std::is_nothrow_move_assignable<T>>;\n\ntemplate <class From, class To>\nusing IsConvertible = StrictConjunction<\n    std::is_constructible<To, From>,\n    std::is_assignable<To&, From>>;\n\ntemplate <class T, class U>\nauto doEmplaceAssign(int, T& t, U&& u)\n    -> decltype(void(t = static_cast<U&&>(u))) {\n  t = static_cast<U&&>(u);\n}\n\ntemplate <class T, class U>\nauto doEmplaceAssign(long, T& t, U&& u)\n    -> decltype(void(T(static_cast<U&&>(u)))) {\n  auto addr = const_cast<void*>(static_cast<void const*>(std::addressof(t)));\n  t.~T();\n  ::new (addr) T(static_cast<U&&>(u));\n}\n\ntemplate <class T, class... Us>\nauto doEmplaceAssign(int, T& t, Us&&... us)\n    -> decltype(void(t = T(static_cast<Us&&>(us)...))) {\n  t = T(static_cast<Us&&>(us)...);\n}\n\ntemplate <class T, class... Us>\nauto doEmplaceAssign(long, T& t, Us&&... us)\n    -> decltype(void(T(static_cast<Us&&>(us)...))) {\n  auto addr = const_cast<void*>(static_cast<void const*>(std::addressof(t)));\n  t.~T();\n  ::new (addr) T(static_cast<Us&&>(us)...);\n}\n\nstruct EmptyTag {};\nstruct ValueTag {};\nstruct ErrorTag {};\nenum class Which : unsigned char { eEmpty, eValue, eError };\nenum class StorageType { ePODStruct, ePODUnion, eUnion };\n\ntemplate <class Value, class Error>\nconstexpr StorageType getStorageType() {\n  return StrictAllOf<std::is_trivially_copyable, Value, Error>::value\n      ? (sizeof(std::pair<Value, Error>) <= sizeof(void* [2]) &&\n                 StrictAllOf<std::is_trivial, Value, Error>::value\n             ? StorageType::ePODStruct\n             : StorageType::ePODUnion)\n      : StorageType::eUnion;\n}\n\ntemplate <\n    class Value,\n    class Error,\n    StorageType = expected_detail::getStorageType<Value, Error>()> // ePODUnion\nstruct ExpectedStorage {\n  using value_type = Value;\n  using error_type = Error;\n  union {\n    Value value_;\n    Error error_;\n    char ch_;\n  };\n  Which which_;\n\n  template <class E = Error, class = decltype(E{})>\n  constexpr ExpectedStorage() noexcept(noexcept(E{}))\n      : error_{}, which_(Which::eError) {}\n  explicit constexpr ExpectedStorage(EmptyTag) noexcept\n      : ch_{unsafe_default_initialized}, which_(Which::eEmpty) {}\n  template <class... Vs>\n  explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(\n      noexcept(Value(static_cast<Vs&&>(vs)...)))\n      : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {}\n  template <class... Es>\n  explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept(\n      noexcept(Error(static_cast<Es&&>(es)...)))\n      : error_(static_cast<Es&&>(es)...), which_(Which::eError) {}\n  void clear() noexcept {}\n  static constexpr bool uninitializedByException() noexcept {\n    // Although which_ may temporarily be eEmpty during construction, it\n    // is always either eValue or eError for a fully-constructed Expected.\n    return false;\n  }\n  template <class... Vs>\n  void assignValue(Vs&&... vs) {\n    expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...);\n    which_ = Which::eValue;\n  }\n  template <class... Es>\n  void assignError(Es&&... es) {\n    expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...);\n    which_ = Which::eError;\n  }\n  template <class Other>\n  void assign(Other&& that) {\n    switch (that.which_) {\n      case Which::eValue:\n        this->assignValue(static_cast<Other&&>(that).value());\n        break;\n      case Which::eError:\n        this->assignError(static_cast<Other&&>(that).error());\n        break;\n      case Which::eEmpty:\n      default:\n        this->clear();\n        break;\n    }\n  }\n  Value& value() & { return value_; }\n  const Value& value() const& { return value_; }\n  Value&& value() && { return std::move(value_); }\n  const Value&& value() const&& { return std::move(value_); }\n  Error& error() & { return error_; }\n  const Error& error() const& { return error_; }\n  Error&& error() && { return std::move(error_); }\n  const Error&& error() const&& { return std::move(error_); }\n};\n\ntemplate <class Value, class Error>\nstruct ExpectedUnion {\n  union {\n    Value value_;\n    Error error_;\n    char ch_ = unsafe_default_initialized;\n  };\n  Which which_ = Which::eEmpty;\n\n  explicit constexpr ExpectedUnion(EmptyTag) noexcept {}\n  template <class... Vs>\n  explicit constexpr ExpectedUnion(ValueTag, Vs&&... vs) noexcept(\n      noexcept(Value(static_cast<Vs&&>(vs)...)))\n      : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {}\n  template <class... Es>\n  explicit constexpr ExpectedUnion(ErrorTag, Es&&... es) noexcept(\n      noexcept(Error(static_cast<Es&&>(es)...)))\n      : error_(static_cast<Es&&>(es)...), which_(Which::eError) {}\n  ExpectedUnion(const ExpectedUnion&) {}\n  ExpectedUnion(ExpectedUnion&&) noexcept {}\n  ExpectedUnion& operator=(const ExpectedUnion&) { return *this; }\n  ExpectedUnion& operator=(ExpectedUnion&&) noexcept { return *this; }\n  ~ExpectedUnion() {}\n  Value& value() & { return value_; }\n  const Value& value() const& { return value_; }\n  Value&& value() && { return std::move(value_); }\n  const Value&& value() const&& { return std::move(value_); }\n  Error& error() & { return error_; }\n  const Error& error() const& { return error_; }\n  Error&& error() && { return std::move(error_); }\n  const Error&& error() const&& { return std::move(error_); }\n};\n\ntemplate <class Derived, bool, bool Noexcept>\nstruct CopyConstructible {\n  constexpr CopyConstructible() = default;\n  CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) {\n    static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));\n  }\n  constexpr CopyConstructible(CopyConstructible&&) = default;\n  CopyConstructible& operator=(const CopyConstructible&) = default;\n  CopyConstructible& operator=(CopyConstructible&&) = default;\n};\n\ntemplate <class Derived, bool Noexcept>\nstruct CopyConstructible<Derived, false, Noexcept> {\n  constexpr CopyConstructible() = default;\n  CopyConstructible(const CopyConstructible&) = delete;\n  constexpr CopyConstructible(CopyConstructible&&) = default;\n  CopyConstructible& operator=(const CopyConstructible&) = default;\n  CopyConstructible& operator=(CopyConstructible&&) = default;\n};\n\ntemplate <class Derived, bool, bool Noexcept>\nstruct MoveConstructible {\n  constexpr MoveConstructible() = default;\n  constexpr MoveConstructible(const MoveConstructible&) = default;\n  MoveConstructible(MoveConstructible&& that) noexcept(Noexcept) {\n    static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));\n  }\n  MoveConstructible& operator=(const MoveConstructible&) = default;\n  MoveConstructible& operator=(MoveConstructible&&) = default;\n};\n\ntemplate <class Derived, bool Noexcept>\nstruct MoveConstructible<Derived, false, Noexcept> {\n  constexpr MoveConstructible() = default;\n  constexpr MoveConstructible(const MoveConstructible&) = default;\n  MoveConstructible(MoveConstructible&&) = delete;\n  MoveConstructible& operator=(const MoveConstructible&) = default;\n  MoveConstructible& operator=(MoveConstructible&&) = default;\n};\n\ntemplate <class Derived, bool, bool Noexcept>\nstruct CopyAssignable {\n  constexpr CopyAssignable() = default;\n  constexpr CopyAssignable(const CopyAssignable&) = default;\n  constexpr CopyAssignable(CopyAssignable&&) = default;\n  CopyAssignable& operator=(const CopyAssignable& that) noexcept(Noexcept) {\n    static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));\n    return *this;\n  }\n  CopyAssignable& operator=(CopyAssignable&&) = default;\n};\n\ntemplate <class Derived, bool Noexcept>\nstruct CopyAssignable<Derived, false, Noexcept> {\n  constexpr CopyAssignable() = default;\n  constexpr CopyAssignable(const CopyAssignable&) = default;\n  constexpr CopyAssignable(CopyAssignable&&) = default;\n  CopyAssignable& operator=(const CopyAssignable&) = delete;\n  CopyAssignable& operator=(CopyAssignable&&) = default;\n};\n\ntemplate <class Derived, bool, bool Noexcept>\nstruct MoveAssignable {\n  constexpr MoveAssignable() = default;\n  constexpr MoveAssignable(const MoveAssignable&) = default;\n  constexpr MoveAssignable(MoveAssignable&&) = default;\n  MoveAssignable& operator=(const MoveAssignable&) = default;\n  MoveAssignable& operator=(MoveAssignable&& that) noexcept(Noexcept) {\n    static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));\n    return *this;\n  }\n};\n\ntemplate <class Derived, bool Noexcept>\nstruct MoveAssignable<Derived, false, Noexcept> {\n  constexpr MoveAssignable() = default;\n  constexpr MoveAssignable(const MoveAssignable&) = default;\n  constexpr MoveAssignable(MoveAssignable&&) = default;\n  MoveAssignable& operator=(const MoveAssignable&) = default;\n  MoveAssignable& operator=(MoveAssignable&& that) = delete;\n};\n\ntemplate <class Value, class Error>\nstruct ExpectedStorage<Value, Error, StorageType::eUnion>\n    : ExpectedUnion<Value, Error>,\n      CopyConstructible<\n          ExpectedStorage<Value, Error, StorageType::eUnion>,\n          StrictAllOf<std::is_copy_constructible, Value, Error>::value,\n          StrictAllOf<std::is_nothrow_copy_constructible, Value, Error>::value>,\n      MoveConstructible<\n          ExpectedStorage<Value, Error, StorageType::eUnion>,\n          StrictAllOf<std::is_move_constructible, Value, Error>::value,\n          StrictAllOf<std::is_nothrow_move_constructible, Value, Error>::value>,\n      CopyAssignable<\n          ExpectedStorage<Value, Error, StorageType::eUnion>,\n          StrictAllOf<IsCopyable, Value, Error>::value,\n          StrictAllOf<IsNothrowCopyable, Value, Error>::value>,\n      MoveAssignable<\n          ExpectedStorage<Value, Error, StorageType::eUnion>,\n          StrictAllOf<IsMovable, Value, Error>::value,\n          StrictAllOf<IsNothrowMovable, Value, Error>::value> {\n  using value_type = Value;\n  using error_type = Error;\n  using Base = ExpectedUnion<Value, Error>;\n  template <class E = Error, class = decltype(E{})>\n  constexpr ExpectedStorage() noexcept(noexcept(E{})) : Base{ErrorTag{}} {}\n  ExpectedStorage(const ExpectedStorage&) = default;\n  ExpectedStorage(ExpectedStorage&&) = default;\n  ExpectedStorage& operator=(const ExpectedStorage&) = default;\n  ExpectedStorage& operator=(ExpectedStorage&&) = default;\n  using ExpectedUnion<Value, Error>::ExpectedUnion;\n  ~ExpectedStorage() { clear(); }\n  void clear() noexcept {\n    switch (this->which_) {\n      case Which::eValue:\n        this->value().~Value();\n        break;\n      case Which::eError:\n        this->error().~Error();\n        break;\n      case Which::eEmpty:\n      default:\n        break;\n    }\n    this->which_ = Which::eEmpty;\n  }\n  bool uninitializedByException() const noexcept {\n    return this->which_ == Which::eEmpty;\n  }\n  template <class... Vs>\n  void assignValue(Vs&&... vs) {\n    auto& val = this->value();\n    if (this->which_ == Which::eValue) {\n      expected_detail::doEmplaceAssign(0, val, static_cast<Vs&&>(vs)...);\n    } else {\n      this->clear();\n      auto addr =\n          const_cast<void*>(static_cast<void const*>(std::addressof(val)));\n      ::new (addr) Value(static_cast<Vs&&>(vs)...);\n      this->which_ = Which::eValue;\n    }\n  }\n  template <class... Es>\n  void assignError(Es&&... es) {\n    if (this->which_ == Which::eError) {\n      expected_detail::doEmplaceAssign(\n          0, this->error(), static_cast<Es&&>(es)...);\n    } else {\n      this->clear();\n      ::new ((void*)std::addressof(this->error()))\n          Error(static_cast<Es&&>(es)...);\n      this->which_ = Which::eError;\n    }\n  }\n  bool isSelfAssign(const ExpectedStorage* that) const { return this == that; }\n  constexpr bool isSelfAssign(const void*) const { return false; }\n  template <class Other>\n  void assign(Other&& that) {\n    if (isSelfAssign(&that)) {\n      return;\n    }\n    FOLLY_PUSH_WARNING\n    FOLLY_CLANG_DISABLE_WARNING(\"-Wcovered-switch-default\")\n    switch (that.which_) {\n      case Which::eValue:\n        this->assignValue(static_cast<Other&&>(that).value());\n        break;\n      case Which::eError:\n        this->assignError(static_cast<Other&&>(that).error());\n        break;\n      case Which::eEmpty:\n      default:\n        this->clear();\n        break;\n    }\n    FOLLY_POP_WARNING\n  }\n};\n\n// For small (pointer-sized) trivial types, a struct is faster than a union.\ntemplate <class Value, class Error>\nstruct ExpectedStorage<Value, Error, StorageType::ePODStruct> {\n  using value_type = Value;\n  using error_type = Error;\n  Which which_;\n  Error error_;\n  Value value_;\n\n  constexpr ExpectedStorage() noexcept\n      : which_(Which::eError), error_{}, value_{} {}\n  explicit constexpr ExpectedStorage(EmptyTag) noexcept\n      : which_(Which::eEmpty), error_{}, value_{} {}\n  template <class... Vs>\n  explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(\n      noexcept(Value(static_cast<Vs&&>(vs)...)))\n      : which_(Which::eValue), error_{}, value_(static_cast<Vs&&>(vs)...) {}\n  template <class... Es>\n  explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept(\n      noexcept(Error(static_cast<Es&&>(es)...)))\n      : which_(Which::eError), error_(static_cast<Es&&>(es)...), value_{} {}\n  void clear() noexcept {}\n  constexpr static bool uninitializedByException() noexcept { return false; }\n  template <class... Vs>\n  void assignValue(Vs&&... vs) {\n    expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...);\n    which_ = Which::eValue;\n  }\n  template <class... Es>\n  void assignError(Es&&... es) {\n    expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...);\n    which_ = Which::eError;\n  }\n  template <class Other>\n  void assign(Other&& that) {\n    switch (that.which_) {\n      case Which::eValue:\n        this->assignValue(static_cast<Other&&>(that).value());\n        break;\n      case Which::eError:\n        this->assignError(static_cast<Other&&>(that).error());\n        break;\n      case Which::eEmpty:\n      default:\n        this->clear();\n        break;\n    }\n  }\n  Value& value() & { return value_; }\n  const Value& value() const& { return value_; }\n  Value&& value() && { return std::move(value_); }\n  const Value&& value() const&& { return std::move(value_); }\n  Error& error() & { return error_; }\n  const Error& error() const& { return error_; }\n  Error&& error() && { return std::move(error_); }\n  const Error&& error() const&& { return std::move(error_); }\n};\n\nnamespace expected_detail_ExpectedHelper {\n// Tricky hack so that Expected::then can handle lambdas that return void\ntemplate <class T>\ninline T&& operator,(T&& t, Unit) noexcept {\n  return static_cast<T&&>(t);\n}\n\nstruct ExpectedHelper {\n  template <typename V, typename E>\n  FOLLY_ERASE static void assume_empty(Expected<V, E> const& x) {\n    compiler_may_unsafely_assume(x.which_ == Which::eEmpty);\n  }\n\n  template <class Error, class T>\n  static constexpr Expected<typename std::decay<T>::type, Error> return_(\n      T&& t) {\n    return folly::makeExpected<Error>(static_cast<T&&>(t));\n  }\n\n  template <\n      class Error,\n      class T,\n      class U FOLLY_REQUIRES_TRAILING(\n          expected_detail::IsConvertible<U&&, Error>::value)>\n  static constexpr Expected<T, Error> return_(Expected<T, U>&& t) {\n    return Expected<T, Error>(static_cast<Expected<T, U>&&>(t));\n  }\n\n  template <class This>\n  static typename std::decay<This>::type then_(This&& ex) {\n    return static_cast<This&&>(ex);\n  }\n\n  FOLLY_PUSH_WARNING\n  // Don't warn about not using the overloaded comma operator.\n  FOLLY_MSVC_DISABLE_WARNING(4913)\n  // On MSVC in optimized builds, the following functions can throw warning 4702\n  // Unreachable Code which will block builds which treat warnings as error.\n  FOLLY_MSVC_DISABLE_WARNING(4702)\n  template <\n      class This,\n      class Fn,\n      class... Fns,\n      class E = ExpectedErrorType<This>,\n      class T = ExpectedHelper>\n  static auto then_(This&& ex, Fn&& fn, Fns&&... fns) -> decltype(T::then_(\n      T::template return_<E>(\n          (std::declval<Fn>()(std::declval<This>().value()), unit)),\n      std::declval<Fns>()...)) {\n    if (FOLLY_LIKELY(ex.which_ == expected_detail::Which::eValue)) {\n      return T::then_(\n          T::template return_<E>(\n              // Uses the comma operator defined above IFF the lambda\n              // returns non-void.\n              (static_cast<Fn&&>(fn)(static_cast<This&&>(ex).value()), unit)),\n          static_cast<Fns&&>(fns)...);\n    }\n    return makeUnexpected(static_cast<This&&>(ex).error());\n  }\n\n  template <\n      class This,\n      class Yes,\n      class No,\n      class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())),\n      class Err = decltype(std::declval<No>()(std::declval<This>().error()))\n          FOLLY_REQUIRES_TRAILING(!std::is_void<Err>::value)>\n  static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {\n    if (FOLLY_LIKELY(ex.which_ == expected_detail::Which::eValue)) {\n      return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));\n    }\n    throw_exception(static_cast<No&&>(no)(static_cast<This&&>(ex).error()));\n  }\n\n  template <\n      class This,\n      class Yes,\n      class No,\n      class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())),\n      class Err = decltype(std::declval<No>()(std::declval<This&>().error()))\n          FOLLY_REQUIRES_TRAILING(std::is_void<Err>::value)>\n  static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {\n    if (FOLLY_LIKELY(ex.which_ == expected_detail::Which::eValue)) {\n      return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));\n    }\n    static_cast<No&&>(no)(ex.error());\n    throw_exception<BadExpectedAccess<ExpectedErrorType<This>>>(\n        static_cast<This&&>(ex).error());\n  }\n\n  /**\n   * Note: the condition for specialization is easy to miss here - this is for\n   * where Err fails is_void, AND the else chain is not void returning. Invoked\n   * when orElse handles a chain.\n   */\n  template <\n      class This,\n      class No,\n      class... AndFns,\n      class E = ExpectedErrorType<This>,\n      class V = ExpectedValueType<This>,\n      class T = ExpectedHelper,\n      class RetValue =\n          decltype(T::then_(\n                       T::template return_<E>(\n                           (std::declval<No>()(std::declval<This>().error()))),\n                       std::declval<AndFns>()...)\n                       .value()),\n      typename std::enable_if<!std::is_same<\n          std::remove_reference<RetValue>,\n          std::remove_reference<folly::Unit&&>>::value>::type* = nullptr,\n      class Err = decltype(std::declval<No>()(std::declval<This&>().error()))\n          FOLLY_REQUIRES_TRAILING(!std::is_void<Err>::value)>\n  static auto orElse_(This&& ex, No&& no, AndFns&&... fns) -> Expected<V, E> {\n    // Note - this basically decays into then_ once the first type (No) is\n    // called for the error.\n    if (FOLLY_LIKELY(ex.which_ == expected_detail::Which::eValue)) {\n      return T::template return_<E>((T::then_(\n          T::template return_<E>(\n              // Uses the comma operator defined above IFF the lambda\n              // returns non-void.\n              static_cast<decltype(ex)&&>(ex).value()))));\n    }\n    return T::then_(\n        T::template return_<E>(\n            (static_cast<No&&>(no)(static_cast<This&&>(ex).error()))),\n        static_cast<AndFns&&>(fns)...);\n  }\n\n  /**\n   * Note: the condition for specialization is easy to miss here - this is for\n   * where Err fails is_void AND the else chain is void returning. Invoked when\n   * orElse handles a chain.\n   */\n  template <\n      class This,\n      class No,\n      class... AndFns,\n      class E = ExpectedErrorType<This>,\n      class T = ExpectedHelper,\n      class RetValue =\n          decltype(T::then_(\n                       T::template return_<E>(\n                           (std::declval<No>()(std::declval<This>().error()))),\n                       std::declval<AndFns>()...)\n                       .value()),\n      typename std::enable_if<std::is_same<\n          std::remove_reference<RetValue>,\n          std::remove_reference<folly::Unit&&>>::value>::type* = nullptr,\n      class Err = decltype(std::declval<No>()(std::declval<This&>().error()))\n          FOLLY_REQUIRES_TRAILING(!std::is_void<Err>::value)>\n  static auto orElse_(This&& ex, No&& no, AndFns&&... fns)\n      -> Expected<folly::Unit, E> {\n    // Note - this basically decays into then_ once the first type (No) is\n    // called for the error.\n    if (std::forward<This>(ex).which_ == expected_detail::Which::eValue) {\n      // Void returning method on else must either throw, or be replaced with a\n      // chain that ends in a valid result.\"\"\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return T::then_(\n        T::template return_<E>(\n            (static_cast<No&&>(no)(static_cast<This&&>(ex).error()))),\n        static_cast<AndFns&&>(fns)...);\n  }\n\n  /**\n   * Note: the condition for specialization is easy to miss here - this is for\n   * where Err passes is_void. Invoked when orElse handles a void returning\n   * func.\n   */\n  template <\n      class This,\n      class No,\n      class... AndFns,\n      class E = ExpectedErrorType<This>,\n      class V = ExpectedValueType<This>,\n      class T = ExpectedHelper,\n      class Err = decltype(std::declval<No>()(std::declval<This&>().error()))\n          FOLLY_REQUIRES_TRAILING(std::is_void<Err>::value)>\n  static auto orElse_(This&& ex, No&& no, AndFns&&...) -> Expected<V, E> {\n    if (FOLLY_LIKELY(ex.which_ == expected_detail::Which::eValue)) {\n      return return_<E>(static_cast<decltype(ex)&&>(ex).value());\n    }\n    static_cast<No&&>(no)(static_cast<This&&>(ex).error());\n    return makeUnexpected(static_cast<decltype(ex)&&>(ex).error());\n  }\n\n  template <\n      class This,\n      class OnError,\n      class Err =\n          decltype(std::declval<OnError>()(std::declval<This>().error()))\n              FOLLY_REQUIRES_TRAILING(std::is_void<Err>::value)>\n  static This onError_(This&& ex, OnError&& onError) {\n    if (UNLIKELY(ex.which_ == expected_detail::Which::eError)) {\n      static_cast<OnError&&>(onError)(static_cast<This&&>(ex).error());\n    }\n    return ex;\n  }\n\n  FOLLY_POP_WARNING\n};\n} // namespace expected_detail_ExpectedHelper\n\nstruct UnexpectedTag {};\n\n} // namespace expected_detail\n\nusing unexpected_t =\n    expected_detail::UnexpectedTag (&)(expected_detail::UnexpectedTag);\n\ninline expected_detail::UnexpectedTag unexpected(\n    expected_detail::UnexpectedTag = {}) {\n  return {};\n}\n\nnamespace expected_detail {\n// empty\n} // namespace expected_detail\n\n/**\n * Expected - For holding a value or an error. Useful as an alternative to\n * exceptions, for APIs where throwing on failure would be too expensive.\n *\n * Expected<Value, Error> is a variant over the types Value and Error.\n *\n * Expected does not offer support for references. Use\n * Expected<std::reference_wrapper<T>, Error> if your API needs to return a\n * reference or an error.\n *\n * Expected offers a continuation-based interface to reduce the boilerplate\n * of checking error codes. The Expected::then member function takes a lambda\n * that is to execute should the Expected object contain a value. The return\n * value of the lambda is wrapped in an Expected and returned. If the lambda is\n * not executed because the Expected contains an error, the error is returned\n * immediately in a new Expected object.\n *\n * Expected<int, Error> funcTheFirst();\n * Expected<std::string, Error> funcTheSecond() {\n *   return funcTheFirst().then([](int i) { return std::to_string(i); });\n * }\n *\n * The above line of code could more verbosely written as:\n *\n * Expected<std::string, Error> funcTheSecond() {\n *   if (auto ex = funcTheFirst()) {\n *     return std::to_string(*ex);\n *   }\n *   return makeUnexpected(ex.error());\n * }\n *\n * Continuations can chain, like:\n *\n * Expected<D, Error> maybeD = someFunc()\n *     .then([](A a){return B(a);})\n *     .then([](B b){return C(b);})\n *     .then([](C c){return D(c);});\n *\n * To avoid the redundant error checking that would happen if a call at the\n * front of the chain returns an error, these call chains can be collapsed into\n * a single call to .then:\n *\n * Expected<D, Error> maybeD = someFunc()\n *     .then([](A a){return B(a);},\n *           [](B b){return C(b);},\n *           [](C c){return D(c);});\n *\n * The result of .then() is wrapped into Expected< ~, Error > if it isn't\n * of that form already. Consider the following code:\n *\n * extern Expected<std::string, Error> readLineFromIO();\n * extern Expected<int, Error> parseInt(std::string);\n * extern int increment(int);\n *\n * Expected<int, Error> x = readLineFromIO().then(parseInt).then(increment);\n *\n * From the code above, we see that .then() works both with functions that\n * return an Expected< ~, Error > (like parseInt) and with ones that return\n * a plain value (like increment). In the case of parseInt, .then() returns\n * the result of parseInt as-is. In the case of increment, it wraps the int\n * that increment returns into an Expected< int, Error >.\n *\n * Sometimes when using a continuation you would prefer an exception to be\n * thrown for a value-less Expected. For that you can use .thenOrThrow, as\n * follows:\n *\n * B b = someFunc()\n *     .thenOrThrow([](A a){return B(a);});\n *\n * The above call to thenOrThrow will invoke the lambda if the Expected returned\n * by someFunc() contains a value. Otherwise, it will throw an exception of type\n * Unexpected<Error>::BadExpectedAccess. If you prefer it throw an exception of\n * a different type, you can pass a second lambda to thenOrThrow:\n *\n * B b = someFunc()\n *     .thenOrThrow([](A a){return B(a);},\n *                  [](Error e) {throw MyException(e);});\n *\n * Like C++17's std::variant, Expected offers the almost-never-empty guarantee;\n * that is, an Expected<Value, Error> almost always contains either a Value or\n * and Error. Partially-formed Expected objects occur when an assignment to\n * an Expected object that would change the type of the contained object (Value-\n * to-Error or vice versa) throws. Trying to access either the contained value\n * or error object causes Expected to throw folly::BadExpectedAccess.\n *\n * Expected models OptionalPointer, so calling 'get_pointer(ex)' will return a\n * pointer to nullptr if the 'ex' is in the error state, and a pointer to the\n * value otherwise:\n *\n *  Expected<int, Error> maybeInt = ...;\n *  if (int* v = get_pointer(maybeInt)) {\n *    cout << *v << endl;\n *  }\n */\ntemplate <class Value, class Error>\nclass Expected final : expected_detail::ExpectedStorage<Value, Error> {\n  template <class, class>\n  friend class Expected;\n  template <class, class, expected_detail::StorageType>\n  friend struct expected_detail::ExpectedStorage;\n  friend struct expected_detail::ExpectedHelper;\n  using Base = expected_detail::ExpectedStorage<Value, Error>;\n  Base& base() & { return *this; }\n  const Base& base() const& { return *this; }\n  Base&& base() && { return std::move(*this); }\n\n  struct MakeBadExpectedAccess {\n    template <class E>\n    auto operator()(E&& e) {\n      return BadExpectedAccess<Error>(static_cast<E&&>(e));\n    }\n  };\n\n public:\n  using value_type = Value;\n  using error_type = Error;\n\n  template <class U>\n  using rebind = Expected<U, Error>;\n\n  using promise_type = expected_detail::Promise<Value, Error>;\n\n  static_assert(\n      !std::is_reference<Value>::value,\n      \"Expected may not be used with reference types\");\n  static_assert(\n      !std::is_abstract<Value>::value,\n      \"Expected may not be used with abstract types\");\n\n  /*\n   * Constructors\n   */\n  template <class B = Base, class = decltype(B{})>\n  Expected() noexcept(noexcept(B{})) : Base{} {}\n  Expected(const Expected& that) = default;\n  Expected(Expected&& that) = default;\n\n  template <\n      class V,\n      class E FOLLY_REQUIRES_TRAILING(\n          !std::is_same<Expected<V, E>, Expected>::value &&\n          std::is_constructible<Value, V&&>::value &&\n          std::is_constructible<Error, E&&>::value)>\n  Expected(Expected<V, E> that) : Base{expected_detail::EmptyTag{}} {\n    this->assign(std::move(that));\n  }\n\n  FOLLY_REQUIRES(std::is_copy_constructible<Value>::value)\n  constexpr /* implicit */ Expected(const Value& val) noexcept(\n      noexcept(Value(val)))\n      : Base{expected_detail::ValueTag{}, val} {}\n\n  FOLLY_REQUIRES(std::is_move_constructible<Value>::value)\n  constexpr /* implicit */ Expected(Value&& val) noexcept(\n      noexcept(Value(std::move(val))))\n      : Base{expected_detail::ValueTag{}, std::move(val)} {}\n\n  template <class T FOLLY_REQUIRES_TRAILING(\n      std::is_convertible<T, Value>::value &&\n      !std::is_convertible<T, Error>::value)>\n  constexpr /* implicit */ Expected(T&& val) noexcept(\n      noexcept(Value(static_cast<T&&>(val))))\n      : Base{expected_detail::ValueTag{}, static_cast<T&&>(val)} {}\n\n  template <class... Ts FOLLY_REQUIRES_TRAILING(\n      std::is_constructible<Value, Ts&&...>::value)>\n  explicit constexpr Expected(std::in_place_t, Ts&&... ts) noexcept(\n      noexcept(Value(std::declval<Ts>()...)))\n      : Base{expected_detail::ValueTag{}, static_cast<Ts&&>(ts)...} {}\n\n  template <\n      class U,\n      class... Ts FOLLY_REQUIRES_TRAILING(\n          std::is_constructible<Value, std::initializer_list<U>&, Ts&&...>::\n              value)>\n  explicit constexpr Expected(\n      std::in_place_t,\n      std::initializer_list<U> il,\n      Ts&&... ts) noexcept(noexcept(Value(std::declval<Ts>()...)))\n      : Base{expected_detail::ValueTag{}, il, static_cast<Ts&&>(ts)...} {}\n\n  // If overload resolution selects one of these deleted functions, that\n  // means you need to use makeUnexpected\n  /* implicit */ Expected(const Error&) = delete;\n  /* implicit */ Expected(Error&&) = delete;\n\n  FOLLY_REQUIRES(std::is_copy_constructible<Error>::value)\n  constexpr Expected(unexpected_t, const Error& err) noexcept(\n      noexcept(Error(err)))\n      : Base{expected_detail::ErrorTag{}, err} {}\n\n  FOLLY_REQUIRES(std::is_move_constructible<Error>::value)\n  constexpr Expected(unexpected_t, Error&& err) noexcept(\n      noexcept(Error(std::move(err))))\n      : Base{expected_detail::ErrorTag{}, std::move(err)} {}\n\n  FOLLY_REQUIRES(std::is_copy_constructible<Error>::value)\n  constexpr /* implicit */ Expected(const Unexpected<Error>& err) noexcept(\n      noexcept(Error(err.error())))\n      : Base{expected_detail::ErrorTag{}, err.error()} {}\n\n  FOLLY_REQUIRES(std::is_move_constructible<Error>::value)\n  constexpr /* implicit */ Expected(Unexpected<Error>&& err) noexcept(\n      noexcept(Error(std::move(err.error()))))\n      : Base{expected_detail::ErrorTag{}, std::move(err.error())} {}\n\n  template <class OtherError FOLLY_REQUIRES_TRAILING(\n      std::is_convertible<const OtherError&, Error>::value)>\n  constexpr /* implicit */ Expected(const Unexpected<OtherError>& err) noexcept(\n      noexcept(Error(err.error())))\n      : Base{expected_detail::ErrorTag{}, Error(err.error())} {}\n\n  template <class OtherError FOLLY_REQUIRES_TRAILING(\n      std::is_convertible<OtherError&&, Error>::value)>\n  constexpr /* implicit  */ Expected(Unexpected<OtherError>&& err) noexcept(\n      noexcept(Error(std::move(err.error()))))\n      : Base{expected_detail::ErrorTag{}, Error(std::move(err.error()))} {}\n\n  /*\n   * Assignment operators\n   */\n  Expected& operator=(const Expected& that) = default;\n  Expected& operator=(Expected&& that) = default;\n\n  template <\n      class V,\n      class E FOLLY_REQUIRES_TRAILING(\n          !std::is_same<Expected<V, E>, Expected>::value &&\n          expected_detail::IsConvertible<V&&, Value>::value &&\n          expected_detail::IsConvertible<E&&, Error>::value)>\n  Expected& operator=(Expected<V, E> that) {\n    this->assign(std::move(that));\n    return *this;\n  }\n\n  FOLLY_REQUIRES(expected_detail::IsCopyable<Value>::value)\n  Expected& operator=(const Value& val) noexcept(\n      expected_detail::IsNothrowCopyable<Value>::value) {\n    this->assignValue(val);\n    return *this;\n  }\n\n  FOLLY_REQUIRES(expected_detail::IsMovable<Value>::value)\n  Expected& operator=(Value&& val) noexcept(\n      expected_detail::IsNothrowMovable<Value>::value) {\n    this->assignValue(std::move(val));\n    return *this;\n  }\n\n  template <class T FOLLY_REQUIRES_TRAILING(\n      std::is_convertible<T, Value>::value &&\n      !std::is_convertible<T, Error>::value)>\n  Expected& operator=(T&& val) {\n    this->assignValue(static_cast<T&&>(val));\n    return *this;\n  }\n\n  FOLLY_REQUIRES(expected_detail::IsCopyable<Error>::value)\n  Expected& operator=(const Unexpected<Error>& err) noexcept(\n      expected_detail::IsNothrowCopyable<Error>::value) {\n    this->assignError(err.error());\n    return *this;\n  }\n\n  FOLLY_REQUIRES(expected_detail::IsMovable<Error>::value)\n  Expected& operator=(Unexpected<Error>&& err) noexcept(\n      expected_detail::IsNothrowMovable<Error>::value) {\n    this->assignError(std::move(err.error()));\n    return *this;\n  }\n\n  template <class... Ts FOLLY_REQUIRES_TRAILING(\n      std::is_constructible<Value, Ts&&...>::value)>\n  void emplace(Ts&&... ts) {\n    this->assignValue(static_cast<Ts&&>(ts)...);\n  }\n\n  /**\n   * swap\n   */\n  void swap(Expected& that) noexcept(\n      std::is_nothrow_swappable_v<Value> &&\n      std::is_nothrow_swappable_v<Error>) {\n    if (this->uninitializedByException() || that.uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    using std::swap;\n    if (*this) {\n      if (that) {\n        swap(this->value_, that.value_);\n      } else {\n        Error e(std::move(that.error_));\n        that.assignValue(std::move(this->value_));\n        this->assignError(std::move(e));\n      }\n    } else {\n      if (!that) {\n        swap(this->error_, that.error_);\n      } else {\n        Error e(std::move(this->error_));\n        this->assignValue(std::move(that.value_));\n        that.assignError(std::move(e));\n      }\n    }\n  }\n\n  // If overload resolution selects one of these deleted functions, that\n  // means you need to use makeUnexpected\n  /* implicit */ Expected& operator=(const Error&) = delete;\n  /* implicit */ Expected& operator=(Error&&) = delete;\n\n  /**\n   * Relational Operators\n   */\n  template <class Val, class Err>\n  friend typename std::enable_if<IsEqualityComparable<Val>::value, bool>::type\n  operator==(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs);\n  template <class Val, class Err>\n  friend typename std::enable_if<IsLessThanComparable<Val>::value, bool>::type\n  operator<(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs);\n\n  /*\n   * Accessors\n   */\n  constexpr bool hasValue() const noexcept {\n    return FOLLY_LIKELY(expected_detail::Which::eValue == this->which_);\n  }\n\n  constexpr bool hasError() const noexcept {\n    return FOLLY_UNLIKELY(expected_detail::Which::eError == this->which_);\n  }\n\n  using Base::uninitializedByException;\n\n  const Value& value() const& {\n    requireValue();\n    return this->Base::value();\n  }\n\n  Value& value() & {\n    requireValue();\n    return this->Base::value();\n  }\n\n  const Value&& value() const&& {\n    requireValueMove();\n    return std::move(this->Base::value());\n  }\n\n  Value&& value() && {\n    requireValueMove();\n    return std::move(this->Base::value());\n  }\n\n  const Error& error() const& {\n    requireError();\n    return this->Base::error();\n  }\n\n  Error& error() & {\n    requireError();\n    return this->Base::error();\n  }\n\n  const Error&& error() const&& {\n    requireError();\n    return std::move(this->Base::error());\n  }\n\n  Error&& error() && {\n    requireError();\n    return std::move(this->Base::error());\n  }\n\n  // Return a copy of the value if set, or a given default if not.\n  template <class U>\n  Value value_or(U&& dflt) const& {\n    if (FOLLY_LIKELY(this->which_ == expected_detail::Which::eValue)) {\n      return this->value_;\n    }\n    return static_cast<U&&>(dflt);\n  }\n\n  template <class U>\n  Value value_or(U&& dflt) && {\n    if (FOLLY_LIKELY(this->which_ == expected_detail::Which::eValue)) {\n      return std::move(this->value_);\n    }\n    return static_cast<U&&>(dflt);\n  }\n\n  explicit constexpr operator bool() const noexcept { return hasValue(); }\n\n  const Value& operator*() const& { return this->value(); }\n\n  Value& operator*() & { return this->value(); }\n\n  Value&& operator*() && { return std::move(std::move(*this).value()); }\n\n  const Value* operator->() const { return std::addressof(this->value()); }\n\n  Value* operator->() { return std::addressof(this->value()); }\n\n  const Value* get_pointer() const& noexcept {\n    return hasValue() ? std::addressof(this->value_) : nullptr;\n  }\n\n  Value* get_pointer() & noexcept {\n    return hasValue() ? std::addressof(this->value_) : nullptr;\n  }\n\n  Value* get_pointer() && = delete;\n\n  /**\n   * then\n   */\n  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>\n  auto\n  then(Fns&&... fns) const& -> decltype(expected_detail::ExpectedHelper::then_(\n      std::declval<const Base&>(), std::declval<Fns>()...)) {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::then_(\n        base(), static_cast<Fns&&>(fns)...);\n  }\n\n  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>\n  auto then(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::then_(\n      std::declval<Base&>(), std::declval<Fns>()...)) {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::then_(\n        base(), static_cast<Fns&&>(fns)...);\n  }\n\n  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>\n  auto then(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::then_(\n      std::declval<Base&&>(), std::declval<Fns>()...)) {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::then_(\n        std::move(base()), static_cast<Fns&&>(fns)...);\n  }\n\n  /**\n   * orElse - returns if it has a value, otherwise it calls a function with the\n   * error type\n   */\n  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>\n  auto orElse(Fns&&... fns)\n      const& -> decltype(expected_detail::ExpectedHelper::orElse_(\n          std::declval<const Base&>(), std::declval<Fns>()...)) {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::orElse_(\n        base(), static_cast<Fns&&>(fns)...);\n  }\n\n  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>\n  auto\n  orElse(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::orElse_(\n      std::declval<Base&>(), std::declval<Fns>()...)) {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::orElse_(\n        base(), static_cast<Fns&&>(fns)...);\n  }\n\n  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>\n  auto\n  orElse(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::orElse_(\n      std::declval<Base&&>(), std::declval<Fns>()...)) {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::orElse_(\n        std::move(base()), static_cast<Fns&&>(fns)...);\n  }\n\n  /**\n   * thenOrThrow\n   */\n  template <class Yes, class No = MakeBadExpectedAccess>\n  auto thenOrThrow(Yes&& yes, No&& no = No{})\n      const& -> decltype(std::declval<Yes>()(std::declval<const Value&>())) {\n    using Ret = decltype(std::declval<Yes>()(std::declval<const Value&>()));\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return Ret(\n        expected_detail::ExpectedHelper::thenOrThrow_(\n            base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));\n  }\n\n  template <class Yes, class No = MakeBadExpectedAccess>\n  auto thenOrThrow(Yes&& yes, No&& no = No{}) & -> decltype(std::declval<Yes>()(\n      std::declval<Value&>())) {\n    using Ret = decltype(std::declval<Yes>()(std::declval<Value&>()));\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return Ret(\n        expected_detail::ExpectedHelper::thenOrThrow_(\n            base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));\n  }\n\n  template <class Yes, class No = MakeBadExpectedAccess>\n  auto\n  thenOrThrow(Yes&& yes, No&& no = No{}) && -> decltype(std::declval<Yes>()(\n      std::declval<Value&&>())) {\n    using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>()));\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return Ret(\n        expected_detail::ExpectedHelper::thenOrThrow_(\n            std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));\n  }\n\n  /**\n   * onError\n   */\n  template <class OnError>\n  auto onError(OnError&& onError) const& {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n\n    return expected_detail::ExpectedHelper::onError_(\n        *this, static_cast<OnError&&>(onError));\n  }\n\n  template <class OnError>\n  auto onError(OnError&& onError) & {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::onError_(\n        *this, static_cast<OnError&&>(onError));\n  }\n\n  template <class OnError>\n  auto onError(OnError&& onError) && {\n    if (this->uninitializedByException()) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n    return expected_detail::ExpectedHelper::onError_(\n        std::move(*this), static_cast<OnError&&>(onError));\n  }\n\n private:\n  friend struct expected_detail::PromiseReturn<Value, Error>;\n  using EmptyTag = expected_detail::EmptyTag;\n\n  explicit Expected(EmptyTag tag) noexcept : Base{tag} {}\n  // for when coroutine promise return-object conversion is eager\n  Expected(EmptyTag tag, Expected*& pointer) noexcept : Base{tag} {\n    pointer = this;\n  }\n\n  void requireValue() const {\n    if (FOLLY_UNLIKELY(!hasValue())) {\n      if (FOLLY_LIKELY(hasError())) {\n        throw_exception<BadExpectedAccess<Error>>(this->error_);\n      }\n      throw_exception<BadExpectedAccess<void>>();\n    }\n  }\n\n  template <typename Self>\n  static void requireValueMove(Self& self) {\n    if (FOLLY_UNLIKELY(!self.hasValue())) {\n      if (FOLLY_LIKELY(self.hasError())) {\n        throw_exception<BadExpectedAccess<Error>>(std::move(self.error_));\n      }\n      throw_exception<BadExpectedAccess<void>>();\n    }\n  }\n\n  void requireValueMove() { return requireValueMove(*this); }\n  void requireValueMove() const { return requireValueMove(*this); }\n\n  void requireError() const {\n    if (FOLLY_UNLIKELY(!hasError())) {\n      throw_exception<BadExpectedAccess<void>>();\n    }\n  }\n\n  expected_detail::Which which() const noexcept { return this->which_; }\n};\n\ntemplate <class Value, class Error>\ninline typename std::enable_if<IsEqualityComparable<Value>::value, bool>::type\noperator==(\n    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {\n  if (FOLLY_UNLIKELY(lhs.uninitializedByException())) {\n    throw_exception<BadExpectedAccess<void>>();\n  }\n  if (FOLLY_UNLIKELY(lhs.which_ != rhs.which_)) {\n    return false;\n  }\n  if (FOLLY_UNLIKELY(lhs.hasError())) {\n    return true; // All error states are considered equal\n  }\n  return lhs.value_ == rhs.value_;\n}\n\ntemplate <\n    class Value,\n    class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Value>::value)>\ninline bool operator!=(\n    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {\n  return !(rhs == lhs);\n}\n\ntemplate <class Value, class Error>\ninline typename std::enable_if<IsLessThanComparable<Value>::value, bool>::type\noperator<(\n    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {\n  if (FOLLY_UNLIKELY(\n          lhs.uninitializedByException() || rhs.uninitializedByException())) {\n    throw_exception<BadExpectedAccess<void>>();\n  }\n  if (FOLLY_UNLIKELY(lhs.hasError())) {\n    return !rhs.hasError();\n  }\n  if (FOLLY_UNLIKELY(rhs.hasError())) {\n    return false;\n  }\n  return lhs.value_ < rhs.value_;\n}\n\ntemplate <\n    class Value,\n    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>\ninline bool operator<=(\n    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {\n  return !(rhs < lhs);\n}\n\ntemplate <\n    class Value,\n    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>\ninline bool operator>(\n    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {\n  return rhs < lhs;\n}\n\ntemplate <\n    class Value,\n    class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>\ninline bool operator>=(\n    const Expected<Value, Error>& lhs, const Expected<Value, Error>& rhs) {\n  return !(lhs < rhs);\n}\n\n/**\n * swap Expected values\n */\ntemplate <class Value, class Error>\nvoid swap(Expected<Value, Error>& lhs, Expected<Value, Error>& rhs) noexcept(\n    std::is_nothrow_swappable_v<Value> && std::is_nothrow_swappable_v<Error>) {\n  lhs.swap(rhs);\n}\n\ntemplate <class Value, class Error>\nconst Value* get_pointer(const Expected<Value, Error>& ex) noexcept {\n  return ex.get_pointer();\n}\n\ntemplate <class Value, class Error>\nValue* get_pointer(Expected<Value, Error>& ex) noexcept {\n  return ex.get_pointer();\n}\n\n/**\n * For constructing an Expected object from a value, with the specified\n * Error type. Usage is as follows:\n *\n * enum MyErrorCode { BAD_ERROR, WORSE_ERROR };\n * Expected<int, MyErrorCode> myAPI() {\n *   int i = // ...;\n *   return i ? makeExpected<MyErrorCode>(i) : makeUnexpected(BAD_ERROR);\n * }\n */\ntemplate <class Error, class Value>\n[[nodiscard]] constexpr Expected<typename std::decay<Value>::type, Error>\nmakeExpected(Value&& val) {\n  return Expected<typename std::decay<Value>::type, Error>{\n      std::in_place, static_cast<Value&&>(val)};\n}\n\n// Suppress comparability of Expected<T> with T, despite implicit conversion.\ntemplate <class Value, class Error>\nbool operator==(const Expected<Value, Error>&, const Value& other) = delete;\ntemplate <class Value, class Error>\nbool operator!=(const Expected<Value, Error>&, const Value& other) = delete;\ntemplate <class Value, class Error>\nbool operator<(const Expected<Value, Error>&, const Value& other) = delete;\ntemplate <class Value, class Error>\nbool operator<=(const Expected<Value, Error>&, const Value& other) = delete;\ntemplate <class Value, class Error>\nbool operator>=(const Expected<Value, Error>&, const Value& other) = delete;\ntemplate <class Value, class Error>\nbool operator>(const Expected<Value, Error>&, const Value& other) = delete;\ntemplate <class Value, class Error>\nbool operator==(const Value& other, const Expected<Value, Error>&) = delete;\ntemplate <class Value, class Error>\nbool operator!=(const Value& other, const Expected<Value, Error>&) = delete;\ntemplate <class Value, class Error>\nbool operator<(const Value& other, const Expected<Value, Error>&) = delete;\ntemplate <class Value, class Error>\nbool operator<=(const Value& other, const Expected<Value, Error>&) = delete;\ntemplate <class Value, class Error>\nbool operator>=(const Value& other, const Expected<Value, Error>&) = delete;\ntemplate <class Value, class Error>\nbool operator>(const Value& other, const Expected<Value, Error>&) = delete;\n\n} // namespace folly\n\n#undef FOLLY_REQUIRES\n#undef FOLLY_REQUIRES_TRAILING\n\n// Enable the use of folly::Expected with `co_await`\n// Inspired by https://github.com/toby-allsopp/coroutine_monad\n//\n// NOTE: Please port applicable improvements to `folly/result/coro.h`.\n#if FOLLY_HAS_COROUTINES\n#include <folly/coro/Coroutine.h>\n\nnamespace folly {\nnamespace expected_detail {\ntemplate <typename Value, typename Error>\nstruct PromiseBase;\n\ntemplate <typename Value, typename Error>\nstruct PromiseReturn {\n  Expected<Value, Error> storage_{EmptyTag{}};\n  Expected<Value, Error>*& pointer_;\n\n  /* implicit */ PromiseReturn(PromiseBase<Value, Error>& p) noexcept\n      : pointer_{p.value_} {\n    pointer_ = &storage_;\n  }\n  PromiseReturn(PromiseReturn const&) = delete;\n  // letting dtor be trivial makes the coroutine crash\n  // TODO: fix clang/llvm codegen\n  ~PromiseReturn() {}\n  /* implicit */ operator Expected<Value, Error>() {\n    // handle both deferred and eager return-object conversion behaviors\n    // see docs for detect_promise_return_object_eager_conversion\n    if (folly::coro::detect_promise_return_object_eager_conversion()) {\n      assert(storage_.which_ == expected_detail::Which::eEmpty);\n      return Expected<Value, Error>{EmptyTag{}, pointer_}; // eager\n    } else {\n      assert(storage_.which_ != expected_detail::Which::eEmpty);\n      return std::move(storage_); // deferred\n    }\n  }\n};\n\ntemplate <typename Value, typename Error>\nstruct PromiseBase {\n  Expected<Value, Error>* value_ = nullptr;\n\n  PromiseBase() = default;\n  PromiseBase(PromiseBase const&) = delete;\n  void operator=(PromiseBase const&) = delete;\n\n  coro::suspend_never initial_suspend() const noexcept { return {}; }\n  coro::suspend_never final_suspend() const noexcept { return {}; }\n  [[noreturn]] void unhandled_exception() {\n    // Technically, throwing from unhandled_exception is underspecified:\n    // https://github.com/GorNishanov/CoroutineWording/issues/17\n    rethrow_current_exception();\n  }\n\n  PromiseReturn<Value, Error> get_return_object() noexcept { return *this; }\n};\n\ntemplate <typename Value>\ninline constexpr bool ReturnsVoid =\n    std::is_trivial_v<Value> && std::is_empty_v<Value>;\n\ntemplate <typename Value, typename Error>\nstruct PromiseReturnsValue : public PromiseBase<Value, Error> {\n  template <typename U = Value>\n  void return_value(U&& u) {\n    auto& v = *this->value_;\n    ExpectedHelper::assume_empty(v);\n    v = static_cast<U&&>(u);\n  }\n};\n\ntemplate <typename Value, typename Error>\nstruct PromiseReturnsVoid : public PromiseBase<Value, Error> {\n  // When the coroutine uses `return;` you can fail via `co_await err`.\n  void return_void() { this->value_->emplace(Value{}); }\n};\n\ntemplate <typename Value, typename Error>\nstruct Promise //\n    : conditional_t<\n          ReturnsVoid<Value>,\n          PromiseReturnsVoid<Value, Error>,\n          PromiseReturnsValue<Value, Error>> {};\n\ntemplate <typename Error>\nstruct UnexpectedAwaitable {\n  Unexpected<Error> o_;\n\n  explicit UnexpectedAwaitable(Unexpected<Error> o) : o_(std::move(o)) {}\n\n  constexpr std::false_type await_ready() const noexcept { return {}; }\n  void await_resume() { compiler_may_unsafely_assume_unreachable(); }\n\n  template <typename U>\n  FOLLY_ALWAYS_INLINE void\n  await_suspend(coro::coroutine_handle<Promise<U, Error>> h) noexcept(\n      IsNothrowMovable<Error>::value) {\n    auto& v = *h.promise().value_;\n    ExpectedHelper::assume_empty(v);\n    v = std::move(o_);\n    h.destroy();\n  }\n};\n\ntemplate <typename Value, typename Error>\nstruct ExpectedAwaitable {\n  Expected<Value, Error> o_;\n\n  explicit ExpectedAwaitable(Expected<Value, Error> o) : o_(std::move(o)) {}\n\n  bool await_ready() const noexcept { return o_.hasValue(); }\n  Value await_resume() { return std::move(o_.value()); }\n\n  // Explicitly only allow suspension into a Promise\n  template <typename U>\n  FOLLY_ALWAYS_INLINE void\n  await_suspend(coro::coroutine_handle<Promise<U, Error>> h) noexcept(\n      IsNothrowMovable<Error>::value) {\n    auto& v = *h.promise().value_;\n    ExpectedHelper::assume_empty(v);\n    v = makeUnexpected(std::move(o_.error()));\n    // Abort the rest of the coroutine. resume() is not going to be called\n    h.destroy();\n  }\n};\n\n} // namespace expected_detail\n\ntemplate <typename Error>\nexpected_detail::UnexpectedAwaitable<Error>\n/* implicit */ operator co_await(Unexpected<Error> o) {\n  return expected_detail::UnexpectedAwaitable<Error>{std::move(o)};\n}\n\ntemplate <typename Value, typename Error>\nexpected_detail::ExpectedAwaitable<Value, Error>\n/* implicit */ operator co_await(Expected<Value, Error> o) {\n  return expected_detail::ExpectedAwaitable<Value, Error>{std::move(o)};\n}\n\n} // namespace folly\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/FBString.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// String type.\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <cassert>\n#include <cstddef>\n#include <cstring>\n#include <iosfwd>\n#include <limits>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n\n#include <fmt/format.h>\n#include <folly/CPortability.h>\n#include <folly/CppAttributes.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/hash/Hash.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/CheckedMath.h>\n#include <folly/lang/Exception.h>\n#include <folly/memory/Malloc.h>\n\n#if FOLLY_CPLUSPLUS >= 202002L\n#include <compare>\n#endif\n\nFOLLY_PUSH_WARNING\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n\nnamespace folly {\n\n// When compiling with ASan, always heap-allocate the string even if\n// it would fit in-situ, so that ASan can detect access to the string\n// buffer after it has been invalidated (destroyed, resized, etc.).\n// Note that this flag doesn't remove support for in-situ strings, as\n// that would break ABI-compatibility and wouldn't allow linking code\n// compiled with this flag with code compiled without.\n#ifdef FOLLY_SANITIZE_ADDRESS\n#define FBSTRING_DISABLE_SSO true\n#else\n#define FBSTRING_DISABLE_SSO false\n#endif\n\nnamespace fbstring_detail {\n\ntemplate <class InIt, class OutIt>\ninline std::pair<InIt, OutIt> copy_n(\n    InIt b, typename std::iterator_traits<InIt>::difference_type n, OutIt d) {\n  for (; n != 0; --n, ++b, ++d) {\n    *d = *b;\n  }\n  return std::make_pair(b, d);\n}\n\ntemplate <class Pod, class T>\ninline void podFill(Pod* b, Pod* e, T c) {\n  assert(b && e && b <= e);\n  constexpr auto kUseMemset = sizeof(T) == 1;\n  if constexpr (kUseMemset) {\n    memset(b, c, size_t(e - b));\n  } else {\n    auto const ee = b + ((e - b) & ~7u);\n    for (; b != ee; b += 8) {\n      b[0] = c;\n      b[1] = c;\n      b[2] = c;\n      b[3] = c;\n      b[4] = c;\n      b[5] = c;\n      b[6] = c;\n      b[7] = c;\n    }\n    // Leftovers\n    for (; b != e; ++b) {\n      *b = c;\n    }\n  }\n}\n\n/*\n * Lightly structured memcpy, simplifies copying PODs and introduces\n * some asserts. Unfortunately using this function may cause\n * measurable overhead (presumably because it adjusts from a begin/end\n * convention to a pointer/size convention, so it does some extra\n * arithmetic even though the caller might have done the inverse\n * adaptation outside).\n */\ntemplate <class Pod>\ninline void podCopy(const Pod* b, const Pod* e, Pod* d) {\n  assert(b != nullptr);\n  assert(e != nullptr);\n  assert(d != nullptr);\n  assert(e >= b);\n  assert(d >= e || d + (e - b) <= b);\n  memcpy(d, b, (e - b) * sizeof(Pod));\n}\n\n/*\n * Lightly structured memmove, simplifies copying PODs and introduces\n * some asserts\n */\ntemplate <class Pod>\ninline void podMove(const Pod* b, const Pod* e, Pod* d) {\n  assert(e >= b);\n  memmove(d, b, (e - b) * sizeof(*b));\n}\n} // namespace fbstring_detail\n\n/**\n * Defines a special acquisition method for constructing fbstring\n * objects. AcquireMallocatedString means that the user passes a\n * pointer to a malloc-allocated string that the fbstring object will\n * take into custody.\n */\nenum class AcquireMallocatedString {};\n\n/*\n * fbstring_core_model is a mock-up type that defines all required\n * signatures of a fbstring core. The fbstring class itself uses such\n * a core object to implement all of the numerous member functions\n * required by the standard.\n *\n * If you want to define a new core, copy the definition below and\n * implement the primitives. Then plug the core into basic_fbstring as\n * a template argument.\n\ntemplate <class Char>\nclass fbstring_core_model {\n public:\n  fbstring_core_model();\n  fbstring_core_model(const fbstring_core_model &);\n  fbstring_core_model& operator=(const fbstring_core_model &) = delete;\n  ~fbstring_core_model();\n  // Returns a pointer to string's buffer (currently only contiguous\n  // strings are supported). The pointer is guaranteed to be valid\n  // until the next call to a non-const member function.\n  const Char * data() const;\n  // Much like data(), except the string is prepared to support\n  // character-level changes. This call is a signal for\n  // e.g. reference-counted implementation to fork the data. The\n  // pointer is guaranteed to be valid until the next call to a\n  // non-const member function.\n  Char* mutableData();\n  // Returns a pointer to string's buffer and guarantees that a\n  // readable '\\0' lies right after the buffer. The pointer is\n  // guaranteed to be valid until the next call to a non-const member\n  // function.\n  const Char * c_str() const;\n  // Shrinks the string by delta characters. Asserts that delta <=\n  // size().\n  void shrink(size_t delta);\n  // Expands the string by delta characters (i.e. after this call\n  // size() will report the old size() plus delta) but without\n  // initializing the expanded region. The expanded region is\n  // zero-terminated. Returns a pointer to the memory to be\n  // initialized (the beginning of the expanded portion). The caller\n  // is expected to fill the expanded area appropriately.\n  // If expGrowth is true, exponential growth is guaranteed.\n  // It is not guaranteed not to reallocate even if size() + delta <\n  // capacity(), so all references to the buffer are invalidated.\n  Char* expandNoinit(size_t delta, bool expGrowth);\n  // Expands the string by one character and sets the last character\n  // to c.\n  void push_back(Char c);\n  // Returns the string's size.\n  size_t size() const;\n  // Returns the string's capacity, i.e. maximum size that the string\n  // can grow to without reallocation. Note that for reference counted\n  // strings that's technically a lie - even assigning characters\n  // within the existing size would cause a reallocation.\n  size_t capacity() const;\n  // Returns true if the data underlying the string is actually shared\n  // across multiple strings (in a refcounted fashion).\n  bool isShared() const;\n  // Makes sure that at least minCapacity characters are available for\n  // the string without reallocation. For reference-counted strings,\n  // it should fork the data even if minCapacity < size().\n  void reserve(size_t minCapacity);\n};\n*/\n\n/**\n * This is the core of the string. The code should work on 32- and\n * 64-bit and both big- and little-endian architectures with any\n * Char size.\n *\n * The storage is selected as follows (assuming we store one-byte\n * characters on a 64-bit machine): (a) \"small\" strings between 0 and\n * 23 chars are stored in-situ without allocation (the rightmost byte\n * stores the size); (b) \"medium\" strings from 24 through 254 chars\n * are stored in malloc-allocated memory that is copied eagerly; (c)\n * \"large\" strings of 255 chars and above are stored in a similar\n * structure as medium arrays, except that the string is\n * reference-counted and copied lazily. the reference count is\n * allocated right before the character array.\n *\n * The discriminator between these three strategies sits in two\n * bits of the rightmost char of the storage:\n * - If neither is set, then the string is small. Its length is represented by\n *   the lower-order bits on little-endian or the high-order bits on big-endian\n *   of that rightmost character. The value of these six bits is\n *   `maxSmallSize - size`, so this quantity must be subtracted from\n *   `maxSmallSize` to compute the `size` of the string (see `smallSize()`).\n *   This scheme ensures that when `size == `maxSmallSize`, the last byte in the\n *   storage is \\0. This way, storage will be a null-terminated sequence of\n *   bytes, even if all 23 bytes of data are used on a 64-bit architecture.\n *   This enables `c_str()` and `data()` to simply return a pointer to the\n *   storage.\n *\n * - If the MSb is set, the string is medium width.\n *\n * - If the second MSb is set, then the string is large. On little-endian,\n *   these 2 bits are the 2 MSbs of MediumLarge::capacity_, while on\n *   big-endian, these 2 bits are the 2 LSbs. This keeps both little-endian\n *   and big-endian fbstring_core equivalent with merely different ops used\n *   to extract capacity/category.\n */\ntemplate <class Char>\nclass fbstring_core {\n public:\n  fbstring_core() noexcept { reset(); }\n\n  fbstring_core(const fbstring_core& rhs) {\n    assert(&rhs != this);\n    switch (rhs.category()) {\n      case Category::isSmall:\n        copySmall(rhs);\n        break;\n      case Category::isMedium:\n        copyMedium(rhs);\n        break;\n      case Category::isLarge:\n        copyLarge(rhs);\n        break;\n      default:\n        folly::assume_unreachable();\n    }\n    assert(size() == rhs.size());\n    assert(memcmp(data(), rhs.data(), size() * sizeof(Char)) == 0);\n  }\n\n  fbstring_core& operator=(const fbstring_core& rhs) = delete;\n\n  fbstring_core(fbstring_core&& goner) noexcept {\n    // Take goner's guts\n    ml_ = goner.ml_;\n    // Clean goner's carcass\n    goner.reset();\n  }\n\n  fbstring_core(\n      const Char* const data,\n      const size_t size,\n      bool disableSSO = FBSTRING_DISABLE_SSO) {\n    if (!disableSSO && size <= maxSmallSize) {\n      initSmall(data, size);\n    } else if (size <= maxMediumSize) {\n      initMedium(data, size);\n    } else {\n      initLarge(data, size);\n    }\n    assert(this->size() == size);\n    assert(size == 0 || memcmp(this->data(), data, size * sizeof(Char)) == 0);\n  }\n\n  ~fbstring_core() noexcept {\n    if (category() == Category::isSmall) {\n      return;\n    }\n    destroyMediumLarge();\n  }\n\n  // Snatches a previously mallocated string. The parameter \"size\"\n  // is the size of the string, and the parameter \"allocatedSize\"\n  // is the size of the mallocated block.  The string must be\n  // \\0-terminated, so allocatedSize >= size + 1 and data[size] == '\\0'.\n  //\n  // So if you want a 2-character string, pass malloc(3) as \"data\",\n  // pass 2 as \"size\", and pass 3 as \"allocatedSize\".\n  fbstring_core(\n      Char* const data,\n      const size_t size,\n      const size_t allocatedSize,\n      AcquireMallocatedString) {\n    if (size > 0) {\n      assert(allocatedSize >= size + 1);\n      assert(data[size] == '\\0');\n      // Use the medium string storage\n      ml_.data_ = data;\n      ml_.size_ = size;\n      // Don't forget about null terminator\n      ml_.setCapacity(allocatedSize - 1, Category::isMedium);\n    } else {\n      // No need for the memory\n      free(data);\n      reset();\n    }\n  }\n\n  // swap below doesn't test whether &rhs == this (and instead\n  // potentially does extra work) on the premise that the rarity of\n  // that situation actually makes the check more expensive than is\n  // worth.\n  void swap(fbstring_core& rhs) {\n    auto const t = ml_;\n    ml_ = rhs.ml_;\n    rhs.ml_ = t;\n  }\n\n  // In C++11 data() and c_str() are 100% equivalent.\n  const Char* data() const { return c_str(); }\n\n  Char* data() { return c_str(); }\n\n  Char* mutableData() {\n    switch (category()) {\n      case Category::isSmall:\n        return small_;\n      case Category::isMedium:\n        return ml_.data_;\n      case Category::isLarge:\n        return mutableDataLarge();\n      default:\n        folly::assume_unreachable();\n    }\n  }\n\n  const Char* c_str() const {\n    const Char* ptr = ml_.data_;\n    // With this syntax, GCC and Clang generate a CMOV instead of a branch.\n    ptr = (category() == Category::isSmall) ? small_ : ptr;\n    return ptr;\n  }\n\n  void shrink(const size_t delta) {\n    if (category() == Category::isSmall) {\n      shrinkSmall(delta);\n    } else if (\n        category() == Category::isMedium || RefCounted::refs(ml_.data_) == 1) {\n      shrinkMedium(delta);\n    } else {\n      shrinkLarge(delta);\n    }\n  }\n\n  FOLLY_NOINLINE\n  void reserve(size_t minCapacity, bool disableSSO = FBSTRING_DISABLE_SSO) {\n    FOLLY_PUSH_WARNING\n    FOLLY_CLANG_DISABLE_WARNING(\"-Wcovered-switch-default\")\n    switch (category()) {\n      case Category::isSmall:\n        reserveSmall(minCapacity, disableSSO);\n        break;\n      case Category::isMedium:\n        reserveMedium(minCapacity);\n        break;\n      case Category::isLarge:\n        reserveLarge(minCapacity);\n        break;\n      default:\n        folly::assume_unreachable();\n    }\n    FOLLY_POP_WARNING\n    assert(capacity() >= minCapacity);\n  }\n\n  Char* expandNoinit(\n      const size_t delta,\n      bool expGrowth = false,\n      bool disableSSO = FBSTRING_DISABLE_SSO);\n\n  void push_back(Char c) { *expandNoinit(1, /* expGrowth = */ true) = c; }\n\n  size_t size() const {\n    size_t ret = ml_.size_;\n    if constexpr (kIsLittleEndian) {\n      // We can save a couple instructions, because the category is\n      // small iff the last char, as unsigned, is <= maxSmallSize.\n      typedef typename std::make_unsigned<Char>::type UChar;\n      auto maybeSmallSize = size_t(maxSmallSize) -\n          size_t(static_cast<UChar>(small_[maxSmallSize]));\n      // With this syntax, GCC and Clang generate a CMOV instead of a branch.\n      ret =\n          (static_cast<ptrdiff_t>(maybeSmallSize) >= 0) ? maybeSmallSize : ret;\n    } else {\n      ret = (category() == Category::isSmall) ? smallSize() : ret;\n    }\n    return ret;\n  }\n\n  size_t capacity() const {\n    FOLLY_PUSH_WARNING\n    FOLLY_CLANG_DISABLE_WARNING(\"-Wcovered-switch-default\")\n    switch (category()) {\n      case Category::isSmall:\n        return maxSmallSize;\n      case Category::isLarge:\n        // For large-sized strings, a multi-referenced chunk has no\n        // available capacity. This is because any attempt to append\n        // data would trigger a new allocation.\n        if (RefCounted::refs(ml_.data_) > 1) {\n          return ml_.size_;\n        }\n        break;\n      case Category::isMedium:\n      default:\n        break;\n    }\n    FOLLY_POP_WARNING\n    return ml_.capacity();\n  }\n\n  bool isShared() const {\n    return category() == Category::isLarge && RefCounted::refs(ml_.data_) > 1;\n  }\n\n  // Returns the reference count for Large (RefCounted) strings, or 1 for\n  // Small/Medium strings which are not reference counted.\n  size_t useCount() const {\n    if (category() == Category::isLarge) {\n      return RefCounted::refs(ml_.data_);\n    }\n    return 1;\n  }\n\n  // Returns true if the string is using RefCounted (Large) storage mode.\n  // Large strings have a RefCounted header that adds sizeof(size_t) bytes\n  // to the allocation, which is important for accurate memory accounting.\n  bool isCounted() const { return category() == Category::isLarge; }\n\n private:\n  Char* c_str() {\n    Char* ptr = ml_.data_;\n    // With this syntax, GCC and Clang generate a CMOV instead of a branch.\n    ptr = (category() == Category::isSmall) ? small_ : ptr;\n    return ptr;\n  }\n\n  void reset() { setSmallSize(0); }\n\n  FOLLY_NOINLINE void destroyMediumLarge() noexcept {\n    auto const c = category();\n    assert(c != Category::isSmall);\n    if (c == Category::isMedium) {\n      free(ml_.data_);\n    } else {\n      RefCounted::decrementRefs(ml_.data_);\n    }\n  }\n\n  struct RefCounted {\n    std::atomic<size_t> refCount_;\n    Char data_[1];\n\n    constexpr static size_t getDataOffset() {\n      return offsetof(RefCounted, data_);\n    }\n\n    static RefCounted* fromData(Char* p) {\n      return static_cast<RefCounted*>(static_cast<void*>(\n          static_cast<unsigned char*>(static_cast<void*>(p)) -\n          getDataOffset()));\n    }\n\n    static size_t refs(Char* p) {\n      return fromData(p)->refCount_.load(std::memory_order_acquire);\n    }\n\n    static void incrementRefs(Char* p) {\n      fromData(p)->refCount_.fetch_add(1, std::memory_order_acq_rel);\n    }\n\n    static void decrementRefs(Char* p) {\n      auto const dis = fromData(p);\n      size_t oldcnt = dis->refCount_.fetch_sub(1, std::memory_order_acq_rel);\n      assert(oldcnt > 0);\n      if (oldcnt == 1) {\n        ::free(dis);\n      }\n    }\n\n    static RefCounted* create(size_t* size) {\n      size_t capacityBytes;\n      if (!folly::checked_add(&capacityBytes, *size, size_t(1))) {\n        throw_exception(std::length_error(\"\"));\n      }\n      if (!folly::checked_muladd(\n              &capacityBytes, capacityBytes, sizeof(Char), getDataOffset())) {\n        throw_exception(std::length_error(\"\"));\n      }\n      const size_t allocSize = goodMallocSize(capacityBytes);\n      auto result = static_cast<RefCounted*>(checkedMalloc(allocSize));\n      result->refCount_.store(1, std::memory_order_release);\n      *size = (allocSize - getDataOffset()) / sizeof(Char) - 1;\n      return result;\n    }\n\n    static RefCounted* create(const Char* data, size_t* size) {\n      const size_t effectiveSize = *size;\n      auto result = create(size);\n      if (FOLLY_LIKELY(effectiveSize > 0)) {\n        fbstring_detail::podCopy(data, data + effectiveSize, result->data_);\n      }\n      return result;\n    }\n\n    static RefCounted* reallocate(\n        Char* const data,\n        const size_t currentSize,\n        const size_t currentCapacity,\n        size_t* newCapacity) {\n      assert(*newCapacity > 0 && *newCapacity > currentSize);\n      size_t capacityBytes;\n      if (!folly::checked_add(&capacityBytes, *newCapacity, size_t(1))) {\n        throw_exception(std::length_error(\"\"));\n      }\n      if (!folly::checked_muladd(\n              &capacityBytes, capacityBytes, sizeof(Char), getDataOffset())) {\n        throw_exception(std::length_error(\"\"));\n      }\n      const size_t allocNewCapacity = goodMallocSize(capacityBytes);\n      auto const dis = fromData(data);\n      assert(dis->refCount_.load(std::memory_order_acquire) == 1);\n      auto result = static_cast<RefCounted*>(smartRealloc(\n          dis,\n          getDataOffset() + (currentSize + 1) * sizeof(Char),\n          getDataOffset() + (currentCapacity + 1) * sizeof(Char),\n          allocNewCapacity));\n      assert(result->refCount_.load(std::memory_order_acquire) == 1);\n      *newCapacity = (allocNewCapacity - getDataOffset()) / sizeof(Char) - 1;\n      return result;\n    }\n  };\n\n  typedef uint8_t category_type;\n\n  enum class Category : category_type {\n    isSmall = 0,\n    isMedium = kIsLittleEndian ? 0x80 : 0x2,\n    isLarge = kIsLittleEndian ? 0x40 : 0x1,\n  };\n\n  Category category() const {\n    // works for both big-endian and little-endian\n    return static_cast<Category>(bytes_[lastChar] & categoryExtractMask);\n  }\n\n  struct MediumLarge {\n    Char* data_;\n    size_t size_;\n    size_t capacity_;\n\n    size_t capacity() const {\n      return kIsLittleEndian ? capacity_ & capacityExtractMask : capacity_ >> 2;\n    }\n\n    void setCapacity(size_t cap, Category cat) {\n      capacity_ = kIsLittleEndian\n          ? cap | (static_cast<size_t>(cat) << kCategoryShift)\n          : (cap << 2) | static_cast<size_t>(cat);\n    }\n  };\n\n  union {\n    uint8_t bytes_[sizeof(MediumLarge)]; // For accessing the last byte.\n    Char small_[sizeof(MediumLarge) / sizeof(Char)];\n    MediumLarge ml_;\n  };\n\n  constexpr static size_t lastChar = sizeof(MediumLarge) - 1;\n  constexpr static size_t maxSmallSize = lastChar / sizeof(Char);\n  constexpr static size_t maxMediumSize = 254 / sizeof(Char);\n  constexpr static uint8_t categoryExtractMask = kIsLittleEndian ? 0xC0 : 0x3;\n  constexpr static size_t kCategoryShift = (sizeof(size_t) - 1) * 8;\n  constexpr static size_t capacityExtractMask = kIsLittleEndian\n      ? ~(size_t(categoryExtractMask) << kCategoryShift)\n      : 0x0 /* unused */;\n\n  static_assert(\n      !(sizeof(MediumLarge) % sizeof(Char)),\n      \"Corrupt memory layout for fbstring.\");\n\n  size_t smallSize() const {\n    assert(category() == Category::isSmall);\n    constexpr auto shift = kIsLittleEndian ? 0 : 2;\n    auto smallShifted = static_cast<size_t>(small_[maxSmallSize]) >> shift;\n    assert(static_cast<size_t>(maxSmallSize) >= smallShifted);\n    return static_cast<size_t>(maxSmallSize) - smallShifted;\n  }\n\n  void setSmallSize(size_t s) {\n    // Warning: this should work with uninitialized strings too,\n    // so don't assume anything about the previous value of\n    // small_[maxSmallSize].\n    assert(s <= maxSmallSize);\n    constexpr auto shift = kIsLittleEndian ? 0 : 2;\n    small_[maxSmallSize] = char((maxSmallSize - s) << shift);\n    small_[s] = '\\0';\n    assert(category() == Category::isSmall && size() == s);\n  }\n\n  void copySmall(const fbstring_core&);\n  void copyMedium(const fbstring_core&);\n  void copyLarge(const fbstring_core&);\n\n  void initSmall(const Char* data, size_t size);\n  void initMedium(const Char* data, size_t size);\n  void initLarge(const Char* data, size_t size);\n\n  void reserveSmall(size_t minCapacity, bool disableSSO);\n  void reserveMedium(size_t minCapacity);\n  void reserveLarge(size_t minCapacity);\n\n  void shrinkSmall(size_t delta);\n  void shrinkMedium(size_t delta);\n  void shrinkLarge(size_t delta);\n\n  void unshare(size_t minCapacity = 0);\n  Char* mutableDataLarge();\n};\n\ntemplate <class Char>\ninline void fbstring_core<Char>::copySmall(const fbstring_core& rhs) {\n  static_assert(offsetof(MediumLarge, data_) == 0, \"fbstring layout failure\");\n  static_assert(\n      offsetof(MediumLarge, size_) == sizeof(ml_.data_),\n      \"fbstring layout failure\");\n  static_assert(\n      offsetof(MediumLarge, capacity_) == 2 * sizeof(ml_.data_),\n      \"fbstring layout failure\");\n  // Just write the whole thing, don't look at details. In\n  // particular we need to copy capacity anyway because we want\n  // to set the size (don't forget that the last character,\n  // which stores a short string's length, is shared with the\n  // ml_.capacity field).\n  ml_ = rhs.ml_;\n  assert(category() == Category::isSmall && this->size() == rhs.size());\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::copyMedium(const fbstring_core& rhs) {\n  // Medium strings are copied eagerly. Don't forget to allocate\n  // one extra Char for the null terminator.\n  auto const allocSize = goodMallocSize((1 + rhs.ml_.size_) * sizeof(Char));\n  ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));\n  // Also copies terminator.\n  fbstring_detail::podCopy(\n      rhs.ml_.data_, rhs.ml_.data_ + rhs.ml_.size_ + 1, ml_.data_);\n  ml_.size_ = rhs.ml_.size_;\n  ml_.setCapacity(allocSize / sizeof(Char) - 1, Category::isMedium);\n  assert(category() == Category::isMedium);\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::copyLarge(const fbstring_core& rhs) {\n  // Large strings are just refcounted\n  ml_ = rhs.ml_;\n  RefCounted::incrementRefs(ml_.data_);\n  assert(category() == Category::isLarge && size() == rhs.size());\n}\n\n// Small strings are bitblitted\ntemplate <class Char>\ninline void fbstring_core<Char>::initSmall(\n    const Char* const data, const size_t size) {\n  // Layout is: Char* data_, size_t size_, size_t capacity_\n  static_assert(\n      sizeof(*this) == sizeof(Char*) + 2 * sizeof(size_t),\n      \"fbstring has unexpected size\");\n  static_assert(\n      sizeof(Char*) == sizeof(size_t), \"fbstring size assumption violation\");\n  // sizeof(size_t) must be a power of 2\n  static_assert(\n      (sizeof(size_t) & (sizeof(size_t) - 1)) == 0,\n      \"fbstring size assumption violation\");\n\n  constexpr size_t kPageSize = 4096;\n\n  const auto addr = reinterpret_cast<uintptr_t>(data);\n  if (!kIsSanitize && // sanitizer would trap on over-reads\n      size && (addr ^ (addr + sizeof(small_) - 1)) < kPageSize) {\n    // the input data is all within one page so over-reads will not segfault\n    std::memcpy(small_, data, sizeof(small_)); // lowers to a 4-insn sequence\n  } else {\n    if (size != 0) {\n      fbstring_detail::podCopy(data, data + size, small_);\n    }\n  }\n  setSmallSize(size);\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::initMedium(\n    const Char* const data, const size_t size) {\n  // Medium strings are allocated normally. Don't forget to\n  // allocate one extra Char for the terminating null.\n  auto const allocSize = goodMallocSize((1 + size) * sizeof(Char));\n  ml_.data_ = static_cast<Char*>(checkedMalloc(allocSize));\n  if (FOLLY_LIKELY(size > 0)) {\n    fbstring_detail::podCopy(data, data + size, ml_.data_);\n  }\n  ml_.size_ = size;\n  ml_.setCapacity(allocSize / sizeof(Char) - 1, Category::isMedium);\n  ml_.data_[size] = '\\0';\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::initLarge(\n    const Char* const data, const size_t size) {\n  // Large strings are allocated differently\n  size_t effectiveCapacity = size;\n  auto const newRC = RefCounted::create(data, &effectiveCapacity);\n  ml_.data_ = newRC->data_;\n  ml_.size_ = size;\n  ml_.setCapacity(effectiveCapacity, Category::isLarge);\n  ml_.data_[size] = '\\0';\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::unshare(size_t minCapacity) {\n  assert(category() == Category::isLarge);\n  size_t effectiveCapacity = std::max(minCapacity, ml_.capacity());\n  auto const newRC = RefCounted::create(&effectiveCapacity);\n  // If this fails, someone placed the wrong capacity in an\n  // fbstring.\n  assert(effectiveCapacity >= ml_.capacity());\n  // Also copies terminator.\n  fbstring_detail::podCopy(ml_.data_, ml_.data_ + ml_.size_ + 1, newRC->data_);\n  RefCounted::decrementRefs(ml_.data_);\n  ml_.data_ = newRC->data_;\n  ml_.setCapacity(effectiveCapacity, Category::isLarge);\n  // size_ remains unchanged.\n}\n\ntemplate <class Char>\ninline Char* fbstring_core<Char>::mutableDataLarge() {\n  assert(category() == Category::isLarge);\n  if (RefCounted::refs(ml_.data_) > 1) { // Ensure unique.\n    unshare();\n  }\n  return ml_.data_;\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::reserveLarge(size_t minCapacity) {\n  assert(category() == Category::isLarge);\n  if (RefCounted::refs(ml_.data_) > 1) { // Ensure unique\n    // We must make it unique regardless; in-place reallocation is\n    // useless if the string is shared. In order to not surprise\n    // people, reserve the new block at current capacity or\n    // more. That way, a string's capacity never shrinks after a\n    // call to reserve.\n    unshare(minCapacity);\n  } else {\n    // String is not shared, so let's try to realloc (if needed)\n    if (minCapacity > ml_.capacity()) {\n      // Asking for more memory\n      auto const newRC = RefCounted::reallocate(\n          ml_.data_, ml_.size_, ml_.capacity(), &minCapacity);\n      ml_.data_ = newRC->data_;\n      ml_.setCapacity(minCapacity, Category::isLarge);\n    }\n    assert(capacity() >= minCapacity);\n  }\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::reserveMedium(\n    const size_t minCapacity) {\n  assert(category() == Category::isMedium);\n  // String is not shared\n  if (minCapacity <= ml_.capacity()) {\n    return; // nothing to do, there's enough room\n  }\n  if (minCapacity <= maxMediumSize) {\n    // Keep the string at medium size. Don't forget to allocate\n    // one extra Char for the terminating null.\n    size_t capacityBytes = goodMallocSize((1 + minCapacity) * sizeof(Char));\n    // Also copies terminator.\n    ml_.data_ = static_cast<Char*>(smartRealloc(\n        ml_.data_,\n        (ml_.size_ + 1) * sizeof(Char),\n        (ml_.capacity() + 1) * sizeof(Char),\n        capacityBytes));\n    ml_.setCapacity(capacityBytes / sizeof(Char) - 1, Category::isMedium);\n  } else {\n    // Conversion from medium to large string\n    fbstring_core nascent;\n    // Will recurse to another branch of this function\n    nascent.reserve(minCapacity);\n    nascent.ml_.size_ = ml_.size_;\n    // Also copies terminator.\n    fbstring_detail::podCopy(\n        ml_.data_, ml_.data_ + ml_.size_ + 1, nascent.ml_.data_);\n    nascent.swap(*this);\n    assert(capacity() >= minCapacity);\n  }\n}\n\ntemplate <class Char>\nFOLLY_NOINLINE void fbstring_core<Char>::reserveSmall(\n    size_t minCapacity, const bool disableSSO) {\n  assert(category() == Category::isSmall);\n  if (!disableSSO && minCapacity <= maxSmallSize) {\n    // small\n    // Nothing to do, everything stays put\n  } else if (minCapacity <= maxMediumSize) {\n    // medium\n    // Don't forget to allocate one extra Char for the terminating null\n    auto const allocSizeBytes =\n        goodMallocSize((1 + minCapacity) * sizeof(Char));\n    auto const pData = static_cast<Char*>(checkedMalloc(allocSizeBytes));\n    auto const size = smallSize();\n    // Also copies terminator.\n    fbstring_detail::podCopy(small_, small_ + size + 1, pData);\n    ml_.data_ = pData;\n    ml_.size_ = size;\n    ml_.setCapacity(allocSizeBytes / sizeof(Char) - 1, Category::isMedium);\n  } else {\n    // large\n    auto const newRC = RefCounted::create(&minCapacity);\n    auto const size = smallSize();\n    // Also copies terminator.\n    fbstring_detail::podCopy(small_, small_ + size + 1, newRC->data_);\n    ml_.data_ = newRC->data_;\n    ml_.size_ = size;\n    ml_.setCapacity(minCapacity, Category::isLarge);\n    assert(capacity() >= minCapacity);\n  }\n}\n\ntemplate <class Char>\ninline Char* fbstring_core<Char>::expandNoinit(\n    const size_t delta,\n    bool expGrowth, /* = false */\n    bool disableSSO /* = FBSTRING_DISABLE_SSO */) {\n  // Strategy is simple: make room, then change size\n  assert(capacity() >= size());\n  size_t sz, newSz;\n  if (category() == Category::isSmall) {\n    sz = smallSize();\n    newSz = sz + delta;\n    if (!disableSSO && FOLLY_LIKELY(newSz <= maxSmallSize)) {\n      setSmallSize(newSz);\n      return small_ + sz;\n    }\n    reserveSmall(\n        expGrowth ? std::max(newSz, 2 * maxSmallSize) : newSz, disableSSO);\n  } else {\n    sz = ml_.size_;\n    newSz = sz + delta;\n    if (FOLLY_UNLIKELY(newSz > capacity())) {\n      // ensures not shared\n      reserve(expGrowth ? std::max(newSz, 1 + capacity() * 3 / 2) : newSz);\n    }\n  }\n  assert(capacity() >= newSz);\n  // Category can't be small - we took care of that above\n  assert(category() == Category::isMedium || category() == Category::isLarge);\n  ml_.size_ = newSz;\n  ml_.data_[newSz] = '\\0';\n  assert(size() == newSz);\n  return ml_.data_ + sz;\n}\n\ntemplate <class Char>\ninline void fbstring_core<Char>::shrinkSmall(const size_t delta) {\n  // Check for underflow\n  assert(delta <= smallSize());\n  setSmallSize(smallSize() - delta);\n}\n\ntemplate <class Char>\ninline void fbstring_core<Char>::shrinkMedium(const size_t delta) {\n  // Medium strings and unique large strings need no special\n  // handling.\n  assert(ml_.size_ >= delta);\n  ml_.size_ -= delta;\n  ml_.data_[ml_.size_] = '\\0';\n}\n\ntemplate <class Char>\ninline void fbstring_core<Char>::shrinkLarge(const size_t delta) {\n  assert(ml_.size_ >= delta);\n  // Shared large string, must make unique. This is because of the\n  // durn terminator must be written, which may trample the shared\n  // data.\n  if (delta) {\n    fbstring_core(ml_.data_, ml_.size_ - delta).swap(*this);\n  }\n  // No need to write the terminator.\n}\n\n/**\n * Dummy fbstring core that uses an actual std::string. This doesn't\n * make any sense - it's just for testing purposes.\n */\ntemplate <class Char>\nclass dummy_fbstring_core {\n public:\n  dummy_fbstring_core() {}\n  dummy_fbstring_core(const dummy_fbstring_core& another)\n      : backend_(another.backend_) {}\n  dummy_fbstring_core(const Char* s, size_t n) : backend_(s, n) {}\n  void swap(dummy_fbstring_core& rhs) { backend_.swap(rhs.backend_); }\n  const Char* data() const { return backend_.data(); }\n  Char* mutableData() { return const_cast<Char*>(backend_.data()); }\n  void shrink(size_t delta) {\n    assert(delta <= size());\n    backend_.resize(size() - delta);\n  }\n  Char* expandNoinit(size_t delta) {\n    auto const sz = size();\n    backend_.resize(size() + delta);\n    return backend_.data() + sz;\n  }\n  void push_back(Char c) { backend_.push_back(c); }\n  size_t size() const { return backend_.size(); }\n  size_t capacity() const { return backend_.capacity(); }\n  bool isShared() const { return false; }\n  size_t useCount() const { return 1; }\n  bool isCounted() const { return false; }\n  void reserve(size_t minCapacity) { backend_.reserve(minCapacity); }\n\n private:\n  std::basic_string<Char> backend_;\n};\n\n/**\n * This is the basic_string replacement. For conformity,\n * basic_fbstring takes the same template parameters, plus the last\n * one which is the core.\n */\ntemplate <\n    typename E,\n    class T = std::char_traits<E>,\n    class A = std::allocator<E>,\n    class Storage = fbstring_core<E>>\nclass basic_fbstring {\n  static_assert(\n      std::is_same<A, std::allocator<E>>::value,\n      \"fbstring ignores custom allocators\");\n\n  template <typename Ex, typename... Args>\n  FOLLY_ALWAYS_INLINE static void enforce(bool condition, Args&&... args) {\n    if (!condition) {\n      throw_exception<Ex>(static_cast<Args&&>(args)...);\n    }\n  }\n\n  bool isSane() const {\n    return begin() <= end() && empty() == (size() == 0) &&\n        empty() == (begin() == end()) && size() <= max_size() &&\n        capacity() <= max_size() && size() <= capacity() &&\n        begin()[size()] == '\\0';\n  }\n\n  struct Invariant {\n    Invariant& operator=(const Invariant&) = delete;\n    explicit Invariant(const basic_fbstring& s) noexcept : s_(s) {\n      assert(s_.isSane());\n    }\n    ~Invariant() noexcept { assert(s_.isSane()); }\n\n   private:\n    const basic_fbstring& s_;\n  };\n\n public:\n  // types\n  typedef T traits_type;\n  typedef typename traits_type::char_type value_type;\n  typedef A allocator_type;\n  typedef typename std::allocator_traits<A>::size_type size_type;\n  typedef typename std::allocator_traits<A>::difference_type difference_type;\n\n  typedef typename std::allocator_traits<A>::value_type& reference;\n  typedef typename std::allocator_traits<A>::value_type const& const_reference;\n  typedef typename std::allocator_traits<A>::pointer pointer;\n  typedef typename std::allocator_traits<A>::const_pointer const_pointer;\n\n  typedef E* iterator;\n  typedef const E* const_iterator;\n  typedef std::reverse_iterator<iterator> reverse_iterator;\n  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;\n\n  static constexpr size_type npos = size_type(-1);\n  typedef std::true_type IsRelocatable;\n\n private:\n  using string_view_type = std::basic_string_view<value_type, traits_type>;\n\n  template <typename StringViewLike>\n  static inline constexpr bool is_string_view_like_v =\n      std::is_convertible_v<StringViewLike const&, string_view_type> &&\n      !std::is_convertible_v<StringViewLike const&, const_pointer>;\n\n  template <typename StringViewLike, typename Dummy>\n  using if_is_string_view_like_t =\n      std::enable_if_t<is_string_view_like_v<StringViewLike>, Dummy>;\n\n  static void procrustes(size_type& n, size_type nmax) {\n    if (n > nmax) {\n      n = nmax;\n    }\n  }\n\n  static size_type traitsLength(const value_type* s);\n\n  struct string_view_ctor {};\n  FOLLY_NOINLINE basic_fbstring(\n      string_view_type view, const A&, string_view_ctor)\n      : store_(view.data(), view.size()) {}\n\n public:\n  // C++11 21.4.2 construct/copy/destroy\n\n  // Note: while the following two constructors can be (and previously were)\n  // collapsed into one constructor written this way:\n  //\n  //   explicit basic_fbstring(const A& a = A()) noexcept { }\n  //\n  // This can cause Clang (at least version 3.7) to fail with the error:\n  //   \"chosen constructor is explicit in copy-initialization ...\n  //   in implicit initialization of field '(x)' with omitted initializer\"\n  //\n  // if used in a struct which is default-initialized.  Hence the split into\n  // these two separate constructors.\n\n  basic_fbstring() noexcept : basic_fbstring(A()) {}\n  /* implicit */ basic_fbstring(std::nullptr_t) = delete;\n\n  explicit basic_fbstring(const A&) noexcept {}\n\n  basic_fbstring(const basic_fbstring& str) : store_(str.store_) {}\n\n  // Move constructor\n  basic_fbstring(basic_fbstring&& goner) noexcept\n      : store_(std::move(goner.store_)) {}\n\n  // This is defined for compatibility with std::string\n  template <typename A2>\n  /* implicit */ basic_fbstring(const std::basic_string<E, T, A2>& str)\n      : store_(str.data(), str.size()) {}\n\n  basic_fbstring(\n      const basic_fbstring& str,\n      size_type pos,\n      size_type n = npos,\n      const A& /* a */ = A()) {\n    assign(str, pos, n);\n  }\n\n  FOLLY_NOINLINE\n  /* implicit */ basic_fbstring(const value_type* s, const A& /*a*/ = A())\n      : store_(s, traitsLength(s)) {}\n\n  FOLLY_NOINLINE\n  basic_fbstring(const value_type* s, size_type n, const A& /*a*/ = A())\n      : store_(s, n) {}\n\n  FOLLY_NOINLINE\n  basic_fbstring(size_type n, value_type c, const A& /*a*/ = A()) {\n    auto const pData = store_.expandNoinit(n);\n    fbstring_detail::podFill(pData, pData + n, c);\n  }\n\n  template <class InIt>\n  FOLLY_NOINLINE basic_fbstring(\n      InIt begin,\n      InIt end,\n      typename std::enable_if<\n          !std::is_same<InIt, value_type*>::value,\n          const A>::type& /*a*/\n      = A()) {\n    assign(begin, end);\n  }\n\n  // Specialization for const char*, const char*\n  FOLLY_NOINLINE\n  basic_fbstring(const value_type* b, const value_type* e, const A& /*a*/ = A())\n      : store_(b, size_type(e - b)) {}\n\n  // Nonstandard constructor\n  basic_fbstring(\n      value_type* s, size_type n, size_type c, AcquireMallocatedString a)\n      : store_(s, n, c, a) {}\n\n  // Construction from initialization list\n  FOLLY_NOINLINE\n  basic_fbstring(std::initializer_list<value_type> il) {\n    assign(il.begin(), il.end());\n  }\n\n  template <\n      typename StringViewLike,\n      if_is_string_view_like_t<StringViewLike, int> = 0>\n  explicit basic_fbstring(const StringViewLike& view, const A& a = A())\n      : basic_fbstring(string_view_type(view), a, string_view_ctor{}) {}\n\n  template <\n      typename StringViewLike,\n      if_is_string_view_like_t<StringViewLike, int> = 0>\n  basic_fbstring(\n      const StringViewLike& view, size_type pos, size_type n, const A& a = A())\n      : basic_fbstring(\n            string_view_type(view).substr(pos, n), a, string_view_ctor{}) {}\n\n  ~basic_fbstring() noexcept {}\n\n  basic_fbstring& operator=(const basic_fbstring& lhs);\n\n  // Move assignment\n  basic_fbstring& operator=(basic_fbstring&& goner) noexcept;\n\n  // Compatibility with std::string\n  template <typename A2>\n  basic_fbstring& operator=(const std::basic_string<E, T, A2>& rhs) {\n    return assign(rhs.data(), rhs.size());\n  }\n\n  // Compatibility with std::string\n  std::basic_string<E, T, A> toStdString() const {\n    return std::basic_string<E, T, A>(data(), size());\n  }\n\n  basic_fbstring& operator=(std::nullptr_t) = delete;\n\n  basic_fbstring& operator=(const value_type* s) { return assign(s); }\n\n  basic_fbstring& operator=(value_type c);\n\n  // This actually goes directly against the C++ spec, but the\n  // value_type overload is dangerous, so we're explicitly deleting\n  // any overloads of operator= that could implicitly convert to\n  // value_type.\n  // Note that we do need to explicitly specify the template types because\n  // otherwise MSVC 2017 will aggressively pre-resolve value_type to\n  // traits_type::char_type, which won't compare as equal when determining\n  // which overload the implementation is referring to.\n  template <typename TP>\n  typename std::enable_if<\n      std::is_convertible<\n          TP,\n          typename basic_fbstring<E, T, A, Storage>::value_type>::value &&\n          !std::is_same<\n              typename std::decay<TP>::type,\n              typename basic_fbstring<E, T, A, Storage>::value_type>::value,\n      basic_fbstring<E, T, A, Storage>&>::type\n  operator=(TP c) = delete;\n\n  basic_fbstring& operator=(std::initializer_list<value_type> il) {\n    return assign(il.begin(), il.end());\n  }\n\n  operator string_view_type() const noexcept { return {data(), size()}; }\n\n  // C++11 21.4.3 iterators:\n  iterator begin() { return store_.mutableData(); }\n\n  const_iterator begin() const { return store_.data(); }\n\n  const_iterator cbegin() const { return begin(); }\n\n  iterator end() { return store_.mutableData() + store_.size(); }\n\n  const_iterator end() const { return store_.data() + store_.size(); }\n\n  const_iterator cend() const { return end(); }\n\n  reverse_iterator rbegin() { return reverse_iterator(end()); }\n\n  const_reverse_iterator rbegin() const {\n    return const_reverse_iterator(end());\n  }\n\n  const_reverse_iterator crbegin() const { return rbegin(); }\n\n  reverse_iterator rend() { return reverse_iterator(begin()); }\n\n  const_reverse_iterator rend() const {\n    return const_reverse_iterator(begin());\n  }\n\n  const_reverse_iterator crend() const { return rend(); }\n\n  // Added by C++11\n  // C++11 21.4.5, element access:\n  const value_type& front() const { return *begin(); }\n  const value_type& back() const {\n    assert(!empty());\n    // Should be begin()[size() - 1], but that branches twice\n    return *(end() - 1);\n  }\n  value_type& front() { return *begin(); }\n  value_type& back() {\n    assert(!empty());\n    // Should be begin()[size() - 1], but that branches twice\n    return *(end() - 1);\n  }\n  void pop_back() {\n    assert(!empty());\n    store_.shrink(1);\n  }\n\n  // C++11 21.4.4 capacity:\n  size_type size() const { return store_.size(); }\n\n  size_type length() const { return size(); }\n\n  size_type max_size() const { return std::numeric_limits<size_type>::max(); }\n\n  void resize(size_type n, value_type c = value_type());\n\n  size_type capacity() const { return store_.capacity(); }\n\n  // Returns the reference count for this string's underlying data.\n  // Returns 1 for Small (SSO) or Medium strings which are not reference\n  // counted. For Large strings, returns the actual reference count.\n  // Useful for memory accounting with fair-share division.\n  size_t use_count() const { return store_.useCount(); }\n\n  // Returns true if the string uses RefCounted (Large) storage mode.\n  // Large strings have a RefCounted header (sizeof(size_t) bytes) before\n  // the data, which must be accounted for in memory calculations.\n  bool is_counted() const { return store_.isCounted(); }\n\n  void reserve(size_type res_arg = 0) {\n    enforce<std::length_error>(res_arg <= max_size(), \"\");\n    store_.reserve(res_arg);\n  }\n\n  void shrink_to_fit() {\n    // Shrink only if slack memory is sufficiently large\n    if (capacity() < size() * 3 / 2) {\n      return;\n    }\n    basic_fbstring(cbegin(), cend()).swap(*this);\n  }\n\n  void clear() { resize(0); }\n\n  bool empty() const { return size() == 0; }\n\n  // C++11 21.4.5 element access:\n  const_reference operator[](size_type pos) const { return *(begin() + pos); }\n\n  reference operator[](size_type pos) { return *(begin() + pos); }\n\n  const_reference at(size_type n) const {\n    enforce<std::out_of_range>(n < size(), \"\");\n    return (*this)[n];\n  }\n\n  reference at(size_type n) {\n    enforce<std::out_of_range>(n < size(), \"\");\n    return (*this)[n];\n  }\n\n  // C++11 21.4.6 modifiers:\n  basic_fbstring& operator+=(const basic_fbstring& str) { return append(str); }\n\n  basic_fbstring& operator+=(const value_type* s) { return append(s); }\n\n  basic_fbstring& operator+=(const value_type c) {\n    push_back(c);\n    return *this;\n  }\n\n  basic_fbstring& operator+=(std::initializer_list<value_type> il) {\n    append(il);\n    return *this;\n  }\n\n  template <\n      typename StringViewLike,\n      if_is_string_view_like_t<StringViewLike, int> = 0>\n  basic_fbstring& operator+=(const StringViewLike& like) {\n    append(like);\n    return *this;\n  }\n\n  basic_fbstring& append(const basic_fbstring& str);\n\n  basic_fbstring& append(\n      const basic_fbstring& str, const size_type pos, size_type n);\n\n  basic_fbstring& append(const value_type* s, size_type n);\n\n  basic_fbstring& append(const value_type* s) {\n    return append(s, traitsLength(s));\n  }\n\n  basic_fbstring& append(size_type n, value_type c);\n\n  template <class InputIterator>\n  basic_fbstring& append(InputIterator first, InputIterator last) {\n    insert(end(), first, last);\n    return *this;\n  }\n\n  basic_fbstring& append(std::initializer_list<value_type> il) {\n    return append(il.begin(), il.end());\n  }\n\n  template <\n      typename StringViewLike,\n      if_is_string_view_like_t<StringViewLike, int> = 0>\n  basic_fbstring& append(const StringViewLike& like) {\n    string_view_type view = like;\n    return append(view.begin(), view.end());\n  }\n\n  void push_back(const value_type c) { // primitive\n    store_.push_back(c);\n  }\n\n  basic_fbstring& assign(const basic_fbstring& str) {\n    if (&str == this) {\n      return *this;\n    }\n    return assign(str.data(), str.size());\n  }\n\n  basic_fbstring& assign(basic_fbstring&& str) {\n    return *this = std::move(str);\n  }\n\n  basic_fbstring& assign(\n      const basic_fbstring& str, const size_type pos, size_type n);\n\n  basic_fbstring& assign(const value_type* s, const size_type n);\n\n  basic_fbstring& assign(const value_type* s) {\n    return assign(s, traitsLength(s));\n  }\n\n  basic_fbstring& assign(std::initializer_list<value_type> il) {\n    return assign(il.begin(), il.end());\n  }\n\n  template <class ItOrLength, class ItOrChar>\n  basic_fbstring& assign(ItOrLength first_or_n, ItOrChar last_or_c) {\n    return replace(begin(), end(), first_or_n, last_or_c);\n  }\n\n  basic_fbstring& insert(size_type pos1, const basic_fbstring& str) {\n    return insert(pos1, str.data(), str.size());\n  }\n\n  basic_fbstring& insert(\n      size_type pos1, const basic_fbstring& str, size_type pos2, size_type n) {\n    enforce<std::out_of_range>(pos2 <= str.length(), \"\");\n    procrustes(n, str.length() - pos2);\n    return insert(pos1, str.data() + pos2, n);\n  }\n\n  basic_fbstring& insert(size_type pos, const value_type* s, size_type n) {\n    enforce<std::out_of_range>(pos <= length(), \"\");\n    insert(begin() + pos, s, s + n);\n    return *this;\n  }\n\n  basic_fbstring& insert(size_type pos, const value_type* s) {\n    return insert(pos, s, traitsLength(s));\n  }\n\n  basic_fbstring& insert(size_type pos, size_type n, value_type c) {\n    enforce<std::out_of_range>(pos <= length(), \"\");\n    insert(begin() + pos, n, c);\n    return *this;\n  }\n\n  iterator insert(const_iterator p, const value_type c) {\n    const size_type pos = p - cbegin();\n    insert(p, 1, c);\n    return begin() + pos;\n  }\n\n private:\n  typedef std::basic_istream<value_type, traits_type> istream_type;\n  istream_type& getlineImpl(istream_type& is, value_type delim);\n\n public:\n  friend inline istream_type& getline(\n      istream_type& is, basic_fbstring& str, value_type delim) {\n    return str.getlineImpl(is, delim);\n  }\n\n  friend inline istream_type& getline(istream_type& is, basic_fbstring& str) {\n    return getline(is, str, '\\n');\n  }\n\n private:\n  iterator insertImplDiscr(\n      const_iterator i, size_type n, value_type c, std::true_type);\n\n  template <class InputIter>\n  iterator insertImplDiscr(\n      const_iterator i, InputIter b, InputIter e, std::false_type);\n\n  template <class FwdIterator>\n  iterator insertImpl(\n      const_iterator i,\n      FwdIterator s1,\n      FwdIterator s2,\n      std::forward_iterator_tag);\n\n  template <class InputIterator>\n  iterator insertImpl(\n      const_iterator i,\n      InputIterator b,\n      InputIterator e,\n      std::input_iterator_tag);\n\n public:\n  template <class ItOrLength, class ItOrChar>\n  iterator insert(const_iterator p, ItOrLength first_or_n, ItOrChar last_or_c) {\n    using Sel =\n        std::bool_constant<std::numeric_limits<ItOrLength>::is_specialized>;\n    return insertImplDiscr(p, first_or_n, last_or_c, Sel());\n  }\n\n  iterator insert(const_iterator p, std::initializer_list<value_type> il) {\n    return insert(p, il.begin(), il.end());\n  }\n\n  basic_fbstring& erase(size_type pos = 0, size_type n = npos) {\n    Invariant checker(*this);\n\n    enforce<std::out_of_range>(pos <= length(), \"\");\n    procrustes(n, length() - pos);\n    std::copy(begin() + pos + n, end(), begin() + pos);\n    resize(length() - n);\n    return *this;\n  }\n\n  iterator erase(iterator position) {\n    const size_type pos(position - begin());\n    enforce<std::out_of_range>(pos <= size(), \"\");\n    erase(pos, 1);\n    return begin() + pos;\n  }\n\n  iterator erase(iterator first, iterator last) {\n    const size_type pos(first - begin());\n    erase(pos, last - first);\n    return begin() + pos;\n  }\n\n  // Replaces at most n1 chars of *this, starting with pos1 with the\n  // content of str\n  basic_fbstring& replace(\n      size_type pos1, size_type n1, const basic_fbstring& str) {\n    return replace(pos1, n1, str.data(), str.size());\n  }\n\n  // Replaces at most n1 chars of *this, starting with pos1,\n  // with at most n2 chars of str starting with pos2\n  basic_fbstring& replace(\n      size_type pos1,\n      size_type n1,\n      const basic_fbstring& str,\n      size_type pos2,\n      size_type n2) {\n    enforce<std::out_of_range>(pos2 <= str.length(), \"\");\n    return replace(\n        pos1, n1, str.data() + pos2, std::min(n2, str.size() - pos2));\n  }\n\n  // Replaces at most n1 chars of *this, starting with pos, with chars from s\n  basic_fbstring& replace(size_type pos, size_type n1, const value_type* s) {\n    return replace(pos, n1, s, traitsLength(s));\n  }\n\n  // Replaces at most n1 chars of *this, starting with pos, with n2\n  // occurrences of c\n  //\n  // consolidated with\n  //\n  // Replaces at most n1 chars of *this, starting with pos, with at\n  // most n2 chars of str.  str must have at least n2 chars.\n  template <class StrOrLength, class NumOrChar>\n  basic_fbstring& replace(\n      size_type pos, size_type n1, StrOrLength s_or_n2, NumOrChar n_or_c) {\n    Invariant checker(*this);\n\n    enforce<std::out_of_range>(pos <= size(), \"\");\n    procrustes(n1, length() - pos);\n    const iterator b = begin() + pos;\n    return replace(b, b + n1, s_or_n2, n_or_c);\n  }\n\n  basic_fbstring& replace(iterator i1, iterator i2, const basic_fbstring& str) {\n    return replace(i1, i2, str.data(), str.length());\n  }\n\n  basic_fbstring& replace(iterator i1, iterator i2, const value_type* s) {\n    return replace(i1, i2, s, traitsLength(s));\n  }\n\n private:\n  basic_fbstring& replaceImplDiscr(\n      iterator i1,\n      iterator i2,\n      const value_type* s,\n      size_type n,\n      std::integral_constant<int, 2>);\n\n  basic_fbstring& replaceImplDiscr(\n      iterator i1,\n      iterator i2,\n      size_type n2,\n      value_type c,\n      std::integral_constant<int, 1>);\n\n  template <class InputIter>\n  basic_fbstring& replaceImplDiscr(\n      iterator i1,\n      iterator i2,\n      InputIter b,\n      InputIter e,\n      std::integral_constant<int, 0>);\n\n private:\n  template <class FwdIterator>\n  bool replaceAliased(\n      iterator /* i1 */,\n      iterator /* i2 */,\n      FwdIterator /* s1 */,\n      FwdIterator /* s2 */,\n      std::false_type) {\n    return false;\n  }\n\n  template <class FwdIterator>\n  bool replaceAliased(\n      iterator i1, iterator i2, FwdIterator s1, FwdIterator s2, std::true_type);\n\n  template <class FwdIterator>\n  void replaceImpl(\n      iterator i1,\n      iterator i2,\n      FwdIterator s1,\n      FwdIterator s2,\n      std::forward_iterator_tag);\n\n  template <class InputIterator>\n  void replaceImpl(\n      iterator i1,\n      iterator i2,\n      InputIterator b,\n      InputIterator e,\n      std::input_iterator_tag);\n\n public:\n  template <class T1, class T2>\n  basic_fbstring& replace(\n      iterator i1, iterator i2, T1 first_or_n_or_s, T2 last_or_c_or_n) {\n    constexpr bool num1 = std::numeric_limits<T1>::is_specialized,\n                   num2 = std::numeric_limits<T2>::is_specialized;\n    using Sel =\n        std::integral_constant<int, num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>;\n    return replaceImplDiscr(i1, i2, first_or_n_or_s, last_or_c_or_n, Sel());\n  }\n\n  size_type copy(value_type* s, size_type n, size_type pos = 0) const {\n    enforce<std::out_of_range>(pos <= size(), \"\");\n    procrustes(n, size() - pos);\n\n    if (n != 0) {\n      fbstring_detail::podCopy(data() + pos, data() + pos + n, s);\n    }\n    return n;\n  }\n\n  void swap(basic_fbstring& rhs) { store_.swap(rhs.store_); }\n\n  const value_type* c_str() const { return store_.c_str(); }\n\n  const value_type* data() const { return c_str(); }\n\n  value_type* data() { return store_.data(); }\n\n  allocator_type get_allocator() const { return allocator_type(); }\n\n  size_type find(const basic_fbstring& str, size_type pos = 0) const {\n    return find(str.data(), pos, str.length());\n  }\n\n  size_type find(\n      const value_type* needle, size_type pos, size_type nsize) const;\n\n  size_type find(const value_type* s, size_type pos = 0) const {\n    return find(s, pos, traitsLength(s));\n  }\n\n  size_type find(value_type c, size_type pos = 0) const {\n    return find(&c, pos, 1);\n  }\n\n  size_type rfind(const basic_fbstring& str, size_type pos = npos) const {\n    return rfind(str.data(), pos, str.length());\n  }\n\n  size_type rfind(const value_type* s, size_type pos, size_type n) const;\n\n  size_type rfind(const value_type* s, size_type pos = npos) const {\n    return rfind(s, pos, traitsLength(s));\n  }\n\n  size_type rfind(value_type c, size_type pos = npos) const {\n    return rfind(&c, pos, 1);\n  }\n\n  size_type find_first_of(const basic_fbstring& str, size_type pos = 0) const {\n    return find_first_of(str.data(), pos, str.length());\n  }\n\n  size_type find_first_of(\n      const value_type* s, size_type pos, size_type n) const;\n\n  size_type find_first_of(const value_type* s, size_type pos = 0) const {\n    return find_first_of(s, pos, traitsLength(s));\n  }\n\n  size_type find_first_of(value_type c, size_type pos = 0) const {\n    return find_first_of(&c, pos, 1);\n  }\n\n  size_type find_last_of(\n      const basic_fbstring& str, size_type pos = npos) const {\n    return find_last_of(str.data(), pos, str.length());\n  }\n\n  size_type find_last_of(const value_type* s, size_type pos, size_type n) const;\n\n  size_type find_last_of(const value_type* s, size_type pos = npos) const {\n    return find_last_of(s, pos, traitsLength(s));\n  }\n\n  size_type find_last_of(value_type c, size_type pos = npos) const {\n    return find_last_of(&c, pos, 1);\n  }\n\n  size_type find_first_not_of(\n      const basic_fbstring& str, size_type pos = 0) const {\n    return find_first_not_of(str.data(), pos, str.size());\n  }\n\n  size_type find_first_not_of(\n      const value_type* s, size_type pos, size_type n) const;\n\n  size_type find_first_not_of(const value_type* s, size_type pos = 0) const {\n    return find_first_not_of(s, pos, traitsLength(s));\n  }\n\n  size_type find_first_not_of(value_type c, size_type pos = 0) const {\n    return find_first_not_of(&c, pos, 1);\n  }\n\n  size_type find_last_not_of(\n      const basic_fbstring& str, size_type pos = npos) const {\n    return find_last_not_of(str.data(), pos, str.length());\n  }\n\n  size_type find_last_not_of(\n      const value_type* s, size_type pos, size_type n) const;\n\n  size_type find_last_not_of(const value_type* s, size_type pos = npos) const {\n    return find_last_not_of(s, pos, traitsLength(s));\n  }\n\n  size_type find_last_not_of(value_type c, size_type pos = npos) const {\n    return find_last_not_of(&c, pos, 1);\n  }\n\n  basic_fbstring substr(size_type pos = 0, size_type n = npos) const& {\n    enforce<std::out_of_range>(pos <= size(), \"\");\n    return basic_fbstring(data() + pos, std::min(n, size() - pos));\n  }\n\n  basic_fbstring substr(size_type pos = 0, size_type n = npos) && {\n    enforce<std::out_of_range>(pos <= size(), \"\");\n    erase(0, pos);\n    if (n < size()) {\n      resize(n);\n    }\n    return std::move(*this);\n  }\n\n  int compare(const basic_fbstring& str) const {\n    // FIX due to Goncalo N M de Carvalho July 18, 2005\n    return compare(0, size(), str);\n  }\n\n  int compare(size_type pos1, size_type n1, const basic_fbstring& str) const {\n    return compare(pos1, n1, str.data(), str.size());\n  }\n\n  int compare(size_type pos1, size_type n1, const value_type* s) const {\n    return compare(pos1, n1, s, traitsLength(s));\n  }\n\n  int compare(\n      size_type pos1, size_type n1, const value_type* s, size_type n2) const {\n    enforce<std::out_of_range>(pos1 <= size(), \"\");\n    procrustes(n1, size() - pos1);\n    // The line below fixed by Jean-Francois Bastien, 04-23-2007. Thanks!\n    const int r = traits_type::compare(pos1 + data(), s, std::min(n1, n2));\n    return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;\n  }\n\n  int compare(\n      size_type pos1,\n      size_type n1,\n      const basic_fbstring& str,\n      size_type pos2,\n      size_type n2) const {\n    enforce<std::out_of_range>(pos2 <= str.size(), \"\");\n    return compare(\n        pos1, n1, str.data() + pos2, std::min(n2, str.size() - pos2));\n  }\n\n  // Code from Jean-Francois Bastien (03/26/2007)\n  int compare(const value_type* s) const {\n    // Could forward to compare(0, size(), s, traitsLength(s))\n    // but that does two extra checks\n    const size_type n1(size()), n2(traitsLength(s));\n    const int r = traits_type::compare(data(), s, std::min(n1, n2));\n    return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0;\n  }\n\n#if FOLLY_CPLUSPLUS >= 202002L\n  friend auto operator<=>(\n      const basic_fbstring& lhs, const basic_fbstring& rhs) {\n    return lhs.spaceship(rhs.data(), rhs.size());\n  }\n  friend auto operator<=>(const basic_fbstring& lhs, const char* rhs) {\n    return lhs.spaceship(rhs, traitsLength(rhs));\n  }\n  template <typename A2>\n  friend auto operator<=>(\n      const basic_fbstring& lhs, const std::basic_string<E, T, A2>& rhs) {\n    return lhs.spaceship(rhs.data(), rhs.size());\n  }\n#endif // FOLLY_CPLUSPLUS >= 202002L\n\n private:\n#if FOLLY_CPLUSPLUS >= 202002L\n  auto spaceship(const value_type* rhsData, size_type rhsSize) const {\n    auto c = compare(0, size(), rhsData, rhsSize);\n    if (c == 0) {\n      return std::strong_ordering::equal;\n    } else if (c < 0) {\n      return std::strong_ordering::less;\n    } else {\n      return std::strong_ordering::greater;\n    }\n  }\n#endif // FOLLY_CPLUSPLUS >= 202002L\n\n  // Data\n  Storage store_;\n};\n\ntemplate <typename E, class T, class A, class S>\nFOLLY_NOINLINE typename basic_fbstring<E, T, A, S>::size_type\nbasic_fbstring<E, T, A, S>::traitsLength(const value_type* s) {\n  return s\n      ? traits_type::length(s)\n      : (throw_exception<std::logic_error>(\n             \"basic_fbstring: null pointer initializer not valid\"),\n         0);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(\n    const basic_fbstring& lhs) {\n  Invariant checker(*this);\n\n  if (FOLLY_UNLIKELY(&lhs == this)) {\n    return *this;\n  }\n\n  return assign(lhs.data(), lhs.size());\n}\n\n// Move assignment\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(\n    basic_fbstring&& goner) noexcept {\n  if (FOLLY_UNLIKELY(&goner == this)) {\n    // Compatibility with std::basic_string<>,\n    // C++11 21.4.2 [string.cons] / 23 requires self-move-assignment support.\n    return *this;\n  }\n  // No need of this anymore\n  this->~basic_fbstring();\n  // Move the goner into this\n  new (&store_) S(std::move(goner.store_));\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::operator=(\n    value_type c) {\n  Invariant checker(*this);\n\n  if (empty()) {\n    store_.expandNoinit(1);\n  } else if (store_.isShared()) {\n    basic_fbstring(1, c).swap(*this);\n    return *this;\n  } else {\n    store_.shrink(size() - 1);\n  }\n  front() = c;\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline void basic_fbstring<E, T, A, S>::resize(\n    const size_type n, const value_type c /*= value_type()*/) {\n  Invariant checker(*this);\n\n  auto size = this->size();\n  if (n <= size) {\n    store_.shrink(size - n);\n  } else {\n    auto const delta = n - size;\n    auto pData = store_.expandNoinit(delta);\n    fbstring_detail::podFill(pData, pData + delta, c);\n  }\n  assert(this->size() == n);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(\n    const basic_fbstring& str) {\n#ifndef NDEBUG\n  auto desiredSize = size() + str.size();\n#endif\n  append(str.data(), str.size());\n  assert(size() == desiredSize);\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(\n    const basic_fbstring& str, const size_type pos, size_type n) {\n  const size_type sz = str.size();\n  enforce<std::out_of_range>(pos <= sz, \"\");\n  procrustes(n, sz - pos);\n  return append(str.data() + pos, n);\n}\n\ntemplate <typename E, class T, class A, class S>\nFOLLY_NOINLINE basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(\n    const value_type* s, size_type n) {\n  Invariant checker(*this);\n\n  if (FOLLY_UNLIKELY(!n)) {\n    // Unlikely but must be done\n    return *this;\n  }\n  auto const oldSize = size();\n  auto const oldData = data();\n  auto pData = store_.expandNoinit(n, /* expGrowth = */ true);\n\n  // Check for aliasing (rare). We could use \"<=\" here but in theory\n  // those do not work for pointers unless the pointers point to\n  // elements in the same array. For that reason we use\n  // std::less_equal, which is guaranteed to offer a total order\n  // over pointers. See discussion at http://goo.gl/Cy2ya for more\n  // info.\n  std::less_equal<const value_type*> le;\n  if (FOLLY_UNLIKELY(le(oldData, s) && !le(oldData + oldSize, s))) {\n    assert(le(s + n, oldData + oldSize));\n    // expandNoinit() could have moved the storage, restore the source.\n    s = data() + (s - oldData);\n    fbstring_detail::podMove(s, s + n, pData);\n  } else {\n    fbstring_detail::podCopy(s, s + n, pData);\n  }\n\n  assert(size() == oldSize + n);\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::append(\n    size_type n, value_type c) {\n  Invariant checker(*this);\n  auto pData = store_.expandNoinit(n, /* expGrowth = */ true);\n  fbstring_detail::podFill(pData, pData + n, c);\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::assign(\n    const basic_fbstring& str, const size_type pos, size_type n) {\n  const size_type sz = str.size();\n  enforce<std::out_of_range>(pos <= sz, \"\");\n  procrustes(n, sz - pos);\n  return assign(str.data() + pos, n);\n}\n\ntemplate <typename E, class T, class A, class S>\nFOLLY_NOINLINE basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::assign(\n    const value_type* s, const size_type n) {\n  Invariant checker(*this);\n\n  if (n == 0) {\n    resize(0);\n  } else if (size() >= n) {\n    // s can alias this, we need to use podMove.\n    fbstring_detail::podMove(s, s + n, store_.mutableData());\n    store_.shrink(size() - n);\n    assert(size() == n);\n  } else {\n    // If n is larger than size(), s cannot alias this string's\n    // storage.\n    resize(0);\n    // Do not use exponential growth here: assign() should be tight,\n    // to mirror the behavior of the equivalent constructor.\n    fbstring_detail::podCopy(s, s + n, store_.expandNoinit(n));\n  }\n\n  assert(size() == n);\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::istream_type&\nbasic_fbstring<E, T, A, S>::getlineImpl(istream_type& is, value_type delim) {\n  Invariant checker(*this);\n\n  clear();\n  size_t size = 0;\n  while (true) {\n    size_t avail = capacity() - size;\n    // fbstring has 1 byte extra capacity for the null terminator,\n    // and getline null-terminates the read string.\n    is.getline(store_.expandNoinit(avail), avail + 1, delim);\n    size += is.gcount();\n\n    if (is.bad() || is.eof() || !is.fail()) {\n      // Done by either failure, end of file, or normal read.\n      if (!is.bad() && !is.eof()) {\n        --size; // gcount() also accounts for the delimiter.\n      }\n      resize(size);\n      break;\n    }\n\n    assert(size == this->size());\n    assert(size == capacity());\n    // Start at minimum allocation 63 + terminator = 64.\n    reserve(std::max<size_t>(63, 3 * size / 2));\n    // Clear the error so we can continue reading.\n    is.clear();\n  }\n  return is;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::size_type\nbasic_fbstring<E, T, A, S>::find(\n    const value_type* needle,\n    const size_type pos,\n    const size_type nsize) const {\n  auto const size = this->size();\n  // nsize + pos can overflow (eg pos == npos), guard against that by checking\n  // that nsize + pos does not wrap around.\n  if (nsize + pos > size || nsize + pos < pos) {\n    return npos;\n  }\n\n  if (nsize == 0) {\n    return pos;\n  }\n  // Don't use std::search, use a Boyer-Moore-like trick by comparing\n  // the last characters first\n  auto const haystack = data();\n  auto const nsize_1 = nsize - 1;\n  auto const lastNeedle = needle[nsize_1];\n\n  // Boyer-Moore skip value for the last char in the needle. Zero is\n  // not a valid value; skip will be computed the first time it's\n  // needed.\n  size_type skip = 0;\n\n  const E* i = haystack + pos;\n  auto iEnd = haystack + size - nsize_1;\n\n  while (i < iEnd) {\n    // Boyer-Moore: match the last element in the needle\n    while (i[nsize_1] != lastNeedle) {\n      if (++i == iEnd) {\n        // not found\n        return npos;\n      }\n    }\n    // Here we know that the last char matches\n    // Continue in pedestrian mode\n    for (size_t j = 0;;) {\n      assert(j < nsize);\n      if (i[j] != needle[j]) {\n        // Not found, we can skip\n        // Compute the skip value lazily\n        if (skip == 0) {\n          skip = 1;\n          while (skip <= nsize_1 && needle[nsize_1 - skip] != lastNeedle) {\n            ++skip;\n          }\n        }\n        i += skip;\n        break;\n      }\n      // Check if done searching\n      if (++j == nsize) {\n        // Yay\n        return i - haystack;\n      }\n    }\n  }\n  return npos;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::iterator\nbasic_fbstring<E, T, A, S>::insertImplDiscr(\n    const_iterator i, size_type n, value_type c, std::true_type) {\n  Invariant checker(*this);\n\n  assert(i >= cbegin() && i <= cend());\n  const size_type pos = i - cbegin();\n\n  auto oldSize = size();\n  store_.expandNoinit(n, /* expGrowth = */ true);\n  auto b = begin();\n  fbstring_detail::podMove(b + pos, b + oldSize, b + pos + n);\n  fbstring_detail::podFill(b + pos, b + pos + n, c);\n\n  return b + pos;\n}\n\ntemplate <typename E, class T, class A, class S>\ntemplate <class InputIter>\ninline typename basic_fbstring<E, T, A, S>::iterator\nbasic_fbstring<E, T, A, S>::insertImplDiscr(\n    const_iterator i, InputIter b, InputIter e, std::false_type) {\n  return insertImpl(\n      i, b, e, typename std::iterator_traits<InputIter>::iterator_category());\n}\n\ntemplate <typename E, class T, class A, class S>\ntemplate <class FwdIterator>\ninline typename basic_fbstring<E, T, A, S>::iterator\nbasic_fbstring<E, T, A, S>::insertImpl(\n    const_iterator i,\n    FwdIterator s1,\n    FwdIterator s2,\n    std::forward_iterator_tag) {\n  Invariant checker(*this);\n\n  assert(i >= cbegin() && i <= cend());\n  const size_type pos = i - cbegin();\n  auto n = std::distance(s1, s2);\n  assert(n >= 0);\n\n  auto oldSize = size();\n  store_.expandNoinit(n, /* expGrowth = */ true);\n  auto b = begin();\n  fbstring_detail::podMove(b + pos, b + oldSize, b + pos + n);\n  std::copy(s1, s2, b + pos);\n\n  return b + pos;\n}\n\ntemplate <typename E, class T, class A, class S>\ntemplate <class InputIterator>\ninline typename basic_fbstring<E, T, A, S>::iterator\nbasic_fbstring<E, T, A, S>::insertImpl(\n    const_iterator i,\n    InputIterator b,\n    InputIterator e,\n    std::input_iterator_tag) {\n  const auto pos = i - cbegin();\n  basic_fbstring temp(cbegin(), i);\n  for (; b != e; ++b) {\n    temp.push_back(*b);\n  }\n  temp.append(i, cend());\n  swap(temp);\n  return begin() + pos;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr(\n    iterator i1,\n    iterator i2,\n    const value_type* s,\n    size_type n,\n    std::integral_constant<int, 2>) {\n  assert(i1 <= i2);\n  assert(begin() <= i1 && i1 <= end());\n  assert(begin() <= i2 && i2 <= end());\n  return replace(i1, i2, s, s + n);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr(\n    iterator i1,\n    iterator i2,\n    size_type n2,\n    value_type c,\n    std::integral_constant<int, 1>) {\n  const size_type n1 = i2 - i1;\n  if (n1 > n2) {\n    std::fill(i1, i1 + n2, c);\n    erase(i1 + n2, i2);\n  } else {\n    std::fill(i1, i2, c);\n    insert(i2, n2 - n1, c);\n  }\n  assert(isSane());\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ntemplate <class InputIter>\ninline basic_fbstring<E, T, A, S>& basic_fbstring<E, T, A, S>::replaceImplDiscr(\n    iterator i1,\n    iterator i2,\n    InputIter b,\n    InputIter e,\n    std::integral_constant<int, 0>) {\n  using Cat = typename std::iterator_traits<InputIter>::iterator_category;\n  replaceImpl(i1, i2, b, e, Cat());\n  return *this;\n}\n\ntemplate <typename E, class T, class A, class S>\ntemplate <class FwdIterator>\ninline bool basic_fbstring<E, T, A, S>::replaceAliased(\n    iterator i1, iterator i2, FwdIterator s1, FwdIterator s2, std::true_type) {\n  std::less_equal<const value_type*> le{};\n  const bool aliased = le(&*begin(), &*s1) && le(&*s1, &*end());\n  if (!aliased) {\n    return false;\n  }\n  // Aliased replace, copy to new string\n  basic_fbstring temp;\n  temp.reserve(size() - (i2 - i1) + std::distance(s1, s2));\n  temp.append(begin(), i1).append(s1, s2).append(i2, end());\n  swap(temp);\n  return true;\n}\n\ntemplate <typename E, class T, class A, class S>\ntemplate <class FwdIterator>\ninline void basic_fbstring<E, T, A, S>::replaceImpl(\n    iterator i1,\n    iterator i2,\n    FwdIterator s1,\n    FwdIterator s2,\n    std::forward_iterator_tag) {\n  Invariant checker(*this);\n\n  // Handle aliased replace\n  using Sel = std::bool_constant<\n      std::is_same<FwdIterator, iterator>::value ||\n      std::is_same<FwdIterator, const_iterator>::value>;\n  if (replaceAliased(i1, i2, s1, s2, Sel())) {\n    return;\n  }\n\n  auto const n1 = i2 - i1;\n  assert(n1 >= 0);\n  auto const n2 = std::distance(s1, s2);\n  assume(n2 >= 0);\n\n  if (n1 > n2) {\n    // shrinks\n    std::copy(s1, s2, i1);\n    erase(i1 + n2, i2);\n  } else {\n    // grows\n    s1 = fbstring_detail::copy_n(s1, n1, i1).first;\n    insert(i2, s1, s2);\n  }\n  assert(isSane());\n}\n\ntemplate <typename E, class T, class A, class S>\ntemplate <class InputIterator>\ninline void basic_fbstring<E, T, A, S>::replaceImpl(\n    iterator i1,\n    iterator i2,\n    InputIterator b,\n    InputIterator e,\n    std::input_iterator_tag) {\n  basic_fbstring temp(begin(), i1);\n  temp.append(b, e).append(i2, end());\n  swap(temp);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::size_type\nbasic_fbstring<E, T, A, S>::rfind(\n    const value_type* s, size_type pos, size_type n) const {\n  if (n > length()) {\n    return npos;\n  }\n  pos = std::min(pos, length() - n);\n  if (n == 0) {\n    return pos;\n  }\n\n  const_iterator i(begin() + pos);\n  for (;; --i) {\n    if (traits_type::eq(*i, *s) && traits_type::compare(&*i, s, n) == 0) {\n      return i - begin();\n    }\n    if (i == begin()) {\n      break;\n    }\n  }\n  return npos;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::size_type\nbasic_fbstring<E, T, A, S>::find_first_of(\n    const value_type* s, size_type pos, size_type n) const {\n  if (pos > length() || n == 0) {\n    return npos;\n  }\n  const_iterator i(begin() + pos), finish(end());\n  for (; i != finish; ++i) {\n    if (traits_type::find(s, n, *i) != nullptr) {\n      return i - begin();\n    }\n  }\n  return npos;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::size_type\nbasic_fbstring<E, T, A, S>::find_last_of(\n    const value_type* s, size_type pos, size_type n) const {\n  if (!empty() && n > 0) {\n    pos = std::min(pos, length() - 1);\n    const_iterator i(begin() + pos);\n    for (;; --i) {\n      if (traits_type::find(s, n, *i) != nullptr) {\n        return i - begin();\n      }\n      if (i == begin()) {\n        break;\n      }\n    }\n  }\n  return npos;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::size_type\nbasic_fbstring<E, T, A, S>::find_first_not_of(\n    const value_type* s, size_type pos, size_type n) const {\n  if (pos < length()) {\n    const_iterator i(begin() + pos), finish(end());\n    for (; i != finish; ++i) {\n      if (traits_type::find(s, n, *i) == nullptr) {\n        return i - begin();\n      }\n    }\n  }\n  return npos;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline typename basic_fbstring<E, T, A, S>::size_type\nbasic_fbstring<E, T, A, S>::find_last_not_of(\n    const value_type* s, size_type pos, size_type n) const {\n  if (!this->empty()) {\n    pos = std::min(pos, size() - 1);\n    const_iterator i(begin() + pos);\n    for (;; --i) {\n      if (traits_type::find(s, n, *i) == nullptr) {\n        return i - begin();\n      }\n      if (i == begin()) {\n        break;\n      }\n    }\n  }\n  return npos;\n}\n\n// non-member functions\n// C++11 21.4.8.1/1\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  basic_fbstring<E, T, A, S> result;\n  result.reserve(lhs.size() + rhs.size());\n  result.append(lhs).append(rhs);\n  return result;\n}\n\n// C++11 21.4.8.1/2\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    basic_fbstring<E, T, A, S>&& lhs, const basic_fbstring<E, T, A, S>& rhs) {\n  return std::move(lhs.append(rhs));\n}\n\n// C++11 21.4.8.1/3\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    const basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>&& rhs) {\n  if (rhs.capacity() >= lhs.size() + rhs.size()) {\n    // Good, at least we don't need to reallocate\n    return std::move(rhs.insert(0, lhs));\n  }\n  // Meh, no go. Forward to operator+(const&, const&).\n  auto const& rhsC = rhs;\n  return lhs + rhsC;\n}\n\n// C++11 21.4.8.1/4\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    basic_fbstring<E, T, A, S>&& lhs, basic_fbstring<E, T, A, S>&& rhs) {\n  return std::move(lhs.append(rhs));\n}\n\n// C++11 21.4.8.1/5\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    const E* lhs, const basic_fbstring<E, T, A, S>& rhs) {\n  //\n  basic_fbstring<E, T, A, S> result;\n  const auto len = basic_fbstring<E, T, A, S>::traits_type::length(lhs);\n  result.reserve(len + rhs.size());\n  result.append(lhs, len).append(rhs);\n  return result;\n}\n\n// C++11 21.4.8.1/6\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    const E* lhs, basic_fbstring<E, T, A, S>&& rhs) {\n  //\n  const auto len = basic_fbstring<E, T, A, S>::traits_type::length(lhs);\n  if (rhs.capacity() >= len + rhs.size()) {\n    // Good, at least we don't need to reallocate\n    rhs.insert(rhs.begin(), lhs, lhs + len);\n    return std::move(rhs);\n  }\n  // Meh, no go. Do it by hand since we have len already.\n  basic_fbstring<E, T, A, S> result;\n  result.reserve(len + rhs.size());\n  result.append(lhs, len).append(rhs);\n  return result;\n}\n\n// C++11 21.4.8.1/7\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    E lhs, const basic_fbstring<E, T, A, S>& rhs) {\n  basic_fbstring<E, T, A, S> result;\n  result.reserve(1 + rhs.size());\n  result.push_back(lhs);\n  result.append(rhs);\n  return result;\n}\n\n// C++11 21.4.8.1/8\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    E lhs, basic_fbstring<E, T, A, S>&& rhs) {\n  //\n  if (rhs.capacity() > rhs.size()) {\n    // Good, at least we don't need to reallocate\n    rhs.insert(rhs.begin(), lhs);\n    return std::move(rhs);\n  }\n  // Meh, no go. Forward to operator+(E, const&).\n  auto const& rhsC = rhs;\n  return lhs + rhsC;\n}\n\n// C++11 21.4.8.1/9\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    const basic_fbstring<E, T, A, S>& lhs, const E* rhs) {\n  typedef typename basic_fbstring<E, T, A, S>::size_type size_type;\n  typedef typename basic_fbstring<E, T, A, S>::traits_type traits_type;\n\n  basic_fbstring<E, T, A, S> result;\n  const size_type len = traits_type::length(rhs);\n  result.reserve(lhs.size() + len);\n  result.append(lhs).append(rhs, len);\n  return result;\n}\n\n// C++11 21.4.8.1/10\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    basic_fbstring<E, T, A, S>&& lhs, const E* rhs) {\n  //\n  return std::move(lhs += rhs);\n}\n\n// C++11 21.4.8.1/11\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    const basic_fbstring<E, T, A, S>& lhs, E rhs) {\n  basic_fbstring<E, T, A, S> result;\n  result.reserve(lhs.size() + 1);\n  result.append(lhs);\n  result.push_back(rhs);\n  return result;\n}\n\n// C++11 21.4.8.1/12\ntemplate <typename E, class T, class A, class S>\ninline basic_fbstring<E, T, A, S> operator+(\n    basic_fbstring<E, T, A, S>&& lhs, E rhs) {\n  //\n  return std::move(lhs += rhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator==(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator==(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator==(\n    const typename basic_fbstring<E, T, A, S>::value_type* lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return rhs == lhs;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator==(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator==(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const typename basic_fbstring<E, T, A, S>::value_type* rhs) {\n  return lhs.compare(rhs) == 0;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator!=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator!=(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator!=(\n    const typename basic_fbstring<E, T, A, S>::value_type* lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator!=(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator!=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const typename basic_fbstring<E, T, A, S>::value_type* rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return lhs.compare(rhs) < 0;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const typename basic_fbstring<E, T, A, S>::value_type* rhs) {\n  return lhs.compare(rhs) < 0;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<(\n    const typename basic_fbstring<E, T, A, S>::value_type* lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return rhs.compare(lhs) > 0;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return rhs < lhs;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const typename basic_fbstring<E, T, A, S>::value_type* rhs) {\n  return rhs < lhs;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>(\n    const typename basic_fbstring<E, T, A, S>::value_type* lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return rhs < lhs;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(rhs < lhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<=(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const typename basic_fbstring<E, T, A, S>::value_type* rhs) {\n  return !(rhs < lhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<=(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<=(\n    const typename basic_fbstring<E, T, A, S>::value_type* lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(rhs < lhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(lhs < rhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>=(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const typename basic_fbstring<E, T, A, S>::value_type* rhs) {\n  return !(lhs < rhs);\n}\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>=(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator>=(\n    const typename basic_fbstring<E, T, A, S>::value_type* lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(lhs < rhs);\n}\n\n#if FOLLY_CPLUSPLUS >= 202002\ntemplate <typename E, class T, class A, class S>\ninline bool operator<=>(std::nullptr_t, const basic_fbstring<E, T, A, S>&) =\n    delete;\n\ntemplate <typename E, class T, class A, class S>\ninline bool operator<=>(const basic_fbstring<E, T, A, S>&, std::nullptr_t) =\n    delete;\n#endif\n\n// C++11 21.4.8.8\ntemplate <typename E, class T, class A, class S>\nvoid swap(basic_fbstring<E, T, A, S>& lhs, basic_fbstring<E, T, A, S>& rhs) {\n  lhs.swap(rhs);\n}\n\n// TODO: make this faster.\ntemplate <typename E, class T, class A, class S>\ninline std::basic_istream<\n    typename basic_fbstring<E, T, A, S>::value_type,\n    typename basic_fbstring<E, T, A, S>::traits_type>&\noperator>>(\n    std::basic_istream<\n        typename basic_fbstring<E, T, A, S>::value_type,\n        typename basic_fbstring<E, T, A, S>::traits_type>& is,\n    basic_fbstring<E, T, A, S>& str) {\n  typedef std::basic_istream<\n      typename basic_fbstring<E, T, A, S>::value_type,\n      typename basic_fbstring<E, T, A, S>::traits_type>\n      _istream_type;\n  typename _istream_type::sentry sentry(is);\n  size_t extracted = 0;\n  typename _istream_type::iostate err = _istream_type::goodbit;\n  if (sentry) {\n    auto n = is.width();\n    if (n <= 0) {\n      n = str.max_size();\n    }\n    str.erase();\n    for (auto got = is.rdbuf()->sgetc(); extracted != size_t(n); ++extracted) {\n      if (got == T::eof()) {\n        err |= _istream_type::eofbit;\n        is.width(0);\n        break;\n      }\n      if (isspace(got)) {\n        break;\n      }\n      str.push_back(got);\n      got = is.rdbuf()->snextc();\n    }\n  }\n  if (!extracted) {\n    err |= _istream_type::failbit;\n  }\n  if (err) {\n    is.setstate(err);\n  }\n  return is;\n}\n\ntemplate <typename E, class T, class A, class S>\ninline std::basic_ostream<\n    typename basic_fbstring<E, T, A, S>::value_type,\n    typename basic_fbstring<E, T, A, S>::traits_type>&\noperator<<(\n    std::basic_ostream<\n        typename basic_fbstring<E, T, A, S>::value_type,\n        typename basic_fbstring<E, T, A, S>::traits_type>& os,\n    const basic_fbstring<E, T, A, S>& str) {\n#ifdef _LIBCPP_VERSION\n  typedef std::basic_ostream<\n      typename basic_fbstring<E, T, A, S>::value_type,\n      typename basic_fbstring<E, T, A, S>::traits_type>\n      _ostream_type;\n  typename _ostream_type::sentry _s(os);\n  if (_s) {\n    typedef std::ostreambuf_iterator<\n        typename basic_fbstring<E, T, A, S>::value_type,\n        typename basic_fbstring<E, T, A, S>::traits_type>\n        _Ip;\n    size_t __len = str.size();\n    bool __left =\n        (os.flags() & _ostream_type::adjustfield) == _ostream_type::left;\n    if (__pad_and_output(\n            _Ip(os),\n            str.data(),\n            __left ? str.data() + __len : str.data(),\n            str.data() + __len,\n            os,\n            os.fill())\n            .failed()) {\n      os.setstate(_ostream_type::badbit | _ostream_type::failbit);\n    }\n  }\n#elif defined(_MSC_VER)\n  typedef decltype(os.precision()) streamsize;\n  // MSVC doesn't define __ostream_insert\n  os.write(str.data(), static_cast<streamsize>(str.size()));\n#else\n  std::__ostream_insert(os, str.data(), str.size());\n#endif\n  return os;\n}\n\n// basic_string compatibility routines\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator==(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const std::basic_string<E, T, A2>& rhs) {\n  return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) == 0;\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator==(\n    const std::basic_string<E, T, A2>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return rhs == lhs;\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator!=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const std::basic_string<E, T, A2>& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator!=(\n    const std::basic_string<E, T, A2>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator<(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const std::basic_string<E, T, A2>& rhs) {\n  return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) < 0;\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator>(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const std::basic_string<E, T, A2>& rhs) {\n  return lhs.compare(0, lhs.size(), rhs.data(), rhs.size()) > 0;\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator<(\n    const std::basic_string<E, T, A2>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return rhs > lhs;\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator>(\n    const std::basic_string<E, T, A2>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return rhs < lhs;\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator<=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const std::basic_string<E, T, A2>& rhs) {\n  return !(lhs > rhs);\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator>=(\n    const basic_fbstring<E, T, A, S>& lhs,\n    const std::basic_string<E, T, A2>& rhs) {\n  return !(lhs < rhs);\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator<=(\n    const std::basic_string<E, T, A2>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(lhs > rhs);\n}\n\ntemplate <typename E, class T, class A, class S, class A2>\ninline bool operator>=(\n    const std::basic_string<E, T, A2>& lhs,\n    const basic_fbstring<E, T, A, S>& rhs) {\n  return !(lhs < rhs);\n}\n\ntypedef basic_fbstring<char> fbstring;\n\n// fbstring is relocatable\ntemplate <class T, class R, class A, class S>\nFOLLY_ASSUME_RELOCATABLE(basic_fbstring<T, R, A, S>);\n\n// Compatibility function, to make sure toStdString(s) can be called\n// to convert a std::string or fbstring variable s into type std::string\n// with very little overhead if s was already std::string\ninline std::string toStdString(const folly::fbstring& s) {\n  return std::string(s.data(), s.size());\n}\n\ninline const std::string& toStdString(const std::string& s) {\n  return s;\n}\n\n// If called with a temporary, the compiler will select this overload instead\n// of the above, so we don't return a (lvalue) reference to a temporary.\ninline std::string&& toStdString(std::string&& s) {\n  return std::move(s);\n}\n\n} // namespace folly\n\n// Hash functions to make fbstring usable with e.g. unordered_map\n\n#define FOLLY_FBSTRING_HASH1(T)                                               \\\n  template <>                                                                 \\\n  struct hash<::folly::basic_fbstring<T>> {                                   \\\n    size_t operator()(const ::folly::basic_fbstring<T>& s) const {            \\\n      return ::folly::hash::fnv32_buf_BROKEN(s.data(), s.size() * sizeof(T)); \\\n    }                                                                         \\\n  };\n\n// The C++11 standard says that these four are defined for basic_string\n#define FOLLY_FBSTRING_HASH      \\\n  FOLLY_FBSTRING_HASH1(char)     \\\n  FOLLY_FBSTRING_HASH1(char16_t) \\\n  FOLLY_FBSTRING_HASH1(char32_t) \\\n  FOLLY_FBSTRING_HASH1(wchar_t)\n\nnamespace std {\n\nFOLLY_FBSTRING_HASH\n\n} // namespace std\n\n#undef FOLLY_FBSTRING_HASH\n#undef FOLLY_FBSTRING_HASH1\n\nFOLLY_POP_WARNING\n\n#undef FBSTRING_DISABLE_SSO\n\nnamespace folly {\ntemplate <class T>\nstruct IsSomeString;\n\ntemplate <>\nstruct IsSomeString<fbstring> : std::true_type {};\n} // namespace folly\n\ntemplate <>\nstruct fmt::formatter<folly::fbstring> : private formatter<fmt::string_view> {\n  using formatter<fmt::string_view>::parse;\n\n  template <typename Context>\n  typename Context::iterator format(\n      const folly::fbstring& s, Context& ctx) const {\n    return formatter<fmt::string_view>::format({s.data(), s.size()}, ctx);\n  }\n};\n"
  },
  {
    "path": "folly/FBVector.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/FBVector.h> // @shim\n"
  },
  {
    "path": "folly/File.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/File.h>\n\n#include <folly/Exception.h>\n#include <folly/FileUtil.h>\n#include <folly/ScopeGuard.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/FmtCompile.h>\n#include <folly/portability/SysFile.h>\n#include <folly/portability/Unistd.h>\n\n#include <system_error>\n\n#include <glog/logging.h>\n\nnamespace folly {\n\nFile::File(int fd, bool ownsFd) noexcept : fd_(fd), ownsFd_(ownsFd) {\n  CHECK_GE(fd, -1) << \"fd must be -1 or non-negative\";\n  CHECK(fd != -1 || !ownsFd) << \"cannot own -1\";\n}\n\nFile::File(const char* name, int flags, mode_t mode)\n    : fd_(fileops::open(name, flags, mode)), ownsFd_(false) {\n  if (fd_ == -1) {\n    throwSystemError(\n        fmt::format(\n            FOLLY_FMT_COMPILE(\"open(\\\"{}\\\", {:#o}, 0{:#o}) failed\"),\n            name,\n            flags,\n            mode));\n  }\n  ownsFd_ = true;\n}\n\nFile::File(const std::string& name, int flags, mode_t mode)\n    : File(name.c_str(), flags, mode) {}\n\nFile::File(StringPiece name, int flags, mode_t mode)\n    : File(name.str(), flags, mode) {}\n\nFile::File(File&& other) noexcept : fd_(other.fd_), ownsFd_(other.ownsFd_) {\n  other.release();\n}\n\nFile& File::operator=(File&& other) {\n  closeNoThrow();\n  swap(other);\n  return *this;\n}\n\nFile::~File() {\n  auto fd = fd_;\n  if (!closeNoThrow()) { // ignore most errors\n    DCHECK_NE(errno, EBADF)\n        << \"closing fd \" << fd << \", it may already \"\n        << \"have been closed. Another time, this might close the wrong FD.\";\n  }\n}\n\n/* static */ File File::temporary() {\n  // make a temp file with tmpfile(), dup the fd, then return it in a File.\n  FILE* tmpFile = tmpfile();\n  checkFopenError(tmpFile, \"tmpfile() failed\");\n  SCOPE_EXIT {\n    fclose(tmpFile);\n  };\n\n  // TODO(nga): consider setting close-on-exec for the resulting FD\n  int fd = ::dup(fileno(tmpFile));\n  checkUnixError(fd, \"dup() failed\");\n\n  return File(fd, true);\n}\n\nint File::release() noexcept {\n  int released = fd_;\n  fd_ = -1;\n  ownsFd_ = false;\n  return released;\n}\n\nvoid File::swap(File& other) noexcept {\n  using std::swap;\n  swap(fd_, other.fd_);\n  swap(ownsFd_, other.ownsFd_);\n}\n\nvoid swap(File& a, File& b) noexcept {\n  a.swap(b);\n}\n\nFile File::dup() const {\n  if (fd_ != -1) {\n    int fd = ::dup(fd_);\n    checkUnixError(fd, \"dup() failed\");\n\n    return File(fd, true);\n  }\n\n  return File();\n}\n\nFile File::dupCloseOnExec() const {\n  if (fd_ != -1) {\n    int fd;\n#ifdef _WIN32\n    fd = ::dup(fd_);\n#else\n    fd = ::fcntl(fd_, F_DUPFD_CLOEXEC, 0);\n#endif\n    checkUnixError(fd, \"dup() failed\");\n\n    return File(fd, true);\n  }\n\n  return File();\n}\n\nvoid File::close() {\n  if (!closeNoThrow()) {\n    throwSystemError(\"close() failed\");\n  }\n}\n\nbool File::closeNoThrow() {\n  int r = ownsFd_ ? fileops::close(fd_) : 0;\n  release();\n  return r == 0;\n}\n\nvoid File::lock() {\n  doLock(LOCK_EX);\n}\nbool File::try_lock() {\n  return doTryLock(LOCK_EX);\n}\nvoid File::lock_shared() {\n  doLock(LOCK_SH);\n}\nbool File::try_lock_shared() {\n  return doTryLock(LOCK_SH);\n}\n\nvoid File::doLock(int op) {\n  checkUnixError(flockNoInt(fd_, op), \"flock() failed (lock)\");\n}\n\nbool File::doTryLock(int op) {\n  int r = flockNoInt(fd_, op | LOCK_NB);\n  // flock returns EWOULDBLOCK if already locked\n  if (r == -1 && errno == EWOULDBLOCK) {\n    return false;\n  }\n  checkUnixError(r, \"flock() failed (try_lock)\");\n  return true;\n}\n\nvoid File::unlock() {\n  checkUnixError(flockNoInt(fd_, LOCK_UN), \"flock() failed (unlock)\");\n}\nvoid File::unlock_shared() {\n  unlock();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/File.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_file\n//\n\n#pragma once\n\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <string>\n#include <system_error>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/Expected.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\n/**\n * A File represents an open file.\n * @class folly::File\n * @refcode folly/docs/examples/folly/File.cpp\n */\nclass File {\n public:\n  /**\n   * Creates an empty File object, for late initialization.\n   */\n  constexpr File() noexcept : fd_(-1), ownsFd_(false) {}\n\n  /**\n   * Create a File object from an existing file descriptor.\n   *\n   * @param fd Existing file descriptor\n   * @param ownsFd Takes ownership of the file descriptor if ownsFd is true.\n   */\n  explicit File(int fd, bool ownsFd = false) noexcept;\n\n  /**\n   * Open and create a file object.  Throws on error.\n   * Owns the file descriptor implicitly.\n   */\n  explicit File(const char* name, int flags = O_RDONLY, mode_t mode = 0666);\n  explicit File(\n      const std::string& name, int flags = O_RDONLY, mode_t mode = 0666);\n  explicit File(StringPiece name, int flags = O_RDONLY, mode_t mode = 0666);\n\n  /**\n   * All the constructors that are not noexcept can throw std::system_error.\n   * This is a helper method to use folly::Expected to chain a file open event\n   * to something else you want to do with the open fd.\n   */\n  template <typename... Args>\n  static Expected<File, exception_wrapper> makeFile(Args&&... args) noexcept {\n    try {\n      return File(std::forward<Args>(args)...);\n    } catch (const std::system_error&) {\n      return makeUnexpected(exception_wrapper(current_exception()));\n    }\n  }\n\n  ~File();\n\n  /**\n   * Create and return a temporary, owned file (uses tmpfile()).\n   */\n  static File temporary();\n\n  /**\n   * Return the file descriptor, or -1 if the file was closed.\n   */\n  int fd() const { return fd_; }\n\n  /**\n   * Returns 'true' iff the file was successfully opened.\n   */\n  explicit operator bool() const { return fd_ != -1; }\n\n  /**\n   * Duplicate file descriptor and return File that owns it.\n   *\n   * Duplicated file descriptor does not have close-on-exec flag set,\n   * so it is leaked to child processes. Consider using \"dupCloseOnExec\".\n   */\n  File dup() const;\n\n  /**\n   * Duplicate file descriptor and return File that owns it.\n   *\n   * This functions creates a descriptor with close-on-exec flag set\n   * (where supported, otherwise it is equivalent to \"dup\").\n   */\n  File dupCloseOnExec() const;\n\n  /**\n   * If we own the file descriptor, close the file and throw on error.\n   * Otherwise, do nothing.\n   */\n  void close();\n\n  /**\n   * Closes the file (if owned).  Returns true on success, false (and sets\n   * errno) on error.\n   */\n  bool closeNoThrow();\n\n  /**\n   * Returns and releases the file descriptor; no longer owned by this File.\n   * Returns -1 if the File object didn't wrap a file.\n   */\n  int release() noexcept;\n\n  /**\n   * Swap this File with another.\n   */\n  void swap(File& other) noexcept;\n\n  // movable\n  File(File&&) noexcept;\n  File& operator=(File&&);\n\n  /**\n   * FLOCK (INTERPROCESS) LOCKS\n   *\n   * NOTE THAT THESE LOCKS ARE flock() LOCKS.  That is, they may only be used\n   * for inter-process synchronization -- an attempt to acquire a second lock\n   * on the same file descriptor from the same process may succeed.  Attempting\n   * to acquire a second lock on a different file descriptor for the same file\n   * should fail, but some systems might implement flock() using fcntl() locks,\n   * in which case it will succeed.\n   */\n  void lock();\n  bool try_lock();\n  void unlock();\n\n  void lock_shared();\n  bool try_lock_shared();\n  void unlock_shared();\n\n private:\n  void doLock(int op);\n  bool doTryLock(int op);\n\n  // unique\n  File(const File&) = delete;\n  File& operator=(const File&) = delete;\n\n  int fd_;\n  bool ownsFd_;\n};\n\n/**\n * Swaps the file descriptors and ownership\n */\nvoid swap(File& a, File& b) noexcept;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/FileUtil.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/FileUtil.h>\n\n#include <cerrno>\n#include <string>\n#include <system_error>\n\n#include <folly/detail/FileUtilDetail.h>\n#include <folly/detail/FileUtilVectorDetail.h>\n#include <folly/net/NetOps.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Stdlib.h>\n#include <folly/portability/SysFile.h>\n#include <folly/portability/SysStat.h>\n\nnamespace folly {\nnamespace {\niovec getIOVecFor(ByteRange);\n} // namespace\n\nusing namespace fileutil_detail;\n\nint openNoInt(const char* name, int flags, mode_t mode) {\n  // Android NDK bionic with FORTIFY has this definition:\n  // https://android.googlesource.com/platform/bionic/+/9349b9e51b/libc/include/bits/fortify/fcntl.h\n  // ```\n  // __BIONIC_ERROR_FUNCTION_VISIBILITY\n  // int open(const char* pathname, int flags, mode_t modes, ...) __overloadable\n  //         __errorattr(__open_too_many_args_error);\n  // ```\n  // This is originally to prevent open() with incorrect parameters.\n  //\n  // However, combined with folly wrapNotInt, template deduction will fail.\n  // In this case, we create a custom lambda to bypass the error.\n  // The solution is referenced from\n  // https://github.com/llvm/llvm-project/commit/0a0e411204a2baa520fd73a8d69b664f98b428ba\n  //\n  auto openWrapper = [&] { return fileops::open(name, flags, mode); };\n  return wrapNoInt(openWrapper);\n}\n\nstatic int filterCloseReturn(int r) {\n  // Ignore EINTR.  On Linux, close() may only return EINTR after the file\n  // descriptor has been closed, so you must not retry close() on EINTR --\n  // in the best case, you'll get EBADF, and in the worst case, you'll end up\n  // closing a different file (one opened from another thread).\n  //\n  // Interestingly enough, the Single Unix Specification says that the state\n  // of the file descriptor is unspecified if close returns EINTR.  In that\n  // case, the safe thing to do is also not to retry close() -- leaking a file\n  // descriptor is definitely better than closing the wrong file.\n  if (r == -1 && errno == EINTR) {\n    return 0;\n  }\n  return r;\n}\n\nint closeNoInt(int fd) {\n  return filterCloseReturn(fileops::close(fd));\n}\n\nint closeNoInt(NetworkSocket fd) {\n  return filterCloseReturn(netops::close(fd));\n}\n\nint fsyncNoInt(int fd) {\n  return wrapNoInt(fsync, fd);\n}\n\nint dupNoInt(int fd) {\n  return wrapNoInt(dup, fd);\n}\n\nint dup2NoInt(int oldFd, int newFd) {\n  return wrapNoInt(dup2, oldFd, newFd);\n}\n\nint fdatasyncNoInt(int fd) {\n#if defined(__APPLE__)\n  return wrapNoInt(fcntl, fd, F_FULLFSYNC);\n#elif defined(__FreeBSD__) || defined(_MSC_VER)\n  return wrapNoInt(fsync, fd);\n#else\n  return wrapNoInt(fdatasync, fd);\n#endif\n}\n\nint ftruncateNoInt(int fd, off_t len) {\n  return wrapNoInt(ftruncate, fd, len);\n}\n\nint truncateNoInt(const char* path, off_t len) {\n  return wrapNoInt(truncate, path, len);\n}\n\nint flockNoInt(int fd, int operation) {\n  return wrapNoInt(flock, fd, operation);\n}\n\nint shutdownNoInt(NetworkSocket fd, int how) {\n  return wrapNoInt(netops::shutdown, fd, how);\n}\n\nssize_t readNoInt(int fd, void* buf, size_t count) {\n  return wrapNoInt(folly::fileops::read, fd, buf, count);\n}\n\nssize_t preadNoInt(int fd, void* buf, size_t count, off_t offset) {\n  return wrapNoInt(pread, fd, buf, count, offset);\n}\n\nssize_t readvNoInt(int fd, const iovec* iov, int count) {\n  return wrapNoInt(readv, fd, iov, count);\n}\n\nssize_t preadvNoInt(int fd, const iovec* iov, int count, off_t offset) {\n  return wrapNoInt(preadv, fd, iov, count, offset);\n}\n\nssize_t writeNoInt(int fd, const void* buf, size_t count) {\n  return wrapNoInt(folly::fileops::write, fd, buf, count);\n}\n\nssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset) {\n  return wrapNoInt(pwrite, fd, buf, count, offset);\n}\n\nssize_t writevNoInt(int fd, const iovec* iov, int count) {\n  return wrapNoInt(writev, fd, iov, count);\n}\n\nssize_t pwritevNoInt(int fd, const iovec* iov, int count, off_t offset) {\n  return wrapNoInt(pwritev, fd, iov, count, offset);\n}\n\nssize_t readFull(int fd, void* buf, size_t count) {\n  return wrapFull(folly::fileops::read, fd, buf, count);\n}\n\nssize_t preadFull(int fd, void* buf, size_t count, off_t offset) {\n  return wrapFull(pread, fd, buf, count, offset);\n}\n\nssize_t writeFull(int fd, const void* buf, size_t count) {\n  return wrapFull(folly::fileops::write, fd, const_cast<void*>(buf), count);\n}\n\nssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset) {\n  return wrapFull(pwrite, fd, const_cast<void*>(buf), count, offset);\n}\n\n#ifndef _WIN32\nssize_t readvFull(int fd, iovec* iov, int count) {\n  return wrapvFull(readv, fd, iov, count);\n}\n\nssize_t preadvFull(int fd, iovec* iov, int count, off_t offset) {\n  return wrapvFull(preadv, fd, iov, count, offset);\n}\n\nssize_t writevFull(int fd, iovec* iov, int count) {\n  return wrapvFull(writev, fd, iov, count);\n}\n\nssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset) {\n  return wrapvFull(pwritev, fd, iov, count, offset);\n}\n#else // _WIN32\n\n// On Windows, the *vFull() functions wrap the simple read/pread/write/pwrite\n// functions.  While folly/portability/SysUio.cpp does define readv() and\n// writev() implementations for Windows, these attempt to lock the file to\n// provide atomicity.  The *vFull() functions do not provide any atomicity\n// guarantees, so we can avoid the locking logic.\n\nssize_t readvFull(int fd, iovec* iov, int count) {\n  return wrapvFull(folly::fileops::read, fd, iov, count);\n}\n\nssize_t preadvFull(int fd, iovec* iov, int count, off_t offset) {\n  return wrapvFull(pread, fd, iov, count, offset);\n}\n\nssize_t writevFull(int fd, iovec* iov, int count) {\n  return wrapvFull(folly::fileops::write, fd, iov, count);\n}\n\nssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset) {\n  return wrapvFull(pwrite, fd, iov, count, offset);\n}\n#endif // _WIN32\n\nWriteFileAtomicOptions& WriteFileAtomicOptions::setPermissions(\n    mode_t _permissions) {\n  permissions = _permissions;\n  return *this;\n}\n\nWriteFileAtomicOptions& WriteFileAtomicOptions::setSyncType(\n    SyncType _syncType) {\n  syncType = _syncType;\n  return *this;\n}\n\nWriteFileAtomicOptions& WriteFileAtomicOptions::setTemporaryDirectory(\n    std::string _temporaryDirectory) {\n  temporaryDirectory = std::move(_temporaryDirectory);\n  return *this;\n}\n\nnamespace {\nvoid throwIfWriteFileAtomicFailed(\n    StringPiece function, StringPiece filename, std::int64_t rc) {\n  if (rc != 0) {\n    auto msg =\n        std::string{function} + \"() failed to update \" + std::string{filename};\n    throw std::system_error(rc, std::generic_category(), msg);\n  }\n}\n\n// We write the data to a temporary file name first, then atomically rename\n// it into place.\n//\n// If SyncType::WITH_SYNC is used, this ensures that the file contents will\n// always be valid, even if we crash or are killed partway through writing out\n// data.\nint writeFileAtomicNoThrowImpl(\n    StringPiece filename,\n    iovec* iov,\n    int count,\n    const WriteFileAtomicOptions& options) {\n  // create a null-terminated version of the filename\n  auto filePathString = std::string{filename};\n  auto temporaryFilePathString = fileutil_detail::getTemporaryFilePathString(\n      filePathString, options.temporaryDirectory);\n\n  auto tmpFD = mkstemp(const_cast<char*>(temporaryFilePathString.data()));\n  if (tmpFD == -1) {\n    return errno;\n  }\n  bool success = false;\n  SCOPE_EXIT {\n    if (tmpFD != -1) {\n      fileops::close(tmpFD);\n    }\n    if (!success) {\n      unlink(temporaryFilePathString.c_str());\n    }\n  };\n\n  auto rc = writevFull(tmpFD, iov, count);\n  if (rc == -1) {\n    return errno;\n  }\n\n  rc = fchmod(tmpFD, options.permissions);\n  if (rc == -1) {\n    return errno;\n  }\n\n  // To guarantee atomicity across power failues on POSIX file systems,\n  // the temporary file must be explicitly sync'ed before the rename.\n  if (options.syncType == SyncType::WITH_SYNC) {\n    rc = fsyncNoInt(tmpFD);\n    if (rc == -1) {\n      return errno;\n    }\n  }\n\n  // Close the file before renaming to make sure all data has\n  // been successfully written.\n  rc = fileops::close(tmpFD);\n  tmpFD = -1;\n  if (rc == -1) {\n    return errno;\n  }\n\n  rc = rename(temporaryFilePathString.c_str(), filePathString.c_str());\n  if (rc == -1) {\n    return errno;\n  }\n  success = true;\n  return 0;\n}\n} // namespace\n\nint writeFileAtomicNoThrow(\n    StringPiece filename,\n    iovec* iov,\n    int count,\n    mode_t permissions,\n    SyncType syncType) {\n  return writeFileAtomicNoThrowImpl(\n      filename,\n      iov,\n      count,\n      WriteFileAtomicOptions{}\n          .setPermissions(permissions)\n          .setSyncType(syncType));\n}\n\nint writeFileAtomicNoThrow(\n    StringPiece filename,\n    StringPiece data,\n    const WriteFileAtomicOptions& options) {\n  auto iov = getIOVecFor(ByteRange{data});\n  return writeFileAtomicNoThrowImpl(filename, &iov, 1, options);\n}\n\nvoid writeFileAtomic(\n    StringPiece filename,\n    iovec* iov,\n    int count,\n    mode_t permissions,\n    SyncType syncType) {\n  auto rc = writeFileAtomicNoThrowImpl(\n      filename,\n      iov,\n      count,\n      WriteFileAtomicOptions{}\n          .setPermissions(permissions)\n          .setSyncType(syncType));\n  throwIfWriteFileAtomicFailed(__func__, filename, rc);\n}\n\nvoid writeFileAtomic(\n    StringPiece filename,\n    ByteRange data,\n    mode_t permissions,\n    SyncType syncType) {\n  auto iov = getIOVecFor(data);\n  writeFileAtomic(filename, &iov, 1, permissions, syncType);\n}\n\nvoid writeFileAtomic(\n    StringPiece filename,\n    StringPiece data,\n    mode_t permissions,\n    SyncType syncType) {\n  writeFileAtomic(filename, ByteRange(data), permissions, syncType);\n}\n\nvoid writeFileAtomic(\n    StringPiece filename,\n    StringPiece data,\n    const WriteFileAtomicOptions& options) {\n  auto iov = getIOVecFor(ByteRange{data});\n  auto rc = writeFileAtomicNoThrowImpl(filename, &iov, 1, options);\n\n  throwIfWriteFileAtomicFailed(__func__, filename, rc);\n}\n\nnamespace {\niovec getIOVecFor(ByteRange byteRange) {\n  iovec iov;\n  iov.iov_base = const_cast<unsigned char*>(byteRange.data());\n  iov.iov_len = byteRange.size();\n  return iov;\n}\n} // namespace\n} // namespace folly\n"
  },
  {
    "path": "folly/FileUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <cassert>\n#include <limits>\n\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/ScopeGuard.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/SysUio.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\n/**\n * Convenience wrappers around some commonly used system calls.  The *NoInt\n * wrappers retry on EINTR.  The *Full wrappers retry on EINTR and also loop\n * until all data is written.  Note that *Full wrappers weaken the thread\n * semantics of underlying system calls.\n */\nint openNoInt(const char* name, int flags, mode_t mode = 0666);\n// Two overloads, as we may be closing either a file or a socket.\nint closeNoInt(int fd);\nint closeNoInt(NetworkSocket fd);\nint dupNoInt(int fd);\nint dup2NoInt(int oldFd, int newFd);\nint fsyncNoInt(int fd);\nint fdatasyncNoInt(int fd);\nint ftruncateNoInt(int fd, off_t len);\nint truncateNoInt(const char* path, off_t len);\nint flockNoInt(int fd, int operation);\nint shutdownNoInt(NetworkSocket fd, int how);\n\nssize_t readNoInt(int fd, void* buf, size_t count);\nssize_t preadNoInt(int fd, void* buf, size_t count, off_t offset);\nssize_t readvNoInt(int fd, const iovec* iov, int count);\nssize_t preadvNoInt(int fd, const iovec* iov, int count, off_t offset);\n\nssize_t writeNoInt(int fd, const void* buf, size_t count);\nssize_t pwriteNoInt(int fd, const void* buf, size_t count, off_t offset);\nssize_t writevNoInt(int fd, const iovec* iov, int count);\nssize_t pwritevNoInt(int fd, const iovec* iov, int count, off_t offset);\n\n/**\n * Wrapper around read() (and pread()) that, in addition to retrying on\n * EINTR, will loop until all data is read.\n *\n * This wrapper is only useful for blocking file descriptors (for non-blocking\n * file descriptors, you have to be prepared to deal with incomplete reads\n * anyway), and only exists because POSIX allows read() to return an incomplete\n * read if interrupted by a signal (instead of returning -1 and setting errno\n * to EINTR).\n *\n * Note that this wrapper weakens the thread safety of read(): the file pointer\n * is shared between threads, but the system call is atomic.  If multiple\n * threads are reading from a file at the same time, you don't know where your\n * data came from in the file, but you do know that the returned bytes were\n * contiguous.  You can no longer make this assumption if using readFull().\n * You should probably use pread() when reading from the same file descriptor\n * from multiple threads simultaneously, anyway.\n *\n * Note that readvFull and preadvFull require iov to be non-const, unlike\n * readv and preadv.  The contents of iov after these functions return\n * is unspecified.\n */\n[[nodiscard]] ssize_t readFull(int fd, void* buf, size_t count);\n[[nodiscard]] ssize_t preadFull(int fd, void* buf, size_t count, off_t offset);\n[[nodiscard]] ssize_t readvFull(int fd, iovec* iov, int count);\n[[nodiscard]] ssize_t preadvFull(int fd, iovec* iov, int count, off_t offset);\n\n/**\n * Similar to readFull and preadFull above, wrappers around write() and\n * pwrite() that loop until all data is written.\n *\n * Generally, the write() / pwrite() system call may always write fewer bytes\n * than requested, just like read().  In certain cases (such as when writing to\n * a pipe), POSIX provides stronger guarantees, but not in the general case.\n * For example, Linux (even on a 64-bit platform) won't write more than 2GB in\n * one write() system call.\n *\n * Note that writevFull and pwritevFull require iov to be non-const, unlike\n * writev and pwritev.  The contents of iov after these functions return\n * is unspecified.\n *\n * These functions return -1 on error, or the total number of bytes written\n * (which is always the same as the number of requested bytes) on success.\n */\nssize_t writeFull(int fd, const void* buf, size_t count);\nssize_t pwriteFull(int fd, const void* buf, size_t count, off_t offset);\nssize_t writevFull(int fd, iovec* iov, int count);\nssize_t pwritevFull(int fd, iovec* iov, int count, off_t offset);\n\n/**\n * Read entire file (if num_bytes is defaulted) or no more than\n * num_bytes (otherwise) into container *out. The container is assumed\n * to be contiguous, with element size equal to 1, and offer size(),\n * reserve(), and random access (e.g. std::vector<char>, std::string,\n * fbstring).\n *\n * Returns: true on success or false on failure. In the latter case\n * errno will be set appropriately by the failing system primitive.\n */\ntemplate <class Container>\nbool readFile(\n    int fd,\n    Container& out,\n    size_t num_bytes = std::numeric_limits<size_t>::max()) {\n  static_assert(\n      sizeof(out[0]) == 1,\n      \"readFile: only containers with byte-sized elements accepted\");\n\n  size_t soFar = 0; // amount of bytes successfully read\n  SCOPE_EXIT {\n    assert(out.size() >= soFar); // resize better doesn't throw\n    out.resize(soFar);\n  };\n\n  // Obtain file size:\n  struct stat buf;\n  if (fstat(fd, &buf) == -1) {\n    return false;\n  }\n  // Some files (notably under /proc and /sys on Linux) lie about\n  // their size, so treat the size advertised by fstat under advise\n  // but don't rely on it. In particular, if the size is zero, we\n  // should attempt to read stuff. If not zero, we'll attempt to read\n  // one extra byte.\n  constexpr size_t initialAlloc = 1024 * 4;\n  out.resize(\n      std::min(\n          buf.st_size > 0 ? (size_t(buf.st_size) + 1) : initialAlloc,\n          num_bytes));\n\n  while (soFar < out.size()) {\n    const auto actual = readFull(fd, &out[soFar], out.size() - soFar);\n    if (actual == -1) {\n      return false;\n    }\n    soFar += actual;\n    if (soFar < out.size()) {\n      // File exhausted\n      break;\n    }\n    // Ew, allocate more memory. Use exponential growth to avoid\n    // quadratic behavior. Cap size to num_bytes.\n    out.resize(std::min(out.size() * 3 / 2, num_bytes));\n  }\n\n  return true;\n}\n\n/**\n * Same as above, but takes in a file name instead of fd\n */\ntemplate <class Container>\nbool readFile(\n    const char* file_name,\n    Container& out,\n    size_t num_bytes = std::numeric_limits<size_t>::max()) {\n  assert(file_name);\n\n  const auto fd = openNoInt(file_name, O_RDONLY | O_CLOEXEC);\n  if (fd == -1) {\n    return false;\n  }\n\n  SCOPE_EXIT {\n    // Ignore errors when closing the file\n    closeNoInt(fd);\n  };\n\n  return readFile(fd, out, num_bytes);\n}\n\n/**\n * Writes container to file. The container is assumed to be\n * contiguous, with element size equal to 1, and offering STL-like\n * methods empty(), size(), and indexed access\n * (e.g. std::vector<char>, std::string, fbstring, StringPiece).\n *\n * \"flags\" dictates the open flags to use. Default is to create file\n * if it doesn't exist and truncate it.\n *\n * Returns: true on success or false on failure. In the latter case\n * errno will be set appropriately by the failing system primitive.\n *\n * Note that this function may leave the file in a partially written state on\n * failure.  Use writeFileAtomic() if you want to ensure that the existing file\n * state will be unchanged on error.\n */\ntemplate <class Container>\nbool writeFile(\n    const Container& data,\n    const char* filename,\n    int flags = O_WRONLY | O_CREAT | O_TRUNC,\n    mode_t mode = 0666) {\n  static_assert(\n      sizeof(data[0]) == 1, \"writeFile works with element size equal to 1\");\n  int fd = fileops::open(filename, flags, mode);\n  if (fd == -1) {\n    return false;\n  }\n  bool ok = data.empty() ||\n      writeFull(fd, &data[0], data.size()) == static_cast<ssize_t>(data.size());\n  return closeNoInt(fd) == 0 && ok;\n}\n\n/* For atomic writes, do we sync to guarantee ordering or not? */\nenum class SyncType {\n  WITH_SYNC,\n  WITHOUT_SYNC,\n};\n\nclass WriteFileAtomicOptions {\n public:\n  WriteFileAtomicOptions() = default;\n\n  mode_t permissions{0644};\n  SyncType syncType{SyncType::WITHOUT_SYNC};\n  std::string temporaryDirectory;\n\n  // The mode bits used for the temporary file\n  WriteFileAtomicOptions& setPermissions(mode_t);\n\n  // The default implementation does not sync the data to storage before the\n  // rename.  Therefore, the write is *not* atomic in the event of a power\n  // failure or OS crash.  To guarantee atomicity in these cases, specify\n  // syncType = WITH_SYNC, which will incur a performance cost of waiting for\n  // the data to be persisted to storage.  Note that the return of the function\n  // does not guarantee the directory modifications have been written to disk; a\n  // further sync of the directory after the function returns is required to\n  // ensure the modification is durable.\n  WriteFileAtomicOptions& setSyncType(SyncType);\n\n  // The implementation creates a temporary file as an implementation detail\n  // within this directory.  The temporary filenames themselves are\n  // implementation defined.\n  WriteFileAtomicOptions& setTemporaryDirectory(std::string);\n};\n\n/*\n * writeFileAtomic() does not currently work on Windows.\n * Windows does not provide atomic file renames, which makes implementing this\n * tricky.  Windows does have a MoveFileTransactedA() API which could\n * potentially be used, but according to the Microsoft documentation this API is\n * discouraged and may be removed in a future version.\n *\n * In order to implement this properly on Windows we would probably need a pair\n * of functions: one for writing the file, and one for reading the contents,\n * where the two functions synchronize with each other.  We can probably only\n * provide atomic update behavior with cooperation from the reader.\n */\n#ifndef _WIN32\n\n/**\n * Write file contents \"atomically\".\n *\n * This writes the data to a temporary file in the destination directory, and\n * then renames it to the specified path.  This guarantees that the specified\n * file will be replaced the specified contents on success, or will not be\n * modified on failure.\n *\n * Note that on platforms that do not provide atomic filesystem rename\n * functionality (e.g., Windows) this behavior may not be truly atomic.\n *\n * The default implementation does not sync the data to storage before the\n * rename.  Therefore, the write is *not* atomic in the event of a power failure\n * or OS crash.  To guarantee atomicity in these cases, specify syncType =\n * WITH_SYNC, which will incur a performance cost of waiting for the data to be\n * persisted to storage.  Note that the return of the function does not\n * guarantee the directory modifications have been written to disk; a further\n * sync of the directory after the function returns is required to ensure the\n * modification is durable.\n */\nvoid writeFileAtomic(\n    StringPiece filePath,\n    iovec* iov,\n    int count,\n    mode_t permissions = 0644,\n    SyncType syncType = SyncType::WITHOUT_SYNC);\nvoid writeFileAtomic(\n    StringPiece filePath,\n    ByteRange data,\n    mode_t permissions = 0644,\n    SyncType syncType = SyncType::WITHOUT_SYNC);\nvoid writeFileAtomic(\n    StringPiece filePath,\n    StringPiece data,\n    mode_t permissions = 0644,\n    SyncType syncType = SyncType::WITHOUT_SYNC);\n\nvoid writeFileAtomic(\n    StringPiece filePath, StringPiece data, const WriteFileAtomicOptions&);\n\n/**\n * A version of writeFileAtomic() that returns an errno value instead of\n * throwing on error.\n *\n * Returns 0 on success or an errno value on error.\n */\nint writeFileAtomicNoThrow(\n    StringPiece filePath,\n    iovec* iov,\n    int count,\n    mode_t permissions = 0644,\n    SyncType syncType = SyncType::WITHOUT_SYNC);\n\nint writeFileAtomicNoThrow(\n    StringPiece filePath, StringPiece data, const WriteFileAtomicOptions&);\n\n#endif // !_WIN32\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Fingerprint.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Fingerprint.h>\n\n#include <folly/Portability.h>\n#include <folly/detail/FingerprintPolynomial.h>\n\n#include <utility>\n\nnamespace folly {\nnamespace detail {\n\nnamespace {\n\n// The polynomials below were generated by a separate program that requires the\n// NTL (Number Theory Library) from http://www.shoup.net/ntl/\n//\n// Briefly: randomly generate a polynomial of degree D, test for\n// irreducibility, repeat until you find an irreducible polynomial\n// (roughly 1/D of all polynomials of degree D are irreducible, so\n// this will succeed in D/2 tries on average; D is small (64..128) so\n// this simple method works well)\n//\n// DO NOT REPLACE THE POLYNOMIALS USED, EVER, as that would change the value\n// of every single fingerprint in existence.\ntemplate <size_t Deg>\nstruct FingerprintTablePoly;\ntemplate <>\nstruct FingerprintTablePoly<63> {\n  static constexpr uint64_t data[1] = {0xbf3736b51869e9b7};\n};\ntemplate <>\nstruct FingerprintTablePoly<95> {\n  static constexpr uint64_t data[2] = {0x51555cb0aa8d39c3, 0xb679ec3700000000};\n};\ntemplate <>\nstruct FingerprintTablePoly<127> {\n  static constexpr uint64_t data[2] = {0xc91bff9b8768b51b, 0x8c5d5853bd77b0d3};\n};\n\ntemplate <typename D, size_t S0, size_t... I0>\nconstexpr auto copy_table(D const (&table)[S0], std::index_sequence<I0...>) {\n  using array = std::array<D, S0>;\n  return array{{table[I0]...}};\n}\ntemplate <typename D, size_t S0>\nconstexpr auto copy_table(D const (&table)[S0]) {\n  return copy_table(table, std::make_index_sequence<S0>{});\n}\n\ntemplate <typename D, size_t S0, size_t S1, size_t... I0>\nconstexpr auto copy_table(\n    D const (&table)[S0][S1], std::index_sequence<I0...>) {\n  using array = std::array<std::array<D, S1>, S0>;\n  return array{{copy_table(table[I0])...}};\n}\ntemplate <typename D, size_t S0, size_t S1>\nconstexpr auto copy_table(D const (&table)[S0][S1]) {\n  return copy_table(table, std::make_index_sequence<S0>{});\n}\n\ntemplate <typename D, size_t S0, size_t S1, size_t S2, size_t... I0>\nconstexpr auto copy_table(\n    D const (&table)[S0][S1][S2], std::index_sequence<I0...>) {\n  using array = std::array<std::array<std::array<D, S2>, S1>, S0>;\n  return array{{copy_table(table[I0])...}};\n}\ntemplate <typename D, size_t S0, size_t S1, size_t S2>\nconstexpr auto copy_table(D const (&table)[S0][S1][S2]) {\n  return copy_table(table, std::make_index_sequence<S0>{});\n}\n\ntemplate <size_t Deg>\nconstexpr poly_table<Deg> make_poly_table() {\n  FingerprintPolynomial<Deg> poly(FingerprintTablePoly<Deg>::data);\n  uint64_t table[8][256][poly_size(Deg)] = {};\n  // table[i][q] is Q(X) * X^(k+8*i) mod P(X),\n  // where k is the number of bits in the fingerprint (and deg(P)) and\n  // Q(X) = q7*X^7 + q6*X^6 + ... + q1*X + q0 is a degree-7 polynomial\n  // whose coefficients are the bits of q.\n  for (uint16_t x = 0; x < 256; x++) {\n    FingerprintPolynomial<Deg> t;\n    t.setHigh8Bits(uint8_t(x));\n    for (auto& entry : table) {\n      t.mulXkmod(8, poly);\n      for (size_t j = 0; j < poly_size(Deg); ++j) {\n        entry[x][j] = t.get(j);\n      }\n    }\n  }\n  return copy_table(table);\n}\n\n// private global variables marked constexpr to enforce that make_poly_table is\n// really invoked at constexpr time, which would not otherwise be guaranteed\nFOLLY_STORAGE_CONSTEXPR auto const poly_table_63 = make_poly_table<63>();\nFOLLY_STORAGE_CONSTEXPR auto const poly_table_95 = make_poly_table<95>();\nFOLLY_STORAGE_CONSTEXPR auto const poly_table_127 = make_poly_table<127>();\n\n} // namespace\n\ntemplate <>\nconst uint64_t FingerprintTable<64>::poly[poly_size(64)] = {\n    FingerprintTablePoly<63>::data[0]};\ntemplate <>\nconst uint64_t FingerprintTable<96>::poly[poly_size(96)] = {\n    FingerprintTablePoly<95>::data[0], FingerprintTablePoly<95>::data[1]};\ntemplate <>\nconst uint64_t FingerprintTable<128>::poly[poly_size(128)] = {\n    FingerprintTablePoly<127>::data[0], FingerprintTablePoly<127>::data[1]};\n\ntemplate <>\nconst poly_table<64> FingerprintTable<64>::table = poly_table_63;\ntemplate <>\nconst poly_table<96> FingerprintTable<96>::table = poly_table_95;\ntemplate <>\nconst poly_table<128> FingerprintTable<128>::table = poly_table_127;\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/Fingerprint.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Compute 64-, 96-, and 128-bit Rabin fingerprints, as described in\n * Michael O. Rabin (1981)\n *   Fingerprinting by Random Polynomials\n *   Center for Research in Computing Technology, Harvard University\n *   Tech Report TR-CSE-03-01\n *\n * The implementation follows the optimization described in\n * Andrei Z. Broder (1993)\n *   Some applications of Rabin's fingerprinting method\n *\n * extended for fingerprints larger than 64 bits, and modified to use\n * 64-bit instead of 32-bit integers for computation.\n *\n * The precomputed tables are in Fingerprint.cpp.\n *\n * Benchmarked on 10/13/2009 on a 2.5GHz quad-core Xeon L5420,\n * - Fingerprint<64>::update64() takes about 12ns\n * - Fingerprint<96>::update64() takes about 30ns\n * - Fingerprint<128>::update128() takes about 30ns\n * (unsurprisingly, Fingerprint<96> and Fingerprint<128> take the\n * same amount of time, as they both use 128-bit operations; the least\n * significant 32 bits of Fingerprint<96> will always be 0)\n */\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n\n#include <folly/Range.h>\n\nnamespace folly {\n\nnamespace detail {\n\nconstexpr size_t poly_size(size_t bits) {\n  return 1 + (bits - 1) / 64;\n}\n\ntemplate <size_t Deg>\nusing poly_table =\n    std::array<std::array<std::array<uint64_t, poly_size(Deg)>, 256>, 8>;\n\ntemplate <int BITS>\nstruct FingerprintTable {\n  static const uint64_t poly[poly_size(BITS)];\n  static const poly_table<BITS> table;\n};\n\ntemplate <int BITS>\nconst uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)] = {};\ntemplate <int BITS>\nconst poly_table<BITS> FingerprintTable<BITS>::table = {};\n\n#ifndef _MSC_VER\n// MSVC as of 2017 can't handle these extern specialization declarations,\n// but they aren't needed for things to work right, so we just don't\n// declare them in the header for MSVC.\n\n#define FOLLY_DECLARE_FINGERPRINT_TABLES(BITS)                  \\\n  template <>                                                   \\\n  const uint64_t FingerprintTable<BITS>::poly[poly_size(BITS)]; \\\n  template <>                                                   \\\n  const poly_table<BITS> FingerprintTable<BITS>::table\n\nFOLLY_DECLARE_FINGERPRINT_TABLES(64);\nFOLLY_DECLARE_FINGERPRINT_TABLES(96);\nFOLLY_DECLARE_FINGERPRINT_TABLES(128);\n\n#undef FOLLY_DECLARE_FINGERPRINT_TABLES\n#endif\n\n} // namespace detail\n\n/**\n * Compute the Rabin fingerprint.\n *\n * TODO(tudorb): Extend this to allow removing values from the computed\n * fingerprint (so we can fingerprint a sliding window, as in the Rabin-Karp\n * string matching algorithm)\n *\n * update* methods return *this, so you can chain them together:\n * Fingerprint<96>().update8(x).update(str).update64(val).write(output);\n */\ntemplate <int BITS>\nclass Fingerprint {\n public:\n  Fingerprint() {\n    // Use a non-zero starting value. We'll use (1 << (BITS-1))\n    fp_[0] = 1ULL << 63;\n    for (int i = 1; i < size(); i++) {\n      fp_[i] = 0;\n    }\n  }\n\n  Fingerprint& update8(uint8_t v) {\n    uint8_t out = shlor8(v);\n    xortab(detail::FingerprintTable<BITS>::table[0][out]);\n    return *this;\n  }\n\n  // update32 and update64 are convenience functions to update the fingerprint\n  // with 4 and 8 bytes at a time.  They are faster than calling update8\n  // in a loop.  They process the bytes in big-endian order.\n  Fingerprint& update32(uint32_t v) {\n    uint32_t out = shlor32(v);\n    for (int i = 0; i < 4; i++) {\n      xortab(detail::FingerprintTable<BITS>::table[i][out & 0xff]);\n      out >>= 8;\n    }\n    return *this;\n  }\n\n  Fingerprint& update64(uint64_t v) {\n    uint64_t out = shlor64(v);\n    for (int i = 0; i < 8; i++) {\n      xortab(detail::FingerprintTable<BITS>::table[i][out & 0xff]);\n      out >>= 8;\n    }\n    return *this;\n  }\n\n  Fingerprint& update(StringPiece str) {\n    // TODO(tudorb): We could be smart and do update64 or update32 if aligned\n    for (auto c : str) {\n      update8(uint8_t(c));\n    }\n    return *this;\n  }\n\n  /**\n   * Return the number of uint64s needed to hold the fingerprint value.\n   */\n  constexpr static int size() { return detail::poly_size(BITS); }\n\n  /**\n   * Write the computed fingerprint to an array of size() uint64_t's.\n   * For Fingerprint<64>,  size()==1; we write 64 bits in out[0]\n   * For Fingerprint<96>,  size()==2; we write 64 bits in out[0] and\n   *                                  the most significant 32 bits of out[1]\n   * For Fingerprint<128>, size()==2; we write 64 bits in out[0] and\n   *                                  64 bits in out[1].\n   */\n  void write(uint64_t* out) const {\n    for (int i = 0; i < size(); i++) {\n      out[i] = fp_[i];\n    }\n  }\n\n private:\n  // XOR the fingerprint with a value from one of the tables.\n  void xortab(std::array<uint64_t, detail::poly_size(BITS)> const& tab) {\n    for (int i = 0; i < size(); i++) {\n      fp_[i] ^= tab[i];\n    }\n  }\n\n  // Helper functions: shift the fingerprint value left by 8/32/64 bits,\n  // return the \"out\" value (the bits that were shifted out), and add \"v\"\n  // in the bits on the right.\n  uint8_t shlor8(uint8_t v);\n  uint32_t shlor32(uint32_t v);\n  uint64_t shlor64(uint64_t v);\n\n  uint64_t fp_[detail::poly_size(BITS)];\n};\n\n// Convenience functions\n\n/**\n * Return the 64-bit Rabin fingerprint of a string.\n */\ninline uint64_t fingerprint64(StringPiece str) {\n  uint64_t fp;\n  Fingerprint<64>().update(str).write(&fp);\n  return fp;\n}\n\n/**\n * Compute the 96-bit Rabin fingerprint of a string.\n * Return the 64 most significant bits in *msb, and the 32 least significant\n * bits in *lsb.\n */\ninline void fingerprint96(StringPiece str, uint64_t* msb, uint32_t* lsb) {\n  uint64_t fp[2];\n  Fingerprint<96>().update(str).write(fp);\n  *msb = fp[0];\n  *lsb = (uint32_t)(fp[1] >> 32);\n}\n\n/**\n * Compute the 128-bit Rabin fingerprint of a string.\n * Return the 64 most significant bits in *msb, and the 64 least significant\n * bits in *lsb.\n */\ninline void fingerprint128(StringPiece str, uint64_t* msb, uint64_t* lsb) {\n  uint64_t fp[2];\n  Fingerprint<128>().update(str).write(fp);\n  *msb = fp[0];\n  *lsb = fp[1];\n}\n\ntemplate <>\ninline uint8_t Fingerprint<64>::shlor8(uint8_t v) {\n  uint8_t out = (uint8_t)(fp_[0] >> 56);\n  fp_[0] = (fp_[0] << 8) | ((uint64_t)v);\n  return out;\n}\n\ntemplate <>\ninline uint32_t Fingerprint<64>::shlor32(uint32_t v) {\n  uint32_t out = (uint32_t)(fp_[0] >> 32);\n  fp_[0] = (fp_[0] << 32) | ((uint64_t)v);\n  return out;\n}\n\ntemplate <>\ninline uint64_t Fingerprint<64>::shlor64(uint64_t v) {\n  uint64_t out = fp_[0];\n  fp_[0] = v;\n  return out;\n}\n\ntemplate <>\ninline uint8_t Fingerprint<96>::shlor8(uint8_t v) {\n  uint8_t out = (uint8_t)(fp_[0] >> 56);\n  fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56);\n  fp_[1] = (fp_[1] << 8) | ((uint64_t)v << 32);\n  return out;\n}\n\ntemplate <>\ninline uint32_t Fingerprint<96>::shlor32(uint32_t v) {\n  uint32_t out = (uint32_t)(fp_[0] >> 32);\n  fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32);\n  fp_[1] = ((uint64_t)v << 32);\n  return out;\n}\n\ntemplate <>\ninline uint64_t Fingerprint<96>::shlor64(uint64_t v) {\n  uint64_t out = fp_[0];\n  fp_[0] = fp_[1] | (v >> 32);\n  fp_[1] = v << 32;\n  return out;\n}\n\ntemplate <>\ninline uint8_t Fingerprint<128>::shlor8(uint8_t v) {\n  uint8_t out = (uint8_t)(fp_[0] >> 56);\n  fp_[0] = (fp_[0] << 8) | (fp_[1] >> 56);\n  fp_[1] = (fp_[1] << 8) | ((uint64_t)v);\n  return out;\n}\n\ntemplate <>\ninline uint32_t Fingerprint<128>::shlor32(uint32_t v) {\n  uint32_t out = (uint32_t)(fp_[0] >> 32);\n  fp_[0] = (fp_[0] << 32) | (fp_[1] >> 32);\n  fp_[1] = (fp_[1] << 32) | ((uint64_t)v);\n  return out;\n}\n\ntemplate <>\ninline uint64_t Fingerprint<128>::shlor64(uint64_t v) {\n  uint64_t out = fp_[0];\n  fp_[0] = fp_[1];\n  fp_[1] = v;\n  return out;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/FixedString.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Fixed-size string type, for constexpr string handling.\n\n#pragma once\n\n#include <cassert>\n#include <cstddef>\n#include <initializer_list>\n#include <iosfwd>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/Utility.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Ordering.h>\n#include <folly/portability/Constexpr.h>\n\nnamespace folly {\n\ntemplate <class Char, std::size_t N>\nclass BasicFixedString;\n\ntemplate <std::size_t N>\nusing FixedString = BasicFixedString<char, N>;\n\nnamespace detail {\nnamespace fixedstring {\n\n// This is a template so that the class static npos can be defined in the\n// header.\ntemplate <class = void>\nstruct FixedStringBase_ {\n  static constexpr std::size_t npos = static_cast<std::size_t>(-1);\n};\n\nusing FixedStringBase = FixedStringBase_<>;\n\n// Intentionally NOT constexpr. By making this not constexpr, we make\n// checkOverflow below ill-formed in a constexpr context when the condition\n// it's testing for fails. In this way, precondition violations are reported\n// at compile-time instead of at runtime.\n[[noreturn]] inline void assertOutOfBounds() {\n  assert(false && \"Array index out of bounds in BasicFixedString\");\n  throw_exception<std::out_of_range>(\n      \"Array index out of bounds in BasicFixedString\");\n}\n\nconstexpr std::size_t checkOverflow(std::size_t i, std::size_t max) {\n  return i <= max ? i : (void(assertOutOfBounds()), max);\n}\n\nconstexpr std::size_t checkOverflowOrNpos(std::size_t i, std::size_t max) {\n  return i == FixedStringBase::npos\n      ? max\n      : (i <= max ? i : (void(assertOutOfBounds()), max));\n}\n\nconstexpr std::size_t checkOverflowIfDebug(std::size_t i, std::size_t size) {\n  return kIsDebug ? checkOverflow(i, size) : i;\n}\n\n// Intentionally NOT constexpr. See note above for assertOutOfBounds\n[[noreturn]] inline void assertNotNullTerminated() noexcept {\n  assert(\n      false &&\n      \"Non-null terminated string used to initialize a BasicFixedString\");\n  std::terminate(); // Fail hard, fail fast.\n}\n\n// Parsing help for human readers: the following is a constexpr noexcept\n// function that accepts a reference to an array as a parameter and returns\n// a reference to the same array.\ntemplate <class Char, std::size_t N>\nconstexpr const Char (&checkNullTerminated(const Char (&a)[N]) noexcept)[N] {\n  // Strange decltype(a)(a) used to make MSVC happy.\n  return a[N - 1u] == Char(0)\n          // In Debug mode, guard against embedded nulls:\n          && (!kIsDebug || N - 1u == folly::constexpr_strlen(a))\n      ? decltype(a)(a)\n      : (assertNotNullTerminated(), decltype(a)(a));\n}\n\ntemplate <class Left, class Right>\nconstexpr ordering compare_(\n    const Left& left,\n    std::size_t left_pos,\n    std::size_t left_size,\n    const Right& right,\n    std::size_t right_pos,\n    std::size_t right_size) noexcept {\n  return left_pos == left_size\n      ? (right_pos == right_size ? ordering::eq : ordering::lt)\n      : (right_pos == right_size\n             ? ordering::gt\n             : (left[left_pos] < right[right_pos]\n                    ? ordering::lt\n                    : (left[left_pos] > right[right_pos]\n                           ? ordering::gt\n                           : fixedstring::compare_(\n                                 left,\n                                 left_pos + 1u,\n                                 left_size,\n                                 right,\n                                 right_pos + 1u,\n                                 right_size))));\n}\n\ntemplate <class Left, class Right>\nconstexpr bool equal_(\n    const Left& left,\n    std::size_t left_size,\n    const Right& right,\n    std::size_t right_size) noexcept {\n  return left_size == right_size &&\n      ordering::eq == compare_(left, 0u, left_size, right, 0u, right_size);\n}\n\ntemplate <class Char, class Left, class Right>\nconstexpr Char char_at_(\n    const Left& left,\n    std::size_t left_count,\n    const Right& right,\n    std::size_t right_count,\n    std::size_t i) noexcept {\n  return i < left_count ? left[i]\n      : i < (left_count + right_count)\n      ? right[i - left_count]\n      : Char(0);\n}\n\ntemplate <class Char, class Left, class Right>\nconstexpr Char char_at_(\n    const Left& left,\n    std::size_t left_size,\n    std::size_t left_pos,\n    std::size_t left_count,\n    const Right& right,\n    std::size_t right_pos,\n    std::size_t right_count,\n    std::size_t i) noexcept {\n  FOLLY_PUSH_WARNING\n#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 14\n#pragma GCC diagnostic ignored \"-Warray-bounds\"\n#endif\n  return i < left_pos\n      ? left[i]\n      : (i < right_count + left_pos\n             ? right[i - left_pos + right_pos]\n             : (i < left_size - left_count + right_count\n                    ? left[i - right_count + left_count]\n                    : Char(0)));\n  FOLLY_POP_WARNING\n}\n\ntemplate <class Left, class Right>\nconstexpr bool find_at_(\n    const Left& left,\n    const Right& right,\n    std::size_t pos,\n    std::size_t count) noexcept {\n  return 0u == count ||\n      (left[pos + count - 1u] == right[count - 1u] &&\n       find_at_(left, right, pos, count - 1u));\n}\n\ntemplate <class Char, class Right>\nconstexpr bool find_one_of_at_(\n    Char ch, const Right& right, std::size_t pos) noexcept {\n  return 0u != pos &&\n      (ch == right[pos - 1u] || find_one_of_at_(ch, right, pos - 1u));\n}\n\ntemplate <class Left, class Right>\nconstexpr std::size_t find_(\n    const Left& left,\n    std::size_t left_size,\n    const Right& right,\n    std::size_t pos,\n    std::size_t count) noexcept {\n  return find_at_(left, right, pos, count) ? pos\n      : left_size <= pos + count\n      ? FixedStringBase::npos\n      : find_(left, left_size, right, pos + 1u, count);\n}\n\ntemplate <class Left, class Right>\nconstexpr std::size_t rfind_(\n    const Left& left,\n    const Right& right,\n    std::size_t pos,\n    std::size_t count) noexcept {\n  return find_at_(left, right, pos, count) ? pos\n      : 0u == pos\n      ? FixedStringBase::npos\n      : rfind_(left, right, pos - 1u, count);\n}\n\ntemplate <class Left, class Right>\nconstexpr std::size_t find_first_of_(\n    const Left& left,\n    std::size_t left_size,\n    const Right& right,\n    std::size_t pos,\n    std::size_t count) noexcept {\n  return find_one_of_at_(left[pos], right, count) ? pos\n      : left_size <= pos + 1u\n      ? FixedStringBase::npos\n      : find_first_of_(left, left_size, right, pos + 1u, count);\n}\n\ntemplate <class Left, class Right>\nconstexpr std::size_t find_first_not_of_(\n    const Left& left,\n    std::size_t left_size,\n    const Right& right,\n    std::size_t pos,\n    std::size_t count) noexcept {\n  return !find_one_of_at_(left[pos], right, count) ? pos\n      : left_size <= pos + 1u\n      ? FixedStringBase::npos\n      : find_first_not_of_(left, left_size, right, pos + 1u, count);\n}\n\ntemplate <class Left, class Right>\nconstexpr std::size_t find_last_of_(\n    const Left& left,\n    const Right& right,\n    std::size_t pos,\n    std::size_t count) noexcept {\n  return find_one_of_at_(left[pos], right, count) ? pos\n      : 0u == pos\n      ? FixedStringBase::npos\n      : find_last_of_(left, right, pos - 1u, count);\n}\n\ntemplate <class Left, class Right>\nconstexpr std::size_t find_last_not_of_(\n    const Left& left,\n    const Right& right,\n    std::size_t pos,\n    std::size_t count) noexcept {\n  return !find_one_of_at_(left[pos], right, count) ? pos\n      : 0u == pos\n      ? FixedStringBase::npos\n      : find_last_not_of_(left, right, pos - 1u, count);\n}\n\nstruct Helper {\n  template <class Char, class Left, class Right, std::size_t... Is>\n  static constexpr BasicFixedString<Char, sizeof...(Is)> concat_(\n      const Left& left,\n      std::size_t left_count,\n      const Right& right,\n      std::size_t right_count,\n      std::index_sequence<Is...> is) noexcept {\n    return {left, left_count, right, right_count, is};\n  }\n\n  template <class Char, class Left, class Right, std::size_t... Is>\n  static constexpr BasicFixedString<Char, sizeof...(Is)> replace_(\n      const Left& left,\n      std::size_t left_size,\n      std::size_t left_pos,\n      std::size_t left_count,\n      const Right& right,\n      std::size_t right_pos,\n      std::size_t right_count,\n      std::index_sequence<Is...> is) noexcept {\n    return {\n        left,\n        left_size,\n        left_pos,\n        left_count,\n        right,\n        right_pos,\n        right_count,\n        is};\n  }\n\n  template <class Char, std::size_t N>\n  static constexpr const Char (\n      &data_(const BasicFixedString<Char, N>& that) noexcept)[N + 1u] {\n    return that.data_;\n  }\n};\n\ntemplate <class T>\nconstexpr void constexpr_swap(T& a, T& b) noexcept(\n    noexcept(a = T(std::move(a)))) {\n  T tmp((std::move(a)));\n  a = std::move(b);\n  b = std::move(tmp);\n}\n\n// For constexpr reverse iteration over a BasicFixedString\ntemplate <class T>\nstruct ReverseIterator {\n private:\n  T* p_ = nullptr;\n  struct dummy_ {\n    T* p_ = nullptr;\n  };\n  using other = typename std::conditional<\n      std::is_const<T>::value,\n      ReverseIterator<typename std::remove_const<T>::type>,\n      dummy_>::type;\n\n public:\n  using value_type = typename std::remove_const<T>::type;\n  using reference = T&;\n  using pointer = T*;\n  using difference_type = std::ptrdiff_t;\n  using iterator_category = std::random_access_iterator_tag;\n\n  constexpr ReverseIterator() = default;\n  constexpr ReverseIterator(const ReverseIterator&) = default;\n  constexpr ReverseIterator& operator=(const ReverseIterator&) = default;\n  constexpr explicit ReverseIterator(T* p) noexcept : p_(p) {}\n  constexpr /* implicit */ ReverseIterator(const other& that) noexcept\n      : p_(that.p_) {}\n  friend constexpr bool operator==(\n      ReverseIterator a, ReverseIterator b) noexcept {\n    return a.p_ == b.p_;\n  }\n  friend constexpr bool operator!=(\n      ReverseIterator a, ReverseIterator b) noexcept {\n    return !(a == b);\n  }\n  constexpr reference operator*() const { return *(p_ - 1); }\n  constexpr ReverseIterator& operator++() noexcept {\n    --p_;\n    return *this;\n  }\n  constexpr ReverseIterator operator++(int) noexcept {\n    auto tmp(*this);\n    --p_;\n    return tmp;\n  }\n  constexpr ReverseIterator& operator--() noexcept {\n    ++p_;\n    return *this;\n  }\n  constexpr ReverseIterator operator--(int) noexcept {\n    auto tmp(*this);\n    ++p_;\n    return tmp;\n  }\n  constexpr ReverseIterator& operator+=(std::ptrdiff_t i) noexcept {\n    p_ -= i;\n    return *this;\n  }\n  friend constexpr ReverseIterator operator+(\n      std::ptrdiff_t i, ReverseIterator that) noexcept {\n    return ReverseIterator{that.p_ - i};\n  }\n  friend constexpr ReverseIterator operator+(\n      ReverseIterator that, std::ptrdiff_t i) noexcept {\n    return ReverseIterator{that.p_ - i};\n  }\n  constexpr ReverseIterator& operator-=(std::ptrdiff_t i) noexcept {\n    p_ += i;\n    return *this;\n  }\n  friend constexpr ReverseIterator operator-(\n      ReverseIterator that, std::ptrdiff_t i) noexcept {\n    return ReverseIterator{that.p_ + i};\n  }\n  friend constexpr std::ptrdiff_t operator-(\n      ReverseIterator a, ReverseIterator b) noexcept {\n    return b.p_ - a.p_;\n  }\n  constexpr reference operator[](std::ptrdiff_t i) const noexcept {\n    return *(*this + i);\n  }\n};\n\n} // namespace fixedstring\n} // namespace detail\n\n/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *\n * \\class BasicFixedString\n *\n * \\tparam Char The character type. Must be a scalar type.\n * \\tparam N The capacity and max size of string instances of this type.\n *\n * \\brief A class for holding up to `N` characters of type `Char` that is\n *        amenable to `constexpr` string manipulation. It is guaranteed to not\n *        perform any dynamic allocation.\n *\n * `BasicFixedString` is a `std::string` work-alike that stores characters in an\n * internal buffer. It has minor interface differences that make it easy to work\n * with strings in a `constexpr` context.\n *\n * \\par Example:\n * \\par\n * \\code\n * constexpr auto hello = makeFixedString(\"hello\");         // a FixedString<5>\n * constexpr auto world = makeFixedString(\"world\");         // a FixedString<5>\n * constexpr auto hello_world = hello + ' ' + world + '!';  // a FixedString<12>\n * static_assert(hello_world == \"hello world!\", \"neato!\");\n * \\endcode\n * \\par\n * `FixedString<N>` is an alias for `BasicFixedString<char, N>`.\n *\n * \\par Constexpr and In-place Mutation\n * \\par\n * On a C++14 compiler, `BasicFixedString` supports the full `std::string`\n * interface as `constexpr` member functions. On a C++11 compiler, the mutating\n * members are not `constexpr`, but non-mutating alternatives, which create a\n * new string, can be used instead. For example, instead of this:\n * \\par\n * \\code\n * constexpr FixedString<10> replace_example_cpp14() {\n *   FixedString<10> test{\"****\"};\n *   test.replace(1, 2, \"!!!!\");\n *   return test; // returns \"*!!!!*\"\n * }\n * \\endcode\n * \\par\n * You might write this instead:\n * \\par\n * \\code\n * constexpr FixedString<10> replace_example_cpp11() {\n *   // GNU compilers have an extension that make it possible to create\n *   // FixedString objects with a `\"\"_fs` user-defined literal.\n *   using namespace folly;\n *   return makeFixedString(\"****\").creplace(1, 2, \"!!!!\"); // \"*!!!!*\"\n * }\n * \\endcode\n *\n * \\par User-defined Literals\n * Instead of using the `folly::makeFixedString` helper function, you can use\n * a user-defined literal to make `FixedString` instances. The UDL feature of\n * C++ has some limitations that make this less than ideal; you must tell the\n * compiler roughly how many characters are in the string. The suffixes `_fs4`,\n * `_fs8`, `_fs16`, `_fs32`, `_fs64`, and `_fs128` exist to create instances\n * of types `FixedString<4>`, `FixedString<8>`, etc. For example:\n * \\par\n * \\code\n * using namespace folly::string_literals;\n * constexpr auto hello = \"hello\"_fs8; // A FixedString<8> containing \"hello\"\n * \\endcode\n * \\par\n * See Error Handling below for what to expect when you try to exceed the\n * capacity of a `FixedString` by storing too many characters in it.\n * \\par\n * If your compiler supports GNU extensions, there is one additional suffix you\n * can use: `_fs`. This suffix always creates `FixedString` objects of exactly\n * the right size. For example:\n * \\par\n * \\code\n * using namespace folly::string_literals;\n * // NOTE: Only works on compilers with GNU extensions enabled. Clang and\n * // gcc support this (-Wgnu-string-literal-operator-template):\n * constexpr auto hello = \"hello\"_fs; // A FixedString<5> containing \"hello\"\n * \\endcode\n *\n * \\par Error Handling:\n * The capacity of a `BasicFixedString` is set at compile time. When the user\n * asks the string to exceed its capacity, one of three things will happen,\n * depending on the context:\n *\\par\n *  -# If the attempt is made while evaluating a constant expression, the\n *     program will fail to compile.\n *  -# Otherwise, if the program is being run in debug mode, it will `assert`.\n *  -# Otherwise, the failed operation will throw a `std::out_of_range`\n *     exception.\n *\\par\n * This is also the case if an invalid offset is passed to any member function,\n * or if `pop_back` or `cpop_back` is called on an empty `BasicFixedString`.\n *\n * Member functions documented as having preconditions will assert in Debug\n * mode (`!defined(NDEBUG)`) on precondition failures. Those documented with\n * \\b Throws clauses will throw the specified exception on failure. Those with\n * both a precondition and a \\b Throws clause will assert in Debug and throw\n * in Release mode.\n */\ntemplate <class Char, std::size_t N>\nclass BasicFixedString : private detail::fixedstring::FixedStringBase {\n private:\n  template <class, std::size_t>\n  friend class BasicFixedString;\n  friend struct detail::fixedstring::Helper;\n\n  // FUTURE: use constexpr_log2 to fold instantiations of BasicFixedString\n  // together. All BasicFixedString<C, N> instantiations could share the\n  // implementation of BasicFixedString<C, M>, where M is the next highest power\n  // of 2 after N.\n  //\n  // Also, because of alignment of the data_ and size_ members, N should never\n  // be smaller than `(alignof(std::size_t)/sizeof(C))-1` (-1 because of the\n  // null terminator). OR, create a specialization for BasicFixedString<C, 0u>\n  // that does not have a size_ member, since it is unnecessary.\n  Char data_[N + 1u]; // +1 for the null terminator\n  std::size_t size_; // Nbr of chars, not incl. null terminator. size_ <= N.\n\n  using Indices = std::make_index_sequence<N>;\n\n  template <class That, std::size_t... Is>\n  constexpr BasicFixedString(\n      const That& that,\n      std::size_t size,\n      std::index_sequence<Is...>,\n      std::size_t pos = 0,\n      std::size_t count = npos) noexcept\n      : data_{(Is < (size - pos) && Is < count ? that[Is + pos] : Char(0))..., Char(0)},\n        size_{folly::constexpr_min(size - pos, count)} {}\n\n  template <std::size_t... Is>\n  constexpr BasicFixedString(\n      std::size_t count, Char ch, std::index_sequence<Is...>) noexcept\n      : data_{((Is < count) ? ch : Char(0))..., Char(0)}, size_{count} {}\n\n  // Concatenation constructor\n  template <class Left, class Right, std::size_t... Is>\n  constexpr BasicFixedString(\n      const Left& left,\n      std::size_t left_size,\n      const Right& right,\n      std::size_t right_size,\n      std::index_sequence<Is...>) noexcept\n      : data_{detail::fixedstring::char_at_<Char>(left, left_size, right, right_size, Is)..., Char(0)},\n        size_{left_size + right_size} {}\n\n  // Replace constructor\n  template <class Left, class Right, std::size_t... Is>\n  constexpr BasicFixedString(\n      const Left& left,\n      std::size_t left_size,\n      std::size_t left_pos,\n      std::size_t left_count,\n      const Right& right,\n      std::size_t right_pos,\n      std::size_t right_count,\n      std::index_sequence<Is...>) noexcept\n      : data_{detail::fixedstring::char_at_<Char>(left, left_size, left_pos, left_count, right, right_pos, right_count, Is)..., Char(0)},\n        size_{left_size - left_count + right_count} {}\n\n public:\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n  using reference = Char&;\n  using const_reference = const Char&;\n  using pointer = Char*;\n  using const_pointer = const Char*;\n  using iterator = Char*;\n  using const_iterator = const Char*;\n  using reverse_iterator = detail::fixedstring::ReverseIterator<Char>;\n  using const_reverse_iterator =\n      detail::fixedstring::ReverseIterator<const Char>;\n\n  using detail::fixedstring::FixedStringBase::npos;\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Default construct\n   * \\post `size() == 0`\n   * \\post `at(0) == Char(0)`\n   */\n  constexpr BasicFixedString() : data_{}, size_{} {}\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Copy construct\n   * \\post `size() == that.size()`\n   * \\post `0 == strncmp(data(), that.data(), size())`\n   * \\post `at(size()) == Char(0)`\n   */\n  constexpr BasicFixedString(const BasicFixedString& /*that*/) = default;\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct from a differently-sized BasicFixedString\n   * \\pre `that.size() <= N`\n   * \\post `size() == that.size()`\n   * \\post `0 == strncmp(data(), that.data(), size())`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when that.size() > N. When M <= N, this\n   *   constructor will never throw.\n   * \\note Conversions from larger-capacity BasicFixedString objects to smaller\n   *   ones (`M > N`) are allowed as long as the *size()* of the source string\n   *   is small enough.\n   */\n  template <std::size_t M>\n  constexpr /* implicit */ BasicFixedString(\n      const BasicFixedString<Char, M>& that) noexcept(M <= N)\n      : BasicFixedString{that, 0u, that.size_} {}\n\n  // Why is this deleted? To avoid confusion with the constructor that takes\n  // a const Char* and a count.\n  template <std::size_t M>\n  constexpr BasicFixedString(\n      const BasicFixedString<Char, M>& that,\n      std::size_t pos) noexcept(false) = delete;\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct from an BasicFixedString, an offset, and a count\n   * \\param that The source string\n   * \\param pos The starting position in `that`\n   * \\param count The number of characters to copy. If `npos`, `count` is taken\n   *              to be `that.size()-pos`.\n   * \\pre `pos <= that.size()`\n   * \\pre `count <= that.size()-pos && count <= N`\n   * \\post `size() == count`\n   * \\post `0 == strncmp(data(), that.data()+pos, size())`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when pos+count > that.size(), or when\n   *        `count > N`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString(\n      const BasicFixedString<Char, M>& that,\n      std::size_t pos,\n      std::size_t count) noexcept(false)\n      : BasicFixedString{\n            that.data_,\n            that.size_,\n            std::make_index_sequence<(M < N ? M : N)>{},\n            pos,\n            detail::fixedstring::checkOverflow(\n                detail::fixedstring::checkOverflowOrNpos(\n                    count,\n                    that.size_ -\n                        detail::fixedstring::checkOverflow(pos, that.size_)),\n                N)} {}\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct from a string literal\n   * \\pre `M-1 <= N`\n   * \\pre `that[M-1] == Char(0)`\n   * \\post `0 == strncmp(data(), that, M-1)`\n   * \\post `size() == M-1`\n   * \\post `at(size()) == Char(0)`\n   */\n  template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type>\n  constexpr /* implicit */ BasicFixedString(const Char (&that)[M]) noexcept\n      : BasicFixedString{\n            detail::fixedstring::checkNullTerminated(that),\n            M - 1u,\n            std::make_index_sequence<M - 1u>{}} {}\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct from a `const Char*` and count\n   * \\pre `that` points to an array of at least `count` characters.\n   * \\pre `count <= N`\n   * \\post `size() == count`\n   * \\post `0 == strncmp(data(), that, size())`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when count > N\n   */\n  constexpr BasicFixedString(const Char* that, std::size_t count) noexcept(\n      false)\n      : BasicFixedString{\n            that, detail::fixedstring::checkOverflow(count, N), Indices{}} {}\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct from a `std::basic_string_view<Char>`\n   * \\param that The source basic_string_view\n   * \\pre `that.size() <= N`\n   * \\post `size() == that.size()`\n   * \\post `0 == strncmp(data(), that.begin(), size())`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when that.size() > N\n   */\n  constexpr /* implicit */ BasicFixedString(\n      std::basic_string_view<Char> that) noexcept(false)\n      : BasicFixedString{that.data(), that.size()} {}\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct an BasicFixedString that contains `count` characters, all\n   *   of which are `ch`.\n   * \\pre `count <= N`\n   * \\post `size() == count`\n   * \\post `npos == find_first_not_of(ch)`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when count > N\n   */\n  constexpr BasicFixedString(std::size_t count, Char ch) noexcept(false)\n      : BasicFixedString{\n            detail::fixedstring::checkOverflow(count, N), ch, Indices{}} {}\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct an BasicFixedString from a `std::initializer_list` of\n   *   characters.\n   * \\pre `il.size() <= N`\n   * \\post `size() == count`\n   * \\post `0 == strncmp(data(), il.begin(), size())`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when il.size() > N\n   */\n  constexpr BasicFixedString(std::initializer_list<Char> il) noexcept(false)\n      : BasicFixedString{il.begin(), il.size()} {}\n\n  constexpr BasicFixedString& operator=(const BasicFixedString&) noexcept =\n      default;\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assign from a `BasicFixedString<Char, M>`.\n   * \\pre `that.size() <= N`\n   * \\post `size() == that.size()`\n   * \\post `0 == strncmp(data(), that.begin(), size())`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when that.size() > N. When M <= N, this\n   *   assignment operator will never throw.\n   * \\note Assignments from larger-capacity BasicFixedString objects to smaller\n   *   ones (`M > N`) are allowed as long as the *size* of the source string is\n   *   small enough.\n   * \\return `*this`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& operator=(\n      const BasicFixedString<Char, M>& that) noexcept(M <= N) {\n    detail::fixedstring::checkOverflow(that.size_, N);\n    size_ = that.copy(data_, that.size_);\n    data_[size_] = Char(0);\n    return *this;\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assign from a null-terminated array of characters.\n   * \\pre `M < N`\n   * \\pre `that` has no embedded null characters\n   * \\pre `that[M-1]==Char(0)`\n   * \\post `size() == M-1`\n   * \\post `0 == strncmp(data(), that, size())`\n   * \\post `at(size()) == Char(0)`\n   * \\return `*this`\n   */\n  template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type>\n  constexpr BasicFixedString& operator=(const Char (&that)[M]) noexcept {\n    return assign(detail::fixedstring::checkNullTerminated(that), M - 1u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assign from an `initializer_list` of characters.\n   * \\pre `il.size() <= N`\n   * \\post `size() == il.size()`\n   * \\post `0 == strncmp(data(), il.begin(), size())`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when il.size() > N\n   * \\return `*this`\n   */\n  constexpr BasicFixedString& operator=(\n      std::initializer_list<Char> il) noexcept(false) {\n    detail::fixedstring::checkOverflow(il.size(), N);\n    for (std::size_t i = 0u; i < il.size(); ++i) {\n      data_[i] = il.begin()[i];\n    }\n    size_ = il.size();\n    data_[size_] = Char(0);\n    return *this;\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Conversion to folly::Range\n   * \\return `Range<Char*>{begin(), end()}`\n   */\n  constexpr Range<Char*> toRange() noexcept { return {begin(), end()}; }\n\n  /**\n   * \\overload\n   */\n  constexpr Range<const Char*> toRange() const noexcept {\n    return {begin(), end()};\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Conversion to std::basic_string<Char>\n   * \\return `std::basic_string<Char>{begin(), end()}`\n   */\n  /* implicit */ operator std::basic_string<Char>() const noexcept(false) {\n    return std::basic_string<Char>{begin(), end()};\n  }\n\n  std::basic_string<Char> toStdString() const noexcept(false) {\n    return std::basic_string<Char>{begin(), end()};\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Conversion to std::basic_string_view<Char>\n   * \\return `std::basic_string_view<Char>{begin(), end()}`\n   */\n  /* implicit */ constexpr operator std::basic_string_view<Char>() const {\n    return std::basic_string_view<Char>{begin(), size()};\n  }\n\n  // Think hard about whether this is a good idea. It's certainly better than\n  // an implicit conversion to `const Char*` since `delete \"hi\"_fs` will fail\n  // to compile. But it creates ambiguities when passing a FixedString to an\n  // API that has overloads for `const char*` and `folly::Range`, for instance.\n  // using ArrayType = Char[N];\n  // constexpr /* implicit */ operator ArrayType&() noexcept {\n  //   return data_;\n  // }\n\n  // using ConstArrayType = const Char[N];\n  // constexpr /* implicit */ operator ConstArrayType&() const noexcept {\n  //   return data_;\n  // }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assigns a sequence of `count` characters of value `ch`.\n   * \\param count The count of characters.\n   * \\param ch\n   * \\pre `count <= N`\n   * \\post `size() == count`\n   * \\post `npos == find_first_not_of(ch)`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when count > N\n   * \\return `*this`\n   */\n  constexpr BasicFixedString& assign(std::size_t count, Char ch) noexcept(\n      false) {\n    detail::fixedstring::checkOverflow(count, N);\n    for (std::size_t i = 0u; i < count; ++i) {\n      data_[i] = ch;\n    }\n    size_ = count;\n    data_[size_] = Char(0);\n    return *this;\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assigns characters from an `BasicFixedString` to this object.\n   * \\note Equivalent to `assign(that, 0, that.size())`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& assign(\n      const BasicFixedString<Char, M>& that) noexcept(M <= N) {\n    return *this = that;\n  }\n\n  // Why is this overload deleted? So users aren't confused by the difference\n  // between str.assign(\"foo\", N) and str.assign(\"foo\"_fs, N). In the former,\n  // N is a count of characters. In the latter, it would be a position, which\n  // totally changes the meaning of the code.\n  template <std::size_t M>\n  constexpr BasicFixedString& assign(\n      const BasicFixedString<Char, M>& that,\n      std::size_t pos) noexcept(false) = delete;\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assigns `count` characters from an `BasicFixedString` to this object,\n   *   starting at position `pos` in the source object.\n   * \\param that The source string.\n   * \\param pos The starting position in the source string.\n   * \\param count The number of characters to copy. If `npos`, `count` is taken\n   *              to be `that.size()-pos`.\n   * \\pre `pos <= that.size()`\n   * \\pre `count <= that.size()-pos`\n   * \\pre `count <= N`\n   * \\post `size() == count`\n   * \\post `0 == strncmp(data(), that.begin() + pos, count)`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when pos > that.size() or count > that.size()-pos\n   *        or count > N.\n   * \\return `*this`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& assign(\n      const BasicFixedString<Char, M>& that,\n      std::size_t pos,\n      std::size_t count) noexcept(false) {\n    detail::fixedstring::checkOverflow(pos, that.size_);\n    return assign(\n        that.data_ + pos,\n        detail::fixedstring::checkOverflowOrNpos(count, that.size_ - pos));\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assigns characters from an `BasicFixedString` to this object.\n   * \\pre `that` contains no embedded nulls.\n   * \\pre `that[M-1] == Char(0)`\n   * \\note Equivalent to `assign(that, M - 1)`\n   */\n  template <std::size_t M, class = typename std::enable_if<(M - 1u <= N)>::type>\n  constexpr BasicFixedString& assign(const Char (&that)[M]) noexcept {\n    return assign(detail::fixedstring::checkNullTerminated(that), M - 1u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Assigns `count` characters from a range of characters to this object.\n   * \\param that A pointer to a range of characters.\n   * \\param count The number of characters to copy.\n   * \\pre `that` points to at least `count` characters.\n   * \\pre `count <= N`\n   * \\post `size() == count`\n   * \\post `0 == strncmp(data(), that, count)`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range when count > N\n   * \\return `*this`\n   */\n  constexpr BasicFixedString& assign(\n      const Char* that, std::size_t count) noexcept(false) {\n    detail::fixedstring::checkOverflow(count, N);\n    for (std::size_t i = 0u; i < count; ++i) {\n      data_[i] = that[i];\n    }\n    size_ = count;\n    data_[size_] = Char(0);\n    return *this;\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Swap the contents of this string with `that`.\n   */\n  constexpr void swap(BasicFixedString& that) noexcept {\n    // less-than-or-equal here to copy the null terminator:\n    for (std::size_t i = 0u; i <= folly::constexpr_max(size_, that.size_);\n         ++i) {\n      detail::fixedstring::constexpr_swap(data_[i], that.data_[i]);\n    }\n    detail::fixedstring::constexpr_swap(size_, that.size_);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Return a pointer to a range of `size()+1` characters, the last of which\n   * is `Char(0)`.\n   */\n  constexpr Char* data() noexcept { return data_; }\n\n  /**\n   * \\overload\n   */\n  constexpr const Char* data() const noexcept { return data_; }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * \\return `data()`.\n   */\n  constexpr const Char* c_str() const noexcept { return data_; }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * \\return `data()`.\n   */\n  constexpr Char* begin() noexcept { return data_; }\n\n  /**\n   * \\overload\n   */\n  constexpr const Char* begin() const noexcept { return data_; }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * \\return `data()`.\n   */\n  constexpr const Char* cbegin() const noexcept { return begin(); }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * \\return `data() + size()`.\n   */\n  constexpr Char* end() noexcept { return data_ + size_; }\n\n  /**\n   * \\overload\n   */\n  constexpr const Char* end() const noexcept { return data_ + size_; }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * \\return `data() + size()`.\n   */\n  constexpr const Char* cend() const noexcept { return end(); }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Returns a reverse iterator to the first character of the reversed string.\n   * It corresponds to the last + 1 character of the non-reversed string.\n   */\n  constexpr reverse_iterator rbegin() noexcept {\n    return reverse_iterator{data_ + size_};\n  }\n\n  /**\n   * \\overload\n   */\n  constexpr const_reverse_iterator rbegin() const noexcept {\n    return const_reverse_iterator{data_ + size_};\n  }\n\n  /**\n   * \\note Equivalent to `rbegin()` on a const-qualified reference to `*this`.\n   */\n  constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Returns a reverse iterator to the last + 1 character of the reversed\n   * string. It corresponds to the first character of the non-reversed string.\n   */\n  constexpr reverse_iterator rend() noexcept { return reverse_iterator{data_}; }\n\n  /**\n   * \\overload\n   */\n  constexpr const_reverse_iterator rend() const noexcept {\n    return const_reverse_iterator{data_};\n  }\n\n  /**\n   * \\note Equivalent to `rend()` on a const-qualified reference to `*this`.\n   */\n  constexpr const_reverse_iterator crend() const noexcept { return rend(); }\n\n  /**\n   * \\return The number of `Char` elements in the string.\n   */\n  constexpr std::size_t size() const noexcept { return size_; }\n\n  /**\n   * \\return The number of `Char` elements in the string.\n   */\n  constexpr std::size_t length() const noexcept { return size_; }\n\n  /**\n   * \\return True if and only if `size() == 0`.\n   */\n  constexpr bool empty() const noexcept { return 0u == size_; }\n\n  /**\n   * \\return `N`.\n   */\n  static constexpr std::size_t capacity() noexcept { return N; }\n\n  /**\n   * \\return `N`.\n   */\n  static constexpr std::size_t max_size() noexcept { return N; }\n\n  /**\n   * \\note `at(size())` is allowed will return `Char(0)`.\n   * \\return `*(data() + i)`\n   * \\throw std::out_of_range when i > size()\n   */\n  constexpr Char& at(std::size_t i) noexcept(false) {\n    return i <= size_\n        ? data_[i]\n        : (throw_exception<std::out_of_range>(\n               \"Out of range in BasicFixedString::at\"),\n           data_[size_]);\n  }\n\n  /**\n   * \\overload\n   */\n  constexpr const Char& at(std::size_t i) const noexcept(false) {\n    return i <= size_\n        ? data_[i]\n        : (throw_exception<std::out_of_range>(\n               \"Out of range in BasicFixedString::at\"),\n           data_[size_]);\n  }\n\n  /**\n   * \\pre `i <= size()`\n   * \\note `(*this)[size()]` is allowed will return `Char(0)`.\n   * \\return `*(data() + i)`\n   */\n  constexpr Char& operator[](std::size_t i) noexcept {\n    return data_[detail::fixedstring::checkOverflowIfDebug(i, size_)];\n  }\n\n  /**\n   * \\overload\n   */\n  constexpr const Char& operator[](std::size_t i) const noexcept {\n    return data_[detail::fixedstring::checkOverflowIfDebug(i, size_)];\n  }\n\n  /**\n   * \\note Equivalent to `(*this)[0]`\n   */\n  constexpr Char& front() noexcept { return (*this)[0u]; }\n\n  /**\n   * \\overload\n   */\n  constexpr const Char& front() const noexcept { return (*this)[0u]; }\n\n  /**\n   * \\note Equivalent to `at(size()-1)`\n   * \\pre `!empty()`\n   */\n  constexpr Char& back() noexcept {\n    return data_[size_ - detail::fixedstring::checkOverflowIfDebug(1u, size_)];\n  }\n\n  /**\n   * \\overload\n   */\n  constexpr const Char& back() const noexcept {\n    return data_[size_ - detail::fixedstring::checkOverflowIfDebug(1u, size_)];\n  }\n\n  /**\n   * Clears the contents of this string.\n   * \\post `size() == 0u`\n   * \\post `at(size()) == Char(0)`\n   */\n  constexpr void clear() noexcept {\n    data_[0u] = Char(0);\n    size_ = 0u;\n  }\n\n  /**\n   * \\note Equivalent to `append(1u, ch)`.\n   */\n  constexpr void push_back(Char ch) noexcept(false) {\n    detail::fixedstring::checkOverflow(1u, N - size_);\n    data_[size_] = ch;\n    data_[++size_] = Char(0);\n  }\n\n  /**\n   * \\note Equivalent to `cappend(1u, ch)`.\n   */\n  constexpr BasicFixedString<Char, N + 1u> cpush_back(Char ch) const noexcept {\n    return cappend(ch);\n  }\n\n  /**\n   * Removes the last character from the string.\n   * \\pre `!empty()`\n   * \\post `size()` is one fewer than before calling `pop_back()`.\n   * \\post `at(size()) == Char(0)`\n   * \\post The characters in the half-open range `[0,size()-1)` are unmodified.\n   * \\throw std::out_of_range if empty().\n   */\n  constexpr void pop_back() noexcept(false) {\n    detail::fixedstring::checkOverflow(1u, size_);\n    --size_;\n    data_[size_] = Char(0);\n  }\n\n  /**\n   * Returns a new string with the first `size()-1` characters from this string.\n   * \\pre `!empty()`\n   * \\note Equivalent to `BasicFixedString<Char, N-1u>{*this, 0u, size()-1u}`\n   * \\throw std::out_of_range if empty().\n   */\n  constexpr BasicFixedString<Char, N - 1u> cpop_back() const noexcept(false) {\n    return {*this, 0u, size_ - detail::fixedstring::checkOverflow(1u, size_)};\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Appends `count` copies of `ch` to this string.\n   * \\pre `count + old_size <= N`\n   * \\post The first `old_size` characters of the string are unmodified.\n   * \\post `size() == old_size + count`\n   * \\throw std::out_of_range if count > N - size().\n   */\n  constexpr BasicFixedString& append(std::size_t count, Char ch) noexcept(\n      false) {\n    detail::fixedstring::checkOverflow(count, N - size_);\n    for (std::size_t i = 0u; i < count; ++i) {\n      data_[size_ + i] = ch;\n    }\n    size_ += count;\n    data_[size_] = Char(0);\n    return *this;\n  }\n\n  /**\n   * \\note Equivalent to `append(*this, 0, that.size())`.\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& append(\n      const BasicFixedString<Char, M>& that) noexcept(false) {\n    return append(that, 0u, that.size_);\n  }\n\n  // Why is this overload deleted? So as not to get confused with\n  // append(\"null-terminated\", N), where N would be a count instead\n  // of a position.\n  template <std::size_t M>\n  constexpr BasicFixedString& append(\n      const BasicFixedString<Char, M>& that,\n      std::size_t pos) noexcept(false) = delete;\n\n  /**\n   * Appends `count` characters from another string to this one, starting at a\n   * given offset, `pos`.\n   * \\param that The source string.\n   * \\param pos The starting position in the source string.\n   * \\param count The number of characters to append. If `npos`, `count` is\n   *              taken to be `that.size()-pos`.\n   * \\pre `pos <= that.size()`\n   * \\pre `count <= that.size() - pos`\n   * \\pre `old_size + count <= N`\n   * \\post The first `old_size` characters of the string are unmodified.\n   * \\post `size() == old_size + count`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range if pos + count > that.size() or if\n   *        `old_size + count > N`.\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& append(\n      const BasicFixedString<Char, M>& that,\n      std::size_t pos,\n      std::size_t count) noexcept(false) {\n    detail::fixedstring::checkOverflow(pos, that.size_);\n    count = detail::fixedstring::checkOverflowOrNpos(count, that.size_ - pos);\n    detail::fixedstring::checkOverflow(count, N - size_);\n    for (std::size_t i = 0u; i < count; ++i) {\n      data_[size_ + i] = that.data_[pos + i];\n    }\n    size_ += count;\n    data_[size_] = Char(0);\n    return *this;\n  }\n\n  /**\n   * \\note Equivalent to `append(that, strlen(that))`.\n   */\n  constexpr BasicFixedString& append(const Char* that) noexcept(false) {\n    return append(that, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Appends `count` characters from the specified character array.\n   * \\pre `that` points to a range of at least `count` characters.\n   * \\pre `count + old_size <= N`\n   * \\post The first `old_size` characters of the string are unmodified.\n   * \\post `size() == old_size + count`\n   * \\post `at(size()) == Char(0)`\n   * \\throw std::out_of_range if old_size + count > N.\n   */\n  constexpr BasicFixedString& append(\n      const Char* that, std::size_t count) noexcept(false) {\n    detail::fixedstring::checkOverflow(count, N - size_);\n    for (std::size_t i = 0u; i < count; ++i) {\n      data_[size_ + i] = that[i];\n    }\n    size_ += count;\n    data_[size_] = Char(0);\n    return *this;\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Creates a new string by appending a character to an existing string, which\n   *   is left unmodified.\n   * \\note Equivalent to `*this + ch`\n   */\n  constexpr BasicFixedString<Char, N + 1u> cappend(Char ch) const noexcept {\n    return *this + ch;\n  }\n\n  /**\n   * Creates a new string by appending a string to an existing string, which\n   *   is left unmodified.\n   * \\note Equivalent to `*this + ch`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M> cappend(\n      const BasicFixedString<Char, M>& that) const noexcept {\n    return *this + that;\n  }\n\n  // Deleted to avoid confusion with append(\"char*\", N), where N is a count\n  // instead of a position.\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M> cappend(\n      const BasicFixedString<Char, M>& that, std::size_t pos) const\n      noexcept(false) = delete;\n\n  /**\n   * Creates a new string by appending characters from one string to another,\n   *   which is left unmodified.\n   * \\note Equivalent to `*this + that.substr(pos, count)`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M> cappend(\n      const BasicFixedString<Char, M>& that,\n      std::size_t pos,\n      std::size_t count) const noexcept(false) {\n    return creplace(size_, 0u, that, pos, count);\n  }\n\n  /**\n   * Creates a new string by appending a string literal to a string,\n   *   which is left unmodified.\n   * \\note Equivalent to `*this + that`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M - 1u> cappend(\n      const Char (&that)[M]) const noexcept {\n    return creplace(size_, 0u, that);\n  }\n\n  // Deleted to avoid confusion with append(\"char*\", N), where N is a count\n  // instead of a position\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M - 1u> cappend(\n      const Char (&that)[M], std::size_t pos) const noexcept(false) = delete;\n\n  /**\n   * Creates a new string by appending characters from one string to another,\n   *   which is left unmodified.\n   * \\note Equivalent to `*this + makeFixedString(that).substr(pos, count)`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M - 1u> cappend(\n      const Char (&that)[M], std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return creplace(size_, 0u, that, pos, count);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Appends characters from a null-terminated string literal to this string.\n   * \\note Equivalent to `append(that)`.\n   */\n  constexpr BasicFixedString& operator+=(const Char* that) noexcept(false) {\n    return append(that);\n  }\n\n  /**\n   * Appends characters from another string to this one.\n   * \\note Equivalent to `append(that)`.\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& operator+=(\n      const BasicFixedString<Char, M>& that) noexcept(false) {\n    return append(that, 0u, that.size_);\n  }\n\n  /**\n   * Appends a character to this string.\n   * \\note Equivalent to `push_back(ch)`.\n   */\n  constexpr BasicFixedString& operator+=(Char ch) noexcept(false) {\n    push_back(ch);\n    return *this;\n  }\n\n  /**\n   * Appends characters from an `initializer_list` to this string.\n   * \\note Equivalent to `append(il.begin(), il.size())`.\n   */\n  constexpr BasicFixedString& operator+=(\n      std::initializer_list<Char> il) noexcept(false) {\n    return append(il.begin(), il.size());\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Erase all characters from this string.\n   * \\note Equivalent to `clear()`\n   * \\return *this;\n   */\n  constexpr BasicFixedString& erase() noexcept {\n    clear();\n    return *this;\n  }\n\n  /**\n   * Erases `count` characters from position `pos`. If `count` is `npos`,\n   *   erases from `pos` to the end of the string.\n   * \\pre `pos <= size()`\n   * \\pre `count <= size() - pos || count == npos`\n   * \\post `size() == old_size - min(count, old_size - pos)`\n   * \\post `at(size()) == Char(0)`\n   * \\return *this;\n   * \\throw std::out_of_range when pos > size().\n   */\n  constexpr BasicFixedString& erase(\n      std::size_t pos, std::size_t count = npos) noexcept(false) {\n    using A = const Char[1];\n    constexpr A a{Char(0)};\n    return replace(\n        pos,\n        detail::fixedstring::checkOverflowOrNpos(\n            count, size_ - detail::fixedstring::checkOverflow(pos, size_)),\n        a,\n        0u);\n  }\n\n  /**\n   * \\note Equivalent to `erase(first - data(), 1)`\n   * \\return A pointer to the first character after the erased character.\n   */\n  constexpr Char* erase(const Char* first) noexcept(false) {\n    erase(first - data_, 1u);\n    return data_ + (first - data_);\n  }\n\n  /**\n   * \\note Equivalent to `erase(first - data(), last - first)`\n   * \\return A pointer to the first character after the erased characters.\n   */\n  constexpr Char* erase(const Char* first, const Char* last) noexcept(false) {\n    erase(first - data_, last - first);\n    return data_ + (first - data_);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Create a new string by erasing all the characters from this string.\n   * \\note Equivalent to `BasicFixedString<Char, 0>{}`\n   */\n  constexpr BasicFixedString<Char, 0u> cerase() const noexcept { return {}; }\n\n  /**\n   * Create a new string by erasing all the characters after position `pos` from\n   *   this string.\n   * \\note Equivalent to `creplace(pos, min(count, pos - size()), \"\")`\n   */\n  constexpr BasicFixedString cerase(\n      std::size_t pos, std::size_t count = npos) const noexcept(false) {\n    using A = const Char[1];\n    return creplace(\n        pos,\n        detail::fixedstring::checkOverflowOrNpos(\n            count, size_ - detail::fixedstring::checkOverflow(pos, size_)),\n        A{Char(0)});\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Compare two strings for lexicographical ordering.\n   * \\note Equivalent to\n   * `compare(0, size(), that.data(), that.size())`\n   */\n  template <std::size_t M>\n  constexpr int compare(const BasicFixedString<Char, M>& that) const noexcept {\n    return compare(0u, size_, that, 0u, that.size_);\n  }\n\n  /**\n   * Compare two strings for lexicographical ordering.\n   * \\note Equivalent to\n   * `compare(this_pos, this_count, that.data(), that.size())`\n   */\n  template <std::size_t M>\n  constexpr int compare(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const BasicFixedString<Char, M>& that) const noexcept(false) {\n    return compare(this_pos, this_count, that, 0u, that.size_);\n  }\n\n  /**\n   * Compare two strings for lexicographical ordering.\n   * \\note Equivalent to\n   * `compare(this_pos, this_count, that.data() + that_pos, that_count)`\n   */\n  template <std::size_t M>\n  constexpr int compare(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const BasicFixedString<Char, M>& that,\n      std::size_t that_pos,\n      std::size_t that_count) const noexcept(false) {\n    return static_cast<int>(detail::fixedstring::compare_(\n        data_,\n        detail::fixedstring::checkOverflow(this_pos, size_),\n        detail::fixedstring::checkOverflow(this_count, size_ - this_pos) +\n            this_pos,\n        that.data_,\n        detail::fixedstring::checkOverflow(that_pos, that.size_),\n        detail::fixedstring::checkOverflow(that_count, that.size_ - that_pos) +\n            that_pos));\n  }\n\n  /**\n   * Compare two strings for lexicographical ordering.\n   * \\note Equivalent to `compare(0, size(), that, strlen(that))`\n   */\n  constexpr int compare(const Char* that) const noexcept {\n    return compare(0u, size_, that, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * \\overload\n   */\n  constexpr int compare(Range<const Char*> that) const noexcept {\n    return compare(0u, size_, that.begin(), that.size());\n  }\n\n  /**\n   * Compare two strings for lexicographical ordering.\n   * \\note Equivalent to\n   *   `compare(this_pos, this_count, that, strlen(that))`\n   */\n  constexpr int compare(\n      std::size_t this_pos, std::size_t this_count, const Char* that) const\n      noexcept(false) {\n    return compare(this_pos, this_count, that, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * \\overload\n   */\n  constexpr int compare(\n      std::size_t this_pos,\n      std::size_t this_count,\n      Range<const Char*> that) const noexcept(false) {\n    return compare(this_pos, this_count, that.begin(), that.size());\n  }\n\n  /**\n   * Compare two strings for lexicographical ordering.\n   *\n   * Let `A` be the\n   *   character sequence {`(*this)[this_pos]`, ...\n   *   `(*this)[this_pos + this_count - 1]`}. Let `B` be the character sequence\n   *   {`that[0]`, ...`that[count - 1]`}. Then...\n   *\n   * \\return\n   *   - `< 0` if `A` is ordered before the `B`\n   *   - `> 0` if `B` is ordered before `A`\n   *   - `0` if `A` equals `B`.\n   *\n   * \\throw std::out_of_range if this_pos + this_count > size().\n   */\n  constexpr int compare(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const Char* that,\n      std::size_t that_count) const noexcept(false) {\n    return static_cast<int>(detail::fixedstring::compare_(\n        data_,\n        detail::fixedstring::checkOverflow(this_pos, size_),\n        detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos) +\n            this_pos,\n        that,\n        0u,\n        that_count));\n  }\n\n  constexpr int compare(\n      std::size_t this_pos,\n      std::size_t this_count,\n      Range<const Char*> that,\n      std::size_t that_count) const noexcept(false) {\n    return compare(\n        this_pos,\n        this_count,\n        that.begin(),\n        detail::fixedstring::checkOverflow(that_count, that.size()));\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Return a substring from `pos` to the end of the string.\n   * \\note Equivalent to `BasicFixedString{*this, pos}`\n   */\n  constexpr BasicFixedString substr(std::size_t pos) const noexcept(false) {\n    return {*this, pos};\n  }\n\n  /**\n   * Return a substring from `pos` to the end of the string.\n   * \\note Equivalent to `BasicFixedString{*this, pos, count}`\n   */\n  constexpr BasicFixedString substr(std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return {*this, pos, count};\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Replace the characters in the range denoted by the half-open range\n   *   [`first`, `last`) with the string `that`.\n   * \\pre `first` and `last` point to characters within this string (including\n   *   the terminating null).\n   * \\note Equivalent to\n   *   `replace(first - data(), last - first, that.data(), that.size())`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& replace(\n      const Char* first,\n      const Char* last,\n      const BasicFixedString<Char, M>& that) noexcept(false) {\n    return replace(first - data_, last - first, that, 0u, that.size_);\n  }\n\n  /**\n   * Replace `this_count` characters starting from position `this_pos` with the\n   *   characters from string `that` starting at position `that_pos`.\n   * \\pre `that_pos <= that.size()`\n   * \\note Equivalent to\n   *   <tt>replace(this_pos, this_count, that.data() + that_pos,\n   *   that.size() - that_pos)</tt>\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& replace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const BasicFixedString<Char, M>& that,\n      std::size_t that_pos = 0u) noexcept(false) {\n    return replace(this_pos, this_count, that, that_pos, that.size_ - that_pos);\n  }\n\n  /**\n   * Replace `this_count` characters starting from position `this_pos` with\n   *   `that_count` characters from string `that` starting at position\n   *   `that_pos`.\n   * \\pre `that_pos <= that.size() && that_count <= that.size() - that_pos`\n   * \\note Equivalent to\n   *   `replace(this_pos, this_count, that.data() + that_pos, that_count)`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString& replace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const BasicFixedString<Char, M>& that,\n      std::size_t that_pos,\n      std::size_t that_count) noexcept(false) {\n    return *this = creplace(this_pos, this_count, that, that_pos, that_count);\n  }\n\n  /**\n   * Replace `this_count` characters starting from position `this_pos` with\n   *   the characters from the string literal `that`.\n   * \\note Equivalent to\n   *   `replace(this_pos, this_count, that, strlen(that))`\n   */\n  constexpr BasicFixedString& replace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const Char* that) noexcept(false) {\n    return replace(this_pos, this_count, that, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Replace the characters denoted by the half-open range [`first`,`last`) with\n   *   the characters from the string literal `that`.\n   * \\pre `first` and `last` point to characters within this string (including\n   *   the terminating null).\n   * \\note Equivalent to\n   *   `replace(first - data(), last - first, that, strlen(that))`\n   */\n  constexpr BasicFixedString& replace(\n      const Char* first, const Char* last, const Char* that) noexcept(false) {\n    return replace(\n        first - data_, last - first, that, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Replace `this_count` characters starting from position `this_pos` with\n   *   `that_count` characters from the character sequence pointed to by `that`.\n   * \\param this_pos The starting offset within `*this` of the first character\n   *   to be replaced.\n   * \\param this_count The number of characters to be replaced. If `npos`,\n   *   it is treated as if `this_count` were `size() - this_pos`.\n   * \\param that A pointer to the replacement string.\n   * \\param that_count The number of characters in the replacement string.\n   * \\pre `this_pos <= size() && this_count <= size() - this_pos`\n   * \\pre `that` points to a contiguous sequence of at least `that_count`\n   *   characters\n   * \\throw std::out_of_range on any of the following conditions:\n   *   - `this_pos > size()`\n   *   - `this_count > size() - this_pos`\n   *   - `size() - this_count + that_count > N`\n   */\n  constexpr BasicFixedString& replace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const Char* that,\n      std::size_t that_count) noexcept(false) {\n    return *this = detail::fixedstring::Helper::replace_<Char>(\n               data_,\n               size_,\n               detail::fixedstring::checkOverflow(this_pos, size_),\n               detail::fixedstring::checkOverflowOrNpos(\n                   this_count, size_ - this_pos),\n               that,\n               0u,\n               that_count,\n               Indices{});\n  }\n\n  /**\n   * Replace `this_count` characters starting from position `this_pos` with\n   *   `that_count` characters `ch`.\n   * \\note Equivalent to\n   *   `replace(this_pos, this_count, BasicFixedString{that_count, ch})`\n   */\n  constexpr BasicFixedString& replace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      std::size_t that_count,\n      Char ch) noexcept(false) {\n    return replace(this_pos, this_count, BasicFixedString{that_count, ch});\n  }\n\n  /**\n   * Replace the characters denoted by the half-open range [`first`,`last`)\n   *   with `that_count` characters `ch`.\n   * \\note Equivalent to\n   *   `replace(first - data(), last - first, BasicFixedString{that_count, ch})`\n   */\n  constexpr BasicFixedString& replace(\n      const Char* first,\n      const Char* last,\n      std::size_t that_count,\n      Char ch) noexcept(false) {\n    return replace(\n        first - data_, last - first, BasicFixedString{that_count, ch});\n  }\n\n  /**\n   * Replace the characters denoted by the half-open range [`first`,`last`) with\n   *   the characters from the string literal `that`.\n   * \\pre `first` and `last` point to characters within this string (including\n   *   the terminating null).\n   * \\note Equivalent to\n   *   `replace(this_pos, this_count, il.begin(), il.size())`\n   */\n  constexpr BasicFixedString& replace(\n      const Char* first,\n      const Char* last,\n      std::initializer_list<Char> il) noexcept(false) {\n    return replace(first - data_, last - first, il.begin(), il.size());\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Construct a new string by replacing `this_count` characters starting from\n   *   position `this_pos` within this string with the characters from string\n   *   `that` starting at position `that_pos`.\n   * \\pre `that_pos <= that.size()`\n   * \\note Equivalent to\n   *   <tt>creplace(this_pos, this_count, that, that_pos,\n   *   that.size() - that_pos)</tt>\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M> creplace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const BasicFixedString<Char, M>& that,\n      std::size_t that_pos = 0u) const noexcept(false) {\n    return creplace(\n        this_pos,\n        this_count,\n        that,\n        that_pos,\n        that.size_ - detail::fixedstring::checkOverflow(that_pos, that.size_));\n  }\n\n  /**\n   * Construct a new string by replacing `this_count` characters starting from\n   *   position `this_pos` within this string with `that_count` characters from\n   *   string `that` starting at position `that_pos`.\n   * \\param this_pos The starting offset within `*this` of the first character\n   *   to be replaced.\n   * \\param this_count The number of characters to be replaced. If `npos`,\n   *   it is treated as if `this_count` were `size() - this_pos`.\n   * \\param that A string that contains the replacement string.\n   * \\param that_pos The offset to the first character in the replacement\n   *   string.\n   * \\param that_count The number of characters in the replacement string.\n   * \\pre `this_pos <= size() && this_count <= size() - this_pos`\n   * \\pre `that_pos <= that.size() && that_count <= that.size() - that_pos`\n   * \\post The size of the returned string is `size() - this_count + that_count`\n   * \\note Equivalent to <tt>BasicFixedString<Char, N + M>{substr(0, this_pos) +\n   *    that.substr(that_pos, that_count) + substr(this_pos + this_count)}</tt>\n   * \\throw std::out_of_range on any of the following conditions:\n   *   - `this_pos > size()`\n   *   - `this_count > size() - this_pos`\n   *   - `that_pos > that.size()`\n   *   - `that_count > that.size() - that_pos`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M> creplace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const BasicFixedString<Char, M>& that,\n      std::size_t that_pos,\n      std::size_t that_count) const noexcept(false) {\n    return detail::fixedstring::Helper::replace_<Char>(\n        data_,\n        size_,\n        detail::fixedstring::checkOverflow(this_pos, size_),\n        detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos),\n        that.data_,\n        detail::fixedstring::checkOverflow(that_pos, that.size_),\n        detail::fixedstring::checkOverflowOrNpos(\n            that_count, that.size_ - that_pos),\n        std::make_index_sequence<N + M>{});\n  }\n\n  /**\n   * Construct a new string by replacing the characters denoted by the half-open\n   *   range [`first`,`last`) within this string with the characters from string\n   *   `that` starting at position `that_pos`.\n   * \\pre `that_pos <= that.size()`\n   * \\note Equivalent to\n   *   <tt>creplace(first - data(), last - first, that, that_pos,\n   *   that.size() - that_pos)</tt>\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M> creplace(\n      const Char* first,\n      const Char* last,\n      const BasicFixedString<Char, M>& that,\n      std::size_t that_pos = 0u) const noexcept(false) {\n    return creplace(\n        first - data_,\n        last - first,\n        that,\n        that_pos,\n        that.size_ - detail::fixedstring::checkOverflow(that_pos, that.size_));\n  }\n\n  /**\n   * Construct a new string by replacing the characters denoted by the half-open\n   *   range [`first`,`last`) within this string with the `that_count`\n   *   characters from string `that` starting at position `that_pos`.\n   * \\note Equivalent to\n   *   <tt>creplace(first - data(), last - first, that, that_pos,\n   *   that_count)</tt>\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M> creplace(\n      const Char* first,\n      const Char* last,\n      const BasicFixedString<Char, M>& that,\n      std::size_t that_pos,\n      std::size_t that_count) const noexcept(false) {\n    return creplace(first - data_, last - first, that, that_pos, that_count);\n  }\n\n  /**\n   * Construct a new string by replacing `this_count` characters starting from\n   *   position `this_pos` within this string with `M-1` characters from\n   *   character array `that`.\n   * \\pre `strlen(that) == M-1`\n   * \\note Equivalent to\n   *   <tt>creplace(this_pos, this_count, that, 0, M - 1)</tt>\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M - 1u> creplace(\n      std::size_t this_pos, std::size_t this_count, const Char (&that)[M]) const\n      noexcept(false) {\n    return creplace(this_pos, this_count, that, 0u, M - 1u);\n  }\n\n  /**\n   * Replace `this_count` characters starting from position `this_pos` with\n   *   `that_count` characters from the character array `that` starting at\n   *   position `that_pos`.\n   * \\param this_pos The starting offset within `*this` of the first character\n   *   to be replaced.\n   * \\param this_count The number of characters to be replaced. If `npos`,\n   *   it is treated as if `this_count` were `size() - this_pos`.\n   * \\param that An array of characters containing the replacement string.\n   * \\param that_pos The starting offset of the replacement string.\n   * \\param that_count The number of characters in the replacement string.  If\n   *   `npos`, it is treated as if `that_count` were `M - 1 - that_pos`\n   * \\pre `this_pos <= size() && this_count <= size() - this_pos`\n   * \\pre `that_pos <= M - 1 && that_count <= M - 1 - that_pos`\n   * \\post The size of the returned string is `size() - this_count + that_count`\n   * \\note Equivalent to <tt>BasicFixedString<Char, N + M - 1>{\n   *    substr(0, this_pos) +\n   *    makeFixedString(that).substr(that_pos, that_count) +\n   *    substr(this_pos + this_count)}</tt>\n   * \\throw std::out_of_range on any of the following conditions:\n   *   - `this_pos > size()`\n   *   - `this_count > size() - this_pos`\n   *   - `that_pos >= M`\n   *   - `that_count >= M - that_pos`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M - 1u> creplace(\n      std::size_t this_pos,\n      std::size_t this_count,\n      const Char (&that)[M],\n      std::size_t that_pos,\n      std::size_t that_count) const noexcept(false) {\n    return detail::fixedstring::Helper::replace_<Char>(\n        data_,\n        size_,\n        detail::fixedstring::checkOverflow(this_pos, size_),\n        detail::fixedstring::checkOverflowOrNpos(this_count, size_ - this_pos),\n        detail::fixedstring::checkNullTerminated(that),\n        detail::fixedstring::checkOverflow(that_pos, M - 1u),\n        detail::fixedstring::checkOverflowOrNpos(that_count, M - 1u - that_pos),\n        std::make_index_sequence<N + M - 1u>{});\n  }\n\n  /**\n   * Construct a new string by replacing the characters denoted by the half-open\n   *   range [`first`,`last`) within this string with the first `M-1`\n   *   characters from the character array `that`.\n   * \\pre `strlen(that) == M-1`\n   * \\note Equivalent to\n   *   <tt>creplace(first - data(), last - first, that, 0, M-1)</tt>\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M - 1u> creplace(\n      const Char* first, const Char* last, const Char (&that)[M]) const\n      noexcept(false) {\n    return creplace(first - data_, last - first, that, 0u, M - 1u);\n  }\n\n  /**\n   * Construct a new string by replacing the characters denoted by the half-open\n   *   range [`first`,`last`) within this string with the `that_count`\n   *   characters from the character array `that` starting at position\n   *   `that_pos`.\n   * \\pre `strlen(that) == M-1`\n   * \\note Equivalent to\n   *   `creplace(first - data(), last - first, that, that_pos, that_count)`\n   */\n  template <std::size_t M>\n  constexpr BasicFixedString<Char, N + M - 1u> creplace(\n      const Char* first,\n      const Char* last,\n      const Char (&that)[M],\n      std::size_t that_pos,\n      std::size_t that_count) const noexcept(false) {\n    return creplace(first - data_, last - first, that, that_pos, that_count);\n  }\n\n  /**\n   * Copies `min(count, size())` characters starting from offset `0`\n   *   from this string into the buffer pointed to by `dest`.\n   * \\return The number of characters copied.\n   */\n  constexpr std::size_t copy(Char* dest, std::size_t count) const noexcept {\n    return copy(dest, count, 0u);\n  }\n\n  /**\n   * Copies `min(count, size() - pos)` characters starting from offset `pos`\n   *   from this string into the buffer pointed to by `dest`.\n   * \\pre `pos <= size()`\n   * \\return The number of characters copied.\n   * \\throw std::out_of_range if `pos > size()`\n   */\n  constexpr std::size_t copy(\n      Char* dest, std::size_t count, std::size_t pos) const noexcept(false) {\n    detail::fixedstring::checkOverflow(pos, size_);\n    for (std::size_t i = 0u; i < count; ++i) {\n      if (i + pos == size_) {\n        return size_;\n      }\n      dest[i] = data_[i + pos];\n    }\n    return count;\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Resizes the current string.\n   * \\note Equivalent to `resize(count, Char(0))`\n   */\n  constexpr void resize(std::size_t count) noexcept(false) {\n    resize(count, Char(0));\n  }\n\n  /**\n   * Resizes the current string by setting the size to `count` and setting\n   *   `data()[count]` to `Char(0)`. If `count > old_size`, the characters\n   *   in the range [`old_size`,`count`) are set to `ch`.\n   */\n  constexpr void resize(std::size_t count, Char ch) noexcept(false) {\n    detail::fixedstring::checkOverflow(count, N);\n    if (count == size_) {\n    } else if (count < size_) {\n      size_ = count;\n      data_[size_] = Char(0);\n    } else {\n      for (; size_ < count; ++size_) {\n        data_[size_] = ch;\n      }\n      data_[size_] = Char(0);\n    }\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Finds the first occurrence of the character sequence `that` in this string.\n   * \\note Equivalent to `find(that.data(), 0, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find(\n      const BasicFixedString<Char, M>& that) const noexcept {\n    return find(that, 0u);\n  }\n\n  /**\n   * Finds the first occurrence of the character sequence `that` in this string,\n   *   starting at offset `pos`.\n   * \\pre `pos <= size()`\n   * \\note Equivalent to `find(that.data(), pos, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find(\n      const BasicFixedString<Char, M>& that, std::size_t pos) const\n      noexcept(false) {\n    return that.size_ <= size_ - detail::fixedstring::checkOverflow(pos, size_)\n        ? detail::fixedstring::find_(data_, size_, that.data_, pos, that.size_)\n        : npos;\n  }\n\n  /**\n   * Finds the first occurrence of the character sequence `that` in this string.\n   * \\note Equivalent to `find(that.data(), 0, strlen(that))`\n   */\n  constexpr std::size_t find(const Char* that) const noexcept {\n    return find(that, 0u, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the first occurrence of the character sequence `that` in this string,\n   *   starting at offset `pos`.\n   * \\pre `pos <= size()`\n   * \\note Equivalent to `find(that.data(), pos, strlen(that))`\n   */\n  constexpr std::size_t find(const Char* that, std::size_t pos) const\n      noexcept(false) {\n    return find(that, pos, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the first occurrence of the first `count` characters in the buffer\n   *   pointed to by `that` in this string, starting at offset `pos`.\n   * \\pre `pos <= size()`\n   * \\pre `that` points to a buffer containing at least `count` contiguous\n   *   characters.\n   * \\return The lowest offset `i` such that `i >= pos` and\n   *   `0 == strncmp(data() + i, that, count)`; or `npos` if there is no such\n   *   offset `i`.\n   * \\throw std::out_of_range when `pos > size()`\n   */\n  constexpr std::size_t find(\n      const Char* that, std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return count <= size_ - detail::fixedstring::checkOverflow(pos, size_)\n        ? detail::fixedstring::find_(data_, size_, that, pos, count)\n        : npos;\n  }\n\n  /**\n   * Finds the first occurrence of the character `ch` in this string.\n   * \\note Equivalent to `find(&ch, 0, 1)`\n   */\n  constexpr std::size_t find(Char ch) const noexcept { return find(ch, 0u); }\n\n  /**\n   * Finds the first occurrence of the character character `c` in this string,\n   *   starting at offset `pos`.\n   * \\pre `pos <= size()`\n   * \\note Equivalent to `find(&ch, pos, 1)`\n   */\n  constexpr std::size_t find(Char ch, std::size_t pos) const noexcept(false) {\n    using A = const Char[1u];\n    return 0u == size_ - detail::fixedstring::checkOverflow(pos, size_)\n        ? npos\n        : detail::fixedstring::find_(data_, size_, A{ch}, pos, 1u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Finds the last occurrence of characters in the string\n   *   `that` in this string.\n   * \\note Equivalent to `rfind(that.data(), size(), that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t rfind(\n      const BasicFixedString<Char, M>& that) const noexcept {\n    return rfind(that, size_);\n  }\n\n  /**\n   * Finds the last occurrence of characters in the string\n   *   `that` in this string, starting at offset `pos`.\n   * \\note Equivalent to `rfind(that.data(), pos, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t rfind(\n      const BasicFixedString<Char, M>& that, std::size_t pos) const\n      noexcept(false) {\n    return that.size_ <= size_\n        ? detail::fixedstring::rfind_(\n              data_,\n              that.data_,\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_),\n                  size_ - that.size_),\n              that.size_)\n        : npos;\n  }\n\n  /**\n   * Finds the last occurrence of characters in the buffer\n   *   pointed to by `that` in this string.\n   * \\note Equivalent to `rfind(that, size(), strlen(that))`\n   */\n  constexpr std::size_t rfind(const Char* that) const noexcept {\n    return rfind(that, size_, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the last occurrence of characters in the buffer\n   *   pointed to by `that` in this string, starting at offset `pos`.\n   * \\note Equivalent to `rfind(that, pos, strlen(that))`\n   */\n  constexpr std::size_t rfind(const Char* that, std::size_t pos) const\n      noexcept(false) {\n    return rfind(that, pos, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the last occurrence of the first `count` characters in the buffer\n   *   pointed to by `that` in this string, starting at offset `pos`.\n   * \\pre `pos <= size()`\n   * \\pre `that` points to a buffer containing at least `count` contiguous\n   *   characters.\n   * \\return The largest offset `i` such that `i <= pos` and\n   *   `i + count <= size()` and `0 == strncmp(data() + i, that, count)`; or\n   *   `npos` if there is no such offset `i`.\n   * \\throw std::out_of_range when `pos > size()`\n   */\n  constexpr std::size_t rfind(\n      const Char* that, std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return count <= size_\n        ? detail::fixedstring::rfind_(\n              data_,\n              that,\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_),\n                  size_ - count),\n              count)\n        : npos;\n  }\n\n  /**\n   * Finds the last occurrence of the character character `ch` in this string.\n   * \\note Equivalent to `rfind(&ch, size(), 1)`\n   */\n  constexpr std::size_t rfind(Char ch) const noexcept {\n    return rfind(ch, size_);\n  }\n\n  /**\n   * Finds the last occurrence of the character character `ch` in this string,\n   *   starting at offset `pos`.\n   * \\pre `pos <= size()`\n   * \\note Equivalent to `rfind(&ch, pos, 1)`\n   */\n  constexpr std::size_t rfind(Char ch, std::size_t pos) const noexcept(false) {\n    using A = const Char[1u];\n    return 0u == size_\n        ? npos\n        : detail::fixedstring::rfind_(\n              data_,\n              A{ch},\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),\n              1u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Finds the first occurrence of any character in `that` in this string.\n   * \\note Equivalent to `find_first_of(that.data(), 0, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_first_of(\n      const BasicFixedString<Char, M>& that) const noexcept {\n    return find_first_of(that, 0u);\n  }\n\n  /**\n   * Finds the first occurrence of any character in `that` in this string,\n   *   starting at offset `pos`\n   * \\note Equivalent to `find_first_of(that.data(), pos, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_first_of(\n      const BasicFixedString<Char, M>& that, std::size_t pos) const\n      noexcept(false) {\n    return size_ == detail::fixedstring::checkOverflow(pos, size_)\n        ? npos\n        : detail::fixedstring::find_first_of_(\n              data_, size_, that.data_, pos, that.size_);\n  }\n\n  /**\n   * Finds the first occurrence of any character in the null-terminated\n   *   character sequence pointed to by `that` in this string.\n   * \\note Equivalent to `find_first_of(that, 0, strlen(that))`\n   */\n  constexpr std::size_t find_first_of(const Char* that) const noexcept {\n    return find_first_of(that, 0u, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the first occurrence of any character in the null-terminated\n   *   character sequence pointed to by `that` in this string,\n   *   starting at offset `pos`\n   * \\note Equivalent to `find_first_of(that, pos, strlen(that))`\n   */\n  constexpr std::size_t find_first_of(const Char* that, std::size_t pos) const\n      noexcept(false) {\n    return find_first_of(that, pos, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the first occurrence of any character in the first `count` characters\n   *   in the buffer pointed to by `that` in this string, starting at offset\n   *  `pos`.\n   * \\pre `pos <= size()`\n   * \\pre `that` points to a buffer containing at least `count` contiguous\n   *   characters.\n   * \\return The smallest offset `i` such that `i >= pos` and\n   *   `std::find(that, that+count, at(i)) != that+count`; or\n   *   `npos` if there is no such offset `i`.\n   * \\throw std::out_of_range when `pos > size()`\n   */\n  constexpr std::size_t find_first_of(\n      const Char* that, std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return size_ == detail::fixedstring::checkOverflow(pos, size_)\n        ? npos\n        : detail::fixedstring::find_first_of_(data_, size_, that, pos, count);\n  }\n\n  /**\n   * Finds the first occurrence of `ch` in this string.\n   * \\note Equivalent to `find_first_of(&ch, 0, 1)`\n   */\n  constexpr std::size_t find_first_of(Char ch) const noexcept {\n    return find_first_of(ch, 0u);\n  }\n\n  /**\n   * Finds the first occurrence of `ch` in this string,\n   *   starting at offset `pos`.\n   * \\note Equivalent to `find_first_of(&ch, pos, 1)`\n   */\n  constexpr std::size_t find_first_of(Char ch, std::size_t pos) const\n      noexcept(false) {\n    using A = const Char[1u];\n    return size_ == detail::fixedstring::checkOverflow(pos, size_)\n        ? npos\n        : detail::fixedstring::find_first_of_(data_, size_, A{ch}, pos, 1u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Finds the first occurrence of any character not in `that` in this string.\n   * \\note Equivalent to `find_first_not_of(that.data(), 0, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_first_not_of(\n      const BasicFixedString<Char, M>& that) const noexcept {\n    return find_first_not_of(that, 0u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Finds the first occurrence of any character not in `that` in this string.\n   * \\note Equivalent to `find_first_not_of(that.data(), 0, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_first_not_of(\n      const BasicFixedString<Char, M>& that, std::size_t pos) const\n      noexcept(false) {\n    return size_ == detail::fixedstring::checkOverflow(pos, size_)\n        ? npos\n        : detail::fixedstring::find_first_not_of_(\n              data_, size_, that.data_, pos, that.size_);\n  }\n\n  /**\n   * Finds the first occurrence of any character not in the null-terminated\n   *   character sequence pointed to by `that` in this string.\n   * \\note Equivalent to `find_first_not_of(that, 0, strlen(that))`\n   */\n  constexpr std::size_t find_first_not_of(const Char* that) const noexcept {\n    return find_first_not_of(that, 0u, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the first occurrence of any character not in the null-terminated\n   *   character sequence pointed to by `that` in this string,\n   *   starting at offset `pos`\n   * \\note Equivalent to `find_first_not_of(that, pos, strlen(that))`\n   */\n  constexpr std::size_t find_first_not_of(\n      const Char* that, std::size_t pos) const noexcept(false) {\n    return find_first_not_of(that, pos, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the first occurrence of any character not in the first `count`\n   *   characters in the buffer pointed to by `that` in this string, starting at\n   *   offset `pos`.\n   * \\pre `pos <= size()`\n   * \\pre `that` points to a buffer containing at least `count` contiguous\n   *   characters.\n   * \\return The smallest offset `i` such that `i >= pos` and\n   *   `std::find(that, that+count, at(i)) == that+count`; or\n   *   `npos` if there is no such offset `i`.\n   * \\throw std::out_of_range when `pos > size()`\n   */\n  constexpr std::size_t find_first_not_of(\n      const Char* that, std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return size_ == detail::fixedstring::checkOverflow(pos, size_)\n        ? npos\n        : detail::fixedstring::find_first_not_of_(\n              data_, size_, that, pos, count);\n  }\n\n  /**\n   * Finds the first occurrence of any character other than `ch` in this string.\n   * \\note Equivalent to `find_first_not_of(&ch, 0, 1)`\n   */\n  constexpr std::size_t find_first_not_of(Char ch) const noexcept {\n    return find_first_not_of(ch, 0u);\n  }\n\n  /**\n   * Finds the first occurrence of any character other than `ch` in this string,\n   *   starting at offset `pos`.\n   * \\note Equivalent to `find_first_not_of(&ch, pos, 1)`\n   */\n  constexpr std::size_t find_first_not_of(Char ch, std::size_t pos) const\n      noexcept(false) {\n    using A = const Char[1u];\n    return 1u <= size_ - detail::fixedstring::checkOverflow(pos, size_)\n        ? detail::fixedstring::find_first_not_of_(data_, size_, A{ch}, pos, 1u)\n        : npos;\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Finds the last occurrence of any character in `that` in this string.\n   * \\note Equivalent to `find_last_of(that.data(), size(), that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_last_of(\n      const BasicFixedString<Char, M>& that) const noexcept {\n    return find_last_of(that, size_);\n  }\n\n  /**\n   * Finds the last occurrence of any character in `that` in this string,\n   *   starting at offset `pos`\n   * \\note Equivalent to `find_last_of(that.data(), pos, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_last_of(\n      const BasicFixedString<Char, M>& that, std::size_t pos) const\n      noexcept(false) {\n    return 0u == size_\n        ? npos\n        : detail::fixedstring::find_last_of_(\n              data_,\n              that.data_,\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),\n              that.size_);\n  }\n\n  /**\n   * Finds the last occurrence of any character in the null-terminated\n   *   character sequence pointed to by `that` in this string.\n   * \\note Equivalent to `find_last_of(that, size(), strlen(that))`\n   */\n  constexpr std::size_t find_last_of(const Char* that) const noexcept {\n    return find_last_of(that, size_, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the last occurrence of any character in the null-terminated\n   *   character sequence pointed to by `that` in this string,\n   *   starting at offset `pos`\n   * \\note Equivalent to `find_last_of(that, pos, strlen(that))`\n   */\n  constexpr std::size_t find_last_of(const Char* that, std::size_t pos) const\n      noexcept(false) {\n    return find_last_of(that, pos, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the last occurrence of any character in the first `count` characters\n   *   in the buffer pointed to by `that` in this string, starting at offset\n   *  `pos`.\n   * \\pre `pos <= size()`\n   * \\pre `that` points to a buffer containing at least `count` contiguous\n   *   characters.\n   * \\return The largest offset `i` such that `i <= pos` and\n   *   `i < size()` and `std::find(that, that+count, at(i)) != that+count`; or\n   *   `npos` if there is no such offset `i`.\n   * \\throw std::out_of_range when `pos > size()`\n   */\n  constexpr std::size_t find_last_of(\n      const Char* that, std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return 0u == size_\n        ? npos\n        : detail::fixedstring::find_last_of_(\n              data_,\n              that,\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),\n              count);\n  }\n\n  /**\n   * Finds the last occurrence of `ch` in this string.\n   * \\note Equivalent to `find_last_of(&ch, size(), 1)`\n   */\n  constexpr std::size_t find_last_of(Char ch) const noexcept {\n    return find_last_of(ch, size_);\n  }\n\n  /**\n   * Finds the last occurrence of `ch` in this string,\n   *   starting at offset `pos`.\n   * \\note Equivalent to `find_last_of(&ch, pos, 1)`\n   */\n  constexpr std::size_t find_last_of(Char ch, std::size_t pos) const\n      noexcept(false) {\n    using A = const Char[1u];\n    return 0u == size_\n        ? npos\n        : detail::fixedstring::find_last_of_(\n              data_,\n              A{ch},\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),\n              1u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Finds the last occurrence of any character not in `that` in this string.\n   * \\note Equivalent to `find_last_not_of(that.data(), size(), that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_last_not_of(\n      const BasicFixedString<Char, M>& that) const noexcept {\n    return find_last_not_of(that, size_);\n  }\n\n  /**\n   * Finds the last occurrence of any character not in `that` in this string,\n   *   starting at offset `pos`\n   * \\note Equivalent to `find_last_not_of(that.data(), pos, that.size())`\n   */\n  template <std::size_t M>\n  constexpr std::size_t find_last_not_of(\n      const BasicFixedString<Char, M>& that, std::size_t pos) const\n      noexcept(false) {\n    return 0u == size_\n        ? npos\n        : detail::fixedstring::find_last_not_of_(\n              data_,\n              that.data_,\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),\n              that.size_);\n  }\n\n  /**\n   * Finds the last occurrence of any character not in the null-terminated\n   *   character sequence pointed to by `that` in this string.\n   * \\note Equivalent to `find_last_not_of(that, size(), strlen(that))`\n   */\n  constexpr std::size_t find_last_not_of(const Char* that) const noexcept {\n    return find_last_not_of(that, size_, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the last occurrence of any character not in the null-terminated\n   *   character sequence pointed to by `that` in this string,\n   *   starting at offset `pos`\n   * \\note Equivalent to `find_last_not_of(that, pos, strlen(that))`\n   */\n  constexpr std::size_t find_last_not_of(\n      const Char* that, std::size_t pos) const noexcept(false) {\n    return find_last_not_of(that, pos, folly::constexpr_strlen(that));\n  }\n\n  /**\n   * Finds the last occurrence of any character not in the first `count`\n   *   characters in the buffer pointed to by `that` in this string, starting at\n   *   offset `pos`.\n   * \\pre `pos <= size()`\n   * \\pre `that` points to a buffer containing at least `count` contiguous\n   *   characters.\n   * \\return The largest offset `i` such that `i <= pos` and\n   *   `i < size()` and `std::find(that, that+count, at(i)) == that+count`; or\n   *   `npos` if there is no such offset `i`.\n   * \\throw std::out_of_range when `pos > size()`\n   */\n  constexpr std::size_t find_last_not_of(\n      const Char* that, std::size_t pos, std::size_t count) const\n      noexcept(false) {\n    return 0u == size_\n        ? npos\n        : detail::fixedstring::find_last_not_of_(\n              data_,\n              that,\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),\n              count);\n  }\n\n  /**\n   * Finds the last occurrence of any character other than `ch` in this string.\n   * \\note Equivalent to `find_last_not_of(&ch, size(), 1)`\n   */\n  constexpr std::size_t find_last_not_of(Char ch) const noexcept {\n    return find_last_not_of(ch, size_);\n  }\n\n  /**\n   * Finds the last occurrence of any character other than `ch` in this string,\n   *   starting at offset `pos`.\n   * \\note Equivalent to `find_last_not_of(&ch, pos, 1)`\n   */\n  constexpr std::size_t find_last_not_of(Char ch, std::size_t pos) const\n      noexcept(false) {\n    using A = const Char[1u];\n    return 0u == size_\n        ? npos\n        : detail::fixedstring::find_last_not_of_(\n              data_,\n              A{ch},\n              folly::constexpr_min(\n                  detail::fixedstring::checkOverflow(pos, size_), size_ - 1u),\n              1u);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Asymmetric relational operators\n   */\n  friend constexpr bool operator==(\n      const Char* a, const BasicFixedString& b) noexcept {\n    return detail::fixedstring::equal_(\n        a, folly::constexpr_strlen(a), b.data_, b.size_);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator==(\n      const BasicFixedString& a, const Char* b) noexcept {\n    return b == a;\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator==(\n      Range<const Char*> a, const BasicFixedString& b) noexcept {\n    return detail::fixedstring::equal_(a.begin(), a.size(), b.data_, b.size_);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator==(\n      const BasicFixedString& a, Range<const Char*> b) noexcept {\n    return b == a;\n  }\n\n  friend constexpr bool operator!=(\n      const Char* a, const BasicFixedString& b) noexcept {\n    return !(a == b);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator!=(\n      const BasicFixedString& a, const Char* b) noexcept {\n    return !(b == a);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator!=(\n      Range<const Char*> a, const BasicFixedString& b) noexcept {\n    return !(a == b);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator!=(\n      const BasicFixedString& a, Range<const Char*> b) noexcept {\n    return !(a == b);\n  }\n\n  friend constexpr bool operator<(\n      const Char* a, const BasicFixedString& b) noexcept {\n    return ordering::lt ==\n        detail::fixedstring::compare_(\n               a, 0u, folly::constexpr_strlen(a), b.data_, 0u, b.size_);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator<(\n      const BasicFixedString& a, const Char* b) noexcept {\n    return ordering::lt ==\n        detail::fixedstring::compare_(\n               a.data_, 0u, a.size_, b, 0u, folly::constexpr_strlen(b));\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator<(\n      Range<const Char*> a, const BasicFixedString& b) noexcept {\n    return ordering::lt ==\n        detail::fixedstring::compare_(\n               a.begin(), 0u, a.size(), b.data_, 0u, b.size_);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator<(\n      const BasicFixedString& a, Range<const Char*> b) noexcept {\n    return ordering::lt ==\n        detail::fixedstring::compare_(\n               a.data_, 0u, a.size_, b.begin(), 0u, b.size());\n  }\n\n  friend constexpr bool operator>(\n      const Char* a, const BasicFixedString& b) noexcept {\n    return b < a;\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator>(\n      const BasicFixedString& a, const Char* b) noexcept {\n    return b < a;\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator>(\n      Range<const Char*> a, const BasicFixedString& b) noexcept {\n    return b < a;\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator>(\n      const BasicFixedString& a, Range<const Char*> b) noexcept {\n    return b < a;\n  }\n\n  friend constexpr bool operator<=(\n      const Char* a, const BasicFixedString& b) noexcept {\n    return !(b < a);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator<=(\n      const BasicFixedString& a, const Char* b) noexcept {\n    return !(b < a);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator<=(\n      Range<const Char*> const& a, const BasicFixedString& b) noexcept {\n    return !(b < a);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator<=(\n      const BasicFixedString& a, Range<const Char*> b) noexcept {\n    return !(b < a);\n  }\n\n  friend constexpr bool operator>=(\n      const Char* a, const BasicFixedString& b) noexcept {\n    return !(a < b);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator>=(\n      const BasicFixedString& a, const Char* b) noexcept {\n    return !(a < b);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator>=(\n      Range<const Char*> a, const BasicFixedString& b) noexcept {\n    return !(a < b);\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr bool operator>=(\n      const BasicFixedString& a, Range<const Char*> const& b) noexcept {\n    return !(a < b);\n  }\n\n  /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n   * Asymmetric concatenation\n   */\n  template <std::size_t M>\n  friend constexpr BasicFixedString<Char, N + M - 1u> operator+(\n      const Char (&a)[M], const BasicFixedString& b) noexcept {\n    return detail::fixedstring::Helper::concat_<Char>(\n        detail::fixedstring::checkNullTerminated(a),\n        M - 1u,\n        b.data_,\n        b.size_,\n        std::make_index_sequence<N + M - 1u>{});\n  }\n\n  /**\n   * \\overload\n   */\n  template <std::size_t M>\n  friend constexpr BasicFixedString<Char, N + M - 1u> operator+(\n      const BasicFixedString& a, const Char (&b)[M]) noexcept {\n    return detail::fixedstring::Helper::concat_<Char>(\n        a.data_,\n        a.size_,\n        detail::fixedstring::checkNullTerminated(b),\n        M - 1u,\n        std::make_index_sequence<N + M - 1u>{});\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr BasicFixedString<Char, N + 1u> operator+(\n      Char a, const BasicFixedString& b) noexcept {\n    using A = const Char[2u];\n    return detail::fixedstring::Helper::concat_<Char>(\n        A{a, Char(0)},\n        1u,\n        b.data_,\n        b.size_,\n        std::make_index_sequence<N + 1u>{});\n  }\n\n  /**\n   * \\overload\n   */\n  friend constexpr BasicFixedString<Char, N + 1u> operator+(\n      const BasicFixedString& a, Char b) noexcept {\n    using A = const Char[2u];\n    return detail::fixedstring::Helper::concat_<Char>(\n        a.data_,\n        a.size_,\n        A{b, Char(0)},\n        1u,\n        std::make_index_sequence<N + 1u>{});\n  }\n};\n\ntemplate <class C, std::size_t N>\ninline std::basic_ostream<C>& operator<<(\n    std::basic_ostream<C>& os, const BasicFixedString<C, N>& string) {\n  using StreamSize = decltype(os.width());\n  os.write(string.begin(), static_cast<StreamSize>(string.size()));\n  return os;\n}\n\n/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n * Symmetric relational operators\n */\ntemplate <class Char, std::size_t A, std::size_t B>\nconstexpr bool operator==(\n    const BasicFixedString<Char, A>& a,\n    const BasicFixedString<Char, B>& b) noexcept {\n  return detail::fixedstring::equal_(\n      detail::fixedstring::Helper::data_(a),\n      a.size(),\n      detail::fixedstring::Helper::data_(b),\n      b.size());\n}\n\ntemplate <class Char, std::size_t A, std::size_t B>\nconstexpr bool operator!=(\n    const BasicFixedString<Char, A>& a, const BasicFixedString<Char, B>& b) {\n  return !(a == b);\n}\n\ntemplate <class Char, std::size_t A, std::size_t B>\nconstexpr bool operator<(\n    const BasicFixedString<Char, A>& a,\n    const BasicFixedString<Char, B>& b) noexcept {\n  return ordering::lt ==\n      detail::fixedstring::compare_(\n             detail::fixedstring::Helper::data_(a),\n             0u,\n             a.size(),\n             detail::fixedstring::Helper::data_(b),\n             0u,\n             b.size());\n}\n\ntemplate <class Char, std::size_t A, std::size_t B>\nconstexpr bool operator>(\n    const BasicFixedString<Char, A>& a,\n    const BasicFixedString<Char, B>& b) noexcept {\n  return b < a;\n}\n\ntemplate <class Char, std::size_t A, std::size_t B>\nconstexpr bool operator<=(\n    const BasicFixedString<Char, A>& a,\n    const BasicFixedString<Char, B>& b) noexcept {\n  return !(b < a);\n}\n\ntemplate <class Char, std::size_t A, std::size_t B>\nconstexpr bool operator>=(\n    const BasicFixedString<Char, A>& a,\n    const BasicFixedString<Char, B>& b) noexcept {\n  return !(a < b);\n}\n\n/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n * Symmetric concatenation\n */\ntemplate <class Char, std::size_t N, std::size_t M>\nconstexpr BasicFixedString<Char, N + M> operator+(\n    const BasicFixedString<Char, N>& a,\n    const BasicFixedString<Char, M>& b) noexcept {\n  return detail::fixedstring::Helper::concat_<Char>(\n      detail::fixedstring::Helper::data_(a),\n      a.size(),\n      detail::fixedstring::Helper::data_(b),\n      b.size(),\n      std::make_index_sequence<N + M>{});\n}\n\n/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n * Construct a `BasicFixedString` object from a null-terminated array of\n * characters. The capacity and size of the string will be equal to one less\n * than the size of the array.\n * \\pre `a` contains no embedded null characters.\n * \\pre `a[N-1] == Char(0)`\n * \\post For a returned string `s`, `s[i]==a[i]` for every `i` in [`0`,`N-1`].\n */\ntemplate <class Char, std::size_t N>\nconstexpr BasicFixedString<Char, N - 1u> makeFixedString(\n    const Char (&a)[N]) noexcept {\n  return {a};\n}\n\n/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **\n * Swap function\n */\ntemplate <class Char, std::size_t N>\nconstexpr void swap(\n    BasicFixedString<Char, N>& a, BasicFixedString<Char, N>& b) noexcept {\n  a.swap(b);\n}\n\ninline namespace literals {\ninline namespace string_literals {\ninline namespace {\n// \"const std::size_t&\" is so that folly::npos has the same address in every\n// translation unit. This is to avoid potential violations of the ODR.\nconstexpr const std::size_t& npos = detail::fixedstring::FixedStringBase::npos;\n} // namespace\n\n#if defined(__GNUC__) && !defined(__ICC)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"\n#pragma GCC diagnostic ignored \"-Wgnu-string-literal-operator-template\"\n\n/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *\n * User-defined literals for creating FixedString objects from string literals\n * on the compilers that support it.\n *\n * \\par Example:\n * \\par\n * \\code\n * using namespace folly::string_literals;\n * constexpr auto hello = \"hello world!\"_fs;\n * \\endcode\n *\n * \\note This requires a GNU compiler extension\n *   (-Wgnu-string-literal-operator-template) supported by clang and gcc,\n *   proposed for standardization in\n *   <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0424r0.pdf>.\n *   \\par\n *   For portable code, prefer the suffixes `_fs4`, `_fs8`, `_fs16`, `_fs32`,\n *   `_fs64`, and `_fs128` for creating instances of types `FixedString<4>`,\n *   `FixedString<8>`, `FixedString<16>`, etc.\n */\ntemplate <class Char, Char... Cs>\nconstexpr BasicFixedString<Char, sizeof...(Cs)> operator\"\"_fs() noexcept {\n  const Char a[] = {Cs..., Char(0)};\n  return {+a, sizeof...(Cs)};\n}\n\n#pragma GCC diagnostic pop\n#endif\n\n#ifndef NO_FIXED_STR_UDL\n#define FOLLY_DEFINE_FIXED_STRING_UDL(N)                     \\\n  constexpr FixedString<N> operator\"\"_fs##N(                 \\\n      const char* that, std::size_t count) noexcept(false) { \\\n    return {that, count};                                    \\\n  }                                                          \\\n/**/\n\n// Define UDLs _fs4, _fs8, _fs16, etc for FixedString<[4, 8, 16, ...]>\nFOLLY_DEFINE_FIXED_STRING_UDL(4)\nFOLLY_DEFINE_FIXED_STRING_UDL(8)\nFOLLY_DEFINE_FIXED_STRING_UDL(16)\nFOLLY_DEFINE_FIXED_STRING_UDL(32)\nFOLLY_DEFINE_FIXED_STRING_UDL(64)\nFOLLY_DEFINE_FIXED_STRING_UDL(128)\n\n#undef FOLLY_DEFINE_FIXED_STRING_UDL\n#endif\n} // namespace string_literals\n} // namespace literals\n\n// TODO:\n// // numeric conversions:\n// template <std::size_t N>\n// constexpr int stoi(const FixedString<N>& str, int base = 10);\n// template <std::size_t N>\n// constexpr unsigned stou(const FixedString<N>& str, int base = 10);\n// template <std::size_t N>\n// constexpr long stol(const FixedString<N>& str, int base = 10);\n// template <std::size_t N>\n// constexpr unsigned long stoul(const FixedString<N>& str, int base = 10;\n// template <std::size_t N>\n// constexpr long long stoll(const FixedString<N>& str, int base = 10);\n// template <std::size_t N>\n// constexpr unsigned long long stoull(const FixedString<N>& str,\n// int base = 10);\n// template <std::size_t N>\n// constexpr float stof(const FixedString<N>& str);\n// template <std::size_t N>\n// constexpr double stod(const FixedString<N>& str);\n// template <std::size_t N>\n// constexpr long double stold(const FixedString<N>& str);\n// template <int val>\n// constexpr FixedString</*...*/> to_fixed_string_i() noexcept;\n// template <unsigned val>\n// constexpr FixedString</*...*/> to_fixed_string_u() noexcept;\n// template <long val>\n// constexpr FixedString</*...*/> to_fixed_string_l() noexcept;\n// template <unsigned long val>\n// constexpr FixedString</*...*/> to_fixed_string_ul() noexcept;\n// template <long long val>\n// constexpr FixedString</*...*/> to_fixed_string_ll() noexcept\n// template <unsigned long long val>\n// constexpr FixedString</*...*/> to_fixed_string_ull() noexcept;\n} // namespace folly\n"
  },
  {
    "path": "folly/FmtUtility.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/FmtUtility.h>\n\n#include <folly/Range.h>\n#include <folly/String.h>\n#include <folly/ssl/OpenSSLHash.h>\n\nnamespace folly {\n\nstd::string fmt_vformat_mangle_name_fn::operator()(\n    std::string_view const key) const {\n  auto& self = *this;\n  std::string out;\n  self(out, key);\n  return out;\n}\n\nvoid fmt_vformat_mangle_name_fn::operator()(\n    std::string& out, std::string_view const key) const {\n  auto const keyr = folly::ByteRange(folly::StringPiece(key));\n  uint8_t enc[32];\n  auto const encr = folly::MutableByteRange{std::begin(enc), std::end(enc)};\n#if FOLLY_OPENSSL_HAS_BLAKE2B\n  folly::ssl::OpenSSLHash::blake2s256(encr, keyr);\n#else\n  folly::ssl::OpenSSLHash::sha256(encr, keyr);\n#endif\n  out.push_back('_');\n  folly::hexlify(encr, out, true);\n}\n\nstd::string fmt_vformat_mangle_format_string_fn::operator()(\n    std::string_view const str) const {\n  return operator()(options{}, str);\n}\n\nstd::string fmt_vformat_mangle_format_string_fn::operator()(\n    options const& opts, std::string_view const str) const {\n  auto const fe_opts =\n      format_string_for_each_named_arg_options{} //\n          .set_numeric_args_as_named(opts.numeric_args_as_named);\n  std::string out;\n  char const* pos = str.data();\n  format_string_for_each_named_arg(fe_opts, str, [&](auto const arg) {\n    out.append(pos, arg.data());\n    fmt_vformat_mangle_name(out, arg);\n    pos = arg.data() + arg.size();\n  });\n  out.append(pos, str.data() + str.size());\n  return out;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/FmtUtility.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fmt/args.h>\n\n#include <folly/CppAttributes.h>\n\nnamespace folly {\n\n/// fmt_make_format_args_from_map_fn\n/// fmt_make_format_args_from_map\n///\n/// A helper function-object type and variable for making a format-args object\n/// from a map.\n///\n/// May be useful for transitioning from legacy folly::svformat to fmt::vformat.\nstruct fmt_make_format_args_from_map_fn {\n  template <typename Map>\n  fmt::dynamic_format_arg_store<fmt::format_context> operator()(\n      [[FOLLY_ATTR_CLANG_LIFETIMEBOUND]] Map const& map) const {\n    fmt::dynamic_format_arg_store<fmt::format_context> ret;\n    ret.reserve(map.size(), map.size());\n    for (auto const& [key, val] : map) {\n      ret.push_back(fmt::arg(key.c_str(), std::cref(val)));\n    }\n    return ret;\n  }\n};\ninline constexpr fmt_make_format_args_from_map_fn\n    fmt_make_format_args_from_map{};\n\n/// fmt_vformat_mangle_name_fn\n/// fmt_vformat_mangle_name\n///\n/// A helper function-object type and variable for mangling vformat named-arg\n/// names which fmt::vformat might not otherwise permit.\nstruct fmt_vformat_mangle_name_fn {\n  std::string operator()(std::string_view const str) const;\n  void operator()(std::string& out, std::string_view const str) const;\n};\ninline constexpr fmt_vformat_mangle_name_fn fmt_vformat_mangle_name{};\n\n/// fmt_vformat_mangle_format_string_fn\n/// fmt_vformat_mangle_format_string\n///\n/// A helper function-object type and variable for mangling the content of\n/// vformat format-strings containing named-arg names which fmt::vformat might\n/// not otherwise permit.\nstruct fmt_vformat_mangle_format_string_fn {\n  struct options {\n    bool numeric_args_as_named = false;\n\n    options& set_numeric_args_as_named(bool value) noexcept {\n      numeric_args_as_named = value;\n      return *this;\n    }\n  };\n\n  std::string operator()(std::string_view const str) const;\n  std::string operator()(options const& opts, std::string_view const str) const;\n};\ninline constexpr fmt_vformat_mangle_format_string_fn\n    fmt_vformat_mangle_format_string{};\n\nusing fmt_vformat_mangle_format_string_options =\n    fmt_vformat_mangle_format_string_fn::options;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/FollyMemcpy.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstring> // @donotremove\n\n#if !defined(__AVX2__) && !(defined(__linux__) && defined(__aarch64__))\nnamespace folly {\n\nextern \"C\" void* __folly_memcpy(void* dst, const void* src, std::size_t size) {\n  if (size == 0)\n    return dst;\n  return std::memmove(dst, src, size);\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/FollyMemcpy.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstddef>\n\nnamespace folly {\n\nextern \"C\" void* __folly_memcpy(void* dst, const void* src, std::size_t size);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/FollyMemset.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstring> // @donotremove\n\n#if !defined(__AVX2__) && !(defined(__linux__) && defined(__aarch64__))\n\nnamespace folly {\n\nextern \"C\" void* __folly_memset(void* dest, int ch, std::size_t count) {\n  return std::memset(dest, ch, count);\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/FollyMemset.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n\nnamespace folly {\n\nextern \"C\" void* __folly_memset(void* dest, int ch, std::size_t count);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Format-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_FORMAT_H_\n#error This file may only be included from Format.h.\n#endif\n\n#include <array>\n#include <cinttypes>\n#include <deque>\n#include <map>\n#include <unordered_map>\n#include <vector>\n\n#include <folly/Exception.h>\n#include <folly/FormatTraits.h>\n#include <folly/MapUtil.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/ToAscii.h>\n#include <folly/portability/Windows.h>\n\n// Ignore -Wformat-nonliteral and -Wconversion warnings within this file\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wformat-nonliteral\")\nFOLLY_GNU_DISABLE_WARNING(\"-Wconversion\")\n\nnamespace folly {\n\nnamespace detail {\n\n// Updates the end of the buffer after the comma separators have been added.\nvoid insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer);\n\nextern const std::array<std::array<char, 2>, 256> formatHexUpper;\nextern const std::array<std::array<char, 2>, 256> formatHexLower;\nextern const std::array<std::array<char, 3>, 512> formatOctal;\nextern const std::array<std::array<char, 8>, 256> formatBinary;\n\nconst size_t kMaxHexLength = 2 * sizeof(uintmax_t);\nconst size_t kMaxOctalLength = 3 * sizeof(uintmax_t);\nconst size_t kMaxBinaryLength = 8 * sizeof(uintmax_t);\n\n/**\n * Convert an unsigned to hex, using repr (which maps from each possible\n * 2-hex-bytes value to the 2-character representation).\n *\n * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of\n * the supplied buffer and returns the offset of the beginning of the string\n * from the start of the buffer.  The formatted string will be in range\n * [buf+begin, buf+bufLen).\n */\ntemplate <class Uint>\nsize_t uintToHex(\n    char* buffer,\n    size_t bufLen,\n    Uint v,\n    std::array<std::array<char, 2>, 256> const& repr) {\n  // 'v >>= 7, v >>= 1' is no more than a work around to get rid of shift size\n  // warning when Uint = uint8_t (it's false as v >= 256 implies sizeof(v) > 1).\n  for (; !less_than<unsigned, 256>(v); v >>= 7, v >>= 1) {\n    auto b = v & 0xff;\n    bufLen -= 2;\n    buffer[bufLen] = repr[b][0];\n    buffer[bufLen + 1] = repr[b][1];\n  }\n  buffer[--bufLen] = repr[v][1];\n  if (v >= 16) {\n    buffer[--bufLen] = repr[v][0];\n  }\n  return bufLen;\n}\n\n/**\n * Convert an unsigned to hex, using lower-case letters for the digits\n * above 9.  See the comments for uintToHex.\n */\ntemplate <class Uint>\ninline size_t uintToHexLower(char* buffer, size_t bufLen, Uint v) {\n  return uintToHex(buffer, bufLen, v, formatHexLower);\n}\n\n/**\n * Convert an unsigned to hex, using upper-case letters for the digits\n * above 9.  See the comments for uintToHex.\n */\ntemplate <class Uint>\ninline size_t uintToHexUpper(char* buffer, size_t bufLen, Uint v) {\n  return uintToHex(buffer, bufLen, v, formatHexUpper);\n}\n\n/**\n * Convert an unsigned to octal.\n *\n * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of\n * the supplied buffer and returns the offset of the beginning of the string\n * from the start of the buffer.  The formatted string will be in range\n * [buf+begin, buf+bufLen).\n */\ntemplate <class Uint>\nsize_t uintToOctal(char* buffer, size_t bufLen, Uint v) {\n  auto& repr = formatOctal;\n  // 'v >>= 7, v >>= 2' is no more than a work around to get rid of shift size\n  // warning when Uint = uint8_t (it's false as v >= 512 implies sizeof(v) > 1).\n  for (; !less_than<unsigned, 512>(v); v >>= 7, v >>= 2) {\n    auto b = v & 0x1ff;\n    bufLen -= 3;\n    buffer[bufLen] = repr[b][0];\n    buffer[bufLen + 1] = repr[b][1];\n    buffer[bufLen + 2] = repr[b][2];\n  }\n  buffer[--bufLen] = repr[v][2];\n  if (v >= 8) {\n    buffer[--bufLen] = repr[v][1];\n  }\n  if (v >= 64) {\n    buffer[--bufLen] = repr[v][0];\n  }\n  return bufLen;\n}\n\n/**\n * Convert an unsigned to binary.\n *\n * Just like folly::detail::uintToBuffer in Conv.h, writes at the *end* of\n * the supplied buffer and returns the offset of the beginning of the string\n * from the start of the buffer.  The formatted string will be in range\n * [buf+begin, buf+bufLen).\n */\ntemplate <class Uint>\nsize_t uintToBinary(char* buffer, size_t bufLen, Uint v) {\n  auto& repr = formatBinary;\n  if (v == 0) {\n    buffer[--bufLen] = '0';\n    return bufLen;\n  }\n  for (; v; v >>= 7, v >>= 1) {\n    auto b = v & 0xff;\n    bufLen -= 8;\n    memcpy(buffer + bufLen, &(repr[b][0]), 8);\n  }\n  while (buffer[bufLen] == '0') {\n    ++bufLen;\n  }\n  return bufLen;\n}\n\ntemplate <bool containerMode, bool RecordUsedArg, class Output>\nvoid baseFormatterCallImpl(\n    Output& out,\n    size_t nargs,\n    const int widths[],\n    std::bool_constant<RecordUsedArg>(used)(const BaseFormatterBase&, size_t),\n    BaseFormatterBase::DoFormatFn<Output>* const funs[],\n    const BaseFormatterBase& base) {\n  // Copy raw string (without format specifiers) to output;\n  // not as simple as we'd like, as we still need to translate \"}}\" to \"}\"\n  // and throw if we see any lone \"}\"\n  auto outputString = [&out](StringPiece s) {\n    auto p = s.begin();\n    auto end = s.end();\n    while (p != end) {\n      auto q = static_cast<const char*>(memchr(p, '}', size_t(end - p)));\n      if (!q) {\n        out(StringPiece(p, end));\n        break;\n      }\n      ++q;\n      out(StringPiece(p, q));\n      p = q;\n\n      if (p == end || *p != '}') {\n        throw_exception<BadFormatArg>(\n            \"folly::format: single '}' in format string\");\n      }\n      ++p;\n    }\n  };\n\n  auto str_ = base.str_;\n  auto p = str_.begin();\n  auto end = str_.end();\n\n  int nextArg = 0;\n  bool hasDefaultArgIndex = false;\n  bool hasExplicitArgIndex = false;\n  while (p != end) {\n    auto q = static_cast<const char*>(memchr(p, '{', size_t(end - p)));\n    if (!q) {\n      outputString(StringPiece(p, end));\n      break;\n    }\n    outputString(StringPiece(p, q));\n    p = q + 1;\n\n    if (p == end) {\n      throw_exception<BadFormatArg>(\n          \"folly::format: '}' at end of format string\");\n    }\n\n    // \"{{\" -> \"{\"\n    if (*p == '{') {\n      out(StringPiece(p, 1));\n      ++p;\n      continue;\n    }\n\n    // Format string\n    q = static_cast<const char*>(memchr(p, '}', size_t(end - p)));\n    if (q == nullptr) {\n      throw_exception<BadFormatArg>(\"folly::format: missing ending '}'\");\n    }\n    FormatArg arg(StringPiece(p, q));\n    p = q + 1;\n\n    int argIndex = 0;\n    auto piece = arg.splitKey<true>(); // empty key component is okay\n    if constexpr (containerMode) {\n      arg.enforce(\n          arg.width != FormatArg::kDynamicWidth,\n          \"dynamic field width not supported in vformat()\");\n      if (piece.empty()) {\n        arg.setNextIntKey(nextArg++);\n        hasDefaultArgIndex = true;\n      } else {\n        arg.setNextKey(piece);\n        hasExplicitArgIndex = true;\n      }\n    } else {\n      if (piece.empty()) {\n        if (arg.width == FormatArg::kDynamicWidth) {\n          arg.enforce(\n              arg.widthIndex == FormatArg::kNoIndex,\n              \"cannot provide width arg index without value arg index\");\n          auto sizeArg = size_t(nextArg++);\n          detail::formatCheckIndex(sizeArg, arg, nargs);\n          if (RecordUsedArg) {\n            used(base, sizeArg);\n          }\n          auto w = widths[sizeArg];\n          arg.enforce(w >= 0, \"dynamic field width argument must be integral\");\n          arg.width = w;\n        }\n\n        argIndex = nextArg++;\n        hasDefaultArgIndex = true;\n      } else {\n        if (arg.width == FormatArg::kDynamicWidth) {\n          arg.enforce(\n              arg.widthIndex != FormatArg::kNoIndex,\n              \"cannot provide value arg index without width arg index\");\n          auto sizeArg = size_t(arg.widthIndex);\n          detail::formatCheckIndex(sizeArg, arg, nargs);\n          if (RecordUsedArg) {\n            used(base, sizeArg);\n          }\n          auto w = widths[sizeArg];\n          arg.enforce(w >= 0, \"dynamic field width argument must be integral\");\n          arg.width = w;\n        }\n\n        auto result = tryTo<int>(piece);\n        arg.enforce(result, \"argument index must be integer\");\n        argIndex = *result;\n        arg.enforce(argIndex >= 0, \"argument index must be non-negative\");\n        hasExplicitArgIndex = true;\n      }\n    }\n\n    if (hasDefaultArgIndex && hasExplicitArgIndex) {\n      throw_exception<BadFormatArg>(\n          \"folly::format: may not have both default and explicit arg indexes\");\n    }\n\n    if (RecordUsedArg) {\n      used(base, argIndex);\n    } else {\n      formatCheckIndex(argIndex, arg, nargs);\n      funs[argIndex](base, arg, out);\n    }\n  }\n}\n\n} // namespace detail\n\ntemplate <class Derived, bool containerMode, size_t... I, class... Args>\ntemplate <class Output>\nvoid BaseFormatterImpl<\n    Derived,\n    containerMode,\n    std::index_sequence<I...>,\n    Args...>::operator()(Output& out) const {\n  constexpr size_t nargs = sizeof...(Args);\n  using RecordUsedSizeArgs = decltype(Derived::recordUsedArg(*this, 0));\n  constexpr auto used = Derived::recordUsedArg;\n  static constexpr auto funs = getDoFormatFnArray<Output>();\n  constexpr auto in = unsafe_default_initialized;\n  int widths[nargs + 1] = {conditional_t<!alignof(Args), int, int>{in}..., in};\n  getSizeArg(widths);\n  detail::baseFormatterCallImpl<containerMode, RecordUsedSizeArgs::value>(\n      out, nargs, widths, *used, funs.data, *this);\n}\n\nnamespace format_value {\n\ntemplate <class FormatCallback>\nvoid formatString(StringPiece val, FormatArg& arg, FormatCallback& cb) {\n  if (arg.width != FormatArg::kDefaultWidth && arg.width < 0) {\n    throw_exception<BadFormatArg>(\"folly::format: invalid width\");\n  }\n  if (arg.precision != FormatArg::kDefaultPrecision && arg.precision < 0) {\n    throw_exception<BadFormatArg>(\"folly::format: invalid precision\");\n  }\n\n  if (arg.precision != FormatArg::kDefaultPrecision &&\n      val.size() > static_cast<size_t>(arg.precision)) {\n    val.reset(val.data(), static_cast<size_t>(arg.precision));\n  }\n\n  constexpr int padBufSize = 128;\n  char padBuf[padBufSize];\n\n  // Output padding, no more than padBufSize at once\n  auto pad = [&padBuf, &cb, padBufSize](int chars) {\n    while (chars) {\n      int n = std::min(chars, padBufSize);\n      cb(StringPiece(padBuf, size_t(n)));\n      chars -= n;\n    }\n  };\n\n  int padRemaining = 0;\n  if (arg.width != FormatArg::kDefaultWidth &&\n      val.size() < static_cast<size_t>(arg.width)) {\n    char fill = arg.fill == FormatArg::kDefaultFill ? ' ' : arg.fill;\n    int padChars = static_cast<int>(arg.width - val.size());\n    memset(padBuf, fill, size_t(std::min(padBufSize, padChars)));\n\n    FOLLY_PUSH_WARNING\n    FOLLY_CLANG_DISABLE_WARNING(\"-Wcovered-switch-default\")\n    switch (arg.align) {\n      case FormatArg::Align::DEFAULT:\n      case FormatArg::Align::LEFT:\n        padRemaining = padChars;\n        break;\n      case FormatArg::Align::CENTER:\n        pad(padChars / 2);\n        padRemaining = padChars - padChars / 2;\n        break;\n      case FormatArg::Align::RIGHT:\n      case FormatArg::Align::PAD_AFTER_SIGN:\n        pad(padChars);\n        break;\n      case FormatArg::Align::INVALID:\n      default:\n        abort();\n        break;\n    }\n    FOLLY_POP_WARNING\n  }\n\n  cb(val);\n\n  if (padRemaining) {\n    pad(padRemaining);\n  }\n}\n\ntemplate <class FormatCallback>\nvoid formatNumber(\n    StringPiece val, int prefixLen, FormatArg& arg, FormatCallback& cb) {\n  // precision means something different for numbers\n  arg.precision = FormatArg::kDefaultPrecision;\n  if (arg.align == FormatArg::Align::DEFAULT) {\n    arg.align = FormatArg::Align::RIGHT;\n  } else if (prefixLen && arg.align == FormatArg::Align::PAD_AFTER_SIGN) {\n    // Split off the prefix, then do any padding if necessary\n    cb(val.subpiece(0, size_t(prefixLen)));\n    val.advance(size_t(prefixLen));\n    arg.width = std::max(arg.width - prefixLen, 0);\n  }\n  format_value::formatString(val, arg, cb);\n}\n\ntemplate <typename FormatCallback>\nstruct FormatFormatterFn {\n  FormatArg& arg;\n  FormatCallback& cb;\n  void operator()(StringPiece sp) {\n    int sz = static_cast<int>(sp.size());\n    if (arg.precision != FormatArg::kDefaultPrecision) {\n      sz = std::min(arg.precision, sz);\n      sp.reset(sp.data(), size_t(sz));\n      arg.precision -= sz;\n    }\n    if (!sp.empty()) {\n      cb(sp);\n      if (arg.width != FormatArg::kDefaultWidth) {\n        arg.width = std::max(arg.width - sz, 0);\n      }\n    }\n  }\n};\n\ntemplate <class FormatCallback, bool containerMode, class... Args>\nvoid formatFormatter(\n    const Formatter<containerMode, Args...>& formatter,\n    FormatArg& arg,\n    FormatCallback& cb) {\n  if (arg.width == FormatArg::kDefaultWidth &&\n      arg.precision == FormatArg::kDefaultPrecision) {\n    // nothing to do\n    formatter(cb);\n  } else if (\n      arg.align != FormatArg::Align::LEFT &&\n      arg.align != FormatArg::Align::DEFAULT) {\n    // We can only avoid creating a temporary string if we align left,\n    // as we'd need to know the size beforehand otherwise\n    format_value::formatString(formatter.str(), arg, cb);\n  } else {\n    auto fn = FormatFormatterFn<FormatCallback>{arg, cb};\n    formatter(fn);\n    if (arg.width != FormatArg::kDefaultWidth && arg.width != 0) {\n      // Rely on formatString to do appropriate padding\n      format_value::formatString(StringPiece(), arg, cb);\n    }\n  }\n}\n\n} // namespace format_value\n\n// Definitions for default FormatValue classes\n\n// Integral types (except bool)\ntemplate <class T>\nclass FormatValue<\n    T,\n    typename std::enable_if<\n        std::is_integral<T>::value && !std::is_same<T, bool>::value>::type> {\n public:\n  explicit FormatValue(T val) : val_(val) {}\n\n  T getValue() const { return val_; }\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    arg.validate(FormatArg::Type::INTEGER);\n    doFormat(arg, cb);\n  }\n\n  template <class FormatCallback>\n  void doFormat(FormatArg& arg, FormatCallback& cb) const {\n    char presentation = arg.presentation;\n    if (presentation == FormatArg::kDefaultPresentation) {\n      presentation = std::is_same<T, char>::value ? 'c' : 'd';\n    }\n\n    // Do all work as unsigned, we'll add the prefix ('0' or '0x' if necessary)\n    // and sign ourselves.\n    typedef typename std::make_unsigned<T>::type UT;\n    UT uval;\n    char sign;\n    if constexpr (std::is_signed<T>::value) {\n      if (folly::is_negative(val_)) {\n        // avoid unary negation of unsigned types, which may be warned against\n        // avoid ub signed integer overflow, which ubsan checks against\n        uval = UT(0 - static_cast<UT>(val_));\n        sign = '-';\n      } else {\n        uval = static_cast<UT>(val_);\n        FOLLY_PUSH_WARNING\n        FOLLY_CLANG_DISABLE_WARNING(\"-Wcovered-switch-default\")\n        switch (arg.sign) {\n          case FormatArg::Sign::PLUS_OR_MINUS:\n            sign = '+';\n            break;\n          case FormatArg::Sign::SPACE_OR_MINUS:\n            sign = ' ';\n            break;\n          case FormatArg::Sign::DEFAULT:\n          case FormatArg::Sign::MINUS:\n          case FormatArg::Sign::INVALID:\n          default:\n            sign = '\\0';\n            break;\n        }\n        FOLLY_POP_WARNING\n      }\n    } else {\n      uval = static_cast<UT>(val_);\n      sign = '\\0';\n\n      arg.enforce(\n          arg.sign == FormatArg::Sign::DEFAULT,\n          \"sign specifications not allowed for unsigned values\");\n    }\n\n    // 1 byte for sign, plus max of:\n    // #x: two byte \"0x\" prefix + kMaxHexLength\n    // #o: one byte \"0\" prefix + kMaxOctalLength\n    // #b: two byte \"0b\" prefix + kMaxBinaryLength\n    // n: 19 bytes + 1 NUL\n    // ,d: 26 bytes (including thousands separators!)\n    //\n    // Binary format must take the most space, so we use that.\n    //\n    // Note that we produce a StringPiece rather than NUL-terminating,\n    // so we don't need an extra byte for a NUL.\n    constexpr size_t valBufSize = 1 + 2 + detail::kMaxBinaryLength;\n    char valBuf[valBufSize];\n    char* valBufBegin = nullptr;\n    char* valBufEnd = nullptr;\n\n    int prefixLen = 0;\n    switch (presentation) {\n      case 'n': {\n        arg.enforce(\n            !arg.basePrefix,\n            \"base prefix not allowed with '\",\n            presentation,\n            \"' specifier\");\n\n        arg.enforce(\n            !arg.thousandsSeparator,\n            \"cannot use ',' with the '\",\n            presentation,\n            \"' specifier\");\n\n        valBufBegin = valBuf + 1; // room for sign\n#if defined(__ANDROID__)\n        int len = snprintf(\n            valBufBegin,\n            (valBuf + valBufSize) - valBufBegin,\n            \"%\" PRIuMAX,\n            static_cast<uintmax_t>(uval));\n#else\n        int len = snprintf(\n            valBufBegin,\n            size_t((valBuf + valBufSize) - valBufBegin),\n            \"%ju\",\n            static_cast<uintmax_t>(uval));\n#endif\n        // valBufSize should always be big enough, so this should never\n        // happen.\n        assert(len < valBuf + valBufSize - valBufBegin);\n        valBufEnd = valBufBegin + len;\n        break;\n      }\n      case 'd':\n        arg.enforce(\n            !arg.basePrefix,\n            \"base prefix not allowed with '\",\n            presentation,\n            \"' specifier\");\n        valBufBegin = valBuf + 1; // room for sign\n\n        // Use to_ascii_decimal, faster than sprintf\n        valBufEnd = valBufBegin +\n            to_ascii_decimal(valBufBegin, valBuf + sizeof(valBuf), uval);\n        if (arg.thousandsSeparator) {\n          detail::insertThousandsGroupingUnsafe(valBufBegin, &valBufEnd);\n        }\n        break;\n      case 'c':\n        arg.enforce(\n            !arg.basePrefix,\n            \"base prefix not allowed with '\",\n            presentation,\n            \"' specifier\");\n        arg.enforce(\n            !arg.thousandsSeparator,\n            \"thousands separator (',') not allowed with '\",\n            presentation,\n            \"' specifier\");\n        valBufBegin = valBuf + 1; // room for sign\n        *valBufBegin = static_cast<char>(uval);\n        valBufEnd = valBufBegin + 1;\n        break;\n      case 'o':\n      case 'O':\n        arg.enforce(\n            !arg.thousandsSeparator,\n            \"thousands separator (',') not allowed with '\",\n            presentation,\n            \"' specifier\");\n        valBufEnd = valBuf + valBufSize;\n        valBufBegin = &valBuf[detail::uintToOctal(valBuf, valBufSize, uval)];\n        if (arg.basePrefix) {\n          *--valBufBegin = '0';\n          prefixLen = 1;\n        }\n        break;\n      case 'x':\n        arg.enforce(\n            !arg.thousandsSeparator,\n            \"thousands separator (',') not allowed with '\",\n            presentation,\n            \"' specifier\");\n        valBufEnd = valBuf + valBufSize;\n        valBufBegin = &valBuf[detail::uintToHexLower(valBuf, valBufSize, uval)];\n        if (arg.basePrefix) {\n          *--valBufBegin = 'x';\n          *--valBufBegin = '0';\n          prefixLen = 2;\n        }\n        break;\n      case 'X':\n        arg.enforce(\n            !arg.thousandsSeparator,\n            \"thousands separator (',') not allowed with '\",\n            presentation,\n            \"' specifier\");\n        valBufEnd = valBuf + valBufSize;\n        valBufBegin = &valBuf[detail::uintToHexUpper(valBuf, valBufSize, uval)];\n        if (arg.basePrefix) {\n          *--valBufBegin = 'X';\n          *--valBufBegin = '0';\n          prefixLen = 2;\n        }\n        break;\n      case 'b':\n      case 'B':\n        arg.enforce(\n            !arg.thousandsSeparator,\n            \"thousands separator (',') not allowed with '\",\n            presentation,\n            \"' specifier\");\n        valBufEnd = valBuf + valBufSize;\n        valBufBegin = &valBuf[detail::uintToBinary(valBuf, valBufSize, uval)];\n        if (arg.basePrefix) {\n          *--valBufBegin = presentation; // 0b or 0B\n          *--valBufBegin = '0';\n          prefixLen = 2;\n        }\n        break;\n      default:\n        arg.error(\"invalid specifier '\", presentation, \"'\");\n    }\n\n    if (sign) {\n      *--valBufBegin = sign;\n      ++prefixLen;\n    }\n\n    format_value::formatNumber(\n        StringPiece(valBufBegin, valBufEnd), prefixLen, arg, cb);\n  }\n\n private:\n  T val_;\n};\n\n// Bool\ntemplate <>\nclass FormatValue<bool> {\n public:\n  explicit FormatValue(bool val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    if (arg.presentation == FormatArg::kDefaultPresentation) {\n      arg.validate(FormatArg::Type::OTHER);\n      format_value::formatString(val_ ? \"true\" : \"false\", arg, cb);\n    } else { // number\n      FormatValue<int>(val_).format(arg, cb);\n    }\n  }\n\n private:\n  bool val_;\n};\n\n// double\ntemplate <>\nclass FormatValue<double> {\n public:\n  explicit FormatValue(double val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    fbstring piece;\n    int prefixLen;\n    formatHelper(piece, prefixLen, arg);\n    format_value::formatNumber(piece, prefixLen, arg, cb);\n  }\n\n private:\n  void formatHelper(fbstring& piece, int& prefixLen, FormatArg& arg) const;\n\n  double val_;\n};\n\n// float (defer to double)\ntemplate <>\nclass FormatValue<float> {\n public:\n  explicit FormatValue(float val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    FormatValue<double>(val_).format(arg, cb);\n  }\n\n private:\n  float val_;\n};\n\n// String-y types (implicitly convertible to StringPiece, except char*)\ntemplate <class T>\nclass FormatValue<\n    T,\n    typename std::enable_if<\n        (!std::is_pointer<T>::value ||\n         !std::is_same<\n             char,\n             typename std::decay<typename std::remove_pointer<T>::type>::type>::\n             value) &&\n        std::is_convertible<T, StringPiece>::value>::type> {\n public:\n  explicit FormatValue(StringPiece val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    if (arg.keyEmpty()) {\n      arg.validate(FormatArg::Type::OTHER);\n      arg.enforce(\n          arg.presentation == FormatArg::kDefaultPresentation ||\n              arg.presentation == 's',\n          \"invalid specifier '\",\n          arg.presentation,\n          \"'\");\n      format_value::formatString(val_, arg, cb);\n    } else {\n      FormatValue<char>(val_.at(size_t(arg.splitIntKey()))).format(arg, cb);\n    }\n  }\n\n private:\n  StringPiece val_;\n};\n\n// Null\ntemplate <>\nclass FormatValue<std::nullptr_t> {\n public:\n  explicit FormatValue(std::nullptr_t) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    arg.validate(FormatArg::Type::OTHER);\n    arg.enforce(\n        arg.presentation == FormatArg::kDefaultPresentation,\n        \"invalid specifier '\",\n        arg.presentation,\n        \"'\");\n    format_value::formatString(\"(null)\", arg, cb);\n  }\n};\n\n// Partial specialization of FormatValue for char*\ntemplate <class T>\nclass FormatValue<\n    T*,\n    typename std::enable_if<\n        std::is_same<char, typename std::decay<T>::type>::value>::type> {\n public:\n  explicit FormatValue(T* val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    if (arg.keyEmpty()) {\n      if (!val_) {\n        FormatValue<std::nullptr_t>(nullptr).format(arg, cb);\n      } else {\n        FormatValue<StringPiece>(val_).format(arg, cb);\n      }\n    } else {\n      FormatValue<typename std::decay<T>::type>(val_[arg.splitIntKey()])\n          .format(arg, cb);\n    }\n  }\n\n private:\n  T* val_;\n};\n\n// Partial specialization of FormatValue for void*\ntemplate <class T>\nclass FormatValue<\n    T*,\n    typename std::enable_if<\n        std::is_same<void, typename std::decay<T>::type>::value>::type> {\n public:\n  explicit FormatValue(T* val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    if (!val_) {\n      FormatValue<std::nullptr_t>(nullptr).format(arg, cb);\n    } else {\n      // Print as a pointer, in hex.\n      arg.validate(FormatArg::Type::OTHER);\n      arg.enforce(\n          arg.presentation == FormatArg::kDefaultPresentation,\n          \"invalid specifier '\",\n          arg.presentation,\n          \"'\");\n      arg.basePrefix = true;\n      arg.presentation = 'x';\n      if (arg.align == FormatArg::Align::DEFAULT) {\n        arg.align = FormatArg::Align::LEFT;\n      }\n      FormatValue<uintptr_t>(reinterpret_cast<uintptr_t>(val_))\n          .doFormat(arg, cb);\n    }\n  }\n\n private:\n  T* val_;\n};\n\ntemplate <class T, class = void>\nclass TryFormatValue {\n public:\n  template <class FormatCallback>\n  static void formatOrFail(\n      T& /* value */, FormatArg& arg, FormatCallback& /* cb */) {\n    arg.error(\"No formatter available for this type\");\n  }\n};\n\ntemplate <class T>\nclass TryFormatValue<\n    T,\n    typename std::enable_if<\n        0 < sizeof(FormatValue<typename std::decay<T>::type>)>::type> {\n public:\n  template <class FormatCallback>\n  static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) {\n    FormatValue<typename std::decay<T>::type>(value).format(arg, cb);\n  }\n};\n\n// Partial specialization of FormatValue for other pointers\ntemplate <class T>\nclass FormatValue<\n    T*,\n    typename std::enable_if<\n        !std::is_same<char, typename std::decay<T>::type>::value &&\n        !std::is_same<void, typename std::decay<T>::type>::value>::type> {\n public:\n  explicit FormatValue(T* val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    if (arg.keyEmpty()) {\n      FormatValue<void*>((void*)val_).format(arg, cb);\n    } else {\n      TryFormatValue<T>::formatOrFail(val_[arg.splitIntKey()], arg, cb);\n    }\n  }\n\n private:\n  T* val_;\n};\n\nnamespace detail {\n\n// std::array\ntemplate <class T, size_t N>\nstruct IndexableTraits<std::array<T, N>>\n    : public IndexableTraitsSeq<std::array<T, N>> {};\n\n// std::vector\ntemplate <class T, class A>\nstruct IndexableTraits<std::vector<T, A>>\n    : public IndexableTraitsSeq<std::vector<T, A>> {};\n\n// std::deque\ntemplate <class T, class A>\nstruct IndexableTraits<std::deque<T, A>>\n    : public IndexableTraitsSeq<std::deque<T, A>> {};\n\n// std::map with integral keys\ntemplate <class K, class T, class C, class A>\nstruct IndexableTraits<\n    std::map<K, T, C, A>,\n    typename std::enable_if<std::is_integral<K>::value>::type>\n    : public IndexableTraitsAssoc<std::map<K, T, C, A>> {};\n\n// std::unordered_map with integral keys\ntemplate <class K, class T, class H, class E, class A>\nstruct IndexableTraits<\n    std::unordered_map<K, T, H, E, A>,\n    typename std::enable_if<std::is_integral<K>::value>::type>\n    : public IndexableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {};\n\n} // namespace detail\n\n// Partial specialization of FormatValue for integer-indexable containers\ntemplate <class T>\nclass FormatValue<T, typename detail::IndexableTraits<T>::enabled> {\n public:\n  explicit FormatValue(const T& val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    FormatValue<typename std::decay<\n        typename detail::IndexableTraits<T>::value_type>::type>(\n        detail::IndexableTraits<T>::at(val_, arg.splitIntKey()))\n        .format(arg, cb);\n  }\n\n private:\n  const T& val_;\n};\n\ntemplate <class Container, class Value>\nclass FormatValue<\n    detail::DefaultValueWrapper<Container, Value>,\n    typename detail::IndexableTraits<Container>::enabled> {\n public:\n  explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)\n      : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    FormatValue<typename std::decay<\n        typename detail::IndexableTraits<Container>::value_type>::type>(\n        detail::IndexableTraits<Container>::at(\n            val_.container, arg.splitIntKey(), val_.defaultValue))\n        .format(arg, cb);\n  }\n\n private:\n  const detail::DefaultValueWrapper<Container, Value>& val_;\n};\n\nnamespace detail {\n\n// Define enabled, key_type, convert from StringPiece to the key types\n// that we support\ntemplate <class T>\nstruct KeyFromStringPiece;\n\n// std::string\ntemplate <>\nstruct KeyFromStringPiece<std::string> : public FormatTraitsBase {\n  typedef std::string key_type;\n  static std::string convert(StringPiece s) { return s.toString(); }\n  typedef void enabled;\n};\n\n// fbstring\ntemplate <>\nstruct KeyFromStringPiece<fbstring> : public FormatTraitsBase {\n  typedef fbstring key_type;\n  static fbstring convert(StringPiece s) { return s.to<fbstring>(); }\n};\n\n// StringPiece\ntemplate <>\nstruct KeyFromStringPiece<StringPiece> : public FormatTraitsBase {\n  typedef StringPiece key_type;\n  static StringPiece convert(StringPiece s) { return s; }\n};\n\n// Base class for associative types keyed by strings\ntemplate <class T>\nstruct KeyableTraitsAssoc : public FormatTraitsBase {\n  typedef typename T::key_type key_type;\n  typedef typename T::value_type::second_type value_type;\n  static const value_type& at(const T& map, StringPiece key) {\n    if (auto ptr = get_ptr(map, KeyFromStringPiece<key_type>::convert(key))) {\n      return *ptr;\n    }\n    throw_exception<FormatKeyNotFoundException>(key);\n  }\n  static const value_type& at(\n      const T& map, StringPiece key, const value_type& dflt) {\n    auto pos = map.find(KeyFromStringPiece<key_type>::convert(key));\n    return pos != map.end() ? pos->second : dflt;\n  }\n};\n\n// Define enabled, key_type, value_type, at() for supported string-keyed\n// types\ntemplate <class T, class Enabled = void>\nstruct KeyableTraits;\n\n// std::map with string key\ntemplate <class K, class T, class C, class A>\nstruct KeyableTraits<\n    std::map<K, T, C, A>,\n    typename KeyFromStringPiece<K>::enabled>\n    : public KeyableTraitsAssoc<std::map<K, T, C, A>> {};\n\n// std::unordered_map with string key\ntemplate <class K, class T, class H, class E, class A>\nstruct KeyableTraits<\n    std::unordered_map<K, T, H, E, A>,\n    typename KeyFromStringPiece<K>::enabled>\n    : public KeyableTraitsAssoc<std::unordered_map<K, T, H, E, A>> {};\n\n} // namespace detail\n\n// Partial specialization of FormatValue for string-keyed containers\ntemplate <class T>\nclass FormatValue<T, typename detail::KeyableTraits<T>::enabled> {\n public:\n  explicit FormatValue(const T& val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    FormatValue<typename std::decay<\n        typename detail::KeyableTraits<T>::value_type>::type>(\n        detail::KeyableTraits<T>::at(val_, arg.splitKey()))\n        .format(arg, cb);\n  }\n\n private:\n  const T& val_;\n};\n\ntemplate <class Container, class Value>\nclass FormatValue<\n    detail::DefaultValueWrapper<Container, Value>,\n    typename detail::KeyableTraits<Container>::enabled> {\n public:\n  explicit FormatValue(const detail::DefaultValueWrapper<Container, Value>& val)\n      : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    FormatValue<typename std::decay<\n        typename detail::KeyableTraits<Container>::value_type>::type>(\n        detail::KeyableTraits<Container>::at(\n            val_.container, arg.splitKey(), val_.defaultValue))\n        .format(arg, cb);\n  }\n\n private:\n  const detail::DefaultValueWrapper<Container, Value>& val_;\n};\n\n// Partial specialization of FormatValue for pairs\ntemplate <class A, class B>\nclass FormatValue<std::pair<A, B>> {\n public:\n  explicit FormatValue(const std::pair<A, B>& val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    int key = arg.splitIntKey();\n    switch (key) {\n      case 0:\n        FormatValue<typename std::decay<A>::type>(val_.first).format(arg, cb);\n        break;\n      case 1:\n        FormatValue<typename std::decay<B>::type>(val_.second).format(arg, cb);\n        break;\n      default:\n        arg.error(\"invalid index for pair\");\n    }\n  }\n\n private:\n  const std::pair<A, B>& val_;\n};\n\n// Partial specialization of FormatValue for tuples\ntemplate <class... Args>\nclass FormatValue<std::tuple<Args...>> {\n  typedef std::tuple<Args...> Tuple;\n\n public:\n  explicit FormatValue(const Tuple& val) : val_(val) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    int key = arg.splitIntKey();\n    arg.enforce(key >= 0, \"tuple index must be non-negative\");\n    doFormat(size_t(key), arg, cb);\n  }\n\n private:\n  template <size_t K>\n  using FV = FormatValue<\n      typename std::decay<typename std::tuple_element<K, Tuple>::type>::type>;\n\n  template <class Callback, size_t... I>\n  void doFormat(\n      size_t i, FormatArg& arg, Callback& cb, std::index_sequence<I...>) const {\n    detail::formatCheckIndex(i, arg, sizeof...(Args));\n    ((i == I && (FV<I>(std::get<I>(val_)).format(arg, cb), 0)), ...);\n  }\n  template <class Callback>\n  void doFormat(size_t i, FormatArg& arg, Callback& cb) const {\n    return doFormat(i, arg, cb, std::index_sequence_for<Args...>{});\n  }\n\n  const Tuple& val_;\n};\n\n// Partial specialization of FormatValue for nested Formatters\ntemplate <bool containerMode, class... Args, template <bool, class...> class F>\nclass FormatValue<\n    F<containerMode, Args...>,\n    typename std::enable_if<\n        detail::IsFormatter<F<containerMode, Args...>>::value>::type> {\n  typedef F<containerMode, Args...> FormatterValue;\n\n public:\n  explicit FormatValue(const FormatterValue& f) : f_(f) {}\n\n  template <class FormatCallback>\n  void format(FormatArg& arg, FormatCallback& cb) const {\n    format_value::formatFormatter(f_, arg, cb);\n  }\n\n private:\n  const FormatterValue& f_;\n};\n\n/**\n * Formatter objects can be appended to strings, and therefore they're\n * compatible with folly::toAppend and folly::to.\n */\ntemplate <class Tgt, bool containerMode, class... Args>\ntypename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(\n    const Formatter<containerMode, Args...>& value, Tgt* result) {\n  value.appendTo(*result);\n}\n\n} // namespace folly\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/Format.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Format.h>\n\n#include <cassert>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Portability.h>\n#include <folly/container/Array.h>\n\n#include <double-conversion/double-conversion.h> // @donotremove\n\nnamespace folly {\nnamespace detail {\n\n//  ctor for items in the align table\nstruct format_table_align_make_item {\n  static constexpr std::size_t size = 256;\n  constexpr FormatArg::Align operator()(std::size_t index) const {\n    // clang-format off\n    return\n        index == '<' ? FormatArg::Align::LEFT:\n        index == '>' ? FormatArg::Align::RIGHT :\n        index == '=' ? FormatArg::Align::PAD_AFTER_SIGN :\n        index == '^' ? FormatArg::Align::CENTER :\n        FormatArg::Align::INVALID;\n    // clang-format on\n  }\n};\n\n//  ctor for items in the conv tables for representing parts of nonnegative\n//  integers into ascii digits of length Size, over a given base Base\ntemplate <std::size_t Base, std::size_t Size, bool Upper = false>\nstruct format_table_conv_make_item {\n  static_assert(Base <= 36, \"Base is unrepresentable\");\n  struct make_item {\n    std::size_t index{};\n    constexpr char alpha(std::size_t ord) const {\n      return static_cast<char>(\n          ord < 10 ? '0' + ord : (Upper ? 'A' : 'a') + (ord - 10));\n    }\n    constexpr char operator()(std::size_t offset) const {\n      return alpha(index / constexpr_pow(Base, Size - offset - 1) % Base);\n    }\n  };\n  constexpr std::array<char, Size> operator()(std::size_t index) const {\n    return make_array_with<Size>(make_item{index});\n  }\n};\n\n//  ctor for items in the sign table\nstruct format_table_sign_make_item {\n  static constexpr std::size_t size = 256;\n  constexpr FormatArg::Sign operator()(std::size_t index) const {\n    // clang-format off\n    return\n        index == '+' ? FormatArg::Sign::PLUS_OR_MINUS :\n        index == '-' ? FormatArg::Sign::MINUS :\n        index == ' ' ? FormatArg::Sign::SPACE_OR_MINUS :\n        FormatArg::Sign::INVALID;\n    // clang-format on\n  }\n};\n\n//  the tables\nFOLLY_STORAGE_CONSTEXPR auto formatAlignTable =\n    make_array_with<256>(format_table_align_make_item{});\nFOLLY_STORAGE_CONSTEXPR auto formatSignTable =\n    make_array_with<256>(format_table_sign_make_item{});\nFOLLY_STORAGE_CONSTEXPR decltype(formatHexLower) formatHexLower =\n    make_array_with<256>(format_table_conv_make_item<16, 2, false>{});\nFOLLY_STORAGE_CONSTEXPR decltype(formatHexUpper) formatHexUpper =\n    make_array_with<256>(format_table_conv_make_item<16, 2, true>{});\nFOLLY_STORAGE_CONSTEXPR decltype(formatOctal) formatOctal =\n    make_array_with<512>(format_table_conv_make_item<8, 3>{});\nFOLLY_STORAGE_CONSTEXPR decltype(formatBinary) formatBinary =\n    make_array_with<256>(format_table_conv_make_item<2, 8>{});\n\n} // namespace detail\n\nusing namespace folly::detail;\n\nvoid FormatValue<double>::formatHelper(\n    fbstring& piece, int& prefixLen, FormatArg& arg) const {\n  using ::double_conversion::DoubleToStringConverter;\n  using ::double_conversion::StringBuilder;\n\n  arg.validate(FormatArg::Type::FLOAT);\n\n  if (arg.presentation == FormatArg::kDefaultPresentation) {\n    arg.presentation = 'g';\n  }\n\n  const char* infinitySymbol = isupper(arg.presentation) ? \"INF\" : \"inf\";\n  const char* nanSymbol = isupper(arg.presentation) ? \"NAN\" : \"nan\";\n  char exponentSymbol = isupper(arg.presentation) ? 'E' : 'e';\n\n  if (arg.precision == FormatArg::kDefaultPrecision) {\n    arg.precision = 6;\n  }\n\n  // 2+: for null terminator and optional sign shenanigans.\n  constexpr int bufLen =\n      2 +\n      constexpr_max(\n          2 + DoubleToStringConverter::kMaxFixedDigitsBeforePoint +\n              DoubleToStringConverter::kMaxFixedDigitsAfterPoint,\n          constexpr_max(\n              8 + DoubleToStringConverter::kMaxExponentialDigits,\n              7 + DoubleToStringConverter::kMaxPrecisionDigits));\n  char buf[bufLen];\n  StringBuilder builder(buf + 1, bufLen - 1);\n\n  char plusSign;\n  switch (arg.sign) {\n    case FormatArg::Sign::PLUS_OR_MINUS:\n      plusSign = '+';\n      break;\n    case FormatArg::Sign::SPACE_OR_MINUS:\n      plusSign = ' ';\n      break;\n    case FormatArg::Sign::DEFAULT:\n    case FormatArg::Sign::MINUS:\n    case FormatArg::Sign::INVALID:\n    default:\n      plusSign = '\\0';\n      break;\n  }\n\n  auto flags = DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN |\n      (arg.trailingDot ? DoubleToStringConverter::EMIT_TRAILING_DECIMAL_POINT\n                       : 0);\n\n  double val = val_;\n  switch (arg.presentation) {\n    case '%':\n      val *= 100;\n      [[fallthrough]];\n    case 'f':\n    case 'F': {\n      if (arg.precision > DoubleToStringConverter::kMaxFixedDigitsAfterPoint) {\n        arg.precision = DoubleToStringConverter::kMaxFixedDigitsAfterPoint;\n      }\n      DoubleToStringConverter conv(\n          flags,\n          infinitySymbol,\n          nanSymbol,\n          exponentSymbol,\n          -4,\n          arg.precision,\n          0,\n          0);\n      arg.enforce(\n          conv.ToFixed(val, arg.precision, &builder),\n          \"fixed double conversion failed\");\n      break;\n    }\n    case 'e':\n    case 'E': {\n      if (arg.precision > DoubleToStringConverter::kMaxExponentialDigits) {\n        arg.precision = DoubleToStringConverter::kMaxExponentialDigits;\n      }\n\n      DoubleToStringConverter conv(\n          flags,\n          infinitySymbol,\n          nanSymbol,\n          exponentSymbol,\n          -4,\n          arg.precision,\n          0,\n          0);\n      arg.enforce(conv.ToExponential(val, arg.precision, &builder));\n      break;\n    }\n    case 'n': // should be locale-aware, but isn't\n    case 'g':\n    case 'G': {\n      if (arg.precision < DoubleToStringConverter::kMinPrecisionDigits) {\n        arg.precision = DoubleToStringConverter::kMinPrecisionDigits;\n      } else if (arg.precision > DoubleToStringConverter::kMaxPrecisionDigits) {\n        arg.precision = DoubleToStringConverter::kMaxPrecisionDigits;\n      }\n      DoubleToStringConverter conv(\n          flags,\n          infinitySymbol,\n          nanSymbol,\n          exponentSymbol,\n          -4,\n          arg.precision,\n          0,\n          0);\n      arg.enforce(conv.ToShortest(val, &builder));\n      break;\n    }\n    default:\n      arg.error(\"invalid specifier '\", arg.presentation, \"'\");\n  }\n\n  auto len = builder.position();\n  builder.Finalize();\n  assert(len > 0);\n\n  // Add '+' or ' ' sign if needed\n  char* p = buf + 1;\n  // anything that's neither negative nor nan\n  prefixLen = 0;\n  if (plusSign && (*p != '-' && *p != 'n' && *p != 'N')) {\n    *--p = plusSign;\n    ++len;\n    prefixLen = 1;\n  } else if (*p == '-') {\n    prefixLen = 1;\n  }\n\n  piece = fbstring(p, size_t(len));\n}\n\nvoid FormatArg::initSlow() {\n  auto b = fullArgString.begin();\n  auto end = fullArgString.end();\n\n  // Parse key\n  auto p = static_cast<const char*>(memchr(b, ':', size_t(end - b)));\n  if (!p) {\n    key_ = StringPiece(b, end);\n    return;\n  }\n  key_ = StringPiece(b, p);\n\n  if (*p == ':') {\n    // parse format spec\n    if (++p == end) {\n      return;\n    }\n\n    // fill/align, or just align\n    Align a;\n    if (p + 1 != end &&\n        (a = formatAlignTable[static_cast<unsigned char>(p[1])]) !=\n            Align::INVALID) {\n      fill = *p;\n      align = a;\n      p += 2;\n      if (p == end) {\n        return;\n      }\n    } else if (\n        (a = formatAlignTable[static_cast<unsigned char>(*p)]) !=\n        Align::INVALID) {\n      align = a;\n      if (++p == end) {\n        return;\n      }\n    }\n\n    Sign s;\n    auto uSign = static_cast<unsigned char>(*p);\n    if ((s = formatSignTable[uSign]) != Sign::INVALID) {\n      sign = s;\n      if (++p == end) {\n        return;\n      }\n    }\n\n    if (*p == '#') {\n      basePrefix = true;\n      if (++p == end) {\n        return;\n      }\n    }\n\n    if (*p == '0') {\n      enforce(align == Align::DEFAULT, \"alignment specified twice\");\n      fill = '0';\n      align = Align::PAD_AFTER_SIGN;\n      if (++p == end) {\n        return;\n      }\n    }\n\n    auto readInt = [&] {\n      auto const c = p;\n      do {\n        ++p;\n      } while (p != end && *p >= '0' && *p <= '9');\n      return to<int>(StringPiece(c, p));\n    };\n\n    if (*p == '*') {\n      width = kDynamicWidth;\n      ++p;\n\n      if (p == end) {\n        return;\n      }\n\n      if (*p >= '0' && *p <= '9') {\n        widthIndex = readInt();\n      }\n\n      if (p == end) {\n        return;\n      }\n    } else if (*p >= '0' && *p <= '9') {\n      width = readInt();\n\n      if (p == end) {\n        return;\n      }\n    }\n\n    if (*p == ',') {\n      thousandsSeparator = true;\n      if (++p == end) {\n        return;\n      }\n    }\n\n    if (*p == '.') {\n      auto d = ++p;\n      while (p != end && *p >= '0' && *p <= '9') {\n        ++p;\n      }\n      if (p != d) {\n        precision = to<int>(StringPiece(d, p));\n        if (p != end && *p == '.') {\n          trailingDot = true;\n          ++p;\n        }\n      } else {\n        trailingDot = true;\n      }\n\n      if (p == end) {\n        return;\n      }\n    }\n\n    presentation = *p;\n    if (++p == end) {\n      return;\n    }\n  }\n\n  error(\"extra characters in format string\");\n}\n\nvoid FormatArg::validate(Type type) const {\n  enforce(keyEmpty(), \"index not allowed\");\n  switch (type) {\n    case Type::INTEGER:\n      enforce(\n          precision == kDefaultPrecision, \"precision not allowed on integers\");\n      break;\n    case Type::FLOAT:\n      enforce(\n          !basePrefix, \"base prefix ('#') specifier only allowed on integers\");\n      enforce(\n          !thousandsSeparator,\n          \"thousands separator (',') only allowed on integers\");\n      break;\n    case Type::OTHER:\n      enforce(\n          align != Align::PAD_AFTER_SIGN,\n          \"'='alignment only allowed on numbers\");\n      enforce(sign == Sign::DEFAULT, \"sign specifier only allowed on numbers\");\n      enforce(\n          !basePrefix, \"base prefix ('#') specifier only allowed on integers\");\n      enforce(\n          !thousandsSeparator,\n          \"thousands separator (',') only allowed on integers\");\n      break;\n  }\n}\n\nnamespace detail {\nvoid insertThousandsGroupingUnsafe(char* start_buffer, char** end_buffer) {\n  auto remaining_digits = uint32_t(*end_buffer - start_buffer);\n  uint32_t separator_size = (remaining_digits - 1) / 3;\n  uint32_t result_size = remaining_digits + separator_size;\n  *end_buffer = *end_buffer + separator_size;\n\n  // get the end of the new string with the separators\n  uint32_t buffer_write_index = result_size - 1;\n  uint32_t buffer_read_index = remaining_digits - 1;\n  start_buffer[buffer_write_index + 1] = 0;\n\n  bool done = false;\n  uint32_t next_group_size = 3;\n\n  while (!done) {\n    uint32_t current_group_size = std::max<uint32_t>(\n        1, std::min<uint32_t>(remaining_digits, next_group_size));\n\n    // write out the current group's digits to the buffer index\n    for (uint32_t i = 0; i < current_group_size; i++) {\n      start_buffer[buffer_write_index--] = start_buffer[buffer_read_index--];\n    }\n\n    // if not finished, write the separator before the next group\n    if (buffer_write_index < buffer_write_index + 1) {\n      start_buffer[buffer_write_index--] = ',';\n    } else {\n      done = true;\n    }\n\n    remaining_digits -= current_group_size;\n  }\n}\n} // namespace detail\n\nFormatKeyNotFoundException::FormatKeyNotFoundException(StringPiece key)\n    : std::out_of_range(kMessagePrefix.str() + key.str()) {}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Format.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_format\n//\n\n/**\n * folly::format has been superseded by\n * [fmt](https://fmt.dev/latest/index.html). `#include <fmt/core.h>`\n *\n * format() performs text-formatting, similar to Python's str.format. The full\n * specification is on github:\n * https://github.com/facebook/folly/blob/main/folly/docs/Format.md\n *\n * @refcode folly/docs/examples/folly/Format.cpp\n * @file Format.h\n */\n\n#pragma once\n#define FOLLY_FORMAT_H_\n\n#include <cstdio>\n#include <ios>\n#include <stdexcept>\n#include <tuple>\n#include <type_traits>\n\n#include <folly/CPortability.h>\n#include <folly/Conv.h>\n#include <folly/FormatArg.h>\n#include <folly/Range.h>\n#include <folly/String.h>\n#include <folly/Traits.h>\n\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n\nnamespace folly {\n\n// forward declarations\ntemplate <bool containerMode, class... Args>\nclass Formatter;\ntemplate <class... Args>\nFormatter<false, Args...> format(StringPiece fmt, Args&&... args);\ntemplate <class T, class Enable = void>\nclass FormatValue;\n\n// meta-attribute to identify formatters in this sea of template weirdness\nnamespace detail {\nclass FormatterTag {};\n\nstruct BaseFormatterBase {\n  template <class Callback>\n  using DoFormatFn = void(const BaseFormatterBase&, FormatArg&, Callback&);\n\n  StringPiece str_;\n\n  static std::false_type recordUsedArg(const BaseFormatterBase&, size_t) {\n    return {};\n  }\n};\n\n// BaseFormatterTuple suffices and is faster to compile than is std::tuple\ntemplate <size_t I, typename A>\nstruct BaseFormatterTupleIndexedValue {\n  A value;\n};\ntemplate <typename, typename...>\nstruct BaseFormatterTuple;\ntemplate <size_t... I, typename... A>\nstruct BaseFormatterTuple<std::index_sequence<I...>, A...>\n    : BaseFormatterTupleIndexedValue<I, A>... {\n  explicit BaseFormatterTuple(std::in_place_t, A&&... a)\n      : BaseFormatterTupleIndexedValue<I, A>{static_cast<A&&>(a)}... {}\n};\n\ntemplate <typename Str>\nstruct BaseFormatterAppendToString {\n  Str& str;\n  void operator()(StringPiece s) const { str.append(s.data(), s.size()); }\n};\n\ninline void formatCheckIndex(size_t i, const FormatArg& arg, size_t max) {\n  arg.enforce(i < max, \"argument index out of range, max=\", max);\n}\n} // namespace detail\n\n/**\n * Formatter class.\n *\n * Note that this class is tricky, as it keeps *references* to its lvalue\n * arguments (while it takes ownership of the temporaries), and it doesn't\n * copy the passed-in format string. Thankfully, you can't use this\n * directly, you have to use format(...) below.\n */\n\n/* BaseFormatter class.\n * Overridable behaviors:\n * You may override the actual formatting of positional parameters in\n * `doFormatArg`. The Formatter class provides the default implementation.\n *\n * You may also override `recordUsedArg`. This override point was added to\n * permit static analysis of format strings, when it is inconvenient or\n * impossible to instantiate a BaseFormatter with the correct storage. If\n * overriding, the return type must be std::true_type.\n */\ntemplate <class Derived, bool containerMode, class Indices, class... Args>\nclass BaseFormatterImpl;\ntemplate <class Derived, bool containerMode, size_t... I, class... Args>\nclass BaseFormatterImpl<\n    Derived,\n    containerMode,\n    std::index_sequence<I...>,\n    Args...> : public detail::BaseFormatterBase {\n public:\n  /**\n   * Append to output.  out(StringPiece sp) may be called (more than once)\n   */\n  template <class Output>\n  void operator()(Output& out) const;\n\n  /**\n   * Append to a string.\n   */\n  template <class Str>\n  typename std::enable_if<IsSomeString<Str>::value>::type appendTo(\n      Str& str) const {\n    detail::BaseFormatterAppendToString<Str> out{str};\n    (*this)(out);\n  }\n\n  /**\n   * Conversion to string\n   */\n  std::string str() const {\n    std::string s;\n    appendTo(s);\n    return s;\n  }\n\n  /**\n   * Metadata to identify generated children of BaseFormatter\n   */\n  using IsFormatter = detail::FormatterTag;\n\n private:\n  template <typename T, typename D = typename std::decay<T>::type>\n  using IsSizeable = std::bool_constant<\n      std::is_integral<D>::value && !std::is_same<D, bool>::value>;\n\n  template <class Callback>\n  static constexpr c_array<DoFormatFn<Callback>*, sizeof...(Args) + 1>\n  getDoFormatFnArray() {\n    return {{Derived::template doFormatArg<I, Callback>..., nullptr}};\n  }\n\n  template <size_t, typename>\n  constexpr int getSizeArgAt(std::false_type) const {\n    return -1;\n  }\n  template <size_t K, typename T>\n  int getSizeArgAt(std::true_type) const {\n    using V = detail::BaseFormatterTupleIndexedValue<K, T>;\n    return static_cast<int>(static_cast<const V&>(values_).value);\n  }\n  void getSizeArg(int* out) const {\n    ((out[I] = getSizeArgAt<I, Args>(IsSizeable<Args>{})), ...);\n  }\n\n protected:\n  explicit BaseFormatterImpl(StringPiece str, Args&&... args)\n      : detail::BaseFormatterBase{str},\n        values_(std::in_place, static_cast<Args&&>(args)...) {}\n\n  // Not copyable\n  BaseFormatterImpl(const BaseFormatterImpl&) = delete;\n  BaseFormatterImpl& operator=(const BaseFormatterImpl&) = delete;\n\n  // Movable, but the move constructor and assignment operator are private,\n  // for the exclusive use of format() (below).  This way, you can't create\n  // a Formatter object, but can handle references to it (for streaming,\n  // conversion to string, etc) -- which is good, as Formatter objects are\n  // dangerous (they may hold references).\n  BaseFormatterImpl(BaseFormatterImpl&&) = default;\n  BaseFormatterImpl& operator=(BaseFormatterImpl&&) = default;\n\n  template <size_t K, typename T = type_pack_element_t<K, Args...>>\n  FormatValue<typename std::decay<T>::type> getFormatValue() const {\n    using V = detail::BaseFormatterTupleIndexedValue<K, T>;\n    auto& v = static_cast<const V&>(values_);\n    return FormatValue<typename std::decay<T>::type>(v.value);\n  }\n\n  detail::BaseFormatterTuple<std::index_sequence<I...>, Args...> values_;\n};\ntemplate <class Derived, bool containerMode, class... Args>\nusing BaseFormatter = BaseFormatterImpl<\n    Derived,\n    containerMode,\n    std::index_sequence_for<Args...>,\n    Args...>;\n\ntemplate <bool containerMode, class... Args>\nclass Formatter\n    : public BaseFormatter<\n          Formatter<containerMode, Args...>,\n          containerMode,\n          Args...> {\n  using self = Formatter<containerMode, Args...>;\n  using base = BaseFormatter<self, containerMode, Args...>;\n\n  static_assert(\n      !containerMode || sizeof...(Args) == 1,\n      \"Exactly one argument required in container mode\");\n\n private:\n  using base::base;\n\n  template <size_t K, class Callback>\n  static void doFormatArg(\n      const detail::BaseFormatterBase& obj, FormatArg& arg, Callback& cb) {\n    auto& d = static_cast<const Formatter&>(obj);\n    d.template getFormatValue<K>().format(arg, cb);\n  }\n\n  friend base;\n\n  template <class... A>\n  friend Formatter<false, A...> format(StringPiece fmt, A&&... arg);\n  template <class Str, class... A>\n  friend typename std::enable_if<IsSomeString<Str>::value>::type format(\n      Str* out, StringPiece fmt, A&&... args);\n  template <class... A>\n  friend std::string sformat(StringPiece fmt, A&&... arg);\n};\n\nnamespace detail {\ntemplate <typename Out>\nstruct FormatterOstreamInsertionWriterFn {\n  Out& out;\n  void operator()(StringPiece sp) const {\n    out.write(sp.data(), std::streamsize(sp.size()));\n  }\n};\n} // namespace detail\n\n/**\n * Formatter objects can be written to streams.\n */\ntemplate <class C, class CT, bool containerMode, class... Args>\nstd::ostream& operator<<(\n    std::basic_ostream<C, CT>& out,\n    const Formatter<containerMode, Args...>& formatter) {\n  using out_t = std::basic_ostream<C, CT>;\n  auto writer = detail::FormatterOstreamInsertionWriterFn<out_t>{out};\n  formatter(writer);\n  return out;\n}\n\n/**\n * Create a formatter object.\n *\n * std::string formatted = format(\"{} {}\", 23, 42).str();\n * LOG(INFO) << format(\"{} {}\", 23, 42);\n * writeTo(stdout, format(\"{} {}\", 23, 42));\n */\ntemplate <class... Args>\n[[deprecated(\n    \"Use fmt::format instead of folly::format for better performance, build \"\n    \"times and compatibility with std::format\")]] //\nFormatter<false, Args...> format(StringPiece fmt, Args&&... args) {\n  return Formatter<false, Args...>(fmt, static_cast<Args&&>(args)...);\n}\n\n/**\n * Like format(), but immediately returns the formatted string instead of an\n * intermediate format object.\n */\ntemplate <class... Args>\ninline std::string sformat(StringPiece fmt, Args&&... args) {\n  return Formatter<false, Args...>(fmt, static_cast<Args&&>(args)...).str();\n}\n\n/**\n * Exception class thrown when a format key is not found in the given\n * associative container keyed by strings. We inherit std::out_of_range for\n * compatibility with callers that expect exception to be thrown directly\n * by std::map or std::unordered_map.\n *\n * Having the key be at the end of the message string, we can access it by\n * simply adding its offset to what(). Not storing separate std::string key\n * makes the exception type small and noexcept-copyable like std::out_of_range,\n * and therefore able to fit in-situ in exception_wrapper.\n */\nclass FOLLY_EXPORT FormatKeyNotFoundException : public std::out_of_range {\n public:\n  explicit FormatKeyNotFoundException(StringPiece key);\n\n  char const* key() const noexcept { return what() + kMessagePrefix.size(); }\n\n private:\n  static constexpr StringPiece const kMessagePrefix = \"format key not found: \";\n};\n\n/**\n * Wrap a sequence or associative container so that out-of-range lookups\n * return a default value rather than throwing an exception.\n *\n * Usage:\n * format(\"[no_such_key\"], defaulted(map, 42))  -> 42\n */\nnamespace detail {\ntemplate <class Container, class Value>\nstruct DefaultValueWrapper {\n  DefaultValueWrapper(const Container& container, const Value& defaultValue)\n      : container(container), defaultValue(defaultValue) {}\n\n  const Container& container;\n  const Value& defaultValue;\n};\n} // namespace detail\n\ntemplate <class Container, class Value>\ndetail::DefaultValueWrapper<Container, Value> defaulted(\n    const Container& c, const Value& v) {\n  return detail::DefaultValueWrapper<Container, Value>(c, v);\n}\n\n/**\n * Append formatted output to a string.\n *\n * std::string foo;\n * format(&foo, \"{} {}\", 42, 23);\n *\n * Shortcut for toAppend(format(...), &foo);\n */\ntemplate <class Str, class... Args>\ntypename std::enable_if<IsSomeString<Str>::value>::type format(\n    Str* out, StringPiece fmt, Args&&... args) {\n  Formatter<false, Args...>(fmt, static_cast<Args&&>(args)...).appendTo(*out);\n}\n\n/**\n * Utilities for all format value specializations.\n */\nnamespace format_value {\n\n/**\n * Format a string in \"val\", obeying appropriate alignment, padding, width,\n * and precision.  Treats Align::DEFAULT as Align::LEFT, and\n * Align::PAD_AFTER_SIGN as Align::RIGHT; use formatNumber for\n * number-specific formatting.\n */\ntemplate <class FormatCallback>\nvoid formatString(StringPiece val, FormatArg& arg, FormatCallback& cb);\n\n/**\n * Format a number in \"val\"; the first prefixLen characters form the prefix\n * (sign, \"0x\" base prefix, etc) which must be left-aligned if the alignment\n * is Align::PAD_AFTER_SIGN.  Treats Align::DEFAULT as Align::LEFT.  Ignores\n * arg.precision, as that has a different meaning for numbers (not \"maximum\n * field width\")\n */\ntemplate <class FormatCallback>\nvoid formatNumber(\n    StringPiece val, int prefixLen, FormatArg& arg, FormatCallback& cb);\n\n/**\n * Format a Formatter object recursively.  Behaves just like\n * formatString(fmt.str(), arg, cb); but avoids creating a temporary\n * string if possible.\n */\ntemplate <class FormatCallback, bool containerMode, class... Args>\nvoid formatFormatter(\n    const Formatter<containerMode, Args...>& formatter,\n    FormatArg& arg,\n    FormatCallback& cb);\n\n} // namespace format_value\n\n/*\n * Specialize folly::FormatValue for your type.\n *\n * FormatValue<T> is constructed with a (reference-collapsed) T&&, which is\n * guaranteed to stay alive until the FormatValue object is destroyed, so you\n * may keep a reference (or pointer) to it instead of making a copy.\n *\n * You must define\n *   template <class Callback>\n *   void format(FormatArg& arg, Callback& cb) const;\n * with the following semantics: format the value using the given argument.\n *\n * arg is given by non-const reference for convenience -- it won't be reused,\n * so feel free to modify it in place if necessary.  (For example, wrap an\n * existing conversion but change the default, or remove the \"key\" when\n * extracting an element from a container)\n *\n * Call the callback to append data to the output.  You may call the callback\n * as many times as you'd like (or not at all, if you want to output an\n * empty string)\n */\n\nnamespace detail {\n\ntemplate <class T, class Enable = void>\nstruct IsFormatter : public std::false_type {};\n\ntemplate <class T>\nstruct IsFormatter<\n    T,\n    typename std::enable_if<\n        std::is_same<typename T::IsFormatter, detail::FormatterTag>::value>::\n        type> : public std::true_type {};\n} // namespace detail\n\n} // namespace folly\n\n#include <folly/Format-inl.h>\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/FormatArg.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdexcept>\n\n#include <folly/CPortability.h>\n#include <folly/Conv.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nstruct FormatArg;\n\nclass FOLLY_EXPORT BadFormatArg : public std::invalid_argument {\n private:\n  friend struct FormatArg;\n  struct ErrorStrTag {};\n\n  template <typename... A>\n  static std::string str(StringPiece descr, A const&... a) {\n    return to<std::string>(\n        \"invalid format argument {\"_sp, descr, \"}: \"_sp, a...);\n  }\n\n public:\n  using invalid_argument::invalid_argument;\n  template <typename... A>\n  explicit BadFormatArg(ErrorStrTag, StringPiece descr, A const&... a)\n      : invalid_argument(str(descr, a...)) {}\n};\n\n/**\n * Parsed format argument.\n */\nstruct FormatArg {\n  /**\n   * Parse a format argument from a string.  Keeps a reference to the\n   * passed-in string -- does not copy the given characters.\n   */\n  explicit FormatArg(StringPiece sp)\n      : fullArgString(sp),\n        fill(kDefaultFill),\n        align(Align::DEFAULT),\n        sign(Sign::DEFAULT),\n        basePrefix(false),\n        thousandsSeparator(false),\n        trailingDot(false),\n        width(kDefaultWidth),\n        widthIndex(kNoIndex),\n        precision(kDefaultPrecision),\n        presentation(kDefaultPresentation),\n        nextKeyMode_(NextKeyMode::NONE) {\n    if (!sp.empty()) {\n      initSlow();\n    }\n  }\n\n  enum class Type {\n    INTEGER,\n    FLOAT,\n    OTHER,\n  };\n  /**\n   * Validate the argument for the given type; throws on error.\n   */\n  void validate(Type type) const;\n\n  /**\n   * Throw an exception if the first argument is false.  The exception\n   * message will contain the argument string as well as any passed-in\n   * arguments to enforce, formatted using folly::to<std::string>.\n   */\n  template <typename Check, typename... Args>\n  void enforce(Check const& v, Args&&... args) const {\n    static_assert(std::is_constructible<bool, Check>::value, \"not castable\");\n    if (FOLLY_UNLIKELY(!v)) {\n      error(static_cast<Args&&>(args)...);\n    }\n  }\n\n  template <typename... Args>\n  [[noreturn]] void error(Args&&... args) const;\n\n  /**\n   * Full argument string, as passed in to the constructor.\n   */\n  StringPiece fullArgString;\n\n  /**\n   * Fill\n   */\n  static constexpr char kDefaultFill = '\\0';\n  char fill;\n\n  /**\n   * Alignment\n   */\n  enum class Align : uint8_t {\n    DEFAULT,\n    LEFT,\n    RIGHT,\n    PAD_AFTER_SIGN,\n    CENTER,\n    INVALID,\n  };\n  Align align;\n\n  /**\n   * Sign\n   */\n  enum class Sign : uint8_t {\n    DEFAULT,\n    PLUS_OR_MINUS,\n    MINUS,\n    SPACE_OR_MINUS,\n    INVALID,\n  };\n  Sign sign;\n\n  /**\n   * Output base prefix (0 for octal, 0x for hex)\n   */\n  bool basePrefix;\n\n  /**\n   * Output thousands separator (comma)\n   */\n  bool thousandsSeparator;\n\n  /**\n   * Force a trailing decimal on doubles which could be rendered as ints\n   */\n  bool trailingDot;\n\n  /**\n   * Field width and optional argument index\n   */\n  static constexpr int kDefaultWidth = -1;\n  static constexpr int kDynamicWidth = -2;\n  static constexpr int kNoIndex = -1;\n  int width;\n  int widthIndex;\n\n  /**\n   * Precision\n   */\n  static constexpr int kDefaultPrecision = -1;\n  int precision;\n\n  /**\n   * Presentation\n   */\n  static constexpr char kDefaultPresentation = '\\0';\n  char presentation;\n\n  /**\n   * Split a key component from \"key\", which must be non-empty (an exception\n   * is thrown otherwise).\n   */\n  template <bool emptyOk = false>\n  StringPiece splitKey();\n\n  /**\n   * Is the entire key empty?\n   */\n  bool keyEmpty() const {\n    return nextKeyMode_ == NextKeyMode::NONE && key_.empty();\n  }\n\n  /**\n   * Split an key component from \"key\", which must be non-empty and a valid\n   * integer (an exception is thrown otherwise).\n   */\n  int splitIntKey();\n\n  void setNextIntKey(int val) {\n    assert(nextKeyMode_ == NextKeyMode::NONE);\n    nextKeyMode_ = NextKeyMode::INT;\n    nextIntKey_ = val;\n  }\n\n  void setNextKey(StringPiece val) {\n    assert(nextKeyMode_ == NextKeyMode::NONE);\n    nextKeyMode_ = NextKeyMode::STRING;\n    nextKey_ = val;\n  }\n\n private:\n  void initSlow();\n  template <bool emptyOk>\n  StringPiece doSplitKey();\n\n  StringPiece key_;\n  int nextIntKey_;\n  StringPiece nextKey_;\n  enum class NextKeyMode {\n    NONE,\n    INT,\n    STRING,\n  };\n  NextKeyMode nextKeyMode_;\n};\n\ntemplate <typename... Args>\n[[noreturn]] inline void FormatArg::error(Args&&... args) const {\n  // take advantage of throw_exception decaying char const (&)[N} to char const*\n  // as a special case of the facility\n  throw_exception<BadFormatArg>(\n      BadFormatArg::ErrorStrTag{}, fullArgString, static_cast<Args&&>(args)...);\n}\n\ntemplate <bool emptyOk>\ninline StringPiece FormatArg::splitKey() {\n  enforce(nextKeyMode_ != NextKeyMode::INT, \"integer key expected\");\n  return doSplitKey<emptyOk>();\n}\n\ntemplate <bool emptyOk>\ninline StringPiece FormatArg::doSplitKey() {\n  if (nextKeyMode_ == NextKeyMode::STRING) {\n    nextKeyMode_ = NextKeyMode::NONE;\n    if (!emptyOk) { // static\n      enforce(!nextKey_.empty(), \"non-empty key required\");\n    }\n    return nextKey_;\n  }\n\n  if (key_.empty()) {\n    if (!emptyOk) { // static\n      error(\"non-empty key required\");\n    }\n    return StringPiece();\n  }\n\n  const char* b = key_.begin();\n  const char* e = key_.end();\n  const char* p;\n  if (e[-1] == ']') {\n    --e;\n    p = static_cast<const char*>(memchr(b, '[', size_t(e - b)));\n    enforce(p != nullptr, \"unmatched ']'\");\n  } else {\n    p = static_cast<const char*>(memchr(b, '.', size_t(e - b)));\n  }\n  if (p) {\n    key_.assign(p + 1, e);\n  } else {\n    p = e;\n    key_.clear();\n  }\n  if (!emptyOk) { // static\n    enforce(b != p, \"non-empty key required\");\n  }\n  return StringPiece(b, p);\n}\n\ninline int FormatArg::splitIntKey() {\n  if (nextKeyMode_ == NextKeyMode::INT) {\n    nextKeyMode_ = NextKeyMode::NONE;\n    return nextIntKey_;\n  }\n  auto result = tryTo<int>(doSplitKey<true>());\n  enforce(result, \"integer key required\");\n  return *result;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/FormatTraits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\nnamespace folly {\nnamespace detail {\n\n// Shortcut, so we don't have to use enable_if everywhere\nstruct FormatTraitsBase {\n  using enabled = void;\n};\n\n// Traits that define enabled, value_type, and at() for anything\n// indexable with integral keys: pointers, arrays, vectors, and maps\n// with integral keys\ntemplate <class T, class Enable = void>\nstruct IndexableTraits;\n\n// Base class for sequences (vectors, deques)\ntemplate <class C>\nstruct IndexableTraitsSeq : public FormatTraitsBase {\n  using container_type = C;\n  using value_type = typename C::value_type;\n\n  static const value_type& at(const C& c, int idx) { return c.at(idx); }\n\n  static const value_type& at(const C& c, int idx, const value_type& dflt) {\n    return (idx >= 0 && size_t(idx) < c.size()) ? c.at(idx) : dflt;\n  }\n};\n\n// Base class for associative types (maps)\ntemplate <class C>\nstruct IndexableTraitsAssoc : public FormatTraitsBase {\n  using value_type = typename C::value_type::second_type;\n\n  static const value_type& at(const C& c, int idx) {\n    return c.at(static_cast<typename C::key_type>(idx));\n  }\n\n  static const value_type& at(const C& c, int idx, const value_type& dflt) {\n    auto pos = c.find(static_cast<typename C::key_type>(idx));\n    return pos != c.end() ? pos->second : dflt;\n  }\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/Function.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_function\n//\n\n/**\n * @class folly::Function\n * @refcode folly/docs/examples/folly/Function.cpp\n *\n * A polymorphic function wrapper that is not copyable and does not\n * require the wrapped function to be copy constructible.\n *\n * `folly::Function` is a polymorphic function wrapper, similar to\n * `std::function`. The template parameters of the `folly::Function` define\n * the parameter signature of the wrapped callable, but not the specific\n * type of the embedded callable. E.g. a `folly::Function<int(int)>`\n * can wrap callables that return an `int` when passed an `int`. This can be a\n * function pointer or any class object implementing one or both of\n *\n *     int operator(int);\n *     int operator(int) const;\n *\n * If both are defined, the non-const one takes precedence.\n *\n * Unlike `std::function`, a `folly::Function` can wrap objects that are not\n * copy constructible. As a consequence of this, `folly::Function` itself\n * is not copyable, either.\n *\n * Another difference is that, unlike `std::function`, `folly::Function` treats\n * const-ness of methods correctly. While a `std::function` allows to wrap\n * an object that only implements a non-const `operator()` and invoke\n * a const-reference of the `std::function`, `folly::Function` requires you to\n * declare a function type as const in order to be able to execute it on a\n * const-reference.\n *\n * For example:\n *\n *     class Foo {\n *      public:\n *       void operator()() {\n *         // mutates the Foo object\n *       }\n *     };\n *\n *     class Bar {\n *       std::function<void(void)> foo_; // wraps a Foo object\n *      public:\n *       void mutateFoo() const\n *       {\n *         foo_();\n *       }\n *     };\n *\n * Even though `mutateFoo` is a const-method, so it can only reference `foo_`\n * as const, it is able to call the non-const `operator()` of the Foo\n * object that is embedded in the foo_ function.\n *\n * `folly::Function` will not allow you to do that. You will have to decide\n * whether you need to invoke your wrapped callable from a const reference\n * (like in the example above), in which case it will only wrap a\n * `operator() const`. If your functor does not implement that,\n * compilation will fail. If you do not require to be able to invoke the\n * wrapped function in a const context, you can wrap any functor that\n * implements either or both of const and non-const `operator()`.\n *\n * The template parameter of `folly::Function`, the `FunctionType`, can be\n * const-qualified. Be aware that the const is part of the function signature.\n * It does not mean that the function type is a const type.\n *\n *   using FunctionType = R(Args...);\n *   using ConstFunctionType = R(Args...) const;\n *\n * In this example, `FunctionType` and `ConstFunctionType` are different\n * types. `ConstFunctionType` is not the same as `const FunctionType`.\n * As a matter of fact, trying to use the latter should emit a compiler\n * warning or error, because it has no defined meaning.\n *\n *     // This will not compile:\n *     folly::Function<void(void) const> func = Foo();\n *     // because Foo does not have a member function of the form:\n *     //   void operator()() const;\n *\n *     // This will compile just fine:\n *     folly::Function<void(void)> func = Foo();\n *     // and it will wrap the existing member function:\n *     //   void operator()();\n *\n * When should a const function type be used? As a matter of fact, you will\n * probably not need to use const function types very often. See the following\n * example:\n *\n *     class Bar {\n *       folly::Function<void()> func_;\n *       folly::Function<void() const> constFunc_;\n *\n *       void someMethod() {\n *         // Can call func_.\n *         func_();\n *         // Can call constFunc_.\n *         constFunc_();\n *       }\n *\n *       void someConstMethod() const {\n *         // Can call constFunc_.\n *         constFunc_();\n *         // However, cannot call func_ because a non-const method cannot\n *         // be called from a const one.\n *       }\n *     };\n *\n * As you can see, whether the `folly::Function`'s function type should\n * be declared const or not is identical to whether a corresponding method\n * would be declared const or not.\n *\n * You only require a `folly::Function` to hold a const function type, if you\n * intend to invoke it from within a const context. This is to ensure that\n * you cannot mutate its inner state when calling in a const context.\n *\n * This is how the const/non-const choice relates to lambda functions:\n *\n *     // Non-mutable lambdas: can be stored in a non-const...\n *     folly::Function<void(int)> print_number =\n *       [] (int number) { std::cout << number << std::endl; };\n *\n *     // ...as well as in a const folly::Function\n *     folly::Function<void(int) const> print_number_const =\n *       [] (int number) { std::cout << number << std::endl; };\n *\n *     // Mutable lambda: can only be stored in a non-const folly::Function:\n *     int number = 0;\n *     folly::Function<void()> print_number =\n *       [number] () mutable { std::cout << ++number << std::endl; };\n *     // Trying to store the above mutable lambda in a\n *     // `folly::Function<void() const>` would lead to a compiler error:\n *     // error: no viable conversion from '(lambda at ...)' to\n *     // 'folly::Function<void () const>'\n *\n * Casting between const and non-const `folly::Function`s:\n * conversion from const to non-const signatures happens implicitly. Any\n * function that takes a `folly::Function<R(Args...)>` can be passed\n * a `folly::Function<R(Args...) const>` without explicit conversion.\n * This is safe, because casting from const to non-const only entails giving\n * up the ability to invoke the function from a const context.\n * Casting from a non-const to a const signature is potentially dangerous,\n * as it means that a function that may change its inner state when invoked\n * is made possible to call from a const context. Therefore this cast does\n * not happen implicitly. The function `folly::constCastFunction` can\n * be used to perform the cast.\n *\n *     // Mutable lambda: can only be stored in a non-const folly::Function:\n *     int number = 0;\n *     folly::Function<void()> print_number =\n *       [number] () mutable { std::cout << ++number << std::endl; };\n *\n *     // const-cast to a const folly::Function:\n *     folly::Function<void() const> print_number_const =\n *       constCastFunction(std::move(print_number));\n *\n * When to use const function types?\n * Generally, only when you need them. When you use a `folly::Function` as a\n * member of a struct or class, only use a const function signature when you\n * need to invoke the function from const context.\n * When passing a `folly::Function` to a function, the function should accept\n * a non-const `folly::Function` whenever possible, i.e. when it does not\n * need to pass on or store a const `folly::Function`. This is the least\n * possible constraint: you can always pass a const `folly::Function` when\n * the function accepts a non-const one.\n *\n * How does the const behaviour compare to `std::function`?\n * `std::function` can wrap object with non-const invocation behaviour but\n * exposes them as const. The equivalent behaviour can be achieved with\n * `folly::Function` like so:\n *\n *     std::function<void(void)> stdfunc = someCallable;\n *\n *     folly::Function<void(void) const> uniqfunc = constCastFunction(\n *       folly::Function<void(void)>(someCallable)\n *     );\n *\n * You need to wrap the callable first in a non-const `folly::Function` to\n * select a non-const invoke operator (or the const one if no non-const one is\n * present), and then move it into a const `folly::Function` using\n * `constCastFunction`.\n */\n\n#pragma once\n\n#include <cstring>\n#include <functional>\n#include <memory>\n#include <new>\n#include <type_traits>\n#include <utility>\n\n#include <folly/CppAttributes.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/New.h>\n\nnamespace folly {\n\ntemplate <typename FunctionType>\nclass Function;\n\ntemplate <typename ReturnType, typename... Args>\nFunction<ReturnType(Args...) const> constCastFunction(\n    Function<ReturnType(Args...)>&&) noexcept;\n\ntemplate <typename ReturnType, typename... Args>\nFunction<ReturnType(Args...) const noexcept> constCastFunction(\n    Function<ReturnType(Args...) noexcept>&&) noexcept;\n\nnamespace detail {\nnamespace function {\n\nenum class Op { MOVE, NUKE, HEAP };\n\nunion Data {\n  struct BigTrivialLayout {\n    void* big;\n    std::size_t size;\n    std::size_t align;\n  };\n\n  void* big;\n  BigTrivialLayout bigt;\n  folly::aligned_storage_t<6 * sizeof(void*)> tiny;\n};\n\nstruct CoerceTag {};\n\ntemplate <typename T>\nusing FunctionNullptrTest =\n    decltype(static_cast<bool>(static_cast<T const&>(T(nullptr)) == nullptr));\n\ntemplate <typename T>\nconstexpr bool IsNullptrCompatible = is_detected_v<FunctionNullptrTest, T>;\n\ntemplate <typename T, std::enable_if_t<!IsNullptrCompatible<T>, int> = 0>\nconstexpr bool isEmptyFunction(T const&) {\n  return false;\n}\ntemplate <typename T, std::enable_if_t<IsNullptrCompatible<T>, int> = 0>\nconstexpr bool isEmptyFunction(T const& t) {\n  return static_cast<bool>(t == nullptr);\n}\n\ntemplate <typename F, typename... Args>\nusing CallableResult = decltype(FOLLY_DECLVAL(F&&)(FOLLY_DECLVAL(Args&&)...));\n\ntemplate <typename F, typename... Args>\nconstexpr bool CallableNoexcept =\n    noexcept(FOLLY_DECLVAL(F&&)(FOLLY_DECLVAL(Args&&)...));\n\ntemplate <\n    typename From,\n    typename To,\n    typename = typename std::enable_if<\n        !std::is_reference<To>::value || std::is_reference<From>::value>::type>\nusing IfSafeResultImpl = decltype(void(static_cast<To>(FOLLY_DECLVAL(From))));\n\n#if defined(_MSC_VER)\n//  Need a workaround for MSVC to avoid the inscrutable error:\n//\n//      folly\\function.h(...) : fatal error C1001: An internal error has\n//          occurred in the compiler.\n//      (compiler file 'f:\\dd\\vctools\\compiler\\utc\\src\\p2\\main.c', line 258)\n//      To work around this problem, try simplifying or changing the program\n//          near the locations listed above.\ntemplate <typename T>\nusing CallArg = T&&;\n#else\ntemplate <typename T>\nusing CallArg = conditional_t<is_register_pass_v<T>, T, T&&>;\n#endif\n\ntemplate <typename F, bool Nx, typename R, typename... A>\nclass FunctionTraitsSharedProxy {\n  std::shared_ptr<Function<F>> sp_;\n\n public:\n  explicit FunctionTraitsSharedProxy(std::nullptr_t) noexcept {}\n  explicit FunctionTraitsSharedProxy(Function<F>&& func)\n      : sp_(func ? std::make_shared<Function<F>>(std::move(func))\n                 : std::shared_ptr<Function<F>>()) {}\n  R operator()(A... args) const noexcept(Nx) {\n    if (!sp_) {\n      throw_exception<std::bad_function_call>();\n    }\n    return (*sp_)(static_cast<A&&>(args)...);\n  }\n\n  explicit operator bool() const noexcept { return sp_ != nullptr; }\n\n  friend bool operator==(\n      FunctionTraitsSharedProxy const& proxy, std::nullptr_t) noexcept {\n    return proxy.sp_ == nullptr;\n  }\n  friend bool operator!=(\n      FunctionTraitsSharedProxy const& proxy, std::nullptr_t) noexcept {\n    return proxy.sp_ != nullptr;\n  }\n\n  friend bool operator==(\n      std::nullptr_t, FunctionTraitsSharedProxy const& proxy) noexcept {\n    return proxy.sp_ == nullptr;\n  }\n  friend bool operator!=(\n      std::nullptr_t, FunctionTraitsSharedProxy const& proxy) noexcept {\n    return proxy.sp_ != nullptr;\n  }\n};\n\ntemplate <\n    typename Fun,\n    bool Small,\n    bool Nx,\n    typename ReturnType,\n    typename... Args>\nReturnType call_(Args... args, Data& p) noexcept(Nx) {\n  auto& fn = *static_cast<Fun*>(Small ? &p.tiny : p.big);\n  if constexpr (std::is_void<ReturnType>::value) {\n    fn(static_cast<Args&&>(args)...);\n  } else {\n    return fn(static_cast<Args&&>(args)...);\n  }\n}\n\ntemplate <typename FunctionType>\nstruct FunctionTraits;\n\ntemplate <typename ReturnType, typename... Args>\nstruct FunctionTraits<ReturnType(Args...)> {\n  using Call = ReturnType (*)(CallArg<Args>..., Data&);\n  using ConstSignature = ReturnType(Args...) const;\n  using NonConstSignature = ReturnType(Args...);\n  using OtherSignature = ConstSignature;\n\n  template <typename F, typename R = CallableResult<F&, Args...>>\n  using IfSafeResult = IfSafeResultImpl<R, ReturnType>;\n\n  template <typename Fun, bool Small>\n  static constexpr Call call =\n      call_<Fun, Small, false, ReturnType, CallArg<Args>...>;\n\n  static ReturnType uninitCall(CallArg<Args>..., Data&) {\n    throw_exception<std::bad_function_call>();\n  }\n\n  ReturnType operator()(Args... args) {\n    auto& fn = *static_cast<Function<NonConstSignature>*>(this);\n    return fn.call_(static_cast<Args&&>(args)..., fn.data_);\n  }\n\n  using SharedProxy =\n      FunctionTraitsSharedProxy<NonConstSignature, false, ReturnType, Args...>;\n};\n\ntemplate <typename ReturnType, typename... Args>\nstruct FunctionTraits<ReturnType(Args...) const> {\n  using Call = ReturnType (*)(CallArg<Args>..., Data&);\n  using ConstSignature = ReturnType(Args...) const;\n  using NonConstSignature = ReturnType(Args...);\n  using OtherSignature = NonConstSignature;\n\n  template <typename F, typename R = CallableResult<const F&, Args...>>\n  using IfSafeResult = IfSafeResultImpl<R, ReturnType>;\n\n  template <typename Fun, bool Small>\n  static constexpr Call call =\n      call_<const Fun, Small, false, ReturnType, CallArg<Args>...>;\n\n  static ReturnType uninitCall(CallArg<Args>..., Data&) {\n    throw_exception<std::bad_function_call>();\n  }\n\n  ReturnType operator()(Args... args) const {\n    auto& fn = *static_cast<const Function<ConstSignature>*>(this);\n    return fn.call_(static_cast<Args&&>(args)..., fn.data_);\n  }\n\n  using SharedProxy =\n      FunctionTraitsSharedProxy<ConstSignature, false, ReturnType, Args...>;\n};\n\ntemplate <typename ReturnType, typename... Args>\nstruct FunctionTraits<ReturnType(Args...) noexcept> {\n  using Call = ReturnType (*)(CallArg<Args>..., Data&) noexcept;\n  using ConstSignature = ReturnType(Args...) const noexcept;\n  using NonConstSignature = ReturnType(Args...) noexcept;\n  using OtherSignature = ConstSignature;\n\n  template <\n      typename F,\n      typename R = CallableResult<F&, Args...>,\n      std::enable_if_t<CallableNoexcept<F&, Args...>, int> = 0>\n  using IfSafeResult = IfSafeResultImpl<R, ReturnType>;\n\n  template <typename Fun, bool Small>\n  static constexpr Call call =\n      call_<Fun, Small, true, ReturnType, CallArg<Args>...>;\n\n  static ReturnType uninitCall(CallArg<Args>..., Data&) noexcept {\n    terminate_with<std::bad_function_call>();\n  }\n\n  ReturnType operator()(Args... args) noexcept {\n    auto& fn = *static_cast<Function<NonConstSignature>*>(this);\n    return fn.call_(static_cast<Args&&>(args)..., fn.data_);\n  }\n\n  using SharedProxy =\n      FunctionTraitsSharedProxy<NonConstSignature, true, ReturnType, Args...>;\n};\n\ntemplate <typename ReturnType, typename... Args>\nstruct FunctionTraits<ReturnType(Args...) const noexcept> {\n  using Call = ReturnType (*)(CallArg<Args>..., Data&) noexcept;\n  using ConstSignature = ReturnType(Args...) const noexcept;\n  using NonConstSignature = ReturnType(Args...) noexcept;\n  using OtherSignature = NonConstSignature;\n\n  template <\n      typename F,\n      typename R = CallableResult<const F&, Args...>,\n      std::enable_if_t<CallableNoexcept<const F&, Args...>, int> = 0>\n  using IfSafeResult = IfSafeResultImpl<R, ReturnType>;\n\n  template <typename Fun, bool Small>\n  static constexpr Call call =\n      call_<const Fun, Small, true, ReturnType, CallArg<Args>...>;\n\n  static ReturnType uninitCall(CallArg<Args>..., Data&) noexcept {\n    terminate_with<std::bad_function_call>();\n  }\n\n  ReturnType operator()(Args... args) const noexcept {\n    auto& fn = *static_cast<const Function<ConstSignature>*>(this);\n    return fn.call_(static_cast<Args&&>(args)..., fn.data_);\n  }\n\n  using SharedProxy =\n      FunctionTraitsSharedProxy<ConstSignature, true, ReturnType, Args...>;\n};\n\n// These are control functions. They type-erase the operations of move-\n// construction, destruction, and conversion to bool.\n//\n// The interface operations are noexcept, so the implementations are as well.\n// Having the implementations be noexcept in the type permits callers to omit\n// exception-handling machinery.\n//\n// This is intentionally instantiated per size rather than per function in order\n// to minimize the number of instantiations. It would be safe to minimize\n// instantiations even more by simply having a single non-template function that\n// copies sizeof(Data) bytes rather than only copying sizeof(Fun) bytes, but\n// then for small function types it would be likely to cross cache lines without\n// need. But it is only necessary to handle those sizes which are multiples of\n// the alignof(Data), and to round up other sizes.\nstruct DispatchSmallTrivial {\n  static constexpr bool is_in_situ = true;\n  static constexpr bool is_trivial = true;\n\n  template <std::size_t Size>\n  static std::size_t exec_(Op o, Data* src, Data* dst) noexcept {\n    switch (o) {\n      case Op::MOVE:\n        std::memcpy(static_cast<void*>(dst), static_cast<void*>(src), Size);\n        break;\n      case Op::NUKE:\n        break;\n      case Op::HEAP:\n        break;\n      default: /* unexpected */\n        abort();\n    }\n    return 0U;\n  }\n  template <std::size_t size, std::size_t adjust = alignof(Data) - 1>\n  static constexpr std::size_t size_ = (size + adjust) & ~adjust;\n  template <typename Fun>\n  static constexpr auto exec = exec_<size_<sizeof(Fun)>>;\n};\n\nstruct DispatchBigTrivial {\n  static constexpr bool is_in_situ = false;\n  static constexpr bool is_trivial = true;\n\n  template <typename Fun, typename Base>\n  static constexpr auto call = Base::template callBig<Fun>;\n\n  static constexpr bool is_align_large(size_t align) {\n    return align > __STDCPP_DEFAULT_NEW_ALIGNMENT__;\n  }\n\n  template <bool IsAlignLarge>\n  static std::size_t exec_(Op o, Data* src, Data* dst) noexcept {\n    switch (o) {\n      case Op::MOVE:\n        dst->bigt = src->bigt;\n        src->bigt = {};\n        break;\n      case Op::NUKE:\n        IsAlignLarge\n            ? operator_delete(\n                  src->big, src->bigt.size, std::align_val_t(src->bigt.align))\n            : operator_delete(src->big, src->bigt.size);\n        break;\n      case Op::HEAP:\n        break;\n      default: /* unexpected */\n        abort();\n    }\n    return src->bigt.size;\n  }\n  template <typename T>\n  static constexpr auto exec = exec_<is_align_large(alignof(T))>;\n\n  FOLLY_ALWAYS_INLINE static void ctor(\n      Data& data,\n      void const* fun,\n      std::size_t size,\n      std::size_t align) noexcept {\n    // cannot use type-specific new since type-specific new is overrideable\n    // in concert with type-specific delete\n    data.bigt.big = is_align_large(align)\n        ? operator_new(size, std::align_val_t(align))\n        : operator_new(size);\n    data.bigt.size = size;\n    data.bigt.align = align;\n    std::memcpy(data.bigt.big, fun, size);\n  }\n};\n\nstruct DispatchSmall {\n  static constexpr bool is_in_situ = true;\n  static constexpr bool is_trivial = false;\n\n  template <typename Fun>\n  static std::size_t exec(Op o, Data* src, Data* dst) noexcept {\n    switch (o) {\n      case Op::MOVE:\n        ::new (static_cast<void*>(&dst->tiny))\n            Fun(static_cast<Fun&&>(\n                *static_cast<Fun*>(static_cast<void*>(&src->tiny))));\n        [[fallthrough]];\n      case Op::NUKE:\n        static_cast<Fun*>(static_cast<void*>(&src->tiny))->~Fun();\n        break;\n      case Op::HEAP:\n        break;\n      default: /* unexpected */\n        abort();\n    }\n    return 0U;\n  }\n};\n\nstruct DispatchBig {\n  static constexpr bool is_in_situ = false;\n  static constexpr bool is_trivial = false;\n\n  template <typename Fun>\n  static std::size_t exec(Op o, Data* src, Data* dst) noexcept {\n    switch (o) {\n      case Op::MOVE:\n        dst->big = src->big;\n        src->big = nullptr;\n        break;\n      case Op::NUKE:\n        delete static_cast<Fun*>(src->big);\n        break;\n      case Op::HEAP:\n        break;\n      default: /* unexpected */\n        abort();\n    }\n    return sizeof(Fun);\n  }\n};\n\ntemplate <bool InSitu, bool IsTriv>\nstruct Dispatch;\ntemplate <>\nstruct Dispatch<true, true> : DispatchSmallTrivial {};\ntemplate <>\nstruct Dispatch<true, false> : DispatchSmall {};\ntemplate <>\nstruct Dispatch<false, true> : DispatchBigTrivial {};\ntemplate <>\nstruct Dispatch<false, false> : DispatchBig {};\n\ntemplate <\n    typename Fun,\n    bool InSituSize = sizeof(Fun) <= sizeof(Data),\n    bool InSituAlign = alignof(Fun) <= alignof(Data),\n    bool InSituNoexcept = noexcept(Fun(FOLLY_DECLVAL(Fun)))>\nusing DispatchOf = Dispatch<\n    InSituSize && InSituAlign && InSituNoexcept,\n    std::is_trivially_copyable_v<Fun>>;\n\n// This cannot be done inside `Function` class, because the word\n// `Function` there refers to the instantiation and not the template.\ntemplate <typename T>\nconstexpr bool is_instantiation_of_folly_function_v =\n    is_instantiation_of_v<Function, T>;\n\n} // namespace function\n} // namespace detail\n\ntemplate <typename FunctionType>\nclass Function final : private detail::function::FunctionTraits<FunctionType> {\n  // These utility types are defined outside of the template to reduce\n  // the number of instantiations, and then imported in the class\n  // namespace for convenience.\n  using Data = detail::function::Data;\n  using Op = detail::function::Op;\n  using CoerceTag = detail::function::CoerceTag;\n\n  using Traits = detail::function::FunctionTraits<FunctionType>;\n  using Call = typename Traits::Call;\n  using Exec = std::size_t (*)(Op, Data*, Data*) noexcept;\n\n  // The `data_` member is mutable to allow `constCastFunction` to work without\n  // invoking undefined behavior. Const-correctness is only violated when\n  // `FunctionType` is a const function type (e.g., `int() const`) and `*this`\n  // is the result of calling `constCastFunction`.\n  mutable Data data_{unsafe_default_initialized};\n  Call call_{&Traits::uninitCall};\n  Exec exec_{nullptr};\n\n  std::size_t exec(Op o, Data* src, Data* dst) const {\n    if (!exec_) {\n      return 0U;\n    }\n    return exec_(o, src, dst);\n  }\n\n  friend Traits;\n  friend Function<typename Traits::ConstSignature> folly::constCastFunction<>(\n      Function<typename Traits::NonConstSignature>&&) noexcept;\n  friend class Function<typename Traits::OtherSignature>;\n\n  template <typename Signature>\n  Function(Function<Signature>&& fun, CoerceTag) {\n    using Fun = Function<Signature>;\n    if (fun) {\n      data_.big = new Fun(static_cast<Fun&&>(fun));\n      call_ = Traits::template call<Fun, false>;\n      exec_ = Exec(detail::function::DispatchBig::exec<Fun>);\n    }\n  }\n\n  Function(Function<typename Traits::OtherSignature>&& that, CoerceTag) noexcept\n      : call_(that.call_), exec_(that.exec_) {\n    that.call_ = &Traits::uninitCall;\n    that.exec_ = nullptr;\n    exec(Op::MOVE, &that.data_, &data_);\n  }\n\n public:\n  /**\n   * Default constructor. Constructs an empty Function.\n   */\n  constexpr Function() = default;\n\n  // not copyable\n  Function(const Function&) = delete;\n\n#ifdef __OBJC__\n  // Make sure Objective C blocks are copied\n  template <class ReturnType, class... Args>\n  /*implicit*/ Function(ReturnType (^objCBlock)(Args... args))\n      : Function([blockCopy = (ReturnType(^)(Args...))[objCBlock copy]](\n                     Args... args) { return blockCopy(args...); }) {}\n#endif\n\n  /**\n   * Move constructor\n   */\n  Function(Function&& that) noexcept : call_(that.call_), exec_(that.exec_) {\n    // that must be uninitialized before exec() call in the case of self move\n    that.call_ = &Traits::uninitCall;\n    that.exec_ = nullptr;\n    exec(Op::MOVE, &that.data_, &data_);\n  }\n\n  /**\n   * Constructs an empty `Function`.\n   */\n  /* implicit */ constexpr Function(std::nullptr_t) noexcept {}\n\n  /**\n   * Constructs a new `Function` from any callable object that is _not_ a\n   * `folly::Function`.\n   *\n   * \\note `typename Traits::template IfSafeResult<Fun>` prevents this overload\n   * from being selected by overload resolution when `fun` is not a compatible\n   * function.\n   *\n   * \\note The noexcept requires some explanation. `is_in_situ` is true when the\n   * decayed type fits within the internal buffer and is noexcept-movable. But\n   * this ctor might copy, not move. What we need here, if this ctor does a\n   * copy, is that this ctor be noexcept when the copy is noexcept. That is not\n   * checked in `is_in_situ`, and shouldn't be, because once the `Function` is\n   * constructed, the contained object is never copied. This check is for this\n   * ctor only, in the case that this ctor does a copy.\n   *\n   * @param fun function pointers, pointers to static\n   *     member functions, `std::reference_wrapper` objects, `std::function`\n   *     objects, and arbitrary objects that implement `operator()` if the\n   * parameter signature matches (i.e. it returns an object convertible to `R`\n   * when called with `Args...`).\n   */\n  template <\n      typename Fun,\n      typename = std::enable_if_t<\n          !detail::function::is_instantiation_of_folly_function_v<Fun>>,\n      typename = typename Traits::template IfSafeResult<Fun>>\n  /* implicit */ constexpr Function(Fun fun) noexcept(\n      detail::function::DispatchOf<Fun>::is_in_situ) {\n    using Dispatch = detail::function::DispatchOf<Fun>;\n    if constexpr (detail::function::IsNullptrCompatible<Fun>) {\n      if (detail::function::isEmptyFunction(fun)) {\n        return;\n      }\n    }\n    if constexpr (Dispatch::is_in_situ) {\n      if constexpr (\n          !std::is_empty<Fun>::value || !std::is_trivially_copyable_v<Fun>) {\n        ::new (&data_.tiny) Fun(static_cast<Fun&&>(fun));\n      }\n    } else {\n      if constexpr (Dispatch::is_trivial) {\n        Dispatch::ctor(data_, &fun, sizeof(Fun), alignof(Fun));\n      } else {\n        data_.big = new Fun(static_cast<Fun&&>(fun));\n      }\n    }\n    call_ = Traits::template call<Fun, Dispatch::is_in_situ>;\n    exec_ = Exec(Dispatch::template exec<Fun>);\n  }\n\n  /**\n   * For move-constructing from a `folly::Function<X(Ys...) [const?]>`.\n   *\n   * For a `Function` with a `const` function type, the object must be\n   * callable from a `const`-reference, i.e. implement `operator() const`.\n   * For a `Function` with a non-`const` function type, the object will\n   * be called from a non-const reference, which means that it will execute\n   * a non-const `operator()` if it is defined, and falls back to\n   * `operator() const` otherwise.\n   */\n  template <\n      typename Signature,\n      typename Fun = Function<Signature>,\n      // prevent gcc from making this a better match than move-ctor\n      typename = std::enable_if_t<!std::is_same<Function, Fun>::value>,\n      typename = typename Traits::template IfSafeResult<Fun>>\n  Function(Function<Signature>&& that) noexcept(\n      noexcept(Function(std::move(that), CoerceTag{})))\n      : Function(std::move(that), CoerceTag{}) {}\n\n  /**\n   * If `ptr` is null, constructs an empty `Function`. Otherwise,\n   * this constructor is equivalent to `Function(std::mem_fn(ptr))`.\n   */\n  template <\n      typename Member,\n      typename Class,\n      // Prevent this overload from being selected when `ptr` is not a\n      // compatible member function pointer.\n      typename = decltype(Function(std::mem_fn((Member Class::*)0)))>\n  /* implicit */ Function(Member Class::* ptr) noexcept {\n    if (ptr) {\n      *this = std::mem_fn(ptr);\n    }\n  }\n\n  ~Function() { exec(Op::NUKE, &data_, nullptr); }\n\n  Function& operator=(const Function&) = delete;\n\n#ifdef __OBJC__\n  // Make sure Objective C blocks are copied\n  template <class ReturnType, class... Args>\n  /* implicit */ Function& operator=(ReturnType (^objCBlock)(Args... args)) {\n    (*this) =\n        [blockCopy = (ReturnType(^)(Args...))[objCBlock copy]](Args... args) {\n          return blockCopy(args...);\n        };\n    return *this;\n  }\n#endif\n\n  /**\n   * Move assignment operator\n   *\n   * \\note Leaves `that` in a valid but unspecified state. If `&that == this`\n   * then `*this` is left in a valid but unspecified state.\n   */\n  Function& operator=(Function&& that) noexcept {\n    exec(Op::NUKE, &data_, nullptr);\n    if (FOLLY_LIKELY(this != &that)) {\n      that.exec(Op::MOVE, &that.data_, &data_);\n      exec_ = that.exec_;\n      call_ = that.call_;\n    }\n    that.exec_ = nullptr;\n    that.call_ = &Traits::uninitCall;\n    return *this;\n  }\n\n  /**\n   * Assigns a callable object to this `Function`. If the operation fails,\n   * `*this` is left unmodified.\n   *\n   * \\note `typename = decltype(Function(FOLLY_DECLVAL(Fun&&)))` prevents this\n   * overload from being selected by overload resolution when `fun` is not a\n   * compatible function.\n   */\n  template <\n      typename Fun,\n      typename...,\n      bool Nx = noexcept(Function(FOLLY_DECLVAL(Fun&&)))>\n  Function& operator=(Fun fun) noexcept(Nx) {\n    // Doing this in place is more efficient when we can do so safely.\n    if (Nx) {\n      // Q: Why is is safe to destroy and reconstruct this object in place?\n      // A: See the explanation in the move assignment operator.\n      this->~Function();\n      ::new (this) Function(static_cast<Fun&&>(fun));\n    } else {\n      // Construct a temporary and (nothrow) swap.\n      Function(static_cast<Fun&&>(fun)).swap(*this);\n    }\n    return *this;\n  }\n\n  /**\n   * For assigning from a `Function<X(Ys..) [const?]>`.\n   */\n  template <\n      typename Signature,\n      typename...,\n      typename = typename Traits::template IfSafeResult<Function<Signature>>>\n  Function& operator=(Function<Signature>&& that) noexcept(\n      noexcept(Function(std::move(that)))) {\n    return (*this = Function(std::move(that)));\n  }\n\n  /**\n   * Clears this `Function`.\n   */\n  Function& operator=(std::nullptr_t) noexcept { return (*this = Function()); }\n\n  /**\n   * If `ptr` is null, clears this `Function`. Otherwise, this assignment\n   * operator is equivalent to `*this = std::mem_fn(ptr)`.\n   */\n  template <typename Member, typename Class>\n  auto operator=(Member Class::* ptr) noexcept\n      // Prevent this overload from being selected when `ptr` is not a\n      // compatible member function pointer.\n      -> decltype(operator=(std::mem_fn(ptr))) {\n    return ptr ? (*this = std::mem_fn(ptr)) : (*this = Function());\n  }\n\n  /**\n   * Call the wrapped callable object with the specified arguments.\n   */\n  using Traits::operator();\n\n  /**\n   * Exchanges the callable objects of `*this` and `that`.\n   *\n   * @param that a folly::Function ref\n   */\n  void swap(Function& that) noexcept { std::swap(*this, that); }\n\n  /**\n   * Returns `true` if this `Function` contains a callable, i.e. is\n   * non-empty.\n   */\n  explicit operator bool() const noexcept { return exec_ != nullptr; }\n\n  /**\n   * Returns the size of the allocation made to store the callable on the\n   * heap. If `0` is returned, there has been no additional memory\n   * allocation because the callable is stored within the `Function` object.\n   */\n  std::size_t heapAllocatedMemory() const noexcept {\n    return exec(Op::HEAP, &data_, nullptr);\n  }\n\n  using typename Traits::SharedProxy;\n\n  /**\n   * Move this `Function` into a copyable callable object, of which all copies\n   * share the state.\n   */\n  SharedProxy asSharedProxy() && { return SharedProxy{std::move(*this)}; }\n\n  /**\n   * Construct a `std::function` by moving in the contents of this `Function`.\n   * Note that the returned `std::function` will share its state (i.e. captured\n   * data) across all copies you make of it, so be very careful when copying.\n   */\n  std::function<typename Traits::NonConstSignature> asStdFunction() && {\n    return std::move(*this).asSharedProxy();\n  }\n};\n\ntemplate <typename FunctionType>\nvoid swap(Function<FunctionType>& lhs, Function<FunctionType>& rhs) noexcept {\n  lhs.swap(rhs);\n}\n\ntemplate <typename FunctionType>\nbool operator==(const Function<FunctionType>& fn, std::nullptr_t) {\n  return !fn;\n}\n\ntemplate <typename FunctionType>\nbool operator==(std::nullptr_t, const Function<FunctionType>& fn) {\n  return !fn;\n}\n\ntemplate <typename FunctionType>\nbool operator!=(const Function<FunctionType>& fn, std::nullptr_t) {\n  return !(fn == nullptr);\n}\n\ntemplate <typename FunctionType>\nbool operator!=(std::nullptr_t, const Function<FunctionType>& fn) {\n  return !(nullptr == fn);\n}\n\n/**\n * Casts a `folly::Function` from non-const to a const signature.\n *\n * NOTE: The name of `constCastFunction` should warn you that something\n * potentially dangerous is happening. As a matter of fact, using\n * `std::function` always involves this potentially dangerous aspect, which\n * is why it is not considered fully const-safe or even const-correct.\n * However, in most of the cases you will not need the dangerous aspect at all.\n * Either you do not require invocation of the function from a const context,\n * in which case you do not need to use `constCastFunction` and just\n * use a non-const `folly::Function`. Or, you may need invocation from const,\n * but the callable you are wrapping does not mutate its state (e.g. it is a\n * class object and implements `operator() const`, or it is a normal,\n * non-mutable lambda), in which case you can wrap the callable in a const\n * `folly::Function` directly, without using `constCastFunction`.\n * Only if you require invocation from a const context of a callable that\n * may mutate itself when invoked you have to go through the above procedure.\n * However, in that case what you do is potentially dangerous and requires\n * the equivalent of a `const_cast`, hence you need to call\n * `constCastFunction`.\n *\n * @param that a non-const folly::Function.\n */\ntemplate <typename ReturnType, typename... Args>\nFunction<ReturnType(Args...) const> constCastFunction(\n    Function<ReturnType(Args...)>&& that) noexcept {\n  return Function<ReturnType(Args...) const>{\n      std::move(that), detail::function::CoerceTag{}};\n}\n\ntemplate <typename ReturnType, typename... Args>\nFunction<ReturnType(Args...) const> constCastFunction(\n    Function<ReturnType(Args...) const>&& that) noexcept {\n  return std::move(that);\n}\n\ntemplate <typename ReturnType, typename... Args>\nFunction<ReturnType(Args...) const noexcept> constCastFunction(\n    Function<ReturnType(Args...) noexcept>&& that) noexcept {\n  return Function<ReturnType(Args...) const noexcept>{\n      std::move(that), detail::function::CoerceTag{}};\n}\n\ntemplate <typename ReturnType, typename... Args>\nFunction<ReturnType(Args...) const noexcept> constCastFunction(\n    Function<ReturnType(Args...) const noexcept>&& that) noexcept {\n  return std::move(that);\n}\n\nnamespace detail {\n\ntemplate <typename Void, typename>\nstruct function_ctor_deduce_;\n\ntemplate <typename P>\nstruct function_ctor_deduce_<\n    std::enable_if_t<std::is_function<std::remove_pointer_t<P>>::value>,\n    P> {\n  using type = std::remove_pointer_t<P>;\n};\n\ntemplate <typename F>\nstruct function_ctor_deduce_<void_t<decltype(&F::operator())>, F> {\n  using type =\n      typename member_pointer_traits<decltype(&F::operator())>::member_type;\n};\n\ntemplate <typename F>\nusing function_ctor_deduce_t = typename function_ctor_deduce_<void, F>::type;\n\n} // namespace detail\n\ntemplate <typename F>\nFunction(F) -> Function<detail::function_ctor_deduce_t<F>>;\n\n/**\n * @class folly::FunctionRef\n *\n * A reference wrapper for callable objects\n *\n * FunctionRef is similar to std::reference_wrapper, but the template parameter\n * is the function signature type rather than the type of the referenced object.\n * A folly::FunctionRef is cheap to construct as it contains only a pointer to\n * the referenced callable and a pointer to a function which invokes the\n * callable.\n *\n * The user of FunctionRef must be aware of the reference semantics: storing a\n * copy of a FunctionRef is potentially dangerous and should be avoided unless\n * the referenced object definitely outlives the FunctionRef object. Thus any\n * function that accepts a FunctionRef parameter should only use it to invoke\n * the referenced function and not store a copy of it. Knowing that FunctionRef\n * itself has reference semantics, it is generally okay to use it to reference\n * lambdas that capture by reference.\n */\n\ntemplate <typename FunctionType>\nclass FunctionRef;\n\ntemplate <typename ReturnType, typename... Args>\nclass FunctionRef<ReturnType(Args...)> final {\n  template <typename Arg>\n  using CallArg = detail::function::CallArg<Arg>;\n\n  using Call = ReturnType (*)(CallArg<Args>..., void*);\n\n  static ReturnType uninitCall(CallArg<Args>..., void*) {\n    throw_exception<std::bad_function_call>();\n  }\n\n  template <\n      typename Fun,\n      std::enable_if_t<!std::is_pointer<Fun>::value, int> = 0>\n  static ReturnType call(CallArg<Args>... args, void* object) {\n    using Pointer = std::add_pointer_t<Fun>;\n    return static_cast<ReturnType>(invoke(\n        static_cast<Fun&&>(*static_cast<Pointer>(object)),\n        static_cast<Args&&>(args)...));\n  }\n  template <\n      typename Fun,\n      std::enable_if_t<std::is_pointer<Fun>::value, int> = 0>\n  static ReturnType call(CallArg<Args>... args, void* object) {\n    return static_cast<ReturnType>(\n        invoke(reinterpret_cast<Fun>(object), static_cast<Args&&>(args)...));\n  }\n\n  void* object_{nullptr};\n  Call call_{&FunctionRef::uninitCall};\n\n public:\n  /**\n   * Default constructor. Constructs an empty FunctionRef.\n   *\n   * Invoking it will throw std::bad_function_call.\n   */\n  constexpr FunctionRef() = default;\n\n  /**\n   * Like default constructor. Constructs an empty FunctionRef.\n   *\n   * Invoking it will throw std::bad_function_call.\n   */\n  constexpr explicit FunctionRef(std::nullptr_t) noexcept {}\n\n  /**\n   * Construct a FunctionRef from a reference to a callable object. If the\n   * callable is considered to be an empty callable, the FunctionRef will be\n   * empty.\n   */\n  template <\n      typename Fun,\n      std::enable_if_t<\n          Conjunction<\n              Negation<std::is_same<FunctionRef, std::decay_t<Fun>>>,\n              is_invocable_r<ReturnType, Fun&&, Args&&...>>::value,\n          int> = 0>\n  constexpr /* implicit */ FunctionRef(Fun&& fun) noexcept {\n    // `Fun` may be a const type, in which case we have to do a const_cast\n    // to store the address in a `void*`. This is safe because the `void*`\n    // will be cast back to `Fun*` (which is a const pointer whenever `Fun`\n    // is a const type) inside `FunctionRef::call`\n    auto& ref = fun; // work around forwarding lint advice\n    if constexpr ( //\n        detail::function::IsNullptrCompatible<std::decay_t<Fun>>) {\n      if (detail::function::isEmptyFunction(fun)) {\n        return;\n      }\n    }\n    auto ptr = std::addressof(ref);\n    object_ = const_cast<void*>(static_cast<void const*>(ptr));\n    call_ = &FunctionRef::template call<Fun>;\n  }\n\n  /**\n   * Constructs a FunctionRef from a pointer to a function. If the\n   * pointer is nullptr, the FunctionRef will be empty.\n   */\n  template <\n      typename Fun,\n      std::enable_if_t<std::is_function<Fun>::value, int> = 0,\n      std::enable_if_t<is_invocable_r_v<ReturnType, Fun&, Args&&...>, int> = 0>\n  constexpr /* implicit */ FunctionRef(Fun* fun) noexcept {\n    if (fun) {\n      object_ = const_cast<void*>(reinterpret_cast<void const*>(fun));\n      call_ = &FunctionRef::template call<Fun*>;\n    }\n  }\n\n  ReturnType operator()(Args... args) const {\n    return call_(static_cast<Args&&>(args)..., object_);\n  }\n\n  constexpr explicit operator bool() const noexcept { return object_; }\n\n  constexpr friend bool operator==(\n      FunctionRef<ReturnType(Args...)> ref, std::nullptr_t) noexcept {\n    return ref.object_ == nullptr;\n  }\n  constexpr friend bool operator!=(\n      FunctionRef<ReturnType(Args...)> ref, std::nullptr_t) noexcept {\n    return ref.object_ != nullptr;\n  }\n\n  constexpr friend bool operator==(\n      std::nullptr_t, FunctionRef<ReturnType(Args...)> ref) noexcept {\n    return ref.object_ == nullptr;\n  }\n  constexpr friend bool operator!=(\n      std::nullptr_t, FunctionRef<ReturnType(Args...)> ref) noexcept {\n    return ref.object_ != nullptr;\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/GLog.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Likely.h>\n\n#include <atomic>\n#include <chrono>\n\n#include <glog/logging.h>\n\n#ifndef FB_LOG_EVERY_MS\n/**\n * Issues a LOG(severity) no more often than every\n * milliseconds. Example:\n *\n * FB_LOG_EVERY_MS(INFO, 10000) << \"At least ten seconds passed\"\n *   \" since you last saw this.\";\n *\n * The implementation uses for statements to introduce variables in\n * a nice way that doesn't mess surrounding statements.  It is thread\n * safe.  Non-positive intervals will always log.\n */\n#define FB_LOG_EVERY_MS(severity, milli_interval)                              \\\n  for (decltype(milli_interval)                                                \\\n           FB_LEM_once = 1,                                                    \\\n           FB_LEM_interval = (milli_interval);                                 \\\n       FB_LEM_once;)                                                           \\\n    for (::std::chrono::milliseconds::rep FB_LEM_prev,                         \\\n         FB_LEM_now = FB_LEM_interval <= 0                                     \\\n             ? 0                                                               \\\n             : ::std::chrono::duration_cast<::std::chrono::milliseconds>(      \\\n                   ::std::chrono::system_clock::now().time_since_epoch())      \\\n                   .count();                                                   \\\n         FB_LEM_once;)                                                         \\\n      for (static ::std::atomic<::std::chrono::milliseconds::rep> FB_LEM_hist; \\\n           FB_LEM_once;                                                        \\\n           FB_LEM_once = 0)                                                    \\\n        if (FB_LEM_interval > 0 &&                                             \\\n            (FB_LEM_now -                                                      \\\n                     (FB_LEM_prev =                                            \\\n                          FB_LEM_hist.load(std::memory_order_acquire)) <       \\\n                 FB_LEM_interval ||                                            \\\n             !FB_LEM_hist.compare_exchange_strong(FB_LEM_prev, FB_LEM_now))) { \\\n        } else                                                                 \\\n          LOG(severity)\n\n#endif\n\n/**\n * Issues a LOG(severity) once.\n *\n *   FB_LOG_ONCE(ERROR) << \"Log this error only once\";\n *\n * This macro is thread-safe and does not impact surrounding statements.\n *\n * NOTE: A load() is used in the fast-path scenario (steady state) in order to\n * avoid a locked RMW operation.\n */\n#ifndef FB_LOG_ONCE\n#define FB_LOG_ONCE(severity)                                                  \\\n  for (auto __folly_detail_glog_once = true; __folly_detail_glog_once;)        \\\n    for (static ::std::atomic_bool __folly_detail_glog_logged{false};          \\\n         __folly_detail_glog_once;                                             \\\n         __folly_detail_glog_once = false)                                     \\\n      if (FOLLY_LIKELY(                                                        \\\n              __folly_detail_glog_logged.load(::std::memory_order_relaxed)) || \\\n          __folly_detail_glog_logged.exchange(                                 \\\n              true, ::std::memory_order_relaxed)) {                            \\\n      } else                                                                   \\\n        LOG(severity)\n#endif\n"
  },
  {
    "path": "folly/GroupVarint.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/GroupVarint.h>\n\n#include <folly/container/Array.h>\n\n#if FOLLY_HAVE_GROUP_VARINT\nnamespace folly {\n\nconst uint32_t GroupVarint32::kMask[] = {\n    0xff,\n    0xffff,\n    0xffffff,\n    0xffffffff,\n};\n\nconst uint64_t GroupVarint64::kMask[] = {\n    0xff,\n    0xffff,\n    0xffffff,\n    0xffffffff,\n    0xffffffffffULL,\n    0xffffffffffffULL,\n    0xffffffffffffffULL,\n    0xffffffffffffffffULL,\n};\n\nnamespace detail {\n\nstruct group_varint_table_base_make_item {\n  constexpr std::size_t get_d(std::size_t index, std::size_t j) const {\n    return 1u + ((index >> (2 * j)) & 3u);\n  }\n  constexpr std::size_t get_offset(std::size_t index, std::size_t j) const {\n    // clang-format off\n    return\n        (j > 0 ? get_d(index, 0) : 0) +\n        (j > 1 ? get_d(index, 1) : 0) +\n        (j > 2 ? get_d(index, 2) : 0) +\n        (j > 3 ? get_d(index, 3) : 0) +\n        0;\n    // clang-format on\n  }\n};\n\nstruct group_varint_table_length_make_item : group_varint_table_base_make_item {\n  constexpr std::uint8_t operator()(std::size_t index) const {\n    return 1u + get_offset(index, 4);\n  }\n};\n\n//  Reference: http://www.stepanovpapers.com/CIKM_2011.pdf\n//\n//  From 17 encoded bytes, we may use between 5 and 17 bytes to encode 4\n//  integers.  The first byte is a key that indicates how many bytes each of\n//  the 4 integers takes:\n//\n//  bit 0..1: length-1 of first integer\n//  bit 2..3: length-1 of second integer\n//  bit 4..5: length-1 of third integer\n//  bit 6..7: length-1 of fourth integer\n//\n//  The value of the first byte is used as the index in a table which returns\n//  a mask value for the SSSE3 PSHUFB instruction, which takes an XMM register\n//  (16 bytes) and shuffles bytes from it into a destination XMM register\n//  (optionally setting some of them to 0)\n//\n//  For example, if the key has value 4, that means that the first integer\n//  uses 1 byte, the second uses 2 bytes, the third and fourth use 1 byte each,\n//  so we set the mask value so that\n//\n//  r[0] = a[0]\n//  r[1] = 0\n//  r[2] = 0\n//  r[3] = 0\n//\n//  r[4] = a[1]\n//  r[5] = a[2]\n//  r[6] = 0\n//  r[7] = 0\n//\n//  r[8] = a[3]\n//  r[9] = 0\n//  r[10] = 0\n//  r[11] = 0\n//\n//  r[12] = a[4]\n//  r[13] = 0\n//  r[14] = 0\n//  r[15] = 0\n\nstruct group_varint_table_sse_mask_make_item\n    : group_varint_table_base_make_item {\n  constexpr auto partial_item(\n      std::size_t d, std::size_t offset, std::size_t k) const {\n    // if k < d, the j'th integer uses d bytes, consume them\n    // set remaining bytes in result to 0\n    // 0xff: set corresponding byte in result to 0\n    return std::uint32_t((k < d ? offset + k : std::size_t(0xff)) << (8 * k));\n  }\n\n  constexpr auto item_impl(std::size_t d, std::size_t offset) const {\n    // clang-format off\n    return\n        partial_item(d, offset, 0) |\n        partial_item(d, offset, 1) |\n        partial_item(d, offset, 2) |\n        partial_item(d, offset, 3) |\n        0;\n    // clang-format on\n  }\n\n  constexpr auto item(std::size_t index, std::size_t j) const {\n    return item_impl(get_d(index, j), get_offset(index, j));\n  }\n\n  constexpr auto operator()(std::size_t index) const {\n    return std::array<std::uint32_t, 4>{{\n        item(index, 0),\n        item(index, 1),\n        item(index, 2),\n        item(index, 3),\n    }};\n  }\n};\n\n#if FOLLY_SSE >= 4\nalignas(16) FOLLY_STORAGE_CONSTEXPR\n    decltype(groupVarintSSEMasks) groupVarintSSEMasks =\n        make_array_with<256>(group_varint_table_sse_mask_make_item{});\n#endif\n\nFOLLY_STORAGE_CONSTEXPR decltype(groupVarintLengths) groupVarintLengths =\n    make_array_with<256>(group_varint_table_length_make_item{});\n\n} // namespace detail\n\n} // namespace folly\n#endif\n"
  },
  {
    "path": "folly/GroupVarint.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <limits>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/detail/GroupVarintDetail.h>\n#include <folly/lang/Bits.h>\n#include <folly/portability/Builtins.h>\n\n#if !defined(__GNUC__) && !defined(_MSC_VER)\n#error GroupVarint.h requires GCC or MSVC\n#endif\n\n#if FOLLY_X64 || defined(__i386__) || FOLLY_PPC64 || FOLLY_AARCH64 || \\\n    FOLLY_RISCV64\n#define FOLLY_HAVE_GROUP_VARINT 1\n#else\n#define FOLLY_HAVE_GROUP_VARINT 0\n#endif\n\n#if FOLLY_HAVE_GROUP_VARINT\n\n#if FOLLY_SSE >= 4\n#include <nmmintrin.h>\nnamespace folly {\nnamespace detail {\nextern const std::array<std::array<std::uint32_t, 4>, 256> groupVarintSSEMasks;\n} // namespace detail\n} // namespace folly\n#endif\n\nnamespace folly {\nnamespace detail {\nextern const std::array<std::uint8_t, 256> groupVarintLengths;\n} // namespace detail\n} // namespace folly\n\nnamespace folly {\n\ntemplate <typename T>\nclass GroupVarint;\n\n/**\n * GroupVarint encoding for 32-bit values.\n *\n * Encodes 4 32-bit integers at once, each using 1-4 bytes depending on size.\n * There is one byte of overhead.  (The first byte contains the lengths of\n * the four integers encoded as two bits each; 00=1 byte .. 11=4 bytes)\n *\n * This implementation assumes little-endian and does unaligned 32-bit\n * accesses, so it's basically not portable outside of the x86[_64] world.\n */\ntemplate <>\nclass GroupVarint<uint32_t> : public detail::GroupVarintBase<uint32_t> {\n public:\n  /**\n   * Return the number of bytes used to encode these four values.\n   */\n  static size_t size(uint32_t a, uint32_t b, uint32_t c, uint32_t d) {\n    return (size_t)kHeaderSize + (size_t)kGroupSize + key(a) + key(b) + key(c) +\n        key(d);\n  }\n\n  /**\n   * Return the number of bytes used to encode four uint32_t values stored\n   * at consecutive positions in an array.\n   */\n  static size_t size(const uint32_t* p) { return size(p[0], p[1], p[2], p[3]); }\n\n  /**\n   * Return the number of bytes used to encode count (<= 4) values.\n   * If you clip a buffer after these many bytes, you can still decode\n   * the first \"count\" values correctly (if the remaining size() -\n   * partialSize() bytes are filled with garbage).\n   */\n  static size_t partialSize(const type* p, size_t count) {\n    DCHECK_LE(count, kGroupSize);\n    size_t s = kHeaderSize + count;\n    for (; count; --count, ++p) {\n      s += key(*p);\n    }\n    return s;\n  }\n\n  /**\n   * Return the number of values from *p that are valid from an encoded\n   * buffer of size bytes.\n   */\n  static size_t partialCount(const char* p, size_t size) {\n    uint8_t v = uint8_t(*p);\n    size_t s = kHeaderSize;\n    s += 1 + b0key(v);\n    if (s > size) {\n      return 0;\n    }\n    s += 1 + b1key(v);\n    if (s > size) {\n      return 1;\n    }\n    s += 1 + b2key(v);\n    if (s > size) {\n      return 2;\n    }\n    s += 1 + b3key(v);\n    if (s > size) {\n      return 3;\n    }\n    return 4;\n  }\n\n  /**\n   * Given a pointer to the beginning of an GroupVarint32-encoded block,\n   * return the number of bytes used by the encoding.\n   */\n  static size_t encodedSize(const char* p) {\n    return (size_t)kHeaderSize + (size_t)kGroupSize + b0key(uint8_t(*p)) +\n        b1key(uint8_t(*p)) + b2key(uint8_t(*p)) + b3key(uint8_t(*p));\n  }\n\n  /**\n   * Encode four uint32_t values into the buffer pointed-to by p, and return\n   * the next position in the buffer (that is, one character past the last\n   * encoded byte).  p needs to have at least size()+4 bytes available.\n   */\n  static char* encode(char* p, uint32_t a, uint32_t b, uint32_t c, uint32_t d) {\n    uint8_t b0key = key(a);\n    uint8_t b1key = key(b);\n    uint8_t b2key = key(c);\n    uint8_t b3key = key(d);\n    *p++ = (b3key << 6) | (b2key << 4) | (b1key << 2) | b0key;\n    storeUnaligned<uint32_t>(p, a);\n    p += b0key + 1;\n    storeUnaligned<uint32_t>(p, b);\n    p += b1key + 1;\n    storeUnaligned<uint32_t>(p, c);\n    p += b2key + 1;\n    storeUnaligned<uint32_t>(p, d);\n    p += b3key + 1;\n    return p;\n  }\n\n  /**\n   * Encode four uint32_t values from the array pointed-to by src into the\n   * buffer pointed-to by p, similar to encode(p,a,b,c,d) above.\n   */\n  static char* encode(char* p, const uint32_t* src) {\n    return encode(p, src[0], src[1], src[2], src[3]);\n  }\n\n  /**\n   * Decode four uint32_t values from a buffer, and return the next position\n   * in the buffer (that is, one character past the last encoded byte).\n   * The buffer needs to have at least 3 extra bytes available (they\n   * may be read but ignored).\n   */\n  static const char* decode_simple(\n      const char* p, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) {\n    size_t k = loadUnaligned<uint8_t>(p);\n    const char* end = p + detail::groupVarintLengths[k];\n    ++p;\n    size_t k0 = b0key(k);\n    *a = loadUnaligned<uint32_t>(p) & kMask[k0];\n    p += k0 + 1;\n    size_t k1 = b1key(k);\n    *b = loadUnaligned<uint32_t>(p) & kMask[k1];\n    p += k1 + 1;\n    size_t k2 = b2key(k);\n    *c = loadUnaligned<uint32_t>(p) & kMask[k2];\n    p += k2 + 1;\n    size_t k3 = b3key(k);\n    *d = loadUnaligned<uint32_t>(p) & kMask[k3];\n    // p += k3+1;\n    return end;\n  }\n\n  /**\n   * Decode four uint32_t values from a buffer and store them in the array\n   * pointed-to by dest, similar to decode(p,a,b,c,d) above.\n   */\n  static const char* decode_simple(const char* p, uint32_t* dest) {\n    return decode_simple(p, dest, dest + 1, dest + 2, dest + 3);\n  }\n\n#if FOLLY_SSE >= 4\n  /**\n   * Just like the non-SSSE3 decode below, but with the additional constraint\n   * that we must be able to read at least 17 bytes from the input pointer, p.\n   */\n  static const char* decode(const char* p, uint32_t* dest) {\n    uint8_t key = uint8_t(p[0]);\n    __m128i val = _mm_loadu_si128((const __m128i*)(p + 1));\n    __m128i mask =\n        _mm_load_si128((const __m128i*)detail::groupVarintSSEMasks[key].data());\n    __m128i r = _mm_shuffle_epi8(val, mask);\n    _mm_storeu_si128((__m128i*)dest, r);\n    return p + detail::groupVarintLengths[key];\n  }\n\n  /**\n   * Just like decode_simple, but with the additional constraint that\n   * we must be able to read at least 17 bytes from the input pointer, p.\n   */\n  static const char* decode(\n      const char* p, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) {\n    uint8_t key = uint8_t(p[0]);\n    __m128i val = _mm_loadu_si128((const __m128i*)(p + 1));\n    __m128i mask =\n        _mm_load_si128((const __m128i*)detail::groupVarintSSEMasks[key].data());\n    __m128i r = _mm_shuffle_epi8(val, mask);\n\n    *a = uint32_t(_mm_extract_epi32(r, 0));\n    *b = uint32_t(_mm_extract_epi32(r, 1));\n    *c = uint32_t(_mm_extract_epi32(r, 2));\n    *d = uint32_t(_mm_extract_epi32(r, 3));\n\n    return p + detail::groupVarintLengths[key];\n  }\n\n#else // FOLLY_SSE >= 4\n  static const char* decode(\n      const char* p, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) {\n    return decode_simple(p, a, b, c, d);\n  }\n\n  static const char* decode(const char* p, uint32_t* dest) {\n    return decode_simple(p, dest);\n  }\n#endif // FOLLY_SSE >= 4\n\n private:\n  static uint8_t key(uint32_t x) {\n    // __builtin_clz is undefined for the x==0 case\n    return uint8_t(3 - (__builtin_clz(x | 1) / 8));\n  }\n  static size_t b0key(size_t x) { return x & 3; }\n  static size_t b1key(size_t x) { return (x >> 2) & 3; }\n  static size_t b2key(size_t x) { return (x >> 4) & 3; }\n  static size_t b3key(size_t x) { return (x >> 6) & 3; }\n\n  static const uint32_t kMask[];\n};\n\n/**\n * GroupVarint encoding for 64-bit values.\n *\n * Encodes 5 64-bit integers at once, each using 1-8 bytes depending on size.\n * There are two bytes of overhead.  (The first two bytes contain the lengths\n * of the five integers encoded as three bits each; 000=1 byte .. 111 = 8 bytes)\n *\n * This implementation assumes little-endian and does unaligned 64-bit\n * accesses, so it's basically not portable outside of the x86[_64] world.\n */\ntemplate <>\nclass GroupVarint<uint64_t> : public detail::GroupVarintBase<uint64_t> {\n public:\n  /**\n   * Return the number of bytes used to encode these five values.\n   */\n  static size_t size(\n      uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) {\n    return (size_t)kHeaderSize + (size_t)kGroupSize + key(a) + key(b) + key(c) +\n        key(d) + key(e);\n  }\n\n  /**\n   * Return the number of bytes used to encode five uint64_t values stored\n   * at consecutive positions in an array.\n   */\n  static size_t size(const uint64_t* p) {\n    return size(p[0], p[1], p[2], p[3], p[4]);\n  }\n\n  /**\n   * Return the number of bytes used to encode count (<= 4) values.\n   * If you clip a buffer after these many bytes, you can still decode\n   * the first \"count\" values correctly (if the remaining size() -\n   * partialSize() bytes are filled with garbage).\n   */\n  static size_t partialSize(const type* p, size_t count) {\n    DCHECK_LE(count, kGroupSize);\n    size_t s = kHeaderSize + count;\n    for (; count; --count, ++p) {\n      s += key(*p);\n    }\n    return s;\n  }\n\n  /**\n   * Return the number of values from *p that are valid from an encoded\n   * buffer of size bytes.\n   */\n  static size_t partialCount(const char* p, size_t size) {\n    uint16_t v = loadUnaligned<uint16_t>(p);\n    size_t s = kHeaderSize;\n    s += 1 + b0key(v);\n    if (s > size) {\n      return 0;\n    }\n    s += 1 + b1key(v);\n    if (s > size) {\n      return 1;\n    }\n    s += 1 + b2key(v);\n    if (s > size) {\n      return 2;\n    }\n    s += 1 + b3key(v);\n    if (s > size) {\n      return 3;\n    }\n    s += 1 + b4key(v);\n    if (s > size) {\n      return 4;\n    }\n    return 5;\n  }\n\n  /**\n   * Given a pointer to the beginning of an GroupVarint64-encoded block,\n   * return the number of bytes used by the encoding.\n   */\n  static size_t encodedSize(const char* p) {\n    uint16_t n = loadUnaligned<uint16_t>(p);\n    return (size_t)kHeaderSize + (size_t)kGroupSize + b0key(n) + b1key(n) +\n        b2key(n) + b3key(n) + b4key(n);\n  }\n\n  /**\n   * Encode five uint64_t values into the buffer pointed-to by p, and return\n   * the next position in the buffer (that is, one character past the last\n   * encoded byte).  p needs to have at least size()+8 bytes available.\n   */\n  static char* encode(\n      char* p, uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) {\n    uint16_t b0key = key(a);\n    uint16_t b1key = key(b);\n    uint16_t b2key = key(c);\n    uint16_t b3key = key(d);\n    uint16_t b4key = key(e);\n    storeUnaligned<uint16_t>(\n        p,\n        uint16_t(\n            (b4key << 12) | (b3key << 9) | (b2key << 6) | (b1key << 3) |\n            b0key));\n    p += 2;\n    storeUnaligned<uint64_t>(p, a);\n    p += b0key + 1;\n    storeUnaligned<uint64_t>(p, b);\n    p += b1key + 1;\n    storeUnaligned<uint64_t>(p, c);\n    p += b2key + 1;\n    storeUnaligned<uint64_t>(p, d);\n    p += b3key + 1;\n    storeUnaligned<uint64_t>(p, e);\n    p += b4key + 1;\n    return p;\n  }\n\n  /**\n   * Encode five uint64_t values from the array pointed-to by src into the\n   * buffer pointed-to by p, similar to encode(p,a,b,c,d,e) above.\n   */\n  static char* encode(char* p, const uint64_t* src) {\n    return encode(p, src[0], src[1], src[2], src[3], src[4]);\n  }\n\n  /**\n   * Decode five uint64_t values from a buffer, and return the next position\n   * in the buffer (that is, one character past the last encoded byte).\n   * The buffer needs to have at least 7 bytes available (they may be read\n   * but ignored).\n   */\n  static const char* decode(\n      const char* p,\n      uint64_t* a,\n      uint64_t* b,\n      uint64_t* c,\n      uint64_t* d,\n      uint64_t* e) {\n    uint16_t k = loadUnaligned<uint16_t>(p);\n    p += 2;\n    uint8_t k0 = b0key(k);\n    *a = loadUnaligned<uint64_t>(p) & kMask[k0];\n    p += k0 + 1;\n    uint8_t k1 = b1key(k);\n    *b = loadUnaligned<uint64_t>(p) & kMask[k1];\n    p += k1 + 1;\n    uint8_t k2 = b2key(k);\n    *c = loadUnaligned<uint64_t>(p) & kMask[k2];\n    p += k2 + 1;\n    uint8_t k3 = b3key(k);\n    *d = loadUnaligned<uint64_t>(p) & kMask[k3];\n    p += k3 + 1;\n    uint8_t k4 = b4key(k);\n    *e = loadUnaligned<uint64_t>(p) & kMask[k4];\n    p += k4 + 1;\n    return p;\n  }\n\n  /**\n   * Decode five uint64_t values from a buffer and store them in the array\n   * pointed-to by dest, similar to decode(p,a,b,c,d,e) above.\n   */\n  static const char* decode(const char* p, uint64_t* dest) {\n    return decode(p, dest, dest + 1, dest + 2, dest + 3, dest + 4);\n  }\n\n private:\n  enum { kHeaderBytes = 2 };\n\n  static uint8_t key(uint64_t x) {\n    // __builtin_clzll is undefined for the x==0 case\n    return uint8_t(7 - (__builtin_clzll(x | 1) / 8));\n  }\n\n  static uint8_t b0key(uint16_t x) { return x & 7u; }\n  static uint8_t b1key(uint16_t x) { return (x >> 3) & 7u; }\n  static uint8_t b2key(uint16_t x) { return (x >> 6) & 7u; }\n  static uint8_t b3key(uint16_t x) { return (x >> 9) & 7u; }\n  static uint8_t b4key(uint16_t x) { return (x >> 12) & 7u; }\n\n  static const uint64_t kMask[];\n};\n\nusing GroupVarint32 = GroupVarint<uint32_t>;\nusing GroupVarint64 = GroupVarint<uint64_t>;\n\n/**\n * Simplify use of GroupVarint* for the case where data is available one\n * entry at a time (instead of one group at a time).  Handles buffering\n * and an incomplete last chunk.\n *\n * Output is a function object that accepts character ranges:\n * out(StringPiece) appends the given character range to the output.\n */\ntemplate <class T, class Output>\nclass GroupVarintEncoder {\n public:\n  using Base = GroupVarint<T>;\n  using type = T;\n\n  explicit GroupVarintEncoder(Output out) : out_(out), count_(0) {}\n\n  ~GroupVarintEncoder() { finish(); }\n\n  /**\n   * Add a value to the encoder.\n   */\n  void add(type val) {\n    buf_[count_++] = val;\n    if (count_ == Base::kGroupSize) {\n      char* p = Base::encode(tmp_, buf_);\n      out_(StringPiece(tmp_, p));\n      count_ = 0;\n    }\n  }\n\n  /**\n   * Finish encoding, flushing any buffered values if necessary.\n   * After finish(), the encoder is immediately ready to encode more data\n   * to the same output.\n   */\n  void finish() {\n    if (count_) {\n      // This is not strictly necessary, but it makes testing easy;\n      // uninitialized bytes are guaranteed to be recorded as taking one byte\n      // (not more).\n      for (size_t i = count_; i < Base::kGroupSize; i++) {\n        buf_[i] = 0;\n      }\n      Base::encode(tmp_, buf_);\n      out_(StringPiece(tmp_, Base::partialSize(buf_, count_)));\n      count_ = 0;\n    }\n  }\n\n  /**\n   * Return the appender that was used.\n   */\n  Output& output() { return out_; }\n  const Output& output() const { return out_; }\n\n  /**\n   * Reset the encoder, disregarding any state (except what was already\n   * flushed to the output, of course).\n   */\n  void clear() { count_ = 0; }\n\n private:\n  Output out_;\n  char tmp_[Base::kMaxSize];\n  type buf_[Base::kGroupSize];\n  size_t count_;\n};\n\n/**\n * Simplify use of GroupVarint* for the case where the last group in the\n * input may be incomplete (but the exact size of the input is known).\n * Allows for extracting values one at a time.\n */\ntemplate <typename T>\nclass GroupVarintDecoder {\n public:\n  using Base = GroupVarint<T>;\n  using type = T;\n\n  GroupVarintDecoder() = default;\n\n  explicit GroupVarintDecoder(StringPiece data, size_t maxCount = (size_t)-1)\n      : rrest_(data.end()),\n        p_(data.data()),\n        end_(data.end()),\n        limit_(end_),\n        pos_(0),\n        count_(0),\n        remaining_(maxCount) {}\n\n  void reset(StringPiece data, size_t maxCount = (size_t)-1) {\n    rrest_ = data.end();\n    p_ = data.data();\n    end_ = data.end();\n    limit_ = end_;\n    pos_ = 0;\n    count_ = 0;\n    remaining_ = maxCount;\n  }\n\n  /**\n   * Read and return the next value.\n   */\n  bool next(type* val) {\n    if (pos_ == count_) {\n      // refill\n      size_t rem = size_t(end_ - p_);\n      if (rem == 0 || remaining_ == 0) {\n        return false;\n      }\n      // next() attempts to read one full group at a time, and so we must have\n      // at least enough bytes readable after its end to handle the case if the\n      // last group is full.\n      //\n      // The best way to ensure this is to ensure that data has at least\n      // Base::kMaxSize - 1 bytes readable *after* the end, otherwise we'll copy\n      // into a temporary buffer.\n      if (limit_ - p_ < Base::kMaxSize) {\n        memcpy(tmp_, p_, rem);\n        p_ = tmp_;\n        end_ = p_ + rem;\n        limit_ = tmp_ + sizeof(tmp_);\n      }\n      pos_ = 0;\n      const char* n = Base::decode(p_, buf_);\n      if (n <= end_) {\n        // Full group could be decoded\n        if (remaining_ >= Base::kGroupSize) {\n          remaining_ -= Base::kGroupSize;\n          count_ = Base::kGroupSize;\n          p_ = n;\n        } else {\n          count_ = remaining_;\n          remaining_ = 0;\n          p_ += Base::partialSize(buf_, count_);\n        }\n      } else {\n        // Can't decode a full group\n        count_ = Base::partialCount(p_, size_t(end_ - p_));\n        if (remaining_ >= count_) {\n          remaining_ -= count_;\n          p_ = end_;\n        } else {\n          count_ = remaining_;\n          remaining_ = 0;\n          p_ += Base::partialSize(buf_, count_);\n        }\n        if (count_ == 0) {\n          return false;\n        }\n      }\n    }\n    *val = buf_[pos_++];\n    return true;\n  }\n\n  StringPiece rest() const {\n    // This is only valid after next() returned false\n    CHECK(pos_ == count_ && (p_ == end_ || remaining_ == 0));\n    // p_ may point to the internal buffer (tmp_), but we want\n    // to return subpiece of the original data\n    size_t size = size_t(end_ - p_);\n    return StringPiece(rrest_ - size, rrest_);\n  }\n\n private:\n  const char* rrest_;\n  const char* p_;\n  const char* end_;\n  const char* limit_;\n  char tmp_[2 * Base::kMaxSize];\n  type buf_[Base::kGroupSize];\n  size_t pos_;\n  size_t count_;\n  size_t remaining_;\n};\n\nusing GroupVarint32Decoder = GroupVarintDecoder<uint32_t>;\nusing GroupVarint64Decoder = GroupVarintDecoder<uint64_t>;\n\n} // namespace folly\n\n#endif // FOLLY_HAVE_GROUP_VARINT\n"
  },
  {
    "path": "folly/Hash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n//  shims:\n#include <folly/hash/Hash.h> // @shim\n"
  },
  {
    "path": "folly/IPAddress.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/IPAddress.h>\n\n#include <limits>\n#include <ostream>\n#include <string>\n#include <vector>\n\n#include <fmt/core.h>\n\n#include <folly/String.h>\n#include <folly/detail/IPAddressSource.h>\n#include <folly/small_vector.h>\n\nusing std::ostream;\nusing std::string;\nusing std::vector;\n\nnamespace folly {\n\n// free functions\nsize_t hash_value(const IPAddress& addr) {\n  return addr.hash();\n}\nostream& operator<<(ostream& os, const IPAddress& addr) {\n  os << addr.str();\n  return os;\n}\nvoid toAppend(IPAddress addr, string* result) {\n  result->append(addr.str());\n}\nvoid toAppend(IPAddress addr, fbstring* result) {\n  result->append(addr.str());\n}\n\nbool IPAddress::validate(StringPiece ip) noexcept {\n  return IPAddressV4::validate(ip) || IPAddressV6::validate(ip);\n}\n\n// public static\nIPAddressV4 IPAddress::createIPv4(const IPAddress& addr) {\n  if (addr.isV4()) {\n    return addr.asV4();\n  } else {\n    return addr.asV6().createIPv4();\n  }\n}\n\n// public static\nIPAddressV6 IPAddress::createIPv6(const IPAddress& addr) {\n  if (addr.isV6()) {\n    return addr.asV6();\n  } else {\n    return addr.asV4().createIPv6();\n  }\n}\n\nnamespace {\n\nauto splitIpSlashCidr(StringPiece ipSlashCidr) {\n  folly::small_vector<folly::StringPiece, 2> vec;\n  folly::split('/', ipSlashCidr, vec);\n  return vec;\n}\n\n} // namespace\n\n// public static\nCIDRNetwork IPAddress::createNetwork(\n    StringPiece ipSlashCidr,\n    int defaultCidr, /* = -1 */\n    bool applyMask /* = true */) {\n  auto const ret =\n      IPAddress::tryCreateNetwork(ipSlashCidr, defaultCidr, applyMask);\n\n  if (ret.hasValue()) {\n    return ret.value();\n  }\n\n  if (ret.error() == CIDRNetworkError::INVALID_DEFAULT_CIDR) {\n    throw std::range_error(\"defaultCidr must be <= UINT8_MAX\");\n  }\n\n  if (ret.error() == CIDRNetworkError::INVALID_IP_SLASH_CIDR) {\n    throw IPAddressFormatException(\n        fmt::format(\n            \"Invalid ipSlashCidr specified. Expected IP/CIDR format, got '{}'\",\n            ipSlashCidr));\n  }\n\n  // Handler the remaining error cases. We re-parse the ip/mask pair\n  // to make error messages more meaningful\n  auto const vec = splitIpSlashCidr(ipSlashCidr);\n\n  switch (ret.error()) {\n    case CIDRNetworkError::INVALID_IP:\n      CHECK_GE(vec.size(), 1);\n      throw IPAddressFormatException(\n          fmt::format(\"Invalid IP address {}\", vec.at(0)));\n    case CIDRNetworkError::INVALID_CIDR:\n      CHECK_GE(vec.size(), 2);\n      throw IPAddressFormatException(\n          fmt::format(\"Mask value '{}' not a valid mask\", vec.at(1)));\n    case CIDRNetworkError::CIDR_MISMATCH: {\n      auto const subnet = IPAddress::tryFromString(vec.at(0)).value();\n      auto cidr = static_cast<uint8_t>(\n          (defaultCidr > -1) ? defaultCidr : (subnet.isV4() ? 32 : 128));\n\n      throw IPAddressFormatException(\n          fmt::format(\n              \"CIDR value '{}' is > network bit count '{}'\",\n              vec.size() == 2 ? vec.at(1) : to<string>(cidr),\n              subnet.bitCount()));\n    }\n    case CIDRNetworkError::INVALID_DEFAULT_CIDR:\n    case CIDRNetworkError::INVALID_IP_SLASH_CIDR:\n    default:\n      // unreachable\n      break;\n  }\n\n  CHECK(0);\n}\n\n// public static\nExpected<CIDRNetwork, CIDRNetworkError> IPAddress::tryCreateNetwork(\n    StringPiece ipSlashCidr, int defaultCidr, bool applyMask) {\n  if (defaultCidr > std::numeric_limits<uint8_t>::max()) {\n    return makeUnexpected(CIDRNetworkError::INVALID_DEFAULT_CIDR);\n  }\n\n  auto const vec = splitIpSlashCidr(ipSlashCidr);\n  auto const elemCount = vec.size();\n\n  if (elemCount == 0 || // weird invalid string\n      elemCount > 2) { // invalid string (IP/CIDR/extras)\n    return makeUnexpected(CIDRNetworkError::INVALID_IP_SLASH_CIDR);\n  }\n\n  auto const subnet = IPAddress::tryFromString(vec.at(0));\n  if (subnet.hasError()) {\n    return makeUnexpected(CIDRNetworkError::INVALID_IP);\n  }\n\n  auto cidr = static_cast<uint8_t>(\n      (defaultCidr > -1) ? defaultCidr : (subnet.value().isV4() ? 32 : 128));\n\n  if (elemCount == 2) {\n    auto const maybeCidr = tryTo<uint8_t>(vec.at(1));\n    if (maybeCidr.hasError()) {\n      return makeUnexpected(CIDRNetworkError::INVALID_CIDR);\n    }\n    cidr = maybeCidr.value();\n  }\n\n  if (cidr > subnet.value().bitCount()) {\n    return makeUnexpected(CIDRNetworkError::CIDR_MISMATCH);\n  }\n\n  return std::make_pair(\n      applyMask ? subnet.value().mask(cidr) : subnet.value(), cidr);\n}\n\n// public static\nstd::string IPAddress::networkToString(const CIDRNetwork& network) {\n  return fmt::format(\"{}/{}\", network.first.str(), network.second);\n}\n\n// public static\nIPAddress IPAddress::fromBinary(ByteRange bytes) {\n  if (bytes.size() == 4) {\n    return IPAddress(IPAddressV4::fromBinary(bytes));\n  } else if (bytes.size() == 16) {\n    return IPAddress(IPAddressV6::fromBinary(bytes));\n  } else {\n    string hexval = detail::Bytes::toHex(bytes.data(), bytes.size());\n    throw IPAddressFormatException(\n        fmt::format(\"Invalid address with hex value '{}'\", hexval));\n  }\n}\n\nExpected<IPAddress, IPAddressFormatError> IPAddress::tryFromBinary(\n    ByteRange bytes) noexcept {\n  // Check IPv6 first since it's our main protocol.\n  if (bytes.size() == 16) {\n    return IPAddressV6::tryFromBinary(bytes);\n  } else if (bytes.size() == 4) {\n    return IPAddressV4::tryFromBinary(bytes);\n  } else {\n    return makeUnexpected(IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY);\n  }\n}\n\n// public static\nIPAddress IPAddress::fromLong(uint32_t src) {\n  return IPAddress(IPAddressV4::fromLong(src));\n}\nIPAddress IPAddress::fromLongHBO(uint32_t src) {\n  return IPAddress(IPAddressV4::fromLongHBO(src));\n}\n\n// default constructor\nIPAddress::IPAddress() : addr_(), family_(AF_UNSPEC) {}\n\n// public string constructor\nIPAddress::IPAddress(StringPiece str) : addr_(), family_(AF_UNSPEC) {\n  auto maybeIp = tryFromString(str);\n  if (maybeIp.hasError()) {\n    throw IPAddressFormatException(\n        to<std::string>(\"Invalid IP address '\", str, \"'\"));\n  }\n  *this = maybeIp.value();\n}\n\nExpected<IPAddress, IPAddressFormatError> IPAddress::tryFromString(\n    StringPiece str) noexcept {\n  // need to check for V4 address second, since IPv4-mapped IPv6 addresses may\n  // contain a period\n  if (str.find(':') != string::npos) {\n    return IPAddressV6::tryFromString(str);\n  } else if (str.find('.') != string::npos) {\n    return IPAddressV4::tryFromString(str);\n  } else {\n    return makeUnexpected(IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY);\n  }\n}\n\n// public sockaddr constructor\nIPAddress::IPAddress(const sockaddr* addr) : addr_(), family_(AF_UNSPEC) {\n  auto ip = tryFromSockAddr(addr);\n  if (ip.hasError()) {\n    switch (ip.error()) {\n      case IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY:\n        throw InvalidAddressFamilyException(addr->sa_family);\n      case IPAddressFormatError::NULL_SOCKADDR:\n        throw IPAddressFormatException(\"sockaddr == nullptr\");\n      case IPAddressFormatError::INVALID_IP:\n        throw IPAddressFormatException(\"Invalid IP\");\n    }\n  }\n  *this = ip.value();\n}\n\nfolly::Expected<IPAddress, IPAddressFormatError> IPAddress::tryFromSockAddr(\n    const sockaddr* addr) noexcept {\n  if (addr == nullptr) {\n    return makeUnexpected(IPAddressFormatError::NULL_SOCKADDR);\n  }\n  switch (addr->sa_family) {\n    case AF_INET: {\n      auto v4addr = reinterpret_cast<const sockaddr_in*>(addr);\n      return IPAddressV4(v4addr->sin_addr);\n    }\n    case AF_INET6: {\n      auto v6addr = reinterpret_cast<const sockaddr_in6*>(addr);\n      return IPAddressV6(*v6addr);\n    }\n    default:\n      return makeUnexpected(IPAddressFormatError::UNSUPPORTED_ADDR_FAMILY);\n  }\n}\n\n// public ipv4 constructor\nIPAddress::IPAddress(const IPAddressV4 ipV4Addr) noexcept\n    : addr_(ipV4Addr), family_(AF_INET) {}\n\n// public ipv4 constructor\nIPAddress::IPAddress(const in_addr ipV4Addr) noexcept\n    : addr_(IPAddressV4(ipV4Addr)), family_(AF_INET) {}\n\n// public ipv6 constructor\nIPAddress::IPAddress(const IPAddressV6& ipV6Addr) noexcept\n    : addr_(ipV6Addr), family_(AF_INET6) {}\n\n// public ipv6 constructor\nIPAddress::IPAddress(const in6_addr& ipV6Addr) noexcept\n    : addr_(IPAddressV6(ipV6Addr)), family_(AF_INET6) {}\n\n// Assign from V4 address\nIPAddress& IPAddress::operator=(const IPAddressV4& ipv4_addr) noexcept {\n  addr_ = IPAddressV46(ipv4_addr);\n  family_ = AF_INET;\n  return *this;\n}\n\n// Assign from V6 address\nIPAddress& IPAddress::operator=(const IPAddressV6& ipv6_addr) noexcept {\n  addr_ = IPAddressV46(ipv6_addr);\n  family_ = AF_INET6;\n  return *this;\n}\n\n// public\nbool IPAddress::inSubnet(StringPiece cidrNetwork) const {\n  auto subnetInfo = IPAddress::createNetwork(cidrNetwork);\n  return inSubnet(subnetInfo.first, subnetInfo.second);\n}\n\n// public\nbool IPAddress::inSubnet(const IPAddress& subnet, uint8_t cidr) const {\n  if (bitCount() == subnet.bitCount()) {\n    if (isV4()) {\n      return asV4().inSubnet(subnet.asV4(), cidr);\n    } else {\n      return asV6().inSubnet(subnet.asV6(), cidr);\n    }\n  }\n  // an IPv4 address can never belong in a IPv6 subnet unless the IPv6 is a 6to4\n  // address and vice-versa\n  if (isV6()) {\n    const IPAddressV6& v6addr = asV6();\n    const IPAddressV4& v4subnet = subnet.asV4();\n    if (v6addr.is6To4()) {\n      return v6addr.getIPv4For6To4().inSubnet(v4subnet, cidr);\n    }\n  } else if (subnet.isV6()) {\n    const IPAddressV6& v6subnet = subnet.asV6();\n    const IPAddressV4& v4addr = asV4();\n    if (v6subnet.is6To4()) {\n      return v4addr.inSubnet(v6subnet.getIPv4For6To4(), cidr);\n    }\n  }\n  return false;\n}\n\n// public\nbool IPAddress::inSubnetWithMask(\n    const IPAddress& subnet, ByteRange mask) const {\n  auto mkByteArray4 = [&]() -> ByteArray4 {\n    ByteArray4 ba{{0}};\n    std::memcpy(ba.data(), mask.begin(), std::min<size_t>(mask.size(), 4));\n    return ba;\n  };\n\n  if (bitCount() == subnet.bitCount()) {\n    if (isV4()) {\n      return asV4().inSubnetWithMask(subnet.asV4(), mkByteArray4());\n    } else {\n      ByteArray16 ba{{0}};\n      std::memcpy(ba.data(), mask.begin(), std::min<size_t>(mask.size(), 16));\n      return asV6().inSubnetWithMask(subnet.asV6(), ba);\n    }\n  }\n\n  // an IPv4 address can never belong in a IPv6 subnet unless the IPv6 is a 6to4\n  // address and vice-versa\n  if (isV6()) {\n    const IPAddressV6& v6addr = asV6();\n    const IPAddressV4& v4subnet = subnet.asV4();\n    if (v6addr.is6To4()) {\n      return v6addr.getIPv4For6To4().inSubnetWithMask(v4subnet, mkByteArray4());\n    }\n  } else if (subnet.isV6()) {\n    const IPAddressV6& v6subnet = subnet.asV6();\n    const IPAddressV4& v4addr = asV4();\n    if (v6subnet.is6To4()) {\n      return v4addr.inSubnetWithMask(v6subnet.getIPv4For6To4(), mkByteArray4());\n    }\n  }\n  return false;\n}\n\nuint8_t IPAddress::getNthMSByte(size_t byteIndex) const {\n  const auto highestIndex = byteCount() - 1;\n  if (byteIndex > highestIndex) {\n    throw std::invalid_argument(\n        fmt::format(\n            \"Byte index must be <= {} for addresses of type: {}\",\n            highestIndex,\n            detail::familyNameStr(family())));\n  }\n  if (isV4()) {\n    return asV4().bytes()[byteIndex];\n  }\n  return asV6().bytes()[byteIndex];\n}\n\n// public\nbool operator==(const IPAddress& addr1, const IPAddress& addr2) {\n  if (addr1.empty() || addr2.empty()) {\n    return addr1.empty() == addr2.empty();\n  }\n  if (addr1.family() == addr2.family()) {\n    if (addr1.isV6()) {\n      return (addr1.asV6() == addr2.asV6());\n    } else if (addr1.isV4()) {\n      return (addr1.asV4() == addr2.asV4());\n    } else {\n      CHECK_EQ(addr1.family(), AF_UNSPEC);\n      // Two default initialized AF_UNSPEC addresses should be considered equal.\n      // AF_UNSPEC is the only other value for which an IPAddress can be\n      // created, in the default constructor case.\n      return true;\n    }\n  }\n  // addr1 is v4 mapped v6 address, addr2 is v4\n  if (addr1.isIPv4Mapped() && addr2.isV4()) {\n    if (IPAddress::createIPv4(addr1) == addr2.asV4()) {\n      return true;\n    }\n  }\n  // addr2 is v4 mapped v6 address, addr1 is v4\n  if (addr2.isIPv4Mapped() && addr1.isV4()) {\n    if (IPAddress::createIPv4(addr2) == addr1.asV4()) {\n      return true;\n    }\n  }\n  // we only compare IPv4 and IPv6 addresses\n  return false;\n}\n\nbool operator<(const IPAddress& addr1, const IPAddress& addr2) {\n  if (addr1.empty() || addr2.empty()) {\n    return addr1.empty() < addr2.empty();\n  }\n  if (addr1.family() == addr2.family()) {\n    if (addr1.isV6()) {\n      return (addr1.asV6() < addr2.asV6());\n    } else if (addr1.isV4()) {\n      return (addr1.asV4() < addr2.asV4());\n    } else {\n      CHECK_EQ(addr1.family(), AF_UNSPEC);\n      // Two default initialized AF_UNSPEC addresses can not be less than each\n      // other. AF_UNSPEC is the only other value for which an IPAddress can be\n      // created, in the default constructor case.\n      return false;\n    }\n  }\n  if (addr1.isV6()) {\n    // means addr2 is v4, convert it to a mapped v6 address and compare\n    return addr1.asV6() < addr2.asV4().createIPv6();\n  }\n  if (addr2.isV6()) {\n    // means addr2 is v6, convert addr1 to v4 mapped and compare\n    return addr1.asV4().createIPv6() < addr2.asV6();\n  }\n  return false;\n}\n\nCIDRNetwork IPAddress::longestCommonPrefix(\n    const CIDRNetwork& one, const CIDRNetwork& two) {\n  if (one.first.family() != two.first.family()) {\n    throw std::invalid_argument(\n        fmt::format(\n            \"Can't compute longest common prefix between addresses of different\"\n            \"families. Passed: {} and {}\",\n            detail::familyNameStr(one.first.family()),\n            detail::familyNameStr(two.first.family())));\n  }\n  if (one.first.isV4()) {\n    auto prefix = IPAddressV4::longestCommonPrefix(\n        {one.first.asV4(), one.second}, {two.first.asV4(), two.second});\n    return {IPAddress(prefix.first), prefix.second};\n  } else if (one.first.isV6()) {\n    auto prefix = IPAddressV6::longestCommonPrefix(\n        {one.first.asV6(), one.second}, {two.first.asV6(), two.second});\n    return {IPAddress(prefix.first), prefix.second};\n  } else {\n    throw std::invalid_argument(\"Unknown address family\");\n  }\n}\n\n// clang-format off\n[[noreturn]] void IPAddress::asV4Throw() const {\n  auto fam = detail::familyNameStr(family());\n  throw InvalidAddressFamilyException(\n      fmt::format(\"Can't convert address with family {} to AF_INET address\", fam));\n}\n\n[[noreturn]] void IPAddress::asV6Throw() const {\n  auto fam = detail::familyNameStr(family());\n  throw InvalidAddressFamilyException(\n      fmt::format(\"Can't convert address with family {} to AF_INET6 address\", fam));\n}\n// clang-format on\n\n} // namespace folly\n"
  },
  {
    "path": "folly/IPAddress.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Provides a unified interface for IP addresses.\n *\n * @refcode folly/docs/examples/folly/ipaddress.cpp\n *\n * @class folly::IPAddress\n * @see IPAddressV6\n * @see IPAddressV4\n */\n\n#pragma once\n\n#include <functional>\n#include <iosfwd>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <utility> // std::pair\n\n#include <folly/ConstexprMath.h>\n#include <folly/IPAddressException.h>\n#include <folly/IPAddressV4.h>\n#include <folly/IPAddressV6.h>\n#include <folly/Range.h>\n#include <folly/detail/IPAddress.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nclass IPAddress;\n\n/**\n * Pair of IPAddress, netmask\n */\ntypedef std::pair<IPAddress, uint8_t> CIDRNetwork;\n\nclass IPAddress {\n private:\n  template <typename F>\n  auto pick(F f) const {\n    return isV4() ? f(asV4()) : isV6() ? f(asV6()) : f(asNone());\n  }\n\n  class IPAddressNone {\n   public:\n    bool isZero() const { return true; }\n    size_t bitCount() const { return 0; }\n    std::string toJson() const {\n      return \"{family:'AF_UNSPEC', addr:'', hash:0}\";\n    }\n    std::size_t hash() const { return std::hash<uint64_t>{}(0); }\n    bool isLoopback() const {\n      throw_exception<InvalidAddressFamilyException>(\"empty address\");\n    }\n    bool isLinkLocal() const {\n      throw_exception<InvalidAddressFamilyException>(\"empty address\");\n    }\n    bool isLinkLocalBroadcast() const {\n      throw_exception<InvalidAddressFamilyException>(\"empty address\");\n    }\n    bool isNonroutable() const {\n      throw_exception<InvalidAddressFamilyException>(\"empty address\");\n    }\n    bool isPrivate() const {\n      throw_exception<InvalidAddressFamilyException>(\"empty address\");\n    }\n    bool isMulticast() const {\n      throw_exception<InvalidAddressFamilyException>(\"empty address\");\n    }\n    IPAddress mask(uint8_t numBits) const {\n      (void)numBits;\n      return IPAddress();\n    }\n    std::string str() const { return \"\"; }\n    std::string toFullyQualified() const { return \"\"; }\n    void toFullyQualifiedAppend(std::string& out) const {\n      (void)out;\n      return;\n    }\n    uint8_t version() const { return 0; }\n    const unsigned char* bytes() const { return nullptr; }\n  };\n\n  IPAddressNone const& asNone() const {\n    if (!empty()) {\n      throw_exception<InvalidAddressFamilyException>(\"not empty\");\n    }\n    return addr_.ipNoneAddr;\n  }\n\n public:\n  /**\n   * Returns true if the input string can be parsed as an IP address.\n   */\n  static bool validate(StringPiece ip) noexcept;\n  /**\n   * Return the IPAddressV4 representation of the address, converting\n   * from V6 to V4 if needed.\n   *\n   *\n   *\n   * @throws IPAddressFormatException if the V6 Address is not IPv4 mappes\n   */\n  static IPAddressV4 createIPv4(const IPAddress& addr);\n\n  /**\n   * Return the address as a IPAddressV6, converting from V4 to V6 if\n   * needed.\n   */\n  static IPAddressV6 createIPv6(const IPAddress& addr);\n\n  /**\n   * Create a network and mask from a CIDR formatted address string.\n   *\n   * @param [in] ipSlashCidr IP/CIDR formatted string to split\n   * @param [in] defaultCidr default value if no /N specified (if defaultCidr\n   *             is -1, will use /32 for IPv4 and /128 for IPv6)\n   * @param [in] mask apply mask on the address or not,\n   *             e.g. 192.168.13.46/24 => 192.168.13.0/24\n   * @return either pair with IPAddress network and uint8_t mask or\n   *         CIDRNetworkError\n   */\n  static Expected<CIDRNetwork, CIDRNetworkError> tryCreateNetwork(\n      StringPiece ipSlashCidr, int defaultCidr = -1, bool mask = true);\n\n  /**\n   * Create a network and mask from a CIDR formatted address string.\n   *\n   * Same as tryCreateNetwork() but throws on error.\n   *\n   * @throws IPAddressFormatException\n   * @return pair with IPAddress network and uint8_t mask\n   */\n  static CIDRNetwork createNetwork(\n      StringPiece ipSlashCidr, int defaultCidr = -1, bool mask = true);\n\n  /**\n   * Return a string representation of a CIDR block created with\n   * createNetwork().\n   *\n   * @param [in] network pair of address and cidr\n   * @return string representing the netblock\n   */\n  static std::string networkToString(const CIDRNetwork& network);\n\n  /**\n   * Create a new IPAddress instance from the provided binary data\n   * in network byte order.\n   *\n   * @throws IPAddressFormatException if the length of `bytes` is not 4 or 16.\n   *\n   */\n  static IPAddress fromBinary(ByteRange bytes);\n\n  /**\n   * Non-throwing version of fromBinary().\n   * On failure returns IPAddressFormatError.\n   */\n  static Expected<IPAddress, IPAddressFormatError> tryFromBinary(\n      ByteRange bytes) noexcept;\n\n  /**\n   * Tries to create a new IPAddress instance from provided string.\n   *\n   * On failure, returns IPAddressFormatError.\n   */\n  static Expected<IPAddress, IPAddressFormatError> tryFromString(\n      StringPiece str) noexcept;\n\n  /**\n   * Tries to create a new IPAddress instance from the provided sockaddr.\n   *\n   * On failure, returns IPAddressFormatError.\n   */\n  static Expected<IPAddress, IPAddressFormatError> tryFromSockAddr(\n      const sockaddr* addr) noexcept;\n\n  /**\n   * Create an IPAddress from a `uint32_t`, using network byte order.\n   *\n   * @throws IPAddressFormatException if `src` does not represent a valid IP\n   * Address.\n   */\n  static IPAddress fromLong(uint32_t src);\n\n  /**\n   * Create an IPAddress from a `uint32_t`, using host byte order.\n   *\n   * @throws IPAddressFormatException if `src` does not represent a valid IP\n   * address.\n   */\n  static IPAddress fromLongHBO(uint32_t src);\n\n  /**\n   * Given 2 (IPAddress, mask) pairs, extract the longest common pair.\n   */\n  static CIDRNetwork longestCommonPrefix(\n      const CIDRNetwork& one, const CIDRNetwork& two);\n\n  /**\n   * Constructs an uninitialized IPAddress.\n   */\n  IPAddress();\n\n  /**\n   * Parse an IPAddress from a string representation.\n   *\n   * Formats accepted are exactly the same as the ones accepted by\n   * `inet_pton()`, using `AF_INET6` if the string contains colons, and `AF_INET\n   * `otherwise; with the exception that the whole address can optionally be\n   * enclosed in square brackets.\n   *\n   * @throws IPAddressFormatException if `str` is not a valid IP Address.\n   */\n  explicit IPAddress(StringPiece str);\n\n  /**\n   * Create an IPAddress from a `sockaddr` struct.\n   *\n   * @throws IPAddressFormatException if `addr` is a nullptr, or not `AF_INET`\n   * or `AF_INET6`\n   */\n  explicit IPAddress(const sockaddr* addr);\n\n  /**\n   * Create an IPAddress from an IPAddressV4\n   */\n  /* implicit */ IPAddress(const IPAddressV4 ipV4Addr) noexcept;\n\n  /**\n   * Create an IPAddress from an `in_addr` representation of an IPV4 address\n   */\n  /* implicit */ IPAddress(const in_addr addr) noexcept;\n\n  /**\n   * Create an IPAddress from an IPAddressV6\n   */\n  /* implicit */ IPAddress(const IPAddressV6& ipV6Addr) noexcept;\n\n  /**\n   * Create an IPAddress from an `in6_addr` representation of an IPV6 address\n   */\n  /* implicit */ IPAddress(const in6_addr& addr) noexcept;\n\n  /**\n   * @overloadbrief Copy assignment from other IPAddress representations\n   *\n   * Copy assignment from an IPAddressV4\n   */\n  IPAddress& operator=(const IPAddressV4& ipv4_addr) noexcept;\n\n  /**\n   * Copy assignment from an IPAddressV6\n   */\n  IPAddress& operator=(const IPAddressV6& ipv6_addr) noexcept;\n\n  /**\n   * Converts an IPAddress to an IPAddressV4 instance.\n   *\n   * @note This is not some handy convenience wrapper to convert an IPv4 address\n   *       to a mapped IPv6 address. If you want that use createIPv6()\n   *\n   * @throws InvalidAddressFamilyException if the IP Address is not currently\n   * a valid V4 instance.\n   */\n  const IPAddressV4& asV4() const {\n    if (FOLLY_UNLIKELY(!isV4())) {\n      asV4Throw();\n    }\n    return addr_.ipV4Addr;\n  }\n\n  /**\n   * Converts an IPAddress to an IPAddressV6 instance\n   *\n   * @throws InvalidAddressFamilyException if the IP Address is not currently\n   * a valid V6 instance.\n   */\n  const IPAddressV6& asV6() const {\n    if (FOLLY_UNLIKELY(!isV6())) {\n      asV6Throw();\n    }\n    return addr_.ipV6Addr;\n  }\n\n  /**\n   * Return then `sa_family_t` of the IP Address\n   */\n  sa_family_t family() const { return family_; }\n\n  /**\n   * Given a `sockaddr_storage` struct, populate it with an appropriate value.\n   *\n   * @param [out] dest The struct to populate\n   * @param port The port number to put in the struct (defaults to 0)\n   *\n   */\n  int toSockaddrStorage(sockaddr_storage* dest, uint16_t port = 0) const {\n    if (dest == nullptr) {\n      throw_exception<IPAddressFormatException>(\"dest must not be null\");\n    }\n    memset(dest, 0, sizeof(sockaddr_storage));\n    dest->ss_family = family();\n\n    if (isV4()) {\n      sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(dest);\n      sin->sin_addr = asV4().toAddr();\n      sin->sin_port = port;\n#if defined(__APPLE__)\n      sin->sin_len = sizeof(*sin);\n#endif\n      return sizeof(*sin);\n    } else if (isV6()) {\n      sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(dest);\n      sin->sin6_addr = asV6().toAddr();\n      sin->sin6_port = port;\n      sin->sin6_scope_id = asV6().getScopeId();\n#if defined(__APPLE__)\n      sin->sin6_len = sizeof(*sin);\n#endif\n      return sizeof(*sin);\n    } else {\n      throw_exception<InvalidAddressFamilyException>(family());\n    }\n  }\n\n  /**\n   * @overloadbrief Check if the address is found in the specified CIDR\n   * netblock.\n   *\n   * This will return false if the specified cidrNet is V4, but the address is\n   * V6. It will also return false if the specified cidrNet is V6 but the\n   * address is V4. This method will do the right thing in the case of a v6\n   * mapped v4 address.\n   *\n   * @note This is slower than the below counterparts. If perf is important use\n   *       one of the two argument variations below.\n   * @param [in] cidrNetwork address in \"192.168.1.0/24\" format\n   * @throws IPAddressFormatException if no /mask in cidrNetwork\n   * @return true if address is part of specified subnet with cidr\n   */\n  bool inSubnet(StringPiece cidrNetwork) const;\n\n  /**\n   * Check if an IPAddress belongs to a subnet.\n   * @param [in] subnet Subnet to check against (e.g. 192.168.1.0)\n   * @param [in] cidr   CIDR for subnet (e.g. 24 for /24)\n   * @return true if address is part of specified subnet with cidr\n   */\n  bool inSubnet(const IPAddress& subnet, uint8_t cidr) const;\n\n  /**\n   * Check if an IPAddress belongs to the subnet with the given mask.\n   *\n   * This is the same as inSubnet but the mask is provided instead of looked up\n   * from the cidr.\n   * @param [in] subnet Subnet to check against\n   * @param [in] mask   The netmask for the subnet\n   * @return true if address is part of the specified subnet with mask\n   */\n  bool inSubnetWithMask(const IPAddress& subnet, ByteRange mask) const;\n\n  /**\n   * Returns true if address is a v4 mapped address\n   */\n  bool isIPv4Mapped() const { return isV6() && asV6().isIPv4Mapped(); }\n\n  /**\n   * Returns true if address is uninitialised\n   */\n  bool empty() const { return family_ == AF_UNSPEC; }\n\n  /**\n   * Returns true if address is initalised\n   */\n  explicit operator bool() const { return !empty(); }\n\n  /**\n   * Returns true if this represents an IPv4 address\n   */\n  bool isV4() const { return family_ == AF_INET; }\n\n  /**\n   * Returns true if this represents an IPv6 address\n   */\n  bool isV6() const { return family_ == AF_INET6; }\n\n  /**\n   * Returns true if the address is all zeros\n   */\n  bool isZero() const {\n    return pick([&](auto& _) { return _.isZero(); });\n  }\n\n  /**\n   * The number of bits in the address representation\n   */\n  size_t bitCount() const {\n    return pick([&](auto& _) { return _.bitCount(); });\n  }\n\n  /**\n   * The number of bytes in the address representation\n   */\n  size_t byteCount() const { return bitCount() / 8; }\n\n  /**\n   * Get the nth most significant bit of the IP address (0-indexed).\n   * @param bitIndex n\n   */\n  bool getNthMSBit(size_t bitIndex) const {\n    return detail::getNthMSBitImpl(*this, bitIndex, family());\n  }\n\n  /**\n   * Get the nth most significant byte of the IP address (0-indexed).\n   * @param byteIndex n\n   */\n  uint8_t getNthMSByte(size_t byteIndex) const;\n\n  /**\n   * Get the nth bit of the IP address (0-indexed).\n   * @param bitIndex n\n   */\n  bool getNthLSBit(size_t bitIndex) const {\n    return getNthMSBit(bitCount() - bitIndex - 1);\n  }\n\n  /**\n   * Get the nth byte of the IP address (0-indexed).\n   * @param byteIndex n\n   */\n  uint8_t getNthLSByte(size_t byteIndex) const {\n    return getNthMSByte(byteCount() - byteIndex - 1);\n  }\n\n  /**\n   * Get a json representation of the IP address.\n   *\n   * This prints a string representation of the address, for human consumption\n   * or logging. The string will take the form of a JSON object that looks like:\n   *  `{family:'AF_INET|AF_INET6', addr:'address', hash:long}`.\n   */\n  std::string toJson() const {\n    return pick([&](auto& _) { return _.toJson(); });\n  }\n\n  /**\n   * Returns a hash of the IP address.\n   */\n  std::size_t hash() const {\n    return pick([&](auto& _) { return _.hash(); });\n  }\n\n  /**\n   * Return true if the IP address qualifies as localhost.\n   */\n  bool isLoopback() const {\n    return pick([&](auto& _) { return _.isLoopback(); });\n  }\n\n  /**\n   * Return true if the IP address qualifies as link local\n   */\n  bool isLinkLocal() const {\n    return pick([&](auto& _) { return _.isLinkLocal(); });\n  }\n\n  /**\n   * Return true if the IP address qualifies as broadcast.\n   */\n  bool isLinkLocalBroadcast() const {\n    return pick([&](auto& _) { return _.isLinkLocalBroadcast(); });\n  }\n\n  /**\n   * Return true if the IP address is a special purpose address, as defined per\n   * RFC 6890 (i.e. 0.0.0.0).\n   *\n   * For V6, true if the address is not in one of global scope blocks:\n   * 2000::/3, ffxe::/16.\n   */\n  bool isNonroutable() const {\n    return pick([&](auto& _) { return _.isNonroutable(); });\n  }\n\n  /**\n   * Return true if the IP address is private, as per RFC 1918 and RFC 4193.\n   *\n   * For example, 192.168.xxx.xxx or fc00::/7 addresses.\n   */\n  bool isPrivate() const {\n    return pick([&](auto& _) { return _.isPrivate(); });\n  }\n\n  /**\n   * Return true if the IP address is a multicast address.\n   */\n  bool isMulticast() const {\n    return pick([&](auto& _) { return _.isMulticast(); });\n  }\n\n  /**\n   * Creates an IPAddress instance with all but most significant numBits set to\n   * 0.\n   *\n   * @throws IPAddressFormatException if numBits > bitCount()\n   *\n   * @param [in] numBits number of bits to mask\n   * @return IPAddress instance with bits set to 0\n   */\n  IPAddress mask(uint8_t numBits) const {\n    return pick([&](auto& _) { return IPAddress(_.mask(numBits)); });\n  }\n\n  /**\n   * Provides a string representation of address.\n   *\n   * @throws IPAddressFormatException on `inet_ntop` error.\n   *\n   * @note The string representation is calculated on demand.\n   */\n  std::string str() const {\n    return pick([&](auto& _) { return _.str(); });\n  }\n\n  /**\n   * Return the fully qualified string representation of the address.\n   *\n   * For V4 addresses this is the same as calling str(). For V6 addresses\n   * this is the hex representation with : characters inserted every 4 digits.\n   */\n  std::string toFullyQualified() const {\n    return pick([&](auto& _) { return _.toFullyQualified(); });\n  }\n\n  /**\n   * Same as toFullyQualified() but append to an output string.\n   */\n  void toFullyQualifiedAppend(std::string& out) const {\n    return pick([&](auto& _) { return _.toFullyQualifiedAppend(out); });\n  }\n\n  /**\n   * Returns the IP address version. 0 if empty, 4 or 6 if nonempty.\n   */\n  uint8_t version() const {\n    return pick([&](auto& _) { return _.version(); });\n  }\n\n  /**\n   * Returns a pointer to the to IP address bytes, in network byte order.\n   */\n  const unsigned char* bytes() const {\n    return pick([&](auto& _) { return _.bytes(); });\n  }\n\n private:\n  [[noreturn]] void asV4Throw() const;\n  [[noreturn]] void asV6Throw() const;\n\n  typedef union IPAddressV46 {\n    IPAddressNone ipNoneAddr;\n    IPAddressV4 ipV4Addr;\n    IPAddressV6 ipV6Addr;\n    IPAddressV46() noexcept : ipNoneAddr() {}\n    explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {}\n    explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {}\n  } IPAddressV46;\n  IPAddressV46 addr_;\n  sa_family_t family_;\n};\n\n/**\n * `boost::hash` uses hash_value(), so this allows `boost::hash` to work\n * automatically for IPAddress\n */\nstd::size_t hash_value(const IPAddress& addr);\n\n/**\n * Appends a string representation of the IP address to the stream using str().\n */\nstd::ostream& operator<<(std::ostream& os, const IPAddress& addr);\n\n/**\n * @overloadbrief Define toAppend() to allow IPAddress to be used with\n * `folly::to<string>`\n */\nvoid toAppend(IPAddress addr, std::string* result);\nvoid toAppend(IPAddress addr, fbstring* result);\n\n/**\n * Return true if two addresses are equal.\n *\n * V4-to-V6-mapped addresses are compared as V4 addresses.\n *\n * @return true if the two addresses are equal.\n */\nbool operator==(const IPAddress& addr1, const IPAddress& addr2);\n\n/**\n * Return true if `addr1 < addr2`\n *\n * V4-to-V6-mapped addresses are compared as V4 addresses.\n */\nbool operator<(const IPAddress& addr1, const IPAddress& addr2);\n\n/**\n * Return true if two address are not equal\n *\n * V4-to-V6-mapped addresses are compared as V4 addresses.\n */\ninline bool operator!=(const IPAddress& addr1, const IPAddress& addr2) {\n  return !(addr1 == addr2);\n}\n\n/**\n * Return true if `addr1 > addr2`\n *\n * V4-to-V6-mapped addresses are compared as V4 addresses.\n */\ninline bool operator>(const IPAddress& addr1, const IPAddress& addr2) {\n  return addr2 < addr1;\n}\n\n/**\n * Return true if `addr1 <= addr2`\n *\n * V4-to-V6-mapped addresses are compared as V4 addresses.\n */\ninline bool operator<=(const IPAddress& addr1, const IPAddress& addr2) {\n  return !(addr1 > addr2);\n}\n\n/**\n * Return true if `addr1 >= addr2`\n *\n * V4-to-V6-mapped addresses are compared as V4 addresses.\n */\ninline bool operator>=(const IPAddress& addr1, const IPAddress& addr2) {\n  return !(addr1 < addr2);\n}\n\n} // namespace folly\n\nnamespace std {\ntemplate <>\nstruct hash<folly::IPAddress> {\n  size_t operator()(const folly::IPAddress& addr) const { return addr.hash(); }\n};\n} // namespace std\n"
  },
  {
    "path": "folly/IPAddressException.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Error enums and exceptions for indicating errors when dealing with IP\n * Addresses. Used in IPAddress, IPAddressV4, and IPAddressV6.\n *\n * @file IPAddressException.h\n */\n\n#pragma once\n\n#include <exception>\n#include <string>\n#include <utility>\n\n#include <folly/CPortability.h>\n#include <folly/detail/IPAddress.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\n/**\n * Error codes for non-throwing interface of IPAddress family of functions.\n */\nenum class IPAddressFormatError {\n  INVALID_IP,\n  UNSUPPORTED_ADDR_FAMILY,\n  NULL_SOCKADDR,\n};\n\n/**\n * Wraps errors from parsing IP/MASK string\n */\nenum class CIDRNetworkError {\n  INVALID_DEFAULT_CIDR,\n  INVALID_IP_SLASH_CIDR,\n  INVALID_IP,\n  INVALID_CIDR,\n  CIDR_MISMATCH,\n};\n\n/**\n * Exception that is thrown when dealing with invalid IP addresses. A subclass\n * of `std::runtime_error`\n */\nclass FOLLY_EXPORT IPAddressFormatException : public std::runtime_error {\n public:\n  using std::runtime_error::runtime_error;\n};\n\n/**\n * Exception that is thrown when an IP Address is not of the family expected\n * (ie, expected a V4 but is a V6). A subclass of IPAddressFormatException.\n */\nclass FOLLY_EXPORT InvalidAddressFamilyException\n    : public IPAddressFormatException {\n public:\n  explicit InvalidAddressFamilyException(const char* msg)\n      : IPAddressFormatException{msg} {}\n  explicit InvalidAddressFamilyException(const std::string& msg) noexcept\n      : IPAddressFormatException{msg} {}\n  explicit InvalidAddressFamilyException(sa_family_t family) noexcept\n      : InvalidAddressFamilyException(\n            \"Address family \" + detail::familyNameStr(family) +\n            \" is not AF_INET or AF_INET6\") {}\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/IPAddressV4.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/IPAddressV4.h>\n\n#include <ostream>\n#include <string>\n\n#include <fmt/core.h>\n\n#include <folly/Conv.h>\n#include <folly/IPAddress.h>\n#include <folly/IPAddressV6.h>\n#include <folly/String.h>\n#include <folly/detail/IPAddressSource.h>\n\nusing std::ostream;\nusing std::string;\n\nnamespace folly {\n\n// free functions\nsize_t hash_value(const IPAddressV4& addr) {\n  return addr.hash();\n}\nostream& operator<<(ostream& os, const IPAddressV4& addr) {\n  os << addr.str();\n  return os;\n}\nvoid toAppend(IPAddressV4 addr, string* result) {\n  result->append(addr.str());\n}\nvoid toAppend(IPAddressV4 addr, fbstring* result) {\n  result->append(addr.str());\n}\n\nbool IPAddressV4::validate(StringPiece ip) noexcept {\n  return tryFromString(ip).hasValue();\n}\n\n// public static\nIPAddressV4 IPAddressV4::fromLong(uint32_t src) {\n  in_addr addr;\n  addr.s_addr = src;\n  return IPAddressV4(addr);\n}\n\nIPAddressV4 IPAddressV4::fromLongHBO(uint32_t src) {\n  in_addr addr;\n  addr.s_addr = htonl(src);\n  return IPAddressV4(addr);\n}\n\n// static public\nuint32_t IPAddressV4::toLong(StringPiece ip) {\n  auto str = ip.str();\n  in_addr addr;\n  if (inet_pton(AF_INET, str.c_str(), &addr) != 1) {\n    throw IPAddressFormatException(\n        fmt::format(\"Can't convert invalid IP '{}' to long\", ip));\n  }\n  return addr.s_addr;\n}\n\n// static public\nuint32_t IPAddressV4::toLongHBO(StringPiece ip) {\n  return ntohl(IPAddressV4::toLong(ip));\n}\n\n// public default constructor\nIPAddressV4::IPAddressV4() = default;\n\n// ByteArray4 constructor\nIPAddressV4::IPAddressV4(const ByteArray4& src) noexcept : addr_(src) {}\n\n// public string constructor\nIPAddressV4::IPAddressV4(StringPiece addr) : addr_() {\n  auto maybeIp = tryFromString(addr);\n  if (maybeIp.hasError()) {\n    throw IPAddressFormatException(\n        to<std::string>(\"Invalid IPv4 address '\", addr, \"'\"));\n  }\n  *this = maybeIp.value();\n}\n\nExpected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromString(\n    StringPiece str) noexcept {\n  struct in_addr inAddr;\n  if (inet_pton(AF_INET, str.str().c_str(), &inAddr) != 1) {\n    return makeUnexpected(IPAddressFormatError::INVALID_IP);\n  }\n  return IPAddressV4(inAddr);\n}\n\n// in_addr constructor\nIPAddressV4::IPAddressV4(const in_addr src) noexcept : addr_(src) {}\n\nIPAddressV4 IPAddressV4::fromBinary(ByteRange bytes) {\n  auto maybeIp = tryFromBinary(bytes);\n  if (maybeIp.hasError()) {\n    throw IPAddressFormatException(\n        to<std::string>(\n            \"Invalid IPv4 binary data: length must be 4 bytes, got \",\n            bytes.size()));\n  }\n  return maybeIp.value();\n}\n\nExpected<IPAddressV4, IPAddressFormatError> IPAddressV4::tryFromBinary(\n    ByteRange bytes) noexcept {\n  IPAddressV4 addr;\n  auto setResult = addr.trySetFromBinary(bytes);\n  if (setResult.hasError()) {\n    return makeUnexpected(setResult.error());\n  }\n  return addr;\n}\n\nExpected<Unit, IPAddressFormatError> IPAddressV4::trySetFromBinary(\n    ByteRange bytes) noexcept {\n  if (bytes.size() != 4) {\n    return makeUnexpected(IPAddressFormatError::INVALID_IP);\n  }\n  memcpy(&addr_.inAddr_.s_addr, bytes.data(), sizeof(in_addr));\n  return folly::unit;\n}\n\n// static\nIPAddressV4 IPAddressV4::fromInverseArpaName(const std::string& arpaname) {\n  auto piece = StringPiece(arpaname);\n  // input must be something like 1.0.168.192.in-addr.arpa\n  if (!piece.removeSuffix(\".in-addr.arpa\")) {\n    throw IPAddressFormatException(\n        fmt::format(\"input does not end with '.in-addr.arpa': '{}'\", arpaname));\n  }\n  std::vector<StringPiece> pieces;\n  split('.', piece, pieces);\n  if (pieces.size() != 4) {\n    throw IPAddressFormatException(fmt::format(\"Invalid input. Got {}\", piece));\n  }\n  // reverse 1.0.168.192 -> 192.168.0.1\n  return IPAddressV4(join(\".\", pieces.rbegin(), pieces.rend()));\n}\nIPAddressV6 IPAddressV4::createIPv6() const {\n  ByteArray16 ba{};\n  ba[10] = 0xff;\n  ba[11] = 0xff;\n  std::memcpy(&ba[12], bytes(), 4);\n  return IPAddressV6(ba);\n}\n\n// public\nIPAddressV6 IPAddressV4::getIPv6For6To4() const {\n  ByteArray16 ba{};\n  ba[0] = (uint8_t)((IPAddressV6::PREFIX_6TO4 & 0xFF00) >> 8);\n  ba[1] = (uint8_t)(IPAddressV6::PREFIX_6TO4 & 0x00FF);\n  std::memcpy(&ba[2], bytes(), 4);\n  return IPAddressV6(ba);\n}\n\n// public\nstring IPAddressV4::toJson() const {\n  return fmt::format(\"{{family:'AF_INET', addr:'{}', hash:{}}}\", str(), hash());\n}\n\n// public\nbool IPAddressV4::inSubnet(StringPiece cidrNetwork) const {\n  auto subnetInfo = IPAddress::createNetwork(cidrNetwork);\n  auto addr = subnetInfo.first;\n  if (!addr.isV4()) {\n    throw IPAddressFormatException(\n        fmt::format(\"Address '{}' is not a V4 address\", addr.toJson()));\n  }\n  return inSubnetWithMask(addr.asV4(), fetchMask(subnetInfo.second));\n}\n\n// public\nbool IPAddressV4::inSubnetWithMask(\n    const IPAddressV4& subnet, const ByteArray4 cidrMask) const {\n  const auto mask = detail::Bytes::mask(toByteArray(), cidrMask);\n  const auto subMask = detail::Bytes::mask(subnet.toByteArray(), cidrMask);\n  return (mask == subMask);\n}\n\n// public\nbool IPAddressV4::isLoopback() const {\n  static IPAddressV4 loopback_addr(\"127.0.0.0\");\n  return inSubnetWithMask(loopback_addr, fetchMask(8));\n}\n\n// public\nbool IPAddressV4::isLinkLocal() const {\n  static IPAddressV4 linklocal_addr(\"169.254.0.0\");\n  return inSubnetWithMask(linklocal_addr, fetchMask(16));\n}\n\n// public\nbool IPAddressV4::isNonroutable() const {\n  auto ip = toLongHBO();\n  FOLLY_PUSH_WARNING\n  FOLLY_CLANG_DISABLE_WARNING(\"-Wtautological-type-limit-compare\")\n  return isPrivate() ||\n      (/* align */ true && ip <= 0x00FFFFFF) || // 0.0.0.0-0.255.255.255\n      (ip >= 0xC0000000 && ip <= 0xC00000FF) || // 192.0.0.0-192.0.0.255\n      (ip >= 0xC0000200 && ip <= 0xC00002FF) || // 192.0.2.0-192.0.2.255\n      (ip >= 0xC6120000 && ip <= 0xC613FFFF) || // 198.18.0.0-198.19.255.255\n      (ip >= 0xC6336400 && ip <= 0xC63364FF) || // 198.51.100.0-198.51.100.255\n      (ip >= 0xCB007100 && ip <= 0xCB0071FF) || // 203.0.113.0-203.0.113.255\n      (ip >= 0xE0000000 && ip <= 0xFFFFFFFF) || // 224.0.0.0-255.255.255.255\n      false;\n  FOLLY_POP_WARNING\n}\n\n// public\nbool IPAddressV4::isPrivate() const {\n  auto ip = toLongHBO();\n  return // some ranges below\n      (ip >= 0x0A000000 && ip <= 0x0AFFFFFF) || // 10.0.0.0-10.255.255.255\n      (ip >= 0x7F000000 && ip <= 0x7FFFFFFF) || // 127.0.0.0-127.255.255.255\n      (ip >= 0xA9FE0000 && ip <= 0xA9FEFFFF) || // 169.254.0.0-169.254.255.255\n      (ip >= 0xAC100000 && ip <= 0xAC1FFFFF) || // 172.16.0.0-172.31.255.255\n      (ip >= 0xC0A80000 && ip <= 0xC0A8FFFF) || // 192.168.0.0-192.168.255.255\n      (ip >= 0x64400000 && ip <= 0x647fffff) || // 100.64.0.0-100.127.255.255\n      false;\n}\n\n// public\nbool IPAddressV4::isMulticast() const {\n  return (toLongHBO() & 0xf0000000) == 0xe0000000;\n}\n\n// public\nIPAddressV4 IPAddressV4::mask(size_t numBits) const {\n  static const auto bits = bitCount();\n  if (numBits > bits) {\n    throw IPAddressFormatException(\n        fmt::format(\"numBits({}) > bitsCount({})\", numBits, bits));\n  }\n\n  ByteArray4 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_);\n  return IPAddressV4(ba);\n}\n\n// public\nstring IPAddressV4::str() const {\n  return detail::fastIpv4ToString(addr_.inAddr_);\n}\n\n// public\nvoid IPAddressV4::toFullyQualifiedAppend(std::string& out) const {\n  detail::fastIpv4AppendToString(addr_.inAddr_, out);\n}\n\n// public\nstring IPAddressV4::toInverseArpaName() const {\n  return fmt::format(\n      \"{}.{}.{}.{}.in-addr.arpa\",\n      addr_.bytes_[3],\n      addr_.bytes_[2],\n      addr_.bytes_[1],\n      addr_.bytes_[0]);\n}\n\n// public\nuint8_t IPAddressV4::getNthMSByte(size_t byteIndex) const {\n  const auto highestIndex = byteCount() - 1;\n  if (byteIndex > highestIndex) {\n    throw std::invalid_argument(\n        fmt::format(\n            \"Byte index must be <= {} for addresses of type: {}\",\n            highestIndex,\n            detail::familyNameStr(AF_INET)));\n  }\n  return bytes()[byteIndex];\n}\n// protected\nByteArray4 IPAddressV4::fetchMask(size_t numBits) {\n  static const size_t bits = bitCount();\n  if (numBits > bits) {\n    throw IPAddressFormatException(\"IPv4 addresses are 32 bits\");\n  }\n  auto const val = Endian::big(uint32_t(~uint64_t(0) << (32 - numBits)));\n  ByteArray4 arr;\n  std::memcpy(arr.data(), &val, sizeof(val));\n  return arr;\n}\n// public static\nCIDRNetworkV4 IPAddressV4::longestCommonPrefix(\n    const CIDRNetworkV4& one, const CIDRNetworkV4& two) {\n  auto prefix = detail::Bytes::longestCommonPrefix(\n      one.first.addr_.bytes_, one.second, two.first.addr_.bytes_, two.second);\n  return {IPAddressV4(prefix.first), prefix.second};\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/IPAddressV4.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A representation of an IPv4 address\n *\n * @class folly::IPAddressV4\n * @see IPAddress\n * @see IPAddressV6\n */\n\n#pragma once\n\n#include <cstring>\n\n#include <array>\n#include <functional>\n#include <iosfwd>\n\n#include <folly/Expected.h>\n#include <folly/FBString.h>\n#include <folly/IPAddressException.h>\n#include <folly/Range.h>\n#include <folly/detail/IPAddress.h>\n#include <folly/hash/Hash.h>\n\nnamespace folly {\n\nclass IPAddress;\nclass IPAddressV4;\nclass IPAddressV6;\n\n/**\n * Pair of IPAddressV4, netmask\n */\nusing CIDRNetworkV4 = std::pair<IPAddressV4, uint8_t>;\n\n/**\n * Specialization of `std::array` for IPv4 addresses\n */\nusing ByteArray4 = std::array<uint8_t, 4>;\n\nclass IPAddressV4 {\n public:\n  /**\n   * Max size of std::string returned by toFullyQualified()\n   */\n  static constexpr size_t kMaxToFullyQualifiedSize =\n      4 /*words*/ * 3 /*max chars per word*/ + 3 /*separators*/;\n\n  /**\n   * Returns true if the input string can be parsed as an IP address.\n   */\n  static bool validate(StringPiece ip) noexcept;\n\n  /**\n   * Create an IPAddressV4 instance from a uint32_t, using network byte\n   * order\n   */\n  static IPAddressV4 fromLong(uint32_t src);\n  /**\n   * Create an IPAddressV4 instance from a uint32_t, using host byte\n   * order\n   */\n  static IPAddressV4 fromLongHBO(uint32_t src);\n\n  /**\n   * Create a new IPAddressV4 from the provided ByteRange.\n   *\n   * @throws IPAddressFormatException if the input length is not 4 bytes.\n   */\n  static IPAddressV4 fromBinary(ByteRange bytes);\n\n  /**\n   * Create a new IPAddressV4 from the provided ByteRange.\n   *\n   * Returns an IPAddressFormatError if the input length is not 4 bytes.\n   */\n  static Expected<IPAddressV4, IPAddressFormatError> tryFromBinary(\n      ByteRange bytes) noexcept;\n\n  /**\n   * Create a new IPAddressV4 from the provided string.\n   *\n   * Returns an IPAddressFormatError if the string is not a valid IP.\n   */\n  static Expected<IPAddressV4, IPAddressFormatError> tryFromString(\n      StringPiece str) noexcept;\n\n  /**\n   * Returns the address as a ByteRange.\n   */\n  ByteRange toBinary() const {\n    return ByteRange((const unsigned char*)&addr_.inAddr_.s_addr, 4);\n  }\n\n  /**\n   * Create a new IPAddressV4 from a `in-addr.arpa` representation of an IP\n   * address.\n   *\n   * @throws IPAddressFormatException if the input is not a valid in-addr.arpa\n   * representation\n   */\n  static IPAddressV4 fromInverseArpaName(const std::string& arpaname);\n\n  /**\n   * Convert a IPv4 address string to a long, in network byte order.\n   */\n\n  static uint32_t toLong(StringPiece ip);\n\n  /**\n   * Convert a IPv4 address string to a long, in host byte order.\n   *\n   * This is slightly slower than toLong()\n   */\n  static uint32_t toLongHBO(StringPiece ip);\n\n  /**\n   * Default constructor for IPAddressV4.\n   *\n   * The address value will be 0.0.0.0\n   */\n  IPAddressV4();\n\n  /**\n   * Construct an IPAddressV4 from a string.\n   *\n   * @throws IPAddressFormatException if the string is not a valid IPv4\n   * address.\n   */\n  explicit IPAddressV4(StringPiece addr);\n\n  /**\n   * Construct an IPAddressV4 from a ByteArray4, in network byte order.\n   */\n  explicit IPAddressV4(const ByteArray4& src) noexcept;\n\n  /**\n   * Construct an IPAddressV4 from an `in_addr` representation of an IPV4\n   * address\n   */\n  explicit IPAddressV4(const in_addr src) noexcept;\n\n  /**\n   * Return the IPV6 mapped representation of the address.\n   */\n  IPAddressV6 createIPv6() const;\n\n  /**\n   * Return an IPV6 address in the format of a 6To4 address.\n   */\n  IPAddressV6 getIPv6For6To4() const;\n\n  /**\n   * Return the uint32_t representation of the address, in network byte order.\n   */\n  uint32_t toLong() const { return toAddr().s_addr; }\n\n  /**\n   * Return the uint32_t representation of the address, in host byte order.\n   */\n  uint32_t toLongHBO() const { return ntohl(toLong()); }\n\n  /**\n   * Returns the number of bits in the IP address.\n   *\n   * @returns 32\n   */\n  static constexpr size_t bitCount() { return 32; }\n\n  /**\n   * Get a json representation of the IP address.\n   *\n   * This prints a string representation of the address, for human consumption\n   * or logging. The string will take the form of a JSON object that looks like:\n   *  `{family:'AF_INET', addr:'address', hash:long}`.\n   */\n  std::string toJson() const;\n\n  /**\n   * Returns a hash of the IP address.\n   */\n  size_t hash() const {\n    static const uint32_t seed = AF_INET;\n    uint32_t hashed = hash::fnv32_buf(&addr_, 4);\n    return hash::hash_combine(seed, hashed);\n  }\n\n  /**\n   * @overloadbrief Check if the IP address is found in the specified CIDR\n   * netblock.\n   *\n   * @throws IPAddressFormatException if no /mask\n   *\n   * @note This is slower than the other inSubnet() overload. If perf is\n   * important use the other overload, or inSubnetWithMask().\n   * @param [in] cidrNetwork address in \"192.168.1.0/24\" format\n   * @return true if address is part of specified subnet with cidr\n   */\n  bool inSubnet(StringPiece cidrNetwork) const;\n\n  /**\n   * Check if an IPAddressV4 belongs to a subnet.\n   * @param [in] subnet Subnet to check against (e.g. 192.168.1.0)\n   * @param [in] cidr   CIDR for subnet (e.g. 24 for /24)\n   * @return true if address is part of specified subnet with cidr\n   */\n  bool inSubnet(const IPAddressV4& subnet, uint8_t cidr) const {\n    return inSubnetWithMask(subnet, fetchMask(cidr));\n  }\n\n  /**\n   * Check if an IPAddressV4 belongs to the subnet with the given mask.\n   *\n   * This is the same as inSubnet but the mask is provided instead of looked up\n   * from the cidr.\n   * @param [in] subnet Subnet to check against\n   * @param [in] mask   The netmask for the subnet\n   * @return true if address is part of the specified subnet with mask\n   */\n  bool inSubnetWithMask(const IPAddressV4& subnet, const ByteArray4 mask) const;\n\n  /**\n   * Return true if the IP address qualifies as localhost.\n   */\n  bool isLoopback() const;\n\n  /**\n   * Return true if the IP address qualifies as link local\n   */\n  bool isLinkLocal() const;\n\n  /**\n   * Return true if the IP address is a special purpose address, as defined per\n   * RFC 6890 (i.e. 0.0.0.0).\n   *\n   */\n  bool isNonroutable() const;\n  /**\n   * Return true if the IP address is private, as per RFC 1918 and RFC 4193.\n   *\n   * For example, 192.168.xxx.xxx\n   */\n  bool isPrivate() const;\n\n  /**\n   * Return true if the IP address is a multicast address.\n   */\n  bool isMulticast() const;\n\n  /**\n   * Returns true if the address is all zeros\n   */\n  bool isZero() const {\n    constexpr auto zero = ByteArray4{{}};\n    return 0 == std::memcmp(bytes(), zero.data(), zero.size());\n  }\n\n  /**\n   * Return true if the IP address qualifies as broadcast.\n   */\n  bool isLinkLocalBroadcast() const {\n    return (INADDR_BROADCAST == toLongHBO());\n  }\n\n  /**\n   * Creates an IPAddressV4 with all but most significant numBits set to\n   * 0.\n   *\n   * @throws IPAddressFormatException if numBits > bitCount()\n   *\n   * @param [in] numBits number of bits to mask\n   * @return IPAddress instance with bits set to 0\n   */\n  IPAddressV4 mask(size_t numBits) const;\n\n  /**\n   * Provides a string representation of address.\n   *\n   * @throws if IPAddressFormatException on `inet_ntop` error.\n   *\n   * The string representation is calculated on demand.\n   */\n  std::string str() const;\n\n  /**\n   * Create the inverse arpa representation of the IP address.\n   *\n   */\n  std::string toInverseArpaName() const;\n\n  /**\n   * Return the underlying `in_addr` structure\n   */\n  in_addr toAddr() const { return addr_.inAddr_; }\n\n  /**\n   * Return the IP address represented as a `sockaddr_in` struct\n   *\n   */\n  sockaddr_in toSockAddr() const {\n    sockaddr_in addr;\n    memset(&addr, 0, sizeof(sockaddr_in));\n    addr.sin_family = AF_INET;\n    memcpy(&addr.sin_addr, &addr_.inAddr_, sizeof(in_addr));\n    return addr;\n  }\n\n  /**\n   * Return a ByteArray4 containing the bytes of the IP address.\n   */\n  ByteArray4 toByteArray() const {\n    ByteArray4 ba{{0}};\n    std::memcpy(ba.data(), bytes(), 4);\n    return ba;\n  }\n\n  /**\n   * Return the fully qualified string representation of the address.\n   *\n   * This is the same as calling str().\n   */\n  std::string toFullyQualified() const { return str(); }\n\n  /**\n   * Same as toFullyQualified() but append to an output string.\n   */\n  void toFullyQualifiedAppend(std::string& out) const;\n\n  /**\n   * Returns the version of the IP Address (4).\n   */\n  uint8_t version() const { return 4; }\n\n  /**\n   * Return the mask associated with the given number of bits.\n   *\n   * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should\n   * be {0xff, 0xff, 0xff, 0x00}.\n   *\n   * @param [in] numBits bitmask to retrieve\n   * @throws abort if numBits == 0 or numBits > bitCount()\n   * @return mask associated with numBits\n   */\n  static ByteArray4 fetchMask(size_t numBits);\n\n  /**\n   * Given 2 (IPAddressV4, mask) pairs extract the longest common (IPAddressV4,\n   * mask) pair\n   */\n  static CIDRNetworkV4 longestCommonPrefix(\n      const CIDRNetworkV4& one, const CIDRNetworkV4& two);\n\n  /**\n   * Return the number of bytes in the IP address.\n   *\n   * @returns 4\n   */\n  static size_t byteCount() { return 4; }\n\n  /**\n   * Get the nth most significant bit of the IP address (0-indexed).\n   * @param bitIndex n\n   */\n  bool getNthMSBit(size_t bitIndex) const {\n    return detail::getNthMSBitImpl(*this, bitIndex, AF_INET);\n  }\n\n  /**\n   * Get the nth most significant byte of the IP address (0-indexed).\n   * @param byteIndex n\n   */\n  uint8_t getNthMSByte(size_t byteIndex) const;\n\n  /**\n   * Get the nth bit of the IP address (0-indexed).\n   * @param bitIndex n\n   */\n  bool getNthLSBit(size_t bitIndex) const {\n    return getNthMSBit(bitCount() - bitIndex - 1);\n  }\n\n  /**\n   * Get the nth byte of the IP address (0-indexed).\n   * @param byteIndex n\n   */\n  uint8_t getNthLSByte(size_t byteIndex) const {\n    return getNthMSByte(byteCount() - byteIndex - 1);\n  }\n\n  /**\n   * Returns a pointer to the to IP address bytes, in network byte order.\n   */\n  const unsigned char* bytes() const { return addr_.bytes_.data(); }\n\n private:\n  union AddressStorage {\n    static_assert(\n        sizeof(in_addr) == sizeof(ByteArray4),\n        \"size of in_addr and ByteArray4 are different\");\n    in_addr inAddr_;\n    ByteArray4 bytes_;\n    AddressStorage() { std::memset(this, 0, sizeof(AddressStorage)); }\n    explicit AddressStorage(const ByteArray4 bytes) : bytes_(bytes) {}\n    explicit AddressStorage(const in_addr addr) : inAddr_(addr) {}\n  } addr_;\n\n  /**\n   * Set the current IPAddressV4 object to the address specified by the\n   * ByteRange given, in network byte order.\n   *\n   * Returns IPAddressFormatError if bytes.size() is not 4.\n   */\n  Expected<Unit, IPAddressFormatError> trySetFromBinary(\n      ByteRange bytes) noexcept;\n};\n\n/**\n * `boost::hash` uses hash_value() so this allows `boost::hash` to work\n * automatically for IPAddressV4\n */\nsize_t hash_value(const IPAddressV4& addr);\n\n/**\n * Appends a string representation of the IP address to the stream using str().\n */\nstd::ostream& operator<<(std::ostream& os, const IPAddressV4& addr);\n\n/**\n * @overloadbrief Define toAppend() to allow IPAddress to be used with\n * `folly::to<string>`\n */\nvoid toAppend(IPAddressV4 addr, std::string* result);\nvoid toAppend(IPAddressV4 addr, fbstring* result);\n\n/**\n * Return true if two addresses are equal.\n */\ninline bool operator==(const IPAddressV4& addr1, const IPAddressV4& addr2) {\n  return (addr1.toLong() == addr2.toLong());\n}\n\n/**\n * Return true if addr1 < addr2.\n */\ninline bool operator<(const IPAddressV4& addr1, const IPAddressV4& addr2) {\n  return (addr1.toLongHBO() < addr2.toLongHBO());\n}\n/**\n * Return true if addr1 != addr2.\n */\ninline bool operator!=(const IPAddressV4& addr1, const IPAddressV4& addr2) {\n  return !(addr1 == addr2);\n}\n/**\n * Return true if addr1 > addr2.\n */\ninline bool operator>(const IPAddressV4& addr1, const IPAddressV4& addr2) {\n  return addr2 < addr1;\n}\n/**\n * Return true if addr1 <= addr2.\n */\ninline bool operator<=(const IPAddressV4& addr1, const IPAddressV4& addr2) {\n  return !(addr1 > addr2);\n}\n/**\n * Return true if addr1 >= addr2.\n */\ninline bool operator>=(const IPAddressV4& addr1, const IPAddressV4& addr2) {\n  return !(addr1 < addr2);\n}\n\n} // namespace folly\n\nnamespace std {\ntemplate <>\nstruct hash<folly::IPAddressV4> {\n  size_t operator()(const folly::IPAddressV4 addr) const { return addr.hash(); }\n};\n} // namespace std\n"
  },
  {
    "path": "folly/IPAddressV6.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/IPAddressV6.h>\n\n#include <algorithm>\n#include <ostream>\n#include <string>\n\n#include <fmt/core.h>\n\n#include <folly/IPAddress.h>\n#include <folly/IPAddressV4.h>\n#include <folly/MacAddress.h>\n#include <folly/ScopeGuard.h>\n#include <folly/String.h>\n#include <folly/detail/IPAddressSource.h>\n\n#ifdef _WIN32\n// Because of the massive pain that is libnl, this can't go into the socket\n// portability header as you can't include <linux/if.h> and <net/if.h> in\n// the same translation unit without getting errors -_-...\n#include <iphlpapi.h> // @manual\n#include <ntddndis.h> // @manual\n\n// Alias the max size of an interface name to what posix expects.\n#define IFNAMSIZ IF_NAMESIZE\n#else\n#include <net/if.h>\n#endif\n\nusing std::ostream;\nusing std::string;\n\nnamespace folly {\n\n// public static const\nconst uint32_t IPAddressV6::PREFIX_TEREDO = 0x20010000;\nconst uint32_t IPAddressV6::PREFIX_6TO4 = 0x2002;\n\n// free functions\nsize_t hash_value(const IPAddressV6& addr) {\n  return addr.hash();\n}\nostream& operator<<(ostream& os, const IPAddressV6& addr) {\n  os << addr.str();\n  return os;\n}\nvoid toAppend(IPAddressV6 addr, string* result) {\n  result->append(addr.str());\n}\nvoid toAppend(IPAddressV6 addr, fbstring* result) {\n  result->append(addr.str());\n}\n\nbool IPAddressV6::validate(StringPiece ip) noexcept {\n  return tryFromString(ip).hasValue();\n}\n\n// public default constructor\nIPAddressV6::IPAddressV6() = default;\n\n// public string constructor\nIPAddressV6::IPAddressV6(StringPiece addr) {\n  auto maybeIp = tryFromString(addr);\n  if (maybeIp.hasError()) {\n    throw IPAddressFormatException(\n        to<std::string>(\"Invalid IPv6 address '\", addr, \"'\"));\n  }\n  *this = maybeIp.value();\n}\n\nExpected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromString(\n    StringPiece str) noexcept {\n  constexpr size_t kMaxSize = 45;\n\n  // Allow addresses surrounded in brackets\n  if (str.size() < 2) {\n    return makeUnexpected(IPAddressFormatError::INVALID_IP);\n  }\n\n  auto ip = str.front() == '[' && str.back() == ']'\n      ? str.subpiece(1, std::min(str.size() - 2, kMaxSize))\n      : str.subpiece(0, std::min(str.size(), kMaxSize));\n\n  std::array<char, kMaxSize + 1> ipBuffer;\n  std::copy(ip.begin(), ip.end(), ipBuffer.begin());\n  ipBuffer[ip.size()] = '\\0';\n\n  struct addrinfo* result;\n  struct addrinfo hints;\n  memset(&hints, 0, sizeof(hints));\n  hints.ai_family = AF_INET6;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_flags = AI_NUMERICHOST;\n  if (::getaddrinfo(ipBuffer.data(), nullptr, &hints, &result) == 0) {\n    SCOPE_EXIT {\n      ::freeaddrinfo(result);\n    };\n    const struct sockaddr_in6* sa =\n        reinterpret_cast<struct sockaddr_in6*>(result->ai_addr);\n    return IPAddressV6(*sa);\n  }\n  return makeUnexpected(IPAddressFormatError::INVALID_IP);\n}\n\n// in6_addr constructor\nIPAddressV6::IPAddressV6(const in6_addr& src) noexcept : addr_(src) {}\n\n// sockaddr_in6 constructor\nIPAddressV6::IPAddressV6(const sockaddr_in6& src) noexcept\n    : addr_(src.sin6_addr), scope_(uint16_t(src.sin6_scope_id)) {}\n\n// ByteArray16 constructor\nIPAddressV6::IPAddressV6(const ByteArray16& src) noexcept : addr_(src) {}\n\n// link-local constructor\nIPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac) : addr_(mac) {}\n\nIPAddressV6::AddressStorage::AddressStorage(MacAddress mac) {\n  // The link-local address uses modified EUI-64 format,\n  // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A\n  const auto* macBytes = mac.bytes();\n  memcpy(&bytes_.front(), \"\\xfe\\x80\\x00\\x00\\x00\\x00\\x00\\x00\", 8);\n  bytes_[8] = uint8_t(macBytes[0] ^ 0x02);\n  bytes_[9] = macBytes[1];\n  bytes_[10] = macBytes[2];\n  bytes_[11] = 0xff;\n  bytes_[12] = 0xfe;\n  bytes_[13] = macBytes[3];\n  bytes_[14] = macBytes[4];\n  bytes_[15] = macBytes[5];\n}\n\nOptional<MacAddress> IPAddressV6::getMacAddressFromLinkLocal() const {\n  // Returned MacAddress must be constructed from a link-local IPv6 address.\n  if (!isLinkLocal()) {\n    return folly::none;\n  }\n  return getMacAddressFromEUI64();\n}\n\nOptional<MacAddress> IPAddressV6::getMacAddressFromEUI64() const {\n  if (!(addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) {\n    return folly::none;\n  }\n  // The auto configured address uses modified EUI-64 format,\n  // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A\n  std::array<uint8_t, MacAddress::SIZE> bytes;\n  // Step 1: first 8 bytes are network prefix, and can be stripped\n  // Step 2: invert the universal/local (U/L) flag (bit 7)\n  bytes[0] = addr_.bytes_[8] ^ 0x02;\n  // Step 3: copy these bytes as they are\n  bytes[1] = addr_.bytes_[9];\n  bytes[2] = addr_.bytes_[10];\n  // Step 4: strip bytes (0xfffe), which are bytes_[11] and bytes_[12]\n  // Step 5: copy the rest.\n  bytes[3] = addr_.bytes_[13];\n  bytes[4] = addr_.bytes_[14];\n  bytes[5] = addr_.bytes_[15];\n  return Optional<MacAddress>(MacAddress::fromBinary(range(bytes)));\n}\n\nIPAddressV6 IPAddressV6::fromBinary(ByteRange bytes) {\n  auto maybeIp = tryFromBinary(bytes);\n  if (maybeIp.hasError()) {\n    throw IPAddressFormatException(\n        to<std::string>(\n            \"Invalid IPv6 binary data: length must be 16 bytes, got \",\n            bytes.size()));\n  }\n  return maybeIp.value();\n}\n\nExpected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromBinary(\n    ByteRange bytes) noexcept {\n  IPAddressV6 addr;\n  auto setResult = addr.trySetFromBinary(bytes);\n  if (setResult.hasError()) {\n    return makeUnexpected(setResult.error());\n  }\n  return addr;\n}\n\nExpected<Unit, IPAddressFormatError> IPAddressV6::trySetFromBinary(\n    ByteRange bytes) noexcept {\n  if (bytes.size() != 16) {\n    return makeUnexpected(IPAddressFormatError::INVALID_IP);\n  }\n  memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr));\n  scope_ = 0;\n  return unit;\n}\n\n// static\nIPAddressV6 IPAddressV6::fromInverseArpaName(const std::string& arpaname) {\n  auto piece = StringPiece(arpaname);\n  if (!piece.removeSuffix(\".ip6.arpa\")) {\n    throw IPAddressFormatException(\n        fmt::format(\n            \"Invalid input. Should end with 'ip6.arpa'. Got '{}'\", arpaname));\n  }\n  std::vector<StringPiece> pieces;\n  split('.', piece, pieces);\n  if (pieces.size() != 32) {\n    throw IPAddressFormatException(\n        fmt::format(\"Invalid input. Got '{}'\", piece));\n  }\n  std::array<char, IPAddressV6::kToFullyQualifiedSize> ip;\n  size_t pos = 0;\n  int count = 0;\n  for (size_t i = 1; i <= pieces.size(); i++) {\n    ip[pos] = pieces[pieces.size() - i][0];\n    pos++;\n    count++;\n    // add ':' every 4 chars\n    if (count == 4 && pos < ip.size()) {\n      ip[pos++] = ':';\n      count = 0;\n    }\n  }\n  return IPAddressV6(folly::range(ip));\n}\n\n// public\nIPAddressV4 IPAddressV6::createIPv4() const {\n  if (!isIPv4Mapped()) {\n    throw IPAddressFormatException(\"addr is not v4-to-v6-mapped\");\n  }\n  const unsigned char* by = bytes();\n  return IPAddressV4(detail::Bytes::mkAddress4(&by[12]));\n}\n\n// convert two uint8_t bytes into a uint16_t as hibyte.lobyte\nstatic inline uint16_t unpack(uint8_t lobyte, uint8_t hibyte) {\n  return uint16_t((uint16_t(hibyte) << 8) | lobyte);\n}\n\n// given a src string, unpack count*2 bytes into dest\n// dest must have as much storage as count\nstatic inline void unpackInto(\n    const unsigned char* src, uint16_t* dest, size_t count) {\n  for (size_t i = 0, hi = 1, lo = 0; i < count; i++) {\n    dest[i] = unpack(src[hi], src[lo]);\n    hi += 2;\n    lo += 2;\n  }\n}\n\n// public\nIPAddressV4 IPAddressV6::getIPv4For6To4() const {\n  if (!is6To4()) {\n    throw IPAddressV6::TypeError(\n        fmt::format(\"Invalid IP '{}': not a 6to4 address\", str()));\n  }\n  // convert 16x8 bytes into first 4x16 bytes\n  uint16_t ints[4] = {0, 0, 0, 0};\n  unpackInto(bytes(), ints, 4);\n  // repack into 4x8\n  union {\n    unsigned char bytes[4];\n    in_addr addr;\n  } ipv4;\n  ipv4.bytes[0] = (uint8_t)((ints[1] & 0xFF00) >> 8);\n  ipv4.bytes[1] = (uint8_t)(ints[1] & 0x00FF);\n  ipv4.bytes[2] = (uint8_t)((ints[2] & 0xFF00) >> 8);\n  ipv4.bytes[3] = (uint8_t)(ints[2] & 0x00FF);\n  return IPAddressV4(ipv4.addr);\n}\n\n// public\nbool IPAddressV6::isIPv4Mapped() const {\n  // v4 mapped addresses have their first 10 bytes set to 0, the next 2 bytes\n  // set to 255 (0xff);\n  const unsigned char* by = bytes();\n\n  // check if first 10 bytes are 0\n  for (int i = 0; i < 10; i++) {\n    if (by[i] != 0x00) {\n      return false;\n    }\n  }\n  // check if bytes 11 and 12 are 255\n  return by[10] == 0xff && by[11] == 0xff;\n}\n\n// public\nIPAddressV6::Type IPAddressV6::type() const {\n  // convert 16x8 bytes into first 2x16 bytes\n  uint16_t ints[2] = {0, 0};\n  unpackInto(bytes(), ints, 2);\n\n  if ((((uint32_t)ints[0] << 16) | ints[1]) == IPAddressV6::PREFIX_TEREDO) {\n    return Type::TEREDO;\n  }\n\n  if ((uint32_t)ints[0] == IPAddressV6::PREFIX_6TO4) {\n    return Type::T6TO4;\n  }\n\n  return Type::NORMAL;\n}\n\n// public\nstring IPAddressV6::toJson() const {\n  return fmt::format(\n      \"{{family:'AF_INET6', addr:'{}', hash:{}}}\", str(), hash());\n}\n\n// public\nsize_t IPAddressV6::hash() const {\n  if (isIPv4Mapped()) {\n    /* An IPAddress containing this object would be equal (i.e. operator==)\n       to an IPAddress containing the corresponding IPv4.\n       So we must make sure that the hash values are the same as well */\n    return IPAddress::createIPv4(*this).hash();\n  }\n\n  static const uint64_t seed = AF_INET6;\n  uint64_t hash1 = 0, hash2 = 0;\n  hash::SpookyHashV2::Hash128(&addr_, 16, &hash1, &hash2);\n  return hash::hash_combine(seed, hash1, hash2);\n}\n\n// public\nbool IPAddressV6::inSubnet(StringPiece cidrNetwork) const {\n  auto subnetInfo = IPAddress::createNetwork(cidrNetwork);\n  auto addr = subnetInfo.first;\n  if (!addr.isV6()) {\n    throw IPAddressFormatException(\n        fmt::format(\"Address '{}' is not a V6 address\", addr.toJson()));\n  }\n  return inSubnetWithMask(addr.asV6(), fetchMask(subnetInfo.second));\n}\n\n// public\nbool IPAddressV6::inSubnetWithMask(\n    const IPAddressV6& subnet, const ByteArray16& cidrMask) const {\n  const auto mask = detail::Bytes::mask(toByteArray(), cidrMask);\n  const auto subMask = detail::Bytes::mask(subnet.toByteArray(), cidrMask);\n  return (mask == subMask);\n}\n\n// public\nbool IPAddressV6::isLoopback() const {\n  // Check if v4 mapped is loopback\n  if (isIPv4Mapped() && createIPv4().isLoopback()) {\n    return true;\n  }\n  auto socka = toSockAddr();\n  return IN6_IS_ADDR_LOOPBACK(&socka.sin6_addr);\n}\n\nbool IPAddressV6::isRoutable() const {\n  return\n      // 2000::/3 is the only assigned global unicast block\n      inBinarySubnet({{0x20, 0x00}}, 3) ||\n      // ffxe::/16 are global scope multicast addresses,\n      // which are eligible to be routed over the internet\n      (isMulticast() && getMulticastScope() == 0xe);\n}\n\nbool IPAddressV6::isLinkLocalBroadcast() const {\n  static const IPAddressV6 kLinkLocalBroadcast(\"ff02::1\");\n  return *this == kLinkLocalBroadcast;\n}\n\n// public\nbool IPAddressV6::isPrivate() const {\n  // Check if mapped is private\n  if (isIPv4Mapped() && createIPv4().isPrivate()) {\n    return true;\n  }\n  return isLoopback() || inBinarySubnet({{0xfc, 0x00}}, 7) || isLinkLocal();\n}\n\n// public\nbool IPAddressV6::isLinkLocal() const {\n  return inBinarySubnet({{0xfe, 0x80}}, 10);\n}\n\nbool IPAddressV6::isMulticast() const {\n  return addr_.bytes_[0] == 0xff;\n}\n\nuint8_t IPAddressV6::getMulticastFlags() const {\n  DCHECK(isMulticast());\n  return uint8_t((addr_.bytes_[1] >> 4) & 0xf);\n}\n\nuint8_t IPAddressV6::getMulticastScope() const {\n  DCHECK(isMulticast());\n  return uint8_t(addr_.bytes_[1] & 0xf);\n}\n\nIPAddressV6 IPAddressV6::getSolicitedNodeAddress() const {\n  // Solicited node addresses must be constructed from unicast (or anycast)\n  // addresses\n  DCHECK(!isMulticast());\n\n  uint8_t bytes[16] = {\n      0xff,\n      0x02,\n      0x00,\n      0x00,\n      0x00,\n      0x00,\n      0x00,\n      0x00,\n      0x00,\n      0x00,\n      0x00,\n      0x01,\n      0xff,\n      addr_.bytes_[13],\n      addr_.bytes_[14],\n      addr_.bytes_[15],\n  };\n  return IPAddressV6::fromBinary(ByteRange(bytes, 16));\n}\n\n// public\nIPAddressV6 IPAddressV6::mask(size_t numBits) const {\n  static const auto bits = bitCount();\n  if (numBits > bits) {\n    throw IPAddressFormatException(\n        fmt::format(\"numBits({}) > bitCount({})\", numBits, bits));\n  }\n  ByteArray16 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_);\n  return IPAddressV6(ba);\n}\n\n// public\nstring IPAddressV6::str() const {\n  char buffer[INET6_ADDRSTRLEN + IFNAMSIZ + 1];\n\n  if (!inet_ntop(AF_INET6, toAddr().s6_addr, buffer, INET6_ADDRSTRLEN)) {\n    throw IPAddressFormatException(\n        fmt::format(\n            \"Invalid address with hex '{}' with error {}\",\n            detail::Bytes::toHex(bytes(), 16),\n            errnoStr(errno)));\n  }\n\n  auto scopeId = getScopeId();\n  if (scopeId != 0) {\n    auto len = strlen(buffer);\n    buffer[len] = '%';\n\n    auto errsv = errno;\n    if (!if_indextoname(scopeId, buffer + len + 1)) {\n      // if we can't map the if because eg. it no longer exists,\n      // append the if index instead\n      snprintf(buffer + len + 1, IFNAMSIZ, \"%u\", scopeId);\n    }\n    errno = errsv;\n  }\n\n  return string(buffer);\n}\n\n// public\nstring IPAddressV6::toFullyQualified() const {\n  return detail::fastIpv6ToString(addr_.in6Addr_);\n}\n\n// public\nvoid IPAddressV6::toFullyQualifiedAppend(std::string& out) const {\n  detail::fastIpv6AppendToString(addr_.in6Addr_, out);\n}\n\n// public\nstring IPAddressV6::toInverseArpaName() const {\n  constexpr folly::StringPiece lut = \"0123456789abcdef\";\n  std::array<char, 32> a;\n  int j = 0;\n  for (int i = 15; i >= 0; i--) {\n    a[j] = (lut[bytes()[i] & 0xf]);\n    a[j + 1] = (lut[bytes()[i] >> 4]);\n    j += 2;\n  }\n  return fmt::format(\"{}.ip6.arpa\", join(\".\", a));\n}\n\n// public\nuint8_t IPAddressV6::getNthMSByte(size_t byteIndex) const {\n  const auto highestIndex = byteCount() - 1;\n  if (byteIndex > highestIndex) {\n    throw std::invalid_argument(\n        fmt::format(\n            \"Byte index must be <= {} for addresses of type: {}\",\n            highestIndex,\n            detail::familyNameStr(AF_INET6)));\n  }\n  return bytes()[byteIndex];\n}\n\n// protected\nByteArray16 IPAddressV6::fetchMask(size_t numBits) {\n  static const size_t bits = bitCount();\n  if (numBits > bits) {\n    throw IPAddressFormatException(\"IPv6 addresses are 128 bits.\");\n  }\n  if (numBits == 0) {\n    return {{0}};\n  }\n  constexpr auto _0s = uint64_t(0);\n  constexpr auto _1s = ~_0s;\n  auto const fragment = Endian::big(_1s << ((128 - numBits) % 64));\n  auto const hi = numBits <= 64 ? fragment : _1s;\n  auto const lo = numBits <= 64 ? _0s : fragment;\n  uint64_t const parts[] = {hi, lo};\n  ByteArray16 arr;\n  std::memcpy(arr.data(), parts, sizeof(parts));\n  return arr;\n}\n\n// public static\nCIDRNetworkV6 IPAddressV6::longestCommonPrefix(\n    const CIDRNetworkV6& one, const CIDRNetworkV6& two) {\n  auto prefix = detail::Bytes::longestCommonPrefix(\n      one.first.addr_.bytes_, one.second, two.first.addr_.bytes_, two.second);\n  return {IPAddressV6(prefix.first), prefix.second};\n}\n\n// protected\nbool IPAddressV6::inBinarySubnet(\n    const std::array<uint8_t, 2> addr, size_t numBits) const {\n  auto masked = mask(numBits);\n  return (std::memcmp(addr.data(), masked.bytes(), 2) == 0);\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/IPAddressV6.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A representation of an IPv6 address\n *\n * @see IPAddress\n * @see IPAddressV4\n *\n * @class folly::IPAddressV6\n */\n\n#pragma once\n\n#include <cstring>\n\n#include <array>\n#include <functional>\n#include <iosfwd>\n#include <map>\n#include <stdexcept>\n\n#include <folly/Expected.h>\n#include <folly/FBString.h>\n#include <folly/IPAddressException.h>\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/detail/IPAddress.h>\n#include <folly/hash/Hash.h>\n\nnamespace folly {\n\nclass IPAddress;\nclass IPAddressV4;\nclass IPAddressV6;\nclass MacAddress;\n\n/**\n * Pair of IPAddressV6, netmask\n */\nusing CIDRNetworkV6 = std::pair<IPAddressV6, uint8_t>;\n\n/**\n * Specialization for `std::array` for IPv6 addresses\n */\nusing ByteArray16 = std::array<uint8_t, 16>;\n\nclass IPAddressV6 {\n public:\n  /**\n   * Represents the different types that IPv6 Addresses can be\n   *\n   */\n  enum Type {\n    TEREDO,\n    T6TO4,\n    NORMAL,\n  };\n\n  /**\n   * A constructor parameter to indicate that we should create a link-local\n   * IPAddressV6.\n   */\n  enum LinkLocalTag {\n    LINK_LOCAL,\n  };\n\n  /**\n   * Alias std::runtime_error, to be thrown when a type assertion fails\n   */\n  using TypeError = std::runtime_error;\n\n  /**\n   * The binary prefix for Teredo networks\n   */\n  static const uint32_t PREFIX_TEREDO;\n\n  /**\n   * The binary prefix for Teredo networks\n   */\n  static const uint32_t PREFIX_6TO4;\n\n  /**\n   * The size of the std::string returned by toFullyQualified.\n   */\n  static constexpr size_t kToFullyQualifiedSize =\n      8 /*words*/ * 4 /*hex chars per word*/ + 7 /*separators*/;\n\n  /**\n   * Return true if the input string can be parsed as an IPv6 addres\n   */\n  static bool validate(StringPiece ip) noexcept;\n\n  /**\n   * Create a new IPAddressV6 instance from the provided binary data, in network\n   * byte order.\n   *\n   * @throws IPAddressFormatException if the input length is not 16 bytes.\n   */\n  static IPAddressV6 fromBinary(ByteRange bytes);\n\n  /**\n   * Create a new IPAddressV6 from the provided ByteRange.\n   *\n   * Returns an IPAddressFormatError if the input length is not 4 bytes.\n   */\n  static Expected<IPAddressV6, IPAddressFormatError> tryFromBinary(\n      ByteRange bytes) noexcept;\n\n  /**\n   * Create a new IPAddressV6 from the provided string.\n   *\n   * Returns an IPAddressFormatError if the string is not a valid IP.\n   */\n  static Expected<IPAddressV6, IPAddressFormatError> tryFromString(\n      StringPiece str) noexcept;\n\n  /**\n   * Create a new IPAddress instance from the ip6.arpa representation.\n   * @throws IPAddressFormatException if the input is not a valid ip6.arpa\n   * representation\n   */\n  static IPAddressV6 fromInverseArpaName(const std::string& arpaname);\n\n  /**\n   * Returns the address as a ByteRange.\n   */\n  ByteRange toBinary() const {\n    return ByteRange((const unsigned char*)&addr_.in6Addr_.s6_addr, 16);\n  }\n\n  /**\n   * Default constructor for IPAddressV6.\n   *\n   * The address value will be ::0\n   */\n  IPAddressV6();\n\n  /**\n   * Construct an IPAddressV6 from a string\n   *\n   * @throws IPAddressFormatException if the string is not a valid IPv6 address.\n   */\n  explicit IPAddressV6(StringPiece addr);\n\n  /**\n   * Construct an IPAddressV6 from a ByteArray16\n   */\n  explicit IPAddressV6(const ByteArray16& src) noexcept;\n\n  /**\n   * Construct an IPAddressV6 from an `in_addr` representation of an IPV6\n   * address\n   */\n  explicit IPAddressV6(const in6_addr& src) noexcept;\n\n  /**\n   * Construct an IPAddressV6 from an `sockaddr_in6` representation of an IPV6\n   * address\n   */\n  explicit IPAddressV6(const sockaddr_in6& src) noexcept;\n\n  /**\n   * Create a link-local IPAddressV6 from the specified ethernet MAC address.\n   */\n  IPAddressV6(LinkLocalTag tag, MacAddress mac);\n\n  /**\n   * Return the mapped IPAddressV4\n   *\n   * @throws IPAddressFormatException if the address is not IPv4 mapped\n   */\n  IPAddressV4 createIPv4() const;\n\n  /**\n   * Return a V4 address if this is a 6To4 address.\n   * @throws TypeError if not a 6To4 address\n   */\n  IPAddressV4 getIPv4For6To4() const;\n\n  /**\n   * Return true if the address is a 6to4 address\n   */\n  bool is6To4() const { return type() == IPAddressV6::Type::T6TO4; }\n\n  /**\n   * Return true if the address is a Teredo address\n   */\n  bool isTeredo() const { return type() == IPAddressV6::Type::TEREDO; }\n\n  /**\n   * Return true if the adddress is IPv4 mapped\n   */\n  bool isIPv4Mapped() const;\n\n  /**\n   * Return what type of IPv6 address this is.\n   *\n   * @see Type\n   */\n  Type type() const;\n\n  /**\n   * Return the number of bits in the IP address representation\n   *\n   * @returns 128\n   */\n  static constexpr size_t bitCount() { return 128; }\n\n  /**\n   * Get a json representation of the IP address.\n   *\n   * This prints a string representation of the address, for human consumption\n   * or logging. The string will take the form of a JSON object that looks like:\n   *  `{family:'AF_INET6', addr:'address', hash:long}`.\n   */\n  std::string toJson() const;\n\n  /**\n   * Returns a hash of the IP address.\n   */\n  size_t hash() const;\n\n  /**\n   * @overloadbrief Check if the address is found in the specified CIDR\n   * netblock.\n   *\n   * This will return false if the specified cidrNet is V4, but the address is\n   * V6. It will also return false if the specified cidrNet is V6 but the\n   * address is V4. This method will do the right thing in the case of a v6\n   * mapped v4 address.\n   *\n   * @note This is slower than the below counterparts. If perf is important use\n   *       one of the two argument variations below.\n   * @param [in] cidrNetwork address in \"192.168.1.0/24\" format\n   * @throws IPAddressFormatException if no /mask in cidrNetwork\n   * @return true if address is part of specified subnet with cidr\n   */\n  bool inSubnet(StringPiece cidrNetwork) const;\n\n  /**\n   * Check if an IPAddress belongs to a subnet.\n   *\n   * @param [in] subnet Subnet to check against (e.g. 192.168.1.0)\n   * @param [in] cidr   CIDR for subnet (e.g. 24 for /24)\n   * @return true if address is part of specified subnet with cidr\n   */\n  bool inSubnet(const IPAddressV6& subnet, uint8_t cidr) const {\n    return inSubnetWithMask(subnet, fetchMask(cidr));\n  }\n\n  /**\n   * Check if an IPAddress belongs to the subnet with the given mask.\n   *\n   * This is the same as inSubnet but the mask is provided instead of looked up\n   * from the cidr.\n   * @param [in] subnet Subnet to check against\n   * @param [in] mask   The netmask for the subnet\n   * @return true if address is part of the specified subnet with mask\n   */\n  bool inSubnetWithMask(\n      const IPAddressV6& subnet, const ByteArray16& mask) const;\n\n  /**\n   * Return true if the IP address qualifies as localhost.\n   */\n  bool isLoopback() const;\n\n  /**\n   * Return true if the IP address is a special purpose address, as defined per\n   * RFC 6890.\n   *\n   */\n  bool isNonroutable() const { return !isRoutable(); }\n\n  /**\n   * Return true if this address is routable.\n   */\n  bool isRoutable() const;\n\n  /**\n   * Return true if the IP address is private, as per RFC 1918 and RFC 4193.\n   *\n   * For example, 192.168.xxx.xxx or fc00::/7 addresses.\n   */\n  bool isPrivate() const;\n\n  /**\n   * Return true if this is a link-local IPv6 address.\n   *\n   * Note that this only returns true for addresses in the fe80::/10 range.\n   * It returns false for the loopback address (::1), even though this address\n   * is also effectively has link-local scope.  It also returns false for\n   * link-scope and interface-scope multicast addresses.\n   */\n  bool isLinkLocal() const;\n\n  /**\n   * Return the mac address if this is a link-local IPv6 address.\n   *\n   * @return a `folly::Optional<MacAddress>` union representing the mac address.\n   *\n   * If the address is not a link-local one it will return an empty Optional.\n   * You can use Optional::value() to check whether the mac address is not null.\n   */\n  Optional<MacAddress> getMacAddressFromLinkLocal() const;\n\n  /**\n   * Return the mac address if this is an auto-configured IPv6 address based on\n   * EUI-64\n   *\n   * If the address is not based on EUI-64 it will return an empty\n   * Optional. You can use Optional::value() to check whether the mac address is\n   * not null.\n   *\n   * @return a `folly::Optional<MacAddress>` union representing the mac address.\n   *\n   */\n  Optional<MacAddress> getMacAddressFromEUI64() const;\n\n  /**\n   * Return true if this is a multicast address.\n   */\n  bool isMulticast() const;\n\n  /**\n   * Return the flags for a multicast address.\n   *\n   * This method may only be called on multicast addresses.\n   */\n  uint8_t getMulticastFlags() const;\n\n  /**\n   * Return the scope for a multicast address.\n   *\n   * This method may only be called on multicast addresses.\n   */\n  uint8_t getMulticastScope() const;\n\n  /**\n   * Return true if the address is 0\n   */\n  bool isZero() const {\n    constexpr auto zero = ByteArray16{{}};\n    return 0 == std::memcmp(bytes(), zero.data(), zero.size());\n  }\n\n  /**\n   * Return true if the IP address qualifies as broadcast.\n   */\n  bool isLinkLocalBroadcast() const;\n\n  /**\n   * Creates an IPAddressV6 instance with all but most significant numBits set\n   * to 0.\n   *\n   * @throws IPAddressFormatException if `numBits > bitCount()`\n   *\n   * @param [in] numBits number of bits to mask\n   * @return IPAddress instance with bits set to 0\n   */\n  IPAddressV6 mask(size_t numBits) const;\n\n  /**\n   * Return the underlying `in6_addr` structure\n   */\n  in6_addr toAddr() const { return addr_.in6Addr_; }\n\n  /**\n   * Return the link-local scope id.\n   *\n   * This should always be 0 for IP addresses that are *not* link-local.\n   *\n   */\n  uint16_t getScopeId() const { return scope_; }\n  /**\n   * Set the link-local scope id.\n   *\n   * This should always be 0 for IP addresses that are *not* link-local.\n   *\n   */\n  void setScopeId(uint16_t scope) { scope_ = scope; }\n\n  /**\n   * Return the IP address represented as a `sockaddr_in6` struct\n   *\n   */\n  sockaddr_in6 toSockAddr() const {\n    sockaddr_in6 addr;\n    memset(&addr, 0, sizeof(sockaddr_in6));\n    addr.sin6_family = AF_INET6;\n    addr.sin6_scope_id = scope_;\n    memcpy(&addr.sin6_addr, &addr_.in6Addr_, sizeof(in6_addr));\n    return addr;\n  }\n\n  /**\n   * Return a ByteArray16 containing the bytes of the IP address.\n   */\n  ByteArray16 toByteArray() const {\n    ByteArray16 ba{{0}};\n    std::memcpy(ba.data(), bytes(), 16);\n    return ba;\n  }\n\n  /**\n   * Return the fully qualified string representation of the address.\n   *\n   * This is the hex representation with : characters inserted every 4 digits.\n   */\n  std::string toFullyQualified() const;\n\n  /**\n   * Same as toFullyQualified() but append to an output string.\n   */\n  void toFullyQualifiedAppend(std::string& out) const;\n\n  /**\n   * Create the inverse arpa representation of the IP address.\n   *\n   */\n  std::string toInverseArpaName() const;\n\n  /**\n   * Provides a string representation of address.\n   *\n   * Throws an IPAddressFormatException on `inet_ntop` error.\n   *\n   * The string representation is calculated on demand.\n   */\n  std::string str() const;\n\n  /**\n   * Returns the version of the IP Address.\n   *\n   * @returns 6\n   */\n  uint8_t version() const { return 6; }\n\n  /**\n   * Return the solicited-node multicast address for this address.\n   */\n  IPAddressV6 getSolicitedNodeAddress() const;\n\n  /**\n   * Return the mask associated with the given number of bits.\n   *\n   * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should\n   * be {0xff, 0xff, 0xff, 0x00}.\n   * @param [in] numBits bitmask to retrieve\n   * @throws abort if numBits == 0 or numBits > bitCount()\n   * @return mask associated with numBits\n   */\n\n  static ByteArray16 fetchMask(size_t numBits);\n\n  /**\n   * Given 2 (IPAddressV6, mask) pairs extract the longest common (IPAddressV6,\n   * mask) pair\n   */\n  static CIDRNetworkV6 longestCommonPrefix(\n      const CIDRNetworkV6& one, const CIDRNetworkV6& two);\n\n  /**\n   * The number of bytes in the IP address\n   *\n   * @returns 16\n   */\n  static constexpr size_t byteCount() { return 16; }\n\n  /**\n   * Get the nth most significant bit of the IP address (0-indexed).\n   * @param bitIndex n\n   */\n  bool getNthMSBit(size_t bitIndex) const {\n    return detail::getNthMSBitImpl(*this, bitIndex, AF_INET6);\n  }\n\n  /**\n   * Get the nth most significant byte of the IP address (0-indexed).\n   * @param byteIndex n\n   */\n  uint8_t getNthMSByte(size_t byteIndex) const;\n\n  /**\n   * Get the nth bit of the IP address (0-indexed).\n   * @param bitIndex n\n   */\n  bool getNthLSBit(size_t bitIndex) const {\n    return getNthMSBit(bitCount() - bitIndex - 1);\n  }\n\n  /**\n   * Get the nth byte of the IP address (0-indexed).\n   * @param byteIndex n\n   */\n  uint8_t getNthLSByte(size_t byteIndex) const {\n    return getNthMSByte(byteCount() - byteIndex - 1);\n  }\n\n  /**\n   * Returns a pointer to the to IP address bytes, in network byte order.\n   */\n  const unsigned char* bytes() const { return addr_.in6Addr_.s6_addr; }\n\n protected:\n  /**\n   * Helper that returns true if the address is in the binary subnet specified\n   * by addr.\n   */\n  bool inBinarySubnet(const std::array<uint8_t, 2> addr, size_t numBits) const;\n\n private:\n  auto tie() const { return std::tie(addr_.bytes_, scope_); }\n\n public:\n  /**\n   * Return true if the two addresses are equal.\n   */\n  friend inline bool operator==(\n      const IPAddressV6& addr1, const IPAddressV6& addr2) {\n    return addr1.tie() == addr2.tie();\n  }\n  /**\n   * Return true if the two addresses are not equal.\n   */\n  friend inline bool operator!=(\n      const IPAddressV6& addr1, const IPAddressV6& addr2) {\n    return addr1.tie() != addr2.tie();\n  }\n\n  /**\n   * Return true if addr1 < addr2.\n   */\n  friend inline bool operator<(\n      const IPAddressV6& addr1, const IPAddressV6& addr2) {\n    return addr1.tie() < addr2.tie();\n  }\n\n  /**\n   * Return true if addr1 > addr2.\n   */\n  friend inline bool operator>(\n      const IPAddressV6& addr1, const IPAddressV6& addr2) {\n    return addr1.tie() > addr2.tie();\n  }\n\n  /**\n   * Return true if addr1 <= addr2.\n   */\n  friend inline bool operator<=(\n      const IPAddressV6& addr1, const IPAddressV6& addr2) {\n    return addr1.tie() <= addr2.tie();\n  }\n\n  /**\n   * Return true if addr1 >= addr2.\n   */\n  friend inline bool operator>=(\n      const IPAddressV6& addr1, const IPAddressV6& addr2) {\n    return addr1.tie() >= addr2.tie();\n  }\n\n private:\n  union AddressStorage {\n    in6_addr in6Addr_;\n    ByteArray16 bytes_;\n    AddressStorage() { std::memset(this, 0, sizeof(AddressStorage)); }\n    explicit AddressStorage(const ByteArray16& bytes) : bytes_(bytes) {}\n    explicit AddressStorage(const in6_addr& addr) : in6Addr_(addr) {}\n    explicit AddressStorage(MacAddress mac);\n  } addr_;\n\n  // Link-local scope id.  This should always be 0 for IPAddresses that\n  // are *not* link-local.\n  uint16_t scope_{0};\n\n  /**\n   * Set the current IPAddressV6 object to have the address specified by bytes.\n   * Returns IPAddressFormatError if bytes.size() is not 16.\n   */\n  Expected<Unit, IPAddressFormatError> trySetFromBinary(\n      ByteRange bytes) noexcept;\n};\n\n/**\n * `boost::hash` uses hash_value(), so this allows `boost::hash` to work\n * automatically for IPAddressV4\n */\nstd::size_t hash_value(const IPAddressV6& addr);\n\n/**\n * Appends a string representation of the IP address to the stream using str().\n */\n\nstd::ostream& operator<<(std::ostream& os, const IPAddressV6& addr);\n\n/**\n * @overloadbrief Define toAppend() to allow IPAddress to be used with\n * `folly::to<string>`\n */\nvoid toAppend(IPAddressV6 addr, std::string* result);\nvoid toAppend(IPAddressV6 addr, fbstring* result);\n\n} // namespace folly\n\nnamespace std {\ntemplate <>\nstruct hash<folly::IPAddressV6> {\n  size_t operator()(const folly::IPAddressV6& addr) const {\n    return addr.hash();\n  }\n};\n} // namespace std\n"
  },
  {
    "path": "folly/Indestructible.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n\nnamespace folly {\n\n/***\n *  Indestructible\n *\n *  When you need a Meyers singleton that will not get destructed, even at\n *  shutdown, and you also want the object stored inline.\n *\n *  Use like:\n *\n *      void doSomethingWithExpensiveData();\n *\n *      void doSomethingWithExpensiveData() {\n *        static const Indestructible<map<string, int>> data{\n *          map<string, int>{{\"key1\", 17}, {\"key2\", 19}, {\"key3\", 23}},\n *        };\n *        callSomethingTakingAMapByRef(*data);\n *      }\n *\n *  This should be used only for Meyers singletons, and, even then, only when\n *  the instance does not need to be destructed ever.\n *\n *  This should not be used more generally, e.g., as member fields, etc.\n *\n *  This is designed as an alternative, but with one fewer allocation at\n *  construction time and one fewer pointer dereference at access time, to the\n *  Meyers singleton pattern of:\n *\n *    void doSomethingWithExpensiveData() {\n *      static const auto data =  // never `delete`d\n *          new map<string, int>{{\"key1\", 17}, {\"key2\", 19}, {\"key3\", 23}};\n *      callSomethingTakingAMapByRef(*data);\n *    }\n */\n\nstruct factory_constructor_t {\n  explicit factory_constructor_t() = default;\n};\n\nconstexpr factory_constructor_t factory_constructor{};\n\ntemplate <typename T>\nclass Indestructible final {\n public:\n  template <typename S = T, typename = decltype(S())>\n  constexpr Indestructible() noexcept(noexcept(T()))\n      : storage_{std::in_place} {}\n\n  /**\n   * Constructor accepting a single argument by forwarding reference, this\n   * allows using list initialization without the overhead of things like\n   * std::in_place, etc and also works with std::initializer_list constructors\n   * which can't be deduced, the default parameter helps there.\n   *\n   *    auto i = folly::Indestructible<std::map<int, int>>{{{1, 2}}};\n   *\n   * This provides convenience\n   *\n   * There are two versions of this constructor - one for when the element is\n   * implicitly constructible from the given argument and one for when the\n   * type is explicitly but not implicitly constructible from the given\n   * argument.\n   */\n  template <\n      typename U = T,\n      std::enable_if_t<std::is_constructible<T, U&&>::value>* = nullptr,\n      std::enable_if_t<\n          !std::is_same<Indestructible<T>, remove_cvref_t<U>>::value>* =\n          nullptr,\n      std::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr>\n  explicit constexpr Indestructible(U&& u) noexcept(\n      noexcept(T(std::declval<U>())))\n      : storage_{std::in_place, std::forward<U>(u)} {}\n  template <\n      typename U = T,\n      std::enable_if_t<std::is_constructible<T, U&&>::value>* = nullptr,\n      std::enable_if_t<\n          !std::is_same<Indestructible<T>, remove_cvref_t<U>>::value>* =\n          nullptr,\n      std::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr>\n  /* implicit */ constexpr Indestructible(U&& u) noexcept(\n      noexcept(T(std::declval<U>())))\n      : storage_{std::in_place, std::forward<U>(u)} {}\n\n  template <typename... Args, typename = decltype(T(std::declval<Args>()...))>\n  explicit constexpr Indestructible(Args&&... args) noexcept(\n      noexcept(T(std::declval<Args>()...)))\n      : storage_{std::in_place, std::forward<Args>(args)...} {}\n  template <\n      typename U,\n      typename... Args,\n      typename = decltype(T(\n          std::declval<std::initializer_list<U>&>(), std::declval<Args>()...))>\n  explicit constexpr Indestructible(std::initializer_list<U> il, Args... args) noexcept(\n      noexcept(T(\n          std::declval<std::initializer_list<U>&>(), std::declval<Args>()...)))\n      : storage_{std::in_place, il, std::forward<Args>(args)...} {}\n\n  template <typename Factory>\n  constexpr Indestructible(factory_constructor_t, Factory&& factory) noexcept(\n      noexcept(factory()))\n      : storage_(factory_constructor, std::forward<Factory>(factory)) {}\n\n  Indestructible(Indestructible const&) = delete;\n  Indestructible& operator=(Indestructible const&) = delete;\n\n  T* get() noexcept { return reinterpret_cast<T*>(&storage_.bytes); }\n  T const* get() const noexcept {\n    return reinterpret_cast<T const*>(&storage_.bytes);\n  }\n  T& operator*() noexcept { return *get(); }\n  T const& operator*() const noexcept { return *get(); }\n  T* operator->() noexcept { return get(); }\n  T const* operator->() const noexcept { return get(); }\n\n  /* implicit */ operator T&() noexcept { return *get(); }\n  /* implicit */ operator T const&() const noexcept { return *get(); }\n\n private:\n  struct Storage {\n    aligned_storage_for_t<T> bytes;\n\n    template <typename... Args, typename = decltype(T(std::declval<Args>()...))>\n    explicit constexpr Storage(std::in_place_t, Args&&... args) noexcept(\n        noexcept(T(std::declval<Args>()...))) {\n      ::new (&bytes) T(std::forward<Args>(args)...);\n    }\n\n    template <typename Factory>\n    constexpr Storage(factory_constructor_t, Factory factory) noexcept(\n        noexcept(factory())) {\n      ::new (&bytes) T(factory());\n    }\n  };\n\n  Storage storage_{};\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/IndexedMemPool.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <assert.h>\n#include <errno.h>\n#include <stdint.h>\n\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/AtomicStruct.h>\n\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n\nnamespace folly {\n\nnamespace detail {\ntemplate <typename Pool>\nstruct IndexedMemPoolRecycler;\n} // namespace detail\n\ntemplate <\n    typename T,\n    bool EagerRecycleWhenTrivial = false,\n    bool EagerRecycleWhenNotTrivial = true>\nstruct IndexedMemPoolTraits {\n  static constexpr bool eagerRecycle() {\n    return std::is_trivial<T>::value\n        ? EagerRecycleWhenTrivial\n        : EagerRecycleWhenNotTrivial;\n  }\n\n  /// Called when the element pointed to by ptr is allocated for the\n  /// first time.\n  static void initialize(T* ptr) {\n    if constexpr (!eagerRecycle()) {\n      new (ptr) T();\n    }\n  }\n\n  /// Called when the element pointed to by ptr is freed at the pool\n  /// destruction time.\n  static void cleanup(T* ptr) {\n    if constexpr (!eagerRecycle()) {\n      ptr->~T();\n    }\n  }\n\n  /// Called when the element is allocated with the arguments forwarded from\n  /// IndexedMemPool::allocElem.\n  template <typename... Args>\n  static void onAllocate(T* ptr, Args&&... args) {\n    static_assert(\n        sizeof...(Args) == 0 || eagerRecycle(),\n        \"emplace-style allocation requires eager recycle, \"\n        \"which is defaulted only for non-trivial types\");\n    if (eagerRecycle()) {\n      new (ptr) T(std::forward<Args>(args)...);\n    }\n  }\n\n  /// Called when the element is recycled.\n  static void onRecycle(T* ptr) {\n    if (eagerRecycle()) {\n      ptr->~T();\n    }\n  }\n};\n\n/// IndexedMemPool traits that implements the lazy lifecycle strategy. In this\n/// strategy elements are default-constructed the first time they are allocated,\n/// and destroyed when the pool itself is destroyed.\ntemplate <typename T>\nusing IndexedMemPoolTraitsLazyRecycle = IndexedMemPoolTraits<T, false, false>;\n\n/// IndexedMemPool traits that implements the eager lifecycle strategy. In this\n/// strategy elements are constructed when they are allocated from the pool and\n/// destroyed when recycled.\ntemplate <typename T>\nusing IndexedMemPoolTraitsEagerRecycle = IndexedMemPoolTraits<T, true, true>;\n\n/// Instances of IndexedMemPool dynamically allocate and then pool their\n/// element type (T), returning 4-byte integer indices that can be passed\n/// to the pool's operator[] method to access or obtain pointers to the\n/// actual elements.  The memory backing items returned from the pool\n/// will always be readable, even if items have been returned to the pool.\n/// These two features are useful for lock-free algorithms.  The indexing\n/// behavior makes it easy to build tagged pointer-like-things, since\n/// a large number of elements can be managed using fewer bits than a\n/// full pointer.  The access-after-free behavior makes it safe to read\n/// from T-s even after they have been recycled, since it is guaranteed\n/// that the memory won't have been returned to the OS and unmapped\n/// (the algorithm must still use a mechanism to validate that the read\n/// was correct, but it doesn't have to worry about page faults), and if\n/// the elements use internal sequence numbers it can be guaranteed that\n/// there won't be an ABA match due to the element being overwritten with\n/// a different type that has the same bit pattern.\n///\n/// The object lifecycle strategy is controlled by the Traits parameter.\n/// One strategy, implemented by IndexedMemPoolTraitsEagerRecycle, is to\n/// construct objects when they are allocated from the pool and destroy\n/// them when they are recycled.  In this mode allocIndex and allocElem\n/// have emplace-like semantics.  In another strategy, implemented by\n/// IndexedMemPoolTraitsLazyRecycle, objects are default-constructed the\n/// first time they are removed from the pool, and deleted when the pool\n/// itself is deleted.  By default the first mode is used for non-trivial\n/// T, and the second is used for trivial T.  Clients can customize the\n/// object lifecycle by providing their own Traits implementation.\n/// See IndexedMemPoolTraits for a Traits example.\n///\n/// IMPORTANT: Space for extra elements is allocated to account for those\n/// that are inaccessible because they are in other local lists, so the\n/// actual number of items that can be allocated ranges from capacity to\n/// capacity + (NumLocalLists_-1)*LocalListLimit_.  This is important if\n/// you are trying to maximize the capacity of the pool while constraining\n/// the bit size of the resulting pointers, because the pointers will\n/// actually range up to the boosted capacity.  See maxIndexForCapacity\n/// and capacityForMaxIndex.\n///\n/// To avoid contention, NumLocalLists_ free lists of limited (less than\n/// or equal to LocalListLimit_) size are maintained, and each thread\n/// retrieves and returns entries from its associated local list.  If the\n/// local list becomes too large then elements are placed in bulk in a\n/// global free list.  This allows items to be efficiently recirculated\n/// from consumers to producers.  AccessSpreader is used to access the\n/// local lists, so there is no performance advantage to having more\n/// local lists than L1 caches.\n///\n/// The pool mmap-s the entire necessary address space when the pool is\n/// constructed, but delays element construction.  This means that only\n/// elements that are actually returned to the caller get paged into the\n/// process's resident set (RSS).\ntemplate <\n    typename T,\n    uint32_t NumLocalLists_ = 32,\n    uint32_t LocalListLimit_ = 200,\n    template <typename> class Atom = std::atomic,\n    typename Traits = IndexedMemPoolTraits<T>>\nstruct IndexedMemPool {\n  using value_type = T;\n\n  using UniquePtr =\n      std::unique_ptr<T, detail::IndexedMemPoolRecycler<IndexedMemPool>>;\n\n  IndexedMemPool(const IndexedMemPool&) = delete;\n  IndexedMemPool& operator=(const IndexedMemPool&) = delete;\n\n  static_assert(LocalListLimit_ <= 255, \"LocalListLimit must fit in 8 bits\");\n  enum {\n    NumLocalLists = NumLocalLists_,\n    LocalListLimit = LocalListLimit_,\n  };\n\n  static_assert(\n      std::is_nothrow_default_constructible<Atom<uint32_t>>::value,\n      \"Atom must be nothrow default constructible\");\n\n  // these are public because clients may need to reason about the number\n  // of bits required to hold indices from a pool, given its capacity\n\n  static constexpr uint32_t maxIndexForCapacity(uint32_t capacity) {\n    // index of std::numeric_limits<uint32_t>::max() is reserved for isAllocated\n    // tracking\n    return uint32_t(\n        std::min(\n            uint64_t(capacity) + (NumLocalLists - 1) * LocalListLimit,\n            uint64_t(std::numeric_limits<uint32_t>::max() - 1)));\n  }\n\n  static constexpr uint32_t capacityForMaxIndex(uint32_t maxIndex) {\n    return maxIndex - (NumLocalLists - 1) * LocalListLimit;\n  }\n\n  /// Constructs a pool that can allocate at least _capacity_ elements,\n  /// even if all the local lists are full\n  explicit IndexedMemPool(uint32_t capacity)\n      : actualCapacity_(maxIndexForCapacity(capacity)),\n        size_(0),\n        globalHead_(TaggedPtr{}) {\n    const size_t needed = sizeof(Slot) * (actualCapacity_ + 1);\n    size_t pagesize = size_t(sysconf(_SC_PAGESIZE));\n    mmapLength_ = ((needed - 1) & ~(pagesize - 1)) + pagesize;\n    assert(needed <= mmapLength_ && mmapLength_ < needed + pagesize);\n    assert((mmapLength_ % pagesize) == 0);\n\n    slots_ = static_cast<Slot*>(mmap(\n        nullptr,\n        mmapLength_,\n        PROT_READ | PROT_WRITE,\n        MAP_PRIVATE | MAP_ANONYMOUS,\n        -1,\n        0));\n    if (slots_ == MAP_FAILED) {\n      assert(errno == ENOMEM);\n      throw std::bad_alloc();\n    }\n  }\n\n  /// Destroys all of the contained elements\n  ~IndexedMemPool() {\n    using A = Atom<uint32_t>;\n    for (uint32_t i = maxAllocatedIndex(); i > 0; --i) {\n      Traits::cleanup(slots_[i].elemPtr());\n      slots_[i].localNext.~A();\n      slots_[i].globalNext.~A();\n    }\n    munmap(slots_, mmapLength_);\n  }\n\n  /// Returns a lower bound on the number of elements that may be\n  /// simultaneously allocated and not yet recycled.  Because of the\n  /// local lists it is possible that more elements than this are returned\n  /// successfully\n  uint32_t capacity() { return capacityForMaxIndex(actualCapacity_); }\n\n  /// Returns the maximum index of elements ever allocated in this pool\n  /// including elements that have been recycled.\n  uint32_t maxAllocatedIndex() const {\n    // Take the minimum since it is possible that size_ > actualCapacity_.\n    // This can happen if there are multiple concurrent requests\n    // when size_ == actualCapacity_ - 1.\n    return std::min(uint32_t(size_), uint32_t(actualCapacity_));\n  }\n\n  /// Finds a slot with a non-zero index, emplaces a T there if we're\n  /// using the eager recycle lifecycle mode, and returns the index,\n  /// or returns 0 if no elements are available.  Passes a pointer to\n  /// the element to Traits::onAllocate before the slot is marked as\n  /// allocated.\n  template <typename... Args>\n  uint32_t allocIndex(Args&&... args) {\n    auto idx = localPop(localHead());\n    if (idx != 0) {\n      Slot& s = slot(idx);\n      Traits::onAllocate(s.elemPtr(), std::forward<Args>(args)...);\n      markAllocated(s);\n    }\n    return idx;\n  }\n\n  /// If an element is available, returns a std::unique_ptr to it that will\n  /// recycle the element to the pool when it is reclaimed, otherwise returns\n  /// a null (falsy) std::unique_ptr.  Passes a pointer to the element to\n  /// Traits::onAllocate before the slot is marked as allocated.\n  template <typename... Args>\n  UniquePtr allocElem(Args&&... args) {\n    auto idx = allocIndex(std::forward<Args>(args)...);\n    T* ptr = idx == 0 ? nullptr : slot(idx).elemPtr();\n    return UniquePtr(ptr, typename UniquePtr::deleter_type(this));\n  }\n\n  /// Gives up ownership previously granted by alloc()\n  void recycleIndex(uint32_t idx) {\n    assert(isAllocated(idx));\n    localPush(localHead(), idx);\n  }\n\n  /// Provides access to the pooled element referenced by idx\n  T& operator[](uint32_t idx) { return *(slot(idx).elemPtr()); }\n\n  /// Provides access to the pooled element referenced by idx\n  const T& operator[](uint32_t idx) const { return *(slot(idx).elemPtr()); }\n\n  /// If elem == &pool[idx], then pool.locateElem(elem) == idx.  Also,\n  /// pool.locateElem(nullptr) == 0\n  uint32_t locateElem(const T* elem) const {\n    if (!elem) {\n      return 0;\n    }\n\n    static_assert(std::is_standard_layout<Slot>::value, \"offsetof needs POD\");\n\n    auto slot = reinterpret_cast<const Slot*>(\n        reinterpret_cast<const char*>(elem) - offsetof(Slot, elemStorage));\n    auto rv = uint32_t(slot - slots_);\n\n    // this assert also tests that rv is in range\n    assert(elem == &(*this)[rv]);\n    return rv;\n  }\n\n  /// Returns true iff idx has been alloc()ed and not recycleIndex()ed\n  bool isAllocated(uint32_t idx) const {\n    return slot(idx).localNext.load(std::memory_order_acquire) == uint32_t(-1);\n  }\n\n private:\n  ///////////// types\n\n  struct Slot {\n    aligned_storage_for_t<T> elemStorage;\n    Atom<uint32_t> localNext;\n    Atom<uint32_t> globalNext;\n\n    Slot() : localNext{}, globalNext{} {}\n    T* elemPtr() { return std::launder(reinterpret_cast<T*>(&elemStorage)); }\n    const T* elemPtr() const {\n      return std::launder(reinterpret_cast<const T*>(&elemStorage));\n    }\n  };\n\n  struct TaggedPtr {\n    uint32_t idx;\n\n    // size is bottom 8 bits, tag in top 24.  g++'s code generation for\n    // bitfields seems to depend on the phase of the moon, plus we can\n    // do better because we can rely on other checks to avoid masking\n    uint32_t tagAndSize;\n\n    enum : uint32_t {\n      SizeBits = 8,\n      SizeMask = (1U << SizeBits) - 1,\n      TagIncr = 1U << SizeBits,\n    };\n\n    uint32_t size() const { return tagAndSize & SizeMask; }\n\n    TaggedPtr withSize(uint32_t repl) const {\n      assert(repl <= LocalListLimit);\n      return TaggedPtr{idx, (tagAndSize & ~SizeMask) | repl};\n    }\n\n    TaggedPtr withSizeIncr() const {\n      assert(size() < LocalListLimit);\n      return TaggedPtr{idx, tagAndSize + 1};\n    }\n\n    TaggedPtr withSizeDecr() const {\n      assert(size() > 0);\n      return TaggedPtr{idx, tagAndSize - 1};\n    }\n\n    TaggedPtr withIdx(uint32_t repl) const {\n      return TaggedPtr{repl, tagAndSize + TagIncr};\n    }\n\n    TaggedPtr withEmpty() const { return withIdx(0).withSize(0); }\n  };\n\n  struct alignas(hardware_destructive_interference_size) LocalList {\n    AtomicStruct<TaggedPtr, Atom> head;\n\n    LocalList() : head(TaggedPtr{}) {}\n  };\n\n  ////////// fields\n\n  /// the number of bytes allocated from mmap, which is a multiple of\n  /// the page size of the machine\n  size_t mmapLength_;\n\n  /// the actual number of slots that we will allocate, to guarantee\n  /// that we will satisfy the capacity requested at construction time.\n  /// They will be numbered 1..actualCapacity_ (note the 1-based counting),\n  /// and occupy slots_[1..actualCapacity_].\n  uint32_t actualCapacity_;\n\n  /// this records the number of slots that have actually been constructed.\n  /// To allow use of atomic ++ instead of CAS, we let this overflow.\n  /// The actual number of constructed elements is min(actualCapacity_,\n  /// size_)\n  Atom<uint32_t> size_;\n\n  /// raw storage, only 1..min(size_,actualCapacity_) (inclusive) are\n  /// actually constructed.  Note that slots_[0] is not constructed or used\n  alignas(hardware_destructive_interference_size) Slot* slots_;\n\n  /// use AccessSpreader to find your list.  We use stripes instead of\n  /// thread-local to avoid the need to grow or shrink on thread start\n  /// or join.   These are heads of lists chained with localNext\n  LocalList local_[NumLocalLists];\n\n  /// this is the head of a list of node chained by globalNext, that are\n  /// themselves each the head of a list chained by localNext\n  alignas(hardware_destructive_interference_size)\n      AtomicStruct<TaggedPtr, Atom> globalHead_;\n\n  ///////////// private methods\n\n  uint32_t slotIndex(uint32_t idx) const {\n    assert(\n        0 < idx && idx <= actualCapacity_ &&\n        idx <= size_.load(std::memory_order_acquire));\n    return idx;\n  }\n\n  Slot& slot(uint32_t idx) { return slots_[slotIndex(idx)]; }\n\n  const Slot& slot(uint32_t idx) const { return slots_[slotIndex(idx)]; }\n\n  // localHead references a full list chained by localNext.  s should\n  // reference slot(localHead), it is passed as a micro-optimization\n  void globalPush(Slot& s, uint32_t localHead) {\n    while (true) {\n      TaggedPtr gh = globalHead_.load(std::memory_order_acquire);\n      s.globalNext.store(gh.idx, std::memory_order_relaxed);\n      if (globalHead_.compare_exchange_strong(gh, gh.withIdx(localHead))) {\n        // success\n        return;\n      }\n    }\n  }\n\n  // idx references a single node\n  void localPush(AtomicStruct<TaggedPtr, Atom>& head, uint32_t idx) {\n    Slot& s = slot(idx);\n    TaggedPtr h = head.load(std::memory_order_acquire);\n    bool recycled = false;\n    while (true) {\n      s.localNext.store(h.idx, std::memory_order_release);\n      if (!recycled) {\n        Traits::onRecycle(slot(idx).elemPtr());\n        recycled = true;\n      }\n\n      if (h.size() == LocalListLimit) {\n        // push will overflow local list, steal it instead\n        if (head.compare_exchange_strong(h, h.withEmpty())) {\n          // steal was successful, put everything in the global list\n          globalPush(s, idx);\n          return;\n        }\n      } else {\n        // local list has space\n        if (head.compare_exchange_strong(h, h.withIdx(idx).withSizeIncr())) {\n          // success\n          return;\n        }\n      }\n      // h was updated by failing CAS\n    }\n  }\n\n  // returns 0 if empty\n  uint32_t globalPop() {\n    while (true) {\n      TaggedPtr gh = globalHead_.load(std::memory_order_acquire);\n      if (gh.idx == 0 ||\n          globalHead_.compare_exchange_strong(\n              gh,\n              gh.withIdx(\n                  slot(gh.idx).globalNext.load(std::memory_order_relaxed)))) {\n        // global list is empty, or pop was successful\n        return gh.idx;\n      }\n    }\n  }\n\n  // returns 0 if allocation failed\n  uint32_t localPop(AtomicStruct<TaggedPtr, Atom>& head) {\n    while (true) {\n      TaggedPtr h = head.load(std::memory_order_acquire);\n      if (h.idx != 0) {\n        // local list is non-empty, try to pop\n        Slot& s = slot(h.idx);\n        auto next = s.localNext.load(std::memory_order_relaxed);\n        if (head.compare_exchange_strong(h, h.withIdx(next).withSizeDecr())) {\n          // success\n          return h.idx;\n        }\n        continue;\n      }\n\n      uint32_t idx = globalPop();\n      if (idx == 0) {\n        // global list is empty, allocate and construct new slot\n        if (size_.load(std::memory_order_relaxed) >= actualCapacity_ ||\n            (idx = ++size_) > actualCapacity_) {\n          // allocation failed\n          return 0;\n        }\n        Slot& s = slot(idx);\n        // Atom is enforced above to be nothrow-default-constructible\n        // As an optimization, use default-initialization (no parens) rather\n        // than direct-initialization (with parens): these locations are\n        // stored-to before they are loaded-from\n        new (&s.localNext) Atom<uint32_t>;\n        new (&s.globalNext) Atom<uint32_t>;\n        Traits::initialize(s.elemPtr());\n        return idx;\n      }\n\n      Slot& s = slot(idx);\n      auto next = s.localNext.load(std::memory_order_relaxed);\n      if (head.compare_exchange_strong(\n              h, h.withIdx(next).withSize(LocalListLimit))) {\n        // global list moved to local list, keep head for us\n        return idx;\n      }\n      // local bulk push failed, return idx to the global list and try again\n      globalPush(s, idx);\n    }\n  }\n\n  AtomicStruct<TaggedPtr, Atom>& localHead() {\n    auto stripe = AccessSpreader<Atom>::current(NumLocalLists);\n    return local_[stripe].head;\n  }\n\n  void markAllocated(Slot& slot) {\n    slot.localNext.store(uint32_t(-1), std::memory_order_release);\n  }\n\n public:\n  static constexpr std::size_t kSlotSize = sizeof(Slot);\n};\n\nnamespace detail {\n\n/// This is a stateful Deleter functor, which allows std::unique_ptr\n/// to track elements allocated from an IndexedMemPool by tracking the\n/// associated pool.  See IndexedMemPool::allocElem.\ntemplate <typename Pool>\nstruct IndexedMemPoolRecycler {\n  Pool* pool;\n\n  explicit IndexedMemPoolRecycler(Pool* pool) : pool(pool) {}\n\n  IndexedMemPoolRecycler(const IndexedMemPoolRecycler<Pool>& rhs) = default;\n  IndexedMemPoolRecycler& operator=(const IndexedMemPoolRecycler<Pool>& rhs) =\n      default;\n\n  void operator()(typename Pool::value_type* elem) const {\n    pool->recycleIndex(pool->locateElem(elem));\n  }\n};\n\n} // namespace detail\n\n} // namespace folly\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/IntrusiveList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/IntrusiveList.h> // @shim\n"
  },
  {
    "path": "folly/Lazy.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include <folly/Optional.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\n\n//////////////////////////////////////////////////////////////////////\n\n/*\n * Lazy -- for delayed initialization of a value.  The value's\n * initialization will be computed on demand at its first use, but\n * will not be recomputed if its value is requested again.  The value\n * may still be mutated after its initialization if the lazy is not\n * declared const.\n *\n * The value is created using folly::lazy, usually with a lambda, and\n * its value is requested using operator().\n *\n * Note that the value is not safe for concurrent accesses by multiple\n * threads, even if you declare it const.  See note below.\n *\n *\n * Example Usage:\n *\n *   void foo() {\n *     auto const val = folly::lazy([&]{\n *       return something_expensive(blah());\n *     });\n *\n *     if (condition1) {\n *       use(val());\n *     }\n *     if (condition2) {\n *       useMaybeAgain(val());\n *     } else {\n *       // Unneeded in this branch.\n *     }\n *   }\n *\n *\n * Rationale:\n *\n *    - operator() is used to request the value instead of an implicit\n *      conversion because the slight syntactic overhead in common\n *      seems worth the increased clarity.\n *\n *    - Lazy values do not model CopyConstructible because it is\n *      unclear what semantics would be desirable.  Either copies\n *      should share the cached value (adding overhead to cases that\n *      don't need to support copies), or they could recompute the\n *      value unnecessarily.  Sharing with mutable lazies would also\n *      leave them with non-value semantics despite looking\n *      value-like.\n *\n *    - Not thread safe for const accesses.  Many use cases for lazy\n *      values are local variables on the stack, where multiple\n *      threads shouldn't even be able to reach the value.  It still\n *      is useful to indicate/check that the value doesn't change with\n *      const, particularly when it is captured by a large family of\n *      lambdas.  Adding internal synchronization seems like it would\n *      pessimize the most common use case in favor of less likely use\n *      cases.\n *\n */\n\n//////////////////////////////////////////////////////////////////////\n\nnamespace detail {\n\ntemplate <class Func>\nstruct Lazy {\n  using result_type = invoke_result_t<Func>;\n\n  static_assert(\n      !std::is_const<Func>::value, \"Func should not be a const-qualified type\");\n  static_assert(\n      !std::is_reference<Func>::value, \"Func should not be a reference type\");\n\n  explicit Lazy(Func&& f) : func_(std::move(f)) {}\n  explicit Lazy(const Func& f) : func_(f) {}\n\n  Lazy(Lazy&& o) : value_(std::move(o.value_)), func_(std::move(o.func_)) {}\n\n  Lazy(const Lazy&) = delete;\n  Lazy& operator=(const Lazy&) = delete;\n  Lazy& operator=(Lazy&&) = delete;\n\n  const result_type& operator()() const {\n    ensure_initialized();\n\n    return *value_;\n  }\n\n  result_type& operator()() {\n    ensure_initialized();\n\n    return *value_;\n  }\n\n private:\n  void ensure_initialized() const {\n    if (!value_) {\n      value_ = func_();\n    }\n  }\n\n  mutable Optional<result_type> value_;\n  mutable Func func_;\n};\n\n} // namespace detail\n\n//////////////////////////////////////////////////////////////////////\n\ntemplate <class Func>\nauto lazy(Func&& fun) {\n  return detail::Lazy<remove_cvref_t<Func>>(std::forward<Func>(fun));\n}\n\n//////////////////////////////////////////////////////////////////////\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Likely.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Likeliness annotations.\n *\n * Useful when the author has better knowledge than the compiler of whether\n * the branch condition is overwhelmingly likely to take a specific value.\n *\n * Useful when the author has better knowledge than the compiler of which code\n * paths are designed as the fast path and which are designed as the slow path,\n * and to force the compiler to optimize for the fast path, even when it is not\n * overwhelmingly likely.\n *\n * Notes:\n * * All supported compilers treat unconditionally-noreturn blocks as unlikely.\n *   This is true for blocks which unconditionally throw exceptions and for\n *   blocks which unconditionally call [[noreturn]]-annotated functions. Such\n *   cases do not require likeliness annotations.\n *\n * @file Likely.h\n * @refcode folly/docs/examples/folly/Likely.cpp\n */\n\n#pragma once\n\n#include <folly/lang/Builtin.h>\n\n/**\n * Treat the condition as likely.\n *\n * @def FOLLY_LIKELY\n */\n#define FOLLY_LIKELY(...) FOLLY_BUILTIN_EXPECT((__VA_ARGS__), 1)\n\n/**\n * Treat the condition as unlikely.\n *\n * @def FOLLY_UNLIKELY\n */\n#define FOLLY_UNLIKELY(...) FOLLY_BUILTIN_EXPECT((__VA_ARGS__), 0)\n\n// Un-namespaced annotations\n\n#undef LIKELY\n#undef UNLIKELY\n\n/**\n * Treat the condition as likely.\n *\n * @def LIKELY\n */\n/**\n * Treat the condition as unlikely.\n *\n * @def UNLIKELY\n */\n#if defined(__GNUC__)\n#define LIKELY(x) (__builtin_expect((x), 1))\n#define UNLIKELY(x) (__builtin_expect((x), 0))\n#else\n#define LIKELY(x) (x)\n#define UNLIKELY(x) (x)\n#endif\n"
  },
  {
    "path": "folly/MPMCPipeline.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <utility>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/detail/MPMCPipelineDetail.h>\n\nnamespace folly {\n\n/**\n * Helper tag template to use amplification > 1\n */\ntemplate <class T, size_t Amp>\nclass MPMCPipelineStage;\n\n/**\n * Multi-Producer, Multi-Consumer pipeline.\n *\n * A N-stage pipeline is a combination of N+1 MPMC queues (see MPMCQueue.h).\n *\n * At each stage, you may dequeue the results from the previous stage (possibly\n * from multiple threads) and enqueue results to the next stage. Regardless of\n * the order of completion, data is delivered to the next stage in the original\n * order.  Each input is matched with a \"ticket\" which must be produced\n * when enqueueing to the next stage.\n *\n * A given stage must produce exactly K (\"amplification factor\", default K=1)\n * results for every input. This is enforced by requiring that each ticket\n * is used exactly K times.\n *\n * Usage:\n *\n * // arguments are queue sizes\n * MPMCPipeline<int, std::string, int> pipeline(10, 10, 10);\n *\n * pipeline.blockingWrite(42);\n *\n * {\n *   int val;\n *   auto ticket = pipeline.blockingReadStage<0>(val);\n *   pipeline.blockingWriteStage<0>(ticket, folly::to<std::string>(val));\n * }\n *\n * {\n *   std::string val;\n *   auto ticket = pipeline.blockingReadStage<1>(val);\n *   int ival = 0;\n *   try {\n *     ival = folly::to<int>(val);\n *   } catch (...) {\n *     // We must produce exactly 1 output even on exception!\n *   }\n *   pipeline.blockingWriteStage<1>(ticket, ival);\n * }\n *\n * int result;\n * pipeline.blockingRead(result);\n * // result == 42\n *\n * To specify amplification factors greater than 1, use\n * MPMCPipelineStage<T, amplification> instead of T in the declaration:\n *\n * MPMCPipeline<int,\n *              MPMCPipelineStage<std::string, 2>,\n *              MPMCPipelineStage<int, 4>>\n *\n * declares a two-stage pipeline: the first stage produces 2 strings\n * for each input int, the second stage produces 4 ints for each input string,\n * so, overall, the pipeline produces 2*4 = 8 ints for each input int.\n *\n * Implementation details: we use N+1 MPMCQueue objects; each intermediate\n * queue connects two adjacent stages.  The MPMCQueue implementation is abused;\n * instead of using it as a queue, we insert in the output queue at the\n * position determined by the input queue's popTicket_.  We guarantee that\n * all slots are filled (and therefore the queue doesn't freeze) because\n * we require that each step produces exactly K outputs for every input.\n */\ntemplate <class In, class... Stages>\nclass MPMCPipeline {\n  typedef std::tuple<detail::PipelineStageInfo<Stages>...> StageInfos;\n  typedef std::tuple<\n      detail::MPMCPipelineStageImpl<In>,\n      detail::MPMCPipelineStageImpl<\n          typename detail::PipelineStageInfo<Stages>::value_type>...>\n      StageTuple;\n  static constexpr size_t kAmplification =\n      detail::AmplificationProduct<StageInfos>::value;\n\n  class TicketBaseDebug {\n   public:\n    TicketBaseDebug() noexcept : owner_(nullptr), value_(0xdeadbeeffaceb00c) {}\n    TicketBaseDebug(TicketBaseDebug&& other) noexcept\n        : owner_(std::exchange(other.owner_, nullptr)),\n          value_(std::exchange(other.value_, 0xdeadbeeffaceb00c)) {}\n    explicit TicketBaseDebug(MPMCPipeline* owner, uint64_t value) noexcept\n        : owner_(owner), value_(value) {}\n    void check_owner(MPMCPipeline* owner) const { CHECK(owner == owner_); }\n\n    MPMCPipeline* owner_;\n    uint64_t value_;\n  };\n\n  class TicketBaseNDebug {\n   public:\n    TicketBaseNDebug() = default;\n    TicketBaseNDebug(TicketBaseNDebug&&) = default;\n    explicit TicketBaseNDebug(MPMCPipeline*, uint64_t value) noexcept\n        : value_(value) {}\n    void check_owner(MPMCPipeline*) const {}\n\n    uint64_t value_;\n  };\n\n  using TicketBase =\n      std::conditional_t<kIsDebug, TicketBaseDebug, TicketBaseNDebug>;\n\n public:\n  /**\n   * Ticket, returned by blockingReadStage, must be given back to\n   * blockingWriteStage. Tickets are not thread-safe.\n   */\n  template <size_t Stage>\n  class Ticket : TicketBase {\n   public:\n    ~Ticket() noexcept {\n      CHECK_EQ(remainingUses_, 0) << \"All tickets must be completely used!\";\n    }\n\n    Ticket() noexcept : remainingUses_(0) {}\n\n    Ticket(Ticket&& other) noexcept\n        : TicketBase(static_cast<TicketBase&&>(other)),\n          remainingUses_(std::exchange(other.remainingUses_, 0)) {}\n\n    Ticket& operator=(Ticket&& other) noexcept {\n      if (this != &other) {\n        this->~Ticket();\n        new (this) Ticket(std::move(other));\n      }\n      return *this;\n    }\n\n   private:\n    friend class MPMCPipeline;\n    size_t remainingUses_;\n\n    Ticket(MPMCPipeline* owner, size_t amplification, uint64_t value) noexcept\n        : TicketBase(owner, value * amplification),\n          remainingUses_(amplification) {}\n\n    uint64_t use(MPMCPipeline* owner) {\n      CHECK_GT(remainingUses_--, 0);\n      TicketBase::check_owner(owner);\n      return TicketBase::value_++;\n    }\n  };\n\n  /**\n   * Default-construct pipeline. Useful to move-assign later,\n   * just like MPMCQueue, see MPMCQueue.h for more details.\n   */\n  MPMCPipeline() = default;\n\n  /**\n   * Construct a pipeline with N+1 queue sizes.\n   */\n  template <class... Sizes>\n  explicit MPMCPipeline(Sizes... sizes) : stages_(sizes...) {}\n\n  /**\n   * Push an element into (the first stage of) the pipeline. Blocking.\n   */\n  template <class... Args>\n  void blockingWrite(Args&&... args) {\n    std::get<0>(stages_).blockingWrite(std::forward<Args>(args)...);\n  }\n\n  /**\n   * Try to push an element into (the first stage of) the pipeline.\n   * Non-blocking.\n   */\n  template <class... Args>\n  bool write(Args&&... args) {\n    return std::get<0>(stages_).write(std::forward<Args>(args)...);\n  }\n\n  /**\n   * Read an element for stage Stage and obtain a ticket. Blocking.\n   */\n  template <size_t Stage>\n  Ticket<Stage> blockingReadStage(\n      typename std::tuple_element<Stage, StageTuple>::type::value_type& elem) {\n    return Ticket<Stage>(\n        this,\n        std::tuple_element<Stage, StageInfos>::type::kAmplification,\n        std::get<Stage>(stages_).blockingRead(elem));\n  }\n\n  /**\n   * Try to read an element for stage Stage and obtain a ticket.\n   * Non-blocking.\n   */\n  template <size_t Stage>\n  bool readStage(\n      Ticket<Stage>& ticket,\n      typename std::tuple_element<Stage, StageTuple>::type::value_type& elem) {\n    uint64_t tval;\n    if (!std::get<Stage>(stages_).readAndGetTicket(tval, elem)) {\n      return false;\n    }\n    ticket = Ticket<Stage>(\n        this,\n        std::tuple_element<Stage, StageInfos>::type::kAmplification,\n        tval);\n    return true;\n  }\n\n  /**\n   * Complete an element in stage Stage (pushing it for stage Stage+1).\n   * Blocking.\n   */\n  template <size_t Stage, class... Args>\n  void blockingWriteStage(Ticket<Stage>& ticket, Args&&... args) {\n    std::get<Stage + 1>(stages_).blockingWriteWithTicket(\n        ticket.use(this), std::forward<Args>(args)...);\n  }\n\n  /**\n   * Pop an element from (the final stage of) the pipeline. Blocking.\n   */\n  void blockingRead(\n      typename std::tuple_element<sizeof...(Stages), StageTuple>::type::\n          value_type& elem) {\n    std::get<sizeof...(Stages)>(stages_).blockingRead(elem);\n  }\n\n  /**\n   * Try to pop an element from (the final stage of) the pipeline.\n   * Non-blocking.\n   */\n  bool read(\n      typename std::tuple_element<sizeof...(Stages), StageTuple>::type::\n          value_type& elem) {\n    return std::get<sizeof...(Stages)>(stages_).read(elem);\n  }\n\n  /**\n   * Estimate queue size, measured as values from the last stage.\n   * (so if the pipeline has an amplification factor > 1, pushing an element\n   * into the first stage will cause sizeGuess() to be == amplification factor)\n   * Elements \"in flight\" (currently processed as part of a stage, so not\n   * in any queue) are also counted.\n   */\n  ssize_t sizeGuess() const noexcept {\n    return ssize_t(\n        std::get<0>(stages_).writeCount() * kAmplification -\n        std::get<sizeof...(Stages)>(stages_).readCount());\n  }\n\n private:\n  StageTuple stages_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/MPMCQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <cassert>\n#include <cstring>\n#include <limits>\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/detail/TurnSequencer.h>\n#include <folly/lang/Exception.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\nnamespace detail {\n\ntemplate <typename T, template <typename> class Atom>\nstruct SingleElementQueue;\n\ntemplate <typename T>\nclass MPMCPipelineStageImpl;\n\n/// MPMCQueue base CRTP template\ntemplate <typename>\nclass MPMCQueueBase;\n\n} // namespace detail\n\n/// MPMCQueue<T> is a high-performance bounded concurrent queue that\n/// supports multiple producers, multiple consumers, and optional blocking.\n/// The queue has a fixed capacity, for which all memory will be allocated\n/// up front.  The bulk of the work of enqueuing and dequeuing can be\n/// performed in parallel.\n///\n/// MPMCQueue is linearizable.  That means that if a call to write(A)\n/// returns before a call to write(B) begins, then A will definitely end up\n/// in the queue before B, and if a call to read(X) returns before a call\n/// to read(Y) is started, that X will be something from earlier in the\n/// queue than Y.  This also means that if a read call returns a value, you\n/// can be sure that all previous elements of the queue have been assigned\n/// a reader (that reader might not yet have returned, but it exists).\n///\n/// The underlying implementation uses a ticket dispenser for the head and\n/// the tail, spreading accesses across N single-element queues to produce\n/// a queue with capacity N.  The ticket dispensers use atomic increment,\n/// which is more robust to contention than a CAS loop.  Each of the\n/// single-element queues uses its own CAS to serialize access, with an\n/// adaptive spin cutoff.  When spinning fails on a single-element queue\n/// it uses futex()'s _BITSET operations to reduce unnecessary wakeups\n/// even if multiple waiters are present on an individual queue (such as\n/// when the MPMCQueue's capacity is smaller than the number of enqueuers\n/// or dequeuers).\n///\n/// In benchmarks (contained in tao/queues/ConcurrentQueueTests)\n/// it handles 1 to 1, 1 to N, N to 1, and N to M thread counts better\n/// than any of the alternatives present in fbcode, for both small (~10)\n/// and large capacities.  In these benchmarks it is also faster than\n/// tbb::concurrent_bounded_queue for all configurations.  When there are\n/// many more threads than cores, MPMCQueue is _much_ faster than the tbb\n/// queue because it uses futex() to block and unblock waiting threads,\n/// rather than spinning with sched_yield.\n///\n/// NOEXCEPT INTERACTION: tl;dr; If it compiles you're fine.  Ticket-based\n/// queues separate the assignment of queue positions from the actual\n/// construction of the in-queue elements, which means that the T\n/// constructor used during enqueue must not throw an exception.  This is\n/// enforced at compile time using type traits, which requires that T be\n/// adorned with accurate noexcept information.  If your type does not\n/// use noexcept, you will have to wrap it in something that provides\n/// the guarantee.\n///\n/// If you have a pool of N queue consumers that you want to shut down\n/// after the queue has drained, one way is to enqueue N sentinel values\n/// to the queue.  If the producer doesn't know how many consumers there\n/// are you can enqueue one sentinel and then have each consumer requeue\n/// two sentinels after it receives it (by requeuing 2 the shutdown can\n/// complete in O(log P) time instead of O(P)).\ntemplate <\n    typename T,\n    template <typename> class Atom = std::atomic,\n    bool Dynamic = false,\n    class Allocator = std::allocator<T>>\nclass MPMCQueue\n    : public detail::MPMCQueueBase<MPMCQueue<T, Atom, Dynamic, Allocator>>,\n      std::allocator_traits<Allocator>::template rebind_alloc<\n          detail::SingleElementQueue<T, Atom>> {\n  static_assert(std::is_nothrow_move_constructible_v<T>);\n  static_assert(std::is_nothrow_destructible_v<T>);\n\n  friend class detail::MPMCPipelineStageImpl<T>;\n  using Base = detail::MPMCQueueBase<MPMCQueue<T, Atom, Dynamic, Allocator>>;\n  using Slot = detail::SingleElementQueue<T, Atom>;\n  using SlotAllocator =\n      typename std::allocator_traits<Allocator>::template rebind_alloc<Slot>;\n  using SlotAllocatorTraits = std::allocator_traits<SlotAllocator>;\n\n public:\n  using typename Base::value_type;\n\n  explicit MPMCQueue(size_t queueCapacity) : Base(queueCapacity) {\n    initQueue(queueCapacity);\n  }\n\n  MPMCQueue(size_t queueCapacity, const SlotAllocator& alloc)\n      : Base(queueCapacity), SlotAllocator(alloc) {\n    initQueue(queueCapacity);\n  }\n\n  MPMCQueue() noexcept = default;\n  MPMCQueue(MPMCQueue&&) noexcept = default;\n  /// IMPORTANT: The move operator is here to make it easier to perform\n  /// the initialization phase, it is not safe to use when there are any\n  /// concurrent accesses (this is not checked).\n  MPMCQueue const& operator=(MPMCQueue&& rhs) {\n    if (this != &rhs) {\n      this->~MPMCQueue();\n      new (this) MPMCQueue(std::move(rhs));\n    }\n    return *this;\n  }\n  ~MPMCQueue() {\n    if (kUsingStdAllocator) {\n      delete[] this->slots_;\n      this->slots_ = nullptr;\n    } else {\n      if (this->slots_) {\n        size_t count = this->capacity_ + 2 * this->kSlotPadding;\n        for (size_t i = 0; i < count; ++i) {\n          SlotAllocatorTraits::destroy(*this, this->slots_ + i);\n        }\n        SlotAllocatorTraits::deallocate(*this, this->slots_, count);\n        this->slots_ = nullptr;\n      }\n    }\n  }\n\n private:\n  static constexpr bool kUsingStdAllocator =\n      is_instantiation_of_v<std::allocator, Allocator>;\n\n  void initQueue(size_t queueCapacity) {\n    this->stride_ = this->computeStride(queueCapacity);\n    size_t count = queueCapacity + 2 * this->kSlotPadding;\n    if (kUsingStdAllocator) {\n      this->slots_ = new Slot[count];\n    } else {\n      this->slots_ = SlotAllocatorTraits::allocate(*this, count);\n      for (size_t i = 0; i < count; ++i) {\n        SlotAllocatorTraits::construct(*this, this->slots_ + i);\n      }\n    }\n  }\n};\n\n/// *** The dynamic version of MPMCQueue is deprecated. ***\n/// Use UnboundedQueue instead.\n\n/// The dynamic version of MPMCQueue allows dynamic expansion of queue\n/// capacity, such that a queue may start with a smaller capacity than\n/// specified and expand only if needed. Users may optionally specify\n/// the initial capacity and the expansion multiplier.\n///\n/// The design uses a seqlock to enforce mutual exclusion among\n/// expansion attempts. Regular operations read up-to-date queue\n/// information (slots array, capacity, stride) inside read-only\n/// seqlock sections, which are unimpeded when no expansion is in\n/// progress.\n///\n/// An expansion computes a new capacity, allocates a new slots array,\n/// and updates stride. No information needs to be copied from the\n/// current slots array to the new one. When this happens, new slots\n/// will not have sequence numbers that match ticket numbers. The\n/// expansion needs to compute a ticket offset such that operations\n/// that use new arrays can adjust the calculations of slot indexes\n/// and sequence numbers that take into account that the new slots\n/// start with sequence numbers of zero. The current ticket offset is\n/// packed with the seqlock in an atomic 64-bit integer. The initial\n/// offset is zero.\n///\n/// Lagging write and read operations with tickets lower than the\n/// ticket offset of the current slots array (i.e., the minimum ticket\n/// number that can be served by the current array) must use earlier\n/// closed arrays instead of the current one. Information about closed\n/// slots arrays (array address, capacity, stride, and offset) is\n/// maintained in a logarithmic-sized structure. Each entry in that\n/// structure never needs to be changed once set. The number of closed\n/// arrays is half the value of the seqlock (when unlocked).\n///\n/// The acquisition of the seqlock to perform an expansion does not\n/// prevent the issuing of new push and pop tickets concurrently. The\n/// expansion must set the new ticket offset to a value that couldn't\n/// have been issued to an operation that has already gone through a\n/// seqlock read-only section (and hence obtained information for\n/// older closed arrays).\n///\n/// Note that the total queue capacity can temporarily exceed the\n/// specified capacity when there are lagging consumers that haven't\n/// yet consumed all the elements in closed arrays. Users should not\n/// rely on the capacity of dynamic queues for synchronization, e.g.,\n/// they should not expect that a thread will definitely block on a\n/// call to blockingWrite() when the queue size is known to be equal\n/// to its capacity.\n///\n/// Note that some writeIfNotFull() and tryWriteUntil() operations may\n/// fail even if the size of the queue is less than its maximum\n/// capacity and despite the success of expansion, if the operation\n/// happens to acquire a ticket that belongs to a closed array. This\n/// is a transient condition. Typically, one or two ticket values may\n/// be subject to such condition per expansion.\n///\n/// The dynamic version is a partial specialization of MPMCQueue with\n/// Dynamic == true.\ntemplate <typename T, template <typename> class Atom, class Allocator>\nclass MPMCQueue<T, Atom, true, Allocator>\n    : public detail::MPMCQueueBase<MPMCQueue<T, Atom, true, Allocator>> {\n  static_assert(\n      is_instantiation_of_v<std::allocator, Allocator>,\n      \"The dynamic version of MPMCQueue does not support custom allocators\");\n  friend class detail::MPMCQueueBase<MPMCQueue<T, Atom, true, Allocator>>;\n  using Slot = detail::SingleElementQueue<T, Atom>;\n  using Base = detail::MPMCQueueBase<MPMCQueue<T, Atom, true, Allocator>>;\n  struct ClosedArray {\n    uint64_t offset_{0};\n    Slot* slots_{nullptr};\n    size_t capacity_{0};\n    int stride_{0};\n  };\n\n public:\n  explicit MPMCQueue(size_t queueCapacity) : Base(queueCapacity) {\n    size_t cap = std::min<size_t>(kDefaultMinDynamicCapacity, queueCapacity);\n    initQueue(cap, kDefaultExpansionMultiplier);\n  }\n\n  explicit MPMCQueue(\n      size_t queueCapacity, size_t minCapacity, size_t expansionMultiplier)\n      : Base(queueCapacity) {\n    minCapacity = std::max<size_t>(1, minCapacity);\n    size_t cap = std::min<size_t>(minCapacity, queueCapacity);\n    expansionMultiplier = std::max<size_t>(2, expansionMultiplier);\n    initQueue(cap, expansionMultiplier);\n  }\n\n  MPMCQueue() noexcept {\n    dmult_ = 0;\n    closed_ = nullptr;\n  }\n\n  MPMCQueue(MPMCQueue<T, Atom, true>&& rhs) noexcept {\n    this->capacity_ = rhs.capacity_;\n    new (&this->dslots_)\n        Atom<Slot*>(rhs.dslots_.load(std::memory_order_relaxed));\n    new (&this->dstride_)\n        Atom<int>(rhs.dstride_.load(std::memory_order_relaxed));\n    this->dstate_.store(\n        rhs.dstate_.load(std::memory_order_relaxed), std::memory_order_relaxed);\n    this->dcapacity_.store(\n        rhs.dcapacity_.load(std::memory_order_relaxed),\n        std::memory_order_relaxed);\n    this->pushTicket_.store(\n        rhs.pushTicket_.load(std::memory_order_relaxed),\n        std::memory_order_relaxed);\n    this->popTicket_.store(\n        rhs.popTicket_.load(std::memory_order_relaxed),\n        std::memory_order_relaxed);\n    this->pushSpinCutoff_.store(\n        rhs.pushSpinCutoff_.load(std::memory_order_relaxed),\n        std::memory_order_relaxed);\n    this->popSpinCutoff_.store(\n        rhs.popSpinCutoff_.load(std::memory_order_relaxed),\n        std::memory_order_relaxed);\n    dmult_ = rhs.dmult_;\n    closed_ = rhs.closed_;\n\n    rhs.capacity_ = 0;\n    rhs.dslots_.store(nullptr, std::memory_order_relaxed);\n    rhs.dstride_.store(0, std::memory_order_relaxed);\n    rhs.dstate_.store(0, std::memory_order_relaxed);\n    rhs.dcapacity_.store(0, std::memory_order_relaxed);\n    rhs.pushTicket_.store(0, std::memory_order_relaxed);\n    rhs.popTicket_.store(0, std::memory_order_relaxed);\n    rhs.pushSpinCutoff_.store(0, std::memory_order_relaxed);\n    rhs.popSpinCutoff_.store(0, std::memory_order_relaxed);\n    rhs.dmult_ = 0;\n    rhs.closed_ = nullptr;\n  }\n\n  /// IMPORTANT: The move operator is here to make it easier to perform\n  /// the initialization phase, it is not safe to use when there are any\n  /// concurrent accesses (this is not checked).\n  MPMCQueue<T, Atom, true> const& operator=(MPMCQueue<T, Atom, true>&& rhs) {\n    if (this != &rhs) {\n      this->~MPMCQueue();\n      new (this) MPMCQueue(std::move(rhs));\n    }\n    return *this;\n  }\n\n  ~MPMCQueue() {\n    if (closed_ != nullptr) {\n      for (int i = getNumClosed(this->dstate_.load()) - 1; i >= 0; --i) {\n        delete[] closed_[i].slots_;\n      }\n      delete[] closed_;\n    }\n    using AtomInt = Atom<int>;\n    this->dstride_.~AtomInt();\n    using AtomSlot = Atom<Slot*>;\n    // Sort of a hack to get ~MPMCQueueBase to free dslots_\n    auto slots = this->dslots_.load();\n    this->dslots_.~AtomSlot();\n    this->slots_ = slots;\n  }\n\n  size_t allocatedCapacity() const noexcept {\n    return this->dcapacity_.load(std::memory_order_relaxed);\n  }\n\n  template <typename... Args>\n  void blockingWrite(Args&&... args) noexcept {\n    uint64_t ticket = this->pushTicket_++;\n    Slot* slots;\n    size_t cap;\n    int stride;\n    uint64_t state;\n    uint64_t offset;\n    do {\n      if (!trySeqlockReadSection(state, slots, cap, stride)) {\n        asm_volatile_pause();\n        continue;\n      }\n      if (maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride)) {\n        // There was an expansion after this ticket was issued.\n        break;\n      }\n      if (slots[this->idx((ticket - offset), cap, stride)].mayEnqueue(\n              this->turn(ticket - offset, cap))) {\n        // A slot is ready. No need to expand.\n        break;\n      } else if (\n          this->popTicket_.load(std::memory_order_relaxed) + cap > ticket) {\n        // May block, but a pop is in progress. No need to expand.\n        // Get seqlock read section info again in case an expansion\n        // occurred with an equal or higher ticket.\n        continue;\n      } else {\n        // May block. See if we can expand.\n        if (tryExpand(state, cap)) {\n          // This or another thread started an expansion. Get updated info.\n          continue;\n        } else {\n          // Can't expand.\n          break;\n        }\n      }\n    } while (true);\n    this->enqueueWithTicketBase(\n        ticket - offset, slots, cap, stride, std::forward<Args>(args)...);\n  }\n\n  void blockingReadWithTicket(uint64_t& ticket, T& elem) noexcept {\n    ticket = this->popTicket_++;\n    Slot* slots;\n    size_t cap;\n    int stride;\n    uint64_t state;\n    uint64_t offset;\n    while (!trySeqlockReadSection(state, slots, cap, stride)) {\n      asm_volatile_pause();\n    }\n    // If there was an expansion after the corresponding push ticket\n    // was issued, adjust accordingly\n    maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride);\n    this->dequeueWithTicketBase(ticket - offset, slots, cap, stride, elem);\n  }\n\n private:\n  enum {\n    kSeqlockBits = 6,\n    kDefaultMinDynamicCapacity = 10,\n    kDefaultExpansionMultiplier = 10,\n  };\n\n  size_t dmult_;\n\n  //  Info about closed slots arrays for use by lagging operations\n  ClosedArray* closed_;\n\n  void initQueue(const size_t cap, const size_t mult) {\n    new (&this->dstride_) Atom<int>(this->computeStride(cap));\n    Slot* slots = new Slot[cap + 2 * this->kSlotPadding];\n    new (&this->dslots_) Atom<Slot*>(slots);\n    this->dstate_.store(0);\n    this->dcapacity_.store(cap);\n    dmult_ = mult;\n    size_t maxClosed = 0;\n    for (size_t expanded = cap; expanded < this->capacity_; expanded *= mult) {\n      ++maxClosed;\n    }\n    closed_ = (maxClosed > 0) ? new ClosedArray[maxClosed] : nullptr;\n  }\n\n  bool tryObtainReadyPushTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    uint64_t state;\n    do {\n      ticket = this->pushTicket_.load(std::memory_order_acquire); // A\n      if (!trySeqlockReadSection(state, slots, cap, stride)) {\n        asm_volatile_pause();\n        continue;\n      }\n\n      // If there was an expansion with offset greater than this ticket,\n      // adjust accordingly\n      uint64_t offset;\n      maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride);\n\n      if (slots[this->idx((ticket - offset), cap, stride)].mayEnqueue(\n              this->turn(ticket - offset, cap))) {\n        // A slot is ready.\n        if (this->pushTicket_.compare_exchange_strong(ticket, ticket + 1)) {\n          // Adjust ticket\n          ticket -= offset;\n          return true;\n        } else {\n          continue;\n        }\n      } else {\n        if (ticket != this->pushTicket_.load(std::memory_order_relaxed)) { // B\n          // Try again. Ticket changed.\n          continue;\n        }\n        // Likely to block.\n        // Try to expand unless the ticket is for a closed array\n        if (offset == getOffset(state)) {\n          if (tryExpand(state, cap)) {\n            // This or another thread started an expansion. Get up-to-date info.\n            continue;\n          }\n        }\n        return false;\n      }\n    } while (true);\n  }\n\n  bool tryObtainPromisedPushTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    uint64_t state;\n    do {\n      ticket = this->pushTicket_.load(std::memory_order_acquire);\n      auto numPops = this->popTicket_.load(std::memory_order_acquire);\n      if (!trySeqlockReadSection(state, slots, cap, stride)) {\n        asm_volatile_pause();\n        continue;\n      }\n\n      const auto curCap = cap;\n      // If there was an expansion with offset greater than this ticket,\n      // adjust accordingly\n      uint64_t offset;\n      maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride);\n\n      int64_t n = ticket - numPops;\n\n      if (n >= static_cast<ssize_t>(cap)) {\n        if ((cap == curCap) && tryExpand(state, cap)) {\n          // This or another thread started an expansion. Start over.\n          continue;\n        }\n        // Can't expand.\n        ticket -= offset;\n        return false;\n      }\n\n      if (this->pushTicket_.compare_exchange_strong(ticket, ticket + 1)) {\n        // Adjust ticket\n        ticket -= offset;\n        return true;\n      }\n    } while (true);\n  }\n\n  bool tryObtainReadyPopTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    uint64_t state;\n    do {\n      ticket = this->popTicket_.load(std::memory_order_relaxed);\n      if (!trySeqlockReadSection(state, slots, cap, stride)) {\n        asm_volatile_pause();\n        continue;\n      }\n\n      // If there was an expansion after the corresponding push ticket\n      // was issued, adjust accordingly\n      uint64_t offset;\n      maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride);\n\n      if (slots[this->idx((ticket - offset), cap, stride)].mayDequeue(\n              this->turn(ticket - offset, cap))) {\n        if (this->popTicket_.compare_exchange_strong(ticket, ticket + 1)) {\n          // Adjust ticket\n          ticket -= offset;\n          return true;\n        }\n      } else {\n        return false;\n      }\n    } while (true);\n  }\n\n  bool tryObtainPromisedPopTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    uint64_t state;\n    do {\n      ticket = this->popTicket_.load(std::memory_order_acquire);\n      auto numPushes = this->pushTicket_.load(std::memory_order_acquire);\n      if (!trySeqlockReadSection(state, slots, cap, stride)) {\n        asm_volatile_pause();\n        continue;\n      }\n\n      uint64_t offset;\n      // If there was an expansion after the corresponding push\n      // ticket was issued, adjust accordingly\n      maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride);\n\n      if (ticket >= numPushes) {\n        ticket -= offset;\n        return false;\n      }\n      if (this->popTicket_.compare_exchange_strong(ticket, ticket + 1)) {\n        ticket -= offset;\n        return true;\n      }\n    } while (true);\n  }\n\n  /// Enqueues an element with a specific ticket number\n  template <typename... Args>\n  void enqueueWithTicket(const uint64_t ticket, Args&&... args) noexcept {\n    Slot* slots;\n    size_t cap;\n    int stride;\n    uint64_t state;\n    uint64_t offset;\n\n    while (!trySeqlockReadSection(state, slots, cap, stride)) {\n    }\n\n    // If there was an expansion after this ticket was issued, adjust\n    // accordingly\n    maybeUpdateFromClosed(state, ticket, offset, slots, cap, stride);\n\n    this->enqueueWithTicketBase(\n        ticket - offset, slots, cap, stride, std::forward<Args>(args)...);\n  }\n\n  uint64_t getOffset(const uint64_t state) const noexcept {\n    return state >> kSeqlockBits;\n  }\n\n  int getNumClosed(const uint64_t state) const noexcept {\n    return (state & ((1 << kSeqlockBits) - 1)) >> 1;\n  }\n\n  /// Try to expand the queue. Returns true if this expansion was\n  /// successful or a concurrent expansion is in progress. Returns\n  /// false if the queue has reached its maximum capacity or\n  /// allocation has failed.\n  bool tryExpand(const uint64_t state, const size_t cap) noexcept {\n    if (cap == this->capacity_) {\n      return false;\n    }\n    // Acquire seqlock\n    uint64_t oldval = state;\n    assert((state & 1) == 0);\n    if (this->dstate_.compare_exchange_strong(oldval, state + 1)) {\n      assert(cap == this->dcapacity_.load());\n      uint64_t ticket =\n          1 + std::max(this->pushTicket_.load(), this->popTicket_.load());\n      size_t newCapacity = std::min(dmult_ * cap, this->capacity_);\n      Slot* newSlots =\n          new (std::nothrow) Slot[newCapacity + 2 * this->kSlotPadding];\n      if (newSlots == nullptr) {\n        // Expansion failed. Restore the seqlock\n        this->dstate_.store(state);\n        return false;\n      }\n      // Successful expansion\n      // calculate the current ticket offset\n      uint64_t offset = getOffset(state);\n      // calculate index in closed array\n      int index = getNumClosed(state);\n      assert((index << 1) < (1 << kSeqlockBits));\n      // fill the info for the closed slots array\n      closed_[index].offset_ = offset;\n      closed_[index].slots_ = this->dslots_.load();\n      closed_[index].capacity_ = cap;\n      closed_[index].stride_ = this->dstride_.load();\n      // update the new slots array info\n      this->dslots_.store(newSlots);\n      this->dcapacity_.store(newCapacity);\n      this->dstride_.store(this->computeStride(newCapacity));\n      // Release the seqlock and record the new ticket offset\n      this->dstate_.store((ticket << kSeqlockBits) + (2 * (index + 1)));\n      return true;\n    } else { // failed to acquire seqlock\n      // Someone acquired the seqlock. Go back to the caller and get\n      // up-to-date info.\n      return true;\n    }\n  }\n\n  /// Seqlock read-only section\n  bool trySeqlockReadSection(\n      uint64_t& state, Slot*& slots, size_t& cap, int& stride) noexcept {\n    state = this->dstate_.load(std::memory_order_acquire);\n    if (state & 1) {\n      // Locked.\n      return false;\n    }\n    // Start read-only section.\n    slots = this->dslots_.load(std::memory_order_relaxed);\n    cap = this->dcapacity_.load(std::memory_order_relaxed);\n    stride = this->dstride_.load(std::memory_order_relaxed);\n    // End of read-only section. Validate seqlock.\n    std::atomic_thread_fence(std::memory_order_acquire);\n    return (state == this->dstate_.load(std::memory_order_relaxed));\n  }\n\n  /// If there was an expansion after ticket was issued, update local variables\n  /// of the lagging operation using the most recent closed array with\n  /// offset <= ticket and return true. Otherwise, return false;\n  bool maybeUpdateFromClosed(\n      const uint64_t state,\n      const uint64_t ticket,\n      uint64_t& offset,\n      Slot*& slots,\n      size_t& cap,\n      int& stride) noexcept {\n    offset = getOffset(state);\n    if (ticket >= offset) {\n      return false;\n    }\n    for (int i = getNumClosed(state) - 1; i >= 0; --i) {\n      offset = closed_[i].offset_;\n      if (offset <= ticket) {\n        slots = closed_[i].slots_;\n        cap = closed_[i].capacity_;\n        stride = closed_[i].stride_;\n        return true;\n      }\n    }\n    // A closed array with offset <= ticket should have been found\n    assert(false);\n    return false;\n  }\n};\n\nnamespace detail {\n\n/// CRTP specialization of MPMCQueueBase\ntemplate <\n    template <\n        typename T,\n        template <typename> class Atom,\n        bool Dynamic,\n        class Allocator>\n\n    class Derived,\n    typename T,\n    template <typename> class Atom,\n    bool Dynamic,\n    class Allocator>\nclass MPMCQueueBase<Derived<T, Atom, Dynamic, Allocator>> {\n  // Note: Using CRTP static casts in several functions of this base\n  // template instead of making called functions virtual or duplicating\n  // the code of calling functions in the derived partially specialized\n  // template\n  using DerivedType = Derived<T, Atom, Dynamic, Allocator>;\n\n  static_assert(std::is_nothrow_move_constructible_v<T>);\n  static_assert(std::is_nothrow_destructible_v<T>);\n\n public:\n  using value_type = T;\n\n  using Slot = detail::SingleElementQueue<T, Atom>;\n\n  explicit MPMCQueueBase(size_t queueCapacity)\n      : capacity_(queueCapacity),\n        dstate_(0),\n        dcapacity_(0),\n        pushTicket_(0),\n        popTicket_(0),\n        pushSpinCutoff_(0),\n        popSpinCutoff_(0) {\n    if (queueCapacity == 0) {\n#if FOLLY_HAS_EXCEPTIONS\n      throw std::invalid_argument(\n          \"MPMCQueue with explicit capacity 0 is impossible\"\n          // Stride computation in derived classes would sigfpe if capacity is 0\n      );\n#endif\n    }\n    FOLLY_SAFE_CHECK(queueCapacity > 0);\n\n    // ideally this would be a static assert, but g++ doesn't allow it\n    assert(\n        alignof(MPMCQueue<T, Atom>) >= hardware_destructive_interference_size);\n    assert(\n        static_cast<uint8_t*>(static_cast<void*>(&popTicket_)) -\n            static_cast<uint8_t*>(static_cast<void*>(&pushTicket_)) >=\n        static_cast<ptrdiff_t>(hardware_destructive_interference_size));\n  }\n\n  /// A default-constructed queue is useful because a usable (non-zero\n  /// capacity) queue can be moved onto it or swapped with it\n  MPMCQueueBase() noexcept\n      : capacity_(0),\n        slots_(nullptr),\n        stride_(0),\n        dstate_(0),\n        dcapacity_(0),\n        pushTicket_(0),\n        popTicket_(0),\n        pushSpinCutoff_(0),\n        popSpinCutoff_(0) {}\n\n  /// IMPORTANT: The move constructor is here to make it easier to perform\n  /// the initialization phase, it is not safe to use when there are any\n  /// concurrent accesses (this is not checked).\n  MPMCQueueBase(MPMCQueueBase<DerivedType>&& rhs) noexcept\n      : capacity_(rhs.capacity_),\n        slots_(rhs.slots_),\n        stride_(rhs.stride_),\n        dstate_(rhs.dstate_.load(std::memory_order_relaxed)),\n        dcapacity_(rhs.dcapacity_.load(std::memory_order_relaxed)),\n        pushTicket_(rhs.pushTicket_.load(std::memory_order_relaxed)),\n        popTicket_(rhs.popTicket_.load(std::memory_order_relaxed)),\n        pushSpinCutoff_(rhs.pushSpinCutoff_.load(std::memory_order_relaxed)),\n        popSpinCutoff_(rhs.popSpinCutoff_.load(std::memory_order_relaxed)) {\n    // relaxed ops are okay for the previous reads, since rhs queue can't\n    // be in concurrent use\n\n    // zero out rhs\n    rhs.capacity_ = 0;\n    rhs.slots_ = nullptr;\n    rhs.stride_ = 0;\n    rhs.dstate_.store(0, std::memory_order_relaxed);\n    rhs.dcapacity_.store(0, std::memory_order_relaxed);\n    rhs.pushTicket_.store(0, std::memory_order_relaxed);\n    rhs.popTicket_.store(0, std::memory_order_relaxed);\n    rhs.pushSpinCutoff_.store(0, std::memory_order_relaxed);\n    rhs.popSpinCutoff_.store(0, std::memory_order_relaxed);\n  }\n\n  MPMCQueueBase& operator=(MPMCQueueBase&& rhs) = delete;\n  MPMCQueueBase(const MPMCQueueBase&) = delete;\n  MPMCQueueBase& operator=(const MPMCQueueBase&) = delete;\n\n  /// MPMCQueue can only be safely destroyed when there are no\n  /// pending enqueuers or dequeuers (this is not checked).\n  ~MPMCQueueBase() { delete[] slots_; }\n\n  /// Returns the number of writes (including threads that are blocked waiting\n  /// to write) minus the number of reads (including threads that are blocked\n  /// waiting to read). So effectively, it becomes:\n  /// elements in queue + pending(calls to write) - pending(calls to read).\n  /// If nothing is pending, then the method returns the actual number of\n  /// elements in the queue.\n  /// The returned value can be negative if there are no writers and the queue\n  /// is empty, but there is one reader that is blocked waiting to read (in\n  /// which case, the returned size will be -1).\n  ssize_t size() const noexcept {\n    // since both pushes and pops increase monotonically, we can get a\n    // consistent snapshot either by bracketing a read of popTicket_ with\n    // two reads of pushTicket_ that return the same value, or the other\n    // way around.  We maximize our chances by alternately attempting\n    // both bracketings.\n    uint64_t pushes = pushTicket_.load(std::memory_order_acquire); // A\n    uint64_t pops = popTicket_.load(std::memory_order_acquire); // B\n    while (true) {\n      uint64_t nextPushes = pushTicket_.load(std::memory_order_acquire); // C\n      if (pushes == nextPushes) {\n        // pushTicket_ didn't change from A (or the previous C) to C,\n        // so we can linearize at B (or D)\n        return ssize_t(pushes - pops);\n      }\n      pushes = nextPushes;\n      uint64_t nextPops = popTicket_.load(std::memory_order_acquire); // D\n      if (pops == nextPops) {\n        // popTicket_ didn't chance from B (or the previous D), so we\n        // can linearize at C\n        return ssize_t(pushes - pops);\n      }\n      pops = nextPops;\n    }\n  }\n\n  /// Returns true if there are no items available for dequeue\n  bool isEmpty() const noexcept { return size() <= 0; }\n\n  /// Returns true if there is currently no empty space to enqueue\n  bool isFull() const noexcept {\n    // careful with signed -> unsigned promotion, since size can be negative\n    return size() >= static_cast<ssize_t>(capacity_);\n  }\n\n  /// Returns is a guess at size() for contexts that don't need a precise\n  /// value, such as stats. More specifically, it returns the number of writes\n  /// minus the number of reads, but after reading the number of writes, more\n  /// writers could have came before the number of reads was sampled,\n  /// and this method doesn't protect against such case.\n  /// The returned value can be negative.\n  ssize_t sizeGuess() const noexcept { return writeCount() - readCount(); }\n\n  /// Doesn't change\n  size_t capacity() const noexcept { return capacity_; }\n\n  /// Doesn't change for non-dynamic\n  size_t allocatedCapacity() const noexcept { return capacity_; }\n\n  /// Returns the total number of calls to blockingWrite or successful\n  /// calls to write, including those blockingWrite calls that are\n  /// currently blocking\n  uint64_t writeCount() const noexcept {\n    return pushTicket_.load(std::memory_order_acquire);\n  }\n\n  /// Returns the total number of calls to blockingRead or successful\n  /// calls to read, including those blockingRead calls that are currently\n  /// blocking\n  uint64_t readCount() const noexcept {\n    return popTicket_.load(std::memory_order_acquire);\n  }\n\n  /// Enqueues a T constructed from args, blocking until space is\n  /// available.  Note that this method signature allows enqueue via\n  /// move, if args is a T rvalue, via copy, if args is a T lvalue, or\n  /// via emplacement if args is an initializer list that can be passed\n  /// to a T constructor.\n  template <typename... Args>\n  void blockingWrite(Args&&... args) noexcept {\n    enqueueWithTicketBase(\n        pushTicket_++, slots_, capacity_, stride_, std::forward<Args>(args)...);\n  }\n\n  /// If an item can be enqueued with no blocking, does so and returns\n  /// true, otherwise returns false.  This method is similar to\n  /// writeIfNotFull, but if you don't have a specific need for that\n  /// method you should use this one.\n  ///\n  /// One of the common usages of this method is to enqueue via the\n  /// move constructor, something like q.write(std::move(x)).  If write\n  /// returns false because the queue is full then x has not actually been\n  /// consumed, which looks strange.  To understand why it is actually okay\n  /// to use x afterward, remember that std::move is just a typecast that\n  /// provides an rvalue reference that enables use of a move constructor\n  /// or operator.  std::move doesn't actually move anything.  It could\n  /// more accurately be called std::rvalue_cast or std::move_permission.\n  template <typename... Args>\n  bool write(Args&&... args) noexcept {\n    uint64_t ticket;\n    Slot* slots;\n    size_t cap;\n    int stride;\n    if (static_cast<DerivedType*>(this)->tryObtainReadyPushTicket(\n\n            ticket, slots, cap, stride)) {\n      // we have pre-validated that the ticket won't block\n      enqueueWithTicketBase(\n          ticket, slots, cap, stride, std::forward<Args>(args)...);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  template <class Clock, typename... Args>\n  bool tryWriteUntil(\n      const std::chrono::time_point<Clock>& when, Args&&... args) noexcept {\n    uint64_t ticket;\n    Slot* slots;\n    size_t cap;\n    int stride;\n    if (tryObtainPromisedPushTicketUntil(ticket, slots, cap, stride, when)) {\n      // we have pre-validated that the ticket won't block, or rather that\n      // it won't block longer than it takes another thread to dequeue an\n      // element from the slot it identifies.\n      enqueueWithTicketBase(\n          ticket, slots, cap, stride, std::forward<Args>(args)...);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  /// If the queue is not full, enqueues and returns true, otherwise\n  /// returns false.  Unlike write this method can be blocked by another\n  /// thread, specifically a read that has linearized (been assigned\n  /// a ticket) but not yet completed.  If you don't really need this\n  /// function you should probably use write.\n  ///\n  /// MPMCQueue isn't lock-free, so just because a read operation has\n  /// linearized (and isFull is false) doesn't mean that space has been\n  /// made available for another write.  In this situation write will\n  /// return false, but writeIfNotFull will wait for the dequeue to finish.\n  /// This method is required if you are composing queues and managing\n  /// your own wakeup, because it guarantees that after every successful\n  /// write a readIfNotEmpty will succeed.\n  template <typename... Args>\n  bool writeIfNotFull(Args&&... args) noexcept {\n    uint64_t ticket;\n    Slot* slots;\n    size_t cap;\n    int stride;\n    if (static_cast<DerivedType*>(this)->tryObtainPromisedPushTicket(\n            ticket, slots, cap, stride)) {\n      // some other thread is already dequeuing the slot into which we\n      // are going to enqueue, but we might have to wait for them to finish\n      enqueueWithTicketBase(\n          ticket, slots, cap, stride, std::forward<Args>(args)...);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  /// Moves a dequeued element onto elem, blocking until an element\n  /// is available\n  void blockingRead(T& elem) noexcept {\n    uint64_t ticket;\n    static_cast<DerivedType*>(this)->blockingReadWithTicket(ticket, elem);\n  }\n\n  /// Same as blockingRead() but also records the ticket nunmer\n  void blockingReadWithTicket(uint64_t& ticket, T& elem) noexcept {\n    assert(capacity_ != 0);\n    ticket = popTicket_++;\n    dequeueWithTicketBase(ticket, slots_, capacity_, stride_, elem);\n  }\n\n  /// If an item can be dequeued with no blocking, does so and returns true,\n  /// otherwise returns false.\n  ///\n  /// Note that if the matching write is still in progress, this may return\n  /// false even if writes that have been started later have already\n  /// completed. If an external mechanism is used for counting completed writes\n  /// (for example a semaphore) to determine when an element is ready to\n  /// dequeue, readIfNotEmpty() should be used instead, which will wait for the\n  /// write in progress.\n  bool read(T& elem) noexcept {\n    uint64_t ticket;\n    return readAndGetTicket(ticket, elem);\n  }\n\n  /// Same as read() but also records the ticket nunmer\n  bool readAndGetTicket(uint64_t& ticket, T& elem) noexcept {\n    Slot* slots;\n    size_t cap;\n    int stride;\n    if (static_cast<DerivedType*>(this)->tryObtainReadyPopTicket(\n\n            ticket, slots, cap, stride)) {\n      // the ticket has been pre-validated to not block\n      dequeueWithTicketBase(ticket, slots, cap, stride, elem);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  template <class Clock, typename... Args>\n  bool tryReadUntil(\n      const std::chrono::time_point<Clock>& when, T& elem) noexcept {\n    uint64_t ticket;\n    Slot* slots;\n    size_t cap;\n    int stride;\n    if (tryObtainPromisedPopTicketUntil(ticket, slots, cap, stride, when)) {\n      // we have pre-validated that the ticket won't block, or rather that\n      // it won't block longer than it takes another thread to enqueue an\n      // element on the slot it identifies.\n      dequeueWithTicketBase(ticket, slots, cap, stride, elem);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  /// If the queue is not empty, dequeues and returns true, otherwise\n  /// returns false.  If the matching write is still in progress then this\n  /// method may block waiting for it.  If you don't rely on being able\n  /// to dequeue (such as by counting completed write) then you should\n  /// prefer read.\n  bool readIfNotEmpty(T& elem) noexcept {\n    uint64_t ticket;\n    Slot* slots;\n    size_t cap;\n    int stride;\n    if (static_cast<DerivedType*>(this)->tryObtainPromisedPopTicket(\n            ticket, slots, cap, stride)) {\n      // the matching enqueue already has a ticket, but might not be done\n      dequeueWithTicketBase(ticket, slots, cap, stride, elem);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n protected:\n  enum {\n    /// Once every kAdaptationFreq we will spin longer, to try to estimate\n    /// the proper spin backoff\n    kAdaptationFreq = 128,\n\n    /// To avoid false sharing in slots_ with neighboring memory\n    /// allocations, we pad it with this many SingleElementQueue-s at\n    /// each end\n    kSlotPadding =\n        (hardware_destructive_interference_size - 1) / sizeof(Slot) + 1\n  };\n\n  /// The maximum number of items in the queue at once\n  alignas(hardware_destructive_interference_size) size_t capacity_;\n\n  /// Anonymous union for use when Dynamic = false and true, respectively\n  union {\n    /// An array of capacity_ SingleElementQueue-s, each of which holds\n    /// either 0 or 1 item.  We over-allocate by 2 * kSlotPadding and don't\n    /// touch the slots at either end, to avoid false sharing\n    Slot* slots_;\n    /// Current dynamic slots array of dcapacity_ SingleElementQueue-s\n    Atom<Slot*> dslots_;\n  };\n\n  /// Anonymous union for use when Dynamic = false and true, respectively\n  union {\n    /// The number of slots_ indices that we advance for each ticket, to\n    /// avoid false sharing.  Ideally slots_[i] and slots_[i + stride_]\n    /// aren't on the same cache line\n    int stride_;\n    /// Current stride\n    Atom<int> dstride_;\n  };\n\n  /// The following two members are used by dynamic MPMCQueue.\n  /// Ideally they should be in MPMCQueue<T,Atom,true>, but we get\n  /// better cache locality if they are in the same cache line as\n  /// dslots_ and dstride_.\n  ///\n  /// Dynamic state. A packed seqlock and ticket offset\n  Atom<uint64_t> dstate_;\n  /// Dynamic capacity\n  Atom<size_t> dcapacity_;\n\n  /// Enqueuers get tickets from here\n  alignas(hardware_destructive_interference_size) Atom<uint64_t> pushTicket_;\n\n  /// Dequeuers get tickets from here\n  alignas(hardware_destructive_interference_size) Atom<uint64_t> popTicket_;\n\n  /// This is how many times we will spin before using FUTEX_WAIT when\n  /// the queue is full on enqueue, adaptively computed by occasionally\n  /// spinning for longer and smoothing with an exponential moving average\n  alignas(\n      hardware_destructive_interference_size) Atom<uint32_t> pushSpinCutoff_;\n\n  /// The adaptive spin cutoff when the queue is empty on dequeue\n  alignas(hardware_destructive_interference_size) Atom<uint32_t> popSpinCutoff_;\n\n  /// Alignment doesn't prevent false sharing at the end of the struct,\n  /// so fill out the last cache line\n  char pad_[hardware_destructive_interference_size - sizeof(Atom<uint32_t>)];\n\n  /// We assign tickets in increasing order, but we don't want to\n  /// access neighboring elements of slots_ because that will lead to\n  /// false sharing (multiple cores accessing the same cache line even\n  /// though they aren't accessing the same bytes in that cache line).\n  /// To avoid this we advance by stride slots per ticket.\n  ///\n  /// We need gcd(capacity, stride) to be 1 so that we will use all\n  /// of the slots.  We ensure this by only considering prime strides,\n  /// which either have no common divisors with capacity or else have\n  /// a zero remainder after dividing by capacity.  That is sufficient\n  /// to guarantee correctness, but we also want to actually spread the\n  /// accesses away from each other to avoid false sharing (consider a\n  /// stride of 7 with a capacity of 8).  To that end we try a few taking\n  /// care to observe that advancing by -1 is as bad as advancing by 1\n  /// when in comes to false sharing.\n  ///\n  /// The simple way to avoid false sharing would be to pad each\n  /// SingleElementQueue, but since we have capacity_ of them that could\n  /// waste a lot of space.\n  static int computeStride(size_t capacity) noexcept {\n    static const int smallPrimes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};\n\n    int bestStride = 1;\n    size_t bestSep = 1;\n    for (int stride : smallPrimes) {\n      if ((stride % capacity) == 0 || (capacity % stride) == 0) {\n        continue;\n      }\n      size_t sep = stride % capacity;\n      sep = std::min(sep, capacity - sep);\n      if (sep > bestSep) {\n        bestStride = stride;\n        bestSep = sep;\n      }\n    }\n    return bestStride;\n  }\n\n  /// Returns the index into slots_ that should be used when enqueuing or\n  /// dequeuing with the specified ticket\n  size_t idx(uint64_t ticket, size_t cap, int stride) noexcept {\n    return ((ticket * stride) % cap) + kSlotPadding;\n  }\n\n  /// Maps an enqueue or dequeue ticket to the turn should be used at the\n  /// corresponding SingleElementQueue\n  uint32_t turn(uint64_t ticket, size_t cap) noexcept {\n    assert(cap != 0);\n    return uint32_t(ticket / cap);\n  }\n\n  /// Tries to obtain a push ticket for which SingleElementQueue::enqueue\n  /// won't block.  Returns true on immediate success, false on immediate\n  /// failure.\n  bool tryObtainReadyPushTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    ticket = pushTicket_.load(std::memory_order_acquire); // A\n    slots = slots_;\n    cap = capacity_;\n    stride = stride_;\n    while (true) {\n      if (!slots[idx(ticket, cap, stride)].mayEnqueue(turn(ticket, cap))) {\n        // if we call enqueue(ticket, ...) on the SingleElementQueue\n        // right now it would block, but this might no longer be the next\n        // ticket.  We can increase the chance of tryEnqueue success under\n        // contention (without blocking) by rechecking the ticket dispenser\n        auto prev = ticket;\n        ticket = pushTicket_.load(std::memory_order_acquire); // B\n        if (prev == ticket) {\n          // mayEnqueue was bracketed by two reads (A or prev B or prev\n          // failing CAS to B), so we are definitely unable to enqueue\n          return false;\n        }\n      } else {\n        // we will bracket the mayEnqueue check with a read (A or prev B\n        // or prev failing CAS) and the following CAS.  If the CAS fails\n        // it will effect a load of pushTicket_\n        if (pushTicket_.compare_exchange_strong(ticket, ticket + 1)) {\n          return true;\n        }\n      }\n    }\n  }\n\n  /// Tries until when to obtain a push ticket for which\n  /// SingleElementQueue::enqueue  won't block.  Returns true on success, false\n  /// on failure.\n  /// ticket is filled on success AND failure.\n  template <class Clock>\n  bool tryObtainPromisedPushTicketUntil(\n      uint64_t& ticket,\n      Slot*& slots,\n      size_t& cap,\n      int& stride,\n      const std::chrono::time_point<Clock>& when) noexcept {\n    bool deadlineReached = false;\n    while (!deadlineReached) {\n      if (static_cast<DerivedType*>(this)->tryObtainPromisedPushTicket(\n              ticket, slots, cap, stride)) {\n        return true;\n      }\n      // ticket is a blocking ticket until the preceding ticket has been\n      // processed: wait until this ticket's turn arrives. We have not reserved\n      // this ticket so we will have to re-attempt to get a non-blocking ticket\n      // if we wake up before we time-out.\n      deadlineReached =\n          !slots[idx(ticket, cap, stride)].tryWaitForEnqueueTurnUntil(\n              turn(ticket, cap),\n              pushSpinCutoff_,\n              (ticket % kAdaptationFreq) == 0,\n              when);\n    }\n    return false;\n  }\n\n  /// Tries to obtain a push ticket which can be satisfied if all\n  /// in-progress pops complete.  This function does not block, but\n  /// blocking may be required when using the returned ticket if some\n  /// other thread's pop is still in progress (ticket has been granted but\n  /// pop has not yet completed).\n  bool tryObtainPromisedPushTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    auto numPushes = pushTicket_.load(std::memory_order_acquire); // A\n    slots = slots_;\n    cap = capacity_;\n    stride = stride_;\n    while (true) {\n      ticket = numPushes;\n      const auto numPops = popTicket_.load(std::memory_order_acquire); // B\n      // n will be negative if pops are pending\n      const int64_t n = int64_t(numPushes - numPops);\n      if (n >= static_cast<ssize_t>(capacity_)) {\n        // Full, linearize at B.  We don't need to recheck the read we\n        // performed at A, because if numPushes was stale at B then the\n        // real numPushes value is even worse\n        return false;\n      }\n      if (pushTicket_.compare_exchange_strong(numPushes, numPushes + 1)) {\n        return true;\n      }\n    }\n  }\n\n  /// Tries to obtain a pop ticket for which SingleElementQueue::dequeue\n  /// won't block.  Returns true on immediate success, false on immediate\n  /// failure.\n  bool tryObtainReadyPopTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    ticket = popTicket_.load(std::memory_order_acquire);\n    slots = slots_;\n    cap = capacity_;\n    stride = stride_;\n    while (true) {\n      if (!slots[idx(ticket, cap, stride)].mayDequeue(turn(ticket, cap))) {\n        auto prev = ticket;\n        ticket = popTicket_.load(std::memory_order_acquire);\n        if (prev == ticket) {\n          return false;\n        }\n      } else {\n        if (popTicket_.compare_exchange_strong(ticket, ticket + 1)) {\n          return true;\n        }\n      }\n    }\n  }\n\n  /// Tries until when to obtain a pop ticket for which\n  /// SingleElementQueue::dequeue won't block.  Returns true on success, false\n  /// on failure.\n  /// ticket is filled on success AND failure.\n  template <class Clock>\n  bool tryObtainPromisedPopTicketUntil(\n      uint64_t& ticket,\n      Slot*& slots,\n      size_t& cap,\n      int& stride,\n      const std::chrono::time_point<Clock>& when) noexcept {\n    bool deadlineReached = false;\n    while (!deadlineReached) {\n      if (static_cast<DerivedType*>(this)->tryObtainPromisedPopTicket(\n              ticket, slots, cap, stride)) {\n        return true;\n      }\n      // ticket is a blocking ticket until the preceding ticket has been\n      // processed: wait until this ticket's turn arrives. We have not reserved\n      // this ticket so we will have to re-attempt to get a non-blocking ticket\n      // if we wake up before we time-out.\n      deadlineReached =\n          !slots[idx(ticket, cap, stride)].tryWaitForDequeueTurnUntil(\n              turn(ticket, cap),\n              pushSpinCutoff_,\n              (ticket % kAdaptationFreq) == 0,\n              when);\n    }\n    return false;\n  }\n\n  /// Similar to tryObtainReadyPopTicket, but returns a pop ticket whose\n  /// corresponding push ticket has already been handed out, rather than\n  /// returning one whose corresponding push ticket has already been\n  /// completed.  This means that there is a possibility that the caller\n  /// will block when using the ticket, but it allows the user to rely on\n  /// the fact that if enqueue has succeeded, tryObtainPromisedPopTicket\n  /// will return true.  The \"try\" part of this is that we won't have\n  /// to block waiting for someone to call enqueue, although we might\n  /// have to block waiting for them to finish executing code inside the\n  /// MPMCQueue itself.\n  bool tryObtainPromisedPopTicket(\n      uint64_t& ticket, Slot*& slots, size_t& cap, int& stride) noexcept {\n    auto numPops = popTicket_.load(std::memory_order_acquire); // A\n    slots = slots_;\n    cap = capacity_;\n    stride = stride_;\n    while (true) {\n      ticket = numPops;\n      const auto numPushes = pushTicket_.load(std::memory_order_acquire); // B\n      if (numPops >= numPushes) {\n        // Empty, or empty with pending pops.  Linearize at B.  We don't\n        // need to recheck the read we performed at A, because if numPops\n        // is stale then the fresh value is larger and the >= is still true\n        return false;\n      }\n      if (popTicket_.compare_exchange_strong(numPops, numPops + 1)) {\n        return true;\n      }\n    }\n  }\n\n  // Given a ticket, constructs an enqueued item using args\n  template <typename... Args>\n  void enqueueWithTicketBase(\n      uint64_t ticket,\n      Slot* slots,\n      size_t cap,\n      int stride,\n      Args&&... args) noexcept {\n    slots[idx(ticket, cap, stride)].enqueue(\n        turn(ticket, cap),\n        pushSpinCutoff_,\n        (ticket % kAdaptationFreq) == 0,\n        std::forward<Args>(args)...);\n  }\n\n  // To support tracking ticket numbers in MPMCPipelineStageImpl\n  template <typename... Args>\n  void enqueueWithTicket(uint64_t ticket, Args&&... args) noexcept {\n    enqueueWithTicketBase(\n        ticket, slots_, capacity_, stride_, std::forward<Args>(args)...);\n  }\n\n  // Given a ticket, dequeues the corresponding element\n  void dequeueWithTicketBase(\n      uint64_t ticket, Slot* slots, size_t cap, int stride, T& elem) noexcept {\n    assert(cap != 0);\n    slots[idx(ticket, cap, stride)].dequeue(\n        turn(ticket, cap),\n        popSpinCutoff_,\n        (ticket % kAdaptationFreq) == 0,\n        elem);\n  }\n};\n\n/// SingleElementQueue implements a blocking queue that holds at most one\n/// item, and that requires its users to assign incrementing identifiers\n/// (turns) to each enqueue and dequeue operation.  Note that the turns\n/// used by SingleElementQueue are doubled inside the TurnSequencer\ntemplate <typename T, template <typename> class Atom>\nstruct SingleElementQueue {\n  ~SingleElementQueue() noexcept {\n    if ((sequencer_.uncompletedTurnLSB() & 1) == 1) {\n      // we are pending a dequeue, so we have a constructed item\n      destroyContents();\n    }\n  }\n\n  /// enqueue using in-place noexcept construction\n  template <typename... Args>\n  void enqueue(\n      const uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      Args&&... args) noexcept {\n    static_assert(std::is_nothrow_constructible_v<T, Args...>);\n    sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);\n    new (&contents_) T(std::forward<Args>(args)...); // noexcept, per above\n    sequencer_.completeTurn(turn * 2);\n  }\n\n  void enqueue(\n      const uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      T&& goner) noexcept {\n    enqueueImpl(turn, spinCutoff, updateSpinCutoff, std::move(goner));\n  }\n\n  /// Waits until either:\n  /// 1: the dequeue turn preceding the given enqueue turn has arrived\n  /// 2: the given deadline has arrived\n  /// Case 1 returns true, case 2 returns false.\n  template <class Clock>\n  bool tryWaitForEnqueueTurnUntil(\n      const uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      const std::chrono::time_point<Clock>& when) noexcept {\n    return sequencer_.tryWaitForTurn(\n               turn * 2, spinCutoff, updateSpinCutoff, &when) !=\n        TurnSequencer<Atom>::TryWaitResult::TIMEDOUT;\n  }\n\n  bool mayEnqueue(const uint32_t turn) const noexcept {\n    return sequencer_.isTurn(turn * 2);\n  }\n\n  void dequeue(\n      uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      T& elem) noexcept {\n    dequeueImpl(turn, spinCutoff, updateSpinCutoff, elem);\n  }\n\n  /// Waits until either:\n  /// 1: the enqueue turn preceding the given dequeue turn has arrived\n  /// 2: the given deadline has arrived\n  /// Case 1 returns true, case 2 returns false.\n  template <class Clock>\n  bool tryWaitForDequeueTurnUntil(\n      const uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      const std::chrono::time_point<Clock>& when) noexcept {\n    return sequencer_.tryWaitForTurn(\n               turn * 2 + 1, spinCutoff, updateSpinCutoff, &when) !=\n        TurnSequencer<Atom>::TryWaitResult::TIMEDOUT;\n  }\n\n  bool mayDequeue(const uint32_t turn) const noexcept {\n    return sequencer_.isTurn(turn * 2 + 1);\n  }\n\n private:\n  /// Storage for a T constructed with placement new\n  aligned_storage_for_t<T> contents_;\n\n  /// Even turns are pushes, odd turns are pops\n  TurnSequencer<Atom> sequencer_;\n\n  T* ptr() noexcept { return static_cast<T*>(static_cast<void*>(&contents_)); }\n\n  void destroyContents() noexcept {\n    ptr()->~T(); // assume noexcept\n    if (kIsDebug) {\n      memset(&contents_, 'Q', sizeof(T));\n    }\n  }\n\n  /// enqueue using nothrow move construction.\n  void enqueueImpl(\n      const uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      T&& goner) noexcept {\n    sequencer_.waitForTurn(turn * 2, spinCutoff, updateSpinCutoff);\n    new (&contents_) T(std::move(goner));\n    sequencer_.completeTurn(turn * 2);\n  }\n\n  /// dequeue by nothrow move assignment.\n  void dequeueImpl(\n      uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      T& elem) noexcept {\n    sequencer_.waitForTurn(turn * 2 + 1, spinCutoff, updateSpinCutoff);\n    elem = std::move(*ptr());\n    destroyContents();\n    sequencer_.completeTurn(turn * 2 + 1);\n  }\n};\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/MacAddress.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/MacAddress.h>\n\n#include <cassert>\n#include <ostream>\n\n#include <folly/Exception.h>\n#include <folly/Format.h>\n#include <folly/IPAddressV6.h>\n#include <folly/String.h>\n\nusing std::invalid_argument;\nusing std::string;\n\nnamespace folly {\n\nconst MacAddress MacAddress::BROADCAST{Endian::big(uint64_t(0xffffffffffffU))};\nconst MacAddress MacAddress::ZERO;\n\nMacAddress::MacAddress(StringPiece str) {\n  memset(&bytes_, 0, 8);\n  parse(str);\n}\n\nMacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {\n  // This method should only be used for multicast addresses.\n  assert(v6addr.isMulticast());\n\n  uint8_t bytes[SIZE];\n  bytes[0] = 0x33;\n  bytes[1] = 0x33;\n  memcpy(bytes + 2, v6addr.bytes() + 12, 4);\n  return fromBinary(ByteRange(bytes, SIZE));\n}\n\nstring MacAddress::toString() const {\n  static const char hexValues[] = \"0123456789abcdef\";\n  string result;\n  result.resize(17);\n  result[0] = hexValues[getByte(0) >> 4];\n  result[1] = hexValues[getByte(0) & 0xf];\n  result[2] = ':';\n  result[3] = hexValues[getByte(1) >> 4];\n  result[4] = hexValues[getByte(1) & 0xf];\n  result[5] = ':';\n  result[6] = hexValues[getByte(2) >> 4];\n  result[7] = hexValues[getByte(2) & 0xf];\n  result[8] = ':';\n  result[9] = hexValues[getByte(3) >> 4];\n  result[10] = hexValues[getByte(3) & 0xf];\n  result[11] = ':';\n  result[12] = hexValues[getByte(4) >> 4];\n  result[13] = hexValues[getByte(4) & 0xf];\n  result[14] = ':';\n  result[15] = hexValues[getByte(5) >> 4];\n  result[16] = hexValues[getByte(5) & 0xf];\n  return result;\n}\n\nExpected<Unit, MacAddressFormatError> MacAddress::trySetFromString(\n    StringPiece value) {\n  return setFromString(value, [](auto _, auto) { return makeUnexpected(_); });\n}\n\nvoid MacAddress::setFromString(StringPiece value) {\n  setFromString(value, [](auto, auto _) { return _(), unit; });\n}\n\nExpected<Unit, MacAddressFormatError> MacAddress::trySetFromBinary(\n    ByteRange value) {\n  return setFromBinary(value, [](auto _, auto) { return makeUnexpected(_); });\n}\n\nvoid MacAddress::setFromBinary(ByteRange value) {\n  setFromBinary(value, [](auto, auto _) { return _(), unit; });\n}\n\ntemplate <typename OnError>\nExpected<Unit, MacAddressFormatError> MacAddress::setFromString(\n    StringPiece str, OnError err) {\n  // Helper function to convert a single hex char into an integer\n  auto isSeparatorChar = [](char c) { return c == ':' || c == '-'; };\n\n  uint8_t parsed[SIZE];\n  auto p = str.begin();\n  for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {\n    if (p == str.end()) {\n      return err(MacAddressFormatError::Invalid, [&] {\n        throw invalid_argument(\n            sformat(\"invalid MAC address '{}': not enough digits\", str));\n      });\n    }\n\n    // Skip over ':' or '-' separators between bytes\n    if (byteIndex != 0 && isSeparatorChar(*p)) {\n      ++p;\n      if (p == str.end()) {\n        return err(MacAddressFormatError::Invalid, [&] {\n          throw invalid_argument(\n              sformat(\"invalid MAC address '{}': not enough digits\", str));\n        });\n      }\n    }\n\n    // Parse the upper nibble\n    uint8_t upper = detail::hexTable[static_cast<uint8_t>(*p)];\n    if (upper & 0x10) {\n      return err(MacAddressFormatError::Invalid, [&] {\n        throw invalid_argument(\n            sformat(\"invalid MAC address '{}': contains non-hex digit\", str));\n      });\n    }\n    ++p;\n\n    // Parse the lower nibble\n    uint8_t lower;\n    if (p == str.end()) {\n      lower = upper;\n      upper = 0;\n    } else {\n      lower = detail::hexTable[static_cast<uint8_t>(*p)];\n      if (lower & 0x10) {\n        // Also accept ':', '-', or '\\0', to handle the case where one\n        // of the bytes was represented by just a single digit.\n        if (isSeparatorChar(*p)) {\n          lower = upper;\n          upper = 0;\n        } else {\n          return err(MacAddressFormatError::Invalid, [&] {\n            throw invalid_argument(sformat(\n                \"invalid MAC address '{}': contains non-hex digit\", str));\n          });\n        }\n      }\n      ++p;\n    }\n\n    // Update parsed with the newly parsed byte\n    parsed[byteIndex] = (upper << 4) | lower;\n  }\n\n  if (p != str.end()) {\n    // String is too long to be a MAC address\n    return err(MacAddressFormatError::Invalid, [&] {\n      throw invalid_argument(\n          sformat(\"invalid MAC address '{}': found trailing characters\", str));\n    });\n  }\n\n  // Only update now that we have successfully parsed the entire\n  // string.  This way we remain unchanged on error.\n  return setFromBinary(ByteRange(parsed, SIZE), err);\n}\n\ntemplate <typename OnError>\nExpected<Unit, MacAddressFormatError> MacAddress::setFromBinary(\n    ByteRange value, OnError err) {\n  if (value.size() != SIZE) {\n    return err(MacAddressFormatError::Invalid, [&] {\n      throw invalid_argument(\n          sformat(\"MAC address must be 6 bytes long, got \", value.size()));\n    });\n  }\n  memcpy(bytes_ + 2, value.begin(), SIZE);\n  return unit;\n}\n\nstd::ostream& operator<<(std::ostream& os, MacAddress address) {\n  os << address.toString();\n  return os;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/MacAddress.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <iosfwd>\n\n#include <folly/Conv.h>\n#include <folly/Expected.h>\n#include <folly/Range.h>\n#include <folly/Unit.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\n\nclass IPAddressV6;\n\nenum class MacAddressFormatError {\n  Invalid,\n};\n\n/*\n * MacAddress represents an IEEE 802 MAC address.\n */\nclass MacAddress {\n public:\n  static constexpr size_t SIZE = 6;\n  static const MacAddress BROADCAST;\n  static const MacAddress ZERO;\n\n  /*\n   * Construct a zero-initialized MacAddress.\n   */\n  MacAddress() { memset(&bytes_, 0, 8); }\n\n  /*\n   * Parse a MacAddress from a human-readable string.\n   * The string must contain 6 one- or two-digit hexadecimal\n   * numbers, separated by dashes or colons.\n   * Examples: 00:02:C9:C8:F9:68 or 0-2-c9-c8-f9-68\n   */\n  explicit MacAddress(StringPiece str);\n\n  static Expected<MacAddress, MacAddressFormatError> tryFromString(\n      StringPiece value) {\n    MacAddress ret;\n    auto ok = ret.trySetFromString(value);\n    if (!ok) {\n      return makeUnexpected(ok.error());\n    }\n    return ret;\n  }\n  static MacAddress fromString(StringPiece value) {\n    MacAddress ret;\n    ret.setFromString(value);\n    return ret;\n  }\n\n  /*\n   * Construct a MAC address from its 6-byte binary value\n   */\n  static Expected<MacAddress, MacAddressFormatError> tryFromBinary(\n      ByteRange value) {\n    MacAddress ret;\n    auto ok = ret.trySetFromBinary(value);\n    if (!ok) {\n      return makeUnexpected(ok.error());\n    }\n    return ret;\n  }\n  static MacAddress fromBinary(ByteRange value) {\n    MacAddress ret;\n    ret.setFromBinary(value);\n    return ret;\n  }\n\n  /*\n   * Construct a MacAddress from a uint64_t in network byte order.\n   *\n   * The first two bytes are ignored, and the MAC address is taken from the\n   * latter 6 bytes.\n   *\n   * This is a static method rather than a constructor to avoid confusion\n   * between host and network byte order constructors.\n   */\n  static MacAddress fromNBO(uint64_t value) { return MacAddress(value); }\n\n  /*\n   * Construct a MacAddress from a uint64_t in host byte order.\n   *\n   * The most significant two bytes are ignored, and the MAC address is taken\n   * from the least significant 6 bytes.\n   *\n   * This is a static method rather than a constructor to avoid confusion\n   * between host and network byte order constructors.\n   */\n  static MacAddress fromHBO(uint64_t value) {\n    return MacAddress(Endian::big(value));\n  }\n\n  /*\n   * Construct the multicast MacAddress for the specified multicast IPv6\n   * address.\n   */\n  static MacAddress createMulticast(IPAddressV6 addr);\n\n  /*\n   * Get a pointer to the MAC address' binary value.\n   *\n   * The returned value points to internal storage inside the MacAddress\n   * object.  It is only valid as long as the MacAddress, and its contents may\n   * change if the MacAddress is updated.\n   */\n  const uint8_t* bytes() const { return bytes_ + 2; }\n\n  /*\n   * Return the address as a uint64_t, in network byte order.\n   *\n   * The first two bytes will be 0, and the subsequent 6 bytes will contain\n   * the address in network byte order.\n   */\n  uint64_t u64NBO() const { return packedBytes(); }\n\n  /*\n   * Return the address as a uint64_t, in host byte order.\n   *\n   * The two most significant bytes will be 0, and the remaining 6 bytes will\n   * contain the address.  The most significant of these 6 bytes will contain\n   * the first byte that appear on the wire, and the least significant byte\n   * will contain the last byte.\n   */\n  uint64_t u64HBO() const {\n    // Endian::big() does what we want here, even though we are converting\n    // from big-endian to host byte order.  This swaps if and only if\n    // the host byte order is little endian.\n    return Endian::big(packedBytes());\n  }\n\n  /*\n   * Return a human-readable representation of the MAC address.\n   */\n  std::string toString() const;\n\n  /*\n   * Update the current MacAddress object from a human-readable string.\n   */\n  Expected<Unit, MacAddressFormatError> trySetFromString(StringPiece value);\n  void setFromString(StringPiece value);\n  void parse(StringPiece str) { setFromString(str); }\n\n  /*\n   * Update the current MacAddress object from a 6-byte binary representation.\n   */\n  Expected<Unit, MacAddressFormatError> trySetFromBinary(ByteRange value);\n  void setFromBinary(ByteRange value);\n\n  bool isBroadcast() const { return *this == BROADCAST; }\n  bool isMulticast() const { return getByte(0) & 0x1; }\n  bool isUnicast() const { return !isMulticast(); }\n\n  /*\n   * Return true if this MAC address is locally administered.\n   *\n   * Locally administered addresses are assigned by the local network\n   * administrator, and are not guaranteed to be globally unique.  (It is\n   * similar to IPv4's private address space.)\n   *\n   * Note that isLocallyAdministered() will return true for the broadcast\n   * address, since it has the locally administered bit set.\n   */\n  bool isLocallyAdministered() const { return getByte(0) & 0x2; }\n\n  // Comparison operators.\n\n  bool operator==(const MacAddress& other) const {\n    // All constructors and modifying methods make sure padding is 0,\n    // so we don't need to mask these bytes out when comparing here.\n    return packedBytes() == other.packedBytes();\n  }\n\n  bool operator<(const MacAddress& other) const {\n    return u64HBO() < other.u64HBO();\n  }\n\n  bool operator!=(const MacAddress& other) const { return !(*this == other); }\n\n  bool operator>(const MacAddress& other) const { return other < *this; }\n\n  bool operator>=(const MacAddress& other) const { return !(*this < other); }\n\n  bool operator<=(const MacAddress& other) const { return !(*this > other); }\n\n private:\n  explicit MacAddress(uint64_t valueNBO) {\n    memcpy(&bytes_, &valueNBO, 8);\n    // Set the pad bytes to 0.\n    // This allows us to easily compare two MacAddresses,\n    // without having to worry about differences in the padding.\n    bytes_[0] = 0;\n    bytes_[1] = 0;\n  }\n\n  template <typename OnError>\n  Expected<Unit, MacAddressFormatError> setFromString(\n      StringPiece value, OnError err);\n\n  template <typename OnError>\n  Expected<Unit, MacAddressFormatError> setFromBinary(\n      ByteRange value, OnError err);\n\n  /* We store the 6 bytes starting at bytes_[2] (most significant)\n     through bytes_[7] (least).\n     bytes_[0] and bytes_[1] are always equal to 0 to simplify comparisons.\n  */\n  unsigned char bytes_[8];\n\n  inline uint64_t getByte(size_t index) const { return bytes_[index + 2]; }\n\n  uint64_t packedBytes() const {\n    uint64_t u64;\n    memcpy(&u64, bytes_, 8);\n    return u64;\n  }\n};\n\n/* Define toAppend() so to<string> will work */\ntemplate <class Tgt>\ntypename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(\n    MacAddress address, Tgt* result) {\n  toAppend(address.toString(), result);\n}\n\nstd::ostream& operator<<(std::ostream& os, MacAddress address);\n\n} // namespace folly\n\nnamespace std {\n\n// Provide an implementation for std::hash<MacAddress>\ntemplate <>\nstruct hash<folly::MacAddress> {\n  size_t operator()(const folly::MacAddress& address) const {\n    return std::hash<uint64_t>()(address.u64HBO());\n  }\n};\n\n} // namespace std\n"
  },
  {
    "path": "folly/MapUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/MapUtil.h> // @shim\n"
  },
  {
    "path": "folly/Math.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Some arithmetic functions that seem to pop up or get hand-rolled a lot.\n * So far they are all focused on integer division.\n */\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <limits>\n#include <type_traits>\n\nnamespace folly {\n\nnamespace detail {\n\ntemplate <typename T>\ninline constexpr T divFloorBranchless(T num, T denom) {\n  // floor != trunc when the answer isn't exact and truncation went the\n  // wrong way (truncation went toward positive infinity).  That happens\n  // when the true answer is negative, which happens when num and denom\n  // have different signs.  The following code compiles branch-free on\n  // many platforms.\n  return (num / denom) +\n      ((num % denom) != 0 ? 1 : 0) *\n      (std::is_signed<T>::value && (num ^ denom) < 0 ? -1 : 0);\n}\n\ntemplate <typename T>\ninline constexpr T divFloorBranchful(T num, T denom) {\n  // First case handles negative result by preconditioning numerator.\n  // Preconditioning decreases the magnitude of the numerator, which is\n  // itself sign-dependent.  Second case handles zero or positive rational\n  // result, where trunc and floor are the same.\n  return std::is_signed<T>::value && (num ^ denom) < 0 && num != 0\n      ? (num + (num > 0 ? -1 : 1)) / denom - 1\n      : num / denom;\n}\n\ntemplate <typename T>\ninline constexpr T divCeilBranchless(T num, T denom) {\n  // ceil != trunc when the answer isn't exact (truncation occurred)\n  // and truncation went away from positive infinity.  That happens when\n  // the true answer is positive, which happens when num and denom have\n  // the same sign.\n  return (num / denom) +\n      ((num % denom) != 0 ? 1 : 0) *\n      (std::is_signed<T>::value && (num ^ denom) < 0 ? 0 : 1);\n}\n\ntemplate <typename T>\ninline constexpr T divCeilBranchful(T num, T denom) {\n  // First case handles negative or zero rational result, where trunc and ceil\n  // are the same.\n  // Second case handles positive result by preconditioning numerator.\n  // Preconditioning decreases the magnitude of the numerator, which is\n  // itself sign-dependent.\n  return (std::is_signed<T>::value && (num ^ denom) < 0) || num == 0\n      ? num / denom\n      : (num + (num > 0 ? -1 : 1)) / denom + 1;\n}\n\ntemplate <typename T>\ninline constexpr T divRoundAwayBranchless(T num, T denom) {\n  // away != trunc whenever truncation actually occurred, which is when\n  // there is a non-zero remainder.  If the unrounded result is negative\n  // then fixup moves it toward negative infinity.  If the unrounded\n  // result is positive then adjustment makes it larger.\n  return (num / denom) +\n      ((num % denom) != 0 ? 1 : 0) *\n      (std::is_signed<T>::value && (num ^ denom) < 0 ? -1 : 1);\n}\n\ntemplate <typename T>\ninline constexpr T divRoundAwayBranchful(T num, T denom) {\n  // First case of second ternary operator handles negative rational\n  // result, which is the same as divFloor.  Second case of second ternary\n  // operator handles positive result, which is the same as divCeil.\n  // Zero case is separated for simplicity.\n  return num == 0\n      ? 0\n      : (num + (num > 0 ? -1 : 1)) / denom +\n          (std::is_signed<T>::value && (num ^ denom) < 0 ? -1 : 1);\n}\n\ntemplate <typename N, typename D>\nusing IdivResultType = typename std::enable_if<\n    std::is_integral<N>::value && std::is_integral<D>::value &&\n        !std::is_same<N, bool>::value && !std::is_same<D, bool>::value,\n    decltype(N{1} / D{1})>::type;\n} // namespace detail\n\n#if defined(__arm__) && !FOLLY_AARCH64\nconstexpr auto kIntegerDivisionGivesRemainder = false;\n#else\nconstexpr auto kIntegerDivisionGivesRemainder = true;\n#endif\n\n/**\n * Returns num/denom, rounded toward negative infinity.  Put another way,\n * returns the largest integral value that is less than or equal to the\n * exact (not rounded) fraction num/denom.\n *\n * The matching remainder (num - divFloor(num, denom) * denom) can be\n * negative only if denom is negative, unlike in truncating division.\n * Note that for unsigned types this is the same as the normal integer\n * division operator.  divFloor is equivalent to python's integral division\n * operator //.\n *\n * This function undergoes the same integer promotion rules as a\n * built-in operator, except that we don't allow bool -> int promotion.\n * This function is undefined if denom == 0.  It is also undefined if the\n * result type T is a signed type, num is std::numeric_limits<T>::min(),\n * and denom is equal to -1 after conversion to the result type.\n */\ntemplate <typename N, typename D>\ninline constexpr detail::IdivResultType<N, D> divFloor(N num, D denom) {\n  using R = decltype(num / denom);\n  return detail::IdivResultType<N, D>(\n      kIntegerDivisionGivesRemainder && std::is_signed<R>::value\n          ? detail::divFloorBranchless<R>(num, denom)\n          : detail::divFloorBranchful<R>(num, denom));\n}\n\n/**\n * Returns num/denom, rounded toward positive infinity.  Put another way,\n * returns the smallest integral value that is greater than or equal to\n * the exact (not rounded) fraction num/denom.\n *\n * This function undergoes the same integer promotion rules as a\n * built-in operator, except that we don't allow bool -> int promotion.\n * This function is undefined if denom == 0.  It is also undefined if the\n * result type T is a signed type, num is std::numeric_limits<T>::min(),\n * and denom is equal to -1 after conversion to the result type.\n */\ntemplate <typename N, typename D>\ninline constexpr detail::IdivResultType<N, D> divCeil(N num, D denom) {\n  using R = decltype(num / denom);\n  return detail::IdivResultType<N, D>(\n      kIntegerDivisionGivesRemainder && std::is_signed<R>::value\n          ? detail::divCeilBranchless<R>(num, denom)\n          : detail::divCeilBranchful<R>(num, denom));\n}\n\n/**\n * Returns num/denom, rounded toward zero.  If num and denom are non-zero\n * and have different signs (so the unrounded fraction num/denom is\n * negative), returns divCeil, otherwise returns divFloor.  If T is an\n * unsigned type then this is always equal to divFloor.\n *\n * Note that this is the same as the normal integer division operator,\n * at least since C99 (before then the rounding for negative results was\n * implementation defined).  This function is here for completeness and\n * as a place to hang this comment.\n *\n * This function undergoes the same integer promotion rules as a\n * built-in operator, except that we don't allow bool -> int promotion.\n * This function is undefined if denom == 0.  It is also undefined if the\n * result type T is a signed type, num is std::numeric_limits<T>::min(),\n * and denom is equal to -1 after conversion to the result type.\n */\ntemplate <typename N, typename D>\ninline constexpr detail::IdivResultType<N, D> divTrunc(N num, D denom) {\n  return detail::IdivResultType<N, D>(num / denom);\n}\n\n/**\n * Returns num/denom, rounded away from zero.  If num and denom are\n * non-zero and have different signs (so the unrounded fraction num/denom\n * is negative), returns divFloor, otherwise returns divCeil.  If T is\n * an unsigned type then this is always equal to divCeil.\n *\n * This function undergoes the same integer promotion rules as a\n * built-in operator, except that we don't allow bool -> int promotion.\n * This function is undefined if denom == 0.  It is also undefined if the\n * result type T is a signed type, num is std::numeric_limits<T>::min(),\n * and denom is equal to -1 after conversion to the result type.\n */\ntemplate <typename N, typename D>\ninline constexpr detail::IdivResultType<N, D> divRoundAway(N num, D denom) {\n  using R = decltype(num / denom);\n  return detail::IdivResultType<N, D>(\n      kIntegerDivisionGivesRemainder && std::is_signed<R>::value\n          ? detail::divRoundAwayBranchless<R>(num, denom)\n          : detail::divRoundAwayBranchful<R>(num, denom));\n}\n\n// clang-format off\n// Disabling clang-formatting for midpoint to retain 1:1 correlation\n// with LLVM\n\n//  midpoint\n//\n//  mimic: std::numeric::midpoint, C++20\n//  from:\n//  https://github.com/llvm/llvm-project/blob/llvmorg-11.0.0/libcxx/include/numeric,\n//  Apache 2.0 with LLVM exceptions\n\ntemplate <class _Tp>\nconstexpr std::enable_if_t<\n    std::is_integral<_Tp>::value && !std::is_same<bool, _Tp>::value &&\n        !std::is_null_pointer<_Tp>::value,\n    _Tp>\nmidpoint(_Tp __a, _Tp __b) noexcept {\n  using _Up = std::make_unsigned_t<_Tp>;\n  constexpr _Up __bitshift = std::numeric_limits<_Up>::digits - 1;\n\n  _Up __diff = _Up(__b) - _Up(__a);\n  _Up __sign_bit = __b < __a;\n\n  _Up __half_diff = (__diff / 2) + (__sign_bit << __bitshift) + (__sign_bit & __diff);\n\n  return __a + __half_diff;\n}\n\ntemplate <class _TPtr>\nconstexpr std::enable_if_t<\n    std::is_pointer<_TPtr>::value &&\n        std::is_object<std::remove_pointer_t<_TPtr>>::value &&\n        !std::is_void<std::remove_pointer_t<_TPtr>>::value &&\n        (sizeof(std::remove_pointer_t<_TPtr>) > 0),\n    _TPtr>\nmidpoint(_TPtr __a, _TPtr __b) noexcept {\n  return __a + midpoint(std::ptrdiff_t(0), __b - __a);\n}\n\ntemplate <class _Fp>\nconstexpr std::enable_if_t<std::is_floating_point<_Fp>::value, _Fp> midpoint(\n    _Fp __a,\n    _Fp __b) noexcept {\n  constexpr _Fp __lo = std::numeric_limits<_Fp>::min()*2;\n  constexpr _Fp __hi = std::numeric_limits<_Fp>::max()/2;\n  return std::abs(__a) <= __hi && std::abs(__b) <= __hi ?  // typical case: overflow is impossible\n    (__a + __b)/2 :                                        // always correctly rounded\n    std::abs(__a) < __lo ? __a + __b/2 :                   // not safe to halve a\n    std::abs(__b) < __lo ? __a/2 + __b :                   // not safe to halve b\n    __a/2 + __b/2;                                         // otherwise correctly rounded\n}\n\n// clang-format on\n\n} // namespace folly\n"
  },
  {
    "path": "folly/MaybeManagedPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\nnamespace folly {\n\n/**\n * MaybeManagedPtr stores either a raw pointer or a shared_ptr. It provides\n * normal pointer operations on the underlying raw pointer/shared_ptr.\n *\n * When storing a raw pointer, MaybeManagedPtr does not manage the pointer's\n * lifetime, i.e. never calls `free` on the pointer.\n *\n * When storing a shared_ptr, MaybeManagedPtr will release the shared_ptr\n * upon its own destruction.\n */\ntemplate <typename T>\nclass MaybeManagedPtr {\n public:\n  /* implicit */ MaybeManagedPtr(T* t)\n      : t_(std::shared_ptr<T>(std::shared_ptr<void>(), t)) {}\n  /* implicit */ MaybeManagedPtr(std::shared_ptr<T> t) : t_(std::move(t)) {}\n\n  /**\n   * Construction from shared_ptr<X>, where X is derived from T.\n   */\n  template <typename Q, typename = std::enable_if_t<std::is_base_of_v<T, Q>>>\n  /* implicit */ MaybeManagedPtr(std::shared_ptr<Q> q) : t_(std::move(q)) {}\n\n  /**\n   * Get pointer to the element contained in MaybeManagedPtr.\n   *\n   * @return             Pointer to the element contained in MaybeManagedPtr.\n   */\n  [[nodiscard]] T* get() const { return t_.get(); }\n\n  /**\n   * Return use count of the underlying shared pointer.\n   *\n   * @return             Use count of the underlying shared pointer.\n   */\n  [[nodiscard]] long useCount() const { return t_.use_count(); }\n\n  /**\n   * Member of pointer operator\n   *\n   * @return             Pointer to the element contained in MaybeManagedPtr.\n   */\n  constexpr T* operator->() const { return t_.get(); }\n\n  /**\n   * Indirection operator\n   *\n   * @return             Reference to the element contained in MaybeManagedPtr.\n   */\n  constexpr T& operator*() const& { return *t_.get(); }\n\n  /**\n   * Boolean type conversion operator\n   *\n   * @return             Returns true if the underlying shared pointer is not\n   * null.\n   */\n  operator bool() const { return (t_.get() != nullptr); }\n\n  /**\n   * Boolean equal to operator\n   *\n   * @return             Returns true if the underlying shared pointer is equal\n   * to rhs.\n   */\n  bool operator==(T* rhs) const { return t_.get() == rhs; }\n\n  /**\n   * Boolean equal to operator\n   *\n   * @return             Returns true if the underlying shared pointer is equal\n   * to rhs.\n   */\n  bool operator==(const std::shared_ptr<T>& rhs) const { return t_ == rhs; }\n\n  /**\n   * Boolean not equal to operator\n   *\n   * @return             Returns true if the underlying shared pointer is not\n   * equal to rhs.\n   */\n  bool operator!=(T* rhs) const { return !(t_.get() == rhs); }\n\n  /**\n   * Boolean not equal to operator\n   *\n   * @return             Returns true if the underlying shared pointer is not\n   * equal to rhs.\n   */\n  bool operator!=(const std::shared_ptr<T>& rhs) const { return !(t_ == rhs); }\n\n  /**\n   * Pointer type conversion operator\n   *\n   * @return             Returns a pointer to the element contained in\n   * MaybeManagedPtr.\n   */\n  operator T*() const { return t_.get(); }\n\n private:\n  std::shared_ptr<T> t_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Memory.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cerrno>\n#include <cstddef>\n#include <cstdlib>\n#include <exception>\n#include <limits>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Thunk.h>\n#include <folly/memory/Malloc.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/Constexpr.h>\n#include <folly/portability/Malloc.h>\n\nnamespace folly {\n\nnamespace access {\n\n/// to_address_fn\n/// to_address\n///\n/// mimic: std::to_address (C++20)\n///\n/// adapted from: https://en.cppreference.com/w/cpp/memory/to_address, CC-BY-SA\nstruct to_address_fn {\n private:\n  template <template <typename...> typename T, typename A, typename... B>\n  static tag_t<A> get_first_arg(tag_t<T<A, B...>>);\n  template <typename T>\n  using first_arg_of = type_list_element_t<0, decltype(get_first_arg(tag<T>))>;\n  template <typename T>\n  using detect_element_type = typename T::element_type;\n  template <typename T>\n  using element_type_of =\n      detected_or_t<first_arg_of<T>, detect_element_type, T>;\n\n  template <typename T>\n  using detect_to_address =\n      decltype(std::pointer_traits<T>::to_address(FOLLY_DECLVAL(T const&)));\n\n  template <typename T>\n  static inline constexpr bool use_pointer_traits_to_address = Conjunction<\n      is_detected<element_type_of, T>,\n      is_detected<detect_to_address, T>>::value;\n\n public:\n  template <typename T>\n  constexpr T* operator()(T* p) const noexcept {\n    static_assert(!std::is_function_v<T>);\n    return p;\n  }\n\n  template <typename T>\n  constexpr auto operator()(T const& p) const noexcept {\n    if constexpr (use_pointer_traits_to_address<T>) {\n      static_assert(noexcept(std::pointer_traits<T>::to_address(p)));\n      return std::pointer_traits<T>::to_address(p);\n    } else {\n      static_assert(noexcept(operator()(p.operator->())));\n      return operator()(p.operator->());\n    }\n  }\n};\ninline constexpr to_address_fn to_address;\n\n} // namespace access\n\n#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \\\n    (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) ||         \\\n    (defined(__ANDROID__) && (__ANDROID_API__ > 16)) ||         \\\n    (defined(__APPLE__)) || defined(__FreeBSD__) || defined(__wasm32__)\n\ninline void* aligned_malloc(size_t size, size_t align) {\n  // use posix_memalign, but mimic the behaviour of memalign\n  void* ptr = nullptr;\n  int rc = posix_memalign(&ptr, align, size);\n  return rc == 0 ? (errno = 0, ptr) : (errno = rc, nullptr);\n}\n\ninline void aligned_free(void* aligned_ptr) {\n  free(aligned_ptr);\n}\n\n#elif defined(_WIN32)\n\ninline void* aligned_malloc(size_t size, size_t align) {\n  return _aligned_malloc(size, align);\n}\n\ninline void aligned_free(void* aligned_ptr) {\n  _aligned_free(aligned_ptr);\n}\n\n#else\n\ninline void* aligned_malloc(size_t size, size_t align) {\n  return memalign(align, size);\n}\n\ninline void aligned_free(void* aligned_ptr) {\n  free(aligned_ptr);\n}\n\n#endif\n\nnamespace detail {\ntemplate <typename Alloc, size_t kAlign, bool kAllocate>\nvoid rawOverAlignedImpl(Alloc const& alloc, size_t n, void*& raw) {\n  static_assert((kAlign & (kAlign - 1)) == 0, \"Align must be a power of 2\");\n\n  using AllocTraits = std::allocator_traits<Alloc>;\n  using T = typename AllocTraits::value_type;\n\n  constexpr bool kCanBypass = std::is_same<Alloc, std::allocator<T>>::value;\n\n  // BaseType is a type that gives us as much alignment as we need if\n  // we can get it naturally, otherwise it is aligned as max_align_t.\n  // kBaseAlign is both the alignment and size of this type.\n  constexpr size_t kBaseAlign = constexpr_min(kAlign, alignof(max_align_t));\n  using BaseType = folly::aligned_storage_t<kBaseAlign, kBaseAlign>;\n  using BaseAllocTraits =\n      typename AllocTraits::template rebind_traits<BaseType>;\n  using BaseAlloc = typename BaseAllocTraits::allocator_type;\n  static_assert(\n      sizeof(BaseType) == kBaseAlign && alignof(BaseType) == kBaseAlign);\n\n#if defined(__cpp_sized_deallocation)\n  if (kCanBypass && kAlign == kBaseAlign) {\n    // until std::allocator uses sized deallocation, it is worth the\n    // effort to bypass it when we are able\n    if (kAllocate) {\n      raw = ::operator new(n * sizeof(T));\n    } else {\n      ::operator delete(raw, n * sizeof(T));\n    }\n    return;\n  }\n#endif\n\n  if (kCanBypass && kAlign > kBaseAlign) {\n    // allocating as BaseType isn't sufficient to get alignment, but\n    // since we can bypass Alloc we can use something like posix_memalign.\n    if (kAllocate) {\n      raw = aligned_malloc(n * sizeof(T), kAlign);\n    } else {\n      aligned_free(raw);\n    }\n    return;\n  }\n\n  // we're not allowed to bypass Alloc, or we don't want to\n  BaseAlloc a(alloc);\n\n  // allocation size is counted in sizeof(BaseType)\n  size_t quanta = (n * sizeof(T) + kBaseAlign - 1) / sizeof(BaseType);\n  if (kAlign <= kBaseAlign) {\n    // rebinding Alloc to BaseType is sufficient to get us the alignment\n    // we want, happy path\n    if (kAllocate) {\n      raw = static_cast<void*>(\n          std::addressof(*BaseAllocTraits::allocate(a, quanta)));\n    } else {\n      BaseAllocTraits::deallocate(\n          a,\n          std::pointer_traits<typename BaseAllocTraits::pointer>::pointer_to(\n              *static_cast<BaseType*>(raw)),\n          quanta);\n    }\n    return;\n  }\n\n  // Overaligned and custom allocator, our only option is to\n  // overallocate and store a delta to the actual allocation just\n  // before the returned ptr.\n  //\n  // If we give ourselves kAlign extra bytes, then since\n  // sizeof(BaseType) divides kAlign we can meet alignment while\n  // getting a prefix of one BaseType.  If we happen to get a\n  // kAlign-aligned block, then we can return a pointer to underlying\n  // + kAlign, otherwise there will be at least kBaseAlign bytes in\n  // the unused prefix of the first kAlign-aligned block.\n  if (kAllocate) {\n    char* base = reinterpret_cast<char*>(std::addressof(\n        *BaseAllocTraits::allocate(a, quanta + kAlign / sizeof(BaseType))));\n    size_t byteDelta =\n        kAlign - (reinterpret_cast<uintptr_t>(base) & (kAlign - 1));\n    raw = static_cast<void*>(base + byteDelta);\n    static_cast<size_t*>(raw)[-1] = byteDelta;\n  } else {\n    size_t byteDelta = static_cast<size_t*>(raw)[-1];\n    char* base = static_cast<char*>(raw) - byteDelta;\n    BaseAllocTraits::deallocate(\n        a,\n        std::pointer_traits<typename BaseAllocTraits::pointer>::pointer_to(\n            *reinterpret_cast<BaseType*>(base)),\n        quanta + kAlign / sizeof(BaseType));\n  }\n}\n} // namespace detail\n\n// Works like std::allocator_traits<Alloc>::allocate, but handles\n// over-aligned types.  Feel free to manually specify any power of two as\n// the Align template arg.  Must be matched with deallocateOverAligned.\n// allocationBytesForOverAligned will give you the number of bytes that\n// this function actually requests.\ntemplate <\n    typename Alloc,\n    size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)>\ntypename std::allocator_traits<Alloc>::pointer allocateOverAligned(\n    Alloc const& alloc, size_t n) {\n  void* raw = nullptr;\n  detail::rawOverAlignedImpl<Alloc, kAlign, true>(alloc, n, raw);\n  return std::pointer_traits<typename std::allocator_traits<Alloc>::pointer>::\n      pointer_to(\n          *static_cast<typename std::allocator_traits<Alloc>::value_type*>(\n              raw));\n}\n\ntemplate <\n    typename Alloc,\n    size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)>\nvoid deallocateOverAligned(\n    Alloc const& alloc,\n    typename std::allocator_traits<Alloc>::pointer ptr,\n    size_t n) {\n  void* raw = static_cast<void*>(std::addressof(*ptr));\n  detail::rawOverAlignedImpl<Alloc, kAlign, false>(alloc, n, raw);\n}\n\ntemplate <\n    typename Alloc,\n    size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)>\nsize_t allocationBytesForOverAligned(size_t n) {\n  static_assert((kAlign & (kAlign - 1)) == 0, \"Align must be a power of 2\");\n\n  using AllocTraits = std::allocator_traits<Alloc>;\n  using T = typename AllocTraits::value_type;\n\n  constexpr size_t kBaseAlign = constexpr_min(kAlign, alignof(max_align_t));\n\n  if (kAlign > kBaseAlign && std::is_same<Alloc, std::allocator<T>>::value) {\n    return n * sizeof(T);\n  } else {\n    size_t quanta = (n * sizeof(T) + kBaseAlign - 1) / kBaseAlign;\n    if (kAlign > kBaseAlign) {\n      quanta += kAlign / kBaseAlign;\n    }\n    return quanta * kBaseAlign;\n  }\n}\n\n/**\n * static_function_deleter\n *\n * So you can write this:\n *\n *      using RSA_deleter = folly::static_function_deleter<RSA, &RSA_free>;\n *      auto rsa = std::unique_ptr<RSA, RSA_deleter>(RSA_new());\n *      RSA_generate_key_ex(rsa.get(), bits, exponent, nullptr);\n *      rsa = nullptr;  // calls RSA_free(rsa.get())\n *\n * This would be sweet as well for BIO, but unfortunately BIO_free has signature\n * int(BIO*) while we require signature void(BIO*). So you would need to make a\n * wrapper for it:\n *\n *      inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); }\n *      using BIO_deleter = folly::static_function_deleter<BIO, &BIO_free_fb>;\n *      auto buf = std::unique_ptr<BIO, BIO_deleter>(BIO_new(BIO_s_mem()));\n *      buf = nullptr;  // calls BIO_free(buf.get())\n */\n\ntemplate <typename T, void (*f)(T*)>\nstruct static_function_deleter {\n  void operator()(T* t) const { f(t); }\n};\n\n/**\n *  to_shared_ptr\n *\n *  Convert unique_ptr to shared_ptr without specifying the template type\n *  parameter and letting the compiler deduce it.\n *\n *  So you can write this:\n *\n *      auto sptr = to_shared_ptr(getSomethingUnique<T>());\n *\n *  Instead of this:\n *\n *      auto sptr = shared_ptr<T>(getSomethingUnique<T>());\n *\n *  Useful when `T` is long, such as:\n *\n *      using T = foobar::FooBarAsyncClient;\n */\ntemplate <typename T, typename D>\nstd::shared_ptr<T> to_shared_ptr(std::unique_ptr<T, D>&& ptr) {\n  return std::shared_ptr<T>(std::move(ptr));\n}\n\n/**\n *  to_shared_ptr_aliasing\n */\ntemplate <typename T, typename U>\nstd::shared_ptr<U> to_shared_ptr_aliasing(std::shared_ptr<T> const& r, U* ptr) {\n  return std::shared_ptr<U>(r, ptr);\n}\n\n/**\n *  to_shared_ptr_non_owning\n */\ntemplate <typename U>\nstd::shared_ptr<U> to_shared_ptr_non_owning(U* ptr) {\n  return std::shared_ptr<U>(std::shared_ptr<void>{}, ptr);\n}\n\n/**\n *  to_weak_ptr\n *\n *  Make a weak_ptr and return it from a shared_ptr without specifying the\n *  template type parameter and letting the compiler deduce it.\n *\n *  So you can write this:\n *\n *      auto wptr = to_weak_ptr(getSomethingShared<T>());\n *\n *  Instead of this:\n *\n *      auto wptr = weak_ptr<T>(getSomethingShared<T>());\n *\n *  Useful when `T` is long, such as:\n *\n *      using T = foobar::FooBarAsyncClient;\n */\ntemplate <typename T>\nstd::weak_ptr<T> to_weak_ptr(const std::shared_ptr<T>& ptr) {\n  return ptr;\n}\n\n#if defined(__GLIBCXX__)\nnamespace detail {\nvoid weak_ptr_set_stored_ptr(std::weak_ptr<void>& w, void* ptr);\n\ntemplate <typename Tag, void* std::__weak_ptr<void>::* WeakPtr_Ptr_Field>\nstruct GenerateWeakPtrInternalsAccessor {\n  friend void weak_ptr_set_stored_ptr(std::weak_ptr<void>& w, void* ptr) {\n    w.*WeakPtr_Ptr_Field = ptr;\n  }\n};\n\n// Each template instantiation of GenerateWeakPtrInternalsAccessor must\n// be a new type, to avoid ODR problems.  We do this by tagging it with\n// a type from an anon namespace.\nnamespace {\nstruct MemoryAnonTag {};\n} // namespace\n\ntemplate struct GenerateWeakPtrInternalsAccessor<\n    MemoryAnonTag,\n    &std::__weak_ptr<void>::_M_ptr>;\n} // namespace detail\n#endif\n\n/**\n *  to_weak_ptr_aliasing\n *\n *  Like to_weak_ptr, but arranges that lock().get() on the returned\n *  pointer points to ptr rather than r.get().\n *\n *  Equivalent to:\n *\n *      to_weak_ptr(std::shared_ptr<U>(r, ptr))\n *\n *  For libstdc++, ABI-specific tricks are used to optimize the\n *  implementation.\n */\ntemplate <typename T, typename U>\nstd::weak_ptr<U> to_weak_ptr_aliasing(const std::shared_ptr<T>& r, U* ptr) {\n#if defined(__GLIBCXX__)\n  std::weak_ptr<void> wv(r);\n  detail::weak_ptr_set_stored_ptr(wv, ptr);\n  FOLLY_PUSH_WARNING\n  FOLLY_GCC_DISABLE_WARNING(\"-Wstrict-aliasing\")\n  return reinterpret_cast<std::weak_ptr<U>&&>(wv);\n  FOLLY_POP_WARNING\n#else\n  return std::shared_ptr<U>(r, ptr);\n#endif\n}\n\n/**\n * fmap_shared_ptr_aliasing\n *\n * This is a helper method that allows one to get aliased shared_ptr to an inner\n * object inside another shared_ptr. For example, if you have an object of type\n * T that contains field of type U, you can use this method to get a shared\n * pointer to the field U by calling `fmap_shared_ptr_aliasing(ptrToT, getU)`\n * where `getU` is a function that returns a pointer to that field.\n * @param getU a function that returns a const pointer to the field of type U by\n * taking a `const T*` as an argument.\n * @return a shared_ptr to the field U or nullptr if the owner is\n * nullptr or getU returns nullptr.\n */\ntemplate <\n    typename T,\n    typename GetU,\n    typename U = std::remove_pointer_t<std::invoke_result_t<GetU&, const T*>>>\nstd::shared_ptr<U> fmap_shared_ptr_aliasing(\n    const std::shared_ptr<T>& owner, GetU getU) {\n  if (auto* tPtr = owner.get()) {\n    if (auto* uPtr = getU(tPtr)) {\n      return to_shared_ptr_aliasing(owner, uPtr);\n    }\n  }\n  return nullptr;\n}\n\ntemplate <\n    typename T,\n    typename GetU,\n    typename U = std::remove_pointer_t<std::invoke_result_t<GetU&, const T*>>>\nstd::shared_ptr<U> fmap_shared_ptr_aliasing(\n    std::shared_ptr<T>&& owner, GetU getU) {\n  if (auto* tPtr = owner.get()) {\n    if (auto* uPtr = getU(tPtr)) {\n      return to_shared_ptr_aliasing(owner, uPtr);\n    }\n  }\n  return nullptr;\n}\n\ntemplate <typename GetU>\nauto fmap_shared_ptr_aliasing(std::nullptr_t owner, GetU&& getU) = delete;\n\n/**\n *  copy_to_unique_ptr\n *\n *  Move or copy the argument to the heap and return it owned by a unique_ptr.\n *\n *  Like std::make_unique, but deduces the type of the owned object.\n */\ntemplate <typename T>\nstd::unique_ptr<remove_cvref_t<T>> copy_to_unique_ptr(T&& t) {\n  return std::make_unique<remove_cvref_t<T>>(static_cast<T&&>(t));\n}\n\n/**\n *  copy_to_shared_ptr\n *\n *  Move or copy the argument to the heap and return it owned by a shared_ptr.\n *\n *  Like make_shared, but deduces the type of the owned object.\n */\ntemplate <typename T>\nstd::shared_ptr<remove_cvref_t<T>> copy_to_shared_ptr(T&& t) {\n  return std::make_shared<remove_cvref_t<T>>(static_cast<T&&>(t));\n}\n\n/**\n *  copy_through_unique_ptr\n *\n *  If the argument is nonnull, allocates a copy of its pointee.\n */\ntemplate <typename T>\nstd::unique_ptr<T> copy_through_unique_ptr(const std::unique_ptr<T>& t) {\n  static_assert(\n      !std::is_polymorphic<T>::value || std::is_final<T>::value,\n      \"possibly slicing\");\n  return t ? std::make_unique<T>(*t) : nullptr;\n}\n\n/**\n *  copy_through_shared_ptr\n *\n *  If the argument is nonnull, allocates a copy of its pointee.\n */\ntemplate <typename T>\nstd::shared_ptr<T> copy_through_shared_ptr(const std::shared_ptr<T>& t) {\n  static_assert(\n      !std::is_polymorphic<T>::value || std::is_final<T>::value,\n      \"possibly slicing\");\n  return t ? std::make_shared<T>(*t) : nullptr;\n}\n\n//  erased_unique_ptr\n//\n//  A type-erased smart-ptr with unique ownership to a heap-allocated object.\nusing erased_unique_ptr = std::unique_ptr<void, void (*)(void*)>;\n\nnamespace detail {\n// for erased_unique_ptr with types that specialize default_delete\ntemplate <typename T>\nvoid erased_unique_ptr_delete(void* ptr) {\n  std::default_delete<T>()(static_cast<T*>(ptr));\n}\n} // namespace detail\n\n//  to_erased_unique_ptr\n//\n//  Converts an owning pointer to an object to an erased_unique_ptr.\ntemplate <typename T>\nerased_unique_ptr to_erased_unique_ptr(T* const ptr) noexcept {\n  return {ptr, detail::erased_unique_ptr_delete<T>};\n}\n\n//  to_erased_unique_ptr\n//\n//  Converts an owning std::unique_ptr to an erased_unique_ptr.\ntemplate <typename T>\nerased_unique_ptr to_erased_unique_ptr(std::unique_ptr<T> ptr) noexcept {\n  return to_erased_unique_ptr(ptr.release());\n}\n\n//  make_erased_unique\n//\n//  Allocate an object of the T on the heap, constructed with a..., and return\n//  an owning erased_unique_ptr to it.\ntemplate <typename T, typename... A>\nerased_unique_ptr make_erased_unique(A&&... a) {\n  return to_erased_unique_ptr(std::make_unique<T>(static_cast<A&&>(a)...));\n}\n\n//  copy_to_erased_unique_ptr\n//\n//  Copy an object to the heap and return an owning erased_unique_ptr to it.\ntemplate <typename T>\nerased_unique_ptr copy_to_erased_unique_ptr(T&& obj) {\n  return to_erased_unique_ptr(copy_to_unique_ptr(static_cast<T&&>(obj)));\n}\n\n//  empty_erased_unique_ptr\n//\n//  Return an empty erased_unique_ptr.\ninline erased_unique_ptr empty_erased_unique_ptr() {\n  return {nullptr, nullptr};\n}\n\n/**\n * SysAllocator\n *\n * Resembles std::allocator, the default Allocator, but wraps std::malloc and\n * std::free.\n */\ntemplate <typename T>\nclass SysAllocator {\n private:\n  using Self = SysAllocator<T>;\n\n public:\n  using value_type = T;\n\n  constexpr SysAllocator() = default;\n\n  constexpr SysAllocator(SysAllocator const&) = default;\n\n  template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>\n  constexpr SysAllocator(SysAllocator<U> const&) noexcept {}\n\n  T* allocate(size_t count) {\n    auto const p = std::malloc(sizeof(T) * count);\n    if (!p) {\n      throw_exception<std::bad_alloc>();\n    }\n    return static_cast<T*>(p);\n  }\n  void deallocate(T* p, size_t count) { sizedFree(p, count * sizeof(T)); }\n\n  friend bool operator==(Self const&, Self const&) noexcept { return true; }\n  friend bool operator!=(Self const&, Self const&) noexcept { return false; }\n};\n\nclass DefaultAlign {\n private:\n  using Self = DefaultAlign;\n  std::size_t align_;\n\n public:\n  explicit DefaultAlign(std::size_t align) noexcept : align_(align) {\n    assert(!(align_ < sizeof(void*)) && bool(\"bad align: too small\"));\n    assert(!(align_ & (align_ - 1)) && bool(\"bad align: not power-of-two\"));\n  }\n  std::size_t operator()() const noexcept { return align_; }\n\n  friend bool operator==(Self const& a, Self const& b) noexcept {\n    return a.align_ == b.align_;\n  }\n  friend bool operator!=(Self const& a, Self const& b) noexcept {\n    return a.align_ != b.align_;\n  }\n};\n\ntemplate <std::size_t Align>\nclass FixedAlign {\n private:\n  static_assert(!(Align < sizeof(void*)), \"bad align: too small\");\n  static_assert(!(Align & (Align - 1)), \"bad align: not power-of-two\");\n  using Self = FixedAlign<Align>;\n\n public:\n  constexpr std::size_t operator()() const noexcept { return Align; }\n\n  friend bool operator==(Self const&, Self const&) noexcept { return true; }\n  friend bool operator!=(Self const&, Self const&) noexcept { return false; }\n};\n\n/**\n * AlignedSysAllocator\n *\n * Resembles std::allocator, the default Allocator, but wraps aligned_malloc and\n * aligned_free.\n *\n * Accepts a policy parameter for providing the alignment, which must:\n *   * be invocable as std::size_t(std::size_t) noexcept\n *     * taking the type alignment and returning the allocation alignment\n *   * be noexcept-copy-constructible\n *   * have noexcept operator==\n *   * have noexcept operator!=\n *   * not be final\n *\n * DefaultAlign and FixedAlign<std::size_t>, provided above, are valid policies.\n */\ntemplate <typename T, typename Align = DefaultAlign>\nclass AlignedSysAllocator : private Align {\n private:\n  using Self = AlignedSysAllocator<T, Align>;\n\n  template <typename, typename>\n  friend class AlignedSysAllocator;\n\n  constexpr Align const& align() const { return *this; }\n\n public:\n  static_assert(std::is_nothrow_copy_constructible<Align>::value);\n  static_assert(is_nothrow_invocable_r_v<std::size_t, Align>);\n\n  using value_type = T;\n\n  using propagate_on_container_copy_assignment = std::true_type;\n  using propagate_on_container_move_assignment = std::true_type;\n  using propagate_on_container_swap = std::true_type;\n\n  using Align::Align;\n\n  // TODO: remove this ctor, which is is no longer required as of under gcc7\n  template <\n      typename S = Align,\n      std::enable_if_t<std::is_default_constructible<S>::value, int> = 0>\n  constexpr AlignedSysAllocator() noexcept(noexcept(Align())) : Align() {}\n\n  constexpr AlignedSysAllocator(AlignedSysAllocator const&) = default;\n\n  template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>\n  constexpr AlignedSysAllocator(\n      AlignedSysAllocator<U, Align> const& other) noexcept\n      : Align(other.align()) {}\n\n  T* allocate(size_t count) {\n    auto const a = align()() < alignof(T) ? alignof(T) : align()();\n    auto const p = aligned_malloc(sizeof(T) * count, a);\n    if (!p) {\n      if (FOLLY_UNLIKELY(errno != ENOMEM)) {\n        std::terminate();\n      }\n      throw_exception<std::bad_alloc>();\n    }\n    return static_cast<T*>(p);\n  }\n  void deallocate(T* p, size_t /* count */) { aligned_free(p); }\n\n  friend bool operator==(Self const& a, Self const& b) noexcept {\n    return a.align() == b.align();\n  }\n  friend bool operator!=(Self const& a, Self const& b) noexcept {\n    return a.align() != b.align();\n  }\n};\n\n/**\n * CxxAllocatorAdaptor\n *\n * A type conforming to C++ concept Allocator, delegating operations to an\n * unowned Inner which has this required interface:\n *\n *   void* allocate(std::size_t)\n *   void deallocate(void*, std::size_t)\n *\n * Note that Inner is *not* a C++ Allocator.\n */\ntemplate <typename T, class Inner, bool FallbackToStdAlloc = false>\nclass CxxAllocatorAdaptor : private std::allocator<T> {\n private:\n  using Self = CxxAllocatorAdaptor<T, Inner, FallbackToStdAlloc>;\n\n  template <typename U, typename UInner, bool UFallback>\n  friend class CxxAllocatorAdaptor;\n\n  Inner* inner_ = nullptr;\n\n public:\n  using value_type = T;\n\n  using propagate_on_container_copy_assignment = std::true_type;\n  using propagate_on_container_move_assignment = std::true_type;\n  using propagate_on_container_swap = std::true_type;\n\n  template <bool X = FallbackToStdAlloc, std::enable_if_t<X, int> = 0>\n  constexpr explicit CxxAllocatorAdaptor() {}\n\n  constexpr explicit CxxAllocatorAdaptor(Inner& ref) : inner_(&ref) {}\n\n  constexpr CxxAllocatorAdaptor(CxxAllocatorAdaptor const&) = default;\n\n  template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>\n  constexpr CxxAllocatorAdaptor(\n      CxxAllocatorAdaptor<U, Inner, FallbackToStdAlloc> const& other)\n      : inner_(other.inner_) {}\n\n  CxxAllocatorAdaptor& operator=(CxxAllocatorAdaptor const& other) = default;\n\n  template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>\n  CxxAllocatorAdaptor& operator=(\n      CxxAllocatorAdaptor<U, Inner, FallbackToStdAlloc> const& other) noexcept {\n    inner_ = other.inner_;\n    return *this;\n  }\n\n  T* allocate(std::size_t n) {\n    if (FallbackToStdAlloc && inner_ == nullptr) {\n      return std::allocator<T>::allocate(n);\n    }\n    return static_cast<T*>(inner_->allocate(sizeof(T) * n));\n  }\n\n  void deallocate(T* p, std::size_t n) {\n    if (inner_ != nullptr) {\n      inner_->deallocate(p, sizeof(T) * n);\n    } else {\n      assert(FallbackToStdAlloc);\n      std::allocator<T>::deallocate(p, n);\n    }\n  }\n\n  friend bool operator==(Self const& a, Self const& b) noexcept {\n    return a.inner_ == b.inner_;\n  }\n  friend bool operator!=(Self const& a, Self const& b) noexcept {\n    return a.inner_ != b.inner_;\n  }\n\n  template <typename U>\n  struct rebind {\n    using other = CxxAllocatorAdaptor<U, Inner, FallbackToStdAlloc>;\n  };\n};\n\n/*\n * allocator_delete\n *\n * A deleter which automatically works with a given allocator.\n *\n * Derives from the allocator to take advantage of the empty base\n * optimization when possible.\n */\ntemplate <typename Alloc>\nclass allocator_delete : private std::remove_reference<Alloc>::type {\n private:\n  using allocator_type = typename std::remove_reference<Alloc>::type;\n  using allocator_traits = std::allocator_traits<allocator_type>;\n  using value_type = typename allocator_traits::value_type;\n  using pointer = typename allocator_traits::pointer;\n\n public:\n  allocator_delete() = default;\n  allocator_delete(allocator_delete const&) = default;\n  allocator_delete(allocator_delete&&) = default;\n  allocator_delete& operator=(allocator_delete const&) = default;\n  allocator_delete& operator=(allocator_delete&&) = default;\n\n  explicit allocator_delete(const allocator_type& alloc)\n      : allocator_type(alloc) {}\n\n  explicit allocator_delete(allocator_type&& alloc)\n      : allocator_type(std::move(alloc)) {}\n\n  template <typename U>\n  allocator_delete(const allocator_delete<U>& other)\n      : allocator_type(other.get_allocator()) {}\n\n  allocator_type const& get_allocator() const { return *this; }\n\n  void operator()(pointer p) const {\n    auto alloc = get_allocator();\n    allocator_traits::destroy(alloc, p);\n    allocator_traits::deallocate(alloc, p, 1);\n  }\n};\n\n/**\n * allocate_unique, like std::allocate_shared but for std::unique_ptr\n */\ntemplate <typename T, typename Alloc, typename... Args>\nstd::unique_ptr<\n    T,\n    allocator_delete<\n        typename std::allocator_traits<Alloc>::template rebind_alloc<T>>>\nallocate_unique(Alloc const& alloc, Args&&... args) {\n  using TAlloc =\n      typename std::allocator_traits<Alloc>::template rebind_alloc<T>;\n\n  using traits = std::allocator_traits<TAlloc>;\n  struct DeferCondDeallocate {\n    bool& cond;\n    TAlloc& copy;\n    T* p;\n    ~DeferCondDeallocate() {\n      if (FOLLY_UNLIKELY(!cond)) {\n        traits::deallocate(copy, p, 1);\n      }\n    }\n  };\n  auto copy = TAlloc(alloc);\n  auto const p = traits::allocate(copy, 1);\n  {\n    bool constructed = false;\n    DeferCondDeallocate handler{constructed, copy, p};\n    traits::construct(copy, p, static_cast<Args&&>(args)...);\n    constructed = true;\n  }\n  return {p, allocator_delete<TAlloc>(std::move(copy))};\n}\n\nstruct SysBufferDeleter {\n  void operator()(void* ptr) { std::free(ptr); }\n};\nusing SysBufferUniquePtr = std::unique_ptr<void, SysBufferDeleter>;\n\ninline SysBufferUniquePtr allocate_sys_buffer(std::size_t size) {\n  auto p = std::malloc(size);\n  if (!p) {\n    throw_exception<std::bad_alloc>();\n  }\n  return {p, {}};\n}\n\n/**\n * AllocatorHasTrivialDeallocate\n *\n * Unambiguously inherits std::integral_constant<bool, V> for some bool V.\n *\n * Describes whether a C++ Aallocator has trivial, i.e. no-op, deallocate().\n *\n * Also may be used to describe types which may be used with\n * CxxAllocatorAdaptor.\n */\ntemplate <typename Alloc>\nstruct AllocatorHasTrivialDeallocate : std::false_type {};\n\ntemplate <typename T, class Alloc>\nstruct AllocatorHasTrivialDeallocate<CxxAllocatorAdaptor<T, Alloc>>\n    : AllocatorHasTrivialDeallocate<Alloc> {};\n\nnamespace detail {\n// note that construct and destroy here are methods, not short names for\n// the constructor and destructor\nFOLLY_CREATE_MEMBER_INVOKER(AllocatorConstruct_, construct);\nFOLLY_CREATE_MEMBER_INVOKER(AllocatorDestroy_, destroy);\n\ntemplate <typename Void, typename Alloc, typename... Args>\nstruct AllocatorCustomizesConstruct_\n    : folly::is_invocable<AllocatorConstruct_, Alloc, Args...> {};\n\ntemplate <typename Alloc, typename... Args>\nstruct AllocatorCustomizesConstruct_<\n    void_t<typename Alloc::folly_has_default_object_construct>,\n    Alloc,\n    Args...> : Negation<typename Alloc::folly_has_default_object_construct> {};\n\ntemplate <typename Void, typename Alloc, typename... Args>\nstruct AllocatorCustomizesDestroy_\n    : folly::is_invocable<AllocatorDestroy_, Alloc, Args...> {};\n\ntemplate <typename Alloc, typename... Args>\nstruct AllocatorCustomizesDestroy_<\n    void_t<typename Alloc::folly_has_default_object_destroy>,\n    Alloc,\n    Args...> : Negation<typename Alloc::folly_has_default_object_destroy> {};\n} // namespace detail\n\n/**\n * AllocatorHasDefaultObjectConstruct\n *\n * AllocatorHasDefaultObjectConstruct<A, T, Args...> unambiguously\n * inherits std::integral_constant<bool, V>, where V will be true iff\n * the effect of std::allocator_traits<A>::construct(a, p, args...) is\n * the same as new (static_cast<void*>(p)) T(args...).  If true then\n * any optimizations applicable to object construction (relying on\n * std::is_trivially_copyable<T>, for example) can be applied to objects\n * in an allocator-aware container using an allocation of type A.\n *\n * Allocator types can override V by declaring a type alias for\n * folly_has_default_object_construct.  It is helpful to do this if you\n * define a custom allocator type that defines a construct method, but\n * that method doesn't do anything except call placement new.\n */\ntemplate <typename Alloc, typename T, typename... Args>\nstruct AllocatorHasDefaultObjectConstruct\n    : Negation<\n          detail::AllocatorCustomizesConstruct_<void, Alloc, T*, Args...>> {};\n\ntemplate <typename Value, typename T, typename... Args>\nstruct AllocatorHasDefaultObjectConstruct<std::allocator<Value>, T, Args...>\n    : std::true_type {};\n\n/**\n * AllocatorHasDefaultObjectDestroy\n *\n * AllocatorHasDefaultObjectDestroy<A, T> unambiguously inherits\n * std::integral_constant<bool, V>, where V will be true iff the effect\n * of std::allocator_traits<A>::destroy(a, p) is the same as p->~T().\n * If true then optimizations applicable to object destruction (relying\n * on std::is_trivially_destructible<T>, for example) can be applied to\n * objects in an allocator-aware container using an allocator of type A.\n *\n * Allocator types can override V by declaring a type alias for\n * folly_has_default_object_destroy.  It is helpful to do this if you\n * define a custom allocator type that defines a destroy method, but that\n * method doesn't do anything except call the object's destructor.\n */\ntemplate <typename Alloc, typename T>\nstruct AllocatorHasDefaultObjectDestroy\n    : Negation<detail::AllocatorCustomizesDestroy_<void, Alloc, T*>> {};\n\ntemplate <typename Value, typename T>\nstruct AllocatorHasDefaultObjectDestroy<std::allocator<Value>, T>\n    : std::true_type {};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/MicroLock.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/MicroLock.h>\n\n#include <thread>\n\n#include <folly/portability/Asm.h>\n\nnamespace folly {\n\nuint8_t MicroLockCore::lockSlowPath(\n    uint8_t oldWord, unsigned maxSpins, unsigned maxYields) noexcept {\n  uint8_t newWord;\n  unsigned spins = 0;\n  uint8_t heldBit = 1;\n  uint8_t waitBit = heldBit << 1;\n  uint8_t needWaitBit = 0;\n\nretry:\n  if ((oldWord & heldBit) != 0) {\n    ++spins;\n    if (spins > maxSpins + maxYields) {\n      // Somebody appears to have the lock.  Block waiting for the\n      // holder to unlock the lock.  We set heldbit(slot) so that the\n      // lock holder knows to FUTEX_WAKE us.\n      newWord = oldWord | waitBit;\n      if (newWord != oldWord) {\n        if (!atomic_ref(lock_).compare_exchange_weak(\n                oldWord,\n                newWord,\n                std::memory_order_relaxed,\n                std::memory_order_relaxed)) {\n          goto retry;\n        }\n      }\n      atomic_wait(&atomic_ref(lock_).atomic(), newWord);\n      needWaitBit = waitBit;\n    } else if (spins > maxSpins) {\n      // sched_yield(), but more portable\n      std::this_thread::yield();\n    } else {\n      folly::asm_volatile_pause();\n    }\n    oldWord = atomic_ref(lock_).load(std::memory_order_relaxed);\n    goto retry;\n  }\n\n  newWord = oldWord | heldBit | needWaitBit;\n  if (!atomic_ref(lock_).compare_exchange_weak(\n          oldWord,\n          newWord,\n          std::memory_order_acquire,\n          std::memory_order_relaxed)) {\n    goto retry;\n  }\n  return decodeDataFromWord(newWord);\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/MicroLock.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstdint>\n\n#include <folly/Optional.h>\n#include <folly/Portability.h>\n#include <folly/Utility.h>\n#include <folly/synchronization/AtomicNotification.h>\n#include <folly/synchronization/AtomicRef.h>\n\nnamespace folly {\n\n/**\n * Tiny exclusive lock that uses 2 bits. It is stored as 1 byte and\n * has APIs for using the remaining 6 bits for storing user data.\n *\n * You should zero-initialize the bits of a MicroLock that you intend\n * to use.\n *\n * If you're not space-constrained, prefer std::mutex, which will\n * likely be faster, since it has more than two bits of information to\n * work with.\n *\n * You are free to put a MicroLock in a union with some other object.\n * If, for example, you want to use the bottom two bits of a pointer\n * as a lock, you can put a MicroLock in a union with the pointer,\n * which will use the two least-significant bits in the bottom byte.\n *\n * (Note that such a union is safe only because MicroLock is based on\n * a character type, and even under a strict interpretation of C++'s\n * aliasing rules, character types may alias anything.)\n *\n * Unused bits in the lock can be used to store user data via\n * lockAndLoad() and unlockAndStore(), or LockGuardWithData.\n *\n * The MaxSpins template parameter controls the number of times we\n * spin trying to acquire the lock.  MaxYields controls the number of\n * times we call sched_yield; once we've tried to acquire the lock\n * MaxSpins + MaxYields times, we sleep on the lock futex.\n * By adjusting these parameters, you can make MicroLock behave as\n * much or as little like a conventional spinlock as you'd like.\n *\n * Performance\n * -----------\n *\n * With the default template options, the timings for uncontended\n * acquire-then-release come out as follows on Intel(R) Xeon(R) CPU\n * E5-2660 0 @ 2.20GHz, in @mode/opt, as of the master tree at Tue, 01\n * Mar 2016 19:48:15.\n *\n * ========================================================================\n * folly/test/SmallLocksBenchmark.cpp          relative  time/iter  iters/s\n * ========================================================================\n * MicroSpinLockUncontendedBenchmark                       13.46ns   74.28M\n * PicoSpinLockUncontendedBenchmark                        14.99ns   66.71M\n * MicroLockUncontendedBenchmark                           27.06ns   36.96M\n * StdMutexUncontendedBenchmark                            25.18ns   39.72M\n * VirtualFunctionCall                                      1.72ns  579.78M\n * ========================================================================\n *\n * (The virtual dispatch benchmark is provided for scale.)\n *\n * While the uncontended case for MicroLock is competitive with the\n * glibc 2.2.0 implementation of std::mutex, std::mutex is likely to be\n * faster in the contended case, because we need to wake up all waiters\n * when we release.\n *\n * Make sure to benchmark your particular workload.\n *\n */\n\nclass MicroLockCore {\n protected:\n  uint8_t lock_{};\n  /**\n   * Mask for bit indicating that the flag is held.\n   */\n  unsigned heldBit() const noexcept;\n  /**\n   * Mask for bit indicating that there is a waiter that should be woken up.\n   */\n  unsigned waitBit() const noexcept;\n\n  uint8_t lockSlowPath(\n      uint8_t oldWord, unsigned maxSpins, unsigned maxYields) noexcept;\n\n  static constexpr unsigned kNumLockBits = 2;\n  static constexpr uint8_t kLockBits =\n      static_cast<uint8_t>((1 << kNumLockBits) - 1);\n  static constexpr uint8_t kDataBits = static_cast<uint8_t>(~kLockBits);\n  /**\n   * Decodes the value stored in the unused bits of the lock.\n   */\n  static constexpr uint8_t decodeDataFromByte(uint8_t lockByte) noexcept {\n    return static_cast<uint8_t>(lockByte >> kNumLockBits);\n  }\n  /**\n   * Encodes the value for the unused bits of the lock.\n   */\n  static constexpr uint8_t encodeDataToByte(uint8_t data) noexcept {\n    return static_cast<uint8_t>(data << kNumLockBits);\n  }\n\n  static constexpr uint8_t decodeDataFromWord(uint8_t word) noexcept {\n    return static_cast<uint8_t>(word >> kNumLockBits);\n  }\n  static constexpr uint8_t encodeDataToWord(\n      uint8_t word, uint8_t value) noexcept {\n    const uint8_t preservedBits = word & ~(kDataBits);\n    const uint8_t newBits = encodeDataToByte(value);\n    return preservedBits | newBits;\n  }\n\n  template <typename Func>\n  void unlockAndStoreWithModifier(Func modifier) noexcept;\n\n public:\n  /**\n   * Loads the data stored in the unused bits of the lock atomically.\n   */\n  uint8_t load(\n      std::memory_order order = std::memory_order_seq_cst) const noexcept {\n    return decodeDataFromWord(atomic_ref(lock_).load(order));\n  }\n\n  /**\n   * Stores the data in the unused bits of the lock atomically. Since 2 bits are\n   * used by the lock, the most significant 2 bits of the provided value will be\n   * ignored.\n   */\n  void store(\n      uint8_t value,\n      std::memory_order order = std::memory_order_seq_cst) noexcept;\n\n  /**\n   * Unlocks the lock and stores the bits of the provided value into the data\n   * bits. Since 2 bits are used by the lock, the most significant 2 bits of the\n   * provided value will be ignored.\n   */\n  void unlockAndStore(uint8_t value) noexcept;\n  void unlock() noexcept;\n};\n\ninline unsigned MicroLockCore::heldBit() const noexcept {\n  return 1U << 0;\n}\n\ninline unsigned MicroLockCore::waitBit() const noexcept {\n  return 1U << 1;\n}\n\ninline void MicroLockCore::store(\n    uint8_t value, std::memory_order order) noexcept {\n  auto oldWord = atomic_ref(lock_).load(std::memory_order_relaxed);\n  while (true) {\n    auto newWord = encodeDataToWord(oldWord, value);\n    if (atomic_ref(lock_).compare_exchange_weak(\n            oldWord, newWord, order, std::memory_order_relaxed)) {\n      break;\n    }\n  }\n}\n\ntemplate <typename Func>\nvoid MicroLockCore::unlockAndStoreWithModifier(Func modifier) noexcept {\n  uint8_t oldWord;\n  uint8_t newWord;\n\n  oldWord = atomic_ref(lock_).load(std::memory_order_relaxed);\n  do {\n    assert(oldWord & heldBit());\n    newWord = modifier(oldWord) & ~(heldBit() | waitBit());\n  } while (!atomic_ref(lock_).compare_exchange_weak(\n      oldWord, newWord, std::memory_order_release, std::memory_order_relaxed));\n\n  if (oldWord & waitBit()) {\n    atomic_notify_one(&atomic_ref(lock_).atomic());\n  }\n}\n\ninline void MicroLockCore::unlockAndStore(uint8_t value) noexcept {\n  unlockAndStoreWithModifier([value](uint8_t oldWord) {\n    return encodeDataToWord(oldWord, value);\n  });\n}\n\ninline void MicroLockCore::unlock() noexcept {\n  unlockAndStoreWithModifier(identity);\n}\n\ntemplate <unsigned MaxSpins = 1000, unsigned MaxYields = 0>\nclass MicroLockBase : public MicroLockCore {\n public:\n  /**\n   * Locks the lock and returns the data stored in the unused bits of the lock.\n   * This is useful when you want to use the unused bits of the lock to store\n   * data, in which case reading and locking should be done in one atomic\n   * operation.\n   */\n  uint8_t lockAndLoad() noexcept;\n  void lock() noexcept { lockAndLoad(); }\n  bool try_lock() noexcept;\n\n  /**\n   * A lock guard which allows reading and writing to the unused bits of the\n   * lock as data.\n   */\n  struct LockGuardWithData {\n    explicit LockGuardWithData(MicroLockBase<MaxSpins, MaxYields>& lock)\n        : lock_(lock) {\n      loadedValue_ = lock_.lockAndLoad();\n    }\n\n    ~LockGuardWithData() noexcept {\n      if (storedValue_) {\n        lock_.unlockAndStore(*storedValue_);\n      } else {\n        lock_.unlock();\n      }\n    }\n\n    /**\n     * The stored data bits at the time of locking.\n     */\n    uint8_t loadedValue() const noexcept { return loadedValue_; }\n\n    /**\n     * The value that will be stored back into data bits when it is unlocked.\n     */\n    void storeValue(uint8_t value) noexcept { storedValue_ = value; }\n\n   private:\n    MicroLockBase<MaxSpins, MaxYields>& lock_;\n    uint8_t loadedValue_;\n    folly::Optional<uint8_t> storedValue_;\n  };\n};\n\ntemplate <unsigned MaxSpins, unsigned MaxYields>\nbool MicroLockBase<MaxSpins, MaxYields>::try_lock() noexcept {\n  // N.B. You might think that try_lock is just the fast path of lock,\n  // but you'd be wrong.  Keep in mind that other parts of our host\n  // word might be changing while we take the lock!  We're not allowed\n  // to fail spuriously if the lock is in fact not held, even if other\n  // people are concurrently modifying other parts of the word.\n  //\n  // We need to loop until we either see firm evidence that somebody\n  // else has the lock (by looking at heldBit) or see our CAS succeed.\n  // A failed CAS by itself does not indicate lock-acquire failure.\n\n  uint8_t oldWord = atomic_ref(lock_).load(std::memory_order_relaxed);\n  do {\n    if (oldWord & heldBit()) {\n      return false;\n    }\n  } while (!atomic_ref(lock_).compare_exchange_weak(\n      oldWord,\n      oldWord | heldBit(),\n      std::memory_order_acquire,\n      std::memory_order_relaxed));\n\n  return true;\n}\n\ntemplate <unsigned MaxSpins, unsigned MaxYields>\nuint8_t MicroLockBase<MaxSpins, MaxYields>::lockAndLoad() noexcept {\n  static_assert(MaxSpins + MaxYields < (unsigned)-1, \"overflow\");\n\n  uint8_t oldWord;\n  oldWord = atomic_ref(lock_).load(std::memory_order_relaxed);\n  if ((oldWord & heldBit()) == 0 &&\n      atomic_ref(lock_).compare_exchange_weak(\n          oldWord,\n          to_narrow(oldWord | heldBit()),\n          std::memory_order_acquire,\n          std::memory_order_relaxed)) {\n    // Fast uncontended case: memory_order_acquire above is our barrier\n    return decodeDataFromWord(to_narrow(oldWord | heldBit()));\n  } else {\n    // lockSlowPath doesn't call waitBit(); it just shifts the input bit.  Make\n    // sure its shifting produces the same result a call to waitBit would.\n    assert(heldBit() << 1 == waitBit());\n    // lockSlowPath emits its own memory barrier\n    return lockSlowPath(oldWord, MaxSpins, MaxYields);\n  }\n}\n\nusing MicroLock = MicroLockBase<>;\n} // namespace folly\n"
  },
  {
    "path": "folly/MicroSpinLock.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/synchronization/MicroSpinLock.h> // @shim\n"
  },
  {
    "path": "folly/MoveWrapper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\nnamespace folly {\n\n/** C++11 closures don't support move-in capture. Nor does std::bind.\n    facepalm.\n\n    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3610.html\n\n    \"[...] a work-around that should make people's stomach crawl:\n    write a wrapper that performs move-on-copy, much like the deprecated\n    auto_ptr\"\n\n    Unlike auto_ptr, this doesn't require a heap allocation.\n    */\ntemplate <class T>\nclass MoveWrapper {\n public:\n  /** If value can be default-constructed, why not?\n      Then we don't have to move it in */\n  MoveWrapper() = default;\n\n  /// Move a value in.\n  explicit MoveWrapper(T&& t) : value(std::move(t)) {}\n\n  /// copy is move\n  MoveWrapper(const MoveWrapper& other) : value(std::move(other.value)) {}\n\n  /// move is also move\n  MoveWrapper(MoveWrapper&& other) : value(std::move(other.value)) {}\n\n  const T& operator*() const { return value; }\n  T& operator*() { return value; }\n\n  const T* operator->() const { return &value; }\n  T* operator->() { return &value; }\n\n  /// move the value out (sugar for std::move(*moveWrapper))\n  T&& move() { return std::move(value); }\n\n  // If you want these you're probably doing it wrong, though they'd be\n  // easy enough to implement\n  MoveWrapper& operator=(MoveWrapper const&) = delete;\n  MoveWrapper& operator=(MoveWrapper&&) = delete;\n\n private:\n  mutable T value;\n};\n\n/// Make a MoveWrapper from the argument. Because the name \"makeMoveWrapper\"\n/// is already quite transparent in its intent, this will work for lvalues as\n/// if you had wrapped them in std::move.\ntemplate <class T, class T0 = typename std::remove_reference<T>::type>\nMoveWrapper<T0> makeMoveWrapper(T&& t) {\n  return MoveWrapper<T0>(std::forward<T0>(t));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ObserverContainer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <bitset>\n#include <vector>\n\n#include <glog/logging.h>\n#include <folly/ConstructorCallbackList.h>\n#include <folly/Function.h>\n#include <folly/Optional.h>\n#include <folly/ScopeGuard.h>\n#include <folly/io/async/DestructorCheck.h>\n#include <folly/lang/Switch.h>\n#include <folly/small_vector.h>\n\n/**\n * Tooling that makes it easier to design observable objects and observers.\n */\n\nnamespace folly {\n\n/**\n * Interface for store of pointers to observers.\n */\ntemplate <typename Observer>\nclass ObserverContainerStoreBase {\n public:\n  using observer_type = Observer;\n\n  // ObserverContainerStore stores shared_ptr<Observer> objects.\n  //\n  // To support observer objects that are NOT managed by a shared_ptr, the\n  // encapsulating ObserverContainer wraps unmanaged pointers inside of\n  // shared_ptrs, but sets an empty deleter for the shared_ptr, so that the\n  // pointer remains unmanaged.\n  //\n  // As a result, it is possible for the shared_ptrs maintained by the store to\n  // be \"unmanaged\". The type alias `MaybeManagedObserverPointer` is used to\n  // ensure that this detail is apparent in other parts of the container code.\n  using MaybeManagedObserverPointer = std::shared_ptr<Observer>;\n\n  virtual ~ObserverContainerStoreBase() = default;\n\n  /**\n   * Add an observer pointer to the store.\n   *\n   * @param observer     Observer to add.\n   * @return             Whether observer was added (not already present).\n   */\n  virtual bool add(MaybeManagedObserverPointer observer) = 0;\n\n  /**\n   * Remove an observer pointer from the store.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether observer found and removed from store.\n   */\n  virtual bool remove(MaybeManagedObserverPointer observer) = 0;\n\n  /**\n   * Get number of observers in store.\n   *\n   * If called while the store is being iterated, the returned value may not\n   * reflect changes that occurred (e.g., observers added or removed) during\n   * iteration.\n   *\n   * @return             Number of observers in store.\n   */\n  virtual size_t size() const = 0;\n\n  /**\n   * Policy that determines how invokeForEachObserver handles mutations.\n   */\n  enum class InvokeWhileIteratingPolicy {\n    InvokeAdded, // if observer added, invoke fn for it\n    DoNotInvokeAdded, // if observer added, do not invoke fn for it\n    CheckNoChange, // observers must not be added or removed during iteration\n    CheckNoAdded // observers must not be added during iteration\n  };\n\n  /**\n   * Invoke function for each observer in the store.\n   *\n   * @param fn           Function to call for each observer in store.\n   * @param policy       InvokeWhileIteratingPolicy policy.\n   */\n  virtual void invokeForEachObserver(\n      folly::Function<void(MaybeManagedObserverPointer&)>&& fn,\n      const InvokeWhileIteratingPolicy policy) = 0;\n\n  /**\n   * Invoke function for each observer in the store.\n   *\n   * @param fn           Function to call for each observer in store.\n   * @param policy       InvokeWhileIteratingPolicy policy.\n   */\n  virtual void invokeForEachObserver(\n      folly::Function<void(Observer*)>&& fn,\n      const InvokeWhileIteratingPolicy policy) {\n    invokeForEachObserver(\n        [fnL = std::move(fn)](MaybeManagedObserverPointer& observer) mutable {\n          fnL(observer.get());\n        },\n        policy);\n  }\n};\n\n/**\n * Policy for ObserverContainerStore.\n *\n * Defines the udnerlying container type and the default size.\n */\ntemplate <unsigned int ReserveElements = 2>\nstruct ObserverContainerStorePolicyDefault {\n  template <typename Observer>\n  using container = std::conditional_t<\n      !kIsMobile,\n      folly::small_vector<Observer, ReserveElements>,\n      std::vector<Observer>>;\n  const static unsigned int reserve_elements = ReserveElements;\n};\n\n/**\n * Policy-based implementation of ObserverContainerStoreBase.\n */\ntemplate <\n    typename Observer,\n    typename Policy = ObserverContainerStorePolicyDefault<>>\nclass ObserverContainerStore : public ObserverContainerStoreBase<Observer> {\n public:\n  using Base = ObserverContainerStoreBase<Observer>;\n  using InvokeWhileIteratingPolicy = typename Base::InvokeWhileIteratingPolicy;\n\n  /**\n   * Construct a new store, reserving as configured.\n   */\n  ObserverContainerStore() { observers_.reserve(Policy::reserve_elements); }\n\n  /**\n   * Add an observer pointer to the store.\n   *\n   * @param observer     Observer to add.\n   * @return             Whether observer was added (not already present).\n   */\n  bool add(std::shared_ptr<Observer> observer) override {\n    // attempts to add the same observer multiple times are rejected\n    if (std::find(observers_.begin(), observers_.end(), observer) !=\n        observers_.end()) {\n      return false;\n    }\n\n    if (iterating_) {\n      CHECK(maybeCurrentIterationPolicy_.has_value());\n      const auto& policy = maybeCurrentIterationPolicy_.value();\n      FOLLY_EXHAUSTIVE_SWITCH({\n        switch (policy) {\n          case InvokeWhileIteratingPolicy::InvokeAdded:\n          case InvokeWhileIteratingPolicy::DoNotInvokeAdded:\n            break;\n          case InvokeWhileIteratingPolicy::CheckNoChange:\n            folly::terminate_with<std::runtime_error>(\n                \"Cannot add observers while iterating \"\n                \"per current iteration policy (CheckNoChange)\");\n            break;\n          case InvokeWhileIteratingPolicy::CheckNoAdded:\n            folly::terminate_with<std::runtime_error>(\n                \"Cannot add observers while iterating \"\n                \"per current iteration policy (CheckNoAdded)\");\n            break;\n          default:\n            folly::assume_unreachable();\n        }\n      });\n    }\n    observers_.insert(observers_.end(), observer);\n    return true;\n  }\n\n  /**\n   * Remove an observer pointer from the store.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether observer found and removed from store.\n   */\n  bool remove(std::shared_ptr<Observer> observer) override {\n    const auto it = std::find(observers_.begin(), observers_.end(), observer);\n    if (it == observers_.end()) {\n      return false;\n    }\n\n    // if store is currently being iterated, set this element to nullptr and it\n    // will be cleaned up after iteration is completed, else erase immediately.\n    if (iterating_) {\n      CHECK(maybeCurrentIterationPolicy_.has_value());\n      const auto& policy = maybeCurrentIterationPolicy_.value();\n      FOLLY_EXHAUSTIVE_SWITCH({\n        switch (policy) {\n          case InvokeWhileIteratingPolicy::InvokeAdded:\n          case InvokeWhileIteratingPolicy::DoNotInvokeAdded:\n            break;\n          case InvokeWhileIteratingPolicy::CheckNoChange:\n            folly::terminate_with<std::runtime_error>(\n                \"Cannot remove observers while iterating \"\n                \"per current iteration policy (CheckNoChange)\");\n            break;\n          case InvokeWhileIteratingPolicy::CheckNoAdded:\n            break;\n          default:\n            folly::assume_unreachable();\n        }\n      });\n\n      *it = nullptr;\n      removalDuringIteration_ = true;\n    } else {\n      observers_.erase(it);\n    }\n\n    return true;\n  }\n\n  /**\n   * Get number of observers in store.\n   *\n   * If called while the store is being iterated, the returned value may not\n   * reflect changes that occurred (e.g., observers added or removed) during\n   * iteration.\n   *\n   * @return             Number of observers in store.\n   */\n  size_t size() const override { return observers_.size(); }\n\n  /**\n   * Invoke function for each observer in the store.\n   *\n   * @param fn           Function to call for each observer in store.\n   * @param policy       InvokeWhileIteratingPolicy policy.\n   */\n  void invokeForEachObserver(\n      folly::Function<void(typename Base::MaybeManagedObserverPointer&)>&& fn,\n      const typename Base::InvokeWhileIteratingPolicy policy) noexcept\n      override {\n    CHECK(!iterating_)\n        << \"Nested iteration of ObserverContainer is prohibited.\";\n    CHECK(!maybeCurrentIterationPolicy_.has_value())\n        << \"Nested iteration of ObserverContainer is prohibited.\";\n    iterating_ = true;\n    maybeCurrentIterationPolicy_ = policy;\n    SCOPE_EXIT {\n      if (removalDuringIteration_) {\n        // observers removed while we were iterating through container;\n        // remove elements for which the element value is null\n        observers_.erase(\n            std::remove_if(\n                observers_.begin(),\n                observers_.end(),\n                [](const auto& elem) { return elem == nullptr; }),\n            observers_.end());\n      }\n      iterating_ = false;\n      maybeCurrentIterationPolicy_ = folly::none;\n      removalDuringIteration_ = false;\n    };\n\n    const auto numObserversAtStart = observers_.size();\n\n    // iterate through the list using indexes, not iterators, so that the list\n    // can mutate during iteration...\n    for (typename container_type::size_type idx = 0;\n         // observers_.size() cannot decrease during iteration, so it should be\n         // insignificantly faster to check the single size in the common case.\n         idx < numObserversAtStart ||\n         (idx < observers_.size() &&\n          policy == InvokeWhileIteratingPolicy::InvokeAdded);\n         idx++) {\n      auto& observer = observers_.at(idx);\n      if (!observer) { // empty space in list caused by incomplete removal\n        continue;\n      }\n\n      fn(observer);\n    }\n  }\n\n  using Base::invokeForEachObserver;\n\n private:\n  using container_type =\n      typename Policy::template container<std::shared_ptr<Observer>>;\n\n  // The actual list of observers.\n  container_type observers_;\n\n  // Whether we are actively iterating through the list of observers.\n  bool iterating_{false};\n\n  // If we are actively iterating, the corresponding InvokeWhileIteratingPolicy.\n  folly::Optional<InvokeWhileIteratingPolicy> maybeCurrentIterationPolicy_;\n\n  // Whether a removal or addition occurred while we iterating through the list.\n  bool removalDuringIteration_{false};\n};\n\n/**\n * Policy for ObserverContainerBase.\n *\n * @tparam EventEnum    Enum of events that observers can subscribe to.\n *                      Each event must have a unique integer value greater\n *                      than zero.\n *\n * @tparam BitsetSize   Size of bitset, must be greater than or equal to the\n *                      number of events in EventEnum.\n */\ntemplate <typename EventEnum, size_t BitsetSize>\nstruct ObserverContainerBasePolicyDefault {\n  static constexpr size_t bitset_size() { return BitsetSize; }\n  using event_enum = EventEnum;\n};\n\n/**\n * Base ObserverContainer and definition of Observers.\n */\ntemplate <\n    typename ObserverInterface,\n    typename Observed,\n    typename ContainerPolicy>\nclass ObserverContainerBase {\n public:\n  using interface_type = ObserverInterface;\n  using observed_type = Observed;\n  using policy_type = ContainerPolicy;\n  using EventEnum = typename ContainerPolicy::event_enum;\n  using EventEnumIntT = std::underlying_type_t<EventEnum>;\n\n  virtual ~ObserverContainerBase() = default;\n\n  /**\n   * EventSet is used to keep track of the observer events that are enabled.\n   */\n  class ObserverEventSet {\n   public:\n    ObserverEventSet() : bitset_(0) {}\n\n    /**\n     * Enables all events.\n     */\n    void enableAllEvents() { bitset_.set(); }\n\n    /**\n     * Enables the events passed in the initializer list.\n     *\n     * @param eventsEnums  Events to enable.\n\n     */\n    template <typename... EventEnums>\n    void enable(EventEnums... eventEnums) {\n      for (auto&& event : {eventEnums...}) {\n        const auto eventAsInt = static_cast<EventEnumIntT>(event);\n        bitset_.set(eventAsInt);\n      }\n    }\n\n    /**\n     * Returns whether the event passed in is enabled.\n     *\n     * @param event        Event to check.\n     * @return             Whether the passed event is enabled.\n     */\n    bool isEnabled(const EventEnum event) const {\n      const auto eventAsInt = static_cast<EventEnumIntT>(event);\n      return bitset_.test(eventAsInt);\n    }\n\n    /**\n     * Builder that makes it easier to pass EventSet to Observer constructor.\n     */\n    class Builder {\n     public:\n      explicit Builder() = default;\n\n      /**\n       * Enables all events.\n       */\n      Builder&& enableAllEvents() {\n        set_.enableAllEvents();\n        return std::move(*this);\n      }\n\n      /**\n       * Enables the events passed in the intiailizer list.\n       *\n       * @param events       Events to enable.\n       */\n      template <typename... EventEnums>\n      Builder&& enable(EventEnums... eventEnums) {\n        set_.enable(eventEnums...);\n        return std::move(*this);\n      }\n\n      /**\n       * Returns the EventSet that has been built.\n       */\n      ObserverEventSet build() && { return set_; }\n\n     private:\n      ObserverEventSet set_;\n    };\n\n   private:\n    std::bitset<ContainerPolicy::bitset_size()> bitset_{0};\n  };\n\n  /**\n   * Observer base interface.\n   *\n   * This interface includes the events exposed by the subject's observer\n   * interface and the set of events that are provided by the ObserverContainer\n   * (attached/detached/moved/destoyed). It also defines how observers subscribe\n   * to specific events made available by the subject.\n   */\n  class ObserverBase : public ObserverInterface, public DestructorCheck {\n   public:\n    using observed_type = Observed;\n    using interface_type = ObserverInterface;\n\n    using EventSet = ObserverEventSet;\n    using EventSetBuilder = typename ObserverEventSet::Builder;\n\n    ~ObserverBase() override = default;\n\n    /**\n     * Construct a new observer with no event subscriptions.\n     */\n    ObserverBase() {}\n\n    /**\n     * Construct a new observer subscribed to events in the passed EventSet.\n     */\n    explicit ObserverBase(EventSet eventSet) : eventSet_(eventSet) {}\n\n    /**\n     * Base class that can be used to pass context about move operation.\n     */\n    class MoveContext {};\n\n    /**\n     * Base class that can be used to pass context about why object destroyed.\n     */\n    class DestroyContext {};\n\n    /**\n     * Invoked when this observer is attached to an object.\n     *\n     * @param obj   Object that observer is now attached to.\n     */\n    virtual void attached(Observed* /* obj */) noexcept {}\n\n    /**\n     * Invoked if this observer is detached from an object.\n     *\n     * @param obj   Object that observer is no longer attached to.\n     */\n    virtual void detached(Observed* /* obj */) noexcept {}\n\n    /**\n     * Invoked when an observed object's destructor is invoked.\n     *\n     * Destruction of the observed object implicitly implies detached, and thus\n     * detached will not be called if an object is destroyed.\n     *\n     * @param obj           Object being destroyed.\n     * @param ctx           Additional info about what triggered destruction.\n     *                      Not available unless provided by the implementation;\n     *                      if not supported it is a nullptr.\n     */\n    virtual void destroyed(\n        Observed* /* obj */, DestroyContext* /* ctx */) noexcept {}\n\n    /**\n     * Invoked when object being observed changes due to move construction.\n     *\n     * @param oldObj        Object previously being observed.\n     * @param newObj        Object now being observed.\n     * @param ctx           Additional info about what triggered the move.\n     *                      Not available unless provided by the implementation;\n     *                      if not supported it is a nullptr.\n     */\n    virtual void moved(\n        Observed* /* oldObj */,\n        Observed* /* newObj */,\n        MoveContext* /* ctx */) noexcept {}\n\n    /**\n     * Proxy function used to invoke a method defined in the observer interface.\n     *\n     * Can be overridden to enable composition of observers, including event bus\n     * architectures in which multiple handlers act on an event.\n     *\n     * Implementations can remove themselves and add/remove other observers from\n     * the container when handling this call. If new observers are added to the\n     * container, invokeInterfaceMethod will be called on those new observers\n     * as well. If you want to avoid this in your observer implementation, delay\n     * mutation of the container until postInvokeInterfaceMethod is called.\n     *\n     * @param obj           Object associated with observer event.\n     * @param fn            Function that will invoke the method associated with\n     *                      an observer event, passing any event context.\n     * @param maybeEvent    The event enum associated with the invocation.\n     */\n    virtual void invokeInterfaceMethod(\n        Observed* obj,\n        folly::Function<void(ObserverBase*, Observed*)>& fn,\n        folly::Optional<EventEnum> /* maybeEvent */) noexcept {\n      fn(this, obj);\n    }\n\n    /**\n     * Invoked after invokeInterfaceMethod has completed for all observers.\n     *\n     * Can be used to delay mutation of the container after processing of an\n     * event has completed. Implementations can remove themselves and add/remove\n     * other observers from the container when handling this call. However, this\n     * function will only be called for the set of observers in the container\n     * when the preceding call to invokeInterfaceMethod finished.\n     *\n     * @param obj           Object associated with observer event.\n     */\n    virtual void postInvokeInterfaceMethod(Observed* /* obj */) noexcept {}\n\n    /**\n     * Returns the EventSet containing the events the observer wants.\n     */\n    const EventSet& getEventSet() const noexcept { return eventSet_; }\n\n   private:\n    const EventSet eventSet_;\n  };\n\n  /**\n   * Observer interface.\n   *\n   * The interface between an observer container and observers in the container.\n   *\n   * This interface includes methods that are called upon relevant changes to\n   * the observer's status in a container (added/removedFromObserverContainer).\n   *\n   * An observer must not be destroyed while it is in a container. This can be\n   * accomplished by removing the observer from the container on its destruction\n   * or delaying destruction.\n   *\n   * Typical use cases should not attempt to implement this interface and should\n   * instead use a specialization such as ManagedObserver.\n   */\n  class Observer : public ObserverBase {\n   public:\n    using observed_type = Observed;\n    using interface_type = ObserverInterface;\n\n    using EventSet = ObserverEventSet;\n    using EventSetBuilder = typename ObserverEventSet::Builder;\n\n    ~Observer() override = default;\n\n    /**\n     * Construct a new observer with no event subscriptions.\n     */\n    Observer() : ObserverBase() {}\n\n    /**\n     * Construct a new observer subscribed to events in the passed EventSet.\n     */\n    explicit Observer(EventSet eventSet) : ObserverBase(eventSet) {}\n\n    /**\n     * Invoked when this observer has been added to an observer container.\n     *\n     * For the typical observer container implementation a call to `attached`\n     * will proceed a call to this method.\n     *\n     * The observer implementation must ensure that it remains alive as long as\n     * it is in this container.\n     *\n     * @param ctr           Container observer has been added to.\n     */\n    virtual void addedToObserverContainer(\n        ObserverContainerBase* ctr) noexcept = 0;\n\n    /**\n     * Invoked when this observer has been removed from an observer container.\n     *\n     * For the typical observer container implementation a call to `detached`\n     * will have occurred before this method is called.\n     *\n     * @param ctr           Container observer has been removed from.\n     */\n    virtual void removedFromObserverContainer(\n        ObserverContainerBase* ctr) noexcept = 0;\n\n    /**\n     * Invoked when this observer is moved from one container to another.\n     *\n     * Occurs in the case of move construction of a new object during which the\n     * observers in the observer container are shifted from the old object to\n     * the new object.\n     *\n     * @param oldCtr        Container observer has been removed from.\n     * @param newCtr        Container observer has been added to.\n     */\n    virtual void movedToObserverContainer(\n        ObserverContainerBase* oldCtr,\n        ObserverContainerBase* newCtr) noexcept = 0;\n  };\n\n  /**\n   * Returns the object associated with the container (e.g., observed object).\n   *\n   * @return             Return object associated with container or nullptr.\n   */\n  virtual Observed* getObject() const = 0;\n\n  /**\n   * Adds an observer to the container.\n   *\n   * If the observer is already in the container, this is a no-op.\n   *\n   * @param observer     Observer to add.\n   */\n  virtual void addObserver(std::shared_ptr<Observer> observer) = 0;\n\n  /**\n   * Adds an observer to the container.\n   *\n   * If the observer is already in the container, this is a no-op.\n   *\n   * @param observer     Observer to add.\n   */\n  virtual void addObserver(Observer* observer) {\n    // create a shared_ptr holding an unmanaged ptr\n    // this does not trigger control block allocation\n    return addObserver(\n        std::shared_ptr<Observer>(std::shared_ptr<void>(), observer));\n  }\n\n  /**\n   * Removes an observer from the container.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether the observer was found and removed.\n   */\n  virtual bool removeObserver(std::shared_ptr<Observer> observer) = 0;\n\n  /**\n   * Removes an observer from the container.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether the observer was found and removed.\n   */\n  virtual bool removeObserver(Observer* observer) {\n    // create a shared_ptr holding an unmanaged ptr\n    // this does not trigger control block allocation\n    return removeObserver(\n        std::shared_ptr<Observer>(std::shared_ptr<void>(), observer));\n  }\n\n  /**\n   * Get number of observers in container.\n   *\n   * @return             Number of observers in container.\n   */\n  size_t numObservers() const { return getStoreConst().size(); }\n\n  /**\n   * Get a list of observers in the container of type T.\n   *\n   * @tparam T           Type of observer to find.\n   * @return             List of observers in the container of type T.\n   */\n  template <typename T = Observer>\n  std::vector<T*> findObservers() {\n    static_assert(\n        std::is_base_of<Observer, T>::value,\n        \"T must derive from ObserverContainer::Observer\");\n\n    std::vector<T*> matchingObservers;\n    using InvokeWhileIteratingPolicy = typename ObserverContainerStoreBase<\n        Observer>::InvokeWhileIteratingPolicy;\n    getStore().invokeForEachObserver(\n        [&matchingObservers](Observer* observer) {\n          auto castPtr = dynamic_cast<T*>(observer);\n          if (castPtr) {\n            matchingObservers.emplace_back(castPtr);\n          }\n        },\n        InvokeWhileIteratingPolicy::CheckNoChange);\n\n    return matchingObservers;\n  }\n\n  /**\n   * Get all observers.\n   *\n   * @return             List of observers in the container.\n   */\n  std::vector<Observer*> getObservers() { return findObservers<Observer>(); }\n\n  /**\n   * Returns if any observer in the container is subscribed to a given event.\n   *\n   * TODO(bschlinker): The current implementation scans the entire container to\n   * search for an observer subscribed to the requested event; we should cache\n   * this information instead and update the cache on observer add / remove.\n   *\n   * @tparam event       Event in EventEnum.\n   * @return             If there are observers subscribed to the given event.\n   */\n  template <EventEnum event>\n  bool hasObserversForEvent() {\n    bool foundObserverWithEvent = false;\n    using InvokeWhileIteratingPolicy = typename ObserverContainerStoreBase<\n        Observer>::InvokeWhileIteratingPolicy;\n    getStore().invokeForEachObserver(\n        [&foundObserverWithEvent](Observer* observer) {\n          foundObserverWithEvent |= observer->getEventSet().isEnabled(event);\n        },\n        InvokeWhileIteratingPolicy::CheckNoChange);\n    return foundObserverWithEvent;\n  }\n\n  /**\n   * Helper class for observers that attach to single object / container.\n   *\n   * Does not have any thread safety, and thus can only be used if the observer\n   * is driven exclusively by the same thread as the thread that controls the\n   * object being observed.\n   *\n   * Tracks the container the observer is in (if any). If the observer's\n   * destructor is triggered while it is in an container, it will be removed\n   * from the container during the destruction process.\n   */\n  class ManagedObserver : public Observer {\n   public:\n    using Observer::Observer;\n    using EventSet = typename Observer::EventSet;\n    using EventSetBuilder = typename Observer::EventSetBuilder;\n\n    ~ManagedObserver() override { detach(); }\n\n    /**\n     * Detach the observer (if currently attached).\n     *\n     * If the observer is not currently attached, this is a no-op.\n     *\n     * @return       If successfully detached.\n     */\n    bool detach() {\n      if (!ctr_) {\n        return false;\n      }\n      return ctr_->removeObserver(this);\n    }\n\n    /**\n     * Return if the observer is observing an object.\n     *\n     * @return       If observer is observing an object.\n     */\n    bool isObserving() const {\n      return ctr_ != nullptr && ctr_->getObject() != nullptr;\n    }\n\n    /**\n     * Get the object that is being observed or nullptr.\n     *\n     * @return       Object being observed.\n     */\n    Observed* getObservedObject() const {\n      if (!ctr_) {\n        return nullptr;\n      }\n      return ctr_->getObject();\n    }\n\n   private:\n    void addedToObserverContainer(\n        ObserverContainerBase* ctr) noexcept override {\n      CHECK(!ctr_);\n      ctr_ = ctr;\n    }\n\n    void removedFromObserverContainer(\n        ObserverContainerBase* ctr) noexcept override {\n      CHECK_EQ(ctr_, ctr);\n      ctr_ = nullptr;\n    }\n\n    void movedToObserverContainer(\n        ObserverContainerBase* oldCtr,\n        ObserverContainerBase* newCtr) noexcept override {\n      CHECK_EQ(ctr_, oldCtr);\n      CHECK_NE(ctr_, newCtr);\n      ctr_ = newCtr;\n    }\n\n    // Container the observer is in (or nullptr).\n    ObserverContainerBase* ctr_{nullptr};\n  };\n\n protected:\n  virtual ObserverContainerStoreBase<Observer>& getStore() = 0;\n\n  virtual const ObserverContainerStoreBase<Observer>& getStoreConst() const = 0;\n\n  void invokeInterfaceMethodImpl(\n      Observed* observed,\n      folly::Function<void(ObserverBase*, Observed*)>&& fn,\n      const folly::Optional<EventEnum> maybeEvent = folly::none) noexcept {\n    using InvokeWhileIteratingPolicy = typename ObserverContainerStoreBase<\n        Observer>::InvokeWhileIteratingPolicy;\n    getStore().invokeForEachObserver(\n        [observed, maybeEvent, &fn](Observer* observer) {\n          if (!maybeEvent.has_value() ||\n              observer->getEventSet().isEnabled(maybeEvent.value())) {\n            observer->invokeInterfaceMethod(observed, fn, maybeEvent);\n          }\n        },\n        InvokeWhileIteratingPolicy::InvokeAdded);\n    getStore().invokeForEachObserver(\n        [observed, maybeEvent](ObserverBase* observer) {\n          if (!maybeEvent.has_value() ||\n              observer->getEventSet().isEnabled(maybeEvent.value())) {\n            observer->postInvokeInterfaceMethod(observed);\n          }\n        },\n        InvokeWhileIteratingPolicy::DoNotInvokeAdded);\n  }\n};\n\n/**\n * Policy-based implementation of ObserverContainerBase.\n */\ntemplate <\n    typename ObserverInterface,\n    typename Observed,\n    typename ContainerPolicy,\n    typename StorePolicy = ObserverContainerStorePolicyDefault<>,\n    std::size_t MaxConstructorCallbacks = 4>\nclass ObserverContainer\n    : public ObserverContainerBase<\n          ObserverInterface,\n          Observed,\n          ContainerPolicy> {\n public:\n  using ContainerBase =\n      ObserverContainerBase<ObserverInterface, Observed, ContainerPolicy>;\n  using Observer = typename ContainerBase::Observer;\n  using EventEnum = typename ContainerBase::EventEnum;\n  using StoreBase = ObserverContainerStoreBase<Observer>;\n  using ContainerConstructorCallbackList =\n      ConstructorCallbackList<ObserverContainer, MaxConstructorCallbacks>;\n\n  explicit ObserverContainer(Observed* obj)\n      : obj_(CHECK_NOTNULL(obj)), constructorCallbackList_(this) {}\n\n  ObserverContainer(Observed* obj, ObserverContainer&& observerContainer)\n      : obj_(CHECK_NOTNULL(obj)), constructorCallbackList_(this) {\n    using InvokeWhileIteratingPolicy =\n        typename StoreBase::InvokeWhileIteratingPolicy;\n    observerContainer.getStore().invokeForEachObserver(\n        [this, &observerContainer](\n            typename StoreBase::MaybeManagedObserverPointer& observer) {\n          // observer may be a managed pointer (e.g., a shared_ptr), and\n          // invokeForEachObserver passes a reference, so we need to copy the\n          // observer object before calling remove so that it will not be\n          // destroyed upon removal from the old ObserverContainer\n          auto observerCopy = observer;\n          CHECK_NOTNULL(observerCopy.get());\n\n          // remove\n          const bool removed = observerContainer.getStore().remove(observer);\n          CHECK(removed);\n\n          // add to new, operating solely on observerCopy\n          const bool added = getStore().add(observerCopy);\n          CHECK(added);\n          observerCopy->movedToObserverContainer(&observerContainer, this);\n          observerCopy->moved(\n              observerContainer.getObject(), obj_, nullptr /* ctx */);\n        },\n        InvokeWhileIteratingPolicy::CheckNoAdded);\n  }\n\n  ~ObserverContainer() override {\n    using InvokeWhileIteratingPolicy =\n        typename StoreBase::InvokeWhileIteratingPolicy;\n    getStore().invokeForEachObserver(\n        [this](Observer* observer) {\n          DestructorCheck::Safety dc(*observer);\n          observer->destroyed(obj_, nullptr /* ctx */);\n          if (!dc.destroyed()) {\n            observer->removedFromObserverContainer(this);\n          }\n        },\n        InvokeWhileIteratingPolicy::CheckNoAdded);\n  }\n\n  /**\n   * Returns the object associated with the container (e.g., observed object).\n   *\n   * @return             Return object associated with container or nullptr.\n   */\n  Observed* getObject() const override { return obj_; }\n\n  using ContainerBase::addObserver;\n  using ContainerBase::removeObserver;\n\n  /**\n   * Adds an observer to the container.\n   *\n   * If the observer is already in the container, this is a no-op.\n   *\n   * @param observer     Observer to add.\n   */\n  void addObserver(std::shared_ptr<Observer> observer) override {\n    CHECK_NOTNULL(observer.get());\n    if (getStore().add(observer)) {\n      DestructorCheck::Safety dc(*observer);\n      observer->addedToObserverContainer(this);\n      if (!dc.destroyed()) {\n        observer->attached(obj_);\n      }\n    }\n  }\n\n  /**\n   * Removes an observer from the container.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether the observer was found and removed.\n   */\n  bool removeObserver(std::shared_ptr<Observer> observer) override {\n    CHECK_NOTNULL(observer.get());\n    if (getStore().remove(observer)) {\n      DestructorCheck::Safety dc(*observer);\n      observer->detached(obj_);\n      if (!dc.destroyed()) {\n        observer->removedFromObserverContainer(this);\n      }\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * Add a callback fired each time obj of the observed type is constructed.\n   *\n   * Uses ConstructorCallbackList. Can be used to attach observers to all\n   * objects of a given type.\n   *\n   * @throw std::length_error() if installing callback would exceed max allowed\n   */\n  static void addConstructorCallback(\n      typename ContainerConstructorCallbackList::Callback cb) {\n    ContainerConstructorCallbackList::addCallback(std::move(cb));\n  }\n\n  /**\n   * Invokes an observer interface method on observers subscribed to an event.\n   *\n   * See instead `invokeInterfaceMethodAllObservers` to invoke an interface\n   * method on all observers without filtering based on observer event\n   * subscription.\n   *\n   * @tparam event       Associated event in EventEnum. The passed function will\n   *                     only be called for observers subscribed to this event.\n   * @param  fn          Function to call for each observer that takes a pointer\n   *                     to the observer and invokes the interface method.\n   */\n  template <EventEnum event>\n  void invokeInterfaceMethod(\n      folly::Function<void(ObserverInterface*, Observed*)>&& fn) noexcept {\n    this->invokeInterfaceMethodImpl(obj_, std::move(fn), event);\n  }\n\n  /**\n   * Invokes an observer interface method on all observers.\n   *\n   * @param  fn          Function to call for each observer that takes a pointer\n   *                     to the observer and invokes the interface method.\n   */\n  void invokeInterfaceMethodAllObservers(\n      folly::Function<void(ObserverInterface*, Observed*)>&& fn) noexcept {\n    this->invokeInterfaceMethodImpl(obj_, std::move(fn), folly::none);\n  }\n\n  ObserverContainer(const ObserverContainer&) = delete;\n  ObserverContainer(ObserverContainer&&) = delete;\n  ObserverContainer& operator=(const ObserverContainer&) = delete;\n  ObserverContainer& operator=(ObserverContainer&& rhs) = delete;\n\n private:\n  StoreBase& getStore() override { return store_; }\n  const StoreBase& getStoreConst() const override { return store_; }\n\n  // Object being observed.\n  Observed* obj_{nullptr};\n\n  // Store that contains the observers in the container.\n  ObserverContainerStore<Observer, StorePolicy> store_;\n\n  // Enables objects to register constructor callbacks.\n  //\n  // This can be used to enable observers to be attached to all objects of a\n  // given type immediately upon object construction.\n  //\n  // Initialized last and in ObserverContainer instead of ObserverContainerBase\n  // to ensure that the container has completed initialization and is ready to\n  // add observers when any constructor callbacks are called.\n  ContainerConstructorCallbackList constructorCallbackList_{this};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/OperationCancelled.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <exception>\n\nnamespace folly {\n\n/// IMPORTANT: `folly`-internal, do NOT use this in new user code. Instead:\n///\n///   - `co_yield coro::co_stopped_may_throw` to signal a coro was cancelled.\n///\n///   - To check for cancellation in `folly::coro` coroutines, use one of:\n///       // (1) default behavior\n///       co_await coro::co_safe_point\n///       // (2) custom behavior\n///       auto& ctok = co_await coro::co_current_cancellation_token;\n///       if (ctok.isCancellationRequested()) {\n///         /* ... do stuff ... */\n///         co_yield coro::co_stopped_may_throw;\n///       }\n///\n///   - Store `stopped_result` to obtain a `result<T>` or `non_value_result`\n///     in a stopped state. To check for it, use `res.has_stopped()`.\n///\n///   - To avoid depending on `OperationCancelled` in code that would do\n///     `try-catch` in `folly::coro` coroutines, do this instead:\n///\n///       auto res = co_await coro::value_or_error_or_stopped(\n///           mightGetCancelled());\n///       if (auto ex = get_exception<MyErr>(res)) {\n///         // Handle error here; `ex` quacks like `const MyErr*`.\n///       } else if (res.has_stopped()) {\n///         // Handle cancellation here\n///         co_yield coro::co_stopped_may_throw;\n///       } else { // get value, or propagate unhandled errors/cancellation\n///         auto v = co_await folly::or_unwind(std::move(res));\n///       }\n///\n///     OR: If you want to transparently propagate cancellation to the parent\n///     coro, use `value_or_error()` instead.  Then, a \"cancellation\" /\n///     \"stopped\" completion will promptly end the current coro WITHOUT\n///     throwing, and its parent will see a \"stopped\" completion as well.  As\n///     with `co_nothrow`, ask for help in Coroutines@ if your current scope\n///     requires async cleanup / async RAII (such as \"async scope\" usage or\n///     other background work).\n///\n/// Rationale / purpose: For now, `folly` uses the `OperationCancelled`\n/// exception to signal \"this work was stopped\".  However, as of C++26 (see\n/// https://wg21.link/P2300, per the arguments in https://wg21.link/P1677),\n/// standard C++ differentiates between \"value\", \"error\", and \"stopped\"\n/// completions.  Therefore, we ask end-user code to use the above\n/// cancellation-specific constructs, WITHOUT assuming that cancellation /\n/// stopping is implemented as an exception.\nstruct OperationCancelled final : public std::exception {\n  const char* what() const noexcept override {\n    return \"coroutine operation cancelled\";\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Optional.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_optional\n//\n\n#pragma once\n\n/*\n * Optional - For conditional initialization of values, like boost::optional,\n * but with support for move semantics and emplacement.  Reference type support\n * has not been included due to limited use cases and potential confusion with\n * semantics of assignment: Assigning to an optional reference could quite\n * reasonably copy its value or redirect the reference.\n *\n * Optional can be useful when a variable might or might not be needed:\n *\n *  Optional<Logger> maybeLogger = ...;\n *  if (maybeLogger) {\n *    maybeLogger->log(\"hello\");\n *  }\n *\n * Optional enables a 'null' value for types which do not otherwise have\n * nullability, especially useful for parameter passing:\n *\n * void testIterator(const unique_ptr<Iterator>& it,\n *                   initializer_list<int> idsExpected,\n *                   Optional<initializer_list<int>> ranksExpected = none) {\n *   for (int i = 0; it->next(); ++i) {\n *     EXPECT_EQ(it->doc().id(), idsExpected[i]);\n *     if (ranksExpected) {\n *       EXPECT_EQ(it->doc().rank(), (*ranksExpected)[i]);\n *     }\n *   }\n * }\n *\n * Optional models OptionalPointee, so calling 'get_pointer(opt)' will return a\n * pointer to nullptr if the 'opt' is empty, and a pointer to the value if it is\n * not:\n *\n *  Optional<int> maybeInt = ...;\n *  if (int* v = get_pointer(maybeInt)) {\n *    cout << *v << endl;\n *  }\n */\n\n#include <cassert>\n#include <cstddef>\n#include <functional>\n#include <new>\n#include <optional>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/hash/traits.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\ntemplate <class Value>\nclass Optional;\n\nnamespace detail {\nstruct OptionalEmptyTag {};\ntemplate <class Value>\nstruct OptionalPromise;\ntemplate <class Value>\nstruct OptionalPromiseReturn;\n\n// Storage types for Optional, parameterized on Value.\ntemplate <class Value>\nstruct OptionalStorageTriviallyDestructible {\n  union {\n    char emptyState;\n    Value value;\n  };\n  bool hasValue;\n\n  constexpr OptionalStorageTriviallyDestructible()\n      : emptyState(unsafe_default_initialized), hasValue{false} {}\n  void clear() { hasValue = false; }\n};\n\ntemplate <class Value>\nstruct OptionalStorageNonTriviallyDestructible {\n  union {\n    char emptyState;\n    Value value;\n  };\n  bool hasValue;\n\n  constexpr OptionalStorageNonTriviallyDestructible() : hasValue{false} {}\n  ~OptionalStorageNonTriviallyDestructible() { clear(); }\n  OptionalStorageNonTriviallyDestructible(\n      const OptionalStorageNonTriviallyDestructible&) = delete;\n  OptionalStorageNonTriviallyDestructible& operator=(\n      const OptionalStorageNonTriviallyDestructible&) = delete;\n  OptionalStorageNonTriviallyDestructible(\n      OptionalStorageNonTriviallyDestructible&&) = delete;\n  OptionalStorageNonTriviallyDestructible& operator=(\n      OptionalStorageNonTriviallyDestructible&&) = delete;\n\n  void clear() {\n    if (hasValue) {\n      hasValue = false;\n      FOLLY_PUSH_WARNING\n      FOLLY_GCC_DISABLE_WARNING(\"-Wmaybe-uninitialized\")\n      value.~Value();\n      FOLLY_POP_WARNING\n    }\n  }\n};\n\n// Base class holding the storage member and construct helper.\ntemplate <class Value>\nstruct OptionalStorage {\n  using Storage = typename std::conditional<\n      std::is_trivially_destructible_v<Value>,\n      OptionalStorageTriviallyDestructible<Value>,\n      OptionalStorageNonTriviallyDestructible<Value>>::type;\n\n  Storage storage_;\n\n  constexpr OptionalStorage() noexcept = default;\n\n  template <class... Args>\n  void construct(Args&&... args) {\n    const void* ptr = &storage_.value;\n    new (const_cast<void*>(ptr)) Value(std::forward<Args>(args)...);\n    storage_.hasValue = true;\n  }\n};\n\n// Trivial case: when Value is trivially copy constructible AND trivially\n// destructible, all special members are implicitly defaulted (trivial).\ntemplate <\n    class Value,\n    bool = std::is_trivially_copy_constructible_v<Value> &&\n        std::is_trivially_destructible_v<Value>>\nstruct OptionalCopyBase : OptionalStorage<Value> {\n  using OptionalStorage<Value>::OptionalStorage;\n};\n\n// Non-trivial case: user-defined copy constructor.\ntemplate <class Value>\nstruct OptionalCopyBase<Value, false> : OptionalStorage<Value> {\n  using OptionalStorage<Value>::OptionalStorage;\n  OptionalCopyBase() = default;\n  OptionalCopyBase(const OptionalCopyBase& src) noexcept(\n      std::is_nothrow_copy_constructible_v<Value>) {\n    if (src.storage_.hasValue) {\n      this->construct(src.storage_.value);\n    }\n  }\n  OptionalCopyBase(OptionalCopyBase&&) noexcept = default;\n  OptionalCopyBase& operator=(const OptionalCopyBase&) = default;\n  OptionalCopyBase& operator=(OptionalCopyBase&&) noexcept = default;\n  ~OptionalCopyBase() = default;\n};\n\n// Trivial case: when Value is trivially copy constructible, trivially copy\n// assignable, AND trivially destructible, the copy assignment operator is\n// implicitly defaulted (trivial).\ntemplate <\n    class Value,\n    bool = std::is_trivially_copy_constructible_v<Value> &&\n        std::is_trivially_copy_assignable_v<Value> &&\n        std::is_trivially_destructible_v<Value>>\nstruct OptionalCopyAssignBase : OptionalCopyBase<Value> {\n  using OptionalCopyBase<Value>::OptionalCopyBase;\n};\n\n// Non-trivial case: user-defined copy assignment operator.\ntemplate <class Value>\nstruct OptionalCopyAssignBase<Value, false> : OptionalCopyBase<Value> {\n  using OptionalCopyBase<Value>::OptionalCopyBase;\n  OptionalCopyAssignBase() = default;\n  OptionalCopyAssignBase(const OptionalCopyAssignBase&) = default;\n  OptionalCopyAssignBase(OptionalCopyAssignBase&&) noexcept = default;\n  ~OptionalCopyAssignBase() = default;\n  OptionalCopyAssignBase& operator=(OptionalCopyAssignBase&& other) noexcept =\n      default;\n  OptionalCopyAssignBase& operator=(const OptionalCopyAssignBase& src) noexcept(\n      std::is_nothrow_copy_constructible_v<Value> &&\n      std::is_nothrow_copy_assignable_v<Value>) {\n    if (this->storage_.hasValue && src.storage_.hasValue) {\n      this->storage_.value = src.storage_.value;\n    } else if (src.storage_.hasValue) {\n      this->construct(src.storage_.value);\n    } else {\n      this->storage_.clear();\n    }\n    return *this;\n  }\n};\n\n} // namespace detail\n\nstruct None {\n  enum class _secret { _token };\n\n  /**\n   * No default constructor to support both `op = {}` and `op = none`\n   * as syntax for clearing an Optional, just like std::nullopt_t.\n   */\n  explicit constexpr None(_secret) {}\n};\nconstexpr None none{None::_secret::_token};\n\nclass FOLLY_EXPORT OptionalEmptyException : public std::runtime_error {\n public:\n  OptionalEmptyException()\n      : std::runtime_error(\"Empty Optional cannot be unwrapped\") {}\n};\n\n/**\n * Optional is superseded by std::optional. Now that the C++ has a standardized\n * implementation, Optional exists primarily for backward compatibility.\n */\ntemplate <class Value>\nclass Optional : private detail::OptionalCopyAssignBase<Value> {\n  using Base = detail::OptionalCopyAssignBase<Value>;\n\n public:\n  using value_type = Value;\n\n  using promise_type = detail::OptionalPromise<Value>;\n\n  static_assert(\n      !std::is_reference<Value>::value,\n      \"Optional may not be used with reference types\");\n  static_assert(\n      !std::is_abstract<Value>::value,\n      \"Optional may not be used with abstract types\");\n\n  /// Default-constructed Optionals are None.\n  constexpr Optional() noexcept {}\n\n  Optional(const Optional&) = default;\n\n  Optional(Optional&& src) noexcept(\n      std::is_nothrow_move_constructible<Value>::value) {\n    if (src.hasValue()) {\n      this->construct(std::move(src.value()));\n      src.reset();\n    }\n  }\n\n  constexpr /* implicit */ Optional(const None&) noexcept {}\n\n  constexpr /* implicit */ Optional(Value&& newValue) noexcept(\n      std::is_nothrow_move_constructible<Value>::value) {\n    this->construct(std::move(newValue));\n  }\n\n  constexpr /* implicit */ Optional(const Value& newValue) noexcept(\n      std::is_nothrow_copy_constructible<Value>::value) {\n    this->construct(newValue);\n  }\n\n  /**\n   * Creates an Optional with a value, where that value is constructed in-place.\n   *\n   * The std::in_place argument exists so that values can be default constructed\n   * (i.e. have no arguments), since this would otherwise be confused with\n   * default-constructing an Optional, which in turn results in None.\n   */\n  template <typename... Args>\n  constexpr explicit Optional(std::in_place_t, Args&&... args) noexcept(\n      std::is_nothrow_constructible<Value, Args...>::value)\n      : Optional{PrivateConstructor{}, std::forward<Args>(args)...} {}\n\n  template <typename U, typename... Args>\n  constexpr explicit Optional(\n      std::in_place_t,\n      std::initializer_list<U> il,\n      Args&&... args) noexcept(std::\n                                   is_nothrow_constructible<\n                                       Value,\n                                       std::initializer_list<U>,\n                                       Args...>::value)\n      : Optional{PrivateConstructor{}, il, std::forward<Args>(args)...} {}\n\n  // Used only when an Optional is used with coroutines on MSVC\n  /* implicit */ Optional(const detail::OptionalPromiseReturn<Value>& p)\n      : Optional{} {\n    p.promise_->value_ = this;\n  }\n\n  // Conversions to ease migration to std::optional\n\n  /// Allow construction of Optional from std::optional.\n  template <\n      typename U,\n      typename = std::enable_if_t<std::is_same_v<U, std::optional<Value>>>>\n  explicit Optional(U&& newValue) noexcept(\n      std::is_nothrow_move_constructible<Value>::value) {\n    if (newValue.has_value()) {\n      this->construct(std::move(*newValue));\n      newValue.reset();\n    }\n  }\n  template <\n      typename U,\n      typename = std::enable_if_t<std::is_same_v<U, std::optional<Value>>>>\n  explicit Optional(const U& newValue) noexcept(\n      std::is_nothrow_copy_constructible<Value>::value) {\n    if (newValue.has_value()) {\n      this->construct(*newValue);\n    }\n  }\n  /// Allow explicit cast to std::optional\n  /// @methodset Migration\n  explicit operator std::optional<Value>() && noexcept(\n      std::is_nothrow_move_constructible<Value>::value) {\n    std::optional<Value> ret = this->storage_.hasValue\n        ? std::optional<Value>(std::move(this->storage_.value))\n        : std::nullopt;\n    reset();\n    return ret;\n  }\n  explicit operator std::optional<Value>() const& noexcept(\n      std::is_nothrow_copy_constructible<Value>::value) {\n    return this->storage_.hasValue\n        ? std::optional<Value>(this->storage_.value)\n        : std::nullopt;\n  }\n\n  std::optional<Value> toStdOptional() && noexcept {\n    if (has_value()) {\n      auto opt = std::optional<Value>(std::move(value()));\n      reset();\n      return opt;\n    }\n    return std::nullopt;\n  }\n\n  std::optional<Value> toStdOptional() const& noexcept {\n    if (has_value()) {\n      return std::optional<Value>(value());\n    }\n    return std::nullopt;\n  }\n\n  /// Set the Optional\n  /// @methodset Modifiers\n  void assign(const None&) { reset(); }\n\n  void assign(Optional&& src) {\n    if (this != &src) {\n      if (src.hasValue()) {\n        assign(std::move(src.value()));\n        src.reset();\n      } else {\n        reset();\n      }\n    }\n  }\n\n  void assign(const Optional& src) {\n    if (src.hasValue()) {\n      assign(src.value());\n    } else {\n      reset();\n    }\n  }\n\n  void assign(Value&& newValue) {\n    if (hasValue()) {\n      FOLLY_PUSH_WARNING\n      FOLLY_GCC_DISABLE_WARNING(\"-Wmaybe-uninitialized\")\n      this->storage_.value = std::move(newValue);\n      FOLLY_POP_WARNING\n    } else {\n      this->construct(std::move(newValue));\n    }\n  }\n\n  void assign(const Value& newValue) {\n    if (hasValue()) {\n      FOLLY_PUSH_WARNING\n      FOLLY_GCC_DISABLE_WARNING(\"-Wmaybe-uninitialized\")\n      this->storage_.value = newValue;\n      FOLLY_POP_WARNING\n    } else {\n      this->construct(newValue);\n    }\n  }\n\n  /// @methodset Modifiers\n  Optional& operator=(None) noexcept {\n    reset();\n    return *this;\n  }\n\n  template <class Arg>\n  Optional& operator=(Arg&& arg) {\n    assign(std::forward<Arg>(arg));\n    return *this;\n  }\n\n  Optional& operator=(Optional&& other) noexcept(\n      std::is_nothrow_move_assignable<Value>::value) {\n    assign(std::move(other));\n    return *this;\n  }\n\n  Optional& operator=(const Optional&) = default;\n\n  /// Construct a new value in the Optional, in-place.\n  /// @methodset Modifiers\n  template <class... Args>\n  Value& emplace(Args&&... args) {\n    reset();\n    this->construct(std::forward<Args>(args)...);\n    return value();\n  }\n\n  template <class U, class... Args>\n  typename std::enable_if<\n      std::is_constructible<Value, std::initializer_list<U>&, Args&&...>::value,\n      Value&>::type\n  emplace(std::initializer_list<U> ilist, Args&&... args) {\n    reset();\n    this->construct(ilist, std::forward<Args>(args)...);\n    return value();\n  }\n\n  /// Set the Optional to None\n  /// @methodset Modifiers\n  void reset() noexcept { this->storage_.clear(); }\n\n  /// Set the Optional to None\n  /// @methodset Modifiers\n  void clear() noexcept { reset(); }\n\n  /// @methodset Modifiers\n  void swap(Optional& that) noexcept(std::is_nothrow_swappable_v<Value>) {\n    if (hasValue() && that.hasValue()) {\n      using std::swap;\n      swap(value(), that.value());\n    } else if (hasValue()) {\n      that.emplace(std::move(value()));\n      reset();\n    } else if (that.hasValue()) {\n      emplace(std::move(that.value()));\n      that.reset();\n    }\n  }\n\n  /// Get the value. Must have value.\n  /// @methodset Getters\n  constexpr const Value& value() const& {\n    require_value();\n    return this->storage_.value;\n  }\n\n  constexpr Value& value() & {\n    require_value();\n    return this->storage_.value;\n  }\n\n  constexpr Value&& value() && {\n    require_value();\n    return std::move(this->storage_.value);\n  }\n\n  constexpr const Value&& value() const&& {\n    require_value();\n    return std::move(this->storage_.value);\n  }\n\n  /// Get the value by pointer; nullptr if None.\n  /// @methodset Getters\n  const Value* get_pointer() const& {\n    return this->storage_.hasValue ? &this->storage_.value : nullptr;\n  }\n  Value* get_pointer() & {\n    return this->storage_.hasValue ? &this->storage_.value : nullptr;\n  }\n  Value* get_pointer() && = delete;\n\n  /// Does this Optional have a value.\n  /// @methodset Observers\n  constexpr bool has_value() const noexcept { return this->storage_.hasValue; }\n\n  /// Does this Optional have a value.\n  /// @methodset Observers\n  constexpr bool hasValue() const noexcept { return has_value(); }\n\n  /// Does this Optional have a value.\n  /// @methodset Observers\n  constexpr explicit operator bool() const noexcept { return has_value(); }\n\n  /// Get the value. Must have value.\n  /// @methodset Getters\n  constexpr const Value& operator*() const& { return value(); }\n  constexpr Value& operator*() & { return value(); }\n  constexpr const Value&& operator*() const&& { return std::move(value()); }\n  constexpr Value&& operator*() && { return std::move(value()); }\n\n  /// Get the value. Must have value.\n  /// @methodset Getters\n  constexpr const Value* operator->() const { return &value(); }\n  constexpr Value* operator->() { return &value(); }\n\n  /// Return a copy of the value if set, or a given default if not.\n  /// @methodset Getters\n  template <class U>\n  constexpr Value value_or(U&& dflt) const& {\n    if (this->storage_.hasValue) {\n      return this->storage_.value;\n    }\n\n    return std::forward<U>(dflt);\n  }\n\n  template <class U>\n  constexpr Value value_or(U&& dflt) && {\n    if (this->storage_.hasValue) {\n      return std::move(this->storage_.value);\n    }\n\n    return std::forward<U>(dflt);\n  }\n\n private:\n  friend struct detail::OptionalPromiseReturn<Value>;\n\n  template <class T>\n  friend constexpr Optional<std::decay_t<T>> make_optional(T&&);\n  template <class T, class... Args>\n  friend constexpr Optional<T> make_optional(Args&&... args);\n  template <class T, class U, class... As>\n  friend constexpr Optional<T> make_optional(std::initializer_list<U>, As&&...);\n\n  /**\n   * Construct the optional in place, this is duplicated as a non-explicit\n   * constructor to allow returning values that are non-movable from\n   * make_optional using list initialization.\n   *\n   * Until C++17, at which point this will become unnecessary because of\n   * specified prvalue elision.\n   */\n  struct PrivateConstructor {\n    explicit PrivateConstructor() = default;\n  };\n  template <typename... Args>\n  constexpr Optional(PrivateConstructor, Args&&... args) noexcept(\n      std::is_nothrow_constructible<Value, Args&&...>::value) {\n    this->construct(std::forward<Args>(args)...);\n  }\n  // for when coroutine promise return-object conversion is eager\n  explicit Optional(detail::OptionalEmptyTag, Optional*& pointer) noexcept {\n    pointer = this;\n  }\n\n  void require_value() const {\n    if (!this->storage_.hasValue) {\n      throw_exception<OptionalEmptyException>();\n    }\n  }\n};\n\ntemplate <class T>\nconst T* get_pointer(const Optional<T>& opt) {\n  return opt.get_pointer();\n}\n\ntemplate <class T>\nT* get_pointer(Optional<T>& opt) {\n  return opt.get_pointer();\n}\n\ntemplate <class T>\nvoid swap(Optional<T>& a, Optional<T>& b) noexcept(noexcept(a.swap(b))) {\n  a.swap(b);\n}\n\ntemplate <class T>\nconstexpr Optional<std::decay_t<T>> make_optional(T&& v) {\n  using PrivateConstructor =\n      typename folly::Optional<std::decay_t<T>>::PrivateConstructor;\n  return {PrivateConstructor{}, std::forward<T>(v)};\n}\n\ntemplate <class T, class... Args>\nconstexpr folly::Optional<T> make_optional(Args&&... args) {\n  using PrivateConstructor = typename folly::Optional<T>::PrivateConstructor;\n  return {PrivateConstructor{}, std::forward<Args>(args)...};\n}\n\ntemplate <class T, class U, class... Args>\nconstexpr folly::Optional<T> make_optional(\n    std::initializer_list<U> il, Args&&... args) {\n  using PrivateConstructor = typename folly::Optional<T>::PrivateConstructor;\n  return {PrivateConstructor{}, il, std::forward<Args>(args)...};\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// Comparisons.\n\ntemplate <class U, class V>\nconstexpr bool operator==(const Optional<U>& a, const V& b) {\n  return a.hasValue() && a.value() == b;\n}\n\ntemplate <class U, class V>\nconstexpr bool operator!=(const Optional<U>& a, const V& b) {\n  return !(a == b);\n}\n\ntemplate <class U, class V>\nconstexpr bool operator==(const U& a, const Optional<V>& b) {\n  return b.hasValue() && b.value() == a;\n}\n\ntemplate <class U, class V>\nconstexpr bool operator!=(const U& a, const Optional<V>& b) {\n  return !(a == b);\n}\n\ntemplate <class U, class V>\nconstexpr bool operator==(const Optional<U>& a, const Optional<V>& b) {\n  if (a.hasValue() != b.hasValue()) {\n    return false;\n  }\n  if (a.hasValue()) {\n    return a.value() == b.value();\n  }\n  return true;\n}\n\ntemplate <class U, class V>\nconstexpr bool operator!=(const Optional<U>& a, const Optional<V>& b) {\n  return !(a == b);\n}\n\ntemplate <class U, class V>\nconstexpr bool operator<(const Optional<U>& a, const Optional<V>& b) {\n  if (a.hasValue() != b.hasValue()) {\n    return a.hasValue() < b.hasValue();\n  }\n  if (a.hasValue()) {\n    return a.value() < b.value();\n  }\n  return false;\n}\n\ntemplate <class U, class V>\nconstexpr bool operator>(const Optional<U>& a, const Optional<V>& b) {\n  return b < a;\n}\n\ntemplate <class U, class V>\nconstexpr bool operator<=(const Optional<U>& a, const Optional<V>& b) {\n  return !(b < a);\n}\n\ntemplate <class U, class V>\nconstexpr bool operator>=(const Optional<U>& a, const Optional<V>& b) {\n  return !(a < b);\n}\n\n// Suppress comparability of Optional<T> with T, despite implicit conversion.\ntemplate <class V>\nbool operator<(const Optional<V>&, const V& other) = delete;\ntemplate <class V>\nbool operator<=(const Optional<V>&, const V& other) = delete;\ntemplate <class V>\nbool operator>=(const Optional<V>&, const V& other) = delete;\ntemplate <class V>\nbool operator>(const Optional<V>&, const V& other) = delete;\ntemplate <class V>\nbool operator<(const V& other, const Optional<V>&) = delete;\ntemplate <class V>\nbool operator<=(const V& other, const Optional<V>&) = delete;\ntemplate <class V>\nbool operator>=(const V& other, const Optional<V>&) = delete;\ntemplate <class V>\nbool operator>(const V& other, const Optional<V>&) = delete;\n\n// Comparisons with none\ntemplate <class V>\nconstexpr bool operator==(const Optional<V>& a, None) noexcept {\n  return !a.hasValue();\n}\ntemplate <class V>\nconstexpr bool operator==(None, const Optional<V>& a) noexcept {\n  return !a.hasValue();\n}\ntemplate <class V>\nconstexpr bool operator<(const Optional<V>&, None) noexcept {\n  return false;\n}\ntemplate <class V>\nconstexpr bool operator<(None, const Optional<V>& a) noexcept {\n  return a.hasValue();\n}\ntemplate <class V>\nconstexpr bool operator>(const Optional<V>& a, None) noexcept {\n  return a.hasValue();\n}\ntemplate <class V>\nconstexpr bool operator>(None, const Optional<V>&) noexcept {\n  return false;\n}\ntemplate <class V>\nconstexpr bool operator<=(None, const Optional<V>&) noexcept {\n  return true;\n}\ntemplate <class V>\nconstexpr bool operator<=(const Optional<V>& a, None) noexcept {\n  return !a.hasValue();\n}\ntemplate <class V>\nconstexpr bool operator>=(const Optional<V>&, None) noexcept {\n  return true;\n}\ntemplate <class V>\nconstexpr bool operator>=(None, const Optional<V>& a) noexcept {\n  return !a.hasValue();\n}\n\n///////////////////////////////////////////////////////////////////////////////\n\n} // namespace folly\n\n// Allow usage of Optional<T> in std::unordered_map and std::unordered_set\nFOLLY_NAMESPACE_STD_BEGIN\ntemplate <class T>\nstruct hash<\n    folly::enable_std_hash_helper<folly::Optional<T>, remove_const_t<T>>> {\n  size_t operator()(folly::Optional<T> const& obj) const {\n    return static_cast<bool>(obj) ? hash<remove_const_t<T>>()(*obj) : 0;\n  }\n};\nFOLLY_NAMESPACE_STD_END\n\n// Enable the use of folly::Optional with `co_await`\n// Inspired by https://github.com/toby-allsopp/coroutine_monad\n#if FOLLY_HAS_COROUTINES\n#include <folly/coro/Coroutine.h>\n\nnamespace folly {\nnamespace detail {\ntemplate <typename Value>\nstruct OptionalPromise;\n\ntemplate <typename Value>\nstruct OptionalPromiseReturn {\n  Optional<Value> storage_;\n  Optional<Value>*& pointer_;\n\n  /* implicit */ OptionalPromiseReturn(OptionalPromise<Value>& p) noexcept\n      : pointer_{p.value_} {\n    pointer_ = &storage_;\n  }\n  OptionalPromiseReturn(OptionalPromiseReturn const&) = delete;\n  // letting dtor be trivial makes the coroutine crash\n  // TODO: fix clang/llvm codegen\n  ~OptionalPromiseReturn() {}\n  /* implicit */ operator Optional<Value>() {\n    // handle both deferred and eager return-object conversion behaviors\n    // see docs for detect_promise_return_object_eager_conversion\n    if (folly::coro::detect_promise_return_object_eager_conversion()) {\n      assert(!storage_.has_value());\n      return Optional{OptionalEmptyTag{}, pointer_}; // eager\n    } else {\n      return std::move(storage_); // deferred\n    }\n  }\n};\n\ntemplate <typename Value>\nstruct OptionalPromise {\n  Optional<Value>* value_ = nullptr;\n  OptionalPromise() = default;\n  OptionalPromise(OptionalPromise const&) = delete;\n  OptionalPromiseReturn<Value> get_return_object() noexcept { return *this; }\n  coro::suspend_never initial_suspend() const noexcept { return {}; }\n  coro::suspend_never final_suspend() const noexcept { return {}; }\n  template <typename U = Value>\n  void return_value(U&& u) {\n    *value_ = static_cast<U&&>(u);\n  }\n  void unhandled_exception() {\n    // Technically, throwing from unhandled_exception is underspecified:\n    // https://github.com/GorNishanov/CoroutineWording/issues/17\n    rethrow_current_exception();\n  }\n};\n\ntemplate <typename Value>\nstruct OptionalAwaitable {\n  Optional<Value> o_;\n  bool await_ready() const noexcept { return o_.hasValue(); }\n  Value await_resume() { return std::move(o_.value()); }\n\n  // Explicitly only allow suspension into an OptionalPromise\n  template <typename U>\n  void await_suspend(\n      coro::coroutine_handle<OptionalPromise<U>> h) const noexcept {\n    // Abort the rest of the coroutine. resume() is not going to be called\n    h.destroy();\n  }\n};\n} // namespace detail\n\ntemplate <typename Value>\ndetail::OptionalAwaitable<Value>\n/* implicit */ operator co_await(Optional<Value> o) {\n  return {std::move(o)};\n}\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/Overload.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n\n/**\n * folly implementation of `std::overload` like functionality\n *\n * Example:\n *  struct One {};\n *  struct Two {};\n *  boost::variant<One, Two> value;\n *\n *  variant_match(value,\n *    [] (const One& one) { ... },\n *    [] (const Two& two) { ... });\n */\n\nnamespace folly {\n\nnamespace detail {\n\n// MSVC does not implement noexcept deduction https://godbolt.org/z/Mxdjao1q6\n#if !defined(_MSC_VER)\n#define FOLLY_DETAIL_NOEXCEPT_SPECIFICATION noexcept(Noexcept)\n#define FOLLY_DETAIL_NOEXCEPT_DECLARATION bool Noexcept,\n#else\n#define FOLLY_DETAIL_NOEXCEPT_SPECIFICATION\n#define FOLLY_DETAIL_NOEXCEPT_DECLARATION\n#endif\n\ntemplate <typename T>\nstruct FunctionClassType {\n  using type = T;\n};\n\n// You cannot derive from a pointer to function, so wrap it in a class\n\ntemplate <FOLLY_DETAIL_NOEXCEPT_DECLARATION typename Return, typename... Args>\nstruct FunctionClassType<Return (*)(\n    Args...) FOLLY_DETAIL_NOEXCEPT_SPECIFICATION> {\n  using Ptr = Return (*)(Args...) FOLLY_DETAIL_NOEXCEPT_SPECIFICATION;\n  struct type {\n    /* implicit */ constexpr type(Ptr function) noexcept\n        : function_(function) {}\n    constexpr auto operator()(Args... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return function_(std::forward<Args>(args)...);\n    }\n\n   private:\n    Ptr function_;\n  };\n};\n\n// You cannot derive from a pointer to member function, so wrap it in a class.\n// This cannot be implemented with\n// `std::enable_if_t<std::is_member_pointer_v<T>>` because you don't get\n// preferred overload resolution on the object type to match const / ref\n// qualifiers.\n\ntemplate <\n    FOLLY_DETAIL_NOEXCEPT_DECLARATION typename Return,\n    typename Self,\n    typename... Args>\nstruct FunctionClassType<Return (Self::*)(\n    Args...) FOLLY_DETAIL_NOEXCEPT_SPECIFICATION> {\n  using Ptr = Return (Self::*)(Args...) FOLLY_DETAIL_NOEXCEPT_SPECIFICATION;\n  struct type {\n    /* implicit */ constexpr type(Ptr memberPointer) noexcept\n        : memberPointer_(memberPointer) {}\n    constexpr auto operator()(Self& self, Args... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return (self.*memberPointer_)(std::forward<Args>(args)...);\n    }\n    constexpr auto operator()(Self&& self, Args... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return (self.*memberPointer_)(std::forward<Args>(args)...);\n    }\n\n   private:\n    Ptr memberPointer_;\n  };\n};\n\ntemplate <\n    FOLLY_DETAIL_NOEXCEPT_DECLARATION typename Return,\n    typename Self,\n    typename... Args>\nstruct FunctionClassType<Return (Self::*)(\n    Args...) const FOLLY_DETAIL_NOEXCEPT_SPECIFICATION> {\n  using Ptr =\n      Return (Self::*)(Args...) const FOLLY_DETAIL_NOEXCEPT_SPECIFICATION;\n  struct type {\n    /* implicit */ constexpr type(Ptr memberPointer) noexcept\n        : memberPointer_(memberPointer) {}\n    constexpr auto operator()(const Self& self, Args... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return (self.*memberPointer_)(std::forward<Args>(args)...);\n    }\n\n   private:\n    Ptr memberPointer_;\n  };\n};\n\ntemplate <\n    FOLLY_DETAIL_NOEXCEPT_DECLARATION typename Return,\n    typename Self,\n    typename... Args>\nstruct FunctionClassType<\n    Return (Self::*)(Args...) & FOLLY_DETAIL_NOEXCEPT_SPECIFICATION> {\n  using Ptr = Return (Self::*)(Args...) & FOLLY_DETAIL_NOEXCEPT_SPECIFICATION;\n  struct type {\n    /* implicit */ constexpr type(Ptr memberPointer) noexcept\n        : memberPointer_(memberPointer) {}\n    constexpr auto operator()(Self& self, Args&&... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return (self.*memberPointer_)(std::forward<Args>(args)...);\n    }\n\n   private:\n    Ptr memberPointer_;\n  };\n};\n\ntemplate <\n    FOLLY_DETAIL_NOEXCEPT_DECLARATION typename Return,\n    typename Self,\n    typename... Args>\nstruct FunctionClassType<\n    Return (Self::*)(Args...) const & FOLLY_DETAIL_NOEXCEPT_SPECIFICATION> {\n  using Ptr =\n      Return (Self::*)(Args...) const& FOLLY_DETAIL_NOEXCEPT_SPECIFICATION;\n  struct type {\n    /* implicit */ constexpr type(Ptr memberPointer) noexcept\n        : memberPointer_(memberPointer) {}\n    constexpr auto operator()(const Self& self, Args... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return (self.*memberPointer_)(std::forward<Args>(args)...);\n    }\n\n   private:\n    Ptr memberPointer_;\n  };\n};\n\ntemplate <\n    FOLLY_DETAIL_NOEXCEPT_DECLARATION typename Return,\n    typename Self,\n    typename... Args>\nstruct FunctionClassType<\n    Return (Self::*)(Args...) && FOLLY_DETAIL_NOEXCEPT_SPECIFICATION> {\n  using Ptr = Return (Self::*)(Args...) && FOLLY_DETAIL_NOEXCEPT_SPECIFICATION;\n  struct type {\n    /* implicit */ constexpr type(Ptr memberPointer) noexcept\n        : memberPointer_(memberPointer) {}\n    constexpr auto operator()(Self&& self, Args... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return (std::move(self).*memberPointer_)(std::forward<Args>(args)...);\n    }\n\n   private:\n    Ptr memberPointer_;\n  };\n};\n\ntemplate <\n    FOLLY_DETAIL_NOEXCEPT_DECLARATION typename Return,\n    typename Self,\n    typename... Args>\nstruct FunctionClassType<\n    Return (Self::*)(Args...) const && FOLLY_DETAIL_NOEXCEPT_SPECIFICATION> {\n  using Ptr =\n      Return (Self::*)(Args...) const&& FOLLY_DETAIL_NOEXCEPT_SPECIFICATION;\n  struct type {\n    /* implicit */ constexpr type(Ptr memberPointer) noexcept\n        : memberPointer_(memberPointer) {}\n    constexpr auto operator()(const Self&& self, Args... args) const\n        FOLLY_DETAIL_NOEXCEPT_SPECIFICATION->Return {\n      return (std::move(self).*memberPointer_)(std::forward<Args>(args)...);\n    }\n\n   private:\n    Ptr memberPointer_;\n  };\n};\n\ntemplate <typename T, typename Self>\nstruct FunctionClassType<T Self::*> {\n  using Ptr = T Self::*;\n  struct type {\n    /* implicit */ constexpr type(Ptr memberPointer) noexcept\n        : memberPointer_(memberPointer) {}\n    constexpr auto operator()(Self& self) const noexcept -> T& {\n      return self.*memberPointer_;\n    }\n    constexpr auto operator()(const Self& self) const noexcept -> const T& {\n      return self.*memberPointer_;\n    }\n    constexpr auto operator()(Self&& self) const noexcept -> T&& {\n      return std::move(self).*memberPointer_;\n    }\n    constexpr auto operator()(const Self&& self) const noexcept -> const T&& {\n      return std::move(self).*memberPointer_;\n    }\n\n   private:\n    Ptr memberPointer_;\n  };\n};\n\n#undef FOLLY_DETAIL_NOEXCEPT_DECLARATION\n#undef FOLLY_DETAIL_NOEXCEPT_SPECIFICATION\n\ntemplate <typename...>\nstruct Overload {};\n\ntemplate <typename Case, typename... Cases>\nstruct Overload<Case, Cases...> : Overload<Cases...>, Case {\n  explicit constexpr Overload(Case c, Cases... cs)\n      : Overload<Cases...>(std::move(cs)...), Case(std::move(c)) {}\n\n  using Case::operator();\n  using Overload<Cases...>::operator();\n};\n\ntemplate <typename Case>\nstruct Overload<Case> : Case {\n  explicit constexpr Overload(Case c) : Case(std::move(c)) {}\n\n  using Case::operator();\n};\n} // namespace detail\n\n/*\n * Combine multiple `Cases` in one function object\n *\n * Each element of `Cases` must be a class type with `operator()`, a pointer to\n * a function, a pointer to a member function, or a pointer to member data.\n * `final` types and pointers to `volatile`-qualified member functions are not\n * supported. If the `Case` type is a pointer to member, the first argument must\n * be a class type or reference to class type (pointer to class type is not\n * supported).\n */\ntemplate <typename... Cases>\nconstexpr decltype(auto) overload(Cases&&... cases) {\n  return detail::Overload<typename detail::FunctionClassType<\n      typename std::decay<Cases>::type>::type...>{\n      std::forward<Cases>(cases)...};\n}\n\nnamespace overload_detail {\nFOLLY_CREATE_MEMBER_INVOKER(valueless_by_exception, valueless_by_exception);\nFOLLY_PUSH_WARNING\nFOLLY_MSVC_DISABLE_WARNING(4003) /* not enough arguments to macro */\nFOLLY_CREATE_FREE_INVOKER(visit, visit);\nFOLLY_CREATE_FREE_INVOKER(apply_visitor, apply_visitor);\nFOLLY_POP_WARNING\n} // namespace overload_detail\n\n/*\n * Match `Variant` with one of the `Cases`\n *\n * Note: you can also use `[] (const auto&) {...}` as default case\n *\n * Selects `visit` if `v.valueless_by_exception()` available and the call to\n * `visit` is valid (e.g. `std::variant`). Otherwise, selects `apply_visitor`\n * (e.g. `boost::variant`, `folly::DiscriminatedPtr`).\n */\ntemplate <typename Variant, typename... Cases>\ndecltype(auto) variant_match(Variant&& variant, Cases&&... cases) {\n  using invoker = std::conditional_t<\n      folly::Conjunction<\n          is_invocable<overload_detail::valueless_by_exception, Variant>,\n          is_invocable<\n              overload_detail::visit,\n              decltype(overload(std::forward<Cases>(cases)...)),\n              Variant>>::value,\n      overload_detail::visit,\n      overload_detail::apply_visitor>;\n  return invoker{}(\n      overload(std::forward<Cases>(cases)...), std::forward<Variant>(variant));\n}\n\ntemplate <typename R, typename Variant, typename... Cases>\nR variant_match(Variant&& variant, Cases&&... cases) {\n  auto f = [&](auto&& v) -> R {\n    if constexpr (std::is_void<R>::value) {\n      overload(std::forward<Cases>(cases)...)(std::forward<decltype(v)>(v));\n    } else {\n      return overload(std::forward<Cases>(cases)...)(\n          std::forward<decltype(v)>(v));\n    }\n  };\n  using invoker = std::conditional_t<\n      folly::Conjunction<\n          is_invocable<overload_detail::valueless_by_exception, Variant>,\n          is_invocable<overload_detail::visit, decltype(f), Variant>>::value,\n      overload_detail::visit,\n      overload_detail::apply_visitor>;\n  return invoker{}(f, std::forward<Variant>(variant));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/PackedSyncPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/synchronization/SmallLocks.h>\n\n#if FOLLY_X64 || FOLLY_PPC64 || FOLLY_AARCH64\n#define FOLLY_HAS_PACKED_SYNC_PTR 1\n#else\n#define FOLLY_HAS_PACKED_SYNC_PTR 0\n#endif\n\n#if FOLLY_HAS_PACKED_SYNC_PTR\n\n/*\n * An 8-byte pointer with an integrated spin lock and 15-bit integer\n * (you can use this for a size of the allocation, if you want, or\n * something else, or nothing).\n *\n * This is using an x64-specific detail about the effective virtual\n * address space.  Long story short: the upper two bytes of all our\n * pointers will be zero in reality---and if you have a couple billion\n * such pointers in core, it makes pretty good sense to try to make\n * use of that memory.  The exact details can be perused here:\n *\n *    http://en.wikipedia.org/wiki/X86-64#Canonical_form_addresses\n *\n * This is not a \"smart\" pointer: nothing automagical is going on\n * here.  Locking is up to the user.  Resource deallocation is up to\n * the user.  Locks are never acquired or released outside explicit\n * calls to lock() and unlock().\n *\n * Change the value of the raw pointer with set(), but you must hold\n * the lock when calling this function if multiple threads could be\n * using this class.\n *\n * TODO(jdelong): should we use the low order bit for the lock, so we\n * get a whole 16-bits for our integer?  (There's also 2 more bits\n * down there if the pointer comes from malloc.)\n */\n\nnamespace folly {\n\ntemplate <class T>\nclass PackedSyncPtr {\n  // This just allows using this class even with T=void.  Attempting\n  // to use the operator* or operator[] on a PackedSyncPtr<void> will\n  // still properly result in a compile error.\n  using reference = typename std::add_lvalue_reference<T>::type;\n\n public:\n  /*\n   * If you default construct one of these, you must call this init()\n   * function before using it.\n   *\n   * (We are avoiding a constructor to ensure gcc allows us to put\n   * this class in packed structures.)\n   */\n  void init(T* initialPtr = nullptr, uint16_t initialExtra = 0) {\n    auto intPtr = reinterpret_cast<uintptr_t>(initialPtr);\n    CHECK(!(intPtr >> 48));\n    data_.init(intPtr << 16);\n    setExtra(initialExtra);\n  }\n\n  /*\n   * Sets a new pointer.  You must hold the lock when calling this\n   * function, or else be able to guarantee no other threads could be\n   * using this PackedSyncPtr<>.\n   */\n  void set(T* t) {\n    auto intPtr = reinterpret_cast<uintptr_t>(t);\n    CHECK(!(intPtr >> 48));\n    data_.setData((intPtr << 16) | uintptr_t(extra()));\n  }\n\n  /*\n   * Get the pointer.\n   *\n   * You can call any of these without holding the lock, with the\n   * normal types of behavior you'll get on x64 from reading a pointer\n   * without locking.\n   */\n  T* get() const { return reinterpret_cast<T*>(data_.getData() >> 16); }\n  T* operator->() const { return get(); }\n  reference operator*() const { return *get(); }\n  reference operator[](std::ptrdiff_t i) const { return get()[i]; }\n\n  // Synchronization (logically const, even though this mutates our\n  // locked state: you can lock a const PackedSyncPtr<T> to read it).\n  void lock() const { data_.lock(); }\n  void unlock() const { data_.unlock(); }\n  bool try_lock() const { return data_.try_lock(); }\n\n  /*\n   * Access extra data stored in unused bytes of the pointer.\n   *\n   * It is ok to call this without holding the lock.\n   */\n  uint16_t extra() const { return data_.getData() & 0xffff; }\n\n  /*\n   * Don't try to put anything into this that has the high bit set:\n   * that's what we're using for the mutex.\n   *\n   * Don't call this without holding the lock.\n   */\n  void setExtra(uint16_t extra) {\n    CHECK(!(extra & 0x8000));\n    auto ptr = data_.getData();\n    data_.setData(uintptr_t(extra) | (ptr & (-1ull << 16)));\n  }\n\n private:\n  PicoSpinLock<uintptr_t, 15> data_;\n};\n\nstatic_assert(\n    std::is_standard_layout<PackedSyncPtr<void>>::value &&\n        std::is_trivial<PackedSyncPtr<void>>::value,\n    \"PackedSyncPtr must be kept a POD type.\");\nstatic_assert(\n    sizeof(PackedSyncPtr<void>) == 8,\n    \"PackedSyncPtr should be only 8 bytes---something is \"\n    \"messed up\");\n\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os, const PackedSyncPtr<T>& ptr) {\n  os << \"PackedSyncPtr(\" << ptr.get() << \", \" << ptr.extra() << \")\";\n  return os;\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/Padded.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cassert>\n#include <cstdint>\n#include <cstring>\n#include <functional>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n\n#include <boost/iterator/iterator_adaptor.hpp>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/portability/SysTypes.h>\n\n/**\n * Code that aids in storing data aligned on block (possibly cache-line)\n * boundaries, perhaps with padding.\n *\n * Class Node represents one block.  Given an iterator to a container of\n * Node, class Iterator encapsulates an iterator to the underlying elements.\n * Adaptor converts a sequence of Node into a sequence of underlying elements\n * (not fully compatible with STL container requirements, see comments\n * near the Node class declaration).\n */\n\nnamespace folly {\nnamespace padded {\n\n/**\n * A Node is a fixed-size container of as many objects of type T as would\n * fit in a region of memory of size NS.  The last NS % sizeof(T)\n * bytes are ignored and uninitialized.\n *\n * Node only works for trivial types, which is usually not a concern.  This\n * is intentional: Node itself is trivial, which means that it can be\n * serialized / deserialized using a simple memcpy.\n */\ntemplate <class T, size_t NS>\nclass Node {\n  static_assert(\n      std::is_trivial_v<T> && sizeof(T) <= NS && NS % alignof(T) == 0);\n\n public:\n  using value_type = T;\n  static constexpr size_t kNodeSize = NS;\n  static constexpr size_t kElementCount = NS / sizeof(T);\n  static constexpr size_t kPaddingBytes = NS % sizeof(T);\n\n  T* data() { return storage_.data; }\n  const T* data() const { return storage_.data; }\n\n  bool operator==(const Node& other) const {\n    return memcmp(data(), other.data(), sizeof(T) * kElementCount) == 0;\n  }\n  bool operator!=(const Node& other) const { return !(*this == other); }\n\n  /**\n   * Return the number of nodes needed to represent n values.  Rounds up.\n   */\n  static constexpr size_t nodeCount(size_t n) {\n    return (n + kElementCount - 1) / kElementCount;\n  }\n\n  /**\n   * Return the total byte size needed to represent n values, rounded up\n   * to the nearest full node.\n   */\n  static constexpr size_t paddedByteSize(size_t n) { return nodeCount(n) * NS; }\n\n  /**\n   * Return the number of bytes used for padding n values.\n   * Note that, even if n is a multiple of kElementCount, this may\n   * return non-zero if kPaddingBytes != 0, as the padding at the end of\n   * the last node is not included in the result.\n   */\n  static constexpr size_t paddingBytes(size_t n) {\n    return (\n        n ? (kPaddingBytes +\n             (kElementCount - 1 - (n - 1) % kElementCount) * sizeof(T))\n          : 0);\n  }\n\n  /**\n   * Return the minimum byte size needed to represent n values.\n   * Does not round up.  Even if n is a multiple of kElementCount, this\n   * may be different from paddedByteSize() if kPaddingBytes != 0, as\n   * the padding at the end of the last node is not included in the result.\n   * Note that the calculation below works for n=0 correctly (returns 0).\n   */\n  static constexpr size_t unpaddedByteSize(size_t n) {\n    return paddedByteSize(n) - paddingBytes(n);\n  }\n\n private:\n  union Storage {\n    unsigned char bytes[NS];\n    T data[kElementCount];\n  } storage_;\n};\n\ntemplate <class Iter>\nclass Iterator;\n\nnamespace detail {\n\nFOLLY_CREATE_MEMBER_INVOKER(emplace_back, emplace_back);\n\n// Helper class template to define a base class for Iterator (below) and save\n// typing.\ntemplate <\n    template <class> class Class,\n    class Iter,\n    class Traits = std::iterator_traits<Iter>,\n    class Ref = typename Traits::reference,\n    class Val = typename Traits::value_type::value_type>\nusing IteratorBase = boost::iterator_adaptor<\n    Class<Iter>, // CRTC\n    Iter, // Base iterator type\n    Val, // Value type\n    boost::use_default, // Category or traversal\n    like_t<Ref, Val>>; // Reference type\n\n} // namespace detail\n\n/**\n * Wrapper around iterators to Node to return iterators to the underlying\n * node elements.\n */\ntemplate <class Iter>\nclass Iterator : public detail::IteratorBase<Iterator, Iter> {\n  using Super = detail::IteratorBase<Iterator, Iter>;\n\n public:\n  using Node = typename std::iterator_traits<Iter>::value_type;\n\n  Iterator() : pos_(0) {}\n\n  explicit Iterator(Iter base) : Super(base), pos_(0) {}\n\n  // Return the current node and the position inside the node\n  const Node& node() const { return *this->base_reference(); }\n  size_t pos() const { return pos_; }\n\n private:\n  typename Super::reference dereference() const {\n    return (*this->base_reference()).data()[pos_];\n  }\n\n  bool equal(const Iterator& other) const {\n    return (\n        this->base_reference() == other.base_reference() && pos_ == other.pos_);\n  }\n\n  void advance(typename Super::difference_type n) {\n    constexpr ssize_t elementCount = Node::kElementCount; // signed!\n    ssize_t newPos = pos_ + n;\n    if (newPos >= 0 && newPos < elementCount) {\n      pos_ = newPos;\n      return;\n    }\n    ssize_t nblocks = newPos / elementCount;\n    newPos %= elementCount;\n    if (newPos < 0) {\n      --nblocks; // negative\n      newPos += elementCount;\n    }\n    this->base_reference() += nblocks;\n    pos_ = newPos;\n  }\n\n  void increment() {\n    if (++pos_ == Node::kElementCount) {\n      ++this->base_reference();\n      pos_ = 0;\n    }\n  }\n\n  void decrement() {\n    if (--pos_ == -1) {\n      --this->base_reference();\n      pos_ = Node::kElementCount - 1;\n    }\n  }\n\n  typename Super::difference_type distance_to(const Iterator& other) const {\n    constexpr ssize_t elementCount = Node::kElementCount; // signed!\n    ssize_t nblocks =\n        std::distance(this->base_reference(), other.base_reference());\n    return nblocks * elementCount + (other.pos_ - pos_);\n  }\n\n  friend class boost::iterator_core_access;\n  ssize_t pos_; // signed for easier advance() implementation\n};\n\n/**\n * Given a container to Node, return iterators to the first element in\n * the first Node / one past the last element in the last Node.\n * Note that the last node is assumed to be full; if that's not the case,\n * subtract from end() as appropriate.\n */\n\ntemplate <class Container>\nIterator<typename Container::const_iterator> cbegin(const Container& c) {\n  return Iterator<typename Container::const_iterator>(std::begin(c));\n}\n\ntemplate <class Container>\nIterator<typename Container::const_iterator> cend(const Container& c) {\n  return Iterator<typename Container::const_iterator>(std::end(c));\n}\n\ntemplate <class Container>\nIterator<typename Container::const_iterator> begin(const Container& c) {\n  return cbegin(c);\n}\n\ntemplate <class Container>\nIterator<typename Container::const_iterator> end(const Container& c) {\n  return cend(c);\n}\n\ntemplate <class Container>\nIterator<typename Container::iterator> begin(Container& c) {\n  return Iterator<typename Container::iterator>(std::begin(c));\n}\n\ntemplate <class Container>\nIterator<typename Container::iterator> end(Container& c) {\n  return Iterator<typename Container::iterator>(std::end(c));\n}\n\n/**\n * Adaptor around a STL sequence container.\n *\n * Converts a sequence of Node into a sequence of its underlying elements\n * (with enough functionality to make it useful, although it's not fully\n * compatible with the STL container requirements, see below).\n *\n * Provides iterators (of the same category as those of the underlying\n * container), size(), front(), back(), push_back(), pop_back(), and const /\n * non-const versions of operator[] (if the underlying container supports\n * them).  Does not provide push_front() / pop_front() or arbitrary insert /\n * emplace / erase.  Also provides reserve() / capacity() if supported by the\n * underlying container.\n *\n * Yes, it's called Adaptor, not Adapter, as that's the name used by the STL\n * and by boost.  Deal with it.\n *\n * Internally, we hold a container of Node and the number of elements in\n * the last block.  We don't keep empty blocks, so the number of elements in\n * the last block is always between 1 and Node::kElementCount (inclusive).\n * (this is true if the container is empty as well to make push_back() simpler,\n * see the implementation of the size() method for details).\n */\ntemplate <class Container>\nclass Adaptor {\n public:\n  using Node = typename Container::value_type;\n  using value_type = typename Node::value_type;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n  using iterator = Iterator<typename Container::iterator>;\n  using const_iterator = Iterator<typename Container::const_iterator>;\n  using difference_type = typename const_iterator::difference_type;\n  using size_type = typename Container::size_type;\n\n  static constexpr size_t kElementsPerNode = Node::kElementCount;\n  // Constructors\n  Adaptor() : lastCount_(Node::kElementCount) {}\n  explicit Adaptor(Container c, size_t lastCount = Node::kElementCount)\n      : c_(std::move(c)), lastCount_(lastCount) {}\n  explicit Adaptor(size_t n, const value_type& value = value_type())\n      : c_(Node::nodeCount(n), fullNode(value)) {\n    const auto count = n % Node::kElementCount;\n    lastCount_ = count != 0 ? count : Node::kElementCount;\n  }\n\n  Adaptor(const Adaptor&) = default;\n  Adaptor& operator=(const Adaptor&) = default;\n  Adaptor(Adaptor&& other) noexcept\n      : c_(std::move(other.c_)), lastCount_(other.lastCount_) {\n    other.lastCount_ = Node::kElementCount;\n  }\n  Adaptor& operator=(Adaptor&& other) {\n    if (this != &other) {\n      c_ = std::move(other.c_);\n      lastCount_ = other.lastCount_;\n      other.lastCount_ = Node::kElementCount;\n    }\n    return *this;\n  }\n\n  // Iterators\n  const_iterator cbegin() const { return const_iterator(c_.begin()); }\n  const_iterator cend() const {\n    auto it = const_iterator(c_.end());\n    if (lastCount_ != Node::kElementCount) {\n      it -= (Node::kElementCount - lastCount_);\n    }\n    return it;\n  }\n  const_iterator begin() const { return cbegin(); }\n  const_iterator end() const { return cend(); }\n  iterator begin() { return iterator(c_.begin()); }\n  iterator end() {\n    auto it = iterator(c_.end());\n    if (lastCount_ != Node::kElementCount) {\n      it -= difference_type(Node::kElementCount - lastCount_);\n    }\n    return it;\n  }\n  void swap(Adaptor& other) {\n    using std::swap;\n    swap(c_, other.c_);\n    swap(lastCount_, other.lastCount_);\n  }\n  bool empty() const { return c_.empty(); }\n  size_type size() const {\n    return (\n        c_.empty() ? 0 : (c_.size() - 1) * Node::kElementCount + lastCount_);\n  }\n  size_type max_size() const {\n    return (\n        (c_.max_size() <=\n         std::numeric_limits<size_type>::max() / Node::kElementCount)\n            ? c_.max_size() * Node::kElementCount\n            : std::numeric_limits<size_type>::max());\n  }\n\n  const value_type& front() const {\n    assert(!empty());\n    return c_.front().data()[0];\n  }\n  value_type& front() {\n    assert(!empty());\n    return c_.front().data()[0];\n  }\n\n  const value_type& back() const {\n    assert(!empty());\n    return c_.back().data()[lastCount_ - 1];\n  }\n  value_type& back() {\n    assert(!empty());\n    return c_.back().data()[lastCount_ - 1];\n  }\n\n  template <typename... Args>\n  void emplace_back(Args&&... args) {\n    new (allocate_back()) value_type(std::forward<Args>(args)...);\n  }\n\n  void push_back(value_type x) { emplace_back(std::move(x)); }\n\n  void pop_back() {\n    assert(!empty());\n    if (--lastCount_ == 0) {\n      c_.pop_back();\n      lastCount_ = Node::kElementCount;\n    }\n  }\n\n  void clear() {\n    c_.clear();\n    lastCount_ = Node::kElementCount;\n  }\n\n  void reserve(size_type n) {\n    assert(n >= 0);\n    c_.reserve(Node::nodeCount(n));\n  }\n\n  size_type capacity() const { return c_.capacity() * Node::kElementCount; }\n\n  const value_type& operator[](size_type idx) const {\n    return c_[idx / Node::kElementCount].data()[idx % Node::kElementCount];\n  }\n  value_type& operator[](size_type idx) {\n    return c_[idx / Node::kElementCount].data()[idx % Node::kElementCount];\n  }\n\n  /**\n   * Return the underlying container and number of elements in the last block,\n   * and clear *this.  Useful when you want to process the data as Nodes\n   * (again) and want to avoid copies.\n   */\n  std::pair<Container, size_t> move() {\n    std::pair<Container, size_t> p(std::move(c_), lastCount_);\n    lastCount_ = Node::kElementCount;\n    return p;\n  }\n\n  /**\n   * Return a const reference to the underlying container and the current\n   * number of elements in the last block.\n   */\n  std::pair<const Container&, size_t> peek() const {\n    return std::make_pair(std::cref(c_), lastCount_);\n  }\n\n  void padToFullNode(const value_type& padValue) {\n    // the if is necessary because c_ may be empty so we can't call c_.back()\n    if (lastCount_ != Node::kElementCount) {\n      auto last = c_.back().data();\n      std::fill(last + lastCount_, last + Node::kElementCount, padValue);\n      lastCount_ = Node::kElementCount;\n    }\n  }\n\n private:\n  value_type* allocate_back() {\n    if (lastCount_ == Node::kElementCount) {\n      if constexpr (is_invocable_v<detail::emplace_back, Container&>) {\n        c_.emplace_back();\n      } else {\n        c_.push_back(typename Container::value_type());\n      }\n      lastCount_ = 0;\n    }\n    return &c_.back().data()[lastCount_++];\n  }\n\n  static Node fullNode(const value_type& value) {\n    Node n;\n    std::fill(n.data(), n.data() + kElementsPerNode, value);\n    return n;\n  }\n  Container c_; // container of Nodes\n  size_t lastCount_; // number of elements in last Node\n};\n\n} // namespace padded\n} // namespace folly\n"
  },
  {
    "path": "folly/Poly-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nnamespace folly {\nnamespace detail {\n\ntemplate <class I>\ninline PolyVal<I>::PolyVal(PolyVal&& that) noexcept {\n  that.vptr_->ops_(Op::eMove, &that, static_cast<Data*>(this));\n  vptr_ = std::exchange(that.vptr_, vtable<I>());\n}\n\ntemplate <class I>\ninline PolyVal<I>::PolyVal(PolyOrNonesuch const& that) {\n  that.vptr_->ops_(\n      Op::eCopy, const_cast<Data*>(that._data_()), PolyAccess::data(*this));\n  vptr_ = that.vptr_;\n}\n\ntemplate <class I>\ninline PolyVal<I>::~PolyVal() {\n  vptr_->ops_(Op::eNuke, this, nullptr);\n}\n\ntemplate <class I>\ninline Poly<I>& PolyVal<I>::operator=(PolyVal that) noexcept {\n  vptr_->ops_(Op::eNuke, _data_(), nullptr);\n  that.vptr_->ops_(Op::eMove, that._data_(), _data_());\n  vptr_ = std::exchange(that.vptr_, vtable<I>());\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ntemplate <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>\ninline PolyVal<I>::PolyVal(T&& t) {\n  using U = std::decay_t<T>;\n  static_assert(\n      std::is_copy_constructible<U>::value || !Copyable::value,\n      \"This Poly<> requires copyability, and the source object is not \"\n      \"copyable\");\n  // The static and dynamic types should match; otherwise, this will slice.\n  assert(\n      typeid(t) == typeid(std::decay_t<T>) &&\n      \"Dynamic and static exception types don't match. Object would \"\n      \"be sliced when storing in Poly.\");\n  if (inSitu<U>()) {\n    auto const buff = static_cast<void*>(&_data_()->buff_);\n    ::new (buff) U(static_cast<T&&>(t));\n  } else {\n    _data_()->pobj_ = new U(static_cast<T&&>(t));\n  }\n  vptr_ = vtableFor<I, U>();\n}\n\ntemplate <class I>\ntemplate <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>>\ninline PolyVal<I>::PolyVal(Poly<I2> that) {\n  static_assert(\n      !Copyable::value || std::is_copy_constructible<Poly<I2>>::value,\n      \"This Poly<> requires copyability, and the source object is not \"\n      \"copyable\");\n  auto* that_vptr = PolyAccess::vtable(that);\n  if (that_vptr->state_ != State::eEmpty) {\n    that_vptr->ops_(Op::eMove, PolyAccess::data(that), _data_());\n    vptr_ = &select<I>(*std::exchange(that_vptr, vtable<std::decay_t<I2>>()));\n  }\n}\n\ntemplate <class I>\ntemplate <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>\ninline Poly<I>& PolyVal<I>::operator=(T&& t) {\n  *this = PolyVal(static_cast<T&&>(t));\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ntemplate <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>>\ninline Poly<I>& PolyVal<I>::operator=(Poly<I2> that) {\n  *this = PolyVal(std::move(that));\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ninline void PolyVal<I>::swap(Poly<I>& that) noexcept {\n  switch (vptr_->state_) {\n    case State::eEmpty:\n      *this = std::move(that);\n      break;\n    case State::eOnHeap:\n      if (State::eOnHeap == that.vptr_->state_) {\n        std::swap(_data_()->pobj_, that._data_()->pobj_);\n        std::swap(vptr_, that.vptr_);\n        return;\n      }\n      [[fallthrough]];\n    case State::eInSitu:\n      std::swap(\n          *this, static_cast<PolyVal<I>&>(that)); // NOTE: qualified, not ADL\n  }\n}\n\ntemplate <class I>\ninline AddCvrefOf<PolyRoot<I>, I>& PolyRef<I>::_polyRoot_() const noexcept {\n  return const_cast<AddCvrefOf<PolyRoot<I>, I>&>(\n      static_cast<PolyRoot<I> const&>(*this));\n}\n\ntemplate <class I>\nconstexpr RefType PolyRef<I>::refType() noexcept {\n  using J = std::remove_reference_t<I>;\n  return std::is_rvalue_reference<I>::value ? RefType::eRvalue\n      : std::is_const<J>::value\n      ? RefType::eConstLvalue\n      : RefType::eLvalue;\n}\n\ntemplate <class I>\ntemplate <class That, class I2>\ninline PolyRef<I>::PolyRef(That&& that, Type<I2>) {\n  auto* that_vptr = PolyAccess::vtable(PolyAccess::root(that));\n  detail::State const that_state = that_vptr->state_;\n  if (that_state == State::eEmpty) {\n    throw BadPolyAccess();\n  }\n  auto* that_data = PolyAccess::data(PolyAccess::root(that));\n  _data_()->pobj_ = that_state == State::eInSitu\n      ? const_cast<void*>(static_cast<void const*>(&that_data->buff_))\n      : that_data->pobj_;\n  this->vptr_ = &select<std::decay_t<I>>(\n      *static_cast<VTable<std::decay_t<I2>> const*>(that_vptr->ops_(\n          Op::eRefr, nullptr, reinterpret_cast<void*>(refType()))));\n}\n\ntemplate <class I>\ninline PolyRef<I>::PolyRef(PolyRef const& that) noexcept {\n  _data_()->pobj_ = that._data_()->pobj_;\n  this->vptr_ = that.vptr_;\n}\n\ntemplate <class I>\ninline Poly<I>& PolyRef<I>::operator=(PolyRef const& that) noexcept {\n  _data_()->pobj_ = that._data_()->pobj_;\n  this->vptr_ = that.vptr_;\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ntemplate <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>\ninline PolyRef<I>::PolyRef(T&& t) noexcept {\n  _data_()->pobj_ =\n      const_cast<void*>(static_cast<void const*>(std::addressof(t)));\n  this->vptr_ = vtableFor<std::decay_t<I>, AddCvrefOf<std::decay_t<T>, I>>();\n}\n\ntemplate <class I>\ntemplate <\n    class I2,\n    std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>>\ninline PolyRef<I>::PolyRef(Poly<I2>&& that) noexcept(\n    std::is_reference<I2>::value)\n    : PolyRef{that, Type<I2>{}} {\n  static_assert(\n      Disjunction<std::is_reference<I2>, std::is_rvalue_reference<I>>::value,\n      \"Attempting to construct a Poly that is a reference to a temporary. \"\n      \"This is probably a mistake.\");\n}\n\ntemplate <class I>\ntemplate <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>\ninline Poly<I>& PolyRef<I>::operator=(T&& t) noexcept {\n  *this = PolyRef(static_cast<T&&>(t));\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ntemplate <\n    class I2,\n    std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>>\ninline Poly<I>& PolyRef<I>::operator=(Poly<I2>&& that) noexcept(\n    std::is_reference<I2>::value) {\n  *this = PolyRef(std::move(that));\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ntemplate <\n    class I2,\n    std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int>>\ninline Poly<I>& PolyRef<I>::operator=(Poly<I2>& that) noexcept(\n    std::is_reference<I2>::value) {\n  *this = PolyRef(that);\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ntemplate <\n    class I2,\n    std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int>>\ninline Poly<I>& PolyRef<I>::operator=(Poly<I2> const& that) noexcept(\n    std::is_reference<I2>::value) {\n  *this = PolyRef(that);\n  return static_cast<Poly<I>&>(*this);\n}\n\ntemplate <class I>\ninline void PolyRef<I>::swap(Poly<I>& that) noexcept {\n  std::swap(_data_()->pobj_, that._data_()->pobj_);\n  std::swap(this->vptr_, that.vptr_);\n}\n\ntemplate <class I>\ninline AddCvrefOf<PolyImpl<I>, I>& PolyRef<I>::get() const noexcept {\n  return const_cast<AddCvrefOf<PolyImpl<I>, I>&>(\n      static_cast<PolyImpl<I> const&>(*this));\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/Poly.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// TODO: [x] \"cast\" from Poly<C&> to Poly<C&&>\n// TODO: [ ] copy/move from Poly<C&>/Poly<C&&> to Poly<C>\n// TODO: [ ] copy-on-write?\n// TODO: [ ] down- and cross-casting? (Possible?)\n// TODO: [ ] shared ownership? (Dubious.)\n// TODO: [ ] can games be played with making the VTable a member of a struct\n//           with strange alignment such that the address of the VTable can\n//           be used to tell whether the object is stored in-situ or not?\n\n#pragma once\n\n#include <cassert>\n#include <new>\n#include <type_traits>\n#include <typeinfo>\n#include <utility>\n\n#include <folly/CPortability.h>\n#include <folly/CppAttributes.h>\n#include <folly/Traits.h>\n#include <folly/detail/TypeList.h>\n#include <folly/lang/Assume.h>\n\n#include <folly/PolyException.h>\n#include <folly/detail/PolyDetail.h>\n\nnamespace folly {\ntemplate <class I>\nstruct Poly;\n\n// MSVC workaround\ntemplate <class Node, class Tfx, class Access>\nstruct PolySelf_ {\n  using type = decltype(Access::template self_<Node, Tfx>());\n};\n\n/**\n * Within the definition of interface `I`, `PolySelf<Base>` is an alias for\n * the instance of `Poly` that is currently being instantiated. It is\n * one of: `Poly<J>`, `Poly<J&&>`, `Poly<J&>`, or `Poly<J const&>`; where\n * `J` is either `I` or some interface that extends `I`.\n *\n * It can be used within interface definitions to declare members that accept\n * other `Poly` objects of the same type as `*this`.\n *\n * The first parameter may optionally be cv- and/or reference-qualified, in\n * which case, the qualification is applies to the type of the interface in the\n * resulting `Poly<>` instance. The second template parameter controls whether\n * or not the interface is decayed before the cv-ref qualifiers of the first\n * argument are applied. For example, given the following:\n *\n *     struct Foo {\n *       template <class Base>\n *       struct Interface : Base {\n *         using A = PolySelf<Base>;\n *         using B = PolySelf<Base &>;\n *         using C = PolySelf<Base const &>;\n *         using X = PolySelf<Base, PolyDecay>;\n *         using Y = PolySelf<Base &, PolyDecay>;\n *         using Z = PolySelf<Base const &, PolyDecay>;\n *       };\n *       // ...\n *     };\n *     struct Bar : PolyExtends<Foo> {\n *       // ...\n *     };\n *\n * Then for `Poly<Bar>`, the typedefs are aliases for the following types:\n * - `A` is `Poly<Bar>`\n * - `B` is `Poly<Bar &>`\n * - `C` is `Poly<Bar const &>`\n * - `X` is `Poly<Bar>`\n * - `Y` is `Poly<Bar &>`\n * - `Z` is `Poly<Bar const &>`\n *\n * And for `Poly<Bar &>`, the typedefs are aliases for the following types:\n * - `A` is `Poly<Bar &>`\n * - `B` is `Poly<Bar &>`\n * - `C` is `Poly<Bar &>`\n * - `X` is `Poly<Bar>`\n * - `Y` is `Poly<Bar &>`\n * - `Z` is `Poly<Bar const &>`\n */\ntemplate <\n    class Node,\n    class Tfx = detail::MetaIdentity,\n    class Access = detail::PolyAccess>\nusing PolySelf = _t<PolySelf_<Node, Tfx, Access>>;\n\n/**\n * When used in conjunction with `PolySelf`, controls how to construct `Poly`\n * types related to the one currently being instantiated.\n *\n * \\sa PolySelf\n */\nusing PolyDecay = detail::MetaQuote<std::decay_t>;\n\n#define FOLLY_POLY_MEMBER(SIG, MEM) ::folly::sig<SIG>(MEM)\n#define FOLLY_POLY_MEMBERS(...) ::folly::PolyMembers<__VA_ARGS__>\n\ntemplate <auto... Ps>\nstruct PolyMembers {};\n\n/**\n * Used in the definition of a `Poly` interface to say that the current\n * interface is an extension of a set of zero or more interfaces.\n *\n * Example:\n *\n *   struct IFoo {\n *     template <class Base> struct Interface : Base {\n *       void foo() { folly::poly_call<0>(*this); }\n *     };\n *     template <class T> using Members = FOLLY_POLY_MEMBERS(&T::foo);\n *   }\n *   struct IBar : PolyExtends<IFoo> {\n *     template <class Base> struct Interface : Base {\n *       void bar(int i) { folly::poly_call<0>(*this, i); }\n *     };\n *     template <class T> using Members = FOLLY_POLY_MEMBERS(&T::bar);\n *   }\n */\ntemplate <class... I>\nstruct PolyExtends : virtual I... {\n  using Subsumptions = detail::TypeList<I...>;\n\n  template <class Base>\n  struct Interface : Base {\n    Interface() = default;\n    using Base::Base;\n  };\n\n  template <class...>\n  using Members = PolyMembers<>;\n};\n\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Call the N-th member of the currently-being-defined interface. When the\n * first parameter is an object of type `PolySelf<Base>` (as opposed to `*this`)\n * you must explicitly specify which interface through which to dispatch.\n * For instance:\n *\n *     struct IAddable {\n *       template <class Base>\n *       struct Interface : Base {\n *         friend folly::PolySelf<Base, folly::PolyDecay>\n *         operator+(\n *             folly::PolySelf<Base> const& a,\n *             folly::PolySelf<Base> const& b) {\n *           return folly::poly_call<0, IAddable>(a, b);\n *         }\n *       };\n *       template <class T>\n *       static auto plus_(T const& a, T const& b) -> decltype(a + b) {\n *         return a + b;\n *       }\n *       template <class T>\n *       using Members = FOLLY_POLY_MEMBERS(&plus_<std::decay_t<T>>);\n *     };\n *\n * \\sa PolySelf\n */\ntemplate <std::size_t N, typename This, typename... As>\nauto poly_call(This&& _this, As&&... as)\n    -> decltype(detail::PolyAccess::call<N>(\n        static_cast<This&&>(_this), static_cast<As&&>(as)...)) {\n  return detail::PolyAccess::call<N>(\n      static_cast<This&&>(_this), static_cast<As&&>(as)...);\n}\n\n/// \\overload\ntemplate <std::size_t N, class I, class Tail, typename... As>\ndecltype(auto) poly_call(detail::PolyNode<I, Tail>&& _this, As&&... as) {\n  using This = detail::InterfaceOf<I, detail::PolyNode<I, Tail>>;\n  return detail::PolyAccess::call<N>(\n      static_cast<This&&>(_this), static_cast<As&&>(as)...);\n}\n\n/// \\overload\ntemplate <std::size_t N, class I, class Tail, typename... As>\ndecltype(auto) poly_call(detail::PolyNode<I, Tail>& _this, As&&... as) {\n  using This = detail::InterfaceOf<I, detail::PolyNode<I, Tail>>;\n  return detail::PolyAccess::call<N>(\n      static_cast<This&>(_this), static_cast<As&&>(as)...);\n}\n\n/// \\overload\ntemplate <std::size_t N, class I, class Tail, typename... As>\ndecltype(auto) poly_call(detail::PolyNode<I, Tail> const& _this, As&&... as) {\n  using This = detail::InterfaceOf<I, detail::PolyNode<I, Tail>>;\n  return detail::PolyAccess::call<N>(\n      static_cast<This const&>(_this), static_cast<As&&>(as)...);\n}\n\n/// \\overload\ntemplate <\n    std::size_t N,\n    class I,\n    class Poly,\n    typename... As,\n    std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0>\nauto poly_call(Poly&& _this, As&&... as) -> decltype(poly_call<N, I>(\n    static_cast<Poly&&>(_this).get(), static_cast<As&&>(as)...)) {\n  return poly_call<N, I>(\n      static_cast<Poly&&>(_this).get(), static_cast<As&&>(as)...);\n}\n\n/// \\cond\n/// \\overload\ntemplate <std::size_t N, class I, typename... As>\n[[noreturn]] detail::Bottom poly_call(detail::ArchetypeBase const&, As&&...) {\n  assume_unreachable();\n}\n/// \\endcond\n\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Try to cast the `Poly` object to the requested type. If the `Poly` stores an\n * object of that type, return a reference to the object; otherwise, throw an\n * exception.\n * \\tparam T The (unqualified) type to which to cast the `Poly` object.\n * \\tparam Poly The type of the `Poly` object.\n * \\param that The `Poly` object to be cast.\n * \\return A reference to the `T` object stored in or referred to by `that`.\n * \\throw BadPolyAccess if `that` is empty.\n * \\throw BadPolyCast if `that` does not store or refer to an object of type\n *        `T`.\n */\ntemplate <class T, class I>\ndetail::AddCvrefOf<T, I>&& poly_cast(detail::PolyRoot<I>&& that) {\n  return detail::PolyAccess::cast<T>(std::move(that));\n}\n\n/// \\overload\ntemplate <class T, class I>\ndetail::AddCvrefOf<T, I>& poly_cast(detail::PolyRoot<I>& that) {\n  return detail::PolyAccess::cast<T>(that);\n}\n\n/// \\overload\ntemplate <class T, class I>\ndetail::AddCvrefOf<T, I> const& poly_cast(detail::PolyRoot<I> const& that) {\n  return detail::PolyAccess::cast<T>(that);\n}\n\n/// \\cond\n/// \\overload\ntemplate <class T, class I>\n[[noreturn]] detail::AddCvrefOf<T, I>&& poly_cast(detail::ArchetypeRoot<I>&&) {\n  assume_unreachable();\n}\n\n/// \\overload\ntemplate <class T, class I>\n[[noreturn]] detail::AddCvrefOf<T, I>& poly_cast(detail::ArchetypeRoot<I>&) {\n  assume_unreachable();\n}\n\n/// \\overload\ntemplate <class T, class I>\n[[noreturn]] detail::AddCvrefOf<T, I> const& poly_cast(\n    detail::ArchetypeRoot<I> const&) {\n  assume_unreachable();\n}\n/// \\endcond\n\n/// \\overload\ntemplate <\n    class T,\n    class Poly,\n    std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0>\nconstexpr auto poly_cast(Poly&& that)\n    -> decltype(poly_cast<T>(std::declval<Poly>().get())) {\n  return poly_cast<T>(static_cast<Poly&&>(that).get());\n}\n\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Returns a reference to the `std::type_info` object corresponding to the\n * object currently stored in `that`. If `that` is empty, returns\n * `typeid(void)`.\n */\ntemplate <class I>\nstd::type_info const& poly_type(detail::PolyRoot<I> const& that) noexcept {\n  return detail::PolyAccess::type(that);\n}\n\n/// \\cond\n/// \\overload\n[[noreturn]] inline std::type_info const& poly_type(\n    detail::ArchetypeBase const&) noexcept {\n  assume_unreachable();\n}\n/// \\endcond\n\n/// \\overload\ntemplate <class Poly, std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0>\nconstexpr auto poly_type(Poly const& that) noexcept\n    -> decltype(poly_type(that.get())) {\n  return poly_type(that.get());\n}\n\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Returns `true` if `that` is not currently storing an object; `false`,\n * otherwise.\n */\ntemplate <class I>\nbool poly_empty(detail::PolyRoot<I> const& that) noexcept {\n  return detail::State::eEmpty == detail::PolyAccess::vtable(that)->state_;\n}\n\n/// \\overload\ntemplate <class I>\nconstexpr bool poly_empty(detail::PolyRoot<I&&> const&) noexcept {\n  return false;\n}\n\n/// \\overload\ntemplate <class I>\nconstexpr bool poly_empty(detail::PolyRoot<I&> const&) noexcept {\n  return false;\n}\n\n/// \\overload\ntemplate <class I>\nconstexpr bool poly_empty(Poly<I&&> const&) noexcept {\n  return false;\n}\n\n/// \\overload\ntemplate <class I>\nconstexpr bool poly_empty(Poly<I&> const&) noexcept {\n  return false;\n}\n\n/// \\cond\n[[noreturn]] inline bool poly_empty(detail::ArchetypeBase const&) noexcept {\n  assume_unreachable();\n}\n/// \\endcond\n\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Given a `Poly<I&>`, return a `Poly<I&&>`. Otherwise, when `I` is not a\n * reference type, returns a `Poly<I>&&` when given a `Poly<I>&`, like\n * `std::move`.\n */\ntemplate <\n    class I,\n    std::enable_if_t<Negation<std::is_reference<I>>::value, int> = 0>\nconstexpr Poly<I>&& poly_move(detail::PolyRoot<I>& that) noexcept {\n  return static_cast<Poly<I>&&>(static_cast<Poly<I>&>(that));\n}\n\n/// \\overload\ntemplate <class I, std::enable_if_t<Negation<std::is_const<I>>::value, int> = 0>\nPoly<I&&> poly_move(detail::PolyRoot<I&> const& that) noexcept {\n  return detail::PolyAccess::move(that);\n}\n\n/// \\overload\ntemplate <class I>\nPoly<I const&> poly_move(detail::PolyRoot<I const&> const& that) noexcept {\n  return detail::PolyAccess::move(that);\n}\n\n/// \\cond\n/// \\overload\n[[noreturn]] inline detail::ArchetypeBase poly_move(\n    detail::ArchetypeBase const&) noexcept {\n  assume_unreachable();\n}\n/// \\endcond\n\n/// \\overload\ntemplate <class Poly, std::enable_if_t<detail::IsPoly<Poly>::value, int> = 0>\nconstexpr auto poly_move(Poly& that) noexcept\n    -> decltype(poly_move(that.get())) {\n  return poly_move(that.get());\n}\n\n/// \\cond\nnamespace detail {\n/**\n * The implementation for `Poly` for when the interface type is not\n * reference-like qualified, as in `Poly<SemiRegular>`.\n */\ntemplate <class I>\nstruct PolyVal : PolyImpl<I> {\n private:\n  friend PolyAccess;\n\n  struct NoneSuch {};\n  using Copyable = std::is_copy_constructible<PolyImpl<I>>;\n  using PolyOrNonesuch = If<Copyable::value, PolyVal, NoneSuch>;\n\n  using PolyRoot<I>::vptr_;\n\n  PolyRoot<I>& _polyRoot_() noexcept { return *this; }\n  PolyRoot<I> const& _polyRoot_() const noexcept { return *this; }\n\n  Data* _data_() noexcept { return PolyAccess::data(*this); }\n  Data const* _data_() const noexcept { return PolyAccess::data(*this); }\n\n public:\n  /**\n   * Default constructor.\n   * \\post `poly_empty(*this) == true`\n   */\n  PolyVal() = default;\n  /**\n   * Move constructor.\n   * \\post `poly_empty(that) == true`\n   */\n  PolyVal(PolyVal&& that) noexcept;\n  /**\n   * A copy constructor if `I` is copyable; otherwise, a useless constructor\n   * from a private, incomplete type.\n   */\n  /* implicit */ PolyVal(PolyOrNonesuch const& that);\n\n  ~PolyVal();\n\n  /**\n   * Inherit any constructors defined by any of the interfaces.\n   */\n  using PolyImpl<I>::PolyImpl;\n\n  /**\n   * Copy assignment, destroys the object currently held (if any) and makes\n   * `*this` equal to `that` by stealing its guts.\n   */\n  Poly<I>& operator=(PolyVal that) noexcept;\n\n  /**\n   * Construct a Poly<I> from a concrete type that satisfies the I concept\n   */\n  template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0>\n  /* implicit */ PolyVal(T&& t);\n\n  /**\n   * Construct a `Poly` from a compatible `Poly`. \"Compatible\" here means: the\n   * other interface extends this one either directly or indirectly.\n   */\n  template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int> = 0>\n  /* implicit */ PolyVal(Poly<I2> that);\n\n  /**\n   * Assign to this `Poly<I>` from a concrete type that satisfies the `I`\n   * concept.\n   */\n  template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0>\n  Poly<I>& operator=(T&& t);\n\n  /**\n   * Assign a compatible `Poly` to `*this`. \"Compatible\" here means: the\n   * other interface extends this one either directly or indirectly.\n   */\n  template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int> = 0>\n  Poly<I>& operator=(Poly<I2> that);\n\n  /**\n   * Swaps the values of two `Poly` objects.\n   */\n  void swap(Poly<I>& that) noexcept;\n};\n\n////////////////////////////////////////////////////////////////////////////////\n/**\n * The implementation of `Poly` for when the interface type is\n * reference-qualified, like `Poly<SemiRegular &>`.\n */\ntemplate <class I>\nstruct PolyRef : private PolyImpl<I> {\n private:\n  friend PolyAccess;\n\n  AddCvrefOf<PolyRoot<I>, I>& _polyRoot_() const noexcept;\n\n  Data* _data_() noexcept { return PolyAccess::data(*this); }\n  Data const* _data_() const noexcept { return PolyAccess::data(*this); }\n\n  static constexpr RefType refType() noexcept;\n\n protected:\n  template <class That, class I2>\n  PolyRef(That&& that, Type<I2>);\n\n public:\n  /**\n   * Copy constructor\n   * \\post `&poly_cast<T>(*this) == &poly_cast<T>(that)`, where `T` is the\n   *       type of the object held by `that`.\n   */\n  PolyRef(PolyRef const& that) noexcept;\n\n  /**\n   * Copy assignment\n   * \\post `&poly_cast<T>(*this) == &poly_cast<T>(that)`, where `T` is the\n   *       type of the object held by `that`.\n   */\n  Poly<I>& operator=(PolyRef const& that) noexcept;\n\n  /**\n   * Construct a `Poly<I>` from a concrete type that satisfies concept `I`.\n   * \\post `!poly_empty(*this)`\n   */\n  template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0>\n  /* implicit */ PolyRef(T&& t) noexcept;\n\n  /**\n   * Construct a `Poly<I>` from a compatible `Poly<I2>`.\n   */\n  template <\n      class I2,\n      std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int> = 0>\n  /* implicit */ PolyRef(Poly<I2>&& that) noexcept(\n      std::is_reference<I2>::value);\n\n  template <\n      class I2,\n      std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int> = 0>\n  /* implicit */ PolyRef(Poly<I2>& that) noexcept(std::is_reference<I2>::value)\n      : PolyRef{that, Type<I2>{}} {}\n\n  template <\n      class I2,\n      std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int> = 0>\n  /* implicit */ PolyRef(Poly<I2> const& that) noexcept(\n      std::is_reference<I2>::value)\n      : PolyRef{that, Type<I2>{}} {}\n\n  /**\n   * Assign to a `Poly<I>` from a concrete type that satisfies concept `I`.\n   * \\post `!poly_empty(*this)`\n   */\n  template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int> = 0>\n  Poly<I>& operator=(T&& t) noexcept;\n\n  /**\n   * Assign to `*this` from another compatible `Poly`.\n   */\n  template <\n      class I2,\n      std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int> = 0>\n  Poly<I>& operator=(Poly<I2>&& that) noexcept(std::is_reference<I2>::value);\n\n  /**\n   * \\overload\n   */\n  template <\n      class I2,\n      std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int> = 0>\n  Poly<I>& operator=(Poly<I2>& that) noexcept(std::is_reference<I2>::value);\n\n  /**\n   * \\overload\n   */\n  template <\n      class I2,\n      std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int> = 0>\n  Poly<I>& operator=(Poly<I2> const& that) noexcept(\n      std::is_reference<I2>::value);\n\n  /**\n   * Swap which object this `Poly` references (\"shallow\" swap).\n   */\n  void swap(Poly<I>& that) noexcept;\n\n  /**\n   * Get a reference to the interface, with correct `const`-ness applied.\n   */\n  AddCvrefOf<PolyImpl<I>, I>& get() const noexcept;\n\n  /**\n   * Get a reference to the interface, with correct `const`-ness applied.\n   */\n  AddCvrefOf<PolyImpl<I>, I>& operator*() const noexcept { return get(); }\n\n  /**\n   * Get a pointer to the interface, with correct `const`-ness applied.\n   */\n  auto operator->() const noexcept { return &get(); }\n};\n\ntemplate <class I>\nusing PolyValOrRef = If<std::is_reference<I>::value, PolyRef<I>, PolyVal<I>>;\n} // namespace detail\n/// \\endcond\n\n/**\n * `Poly` is a class template that makes it relatively easy to define a\n * type-erasing polymorphic object wrapper.\n *\n * \\par Type-erasure\n *\n * \\par\n * `std::function` is one example of a type-erasing polymorphic object wrapper;\n * `folly::exception_wrapper` is another. Type-erasure is often used as an\n * alternative to dynamic polymorphism via inheritance-based virtual dispatch.\n * The distinguishing characteristic of type-erasing wrappers are:\n * \\li **Duck typing:** Types do not need to inherit from an abstract base\n *     class in order to be assignable to a type-erasing wrapper; they merely\n *     need to satisfy a particular interface.\n * \\li **Value semantics:** Type-erasing wrappers are objects that can be\n *     passed around _by value_. This is in contrast to abstract base classes\n *     which must be passed by reference or by pointer or else suffer from\n *     _slicing_, which causes them to lose their polymorphic behaviors.\n *     Reference semantics make it difficult to reason locally about code.\n * \\li **Automatic memory management:** When dealing with inheritance-based\n *     dynamic polymorphism, it is often necessary to allocate and manage\n *     objects on the heap. This leads to a proliferation of `shared_ptr`s and\n *     `unique_ptr`s in APIs, complicating their point-of-use. APIs that take\n *     type-erasing wrappers, on the other hand, can often store small objects\n *     in-situ, with no dynamic allocation. The memory management, if any, is\n *     handled for you, and leads to cleaner APIs: consumers of your API don't\n *     need to pass `shared_ptr<AbstractBase>`; they can simply pass any object\n *     that satisfies the interface you require. (`std::function` is a\n *     particularly compelling example of this benefit. Far worse would be an\n *     inheritance-based callable solution like\n *     `shared_ptr<ICallable<void(int)>>`. )\n *\n * \\par Example: Defining a type-erasing function wrapper with `folly::Poly`\n *\n * \\par\n * Defining a polymorphic wrapper with `Poly` is a matter of defining two\n * things:\n * \\li An *interface*, consisting of public member functions, and\n * \\li A *mapping* from a concrete type to a set of member function bindings.\n *\n * Below is a (heavily commented) example of a simple implementation of a\n * `std::function`-like polymorphic wrapper. Its interface has only a single\n * member function: `operator()`\n *\n *     // An interface for a callable object of a particular signature, Fun\n *     // (most interfaces don't need to be templates, FWIW).\n *     template <class Fun>\n *     struct IFunction;\n *\n *     template <class R, class... As>\n *     struct IFunction<R(As...)> {\n *       // An interface is defined as a nested class template called\n *       // Interface that takes a single template parameter, Base, from\n *       // which it inherits.\n *       template <class Base>\n *       struct Interface : Base {\n *         // The Interface has public member functions. These become the\n *         // public interface of the resulting Poly instantiation.\n *         // (Implementation note: Poly<IFunction<Sig>> will publicly\n *         // inherit from this struct, which is what gives it the right\n *         // member functions.)\n *         R operator()(As... as) const {\n *           // The definition of each member function in your interface will\n *           // always consist of a single line dispatching to\n *           // folly::poly_call<N>. The \"N\" corresponds to the N-th member\n *           // function in the list of member function bindings, Members,\n *           // defined below. The first argument will always be *this, and the\n *           // rest of the arguments should simply forward (if necessary) the\n *           // member function's arguments.\n *           return static_cast<R>(\n *               folly::poly_call<0>(*this, std::forward<As>(as)...));\n *         }\n *       };\n *\n *       // The \"Members\" alias template is a comma-separated list of bound\n *       // member functions for a given concrete type \"T\". The\n *       // \"FOLLY_POLY_MEMBERS\" macro accepts a comma-separated list, and the\n *       // (optional) \"FOLLY_POLY_MEMBER\" macro lets you disambiguate overloads\n *       // by explicitly specifying the function signature the target member\n *       // function should have. In this case, we require \"T\" to have a\n *       // function call operator with the signature `R(As...) const`.\n *       //\n *       // If you are using a C++17-compatible compiler, you can do away with\n *       // the macros and write this as:\n *       //\n *       //   template <class T>\n *       //   using Members = folly::PolyMembers<\n *       //       folly::sig<R(As...) const>(&T::operator())>;\n *       //\n *       // And since `folly::sig` is only needed for disambiguation in case of\n *       // overloads, if you are not concerned about objects with overloaded\n *       // function call operators, it could be further simplified to:\n *       //\n *       //   template <class T>\n *       //   using Members = folly::PolyMembers<&T::operator()>;\n *       //\n *       template <class T>\n *       using Members = FOLLY_POLY_MEMBERS(\n *           FOLLY_POLY_MEMBER(R(As...) const, &T::operator()));\n *     };\n *\n *     // Now that we have defined the interface, we can pass it to Poly to\n *     // create our type-erasing wrapper:\n *     template <class Fun>\n *     using Function = Poly<IFunction<Fun>>;\n *\n * \\par\n * Given the above definition of `Function`, users can now initialize instances\n * of (say) `Function<int(int, int)>` with function objects like\n * `std::plus<int>` and `std::multiplies<int>`, as below:\n *\n *     Function<int(int, int)> fun = std::plus<int>{};\n *     assert(5 == fun(2, 3));\n *     fun = std::multiplies<int>{};\n *     assert(6 = fun(2, 3));\n *\n * \\par Defining an interface with C++17\n *\n * \\par\n * With C++17, defining an interface to be used with `Poly` is fairly\n * straightforward. As in the `Function` example above, there is a struct with\n * a nested `Interface` class template and a nested `Members` alias template.\n * No macros are needed with C++17.\n * \\par\n * Imagine we were defining something like a Java-style iterator. If we are\n * using a C++17 compiler, our interface would look something like this:\n *\n *     template <class Value>\n *     struct IJavaIterator {\n *       template <class Base>\n *       struct Interface : Base {\n *         bool Done() const { return folly::poly_call<0>(*this); }\n *         Value Current() const { return folly::poly_call<1>(*this); }\n *         void Next() { folly::poly_call<2>(*this); }\n *       };\n *       // NOTE: This works in C++17 only:\n *       template <class T>\n *       using Members = folly::PolyMembers<&T::Done, &T::Current, &T::Next>;\n *     };\n *\n *     template <class Value>\n *     using JavaIterator = Poly<IJavaIterator>;\n *\n * \\par\n * Given the above definition, `JavaIterator<int>` can be used to hold instances\n * of any type that has `Done`, `Current`, and `Next` member functions with the\n * correct (or compatible) signatures.\n *\n * \\par\n * The presence of overloaded member functions complicates this picture. Often,\n * property members are faked in C++ with `const` and non-`const` member\n * function overloads, like in the interface specified below:\n *\n *     struct IIntProperty {\n *       template <class Base>\n *       struct Interface : Base {\n *         int Value() const { return folly::poly_call<0>(*this); }\n *         void Value(int i) { folly::poly_call<1>(*this, i); }\n *       };\n *       // NOTE: This works in C++17 only:\n *       template <class T>\n *       using Members = folly::PolyMembers<\n *         folly::sig<int() const>(&T::Value),\n *         folly::sig<void(int)>(&T::Value)>;\n *     };\n *\n *     using IntProperty = Poly<IIntProperty>;\n *\n * \\par\n * Now, any object that has `Value` members of compatible signatures can be\n * assigned to instances of `IntProperty` object. Note how `folly::sig` is used\n * to disambiguate the overloads of `&T::Value`.\n *\n * \\par Defining an interface with C++14\n *\n * \\par\n * In C++14, the nice syntax above doesn't work, so we have to resort to macros.\n * The two examples above would look like this:\n *\n *     template <class Value>\n *     struct IJavaIterator {\n *       template <class Base>\n *       struct Interface : Base {\n *         bool Done() const { return folly::poly_call<0>(*this); }\n *         Value Current() const { return folly::poly_call<1>(*this); }\n *         void Next() { folly::poly_call<2>(*this); }\n *       };\n *       // NOTE: This works in C++14 and C++17:\n *       template <class T>\n *       using Members = FOLLY_POLY_MEMBERS(&T::Done, &T::Current, &T::Next);\n *     };\n *\n *     template <class Value>\n *     using JavaIterator = Poly<IJavaIterator>;\n *\n * \\par\n * and\n *\n *     struct IIntProperty {\n *       template <class Base>\n *       struct Interface : Base {\n *         int Value() const { return folly::poly_call<0>(*this); }\n *         void Value(int i) { return folly::poly_call<1>(*this, i); }\n *       };\n *       // NOTE: This works in C++14 and C++17:\n *       template <class T>\n *       using Members = FOLLY_POLY_MEMBERS(\n *         FOLLY_POLY_MEMBER(int() const, &T::Value),\n *         FOLLY_POLY_MEMBER(void(int), &T::Value));\n *     };\n *\n *     using IntProperty = Poly<IIntProperty>;\n *\n * \\par Extending interfaces\n *\n * \\par\n * One typical advantage of inheritance-based solutions to runtime polymorphism\n * is that one polymorphic interface could extend another through inheritance.\n * The same can be accomplished with type-erasing polymorphic wrappers. In\n * the `Poly` library, you can use `folly::PolyExtends` to say that one\n * interface extends another.\n *\n *     struct IFoo {\n *       template <class Base>\n *       struct Interface : Base {\n *         void Foo() const { return folly::poly_call<0>(*this); }\n *       };\n *       template <class T>\n *       using Members = FOLLY_POLY_MEMBERS(&T::Foo);\n *     };\n *\n *     // The IFooBar interface extends the IFoo interface\n *     struct IFooBar : PolyExtends<IFoo> {\n *       template <class Base>\n *       struct Interface : Base {\n *         void Bar() const { return folly::poly_call<0>(*this); }\n *       };\n *       template <class T>\n *       using Members = FOLLY_POLY_MEMBERS(&T::Bar);\n *     };\n *\n *     using FooBar = Poly<IFooBar>;\n *\n * \\par\n * Given the above defintion, instances of type `FooBar` have both `Foo()` and\n * `Bar()` member functions.\n *\n * \\par\n * The sensible conversions exist between a wrapped derived type and a wrapped\n * base type. For instance, assuming `IDerived` extends `IBase` with\n * `PolyExtends`:\n *\n *     Poly<IDerived> derived = ...;\n *     Poly<IBase> base = derived; // This conversion is OK.\n *\n * \\par\n * As you would expect, there is no conversion in the other direction, and at\n * present there is no `Poly` equivalent to `dynamic_cast`.\n *\n * \\par Type-erasing polymorphic reference wrappers\n *\n * \\par\n * Sometimes you don't need to own a copy of an object; a reference will do. For\n * that you can use `Poly` to capture a _reference_ to an object satisfying an\n * interface rather than the whole object itself. The syntax is intuitive.\n *\n *     int i = 42;\n *     // Capture a mutable reference to an object of any IRegular type:\n *     Poly<IRegular &> intRef = i;\n *     assert(42 == folly::poly_cast<int>(intRef));\n *     // Assert that we captured the address of \"i\":\n *     assert(&i == &folly::poly_cast<int>(intRef));\n *\n * \\par\n * A reference-like `Poly` has a different interface than a value-like `Poly`.\n * Rather than calling member functions with the `obj.fun()` syntax, you would\n * use the `obj->fun()` syntax. This is for the sake of `const`-correctness.\n * For example, consider the code below:\n *\n *     struct IFoo {\n *       template <class Base>\n *       struct Interface {\n *         void Foo() { folly::poly_call<0>(*this); }\n *       };\n *       template <class T>\n *       using Members = folly::PolyMembers<&T::Foo>;\n *     };\n *\n *     struct SomeFoo {\n *       void Foo() { std::printf(\"SomeFoo::Foo\\n\"); }\n *     };\n *\n *     SomeFoo foo;\n *     Poly<IFoo &> const anyFoo = foo;\n *     anyFoo->Foo(); // prints \"SomeFoo::Foo\"\n *\n * \\par\n * Notice in the above code that the `Foo` member function is non-`const`.\n * Notice also that the `anyFoo` object is `const`. However, since it has\n * captured a non-`const` reference to the `foo` object, it should still be\n * possible to dispatch to the non-`const` `Foo` member function. When\n * instantiated with a reference type, `Poly` has an overloaded `operator->`\n * member that returns a pointer to the `IFoo` interface with the correct\n * `const`-ness, which makes this work.\n *\n * \\par\n * The same mechanism also prevents users from calling non-`const` member\n * functions on `Poly` objects that have captured `const` references, which\n * would violate `const`-correctness.\n *\n * \\par\n * Sensible conversions exist between non-reference and reference `Poly`s. For\n * instance:\n *\n *     Poly<IRegular> value = 42;\n *     Poly<IRegular &> mutable_ref = value;\n *     Poly<IRegular const &> const_ref = mutable_ref;\n *\n *     assert(&poly_cast<int>(value) == &poly_cast<int>(mutable_ref));\n *     assert(&poly_cast<int>(value) == &poly_cast<int>(const_ref));\n *\n * \\par Non-member functions (C++17)\n *\n * \\par\n * If you wanted to write the interface `ILogicallyNegatable`, which captures\n * all types that can be negated with unary `operator!`, you could do it\n * as we've shown above, by binding `&T::operator!` in the nested `Members`\n * alias template, but that has the problem that it won't work for types that\n * have defined unary `operator!` as a free function. To handle this case,\n * the `Poly` library lets you use a free function instead of a member function\n * when creating a binding.\n *\n * \\par\n * With C++17 you may use a lambda to create a binding, as shown in the example\n * below:\n *\n *     struct ILogicallyNegatable {\n *       template <class Base>\n *       struct Interface : Base {\n *         bool operator!() const { return folly::poly_call<0>(*this); }\n *       };\n *       template <class T>\n *       using Members = folly::PolyMembers<\n *         +[](T const& t) -> decltype(!t) { return !t; }>;\n *     };\n *\n * \\par\n * This requires some explanation. The unary `operator+` in front of the lambda\n * is necessary! It causes the lambda to decay to a C-style function pointer,\n * which is one of the types that `folly::PolyMembers` accepts. The `decltype`\n * in the lambda return type is also necessary. Through the magic of SFINAE, it\n * will cause `Poly<ILogicallyNegatable>` to reject any types that don't support\n * unary `operator!`.\n *\n * \\par\n * If you are using a free function to create a binding, the first parameter is\n * implicitly the `this` parameter. It will receive the type-erased object.\n *\n * \\par Non-member functions (C++14)\n *\n * \\par\n * If you are using a C++14 compiler, the defintion of `ILogicallyNegatable`\n * above will fail because lambdas are not `constexpr`. We can get the same\n * effect by writing the lambda as a named free function, as show below:\n *\n *     struct ILogicallyNegatable {\n *       template <class Base>\n *       struct Interface : Base {\n *         bool operator!() const { return folly::poly_call<0>(*this); }\n *       };\n *\n *       template <class T>\n *       static auto negate(T const& t) -> decltype(!t) { return !t; }\n *\n *       template <class T>\n *       using Members = FOLLY_POLY_MEMBERS(&negate<T>);\n *     };\n *\n * \\par\n * As with the example that uses the lambda in the preceding section, the first\n * parameter is implicitly the `this` parameter. It will receive the type-erased\n * object.\n *\n * \\par Multi-dispatch\n *\n * \\par\n * What if you want to create an `IAddable` interface for things that can be\n * added? Adding requires _two_ objects, both of which are type-erased. This\n * interface requires dispatching on both objects, doing the addition only\n * if the types are the same. For this we make use of the `PolySelf` template\n * alias to define an interface that takes more than one object of the\n * erased type.\n *\n *     struct IAddable {\n *       template <class Base>\n *       struct Interface : Base {\n *         friend PolySelf<Base, Decay>\n *         operator+(PolySelf<Base> const& a, PolySelf<Base> const& b) {\n *           return folly::poly_call<0, IAddable>(a, b);\n *         }\n *       };\n *\n *       template <class T>\n *       using Members = folly::PolyMembers<\n *         +[](T const& a, T const& b) -> decltype(a + b) { return a + b; }>;\n *     };\n *\n * \\par\n * Given the above defintion of `IAddable` we would be able to do the following:\n *\n *     Poly<IAddable> a = 2, b = 3;\n *     Poly<IAddable> c = a + b;\n *     assert(poly_cast<int>(c) == 5);\n *\n * \\par\n * If `a` and `b` stored objects of different types, a `BadPolyCast` exception\n * would be thrown.\n *\n * \\par Move-only types\n *\n * \\par\n * If you want to store move-only types, then your interface should extend the\n * `IMoveOnly` interface.\n *\n * \\par Implementation notes\n * \\par\n * `Poly` will store \"small\" objects in an internal buffer, avoiding the cost of\n * of dynamic allocations. At present, this size is not configurable; it is\n * pegged at the size of two `double`s.\n *\n * \\par\n * `Poly` objects are always nothrow movable. If you store an object in one that\n * has a potentially throwing move contructor, the object will be stored on the\n * heap, even if it could fit in the internal storage of the `Poly` object.\n * (So be sure to give your objects nothrow move constructors!)\n *\n * \\par\n * `Poly` implements type-erasure in a manner very similar to how the compiler\n * accomplishes virtual dispatch. Every `Poly` object contains a pointer to a\n * table of function pointers. Member function calls involve a double-\n * indirection: once through the v-pointer, and other indirect function call\n * through the function pointer.\n */\ntemplate <class I>\nstruct Poly final : detail::PolyValOrRef<I> {\n  friend detail::PolyAccess;\n  Poly() = default;\n  using detail::PolyValOrRef<I>::PolyValOrRef;\n  using detail::PolyValOrRef<I>::operator=;\n};\n\n/**\n * Swap two `Poly<I>` instances.\n */\ntemplate <class I>\nvoid swap(Poly<I>& left, Poly<I>& right) noexcept {\n  left.swap(right);\n}\n\n/**\n * Pseudo-function template handy for disambiguating function overloads.\n *\n * For example, given:\n *     struct S {\n *       int property() const;\n *       void property(int);\n *     };\n *\n * You can get a member function pointer to the first overload with:\n *     folly::sig<int()const>(&S::property);\n *\n * This is arguably a nicer syntax that using the built-in `static_cast`:\n *     static_cast<int (S::*)() const>(&S::property);\n *\n * `sig` is also more permissive than `static_cast` about `const`. For instance,\n * the following also works:\n *     folly::sig<int()>(&S::property);\n *\n * The above is permitted\n */\ntemplate <class Sig>\ninline constexpr detail::Sig<Sig> const sig = {};\n\n} // namespace folly\n\n#include <folly/Poly-inl.h>\n"
  },
  {
    "path": "folly/PolyException.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <exception>\n#include <typeinfo>\n\n#include <folly/CPortability.h>\n\nnamespace folly {\n\n/**\n * Exception type that is thrown on invalid access of an empty `Poly` object.\n */\nstruct FOLLY_EXPORT BadPolyAccess : std::exception {\n  BadPolyAccess() = default;\n  char const* what() const noexcept override { return \"BadPolyAccess\"; }\n};\n\n/**\n * Exception type that is thrown when attempting to extract from a `Poly` a\n * value of the wrong type.\n */\nstruct FOLLY_EXPORT BadPolyCast : std::bad_cast {\n  BadPolyCast() = default;\n  char const* what() const noexcept override { return \"BadPolyCast\"; }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Portability.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n\n#include <folly/CPortability.h>\n#include <folly/portability/Config.h>\n\n#if defined(_MSC_VER)\n#define FOLLY_CPLUSPLUS _MSVC_LANG\n#else\n#define FOLLY_CPLUSPLUS __cplusplus\n#endif\n\n// On MSVC an incorrect <version> header get's picked up\n#if !defined(_MSC_VER) && __has_include(<version>)\n#include <version>\n#endif\n\nstatic_assert(FOLLY_CPLUSPLUS >= 201703L, \"__cplusplus >= 201703L\");\n\n#if defined(__GNUC__) && !defined(__clang__)\n#if defined(FOLLY_CONFIG_TEMPORARY_DOWNGRADE_GCC)\nstatic_assert(__GNUC__ >= 9, \"__GNUC__ >= 9\");\n#else\nstatic_assert(__GNUC__ >= 10, \"__GNUC__ >= 10\");\n#endif\n#endif\n\n#if defined(_MSC_VER)\nstatic_assert(_MSC_VER >= 1920);\n#endif\n\n#if defined(_MSC_VER) || defined(_CPPLIB_VER)\nstatic_assert(FOLLY_CPLUSPLUS >= 201703L, \"__cplusplus >= 201703L\");\n#endif\n\n// Unaligned loads and stores\nnamespace folly {\n#if defined(FOLLY_HAVE_UNALIGNED_ACCESS) && FOLLY_HAVE_UNALIGNED_ACCESS\nconstexpr bool kHasUnalignedAccess = true;\n#else\nconstexpr bool kHasUnalignedAccess = false;\n#endif\n} // namespace folly\n\n// compiler specific attribute translation\n// msvc should come first, so if clang is in msvc mode it gets the right defines\n\n// NOTE: this will only do checking in msvc with versions that support /analyze\n#ifdef _MSC_VER\n#ifdef _USE_ATTRIBUTES_FOR_SAL\n#undef _USE_ATTRIBUTES_FOR_SAL\n#endif\n/* nolint */\n#define _USE_ATTRIBUTES_FOR_SAL 1\n#include <sal.h> // @manual\n#define FOLLY_PRINTF_FORMAT _Printf_format_string_\n#define FOLLY_PRINTF_FORMAT_ATTR(format_param, dots_param) /**/\n#else\n#define FOLLY_PRINTF_FORMAT /**/\n#define FOLLY_PRINTF_FORMAT_ATTR(format_param, dots_param) \\\n  __attribute__((__format__(__printf__, format_param, dots_param)))\n#endif\n\n// older clang-format gets confused by [[deprecated(...)]] on class decls\n#define FOLLY_DEPRECATED(...) [[deprecated(__VA_ARGS__)]]\n\n// target\n#ifdef _MSC_VER\n#define FOLLY_TARGET_ATTRIBUTE(target)\n#else\n#define FOLLY_TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))\n#endif\n\n#if defined(__i386__) || defined(__i686__) || defined(__x86__) || \\\n    defined(_M_IX86)\n#define FOLLY_X86 1\n#else\n#define FOLLY_X86 0\n#endif\n\n// detection for 64 bit\n#if defined(__x86_64__) || defined(_M_X64)\n#define FOLLY_X64 1\n#else\n#define FOLLY_X64 0\n#endif\n\n#if defined(__arm__)\n#define FOLLY_ARM 1\n#else\n#define FOLLY_ARM 0\n#endif\n\n#if defined(__aarch64__)\n#define FOLLY_AARCH64 1\n#else\n#define FOLLY_AARCH64 0\n#endif\n\n#if defined(__powerpc64__)\n#define FOLLY_PPC64 1\n#else\n#define FOLLY_PPC64 0\n#endif\n\n#if defined(__s390x__)\n#define FOLLY_S390X 1\n#else\n#define FOLLY_S390X 0\n#endif\n\n#if defined(__riscv)\n#define FOLLY_RISCV64 1\n#else\n#define FOLLY_RISCV64 0\n#endif\n\n#if defined(__wasm__)\n#define FOLLY_WASM 1\n#else\n#define FOLLY_WASM 0\n#endif\n\nnamespace folly {\nconstexpr bool kIsArchArm = FOLLY_ARM == 1;\nconstexpr bool kIsArchX86 = FOLLY_X86 == 1;\nconstexpr bool kIsArchAmd64 = FOLLY_X64 == 1;\nconstexpr bool kIsArchAArch64 = FOLLY_AARCH64 == 1;\nconstexpr bool kIsArchPPC64 = FOLLY_PPC64 == 1;\nconstexpr bool kIsArchS390X = FOLLY_S390X == 1;\nconstexpr bool kIsArchRISCV64 = FOLLY_RISCV64 == 1;\nconstexpr bool kIsArchWasm = FOLLY_WASM == 1;\n} // namespace folly\n\nnamespace folly {\n\n/**\n * folly::kIsLibrarySanitizeAddress reports if folly was compiled with ASAN\n * enabled.  Note that for compilation units outside of folly that include\n * folly/Portability.h, the value of kIsLibrarySanitizeAddress may be different\n * from whether or not the current compilation unit is being compiled with ASAN.\n */\n#if FOLLY_LIBRARY_SANITIZE_ADDRESS\nconstexpr bool kIsLibrarySanitizeAddress = true;\n#else\nconstexpr bool kIsLibrarySanitizeAddress = false;\n#endif\n\n#ifdef FOLLY_SANITIZE_ADDRESS\nconstexpr bool kIsSanitizeAddress = true;\n#else\nconstexpr bool kIsSanitizeAddress = false;\n#endif\n\n#ifdef FOLLY_SANITIZE_THREAD\nconstexpr bool kIsSanitizeThread = true;\n#else\nconstexpr bool kIsSanitizeThread = false;\n#endif\n\n#ifdef FOLLY_SANITIZE_DATAFLOW\nconstexpr bool kIsSanitizeDataflow = true;\n#else\nconstexpr bool kIsSanitizeDataflow = false;\n#endif\n\n#ifdef FOLLY_SANITIZE\nconstexpr bool kIsSanitize = true;\n#else\nconstexpr bool kIsSanitize = false;\n#endif\n\n#if defined(__OPTIMIZE__)\nconstexpr bool kIsOptimize = true;\n#else\nconstexpr bool kIsOptimize = false;\n#endif\n\n#if defined(__OPTIMIZE_SIZE__)\nconstexpr bool kIsOptimizeSize = true;\n#else\nconstexpr bool kIsOptimizeSize = false;\n#endif\n} // namespace folly\n\n// packing is very ugly in msvc\n#ifdef _MSC_VER\n#define FOLLY_PACK_ATTR /**/\n#define FOLLY_PACK_PUSH __pragma(pack(push, 1))\n#define FOLLY_PACK_POP __pragma(pack(pop))\n#elif defined(__GNUC__)\n#define FOLLY_PACK_ATTR __attribute__((__packed__))\n#define FOLLY_PACK_PUSH /**/\n#define FOLLY_PACK_POP /**/\n#else\n#define FOLLY_PACK_ATTR /**/\n#define FOLLY_PACK_PUSH /**/\n#define FOLLY_PACK_POP /**/\n#endif\n\n// It turns out that GNU libstdc++ and LLVM libc++ differ on how they implement\n// the 'std' namespace; the latter uses inline namespaces. Wrap this decision\n// up in a macro to make forward-declarations easier.\n#if defined(_LIBCPP_VERSION)\n#define FOLLY_NAMESPACE_STD_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD\n#define FOLLY_NAMESPACE_STD_END _LIBCPP_END_NAMESPACE_STD\n#else\n#define FOLLY_NAMESPACE_STD_BEGIN namespace std {\n#define FOLLY_NAMESPACE_STD_END }\n#endif\n\n// If the new c++ ABI is used, __cxx11 inline namespace needs to be added to\n// some types, e.g. std::list.\n#if defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI\n#define FOLLY_GLIBCXX_NAMESPACE_CXX11_BEGIN \\\n  inline _GLIBCXX_BEGIN_NAMESPACE_CXX11\n#define FOLLY_GLIBCXX_NAMESPACE_CXX11_END _GLIBCXX_END_NAMESPACE_CXX11\n#else\n#define FOLLY_GLIBCXX_NAMESPACE_CXX11_BEGIN\n#define FOLLY_GLIBCXX_NAMESPACE_CXX11_END\n#endif\n\n// MSVC specific defines\n// mainly for posix compat\n#ifdef _MSC_VER\n\n// We have compiler support for the newest of the new, but\n// MSVC doesn't tell us that.\n//\n// Clang pretends to be MSVC on Windows, but it refuses to compile\n// SSE4.2 intrinsics unless -march argument is specified.\n// So cannot unconditionally define __SSE4_2__ in clang.\n#ifndef __clang__\n#if !defined(_M_ARM) && !defined(_M_ARM64)\n#define __SSE4_2__ 1\n#endif // !defined(_M_ARM) && !defined(_M_ARM64)\n\n// Hide a GCC specific thing that breaks MSVC if left alone.\n#define __extension__\n\n// compiler specific to compiler specific\n// nolint\n#define __PRETTY_FUNCTION__ __FUNCSIG__\n#endif\n\n#endif\n\n// Define FOLLY_HAS_EXCEPTIONS\n#if (defined(__cpp_exceptions) && __cpp_exceptions >= 199711) || \\\n    FOLLY_HAS_FEATURE(cxx_exceptions)\n#define FOLLY_HAS_EXCEPTIONS 1\n#elif __GNUC__\n#if defined(__EXCEPTIONS) && __EXCEPTIONS\n#define FOLLY_HAS_EXCEPTIONS 1\n#else // __EXCEPTIONS\n#define FOLLY_HAS_EXCEPTIONS 0\n#endif // __EXCEPTIONS\n#elif FOLLY_MICROSOFT_ABI_VER\n#if _CPPUNWIND\n#define FOLLY_HAS_EXCEPTIONS 1\n#else // _CPPUNWIND\n#define FOLLY_HAS_EXCEPTIONS 0\n#endif // _CPPUNWIND\n#else\n#define FOLLY_HAS_EXCEPTIONS 1 // default assumption for unknown platforms\n#endif\n\n// Debug\nnamespace folly {\n#ifdef NDEBUG\nconstexpr auto kIsDebug = false;\n#else\nconstexpr auto kIsDebug = true;\n#endif\n} // namespace folly\n\n// Exceptions\nnamespace folly {\n#if FOLLY_HAS_EXCEPTIONS\nconstexpr auto kHasExceptions = true;\n#else\nconstexpr auto kHasExceptions = false;\n#endif\n} // namespace folly\n\n// Endianness\nnamespace folly {\n#ifdef _MSC_VER\n// It's MSVC, so we just have to guess ... and allow an override\n#ifdef FOLLY_ENDIAN_BE\nconstexpr auto kIsLittleEndian = false;\n#else\nconstexpr auto kIsLittleEndian = true;\n#endif\n#else\nconstexpr auto kIsLittleEndian = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__;\n#endif\nconstexpr auto kIsBigEndian = !kIsLittleEndian;\n} // namespace folly\n\n// Weak\nnamespace folly {\n#if FOLLY_HAVE_WEAK_SYMBOLS\nconstexpr auto kHasWeakSymbols = true;\n#else\nconstexpr auto kHasWeakSymbols = false;\n#endif\n} // namespace folly\n\n#ifndef FOLLY_SSE\n#if defined(__SSE4_2__)\n#define FOLLY_SSE 4\n#define FOLLY_SSE_MINOR 2\n#elif defined(__SSE4_1__)\n#define FOLLY_SSE 4\n#define FOLLY_SSE_MINOR 1\n#elif defined(__SSE4__)\n#define FOLLY_SSE 4\n#define FOLLY_SSE_MINOR 0\n#elif defined(__SSE3__)\n#define FOLLY_SSE 3\n#define FOLLY_SSE_MINOR 0\n#elif defined(__SSE2__)\n#define FOLLY_SSE 2\n#define FOLLY_SSE_MINOR 0\n#elif defined(__SSE__)\n#define FOLLY_SSE 1\n#define FOLLY_SSE_MINOR 0\n#else\n#define FOLLY_SSE 0\n#define FOLLY_SSE_MINOR 0\n#endif\n#endif\n\n#ifndef FOLLY_SSSE\n#if defined(__SSSE3__)\n#define FOLLY_SSSE 3\n#else\n#define FOLLY_SSSE 0\n#endif\n#endif\n\n#define FOLLY_SSE_PREREQ(major, minor) \\\n  (FOLLY_SSE > major || FOLLY_SSE == major && FOLLY_SSE_MINOR >= minor)\n\n#ifndef FOLLY_NEON\n#if (defined(__ARM_NEON) || defined(__ARM_NEON__)) && !defined(__CUDACC__)\n#define FOLLY_NEON 1\n#else\n#define FOLLY_NEON 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_CRC32\n#ifdef __ARM_FEATURE_CRC32\n#define FOLLY_ARM_FEATURE_CRC32 1\n#else\n#define FOLLY_ARM_FEATURE_CRC32 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_CRYPTO\n#ifdef __ARM_FEATURE_CRYPTO\n#define FOLLY_ARM_FEATURE_CRYPTO 1\n#else\n#define FOLLY_ARM_FEATURE_CRYPTO 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_AES\n#ifdef __ARM_FEATURE_AES\n#define FOLLY_ARM_FEATURE_AES 1\n#else\n#define FOLLY_ARM_FEATURE_AES 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_SHA2\n#ifdef __ARM_FEATURE_SHA2\n#define FOLLY_ARM_FEATURE_SHA2 1\n#else\n#define FOLLY_ARM_FEATURE_SHA2 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_SHA3\n#ifdef __ARM_FEATURE_SHA3\n#define FOLLY_ARM_FEATURE_SHA3 1\n#else\n#define FOLLY_ARM_FEATURE_SHA3 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_SVE\n#ifdef __ARM_FEATURE_SVE\n#define FOLLY_ARM_FEATURE_SVE 1\n#else\n#define FOLLY_ARM_FEATURE_SVE 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_SVE2\n#ifdef __ARM_FEATURE_SVE2\n#define FOLLY_ARM_FEATURE_SVE2 1\n#else\n#define FOLLY_ARM_FEATURE_SVE2 0\n#endif\n#endif\n\n#ifndef FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n#if FOLLY_ARM_FEATURE_SVE && __has_include(<arm_neon_sve_bridge.h>)\n#define FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE 1\n#else\n#define FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE 0\n#endif\n#endif\n\n// RTTI may not be enabled for this compilation unit.\n#if defined(__GXX_RTTI) || defined(__cpp_rtti) || \\\n    (defined(_MSC_VER) && defined(_CPPRTTI))\n#define FOLLY_HAS_RTTI 1\n#else\n#define FOLLY_HAS_RTTI 0\n#endif\n\nnamespace folly {\nconstexpr bool const kHasRtti = FOLLY_HAS_RTTI;\n} // namespace folly\n\n#if defined(__APPLE__) || defined(_MSC_VER)\n#define FOLLY_STATIC_CTOR_PRIORITY_MAX\n#else\n// 101 is the highest priority allowed by the init_priority attribute.\n// This priority is already used by JEMalloc and other memory allocators so\n// we will take the next one.\n#define FOLLY_STATIC_CTOR_PRIORITY_MAX __attribute__((__init_priority__(102)))\n#endif\n\n#if defined(__APPLE__) && TARGET_OS_IOS\n#define FOLLY_APPLE_IOS 1\n#else\n#define FOLLY_APPLE_IOS 0\n#endif\n\n#if defined(__APPLE__) && TARGET_OS_OSX\n#define FOLLY_APPLE_MACOS 1\n#else\n#define FOLLY_APPLE_MACOS 0\n#endif\n\n#if defined(__APPLE__) && TARGET_OS_TV\n#define FOLLY_APPLE_TVOS 1\n#else\n#define FOLLY_APPLE_TVOS 0\n#endif\n\n#if defined(__APPLE__) && TARGET_OS_WATCH\n#define FOLLY_APPLE_WATCHOS 1\n#else\n#define FOLLY_APPLE_WATCHOS 0\n#endif\n\nnamespace folly {\n\n#ifdef __OBJC__\nconstexpr auto kIsObjC = true;\n#else\nconstexpr auto kIsObjC = false;\n#endif\n\n#if FOLLY_MOBILE\nconstexpr auto kIsMobile = true;\n#else\nconstexpr auto kIsMobile = false;\n#endif\n\n#if defined(__linux__)\nconstexpr auto kIsLinuxActual = true;\n#else\nconstexpr auto kIsLinuxActual = false;\n#endif\n\nconstexpr auto kIsLinux = kIsLinuxActual && !kIsMobile;\n\n#if defined(__FreeBSD__)\nconstexpr auto kIsFreeBSD = true;\n#else\nconstexpr auto kIsFreeBSD = false;\n#endif\n\n#if defined(_WIN32)\nconstexpr auto kIsWindows = true;\n#else\nconstexpr auto kIsWindows = false;\n#endif\n\n#if defined(__ANDROID__)\nconstexpr auto kIsAndroid = true;\n#else\nconstexpr auto kIsAndroid = false;\n#endif\n\n#if defined(__APPLE__)\nconstexpr auto kIsApple = true;\n#else\nconstexpr auto kIsApple = false;\n#endif\n\nconstexpr bool kIsAppleIOS = FOLLY_APPLE_IOS == 1;\nconstexpr bool kIsAppleMacOS = FOLLY_APPLE_MACOS == 1;\nconstexpr bool kIsAppleTVOS = FOLLY_APPLE_TVOS == 1;\nconstexpr bool kIsAppleWatchOS = FOLLY_APPLE_WATCHOS == 1;\n\n#if defined(__GLIBCXX__)\nconstexpr auto kIsGlibcxx = true;\n#else\nconstexpr auto kIsGlibcxx = false;\n#endif\n\n#if defined(__GLIBCXX__) && _GLIBCXX_RELEASE // major version, 7+\nconstexpr auto kGlibcxxVer = _GLIBCXX_RELEASE;\n#else\nconstexpr auto kGlibcxxVer = 0;\n#endif\n\n#if defined(__GLIBCXX__) && defined(_GLIBCXX_ASSERTIONS)\nconstexpr auto kGlibcxxAssertions = true;\n#else\nconstexpr auto kGlibcxxAssertions = false;\n#endif\n\n#ifdef _LIBCPP_VERSION\nconstexpr auto kIsLibcpp = true;\n#else\nconstexpr auto kIsLibcpp = false;\n#endif\n\n#if defined(__GLIBCXX__)\nconstexpr auto kIsLibstdcpp = true;\n#else\nconstexpr auto kIsLibstdcpp = false;\n#endif\n\n#ifdef _MSC_VER\nconstexpr auto kMscVer = _MSC_VER;\n#else\nconstexpr auto kMscVer = 0;\n#endif\n\n#if defined(__GNUC__) && __GNUC__\nconstexpr auto kGnuc = __GNUC__;\n#else\nconstexpr auto kGnuc = 0;\n#endif\n\n#if __clang__\nconstexpr auto kIsClang = true;\nconstexpr auto kClangVerMajor = __clang_major__;\n#else\nconstexpr auto kIsClang = false;\nconstexpr auto kClangVerMajor = 0;\n#endif\n\n#ifdef FOLLY_MICROSOFT_ABI_VER\nconstexpr auto kMicrosoftAbiVer = FOLLY_MICROSOFT_ABI_VER;\n#else\nconstexpr auto kMicrosoftAbiVer = 0;\n#endif\n\n// cpplib is an implementation of the standard library, and is the one typically\n// used with the msvc compiler\n#ifdef _CPPLIB_VER\nconstexpr auto kCpplibVer = _CPPLIB_VER;\n#else\nconstexpr auto kCpplibVer = 0;\n#endif\n} // namespace folly\n\n#define FOLLY_PRAGMA_DETAIL_STR(X) #X\n\n#if defined(_MSC_VER)\n#define FOLLY_PRAGMA_UNROLL_N(N)\n#elif defined(__GNUC__)\n#define FOLLY_PRAGMA_UNROLL_N(N) _Pragma(FOLLY_PRAGMA_DETAIL_STR(GCC unroll(N)))\n#else\n#define FOLLY_PRAGMA_UNROLL_N(N) _Pragma(FOLLY_PRAGMA_DETAIL_STR(unroll(N)))\n#endif\n\n//  MSVC does not permit:\n//\n//    extern int const num;\n//    constexpr int const num = 3;\n//\n//  Instead:\n//\n//    extern int const num;\n//    FOLLY_STORAGE_CONSTEXPR int const num = 3;\n//\n//  True as of MSVC 2017.\n#ifdef _MSC_VER\n#define FOLLY_STORAGE_CONSTEXPR\n#else\n#define FOLLY_STORAGE_CONSTEXPR constexpr\n#endif\n\n//  FOLLY_CXX23_CONSTEXPR\n//\n//  C++23 permits more cases to be marked constexpr, including definitions of\n//  variables of non-literal type in constexpr function as long as they are not\n//  constant-evaluated.\n#if FOLLY_CPLUSPLUS >= 202302L\n#define FOLLY_CXX23_CONSTEXPR constexpr\n#else\n#define FOLLY_CXX23_CONSTEXPR\n#endif\n\n// C++20 constinit\n#if defined(__cpp_constinit) && __cpp_constinit >= 201907L\n#define FOLLY_CONSTINIT constinit\n#else\n#define FOLLY_CONSTINIT\n#endif\n\n#if defined(FOLLY_CFG_NO_COROUTINES)\n#define FOLLY_HAS_COROUTINES 0\n#define FOLLY_HAS_IMMOVABLE_COROUTINES 0\n#else\n// folly::coro requires C++17 support\n#if defined(__NVCC__)\n// For now, NVCC matches other compilers but does not offer coroutines.\n#define FOLLY_HAS_COROUTINES 0\n#elif defined(_WIN32) && defined(__clang__) && !defined(LLVM_COROUTINES) && \\\n    !defined(LLVM_COROUTINES_CPP20)\n// LLVM and MSVC coroutines are ABI incompatible, so for the MSVC implementation\n// of <experimental/coroutine> on Windows we *don't* have coroutines.\n//\n// LLVM_COROUTINES indicates that LLVM compatible header is added to include\n// path and can be used.\n//\n// LLVM_COROUTINES_CPP20 indicates that an LLVM compatible header using\n// <coroutine> is added to the include path and can be used.\n\n//\n// Worse, if we define FOLLY_HAS_COROUTINES 1 we will include\n// <experimental/coroutine> which will conflict with anyone who wants to load\n// the LLVM implementation of coroutines on Windows.\n#define FOLLY_HAS_COROUTINES 0\n#elif defined(_MSC_VER) && _MSC_VER && defined(_RESUMABLE_FUNCTIONS_SUPPORTED)\n// NOTE: MSVC 2017 does not currently support the full Coroutines TS since it\n// does not yet support symmetric-transfer.\n#define FOLLY_HAS_COROUTINES 0\n#elif (                                                                    \\\n    (defined(__cpp_coroutines) && __cpp_coroutines >= 201703L) ||          \\\n    (defined(__cpp_impl_coroutine) && __cpp_impl_coroutine >= 201902L)) && \\\n    (__has_include(<coroutine>) || __has_include(<experimental/coroutine>))\n#define FOLLY_HAS_COROUTINES 1\n// This is mainly to workaround bugs triggered by LTO, when stack allocated\n// variables in await_suspend end up on a coroutine frame.\n#define FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES FOLLY_NOINLINE\n#else\n#define FOLLY_HAS_COROUTINES 0\n#endif\n\n// NB: The C++20 requirement could be relaxed, but there's no clear benefit as\n// of right now.\n#if !FOLLY_HAS_COROUTINES || FOLLY_CPLUSPLUS < 202002L\n#define FOLLY_HAS_IMMOVABLE_COROUTINES 0\n// This logic is written as \"good until proven broken\" because it's possible\n// that there's a good compiler older than the oldest good version I checked.\n#elif defined(__clang_major__) && __clang_major__ <= 14\n//  - 12.0.1 is bad: https://godbolt.org/z/6s489xE8P\n//  - 14 is still bad: https://godbolt.org/z/nW1W8cWvb\n//  - 15.0.0 is good: https://godbolt.org/z/Tco4c9hbq and sEaKKTf8r\n#define FOLLY_HAS_IMMOVABLE_COROUTINES 0\n// On Windows, Clang with -fms-compatibility defines _MSC_FULL_VER to\n// emulate MSVC - even for newer versions of Clang.\n// Explicitly allow Clang 15+, prior to checking _MSC_FULL_VER\n#elif defined(__clang_major__) && __clang_major__ >= 15\n#define FOLLY_HAS_IMMOVABLE_COROUTINES 1\n// BEWARE: Older versions of Clang pretend to be MSVC and define\n// `_MSC_FULL_VER`\n#elif defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 192930040\n//  - 192930040 is bad: https://godbolt.org/z/E797W8xTT\n//  - 192930153 is good: https://godbolt.org/z/cM4nW5rTK\n#define FOLLY_HAS_IMMOVABLE_COROUTINES 0\n#else\n#define FOLLY_HAS_IMMOVABLE_COROUTINES 1 // good until proven broken\n#endif\n#endif // FOLLY_CFG_NO_COROUTINES\n\n// It'd be possible to relax this, by refactoring `folly/result` code down to\n// C++17, and by only blocking the coroutine support for non-coro compiles.\n// However, `result<T>` is primarily targeted at newer codebases.\n#if FOLLY_CPLUSPLUS >= 202002L && FOLLY_HAS_COROUTINES\n#define FOLLY_HAS_RESULT 1\n#else\n#define FOLLY_HAS_RESULT 0\n#endif\n\n// C++20 consteval\n#if FOLLY_CPLUSPLUS >= 202002L\n#define FOLLY_CONSTEVAL consteval\n#else\n#define FOLLY_CONSTEVAL constexpr\n#endif\n"
  },
  {
    "path": "folly/Preprocessor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CPortability.h>\n\n/**\n * Necessarily evil preprocessor-related amenities.\n */\n\n// MSVC's preprocessor is a pain, so we have to\n// forcefully expand the VA args in some places.\n#define FB_VA_GLUE(a, b) a b\n\n/**\n * FB_ONE_OR_NONE(hello, world) expands to hello and\n * FB_ONE_OR_NONE(hello) expands to nothing. This macro is used to\n * insert or eliminate text based on the presence of another argument.\n */\n#define FB_ONE_OR_NONE(a, ...) FB_VA_GLUE(FB_THIRD, (a, ##__VA_ARGS__, a))\n#define FB_THIRD(a, b, ...) __VA_ARGS__\n\n/**\n * Helper macro that extracts the first argument out of a list of any\n * number of arguments.\n */\n#define FB_ARG_1(a, ...) a\n\n/**\n * Helper macro that extracts the second argument out of a list of any\n * number of arguments. If only one argument is given, it returns\n * that.\n */\n#ifdef _MSC_VER\n// GCC refuses to expand this correctly if this macro itself was\n// called with FB_VA_GLUE :(\n#define FB_ARG_2_OR_1(...) \\\n  FB_VA_GLUE(FB_ARG_2_OR_1_IMPL, (__VA_ARGS__, __VA_ARGS__))\n#else\n#define FB_ARG_2_OR_1(...) FB_ARG_2_OR_1_IMPL(__VA_ARGS__, __VA_ARGS__)\n#endif\n// Support macro for the above\n#define FB_ARG_2_OR_1_IMPL(a, b, ...) b\n\n/**\n * Helper macro that provides a way to pass argument with commas in it to\n * some other macro whose syntax doesn't allow using extra parentheses.\n * Example:\n *\n *   #define MACRO(type, name) type name\n *   MACRO(FB_SINGLE_ARG(std::pair<size_t, size_t>), x);\n *\n */\n#define FB_SINGLE_ARG(...) __VA_ARGS__\n\n#define FOLLY_PP_DETAIL_APPEND_VA_ARG(...) , ##__VA_ARGS__\n\n/**\n * Helper macro that just ignores its parameters.\n */\n#define FOLLY_IGNORE(...)\n\n/**\n * Helper macro that just ignores its parameters and inserts a semicolon.\n */\n#define FOLLY_SEMICOLON(...) ;\n\n/**\n * FB_ANONYMOUS_VARIABLE(str) introduces an identifier starting with\n * str and ending with a number that varies with the line.\n */\n#ifndef FB_ANONYMOUS_VARIABLE\n#define FB_CONCATENATE_IMPL(s1, s2) s1##s2\n#define FB_CONCATENATE(s1, s2) FB_CONCATENATE_IMPL(s1, s2)\n#ifdef __COUNTER__\n// Modular builds build each module with its own preprocessor state, meaning\n// `__COUNTER__` no longer provides a unique number across a TU.  Instead of\n// calling back to just `__LINE__`, use a mix of `__COUNTER__` and `__LINE__`\n// to try provide as much uniqueness as possible.\n#if FOLLY_HAS_FEATURE(modules)\n#define FB_ANONYMOUS_VARIABLE(str) \\\n  FB_CONCATENATE(FB_CONCATENATE(FB_CONCATENATE(str, __COUNTER__), _), __LINE__)\n#else\n#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __COUNTER__)\n#endif\n#else\n#define FB_ANONYMOUS_VARIABLE(str) FB_CONCATENATE(str, __LINE__)\n#endif\n// FB_ANONYMOUS_VARIABLE_ODR_SAFE doesn't rely on __COUNTER__ and is safe to use\n// in headers that should not violate the one-definition rule (ODR). It is\n// especially useful for C++ modules that check for ODR violations.\n#define FB_ANONYMOUS_VARIABLE_ODR_SAFE(str) FB_CONCATENATE(str, __LINE__)\n#endif\n\n/**\n * Use FOLLY_PP_STRINGIZE(x) when you'd want to do what #x does inside\n * another macro expansion.\n */\n#define FOLLY_PP_STRINGIZE(x) #x\n\n/**\n * Use FOLLY_PP_STRINGIZE_MACRO(x) when you want the string representation\n * of a non-string c++ preprocessing macro value, ex\n * FOLLY_PP_STRINGIZE_MACRO(__LINE__).\n */\n#define FOLLY_PP_STRINGIZE_MACRO(x) FOLLY_PP_STRINGIZE(x)\n\n#define FOLLY_PP_DETAIL_NARGS_1( \\\n    dummy,                       \\\n    _15,                         \\\n    _14,                         \\\n    _13,                         \\\n    _12,                         \\\n    _11,                         \\\n    _10,                         \\\n    _9,                          \\\n    _8,                          \\\n    _7,                          \\\n    _6,                          \\\n    _5,                          \\\n    _4,                          \\\n    _3,                          \\\n    _2,                          \\\n    _1,                          \\\n    _0,                          \\\n    ...)                         \\\n  _0\n#define FOLLY_PP_DETAIL_NARGS(...) \\\n  FOLLY_PP_DETAIL_NARGS_1(         \\\n      dummy,                       \\\n      ##__VA_ARGS__,               \\\n      15,                          \\\n      14,                          \\\n      13,                          \\\n      12,                          \\\n      11,                          \\\n      10,                          \\\n      9,                           \\\n      8,                           \\\n      7,                           \\\n      6,                           \\\n      5,                           \\\n      4,                           \\\n      3,                           \\\n      2,                           \\\n      1,                           \\\n      0)\n\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_0(fn, ...)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_1(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_0(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_2(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_1(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_3(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_2(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_4(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_3(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_5(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_4(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_6(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_5(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_7(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_6(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_8(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_7(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_9(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_8(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_10(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_9(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_11(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_10(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_12(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_11(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_13(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_12(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_14(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_13(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_REC_15(fn, a, ...) \\\n  fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_14(fn, __VA_ARGS__)\n\n#define FOLLY_PP_DETAIL_FOR_EACH_2(fn, n, ...) \\\n  FOLLY_PP_DETAIL_FOR_EACH_REC_##n(fn, __VA_ARGS__)\n#define FOLLY_PP_DETAIL_FOR_EACH_1(fn, n, ...) \\\n  FOLLY_PP_DETAIL_FOR_EACH_2(fn, n, __VA_ARGS__)\n\n/**\n *  FOLLY_PP_FOR_EACH\n *\n *  Used to invoke a preprocessor macro, the name of which is passed as the\n *  first argument, once for each subsequent variadic argument.\n *\n *  At present, supports [0, 16) arguments.\n *\n *  This input:\n *\n *    #define DOIT(a) go_do_it(a);\n *    FOLLY_PP_FOR_EACH(DOIT, 3, 5, 7)\n *    #undef DOIT\n *\n *  Expands to this output (with whitespace adjusted for clarity):\n *\n *    go_do_it(3);\n *    go_do_it(5);\n *    go_do_it(7);\n */\n#define FOLLY_PP_FOR_EACH(fn, ...) \\\n  FOLLY_PP_DETAIL_FOR_EACH_1(      \\\n      fn, FOLLY_PP_DETAIL_NARGS(__VA_ARGS__), __VA_ARGS__)\n\n#if defined(U)\n#error defined(U) // literal U is used below\n#endif\n\n//  FOLLY_PP_CONSTINIT_LINE_UNSIGNED\n//\n//  MSVC with /ZI has a special backing variable for __LINE__ which is not a\n//  literal - but token-pasting __LINE__ suppresses this backing variable. This\n//  is done in MSVC to support its edit-and-continue feature.\n//\n//  This macro evaluates to:\n//    __LINE__ ## U\n//\n//  So this macro may be ill-suited to cases which need exactly __LINE__.\n//\n//  Documentation:\n//    https://docs.microsoft.com/en-us/cpp/build/reference/z7-zi-zi-debug-information-format?view=msvc-170#zi-1\n//  Workaround:\n//    https://stackoverflow.com/questions/57137351/line-is-not-constexpr-in-msvc\n#define FOLLY_PP_CONSTINIT_LINE_UNSIGNED FB_CONCATENATE(__LINE__, U)\n"
  },
  {
    "path": "folly/ProducerConsumerQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <cstdlib>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <folly/concurrency/CacheLocality.h>\n\nnamespace folly {\n\n/*\n * ProducerConsumerQueue is a one producer and one consumer queue\n * without locks.\n */\ntemplate <class T>\nstruct ProducerConsumerQueue {\n  using value_type = T;\n\n  ProducerConsumerQueue(const ProducerConsumerQueue&) = delete;\n  ProducerConsumerQueue& operator=(const ProducerConsumerQueue&) = delete;\n\n  // size must be >= 2.\n  //\n  // Also, note that the number of usable slots in the queue at any\n  // given time is actually (size-1), so if you start with an empty queue,\n  // isFull() will return true after size-1 insertions.\n  explicit ProducerConsumerQueue(uint32_t size)\n      : size_(size),\n        records_(static_cast<T*>(std::malloc(sizeof(T) * size))),\n        readIndex_(0),\n        writeIndex_(0) {\n    assert(size >= 2);\n    if (!records_) {\n      throw std::bad_alloc();\n    }\n  }\n\n  ~ProducerConsumerQueue() {\n    // We need to destruct anything that may still exist in our queue.\n    // (No real synchronization needed at destructor time: only one\n    // thread can be doing this.)\n    if (!std::is_trivially_destructible<T>::value) {\n      size_t readIndex = readIndex_;\n      size_t endIndex = writeIndex_;\n      while (readIndex != endIndex) {\n        records_[readIndex].~T();\n        if (++readIndex == size_) {\n          readIndex = 0;\n        }\n      }\n    }\n\n    std::free(records_);\n  }\n\n  template <class... Args>\n  bool write(Args&&... recordArgs) {\n    auto const currentWrite = writeIndex_.load(std::memory_order_relaxed);\n    auto nextRecord = currentWrite + 1;\n    if (nextRecord == size_) {\n      nextRecord = 0;\n    }\n    if (nextRecord != readIndex_.load(std::memory_order_acquire)) {\n      new (&records_[currentWrite]) T(std::forward<Args>(recordArgs)...);\n      writeIndex_.store(nextRecord, std::memory_order_release);\n      return true;\n    }\n\n    // queue is full\n    return false;\n  }\n\n  // move (or copy) the value at the front of the queue to given variable\n  bool read(T& record) {\n    auto const currentRead = readIndex_.load(std::memory_order_relaxed);\n    if (currentRead == writeIndex_.load(std::memory_order_acquire)) {\n      // queue is empty\n      return false;\n    }\n\n    auto nextRecord = currentRead + 1;\n    if (nextRecord == size_) {\n      nextRecord = 0;\n    }\n    record = std::move(records_[currentRead]);\n    records_[currentRead].~T();\n    readIndex_.store(nextRecord, std::memory_order_release);\n    return true;\n  }\n\n  // pointer to the value at the front of the queue (for use in-place) or\n  // nullptr if empty.\n  T* frontPtr() {\n    auto const currentRead = readIndex_.load(std::memory_order_relaxed);\n    if (currentRead == writeIndex_.load(std::memory_order_acquire)) {\n      // queue is empty\n      return nullptr;\n    }\n    return &records_[currentRead];\n  }\n\n  // queue must not be empty\n  void popFront() {\n    auto const currentRead = readIndex_.load(std::memory_order_relaxed);\n    assert(currentRead != writeIndex_.load(std::memory_order_acquire));\n\n    auto nextRecord = currentRead + 1;\n    if (nextRecord == size_) {\n      nextRecord = 0;\n    }\n    records_[currentRead].~T();\n    readIndex_.store(nextRecord, std::memory_order_release);\n  }\n\n  bool isEmpty() const {\n    return readIndex_.load(std::memory_order_acquire) ==\n        writeIndex_.load(std::memory_order_acquire);\n  }\n\n  bool isFull() const {\n    auto nextRecord = writeIndex_.load(std::memory_order_acquire) + 1;\n    if (nextRecord == size_) {\n      nextRecord = 0;\n    }\n    if (nextRecord != readIndex_.load(std::memory_order_acquire)) {\n      return false;\n    }\n    // queue is full\n    return true;\n  }\n\n  // * If called by consumer, then true size may be more (because producer may\n  //   be adding items concurrently).\n  // * If called by producer, then true size may be less (because consumer may\n  //   be removing items concurrently).\n  // * It is undefined to call this from any other thread.\n  size_t sizeGuess() const {\n    int ret = writeIndex_.load(std::memory_order_acquire) -\n        readIndex_.load(std::memory_order_acquire);\n    if (ret < 0) {\n      ret += size_;\n    }\n    return ret;\n  }\n\n  // maximum number of items in the queue.\n  size_t capacity() const { return size_ - 1; }\n\n private:\n  using AtomicIndex = std::atomic<unsigned int>;\n\n  char pad0_[hardware_destructive_interference_size];\n  const uint32_t size_;\n  T* const records_;\n\n  alignas(hardware_destructive_interference_size) AtomicIndex readIndex_;\n  alignas(hardware_destructive_interference_size) AtomicIndex writeIndex_;\n\n  char pad1_[hardware_destructive_interference_size - sizeof(AtomicIndex)];\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/RWSpinLock.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/synchronization/RWSpinLock.h> // @shim\n"
  },
  {
    "path": "folly/Random-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_RANDOM_H_\n#error This file may only be included from folly/Random.h\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\nstruct SeedSeqSecureRandom {\n  using result_type = uint32_t;\n  template <typename Word>\n  void generate(Word* b, Word* e) {\n    static_assert(is_non_bool_integral_v<Word>);\n    static_assert(sizeof(Word) >= sizeof(result_type));\n    Random::secureRandom(b, (e - b) * sizeof(Word));\n  }\n};\n\n} // namespace detail\n\ntemplate <class RNG, class /* EnableIf */>\nvoid Random::seed(RNG& rng) {\n  detail::SeedSeqSecureRandom seq;\n  rng.seed(seq);\n}\n\ntemplate <class RNG, class /* EnableIf */>\nauto Random::create() -> RNG {\n  detail::SeedSeqSecureRandom seq;\n  return RNG(seq);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Random.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Random.h>\n\n#include <array>\n#include <mutex>\n#include <random>\n\n#include <glog/logging.h>\n#include <folly/CppAttributes.h>\n#include <folly/SingletonThreadLocal.h>\n#include <folly/ThreadLocal.h>\n#include <folly/detail/FileUtilDetail.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/SysTime.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\n#ifdef _WIN32\n#include <wincrypt.h> // @manual\n#pragma comment(lib, \"advapi32.lib\")\n#else\n#include <fcntl.h>\n#endif\n\n#if FOLLY_HAVE_GETRANDOM\n#include <sys/random.h>\n#endif\n\nnamespace folly {\n\nnamespace {\n\nvoid readRandomDevice(void* data, size_t size) {\n#ifdef _WIN32\n  static auto const cryptoProv = [] {\n    HCRYPTPROV prov;\n    if (!CryptAcquireContext(\n            &prov, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {\n      if (GetLastError() == NTE_BAD_KEYSET) {\n        // Mostly likely cause of this is that no key container\n        // exists yet, so try to create one.\n        PCHECK(CryptAcquireContext(\n            &prov, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET));\n      } else {\n        LOG(FATAL) << \"Failed to acquire the default crypto context.\";\n      }\n    }\n    return prov;\n  }();\n  CHECK(size <= std::numeric_limits<DWORD>::max());\n  PCHECK(CryptGenRandom(cryptoProv, (DWORD)size, (BYTE*)data));\n#else\n  ssize_t bytesRead = 0;\n  auto gen = [](int, void* buf, size_t buflen) {\n#if FOLLY_HAVE_GETRANDOM\n    auto flags = 0u;\n    return ::getrandom(buf, buflen, flags);\n#else\n    [](...) {}(buf, buflen);\n    errno = ENOSYS;\n    return -1;\n#endif\n  };\n  bytesRead = fileutil_detail::wrapFull(gen, -1, data, size);\n  if (bytesRead == -1 && errno == ENOSYS) {\n    // Keep the random device open for the duration of the program.\n    static int randomFd = ::open(\"/dev/urandom\", O_RDONLY | O_CLOEXEC);\n    PCHECK(randomFd >= 0);\n    bytesRead = fileutil_detail::wrapFull(::read, randomFd, data, size);\n  }\n  PCHECK(bytesRead >= 0);\n  CHECK_EQ(size_t(bytesRead), size);\n#endif\n}\n\nclass BufferedRandomDevice {\n public:\n  static constexpr size_t kDefaultBufferSize = 128;\n\n  static void notifyNewGlobalEpoch() { ++globalEpoch_; }\n\n  explicit BufferedRandomDevice(size_t bufferSize = kDefaultBufferSize);\n\n  void get(void* data, size_t size) {\n    if (FOLLY_LIKELY(epoch_ == globalEpoch_ && size <= remaining())) {\n      memcpy(data, ptr_, size);\n      ptr_ += size;\n    } else {\n      getSlow(static_cast<unsigned char*>(data), size);\n    }\n  }\n\n private:\n  void getSlow(unsigned char* data, size_t size);\n\n  inline size_t remaining() const {\n    return size_t(buffer_.get() + bufferSize_ - ptr_);\n  }\n\n  static relaxed_atomic<size_t> globalEpoch_;\n\n  size_t epoch_{size_t(-1)}; // refill on first use\n  const size_t bufferSize_;\n  std::unique_ptr<unsigned char[]> buffer_;\n  unsigned char* ptr_;\n};\n\nrelaxed_atomic<size_t> BufferedRandomDevice::globalEpoch_{0};\nstruct RandomTag {};\n\nBufferedRandomDevice::BufferedRandomDevice(size_t bufferSize)\n    : bufferSize_(bufferSize),\n      buffer_(new unsigned char[bufferSize]),\n      ptr_(buffer_.get() + bufferSize) { // refill on first use\n  [[maybe_unused]] static auto const init = [] {\n    AtFork::registerHandler(\n        nullptr,\n        /*prepare*/ []() { return true; },\n        /*parent*/ []() {},\n        /*child*/\n        []() {\n          // Ensure child and parent do not share same entropy pool.\n          BufferedRandomDevice::notifyNewGlobalEpoch();\n        });\n    return 0;\n  }();\n}\n\nvoid BufferedRandomDevice::getSlow(unsigned char* data, size_t size) {\n  if (epoch_ != globalEpoch_) {\n    epoch_ = globalEpoch_;\n    ptr_ = buffer_.get() + bufferSize_;\n  }\n\n  DCHECK_GT(size, remaining());\n  if (size >= bufferSize_) {\n    // Just read directly.\n    readRandomDevice(data, size);\n    return;\n  }\n\n  size_t copied = remaining();\n  memcpy(data, ptr_, copied);\n  data += copied;\n  size -= copied;\n\n  // refill\n  readRandomDevice(buffer_.get(), bufferSize_);\n  ptr_ = buffer_.get();\n\n  memcpy(data, ptr_, size);\n  ptr_ += size;\n}\n\n} // namespace\n\nvoid Random::secureRandom(void* data, size_t size) {\n  using Single = SingletonThreadLocal<BufferedRandomDevice, RandomTag>;\n  Single::get().get(data, size);\n}\n\nThreadLocalPRNG::result_type ThreadLocalPRNG::operator()() {\n  struct Wrapper {\n    Generator object{Random::create()};\n  };\n  using Single = SingletonThreadLocal<Wrapper, RandomTag>;\n  return Single::get().object();\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/Random.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbvref_random\n//\n\n#pragma once\n#define FOLLY_RANDOM_H_\n\n#include <cstdint>\n#include <random>\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Bits.h>\n#include <folly/random/xoshiro256pp.h>\n\n#if defined(FOLLY_HAVE_EXTRANDOM_SFMT19937) && FOLLY_HAVE_EXTRANDOM_SFMT19937\n#include <ext/random>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\nusing DefaultGenerator = folly::xoshiro256pp_32;\n\n#if defined(FOLLY_HAVE_EXTRANDOM_SFMT19937) && FOLLY_HAVE_EXTRANDOM_SFMT19937\nusing LegacyGenerator = __gnu_cxx::sfmt19937;\n#else\nusing LegacyGenerator = std::mt19937;\n#endif\n\n} // namespace detail\n\n/**\n * A PRNG with one instance per thread. This PRNG uses a mersenne twister random\n * number generator and is seeded from /dev/urandom. It should not be used for\n * anything which requires security, only for statistical randomness.\n */\nclass ThreadLocalPRNG {\n  using Generator = detail::DefaultGenerator;\n\n public:\n  using result_type = Generator::result_type;\n\n  result_type operator()();\n\n  static constexpr result_type min() { return Generator::min(); }\n  static constexpr result_type max() { return Generator::max(); }\n};\n\nclass Random {\n private:\n  template <class RNG>\n  using ValidRNG = typename std::\n      enable_if<std::is_unsigned<invoke_result_t<RNG&>>::value, RNG>::type;\n\n  template <class T>\n  class SecureRNG {\n   public:\n    using result_type = typename std::enable_if<\n        std::is_integral<T>::value && !std::is_same<T, bool>::value,\n        T>::type;\n\n    result_type operator()() { return Random::secureRandom<result_type>(); }\n\n    static constexpr result_type min() {\n      return std::numeric_limits<result_type>::min();\n    }\n\n    static constexpr result_type max() {\n      return std::numeric_limits<result_type>::max();\n    }\n  };\n\n  // Whether RNG output is surjective and uniform when truncated to ResultType.\n  template <class RNG, class ResultType>\n  static constexpr bool UniformRNG =\n      (std::is_unsigned<ResultType>::value &&\n       std::is_unsigned<typename RNG::result_type>::value &&\n       // RNG range covers ResultType.\n       RNG::min() == 0 &&\n       RNG::max() >= std::numeric_limits<ResultType>::max() &&\n       // Truncating the output maintains uniformness.\n       (~RNG::max() == 0 || isPowTwo(RNG::max() + 1)));\n\n public:\n  using DefaultGenerator = detail::DefaultGenerator;\n  using LegacyGenerator = detail::LegacyGenerator;\n\n  /**\n   * Get secure random bytes. (On Linux and OSX, this means /dev/urandom).\n   */\n  static void secureRandom(void* data, size_t size);\n\n  /**\n   * Shortcut to get a secure random value of integral type.\n   */\n  template <class T>\n  static typename std::enable_if<\n      std::is_integral<T>::value && !std::is_same<T, bool>::value,\n      T>::type\n  secureRandom() {\n    T val;\n    secureRandom(&val, sizeof(val));\n    return val;\n  }\n\n  /**\n   * Returns a secure random uint32_t\n   */\n  static uint32_t secureRand32() { return secureRandom<uint32_t>(); }\n\n  /**\n   * Returns a secure random uint32_t in [0, max). If max == 0, returns 0.\n   */\n  static uint32_t secureRand32(uint32_t max) {\n    SecureRNG<uint32_t> srng;\n    return rand32(max, srng);\n  }\n\n  /**\n   * Returns a secure random uint32_t in [min, max). If min == max, returns min.\n   */\n  static uint32_t secureRand32(uint32_t min, uint32_t max) {\n    SecureRNG<uint32_t> srng;\n    return rand32(min, max, srng);\n  }\n\n  /**\n   * Returns a secure random uint64_t\n   */\n  static uint64_t secureRand64() { return secureRandom<uint64_t>(); }\n\n  /**\n   * Returns a secure random uint64_t in [0, max). If max == 0, returns 0.\n   */\n  static uint64_t secureRand64(uint64_t max) {\n    SecureRNG<uint64_t> srng;\n    return rand64(max, srng);\n  }\n\n  /**\n   * Returns a secure random uint64_t in [min, max). If min == max, returns min.\n   */\n  static uint64_t secureRand64(uint64_t min, uint64_t max) {\n    SecureRNG<uint64_t> srng;\n    return rand64(min, max, srng);\n  }\n\n  /**\n   * Returns true 1/n of the time. If n == 0, always returns false\n   */\n  static bool secureOneIn(uint32_t n) {\n    if (n < 2) {\n      return n;\n    }\n    SecureRNG<uint32_t> srng;\n    return rand32(0, n, srng) == 0;\n  }\n\n  /**\n   * Returns true 1/n of the time. If n == 0, always returns false\n   */\n  static bool secureOneIn64(uint64_t n) {\n    if (n < 2) {\n      return n;\n    }\n    SecureRNG<uint64_t> srng;\n    return rand64(0, n, srng) == 0;\n  }\n\n  /**\n   * Returns a secure double in [0, 1)\n   */\n  static double secureRandDouble01() {\n    SecureRNG<uint64_t> srng;\n    return randDouble01(srng);\n  }\n\n  /**\n   * Returns a secure double in [min, max), if min == max, returns min.\n   */\n  static double secureRandDouble(double min, double max) {\n    SecureRNG<uint64_t> srng;\n    return randDouble(min, max, srng);\n  }\n\n  /**\n   * (Re-)Seed an existing RNG with a good seed.\n   *\n   * Note that you should usually use ThreadLocalPRNG unless you need\n   * reproducibility (such as during a test), in which case you'd want\n   * to create a RNG with a good seed in production, and seed it yourself\n   * in test.\n   */\n  template <class RNG = DefaultGenerator, class /* EnableIf */ = ValidRNG<RNG>>\n  static void seed(RNG& rng);\n\n  /**\n   * Create a new RNG, seeded with a good seed.\n   *\n   * Note that you should usually use ThreadLocalPRNG unless you need\n   * reproducibility (such as during a test), in which case you'd want\n   * to create a RNG with a good seed in production, and seed it yourself\n   * in test.\n   */\n  template <class RNG = DefaultGenerator, class /* EnableIf */ = ValidRNG<RNG>>\n  static RNG create();\n\n  /**\n   * Create a new RNG, which can be used for applications that require secure\n   * randomness.\n   *\n   * The resulting RNG will have worse performance than one created with\n   * create(), so use it if you need the security.\n   */\n  static SecureRNG<uint32_t> createSecure() { return SecureRNG<uint32_t>(); }\n\n  /**\n   * Returns a random uint32_t\n   */\n  static uint32_t rand32() { return rand32(ThreadLocalPRNG()); }\n\n  /**\n   * Returns a random uint32_t given a specific RNG\n   */\n  template <class RNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static uint32_t rand32(RNG&& rng) {\n    if constexpr (UniformRNG<std::decay_t<RNG>, uint32_t>) {\n      return static_cast<uint32_t>(rng());\n    } else {\n      return std::uniform_int_distribution<uint32_t>(\n          0, std::numeric_limits<uint32_t>::max())(rng);\n    }\n  }\n\n  /**\n   * Returns a random uint32_t in [0, max). If max == 0, returns 0.\n   */\n  static uint32_t rand32(uint32_t max) {\n    return rand32(0, max, ThreadLocalPRNG());\n  }\n\n  /**\n   * Returns a random uint32_t in [0, max) given a specific RNG.\n   * If max == 0, returns 0.\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static uint32_t rand32(uint32_t max, RNG&& rng) {\n    return rand32(0, max, rng);\n  }\n\n  /**\n   * Returns a random uint32_t in [min, max). If min == max, returns min.\n   */\n  static uint32_t rand32(uint32_t min, uint32_t max) {\n    return rand32(min, max, ThreadLocalPRNG());\n  }\n\n  /**\n   * Returns a random uint32_t in [min, max) given a specific RNG.\n   * If min == max, returns min.\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static uint32_t rand32(uint32_t min, uint32_t max, RNG&& rng) {\n    if (min == max) {\n      return min;\n    }\n    return std::uniform_int_distribution<uint32_t>(min, max - 1)(rng);\n  }\n\n  /**\n   * Returns a random uint64_t\n   */\n  static uint64_t rand64() { return rand64(ThreadLocalPRNG()); }\n\n  /**\n   * Returns a random uint64_t\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static uint64_t rand64(RNG&& rng) {\n    if constexpr (UniformRNG<std::decay_t<RNG>, uint64_t>) {\n      return rng();\n    } else if constexpr (UniformRNG<std::decay_t<RNG>, uint32_t>) {\n      return (static_cast<uint64_t>(rng()) << 32) |\n          static_cast<uint32_t>(rng());\n    } else {\n      return std::uniform_int_distribution<uint64_t>(\n          0, std::numeric_limits<uint64_t>::max())(rng);\n    }\n  }\n\n  /**\n   * Returns a random uint64_t in [0, max). If max == 0, returns 0.\n   */\n  static uint64_t rand64(uint64_t max) {\n    return rand64(0, max, ThreadLocalPRNG());\n  }\n\n  /**\n   * Returns a random uint64_t in [0, max). If max == 0, returns 0.\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static uint64_t rand64(uint64_t max, RNG&& rng) {\n    return rand64(0, max, rng);\n  }\n\n  /**\n   * Returns a random uint64_t in [min, max). If min == max, returns min.\n   */\n  static uint64_t rand64(uint64_t min, uint64_t max) {\n    return rand64(min, max, ThreadLocalPRNG());\n  }\n\n  /**\n   * Returns a random uint64_t in [min, max). If min == max, returns min.\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static uint64_t rand64(uint64_t min, uint64_t max, RNG&& rng) {\n    if (min == max) {\n      return min;\n    }\n    return std::uniform_int_distribution<uint64_t>(min, max - 1)(rng);\n  }\n\n  /**\n   * Returns true 1/n of the time. If n == 0, always returns false\n   */\n  static bool oneIn(uint32_t n) { return oneIn(n, ThreadLocalPRNG()); }\n\n  /**\n   * Returns true 1/n of the time. If n == 0, always returns false\n   */\n  static bool oneIn64(uint64_t n) { return oneIn64(n, ThreadLocalPRNG()); }\n\n  /**\n   * Returns true 1/n of the time. If n == 0, always returns false\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static bool oneIn(uint32_t n, RNG&& rng) {\n    if (n < 2) {\n      return n;\n    }\n    return rand32(0, n, std::forward<RNG>(rng)) == 0;\n  }\n\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static bool oneIn64(uint64_t n, RNG&& rng) {\n    if (n < 2) {\n      return n;\n    }\n    return rand64(0, n, std::forward<RNG>(rng)) == 0;\n  }\n\n  /**\n   * Returns true with the probability of p, false otherwise\n   */\n  static bool randBool(double p) { return randBool(p, ThreadLocalPRNG()); }\n\n  /**\n   * Returns true with the probability of p, false otherwise\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static bool randBool(double p, RNG&& rng) {\n    return randDouble01(std::forward<RNG>(rng)) < p;\n  }\n\n  /**\n   * Returns a double in [0, 1)\n   */\n  static double randDouble01() { return randDouble01(ThreadLocalPRNG()); }\n\n  /**\n   * Returns a double in [0, 1)\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static double randDouble01(RNG&& rng) {\n    // Assuming 64-bit IEEE754 doubles, numbers in the form k/2^53 can be\n    // represented exactly, so we can sample uniformly in [0, 1) by sampling an\n    // integer in [0, 2^53) and scaling accordingly. This is the highest\n    // precision we can obtain if we want a symmetric output distribution.\n    // See https://prng.di.unimi.it/#remarks for more details.\n    static_assert(\n        std::numeric_limits<double>::digits == 53, \"Unsupported double type\");\n    return (rand64(std::forward<RNG>(rng)) >> 11) * 0x1.0p-53;\n  }\n\n  /**\n   * Returns a double in [min, max), if min == max, returns min.\n   */\n  static double randDouble(double min, double max) {\n    return randDouble(min, max, ThreadLocalPRNG());\n  }\n\n  /**\n   * Returns a double in [min, max), if min == max, returns min.\n   */\n  template <class RNG = ThreadLocalPRNG, class /* EnableIf */ = ValidRNG<RNG>>\n  static double randDouble(double min, double max, RNG&& rng) {\n    if (std::fabs(max - min) < std::numeric_limits<double>::epsilon()) {\n      return min;\n    }\n    return std::uniform_real_distribution<double>(min, max)(rng);\n  }\n};\n\n/*\n * Return a good seed for a random number generator.\n * Note that this is a legacy function, as it returns a 32-bit value, which\n * is too small to be useful as a \"real\" RNG seed. Use the functions in class\n * Random instead.\n */\ninline uint32_t randomNumberSeed() {\n  return Random::rand32();\n}\n\n} // namespace folly\n\n#include <folly/Random-inl.h>\n"
  },
  {
    "path": "folly/Range.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_range\n//\n\n/**\n * Range abstraction using a pair of iterators. It is not\n * similar to boost's range abstraction because an API identical\n * with the former StringPiece class is required, which is used a lot\n * internally. This abstraction does fulfill the needs of boost's\n * range-oriented algorithms though.\n *\n * Note: (Keep memory lifetime in mind when using this class, since it\n * does not manage the data it refers to - just like an iterator\n * would not.)\n *\n * Additional documentation is in folly/docs/Range.md\n *\n * @refcode folly/docs/examples/folly/Range.h\n * @struct folly::range\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/hash/SpookyHashV2.h>\n#include <folly/lang/CString.h>\n#include <folly/lang/Exception.h>\n#include <folly/portability/Constexpr.h>\n\n#include <algorithm>\n#include <array>\n#include <cassert>\n#include <climits>\n#include <cstddef>\n#include <cstring>\n#include <iosfwd>\n#include <iterator>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <type_traits>\n\n#if defined(__cpp_lib_ranges)\n#include <ranges>\n#endif\n\n#if __has_include(<fmt/format.h>)\n#include <fmt/format.h>\n#endif\n\n#include <folly/CpuId.h>\n#include <folly/Likely.h>\n#include <folly/Traits.h>\n#include <folly/detail/RangeCommon.h>\n#include <folly/detail/RangeSimd.h>\n\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n\nnamespace folly {\n\n/**\n * Ubiquitous helper template for knowing what's a string.\n */\ntemplate <class T>\nstruct IsSomeString : std::false_type {};\n\ntemplate <typename Alloc>\nstruct IsSomeString<std::basic_string<char, std::char_traits<char>, Alloc>>\n    : std::true_type {};\n\ntemplate <class Iter>\nclass Range;\n\n/**\n * Finds the first occurrence of needle in haystack. The algorithm is on\n * average faster than O(haystack.size() * needle.size()) but not as fast\n * as Boyer-Moore. On the upside, it does not do any upfront\n * preprocessing and does not allocate memory.\n */\ntemplate <\n    class Iter,\n    class Comp = std::equal_to<typename Range<Iter>::value_type>>\ninline size_t qfind(\n    const Range<Iter>& haystack, const Range<Iter>& needle, Comp eq = Comp());\n\n/**\n * Finds the first occurrence of needle in haystack. The result is the\n * offset reported to the beginning of haystack, or string::npos if\n * needle wasn't found.\n */\ntemplate <class Iter>\nsize_t qfind(\n    const Range<Iter>& haystack,\n    const typename Range<Iter>::value_type& needle);\n\n/**\n * Finds the last occurrence of needle in haystack. The result is the\n * offset reported to the beginning of haystack, or string::npos if\n * needle wasn't found.\n */\ntemplate <class Iter>\nsize_t rfind(\n    const Range<Iter>& haystack,\n    const typename Range<Iter>::value_type& needle);\n\n/**\n * Finds the first occurrence of any element of needle in\n * haystack. The algorithm is O(haystack.size() * needle.size()).\n */\ntemplate <class Iter>\ninline size_t qfind_first_of(\n    const Range<Iter>& haystack, const Range<Iter>& needle);\n\n/**\n * Small internal helper - returns the value just before an iterator.\n */\nnamespace detail {\n\n/*\n * Use IsCharPointer<T>::type to enable const char* or char*.\n * Use IsCharPointer<T>::const_type to enable only const char*.\n */\ntemplate <class T>\nstruct IsCharPointer {};\n\ntemplate <>\nstruct IsCharPointer<char*> {\n  using type = int;\n};\n\ntemplate <>\nstruct IsCharPointer<const char*> {\n  using const_type = int;\n  using type = int;\n};\n\n#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L\ntemplate <>\nstruct IsCharPointer<char8_t*> {\n  using type = int;\n};\n\ntemplate <>\nstruct IsCharPointer<const char8_t*> {\n  using const_type = int;\n  using type = int;\n};\n#endif\n\ntemplate <class T>\nstruct IsUnsignedCharPointer {};\n\ntemplate <>\nstruct IsUnsignedCharPointer<unsigned char*> {\n  using type = int;\n};\n\ntemplate <>\nstruct IsUnsignedCharPointer<const unsigned char*> {\n  using const_type = int;\n  using type = int;\n};\n\nvoid range_is_char_type_f_(char const*);\nvoid range_is_char_type_f_(wchar_t const*);\n#if (defined(__cpp_char8_t) && __cpp_char8_t >= 201811L) || \\\n    FOLLY_CPLUSPLUS >= 202002\nvoid range_is_char_type_f_(char8_t const*);\n#endif\nvoid range_is_char_type_f_(char16_t const*);\nvoid range_is_char_type_f_(char32_t const*);\ntemplate <typename Iter>\nusing range_is_char_type_d_ =\n    decltype(folly::detail::range_is_char_type_f_(FOLLY_DECLVAL(Iter)));\ntemplate <typename Iter>\nconstexpr bool range_is_char_type_v_ =\n    is_detected_v<range_is_char_type_d_, Iter>;\n\nvoid range_is_byte_type_f_(unsigned char const*);\nvoid range_is_byte_type_f_(signed char const*);\nvoid range_is_byte_type_f_(std::byte const*);\ntemplate <typename Iter>\nusing range_is_byte_type_d_ =\n    decltype(folly::detail::range_is_byte_type_f_(FOLLY_DECLVAL(Iter)));\ntemplate <typename Iter>\nconstexpr bool range_is_byte_type_v_ =\n    is_detected_v<range_is_byte_type_d_, Iter>;\n\nstruct range_traits_char_ {\n  template <typename Value>\n  using apply = std::char_traits<Value>;\n};\nstruct range_traits_byte_ {\n  template <typename Value>\n  struct apply {\n    FOLLY_ERASE static constexpr bool eq(Value a, Value b) { return a == b; }\n\n    FOLLY_ERASE static constexpr int compare(\n        Value const* a, Value const* b, std::size_t c) {\n      return !c ? 0 : std::memcmp(a, b, c);\n    }\n  };\n};\nstruct range_traits_fbck_ {\n  template <typename Value>\n  struct apply {\n    FOLLY_ERASE static constexpr bool eq(Value a, Value b) { return a == b; }\n\n    FOLLY_ERASE static constexpr int compare(\n        Value const* a, Value const* b, std::size_t c) {\n      while (c--) {\n        auto&& ai = *a++;\n        auto&& bi = *b++;\n        if (ai < bi) {\n          return -1;\n        }\n        if (bi < ai) {\n          return +1;\n        }\n      }\n      return 0;\n    }\n  };\n};\n\ntemplate <typename Iter>\nusing range_traits_c_ = conditional_t<\n    range_is_char_type_v_<Iter>,\n    range_traits_char_,\n    conditional_t< //\n        range_is_byte_type_v_<Iter>,\n        range_traits_byte_,\n        range_traits_fbck_>>;\ntemplate <typename Iter, typename Value>\nusing range_traits_t_ = typename range_traits_c_<Iter>::template apply<Value>;\n\n} // namespace detail\n\ntemplate <class Iter>\nclass Range {\n private:\n  using iter_traits = std::iterator_traits<Iter>;\n\n  template <typename Alloc>\n  using string = std::basic_string<char, std::char_traits<char>, Alloc>;\n\n public:\n  using value_type = typename iter_traits::value_type;\n  using size_type = std::size_t;\n  using difference_type = typename iter_traits::difference_type;\n  using iterator = Iter;\n  using const_iterator = Iter;\n  using reference = typename iter_traits::reference;\n  using const_reference = conditional_t<\n      std::is_lvalue_reference_v<reference>,\n      std::add_lvalue_reference_t<\n          std::add_const_t<std::remove_reference_t<reference>>>,\n      conditional_t<\n          std::is_rvalue_reference_v<reference>,\n          std::add_rvalue_reference_t<\n              std::add_const_t<std::remove_reference_t<reference>>>,\n          reference>>;\n\n  /*\n   * For MutableStringPiece and MutableByteRange we define StringPiece\n   * and ByteRange as const_range_type (for everything else its just\n   * identity). We do that to enable operations such as find with\n   * args which are const.\n   */\n  using const_range_type = typename std::conditional<\n      std::is_same<Iter, char*>::value ||\n          std::is_same<Iter, unsigned char*>::value,\n      Range<const value_type*>,\n      Range<Iter>>::type;\n\n  using traits_type = detail::range_traits_t_<Iter, value_type>;\n\n  static const size_type npos;\n\n  /**\n   * Works for all iterator\n   *\n   *\n   * @methodset Range\n   */\n  constexpr Range() : b_(), e_() {}\n\n  constexpr Range(const Range&) = default;\n  constexpr Range(Range&&) = default;\n\n public:\n  /**\n   * Works for all iterators\n   *\n   *\n   * @methodset Range\n   */\n  constexpr Range(Iter start, Iter end) : b_(start), e_(end) {}\n  /**\n   *  Works only for random-access iterators\n   *\n   *\n   * @methodset Range\n   */\n  constexpr Range(Iter start, size_t size) : b_(start), e_(start + size) {}\n\n  /* implicit */ Range(std::nullptr_t) = delete;\n\n  constexpr /* implicit */ Range(Iter str)\n      : b_(str), e_(str + constexpr_strlen(str)) {\n    static_assert(\n        std::is_same<int, typename detail::IsCharPointer<Iter>::type>::value,\n        \"This constructor is only available for character ranges\");\n  }\n\n  template <\n      class Alloc,\n      class T = Iter,\n      typename detail::IsCharPointer<T>::const_type = 0>\n  /* implicit */ Range(const string<Alloc>& str)\n      : b_(str.data()), e_(b_ + str.size()) {}\n\n  template <\n      class Alloc,\n      class T = Iter,\n      typename detail::IsCharPointer<T>::const_type = 0>\n  Range(const string<Alloc>& str, typename string<Alloc>::size_type startFrom) {\n    if (FOLLY_UNLIKELY(startFrom > str.size())) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    b_ = str.data() + startFrom;\n    e_ = str.data() + str.size();\n  }\n\n  template <\n      class Alloc,\n      class T = Iter,\n      typename detail::IsCharPointer<T>::const_type = 0>\n  Range(\n      const string<Alloc>& str,\n      typename string<Alloc>::size_type startFrom,\n      typename string<Alloc>::size_type size) {\n    if (FOLLY_UNLIKELY(startFrom > str.size())) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    b_ = str.data() + startFrom;\n    if (str.size() - startFrom < size) {\n      e_ = str.data() + str.size();\n    } else {\n      e_ = b_ + size;\n    }\n  }\n\n  Range(const Range& other, size_type first, size_type length = npos)\n      : Range(other.subpiece(first, length)) {}\n\n  template <\n      class Container,\n      class = typename std::enable_if<\n          std::is_same<Iter, typename Container::const_pointer>::value>::type,\n      class = decltype(\n          Iter(std::declval<Container const&>().data()),\n          Iter(\n              std::declval<Container const&>().data() +\n              std::declval<Container const&>().size()))>\n  /* implicit */ constexpr Range(Container const& container)\n      : Range(container.data(), container.size()) {}\n\n  template <\n      class Container,\n      class = typename std::enable_if<\n          std::is_same<Iter, typename Container::const_pointer>::value>::type,\n      class = decltype(\n          Iter(std::declval<Container const&>().data()),\n          Iter(\n              std::declval<Container const&>().data() +\n              std::declval<Container const&>().size()))>\n  Range(Container const& container, typename Container::size_type startFrom) {\n    auto const cdata = container.data();\n    auto const csize = container.size();\n    if (FOLLY_UNLIKELY(startFrom > csize)) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    b_ = cdata + startFrom;\n    e_ = cdata + csize;\n  }\n\n  template <\n      class Container,\n      class = typename std::enable_if<\n          std::is_same<Iter, typename Container::const_pointer>::value>::type,\n      class = decltype(\n          Iter(std::declval<Container const&>().data()),\n          Iter(\n              std::declval<Container const&>().data() +\n              std::declval<Container const&>().size()))>\n  Range(\n      Container const& container,\n      typename Container::size_type startFrom,\n      typename Container::size_type size) {\n    auto const cdata = container.data();\n    auto const csize = container.size();\n    if (FOLLY_UNLIKELY(startFrom > csize)) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    b_ = cdata + startFrom;\n    if (csize - startFrom < size) {\n      e_ = cdata + csize;\n    } else {\n      e_ = b_ + size;\n    }\n  }\n\n  /**\n   * @brief Allow explicit construction of ByteRange from std::string_view or\n   * std::string.\n\n   * Given that we allow implicit construction of ByteRange from\n   * StringPiece, it makes sense to allow this explicit construction, and avoids\n   * callers having to say ByteRange{StringPiece{str}} when they want a\n   * ByteRange pointing to data in a std::string.\n   *\n   *\n   * @methodset Range\n   */\n  template <\n      class Container,\n      class T = Iter,\n      typename detail::IsUnsignedCharPointer<T>::const_type = 0,\n      class = typename std::enable_if<\n            std::is_same<typename Container::const_pointer, const char*>::value\n          >::type,\n      class = decltype(\n          Iter(std::declval<Container const&>().data()),\n          Iter(\n              std::declval<Container const&>().data() +\n              std::declval<Container const&>().size()))>\n  explicit Range(const Container& str)\n      : b_(reinterpret_cast<Iter>(str.data())), e_(b_ + str.size()) {}\n  /**\n   * @brief Allow implicit conversion from Range<const char*> (aka StringPiece)\n   to\n   * Range<const unsigned char*> (aka ByteRange).\n\n   * Give both are frequently\n   * used to represent ranges of bytes.  Allow explicit conversion in the other\n   * direction.\n   *\n   * @methodset Range\n   */\n  template <\n      class OtherIter,\n      typename std::enable_if<\n          (std::is_same<Iter, const unsigned char*>::value &&\n           (std::is_same<OtherIter, const char*>::value ||\n            std::is_same<OtherIter, char*>::value)),\n          int>::type = 0>\n  /* implicit */ Range(const Range<OtherIter>& other)\n      : b_(reinterpret_cast<const unsigned char*>(other.begin())),\n        e_(reinterpret_cast<const unsigned char*>(other.end())) {}\n\n  template <\n      class OtherIter,\n      typename std::enable_if<\n          (std::is_same<Iter, unsigned char*>::value &&\n           std::is_same<OtherIter, char*>::value),\n          int>::type = 0>\n  /* implicit */ Range(const Range<OtherIter>& other)\n      : b_(reinterpret_cast<unsigned char*>(other.begin())),\n        e_(reinterpret_cast<unsigned char*>(other.end())) {}\n\n  template <\n      class OtherIter,\n      typename std::enable_if<\n          (std::is_same<Iter, const char*>::value &&\n           (std::is_same<OtherIter, const unsigned char*>::value ||\n            std::is_same<OtherIter, unsigned char*>::value)),\n          int>::type = 0>\n  explicit Range(const Range<OtherIter>& other)\n      : b_(reinterpret_cast<const char*>(other.begin())),\n        e_(reinterpret_cast<const char*>(other.end())) {}\n\n  template <\n      class OtherIter,\n      typename std::enable_if<\n          (std::is_same<Iter, char*>::value &&\n           std::is_same<OtherIter, unsigned char*>::value),\n          int>::type = 0>\n  explicit Range(const Range<OtherIter>& other)\n      : b_(reinterpret_cast<char*>(other.begin())),\n        e_(reinterpret_cast<char*>(other.end())) {}\n\n  /**\n   * Allow implicit conversion from Range<From> to Range<To> if From is\n   * implicitly convertible to To.\n   *\n   * @methodset Range\n   */\n  template <\n      class OtherIter,\n      typename std::enable_if<\n          (!std::is_same<Iter, OtherIter>::value &&\n           std::is_convertible<OtherIter, Iter>::value),\n          int>::type = 0>\n  constexpr /* implicit */ Range(const Range<OtherIter>& other)\n      : b_(other.begin()), e_(other.end()) {}\n  /**\n   * Allow explicit conversion from Range<From> to Range<To> if From is\n   * explicitly convertible to To.\n   *\n   * @methodset Range\n   */\n  template <\n      class OtherIter,\n      typename std::enable_if<\n          (!std::is_same<Iter, OtherIter>::value &&\n           !std::is_convertible<OtherIter, Iter>::value &&\n           std::is_constructible<Iter, const OtherIter&>::value),\n          int>::type = 0>\n  constexpr explicit Range(const Range<OtherIter>& other)\n      : b_(other.begin()), e_(other.end()) {}\n\n  /**\n   * Allow explicit construction of Range() from a std::array of a\n   * convertible type.\n   *\n   * For instance, this allows constructing StringPiece from a\n   * std::array<char, N> or a std::array<const char, N>\n   *\n   * @methodset Range\n   */\n  template <\n      class T,\n      size_t N,\n      typename = typename std::enable_if<\n          std::is_convertible<const T*, Iter>::value>::type>\n  constexpr explicit Range(const std::array<T, N>& array)\n      : b_{array.empty() ? nullptr : &array.at(0)},\n        e_{array.empty() ? nullptr : &array.at(0) + N} {}\n  template <\n      class T,\n      size_t N,\n      typename =\n          typename std::enable_if<std::is_convertible<T*, Iter>::value>::type>\n  constexpr explicit Range(std::array<T, N>& array)\n      : b_{array.empty() ? nullptr : &array.at(0)},\n        e_{array.empty() ? nullptr : &array.at(0) + N} {}\n\n  Range& operator=(const Range& rhs) & = default;\n  Range& operator=(Range&& rhs) & = default;\n\n  template <\n      class Alloc,\n      class T = Iter,\n      typename detail::IsCharPointer<T>::const_type = 0>\n  Range& operator=(string<Alloc>&& rhs) = delete;\n\n  /**\n   * Clear start and end iterators\n   */\n  void clear() {\n    b_ = Iter();\n    e_ = Iter();\n  }\n\n  /**\n   * Assign start and end iterators\n   */\n  void assign(Iter start, Iter end) {\n    b_ = start;\n    e_ = end;\n  }\n\n  /**\n   * Reset start and end iterator based on size\n   */\n  void reset(Iter start, size_type size) {\n    b_ = start;\n    e_ = start + size;\n  }\n\n  // Works only for Range<const char*>\n  template <typename Alloc>\n  void reset(const string<Alloc>& str) {\n    reset(str.data(), str.size());\n  }\n\n  constexpr size_type size() const {\n    assert(b_ <= e_);\n    return size_type(e_ - b_);\n  }\n  constexpr size_type walk_size() const {\n    return size_type(std::distance(b_, e_));\n  }\n  constexpr bool empty() const { return b_ == e_; }\n  constexpr Iter data() const { return b_; }\n  constexpr Iter start() const { return b_; }\n  constexpr Iter begin() const { return b_; }\n  constexpr Iter end() const { return e_; }\n  constexpr Iter cbegin() const { return b_; }\n  constexpr Iter cend() const { return e_; }\n  reference front() {\n    assert(b_ < e_);\n    return *b_;\n  }\n  reference back() {\n    assert(b_ < e_);\n    return *std::prev(e_);\n  }\n  const_reference front() const {\n    assert(b_ < e_);\n    return *b_;\n  }\n  const_reference back() const {\n    assert(b_ < e_);\n    return *std::prev(e_);\n  }\n\n private:\n  // It would be nice to be able to implicit convert to any target type\n  // T for which either an (Iter, Iter) or (Iter, size_type) noexcept\n  // constructor was available, and explicitly convert to any target\n  // type for which those signatures were available but not noexcept.\n  // The problem is that this creates ambiguity when there is also a\n  // T constructor that takes a type U that is implicitly convertible\n  // from Range.\n  //\n  // To avoid ambiguity, we need to avoid having explicit operator T\n  // and implicit operator U coexist when T is constructible from U.\n  // U cannot be deduced when searching for operator T (and C++ won't\n  // perform an existential search for it), so we must limit the implicit\n  // target types to a finite set that we can enumerate.\n  //\n  // At the moment the set of implicit target types consists of just\n  // std::string_view (when it is available).\n  struct NotStringView {};\n  struct StringViewTypeChar {\n    template <typename ValueType>\n    using apply = std::basic_string_view<ValueType>;\n  };\n  struct StringViewTypeNone {\n    template <typename>\n    using apply = NotStringView;\n  };\n  template <typename ValueType>\n  using StringViewTypeFunc = std::conditional_t<\n      detail::range_is_char_type_v_<Iter>,\n      StringViewTypeChar,\n      StringViewTypeNone>;\n  template <typename ValueType>\n  struct StringViewType {\n    using type =\n        typename StringViewTypeFunc<ValueType>::template apply<ValueType>;\n  };\n\n  template <typename Target>\n  struct IsConstructibleViaStringView\n      : Conjunction<\n            std::is_constructible<\n                _t<StringViewType<value_type>>,\n                Iter const&,\n                size_type>,\n            std::is_constructible<Target, _t<StringViewType<value_type>>>> {};\n\n public:\n  /// explicit operator conversion to any compatible type\n  ///\n  /// A compatible type is one which is constructible with an iterator and a\n  /// size (preferred), or a pair of iterators (fallback), passed by const-ref.\n  ///\n  /// Participates in overload resolution precisely when the target type is\n  /// compatible. This allows std::is_constructible compile-time checks to work.\n  template <\n      typename Tgt,\n      std::enable_if_t<\n          std::is_constructible<Tgt, Iter const&, size_type>::value &&\n              !IsConstructibleViaStringView<Tgt>::value,\n          int> = 0>\n  constexpr explicit operator Tgt() const noexcept(\n      std::is_nothrow_constructible<Tgt, Iter const&, size_type>::value) {\n    return Tgt(b_, walk_size());\n  }\n  template <\n      typename Tgt,\n      std::enable_if_t<\n          !std::is_constructible<Tgt, Iter const&, size_type>::value &&\n              std::is_constructible<Tgt, Iter const&, Iter const&>::value &&\n              !IsConstructibleViaStringView<Tgt>::value,\n          int> = 0>\n  constexpr explicit operator Tgt() const noexcept(\n      std::is_nothrow_constructible<Tgt, Iter const&, Iter const&>::value) {\n    return Tgt(b_, e_);\n  }\n\n  /// implicit operator conversion to std::string_view\n  template <\n      typename Tgt,\n      typename ValueType = value_type,\n      std::enable_if_t<\n          StrictConjunction<\n              std::is_same<Tgt, _t<StringViewType<ValueType>>>,\n              std::is_constructible<\n                  _t<StringViewType<ValueType>>,\n                  Iter const&,\n                  size_type>>::value,\n          int> = 0>\n  constexpr operator Tgt() const noexcept(\n      std::is_nothrow_constructible<Tgt, Iter const&, size_type>::value) {\n    return Tgt(b_, walk_size());\n  }\n\n#if FMT_VERSION < 100000\n  template <\n      typename IterType = Iter,\n      std::enable_if_t<detail::range_is_char_type_v_<IterType>, int> = 0>\n  constexpr operator fmt::basic_string_view<value_type>() const noexcept {\n    return _t<StringViewType<value_type>>(*this);\n  }\n#endif\n\n  /// explicit non-operator conversion to any compatible type\n  ///\n  /// A compatible type is one which is constructible with an iterator and a\n  /// size (preferred), or a pair of iterators (fallback), passed by const-ref.\n  ///\n  /// Participates in overload resolution precisely when the target type is\n  /// compatible. This allows is_invocable compile-time checks to work.\n  ///\n  /// Provided in addition to the explicit operator conversion to permit passing\n  /// additional arguments to the target type constructor. A canonical example\n  /// of an additional argument might be an allocator, where the target type is\n  /// some specialization of std::vector or std::basic_string in a context which\n  /// requires a non-default-constructed allocator.\n  template <typename Tgt, typename... Args>\n  constexpr std::enable_if_t<\n      std::is_constructible<Tgt, Iter const&, size_type>::value,\n      Tgt>\n  to(Args&&... args) const noexcept(\n      std::is_nothrow_constructible<Tgt, Iter const&, size_type, Args&&...>::\n          value) {\n    return Tgt(b_, walk_size(), static_cast<Args&&>(args)...);\n  }\n  template <typename Tgt, typename... Args>\n  constexpr std::enable_if_t<\n      !std::is_constructible<Tgt, Iter const&, size_type>::value &&\n          std::is_constructible<Tgt, Iter const&, Iter const&>::value,\n      Tgt>\n  to(Args&&... args) const noexcept(\n      std::is_nothrow_constructible<Tgt, Iter const&, Iter const&, Args&&...>::\n          value) {\n    return Tgt(b_, e_, static_cast<Args&&>(args)...);\n  }\n\n  // Works only for Range<const char*> and Range<char*>\n  std::string str() const { return to<std::string>(); }\n  std::string toString() const { return to<std::string>(); }\n\n  const_range_type castToConst() const { return const_range_type(*this); }\n\n  int compare(const const_range_type& o) const {\n    const size_type tsize = this->size();\n    const size_type osize = o.size();\n    const size_type msize = std::min(tsize, osize);\n    int r = traits_type::compare(data(), o.data(), msize);\n    if (r == 0 && tsize != osize) {\n      // We check the signed bit of the subtraction and bit shift it\n      // to produce either 0 or 2. The subtraction yields the\n      // comparison values of either -1 or 1.\n      r = (static_cast<int>((osize - tsize) >> (CHAR_BIT * sizeof(size_t) - 1))\n           << 1) -\n          1;\n    }\n    return r;\n  }\n\n  reference operator[](size_t i) {\n    assert(i < size());\n    return b_[i];\n  }\n\n  const_reference operator[](size_t i) const {\n    assert(i < size());\n    return b_[i];\n  }\n\n  reference at(size_t i) {\n    if (i >= size()) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    return b_[i];\n  }\n\n  const_reference at(size_t i) const {\n    if (i >= size()) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    return b_[i];\n  }\n\n  void advance(size_type n) {\n    if (FOLLY_UNLIKELY(n > size())) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    b_ += n;\n  }\n\n  void subtract(size_type n) {\n    if (FOLLY_UNLIKELY(n > size())) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    e_ -= n;\n  }\n\n  // Returns a window into the current range, starting at first, and spans\n  // length characters (or until the end of the current range, whichever comes\n  // first). Throws if first is past the end of the current range.\n  Range subpiece(size_type first, size_type length = npos) const {\n    if (FOLLY_UNLIKELY(first > size())) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n\n    return Range(b_ + first, std::min(length, size() - first));\n  }\n\n  template <\n      typename...,\n      typename T = Iter,\n      std::enable_if_t<detail::range_is_char_type_v_<T>, int> = 0>\n  Range substr(size_type first, size_type length = npos) const {\n    return subpiece(first, length);\n  }\n\n  // unchecked versions\n  void uncheckedAdvance(size_type n) {\n    assert(n <= size());\n    b_ += n;\n  }\n\n  void uncheckedSubtract(size_type n) {\n    assert(n <= size());\n    e_ -= n;\n  }\n\n  Range uncheckedSubpiece(size_type first, size_type length = npos) const {\n    assert(first <= size());\n    return Range(b_ + first, std::min(length, size() - first));\n  }\n\n  void pop_front() {\n    assert(b_ < e_);\n    ++b_;\n  }\n\n  void pop_back() {\n    assert(b_ < e_);\n    --e_;\n  }\n\n  // string work-alike functions\n  size_type find(const_range_type str) const {\n    return qfind(castToConst(), str);\n  }\n\n  size_type find(const_range_type str, size_t pos) const {\n    if (pos > size()) {\n      return std::string::npos;\n    }\n    size_t ret = qfind(castToConst().subpiece(pos), str);\n    return ret == npos ? ret : ret + pos;\n  }\n\n  size_type find(Iter s, size_t pos, size_t n) const {\n    if (pos > size()) {\n      return std::string::npos;\n    }\n    auto forFinding = castToConst();\n    size_t ret = qfind(\n        pos ? forFinding.subpiece(pos) : forFinding, const_range_type(s, n));\n    return ret == npos ? ret : ret + pos;\n  }\n\n  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor\n  size_type find(const Iter s) const {\n    return qfind(castToConst(), const_range_type(s));\n  }\n\n  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor\n  size_type find(const Iter s, size_t pos) const {\n    if (pos > size()) {\n      return std::string::npos;\n    }\n    size_type ret = qfind(castToConst().subpiece(pos), const_range_type(s));\n    return ret == npos ? ret : ret + pos;\n  }\n\n  size_type find(const value_type& c) const { return qfind(castToConst(), c); }\n\n  size_type rfind(const value_type& c) const {\n    return folly::rfind(castToConst(), c);\n  }\n\n  size_type find(const value_type& c, size_t pos) const {\n    if (pos > size()) {\n      return std::string::npos;\n    }\n    size_type ret = qfind(castToConst().subpiece(pos), c);\n    return ret == npos ? ret : ret + pos;\n  }\n\n  size_type find_first_of(const_range_type needles) const {\n    return qfind_first_of(castToConst(), needles);\n  }\n\n  size_type find_first_of(const_range_type needles, size_t pos) const {\n    if (pos > size()) {\n      return std::string::npos;\n    }\n    size_type ret = qfind_first_of(castToConst().subpiece(pos), needles);\n    return ret == npos ? ret : ret + pos;\n  }\n\n  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor\n  size_type find_first_of(Iter needles) const {\n    return find_first_of(const_range_type(needles));\n  }\n\n  // Works only for Range<(const) (unsigned) char*> which have Range(Iter) ctor\n  size_type find_first_of(Iter needles, size_t pos) const {\n    return find_first_of(const_range_type(needles), pos);\n  }\n\n  size_type find_first_of(Iter needles, size_t pos, size_t n) const {\n    return find_first_of(const_range_type(needles, n), pos);\n  }\n\n  size_type find_first_of(const value_type& c) const { return find(c); }\n\n  size_type find_first_of(const value_type& c, size_t pos) const {\n    return find(c, pos);\n  }\n\n  /**\n   * Determine whether the range contains the given subrange or item.\n   *\n   * Note: Call find() directly if the index is needed.\n   */\n  bool contains(const const_range_type& other) const {\n    return find(other) != std::string::npos;\n  }\n\n  bool contains(const value_type& other) const {\n    return find(other) != std::string::npos;\n  }\n\n  void swap(Range& rhs) noexcept(std::is_nothrow_swappable_v<Iter>) {\n    std::swap(b_, rhs.b_);\n    std::swap(e_, rhs.e_);\n  }\n\n  /**\n   * Does this Range start with another range?\n   */\n  bool startsWith(const const_range_type& other) const {\n    return size() >= other.size() &&\n        castToConst().subpiece(0, other.size()) == other;\n  }\n  bool startsWith(const value_type& c) const {\n    return !empty() && front() == c;\n  }\n\n  template <class Comp>\n  bool startsWith(const const_range_type& other, Comp&& eq) const {\n    if (size() < other.size()) {\n      return false;\n    }\n    auto const trunc = subpiece(0, other.size());\n    return std::equal(\n        trunc.begin(), trunc.end(), other.begin(), std::forward<Comp>(eq));\n  }\n\n  bool starts_with(const_range_type other) const noexcept {\n    return startsWith(other);\n  }\n  bool starts_with(const value_type& c) const noexcept { return startsWith(c); }\n  template <\n      typename...,\n      typename T = Iter,\n      std::enable_if_t<detail::range_is_char_type_v_<T>, int> = 0>\n  bool starts_with(const value_type* other) const {\n    return startsWith(other);\n  }\n\n  /**\n   * Does this Range end with another range?\n   */\n  bool endsWith(const const_range_type& other) const {\n    return size() >= other.size() &&\n        castToConst().subpiece(size() - other.size()) == other;\n  }\n  bool endsWith(const value_type& c) const { return !empty() && back() == c; }\n\n  template <class Comp>\n  bool endsWith(const const_range_type& other, Comp&& eq) const {\n    if (size() < other.size()) {\n      return false;\n    }\n    auto const trunc = subpiece(size() - other.size());\n    return std::equal(\n        trunc.begin(), trunc.end(), other.begin(), std::forward<Comp>(eq));\n  }\n\n  template <class Comp>\n  bool equals(const const_range_type& other, Comp&& eq) const {\n    return size() == other.size() &&\n        std::equal(begin(), end(), other.begin(), std::forward<Comp>(eq));\n  }\n\n  bool ends_with(const_range_type other) const noexcept {\n    return endsWith(other);\n  }\n  bool ends_with(const value_type& c) const noexcept { return endsWith(c); }\n  template <\n      typename...,\n      typename T = Iter,\n      std::enable_if_t<detail::range_is_char_type_v_<T>, int> = 0>\n  bool ends_with(const value_type* other) const {\n    return endsWith(other);\n  }\n\n  /**\n   * Remove the items in [b, e), as long as this subrange is at the beginning\n   * or end of the Range.\n   *\n   * Required for boost::algorithm::trim()\n   */\n  void erase(Iter b, Iter e) {\n    if (b == b_) {\n      b_ = e;\n    } else if (e == e_) {\n      e_ = b;\n    } else {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n  }\n\n  /**\n   * Remove the given prefix and return true if the range starts with the given\n   * prefix; return false otherwise.\n   */\n  bool removePrefix(const const_range_type& prefix) {\n    return startsWith(prefix) && (b_ += prefix.size(), true);\n  }\n  bool removePrefix(const value_type& prefix) {\n    return startsWith(prefix) && (++b_, true);\n  }\n\n  /**\n   * Remove the given suffix and return true if the range ends with the given\n   * suffix; return false otherwise.\n   */\n  bool removeSuffix(const const_range_type& suffix) {\n    return endsWith(suffix) && (e_ -= suffix.size(), true);\n  }\n  bool removeSuffix(const value_type& suffix) {\n    return endsWith(suffix) && (--e_, true);\n  }\n\n  /**\n   * Replaces the content of the range, starting at position 'pos', with\n   * contents of 'replacement'. Entire 'replacement' must fit into the\n   * range. Returns false if 'replacements' does not fit. Example use:\n   *\n   * char in[] = \"buffer\";\n   * auto msp = MutableStringPiece(input);\n   * EXPECT_TRUE(msp.replaceAt(2, \"tt\"));\n   * EXPECT_EQ(msp, \"butter\");\n   *\n   * // not enough space\n   * EXPECT_FALSE(msp.replace(msp.size() - 1, \"rr\"));\n   * EXPECT_EQ(msp, \"butter\"); // unchanged\n   */\n  bool replaceAt(size_t pos, const_range_type replacement) {\n    if (size() < pos + replacement.size()) {\n      return false;\n    }\n\n    std::copy(replacement.begin(), replacement.end(), begin() + pos);\n\n    return true;\n  }\n\n  /**\n   * Replaces all occurrences of 'source' with 'dest'. Returns number\n   * of replacements made. Source and dest have to have the same\n   * length. Throws if the lengths are different. If 'source' is a\n   * pattern that is overlapping with itself, we perform sequential\n   * replacement: \"aaaaaaa\".replaceAll(\"aa\", \"ba\") --> \"bababaa\"\n   *\n   * Example use:\n   *\n   * char in[] = \"buffer\";\n   * auto msp = MutableStringPiece(input);\n   * EXPECT_EQ(msp.replaceAll(\"ff\",\"tt\"), 1);\n   * EXPECT_EQ(msp, \"butter\");\n   */\n  size_t replaceAll(const_range_type source, const_range_type dest) {\n    if (source.size() != dest.size()) {\n      throw_exception<std::invalid_argument>(\n          \"replacement must have the same size as source\");\n    }\n\n    if (dest.empty()) {\n      return 0;\n    }\n\n    size_t pos = 0;\n    size_t num_replaced = 0;\n    size_type found = std::string::npos;\n    while ((found = find(source, pos)) != std::string::npos) {\n      replaceAt(found, dest);\n      pos += source.size();\n      ++num_replaced;\n    }\n\n    return num_replaced;\n  }\n\n  /**\n   * Splits this `Range` `[b, e)` in the position `i` dictated by the next\n   * occurrence of `delimiter`.\n   *\n   * Returns a new `Range` `[b, i)` and adjusts this range to start right after\n   * the delimiter's position. This range will be empty if the delimiter is not\n   * found. If called on an empty `Range`, both this and the returned `Range`\n   * will be empty.\n   *\n   * Example:\n   *\n   *  folly::StringPiece s(\"sample string for split_next\");\n   *  auto p = s.split_step(' ');\n   *\n   *  // prints \"string for split_next\"\n   *  cout << s << endl;\n   *\n   *  // prints \"sample\"\n   *  cout << p << endl;\n   *\n   * Example 2:\n   *\n   *  void tokenize(StringPiece s, char delimiter) {\n   *    while (!s.empty()) {\n   *      cout << s.split_step(delimiter);\n   *    }\n   *  }\n   *\n   */\n  Range split_step(const value_type& delimiter) {\n    auto i = find(delimiter);\n    Range result(b_, i == std::string::npos ? size() : i);\n\n    b_ = result.end() == e_ ? e_ : std::next(result.end());\n\n    return result;\n  }\n\n  Range split_step(Range delimiter) {\n    auto i = find(delimiter);\n    Range result(b_, i == std::string::npos ? size() : i);\n\n    b_ = result.end() == e_\n        ? e_\n        : std::next(result.end(), difference_type(delimiter.size()));\n\n    return result;\n  }\n\n  /**\n   * Convenience method that calls `split_step()` and passes the result to a\n   * functor, returning whatever the functor does. Any additional arguments\n   * `args` passed to this function are perfectly forwarded to the functor.\n   *\n   * Say you have a functor with this signature:\n   *\n   *  Foo fn(Range r) { }\n   *\n   * `split_step()`'s return type will be `Foo`. It works just like:\n   *\n   *  auto result = fn(myRange.split_step(' '));\n   *\n   * A functor returning `void` is also supported.\n   *\n   * Example:\n   *\n   *  void do_some_parsing(folly::StringPiece s) {\n   *    auto version = s.split_step(' ', [&](folly::StringPiece x) {\n   *      if (x.empty()) {\n   *        throw std::invalid_argument(\"empty string\");\n   *      }\n   *      return std::strtoull(x.begin(), x.end(), 16);\n   *    });\n   *\n   *    // ...\n   *  }\n   *\n   *  struct Foo {\n   *    void parse(folly::StringPiece s) {\n   *      s.split_step(' ', parse_field, bar, 10);\n   *      s.split_step('\\t', parse_field, baz, 20);\n   *\n   *      auto const kludge = [](folly::StringPiece x, int &out, int def) {\n   *        if (x == \"null\") {\n   *          out = 0;\n   *        } else {\n   *          parse_field(x, out, def);\n   *        }\n   *      };\n   *\n   *      s.split_step('\\t', kludge, gaz);\n   *      s.split_step(' ', kludge, foo);\n   *    }\n   *\n   *  private:\n   *    int bar;\n   *    int baz;\n   *    int gaz;\n   *    int foo;\n   *\n   *    static parse_field(folly::StringPiece s, int &out, int def) {\n   *      try {\n   *        out = folly::to<int>(s);\n   *      } catch (std::exception const &) {\n   *        value = def;\n   *      }\n   *    }\n   *  };\n   *\n   */\n  template <typename TProcess, typename... Args>\n  auto split_step(\n      const value_type& delimiter, TProcess&& process, Args&&... args)\n      -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...)) {\n    return process(split_step(delimiter), std::forward<Args>(args)...);\n  }\n\n  template <typename TProcess, typename... Args>\n  auto split_step(Range delimiter, TProcess&& process, Args&&... args)\n      -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...)) {\n    return process(split_step(delimiter), std::forward<Args>(args)...);\n  }\n\n private:\n  Iter b_;\n  Iter e_;\n};\n\ntemplate <class Iter>\nconst typename Range<Iter>::size_type Range<Iter>::npos = std::string::npos;\n\ntemplate <class Iter>\nvoid swap(Range<Iter>& lhs, Range<Iter>& rhs) noexcept(\n    noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\n/**\n * Create a range from two iterators, with type deduction.\n */\ntemplate <class Iter>\nconstexpr Range<Iter> range(Iter first, Iter last) {\n  return Range<Iter>(first, last);\n}\n\n/*\n * Creates a range to reference the contents of a contiguous-storage container.\n */\n// Use pointers for types with '.data()' member\ntemplate <class Collection>\nconstexpr auto range(Collection& v) -> Range<decltype(v.data())> {\n  return Range<decltype(v.data())>(v.data(), v.data() + v.size());\n}\ntemplate <class Collection>\nconstexpr auto range(Collection const& v) -> Range<decltype(v.data())> {\n  return Range<decltype(v.data())>(v.data(), v.data() + v.size());\n}\ntemplate <class Collection>\nconstexpr auto crange(Collection const& v) -> Range<decltype(v.data())> {\n  return Range<decltype(v.data())>(v.data(), v.data() + v.size());\n}\n\ntemplate <class T, size_t n>\nconstexpr Range<T*> range(T (&array)[n]) {\n  return Range<T*>(array, array + n);\n}\ntemplate <class T, size_t n>\nconstexpr Range<T const*> range(T const (&array)[n]) {\n  return Range<T const*>(array, array + n);\n}\ntemplate <class T, size_t n>\nconstexpr Range<T const*> crange(T const (&array)[n]) {\n  return Range<T const*>(array, array + n);\n}\n\ntemplate <class T, size_t n>\nconstexpr Range<T*> range(std::array<T, n>& array) {\n  return Range<T*>{array};\n}\ntemplate <class T, size_t n>\nconstexpr Range<T const*> range(std::array<T, n> const& array) {\n  return Range<T const*>{array};\n}\ntemplate <class T, size_t n>\nconstexpr Range<T const*> crange(std::array<T, n> const& array) {\n  return Range<T const*>{array};\n}\n\ntemplate <class T>\nconstexpr Range<T const*> range(std::initializer_list<T> ilist) {\n  return Range<T const*>(ilist.begin(), ilist.end());\n}\n\ntemplate <class T>\nconstexpr Range<T const*> crange(std::initializer_list<T> ilist) {\n  return Range<T const*>(ilist.begin(), ilist.end());\n}\n\nusing StringPiece = Range<const char*>;\nusing MutableStringPiece = Range<char*>;\nusing ByteRange = Range<const unsigned char*>;\nusing MutableByteRange = Range<unsigned char*>;\n\ntemplate <class C>\nstd::basic_ostream<C>& operator<<(\n    std::basic_ostream<C>& os, Range<C const*> piece) {\n  using StreamSize = decltype(os.width());\n  os.write(piece.start(), static_cast<StreamSize>(piece.size()));\n  return os;\n}\n\ntemplate <class C>\nstd::basic_ostream<C>& operator<<(std::basic_ostream<C>& os, Range<C*> piece) {\n  using StreamSize = decltype(os.width());\n  os.write(piece.start(), static_cast<StreamSize>(piece.size()));\n  return os;\n}\n\n/**\n * Templated comparison operators\n */\n\ntemplate <class Iter>\ninline bool operator==(const Range<Iter>& lhs, const Range<Iter>& rhs) {\n  using value_type = typename Range<Iter>::value_type;\n  if (lhs.size() != rhs.size()) {\n    return false;\n  }\n  if constexpr (\n      std::is_pointer_v<Iter> &&\n      (std::is_integral_v<value_type> || std::is_enum_v<value_type>)) {\n    auto const size = lhs.size() * sizeof(value_type);\n    return 0 == size || 0 == std::memcmp(lhs.data(), rhs.data(), size);\n  } else {\n    for (size_t i = 0; i < lhs.size(); ++i) {\n      if (!Range<Iter>::traits_type::eq(lhs[i], rhs[i])) {\n        return false;\n      }\n    }\n    return true;\n  }\n}\n\ntemplate <class Iter>\ninline bool operator!=(const Range<Iter>& lhs, const Range<Iter>& rhs) {\n  return !(operator==(lhs, rhs));\n}\n\ntemplate <class Iter>\ninline bool operator<(const Range<Iter>& lhs, const Range<Iter>& rhs) {\n  return lhs.compare(rhs) < 0;\n}\n\ntemplate <class Iter>\ninline bool operator<=(const Range<Iter>& lhs, const Range<Iter>& rhs) {\n  return lhs.compare(rhs) <= 0;\n}\n\ntemplate <class Iter>\ninline bool operator>(const Range<Iter>& lhs, const Range<Iter>& rhs) {\n  return lhs.compare(rhs) > 0;\n}\n\ntemplate <class Iter>\ninline bool operator>=(const Range<Iter>& lhs, const Range<Iter>& rhs) {\n  return lhs.compare(rhs) >= 0;\n}\n\n/**\n * Specializations of comparison operators for StringPiece\n */\n\nnamespace detail {\n\ntemplate <class A, class B>\nstruct ComparableAsStringPiece {\n  enum {\n    value = (std::is_convertible<A, StringPiece>::value &&\n             std::is_same<B, StringPiece>::value) ||\n        (std::is_convertible<B, StringPiece>::value &&\n         std::is_same<A, StringPiece>::value)\n  };\n};\n\n} // namespace detail\n\n/**\n * operator== through conversion for Range<const char*>\n */\ntemplate <class T, class U>\nstd::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator==(\n    const T& lhs, const U& rhs) {\n  return StringPiece(lhs) == StringPiece(rhs);\n}\n\n/**\n * operator!= through conversion for Range<const char*>\n */\ntemplate <class T, class U>\nstd::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator!=(\n    const T& lhs, const U& rhs) {\n  return StringPiece(lhs) != StringPiece(rhs);\n}\n\n/**\n * operator< through conversion for Range<const char*>\n */\ntemplate <class T, class U>\nstd::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator<(\n    const T& lhs, const U& rhs) {\n  return StringPiece(lhs) < StringPiece(rhs);\n}\n\n/**\n * operator> through conversion for Range<const char*>\n */\ntemplate <class T, class U>\nstd::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator>(\n    const T& lhs, const U& rhs) {\n  return StringPiece(lhs) > StringPiece(rhs);\n}\n\n/**\n * operator< through conversion for Range<const char*>\n */\ntemplate <class T, class U>\nstd::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator<=(\n    const T& lhs, const U& rhs) {\n  return StringPiece(lhs) <= StringPiece(rhs);\n}\n\n/**\n * operator> through conversion for Range<const char*>\n */\ntemplate <class T, class U>\nstd::enable_if_t<detail::ComparableAsStringPiece<T, U>::value, bool> operator>=(\n    const T& lhs, const U& rhs) {\n  return StringPiece(lhs) >= StringPiece(rhs);\n}\n\n/**\n * Finds substrings faster than brute force by borrowing from Boyer-Moore\n */\ntemplate <class Iter, class Comp>\nsize_t qfind(const Range<Iter>& haystack, const Range<Iter>& needle, Comp eq) {\n  // Don't use std::search, use a Boyer-Moore-like trick by comparing\n  // the last characters first\n  auto const nsize = needle.size();\n  if (haystack.size() < nsize) {\n    return std::string::npos;\n  }\n  if (!nsize) {\n    return 0;\n  }\n  auto const nsize_1 = nsize - 1;\n  auto const lastNeedle = needle[nsize_1];\n\n  // Boyer-Moore skip value for the last char in the needle. Zero is\n  // not a valid value; skip will be computed the first time it's\n  // needed.\n  std::string::size_type skip = 0;\n\n  auto i = haystack.begin();\n  auto iEnd = haystack.end() - nsize_1;\n\n  while (i < iEnd) {\n    // Boyer-Moore: match the last element in the needle\n    while (!eq(i[nsize_1], lastNeedle)) {\n      if (++i == iEnd) {\n        // not found\n        return std::string::npos;\n      }\n    }\n    // Here we know that the last char matches\n    // Continue in pedestrian mode\n    for (size_t j = 0;;) {\n      assert(j < nsize);\n      if (!eq(i[j], needle[j])) {\n        // Not found, we can skip\n        // Compute the skip value lazily\n        if (skip == 0) {\n          skip = 1;\n          while (skip <= nsize_1 && !eq(needle[nsize_1 - skip], lastNeedle)) {\n            ++skip;\n          }\n        }\n        i += skip;\n        break;\n      }\n      // Check if done searching\n      if (++j == nsize) {\n        // Yay\n        return size_t(i - haystack.begin());\n      }\n    }\n  }\n  return std::string::npos;\n}\n\nnamespace detail {\n\ninline size_t qfind_first_byte_of(\n    const StringPiece haystack, const StringPiece needles) {\n  // Let's default to the SIMD implementation. Internally, if that's not\n  // available, the _nosimd version gets picked instead.\n  return qfind_first_byte_of_simd(haystack, needles);\n}\n\n} // namespace detail\n\ntemplate <class Iter, class Comp>\nsize_t qfind_first_of(\n    const Range<Iter>& haystack, const Range<Iter>& needles, Comp eq) {\n  auto ret = std::find_first_of(\n      haystack.begin(), haystack.end(), needles.begin(), needles.end(), eq);\n  return ret == haystack.end() ? std::string::npos : ret - haystack.begin();\n}\n\nstruct AsciiCaseSensitive {\n  bool operator()(char lhs, char rhs) const { return lhs == rhs; }\n};\n\n/**\n * Check if two ascii characters are case insensitive equal.\n * The difference between the lower/upper case characters are the 6-th bit.\n * We also check they are alpha chars, in case of xor = 32.\n */\nstruct AsciiCaseInsensitive {\n  bool operator()(char lhs, char rhs) const {\n    char k = lhs ^ rhs;\n    if (k == 0) {\n      return true;\n    }\n    if (k != 32) {\n      return false;\n    }\n    k = lhs | rhs;\n    return (k >= 'a' && k <= 'z');\n  }\n};\n\ntemplate <class Iter>\nsize_t qfind(\n    const Range<Iter>& haystack,\n    const typename Range<Iter>::value_type& needle) {\n  auto pos = std::find(haystack.begin(), haystack.end(), needle);\n  return pos == haystack.end() ? std::string::npos : pos - haystack.data();\n}\n\ntemplate <class Iter>\nsize_t rfind(\n    const Range<Iter>& haystack,\n    const typename Range<Iter>::value_type& needle) {\n  for (auto i = haystack.size(); i-- > 0;) {\n    if (haystack[i] == needle) {\n      return i;\n    }\n  }\n  return std::string::npos;\n}\n\n// specialization for StringPiece\ntemplate <>\ninline size_t qfind(const Range<const char*>& haystack, const char& needle) {\n  // memchr expects a not-null pointer, early return if the range is empty.\n  if (haystack.empty()) {\n    return std::string::npos;\n  }\n  auto pos = static_cast<const char*>(\n      ::memchr(haystack.data(), needle, haystack.size()));\n  return pos == nullptr ? std::string::npos : pos - haystack.data();\n}\n\ntemplate <>\ninline size_t rfind(const Range<const char*>& haystack, const char& needle) {\n  // memchr expects a not-null pointer, early return if the range is empty.\n  if (haystack.empty()) {\n    return std::string::npos;\n  }\n  auto pos = static_cast<const char*>(\n      memrchr(haystack.data(), needle, haystack.size()));\n  return pos == nullptr ? std::string::npos : pos - haystack.data();\n}\n\n// specialization for ByteRange\ntemplate <>\ninline size_t qfind(\n    const Range<const unsigned char*>& haystack, const unsigned char& needle) {\n  // memchr expects a not-null pointer, early return if the range is empty.\n  if (haystack.empty()) {\n    return std::string::npos;\n  }\n  auto pos = static_cast<const unsigned char*>(\n      ::memchr(haystack.data(), needle, haystack.size()));\n  return pos == nullptr ? std::string::npos : pos - haystack.data();\n}\n\ntemplate <>\ninline size_t rfind(\n    const Range<const unsigned char*>& haystack, const unsigned char& needle) {\n  // memchr expects a not-null pointer, early return if the range is empty.\n  if (haystack.empty()) {\n    return std::string::npos;\n  }\n  auto pos = static_cast<const unsigned char*>(\n      memrchr(haystack.data(), needle, haystack.size()));\n  return pos == nullptr ? std::string::npos : pos - haystack.data();\n}\n\ntemplate <class Iter>\nsize_t qfind_first_of(const Range<Iter>& haystack, const Range<Iter>& needles) {\n  return qfind_first_of(haystack, needles, AsciiCaseSensitive());\n}\n\n// specialization for StringPiece\ntemplate <>\ninline size_t qfind_first_of(\n    const Range<const char*>& haystack, const Range<const char*>& needles) {\n  return detail::qfind_first_byte_of(haystack, needles);\n}\n\n// specialization for ByteRange\ntemplate <>\ninline size_t qfind_first_of(\n    const Range<const unsigned char*>& haystack,\n    const Range<const unsigned char*>& needles) {\n  return detail::qfind_first_byte_of(\n      StringPiece(haystack), StringPiece(needles));\n}\n\ntemplate <class Key, class Enable>\nstruct hasher;\n\ntemplate <class T>\nstruct hasher<\n    folly::Range<T*>,\n    std::enable_if_t<std::is_integral<T>::value, void>> {\n  using folly_is_avalanching = std::true_type;\n\n  size_t operator()(folly::Range<T*> r) const {\n    // std::is_integral<T> is too restrictive, but is sufficient to\n    // guarantee we can just hash all of the underlying bytes to get a\n    // suitable hash of T.  Something like absl::is_uniquely_represented<T>\n    // would be better.  std::is_pod is not enough, because POD types\n    // can contain pointers and padding.  Also, floating point numbers\n    // may be == without being bit-identical.  size_t is less than 64\n    // bits on some platforms.\n    return static_cast<size_t>(\n        hash::SpookyHashV2::Hash64(r.begin(), r.size() * sizeof(T), 0));\n  }\n};\n\n/**\n * _sp is a user-defined literal suffix to make an appropriate Range\n * specialization from a literal string.\n *\n * Modeled after C++17's `sv` suffix.\n */\ninline namespace literals {\ninline namespace string_piece_literals {\nconstexpr Range<char const*> operator\"\"_sp(\n    char const* str, size_t len) noexcept {\n  return Range<char const*>(str, len);\n}\n\n#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L\nconstexpr Range<char8_t const*> operator\"\"_sp(\n    char8_t const* str, size_t len) noexcept {\n  return Range<char8_t const*>(str, len);\n}\n#endif\n\nconstexpr Range<char16_t const*> operator\"\"_sp(\n    char16_t const* str, size_t len) noexcept {\n  return Range<char16_t const*>(str, len);\n}\n\nconstexpr Range<char32_t const*> operator\"\"_sp(\n    char32_t const* str, size_t len) noexcept {\n  return Range<char32_t const*>(str, len);\n}\n\nconstexpr Range<wchar_t const*> operator\"\"_sp(\n    wchar_t const* str, size_t len) noexcept {\n  return Range<wchar_t const*>(str, len);\n}\n} // namespace string_piece_literals\n} // namespace literals\n\n} // namespace folly\n\n// Avoid ambiguity in older fmt versions due to StringPiece's conversions.\n#if FMT_VERSION >= 70000\nnamespace fmt {\ntemplate <>\nstruct formatter<folly::StringPiece> : private formatter<string_view> {\n  using formatter<string_view>::parse;\n\n#if FMT_VERSION >= 80000\n  template <typename Context>\n  typename Context::iterator format(folly::StringPiece s, Context& ctx) const {\n    return formatter<string_view>::format({s.data(), s.size()}, ctx);\n  }\n#else\n  template <typename Context>\n  typename Context::iterator format(folly::StringPiece s, Context& ctx) {\n    return formatter<string_view>::format({s.data(), s.size()}, ctx);\n  }\n#endif\n};\n} // namespace fmt\n#endif\n\nFOLLY_POP_WARNING\n\nFOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(folly::Range)\n\n// Unfortunately it is not possible to forward declare enable_view under\n// MSVC 2019.8 due to compiler bugs, so we need to include the actual\n// definition if available.\n#if __has_include(<range/v3/range/concepts.hpp>) && defined(_MSC_VER)\n#include <range/v3/range/concepts.hpp> // @manual\n#else\nnamespace ranges {\ntemplate <class T>\nextern const bool enable_view;\n} // namespace ranges\n#endif\n\n// Tell the range-v3 library that this type should satisfy\n// the view concept (a lightweight, non-owning range).\nnamespace ranges {\ntemplate <class Iter>\ninline constexpr bool enable_view<::folly::Range<Iter>> = true;\n} // namespace ranges\n\n#if defined(__cpp_lib_ranges)\ntemplate <typename T>\nconstexpr bool std::ranges::enable_borrowed_range<folly::Range<T>> = true;\n#endif\n"
  },
  {
    "path": "folly/Replaceable.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <initializer_list>\n#include <new>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n\n/**\n * An instance of `Replaceable<T>` wraps an instance of `T`.\n *\n * You access the inner `T` instance with `operator*` and `operator->` (as if\n * it were a smart pointer).\n *\n * `Replaceable<T>` adds no indirection cost and performs no allocations.\n *\n * `Replaceable<T>` has the same size and alignment as `T`.\n *\n * You can replace the `T` within a `Replaceable<T>` using the `emplace` method\n * (presuming that it is constructible and destructible without throwing\n * exceptions). If the destructor or constructor you're using could throw an\n * exception you should use `Optional<T>` instead, as it's not a logic error\n * for that to be empty.\n *\n * Frequently Asked Questions\n * ==========================\n *\n * Why does this need to be so complicated?\n * ----------------------------------------\n *\n * If a `T` instance contains `const`-qualified member variables or reference\n * member variables we can't safely replace a `T` instance by destructing it\n * manually and using placement new. This is because compilers are permitted to\n * assume that the `const` or reference members of a named, referenced, or\n * pointed-to object do not change.\n *\n * For pointed-to objects in allocated storage you can use the pointer returned\n * by placement new or use the `launder` function to get a pointer to the new\n * object.  Note that `launder` doesn't affect its argument, it's still\n * undefined behaviour to use the original pointer. And none of this helps if\n * the object is a local or a member variable because the destructor call will\n * not have been laundered. In summary, this is the only way to use placement\n * new that is both simple and safe:\n *\n *      T* pT = new T(...);\n *      pT->~T();\n *      pT = ::new (pT) T(...);\n *      delete pT;\n *\n * What are the other safe solutions to this problem?\n * --------------------------------------------------\n *\n * * Ask the designer of `T` to de-`const` and -`reference` the members of `T`.\n *  - Makes `T` harder to reason about\n *  - Can reduce the performance of `T` methods\n *  - They can refuse to make the change\n * * Put the `T` on the heap and use a raw/unique/shared pointer.\n *  - Adds a level of indirection, costing performance.\n *  - Harder to reason about your code as you need to check for nullptr.\n * * Put the `T` in an `Optional`.\n *  - Harder to reason about your code as you need to check for None.\n * * Pass the problem on, making the new code also not-replaceable\n *  - Contagion is not really a solution\n *\n * Are there downsides to this?\n * ----------------------------\n *\n * There is a potential performance penalty after converting `T` to\n * `Replaceable<T>` if you have non-`T`-member-function code which repeatedly\n * examines the value of a `const` or `reference` data member of `T`, because\n * the compiler now has to look at the value each time whereas previously it\n * was permitted to load it once up-front and presume that it could never\n * change.\n *\n * Usage notes\n * ===========\n *\n * Don't store a reference to the `T` within a `Replaceable<T>` unless you can\n * show that its lifetime does not cross an `emplace` call. For safety a\n * reasonable rule is to always use `operator*()` to get a fresh temporary each\n * time you need a `T&.\n *\n * If you store a pointer to the `T` within a `Replaceable<T>` you **must**\n * launder it after each call to `emplace` before using it. Again you can\n * reasonably choose to always use `operator->()` to get a fresh temporary each\n * time you need a `T*.\n *\n * Thus far I haven't thought of a good reason to use `Replaceable<T>` or\n * `Replaceable<T> const&` as a function parameter type.\n *\n * `Replaceable<T>&` can make sense to pass to a function that conditionally\n * replaces the `T`, where `T` has `const` or reference member variables.\n *\n * The main use of `Replaceable<T>` is as a class member type or a local type\n * in long-running functions.\n *\n * It's probably time to rethink your design choices if you end up with\n * `Replaceable<Replaceable<T>>`, `Optional<Replaceable<T>>`,\n * `Replaceable<Optional<T>>`, `unique_ptr<Replaceable<T>>` etc. except as a\n *  result of template expansion.\n */\n\nnamespace folly {\n\ntemplate <class T>\nclass Replaceable;\n\nnamespace replaceable_detail {\n/* Mixin templates to give `replaceable<T>` the following properties:\n *\n * 1. Trivial destructor if `T` has a trivial destructor; user-provided\n *    otherwise\n * 2. Move constructor if `T` has a move constructor; deleted otherwise\n * 3. Move assignment operator if `T` has a move constructor; deleted\n *    otherwise\n * 4. Copy constructor if `T` has a copy constructor; deleted otherwise\n * 5. Copy assignment operator if `T` has a copy constructor; deleted\n *    otherwise\n *\n * Has to be done in this way because we can't `enable_if` them away\n */\ntemplate <\n    class T,\n    bool = std::is_destructible<T>::value,\n    bool = std::is_trivially_destructible<T>::value>\nstruct dtor_mixin;\n\n/* Destructible and trivially destructible */\ntemplate <class T>\nstruct dtor_mixin<T, true, true> {};\n\n/* Destructible and not trivially destructible */\ntemplate <class T>\nstruct dtor_mixin<T, true, false> {\n  dtor_mixin() = default;\n  dtor_mixin(dtor_mixin&&) = default;\n  dtor_mixin(dtor_mixin const&) = default;\n  dtor_mixin& operator=(dtor_mixin&&) = default;\n  dtor_mixin& operator=(dtor_mixin const&) = default;\n  ~dtor_mixin() noexcept(std::is_nothrow_destructible<T>::value) {\n    T* destruct_ptr = std::launder(\n        reinterpret_cast<T*>(\n            reinterpret_cast<Replaceable<T>*>(this)->storage_));\n    destruct_ptr->~T();\n  }\n};\n\n/* Not destructible */\ntemplate <class T, bool A>\nstruct dtor_mixin<T, false, A> {\n  dtor_mixin() = default;\n  dtor_mixin(dtor_mixin&&) = default;\n  dtor_mixin(dtor_mixin const&) = default;\n  dtor_mixin& operator=(dtor_mixin&&) = default;\n  dtor_mixin& operator=(dtor_mixin const&) = default;\n  ~dtor_mixin() = delete;\n};\n\ntemplate <\n    class T,\n    bool = std::is_default_constructible<T>::value,\n    bool = std::is_move_constructible<T>::value>\nstruct default_and_move_ctor_mixin;\n\n/* Not default-constructible and not move-constructible */\ntemplate <class T>\nstruct default_and_move_ctor_mixin<T, false, false> {\n  default_and_move_ctor_mixin() = delete;\n  default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete;\n  default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;\n  default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =\n      default;\n  default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =\n      default;\n\n protected:\n  inline explicit default_and_move_ctor_mixin(int) {}\n};\n\n/* Default-constructible and move-constructible */\ntemplate <class T>\nstruct default_and_move_ctor_mixin<T, true, true> {\n  inline default_and_move_ctor_mixin() noexcept(\n      std::is_nothrow_constructible<T>::value) {\n    ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();\n  }\n  inline default_and_move_ctor_mixin(\n      default_and_move_ctor_mixin&&\n          other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {\n    ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)\n        T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));\n  }\n  default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;\n  default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =\n      default;\n  inline default_and_move_ctor_mixin& operator=(\n      default_and_move_ctor_mixin const&) = default;\n\n protected:\n  inline explicit default_and_move_ctor_mixin(int) {}\n};\n\n/* Default-constructible and not move-constructible */\ntemplate <class T>\nstruct default_and_move_ctor_mixin<T, true, false> {\n  inline default_and_move_ctor_mixin() noexcept(\n      std::is_nothrow_constructible<T>::value) {\n    ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();\n  }\n  default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete;\n  default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;\n  default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =\n      default;\n  default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =\n      default;\n\n protected:\n  inline explicit default_and_move_ctor_mixin(int) {}\n};\n\n/* Not default-constructible but is move-constructible */\ntemplate <class T>\nstruct default_and_move_ctor_mixin<T, false, true> {\n  default_and_move_ctor_mixin() = delete;\n  inline default_and_move_ctor_mixin(\n      default_and_move_ctor_mixin&&\n          other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {\n    ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)\n        T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));\n  }\n  default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;\n  default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =\n      default;\n  default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =\n      default;\n\n protected:\n  inline explicit default_and_move_ctor_mixin(int) {}\n};\n\ntemplate <\n    class T,\n    bool = (std::is_destructible<T>::value) &&\n        (std::is_move_constructible<T>::value)>\nstruct move_assignment_mixin;\n\n/* Not (destructible and move-constructible) */\ntemplate <class T>\nstruct move_assignment_mixin<T, false> {\n  move_assignment_mixin() = default;\n  move_assignment_mixin(move_assignment_mixin&&) = default;\n  move_assignment_mixin(move_assignment_mixin const&) = default;\n  move_assignment_mixin& operator=(move_assignment_mixin&&) = delete;\n  move_assignment_mixin& operator=(move_assignment_mixin const&) = default;\n};\n\n/* Both destructible and move-constructible */\ntemplate <class T>\nstruct move_assignment_mixin<T, true> {\n  move_assignment_mixin() = default;\n  move_assignment_mixin(move_assignment_mixin&&) = default;\n  move_assignment_mixin(move_assignment_mixin const&) = default;\n  inline move_assignment_mixin&\n  operator=(move_assignment_mixin&& other) noexcept(\n      std::is_nothrow_destructible<T>::value &&\n      std::is_nothrow_move_constructible<T>::value) {\n    T* destruct_ptr = std::launder(\n        reinterpret_cast<T*>(\n            reinterpret_cast<Replaceable<T>*>(this)->storage_));\n    destruct_ptr->~T();\n    ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)\n        T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));\n    return *this;\n  }\n  move_assignment_mixin& operator=(move_assignment_mixin const&) = default;\n};\n\ntemplate <class T, bool = std::is_copy_constructible<T>::value>\nstruct copy_ctor_mixin;\n\n/* Not copy-constructible */\ntemplate <class T>\nstruct copy_ctor_mixin<T, false> {\n  copy_ctor_mixin() = default;\n  copy_ctor_mixin(copy_ctor_mixin&&) = default;\n  copy_ctor_mixin(copy_ctor_mixin const&) = delete;\n  copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;\n  copy_ctor_mixin& operator=(copy_ctor_mixin const&) = delete;\n};\n\n/* Copy-constructible */\ntemplate <class T>\nstruct copy_ctor_mixin<T, true> {\n  copy_ctor_mixin() = default;\n  inline copy_ctor_mixin(copy_ctor_mixin const& other) noexcept(\n      std::is_nothrow_constructible<T, T const&>::value) {\n    ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)\n        T(*reinterpret_cast<Replaceable<T> const&>(other));\n  }\n  copy_ctor_mixin(copy_ctor_mixin&&) = default;\n  copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;\n  copy_ctor_mixin& operator=(copy_ctor_mixin const&) = default;\n};\n\ntemplate <\n    class T,\n    bool = (std::is_destructible<T>::value) &&\n        (std::is_copy_constructible<T>::value)>\nstruct copy_assignment_mixin;\n\n/* Not (destructible and copy-constructible) */\ntemplate <class T>\nstruct copy_assignment_mixin<T, false> {\n  copy_assignment_mixin() = default;\n  copy_assignment_mixin(copy_assignment_mixin&&) = default;\n  copy_assignment_mixin(copy_assignment_mixin const&) = default;\n  copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;\n  copy_assignment_mixin& operator=(copy_assignment_mixin const&) = delete;\n};\n\n/* Both destructible and copy-constructible */\ntemplate <class T>\nstruct copy_assignment_mixin<T, true> {\n  copy_assignment_mixin() = default;\n  copy_assignment_mixin(copy_assignment_mixin&&) = default;\n  copy_assignment_mixin(copy_assignment_mixin const&) = default;\n  copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;\n  inline copy_assignment_mixin&\n  operator=(copy_assignment_mixin const& other) noexcept(\n      std::is_nothrow_destructible<T>::value &&\n      std::is_nothrow_copy_constructible<T>::value) {\n    T* destruct_ptr = std::launder(\n        reinterpret_cast<T*>(\n            reinterpret_cast<Replaceable<T>*>(this)->storage_));\n    destruct_ptr->~T();\n    ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)\n        T(*reinterpret_cast<Replaceable<T> const&>(other));\n    return *this;\n  }\n};\n\ntemplate <typename T>\nstruct is_constructible_from_replaceable\n    : std::bool_constant<\n          std::is_constructible<T, Replaceable<T>&>::value ||\n          std::is_constructible<T, Replaceable<T>&&>::value ||\n          std::is_constructible<T, const Replaceable<T>&>::value ||\n          std::is_constructible<T, const Replaceable<T>&&>::value> {};\n\ntemplate <typename T>\nstruct is_convertible_from_replaceable\n    : std::bool_constant<\n          std::is_convertible<Replaceable<T>&, T>::value ||\n          std::is_convertible<Replaceable<T>&&, T>::value ||\n          std::is_convertible<const Replaceable<T>&, T>::value ||\n          std::is_convertible<const Replaceable<T>&&, T>::value> {};\n} // namespace replaceable_detail\n\ntemplate <class T>\nusing is_replaceable = is_instantiation_of<Replaceable, T>;\n\n// Function to make a Replaceable with a type deduced from its input\ntemplate <class T>\nconstexpr Replaceable<std::decay_t<T>> make_replaceable(T&& t) {\n  return Replaceable<std::decay_t<T>>(std::forward<T>(t));\n}\n\ntemplate <class T, class... Args>\nconstexpr Replaceable<T> make_replaceable(Args&&... args) {\n  return Replaceable<T>(std::in_place, std::forward<Args>(args)...);\n}\n\ntemplate <class T, class U, class... Args>\nconstexpr Replaceable<T> make_replaceable(\n    std::initializer_list<U> il, Args&&... args) {\n  return Replaceable<T>(std::in_place, il, std::forward<Args>(args)...);\n}\n\ntemplate <class T>\nclass alignas(T) Replaceable\n    : public replaceable_detail::dtor_mixin<T>,\n      public replaceable_detail::default_and_move_ctor_mixin<T>,\n      public replaceable_detail::copy_ctor_mixin<T>,\n      public replaceable_detail::move_assignment_mixin<T>,\n      public replaceable_detail::copy_assignment_mixin<T> {\n  using ctor_base = replaceable_detail::default_and_move_ctor_mixin<T>;\n\n public:\n  using value_type = T;\n\n  /* Rule-of-zero default- copy- and move- constructors. The ugly code to make\n   * these work are above, in namespace folly::replaceable_detail.\n   */\n  constexpr Replaceable() = default;\n  constexpr Replaceable(const Replaceable&) = default;\n  constexpr Replaceable(Replaceable&&) = default;\n\n  /* Rule-of-zero copy- and move- assignment operators. The ugly code to make\n   * these work are above, in namespace folly::replaceable_detail.\n   *\n   * Note - these destruct the `T` and then in-place construct a new one based\n   * on what is in the other replaceable; they do not invoke the assignment\n   * operator of `T`.\n   */\n  Replaceable& operator=(const Replaceable&) = default;\n  Replaceable& operator=(Replaceable&&) = default;\n\n  /* Rule-of-zero destructor. The ugly code to make this work is above, in\n   * namespace folly::replaceable_detail.\n   */\n  ~Replaceable() = default;\n\n  /**\n   * Constructors; these are modeled very closely on the definition of\n   * `std::optional` in C++17.\n   */\n  template <\n      class... Args,\n      std::enable_if_t<std::is_constructible<T, Args&&...>::value, int> = 0>\n  constexpr explicit Replaceable(std::in_place_t, Args&&... args)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<T, Args&&...>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(std::forward<Args>(args)...);\n  }\n\n  template <\n      class U,\n      class... Args,\n      std::enable_if_t<\n          std::is_constructible<T, std::initializer_list<U>, Args&&...>::value,\n          int> = 0>\n  constexpr explicit Replaceable(\n      std::in_place_t, std::initializer_list<U> il, Args&&... args)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<\n          T,\n          std::initializer_list<U>,\n          Args&&...>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(il, std::forward<Args>(args)...);\n  }\n\n  template <\n      class U = T,\n      std::enable_if_t<\n          std::is_constructible<T, U&&>::value &&\n              !std::is_same<std::decay_t<U>, std::in_place_t>::value &&\n              !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&\n              std::is_convertible<U&&, T>::value,\n          int> = 0>\n  constexpr /* implicit */ Replaceable(U&& other)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<T, U&&>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(std::forward<U>(other));\n  }\n\n  template <\n      class U = T,\n      std::enable_if_t<\n          std::is_constructible<T, U&&>::value &&\n              !std::is_same<std::decay_t<U>, std::in_place_t>::value &&\n              !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&\n              !std::is_convertible<U&&, T>::value,\n          int> = 0>\n  constexpr explicit Replaceable(U&& other)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<T, U&&>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(std::forward<U>(other));\n  }\n\n  template <\n      class U,\n      std::enable_if_t<\n          std::is_constructible<T, const U&>::value &&\n              !replaceable_detail::is_constructible_from_replaceable<\n                  T>::value &&\n              !replaceable_detail::is_convertible_from_replaceable<T>::value &&\n              std::is_convertible<const U&, T>::value,\n          int> = 0>\n  /* implicit */ Replaceable(const Replaceable<U>& other)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<T, U const&>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(*other);\n  }\n\n  template <\n      class U,\n      std::enable_if_t<\n          std::is_constructible<T, const U&>::value &&\n              !replaceable_detail::is_constructible_from_replaceable<\n                  T>::value &&\n              !replaceable_detail::is_convertible_from_replaceable<T>::value &&\n              !std::is_convertible<const U&, T>::value,\n          int> = 0>\n  explicit Replaceable(const Replaceable<U>& other)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<T, U const&>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(*other);\n  }\n\n  template <\n      class U,\n      std::enable_if_t<\n          std::is_constructible<T, U&&>::value &&\n              !replaceable_detail::is_constructible_from_replaceable<\n                  T>::value &&\n              !replaceable_detail::is_convertible_from_replaceable<T>::value &&\n              std::is_convertible<U&&, T>::value,\n          int> = 0>\n  /* implicit */ Replaceable(Replaceable<U>&& other)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<T, U&&>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(std::move(*other));\n  }\n\n  template <\n      class U,\n      std::enable_if_t<\n          std::is_constructible<T, U&&>::value &&\n              !replaceable_detail::is_constructible_from_replaceable<\n                  T>::value &&\n              !replaceable_detail::is_convertible_from_replaceable<T>::value &&\n              !std::is_convertible<U&&, T>::value,\n          int> = 0>\n  explicit Replaceable(Replaceable<U>&& other)\n      // clang-format off\n      noexcept(std::is_nothrow_constructible<T, U&&>::value)\n      // clang-format on\n      : ctor_base(0) {\n    ::new (storage_) T(std::move(*other));\n  }\n\n  /**\n   * `emplace` destructs the contained object and in-place constructs the\n   * replacement.\n   *\n   * The destructor must not throw (as usual). The constructor must not throw\n   * because that would violate the invariant that a `Replaceable<T>` always\n   * contains a T instance.\n   *\n   * As these methods are `noexcept` the program will be terminated if an\n   * exception is thrown. If you are encountering this issue you should look at\n   * using `Optional` instead.\n   */\n  template <class... Args>\n  T& emplace(Args&&... args) noexcept {\n    T* destruct_ptr = std::launder(reinterpret_cast<T*>(storage_));\n    destruct_ptr->~T();\n    return *::new (storage_) T(std::forward<Args>(args)...);\n  }\n\n  template <class U, class... Args>\n  T& emplace(std::initializer_list<U> il, Args&&... args) noexcept {\n    T* destruct_ptr = std::launder(reinterpret_cast<T*>(storage_));\n    destruct_ptr->~T();\n    return *::new (storage_) T(il, std::forward<Args>(args)...);\n  }\n\n  /**\n   * `swap` just calls `swap(T&, T&)`.\n   */\n  void swap(Replaceable& other) noexcept(\n      std::is_nothrow_swappable_v<Replaceable>) {\n    using std::swap;\n    swap(*(*this), *other);\n  }\n\n  /**\n   * Methods to access the contained object. Intended to be very unsurprising.\n   */\n  constexpr const T* operator->() const {\n    return std::launder(reinterpret_cast<T const*>(storage_));\n  }\n\n  constexpr T* operator->() {\n    return std::launder(reinterpret_cast<T*>(storage_));\n  }\n\n  constexpr const T& operator*() const& {\n    return *std::launder(reinterpret_cast<T const*>(storage_));\n  }\n\n  constexpr T& operator*() & {\n    return *std::launder(reinterpret_cast<T*>(storage_));\n  }\n\n  constexpr T&& operator*() && {\n    return std::move(*std::launder(reinterpret_cast<T*>(storage_)));\n  }\n\n  constexpr const T&& operator*() const&& {\n    return std::move(*std::launder(reinterpret_cast<T const*>(storage_)));\n  }\n\n private:\n  friend struct replaceable_detail::dtor_mixin<T>;\n  friend struct replaceable_detail::default_and_move_ctor_mixin<T>;\n  friend struct replaceable_detail::copy_ctor_mixin<T>;\n  friend struct replaceable_detail::move_assignment_mixin<T>;\n  friend struct replaceable_detail::copy_assignment_mixin<T>;\n  aligned_storage_for_t<T> storage_[1];\n};\n\ntemplate <class T>\nReplaceable(T) -> Replaceable<T>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ScopeGuard.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ScopeGuard.h>\n\n#include <exception>\n#include <iostream>\n\n/*static*/ void folly::detail::ScopeGuardImplBase::terminate() noexcept {\n  // Ensure the availability of std::cerr\n  std::ios_base::Init ioInit;\n  std::cerr\n      << \"This program will now terminate because a folly::ScopeGuard callback \"\n         \"threw an \\nexception.\\n\";\n  std::rethrow_exception(current_exception());\n}\n"
  },
  {
    "path": "folly/ScopeGuard.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_scopeguard\n//\n\n/**\n * ScopeGuard is a general implementation of the \"Initialization is\n * Resource Acquisition\" idiom.  It guarantees that a function\n * is executed upon leaving the current scope.\n *\n * @file ScopeGuard.h\n * @refcode folly/docs/examples/folly/ScopeGuard.cpp\n */\n/*\n * The makeGuard() function is used to create a new ScopeGuard object.\n * It can be instantiated with a lambda function, a std::function<void()>,\n * a functor, or a void(*)() function pointer.\n *\n *\n * Usage example: Add a friend to memory if and only if it is also added\n * to the db.\n *\n * void User::addFriend(User& newFriend) {\n *   // add the friend to memory\n *   friends_.push_back(&newFriend);\n *\n *   // If the db insertion that follows fails, we should\n *   // remove it from memory.\n *   auto guard = makeGuard([&] { friends_.pop_back(); });\n *\n *   // this will throw an exception upon error, which\n *   // makes the ScopeGuard execute UserCont::pop_back()\n *   // once the Guard's destructor is called.\n *   db_->addFriend(GetName(), newFriend.GetName());\n *\n *   // an exception was not thrown, so don't execute\n *   // the Guard.\n *   guard.dismiss();\n * }\n *\n * It is also possible to create a guard in dismissed state with\n * makeDismissedGuard(), and later rehire it with the rehire()\n * method.\n *\n * makeDismissedGuard() is not just syntactic sugar for creating a guard and\n * immediately dismissing it, but it has a subtle behavior difference if\n * move-construction of the passed function can throw: if it does, the function\n * will be called by makeGuard(), but not by makeDismissedGuard().\n *\n * Examine ScopeGuardTest.cpp for some more sample usage.\n *\n * Stolen from:\n *   Andrei's and Petru Marginean's CUJ article:\n *     http://drdobbs.com/184403758\n *   and the loki library:\n *     http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer\n *   and triendl.kj article:\n *     http://www.codeproject.com/KB/cpp/scope_guard.aspx\n */\n#pragma once\n\n#include <cstddef>\n#include <cstdlib>\n#include <functional>\n#include <new>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Portability.h>\n#include <folly/Preprocessor.h>\n#include <folly/Utility.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/UncaughtExceptions.h>\n\nnamespace folly {\n\nnamespace detail {\n\nstruct ScopeGuardDismissed {};\n\nclass ScopeGuardImplBase {\n public:\n  void dismiss() noexcept { dismissed_ = true; }\n  void rehire() noexcept { dismissed_ = false; }\n\n protected:\n  ScopeGuardImplBase(bool dismissed = false) noexcept : dismissed_(dismissed) {}\n\n  [[noreturn]] static void terminate() noexcept;\n  static ScopeGuardImplBase makeEmptyScopeGuard() noexcept {\n    return ScopeGuardImplBase{};\n  }\n\n  bool dismissed_;\n};\n\ntemplate <typename FunctionType, bool InvokeNoexcept>\nclass ScopeGuardImpl : public ScopeGuardImplBase {\n public:\n  explicit ScopeGuardImpl(FunctionType& fn) noexcept(\n      std::is_nothrow_copy_constructible_v<FunctionType>)\n      : ScopeGuardImpl(\n            std::as_const(fn),\n            makeFailsafe(\n                std::is_nothrow_copy_constructible<FunctionType>{}, &fn)) {}\n\n  explicit ScopeGuardImpl(const FunctionType& fn) noexcept(\n      std::is_nothrow_copy_constructible_v<FunctionType>)\n      : ScopeGuardImpl(\n            fn,\n            makeFailsafe(\n                std::is_nothrow_copy_constructible<FunctionType>{}, &fn)) {}\n\n  explicit ScopeGuardImpl(FunctionType&& fn) noexcept(\n      std::is_nothrow_move_constructible_v<FunctionType>)\n      : ScopeGuardImpl(\n            std::move_if_noexcept(fn),\n            makeFailsafe(\n                std::is_nothrow_move_constructible<FunctionType>{}, &fn)) {}\n\n  explicit ScopeGuardImpl(FunctionType&& fn, ScopeGuardDismissed) noexcept(\n      std::is_nothrow_move_constructible_v<FunctionType>)\n      // No need for failsafe in this case, as the guard is dismissed.\n      : ScopeGuardImplBase{true}, function_(std::forward<FunctionType>(fn)) {}\n\n  ScopeGuardImpl(ScopeGuardImpl&& other) noexcept(\n      std::is_nothrow_move_constructible_v<FunctionType>)\n      : function_(std::move_if_noexcept(other.function_)) {\n    // If the above line attempts a copy and the copy throws, other is\n    // left owning the cleanup action and will execute it (or not) depending\n    // on the value of other.dismissed_. The following lines only execute\n    // if the move/copy succeeded, in which case *this assumes ownership of\n    // the cleanup action and dismisses other.\n    dismissed_ = std::exchange(other.dismissed_, true);\n  }\n\n  ~ScopeGuardImpl() noexcept(InvokeNoexcept) {\n    if (!dismissed_) {\n      execute();\n    }\n  }\n\n private:\n  static ScopeGuardImplBase makeFailsafe(std::true_type, const void*) noexcept {\n    return makeEmptyScopeGuard();\n  }\n\n  template <typename Fn>\n  static auto makeFailsafe(std::false_type, Fn* fn) noexcept\n      -> ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept> {\n    return ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept>{\n        std::ref(*fn)};\n  }\n\n  template <typename Fn>\n  explicit ScopeGuardImpl(Fn&& fn, ScopeGuardImplBase&& failsafe)\n      : ScopeGuardImplBase{}, function_(std::forward<Fn>(fn)) {\n    failsafe.dismiss();\n  }\n\n  void* operator new(std::size_t) = delete;\n\n  void execute() noexcept(InvokeNoexcept) {\n    if constexpr (InvokeNoexcept) {\n      static_assert(std::is_same_v<void, decltype(function_())>);\n      catch_exception(function_, &terminate);\n    } else {\n      function_();\n    }\n  }\n\n  FunctionType function_;\n};\n\ntemplate <typename F, bool INE>\nusing ScopeGuardImplDecay = ScopeGuardImpl<std::decay_t<F>, INE>;\n\n} // namespace detail\n\n/**\n * Create a scope guard.\n *\n * The returned object has methods .dismiss() and .rehire(), which will\n * deactivate/reactivate the calling of the function upon destruction.\n *\n * The return value of this function must be captured. Otherwise, since it is a\n * temporary, it will be destroyed immediately, thus calling the function.\n *\n *     auto guard = makeGuard(...); // good\n *\n *     makeGuard(...); // bad\n *\n * @param f  The function to execute upon the guard's destruction.\n * @refcode folly/docs/examples/folly/ScopeGuard2.cpp\n */\ntemplate <typename F>\n[[nodiscard]] detail::ScopeGuardImplDecay<F, true> makeGuard(F&& f) noexcept(\n    noexcept(detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f)))) {\n  return detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f));\n}\n\n/**\n * Create a scope guard in the dismissed state.\n *\n * The guard can be enabled using .rehire().\n *\n * @see makeGuard\n * @refcode folly/docs/examples/folly/ScopeGuard2.cpp\n */\ntemplate <typename F>\n[[nodiscard]] detail::ScopeGuardImplDecay<F, true>\nmakeDismissedGuard(F&& f) noexcept(\n    noexcept(detail::ScopeGuardImplDecay<F, true>(\n        static_cast<F&&>(f), detail::ScopeGuardDismissed{}))) {\n  return detail::ScopeGuardImplDecay<F, true>(\n      static_cast<F&&>(f), detail::ScopeGuardDismissed{});\n}\n\nnamespace detail {\n\n/**\n * ScopeGuard used for executing a function when leaving the current scope\n * depending on the presence of a new uncaught exception.\n *\n * If the executeOnException template parameter is true, the function is\n * executed if a new uncaught exception is present at the end of the scope.\n * If the parameter is false, then the function is executed if no new uncaught\n * exceptions are present at the end of the scope.\n *\n * Used to implement SCOPE_FAIL and SCOPE_SUCCESS below.\n */\ntemplate <typename FunctionType, bool ExecuteOnException>\nclass ScopeGuardForNewException {\n public:\n  explicit ScopeGuardForNewException(const FunctionType& fn) : guard_(fn) {}\n\n  explicit ScopeGuardForNewException(FunctionType&& fn)\n      : guard_(std::move(fn)) {}\n\n  ScopeGuardForNewException(ScopeGuardForNewException&& other) = default;\n\n  ~ScopeGuardForNewException() noexcept(ExecuteOnException) {\n    if (ExecuteOnException != (exceptionCounter_ < uncaught_exceptions())) {\n      guard_.dismiss();\n    }\n  }\n\n private:\n  void* operator new(std::size_t) = delete;\n  void operator delete(void*) = delete;\n\n  ScopeGuardImpl<FunctionType, ExecuteOnException> guard_;\n  int exceptionCounter_{uncaught_exceptions()};\n};\n\n/**\n * Internal use for the macro SCOPE_FAIL below\n */\nenum class ScopeGuardOnFail {};\n\ntemplate <typename FunctionType>\nScopeGuardForNewException<std::decay_t<FunctionType>, true> operator+(\n    detail::ScopeGuardOnFail, FunctionType&& fn) {\n  return ScopeGuardForNewException<std::decay_t<FunctionType>, true>(\n      std::forward<FunctionType>(fn));\n}\n\n/**\n * Internal use for the macro SCOPE_SUCCESS below\n */\nenum class ScopeGuardOnSuccess {};\n\ntemplate <typename FunctionType>\nScopeGuardForNewException<std::decay_t<FunctionType>, false> operator+(\n    ScopeGuardOnSuccess, FunctionType&& fn) {\n  return ScopeGuardForNewException<std::decay_t<FunctionType>, false>(\n      std::forward<FunctionType>(fn));\n}\n\n/**\n * Internal use for the macro SCOPE_EXIT below\n */\nenum class ScopeGuardOnExit {};\n\ntemplate <typename FunctionType>\nScopeGuardImpl<std::decay_t<FunctionType>, true> operator+(\n    detail::ScopeGuardOnExit, FunctionType&& fn) {\n  return ScopeGuardImpl<std::decay_t<FunctionType>, true>(\n      std::forward<FunctionType>(fn));\n}\n} // namespace detail\n\n} // namespace folly\n\n//  SCOPE_EXIT\n//\n//  Example:\n//\n//      /* open scope */ {\n//\n//        some_resource_t resource;\n//        some_resource_init(resource);\n//        SCOPE_EXIT { some_resource_fini(resource); };\n//\n//        if (!cond)\n//          throw 0; // the cleanup happens at end of the scope\n//        else\n//          return; // the cleanup happens at end of the scope\n//\n//        use_some_resource(resource); // may throw; cleanup will happen\n//\n//      } /* close scope */\n//\n//  The code in the braces passed to SCOPE_EXIT executes at the end of the\n//  containing scope as if the code is the content of the destructor of an\n//  object instantiated at the point of the SCOPE_EXIT, where the destructor\n//  reference-captures all local variables it uses.\n//\n//  The cleanup code - the code in the braces passed to SCOPE_EXIT - always\n//  executes at the end of the scope, regardless of whether the scope exits\n//  normally or erroneously as if via the throw statement.\n//\n//  Caution: Suitable for coroutine functions only when the cleanup code does\n//  not use captured references to thread-local objects. Recall that there is\n//  no assumption that coroutines resume from co-await, co-yield, or co-return\n//  in the same thread as the one in which they suspend.\n//\n//  Caution: May not execute if the scope exits erroneously but stack unwinding\n//  is skipped, or if the scope does not exit at all such as with std::abort or\n//  setcontext, which fibers use.\n\n/**\n * Capture code that shall be run when the current scope exits.\n *\n * The code within SCOPE_EXIT's braces shall execute as if the code was in the\n * destructor of an object instantiated at the point of SCOPE_EXIT.\n *\n * Variables used within SCOPE_EXIT are captured by reference.\n *\n * @def SCOPE_EXIT\n */\n#define SCOPE_EXIT                                        \\\n  auto FB_ANONYMOUS_VARIABLE_ODR_SAFE(SCOPE_EXIT_STATE) = \\\n      ::folly::detail::ScopeGuardOnExit() + [&]() noexcept\n\n//  SCOPE_FAIL\n//\n//  May be useful in situations where the caller requests a resource where\n//  initializations of the resource is multi-step and may fail.\n//\n//  Example:\n//\n//      some_resource_t resource;\n//      some_resource_init(resource);\n//      SCOPE_FAIL { some_resource_fini(resource); };\n//\n//      if (do_throw)\n//        throw 0; // the cleanup happens at the end of the scope\n//      else\n//        return resource; // the cleanup does not happen\n//\n//  Warning: Not suitable for coroutine functions.\n\n/**\n * Capture code to run if the scope exits with an exception.\n *\n * Like SCOPE_EXIT, but only executes the code if the scope exited due to an\n * exception.\n *\n * @def SCOPE_FAIL\n */\n#define SCOPE_FAIL                                        \\\n  auto FB_ANONYMOUS_VARIABLE_ODR_SAFE(SCOPE_FAIL_STATE) = \\\n      ::folly::detail::ScopeGuardOnFail() + [&]() noexcept\n\n//  SCOPE_SUCCESS\n//\n//  In a sense, the opposite of SCOPE_FAIL.\n//\n//  Example:\n//\n//      folly::stop_watch<> watch;\n//      SCOPE_FAIL { log_failure(watch.elapsed(); };\n//      SCOPE_SUCCESS { log_success(watch.elapsed(); };\n//\n//      if (do_throw)\n//        throw 0; // the cleanup does not happen; log failure\n//      else\n//        return; // the cleanup happens at the end of the scope; log success\n//\n//  Warning: Not suitable for coroutine functions.\n\n/**\n * Capture code to run if the scope exits without an exception.\n *\n * Like SCOPE_EXIT, but does not execute the code if the scope exited due to an\n * exception.\n *\n * @def SCOPE_SUCCESS\n */\n#define SCOPE_SUCCESS                                        \\\n  auto FB_ANONYMOUS_VARIABLE_ODR_SAFE(SCOPE_SUCCESS_STATE) = \\\n      ::folly::detail::ScopeGuardOnSuccess() + [&]()\n"
  },
  {
    "path": "folly/SharedMutex.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <system_error>\n\n#include <folly/SharedMutex.h>\n\n#include <folly/Indestructible.h>\n#include <folly/lang/Exception.h>\n#include <folly/portability/SysResource.h>\n\nnamespace folly {\n// Explicitly instantiate SharedMutex here:\ntemplate class SharedMutexImpl<true>;\ntemplate class SharedMutexImpl<false>;\n\nnamespace shared_mutex_detail {\nstd::unique_lock<std::mutex> annotationGuard(void* ptr) {\n  if (folly::kIsSanitizeThread) {\n    // On TSAN builds, we have an array of mutexes and index into them based on\n    // the address. If the array is of prime size things will work out okay\n    // without a complicated hash function.\n    static constexpr std::size_t kNumAnnotationMutexes = 251;\n    static Indestructible<std::array<std::mutex, kNumAnnotationMutexes>>\n        kAnnotationMutexes;\n    auto index = reinterpret_cast<uintptr_t>(ptr) % kNumAnnotationMutexes;\n    return std::unique_lock<std::mutex>((*kAnnotationMutexes)[index]);\n  } else {\n    return std::unique_lock<std::mutex>();\n  }\n}\n\nuint32_t getMaxDeferredReadersSlow(relaxed_atomic<uint32_t>& cache) {\n  uint32_t maxDeferredReaders = std::min(\n      static_cast<uint32_t>(\n          folly::nextPowTwo(CacheLocality::system().numCpus) << 1),\n      shared_mutex_detail::kMaxDeferredReadersAllocated);\n  // maxDeferredReaders must be a power of 2\n  assert(!(maxDeferredReaders & (maxDeferredReaders - 1)));\n  cache = maxDeferredReaders;\n  return maxDeferredReaders;\n}\n\nlong getCurrentThreadInvoluntaryContextSwitchCount() {\n#ifdef RUSAGE_THREAD\n  struct rusage usage;\n  if (getrusage(RUSAGE_THREAD, &usage)) {\n    return 0;\n  } else {\n    return usage.ru_nivcsw;\n  }\n#else\n  return 0;\n#endif\n}\n\n[[noreturn]] void throwOperationNotPermitted() {\n  folly::throw_exception<std::system_error>(\n      std::make_error_code(std::errc::operation_not_permitted));\n}\n\n[[noreturn]] void throwDeadlockWouldOccur() {\n  folly::throw_exception<std::system_error>(\n      std::make_error_code(std::errc::resource_deadlock_would_occur));\n}\n\n} // namespace shared_mutex_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/SharedMutex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#include <atomic>\n#include <chrono>\n#include <memory>\n#include <mutex>\n#include <shared_mutex>\n#include <thread>\n#include <type_traits>\n#include <utility>\n\n#include <folly/CPortability.h>\n#include <folly/CppAttributes.h>\n#include <folly/Likely.h>\n#include <folly/chrono/Hardware.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/detail/Futex.h>\n#include <folly/portability/Asm.h>\n#include <folly/synchronization/Lock.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n#include <folly/synchronization/SanitizeThread.h>\n#include <folly/system/ThreadId.h>\n\n// SharedMutex is a reader-writer lock.  It is small, very fast, scalable\n// on multi-core, and suitable for use when readers or writers may block.\n// Unlike most other reader-writer locks, its throughput with concurrent\n// readers scales linearly; it is able to acquire and release the lock\n// in shared mode without cache line ping-ponging.  It is suitable for\n// a wide range of lock hold times because it starts with spinning,\n// proceeds to using sched_yield with a preemption heuristic, and then\n// waits using futex and precise wakeups.\n//\n// SharedMutex provides all of the methods of folly::RWSpinLock,\n// boost::shared_mutex, boost::upgrade_mutex, and C++14's\n// std::shared_timed_mutex.  All operations that can block are available\n// in try, try-for, and try-until (system_clock or steady_clock) versions.\n//\n// SharedMutexReadPriority gives priority to readers,\n// SharedMutexWritePriority gives priority to writers.  SharedMutex is an\n// alias for SharedMutexWritePriority, because writer starvation is more\n// likely than reader starvation for the read-heavy workloads targeted\n// by SharedMutex.\n//\n// In my tests SharedMutex is as good or better than the other\n// reader-writer locks in use at Facebook for almost all use cases,\n// sometimes by a wide margin.  (If it is rare that there are actually\n// concurrent readers then RWSpinLock can be a few nanoseconds faster.)\n// I compared it to folly::RWSpinLock, boost::shared_mutex,\n// pthread_rwlock_t, and a RWLock that internally uses spinlocks to guard\n// state and pthread_mutex_t+pthread_cond_t to block.\n// (Thrift's ReadWriteMutex is based underneath on pthread_rwlock_t.)\n// It is generally as good or better than the rest when evaluating size,\n// speed, scalability, or latency outliers.  In the corner cases where\n// it is not the fastest (such as single-threaded use or heavy write\n// contention) it is never very much worse than the best.  See the bottom\n// of folly/test/SharedMutexTest.cpp for lots of microbenchmark results.\n//\n// Comparison to folly::RWSpinLock:\n//\n//  * SharedMutex is faster than RWSpinLock when there are actually\n//    concurrent read accesses (sometimes much faster), and ~5 nanoseconds\n//    slower when there is not actually any contention.  SharedMutex is\n//    faster in every (benchmarked) scenario where the shared mode of\n//    the lock is actually useful.\n//\n//  * Concurrent shared access to SharedMutex scales linearly, while total\n//    RWSpinLock throughput drops as more threads try to access the lock\n//    in shared mode.  Under very heavy read contention SharedMutex can\n//    be two orders of magnitude faster than RWSpinLock (or any reader\n//    writer lock that doesn't use striping or deferral).\n//\n//  * SharedMutex can safely protect blocking calls, because after an\n//    initial period of spinning it waits using futex().\n//\n//  * RWSpinLock prioritizes readers, SharedMutex has both reader- and\n//    writer-priority variants, but defaults to write priority.\n//\n//  * RWSpinLock's upgradeable mode blocks new readers, while SharedMutex's\n//    doesn't.  Both semantics are reasonable.  The boost documentation\n//    doesn't explicitly talk about this behavior (except by omitting\n//    any statement that those lock modes conflict), but the boost\n//    implementations do allow new readers while the upgradeable mode\n//    is held.  See https://github.com/boostorg/thread/blob/master/\n//      include/boost/thread/pthread/shared_mutex.hpp\n//\n// Both SharedMutex and RWSpinLock provide \"exclusive\", \"upgrade\",\n// and \"shared\" modes.  At all times num_threads_holding_exclusive +\n// num_threads_holding_upgrade <= 1, and num_threads_holding_exclusive ==\n// 0 || num_threads_holding_shared == 0.  RWSpinLock has the additional\n// constraint that num_threads_holding_shared cannot increase while\n// num_threads_holding_upgrade is non-zero.\n//\n// Comparison to the internal RWLock:\n//\n//  * SharedMutex doesn't allow a maximum reader count to be configured,\n//    so it can't be used as a semaphore in the same way as RWLock.\n//\n//  * SharedMutex is 4 bytes, RWLock is 256.\n//\n//  * SharedMutex is as fast or faster than RWLock in all of my\n//    microbenchmarks, and has positive rather than negative scalability.\n//\n//  * RWLock and SharedMutex are both writer priority locks.\n//\n//  * SharedMutex avoids latency outliers as well as RWLock.\n//\n//  * SharedMutex uses different names (t != 0 below):\n//\n//    RWLock::lock(0)    => SharedMutex::lock()\n//\n//    RWLock::lock(t)    => SharedMutex::try_lock_for(milliseconds(t))\n//\n//    RWLock::tryLock()  => SharedMutex::try_lock()\n//\n//    RWLock::unlock()   => SharedMutex::unlock()\n//\n//    RWLock::enter(0)   => SharedMutex::lock_shared()\n//\n//    RWLock::enter(t)   =>\n//        SharedMutex::try_lock_shared_for(milliseconds(t))\n//\n//    RWLock::tryEnter() => SharedMutex::try_lock_shared()\n//\n//    RWLock::leave()    => SharedMutex::unlock_shared()\n//\n//  * RWLock allows the reader count to be adjusted by a value other\n//    than 1 during enter() or leave(). SharedMutex doesn't currently\n//    implement this feature.\n//\n//  * RWLock's methods are marked const, SharedMutex's aren't.\n//\n// Reader-writer locks have the potential to allow concurrent access\n// to shared read-mostly data, but in practice they often provide no\n// improvement over a mutex.  The problem is the cache coherence protocol\n// of modern CPUs.  Coherence is provided by making sure that when a cache\n// line is written it is present in only one core's cache.  Since a memory\n// write is required to acquire a reader-writer lock in shared mode, the\n// cache line holding the lock is invalidated in all of the other caches.\n// This leads to cache misses when another thread wants to acquire or\n// release the lock concurrently.  When the RWLock is colocated with the\n// data it protects (common), cache misses can also continue occur when\n// a thread that already holds the lock tries to read the protected data.\n//\n// Ideally, a reader-writer lock would allow multiple cores to acquire\n// and release the lock in shared mode without incurring any cache misses.\n// This requires that each core records its shared access in a cache line\n// that isn't read or written by other read-locking cores.  (Writers will\n// have to check all of the cache lines.)  Typical server hardware when\n// this comment was written has 16 L1 caches and cache lines of 64 bytes,\n// so a lock striped over all L1 caches would occupy a prohibitive 1024\n// bytes.  Nothing says that we need a separate set of per-core memory\n// locations for each lock, however.  Each SharedMutex instance is only\n// 4 bytes, but all locks together share a 2K area in which they make a\n// core-local record of lock acquisitions.\n//\n// SharedMutex's strategy of using a shared set of core-local stripes has\n// a potential downside, because it means that acquisition of any lock in\n// write mode can conflict with acquisition of any lock in shared mode.\n// If a lock instance doesn't actually experience concurrency then this\n// downside will outweight the upside of improved scalability for readers.\n// To avoid this problem we dynamically detect concurrent accesses to\n// SharedMutex, and don't start using the deferred mode unless we actually\n// observe concurrency.  See kNumSharedToStartDeferring.\n//\n// It is explicitly allowed to call unlock_shared() from a different\n// thread than lock_shared(), so long as they are properly paired.\n// unlock_shared() needs to find the location at which lock_shared()\n// recorded the lock, which might be in the lock itself or in any of\n// the shared slots.  If you can conveniently pass state from lock\n// acquisition to release then the fastest mechanism is to std::move\n// the std::shared_lock instance or an SharedMutex::Token (using\n// lock_shared(Token&) and unlock_shared(Token&)).  The guard or token\n// will tell unlock_shared where in deferredReaders[] to look for the\n// deferred lock.  The Token-less version of unlock_shared() works in all\n// cases, but is optimized for the common (no inter-thread handoff) case.\n//\n// In both read- and write-priority mode, a waiting lock() (exclusive mode)\n// only blocks readers after it has waited for an active upgrade lock to be\n// released; until the upgrade lock is released (or upgraded or downgraded)\n// readers will still be able to enter.  Preferences about lock acquisition\n// are not guaranteed to be enforced perfectly (even if they were, there\n// is theoretically the chance that a thread could be arbitrarily suspended\n// between calling lock() and SharedMutex code actually getting executed).\n//\n// try_*_for methods always try at least once, even if the duration\n// is zero or negative.  The duration type must be compatible with\n// std::chrono::steady_clock.  try_*_until methods also always try at\n// least once.  std::chrono::system_clock and std::chrono::steady_clock\n// are supported.\n//\n// If you have observed by profiling that your SharedMutex-s are getting\n// cache misses on deferredReaders[] due to another SharedMutex user, then\n// you can use the tag type to create your own instantiation of the type.\n// The contention threshold (see kNumSharedToStartDeferring) should make\n// this unnecessary in all but the most extreme cases.  Make sure to check\n// that the increased icache and dcache footprint of the tagged result is\n// worth it.\n\n// SharedMutex's use of thread local storage is an optimization, so\n// for the case where thread local storage is not supported, define it\n// away.\n\n// Note about TSAN (ThreadSanitizer): the SharedMutexWritePriority version\n// (the default) of this mutex is annotated appropriately so that TSAN can\n// perform lock inversion analysis. However, the SharedMutexReadPriority version\n// is not annotated.  This is because TSAN's lock order heuristic\n// assumes that two calls to lock_shared must be ordered, which leads\n// to too many false positives for the reader-priority case.\n//\n// Suppose thread A holds a SharedMutexWritePriority lock in shared mode and an\n// independent thread B is waiting for exclusive access. Then a thread C's\n// lock_shared can't proceed until A has released the lock. Discounting\n// situations that never use exclusive mode (so no lock is necessary at all)\n// this means that without higher-level reasoning it is not safe to ignore\n// reader <-> reader interactions.\n//\n// This reasoning does not apply to SharedMutexReadPriority, because there are\n// no actions by a thread B that can make C need to wait for A. Since the\n// overwhelming majority of SharedMutex instances use write priority, we\n// restrict the TSAN annotations to only SharedMutexWritePriority.\n\nnamespace folly {\n\nstruct SharedMutexToken {\n  enum class State : uint16_t {\n    Invalid = 0,\n    LockedShared, // May be inline or deferred.\n    LockedInlineShared,\n    LockedDeferredShared,\n  };\n\n  State state_{};\n  uint16_t slot_{};\n\n  constexpr SharedMutexToken() = default;\n\n  explicit operator bool() const { return state_ != State::Invalid; }\n};\n\nstruct SharedMutexPolicyDefault {\n  static constexpr uint64_t max_spin_cycles = 4000;\n  static constexpr uint32_t max_soft_yield_count = 1;\n  static constexpr bool track_thread_id = false;\n  static constexpr bool skip_annotate_rwlock = false;\n};\n\nnamespace shared_mutex_detail {\n\nstruct PolicyTracked : SharedMutexPolicyDefault {\n  static constexpr bool track_thread_id = true;\n};\nstruct PolicySuppressTSAN : SharedMutexPolicyDefault {\n  static constexpr bool skip_annotate_rwlock = true;\n};\n\n// Returns a guard that gives permission for the current thread to\n// annotate, and adjust the annotation bits in, the SharedMutex at ptr.\nstd::unique_lock<std::mutex> annotationGuard(void* ptr);\n\nconstexpr uint32_t kMaxDeferredReadersAllocated = 256 * 2;\n\n[[FOLLY_ATTR_GNU_COLD]] uint32_t getMaxDeferredReadersSlow(\n    relaxed_atomic<uint32_t>& cache);\n\nlong getCurrentThreadInvoluntaryContextSwitchCount();\n\n// kMaxDeferredReaders\nFOLLY_EXPORT FOLLY_ALWAYS_INLINE uint32_t getMaxDeferredReaders() {\n  static relaxed_atomic<uint32_t> cache{0};\n  uint32_t const value = cache;\n  return FOLLY_LIKELY(!!value) ? value : getMaxDeferredReadersSlow(cache);\n}\n\nclass NopOwnershipTracker {\n public:\n  void beginThreadOwnership() {}\n\n  void maybeBeginThreadOwnership(bool) {}\n\n  void endThreadOwnership() {}\n};\n\nclass ThreadIdOwnershipTracker {\n public:\n  void beginThreadOwnership() {\n    assert(ownerTid_ == 0);\n    ownerTid_ = tid();\n  }\n\n  void maybeBeginThreadOwnership(bool own) {\n    if (own) {\n      beginThreadOwnership();\n    }\n  }\n\n  void endThreadOwnership() {\n    // if you want to check that unlock happens on the same thread as lock,\n    // assert that ownerTid_ == tid() here\n    ownerTid_ = 0;\n  }\n\n private:\n  static unsigned tid() {\n    /* library-local */ static thread_local unsigned cached = 0;\n    auto z = cached;\n    if (z == 0) {\n      z = static_cast<unsigned>(getOSThreadID());\n      cached = z;\n    }\n    return z;\n  }\n\n private:\n  // gettid() of thread holding the lock in U or E mode\n  unsigned ownerTid_ = 0;\n};\n} // namespace shared_mutex_detail\n\ntemplate <\n    bool ReaderPriority,\n    typename Tag_ = void,\n    template <typename> class Atom = std::atomic,\n    typename Policy = SharedMutexPolicyDefault>\nclass SharedMutexImpl\n    : std::conditional_t<\n          Policy::track_thread_id,\n          shared_mutex_detail::ThreadIdOwnershipTracker,\n          shared_mutex_detail::NopOwnershipTracker> {\n private:\n  static constexpr bool AnnotateForThreadSanitizer =\n      kIsSanitizeThread && !ReaderPriority && !Policy::skip_annotate_rwlock;\n\n  typedef std::conditional_t<\n      Policy::track_thread_id,\n      shared_mutex_detail::ThreadIdOwnershipTracker,\n      shared_mutex_detail::NopOwnershipTracker>\n      OwnershipTrackerBase;\n\n public:\n  static constexpr bool kReaderPriority = ReaderPriority;\n  typedef Tag_ Tag;\n\n  typedef SharedMutexToken Token;\n\n  constexpr SharedMutexImpl() noexcept : state_(0) {}\n\n  SharedMutexImpl(const SharedMutexImpl&) = delete;\n  SharedMutexImpl(SharedMutexImpl&&) = delete;\n  SharedMutexImpl& operator=(const SharedMutexImpl&) = delete;\n  SharedMutexImpl& operator=(SharedMutexImpl&&) = delete;\n\n  // It is an error to destroy an SharedMutex that still has\n  // any outstanding locks.  This is checked if NDEBUG isn't defined.\n  // SharedMutex's exclusive mode can be safely used to guard the lock's\n  // own destruction.  If, for example, you acquire the lock in exclusive\n  // mode and then observe that the object containing the lock is no longer\n  // needed, you can unlock() and then immediately destroy the lock.\n  // See https://sourceware.org/bugzilla/show_bug.cgi?id=13690 for a\n  // description about why this property needs to be explicitly mentioned.\n  ~SharedMutexImpl() {\n    auto state = state_.load(std::memory_order_relaxed);\n    if (FOLLY_UNLIKELY((state & kHasS) != 0)) {\n      cleanupTokenlessSharedDeferred(state);\n    }\n\n    if (folly::kIsDebug) {\n      // These asserts check that everybody has released the lock before it\n      // is destroyed.  If you arrive here while debugging that is likely\n      // the problem.  (You could also have general heap corruption.)\n\n      // if a futexWait fails to go to sleep because the value has been\n      // changed, we don't necessarily clean up the wait bits, so it is\n      // possible they will be set here in a correct system\n      assert((state & ~(kWaitingAny | kMayDefer | kAnnotationCreated)) == 0);\n      if ((state & kMayDefer) != 0) {\n        const uint32_t maxDeferredReaders =\n            shared_mutex_detail::getMaxDeferredReaders();\n        for (uint32_t slot = 0; slot < maxDeferredReaders; ++slot) {\n          auto slotValue =\n              deferredReader(slot)->load(std::memory_order_relaxed);\n          assert(!slotValueIsThis(slotValue));\n          (void)slotValue;\n        }\n      }\n    }\n    annotateDestroy();\n  }\n\n  // Checks if an exclusive lock could succeed so that lock elision could be\n  // enabled. Different from the two eligible_for_lock_{upgrade|shared}_elision\n  // functions, this is a conservative check since kMayDefer indicates\n  // \"may-existing\" deferred readers.\n  bool eligible_for_lock_elision() const {\n    // We rely on the transaction for linearization.  Wait bits are\n    // irrelevant because a successful transaction will be in and out\n    // without affecting the wakeup.  kBegunE is also okay for a similar\n    // reason.\n    auto state = state_.load(std::memory_order_relaxed);\n    return (state & (kHasS | kMayDefer | kHasE | kHasU)) == 0;\n  }\n\n  // Checks if an upgrade lock could succeed so that lock elision could be\n  // enabled.\n  bool eligible_for_lock_upgrade_elision() const {\n    auto state = state_.load(std::memory_order_relaxed);\n    return (state & (kHasE | kHasU)) == 0;\n  }\n\n  // Checks if a shared lock could succeed so that lock elision could be\n  // enabled.\n  bool eligible_for_lock_shared_elision() const {\n    // No need to honor kBegunE because a transaction doesn't block anybody\n    auto state = state_.load(std::memory_order_relaxed);\n    return (state & kHasE) == 0;\n  }\n\n  void lock() {\n    WaitForever ctx;\n    (void)lockExclusiveImpl(kHasSolo, ctx);\n    OwnershipTrackerBase::beginThreadOwnership();\n    annotateAcquired(annotate_rwlock_level::wrlock);\n  }\n\n  bool try_lock() {\n    WaitNever ctx;\n    auto result = lockExclusiveImpl(kHasSolo, ctx);\n    OwnershipTrackerBase::maybeBeginThreadOwnership(result);\n    annotateTryAcquired(result, annotate_rwlock_level::wrlock);\n    return result;\n  }\n\n  template <class Rep, class Period>\n  bool try_lock_for(const std::chrono::duration<Rep, Period>& duration) {\n    WaitForDuration<Rep, Period> ctx(duration);\n    auto result = lockExclusiveImpl(kHasSolo, ctx);\n    OwnershipTrackerBase::maybeBeginThreadOwnership(result);\n    annotateTryAcquired(result, annotate_rwlock_level::wrlock);\n    return result;\n  }\n\n  template <class Clock, class Duration>\n  bool try_lock_until(\n      const std::chrono::time_point<Clock, Duration>& absDeadline) {\n    WaitUntilDeadline<Clock, Duration> ctx{absDeadline};\n    auto result = lockExclusiveImpl(kHasSolo, ctx);\n    OwnershipTrackerBase::maybeBeginThreadOwnership(result);\n    annotateTryAcquired(result, annotate_rwlock_level::wrlock);\n    return result;\n  }\n\n  void unlock() {\n    annotateReleased(annotate_rwlock_level::wrlock);\n    OwnershipTrackerBase::endThreadOwnership();\n    // It is possible that we have a left-over kWaitingNotS if the last\n    // unlock_shared() that let our matching lock() complete finished\n    // releasing before lock()'s futexWait went to sleep.  Clean it up now\n    auto state = (state_ &= ~(kWaitingNotS | kPrevDefer | kHasE));\n    assert((state & ~(kWaitingAny | kAnnotationCreated)) == 0);\n    wakeRegisteredWaiters(state, kWaitingE | kWaitingU | kWaitingS);\n  }\n\n  // Managing the token yourself makes unlock_shared a bit faster. If the\n  // tokenful version of lock_shared() is used, then it is required to pair the\n  // lock with the tokenful version of unlock_shared(); alternatively, the token\n  // can be invalidated with release_token(), which allows to use the tokenless\n  // unlock_shared().\n\n  void lock_shared() {\n    WaitForever ctx;\n    (void)lockSharedImpl(nullptr, ctx);\n    annotateAcquired(annotate_rwlock_level::rdlock);\n  }\n\n  void lock_shared(Token& token) {\n    WaitForever ctx;\n    (void)lockSharedImpl(&token, ctx);\n    annotateAcquired(annotate_rwlock_level::rdlock);\n  }\n\n  bool try_lock_shared() {\n    WaitNever ctx;\n    auto result = lockSharedImpl(nullptr, ctx);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  bool try_lock_shared(Token& token) {\n    WaitNever ctx;\n    auto result = lockSharedImpl(&token, ctx);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  template <class Rep, class Period>\n  bool try_lock_shared_for(const std::chrono::duration<Rep, Period>& duration) {\n    WaitForDuration<Rep, Period> ctx(duration);\n    auto result = lockSharedImpl(nullptr, ctx);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  template <class Rep, class Period>\n  bool try_lock_shared_for(\n      const std::chrono::duration<Rep, Period>& duration, Token& token) {\n    WaitForDuration<Rep, Period> ctx(duration);\n    auto result = lockSharedImpl(&token, ctx);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  template <class Clock, class Duration>\n  bool try_lock_shared_until(\n      const std::chrono::time_point<Clock, Duration>& absDeadline) {\n    WaitUntilDeadline<Clock, Duration> ctx{absDeadline};\n    auto result = lockSharedImpl(nullptr, ctx);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  template <class Clock, class Duration>\n  bool try_lock_shared_until(\n      const std::chrono::time_point<Clock, Duration>& absDeadline,\n      Token& token) {\n    WaitUntilDeadline<Clock, Duration> ctx{absDeadline};\n    auto result = lockSharedImpl(&token, ctx);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  void unlock_shared() {\n    annotateReleased(annotate_rwlock_level::rdlock);\n\n    auto state = state_.load(std::memory_order_acquire);\n\n    // kPrevDefer can only be set if HasE or BegunE is set\n    assert((state & (kPrevDefer | kHasE | kBegunE)) != kPrevDefer);\n\n    // lock() strips kMayDefer immediately, but then copies it to\n    // kPrevDefer so we can tell if the pre-lock() lock_shared() might\n    // have deferred\n    if ((state & (kMayDefer | kPrevDefer)) == 0 ||\n        !tryUnlockTokenlessSharedDeferred()) {\n      // Matching lock_shared() couldn't have deferred, or the deferred\n      // lock has already been inlined by applyDeferredReaders()\n      unlockSharedInline();\n    }\n  }\n\n  void unlock_shared(Token& token) {\n    if (token.state_ == Token::State::LockedShared) {\n      unlock_shared();\n      if (folly::kIsDebug) {\n        token.state_ = Token::State::Invalid;\n      }\n      return;\n    }\n\n    annotateReleased(annotate_rwlock_level::rdlock);\n\n    assert(\n        token.state_ == Token::State::LockedInlineShared ||\n        token.state_ == Token::State::LockedDeferredShared);\n\n    if (token.state_ != Token::State::LockedDeferredShared ||\n        !tryUnlockSharedDeferred(token.slot_)) {\n      unlockSharedInline();\n    }\n    if (folly::kIsDebug) {\n      token.state_ = Token::State::Invalid;\n    }\n  }\n\n  // Invalidates the given token so that the tokenless version of\n  // unlock_shared() can be called for a lock that was obtained from a tokenful\n  // lock_shared(). Note that this does not unlock the mutex at any point.\n  void release_token(Token& token) {\n    assert(token.state_ != Token::State::Invalid);\n    if (token.state_ != Token::State::LockedDeferredShared) {\n      return;\n    }\n\n    auto slot = token.slot_;\n    assert(slot < shared_mutex_detail::getMaxDeferredReaders());\n    auto slotValue = tokenfulSlotValue();\n    // Lock may have been inlined, in which case this will return false. We\n    // don't need to do anything in this case.\n    deferredReader(slot)->compare_exchange_strong(\n        slotValue, tokenlessSlotValue());\n\n    if (folly::kIsDebug) {\n      token.state_ = Token::State::Invalid;\n    }\n  }\n\n  void unlock_and_lock_shared() {\n    OwnershipTrackerBase::endThreadOwnership();\n    annotateReleased(annotate_rwlock_level::wrlock);\n    annotateAcquired(annotate_rwlock_level::rdlock);\n    // We can't use state_ -=, because we need to clear 2 bits (1 of which\n    // has an uncertain initial state) and set 1 other.  We might as well\n    // clear the relevant wake bits at the same time.  Note that since S\n    // doesn't block the beginning of a transition to E (writer priority\n    // can cut off new S, reader priority grabs BegunE and blocks deferred\n    // S) we need to wake E as well.\n    auto state = state_.load(std::memory_order_acquire);\n    do {\n      assert(\n          (state & ~(kWaitingAny | kPrevDefer | kAnnotationCreated)) == kHasE);\n    } while (!state_.compare_exchange_strong(\n        state, (state & ~(kWaitingAny | kPrevDefer | kHasE)) + kIncrHasS));\n    if ((state & (kWaitingE | kWaitingU | kWaitingS)) != 0) {\n      futexWakeAll(kWaitingE | kWaitingU | kWaitingS);\n    }\n  }\n\n  void unlock_and_lock_shared(Token& token) {\n    unlock_and_lock_shared();\n    token.state_ = Token::State::LockedInlineShared;\n  }\n\n  void lock_upgrade() {\n    WaitForever ctx;\n    (void)lockUpgradeImpl(ctx);\n    OwnershipTrackerBase::beginThreadOwnership();\n    // For TSAN: treat upgrade locks as equivalent to read locks\n    annotateAcquired(annotate_rwlock_level::rdlock);\n  }\n\n  bool try_lock_upgrade() {\n    WaitNever ctx;\n    auto result = lockUpgradeImpl(ctx);\n    OwnershipTrackerBase::maybeBeginThreadOwnership(result);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  template <class Rep, class Period>\n  bool try_lock_upgrade_for(\n      const std::chrono::duration<Rep, Period>& duration) {\n    WaitForDuration<Rep, Period> ctx(duration);\n    auto result = lockUpgradeImpl(ctx);\n    OwnershipTrackerBase::maybeBeginThreadOwnership(result);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  template <class Clock, class Duration>\n  bool try_lock_upgrade_until(\n      const std::chrono::time_point<Clock, Duration>& absDeadline) {\n    WaitUntilDeadline<Clock, Duration> ctx{absDeadline};\n    auto result = lockUpgradeImpl(ctx);\n    OwnershipTrackerBase::maybeBeginThreadOwnership(result);\n    annotateTryAcquired(result, annotate_rwlock_level::rdlock);\n    return result;\n  }\n\n  void unlock_upgrade() {\n    annotateReleased(annotate_rwlock_level::rdlock);\n    OwnershipTrackerBase::endThreadOwnership();\n    auto state = (state_ -= kHasU);\n    assert((state & (kWaitingNotS | kHasSolo)) == 0);\n    wakeRegisteredWaiters(state, kWaitingE | kWaitingU);\n  }\n\n  void unlock_upgrade_and_lock() {\n    // no waiting necessary, so waitMask is empty\n    WaitForever ctx;\n    (void)lockExclusiveImpl(0, ctx);\n    annotateReleased(annotate_rwlock_level::rdlock);\n    annotateAcquired(annotate_rwlock_level::wrlock);\n  }\n\n  void unlock_upgrade_and_lock_shared() {\n    // No need to annotate for TSAN here because we model upgrade and shared\n    // locks as the same.\n    OwnershipTrackerBase::endThreadOwnership();\n    auto state = (state_ -= kHasU - kIncrHasS);\n    assert((state & (kWaitingNotS | kHasSolo)) == 0);\n    wakeRegisteredWaiters(state, kWaitingE | kWaitingU);\n  }\n\n  void unlock_upgrade_and_lock_shared(Token& token) {\n    unlock_upgrade_and_lock_shared();\n    token.state_ = Token::State::LockedInlineShared;\n  }\n\n  void unlock_and_lock_upgrade() {\n    annotateReleased(annotate_rwlock_level::wrlock);\n    annotateAcquired(annotate_rwlock_level::rdlock);\n    // We can't use state_ -=, because we need to clear 2 bits (1 of\n    // which has an uncertain initial state) and set 1 other.  We might\n    // as well clear the relevant wake bits at the same time.\n    auto state = state_.load(std::memory_order_acquire);\n    while (true) {\n      assert(\n          (state & ~(kWaitingAny | kPrevDefer | kAnnotationCreated)) == kHasE);\n      auto after =\n          (state & ~(kWaitingNotS | kWaitingS | kPrevDefer | kHasE)) + kHasU;\n      if (state_.compare_exchange_strong(state, after)) {\n        if ((state & kWaitingS) != 0) {\n          futexWakeAll(kWaitingS);\n        }\n        return;\n      }\n    }\n  }\n\n private:\n  typedef typename folly::detail::Futex<Atom> Futex;\n\n  // Internally we use four kinds of wait contexts.  These are structs\n  // that provide a doWait method that returns true if a futex wake\n  // was issued that intersects with the waitMask, false if there was a\n  // timeout and no more waiting should be performed.  Spinning occurs\n  // before the wait context is invoked.\n\n  struct WaitForever {\n    bool canBlock() { return true; }\n    bool canTimeOut() { return false; }\n    bool shouldTimeOut() { return false; }\n\n    bool doWait(Futex& futex, uint32_t expected, uint32_t waitMask) {\n      detail::futexWait(&futex, expected, waitMask);\n      return true;\n    }\n  };\n\n  struct WaitNever {\n    bool canBlock() { return false; }\n    bool canTimeOut() { return true; }\n    bool shouldTimeOut() { return true; }\n\n    bool doWait(\n        Futex& /* futex */, uint32_t /* expected */, uint32_t /* waitMask */) {\n      return false;\n    }\n  };\n\n  template <class Rep, class Period>\n  struct WaitForDuration {\n    std::chrono::duration<Rep, Period> duration_;\n    bool deadlineComputed_;\n    std::chrono::steady_clock::time_point deadline_;\n\n    explicit WaitForDuration(const std::chrono::duration<Rep, Period>& duration)\n        : duration_(duration), deadlineComputed_(false) {}\n\n    std::chrono::steady_clock::time_point deadline() {\n      if (!deadlineComputed_) {\n        deadline_ = std::chrono::steady_clock::now() + duration_;\n        deadlineComputed_ = true;\n      }\n      return deadline_;\n    }\n\n    bool canBlock() { return duration_.count() > 0; }\n    bool canTimeOut() { return true; }\n\n    bool shouldTimeOut() {\n      return std::chrono::steady_clock::now() > deadline();\n    }\n\n    bool doWait(Futex& futex, uint32_t expected, uint32_t waitMask) {\n      auto result =\n          detail::futexWaitUntil(&futex, expected, deadline(), waitMask);\n      return result != folly::detail::FutexResult::TIMEDOUT;\n    }\n  };\n\n  template <class Clock, class Duration>\n  struct WaitUntilDeadline {\n    std::chrono::time_point<Clock, Duration> absDeadline_;\n\n    bool canBlock() { return true; }\n    bool canTimeOut() { return true; }\n    bool shouldTimeOut() { return Clock::now() > absDeadline_; }\n\n    bool doWait(Futex& futex, uint32_t expected, uint32_t waitMask) {\n      auto result =\n          detail::futexWaitUntil(&futex, expected, absDeadline_, waitMask);\n      return result != folly::detail::FutexResult::TIMEDOUT;\n    }\n  };\n\n  void annotateLazyCreate() {\n    if (AnnotateForThreadSanitizer &&\n        (state_.load() & kAnnotationCreated) == 0) {\n      auto guard = shared_mutex_detail::annotationGuard(this);\n      // check again\n      if ((state_.load() & kAnnotationCreated) == 0) {\n        state_.fetch_or(kAnnotationCreated);\n        annotate_benign_race_sized(\n            &state_, sizeof(state_), \"init TSAN\", __FILE__, __LINE__);\n        annotate_rwlock_create(this, __FILE__, __LINE__);\n      }\n    }\n  }\n\n  void annotateDestroy() {\n    if (AnnotateForThreadSanitizer) {\n      // call destroy only if the annotation was created\n      if (state_.load() & kAnnotationCreated) {\n        annotate_rwlock_destroy(this, __FILE__, __LINE__);\n      }\n    }\n  }\n\n  void annotateAcquired(annotate_rwlock_level w) {\n    if (AnnotateForThreadSanitizer) {\n      annotateLazyCreate();\n      annotate_rwlock_acquired(this, w, __FILE__, __LINE__);\n    }\n  }\n\n  void annotateTryAcquired(bool result, annotate_rwlock_level w) {\n    if (AnnotateForThreadSanitizer) {\n      annotateLazyCreate();\n      annotate_rwlock_try_acquired(this, w, result, __FILE__, __LINE__);\n    }\n  }\n\n  void annotateReleased(annotate_rwlock_level w) {\n    if (AnnotateForThreadSanitizer) {\n      assert((state_.load() & kAnnotationCreated) != 0);\n      annotate_rwlock_released(this, w, __FILE__, __LINE__);\n    }\n  }\n\n  // 32 bits of state\n  Futex state_{};\n\n  // S count needs to be on the end, because we explicitly allow it to\n  // underflow.  This can occur while we are in the middle of applying\n  // deferred locks (we remove them from deferredReaders[] before\n  // inlining them), or during token-less unlock_shared() if a racing\n  // lock_shared();unlock_shared() moves the deferredReaders slot while\n  // the first unlock_shared() is scanning.  The former case is cleaned\n  // up before we finish applying the locks.  The latter case can persist\n  // until destruction, when it is cleaned up.\n  static constexpr uint32_t kIncrHasS = 1 << 11;\n  static constexpr uint32_t kHasS = ~(kIncrHasS - 1);\n\n  // Set if annotation has been completed for this instance.  That annotation\n  // (and setting this bit afterward) must be guarded by one of the mutexes in\n  // annotationCreationGuards.\n  static constexpr uint32_t kAnnotationCreated = 1 << 10;\n\n  // If false, then there are definitely no deferred read locks for this\n  // instance.  Cleared after initialization and when exclusively locked.\n  static constexpr uint32_t kMayDefer = 1 << 9;\n\n  // lock() cleared kMayDefer as soon as it starts draining readers (so\n  // that it doesn't have to do a second CAS once drain completes), but\n  // unlock_shared() still needs to know whether to scan deferredReaders[]\n  // or not.  We copy kMayDefer to kPrevDefer when setting kHasE or\n  // kBegunE, and clear it when clearing those bits.\n  static constexpr uint32_t kPrevDefer = 1 << 8;\n\n  // Exclusive-locked blocks all read locks and write locks.  This bit\n  // may be set before all readers have finished, but in that case the\n  // thread that sets it won't return to the caller until all read locks\n  // have been released.\n  static constexpr uint32_t kHasE = 1 << 7;\n\n  // Exclusive-draining means that lock() is waiting for existing readers\n  // to leave, but that new readers may still acquire shared access.\n  // This is only used in reader priority mode.  New readers during\n  // drain must be inline.  The difference between this and kHasU is that\n  // kBegunE prevents kMayDefer from being set.\n  static constexpr uint32_t kBegunE = 1 << 6;\n\n  // At most one thread may have either exclusive or upgrade lock\n  // ownership.  Unlike exclusive mode, ownership of the lock in upgrade\n  // mode doesn't preclude other threads holding the lock in shared mode.\n  // boost's concept for this doesn't explicitly say whether new shared\n  // locks can be acquired one lock_upgrade has succeeded, but doesn't\n  // list that as disallowed.  RWSpinLock disallows new read locks after\n  // lock_upgrade has been acquired, but the boost implementation doesn't.\n  // We choose the latter.\n  static constexpr uint32_t kHasU = 1 << 5;\n\n  // There are three states that we consider to be \"solo\", in that they\n  // cannot coexist with other solo states.  These are kHasE, kBegunE,\n  // and kHasU.  Note that S doesn't conflict with any of these, because\n  // setting the kHasE is only one of the two steps needed to actually\n  // acquire the lock in exclusive mode (the other is draining the existing\n  // S holders).\n  static constexpr uint32_t kHasSolo = kHasE | kBegunE | kHasU;\n\n  // Once a thread sets kHasE it needs to wait for the current readers\n  // to exit the lock.  We give this a separate wait identity from the\n  // waiting to set kHasE so that we can perform partial wakeups (wake\n  // one instead of wake all).\n  static constexpr uint32_t kWaitingNotS = 1 << 4;\n\n  // When waking writers we can either wake them all, in which case we\n  // can clear kWaitingE, or we can call futexWake(1).  futexWake tells\n  // us if anybody woke up, but even if we detect that nobody woke up we\n  // can't clear the bit after the fact without issuing another wakeup.\n  // To avoid thundering herds when there are lots of pending lock()\n  // without needing to call futexWake twice when there is only one\n  // waiter, kWaitingE actually encodes if we have observed multiple\n  // concurrent waiters.  Tricky: ABA issues on futexWait mean that when\n  // we see kWaitingESingle we can't assume that there is only one.\n  static constexpr uint32_t kWaitingESingle = 1 << 2;\n  static constexpr uint32_t kWaitingEMultiple = 1 << 3;\n  static constexpr uint32_t kWaitingE = kWaitingESingle | kWaitingEMultiple;\n\n  // kWaitingU is essentially a 1 bit saturating counter.  It always\n  // requires a wakeAll.\n  static constexpr uint32_t kWaitingU = 1 << 1;\n\n  // All blocked lock_shared() should be awoken, so it is correct (not\n  // suboptimal) to wakeAll if there are any shared readers.\n  static constexpr uint32_t kWaitingS = 1 << 0;\n\n  // kWaitingAny is a mask of all of the bits that record the state of\n  // threads, rather than the state of the lock.  It is convenient to be\n  // able to mask them off during asserts.\n  static constexpr uint32_t kWaitingAny =\n      kWaitingNotS | kWaitingE | kWaitingU | kWaitingS;\n\n  // The reader count at which a reader will attempt to use the lock\n  // in deferred mode.  If this value is 2, then the second concurrent\n  // reader will set kMayDefer and use deferredReaders[].  kMayDefer is\n  // cleared during exclusive access, so this threshold must be reached\n  // each time a lock is held in exclusive mode.\n  static constexpr uint32_t kNumSharedToStartDeferring = 2;\n\n  // Maximum time in cycles a thread will spin waiting for a state transition.\n  static constexpr uint64_t kMaxSpinCycles = Policy::max_spin_cycles;\n\n  // The maximum number of soft yields before falling back to futex.\n  // If the preemption heuristic is activated we will fall back before\n  // this.  A soft yield takes ~900 nanos (two sched_yield plus a call\n  // to getrusage, with checks of the goal at each step).  Soft yields\n  // aren't compatible with deterministic execution under test (unlike\n  // futexWaitUntil, which has a capricious but deterministic back end).\n  static constexpr uint32_t kMaxSoftYieldCount = Policy::max_soft_yield_count;\n\n  // If AccessSpreader assigns indexes from 0..k*n-1 on a system where some\n  // level of the memory hierarchy is symmetrically divided into k pieces\n  // (NUMA nodes, last-level caches, L1 caches, ...), then slot indexes\n  // that are the same after integer division by k share that resource.\n  // Our strategy for deferred readers is to probe up to numSlots/4 slots,\n  // using the full granularity of AccessSpreader for the start slot\n  // and then search outward.\n  //\n  // In order to give each L1 cache its own playground, we need\n  // kMaxDeferredReaders >= #L1 caches. We double it, making it\n  // essentially the number of cores, so it doesn't easily run\n  // out of deferred reader slots and start inlining the readers.\n  // We do not know the number of cores at compile time, as the code\n  // can be compiled from different server types than the one running\n  // the service. So we allocate the static storage large enough to\n  // hold all the slots (256).\n  //\n  // On x86_64 each DeferredReaderSlot is 8 bytes, so we need\n  // kMaxDeferredReaders\n  // * kDeferredSeparationFactor >= 64 * #L1 caches / 8 == 128.  If\n  // kDeferredSearchDistance * kDeferredSeparationFactor <=\n  // 64 / 8 then we will search only within a single cache line, which\n  // guarantees we won't have inter-L1 contention.\n public:\n  static constexpr uint32_t kDeferredSearchDistance = 2;\n  static constexpr uint32_t kDeferredSeparationFactor = 4;\n\n private:\n  static_assert(\n      !(kDeferredSearchDistance & (kDeferredSearchDistance - 1)),\n      \"kDeferredSearchDistance must be a power of 2\");\n\n  // We need to make sure that if there is a lock_shared()\n  // and lock_shared(token) followed by unlock_shared() and\n  // unlock_shared(token), the token-less unlock doesn't null\n  // out deferredReaders[token.slot_].  If we allowed that, then\n  // unlock_shared(token) wouldn't be able to assume that its lock\n  // had been inlined by applyDeferredReaders when it finds that\n  // deferredReaders[token.slot_] no longer points to this.  We accomplish\n  // this by stealing bit 0 from the pointer to record that the slot's\n  // element has no token, hence our use of uintptr_t in deferredReaders[].\n  static constexpr uintptr_t kTokenless = 0x1;\n\n  // This is the starting location for Token-less unlock_shared().\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static relaxed_atomic<uint32_t>&\n  tls_lastTokenlessSlot() {\n    static relaxed_atomic<uint32_t> non_tl{};\n    static thread_local relaxed_atomic<uint32_t> tl{};\n    return kIsMobile ? non_tl : tl;\n  }\n\n  // Last deferred reader slot used.\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static relaxed_atomic<uint32_t>&\n  tls_lastDeferredReaderSlot() {\n    static relaxed_atomic<uint32_t> non_tl{};\n    static thread_local relaxed_atomic<uint32_t> tl{};\n    return kIsMobile ? non_tl : tl;\n  }\n\n  // Only indexes divisible by kDeferredSeparationFactor are used.\n  // If any of those elements points to a SharedMutexImpl, then it\n  // should be considered that there is a shared lock on that instance.\n  // See kTokenless.\n public:\n  typedef Atom<uintptr_t> DeferredReaderSlot;\n\n private:\n  alignas(hardware_destructive_interference_size) static DeferredReaderSlot\n      deferredReaders\n          [shared_mutex_detail::kMaxDeferredReadersAllocated *\n           kDeferredSeparationFactor];\n\n  // Performs an exclusive lock, waiting for state_ & waitMask to be\n  // zero first\n  template <class WaitContext>\n  bool lockExclusiveImpl(uint32_t preconditionGoalMask, WaitContext& ctx) {\n    uint32_t state = state_.load(std::memory_order_acquire);\n    if (FOLLY_LIKELY(\n            (state & (preconditionGoalMask | kMayDefer | kHasS)) == 0 &&\n            state_.compare_exchange_strong(state, (state | kHasE) & ~kHasU))) {\n      return true;\n    } else {\n      return lockExclusiveImpl(state, preconditionGoalMask, ctx);\n    }\n  }\n\n  template <class WaitContext>\n  bool lockExclusiveImpl(\n      uint32_t& state, uint32_t preconditionGoalMask, WaitContext& ctx) {\n    while (true) {\n      if (FOLLY_UNLIKELY((state & preconditionGoalMask) != 0) &&\n          !waitForZeroBits(state, preconditionGoalMask, kWaitingE, ctx) &&\n          ctx.canTimeOut()) {\n        return false;\n      }\n\n      uint32_t after = (state & kMayDefer) == 0 ? 0 : kPrevDefer;\n      if (!kReaderPriority || (state & (kMayDefer | kHasS)) == 0) {\n        // Block readers immediately, either because we are in write\n        // priority mode or because we can acquire the lock in one\n        // step.  Note that if state has kHasU, then we are doing an\n        // unlock_upgrade_and_lock() and we should clear it (reader\n        // priority branch also does this).\n        after |= (state | kHasE) & ~(kHasU | kMayDefer);\n      } else {\n        after |= (state | kBegunE) & ~(kHasU | kMayDefer);\n      }\n      if (state_.compare_exchange_strong(state, after)) {\n        auto before = state;\n        state = after;\n\n        // If we set kHasE (writer priority) then no new readers can\n        // arrive.  If we set kBegunE then they can still enter, but\n        // they must be inline.  Either way we need to either spin on\n        // deferredReaders[] slots, or inline them so that we can wait on\n        // kHasS to zero itself.  deferredReaders[] is pointers, which on\n        // x86_64 are bigger than futex() can handle, so we inline the\n        // deferred locks instead of trying to futexWait on each slot.\n        // Readers are responsible for rechecking state_ after recording\n        // a deferred read to avoid atomicity problems between the state_\n        // CAS and applyDeferredReader's reads of deferredReaders[].\n        if (FOLLY_UNLIKELY((before & kMayDefer) != 0)) {\n          applyDeferredReaders(state, ctx);\n        }\n        while (true) {\n          assert((state & (kHasE | kBegunE)) != 0 && (state & kHasU) == 0);\n          if (FOLLY_UNLIKELY((state & kHasS) != 0) &&\n              !waitForZeroBits(state, kHasS, kWaitingNotS, ctx) &&\n              ctx.canTimeOut()) {\n            // Ugh.  We blocked new readers and other writers for a while,\n            // but were unable to complete.  Move on.  On the plus side\n            // we can clear kWaitingNotS because nobody else can piggyback\n            // on it.\n            state = (state_ &= ~(kPrevDefer | kHasE | kBegunE | kWaitingNotS));\n            wakeRegisteredWaiters(state, kWaitingE | kWaitingU | kWaitingS);\n            return false;\n          }\n\n          if (kReaderPriority && (state & kHasE) == 0) {\n            assert((state & kBegunE) != 0);\n            if (!state_.compare_exchange_strong(\n                    state, (state & ~kBegunE) | kHasE)) {\n              continue;\n            }\n          }\n\n          return true;\n        }\n      }\n    }\n  }\n\n  template <class WaitContext>\n  bool waitForZeroBits(\n      uint32_t& state, uint32_t goal, uint32_t waitMask, WaitContext& ctx) {\n    for (uint64_t start = hardware_timestamp();;) {\n      state = state_.load(std::memory_order_acquire);\n      if ((state & goal) == 0) {\n        return true;\n      }\n      const uint64_t elapsed = hardware_timestamp() - start;\n      // NOTE: This is also true if hardware_timestamp() goes back in time, as\n      // elapsed underflows.\n      if (FOLLY_UNLIKELY(elapsed >= kMaxSpinCycles)) {\n        return ctx.canBlock() &&\n            yieldWaitForZeroBits(state, goal, waitMask, ctx);\n      }\n      asm_volatile_pause();\n    }\n  }\n\n  template <class WaitContext>\n  bool yieldWaitForZeroBits(\n      uint32_t& state, uint32_t goal, uint32_t waitMask, WaitContext& ctx) {\n    long thread_nivcsw = 0;\n    long before = -1;\n    for (uint32_t yieldCount = 0; yieldCount < kMaxSoftYieldCount;\n         ++yieldCount) {\n      for (int softState = 0; softState < 3; ++softState) {\n        if (softState < 2) {\n          std::this_thread::yield();\n        } else {\n          thread_nivcsw = shared_mutex_detail::\n              getCurrentThreadInvoluntaryContextSwitchCount();\n        }\n        if (((state = state_.load(std::memory_order_acquire)) & goal) == 0) {\n          return true;\n        }\n        if (ctx.shouldTimeOut()) {\n          return false;\n        }\n      }\n      if (before >= 0 && thread_nivcsw >= before + 2) {\n        // One involuntary csw might just be occasional background work,\n        // but if we get two in a row then we guess that there is someone\n        // else who can profitably use this CPU.  Fall back to futex\n        break;\n      }\n      before = thread_nivcsw;\n    }\n    return futexWaitForZeroBits(state, goal, waitMask, ctx);\n  }\n\n  template <class WaitContext>\n  bool futexWaitForZeroBits(\n      uint32_t& state, uint32_t goal, uint32_t waitMask, WaitContext& ctx) {\n    assert(\n        waitMask == kWaitingNotS || waitMask == kWaitingE ||\n        waitMask == kWaitingU || waitMask == kWaitingS);\n\n    while (true) {\n      state = state_.load(std::memory_order_acquire);\n      if ((state & goal) == 0) {\n        return true;\n      }\n\n      auto after = state;\n      if (waitMask == kWaitingE) {\n        if ((state & kWaitingESingle) != 0) {\n          after |= kWaitingEMultiple;\n        } else {\n          after |= kWaitingESingle;\n        }\n      } else {\n        after |= waitMask;\n      }\n\n      // CAS is better than atomic |= here, because it lets us avoid\n      // setting the wait flag when the goal is concurrently achieved\n      if (after != state && !state_.compare_exchange_strong(state, after)) {\n        continue;\n      }\n\n      if (!ctx.doWait(state_, after, waitMask)) {\n        // timed out\n        return false;\n      }\n    }\n  }\n\n  // Wakes up waiters registered in state_ as appropriate, clearing the\n  // awaiting bits for anybody that was awoken.  Tries to perform direct\n  // single wakeup of an exclusive waiter if appropriate\n  void wakeRegisteredWaiters(uint32_t& state, uint32_t wakeMask) {\n    if (FOLLY_UNLIKELY((state & wakeMask) != 0)) {\n      wakeRegisteredWaitersImpl(state, wakeMask);\n    }\n  }\n\n  [[FOLLY_ATTR_GNU_USED]]\n  void wakeRegisteredWaitersImpl(uint32_t& state, uint32_t wakeMask) {\n    // If there are multiple lock() pending only one of them will actually\n    // get to wake up, so issuing futexWakeAll will make a thundering herd.\n    // There's nothing stopping us from issuing futexWake(1) instead,\n    // so long as the wait bits are still an accurate reflection of\n    // the waiters.  If we notice (via futexWake's return value) that\n    // nobody woke up then we can try again with the normal wake-all path.\n    // Note that we can't just clear the bits at that point; we need to\n    // clear the bits and then issue another wakeup.\n    //\n    // It is possible that we wake an E waiter but an outside S grabs the\n    // lock instead, at which point we should wake pending U and S waiters.\n    // Rather than tracking state to make the failing E regenerate the\n    // wakeup, we just disable the optimization in the case that there\n    // are waiting U or S that we are eligible to wake.\n    if ((wakeMask & kWaitingE) == kWaitingE &&\n        (state & wakeMask) == kWaitingE &&\n        detail::futexWake(&state_, 1, kWaitingE) > 0) {\n      // somebody woke up, so leave state_ as is and clear it later\n      return;\n    }\n\n    if ((state & wakeMask) != 0) {\n      auto prev = state_.fetch_and(~wakeMask);\n      if ((prev & wakeMask) != 0) {\n        futexWakeAll(wakeMask);\n      }\n      state = prev & ~wakeMask;\n    }\n  }\n\n  void futexWakeAll(uint32_t wakeMask) {\n    detail::futexWake(&state_, std::numeric_limits<int>::max(), wakeMask);\n  }\n\n  DeferredReaderSlot* deferredReader(uint32_t slot) {\n    return &deferredReaders[slot * kDeferredSeparationFactor];\n  }\n\n  uintptr_t tokenfulSlotValue() { return reinterpret_cast<uintptr_t>(this); }\n\n  uintptr_t tokenlessSlotValue() { return tokenfulSlotValue() | kTokenless; }\n\n  bool slotValueIsThis(uintptr_t slotValue) {\n    return (slotValue & ~kTokenless) == tokenfulSlotValue();\n  }\n\n  // Clears any deferredReaders[] that point to this, adjusting the inline\n  // shared lock count to compensate.  Does some spinning and yielding\n  // to avoid the work.  Always finishes the application, even if ctx\n  // times out.\n  template <class WaitContext>\n  void applyDeferredReaders(uint32_t& state, WaitContext& ctx) {\n    uint32_t slot = 0;\n\n    const uint32_t maxDeferredReaders =\n        shared_mutex_detail::getMaxDeferredReaders();\n    for (uint64_t start = hardware_timestamp();;) {\n      while (!slotValueIsThis(\n          deferredReader(slot)->load(std::memory_order_acquire))) {\n        if (++slot == maxDeferredReaders) {\n          return;\n        }\n      }\n      const uint64_t elapsed = hardware_timestamp() - start;\n      // NOTE: This is also true if hardware_timestamp() goes back in time, as\n      // elapsed underflows.\n      if (FOLLY_UNLIKELY(elapsed >= kMaxSpinCycles)) {\n        applyDeferredReaders(state, ctx, slot);\n        return;\n      }\n      asm_volatile_pause();\n    }\n  }\n\n  template <class WaitContext>\n  void applyDeferredReaders(uint32_t& state, WaitContext& ctx, uint32_t slot) {\n    long thread_nivcsw = 0;\n    long before = -1;\n    const uint32_t maxDeferredReaders =\n        shared_mutex_detail::getMaxDeferredReaders();\n    for (uint32_t yieldCount = 0; yieldCount < kMaxSoftYieldCount;\n         ++yieldCount) {\n      for (int softState = 0; softState < 3; ++softState) {\n        if (softState < 2) {\n          std::this_thread::yield();\n        } else {\n          thread_nivcsw = shared_mutex_detail::\n              getCurrentThreadInvoluntaryContextSwitchCount();\n        }\n        while (!slotValueIsThis(\n            deferredReader(slot)->load(std::memory_order_acquire))) {\n          if (++slot == maxDeferredReaders) {\n            return;\n          }\n        }\n        if (ctx.shouldTimeOut()) {\n          // finish applying immediately on timeout\n          break;\n        }\n      }\n      if (before >= 0 && thread_nivcsw >= before + 2) {\n        // heuristic says run queue is not empty\n        break;\n      }\n      before = thread_nivcsw;\n    }\n\n    uint32_t movedSlotCount = 0;\n    for (; slot < maxDeferredReaders; ++slot) {\n      auto slotPtr = deferredReader(slot);\n      auto slotValue = slotPtr->load(std::memory_order_acquire);\n      if (slotValueIsThis(slotValue) &&\n          slotPtr->compare_exchange_strong(slotValue, 0)) {\n        ++movedSlotCount;\n      }\n    }\n\n    if (movedSlotCount > 0) {\n      state = (state_ += movedSlotCount * kIncrHasS);\n    }\n    assert((state & (kHasE | kBegunE)) != 0);\n\n    // if state + kIncrHasS overflows (off the end of state) then either\n    // we have 2^(32-9) readers (almost certainly an application bug)\n    // or we had an underflow (also a bug)\n    assert(state < state + kIncrHasS);\n  }\n\n  // It is straightforward to make a token-less lock_shared() and\n  // unlock_shared() either by making the token-less version always use\n  // LockedInlineShared mode or by removing the token version.  Supporting\n  // deferred operation for both types is trickier than it appears, because\n  // the purpose of the token it so that unlock_shared doesn't have to\n  // look in other slots for its deferred lock.  Token-less unlock_shared\n  // might place a deferred lock in one place and then release a different\n  // slot that was originally used by the token-ful version.  If this was\n  // important we could solve the problem by differentiating the deferred\n  // locks so that cross-variety release wouldn't occur.  The best way\n  // is probably to steal a bit from the pointer, making deferredLocks[]\n  // an array of Atom<uintptr_t>.\n\n  template <class WaitContext>\n  bool lockSharedImpl(Token* token, WaitContext& ctx) {\n    uint32_t state = state_.load(std::memory_order_relaxed);\n    if ((state & (kHasS | kMayDefer | kHasE)) == 0 &&\n        state_.compare_exchange_strong(state, state + kIncrHasS)) {\n      if (token != nullptr) {\n        token->state_ = Token::State::LockedInlineShared;\n      }\n      return true;\n    }\n    return lockSharedImpl(state, token, ctx);\n  }\n\n  template <class WaitContext>\n  bool lockSharedImpl(uint32_t& state, Token* token, WaitContext& ctx);\n\n  // Updates the state in/out argument as if the locks were made inline,\n  // but does not update state_\n  void cleanupTokenlessSharedDeferred(uint32_t& state) {\n    const uint32_t maxDeferredReaders =\n        shared_mutex_detail::getMaxDeferredReaders();\n    for (uint32_t i = 0; i < maxDeferredReaders; ++i) {\n      auto slotPtr = deferredReader(i);\n      auto slotValue = slotPtr->load(std::memory_order_relaxed);\n      if (slotValue == tokenlessSlotValue()) {\n        slotPtr->store(0, std::memory_order_relaxed);\n        state += kIncrHasS;\n        if ((state & kHasS) == 0) {\n          break;\n        }\n      }\n    }\n  }\n\n  bool tryUnlockTokenlessSharedDeferred();\n\n  bool tryUnlockSharedDeferred(uint32_t slot) {\n    assert(slot < shared_mutex_detail::getMaxDeferredReaders());\n    auto slotValue = tokenfulSlotValue();\n    return deferredReader(slot)->compare_exchange_strong(slotValue, 0);\n  }\n\n  uint32_t unlockSharedInline() {\n    uint32_t state = (state_ -= kIncrHasS);\n    assert(\n        (state & (kHasE | kBegunE | kMayDefer)) != 0 ||\n        state < state + kIncrHasS);\n    if ((state & kHasS) == 0) {\n      // Only the second half of lock() can be blocked by a non-zero\n      // reader count, so that's the only thing we need to wake\n      wakeRegisteredWaiters(state, kWaitingNotS);\n    }\n    return state;\n  }\n\n  template <class WaitContext>\n  bool lockUpgradeImpl(WaitContext& ctx) {\n    uint32_t state;\n    do {\n      if (!waitForZeroBits(state, kHasSolo, kWaitingU, ctx)) {\n        return false;\n      }\n    } while (!state_.compare_exchange_strong(state, state | kHasU));\n    return true;\n  }\n};\n\nusing SharedMutexReadPriority = SharedMutexImpl<true>;\nusing SharedMutexWritePriority = SharedMutexImpl<false>;\nusing SharedMutex = SharedMutexWritePriority;\nusing SharedMutexTracked = SharedMutexImpl<\n    false,\n    void,\n    std::atomic,\n    shared_mutex_detail::PolicyTracked>;\nusing SharedMutexSuppressTSAN = SharedMutexImpl<\n    false,\n    void,\n    std::atomic,\n    shared_mutex_detail::PolicySuppressTSAN>;\n\n// Prevent the compiler from instantiating these in other translation units.\n// They are instantiated once in SharedMutex.cpp\nextern template class SharedMutexImpl<true>;\nextern template class SharedMutexImpl<false>;\n\ntemplate <\n    bool ReaderPriority,\n    typename Tag_,\n    template <typename> class Atom,\n    typename Policy>\nalignas(hardware_destructive_interference_size)\n    typename SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>::\n        DeferredReaderSlot\n    SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>::deferredReaders\n        [shared_mutex_detail::kMaxDeferredReadersAllocated *\n         kDeferredSeparationFactor] = {};\n\ntemplate <\n    bool ReaderPriority,\n    typename Tag_,\n    template <typename> class Atom,\n    typename Policy>\nbool SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>::\n    tryUnlockTokenlessSharedDeferred() {\n  uint32_t bestSlot = tls_lastTokenlessSlot();\n  // use do ... while to avoid calling\n  // shared_mutex_detail::getMaxDeferredReaders() unless necessary\n  uint32_t i = 0;\n  do {\n    auto slotPtr = deferredReader(bestSlot ^ i);\n    auto slotValue = slotPtr->load(std::memory_order_relaxed);\n    if (slotValue == tokenlessSlotValue() &&\n        slotPtr->compare_exchange_strong(slotValue, 0)) {\n      tls_lastTokenlessSlot() = bestSlot ^ i;\n      return true;\n    }\n    ++i;\n  } while (i < shared_mutex_detail::getMaxDeferredReaders());\n  return false;\n}\n\ntemplate <\n    bool ReaderPriority,\n    typename Tag_,\n    template <typename> class Atom,\n    typename Policy>\ntemplate <class WaitContext>\nbool SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>::lockSharedImpl(\n    uint32_t& state, Token* token, WaitContext& ctx) {\n  const uint32_t maxDeferredReaders =\n      shared_mutex_detail::getMaxDeferredReaders();\n  while (true) {\n    if (FOLLY_UNLIKELY((state & kHasE) != 0) &&\n        !waitForZeroBits(state, kHasE, kWaitingS, ctx) && ctx.canTimeOut()) {\n      return false;\n    }\n\n    uint32_t slot = tls_lastDeferredReaderSlot();\n    uintptr_t slotValue = 1; // any non-zero value will do\n\n    bool canAlreadyDefer = (state & kMayDefer) != 0;\n    bool aboveDeferThreshold =\n        (state & kHasS) >= (kNumSharedToStartDeferring - 1) * kIncrHasS;\n    bool drainInProgress = ReaderPriority && (state & kBegunE) != 0;\n    if (canAlreadyDefer || (aboveDeferThreshold && !drainInProgress)) {\n      /* Try using the most recent slot first. */\n      slotValue = deferredReader(slot)->load(std::memory_order_relaxed);\n      if (slotValue != 0) {\n        // starting point for our empty-slot search, can change after\n        // calling waitForZeroBits\n        uint32_t bestSlot =\n            (uint32_t)folly::AccessSpreader<Atom>::current(maxDeferredReaders);\n\n        // deferred readers are already enabled, or it is time to\n        // enable them if we can find a slot\n        for (uint32_t i = 0; i < kDeferredSearchDistance; ++i) {\n          slot = bestSlot ^ i;\n          assert(slot < maxDeferredReaders);\n          slotValue = deferredReader(slot)->load(std::memory_order_relaxed);\n          if (slotValue == 0) {\n            // found empty slot\n            tls_lastDeferredReaderSlot() = slot;\n            break;\n          }\n        }\n      }\n    }\n\n    if (slotValue != 0) {\n      // not yet deferred, or no empty slots\n      if (state_.compare_exchange_strong(state, state + kIncrHasS)) {\n        // successfully recorded the read lock inline\n        if (token != nullptr) {\n          token->state_ = Token::State::LockedInlineShared;\n        }\n        return true;\n      }\n      // state is updated, try again\n      continue;\n    }\n\n    // record that deferred readers might be in use if necessary\n    if ((state & kMayDefer) == 0) {\n      if (!state_.compare_exchange_strong(state, state | kMayDefer)) {\n        // keep going if CAS failed because somebody else set the bit\n        // for us\n        if ((state & (kHasE | kMayDefer)) != kMayDefer) {\n          continue;\n        }\n      }\n      // state = state | kMayDefer;\n    }\n\n    // try to use the slot\n    bool gotSlot = deferredReader(slot)->compare_exchange_strong(\n        slotValue,\n        token == nullptr ? tokenlessSlotValue() : tokenfulSlotValue());\n\n    // If we got the slot, we need to verify that an exclusive lock\n    // didn't happen since we last checked.  If we didn't get the slot we\n    // need to recheck state_ anyway to make sure we don't waste too much\n    // work.  It is also possible that since we checked state_ someone\n    // has acquired and released the write lock, clearing kMayDefer.\n    // Both cases are covered by looking for the readers-possible bit,\n    // because it is off when the exclusive lock bit is set.\n    state = state_.load(std::memory_order_acquire);\n\n    if (!gotSlot) {\n      continue;\n    }\n\n    if (token == nullptr) {\n      tls_lastTokenlessSlot() = slot;\n    }\n\n    if ((state & kMayDefer) != 0) {\n      assert((state & kHasE) == 0);\n      // success\n      if (token != nullptr) {\n        token->state_ = Token::State::LockedDeferredShared;\n        token->slot_ = (uint16_t)slot;\n      }\n      return true;\n    }\n\n    // release the slot before retrying\n    if (token == nullptr) {\n      // We can't rely on slot.  Token-less slot values can be freed by\n      // any unlock_shared(), so we need to do the full deferredReader\n      // search during unlock.  Unlike unlock_shared(), we can't trust\n      // kPrevDefer here.  This deferred lock isn't visible to lock()\n      // (that's the whole reason we're undoing it) so there might have\n      // subsequently been an unlock() and lock() with no intervening\n      // transition to deferred mode.\n      if (!tryUnlockTokenlessSharedDeferred()) {\n        unlockSharedInline();\n      }\n    } else {\n      if (!tryUnlockSharedDeferred(slot)) {\n        unlockSharedInline();\n      }\n    }\n\n    // We got here not because the lock was unavailable, but because\n    // we lost a compare-and-swap.  Try-lock is typically allowed to\n    // have spurious failures, but there is no lock efficiency gain\n    // from exploiting that freedom here.\n  }\n}\n\nnamespace shared_mutex_detail {\n\n[[noreturn]] void throwOperationNotPermitted();\n\n[[noreturn]] void throwDeadlockWouldOccur();\n\n} // namespace shared_mutex_detail\n\n} // namespace folly\n\n// std::shared_lock specialization for folly::SharedMutex to leverage tokenful\n// version of unlock_shared for faster unlocking.\nnamespace std {\n\ntemplate <\n    bool ReaderPriority,\n    typename Tag_,\n    template <typename> class Atom,\n    typename Policy>\nclass shared_lock<\n    ::folly::SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>> {\n public:\n  using mutex_type =\n      ::folly::SharedMutexImpl<ReaderPriority, Tag_, Atom, Policy>;\n  using token_type = typename mutex_type::Token;\n\n  shared_lock() noexcept = default;\n\n  [[nodiscard]] explicit shared_lock(mutex_type& mutex)\n      : mutex_(std::addressof(mutex)) {\n    lock();\n  }\n\n  shared_lock(mutex_type& mutex, std::defer_lock_t) noexcept\n      : mutex_(std::addressof(mutex)) {}\n\n  [[nodiscard]] shared_lock(mutex_type& mutex, std::try_to_lock_t)\n      : mutex_(std::addressof(mutex)) {\n    try_lock();\n  }\n\n  [[nodiscard]] shared_lock(mutex_type& mutex, std::adopt_lock_t)\n      : mutex_(std::addressof(mutex)) {\n    token_.state_ = token_type::State::LockedShared;\n  }\n\n  template <typename Clock, typename Duration>\n  [[nodiscard]] shared_lock(\n      mutex_type& mutex,\n      const std::chrono::time_point<Clock, Duration>& deadline)\n      : mutex_(std::addressof(mutex)) {\n    try_lock_until(deadline);\n  }\n\n  template <typename Rep, typename Period>\n  [[nodiscard]] shared_lock(\n      mutex_type& mutex, const std::chrono::duration<Rep, Period>& timeout)\n      : mutex_(std::addressof(mutex)) {\n    try_lock_for(timeout);\n  }\n\n  ~shared_lock() {\n    if (owns_lock()) {\n      mutex_->unlock_shared(token_);\n    }\n  }\n\n  shared_lock(const shared_lock&) = delete;\n\n  shared_lock& operator=(const shared_lock&) = delete;\n\n  shared_lock(shared_lock&& other) noexcept : shared_lock() { swap(other); }\n\n  shared_lock& operator=(shared_lock&& other) noexcept {\n    shared_lock(std::move(other)).swap(*this);\n    return *this;\n  }\n\n  void lock() {\n    error_if_not_lockable();\n    mutex_->lock_shared(token_);\n  }\n\n  bool try_lock() {\n    error_if_not_lockable();\n    return mutex_->try_lock_shared(token_);\n  }\n\n  template <typename Rep, typename Period>\n  bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout) {\n    error_if_not_lockable();\n    return mutex_->try_lock_shared_for(timeout, token_);\n  }\n\n  template <typename Clock, typename Duration>\n  bool try_lock_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) {\n    error_if_not_lockable();\n    return mutex_->try_lock_shared_until(deadline, token_);\n  }\n\n  void unlock() {\n    if (FOLLY_UNLIKELY(!owns_lock())) {\n      ::folly::shared_mutex_detail::throwOperationNotPermitted();\n    }\n    mutex_->unlock_shared(token_);\n    token_ = {};\n  }\n\n  void swap(shared_lock& other) noexcept {\n    std::swap(mutex_, other.mutex_);\n    std::swap(token_, other.token_);\n  }\n\n  mutex_type* release() noexcept {\n    if (owns_lock()) {\n      mutex_->release_token(token_);\n      token_ = {};\n    }\n    return std::exchange(mutex_, nullptr);\n  }\n\n  [[nodiscard]] bool owns_lock() const noexcept {\n    return static_cast<bool>(token_);\n  }\n\n  explicit operator bool() const noexcept { return owns_lock(); }\n\n  [[nodiscard]] mutex_type* mutex() const noexcept { return mutex_; }\n\n private:\n  void error_if_not_lockable() const {\n    if (FOLLY_UNLIKELY(mutex_ == nullptr)) {\n      ::folly::shared_mutex_detail::throwOperationNotPermitted();\n    }\n    if (FOLLY_UNLIKELY(owns_lock())) {\n      ::folly::shared_mutex_detail::throwDeadlockWouldOccur();\n    }\n  }\n\n  mutex_type* mutex_ = nullptr;\n  token_type token_;\n};\n\n} // namespace std\n"
  },
  {
    "path": "folly/Singleton-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nnamespace folly {\n\nnamespace detail {\n\ntemplate <typename T>\ntemplate <typename Tag, typename VaultTag>\nstruct SingletonHolder<T>::Impl : SingletonHolder<T> {\n  Impl()\n      : SingletonHolder<T>(\n            {typeid(T), typeid(Tag)}, *SingletonVault::singleton<VaultTag>()) {}\n};\n\ntemplate <typename T>\ntemplate <typename Tag, typename VaultTag>\ninline SingletonHolder<T>& SingletonHolder<T>::singleton() {\n  return detail::createGlobal<Impl<Tag, VaultTag>, void>();\n}\n\n[[noreturn]] void singletonWarnDoubleRegistrationAndAbort(\n    const TypeDescriptor& type);\n\ntemplate <typename T>\nvoid SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) {\n  std::lock_guard entry_lock(mutex_);\n\n  if (state_ != SingletonHolderState::NotRegistered) {\n    /* Possible causes:\n     *\n     * You have two instances of the same\n     * folly::Singleton<Class>. Probably because you define the\n     * singleton in a header included in multiple places? In general,\n     * folly::Singleton shouldn't be in the header, only off in some\n     * anonymous namespace in a cpp file. Code needing the singleton\n     * will find it when that code references folly::Singleton<Class>.\n     *\n     * Alternatively, you could have 2 singletons with the same type\n     * defined with a different name in a .cpp (source) file. For\n     * example:\n     *\n     * Singleton<int> a([] { return new int(3); });\n     * Singleton<int> b([] { return new int(4); });\n     *\n     * Adding tags should fix this (see documentation in the header).\n     *\n     */\n    singletonWarnDoubleRegistrationAndAbort(type());\n  }\n\n  create_ = std::move(c);\n  teardown_ = std::move(t);\n\n  state_ = SingletonHolderState::Dead;\n}\n\ntemplate <typename T>\nvoid SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) {\n  if (state_ == SingletonHolderState::NotRegistered) {\n    detail::singletonWarnRegisterMockEarlyAndAbort(type());\n  }\n  if (state_ == SingletonHolderState::Living ||\n      state_ == SingletonHolderState::LivingInChildAfterFork) {\n    destroyInstance();\n  }\n\n  {\n    auto creationOrder = vault_.creationOrder_.wlock();\n\n    auto it = std::find(creationOrder->begin(), creationOrder->end(), type());\n    if (it != creationOrder->end()) {\n      creationOrder->erase(it);\n    }\n  }\n\n  std::lock_guard entry_lock(mutex_);\n\n  create_ = std::move(c);\n  teardown_ = std::move(t);\n}\n\ntemplate <typename T>\nT* SingletonHolder<T>::get() {\n  if (FOLLY_LIKELY(\n          state_.load(std::memory_order_acquire) ==\n          SingletonHolderState::Living)) {\n    return instance_ptr_;\n  }\n  createInstance();\n\n  if (instance_weak_.expired()) {\n    detail::singletonThrowGetInvokedAfterDestruction(type());\n  }\n\n  return instance_ptr_;\n}\n\ntemplate <typename T>\nstd::weak_ptr<T> SingletonHolder<T>::get_weak() {\n  if (FOLLY_UNLIKELY(\n          state_.load(std::memory_order_acquire) !=\n          SingletonHolderState::Living)) {\n    createInstance();\n  }\n\n  return instance_weak_core_cached_.get();\n}\n\ntemplate <typename T>\nstd::shared_ptr<T> SingletonHolder<T>::try_get() {\n  if (FOLLY_UNLIKELY(\n          state_.load(std::memory_order_acquire) !=\n          SingletonHolderState::Living)) {\n    createInstance();\n  }\n\n  return instance_weak_core_cached_.lock();\n}\n\ntemplate <typename T>\nfolly::ReadMostlySharedPtr<T> SingletonHolder<T>::try_get_fast() {\n  if (FOLLY_UNLIKELY(\n          state_.load(std::memory_order_acquire) !=\n          SingletonHolderState::Living)) {\n    createInstance();\n  }\n\n  return instance_weak_fast_.lock();\n}\n\ntemplate <typename T>\ntemplate <typename Func>\ninvoke_result_t<Func, T*> detail::SingletonHolder<T>::apply(Func f) {\n  return f(try_get().get());\n}\n\ntemplate <typename T>\nvoid SingletonHolder<T>::vivify() {\n  if (FOLLY_UNLIKELY(\n          state_.load(std::memory_order_relaxed) !=\n          SingletonHolderState::Living)) {\n    createInstance();\n  }\n}\n\ntemplate <typename T>\nbool SingletonHolder<T>::hasLiveInstance() {\n  return !instance_weak_.expired();\n}\n\ntemplate <typename T>\nvoid SingletonHolder<T>::preDestroyInstance(\n    ReadMostlyMainPtrDeleter<>& deleter) {\n  instance_copy_ = instance_;\n  deleter.add(std::move(instance_));\n}\n\ntemplate <typename T>\nvoid SingletonHolder<T>::destroyInstance() {\n  if (state_.load(std::memory_order_relaxed) ==\n      SingletonHolderState::LivingInChildAfterFork) {\n    if (vault_.failOnUseAfterFork_) {\n      LOG(DFATAL) << \"Attempting to destroy singleton \" << type().name()\n                  << \" in child process after fork\";\n    } else {\n      LOG(ERROR) << \"Attempting to destroy singleton \" << type().name()\n                 << \" in child process after fork\";\n    }\n  }\n  state_ = SingletonHolderState::Dead;\n  instance_core_cached_.reset();\n  instance_.reset();\n  instance_copy_.reset();\n  if (destroy_baton_) {\n    constexpr std::chrono::seconds kDestroyWaitTime{5};\n    auto const wait_options =\n        destroy_baton_->wait_options().logging_enabled(false);\n    auto last_reference_released =\n        destroy_baton_->try_wait_for(kDestroyWaitTime, wait_options);\n    if (last_reference_released) {\n      vault_.addToShutdownLog(\"Destroying \" + type().name());\n      teardown_(instance_ptr_);\n      vault_.addToShutdownLog(type().name() + \" destroyed.\");\n    } else {\n      print_destructor_stack_trace_->store(true);\n      detail::singletonWarnDestroyInstanceLeak(type(), instance_ptr_);\n    }\n  }\n}\n\ntemplate <typename T>\nvoid SingletonHolder<T>::inChildAfterFork() {\n  auto expected = SingletonHolderState::Living;\n  state_.compare_exchange_strong(\n      expected,\n      SingletonHolderState::LivingInChildAfterFork,\n      std::memory_order_relaxed,\n      std::memory_order_relaxed);\n}\n\ntemplate <typename T>\nSingletonHolder<T>::SingletonHolder(\n    TypeDescriptor typeDesc, SingletonVault& vault) noexcept\n    : SingletonHolderBase(typeDesc), vault_(vault) {}\n\ntemplate <typename T>\nbool SingletonHolder<T>::creationStarted() {\n  // If alive, then creation was of course started.\n  // This is flipped after creating_thread_ was set, and before it was reset.\n  if (state_.load(std::memory_order_acquire) == SingletonHolderState::Living) {\n    return true;\n  }\n\n  // Not yet built.  Is it currently in progress?\n  if (creating_thread_.load(std::memory_order_acquire) != std::thread::id()) {\n    return true;\n  }\n\n  return false;\n}\n\ntemplate <typename T>\nvoid SingletonHolder<T>::createInstance() {\n  if (creating_thread_.load(std::memory_order_acquire) ==\n      std::this_thread::get_id()) {\n    detail::singletonWarnCreateCircularDependencyAndAbort(type());\n  }\n\n  // NOTE: Keeping the vault state lock at the top of the createInstance\n  // function is important to not deadlock singleton creation in child\n  // processes.\n  //\n  // We take and hold the vault state lock at the start to ensure that a write\n  // lock for the vault state isn't taken during the time that this Singleton's\n  // mutex_ is held, as it may result in a deadlock in creation of this\n  // singleton in the future if a fork happens in-between the mutex_ being\n  // acquired and the completion of this function that could \"easily\" happen if\n  // there's contention on the vault state, as another thread may initiate to\n  // destroyInstances in preparation for a fork\n  //\n  // This still doesn't 100% remove the risk of deadlock in child processes, as\n  // it's possible that the vault state rlock here is acquired in another\n  // thread before the fork happens. That would block the child process from\n  // re-instantiating the singleton vault, as it would never be able to get a\n  // write lock on it.\n  auto vault_state = vault_.state_.rlock();\n  if (vault_state->state == detail::SingletonVaultState::Type::Quiescing) {\n    return;\n  }\n\n  std::lock_guard entry_lock(mutex_);\n  switch (auto state = state_.load(std::memory_order_acquire)) {\n    case SingletonHolderState::Living:\n      return; // nothing to do\n    case SingletonHolderState::LivingInChildAfterFork:\n      if (vault_.failOnUseAfterFork_) {\n        LOG(DFATAL) << \"Attempting to use singleton \" << type().name()\n                    << \" in child process after fork\";\n      } else {\n        LOG(ERROR) << \"Attempting to use singleton \" << type().name()\n                   << \" in child process after fork\";\n      }\n      state_.compare_exchange_strong(\n          state,\n          SingletonHolderState::Living,\n          std::memory_order_relaxed,\n          std::memory_order_relaxed);\n      return; // just need to do the state transition\n    case SingletonHolderState::NotRegistered:\n      detail::singletonWarnCreateUnregisteredAndAbort(type()); // noreturn\n    case SingletonHolderState::Dead:\n      break; // recreate it below\n    default:\n      assume_unreachable(); // impossible state\n  }\n\n  SCOPE_EXIT {\n    // Clean up creator thread when complete, and also, in case of errors here,\n    // so that subsequent attempts don't think this is still in the process of\n    // being built.\n    creating_thread_.store(std::thread::id(), std::memory_order_release);\n  };\n\n  creating_thread_.store(std::this_thread::get_id(), std::memory_order_release);\n\n  if (vault_.type_.load(std::memory_order_relaxed) !=\n          SingletonVault::Type::Relaxed &&\n      !vault_state->registrationComplete) {\n    detail::singletonWarnCreateBeforeRegistrationCompleteAndAbort(type());\n  }\n\n  auto destroy_baton = std::make_shared<folly::Baton<>>();\n  auto print_destructor_stack_trace =\n      std::make_shared<std::atomic<bool>>(false);\n\n  // Can't use make_shared -- no support for a custom deleter, sadly.\n  std::shared_ptr<T> instance(\n      create_(),\n      [destroy_baton, print_destructor_stack_trace, type = type()](T*) mutable {\n        destroy_baton->post();\n        if (print_destructor_stack_trace->load()) {\n          detail::singletonPrintDestructionStackTrace(type);\n        }\n      });\n\n  // We should schedule destroyInstances() only after the singleton was\n  // created. This will ensure it will be destroyed before singletons,\n  // not managed by folly::Singleton, which were initialized in its\n  // constructor\n  SingletonVault::scheduleDestroyInstances();\n\n  instance_weak_ = instance;\n  instance_ptr_ = instance.get();\n  instance_core_cached_.reset(instance);\n  instance_.reset(std::move(instance));\n  instance_weak_fast_ = instance_;\n  instance_weak_core_cached_.reset(instance_core_cached_);\n\n  destroy_baton_ = std::move(destroy_baton);\n  print_destructor_stack_trace_ = std::move(print_destructor_stack_trace);\n\n  // This has to be the last step, because once state is Living other threads\n  // may access instance and instance_weak w/o synchronization.\n  state_.store(SingletonHolderState::Living, std::memory_order_release);\n\n  vault_.creationOrder_.wlock()->push_back(type());\n  vault_.instantiatedAtLeastOnce_.wlock()->insert(type());\n}\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Singleton.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Singleton.h>\n\n#ifndef _WIN32\n#include <dlfcn.h>\n#include <signal.h>\n#include <time.h>\n#endif\n\n#include <atomic>\n#include <chrono>\n#include <cstdio>\n#include <cstdlib>\n#include <string>\n#include <fmt/chrono.h>\n#include <fmt/format.h>\n\n#include <folly/Demangle.h>\n#include <folly/ScopeGuard.h>\n#include <folly/experimental/symbolizer/Symbolizer.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/FmtCompile.h>\n#include <folly/system/AtFork.h>\n// Before registrationComplete() we cannot assume that glog has been\n// initialized, so we need to use RAW_LOG for any message that may be logged\n// before that.\n#include <glog/raw_logging.h>\n\n#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__)\n#define FOLLY_SINGLETON_HAVE_DLSYM 1\n#else\n#define FOLLY_SINGLETON_HAVE_DLSYM 0\n#endif\n\nnamespace folly {\n\n#if FOLLY_SINGLETON_HAVE_DLSYM\nnamespace detail {\nstatic void singleton_hs_init_weak(int* argc, char** argv[])\n    __attribute__((__weakref__(\"hs_init\")));\n} // namespace detail\n#endif\n\nSingletonVault::Type SingletonVault::defaultVaultType() {\n#if FOLLY_SINGLETON_HAVE_DLSYM\n  bool isPython = dlsym(RTLD_DEFAULT, \"Py_Main\");\n  bool isHaskell =\n      detail::singleton_hs_init_weak || dlsym(RTLD_DEFAULT, \"hs_init\");\n  bool isJVM = dlsym(RTLD_DEFAULT, \"JNI_GetCreatedJavaVMs\");\n  bool isD = dlsym(RTLD_DEFAULT, \"_d_run_main\");\n  bool isCgo = dlsym(RTLD_DEFAULT, \"_cgo_topofstack\") ||\n      dlsym(RTLD_DEFAULT, \"_cgo_panic\");\n\n  return isPython || isHaskell || isJVM || isD || isCgo\n      ? Type::Relaxed\n      : Type::Strict;\n#else\n  return Type::Relaxed;\n#endif\n}\n\nnamespace detail {\n\nstd::string TypeDescriptor::name() const {\n  auto ret = demangle(ti_.name());\n  if (tag_ti_ != std::type_index(typeid(DefaultTag))) {\n    ret += \"/\";\n    ret += demangle(tag_ti_.name());\n  }\n  return ret.toStdString();\n}\n\n[[noreturn]] void singletonWarnDoubleRegistrationAndAbort(\n    const TypeDescriptor& type) {\n  FOLLY_SAFE_FATAL( // May happen before registrationComplete().\n      fmt::format(\n          fmt::runtime( //\n              \"Double registration of singletons of the same \"\n              \"underlying type; check for multiple definitions \"\n              \"of type folly::Singleton<{}>\"),\n          type.name())\n          .c_str());\n}\n\n[[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort(\n    const TypeDescriptor& type) {\n  FOLLY_SAFE_FATAL( // May happen before registrationComplete().\n      fmt::format(\n          fmt::runtime( //\n              \"Double registration of singletons of the same \"\n              \"underlying type; check for multiple definitions \"\n              \"of type folly::LeakySingleton<{}>\"),\n          type.name())\n          .c_str());\n}\n\n[[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort(\n    const TypeDescriptor& type) {\n  FOLLY_SAFE_FATAL( // May happen before registrationComplete().\n      \"Creating instance for unregistered singleton: \",\n      type.name().c_str());\n}\n\n[[noreturn]] void singletonWarnRegisterMockEarlyAndAbort(\n    const TypeDescriptor& type) {\n  FOLLY_SAFE_FATAL( // May happen before registrationComplete().\n      \"Registering mock before singleton was registered: \",\n      type.name().c_str());\n}\n\nvoid singletonWarnDestroyInstanceLeak(\n    const TypeDescriptor& type, const void* ptr) {\n  LOG(ERROR)\n      << \"Singleton of type \" << type.name() << \" has a \"\n      << \"living reference at destroyInstances time; beware! Raw \"\n      << \"pointer is \" << ptr << \". It is very likely \"\n      << \"that some other singleton is holding a shared_ptr to it. \"\n      << \"This singleton will be leaked (even if a shared_ptr to it \"\n      << \"is eventually released).\"\n      << \"Make sure dependencies between these singletons are \"\n      << \"properly defined.\";\n}\n\n[[noreturn]] void singletonWarnCreateCircularDependencyAndAbort(\n    const TypeDescriptor& type) {\n  FOLLY_SAFE_FATAL(\"circular singleton dependency: \", type.name().c_str());\n}\n\n[[noreturn]] void singletonWarnCreateUnregisteredAndAbort(\n    const TypeDescriptor& type) {\n  FOLLY_SAFE_FATAL( // May happen before registrationComplete().\n      \"Creating instance for unregistered singleton: \",\n      type.name().c_str());\n}\n\n[[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort(\n    const TypeDescriptor& type) {\n  FOLLY_SAFE_FATAL( // May happen before registrationComplete().\n      fmt::format(\n          fmt::runtime( //\n              \"Singleton {} requested before \"\n              \"registrationComplete() call.\\n\"\n              \"This usually means that either main() never called \"\n              \"folly::init, or singleton was requested before main() \"\n              \"(which is not allowed)\"),\n          type.name())\n          .c_str());\n}\n\nvoid singletonPrintDestructionStackTrace(const TypeDescriptor& type) {\n  auto trace = symbolizer::getStackTraceStr();\n  LOG(ERROR) << \"Singleton \" << type.name() << \" was released.\\n\"\n             << \"Stacktrace:\\n\"\n             << (!trace.empty() ? trace : \"(not available)\");\n}\n\n[[noreturn]] void singletonThrowNullCreator(const std::type_info& type) {\n  auto const msg = fmt::format(\n      FOLLY_FMT_COMPILE(\n          \"nullptr_t should be passed if you want {} to be default constructed\"),\n      folly::StringPiece(demangle(type)));\n  throw std::logic_error(msg);\n}\n\n[[noreturn]] void singletonThrowGetInvokedAfterDestruction(\n    const TypeDescriptor& type) {\n  throw std::runtime_error(\n      \"Raw pointer to a singleton requested after its destruction.\"\n      \" Singleton type is: \" +\n      type.name());\n}\n\n} // namespace detail\n\nnamespace {\n\nstruct FatalHelper {\n  ~FatalHelper() {\n    if (!leakedSingletons_.empty()) {\n      std::string leakedTypes;\n      for (const auto& singleton : leakedSingletons_) {\n        leakedTypes += \"\\t\" + singleton.name() + \"\\n\";\n      }\n      LOG(DFATAL)\n          << \"Singletons of the following types had living references \"\n          << \"after destroyInstances was finished:\\n\"\n          << leakedTypes\n          << \"beware! It is very likely that those singleton instances \"\n          << \"are leaked.\";\n    }\n  }\n\n  std::vector<detail::TypeDescriptor> leakedSingletons_;\n};\n\n#if defined(__APPLE__) || defined(_MSC_VER)\n// OS X doesn't support constructor priorities.\nFatalHelper fatalHelper;\n#else\nFatalHelper __attribute__((__init_priority__(101))) fatalHelper;\n#endif\n\n} // namespace\n\nSingletonVault::SingletonVault(Type type) noexcept : type_(type) {\n  AtFork::registerHandler(\n      this,\n      /*prepare*/\n      [this]() {\n        auto singletons = singletons_.rlock();\n        auto creationOrder = creationOrder_.rlock();\n\n        CHECK_GE(singletons->size(), creationOrder->size());\n\n        for (const auto& singletonType : *creationOrder) {\n          liveSingletonsPreFork_.insert(singletons->at(singletonType));\n        }\n\n        return true;\n      },\n      /*parent*/ [this]() { liveSingletonsPreFork_.clear(); },\n      /*child*/\n      [this]() {\n        for (auto singleton : liveSingletonsPreFork_) {\n          singleton->inChildAfterFork();\n        }\n        liveSingletonsPreFork_.clear();\n      });\n}\n\nSingletonVault::~SingletonVault() {\n  AtFork::unregisterHandler(this);\n  destroyInstances();\n}\n\nvoid SingletonVault::registerSingleton(detail::SingletonHolderBase* entry) {\n  auto state = state_.rlock();\n  state->check(detail::SingletonVaultState::Type::Running);\n\n  if (FOLLY_UNLIKELY(state->registrationComplete) &&\n      type_.load(std::memory_order_relaxed) == Type::Strict) {\n    LOG(ERROR) << \"Registering singleton after registrationComplete().\";\n  }\n\n  auto singletons = singletons_.wlock();\n  CHECK_THROW(\n      singletons->emplace(entry->type(), entry).second, std::logic_error);\n}\n\nvoid SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {\n  auto state = state_.rlock();\n  state->check(detail::SingletonVaultState::Type::Running);\n\n  if (FOLLY_UNLIKELY(state->registrationComplete) &&\n      type_.load(std::memory_order_relaxed) == Type::Strict) {\n    LOG(ERROR) << \"Registering for eager-load after registrationComplete().\";\n  }\n\n  CHECK_THROW(singletons_.rlock()->count(entry->type()), std::logic_error);\n\n  auto eagerInitSingletons = eagerInitSingletons_.wlock();\n  eagerInitSingletons->insert(entry);\n}\n\nvoid SingletonVault::addEagerInitOnReenableSingleton(\n    detail::SingletonHolderBase* entry) {\n  auto state = state_.rlock();\n  state->check(detail::SingletonVaultState::Type::Running);\n\n  if (FOLLY_UNLIKELY(state->registrationComplete) &&\n      type_.load(std::memory_order_relaxed) == Type::Strict) {\n    LOG(ERROR)\n        << \"Registering for eager-load on re-enable after registrationComplete().\";\n  }\n\n  CHECK_THROW(singletons_.rlock()->count(entry->type()), std::logic_error);\n\n  auto eagerInitOnReenableSingletons = eagerInitOnReenableSingletons_.wlock();\n  eagerInitOnReenableSingletons->insert(entry);\n}\n\nvoid SingletonVault::registrationComplete() {\n  scheduleDestroyInstances();\n\n  auto state = state_.wlock();\n  state->check(detail::SingletonVaultState::Type::Running);\n\n  if (state->registrationComplete) {\n    return;\n  }\n\n  auto singletons = singletons_.rlock();\n  if (type_.load(std::memory_order_relaxed) == Type::Strict) {\n    for (const auto& p : *singletons) {\n      if (p.second->hasLiveInstance()) {\n        throw std::runtime_error(\n            \"Singleton \" + p.first.name() +\n            \" created before registration was complete.\");\n      }\n    }\n  }\n\n  state->registrationComplete = true;\n}\n\nvoid SingletonVault::doEagerInit() {\n  {\n    auto state = state_.rlock();\n    state->check(detail::SingletonVaultState::Type::Running);\n    if (FOLLY_UNLIKELY(!state->registrationComplete)) {\n      throw std::logic_error(\"registrationComplete() not yet called\");\n    }\n  }\n\n  auto eagerInitSingletons = eagerInitSingletons_.rlock();\n  for (auto* single : *eagerInitSingletons) {\n    single->createInstance();\n  }\n}\n\nvoid SingletonVault::doEagerInitVia(Executor& exe, folly::Baton<>* done) {\n  {\n    auto state = state_.rlock();\n    state->check(detail::SingletonVaultState::Type::Running);\n    if (FOLLY_UNLIKELY(!state->registrationComplete)) {\n      throw std::logic_error(\"registrationComplete() not yet called\");\n    }\n  }\n\n  auto eagerInitSingletons = eagerInitSingletons_.rlock();\n  auto countdown =\n      std::make_shared<std::atomic<size_t>>(eagerInitSingletons->size());\n  for (auto* single : *eagerInitSingletons) {\n    // countdown is retained by shared_ptr, and will be alive until last lambda\n    // is done.  notifyBaton is provided by the caller, and expected to remain\n    // present (if it's non-nullptr).  singletonSet can go out of scope but\n    // its values, which are SingletonHolderBase pointers, are alive as long as\n    // SingletonVault is not being destroyed.\n    exe.add([=] {\n      // decrement counter and notify if requested, whether initialization\n      // was successful, was skipped (already initialized), or exception thrown.\n      SCOPE_EXIT {\n        if (--(*countdown) == 0) {\n          if (done != nullptr) {\n            done->post();\n          }\n        }\n      };\n      // if initialization is in progress in another thread, don't try to init\n      // here.  Otherwise the current thread will block on 'createInstance'.\n      if (!single->creationStarted()) {\n        single->createInstance();\n      }\n    });\n  }\n}\n\nvoid SingletonVault::destroyInstances() {\n  cancellationSource_.wlock()->requestCancellation();\n\n  auto stateW = state_.wlock();\n  if (stateW->state == detail::SingletonVaultState::Type::Quiescing) {\n    return;\n  }\n  stateW->state = detail::SingletonVaultState::Type::Quiescing;\n\n  auto stateR = stateW.moveFromWriteToRead();\n  {\n    auto singletons = singletons_.rlock();\n    auto creationOrder = creationOrder_.rlock();\n\n    CHECK_GE(singletons->size(), creationOrder->size());\n\n    // Release all ReadMostlyMainPtrs at once\n    {\n      ReadMostlyMainPtrDeleter<> deleter;\n      for (auto& singleton_type : *creationOrder) {\n        singletons->at(singleton_type)->preDestroyInstance(deleter);\n      }\n    }\n\n    for (auto type_iter = creationOrder->rbegin();\n         type_iter != creationOrder->rend();\n         ++type_iter) {\n      singletons->at(*type_iter)->destroyInstance();\n    }\n\n    for (auto& singleton_type : *creationOrder) {\n      auto instance = singletons->at(singleton_type);\n      if (!instance->hasLiveInstance()) {\n        continue;\n      }\n\n      fatalHelper.leakedSingletons_.push_back(instance->type());\n    }\n  }\n\n  {\n    auto creationOrder = creationOrder_.wlock();\n    creationOrder->clear();\n  }\n}\n\nvoid SingletonVault::reenableInstances() {\n  CHECK(!shutdownTimerStarted_.load(std::memory_order_relaxed))\n      << \"reenableInstances() called after destroyInstancesFinal()\";\n  {\n    auto state = state_.wlock();\n\n    state->check(detail::SingletonVaultState::Type::Quiescing);\n\n    state->state = detail::SingletonVaultState::Type::Running;\n  }\n\n  // reset the cancellation source\n  cancellationSource_.withWLock([&](auto& cancellationSource) {\n    cancellationSource = folly::CancellationSource{};\n  });\n\n  auto eagerInitOnReenableSingletons = eagerInitOnReenableSingletons_.copy();\n  auto instantiatedAtLeastOnce = instantiatedAtLeastOnce_.copy();\n  for (auto* single : eagerInitOnReenableSingletons) {\n    if (!instantiatedAtLeastOnce.count(single->type())) {\n      continue;\n    }\n    single->createInstance();\n  }\n}\n\nvoid SingletonVault::scheduleDestroyInstances() {\n  // Add a dependency on folly::ThreadLocal to make sure all its static\n  // singletons are initialized first.\n  threadlocal_detail::StaticMeta<void, void>::instance();\n#if !defined(FOLLY_SINGLETON_SKIP_SCHEDULE_ATEXIT) || \\\n    !FOLLY_SINGLETON_SKIP_SCHEDULE_ATEXIT\n  std::atexit([] { SingletonVault::singleton()->destroyInstancesFinal(); });\n#endif\n}\n\nvoid SingletonVault::destroyInstancesFinal() {\n  startShutdownTimer();\n  destroyInstances();\n}\n\nvoid SingletonVault::addToShutdownLog(std::string message) {\n  std::chrono::time_point<std::chrono::system_clock> now =\n      std::chrono::system_clock::now();\n  std::chrono::milliseconds millis =\n      std::chrono::duration_cast<std::chrono::milliseconds>(\n          now.time_since_epoch());\n  shutdownLog_.wlock()->push_back(fmt::format(\"{:%T} {}\", millis, message));\n}\n\n#if FOLLY_HAVE_LIBRT\nnamespace {\n[[noreturn]] void fireShutdownSignalHelper(sigval_t sigval) {\n  static_cast<SingletonVault*>(sigval.sival_ptr)->fireShutdownTimer();\n}\n} // namespace\n#endif\n\nvoid SingletonVault::startShutdownTimer() {\n#if FOLLY_HAVE_LIBRT\n  if (shutdownTimerStarted_.exchange(true)) {\n    return;\n  }\n\n  if (!shutdownTimeout_.count()) {\n    return;\n  }\n\n  struct sigevent sig;\n  sig.sigev_notify = SIGEV_THREAD;\n  sig.sigev_notify_function = fireShutdownSignalHelper;\n  sig.sigev_value.sival_ptr = this;\n  sig.sigev_notify_attributes = nullptr;\n  timer_t timerId = nullptr;\n  PCHECK(timer_create(CLOCK_MONOTONIC, &sig, &timerId) == 0);\n\n  struct itimerspec newValue, oldValue;\n  newValue.it_value.tv_sec =\n      std::chrono::milliseconds(shutdownTimeout_).count() / 1000;\n  newValue.it_value.tv_nsec =\n      std::chrono::milliseconds(shutdownTimeout_).count() % 1000 * 1000000;\n  newValue.it_interval.tv_sec = 0;\n  newValue.it_interval.tv_nsec = 0;\n  PCHECK(timer_settime(timerId, 0, &newValue, &oldValue) == 0);\n#endif\n}\n\n[[noreturn]] void SingletonVault::fireShutdownTimer() {\n  std::string shutdownLog;\n  for (auto& logMessage : shutdownLog_.copy()) {\n    shutdownLog += logMessage + \"\\n\";\n  }\n\n  auto msg = folly::to<std::string>(\n      \"Failed to complete shutdown within \",\n      std::chrono::milliseconds(shutdownTimeout_).count(),\n      \"ms. Shutdown log:\\n\",\n      shutdownLog);\n  folly::terminate_with<std::runtime_error>(msg);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Singleton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_singleton\n//\n\n/// Recommended usage of this class: suppose you have a class\n/// called `MyExpensiveService`, and you only want to construct one (ie,\n/// it's a singleton), but you only want to construct it if it is used.\n///\n/// In your .h file:\n///\n///     class MyExpensiveService {\n///       // Caution - may return a null ptr during startup and shutdown.\n///       static std::shared_ptr<MyExpensiveService> getInstance();\n///       ....\n///     };\n///\n/// In your .cpp file:\n///\n///     namespace { struct PrivateTag {}; }\n///\n///     static folly::Singleton<MyExpensiveService, PrivateTag> the_singleton;\n///\n///     std::shared_ptr<MyExpensiveService> MyExpensiveService::getInstance() {\n///       return the_singleton.try_get();\n///     }\n///\n/// Code in other modules can access it via:\n///\n///     auto instance = MyExpensiveService::getInstance();\n///\n/// ### Advanced usage and notes\n///\n/// You can also access a singleton instance with\n/// `Singleton<ObjectType, TagType>::try_get()`. We recommend\n/// that you prefer the form `the_singleton.try_get()` because it ensures that\n/// `the_singleton` is used and cannot be garbage-collected during linking: this\n/// is necessary because the constructor of `the_singleton` is what registers it\n/// to the SingletonVault.\n///\n/// The singleton will be created on demand.  If the constructor for\n/// MyExpensiveService actually makes use of *another* Singleton, then\n/// the right thing will happen -- that other singleton will complete\n/// construction before get() returns.  However, in the event of a\n/// circular dependency, a runtime error will occur.\n///\n/// You can have multiple singletons of the same underlying type, but\n/// each must be given a unique tag. If no tag is specified a default tag is\n/// used. We recommend that you use a tag from an anonymous namespace private to\n/// your implementation file, as this ensures that the singleton is only\n/// available via your interface and not also through `Singleton<T>::try_get()`\n///\n///     namespace {\n///     struct Tag1 {};\n///     struct Tag2 {};\n///     folly::Singleton<MyExpensiveService> s_default;\n///     folly::Singleton<MyExpensiveService, Tag1> s1;\n///     folly::Singleton<MyExpensiveService, Tag2> s2;\n///     }\n///     ...\n///     MyExpensiveService* svc_default = s_default.get();\n///     MyExpensiveService* svc1 = s1.get();\n///     MyExpensiveService* svc2 = s2.get();\n///\n/// By default, the singleton instance is constructed via new and\n/// deleted via delete, but this is configurable:\n///\n///     namespace {\n///     folly::Singleton<MyExpensiveService> the_singleton(create, destroy);\n///     }\n///\n/// Where create and destroy are functions, `Singleton<T>::CreateFunc`\n/// `Singleton<T>::TeardownFunc`.\n///\n/// For example, if you need to pass arguments to your class's constructor:\n///\n///     class X {\n///      public:\n///        X(int a1, std::string a2);\n///      // ...\n///     }\n///\n/// Make your singleton like this:\n///\n///     folly::Singleton<X> singleton_x([]() { return new X(42, \"foo\"); });\n///\n/// The above examples detail a situation where an expensive singleton is loaded\n/// on-demand (thus only if needed).  However if there is an expensive singleton\n/// that will likely be needed, and initialization takes a potentially long\n/// time, e.g. while initializing, parsing some files, talking to remote\n/// services, making uses of other singletons, and so on, the initialization of\n/// those can be scheduled up front, or \"eagerly\".\n///\n/// In that case the singleton can be declared this way:\n///\n///     namespace {\n///     auto the_singleton =\n///         folly::Singleton<MyExpensiveService>(\n///                 /* optional args, destroy args */)\n///             .shouldEagerInit();\n///     }\n///\n/// This way the singleton's instance is built at program initialization,\n/// if the program opted-in to that feature by calling \"doEagerInit\" or\n/// \"doEagerInitVia\" during its startup.\n///\n/// What if you need to destroy all of your singletons?  Say, some of\n/// your singletons manage threads, but you need to fork?  Or your unit\n/// test wants to clean up all global state?  Then you can call\n/// `SingletonVault::singleton()->destroyInstances()`, which invokes the\n/// TeardownFunc for each singleton, in the reverse order they were\n/// created.  It is your responsibility to ensure your singletons can\n/// handle cases where the singletons they depend on go away, however.\n/// Singletons won't be recreated after destroyInstances call. If you\n/// want to re-enable singleton creation (say after fork was called) you\n/// should call reenableInstances.\n///\n/// NOTE: Calling `try_get()` **before**\n/// `SingletonVault::registrationComplete()` has completed (i.e. before\n/// `folly::init()` completed) is a logic error. In this case, `try_get()`\n///  will abort via `singletonWarnCreateBeforeRegistrationCompleteAndAbort()`.\n/// @class folly::Singleton\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/Exception.h>\n#include <folly/Executor.h>\n#include <folly/Memory.h>\n#include <folly/Synchronized.h>\n#include <folly/concurrency/CoreCachedSharedPtr.h>\n#include <folly/concurrency/memory/ReadMostlySharedPtr.h>\n#include <folly/detail/Singleton.h>\n#include <folly/detail/StaticSingletonManager.h>\n#include <folly/hash/Hash.h>\n#include <folly/lang/Exception.h>\n#include <folly/memory/SanitizeLeak.h>\n#include <folly/synchronization/Baton.h>\n\n#include <algorithm>\n#include <atomic>\n#include <condition_variable>\n#include <functional>\n#include <list>\n#include <memory>\n#include <mutex>\n#include <string>\n#include <thread>\n#include <typeindex>\n#include <typeinfo>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <glog/logging.h>\n\nnamespace folly {\n\n// For actual usage, please see the Singleton<T> class at the bottom\n// of this file; that is what you will actually interact with.\n\n/// SingletonVault - a library to manage the creation and destruction\n/// of interdependent singletons.\n///\n/// SingletonVault is the class that manages singleton instances.  It\n/// is unaware of the underlying types of singletons, and simply\n/// manages lifecycles and invokes CreateFunc and TeardownFunc when\n/// appropriate.  In general, you won't need to interact with the\n/// SingletonVault itself.\n///\n/// A vault goes through a few stages of life:\n///\n///   1. Registration phase; singletons can be registered:\n///      a) Strict: no singleton can be created in this stage.\n///      b) Relaxed: singleton can be created (the default vault is Relaxed).\n///   2. registrationComplete() has been called; singletons can no\n///      longer be registered, but they can be created.\n///   3. A vault can return to stage 1 when destroyInstances is called.\n///\n/// In general, you don't need to worry about any of the above; just\n/// ensure registrationComplete() is called near the top of your main()\n/// function, otherwise no singletons can be instantiated.\n/// @class folly::SingletonVault\nclass SingletonVault;\n\nnamespace detail {\n\n// A TypeDescriptor is the unique handle for a given singleton.  It is\n// a combination of the type and of the optional name, and is used as\n// a key in unordered_maps.\nclass TypeDescriptor {\n public:\n  TypeDescriptor(const std::type_info& ti, const std::type_info& tag_ti)\n      : ti_(ti), tag_ti_(tag_ti) {}\n\n  TypeDescriptor(const TypeDescriptor& other)\n      : ti_(other.ti_), tag_ti_(other.tag_ti_) {}\n\n  TypeDescriptor& operator=(const TypeDescriptor& other) {\n    if (this != &other) {\n      ti_ = other.ti_;\n      tag_ti_ = other.tag_ti_;\n    }\n\n    return *this;\n  }\n\n  std::string name() const;\n\n  friend class TypeDescriptorHasher;\n\n  bool operator==(const TypeDescriptor& other) const {\n    return ti_ == other.ti_ && tag_ti_ == other.tag_ti_;\n  }\n\n private:\n  std::type_index ti_;\n  std::type_index tag_ti_;\n};\n\nclass TypeDescriptorHasher {\n public:\n  size_t operator()(const TypeDescriptor& ti) const {\n    return folly::hash::hash_combine(ti.ti_, ti.tag_ti_);\n  }\n};\n\n[[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort(\n    const TypeDescriptor& type);\n\n[[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort(\n    const TypeDescriptor& type);\n\n[[noreturn]] void singletonWarnRegisterMockEarlyAndAbort(\n    const TypeDescriptor& type);\n\nvoid singletonWarnDestroyInstanceLeak(\n    const TypeDescriptor& type, const void* ptr);\n\n[[noreturn]] void singletonWarnCreateCircularDependencyAndAbort(\n    const TypeDescriptor& type);\n\n[[noreturn]] void singletonWarnCreateUnregisteredAndAbort(\n    const TypeDescriptor& type);\n\n[[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort(\n    const TypeDescriptor& type);\n\nvoid singletonPrintDestructionStackTrace(const TypeDescriptor& type);\n\n[[noreturn]] void singletonThrowNullCreator(const std::type_info& type);\n\n[[noreturn]] void singletonThrowGetInvokedAfterDestruction(\n    const TypeDescriptor& type);\n\nstruct SingletonVaultState {\n  // The two stages of life for a vault, as mentioned in the class comment.\n  enum class Type {\n    Running,\n    Quiescing,\n  };\n\n  Type state{Type::Running};\n  bool registrationComplete{false};\n\n  // Each singleton in the vault can be in two states: dead\n  // (registered but never created), living (CreateFunc returned an instance).\n\n  void check(\n      Type expected,\n      const char* msg = \"Unexpected singleton state change\") const {\n    if (expected != state) {\n      throw_exception<std::logic_error>(msg);\n    }\n  }\n\n  bool isDisabled() const { return state == Type::Quiescing; }\n};\n\n// This interface is used by SingletonVault to interact with SingletonHolders.\n// Having a non-template interface allows SingletonVault to keep a list of all\n// SingletonHolders.\nclass SingletonHolderBase {\n public:\n  explicit SingletonHolderBase(TypeDescriptor typeDesc) noexcept\n      : type_(typeDesc) {}\n  virtual ~SingletonHolderBase() = default;\n\n  TypeDescriptor type() const { return type_; }\n  virtual bool hasLiveInstance() = 0;\n  virtual void createInstance() = 0;\n  virtual bool creationStarted() = 0;\n  virtual void preDestroyInstance(ReadMostlyMainPtrDeleter<>&) = 0;\n  virtual void destroyInstance() = 0;\n  virtual void inChildAfterFork() = 0;\n\n private:\n  TypeDescriptor type_;\n};\n\n// An actual instance of a singleton, tracking the instance itself,\n// its state as described above, and the create and teardown\n// functions.\ntemplate <typename T>\nstruct SingletonHolder : public SingletonHolderBase {\n public:\n  typedef std::function<void(T*)> TeardownFunc;\n  typedef std::function<T*(void)> CreateFunc;\n\n  template <typename Tag, typename VaultTag>\n  inline static SingletonHolder<T>& singleton();\n\n  inline T* get();\n  inline std::weak_ptr<T> get_weak();\n  inline std::shared_ptr<T> try_get();\n  inline folly::ReadMostlySharedPtr<T> try_get_fast();\n  template <typename Func>\n  inline invoke_result_t<Func, T*> apply(Func f);\n  inline void vivify();\n\n  void registerSingleton(CreateFunc c, TeardownFunc t);\n  void registerSingletonMock(CreateFunc c, TeardownFunc t);\n  bool hasLiveInstance() override;\n  void createInstance() override;\n  bool creationStarted() override;\n  void preDestroyInstance(ReadMostlyMainPtrDeleter<>&) override;\n  void destroyInstance() override;\n  void inChildAfterFork() override;\n\n private:\n  template <typename Tag, typename VaultTag>\n  struct Impl;\n\n  SingletonHolder(TypeDescriptor type, SingletonVault& vault) noexcept;\n\n  enum class SingletonHolderState {\n    NotRegistered,\n    Dead,\n    Living,\n    LivingInChildAfterFork,\n  };\n\n  SingletonVault& vault_;\n\n  // mutex protects the entire entry during construction/destruction\n  std::mutex mutex_;\n\n  // State of the singleton entry. If state is Living, instance_ptr and\n  // instance_weak can be safely accessed w/o synchronization.\n  std::atomic<SingletonHolderState> state_{SingletonHolderState::NotRegistered};\n\n  // the thread creating the singleton (only valid while creating an object)\n  std::atomic<std::thread::id> creating_thread_{};\n\n  // The singleton itself and related functions.\n\n  // holds a ReadMostlyMainPtr to singleton instance, set when state is changed\n  // from Dead to Living. Reset when state is changed from Living to Dead.\n  folly::ReadMostlyMainPtr<T> instance_;\n  // used to release all ReadMostlyMainPtrs at once\n  folly::ReadMostlySharedPtr<T> instance_copy_;\n  // per-core shared_ptrs that in turn hold the instance shared_ptr, to avoid\n  // contention in acquiring them.\n  folly::CoreCachedSharedPtr<T> instance_core_cached_;\n  // weak references to the previous pointers. These are never written to after\n  // initialization, so they're safe to read without synchronization once the\n  // state has transitioned to Living.\n  // instance_weak_ is a reference to the main instance, so it is authoritative\n  // on whether the instance is expired.\n  std::weak_ptr<T> instance_weak_;\n  folly::ReadMostlyWeakPtr<T> instance_weak_fast_;\n  folly::CoreCachedWeakPtr<T> instance_weak_core_cached_;\n\n  // Time we wait on destroy_baton after releasing Singleton shared_ptr.\n  std::shared_ptr<folly::Baton<>> destroy_baton_;\n  T* instance_ptr_ = nullptr;\n  CreateFunc create_ = nullptr;\n  TeardownFunc teardown_ = nullptr;\n\n  std::shared_ptr<std::atomic<bool>> print_destructor_stack_trace_;\n\n  SingletonHolder(const SingletonHolder&) = delete;\n  SingletonHolder& operator=(const SingletonHolder&) = delete;\n  SingletonHolder& operator=(SingletonHolder&&) = delete;\n  SingletonHolder(SingletonHolder&&) = delete;\n};\n\n} // namespace detail\n\nclass SingletonVault {\n public:\n  enum class Type {\n    Strict, // Singletons can't be created before registrationComplete()\n    Relaxed, // Singletons can be created before registrationComplete()\n  };\n\n  /**\n   * Clears all singletons in the given vault at ctor and dtor times.\n   * Useful for unit-tests that need to clear the world.\n   *\n   * This need can arise when a unit-test needs to swap out an object used by a\n   * singleton for a test-double, but the singleton needing its dependency to be\n   * swapped has a type or a tag local to some other translation unit and\n   * unavailable in the current translation unit.\n   *\n   * Other, better approaches to this need are \"plz 2 refactor\" ....\n   */\n  struct ScopedExpunger {\n    SingletonVault* vault;\n    explicit ScopedExpunger(SingletonVault* v) : vault(v) { expunge(); }\n    ~ScopedExpunger() { expunge(); }\n    void expunge() {\n      vault->destroyInstances();\n      vault->reenableInstances();\n    }\n  };\n\n  static Type defaultVaultType();\n\n  explicit SingletonVault(Type type = defaultVaultType()) noexcept;\n\n  // Destructor is only called by unit tests to check destroyInstances.\n  ~SingletonVault();\n\n  typedef std::function<void(void*)> TeardownFunc;\n  typedef std::function<void*(void)> CreateFunc;\n\n  // Ensure that Singleton has not been registered previously and that\n  // registration is not complete. If validations succeeds,\n  // register a singleton of a given type with the create and teardown\n  // functions.\n  void registerSingleton(detail::SingletonHolderBase* entry);\n\n  /**\n   * Called by `Singleton<T>.shouldEagerInit()` to ensure the instance\n   * is built when `doEagerInit[Via]` is called; see those methods\n   * for more info.\n   */\n  void addEagerInitSingleton(detail::SingletonHolderBase* entry);\n\n  void addEagerInitOnReenableSingleton(detail::SingletonHolderBase* entry);\n\n  // Mark registration is complete; no more singletons can be\n  // registered at this point.\n  void registrationComplete();\n\n  /**\n   * Initialize all singletons which were marked as eager-initialized\n   * (using `shouldEagerInit()`).  No return value.  Propagates exceptions\n   * from constructors / create functions, as is the usual case when calling\n   * for example `Singleton<Foo>::get_weak()`.\n   */\n  void doEagerInit();\n\n  /**\n   * Schedule eager singletons' initializations through the given executor.\n   * If baton ptr is not null, its `post` method is called after all\n   * early initialization has completed.\n   *\n   * If exceptions are thrown during initialization, this method will still\n   * `post` the baton to indicate completion.  The exception will not propagate\n   * and future attempts to `try_get` or `get_weak` the failed singleton will\n   * retry initialization.\n   *\n   * Sample usage:\n   *\n   *   folly::IOThreadPoolExecutor executor(max_concurrency_level);\n   *   folly::Baton<> done;\n   *   doEagerInitVia(executor, &done);\n   *   done.wait();  // or 'try_wait_for', etc.\n   *\n   */\n  void doEagerInitVia(Executor& exe, folly::Baton<>* done = nullptr);\n\n  // Destroy all singletons; when complete, the vault can't create\n  // singletons once again until reenableInstances() is called.\n  // If reenableInstances() will not be called, destroyInstancesFinal()\n  // should be used instead.\n  void destroyInstances();\n\n  // Enable re-creating singletons after destroyInstances() was called.\n  void reenableInstances();\n\n  // Same as destroyInstances() but reenableInstances() should not be called\n  // after it. Starts a shutdown timer.\n  void destroyInstancesFinal();\n\n  // For testing; how many registered and living singletons we have.\n  size_t registeredSingletonCount() const {\n    return singletons_.rlock()->size();\n  }\n\n  /**\n   * Flips to true if eager initialization was used, and has completed.\n   * Never set to true if \"doEagerInit()\" or \"doEagerInitVia\" never called.\n   */\n  bool eagerInitComplete() const;\n\n  size_t livingSingletonCount() const {\n    auto state = state_.rlock();\n    auto singletons = singletons_.rlock();\n\n    size_t ret = 0;\n    for (const auto& p : *singletons) {\n      if (p.second->hasLiveInstance()) {\n        ++ret;\n      }\n    }\n\n    return ret;\n  }\n\n  // A well-known vault; you can actually have others, but this is the\n  // default.\n  static SingletonVault* singleton() { return singleton<>(); }\n\n  // Gets singleton vault for any Tag. Non-default tag should be used in unit\n  // tests only.\n  template <typename VaultTag = detail::DefaultTag>\n  static SingletonVault* singleton() {\n    return &detail::createGlobal<SingletonVault, VaultTag>();\n  }\n\n  // Get a cancellation token that gets triggered when singleton destruction\n  // starts.\n  //\n  // The underlying cancellation source gets reset when reenableInstances() is\n  // called.\n  folly::CancellationToken getDestructionCancellationToken() {\n    return cancellationSource_.wlock()->getToken();\n  }\n\n  void setType(Type type) { type_.store(type, std::memory_order_relaxed); }\n\n  void setShutdownTimeout(std::chrono::milliseconds shutdownTimeout) {\n    shutdownTimeout_ = shutdownTimeout;\n  }\n\n  void disableShutdownTimeout() {\n    shutdownTimeout_ = std::chrono::milliseconds::zero();\n  }\n\n  void addToShutdownLog(std::string message);\n\n  [[noreturn]] void fireShutdownTimer();\n\n  void setFailOnUseAfterFork(bool failOnUseAfterFork) {\n    failOnUseAfterFork_ = failOnUseAfterFork;\n  }\n\n  bool isDisabled() const { return state_.rlock()->isDisabled(); }\n\n private:\n  template <typename T>\n  friend struct detail::SingletonHolder;\n\n  // This method only matters if registrationComplete() is never called.\n  // Otherwise destroyInstances is scheduled to be executed atexit.\n  //\n  // Initializes static object, which calls destroyInstances on destruction.\n  // Used to have better deletion ordering with singleton not managed by\n  // folly::Singleton. The desruction will happen in the following order:\n  // 1. Singletons, not managed by folly::Singleton, which were created after\n  //    any of the singletons managed by folly::Singleton was requested.\n  // 2. All singletons managed by folly::Singleton\n  // 3. Singletons, not managed by folly::Singleton, which were created before\n  //    any of the singletons managed by folly::Singleton was requested.\n  static void scheduleDestroyInstances();\n\n  void startShutdownTimer();\n\n  typedef std::unordered_map<\n      detail::TypeDescriptor,\n      detail::SingletonHolderBase*,\n      detail::TypeDescriptorHasher>\n      SingletonMap;\n\n  // Use SharedMutexSuppressTSAN to suppress noisy lock inversions when building\n  // with TSAN. If TSAN is not enabled, SharedMutexSuppressTSAN is equivalent\n  // to a normal SharedMutex.\n  Synchronized<SingletonMap, SharedMutexSuppressTSAN> singletons_;\n  Synchronized<\n      std::unordered_set<detail::SingletonHolderBase*>,\n      SharedMutexSuppressTSAN>\n      eagerInitSingletons_;\n  Synchronized<\n      std::unordered_set<detail::SingletonHolderBase*>,\n      SharedMutexSuppressTSAN>\n      eagerInitOnReenableSingletons_;\n  Synchronized<std::vector<detail::TypeDescriptor>, SharedMutexSuppressTSAN>\n      creationOrder_;\n  Synchronized<\n      std::unordered_set<detail::TypeDescriptor, detail::TypeDescriptorHasher>,\n      SharedMutexSuppressTSAN>\n      instantiatedAtLeastOnce_;\n  std::unordered_set<detail::SingletonHolderBase*> liveSingletonsPreFork_;\n\n  // Using SharedMutexReadPriority is important here, because we want to make\n  // sure we don't block nested singleton creation happening concurrently with\n  // destroyInstances().\n  Synchronized<detail::SingletonVaultState, SharedMutexReadPriority> state_;\n\n  std::atomic<Type> type_;\n\n  std::atomic<bool> shutdownTimerStarted_{false};\n  std::chrono::milliseconds shutdownTimeout_{std::chrono::minutes{5}};\n  Synchronized<std::vector<std::string>> shutdownLog_;\n  // We use a lock around CancellationSource to get the guarantee that all\n  // cancellation callbacks that got triggered on requestCancellation() are done\n  // executing by the time we start destruction.  This prevents silent callbacks\n  // that take long to block destruction.\n  folly::Synchronized<CancellationSource> cancellationSource_;\n  bool failOnUseAfterFork_{true};\n};\n\n/**\n * Singleton allows for simple access to registering and instantiating\n * singletons.  Create instances of this class in the global scope of\n * type Singleton<T> to register your singleton for later access via\n * Singleton<T>::try_get().\n *\n * There are many supporting libraries and classes for Singleton; this is the\n * one that users typically interact with.\n */\ntemplate <\n    typename T,\n    typename Tag = detail::DefaultTag,\n    typename VaultTag = detail::DefaultTag /* for testing */>\nclass Singleton {\n public:\n  typedef std::function<T*(void)> CreateFunc;\n  typedef std::function<void(T*)> TeardownFunc;\n\n  /**\n   * Get a pointer to the singleton.\n   *\n   * It is preferable to call get() repeatedly than to store the returned\n   * pointer. Accessing the returned pointer often fails during shutdown.\n   *\n   * Deprecated in favor of try_get().\n   */\n  [[deprecated(\"Replaced by try_get\")]] static T* get() {\n    return getEntry().get();\n  }\n\n  /**\n   * Get a weak_ptr to the singleton.\n   *\n   * If you cannot lock the weak_ptr, this usually means the vault has been\n   * destroyed.\n   *\n   * Deprecated in favor of try_get().\n   */\n  [[deprecated(\"Replaced by try_get\")]] static std::weak_ptr<T> get_weak() {\n    return getEntry().get_weak();\n  }\n\n  /**\n   * Get a shared_ptr to the singleton.\n   *\n   * It is recommended to call try_get() repeatedly, rather than storing the\n   * shared_ptr, because storing the shared_ptr prevents the singleton from\n   * being destroyed during shutdown.\n   */\n  static std::shared_ptr<T> try_get() { return getEntry().try_get(); }\n\n  /**\n   * Get a ReadMostlySharedPtr to the singleton.\n   *\n   * It is recommended to call try_get_fast() repeatedly, rather than storing\n   * the ReadMostlySharedPtr, because storing the ReadMostlySharedPtr prevents\n   * the singleton from being destroyed during shutdown.\n   */\n  static folly::ReadMostlySharedPtr<T> try_get_fast() {\n    return getEntry().try_get_fast();\n  }\n\n  /**\n   * Applies a callback to the possibly-nullptr singleton instance, returning\n   * the callback's result.\n   *\n   * That is, the following two are functionally equivalent:\n   *    singleton.apply(std::ref(f));\n   *    f(singleton.try_get().get());\n   *\n   * For example, the following returns the singleton\n   * instance directly without any extra operations on the instance:\n   * auto ret = Singleton<T>::apply([](auto* v) { return v; });\n   */\n  template <typename Func>\n  static invoke_result_t<Func, T*> apply(Func f) {\n    return getEntry().apply(std::ref(f));\n  }\n\n  /// Ensure the instance exists.\n  static void vivify() { getEntry().vivify(); }\n\n  /**\n   * Create a singleton, which uses the default-constructor for its wrapped\n   * type.\n   *\n   * @param t  The teardown function to use (in lieu of delete).\n   */\n  explicit Singleton(\n      std::nullptr_t /* _ */ = nullptr,\n      typename Singleton::TeardownFunc t = nullptr)\n      : Singleton([]() { return new T; }, std::move(t)) {}\n\n  /**\n   * @param c  The create function to use (in lieu of new).\n   * @param t  The teardown function to use (in lieu of delete).\n   */\n  explicit Singleton(\n      typename Singleton::CreateFunc c,\n      typename Singleton::TeardownFunc t = nullptr) {\n    if (c == nullptr) {\n      detail::singletonThrowNullCreator(typeid(T));\n    }\n\n    auto vault = SingletonVault::singleton<VaultTag>();\n    getEntry().registerSingleton(std::move(c), getTeardownFunc(std::move(t)));\n    vault->registerSingleton(&getEntry());\n  }\n\n  /**\n   * Specify that the singleton should be eagerly initialized.\n   *\n   * Should be instantiated as soon as \"doEagerInit[Via]\" is called.\n   * Singletons are usually lazy-loaded (built on-demand) but for those which\n   * are known to be needed, to avoid the potential lag for objects that take\n   * long to construct during runtime, there is an option to make sure these\n   * are built up-front.\n   *\n   * Use like:\n   *   auto gFooInstance = Singleton<Foo>(...).shouldEagerInit();\n   *\n   * Or alternately, define the singleton as usual, and say\n   *   gFooInstance.shouldEagerInit();\n   *\n   * at some point prior to calling registrationComplete().\n   * Then doEagerInit() or doEagerInitVia(Executor*) can be called.\n   */\n  Singleton& shouldEagerInit() {\n    auto vault = SingletonVault::singleton<VaultTag>();\n    vault->addEagerInitSingleton(&getEntry());\n    return *this;\n  }\n\n  /**\n   * Specify that the singleton should be eagerly initialized when singletons\n   * reenable.\n   *\n   * Should be re-instantiated as soon as reenableInstances() is called.\n   * Note that it will be re-instantiated only if it was instantiated before\n   * destroyInstances() call.\n   *\n   * Use like:\n   *   auto gFooInstance = Singleton<Foo>(...).shouldEagerInitOnReenable();\n   */\n  Singleton& shouldEagerInitOnReenable() {\n    auto vault = SingletonVault::singleton<VaultTag>();\n    vault->addEagerInitOnReenableSingleton(&getEntry());\n    return *this;\n  }\n\n  /**\n   * Inject a mock singleton, for testing.\n   *\n   * Construct and inject a mock singleton which should be used only from tests.\n   * Unlike regular singletons which are initialized once per process lifetime,\n   * mock singletons live for the duration of a test. This means that one\n   * process running multiple tests can initialize and register the same\n   * singleton multiple times. This functionality should be used only from tests\n   * since it relaxes validation and performance in order to be able to perform\n   * the injection. The returned mock singleton is functionality identical to\n   * regular singletons.\n   */\n  static void make_mock(\n      std::nullptr_t /* c */ = nullptr,\n      typename Singleton<T>::TeardownFunc t = nullptr) {\n    make_mock([]() { return new T; }, t);\n  }\n\n  static void make_mock(\n      CreateFunc c, typename Singleton<T>::TeardownFunc t = nullptr) {\n    if (c == nullptr) {\n      detail::singletonThrowNullCreator(typeid(T));\n    }\n\n    auto& entry = getEntry();\n\n    entry.registerSingletonMock(c, getTeardownFunc(t));\n  }\n\n private:\n  inline static detail::SingletonHolder<T>& getEntry() {\n    return detail::SingletonHolder<T>::template singleton<Tag, VaultTag>();\n  }\n\n  // Construct TeardownFunc.\n  static typename detail::SingletonHolder<T>::TeardownFunc getTeardownFunc(\n      TeardownFunc t) {\n    if (t == nullptr) {\n      return [](T* v) { delete v; };\n    } else {\n      return t;\n    }\n  }\n};\n\ntemplate <typename T, typename Tag = detail::DefaultTag>\nclass LeakySingleton {\n public:\n  using CreateFunc = std::function<T*()>;\n\n  LeakySingleton() : LeakySingleton([] { return new T(); }) {}\n\n  explicit LeakySingleton(CreateFunc createFunc) {\n    auto& entry = entryInstance();\n    if (entry.state != State::NotRegistered) {\n      detail::singletonWarnLeakyDoubleRegistrationAndAbort(entry.type_);\n    }\n    entry.createFunc = createFunc;\n    entry.state = State::Dead;\n  }\n\n  static T& get() { return instance(); }\n\n  static void make_mock(std::nullptr_t /* c */ = nullptr) {\n    make_mock([]() { return new T; });\n  }\n\n  static void make_mock(CreateFunc createFunc) {\n    if (createFunc == nullptr) {\n      detail::singletonThrowNullCreator(typeid(T));\n    }\n\n    auto& entry = entryInstance();\n    std::lock_guard lg(entry.mutex);\n    if (entry.ptr) {\n      lsan_ignore_object(std::atomic_exchange(&entry.ptr, (T*)nullptr));\n    }\n    entry.createFunc = createFunc;\n    entry.state = State::Dead;\n  }\n\n private:\n  enum class State { NotRegistered, Dead, Living };\n\n  struct Entry {\n    Entry() noexcept {}\n    Entry(const Entry&) = delete;\n    Entry& operator=(const Entry&) = delete;\n\n    std::atomic<State> state{State::NotRegistered};\n    std::atomic<T*> ptr{nullptr};\n    CreateFunc createFunc;\n    std::mutex mutex;\n    detail::TypeDescriptor type_{typeid(T), typeid(Tag)};\n  };\n\n  static Entry& entryInstance() { return detail::createGlobal<Entry, Tag>(); }\n\n  static T& instance() {\n    auto& entry = entryInstance();\n    if (FOLLY_UNLIKELY(entry.state != State::Living)) {\n      createInstance();\n    }\n\n    return *entry.ptr;\n  }\n\n  static void createInstance() {\n    auto& entry = entryInstance();\n\n    std::lock_guard lg(entry.mutex);\n    if (entry.state == State::Living) {\n      return;\n    }\n\n    if (entry.state == State::NotRegistered) {\n      detail::singletonWarnLeakyInstantiatingNotRegisteredAndAbort(entry.type_);\n    }\n\n    entry.ptr = entry.createFunc();\n    entry.state = State::Living;\n  }\n};\n} // namespace folly\n\n#include <folly/Singleton-inl.h>\n"
  },
  {
    "path": "folly/SingletonThreadLocal.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/SingletonThreadLocal.h>\n\nnamespace folly {\n\nnamespace detail {\n\nFOLLY_NOINLINE SingletonThreadLocalState::Tracking::Tracking() noexcept {}\n\nFOLLY_NOINLINE SingletonThreadLocalState::Tracking::~Tracking() {\n  for (auto& kvp : caches) {\n    kvp.first->object = nullptr;\n  }\n}\n\nFOLLY_NOINLINE void SingletonThreadLocalState::LocalLifetime::destroy(\n    Tracking& tracking) noexcept {\n  auto& lifetimes = tracking.lifetimes[this];\n  for (auto cache : lifetimes) {\n    auto const it = tracking.caches.find(cache);\n    if (!--it->second) {\n      tracking.caches.erase(it);\n      cache->object = nullptr;\n    }\n  }\n  tracking.lifetimes.erase(this);\n}\n\nFOLLY_NOINLINE void SingletonThreadLocalState::LocalLifetime::track(\n    LocalCache& cache, Tracking& tracking, void* object) noexcept {\n  cache.object = object;\n  auto const inserted = tracking.lifetimes[this].insert(&cache);\n  tracking.caches[&cache] += inserted.second;\n}\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/SingletonThreadLocal.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <thread>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n\n#include <folly/ScopeGuard.h>\n#include <folly/ThreadLocal.h>\n#include <folly/detail/Iterators.h>\n#include <folly/detail/Singleton.h>\n#include <folly/detail/UniqueInstance.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Hint.h>\n\nnamespace folly {\n\nnamespace detail {\n\nstruct SingletonThreadLocalState {\n  struct LocalCache {\n    void* object; // type-erased pointer to the object field of wrapper, below\n  };\n  static_assert( // pod avoids tls-init guard var and tls-fini ub use-after-dtor\n      std::is_standard_layout<LocalCache>::value &&\n          std::is_trivial<LocalCache>::value,\n      \"non-pod\");\n\n  struct LocalLifetime;\n\n  struct Tracking {\n    using LocalCacheSet = std::unordered_set<LocalCache*>;\n\n    // per-cache refcounts, the number of lifetimes tracking that cache\n    std::unordered_map<LocalCache*, size_t> caches;\n\n    // per-lifetime cache tracking; 1-M lifetimes may track 1-N caches\n    std::unordered_map<LocalLifetime*, LocalCacheSet> lifetimes;\n\n    Tracking() noexcept;\n    ~Tracking();\n  };\n\n  struct LocalLifetime {\n    void destroy(Tracking& tracking) noexcept;\n    void track(LocalCache& cache, Tracking& tracking, void* object) noexcept;\n  };\n};\n\n} // namespace detail\n\n/// SingletonThreadLocal\n///\n/// Useful for a per-thread leaky-singleton model in libraries and applications.\n///\n/// By \"leaky\" it is meant that the T instances held by the instantiation\n/// SingletonThreadLocal<T> will survive until their owning thread exits.\n/// Therefore, they can safely be used before main() begins and after main()\n/// ends, and they can also safely be used in an application that spawns many\n/// temporary threads throughout its life.\n///\n/// Example:\n///\n///   struct UsefulButHasExpensiveCtor {\n///     UsefulButHasExpensiveCtor(); // this is expensive\n///     Result operator()(Arg arg);\n///   };\n///\n///   Result useful(Arg arg) {\n///     using Useful = UsefulButHasExpensiveCtor;\n///     auto& useful = folly::SingletonThreadLocal<Useful>::get();\n///     return useful(arg);\n///   }\n///\n/// As an example use-case, the random generators in <random> are expensive to\n/// construct. And their constructors are deterministic, but many cases require\n/// that they be randomly seeded. So folly::Random makes good canonical uses of\n/// folly::SingletonThreadLocal so that a seed is computed from the secure\n/// random device once per thread, and the random generator is constructed with\n/// the seed once per thread.\n///\n/// Keywords to help people find this class in search:\n/// Thread Local Singleton ThreadLocalSingleton\ntemplate <\n    typename T,\n    typename Tag = detail::DefaultTag,\n    typename Make = void,\n    typename TLTag = std::\n        conditional_t<std::is_same<Tag, detail::DefaultTag>::value, void, Tag>>\nclass SingletonThreadLocal {\n private:\n  static detail::UniqueInstance unique;\n\n  using State = detail::SingletonThreadLocalState;\n  using LocalCache = State::LocalCache;\n\n  using MakeFn =\n      std::conditional_t<std::is_void_v<Make>, detail::DefaultMake<T>, Make>;\n  using Object = invoke_result_t<MakeFn>;\n  static_assert(std::is_convertible<Object&, T&>::value, \"inconvertible\");\n\n  struct ObjectWrapper {\n    // keep as first field in first base, to save 1 instr in the fast path\n    Object object{MakeFn{}()};\n  };\n  struct Wrapper : ObjectWrapper, State::Tracking {\n    /* implicit */ operator T&() { return ObjectWrapper::object; }\n  };\n\n  using WrapperTL = ThreadLocal<Wrapper, TLTag>;\n\n  struct LocalLifetime : State::LocalLifetime {\n    ~LocalLifetime() { destroy(getWrapper()); }\n  };\n\n  SingletonThreadLocal() = delete;\n\n  FOLLY_ALWAYS_INLINE static WrapperTL& getWrapperTL() {\n    (void)unique; // force the object not to be thrown out as unused\n    return detail::createGlobal<WrapperTL, Tag>();\n  }\n\n  FOLLY_NOINLINE static Wrapper& getWrapper() { return *getWrapperTL(); }\n\n  FOLLY_NOINLINE static Wrapper& getSlow(LocalCache& cache) {\n    auto& wrapper = getWrapper();\n    if (threadlocal_detail::StaticMetaBase::dying()) {\n      return wrapper;\n    }\n    static thread_local LocalLifetime lifetime;\n    lifetime.track(cache, wrapper, &wrapper.object); // idempotent\n    return wrapper;\n  }\n\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static LocalCache& getLocalCache() {\n    static thread_local LocalCache cache;\n    return cache;\n  }\n\n public:\n  FOLLY_ALWAYS_INLINE static T& get() {\n    if (kIsMobile) {\n      return getWrapper();\n    }\n    auto& cache = getLocalCache();\n    auto* object = static_cast<Object*>(cache.object);\n    return FOLLY_LIKELY(!!object) ? *object : getSlow(cache).object;\n  }\n\n  static T* try_get() {\n    if (!kIsMobile) {\n      auto& cache = getLocalCache();\n      if (auto* object = static_cast<Object*>(cache.object)) {\n        return object;\n      }\n    }\n    if (threadlocal_detail::StaticMetaBase::dying()) {\n      return nullptr;\n    }\n    auto* wrapper = getWrapperTL().get_existing();\n    return wrapper ? &static_cast<T&>(*wrapper) : nullptr;\n  }\n\n  class Accessor {\n   private:\n    using Inner = typename WrapperTL::Accessor;\n    using IteratorBase = typename Inner::Iterator;\n    using IteratorTag = typename IteratorBase::iterator_category;\n\n    Inner inner_;\n\n    explicit Accessor(Inner inner) noexcept : inner_(std::move(inner)) {}\n\n   public:\n    friend class SingletonThreadLocal<T, Tag, Make, TLTag>;\n\n    class Iterator\n        : public detail::\n              IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag> {\n     private:\n      using Super =\n          detail::IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag>;\n      using Super::Super;\n\n     public:\n      friend class Accessor;\n\n      T& dereference() const {\n        return const_cast<Iterator*>(this)->base()->object;\n      }\n\n      std::thread::id getThreadId() const { return this->base().getThreadId(); }\n\n      uint64_t getOSThreadId() const { return this->base().getOSThreadId(); }\n    };\n\n    Accessor(const Accessor&) = delete;\n    Accessor& operator=(const Accessor&) = delete;\n    Accessor(Accessor&&) = default;\n    Accessor& operator=(Accessor&&) = default;\n\n    Iterator begin() const { return Iterator(inner_.begin()); }\n\n    Iterator end() const { return Iterator(inner_.end()); }\n  };\n\n  // Must use a unique Tag, takes a lock that is one per Tag\n  static Accessor accessAllThreads() {\n    return Accessor(getWrapperTL().accessAllThreads());\n  }\n};\n\nFOLLY_PUSH_WARNING\nFOLLY_CLANG_DISABLE_WARNING(\"-Wglobal-constructors\")\ntemplate <typename T, typename Tag, typename Make, typename TLTag>\ndetail::UniqueInstance SingletonThreadLocal<T, Tag, Make, TLTag>::unique{\n    tag<SingletonThreadLocal>, tag<T, Tag>, tag<Make, TLTag>};\nFOLLY_POP_WARNING\n\n} // namespace folly\n\n/// FOLLY_DECLARE_REUSED\n///\n/// Useful for local variables of container types, where it is desired to avoid\n/// the overhead associated with the local variable entering and leaving scope.\n/// Rather, where it is desired that the memory be reused between invocations\n/// of the same scope in the same thread rather than deallocated and reallocated\n/// between invocations of the same scope in the same thread. Note that the\n/// container will always be cleared between invocations; it is only the backing\n/// memory allocation which is reused.\n///\n/// Example:\n///\n///   void traverse_perform(int root);\n///   template <typename F>\n///   void traverse_each_child_r(int root, F const&);\n///   void traverse_depthwise(int root) {\n///     // preserves some of the memory backing these per-thread data structures\n///     FOLLY_DECLARE_REUSED(seen, std::unordered_set<int>);\n///     FOLLY_DECLARE_REUSED(work, std::vector<int>);\n///     // example algorithm that uses these per-thread data structures\n///     work.push_back(root);\n///     while (!work.empty()) {\n///       root = work.back();\n///       work.pop_back();\n///       seen.insert(root);\n///       traverse_perform(root);\n///       traverse_each_child_r(root, [&](int item) {\n///         if (!seen.count(item)) {\n///           work.push_back(item);\n///         }\n///       });\n///     }\n///   }\n#define FOLLY_DECLARE_REUSED(name, ...)                                        \\\n  struct __folly_reused_type_##name {                                          \\\n    __VA_ARGS__ object;                                                        \\\n  };                                                                           \\\n  [[maybe_unused]] ::folly::unsafe_for_async_usage                             \\\n      __folly_reused_g_prevent_async_##name;                                   \\\n  auto& name =                                                                 \\\n      ::folly::SingletonThreadLocal<__folly_reused_type_##name>::get().object; \\\n  auto __folly_reused_g_##name = ::folly::makeGuard([&] { name.clear(); })\n"
  },
  {
    "path": "folly/SocketAddress.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include <folly/SocketAddress.h>\n\n#include <cassert>\n#include <cerrno>\n#include <cstdio>\n#include <cstring>\n#include <sstream>\n#include <string>\n#include <system_error>\n#include <type_traits>\n\n#include <boost/functional/hash.hpp>\n\n#include <fmt/core.h>\n\n#include <folly/Exception.h>\n#include <folly/hash/Hash.h>\n#include <folly/net/NetOps.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace {\n\n/**\n * A structure to free a struct addrinfo when it goes out of scope.\n */\nstruct ScopedAddrInfo {\n  explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}\n  ~ScopedAddrInfo() { freeaddrinfo(info); }\n\n  struct addrinfo* info;\n};\n\n/**\n * A simple data structure for parsing a host-and-port string.\n *\n * Accepts a string of the form \"<host>:<port>\" or just \"<port>\",\n * and contains two string pointers to the host and the port portion of the\n * string.\n *\n * The HostAndPort may contain pointers into the original string.  It is\n * responsible for the user to ensure that the input string is valid for the\n * lifetime of the HostAndPort structure.\n */\nstruct HostAndPort {\n  HostAndPort(const char* str, bool hostRequired)\n      : host(nullptr), port(nullptr), allocated(nullptr) {\n    // Look for the last colon\n    const char* colon = strrchr(str, ':');\n    if (colon == nullptr) {\n      // No colon, just a port number.\n      if (hostRequired) {\n        throw std::invalid_argument(\n            \"expected a host and port string of the \"\n            \"form \\\"<host>:<port>\\\"\");\n      }\n      port = str;\n      return;\n    }\n\n    // We have to make a copy of the string so we can modify it\n    // and change the colon to a NUL terminator.\n    allocated = strdup(str);\n    if (!allocated) {\n      throw std::bad_alloc();\n    }\n\n    char* allocatedColon = allocated + (colon - str);\n    *allocatedColon = '\\0';\n    host = allocated;\n    port = allocatedColon + 1;\n    // bracketed IPv6 address, remove the brackets\n    // allocatedColon[-1] is fine, as allocatedColon >= host and\n    // *allocatedColon != *host therefore allocatedColon > host\n    if (*host == '[' && allocatedColon[-1] == ']') {\n      allocatedColon[-1] = '\\0';\n      ++host;\n    }\n  }\n\n  ~HostAndPort() { free(allocated); }\n\n  const char* host;\n  const char* port;\n  char* allocated;\n};\n\nstruct GetAddrInfoError {\n#ifdef _WIN32\n  std::string error;\n  const char* str() const { return error.c_str(); }\n  explicit GetAddrInfoError(int errorCode) {\n    auto s = gai_strerror(errorCode);\n    using Char = std::remove_reference_t<decltype(*s)>;\n    error.assign(s, s + std::char_traits<Char>::length(s));\n  }\n#else\n  const char* error;\n  const char* str() const { return error ? error : \"Unknown error\"; }\n  explicit GetAddrInfoError(int errorCode) : error(gai_strerror(errorCode)) {}\n#endif\n};\n\n} // namespace\n\nnamespace folly {\n\nbool SocketAddress::isPrivateAddress() const {\n  if (holdsInet()) {\n    return std::get<IPAddr>(storage_).ip.isPrivate();\n  } else {\n    // Unix and vsock addresses are always local to a host.  Return true,\n    // since this conforms to the semantics of returning true for IP loopback\n    // addresses.\n    return true;\n  }\n}\n\nbool SocketAddress::isLoopbackAddress() const {\n  if (holdsInet()) {\n    return std::get<IPAddr>(storage_).ip.isLoopback();\n#if FOLLY_HAVE_VSOCK\n  } else if (holdsVsock()) {\n    // VSOCK addresses with CID_LOCAL are considered loopback\n    const auto& vsockAddr = std::get<VsockAddr>(storage_);\n    return vsockAddr.cid == VMADDR_CID_LOCAL;\n#endif\n  } else {\n    // Return true for UNIX addresses, since they are always local to a host.\n    return true;\n  }\n}\n\nvoid SocketAddress::setFromHostPort(const char* host, uint16_t port) {\n  ScopedAddrInfo results(getAddrInfo(host, port, 0));\n  setFromAddrInfo(results.info);\n}\n\nvoid SocketAddress::setFromIpPort(const char* ip, uint16_t port) {\n  ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST));\n  setFromAddrInfo(results.info);\n}\n\nvoid SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) {\n  storage_ = IPAddr(ipAddr, port);\n}\n\nvoid SocketAddress::setFromLocalPort(uint16_t port) {\n  ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));\n  setFromLocalAddr(results.info);\n}\n\nvoid SocketAddress::setFromLocalPort(const char* port) {\n  ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));\n  setFromLocalAddr(results.info);\n}\n\nvoid SocketAddress::setFromLocalIpPort(const char* addressAndPort) {\n  HostAndPort hp(addressAndPort, false);\n  ScopedAddrInfo results(\n      getAddrInfo(hp.host, hp.port, AI_NUMERICHOST | AI_ADDRCONFIG));\n  setFromLocalAddr(results.info);\n}\n\nvoid SocketAddress::setFromIpPort(const char* addressAndPort) {\n  HostAndPort hp(addressAndPort, true);\n  ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST));\n  setFromAddrInfo(results.info);\n}\n\nvoid SocketAddress::setFromHostPort(const char* hostAndPort) {\n  HostAndPort hp(hostAndPort, true);\n  ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0));\n  setFromAddrInfo(results.info);\n}\n\n#if FOLLY_HAVE_VSOCK\nvoid SocketAddress::setFromVsockCIDPort(uint32_t cid, uint32_t port) {\n  storage_ = VsockAddr(cid, port);\n}\n#endif\n\nint SocketAddress::getPortFrom(const struct sockaddr* address) {\n  switch (address->sa_family) {\n    case AF_INET:\n      return ntohs(((sockaddr_in*)address)->sin_port);\n\n    case AF_INET6:\n      return ntohs(((sockaddr_in6*)address)->sin6_port);\n\n#if FOLLY_HAVE_VSOCK\n    case AF_VSOCK:\n      return ((sockaddr_vm*)address)->svm_port;\n#endif\n\n    default:\n      return -1;\n  }\n}\n\nconst char* SocketAddress::getFamilyNameFrom(\n    const struct sockaddr* address, const char* defaultResult) {\n#define GETFAMILYNAMEFROM_IMPL(Family) \\\n  case Family:                         \\\n    return #Family\n\n  switch ((int)address->sa_family) {\n    GETFAMILYNAMEFROM_IMPL(AF_INET);\n    GETFAMILYNAMEFROM_IMPL(AF_INET6);\n    GETFAMILYNAMEFROM_IMPL(AF_UNIX);\n#if FOLLY_HAVE_VSOCK\n    GETFAMILYNAMEFROM_IMPL(AF_VSOCK);\n#endif\n    GETFAMILYNAMEFROM_IMPL(AF_UNSPEC);\n\n    default:\n      return defaultResult;\n  }\n\n#undef GETFAMILYNAMEFROM_IMPL\n}\n\nvoid SocketAddress::setFromPath(StringPiece path) {\n  // Before we touch storage_, check to see if the length is too big.\n  if (path.size() > sizeof(ExternalUnixAddr().addr->sun_path)) {\n    throw std::invalid_argument(\n        \"socket path too large to fit into sockaddr_un\");\n  }\n\n  // Create a new ExternalUnixAddr if we don't already have one\n  if (!holdsUnix()) {\n    storage_ = ExternalUnixAddr();\n  }\n\n  auto& unixAddr = std::get<ExternalUnixAddr>(storage_);\n  size_t len = path.size();\n  unixAddr.len = socklen_t(offsetof(struct sockaddr_un, sun_path) + len);\n  memcpy(unixAddr.addr->sun_path, path.data(), len);\n  // If there is room, put a terminating NUL byte in sun_path.  In general the\n  // path should be NUL terminated, although getsockname() and getpeername()\n  // may return Unix socket addresses with paths that fit exactly in sun_path\n  // with no terminating NUL.\n  if (len < sizeof(unixAddr.addr->sun_path)) {\n    unixAddr.addr->sun_path[len] = '\\0';\n  }\n}\n\nvoid SocketAddress::setFromPeerAddress(NetworkSocket socket) {\n  setFromSocket(socket, netops::getpeername);\n}\n\nvoid SocketAddress::setFromLocalAddress(NetworkSocket socket) {\n  setFromSocket(socket, netops::getsockname);\n}\n\nvoid SocketAddress::setFromSockaddr(const struct sockaddr* address) {\n  uint16_t port;\n\n  if (address->sa_family == AF_INET) {\n    port = ntohs(((sockaddr_in*)address)->sin_port);\n  } else if (address->sa_family == AF_INET6) {\n    port = ntohs(((sockaddr_in6*)address)->sin6_port);\n  } else if (address->sa_family == AF_UNIX) {\n    // We need an explicitly specified length for AF_UNIX addresses,\n    // to be able to distinguish anonymous addresses from addresses\n    // in Linux's abstract namespace.\n    throw std::invalid_argument(\n        \"SocketAddress::setFromSockaddr(): the address \"\n        \"length must be explicitly specified when \"\n        \"setting AF_UNIX addresses\");\n#if FOLLY_HAVE_VSOCK\n  } else if (address->sa_family == AF_VSOCK) {\n    // For VSOCK addresses, store the CID and port in the VsockAddr\n    const auto* vsockAddr = reinterpret_cast<const sockaddr_vm*>(address);\n    storage_ = VsockAddr(vsockAddr->svm_cid, vsockAddr->svm_port);\n    return;\n#endif\n  } else {\n    throw std::invalid_argument(\n        fmt::format(\n            \"SocketAddress::setFromSockaddr() called \"\n            \"with unsupported address type {}\",\n            address->sa_family));\n  }\n\n  // For IP addresses, use the IPAddress constructor\n  storage_ = IPAddr(folly::IPAddress(address), port);\n}\n\nvoid SocketAddress::setFromSockaddr(\n    const struct sockaddr* address, socklen_t addrlen) {\n  // Check the length to make sure we can access address->sa_family\n  if (addrlen <\n      (offsetof(struct sockaddr, sa_family) + sizeof(address->sa_family))) {\n    throw std::invalid_argument(\n        \"SocketAddress::setFromSockaddr() called \"\n        \"with length too short for a sockaddr\");\n  }\n\n  if (address->sa_family == AF_INET) {\n    if (addrlen < sizeof(struct sockaddr_in)) {\n      throw std::invalid_argument(\n          \"SocketAddress::setFromSockaddr() called \"\n          \"with length too short for a sockaddr_in\");\n    }\n    setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address));\n  } else if (address->sa_family == AF_INET6) {\n    if (addrlen < sizeof(struct sockaddr_in6)) {\n      throw std::invalid_argument(\n          \"SocketAddress::setFromSockaddr() called \"\n          \"with length too short for a sockaddr_in6\");\n    }\n    setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address));\n  } else if (address->sa_family == AF_UNIX) {\n    setFromSockaddr(\n        reinterpret_cast<const struct sockaddr_un*>(address), addrlen);\n#if FOLLY_HAVE_VSOCK\n  } else if (address->sa_family == AF_VSOCK) {\n    setFromSockaddr(reinterpret_cast<const struct sockaddr_vm*>(address));\n#endif\n  } else {\n    throw std::invalid_argument(\n        \"SocketAddress::setFromSockaddr() called \"\n        \"with unsupported address type\");\n  }\n}\n\nvoid SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {\n  assert(address->sin_family == AF_INET);\n  setFromSockaddr((sockaddr*)address);\n}\n\nvoid SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {\n  assert(address->sin6_family == AF_INET6);\n  setFromSockaddr((sockaddr*)address);\n}\n\nvoid SocketAddress::setFromSockaddr(\n    const struct sockaddr_un* address, socklen_t addrlen) {\n  assert(address->sun_family == AF_UNIX);\n  if (addrlen > sizeof(struct sockaddr_un)) {\n    throw std::invalid_argument(\n        \"SocketAddress::setFromSockaddr() called \"\n        \"with length too long for a sockaddr_un\");\n  }\n\n  // Create a new ExternalUnixAddr if we don't already have one\n  if (!holdsUnix()) {\n    storage_ = ExternalUnixAddr();\n  }\n\n  auto& unixAddr = std::get<ExternalUnixAddr>(storage_);\n  memcpy(unixAddr.addr, address, size_t(addrlen));\n  updateUnixAddressLength(addrlen);\n\n  // Fill the rest with 0s, just for safety\n  if (addrlen < sizeof(struct sockaddr_un)) {\n    auto p = reinterpret_cast<char*>(unixAddr.addr);\n    memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);\n  }\n}\n\n#if FOLLY_HAVE_VSOCK\nvoid SocketAddress::setFromSockaddr(const struct sockaddr_vm* address) {\n  assert(address->svm_family == AF_VSOCK);\n  storage_ = VsockAddr(address->svm_cid, address->svm_port);\n}\n#endif\n\nconst folly::IPAddress& SocketAddress::getIPAddress() const {\n  auto family = getFamily();\n  if (family != AF_INET && family != AF_INET6) {\n    throw InvalidAddressFamilyException(family);\n  }\n  return std::get<IPAddr>(storage_).ip;\n}\n\nsocklen_t SocketAddress::getActualSize() const {\n  switch (getFamily()) {\n    case AF_UNSPEC:\n    case AF_INET:\n      return sizeof(struct sockaddr_in);\n    case AF_INET6:\n      return sizeof(struct sockaddr_in6);\n    case AF_UNIX:\n      return std::get<ExternalUnixAddr>(storage_).len;\n#if FOLLY_HAVE_VSOCK\n    case AF_VSOCK:\n      return sizeof(struct sockaddr_vm);\n#endif\n    default:\n      throw std::invalid_argument(\n          \"SocketAddress::getActualSize() called \"\n          \"with unrecognized address family\");\n  }\n}\n\nstd::string SocketAddress::getFullyQualified() const {\n  if (!isFamilyInet()) {\n    throw std::invalid_argument(\"Can't get address str for non ip address\");\n  }\n  return std::get<IPAddr>(storage_).ip.toFullyQualified();\n}\n\nstd::string SocketAddress::getAddressStr() const {\n  if (!isFamilyInet()) {\n    throw std::invalid_argument(\"Can't get address str for non ip address\");\n  }\n  return std::get<IPAddr>(storage_).ip.str();\n}\n\nbool SocketAddress::isFamilyInet() const {\n  auto family = getFamily();\n  return family == AF_INET || family == AF_INET6;\n}\n\nvoid SocketAddress::getAddressStr(char* buf, size_t buflen) const {\n  auto ret = getAddressStr();\n  size_t len = std::min(buflen - 1, ret.size());\n  memcpy(buf, ret.data(), len);\n  buf[len] = '\\0';\n}\n\nuint16_t SocketAddress::getPort() const {\n  switch (getFamily()) {\n    case AF_INET:\n    case AF_INET6:\n      return std::get<IPAddr>(storage_).port;\n    default:\n      throw std::invalid_argument(\n          \"SocketAddress::getPort() called on non-IP \"\n          \"address\");\n  }\n}\n\n#if FOLLY_HAVE_VSOCK\nuint32_t SocketAddress::getVsockPort() const {\n  switch (getFamily()) {\n    case AF_VSOCK:\n      return std::get<VsockAddr>(storage_).port;\n    default:\n      throw std::invalid_argument(\n          \"SocketAddress::getVsockPort() called on non-VSOCK address\");\n  }\n}\n#endif\n\nvoid SocketAddress::setPort(uint16_t port) {\n  switch (getFamily()) {\n    case AF_INET:\n    case AF_INET6:\n      std::get<IPAddr>(storage_).port = port;\n      return;\n    default:\n      throw std::invalid_argument(\n          \"SocketAddress::setPort() called on non-IP \"\n          \"address\");\n  }\n}\n\nvoid SocketAddress::convertToIPv4() {\n  if (!tryConvertToIPv4()) {\n    throw std::invalid_argument(\n        \"convertToIPv4() called on an address that is \"\n        \"not an IPv4-mapped address\");\n  }\n}\n\nbool SocketAddress::tryConvertToIPv4() {\n  if (!isIPv4Mapped()) {\n    return false;\n  }\n\n  auto& ipAddr = std::get<IPAddr>(storage_);\n  ipAddr.ip = folly::IPAddress::createIPv4(ipAddr.ip);\n  return true;\n}\n\nbool SocketAddress::mapToIPv6() {\n  if (getFamily() != AF_INET) {\n    return false;\n  }\n\n  auto& ipAddr = std::get<IPAddr>(storage_);\n  ipAddr.ip = folly::IPAddress::createIPv6(ipAddr.ip);\n  return true;\n}\n\nstd::string SocketAddress::getHostStr() const {\n  return getIpString(0);\n}\n\nstd::string SocketAddress::getPath() const {\n  if (!holdsUnix()) {\n    throw std::invalid_argument(\n        \"SocketAddress: attempting to get path \"\n        \"for a non-Unix address\");\n  }\n\n  const auto& unixAddr = std::get<ExternalUnixAddr>(storage_);\n  if (unixAddr.pathLength() == 0) {\n    // anonymous address\n    return std::string();\n  }\n  if (unixAddr.addr->sun_path[0] == '\\0') {\n    // abstract namespace\n    return std::string(unixAddr.addr->sun_path, size_t(unixAddr.pathLength()));\n  }\n\n  return std::string(\n      unixAddr.addr->sun_path,\n      strnlen(unixAddr.addr->sun_path, size_t(unixAddr.pathLength())));\n}\n\n#if FOLLY_HAVE_VSOCK\nuint32_t SocketAddress::getVsockCID() const {\n  if (!holdsVsock()) {\n    throw std::invalid_argument(\n        \"SocketAddress: attempting to get CID \"\n        \"for a non-VSOCK address\");\n  }\n\n  return std::get<VsockAddr>(storage_).cid;\n}\n#endif\n\nstd::string SocketAddress::describe() const {\n  if (holdsUnix()) {\n    const auto& unixAddr = std::get<ExternalUnixAddr>(storage_);\n    if (unixAddr.pathLength() == 0) {\n      return \"<anonymous unix address>\";\n    }\n\n    if (unixAddr.addr->sun_path[0] == '\\0') {\n      // Linux supports an abstract namespace for unix socket addresses\n      return \"<abstract unix address>\";\n    }\n\n    return std::string(\n        unixAddr.addr->sun_path,\n        strnlen(unixAddr.addr->sun_path, size_t(unixAddr.pathLength())));\n  }\n  switch (getFamily()) {\n    case AF_UNSPEC:\n      return \"<uninitialized address>\";\n    case AF_INET: {\n      char buf[NI_MAXHOST + 16];\n      getAddressStr(buf, sizeof(buf));\n      size_t iplen = strlen(buf);\n      snprintf(buf + iplen, sizeof(buf) - iplen, \":%\" PRIu16, getPort());\n      return buf;\n    }\n    case AF_INET6: {\n      char buf[NI_MAXHOST + 18];\n      buf[0] = '[';\n      getAddressStr(buf + 1, sizeof(buf) - 1);\n      size_t iplen = strlen(buf);\n      snprintf(buf + iplen, sizeof(buf) - iplen, \"]:%\" PRIu16, getPort());\n      return buf;\n    }\n#if FOLLY_HAVE_VSOCK\n    case AF_VSOCK: {\n      char buf[32];\n      const auto& vsockAddr = std::get<VsockAddr>(storage_);\n      auto* maybeName = vsockAddr.getMappedName();\n      if (maybeName) {\n        snprintf(\n            buf, sizeof(buf), \"[%s:%\" PRIu32 \"]\", maybeName, vsockAddr.port);\n      } else {\n        snprintf(\n            buf,\n            sizeof(buf),\n            \"[%\" PRIu32 \":%\" PRIu32 \"]\",\n            vsockAddr.cid,\n            vsockAddr.port);\n      }\n      return buf;\n    }\n#endif\n    default: {\n      char buf[64];\n      snprintf(buf, sizeof(buf), \"<unknown address family %d>\", getFamily());\n      return buf;\n    }\n  }\n}\n\nbool SocketAddress::operator==(const SocketAddress& other) const {\n  if (other.getFamily() != getFamily()) {\n    return false;\n  }\n\n  if (holdsUnix()) {\n    const auto& thisUnixAddr = std::get<ExternalUnixAddr>(storage_);\n    const auto& otherUnixAddr = std::get<ExternalUnixAddr>(other.storage_);\n\n    // anonymous addresses are never equal to any other addresses\n    if (thisUnixAddr.pathLength() == 0 || otherUnixAddr.pathLength() == 0) {\n      return false;\n    }\n\n    if (thisUnixAddr.len != otherUnixAddr.len) {\n      return false;\n    }\n    int cmp = memcmp(\n        thisUnixAddr.addr->sun_path,\n        otherUnixAddr.addr->sun_path,\n        size_t(thisUnixAddr.pathLength()));\n    return cmp == 0;\n  }\n\n  switch (getFamily()) {\n    case AF_INET:\n    case AF_INET6:\n      return (std::get<IPAddr>(other.storage_).ip ==\n              std::get<IPAddr>(storage_).ip) &&\n          (std::get<IPAddr>(other.storage_).port ==\n           std::get<IPAddr>(storage_).port);\n#if FOLLY_HAVE_VSOCK\n    case AF_VSOCK:\n      return (std::get<VsockAddr>(other.storage_).cid ==\n              std::get<VsockAddr>(storage_).cid) &&\n          (std::get<VsockAddr>(other.storage_).port ==\n           std::get<VsockAddr>(storage_).port);\n#endif\n    case AF_UNSPEC:\n      return std::get<IPAddr>(other.storage_).ip.empty();\n    default:\n      throw_exception<std::invalid_argument>(\n          \"SocketAddress: unsupported address family for comparison\");\n  }\n}\n\nbool SocketAddress::prefixMatch(\n    const SocketAddress& other, unsigned prefixLength) const {\n  if (other.getFamily() != getFamily()) {\n    return false;\n  }\n  uint8_t mask_length = 128;\n  switch (getFamily()) {\n    case AF_INET:\n      mask_length = 32;\n      [[fallthrough]];\n    case AF_INET6: {\n      auto prefix = folly::IPAddress::longestCommonPrefix(\n          {std::get<IPAddr>(storage_).ip, mask_length},\n          {std::get<IPAddr>(other.storage_).ip, mask_length});\n      return prefix.second >= prefixLength;\n    }\n    default:\n      return false;\n  }\n}\n\nsize_t SocketAddress::hash() const {\n  size_t seed = folly::hash::twang_mix64(getFamily());\n\n  if (holdsUnix()) {\n    const auto& unixAddr = std::get<ExternalUnixAddr>(storage_);\n    enum { kUnixPathMax = sizeof(unixAddr.addr->sun_path) };\n    const char* path = unixAddr.addr->sun_path;\n    auto pathLength = unixAddr.pathLength();\n    // TODO: this probably could be made more efficient\n    for (off_t n = 0; n < pathLength; ++n) {\n      boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n])));\n    }\n  }\n\n  switch ((int)getFamily()) {\n    case AF_INET:\n    case AF_INET6: {\n      boost::hash_combine(seed, std::get<IPAddr>(storage_).port);\n      boost::hash_combine(seed, std::get<IPAddr>(storage_).ip.hash());\n      break;\n    }\n#if FOLLY_HAVE_VSOCK\n    case AF_VSOCK: {\n      boost::hash_combine(seed, std::get<VsockAddr>(storage_).port);\n      boost::hash_combine(seed, std::get<VsockAddr>(storage_).cid);\n      break;\n    }\n#endif\n    case AF_UNIX:\n      // Already handled above\n      break;\n    case AF_UNSPEC:\n      boost::hash_combine(seed, std::get<IPAddr>(storage_).ip.hash());\n      break;\n    default:\n      throw_exception<std::invalid_argument>(\n          \"SocketAddress: unsupported address family for comparison\");\n  }\n\n  return seed;\n}\n\nstruct addrinfo* SocketAddress::getAddrInfo(\n    const char* host, uint16_t port, int flags) {\n  // getaddrinfo() requires the port number as a string\n  char portString[sizeof(\"65535\")];\n  snprintf(portString, sizeof(portString), \"%\" PRIu16, port);\n\n  return getAddrInfo(host, portString, flags);\n}\n\nstruct addrinfo* SocketAddress::getAddrInfo(\n    const char* host, const char* port, int flags) {\n  struct addrinfo hints;\n  memset(&hints, 0, sizeof(hints));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags;\n\n  struct addrinfo* results;\n  int error = getaddrinfo(host, port, &hints, &results);\n  if (error != 0) {\n    auto os = fmt::format(\n        \"Failed to resolve address for '{}': {} (error={})\",\n        (host ? host : \"<null>\"),\n        GetAddrInfoError(error).str(),\n        error);\n    throw std::system_error(error, std::generic_category(), os);\n  }\n\n  return results;\n}\n\nvoid SocketAddress::setFromAddrInfo(const struct addrinfo* info) {\n  setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));\n}\n\nvoid SocketAddress::setFromLocalAddr(const struct addrinfo* info) {\n  // If an IPv6 address is present, prefer to use it, since IPv4 addresses\n  // can be mapped into IPv6 space.\n  for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {\n    if (ai->ai_family == AF_INET6) {\n      setFromSockaddr(ai->ai_addr, socklen_t(ai->ai_addrlen));\n      return;\n    }\n  }\n\n  // Otherwise, just use the first address in the list.\n  setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));\n}\n\nvoid SocketAddress::setFromSocket(\n    NetworkSocket socket,\n    int (*fn)(NetworkSocket, struct sockaddr*, socklen_t*)) {\n  // Try to put the address into a local storage buffer.\n  sockaddr_storage tmp_sock;\n  socklen_t addrLen = sizeof(tmp_sock);\n  if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {\n    folly::throwSystemError(\"setFromSocket() failed\");\n  }\n\n  setFromSockaddr((sockaddr*)&tmp_sock, addrLen);\n}\n\nstd::string SocketAddress::getIpString(int flags) const {\n  char addrString[NI_MAXHOST];\n  getIpString(addrString, sizeof(addrString), flags);\n  return std::string(addrString);\n}\n\nvoid SocketAddress::getIpString(char* buf, size_t buflen, int flags) const {\n  auto family = getFamily();\n  if (family != AF_INET && family != AF_INET6) {\n    throw std::invalid_argument(\n        \"SocketAddress: attempting to get IP address \"\n        \"for a non-IP address\");\n  }\n\n  sockaddr_storage tmp_sock;\n  std::get<IPAddr>(storage_).ip.toSockaddrStorage(\n      &tmp_sock, std::get<IPAddr>(storage_).port);\n  int rc = getnameinfo(\n      (sockaddr*)&tmp_sock,\n      sizeof(sockaddr_storage),\n      buf,\n      buflen,\n      nullptr,\n      0,\n      flags);\n  if (rc != 0) {\n    auto os = fmt::format(\n        \"getnameinfo() failed in getIpString() error = {}\",\n        GetAddrInfoError(rc).str());\n    throw std::system_error(rc, std::generic_category(), os);\n  }\n}\n\nvoid SocketAddress::updateUnixAddressLength(socklen_t addrlen) {\n  if (addrlen < offsetof(struct sockaddr_un, sun_path)) {\n    throw std::invalid_argument(\n        \"SocketAddress: attempted to set a Unix socket \"\n        \"with a length too short for a sockaddr_un\");\n  }\n\n  auto& unixAddr = std::get<ExternalUnixAddr>(storage_);\n  unixAddr.len = addrlen;\n  if (unixAddr.pathLength() == 0) {\n    // anonymous address\n    return;\n  }\n\n  if (unixAddr.addr->sun_path[0] == '\\0') {\n    // abstract namespace.  honor the specified length\n  } else {\n    // Call strnlen(), just in case the length was overspecified.\n    size_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);\n    size_t pathLength = strnlen(unixAddr.addr->sun_path, maxLength);\n    unixAddr.len =\n        socklen_t(offsetof(struct sockaddr_un, sun_path) + pathLength);\n  }\n}\n\nbool SocketAddress::operator<(const SocketAddress& other) const {\n  if (getFamily() != other.getFamily()) {\n    return getFamily() < other.getFamily();\n  }\n\n  if (holdsUnix()) {\n    // Anonymous addresses can't be compared to anything else.\n    // Return that they are never less than anything.\n    //\n    // Note that this still meets the requirements for a strict weak\n    // ordering, so we can use this operator<() with standard C++\n    // containers.\n    const auto& thisUnixAddr = std::get<ExternalUnixAddr>(storage_);\n    auto thisPathLength = thisUnixAddr.pathLength();\n    if (thisPathLength == 0) {\n      return false;\n    }\n    const auto& otherUnixAddr = std::get<ExternalUnixAddr>(other.storage_);\n    auto otherPathLength = otherUnixAddr.pathLength();\n    if (otherPathLength == 0) {\n      return true;\n    }\n\n    // Compare based on path length first, for efficiency\n    if (thisPathLength != otherPathLength) {\n      return thisPathLength < otherPathLength;\n    }\n    int cmp = memcmp(\n        thisUnixAddr.addr->sun_path,\n        otherUnixAddr.addr->sun_path,\n        size_t(thisPathLength));\n    return cmp < 0;\n  }\n  switch (getFamily()) {\n    case AF_INET:\n    case AF_INET6: {\n      auto& thisAddr = std::get<IPAddr>(storage_);\n      auto& otherAddr = std::get<IPAddr>(other.storage_);\n      if (thisAddr.port != otherAddr.port) {\n        return thisAddr.port < otherAddr.port;\n      }\n\n      return thisAddr.ip < otherAddr.ip;\n    }\n    case AF_UNSPEC:\n    default:\n      throw std::invalid_argument(\n          \"SocketAddress: unsupported address family for comparing\");\n  }\n}\n\n#if FOLLY_HAVE_VSOCK\nconst char* SocketAddress::VsockAddr::getMappedName() const {\n  // Use special names for well-known CIDs\n  if (cid == VMADDR_CID_ANY) {\n    return \"any\";\n  } else if (cid == VMADDR_CID_HYPERVISOR) {\n    return \"hypervisor\";\n  } else if (cid == VMADDR_CID_LOCAL) {\n    return \"local\";\n  } else if (cid == VMADDR_CID_HOST) {\n    return \"host\";\n  } else {\n    return nullptr;\n  }\n}\n#endif\n\nsize_t hash_value(const SocketAddress& address) {\n  return address.hash();\n}\n\nstd::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {\n  os << addr.describe();\n  return os;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/SocketAddress.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <cstddef>\n#include <iosfwd>\n#include <string>\n\n#include <variant>\n#include <folly/IPAddress.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/Sockets.h>\n\n#if FOLLY_HAVE_VSOCK\n#include <linux/vm_sockets.h>\n#endif\n\nnamespace folly {\n\n/**\n * Provides a unified interface for socket addresses.\n *\n * @class folly::SocketAddress\n *\n */\n\nclass SocketAddress {\n public:\n  SocketAddress() = default;\n\n  /**\n   * Construct a SocketAddress from a hostname and port.\n   *\n   * Note: If the host parameter is not a numeric IP address, hostname\n   * resolution will be performed, which can be quite slow.\n   *\n   * Raises std::system_error on error.\n   *\n   * @param host The IP address (or hostname, if allowNameLookup is true)\n   * @param port The port (in host byte order)\n   * @param allowNameLookup  If true, attempt to perform hostname lookup\n   *        if the hostname does not appear to be a numeric IP address.\n   *        This is potentially a very slow operation, so is disabled by\n   *        default.\n   */\n  SocketAddress(const char* host, uint16_t port, bool allowNameLookup = false) {\n    // Initialize the address family first,\n    // since setFromHostPort() and setFromIpPort() will check it.\n\n    if (allowNameLookup) {\n      setFromHostPort(host, port);\n    } else {\n      setFromIpPort(host, port);\n    }\n  }\n  /**\n   * Similar to the constructor which accepts hostname and port.\n   * This variant accepts host as std::string.\n   */\n  SocketAddress(\n      const std::string& host, uint16_t port, bool allowNameLookup = false) {\n    // Initialize the address family first,\n    // since setFromHostPort() and setFromIpPort() will check it.\n\n    if (allowNameLookup) {\n      setFromHostPort(host.c_str(), port);\n    } else {\n      setFromIpPort(host.c_str(), port);\n    }\n  }\n  /**\n   * Construct a SocketAddress from a hostname and port.\n   *\n   * Raises std::system_error on error.\n   *\n   * @param ipAddr The IP address\n   * @param port The port (in host byte order)\n   */\n  SocketAddress(const IPAddress& ipAddr, uint16_t port) {\n    setFromIpAddrPort(ipAddr, port);\n  }\n\n  SocketAddress(const SocketAddress& addr) { storage_ = addr.storage_; }\n\n  SocketAddress& operator=(const SocketAddress& addr) {\n    storage_ = addr.storage_;\n    return *this;\n  }\n\n  SocketAddress(SocketAddress&& addr) noexcept {\n    storage_ = std::move(addr.storage_);\n  }\n\n  SocketAddress& operator=(SocketAddress&& addr) {\n    storage_ = std::move(addr.storage_);\n    return *this;\n  }\n\n  ~SocketAddress() = default;\n\n  /**\n   * Return whether this SocketAddress is initialized.\n   */\n  bool isInitialized() const { return (getFamily() != AF_UNSPEC); }\n\n  /**\n   * Return whether this address is within private network.\n   *\n   * According to RFC1918, the 10/8 prefix, 172.16/12 prefix, and 192.168/16\n   * prefix are reserved for private networks.\n   * fc00::/7 is the IPv6 version, defined in RFC4139.  IPv6 link-local\n   * addresses (fe80::/10) are also considered private addresses.\n   *\n   * The loopback addresses 127/8 and ::1 are also regarded as private networks\n   * for the purpose of this function.\n   *\n   * @return true if this is a private network address, false otherwise\n   */\n  bool isPrivateAddress() const;\n\n  /**\n   * Return whether this address is a loopback address.\n   *\n   * @return true if this is a loopback address, false otherwise\n   */\n  bool isLoopbackAddress() const;\n\n  /**\n   * Reset this SocketAddress by clearing the associated address and\n   * freeing up any external storage being used.\n   */\n  void reset() { storage_ = IPAddr(); }\n\n  /**\n   * @overloadbrief Initialize this SocketAddress from a hostname and port.\n   *\n   * Note: If the host parameter is not a numeric IP address, hostname\n   * resolution will be performed, which can be quite slow.\n   *\n   * If the hostname resolves to multiple addresses, only the first will be\n   * returned.\n   *\n   * Raises std::system_error on error.\n   *\n   * @param host The hostname or IP address\n   * @param port The port (in host byte order)\n   */\n  void setFromHostPort(const char* host, uint16_t port);\n  /**\n   * Similar to the function setFromHostPort above, but accepts the IP address\n   * as a std::string.\n   */\n  void setFromHostPort(const std::string& host, uint16_t port) {\n    setFromHostPort(host.c_str(), port);\n  }\n\n  /**\n   * @overloadbrief Initialize this SocketAddress from an IP address and port.\n   *\n   * This is similar to setFromHostPort(), but only accepts numeric IP\n   * addresses.  If the IP string does not look like an IP address, it throws a\n   * std::invalid_argument rather than trying to perform a hostname resolution.\n   *\n   * Raises std::system_error on error.\n   *\n   * @param ip The IP address, as a human-readable string.\n   * @param port The port (in host byte order)\n   */\n  void setFromIpPort(const char* ip, uint16_t port);\n  /**\n   * Similar to the function setFromIpPort above, but accepts the IP address as\n   * a std::string.\n   */\n  void setFromIpPort(const std::string& ip, uint16_t port) {\n    setFromIpPort(ip.c_str(), port);\n  }\n\n  /**\n   * Initialize this SocketAddress from an IPAddress struct and port.\n   *\n   * @param ip The IP address in IPAddress format\n   * @param port The port (in host byte order)\n   */\n  void setFromIpAddrPort(const IPAddress& ip, uint16_t port);\n\n  /**\n   * @overloadbrief Initialize this SocketAddress from a local port number.\n   *\n   * This is intended to be used by server code to determine the address to\n   * listen on.\n   *\n   * If the current machine has any IPv6 addresses configured, an IPv6 address\n   * will be returned (since connections from IPv4 clients can be mapped to the\n   * IPv6 address).  If the machine does not have any IPv6 addresses, an IPv4\n   * address will be returned.\n   *\n   * @param port The local port number (in host byte order)\n   */\n  void setFromLocalPort(uint16_t port);\n  /**\n   * Initialize this SocketAddress from a local port number.\n   *\n   * This version of setFromLocalPort() accepts the port as a string.  A\n   * std::invalid_argument will be raised if the string does not refer to a port\n   * number.  Non-numeric service port names are not accepted.\n   *\n   * @param port The local port number\n   */\n  void setFromLocalPort(const char* port);\n  /**\n   * Similar to the function setFromLocalPort above, but accepts the port as\n   * a std::string.\n   */\n  void setFromLocalPort(const std::string& port) {\n    return setFromLocalPort(port.c_str());\n  }\n\n  /**\n   * @overloadbrief Initialize this SocketAddress from a local port number and\n   * optional IP address.\n   *\n   * The addressAndPort string may be specified either as \"<ip>:<port>\", or\n   * just as \"<port>\".  If the IP is not specified, the address will be\n   * initialized to 0, so that a server socket bound to this address will\n   * accept connections on all local IP addresses.\n   *\n   * Both the IP address and port number must be numeric.  DNS host names and\n   * non-numeric service port names are not accepted.\n   *\n   * @param addressAndPort Address and the port separated by ':', or the port\n   */\n  void setFromLocalIpPort(const char* addressAndPort);\n  /**\n   * Similar to the function setFromLocalIpPort above, but accepts the address\n   * and port as a std::string.\n   */\n  void setFromLocalIpPort(const std::string& addressAndPort) {\n    return setFromLocalIpPort(addressAndPort.c_str());\n  }\n\n  /**\n   * @overloadbrief Initialize this SocketAddress from an IP address and port\n   * number.\n   *\n   * The addressAndPort string must be of the form \"<ip>:<port>\".  E.g.,\n   * \"10.0.0.1:1234\".\n   *\n   * Both the IP address and port number must be numeric.  DNS host names and\n   * non-numeric service port names are not accepted.\n   *\n   * @param addressAndPort Address and the port separated by ':'\n   */\n  void setFromIpPort(const char* addressAndPort);\n  /**\n   * Similar to the function setFromIpPort above, but accepts the address\n   * and port as a std::string.\n   */\n  void setFromIpPort(const std::string& addressAndPort) {\n    return setFromIpPort(addressAndPort.c_str());\n  }\n\n  /**\n   * @overloadbrief Initialize this SocketAddress from a host name and port\n   * number.\n   *\n   * The addressAndPort string must be of the form \"<host>:<port>\".  E.g.,\n   * \"www.facebook.com:443\".\n   *\n   * If the host name is not a numeric IP address, a DNS lookup will be\n   * performed.  Beware that the DNS lookup may be very slow.  The port number\n   * must be numeric; non-numeric service port names are not accepted.\n   *\n   * @param hostAndPort Host name and the port separated by ':'\n   */\n  void setFromHostPort(const char* hostAndPort);\n  /**\n   * Similar to the function setFromHostPort above, but accepts the host name\n   * and port as a std::string.\n   */\n  void setFromHostPort(const std::string& hostAndPort) {\n    return setFromHostPort(hostAndPort.c_str());\n  }\n\n#if FOLLY_HAVE_VSOCK\n  /**\n   * Initialize this SocketAddress from a VSOCK CID and port.\n   */\n  void setFromVsockCIDPort(uint32_t cid, uint32_t port);\n#endif\n\n  /**\n   * Returns the port number from the given socketaddr structure.\n   *\n   * Currently only IPv4 and IPv6 are supported.\n   *\n   * @param address The socketaddr structure to get port from\n   *\n   * @return The port number, or -1 for unsupported socket families.\n   */\n  static int getPortFrom(const struct sockaddr* address);\n\n  /**\n   * Returns the family name from the given socketaddr structure (e.g.: AF_INET6\n   * for IPv6).\n   *\n   * @param address The socketaddr structure to get family name from\n   * @param defaultResult The default family name to be returned in case of\n   * unsupported socket. If no value is passed, `nullptr` is returned as default\n   * family name.\n   *\n   * @return The family name, or `defaultResult` passed for unsupported socket\n   * families.\n   */\n  static const char* getFamilyNameFrom(\n      const struct sockaddr* address, const char* defaultResult = nullptr);\n\n  /**\n   * @overloadbrief Initialize this SocketAddress from a local unix path.\n   *\n   * Raises std::invalid_argument on error.\n   *\n   * @param path Local unix path\n   */\n  void setFromPath(StringPiece path);\n  /**\n   * Similar to setFromPath, but accepts local unix path as const char* and\n   * its length.\n   */\n  void setFromPath(const char* path, size_t length) {\n    setFromPath(StringPiece{path, length});\n  }\n\n  /**\n   * Construct a SocketAddress from a local unix socket path.\n   *\n   * Raises std::invalid_argument on error.\n   *\n   * @param path The Unix domain socket path.\n   */\n  static SocketAddress makeFromPath(StringPiece path) {\n    SocketAddress addr;\n    addr.setFromPath(path);\n    return addr;\n  }\n\n  /**\n   * Construct a SocketAddress from a local port number.\n   * see setFromLocalPort for more details.\n   *\n   * Raises std::invalid_argument on error.\n   *\n   * @param port The local port number (in host byte order)\n   */\n  static SocketAddress makeFromLocalPort(uint16_t port) {\n    SocketAddress addr;\n    addr.setFromLocalPort(port);\n    return addr;\n  }\n\n  /**\n   * Similar to makeFromLocalPort(uint16_t port), but accepts the port as\n   * a std::string.\n   *\n   * Raises std::invalid_argument on error.\n   *\n   * @param port The local port number\n   */\n  static SocketAddress makeFromLocalPort(const char* port) {\n    SocketAddress addr;\n    addr.setFromLocalPort(port);\n    return addr;\n  }\n\n  /**\n   * Initialize this SocketAddress from a socket's peer address.\n   *\n   * Raises std::system_error on error.\n   *\n   * @param socket Socket whose peer address is used to initialize\n   */\n  void setFromPeerAddress(NetworkSocket socket);\n\n  /**\n   * Initialize this SocketAddress from a socket's local address.\n   *\n   * Raises std::system_error on error.\n   *\n   * @param socket Socket whose local address is used to initialize\n   */\n  void setFromLocalAddress(NetworkSocket socket);\n\n  /**\n   * Initialize this folly::SocketAddress from a struct sockaddr.\n   *\n   * Raises std::system_error on error.\n   *\n   * This method is not supported for AF_UNIX addresses.  For unix addresses,\n   * the address length must be explicitly specified.\n   *\n   * @param address  A struct sockaddr.  The size of the address is implied\n   *                 from address->sa_family.\n   */\n  void setFromSockaddr(const struct sockaddr* address);\n\n  /**\n   * Initialize this SocketAddress from a struct sockaddr.\n   *\n   * Raises std::system_error on error.\n   *\n   * @param address  A struct sockaddr.\n   * @param addrlen  The length of address data available.  This must be long\n   *                 enough for the full address type required by\n   *                 address->sa_family.\n   */\n  void setFromSockaddr(const struct sockaddr* address, socklen_t addrlen);\n\n  /**\n   * Initialize this SocketAddress from a struct sockaddr_in.\n   *\n   * @param address  A struct sockaddr_in to initialize from\n   */\n  void setFromSockaddr(const struct sockaddr_in* address);\n\n  /**\n   * Initialize this SocketAddress from a struct sockaddr_in6.\n   *\n   * @param address  A struct sockaddr_in6 to initialize from\n   */\n  void setFromSockaddr(const struct sockaddr_in6* address);\n\n  /**\n   * Initialize this SocketAddress from a struct sockaddr_un.\n   *\n   * Note that the addrlen parameter is necessary to properly detect anonymous\n   * addresses, which have 0 valid path bytes, and may not even have a NUL\n   * character at the start of the path.\n   *\n   * @param address  A struct sockaddr_un.\n   * @param addrlen  The length of address data.  This should include all of\n   *                 the valid bytes of sun_path, not including any NUL\n   *                 terminator.\n   */\n  void setFromSockaddr(const struct sockaddr_un* address, socklen_t addrlen);\n\n#if FOLLY_HAVE_VSOCK\n  /**\n   * Initialize this SocketAddress from a struct sockaddr_vm.\n   *\n   * @param address  A struct sockaddr_vm to initialize from\n   */\n  void setFromSockaddr(const struct sockaddr_vm* address);\n#endif\n\n  /**\n   * Fill in a given sockaddr_storage with the ip or unix address.\n   *\n   * @param addr sockaddr_storage out parameter\n   *\n   * @return The actual size of the socket address\n   */\n  socklen_t getAddress(sockaddr_storage* addr) const {\n    if (isFamilyInet()) {\n      return std::get<IPAddr>(storage_).ip.toSockaddrStorage(\n          addr, htons(std::get<IPAddr>(storage_).port));\n#if FOLLY_HAVE_VSOCK\n    } else if (holdsVsock()) {\n      const auto& vsockAddr = std::get<VsockAddr>(storage_);\n      auto* svm = reinterpret_cast<sockaddr_vm*>(addr);\n      memset(svm, 0, sizeof(sockaddr_vm));\n      svm->svm_family = AF_VSOCK;\n      svm->svm_cid = vsockAddr.cid;\n      svm->svm_port = vsockAddr.port;\n      return sizeof(sockaddr_vm);\n#endif\n    } else {\n      const auto& unixAddr = std::get<ExternalUnixAddr>(storage_);\n      memcpy(addr, unixAddr.addr, sizeof(*unixAddr.addr));\n      return unixAddr.len;\n    }\n  }\n\n  /**\n   * Return the IP address of this SocketAddress.\n   *\n   * @throws folly::InvalidAddressFamilyException if the family is not IPv4 or\n   * IPv6\n   *\n   * @return IP address\n   */\n  const folly::IPAddress& getIPAddress() const;\n\n  /**\n   * DEPRECATED: SocketAddress::getAddress() above returns the same size as\n   * getActualSize()\n   *\n   * Return the size of the underlying socket address\n   *\n   * @return The size of the socket address\n   */\n  socklen_t getActualSize() const;\n\n  /**\n   * Return the address family of this SocketAddress\n   *\n   * @return Socket address family\n   */\n  sa_family_t getFamily() const {\n    if (holdsUnix()) {\n      return sa_family_t(AF_UNIX);\n#if FOLLY_HAVE_VSOCK\n    } else if (holdsVsock()) {\n      return sa_family_t(AF_VSOCK);\n#endif\n    } else {\n      return std::get<IPAddr>(storage_).ip.family();\n    }\n  }\n\n  /**\n   * Return if the SocketAddress is `empty` i.e., the address family is\n   * unspecified.\n   *\n   * @return true, if socket is `empty` i.e., address family is unspecified,\n   * else false\n   */\n  bool empty() const { return getFamily() == AF_UNSPEC; }\n\n  /**\n   * Get a string representation of the IPv4 or IPv6 address.\n   *\n   * Raises std::invalid_argument if an error occurs (for example, if\n   * the address is not an IPv4 or IPv6 address).\n   *\n   * @return String representation of the IP address\n   */\n  std::string getAddressStr() const;\n\n  /**\n   * Get a string representation of the IPv4 or IPv6 address.\n   *\n   * Raises std::invalid_argument if an error occurs (for example, if\n   * the address is not an IPv4 or IPv6 address).\n   *\n   * @param buf Char buffer to write the string representation into\n   * @param buflen Size of the buffer\n   */\n  void getAddressStr(char* buf, size_t buflen) const;\n\n  /**\n   * Return whether this address is a valid IPv4 or IPv6 address.\n   *\n   * @return true if address a valid IPv4 or IPv6 address, false otherwise\n   */\n  bool isFamilyInet() const;\n\n  /**\n   * For v4 & v6 addresses, return the fully qualified address string\n   *\n   * Raises std::invalid_argument if this is not an IPv4 or IPv6 address.\n   *\n   * @return Fully qualified IP address\n   */\n  std::string getFullyQualified() const;\n\n  /**\n   * Get the IPv4 or IPv6 port for this address.\n   *\n   * Raises std::invalid_argument if this is not an IPv4 or IPv6 address.\n   *\n   * @return The port, in host byte order\n   */\n  uint16_t getPort() const;\n\n#if FOLLY_HAVE_VSOCK\n  /**\n   * Get the port for a VSOCK address.\n   *\n   * Raises std::invalid_argument if this is not a VSOCK address.\n   *\n   * @return The port, in host byte order\n   */\n  uint32_t getVsockPort() const;\n#endif\n\n  /**\n   * Set the IPv4 or IPv6 port for this address.\n   *\n   * Raises std::invalid_argument if this is not an IPv4 or IPv6 address.\n   *\n   * @param port The port to set, in host byte order\n   */\n  void setPort(uint16_t port);\n\n  /**\n   * Return true if this is an IPv4-mapped IPv6 address.\n   *\n   * @return true if this address is a IPv6 address which is IPv4-mapped,\n   * false otherwise\n   */\n  bool isIPv4Mapped() const {\n    return (\n        getFamily() == AF_INET6 &&\n        std::get<IPAddr>(storage_).ip.isIPv4Mapped());\n  }\n\n  /**\n   * Convert an IPv4-mapped IPv6 address to an IPv4 address.\n   *\n   * Raises std::invalid_argument if this is not an IPv4-mapped IPv6 address.\n   *\n   * @note SocketAddress::tryConvertToIPv4 is no-throw variant of this function\n   */\n  void convertToIPv4();\n\n  /**\n   * Try to convert an address to IPv4.\n   *\n   * This attempts to convert an address to an IPv4 address if possible.\n   * If the address is an IPv4-mapped IPv6 address, it is converted to an IPv4\n   * address and true is returned.  Otherwise nothing is done, and false is\n   * returned.\n   *\n   * @return true if the address was converted to IPv4-mapped address, false\n   * otherwise\n   */\n  bool tryConvertToIPv4();\n\n  /**\n   * Convert an IPv4 address to IPv6 [::ffff:a.b.c.d]\n   *\n   * @return true if the address conversion was done, false otherwise\n   */\n  bool mapToIPv6();\n\n  /**\n   * Get string representation of the host name (or IP address if the host name\n   * cannot be resolved).\n   *\n   * Warning: Using this method is strongly discouraged.  It performs a\n   * DNS lookup, which may block for many seconds.\n   *\n   * Raises std::invalid_argument if an error occurs.\n   *\n   * @return Host name (or IP address)\n   */\n  std::string getHostStr() const;\n\n  /**\n   * Get the path name for a Unix domain socket.\n   *\n   * Returns a std::string containing the path.  For anonymous sockets, an\n   * empty string is returned.\n   *\n   * For addresses in the abstract namespace (Linux-specific), a std::string\n   * containing binary data is returned.  In this case the first character will\n   * always be a NUL character.\n   *\n   * Raises std::invalid_argument if called on a non-Unix domain socket.\n   *\n   * @return Path name for a Unix domain socket\n   */\n  std::string getPath() const;\n\n#if FOLLY_HAVE_VSOCK\n  /**\n   * Get the CID (Context Identifier) for a VSOCK address.\n   *\n   * Raises std::invalid_argument if called on a non-VSOCK address.\n   *\n   * @return CID for a VSOCK address\n   */\n  uint32_t getVsockCID() const;\n#endif\n\n  /**\n   * Get human-readable string representation of the address.\n   *\n   * This prints a string representation of the address, for human consumption.\n   * For IP addresses, the string is of the form \"<IP>:<port>\".\n   *\n   * @return Human-readable representation of the address\n   */\n  std::string describe() const;\n\n  bool operator==(const SocketAddress& other) const;\n  bool operator!=(const SocketAddress& other) const {\n    return !(*this == other);\n  }\n\n  /**\n   * Check whether the first N bits of this address match the first N\n   * bits of another address.\n   *\n   * @note returns false if the addresses are not from the same\n   *       address family or if the family is neither IPv4 nor IPv6\n   *\n   * @param other The address to match against\n   * @param prefixLength Length of the prefix to match\n   * @return true if `prefixLength` this address matches with `other`,\n   * false otherwise\n   */\n  bool prefixMatch(const SocketAddress& other, unsigned prefixLength) const;\n\n  /**\n   * Use this operator for storing maps based on SocketAddress.\n   */\n  bool operator<(const SocketAddress& other) const;\n\n  /**\n   * Compuate a hash of a SocketAddress.\n   *\n   * @return Hash for this SocketAddress\n   */\n  size_t hash() const;\n\n private:\n  /**\n   * Unix socket addresses require more storage than IPv4 and IPv6 addresses,\n   * and are comparatively little-used.\n   *\n   * Therefore SocketAddress' internal storage_ member variable doesn't\n   * contain room for a full unix address, to avoid wasting space in the common\n   * case.  When we do need to store a Unix socket address, we use this\n   * ExternalUnixAddr structure to allocate a struct sockaddr_un separately on\n   * the heap.\n   */\n  struct ExternalUnixAddr {\n    struct sockaddr_un* addr;\n    socklen_t len;\n\n    socklen_t pathLength() const {\n      return socklen_t(len - offsetof(struct sockaddr_un, sun_path));\n    }\n\n    ExternalUnixAddr() {\n      addr = new struct sockaddr_un;\n      addr->sun_family = AF_UNIX;\n      len = 0;\n    }\n\n    ExternalUnixAddr(const ExternalUnixAddr& other) : ExternalUnixAddr() {\n      len = other.len;\n      memcpy(addr, other.addr, size_t(len));\n    }\n\n    ExternalUnixAddr& operator=(const ExternalUnixAddr& other) {\n      if (this != &other) {\n        len = other.len;\n        memcpy(addr, other.addr, size_t(len));\n      }\n      return *this;\n    }\n\n    ~ExternalUnixAddr() { delete addr; }\n  };\n\n  /**\n   * This class stores an IP address and port.\n   */\n  struct IPAddr {\n    folly::IPAddress ip;\n    uint16_t port;\n\n    IPAddr() : ip(), port(0) {}\n    IPAddr(const folly::IPAddress& ip_, uint16_t port_)\n        : ip(ip_), port(port_) {}\n  };\n\n  /**\n   * This class stores the CID (Context Identifier) and port for VSOCK\n   * addresses.\n   */\n  struct VsockAddr {\n    uint32_t cid;\n    uint32_t port;\n\n    explicit VsockAddr(uint32_t cid_) : cid(cid_), port(0) {}\n    VsockAddr(uint32_t cid_, uint32_t port_) : cid(cid_), port(port_) {}\n\n#if FOLLY_HAVE_VSOCK\n    const char* getMappedName() const;\n#endif\n  };\n\n  bool holdsInet() const { return std::holds_alternative<IPAddr>(storage_); }\n\n  bool holdsUnix() const {\n    return std::holds_alternative<ExternalUnixAddr>(storage_);\n  }\n\n  bool holdsVsock() const {\n    return std::holds_alternative<VsockAddr>(storage_);\n  }\n\n  struct addrinfo* getAddrInfo(const char* host, uint16_t port, int flags);\n  struct addrinfo* getAddrInfo(const char* host, const char* port, int flags);\n  void setFromAddrInfo(const struct addrinfo* info);\n  void setFromLocalAddr(const struct addrinfo* info);\n  void setFromSocket(\n      NetworkSocket socket,\n      int (*fn)(NetworkSocket, struct sockaddr*, socklen_t*));\n  std::string getIpString(int flags) const;\n  void getIpString(char* buf, size_t buflen, int flags) const;\n\n  void updateUnixAddressLength(socklen_t addrlen);\n\n  /*\n   * storage_ contains either an IPAddr, an ExternalUnixAddr, or a VsockAddr.\n   * IPAddr is used for IPv4 and IPv6 addresses.\n   * ExternalUnixAddr is used for Unix domain sockets.\n   * VsockAddr is used for VSOCK addresses.\n   */\n  std::variant<IPAddr, ExternalUnixAddr, VsockAddr> storage_{IPAddr()};\n};\n\n/**\n * Hash a SocketAddress object.\n *\n * boost::hash uses hash_value(), so this allows boost::hash to automatically\n * work for SocketAddress.\n */\nsize_t hash_value(const SocketAddress& address);\n\nstd::ostream& operator<<(std::ostream& os, const SocketAddress& addr);\n} // namespace folly\n\nnamespace std {\n\n// Provide an implementation for std::hash<SocketAddress>\ntemplate <>\nstruct hash<folly::SocketAddress> {\n  size_t operator()(const folly::SocketAddress& addr) const {\n    return addr.hash();\n  }\n};\n} // namespace std\n"
  },
  {
    "path": "folly/SpinLock.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * N.B. You most likely do _not_ want to use SpinLock or any other\n * kind of spinlock.  Use std::mutex instead.\n *\n * In short, spinlocks in preemptive multi-tasking operating systems\n * have serious problems and fast mutexes like std::mutex are almost\n * certainly the better choice, because letting the OS scheduler put a\n * thread to sleep is better for system responsiveness and throughput\n * than wasting a timeslice repeatedly querying a lock held by a\n * thread that's blocked, and you can't prevent userspace\n * programs blocking.\n *\n * Spinlocks in an operating system kernel make much more sense than\n * they do in userspace.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/synchronization/SmallLocks.h>\n\nnamespace folly {\n\nclass SpinLock {\n public:\n  FOLLY_ALWAYS_INLINE SpinLock() noexcept { lock_.init(); }\n  FOLLY_ALWAYS_INLINE void lock() const noexcept { lock_.lock(); }\n  FOLLY_ALWAYS_INLINE void unlock() const noexcept { lock_.unlock(); }\n  FOLLY_ALWAYS_INLINE bool try_lock() const noexcept {\n    return lock_.try_lock();\n  }\n\n private:\n  mutable folly::MicroSpinLock lock_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/String-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <iterator>\n#include <stdexcept>\n\n#include <folly/CppAttributes.h>\n#include <folly/container/Reserve.h>\n\n#ifndef FOLLY_STRING_H_\n#error This file may only be included from String.h\n#endif\n\nnamespace folly {\n\nnamespace detail {\n// Map from character code to value of one-character escape sequence\n// ('\\n' = 10 maps to 'n'), 'O' if the character should be printed as\n// an octal escape sequence, or 'P' if the character is printable and\n// should be printed as is.\nextern const std::array<char, 256> cEscapeTable;\n} // namespace detail\n\ntemplate <class String>\nvoid cEscape(StringPiece str, String& out) {\n  char esc[4];\n  esc[0] = '\\\\';\n  grow_capacity_by(out, str.size());\n  auto p = str.begin();\n  auto last = p; // last regular character\n  // We advance over runs of regular characters (printable, not double-quote or\n  // backslash) and copy them in one go; this is faster than calling push_back\n  // repeatedly.\n  while (p != str.end()) {\n    char c = *p;\n    unsigned char v = static_cast<unsigned char>(c);\n    char e = detail::cEscapeTable[v];\n    if (e == 'P') { // printable\n      ++p;\n    } else if (e == 'O') { // octal\n      out.append(&*last, size_t(p - last));\n      esc[1] = '0' + ((v >> 6) & 7);\n      esc[2] = '0' + ((v >> 3) & 7);\n      esc[3] = '0' + (v & 7);\n      out.append(esc, 4);\n      ++p;\n      last = p;\n    } else { // special 1-character escape\n      out.append(&*last, size_t(p - last));\n      esc[1] = e;\n      out.append(esc, 2);\n      ++p;\n      last = p;\n    }\n  }\n  out.append(&*last, size_t(p - last));\n}\n\nnamespace detail {\n// Map from the character code of the character following a backslash to\n// the unescaped character if a valid one-character escape sequence\n// ('n' maps to 10 = '\\n'), 'O' if this is the first character of an\n// octal escape sequence, 'X' if this is the first character of a\n// hexadecimal escape sequence, or 'I' if this escape sequence is invalid.\nextern const std::array<char, 256> cUnescapeTable;\n\n// Map from the character code to the hex value, or 16 if invalid hex char.\nextern const std::array<unsigned char, 256> hexTable;\n} // namespace detail\n\ntemplate <class String>\nvoid cUnescape(StringPiece str, String& out, bool strict) {\n  grow_capacity_by(out, str.size());\n  auto p = str.begin();\n  auto last = p; // last regular character (not part of an escape sequence)\n  // We advance over runs of regular characters (not backslash) and copy them\n  // in one go; this is faster than calling push_back repeatedly.\n  while (p != str.end()) {\n    char c = *p;\n    if (c != '\\\\') { // normal case\n      ++p;\n      continue;\n    }\n    out.append(&*last, p - last);\n    ++p;\n    if (p == str.end()) { // backslash at end of string\n      if (strict) {\n        throw_exception<std::invalid_argument>(\"incomplete escape sequence\");\n      }\n      out.push_back('\\\\');\n      last = p;\n      continue;\n    }\n    char e = detail::cUnescapeTable[static_cast<unsigned char>(*p)];\n    if (e == 'O') { // octal\n      unsigned char val = 0;\n      for (int i = 0; i < 3 && p != str.end() && *p >= '0' && *p <= '7';\n           ++i, ++p) {\n        val <<= 3;\n        val |= (*p - '0');\n      }\n      out.push_back(val);\n      last = p;\n    } else if (e == 'X') { // hex\n      ++p;\n      if (p == str.end()) { // \\x at end of string\n        if (strict) {\n          throw_exception<std::invalid_argument>(\n              \"incomplete hex escape sequence\");\n        }\n        out.append(\"\\\\x\");\n        last = p;\n        continue;\n      }\n      unsigned char val = 0;\n      unsigned char h;\n      for (; (p != str.end() &&\n              (h = detail::hexTable[static_cast<unsigned char>(*p)]) < 16);\n           ++p) {\n        val <<= 4;\n        val |= h;\n      }\n      out.push_back(val);\n      last = p;\n    } else if (e == 'I') { // invalid\n      if (strict) {\n        throw_exception<std::invalid_argument>(\"invalid escape sequence\");\n      }\n      out.push_back('\\\\');\n      out.push_back(*p);\n      ++p;\n      last = p;\n    } else { // standard escape sequence, \\' etc\n      out.push_back(e);\n      ++p;\n      last = p;\n    }\n  }\n  out.append(&*last, p - last);\n}\n\nnamespace detail {\n// Map from character code to escape mode:\n// 0 = pass through\n// 1 = unused\n// 2 = pass through in PATH mode\n// 3 = space, replace with '+' in QUERY mode\n// 4 = percent-encode\nextern const std::array<unsigned char, 256> uriEscapeTable;\n} // namespace detail\n\ntemplate <class String>\nvoid uriEscape(StringPiece str, String& out, UriEscapeMode mode) {\n  static const char hexValues[] = \"0123456789abcdef\";\n  char esc[3];\n  esc[0] = '%';\n  // Preallocate assuming that 25% of the input string will be escaped\n  grow_capacity_by(out, str.size() + 3 * (str.size() / 4));\n  auto p = str.begin();\n  auto last = p; // last regular character\n  // We advance over runs of passthrough characters and copy them in one go;\n  // this is faster than calling push_back repeatedly.\n  unsigned char minEncode = static_cast<unsigned char>(mode);\n  while (p != str.end()) {\n    char c = *p;\n    unsigned char v = static_cast<unsigned char>(c);\n    unsigned char discriminator = detail::uriEscapeTable[v];\n    if (FOLLY_LIKELY(discriminator <= minEncode)) {\n      ++p;\n    } else if (mode == UriEscapeMode::QUERY && discriminator == 3) {\n      out.append(&*last, size_t(p - last));\n      out.push_back('+');\n      ++p;\n      last = p;\n    } else {\n      out.append(&*last, size_t(p - last));\n      esc[1] = hexValues[v >> 4];\n      esc[2] = hexValues[v & 0x0f];\n      out.append(esc, 3);\n      ++p;\n      last = p;\n    }\n  }\n  out.append(&*last, size_t(p - last));\n}\n\ntemplate <class String>\nbool tryUriUnescape(StringPiece str, String& out, UriEscapeMode mode) {\n  grow_capacity_by(out, str.size());\n  auto p = str.begin();\n  auto last = p;\n  // We advance over runs of passthrough characters and copy them in one go;\n  // this is faster than calling push_back repeatedly.\n  while (p != str.end()) {\n    char c = *p;\n    switch (c) {\n      case '%': {\n        if (FOLLY_UNLIKELY(std::distance(p, str.end()) < 3)) {\n          return false;\n        }\n        auto h1 = detail::hexTable[static_cast<unsigned char>(p[1])];\n        auto h2 = detail::hexTable[static_cast<unsigned char>(p[2])];\n        if (FOLLY_UNLIKELY(h1 == 16 || h2 == 16)) {\n          return false;\n        }\n        out.append(&*last, size_t(p - last));\n        out.push_back(decltype(h1)(h1 << 4) | h2);\n        p += 3;\n        last = p;\n        break;\n      }\n      case '+':\n        if (mode == UriEscapeMode::QUERY) {\n          out.append(&*last, size_t(p - last));\n          out.push_back(' ');\n          ++p;\n          last = p;\n          break;\n        }\n        // else fallthrough\n        [[fallthrough]];\n      default:\n        ++p;\n        break;\n    }\n  }\n  out.append(&*last, size_t(p - last));\n\n  return true;\n}\n\ntemplate <class String>\nvoid uriUnescape(StringPiece str, String& out, UriEscapeMode mode) {\n  auto success = tryUriUnescape(str, out, mode);\n\n  if (!success) {\n    // tryUriEscape implementation only fails on invalid argument\n    throw_exception<std::invalid_argument>(\n        \"incomplete percent encode sequence\");\n  }\n}\n\nnamespace detail {\n\n/*\n * The following functions are type-overloaded helpers for\n * internalSplit().\n */\ninline size_t delimSize(char) {\n  return 1;\n}\ninline size_t delimSize(StringPiece s) {\n  return s.size();\n}\ninline bool atDelim(const char* s, char c) {\n  return *s == c;\n}\ninline bool atDelim(const char* s, StringPiece sp) {\n  return !std::memcmp(s, sp.start(), sp.size());\n}\n\n// These are used to short-circuit internalSplit() in the case of\n// 1-character strings.\ninline char delimFront(char) {\n  // This one exists only for compile-time; it should never be called.\n  std::abort();\n}\ninline char delimFront(StringPiece s) {\n  assert(!s.empty() && s.start() != nullptr);\n  return *s.start();\n}\n\ntemplate <class OutStringT, class DelimT, class OutputIterator>\nvoid internalSplit(\n    DelimT delim, StringPiece sp, OutputIterator out, bool ignoreEmpty);\n\n/*\n * Count the number of tokens that would be produced by splitting\n * the given StringPiece with the given delimiter.\n * This is used for pre-allocation optimization in split().\n */\nsize_t delimCountTokens(char delim, StringPiece sp, bool ignoreEmpty);\nsize_t delimCountTokens(StringPiece delim, StringPiece sp, bool ignoreEmpty);\n\ntemplate <class OutStringT, class Container>\nstd::enable_if_t<\n    IsSplitSupportedContainer<Container>::value &&\n    HasSimdSplitCompatibleValueType<Container>::value>\ninternalSplitRecurseChar(\n    char delim,\n    folly::StringPiece sp,\n    std::back_insert_iterator<Container> it,\n    bool ignoreEmpty) {\n  using base = std::back_insert_iterator<Container>;\n  struct accessor : base {\n    accessor(base b) : base(b) {}\n    using base::container;\n  };\n  detail::simdSplitByChar(delim, sp, *accessor{it}.container, ignoreEmpty);\n}\n\ntemplate <class OutStringT, class Iterator>\nvoid internalSplitRecurseChar(\n    char delim, folly::StringPiece sp, Iterator it, bool ignoreEmpty) {\n  internalSplit<OutStringT>(delim, sp, it, ignoreEmpty);\n}\n\n/*\n * Shared implementation for all the split() overloads.\n *\n * This uses some external helpers that are overloaded to let this\n * algorithm be more performant if the deliminator is a single\n * character instead of a whole string.\n *\n * @param ignoreEmpty if true, don't copy empty segments to output\n */\ntemplate <class OutStringT, class DelimT, class OutputIterator>\nvoid internalSplit(\n    DelimT delim, StringPiece sp, OutputIterator out, bool ignoreEmpty) {\n  assert(sp.empty() || sp.start() != nullptr);\n\n  const char* s = sp.start();\n  const size_t strSize = sp.size();\n  const size_t dSize = delimSize(delim);\n\n  if (dSize > strSize || dSize == 0) {\n    if (!ignoreEmpty || strSize > 0) {\n      *out++ = to<OutStringT>(sp);\n    }\n    return;\n  }\n  if (std::is_same<DelimT, StringPiece>::value && dSize == 1) {\n    // Call the char version because it is significantly faster.\n    return internalSplitRecurseChar<OutStringT>(\n        delimFront(delim), sp, out, ignoreEmpty);\n  }\n\n  size_t tokenStartPos = 0;\n  size_t tokenSize = 0;\n  for (size_t i = 0; i <= strSize - dSize; ++i) {\n    if (atDelim(&s[i], delim)) {\n      if (!ignoreEmpty || tokenSize > 0) {\n        *out++ = to<OutStringT>(sp.subpiece(tokenStartPos, tokenSize));\n      }\n\n      tokenStartPos = i + dSize;\n      tokenSize = 0;\n      i += dSize - 1;\n    } else {\n      ++tokenSize;\n    }\n  }\n  tokenSize = strSize - tokenStartPos;\n  if (!ignoreEmpty || tokenSize > 0) {\n    *out++ = to<OutStringT>(sp.subpiece(tokenStartPos, tokenSize));\n  }\n}\n\ntemplate <class String>\nStringPiece prepareDelim(const String& s) {\n  return StringPiece(s);\n}\ninline char prepareDelim(char c) {\n  return c;\n}\n\ntemplate <class OutputType>\nvoid toOrIgnore(StringPiece input, OutputType& output) {\n  output = folly::to<OutputType>(input);\n}\n\ninline void toOrIgnore(StringPiece, decltype(std::ignore)&) {}\n\ntemplate <bool exact, class Delim, class OutputType>\nbool splitFixed(const Delim& delimiter, StringPiece input, OutputType& output) {\n  if (exact && FOLLY_UNLIKELY(std::string::npos != input.find(delimiter))) {\n    return false;\n  }\n  toOrIgnore(input, output);\n  return true;\n}\n\ntemplate <bool exact, class Delim, class OutputType, class... OutputTypes>\nbool splitFixed(\n    const Delim& delimiter,\n    StringPiece input,\n    OutputType& outHead,\n    OutputTypes&... outTail) {\n  size_t cut = input.find(delimiter);\n  if (FOLLY_UNLIKELY(cut == std::string::npos)) {\n    return false;\n  }\n  StringPiece head(input.begin(), input.begin() + cut);\n  StringPiece tail(\n      input.begin() + cut + detail::delimSize(delimiter), input.end());\n  if (FOLLY_LIKELY(splitFixed<exact>(delimiter, tail, outTail...))) {\n    toOrIgnore(head, outHead);\n    return true;\n  }\n  return false;\n}\n\n// Overload for no remaining output fields; requires empty input.\ntemplate <class Delim>\nExpected<Unit, SubstringConversionCode> trySplitTo(\n    StringPiece input, const Delim&) {\n  if (input.empty()) {\n    return unit;\n  }\n  return makeUnexpected(\n      SubstringConversionCode{input, ConversionCode::SPLIT_ERROR});\n}\n\n// Replace custom conversion codes with folly::ConversionCode::CUSTOM_OTHER.\ntemplate <class CustomCode>\ninline ConversionCode convertError(CustomCode&&) {\n  return ConversionCode::CUSTOM;\n}\n\ninline ConversionCode convertError(ConversionCode code) {\n  return code;\n}\n\n// tryFieldTo helpers, wrapping tryTo<>, but adding support for std::ignore and\n// replacing custom error types with ConversionCode::CUSTOM.\ntemplate <class Output>\nExpected<Output, ConversionCode> tryFieldTo(folly::StringPiece input) {\n  if (auto result = tryTo<Output>(input)) {\n    return std::move(result.value());\n  } else {\n    return makeUnexpected(convertError(result.error()));\n  }\n}\n\ntemplate <>\ninline Expected<decltype(std::ignore), ConversionCode>\ntryFieldTo<decltype(std::ignore)>(folly::StringPiece /*input*/) {\n  return std::ignore;\n}\n\ntemplate <class Delim, class Output, class... Outputs>\nExpected<Unit, SubstringConversionCode> trySplitTo(\n    StringPiece input,\n    const Delim& delim,\n    Output& output,\n    Outputs&... outputs) {\n  auto pos = input.find(delim);\n  if ((pos == std::string::npos) != (sizeof...(outputs) == 0)) {\n    return makeUnexpected(\n        SubstringConversionCode{input, ConversionCode::SPLIT_ERROR});\n  }\n  StringPiece head, tail;\n  if (pos == std::string::npos) {\n    head = input;\n  } else {\n    head = input.subpiece(0, pos);\n    tail = input.subpiece(pos + delimSize(delim));\n  }\n  // Eagerly attempt parsing the head value, but only assign on the way back\n  // from the recursive calls to ensure all outputs are untouched on failure.\n  if (auto headResult = tryFieldTo<Output>(head)) {\n    if (auto tailResult = trySplitTo(tail, delim, outputs...)) {\n      output = *headResult;\n      return unit;\n\n    } else {\n      return makeUnexpected(tailResult.error());\n    }\n  } else {\n    // First failure (left-to-right) is returned.\n    return makeUnexpected(SubstringConversionCode{head, headResult.error()});\n  }\n}\n\n} // namespace detail\n\n//////////////////////////////////////////////////////////////////////\n\ntemplate <class Delim, class String, class OutputType>\nstd::enable_if_t<\n    (!detail::IsSimdSupportedDelim<Delim>::value ||\n     !detail::HasSimdSplitCompatibleValueType<OutputType>::value) &&\n    detail::IsSplitSupportedContainer<OutputType>::value>\nsplit(\n    const Delim& delimiter,\n    const String& input,\n    OutputType& out,\n    bool ignoreEmpty) {\n  detail::internalSplit<typename OutputType::value_type>(\n      detail::prepareDelim(delimiter),\n      StringPiece(input),\n      std::back_inserter(out),\n      ignoreEmpty);\n}\n\ntemplate <class Delim, class String, class OutputType>\nstd::enable_if_t<detail::IsSplitSupportedContainer<OutputType>::value> split(\n    const Delim& delimiter,\n    const String& input,\n    OutputType& out,\n    const SplitOptions& options) {\n  size_t predicted_count = 0;\n  if (options.preallocate()) {\n    // First pass: count the expected number of tokens\n    StringPiece sp(input);\n    auto delim_prepared = detail::prepareDelim(delimiter);\n    predicted_count =\n        detail::delimCountTokens(delim_prepared, sp, options.ignore_empty());\n    grow_capacity_by(out, predicted_count);\n  }\n\n  size_t initial_size = out.size();\n  detail::internalSplit<typename OutputType::value_type>(\n      detail::prepareDelim(delimiter),\n      StringPiece(input),\n      std::back_inserter(out),\n      options.ignore_empty());\n\n  if (options.preallocate()) {\n    [[maybe_unused]] size_t actual_count = out.size() - initial_size;\n    assert(predicted_count == actual_count);\n  }\n}\n\ntemplate <\n    class OutputValueType,\n    class Delim,\n    class String,\n    class OutputIterator>\nvoid splitTo(\n    const Delim& delimiter,\n    const String& input,\n    OutputIterator out,\n    bool ignoreEmpty) {\n  detail::internalSplit<OutputValueType>(\n      detail::prepareDelim(delimiter), StringPiece(input), out, ignoreEmpty);\n}\n\ntemplate <class Delim, class... OutputTypes>\ntypename std::enable_if<\n    StrictConjunction<IsConvertible<OutputTypes>...>::value,\n    Expected<Unit, SubstringConversionCode>>::type\ntrySplitTo(StringPiece input, const Delim& delim, OutputTypes&... outputs) {\n  return detail::trySplitTo(input, detail::prepareDelim(delim), outputs...);\n}\n\ntemplate <bool exact, class Delim, class... OutputTypes>\ntypename std::enable_if<\n    StrictConjunction<IsConvertible<OutputTypes>...>::value &&\n        sizeof...(OutputTypes) >= 1,\n    bool>::type\nsplit(const Delim& delimiter, StringPiece input, OutputTypes&... outputs) {\n  return detail::splitFixed<exact>(\n      detail::prepareDelim(delimiter), input, outputs...);\n}\n\nnamespace detail {\n\n/*\n * If a type can have its string size determined cheaply, we can more\n * efficiently append it in a loop (see internalJoinAppend). Note that the\n * struct need not conform to the std::string api completely (ex. does not need\n * to implement append()).\n */\ntemplate <class T>\nstruct IsSizableString {\n  enum {\n    value = IsSomeString<T>::value || std::is_same<T, StringPiece>::value\n  };\n};\n\ntemplate <class Iterator>\nstruct IsSizableStringContainerIterator\n    : IsSizableString<typename std::iterator_traits<Iterator>::value_type> {};\n\ntemplate <class Delim, class Iterator, class String>\nvoid internalJoinAppend(\n    Delim delimiter, Iterator begin, Iterator end, String& output) {\n  assert(begin != end);\n  if (std::is_same<Delim, StringPiece>::value && delimSize(delimiter) == 1) {\n    internalJoinAppend(delimFront(delimiter), begin, end, output);\n    return;\n  }\n  toAppend(*begin, &output);\n  while (++begin != end) {\n    toAppend(delimiter, *begin, &output);\n  }\n}\n\ntemplate <class Delim, class Iterator, class String>\ntypename std::enable_if<IsSizableStringContainerIterator<Iterator>::value>::type\ninternalJoin(Delim delimiter, Iterator begin, Iterator end, String& output) {\n  output.clear();\n  if (begin == end) {\n    return;\n  }\n  const size_t dsize = delimSize(delimiter);\n  Iterator it = begin;\n  size_t size = it->size();\n  while (++it != end) {\n    size += dsize + it->size();\n  }\n  output.reserve(size);\n  internalJoinAppend(delimiter, begin, end, output);\n}\n\ntemplate <class Delim, class Iterator, class String>\ntypename std::enable_if<\n    !IsSizableStringContainerIterator<Iterator>::value>::type\ninternalJoin(Delim delimiter, Iterator begin, Iterator end, String& output) {\n  output.clear();\n  if (begin == end) {\n    return;\n  }\n  internalJoinAppend(delimiter, begin, end, output);\n}\n\n} // namespace detail\n\ntemplate <class Delim, class Iterator, class String>\nvoid join(\n    const Delim& delimiter, Iterator begin, Iterator end, String& output) {\n  detail::internalJoin(detail::prepareDelim(delimiter), begin, end, output);\n}\n\ntemplate <class OutputString>\nvoid backslashify(\n    folly::StringPiece input, OutputString& output, bool hex_style) {\n  static const char hexValues[] = \"0123456789abcdef\";\n  output.clear();\n  output.reserve(3 * input.size());\n  for (unsigned char c : input) {\n    // less than space or greater than '~' are considered unprintable\n    if (c < 0x20 || c > 0x7e || c == '\\\\') {\n      bool hex_append = false;\n      output.push_back('\\\\');\n      if (hex_style) {\n        hex_append = true;\n      } else {\n        if (c == '\\r') {\n          output += 'r';\n        } else if (c == '\\n') {\n          output += 'n';\n        } else if (c == '\\t') {\n          output += 't';\n        } else if (c == '\\a') {\n          output += 'a';\n        } else if (c == '\\b') {\n          output += 'b';\n        } else if (c == '\\0') {\n          output += '0';\n        } else if (c == '\\\\') {\n          output += '\\\\';\n        } else {\n          hex_append = true;\n        }\n      }\n      if (hex_append) {\n        output.push_back('x');\n        output.push_back(hexValues[(c >> 4) & 0xf]);\n        output.push_back(hexValues[c & 0xf]);\n      }\n    } else {\n      output += c;\n    }\n  }\n}\n\ntemplate <class String1, class String2>\nvoid humanify(const String1& input, String2& output) {\n  size_t numUnprintable = 0;\n  size_t numPrintablePrefix = 0;\n  for (unsigned char c : input) {\n    if (c < 0x20 || c > 0x7e || c == '\\\\') {\n      ++numUnprintable;\n    }\n    if (numUnprintable == 0) {\n      ++numPrintablePrefix;\n    }\n  }\n\n  // hexlify doubles a string's size; backslashify can potentially\n  // explode it by 4x.  Now, the printable range of the ascii\n  // \"spectrum\" is around 95 out of 256 values, so a \"random\" binary\n  // string should be around 60% unprintable.  We use a 50% heuristic\n  // here, so if a string is 60% unprintable, then we just use hex\n  // output.  Otherwise we backslash.\n  //\n  // UTF8 is completely ignored; as a result, utf8 characters will\n  // likely be \\x escaped (since most common glyphs fit in two bytes).\n  // This is a tradeoff of complexity/speed instead of a convenience\n  // that likely would rarely matter.  Moreover, this function is more\n  // about displaying underlying bytes, not about displaying glyphs\n  // from languages.\n  if (numUnprintable == 0) {\n    output = input;\n  } else if (5 * numUnprintable >= 3 * input.size()) {\n    // However!  If we have a \"meaningful\" prefix of printable\n    // characters, say 20% of the string, we backslashify under the\n    // assumption viewing the prefix as ascii is worth blowing the\n    // output size up a bit.\n    if (5 * numPrintablePrefix >= input.size()) {\n      backslashify(input, output);\n    } else {\n      output = \"0x\";\n      hexlify(input, output, true /* append output */);\n    }\n  } else {\n    backslashify(input, output);\n  }\n}\n\ntemplate <class InputString, class OutputString>\nbool hexlify(\n    const InputString& input, OutputString& output, bool append_output) {\n  if (!append_output) {\n    output.clear();\n  }\n\n  static char hexValues[] = \"0123456789abcdef\";\n  auto j = output.size();\n  output.resize(2 * input.size() + output.size());\n  for (size_t i = 0; i < input.size(); ++i) {\n    int ch = input[i];\n    output[j++] = hexValues[(ch >> 4) & 0xf];\n    output[j++] = hexValues[ch & 0xf];\n  }\n  return true;\n}\n\ntemplate <class InputString, class OutputString>\nbool unhexlify(const InputString& input, OutputString& output) {\n  if (input.size() % 2 != 0) {\n    return false;\n  }\n  output.resize(input.size() / 2);\n  int j = 0;\n\n  for (size_t i = 0; i < input.size(); i += 2) {\n    int highBits = detail::hexTable[static_cast<uint8_t>(input[i])];\n    int lowBits = detail::hexTable[static_cast<uint8_t>(input[i + 1])];\n    if ((highBits | lowBits) & 0x10) {\n      // One of the characters wasn't a hex digit\n      return false;\n    }\n    output[j++] = (highBits << 4) + lowBits;\n  }\n  return true;\n}\n\nnamespace detail {\n/**\n * Hex-dump at most 16 bytes starting at offset from a memory area of size\n * bytes.  Return the number of bytes actually dumped.\n */\nsize_t hexDumpLine(\n    const void* ptr, size_t offset, size_t size, std::string& line);\n} // namespace detail\n\ntemplate <class OutIt>\nvoid hexDump(const void* ptr, size_t size, OutIt out) {\n  size_t offset = 0;\n  std::string line;\n  while (offset < size) {\n    offset += detail::hexDumpLine(ptr, offset, size, line);\n    *out++ = line;\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/String.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/String.h>\n\n#include <cctype>\n#include <cerrno>\n#include <cstdarg>\n#include <cstring>\n#include <iterator>\n#include <sstream>\n#include <stdexcept>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/container/Array.h>\n\nnamespace folly {\n\nstatic_assert(IsConvertible<float>::value);\nstatic_assert(IsConvertible<int>::value);\nstatic_assert(IsConvertible<bool>::value);\nstatic_assert(IsConvertible<int>::value);\nstatic_assert(!IsConvertible<std::vector<int>>::value);\n\nnamespace detail {\n\nstruct string_table_c_escape_make_item {\n  constexpr char operator()(std::size_t index) const {\n    // clang-format off\n    return\n        index == '\"' ? '\"' :\n        index == '\\\\' ? '\\\\' :\n        index == '?' ? '?' :\n        index == '\\n' ? 'n' :\n        index == '\\r' ? 'r' :\n        index == '\\t' ? 't' :\n        index < 32 || index > 126 ? 'O' : // octal\n        'P'; // printable\n    // clang-format on\n  }\n};\n\nstruct string_table_c_unescape_make_item {\n  constexpr char operator()(std::size_t index) const {\n    // clang-format off\n    return\n        index == '\\'' ? '\\'' :\n        index == '?' ? '?' :\n        index == '\\\\' ? '\\\\' :\n        index == '\"' ? '\"' :\n        index == 'a' ? '\\a' :\n        index == 'b' ? '\\b' :\n        index == 'f' ? '\\f' :\n        index == 'n' ? '\\n' :\n        index == 'r' ? '\\r' :\n        index == 't' ? '\\t' :\n        index == 'v' ? '\\v' :\n        index >= '0' && index <= '7' ? 'O' : // octal\n        index == 'x' ? 'X' : // hex\n        'I'; // invalid\n    // clang-format on\n  }\n};\n\nstruct string_table_hex_make_item {\n  constexpr unsigned char operator()(std::size_t index) const {\n    // clang-format off\n    return static_cast<unsigned char>(\n        index >= '0' && index <= '9' ? index - '0' :\n        index >= 'a' && index <= 'f' ? index - 'a' + 10 :\n        index >= 'A' && index <= 'F' ? index - 'A' + 10 :\n        16);\n    // clang-format on\n  }\n};\n\nstruct string_table_uri_escape_make_item {\n  //  0 = passthrough\n  //  1 = unused\n  //  2 = safe in path (/)\n  //  3 = space (replace with '+' in query)\n  //  4 = always percent-encode\n  constexpr unsigned char operator()(std::size_t index) const {\n    // clang-format off\n    return\n        index >= '0' && index <= '9' ? 0 :\n        index >= 'A' && index <= 'Z' ? 0 :\n        index >= 'a' && index <= 'z' ? 0 :\n        index == '-' ? 0 :\n        index == '_' ? 0 :\n        index == '.' ? 0 :\n        index == '~' ? 0 :\n        index == '/' ? 2 :\n        index == ' ' ? 3 :\n        4;\n    // clang-format on\n  }\n};\n\nFOLLY_STORAGE_CONSTEXPR decltype(cEscapeTable) cEscapeTable =\n    make_array_with<256>(string_table_c_escape_make_item{});\nFOLLY_STORAGE_CONSTEXPR decltype(cUnescapeTable) cUnescapeTable =\n    make_array_with<256>(string_table_c_unescape_make_item{});\nFOLLY_STORAGE_CONSTEXPR decltype(hexTable) hexTable =\n    make_array_with<256>(string_table_hex_make_item{});\nFOLLY_STORAGE_CONSTEXPR decltype(uriEscapeTable) uriEscapeTable =\n    make_array_with<256>(string_table_uri_escape_make_item{});\n\n} // namespace detail\n\nstatic inline bool is_oddspace(char c) {\n  return c == '\\n' || c == '\\t' || c == '\\r';\n}\n\nStringPiece ltrimWhitespace(StringPiece sp) {\n  // Spaces other than ' ' characters are less common but should be\n  // checked.  This configuration where we loop on the ' '\n  // separately from oddspaces was empirically fastest.\n\n  while (true) {\n    while (!sp.empty() && sp.front() == ' ') {\n      sp.pop_front();\n    }\n    if (!sp.empty() && is_oddspace(sp.front())) {\n      sp.pop_front();\n      continue;\n    }\n\n    return sp;\n  }\n}\n\nStringPiece rtrimWhitespace(StringPiece sp) {\n  // Spaces other than ' ' characters are less common but should be\n  // checked.  This configuration where we loop on the ' '\n  // separately from oddspaces was empirically fastest.\n\n  while (true) {\n    while (!sp.empty() && sp.back() == ' ') {\n      sp.pop_back();\n    }\n    if (!sp.empty() && is_oddspace(sp.back())) {\n      sp.pop_back();\n      continue;\n    }\n\n    return sp;\n  }\n}\n\nnamespace {\n\nint stringAppendfImplHelper(\n    char* buf, size_t bufsize, const char* format, va_list args) {\n  va_list args_copy;\n  va_copy(args_copy, args);\n  int bytes_used = vsnprintf(buf, bufsize, format, args_copy);\n  va_end(args_copy);\n  return bytes_used;\n}\n\nvoid stringAppendfImpl(std::string& output, const char* format, va_list args) {\n  // Very simple; first, try to avoid an allocation by using an inline\n  // buffer.  If that fails to hold the output string, allocate one on\n  // the heap, use it instead.\n  //\n  // It is hard to guess the proper size of this buffer; some\n  // heuristics could be based on the number of format characters, or\n  // static analysis of a codebase.  Or, we can just pick a number\n  // that seems big enough for simple cases (say, one line of text on\n  // a terminal) without being large enough to be concerning as a\n  // stack variable.\n  std::array<char, 128> inline_buffer;\n\n  int bytes_used = stringAppendfImplHelper(\n      inline_buffer.data(), inline_buffer.size(), format, args);\n  if (bytes_used < 0) {\n    throw std::runtime_error(\n        to<std::string>(\n            \"Invalid format string; snprintf returned negative \"\n            \"with format string: \",\n            format));\n  }\n\n  if (static_cast<size_t>(bytes_used) < inline_buffer.size()) {\n    output.append(inline_buffer.data(), size_t(bytes_used));\n    return;\n  }\n\n  // Couldn't fit.  Heap allocate a buffer, oh well.\n  std::unique_ptr<char[]> heap_buffer(new char[size_t(bytes_used + 1)]);\n  int final_bytes_used = stringAppendfImplHelper(\n      heap_buffer.get(), size_t(bytes_used + 1), format, args);\n  // The second call can take fewer bytes if, for example, we were printing a\n  // string buffer with null-terminating char using a width specifier -\n  // vsnprintf(\"%.*s\", buf.size(), buf)\n  CHECK(bytes_used >= final_bytes_used);\n\n  // We don't keep the trailing '\\0' in our output string\n  output.append(heap_buffer.get(), size_t(final_bytes_used));\n}\n\n} // namespace\n\nstd::string stringPrintf(const char* format, ...) {\n  va_list ap;\n  va_start(ap, format);\n  SCOPE_EXIT {\n    va_end(ap);\n  };\n  return stringVPrintf(format, ap);\n}\n\nstd::string stringVPrintf(const char* format, va_list ap) {\n  std::string ret;\n  stringAppendfImpl(ret, format, ap);\n  return ret;\n}\n\n// Basic declarations; allow for parameters of strings and string\n// pieces to be specified.\nstd::string& stringAppendf(std::string* output, const char* format, ...) {\n  va_list ap;\n  va_start(ap, format);\n  SCOPE_EXIT {\n    va_end(ap);\n  };\n  return stringVAppendf(output, format, ap);\n}\n\nstd::string& stringVAppendf(\n    std::string* output, const char* format, va_list ap) {\n  stringAppendfImpl(*output, format, ap);\n  return *output;\n}\n\nvoid stringPrintf(std::string* output, const char* format, ...) {\n  va_list ap;\n  va_start(ap, format);\n  SCOPE_EXIT {\n    va_end(ap);\n  };\n  return stringVPrintf(output, format, ap);\n}\n\nvoid stringVPrintf(std::string* output, const char* format, va_list ap) {\n  output->clear();\n  stringAppendfImpl(*output, format, ap);\n}\n\nnamespace {\n\nstruct PrettySuffix {\n  const char* suffix;\n  double val;\n};\n\nconst PrettySuffix kPrettyTimeSuffixes[] = {\n    {\"s \", 1e0L},\n    {\"ms\", 1e-3L},\n    {\"us\", 1e-6L},\n    {\"ns\", 1e-9L},\n    {\"ps\", 1e-12L},\n    {\"s \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyTimeHmsSuffixes[] = {\n    {\"h \", 60L * 60L},\n    {\"m \", 60L},\n    {\"s \", 1e0L},\n    {\"ms\", 1e-3L},\n    {\"us\", 1e-6L},\n    {\"ns\", 1e-9L},\n    {\"ps\", 1e-12L},\n    {\"s \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyBytesMetricSuffixes[] = {\n    {\"EB\", 1e18L},\n    {\"PB\", 1e15L},\n    {\"TB\", 1e12L},\n    {\"GB\", 1e9L},\n    {\"MB\", 1e6L},\n    {\"kB\", 1e3L},\n    {\"B \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyBytesBinarySuffixes[] = {\n    {\"EB\", int64_t(1) << 60},\n    {\"PB\", int64_t(1) << 50},\n    {\"TB\", int64_t(1) << 40},\n    {\"GB\", int64_t(1) << 30},\n    {\"MB\", int64_t(1) << 20},\n    {\"kB\", int64_t(1) << 10},\n    {\"B \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyBytesBinaryIECSuffixes[] = {\n    {\"EiB\", int64_t(1) << 60},\n    {\"PiB\", int64_t(1) << 50},\n    {\"TiB\", int64_t(1) << 40},\n    {\"GiB\", int64_t(1) << 30},\n    {\"MiB\", int64_t(1) << 20},\n    {\"KiB\", int64_t(1) << 10},\n    {\"B  \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyUnitsMetricSuffixes[] = {\n    {\"qntl\", 1e18L},\n    {\"qdrl\", 1e15L},\n    {\"tril\", 1e12L},\n    {\"bil\", 1e9L},\n    {\"M\", 1e6L},\n    {\"k\", 1e3L},\n    {\" \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyUnitsBinarySuffixes[] = {\n    {\"E\", int64_t(1) << 60},\n    {\"P\", int64_t(1) << 50},\n    {\"T\", int64_t(1) << 40},\n    {\"G\", int64_t(1) << 30},\n    {\"M\", int64_t(1) << 20},\n    {\"k\", int64_t(1) << 10},\n    {\" \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyUnitsBinaryIECSuffixes[] = {\n    {\"Ei\", int64_t(1) << 60},\n    {\"Pi\", int64_t(1) << 50},\n    {\"Ti\", int64_t(1) << 40},\n    {\"Gi\", int64_t(1) << 30},\n    {\"Mi\", int64_t(1) << 20},\n    {\"Ki\", int64_t(1) << 10},\n    {\"  \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix kPrettySISuffixes[] = {\n    {\"Y\", 1e24L},  {\"Z\", 1e21L},  {\"E\", 1e18L},  {\"P\", 1e15L},  {\"T\", 1e12L},\n    {\"G\", 1e9L},   {\"M\", 1e6L},   {\"k\", 1e3L},   {\"h\", 1e2L},   {\"da\", 1e1L},\n    {\"d\", 1e-1L},  {\"c\", 1e-2L},  {\"m\", 1e-3L},  {\"u\", 1e-6L},  {\"n\", 1e-9L},\n    {\"p\", 1e-12L}, {\"f\", 1e-15L}, {\"a\", 1e-18L}, {\"z\", 1e-21L}, {\"y\", 1e-24L},\n    {\" \", 0},      {nullptr, 0},\n};\n\nconst PrettySuffix kPrettyBitsMetricSuffixes[] = {\n    {\"Eb\", 1e18L},\n    {\"Pb\", 1e15L},\n    {\"Tb\", 1e12L},\n    {\"Gb\", 1e9L},\n    {\"Mb\", 1e6L},\n    {\"kb\", 1e3L},\n    {\"b \", 0},\n    {nullptr, 0},\n};\n\nconst PrettySuffix* const kPrettySuffixes[PRETTY_NUM_TYPES] = {\n    kPrettyTimeSuffixes,\n    kPrettyTimeHmsSuffixes,\n    kPrettyBytesMetricSuffixes,\n    kPrettyBytesBinarySuffixes,\n    kPrettyBytesBinaryIECSuffixes,\n    kPrettyUnitsMetricSuffixes,\n    kPrettyUnitsBinarySuffixes,\n    kPrettyUnitsBinaryIECSuffixes,\n    kPrettySISuffixes,\n    kPrettyBitsMetricSuffixes,\n};\n\n} // namespace\n\nstd::string prettyPrint(double val, PrettyType type, bool addSpace) {\n  char buf[100];\n\n  // pick the suffixes to use\n  assert(type >= 0);\n  assert(type < PRETTY_NUM_TYPES);\n  const PrettySuffix* suffixes = kPrettySuffixes[type];\n\n  // find the first suffix we're bigger than -- then use it\n  double abs_val = fabs(val);\n  for (int i = 0; suffixes[i].suffix; ++i) {\n    if (abs_val >= suffixes[i].val) {\n      snprintf(\n          buf,\n          sizeof buf,\n          \"%.4g%s%s\",\n          (suffixes[i].val != 0. ? (val / suffixes[i].val) : val),\n          (addSpace ? \" \" : \"\"),\n          suffixes[i].suffix);\n      return std::string(buf);\n    }\n  }\n\n  // no suffix, we've got a tiny value -- just print it in sci-notation\n  snprintf(buf, sizeof buf, \"%.4g\", val);\n  return std::string(buf);\n}\n\n// TODO:\n// 1) Benchmark & optimize\ndouble prettyToDouble(\n    folly::StringPiece* const prettyString, const PrettyType type) {\n  auto value = folly::to<double>(prettyString);\n  while (!prettyString->empty() && std::isspace(prettyString->front())) {\n    prettyString->advance(1); // Skipping spaces between number and suffix\n  }\n  const PrettySuffix* suffixes = kPrettySuffixes[type];\n  int longestPrefixLen = -1;\n  int bestPrefixId = -1;\n  for (int j = 0; suffixes[j].suffix; ++j) {\n    if (suffixes[j].suffix[0] == ' ') { // Checking for \" \" -> number rule.\n      if (longestPrefixLen == -1) {\n        longestPrefixLen = 0; // No characters to skip\n        bestPrefixId = j;\n      }\n    } else if (prettyString->startsWith(suffixes[j].suffix)) {\n      int suffixLen = int(strlen(suffixes[j].suffix));\n      // We are looking for a longest suffix matching prefix of the string\n      // after numeric value. We need this in case suffixes have common prefix.\n      if (suffixLen > longestPrefixLen) {\n        longestPrefixLen = suffixLen;\n        bestPrefixId = j;\n      }\n    }\n  }\n  if (bestPrefixId == -1) { // No valid suffix rule found\n    throw std::invalid_argument(\n        folly::to<std::string>(\n            \"Unable to parse suffix \\\"\", *prettyString, \"\\\"\"));\n  }\n  prettyString->advance(size_t(longestPrefixLen));\n  return suffixes[bestPrefixId].val != 0.\n      ? value * suffixes[bestPrefixId].val\n      : value;\n}\n\ndouble prettyToDouble(folly::StringPiece prettyString, const PrettyType type) {\n  double result = prettyToDouble(&prettyString, type);\n  detail::enforceWhitespace(prettyString);\n  return result;\n}\n\nstd::string hexDump(const void* ptr, size_t size) {\n  std::ostringstream os;\n  hexDump(ptr, size, std::ostream_iterator<StringPiece>(os, \"\\n\"));\n  return os.str();\n}\n\n// There are two variants of `strerror_r` function, one returns\n// `int`, and another returns `char*`. Selecting proper version using\n// preprocessor macros portably is extremely hard.\n//\n// For example, on Android function signature depends on `__USE_GNU` and\n// `__ANDROID_API__` macros (https://git.io/fjBBE).\n//\n// So we are using C++ overloading trick: we pass a pointer of\n// `strerror_r` to `invoke_strerror_r` function, and C++ compiler\n// selects proper function.\n\n[[maybe_unused]] static std::string invoke_strerror_r(\n    int (*strerror_r)(int, char*, size_t), int err, char* buf, size_t buflen) {\n  // Using XSI-compatible strerror_r\n  int r = strerror_r(err, buf, buflen);\n\n  // OSX/FreeBSD use EINVAL and Linux uses -1 so just check for non-zero\n  if (r != 0) {\n    return to<std::string>(\n        \"Unknown error \", err, \" (strerror_r failed with error \", errno, \")\");\n  } else {\n    return buf;\n  }\n}\n\n[[maybe_unused]] static std::string invoke_strerror_r(\n    char* (*strerror_r)(int, char*, size_t),\n    int err,\n    char* buf,\n    size_t buflen) {\n  // Using GNU strerror_r\n  return strerror_r(err, buf, buflen);\n}\n\nstd::string errnoStr(int err) {\n  int savedErrno = errno;\n\n  // Ensure that we reset errno upon exit.\n  auto guard(makeGuard([&] { errno = savedErrno; }));\n\n  char buf[1024];\n  buf[0] = '\\0';\n\n  std::string result;\n\n  // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/strerror_r.3.html\n  // http://www.kernel.org/doc/man-pages/online/pages/man3/strerror.3.html\n#if defined(_WIN32) && (defined(__MINGW32__) || defined(_MSC_VER))\n  // mingw64 has no strerror_r, but Windows has strerror_s, which C11 added\n  // as well. So maybe we should use this across all platforms (together\n  // with strerrorlen_s). Note strerror_r and _s have swapped args.\n  int r = strerror_s(buf, sizeof(buf), err);\n  if (r != 0) {\n    result = to<std::string>(\n        \"Unknown error \", err, \" (strerror_r failed with error \", errno, \")\");\n  } else {\n    result.assign(buf);\n  }\n#else\n  // Using any strerror_r\n  result.assign(invoke_strerror_r(strerror_r, err, buf, sizeof(buf)));\n#endif\n\n  return result;\n}\n\nnamespace {\n\nvoid toLowerAscii8(char& c) {\n  // Branchless tolower, based on the input-rotating trick described\n  // at http://www.azillionmonkeys.com/qed/asmexample.html\n  //\n  // This algorithm depends on an observation: each uppercase\n  // ASCII character can be converted to its lowercase equivalent\n  // by adding 0x20.\n\n  // Step 1: Clear the high order bit. We'll deal with it in Step 5.\n  auto rotated = uint8_t(c & 0x7f);\n  // Currently, the value of rotated, as a function of the original c is:\n  //   below 'A':   0- 64\n  //   'A'-'Z':    65- 90\n  //   above 'Z':  91-127\n\n  // Step 2: Add 0x25 (37)\n  rotated += 0x25;\n  // Now the value of rotated, as a function of the original c is:\n  //   below 'A':   37-101\n  //   'A'-'Z':    102-127\n  //   above 'Z':  128-164\n\n  // Step 3: clear the high order bit\n  rotated &= 0x7f;\n  //   below 'A':   37-101\n  //   'A'-'Z':    102-127\n  //   above 'Z':    0- 36\n\n  // Step 4: Add 0x1a (26)\n  rotated += 0x1a;\n  //   below 'A':   63-127\n  //   'A'-'Z':    128-153\n  //   above 'Z':   25- 62\n\n  // At this point, note that only the uppercase letters have been\n  // transformed into values with the high order bit set (128 and above).\n\n  // Step 5: Shift the high order bit 2 spaces to the right: the spot\n  // where the only 1 bit in 0x20 is.  But first, how we ignored the\n  // high order bit of the original c in step 1?  If that bit was set,\n  // we may have just gotten a false match on a value in the range\n  // 128+'A' to 128+'Z'.  To correct this, need to clear the high order\n  // bit of rotated if the high order bit of c is set.  Since we don't\n  // care about the other bits in rotated, the easiest thing to do\n  // is invert all the bits in c and bitwise-and them with rotated.\n  rotated &= ~c;\n  rotated >>= 2;\n\n  // Step 6: Apply a mask to clear everything except the 0x20 bit\n  // in rotated.\n  rotated &= 0x20;\n\n  // At this point, rotated is 0x20 if c is 'A'-'Z' and 0x00 otherwise\n\n  // Step 7: Add rotated to c\n  c += char(rotated);\n}\n\nvoid toLowerAscii32(uint32_t& c) {\n  // Besides being branchless, the algorithm in toLowerAscii8() has another\n  // interesting property: None of the addition operations will cause\n  // an overflow in the 8-bit value.  So we can pack four 8-bit values\n  // into a uint32_t and run each operation on all four values in parallel\n  // without having to use any CPU-specific SIMD instructions.\n  uint32_t rotated = c & uint32_t(0x7f7f7f7fUL);\n  rotated += uint32_t(0x25252525UL);\n  rotated &= uint32_t(0x7f7f7f7fUL);\n  rotated += uint32_t(0x1a1a1a1aUL);\n\n  // Step 5 involves a shift, so some bits will spill over from each\n  // 8-bit value into the next.  But that's okay, because they're bits\n  // that will be cleared by the mask in step 6 anyway.\n  rotated &= ~c;\n  rotated >>= 2;\n  rotated &= uint32_t(0x20202020UL);\n  c += rotated;\n}\n\nvoid toLowerAscii64(uint64_t& c) {\n  // 64-bit version of toLower32\n  uint64_t rotated = c & uint64_t(0x7f7f7f7f7f7f7f7fULL);\n  rotated += uint64_t(0x2525252525252525ULL);\n  rotated &= uint64_t(0x7f7f7f7f7f7f7f7fULL);\n  rotated += uint64_t(0x1a1a1a1a1a1a1a1aULL);\n  rotated &= ~c;\n  rotated >>= 2;\n  rotated &= uint64_t(0x2020202020202020ULL);\n  c += rotated;\n}\n\n} // namespace\n\nvoid toLowerAscii(char* str, size_t length) {\n  static const size_t kAlignMask64 = 7;\n  static const size_t kAlignMask32 = 3;\n\n  // Convert a character at a time until we reach an address that\n  // is at least 32-bit aligned\n  auto n = (size_t)str;\n  n &= kAlignMask32;\n  n = std::min(n, length);\n  size_t offset = 0;\n  if (n != 0) {\n    n = std::min(4 - n, length);\n    do {\n      toLowerAscii8(str[offset]);\n      offset++;\n    } while (offset < n);\n  }\n\n  n = (size_t)(str + offset);\n  n &= kAlignMask64;\n  if ((n != 0) && (offset + 4 <= length)) {\n    // The next address is 32-bit aligned but not 64-bit aligned.\n    // Convert the next 4 bytes in order to get to the 64-bit aligned\n    // part of the input.\n    toLowerAscii32(*(uint32_t*)(str + offset));\n    offset += 4;\n  }\n\n  // Convert 8 characters at a time\n  while (offset + 8 <= length) {\n    toLowerAscii64(*(uint64_t*)(str + offset));\n    offset += 8;\n  }\n\n  // Convert 4 characters at a time\n  while (offset + 4 <= length) {\n    toLowerAscii32(*(uint32_t*)(str + offset));\n    offset += 4;\n  }\n\n  // Convert any characters remaining after the last 4-byte aligned group\n  while (offset < length) {\n    toLowerAscii8(str[offset]);\n    offset++;\n  }\n}\n\nnamespace detail {\n\nsize_t hexDumpLine(\n    const void* ptr, size_t offset, size_t size, std::string& line) {\n  static char hexValues[] = \"0123456789abcdef\";\n  // Line layout:\n  // 8: address\n  // 1: space\n  // (1+2)*16: hex bytes, each preceded by a space\n  // 1: space separating the two halves\n  // 3: \"  |\"\n  // 16: characters\n  // 1: \"|\"\n  // Total: 78\n  line.clear();\n  line.reserve(78);\n  const uint8_t* p = reinterpret_cast<const uint8_t*>(ptr) + offset;\n  size_t n = std::min(size - offset, size_t(16));\n  line.push_back(hexValues[(offset >> 28) & 0xf]);\n  line.push_back(hexValues[(offset >> 24) & 0xf]);\n  line.push_back(hexValues[(offset >> 20) & 0xf]);\n  line.push_back(hexValues[(offset >> 16) & 0xf]);\n  line.push_back(hexValues[(offset >> 12) & 0xf]);\n  line.push_back(hexValues[(offset >> 8) & 0xf]);\n  line.push_back(hexValues[(offset >> 4) & 0xf]);\n  line.push_back(hexValues[offset & 0xf]);\n  line.push_back(' ');\n\n  for (size_t i = 0; i < n; i++) {\n    if (i == 8) {\n      line.push_back(' ');\n    }\n\n    line.push_back(' ');\n    line.push_back(hexValues[(p[i] >> 4) & 0xf]);\n    line.push_back(hexValues[p[i] & 0xf]);\n  }\n\n  // 3 spaces for each byte we're not printing, one separating the halves\n  // if necessary\n  line.append(3 * (16 - n) + (n <= 8), ' ');\n  line.append(\"  |\");\n\n  for (size_t i = 0; i < n; i++) {\n    char c = (p[i] >= 32 && p[i] <= 126 ? static_cast<char>(p[i]) : '.');\n    line.push_back(c);\n  }\n  line.append(16 - n, ' ');\n  line.push_back('|');\n  DCHECK_EQ(line.size(), 78u);\n\n  return n;\n}\n\n} // namespace detail\n\nstd::string stripLeftMargin(std::string s) {\n  std::vector<StringPiece> pieces;\n  split('\\n', s, pieces);\n  auto piecer = range(pieces);\n\n  auto piece = (piecer.end() - 1);\n  auto needle = std::find_if(piece->begin(), piece->end(), [](char c) {\n    return c != ' ' && c != '\\t';\n  });\n  if (needle == piece->end()) {\n    (piecer.end() - 1)->clear();\n  }\n  piece = piecer.begin();\n  needle = std::find_if(piece->begin(), piece->end(), [](char c) {\n    return c != ' ' && c != '\\t';\n  });\n  if (needle == piece->end()) {\n    piecer.erase(piecer.begin(), piecer.begin() + 1);\n  }\n\n  const auto sentinel = std::numeric_limits<size_t>::max();\n  auto indent = sentinel;\n  size_t max_length = 0;\n  for (piece = piecer.begin(); piece != piecer.end(); piece++) {\n    needle = std::find_if(piece->begin(), piece->end(), [](char c) {\n      return c != ' ' && c != '\\t';\n    });\n    if (needle != piece->end()) {\n      indent = std::min<size_t>(indent, size_t(needle - piece->begin()));\n    } else {\n      max_length = std::max<size_t>(piece->size(), max_length);\n    }\n  }\n  indent = indent == sentinel ? max_length : indent;\n  for (piece = piecer.begin(); piece != piecer.end(); piece++) {\n    if (piece->size() < indent) {\n      piece->clear();\n    } else {\n      piece->erase(piece->begin(), piece->begin() + indent);\n    }\n  }\n  return join(\"\\n\", piecer);\n}\n\nbool SubstringConversionCode::operator==(\n    const SubstringConversionCode& other) const {\n  return this->code == other.code && this->substring == other.substring;\n}\n\nnamespace detail {\n\n// Template implementation that both concrete overloads delegate to\ntemplate <class DelimT>\nsize_t delimCountTokensImpl(DelimT delim, StringPiece sp, bool ignoreEmpty) {\n  assert(sp.empty() || sp.start() != nullptr);\n\n  const char* s = sp.start();\n  const size_t strSize = sp.size();\n  const size_t dSize = delimSize(delim);\n\n  if (dSize > strSize || dSize == 0) {\n    return (!ignoreEmpty || strSize > 0) ? 1 : 0;\n  }\n\n  size_t tokenCount = 0;\n  size_t tokenStartPos = 0;\n  size_t tokenSize = 0;\n\n  for (size_t i = 0; i <= strSize - dSize; ++i) {\n    if (atDelim(&s[i], delim)) {\n      if (!ignoreEmpty || tokenSize > 0) {\n        ++tokenCount;\n      }\n      tokenStartPos = i + dSize;\n      tokenSize = 0;\n      i += dSize - 1;\n    } else {\n      ++tokenSize;\n    }\n  }\n\n  // Count the final token\n  tokenSize = strSize - tokenStartPos;\n  if (!ignoreEmpty || tokenSize > 0) {\n    ++tokenCount;\n  }\n\n  return tokenCount;\n}\n\n// Concrete overloads that delegate to the template implementation\nsize_t delimCountTokens(char delim, StringPiece sp, bool ignoreEmpty) {\n  return delimCountTokensImpl(delim, sp, ignoreEmpty);\n}\n\nsize_t delimCountTokens(StringPiece delim, StringPiece sp, bool ignoreEmpty) {\n  return delimCountTokensImpl(delim, sp, ignoreEmpty);\n}\n\n} // namespace detail\n\n} // namespace folly\n\n#ifdef FOLLY_DEFINED_DMGL\n#undef FOLLY_DEFINED_DMGL\n#undef DMGL_NO_OPTS\n#undef DMGL_PARAMS\n#undef DMGL_ANSI\n#undef DMGL_JAVA\n#undef DMGL_VERBOSE\n#undef DMGL_TYPES\n#undef DMGL_RET_POSTFIX\n#endif\n"
  },
  {
    "path": "folly/String.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_string\n//\n\n/**\n * Convenience functions for working with strings.\n *\n * @file String.h\n */\n\n#pragma once\n#define FOLLY_STRING_H_\n\n#include <cstdarg>\n#include <exception>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <folly/Conv.h>\n#include <folly/ExceptionString.h>\n#include <folly/Optional.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/Unit.h>\n#include <folly/detail/SimpleSimdStringUtils.h>\n#include <folly/detail/SplitStringSimd.h>\n\nnamespace folly {\n\n/// SplitOptions\n///\n/// Options for controlling split() behavior. This class uses a builder pattern\n/// to allow for easy configuration and method chaining.\nclass SplitOptions {\n public:\n  struct Defaults {\n    /// preallocate\n    ///\n    /// If true, split will count the expected number of tokens first\n    /// and pre-allocate container capacity using grow_capacity_by.\n    /// This can improve performance when splitting large strings with\n    /// many tokens, but may add overhead for small strings.\n    static constexpr bool preallocate = false;\n\n    /// ignoreEmpty\n    ///\n    /// If true, adjacent delimiters are treated as one single separator\n    /// (ignoring empty tokens), otherwise empty tokens are generated.\n    static constexpr bool ignore_empty = false;\n  };\n\n  constexpr bool preallocate() const { return preallocate_; }\n  constexpr SplitOptions& preallocate(bool enable) {\n    preallocate_ = enable;\n    return *this;\n  }\n\n  constexpr bool ignore_empty() const { return ignore_empty_; }\n  constexpr SplitOptions& ignore_empty(bool enable) {\n    ignore_empty_ = enable;\n    return *this;\n  }\n\n private:\n  bool preallocate_ = Defaults::preallocate;\n  bool ignore_empty_ = Defaults::ignore_empty;\n};\n\n/**\n * @overloadbrief C-escape a string.\n *\n * Make the string suitable for representation as a C string\n * literal.  Appends the result to the output string.\n *\n * Backslashes all occurrences of backslash, double-quote, and question mark:\n *   \"  ->  \\\"\n *   \\  ->  \\\\\n *   ?  ->  \\?\n *\n * (Question marks are escaped in order to prevent creating trigraphs in\n * the output -- \"??x\" where x is one of \"=/'()!<>-\")\n *\n * Also backslashes certain whitespace characters: \\n, \\r, \\t\n *\n * Replaces all non-printable ASCII characters with backslash-octal\n * representation:\n *   <ASCII 254> -> \\376\n *\n * Note that we use backslash-octal instead of backslash-hex because the octal\n * representation is guaranteed to consume no more than 3 characters; \"\\3760\"\n * represents two characters, one with value 254, and one with value 48 ('0'),\n * whereas \"\\xfe0\" represents only one character (with value 4064, which leads\n * to implementation-defined behavior).\n */\ntemplate <class String>\nvoid cEscape(StringPiece str, String& out);\n\n/**\n * Similar to cEscape above, but returns the escaped string.\n */\ntemplate <class String>\nString cEscape(StringPiece str) {\n  String out;\n  cEscape(str, out);\n  return out;\n}\n\n/**\n * @overloadbrief C-Unescape a string.\n *\n * The opposite of cEscape above.  Appends the result\n * to the output string.\n *\n * Recognizes the standard C escape sequences:\n *\n * \\code\n * \\' \\\" \\? \\\\ \\a \\b \\f \\n \\r \\t \\v\n * \\[0-7]+\n * \\x[0-9a-fA-F]+\n * \\endcode\n *\n * In strict mode (default), throws std::invalid_argument if it encounters\n * an unrecognized escape sequence.  In non-strict mode, it leaves\n * the escape sequence unchanged.\n */\ntemplate <class String>\nvoid cUnescape(StringPiece str, String& out, bool strict = true);\n\n/**\n * Similar to cUnescape above, but returns the escaped string.\n */\ntemplate <class String>\nString cUnescape(StringPiece str, bool strict = true) {\n  String out;\n  cUnescape(str, out, strict);\n  return out;\n}\n\n/**\n * @overloadbrief URI-escape a string.\n *\n * Appends the result to the output string.\n *\n * Alphanumeric characters and other characters marked as \"unreserved\" in RFC\n * 3986 ( -_.~ ) are left unchanged.  In PATH mode, the forward slash (/) is\n * also left unchanged.  In QUERY mode, spaces are replaced by '+'.  All other\n * characters are percent-encoded.\n */\nenum class UriEscapeMode : unsigned char {\n  // The values are meaningful, see generate_escape_tables.py\n  ALL = 0,\n  QUERY = 1,\n  PATH = 2\n};\ntemplate <class String>\nvoid uriEscape(\n    StringPiece str, String& out, UriEscapeMode mode = UriEscapeMode::ALL);\n\n/**\n * Similar to uriEscape above, but returns the escaped string.\n */\ntemplate <class String>\nString uriEscape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) {\n  String out;\n  uriEscape(str, out, mode);\n  return out;\n}\n\n/**\n * @overloadbrief URI-unescape a string.\n *\n * Appends the result to the output string.\n *\n * In QUERY mode, '+' are replaced by space.  %XX sequences are decoded if\n * XX is a valid hex sequence, otherwise we return an unexpected\n * std::invalid_argument.\n */\ntemplate <class String>\nbool tryUriUnescape(\n    StringPiece str, String& out, UriEscapeMode mode = UriEscapeMode::ALL);\n\n/**\n * Similar to tryUriUnescape above, but returning the unescaped string as a\n * folly::Expected.\n */\ntemplate <class String>\nfolly::Optional<String> tryUriUnescape(\n    StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) {\n  String out;\n  auto success = tryUriUnescape(str, out, mode);\n\n  if (!success) {\n    return folly::none;\n  }\n\n  return out;\n}\n\n/**\n * Similar to tryUriUnescape above, but without folly::Expected wrapping, and\n * throwing std::invalid_argument on malformed input.\n */\ntemplate <class String>\nvoid uriUnescape(\n    StringPiece str, String& out, UriEscapeMode mode = UriEscapeMode::ALL);\n\n/**\n * Similar to uriUnescape above, but returns the unescaped string.\n */\ntemplate <class String>\nString uriUnescape(StringPiece str, UriEscapeMode mode = UriEscapeMode::ALL) {\n  String out;\n  uriUnescape(str, out, mode);\n  return out;\n}\n\n/**\n * @overloadbrief printf into a string.\n *\n * stringPrintf is much like printf but deposits its result into a\n * string. Two signatures are supported: the first simply returns the\n * resulting string, and the second appends the produced characters to\n * the specified string and returns a reference to it.\n */\nstd::string stringPrintf(FOLLY_PRINTF_FORMAT const char* format, ...)\n    FOLLY_PRINTF_FORMAT_ATTR(1, 2);\n\n/* Similar to stringPrintf, with different signature. */\nvoid stringPrintf(std::string* out, FOLLY_PRINTF_FORMAT const char* format, ...)\n    FOLLY_PRINTF_FORMAT_ATTR(2, 3);\n\n/**\n * Append printf-style output to string.\n */\nstd::string& stringAppendf(\n    std::string* output, FOLLY_PRINTF_FORMAT const char* format, ...)\n    FOLLY_PRINTF_FORMAT_ATTR(2, 3);\n\n/**\n * @overloadbrief stringPrintf with va_list argument\n *\n * As with vsnprintf() itself, the value of ap is undefined after the call.\n * These functions do not call va_end() on ap.\n */\nstd::string stringVPrintf(const char* format, va_list ap);\nvoid stringVPrintf(std::string* out, const char* format, va_list ap);\n\n/**\n * Append va_list printf-style output to string.\n */\nstd::string& stringVAppendf(std::string* out, const char* format, va_list ap);\n\n/**\n * Backslashify a string.\n *\n * That is, replace non-printable characters\n * with C-style (but NOT C compliant) \"\\xHH\" encoding.  If hex_style\n * is false, then shorthand notations like \"\\0\" will be used instead\n * of \"\\x00\" for the most common backslash cases.\n *\n * There are two forms, one returning the input string, and one\n * creating output in the specified output string.\n *\n * This is mainly intended for printing to a terminal, so it is not\n * particularly optimized.\n *\n * Do *not* use this in situations where you expect to be able to feed\n * the string to a C or C++ compiler, as there are nuances with how C\n * parses such strings that lead to failures.  This is for display\n * purposed only.  If you want a string you can embed for use in C or\n * C++, use cEscape instead.  This function is for display purposes\n * only.\n */\ntemplate <class OutputString>\nvoid backslashify(\n    folly::StringPiece input, OutputString& output, bool hex_style = false);\n\ntemplate <class OutputString = std::string>\nOutputString backslashify(StringPiece input, bool hex_style = false) {\n  OutputString output;\n  backslashify(input, output, hex_style);\n  return output;\n}\n\n/**\n * Take a string and \"humanify\" it -- that is, make it look better.\n *\n * Since \"better\" is subjective, caveat emptor.  The basic approach is\n * to count the number of unprintable characters.  If there are none,\n * then the output is the input.  If there are relatively few, or if\n * there is a long \"enough\" prefix of printable characters, use\n * backslashify.  If it is mostly binary, then simply hex encode.\n *\n * This is an attempt to make a computer smart, and so likely is wrong\n * most of the time.\n */\ntemplate <class String1, class String2>\nvoid humanify(const String1& input, String2& output);\n\ntemplate <class String>\nString humanify(const String& input) {\n  String output;\n  humanify(input, output);\n  return output;\n}\n\n/**\n * Convert input to hexadecimal representation.\n *\n * Same functionality as Python's binascii.hexlify.  Returns true\n * on successful conversion.\n *\n * If append_output is true, append data to the output rather than\n * replace it.\n */\ntemplate <class InputString, class OutputString>\nbool hexlify(\n    const InputString& input, OutputString& output, bool append = false);\n\ntemplate <class OutputString = std::string>\nOutputString hexlify(ByteRange input) {\n  OutputString output;\n  if (!hexlify(input, output)) {\n    // hexlify() currently always returns true, so this can't really happen\n    throw_exception<std::runtime_error>(\"hexlify failed\");\n  }\n  return output;\n}\n\ntemplate <class OutputString = std::string>\nOutputString hexlify(StringPiece input) {\n  return hexlify<OutputString>(ByteRange{input});\n}\n\n/**\n * Get binary data from hexadecimal representation.\n *\n * Same functionality as Python's binascii.unhexlify.  Returns true\n * on successful conversion.\n */\ntemplate <class InputString, class OutputString>\nbool unhexlify(const InputString& input, OutputString& output);\n\ntemplate <class OutputString = std::string>\nOutputString unhexlify(StringPiece input) {\n  OutputString output;\n  if (!unhexlify(input, output)) {\n    // unhexlify() fails if the input has non-hexidecimal characters,\n    // or if it doesn't consist of a whole number of bytes\n    throw_exception<std::domain_error>(\"unhexlify() called with non-hex input\");\n  }\n  return output;\n}\n\nenum PrettyType {\n  PRETTY_TIME,\n  PRETTY_TIME_HMS,\n\n  PRETTY_BYTES_METRIC,\n  PRETTY_BYTES_BINARY,\n  PRETTY_BYTES = PRETTY_BYTES_BINARY,\n  PRETTY_BYTES_BINARY_IEC,\n  PRETTY_BYTES_IEC = PRETTY_BYTES_BINARY_IEC,\n\n  PRETTY_UNITS_METRIC,\n  PRETTY_UNITS_BINARY,\n  PRETTY_UNITS_BINARY_IEC,\n\n  PRETTY_SI,\n\n  PRETTY_BITS_METRIC,\n  PRETTY_BITS = PRETTY_BITS_METRIC,\n\n  PRETTY_NUM_TYPES,\n};\n\n/**\n * Pretty printer for numbers with units.\n *\n * A pretty-printer for numbers that appends suffixes of units of the\n * given type.  It prints 4 sig-figs of value with the most\n * appropriate unit.\n *\n * If `addSpace' is true, we put a space between the units suffix and\n * the value.\n *\n * Current types are:\n *     PRETTY_TIME         - s, ms, us, ns, etc.\n *     PRETTY_TIME_HMS     - h, m, s, ms, us, ns, etc.\n *     PRETTY_BYTES_METRIC - kB, MB, GB, etc (goes up by 10^3 = 1000 each time)\n *     PRETTY_BYTES        - kB, MB, GB, etc (goes up by 2^10 = 1024 each time)\n *     PRETTY_BYTES_IEC    - KiB, MiB, GiB, etc\n *     PRETTY_UNITS_METRIC - k, M, G, etc (goes up by 10^3 = 1000 each time)\n *     PRETTY_UNITS_BINARY - k, M, G, etc (goes up by 2^10 = 1024 each time)\n *     PRETTY_UNITS_BINARY_IEC - Ki, Mi, Gi, etc\n *     PRETTY_SI           - full SI metric prefixes from yocto to Yotta\n *                           http://en.wikipedia.org/wiki/Metric_prefix\n *     PRETTY_BITS_METRIC  - kb, Mb, Gb, etc (goes up by 10^3 = 1000 each time)\n *                           Useful for network bandwidth (e.g., 1 Gbps)\n *     PRETTY_BITS         - alias for PRETTY_BITS_METRIC\n *\n */\nstd::string prettyPrint(double val, PrettyType, bool addSpace = true);\n\n/**\n * @overloadbrief Reverse prettyPrint.\n *\n * This utility converts StringPiece in pretty format (look above) to double,\n * with progress information. Alters the  StringPiece parameter\n * to get rid of the already-parsed characters.\n * Expects string in form <floating point number> {space}* [<suffix>]\n * If string is not in correct format, utility finds longest valid prefix and\n * if there at least one, returns double value based on that prefix and\n * modifies string to what is left after parsing. Throws and std::range_error\n * exception if there is no correct parse.\n * Examples(for PRETTY_UNITS_METRIC):\n * '10M' => 10 000 000\n * '10 M' => 10 000 000\n * '10' => 10\n * '10 Mx' => 10 000 000, prettyString == \"x\"\n * 'abc' => throws std::range_error\n */\ndouble prettyToDouble(\n    folly::StringPiece* const prettyString, const PrettyType type);\n\n/**\n * Same as prettyToDouble(folly::StringPiece*, PrettyType), but\n * expects whole string to be correctly parseable. Throws std::range_error\n * otherwise\n */\ndouble prettyToDouble(folly::StringPiece prettyString, const PrettyType type);\n\n/**\n * @overloadbrief Write a hex dump of size bytes starting at ptr to out.\n *\n * The hex dump is formatted as follows:\n *\n * for the string \"abcdefghijklmnopqrstuvwxyz\\x02\"\n00000000  61 62 63 64 65 66 67 68  69 6a 6b 6c 6d 6e 6f 70  |abcdefghijklmnop|\n00000010  71 72 73 74 75 76 77 78  79 7a 02                 |qrstuvwxyz.     |\n *\n * that is, we write 16 bytes per line, both as hex bytes and as printable\n * characters.  Non-printable characters are replaced with '.'\n * Lines are written to out one by one (one StringPiece at a time) without\n * delimiters.\n */\ntemplate <class OutIt>\nvoid hexDump(const void* ptr, size_t size, OutIt out);\n\n/**\n * Return the hex dump of size bytes starting at ptr as a string.\n */\nstd::string hexDump(const void* ptr, size_t size);\n\n/**\n * Pretty print an errno.\n *\n * Return a string containing the description of the given errno value.\n * Takes care not to overwrite the actual system errno, so calling\n * errnoStr(errno) is valid.\n */\nstd::string errnoStr(int err);\n\ntemplate <typename T, std::size_t M, typename P>\nclass small_vector;\n\ntemplate <typename T, typename Allocator>\nclass fbvector;\n\nnamespace detail {\n\n// We don't use SimdSplitByCharIsDefinedFor because\n// we would like the user to get an error where they could use SIMD\n// implementation but didn't use quite correct parameters.\ntemplate <typename>\nstruct IsSplitSupportedContainer : std::false_type {};\n\ntemplate <typename T>\nusing HasSimdSplitCompatibleValueType =\n    std::is_convertible<typename T::value_type, folly::StringPiece>;\n\ntemplate <typename T, typename A>\nstruct IsSplitSupportedContainer<std::vector<T, A>> : std::true_type {};\n\ntemplate <typename T, typename A>\nstruct IsSplitSupportedContainer<fbvector<T, A>> : std::true_type {};\n\ntemplate <typename T, std::size_t M, typename P>\nstruct IsSplitSupportedContainer<small_vector<T, M, P>> : std::true_type {};\n\ntemplate <typename>\nstruct IsSimdSupportedDelim : std::false_type {};\n\ntemplate <>\nstruct IsSimdSupportedDelim<char> : std::true_type {};\n\n} // namespace detail\n\n/**\n * Split a string into a list of tokens by delimiter.\n *\n * The split interface here supports different output types, selected\n * at compile time: StringPiece, fbstring, or std::string.  If you are\n * using a vector to hold the output, it detects the type based on\n * what your vector contains.  If the output vector is not empty, split\n * will append to the end of the vector.\n *\n * You can also use splitTo() to write the output to an arbitrary\n * OutputIterator (e.g. std::inserter() on a std::set<>), in which\n * case you have to tell the function the type.  (Rationale:\n * OutputIterators don't have a value_type, so we can't detect the\n * type in splitTo without being told.)\n *\n * Examples:\n *\n *   std::vector<folly::StringPiece> v;\n *   folly::split(':', \"asd:bsd\", v);\n *\n *   folly::small_vector<folly::StringPiece, 3> v;\n *   folly::split(':', \"asd:bsd:csd\", v)\n *\n *   std::set<StringPiece> s;\n *   folly::splitTo<StringPiece>(\"::\", \"asd::bsd::asd::csd\",\n *    std::inserter(s, s.begin()));\n *\n * Split also takes a flag (ignoreEmpty) that indicates whether adjacent\n * delimiters should be treated as one single separator (ignoring empty tokens)\n * or not (generating empty tokens).\n */\n\ntemplate <class Delim, class String, class OutputType>\nFOLLY_ALWAYS_INLINE std::enable_if_t<\n    detail::IsSimdSupportedDelim<Delim>::value &&\n    detail::HasSimdSplitCompatibleValueType<OutputType>::value &&\n    detail::IsSplitSupportedContainer<OutputType>::value>\nsplit(\n    const Delim& delimiter,\n    const String& input,\n    OutputType& out,\n    const bool ignoreEmpty = false) {\n  return detail::simdSplitByChar(delimiter, input, out, ignoreEmpty);\n}\n\ntemplate <class Delim, class String, class OutputType>\nstd::enable_if_t<\n    (!detail::IsSimdSupportedDelim<Delim>::value ||\n     !detail::HasSimdSplitCompatibleValueType<OutputType>::value) &&\n    detail::IsSplitSupportedContainer<OutputType>::value>\nsplit(\n    const Delim& delimiter,\n    const String& input,\n    OutputType& out,\n    const bool ignoreEmpty = false);\n\n/**\n * Split a string into a list of tokens by delimiter with options.\n *\n * Same as split() above but with additional options to control behavior.\n * The SplitOptions allow enabling preallocation which can improve performance\n * when splitting large strings with many expected tokens.\n */\ntemplate <class Delim, class String, class OutputType>\nstd::enable_if_t<detail::IsSplitSupportedContainer<OutputType>::value> split(\n    const Delim& delimiter,\n    const String& input,\n    OutputType& out,\n    const SplitOptions& options);\n\n/**\n * split, to an output iterator\n */\ntemplate <\n    class OutputValueType,\n    class Delim,\n    class String,\n    class OutputIterator>\nvoid splitTo(\n    const Delim& delimiter,\n    const String& input,\n    OutputIterator out,\n    const bool ignoreEmpty = false);\n\nnamespace detail {\ntemplate <typename Void, typename OutputType>\nstruct IsConvertible : std::false_type {};\n\ntemplate <>\nstruct IsConvertible<void, decltype(std::ignore)> : std::true_type {};\n\ntemplate <typename OutputType>\nstruct IsConvertible<\n    void_t<decltype(parseTo(StringPiece{}, std::declval<OutputType&>()))>,\n    OutputType> : std::true_type {};\n} // namespace detail\ntemplate <typename OutputType>\nstruct IsConvertible : detail::IsConvertible<void, OutputType> {};\n\n/**\n * Split a string into a fixed number of string pieces and/or numeric types\n * by delimiter. Conversions are supported for any type which folly:to<> can\n * target, including all overloads of parseTo(). Returns 'true' if the fields\n * were all successfully populated.  Returns 'false' if there were too few\n * fields in the input, or too many fields if exact=true.  Casting exceptions\n * will not be caught.\n *\n * Examples:\n *\n *  folly::StringPiece name, key, value;\n *  if (folly::split('\\t', line, name, key, value))\n *    ...\n *\n *  folly::StringPiece name;\n *  double value;\n *  int id;\n *  if (folly::split('\\t', line, name, value, id))\n *    ...\n *\n * The 'exact' template parameter specifies how the function behaves when too\n * many fields are present in the input string. When 'exact' is set to its\n * default value of 'true', a call to split will fail if the number of fields in\n * the input string does not exactly match the number of output parameters\n * passed. If 'exact' is overridden to 'false', all remaining fields will be\n * stored, unsplit, in the last field, as shown below:\n *\n *  folly::StringPiece x, y.\n *  if (folly::split<false>(':', \"a:b:c\", x, y))\n *    assert(x == \"a\" && y == \"b:c\");\n *\n * Note that this will likely not work if the last field's target is of numeric\n * type, in which case folly::to<> will throw an exception.\n */\ntemplate <bool exact = true, class Delim, class... OutputTypes>\ntypename std::enable_if<\n    StrictConjunction<IsConvertible<OutputTypes>...>::value &&\n        sizeof...(OutputTypes) >= 1,\n    bool>::type\nsplit(const Delim& delimiter, StringPiece input, OutputTypes&... outputs);\n\n// Error type for trySplitTo(), below.\nstruct SubstringConversionCode {\n  StringPiece substring;\n  ConversionCode code;\n  bool operator==(const SubstringConversionCode& other) const;\n};\n\n/**\n * Try to split a string into a fixed number of fields by delimiter, using\n * folly::tryTo<> for conversions. types by delimiter.\n * - On success, all output values will be initialized and the 'Unit{}' value is\n *   returned. Arguments are assigned in reverse order.\n * - On failure, the first failing 'ConversionCode' is returned with its\n *   associated substring in a 'SubstringConversionCode'.\n * - String splitting is performed prior to each conversion; field values will\n *   not contain the delimiter.\n * - All custom error codes are mapped to ConversionCode::CUSTOM.\n *\n * Examples:\n *\n *  folly::StringPiece name, key, value;\n *  if (folly::trySplitTo(line, '\\t',  name, key, value))\n *    ...\n *\n *  folly::StringPiece name;\n *  double value;\n *  int id;\n *  if (folly::trySplitTo(line, '\\t', name, value, id))\n *    ...\n *\n */\ntemplate <class Delim, class... OutputTypes>\ntypename std::enable_if<\n    StrictConjunction<IsConvertible<OutputTypes>...>::value,\n    Expected<Unit, SubstringConversionCode>>::type\ntrySplitTo(StringPiece input, const Delim& delimiter, OutputTypes&... outputs);\n\n/**\n * Join list of tokens.\n *\n * Stores a string representation of tokens in the same order with\n * delimiter between each element.\n */\ntemplate <class Delim, class Iterator, class String>\nvoid join(const Delim& delimiter, Iterator begin, Iterator end, String& output);\n\ntemplate <class Delim, class Container, class String>\nvoid join(const Delim& delimiter, const Container& container, String& output) {\n  join(delimiter, container.begin(), container.end(), output);\n}\n\ntemplate <class Delim, class Value, class String>\nvoid join(\n    const Delim& delimiter,\n    const std::initializer_list<Value>& values,\n    String& output) {\n  join(delimiter, values.begin(), values.end(), output);\n}\n\ntemplate <class Delim, class Container>\nstd::string join(const Delim& delimiter, const Container& container) {\n  std::string output;\n  join(delimiter, container.begin(), container.end(), output);\n  return output;\n}\n\ntemplate <class Delim, class Value>\nstd::string join(\n    const Delim& delimiter, const std::initializer_list<Value>& values) {\n  std::string output;\n  join(delimiter, values.begin(), values.end(), output);\n  return output;\n}\n\ntemplate <\n    class Delim,\n    class Iterator,\n    typename std::enable_if<std::is_base_of<\n        std::forward_iterator_tag,\n        typename std::iterator_traits<Iterator>::iterator_category>::value>::\n        type* = nullptr>\nstd::string join(const Delim& delimiter, Iterator begin, Iterator end) {\n  std::string output;\n  join(delimiter, begin, end, output);\n  return output;\n}\n\n/**\n * Remove leading whitespace.\n *\n * Returns a subpiece with all whitespace removed from the front of @sp.\n * Whitespace means any of [' ', '\\n', '\\r', '\\t'].\n */\nStringPiece ltrimWhitespace(StringPiece sp);\n\n/**\n * Remove trailing whitespace.\n *\n * Returns a subpiece with all whitespace removed from the back of @sp.\n * Whitespace means any of [' ', '\\n', '\\r', '\\t'].\n */\nStringPiece rtrimWhitespace(StringPiece sp);\n\n/**\n * Remove leading and trailing whitespace.\n *\n * Returns a subpiece with all whitespace removed from the back and front of\n * @sp. Whitespace means any of [' ', '\\n', '\\r', '\\t'].\n */\ninline StringPiece trimWhitespace(StringPiece sp) {\n  return ltrimWhitespace(rtrimWhitespace(sp));\n}\n\n/**\n * DEPRECATED: Use ltrimWhitespace instead\n *\n * Returns a subpiece with all whitespace removed from the front of @sp.\n * Whitespace means any of [' ', '\\n', '\\r', '\\t'].\n */\ninline StringPiece skipWhitespace(StringPiece sp) {\n  return ltrimWhitespace(sp);\n}\n\n/**\n * Specify characters to ltrim.\n *\n * Returns a subpiece with all characters the provided @toTrim returns true\n * for removed from the front of @sp.\n */\ntemplate <typename ToTrim>\nStringPiece ltrim(StringPiece sp, ToTrim toTrim) {\n  while (!sp.empty() && toTrim(sp.front())) {\n    sp.pop_front();\n  }\n\n  return sp;\n}\n\n/**\n * Specify characters to rtrim.\n *\n * Returns a subpiece with all characters the provided @toTrim returns true\n * for removed from the back of @sp.\n */\ntemplate <typename ToTrim>\nStringPiece rtrim(StringPiece sp, ToTrim toTrim) {\n  while (!sp.empty() && toTrim(sp.back())) {\n    sp.pop_back();\n  }\n\n  return sp;\n}\n\n/**\n * Specify characters to trim.\n *\n * Returns a subpiece with all characters the provided @toTrim returns true\n * for removed from the back and front of @sp.\n */\ntemplate <typename ToTrim>\nStringPiece trim(StringPiece sp, ToTrim toTrim) {\n  return ltrim(rtrim(sp, std::ref(toTrim)), std::ref(toTrim));\n}\n\n/**\n * De-indent a string.\n *\n * Strips the leading and the trailing whitespace-only lines. Then looks for\n * the least indented non-whitespace-only line and removes its amount of\n * leading whitespace from every line. Assumes leading whitespace is either all\n * spaces or all tabs.\n *\n * Purpose: including a multiline string literal in source code, indented to\n * the level expected from context.\n */\nstd::string stripLeftMargin(std::string s);\n\n/**\n * Convert ascii to lowercase, in-place.\n *\n * Leaves all other characters unchanged, including those with the 0x80\n * bit set.\n * @param str String to convert\n * @param length Length of str, in bytes\n */\nvoid toLowerAscii(char* str, size_t length);\n\ninline void toLowerAscii(MutableStringPiece str) {\n  toLowerAscii(str.begin(), str.size());\n}\n\ninline void toLowerAscii(std::string& str) {\n  // str[0] is legal also if the string is empty.\n  toLowerAscii(&str[0], str.size());\n}\n\n/**\n * Returns if string contains std::isspace or std::iscntrl characters.\n **/\ninline bool hasSpaceOrCntrlSymbols(folly::StringPiece s) {\n  return detail::simdHasSpaceOrCntrlSymbols(s);\n}\n\nstruct format_string_for_each_named_arg_fn {\n  struct options {\n    bool numeric_args_as_named = false;\n\n    options& set_numeric_args_as_named(bool value) noexcept {\n      numeric_args_as_named = value;\n      return *this;\n    }\n  };\n\n  template <typename C, typename CT, typename Fn>\n  constexpr void operator()(std::basic_string_view<C, CT> str, Fn fn) const\n      noexcept(noexcept(fn(str))) {\n    return operator()(options{}, str, std::ref(fn));\n  }\n\n  template <typename C, typename CT, typename Fn>\n  constexpr void operator()(\n      options const& opts, std::basic_string_view<C, CT> str, Fn fn) const\n      noexcept(noexcept(fn(str))) {\n    using view = std::basic_string_view<C, CT>;\n    while (true) {\n      auto const pos = str.find('{');\n      auto const beg = pos == view::npos ? str.size() : pos + 1;\n      if (beg == str.size()) {\n        return; // completed\n      }\n      if (str[beg] == '{') {\n        str = str.substr(beg + 1);\n        continue; // escaped\n      }\n      auto const end = std::min(str.find('}', pos), str.find(':', pos));\n      if (end == view::npos) {\n        return; // malformed\n      }\n      auto const arg = str.substr(beg, end - beg);\n      auto const c = arg.empty() ? 0 : arg[0];\n      if (c && (opts.numeric_args_as_named || !(c >= '0' && c <= '9'))) {\n        fn(arg);\n      }\n      str = str.substr(end);\n    }\n  }\n};\n\ninline constexpr format_string_for_each_named_arg_fn\n    format_string_for_each_named_arg{};\n\nusing format_string_for_each_named_arg_options =\n    format_string_for_each_named_arg_fn::options;\n\n} // namespace folly\n\n#include <folly/String-inl.h>\n"
  },
  {
    "path": "folly/Subprocess.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\n#endif\n\n#include <folly/Subprocess.h>\n\n#if defined(__linux__)\n#include <sys/prctl.h>\n#endif\n#include <dlfcn.h>\n#include <fcntl.h>\n\n#include <algorithm>\n#include <array>\n#include <system_error>\n#include <thread>\n\n#include <boost/container/flat_set.hpp>\n#include <boost/range/adaptors.hpp>\n\n#include <folly/Conv.h>\n#include <folly/Exception.h>\n#include <folly/ScopeGuard.h>\n#include <folly/String.h>\n#include <folly/io/Cursor.h>\n#include <folly/lang/Assume.h>\n#include <folly/logging/xlog.h>\n#include <folly/portability/Dirent.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Stdlib.h>\n#include <folly/portability/SysSyscall.h>\n#include <folly/portability/Unistd.h>\n#include <folly/system/AtFork.h>\n#include <folly/system/Shell.h>\n\n/// interceptors to work around:\n///\n/// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp\n/// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/compiler-rt/lib/sanitizer_common/sanitizer_signal_interceptors.inc\n\n/// In sanitized builds, explicitly disable sanitization in the child process.\n/// * Disable sanitizer function transformation, including __tsan_func_entry and\n///   __tsan_func_exit hooks (under clang).\n/// * Bypass sanitizer interceptors of libc/posix functions, including of vfork\n///   and of all other libc/posix callees in the child process. Interceptors\n///   look like __interceptor_trampoline_{name} for libc/posix function {name}.\n\n#if __has_attribute(disable_sanitizer_instrumentation)\n#define FOLLY_DETAIL_SUBPROCESS_RAW                  \\\n  __attribute__((                                    \\\n      noinline,                                      \\\n      no_sanitize(\"address\", \"undefined\", \"thread\"), \\\n      disable_sanitizer_instrumentation))\n#else\n#define FOLLY_DETAIL_SUBPROCESS_RAW \\\n  __attribute__((noinline, no_sanitize(\"address\", \"undefined\", \"thread\")))\n#endif\n\nconstexpr int kExecFailure = 127;\nconstexpr int kChildFailure = 126;\n\nnamespace folly {\n\nusing detail::linux_syscall;\n\nnamespace detail {\n\nSubprocessFdActionsList::SubprocessFdActionsList(\n    span<value_type const> rep) noexcept\n    : begin_{rep.data()}, end_{rep.data() + rep.size()} {\n  [[maybe_unused]] auto lt = [](auto a, auto b) { return a.first < b.first; };\n  assert(std::is_sorted(begin_, end_, lt));\n  [[maybe_unused]] auto eq = [](auto a, auto b) { return a.first == b.first; };\n  assert(std::adjacent_find(begin_, end_, eq) == end_);\n}\n\nFOLLY_DETAIL_SUBPROCESS_RAW\nauto SubprocessFdActionsList::begin() const noexcept -> value_type const* {\n  return begin_;\n}\nFOLLY_DETAIL_SUBPROCESS_RAW\nauto SubprocessFdActionsList::end() const noexcept -> value_type const* {\n  return end_;\n}\nFOLLY_DETAIL_SUBPROCESS_RAW\nauto SubprocessFdActionsList::find(int fd) const noexcept -> int const* {\n  auto lo = begin_;\n  auto hi = end_;\n  while (lo < hi) {\n    auto mid = lo + (hi - lo) / 2;\n    if (mid->first == fd) {\n      return &mid->second;\n    }\n    if (mid->first < fd) {\n      lo = mid + 1;\n    } else {\n      hi = mid;\n    }\n  }\n  return nullptr;\n}\n\n// clang-format off\nstatic inline constexpr auto subprocess_libc_soname =\n    kIsLinux ? \"libc.so.6\" :\n    kIsFreeBSD ? \"libc.so.7\" :\n    kIsApple ? \"/usr/lib/libSystem.B.dylib\" :\n    nullptr;\n// clang-format on\n\ntemplate <typename Ret>\nstatic Ret subprocess_libc_load(\n    void* const handle, Ret const ptr, char const* const name) {\n  return !kIsSanitize ? ptr : reinterpret_cast<Ret>(::dlsym(handle, name));\n}\n\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_X_BASE(X) \\\n  X(_exit, _exit)                              \\\n  X(close, close)                              \\\n  X(dup2, dup2)                                \\\n  X(fcntl, fcntl)                              \\\n  X(pthread_sigmask, pthread_sigmask)          \\\n  X(signal, signal)                            \\\n  X(sprintf, sprintf)                          \\\n  X(strtol, strtol)                            \\\n  X(vfork, vfork)                              \\\n  X(write, write)\n\n#if defined(__BIONIC_INCLUDE_FORTIFY_HEADERS)\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_X_OPEN(X) \\\n  X(open, __open_real)                         \\\n  X(openat, __openat_real)\n#else\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_X_OPEN(X) \\\n  X(open, open)                                \\\n  X(openat, openat)\n#endif\n\n#if defined(__linux__)\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_X_PRCTL(X) X(prctl, prctl)\n#else\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_X_PRCTL(X)\n#endif\n\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_X(X) \\\n  FOLLY_DETAIL_SUBPROCESS_LIBC_X_BASE(X)  \\\n  FOLLY_DETAIL_SUBPROCESS_LIBC_X_OPEN(X)  \\\n  FOLLY_DETAIL_SUBPROCESS_LIBC_X_PRCTL(X)\n\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_FIELD_DECL(name, func) \\\n  static decltype(&::func) name;\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_FIELD_DEFN(name, func) \\\n  decltype(&::func) subprocess_libc::name;\n#define FOLLY_DETAIL_SUBPROCESS_LIBC_INIT(name, func) \\\n  subprocess_libc::name = subprocess_libc_load(handle, &::func, #name);\n\nstruct subprocess_libc {\n  FOLLY_DETAIL_SUBPROCESS_LIBC_X(FOLLY_DETAIL_SUBPROCESS_LIBC_FIELD_DECL)\n};\n\nFOLLY_DETAIL_SUBPROCESS_LIBC_X(FOLLY_DETAIL_SUBPROCESS_LIBC_FIELD_DEFN)\n\n__attribute__((constructor(101))) static void subprocess_libc_init() {\n  auto handle = !kIsSanitize\n      ? nullptr\n      : ::dlopen(subprocess_libc_soname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);\n  assert(!kIsSanitize || !!handle);\n  FOLLY_DETAIL_SUBPROCESS_LIBC_X(FOLLY_DETAIL_SUBPROCESS_LIBC_INIT)\n}\n\n#undef FOLLY_DETAIL_SUBPROCESS_LIBC_FIELD_DECL\n#undef FOLLY_DETAIL_SUBPROCESS_LIBC_FIELD_DEFN\n#undef FOLLY_DETAIL_SUBPROCESS_LIBC_INIT\n#undef FOLLY_DETAIL_SUBPROCESS_LIBC_X\n#undef FOLLY_DETAIL_SUBPROCESS_LIBC_X_PRCTL\n#undef FOLLY_DETAIL_SUBPROCESS_LIBC_X_OPEN\n#undef FOLLY_DETAIL_SUBPROCESS_LIBC_X_BASE\n\n} // namespace detail\n\nstruct Subprocess::SpawnRawArgs {\n  struct Scratch {\n    std::vector<std::pair<int, int>> fdActions;\n    std::vector<char*> setPrintPidToBuffer;\n    std::vector<std::pair<int, Options::AttrWithMeta<rlimit>>> rlimits;\n\n    explicit Scratch(Options const& options)\n        : fdActions{options.fdActions_.begin(), options.fdActions_.end()},\n          setPrintPidToBuffer{\n              options.setPrintPidToBuffer_.begin(),\n              options.setPrintPidToBuffer_.end()},\n          rlimits{options.rlimits_.begin(), options.rlimits_.end()} {\n      std::sort(fdActions.begin(), fdActions.end());\n    }\n  };\n\n  template <typename T>\n  struct AttrWithMeta {\n    T value{};\n    int* errout{};\n  };\n\n  static char const* getCStrForNonEmpty(std::string const& str) {\n    return str.empty() ? nullptr : str.c_str();\n  }\n\n  // from options\n  char const* childDir{};\n  AttrWithMeta<int> linuxCGroupFd{-1, nullptr};\n  AttrWithMeta<char const*> linuxCGroupPath{nullptr, nullptr};\n  bool closeOtherFds{};\n#if defined(__linux__)\n  Options::AttrWithMeta<cpu_set_t> const* cpuSet{};\n#endif\n  bool detach{};\n  detail::SubprocessFdActionsList fdActions;\n  int parentDeathSignal{};\n  bool processGroupLeader{};\n  bool usePath{};\n  Options::AttrWithMeta<uid_t> const* uid{};\n  Options::AttrWithMeta<gid_t> const* gid{};\n  Options::AttrWithMeta<uid_t> const* euid{};\n  Options::AttrWithMeta<gid_t> const* egid{};\n  char* const* setPrintPidToBufferData{};\n  size_t setPrintPidToBufferSize{};\n  std::pair<int, Options::AttrWithMeta<rlimit>> const* rlimitsData{};\n  size_t rlimitsSize{};\n\n  // assigned explicitly\n  char const* const* argv{};\n  char const* const* envv{};\n  char const* executable{};\n  ChildErrorInfo* err{};\n  sigset_t oldSignals{};\n\n  explicit SpawnRawArgs(Scratch const& scratch, Options const& options)\n      : childDir{getCStrForNonEmpty(options.childDir_)},\n        linuxCGroupFd{\n            options.linuxCGroupFd_.value, options.linuxCGroupFd_.errout},\n        linuxCGroupPath{\n            getCStrForNonEmpty(options.linuxCGroupPath_.value),\n            options.linuxCGroupPath_.errout},\n        closeOtherFds{options.closeOtherFds_},\n#if defined(__linux__)\n        cpuSet{get_pointer(options.cpuSet_)},\n#endif\n        detach{options.detach_},\n        fdActions{scratch.fdActions},\n#if defined(__linux__)\n        parentDeathSignal{options.parentDeathSignal_},\n#endif\n        processGroupLeader{options.processGroupLeader_},\n        usePath{options.usePath_},\n        uid{options.uid_.get_pointer()},\n        gid{options.gid_.get_pointer()},\n        euid{options.euid_.get_pointer()},\n        egid{options.egid_.get_pointer()},\n        setPrintPidToBufferData{scratch.setPrintPidToBuffer.data()},\n        setPrintPidToBufferSize{scratch.setPrintPidToBuffer.size()},\n        rlimitsData{scratch.rlimits.data()},\n        rlimitsSize{scratch.rlimits.size()} {\n    static_assert(std::is_standard_layout_v<Subprocess::SpawnRawArgs>);\n    static_assert(std::is_trivially_destructible_v<Subprocess::SpawnRawArgs>);\n  }\n};\n\nProcessReturnCode ProcessReturnCode::make(int status) {\n  if (!WIFEXITED(status) && !WIFSIGNALED(status)) {\n    throw std::runtime_error(\n        to<std::string>(\"Invalid ProcessReturnCode: \", status));\n  }\n  return ProcessReturnCode(status);\n}\n\nProcessReturnCode::ProcessReturnCode(ProcessReturnCode&& p) noexcept\n    : rawStatus_(p.rawStatus_) {\n  p.rawStatus_ = ProcessReturnCode::RV_NOT_STARTED;\n}\n\nProcessReturnCode& ProcessReturnCode::operator=(\n    ProcessReturnCode&& p) noexcept {\n  rawStatus_ = p.rawStatus_;\n  p.rawStatus_ = ProcessReturnCode::RV_NOT_STARTED;\n  return *this;\n}\n\nProcessReturnCode::State ProcessReturnCode::state() const {\n  if (rawStatus_ == RV_NOT_STARTED) {\n    return NOT_STARTED;\n  }\n  if (rawStatus_ == RV_RUNNING) {\n    return RUNNING;\n  }\n  if (WIFEXITED(rawStatus_)) {\n    return EXITED;\n  }\n  if (WIFSIGNALED(rawStatus_)) {\n    return KILLED;\n  }\n  assume_unreachable();\n}\n\nvoid ProcessReturnCode::enforce(State expected) const {\n  State s = state();\n  if (s != expected) {\n    throw std::logic_error(\n        to<std::string>(\n            \"Bad use of ProcessReturnCode; state is \",\n            s,\n            \" expected \",\n            expected));\n  }\n}\n\nint ProcessReturnCode::exitStatus() const {\n  enforce(EXITED);\n  return WEXITSTATUS(rawStatus_);\n}\n\nint ProcessReturnCode::killSignal() const {\n  enforce(KILLED);\n  return WTERMSIG(rawStatus_);\n}\n\nbool ProcessReturnCode::coreDumped() const {\n  enforce(KILLED);\n  return WCOREDUMP(rawStatus_);\n}\n\nbool ProcessReturnCode::succeeded() const {\n  return exited() && exitStatus() == 0;\n}\n\nstd::string ProcessReturnCode::str() const {\n  switch (state()) {\n    case NOT_STARTED:\n      return \"not started\";\n    case RUNNING:\n      return \"running\";\n    case EXITED:\n      return to<std::string>(\"exited with status \", exitStatus());\n    case KILLED:\n      return to<std::string>(\n          \"killed by signal \",\n          killSignal(),\n          (coreDumped() ? \" (core dumped)\" : \"\"));\n  }\n  assume_unreachable();\n}\n\nCalledProcessError::CalledProcessError(ProcessReturnCode rc)\n    : SubprocessError(rc.str()), returnCode_(rc) {}\n\nstatic inline std::string toSubprocessSpawnErrorMessage(\n    char const* executable, int errCode, int errnoValue) {\n  auto prefix = errCode == kExecFailure\n      ? \"failed to execute \"\n      : \"error preparing to execute \";\n  return to<std::string>(prefix, executable, \": \", errnoStr(errnoValue));\n}\n\nSubprocessSpawnError::SubprocessSpawnError(\n    const char* executable, int errCode, int errnoValue)\n    : SubprocessError(\n          toSubprocessSpawnErrorMessage(executable, errCode, errnoValue)),\n      errnoValue_(errnoValue) {}\n\nnamespace {\n\n// Copy pointers to the given strings in a format suitable for posix_spawn\nstd::unique_ptr<const char*[]> cloneStrings(const std::vector<std::string>& s) {\n  std::unique_ptr<const char*[]> d(new const char*[s.size() + 1]);\n  for (size_t i = 0; i < s.size(); i++) {\n    d[i] = s[i].c_str();\n  }\n  d[s.size()] = nullptr;\n  return d;\n}\n\n// Check a wait() status, throw on non-successful\nvoid checkStatus(ProcessReturnCode returnCode) {\n  if (returnCode.state() != ProcessReturnCode::EXITED ||\n      returnCode.exitStatus() != 0) {\n    throw CalledProcessError(returnCode);\n  }\n}\n\n} // namespace\n\nSubprocess::Options& Subprocess::Options::fd(int fd, int action) {\n  if (fdActions_.contains(fd)) {\n    throw std::invalid_argument(\"fd already added\");\n  }\n  if (action == Subprocess::PIPE) {\n    if (fd == 0) {\n      action = Subprocess::PIPE_IN;\n    } else if (fd == 1 || fd == 2) {\n      action = Subprocess::PIPE_OUT;\n    } else {\n      throw std::invalid_argument(\n          to<std::string>(\"Only fds 0, 1, 2 are valid for action=PIPE: \", fd));\n    }\n  }\n  fdActions_[fd] = action;\n  return *this;\n}\n\n#if defined(__linux__)\n\nSubprocess::Options& Subprocess::Options::setLinuxCGroupFd(\n    int cgroupFd, std::shared_ptr<int> errout) {\n  if (linuxCGroupFd_.value >= 0 || !linuxCGroupPath_.value.empty()) {\n    throw std::runtime_error(\"setLinuxCGroup* called more than once\");\n  }\n  linuxCGroupFd_ = {cgroupFd, std::move(errout)};\n  return *this;\n}\n\nSubprocess::Options& Subprocess::Options::setLinuxCGroupPath(\n    const std::string& cgroupPath, std::shared_ptr<int> errout) {\n  if (linuxCGroupFd_.value >= 0 || !linuxCGroupPath_.value.empty()) {\n    throw std::runtime_error(\"setLinuxCGroup* called more than once\");\n  }\n  linuxCGroupPath_ = {cgroupPath, std::move(errout)};\n  return *this;\n}\n\n#endif\n\nSubprocess::Options& Subprocess::Options::addPrintPidToBuffer(span<char> buf) {\n  if (buf.size() < kPidBufferMinSize) {\n    throw std::invalid_argument(\"buf size too small\");\n  }\n  setPrintPidToBuffer_.insert(buf.data());\n  return *this;\n}\n\nSubprocess::Options& Subprocess::Options::addRLimit(\n    int resource, rlimit limit, std::shared_ptr<int> errout) {\n  if (rlimits_.count(resource)) {\n    throw std::runtime_error(\"addRLimit called with same limit more than once\");\n  }\n  rlimits_[resource] = AttrWithMeta<rlimit>{limit, std::move(errout)};\n  return *this;\n}\n\nSubprocess::Subprocess() = default;\n\nSubprocess::Subprocess(\n    const std::vector<std::string>& argv,\n    const Options& options,\n    const char* executable,\n    const std::vector<std::string>* env)\n    : destroyBehavior_(options.destroyBehavior_) {\n  if (argv.empty()) {\n    throw std::invalid_argument(\"argv must not be empty\");\n  }\n  if (!executable) {\n    executable = argv[0].c_str();\n  }\n  spawn(cloneStrings(argv), executable, options, env);\n}\n\nSubprocess::Subprocess(\n    const std::string& cmd,\n    const Options& options,\n    const std::vector<std::string>* env)\n    : destroyBehavior_(options.destroyBehavior_) {\n  if (options.usePath_) {\n    throw std::invalid_argument(\"usePath() not allowed when running in shell\");\n  }\n\n  std::vector<std::string> argv = {\"/bin/sh\", \"-c\", cmd};\n  spawn(cloneStrings(argv), argv[0].c_str(), options, env);\n}\n\nSubprocess Subprocess::fromExistingProcess(pid_t pid) {\n  Subprocess sp;\n  sp.pid_ = pid;\n  sp.destroyBehavior_ = DestroyBehaviorLeak;\n  sp.returnCode_ = ProcessReturnCode::makeRunning();\n  return sp;\n}\n\nSubprocess::~Subprocess() {\n  if (returnCode_.state() == ProcessReturnCode::RUNNING) {\n    if (destroyBehavior_ == DestroyBehaviorFatal) {\n      // Explicitly crash if we are destroyed without reaping the child process.\n      //\n      // If you are running into this crash, you are destroying a Subprocess\n      // without cleaning up the child process first, which can leave behind a\n      // zombie process on the system until the current process exits.  You may\n      // want to use one of the following options instead when creating the\n      // Subprocess:\n      // - Options::detach()\n      //   If you do not want to wait on the child process to complete, and do\n      //   not care about its exit status, use detach().\n      // - Options::killChildOnDestruction()\n      //   If you want the child process to be automatically killed when the\n      //   Subprocess is destroyed, use killChildOnDestruction() or\n      //   terminateChildOnDestruction()\n      XLOG(FATAL) << \"Subprocess destroyed without reaping child\";\n    } else if (destroyBehavior_ == DestroyBehaviorLeak) {\n      // Do nothing if we are destroyed without reaping the child process.\n      XLOG(DBG) << \"Subprocess destroyed without reaping child process\";\n    } else {\n      // If we are killed without reaping the child process, explicitly\n      // terminate/kill it and wait for it to exit.\n      try {\n        TimeoutDuration timeout(destroyBehavior_);\n        terminateOrKill(timeout);\n      } catch (const std::exception& ex) {\n        XLOG(WARN) << \"error terminating process in Subprocess destructor: \"\n                   << ex.what();\n      }\n    }\n  }\n}\n\nstruct Subprocess::ChildErrorInfo {\n  int errCode;\n  int errnoValue;\n};\n\n[[noreturn]]\nFOLLY_DETAIL_SUBPROCESS_RAW void Subprocess::childError(\n    SpawnRawArgs const& args, int errCode, int errnoValue) {\n  *args.err = {errCode, errnoValue};\n  detail::subprocess_libc::_exit(errCode);\n  __builtin_unreachable();\n}\n\nvoid Subprocess::setAllNonBlocking() {\n  for (auto& p : pipes_) {\n    int fd = p.pipe.fd();\n    int flags = ::fcntl(fd, F_GETFL);\n    checkUnixError(flags, \"fcntl\");\n    int r = ::fcntl(fd, F_SETFL, flags | O_NONBLOCK);\n    checkUnixError(r, \"fcntl\");\n  }\n}\n\nvoid Subprocess::spawn(\n    std::unique_ptr<const char*[]> argv,\n    const char* executable,\n    const Options& optionsIn,\n    const std::vector<std::string>* env) {\n  if (optionsIn.usePath_ && env) {\n    throw std::invalid_argument(\n        \"usePath() not allowed when overriding environment\");\n  }\n\n  // Make a copy, we'll mutate options\n  Options options(optionsIn);\n\n  // On error, close all pipes_ (ignoring errors, but that seems fine here).\n  auto pipesGuard = makeGuard([this] { pipes_.clear(); });\n\n  ChildErrorInfo err{};\n\n  // Perform the actual work of setting up pipes then forking and\n  // executing the child.\n  spawnInternal(std::move(argv), executable, options, env, &err);\n\n  // After spawnInternal() returns the child is alive.  We have to be very\n  // careful about throwing after this point.  We are inside the constructor,\n  // so if we throw the Subprocess object will have never existed, and the\n  // destructor will never be called.\n  //\n  // We should only throw if we got an error via the ChildErrorInfo, and we know\n  // the child has exited and can be immediately waited for.  In all other\n  // cases, we have no way of cleaning up the child.\n  readChildErrorNum(err, executable);\n\n  // If we spawned a detached child, wait on the intermediate child process.\n  // It always exits immediately.\n  if (options.detach_) {\n    wait();\n  }\n\n  // We have fully succeeded now, so release the guard on pipes_\n  pipesGuard.dismiss();\n}\n\nvoid Subprocess::spawnInternal(\n    std::unique_ptr<const char*[]> argv,\n    const char* executable,\n    Options& options,\n    const std::vector<std::string>* env,\n    ChildErrorInfo* err) {\n  // Parent work, pre-fork: create pipes\n  std::vector<int> childFds;\n  // Close all of the childFds as we leave this scope\n  SCOPE_EXIT {\n    // These are only pipes, closing them shouldn't fail\n    for (int cfd : childFds) {\n      CHECK_ERR(fileops::close(cfd));\n    }\n  };\n\n  int r;\n  for (auto& p : options.fdActions_) {\n    if (p.second == PIPE_IN || p.second == PIPE_OUT) {\n      int fds[2];\n      // We're setting both ends of the pipe as close-on-exec. The child\n      // doesn't need to reset the flag on its end, as we always dup2() the fd,\n      // and dup2() fds don't share the close-on-exec flag.\n#if FOLLY_HAVE_PIPE2\n      // If possible, set close-on-exec atomically. Otherwise, a concurrent\n      // Subprocess invocation can fork() between \"pipe\" and \"fnctl\",\n      // causing FDs to leak.\n      r = ::pipe2(fds, O_CLOEXEC);\n      checkUnixError(r, \"pipe2\");\n#else\n      r = fileops::pipe(fds);\n      checkUnixError(r, \"pipe\");\n      r = fcntl(fds[0], F_SETFD, FD_CLOEXEC);\n      checkUnixError(r, \"set FD_CLOEXEC\");\n      r = fcntl(fds[1], F_SETFD, FD_CLOEXEC);\n      checkUnixError(r, \"set FD_CLOEXEC\");\n#endif\n      pipes_.emplace_back();\n      Pipe& pipe = pipes_.back();\n      pipe.direction = p.second;\n      int cfd;\n      if (p.second == PIPE_IN) {\n        // Child gets reading end\n        pipe.pipe = folly::File(fds[1], /*ownsFd=*/true);\n        cfd = fds[0];\n      } else {\n        pipe.pipe = folly::File(fds[0], /*ownsFd=*/true);\n        cfd = fds[1];\n      }\n      p.second = cfd; // ensure it gets dup2()ed\n      pipe.childFd = p.first;\n      childFds.push_back(cfd);\n    }\n  }\n\n  // This should already be sorted, as options.fdActions_ is\n  DCHECK(std::is_sorted(pipes_.begin(), pipes_.end()));\n\n  // Note that the const casts below are legit, per\n  // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html\n\n  // Set up environment\n  std::unique_ptr<const char*[]> envHolder;\n  if (env) {\n    envHolder = cloneStrings(*env);\n  }\n\n  // Block all signals around vfork; see http://ewontfix.com/7/.\n  //\n  // As the child may run in the same address space as the parent until\n  // the actual execve() system call, any (custom) signal handlers that\n  // the parent has might alter parent's memory if invoked in the child,\n  // with undefined results.  So we block all signals in the parent before\n  // vfork(), which will cause them to be blocked in the child as well (we\n  // rely on the fact that Linux, just like all sane implementations, only\n  // clones the calling thread).  Then, in the child, we reset all signals\n  // to their default dispositions (while still blocked), and unblock them\n  // (so the exec()ed process inherits the parent's signal mask)\n  //\n  // The parent also unblocks all signals as soon as vfork() returns.\n  sigset_t allBlocked;\n  r = sigfillset(&allBlocked);\n  checkUnixError(r, \"sigfillset\");\n  sigset_t oldSignals;\n\n  r = pthread_sigmask(SIG_SETMASK, &allBlocked, &oldSignals);\n  checkPosixError(r, \"pthread_sigmask\");\n  SCOPE_EXIT {\n    // Restore signal mask\n    r = pthread_sigmask(SIG_SETMASK, &oldSignals, nullptr);\n    CHECK_EQ(r, 0) << \"pthread_sigmask: \" << errnoStr(r); // shouldn't fail\n  };\n\n  SpawnRawArgs::Scratch scratch{options};\n  SpawnRawArgs args{scratch, options};\n  args.argv = argv.get();\n  args.envv = env ? envHolder.get() : environ;\n  args.executable = executable;\n  args.err = err;\n  args.oldSignals = options.sigmask_.value_or(oldSignals);\n\n  // Child is alive.  We have to be very careful about throwing after this\n  // point.  We are inside the constructor, so if we throw the Subprocess\n  // object will have never existed, and the destructor will never be called.\n  //\n  // We should only throw if we got an error via the errFd, and we know the\n  // child has exited and can be immediately waited for.  In all other cases,\n  // we have no way of cleaning up the child.\n  pid_ = spawnInternalDoFork(args);\n  returnCode_ = ProcessReturnCode::makeRunning();\n}\n\n// With -Wclobbered, gcc complains about vfork potentially clobbering the\n// childDir variable, even though we only use it on the child side of the\n// vfork.\n\nFOLLY_PUSH_WARNING\nFOLLY_GCC_DISABLE_WARNING(\"-Wclobbered\")\nFOLLY_DETAIL_SUBPROCESS_RAW\npid_t Subprocess::spawnInternalDoFork(SpawnRawArgs const& args) {\n  pid_t pid = detail::subprocess_libc::vfork();\n  checkUnixError(pid, errno, \"failed to fork\");\n  if (pid != 0) {\n    return pid;\n  }\n\n  // From this point onward, we are in the child.\n\n  // Fork a second time if detach_ was requested.\n  // This must be done before signals are restored in prepareChild()\n  if (args.detach) {\n    pid = detail::subprocess_libc::vfork();\n    if (pid == -1) {\n      // Inform our parent process of the error so it can throw in the parent.\n      childError(args, kChildFailure, errno);\n    } else if (pid != 0) {\n      // We are the intermediate process.  Exit immediately.\n      // Our child will still inform the original parent of success/failure\n      // through errFd.  The pid of the grandchild process never gets\n      // propagated back up to the original parent.  In the future we could\n      // potentially send it back using errFd if we needed to.\n      detail::subprocess_libc::_exit(0);\n    }\n  }\n\n  int errnoValue = prepareChild(args);\n  if (errnoValue != 0) {\n    childError(args, kChildFailure, errnoValue);\n  }\n\n  errnoValue = runChild(args);\n  // If we get here, exec() failed.\n  childError(args, kExecFailure, errnoValue);\n}\nFOLLY_POP_WARNING\n\nFOLLY_DETAIL_SUBPROCESS_RAW\nint Subprocess::prepareChildDoOptionalError(int* errout) {\n  if (errout) {\n    *errout = errno;\n    return 0;\n  } else {\n    return errno;\n  }\n}\n\nFOLLY_DETAIL_SUBPROCESS_RAW\nint Subprocess::prepareChildDoLinuxCGroup(SpawnRawArgs const& args) {\n  auto cgroupPath = args.linuxCGroupPath;\n  auto cgroupFd = args.linuxCGroupFd;\n  if (nullptr != cgroupPath.value) {\n    int fd = detail::subprocess_libc::open(\n        cgroupPath.value, O_RDONLY | O_DIRECTORY | O_CLOEXEC);\n    if (-1 == fd) {\n      return prepareChildDoOptionalError(cgroupPath.errout);\n    }\n    cgroupFd = {fd, cgroupPath.errout};\n  }\n  if (-1 != cgroupFd.value) {\n    int fd = detail::subprocess_libc::openat(\n        cgroupFd.value, \"cgroup.procs\", O_WRONLY | O_CLOEXEC);\n    if (fd == -1) {\n      return prepareChildDoOptionalError(cgroupFd.errout);\n    }\n    int rc = 0;\n    do {\n      constexpr char const buf = '0';\n      rc = detail::subprocess_libc::write(fd, &buf, 1);\n    } while (rc == -1 && errno == EINTR);\n    if (rc == -1) {\n      return prepareChildDoOptionalError(cgroupFd.errout);\n    }\n  }\n  return 0;\n}\n\n// If requested, close all other file descriptors.  Don't close\n// any fds in options.fdActions_, and don't touch stdin, stdout, stderr.\n// Ignore errors.\n//\n//\n// This function is called in the child after fork but before exec so\n// there is very little it can do. It cannot allocate memory and\n// it cannot lock a mutex, just as if it were running in a signal\n// handler.\nFOLLY_DETAIL_SUBPROCESS_RAW\nvoid Subprocess::closeInheritedFds(const SpawnRawArgs& args) {\n#if defined(__linux__)\n  int dirfd = detail::subprocess_libc::open(\"/proc/self/fd\", O_RDONLY);\n  if (dirfd != -1) {\n    char buffer[32768];\n    int res;\n    while ((res = syscall(SYS_getdents64, dirfd, buffer, sizeof(buffer))) > 0) {\n      // linux_dirent64 is part of the kernel ABI for the getdents64 system\n      // call. It is currently the same as struct dirent64 in both glibc and\n      // musl, but those are library specific and could change. linux_dirent64\n      // is not defined in the standard set of Linux userspace headers\n      // (/usr/include/linux)\n      //\n      // We do not use the POSIX interfaces (opendir, readdir, etc..) for\n      // reading a directory since they may allocate memory / grab a lock, which\n      // is unsafe in this context.\n      FOLLY_PUSH_WARNING\n      FOLLY_CLANG_DISABLE_WARNING(\"-Wzero-length-array\")\n      struct linux_dirent64 {\n        uint64_t d_ino;\n        int64_t d_off;\n        uint16_t d_reclen;\n        unsigned char d_type;\n        char d_name[0];\n      } const* entry;\n      FOLLY_POP_WARNING\n      for (int offset = 0; offset < res; offset += entry->d_reclen) {\n        entry = reinterpret_cast<struct linux_dirent64*>(buffer + offset);\n        if (entry->d_type != DT_LNK) {\n          continue;\n        }\n        char* end_p = nullptr;\n        errno = 0;\n        int fd = static_cast<int>(\n            detail::subprocess_libc::strtol(entry->d_name, &end_p, 10));\n        if (errno == ERANGE || fd < 3 || end_p == entry->d_name) {\n          continue;\n        }\n        if ((fd != dirfd) && (args.fdActions.find(fd) == nullptr)) {\n          detail::subprocess_libc::close(fd);\n        }\n      }\n    }\n    detail::subprocess_libc::close(dirfd);\n    return;\n  }\n#endif\n  // If not running on Linux or if we failed to open /proc/self/fd, try to close\n  // all possible open file descriptors.\n  for (auto fd = sysconf(_SC_OPEN_MAX) - 1; fd >= 3; --fd) {\n    if (args.fdActions.find(fd) == nullptr) {\n      detail::subprocess_libc::close(fd);\n    }\n  }\n}\n\nFOLLY_DETAIL_SUBPROCESS_RAW\nint Subprocess::prepareChild(SpawnRawArgs const& args) {\n  // While all signals are blocked, we must reset their\n  // dispositions to default.\n  for (int sig = 1; sig < NSIG; ++sig) {\n    detail::subprocess_libc::signal(sig, SIG_DFL);\n  }\n\n  {\n    // Unblock signals; restore signal mask.\n    int r = detail::subprocess_libc::pthread_sigmask(\n        SIG_SETMASK, &args.oldSignals, nullptr);\n    if (r != 0) {\n      return r; // pthread_sigmask() returns an errno value\n    }\n  }\n\n  // Move the child process into a linux cgroup, if one is given\n  if (auto rc = prepareChildDoLinuxCGroup(args)) {\n    return rc;\n  }\n\n  for (size_t i = 0; i < args.rlimitsSize; ++i) {\n    auto const& limit = args.rlimitsData[i];\n    if (setrlimit(limit.first, &limit.second.value) == -1) {\n      if (limit.second.errout) {\n        *limit.second.errout = errno;\n      } else {\n        return errno;\n      }\n    }\n  }\n\n  // Change the working directory, if one is given\n  if (args.childDir) {\n    if (::chdir(args.childDir) == -1) {\n      return errno;\n    }\n  }\n\n#ifdef __linux__\n  // Best effort\n  if (args.cpuSet) {\n    const auto& cpuSet = *args.cpuSet;\n    if (::sched_setaffinity(0, sizeof(cpuSet.value), &cpuSet.value) == -1) {\n      if (cpuSet.errout) {\n        *cpuSet.errout = errno;\n      } else {\n        return errno;\n      }\n    }\n  }\n#endif\n\n  // Change effective/real group/user, if requested\n  // Call the raw syscall directly for linux, as glibc set*id() is not\n  // safe after vfork() - Linux is the only kernel where libc set*id is not just\n  // a call to syscall set*id\n  // The risk is that some vfork child is terminated within set*id() with\n  // a lock held, deadlocking the next vfork child\n  auto idval = [](auto id) { return id ? &id->value : nullptr; };\n#if defined(__linux__)\n  constexpr auto k_sys_setresgid = SYS_setresgid;\n  constexpr auto k_sys_setresuid = SYS_setresuid;\n#else\n  // Unused\n  constexpr auto k_sys_setresgid = -1;\n  constexpr auto k_sys_setresuid = -1;\n#endif\n  if (auto p = idval(args.egid); kIsLinux\n          ? p && 0 != linux_syscall(k_sys_setresgid, -1, *p, -1)\n          : p && 0 != ::setegid(*p)) {\n    if (auto out = args.egid->errout) {\n      *out = errno;\n    } else {\n      return errno;\n    }\n  }\n  if (auto p = idval(args.gid); kIsLinux\n          ? p && 0 != linux_syscall(SYS_setgid, *p)\n          : p && 0 != ::setgid(*p)) {\n    if (auto out = args.gid->errout) {\n      *out = errno;\n    } else {\n      return errno;\n    }\n  }\n  if (auto p = idval(args.euid); kIsLinux\n          ? p && 0 != linux_syscall(k_sys_setresuid, -1, *p, -1)\n          : p && 0 != ::seteuid(*p)) {\n    if (auto out = args.euid->errout) {\n      *out = errno;\n    } else {\n      return errno;\n    }\n  }\n  if (auto p = idval(args.uid); kIsLinux\n          ? p && 0 != linux_syscall(SYS_setuid, *p)\n          : p && 0 != ::setuid(*p)) {\n    if (auto out = args.uid->errout) {\n      *out = errno;\n    } else {\n      return errno;\n    }\n  }\n\n  // We don't have to explicitly close the parent's end of all pipes,\n  // as they all have the FD_CLOEXEC flag set and will be closed at\n  // exec time.\n\n  // Redirect requested FDs to /dev/null or NUL\n  // dup2 any explicitly specified FDs\n  for (auto p : args.fdActions) {\n    if (p.second == DEV_NULL) {\n      // folly/portability/Fcntl provides an impl of open that will\n      // map this to NUL on Windows.\n      auto devNull =\n          detail::subprocess_libc::open(\"/dev/null\", O_RDWR | O_CLOEXEC);\n      if (devNull == -1) {\n        return errno;\n      }\n      // note: dup2 will not set CLOEXEC on the destination\n      if (detail::subprocess_libc::dup2(devNull, p.first) == -1) {\n        // explicit close on error to avoid leaking fds\n        detail::subprocess_libc::close(devNull);\n        return errno;\n      }\n      detail::subprocess_libc::close(devNull);\n    } else if (p.second != p.first && p.second != NO_CLOEXEC) {\n      if (detail::subprocess_libc::dup2(p.second, p.first) == -1) {\n        return errno;\n      }\n    } else if (p.second == p.first || p.second == NO_CLOEXEC) {\n      int flags = detail::subprocess_libc::fcntl(p.first, F_GETFD);\n      if (flags == -1) {\n        return errno;\n      }\n      if (int newflags = flags & ~FD_CLOEXEC; newflags != flags) {\n        if (detail::subprocess_libc::fcntl(p.first, F_SETFD, newflags) == -1) {\n          return errno;\n        }\n      }\n    }\n  }\n\n  if (args.closeOtherFds) {\n    closeInheritedFds(args);\n  }\n\n#if defined(__linux__)\n  // Opt to receive signal on parent death, if requested\n  if (args.parentDeathSignal != 0) {\n    const auto parentDeathSignal =\n        static_cast<unsigned long>(args.parentDeathSignal);\n    if (detail::subprocess_libc::prctl(\n            PR_SET_PDEATHSIG, parentDeathSignal, 0, 0, 0) == -1) {\n      return errno;\n    }\n  }\n#endif\n\n  if (args.processGroupLeader) {\n#if !defined(__FreeBSD__)\n    if (setpgrp() == -1) {\n#else\n    if (setpgrp(getpid(), getpgrp()) == -1) {\n#endif\n      return errno;\n    }\n  }\n\n  for (size_t i = 0; i < args.setPrintPidToBufferSize; ++i) {\n    auto buf = args.setPrintPidToBufferData[i];\n    detail::subprocess_libc::sprintf(buf, \"%d\", getpid());\n  }\n\n  return 0;\n}\n\nFOLLY_DETAIL_SUBPROCESS_RAW\nint Subprocess::runChild(SpawnRawArgs const& args) {\n  auto argv = const_cast<char* const*>(args.argv);\n  auto envv = const_cast<char* const*>(args.envv);\n  // Now, finally, exec.\n  if (args.usePath) {\n    ::execvp(args.executable, argv);\n  } else {\n    ::execve(args.executable, argv, envv);\n  }\n  return errno;\n}\n\nvoid Subprocess::readChildErrorNum(ChildErrorInfo err, const char* executable) {\n  if (err.errCode == 0) {\n    return;\n  }\n\n  // We got error data from the child.  The child should exit immediately in\n  // this case, so wait on it to clean up.\n  wait();\n\n  // Throw to signal the error\n  throw SubprocessSpawnError(executable, err.errCode, err.errnoValue);\n}\n\nProcessReturnCode Subprocess::poll(struct rusage* ru) {\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  DCHECK_GT(pid_, 0);\n  int status;\n  pid_t found = ::wait4(pid_, &status, WNOHANG, ru);\n  // The spec guarantees that EINTR does not occur with WNOHANG, so the only\n  // two remaining errors are ECHILD (other code reaped the child?), or\n  // EINVAL (cosmic rays?), both of which merit an abort:\n  PCHECK(found != -1) << \"waitpid(\" << pid_ << \", &status, WNOHANG)\";\n  if (found != 0) {\n    // Though the child process had quit, this call does not close the pipes\n    // since its descendants may still be using them.\n    returnCode_ = ProcessReturnCode::make(status);\n    pid_ = -1;\n  }\n  return returnCode_;\n}\n\nbool Subprocess::pollChecked() {\n  if (poll().state() == ProcessReturnCode::RUNNING) {\n    return false;\n  }\n  checkStatus(returnCode_);\n  return true;\n}\n\nProcessReturnCode Subprocess::wait() {\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  DCHECK_GT(pid_, 0);\n  int status;\n  pid_t found;\n  do {\n    found = ::waitpid(pid_, &status, 0);\n  } while (found == -1 && errno == EINTR);\n  // The only two remaining errors are ECHILD (other code reaped the\n  // child?), or EINVAL (cosmic rays?), and both merit an abort:\n  PCHECK(found != -1) << \"waitpid(\" << pid_ << \", &status, 0)\";\n  // Though the child process had quit, this call does not close the pipes\n  // since its descendants may still be using them.\n  DCHECK_EQ(found, pid_);\n  returnCode_ = ProcessReturnCode::make(status);\n  pid_ = -1;\n  return returnCode_;\n}\n\nProcessReturnCode Subprocess::waitAndGetRusage(struct rusage* ru) {\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  DCHECK_GT(pid_, 0);\n  int status;\n  pid_t found;\n  do {\n    found = ::wait4(pid_, &status, 0, ru);\n  } while (found == -1 && errno == EINTR);\n  // The only two remaining errors are ECHILD (other code reaped the\n  // child?), or EINVAL (cosmic rays?), and both merit an abort:\n  PCHECK(found != -1) << \"wait4(\" << pid_ << \", &status, 0, resourceUsage)\";\n  // Though the child process had quit, this call does not close the pipes\n  // since its descendants may still be using them.\n  DCHECK_EQ(found, pid_);\n  returnCode_ = ProcessReturnCode::make(status);\n  pid_ = -1;\n  return returnCode_;\n}\n\nvoid Subprocess::waitChecked() {\n  wait();\n  checkStatus(returnCode_);\n}\n\nProcessReturnCode Subprocess::waitTimeout(TimeoutDuration timeout) {\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  DCHECK_GT(pid_, 0) << \"The subprocess has been waited already\";\n\n  auto pollUntil = std::chrono::steady_clock::now() + timeout;\n  auto sleepDuration = std::chrono::milliseconds{2};\n  constexpr auto maximumSleepDuration = std::chrono::milliseconds{100};\n\n  for (;;) {\n    // Always call waitpid once after the full timeout has elapsed.\n    auto now = std::chrono::steady_clock::now();\n\n    int status;\n    pid_t found;\n    do {\n      found = ::waitpid(pid_, &status, WNOHANG);\n    } while (found == -1 && errno == EINTR);\n    PCHECK(found != -1) << \"waitpid(\" << pid_ << \", &status, WNOHANG)\";\n    if (found) {\n      // Just on the safe side, make sure it's the actual pid we are waiting.\n      DCHECK_EQ(found, pid_);\n      returnCode_ = ProcessReturnCode::make(status);\n      // Change pid_ to -1 to detect programming error like calling\n      // this method multiple times.\n      pid_ = -1;\n      return returnCode_;\n    }\n    if (now > pollUntil) {\n      // Timed out: still running().\n      return returnCode_;\n    }\n    // The subprocess is still running, sleep for increasing periods of time.\n    std::this_thread::sleep_for(sleepDuration);\n    sleepDuration =\n        std::min(maximumSleepDuration, sleepDuration + sleepDuration);\n  }\n}\n\nvoid Subprocess::sendSignal(int signal) {\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  int r = ::kill(pid_, signal);\n  checkUnixError(r, \"kill\");\n}\n\nProcessReturnCode Subprocess::waitOrTerminateOrKill(\n    TimeoutDuration waitTimeout, TimeoutDuration sigtermTimeout) {\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  DCHECK_GT(pid_, 0) << \"The subprocess has been waited already\";\n\n  this->waitTimeout(waitTimeout);\n\n  if (returnCode_.running()) {\n    return terminateOrKill(sigtermTimeout);\n  }\n  return returnCode_;\n}\n\nProcessReturnCode Subprocess::terminateOrKill(TimeoutDuration sigtermTimeout) {\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  DCHECK_GT(pid_, 0) << \"The subprocess has been waited already\";\n\n  if (sigtermTimeout > TimeoutDuration(0)) {\n    // 1. Send SIGTERM to kill the process\n    terminate();\n    // 2. check whether subprocess has terminated using non-blocking waitpid\n    waitTimeout(sigtermTimeout);\n    if (!returnCode_.running()) {\n      return returnCode_;\n    }\n  }\n\n  // 3. If we are at this point, we have waited enough time after\n  // sending SIGTERM, we have to use nuclear option SIGKILL to kill\n  // the subprocess.\n  XLOGF(INFO, \"Send SIGKILL to {}\", pid_);\n  kill();\n  // 4. SIGKILL should kill the process otherwise there must be\n  // something seriously wrong, just use blocking wait to wait for the\n  // subprocess to finish.\n  return wait();\n}\n\npid_t Subprocess::pid() const {\n  return pid_;\n}\n\nnamespace {\n\nByteRange queueFront(const IOBufQueue& queue) {\n  auto* p = queue.front();\n  if (!p) {\n    return ByteRange{};\n  }\n  return io::Cursor(p).peekBytes();\n}\n\n// fd write\nbool handleWrite(int fd, IOBufQueue& queue) {\n  for (;;) {\n    auto b = queueFront(queue);\n    if (b.empty()) {\n      return true; // EOF\n    }\n\n    ssize_t n = writeNoInt(fd, b.data(), b.size());\n    if (n == -1 && errno == EAGAIN) {\n      return false;\n    }\n    checkUnixError(n, \"write\");\n    queue.trimStart(n);\n  }\n}\n\n// fd read\nbool handleRead(int fd, IOBufQueue& queue) {\n  for (;;) {\n    auto p = queue.preallocate(100, 65000);\n    ssize_t n = readNoInt(fd, p.first, p.second);\n    if (n == -1 && errno == EAGAIN) {\n      return false;\n    }\n    checkUnixError(n, \"read\");\n    if (n == 0) {\n      return true;\n    }\n    queue.postallocate(n);\n  }\n}\n\nbool discardRead(int fd) {\n  static const size_t bufSize = 65000;\n  // Thread unsafe, but it doesn't matter.\n  static std::unique_ptr<char[]> buf(new char[bufSize]);\n\n  for (;;) {\n    ssize_t n = readNoInt(fd, buf.get(), bufSize);\n    if (n == -1 && errno == EAGAIN) {\n      return false;\n    }\n    checkUnixError(n, \"read\");\n    if (n == 0) {\n      return true;\n    }\n  }\n}\n\n} // namespace\n\nstd::pair<std::string, std::string> Subprocess::communicate(StringPiece input) {\n  IOBufQueue inputQueue;\n  inputQueue.wrapBuffer(input.data(), input.size());\n\n  auto outQueues = communicateIOBuf(std::move(inputQueue));\n  auto outBufs =\n      std::make_pair(outQueues.first.move(), outQueues.second.move());\n  std::pair<std::string, std::string> out;\n  if (outBufs.first) {\n    outBufs.first->coalesce();\n    out.first.assign(\n        reinterpret_cast<const char*>(outBufs.first->data()),\n        outBufs.first->length());\n  }\n  if (outBufs.second) {\n    outBufs.second->coalesce();\n    out.second.assign(\n        reinterpret_cast<const char*>(outBufs.second->data()),\n        outBufs.second->length());\n  }\n  return out;\n}\n\nstd::pair<IOBufQueue, IOBufQueue> Subprocess::communicateIOBuf(\n    IOBufQueue input) {\n  // If the user supplied a non-empty input buffer, make sure\n  // that stdin is a pipe so we can write the data.\n  if (!input.empty()) {\n    // findByChildFd() will throw std::invalid_argument if no pipe for\n    // STDIN_FILENO exists\n    findByChildFd(STDIN_FILENO);\n  }\n\n  std::pair<IOBufQueue, IOBufQueue> out;\n\n  auto readCallback = [&](int pfd, int cfd) -> bool {\n    if (cfd == STDOUT_FILENO) {\n      return handleRead(pfd, out.first);\n    } else if (cfd == STDERR_FILENO) {\n      return handleRead(pfd, out.second);\n    } else {\n      // Don't close the file descriptor, the child might not like SIGPIPE,\n      // just read and throw the data away.\n      return discardRead(pfd);\n    }\n  };\n\n  auto writeCallback = [&](int pfd, int cfd) -> bool {\n    if (cfd == STDIN_FILENO) {\n      return handleWrite(pfd, input);\n    } else {\n      // If we don't want to write to this fd, just close it.\n      return true;\n    }\n  };\n\n  communicate(std::move(readCallback), std::move(writeCallback));\n\n  return out;\n}\n\nvoid Subprocess::communicate(\n    FdCallback readCallback, FdCallback writeCallback) {\n  // This serves to prevent wait() followed by communicate(), but if you\n  // legitimately need that, send a patch to delete this line.\n  returnCode_.enforce(ProcessReturnCode::RUNNING);\n  setAllNonBlocking();\n\n  std::vector<pollfd> fds;\n  fds.reserve(pipes_.size());\n  std::vector<size_t> toClose; // indexes into pipes_\n  toClose.reserve(pipes_.size());\n\n  while (!pipes_.empty()) {\n    fds.clear();\n    toClose.clear();\n\n    for (auto& p : pipes_) {\n      pollfd pfd;\n      pfd.fd = p.pipe.fd();\n      // Yes, backwards, PIPE_IN / PIPE_OUT are defined from the\n      // child's point of view.\n      if (!p.enabled) {\n        // Still keeping fd in watched set so we get notified of POLLHUP /\n        // POLLERR\n        pfd.events = 0;\n      } else if (p.direction == PIPE_IN) {\n        pfd.events = POLLOUT;\n      } else {\n        pfd.events = POLLIN;\n      }\n      fds.push_back(pfd);\n    }\n\n    int r;\n    do {\n      r = ::poll(fds.data(), fds.size(), -1);\n    } while (r == -1 && errno == EINTR);\n    checkUnixError(r, \"poll\");\n\n    for (size_t i = 0; i < pipes_.size(); ++i) {\n      auto& p = pipes_[i];\n      auto parentFd = p.pipe.fd();\n      DCHECK_EQ(fds[i].fd, parentFd);\n      short events = fds[i].revents;\n\n      bool closed = false;\n      if (events & POLLOUT) {\n        DCHECK(!(events & POLLIN));\n        if (writeCallback(parentFd, p.childFd)) {\n          toClose.push_back(i);\n          closed = true;\n        }\n      }\n\n      // Call read callback on POLLHUP, to give it a chance to read (and act\n      // on) end of file\n      if (events & (POLLIN | POLLHUP)) {\n        DCHECK(!(events & POLLOUT));\n        if (readCallback(parentFd, p.childFd)) {\n          toClose.push_back(i);\n          closed = true;\n        }\n      }\n\n      if ((events & (POLLHUP | POLLERR)) && !closed) {\n        toClose.push_back(i);\n      }\n    }\n\n    // Close the fds in reverse order so the indexes hold after erase()\n    for (int idx : boost::adaptors::reverse(toClose)) {\n      auto pos = pipes_.begin() + idx;\n      pos->pipe.close(); // Throws on error\n      pipes_.erase(pos);\n    }\n  }\n}\n\nvoid Subprocess::enableNotifications(int childFd, bool enabled) {\n  pipes_[findByChildFd(childFd)].enabled = enabled;\n}\n\nbool Subprocess::notificationsEnabled(int childFd) const {\n  return pipes_[findByChildFd(childFd)].enabled;\n}\n\nsize_t Subprocess::findByChildFd(int childFd) const {\n  auto pos = std::lower_bound(\n      pipes_.begin(), pipes_.end(), childFd, [](const Pipe& pipe, int fd) {\n        return pipe.childFd < fd;\n      });\n  if (pos == pipes_.end() || pos->childFd != childFd) {\n    throw std::invalid_argument(\n        folly::to<std::string>(\"child fd not found \", childFd));\n  }\n  return pos - pipes_.begin();\n}\n\nvoid Subprocess::closeParentFd(int childFd) {\n  int idx = findByChildFd(childFd);\n  pipes_[idx].pipe.close(); // May throw\n  pipes_.erase(pipes_.begin() + idx);\n}\n\nstd::vector<Subprocess::ChildPipe> Subprocess::takeOwnershipOfPipes() {\n  std::vector<Subprocess::ChildPipe> pipes;\n  for (auto& p : pipes_) {\n    pipes.emplace_back(p.childFd, std::move(p.pipe));\n  }\n  // release memory\n  std::vector<Pipe>().swap(pipes_);\n  return pipes;\n}\n\nnamespace {\n\nclass Initializer {\n public:\n  Initializer() {\n    // We like EPIPE, thanks.\n    ::signal(SIGPIPE, SIG_IGN);\n  }\n};\n\nInitializer initializer;\n\n} // namespace\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Subprocess.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Subprocess library, modeled after Python's subprocess module\n * (http://docs.python.org/2/library/subprocess.html)\n *\n * This library defines one class (Subprocess) which represents a child\n * process.  Subprocess has two constructors: one that takes a vector<string>\n * and executes the given executable without using the shell, and one\n * that takes a string and executes the given command using the shell.\n * Subprocess allows you to redirect the child's standard input, standard\n * output, and standard error to/from child descriptors in the parent,\n * or to create communication pipes between the child and the parent.\n *\n * The simplest example is a thread-safe [1] version of the system() library\n * function:\n *    Subprocess(cmd).wait();\n * which executes the command using the default shell and waits for it\n * to complete, returning the exit status.\n *\n * A thread-safe [1] version of popen() (type=\"r\", to read from the child):\n *    Subprocess proc(cmd, Subprocess::Options().pipeStdout());\n *    // read from proc.stdoutFd()\n *    proc.wait();\n *\n * A thread-safe [1] version of popen() (type=\"w\", to write to the child):\n *    Subprocess proc(cmd, Subprocess::Options().pipeStdin());\n *    // write to proc.stdinFd()\n *    proc.wait();\n *\n * If you want to redirect both stdin and stdout to pipes, you can, but note\n * that you're subject to a variety of deadlocks.  You'll want to use\n * nonblocking I/O, like the callback version of communicate().\n *\n * The string or IOBuf-based variants of communicate() are the simplest way\n * to communicate with a child via its standard input, standard output, and\n * standard error.  They buffer everything in memory, so they are not great\n * for large amounts of data (or long-running processes), but they are much\n * simpler than the callback version.\n *\n * == A note on thread-safety ==\n *\n * [1] \"thread-safe\" refers ONLY to the fact that Subprocess is very careful\n * to fork in a way that does not cause grief in multithreaded programs.\n *\n * Caveat: If your system does not have the atomic pipe2 system call, it is\n * not safe to concurrently call Subprocess from different threads.\n * Therefore, it is best to have a single thread be responsible for spawning\n * subprocesses.\n *\n * A particular instances of Subprocess is emphatically **not** thread-safe.\n * If you need to simultaneously communicate via the pipes, and interact\n * with the Subprocess state, your best bet is to:\n *  - takeOwnershipOfPipes() to separate the pipe I/O from the subprocess.\n *  - Only interact with the Subprocess from one thread at a time.\n *\n * The current implementation of communicate() cannot be safely interrupted.\n * To do so correctly, one would need to use EventFD, or open a dedicated\n * pipe to be messaged from a different thread -- in particular, kill() will\n * not do, since a descendant may keep the pipes open indefinitely.\n *\n * So, once you call communicate(), you must wait for it to return, and not\n * touch the pipes from other threads.  closeParentFd() is emphatically\n * unsafe to call concurrently, and even sendSignal() is not a good idea.\n * You can perhaps give the Subprocess's PID to a different thread before\n * starting communicate(), and use that PID to send a signal without\n * accessing the Subprocess object.  In that case, you will need a mutex\n * that ensures you don't wait() before you sent said signal.  In a\n * nutshell, don't do this.\n *\n * In fact, signals are inherently concurrency-unsafe on Unix: if you signal\n * a PID, while another thread is in waitpid(), the signal may fire either\n * before or after the process is reaped.  This means that your signal can,\n * in pathological circumstances, be delivered to the wrong process (ouch!).\n * To avoid this, you should only use non-blocking waits (i.e. poll()), and\n * make sure to serialize your signals (i.e. kill()) with the waits --\n * either wait & signal from the same thread, or use a mutex.\n */\n\n#pragma once\n\n#ifdef _WIN32\n#error Subprocess is not supported on Windows.\n#endif\n\n#include <signal.h>\n#include <sys/resource.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n\n#include <chrono>\n#include <exception>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include <boost/container/flat_map.hpp>\n#include <boost/operators.hpp>\n\n#include <folly/Exception.h>\n#include <folly/File.h>\n#include <folly/FileUtil.h>\n#include <folly/Function.h>\n#include <folly/MapUtil.h>\n#include <folly/Optional.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/container/span.h>\n#include <folly/gen/String.h>\n#include <folly/io/IOBufQueue.h>\n#include <folly/portability/SysResource.h>\n\nnamespace folly {\n\nnamespace detail {\n\n/// SubprocessFdActionsList\n///\n/// A sorted vector-map with a binary search. Declared in the header so that the\n/// binary search can be unit-tested.\n///\n/// Not using a library container type since this binary search is done in the\n/// child process after a vfork(), including in sanitized builds. Relevant\n/// member functions are explicitly marked non-sanitized (under clang).\nclass SubprocessFdActionsList {\n private:\n  using value_type = std::pair<int, int>;\n\n  value_type const* begin_;\n  value_type const* end_;\n\n public:\n  explicit SubprocessFdActionsList(span<value_type const> rep) noexcept;\n\n  value_type const* begin() const noexcept;\n  value_type const* end() const noexcept;\n\n  int const* find(int fd) const noexcept;\n};\n\n} // namespace detail\n\n/**\n * Class to wrap a process return code.\n */\nclass Subprocess;\nclass ProcessReturnCode {\n public:\n  enum State {\n    // Subprocess starts in the constructor, so this state designates only\n    // default-initialized or moved-out ProcessReturnCodes.\n    NOT_STARTED,\n    RUNNING,\n    EXITED,\n    KILLED,\n  };\n\n  static ProcessReturnCode makeNotStarted() {\n    return ProcessReturnCode(RV_NOT_STARTED);\n  }\n\n  static ProcessReturnCode makeRunning() {\n    return ProcessReturnCode(RV_RUNNING);\n  }\n\n  static ProcessReturnCode make(int status);\n\n  // Default-initialized for convenience. Subprocess::returnCode() will\n  // never produce this value.\n  ProcessReturnCode() : rawStatus_(RV_NOT_STARTED) {}\n\n  // Trivially copyable\n  ProcessReturnCode(const ProcessReturnCode& p) = default;\n  ProcessReturnCode& operator=(const ProcessReturnCode& p) = default;\n  // Non-default move: In order for Subprocess to be movable, the \"moved\n  // out\" state must not be \"running\", or ~Subprocess() will abort.\n  ProcessReturnCode(ProcessReturnCode&& p) noexcept;\n  ProcessReturnCode& operator=(ProcessReturnCode&& p) noexcept;\n\n  /**\n   * Process state.  One of:\n   * NOT_STARTED: process hasn't been started successfully\n   * RUNNING: process is currently running\n   * EXITED: process exited (successfully or not)\n   * KILLED: process was killed by a signal.\n   */\n  State state() const;\n\n  /**\n   * Helper wrappers around state().\n   */\n  bool notStarted() const { return state() == NOT_STARTED; }\n  bool running() const { return state() == RUNNING; }\n  bool exited() const { return state() == EXITED; }\n  bool killed() const { return state() == KILLED; }\n\n  /**\n   * Exit status.  Only valid if state() == EXITED; throws otherwise.\n   */\n  int exitStatus() const;\n\n  /**\n   * Signal that caused the process's termination.  Only valid if\n   * state() == KILLED; throws otherwise.\n   */\n  int killSignal() const;\n\n  /**\n   * Was a core file generated?  Only valid if state() == KILLED; throws\n   * otherwise.\n   */\n  bool coreDumped() const;\n\n  /**\n   * Process exited normally with a zero exit status\n   */\n  bool succeeded() const;\n\n  /**\n   * String representation; one of\n   * \"not started\"\n   * \"running\"\n   * \"exited with status <status>\"\n   * \"killed by signal <signal>\"\n   * \"killed by signal <signal> (core dumped)\"\n   */\n  std::string str() const;\n\n  /**\n   * Helper function to enforce a precondition based on this.\n   * Throws std::logic_error if in an unexpected state.\n   */\n  void enforce(State expected) const;\n\n private:\n  explicit ProcessReturnCode(int rv) : rawStatus_(rv) {}\n  static constexpr int RV_NOT_STARTED = -2;\n  static constexpr int RV_RUNNING = -1;\n\n  int rawStatus_;\n};\n\n/**\n * Base exception thrown by the Subprocess methods.\n */\nclass FOLLY_EXPORT SubprocessError : public std::runtime_error {\n public:\n  using std::runtime_error::runtime_error;\n};\n\n/**\n * Exception thrown by *Checked methods of Subprocess.\n */\nclass FOLLY_EXPORT CalledProcessError : public SubprocessError {\n public:\n  explicit CalledProcessError(ProcessReturnCode rc);\n  ~CalledProcessError() noexcept override = default;\n  ProcessReturnCode returnCode() const { return returnCode_; }\n\n private:\n  ProcessReturnCode returnCode_;\n};\n\n/**\n * Exception thrown if the subprocess cannot be started.\n */\nclass FOLLY_EXPORT SubprocessSpawnError : public SubprocessError {\n public:\n  SubprocessSpawnError(const char* executable, int errCode, int errnoValue);\n  ~SubprocessSpawnError() noexcept override = default;\n  int errnoValue() const { return errnoValue_; }\n\n private:\n  int errnoValue_;\n};\n\n/**\n * Subprocess.\n */\nclass Subprocess {\n public:\n  using TimeoutDuration = std::chrono::milliseconds;\n\n  // removed CLOSE = -1\n  static const int PIPE = -2;\n  static const int PIPE_IN = -3;\n  static const int PIPE_OUT = -4;\n  static const int DEV_NULL = -5;\n  static const int NO_CLOEXEC = -6;\n\n  /**\n   * Class representing various options: file descriptor behavior, and\n   * whether to use $PATH for searching for the executable,\n   *\n   * By default, we don't use $PATH, file descriptors are closed if\n   * the close-on-exec flag is set (fcntl FD_CLOEXEC) and inherited\n   * otherwise.\n   */\n  class Options {\n    friend class Subprocess;\n\n   public:\n    // digits10 is the maximum number of decimal digits such that any number\n    // up to this many decimal digits can always be represented in the given\n    // integer type\n    // but we need to have storage for the decimal representation of any\n    // integer, so +1, and we need to have storage for the terminal null, so\n    // again +1.\n    static inline constexpr size_t kPidBufferMinSize =\n        std::numeric_limits<pid_t>::digits10 + 2;\n\n    Options() {} // E.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58328\n\n    /**\n     * Change action for file descriptor fd.\n     *\n     * \"action\" may be another file descriptor number (dup2()ed before the\n     * child execs), or one of CLOSE, PIPE_IN, and PIPE_OUT.\n     *\n     * CLOSE: close the file descriptor in the child\n     * PIPE_IN: open a pipe *from* the child\n     * PIPE_OUT: open a pipe *to* the child\n     *\n     * PIPE is a shortcut; same as PIPE_IN for stdin (fd 0), same as\n     * PIPE_OUT for stdout (fd 1) or stderr (fd 2), and an error for\n     * other file descriptors.\n     */\n    Options& fd(int fd, int action);\n\n    /**\n     * Shortcut to change the action for standard input.\n     */\n    Options& stdinFd(int action) { return fd(STDIN_FILENO, action); }\n\n    /**\n     * Shortcut to change the action for standard output.\n     */\n    Options& stdoutFd(int action) { return fd(STDOUT_FILENO, action); }\n\n    /**\n     * Shortcut to change the action for standard error.\n     * Note that stderr(1) will redirect the standard error to the same\n     * file descriptor as standard output; the equivalent of bash's \"2>&1\"\n     */\n    Options& stderrFd(int action) { return fd(STDERR_FILENO, action); }\n\n    Options& pipeStdin() { return fd(STDIN_FILENO, PIPE_IN); }\n    Options& pipeStdout() { return fd(STDOUT_FILENO, PIPE_OUT); }\n    Options& pipeStderr() { return fd(STDERR_FILENO, PIPE_OUT); }\n\n    /**\n     * Close all other fds (other than standard input, output, error,\n     * and file descriptors explicitly specified with fd()).\n     *\n     * This is potentially slow; it's generally a better idea to\n     * set the close-on-exec flag on all file descriptors that shouldn't\n     * be inherited by the child.\n     *\n     * Even with this option set, standard input, output, and error are\n     * not closed; use stdin(CLOSE), stdout(CLOSE), stderr(CLOSE) if you\n     * desire this.\n     */\n    Options& closeOtherFds() {\n      closeOtherFds_ = true;\n      return *this;\n    }\n\n    /**\n     * Use the search path ($PATH) when searching for the executable.\n     */\n    Options& usePath() {\n      usePath_ = true;\n      return *this;\n    }\n\n    /**\n     * Change the child's working directory, after the vfork.\n     */\n    Options& chdir(const std::string& dir) {\n      childDir_ = dir;\n      return *this;\n    }\n\n#if defined(__linux__)\n    /**\n     * Child will receive a signal when the parent *thread* exits.\n     *\n     * This is especially important when this option is used but the calling\n     * thread does not block for the duration of the subprocess. If the original\n     * thread that created the subprocess ends then the subprocess will\n     * terminate. For example, thread pool executors which can reap unused\n     * threads may trigger this behavior.\n     */\n    Options& parentDeathSignal(int sig) {\n      parentDeathSignal_ = sig;\n      return *this;\n    }\n#endif\n\n    /**\n     * Child will be made a process group leader when it starts. Upside: one\n     * can reliably kill all its non-daemonizing descendants.  Downside: the\n     * child will not receive Ctrl-C etc during interactive use.\n     */\n    Options& processGroupLeader() {\n      processGroupLeader_ = true;\n      return *this;\n    }\n\n    /**\n     * Detach the spawned process, to allow destroying the Subprocess object\n     * without waiting for the child process to finish.\n     *\n     * This causes the code to vfork twice before executing the command. The\n     * intermediate child process will exit immediately after execve, causing\n     * the process running the executable to be reparented to init (pid 1).\n     *\n     * Subprocess objects created with detach() enabled will already be in an\n     * \"EXITED\" state when the constructor returns.  The caller should not call\n     * wait() or poll() on the Subprocess, and pid() will return -1.\n     */\n    Options& detach() {\n      detach_ = true;\n      return *this;\n    }\n\n    /**\n     * If the Subprocess object is destroyed while the process is still running,\n     * automatically kill the child with SIGKILL and wait on the pid.\n     */\n    Options& killChildOnDestruction() {\n      destroyBehavior_ = 0;\n      return *this;\n    }\n\n    /**\n     * If the Subprocess object is destroyed while the process is still running,\n     * use terminateOrKill() to stop it and wait for it to exit.\n     *\n     * Beware that this may cause the Subprocess destructor to block while\n     * waiting on the child process to exit.\n     */\n    Options& terminateChildOnDestruction(TimeoutDuration timeout) {\n      destroyBehavior_ = std::max(TimeoutDuration::rep(0), timeout.count());\n      return *this;\n    }\n\n    /**\n     * By default, if Subprocess is destroyed while the child process is\n     * still RUNNING, the destructor will log a fatal.  You can skip this\n     * behavior by setting it to true here.\n     *\n     * Note that detach()ed processes are never in RUNNING state, so this\n     * setting does not impact such processes.\n     *\n     * BEWARE: setting this flag can leave zombie processes behind on the system\n     * after the folly::Subprocess is destroyed.  In general you should avoid\n     * using this setting.  In general, prefer using one of the following\n     * options instead:\n     * - If you do not care about monitoring the child process or waiting for it\n     *   to complete, use detach().\n     * - If you want to automatically clean up the child process when the\n     *   Subprocess is destroyed, use killChildOnDestruction() or\n     *   terminateChildOnDestruction()\n     * - If you want to allow the parent process to exit without waiting on the\n     *   child, prefer simply leaking the folly::Subprocess object when the\n     *   parent process exits.  You could exit with _exit(), or you could\n     *   explicitly leak the Subprocess using std::unique_ptr::release() or\n     *   similar mechanisms.\n     */\n    Options& allowDestructionWhileProcessRunning(bool val) {\n      destroyBehavior_ = val ? DestroyBehaviorLeak : DestroyBehaviorFatal;\n      return *this;\n    }\n\n#if defined(__linux__)\n    Options& setCpuSet(\n        const cpu_set_t& cpuSet, std::shared_ptr<int> errout = nullptr) {\n      cpuSet_ = AttrWithMeta<cpu_set_t>{cpuSet, std::move(errout)};\n      return *this;\n    }\n\n    /*\n     * setLinuxCGroup*\n     * Takes a fd or a path to the cgroup dir. Only one may be provided.\n     * Note that the cgroup filesystem may be mounted at any arbitrary point in\n     * the filesystem hierarchy, and that different distributions may have their\n     * own standard points. So just taking a cgroup name would be non-portable.\n     */\n    Options& setLinuxCGroupFd(\n        int cgroupFd, std::shared_ptr<int> errout = nullptr);\n    Options& setLinuxCGroupPath(\n        const std::string& cgroupPath, std::shared_ptr<int> errout = nullptr);\n#endif\n\n    Options& setUid(uid_t uid, std::shared_ptr<int> errout = nullptr) {\n      uid_ = AttrWithMeta<uid_t>{uid, std::move(errout)};\n      return *this;\n    }\n    Options& setGid(gid_t gid, std::shared_ptr<int> errout = nullptr) {\n      gid_ = AttrWithMeta<gid_t>{gid, std::move(errout)};\n      return *this;\n    }\n    Options& setEUid(uid_t uid, std::shared_ptr<int> errout = nullptr) {\n      euid_ = AttrWithMeta<uid_t>{uid, std::move(errout)};\n      return *this;\n    }\n    Options& setEGid(gid_t gid, std::shared_ptr<int> errout = nullptr) {\n      egid_ = AttrWithMeta<gid_t>{gid, std::move(errout)};\n      return *this;\n    }\n\n    Options& setSignalMask(sigset_t sigmask) {\n      sigmask_ = sigmask;\n      return *this;\n    }\n\n    Options& addPrintPidToBuffer(span<char> buf);\n\n    Options& addRLimit(\n        int resource, rlimit limit, std::shared_ptr<int> errout = nullptr);\n\n   private:\n    template <typename T>\n    struct AttrWithMeta {\n      T value{};\n\n      /// nullptr if required, ptr if optional to report failure\n      std::shared_ptr<int> erroutLifetime_{}; // do not access in child\n      int* errout{erroutLifetime_.get()};\n    };\n\n    using FdMap = boost::container::flat_map<int, int>;\n    FdMap fdActions_;\n    bool closeOtherFds_{false};\n    bool usePath_{false};\n    bool processGroupLeader_{false};\n    bool detach_{false};\n    // The behavior to take if the Subprocess destructor is invoked while the\n    // child process is still running.  This is either\n    // DestroyBehaviorFatal, DestroyBehaviorLeak, or a timeout value to pass to\n    // terminateOrKill() to kill the child process.\n    TimeoutDuration::rep destroyBehavior_{DestroyBehaviorFatal};\n    std::string childDir_; // \"\" keeps the parent's working directory\n    AttrWithMeta<int> linuxCGroupFd_{-1, nullptr}; // -1 means no cgroup\n    AttrWithMeta<std::string> linuxCGroupPath_{}; // empty means no cgroup\n#if defined(__linux__)\n    int parentDeathSignal_{0};\n    Optional<AttrWithMeta<cpu_set_t>> cpuSet_;\n#endif\n    Optional<AttrWithMeta<uid_t>> uid_;\n    Optional<AttrWithMeta<gid_t>> gid_;\n    Optional<AttrWithMeta<uid_t>> euid_;\n    Optional<AttrWithMeta<gid_t>> egid_;\n    Optional<sigset_t> sigmask_;\n    std::unordered_set<char*> setPrintPidToBuffer_;\n    std::unordered_map<int, AttrWithMeta<rlimit>> rlimits_;\n  };\n\n  // Non-copyable, but movable\n  Subprocess(const Subprocess&) = delete;\n  Subprocess& operator=(const Subprocess&) = delete;\n  Subprocess(Subprocess&&) = default;\n  Subprocess& operator=(Subprocess&&) = default;\n\n  /**\n   * Create an uninitialized subprocess.\n   *\n   * In this state it can only be destroyed, or assigned to using the move\n   * assignment operator.\n   */\n  Subprocess();\n\n  /**\n   * Create a subprocess from the given arguments.  argv[0] must be listed.\n   * If not-null, executable must be the actual executable\n   * being used (otherwise it's the same as argv[0]).\n   *\n   * If env is not-null, it must contain name=value strings to be used\n   * as the child's environment; otherwise, we inherit the environment\n   * from the parent.  env must be null if options.usePath is set.\n   */\n  explicit Subprocess(\n      const std::vector<std::string>& argv,\n      const Options& options = Options(),\n      const char* executable = nullptr,\n      const std::vector<std::string>* env = nullptr);\n  ~Subprocess();\n\n  /**\n   * Create a Subprocess object for an existing child process ID.\n   *\n   * The process ID must refer to an immediate child process of the current\n   * process.  This allows using the poll() and wait() APIs on a process ID\n   * that was not originally spawned by Subprocess.\n   */\n  static Subprocess fromExistingProcess(pid_t pid);\n\n  /**\n   * Create a subprocess run as a shell command (as shell -c 'command')\n   *\n   * The shell to use is taken from the environment variable $SHELL,\n   * or /bin/sh if $SHELL is unset.\n   */\n  // clang-format off\n  [[deprecated(\n      \"Prefer not running in a shell or use `shellify`.\")]]\n  explicit Subprocess(\n      const std::string& cmd,\n      const Options& options = Options(),\n      const std::vector<std::string>* env = nullptr);\n  // clang-format on\n\n  ////\n  //// The methods below only manipulate the process state, and do not\n  //// affect its communication pipes.\n  ////\n\n  /**\n   * Return the child's pid, or -1 if the child wasn't successfully spawned\n   * or has already been wait()ed upon.\n   */\n  pid_t pid() const;\n\n  /**\n   * Return the child's status (as per wait()) if the process has already\n   * been waited on, -1 if the process is still running, or -2 if the\n   * process hasn't been successfully started.  NOTE that this does not call\n   * waitpid() or Subprocess::poll(), but simply returns the status stored\n   * in the Subprocess object.\n   */\n  ProcessReturnCode returnCode() const { return returnCode_; }\n\n  /**\n   * Poll the child's status and return it. Return the exit status if the\n   * subprocess had quit, or RUNNING otherwise.  Throws an std::logic_error\n   * if called on a Subprocess whose status is no longer RUNNING.  No other\n   * exceptions are possible.  Aborts on egregious violations of contract,\n   * e.g. if you wait for the underlying process without going through this\n   * Subprocess instance.\n   */\n  ProcessReturnCode poll(struct rusage* ru = nullptr);\n\n  /**\n   * Poll the child's status.  If the process is still running, return false.\n   * Otherwise, return true if the process exited with status 0 (success),\n   * or throw CalledProcessError if the process exited with a non-zero status.\n   * Note: this should only be called for processes in the RUNNING state. If\n   * another trigger has caused the process to register as exited, or the\n   * process is yet to start before this is called, this will throw a\n   * std::logic_error.\n   */\n  bool pollChecked();\n\n  /**\n   * Wait for the process to terminate and return its status.  Like poll(),\n   * the only exception this can throw is std::logic_error if you call this\n   * on a Subprocess whose status is not RUNNING.  Aborts on egregious\n   * violations of contract, like an out-of-band waitpid(p.pid(), 0, 0).\n   */\n  ProcessReturnCode wait();\n\n  /**\n   * Wait for the process to terminate and return its status and rusage.  Like\n   * poll(), the only exception this can throw is std::logic_error if you call\n   * this on a Subprocess whose status is not RUNNING.  Aborts on egregious\n   * violations of contract, like an out-of-band wait4(p.pid(), 0, 0, nullptr).\n   */\n  ProcessReturnCode waitAndGetRusage(struct rusage* ru);\n\n  /**\n   * Wait for the process to terminate, throw if unsuccessful.\n   */\n  void waitChecked();\n\n  /**\n   * Call `waitpid` non-blockingly up to `timeout`. Throws std::logic_error if\n   * called on a Subprocess whose status is not RUNNING.\n   *\n   * The return code will be running() if waiting timed out.\n   */\n  ProcessReturnCode waitTimeout(TimeoutDuration timeout);\n\n  /**\n   * Send a signal to the child.  Shortcuts for the commonly used Unix\n   * signals are below.\n   */\n  void sendSignal(int signal);\n  void terminate() { sendSignal(SIGTERM); }\n  void kill() { sendSignal(SIGKILL); }\n\n  /**\n   * Call `waitpid` non-blockingly up to `waitTimeout`. If the process hasn't\n   * terminated after that, fall back on `terminateOrKill` with\n   * `sigtermTimeoutSeconds`.\n   */\n  ProcessReturnCode waitOrTerminateOrKill(\n      TimeoutDuration waitTimeout, TimeoutDuration sigtermTimeout);\n\n  /**\n   * Send the SIGTERM to terminate the process, poll `waitpid` non-blockingly\n   * several times up to `sigtermTimeout`. If the process hasn't terminated\n   * after that, send SIGKILL to kill the process and call `waitpid` blockingly.\n   * Return the exit code of process.\n   *\n   * If sigtermTimeout is 0 or negative, this will immediately send SIGKILL\n   * without first sending SIGTERM.\n   */\n  ProcessReturnCode terminateOrKill(TimeoutDuration sigtermTimeout);\n\n  ////\n  //// The methods below only affect the process's communication pipes, but\n  //// not its return code or state (they do not poll() or wait()).\n  ////\n\n  /**\n   * Communicate with the child until all pipes to/from the child are closed.\n   *\n   * The input buffer is written to the process' stdin pipe, and data is read\n   * from the stdout and stderr pipes.  Non-blocking I/O is performed on all\n   * pipes simultaneously to avoid deadlocks.\n   *\n   * The stdin pipe will be closed after the full input buffer has been written.\n   * An error will be thrown if a non-empty input buffer is supplied but stdin\n   * was not configured as a pipe.\n   *\n   * Returns a pair of buffers containing the data read from stdout and stderr.\n   * If stdout or stderr is not a pipe, an empty IOBuf queue will be returned\n   * for the respective buffer.\n   *\n   * Note that communicate() and communicateIOBuf() both return when all\n   * pipes to/from the child are closed; the child might stay alive after\n   * that, so you must still wait().\n   *\n   * communicateIOBuf() uses IOBufQueue for buffering (which has the\n   * advantage that it won't try to allocate all data at once), but it does\n   * store the subprocess's entire output in memory before returning.\n   *\n   * communicate() uses strings for simplicity.\n   */\n  std::pair<IOBufQueue, IOBufQueue> communicateIOBuf(\n      IOBufQueue input = IOBufQueue());\n\n  std::pair<std::string, std::string> communicate(\n      StringPiece input = StringPiece());\n\n  /**\n   * Communicate with the child until all pipes to/from the child are closed.\n   *\n   * == Semantics ==\n   *\n   * readCallback(pfd, cfd) will be called whenever there's data available\n   * on any pipe *from* the child (PIPE_OUT).  pfd is the file descriptor\n   * in the parent (that you use to read from); cfd is the file descriptor\n   * in the child (used for identifying the stream; 1 = child's standard\n   * output, 2 = child's standard error, etc)\n   *\n   * writeCallback(pfd, cfd) will be called whenever a pipe *to* the child is\n   * writable (PIPE_IN).  pfd is the file descriptor in the parent (that you\n   * use to write to); cfd is the file descriptor in the child (used for\n   * identifying the stream; 0 = child's standard input, etc)\n   *\n   * The read and write callbacks must read from / write to pfd and return\n   * false during normal operation.  Return true to tell communicate() to\n   * close the pipe.  For readCallback, this might send SIGPIPE to the\n   * child, or make its writes fail with EPIPE, so you should generally\n   * avoid returning true unless you've reached end-of-file.\n   *\n   * communicate() returns when all pipes to/from the child are closed; the\n   * child might stay alive after that, so you must still wait().\n   * Conversely, the child may quit long before its pipes are closed, since\n   * its descendants can keep them alive forever.\n   *\n   * Most users won't need to use this callback version; the simpler version\n   * of communicate (which buffers data in memory) will probably work fine.\n   *\n   * == Things you must get correct ==\n   *\n   * 1) You MUST consume all data passed to readCallback (or return true to\n   * close the pipe).  Similarly, you MUST write to a writable pipe (or\n   * return true to close the pipe).  To do otherwise is an error that can\n   * result in a deadlock.  You must do this even for pipes you are not\n   * interested in.\n   *\n   * 2) pfd is nonblocking, so be prepared for read() / write() to return -1\n   * and set errno to EAGAIN (in which case you should return false).  Use\n   * readNoInt() from FileUtil.h to handle interrupted reads for you.\n   *\n   * 3) Your callbacks MUST NOT call any of the Subprocess methods that\n   * manipulate the pipe FDs.  Check the docblocks, but, for example,\n   * neither closeParentFd (return true instead) nor takeOwnershipOfPipes\n   * are safe.  Stick to reading/writing from pfd, as appropriate.\n   *\n   * == Good to know ==\n   *\n   * 1) See ReadLinesCallback for an easy way to consume the child's output\n   * streams line-by-line (or tokenized by another delimiter).\n   *\n   * 2) \"Wait until the descendants close the pipes\" is usually the behavior\n   * you want, since the descendants may have something to say even if the\n   * immediate child is dead.  If you need to be able to force-close all\n   * parent FDs, communicate() will NOT work for you.  Do it your own way by\n   * using takeOwnershipOfPipes().\n   *\n   * Why not? You can return \"true\" from your callbacks to sever active\n   * pipes, but inactive ones can remain open indefinitely.  It is\n   * impossible to safely close inactive pipes while another thread is\n   * blocked in communicate().  This is BY DESIGN.  Racing communicate()'s\n   * read/write callbacks can result in wrong I/O and data corruption.  This\n   * class would need internal synchronization and timeouts, a poor and\n   * expensive implementation choice, in order to make closeParentFd()\n   * thread-safe.\n   */\n  using FdCallback = folly::Function<bool(int, int)>;\n  void communicate(FdCallback readCallback, FdCallback writeCallback);\n\n  /**\n   * A readCallback for Subprocess::communicate() that helps you consume\n   * lines (or other delimited pieces) from your subprocess's file\n   * descriptors.  Use the readLinesCallback() helper to get template\n   * deduction.  For example:\n   *\n   *   subprocess.communicate(\n   *     Subprocess::readLinesCallback(\n   *       [](int fd, folly::StringPiece s) {\n   *         std::cout << fd << \" said: \" << s;\n   *         return false;  // Keep reading from the child\n   *       }\n   *     ),\n   *     [](int pdf, int cfd){ return true; }  // Don't write to the child\n   *   );\n   *\n   * If a file line exceeds maxLineLength, your callback will get some\n   * initial chunks of maxLineLength with no trailing delimiters.  The final\n   * chunk of a line is delimiter-terminated iff the delimiter was present\n   * in the input.  In particular, the last line in a file always lacks a\n   * delimiter -- so if a file ends on a delimiter, the final line is empty.\n   *\n   * Like a regular communicate() callback, your fdLineCb() normally returns\n   * false.  It may return true to tell Subprocess to close the underlying\n   * file descriptor.  The child process may then receive SIGPIPE or get\n   * EPIPE errors on writes.\n   */\n  template <class Callback>\n  class ReadLinesCallback {\n   private:\n    // Binds an FD to the client-provided FD+line callback\n    struct StreamSplitterCallback {\n      StreamSplitterCallback(Callback& cb, int fd) : cb_(cb), fd_(fd) {}\n      // The return value semantics are inverted vs StreamSplitter\n      bool operator()(StringPiece s) { return !cb_(fd_, s); }\n      Callback& cb_;\n      int fd_;\n    };\n    using LineSplitter = gen::StreamSplitter<StreamSplitterCallback>;\n\n   public:\n    explicit ReadLinesCallback(\n        Callback&& fdLineCb,\n        uint64_t maxLineLength = 0, // No line length limit by default\n        char delimiter = '\\n',\n        uint64_t bufSize = 1024)\n        : fdLineCb_(std::forward<Callback>(fdLineCb)),\n          maxLineLength_(maxLineLength),\n          delimiter_(delimiter),\n          bufSize_(bufSize) {}\n\n    bool operator()(int pfd, int cfd) {\n      // Make a splitter for this cfd if it doesn't already exist\n      auto it = fdToSplitter_.find(cfd);\n      auto& splitter = (it != fdToSplitter_.end())\n          ? it->second\n          : fdToSplitter_\n                .emplace(\n                    cfd,\n                    LineSplitter(\n                        delimiter_,\n                        StreamSplitterCallback(fdLineCb_, cfd),\n                        maxLineLength_))\n                .first->second;\n      // Read as much as we can from this FD\n      char buf[bufSize_];\n      while (true) {\n        ssize_t ret = readNoInt(pfd, buf, bufSize_);\n        if (ret == -1 && errno == EAGAIN) { // No more data for now\n          return false;\n        }\n        checkUnixError(ret, \"read\");\n        if (ret == 0) { // Reached end-of-file\n          splitter.flush(); // Ignore return since the file is over anyway\n          return true;\n        }\n        if (!splitter(StringPiece(buf, ret))) {\n          return true; // The callback told us to stop\n        }\n      }\n    }\n\n   private:\n    Callback fdLineCb_;\n    const uint64_t maxLineLength_;\n    const char delimiter_;\n    const uint64_t bufSize_;\n    // We lazily make splitters for all cfds that get used.\n    std::unordered_map<int, LineSplitter> fdToSplitter_;\n  };\n\n  // Helper to enable template deduction\n  template <class Callback>\n  static auto readLinesCallback(\n      Callback&& fdLineCb,\n      uint64_t maxLineLength = 0, // No line length limit by default\n      char delimiter = '\\n',\n      uint64_t bufSize = 1024)\n      -> ReadLinesCallback<typename std::decay<Callback>::type> {\n    return ReadLinesCallback<typename std::decay<Callback>::type>(\n        std::forward<Callback>(fdLineCb), maxLineLength, delimiter, bufSize);\n  }\n\n  /**\n   * communicate() callbacks can use this to temporarily enable/disable\n   * notifications (callbacks) for a pipe to/from the child.  By default,\n   * all are enabled.  Useful for \"chatty\" communication -- you want to\n   * disable write callbacks until you receive the expected message.\n   *\n   * Disabling a pipe does not free you from the requirement to consume all\n   * incoming data.  Failing to do so will easily create deadlock bugs.\n   *\n   * Throws if the childFd is not known.\n   */\n  void enableNotifications(int childFd, bool enabled);\n\n  /**\n   * Are notifications for one pipe to/from child enabled?  Throws if the\n   * childFd is not known.\n   */\n  bool notificationsEnabled(int childFd) const;\n\n  ////\n  //// The following methods are meant for the cases when communicate() is\n  //// not suitable.  You should not need them when you call communicate(),\n  //// and, in fact, it is INHERENTLY UNSAFE to use closeParentFd() or\n  //// takeOwnershipOfPipes() from a communicate() callback.\n  ////\n\n  /**\n   * Close the parent file descriptor given a file descriptor in the child.\n   * DO NOT USE from communicate() callbacks; make them return true instead.\n   */\n  void closeParentFd(int childFd);\n\n  /**\n   * Set all pipes from / to child to be non-blocking.  communicate() does\n   * this for you.\n   */\n  void setAllNonBlocking();\n\n  /**\n   * Get parent file descriptor corresponding to the given file descriptor\n   * in the child.  Throws if childFd isn't a pipe (PIPE_IN / PIPE_OUT).\n   * Do not close() the returned file descriptor; use closeParentFd, above.\n   */\n  int parentFd(int childFd) const {\n    return pipes_[findByChildFd(childFd)].pipe.fd();\n  }\n  int stdinFd() const { return parentFd(0); }\n  int stdoutFd() const { return parentFd(1); }\n  int stderrFd() const { return parentFd(2); }\n\n  /**\n   * The child's pipes are logically separate from the process metadata\n   * (they may even be kept alive by the child's descendants).  This call\n   * lets you manage the pipes' lifetime separately from the lifetime of the\n   * child process.\n   *\n   * After this call, the Subprocess instance will have no knowledge of\n   * these pipes, and the caller assumes responsibility for managing their\n   * lifetimes.  Pro-tip: prefer to explicitly close() the pipes, since\n   * folly::File would otherwise silently suppress I/O errors.\n   *\n   * No, you may NOT call this from a communicate() callback.\n   */\n  struct ChildPipe {\n    ChildPipe(int fd, folly::File&& ppe) : childFd(fd), pipe(std::move(ppe)) {}\n    int childFd;\n    folly::File pipe; // Owns the parent FD\n  };\n  std::vector<ChildPipe> takeOwnershipOfPipes();\n\n private:\n  struct LibcReal;\n  struct SpawnRawArgs;\n  struct ChildErrorInfo;\n\n  // spawn() sets up a pipe to read errors from the child,\n  // then calls spawnInternal() to do the bulk of the work.  Once\n  // spawnInternal() returns it reads the error pipe to see if the child\n  // encountered any errors.\n  void spawn(\n      std::unique_ptr<const char*[]> argv,\n      const char* executable,\n      const Options& options,\n      const std::vector<std::string>* env);\n  void spawnInternal(\n      std::unique_ptr<const char*[]> argv,\n      const char* executable,\n      Options& options,\n      const std::vector<std::string>* env,\n      ChildErrorInfo* err);\n\n  static pid_t spawnInternalDoFork(SpawnRawArgs const& args);\n  [[noreturn]] static void childError(\n      SpawnRawArgs const& args, int errCode, int errnoValue);\n\n  // Actions to run in child.\n  // Note that this runs after vfork(), so tread lightly.\n  // Returns 0 on success, or an errno value on failure.\n  static int prepareChild(SpawnRawArgs const& args);\n  static int prepareChildDoOptionalError(int* errout);\n  static int prepareChildDoLinuxCGroup(SpawnRawArgs const& args);\n  static int runChild(SpawnRawArgs const& args);\n\n  // Closes fds inherited from parent in child process\n  static void closeInheritedFds(const SpawnRawArgs& args);\n\n  /**\n   * Read from the error pipe, and throw SubprocessSpawnError if the child\n   * failed before calling exec().\n   */\n  void readChildErrorNum(ChildErrorInfo err, const char* executable);\n\n  // Returns an index into pipes_. Throws std::invalid_argument if not found.\n  size_t findByChildFd(const int childFd) const;\n\n  static constexpr TimeoutDuration::rep DestroyBehaviorFatal = -1;\n  static constexpr TimeoutDuration::rep DestroyBehaviorLeak = -2;\n\n  pid_t pid_{-1};\n  ProcessReturnCode returnCode_;\n  TimeoutDuration::rep destroyBehavior_ = DestroyBehaviorFatal;\n\n  /**\n   * Represents a pipe between this process, and the child process (or its\n   * descendant).  To interact with these pipes, you can use communicate(),\n   * or use parentFd() and related methods, or separate them from the\n   * Subprocess instance entirely via takeOwnershipOfPipes().\n   */\n  struct Pipe : private boost::totally_ordered<Pipe> {\n    folly::File pipe; // Our end of the pipe, wrapped in a File to auto-close.\n    int childFd = -1; // Identifies the pipe: what FD is this in the child?\n    int direction = PIPE_IN; // one of PIPE_IN / PIPE_OUT\n    bool enabled = true; // Are notifications enabled in communicate()?\n\n    bool operator<(const Pipe& other) const { return childFd < other.childFd; }\n    bool operator==(const Pipe& other) const {\n      return childFd == other.childFd;\n    }\n  };\n\n  // Populated at process start according to fdActions, empty after\n  // takeOwnershipOfPipes().  Sorted by childFd.  Can only have elements\n  // erased, but not inserted, after being populated.\n  //\n  // The number of pipes between parent and child is assumed to be small,\n  // so we're happy with a vector here, even if it means linear erase.\n  std::vector<Pipe> pipes_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Synchronized.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_synchronized\n//\n\n/**\n * This module implements a Synchronized abstraction useful in\n * mutex-based concurrency.\n *\n * The Synchronized<T, Mutex> class is the primary public API exposed by this\n * module.  See folly/docs/Synchronized.md for a more complete explanation of\n * this class and its benefits.\n */\n\n#pragma once\n\n#include <folly/Function.h>\n#include <folly/Likely.h>\n#include <folly/Preprocessor.h>\n#include <folly/SharedMutex.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/container/Foreach.h>\n#include <folly/functional/ApplyTuple.h>\n#include <folly/synchronization/Lock.h>\n\n#include <glog/logging.h>\n\n#include <array>\n#include <mutex>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\nnamespace folly {\n\nnamespace detail {\n\ntemplate <typename, typename Mutex>\ninline constexpr bool kSynchronizedMutexIsUnique = false;\ntemplate <typename Mutex>\ninline constexpr bool kSynchronizedMutexIsUnique<\n    decltype(void(std::declval<Mutex&>().lock())),\n    Mutex> = true;\n\ntemplate <typename, typename Mutex>\ninline constexpr bool kSynchronizedMutexIsShared = false;\ntemplate <typename Mutex>\ninline constexpr bool kSynchronizedMutexIsShared<\n    decltype(void(std::declval<Mutex&>().lock_shared())),\n    Mutex> = true;\n\ntemplate <typename, typename Mutex>\ninline constexpr bool kSynchronizedMutexIsUpgrade = false;\ntemplate <typename Mutex>\ninline constexpr bool kSynchronizedMutexIsUpgrade<\n    decltype(void(std::declval<Mutex&>().lock_upgrade())),\n    Mutex> = true;\n\n/**\n * An enum to describe the \"level\" of a mutex.  The supported levels are\n *  Unique - a normal mutex that supports only exclusive locking\n *  Shared - a shared mutex which has shared locking and unlocking functions;\n *  Upgrade - a mutex that has all the methods of the two above along with\n *            support for upgradable locking\n */\nenum class SynchronizedMutexLevel { Unknown, Unique, Shared, Upgrade };\n\ntemplate <typename Mutex>\ninline constexpr SynchronizedMutexLevel kSynchronizedMutexLevel =\n    kSynchronizedMutexIsUpgrade<void, Mutex>  ? SynchronizedMutexLevel::Upgrade\n    : kSynchronizedMutexIsShared<void, Mutex> ? SynchronizedMutexLevel::Shared\n    : kSynchronizedMutexIsUnique<void, Mutex>\n    ? SynchronizedMutexLevel::Unique\n    : SynchronizedMutexLevel::Unknown;\n\nenum class SynchronizedMutexMethod { Lock, TryLock };\n\ntemplate <SynchronizedMutexLevel Level, SynchronizedMutexMethod Method>\nstruct SynchronizedLockPolicy {\n  static constexpr SynchronizedMutexLevel level = Level;\n  static constexpr SynchronizedMutexMethod method = Method;\n};\nusing SynchronizedLockPolicyExclusive = SynchronizedLockPolicy<\n    SynchronizedMutexLevel::Unique,\n    SynchronizedMutexMethod::Lock>;\nusing SynchronizedLockPolicyTryExclusive = SynchronizedLockPolicy<\n    SynchronizedMutexLevel::Unique,\n    SynchronizedMutexMethod::TryLock>;\nusing SynchronizedLockPolicyShared = SynchronizedLockPolicy<\n    SynchronizedMutexLevel::Shared,\n    SynchronizedMutexMethod::Lock>;\nusing SynchronizedLockPolicyTryShared = SynchronizedLockPolicy<\n    SynchronizedMutexLevel::Shared,\n    SynchronizedMutexMethod::TryLock>;\nusing SynchronizedLockPolicyUpgrade = SynchronizedLockPolicy<\n    SynchronizedMutexLevel::Upgrade,\n    SynchronizedMutexMethod::Lock>;\nusing SynchronizedLockPolicyTryUpgrade = SynchronizedLockPolicy<\n    SynchronizedMutexLevel::Upgrade,\n    SynchronizedMutexMethod::TryLock>;\n\ntemplate <SynchronizedMutexLevel>\nstruct SynchronizedLockType_ {};\ntemplate <>\nstruct SynchronizedLockType_<SynchronizedMutexLevel::Unique> {\n  template <typename Mutex>\n  using apply = std::unique_lock<Mutex>;\n};\ntemplate <>\nstruct SynchronizedLockType_<SynchronizedMutexLevel::Shared> {\n  template <typename Mutex>\n  using apply = std::shared_lock<Mutex>;\n};\ntemplate <>\nstruct SynchronizedLockType_<SynchronizedMutexLevel::Upgrade> {\n  template <typename Mutex>\n  using apply = upgrade_lock<Mutex>;\n};\ntemplate <SynchronizedMutexLevel Level, typename MutexType>\nusing SynchronizedLockType =\n    typename SynchronizedLockType_<Level>::template apply<MutexType>;\n\n} // namespace detail\n\n/**\n * SynchronizedBase is a helper parent class for Synchronized<T>.\n *\n * It provides wlock() and rlock() methods for shared mutex types,\n * or lock() methods for purely exclusive mutex types.\n */\ntemplate <class Subclass, detail::SynchronizedMutexLevel level>\nclass SynchronizedBase;\n\ntemplate <class LockedType, class Mutex, class LockPolicy>\nclass LockedPtrBase;\ntemplate <class LockedType, class LockPolicy>\nclass LockedPtr;\n\n/**\n * SynchronizedBase specialization for shared mutex types.\n *\n * This class provides wlock() and rlock() methods for acquiring the lock and\n * accessing the data.\n */\ntemplate <class Subclass>\nclass SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Shared> {\n private:\n  template <typename T, typename P>\n  using LockedPtr_ = ::folly::LockedPtr<T, P>;\n\n public:\n  using LockPolicyExclusive = detail::SynchronizedLockPolicyExclusive;\n  using LockPolicyShared = detail::SynchronizedLockPolicyShared;\n  using LockPolicyTryExclusive = detail::SynchronizedLockPolicyTryExclusive;\n  using LockPolicyTryShared = detail::SynchronizedLockPolicyTryShared;\n\n  using WLockedPtr = LockedPtr_<Subclass, LockPolicyExclusive>;\n  using ConstWLockedPtr = LockedPtr_<const Subclass, LockPolicyExclusive>;\n\n  using RLockedPtr = LockedPtr_<Subclass, LockPolicyShared>;\n  using ConstRLockedPtr = LockedPtr_<const Subclass, LockPolicyShared>;\n\n  using TryWLockedPtr = LockedPtr_<Subclass, LockPolicyTryExclusive>;\n  using ConstTryWLockedPtr = LockedPtr_<const Subclass, LockPolicyTryExclusive>;\n\n  using TryRLockedPtr = LockedPtr_<Subclass, LockPolicyTryShared>;\n  using ConstTryRLockedPtr = LockedPtr_<const Subclass, LockPolicyTryShared>;\n\n  // These aliases are deprecated.\n  // TODO: Codemod them away.\n  using LockedPtr = WLockedPtr;\n  using ConstLockedPtr = ConstRLockedPtr;\n\n  /**\n   * @brief Acquire an exclusive lock.\n   *\n   * Acquire an exclusive lock, and return a LockedPtr that can be used to\n   * safely access the datum.\n   *\n   * LockedPtr offers operator -> and * to provide access to the datum.\n   * The lock will be released when the LockedPtr is destroyed.\n   *\n   * @methodset Exclusive lock\n   */\n  LockedPtr wlock() { return LockedPtr(static_cast<Subclass*>(this)); }\n  ConstWLockedPtr wlock() const {\n    return ConstWLockedPtr(static_cast<const Subclass*>(this));\n  }\n\n  /**\n   * @brief Acquire an exclusive lock, or null.\n   *\n   * Attempts to acquire the lock in exclusive mode.  If acquisition is\n   * unsuccessful, the returned LockedPtr will be null.\n   *\n   * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for\n   * validity.)\n   *\n   * @methodset Exclusive lock\n   */\n  TryWLockedPtr tryWLock() {\n    return TryWLockedPtr{static_cast<Subclass*>(this)};\n  }\n  ConstTryWLockedPtr tryWLock() const {\n    return ConstTryWLockedPtr{static_cast<const Subclass*>(this)};\n  }\n\n  /**\n   * @brief Acquire a read lock.\n   *\n   * The returned LockedPtr will force const access to the data unless the lock\n   * is acquired in non-const context and asNonConstUnsafe() is used.\n   *\n   * @methodset Shared lock\n   */\n  RLockedPtr rlock() { return RLockedPtr(static_cast<Subclass*>(this)); }\n  ConstLockedPtr rlock() const {\n    return ConstLockedPtr(static_cast<const Subclass*>(this));\n  }\n\n  /**\n   * @brief Acquire a read lock, or null.\n   *\n   * Attempts to acquire the lock in shared mode.  If acquisition is\n   * unsuccessful, the returned LockedPtr will be null.\n   *\n   * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for\n   * validity.)\n   *\n   * @methodset Shared lock\n   */\n  TryRLockedPtr tryRLock() {\n    return TryRLockedPtr{static_cast<Subclass*>(this)};\n  }\n  ConstTryRLockedPtr tryRLock() const {\n    return ConstTryRLockedPtr{static_cast<const Subclass*>(this)};\n  }\n\n  /**\n   * Attempts to acquire the lock, or fails if the timeout elapses first.\n   * If acquisition is unsuccessful, the returned LockedPtr will be null.\n   *\n   * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for\n   * validity.)\n   *\n   * @methodset Exclusive lock\n   */\n  template <class Rep, class Period>\n  LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) {\n    return LockedPtr(static_cast<Subclass*>(this), timeout);\n  }\n  template <class Rep, class Period>\n  LockedPtr wlock(const std::chrono::duration<Rep, Period>& timeout) const {\n    return LockedPtr(static_cast<const Subclass*>(this), timeout);\n  }\n\n  /**\n   * Attempts to acquire the lock, or fails if the timeout elapses first.\n   * If acquisition is unsuccessful, the returned LockedPtr will be null.\n   *\n   * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for\n   * validity.)\n   *\n   * @methodset Shared lock\n   */\n  template <class Rep, class Period>\n  RLockedPtr rlock(const std::chrono::duration<Rep, Period>& timeout) {\n    return RLockedPtr(static_cast<Subclass*>(this), timeout);\n  }\n  template <class Rep, class Period>\n  ConstRLockedPtr rlock(\n      const std::chrono::duration<Rep, Period>& timeout) const {\n    return ConstRLockedPtr(static_cast<const Subclass*>(this), timeout);\n  }\n\n  /**\n   * Invoke a function while holding the lock exclusively.\n   *\n   * A reference to the datum will be passed into the function as its only\n   * argument.\n   *\n   * This can be used with a lambda argument for easily defining small critical\n   * sections in the code.  For example:\n   *\n   *   auto value = obj.withWLock([](auto& data) {\n   *     data.doStuff();\n   *     return data.getValue();\n   *   });\n   *\n   * @methodset Exclusive lock\n   */\n  template <class Function>\n  auto withWLock(Function&& function) {\n    return function(*wlock());\n  }\n  template <class Function>\n  auto withWLock(Function&& function) const {\n    return function(*wlock());\n  }\n\n  /**\n   * Invoke a function while holding the lock exclusively.\n   *\n   * This is similar to withWLock(), but the function will be passed a\n   * LockedPtr rather than a reference to the data itself.\n   *\n   * This allows scopedUnlock() to be called on the LockedPtr argument if\n   * desired.\n   *\n   * @methodset Exclusive lock\n   */\n  template <class Function>\n  auto withWLockPtr(Function&& function) {\n    return function(wlock());\n  }\n  template <class Function>\n  auto withWLockPtr(Function&& function) const {\n    return function(wlock());\n  }\n\n  /**\n   * Invoke a function while holding an the lock in shared mode.\n   *\n   * A const reference to the datum will be passed into the function as its\n   * only argument.\n   *\n   * @methodset Shared lock\n   */\n  template <class Function>\n  auto withRLock(Function&& function) const {\n    return function(*rlock());\n  }\n\n  /**\n   * Invoke a function while holding the lock in shared mode.\n   *\n   * This is similar to withRLock(), but the function will be passed a\n   * LockedPtr rather than a reference to the data itself.\n   *\n   * This allows scopedUnlock() to be called on the LockedPtr argument if\n   * desired.\n   *\n   * @methodset Shared lock\n   */\n  template <class Function>\n  auto withRLockPtr(Function&& function) {\n    return function(rlock());\n  }\n\n  template <class Function>\n  auto withRLockPtr(Function&& function) const {\n    return function(rlock());\n  }\n};\n\n/**\n * SynchronizedBase specialization for upgrade mutex types.\n *\n * This class provides all the functionality provided by the SynchronizedBase\n * specialization for shared mutexes and a ulock() method that returns an\n * upgrade lock RAII proxy\n */\ntemplate <class Subclass>\nclass SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Upgrade>\n    : public SynchronizedBase<\n          Subclass,\n          detail::SynchronizedMutexLevel::Shared> {\n private:\n  template <typename T, typename P>\n  using LockedPtr_ = ::folly::LockedPtr<T, P>;\n\n public:\n  using LockPolicyUpgrade = detail::SynchronizedLockPolicyUpgrade;\n  using LockPolicyTryUpgrade = detail::SynchronizedLockPolicyTryUpgrade;\n\n  using UpgradeLockedPtr = LockedPtr_<Subclass, LockPolicyUpgrade>;\n  using ConstUpgradeLockedPtr = LockedPtr_<const Subclass, LockPolicyUpgrade>;\n\n  using TryUpgradeLockedPtr = LockedPtr_<Subclass, LockPolicyTryUpgrade>;\n  using ConstTryUpgradeLockedPtr =\n      LockedPtr_<const Subclass, LockPolicyTryUpgrade>;\n\n  /**\n   * @brief Acquire an upgrade lock.\n   *\n   * The returned LockedPtr will have force const access to the data unless the\n   * lock is acquired in non-const context and asNonConstUnsafe() is used.\n   *\n   * @methodset Upgrade lock\n   */\n  UpgradeLockedPtr ulock() {\n    return UpgradeLockedPtr(static_cast<Subclass*>(this));\n  }\n  ConstUpgradeLockedPtr ulock() const {\n    return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this));\n  }\n\n  /**\n   * @brief Acquire an upgrade lock, or null.\n   *\n   * Attempts to acquire the lock in upgrade mode.  If acquisition is\n   * unsuccessful, the returned LockedPtr will be null.\n   *\n   * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for\n   * validity.)\n   *\n   * @methodset Upgrade lock\n   */\n  TryUpgradeLockedPtr tryULock() {\n    return TryUpgradeLockedPtr{static_cast<Subclass*>(this)};\n  }\n\n  /**\n   * Acquire an upgrade lock and return a LockedPtr that can be used to safely\n   * access the datum\n   *\n   * And the const version\n   *\n   * @methodset Upgrade lock\n   */\n  template <class Rep, class Period>\n  UpgradeLockedPtr ulock(const std::chrono::duration<Rep, Period>& timeout) {\n    return UpgradeLockedPtr(static_cast<Subclass*>(this), timeout);\n  }\n\n  /**\n   * Invoke a function while holding the lock.\n   *\n   * A reference to the datum will be passed into the function as its only\n   * argument.\n   *\n   * This can be used with a lambda argument for easily defining small critical\n   * sections in the code.  For example:\n   *\n   *   auto value = obj.withULock([](auto& data) {\n   *     data.doStuff();\n   *     return data.getValue();\n   *   });\n   *\n   * This is probably not the function you want.  If the intent is to read the\n   * data object and determine whether you should upgrade to a write lock then\n   * the withULockPtr() method should be called instead, since it gives access\n   * to the LockedPtr proxy (which can be upgraded via the\n   * moveFromUpgradeToWrite() method)\n   *\n   * @methodset Upgrade lock\n   */\n  template <class Function>\n  auto withULock(Function&& function) {\n    return function(*ulock());\n  }\n  template <class Function>\n  auto withULock(Function&& function) const {\n    return function(*ulock());\n  }\n\n  /**\n   * Invoke a function while holding the lock exclusively.\n   *\n   * This is similar to withULock(), but the function will be passed a\n   * LockedPtr rather than a reference to the data itself.\n   *\n   * This allows scopedUnlock() and as_lock() to be called on the\n   * LockedPtr argument.\n   *\n   * This also allows you to upgrade the LockedPtr proxy to a write state so\n   * that changes can be made to the underlying data\n   *\n   * @methodset Upgrade lock\n   */\n  template <class Function>\n  auto withULockPtr(Function&& function) {\n    return function(ulock());\n  }\n  template <class Function>\n  auto withULockPtr(Function&& function) const {\n    return function(ulock());\n  }\n};\n\n/**\n * SynchronizedBase specialization for non-shared mutex types.\n *\n * This class provides lock() methods for acquiring the lock and accessing the\n * data.\n */\ntemplate <class Subclass>\nclass SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Unique> {\n private:\n  template <typename T, typename P>\n  using LockedPtr_ = ::folly::LockedPtr<T, P>;\n\n public:\n  using LockPolicyExclusive = detail::SynchronizedLockPolicyExclusive;\n  using LockPolicyTryExclusive = detail::SynchronizedLockPolicyTryExclusive;\n\n  using LockedPtr = LockedPtr_<Subclass, LockPolicyExclusive>;\n  using ConstLockedPtr = LockedPtr_<const Subclass, LockPolicyExclusive>;\n\n  using TryLockedPtr = LockedPtr_<Subclass, LockPolicyTryExclusive>;\n  using ConstTryLockedPtr = LockedPtr_<const Subclass, LockPolicyTryExclusive>;\n\n  /**\n   * @brief Acquire the lock.\n   *\n   * Return a LockedPtr that can be used to safely access the datum.\n   *\n   * @methodset Non-shareable lock\n   */\n  LockedPtr lock() { return LockedPtr(static_cast<Subclass*>(this)); }\n\n  /**\n   * Acquire a lock, and return a ConstLockedPtr that can be used to safely\n   * access the datum.\n   *\n   * @methodset Non-shareable lock\n   */\n  ConstLockedPtr lock() const {\n    return ConstLockedPtr(static_cast<const Subclass*>(this));\n  }\n\n  /**\n   * @brief Acquire the lock, or null.\n   *\n   * Attempts to acquire the lock in exclusive mode.  If acquisition is\n   * unsuccessful, the returned LockedPtr will be null.\n   *\n   * (Use LockedPtr::operator bool() or LockedPtr::isNull() to check for\n   * validity.)\n   *\n   * @methodset Non-shareable lock\n   */\n  TryLockedPtr tryLock() { return TryLockedPtr{static_cast<Subclass*>(this)}; }\n  ConstTryLockedPtr tryLock() const {\n    return ConstTryLockedPtr{static_cast<const Subclass*>(this)};\n  }\n\n  /**\n   * Attempts to acquire the lock, or fails if the timeout elapses first.\n   * If acquisition is unsuccessful, the returned LockedPtr will be null.\n   *\n   * @methodset Non-shareable lock\n   */\n  template <class Rep, class Period>\n  LockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) {\n    return LockedPtr(static_cast<Subclass*>(this), timeout);\n  }\n\n  /**\n   * Attempts to acquire the lock, or fails if the timeout elapses first.\n   * If acquisition is unsuccessful, the returned LockedPtr will be null.\n   *\n   * @methodset Non-shareable lock\n   */\n  template <class Rep, class Period>\n  ConstLockedPtr lock(const std::chrono::duration<Rep, Period>& timeout) const {\n    return ConstLockedPtr(static_cast<const Subclass*>(this), timeout);\n  }\n\n  /**\n   * Invoke a function while holding the lock.\n   *\n   * A reference to the datum will be passed into the function as its only\n   * argument.\n   *\n   * This can be used with a lambda argument for easily defining small critical\n   * sections in the code.  For example:\n   *\n   *   auto value = obj.withLock([](auto& data) {\n   *     data.doStuff();\n   *     return data.getValue();\n   *   });\n   *\n   * @methodset Non-shareable lock\n   */\n  template <class Function>\n  auto withLock(Function&& function) {\n    return function(*lock());\n  }\n  template <class Function>\n  auto withLock(Function&& function) const {\n    return function(*lock());\n  }\n\n  /**\n   * Invoke a function while holding the lock exclusively.\n   *\n   * This is similar to withWLock(), but the function will be passed a\n   * LockedPtr rather than a reference to the data itself.\n   *\n   * This allows scopedUnlock() and as_lock() to be called on the\n   * LockedPtr argument.\n   *\n   * @methodset Non-shareable lock\n   */\n  template <class Function>\n  auto withLockPtr(Function&& function) {\n    return function(lock());\n  }\n  template <class Function>\n  auto withLockPtr(Function&& function) const {\n    return function(lock());\n  }\n};\n\n/**\n * `folly::Synchronized` pairs a datum with a mutex. The datum can only be\n * reached through a `LockedPtr`, typically acquired via `.rlock()` or\n * `.wlock()`; the mutex is held for the lifetime of the `LockedPtr`.\n *\n * It is recommended to explicitly open a new nested scope when aquiring\n * a `LockedPtr` object, to help visibly delineate the critical section and to\n * ensure that the `LockedPtr` is destroyed as soon as it is no longer needed.\n *\n * @tparam T  The type of datum to be stored.\n * @tparam Mutex  The mutex type that guards the datum. Must be Lockable.\n *\n * @refcode folly/docs/examples/folly/Synchronized.cpp\n */\ntemplate <class T, class Mutex = SharedMutex>\nstruct Synchronized\n    : public SynchronizedBase<\n          Synchronized<T, Mutex>,\n          detail::kSynchronizedMutexLevel<Mutex>> {\n private:\n  using Base = SynchronizedBase<\n      Synchronized<T, Mutex>,\n      detail::kSynchronizedMutexLevel<Mutex>>;\n  static constexpr bool nxCopyCtor{\n      std::is_nothrow_copy_constructible<T>::value};\n  static constexpr bool nxMoveCtor{\n      std::is_nothrow_move_constructible<T>::value};\n\n  // used to disable copy construction and assignment\n  class NonImplementedType;\n\n public:\n  using LockedPtr = typename Base::LockedPtr;\n  using ConstLockedPtr = typename Base::ConstLockedPtr;\n  using DataType = T;\n  using MutexType = Mutex;\n\n  /**\n   * Default constructor leaves both members call their own default constructor.\n   */\n  constexpr Synchronized() = default;\n\n public:\n  /**\n   * Copy constructor. Enabled only when the data type is copy-constructible.\n   *\n   * Takes a shared-or-exclusive lock on the source mutex while performing the\n   * copy-construction of the destination data from the source data. No lock is\n   * taken on the destination mutex.\n   *\n   * May throw even when the data type is is nothrow-copy-constructible because\n   * acquiring a lock may throw.\n   *\n   * deprecated\n   */\n  /* implicit */ Synchronized(\n      typename std::conditional<\n          std::is_copy_constructible<T>::value,\n          const Synchronized&,\n          NonImplementedType>::type rhs) /* may throw */\n      : Synchronized(rhs.copy()) {}\n\n  /**\n   * Move-constructs from the source data without locking either the source or\n   * the destination mutex.\n   *\n   * Semantically, assumes that the source object is a true rvalue and therefore\n   * that no synchronization is required for accessing it.\n   *\n   * deprecated\n   */\n  Synchronized(Synchronized&& rhs) noexcept(nxMoveCtor)\n      : Synchronized(std::move(rhs.datum_)) {}\n\n  /**\n   * Constructor taking a datum as argument copies it. There is no\n   * need to lock the constructing object.\n   */\n  explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {}\n\n  /**\n   * Constructor taking a datum rvalue as argument moves it. There is no need\n   * to lock the constructing object.\n   */\n  explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)\n      : datum_(std::move(rhs)) {}\n\n  /**\n   * Lets you construct non-movable types in-place. Use the constexpr\n   * instance `in_place` as the first argument.\n   */\n  template <typename... Args>\n  explicit constexpr Synchronized(std::in_place_t, Args&&... args)\n      : datum_(std::forward<Args>(args)...) {}\n\n  /**\n   * Lets you construct the synchronized object and also pass construction\n   * parameters to the underlying mutex if desired\n   */\n  template <typename... DatumArgs, typename... MutexArgs>\n  Synchronized(\n      std::piecewise_construct_t,\n      std::tuple<DatumArgs...> datumArgs,\n      std::tuple<MutexArgs...> mutexArgs)\n      : Synchronized{\n            std::piecewise_construct,\n            std::move(datumArgs),\n            std::move(mutexArgs),\n            std::make_index_sequence<sizeof...(DatumArgs)>{},\n            std::make_index_sequence<sizeof...(MutexArgs)>{}} {}\n\n  /**\n   * Copy assignment operator.\n   *\n   * Enabled only when the data type is copy-constructible and move-assignable.\n   *\n   * Move-assigns from a copy of the source data.\n   *\n   * Takes a shared-or-exclusive lock on the source mutex while copying the\n   * source data to a temporary. Takes an exclusive lock on the destination\n   * mutex while move-assigning from the temporary.\n   *\n   * This technique consts an extra temporary but avoids the need to take locks\n   * on both mutexes together.\n   *\n   * deprecated\n   */\n  Synchronized& operator=(\n      typename std::conditional<\n          std::is_copy_constructible<T>::value &&\n              std::is_move_assignable<T>::value,\n          const Synchronized&,\n          NonImplementedType>::type rhs) {\n    return *this = rhs.copy();\n  }\n\n  /**\n   * Move assignment operator.\n   *\n   * Takes an exclusive lock on the destination mutex while move-assigning the\n   * destination data from the source data. The source mutex is not locked or\n   * otherwise accessed.\n   *\n   * Semantically, assumes that the source object is a true rvalue and therefore\n   * that no synchronization is required for accessing it.\n   *\n   * deprecated\n   */\n  Synchronized& operator=(Synchronized&& rhs) {\n    return *this = std::move(rhs.datum_);\n  }\n\n  /**\n   * Lock object, assign datum.\n   */\n  Synchronized& operator=(const T& rhs) {\n    if (&datum_ != &rhs) {\n      auto guard = LockedPtr{this};\n      datum_ = rhs;\n    }\n    return *this;\n  }\n\n  /**\n   * Lock object, move-assign datum.\n   */\n  Synchronized& operator=(T&& rhs) {\n    if (&datum_ != &rhs) {\n      auto guard = LockedPtr{this};\n      datum_ = std::move(rhs);\n    }\n    return *this;\n  }\n\n  /**\n   * @brief Acquire some lock.\n   *\n   * If the mutex is a shared mutex, and the Synchronized instance is const,\n   * this acquires a shared lock.  Otherwise this acquires an exclusive lock.\n   *\n   * In general, prefer using the explicit rlock() and wlock() methods\n   * for read-write locks, and lock() for purely exclusive locks.\n   *\n   * contextualLock() is primarily intended for use in other template functions\n   * that do not necessarily know the lock type.\n   */\n  LockedPtr contextualLock() { return LockedPtr(this); }\n  ConstLockedPtr contextualLock() const { return ConstLockedPtr(this); }\n  template <class Rep, class Period>\n  LockedPtr contextualLock(const std::chrono::duration<Rep, Period>& timeout) {\n    return LockedPtr(this, timeout);\n  }\n  template <class Rep, class Period>\n  ConstLockedPtr contextualLock(\n      const std::chrono::duration<Rep, Period>& timeout) const {\n    return ConstLockedPtr(this, timeout);\n  }\n  /**\n   * @brief Acquire a lock for reading.\n   *\n   * contextualRLock() acquires a read lock if the mutex type is shared,\n   * or a regular exclusive lock for non-shared mutex types.\n   *\n   * contextualRLock() when you know that you prefer a read lock (if\n   * available), even if the Synchronized<T> object itself is non-const.\n   */\n  ConstLockedPtr contextualRLock() const { return ConstLockedPtr(this); }\n  template <class Rep, class Period>\n  ConstLockedPtr contextualRLock(\n      const std::chrono::duration<Rep, Period>& timeout) const {\n    return ConstLockedPtr(this, timeout);\n  }\n\n  /**\n   * @brief Acquire a LockedPtr with timeout.\n   *\n   * Attempts to acquire for a given number of milliseconds. If\n   * acquisition is unsuccessful, the returned LockedPtr is nullptr.\n   *\n   * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.\n   * In the future it will be marked with a deprecation attribute to emit\n   * build-time warnings, and then it will be removed entirely.\n   */\n  LockedPtr timedAcquire(unsigned int milliseconds) {\n    return LockedPtr(this, std::chrono::milliseconds(milliseconds));\n  }\n\n  /**\n   * Attempts to acquire for a given number of milliseconds. If\n   * acquisition is unsuccessful, the returned ConstLockedPtr is nullptr.\n   *\n   * NOTE: This API is deprecated.  Use lock(), wlock(), or rlock() instead.\n   * In the future it will be marked with a deprecation attribute to emit\n   * build-time warnings, and then it will be removed entirely.\n   */\n  ConstLockedPtr timedAcquire(unsigned int milliseconds) const {\n    return ConstLockedPtr(this, std::chrono::milliseconds(milliseconds));\n  }\n\n  /**\n   * @brief Swap datum.\n   *\n   * Swaps with another Synchronized. Protected against\n   * self-swap. Only data is swapped. Locks are acquired in increasing\n   * address order.\n   */\n  void swap(Synchronized& rhs) {\n    if (this == &rhs) {\n      return;\n    }\n    if (this > &rhs) {\n      return rhs.swap(*this);\n    }\n    auto guard1 = LockedPtr{this};\n    auto guard2 = LockedPtr{&rhs};\n\n    using std::swap;\n    swap(datum_, rhs.datum_);\n  }\n\n  /**\n   * Swap with another datum. Recommended because it keeps the mutex\n   * held only briefly.\n   */\n  void swap(T& rhs) {\n    LockedPtr guard(this);\n\n    using std::swap;\n    swap(datum_, rhs);\n  }\n\n  /**\n   * @brief Exchange datum.\n   *\n   * Assign another datum and return the original value. Recommended\n   * because it keeps the mutex held only briefly.\n   */\n  T exchange(T&& rhs) {\n    swap(rhs);\n    return std::move(rhs);\n  }\n\n  /**\n   * Copies datum to a given target.\n   */\n  void copyInto(T& target) const {\n    ConstLockedPtr guard(this);\n    target = datum_;\n  }\n\n  /**\n   * Returns a fresh copy of the datum.\n   */\n  T copy() const {\n    ConstLockedPtr guard(this);\n    return datum_;\n  }\n\n  /**\n   * @brief Access datum without locking.\n   *\n   * Returns a reference to the datum without acquiring a lock.\n   *\n   * Provided as a backdoor for call-sites where it is known safe to be used.\n   * For example, when it is known that only one thread has access to the\n   * Synchronized instance.\n   *\n   * To be used with care - this method explicitly overrides the normal safety\n   * guarantees provided by the rest of the Synchronized API.\n   */\n  T& unsafeGetUnlocked() { return datum_; }\n  const T& unsafeGetUnlocked() const { return datum_; }\n\n  /**\n   * @brief Access underlying mutex_ directly.\n   *\n   * Provided as a backdoor for call-sites where the lock and unlock are paired\n   * in different calls. For example, in fork handlers. Use carefully as the\n   * caller is responsible to ensure it is paired with an unlock and there is\n   * nothing else in between that tries to implicitly or explicitly acquire the\n   * lock again.\n   */\n  Mutex& unsafeGetMutex() { return mutex_; }\n\n private:\n  template <class LockedType, class MutexType, class LockPolicy>\n  friend class folly::LockedPtrBase;\n  template <class LockedType, class LockPolicy>\n  friend class folly::LockedPtr;\n\n  /**\n   * Helper constructors to enable Synchronized for\n   * non-default constructible types T.\n   * Guards are created in actual public constructors and are alive\n   * for the time required to construct the object\n   */\n  Synchronized(\n      const Synchronized& rhs,\n      const ConstLockedPtr& /*guard*/) noexcept(nxCopyCtor)\n      : datum_(rhs.datum_) {}\n\n  Synchronized(Synchronized&& rhs, const LockedPtr& /*guard*/) noexcept(\n      nxMoveCtor)\n      : datum_(std::move(rhs.datum_)) {}\n\n  template <\n      typename... DatumArgs,\n      typename... MutexArgs,\n      std::size_t... IndicesOne,\n      std::size_t... IndicesTwo>\n  Synchronized(\n      std::piecewise_construct_t,\n      std::tuple<DatumArgs...> datumArgs,\n      std::tuple<MutexArgs...> mutexArgs,\n      std::index_sequence<IndicesOne...>,\n      std::index_sequence<IndicesTwo...>)\n      : datum_{std::get<IndicesOne>(std::move(datumArgs))...},\n        mutex_{std::get<IndicesTwo>(std::move(mutexArgs))...} {}\n\n  // simulacrum of data members - keep data members in sync!\n  // LockedPtr needs offsetof() which is specified only for standard-layout\n  // types which Synchronized is not so we define a simulacrum for offsetof\n  struct Simulacrum {\n    aligned_storage_for_t<DataType> datum_;\n    aligned_storage_for_t<MutexType> mutex_;\n  };\n\n  // data members - keep simulacrum of data members in sync!\n  T datum_;\n  mutable Mutex mutex_;\n};\n\n/**\n * Deprecated subclass of Synchronized that provides implicit locking\n * via operator->. This is intended to ease migration while preventing\n * accidental use of operator-> in new code.\n */\ntemplate <class T, class Mutex = SharedMutex>\nstruct [[deprecated(\n    \"use Synchronized and explicit lock(), wlock(), or rlock() instead\")]]\nImplicitSynchronized : Synchronized<T, Mutex> {\n private:\n  using Base = Synchronized<T, Mutex>;\n\n public:\n  using LockedPtr = typename Base::LockedPtr;\n  using ConstLockedPtr = typename Base::ConstLockedPtr;\n  using DataType = typename Base::DataType;\n  using MutexType = typename Base::MutexType;\n\n  using Base::Base;\n  using Base::operator=;\n\n  /**\n   * @brief Access the datum under lock.\n   *\n   * deprecated\n   *\n   * This accessor offers a LockedPtr. In turn, LockedPtr offers\n   * operator-> returning a pointer to T. The operator-> keeps\n   * expanding until it reaches a pointer, so syncobj->foo() will lock\n   * the object and call foo() against it.\n   *\n   * NOTE: This API is planned to be deprecated in an upcoming diff.\n   * Prefer using lock(), wlock(), or rlock() instead.\n   */\n  [[deprecated(\"use explicit lock(), wlock(), or rlock() instead\")]] LockedPtr\n  operator->() {\n    return LockedPtr(this);\n  }\n\n  /**\n   * deprecated\n   *\n   * Obtain a ConstLockedPtr.\n   *\n   * NOTE: This API is planned to be deprecated in an upcoming diff.\n   * Prefer using lock(), wlock(), or rlock() instead.\n   */\n  [[deprecated(\n      \"use explicit lock(), wlock(), or rlock() instead\")]] ConstLockedPtr\n  operator->() const {\n    return ConstLockedPtr(this);\n  }\n};\n\ntemplate <class SynchronizedType, class LockPolicy>\nclass ScopedUnlocker;\n\nnamespace detail {\n/*\n * A helper alias that resolves to \"const T\" if the template parameter\n * is a const Synchronized<T>, or \"T\" if the parameter is not const.\n */\ntemplate <class SynchronizedType, bool AllowsConcurrentAccess>\nusing SynchronizedDataType = typename std::conditional<\n    AllowsConcurrentAccess || std::is_const<SynchronizedType>::value,\n    typename SynchronizedType::DataType const,\n    typename SynchronizedType::DataType>::type;\n/*\n * A helper alias that resolves to a ConstLockedPtr if the template parameter\n * is a const Synchronized<T>, or a LockedPtr if the parameter is not const.\n */\ntemplate <class SynchronizedType>\nusing LockedPtrType = typename std::conditional<\n    std::is_const<SynchronizedType>::value,\n    typename SynchronizedType::ConstLockedPtr,\n    typename SynchronizedType::LockedPtr>::type;\n\ntemplate <\n    typename Synchronized,\n    typename LockFunc,\n    typename TryLockFunc,\n    typename... Args>\nclass SynchronizedLocker {\n public:\n  using LockedPtr = invoke_result_t<LockFunc&, Synchronized&, const Args&...>;\n\n  template <typename LockFuncType, typename TryLockFuncType, typename... As>\n  SynchronizedLocker(\n      Synchronized& sync,\n      LockFuncType&& lockFunc,\n      TryLockFuncType tryLockFunc,\n      As&&... as)\n      : synchronized{sync},\n        lockFunc_{std::forward<LockFuncType>(lockFunc)},\n        tryLockFunc_{std::forward<TryLockFuncType>(tryLockFunc)},\n        args_{std::forward<As>(as)...} {}\n\n  auto lock() const {\n    auto args = std::tuple<const Args&...>{args_};\n    return apply(lockFunc_, std::tuple_cat(std::tie(synchronized), args));\n  }\n  auto tryLock() const { return tryLockFunc_(synchronized); }\n\n private:\n  Synchronized& synchronized;\n  LockFunc lockFunc_;\n  TryLockFunc tryLockFunc_;\n  std::tuple<Args...> args_;\n};\n\ntemplate <\n    typename Synchronized,\n    typename LockFunc,\n    typename TryLockFunc,\n    typename... Args>\nauto makeSynchronizedLocker(\n    Synchronized& synchronized,\n    LockFunc&& lockFunc,\n    TryLockFunc&& tryLockFunc,\n    Args&&... args) {\n  using LockFuncType = std::decay_t<LockFunc>;\n  using TryLockFuncType = std::decay_t<TryLockFunc>;\n  return SynchronizedLocker<\n      Synchronized,\n      LockFuncType,\n      TryLockFuncType,\n      std::decay_t<Args>...>{\n      synchronized,\n      std::forward<LockFunc>(lockFunc),\n      std::forward<TryLockFunc>(tryLockFunc),\n      std::forward<Args>(args)...};\n}\n\n/**\n * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe\n * manner.\n *\n * The function uses the \"smart and polite\" algorithm from this link\n * http://howardhinnant.github.io/dining_philosophers.html#Polite\n *\n * The gist of the algorithm is that it locks a mutex, then tries to lock the\n * other mutexes in a non-blocking manner.  If all the locks succeed, we are\n * done, if not, we release the locks we have held, yield to allow other\n * threads to continue and then block on the mutex that we failed to acquire.\n *\n * This allows dynamically yielding ownership of all the mutexes but one, so\n * that other threads can continue doing work and locking the other mutexes.\n * See the benchmarks in folly/test/SynchronizedBenchmark.cpp for more.\n */\ntemplate <typename... SynchronizedLocker>\nauto lock(SynchronizedLocker... lockersIn)\n    -> std::tuple<typename SynchronizedLocker::LockedPtr...> {\n  // capture the list of lockers as a tuple\n  auto lockers = std::forward_as_tuple(lockersIn...);\n\n  // make a list of null LockedPtr instances that we will return to the caller\n  auto lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{};\n\n  // start by locking the first thing in the list\n  std::get<0>(lockedPtrs) = std::get<0>(lockers).lock();\n  auto indexLocked = 0;\n\n  while (true) {\n    auto couldLockAll = true;\n\n    for_each(lockers, [&](auto& locker, auto index) {\n      // if we should try_lock on the current locker then do so\n      if (index != indexLocked) {\n        auto lockedPtr = locker.tryLock();\n\n        // if we were unable to lock this mutex,\n        //\n        // 1. release all the locks,\n        // 2. yield control to another thread to be nice\n        // 3. block on the mutex we failed to lock, acquire the lock\n        // 4. break out and set the index of the current mutex to indicate\n        //    which mutex we have locked\n        if (!lockedPtr) {\n          // writing lockedPtrs = decltype(lockedPtrs){} does not compile on\n          // gcc, I believe this is a bug D7676798\n          lockedPtrs = std::tuple<typename SynchronizedLocker::LockedPtr...>{};\n\n          std::this_thread::yield();\n          fetch(lockedPtrs, index) = locker.lock();\n          indexLocked = index;\n          couldLockAll = false;\n\n          return loop_break;\n        }\n\n        // else store the locked mutex in the list we return\n        fetch(lockedPtrs, index) = std::move(lockedPtr);\n      }\n\n      return loop_continue;\n    });\n\n    if (couldLockAll) {\n      return lockedPtrs;\n    }\n  }\n}\n\ntemplate <typename Synchronized, typename... Args>\nauto wlock(Synchronized& synchronized, Args&&... args) {\n  return detail::makeSynchronizedLocker(\n      synchronized,\n      [](auto& s, auto&&... a) {\n        return s.wlock(std::forward<decltype(a)>(a)...);\n      },\n      [](auto& s) { return s.tryWLock(); },\n      std::forward<Args>(args)...);\n}\ntemplate <typename Synchronized, typename... Args>\nauto rlock(Synchronized& synchronized, Args&&... args) {\n  return detail::makeSynchronizedLocker(\n      synchronized,\n      [](auto& s, auto&&... a) {\n        return s.rlock(std::forward<decltype(a)>(a)...);\n      },\n      [](auto& s) { return s.tryRLock(); },\n      std::forward<Args>(args)...);\n}\ntemplate <typename Synchronized, typename... Args>\nauto ulock(Synchronized& synchronized, Args&&... args) {\n  return detail::makeSynchronizedLocker(\n      synchronized,\n      [](auto& s, auto&&... a) {\n        return s.ulock(std::forward<decltype(a)>(a)...);\n      },\n      [](auto& s) { return s.tryULock(); },\n      std::forward<Args>(args)...);\n}\ntemplate <typename Synchronized, typename... Args>\nauto lock(Synchronized& synchronized, Args&&... args) {\n  return detail::makeSynchronizedLocker(\n      synchronized,\n      [](auto& s, auto&&... a) {\n        return s.lock(std::forward<decltype(a)>(a)...);\n      },\n      [](auto& s) { return s.tryLock(); },\n      std::forward<Args>(args)...);\n}\n\n} // namespace detail\n\n/**\n * This class temporarily unlocks a LockedPtr in a scoped manner.\n */\ntemplate <class SynchronizedType, class LockPolicy>\nclass ScopedUnlocker {\n public:\n  explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p) noexcept\n      : ptr_(p), parent_(p->parent()) {\n    ptr_->releaseLock();\n  }\n  ScopedUnlocker(const ScopedUnlocker&) = delete;\n  ScopedUnlocker& operator=(const ScopedUnlocker&) = delete;\n  ScopedUnlocker(ScopedUnlocker&& other) noexcept\n      : ptr_(std::exchange(other.ptr_, nullptr)),\n        parent_(std::exchange(other.parent_, nullptr)) {}\n  ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete;\n\n  ~ScopedUnlocker() noexcept(false) {\n    if (ptr_) {\n      ptr_->reacquireLock(parent_);\n    }\n  }\n\n private:\n  LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr};\n  SynchronizedType* parent_{nullptr};\n};\n\n/**\n * A LockedPtr keeps a Synchronized<T> object locked for the duration of\n * LockedPtr's existence.\n *\n * It provides access the datum's members directly by using operator->() and\n * operator*().\n *\n * The LockPolicy parameter controls whether or not the lock is acquired in\n * exclusive or shared mode.\n */\ntemplate <class SynchronizedType, class LockPolicy>\nclass LockedPtr {\n private:\n  constexpr static bool AllowsConcurrentAccess =\n      LockPolicy::level != detail::SynchronizedMutexLevel::Unique;\n\n  using CDataType = // the DataType with the appropriate const-qualification\n      detail::SynchronizedDataType<SynchronizedType, AllowsConcurrentAccess>;\n\n  template <typename LockPolicyOther>\n  using EnableIfSameLevel =\n      std::enable_if_t<LockPolicy::level == LockPolicyOther::level>;\n\n  template <typename, typename>\n  friend class LockedPtr;\n\n  friend class ScopedUnlocker<SynchronizedType, LockPolicy>;\n\n public:\n  using DataType = typename SynchronizedType::DataType;\n  using MutexType = typename SynchronizedType::MutexType;\n  using Synchronized = typename std::remove_const<SynchronizedType>::type;\n  using LockType = detail::SynchronizedLockType<LockPolicy::level, MutexType>;\n\n  /**\n   * Creates an uninitialized LockedPtr.\n   *\n   * Dereferencing an uninitialized LockedPtr is not allowed.\n   */\n  LockedPtr() = default;\n\n  /**\n   * Takes a Synchronized<T> and locks it.\n   */\n  explicit LockedPtr(SynchronizedType* parent)\n      : lock_{!parent ? LockType{} : doLock(parent->mutex_)} {}\n\n  /**\n   * Takes a Synchronized<T> and attempts to lock it, within the specified\n   * timeout.\n   *\n   * Blocks until the lock is acquired or until the specified timeout expires.\n   * If the timeout expired without acquiring the lock, the LockedPtr will be\n   * null, and LockedPtr::isNull() will return true.\n   */\n  template <class Rep, class Period>\n  LockedPtr(\n      SynchronizedType* parent,\n      const std::chrono::duration<Rep, Period>& timeout)\n      : lock_{parent ? LockType{parent->mutex_, timeout} : LockType{}} {\n    if (isNull()) {\n      lock_ = {};\n    }\n  }\n\n  /**\n   * Move constructor.\n   */\n  LockedPtr(LockedPtr&& rhs) noexcept = default;\n  template <\n      typename Type = SynchronizedType,\n      std::enable_if_t<std::is_const<Type>::value, int> = 0>\n  /* implicit */ LockedPtr(LockedPtr<Synchronized, LockPolicy>&& rhs) noexcept\n      : lock_{std::move(rhs.lock_)} {}\n  template <\n      typename LockPolicyType,\n      EnableIfSameLevel<LockPolicyType>* = nullptr>\n  explicit LockedPtr(\n      LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept\n      : lock_{std::move(other.lock_)} {}\n  template <\n      typename Type = SynchronizedType,\n      typename LockPolicyType,\n      std::enable_if_t<std::is_const<Type>::value, int> = 0,\n      EnableIfSameLevel<LockPolicyType>* = nullptr>\n  explicit LockedPtr(LockedPtr<Synchronized, LockPolicyType>&& rhs) noexcept\n      : lock_{std::move(rhs.lock_)} {}\n\n  /**\n   * Move assignment operator.\n   */\n  LockedPtr& operator=(LockedPtr&& rhs) noexcept = default;\n  template <\n      typename LockPolicyType,\n      EnableIfSameLevel<LockPolicyType>* = nullptr>\n  LockedPtr& operator=(\n      LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept {\n    lock_ = std::move(other.lock_);\n    return *this;\n  }\n  template <\n      typename Type = SynchronizedType,\n      typename LockPolicyType,\n      std::enable_if_t<std::is_const<Type>::value, int> = 0,\n      EnableIfSameLevel<LockPolicyType>* = nullptr>\n  LockedPtr& operator=(\n      LockedPtr<Synchronized, LockPolicyType>&& other) noexcept {\n    lock_ = std::move(other.lock_);\n    return *this;\n  }\n\n  /*\n   * Copy constructor and assignment operator are deleted.\n   */\n  LockedPtr(const LockedPtr& rhs) = delete;\n  LockedPtr& operator=(const LockedPtr& rhs) = delete;\n\n  /**\n   * Destructor releases.\n   */\n  ~LockedPtr() = default;\n\n  /**\n   * Access the underlying lock object.\n   */\n  LockType& as_lock() noexcept { return lock_; }\n  LockType const& as_lock() const noexcept { return lock_; }\n\n  /**\n   * Check if this LockedPtr is uninitialized, or points to valid locked data.\n   *\n   * This method can be used to check if a timed-acquire operation succeeded.\n   * If an acquire operation times out it will result in a null LockedPtr.\n   *\n   * A LockedPtr is always either null, or holds a lock to valid data.\n   * Methods such as scopedUnlock() reset the LockedPtr to null for the\n   * duration of the unlock.\n   */\n  bool isNull() const { return !lock_.owns_lock(); }\n\n  /**\n   * Explicit boolean conversion.\n   *\n   * Returns !isNull()\n   */\n  explicit operator bool() const { return lock_.owns_lock(); }\n\n  /**\n   * Access the locked data.\n   *\n   * This method should only be used if the LockedPtr is valid.\n   */\n  CDataType* operator->() const { return std::addressof(parent()->datum_); }\n\n  /**\n   * Access the locked data.\n   *\n   * This method should only be used if the LockedPtr is valid.\n   */\n  CDataType& operator*() const { return parent()->datum_; }\n\n  void unlock() noexcept { lock_ = {}; }\n\n  /**\n   * Locks that allow concurrent access (shared, upgrade) force const\n   * access with the standard accessors even if the Synchronized\n   * object is non-const.\n   *\n   * In some cases non-const access can be needed, for example:\n   *\n   *   - Under an upgrade lock, to get references that will be mutated\n   *     after upgrading to a write lock.\n   *\n   *   - Under an read lock, if some mutating operations on the data\n   *     are thread safe (e.g. mutating the value in an associative\n   *     container with reference stability).\n   *\n   * asNonConstUnsafe() returns a non-const reference to the data if\n   * the parent Synchronized object was non-const at the point of lock\n   * acquisition.\n   */\n  template <typename = void>\n  DataType& asNonConstUnsafe() const {\n    static_assert(\n        AllowsConcurrentAccess && !std::is_const<SynchronizedType>::value,\n        \"asNonConstUnsafe() is only available on non-exclusive locks\"\n        \" acquired in a non-const context\");\n\n    return parent()->datum_;\n  }\n\n  /**\n   * Temporarily unlock the LockedPtr, and reset it to null.\n   *\n   * Returns an helper object that will re-lock and restore the LockedPtr when\n   * the helper is destroyed.  The LockedPtr may not be dereferenced for as\n   * long as this helper object exists.\n   */\n  ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() {\n    return ScopedUnlocker<SynchronizedType, LockPolicy>(this);\n  }\n\n  /***************************************************************************\n   * Upgrade lock methods.\n   * These are disabled via SFINAE when the mutex is not an upgrade mutex.\n   **************************************************************************/\n  /**\n   * Move the locked ptr from an upgrade state to an exclusive state.  The\n   * current lock is left in a null state.\n   */\n  template <\n      typename SyncType = SynchronizedType,\n      decltype(void(std::declval<typename SyncType::MutexType&>()\n                        .lock_upgrade()))* = nullptr>\n  LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyExclusive>\n  moveFromUpgradeToWrite() {\n    static_assert(std::is_same<SyncType, SynchronizedType>::value, \"mismatch\");\n    return transition_to_unique_lock(lock_);\n  }\n\n  /**\n   * Move the locked ptr from an exclusive state to an upgrade state.  The\n   * current lock is left in a null state.\n   */\n  template <\n      typename SyncType = SynchronizedType,\n      decltype(void(std::declval<typename SyncType::MutexType&>()\n                        .lock_upgrade()))* = nullptr>\n  LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyUpgrade>\n  moveFromWriteToUpgrade() {\n    static_assert(std::is_same<SyncType, SynchronizedType>::value, \"mismatch\");\n    return transition_to_upgrade_lock(lock_);\n  }\n\n  /**\n   * Move the locked ptr from an upgrade state to a shared state.  The\n   * current lock is left in a null state.\n   */\n  template <\n      typename SyncType = SynchronizedType,\n      decltype(void(std::declval<typename SyncType::MutexType&>()\n                        .lock_upgrade()))* = nullptr>\n  LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyShared>\n  moveFromUpgradeToRead() {\n    static_assert(std::is_same<SyncType, SynchronizedType>::value, \"mismatch\");\n    return transition_to_shared_lock(lock_);\n  }\n\n  /**\n   * Move the locked ptr from an exclusive state to a shared state.  The\n   * current lock is left in a null state.\n   */\n  template <\n      typename SyncType = SynchronizedType,\n      decltype(void(std::declval<typename SyncType::MutexType&>()\n                        .lock_shared()))* = nullptr>\n  LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyShared>\n  moveFromWriteToRead() {\n    static_assert(std::is_same<SyncType, SynchronizedType>::value, \"mismatch\");\n    return transition_to_shared_lock(lock_);\n  }\n\n  SynchronizedType* parent() const {\n    using simulacrum = typename SynchronizedType::Simulacrum;\n    static_assert(sizeof(simulacrum) == sizeof(SynchronizedType), \"mismatch\");\n    static_assert(alignof(simulacrum) == alignof(SynchronizedType), \"mismatch\");\n    auto off = offsetof(simulacrum, mutex_);\n    const auto raw = reinterpret_cast<char*>(lock_.mutex());\n    return reinterpret_cast<SynchronizedType*>(raw - (raw ? off : 0));\n  }\n\n private:\n  /* implicit */ LockedPtr(LockType lock) noexcept : lock_{std::move(lock)} {}\n\n  template <typename LP>\n  static constexpr bool is_try =\n      LP::method == detail::SynchronizedMutexMethod::TryLock;\n\n  template <\n      typename MT,\n      typename LT = LockType,\n      typename LP = LockPolicy,\n      std::enable_if_t<is_try<LP>, int> = 0>\n  FOLLY_ERASE static LT doLock(MT& mutex) {\n    return LT{mutex, std::try_to_lock};\n  }\n  template <\n      typename MT,\n      typename LT = LockType,\n      typename LP = LockPolicy,\n      std::enable_if_t<!is_try<LP>, int> = 0>\n  FOLLY_ERASE static LT doLock(MT& mutex) {\n    return LT{mutex};\n  }\n\n  void releaseLock() noexcept {\n    DCHECK(lock_.owns_lock());\n    lock_ = {};\n  }\n  void reacquireLock(SynchronizedType* parent) {\n    DCHECK(parent);\n    DCHECK(!lock_.owns_lock());\n    lock_ = doLock(parent->mutex_);\n  }\n\n  LockType lock_;\n};\n\n/**\n * Helper functions that should be passed to either a lock() or synchronized()\n * invocation, these return implementation defined structs that will be used\n * to lock the synchronized instance appropriately.\n *\n *    lock(wlock(one), rlock(two), wlock(three));\n *    synchronized([](auto one, two) { ... }, wlock(one), rlock(two));\n *\n * For example in the above rlock() produces an implementation defined read\n * locking helper instance and wlock() a write locking helper\n *\n * Subsequent arguments passed to these locking helpers, after the first, will\n * be passed by const-ref to the corresponding function on the synchronized\n * instance.  This means that if the function accepts these parameters by\n * value, they will be copied.  Note that it is not necessary that the primary\n * locking function will be invoked at all (for eg.  the implementation might\n * just invoke the try*Lock() method)\n *\n *    // Try to acquire the lock for one second\n *    synchronized([](auto) { ... }, wlock(one, 1s));\n *\n *    // The timed lock acquire might never actually be called, if it is not\n *    // needed by the underlying deadlock avoiding algorithm\n *    synchronized([](auto, auto) { ... }, rlock(one), wlock(two, 1s));\n *\n * Note that the arguments passed to to *lock() calls will be passed by\n * const-ref to the function invocation, as the implementation might use them\n * many times\n */\ntemplate <typename D, typename M, typename... Args>\nauto wlock(Synchronized<D, M>& synchronized, Args&&... args) {\n  return detail::wlock(synchronized, std::forward<Args>(args)...);\n}\ntemplate <typename D, typename M, typename... Args>\nauto wlock(const Synchronized<D, M>& synchronized, Args&&... args) {\n  return detail::wlock(synchronized, std::forward<Args>(args)...);\n}\ntemplate <typename Data, typename Mutex, typename... Args>\nauto rlock(const Synchronized<Data, Mutex>& synchronized, Args&&... args) {\n  return detail::rlock(synchronized, std::forward<Args>(args)...);\n}\ntemplate <typename D, typename M, typename... Args>\nauto ulock(Synchronized<D, M>& synchronized, Args&&... args) {\n  return detail::ulock(synchronized, std::forward<Args>(args)...);\n}\ntemplate <typename D, typename M, typename... Args>\nauto lock(Synchronized<D, M>& synchronized, Args&&... args) {\n  return detail::lock(synchronized, std::forward<Args>(args)...);\n}\ntemplate <typename D, typename M, typename... Args>\nauto lock(const Synchronized<D, M>& synchronized, Args&&... args) {\n  return detail::lock(synchronized, std::forward<Args>(args)...);\n}\n\n/**\n * Acquire locks for multiple Synchronized<> objects, in a deadlock-safe\n * manner.\n *\n * Wrap the synchronized instances with the appropriate locking strategy by\n * using one of the four strategies - folly::lock (exclusive acquire for\n * exclusive only mutexes), folly::rlock (shared acquire for shareable\n * mutexes), folly::wlock (exclusive acquire for shareable mutexes) or\n * folly::ulock (upgrade acquire for upgrade mutexes) (see above)\n *\n * The locks will be acquired and the passed callable will be invoked with the\n * LockedPtr instances in the order that they were passed to the function\n */\ntemplate <typename Func, typename... SynchronizedLockers>\ndecltype(auto) synchronized(Func&& func, SynchronizedLockers&&... lockers) {\n  return apply(\n      std::forward<Func>(func),\n      lock(std::forward<SynchronizedLockers>(lockers)...));\n}\n\n/**\n * Acquire locks on many lockables or synchronized instances in such a way\n * that the sequence of calls within the function does not cause deadlocks.\n *\n * This can often result in a performance boost as compared to simply\n * acquiring your locks in an ordered manner.  Even for very simple cases.\n * The algorithm tried to adjust to contention by blocking on the mutex it\n * thinks is the best fit, leaving all other mutexes open to be locked by\n * other threads.  See the benchmarks in folly/test/SynchronizedBenchmark.cpp\n * for more\n *\n * This works differently as compared to the locking algorithm in libstdc++\n * and is the recommended way to acquire mutexes in a generic order safe\n * manner.  Performance benchmarks show that this does better than the one in\n * libstdc++ even for the simple cases\n *\n * Usage is the same as std::lock() for arbitrary lockables\n *\n *    folly::lock(one, two, three);\n *\n * To make it work with folly::Synchronized you have to specify how you want\n * the locks to be acquired, use the folly::wlock(), folly::rlock(),\n * folly::ulock() and folly::lock() helpers defined below\n *\n *    auto [one, two] = lock(folly::wlock(a), folly::rlock(b));\n *\n * Note that you can/must avoid the folly:: namespace prefix on the lock()\n * function if you use the helpers, ADL lookup is done to find the lock function\n *\n * This will execute the deadlock avoidance algorithm and acquire a write lock\n * for a and a read lock for b\n */\ntemplate <typename LockableOne, typename LockableTwo, typename... Lockables>\nvoid lock(LockableOne& one, LockableTwo& two, Lockables&... lockables) {\n  auto locker = [](auto& lockable) {\n    using Lockable = std::remove_reference_t<decltype(lockable)>;\n    return detail::makeSynchronizedLocker(\n        lockable,\n        [](auto& l) { return std::unique_lock<Lockable>{l}; },\n        [](auto& l) {\n          return std::unique_lock<Lockable>{l, std::try_to_lock};\n        });\n  };\n  auto locks = lock(locker(one), locker(two), locker(lockables)...);\n\n  // release ownership of the locks from the RAII lock wrapper returned by the\n  // function above\n  for_each(locks, [&](auto& lock) { lock.release(); });\n}\n\n/**\n * Acquire locks for multiple Synchronized<T> objects, in a deadlock-safe\n * manner.\n *\n * The locks are acquired in order from lowest address to highest address.\n * (Note that this is not necessarily the same algorithm used by std::lock().)\n * For parameters that are const and support shared locks, a read lock is\n * acquired.  Otherwise an exclusive lock is acquired.\n *\n * use lock() with folly::wlock(), folly::rlock() and folly::ulock() for\n * arbitrary locking without causing a deadlock (as much as possible), with the\n * same effects as std::lock()\n */\ntemplate <class Sync1, class Sync2>\nstd::tuple<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>\nacquireLocked(Sync1& l1, Sync2& l2) {\n  if (static_cast<const void*>(&l1) < static_cast<const void*>(&l2)) {\n    auto p1 = l1.contextualLock();\n    auto p2 = l2.contextualLock();\n    return std::make_tuple(std::move(p1), std::move(p2));\n  } else {\n    auto p2 = l2.contextualLock();\n    auto p1 = l1.contextualLock();\n    return std::make_tuple(std::move(p1), std::move(p2));\n  }\n}\n\n/**\n * A version of acquireLocked() that returns a std::pair rather than a\n * std::tuple, which is easier to use in many places.\n */\ntemplate <class Sync1, class Sync2>\nstd::pair<detail::LockedPtrType<Sync1>, detail::LockedPtrType<Sync2>>\nacquireLockedPair(Sync1& l1, Sync2& l2) {\n  auto lockedPtrs = acquireLocked(l1, l2);\n  return {\n      std::move(std::get<0>(lockedPtrs)), std::move(std::get<1>(lockedPtrs))};\n}\n\n/************************************************************************\n * NOTE: All APIs below this line will be deprecated in upcoming diffs.\n ************************************************************************/\n\n// Non-member swap primitive\ntemplate <class T, class M>\nvoid swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {\n  lhs.swap(rhs);\n}\n\n/**\n * Disambiguate the name var by concatenating the line number of the original\n * point of expansion. This avoids shadowing warnings for nested\n * SYNCHRONIZEDs. The name is consistent if used multiple times within\n * another macro.\n * Only for internal use.\n */\n#define SYNCHRONIZED_VAR(var) FB_CONCATENATE(SYNCHRONIZED_##var##_, __LINE__)\n\nnamespace detail {\nstruct [[deprecated(\"use explicit lock(), wlock(), or rlock() instead\")]]\nSYNCHRONIZED_macro_is_deprecated {};\n} // namespace detail\n\n/**\n * NOTE: This API is deprecated.  Use lock(), wlock(), rlock() or the withLock\n * functions instead.  In the future it will be marked with a deprecation\n * attribute to emit build-time warnings, and then it will be removed entirely.\n *\n * SYNCHRONIZED is the main facility that makes Synchronized<T>\n * helpful. It is a pseudo-statement that introduces a scope where the\n * object is locked. Inside that scope you get to access the unadorned\n * datum.\n *\n * Example:\n *\n * Synchronized<vector<int>> svector;\n * ...\n * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }\n * or\n * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }\n *\n * Refer to folly/docs/Synchronized.md for a detailed explanation and more\n * examples.\n */\n#define SYNCHRONIZED(...)                                                 \\\n  FOLLY_PUSH_WARNING                                                      \\\n  FOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")                                   \\\n  FOLLY_MSVC_DISABLE_WARNING(4189) /* initialized but unreferenced */     \\\n  FOLLY_MSVC_DISABLE_WARNING(4456) /* declaration hides local */          \\\n  FOLLY_MSVC_DISABLE_WARNING(4457) /* declaration hides parameter */      \\\n  FOLLY_MSVC_DISABLE_WARNING(4458) /* declaration hides member */         \\\n  FOLLY_MSVC_DISABLE_WARNING(4459) /* declaration hides global */         \\\n  FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS                                   \\\n  if (bool SYNCHRONIZED_VAR(state) = false) {                             \\\n    (void)::folly::detail::SYNCHRONIZED_macro_is_deprecated{};            \\\n  } else                                                                  \\\n    for (auto SYNCHRONIZED_VAR(lockedPtr) =                               \\\n             (FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))).contextualLock(); \\\n         !SYNCHRONIZED_VAR(state);                                        \\\n         SYNCHRONIZED_VAR(state) = true)                                  \\\n      for ([[maybe_unused]] auto& FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)) =   \\\n               *SYNCHRONIZED_VAR(lockedPtr).operator->();                 \\\n           !SYNCHRONIZED_VAR(state);                                      \\\n           SYNCHRONIZED_VAR(state) = true)                                \\\n    FOLLY_POP_WARNING\n\n/**\n * NOTE: This API is deprecated.  Use lock(), wlock(), rlock() or the withLock\n * functions instead.  In the future it will be marked with a deprecation\n * attribute to emit build-time warnings, and then it will be removed entirely.\n *\n * Similar to SYNCHRONIZED, but only uses a read lock.\n */\n#define SYNCHRONIZED_CONST(...)            \\\n  SYNCHRONIZED(                            \\\n      FB_VA_GLUE(FB_ARG_1, (__VA_ARGS__)), \\\n      std::as_const(FB_VA_GLUE(FB_ARG_2_OR_1, (__VA_ARGS__))))\n\n/**\n * NOTE: This API is deprecated.  Use lock(), wlock(), rlock() or the withLock\n * functions instead.  In the future it will be marked with a deprecation\n * attribute to emit build-time warnings, and then it will be removed entirely.\n *\n * Synchronizes two Synchronized objects (they may encapsulate\n * different data). Synchronization is done in increasing address of\n * object order, so there is no deadlock risk.\n */\n#define SYNCHRONIZED_DUAL(n1, e1, n2, e2)                                      \\\n  if (bool SYNCHRONIZED_VAR(state) = false) {                                  \\\n    (void)::folly::detail::SYNCHRONIZED_macro_is_deprecated{};                 \\\n  } else                                                                       \\\n    for (auto SYNCHRONIZED_VAR(ptrs) = acquireLockedPair(e1, e2);              \\\n         !SYNCHRONIZED_VAR(state);                                             \\\n         SYNCHRONIZED_VAR(state) = true)                                       \\\n      for (auto& n1 = *SYNCHRONIZED_VAR(ptrs).first; !SYNCHRONIZED_VAR(state); \\\n           SYNCHRONIZED_VAR(state) = true)                                     \\\n        for (auto& n2 = *SYNCHRONIZED_VAR(ptrs).second;                        \\\n             !SYNCHRONIZED_VAR(state);                                         \\\n             SYNCHRONIZED_VAR(state) = true)\n\n} /* namespace folly */\n"
  },
  {
    "path": "folly/SynchronizedPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Synchronized.h>\n\n/* `SynchronizedPtr` is a variation on the `Synchronized` idea that's useful for\n * some cases where you want to protect a pointed-to object (or an object within\n * some pointer-like wrapper). If you would otherwise need to use\n * `Synchronized<smart_ptr<Synchronized<T>>>` consider using\n * `SynchronizedPtr<smart_ptr<T>>`as it is a bit easier to use and it works when\n * you want the `T` object at runtime to actually a subclass of `T`.\n *\n * You can access the contained `T` with `.rlock()`, and `.wlock()`, and the\n * pointer or pointer-like wrapper with `.wlockPointer()`. The corresponding\n * `with...` methods take a callback, invoke it with a `T const&`, `T&` or\n * `smart_ptr<T>&` respectively, and return the callback's result.\n */\nnamespace folly {\ntemplate <typename LockHolder, typename Element>\nstruct SynchronizedPtrLockedElement {\n  explicit SynchronizedPtrLockedElement(LockHolder&& holder)\n      : holder_(std::move(holder)) {}\n\n  Element& operator*() const { return **holder_; }\n\n  Element* operator->() const { return &**holder_; }\n\n  explicit operator bool() const { return static_cast<bool>(*holder_); }\n\n private:\n  LockHolder holder_;\n};\n\ntemplate <typename PointerType, typename MutexType = SharedMutex>\nclass SynchronizedPtr {\n  using inner_type = Synchronized<PointerType, MutexType>;\n  inner_type inner_;\n\n public:\n  using pointer_type = PointerType;\n  using element_type = typename std::pointer_traits<pointer_type>::element_type;\n  using const_element_type = typename std::add_const<element_type>::type;\n  using read_locked_element = SynchronizedPtrLockedElement<\n      typename inner_type::ConstLockedPtr,\n      const_element_type>;\n  using write_locked_element = SynchronizedPtrLockedElement<\n      typename inner_type::LockedPtr,\n      element_type>;\n  using write_locked_pointer = typename inner_type::LockedPtr;\n\n  template <typename... Args>\n  explicit SynchronizedPtr(Args... args)\n      : inner_(std::forward<Args>(args)...) {}\n\n  SynchronizedPtr() = default;\n  SynchronizedPtr(SynchronizedPtr const&) = default;\n  SynchronizedPtr(SynchronizedPtr&&) = default;\n  SynchronizedPtr& operator=(SynchronizedPtr const&) = default;\n  SynchronizedPtr& operator=(SynchronizedPtr&&) = default;\n\n  // Methods to provide appropriately locked and const-qualified access to the\n  // element.\n\n  read_locked_element rlock() const {\n    return read_locked_element(inner_.rlock());\n  }\n\n  template <class Function>\n  auto withRLock(Function&& function) const {\n    return function(*rlock());\n  }\n\n  write_locked_element wlock() { return write_locked_element(inner_.wlock()); }\n\n  template <class Function>\n  auto withWLock(Function&& function) {\n    return function(*wlock());\n  }\n\n  // Methods to provide write-locked access to the pointer. We deliberately make\n  // it difficult to get a read-locked pointer because that provides read-locked\n  // non-const access to the element, and the purpose of this class is to\n  // discourage that.\n  write_locked_pointer wlockPointer() { return inner_.wlock(); }\n\n  template <class Function>\n  auto withWLockPointer(Function&& function) {\n    return function(*wlockPointer());\n  }\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/ThreadCachedInt.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Higher performance (up to 10x) atomic increment using thread caching.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/Likely.h>\n#include <folly/ThreadLocal.h>\n\nnamespace folly {\n\n// Note that readFull requires holding a lock and iterating through all of the\n// thread local objects with the same Tag, so if you have a lot of\n// ThreadCachedInt's you should considering breaking up the Tag space even\n// further.\ntemplate <class IntT, class Tag = IntT>\nclass ThreadCachedInt {\n  struct IntCache;\n\n public:\n  explicit ThreadCachedInt(IntT initialVal = 0, uint32_t cacheSize = 1000)\n      : target_(initialVal), cacheSize_(cacheSize) {}\n\n  ThreadCachedInt(const ThreadCachedInt&) = delete;\n  ThreadCachedInt& operator=(const ThreadCachedInt&) = delete;\n\n  void increment(IntT inc) {\n    auto cache = cache_.get();\n    if (FOLLY_UNLIKELY(cache == nullptr)) {\n      cache = new IntCache(*this);\n      cache_.reset(cache);\n    }\n    cache->increment(inc);\n  }\n\n  // Quickly grabs the current value which may not include some cached\n  // increments.\n  IntT readFast() const { return target_.load(std::memory_order_relaxed); }\n\n  // Reads the current value plus all the cached increments.  Requires grabbing\n  // a lock, so this is significantly slower than readFast().\n  IntT readFull() const {\n    // This could race with thread destruction and so the access lock should be\n    // acquired before reading the current value\n    const auto accessor = cache_.accessAllThreads();\n    IntT ret = readFast();\n    for (const auto& cache : accessor) {\n      if (!cache.reset_.load(std::memory_order_acquire)) {\n        ret += cache.val_.load(std::memory_order_relaxed);\n      }\n    }\n    return ret;\n  }\n\n  // Quickly reads and resets current value (doesn't reset cached increments).\n  IntT readFastAndReset() {\n    return target_.exchange(0, std::memory_order_release);\n  }\n\n  // This function is designed for accumulating into another counter, where you\n  // only want to count each increment once.  It can still get the count a\n  // little off, however, but it should be much better than calling readFull()\n  // and set(0) sequentially.\n  IntT readFullAndReset() {\n    // This could race with thread destruction and so the access lock should be\n    // acquired before reading the current value\n    auto accessor = cache_.accessAllThreads();\n    IntT ret = readFastAndReset();\n    for (auto& cache : accessor) {\n      if (!cache.reset_.load(std::memory_order_acquire)) {\n        ret += cache.val_.load(std::memory_order_relaxed);\n        cache.reset_.store(true, std::memory_order_release);\n      }\n    }\n    return ret;\n  }\n\n  void setCacheSize(uint32_t newSize) {\n    cacheSize_.store(newSize, std::memory_order_release);\n  }\n\n  uint32_t getCacheSize() const { return cacheSize_.load(); }\n\n  ThreadCachedInt& operator+=(IntT inc) {\n    increment(inc);\n    return *this;\n  }\n  ThreadCachedInt& operator-=(IntT inc) {\n    increment(-inc);\n    return *this;\n  }\n  // pre-increment (we don't support post-increment)\n  ThreadCachedInt& operator++() {\n    increment(1);\n    return *this;\n  }\n  ThreadCachedInt& operator--() {\n    increment(IntT(-1));\n    return *this;\n  }\n\n  // Thread-safe set function.\n  // This is a best effort implementation. In some edge cases, there could be\n  // data loss (missing counts)\n  void set(IntT newVal) {\n    for (auto& cache : cache_.accessAllThreads()) {\n      cache.reset_.store(true, std::memory_order_release);\n    }\n    target_.store(newVal, std::memory_order_release);\n  }\n\n private:\n  std::atomic<IntT> target_;\n  std::atomic<uint32_t> cacheSize_;\n  ThreadLocalPtr<IntCache, Tag, AccessModeStrict>\n      cache_; // Must be last for dtor ordering\n\n  // This should only ever be modified by one thread\n  struct IntCache {\n    ThreadCachedInt* parent_;\n    mutable std::atomic<IntT> val_;\n    mutable uint32_t numUpdates_;\n    std::atomic<bool> reset_;\n\n    explicit IntCache(ThreadCachedInt& parent)\n        : parent_(&parent), val_(0), numUpdates_(0), reset_(false) {}\n\n    void increment(IntT inc) {\n      if (FOLLY_LIKELY(!reset_.load(std::memory_order_acquire))) {\n        // This thread is the only writer to val_, so it's fine do do\n        // a relaxed load and do the addition non-atomically.\n        val_.store(\n            val_.load(std::memory_order_relaxed) + inc,\n            std::memory_order_release);\n      } else {\n        val_.store(inc, std::memory_order_relaxed);\n        reset_.store(false, std::memory_order_release);\n      }\n      ++numUpdates_;\n      if (FOLLY_UNLIKELY(\n              numUpdates_ >\n              parent_->cacheSize_.load(std::memory_order_acquire))) {\n        flush();\n      }\n    }\n\n    void flush() const {\n      parent_->target_.fetch_add(val_, std::memory_order_release);\n      val_.store(0, std::memory_order_release);\n      numUpdates_ = 0;\n    }\n\n    ~IntCache() { flush(); }\n  };\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/ThreadLocal.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Improved thread local storage for non-trivial types (similar speed as\n * pthread_getspecific but only consumes a single pthread_key_t, and 4x faster\n * than boost::thread_specific_ptr).\n *\n * ThreadLocal objects can be grouped together logically under a tag. Within\n * a tag, each object has a unique id. The combination of tag and id is used to\n * locate the managed object corresponding to the current thread.\n *\n * Also includes an accessor interface to iterate all of the managed\n * objects owned by a ThreadLocal object, each corresponding to a\n * separate thread.  accessAllThreads() initializes an accessor\n * which holds\n * a lock *that blocks all creation and destruction of managed\n * objects managed by the ThreadLocal. The accessor can be used\n * as an iterable container. Note: for now, the accessor also happens to hold\n * other per tag global locks and hence calls to accessAllThreads() are\n * serialized at tag level.\n *\n * accessAllThreads() can race with destruction of thread-local elements. We\n * provide a strict mode which is dangerous because it requires the access lock\n * to be held while destroying thread-local elements which could cause\n * deadlocks. We gate this mode behind the AccessModeStrict template parameter.\n *\n * Intended use is for frequent write, infrequent read data access patterns such\n * as counters.\n *\n * There are two classes here - ThreadLocal and ThreadLocalPtr.  ThreadLocalPtr\n * has semantics similar to boost::thread_specific_ptr. ThreadLocal is a thin\n * wrapper around ThreadLocalPtr that manages allocation automatically.\n */\n\n#pragma once\n\n#include <iterator>\n#include <thread>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/SharedMutex.h>\n#include <folly/detail/ThreadLocalDetail.h>\n\nnamespace folly {\n\ntemplate <class T, class Tag, class AccessMode>\nclass ThreadLocalPtr;\n\ntemplate <class T, class Tag = void, class AccessMode = void>\nclass ThreadLocal {\n public:\n  constexpr ThreadLocal() noexcept : constructor_([]() { return T(); }) {}\n\n  template <typename F, std::enable_if_t<is_invocable_r_v<T, F>, int> = 0>\n  explicit ThreadLocal(F&& constructor)\n      : constructor_(std::forward<F>(constructor)) {}\n\n  ThreadLocal(ThreadLocal&& that) noexcept\n      : tlp_{std::move(that.tlp_)},\n        constructor_{std::exchange(that.constructor_, {})} {}\n\n  ThreadLocal& operator=(ThreadLocal&& that) noexcept {\n    assert(this != &that);\n    tlp_ = std::exchange(that.tlp_, {});\n    constructor_ = std::exchange(that.constructor_, {});\n    return *this;\n  }\n\n  FOLLY_ERASE T* get() const {\n    auto const ptr = tlp_.get();\n    return FOLLY_LIKELY(!!ptr) ? ptr : makeTlp();\n  }\n\n  // may return null\n  FOLLY_ERASE T* get_existing() const { return tlp_.get(); }\n\n  T* operator->() const { return get(); }\n\n  T& operator*() const { return *get(); }\n\n  void reset(T* newPtr = nullptr) { tlp_.reset(newPtr); }\n\n  using Accessor = typename ThreadLocalPtr<T, Tag, AccessMode>::Accessor;\n  Accessor accessAllThreads() const { return tlp_.accessAllThreads(); }\n\n private:\n  // non-copyable\n  ThreadLocal(const ThreadLocal&) = delete;\n  ThreadLocal& operator=(const ThreadLocal&) = delete;\n\n  FOLLY_NOINLINE T* makeTlp() const {\n    auto const ptr = new T(constructor_());\n    tlp_.reset(ptr);\n    return ptr;\n  }\n\n  mutable ThreadLocalPtr<T, Tag, AccessMode> tlp_;\n  std::function<T()> constructor_;\n};\n\n/*\n * The idea here is that __thread is faster than pthread_getspecific, so we\n * keep a __thread array of pointers to objects (ThreadEntry::elements) where\n * each array has an index for each unique instance of the ThreadLocalPtr\n * object.  Each ThreadLocalPtr object has a unique id that is an index into\n * these arrays so we can fetch the correct object from thread local storage\n * very efficiently.\n *\n * In order to prevent unbounded growth of the id space and thus huge\n * ThreadEntry::elements, arrays, for example due to continuous creation and\n * destruction of ThreadLocalPtr objects, we keep a set of all active\n * instances.  When an instance is destroyed we remove it from the active\n * set and insert the id into freeIds_ for reuse.  These operations require a\n * global mutex, but only happen at construction and destruction time.\n *\n * We use a single global pthread_key_t per Tag to manage object destruction and\n * memory cleanup upon thread exit because there is a finite number of\n * pthread_key_t's available per machine.\n *\n * NOTE: Apple platforms don't support the same semantics for __thread that\n *       Linux does (and it's only supported at all on i386). For these, use\n *       pthread_setspecific()/pthread_getspecific() for the per-thread\n *       storage.  Windows (MSVC and GCC) does support the same semantics\n *       with __declspec(thread)\n */\n\ntemplate <class T, class Tag = void, class AccessMode = void>\nclass ThreadLocalPtr {\n private:\n  using StaticMeta = threadlocal_detail::StaticMeta<Tag, AccessMode>;\n\n  using AccessAllThreadsEnabled = Negation<std::is_same<Tag, void>>;\n\n public:\n  constexpr ThreadLocalPtr() noexcept : id_() {}\n\n  ThreadLocalPtr(ThreadLocalPtr&& other) noexcept : id_(std::move(other.id_)) {}\n\n  ThreadLocalPtr& operator=(ThreadLocalPtr&& other) noexcept {\n    assert(this != &other);\n    destroy(); // user-provided dtors invoked within here must not throw\n    id_ = std::move(other.id_);\n    return *this;\n  }\n\n  ~ThreadLocalPtr() { destroy(); }\n\n  T* get() const {\n    threadlocal_detail::ElementWrapper& w = StaticMeta::get(&id_);\n    return static_cast<T*>(w.ptr);\n  }\n\n  T* operator->() const { return get(); }\n\n  T& operator*() const { return *get(); }\n\n  T* release() {\n    auto rlocked = getForkGuard();\n    threadlocal_detail::ThreadEntry* te = StaticMeta::getThreadEntry(&id_);\n    auto id = id_.getOrInvalid();\n    // Only valid index into the elements array\n    DCHECK_NE(id, threadlocal_detail::kEntryIDInvalid);\n    return static_cast<T*>(te->releaseElement(id));\n  }\n\n  void reset(T* newPtr = nullptr) {\n    auto rlocked = getForkGuard();\n    auto guard = makeGuard([&] { delete newPtr; });\n    threadlocal_detail::ThreadEntry* te = StaticMeta::getThreadEntry(&id_);\n    uint32_t id = id_.getOrInvalid();\n    // Only valid index into the elements array\n    DCHECK_NE(id, threadlocal_detail::kEntryIDInvalid);\n    te->resetElement(newPtr, id);\n    guard.dismiss();\n  }\n\n  explicit operator bool() const { return get() != nullptr; }\n\n  /**\n   * reset() that transfers ownership from a smart pointer\n   */\n  template <\n      typename SourceT,\n      typename Deleter,\n      typename = typename std::enable_if<\n          std::is_convertible<SourceT*, T*>::value>::type>\n  void reset(std::unique_ptr<SourceT, Deleter> source) {\n    auto deleter =\n        [delegate = source.get_deleter()](T* ptr, TLPDestructionMode) {\n          delegate(ptr);\n        };\n    reset(source.release(), deleter);\n  }\n\n  /**\n   * reset() that transfers ownership from a smart pointer with the default\n   * deleter\n   */\n  template <\n      typename SourceT,\n      typename = typename std::enable_if<\n          std::is_convertible<SourceT*, T*>::value>::type>\n  void reset(std::unique_ptr<SourceT> source) {\n    reset(source.release());\n  }\n\n  /**\n   * reset() with a custom deleter:\n   * deleter(T* ptr, TLPDestructionMode mode)\n   * \"mode\" is ALL_THREADS if we're destructing this ThreadLocalPtr (and thus\n   * deleting pointers for all threads), and THIS_THREAD if we're only deleting\n   * the member for one thread (because of thread exit or reset()).\n   * Invoking the deleter must not throw.\n   */\n  template <class Deleter>\n  void reset(T* newPtr, const Deleter& deleter) {\n    auto guard = makeGuard([&] {\n      if (newPtr) {\n        deleter(newPtr, TLPDestructionMode::THIS_THREAD);\n      }\n    });\n\n    auto rlocked = getForkGuard();\n    threadlocal_detail::ThreadEntry* te = StaticMeta::getThreadEntry(&id_);\n    uint32_t id = id_.getOrInvalid();\n    // Only valid index into the elements array\n    DCHECK_NE(id, threadlocal_detail::kEntryIDInvalid);\n    te->resetElement(newPtr, deleter, id);\n    guard.dismiss();\n  }\n\n  void reset(const std::shared_ptr<T>& newPtr) {\n    reset(newPtr.get(), threadlocal_detail::SharedPtrDeleter{newPtr});\n  }\n\n  // Holds a global lock for iteration through all thread local child objects.\n  // Can be used as an iterable container.\n  // Use accessAllThreads() to obtain one.\n  class Accessor {\n    friend class ThreadLocalPtr<T, Tag, AccessMode>;\n\n    threadlocal_detail::StaticMetaBase& meta_ =\n        threadlocal_detail::StaticMeta<Tag, AccessMode>::instance();\n    std::unique_lock<SharedMutex> accessAllThreadsLock_;\n    std::shared_lock<SharedMutex> forkHandlerLock_;\n    uint32_t id_ = 0;\n\n    // Prevent the entry set from changing while we are iterating over it.\n    // reset() calls to populate will acquire shared lock on the id's set.\n    threadlocal_detail::StaticMetaBase::SynchronizedThreadEntrySet::WLockedPtr\n        wlockedThreadEntrySet_;\n\n   public:\n    class Iterator;\n    friend class Iterator;\n\n    // The iterators obtained from Accessor are bidirectional iterators.\n    class Iterator {\n      friend class Accessor;\n      const Accessor* accessor_{nullptr};\n      using InnerVector = threadlocal_detail::ThreadEntrySet::ElementVector;\n      using InnerIterator = InnerVector::iterator;\n\n      InnerVector& vec_;\n      InnerIterator iter_;\n\n      void increment() {\n        if (iter_ != vec_.end()) {\n          ++iter_;\n          incrementToValid();\n        }\n      }\n\n      void decrement() {\n        if (iter_ != vec_.begin()) {\n          --iter_;\n          decrementToValid();\n        }\n      }\n\n      const T& dereference() const {\n        return *static_cast<T*>(iter_->wrapper.ptr);\n      }\n\n      T& dereference() { return *static_cast<T*>(iter_->wrapper.ptr); }\n\n      bool equal(const Iterator& other) const {\n        return (accessor_->id_ == other.accessor_->id_ && iter_ == other.iter_);\n      }\n\n      void setToEnd() { iter_ = vec_.end(); }\n\n      explicit Iterator(const Accessor* accessor, bool toEnd = false)\n          : accessor_(accessor),\n            vec_(accessor_->wlockedThreadEntrySet_->threadElements),\n            iter_(vec_.begin()) {\n        if (toEnd) {\n          setToEnd();\n        } else {\n          incrementToValid();\n        }\n      }\n\n      // we just need to check the ptr since it can be set to nullptr\n      // even if the entry is part of the list\n      bool valid() const { return (iter_ != vec_.end() && iter_->wrapper.ptr); }\n\n      void incrementToValid() {\n        for (; iter_ != vec_.end() && !valid(); ++iter_) {\n        }\n      }\n\n      void decrementToValid() {\n        for (; iter_ != vec_.begin() && !valid(); --iter_) {\n        }\n      }\n\n     public:\n      using difference_type = ssize_t;\n      using value_type = T;\n      using reference = T const&;\n      using pointer = T const*;\n      using iterator_category = std::bidirectional_iterator_tag;\n\n      Iterator() = default;\n\n      Iterator& operator++() {\n        increment();\n        return *this;\n      }\n\n      Iterator& operator++(int) {\n        Iterator copy(*this);\n        increment();\n        return copy;\n      }\n\n      Iterator& operator--() {\n        decrement();\n        return *this;\n      }\n\n      Iterator& operator--(int) {\n        Iterator copy(*this);\n        decrement();\n        return copy;\n      }\n\n      T& operator*() { return dereference(); }\n\n      T const& operator*() const { return dereference(); }\n\n      T* operator->() { return &dereference(); }\n\n      T const* operator->() const { return &dereference(); }\n\n      bool operator==(Iterator const& rhs) const { return equal(rhs); }\n\n      bool operator!=(Iterator const& rhs) const { return !equal(rhs); }\n\n      std::thread::id getThreadId() const { return iter_->threadEntry->tid(); }\n\n      uint64_t getOSThreadId() const { return iter_->threadEntry->tid_os; }\n    };\n\n    ~Accessor() { release(); }\n\n    Iterator begin() const { return Iterator(this); }\n\n    Iterator end() const { return Iterator(this, true); }\n\n    Accessor(const Accessor&) = delete;\n    Accessor& operator=(const Accessor&) = delete;\n\n    Accessor(Accessor&& other) noexcept\n        : meta_(other.meta_),\n          accessAllThreadsLock_(std::move(other.accessAllThreadsLock_)),\n          forkHandlerLock_(std::move(other.forkHandlerLock_)),\n          id_(std::exchange(other.id_, 0)) {\n      wlockedThreadEntrySet_ = std::move(other.wlockedThreadEntrySet_);\n    }\n\n    Accessor& operator=(Accessor&& other) noexcept {\n      // Each Tag has its own unique meta, and accessors with different Tags\n      // have different types.  So either *this is empty, or this and other\n      // have the same tag.  But if they have the same tag, they have the same\n      // meta (and lock), so they'd both hold the lock at the same time,\n      // which is impossible, which leaves only one possible scenario --\n      // *this is empty.  Assert it.\n      assert(&meta_ == &other.meta_);\n      using std::swap;\n      swap(accessAllThreadsLock_, other.accessAllThreadsLock_);\n      swap(forkHandlerLock_, other.forkHandlerLock_);\n      swap(id_, other.id_);\n      wlockedThreadEntrySet_.unlock();\n      swap(wlockedThreadEntrySet_, other.wlockedThreadEntrySet_);\n    }\n\n    Accessor() = default;\n\n   private:\n    explicit Accessor(uint32_t id)\n        : accessAllThreadsLock_(meta_.accessAllThreadsLock_, std::defer_lock),\n          forkHandlerLock_(meta_.forkHandlerLock_, std::defer_lock),\n          id_(id) {\n      forkHandlerLock_.lock();\n      accessAllThreadsLock_.lock();\n      wlockedThreadEntrySet_ = meta_.allId2ThreadEntrySets_[id_].wlock();\n    }\n\n    void release() {\n      if (accessAllThreadsLock_) {\n        wlockedThreadEntrySet_.unlock();\n        accessAllThreadsLock_.unlock();\n        DCHECK(forkHandlerLock_);\n        forkHandlerLock_.unlock();\n        id_ = 0;\n      }\n    }\n  };\n\n  // accessor allows a client to iterate through all thread local child\n  // elements of this ThreadLocal instance.  Holds a global lock for each <Tag>\n  Accessor accessAllThreads() const {\n    static_assert(\n        AccessAllThreadsEnabled::value,\n        \"Must use a unique Tag to use the accessAllThreads feature\");\n    return Accessor(id_.getOrAllocate(StaticMeta::instance()));\n  }\n\n private:\n  void destroy() noexcept {\n    auto const val = id_.value.load(std::memory_order_relaxed);\n    if (val == threadlocal_detail::kEntryIDInvalid) {\n      return;\n    }\n    StaticMeta::instance().destroy(&id_);\n    // User provided destructors should not cause the TL to have its id\n    // reallocated.\n    DCHECK(\n        id_.value.load(std::memory_order_relaxed) ==\n        threadlocal_detail::kEntryIDInvalid);\n  }\n\n  // non-copyable\n  ThreadLocalPtr(const ThreadLocalPtr&) = delete;\n  ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete;\n\n  static auto getForkGuard() {\n    auto& mutex = StaticMeta::instance().forkHandlerLock_;\n    return std::shared_lock{mutex};\n  }\n\n  mutable typename StaticMeta::EntryID id_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/TimeoutQueue.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/TimeoutQueue.h>\n\n#include <algorithm>\n#include <limits>\n#include <vector>\n\nnamespace folly {\n\nTimeoutQueue::Id TimeoutQueue::add(\n    int64_t now, int64_t delay, Callback callback) {\n  Id id = nextId_++;\n  timeouts_.insert({id, now + delay, -1, std::move(callback)});\n  return id;\n}\n\nTimeoutQueue::Id TimeoutQueue::addRepeating(\n    int64_t now, int64_t interval, Callback callback) {\n  Id id = nextId_++;\n  timeouts_.insert({id, now + interval, interval, std::move(callback)});\n  return id;\n}\n\nint64_t TimeoutQueue::nextExpiration() const {\n  return (\n      timeouts_.empty()\n          ? std::numeric_limits<int64_t>::max()\n          : timeouts_.get<BY_EXPIRATION>().begin()->expiration);\n}\n\nbool TimeoutQueue::erase(Id id) {\n  return timeouts_.get<BY_ID>().erase(id);\n}\n\nint64_t TimeoutQueue::runInternal(int64_t now, bool onceOnly) {\n  auto& byExpiration = timeouts_.get<BY_EXPIRATION>();\n  int64_t nextExp;\n  do {\n    const auto end = byExpiration.upper_bound(now);\n    std::vector<Event> expired;\n    std::move(byExpiration.begin(), end, std::back_inserter(expired));\n    byExpiration.erase(byExpiration.begin(), end);\n    for (const auto& event : expired) {\n      // Reinsert if repeating, do this before executing callbacks\n      // so the callbacks have a chance to call erase\n      if (event.repeatInterval >= 0) {\n        timeouts_.insert(\n            {event.id,\n             now + event.repeatInterval,\n             event.repeatInterval,\n             event.callback});\n      }\n    }\n\n    // Call callbacks\n    for (const auto& event : expired) {\n      event.callback(event.id, now);\n    }\n    nextExp = nextExpiration();\n  } while (!onceOnly && nextExp <= now);\n  return nextExp;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/TimeoutQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Simple timeout queue.  Call user-specified callbacks when their timeouts\n * expire.\n *\n * This class assumes that \"time\" is an int64_t and doesn't care about time\n * units (seconds, milliseconds, etc).  You call runOnce() / runLoop() using\n * the same time units that you use to specify callbacks.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <functional>\n\n#include <boost/multi_index/indexed_by.hpp>\n#include <boost/multi_index/member.hpp>\n#include <boost/multi_index/ordered_index.hpp>\n#include <boost/multi_index_container.hpp>\n\nnamespace folly {\n\nclass TimeoutQueue {\n public:\n  typedef int64_t Id;\n  typedef std::function<void(Id, int64_t)> Callback;\n\n  TimeoutQueue() : nextId_(1) {}\n\n  /**\n   * Add a one-time timeout event that will fire \"delay\" time units from \"now\"\n   * (that is, the first time that run*() is called with a time value >= now\n   * + delay).\n   */\n  Id add(int64_t now, int64_t delay, Callback callback);\n\n  /**\n   * Add a repeating timeout event that will fire every \"interval\" time units\n   * (it will first fire when run*() is called with a time value >=\n   * now + interval).\n   *\n   * run*() will always invoke each repeating event at most once, even if\n   * more than one \"interval\" period has passed.\n   */\n  Id addRepeating(int64_t now, int64_t interval, Callback callback);\n\n  /**\n   * Erase a given timeout event, returns true if the event was actually\n   * erased and false if it didn't exist in our queue.\n   */\n  bool erase(Id id);\n\n  /**\n   * Process all events that are due at times <= \"now\" by calling their\n   * callbacks.\n   *\n   * Callbacks are allowed to call back into the queue and add / erase events;\n   * they might create more events that are already due.  In this case,\n   * runOnce() will only go through the queue once, and return a \"next\n   * expiration\" time in the past or present (<= now); runLoop()\n   * will process the queue again, until there are no events already due.\n   *\n   * Note that it is then possible for runLoop to never return if\n   * callbacks re-add themselves to the queue (or if you have repeating\n   * callbacks with an interval of 0).\n   *\n   * Return the time that the next event will be due (same as\n   * nextExpiration(), below)\n   */\n  int64_t runOnce(int64_t now) { return runInternal(now, true); }\n  int64_t runLoop(int64_t now) { return runInternal(now, false); }\n\n  /**\n   * Return the time that the next event will be due.\n   */\n  int64_t nextExpiration() const;\n\n private:\n  int64_t runInternal(int64_t now, bool onceOnly);\n  TimeoutQueue(const TimeoutQueue&) = delete;\n  TimeoutQueue& operator=(const TimeoutQueue&) = delete;\n\n  struct Event {\n    Id id;\n    int64_t expiration;\n    int64_t repeatInterval;\n    Callback callback;\n  };\n\n  typedef boost::multi_index_container<\n      Event,\n      boost::multi_index::indexed_by<\n          boost::multi_index::ordered_unique<\n              boost::multi_index::member<Event, Id, &Event::id>>,\n          boost::multi_index::ordered_non_unique<\n              boost::multi_index::member<Event, int64_t, &Event::expiration>>>>\n      Set;\n\n  enum {\n    BY_ID = 0,\n    BY_EXPIRATION = 1,\n  };\n\n  Set timeouts_;\n  Id nextId_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/TokenBucket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <chrono>\n#include <thread>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Likely.h>\n#include <folly/Optional.h>\n#include <folly/concurrency/CacheLocality.h>\n\nnamespace folly {\n\nstruct TokenBucketPolicyDefault {\n  using align =\n      std::integral_constant<size_t, hardware_destructive_interference_size>;\n\n  template <typename T>\n  using atom = std::atomic<T>;\n\n  using clock = std::chrono::steady_clock;\n\n  using concurrent = std::true_type;\n};\n\n/**\n * Thread-safe (atomic) token bucket primitive.\n *\n * This primitive can be used to implement a token bucket\n * (http://en.wikipedia.org/wiki/Token_bucket).  It handles\n * the storage of the state in an atomic way, and presents\n * an interface dealing with tokens, rate, burstSize and time.\n *\n * This primitive records the last time it was updated. This allows the\n * token bucket to add tokens \"just in time\" when tokens are requested.\n *\n * @tparam Policy A policy.\n */\ntemplate <typename Policy = TokenBucketPolicyDefault>\nclass TokenBucketStorage {\n  template <typename T>\n  using Atom = typename Policy::template atom<T>;\n  using Align = typename Policy::align;\n  using Clock = typename Policy::clock; // do we need clock here?\n  using Concurrent = typename Policy::concurrent;\n\n  static_assert(Clock::is_steady, \"clock must be steady\"); // do we need clock?\n\n public:\n  /**\n   * Constructor.\n   *\n   * @param zeroTime Initial time at which to consider the token bucket\n   *                 starting to fill. Defaults to 0, so by default token\n   *                 buckets are \"empty\" after construction.\n   */\n  explicit TokenBucketStorage(double zeroTime = 0) noexcept\n      : zeroTime_(zeroTime) {}\n\n  /**\n   * Copy constructor.\n   *\n   * Thread-safe. (Copy constructors of derived classes may not be thread-safe\n   * however.)\n   */\n  TokenBucketStorage(const TokenBucketStorage& other) noexcept\n      : zeroTime_(other.zeroTime_.load(std::memory_order_relaxed)) {}\n\n  /**\n   * Copy-assignment operator.\n   *\n   * Warning: not thread safe for the object being assigned to (including\n   * self-assignment). Thread-safe for the other object.\n   */\n  TokenBucketStorage& operator=(const TokenBucketStorage& other) noexcept {\n    zeroTime_.store(other.zeroTime(), std::memory_order_relaxed);\n    return *this;\n  }\n\n  /**\n   * Re-initialize token bucket.\n   *\n   * Thread-safe.\n   *\n   * @param zeroTime Initial time at which to consider the token bucket\n   *                 starting to fill. Defaults to 0, so by default token\n   *                 bucket is reset to \"empty\".\n   */\n  void reset(double zeroTime = 0) noexcept {\n    zeroTime_.store(zeroTime, std::memory_order_relaxed);\n  }\n\n  /**\n   * Returns the token balance at specified time (negative if bucket in debt).\n   *\n   * Thread-safe (but returned value may immediately be outdated).\n   */\n  double balance(\n      double rate, double burstSize, double nowInSeconds) const noexcept {\n    assert(rate > 0);\n    assert(burstSize > 0);\n    double zt = this->zeroTime_.load(std::memory_order_relaxed);\n    return std::min((nowInSeconds - zt) * rate, burstSize);\n  }\n\n  /**\n   * Consume tokens at the given rate/burst/time.\n   *\n   * Consumption is actually done by the callback function: it's given a\n   * reference with the number of available tokens and returns the number\n   * consumed.  Typically the return value would be between 0.0 and available,\n   * but there are no restrictions.\n   *\n   * Note: the callback may be called multiple times, so please no side-effects\n   */\n  template <typename Callback>\n  double consume(\n      double rate,\n      double burstSize,\n      double nowInSeconds,\n      const Callback& callback) {\n    assert(rate > 0);\n    assert(burstSize > 0);\n\n    double zeroTimeOld;\n    double zeroTimeNew;\n    double consumed;\n    do {\n      zeroTimeOld = zeroTime();\n      double tokens = std::min((nowInSeconds - zeroTimeOld) * rate, burstSize);\n      consumed = callback(tokens);\n      double tokensNew = tokens - consumed;\n      if (consumed == 0.0) {\n        return consumed;\n      }\n\n      zeroTimeNew = nowInSeconds - tokensNew / rate;\n    } while (FOLLY_UNLIKELY(\n        !compare_exchange_weak_relaxed(zeroTime_, zeroTimeOld, zeroTimeNew)));\n\n    return consumed;\n  }\n\n  /**\n   * returns the time at which the bucket will have `target` tokens available.\n   *\n   * Caution: it doesn't make sense to ask about target > burstSize\n   *\n   * Eg.\n   *     // time debt repaid\n   *     bucket.timeWhenBucket(rate, 0);\n   *\n   *     // time bucket is full\n   *     bucket.timeWhenBucket(rate, burstSize);\n   */\n\n  double timeWhenBucket(double rate, double target) {\n    return zeroTime() + target / rate;\n  }\n\n  /**\n   * Return extra tokens back to the bucket.\n   *\n   * Thread-safe.\n   */\n  void returnTokens(double tokensToReturn, double rate) {\n    assert(rate > 0);\n\n    returnTokensImpl(tokensToReturn, rate);\n  }\n\n private:\n  /**\n   * Adjust zeroTime based on rate and tokenCount and return the new value of\n   * zeroTime_. Note: Token count can be negative to move the zeroTime_\n   * into the future.\n   */\n  double returnTokensImpl(double tokenCount, double rate) {\n    auto zeroTimeOld = zeroTime_.load(std::memory_order_relaxed);\n\n    double zeroTimeNew;\n    do {\n      zeroTimeNew = zeroTimeOld - tokenCount / rate;\n\n    } while (FOLLY_UNLIKELY(\n        !compare_exchange_weak_relaxed(zeroTime_, zeroTimeOld, zeroTimeNew)));\n    return zeroTimeNew;\n  }\n\n  static bool compare_exchange_weak_relaxed(\n\n      Atom<double>& atom, double& expected, double zeroTime) {\n    if (Concurrent::value) {\n      return atom.compare_exchange_weak(\n          expected, zeroTime, std::memory_order_relaxed);\n    } else {\n      return atom.store(zeroTime, std::memory_order_relaxed), true;\n    }\n  }\n\n  double zeroTime() const {\n    return this->zeroTime_.load(std::memory_order_relaxed);\n  }\n\n  static constexpr size_t AlignZeroTime =\n      constexpr_max(Align::value, alignof(Atom<double>));\n  alignas(AlignZeroTime) Atom<double> zeroTime_;\n};\n\n/**\n * Thread-safe (atomic) token bucket implementation.\n *\n * A token bucket (http://en.wikipedia.org/wiki/Token_bucket) models a stream\n * of events with an average rate and some amount of burstiness. The canonical\n * example is a packet switched network: the network can accept some number of\n * bytes per second and the bytes come in finite packets (bursts). A token\n * bucket stores up to a fixed number of tokens (the burst size). Some number\n * of tokens are removed when an event occurs. The tokens are replenished at a\n * fixed rate. Failure to allocate tokens implies resource is unavailable and\n * caller needs to implement its own retry mechanism. For simple cases where\n * caller is okay with a FIFO starvation-free scheduling behavior, there are\n * also APIs to 'borrow' from the future effectively assigning a start time to\n * the caller when it should proceed with using the resource. It is also\n * possible to 'return' previously allocated tokens to make them available to\n * other users. Returns in excess of burstSize are considered expired and\n * will not be available to later callers.\n *\n * This implementation records the last time it was updated. This allows the\n * token bucket to add tokens \"just in time\" when tokens are requested.\n *\n * The \"dynamic\" base variant allows the token generation rate and maximum\n * burst size to change with every token consumption.\n *\n * @tparam Policy A policy.\n */\ntemplate <typename Policy = TokenBucketPolicyDefault>\nclass BasicDynamicTokenBucket {\n  template <typename T>\n  using Atom = typename Policy::template atom<T>;\n  using Align = typename Policy::align;\n  using Clock = typename Policy::clock;\n  using Concurrent = typename Policy::concurrent;\n\n  static_assert(Clock::is_steady, \"clock must be steady\");\n\n public:\n  /**\n   * Constructor.\n   *\n   * @param zeroTime Initial time at which to consider the token bucket\n   *                 starting to fill. Defaults to 0, so by default token\n   *                 buckets are \"empty\" after construction.\n   */\n  explicit BasicDynamicTokenBucket(double zeroTime = 0) noexcept\n      : bucket_(zeroTime) {}\n\n  /**\n   * Copy constructor and copy assignment operator.\n   *\n   * Thread-safe. (Copy constructors of derived classes may not be thread-safe\n   * however.)\n   */\n  BasicDynamicTokenBucket(const BasicDynamicTokenBucket& other) noexcept =\n      default;\n  BasicDynamicTokenBucket& operator=(\n      const BasicDynamicTokenBucket& other) noexcept = default;\n\n  /**\n   * Re-initialize token bucket.\n   *\n   * Thread-safe.\n   *\n   * @param zeroTime Initial time at which to consider the token bucket\n   *                 starting to fill. Defaults to 0, so by default token\n   *                 bucket is reset to \"empty\".\n   */\n  void reset(double zeroTime = 0) noexcept { bucket_.reset(zeroTime); }\n\n  /**\n   * Returns the current time in seconds since Epoch.\n   */\n  static double defaultClockNow() noexcept {\n    auto const now = Clock::now().time_since_epoch();\n    return std::chrono::duration<double>(now).count();\n  }\n\n  /**\n   * Attempts to consume some number of tokens. Tokens are first added to the\n   * bucket based on the time elapsed since the last attempt to consume tokens.\n   * Note: Attempts to consume more tokens than the burst size will always\n   * fail.\n   *\n   * Thread-safe.\n   *\n   * @param toConsume The number of tokens to consume.\n   * @param rate Number of tokens to generate per second.\n   * @param burstSize Maximum burst size. Must be greater than 0.\n   * @param nowInSeconds Current time in seconds. Should be monotonically\n   *                     increasing from the nowInSeconds specified in\n   *                     this token bucket's constructor.\n   * @return True if the rate limit check passed, false otherwise.\n   */\n  bool consume(\n      double toConsume,\n      double rate,\n      double burstSize,\n      double nowInSeconds = defaultClockNow()) {\n    assert(rate > 0);\n    assert(burstSize > 0);\n\n    if (bucket_.balance(rate, burstSize, nowInSeconds) < 0.0) {\n      return 0;\n    }\n\n    double consumed = bucket_.consume(\n        rate, burstSize, nowInSeconds, [toConsume](double available) {\n          return available < toConsume ? 0.0 : toConsume;\n        });\n\n    assert(consumed == toConsume || consumed == 0.0);\n    return consumed == toConsume;\n  }\n\n  /**\n   * Similar to consume, but always consumes some number of tokens.  If the\n   * bucket contains enough tokens - consumes toConsume tokens.  Otherwise the\n   * bucket is drained.\n   *\n   * Thread-safe.\n   *\n   * @param toConsume The number of tokens to consume.\n   * @param rate Number of tokens to generate per second.\n   * @param burstSize Maximum burst size. Must be greater than 0.\n   * @param nowInSeconds Current time in seconds. Should be monotonically\n   *                     increasing from the nowInSeconds specified in\n   *                     this token bucket's constructor.\n   * @return number of tokens that were consumed.\n   */\n  double consumeOrDrain(\n      double toConsume,\n      double rate,\n      double burstSize,\n      double nowInSeconds = defaultClockNow()) {\n    assert(rate > 0);\n    assert(burstSize > 0);\n\n    if (bucket_.balance(rate, burstSize, nowInSeconds) <= 0.0) {\n      return 0;\n    }\n\n    double consumed = bucket_.consume(\n        rate, burstSize, nowInSeconds, [toConsume](double available) {\n          return constexpr_min(available, toConsume);\n        });\n    return consumed;\n  }\n\n  /**\n   * Return extra tokens back to the bucket.\n   *\n   * Thread-safe.\n   */\n  void returnTokens(double tokensToReturn, double rate) {\n    assert(rate > 0);\n    assert(tokensToReturn > 0);\n\n    bucket_.returnTokens(tokensToReturn, rate);\n  }\n\n  /**\n   * Like consumeOrDrain but the call will always satisfy the asked for count.\n   * It does so by borrowing tokens from the future if the currently available\n   * count isn't sufficient.\n   *\n   * Returns a folly::Optional<double>. The optional wont be set if the request\n   * cannot be satisfied: only case is when it is larger than burstSize. The\n   * value of the optional is a double indicating the time in seconds that the\n   * caller needs to wait at which the reservation becomes valid. The caller\n   * could simply sleep for the returned duration to smooth out the allocation\n   * to match the rate limiter or do some other computation in the meantime. In\n   * any case, any regular consume or consumeOrDrain calls will fail to allocate\n   * any tokens until the future time is reached.\n   *\n   * Note: It is assumed the caller will not ask for a very large count nor use\n   * it immediately (if not waiting inline) as that would break the burst\n   * prevention the limiter is meant to be used for.\n   *\n   * Thread-safe.\n   */\n  Optional<double> consumeWithBorrowNonBlocking(\n      double toConsume,\n      double rate,\n      double burstSize,\n      double nowInSeconds = defaultClockNow()) {\n    assert(rate > 0);\n    assert(burstSize > 0);\n\n    if (burstSize < toConsume) {\n      return folly::none;\n    }\n\n    while (toConsume > 0) {\n      double consumed =\n          consumeOrDrain(toConsume, rate, burstSize, nowInSeconds);\n      if (consumed > 0) {\n        toConsume -= consumed;\n      } else {\n        bucket_.returnTokens(-toConsume, rate);\n        double debtPaid = bucket_.timeWhenBucket(rate, 0);\n        double napTime = std::max(0.0, debtPaid - nowInSeconds);\n        return napTime;\n      }\n    }\n    return 0;\n  }\n\n  /**\n   * Convenience wrapper around non-blocking borrow to sleep inline until\n   * reservation is valid.\n   */\n  bool consumeWithBorrowAndWait(\n      double toConsume,\n      double rate,\n      double burstSize,\n      double nowInSeconds = defaultClockNow()) {\n    auto res =\n        consumeWithBorrowNonBlocking(toConsume, rate, burstSize, nowInSeconds);\n    if (res.value_or(0) > 0) {\n      const auto napUSec = static_cast<int64_t>(res.value() * 1000000);\n      std::this_thread::sleep_for(std::chrono::microseconds(napUSec));\n    }\n    return res.has_value();\n  }\n\n  /**\n   * Returns the tokens available at specified time (zero if in debt).\n   *\n   * Use balance() to get the balance of tokens.\n   *\n   * Thread-safe (but returned value may immediately be outdated).\n   */\n  double available(\n      double rate,\n      double burstSize,\n      double nowInSeconds = defaultClockNow()) const noexcept {\n    return std::max(0.0, balance(rate, burstSize, nowInSeconds));\n  }\n\n  /**\n   * Returns the token balance at specified time (negative if bucket in debt).\n   *\n   * Thread-safe (but returned value may immediately be outdated).\n   */\n  double balance(\n      double rate,\n      double burstSize,\n      double nowInSeconds = defaultClockNow()) const noexcept {\n    return bucket_.balance(rate, burstSize, nowInSeconds);\n  }\n\n private:\n  TokenBucketStorage<Policy> bucket_;\n};\n\n/**\n * Specialization of BasicDynamicTokenBucket with a fixed token\n * generation rate and a fixed maximum burst size.\n */\ntemplate <typename Policy = TokenBucketPolicyDefault>\nclass BasicTokenBucket {\n private:\n  using Impl = BasicDynamicTokenBucket<Policy>;\n\n public:\n  /**\n   * Construct a token bucket with a specific maximum rate and burst size.\n   *\n   * @param genRate Number of tokens to generate per second.\n   * @param burstSize Maximum burst size. Must be greater than 0.\n   * @param zeroTime Initial time at which to consider the token bucket\n   *                 starting to fill. Defaults to 0, so by default token\n   *                 bucket is \"empty\" after construction.\n   */\n  BasicTokenBucket(\n      double genRate, double burstSize, double zeroTime = 0) noexcept\n      : tokenBucket_(zeroTime), rate_(genRate), burstSize_(burstSize) {\n    assert(rate_ > 0);\n    assert(burstSize_ > 0);\n  }\n\n  /**\n   * Copy constructor.\n   *\n   * Warning: not thread safe!\n   */\n  BasicTokenBucket(const BasicTokenBucket& other) noexcept = default;\n\n  /**\n   * Copy-assignment operator.\n   *\n   * Warning: not thread safe!\n   */\n  BasicTokenBucket& operator=(const BasicTokenBucket& other) noexcept = default;\n\n  /**\n   * Returns the current time in seconds since Epoch.\n   */\n  static double defaultClockNow() noexcept(noexcept(Impl::defaultClockNow())) {\n    return Impl::defaultClockNow();\n  }\n\n  /**\n   * Change rate and burst size.\n   *\n   * Warning: not thread safe!\n   *\n   * @param genRate Number of tokens to generate per second.\n   * @param burstSize Maximum burst size. Must be greater than 0.\n   * @param nowInSeconds Current time in seconds. Should be monotonically\n   *                     increasing from the nowInSeconds specified in\n   *                     this token bucket's constructor.\n   */\n  void reset(\n      double genRate,\n      double burstSize,\n      double nowInSeconds = defaultClockNow()) noexcept {\n    assert(genRate > 0);\n    assert(burstSize > 0);\n    const double availTokens = available(nowInSeconds);\n    rate_ = genRate;\n    burstSize_ = burstSize;\n    setCapacity(availTokens, nowInSeconds);\n  }\n\n  /**\n   * Change number of tokens in bucket.\n   *\n   * Warning: not thread safe!\n   *\n   * @param tokens Desired number of tokens in bucket after the call.\n   * @param nowInSeconds Current time in seconds. Should be monotonically\n   *                     increasing from the nowInSeconds specified in\n   *                     this token bucket's constructor.\n   */\n  void setCapacity(double tokens, double nowInSeconds) noexcept {\n    tokenBucket_.reset(nowInSeconds - tokens / rate_);\n  }\n\n  /**\n   * Attempts to consume some number of tokens. Tokens are first added to the\n   * bucket based on the time elapsed since the last attempt to consume tokens.\n   * Note: Attempts to consume more tokens than the burst size will always\n   * fail.\n   *\n   * Thread-safe.\n   *\n   * @param toConsume The number of tokens to consume.\n   * @param nowInSeconds Current time in seconds. Should be monotonically\n   *                     increasing from the nowInSeconds specified in\n   *                     this token bucket's constructor.\n   * @return True if the rate limit check passed, false otherwise.\n   */\n  bool consume(double toConsume, double nowInSeconds = defaultClockNow()) {\n    return tokenBucket_.consume(toConsume, rate_, burstSize_, nowInSeconds);\n  }\n\n  /**\n   * Similar to consume, but always consumes some number of tokens.  If the\n   * bucket contains enough tokens - consumes toConsume tokens.  Otherwise the\n   * bucket is drained.\n   *\n   * Thread-safe.\n   *\n   * @param toConsume The number of tokens to consume.\n   * @param nowInSeconds Current time in seconds. Should be monotonically\n   *                     increasing from the nowInSeconds specified in\n   *                     this token bucket's constructor.\n   * @return number of tokens that were consumed.\n   */\n  double consumeOrDrain(\n      double toConsume, double nowInSeconds = defaultClockNow()) {\n    return tokenBucket_.consumeOrDrain(\n        toConsume, rate_, burstSize_, nowInSeconds);\n  }\n\n  /**\n   * Returns extra token back to the bucket.  Cannot be negative.\n   * For negative tokens, setCapacity() can be used\n   */\n  void returnTokens(double tokensToReturn) {\n    return tokenBucket_.returnTokens(tokensToReturn, rate_);\n  }\n\n  /**\n   * Reserve tokens and return time to wait for in order for the reservation to\n   * be compatible with the bucket configuration.\n   */\n  Optional<double> consumeWithBorrowNonBlocking(\n      double toConsume, double nowInSeconds = defaultClockNow()) {\n    return tokenBucket_.consumeWithBorrowNonBlocking(\n        toConsume, rate_, burstSize_, nowInSeconds);\n  }\n\n  /**\n   * Reserve tokens. Blocks if need be until reservation is satisfied.\n   */\n  bool consumeWithBorrowAndWait(\n      double toConsume, double nowInSeconds = defaultClockNow()) {\n    return tokenBucket_.consumeWithBorrowAndWait(\n        toConsume, rate_, burstSize_, nowInSeconds);\n  }\n\n  /**\n   * Returns the tokens available at specified time (zero if in debt).\n   *\n   * Use balance() to get the balance of tokens.\n   *\n   * Thread-safe (but returned value may immediately be outdated).\n   */\n  double available(double nowInSeconds = defaultClockNow()) const noexcept {\n    return std::max(0.0, balance(nowInSeconds));\n  }\n\n  /**\n   * Returns the token balance at specified time (negative if bucket in debt).\n   *\n   * Thread-safe (but returned value may immediately be outdated).\n   */\n  double balance(double nowInSeconds = defaultClockNow()) const noexcept {\n    return tokenBucket_.balance(rate_, burstSize_, nowInSeconds);\n  }\n\n  /**\n   * Returns the number of tokens generated per second.\n   *\n   * Thread-safe (but returned value may immediately be outdated).\n   */\n  double rate() const noexcept { return rate_; }\n\n  /**\n   * Returns the maximum burst size.\n   *\n   * Thread-safe (but returned value may immediately be outdated).\n   */\n  double burst() const noexcept { return burstSize_; }\n\n private:\n  Impl tokenBucket_;\n  double rate_;\n  double burstSize_;\n};\n\nusing TokenBucket = BasicTokenBucket<>;\nusing DynamicTokenBucket = BasicDynamicTokenBucket<>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <limits>\n#include <memory>\n#include <tuple>\n#include <type_traits>\n\n#include <folly/Portability.h>\n\nnamespace folly {\n\n#if defined(__cpp_lib_type_identity) && __cpp_lib_type_identity >= 201806L\n\nusing std::type_identity;\nusing std::type_identity_t;\n\n#else\n\n/// type_identity_t\n/// type_identity\n///\n/// mimic: std::type_identity_t, std::type_identity, c++20\ntemplate <typename T>\nstruct type_identity {\n  using type = T;\n};\ntemplate <typename T>\nusing type_identity_t = typename type_identity<T>::type;\n\n#endif\n\n/// tag_t\n/// tag\n///\n/// A generic type-list value type and value.\n///\n/// A type-list is a class template parameterized by a pack of types.\ntemplate <typename...>\nstruct tag_t {};\ntemplate <typename... T>\ninline constexpr tag_t<T...> tag{};\n\n/// vtag_t\n/// vtag\n///\n/// A generic value-list value type and value.\n///\n/// A value-list is a class template parameterized by a pack of values.\ntemplate <auto...>\nstruct vtag_t {};\ntemplate <auto... V>\ninline constexpr vtag_t<V...> vtag{};\n\ntemplate <std::size_t I>\nusing index_constant = std::integral_constant<std::size_t, I>;\n\nnamespace detail {\n\ntemplate <typename Int>\nconstexpr Int parse_uic(char const* str) noexcept {\n  Int result = 0;\n  while (*str) {\n    auto const c = *str++;\n    if (c >= '0' && c <= '9') {\n      result = result * 10 + (c - '0');\n    }\n  }\n  return result;\n}\n\n} // namespace detail\n\ninline namespace literals {\ninline namespace integral_constant_literals {\n\n/// operator\"\"_uzic\n///\n/// Evaluates {XYZ}_uzic as index_constant<{XYZ}>.\n///\n/// mimic: operator\"\"_uzic, p2725r0\ntemplate <char... Digits>\nconstexpr auto operator\"\"_uzic() noexcept {\n  constexpr char digits[] = {Digits..., '\\0'};\n  return index_constant<detail::parse_uic<size_t>(digits)>{};\n}\n\n} // namespace integral_constant_literals\n} // namespace literals\n\n/// always_false\n///\n/// A variable template that is always false but requires template arguments to\n/// be provided (which are then ignored). This is useful in very specific cases\n/// where we want type-dependent expressions to defer static_assert's.\n///\n/// A common use-case is for exhaustive constexpr if branches:\n///\n///   template <typename T>\n///   void foo(T value) {\n///     if constexpr (std::is_integral_v<T>) foo_integral(value);\n///     else if constexpr (std::is_same_v<T, std::string>) foo_string(value);\n///     else static_assert(always_false<T>, \"Unsupported type\");\n///   }\n///\n/// If we had used static_assert(false), then this would always fail to compile,\n/// even if foo is never instantiated!\n///\n/// Another use case is if a template that is expected to always be specialized\n/// is erroneously instantiated with the base template.\n///\n///   template <typename T>\n///   struct Foo {\n///     static_assert(always_false<T>, \"Unsupported type\");\n///   };\n///   template <>\n///   struct Foo<int> {};\n///\n///   Foo<int> a;         // fine\n///   Foo<std::string> b; // fails! And you get a nice (custom) error message\n///\n/// This is similar to leaving the base template undefined but we get a nicer\n/// compiler error message with static_assert.\ntemplate <typename...>\ninline constexpr bool always_false = false;\n\nnamespace detail {\n\ntemplate <typename Void, typename T>\nstruct require_sizeof_ {\n  static_assert(\n      always_false<T>,\n      \"application of sizeof fails substitution - most commonly, the type is incomplete\");\n};\ntemplate <typename T>\nstruct require_sizeof_<decltype(void(sizeof(T))), T> {\n  template <typename V>\n  using apply_t = V;\n\n  static constexpr std::size_t size = sizeof(T);\n};\n\n} // namespace detail\n\n/// require_sizeof\n///\n/// Equivalent to sizeof, but with a static_assert enforcing that application of\n/// sizeof would not fail substitution.\n///\n/// Application of sizeof fails on the following kinds of types:\n/// * function types.\n/// * incomplete types, including possibly-cv-qualified void\n/// * references to types to which application of sizeof would fail\ntemplate <typename T>\nconstexpr std::size_t require_sizeof = detail::require_sizeof_<void, T>::size;\n\n/// is_complete\n/// is_complete_v\n///\n/// It is tempting to define is_complete and is_complete_v, but ultimately these\n/// would be a bad idea. These traits are defined here to witness that these are\n/// intentionally excluded and not merely a missing feature.\ntemplate <typename T>\nstruct is_complete {\n  static_assert(always_false<T>, \"is_complete would break ODR\");\n};\ntemplate <typename T>\nconstexpr auto is_complete_v = is_complete<T>::value;\n\n/// is_unbounded_array_v\n/// is_unbounded_array\n///\n/// A trait variable and type to check if a given type is an unbounded array.\n///\n/// mimic: std::is_unbounded_array_d, std::is_unbounded_array (C++20)\ntemplate <typename T>\ninline constexpr bool is_unbounded_array_v = false;\ntemplate <typename T>\ninline constexpr bool is_unbounded_array_v<T[]> = true;\ntemplate <typename T>\nstruct is_unbounded_array : std::bool_constant<is_unbounded_array_v<T>> {};\n\n/// is_bounded_array_v\n/// is_bounded_array\n///\n/// A trait variable and type to check if a given type is a bounded array.\n///\n/// mimic: std::is_bounded_array_d, std::is_bounded_array (C++20)\ntemplate <typename T>\ninline constexpr bool is_bounded_array_v = false;\ntemplate <typename T, std::size_t S>\ninline constexpr bool is_bounded_array_v<T[S]> = true;\ntemplate <typename T>\nstruct is_bounded_array : std::bool_constant<is_bounded_array_v<T>> {};\n\n/// is_instantiation_of_v\n/// is_instantiation_of\n/// instantiated_from\n/// uncvref_instantiated_from\n///\n/// A trait variable and type to check if a given type is an instantiation of a\n/// class template. And corresponding concepts.\n///\n/// Note that this only works with type template parameters. It does not work\n/// with non-type template parameters, template template parameters, or alias\n/// templates.\ntemplate <template <typename...> class, typename>\ninline constexpr bool is_instantiation_of_v = false;\ntemplate <template <typename...> class C, typename... T>\ninline constexpr bool is_instantiation_of_v<C, C<T...>> = true;\ntemplate <template <typename...> class C, typename... T>\nstruct is_instantiation_of\n    : std::bool_constant<is_instantiation_of_v<C, T...>> {};\n\n#if defined(__cpp_concepts)\n\ntemplate <typename T, template <typename...> class Templ>\nconcept instantiated_from = is_instantiation_of_v<Templ, T>;\n\ntemplate <typename T, template <typename...> class Templ>\nconcept uncvref_instantiated_from =\n    is_instantiation_of_v<Templ, std::remove_cvref_t<T>>;\n\n#endif\n\n/// member_pointer_traits\n///\n/// For a member-pointer, reveals its constituent member-type and object-type.\n///\n/// Works for both member-object-pointer and member-function-pointer.\ntemplate <typename>\nstruct member_pointer_traits;\ntemplate <typename M, typename O>\nstruct member_pointer_traits<M O::*> {\n  using member_type = M;\n  using object_type = O;\n};\n\n/// member_pointer_member_t\n///\n/// The member-type of a pointer-to-member type.\ntemplate <typename P>\nusing member_pointer_member_t = typename member_pointer_traits<P>::member_type;\n\n/// member_pointer_object_t\n///\n/// The object-type of a pointer-to-member type.\ntemplate <typename P>\nusing member_pointer_object_t = typename member_pointer_traits<P>::object_type;\n\nnamespace detail {\n\nstruct is_constexpr_default_constructible_ {\n  template <typename T>\n  static constexpr auto make(tag_t<T>) -> decltype(void(T()), 0) {\n    return (void(T()), 0);\n  }\n  //  second param should just be: int = (void(T()), 0)\n  //  but under clang 10, crash: https://bugs.llvm.org/show_bug.cgi?id=47620\n  //  and, with assertions disabled, expectation failures showing compiler\n  //  deviation from the language spec\n  //  xcode renumbers clang versions so detection is tricky, but, if detection\n  //  were desired, a combination of __apple_build_version__ and __clang_major__\n  //  may be used to reduce frontend overhead under correct compilers: clang 12\n  //  under xcode and clang 10 otherwise\n  template <typename T, int = make(tag<T>)>\n  static std::true_type sfinae(T*);\n  static std::false_type sfinae(void*);\n  template <typename T>\n  static constexpr bool apply =\n      !require_sizeof<T> || decltype(sfinae(static_cast<T*>(nullptr)))::value;\n};\n\n} // namespace detail\n\n/// is_constexpr_default_constructible_v\n/// is_constexpr_default_constructible\n///\n/// A trait variable and type which determines whether the type parameter is\n/// constexpr default-constructible, that is, default-constructible in a\n/// constexpr context.\ntemplate <typename T>\ninline constexpr bool is_constexpr_default_constructible_v =\n    detail::is_constexpr_default_constructible_::apply<T>;\ntemplate <typename T>\nstruct is_constexpr_default_constructible\n    : std::bool_constant<is_constexpr_default_constructible_v<T>> {};\n\n/***\n *  _t\n *\n *  Instead of:\n *\n *    using decayed = typename std::decay<T>::type;\n *\n *  With the C++14 standard trait aliases, we could use:\n *\n *    using decayed = std::decay_t<T>;\n *\n *  Without them, we could use:\n *\n *    using decayed = _t<std::decay<T>>;\n *\n *  Also useful for any other library with template types having dependent\n *  member types named `type`, like the standard trait types.\n */\ntemplate <typename T>\nusing _t = typename T::type;\n\n/**\n * A type trait to remove all const volatile and reference qualifiers on a\n * type T\n */\ntemplate <typename T>\nstruct remove_cvref {\n  using type =\n      typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n};\ntemplate <typename T>\nusing remove_cvref_t = typename remove_cvref<T>::type;\n\nnamespace detail {\ntemplate <typename Src>\nstruct copy_cvref_ {\n  template <typename Dst>\n  using apply = Dst;\n};\ntemplate <typename Src>\nstruct copy_cvref_<Src const> {\n  template <typename Dst>\n  using apply = Dst const;\n};\ntemplate <typename Src>\nstruct copy_cvref_<Src volatile> {\n  template <typename Dst>\n  using apply = Dst volatile;\n};\ntemplate <typename Src>\nstruct copy_cvref_<Src const volatile> {\n  template <typename Dst>\n  using apply = Dst const volatile;\n};\ntemplate <typename Src>\nstruct copy_cvref_<Src&> {\n  template <typename Dst>\n  using apply = typename copy_cvref_<Src>::template apply<Dst>&;\n};\ntemplate <typename Src>\nstruct copy_cvref_<Src&&> {\n  template <typename Dst>\n  using apply = typename copy_cvref_<Src>::template apply<Dst>&&;\n};\n} // namespace detail\n\n/// copy_cvref_t\n///\n/// A trait alias to replace the cvref category of `Dst` with that of `Src`.\n///\n/// CAUTION: This is not what is typically wanted in a forwarding or\n/// deducing-`this` context, or in most cases of casting one reference\n/// to another. In such cases, the most appropriate tool would be `like_t`,\n/// or `std::forward_like` in C++23.\n///\n/// Some of the problems with forwarding via `copy_cvref` are:\n/// - Removing `const` from a `Dst` that is backed by a value is quite\n///   problematic. The case of `static_cast<copy_cvref_t<...>>(dst)`\n///   would yield a compile error. The case of a C-style cast, `const_cast`,\n///   `reinterpret_cast`, or functional case may compile but may have\n///   undefined behavior by treating non-writable memory as writable.\n/// - The `Dst` value would typically have an address, and the `volatile`\n///   qualifier is a function of that address, so it would be incorrect\n///   to derive that from `Src`.\n///\n/// `like_t` and `forward_like` avoid these problems.\ntemplate <typename Src, typename Dst>\nusing copy_cvref_t =\n    typename detail::copy_cvref_<Src>::template apply<remove_cvref_t<Dst>>;\n\nnamespace detail {\n// These `copy_ref_` functions assume `Dst` is not a reference.\ntemplate <typename Src>\nstruct copy_ref_ {\n  template <typename Dst>\n  using apply = Dst;\n};\ntemplate <typename Src>\nstruct copy_ref_<Src&> {\n  template <typename Dst>\n  using apply = Dst&;\n};\ntemplate <typename Src>\nstruct copy_ref_<Src&&> {\n  template <typename Dst>\n  using apply = Dst&&;\n};\ntemplate <typename Src, typename Dst>\nusing copy_const_t = std::conditional_t<\n    std::is_const_v<std::remove_reference_t<Src>>,\n    Dst const,\n    Dst>;\n} // namespace detail\n\n/// like\n/// like_t\n///\n/// Similar to `like` and `like_t` from p0847r0, but with semantics made\n/// compatible with the C++23 `std::forward_like` from p2445r0.\n///\n/// Differences:\n/// - Never removes `const` qualifiers from `Dst`.\n/// - Leaves any `volatile` as it was on `Dst`, `Src` volatility is ignored.\n/// - Unlike `__forward_like_t` from p2445r0, distinguishes between value\n///   `Src` and rvalue-reference `Src` types.\n///\n/// The above are the right semantics for most cases.\n///\n/// The rare cases which need to replace all cvref qualifiers from `Src` onto\n/// `Dst` may use `copy_cvref_t`. But heed the cautions in its documentation.\n///\n/// mimic: `like`, p0847r0\ntemplate <typename Src, typename Dst>\nusing like_t = typename detail::copy_ref_<Src>::template apply<\n    detail::copy_const_t<Src, std::remove_reference_t<Dst>>>;\ntemplate <typename Src, typename Dst>\nstruct like {\n  using type = like_t<Src, Dst>;\n};\n\n#if defined(__cpp_concepts)\n\n/**\n *  Concept to check that a type is same as a given type,\n *  when stripping qualifiers and refernces.\n *  Especially useful for perfect forwarding of a specific type.\n *\n *  Example:\n *\n *    void foo(folly::uncvref_same_as<std::vector<int>> auto&& vec);\n *\n */\ntemplate <typename Ref, typename To>\nconcept uncvref_same_as = std::is_same_v<std::remove_cvref_t<Ref>, To>;\n\n#endif\n\n/**\n *  type_t\n *\n *  A type alias for the first template type argument. `type_t` is useful for\n *  controlling class-template and function-template partial specialization.\n *\n *  Example:\n *\n *    template <typename Value>\n *    class Container {\n *     public:\n *      template <typename... Args>\n *      Container(\n *          type_t<in_place_t, decltype(Value(std::declval<Args>()...))>,\n *          Args&&...);\n *    };\n *\n *  void_t\n *\n *  A type alias for `void`. `void_t` is useful for controlling class-template\n *  and function-template partial specialization.\n *\n *  Example:\n *\n *    // has_value_type<T>::value is true if T has a nested type `value_type`\n *    template <class T, class = void>\n *    struct has_value_type\n *        : std::false_type {};\n *\n *    template <class T>\n *    struct has_value_type<T, folly::void_t<typename T::value_type>>\n *        : std::true_type {};\n */\n\n/**\n * There is a bug in libstdc++, libc++, and MSVC's STL that causes it to\n * ignore unused template parameter arguments in template aliases and does not\n * cause substitution failures. This defect has been recorded here:\n * http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558.\n *\n * This causes the implementation of std::void_t to be buggy, as it is likely\n * defined as something like the following:\n *\n *  template <typename...>\n *  using void_t = void;\n *\n * This causes the compiler to ignore all the template arguments and does not\n * help when one wants to cause substitution failures.  Rather declarations\n * which have void_t in orthogonal specializations are treated as the same.\n * For example, assuming the possible `T` types are only allowed to have\n * either the alias `one` or `two` and never both or none:\n *\n *  template <typename T,\n *            typename std::void_t<std::decay_t<T>::one>* = nullptr>\n *  void foo(T&&) {}\n *  template <typename T,\n *            typename std::void_t<std::decay_t<T>::two>* = nullptr>\n *  void foo(T&&) {}\n *\n * The second foo() will be a redefinition because it conflicts with the first\n * one; void_t does not cause substitution failures - the template types are\n * just ignored.\n */\n\nnamespace traits_detail {\ntemplate <class T, class...>\nstruct type_t_ {\n  using type = T;\n};\n} // namespace traits_detail\n\ntemplate <class T, class... Ts>\nusing type_t = typename traits_detail::type_t_<T, Ts...>::type;\ntemplate <class... Ts>\nusing void_t = type_t<void, Ts...>;\n\n/// nonesuch\n///\n/// A tag type which traits may use to indicate lack of a result type.\n///\n/// Similar to void in that no values of this type may be constructed. Different\n/// from void in that no functions may be defined with this return type and no\n/// complete expressions may evaluate with this expression type.\n///\n/// mimic: std::experimental::nonesuch, Library Fundamentals TS v2\nstruct nonesuch {\n  ~nonesuch() = delete;\n  nonesuch(nonesuch const&) = delete;\n  void operator=(nonesuch const&) = delete;\n};\n\nnamespace detail {\n\ntemplate <typename Void, typename D, template <typename...> class, typename...>\nstruct detected_ {\n  using value_t = std::false_type;\n  using type = D;\n};\ntemplate <typename D, template <typename...> class T, typename... A>\nstruct detected_<void_t<T<A...>>, D, T, A...> {\n  using value_t = std::true_type;\n  using type = T<A...>;\n};\n\n} // namespace detail\n\n/// detected_or\n///\n/// If T<A...> substitutes, has member type alias value_t as std::true_type\n/// and has member type alias type as T<A...>. Otherwise, has member type\n/// alias value_t as std::false_type and has member type alias type as D.\n///\n/// mimic: std::experimental::detected_or, Library Fundamentals TS v2\n///\n/// Note: not resilient against incomplete types; may violate ODR.\ntemplate <typename D, template <typename...> class T, typename... A>\nusing detected_or = detail::detected_<void, D, T, A...>;\n\n/// detected_or_t\n///\n/// A trait type alias which results in T<A...> if substitution would succeed\n/// and in D otherwise.\n///\n/// Equivalent to detected_or<D, T, A...>::type.\n///\n/// mimic: std::experimental::detected_or_t, Library Fundamentals TS v2\n///\n/// Note: not resilient against incomplete types; may violate ODR.\ntemplate <typename D, template <typename...> class T, typename... A>\nusing detected_or_t = typename detected_or<D, T, A...>::type;\n\n/// detected_t\n///\n/// A trait type alias which results in T<A...> if substitution would succeed\n/// and in nonesuch otherwise.\n///\n/// Equivalent to detected_or_t<nonesuch, T, A...>.\n///\n/// mimic: std::experimental::detected_t, Library Fundamentals TS v2\n///\n/// Note: not resilient against incomplete types; may violate ODR.\ntemplate <template <typename...> class T, typename... A>\nusing detected_t = detected_or_t<nonesuch, T, A...>;\n\n//  is_detected_v\n//  is_detected\n//\n//  A trait variable and type to test whether some metafunction from types to\n//  types would succeed or fail in substitution over a given set of arguments.\n//\n//  The trait variable is_detected_v<T, A...> is equivalent to\n//  detected_or<nonesuch, T, A...>::value_t::value.\n//  The trait type is_detected<T, A...> unambiguously inherits\n//  std::bool_constant<V> where V is is_detected_v<T, A...>.\n//\n//  mimic: std::experimental::is_detected, std::experimental::is_detected_v,\n//    Library Fundamentals TS v2\n//\n//  Note: not resilient against incomplete types; may violate ODR.\n//\n//  Note: the trait type is_detected differs here by being deferred.\ntemplate <template <typename...> class T, typename... A>\ninline constexpr bool is_detected_v =\n    detected_or<nonesuch, T, A...>::value_t::value;\ntemplate <template <typename...> class T, typename... A>\nstruct is_detected : detected_or<nonesuch, T, A...>::value_t {};\n\nnamespace detail {\nconstexpr std::size_t aligned_storage_default_align(std::size_t len) {\n  // Match std::aligned_storage default: the most stringent fundamental\n  // alignment for any type whose size is no greater than len.\n  // Compute bit_floor(min(len, alignof(max_align_t))): propagate the highest\n  // set bit to all lower positions, then isolate it. O(log(bits)) steps.\n  len = len < alignof(std::max_align_t) ? len : alignof(std::max_align_t);\n  len |= len >> 1;\n  len |= len >> 2;\n  len |= len >> 4;\n  len |= len >> 8;\n  if constexpr (sizeof(std::size_t) > 2) {\n    len |= len >> 16;\n  }\n  if constexpr (sizeof(std::size_t) > 4) {\n    len |= len >> 32;\n  }\n  return len - (len >> 1);\n}\n\ntemplate <std::size_t Len, std::size_t Align>\nstruct aligned_storage {\n  struct type {\n    alignas(Align) std::byte data[Len];\n  };\n};\n} // namespace detail\n\ntemplate <\n    std::size_t Len,\n    std::size_t Align = detail::aligned_storage_default_align(Len)>\nusing aligned_storage_t = typename detail::aligned_storage<Len, Align>::type;\n\ntemplate <typename T>\nusing aligned_storage_for_t = aligned_storage_t<sizeof(T), alignof(T)>;\n\n//  ----\n\nnamespace fallback {\ntemplate <typename From, typename To>\ninline constexpr bool is_nothrow_convertible_v =\n    (std::is_void<From>::value && std::is_void<To>::value) ||\n    ( //\n        std::is_convertible<From, To>::value &&\n        std::is_nothrow_constructible<To, From>::value);\ntemplate <typename From, typename To>\nstruct is_nothrow_convertible\n    : std::bool_constant<is_nothrow_convertible_v<From, To>> {};\n} // namespace fallback\n\n//  is_nothrow_convertible\n//  is_nothrow_convertible_v\n//\n//  Import or backport:\n//  * std::is_nothrow_convertible\n//  * std::is_nothrow_convertible_v\n//\n//  mimic: is_nothrow_convertible, C++20\n#if defined(__cpp_lib_is_nothrow_convertible) && \\\n    __cpp_lib_is_nothrow_convertible >= 201806L\nusing std::is_nothrow_convertible;\nusing std::is_nothrow_convertible_v;\n#else\nusing fallback::is_nothrow_convertible;\nusing fallback::is_nothrow_convertible_v;\n#endif\n\n/**\n * IsRelocatable<T>::value describes the ability of moving around\n * memory a value of type T by using memcpy (as opposed to the\n * conservative approach of calling the copy constructor and then\n * destroying the old temporary. Essentially for a relocatable type,\n * the following two sequences of code should be semantically\n * equivalent:\n *\n * void move1(T * from, T * to) {\n *   new(to) T(from);\n *   (*from).~T();\n * }\n *\n * void move2(T * from, T * to) {\n *   memcpy(to, from, sizeof(T));\n * }\n *\n * Most C++ types are relocatable; the ones that aren't would include\n * internal pointers or (very rarely) would need to update remote\n * pointers to pointers tracking them. All C++ primitive types and\n * type constructors are relocatable.\n *\n * This property can be used in a variety of optimizations. Currently\n * fbvector uses this property intensively.\n *\n * The default conservatively assumes the type is not\n * relocatable. Several specializations are defined for known\n * types. You may want to add your own specializations. Do so in\n * namespace folly and make sure you keep the specialization of\n * IsRelocatable<SomeStruct> in the same header as SomeStruct.\n *\n * You may also declare a type to be relocatable by including\n *    `typedef std::true_type IsRelocatable;`\n * in the class header.\n *\n * It may be unset in a base class by overriding the typedef to false_type.\n */\n/*\n * IsZeroInitializable describes the property that value-initialization\n * is the same as memset(dst, 0, sizeof(T)).\n */\n\nnamespace traits_detail {\n\n#define FOLLY_HAS_TRUE_XXX(name)                                             \\\n  template <typename T>                                                      \\\n  using detect_##name = typename T::name;                                    \\\n  template <class T>                                                         \\\n  struct name##_is_true : std::is_same<typename T::name, std::true_type> {}; \\\n  template <class T>                                                         \\\n  struct has_true_##name                                                     \\\n      : std::conditional<                                                    \\\n            is_detected_v<detect_##name, T>,                                 \\\n            name##_is_true<T>,                                               \\\n            std::false_type>::type {}\n\nFOLLY_HAS_TRUE_XXX(IsRelocatable);\nFOLLY_HAS_TRUE_XXX(IsZeroInitializable);\n\n#undef FOLLY_HAS_TRUE_XXX\n\n} // namespace traits_detail\n\nstruct Ignore {\n  Ignore() = default;\n  template <class T>\n  constexpr /* implicit */ Ignore(const T&) {}\n  template <class T>\n  const Ignore& operator=(T const&) const {\n    return *this;\n  }\n};\n\ntemplate <class...>\nusing Ignored = Ignore;\n\nnamespace traits_detail_IsEqualityComparable {\nIgnore operator==(Ignore, Ignore);\n\ntemplate <class T, class U = T>\nstruct IsEqualityComparable\n    : std::is_convertible<\n          decltype(std::declval<T>() == std::declval<U>()),\n          bool> {};\n} // namespace traits_detail_IsEqualityComparable\n\n/* using override */ using traits_detail_IsEqualityComparable::\n    IsEqualityComparable;\n\nnamespace traits_detail_IsLessThanComparable {\nIgnore operator<(Ignore, Ignore);\n\ntemplate <class T, class U = T>\nstruct IsLessThanComparable\n    : std::is_convertible<\n          decltype(std::declval<T>() < std::declval<U>()),\n          bool> {};\n} // namespace traits_detail_IsLessThanComparable\n\n/* using override */ using traits_detail_IsLessThanComparable::\n    IsLessThanComparable;\n\ntemplate <class T>\nstruct IsRelocatable\n    : std::conditional<\n          !require_sizeof<T> ||\n              is_detected_v<traits_detail::detect_IsRelocatable, T>,\n          traits_detail::has_true_IsRelocatable<T>,\n#if defined(__cpp_lib_is_trivially_relocatable) // P1144\n          std::is_trivially_relocatable<T>\n#else\n          std::is_trivially_copyable<T>\n#endif\n          >::type {\n};\n\ntemplate <class T>\nstruct IsZeroInitializable\n    : std::conditional<\n          !require_sizeof<T> ||\n              is_detected_v<traits_detail::detect_IsZeroInitializable, T>,\n          traits_detail::has_true_IsZeroInitializable<T>,\n          std::bool_constant< //\n              !std::is_class<T>::value && //\n              !std::is_union<T>::value && //\n              !std::is_member_object_pointer<T>::value && // itanium\n              true>>::type {};\n\nnamespace detail {\ntemplate <bool>\nstruct conditional_;\ntemplate <>\nstruct conditional_<false> {\n  template <typename, typename T>\n  using apply = T;\n};\ntemplate <>\nstruct conditional_<true> {\n  template <typename T, typename>\n  using apply = T;\n};\n} // namespace detail\n\n/// conditional_t\n///\n/// Like std::conditional_t but with only two total class template instances,\n/// rather than as many class template instances as there are uses.\n///\n/// As one effect, the result can be used in deducible contexts, allowing\n/// deduction of conditional_t<V, T, F> to work when T or F is a template param.\ntemplate <bool V, typename T, typename F>\nusing conditional_t = typename detail::conditional_<V>::template apply<T, F>;\n\ntemplate <typename...>\nstruct Conjunction : std::true_type {};\ntemplate <typename T>\nstruct Conjunction<T> : T {};\ntemplate <typename T, typename... TList>\nstruct Conjunction<T, TList...>\n    : std::conditional<T::value, Conjunction<TList...>, T>::type {};\n\ntemplate <typename...>\nstruct Disjunction : std::false_type {};\ntemplate <typename T>\nstruct Disjunction<T> : T {};\ntemplate <typename T, typename... TList>\nstruct Disjunction<T, TList...>\n    : std::conditional<T::value, T, Disjunction<TList...>>::type {};\n\ntemplate <typename T>\nstruct Negation : std::bool_constant<!T::value> {};\n\ntemplate <bool... Bs>\nstruct Bools {\n  using valid_type = bool;\n  static constexpr std::size_t size() { return sizeof...(Bs); }\n};\n\n//  Lighter-weight than Conjunction, but evaluates all sub-conditions eagerly.\ntemplate <class... Ts>\nstruct StrictConjunction\n    : std::is_same<Bools<Ts::value...>, Bools<(Ts::value || true)...>> {};\n\ntemplate <class... Ts>\nstruct StrictDisjunction\n    : Negation<\n          std::is_same<Bools<Ts::value...>, Bools<(Ts::value && false)...>>> {};\n\nnamespace detail {\ntemplate <typename T>\nusing is_transparent_ = typename T::is_transparent;\n} // namespace detail\n\n/// is_transparent_v\n/// is_transparent\n///\n/// A trait variable and type to test whether a less, equal-to, or hash type\n/// follows the is-transparent protocol used by containers with optional\n/// heterogeneous access.\ntemplate <typename T>\ninline constexpr bool is_transparent_v =\n    is_detected_v<detail::is_transparent_, T>;\ntemplate <typename T>\nstruct is_transparent : std::bool_constant<is_transparent_v<T>> {};\n\nnamespace detail {\n\ntemplate <typename T, typename = void>\ninline constexpr bool is_allocator_ = !require_sizeof<T>;\ntemplate <typename T>\ninline constexpr bool is_allocator_<\n    T,\n    void_t<\n        typename T::value_type,\n        decltype(std::declval<T&>().allocate(std::size_t{})),\n        decltype(std::declval<T&>().deallocate(\n            static_cast<typename T::value_type*>(nullptr), std::size_t{}))>> =\n    true;\n\n} // namespace detail\n\n/// is_allocator_v\n/// is_allocator\n///\n/// A trait variable and type to test whether a type is an allocator according\n/// to the minimum protocol required by std::allocator_traits.\ntemplate <typename T>\ninline constexpr bool is_allocator_v = detail::is_allocator_<T>;\ntemplate <typename T>\nstruct is_allocator : std::bool_constant<is_allocator_v<T>> {};\n\n} // namespace folly\n\n/**\n * Use this macro ONLY inside namespace folly. When using it with a\n * regular type, use it like this:\n *\n * // Make sure you're at namespace ::folly scope\n * template <> FOLLY_ASSUME_RELOCATABLE(MyType)\n *\n * When using it with a template type, use it like this:\n *\n * // Make sure you're at namespace ::folly scope\n * template <class T1, class T2>\n * FOLLY_ASSUME_RELOCATABLE(MyType<T1, T2>)\n */\n#define FOLLY_ASSUME_RELOCATABLE(...) \\\n  struct IsRelocatable<__VA_ARGS__> : std::true_type {}\n\n/**\n * The FOLLY_ASSUME_FBVECTOR_COMPATIBLE* macros below encode the\n * assumption that the type is relocatable per IsRelocatable\n * above. Many types can be assumed to satisfy this condition, but\n * it is the responsibility of the user to state that assumption.\n * User-defined classes will not be optimized for use with\n * fbvector (see FBVector.h) unless they state that assumption.\n *\n * Use FOLLY_ASSUME_FBVECTOR_COMPATIBLE with regular types like this:\n *\n * FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MyType)\n *\n * The versions FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1, _2, _3, and _4\n * allow using the macro for describing templatized classes with 1, 2,\n * 3, and 4 template parameters respectively. For template classes\n * just use the macro with the appropriate number and pass the name of\n * the template to it. Example:\n *\n * template <class T1, class T2> class MyType { ... };\n * ...\n * // Make sure you're at global scope\n * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyType)\n */\n\n// Use this macro ONLY at global level (no namespace)\n#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE(...) \\\n  namespace folly {                           \\\n  template <>                                 \\\n  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__);      \\\n  }\n// Use this macro ONLY at global level (no namespace)\n#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(...) \\\n  namespace folly {                             \\\n  template <class T1>                           \\\n  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1>);    \\\n  }\n// Use this macro ONLY at global level (no namespace)\n#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(...)  \\\n  namespace folly {                              \\\n  template <class T1, class T2>                  \\\n  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2>); \\\n  }\n// Use this macro ONLY at global level (no namespace)\n#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(...)      \\\n  namespace folly {                                  \\\n  template <class T1, class T2, class T3>            \\\n  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3>); \\\n  }\n// Use this macro ONLY at global level (no namespace)\n#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(...)          \\\n  namespace folly {                                      \\\n  template <class T1, class T2, class T3, class T4>      \\\n  FOLLY_ASSUME_RELOCATABLE(__VA_ARGS__<T1, T2, T3, T4>); \\\n  }\n\nnamespace folly {\n\n// STL commonly-used types\ntemplate <class T, class U>\nstruct IsRelocatable<std::pair<T, U>>\n    : std::bool_constant<IsRelocatable<T>::value && IsRelocatable<U>::value> {};\n\n// Is T one of T1, T2, ..., Tn?\ntemplate <typename T, typename... Ts>\nusing IsOneOf = StrictDisjunction<std::is_same<T, Ts>...>;\n\ntemplate <typename T, typename... Ts>\ninline constexpr bool is_one_of_v = IsOneOf<T, Ts...>::value;\n\n/*\n * Complementary type traits for integral comparisons.\n *\n * For instance, `if(x < 0)` yields an error in clang for unsigned types\n * when -Werror is used due to -Wtautological-compare\n */\n\n// same as `x < 0`\ntemplate <typename T>\nconstexpr bool is_negative(T x) {\n  return std::is_signed<T>::value && x < T(0);\n}\n\n// same as `x <= 0`\ntemplate <typename T>\nconstexpr bool is_non_positive(T x) {\n  return !x || folly::is_negative(x);\n}\n\n// same as `x > 0`\ntemplate <typename T>\nconstexpr bool is_positive(T x) {\n  return !is_non_positive(x);\n}\n\n// same as `x >= 0`\ntemplate <typename T>\nconstexpr bool is_non_negative(T x) {\n  return !x || is_positive(x);\n}\n\nnamespace detail {\n\n//  folly::to integral specializations can end up generating code\n//  inside what are really static ifs (not executed because of the templated\n//  types) that violate -Wsign-compare and/or -Wbool-compare so suppress them\n//  in order to not prevent all calling code from using it.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wsign-compare\")\nFOLLY_GCC_DISABLE_WARNING(\"-Wbool-compare\")\nFOLLY_MSVC_DISABLE_WARNING(4287) // unsigned/negative constant mismatch\nFOLLY_MSVC_DISABLE_WARNING(4388) // sign-compare\nFOLLY_MSVC_DISABLE_WARNING(4804) // bool-compare\n\ntemplate <typename RHS, RHS rhs, typename LHS>\nbool less_than_impl(LHS const lhs) {\n  // clang-format off\n  return\n      // Ensure signed and unsigned values won't be compared directly.\n      (!std::is_signed<RHS>::value && is_negative(lhs)) ? true :\n      (!std::is_signed<LHS>::value && is_negative(rhs)) ? false :\n      rhs > std::numeric_limits<LHS>::max() ? true :\n      rhs <= std::numeric_limits<LHS>::lowest() ? false :\n      lhs < rhs;\n  // clang-format on\n}\n\ntemplate <typename RHS, RHS rhs, typename LHS>\nbool greater_than_impl(LHS const lhs) {\n  // clang-format off\n  return\n      // Ensure signed and unsigned values won't be compared directly.\n      (!std::is_signed<RHS>::value && is_negative(lhs)) ? false :\n      (!std::is_signed<LHS>::value && is_negative(rhs)) ? true :\n      rhs > std::numeric_limits<LHS>::max() ? false :\n      rhs < std::numeric_limits<LHS>::lowest() ? true :\n      lhs > rhs;\n  // clang-format on\n}\n\nFOLLY_POP_WARNING\n\n} // namespace detail\n\ntemplate <typename RHS, RHS rhs, typename LHS>\nbool less_than(LHS const lhs) {\n  return detail::\n      less_than_impl<RHS, rhs, typename std::remove_reference<LHS>::type>(lhs);\n}\n\ntemplate <typename RHS, RHS rhs, typename LHS>\nbool greater_than(LHS const lhs) {\n  return detail::\n      greater_than_impl<RHS, rhs, typename std::remove_reference<LHS>::type>(\n          lhs);\n}\n} // namespace folly\n\n// Assume nothing when compiling with MSVC.\n#ifndef _MSC_VER\nFOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(std::unique_ptr)\nFOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::shared_ptr)\n#endif\n\nnamespace folly {\n\n/// is_non_bool_integral_v\n///\n/// A common need.\ntemplate <typename Int>\ninline constexpr bool is_non_bool_integral_v =\n    !std::is_same_v<bool, std::remove_cv_t<Int>> && std::is_integral_v<Int>;\n\ntemplate <typename Int>\nstruct is_non_bool_integral //\n    : std::bool_constant<is_non_bool_integral_v<Int>> {};\n\n//  Some compilers have signed __int128 and unsigned __int128 types, and some\n//  libraries with some compilers have traits for those types. It's a mess.\n//  Import things into folly and then fill in whatever is missing.\n//\n//  The aliases:\n//    int128_t\n//    uint128_t\n//\n//  The traits:\n//    is_arithmetic\n//    is_arithmetic_v\n//    is_integral\n//    is_integral_v\n//    is_signed\n//    is_signed_v\n//    is_unsigned\n//    is_unsigned_v\n//    make_signed\n//    make_signed_t\n//    make_unsigned\n//    make_unsigned_t\n\ntemplate <typename T>\nstruct is_arithmetic : std::is_arithmetic<T> {};\ntemplate <typename T>\ninline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;\n\ntemplate <typename T>\nstruct is_integral : std::is_integral<T> {};\ntemplate <typename T>\ninline constexpr bool is_integral_v = is_integral<T>::value;\n\ntemplate <typename T>\nstruct is_signed : std::is_signed<T> {};\ntemplate <typename T>\ninline constexpr bool is_signed_v = is_signed<T>::value;\n\ntemplate <typename T>\nstruct is_unsigned : std::is_unsigned<T> {};\ntemplate <typename T>\ninline constexpr bool is_unsigned_v = is_unsigned<T>::value;\n\ntemplate <typename T>\nstruct make_signed : std::make_signed<T> {};\ntemplate <typename T>\nusing make_signed_t = typename make_signed<T>::type;\n\ntemplate <typename T>\nstruct make_unsigned : std::make_unsigned<T> {};\ntemplate <typename T>\nusing make_unsigned_t = typename make_unsigned<T>::type;\n\n#if FOLLY_HAVE_INT128_T\n\nusing int128_t = signed __int128;\nusing uint128_t = unsigned __int128;\n\ntemplate <>\nstruct is_arithmetic<int128_t> : std::true_type {};\ntemplate <>\nstruct is_arithmetic<uint128_t> : std::true_type {};\n\ntemplate <>\nstruct is_integral<int128_t> : std::true_type {};\ntemplate <>\nstruct is_integral<uint128_t> : std::true_type {};\n\ntemplate <>\nstruct is_signed<int128_t> : std::true_type {};\ntemplate <>\nstruct is_signed<uint128_t> : std::false_type {};\ntemplate <>\nstruct is_unsigned<int128_t> : std::false_type {};\ntemplate <>\nstruct is_unsigned<uint128_t> : std::true_type {};\n\ntemplate <>\nstruct make_signed<int128_t> {\n  using type = int128_t;\n};\ntemplate <>\nstruct make_signed<uint128_t> {\n  using type = int128_t;\n};\n\ntemplate <>\nstruct make_unsigned<int128_t> {\n  using type = uint128_t;\n};\ntemplate <>\nstruct make_unsigned<uint128_t> {\n  using type = uint128_t;\n};\n#endif // FOLLY_HAVE_INT128_T\n\nnamespace traits_detail {\ntemplate <std::size_t>\nstruct uint_bits_t_ {};\ntemplate <>\nstruct uint_bits_t_<8> : type_t_<std::uint8_t> {};\ntemplate <>\nstruct uint_bits_t_<16> : type_t_<std::uint16_t> {};\ntemplate <>\nstruct uint_bits_t_<32> : type_t_<std::uint32_t> {};\ntemplate <>\nstruct uint_bits_t_<64> : type_t_<std::uint64_t> {};\n#if FOLLY_HAVE_INT128_T\ntemplate <>\nstruct uint_bits_t_<128> : type_t_<uint128_t> {};\n#endif // FOLLY_HAVE_INT128_T\n} // namespace traits_detail\n\ntemplate <std::size_t bits>\nusing uint_bits_t = _t<traits_detail::uint_bits_t_<bits>>;\n\ntemplate <std::size_t lg_bits>\nusing uint_bits_lg_t = uint_bits_t<(1u << lg_bits)>;\n\ntemplate <std::size_t bits>\nusing int_bits_t = make_signed_t<uint_bits_t<bits>>;\n\ntemplate <std::size_t lg_bits>\nusing int_bits_lg_t = make_signed_t<uint_bits_lg_t<lg_bits>>;\n\nnamespace traits_detail {\n\ntemplate <std::size_t I, typename T>\nstruct type_pack_element_indexed_type {\n  using type = T;\n};\n\ntemplate <typename, typename...>\nstruct type_pack_element_set;\ntemplate <std::size_t... I, typename... T>\nstruct type_pack_element_set<std::index_sequence<I...>, T...>\n    : type_pack_element_indexed_type<I, T>... {};\ntemplate <typename... T>\nusing type_pack_element_set_t =\n    type_pack_element_set<std::index_sequence_for<T...>, T...>;\n\ntemplate <std::size_t I>\nstruct type_pack_element_test {\n  template <typename T>\n  static type_pack_element_indexed_type<I, T> impl(\n      type_pack_element_indexed_type<I, T>*);\n};\n\ntemplate <std::size_t I, typename... Ts>\nusing type_pack_element_fallback = _t<decltype(type_pack_element_test<I>::impl(\n    static_cast<type_pack_element_set_t<Ts...>*>(nullptr)))>;\n\n} // namespace traits_detail\n\n/// type_pack_element_t\n///\n/// In the type pack Ts..., the Ith element.\n///\n/// Wraps the builtin __type_pack_element where the builtin is available; where\n/// not, implemented directly.\n///\n/// Under gcc, the builtin is available but does not mangle. Therefore, this\n/// trait must not be used anywhere it might be subject to mangling, such as in\n/// a return-type expression.\n\n#if FOLLY_HAS_BUILTIN(__type_pack_element)\n\ntemplate <std::size_t I, typename... Ts>\nusing type_pack_element_t = __type_pack_element<I, Ts...>;\n\n#else\n\ntemplate <std::size_t I, typename... Ts>\nusing type_pack_element_t = traits_detail::type_pack_element_fallback<I, Ts...>;\n\n#endif\n\n/// type_pack_size_v\n///\n/// The size of a type pack.\n///\n/// A metafunction around sizeof...(Ts).\ntemplate <typename... Ts>\ninline constexpr std::size_t type_pack_size_v = sizeof...(Ts);\n\n/// type_pack_size_t\n///\n/// The size of a type pack.\n///\n/// A metafunction around index_constant<sizeof...(Ts)>.\ntemplate <typename... Ts>\nusing type_pack_size_t = index_constant<sizeof...(Ts)>;\n\nnamespace traits_detail {\n\ntemplate <std::size_t I, template <typename...> class List, typename... T>\ntype_identity<type_pack_element_t<I, T...>> type_list_element_(\n    List<T...> const*);\n\ntemplate <template <typename...> class List, typename... T>\nindex_constant<sizeof...(T)> type_list_size_(List<T...> const*);\n\n} // namespace traits_detail\n\n/// type_list_element_t\n///\n/// In the type list List<T...>, where List has kind template <typename...> and\n/// T... is a type-pack, equivalent to type_pack_element_t<I, T...>.\ntemplate <std::size_t I, typename List>\nusing type_list_element_t = _t<decltype(traits_detail::type_list_element_<I>(\n    static_cast<List const*>(nullptr)))>;\n\n/// type_list_size_v\n///\n/// The size of a type list.\n///\n/// For List<T...>, equivalent to type_pack_size_v<T...>.\ntemplate <typename List>\ninline constexpr std::size_t type_list_size_v =\n    decltype(traits_detail::type_list_size_(\n        static_cast<List const*>(nullptr)))::value;\n\n/// type_list_size_t\n///\n/// The size of a type list.\n///\n/// For List<T...>, equivalent to type_pack_size_t<T...>.\ntemplate <typename List>\nusing type_list_size_t =\n    decltype(traits_detail::type_list_size_(static_cast<List const*>(nullptr)));\n\nnamespace detail {\n\n// The arguments to this \"error\" type help the user debug bad invocations.\n// It is purposely undefined to cause a compile error.\ntemplate <typename...>\nstruct error_list_concat_params_should_be_non_cvref;\n\n// The primary template is only invoked for invalid parameters.\ntemplate <template <typename...> class Out, typename... T>\ninline constexpr auto type_list_concat_ =\n    error_list_concat_params_should_be_non_cvref<T...>{};\n\ntemplate <template <typename...> class Out>\ninline constexpr type_identity<Out<>> type_list_concat_<Out>;\n\ntemplate <\n    template <typename...> class Out,\n    template <typename...> class In,\n    typename... T>\ninline constexpr auto type_list_concat_<Out, In<T...>> =\n    type_identity<Out<T...>>{};\n\ntemplate <\n    template <typename...> class Out,\n    // Allow input lists to come from heterogeneous templates.\n    template <typename...> class InA,\n    typename... A,\n    template <typename...> class InB,\n    typename... B,\n    typename... Tail>\ninline constexpr auto type_list_concat_<Out, InA<A...>, InB<B...>, Tail...> =\n    // Avoid instantiating the `In*` or `Out` types for the intermediate\n    // lists, since those types may be invalid, or expensive.  Per my tests\n    // on clang using `tag_t` for the intermediate list is no more expensive\n    // than using a dedicated incomplete list type.\n    type_list_concat_<Out, tag_t<A..., B...>, Tail...>;\n\n} // namespace detail\n\n/// type_list_concat_t\n///\n/// Each `List` is a type list of the form `InK<TypeK...>`, where the\n/// templates `InK` are potentially heterogeneous.  Concatenates these\n/// `List`s into a single type list `Out<Type1..., Type2..., ...>`.\ntemplate <template <typename...> class Out, typename... List>\nusing type_list_concat_t =\n    typename decltype(detail::type_list_concat_<Out, List...>)::type;\n\nnamespace traits_detail {\n\ntemplate <decltype(auto) V>\nstruct value_pack_constant {\n  inline static constexpr decltype(V) value = V;\n};\n\n} // namespace traits_detail\n\n/// value_pack_size_v\n///\n/// The size of a value pack.\n///\n/// A metafunction around sizeof...(V).\ntemplate <auto... V>\ninline constexpr std::size_t value_pack_size_v = sizeof...(V);\n\n/// value_pack_size_t\n///\n/// The size of a value pack.\n///\n/// A metafunction around index_constant<sizeof...(V)>.\ntemplate <auto... V>\nusing value_pack_size_t = index_constant<sizeof...(V)>;\n\n/// value_pack_element_type_t\n///\n/// In the value pack V..., the type of the Ith element.\ntemplate <std::size_t I, auto... V>\nusing value_pack_element_type_t = type_pack_element_t<I, decltype(V)...>;\n\n/// value_pack_element_type_t\n///\n/// In the value pack V..., the Ith element.\ntemplate <std::size_t I, auto... V>\ninline constexpr value_pack_element_type_t<I, V...> value_pack_element_v =\n    type_pack_element_t<I, traits_detail::value_pack_constant<V>...>::value;\n\nnamespace traits_detail {\n\ntemplate <typename List>\nstruct value_list_traits_;\ntemplate <template <auto...> class List, auto... V>\nstruct value_list_traits_<List<V...>> {\n  static constexpr std::size_t size = sizeof...(V);\n  template <std::size_t I>\n  using element_type = value_pack_element_type_t<I, V...>;\n  template <std::size_t I>\n  static constexpr value_pack_element_type_t<I, V...> element =\n      value_pack_element_v<I, V...>;\n};\n\n} // namespace traits_detail\n\n/// value_list_size_v\n///\n/// The size of a value list.\n///\n/// For List<V...>, equivalent to value_pack_size_v<V...>.\ntemplate <typename List>\ninline constexpr std::size_t value_list_size_v =\n    traits_detail::value_list_traits_<List>::size;\n\n/// value_list_size_t\n///\n/// The size of a value list.\n///\n/// For List<V...>, equivalent to value_pack_size_t<V...>.\ntemplate <typename List>\nusing value_list_size_t = index_constant<value_list_size_v<List>>;\n\n/// value_list_element_type_t\n///\n/// For List<V...>, the type of the Ith element.\ntemplate <std::size_t I, typename List>\nusing value_list_element_type_t =\n    typename traits_detail::value_list_traits_<List>::template element_type<I>;\n\n/// value_list_element_v\n///\n/// For List<V...>, the Ith element.\ntemplate <std::size_t I, typename List>\ninline constexpr value_list_element_type_t<I, List> value_list_element_v =\n    traits_detail::value_list_traits_<List>::template element<I>;\n\nnamespace detail {\n\n// The primary template is only invoked for invalid parameters.\ntemplate <template <auto...> class Out, typename... T>\ninline constexpr auto value_list_concat_ =\n    error_list_concat_params_should_be_non_cvref<T...>{};\n\ntemplate <template <auto...> class Out>\ninline constexpr type_identity<Out<>> value_list_concat_<Out>;\n\ntemplate <template <auto...> class Out, template <auto...> class In, auto... V>\ninline constexpr auto value_list_concat_<Out, In<V...>> =\n    type_identity<Out<V...>>{};\n\ntemplate <\n    template <auto...> class Out,\n    // Allow input lists to come from heterogeneous templates.\n    template <auto...> class InA,\n    auto... A,\n    template <auto...> class InB,\n    auto... B,\n    typename... Tail>\ninline constexpr auto value_list_concat_<Out, InA<A...>, InB<B...>, Tail...> =\n    // The use of `vtag_t` is explained in the analogous `type_list_concat_.\n    value_list_concat_<Out, vtag_t<A..., B...>, Tail...>;\n\n} // namespace detail\n\n/// value_list_concat_t\n///\n/// Each `List` is a value list of the form `InK<ValK...>`, where the\n/// templates `InK` are potentially heterogeneous.  Concatenates these\n/// `List`s into a single value list `Out<Val1..., Val2..., ...>`.\ntemplate <template <auto...> class Out, typename... List>\nusing value_list_concat_t =\n    typename decltype(detail::value_list_concat_<Out, List...>)::type;\n\nnamespace detail {\n\ntemplate <typename V, typename... T>\nconstexpr bool type_pack_find_a_[sizeof...(T) + 1] = {\n    std::is_same_v<V, T>..., true};\n\nconstexpr std::size_t type_pack_find_(bool const* eq) {\n  size_t i = 0;\n  while (!eq[i]) {\n    ++i;\n  }\n  return i;\n}\n\ntemplate <typename>\nstruct type_list_find_;\ntemplate <template <typename...> class List, typename... T>\nstruct type_list_find_<List<T...>> {\n  template <typename V>\n  static inline constexpr std::size_t apply =\n      type_pack_find_(type_pack_find_a_<V, T...>);\n};\n\n} // namespace detail\n\n/// type_pack_find_v\n///\n/// The index of the element of the type pack which is identical to the given\n/// type, or the size of the pack if there is no such element.\ntemplate <typename V, typename... T>\ninline constexpr std::size_t type_pack_find_v =\n    detail::type_pack_find_(detail::type_pack_find_a_<V, T...>);\n\n/// type_pack_find_t\n///\n/// The index of the element of the type pack which is identical to the given\n/// type, or the size of the pack if there is no such element.\ntemplate <typename V, typename... T>\nusing type_pack_find_t = index_constant<type_pack_find_v<V, T...>>;\n\n/// type_list_find_v\n///\n/// The index of the element of the type list which is identical to the given\n/// type, or the size of the list if there is no such element.\ntemplate <typename V, typename List>\ninline constexpr std::size_t type_list_find_v =\n    detail::type_list_find_<List>::template apply<V>;\n\n/// type_list_find_t\n///\n/// The index of the element of the type list which is identical to the given\n/// type, or the size of the list if there is no such element.\ntemplate <typename V, typename List>\nusing type_list_find_t = index_constant<type_list_find_v<V, List>>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Try-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Utility.h>\n#include <folly/functional/Invoke.h>\n\n#include <stdexcept>\n#include <tuple>\n#include <utility>\n\nnamespace folly {\n\nnamespace detail {\ntemplate <class T>\nTryBase<T>::TryBase(TryBase<T>&& t) noexcept(\n    std::is_nothrow_move_constructible<T>::value)\n    : contains_(t.contains_) {\n  if (contains_ == Contains::VALUE) {\n    ::new (static_cast<void*>(std::addressof(value_))) T(std::move(t.value_));\n  } else if (contains_ == Contains::EXCEPTION) {\n    new (&e_) exception_wrapper(std::move(t.e_));\n  }\n}\n\ntemplate <class T>\nTryBase<T>& TryBase<T>::operator=(TryBase<T>&& t) noexcept(\n    std::is_nothrow_move_constructible<T>::value) {\n  if (this == &t) {\n    return *this;\n  }\n\n  destroy();\n\n  if (t.contains_ == Contains::VALUE) {\n    ::new (static_cast<void*>(std::addressof(value_))) T(std::move(t.value_));\n  } else if (t.contains_ == Contains::EXCEPTION) {\n    new (&e_) exception_wrapper(std::move(t.e_));\n  }\n\n  contains_ = t.contains_;\n\n  return *this;\n}\n\ntemplate <class T>\nTryBase<T>::TryBase(const TryBase<T>& t) noexcept(\n    std::is_nothrow_copy_constructible<T>::value) {\n  contains_ = t.contains_;\n  if (contains_ == Contains::VALUE) {\n    ::new (static_cast<void*>(std::addressof(value_))) T(t.value_);\n  } else if (contains_ == Contains::EXCEPTION) {\n    new (&e_) exception_wrapper(t.e_);\n  }\n}\n\ntemplate <class T>\nTryBase<T>& TryBase<T>::operator=(const TryBase<T>& t) noexcept(\n    std::is_nothrow_copy_constructible<T>::value) {\n  if (this == &t) {\n    return *this;\n  }\n\n  destroy();\n\n  if (t.contains_ == Contains::VALUE) {\n    ::new (static_cast<void*>(std::addressof(value_))) T(t.value_);\n  } else if (t.contains_ == Contains::EXCEPTION) {\n    new (&e_) exception_wrapper(t.e_);\n  }\n\n  contains_ = t.contains_;\n\n  return *this;\n}\n\ntemplate <class T>\nvoid TryBase<T>::destroy() noexcept {\n  auto oldContains = std::exchange(contains_, Contains::NOTHING);\n  if (FOLLY_LIKELY(oldContains == Contains::VALUE)) {\n    value_.~T();\n  } else if (FOLLY_UNLIKELY(oldContains == Contains::EXCEPTION)) {\n    e_.~exception_wrapper();\n  }\n}\n\ntemplate <class T>\ntemplate <class T2>\nTryBase<T>::TryBase(\n    typename std::enable_if<std::is_same<Unit, T2>::value, Try<void> const&>::\n        type t) noexcept\n    : contains_(Contains::NOTHING) {\n  if (t.hasValue()) {\n    contains_ = Contains::VALUE;\n    ::new (static_cast<void*>(std::addressof(value_))) T();\n  } else if (t.hasException()) {\n    contains_ = Contains::EXCEPTION;\n    new (&e_) exception_wrapper(t.exception());\n  }\n}\n\ntemplate <class T>\nTryBase<T>::~TryBase() {\n  if (FOLLY_LIKELY(contains_ == Contains::VALUE)) {\n    value_.~T();\n  } else if (FOLLY_UNLIKELY(contains_ == Contains::EXCEPTION)) {\n    e_.~exception_wrapper();\n  }\n}\n\n} // namespace detail\n\nTry<void>::Try(const Try<Unit>& t) noexcept : hasValue_(!t.hasException()) {\n  if (t.hasException()) {\n    new (&this->e_) exception_wrapper(t.exception());\n  }\n}\n\ntemplate <typename T>\ntemplate <typename... Args>\nT& Try<T>::emplace(Args&&... args) noexcept(\n    std::is_nothrow_constructible<T, Args&&...>::value) {\n  this->destroy();\n  ::new (static_cast<void*>(std::addressof(this->value_)))\n      T(static_cast<Args&&>(args)...);\n  this->contains_ = Contains::VALUE;\n  return this->value_;\n}\n\ntemplate <typename T>\ntemplate <typename... Args>\nexception_wrapper& Try<T>::emplaceException(Args&&... args) noexcept(\n    std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) {\n  this->destroy();\n  new (&this->e_) exception_wrapper(static_cast<Args&&>(args)...);\n  this->contains_ = Contains::EXCEPTION;\n  return this->e_;\n}\n\ntemplate <class T>\nT& Try<T>::value() & {\n  throwUnlessValue();\n  return this->value_;\n}\n\ntemplate <class T>\nT&& Try<T>::value() && {\n  throwUnlessValue();\n  return std::move(this->value_);\n}\n\ntemplate <class T>\nconst T& Try<T>::value() const& {\n  throwUnlessValue();\n  return this->value_;\n}\n\ntemplate <class T>\nconst T&& Try<T>::value() const&& {\n  throwUnlessValue();\n  return std::move(this->value_);\n}\n\ntemplate <class T>\ntemplate <class U>\nT Try<T>::value_or(U&& defaultValue) const& {\n  return hasValue() ? **this : static_cast<T>(static_cast<U&&>(defaultValue));\n}\n\ntemplate <class T>\ntemplate <class U>\nT Try<T>::value_or(U&& defaultValue) && {\n  return hasValue()\n      ? std::move(**this)\n      : static_cast<T>(static_cast<U&&>(defaultValue));\n}\n\ntemplate <class T>\nvoid Try<T>::throwUnlessValue() const {\n  switch (this->contains_) {\n    case Contains::VALUE:\n      return;\n    case Contains::EXCEPTION:\n      this->e_.throw_exception();\n    case Contains::NOTHING:\n    default:\n      throw_exception<UsingUninitializedTry>();\n  }\n}\n\ntemplate <class T>\nvoid Try<T>::throwIfFailed() const {\n  throwUnlessValue();\n}\n\nTry<void>& Try<void>::operator=(const Try<void>& t) noexcept {\n  if (t.hasException()) {\n    if (hasException()) {\n      this->e_ = t.e_;\n    } else {\n      new (&this->e_) exception_wrapper(t.e_);\n      hasValue_ = false;\n    }\n  } else {\n    if (hasException()) {\n      this->e_.~exception_wrapper();\n      hasValue_ = true;\n    }\n  }\n  return *this;\n}\n\ntemplate <typename... Args>\nexception_wrapper& Try<void>::emplaceException(Args&&... args) noexcept(\n    std::is_nothrow_constructible<exception_wrapper, Args&&...>::value) {\n  if (hasException()) {\n    this->e_.~exception_wrapper();\n  }\n  new (&this->e_) exception_wrapper(static_cast<Args&&>(args)...);\n  hasValue_ = false;\n  return this->e_;\n}\n\nvoid Try<void>::throwIfFailed() const {\n  if (hasException()) {\n    this->e_.throw_exception();\n  }\n}\n\nvoid Try<void>::throwUnlessValue() const {\n  throwIfFailed();\n}\n\ntemplate <typename F>\ntypename std::enable_if<\n    !std::is_same<invoke_result_t<F>, void>::value,\n    Try<invoke_result_t<F>>>::type\nmakeTryWithNoUnwrap(F&& f) noexcept {\n  using ResultType = invoke_result_t<F>;\n  try {\n    return Try<ResultType>(f());\n  } catch (...) {\n    return Try<ResultType>(exception_wrapper(current_exception()));\n  }\n}\n\ntemplate <typename F>\ntypename std::\n    enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type\n    makeTryWithNoUnwrap(F&& f) noexcept {\n  try {\n    f();\n    return Try<void>();\n  } catch (...) {\n    return Try<void>(exception_wrapper(current_exception()));\n  }\n}\n\ntemplate <typename F>\ntypename std::\n    enable_if<!isTry<invoke_result_t<F>>::value, Try<invoke_result_t<F>>>::type\n    makeTryWith(F&& f) noexcept {\n  return makeTryWithNoUnwrap(std::forward<F>(f));\n}\n\ntemplate <typename F>\ntypename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>::\n    type\n    makeTryWith(F&& f) noexcept {\n  using ResultType = invoke_result_t<F>;\n  try {\n    return f();\n  } catch (...) {\n    return ResultType(exception_wrapper(current_exception()));\n  }\n}\n\ntemplate <typename T, typename... Args>\nT* tryEmplace(Try<T>& t, Args&&... args) noexcept {\n  try {\n    return std::addressof(t.emplace(static_cast<Args&&>(args)...));\n  } catch (...) {\n    t.emplaceException(current_exception());\n    return nullptr;\n  }\n}\n\nvoid tryEmplace(Try<void>& t) noexcept {\n  t.emplace();\n}\n\ntemplate <typename T, typename Func>\nT* tryEmplaceWith(Try<T>& t, Func&& func) noexcept {\n  static_assert(\n      std::is_constructible<T, folly::invoke_result_t<Func>>::value,\n      \"Unable to initialise a value of type T with the result of 'func'\");\n  try {\n    return std::addressof(t.emplace(static_cast<Func&&>(func)()));\n  } catch (...) {\n    t.emplaceException(current_exception());\n    return nullptr;\n  }\n}\n\ntemplate <typename Func>\nbool tryEmplaceWith(Try<void>& t, Func&& func) noexcept {\n  static_assert(\n      std::is_void<folly::invoke_result_t<Func>>::value,\n      \"Func returns non-void. Cannot be used to emplace Try<void>\");\n  try {\n    static_cast<Func&&>(func)();\n    t.emplace();\n    return true;\n  } catch (...) {\n    t.emplaceException(current_exception());\n    return false;\n  }\n}\n\nnamespace try_detail {\n\n/**\n * Trait that removes the layer of Try abstractions from the passed in type\n */\ntemplate <typename Type>\nstruct RemoveTry;\ntemplate <template <typename...> class TupleType, typename... Types>\nstruct RemoveTry<TupleType<folly::Try<Types>...>> {\n  using type = TupleType<Types...>;\n};\n\ntemplate <std::size_t... Indices, typename Tuple>\nauto unwrapTryTupleImpl(std::index_sequence<Indices...>, Tuple&& instance) {\n  using std::get;\n  using ReturnType = typename RemoveTry<typename std::decay<Tuple>::type>::type;\n  return ReturnType{(get<Indices>(std::forward<Tuple>(instance)).value())...};\n}\n} // namespace try_detail\n\ntemplate <typename Tuple>\nauto unwrapTryTuple(Tuple&& instance) {\n  using TupleDecayed = typename std::decay<Tuple>::type;\n  using Seq = std::make_index_sequence<std::tuple_size<TupleDecayed>::value>;\n  return try_detail::unwrapTryTupleImpl(Seq{}, std::forward<Tuple>(instance));\n}\n\ntemplate <typename T>\nvoid tryAssign(Try<T>& t, Try<T>&& other) noexcept {\n  try {\n    t = std::move(other);\n  } catch (...) {\n    t.emplaceException(current_exception());\n  }\n}\n\n// limited to the instances unconditionally forced by the futures library\nextern template class Try<Unit>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Try.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Try.h>\n\nnamespace folly {\n\ntemplate class Try<Unit>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Try.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <exception>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/Likely.h>\n#include <folly/Memory.h>\n#include <folly/Portability.h>\n#include <folly/Unit.h>\n#include <folly/Utility.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nclass FOLLY_EXPORT TryException : public std::logic_error {\n public:\n  using std::logic_error::logic_error;\n  TryException() : std::logic_error{\"\"} {}\n};\n\nclass FOLLY_EXPORT UsingUninitializedTry : public TryException {\n public:\n  UsingUninitializedTry() = default;\n  char const* what() const noexcept override {\n    return \"Using uninitialized try\";\n  }\n};\n\ntemplate <class T>\nclass Try;\n\nnamespace detail {\ntemplate <class T>\nclass TryBase {\n protected:\n  enum class Contains {\n    VALUE,\n    EXCEPTION,\n    NOTHING,\n  };\n\n public:\n  /*\n   * Construct an empty Try\n   */\n  TryBase() noexcept : contains_(Contains::NOTHING) {}\n\n  /*\n   * Construct a Try with a value by copy\n   *\n   * @param v The value to copy in\n   */\n  explicit TryBase(const T& v) noexcept(\n      std::is_nothrow_copy_constructible<T>::value)\n      : contains_(Contains::VALUE), value_(v) {}\n\n  /*\n   * Construct a Try with a value by move\n   *\n   * @param v The value to move in\n   */\n  explicit TryBase(T&& v) noexcept(std::is_nothrow_move_constructible<T>::value)\n      : contains_(Contains::VALUE), value_(std::move(v)) {}\n\n  template <typename... Args>\n  explicit TryBase(std::in_place_t, Args&&... args) noexcept(\n      std::is_nothrow_constructible<T, Args&&...>::value)\n      : contains_(Contains::VALUE), value_(static_cast<Args&&>(args)...) {}\n\n  /// Implicit conversion from Try<void> to Try<Unit>\n  template <class T2 = T>\n  /* implicit */ TryBase(\n      typename std::enable_if<std::is_same<Unit, T2>::value, Try<void> const&>::\n          type t) noexcept;\n\n  /*\n   * Construct a Try with an exception_wrapper\n   *\n   * @param e The exception_wrapper\n   */\n  explicit TryBase(exception_wrapper e) noexcept\n      : contains_(Contains::EXCEPTION), e_(std::move(e)) {}\n\n  ~TryBase();\n\n  // Move constructor\n  TryBase(TryBase&& t) noexcept(std::is_nothrow_move_constructible<T>::value);\n  // Move assigner\n  TryBase& operator=(TryBase&& t) noexcept(\n      std::is_nothrow_move_constructible<T>::value);\n\n  // Copy constructor\n  TryBase(const TryBase& t) noexcept(\n      std::is_nothrow_copy_constructible<T>::value);\n  // Copy assigner\n  TryBase& operator=(const TryBase& t) noexcept(\n      std::is_nothrow_copy_constructible<T>::value);\n\n protected:\n  void destroy() noexcept;\n\n  Contains contains_;\n  union {\n    T value_;\n    exception_wrapper e_;\n  };\n};\n} // namespace detail\n\n/*\n * Try<T> is a wrapper that contains either an instance of T, an exception, or\n * nothing. Exceptions are stored as exception_wrappers so that the user can\n * minimize rethrows if so desired.\n *\n * To represent success or a captured exception, use Try<Unit>.\n */\ntemplate <class T>\nclass Try\n    : detail::TryBase<T>,\n      moveonly_::EnableCopyMove<\n          std::is_copy_constructible<T>::value,\n          std::is_move_constructible<T>::value> {\n  static_assert(\n      !std::is_reference<T>::value, \"Try may not be used with reference types\");\n  using typename detail::TryBase<T>::Contains;\n\n public:\n  using detail::TryBase<T>::TryBase;\n\n  /*\n   * The value type for the Try\n   */\n  using element_type = T;\n\n  /*\n   * In-place construct the value in the Try object.\n   *\n   * Destroys any previous value prior to constructing the new value.\n   * Leaves *this in an empty state if the construction of T throws.\n   *\n   * @returns reference to the newly constructed value.\n   */\n  template <typename... Args>\n  T& emplace(Args&&... args) noexcept(\n      std::is_nothrow_constructible<T, Args&&...>::value);\n\n  /*\n   * In-place construct an exception in the Try object.\n   *\n   * Destroys any previous value prior to constructing the new value.\n   * Leaves *this in an empty state if the construction of the exception_wrapper\n   * throws.\n   *\n   * Any arguments passed to emplaceException() are forwarded on to the\n   * exception_wrapper constructor.\n   *\n   * @returns reference to the newly constructed exception_wrapper.\n   */\n  template <typename... Args>\n  exception_wrapper& emplaceException(Args&&... args) noexcept(\n      std::is_nothrow_constructible<exception_wrapper, Args&&...>::value);\n\n  /*\n   * Get a mutable reference to the contained value.\n   * [Re]throws if the Try contains an exception or is empty.\n   *\n   * @returns mutable reference to the contained value\n   */\n  T& value() &;\n  /*\n   * Get a rvalue reference to the contained value.\n   * [Re]throws if the Try contains an exception or is empty.\n   *\n   * @returns rvalue reference to the contained value\n   */\n  T&& value() &&;\n  /*\n   * Get a const reference to the contained value.\n   * [Re]throws if the Try contains an exception or is empty.\n   *\n   * @returns const reference to the contained value\n   */\n  const T& value() const&;\n  /*\n   * Get a const rvalue reference to the contained value.\n   * [Re]throws if the Try contains an exception or is empty.\n   *\n   * @returns const rvalue reference to the contained value\n   */\n  const T&& value() const&&;\n\n  /*\n   * Returns a copy of the contained value if *this has a value,\n   * otherwise returns a value constructed from defaultValue.\n   *\n   * The selected constructor of the return value may throw exceptions.\n   */\n  template <class U>\n  T value_or(U&& defaultValue) const&;\n  template <class U>\n  T value_or(U&& defaultValue) &&;\n\n  /*\n   * [Re]throw if the Try contains an exception or is empty. Otherwise do\n   * nothing.\n   */\n  void throwUnlessValue() const;\n  [[deprecated(\"Replaced by throwUnlessValue\")]] void throwIfFailed() const;\n\n  /*\n   * Const dereference operator.\n   * [Re]throws if the Try contains an exception or is empty.\n   *\n   * @returns const reference to the contained value\n   */\n  const T& operator*() const& { return value(); }\n  /*\n   * Dereference operator. If the Try contains an exception it will be rethrown.\n   *\n   * @returns mutable reference to the contained value\n   */\n  T& operator*() & { return value(); }\n  /*\n   * Mutable rvalue dereference operator.  If the Try contains an exception it\n   * will be rethrown.\n   *\n   * @returns rvalue reference to the contained value\n   */\n  T&& operator*() && { return std::move(value()); }\n  /*\n   * Const rvalue dereference operator.  If the Try contains an exception it\n   * will be rethrown.\n   *\n   * @returns rvalue reference to the contained value\n   */\n  const T&& operator*() const&& { return std::move(value()); }\n\n  /*\n   * Const arrow operator.\n   * [Re]throws if the Try contains an exception or is empty.\n   *\n   * @returns const reference to the contained value\n   */\n  const T* operator->() const { return &value(); }\n  /*\n   * Arrow operator. If the Try contains an exception it will be rethrown.\n   *\n   * @returns mutable reference to the contained value\n   */\n  T* operator->() { return &value(); }\n\n  /*\n   * @returns True if the Try contains a value, false otherwise\n   */\n  bool hasValue() const noexcept { return this->contains_ == Contains::VALUE; }\n  /*\n   * @returns True if the Try contains an exception, false otherwise\n   */\n  bool hasException() const noexcept {\n    return this->contains_ == Contains::EXCEPTION;\n  }\n\n  /*\n   * @returns True if the Try contains an exception of type Ex, false otherwise\n   */\n  template <class Ex>\n  bool hasException() const noexcept {\n    return hasException() && this->e_.template is_compatible_with<Ex>();\n  }\n\n  exception_wrapper& exception() & {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return this->e_;\n  }\n\n  exception_wrapper&& exception() && {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return std::move(this->e_);\n  }\n\n  const exception_wrapper& exception() const& {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return this->e_;\n  }\n\n  const exception_wrapper&& exception() const&& {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return std::move(this->e_);\n  }\n\n  /*\n   * @returns a pointer to the `std::exception` held by `*this`, if one is held;\n   *          otherwise, returns `nullptr`.\n   */\n  std::exception const* tryGetExceptionObject() const noexcept {\n    return hasException() ? this->e_.get_exception() : nullptr;\n  }\n\n  /*\n   * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose\n   *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,\n   *          returns `nullptr`.\n   */\n  template <class Ex>\n  Ex const* tryGetExceptionObject() const noexcept {\n    return hasException() ? this->e_.template get_exception<Ex>() : nullptr;\n  }\n\n  /*\n   * If the Try contains an exception and it is of type Ex, execute func(Ex)\n   *\n   * @param func a function that takes a single parameter of type const Ex&\n   *\n   * @returns True if the Try held an Ex and func was executed, false otherwise\n   */\n  template <class Ex, class F>\n  bool withException(F func) {\n    if (!hasException()) {\n      return false;\n    }\n    return this->e_.template with_exception<Ex>(std::move(func));\n  }\n  template <class Ex, class F>\n  bool withException(F func) const {\n    if (!hasException()) {\n      return false;\n    }\n    return this->e_.template with_exception<Ex>(std::move(func));\n  }\n\n  /*\n   * If the Try contains an exception and it is of type compatible with Ex as\n   * deduced from the first parameter of func, execute func(Ex)\n   *\n   * @param func a function that takes a single parameter of type const Ex&\n   *\n   * @returns True if the Try held an Ex and func was executed, false otherwise\n   */\n  template <class F>\n  bool withException(F func) {\n    if (!hasException()) {\n      return false;\n    }\n    return this->e_.with_exception(std::move(func));\n  }\n  template <class F>\n  bool withException(F func) const {\n    if (!hasException()) {\n      return false;\n    }\n    return this->e_.with_exception(std::move(func));\n  }\n\n  template <bool isTry, typename R>\n  typename std::enable_if<isTry, R>::type get() {\n    return std::forward<R>(*this);\n  }\n\n  template <bool isTry, typename R>\n  typename std::enable_if<!isTry, R>::type get() {\n    return std::forward<R>(value());\n  }\n};\n\n/*\n * Specialization of Try for void value type. Encapsulates either success or an\n * exception.\n */\ntemplate <>\nclass Try<void> {\n public:\n  /*\n   * The value type for the Try\n   */\n  using element_type = void;\n\n  // Construct a Try holding a successful and void result\n  Try() noexcept : hasValue_(true) {}\n\n  /*\n   * Construct a Try with an exception_wrapper\n   *\n   * @param e The exception_wrapper\n   */\n  explicit Try(exception_wrapper e) noexcept\n      : hasValue_(false), e_(std::move(e)) {}\n\n  /// Implicit conversion from Try<Unit> to Try<void>\n  /* implicit */ inline Try(const Try<Unit>& t) noexcept;\n\n  // Copy assigner\n  inline Try& operator=(const Try<void>& t) noexcept;\n\n  // Copy constructor\n  Try(const Try<void>& t) noexcept : hasValue_(t.hasValue_) {\n    if (t.hasException()) {\n      new (&e_) exception_wrapper(t.e_);\n    }\n  }\n\n  ~Try() {\n    if (hasException()) {\n      e_.~exception_wrapper();\n    }\n  }\n\n  /*\n   * In-place construct a 'void' value into this Try object.\n   *\n   * This has the effect of clearing any existing exception stored in the\n   * Try object.\n   */\n  void emplace() noexcept {\n    if (hasException()) {\n      e_.~exception_wrapper();\n      hasValue_ = true;\n    }\n  }\n\n  /*\n   * In-place construct an exception in the Try object.\n   *\n   * Destroys any previous value prior to constructing the new value.\n   * Leaves *this in an empty state if the construction of the exception_wrapper\n   * throws.\n   *\n   * Any arguments passed to emplaceException() are forwarded on to the\n   * exception_wrapper constructor.\n   *\n   * @returns reference to the newly constructed exception_wrapper.\n   */\n  template <typename... Args>\n  exception_wrapper& emplaceException(Args&&... args) noexcept(\n      std::is_nothrow_constructible<exception_wrapper, Args&&...>::value);\n\n  // If the Try contains an exception, throws it\n  void value() const { throwIfFailed(); }\n  // Dereference operator. If the Try contains an exception, throws it\n  void operator*() const { return value(); }\n\n  // If the Try contains an exception, throws it\n  inline void throwIfFailed() const;\n  inline void throwUnlessValue() const;\n\n  // @returns False if the Try contains an exception, true otherwise\n  bool hasValue() const noexcept { return hasValue_; }\n  // @returns True if the Try contains an exception, false otherwise\n  bool hasException() const noexcept { return !hasValue_; }\n\n  // @returns True if the Try contains an exception of type Ex, false otherwise\n  template <class Ex>\n  bool hasException() const noexcept {\n    return hasException() && e_.is_compatible_with<Ex>();\n  }\n\n  /*\n   * @throws TryException if the Try doesn't contain an exception\n   *\n   * @returns mutable reference to the exception contained by this Try\n   */\n  exception_wrapper& exception() & {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return e_;\n  }\n\n  exception_wrapper&& exception() && {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return std::move(e_);\n  }\n\n  const exception_wrapper& exception() const& {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return e_;\n  }\n\n  const exception_wrapper&& exception() const&& {\n    if (!hasException()) {\n      throw_exception<TryException>(\"Try does not contain an exception\");\n    }\n    return std::move(e_);\n  }\n\n  /*\n   * @returns a pointer to the `std::exception` held by `*this`, if one is held;\n   *          otherwise, returns `nullptr`.\n   */\n  std::exception const* tryGetExceptionObject() const noexcept {\n    return hasException() ? e_.get_exception() : nullptr;\n  }\n\n  /*\n   * @returns a pointer to the `Ex` held by `*this`, if it holds an object whose\n   *          type `From` permits `std::is_convertible<From*, Ex*>`; otherwise,\n   *          returns `nullptr`.\n   */\n  template <class E>\n  E const* tryGetExceptionObject() const noexcept {\n    return hasException() ? e_.get_exception<E>() : nullptr;\n  }\n\n  /*\n   * If the Try contains an exception and it is of type Ex, execute func(Ex)\n   *\n   * @param func a function that takes a single parameter of type const Ex&\n   *\n   * @returns True if the Try held an Ex and func was executed, false otherwise\n   */\n  template <class Ex, class F>\n  bool withException(F func) {\n    if (!hasException()) {\n      return false;\n    }\n    return e_.with_exception<Ex>(std::move(func));\n  }\n  template <class Ex, class F>\n  bool withException(F func) const {\n    if (!hasException()) {\n      return false;\n    }\n    return e_.with_exception<Ex>(std::move(func));\n  }\n\n  /*\n   * If the Try contains an exception and it is of type compatible with Ex as\n   * deduced from the first parameter of func, execute func(Ex)\n   *\n   * @param func a function that takes a single parameter of type const Ex&\n   *\n   * @returns True if the Try held an Ex and func was executed, false otherwise\n   */\n  template <class F>\n  bool withException(F func) {\n    if (!hasException()) {\n      return false;\n    }\n    return e_.with_exception(std::move(func));\n  }\n  template <class F>\n  bool withException(F func) const {\n    if (!hasException()) {\n      return false;\n    }\n    return e_.with_exception(std::move(func));\n  }\n\n  template <bool, typename R>\n  R get() {\n    return std::forward<R>(*this);\n  }\n\n private:\n  bool hasValue_;\n  union {\n    exception_wrapper e_;\n  };\n};\n\ntemplate <typename T>\nstruct isTry : std::false_type {};\n\ntemplate <typename T>\nstruct isTry<Try<T>> : std::true_type {};\n\n/*\n * @param f a function to execute and capture the result of (value or exception)\n *\n * @returns Try holding the result of f\n */\ntemplate <typename F>\ntypename std::enable_if<\n    !std::is_same<invoke_result_t<F>, void>::value,\n    Try<invoke_result_t<F>>>::type\nmakeTryWithNoUnwrap(F&& f) noexcept;\n\n/*\n * Specialization of makeTryWith for void return\n *\n * @param f a function to execute and capture the result of\n *\n * @returns Try<void> holding the result of f\n */\ntemplate <typename F>\ntypename std::\n    enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type\n    makeTryWithNoUnwrap(F&& f) noexcept;\n\n/*\n * @param f a function to execute and capture the result of (value or exception)\n *\n * @returns Try holding the result of f\n */\ntemplate <typename F>\ntypename std::\n    enable_if<!isTry<invoke_result_t<F>>::value, Try<invoke_result_t<F>>>::type\n    makeTryWith(F&& f) noexcept;\n\n/*\n * Specialization of makeTryWith for functions that return Try<T>\n * Causes makeTryWith to not double-wrap the try.\n *\n * @param f a function to execute and capture the result of\n *\n * @returns result of f if f did not throw. Otherwise Try<T> containing\n * exception\n */\ntemplate <typename F>\ntypename std::enable_if<isTry<invoke_result_t<F>>::value, invoke_result_t<F>>::\n    type\n    makeTryWith(F&& f) noexcept;\n\n/*\n * Try to in-place construct a new value from the specified arguments.\n *\n * If T's constructor throws an exception then this is caught and the Try<T>\n * object is initialised to hold that exception.\n *\n * @param args Are passed to T's constructor.\n */\ntemplate <typename T, typename... Args>\nT* tryEmplace(Try<T>& t, Args&&... args) noexcept;\n\n/*\n * Overload of tryEmplace() for Try<void>.\n */\ninline void tryEmplace(Try<void>& t) noexcept;\n\n/*\n * Try to in-place construct a new value from the result of a function.\n *\n * If the function completes successfully then attempts to in-place construct\n * a value of type, T, passing the result of the function as the only parameter.\n *\n * If either the call to the function completes with an exception or the\n * constructor completes with an exception then the exception is caught and\n * stored in the Try object.\n *\n * @returns A pointer to the newly constructed object if it completed\n * successfully, otherwise returns nullptr if the operation completed with\n * an exception.\n */\ntemplate <typename T, typename Func>\nT* tryEmplaceWith(Try<T>& t, Func&& func) noexcept;\n\n/*\n * Specialization of tryEmplaceWith() for Try<void>.\n *\n * Calls func() and if it doesn't throw an exception then calls t.emplace().\n * If func() throws then captures the exception in t using t.emplaceException().\n *\n * Func must be callable with zero parameters and must return void.\n *\n * @returns true if func() completed without an exception, false if func()\n * threw an exception.\n */\ntemplate <typename Func>\nbool tryEmplaceWith(Try<void>& t, Func&& func) noexcept;\n\n/**\n * Tuple<Try<Type>...> -> std::tuple<Type...>\n *\n * Unwraps a tuple-like type containing a sequence of Try<Type> instances to\n * std::tuple<Type>\n */\ntemplate <typename Tuple>\nauto unwrapTryTuple(Tuple&&);\n\n/*\n * Try to move the value/exception from another Try object.\n *\n * If T's constructor throws an exception then this is caught and the Try<T>\n * object is initialised to hold that exception.\n */\ntemplate <typename T>\nvoid tryAssign(Try<T>& t, Try<T>&& other) noexcept;\n\ntemplate <typename T>\nTry(T&&) -> Try<std::decay_t<T>>;\n\n} // namespace folly\n\n#include <folly/Try-inl.h>\n"
  },
  {
    "path": "folly/UTF8String.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <boost/regex/pending/unicode_iterator.hpp>\n\n#include <folly/Range.h>\n\nnamespace folly {\n\nclass UTF8StringPiece {\n public:\n  using iterator = boost::u8_to_u32_iterator<const char*>;\n  using size_type = std::size_t;\n\n  /* implicit */ UTF8StringPiece(const folly::StringPiece piece)\n      : begin_{piece.begin(), piece.begin(), piece.end()},\n        end_{piece.end(), piece.begin(), piece.end()} {}\n  template <\n      typename T,\n      std::enable_if_t<std::is_convertible_v<T, folly::StringPiece>, int> = 0>\n  /* implicit */ UTF8StringPiece(const T& t)\n      : UTF8StringPiece(folly::StringPiece(t)) {}\n\n  iterator begin() const noexcept { return begin_; }\n  iterator cbegin() const noexcept { return begin_; }\n  iterator end() const noexcept { return end_; }\n  iterator cend() const noexcept { return end_; }\n\n  bool empty() const noexcept { return begin_ == end_; }\n  size_type walk_size() const {\n    return static_cast<size_type>(std::distance(begin_, end_));\n  }\n\n private:\n  iterator begin_;\n  iterator end_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Unicode.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Unicode.h>\n\n#include <initializer_list>\n\n#include <folly/Conv.h>\n\nnamespace folly {\n\nunicode_code_point_utf8 unicode_code_point_to_utf8(char32_t const cp) {\n  // Based on description from http://en.wikipedia.org/wiki/UTF-8.\n\n  auto const ret = [](std::initializer_list<char> const ilist) {\n    unicode_code_point_utf8 val{to_narrow(ilist.size()), {}};\n    std::memcpy(val.data, ilist.begin(), ilist.size());\n    return val;\n  };\n\n  if (cp <= 0x7f) {\n    return ret({\n        static_cast<char>(cp),\n    });\n  } else if (cp <= 0x7FF) {\n    return ret({\n        static_cast<char>(0xC0 | (cp >> 6)),\n        static_cast<char>(0x80 | (0x3f & cp)),\n    });\n  } else if (cp <= 0xFFFF) {\n    return ret({\n        static_cast<char>(0xE0 | (cp >> 12)),\n        static_cast<char>(0x80 | (0x3f & (cp >> 6))),\n        static_cast<char>(0x80 | (0x3f & cp)),\n    });\n  } else if (cp <= 0x10FFFF) {\n    return ret({\n        static_cast<char>(0xF0 | (cp >> 18)),\n        static_cast<char>(0x80 | (0x3f & (cp >> 12))),\n        static_cast<char>(0x80 | (0x3f & (cp >> 6))),\n        static_cast<char>(0x80 | (0x3f & cp)),\n    });\n  } else {\n    return {0, {}};\n  }\n}\n\nvoid appendCodePointToUtf8(char32_t const cp, std::string& out) {\n  auto const utf8 = unicode_code_point_to_utf8(cp);\n  out.append(reinterpret_cast<char const*>(utf8.data), utf8.size);\n}\n\nstd::string codePointToUtf8(char32_t const cp) {\n  std::string result;\n  appendCodePointToUtf8(cp, result);\n  return result;\n}\n\nchar32_t utf8ToCodePoint(\n    const unsigned char*& p, const unsigned char* const e, bool skipOnError) {\n  // clang-format off\n  /** UTF encodings\n  *  | # of B | First CP |  Last CP  | Bit Pattern\n  *  |   1    |   0x0000 |   0x007F  | 0xxxxxxx\n  *  |   2    |   0x0080 |   0x07FF  | 110xxxxx 10xxxxxx\n  *  |   3    |   0x0800 |   0xFFFF  | 1110xxxx 10xxxxxx 10xxxxxx\n  *  |   4    |  0x10000 | 0x10FFFF  | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n  *  |   5    |       -  |        -  | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx\n  *  |   6    |       -  |        -  | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx\n  *\n  *\n  * NOTE:\n  * - the 4B encoding can encode values up to 0x1FFFFF,\n  *   but Unicode defines 0x10FFFF to be the largest code point\n  * - the 5B & 6B encodings will all encode values larger than 0x1FFFFF\n  *   (so larger than the largest code point value 0x10FFFF) so they form invalid\n  *   Unicode code points\n  *\n  * On invalid input (invalid encoding or code points larger than 0x10FFFF):\n  * - When skipOnError is true, this function will skip the first byte and return\n  *   U'\\ufffd'. Potential optimization: skip the whole invalid range.\n  * - When skipOnError is false, throws.\n  */\n  // clang-format on\n\n  const auto skip = [&] {\n    ++p;\n    return U'\\ufffd';\n  };\n\n  if (p >= e) {\n    if (skipOnError) {\n      return skip();\n    }\n    throw std::runtime_error(\"folly::utf8ToCodePoint empty/invalid string\");\n  }\n\n  unsigned char fst = *p;\n  if (!(fst & 0x80)) {\n    // trivial case, 1 byte encoding\n    return *p++;\n  }\n\n  static const uint32_t bitMask[] = {\n      (1 << 7) - 1,\n      (1 << 11) - 1,\n      (1 << 16) - 1,\n      (1 << 21) - 1,\n  };\n\n  // upper control bits are masked out later\n  uint32_t d = fst;\n\n  // multi-byte encoded values must start with bits 0b11. 0xC0 is 0b11000000\n  if ((fst & 0xC0) != 0xC0) {\n    if (skipOnError) {\n      return skip();\n    }\n    throw std::runtime_error(\n        to<std::string>(\"folly::utf8ToCodePoint i=0 d=\", d));\n  }\n\n  fst <<= 1;\n\n  for (unsigned int i = 1; i != 4 && p + i < e; ++i) {\n    const unsigned char tmp = p[i];\n\n    // from the second byte on, format should be 10xxxxxx\n    if ((tmp & 0xC0) != 0x80) {\n      if (skipOnError) {\n        return skip();\n      }\n      throw std::runtime_error(\n          to<std::string>(\n              \"folly::utf8ToCodePoint i=\", i, \" tmp=\", (uint32_t)tmp));\n    }\n\n    // gradually fill a 32 bit integer d with non control bits in tmp\n    // 0x3F is 0b00111111 which clears out the first 2 control bits\n    d = (d << 6) | (tmp & 0x3F);\n    fst <<= 1;\n\n    if (!(fst & 0x80)) {\n      // We know the length of encoding now, since we encounter the first \"0\" in\n      // fst (the first byte). This branch processes the last byte of encoding.\n      d &= bitMask[i]; // d is now the code point\n\n      // overlong, could have been encoded with i bytes\n      if ((d & ~bitMask[i - 1]) == 0) {\n        if (skipOnError) {\n          return skip();\n        }\n        throw std::runtime_error(\n            to<std::string>(\"folly::utf8ToCodePoint i=\", i, \" d=\", d));\n      }\n\n      // check for surrogates only needed for 3 bytes\n      if (i == 2) {\n        if (d >= 0xD800 && d <= 0xDFFF) {\n          if (skipOnError) {\n            return skip();\n          }\n          throw std::runtime_error(\n              to<std::string>(\"folly::utf8ToCodePoint i=\", i, \" d=\", d));\n        }\n      }\n\n      // While UTF-8 encoding can encode arbitrary numbers, 0x10FFFF is the\n      // largest defined Unicode code point.\n      // Only >=4 bytes can UTF-8 encode such values, so i=3 here.\n      if (d > 0x10FFFF) {\n        if (skipOnError) {\n          return skip();\n        }\n        throw std::runtime_error(\n            \"folly::utf8ToCodePoint encoding exceeds max unicode code point\");\n      }\n      p += i + 1;\n      return d;\n    }\n  }\n\n  if (skipOnError) {\n    return skip();\n  }\n  throw std::runtime_error(\"folly::utf8ToCodePoint encoding length maxed out\");\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Unicode.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Some utility routines relating to Unicode.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <stdexcept>\n#include <string>\n\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nclass FOLLY_EXPORT unicode_error : public std::runtime_error {\n public:\n  using std::runtime_error::runtime_error;\n};\n\n//  Unicode code points are split into 17 planes.\n//\n//  The Basic Multilingual Plane covers code points in [0-0xFFFF] but reserves\n//  two invalid ranges:\n//  - High surrogates: [0xD800-0xDBFF].\n//  - Low surrogates: [0xDC00-0xDFFF].\n//\n//  UTF-16 code units are 2 bytes wide and are represented here with char16_t.\n//  Unicode code points are represented in UTF-16 across either 1-2 code units:\n//  - Valid BMP code points [0x0000-0xD7FF] + [0xE000-0xFFFF] are encoded\n//    directly as 1 code unit.\n//  - Code points larger than BMP (>0xFFFF) are encoded as 2 code units, with\n//    values respectively in the high surrogates and low surrogates ranges.\n//\n//  JSON text permits the inclusion of Unicode escape sequences within quoted\n//  strings:\n//  - Valid BMP code points are encoded as \\xXXXX, where XXXX are the base-16\n//    digits of the code point.\n//  - Code points larger than BMP are encoded as \\uHHHH\\uLLLL, where HHHH and\n//    LLLL are respectively the base-16 digits of the high and low surrogates of\n//    the UTF-16 encoding of the code point.\n\ninline bool utf16_code_unit_is_bmp(char16_t const c) {\n  return c < 0xd800 || c >= 0xe000;\n}\ninline bool utf16_code_unit_is_high_surrogate(char16_t const c) {\n  return c >= 0xd800 && c < 0xdc00;\n}\ninline bool utf16_code_unit_is_low_surrogate(char16_t const c) {\n  return c >= 0xdc00 && c < 0xe000;\n}\ninline char32_t unicode_code_point_from_utf16_surrogate_pair(\n    char16_t const high, char16_t const low) {\n  if (!utf16_code_unit_is_high_surrogate(high)) {\n    throw_exception<unicode_error>(\"invalid high surrogate\");\n  }\n  if (!utf16_code_unit_is_low_surrogate(low)) {\n    throw_exception<unicode_error>(\"invalid low surrogate\");\n  }\n  return 0x10000 + ((char32_t(high) & 0x3ff) << 10) + (char32_t(low) & 0x3ff);\n}\n\n//////////////////////////////////////////////////////////////////////\n\nstruct unicode_code_point_utf8 {\n  uint32_t size;\n  uint8_t data[4];\n};\nunicode_code_point_utf8 unicode_code_point_to_utf8(char32_t cp);\n\n/*\n * Encode a single Unicode code point into a UTF-8 byte sequence.\n *\n * Result is undefined if `cp' is an invalid code point.\n */\nvoid appendCodePointToUtf8(char32_t cp, std::string& out);\nstd::string codePointToUtf8(char32_t cp);\n\n/*\n * Decode a single Unicode code point from UTF-8 byte sequence.\n */\nchar32_t utf8ToCodePoint(\n    const unsigned char*& p, const unsigned char* const e, bool skipOnError);\n\n//////////////////////////////////////////////////////////////////////\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Unit.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\nnamespace folly {\n\n/// In functional programming, the degenerate case is often called \"unit\". In\n/// C++, \"void\" is often the best analogue. However, because of the syntactic\n/// special-casing required for void, it is frequently a liability for template\n/// metaprogramming. So, instead of writing specializations to handle cases like\n/// SomeContainer<void>, a library author may instead rule that out and simply\n/// have library users use SomeContainer<Unit>. Contained values may be ignored.\n/// Much easier.\n///\n/// \"void\" is the type that admits of no values at all. It is not possible to\n/// construct a value of this type.\n/// \"unit\" is the type that admits of precisely one unique value. It is\n/// possible to construct a value of this type, but it is always the same value\n/// every time, so it is uninteresting.\nstruct Unit {\n  constexpr bool operator==(const Unit& /*other*/) const { return true; }\n  constexpr bool operator!=(const Unit& /*other*/) const { return false; }\n};\n\nconstexpr Unit unit{};\n\ntemplate <typename T>\nstruct lift_unit {\n  using type = T;\n};\ntemplate <>\nstruct lift_unit<void> {\n  using type = Unit;\n};\ntemplate <typename T>\nusing lift_unit_t = typename lift_unit<T>::type;\n\ntemplate <typename T>\nstruct drop_unit {\n  using type = T;\n};\ntemplate <>\nstruct drop_unit<Unit> {\n  using type = void;\n};\ntemplate <typename T>\nusing drop_unit_t = typename drop_unit<T>::type;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Uri-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_URI_H_\n#error This file may only be included from folly/Uri.h\n#endif\n\n#include <functional>\n#include <tuple>\n\n#include <folly/Conv.h>\n#include <folly/hash/Hash.h>\n\nnamespace folly {\n\nnamespace uri_detail {\n\nusing UriTuple = std::tuple<\n    const std::string&,\n    const std::string&,\n    const std::string&,\n    const std::string&,\n    uint16_t,\n    const std::string&,\n    const std::string&,\n    const std::string&>;\n\ninline UriTuple as_tuple(const folly::Uri& k) {\n  return UriTuple(\n      k.scheme(),\n      k.username(),\n      k.password(),\n      k.host(),\n      k.port(),\n      k.path(),\n      k.query(),\n      k.fragment());\n}\n\n} // namespace uri_detail\n\ntemplate <class String>\nString Uri::toString() const {\n  String str;\n  if (hasAuthority_) {\n    toAppend(scheme_, \"://\", &str);\n    if (!password_.empty()) {\n      toAppend(username_, ':', password_, '@', &str);\n    } else if (!username_.empty()) {\n      toAppend(username_, '@', &str);\n    }\n    toAppend(host_, &str);\n    if (port_ != 0) {\n      toAppend(':', port_, &str);\n    }\n  } else {\n    toAppend(scheme_, ':', &str);\n  }\n  toAppend(path_, &str);\n  if (!query_.empty()) {\n    toAppend('?', query_, &str);\n  }\n  if (!fragment_.empty()) {\n    toAppend('#', fragment_, &str);\n  }\n  return str;\n}\n\n} // namespace folly\n\nnamespace std {\n\ntemplate <>\nstruct hash<folly::Uri> {\n  std::size_t operator()(const folly::Uri& k) const {\n    return std::hash<folly::uri_detail::UriTuple>{}(\n        folly::uri_detail::as_tuple(k));\n  }\n};\n\ntemplate <>\nstruct equal_to<folly::Uri> {\n  bool operator()(const folly::Uri& a, const folly::Uri& b) const {\n    return folly::uri_detail::as_tuple(a) == folly::uri_detail::as_tuple(b);\n  }\n};\n\n} // namespace std\n"
  },
  {
    "path": "folly/Uri.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Uri.h>\n\n#include <algorithm>\n#include <cctype>\n\n#include <boost/regex.hpp>\n\nnamespace folly {\n\nnamespace {\n\nstd::string submatch(const boost::cmatch& m, int idx) {\n  const auto& sub = m[idx];\n  return std::string(sub.first, sub.second);\n}\n\n} // namespace\n\n// private default constructor\nUri::Uri() : hasAuthority_(false), port_(0) {}\n\n// public string constructor\nUri::Uri(StringPiece str) : hasAuthority_(false), port_(0) {\n  auto maybeUri = tryFromString(str);\n  if (maybeUri.hasError()) {\n    switch (maybeUri.error()) {\n      case UriFormatError::INVALID_URI_AUTHORITY:\n        throw std::invalid_argument(\n            to<std::string>(\"invalid URI authority: \", str));\n      case UriFormatError::INVALID_URI_PORT:\n        throw std::invalid_argument(to<std::string>(\"invalid URI port: \", str));\n      case UriFormatError::INVALID_URI:\n      default:\n        throw std::invalid_argument(to<std::string>(\"invalid URI: \", str));\n    }\n  }\n  *this = maybeUri.value();\n}\n\nExpected<Uri, UriFormatError> Uri::tryFromString(StringPiece str) noexcept {\n  Uri result;\n\n  static const boost::regex uriRegex(\n      \"([a-zA-Z][a-zA-Z0-9+.-]*):\" // scheme:\n      \"([^?#]*)\" // authority and path\n      \"(?:\\\\?([^#]*))?\" // ?query\n      \"(?:#(.*))?\"); // #fragment\n  static const boost::regex authorityAndPathRegex(\"//([^/]*)(/.*)?\");\n\n  boost::cmatch match;\n  if (FOLLY_UNLIKELY(\n          !boost::regex_match(str.begin(), str.end(), match, uriRegex))) {\n    return makeUnexpected(UriFormatError::INVALID_URI);\n  }\n\n  result.scheme_ = submatch(match, 1);\n  std::transform(\n      result.scheme_.begin(),\n      result.scheme_.end(),\n      result.scheme_.begin(),\n      ::tolower);\n\n  StringPiece authorityAndPath(match[2].first, match[2].second);\n  boost::cmatch authorityAndPathMatch;\n  if (!boost::regex_match(\n          authorityAndPath.begin(),\n          authorityAndPath.end(),\n          authorityAndPathMatch,\n          authorityAndPathRegex)) {\n    // Does not start with //, doesn't have authority\n    result.hasAuthority_ = false;\n    result.path_ = authorityAndPath.str();\n  } else {\n    static const boost::regex authorityRegex(\n        \"(?:([^@:]*)(?::([^@]*))?@)?\" // username, password\n        \"(\\\\[[^\\\\]]*\\\\]|[^\\\\[:]*)\" // host (IP-literal (e.g. '['+IPv6+']',\n                                   // dotted-IPv4, or named host)\n        \"(?::(\\\\d*))?\"); // port\n\n    const auto authority = authorityAndPathMatch[1];\n    boost::cmatch authorityMatch;\n    if (!boost::regex_match(\n            authority.first,\n            authority.second,\n            authorityMatch,\n            authorityRegex)) {\n      return makeUnexpected(UriFormatError::INVALID_URI_AUTHORITY);\n    }\n\n    StringPiece port(authorityMatch[4].first, authorityMatch[4].second);\n    if (!port.empty()) {\n      try {\n        result.port_ = to<uint16_t>(port);\n      } catch (ConversionError const&) {\n        return makeUnexpected(UriFormatError::INVALID_URI_PORT);\n      }\n    }\n\n    result.hasAuthority_ = true;\n    result.username_ = submatch(authorityMatch, 1);\n    result.password_ = submatch(authorityMatch, 2);\n    result.host_ = submatch(authorityMatch, 3);\n    result.path_ = submatch(authorityAndPathMatch, 2);\n  }\n\n  result.query_ = submatch(match, 3);\n  result.fragment_ = submatch(match, 4);\n\n  return result;\n}\n\nstd::string Uri::authority() const {\n  std::string result;\n\n  // Port is 5 characters max and we have up to 3 delimiters.\n  result.reserve(host().size() + username().size() + password().size() + 8);\n\n  if (!username().empty() || !password().empty()) {\n    result.append(username());\n\n    if (!password().empty()) {\n      result.push_back(':');\n      result.append(password());\n    }\n\n    result.push_back('@');\n  }\n\n  result.append(host());\n\n  if (port() != 0) {\n    result.push_back(':');\n    toAppend(port(), &result);\n  }\n\n  return result;\n}\n\nstd::string Uri::hostname() const {\n  if (!host_.empty() && host_[0] == '[') {\n    // If it starts with '[', then it should end with ']', this is ensured by\n    // regex\n    return host_.substr(1, host_.size() - 2);\n  }\n  return host_;\n}\n\nconst std::vector<std::pair<std::string, std::string>>& Uri::getQueryParams() {\n  if (!query_.empty() && queryParams_.empty()) {\n    // Parse query string\n    static const boost::regex queryParamRegex(\n        \"(^|&)\" /*start of query or start of parameter \"&\"*/\n        \"([^=&]*)=?\" /*parameter name and \"=\" if value is expected*/\n        \"([^=&]*)\" /*parameter value*/\n        \"(?=(&|$))\" /*forward reference, next should be end of query or\n                      start of next parameter*/);\n    const boost::cregex_iterator paramBeginItr(\n        query_.data(), query_.data() + query_.size(), queryParamRegex);\n    boost::cregex_iterator paramEndItr;\n    for (auto itr = paramBeginItr; itr != paramEndItr; ++itr) {\n      if (itr->length(2) == 0) {\n        // key is empty, ignore it\n        continue;\n      }\n      queryParams_.emplace_back(\n          std::string((*itr)[2].first, (*itr)[2].second), // parameter name\n          std::string((*itr)[3].first, (*itr)[3].second) // parameter value\n      );\n    }\n  }\n  return queryParams_;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/Uri.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_URI_H_\n\n#include <string>\n#include <vector>\n\n#include <folly/Expected.h>\n#include <folly/String.h>\n\nnamespace folly {\n/**\n * Error codes for parsing issues. Used by tryFromString()\n */\nenum class UriFormatError {\n  INVALID_URI,\n  INVALID_URI_AUTHORITY,\n  INVALID_URI_PORT,\n};\n\n/**\n * Class representing a URI.\n *\n * Consider http://www.facebook.com/foo/bar?key=foo#anchor\n *\n * The URI is broken down into its parts: scheme (\"http\"), authority\n * (ie. host and port, in most cases: \"www.facebook.com\"), path\n * (\"/foo/bar\"), query (\"key=foo\") and fragment (\"anchor\").  The scheme is\n * lower-cased.\n *\n * If this Uri represents a URL, note that, to prevent ambiguity, the component\n * parts are NOT percent-decoded; you should do this yourself with\n * uriUnescape() (for the authority and path) and uriUnescape(...,\n * UriEscapeMode::QUERY) (for the query, but probably only after splitting at\n * '&' to identify the individual parameters).\n */\nclass Uri {\n public:\n  /**\n   * Parse a Uri from a string.  Same as tryFromString except it throws\n   * a std::invalid_argument if there's an error.\n   */\n  explicit Uri(StringPiece str);\n\n  /**\n   * Parse a Uri from a string.\n   *\n   * On failure, returns UriFormatError.\n   */\n  static Expected<Uri, UriFormatError> tryFromString(StringPiece str) noexcept;\n\n  const std::string& scheme() const { return scheme_; }\n  const std::string& username() const { return username_; }\n  const std::string& password() const { return password_; }\n  /**\n   * Get host part of URI. If host is an IPv6 address, square brackets will be\n   * returned, for example: \"[::1]\".\n   */\n  const std::string& host() const { return host_; }\n  /**\n   * Get host part of URI. If host is an IPv6 address, square brackets will not\n   * be returned, for example \"::1\"; otherwise it returns the same thing as\n   * host().\n   *\n   * hostname() is what one needs to call if passing the host to any other tool\n   * or API that connects to that host/port; e.g. getaddrinfo() only understands\n   * IPv6 host without square brackets\n   */\n  std::string hostname() const;\n  uint16_t port() const { return port_; }\n  const std::string& path() const { return path_; }\n  const std::string& query() const { return query_; }\n  const std::string& fragment() const { return fragment_; }\n\n  std::string authority() const;\n\n  template <class String>\n  String toString() const;\n\n  std::string str() const { return toString<std::string>(); }\n  fbstring fbstr() const { return toString<fbstring>(); }\n\n  void setPort(uint16_t port) {\n    hasAuthority_ = true;\n    port_ = port;\n  }\n\n  /**\n   * Get query parameters as key-value pairs.\n   * e.g. for URI containing query string:  key1=foo&key2=&key3&=bar&=bar=\n   * In returned list, there are 3 entries:\n   *     \"key1\" => \"foo\"\n   *     \"key2\" => \"\"\n   *     \"key3\" => \"\"\n   * Parts \"=bar\" and \"=bar=\" are ignored, as they are not valid query\n   * parameters. \"=bar\" is missing parameter name, while \"=bar=\" has more than\n   * one equal signs, we don't know which one is the delimiter for key and\n   * value.\n   *\n   * Note, this method is not thread safe, it might update internal state, but\n   * only the first call to this method update the state. After the first call\n   * is finished, subsequent calls to this method are thread safe.\n   *\n   * @return  query parameter key-value pairs in a vector, each element is a\n   *          pair of which the first element is parameter name and the second\n   *          one is parameter value\n   */\n  const std::vector<std::pair<std::string, std::string>>& getQueryParams();\n\n private:\n  explicit Uri();\n\n  std::string scheme_;\n  std::string username_;\n  std::string password_;\n  std::string host_;\n  bool hasAuthority_;\n  uint16_t port_;\n  std::string path_;\n  std::string query_;\n  std::string fragment_;\n  std::vector<std::pair<std::string, std::string>> queryParams_;\n};\n\n} // namespace folly\n\n#include <folly/Uri-inl.h>\n"
  },
  {
    "path": "folly/Utility.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include <folly/CPortability.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n\nnamespace folly {\n\n/*\n * FOLLY_DECLVAL(T)\n *\n * This macro works like std::declval<T>() but does the same thing in a way\n * that does not require instantiating a function template.\n *\n * Use this macro instead of std::declval<T>() in places that are widely\n * instantiated to reduce compile-time overhead of instantiating function\n * templates.\n *\n * Note that, like std::declval<T>(), this macro can only be used in\n * unevaluated contexts.\n *\n * There are some small differences between this macro and std::declval<T>().\n * - This macro results in a value of type 'T' instead of 'T&&'.\n * - This macro requires the type T to be a complete type at the\n *   point of use.\n *   If this is a problem then use FOLLY_DECLVAL(T&&) instead, or if T might\n *   be 'void', then use FOLLY_DECLVAL(std::add_rvalue_reference_t<T>).\n */\n#define FOLLY_DECLVAL(...) static_cast<__VA_ARGS__ (*)() noexcept>(nullptr)()\n\nnamespace detail {\ntemplate <typename T>\nT decay_1_(T const volatile&&);\ntemplate <typename T>\nT decay_1_(T const&);\ntemplate <typename T>\nT* decay_1_(T*);\n\ntemplate <typename T>\nauto decay_0_(int) -> decltype(detail::decay_1_(FOLLY_DECLVAL(T&&)));\ntemplate <typename T>\nauto decay_0_(short) -> void;\n\ntemplate <typename T>\nusing decay_t = decltype(detail::decay_0_<T>(0));\n} // namespace detail\n\n//  decay_t\n//\n//  Like std::decay_t but possibly faster to compile.\n//\n//  mimic: std::decay_t, C++14\nusing detail::decay_t;\n\n/**\n *  copy\n *\n *  Usable when you have a function with two overloads:\n *\n *      class MyData;\n *      void something(MyData&&);\n *      void something(const MyData&);\n *\n *  Where the purpose is to make copies and moves explicit without having to\n *  spell out the full type names - in this case, for copies, to invoke copy\n *  constructors.\n *\n *  When the caller wants to pass a copy of an lvalue, the caller may:\n *\n *      void foo() {\n *        MyData data;\n *        something(folly::copy(data)); // explicit copy\n *        something(std::move(data)); // explicit move\n *        something(data); // const& - neither move nor copy\n *      }\n *\n *  Note: If passed an rvalue, invokes the move-ctor, not the copy-ctor. This\n *  can be used to to force a move, where just using std::move would not:\n *\n *      folly::copy(std::move(data)); // force-move, not just a cast to &&\n *\n *  Note: The following text appears in the standard:\n *\n *      In several places in this Clause the operation //DECAY_COPY(x)// is\n *      used. All such uses mean call the function `decay_copy(x)` and use the\n *      result, where `decay_copy` is defined as follows:\n *\n *        template <class T> decay_t<T> decay_copy(T&& v)\n *          { return std::forward<T>(v); }\n *\n *      http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf\n *        30.2.6 `decay_copy` [thread.decaycopy].\n *\n *  We mimic it, with a `noexcept` specifier for good measure.\n */\n\ntemplate <typename T>\nconstexpr detail::decay_t<T> copy(T&& value) noexcept(\n    noexcept(detail::decay_t<T>(static_cast<T&&>(value)))) {\n  return static_cast<T&&>(value);\n}\n\n//  mimic: `std::forward_like`, C++23 / p2445r0.\ntemplate <typename Src, typename Dst>\nconstexpr like_t<Src, Dst>&& forward_like(Dst&& dst) noexcept {\n  return std::forward<like_t<Src, Dst>>(static_cast<Dst&&>(dst));\n}\n\n/**\n * Initializer lists are a powerful compile time syntax introduced in C++11\n * but due to their often conflicting syntax they are not used by APIs for\n * construction.\n *\n * Further standard conforming compilers *strongly* favor an\n * std::initializer_list overload for construction if one exists.  The\n * following is a simple tag used to disambiguate construction with\n * initializer lists and regular uniform initialization.\n *\n * For example consider the following case\n *\n *  class Something {\n *  public:\n *    explicit Something(int);\n *    Something(std::initializer_list<int>);\n *\n *    operator int();\n *  };\n *\n *  ...\n *  Something something{1}; // SURPRISE!!\n *\n * The last call to instantiate the Something object will go to the\n * initializer_list overload.  Which may be surprising to users.\n *\n * If however this tag was used to disambiguate such construction it would be\n * easy for users to see which construction overload their code was referring\n * to.  For example\n *\n *  class Something {\n *  public:\n *    explicit Something(int);\n *    Something(folly::initlist_construct_t, std::initializer_list<int>);\n *\n *    operator int();\n *  };\n *\n *  ...\n *  Something something_one{1}; // not the initializer_list overload\n *  Something something_two{folly::initlist_construct, {1}}; // correct\n */\nstruct initlist_construct_t {};\nconstexpr initlist_construct_t initlist_construct{};\n\n//  sorted_unique_t, sorted_unique\n//\n//  A generic tag type and value to indicate that some constructor or method\n//  accepts a container in which the values are sorted and unique.\n//\n//  Example:\n//\n//    void takes_numbers(folly::sorted_unique_t, std::vector<int> alist) {\n//      assert(std::is_sorted(alist.begin(), alist.end()));\n//      assert(std::unique(alist.begin(), alist.end()) == alist.end());\n//      for (i : alist) {\n//        // some behavior which safe only when alist is sorted and unique\n//      }\n//    }\n//    void takes_numbers(std::vector<int> alist) {\n//      std::sort(alist.begin(), alist.end());\n//      alist.erase(std::unique(alist.begin(), alist.end()), alist.end());\n//      takes_numbers(folly::sorted_unique, alist);\n//    }\n//\n//  mimic: std::sorted_unique_t, std::sorted_unique, p0429r6\nstruct sorted_unique_t {};\nconstexpr sorted_unique_t sorted_unique{};\n\n//  sorted_equivalent_t, sorted_equivalent\n//\n//  A generic tag type and value to indicate that some constructor or method\n//  accepts a container in which the values are sorted but not necessarily\n//  unique.\n//\n//  Example:\n//\n//    void takes_numbers(folly::sorted_equivalent_t, std::vector<int> alist) {\n//      assert(std::is_sorted(alist.begin(), alist.end()));\n//      for (i : alist) {\n//        // some behavior which safe only when alist is sorted\n//      }\n//    }\n//    void takes_numbers(std::vector<int> alist) {\n//      std::sort(alist.begin(), alist.end());\n//      takes_numbers(folly::sorted_equivalent, alist);\n//    }\n//\n//  mimic: std::sorted_equivalent_t, std::sorted_equivalent, p0429r6\nstruct sorted_equivalent_t {};\nconstexpr sorted_equivalent_t sorted_equivalent{};\n\ntemplate <typename T>\nstruct transparent : T {\n  using is_transparent = void;\n  using T::T;\n};\n\n/**\n * A simple function object that passes its argument through unchanged.\n *\n * Example:\n *\n *   int i = 42;\n *   int &j = identity(i);\n *   assert(&i == &j);\n *\n * Warning: passing a prvalue through identity turns it into an xvalue,\n * which can effect whether lifetime extension occurs or not. For instance:\n *\n *   auto&& x = std::make_unique<int>(42);\n *   cout << *x ; // OK, x refers to a valid unique_ptr.\n *\n *   auto&& y = identity(std::make_unique<int>(42));\n *   cout << *y ; // ERROR: y did not lifetime-extend the unique_ptr. It\n *                // is no longer valid\n */\nstruct identity_fn {\n  template <class T>\n  constexpr T&& operator()(T&& x) const noexcept {\n    return static_cast<T&&>(x);\n  }\n};\nusing Identity = identity_fn;\ninline constexpr identity_fn identity{};\n\n#if FOLLY_CPLUSPLUS >= 202002 && !defined(__NVCC__)\n\n/// literal_c_str\n///\n/// This can only wrap literal strings, since the constructor is marked\n/// consteval.  Like with fmt::format_string.\nstruct literal_c_str {\n  const char* const ptr;\n  /* implicit */ consteval literal_c_str(const char* p) : ptr(p) {}\n};\n\n#endif\n\n/// literal_string\n///\n/// A structural type representing a literal string. A structural type may be\n/// a non-type template argument.\n///\n/// May at times be useful since language-level literal strings are not allowed\n/// as non-type template arguments.\n///\n/// This may typically be used with vtag for passing the literal string as a\n/// constant-expression via a non-type template argument.\n///\n/// Example:\n///\n///   template <size_t N, literal_string<char, N> Str>\n///   void do_something_with_literal_string(vtag_t<Str>);\n///\n///   void do_something() {\n///     do_something_with_literal_string(vtag<literal_string{\"foobar\"}>);\n///   }\ntemplate <typename C, std::size_t N>\nstruct literal_string {\n  C buffer[N] = {};\n\n  FOLLY_CONSTEVAL /* implicit */ literal_string(C const (&buf)[N]) noexcept {\n    for (std::size_t i = 0; i < N; ++i) {\n      buffer[i] = buf[i];\n    }\n  }\n\n  constexpr std::size_t size() const noexcept { return N - 1; }\n  constexpr C const* data() const noexcept { return buffer; }\n  constexpr C const* c_str() const noexcept { return buffer; }\n\n  template <\n      typename String,\n      decltype((void(String(FOLLY_DECLVAL(C const*), N - 1)), 0)) = 0>\n  constexpr explicit operator String() const //\n      noexcept(noexcept(String(FOLLY_DECLVAL(C const*), N - 1))) {\n    return String(data(), N - 1);\n  }\n};\n\ninline namespace literals {\ninline namespace string_literals {\n\n#if FOLLY_CPLUSPLUS >= 202002 && !defined(__NVCC__)\ntemplate <literal_string Str>\nFOLLY_CONSTEVAL decltype(Str) operator\"\"_lit() noexcept {\n  return Str;\n}\ntemplate <literal_string Str>\nFOLLY_CONSTEVAL vtag_t<Str> operator\"\"_litv() noexcept {\n  return vtag<Str>;\n}\n#endif\n\n} // namespace string_literals\n} // namespace literals\n\nnamespace detail {\n\ntemplate <typename T>\nstruct inheritable_inherit_ : T {\n  using T::T;\n  template <\n      typename... A,\n      std::enable_if_t<std::is_constructible<T, A...>::value, int> = 0>\n  /* implicit */ FOLLY_ERASE inheritable_inherit_(A&&... a) noexcept(\n      noexcept(T(static_cast<A&&>(a)...)))\n      : T(static_cast<A&&>(a)...) {}\n};\n\ntemplate <typename T>\nstruct inheritable_contain_ {\n  T v;\n  template <\n      typename... A,\n      std::enable_if_t<std::is_constructible<T, A...>::value, int> = 0>\n  /* implicit */ FOLLY_ERASE inheritable_contain_(A&&... a) noexcept(\n      noexcept(T(static_cast<A&&>(a)...)))\n      : v(static_cast<A&&>(a)...) {}\n  FOLLY_ERASE operator T&() & noexcept { return v; }\n  FOLLY_ERASE operator T&&() && noexcept { return static_cast<T&&>(v); }\n  FOLLY_ERASE operator T const&() const& noexcept { return v; }\n  FOLLY_ERASE operator T const&&() const&& noexcept {\n    return static_cast<T const&&>(v);\n  }\n};\n\ntemplate <typename T>\nconstexpr bool is_inheritable_v_ = //\n    (std::is_class_v<T> || std::is_union_v<T>) &&\n    !(std::is_abstract_v<T> || std::is_final_v<T>);\n\ntemplate <bool>\nstruct inheritable_;\ntemplate <>\nstruct inheritable_<false> {\n  template <typename T>\n  using apply = inheritable_contain_<T>;\n};\ntemplate <>\nstruct inheritable_<true> {\n  template <typename T>\n  using apply = inheritable_inherit_<T>;\n};\n\n//  inheritable\n//\n//  A class wrapping an arbitrary type T which is always inheritable, and which\n//  enables empty-base-optimization when possible.\ntemplate <typename T, bool C = is_inheritable_v_<T>>\nusing inheritable = typename inheritable_<C>::template apply<T>;\n\n} // namespace detail\n\n// Prevent child classes from finding anything in folly:: by ADL.\nnamespace moveonly_ {\n\n/**\n * Disallow copy but not move in derived types. This is essentially\n * boost::noncopyable (the implementation is almost identical), except:\n * 1) It doesn't delete move constructor and move assignment.\n * 2) It has public methods, enabling aggregate initialization.\n */\nstruct MoveOnly {\n  constexpr MoveOnly() noexcept = default;\n  ~MoveOnly() noexcept = default;\n\n  MoveOnly(MoveOnly&&) noexcept = default;\n  MoveOnly& operator=(MoveOnly&&) noexcept = default;\n  MoveOnly(const MoveOnly&) = delete;\n  MoveOnly& operator=(const MoveOnly&) = delete;\n};\n\n/**\n * Disallow copy and move for derived types. This is essentially\n * boost::noncopyable (the implementation is almost identical), except it has\n * public methods, enabling aggregate initialization.\n */\nstruct NonCopyableNonMovable {\n  constexpr NonCopyableNonMovable() noexcept = default;\n  ~NonCopyableNonMovable() noexcept = default;\n\n  NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;\n  NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;\n  NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;\n  NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;\n};\n\nstruct Default {};\n\ntemplate <bool Copy, bool Move>\nusing EnableCopyMove = std::conditional_t<\n    Copy,\n    Default,\n    std::conditional_t<Move, MoveOnly, NonCopyableNonMovable>>;\n\n} // namespace moveonly_\n\nusing moveonly_::MoveOnly;\nusing moveonly_::NonCopyableNonMovable;\n\n/// variadic_noop\n/// variadic_noop_fn\n///\n/// An invocable object and type that has no side-effects - that does nothing\n/// when invoked regardless of the arguments with which it is invoked - and that\n/// returns void.\n///\n/// May be invoked with any arguments. Returns void.\nstruct variadic_noop_fn {\n  template <typename... A>\n  constexpr void operator()(A&&... /*unused*/) const noexcept {}\n};\ninline constexpr variadic_noop_fn variadic_noop;\n\n/// variadic_constant_of\n/// variadic_constant_of_fn\n///\n/// An invocable object and type that has no side-effects - that does nothing\n/// when invoked regardless of the arguments with which it is invoked - and that\n/// returns a constant value.\ntemplate <auto Value>\nstruct variadic_constant_of_fn {\n  using value_type = decltype(Value);\n  static inline constexpr value_type value = Value;\n  template <typename... A>\n  constexpr value_type operator()(A&&... /*unused*/) const noexcept {\n    return value;\n  }\n};\ntemplate <auto Value>\ninline constexpr variadic_constant_of_fn<Value> variadic_constant_of;\n\n//  unsafe_default_initialized\n//  unsafe_default_initialized_cv\n//\n//  An object which is explicitly convertible to any default-constructible type\n//  and which, upon conversion, yields a default-initialized value of that type.\n//\n//  https://en.cppreference.com/w/cpp/language/default_initialization\n//\n//  For fundamental types, a default-initialized instance may have indeterminate\n//  value. Reading an indeterminate value is undefined behavior but may offer a\n//  performance optimization. When using an indeterminate value as a performance\n//  optimization, it is best to be explicit.\n//\n//  Useful as an escape hatch when enabling warnings or errors:\n//  * gcc:\n//    * uninitialized\n//    * maybe-uninitialized\n//  * clang:\n//    * uninitialized\n//    * conditional-uninitialized\n//    * sometimes-uninitialized\n//    * uninitialized-const-reference\n//  * msvc:\n//    * C4701: potentially uninitialized local variable used\n//    * C4703: potentially uninitialized local pointer variable used\n//\n//  Example:\n//\n//      int local = folly::unsafe_default_initialized;\n//      store_value_into_int_ptr(&value); // suppresses possible warning\n//      use_value(value); // suppresses possible warning\nstruct unsafe_default_initialized_cv {\n  FOLLY_PUSH_WARNING\n  // MSVC requires warning disables to be outside of function definition\n  // Uninitialized local variable 'uninit' used\n  FOLLY_MSVC_DISABLE_WARNING(4700)\n  // Potentially uninitialized local variable 'uninit' used\n  FOLLY_MSVC_DISABLE_WARNING(4701)\n  // Potentially uninitialized local pointer variable 'uninit' used\n  FOLLY_MSVC_DISABLE_WARNING(4703)\n  // Using uninitialized memory `uninit`\n  FOLLY_MSVC_DISABLE_WARNING(6001)\n  FOLLY_GNU_DISABLE_WARNING(\"-Wuninitialized\")\n  // Clang doesn't implement -Wmaybe-uninitialized and warns about it\n  FOLLY_GCC_DISABLE_WARNING(\"-Wmaybe-uninitialized\")\n  template <typename T>\n  FOLLY_ERASE constexpr /* implicit */ operator T() const noexcept {\n#if defined(__cpp_lib_is_constant_evaluated)\n#if __cpp_lib_is_constant_evaluated >= 201811L\n#if (defined(_MSC_VER) && !defined(__MSVC_RUNTIME_CHECKS)) || \\\n    (defined(__clang__) && !defined(__GNUC__))\n    if (!std::is_constant_evaluated()) {\n      T uninit;\n      return uninit;\n    }\n#endif\n#endif\n#endif\n    return T();\n  }\n  FOLLY_POP_WARNING\n};\ninline constexpr unsafe_default_initialized_cv unsafe_default_initialized{};\n\n/// to_bool\n/// to_bool_fn\n///\n/// Constructs a boolean from the argument.\n///\n/// Particularly useful for testing sometimes-weak function declarations. They\n/// may be declared weak on some platforms but not on others. GCC likes to warn\n/// about them but the warning is unhelpful.\nstruct to_bool_fn {\n  template <typename..., typename T>\n  FOLLY_ERASE constexpr auto operator()(T const& t) const noexcept\n      -> decltype(static_cast<bool>(t)) {\n    FOLLY_PUSH_WARNING\n    FOLLY_GCC_DISABLE_WARNING(\"-Waddress\")\n    FOLLY_GCC_DISABLE_WARNING(\"-Wnonnull-compare\")\n    return static_cast<bool>(t);\n    FOLLY_POP_WARNING\n  }\n};\ninline constexpr to_bool_fn to_bool{};\n\nstruct to_signed_fn {\n  template <typename..., typename T>\n  constexpr auto operator()(T const& t) const noexcept ->\n      typename std::make_signed<T>::type {\n    using S = typename std::make_signed<T>::type;\n    // note: static_cast<S>(t) would be more straightforward, but it would also\n    // be implementation-defined behavior and that is typically to be avoided;\n    // the following code optimized into the same thing, though\n    constexpr auto m = static_cast<T>(std::numeric_limits<S>::max());\n    return static_cast<S>(m < t ? -static_cast<S>(~t) + S{-1} : t);\n  }\n};\ninline constexpr to_signed_fn to_signed{};\n\nstruct to_unsigned_fn {\n  template <typename..., typename T>\n  constexpr auto operator()(T const& t) const noexcept ->\n      typename std::make_unsigned<T>::type {\n    using U = typename std::make_unsigned<T>::type;\n    return static_cast<U>(t);\n  }\n};\ninline constexpr to_unsigned_fn to_unsigned{};\n\nnamespace detail {\ntemplate <typename Src, typename Dst>\ninline constexpr bool is_to_narrow_convertible_v =\n    (std::is_integral<Dst>::value) &&\n    (std::is_signed<Dst>::value == std::is_signed<Src>::value);\n}\n\ntemplate <typename Src>\nclass to_narrow_convertible {\n  static_assert(std::is_integral<Src>::value, \"not an integer\");\n\n  template <typename Dst>\n  struct to_\n      : std::bool_constant<detail::is_to_narrow_convertible_v<Src, Dst>> {};\n\n public:\n  explicit constexpr to_narrow_convertible(Src const& value) noexcept\n      : value_(value) {}\n  explicit to_narrow_convertible(to_narrow_convertible const&) = default;\n  explicit to_narrow_convertible(to_narrow_convertible&&) = default;\n  to_narrow_convertible& operator=(to_narrow_convertible const&) = default;\n  to_narrow_convertible& operator=(to_narrow_convertible&&) = default;\n\n  template <typename Dst, std::enable_if_t<to_<Dst>::value, int> = 0>\n  /* implicit */ constexpr operator Dst() const noexcept {\n    FOLLY_PUSH_WARNING\n    FOLLY_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments\n    FOLLY_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables\n    FOLLY_GNU_DISABLE_WARNING(\"-Wconversion\")\n    return value_;\n    FOLLY_POP_WARNING\n  }\n\n private:\n  Src value_;\n};\n\n//  to_narrow\n//\n//  A utility for performing explicit possibly-narrowing integral conversion\n//  without specifying the destination type. Does not permit changing signs.\n//  Sometimes preferable to static_cast<Dst>(src) to document the intended\n//  semantics of the cast.\n//\n//  Models explicit conversion with an elided destination type. Sits in between\n//  a stricter explicit conversion with a named destination type and a more\n//  lenient implicit conversion. Implemented with implicit conversion in order\n//  to take advantage of the undefined-behavior sanitizer's inspection of all\n//  implicit conversions - it checks for truncation, with suppressions in place\n//  for warnings which guard against narrowing implicit conversions.\nstruct to_narrow_fn {\n  template <typename..., typename Src>\n  constexpr auto operator()(Src const& src) const noexcept\n      -> to_narrow_convertible<Src> {\n    return to_narrow_convertible<Src>{src};\n  }\n};\ninline constexpr to_narrow_fn to_narrow{};\n\ntemplate <typename Src>\nclass to_integral_convertible {\n  static_assert(std::is_floating_point<Src>::value, \"not a floating-point\");\n\n  template <typename Dst>\n  static constexpr bool to_ = std::is_integral<Dst>::value;\n\n public:\n  explicit constexpr to_integral_convertible(Src const& value) noexcept\n      : value_(value) {}\n\n  explicit to_integral_convertible(to_integral_convertible const&) = default;\n  explicit to_integral_convertible(to_integral_convertible&&) = default;\n  to_integral_convertible& operator=(to_integral_convertible const&) = default;\n  to_integral_convertible& operator=(to_integral_convertible&&) = default;\n\n  template <typename Dst, std::enable_if_t<to_<Dst>, int> = 0>\n  /* implicit */ constexpr operator Dst() const noexcept {\n    FOLLY_PUSH_WARNING\n    FOLLY_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments\n    FOLLY_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables\n    FOLLY_GNU_DISABLE_WARNING(\"-Wconversion\")\n    return value_;\n    FOLLY_POP_WARNING\n  }\n\n private:\n  Src value_;\n};\n\n//  to_integral\n//\n//  A utility for performing explicit floating-point-to-integral conversion\n//  without specifying the destination type. Sometimes preferable to\n//  static_cast<Dst>(src) to document the intended semantics of the cast.\n//\n//  Models explicit conversion with an elided destination type. Sits in between\n//  a stricter explicit conversion with a named destination type and a more\n//  lenient implicit conversion. Implemented with implicit conversion in order\n//  to take advantage of the undefined-behavior sanitizer's inspection of all\n//  implicit conversions.\nstruct to_integral_fn {\n  template <typename..., typename Src>\n  constexpr auto operator()(Src const& src) const noexcept\n      -> to_integral_convertible<Src> {\n    return to_integral_convertible<Src>{src};\n  }\n};\ninline constexpr to_integral_fn to_integral{};\n\ntemplate <typename Src>\nclass to_floating_point_convertible {\n  static_assert(std::is_integral<Src>::value, \"not a floating-point\");\n\n  template <typename Dst>\n  static constexpr bool to_ = std::is_floating_point<Dst>::value;\n\n public:\n  explicit constexpr to_floating_point_convertible(Src const& value) noexcept\n      : value_(value) {}\n\n  explicit to_floating_point_convertible(to_floating_point_convertible const&) =\n      default;\n  explicit to_floating_point_convertible(to_floating_point_convertible&&) =\n      default;\n  to_floating_point_convertible& operator=(\n      to_floating_point_convertible const&) = default;\n  to_floating_point_convertible& operator=(to_floating_point_convertible&&) =\n      default;\n\n  template <typename Dst, std::enable_if_t<to_<Dst>, int> = 0>\n  /* implicit */ constexpr operator Dst() const noexcept {\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Wconversion\")\n    return value_;\n    FOLLY_POP_WARNING\n  }\n\n private:\n  Src value_;\n};\n\n//  to_floating_point\n//\n//  A utility for performing explicit integral-to-floating-point conversion\n//  without specifying the destination type. Sometimes preferable to\n//  static_cast<Dst>(src) to document the intended semantics of the cast.\n//\n//  Models explicit conversion with an elided destination type. Sits in between\n//  a stricter explicit conversion with a named destination type and a more\n//  lenient implicit conversion. Implemented with implicit conversion in order\n//  to take advantage of the undefined-behavior sanitizer's inspection of all\n//  implicit conversions.\nstruct to_floating_point_fn {\n  template <typename..., typename Src>\n  constexpr auto operator()(Src const& src) const noexcept\n      -> to_floating_point_convertible<Src> {\n    return to_floating_point_convertible<Src>{src};\n  }\n};\ninline constexpr to_floating_point_fn to_floating_point{};\n\nstruct to_underlying_fn {\n  template <typename..., class E>\n  constexpr std::underlying_type_t<E> operator()(E e) const noexcept {\n    static_assert(std::is_enum<E>::value, \"not an enum type\");\n    return static_cast<std::underlying_type_t<E>>(e);\n  }\n};\ninline constexpr to_underlying_fn to_underlying{};\n\nnamespace detail {\ntemplate <typename R>\nusing invocable_to_detect = decltype(FOLLY_DECLVAL(R)());\n\ntemplate <\n    typename F,\n    //  MSVC 14.16.27023 does not permit these to be in the class body:\n    //    error C2833: 'operator decltype' is not a recognized operator or type\n    //  TODO: return these to the class body and remove the static assertions\n    typename TML = detected_t<invocable_to_detect, F&>,\n    typename TCL = detected_t<invocable_to_detect, F const&>,\n    typename TMR = detected_t<invocable_to_detect, F&&>,\n    typename TCR = detected_t<invocable_to_detect, F const&&>>\nclass invocable_to_convertible : private inheritable<F> {\n private:\n  static_assert(std::is_same<F, decay_t<F>>::value, \"mismatch\");\n\n  using base = inheritable<F>;\n\n  template <typename R>\n  using result_t = detected_t<invocable_to_detect, R>;\n  template <typename R>\n  static constexpr bool detected_v = is_detected_v<invocable_to_detect, R>;\n  template <typename R>\n  using if_invocable_as_v = std::enable_if_t<detected_v<R>, int>;\n  template <typename R>\n  static constexpr bool nx_v = noexcept(FOLLY_DECLVAL(R)());\n  template <typename G>\n  static constexpr bool constructible_v = std::is_constructible<F, G&&>::value;\n\n  using FML = F&;\n  using FCL = F const&;\n  using FMR = F&&;\n  using FCR = F const&&;\n  static_assert(std::is_same<TML, result_t<FML>>::value, \"mismatch\");\n  static_assert(std::is_same<TCL, result_t<FCL>>::value, \"mismatch\");\n  static_assert(std::is_same<TMR, result_t<FMR>>::value, \"mismatch\");\n  static_assert(std::is_same<TCR, result_t<FCR>>::value, \"mismatch\");\n\n  using BML = base&;\n  using BCL = base const&;\n  using BMR = base&&;\n  using BCR = base const&&;\n\n public:\n  template <typename G, std::enable_if_t<constructible_v<G&&>, int> = 0>\n  FOLLY_ERASE explicit constexpr invocable_to_convertible(G&& g) noexcept(\n      noexcept(F(static_cast<G&&>(g))))\n      : inheritable<F>(static_cast<G&&>(g)) {}\n\n  template <typename..., typename R = FML, if_invocable_as_v<R> = 0>\n  FOLLY_ERASE constexpr operator TML() & noexcept(nx_v<R>) {\n    return static_cast<FML>(static_cast<BML>(*this))();\n  }\n  template <typename..., typename R = FCL, if_invocable_as_v<R> = 0>\n  FOLLY_ERASE constexpr operator TCL() const& noexcept(nx_v<R>) {\n    return static_cast<FCL>(static_cast<BCL>(*this))();\n  }\n  template <typename..., typename R = FMR, if_invocable_as_v<R> = 0>\n  FOLLY_ERASE constexpr operator TMR() && noexcept(nx_v<R>) {\n    return static_cast<FMR>(static_cast<BMR>(*this))();\n  }\n  template <typename..., typename R = FCR, if_invocable_as_v<R> = 0>\n  FOLLY_ERASE constexpr operator TCR() const&& noexcept(nx_v<R>) {\n    return static_cast<FCR>(static_cast<BCR>(*this))();\n  }\n};\n} // namespace detail\n\n//  invocable_to\n//  invocable_to_fn\n//\n//  Given an invocable, returns an object which is implicitly convertible to the\n//  type which the invocable returns when invoked with no arguments. Conversion\n//  invokes the invocables and returns the value.\n//\n//  The return object has unspecified type with the following semantics:\n//  * It stores a decay-copy of the passed invocable.\n//  * It defines four-way conversion operators. Each conversion operator purely\n//    forwards to the invocable as forwarded-like the convertible, and has the\n//    same exception specification and the same participation in overload\n//    resolution as invocation of the invocable.\n//\n//  Example:\n//\n//    Given a setup:\n//\n//      struct stable {\n//        int value = 0;\n//        stable() = default;\n//        stable(stable const&); // expensive!\n//      };\n//      std::list<stable const> list;\n//\n//    The goal is to insert a stable with a value of 7 to the back of the list.\n//\n//    The obvious ways are expensive:\n//\n//      stable obj;\n//      obj.value = 7;\n//      list.push_back(obj); // or variations with emplace_back or std::move\n//\n//    With a lambda and copy elision optimization (NRVO), the expense remains:\n//\n//      list.push_back(std::invoke([] {\n//        stable obj;\n//        obj.value = 7;\n//        return obj;\n//      }));\n//\n//    But conversion, as done with this utility, makes this goal achievable.\n//\n//      list.emplace_back(folly::invocable_to([] {\n//        stable obj;\n//        obj.value = 7;\n//        return obj;\n//      }));\nstruct invocable_to_fn {\n  template <\n      typename F,\n      typename...,\n      typename D = detail::decay_t<F>,\n      typename R = detail::invocable_to_convertible<D>,\n      std::enable_if_t<std::is_constructible<D, F&&>::value, int> = 0>\n  FOLLY_ERASE constexpr R operator()(F&& f) const\n      noexcept(noexcept(R(static_cast<F&&>(f)))) {\n    return R(static_cast<F&&>(f));\n  }\n};\ninline constexpr invocable_to_fn invocable_to{};\n\n/// object_from_member\n/// object_from_member_fn\n///\n/// Returns the object containing the given field.\n///\n/// Similar to container_of (linux kernel).\n///\n/// Example:\n///\n///   using obj_t = std::pair<int, float>;\n///   obj_t obj = {1, 3.0};\n///   assert(&obj == object_from_member(obj_t::second, &obj.second);\nstruct object_from_member_fn {\n private:\n  template <typename M, typename O>\n  using ptr_t = M O::*;\n\n  template <typename M, typename O>\n  static std::ptrdiff_t off(ptr_t<M, O> const p) noexcept {\n    O* o = nullptr;\n    M* m = &(o->*p);\n    return reinterpret_cast<char*>(m) - reinterpret_cast<char*>(o);\n  }\n\n public:\n  template <typename M, typename O>\n  O* operator()(ptr_t<M, O> const p, M* m) const noexcept {\n    return reinterpret_cast<O*>(reinterpret_cast<char*>(m) - off(p));\n  }\n  template <typename M, typename O>\n  O const* operator()(ptr_t<M, O> const p, M const* m) const noexcept {\n    return reinterpret_cast<O*>(reinterpret_cast<char const*>(m) - off(p));\n  }\n\n  template <typename M, typename O>\n  O& operator()(ptr_t<M, O> const p, M& m) const noexcept {\n    return *operator()(p, &m);\n  }\n  template <typename M, typename O>\n  O const& operator()(ptr_t<M, O> const p, M const& m) const noexcept {\n    return *operator()(p, &m);\n  }\n  template <typename M, typename O>\n  O&& operator()(ptr_t<M, O> const p, M&& m) const noexcept {\n    return static_cast<O&&>(*operator()(p, &m));\n  }\n  template <typename M, typename O>\n  O const&& operator()(ptr_t<M, O> const p, M const& m) const noexcept {\n    return static_cast<O const&&>(*operator()(p, &m));\n  }\n};\ninline constexpr object_from_member_fn object_from_member{};\n\n#define FOLLY_DETAIL_FORWARD_BODY(...)                     \\\n  noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \\\n    return __VA_ARGS__;                                    \\\n  }\n\n/// FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE\n///\n/// Helper macro to add 4 delegated, qualifier-overloaded methods to a class\n///\n/// Example:\n///\n///     template <typename T>\n///     class optional {\n///      public:\n///       bool has_value() const;\n///\n///       T& value() & {\n///         if (!has_value()) { throw std::bad_optional_access(); }\n///         return m_value;\n///       }\n///\n///       const T& value() const& {\n///         if (!has_value()) { throw std::bad_optional_access(); }\n///         return m_value;\n///       }\n///\n///       T&& value() && {\n///         if (!has_value()) { throw std::bad_optional_access(); }\n///         return std::move(m_value);\n///       }\n///\n///       const T&& value() const&& {\n///         if (!has_value()) { throw std::bad_optional_access(); }\n///         return std::move(m_value);\n///       }\n///     };\n///\n/// This is equivalent to\n///\n///     template <typename T>\n///     class optional {\n///       template <typename Self>\n///       decltype(auto) value_impl(Self&& self) {\n///         if (!self.has_value()) {\n///           throw std::bad_optional_access();\n///         }\n///         return std::forward<Self>(self).m_value;\n///       }\n///       // ...\n///\n///      public:\n///       bool has_value() const;\n///\n///       FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(value,\n///       value_impl);\n///     };\n///\n/// Note: This can be migrated to C++23's deducing this:\n/// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html\n///\n// clang-format off\n#define FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(MEMBER, DELEGATE) \\\n  template <class... Args>                                                    \\\n  [[maybe_unused]] FOLLY_ERASE_HACK_GCC                                       \\\n  constexpr auto MEMBER(Args&&... args) & FOLLY_DETAIL_FORWARD_BODY(          \\\n      ::folly::remove_cvref_t<decltype(*this)>::DELEGATE(                     \\\n          *this, static_cast<Args&&>(args)...))                               \\\n  template <class... Args>                                                    \\\n  [[maybe_unused]] FOLLY_ERASE_HACK_GCC                                       \\\n  constexpr auto MEMBER(Args&&... args) const& FOLLY_DETAIL_FORWARD_BODY(     \\\n      ::folly::remove_cvref_t<decltype(*this)>::DELEGATE(                     \\\n          *this, static_cast<Args&&>(args)...))                               \\\n  template <class... Args>                                                    \\\n  [[maybe_unused]] FOLLY_ERASE_HACK_GCC                                       \\\n  constexpr auto MEMBER(Args&&... args) && FOLLY_DETAIL_FORWARD_BODY(         \\\n      ::folly::remove_cvref_t<decltype(*this)>::DELEGATE(                     \\\n          std::move(*this), static_cast<Args&&>(args)...))                    \\\n  template <class... Args>                                                    \\\n  [[maybe_unused]] FOLLY_ERASE_HACK_GCC                                       \\\n  constexpr auto MEMBER(Args&&... args) const&& FOLLY_DETAIL_FORWARD_BODY(    \\\n      ::folly::remove_cvref_t<decltype(*this)>::DELEGATE(                     \\\n          std::move(*this), static_cast<Args&&>(args)...))                    \\\n  /* enforce semicolon after macro */ static_assert(true)\n// clang-format on\n} // namespace folly\n"
  },
  {
    "path": "folly/VERSION",
    "content": "57:0"
  },
  {
    "path": "folly/Varint.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/Conv.h>\n#include <folly/Expected.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n\nnamespace folly {\n\n/**\n * Variable-length integer encoding, using a little-endian, base-128\n * representation.\n *\n * The MSb is set on all bytes except the last.\n *\n * Details:\n * https://developers.google.com/protocol-buffers/docs/encoding#varints\n *\n * If you want to encode multiple values, GroupVarint (in GroupVarint.h)\n * is faster and likely smaller.\n */\n\n/**\n * Maximum length (in bytes) of the varint encoding of a 32-bit value.\n */\nconstexpr size_t kMaxVarintLength32 = 5;\n\n/**\n * Maximum length (in bytes) of the varint encoding of a 64-bit value.\n */\nconstexpr size_t kMaxVarintLength64 = 10;\n\n/**\n * Encode a value in the given buffer, returning the number of bytes used\n * for encoding.\n * buf must have enough space to represent the value (at least\n * kMaxVarintLength64 bytes to encode arbitrary 64-bit values)\n */\nsize_t encodeVarint(uint64_t val, uint8_t* buf);\n\n/**\n * Determine the number of bytes needed to represent \"val\".\n * 32-bit values need at most 5 bytes.\n * 64-bit values need at most 10 bytes.\n */\nint encodeVarintSize(uint64_t val);\n\n/**\n * Decode a value from a given buffer, advances data past the returned value.\n * Throws on error.\n */\ntemplate <class T>\nuint64_t decodeVarint(Range<T*>& data);\n\nenum class DecodeVarintError {\n  TooManyBytes = 0,\n  TooFewBytes = 1,\n};\n\n/**\n * A variant of decodeVarint() that does not throw on error. Useful in contexts\n * where only part of a serialized varint may be attempted to be decoded, e.g.,\n * when a serialized varint arrives on the boundary of a network packet.\n */\ntemplate <class T>\nExpected<uint64_t, DecodeVarintError> tryDecodeVarint(Range<T*>& data);\n\n/**\n * ZigZag encoding that maps signed integers with a small absolute value\n * to unsigned integers with a small (positive) values. Without this,\n * encoding negative values using Varint would use up 9 or 10 bytes.\n *\n * if x >= 0, encodeZigZag(x) == 2*x\n * if x <  0, encodeZigZag(x) == -2*x - 1\n */\n\ninline uint64_t encodeZigZag(int64_t val) {\n  // Bit-twiddling magic stolen from the Google protocol buffer document;\n  // val >> 63 is an arithmetic shift because val is signed\n  auto uval = static_cast<uint64_t>(val);\n  return static_cast<uint64_t>((uval << 1) ^ (val >> 63));\n}\n\ninline int64_t decodeZigZag(uint64_t val) {\n  return static_cast<int64_t>((val >> 1) ^ -(val & 1));\n}\n\n// Implementation below\n\ninline size_t encodeVarint(uint64_t val, uint8_t* buf) {\n  uint8_t* p = buf;\n  while (val >= 128) {\n    *p++ = 0x80 | (val & 0x7f);\n    val >>= 7;\n  }\n  *p++ = uint8_t(val);\n  return size_t(p - buf);\n}\n\ninline int encodeVarintSize(uint64_t val) {\n  if (folly::kIsArchAmd64) {\n    // __builtin_clzll is undefined for 0\n    int highBit = 64 - __builtin_clzll(val | 1);\n    return (highBit + 6) / 7;\n  } else {\n    int s = 1;\n    while (val >= 128) {\n      ++s;\n      val >>= 7;\n    }\n    return s;\n  }\n}\n\ntemplate <class T>\ninline uint64_t decodeVarint(Range<T*>& data) {\n  auto expected = tryDecodeVarint(data);\n  if (!expected) {\n    throw std::invalid_argument(\n        expected.error() == DecodeVarintError::TooManyBytes\n            ? \"Invalid varint value: too many bytes.\"\n            : \"Invalid varint value: too few bytes.\");\n  }\n  return *expected;\n}\n\ntemplate <class T>\ninline Expected<uint64_t, DecodeVarintError> tryDecodeVarint(Range<T*>& data) {\n  static_assert(\n      std::is_same<typename std::remove_cv<T>::type, char>::value ||\n          std::is_same<typename std::remove_cv<T>::type, unsigned char>::value,\n      \"Only character ranges are supported\");\n\n  const int8_t* begin = reinterpret_cast<const int8_t*>(data.begin());\n  const int8_t* end = reinterpret_cast<const int8_t*>(data.end());\n  const int8_t* p = begin;\n  uint64_t val = 0;\n\n  // end is always greater than or equal to begin, so this subtraction is safe\n  if (FOLLY_LIKELY(size_t(end - begin) >= kMaxVarintLength64)) { // fast path\n    int64_t b;\n    do {\n      b = *p++;\n      val = (b & 0x7f);\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 7;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 14;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 21;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 28;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 35;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 42;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 49;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x7f) << 56;\n      if (b >= 0) {\n        break;\n      }\n      b = *p++;\n      val |= (b & 0x01) << 63;\n      if (b >= 0) {\n        break;\n      }\n      return makeUnexpected(DecodeVarintError::TooManyBytes);\n    } while (false);\n  } else {\n    int shift = 0;\n    while (p != end && *p < 0) {\n      val |= static_cast<uint64_t>(*p++ & 0x7f) << shift;\n      shift += 7;\n    }\n    if (p == end) {\n      return makeUnexpected(DecodeVarintError::TooFewBytes);\n    }\n    val |= static_cast<uint64_t>(*p++) << shift;\n  }\n\n  data.uncheckedAdvance(p - begin);\n  return val;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/VirtualExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/VirtualExecutor.h> // @shim\n"
  },
  {
    "path": "folly/algorithm/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"binary_heap\",\n    headers = [\"BinaryHeap.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/lang:builtin\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"stable_radix_sort\",\n    headers = [\"StableRadixSort.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:traits\",\n    ],\n)\n"
  },
  {
    "path": "folly/algorithm/BinaryHeap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <iterator>\n\n#include <folly/lang/Builtin.h>\n\nnamespace folly {\n\n/**\n * Companion to std::push/pop_heap(). Restores the heap property if the heap's\n * top is modified.\n */\ntemplate <class RandomIt, class Compare>\nvoid down_heap(RandomIt first, RandomIt last, Compare comp) {\n  size_t size = last - first;\n  size_t parent = 0;\n  size_t child;\n  // Iterate while both left and right children exist.\n  while ((child = 2 * parent + 2) < size) {\n    // Find the max among the two children.\n    child = FOLLY_BUILTIN_UNPREDICTABLE(comp(first[child], first[child - 1]))\n        ? child - 1\n        : child;\n    if (comp(first[parent], first[child])) {\n      std::iter_swap(first + parent, first + child);\n      parent = child;\n    } else {\n      return;\n    }\n  }\n\n  // Now parent can have either no children or only a left child.\n  if (--child < size && comp(first[parent], first[child])) {\n    std::iter_swap(first + parent, first + child);\n  }\n}\n\ntemplate <class RandomIt>\nvoid down_heap(RandomIt first, RandomIt last) {\n  down_heap(first, last, std::less<>{});\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME binary_heap\n  HEADERS\n    BinaryHeap.h\n  EXPORTED_DEPS\n    folly_lang_builtin\n)\n\nfolly_add_library(\n  NAME stable_radix_sort\n  HEADERS\n    StableRadixSort.h\n  EXPORTED_DEPS\n    folly_traits\n)\n\nadd_subdirectory(simd)\n"
  },
  {
    "path": "folly/algorithm/StableRadixSort.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <bit>\n#include <cstddef>\n#include <cstdint>\n#include <cstring>\n#include <iterator>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Traits.h>\n\nnamespace folly {\n\n/**\n * Stable LSD (Least Significant Digit) Radix Sort\n *\n * A stable, O(n) radix sort implementation using counting sort with\n * alternating buffers. Unlike MSD radix sort (e.g., boost::spreadsort),\n * LSD radix sort processes digits from least to most significant, which\n * inherently preserves stability.\n *\n * Key features:\n * - Stable: elements with equal keys preserve their original relative order\n * - O(n) time complexity: k passes of O(n + 256) where k = sizeof(key)/8\n * - O(n) space complexity: requires a temporary buffer\n * - Supports ascending and descending order\n * - Supports custom projections for sorting by member fields\n * - Handles floating-point keys via IEEE 754 bit transformation\n *\n * Example usage:\n *\n *   // Sort vector of integers\n *   std::vector<uint64_t> values = {5, 2, 8, 1, 9};\n *   folly::stable_radix_sort(values.begin(), values.end());\n *\n *   // Sort by struct member\n *   struct Item { double score; int id; };\n *   std::vector<Item> items = ...;\n *   folly::stable_radix_sort(\n *       items.begin(), items.end(),\n *       [](const Item& item) { return item.score; });\n *\n *   // Sort descending\n *   folly::stable_radix_sort_descending(values.begin(), values.end());\n */\n\nnamespace stable_radix_sort_keys {\n\n/**\n * FloatKey - Converts IEEE 754 floats/doubles to sortable unsigned integers.\n *\n * IEEE 754 floats are almost sortable in their bit representation, except:\n * - The sign bit inverts the comparison for negative numbers\n * - Negative numbers sort in reverse order\n *\n * This transformation maps the float ordering to unsigned integer ordering:\n * - -inf maps to 0x0000...\n * - -0.0 maps to 0x7FFF...  (just below +0.0)\n * - +0.0 maps to 0x8000...\n * - +inf maps to 0xFFFF...\n *\n * Template parameters:\n *   Float - float or double\n *   Descending - if true, inverts the key for descending sort\n */\ntemplate <typename Float, bool Descending = false>\nstruct FloatKey {\n  static_assert(\n      std::is_floating_point_v<Float>,\n      \"FloatKey requires a floating-point type\");\n\n  using UInt = uint_bits_t<sizeof(Float) * 8>;\n  static_assert(sizeof(Float) == sizeof(UInt));\n\n  UInt operator()(Float f) const noexcept {\n    UInt bits = std::bit_cast<UInt>(f);\n    constexpr UInt kSignBit = UInt{1} << (sizeof(UInt) * 8 - 1);\n    // If negative: flip all bits (makes most negative -> 0)\n    // If positive: flip only sign bit (makes positive > all negatives)\n    UInt mask = (bits & kSignBit) ? ~UInt{0} : kSignBit;\n    UInt sortable = bits ^ mask;\n    return Descending ? ~sortable : sortable;\n  }\n};\n\n/**\n * IntegralKey - Handles signed/unsigned integers for radix sort.\n *\n * For unsigned types: identity (no transformation needed)\n * For signed types: flips sign bit to map signed ordering to unsigned\n *\n * Template parameters:\n *   Int - any integral type\n *   Descending - if true, inverts the key for descending sort\n */\ntemplate <typename Int, bool Descending = false>\nstruct IntegralKey {\n  static_assert(\n      std::is_integral_v<Int>, \"IntegralKey requires an integral type\");\n\n  using UInt = std::make_unsigned_t<Int>;\n\n  UInt operator()(Int value) const noexcept {\n    constexpr UInt kSignBit =\n        std::is_signed_v<Int> ? (UInt{1} << (sizeof(UInt) * 8 - 1)) : 0;\n    UInt result = static_cast<UInt>(value) ^ kSignBit;\n    return Descending ? ~result : result;\n  }\n};\n\n/**\n * IdentityKey - Returns the value unchanged (for unsigned integers).\n */\ntemplate <bool Descending = false>\nstruct IdentityKey {\n  template <typename T>\n  T operator()(T value) const noexcept {\n    return Descending ? ~value : value;\n  }\n};\n\n} // namespace stable_radix_sort_keys\n\nnamespace stable_radix_sort_detail {\n\n// Size threshold below which we fall back to insertion sort\ninline constexpr size_t kRadixSortThreshold = 16;\n\n// Radix configuration\ninline constexpr size_t kRadixBits = 8;\nstatic_assert(kRadixBits == 8, \"extractRadixDigit assumes 8-bit radix\");\ninline constexpr size_t kRadixBuckets = 1 << kRadixBits; // 256\ninline constexpr size_t kRadixMask = kRadixBuckets - 1;\n\ntemplate <typename Key>\ninline constexpr size_t kRadixPasses = sizeof(Key);\n\n/**\n * Extract the radix digit at the given pass index from a key.\n * Pass 0 extracts the least significant digit.\n */\ntemplate <typename Key>\ninline uint8_t extractRadixDigit(Key key, size_t pass) noexcept {\n  static_assert(std::is_unsigned_v<Key>, \"Key must be unsigned\");\n  // kRadixPasses ensures pass is always < sizeof(Key), so shift is safe\n  return static_cast<uint8_t>((key >> (pass * kRadixBits)) & kRadixMask);\n}\n\n/**\n * Stable insertion sort for small inputs.\n * O(n^2) but fast for small n due to low overhead.\n */\ntemplate <typename RandomIt, typename Projection>\nvoid insertionSort(RandomIt first, RandomIt last, Projection proj) {\n  for (auto i = first; i != last; ++i) {\n    auto key = proj(*i);\n    auto j = i;\n    while (j != first) {\n      auto prev = j;\n      --prev;\n      if (proj(*prev) <= key) {\n        break;\n      }\n      std::swap(*prev, *j);\n      j = prev;\n    }\n  }\n}\n\n/**\n * Core LSD radix sort implementation with alternating buffers.\n *\n * Uses counting sort for each pass, processing from least to most\n * significant byte. The alternating buffer pattern avoids copying\n * data between passes - we simply swap source and destination pointers.\n */\ntemplate <typename T, typename Projection>\nvoid stableRadixSortImpl(T* data, T* buffer, size_t n, Projection proj) {\n  using Key = std::invoke_result_t<Projection, const T&>;\n  static_assert(std::is_unsigned_v<Key>, \"Key type must be unsigned\");\n\n  constexpr size_t kPasses = kRadixPasses<Key>;\n\n  T* src = data;\n  T* dst = buffer;\n\n  for (size_t pass = 0; pass < kPasses; ++pass) {\n    // Phase 1: Count occurrences of each byte value\n    alignas(64) std::array<size_t, kRadixBuckets> counts{};\n    for (size_t i = 0; i < n; ++i) {\n      auto key = proj(src[i]);\n      ++counts[extractRadixDigit(key, pass)];\n    }\n\n    // Check if this pass can be skipped (all elements have the same byte value)\n    size_t nonzeroCount = 0;\n    for (size_t c : counts) {\n      if (c != 0 && ++nonzeroCount > 1) {\n        break;\n      }\n    }\n    if (nonzeroCount <= 1) {\n      continue;\n    }\n\n    // Phase 2: Convert counts to starting offsets (prefix sum)\n    alignas(64) std::array<size_t, kRadixBuckets> offsets{};\n    size_t total = 0;\n    for (size_t i = 0; i < kRadixBuckets; ++i) {\n      offsets[i] = total;\n      total += counts[i];\n    }\n\n    // Phase 3: Scatter elements to output (preserves stability)\n    for (size_t i = 0; i < n; ++i) {\n      auto key = proj(src[i]);\n      size_t& offset = offsets[extractRadixDigit(key, pass)];\n      dst[offset] = src[i];\n      ++offset;\n    }\n\n    // Swap source and destination for next pass\n    std::swap(src, dst);\n  }\n\n  // If the final result is in the buffer, copy it back to data\n  if (src != data) {\n    std::copy(src, src + n, data);\n  }\n}\n\n/**\n * Wrapper that handles buffer allocation and fallback to std::stable_sort.\n */\ntemplate <typename ContiguousIt, typename Projection>\nvoid stableRadixSort(ContiguousIt first, ContiguousIt last, Projection proj) {\n  using T = typename std::iterator_traits<ContiguousIt>::value_type;\n  static_assert(\n      std::contiguous_iterator<ContiguousIt>,\n      \"stable_radix_sort requires contiguous iterators\");\n\n  size_t n = static_cast<size_t>(std::distance(first, last));\n\n  // Fall back to insertion sort for small inputs (handles n <= 1 as well)\n  if (n < kRadixSortThreshold) {\n    insertionSort(first, last, proj);\n    return;\n  }\n\n  // Allocate temporary buffer\n  auto buffer = std::make_unique_for_overwrite<T[]>(n);\n\n  // Get pointer to data\n  T* data = std::to_address(first);\n\n  stableRadixSortImpl(data, buffer.get(), n, proj);\n}\n\n/**\n * Default projection that handles common types.\n */\ntemplate <typename T, bool Descending>\nstruct DefaultProjection {\n  auto operator()(const T& value) const noexcept {\n    static_assert(\n        std::is_floating_point_v<T> || is_non_bool_integral_v<T>,\n        \"stable_radix_sort requires floating-point or integral types\");\n    if constexpr (std::is_floating_point_v<T>) {\n      return stable_radix_sort_keys::FloatKey<T, Descending>{}(value);\n    } else {\n      return stable_radix_sort_keys::IntegralKey<T, Descending>{}(value);\n    }\n  }\n};\n\n/**\n * Projection wrapper that applies type-appropriate transformation.\n */\ntemplate <bool Descending, typename Projection>\nstruct TransformingProjection {\n  Projection inner;\n\n  template <typename T>\n  auto operator()(const T& value) const noexcept {\n    auto extracted = inner(value);\n    using Extracted = decltype(extracted);\n    static_assert(\n        std::is_floating_point_v<Extracted> ||\n            is_non_bool_integral_v<Extracted>,\n        \"projection must return floating-point or integral\");\n    if constexpr (std::is_floating_point_v<Extracted>) {\n      return stable_radix_sort_keys::FloatKey<Extracted, Descending>{}(\n          extracted);\n    } else if constexpr (std::is_signed_v<Extracted>) {\n      return stable_radix_sort_keys::IntegralKey<Extracted, Descending>{}(\n          extracted);\n    } else {\n      return Descending ? ~extracted : extracted;\n    }\n  }\n};\n\n} // namespace stable_radix_sort_detail\n\n/**\n * stable_radix_sort - Sort elements in ascending order.\n *\n * Sorts elements using LSD radix sort. This is a stable sort: elements\n * with equal keys preserve their original relative order.\n *\n * Note: For floating-point types, the sort uses an augmented ordering\n * equivalent to std::stable_sort(first, last, augmented_less) where:\n *\n *   template <std::floating_point T>\n *   struct augmented_less {\n *     bool operator()(T lhs, T rhs) const noexcept {\n *       if (lhs < rhs) return true;\n *       if (rhs < lhs) return false;\n *       return std::signbit(lhs) > std::signbit(rhs);\n *     }\n *   };\n *\n * In other words, -0.0 sorts before +0.0. For all other values, the\n * ordering matches operator<.\n *\n * Requirements:\n * - ContiguousIt must be a contiguous iterator\n * - Value type must be floating-point or integral\n *\n * Complexity: O(n * k) where k = sizeof(value_type) / 8\n * Space: O(n) for temporary buffer\n */\ntemplate <typename ContiguousIt>\nvoid stable_radix_sort(ContiguousIt first, ContiguousIt last) {\n  using T = typename std::iterator_traits<ContiguousIt>::value_type;\n  stable_radix_sort_detail::stableRadixSort(\n      first, last, stable_radix_sort_detail::DefaultProjection<T, false>{});\n}\n\n/**\n * stable_radix_sort - Sort elements by extracted key in ascending order.\n *\n * Sorts elements by the key returned by proj. The key must be a type\n * that can be sorted by radix sort (floating-point, integral, or unsigned).\n *\n * Example:\n *   struct Item { double score; int id; };\n *   std::vector<Item> items = ...;\n *   folly::stable_radix_sort(\n *       items.begin(), items.end(),\n *       [](const Item& item) { return item.score; });\n */\ntemplate <typename ContiguousIt, typename Projection>\nvoid stable_radix_sort(ContiguousIt first, ContiguousIt last, Projection proj) {\n  using Wrapped =\n      stable_radix_sort_detail::TransformingProjection<false, Projection>;\n  stable_radix_sort_detail::stableRadixSort(first, last, Wrapped{proj});\n}\n\n/**\n * stable_radix_sort_descending - Sort elements in descending order.\n *\n * Like stable_radix_sort, but produces descending order.\n */\ntemplate <typename ContiguousIt>\nvoid stable_radix_sort_descending(ContiguousIt first, ContiguousIt last) {\n  using T = typename std::iterator_traits<ContiguousIt>::value_type;\n  stable_radix_sort_detail::stableRadixSort(\n      first, last, stable_radix_sort_detail::DefaultProjection<T, true>{});\n}\n\n/**\n * stable_radix_sort_descending - Sort by extracted key in descending order.\n *\n * Like stable_radix_sort with projection, but produces descending order.\n */\ntemplate <typename ContiguousIt, typename Projection>\nvoid stable_radix_sort_descending(\n    ContiguousIt first, ContiguousIt last, Projection proj) {\n  using Wrapped =\n      stable_radix_sort_detail::TransformingProjection<true, Projection>;\n  stable_radix_sort_detail::stableRadixSort(first, last, Wrapped{proj});\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/simd/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"contains\",\n    srcs = [\"Contains.cpp\"],\n    headers = [\"Contains.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/algorithm/simd/detail:simd_contains_impl\",\n    ],\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly/algorithm/simd/detail:traits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"find_first_of\",\n    headers = [\"find_first_of.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":movemask\",\n        \"//folly:portability\",\n        \"//folly:utility\",\n        \"//folly/container:span\",\n        \"//folly/container:sparse_byte_set\",\n        \"//folly/lang:align\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:hint\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"find_first_of_extra\",\n    headers = [\"find_first_of_extra.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:range\",\n        \"//folly/container:span\",\n    ],\n    exported_external_deps = [\n        \"tbb\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"find_fixed\",\n    headers = [\"FindFixed.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":movemask\",\n        \"//folly:portability\",\n        \"//folly/algorithm/simd/detail:traits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"ignore\",\n    headers = [\"Ignore.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/lang:bits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"movemask\",\n    headers = [\"Movemask.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":ignore\",\n        \"//folly:portability\",\n        \"//folly/lang:bits\",\n    ],\n)\n"
  },
  {
    "path": "folly/algorithm/simd/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME contains\n  SRCS\n    Contains.cpp\n  HEADERS\n    Contains.h\n  DEPS\n    folly_algorithm_simd_detail_simd_contains_impl\n  EXPORTED_DEPS\n    folly_algorithm_simd_detail_traits\n    folly_c_portability\n)\n\nfolly_add_library(\n  NAME find_first_of\n  HEADERS\n    find_first_of.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_movemask\n    folly_container_span\n    folly_container_sparse_byte_set\n    folly_lang_align\n    folly_lang_bits\n    folly_lang_hint\n    folly_portability\n    folly_utility\n)\n\nfolly_add_library(\n  NAME find_first_of_extra\n  HEADERS\n    find_first_of_extra.h\n  EXPORTED_DEPS\n    folly_container_span\n    folly_range\n)\n\nfolly_add_library(\n  NAME find_fixed\n  HEADERS\n    FindFixed.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_detail_traits\n    folly_algorithm_simd_movemask\n    folly_portability\n)\n\nfolly_add_library(\n  NAME ignore\n  HEADERS\n    Ignore.h\n  EXPORTED_DEPS\n    folly_lang_bits\n)\n\nfolly_add_library(\n  NAME movemask\n  HEADERS\n    Movemask.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_ignore\n    folly_lang_bits\n    folly_portability\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/algorithm/simd/Contains.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/Contains.h>\n\n#include <algorithm>\n#include <cstring>\n#include <folly/algorithm/simd/detail/ContainsImpl.h>\n\nnamespace folly::simd::detail {\n\nbool containsU8(\n    folly::span<const std::uint8_t> haystack, std::uint8_t needle) noexcept {\n  return containsImpl(haystack, needle);\n}\nbool containsU16(\n    folly::span<const std::uint16_t> haystack, std::uint16_t needle) noexcept {\n  return containsImpl(haystack, needle);\n}\nbool containsU32(\n    folly::span<const std::uint32_t> haystack, std::uint32_t needle) noexcept {\n  return containsImpl(haystack, needle);\n}\n\nbool containsU64(\n    folly::span<const std::uint64_t> haystack, std::uint64_t needle) noexcept {\n  return containsImpl(haystack, needle);\n}\n\n} // namespace folly::simd::detail\n"
  },
  {
    "path": "folly/algorithm/simd/Contains.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CPortability.h>\n#include <folly/algorithm/simd/detail/Traits.h>\n\n#include <iterator>\n\nnamespace folly::simd {\nnamespace detail {\n\n// no overloading for easier profiling.\n\nbool containsU8(\n    folly::span<const std::uint8_t> haystack, std::uint8_t needle) noexcept;\nbool containsU16(\n    folly::span<const std::uint16_t> haystack, std::uint16_t needle) noexcept;\nbool containsU32(\n    folly::span<const std::uint32_t> haystack, std::uint32_t needle) noexcept;\nbool containsU64(\n    folly::span<const std::uint64_t> haystack, std::uint64_t needle) noexcept;\n\ntemplate <typename R>\nusing std_range_value_t = typename std::iterator_traits<decltype(std::begin(\n    std::declval<R&>()))>::value_type;\n\n// Constexpr check that we can always safely cast from From to To.\n// If we don't require this, we might silently get different semantics from\n// standard algorithms.\ntemplate <typename From, typename To>\nconstexpr bool convertible_with_no_loss() {\n  if (sizeof(From) > sizeof(To)) {\n    return false;\n  }\n  if (std::is_signed_v<From>) {\n    return std::is_signed_v<To>;\n  }\n\n  return std::is_unsigned_v<To> || sizeof(From) < sizeof(To);\n}\n\n// All the requirements to call contains(haystack, needle);\n//  * both are simd friendly (contiguous range, primitive types)\n//  * integrals only\n//  * needle can be converted to the value_type of haystack and\n//    the result of equality comparison will be the same.\ntemplate <typename R, typename T>\nconstexpr bool contains_haystack_needle_test() {\n  if constexpr (!std::is_invocable_v<AsSimdFriendlyUintFn, R>) {\n    return false;\n  } else if constexpr (!has_integral_simd_friendly_equivalent_scalar_v<T>) {\n    return false;\n  } else {\n    using simd_haystack_value =\n        simd_friendly_equivalent_scalar_t<std_range_value_t<R>>;\n    using simd_needle = simd_friendly_equivalent_scalar_t<T>;\n    return convertible_with_no_loss<simd_needle, simd_haystack_value>();\n  }\n}\n\n} // namespace detail\n\n/**\n * folly::simd::contains\n * folly::simd::contains_fn\n *\n * A vectorized version of `std::ranges::find(r, x) != r.end()`.\n * Only works for \"simd friendly cases\" -\n * specifically the ones where the type can be reasonably cast\n * to `uint8/16/32/64_t`.\n *\n **/\nstruct contains_fn {\n  template <\n      typename R,\n      typename T,\n      typename =\n          std::enable_if_t<detail::contains_haystack_needle_test<R, T>()>>\n  FOLLY_ERASE bool operator()(R&& r, T x) const {\n    auto castR = detail::asSimdFriendlyUint(folly::span(r));\n    using value_type = detail::std_range_value_t<decltype(castR)>;\n\n    auto castX = static_cast<value_type>(detail::asSimdFriendlyUint(x));\n\n    if constexpr (std::is_same_v<value_type, std::uint8_t>) {\n      return detail::containsU8(castR, castX);\n    } else if constexpr (std::is_same_v<value_type, std::uint16_t>) {\n      return detail::containsU16(castR, castX);\n    } else if constexpr (std::is_same_v<value_type, std::uint32_t>) {\n      return detail::containsU32(castR, castX);\n    } else {\n      static_assert(\n          std::is_same_v<value_type, std::uint64_t>,\n          \"internal error, unknown type\");\n      return detail::containsU64(castR, castX);\n    }\n  }\n};\n\ninline constexpr contains_fn contains;\n\n} // namespace folly::simd\n"
  },
  {
    "path": "folly/algorithm/simd/FindFixed.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <bit>\n#include <concepts>\n#include <cstdint>\n#include <cstring>\n#include <optional>\n#include <span>\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/algorithm/simd/Movemask.h>\n#include <folly/algorithm/simd/detail/Traits.h>\n\n#if FOLLY_X64\n#include <immintrin.h>\n#endif\n\n#if FOLLY_AARCH64\n#include <arm_neon.h>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\n// Note: using std::same_as will just be slower to compile than is_same_v\ntemplate <typename T>\nconcept SimdFriendlyType =\n    (std::is_same_v<std::int8_t, T> || std::is_same_v<std::uint8_t, T> ||\n     std::is_same_v<std::int16_t, T> || std::is_same_v<std::uint16_t, T> ||\n     std::is_same_v<std::int32_t, T> || std::is_same_v<std::uint32_t, T> ||\n     std::is_same_v<std::int64_t, T> || std::is_same_v<std::uint64_t, T>);\n\n} // namespace detail\n\ntemplate <typename T>\nconcept FollyFindFixedSupportedType = detail::SimdFriendlyType<T> ||\n    (std::is_enum_v<T> && detail::SimdFriendlyType<std::underlying_type_t<T>>);\n\n/*\n * # folly::findFixed\n *\n * A function to linear search in number of elements, known at compiled time.\n *\n * Example:\n *   std::vector<int> v {1, 3, 1, 2};\n *   std::span<const int, 4> vspan(v.data(), 4);\n *   auto m0 = folly::findFixed(vspan, 3); // m0 == 1;\n *   auto m1 = folly::findFixed(vspan, 5); // m0 == std::nullopt;\n *\n * Supported types:\n *  any 8,16,32,64 bit integers\n *  enums\n *\n * Max supported size of the range is 64 bytes.\n */\ntemplate <\n    FollyFindFixedSupportedType T,\n    std::convertible_to<T> U,\n    std::size_t N>\nconstexpr std::optional<std::size_t> findFixed(std::span<const T, N> where, U x)\n  requires(sizeof(T) * N <= 64);\n\n// implementation ---------------------------------------------------------\n\nnamespace find_fixed_detail {\n\ntemplate <typename T>\nconstexpr std::optional<std::size_t> findFixedConstexpr(\n    std::span<const T> where, T x) {\n  std::size_t res = 0;\n  for (T e : where) {\n    if (e == x) {\n      return res;\n    }\n    ++res;\n  }\n  return std::nullopt;\n}\n\n// clang just checks all elements one by one, without any vectorization.\n// even for not very friendly to SIMD cases we could do better but for\n// now only special powers of 2 were interesting.\ntemplate <typename T, std::size_t N>\nstd::optional<std::size_t> findFixedLetTheCompilerDoIt(\n    std::span<const T, N> where, T x) {\n  // this get's unrolled by both clang and gcc.\n  // Experimenting with more complex ways of writing this code\n  // didn't yield any results.\n  return findFixedConstexpr(std::span<const T>(where), x);\n}\n\n#if FOLLY_X64\n#if defined(__AVX2__)\nconstexpr std::size_t kMaxSimdRegister = 32;\n#else\nconstexpr std::size_t kMaxSimdRegister = 16;\n#endif\n#elif FOLLY_AARCH64\nconstexpr std::size_t kMaxSimdRegister = 16;\n#else\nconstexpr std::size_t kMaxSimdRegister = 1;\n#endif\n\ntemplate <typename T>\nstd::optional<std::size_t> find8bytes(const T* from, T x);\ntemplate <typename T>\nstd::optional<std::size_t> find16bytes(const T* from, T x);\ntemplate <typename T>\nstd::optional<std::size_t> find32bytes(const T* from, T x);\n\ntemplate <typename T, std::size_t N>\nstd::optional<std::size_t> find2Overlaping(std::span<const T, N> where, T x);\n\ntemplate <typename T, std::size_t N>\nstd::optional<std::size_t> findSplitFirstRegister(\n    std::span<const T, N> where, T x);\n\ntemplate <typename T, std::size_t N>\nstd::optional<std::size_t> findFixedDispatch(std::span<const T, N> where, T x) {\n  constexpr std::size_t kNumBytes = N * sizeof(T);\n\n  if constexpr (N == 0) {\n    return std::nullopt;\n  } else if constexpr (N <= 2 || kNumBytes < 8 || kMaxSimdRegister == 1) {\n    return findFixedLetTheCompilerDoIt(where, x);\n  } else if constexpr (kNumBytes == 8) {\n    return find8bytes(where.data(), x);\n  } else if constexpr (kNumBytes == 16) {\n    return find16bytes(where.data(), x);\n  } else if constexpr (kMaxSimdRegister >= 32 && kNumBytes == 32) {\n    return find32bytes(where.data(), x);\n  } else if constexpr (kMaxSimdRegister * 2 <= kNumBytes) {\n    return findSplitFirstRegister(where, x);\n  } else {\n    // we can maybe do one better here probably with either out of bounds\n    // loads or combined two register search but it's ok for now.\n    return find2Overlaping(where, x);\n  }\n}\n\ntemplate <typename T, std::size_t N>\nstd::optional<std::size_t> find2Overlaping(std::span<const T, N> where, T x) {\n  constexpr std::size_t kRegSize = std::bit_floor(N);\n\n  std::span<const T, kRegSize> firstOverlap(where.data(), kRegSize);\n  if (auto res = findFixed(firstOverlap, x)) {\n    return res;\n  }\n\n  std::span<const T, kRegSize> secondOverlap(\n      where.data() + (N - kRegSize), kRegSize);\n  if (auto res = findFixed(secondOverlap, x)) {\n    return *res + (N - kRegSize);\n  }\n  return std::nullopt;\n}\n\ntemplate <typename T, std::size_t N>\nstd::optional<std::size_t> findSplitFirstRegister(\n    std::span<const T, N> where, T x) {\n  constexpr std::size_t kRegSize = kMaxSimdRegister / sizeof(T);\n\n  std::span<const T, kRegSize> head(where.data(), kRegSize);\n  if (auto res = findFixed(head, x)) {\n    return res;\n  }\n\n  std::span<const T, N - kRegSize> tail(where.data() + kRegSize, N - kRegSize);\n  if (auto res = findFixed(tail, x)) {\n    return *res + kRegSize;\n  }\n  return std::nullopt;\n}\n\ntemplate <typename Scalar, typename Reg>\nstd::optional<std::size_t> firstTrue(Reg reg) {\n  auto [bits, bitsPerElement] = folly::simd::movemask<Scalar>(reg);\n  if (bits) {\n    return std::countr_zero(bits) / bitsPerElement();\n  }\n  return std::nullopt;\n}\n\n#if FOLLY_X64\n\ntemplate <typename T>\nstd::optional<std::size_t> find16ByteReg(__m128i reg, T x) {\n  if constexpr (sizeof(T) == 1) {\n    return firstTrue<T>(_mm_cmpeq_epi8(reg, _mm_set1_epi8(x)));\n  } else if constexpr (sizeof(T) == 2) {\n    return firstTrue<T>(_mm_cmpeq_epi16(reg, _mm_set1_epi16(x)));\n  } else if constexpr (sizeof(T) == 4) {\n    return firstTrue<T>(_mm_cmpeq_epi32(reg, _mm_set1_epi32(x)));\n  }\n}\n\ntemplate <typename T>\nstd::optional<std::size_t> find8bytes(const T* from, T x) {\n  std::uint64_t reg;\n  std::memcpy(&reg, from, 8);\n  return find16ByteReg(_mm_set1_epi64x(reg), x);\n}\n\ntemplate <typename T>\nstd::optional<std::size_t> find16bytes(const T* from, T x) {\n  __m128i reg = _mm_loadu_si128(reinterpret_cast<const __m128i*>(from));\n  return find16ByteReg(reg, x);\n}\n\n#if defined(__AVX2__)\ntemplate <typename T>\nstd::optional<std::size_t> find32ByteReg(__m256i reg, T x) {\n  if constexpr (sizeof(T) == 1) {\n    return firstTrue<T>(_mm256_cmpeq_epi8(reg, _mm256_set1_epi8(x)));\n  } else if constexpr (sizeof(T) == 2) {\n    return firstTrue<T>(_mm256_cmpeq_epi16(reg, _mm256_set1_epi16(x)));\n  } else if constexpr (sizeof(T) == 4) {\n    return firstTrue<T>(_mm256_cmpeq_epi32(reg, _mm256_set1_epi32(x)));\n  } else if constexpr (sizeof(T) == 8) {\n    return firstTrue<T>(_mm256_cmpeq_epi64(reg, _mm256_set1_epi64x(x)));\n  }\n}\n\ntemplate <typename T>\nstd::optional<std::size_t> find32bytes(const T* from, T x) {\n  __m256i reg = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(from));\n  return find32ByteReg(reg, x);\n}\n\n#endif\n#endif\n\n#if FOLLY_AARCH64\n\ntemplate <typename T>\nstd::optional<std::size_t> find8bytes(const T* from, T x) {\n  if constexpr (std::same_as<T, std::uint8_t>) {\n    return firstTrue<T>(vceq_u8(vld1_u8(from), vdup_n_u8(x)));\n  } else if constexpr (std::same_as<T, std::uint16_t>) {\n    return firstTrue<T>(vceq_u16(vld1_u16(from), vdup_n_u16(x)));\n  } else {\n    return firstTrue<T>(vceq_u32(vld1_u32(from), vdup_n_u32(x)));\n  }\n}\n\ntemplate <typename T>\nstd::optional<std::size_t> find16bytes(const T* from, T x) {\n  if constexpr (std::same_as<T, std::uint8_t>) {\n    return firstTrue<T>(vceqq_u8(vld1q_u8(from), vdupq_n_u8(x)));\n  } else if constexpr (std::same_as<T, std::uint16_t>) {\n    return firstTrue<T>(vceqq_u16(vld1q_u16(from), vdupq_n_u16(x)));\n  } else if constexpr (std::same_as<T, std::uint32_t>) {\n    return firstTrue<T>(vceqq_u32(vld1q_u32(from), vdupq_n_u32(x)));\n  } else {\n    return firstTrue<T>(vceqq_u64(vld1q_u64(from), vdupq_n_u64(x)));\n  }\n}\n\n#endif\n\n} // namespace find_fixed_detail\n\ntemplate <\n    FollyFindFixedSupportedType T,\n    std::convertible_to<T> U,\n    std::size_t N>\nconstexpr std::optional<std::size_t> findFixed(std::span<const T, N> where, U x)\n  requires(sizeof(T) * N <= 64)\n{\n  if constexpr (!std::is_same_v<T, U>) {\n    return findFixed(where, static_cast<T>(x));\n  } else if (std::is_constant_evaluated()) {\n    return find_fixed_detail::findFixedConstexpr(std::span<const T>(where), x);\n  } else {\n    return find_fixed_detail::findFixedDispatch(\n        simd::detail::asSimdFriendlyUint(where),\n        simd::detail::asSimdFriendlyUint(x));\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/simd/Ignore.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/lang/Bits.h>\n\n#include <type_traits>\n\nnamespace folly::simd {\n\n/**\n * ignore(_none/_extrema)\n *\n * tag types to be used in some simd operations.\n *\n * They are used to indicate to the function that\n * some of the elements in are garbage.\n *\n * ignore_none indicates that the whole register is used.\n * ignore_extrema.first, .last show how many elements are out of the data.\n *\n * Example:\n * register: [true, true, false, false, false, false, false, true]\n * indexes   [0,    1,    2,     3,     4,     5,     6,     7   ]\n *\n * ignore_extrema{.first = 1, .last = 2}\n * means that elements with indexes 0, 6, and 7 will be ignored\n * (w/e that means for an operation)\n */\n\nstruct ignore_extrema {\n  int first = 0;\n  int last = 0;\n};\n\nstruct ignore_none {};\n\n} // namespace folly::simd\n"
  },
  {
    "path": "folly/algorithm/simd/Movemask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/algorithm/simd/Ignore.h>\n#include <folly/lang/Bits.h>\n\n#include <cstdint>\n#include <type_traits>\n#include <utility>\n\n#if FOLLY_SSE >= 2\n#include <immintrin.h>\n#endif\n\n#if FOLLY_NEON\n#include <arm_neon.h>\n#endif\n\nFOLLY_PUSH_WARNING\nFOLLY_GCC_DISABLE_WARNING(\"-Wignored-attributes\")\n\nnamespace folly::simd {\n\n/**\n * movemask\n * movemask_fn\n *\n * This is a low level utility used for simd search algorithms.\n *\n * It is a logical extension of _mm_movemask_epi8 for different types\n * for both x86 and arm.\n *\n * Main interface looks like this:\n * folly::simd::movemask<scalar_type>(simdRegister)\n *   -> std::pair<Bits, BitsPerElement>;\n *\n * scalar type - type of element in the simdRegister\n *\n *  Bits - unsigned integral, containing the bitmask (first is lowest bit).\n *  BitsPerElement - std::integral_constant with number of bits per element.\n *\n * There are also overloads taking `ignore`\n *\n *   folly::simd::movemask<T>(nativeRegister, ignore_extrema)\n *   folly::simd::movemask<T>(nativeRegister, ignore_none)\n *\n * These are there if not all the native register contains valid results,\n * and some need to be ignored (zeroed out)\n *\n * Example: find in 8 shorts on arm.\n *\n *  std::optional<std::uint32_t> findUint16(\n *       std::span<const std::uint16_t> haystack,\n *       std::uint16_t needle) {\n *    uint16x8_t loaded = vld1q_u16(arr.data());\n *    uint16x8_t simdNeedle = vdupq_n_u16(needle);\n *    uint16x8_t test = vceqq_u16(loaded, simdNeedle);\n *\n *    auto [bits, bitsPerElement] = folly::simd::movemask<std::uint16_t>(test);\n *    if (!bits) {\n *      return std::nullopt;\n *    }\n *    return std::countl_zero(bits) / bitsPerElement();\n *  }\n *\n * Arm implementation is based on:\n * https://github.com/jfalcou/eve/blob/a2e2cf539e36e9a3326800194ad5206a8ef3f5b7/include/eve/detail/function/simd/arm/neon/movemask.hpp#L48\n *\n **/\n\ntemplate <typename Scalar>\nstruct movemask_fn {\n  template <typename Reg>\n  auto operator()(Reg reg) const;\n\n  template <typename Reg, typename Ignore>\n  auto operator()(Reg reg, Ignore ignore) const;\n};\n\ntemplate <typename Scalar>\ninline constexpr movemask_fn<Scalar> movemask;\n\n#if FOLLY_SSE >= 2\n\ntemplate <typename Scalar>\ntemplate <typename Reg>\nFOLLY_ERASE auto movemask_fn<Scalar>::operator()(Reg reg) const {\n  std::integral_constant<std::uint32_t, sizeof(Scalar) == 2 ? 2 : 1>\n      bitsPerElement;\n\n  using uint_t = std::\n      conditional_t<std::is_same_v<Reg, __m128i>, std::uint16_t, std::uint32_t>;\n\n  auto mmask = static_cast<uint_t>([&] {\n    if constexpr (std::is_same_v<Reg, __m128i>) {\n      if constexpr (sizeof(Scalar) <= 2) {\n        return _mm_movemask_epi8(reg);\n      } else if constexpr (sizeof(Scalar) == 4) {\n        return _mm_movemask_ps(_mm_castsi128_ps(reg));\n      } else if constexpr (sizeof(Scalar) == 8) {\n        return _mm_movemask_pd(_mm_castsi128_pd(reg));\n      }\n    }\n#if defined(__AVX2__)\n    else if constexpr (std::is_same_v<Reg, __m256i>) {\n      if constexpr (sizeof(Scalar) <= 2) {\n        return _mm256_movemask_epi8(reg);\n      } else if constexpr (sizeof(Scalar) == 4) {\n        return _mm256_movemask_ps(_mm256_castsi256_ps(reg));\n      } else if constexpr (sizeof(Scalar) == 8) {\n        return _mm256_movemask_pd(_mm256_castsi256_pd(reg));\n      }\n    }\n#endif\n  }());\n  return std::pair{mmask, bitsPerElement};\n}\n\n#endif\n\n#if FOLLY_NEON\n\nnamespace detail {\n\nFOLLY_ERASE auto movemaskChars16Aarch64(uint8x16_t reg) {\n  uint16x8_t u16s = vreinterpretq_u16_u8(reg);\n  u16s = vshrq_n_u16(u16s, 4);\n  uint8x8_t packed = vmovn_u16(u16s);\n  std::uint64_t bits = vget_lane_u64(vreinterpret_u64_u8(packed), 0);\n  return std::pair{bits, std::integral_constant<std::uint32_t, 4>{}};\n}\n\ntemplate <typename Reg>\nFOLLY_ERASE uint64x1_t asUint64x1Aarch64(Reg reg) {\n  if constexpr (std::is_same_v<Reg, uint64x1_t>) {\n    return reg;\n  } else if constexpr (std::is_same_v<Reg, uint32x2_t>) {\n    return vreinterpret_u64_u32(reg);\n  } else if constexpr (std::is_same_v<Reg, uint16x4_t>) {\n    return vreinterpret_u64_u16(reg);\n  } else {\n    return vreinterpret_u64_u8(reg);\n  }\n}\n\n} // namespace detail\n\ntemplate <typename Scalar>\ntemplate <typename Reg>\nFOLLY_ERASE auto movemask_fn<Scalar>::operator()(Reg reg) const {\n  if constexpr (std::is_same_v<Reg, uint64x2_t>) {\n    return movemask<std::uint32_t>(vmovn_u64(reg));\n  } else if constexpr (std::is_same_v<Reg, uint32x4_t>) {\n    return movemask<std::uint16_t>(vmovn_u32(reg));\n  } else if constexpr (std::is_same_v<Reg, uint16x8_t>) {\n    return movemask<std::uint8_t>(vmovn_u16(reg));\n  } else if constexpr (std::is_same_v<Reg, uint8x16_t>) {\n    return detail::movemaskChars16Aarch64(reg);\n  } else {\n    std::uint64_t mmask = vget_lane_u64(detail::asUint64x1Aarch64(reg), 0);\n    return std::pair{\n        mmask, std::integral_constant<std::uint32_t, sizeof(Scalar) * 8>{}};\n  }\n}\n\n#endif\n\n#if FOLLY_SSE >= 2 || FOLLY_NEON\n\ntemplate <typename Scalar>\ntemplate <typename Reg, typename Ignore>\nFOLLY_ERASE auto movemask_fn<Scalar>::operator()(Reg reg, Ignore ignore) const {\n  auto [bits, bitsPerElement] = operator()(reg);\n\n  if constexpr (std::is_same_v<Ignore, ignore_none>) {\n    return std::pair{bits, bitsPerElement};\n  } else {\n    static constexpr int kCardinal = sizeof(Reg) / sizeof(Scalar);\n\n    int bitsToKeep = (kCardinal - ignore.last) * bitsPerElement;\n\n    bits = clear_n_least_significant_bits(bits, ignore.first * bitsPerElement);\n    bits = clear_n_most_significant_bits(bits, sizeof(bits) * 8 - bitsToKeep);\n    return std::pair{bits, bitsPerElement};\n  }\n}\n\n#endif\n\n} // namespace folly::simd\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/algorithm/simd/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"simd_any_of\",\n    headers = [\"SimdAnyOf.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":simd_for_each\",\n        \":unroll_utils\",\n        \"//folly:c_portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"simd_platform\",\n    headers = [\"SimdPlatform.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly/algorithm/simd:ignore\",\n        \"//folly/algorithm/simd:movemask\",\n        \"//folly/detail:avx2\",\n        \"//folly/detail:sse\",\n        \"//folly/lang:safe_assert\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"simd_contains_impl\",\n    headers = [\"ContainsImpl.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":simd_any_of\",\n        \":simd_platform\",\n        \"//folly:c_portability\",\n        \"//folly/container:span\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"simd_for_each\",\n    headers = [\"SimdForEach.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":unroll_utils\",\n        \"//folly:c_portability\",\n        \"//folly:traits\",\n        \"//folly/algorithm/simd:ignore\",\n        \"//folly/lang:align\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"traits\",\n    headers = [\"Traits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:memory\",\n        \"//folly:traits\",\n        \"//folly/container:span\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"unroll_utils\",\n    headers = [\"UnrollUtils.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly:traits\",\n    ],\n)\n"
  },
  {
    "path": "folly/algorithm/simd/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME simd_any_of\n  HEADERS\n    SimdAnyOf.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_detail_simd_for_each\n    folly_algorithm_simd_detail_unroll_utils\n    folly_c_portability\n)\n\nfolly_add_library(\n  NAME simd_contains_impl\n  HEADERS\n    ContainsImpl.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_detail_simd_any_of\n    folly_algorithm_simd_detail_simd_platform\n    folly_c_portability\n    folly_container_span\n)\n\nfolly_add_library(\n  NAME simd_for_each\n  HEADERS\n    SimdForEach.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_detail_unroll_utils\n    folly_algorithm_simd_ignore\n    folly_c_portability\n    folly_lang_align\n    folly_traits\n)\n\nfolly_add_library(\n  NAME simd_platform\n  HEADERS\n    SimdPlatform.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_ignore\n    folly_algorithm_simd_movemask\n    folly_detail_avx2\n    folly_detail_sse\n    folly_lang_safe_assert\n    folly_portability\n)\n\nfolly_add_library(\n  NAME traits\n  HEADERS\n    Traits.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_container_span\n    folly_memory\n    folly_traits\n)\n\nfolly_add_library(\n  NAME unroll_utils\n  HEADERS\n    UnrollUtils.h\n  EXPORTED_DEPS\n    folly_portability\n    folly_traits\n)\n"
  },
  {
    "path": "folly/algorithm/simd/detail/ContainsImpl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cstring>\n#include <cwchar>\n#include <type_traits>\n\n#include <folly/CPortability.h>\n#include <folly/algorithm/simd/detail/SimdAnyOf.h>\n#include <folly/algorithm/simd/detail/SimdPlatform.h>\n#include <folly/container/span.h>\n\nnamespace folly::simd::detail {\n\n/*\n * The functions in this file are FOLLY_ERASE to make sure\n * that the only place behind a call boundary is the explicit one.\n */\n\ntemplate <typename T>\nFOLLY_ALWAYS_INLINE bool containsImplStd(\n    folly::span<const T> haystack, T needle) {\n  static_assert(\n      std::is_unsigned_v<T>, \"we should only get here for uint8/16/32/64\");\n  if constexpr (sizeof(T) == 1) {\n    auto* ptr = reinterpret_cast<const char*>(haystack.data());\n    auto castNeedle = static_cast<char>(needle);\n    if (haystack.empty()) { // memchr requires not null\n      return false;\n    }\n    return std::memchr(ptr, castNeedle, haystack.size()) != nullptr;\n  } else if constexpr (sizeof(T) == sizeof(wchar_t)) {\n    auto* ptr = reinterpret_cast<const wchar_t*>(haystack.data());\n    auto castNeedle = static_cast<wchar_t>(needle);\n    if (haystack.empty()) { // wmemchr requires not null\n      return false;\n    }\n    return std::wmemchr(ptr, castNeedle, haystack.size()) != nullptr;\n  } else {\n    // Using find instead of any_of on an off chance that the standard library\n    // will add some custom vectorization.\n    // That wouldn't be possible for any_of because of the predicates.\n    return std::find(haystack.begin(), haystack.end(), needle) !=\n        haystack.end();\n  }\n}\n\ntemplate <typename T>\nconstexpr bool hasHandwrittenContains() {\n  return !std::is_same_v<SimdPlatform<T>, void> &&\n      (std::is_same_v<std::uint8_t, T> || std::is_same_v<std::uint16_t, T> ||\n       std::is_same_v<std::uint32_t, T> || std::is_same_v<std::uint64_t, T>);\n}\n\ntemplate <typename T, typename Platform = SimdPlatform<T>>\nFOLLY_ALWAYS_INLINE bool containsImplHandwritten(\n    folly::span<const T> haystack, T needle) {\n  static_assert(!std::is_same_v<Platform, void>);\n  return simdAnyOf<Platform, 4>(\n      haystack.data(),\n      haystack.data() + haystack.size(),\n      [&](typename Platform::reg_t x) {\n        return Platform::equal(x, static_cast<T>(needle));\n      });\n}\n\ntemplate <typename T>\nFOLLY_ALWAYS_INLINE bool containsImpl(folly::span<const T> haystack, T needle) {\n  if constexpr (hasHandwrittenContains<T>()) {\n    return containsImplHandwritten(haystack, needle);\n  } else {\n    return containsImplStd(haystack, needle);\n  }\n}\n\n} // namespace folly::simd::detail\n"
  },
  {
    "path": "folly/algorithm/simd/detail/SimdAnyOf.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CPortability.h>\n#include <folly/algorithm/simd/detail/SimdForEach.h>\n#include <folly/algorithm/simd/detail/UnrollUtils.h>\n\nnamespace folly {\nnamespace simd::detail {\n\n/**\n * AnyOfDelegate\n *\n * Implementation detail of simdAnyOf\n * This is a delegate to simdForEach\n *\n * Based on\n * https://github.com/jfalcou/eve/blob/9309d6d17a35004adb371099d79082c8cc75d3a6/include/eve/module/algo/algo/any_of.hpp#L23\n */\ntemplate <typename Platform, typename I, typename P>\nstruct AnyOfDelegate {\n  // _p to deal with a shadow warning on an old gcc\n  explicit AnyOfDelegate(P _p) : p(_p) {}\n\n  template <typename Ignore, typename UnrollStep>\n  FOLLY_ALWAYS_INLINE bool step(I it, Ignore ignore, UnrollStep) {\n    auto test = p(Platform::loada(it, ignore));\n    res = Platform::any(test, ignore);\n    return res;\n  }\n\n  template <std::size_t N>\n  FOLLY_ALWAYS_INLINE bool unrolledStep(std::array<I, N> arr) {\n    // Don't have to forceinline - no user code dependency\n    auto loaded = detail::UnrollUtils::arrayMap(arr, [](I it) {\n      return Platform::loada(it, ignore_none{});\n    });\n    auto tests = detail::UnrollUtils::arrayMap(loaded, p);\n    auto test = detail::UnrollUtils::arrayReduce(tests, Platform::logical_or);\n    res = Platform::any(test, ignore_none{});\n    return res;\n  }\n\n  P p;\n  bool res = false;\n};\n\n/**\n * simdAnyOf<Platform, unrolling = 4>(f, l, p);\n *\n * Like std::any_of but with vectorized predicates.\n * Predicate should accept Platform::reg_t and return Platform::logical_t.\n *\n * By default is unrolled 4 ways but for expensive predicates you might want to\n * use an unroll factor of 1.\n *\n * Function is marked as FOLLY_ALWAYS_INLINE because we don't want the end users\n * to include this directly, we want to implement a specific function and hide\n * that code behind a compile time boundary.\n */\ntemplate <typename Platform, int unrolling = 4, typename T, typename P>\nFOLLY_ALWAYS_INLINE bool simdAnyOf(T* f, T* l, P p) {\n  AnyOfDelegate<Platform, T*, P> delegate{p};\n  simdForEachAligning<unrolling>(Platform::kCardinal, f, l, delegate);\n  return delegate.res;\n}\n\n} // namespace simd::detail\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/simd/detail/SimdForEach.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CPortability.h>\n#include <folly/Traits.h>\n#include <folly/algorithm/simd/Ignore.h>\n#include <folly/algorithm/simd/detail/UnrollUtils.h>\n#include <folly/lang/Align.h>\n\n#include <array>\n#include <cstdint>\n#include <type_traits>\n\nnamespace folly {\nnamespace simd::detail {\n\n// Based on\n// https://github.com/jfalcou/eve/blob/5264e20c51aeca17675e67abf236ce1ead781c52/include/eve/module/algo/algo/for_each_iteration.hpp#L148\n//\n// Everything is ALWAYS_INLINE because we want to have one top level noinline\n// function that does everything. Otherwise sometimes the compiler tends\n// to mess that up.\n//\n\n/**\n * simdForEachAligning<unrolling>(cardinal, f, l, delegate);\n *\n * The main idea is that you can read from the memory within the page.\n * The beginning and end of the page are aligned to 4KB.\n * That means, the previous aligned address is always safe to read from\n * (requires asan disablement). This is how strlen works\n * https://stackoverflow.com/questions/25566302/vectorized-strlen-getting-away-with-reading-unallocated-memory\n *\n * The interface parameters are:\n * - unrolling: by how much do you want to unroll the main loop. 4 is a good\n * default for simple operations.\n * - cardinal: how big is your register in elements.\n * - f, l: [first, last) range\n * - delegate:\n *     conceptually a callback but has 2 different operations.\n *     - bool step(T*, ignore): to process one register.\n *       Is called for tails and gets ignore_none/ignore_extrema.\n *     - bool unrolledStep(std::array<T*, unrolling>) -\n *       to process the unrolled part. unrolledStep is not called\n *       if unrolling == 1.\n *   Both step and unrolledStep should return true if they would like to break.\n *   Delegate is passed by reference so that the caller can store state in it.\n */\ntemplate <int unrolling, typename T, typename Delegate>\nFOLLY_ALWAYS_INLINE void simdForEachAligning(\n    int cardinal, T* f, T* l, Delegate& delegate);\n\n/**\n * previousAlignedAddress\n *\n * Given a pointer returns a closest pointer aligned to a given size\n * (in elements).\n */\ntemplate <typename T>\nFOLLY_ALWAYS_INLINE T* previousAlignedAddress(T* ptr, int to) {\n  return align_floor(ptr, sizeof(T) * to);\n}\n\n/**\n * SimdForEachMainLoop\n *\n * Implementation detail of simdForEach\n *\n * Regardless of how you chose to handle tails, the middle will be the same.\n * The operator() returns true if the delegate returned to break.\n *\n * There are two variations:\n * - no unrolling (unroll<1>)\n * - unrolling > 1\n *\n * For explanation of parameters see simdForEachAligning\n */\nstruct SimdForEachMainLoop {\n  template <typename T, typename Delegate>\n  FOLLY_ALWAYS_INLINE bool operator()(\n      int cardinal, T*& f, T* l, Delegate& delegate, index_constant<1>) const {\n    while (f != l) {\n      if (delegate.step(f, ignore_none{}, index_constant<0>{})) {\n        return true;\n      }\n      f += cardinal;\n    }\n\n    return false;\n  }\n\n  template <typename T, typename Delegate>\n  struct SmallStepsLambda {\n    bool& shouldBreak;\n    int cardinal;\n    T*& f;\n    T* l;\n    Delegate& delegate;\n\n    template <std::size_t i>\n    FOLLY_ALWAYS_INLINE bool operator()(index_constant<i> unrollI) {\n      if (f == l) {\n        return true;\n      }\n\n      shouldBreak = delegate.step(f, ignore_none{}, unrollI);\n      f += cardinal;\n      return shouldBreak;\n    }\n  };\n\n  template <typename T, typename Delegate, std::size_t unrolling>\n  FOLLY_ALWAYS_INLINE bool operator()(\n      int cardinal, T*& f, T* l, Delegate& delegate, index_constant<unrolling>)\n      const {\n    // Not enough to fully unroll explanation.\n    //\n    // There are a few approaches to handle this:\n    // 1. Duff's device. Is not good for simd algorithms, we are not that\n    // pressed for space and we'd like the code to work differently when\n    // we have many registers to process.\n    // 2. Traditional: do unrolled steps first, then single steps.\n    // 3. What we do here: put single steps before the unrolled ones to also do\n    //    them in the beginning.\n    //\n    // The reason to prefer 3 over 2 is that unrolled part often would need more\n    // set up, and this allows us to avoid it for small arrays.\n\n    // Jump to this while true will be performed at most once, to do final\n    // single steps.\n    while (true) {\n      // Delegate said we should break. We might also stop because we reached\n      // the end.\n      bool shouldBreak = false;\n\n      // single steps\n      if (UnrollUtils::unrollUntil<unrolling>(SmallStepsLambda<T, Delegate>{\n              shouldBreak, cardinal, f, l, delegate})) {\n        return shouldBreak;\n      }\n\n      for (std::ptrdiff_t bigStepsCount = (l - f) / (cardinal * unrolling);\n           bigStepsCount != 0;\n           --bigStepsCount) {\n        std::array<T*, unrolling> arr;\n        // Since there is no callback, we can rely on the inlining\n        UnrollUtils::unrollUntil<unrolling>([&](auto idx) {\n          arr[idx()] = f;\n          f += cardinal;\n          return false;\n        });\n        if (delegate.unrolledStep(arr)) {\n          return true;\n        }\n      }\n    }\n  }\n};\n\n// Comment is at the top of the file.\ntemplate <int unrolling, typename T, typename Delegate>\nFOLLY_ALWAYS_INLINE void simdForEachAligning(\n    int cardinal, T* f, T* l, Delegate& delegate) {\n  if (f == l) {\n    return;\n  }\n\n  T* af = previousAlignedAddress(f, cardinal);\n  T* al = previousAlignedAddress(l, cardinal);\n\n  ignore_extrema ignore{static_cast<int>(f - af), 0};\n  if (af != al) {\n    // first chunk\n    if (delegate.step(af, ignore, index_constant<0>{})) {\n      return;\n    }\n    ignore.first = 0;\n    af += cardinal;\n\n    if (SimdForEachMainLoop{}(\n            cardinal, af, al, delegate, index_constant<unrolling>{})) {\n      return;\n    }\n\n    // Here af might be exactly at the end of page.\n    if (af == l) {\n      return;\n    }\n  }\n\n  ignore.last = static_cast<int>(af + cardinal - l);\n  delegate.step(af, ignore, index_constant<0>{});\n}\n\n} // namespace simd::detail\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/simd/detail/SimdPlatform.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/algorithm/simd/Ignore.h>\n#include <folly/algorithm/simd/Movemask.h>\n#include <folly/algorithm/simd/detail/SimdPlatform.h>\n#include <folly/detail/Avx2.h>\n#include <folly/detail/Sse.h>\n#include <folly/lang/SafeAssert.h>\n\n#include <array>\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n#include <immintrin.h>\n#endif\n\n#if FOLLY_AARCH64\n#include <arm_neon.h>\n#endif\n\nnamespace folly {\nnamespace simd::detail {\n\n/**\n * SimdPlatform<T>\n *\n * Common interface for some SIMD operations between: sse4.2, avx2,\n * arm-neon.\n *\n * Supported types for T at the moment are uint8_16/uint16_t/uint32_t/uint64_t\n *\n * If it's not one of the supported platforms:\n *  std::same_as<SimdPlatform<T>, void>\n *  There is also a macro: FOLLY_DETAIL_HAS_SIMD_PLATFORM set to 1 or 0\n *\n **/\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2) || FOLLY_AARCH64\n\ntemplate <typename Platform>\nstruct SimdPlatformCommon {\n  /**\n   * scalar_t - type of scalar we operate on (uint8_t, uint16_t etc)\n   * reg_t - type of a simd register (__m128i)\n   * logical_t - type of a simd logical register (matches reg_t so far)\n   **/\n  using scalar_t = typename Platform::scalar_t;\n  using reg_t = typename Platform::reg_t;\n  using logical_t = typename Platform::logical_t;\n\n  static constexpr int kCardinal = sizeof(reg_t) / sizeof(scalar_t);\n\n  /**\n   * loads:\n   * precondition: at least one element should be not ignored.\n   *\n   * loada - load from an aligned (to sizeof(reg_t)) address\n   * loadu - load from an unaligned address\n   * unsafeLoadu - load from an unaligned address that disables sanitizers.\n   *               This is for reading a register within a page\n   *               but maybe outside of the array's boundary.\n   *\n   * Ignored values can be garbage.\n   **/\n  template <typename Ignore>\n  static reg_t loada(const scalar_t* ptr, Ignore);\n  static reg_t loadu(const scalar_t* ptr, ignore_none);\n  static reg_t unsafeLoadu(const scalar_t* ptr, ignore_none);\n\n  /**\n   * Comparing reg_t against the scalar.\n   *\n   * NOTE: less_equal only implemented for uint8_t\n   *       for now.\n   **/\n  static logical_t equal(reg_t reg, scalar_t x);\n  static logical_t less_equal(reg_t reg, scalar_t x);\n\n  /**\n   * logical reduction\n   **/\n  template <typename Ignore>\n  static bool any(logical_t logical, Ignore ignore);\n\n  template <typename Ignore>\n  static bool all(logical_t logical, Ignore ignore);\n\n  /**\n   * logical operations\n   **/\n  static logical_t logical_or(logical_t x, logical_t y);\n\n  /**\n   * Converting register to an array for debugging\n   **/\n  static auto toArray(reg_t x);\n};\n\ntemplate <typename Platform>\ntemplate <typename Ignore>\nFOLLY_ERASE auto SimdPlatformCommon<Platform>::loada(\n    const scalar_t* ptr, [[maybe_unused]] Ignore ignore) -> reg_t {\n  if constexpr (std::is_same_v<ignore_none, Ignore>) {\n    // There is not point to aligned load instructions\n    // on modern cpus. Arm doesn't even have any.\n    return loadu(ptr, ignore_none{});\n  } else {\n    // We have a precondition: at least one element is loaded.\n    // From this we can prove that we can unsafely load from\n    // and aligned address.\n    //\n    // Here is an explanation from Stephen Canon:\n    // https://stackoverflow.com/questions/25566302/vectorized-strlen-getting-away-with-reading-unallocated-memory\n    if constexpr (!kIsSanitizeAddress) {\n      return unsafeLoadu(ptr, ignore_none{});\n    } else {\n      // If the sanitizers are enabled, we want to trigger the issues.\n      // We also want to match the garbage values with/without asan,\n      // so that testing works on the same values as prod.\n      scalar_t buf[kCardinal];\n      std::memcpy(\n          buf + ignore.first,\n          ptr + ignore.first,\n          (kCardinal - ignore.first - ignore.last) * sizeof(scalar_t));\n\n      auto testAgainst = loadu(buf, ignore_none{});\n      auto res = unsafeLoadu(ptr, ignore_none{});\n\n      // Extra sanity check.\n      FOLLY_SAFE_CHECK(all(Platform::equal(res, testAgainst), ignore));\n      return res;\n    }\n  }\n}\n\ntemplate <typename Platform>\nFOLLY_ERASE auto SimdPlatformCommon<Platform>::loadu(\n    const scalar_t* ptr, ignore_none) -> reg_t {\n  return Platform::loadu(ptr);\n}\n\ntemplate <typename Platform>\nFOLLY_ERASE auto SimdPlatformCommon<Platform>::unsafeLoadu(\n    const scalar_t* ptr, ignore_none) -> reg_t {\n  return Platform::unsafeLoadu(ptr);\n}\n\ntemplate <typename Platform>\nFOLLY_ERASE auto SimdPlatformCommon<Platform>::equal(reg_t reg, scalar_t x)\n    -> logical_t {\n  return Platform::equal(reg, Platform::broadcast(x));\n}\n\ntemplate <typename Platform>\nFOLLY_ERASE auto SimdPlatformCommon<Platform>::less_equal(reg_t reg, scalar_t x)\n    -> logical_t {\n  static_assert(std::is_same_v<scalar_t, std::uint8_t>, \"not implemented\");\n  return Platform::less_equal(reg, Platform::broadcast(x));\n}\n\ntemplate <typename Platform>\ntemplate <typename Ignore>\nFOLLY_ERASE bool SimdPlatformCommon<Platform>::any(\n    logical_t logical, Ignore ignore) {\n  if constexpr (std::is_same_v<Ignore, ignore_none>) {\n    return Platform::any(logical);\n  } else {\n    return movemask<scalar_t>(logical, ignore).first;\n  }\n}\n\ntemplate <typename Platform>\ntemplate <typename Ignore>\nFOLLY_ERASE bool SimdPlatformCommon<Platform>::all(\n    logical_t logical, Ignore ignore) {\n  if constexpr (std::is_same_v<Ignore, ignore_none>) {\n    return Platform::all(logical);\n  } else {\n    auto [bits, bitsPerElement] = movemask<scalar_t>(logical, ignore_none{});\n\n    auto expected = n_least_significant_bits<decltype(bits)>(\n        bitsPerElement * (kCardinal - ignore.last));\n    expected =\n        clear_n_least_significant_bits(expected, ignore.first * bitsPerElement);\n\n    return (bits & expected) == expected;\n  }\n}\n\ntemplate <typename Platform>\nFOLLY_ERASE auto SimdPlatformCommon<Platform>::logical_or(\n    logical_t x, logical_t y) -> logical_t {\n  return Platform::logical_or(x, y);\n}\n\ntemplate <typename Platform>\nFOLLY_ERASE auto SimdPlatformCommon<Platform>::toArray(reg_t x) {\n  std::array<scalar_t, kCardinal> res;\n  std::memcpy(&res, &x, sizeof(x));\n  return res;\n}\n\n#endif\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n\ntemplate <typename T>\nstruct SimdSse42PlatformSpecific {\n  using scalar_t = T;\n  using reg_t = __m128i;\n  using logical_t = reg_t;\n\n  static constexpr std::size_t kCardinal = sizeof(reg_t) / sizeof(scalar_t);\n\n  FOLLY_ERASE\n  static reg_t loadu(const scalar_t* p) {\n    return _mm_loadu_si128(reinterpret_cast<const reg_t*>(p));\n  }\n\n  FOLLY_ERASE\n  static reg_t unsafeLoadu(const scalar_t* p) {\n    return folly::detail::_mm_loadu_si128_unchecked(\n        reinterpret_cast<const reg_t*>(p));\n  }\n\n  FOLLY_ERASE\n  static reg_t broadcast(scalar_t x) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return _mm_set1_epi8(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return _mm_set1_epi16(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return _mm_set1_epi32(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return _mm_set1_epi64x(x);\n    }\n  }\n\n  FOLLY_ERASE\n  static logical_t equal(reg_t x, reg_t y) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return _mm_cmpeq_epi8(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return _mm_cmpeq_epi16(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return _mm_cmpeq_epi32(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return _mm_cmpeq_epi64(x, y);\n    }\n  }\n\n  FOLLY_ERASE\n  static logical_t less_equal(reg_t x, reg_t y) {\n    static_assert(\n        std::is_same_v<std::uint8_t, scalar_t>, \"other types not implemented\");\n    // No unsigned comparisons on x86\n    // less equal <=> equal (min)\n    reg_t min = _mm_min_epu8(x, y);\n    return equal(x, min);\n  }\n\n  FOLLY_ERASE\n  static logical_t logical_or(logical_t x, logical_t y) {\n    return _mm_or_si128(x, y);\n  }\n\n  FOLLY_ERASE\n  static bool any(logical_t log) { return movemask<scalar_t>(log).first; }\n\n#if 0 // disabled until we have a test where this is relevant\n  FOLLY_ERASE\n  static bool all(logical_t log) {\n    auto [bits, bitsPerElement] = movemask<scalar_t>(log);\n    return movemask<scalar_t>(log) ==\n        n_least_significant_bits<decltype(bits)>(kCardinal * bitsPerElement);\n  }\n#endif\n};\n\n#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 1\n\ntemplate <typename T>\nstruct SimdSse42Platform : SimdPlatformCommon<SimdSse42PlatformSpecific<T>> {};\n\n#if defined(__AVX2__)\n\ntemplate <typename T>\nstruct SimdAvx2PlatformSpecific {\n  using scalar_t = T;\n  using reg_t = __m256i;\n  using logical_t = reg_t;\n\n  static constexpr std::size_t kCardinal = sizeof(reg_t) / sizeof(scalar_t);\n\n  FOLLY_ERASE\n  static reg_t loadu(const scalar_t* p) {\n    return _mm256_loadu_si256(reinterpret_cast<const reg_t*>(p));\n  }\n\n  FOLLY_ERASE\n  static reg_t unsafeLoadu(const scalar_t* p) {\n    return folly::detail::_mm256_loadu_si256_unchecked(\n        reinterpret_cast<const reg_t*>(p));\n  }\n\n  FOLLY_ERASE\n  static reg_t broadcast(scalar_t x) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return _mm256_set1_epi8(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return _mm256_set1_epi16(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return _mm256_set1_epi32(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return _mm256_set1_epi64x(x);\n    }\n  }\n\n  FOLLY_ERASE\n  static logical_t equal(reg_t x, reg_t y) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return _mm256_cmpeq_epi8(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return _mm256_cmpeq_epi16(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return _mm256_cmpeq_epi32(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return _mm256_cmpeq_epi64(x, y);\n    }\n  }\n\n  FOLLY_ERASE\n  static logical_t less_equal(reg_t x, reg_t y) {\n    static_assert(\n        std::is_same_v<std::uint8_t, scalar_t>, \"other types not implemented\");\n    // See SSE comment\n    reg_t min = _mm256_min_epu8(x, y);\n    return _mm256_cmpeq_epi8(x, min);\n  }\n\n  FOLLY_ERASE\n  static logical_t logical_or(logical_t x, logical_t y) {\n    return _mm256_or_si256(x, y);\n  }\n\n  FOLLY_ERASE\n  static bool any(logical_t log) { return simd::movemask<scalar_t>(log).first; }\n\n#if 0 // disabled until we have a test where this is relevant\n  FOLLY_ERASE\n  static bool all(logical_t log) {\n    auto [bits, bitsPerElement] = movemask<scalar_t>(log);\n    return movemask<scalar_t>(log) ==\n        n_least_significant_bits<decltype(bits)>(kCardinal * bitsPerElement);\n  }\n#endif\n};\n\ntemplate <typename T>\nstruct SimdAvx2Platform : SimdPlatformCommon<SimdAvx2PlatformSpecific<T>> {};\n\ntemplate <typename T>\nusing SimdPlatform = SimdAvx2Platform<T>;\n\n#else\n\ntemplate <typename T>\nusing SimdPlatform = SimdSse42Platform<T>;\n\n#endif\n\n#elif FOLLY_AARCH64\n\ntemplate <typename T>\nstruct SimdAarch64PlatformSpecific {\n  using scalar_t = T;\n\n  FOLLY_ERASE\n  static auto loadu(const scalar_t* p) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return vld1q_u8(p);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return vld1q_u16(p);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return vld1q_u32(p);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return vld1q_u64(p);\n    }\n  }\n\n  using reg_t = decltype(loadu(nullptr));\n  using logical_t = reg_t;\n\n  FOLLY_DISABLE_SANITIZERS\n  FOLLY_ERASE\n  static reg_t unsafeLoadu(const scalar_t* p) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return vld1q_u8(p);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return vld1q_u16(p);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return vld1q_u32(p);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return vld1q_u64(p);\n    }\n  }\n\n  FOLLY_ERASE\n  static reg_t broadcast(scalar_t x) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return vdupq_n_u8(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return vdupq_n_u16(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return vdupq_n_u32(x);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return vdupq_n_u64(x);\n    }\n  }\n\n  FOLLY_ERASE\n  static logical_t equal(reg_t x, reg_t y) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return vceqq_u8(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return vceqq_u16(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return vceqq_u32(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return vceqq_u64(x, y);\n    }\n  }\n\n  FOLLY_ERASE\n  static logical_t less_equal(reg_t x, reg_t y) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return vcleq_u8(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return vcleq_u16(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return vcleq_u32(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return vcleq_u64(x, y);\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE\n  static logical_t logical_or(logical_t x, logical_t y) {\n    if constexpr (std::is_same_v<scalar_t, std::uint8_t>) {\n      return vorrq_u8(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint16_t>) {\n      return vorrq_u16(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint32_t>) {\n      return vorrq_u32(x, y);\n    } else if constexpr (std::is_same_v<scalar_t, std::uint64_t>) {\n      return vorrq_u64(x, y);\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE\n  static bool any(logical_t log) {\n    // https://github.com/dotnet/runtime/pull/75864\n    auto u32 = bit_cast<uint32x4_t>(log);\n    u32 = vpmaxq_u32(u32, u32);\n    auto u64 = bit_cast<uint64x2_t>(u32);\n    return vgetq_lane_u64(u64, 0);\n  }\n\n#if 0 // disabled until we have a test where this is relevant\n  FOLLY_ERASE\n  static bool all(logical_t log) {\n    // Not quite what they did in .Net runtime, but\n    // should be close.\n    // https://github.com/dotnet/runtime/pull/75864\n    auto u32 = bit_cast<uint32x4_t>(log);\n    u32 = vpminq_u32(u32, u32);\n    auto u64 = bit_cast<uint64x2_t>(u32);\n    return u64 == n_least_significant_bits<std::uint64_t>(64);\n  }\n#endif\n};\n\n#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 1\n\ntemplate <typename T>\nstruct SimdAarch64Platform\n    : SimdPlatformCommon<SimdAarch64PlatformSpecific<T>> {};\n\ntemplate <typename T>\nusing SimdPlatform = SimdAarch64Platform<T>;\n\n#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 1\n\n#else\n\n#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 0\n\ntemplate <typename T>\nusing SimdPlatform = void;\n\n#endif\n\n} // namespace simd::detail\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/simd/detail/Traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CPortability.h>\n#include <folly/Memory.h>\n#include <folly/Traits.h>\n#include <folly/container/span.h>\n\n#include <concepts>\n#include <type_traits>\n\nnamespace folly::simd::detail {\n\ntemplate <typename T>\nauto findSimdFriendlyEquivalent() {\n  static_assert(std::is_same_v<T, remove_cvref_t<T>>);\n  if constexpr (std::is_enum_v<T>) {\n    return findSimdFriendlyEquivalent<std::underlying_type_t<T>>();\n  } else if constexpr (std::is_pointer_v<T>) {\n    // We use signed numbers for pointers because x86 support for signed\n    // numbers is better and we can get away with it, in terms of correctness.\n    return int_bits_t<sizeof(T) * 8>{};\n  } else if constexpr (std::is_floating_point_v<T>) {\n    if constexpr (sizeof(T) == 4) {\n      return float{};\n    } else {\n      return double{};\n    }\n  } else if constexpr (std::is_signed_v<T>) {\n    return int_bits_t<sizeof(T) * 8>{};\n  } else if constexpr (std::is_unsigned_v<T>) {\n    return uint_bits_t<sizeof(T) * 8>{};\n  }\n}\n\ntemplate <typename T>\nconstexpr bool has_simd_friendly_equivalent_scalar = !std::is_void_v<\n    decltype(findSimdFriendlyEquivalent<std::remove_const_t<T>>())>;\n\ntemplate <typename T>\nusing simd_friendly_equivalent_scalar_t = std::enable_if_t<\n    has_simd_friendly_equivalent_scalar<T>,\n    like_t<T, decltype(findSimdFriendlyEquivalent<std::remove_const_t<T>>())>>;\n\ntemplate <typename T>\nconstexpr bool has_integral_simd_friendly_equivalent_scalar_v =\n    std::is_integral_v< // void will return false\n        decltype(findSimdFriendlyEquivalent<std::remove_const_t<T>>())>;\n\ntemplate <typename T>\nusing unsigned_simd_friendly_equivalent_scalar_t = std::enable_if_t<\n    has_integral_simd_friendly_equivalent_scalar_v<T>,\n    like_t<T, uint_bits_t<sizeof(T) * 8>>>;\n\ntemplate <typename R>\nusing span_for = decltype(folly::span(std::declval<const R&>()));\n\nstruct AsSimdFriendlyFn {\n  template <typename T, std::size_t extent>\n  FOLLY_ERASE auto operator()(folly::span<T, extent> s) const\n      -> folly::span<simd_friendly_equivalent_scalar_t<T>, extent> {\n    return reinterpret_span_cast<simd_friendly_equivalent_scalar_t<T>>(s);\n  }\n\n  template <typename R>\n  FOLLY_ERASE auto operator()(R&& r) const\n      -> decltype(operator()(span_for<R>(r))) {\n    return operator()(folly::span(r));\n  }\n\n  template <typename T>\n  FOLLY_ERASE constexpr auto operator()(T x) const\n      -> simd_friendly_equivalent_scalar_t<T> {\n    using res_t = simd_friendly_equivalent_scalar_t<T>;\n    if constexpr (!std::is_pointer_v<T>) {\n      return static_cast<res_t>(x);\n    } else {\n      return reinterpret_cast<res_t>(x);\n    }\n  }\n};\ninline constexpr AsSimdFriendlyFn asSimdFriendly;\n\nstruct AsSimdFriendlyUintFn {\n  template <typename T, std::size_t extent>\n  FOLLY_ERASE auto operator()(folly::span<T, extent> s) const\n      -> folly::span<unsigned_simd_friendly_equivalent_scalar_t<T>, extent> {\n    return reinterpret_span_cast<unsigned_simd_friendly_equivalent_scalar_t<T>>(\n        s);\n  }\n\n  template <typename R>\n  FOLLY_ERASE auto operator()(R&& r) const\n      -> decltype(operator()(span_for<R>(r))) {\n    return operator()(folly::span(r));\n  }\n\n  template <typename T>\n  FOLLY_ERASE constexpr auto operator()(T x) const\n      -> unsigned_simd_friendly_equivalent_scalar_t<T> {\n    using res_t = unsigned_simd_friendly_equivalent_scalar_t<T>;\n    if constexpr (!std::is_pointer_v<T>) {\n      return static_cast<res_t>(x);\n    } else {\n      return reinterpret_cast<res_t>(x);\n    }\n  }\n};\ninline constexpr AsSimdFriendlyUintFn asSimdFriendlyUint;\n\n} // namespace folly::simd::detail\n"
  },
  {
    "path": "folly/algorithm/simd/detail/UnrollUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n\n#include <array>\n#include <type_traits>\n\nnamespace folly::simd::detail {\n\n/**\n * UnrollUtils\n *\n * Unfortunately compilers often don't unroll the loops with small\n * fixed number of iterations and/or not unroll them properly.\n *\n * This is a collection of helpers that use templates to do some\n * common unrolled loops.\n */\nstruct UnrollUtils {\n public:\n  /**\n   * arrayMap(x, op)\n   *\n   * Typical \"map\" from functional languages: apply op for each element,\n   * return an array of results.\n   */\n  template <typename T, std::size_t N, typename Op>\n  [[nodiscard]] FOLLY_ALWAYS_INLINE static constexpr auto arrayMap(\n      const std::array<T, N>& x, Op op) {\n    return arrayMapImpl(x, op, std::make_index_sequence<N>());\n  }\n\n  /**\n   * arrayReduce(x, op)\n   *\n   * std::reduce(x.begin(), x.end(), op) but unrolled and orders operations\n   * to minimize dependencies.\n   *\n   * (a + b) + (c + d)\n   */\n  template <typename T, std::size_t N, typename Op>\n  [[nodiscard]] FOLLY_ALWAYS_INLINE static constexpr T arrayReduce(\n      const std::array<T, N>& x, Op op) {\n    return arrayReduceImpl<0, N>(x, op);\n  }\n\n  /**\n   * unrollUntil<N>(op)\n   *\n   * Do operation N times or until it returns true to break.\n   * Op accepts index_constant so it can keep track of a step begin executed.\n   *\n   * Returns whether true if it was interrupted (you can know if the op broke)\n   */\n  template <std::size_t N, typename Op>\n  FOLLY_ALWAYS_INLINE static constexpr bool unrollUntil(Op op) {\n    return unrollUntilImpl(op, std::make_index_sequence<N>{});\n  }\n\n private:\n  template <typename T, std::size_t N, typename Op, std::size_t... i>\n  FOLLY_ALWAYS_INLINE static constexpr auto arrayMapImpl(\n      const std::array<T, N>& x, Op op, std::index_sequence<i...> /*unused*/) {\n    using U = decltype(op(std::declval<const T&>()));\n\n    FOLLY_PUSH_WARNING\n    // This is a very common gcc issue,\n    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97222 apparently discarding\n    // it here is fine and done through out.\n    FOLLY_GCC_DISABLE_WARNING(\"-Wignored-attributes\")\n    std::array<U, N> res{{op(x[i])...}};\n    FOLLY_POP_WARNING\n    return res;\n  }\n\n  template <\n      std::size_t f,\n      std::size_t l,\n      typename T,\n      std::size_t N,\n      typename Op>\n  FOLLY_ALWAYS_INLINE static constexpr std::enable_if_t<l - f == 1, T>\n  arrayReduceImpl(std::array<T, N> const& x, Op /*unused*/) {\n    return x[f];\n  }\n\n  template <\n      std::size_t f,\n      std::size_t l,\n      typename T,\n      std::size_t N,\n      typename Op>\n  FOLLY_ALWAYS_INLINE static constexpr std::enable_if_t<l - f != 1, T>\n  arrayReduceImpl(std::array<T, N> const& x, Op op) {\n    constexpr std::size_t n = l - f;\n    T leftSum = arrayReduceImpl<f, f + n / 2>(x, op);\n    T rightSum = arrayReduceImpl<f + n / 2, l>(x, op);\n    return op(leftSum, rightSum);\n  }\n\n  template <typename Op, std::size_t... i>\n  FOLLY_ALWAYS_INLINE static constexpr bool unrollUntilImpl(\n      Op op, std::index_sequence<i...> /*unused*/) {\n    return (... || op(index_constant<i>{}));\n  }\n};\n\n} // namespace folly::simd::detail\n"
  },
  {
    "path": "folly/algorithm/simd/detail/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\nload(\"../../../../defs.bzl\", \"folly_xplat_cxx_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"simd_any_of_test\",\n    srcs = [\n        \"SimdAnyOfTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly/algorithm/simd/detail:simd_any_of\",\n        \"//xplat/folly/algorithm/simd/detail:simd_platform\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"simd_for_each_test\",\n    srcs = [\"SimdForEachTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/algorithm/simd/detail:simd_for_each\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"traits_test\",\n    srcs = [\"TraitsTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/algorithm/simd/detail:traits\",\n        \"//folly/lang:bits\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"unroll_utils_test\",\n    srcs = [\n        \"UnrollUtilsTest.cpp\",\n    ],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/algorithm/simd/detail:unroll_utils\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"simd_any_of_test\",\n    srcs = [\n        \"SimdAnyOfTest.cpp\",\n    ],\n    deps = [\n        \"//folly:range\",\n        \"//folly/algorithm/simd/detail:simd_any_of\",\n        \"//folly/algorithm/simd/detail:simd_platform\",\n        \"//folly/container:span\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/algorithm/simd/detail/test/SimdAnyOfTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/detail/SimdAnyOf.h>\n\n#include <folly/Range.h>\n#include <folly/algorithm/simd/detail/SimdPlatform.h>\n#include <folly/container/span.h>\n#include <folly/portability/GTest.h>\n\n#include <array>\n\n#if FOLLY_DETAIL_HAS_SIMD_PLATFORM\n\nnamespace folly {\nnamespace simd::detail {\n\ntemplate <typename Platform, int unrolling>\nvoid anySpacesTestForPlatformUnrolling(\n    folly::span<const std::uint8_t> s, bool expected) {\n  bool actual = simdAnyOf<Platform, unrolling>(\n      s.data(), s.data() + s.size(), [](typename Platform::reg_t x) {\n        return Platform::equal(x, ' ');\n      });\n  ASSERT_EQ(expected, actual)\n      << folly::StringPiece(folly::reinterpret_span_cast<const char>(s));\n}\n\ntemplate <typename Platform>\nvoid anySpacesTestForPlatform(\n    folly::span<const std::uint8_t> s, bool expected) {\n  ASSERT_NO_FATAL_FAILURE(\n      (anySpacesTestForPlatformUnrolling<Platform, 1>(s, expected)));\n  ASSERT_NO_FATAL_FAILURE(\n      (anySpacesTestForPlatformUnrolling<Platform, 2>(s, expected)));\n  ASSERT_NO_FATAL_FAILURE(\n      (anySpacesTestForPlatformUnrolling<Platform, 3>(s, expected)));\n  ASSERT_NO_FATAL_FAILURE(\n      (anySpacesTestForPlatformUnrolling<Platform, 4>(s, expected)));\n}\n\nvoid anySpacesTest(folly::StringPiece sChars, bool expected) {\n  auto s =\n      folly::reinterpret_span_cast<const std::uint8_t>(folly::span(sChars));\n\n  ASSERT_NO_FATAL_FAILURE(\n      anySpacesTestForPlatform<SimdPlatform<std::uint8_t>>(s, expected));\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n  ASSERT_NO_FATAL_FAILURE(\n      anySpacesTestForPlatform<SimdSse42Platform<std::uint8_t>>(s, expected));\n#if defined(__AVX2__)\n  ASSERT_NO_FATAL_FAILURE(\n      anySpacesTestForPlatform<SimdAvx2Platform<std::uint8_t>>(s, expected));\n#endif\n#endif\n#if FOLLY_AARCH64\n  ASSERT_NO_FATAL_FAILURE(\n      anySpacesTestForPlatform<SimdAarch64Platform<std::uint8_t>>(s, expected));\n#endif\n}\n\n// Main tests for this are coming from fuzzing users\n\nTEST(SimdAnyOfSimple, Basic) {\n  anySpacesTest(\"\", false);\n  anySpacesTest(\" \", true);\n  anySpacesTest(\"a\", false);\n\n  anySpacesTest(\"aaaa aaaa\", true);\n  anySpacesTest(\"aaaaaaaaaaaa\", false);\n\n  anySpacesTest(std::string(15u, 'a'), false);\n}\n\nTEST(SimdAnyOfSimple, Ignore) {\n  alignas(64) std::array<char, 64> buffer;\n  buffer.fill(' ');\n  for (auto& c : buffer) {\n    c = 'a';\n    ASSERT_NO_FATAL_FAILURE(anySpacesTest({&c, 1}, false));\n    c = ' ';\n  }\n}\n\nTEST(SimdAnyOfSimple, BigChunk) {\n  std::string buffer(300, 'a');\n\n  for (std::size_t i = 0; i != 32; ++i) {\n    for (std::size_t j = 0; j != 32; ++j) {\n      char* f = buffer.data() + i;\n      char* l = buffer.data() + buffer.size() - j;\n\n      folly::StringPiece toTest =\n          folly::StringPiece{f, static_cast<std::size_t>(l - f)};\n\n      anySpacesTest(toTest, false);\n\n      while (f != l) {\n        *f = ' ';\n        anySpacesTest(toTest, true);\n        *f = 'a';\n        ++f;\n      }\n    }\n  }\n}\n\n} // namespace simd::detail\n} // namespace folly\n\n#endif // FOLLY_DETAIL_HAS_SIMD_PLATFORM\n"
  },
  {
    "path": "folly/algorithm/simd/detail/test/SimdForEachTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/detail/SimdForEach.h>\n\n#include <folly/portability/GTest.h>\n\n#include <array>\n\nnamespace folly {\nnamespace simd::detail {\n\nconstexpr int kCardinal = 4;\n\ntemplate <bool kSameUnrollValue>\nstruct TestDelegate {\n  char* stopAt = nullptr;\n\n  template <typename N>\n  bool step(char* s, ignore_extrema ignore, N unroll_i) const {\n    int middle = kCardinal - ignore.first - ignore.last;\n    while (ignore.first--) {\n      EXPECT_EQ(*s, 0);\n      *s++ = 'i';\n    }\n    while (middle--) {\n      EXPECT_EQ(*s, 0);\n      if (kSameUnrollValue) {\n        *s++ = 'a';\n      } else {\n        *s++ = 'a' + unroll_i();\n      }\n    }\n    while (ignore.last--) {\n      *s++ = 'i';\n    }\n\n    return stopAt != nullptr && s > stopAt;\n  }\n\n  template <typename N>\n  bool step(char* s, ignore_none, N unroll_i) const {\n    for (int i = 0; i != kCardinal; ++i) {\n      EXPECT_EQ(*s, 0);\n      if (kSameUnrollValue) {\n        *s++ = 'a';\n      } else {\n        *s++ = 'a' + unroll_i();\n      }\n    }\n    return stopAt != nullptr && s > stopAt;\n  }\n\n  template <std::size_t unroll>\n  bool unrolledStep(std::array<char*, unroll> unrolled) {\n    return detail::UnrollUtils::unrollUntil<static_cast<int>(unroll)>(\n        [&](auto unrollI) {\n          return step(\n              unrolled[unrollI()],\n              ignore_none{},\n              folly::index_constant<decltype(unrollI)::value + ('A' - 'a')>{});\n        });\n  }\n};\n\ntemplate <int unroll, bool kSameUnrollValue = false>\nstd::string run(int offset, int len, int stopAt) {\n  alignas(64) std::array<char, 100u> buf;\n  buf.fill(0);\n\n  TestDelegate<kSameUnrollValue> delegate{\n      stopAt == -1 ? nullptr : buf.data() + stopAt};\n  simdForEachAligning<unroll>(\n      kCardinal, buf.data() + offset, buf.data() + offset + len, delegate);\n  return std::string(buf.data());\n}\n\nstd::string runAllUnrolls(int offset, int len, int stopAt) {\n  std::string res = run<1, /*kSameUnrollValue*/ true>(offset, len, stopAt);\n  EXPECT_EQ(res, (run<2, /*kSameUnrollValue*/ true>(offset, len, stopAt)));\n  EXPECT_EQ(res, (run<3, /*kSameUnrollValue*/ true>(offset, len, stopAt)));\n  EXPECT_EQ(res, (run<4, /*kSameUnrollValue*/ true>(offset, len, stopAt)));\n  return res;\n}\n\nTEST(SimdForEachAligningTest, Tails) {\n  ASSERT_EQ(\"\", runAllUnrolls(0, 0, -1));\n  ASSERT_EQ(\"\", runAllUnrolls(1, 0, -1));\n  ASSERT_EQ(\"\", runAllUnrolls(2, 0, -1));\n  ASSERT_EQ(\"\", runAllUnrolls(3, 0, -1));\n\n  ASSERT_EQ(\"aiii\", runAllUnrolls(0, 1, -1));\n  ASSERT_EQ(\"iaii\", runAllUnrolls(1, 1, -1));\n  ASSERT_EQ(\"iiai\", runAllUnrolls(2, 1, -1));\n  ASSERT_EQ(\"iiia\", runAllUnrolls(3, 1, -1));\n\n  ASSERT_EQ(\"aaii\", runAllUnrolls(0, 2, -1));\n  ASSERT_EQ(\"iaai\", runAllUnrolls(1, 2, -1));\n  ASSERT_EQ(\"iiaa\", runAllUnrolls(2, 2, -1));\n  ASSERT_EQ(\"iiiaaiii\", runAllUnrolls(3, 2, -1));\n\n  ASSERT_EQ(\"aaai\", runAllUnrolls(0, 3, -1));\n  ASSERT_EQ(\"iaaa\", runAllUnrolls(1, 3, -1));\n  ASSERT_EQ(\"iiaaaiii\", runAllUnrolls(2, 3, -1));\n  ASSERT_EQ(\"iiiaaaii\", runAllUnrolls(3, 3, -1));\n\n  ASSERT_EQ(\"aaaa\", runAllUnrolls(0, 4, -1));\n  ASSERT_EQ(\"iaaaaiii\", runAllUnrolls(1, 4, -1));\n  ASSERT_EQ(\"iiaaaaii\", runAllUnrolls(2, 4, -1));\n  ASSERT_EQ(\"iiiaaaai\", runAllUnrolls(3, 4, -1));\n\n  ASSERT_EQ(\"aaaaaiii\", runAllUnrolls(0, 5, -1));\n  ASSERT_EQ(\"iaaaaaii\", runAllUnrolls(1, 5, -1));\n  ASSERT_EQ(\"iiaaaaai\", runAllUnrolls(2, 5, -1));\n  ASSERT_EQ(\"iiiaaaaa\", runAllUnrolls(3, 5, -1));\n}\n\nTEST(SimdForEachAligningTest, Large) {\n  ASSERT_EQ(\n      \"aaaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaii\",\n      runAllUnrolls(0, 18, -1));\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaai\",\n      runAllUnrolls(1, 18, -1));\n  ASSERT_EQ(\n      \"iiaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaaa\",\n      runAllUnrolls(2, 18, -1));\n  ASSERT_EQ(\n      \"iiia\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aaaa\"\n      \"aiii\",\n      runAllUnrolls(3, 18, -1));\n}\n\nTEST(SimdForEachAligningTest, Stops) {\n  for (int i = 0; i != 4; ++i) {\n    ASSERT_EQ(\"aaaa\", runAllUnrolls(0, 18, i));\n  }\n  for (int i = 0; i != 4; ++i) {\n    ASSERT_EQ(\n        \"aaaa\"\n        \"aaaa\",\n        runAllUnrolls(0, 18, 4 + i));\n  }\n  for (int i = 0; i != 4; ++i) {\n    ASSERT_EQ(\n        \"aaaa\"\n        \"aaaa\"\n        \"aaaa\"\n        \"aaaa\"\n        \"aaii\",\n        runAllUnrolls(0, 18, 16 + i));\n  }\n}\n\nTEST(SimdForEachAligningTest, UnrollIndexes) {\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"aaaa\",\n      run<1>(1, 11, -1));\n\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"bbbb\",\n      run<2>(1, 11, -1));\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"aaii\",\n      run<2>(1, 13, -1));\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"AAAA\"\n      \"BBBB\",\n      run<2>(1, 19, -1));\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"AAAA\"\n      \"BBBB\"\n      \"aaaa\"\n      \"aiii\",\n      run<2>(1, 24, -1));\n\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"bbbb\",\n      run<3>(1, 11, -1));\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"cccc\"\n      \"aaaa\",\n      run<3>(1, 19, -1));\n  ASSERT_EQ(\n      \"aaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"cccc\"\n      \"AAAA\"\n      \"BBBB\"\n      \"CCCC\",\n      run<3>(0, 28, -1));\n  ASSERT_EQ(\n      \"iaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"cccc\"\n      \"AAAA\"\n      \"BBBB\"\n      \"CCCC\"\n      \"aaii\",\n      run<3>(1, 29, -1));\n\n  ASSERT_EQ(\n      \"aaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"cccc\"\n      \"dddd\"\n      \"aiii\",\n      run<4>(0, 21, -1));\n  ASSERT_EQ(\n      \"aaaa\"\n      \"aaaa\"\n      \"bbbb\"\n      \"cccc\"\n      \"dddd\"\n      \"AAAA\"\n      \"BBBB\"\n      \"CCCC\"\n      \"DDDD\"\n      \"aiii\",\n      run<4>(0, 37, -1));\n}\n\n} // namespace simd::detail\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/simd/detail/test/TraitsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/detail/Traits.h>\n\n#include <folly/lang/Bits.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\n#include <set>\n#include <vector>\n\nnamespace folly::simd::detail {\n\nstruct SimdTraitsTest : testing::Test {};\n\nnamespace simd_friendly_equivalent_scalar_test {\n\n// ints\nstatic_assert( //\n    std::is_same_v< //\n        std::int8_t,\n        simd_friendly_equivalent_scalar_t<signed char>>);\nstatic_assert( //\n    std::is_same_v<\n        std::uint8_t,\n        simd_friendly_equivalent_scalar_t<unsigned char>>);\n\nstatic_assert( //\n    std::is_same_v< //\n        std::int16_t,\n        simd_friendly_equivalent_scalar_t<short>>);\nstatic_assert( //\n    std::is_same_v<\n        std::uint16_t,\n        simd_friendly_equivalent_scalar_t<unsigned short>>);\n\nstatic_assert( //\n    std::is_same_v< //\n        std::int32_t,\n        simd_friendly_equivalent_scalar_t<int>>);\nstatic_assert( //\n    std::is_same_v<\n        std::uint32_t,\n        simd_friendly_equivalent_scalar_t<unsigned int>>);\n\nstatic_assert( //\n    std::is_same_v<\n        std::int64_t,\n        simd_friendly_equivalent_scalar_t<std::int64_t>>);\nstatic_assert( //\n    std::is_same_v<\n        std::uint64_t,\n        simd_friendly_equivalent_scalar_t<std::uint64_t>>);\n\n// floats\nstatic_assert( //\n    std::is_same_v<float, simd_friendly_equivalent_scalar_t<float>>);\nstatic_assert(\n    std::is_same_v<double, simd_friendly_equivalent_scalar_t<double>>);\n\n// enum\nenum SomeInt : std::int16_t {};\nenum class SomeIntClass : std::uint32_t {};\n\nstatic_assert( //\n    std::is_same_v< //\n        std::int16_t,\n        simd_friendly_equivalent_scalar_t<SomeInt>>);\nstatic_assert( //\n    std::is_same_v<\n        std::uint32_t,\n        simd_friendly_equivalent_scalar_t<SomeIntClass>>);\n\n// const\n\nstatic_assert( //\n    std::is_same_v<\n        const std::int32_t,\n        simd_friendly_equivalent_scalar_t<const int>>);\n\n// pointers\n\nstatic_assert(\n    sizeof(std::int64_t) != sizeof(void*) ||\n    std::is_same_v<std::int64_t, simd_friendly_equivalent_scalar_t<void*>>);\n\n// sfinae\n\nstruct sfinae_call {\n  template <typename T>\n  simd_friendly_equivalent_scalar_t<T> operator()(T) const {\n    return {};\n  }\n};\n\nstatic_assert(std::is_invocable_v<sfinae_call, int>);\n\nstruct NotSimdFriendly {};\nstatic_assert(!std::is_invocable_v<sfinae_call, NotSimdFriendly>);\n\n} // namespace simd_friendly_equivalent_scalar_test\n\nnamespace as_simd_friendly_type_test {\n\ntemplate <typename T>\nusing AsSimdFriendlyResult = std::invoke_result_t<AsSimdFriendlyFn, T>;\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<std::int32_t>,\n        AsSimdFriendlyResult<folly::span<std::int32_t>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<std::int32_t>,\n        AsSimdFriendlyResult<folly::span<int>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<std::int32_t>,\n        AsSimdFriendlyResult<std::vector<int>&>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<const std::int32_t>,\n        AsSimdFriendlyResult<const std::vector<int>&>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<const double>,\n        AsSimdFriendlyResult<const std::vector<double>&>>);\n\nstatic_assert(std::is_same_v<double, AsSimdFriendlyResult<double>>);\n\nstatic_assert(!std::is_invocable_v<AsSimdFriendlyFn, std::set<int>>);\n\n} // namespace as_simd_friendly_type_test\n\nnamespace as_simd_friendly_uint_type_test {\n\ntemplate <typename T>\nusing AsSimdFriendlyUintResult = std::invoke_result_t<AsSimdFriendlyUintFn, T>;\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<std::uint32_t>,\n        AsSimdFriendlyUintResult<folly::span<std::uint32_t>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<std::uint32_t>,\n        AsSimdFriendlyUintResult<folly::span<std::int32_t>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<std::uint32_t>,\n        AsSimdFriendlyUintResult<folly::span<int>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<std::uint32_t>,\n        AsSimdFriendlyUintResult<std::vector<int>&>>);\n\nstatic_assert( //\n    std::is_same_v<\n        folly::span<const std::uint32_t>,\n        AsSimdFriendlyUintResult<const std::vector<std::uint32_t>&>>);\n\nstatic_assert(\n    std::is_same_v<std::uint32_t, AsSimdFriendlyUintResult<std::int32_t>>);\n\nstatic_assert(\n    !std::is_invocable_v<AsSimdFriendlyUintFn, const std::vector<double>&>);\n\nstatic_assert(\n    !std::is_invocable_v<AsSimdFriendlyUintFn, const std::vector<double>&>);\n\nstatic_assert(!std::is_invocable_v<AsSimdFriendlyUintFn, std::set<int>>);\n\n} // namespace as_simd_friendly_uint_type_test\n\nTEST_F(SimdTraitsTest, AsSimdFriendly) {\n  enum SomeEnum : int { Foo = 1, Bar, Baz };\n\n  static_assert(asSimdFriendly(SomeEnum::Foo) == 1);\n\n  std::array arr{SomeEnum::Foo, SomeEnum::Bar, SomeEnum::Baz};\n  folly::span<int, 3> castSpan = asSimdFriendly(folly::span(arr));\n  ASSERT_THAT(castSpan, testing::ElementsAre(1, 2, 3));\n\n  // pointer\n  {\n    auto expected = folly::bit_cast<std::intptr_t>(arr.data());\n    auto actual = asSimdFriendly(arr.data());\n    ASSERT_EQ(expected, actual);\n  }\n}\n\nTEST_F(SimdTraitsTest, AsSimdFriendlyUint) {\n  enum SomeEnum : int { Foo = 1, Bar, Baz };\n\n  static_assert(asSimdFriendlyUint(SomeEnum::Foo) == 1U);\n\n  std::array arr{SomeEnum::Foo, SomeEnum::Bar, SomeEnum::Baz};\n  folly::span<std::uint32_t, 3> castSpan = asSimdFriendlyUint(folly::span(arr));\n  ASSERT_THAT(castSpan, testing::ElementsAre(1, 2, 3));\n\n  // pointer\n  {\n    auto expected = folly::bit_cast<std::uintptr_t>(arr.data());\n    auto actual = asSimdFriendlyUint(arr.data());\n    ASSERT_EQ(expected, actual);\n  }\n}\n\n} // namespace folly::simd::detail\n"
  },
  {
    "path": "folly/algorithm/simd/detail/test/UnrollUtilsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/detail/UnrollUtils.h>\n\n#include <folly/portability/GTest.h>\n\n#include <array>\n\nnamespace folly::simd::detail {\n\nTEST(UnrollUtilsTest, ArrayMap) {\n  constexpr std::array<int, 3> in = {1, 2, 3};\n\n  struct {\n    constexpr double operator()(int x) { return x + 1; }\n  } constexpr op;\n\n  constexpr auto actual = UnrollUtils::arrayMap(in, op);\n  constexpr std::array<double, 3> expected{2.0, 3.0, 4.0};\n\n  // non constexpr std::array operator== for older standards\n  static_assert(expected[0] == actual[0]);\n  static_assert(expected[1] == actual[1]);\n  static_assert(expected[2] == actual[2]);\n}\n\ntemplate <int... values>\nconstexpr int reduceValues() {\n  std::array<int, sizeof...(values)> arr{values...};\n  return UnrollUtils::arrayReduce(arr, std::plus<>{});\n}\n\nTEST(UnrollUtilsTest, ArrayReduce) {\n  static_assert(1 == reduceValues<1>());\n  static_assert(3 == reduceValues<1, 2>());\n  static_assert(4 == reduceValues<1, 0, 3>());\n  static_assert(10 == reduceValues<1, 2, 3, 4>());\n}\n\ntemplate <std::size_t stopAt>\nstruct UnrollUntilTestOp {\n  std::size_t* lastStep;\n\n  template <std::size_t i>\n  constexpr bool operator()(folly::index_constant<i>) const {\n    *lastStep = i;\n    return i == stopAt;\n  }\n};\n\nconstexpr std::size_t kNoStop = std::numeric_limits<std::size_t>::max();\n\ntemplate <\n    std::size_t N,\n    std::size_t stopAt,\n    bool expectedRes,\n    std::size_t expectedLastStep>\nconstexpr bool unrollUntilTest() {\n  std::size_t lastStep = kNoStop;\n  UnrollUntilTestOp<stopAt> op{&lastStep};\n  bool res = UnrollUtils::unrollUntil<N>(op);\n\n  return (res == expectedRes) && (lastStep == expectedLastStep);\n}\n\nTEST(UnrollUtilsTest, UnrollUntil) {\n  static_assert(\n      unrollUntilTest<\n          /*N*/ 0,\n          /*stopAt*/ 0,\n          /*expectedRes*/ false,\n          /*ExpectedLastStep*/ kNoStop>());\n  static_assert(\n      unrollUntilTest<\n          /*N*/ 0,\n          /*stopAt*/ 1,\n          /*expectedRes*/ false,\n          /*ExpectedLastStep*/ kNoStop>());\n  static_assert(\n      unrollUntilTest<\n          /*N*/ 3,\n          /*stopAt*/ 1,\n          /*expectedRes*/ true,\n          /*ExpectedLastStep*/ 1>());\n  static_assert(\n      unrollUntilTest<\n          /*N*/ 3,\n          /*stopAt*/ 4,\n          /*expectedRes*/ false,\n          /*ExpectedLastStep*/ 2>());\n  static_assert(\n      unrollUntilTest<\n          /*N*/ 5,\n          /*stopAt*/ 3,\n          /*expectedRes*/ true,\n          /*ExpectedLastStep*/ 3>());\n}\n\n} // namespace folly::simd::detail\n"
  },
  {
    "path": "folly/algorithm/simd/find_first_of.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <cstdint>\n\n#include <folly/Portability.h>\n#include <folly/Utility.h>\n#include <folly/algorithm/simd/Movemask.h>\n#include <folly/container/SparseByteSet.h>\n#include <folly/container/span.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/Bits.h>\n#include <folly/lang/Hint.h>\n\n#if FOLLY_SSE\n#include <immintrin.h>\n#endif\n\n#if FOLLY_NEON\n#include <arm_neon.h>\n#endif\n\n#if FOLLY_ARM_FEATURE_SVE\n#include <arm_sve.h>\n#if __has_include(<arm_neon_sve_bridge.h>)\n#include <arm_neon_sve_bridge.h> // @manual\n#endif\n#endif\n\nnamespace folly::simd {\n\nnamespace detail {\n\n/// stdfind_scalar_finder_first_of\n///\n/// A find-first-of finder which simply wraps std::find.\ntemplate <typename CharT>\nclass stdfind_scalar_finder_first_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n\n  alignas(sizeof(view)) view const alphabet_;\n\n public:\n  constexpr explicit stdfind_scalar_finder_first_of(\n      view const alphabet) noexcept\n      : alphabet_{alphabet} {}\n\n  size_t operator()(view const input, size_t const pos = 0) const noexcept {\n    auto const r = std::find_first_of(\n        input.subspan(pos).begin(),\n        input.end(),\n        alphabet_.begin(),\n        alphabet_.end());\n    return r - input.begin();\n  }\n};\n\n/// default_scalar_finder_first_of\n///\n/// A find-first-of finder which, for each element of the input, iterates the\n/// search alphabet. Has complexity O(MN), with M the length of the alphabet and\n/// with N the length of the input.\n///\n/// Requires no precomputation or storage.\ntemplate <typename CharT, bool Eq>\nclass default_scalar_finder_first_op_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n\n  alignas(sizeof(view)) view alphabet_;\n\n  bool match(value_type const c) const noexcept {\n    bool ret = !Eq;\n    for (auto const a : alphabet_) {\n      auto const v = a == c;\n      ret = Eq ? ret || v : ret && !v;\n    }\n    return ret;\n  }\n\n public:\n  constexpr explicit default_scalar_finder_first_op_of(\n      view const alphabet) noexcept\n      : alphabet_{alphabet} {}\n\n  size_t operator()(view const input, size_t const pos = 0) const noexcept {\n    for (size_t i = pos; i < input.size(); ++i) {\n      if (match(input[i])) {\n        return i;\n      }\n    }\n    return input.size();\n  }\n};\n\n/// ltindex_scalar_finder_first_of\n///\n/// A find-first-of finder which, for each element of the input, looks up that\n/// element in a lookup table. Has complexity O(N), with N the length of the\n/// input.\n///\n/// Precomputes and stores a 256-byte lookup table. Precomputation has\n/// complexity O(M), with M the length of the alphabet.\n///\n/// Restricted to elements which are 1 byte wide.\ntemplate <typename CharT, bool Eq>\nclass ltindex_scalar_finder_first_op_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n  using index = std::array<bool, 256>;\n\n  static_assert(sizeof(value_type) == 1);\n\n  alignas(hardware_destructive_interference_size) index const ltindex_;\n\n  static constexpr index make_index(view const alphabet) noexcept {\n    index ltindex{};\n    for (auto const a : alphabet) {\n      ltindex[static_cast<uint8_t>(a)] = true;\n    }\n    return ltindex;\n  }\n\n  bool match(value_type const c) const noexcept {\n    return Eq == ltindex_[static_cast<uint8_t>(c)];\n  }\n\n public:\n  constexpr explicit ltindex_scalar_finder_first_op_of(\n      view const alphabet) noexcept\n      : ltindex_{make_index(alphabet)} {}\n\n  size_t operator()(view const input, size_t const pos = 0) const noexcept {\n    for (size_t i = pos; i < input.size(); ++i) {\n      if (match(input[i])) {\n        return i;\n      }\n    }\n    return input.size();\n  }\n};\n\n/// ltsparse_scalar_finder_first_of\n///\n/// A find-first-of finder which, for each element of the input, looks up that\n/// element in a lookup table. Has complexity O(M+N), with M the length of the\n/// alphabet and with N the length of the input.\n///\n/// Similar to ltindex_scalar_finder_first_of, but where the precomputation is\n/// instead done at the beginning of each search using an alternative set type.\n/// This alternative set type has lower setup cost but higher lookup cost as\n/// compared with the set type in ltindex_scalar_finder_first_of, making this\n/// implementation more suitable for unpredictable alphabets.\n///\n/// Requires no precomputation or storage.\n///\n/// Restricted to elements which are 1 byte wide.\ntemplate <typename CharT, bool Eq>\nclass ltsparse_scalar_finder_first_op_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n\n  static_assert(sizeof(value_type) == 1);\n\n  alignas(sizeof(view)) view alphabet_;\n\n  void prep(SparseByteSet& set) const noexcept {\n    for (auto const a : alphabet_) {\n      set.add(static_cast<uint8_t>(a));\n    }\n  }\n\n  bool match(value_type const c, SparseByteSet const& set) const noexcept {\n    return Eq == set.contains(static_cast<uint8_t>(c));\n  }\n\n public:\n  constexpr explicit ltsparse_scalar_finder_first_op_of(\n      view const alphabet) noexcept\n      : alphabet_{alphabet} {}\n\n  size_t operator()(view const input, size_t const pos = 0) const noexcept {\n    [[FOLLY_ATTR_CLANG_UNINITIALIZED]] SparseByteSet set;\n    prep(set);\n    for (size_t i = pos; i < input.size(); ++i) {\n      if (match(input[i], set)) {\n        return i;\n      }\n    }\n    return input.size();\n  }\n};\n\n/// default_vector_finder_first_of\n///\n/// A find-first-of finder which, for each element of the input, iterates the\n/// search alphabet. Has complexity O(MN), with M the length of the alphabet and\n/// with N the length of the input.\n///\n/// Like default_scalar_finder_first_of, but accelerated with simd instructions\n/// to search up to 16 elements of the input at a time.\n///\n/// Requires no precomputation or storage.\n///\n/// Restricted to elements which are 1 byte wide.\n///\n/// Implemented for x86-64 and aarch64 architectures.\n///\n/// Requires a fallback scalar finder for not-implemented-architecture and for\n/// near-end-of-input.\ntemplate <typename CharT, bool Eq>\nclass default_vector_finder_first_op_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n\n  static_assert(sizeof(value_type) == 1);\n\n  alignas(sizeof(view)) view const alphabet_;\n\n public:\n  constexpr explicit default_vector_finder_first_op_of(\n      view const alphabet) noexcept\n      : alphabet_{alphabet} {}\n\n  template <typename Scalar>\n  size_t operator()(\n      Scalar const& scalar,\n      view const input,\n      size_t const pos = 0) const noexcept {\n    return operator()(scalar, true, input, pos);\n  }\n\n  template <typename Scalar>\n  size_t operator()(\n      Scalar const& scalar,\n      bool const vector,\n      view const input,\n      size_t const pos = 0) const noexcept {\n    size_t size = pos;\n    if (vector) {\n#if (FOLLY_SSE >= 2 || (FOLLY_NEON && FOLLY_AARCH64))\n      while (input.size() >= size + 16) {\n#if FOLLY_SSE\n        auto const vhaystack = _mm_loadu_si128(\n            reinterpret_cast<__m128i const*>(input.data() + size));\n        auto vmask = _mm_set1_epi8(Eq ? 0 : -1);\n        for (auto const a : alphabet_) {\n          auto const veq = _mm_cmpeq_epi8(vhaystack, _mm_set1_epi8(a));\n          vmask = Eq ? _mm_or_si128(veq, vmask) : _mm_andnot_si128(veq, vmask);\n        }\n#elif FOLLY_NEON\n        auto const vhaystack =\n            vld1q_u8(reinterpret_cast<uint8_t const*>(input.data() + size));\n        auto vmask = vdupq_n_u8(Eq ? 0 : -1);\n        for (auto const a : alphabet_) {\n          auto const veq = vhaystack == vdupq_n_u8(a);\n          vmask = Eq ? veq | vmask : ~veq & vmask;\n        }\n#endif\n        if (auto const [word, bits] = movemask<CharT>(vmask); word) {\n          return size + to_signed((findFirstSet(word) - 1) / bits);\n        }\n        size += 16;\n      }\n      if (input.size() < size) {\n        compiler_may_unsafely_assume_unreachable();\n      }\n#endif\n    }\n    return scalar(input, size);\n  }\n};\n\n/// shuffle_vector_finder_first_of\n///\n/// A find-first-of finder which, for each element of the input, looks up that\n/// element in a lookup table. Has complexity O(MN), with M the length of the\n/// alphabet after deduplication and with N the length of the input.\n///\n/// Precomputes and stores a 256-byte lookup table. Precomputation has\n/// complexity O(M), with M the length of the alphabet.\n///\n/// Like ltindex_scalar_finder_first_of, but accelerated with simd instructions\n/// to search up to 16 elements of the input at a time and decelerated by\n/// splitting the lookup table into a sequence of lookup tables of length O(M).\n///\n/// Restricted to elements which are 1 byte wide.\n///\n/// Implemented for x86-64 and aarch64 architectures.\n///\n/// Requires a fallback scalar finder for not-implemented-architecture and for\n/// near-end-of-input.\ntemplate <typename CharT, bool Eq>\nclass shuffle_vector_finder_first_op_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n  using shufvec = std::array<value_type, 256>;\n\n  struct shuffle {\n    shufvec table;\n    size_t rounds;\n  };\n\n  static_assert(sizeof(value_type) == 1);\n\n  //  invariant: (a in alphabet) <=> (exists k : shufvec[k * 16 + a % 16] = a)\n  alignas(hardware_destructive_interference_size) shuffle const shuffle_;\n\n  //  mimic: std::exchange (constexpr), C++20\n  template <typename T, typename U = T>\n  static constexpr T exchange(T& obj, U&& val) noexcept {\n    auto ret = std::move(obj);\n    obj = std::forward<U>(val);\n    return ret;\n  }\n\n  static constexpr shuffle make_shuffle(view const alphabet) noexcept {\n    //  init requires: forall k, a : result[k * 16 + a % 16] != a\n    shufvec table{1}; // 1, 0, 0, ...\n    size_t maxk{};\n\n    std::array<bool, 256> seen{};\n    std::array<size_t, 16> lo_seen{};\n\n    for (auto const a : alphabet) {\n      auto const v = static_cast<uint8_t>(a);\n      if (!exchange(seen[v], true)) {\n        auto const k = lo_seen[v % 16]++;\n        maxk = maxk < k ? k : maxk;\n        table[k * 16 + v % 16] = v;\n      }\n    }\n\n    return {table, maxk + 1};\n  }\n\n public:\n  constexpr explicit shuffle_vector_finder_first_op_of(\n      view const alphabet) noexcept\n      : shuffle_{make_shuffle(alphabet)} {}\n\n  template <typename Scalar>\n  size_t operator()(\n      Scalar const& scalar,\n      view const input,\n      size_t const pos = 0) const noexcept {\n    return operator()(scalar, true, input, pos);\n  }\n\n  template <typename Scalar>\n  size_t operator()(\n      Scalar const& scalar,\n      bool const vector,\n      view const input,\n      size_t const pos = 0) const noexcept {\n    size_t size = pos;\n    if (vector) {\n#if ((FOLLY_SSE >= 2 && FOLLY_SSSE >= 3) || (FOLLY_NEON && FOLLY_AARCH64))\n      auto const table = shuffle_.table.data();\n      while (input.size() >= size + 16) {\n#if FOLLY_SSE\n        auto const vtable = reinterpret_cast<__m128i const*>(table);\n        auto const vhaystack = _mm_loadu_si128(\n            reinterpret_cast<__m128i const*>(input.data() + size));\n        auto const vhaystackm = _mm_and_si128(vhaystack, _mm_set1_epi8(15));\n        auto vmask = _mm_set1_epi8(Eq ? 0 : -1);\n        for (size_t i = 0; i < shuffle_.rounds; ++i) {\n          auto const vshuffle = _mm_shuffle_epi8(vtable[i], vhaystackm);\n          auto const veq = _mm_cmpeq_epi8(vshuffle, vhaystack);\n          vmask = Eq ? _mm_or_si128(veq, vmask) : _mm_andnot_si128(veq, vmask);\n        }\n#elif FOLLY_NEON\n        auto const vtable = reinterpret_cast<uint8x16_t const*>(table);\n        auto const vhaystack =\n            vld1q_u8(reinterpret_cast<uint8_t const*>(input.data() + size));\n        auto vmask = vdupq_n_u8(Eq ? 0 : -1);\n        for (size_t i = 0; i < shuffle_.rounds; ++i) {\n          auto const veq = vqtbl1q_u8(vtable[i], vhaystack & 15) == vhaystack;\n          vmask = Eq ? veq | vmask : ~veq & vmask;\n        }\n#endif\n        if (auto const [word, bits] = movemask<CharT>(vmask); word) {\n          return size + to_signed((findFirstSet(word) - 1) / bits);\n        }\n        size += 16;\n      }\n      if (input.size() < size) {\n        compiler_may_unsafely_assume_unreachable();\n      }\n#endif\n    }\n    return scalar(input, size);\n  }\n};\n\n/// azmatch_vector_finder_first_of\n///\n/// A find-first-of finder which, for each element of the input, looks up that\n/// element in a lookup table. Has complexity O(MN), with M the length of the\n/// alphabet after deduplication and with N the length of the input.\n///\n/// Precomputes and stores a 256-byte lookup table. Precomputation has\n/// complexity O(M), with M the length of the alphabet.\n///\n/// Like ltindex_scalar_finder_first_of, but accelerated with simd instructions\n/// to search up to 16 elements of the input at a time and decelerated by\n/// splitting the lookup table into a sequence of lookup tables of length O(M).\n///\n/// Like ltindex_vector_finder_first_of, but with a different technique.\n///\n/// Restricted to elements which are 1 byte wide.\n///\n/// Implemented for aarch64 architectures with sve.\n///\n/// Requires a fallback scalar finder for not-implemented-architecture and for\n/// near-end-of-input.\ntemplate <typename CharT, bool Eq>\nclass azmatch_vector_finder_first_op_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n  using matchvec = std::array<value_type, 256>;\n\n  struct meta {\n    matchvec table;\n    size_t rounds;\n  };\n\n  static_assert(sizeof(value_type) == 1);\n\n  alignas(hardware_destructive_interference_size) meta const meta_;\n\n  //  mimic: std::exchange (constexpr), C++20\n  template <typename T, typename U = T>\n  static constexpr T exchange(T& obj, U&& val) noexcept {\n    auto ret = std::move(obj);\n    obj = std::forward<U>(val);\n    return ret;\n  }\n\n  static constexpr size_t next_segment(\n      view& alphabet, span<value_type, 16> out, span<bool, 256> seen) noexcept {\n    if (!alphabet.size()) {\n      return 0;\n    }\n    for (size_t i = 0; i < 16; ++i) {\n      out[i] = alphabet[0];\n    }\n    size_t items = 0;\n    while (items < 16 && alphabet.size()) {\n      auto const v = static_cast<uint8_t>(alphabet[0]);\n      alphabet = alphabet.subspan(1);\n      if (!exchange(seen[v], true)) {\n        out[items++] = v;\n      }\n    }\n    return items;\n  }\n\n  static constexpr meta make_meta(view alphabet) noexcept {\n    std::array<bool, 256> seen{};\n    size_t rounds = 0;\n    matchvec vec{};\n    while (true) {\n      auto segment = span<value_type, 16>{vec.data() + 16 * rounds, 16};\n      auto segsize = next_segment(alphabet, segment, seen);\n      if (!segsize) {\n        break;\n      }\n      ++rounds;\n    }\n    return meta{vec, rounds};\n  }\n\n#if FOLLY_ARM_FEATURE_SVE\n  static auto svld1_u8_nopred_16(uint8_t const* p) noexcept {\n#if __has_include(<arm_neon_sve_bridge.h>)\n    return svset_neonq_u8(svundef_u8(), vld1q_u8(p));\n#else\n    return svld1_u8(svptrue_pat_b8(SV_VL16), p);\n#endif\n  }\n#endif\n\n public:\n  constexpr explicit azmatch_vector_finder_first_op_of(\n      view const alphabet) noexcept\n      : meta_{make_meta(alphabet)} {}\n\n  template <typename Scalar>\n  size_t operator()(\n      Scalar const& scalar,\n      view const input,\n      size_t const pos = 0) const noexcept {\n    return operator()(scalar, true, input, pos);\n  }\n\n  template <typename Scalar>\n  size_t operator()(\n      Scalar const& scalar,\n      bool const vector,\n      view const input,\n      size_t const pos = 0) const noexcept {\n    size_t size = pos;\n    if (vector) {\n#if FOLLY_ARM_FEATURE_SVE\n      auto const table = reinterpret_cast<uint8_t const*>(meta_.table.data());\n      while (input.size() >= size + 16) {\n        auto const pred = svptrue_b8();\n        auto const vhaystack = svld1_u8_nopred_16(\n            reinterpret_cast<uint8_t const*>(input.data() + size));\n        auto vmask = Eq ? svpfalse_b() : pred;\n        for (size_t i = 0; i < meta_.rounds; ++i) {\n          auto const vsegment = svld1_u8_nopred_16(table + 16 * i);\n          vmask = Eq\n              ? svorr_b_z(pred, vmask, svmatch_u8(pred, vhaystack, vsegment))\n              : svand_b_z(pred, vmask, svnmatch_u8(pred, vhaystack, vsegment));\n        }\n        // an important optimization that llvm-17 *could*, but doesn't, do for\n        // sve\n        if (meta_.rounds == 1) {\n          auto const vsegment = svld1_u8_nopred_16(table);\n          vmask = Eq\n              ? svmatch_u8(pred, vhaystack, vsegment)\n              : svnmatch_u8(pred, vhaystack, vsegment);\n        }\n        auto const count = svcntp_b8(pred, svbrkb_b_z(pred, vmask));\n        if (count < 16) {\n          return size + count;\n        }\n        size += 16;\n      }\n      if (input.size() < size) {\n        compiler_may_unsafely_assume_unreachable();\n      }\n#endif\n    }\n    return scalar(input, size);\n  }\n};\n\n} // namespace detail\n\ntemplate <typename CharT>\nusing basic_stdfind_scalar_finder_first_of =\n    detail::stdfind_scalar_finder_first_of<CharT>;\n\ntemplate <typename CharT>\nusing basic_default_scalar_finder_first_of =\n    detail::default_scalar_finder_first_op_of<CharT, true>;\ntemplate <typename CharT>\nusing basic_default_scalar_finder_first_not_of =\n    detail::default_scalar_finder_first_op_of<CharT, false>;\n\ntemplate <typename CharT>\nusing basic_ltindex_scalar_finder_first_of =\n    detail::ltindex_scalar_finder_first_op_of<CharT, true>;\ntemplate <typename CharT>\nusing basic_ltindex_scalar_finder_first_not_of =\n    detail::ltindex_scalar_finder_first_op_of<CharT, false>;\n\ntemplate <typename CharT>\nusing basic_ltsparse_scalar_finder_first_of =\n    detail::ltsparse_scalar_finder_first_op_of<CharT, true>;\ntemplate <typename CharT>\nusing basic_ltsparse_scalar_finder_first_not_of =\n    detail::ltsparse_scalar_finder_first_op_of<CharT, false>;\n\ntemplate <typename CharT>\nusing basic_default_vector_finder_first_of =\n    detail::default_vector_finder_first_op_of<CharT, true>;\ntemplate <typename CharT>\nusing basic_default_vector_finder_first_not_of =\n    detail::default_vector_finder_first_op_of<CharT, false>;\n\ntemplate <typename CharT>\nusing basic_shuffle_vector_finder_first_of =\n    detail::shuffle_vector_finder_first_op_of<CharT, true>;\ntemplate <typename CharT>\nusing basic_shuffle_vector_finder_first_not_of =\n    detail::shuffle_vector_finder_first_op_of<CharT, false>;\n\ntemplate <typename CharT>\nusing basic_azmatch_vector_finder_first_of =\n    detail::azmatch_vector_finder_first_op_of<CharT, true>;\ntemplate <typename CharT>\nusing basic_azmatch_vector_finder_first_not_of =\n    detail::azmatch_vector_finder_first_op_of<CharT, false>;\n\nusing stdfind_scalar_finder_first_of =\n    basic_stdfind_scalar_finder_first_of<char>;\n\nusing default_scalar_finder_first_of =\n    basic_default_scalar_finder_first_of<char>;\nusing default_scalar_finder_first_not_of =\n    basic_default_scalar_finder_first_not_of<char>;\n\nusing ltindex_scalar_finder_first_of =\n    basic_ltindex_scalar_finder_first_of<char>;\nusing ltindex_scalar_finder_first_not_of =\n    basic_ltindex_scalar_finder_first_not_of<char>;\n\nusing ltsparse_scalar_finder_first_of =\n    basic_ltsparse_scalar_finder_first_of<char>;\nusing ltsparse_scalar_finder_first_not_of =\n    basic_ltsparse_scalar_finder_first_not_of<char>;\n\nusing default_vector_finder_first_of =\n    basic_default_vector_finder_first_of<char>;\nusing default_vector_finder_first_not_of =\n    basic_default_vector_finder_first_not_of<char>;\n\nusing shuffle_vector_finder_first_of =\n    basic_shuffle_vector_finder_first_of<char>;\nusing shuffle_vector_finder_first_not_of =\n    basic_shuffle_vector_finder_first_not_of<char>;\n\nusing azmatch_vector_finder_first_of =\n    basic_azmatch_vector_finder_first_of<char>;\nusing azmatch_vector_finder_first_not_of =\n    basic_azmatch_vector_finder_first_not_of<char>;\n\n/// composite_finder\n///\n/// A find-first-of finder which composes a vector finder with a scalar finder.\n///\n/// A vector finder requires a scalar finder for not-implemented-architecture\n/// and for near-end-of-input. This combinator producers a finder which uses the\n/// vector finder where possible and otherwise falls back to the scalar finder.\ntemplate <typename Vector, typename Scalar>\nclass composite_finder_first_of : private Vector, Scalar {\n private:\n  using view = span<char const>;\n\n public:\n  constexpr explicit composite_finder_first_of(view const alphabet) noexcept\n      : Vector{alphabet}, Scalar{alphabet} {}\n\n  size_t operator()(view const input, size_t const pos = 0) const noexcept {\n    auto const& vector = static_cast<Vector const&>(*this);\n    auto const& scalar = static_cast<Scalar const&>(*this);\n    return vector(scalar, input, pos);\n  }\n};\n\n} // namespace folly::simd\n"
  },
  {
    "path": "folly/algorithm/simd/find_first_of_extra.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <execution>\n\n#include <folly/Range.h>\n#include <folly/container/span.h>\n\nnamespace folly::simd {\n\nnamespace detail {\n\n#if __cpp_lib_execution >= 201902L\n\n/// stdfind_vector_finder_first_of\n///\n/// A find-first-of finder which simply wraps std::find_first_of with an\n/// execution policy.\n///\n/// Like stdfind_scalar_finder_first_of, but potentially accelerated. Depends\n/// on the implementation.\n///\n/// Requires no precomputation or storage.\n///\n/// Extracted to a separate facility since this has high startup cost and to\n/// isolate the dependency on tbb which libstdc++ brings.\n///\n/// Only supports positive-match (find-first-of) and not negative-match\n/// (find-first-not-of) since std::find\ntemplate <typename CharT>\nclass stdfind_vector_finder_first_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n\n  alignas(sizeof(view)) view const alphabet_;\n\n public:\n  constexpr explicit stdfind_vector_finder_first_of(\n      view const alphabet) noexcept\n      : alphabet_{alphabet} {}\n\n  size_t operator()(view const input, size_t const pos = 0) const noexcept {\n    auto const r = std::find_first_of(\n        std::execution::unseq,\n        input.subspan(pos).begin(),\n        input.end(),\n        alphabet_.begin(),\n        alphabet_.end());\n    return r - input.begin();\n  }\n};\n\n#endif\n\n/// rngfind_vector_finder_first_of\n///\n/// A find-first-of finder which simply wraps folly::Range::find_first_of. This\n/// algorithm has its own internal acceleration.\n///\n/// Requires no precomputation or storage.\n///\n/// Implemented for x86-64 architecture.\n///\n/// Extracted to a separate facility since this has high startup cost.\n///\n/// Only supports positive-match (find-first-of) and not negative-match\n/// (find-first-not-of) since std::find\ntemplate <typename CharT>\nclass rngfind_vector_finder_first_of {\n private:\n  using value_type = CharT;\n  using view = span<CharT const>;\n\n  alignas(sizeof(view)) view const alphabet_;\n\n public:\n  constexpr explicit rngfind_vector_finder_first_of(\n      view const alphabet) noexcept\n      : alphabet_{alphabet} {}\n\n  size_t operator()(view const input, size_t const pos = 0) const noexcept {\n    auto const r = crange(input).find_first_of(crange(alphabet_), pos);\n    return r == size_t(-1) ? input.size() : r;\n  }\n};\n\n} // namespace detail\n\n#if __cpp_lib_execution >= 201902L\n\ntemplate <typename CharT>\nusing basic_stdfind_vector_finder_first_of =\n    detail::stdfind_vector_finder_first_of<CharT>;\n\nusing stdfind_vector_finder_first_of =\n    basic_stdfind_vector_finder_first_of<char>;\n\n#endif\n\ntemplate <typename CharT>\nusing basic_rngfind_vector_finder_first_of =\n    detail::rngfind_vector_finder_first_of<CharT>;\n\nusing rngfind_vector_finder_first_of =\n    basic_rngfind_vector_finder_first_of<char>;\n\n} // namespace folly::simd\n"
  },
  {
    "path": "folly/algorithm/simd/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\nload(\"../../../defs.bzl\", \"folly_xplat_cxx_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_unittest(\n    name = \"contains_test\",\n    srcs = [\"ContainsTest.cpp\"],\n    headers = [],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/algorithm/simd:contains\",\n        \"//folly/algorithm/simd/detail:simd_contains_impl\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"find_first_of_test\",\n    srcs = [\"find_first_of_test.cpp\"],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly/algorithm/simd:find_first_of\",\n        \"//xplat/folly/algorithm/simd:find_first_of_extra\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"find_fixed_test\",\n    srcs = [\"FindFixedTest.cpp\"],\n    headers = [],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:portability\",\n        \"//folly/algorithm/simd:find_fixed\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"momemask_test\",\n    srcs = [\"MovemaskTest.cpp\"],\n    headers = [],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly:portability\",\n        \"//folly/algorithm/simd:movemask\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"find_first_of_bench\",\n    srcs = [\"find_first_of_bench.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/algorithm/simd:find_first_of\",\n        \"//folly/algorithm/simd:find_first_of_extra\",\n        \"//folly/init:init\",\n        \"//folly/lang:keep\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"find_first_of_test\",\n    srcs = [\"find_first_of_test.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:string\",\n        \"//folly/algorithm/simd:find_first_of\",\n        \"//folly/algorithm/simd:find_first_of_extra\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"find_fixed_bench\",\n    srcs = [\"FindFixedBenchmark.cpp\"],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:benchmark\",\n        \"//folly/algorithm/simd:find_fixed\",\n        \"//folly/init:init\",\n    ],\n)\n"
  },
  {
    "path": "folly/algorithm/simd/test/ContainsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/Contains.h>\n\n#include <folly/algorithm/simd/detail/ContainsImpl.h>\n\n#include <folly/portability/GTest.h>\n#include <folly/test/TestUtils.h>\n\n#include <list>\n#include <vector>\n\nstatic_assert( //\n    !std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<double>&,\n        double>);\n\nstatic_assert( //\n    std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<int>&,\n        int>);\n\nstatic_assert( //\n    std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<int>&,\n        int>);\n\nstatic_assert( //\n    std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<int>&,\n        std::int16_t>);\n\nstatic_assert( //\n    std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<int>&,\n        std::uint16_t>);\n\nstatic_assert( //\n    !std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<int>&,\n        std::uint32_t>);\n\nstatic_assert( //\n    !std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<int>&,\n        std::int64_t>);\n\nstatic_assert( //\n    !std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<std::uint32_t>&,\n        std::int16_t>);\n\nstatic_assert( //\n    std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::vector<std::uint32_t>&,\n        std::uint16_t>);\n\nstatic_assert( //\n    !std::is_invocable_v< //\n        folly::simd::contains_fn,\n        std::list<std::int32_t>&,\n        std::int32_t>);\n\nstatic_assert( //\n    !std::is_invocable_v< //\n        folly::simd::contains_fn,\n        const std::vector<std::vector<std::int32_t>>&,\n        std::vector<std::int32_t>>);\n\ntemplate <typename T>\nstruct ContainsTest : ::testing::Test {};\n\nstruct ContainsTestSpeicalCases : ::testing::Test {};\n\nusing TypesToTest = ::testing::Types<\n    std::int8_t,\n    std::int16_t,\n    std::int32_t,\n    std::int64_t,\n    std::uint8_t,\n    std::uint16_t,\n    std::uint32_t,\n    std::uint64_t>;\n\nTYPED_TEST_SUITE(ContainsTest, TypesToTest);\n\nnamespace folly::simd {\nusing detail::containsImplHandwritten;\nusing detail::containsImplStd;\nusing detail::hasHandwrittenContains;\n} // namespace folly::simd\n\ntemplate <typename T>\nvoid testSimdContainsVerify(folly::span<T> haystack, T needle, bool expected) {\n  bool actual1 = folly::simd::contains(haystack, needle);\n  ASSERT_EQ(expected, actual1);\n\n  auto const_haystack = folly::static_span_cast<const T>(haystack);\n\n  if constexpr (\n      std::is_same_v<T, std::uint8_t> || std::is_same_v<T, std::uint16_t> ||\n      std::is_same_v<T, std::uint32_t> || std::is_same_v<T, std::uint64_t>) {\n    bool actual2 = folly::simd::containsImplStd(const_haystack, needle);\n    ASSERT_EQ(expected, actual2) << \" haystack.size(): \" << haystack.size();\n  }\n\n  if constexpr (folly::simd::detail::hasHandwrittenContains<T>()) {\n    bool actual3 = folly::simd::containsImplHandwritten(const_haystack, needle);\n    ASSERT_EQ(expected, actual3) << \" haystack.size(): \" << haystack.size();\n  }\n}\n\nTYPED_TEST(ContainsTest, Basic) {\n  using T = TypeParam;\n\n  for (std::size_t size = 0; size != 100; ++size) {\n    std::vector<T> buf(size, T{0});\n    auto const bound = std::min(std::size_t(32), size);\n    for (std::size_t offset = 0; offset != bound; ++offset) {\n      folly::span<T> haystack(buf.data() + offset, buf.data() + buf.size());\n      T needle{1};\n      testSimdContainsVerify(haystack, needle, /*expected*/ false);\n\n      for (auto& x : haystack) {\n        x = needle;\n        testSimdContainsVerify(haystack, needle, /*expected*/ true);\n        x = 0;\n      }\n    }\n  }\n}\n\nTEST_F(ContainsTestSpeicalCases, Pointers) {\n  std::array ints = {0, 1, 2, 3};\n  std::array ptrs = {&ints[0], &ints[1], &ints[3]};\n  EXPECT_TRUE(folly::simd::contains(ptrs, &ints[1]));\n  EXPECT_FALSE(folly::simd::contains(ptrs, &ints[2]));\n}\n\nTEST_F(ContainsTestSpeicalCases, AsanShouldDetectInvalidRange) {\n  SKIP_IF(!folly::kIsSanitizeAddress);\n\n  std::vector<int> v;\n  v.resize(3);\n  folly::span<int> s(v.data() + 1, v.data() + 4);\n  EXPECT_DEATH(\n      (folly::simd::contains(s, 0)), \"AddressSanitizer: heap-buffer-overflow\");\n}\n"
  },
  {
    "path": "folly/algorithm/simd/test/FindFixedBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/FindFixed.h>\n\n#include <folly/Benchmark.h>\n#include <folly/init/Init.h>\n\n#include <fmt/core.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <numeric>\n#include <span>\n#include <vector>\n\nnamespace folly {\nnamespace {\n\ntemplate <typename T, std::size_t N>\nconst auto kInput = [] {\n  std::vector<T> res(N, T{0});\n  std::iota(res.begin(), res.end(), T{0});\n  return res;\n}();\n\ntemplate <std::size_t N>\nusing IndexConstant = std::integral_constant<std::size_t, N>;\n\ntemplate <typename T, std::size_t N>\nvoid stdFindBenchmark(unsigned n) {\n  const auto& in = kInput<T, N>;\n\n  while (n--) {\n    for (auto x : in) {\n      folly::doNotOptimizeAway(std::ranges::find(in, x));\n    }\n  }\n}\n\ntemplate <typename T, std::size_t N>\nvoid follyFindFixedBenchmark(unsigned n) {\n  const auto& in = kInput<T, N>;\n\n  while (n--) {\n    for (auto x : in) {\n      std::span<const T, N> s(in.data(), N);\n      folly::doNotOptimizeAway(folly::findFixed(s, x));\n    }\n  }\n}\n\ntemplate <typename T, std::size_t N>\nvoid registerBenchmark(T, IndexConstant<N>) {\n  (void)kInput<T, N>;\n\n  folly::addBenchmark(\n      __FILE__,\n      fmt::format(\n          \"total size:{}, sizeof(T):{} std::find\", N * sizeof(T), sizeof(T)),\n      [](unsigned n) -> unsigned {\n        stdFindBenchmark<T, N>(n);\n        return n;\n      });\n  folly::addBenchmark(\n      __FILE__,\n      fmt::format(\n          \"total size:{}, sizeof(T):{} folly::findFixed\",\n          N * sizeof(T),\n          sizeof(T)),\n      [](unsigned n) -> unsigned {\n        follyFindFixedBenchmark<T, N>(n);\n        return n;\n      });\n}\n\nvoid drawLine() {\n  folly::addBenchmark(__FILE__, \"-\", []() -> unsigned { return 0; });\n}\n\nvoid registerAllBenchmarks() {\n  // 8 bytes\n  registerBenchmark(std::int8_t{}, IndexConstant<8>{});\n  registerBenchmark(std::int16_t{}, IndexConstant<4>{});\n  registerBenchmark(std::int32_t{}, IndexConstant<2>{});\n  drawLine();\n  // 16 bytes\n  registerBenchmark(std::int8_t{}, IndexConstant<16>{});\n  registerBenchmark(std::int16_t{}, IndexConstant<8>{});\n  registerBenchmark(std::int32_t{}, IndexConstant<4>{});\n  registerBenchmark(std::int64_t{}, IndexConstant<2>{});\n  drawLine();\n  // 32 bytes\n  registerBenchmark(std::int8_t{}, IndexConstant<32>{});\n  registerBenchmark(std::int16_t{}, IndexConstant<16>{});\n  registerBenchmark(std::int32_t{}, IndexConstant<8>{});\n  registerBenchmark(std::int64_t{}, IndexConstant<4>{});\n  drawLine();\n  // 40 bytes\n  registerBenchmark(std::int8_t{}, IndexConstant<40>{});\n  registerBenchmark(std::int16_t{}, IndexConstant<20>{});\n  registerBenchmark(std::int32_t{}, IndexConstant<10>{});\n  registerBenchmark(std::int64_t{}, IndexConstant<5>{});\n}\n\n} // namespace\n} // namespace folly\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv, true);\n  folly::registerAllBenchmarks();\n  folly::runBenchmarks();\n}\n"
  },
  {
    "path": "folly/algorithm/simd/test/FindFixedTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#ifdef __cpp_lib_concepts // these tests need C++ concepts\n\n#include <folly/algorithm/simd/FindFixed.h>\n\n#include <fmt/core.h>\n#include <fmt/ranges.h>\n\n#include <folly/portability/GTest.h>\n\n#include <cstddef>\n#include <cstdint>\n#include <span>\n#include <vector>\n\nnamespace {\n\ntemplate <typename T, std::size_t N>\nvoid allTestsForN(std::span<T, N> buf) {\n  auto errorMsg = [&] {\n    return fmt::format(\"looking in: {}, sizeof(T): {}\", buf, sizeof(T));\n  };\n\n  T foundX = static_cast<T>(0);\n  T notFoundX = static_cast<T>(1);\n\n  std::fill_n(buf.begin(), N, foundX);\n  std::span<const T, N> cbuf = buf;\n  ASSERT_EQ(std::nullopt, folly::findFixed(cbuf, notFoundX)) << errorMsg();\n  if (N == 0) {\n    return;\n  }\n\n  ASSERT_EQ(0, folly::findFixed(cbuf, foundX)) << errorMsg();\n  for (std::size_t found = 1; found < N; ++found) {\n    buf[found - 1] = notFoundX;\n    ASSERT_EQ(found, folly::findFixed(cbuf, foundX)) << errorMsg();\n  }\n}\n\ntemplate <typename T, std::size_t... idx>\nvoid allTestsForImpl(std::span<T> buf, std::index_sequence<idx...>) {\n  (allTestsForN(std::span<T, idx>(buf.data(), idx)), ...);\n}\n\ntemplate <typename T>\nvoid allTestsFor() {\n  constexpr std::size_t kMaxSize = 64 / sizeof(T);\n  std::vector<T> buf;\n  buf.resize(2 * kMaxSize, static_cast<T>(0));\n\n  // simd code can depend on alignment, so we better test it\n  for (std::size_t offset = 0; offset != kMaxSize; ++offset) {\n    ASSERT_NO_FATAL_FAILURE(allTestsForImpl(\n        std::span(buf.data() + offset, kMaxSize),\n        std::make_index_sequence<kMaxSize + 1>{}))\n        << offset;\n  }\n}\n\n} // namespace\n\nTEST(FindFixed, Basic) {\n  // Int is an important case, it should work\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<int>());\n\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::int8_t>());\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::int16_t>());\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::int32_t>());\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::int64_t>());\n\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::uint8_t>());\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::uint16_t>());\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::uint32_t>());\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::uint64_t>());\n\n  // just a enum test\n  ASSERT_NO_FATAL_FAILURE(allTestsFor<std::byte>());\n}\n\nTEST(FindFixed, Interfaces) {\n  // constexpr\n  {\n    constexpr std::array<std::int64_t, 3> arr{1, 2, 3};\n    static_assert(folly::findFixed(std::span(std::as_const(arr)), 1) == 0);\n    static_assert(folly::findFixed(std::span(std::as_const(arr)), 3) == 2);\n    static_assert(\n        folly::findFixed(std::span(std::as_const(arr)), 4) == std::nullopt);\n  }\n\n  // array\n  {\n    std::array<int, 3> arr{1, 2, 3};\n    ASSERT_EQ(std::nullopt, folly::findFixed(std::span(std::as_const(arr)), 0));\n    ASSERT_EQ(1, folly::findFixed(std::span(std::as_const(arr)), 2));\n  }\n\n  // mutable span\n  {\n    std::array<int, 3> arr{1, 2, 3};\n    std::span<int, 3> s(arr);\n    ASSERT_EQ(std::nullopt, folly::findFixed(std::span<const int, 3>(s), 0));\n    ASSERT_EQ(1, folly::findFixed(std::span<const int, 3>(s), 2));\n  }\n}\n\ntemplate <typename T>\nconcept findFixedWorksFor = requires(const T& x) {\n  { folly::findFixed(x) };\n};\n\nTEST(FollyFindFixed, SfianeFriendlyUnsupportedTypes) {\n  EXPECT_FALSE(findFixedWorksFor<std::span<const int>>)\n      << \"dynamic extend is not supported\";\n  EXPECT_FALSE(findFixedWorksFor<std::vector<int>>)\n      << \"vector is dynamic size by definition\";\n  EXPECT_FALSE((findFixedWorksFor<std::span<const std::vector<int>, 3>>))\n      << \"find fixed works only for some trivial types.\";\n}\n\n#endif\n"
  },
  {
    "path": "folly/algorithm/simd/test/MovemaskTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/Movemask.h>\n\n#include <folly/Portability.h>\n#include <folly/portability/GTest.h>\n\n#include <array>\n#include <cstdint>\n#include <cstring>\n\n#if FOLLY_X64\n#include <immintrin.h>\n#endif\n\n#if FOLLY_AARCH64\n#include <arm_neon.h>\n#endif\n\nnamespace folly {\n\nnamespace {\n\ntemplate <typename Reg, typename T, std::size_t N>\nReg loadReg(const std::array<T, N>& arr) {\n  Reg res;\n  std::memcpy(&res, arr.data(), sizeof(T) * N);\n  return res;\n}\n\nstd::uint64_t safeShift(std::uint64_t what, std::uint32_t shift) {\n  if (!shift) {\n    return what;\n  }\n  what <<= shift - 1;\n  what <<= 1;\n  return what;\n}\n\ntemplate <typename Reg, typename T, std::size_t N>\nvoid allOneTrueTests() {\n  constexpr auto kTrue = static_cast<T>(-1);\n  constexpr auto kFalse = static_cast<T>(0);\n\n  std::array<T, N> arr;\n  arr.fill(kFalse);\n\n  ASSERT_EQ(0, simd::movemask<T>(loadReg<Reg>(arr)).first);\n  ASSERT_EQ(0, simd::movemask<T>(loadReg<Reg>(arr), simd::ignore_none{}).first);\n\n  ASSERT_EQ(\n      0,\n      simd::movemask<T>(loadReg<Reg>(arr), simd::ignore_extrema{1, 0}).first);\n\n  for (int i = 0; i != N; ++i) {\n    arr[i] = kTrue;\n    auto [bits, bitsPerElement] = simd::movemask<T>(loadReg<Reg>(arr));\n    std::uint64_t oneElement = safeShift(1, bitsPerElement()) - 1;\n    std::uint64_t expectedBits = safeShift(oneElement, i * bitsPerElement());\n\n    ASSERT_EQ(expectedBits, bits) << \"sizeof(T): \" << sizeof(T) << \" i: \" << i;\n\n    ASSERT_EQ(\n        0,\n        simd::movemask<T>(loadReg<Reg>(arr), simd::ignore_extrema{i + 1, 0})\n            .first)\n        << \"sizeof(T): \" << sizeof(T) << \" i: \" << i;\n    ASSERT_EQ(\n        expectedBits,\n        simd::movemask<T>(loadReg<Reg>(arr), simd::ignore_extrema{i, 0}).first)\n        << \"sizeof(T): \" << sizeof(T) << \" i: \" << i;\n\n    ASSERT_EQ(\n        0,\n        simd::movemask<T>(\n            loadReg<Reg>(arr), simd::ignore_extrema{0, static_cast<int>(N) - i})\n            .first)\n        << \"sizeof(T): \" << sizeof(T) << \" i: \" << i;\n    ASSERT_EQ(\n        expectedBits,\n        simd::movemask<T>(\n            loadReg<Reg>(arr),\n            simd::ignore_extrema{0, static_cast<int>(N) - i - 1})\n            .first)\n        << \"sizeof(T): \" << sizeof(T) << \" i: \" << i;\n\n    arr[i] = kFalse;\n  }\n}\n\n} // namespace\n\n#if FOLLY_X64\n\nTEST(Movemask, Sse2Example) {\n  /*\n   * This test tries to be documentation\n   */\n\n  // Assume we want to find a 3 in the array\n  std::array<std::uint16_t, 8> input{0, 3, 0, 0, 0, 3, 0, 0};\n\n  // The answers are 1 and 6.\n\n  // Step 1:\n  // Load the register and compare it against\n  // 3.\n\n  const __m128i reg =\n      _mm_loadu_si128(reinterpret_cast<const __m128i*>(input.data()));\n  const __m128i val3 = _mm_set1_epi16(3);\n  const __m128i test = _mm_cmpeq_epi16(reg, val3);\n\n  // Step 2:\n  // Now we need to go from simd register test to a bitmask.\n  // This is where movemask comes in.\n\n  auto [bits, bitsPerElement] = folly::simd::movemask<std::uint16_t>(test);\n\n  // It so happens that on x86, it's better to use 2 bits\n  // per element for a bitmask, so bits_per element will be 2.\n  static_assert(2 == decltype(bitsPerElement){});\n\n  // This is how the bitmask would look.\n  // 2 bits per element.\n  // 2nd and 6th elements are true (note the little endianness)\n  ASSERT_EQ(0b00'00'11'00'00'00'11'00, bits);\n\n  // Now we could do `countr_zero` to get the positions.\n\n  // Ignore ===============\n  // What if some of the matches were not relevant to us?\n  // This is a common thing during tail handling.\n  // This is where ignore comes in.\n\n  simd::ignore_extrema ignore{};\n\n  // Example: first 2 elements are irrelevant\n  ignore = {};\n  ignore.first = 2;\n  bits = folly::simd::movemask<std::uint16_t>(test, ignore).first;\n  ASSERT_EQ(0b00'00'11'00'00'00'00'00, bits);\n\n  // Example: last 3 elements are irrelevant\n  ignore = {};\n  ignore.last = 3;\n  bits = folly::simd::movemask<std::uint16_t>(test, ignore).first;\n  ASSERT_EQ(0b00'00'00'00'00'00'11'00, bits);\n}\n\nTEST(Movemask, Sse2) {\n  allOneTrueTests<__m128i, std::uint8_t, 16>();\n  allOneTrueTests<__m128i, std::uint16_t, 8>();\n  allOneTrueTests<__m128i, std::uint32_t, 4>();\n  allOneTrueTests<__m128i, std::uint64_t, 2>();\n}\n\n#if defined(__AVX2__)\n\nTEST(Movemask, Avx2) {\n  allOneTrueTests<__m256i, std::uint8_t, 32>();\n  allOneTrueTests<__m256i, std::uint16_t, 16>();\n  allOneTrueTests<__m256i, std::uint32_t, 8>();\n  allOneTrueTests<__m256i, std::uint64_t, 4>();\n}\n\n#endif\n\n#endif\n\n#if FOLLY_AARCH64\n\nTEST(Movemask, AARCH64) {\n  allOneTrueTests<uint8x8_t, std::uint8_t, 8>();\n  allOneTrueTests<uint16x4_t, std::uint16_t, 4>();\n  allOneTrueTests<uint32x2_t, std::uint32_t, 2>();\n  allOneTrueTests<uint64x1_t, std::uint64_t, 1>();\n\n  allOneTrueTests<uint8x16_t, std::uint8_t, 16>();\n  allOneTrueTests<uint16x8_t, std::uint16_t, 8>();\n  allOneTrueTests<uint32x4_t, std::uint32_t, 4>();\n  allOneTrueTests<uint64x2_t, std::uint64_t, 2>();\n}\n\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/algorithm/simd/test/find_first_of_bench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/find_first_of.h>\n#include <folly/algorithm/simd/find_first_of_extra.h>\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <random>\n#include <string>\n#include <string_view>\n\n#include <folly/Benchmark.h>\n#include <folly/init/Init.h>\n#include <folly/lang/Keep.h>\n\nusing namespace std::literals;\n\nstatic constexpr auto json_str_bound = \"\\\\\\\"\"sv;\nstatic constexpr auto json_ws_whitespace = \" \\r\\n\\t\"sv;\n\n//  generic find_first_of\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_stdfind_scalar_finder_find_first_of(\n    folly::simd::stdfind_scalar_finder_first_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_scalar_finder_find_first_of(\n    folly::simd::default_scalar_finder_first_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltindex_scalar_finder_find_first_of(\n    folly::simd::ltindex_scalar_finder_first_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltsparse_scalar_finder_find_first_of(\n    folly::simd::ltsparse_scalar_finder_first_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n\n#if __cpp_lib_execution >= 201902L\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_stdfind_vector_finder_find_first_of(\n    folly::simd::stdfind_vector_finder_first_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n#endif\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_vector_stdfind_scalar_finder_find_first_of(\n    folly::simd::default_vector_finder_first_of const& vector,\n    folly::simd::stdfind_scalar_finder_first_of const& scalar,\n    std::string_view const haystack) {\n  return vector(scalar, haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_shuffle_vector_stdfind_scalar_finder_find_first_of(\n    folly::simd::shuffle_vector_finder_first_of const& vector,\n    folly::simd::stdfind_scalar_finder_first_of const& scalar,\n    std::string_view const haystack) {\n  return vector(scalar, haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_azmatch_vector_stdfind_scalar_finder_find_first_of(\n    folly::simd::azmatch_vector_finder_first_of const& vector,\n    folly::simd::stdfind_scalar_finder_first_of const& scalar,\n    std::string_view const haystack) {\n  return vector(scalar, haystack);\n}\n\n//  generic find_first_not_of\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_scalar_finder_find_first_not_of(\n    folly::simd::default_scalar_finder_first_not_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltindex_scalar_finder_find_first_not_of(\n    folly::simd::ltindex_scalar_finder_first_not_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltsparse_scalar_finder_find_first_not_of(\n    folly::simd::ltsparse_scalar_finder_first_not_of const& needle,\n    std::string_view const haystack) {\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_vector_default_scalar_finder_find_first_not_of(\n    folly::simd::default_vector_finder_first_not_of const& vector,\n    folly::simd::default_scalar_finder_first_not_of const& scalar,\n    std::string_view const haystack) {\n  return vector(scalar, haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_shuffle_vector_default_scalar_finder_find_first_not_of(\n    folly::simd::shuffle_vector_finder_first_not_of const& vector,\n    folly::simd::default_scalar_finder_first_not_of const& scalar,\n    std::string_view const haystack) {\n  return vector(scalar, haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_azmatch_vector_default_scalar_finder_find_first_not_of(\n    folly::simd::azmatch_vector_finder_first_not_of const& vector,\n    folly::simd::default_scalar_finder_first_not_of const& scalar,\n    std::string_view const haystack) {\n  return vector(scalar, haystack);\n}\n\n//  specialized find_first_of\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_stdfind_scalar_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using scalar = folly::simd::stdfind_scalar_finder_first_of;\n  static constexpr auto needle = scalar(json_str_bound);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_scalar_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using scalar = folly::simd::default_scalar_finder_first_of;\n  static constexpr auto needle = scalar(json_str_bound);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltindex_scalar_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using scalar = folly::simd::ltindex_scalar_finder_first_of;\n  static constexpr auto needle = scalar(json_str_bound);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltsparse_scalar_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using scalar = folly::simd::ltsparse_scalar_finder_first_of;\n  static constexpr auto needle = scalar(json_str_bound);\n  return needle(haystack);\n}\n\n#if __cpp_lib_execution >= 201902L\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_stdfind_vector_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using vector = folly::simd::stdfind_vector_finder_first_of;\n  static constexpr auto needle = vector(json_str_bound);\n  return needle(haystack);\n}\n#endif\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_vector_stdfind_scalar_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using scalar = folly::simd::stdfind_scalar_finder_first_of;\n  using vector = folly::simd::default_vector_finder_first_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  static constexpr auto needle = finder(json_str_bound);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_shuffle_vector_stdfind_scalar_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using scalar = folly::simd::stdfind_scalar_finder_first_of;\n  using vector = folly::simd::shuffle_vector_finder_first_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  static constexpr auto needle = finder(json_str_bound);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_azmatch_vector_stdfind_scalar_finder_find_first_of_json_str(\n    std::string_view const haystack) {\n  using scalar = folly::simd::stdfind_scalar_finder_first_of;\n  using vector = folly::simd::azmatch_vector_finder_first_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  static constexpr auto needle = finder(json_str_bound);\n  return needle(haystack);\n}\n\n//  specialized find_first_not_of\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_scalar_finder_find_first_not_of_json_ws(\n    std::string_view const haystack) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  static constexpr auto needle = scalar(json_ws_whitespace);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltindex_scalar_finder_find_first_not_of_json_ws(\n    std::string_view const haystack) {\n  using scalar = folly::simd::ltindex_scalar_finder_first_not_of;\n  static constexpr auto needle = scalar(json_ws_whitespace);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_ltsparse_scalar_finder_find_first_not_of_json_ws(\n    std::string_view const haystack) {\n  using scalar = folly::simd::ltsparse_scalar_finder_first_not_of;\n  static constexpr auto needle = scalar(json_ws_whitespace);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_default_vector_default_scalar_finder_find_first_not_of_json_ws(\n    std::string_view const haystack) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  using vector = folly::simd::default_vector_finder_first_not_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  static constexpr auto needle = finder(json_ws_whitespace);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_shuffle_vector_default_scalar_finder_find_first_not_of_json_ws(\n    std::string_view const haystack) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  using vector = folly::simd::shuffle_vector_finder_first_not_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  static constexpr auto needle = finder(json_ws_whitespace);\n  return needle(haystack);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_simd_azmatch_vector_default_scalar_finder_find_first_not_of_json_ws(\n    std::string_view const haystack) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  using vector = folly::simd::azmatch_vector_finder_first_not_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  static constexpr auto needle = finder(json_ws_whitespace);\n  return needle(haystack);\n}\n\nstatic constexpr auto json_str_text = \"abcdefghijklmopqrstuvwxyz\"sv;\n\nstatic std::mt19937 rng;\n\nstatic std::string make_string(std::string_view alphabet, size_t run) {\n  std::uniform_int_distribution<size_t> dist{0, alphabet.size() - 1};\n  std::string ret;\n  while (run--) {\n    ret.push_back(alphabet[dist(rng)]);\n  }\n  return ret;\n}\n\ntemplate <typename Finder>\nFOLLY_NOINLINE static void do_find_first_op_of(\n    size_t iters, Finder const& needle, std::string_view haystack) {\n  size_t n = 0;\n  while (iters--) {\n    auto r = needle(haystack);\n    folly::compiler_must_not_predict(r);\n    n += r;\n  }\n  folly::compiler_must_not_elide(n);\n}\n\ntemplate <typename Finder>\nstatic void find_first_of_json_str(size_t iters, size_t run) {\n  static constexpr Finder needle{json_str_bound};\n  folly::BenchmarkSuspender bs;\n  auto const s = \"\"s //\n      + make_string(json_str_text, run) //\n      + make_string(json_str_bound, 1) //\n      + make_string(json_str_text, 47) //\n      ;\n  bs.dismissing([&] { //\n    do_find_first_op_of(iters, needle, s);\n  });\n}\n\ntemplate <typename Finder>\nstatic void find_first_not_of_json_ws(size_t iters, size_t run) {\n  static constexpr Finder needle{json_ws_whitespace};\n  folly::BenchmarkSuspender bs;\n  auto const s = \"\"s //\n      + make_string(json_ws_whitespace, run) //\n      + make_string(json_str_text, 48) //\n      ;\n  bs.dismissing([&] { //\n    do_find_first_op_of(iters, needle, s);\n  });\n}\n\nvoid find_first_of_json_str_stdfind_scalar(size_t iters, size_t run) {\n  using scalar = folly::simd::stdfind_scalar_finder_first_of;\n  find_first_of_json_str<scalar>(iters, run);\n}\n\nvoid find_first_of_json_str_default_scalar(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_of;\n  find_first_of_json_str<scalar>(iters, run);\n}\n\nvoid find_first_of_json_str_ltindex_scalar(size_t iters, size_t run) {\n  using scalar = folly::simd::ltindex_scalar_finder_first_of;\n  find_first_of_json_str<scalar>(iters, run);\n}\n\nvoid find_first_of_json_str_ltsparse_scalar(size_t iters, size_t run) {\n  using scalar = folly::simd::ltindex_scalar_finder_first_of;\n  find_first_of_json_str<scalar>(iters, run);\n}\n\n#if __cpp_lib_execution >= 201902L\nvoid find_first_of_json_str_stdfind_vector(size_t iters, size_t run) {\n  using vector = folly::simd::stdfind_vector_finder_first_of;\n  find_first_of_json_str<vector>(iters, run);\n}\n#endif\n\nvoid find_first_of_json_str_rngfind_vector(size_t iters, size_t run) {\n  using vector = folly::simd::rngfind_vector_finder_first_of;\n  find_first_of_json_str<vector>(iters, run);\n}\n\nvoid find_first_of_json_str_default_vector(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_of;\n  using vector = folly::simd::default_vector_finder_first_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  find_first_of_json_str<finder>(iters, run);\n}\n\nvoid find_first_of_json_str_shuffle_vector(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_of;\n  using vector = folly::simd::shuffle_vector_finder_first_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  find_first_of_json_str<finder>(iters, run);\n}\n\nvoid find_first_of_json_str_azmatch_vector(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_of;\n  using vector = folly::simd::azmatch_vector_finder_first_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  find_first_of_json_str<finder>(iters, run);\n}\n\nvoid find_first_not_of_json_ws_default_scalar(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  find_first_not_of_json_ws<scalar>(iters, run);\n}\n\nvoid find_first_not_of_json_ws_ltindex_scalar(size_t iters, size_t run) {\n  using scalar = folly::simd::ltindex_scalar_finder_first_not_of;\n  find_first_not_of_json_ws<scalar>(iters, run);\n}\n\nvoid find_first_not_of_json_ws_ltsparse_scalar(size_t iters, size_t run) {\n  using scalar = folly::simd::ltsparse_scalar_finder_first_not_of;\n  find_first_not_of_json_ws<scalar>(iters, run);\n}\n\nvoid find_first_not_of_json_ws_default_vector(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  using vector = folly::simd::default_vector_finder_first_not_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  find_first_not_of_json_ws<finder>(iters, run);\n}\n\nvoid find_first_not_of_json_ws_shuffle_vector(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  using vector = folly::simd::shuffle_vector_finder_first_not_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  find_first_not_of_json_ws<finder>(iters, run);\n}\n\nvoid find_first_not_of_json_ws_azmatch_vector(size_t iters, size_t run) {\n  using scalar = folly::simd::default_scalar_finder_first_not_of;\n  using vector = folly::simd::azmatch_vector_finder_first_not_of;\n  using finder = folly::simd::composite_finder_first_of<vector, scalar>;\n  find_first_not_of_json_ws<finder>(iters, run);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_scalar, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_default_scalar, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_ltindex_scalar, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_ltsparse_scalar, 0x13)\n\n#if __cpp_lib_execution >= 201902L\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_stdfind_vector, 0x13)\n\n#endif\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_rngfind_vector, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_default_vector, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_shuffle_vector, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x00)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x01)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x02)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x03)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x04)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x05)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x06)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x07)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x08)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x09)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x0a)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x0b)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x0c)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x0d)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x0e)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x0f)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x10)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x11)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x12)\nBENCHMARK_PARAM(find_first_of_json_str_azmatch_vector, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x00)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x01)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x02)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x03)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x04)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x05)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x06)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x07)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x08)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x09)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x0a)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x0b)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x0c)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x0d)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x0e)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x0f)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x10)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x11)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x12)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_scalar, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x00)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x01)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x02)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x03)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x04)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x05)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x06)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x07)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x08)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x09)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x0a)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x0b)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x0c)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x0d)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x0e)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x0f)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x10)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x11)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x12)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltindex_scalar, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x00)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x01)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x02)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x03)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x04)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x05)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x06)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x07)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x08)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x09)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x0a)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x0b)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x0c)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x0d)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x0e)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x0f)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x10)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x11)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x12)\nBENCHMARK_PARAM(find_first_not_of_json_ws_ltsparse_scalar, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x00)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x01)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x02)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x03)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x04)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x05)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x06)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x07)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x08)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x09)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x0a)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x0b)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x0c)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x0d)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x0e)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x0f)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x10)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x11)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x12)\nBENCHMARK_PARAM(find_first_not_of_json_ws_default_vector, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x00)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x01)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x02)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x03)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x04)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x05)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x06)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x07)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x08)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x0b)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x0c)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x0d)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x0e)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x0f)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x10)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x11)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x12)\nBENCHMARK_PARAM(find_first_not_of_json_ws_shuffle_vector, 0x13)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x00)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x01)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x02)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x03)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x04)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x05)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x06)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x07)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x08)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x0b)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x0c)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x0d)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x0e)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x0f)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x10)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x11)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x12)\nBENCHMARK_PARAM(find_first_not_of_json_ws_azmatch_vector, 0x13)\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/algorithm/simd/test/find_first_of_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/simd/find_first_of.h>\n#include <folly/algorithm/simd/find_first_of_extra.h>\n\n#include <algorithm>\n#include <random>\n#include <string_view>\n\n#include <folly/String.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals;\n\nstatic auto quote(std::string_view sv) {\n  return \"\\\"\" + folly::cEscape<std::string>(sv) + \"\\\"\";\n}\n\ntemplate <typename Param>\nstruct FindFirstOpOfTest : testing::TestWithParam<Param> {\n  using finder_type = Param;\n\n  static inline constexpr size_t big = 40;\n\n  static constexpr finder_type make_finder(std::string_view const alphabet) {\n    return finder_type(alphabet);\n  }\n\n  std::mt19937 rng;\n\n  auto make_from(std::string_view const in, size_t const len) {\n    std::string s;\n    for (size_t i = 0; i < len; ++i) {\n      s += in[rng() % in.size()];\n    }\n    return s;\n  }\n};\n\ntemplate <typename Param>\nstruct FindFirstOfTest : FindFirstOpOfTest<Param> {\n  static inline constexpr auto json_strbound = \"\\\"\\\\\"sv;\n  static inline constexpr auto json_loalpha = \"abcdefghijklmnopqrstuvwxyz\"sv;\n};\n\nTYPED_TEST_SUITE_P(FindFirstOfTest);\n\nTYPED_TEST_P(FindFirstOfTest, json_str_stress_loalpha) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_strbound);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = self::make_from(self::json_loalpha, i);\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstOfTest, json_str_stress_loalpha_with_esc) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_strbound);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const loa = self::make_from(self::json_loalpha, i);\n    auto const s = fmt::format(R\"({}\\{})\", loa, loa);\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstOfTest, json_str_stress_loalpha_with_esc2) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_strbound);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const loa = self::make_from(self::json_loalpha, i);\n    auto const s = fmt::format(R\"({}\\\\{})\", loa, loa);\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstOfTest, json_str_stress_loalpha_with_esc2n) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_strbound);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const loa = self::make_from(self::json_loalpha, i);\n    auto const s = fmt::format(\n        R\"({}\\{}\\{})\", loa, self::make_from(self::json_loalpha, 1), loa);\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstOfTest, json_str_stress_loalpha_with_esc_edge) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_strbound);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const loa = self::make_from(self::json_loalpha, i);\n    auto const s = fmt::format(R\"({}\\)\", loa);\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstOfTest, json_str_stress_loalpha_with_esc2_edge) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_strbound);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const loa = self::make_from(self::json_loalpha, i);\n    auto const s = fmt::format(R\"({}\\\\)\", loa);\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstOfTest, json_str_stress_loalpha_with_esc2n_edge) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_strbound);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const loa = self::make_from(self::json_loalpha, i);\n    auto const s =\n        fmt::format(R\"({}\\{}\\)\", loa, self::make_from(self::json_loalpha, 1));\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\n// clang-format off\nREGISTER_TYPED_TEST_SUITE_P(\n    FindFirstOfTest\n    , json_str_stress_loalpha\n    , json_str_stress_loalpha_with_esc\n    , json_str_stress_loalpha_with_esc2\n    , json_str_stress_loalpha_with_esc2n\n    , json_str_stress_loalpha_with_esc_edge\n    , json_str_stress_loalpha_with_esc2_edge\n    , json_str_stress_loalpha_with_esc2n_edge\n    );\n// clang-format on\n\nstruct find_first_of_impls {\n  using stdfind_scalar = folly::simd::stdfind_scalar_finder_first_of;\n  using default_scalar = folly::simd::default_scalar_finder_first_of;\n  using ltindex_scalar = folly::simd::ltindex_scalar_finder_first_of;\n  using ltsparse_scalar = folly::simd::ltsparse_scalar_finder_first_of;\n#if __cpp_lib_execution >= 201902L\n  using stdfind_vector = folly::simd::stdfind_vector_finder_first_of;\n#endif\n  using rngfind_vector = folly::simd::rngfind_vector_finder_first_of;\n  using default_vector = folly::simd::composite_finder_first_of<\n      folly::simd::default_vector_finder_first_of,\n      default_scalar>;\n  using shuffle_vector = folly::simd::composite_finder_first_of<\n      folly::simd::shuffle_vector_finder_first_of,\n      default_scalar>;\n  using azmatch_vector = folly::simd::composite_finder_first_of<\n      folly::simd::azmatch_vector_finder_first_of,\n      default_scalar>;\n};\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    stdfind_scalar, FindFirstOfTest, find_first_of_impls::stdfind_scalar);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    default_scalar, FindFirstOfTest, find_first_of_impls::default_scalar);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    ltindex_scalar, FindFirstOfTest, find_first_of_impls::ltindex_scalar);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    ltsparse_scalar, FindFirstOfTest, find_first_of_impls::ltsparse_scalar);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    rngfind_vector, FindFirstOfTest, find_first_of_impls::rngfind_vector);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    default_vector, FindFirstOfTest, find_first_of_impls::default_vector);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    shuffle_vector, FindFirstOfTest, find_first_of_impls::shuffle_vector);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    azmatch_vector, FindFirstOfTest, find_first_of_impls::azmatch_vector);\n\ntemplate <typename Param>\nstruct FindFirstNotOfTest : FindFirstOpOfTest<Param> {\n  static inline constexpr auto json_whitespace = \" \\r\\n\\t\"sv;\n\n  static inline constexpr auto json_blacktext = \"[[[blackspace]]]\"sv;\n  static inline constexpr auto json_nulltext = \"\\0   [blackspace]\"sv;\n  static inline constexpr auto json_hitext = \"\\xff   [blackspace]\"sv;\n\n  static inline constexpr auto loweq_chars = \"\\x47\\x57\"sv;\n\n  static inline constexpr auto weird_chars = \"\\x47\\x83\"sv;\n\n  auto make_json_spaces(size_t const len) { //\n    return std::string(len, ' ');\n  }\n};\n\nTYPED_TEST_SUITE_P(FindFirstNotOfTest);\n\n// dup?\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_whitespace) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = self::make_from(self::json_whitespace, i);\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_spaces) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_spaces_then_text) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        + std::string(self::json_blacktext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_spaces_then_nulls) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        + std::string(self::json_nulltext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_spaces_then_his) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        + std::string(self::json_hitext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_wspaces) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_wspaces_then_text) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        + std::string(self::json_blacktext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_wspaces_then_nulls) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        + std::string(self::json_nulltext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, json_ws_stress_wspaces_then_his) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::json_whitespace);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_json_spaces(i) //\n        + std::string(self::json_hitext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, loweq_stress) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::loweq_chars);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_from(self::loweq_chars, i) //\n        + std::string(self::json_blacktext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\nTYPED_TEST_P(FindFirstNotOfTest, weird_stress) {\n  using self = folly::remove_cvref_t<decltype(*this)>;\n  constexpr auto const finder = self::make_finder(self::weird_chars);\n\n  for (size_t i = 0; i <= self::big; ++i) {\n    auto const s = \"\"s //\n        + self::make_from(self::weird_chars, i) //\n        + std::string(self::json_blacktext) //\n        ;\n    EXPECT_EQ(i, finder(s)) //\n        << \"input: \" << quote(s);\n  }\n}\n\n// clang-format off\nREGISTER_TYPED_TEST_SUITE_P(\n    FindFirstNotOfTest\n    , json_ws_stress_whitespace // dup?\n    , json_ws_stress_spaces\n    , json_ws_stress_spaces_then_text\n    , json_ws_stress_spaces_then_nulls\n    , json_ws_stress_spaces_then_his\n    , json_ws_stress_wspaces\n    , json_ws_stress_wspaces_then_text\n    , json_ws_stress_wspaces_then_nulls\n    , json_ws_stress_wspaces_then_his\n    , loweq_stress\n    , weird_stress\n    );\n// clang-format on\n\nstruct find_first_not_of_impls {\n  using default_scalar = folly::simd::default_scalar_finder_first_not_of;\n  using ltindex_scalar = folly::simd::ltindex_scalar_finder_first_not_of;\n  using ltsparse_scalar = folly::simd::ltsparse_scalar_finder_first_not_of;\n  using default_vector = folly::simd::composite_finder_first_of<\n      folly::simd::default_vector_finder_first_not_of,\n      default_scalar>;\n  using shuffle_vector = folly::simd::composite_finder_first_of<\n      folly::simd::shuffle_vector_finder_first_not_of,\n      default_scalar>;\n  using azmatch_vector = folly::simd::composite_finder_first_of<\n      folly::simd::azmatch_vector_finder_first_not_of,\n      default_scalar>;\n};\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    default_scalar,\n    FindFirstNotOfTest,\n    find_first_not_of_impls::default_scalar);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    ltindex_scalar,\n    FindFirstNotOfTest,\n    find_first_not_of_impls::ltindex_scalar);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    ltsparse_scalar,\n    FindFirstNotOfTest,\n    find_first_not_of_impls::ltsparse_scalar);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    default_vector,\n    FindFirstNotOfTest,\n    find_first_not_of_impls::default_vector);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    shuffle_vector,\n    FindFirstNotOfTest,\n    find_first_not_of_impls::shuffle_vector);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    azmatch_vector,\n    FindFirstNotOfTest,\n    find_first_not_of_impls::azmatch_vector);\n"
  },
  {
    "path": "folly/algorithm/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"binary_heap_test\",\n    srcs = [\"BinaryHeapTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/algorithm:binary_heap\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"stable_radix_sort_test\",\n    srcs = [\"StableRadixSortTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/algorithm:stable_radix_sort\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"stable_radix_sort_benchmark\",\n    srcs = [\"StableRadixSortBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/algorithm:stable_radix_sort\",\n        \"//folly/init:init\",\n        \"//folly/stats/detail:double_radix_sort\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_sort\"),\n    ],\n)\n"
  },
  {
    "path": "folly/algorithm/test/BinaryHeapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/BinaryHeap.h>\n\n#include <algorithm>\n#include <numeric>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\nTEST(BinaryHeap, DownHeap) {\n  std::vector<int> heap(16);\n  std::iota(heap.begin(), heap.end(), 1);\n  std::make_heap(heap.begin(), heap.end());\n\n  // To test heaps of different sizes, decrement the top, remove 0, and adjust\n  // until we get to an empty heap.\n  while (!heap.empty()) {\n    ASSERT_TRUE(std::is_heap(heap.begin(), heap.end()));\n    if (heap.front() > 1) {\n      --heap.front();\n    } else {\n      std::swap(heap.front(), heap.back());\n      heap.pop_back();\n    }\n    folly::down_heap(heap.begin(), heap.end());\n  }\n\n  // Empty heap is still a heap.\n  folly::down_heap(heap.begin(), heap.end());\n}\n"
  },
  {
    "path": "folly/algorithm/test/StableRadixSortBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/StableRadixSort.h>\n\n#include <folly/stats/detail/DoubleRadixSort.h>\n\n#include <boost/sort/pdqsort/pdqsort.hpp>\n#include <boost/sort/spreadsort/float_sort.hpp>\n#include <boost/sort/spreadsort/integer_sort.hpp>\n#include <folly/Benchmark.h>\n#include <folly/init/Init.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <random>\n#include <span>\n#include <type_traits>\n#include <vector>\n\nnamespace {\n\nconstexpr uint32_t kSeed = 42;\n\ntemplate <typename T>\nstd::span<const T> generateData(size_t n) {\n  static std::vector<T> data = [] {\n    std::vector<T> v(100000);\n    std::mt19937_64 gen(kSeed);\n    if constexpr (std::is_floating_point_v<T>) {\n      std::uniform_real_distribution<T> dist(-1e9, 1e9);\n      std::generate(v.begin(), v.end(), [&] { return dist(gen); });\n    } else {\n      std::uniform_int_distribution<T> dist;\n      std::generate(v.begin(), v.end(), [&] { return dist(gen); });\n    }\n    return v;\n  }();\n  return {data.data(), n};\n}\n\ntemplate <typename T>\nvoid benchmarkStdSort(size_t n) {\n  auto data = generateData<T>(n);\n  std::vector<T> copy(data.begin(), data.end());\n  std::stable_sort(copy.begin(), copy.end());\n  folly::doNotOptimizeAway(copy);\n}\n\ntemplate <typename T>\nvoid benchmarkRadixSort(size_t n) {\n  auto data = generateData<T>(n);\n  std::vector<T> copy(data.begin(), data.end());\n  folly::stable_radix_sort(copy.begin(), copy.end());\n  folly::doNotOptimizeAway(copy);\n}\n\ntemplate <typename T>\nvoid benchmarkSpreadsort(size_t n) {\n  auto data = generateData<T>(n);\n  std::vector<T> copy(data.begin(), data.end());\n  if constexpr (std::is_floating_point_v<T>) {\n    boost::sort::spreadsort::float_sort(copy.begin(), copy.end());\n  } else {\n    boost::sort::spreadsort::integer_sort(copy.begin(), copy.end());\n  }\n  folly::doNotOptimizeAway(copy);\n}\n\ntemplate <typename T>\nvoid benchmarkPdqsort(size_t n) {\n  auto data = generateData<T>(n);\n  std::vector<T> copy(data.begin(), data.end());\n  boost::sort::pdqsort(copy.begin(), copy.end());\n  folly::doNotOptimizeAway(copy);\n}\n\nvoid benchmarkDoubleDetailRadixSort(size_t n) {\n  auto data = generateData<double>(n);\n  std::vector<double> copy(data.begin(), data.end());\n  std::vector<double> tmp(n);\n  std::vector<uint64_t> buckets(256 * 9);\n  folly::detail::double_radix_sort(n, buckets.data(), copy.data(), tmp.data());\n  folly::doNotOptimizeAway(copy);\n}\n\n} // namespace\n\n// clang-format off\n#define BENCHMARK_UINT64(N) \\\n  BENCHMARK(Uint64_StdStableSort_##N) { benchmarkStdSort<uint64_t>(N); } \\\n  BENCHMARK(Uint64_RadixSort_##N) { benchmarkRadixSort<uint64_t>(N); } \\\n  BENCHMARK(Uint64_Spreadsort_##N) { benchmarkSpreadsort<uint64_t>(N); } \\\n  BENCHMARK(Uint64_Pdqsort_##N) { benchmarkPdqsort<uint64_t>(N); }\n\n#define BENCHMARK_INT64(N) \\\n  BENCHMARK(Int64_StdStableSort_##N) { benchmarkStdSort<int64_t>(N); } \\\n  BENCHMARK(Int64_RadixSort_##N) { benchmarkRadixSort<int64_t>(N); } \\\n  BENCHMARK(Int64_Spreadsort_##N) { benchmarkSpreadsort<int64_t>(N); } \\\n  BENCHMARK(Int64_Pdqsort_##N) { benchmarkPdqsort<int64_t>(N); }\n\n#define BENCHMARK_DOUBLE(N) \\\n  BENCHMARK(Double_StdStableSort_##N) { benchmarkStdSort<double>(N); } \\\n  BENCHMARK(Double_RadixSort_##N) { benchmarkRadixSort<double>(N); } \\\n  BENCHMARK(Double_Spreadsort_##N) { benchmarkSpreadsort<double>(N); } \\\n  BENCHMARK(Double_Pdqsort_##N) { benchmarkPdqsort<double>(N); } \\\n  BENCHMARK(Double_DetailRadixSort_##N) { benchmarkDoubleDetailRadixSort(N); }\n\n#define BENCHMARK_ALL_TYPES(N) \\\n  BENCHMARK_UINT64(N) \\\n  BENCHMARK_DRAW_LINE(); \\\n  BENCHMARK_INT64(N) \\\n  BENCHMARK_DRAW_LINE(); \\\n  BENCHMARK_DOUBLE(N) \\\n  BENCHMARK_DRAW_LINE();\n// clang-format on\n\nBENCHMARK_ALL_TYPES(100)\nBENCHMARK_ALL_TYPES(1000)\nBENCHMARK_ALL_TYPES(10000)\nBENCHMARK_ALL_TYPES(100000)\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/algorithm/test/StableRadixSortTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/algorithm/StableRadixSort.h>\n\n#include <algorithm>\n#include <cmath>\n#include <limits>\n#include <random>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace {\n\n// Helper struct for testing key extractors and stability\nstruct Item {\n  double score;\n  int originalIndex;\n\n  [[maybe_unused]] friend bool operator==(const Item&, const Item&) = default;\n};\n\n// Augmented comparator that distinguishes -0.0 from +0.0 via signbit,\n// matching the radix sort's natural ordering.\ntemplate <typename T>\nbool augmented_less(const T& a, const T& b) {\n  if constexpr (std::is_floating_point_v<T>) {\n    if (a < b) {\n      return true;\n    }\n    if (b < a) {\n      return false;\n    }\n    // a and b are equal (e.g. -0.0 and +0.0): negative sign sorts first\n    return std::signbit(a) > std::signbit(b);\n  } else {\n    return a < b;\n  }\n}\n\ntemplate <typename T>\nbool augmented_greater(const T& a, const T& b) {\n  return augmented_less(b, a);\n}\n\n// Validate that stable_radix_sort produces the same result as std::stable_sort\ntemplate <typename T>\nvoid validate(std::vector<T> v) {\n  auto expected = v;\n  std::stable_sort(expected.begin(), expected.end(), augmented_less<T>);\n  stable_radix_sort(v.begin(), v.end());\n  EXPECT_EQ(v, expected);\n}\n\ntemplate <typename T, typename Proj>\nvoid validate(std::vector<T> v, Proj proj) {\n  auto expected = v;\n  std::stable_sort(\n      expected.begin(), expected.end(), [&](const T& a, const T& b) {\n        return augmented_less(proj(a), proj(b));\n      });\n  stable_radix_sort(v.begin(), v.end(), proj);\n  EXPECT_EQ(v, expected);\n}\n\ntemplate <typename T>\nvoid validateDescending(std::vector<T> v) {\n  auto expected = v;\n  std::stable_sort(expected.begin(), expected.end(), augmented_greater<T>);\n  stable_radix_sort_descending(v.begin(), v.end());\n  EXPECT_EQ(v, expected);\n}\n\ntemplate <typename T, typename Proj>\nvoid validateDescending(std::vector<T> v, Proj proj) {\n  auto expected = v;\n  std::stable_sort(\n      expected.begin(), expected.end(), [&](const T& a, const T& b) {\n        return augmented_greater(proj(a), proj(b));\n      });\n  stable_radix_sort_descending(v.begin(), v.end(), proj);\n  EXPECT_EQ(v, expected);\n}\n\n// Test fixture\nclass StableRadixSortTest : public ::testing::Test {\n protected:\n  std::mt19937 rng{42}; // Fixed seed for reproducibility\n};\n\n// ============================================================================\n// Basic functionality tests\n// ============================================================================\n\nTEST_F(StableRadixSortTest, EmptyRange) {\n  validate(std::vector<uint64_t>{});\n}\n\nTEST_F(StableRadixSortTest, SingleElement) {\n  validate(std::vector<uint64_t>{42});\n}\n\nTEST_F(StableRadixSortTest, TwoElements) {\n  validate(std::vector<uint64_t>{5, 3});\n}\n\nTEST_F(StableRadixSortTest, AlreadySorted) {\n  validate(std::vector<uint64_t>{1, 2, 3, 4, 5});\n}\n\nTEST_F(StableRadixSortTest, ReverseSorted) {\n  validate(std::vector<uint64_t>{5, 4, 3, 2, 1});\n}\n\nTEST_F(StableRadixSortTest, AllEqual) {\n  validate(std::vector<uint64_t>(100, 42));\n}\n\n// ============================================================================\n// Unsigned integer types\n// ============================================================================\n\nTEST_F(StableRadixSortTest, Uint32Random) {\n  std::vector<uint32_t> v(1000);\n  std::uniform_int_distribution<uint32_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, Uint64Random) {\n  std::vector<uint64_t> v(1000);\n  std::uniform_int_distribution<uint64_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, Uint16Random) {\n  std::vector<uint16_t> v(1000);\n  std::uniform_int_distribution<uint16_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, Uint8Random) {\n  std::vector<uint8_t> v(1000);\n  std::uniform_int_distribution<int> dist(0, 255);\n  for (auto& x : v) {\n    x = static_cast<uint8_t>(dist(rng));\n  }\n  validate(std::move(v));\n}\n\n// ============================================================================\n// Signed integer types\n// ============================================================================\n\nTEST_F(StableRadixSortTest, Int32Random) {\n  std::vector<int32_t> v(1000);\n  std::uniform_int_distribution<int32_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, Int64Random) {\n  std::vector<int64_t> v(1000);\n  std::uniform_int_distribution<int64_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, Int32NegativeValues) {\n  validate(std::vector<int32_t>{-5, 3, -1, 0, -10, 7, 2, -3});\n}\n\nTEST_F(StableRadixSortTest, Int64Extremes) {\n  validate(\n      std::vector<int64_t>{\n          std::numeric_limits<int64_t>::min(),\n          std::numeric_limits<int64_t>::max(),\n          0,\n          -1,\n          1,\n          std::numeric_limits<int64_t>::min() + 1,\n          std::numeric_limits<int64_t>::max() - 1,\n      });\n}\n\n// ============================================================================\n// Floating-point types\n// ============================================================================\n\nTEST_F(StableRadixSortTest, DoubleRandom) {\n  std::vector<double> v(1000);\n  std::uniform_real_distribution<double> dist(-1000.0, 1000.0);\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, FloatRandom) {\n  std::vector<float> v(1000);\n  std::uniform_real_distribution<float> dist(-1000.0f, 1000.0f);\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, DoubleSpecialValues) {\n  validate(\n      std::vector<double>{\n          std::numeric_limits<double>::infinity(),\n          -std::numeric_limits<double>::infinity(),\n          0.0,\n          -0.0,\n          1.0,\n          -1.0,\n          std::numeric_limits<double>::max(),\n          std::numeric_limits<double>::lowest(),\n          std::numeric_limits<double>::denorm_min(), // smallest positive\n                                                     // subnormal\n      });\n}\n\nTEST_F(StableRadixSortTest, DoubleNegativeNumbers) {\n  validate(std::vector<double>{-0.5, -0.1, -0.9, -0.3, -0.7});\n}\n\nTEST_F(StableRadixSortTest, NegativeZeroPositiveZeroEquality) {\n  // Radix sort uses an augmented ordering where -0.0 sorts before +0.0\n  // (distinguished by signbit). Stability is preserved within each group.\n  std::vector<Item> items = {\n      {-0.0, 0},\n      {+0.0, 1},\n      {-0.0, 2},\n      {+0.0, 3},\n  };\n\n  stable_radix_sort(items.begin(), items.end(), [](const Item& item) {\n    return item.score;\n  });\n\n  // -0.0 values come first (stable among themselves), then +0.0 values\n  EXPECT_EQ(items[0].originalIndex, 0);\n  EXPECT_EQ(items[1].originalIndex, 2);\n  EXPECT_EQ(items[2].originalIndex, 1);\n  EXPECT_EQ(items[3].originalIndex, 3);\n}\n\nTEST_F(StableRadixSortTest, NegativeZeroPositiveZeroEqualityFloat) {\n  // Same test for float type\n  struct FloatItem {\n    float score;\n    int originalIndex;\n  };\n\n  std::vector<FloatItem> items = {\n      {-0.0f, 0},\n      {+0.0f, 1},\n      {-0.0f, 2},\n      {+0.0f, 3},\n  };\n\n  stable_radix_sort(items.begin(), items.end(), [](const FloatItem& item) {\n    return item.score;\n  });\n\n  // -0.0f values come first (stable among themselves), then +0.0f values\n  EXPECT_EQ(items[0].originalIndex, 0);\n  EXPECT_EQ(items[1].originalIndex, 2);\n  EXPECT_EQ(items[2].originalIndex, 1);\n  EXPECT_EQ(items[3].originalIndex, 3);\n}\n\n// ============================================================================\n// Descending order tests\n// ============================================================================\n\nTEST_F(StableRadixSortTest, Uint64Descending) {\n  std::vector<uint64_t> v(1000);\n  std::uniform_int_distribution<uint64_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validateDescending(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, Int64Descending) {\n  std::vector<int64_t> v(1000);\n  std::uniform_int_distribution<int64_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validateDescending(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, DoubleDescending) {\n  std::vector<double> v(1000);\n  std::uniform_real_distribution<double> dist(-1000.0, 1000.0);\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validateDescending(std::move(v));\n}\n\n// ============================================================================\n// Key extractor tests\n// ============================================================================\n\nTEST_F(StableRadixSortTest, KeyExtractorDouble) {\n  std::vector<Item> items(100);\n  std::uniform_real_distribution<double> dist(-100.0, 100.0);\n  for (int i = 0; i < 100; ++i) {\n    items[i].score = dist(rng);\n    items[i].originalIndex = i;\n  }\n\n  validate(std::move(items), [](const Item& item) { return item.score; });\n}\n\nTEST_F(StableRadixSortTest, KeyExtractorDescending) {\n  std::vector<Item> items(100);\n  std::uniform_real_distribution<double> dist(-100.0, 100.0);\n  for (int i = 0; i < 100; ++i) {\n    items[i].score = dist(rng);\n    items[i].originalIndex = i;\n  }\n\n  validateDescending(std::move(items), [](const Item& item) {\n    return item.score;\n  });\n}\n\nTEST_F(StableRadixSortTest, KeyExtractorInteger) {\n  struct Record {\n    uint64_t id{};\n    std::string name;\n\n    bool operator==(const Record&) const = default;\n  };\n\n  std::vector<Record> records = {\n      {5, \"five\"},\n      {2, \"two\"},\n      {8, \"eight\"},\n      {1, \"one\"},\n      {3, \"three\"},\n  };\n\n  validate(std::move(records), [](const Record& r) { return r.id; });\n}\n\n// ============================================================================\n// Stability tests\n// ============================================================================\n\nTEST_F(StableRadixSortTest, StabilityPreserved) {\n  // Create items with duplicate scores\n  std::vector<Item> items;\n  items.reserve(100);\n  for (int i = 0; i < 100; ++i) {\n    items.push_back({static_cast<double>(i / 10), i}); // 10 items per score\n  }\n\n  // Shuffle to randomize order\n  std::shuffle(items.begin(), items.end(), rng);\n\n  validate(std::move(items), [](const Item& item) { return item.score; });\n}\n\nTEST_F(StableRadixSortTest, StabilityWithMultipleGroups) {\n  // Create items with 3 distinct score values\n  std::vector<Item> items;\n  for (int i = 0; i < 30; ++i) {\n    double score = static_cast<double>(i % 3); // 0.0, 1.0, or 2.0\n    items.push_back({score, i});\n  }\n\n  validate(std::move(items), [](const Item& item) { return item.score; });\n}\n\n// ============================================================================\n// Comparison with std::stable_sort\n// ============================================================================\n\nTEST_F(StableRadixSortTest, MatchesStableSort) {\n  std::vector<double> v(500);\n  std::uniform_real_distribution<double> dist(-1000.0, 1000.0);\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, MatchesStableSortDescending) {\n  std::vector<double> v(500);\n  std::uniform_real_distribution<double> dist(-1000.0, 1000.0);\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validateDescending(std::move(v));\n}\n\n// ============================================================================\n// Large input tests\n// ============================================================================\n\nTEST_F(StableRadixSortTest, LargeInput) {\n  constexpr size_t kSize = 100000;\n  std::vector<uint64_t> v(kSize);\n  std::uniform_int_distribution<uint64_t> dist;\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\nTEST_F(StableRadixSortTest, LargeInputDouble) {\n  constexpr size_t kSize = 100000;\n  std::vector<double> v(kSize);\n  std::uniform_real_distribution<double> dist(-1e9, 1e9);\n  for (auto& x : v) {\n    x = dist(rng);\n  }\n  validate(std::move(v));\n}\n\n// ============================================================================\n// Small input (fallback to std::stable_sort)\n// ============================================================================\n\nTEST_F(StableRadixSortTest, SmallInputFallback) {\n  validate(std::vector<uint64_t>{9, 7, 5, 3, 1, 8, 6, 4, 2, 0});\n}\n\n// ============================================================================\n// Key transformation helpers\n// ============================================================================\n\nTEST_F(StableRadixSortTest, FloatKeyCorrectness) {\n  using namespace stable_radix_sort_keys;\n\n  FloatKey<double, false> asc;\n  FloatKey<double, true> desc;\n\n  // Verify ordering is preserved\n  EXPECT_LT(asc(-1.0), asc(0.0));\n  EXPECT_LT(asc(0.0), asc(1.0));\n  EXPECT_LT(asc(-std::numeric_limits<double>::infinity()), asc(-1e308));\n  EXPECT_LT(asc(1e308), asc(std::numeric_limits<double>::infinity()));\n\n  // Descending should invert\n  EXPECT_GT(desc(-1.0), desc(0.0));\n  EXPECT_GT(desc(0.0), desc(1.0));\n}\n\nTEST_F(StableRadixSortTest, IntegralKeyCorrectness) {\n  using namespace stable_radix_sort_keys;\n\n  IntegralKey<int64_t, false> asc;\n  IntegralKey<int64_t, true> desc;\n\n  // Verify ordering is preserved\n  EXPECT_LT(asc(std::numeric_limits<int64_t>::min()), asc(-1));\n  EXPECT_LT(asc(-1), asc(0));\n  EXPECT_LT(asc(0), asc(1));\n  EXPECT_LT(asc(1), asc(std::numeric_limits<int64_t>::max()));\n\n  // Descending should invert\n  EXPECT_GT(desc(-1), desc(0));\n  EXPECT_GT(desc(0), desc(1));\n}\n\n} // namespace\n} // namespace folly\n"
  },
  {
    "path": "folly/base64.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <vector>\n#include <folly/CPortability.h>\n#include <folly/Portability.h>\n#include <folly/detail/base64_detail/Base64Api.h>\n#include <folly/detail/base64_detail/Base64Common.h>\n#include <folly/lang/Exception.h>\n#include <folly/memory/UninitializedMemoryHacks.h>\n\nnamespace folly {\n\n//\n// base64 encoding/decoding\n//\n// There are a few variations of base64 encoding.\n//\n// We have 2: base64 and base64URL.\n//\n// base64 uses '+' '/' for encoding 62 and 63 and uses '=' padding symbol.\n// (padding symbols are required on decoding)\n//\n// base64URL uses '-' '_' for encoding 62 and 63 and has no padding.\n// Decoding with base64URL will accept both base64 and base64URL encoded data +\n// padding is always optional.\n//\n// SIMD implementation is based on 0x80 blog.\n// See details explained in folly/detail/base64_detail/README.md\n//\n\n//\n// High level API.\n// Encoding never fails, except for allocation.\n// Decoding will throw base64_decode_error if it fails.\n//\n// NOTE: the expection does not contain detailed information\n//       about the error because keeping track of that is overhead.\n//       We can potentially improve error reporting by doing a second\n//       pass if we decide that it's benefitial.\n\nstruct base64_decode_error;\n\ninline auto base64Encode(std::string_view s) -> std::string;\ninline auto base64Decode(std::string_view s) -> std::string;\ninline auto base64URLEncode(std::string_view s) -> std::string;\ninline auto base64URLDecode(std::string_view s) -> std::string;\ninline auto base64PHPStrictDecode(std::string_view s) -> std::string;\n\n// Low level API.\n//\n// This API does not throw and is constexpr enabled.\n//\n// Encode returns a pointer past the last the byte written\n// Decode returns a struct with `is_success` flag and the pointer `o`\n// past the last char written.\n//\n// NOTE: decode will not stop writing when encountering a failure\n//       and can always write up to size.\n//\n// NOTE: since on C++17 we cannot always adequately determine if\n//       the function is running in compile time or not,\n//       we provide explicit runime versions too.\n\nconstexpr std::size_t base64EncodedSize(std::size_t inSize) noexcept;\nconstexpr std::size_t base64URLEncodedSize(std::size_t inSize) noexcept;\n\ninline constexpr char* base64Encode(\n    const char* f, const char* l, char* o) noexcept;\ninline constexpr char* base64URLEncode(\n    const char* f, const char* l, char* o) noexcept;\n\ninline char* base64EncodeRuntime(\n    const char* f, const char* l, char* o) noexcept;\ninline char* base64URLEncodeRuntime(\n    const char* f, const char* l, char* o) noexcept;\n\nconstexpr std::size_t base64DecodedSize(const char* f, const char* l) noexcept;\nconstexpr std::size_t base64DecodedSize(std::string_view s) noexcept;\n\nconstexpr std::size_t base64URLDecodedSize(\n    const char* f, const char* l) noexcept;\nconstexpr std::size_t base64URLDecodedSize(std::string_view s) noexcept;\n\nconstexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(\n    const char* f, const char* l) noexcept;\nconstexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(\n    std::string_view s) noexcept;\n\nstruct base64_decode_result {\n  bool is_success;\n  char* o;\n};\n\ninline constexpr base64_decode_result base64Decode(\n    const char* f, const char* l, char* o) noexcept;\ninline constexpr base64_decode_result base64Decode(\n    std::string_view s, char* o) noexcept;\n\ninline constexpr base64_decode_result base64URLDecode(\n    const char* f, const char* l, char* o) noexcept;\ninline constexpr base64_decode_result base64URLDecode(\n    std::string_view s, char* o) noexcept;\n\ninline base64_decode_result base64DecodeRuntime(\n    const char* f, const char* l, char* o) noexcept;\ninline base64_decode_result base64DecodeRuntime(\n    std::string_view s, char* o) noexcept;\n\ninline base64_decode_result base64URLDecodeRuntime(\n    const char* f, const char* l, char* o) noexcept;\ninline base64_decode_result base64URLDecodeRuntime(\n    std::string_view s, char* o) noexcept;\n\ninline constexpr base64_decode_result base64PHPStrictDecode(\n    const char* f, const char* l, char* o) noexcept;\ninline constexpr base64_decode_result base64PHPStrictDecode(\n    std::string_view s, char* o) noexcept;\n\n// -----------------------------------------------------------------\n// implementation\n\nstruct base64_decode_error : std::runtime_error {\n  using std::runtime_error::runtime_error;\n};\n\nconstexpr std::size_t base64EncodedSize(std::size_t inSize) noexcept {\n  return detail::base64_detail::base64EncodedSize(inSize);\n}\n\nconstexpr std::size_t base64URLEncodedSize(std::size_t inSize) noexcept {\n  return detail::base64_detail::base64URLEncodedSize(inSize);\n}\n\ninline constexpr char* base64Encode(\n    const char* f, const char* l, char* o) noexcept {\n  return detail::base64_detail::base64Encode(f, l, o);\n}\n\ninline constexpr char* base64URLEncode(\n    const char* f, const char* l, char* o) noexcept {\n  return detail::base64_detail::base64URLEncode(f, l, o);\n}\n\ninline char* base64EncodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  return detail::base64_detail::base64EncodeRuntime(f, l, o);\n}\n\ninline char* base64URLEncodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  return detail::base64_detail::base64URLEncodeRuntime(f, l, o);\n}\n\ninline std::string base64Encode(std::string_view s) {\n  std::string res;\n  std::size_t resSize = folly::base64EncodedSize(s.size());\n  folly::resizeWithoutInitialization(res, resSize);\n  folly::base64EncodeRuntime(s.data(), s.data() + s.size(), res.data());\n  return res;\n}\n\ninline std::string base64URLEncode(std::string_view s) {\n  std::string res;\n  std::size_t resSize = folly::base64URLEncodedSize(s.size());\n  folly::resizeWithoutInitialization(res, resSize);\n  folly::base64URLEncodeRuntime(s.data(), s.data() + s.size(), res.data());\n  return res;\n}\n\nconstexpr std::size_t base64DecodedSize(const char* f, const char* l) noexcept {\n  return detail::base64_detail::base64DecodedSize(f, l);\n}\n\nconstexpr std::size_t base64DecodedSize(std::string_view s) noexcept {\n  return folly::base64DecodedSize(s.data(), s.data() + s.size());\n}\n\nconstexpr std::size_t base64URLDecodedSize(\n    const char* f, const char* l) noexcept {\n  return detail::base64_detail::base64URLDecodedSize(f, l);\n}\n\nconstexpr std::size_t base64URLDecodedSize(std::string_view s) noexcept {\n  return folly::base64URLDecodedSize(s.data(), s.data() + s.size());\n}\n\nconstexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(\n    const char* f, const char* l) noexcept {\n  return detail::base64_detail::base64PHPStrictDecodeRequiredOutputSize(f, l);\n}\n\nconstexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(\n    std::string_view s) noexcept {\n  return folly::base64PHPStrictDecodeRequiredOutputSize(\n      s.data(), s.data() + s.size());\n}\n\ninline constexpr base64_decode_result base64Decode(\n    const char* f, const char* l, char* o) noexcept {\n  auto detailResult = detail::base64_detail::base64Decode(f, l, o);\n  return {detailResult.isSuccess, detailResult.o};\n}\n\ninline constexpr base64_decode_result base64Decode(\n    std::string_view s, char* o) noexcept {\n  return folly::base64Decode(s.data(), s.data() + s.size(), o);\n}\n\ninline constexpr base64_decode_result base64URLDecode(\n    const char* f, const char* l, char* o) noexcept {\n  auto detailResult = detail::base64_detail::base64URLDecode(f, l, o);\n  return {detailResult.isSuccess, detailResult.o};\n}\n\ninline constexpr base64_decode_result base64URLDecode(\n    std::string_view s, char* o) noexcept {\n  return folly::base64URLDecode(s.data(), s.data() + s.size(), o);\n}\n\ninline base64_decode_result base64DecodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  auto detailResult = detail::base64_detail::base64DecodeRuntime(f, l, o);\n  return {detailResult.isSuccess, detailResult.o};\n}\n\ninline base64_decode_result base64DecodeRuntime(\n    std::string_view s, char* o) noexcept {\n  return folly::base64DecodeRuntime(s.data(), s.data() + s.size(), o);\n}\n\ninline base64_decode_result base64URLDecodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  auto detailResult = detail::base64_detail::base64URLDecodeRuntime(f, l, o);\n  return {detailResult.isSuccess, detailResult.o};\n}\n\ninline base64_decode_result base64URLDecodeRuntime(\n    std::string_view s, char* o) noexcept {\n  return folly::base64URLDecodeRuntime(s.data(), s.data() + s.size(), o);\n}\n\ninline constexpr base64_decode_result base64PHPStrictDecode(\n    const char* f, const char* l, char* o) noexcept {\n  auto detailResult = detail::base64_detail::base64PHPStrictDecode(f, l, o);\n  return {detailResult.isSuccess, detailResult.o};\n}\n\ninline constexpr base64_decode_result base64PHPStrictDecode(\n    std::string_view s, char* o) noexcept {\n  return folly::base64PHPStrictDecode(s.data(), s.data() + s.size(), o);\n}\n\n// NOTE: for resizeWithoutInitialization we don't need to declare the macros,\n//       since we are using char which is already included by default.\ninline std::string base64Decode(std::string_view s) {\n  std::string res;\n  std::size_t resSize = folly::base64DecodedSize(s);\n  folly::resizeWithoutInitialization(res, resSize);\n\n  if (!folly::base64DecodeRuntime(s, res.data()).is_success) {\n    folly::throw_exception<base64_decode_error>(\"Base64 Decoding failed\");\n  }\n  return res;\n}\n\ninline std::string base64URLDecode(std::string_view s) {\n  std::string res;\n  std::size_t resSize = folly::base64URLDecodedSize(s);\n  folly::resizeWithoutInitialization(res, resSize);\n\n  if (!folly::base64URLDecodeRuntime(s, res.data()).is_success) {\n    folly::throw_exception<base64_decode_error>(\"Base64URL Decoding failed\");\n  }\n  return res;\n}\n\ninline std::string base64PHPStrictDecode(std::string_view s) {\n  std::string res;\n  folly::resizeWithoutInitialization(\n      res, base64PHPStrictDecodeRequiredOutputSize(s));\n\n  if (s.empty()) {\n    return res;\n  }\n\n  base64_decode_result decoded = folly::base64PHPStrictDecode(s, res.data());\n\n  if (!decoded.is_success) {\n    folly::throw_exception<base64_decode_error>(\"Base64PHP Decoding failed\");\n  }\n\n  res.resize(decoded.o - res.data());\n  res.shrink_to_fit();\n  return res;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/buck_config/BUCK",
    "content": "load(\"@fbsource//tools/build_defs:fb_native_wrapper.bzl\", \"fb_native\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_native.config_setting(\n    name = \"folly-singleton-schedule-at-exit-disabled\",\n    constraint_values = [\n        \"fbsource//xplat/folly/buck_config/constraints:singleton-schedule-at-exit-disabled\",\n    ],\n    visibility = [\"PUBLIC\"],\n)\n\nfb_native.config_setting(\n    name = \"folly-f14-fallback-disabled\",\n    constraint_values = [\n        \"fbsource//xplat/folly/buck_config/constraints:f14-fallback-disabled\",\n    ],\n    visibility = [\"PUBLIC\"],\n)\n\nfb_native.config_setting(\n    name = \"folly-gflags-enabled\",\n    constraint_values = [\n        \"fbsource//xplat/folly/buck_config/constraints:gflags-enabled\",\n    ],\n    visibility = [\"PUBLIC\"],\n)\n\n# !!!! fbcode/folly/buck_config/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n"
  },
  {
    "path": "folly/buck_config/constraints/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"non_fbcode_target\")\nload(\"@fbsource//tools/build_defs:fb_native_wrapper.bzl\", \"fb_native\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = fb_native.constraint_setting,\n    name = \"singleton\",\n    visibility = [\"PUBLIC\"],\n)\n\nnon_fbcode_target(\n    _kind = fb_native.constraint_value,\n    name = \"singleton-schedule-at-exit-disabled\",\n    constraint_setting = \":singleton\",\n    visibility = [\"PUBLIC\"],\n)\n\nnon_fbcode_target(\n    _kind = fb_native.constraint_setting,\n    name = \"f14-fallback\",\n    visibility = [\"PUBLIC\"],\n)\n\nnon_fbcode_target(\n    _kind = fb_native.constraint_value,\n    name = \"f14-fallback-disabled\",\n    constraint_setting = \":f14-fallback\",\n    visibility = [\"PUBLIC\"],\n)\n\nnon_fbcode_target(\n    _kind = fb_native.constraint_setting,\n    name = \"gflags\",\n    visibility = [\"PUBLIC\"],\n)\n\nnon_fbcode_target(\n    _kind = fb_native.constraint_value,\n    name = \"gflags-enabled\",\n    constraint_setting = \":gflags\",\n    visibility = [\"PUBLIC\"],\n)\n"
  },
  {
    "path": "folly/build/bootstrap-osx-homebrew.sh",
    "content": "#!/bin/bash -x\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# The only prerequisite should be homebrew. If something doesn't work out of\n# the box with just homebrew, let's fix it.\n\n# fail fast\nset -e\n\nBUILD_DIR=${BUILD_DIR:-_build}\n\n# brew install alias\nbrew_install() {\n    brew install \"$@\" || brew upgrade \"$@\"\n}\n\n# install deps\ninstall_deps() {\n    # folly deps\n    dependencies=(\n        boost\n        cmake\n        double-conversion\n        gflags\n        glog\n        jemalloc\n        libevent\n        lz4\n        openssl\n        pkg-config\n        snappy\n        xz\n        fmt\n    )\n\n    # fetch deps\n    for dependency in \"${dependencies[@]}\"; do\n        brew_install \"${dependency}\"\n    done\n}\n\ninstall_deps\n\n# Allows this script to be invoked from anywhere in the source tree but the\n# BUILD_DIR we create will always be in the top level folly directory\nTOP_LEVEL_DIR=\"$(cd \"$(dirname -- \"$0\")\"/../.. ; pwd)\"  # folly\ncd \"$TOP_LEVEL_DIR\"\nmkdir -p \"${BUILD_DIR}\"\ncd \"${BUILD_DIR}\"\n\nOPENSSL_INCLUDES=$(brew --prefix openssl)/include\ncmake \\\n    -DOPENSSL_INCLUDE_DIR=\"${OPENSSL_INCLUDES}\" \\\n    -DFOLLY_HAVE_WEAK_SYMBOLS=ON \\\n    \"$@\" \\\n    ..\n\n# fetch googletest, if doesn't exist\nGTEST_VER=1.10.0\nGTEST_DIR=gtest-${GTEST_VER}\nif [ ! -d ${GTEST_DIR} ]; then\n    mkdir ${GTEST_DIR}\n    curl -SL \\\n        https://github.com/google/googletest/archive/release-${GTEST_VER}.tar.gz | \\\n        tar -xvzf - --strip-components=1 -C ${GTEST_DIR}\nfi\n\n# make, test, install\nmake\nmake install\n"
  },
  {
    "path": "folly/build/build-debs-ubuntu-18.04.sh",
    "content": "#!/bin/sh -x\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Note: this script was written on Ubuntu 18.04 and will need work in order to\n# work on additional platforms. This is left as an exercise for the reader.\n\nset -e\n\nBUILD_DIR=${BUILD_DIR:-_build}\nBOOST_VERSION=${BOOST_VERSION:-1.65.1}\nLIBEVENT_VERSION=${LIBEVENT_VERSION:-2.0-5}\nSSL_VERSION=${SSL_VERSION:-1.0.0}\n\nVERSION_FILE_PATH=folly/VERSION\nVERSION=${VERSION:-\"$(sed 's/:/./' ${VERSION_FILE_PATH})\"}\nITERATION=${ITERATION:-1}\n\nDESTDIR=${DESTDIR:-$(mktemp -d)}\nDEBUGDIR=${DEBUGDIR:-$DESTDIR/usr/lib/debug}\nPKGDIR=${PKGDIR:-/tmp}\n\nDESC=\"Folly is an open-source C++ library developed and used at Facebook\n  as a foundation for our infrastructure.\"\nURL=https://github.com/facebook/folly\nLICENSE=\"Apache License v2.0\"\nMAINTAINER=\"Folly Eng\"\n\ncommand -v fpm || (echo \"Please install fpm from https://github.com/jordansissel/fpm\" && exit 1)\n[ -d \"$DESTDIR\" ]\n\ncommand -v cmake || (echo \"Please install cmake.\" && exit 1)\n\n# Make\nrm -rf \"${BUILD_DIR}\"\nmkdir \"${BUILD_DIR}\"\ncd \"${BUILD_DIR}\"\ncmake ..\nmake\nmake install DESTDIR=\"$DESTDIR\"\n\n# Move symbols to debug file\n[ -d \"$DEBUGDIR/usr/lib\" ] || mkdir -p \"$DEBUGDIR/usr/lib\"\nfind \"$DESTDIR/usr/lib\" -maxdepth 1 -iname \"lib*.so.*\" -type f \\\n  -execdir objcopy --only-keep-debug {} \"$DEBUGDIR/usr/lib/{}.debug\" \\; \\\n  -execdir strip --strip-debug --strip-unneeded {} \\; \\\n  -execdir objcopy --add-gnu-debuglink \"$DEBUGDIR/usr/lib/{}.debug\" {} \\;\n\n# Build debs\nfpm \\\n  -s dir -t deb \\\n  -n \"libfolly$VERSION\" \\\n  -v \"$VERSION\" --iteration \"$ITERATION\" \\\n  -p \"$PKGDIR/NAME_VERSION-ITERATION_ARCH.deb\" \\\n  -C \"$DESTDIR\" \\\n  --description \"$DESC\" \\\n  --vendor Facebook \\\n  --url \"$URL\" \\\n  --license \"$LICENSE\" \\\n  --maintainer \"$MAINTAINER\" \\\n  --category libs \\\n  --provides libfolly \\\n  --depends libc6 \\\n  --depends libstdc++6 \\\n  --depends libboost-context\"$BOOST_VERSION\" \\\n  --depends libboost-filesystem\"$BOOST_VERSION\" \\\n  --depends libboost-program-options\"$BOOST_VERSION\" \\\n  --depends libboost-regex\"$BOOST_VERSION\" \\\n  --depends libboost-system\"$BOOST_VERSION\" \\\n  --depends libboost-thread\"$BOOST_VERSION\" \\\n  --depends libdouble-conversion1 \\\n  --depends libevent-\"$LIBEVENT_VERSION\" \\\n  --depends libgflags2 \\\n  --depends libgoogle-glog0 \\\n  --depends libicu52 \\\n  --depends libjemalloc1 \\\n  --depends liblz4-1 \\\n  --depends liblzma5 \\\n  --depends libsnappy1 \\\n  --depends libssl\"$SSL_VERSION\" \\\n  --depends zlib1g \\\n  --exclude usr/lib/debug \\\n  --exclude usr/lib/*.a \\\n  --exclude usr/lib/*.la \\\n  usr/lib\n\nfpm \\\n  -s dir -t deb \\\n  -n libfolly-dev \\\n  -v \"$VERSION\" --iteration \"$ITERATION\" \\\n  -p \"$PKGDIR/NAME_VERSION-ITERATION_ARCH.deb\" \\\n  -C \"$DESTDIR\" \\\n  --description \"$DESC\" \\\n  --vendor Facebook \\\n  --url \"$URL\" \\\n  --license \"$LICENSE\" \\\n  --maintainer \"$MAINTAINER\" \\\n  --category devel \\\n  --depends \"libfolly$VERSION\" \\\n  --exclude usr/lib/*.so* \\\n  usr/include \\\n  usr/lib/debug\n\necho \"${DESTDIR}\"\n\n"
  },
  {
    "path": "folly/channels/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"channel\",\n    headers = [\n        \"Channel.h\",\n        \"Channel-fwd.h\",\n        \"Channel-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:synchronized\",\n        \"//folly/coro:coroutine\",\n        \"//folly/experimental/channels/detail:channel_bridge\",\n    ],\n)\n\n# !!!! fbcode/folly/channels/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"channel_callback_handle\",\n    headers = [\n        \"ChannelCallbackHandle.h\",\n    ],\n    exported_deps = [\n        \"//folly:intrusive_list\",\n        \"//folly:scope_guard\",\n        \"//folly/channels:channel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"channel_processor\",\n    headers = [\n        \"ChannelProcessor.h\",\n        \"ChannelProcessor-inl.h\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:executor\",\n        \"//folly/channels:consume_channel\",\n        \"//folly/channels:merge_channel\",\n        \"//folly/channels:rate_limiter\",\n        \"//folly/channels:transform\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/experimental/channels/detail:intrusive_ptr\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"consume_channel\",\n    headers = [\n        \"ConsumeChannel.h\",\n        \"ConsumeChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:executor\",\n        \"//folly:format\",\n        \"//folly:intrusive_list\",\n        \"//folly:scope_guard\",\n        \"//folly/channels:channel\",\n        \"//folly/channels:channel_callback_handle\",\n        \"//folly/coro:task\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"fanout_channel\",\n    headers = [\n        \"FanoutChannel.h\",\n        \"FanoutChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel\",\n        \"//folly/channels:fanout_sender\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"fanout_sender\",\n    headers = [\n        \"FanoutSender.h\",\n        \"FanoutSender-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:pointer_variant\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"merge\",\n    headers = [\n        \"Merge.h\",\n        \"Merge-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"merge_channel\",\n    headers = [\n        \"MergeChannel.h\",\n        \"MergeChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"multiplex_channel\",\n    headers = [\n        \"MultiplexChannel.h\",\n        \"MultiplexChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/range-v3:range-v3\",\n        \"//folly/channels:channel\",\n        \"//folly/channels:fanout_sender\",\n        \"//folly/channels:on_closed_exception\",\n        \"//folly/channels:rate_limiter\",\n        \"//folly/container:f14_hash\",\n        \"//folly/coro:future_util\",\n        \"//folly/coro:mutex\",\n        \"//folly/coro:promise\",\n        \"//folly/coro:task\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:multiplexer_traits\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"on_closed_exception\",\n    headers = [\n        \"OnClosedException.h\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"producer\",\n    headers = [\n        \"Producer.h\",\n        \"Producer-inl.h\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:cancellation_token\",\n        \"//folly/channels:channel\",\n        \"//folly/channels:channel_callback_handle\",\n        \"//folly/channels:consume_channel\",\n        \"//folly/coro:task\",\n        \"//folly/executors:sequenced_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"proxy_channel\",\n    headers = [\n        \"ProxyChannel.h\",\n        \"ProxyChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"rate_limiter\",\n    headers = [\n        \"RateLimiter.h\",\n    ],\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly/executors:sequenced_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"max_concurrent_rate_limiter\",\n    srcs = [\n        \"MaxConcurrentRateLimiter.cpp\",\n    ],\n    exported_deps = [\n        \"//folly:synchronized\",\n        \"//folly/channels:rate_limiter\",\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/executors:sequenced_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"transform\",\n    headers = [\n        \"Transform.h\",\n        \"Transform-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel\",\n        \"//folly/channels:on_closed_exception\",\n        \"//folly/channels:rate_limiter\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:task\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/experimental/channels/detail:utility\",\n    ],\n)\n"
  },
  {
    "path": "folly/channels/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME channel\n  HEADERS\n    Channel-fwd.h\n    Channel-inl.h\n    Channel.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_coroutine\n    folly_experimental_channels_detail_channel_bridge\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME channel_callback_handle\n  HEADERS\n    ChannelCallbackHandle.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_intrusive_list\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME channel_processor\n  HEADERS\n    ChannelProcessor-inl.h\n    ChannelProcessor.h\n  EXPORTED_DEPS\n    folly_channels_consume_channel\n    folly_channels_merge_channel\n    folly_channels_rate_limiter\n    folly_channels_transform\n    folly_executor\n    folly_executors_serial_executor\n    folly_experimental_channels_detail_intrusive_ptr\n)\n\nfolly_add_library(\n  NAME consume_channel\n  HEADERS\n    ConsumeChannel-inl.h\n    ConsumeChannel.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_channels_channel_callback_handle\n    folly_coro_task\n    folly_executor\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_utility\n    folly_format\n    folly_intrusive_list\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME fanout_channel\n  HEADERS\n    FanoutChannel-inl.h\n    FanoutChannel.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_channels_fanout_sender\n    folly_container_f14_hash\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_utility\n)\n\nfolly_add_library(\n  NAME fanout_sender\n  HEADERS\n    FanoutSender-inl.h\n    FanoutSender.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_container_f14_hash\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_pointer_variant\n    folly_experimental_channels_detail_utility\n)\n\nfolly_add_library(\n  NAME max_concurrent_rate_limiter\n  SRCS\n    MaxConcurrentRateLimiter.cpp\n  EXPORTED_DEPS\n    folly_channels_rate_limiter\n    folly_concurrency_unbounded_queue\n    folly_executors_sequenced_executor\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME merge\n  HEADERS\n    Merge-inl.h\n    Merge.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_container_f14_hash\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_utility\n)\n\nfolly_add_library(\n  NAME merge_channel\n  HEADERS\n    MergeChannel-inl.h\n    MergeChannel.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_container_f14_hash\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_utility\n)\n\nfolly_add_library(\n  NAME multiplex_channel\n  HEADERS\n    MultiplexChannel-inl.h\n    MultiplexChannel.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_channels_fanout_sender\n    folly_channels_on_closed_exception\n    folly_channels_rate_limiter\n    folly_container_f14_hash\n    folly_coro_future_util\n    folly_coro_mutex\n    folly_coro_promise\n    folly_coro_task\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_multiplexer_traits\n    folly_experimental_channels_detail_utility\n)\n\nfolly_add_library(\n  NAME on_closed_exception\n  HEADERS\n    OnClosedException.h\n)\n\nfolly_add_library(\n  NAME producer\n  HEADERS\n    Producer-inl.h\n    Producer.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_channels_channel\n    folly_channels_channel_callback_handle\n    folly_channels_consume_channel\n    folly_coro_task\n    folly_executors_sequenced_executor\n)\n\nfolly_add_library(\n  NAME proxy_channel\n  HEADERS\n    ProxyChannel-inl.h\n    ProxyChannel.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_utility\n)\n\nfolly_add_library(\n  NAME rate_limiter\n  HEADERS\n    RateLimiter.h\n  EXPORTED_DEPS\n    folly_executors_sequenced_executor\n    folly_function\n)\n\nfolly_add_library(\n  NAME transform\n  HEADERS\n    Transform-inl.h\n    Transform.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_channels_on_closed_exception\n    folly_channels_rate_limiter\n    folly_coro_async_generator\n    folly_coro_task\n    folly_executors_sequenced_executor\n    folly_experimental_channels_detail_utility\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/channels/Channel-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/experimental/channels/detail/ChannelBridge.h>\n\n#include <optional>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename TValue>\nclass Receiver;\n\ntemplate <typename TValue>\nclass Sender;\n\nnamespace detail {\ntemplate <typename TValue>\nChannelBridgePtr<TValue>& senderGetBridge(Sender<TValue>& sender);\n\ntemplate <typename TValue>\nbool receiverWait(\n    Receiver<TValue>& receiver, detail::IChannelCallback* receiverCallback);\n\ntemplate <typename TValue>\ndetail::IChannelCallback* cancelReceiverWait(Receiver<TValue>& receiver);\n\ntemplate <typename TValue>\nstd::optional<Try<TValue>> receiverGetValue(Receiver<TValue>& receiver);\n\ntemplate <typename TValue>\nstd::pair<detail::ChannelBridgePtr<TValue>, detail::ReceiverQueue<TValue>>\nreceiverUnbuffer(Receiver<TValue>&& receiver);\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Channel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/Synchronized.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/experimental/channels/detail/ChannelBridge.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\ntemplate <typename TValue>\nChannelBridgePtr<TValue>& senderGetBridge(Sender<TValue>& sender) {\n  return sender.bridge_;\n}\n\ntemplate <typename TValue>\nbool receiverWait(\n    Receiver<TValue>& receiver, detail::IChannelCallback* callback) {\n  if (!receiver.buffer_.empty()) {\n    return false;\n  }\n  return receiver.bridge_->receiverWait(callback);\n}\n\ntemplate <typename TValue>\ndetail::IChannelCallback* cancelReceiverWait(Receiver<TValue>& receiver) {\n  return receiver.bridge_->cancelReceiverWait();\n}\n\ntemplate <typename TValue>\nstd::optional<Try<TValue>> receiverGetValue(Receiver<TValue>& receiver) {\n  if (receiver.buffer_.empty()) {\n    receiver.buffer_ = receiver.bridge_->receiverGetValues();\n    if (receiver.buffer_.empty()) {\n      return std::nullopt;\n    }\n  }\n  auto result = std::move(receiver.buffer_.front());\n  receiver.buffer_.pop();\n  return result;\n}\n\ntemplate <typename TValue>\nstd::pair<detail::ChannelBridgePtr<TValue>, detail::ReceiverQueue<TValue>>\nreceiverUnbuffer(Receiver<TValue>&& receiver) {\n  return std::make_pair(\n      std::move(receiver.bridge_), std::move(receiver.buffer_));\n}\n\n} // namespace detail\n\ntemplate <typename TValue>\nclass Receiver<TValue>::Waiter : public detail::IChannelCallback {\n public:\n  Waiter(\n      Receiver<TValue>* receiver,\n      folly::CancellationToken cancelToken,\n      bool closeOnCancel)\n      : state_(State{.receiver = receiver}),\n        cancelCallback_(\n            makeCancellationCallback(std::move(cancelToken), closeOnCancel)) {}\n\n  bool await_ready() const noexcept {\n    // We are ready immediately if the receiver is either cancelled or closed.\n    return state_.withRLock([&](const State& state) {\n      return state.cancelled || !state.receiver;\n    });\n  }\n\n  bool await_suspend(\n      folly::coro::coroutine_handle<> awaitingCoroutine) noexcept {\n    return state_.withWLock([&](State& state) {\n      if (state.cancelled || !state.receiver ||\n          !receiverWait(*state.receiver, this)) {\n        // We will not suspend at all if the receiver is either cancelled or\n        // closed.\n        return false;\n      }\n      state.awaitingCoroutine = awaitingCoroutine;\n      return true;\n    });\n  }\n\n  std::optional<TValue> await_resume() {\n    auto result = getResult();\n    if (!result.hasValue() && !result.hasException()) {\n      return std::nullopt;\n    }\n    return std::move(result.value());\n  }\n\n  // FIXME: The default implementation of `value_or_error_or_stopped` will\n  // convert the `getResult()` branch below that returns an empty `Try` into an\n  // error state.  That's because the normal `folly::coro` contract does not\n  // allow producing an empty `Try`.  Therefore, any future\n  // `await_resume_result()` implementation should investigate the intended\n  // semantics of this `getResult()` logic, and provide better handling:\n  //\n  //   if (!state.receiver) {\n  //     return Try<TValue>();\n  //   }\n  //\n  // Also, after D73731681 (rich_error), supporting `await_resume_result()`\n  // will be also motivated by better perf for signaling cancellation.\n  Try<TValue> await_resume_try() { return getResult(); }\n\n protected:\n  struct State {\n    Receiver<TValue>* receiver;\n    folly::coro::coroutine_handle<> awaitingCoroutine{};\n    bool cancelled{false};\n  };\n\n  std::unique_ptr<folly::CancellationCallback> makeCancellationCallback(\n      folly::CancellationToken cancelToken, bool closeOnCancel) {\n    if (!cancelToken.canBeCancelled()) {\n      return nullptr;\n    }\n    return std::make_unique<folly::CancellationCallback>(\n        std::move(cancelToken), [this, closeOnCancel] {\n          auto receiver = state_.withWLock([&](State& state) {\n            state.cancelled = true;\n            return std::exchange(state.receiver, nullptr);\n          });\n          if (!receiver) {\n            return;\n          }\n          if (closeOnCancel) {\n            std::move(*receiver).cancel();\n          } else {\n            auto* callback = detail::cancelReceiverWait(*receiver);\n            if (callback) {\n              callback->canceled(nullptr);\n            }\n          }\n        });\n  }\n\n  void consume(detail::ChannelBridgeBase*) override { resume(); }\n\n  void canceled(detail::ChannelBridgeBase*) override { resume(); }\n\n  void resume() {\n    auto awaitingCoroutine = state_.withWLock([&](State& state) {\n      return std::exchange(state.awaitingCoroutine, nullptr);\n    });\n    awaitingCoroutine.resume();\n  }\n\n  Try<TValue> getResult() {\n    cancelCallback_.reset();\n    return state_.withWLock([&](State& state) {\n      if (state.cancelled) {\n        return Try<TValue>(\n            folly::make_exception_wrapper<folly::OperationCancelled>());\n      }\n      if (!state.receiver) {\n        return Try<TValue>();\n      }\n      auto result =\n          std::move(detail::receiverGetValue(*state.receiver).value());\n      if (!result.hasValue()) {\n        std::move(*state.receiver).cancel();\n        state.receiver = nullptr;\n      }\n      return result;\n    });\n  }\n\n  folly::Synchronized<State> state_;\n  std::unique_ptr<folly::CancellationCallback> cancelCallback_;\n};\n\ntemplate <typename TValue>\nstruct Receiver<TValue>::NextSemiAwaitable {\n public:\n  explicit NextSemiAwaitable(\n      Receiver<TValue>* receiver,\n      bool closeOnCancel,\n      std::optional<folly::CancellationToken> cancelToken = std::nullopt)\n      : receiver_(receiver),\n        closeOnCancel_(closeOnCancel),\n        cancelToken_(std::move(cancelToken)) {}\n\n  [[nodiscard]] Waiter operator co_await() {\n    return Waiter(\n        receiver_,\n        cancelToken_.value_or(folly::CancellationToken()),\n        closeOnCancel_);\n  }\n\n  friend NextSemiAwaitable co_withCancellation(\n      folly::CancellationToken cancelToken, NextSemiAwaitable&& awaitable) {\n    if (awaitable.cancelToken_.has_value()) {\n      return std::move(awaitable);\n    }\n    return NextSemiAwaitable(\n        awaitable.receiver_, awaitable.closeOnCancel_, std::move(cancelToken));\n  }\n\n private:\n  Receiver<TValue>* receiver_;\n  bool closeOnCancel_;\n  std::optional<folly::CancellationToken> cancelToken_;\n};\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Channel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel-fwd.h>\n#include <folly/experimental/channels/detail/ChannelBridge.h>\n\nnamespace folly {\nnamespace channels {\n\n/*\n * A channel is a sender and receiver pair that allows one component to send\n * values to another. A sender and receiver pair is similar to an AsyncPipe and\n * AsyncGenerator pair. However, unlike AsyncPipe/AsyncGenerator, senders and\n * receivers can be used by memory-efficient higher level transformation\n * abstractions.\n *\n * Typical usage:\n *   auto [receiver, sender] = Channel<T>::create();\n *   sender.write(val1);\n *   auto val2 = co_await receiver.next();\n */\ntemplate <typename TValue>\nclass Channel {\n public:\n  /**\n   * Creates a new channel with a sender/receiver pair. The channel will be\n   * closed if the sender is destroyed, and will be cancelled if the receiver is\n   * destroyed.\n   */\n  static std::pair<Receiver<TValue>, Sender<TValue>> create() {\n    auto senderBridge = detail::ChannelBridge<TValue>::create();\n    auto receiverBridge = senderBridge->copy();\n    return std::make_pair(\n        Receiver<TValue>(std::move(receiverBridge)),\n        Sender<TValue>(std::move(senderBridge)));\n  }\n};\n\n/**\n * A sender sends values to be consumed by a receiver.\n */\ntemplate <typename TValue>\nclass Sender {\n public:\n  friend Channel<TValue>;\n  using ValueType = TValue;\n\n  Sender(Sender&& other) noexcept : bridge_(std::move(other.bridge_)) {}\n\n  Sender& operator=(Sender&& other) noexcept {\n    if (this == &other) {\n      return *this;\n    }\n\n    if (bridge_) {\n      std::move(*this).close();\n    }\n    bridge_ = std::move(other.bridge_);\n    return *this;\n  }\n\n  ~Sender() {\n    if (bridge_) {\n      std::move(*this).close();\n    }\n  }\n\n  /**\n   * Returns whether or not this sender instance is valid. This will return\n   * false if the sender was closed or moved away.\n   */\n  explicit operator bool() const { return bridge_ != nullptr; }\n\n  /**\n   * Writes a value into the pipe.\n   */\n  template <typename U = TValue>\n  void write(U&& element) {\n    if (!bridge_->isSenderClosed()) {\n      bridge_->senderPush(std::forward<U>(element));\n    }\n  }\n\n  /**\n   * Closes the pipe without an exception.\n   */\n  void close() && {\n    if (!bridge_->isSenderClosed()) {\n      bridge_->senderClose();\n    }\n    bridge_ = nullptr;\n  }\n\n  /**\n   * Closes the pipe with an exception.\n   */\n  void close(exception_wrapper exception) && {\n    if (!bridge_->isSenderClosed()) {\n      bridge_->senderClose(std::move(exception));\n    }\n    bridge_ = nullptr;\n  }\n\n  /**\n   * Returns whether or not the corresponding receiver has been cancelled or\n   * destroyed.\n   */\n  bool isReceiverCancelled() {\n    if (bridge_->isSenderClosed()) {\n      return true;\n    }\n    auto values = bridge_->senderGetValues();\n    if (!values.empty()) {\n      bridge_->senderClose();\n      return true;\n    }\n    return false;\n  }\n\n private:\n  friend detail::ChannelBridgePtr<TValue>& detail::senderGetBridge<>(\n      Sender<TValue>&);\n\n  explicit Sender(detail::ChannelBridgePtr<TValue> bridge)\n      : bridge_(std::move(bridge)) {}\n\n  detail::ChannelBridgePtr<TValue> bridge_;\n};\n\n/**\n * A receiver that receives values sent by a sender. There are several ways that\n * a receiver can be consumed:\n *\n * 1. Call co_await receiver.next() to get the next value. See the docstring of\n *    next() for more details. This is the easiest way to consume the values\n *    from a receiver, but it is also the most expensive memory-wise (as it\n *    creates a long-lived coroutine frame). This is typically used in scenarios\n *    where O(1) channels are being consumed (and therefore coroutine memory\n *    overhead is negligible).\n *\n * 2. Call consumeChannelWithCallback to get a callback when each value comes\n *    in. See ConsumeChannel.h for more details. This uses less memory than\n *    #1, as it only needs to allocate coroutine frames when processing values\n *    (rather than always having such frames allocated when waiting for values).\n *\n * 3. Use MergeChannel in folly/experimental/channels/MergeChannel.h.\n *    This construct allows you to consume the merged output of a dynamically\n *    changing set of receivers. This is the cheapest way to consume the output\n *    of a large number of receivers. It is useful when the consumer wants to\n *    process all values from all receivers sequentially.\n *\n * 4. Use ChannelProcessor in folly/experimental/channels/ChannelProcessor.h.\n *    This construct allows you to consume a dynamically changing set of\n *    receivers in parallel.\n *\n * 5. A receiver may also be passed to other framework primitives that consume\n *    the receiver (such as transform). As with options 2-4, these primitives\n *    do not require coroutine frames to be allocated when waiting for values.\n */\ntemplate <typename TValue>\nclass Receiver {\n  class Waiter;\n  struct NextSemiAwaitable;\n\n public:\n  friend Channel<TValue>;\n  using ValueType = TValue;\n\n  Receiver() {}\n\n  Receiver(Receiver&& other) noexcept\n      : bridge_(std::move(other.bridge_)), buffer_(std::move(other.buffer_)) {}\n\n  Receiver& operator=(Receiver&& other) noexcept {\n    if (this == &other) {\n      return *this;\n    }\n    if (bridge_ != nullptr) {\n      std::move(*this).cancel();\n    }\n    bridge_ = std::move(other.bridge_);\n    buffer_ = std::move(other.buffer_);\n    return *this;\n  }\n\n  ~Receiver() {\n    if (bridge_ != nullptr) {\n      std::move(*this).cancel();\n    }\n  }\n\n  /**\n   * Returns whether or not this receiver instance is valid. This will return\n   * false if the receiver was cancelled or moved away.\n   */\n  explicit operator bool() const { return bridge_ != nullptr; }\n\n  /**\n   * Returns the next value sent by a sender. The behavior similar to the\n   * behavior of next() on folly::coro::AsyncGenerator<TValue>.\n   *\n   * When closeOnCancel is true, if the returned semi-awaitable is cancelled,\n   * the underlying channel will be closed. No more values will be received,\n   * even if they were sent by the sender. This matches the behavior of\n   * folly::coro::AsyncGenerator.\n   *\n   * When closeOnCancel is false, cancelling the returned semi-awaitable will\n   * not close the underlying channel. Instead, it will just cancel the next()\n   * operation. This means that the caller can call next() again and continue\n   * to receive values sent by the sender.\n   *\n   * If consumed directly with co_await, next() will return an std::optional:\n   *\n   *    std::optional<TValue> value = co_await receiver.next();\n   *\n   *    - If a value is sent, the std::optional will contain the value.\n   *    - If the channel is closed by the sender with no exception, the optional\n   *        will be empty.\n   *    - If the channel is closed by the sender with an exception, next() will\n   *        throw the exception.\n   *    - If the next() call was cancelled, next() will throw an exception of\n   *        type folly::OperationCancelled.\n   *\n   * If consumed with folly::coro::co_awaitTry, this will return a Try:\n   *\n   *    Try<TValue> value = co_await folly::coro::co_awaitTry(\n   *        receiver.next());\n   *\n   *    - If a value is sent, the Try will contain the value.\n   *    - If the channel is closed by the sender with no exception, the try will\n   *        be empty (with no value or exception).\n   *    - If the channel is closed by the sender with an exception, the try will\n   *        contain the exception.\n   *    - If the next() call was cancelled, the try will contain an exception of\n   *        type folly::OperationCancelled.\n   */\n  NextSemiAwaitable next(bool closeOnCancel = true) {\n    return NextSemiAwaitable(*this ? this : nullptr, closeOnCancel);\n  }\n\n  /**\n   * Cancels this receiver. If the receiver is currently being consumed, the\n   * consumer will receive a folly::OperationCancelled exception.\n   */\n  void cancel() && {\n    bridge_->receiverCancel();\n    bridge_ = nullptr;\n    buffer_.clear();\n  }\n\n private:\n  explicit Receiver(detail::ChannelBridgePtr<TValue> bridge)\n      : bridge_(std::move(bridge)) {}\n\n  friend bool detail::receiverWait<>(\n      Receiver<TValue>&, detail::IChannelCallback*);\n\n  friend detail::IChannelCallback* detail::cancelReceiverWait<>(\n      Receiver<TValue>&);\n\n  friend std::optional<Try<TValue>> detail::receiverGetValue<>(\n      Receiver<TValue>&);\n\n  friend std::\n      pair<detail::ChannelBridgePtr<TValue>, detail::ReceiverQueue<TValue>>\n      detail::receiverUnbuffer<>(Receiver<TValue>&& receiver);\n\n  detail::ChannelBridgePtr<TValue> bridge_;\n  detail::ReceiverQueue<TValue> buffer_;\n};\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/Channel-inl.h>\n"
  },
  {
    "path": "folly/channels/ChannelCallbackHandle.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/IntrusiveList.h>\n#include <folly/ScopeGuard.h>\n#include <folly/channels/Channel.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\nclass ChannelCallbackProcessor : public IChannelCallback {\n public:\n  virtual void onHandleDestroyed() = 0;\n};\n} // namespace detail\n\n/**\n * A callback handle for a consumption operation on a channel. The consumption\n * operation will be cancelled when this handle is destroyed.\n */\nclass ChannelCallbackHandle {\n public:\n  ChannelCallbackHandle() : processor_(nullptr) {}\n\n  explicit ChannelCallbackHandle(detail::ChannelCallbackProcessor* processor)\n      : processor_(processor) {}\n\n  ~ChannelCallbackHandle() {\n    if (processor_) {\n      processor_->onHandleDestroyed();\n    }\n  }\n\n  ChannelCallbackHandle(ChannelCallbackHandle&& other) noexcept\n      : processor_(std::exchange(other.processor_, nullptr)) {}\n\n  ChannelCallbackHandle& operator=(ChannelCallbackHandle&& other) {\n    if (&other == this) {\n      return *this;\n    }\n    reset();\n    processor_ = std::exchange(other.processor_, nullptr);\n    return *this;\n  }\n\n  void reset() {\n    if (processor_) {\n      processor_->onHandleDestroyed();\n      processor_ = nullptr;\n    }\n  }\n\n private:\n  detail::ChannelCallbackProcessor* processor_;\n};\n\nnamespace detail {\n\n/**\n * A wrapper around a ChannelCallbackHandle that belongs to an intrusive linked\n * list. When the holder is destroyed, the object will automatically be unlinked\n * from the linked list that it is in (if any).\n */\nstruct ChannelCallbackHandleHolder {\n  explicit ChannelCallbackHandleHolder(ChannelCallbackHandle _handle)\n      : handle(std::move(_handle)) {}\n\n  ChannelCallbackHandleHolder(ChannelCallbackHandleHolder&& other) noexcept\n      : handle(std::move(other.handle)) {\n    hook.swap_nodes(other.hook);\n  }\n\n  ChannelCallbackHandleHolder& operator=(\n      ChannelCallbackHandleHolder&& other) noexcept {\n    if (&other == this) {\n      return *this;\n    }\n    handle = std::move(other.handle);\n    hook.unlink();\n    hook.swap_nodes(other.hook);\n    return *this;\n  }\n\n  void requestCancellation() { handle.reset(); }\n\n  ChannelCallbackHandle handle;\n  folly::IntrusiveListHook hook;\n};\n\ntemplate <typename TValue, typename OnNextFunc>\nclass ChannelCallbackProcessorImplWithList;\n} // namespace detail\n\n/**\n * A list of channel callback handles. When consumeChannelWithCallback is\n * invoked with a list, a cancellation handle is automatically added to the list\n * for the consumption operation. Similarly, when a consumption operation is\n * completed, the handle is automatically removed from the lists.\n *\n * If the list still has any cancellation handles remaining when the list is\n * destroyed, cancellation is triggered for each handle in the list.\n *\n * This list is not thread safe.\n */\nclass ChannelCallbackHandleList {\n public:\n  ChannelCallbackHandleList() {}\n\n  ChannelCallbackHandleList(ChannelCallbackHandleList&& other) noexcept {\n    holders_.swap(other.holders_);\n  }\n\n  ChannelCallbackHandleList& operator=(\n      ChannelCallbackHandleList&& other) noexcept {\n    if (&other == this) {\n      return *this;\n    }\n    holders_.swap(other.holders_);\n    return *this;\n  }\n\n  ~ChannelCallbackHandleList() { clear(); }\n\n  void clear() {\n    for (auto& holder : holders_) {\n      holder.requestCancellation();\n    }\n    holders_.clear();\n  }\n\n private:\n  template <typename TValue, typename OnNextFunc>\n  friend class detail::ChannelCallbackProcessorImplWithList;\n\n  void add(detail::ChannelCallbackHandleHolder& holder) {\n    holders_.push_back(holder);\n  }\n\n  using ChannelCallbackHandleListImpl = folly::IntrusiveList<\n      detail::ChannelCallbackHandleHolder,\n      &detail::ChannelCallbackHandleHolder::hook>;\n\n  ChannelCallbackHandleListImpl holders_;\n};\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/ChannelProcessor-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fmt/format.h>\n#include <folly/channels/ChannelProcessor.h>\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/MergeChannel.h>\n#include <folly/channels/Transform.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/experimental/channels/detail/IntrusivePtr.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\ntemplate <typename KeyType>\nclass ChannelProcessorImpl {\n public:\n  ChannelProcessorImpl(\n      std::vector<folly::Executor::KeepAlive<folly::SequencedExecutor>>\n          executors,\n      std::shared_ptr<folly::channels::RateLimiter> rateLimiter,\n      MergeChannel<KeyType, Unit> mergeChannel,\n      Receiver<MergeChannelEvent<KeyType, Unit>> mergeChannelReceiver)\n      : implState_(\n            make_intrusive<ImplState>(\n                std::move(executors), std::move(rateLimiter))),\n        channels_(std::move(mergeChannel)),\n        handle_(consumeChannelWithCallback(\n            std::move(mergeChannelReceiver),\n            implState_->executors[0],\n            [](Try<MergeChannelEvent<KeyType, Unit>>)\n                -> folly::coro::Task<bool> {\n              // Do nothing\n              co_return true;\n            })) {}\n\n  template <typename ReceiverType, typename OnUpdateFunc>\n  void addChannel(KeyType key, ReceiverType receiver, OnUpdateFunc onUpdate) {\n    using InputValueType = typename ReceiverType::ValueType;\n    channels_.removeReceiver(key);\n    channels_.addNewReceiver(\n        std::move(key),\n        transform(\n            std::move(receiver),\n            Transformer<InputValueType, OnUpdateFunc>(\n                implState_, std::move(onUpdate))));\n  }\n\n  template <\n      typename InitializeArg,\n      typename InitializeFunc,\n      typename OnUpdateFunc>\n  void addResumableChannelWithState(\n      KeyType key,\n      InitializeArg initializeArg,\n      InitializeFunc initialize,\n      OnUpdateFunc onUpdate) {\n    addResumableChannelWithState(\n        std::move(key),\n        std::move(initializeArg),\n        std::move(initialize),\n        std::move(onUpdate),\n        NoChannelState());\n  }\n\n  template <\n      typename InitializeArg,\n      typename InitializeFunc,\n      typename OnUpdateFunc,\n      typename ChannelState>\n  void addResumableChannelWithState(\n      KeyType key,\n      InitializeArg initializeArg,\n      InitializeFunc initialize,\n      OnUpdateFunc onUpdate,\n      ChannelState channelState) {\n    using ReceiverType = typename decltype(initialize(\n        std::move(initializeArg), channelState))::StorageType;\n    using InputValueType = typename ReceiverType::ValueType;\n    channels_.removeReceiver(key);\n    channels_.addNewReceiver(\n        std::move(key),\n        resumableTransform(\n            std::move(initializeArg),\n            ResumableTransformer<\n                InitializeArg,\n                InputValueType,\n                InitializeFunc,\n                OnUpdateFunc,\n                ChannelState>(\n                implState_,\n                std::move(initialize),\n                std::move(onUpdate),\n                std::move(channelState))));\n  }\n\n  void removeChannel(const KeyType& keyType) {\n    channels_.removeReceiver(keyType);\n  }\n\n private:\n  struct NoChannelState {};\n\n  template <\n      typename Function,\n      typename ReturnType = typename invoke_result_t<Function>::StorageType>\n  static folly::coro::Task<ReturnType> catchNonCoroException(Function func) {\n    auto result = folly::makeTryWith(std::move(func));\n    if (result.hasException()) {\n      return folly::coro::makeErrorTask<ReturnType>(\n          std::move(result.exception()));\n    } else {\n      return std::move(result.value());\n    }\n  }\n\n  struct ImplState : public IntrusivePtrBase<ImplState> {\n    ImplState(\n        std::vector<folly::Executor::KeepAlive<folly::SequencedExecutor>>\n            _executors,\n        std::shared_ptr<folly::channels::RateLimiter> _rateLimiter)\n        : executors(std::move(_executors)),\n          rateLimiter(std::move(_rateLimiter)) {}\n\n    std::vector<folly::Executor::KeepAlive<folly::SequencedExecutor>> executors;\n    std::shared_ptr<folly::channels::RateLimiter> rateLimiter;\n  };\n\n  template <typename InputValueType, typename OnUpdateFunc>\n  class Transformer : public std::tuple<OnUpdateFunc> {\n   public:\n    Transformer(intrusive_ptr<ImplState> implState, OnUpdateFunc onUpdate)\n        : std::tuple<OnUpdateFunc>(std::move(onUpdate)),\n          implState_(std::move(implState)) {}\n\n    folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() {\n      return implState_->executors\n          [std::hash<decltype(this)>()(this) % implState_->executors.size()];\n    }\n\n    std::shared_ptr<folly::channels::RateLimiter> getRateLimiter() {\n      return implState_->rateLimiter;\n    }\n\n    folly::coro::AsyncGenerator<Unit&&> transformValue(\n        Try<InputValueType> value) {\n      auto result = co_await folly::coro::co_awaitTry(catchNonCoroException(\n          [&] { return std::get<OnUpdateFunc>(*this)(std::move(value)); }));\n      if (result.template hasException<folly::OperationCancelled>() ||\n          result.template hasException<OnClosedException>()) {\n        co_yield folly::coro::co_error(OnClosedException());\n      } else if (result.hasException()) {\n        LOG(FATAL) << fmt::format(\n            \"Encountered exception from callback when consuming channel of \"\n            \"type {}: {}\",\n            typeid(InputValueType).name(),\n            result.exception().what());\n      }\n    }\n\n   private:\n    intrusive_ptr<ImplState> implState_;\n  };\n\n  template <\n      typename InitializeArg,\n      typename InputValueType,\n      typename InitializeFunc,\n      typename OnUpdateFunc,\n      typename ChannelState>\n  class ResumableTransformer\n      : public std::tuple<InitializeFunc, OnUpdateFunc, ChannelState> {\n   public:\n    ResumableTransformer(\n        intrusive_ptr<ImplState> implState,\n        InitializeFunc initialize,\n        OnUpdateFunc onUpdate,\n        ChannelState channelState)\n        : std::tuple<InitializeFunc, OnUpdateFunc, ChannelState>(\n              std::move(initialize),\n              std::move(onUpdate),\n              std::move(channelState)),\n          implState_(std::move(implState)) {}\n\n    folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() {\n      return implState_->executors\n          [std::hash<decltype(this)>()(this) % implState_->executors.size()];\n    }\n\n    std::shared_ptr<folly::channels::RateLimiter> getRateLimiter() {\n      return implState_->rateLimiter;\n    }\n\n    folly::coro::Task<std::pair<std::vector<Unit>, Receiver<InputValueType>>>\n    initializeTransform(InitializeArg initializeArg) {\n      auto result = co_await folly::coro::co_awaitTry(\n          initialize(std::move(initializeArg)));\n      if (result.template hasException<folly::OperationCancelled>() ||\n          result.template hasException<OnClosedException>()) {\n        co_yield folly::coro::co_error(OnClosedException());\n      } else if (result.hasException()) {\n        LOG(FATAL) << fmt::format(\n            \"Encountered exception from callback when consuming channel of \"\n            \"type {}: {}\",\n            typeid(InputValueType).name(),\n            result.exception().what());\n      }\n      co_return std::make_pair(std::vector<Unit>(), std::move(result.value()));\n    }\n\n    folly::coro::AsyncGenerator<Unit&&> transformValue(\n        Try<InputValueType> value) {\n      auto result =\n          co_await folly::coro::co_awaitTry(onUpdate(std::move(value)));\n      if (result\n              .template hasException<ReinitializeException<InitializeArg>>()) {\n        co_yield folly::coro::co_error(std::move(result.exception()));\n      } else if (\n          result.template hasException<folly::OperationCancelled>() ||\n          result.template hasException<OnClosedException>()) {\n        co_yield folly::coro::co_error(OnClosedException());\n      } else if (result.hasException()) {\n        LOG(FATAL) << fmt::format(\n            \"Encountered exception from callback when consuming channel of \"\n            \"type {}: {}\",\n            typeid(InputValueType).name(),\n            result.exception().what());\n      }\n    }\n\n   private:\n    folly::coro::Task<Receiver<InputValueType>> initialize(\n        InitializeArg initializeArg) {\n      if constexpr (std::is_same_v<ChannelState, NoChannelState>) {\n        co_return co_await catchNonCoroException([&] {\n          return std::get<InitializeFunc>(*this)(std::move(initializeArg));\n        });\n      } else {\n        co_return co_await catchNonCoroException([&] {\n          return std::get<InitializeFunc>(*this)(\n              std::move(initializeArg), std::get<ChannelState>(*this));\n        });\n      }\n    }\n\n    folly::coro::Task<void> onUpdate(Try<InputValueType> value) {\n      if constexpr (std::is_same_v<ChannelState, NoChannelState>) {\n        co_await catchNonCoroException([&] {\n          return std::get<OnUpdateFunc>(*this)(std::move(value));\n        });\n      } else {\n        co_await catchNonCoroException([&] {\n          return std::get<OnUpdateFunc>(*this)(\n              std::move(value), std::get<ChannelState>(*this));\n        });\n      }\n    }\n\n    intrusive_ptr<ImplState> implState_;\n  };\n\n  intrusive_ptr<ImplState> implState_;\n  MergeChannel<KeyType, Unit> channels_;\n  ChannelCallbackHandle handle_;\n};\n} // namespace detail\n\ntemplate <typename KeyType>\nChannelProcessor<KeyType>::ChannelProcessor(\n    std::unique_ptr<detail::ChannelProcessorImpl<KeyType>> impl)\n    : impl_(std::move(impl)) {}\n\ntemplate <typename KeyType>\nChannelProcessor<KeyType>::operator bool() const {\n  return impl_ != nullptr;\n}\n\ntemplate <typename KeyType>\ntemplate <typename ReceiverType, typename OnUpdateFunc>\nvoid ChannelProcessor<KeyType>::addChannel(\n    KeyType key, ReceiverType receiver, OnUpdateFunc onUpdate) {\n  impl_->addChannel(std::move(key), std::move(receiver), std::move(onUpdate));\n}\n\ntemplate <typename KeyType>\ntemplate <\n    typename InitializeArg,\n    typename InitializeFunc,\n    typename OnUpdateFunc>\nvoid ChannelProcessor<KeyType>::addResumableChannel(\n    KeyType key,\n    InitializeArg initializeArg,\n    InitializeFunc initialize,\n    OnUpdateFunc onUpdate) {\n  impl_->addResumableChannel(\n      std::move(key),\n      std::move(initializeArg),\n      std::move(initialize),\n      std::move(onUpdate));\n}\n\ntemplate <typename KeyType>\ntemplate <\n    typename InitializeArg,\n    typename InitializeFunc,\n    typename OnUpdateFunc,\n    typename ChannelState>\nvoid ChannelProcessor<KeyType>::addResumableChannelWithState(\n    KeyType key,\n    InitializeArg initializeArg,\n    InitializeFunc initialize,\n    OnUpdateFunc onUpdate,\n    ChannelState channelState) {\n  impl_->addResumableChannelWithState(\n      std::move(key),\n      std::move(initializeArg),\n      std::move(initialize),\n      std::move(onUpdate),\n      std::move(channelState));\n}\n\ntemplate <typename KeyType>\nvoid ChannelProcessor<KeyType>::removeChannel(const KeyType& keyType) {\n  impl_->removeChannel(keyType);\n}\n\ntemplate <typename KeyType>\nvoid ChannelProcessor<KeyType>::close() && {\n  impl_.reset();\n}\n\ntemplate <typename KeyType>\nChannelProcessor<KeyType> createChannelProcessor(\n    std::vector<folly::Executor::KeepAlive<folly::SequencedExecutor>> executors,\n    std::shared_ptr<RateLimiter> rateLimiter) {\n  CHECK_GT(executors.size(), 0);\n  auto [mergeChannelReceiver, mergeChannel] =\n      createMergeChannel<KeyType, Unit>(executors[0]);\n  return ChannelProcessor<KeyType>(\n      std::make_unique<detail::ChannelProcessorImpl<KeyType>>(\n          std::move(executors),\n          std::move(rateLimiter),\n          std::move(mergeChannel),\n          std::move(mergeChannelReceiver)));\n}\n\ntemplate <typename KeyType>\nChannelProcessor<KeyType> createChannelProcessor(\n    folly::Executor::KeepAlive<> executor,\n    std::shared_ptr<RateLimiter> rateLimiter,\n    size_t numSequencedExecutors) {\n  CHECK_GT(numSequencedExecutors, 0);\n  auto executors =\n      std::vector<folly::Executor::KeepAlive<folly::SequencedExecutor>>();\n  for (size_t i = 0; i < numSequencedExecutors; i++) {\n    executors.push_back(folly::SerialExecutor::create(executor));\n  }\n  return createChannelProcessor<KeyType>(\n      std::move(executors), std::move(rateLimiter));\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/ChannelProcessor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n#include <folly/channels/RateLimiter.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\ntemplate <typename KeyType>\nclass ChannelProcessorImpl;\n}\n\n/**\n * This object allows for memory-efficient processing of values many channels.\n *\n * A channel is added with a unique key and a callback. The callback will be\n * called for every value pushed to the receiver.\n *\n * A resumable channel can also be added. A resumable channel involves two\n * callbacks. An initialization callback is called to get the receiver, and the\n * update callback is called on every update (as for a normal channel). The\n * update callback can throw a ReinitializeException at any time, which will\n * trigger the initialize callback to re-run.\n *\n * Values for a given channel are processed until one of the following occurs:\n *     1. The channel is closed\n *     2. The channel callback throws an OnClosedException\n *     3. The channel callback throws a folly::OperationCancelled exception.\n *     4. The channel is removed with a call to removeChannel.\n *\n * If a channel is removed with removeChannel, processing will eventually stop\n * for that channel. This will not necessarily happen immediately.\n *\n * If a channel is added for an already existing key, the previous channel for\n * that key will be removed and processing will eventually stop.\n *\n * Processing for all channels will run on the user-provided executor. For any\n * particular channel, all processing will happen sequentially. For any two\n * distinct channels, processing may happen in parallel (subject to any\n * constraints of the provided executor).\n */\ntemplate <typename KeyType>\nclass ChannelProcessor {\n public:\n  explicit ChannelProcessor(\n      std::unique_ptr<detail::ChannelProcessorImpl<KeyType>> impl);\n\n  /**\n   * Returns whether this ChannelProcessor is a valid object.\n   */\n  explicit operator bool() const;\n\n  /**\n   * Processes a channel with a given key and callback. For a receiver of type\n   * Receiver<InputValueType>, the callback must accept a single parameter of\n   * type Try<InputValueType>, and return a void task. If the callback\n   * throws an exception of type OperationCancelled or OnClosedException, the\n   * channel will be removed. Any other exception thrown by the callback will\n   * terminate the process.\n   *\n   * If there is an existing channel with the same key, it will be removed\n   * before the new channel is added. The old channel's callback can check the\n   * current cancellation token to see if it was removed while processing\n   * values. See removeChannel for more details.\n   *\n   * Example:\n   *\n   *   // Example function that returns a receiver for a given entity\n   *   Receiver<int> subscribe(const std::string& entity);\n   *\n   *   // Example function that returns an executor\n   *   folly::Executor::KeepAlive<> getExecutor();\n   *\n   *   auto channelProcessor = createChannelProcessor<std::string>(\n   *       getExecutor());\n   *\n   *   channelProcessor.addChannel(\n   *       \"abc\",\n   *       subscribe(\"abc\"),\n   *       [](Try<int> value) -> folly::coro::Task<void> {\n   *         LOG(INFO) << fmt::format(\"Received value {}\", *value);\n   *         co_return;\n   *       });\n   */\n  template <typename ReceiverType, typename OnUpdateFunc>\n  void addChannel(KeyType key, ReceiverType receiver, OnUpdateFunc onUpdate);\n\n  /**\n   * Processing a resumable channel involves two callbacks. The initialization\n   * callback accepts an initialization argument of a user-defined type, and\n   * must return a folly::coro::Task<Receiver<InputValueType>>. The onUpdate\n   * callback accepts a Try<InputValueType>, and returns a void task. The\n   * onUpdate callback can throw a ReinitializeException<InitializeArg> at any\n   * time, which will trigger the initialize function to be run again. In\n   * addition, if either callback throws an exception of type OperationCancelled\n   * or OnClosedException, the channel will be removed. Any other exception\n   * thrown by either callback will terminate the process.\n   *\n   * If there is an existing channel with the same key, it will be removed\n   * before the new channel is added. The old channel's callbacks can check the\n   * current cancellation token to see if it was removed while processing\n   * values. See removeChannel for more details.\n   *\n   * Example:\n   *\n   *   struct InitializeArg {\n   *     std::string param;\n   *   }\n   *\n   *   // Example function that returns a receiver for a given entity\n   *   Receiver<int> subscribe(const InitializeArg& initializeArg);\n   *\n   *   // Example function that returns an executor\n   *   folly::Executor::KeepAlive<> getExecutor();\n   *\n   *   auto channelProcessor = createChannelProcessor<std::string>(\n   *       getExecutor());\n   *\n   *   channelProcessor.addResumableChannel(\n   *       \"abc\",\n   *       InitializeArg({\"param\"}),\n   *       [](InitializeArg initializeArg) -> folly::coro::Task<Receiver<int>> {\n   *         co_return subscribe(initializeArg);\n   *       },\n   *       [](Try<int> value) -> folly::coro::Task<void> {\n   *         if (*value == -1) {\n   *           throw ReinitializeException(InitializeArg({\"param\"}));\n   *         }\n   *         LOG(INFO) << fmt::format(\"Received value {}\", *value);\n   *         co_return;\n   *       });\n   */\n  template <\n      typename InitializeArg,\n      typename InitializeFunc,\n      typename OnUpdateFunc>\n  void addResumableChannel(\n      KeyType key,\n      InitializeArg initializeArg,\n      InitializeFunc initialize,\n      OnUpdateFunc onUpdate);\n\n  /*\n   * This is similar to addResumableChannel. However, it allows a user-provided\n   * state object to be stored with the channel. That state object will be\n   * passed to both callbacks, and will be destructed when the channel is\n   * removed or closed.\n   *\n   * * Example:\n   *\n   *   struct InitializeArg {\n   *     std::string param;\n   *   }\n   *\n   *   struct State {\n   *     int prevValue{-1};\n   *   }\n   *\n   *   // Example function that returns a receiver for a given entity\n   *   Receiver<int> subscribe(const InitializeArg& initializeArg);\n   *\n   *   // Example function that returns an executor\n   *   folly::Executor::KeepAlive<> getExecutor();\n   *\n   *   auto channelProcessor = createChannelProcessor<std::string>(\n   *       getExecutor());\n   *\n   *   channelProcessor.addResumableChannelWithState(\n   *       \"abc\",\n   *       InitializeArg({\"param\"}),\n   *       [](InitializeArg initializeArg, State& state)\n   *                        -> folly::coro::Task<Receiver<int>> {\n   *         co_return subscribe(initializeArg);\n   *       },\n   *       [](Try<int> value, State& state) -> folly::coro::Task<void> {\n   *         if (*value == -1) {\n   *           throw ReinitializeException(InitializeArg({\"param\"}));\n   *         }\n   *         LOG(INFO) << fmt::format(\n   *             \"Received value {}. Previous: {}.\", *value, state.prevValue);\n   *         state.prevValue = *value;\n   *         co_return;\n   *       },\n   *       State());\n   */\n  template <\n      typename InitializeArg,\n      typename InitializeFunc,\n      typename OnUpdateFunc,\n      typename ChannelState>\n  void addResumableChannelWithState(\n      KeyType key,\n      InitializeArg initializeArg,\n      InitializeFunc initialize,\n      OnUpdateFunc onUpdate,\n      ChannelState channelState);\n\n  /**\n   * Removes the channel with the given key, if such a channel exists. The\n   * channel will be asynchronously removed, so the channels' callback may\n   * still receive some values after this call. The callback can detect whether\n   * or not the channel was removed by examining its current cancellation token.\n   */\n  void removeChannel(const KeyType& keyType);\n\n  /**\n   * Closes all channels being processed, causing all processing to eventually\n   * stop. Calling this function will make the object invalid.\n   */\n  void close() &&;\n\n private:\n  std::unique_ptr<detail::ChannelProcessorImpl<KeyType>> impl_;\n};\n\n/**\n * Creates a new channel processor.\n */\ntemplate <typename KeyType>\nChannelProcessor<KeyType> createChannelProcessor(\n    folly::Executor::KeepAlive<> executor,\n    std::shared_ptr<RateLimiter> rateLimiter = nullptr,\n    size_t numSequencedExecutors = 1);\n\n/**\n * Creates a new channel processor.\n */\ntemplate <typename KeyType>\nChannelProcessor<KeyType> createChannelProcessor(\n    std::vector<folly::Executor::KeepAlive<folly::SequencedExecutor>> executors,\n    std::shared_ptr<RateLimiter> rateLimiter = nullptr);\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/ChannelProcessor-inl.h>\n"
  },
  {
    "path": "folly/channels/ConsumeChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fmt/format.h>\n#include <folly/Executor.h>\n#include <folly/Format.h>\n#include <folly/IntrusiveList.h>\n#include <folly/ScopeGuard.h>\n#include <folly/channels/Channel.h>\n#include <folly/channels/ChannelCallbackHandle.h>\n#include <folly/coro/Task.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\n\ntemplate <typename TValue, typename OnNextFunc>\nclass ChannelCallbackProcessorImpl : public ChannelCallbackProcessor {\n public:\n  ChannelCallbackProcessorImpl(\n      ChannelBridgePtr<TValue> receiver,\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      OnNextFunc onNext)\n      : receiver_(std::move(receiver)),\n        executor_(std::move(executor)),\n        onNext_(std::move(onNext)),\n        cancelSource_(folly::CancellationSource::invalid()) {}\n\n  void start(std::optional<detail::ReceiverQueue<TValue>> buffer) {\n    co_withExecutor(\n        executor_,\n        runCoroutineWithCancellation(\n            processAllAvailableValues(std::move(buffer))))\n        .start();\n  }\n\n private:\n  /**\n   * Called when the handle is destroyed.\n   */\n  void onHandleDestroyed() override {\n    executor_->add([=, this]() { processHandleDestroyed(); });\n  }\n\n  /**\n   * Called when the channel we are listening to has an update.\n   */\n  void consume(ChannelBridgeBase*) override {\n    co_withExecutor(\n        executor_, runCoroutineWithCancellation(processAllAvailableValues()))\n        .start();\n  }\n\n  /**\n   * Called after we cancelled the input channel (which happens after the handle\n   * is destroyed).\n   */\n  void canceled(ChannelBridgeBase*) override {\n    co_withExecutor(\n        executor_,\n        runCoroutineWithCancellation(\n            processReceiverCancelled(true /* fromHandleDestruction */)))\n        .start();\n  }\n\n  /**\n   * Processes all available values from the input receiver (starting from the\n   * provided buffer, if present).\n   *\n   * If a value was received indicating that the input channel has been closed,\n   * we will process cancellation for the input receiver.\n   */\n  folly::coro::Task<void> processAllAvailableValues(\n      std::optional<ReceiverQueue<TValue>> buffer = std::nullopt) {\n    bool closed = buffer.has_value()\n        ? !co_await processValues(std::move(buffer.value()))\n        : false;\n    while (!closed) {\n      if (receiver_->receiverWait(this)) {\n        // There are no more values available right now, but more values may\n        // come in the future. We will stop processing for now, until we\n        // re-start processing when the consume() callback is fired.\n        break;\n      }\n      auto values = receiver_->receiverGetValues();\n      CHECK(!values.empty());\n      closed = !co_await processValues(std::move(values));\n    }\n    if (closed) {\n      // The input receiver was closed.\n      receiver_->receiverCancel();\n      co_await processReceiverCancelled(false /* fromHandleDestruction */);\n    }\n  }\n\n  /**\n   * Processes values from the channel. Returns false if the channel has been\n   * closed, so the caller can stop processing values from it.\n   */\n  folly::coro::Task<bool> processValues(ReceiverQueue<TValue> values) {\n    auto cancelToken = co_await folly::coro::co_current_cancellation_token;\n    while (!values.empty()) {\n      if (cancelToken.isCancellationRequested()) {\n        co_return true;\n      }\n      auto result = std::move(values.front());\n      values.pop();\n      bool closed = !result.hasValue();\n      if (!co_await callCallback(std::move(result))) {\n        closed = true;\n      }\n      if (closed) {\n        co_return false;\n      }\n      co_await folly::coro::co_reschedule_on_current_executor;\n    }\n    co_return true;\n  }\n\n  /**\n   * Process cancellation of the input receiver.\n   *\n   * @param fromHandleDestruction: Whether the cancellation was prompted by the\n   *    handle being destroyed. If true, we will call the user's callback with\n   *    a folly::OperationCancelled exception. This will be false if the\n   *    cancellation was prompted by the closure of the channel.\n   */\n  folly::coro::Task<void> processReceiverCancelled(bool fromHandleDestruction) {\n    CHECK_EQ(getReceiverState(), ChannelState::CancellationTriggered);\n    receiver_ = nullptr;\n    if (fromHandleDestruction) {\n      co_await callCallback(\n          Try<TValue>(\n              folly::make_exception_wrapper<folly::OperationCancelled>()));\n    }\n    maybeDelete();\n  }\n\n  /**\n   * Processes the destruction of the handle.\n   */\n  void processHandleDestroyed() {\n    CHECK(!handleDestroyed_);\n    handleDestroyed_ = true;\n    cancelSource_.requestCancellation();\n    if (getReceiverState() == ChannelState::Active) {\n      receiver_->receiverCancel();\n    }\n    maybeDelete();\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * receiver and the handle.\n   */\n  void maybeDelete() {\n    if (getReceiverState() == ChannelState::CancellationProcessed &&\n        handleDestroyed_) {\n      delete this;\n    }\n  }\n\n  /**\n   * Calls the user's callback with the given result.\n   */\n  folly::coro::Task<bool> callCallback(Try<TValue> result) {\n    auto retVal = co_await folly::coro::co_awaitTry(onNext_(std::move(result)));\n    if (retVal.template hasException<folly::OperationCancelled>()) {\n      co_return false;\n    } else if (retVal.hasException()) {\n      LOG(FATAL) << fmt::format(\n          \"Encountered exception from callback when consuming channel of \"\n          \"type {}: {}\",\n          typeid(TValue).name(),\n          retVal.exception().what());\n    }\n    co_return retVal.value();\n  }\n\n  /**\n   * Runs the given coroutine while listening for cancellation triggered by the\n   * handle's destruction.\n   */\n  folly::coro::Task<void> runCoroutineWithCancellation(\n      folly::coro::Task<void> task) {\n    cancelSource_ = folly::CancellationSource();\n    if (handleDestroyed_) {\n      // The handle was already destroyed before we even started the coroutine.\n      // Request cancellation so that the user's callback knows to stop quickly.\n      cancelSource_.requestCancellation();\n    }\n    auto token = cancelSource_.getToken();\n    auto retVal = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(token, std::move(task)));\n    CHECK(!retVal.hasException()) << fmt::format(\n        \"Unexpected exception when running coroutine: {}\",\n        retVal.exception().what());\n    if (!token.isCancellationRequested()) {\n      cancelSource_ = folly::CancellationSource::invalid();\n    }\n  }\n\n  ChannelState getReceiverState() {\n    return detail::getReceiverState(receiver_.get());\n  }\n\n  ChannelBridgePtr<TValue> receiver_;\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  OnNextFunc onNext_;\n  folly::CancellationSource cancelSource_;\n  bool handleDestroyed_{false};\n};\n} // namespace detail\n\ntemplate <\n    typename TReceiver,\n    typename OnNextFunc,\n    typename TValue,\n    std::enable_if_t<\n        std::is_constructible_v<\n            folly::Function<folly::coro::Task<bool>(Try<TValue>)>,\n            OnNextFunc>,\n        int>>\nChannelCallbackHandle consumeChannelWithCallback(\n    TReceiver receiver,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    OnNextFunc onNext) {\n  detail::ChannelCallbackProcessorImpl<TValue, OnNextFunc>* processor = nullptr;\n  auto [unbufferedReceiver, buffer] =\n      detail::receiverUnbuffer(std::move(receiver));\n  processor = new detail::ChannelCallbackProcessorImpl<TValue, OnNextFunc>(\n      std::move(unbufferedReceiver), std::move(executor), std::move(onNext));\n  processor->start(std::move(buffer));\n  return ChannelCallbackHandle(processor);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/ConsumeChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n#include <folly/IntrusiveList.h>\n#include <folly/channels/Channel.h>\n#include <folly/channels/ChannelCallbackHandle.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\n/**\n * This function takes a Receiver, and consumes updates from that receiver with\n * a callback.\n *\n * This function returns a ChannelCallbackHandle. On destruction of this handle,\n * the callback will receive a try containing an exception of type\n * folly::OperationCancelled. If an active callback is running at the time the\n * cancellation request is received, cancellation will be requested on the\n * ambient cancellation token of the callback.\n *\n * The callback is run for each received value on the given executor. A try\n * is passed to the callback with the result:\n *\n *    - If a value is sent, the Try will contain the value.\n *    - If the channel is closed by the sender with no exception, the try will\n *          be empty (with no value or exception).\n *    - If the channel is closed by the sender with an exception, the try will\n *          contain the exception.\n *    - If the channel was cancelled (by the destruction of the returned\n *          handle), the try will contain an exception of type\n *          folly::OperationCancelled.\n *\n * If the callback returns false or throws a folly::OperationCancelled\n * exception, the channel will be cancelled and no further values will be\n * received.\n */\ntemplate <\n    typename TReceiver,\n    typename OnNextFunc,\n    typename TValue = typename TReceiver::ValueType,\n    std::enable_if_t<\n        std::is_constructible_v<\n            folly::Function<folly::coro::Task<bool>(Try<TValue>)>,\n            OnNextFunc>,\n        int> = 0>\nChannelCallbackHandle consumeChannelWithCallback(\n    TReceiver receiver,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    OnNextFunc onNext);\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/ConsumeChannel-inl.h>\n"
  },
  {
    "path": "folly/channels/FanoutChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/channels/FanoutSender.h>\n#include <folly/container/F14Set.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename ValueType, typename ContextType>\nFanoutChannel<ValueType, ContextType>::FanoutChannel(TProcessor* processor)\n    : processor_(processor) {}\n\ntemplate <typename ValueType, typename ContextType>\nFanoutChannel<ValueType, ContextType>::FanoutChannel(\n    FanoutChannel&& other) noexcept\n    : processor_(std::exchange(other.processor_, nullptr)) {}\n\ntemplate <typename ValueType, typename ContextType>\nFanoutChannel<ValueType, ContextType>&\nFanoutChannel<ValueType, ContextType>::operator=(\n    FanoutChannel&& other) noexcept {\n  if (&other == this) {\n    return *this;\n  }\n  if (processor_) {\n    std::move(*this).close();\n  }\n  processor_ = std::exchange(other.processor_, nullptr);\n  return *this;\n}\n\ntemplate <typename ValueType, typename ContextType>\nFanoutChannel<ValueType, ContextType>::~FanoutChannel() {\n  if (processor_ != nullptr) {\n    std::move(*this).close(exception_wrapper());\n  }\n}\n\ntemplate <typename ValueType, typename ContextType>\nFanoutChannel<ValueType, ContextType>::operator bool() const {\n  return processor_ != nullptr;\n}\n\ntemplate <typename ValueType, typename ContextType>\nReceiver<ValueType> FanoutChannel<ValueType, ContextType>::subscribe(\n    folly::Function<std::vector<ValueType>(const ContextType&)>\n        getInitialValues) {\n  return processor_->subscribe(std::move(getInitialValues));\n}\n\ntemplate <typename ValueType, typename ContextType>\nbool FanoutChannel<ValueType, ContextType>::anySubscribers() const {\n  return processor_->anySubscribers();\n}\n\ntemplate <typename ValueType, typename ContextType>\nvoid FanoutChannel<ValueType, ContextType>::closeSubscribers(\n    exception_wrapper ex) {\n  processor_->closeSubscribers(\n      ex ? detail::CloseResult(std::move(ex)) : detail::CloseResult());\n}\n\ntemplate <typename ValueType, typename ContextType>\nvoid FanoutChannel<ValueType, ContextType>::close(exception_wrapper ex) && {\n  processor_->destroyHandle(\n      ex ? detail::CloseResult(std::move(ex)) : detail::CloseResult());\n  processor_ = nullptr;\n}\n\ntemplate <typename ValueType, typename ContextType>\nContextType FanoutChannel<ValueType, ContextType>::getContext() const {\n  return processor_->getContext();\n}\n\nnamespace detail {\n\ntemplate <typename ValueType, typename ContextType>\nclass IFanoutChannelProcessor : public IChannelCallback {\n public:\n  virtual Receiver<ValueType> subscribe(\n      folly::Function<std::vector<ValueType>(const ContextType&)>\n          getInitialValues) = 0;\n\n  virtual bool anySubscribers() = 0;\n\n  virtual void closeSubscribers(CloseResult closeResult) = 0;\n\n  virtual void destroyHandle(CloseResult closeResult) = 0;\n\n  virtual ContextType getContext() = 0;\n};\n\n/**\n * This object fans out values from the input receiver to all output receivers.\n * The lifetime of this object is described by the following state machine.\n *\n * The input receiver can be in one of three conceptual states: Active,\n * CancellationTriggered, or CancellationProcessed (removed). When the input\n * receiver reaches the CancellationProcessed state AND the user's FanoutChannel\n * object is deleted, this object is deleted.\n *\n * When an input receiver receives a value indicating that the channel has\n * been closed, the state of the input receiver transitions from Active directly\n * to CancellationProcessed (and this object will be deleted once the user\n * destroys their FanoutChannel object).\n *\n * When the user destroys their FanoutChannel object, the state of the input\n * receiver transitions from Active to CancellationTriggered. This object will\n * then be deleted once the input receiver transitions to the\n * CancellationProcessed state.\n */\ntemplate <typename ValueType, typename ContextType>\nclass FanoutChannelProcessor\n    : public IFanoutChannelProcessor<ValueType, ContextType> {\n private:\n  struct State {\n    State(ContextType _context) : context(std::move(_context)) {}\n\n    ChannelState getReceiverState() {\n      return detail::getReceiverState(receiver.get());\n    }\n\n    ChannelBridgePtr<ValueType> receiver;\n    FanoutSender<ValueType> fanoutSender;\n    ContextType context;\n    bool handleDeleted{false};\n  };\n\n  using WLockedStatePtr = typename folly::Synchronized<State>::WLockedPtr;\n\n public:\n  explicit FanoutChannelProcessor(\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      ContextType context)\n      : executor_(std::move(executor)), state_(std::move(context)) {}\n\n  /**\n   * Starts fanning out values from the input receiver to all output receivers.\n   *\n   * @param inputReceiver: The input receiver to fan out values from.\n   */\n  void start(Receiver<ValueType> inputReceiver) {\n    auto state = state_.wlock();\n    auto [unbufferedInputReceiver, buffer] =\n        detail::receiverUnbuffer(std::move(inputReceiver));\n    state->receiver = std::move(unbufferedInputReceiver);\n\n    // Start processing new values that come in from the input receiver.\n    processAllAvailableValues(state, std::move(buffer));\n  }\n\n  /**\n   * Returns a new output receiver that will receive all values from the input\n   * receiver. If a getInitialValues parameter is provided, it will be executed\n   * to determine the set of initial values that will (only) go to the new input\n   * receiver.\n   */\n  Receiver<ValueType> subscribe(\n      folly::Function<std::vector<ValueType>(const ContextType&)>\n          getInitialValues) override {\n    auto state = state_.wlock();\n    auto initialValues = getInitialValues\n        ? getInitialValues(state->context)\n        : std::vector<ValueType>();\n    if (!state->receiver) {\n      auto [receiver, sender] = Channel<ValueType>::create();\n      for (auto&& value : initialValues) {\n        sender.write(std::move(value));\n      }\n      std::move(sender).close();\n      return std::move(receiver);\n    }\n    return state->fanoutSender.subscribe(std::move(initialValues));\n  }\n\n  /**\n   * Closes all subscribers without closing the fanout channel.\n   */\n  void closeSubscribers(CloseResult closeResult) override {\n    auto state = state_.wlock();\n    std::move(state->fanoutSender)\n        .close(\n            closeResult.exception.has_value()\n                ? closeResult.exception.value()\n                : exception_wrapper());\n  }\n\n  /**\n   * This is called when the user's FanoutChannel object has been destroyed.\n   */\n  void destroyHandle(CloseResult closeResult) override {\n    auto state = state_.wlock();\n    processHandleDestroyed(state, std::move(closeResult));\n  }\n\n  /**\n   * Returns whether this fanout channel has any output receivers.\n   */\n  bool anySubscribers() override {\n    return state_.wlock()->fanoutSender.anySubscribers();\n  }\n\n  ContextType getContext() override { return state_.rlock()->context; }\n\n private:\n  /**\n   * Called when one of the channels we are listening to has an update (either\n   * a value from the input receiver or a cancellation from an output receiver).\n   */\n  void consume(ChannelBridgeBase*) override {\n    executor_->add([=, this]() {\n      // One or more values are now available from the input receiver.\n      auto state = state_.wlock();\n      CHECK_NE(state->getReceiverState(), ChannelState::CancellationProcessed);\n      processAllAvailableValues(state);\n    });\n  }\n\n  void canceled(ChannelBridgeBase*) override {\n    executor_->add([=, this]() {\n      // We previously cancelled this input receiver, due to the destruction of\n      // the handle. Process the cancellation for this input receiver.\n      auto state = state_.wlock();\n      processReceiverCancelled(state, CloseResult());\n    });\n  }\n\n  /**\n   * Processes all available values from the input receiver (starting from the\n   * provided buffer, if present).\n   *\n   * If an value was received indicating that the input channel has been closed\n   * (or if the transform function indicated that channel should be closed), we\n   * will process cancellation for the input receiver.\n   */\n  void processAllAvailableValues(\n      WLockedStatePtr& state,\n      std::optional<ReceiverQueue<ValueType>> buffer = std::nullopt) {\n    auto closeResult = state->receiver->isReceiverCancelled()\n        ? CloseResult()\n        : (buffer.has_value() ? processValues(state, std::move(buffer.value()))\n                              : std::nullopt);\n    while (!closeResult.has_value()) {\n      if (state->receiver->receiverWait(this)) {\n        // There are no more values available right now. We will stop processing\n        // until the channel fires the consume() callback (indicating that more\n        // values are available).\n        break;\n      }\n      auto values = state->receiver->receiverGetValues();\n      CHECK(!values.empty());\n      closeResult = processValues(state, std::move(values));\n    }\n    if (closeResult.has_value()) {\n      // The receiver received a value indicating channel closure.\n      state->receiver->receiverCancel();\n      processReceiverCancelled(state, std::move(closeResult.value()));\n    }\n  }\n\n  /**\n   * Processes the given set of values for the input receiver. Returns a\n   * CloseResult if channel was closed, so the caller can stop attempting to\n   * process values from it.\n   */\n  std::optional<CloseResult> processValues(\n      WLockedStatePtr& state, ReceiverQueue<ValueType> values) {\n    while (!values.empty()) {\n      auto inputResult = std::move(values.front());\n      values.pop();\n      if (inputResult.hasValue()) {\n        // We have received a normal value from the input receiver. Write it to\n        // all output senders.\n        state->context.update(\n            inputResult.value(), state->fanoutSender.numSubscribers());\n        state->fanoutSender.write(std::move(inputResult.value()));\n      } else {\n        // The input receiver was closed.\n        return inputResult.hasException()\n            ? CloseResult(std::move(inputResult.exception()))\n            : CloseResult();\n      }\n    }\n    return std::nullopt;\n  }\n\n  /**\n   * Processes the cancellation of the input receiver. We will close all senders\n   * with the exception received from the input receiver (if any).\n   */\n  void processReceiverCancelled(\n      WLockedStatePtr& state, CloseResult closeResult) {\n    CHECK_EQ(state->getReceiverState(), ChannelState::CancellationTriggered);\n    state->receiver = nullptr;\n    std::move(state->fanoutSender)\n        .close(\n            closeResult.exception.has_value()\n                ? closeResult.exception.value()\n                : exception_wrapper());\n    maybeDelete(state);\n  }\n\n  /**\n   * Processes the destruction of the user's FanoutChannel object.  We will\n   * cancel the receiver and trigger cancellation for all senders not already\n   * cancelled.\n   */\n  void processHandleDestroyed(WLockedStatePtr& state, CloseResult closeResult) {\n    state->handleDeleted = true;\n    if (state->getReceiverState() == ChannelState::Active) {\n      state->receiver->receiverCancel();\n    }\n    std::move(state->fanoutSender)\n        .close(\n            closeResult.exception.has_value()\n                ? closeResult.exception.value()\n                : exception_wrapper());\n    maybeDelete(state);\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * receiver and all senders, and if the user's FanoutChannel object was\n   * destroyed.\n   */\n  void maybeDelete(WLockedStatePtr& state) {\n    if (state->getReceiverState() == ChannelState::CancellationProcessed &&\n        state->handleDeleted) {\n      state.unlock();\n      delete this;\n    }\n  }\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  folly::Synchronized<State> state_;\n};\n} // namespace detail\n\ntemplate <typename TReceiver, typename ValueType, typename ContextType>\nFanoutChannel<ValueType, ContextType> createFanoutChannel(\n    TReceiver inputReceiver,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    ContextType context) {\n  auto* processor = new detail::FanoutChannelProcessor<ValueType, ContextType>(\n      std::move(executor), std::move(context));\n  processor->start(std::move(inputReceiver));\n  return FanoutChannel<ValueType, ContextType>(processor);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/FanoutChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\ntemplate <typename ValueType, typename ContextType>\nclass IFanoutChannelProcessor;\n}\n\ntemplate <typename TValue>\nstruct NoContext {\n  void update(const TValue&, size_t) {}\n};\n\n/**\n * A fanout channel allows fanning out updates from a single input receiver\n * to multiple output receivers.\n *\n * When a new output receiver is added, an optional function will be run that\n * computes a set of initial values. These initial values will only be sent to\n * the new receiver.\n *\n * FanoutChannel allows specifying an optional context object. If specified, the\n * context object must have a void update function:\n *\n *   void update(const ValueType&);\n *\n * This update function will be called on every value from the input receiver.\n * The context will be passed to the getInitialUpdates argument to subscribe,\n * allowing for initial updates to depend on the context. This facilitates the\n * common pattern of letting new subscribers know where they are starting from.\n *\n * Example without context:\n *\n *   // Function that returns a receiver:\n *   Receiver<int> getInputReceiver();\n *\n *   // Function that returns an executor\n *   folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *   auto fanoutChannel = createFanoutChannel(getReceiver(), getExecutor());\n *   auto receiver1 = fanoutChannel.subscribe();\n *   auto receiver2 = fanoutChannel.subscribe();\n *   auto receiver3 = fanoutChannel.subscribe([]{ return {1, 2, 3}; });\n *\n * Example with context:\n *\n *   struct Context {\n *     int lastValue{-1};\n *\n *     void update(const int& value) {\n *       lastValue = value;\n *     }\n *   };\n *\n *   auto fanoutChannel =\n *       createFanoutChannel(getReceiver(), getExecutor(), Context());\n *   auto receiver1 = fanoutChannel.subscribe(\n *       [](const Context& context) { return {context.latestValue}; });\n *   auto receiver2 = fanoutChannel.subscribe(\n *       [](const Context& context) { return {context.latestValue}; });\n *   std::move(fanoutChannel).close();\n */\ntemplate <typename ValueType, typename ContextType = NoContext<ValueType>>\nclass FanoutChannel {\n  using TProcessor = detail::IFanoutChannelProcessor<ValueType, ContextType>;\n\n public:\n  explicit FanoutChannel(TProcessor* processor);\n  FanoutChannel(FanoutChannel&& other) noexcept;\n  FanoutChannel& operator=(FanoutChannel&& other) noexcept;\n  ~FanoutChannel();\n\n  /**\n   * Returns whether this FanoutChannel is a valid object.\n   */\n  explicit operator bool() const;\n\n  /**\n   * Returns a new output receiver that will receive all values from the input\n   * receiver.\n   *\n   * If a getInitialValues parameter is provided, it will be executed\n   * to determine the set of initial values that will (only) go to the new input\n   * receiver. Other functions on this class should not be called from within\n   * getInitialValues, or a deadlock will occur.\n   */\n  Receiver<ValueType> subscribe(\n      folly::Function<std::vector<ValueType>(const ContextType&)>\n          getInitialValues = {});\n\n  /**\n   * Returns whether this fanout channel has any subscribers.\n   */\n  bool anySubscribers() const;\n\n  /**\n   * Closes all subscribers, without closing the fanout channel. New subscribers\n   * can be added after this call.\n   */\n  void closeSubscribers(exception_wrapper ex = exception_wrapper());\n\n  /**\n   * Closes the fanout channel.\n   */\n  void close(exception_wrapper ex = exception_wrapper()) &&;\n\n  /**\n   * Get the context\n   */\n  ContextType getContext() const;\n\n private:\n  TProcessor* processor_;\n};\n\n/**\n * Creates a new fanout channel that fans out updates from an input receiver.\n */\ntemplate <\n    typename ReceiverType,\n    typename ValueType = typename ReceiverType::ValueType,\n    typename ContextType = NoContext<typename ReceiverType::ValueType>>\nFanoutChannel<ValueType, ContextType> createFanoutChannel(\n    ReceiverType inputReceiver,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    ContextType context = ContextType());\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/FanoutChannel-inl.h>\n"
  },
  {
    "path": "folly/channels/FanoutSender-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/container/F14Set.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\ntemplate <typename ValueType>\nclass FanoutSenderProcessor : public IChannelCallback {\n private:\n  struct State {\n    folly::F14FastSet<ChannelBridge<ValueType>*> senders_;\n    bool handleDestroyed_{false};\n  };\n\n  using WLockedStatePtr = typename folly::Synchronized<State>::WLockedPtr;\n\n public:\n  /**\n   * Subscribes with an already-created sender.\n   */\n  void addSender(detail::ChannelBridgePtr<ValueType> sender) {\n    auto state = state_.wlock();\n    sender->senderWait(this);\n    state->senders_.insert(sender.release());\n  }\n\n  /**\n   * Sends the given value to all corresponding receivers.\n   */\n  template <typename U = ValueType>\n  void write(U&& element) {\n    auto state = state_.wlock();\n    for (auto* sender : state->senders_) {\n      sender->senderPush(element);\n    }\n  }\n\n  /**\n   * This is called when the user's FanoutSender object has been destroyed.\n   */\n  void destroyHandle(CloseResult closeResult) {\n    processHandleDestroyed(state_.wlock(), std::move(closeResult));\n  }\n\n  /**\n   * Returns whether this fanout channel has any output receivers.\n   */\n  size_t numSubscribers() const { return state_.rlock()->senders_.size(); }\n\n  std::pair<bool, ChannelBridgePtr<ValueType>>\n  stealSenderAndDestorySelfIfSingle() {\n    auto state = state_.wlock();\n    if (state->senders_.empty()) {\n      // There are no remaining senders. We will destroy ourselves.\n      state->handleDestroyed_ = true;\n      maybeDelete(std::move(state));\n      return std::make_pair(true, ChannelBridgePtr<ValueType>());\n    } else if (state->senders_.size() == 1) {\n      // There is one remaining sender.\n      auto* sender = *state->senders_.begin();\n      auto* callback = sender->cancelSenderWait();\n      if (callback) {\n        // We successfully cancelled the callback, so we can now destroy\n        // ourselves and return the sender.\n        state->senders_.clear();\n        state->handleDestroyed_ = true;\n        maybeDelete(std::move(state));\n        return std::make_pair(true, ChannelBridgePtr<ValueType>(sender));\n      } else {\n        // We failed to cancel the callback. This means that another thread is\n        // invoking the callback by calling consume(), letting us know that the\n        // corresponding receiver was deleted. The callback will start running\n        // once we release the lock, so we will let the callback delete the\n        // sender (and then destroy ourselves).\n        return std::make_pair(true, ChannelBridgePtr<ValueType>());\n      }\n    } else {\n      // There is more than one sender. Do not destroy ourselves.\n      return std::make_pair(false, ChannelBridgePtr<ValueType>());\n    }\n  }\n\n  static ChannelState getSenderState(ChannelBridge<ValueType>* sender) {\n    return detail::getSenderState(sender);\n  }\n\n private:\n  /**\n   * Called when receiving a cancellation from an output receiver.\n   */\n  void consume(ChannelBridgeBase* bridge) override {\n    // The consumer of an output receiver has stopped consuming.\n    auto state = state_.wlock();\n    auto* sender = static_cast<ChannelBridge<ValueType>*>(bridge);\n    CHECK_NE(getSenderState(sender), ChannelState::CancellationProcessed);\n    sender->senderClose();\n    processSenderCancelled(std::move(state), sender);\n  }\n\n  void canceled(ChannelBridgeBase*) override {\n    // We cancel the callback before we close the sender explicitly, so this\n    // should never be hit.\n    CHECK(false);\n  }\n\n  /**\n   * Processes the cancellation of a sender (indicating that the consumer of\n   * the corresponding output receiver has stopped consuming).\n   */\n  void processSenderCancelled(\n      WLockedStatePtr state, ChannelBridge<ValueType>* sender) {\n    CHECK_EQ(getSenderState(sender), ChannelState::CancellationTriggered);\n    state->senders_.erase(sender);\n    deleteSender(sender);\n    maybeDelete(std::move(state));\n  }\n\n  /**\n   * Processes the destruction of the user's FanoutChannel object.  We will\n   * cancel the receiver and trigger cancellation for all senders not already\n   * cancelled.\n   */\n  void processHandleDestroyed(WLockedStatePtr state, CloseResult closeResult) {\n    CHECK(!state->handleDestroyed_);\n    state->handleDestroyed_ = true;\n    auto senders = state->senders_;\n    for (auto* sender : senders) {\n      auto* callback = sender->cancelSenderWait();\n      if (closeResult.exception.has_value()) {\n        sender->senderClose(closeResult.exception.value());\n      } else {\n        sender->senderClose();\n      }\n      if (callback) {\n        // We successfully cancelled the callback, so we can now delete the\n        // sender.\n        CHECK_EQ(callback, this);\n        state->senders_.erase(sender);\n        deleteSender(sender);\n      } else {\n        // We failed to cancel the callback. This means that another thread is\n        // invoking the callback by calling consume(), letting us know that the\n        // corresponding receiver was deleted. The callback will start running\n        // once we release the lock, so we will let the callback delete the\n        // sender.\n      }\n    }\n    maybeDelete(std::move(state));\n  }\n\n  /*\n   * Deletes the given sender.\n   */\n  void deleteSender(ChannelBridge<ValueType>* sender) {\n    (ChannelBridgePtr<ValueType>(sender));\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * receiver and all senders, and if the user's FanoutChannel object was\n   * destroyed.\n   */\n  void maybeDelete(WLockedStatePtr state) {\n    if (state->senders_.empty() && state->handleDestroyed_) {\n      state.unlock();\n      delete this;\n    }\n  }\n\n  folly::Synchronized<State> state_;\n};\n} // namespace detail\n\ntemplate <typename ValueType>\nFanoutSender<ValueType>::FanoutSender()\n    : senders_(static_cast<detail::ChannelBridge<ValueType>*>(nullptr)) {}\n\ntemplate <typename ValueType>\nFanoutSender<ValueType>::FanoutSender(FanoutSender&& other) noexcept\n    : senders_(std::move(other.senders_)) {}\n\ntemplate <typename ValueType>\nFanoutSender<ValueType>& FanoutSender<ValueType>::operator=(\n    FanoutSender&& other) noexcept {\n  if (&other == this) {\n    return *this;\n  }\n  std::move(*this).close();\n  senders_ = std::move(senders_);\n  return *this;\n}\n\ntemplate <typename ValueType>\nFanoutSender<ValueType>::~FanoutSender() {\n  std::move(*this).close();\n}\n\ntemplate <typename ValueType>\nReceiver<ValueType> FanoutSender<ValueType>::subscribe(\n    std::vector<ValueType> initialValues) {\n  auto [newReceiver, newSender] = Channel<ValueType>::create();\n  for (auto&& initialValue : initialValues) {\n    newSender.write(std::move(initialValue));\n  }\n  subscribe(std::move(newSender));\n  return std::move(newReceiver);\n}\n\ntemplate <typename ValueType>\nvoid FanoutSender<ValueType>::subscribe(Sender<ValueType> newSender) {\n  clearSendersWithClosedReceivers();\n  if (!anySubscribersImpl()) {\n    // There are currently no output receivers. Store the new output receiver.\n    senders_.set(detail::senderGetBridge(newSender).release());\n  } else if (!hasProcessor()) {\n    // There is currently exactly one output receiver. Convert to a processor.\n    auto* processor = new detail::FanoutSenderProcessor<ValueType>();\n    processor->addSender(\n        detail::ChannelBridgePtr<ValueType>(getSingleSender()));\n    processor->addSender(std::move(detail::senderGetBridge(newSender)));\n    senders_.set(processor);\n  } else {\n    // There are currently more than one output receivers. Add the new receiver\n    // to the existing processor.\n    auto* processor = getProcessor();\n    processor->addSender(std::move(detail::senderGetBridge(newSender)));\n  }\n}\n\ntemplate <typename ValueType>\nbool FanoutSender<ValueType>::anySubscribers() const {\n  clearSendersWithClosedReceivers();\n  return anySubscribersImpl();\n}\n\ntemplate <typename ValueType>\nstd::uint64_t FanoutSender<ValueType>::numSubscribers() const {\n  clearSendersWithClosedReceivers();\n  if (!anySubscribersImpl()) {\n    return 0;\n  } else if (!hasProcessor()) {\n    return 1;\n  } else {\n    return getProcessor()->numSubscribers();\n  }\n}\n\ntemplate <typename ValueType>\ntemplate <typename U>\nvoid FanoutSender<ValueType>::write(U&& element) {\n  clearSendersWithClosedReceivers();\n  if (!anySubscribersImpl()) {\n    // There are currently no output receivers to write to.\n    return;\n  } else if (!hasProcessor()) {\n    // There is exactly one output receiver. Write the value to that receiver.\n    getSingleSender()->senderPush(std::forward<U>(element));\n  } else {\n    getProcessor()->write(std::forward<U>(element));\n  }\n}\n\ntemplate <typename ValueType>\nvoid FanoutSender<ValueType>::close(exception_wrapper ex) && {\n  clearSendersWithClosedReceivers();\n  if (!anySubscribersImpl()) {\n    // There are no output receivers to close.\n    return;\n  } else if (!hasProcessor()) {\n    // There is exactly one output receiver to close.\n    if (ex) {\n      getSingleSender()->senderClose(ex);\n    } else {\n      getSingleSender()->senderClose();\n    }\n    // Delete the output receiver.\n    (detail::ChannelBridgePtr<ValueType>(getSingleSender()));\n    senders_.set(static_cast<detail::ChannelBridge<ValueType>*>(nullptr));\n  } else {\n    // There is more than one output receiver to close.\n    getProcessor()->destroyHandle(\n        ex ? detail::CloseResult(std::move(ex)) : detail::CloseResult());\n    senders_.set(static_cast<detail::ChannelBridge<ValueType>*>(nullptr));\n  }\n}\n\ntemplate <typename ValueType>\nbool FanoutSender<ValueType>::anySubscribersImpl() const {\n  return hasProcessor() || getSingleSender() != nullptr;\n}\n\ntemplate <typename ValueType>\nbool FanoutSender<ValueType>::hasProcessor() const {\n  return senders_.index() == 1;\n}\n\ntemplate <typename ValueType>\ndetail::ChannelBridge<ValueType>* FanoutSender<ValueType>::getSingleSender()\n    const {\n  return senders_.get(folly::tag_t<detail::ChannelBridge<ValueType>>{});\n}\n\ntemplate <typename ValueType>\ndetail::FanoutSenderProcessor<ValueType>*\nFanoutSender<ValueType>::getProcessor() const {\n  return senders_.get(folly::tag_t<detail::FanoutSenderProcessor<ValueType>>{});\n}\n\ntemplate <typename ValueType>\nvoid FanoutSender<ValueType>::clearSendersWithClosedReceivers() const {\n  if (hasProcessor()) {\n    auto [processorDestroyed, remainingSender] =\n        getProcessor()->stealSenderAndDestorySelfIfSingle();\n    if (processorDestroyed) {\n      if (remainingSender) {\n        senders_.set(remainingSender.release());\n      } else {\n        senders_.set(static_cast<detail::ChannelBridge<ValueType>*>(nullptr));\n      }\n    }\n  } else {\n    auto* bridge = getSingleSender();\n    if (bridge) {\n      // There is currently exactly one output receiver. Check to see if it has\n      // been cancelled.\n      auto values = bridge->senderGetValues();\n      if (!values.empty()) {\n        bridge->senderClose();\n        senders_.set(static_cast<detail::ChannelBridge<ValueType>*>(nullptr));\n        // Delete the output receiver.\n        (detail::ChannelBridgePtr<ValueType>(bridge));\n      }\n    }\n  }\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/FanoutSender.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/container/F14Set.h>\n#include <folly/experimental/channels/detail/PointerVariant.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\ntemplate <typename ValueType>\nclass FanoutSenderProcessor;\n}\n\n/**\n * A FanoutSender allows fanning out updates to multiple output receivers.\n * Values can be written as with a normal Sender. When there is only one output\n * receiver, the memory used by a FanoutSender (and the corresponding output\n * receiver) is the same as the memory used by a normal channel.\n *\n * When a new output receiver is added, an optional vector of initial values\n * can be provided. These initial values will only be sent to the new receiver.\n *\n * Memory used by closed receivers is reclaimed lazily (when iterating over\n * receivers).\n *\n * Example:\n *\n *  FanoutSender<int> fanoutSender;\n *  auto receiver1 = fanoutSender.subscribe();\n *  auto receiver2 = fanoutSender.subscribe();\n *  auto receiver3 = fanoutSender.subscribe({1, 2, 3});\n *  std::move(fanoutSender).close();\n */\ntemplate <typename ValueType>\nclass FanoutSender {\n public:\n  FanoutSender();\n  FanoutSender(FanoutSender&& other) noexcept;\n  FanoutSender& operator=(FanoutSender&& other) noexcept;\n  ~FanoutSender();\n\n  /**\n   * Returns a new output receiver that will receive all values written to the\n   * FanoutSender. If the initialValues parameter is provided, the given values\n   * will (only) go to the new output receiver.\n   */\n  Receiver<ValueType> subscribe(std::vector<ValueType> initialValues = {});\n\n  /**\n   * Subscribes with an already-created sender.\n   */\n  void subscribe(Sender<ValueType> sender);\n\n  /**\n   * Returns whether this fanout sender has any active output receivers.\n   */\n  bool anySubscribers() const;\n\n  /**\n   * Returns the number of output receivers for this fanout sender.\n   */\n  std::uint64_t numSubscribers() const;\n\n  /**\n   * Sends the given value to all corresponding receivers.\n   */\n  template <typename U = ValueType>\n  void write(U&& element);\n\n  /**\n   * Closes the fanout sender.\n   */\n  void close(exception_wrapper ex = exception_wrapper()) &&;\n\n private:\n  bool anySubscribersImpl() const;\n\n  bool hasProcessor() const;\n\n  detail::ChannelBridge<ValueType>* getSingleSender() const;\n\n  detail::FanoutSenderProcessor<ValueType>* getProcessor() const;\n\n  void clearSendersWithClosedReceivers() const;\n\n  mutable detail::PointerVariant<\n      detail::ChannelBridge<ValueType>,\n      detail::FanoutSenderProcessor<ValueType>>\n      senders_;\n};\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/FanoutSender-inl.h>\n"
  },
  {
    "path": "folly/channels/MaxConcurrentRateLimiter.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/MaxConcurrentRateLimiter.h>\n\nnamespace folly {\nnamespace channels {\n\nclass MaxConcurrentRateLimiter::Token : public RateLimiter::Token {\n public:\n  explicit Token(\n      std::shared_ptr<MaxConcurrentRateLimiter> maxConcurrentRateLimiter)\n      : maxConcurrentRateLimiter_{std::move(maxConcurrentRateLimiter)} {}\n\n  Token(Token&&) = default;\n  Token& operator=(Token&&) = default;\n  Token(const Token&) = delete;\n  Token& operator=(const Token&) = delete;\n\n  ~Token() override {\n    if (maxConcurrentRateLimiter_) {\n      maxConcurrentRateLimiter_->release();\n    }\n  }\n\n private:\n  std::shared_ptr<MaxConcurrentRateLimiter> maxConcurrentRateLimiter_;\n};\n\nstd::shared_ptr<MaxConcurrentRateLimiter> MaxConcurrentRateLimiter::create(\n    size_t maxConcurrent) {\n  return std::shared_ptr<MaxConcurrentRateLimiter>(\n      new MaxConcurrentRateLimiter(maxConcurrent));\n}\n\nMaxConcurrentRateLimiter::MaxConcurrentRateLimiter(size_t maxConcurrent)\n    : maxConcurrent_(maxConcurrent) {}\n\nvoid MaxConcurrentRateLimiter::executeWhenReady(\n    folly::Function<void(std::unique_ptr<RateLimiter::Token>)> func,\n    Executor::KeepAlive<SequencedExecutor> executor) {\n  auto state = state_.wlock();\n  if (state->running < maxConcurrent_) {\n    CHECK(state->queue.empty());\n    state->running++;\n    executor->add(\n        [func = std::move(func),\n         token = std::make_unique<MaxConcurrentRateLimiter::Token>(\n             std::static_pointer_cast<MaxConcurrentRateLimiter>(\n                 shared_from_this()))]() mutable { func(std::move(token)); });\n  } else {\n    state->queue.enqueue(QueueItem{std::move(func), std::move(executor)});\n  }\n}\n\nvoid MaxConcurrentRateLimiter::release() {\n  auto state = state_.wlock();\n  if (!state->queue.empty()) {\n    auto queueItem = state->queue.dequeue();\n    queueItem.executor->add(\n        [func = std::move(queueItem.func),\n         token = std::make_unique<MaxConcurrentRateLimiter::Token>(\n             std::static_pointer_cast<MaxConcurrentRateLimiter>(\n                 shared_from_this()))]() mutable { func(std::move(token)); });\n  } else {\n    state->running--;\n  }\n}\n\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/MaxConcurrentRateLimiter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Synchronized.h>\n#include <folly/channels/RateLimiter.h>\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\nclass MaxConcurrentRateLimiter : public RateLimiter {\n public:\n  static std::shared_ptr<MaxConcurrentRateLimiter> create(size_t maxConcurrent);\n\n  void executeWhenReady(\n      folly::Function<void(std::unique_ptr<Token>)> func,\n      Executor::KeepAlive<SequencedExecutor> executor) override;\n\n private:\n  class Token;\n  friend class Token;\n\n  explicit MaxConcurrentRateLimiter(size_t maxConcurrent);\n  void release();\n\n  struct QueueItem {\n    folly::Function<void(std::unique_ptr<Token>)> func;\n    Executor::KeepAlive<SequencedExecutor> executor;\n  };\n\n  struct State {\n    USPSCQueue<QueueItem, false /* MayBlock */, 6 /* LgSegmentSize */> queue;\n    size_t running{0};\n  };\n\n  const size_t maxConcurrent_;\n  folly::Synchronized<State> state_;\n};\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Merge-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/container/F14Set.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\n\n/**\n * This object does the merging of values from the input receiver to the output\n * receiver. It is not an object that the user is aware of or holds a pointer\n * to. The lifetime of this object is described by the following state machine.\n *\n * The sender and all receivers can be in one of three conceptual states:\n * Active, CancellationTriggered, or CancellationProcessed. When the sender and\n * all receivers reach the CancellationProcessed state, this object is deleted.\n *\n * When an input receiver receives a value indicating that the channel has been\n * closed, the state of that receiver transitions from Active directly to\n * CancellationProcessed.\n *\n * If this is the last receiver to be closed, or if the receiver closed with an\n * exception, the state of the sender and all other receivers transitions from\n * Active to CancellationTriggered. In that case, once we receive callbacks\n * indicating the cancellation signal has been received for all other receivers\n * and the sender, the state of the sender and all other receivers transitions\n * to CancellationProcessed (and this object is deleted).\n *\n * When the sender receives notification that the consumer of the output\n * receiver has stopped consuming, the state of the sender transitions from\n * Active directly to CancellationProcessed, and the state of all remaining\n * input receivers transitions from Active to CancellationTriggered. This\n * object will then be deleted once each remaining input receiver transitions to\n * the CancellationProcessed state (after we receive each cancelled callback).\n */\ntemplate <typename TValue, bool WaitForAllInputsToClose>\nclass MergeProcessor : public IChannelCallback {\n public:\n  MergeProcessor(\n      Sender<TValue> sender,\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n      : sender_(std::move(detail::senderGetBridge(sender))),\n        executor_(std::move(executor)) {}\n\n  /**\n   * Starts merging inputs from all input receivers into the output receiver.\n   *\n   * @param inputReceivers: The collection of input receivers to merge.\n   */\n  void start(std::vector<Receiver<TValue>> inputReceivers) {\n    executor_->add([=,\n                    this,\n                    inputReceivers = std::move(inputReceivers)]() mutable {\n      if (!sender_->senderWait(this)) {\n        sender_->senderClose();\n        processSenderCancelled();\n        return;\n      }\n      auto buffers =\n          folly::F14FastMap<ChannelBridge<TValue>*, ReceiverQueue<TValue>>();\n      receivers_.reserve(inputReceivers.size());\n      buffers.reserve(inputReceivers.size());\n      for (auto& inputReceiver : inputReceivers) {\n        auto [unbufferedInputReceiver, buffer] =\n            detail::receiverUnbuffer(std::move(inputReceiver));\n        CHECK(unbufferedInputReceiver != nullptr)\n            << \"The bridge in the input receiver is null.\";\n        CHECK(buffers\n                  .insert(\n                      std::make_pair(\n                          unbufferedInputReceiver.get(), std::move(buffer)))\n                  .second);\n        receivers_.insert(unbufferedInputReceiver.release());\n      }\n      for (auto* receiver : receivers_) {\n        processAllAvailableValues(\n            receiver,\n            !buffers.empty()\n                ? std::make_optional(std::move(buffers.at(receiver)))\n                : std::nullopt);\n      }\n    });\n  }\n\n  /**\n   * Called when one of the channels we are listening to has an update (either\n   * a value from an input receiver or a cancellation from the output receiver).\n   */\n  void consume(ChannelBridgeBase* bridge) override {\n    executor_->add([=, this]() {\n      if (bridge == sender_.get()) {\n        // The consumer of the output receiver has stopped consuming.\n        CHECK_NE(getSenderState(), ChannelState::CancellationProcessed);\n        sender_->senderClose();\n        processSenderCancelled();\n      } else {\n        // One or more values are now available from an input receiver.\n        auto* receiver = static_cast<ChannelBridge<TValue>*>(bridge);\n        CHECK_NE(\n            getReceiverState(receiver), ChannelState::CancellationProcessed);\n        processAllAvailableValues(receiver);\n      }\n    });\n  }\n\n  /**\n   * Called after we cancelled one of the channels we were listening to (either\n   * the sender or an input receiver).\n   */\n  void canceled(ChannelBridgeBase* bridge) override {\n    executor_->add([=, this]() {\n      if (bridge == sender_.get()) {\n        // We previously cancelled the sender due to an input receiver closure\n        // with an exception (or the closure of all input receivers without an\n        // exception). Process the cancellation for the sender.\n        CHECK_EQ(getSenderState(), ChannelState::CancellationTriggered);\n        processSenderCancelled();\n      } else {\n        // We previously cancelled this input receiver, either because the\n        // consumer of the output receiver stopped consuming or because another\n        // input receiver received an exception. Process the cancellation for\n        // this input receiver.\n        auto* receiver = static_cast<ChannelBridge<TValue>*>(bridge);\n        CHECK_EQ(\n            getReceiverState(receiver), ChannelState::CancellationTriggered);\n        processReceiverCancelled(receiver, CloseResult());\n      }\n    });\n  }\n\n private:\n  /**\n   * Processes all available values from the input receiver (starting from the\n   * provided buffer, if present).\n   *\n   * If an value was received indicating that the input channel has been closed\n   * (or if the transform function indicated that channel should be closed), we\n   * will process cancellation for the input receiver.\n   */\n  void processAllAvailableValues(\n      ChannelBridge<TValue>* receiver,\n      std::optional<ReceiverQueue<TValue>> buffer = std::nullopt) {\n    auto closeResult =\n        getReceiverState(receiver) == ChannelState::CancellationTriggered\n        ? CloseResult()\n        : buffer.has_value()\n        ? processValues(std::move(buffer.value()))\n        : std::nullopt;\n    while (!closeResult.has_value()) {\n      if (receiver->receiverWait(this)) {\n        // There are no more values available right now. We will stop processing\n        // until the channel fires the consume() callback (indicating that more\n        // values are available).\n        break;\n      }\n      auto values = receiver->receiverGetValues();\n      CHECK(!values.empty());\n      closeResult = processValues(std::move(values));\n    }\n    if (closeResult.has_value()) {\n      // The receiver received a value indicating channel closure.\n      receiver->receiverCancel();\n      processReceiverCancelled(receiver, std::move(closeResult.value()));\n    }\n  }\n\n  /**\n   * Processes the given set of values for an input receiver. Returns a\n   * CloseResult if the given channel was closed, so the caller can stop\n   * attempting to process values from it.\n   */\n  std::optional<CloseResult> processValues(ReceiverQueue<TValue> values) {\n    while (!values.empty()) {\n      auto inputResult = std::move(values.front());\n      values.pop();\n      if (inputResult.hasValue()) {\n        // We have received a normal value from an input receiver. Write it to\n        // the output receiver.\n        sender_->senderPush(std::move(inputResult.value()));\n      } else {\n        // The input receiver was closed.\n        return inputResult.hasException()\n            ? CloseResult(std::move(inputResult.exception()))\n            : CloseResult();\n      }\n    }\n    return std::nullopt;\n  }\n\n  /**\n   * Processes the cancellation of an input receiver. If the cancellation was\n   * due to receipt of an exception (or the cancellation was the last input\n   * receiver to be closed), we will also trigger cancellation for the sender\n   * (and all other input receivers).\n   */\n  void processReceiverCancelled(\n      ChannelBridge<TValue>* receiver, CloseResult closeResult) {\n    CHECK_EQ(getReceiverState(receiver), ChannelState::CancellationTriggered);\n    receivers_.erase(receiver);\n    (ChannelBridgePtr<TValue>(receiver));\n    if (closeResult.exception.has_value() || !WaitForAllInputsToClose) {\n      // We need to close the sender and all other receivers.\n      if (getSenderState() == ChannelState::Active) {\n        if (closeResult.exception.has_value()) {\n          sender_->senderClose(std::move(closeResult.exception.value()));\n        } else {\n          sender_->senderClose();\n        }\n      }\n      for (auto* otherReceiver : receivers_) {\n        if (getReceiverState(otherReceiver) == ChannelState::Active) {\n          otherReceiver->receiverCancel();\n        }\n      }\n    } else if (receivers_.empty()) {\n      // We just closed the last receiver. Close the sender.\n      if (getSenderState() == ChannelState::Active) {\n        sender_->senderClose();\n      }\n    }\n    maybeDelete();\n  }\n\n  /**\n   * Processes the cancellation of the sender (indicating that the consumer of\n   * the output receiver has stopped consuming). We will trigger cancellation\n   * for all input receivers not already cancelled.\n   */\n  void processSenderCancelled() {\n    CHECK_EQ(getSenderState(), ChannelState::CancellationTriggered);\n    sender_ = nullptr;\n    for (auto* receiver : receivers_) {\n      if (getReceiverState(receiver) == ChannelState::Active) {\n        receiver->receiverCancel();\n      }\n    }\n    maybeDelete();\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * sender and all input receivers.\n   */\n  void maybeDelete() {\n    if (getSenderState() == ChannelState::CancellationProcessed &&\n        receivers_.empty()) {\n      delete this;\n    }\n  }\n\n  ChannelState getReceiverState(ChannelBridge<TValue>* receiver) {\n    return detail::getReceiverState(receiver);\n  }\n\n  ChannelState getSenderState() {\n    return detail::getSenderState(sender_.get());\n  }\n\n  folly::F14FastSet<ChannelBridge<TValue>*> receivers_;\n  ChannelBridgePtr<TValue> sender_;\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n};\n} // namespace detail\n\ntemplate <typename TReceiver, typename TValue>\nReceiver<TValue> merge(\n    std::vector<TReceiver> inputReceivers,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    bool waitForAllInputsToClose) {\n  auto [outputReceiver, outputSender] = Channel<TValue>::create();\n  if (waitForAllInputsToClose) {\n    auto* processor = new detail::MergeProcessor<TValue, true>(\n        std::move(outputSender), std::move(executor));\n    processor->start(std::move(inputReceivers));\n  } else {\n    auto* processor = new detail::MergeProcessor<TValue, false>(\n        std::move(outputSender), std::move(executor));\n    processor->start(std::move(inputReceivers));\n  }\n  return std::move(outputReceiver);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Merge.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\n/**\n * Merge takes a list of receivers, and returns a new receiver that receives\n * all updates from all input receivers. If any input receiver closes with\n * an exception, the exception is forwarded and the channel is closed. If any\n * input receiver closes without an exception, the channel continues to merge\n * values from the other input receivers until all input receivers are closed.\n *\n * @param inputReceivers: The collection of input receivers to merge.\n *\n * @param executor: A SequencedExecutor used to merge input values.\n *\n * @param waitForAllInputsToClose: When true, if any input receiver closes\n * without an exception, the channel continues to merge values from the other\n * input receivers until all input receivers are closed. If false, the channel\n * closes as soon as any input receiver has closed.\n *\n * Example:\n *\n *  // Example function that returns a list of receivers\n *  std::vector<Receiver<int>> getReceivers();\n *\n *  // Example function that returns an executor\n *  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *  Receiver<int> mergedReceiver = merge(getReceivers(), getExecutor());\n */\ntemplate <typename TReceiver, typename TValue = typename TReceiver::ValueType>\nReceiver<TValue> merge(\n    std::vector<TReceiver> inputReceivers,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    bool waitForAllInputsToClose = true);\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/Merge-inl.h>\n"
  },
  {
    "path": "folly/channels/MergeChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename KeyType, typename ValueType>\nMergeChannel<KeyType, ValueType>::MergeChannel(TProcessor* processor)\n    : processor_(processor) {}\n\ntemplate <typename KeyType, typename ValueType>\nMergeChannel<KeyType, ValueType>::MergeChannel(MergeChannel&& other) noexcept\n    : processor_(std::exchange(other.processor_, nullptr)) {}\n\ntemplate <typename KeyType, typename ValueType>\nMergeChannel<KeyType, ValueType>& MergeChannel<KeyType, ValueType>::operator=(\n    MergeChannel&& other) noexcept {\n  if (&other == this) {\n    return *this;\n  }\n  if (processor_) {\n    std::move(*this).close();\n  }\n  processor_ = std::exchange(other.processor_, nullptr);\n  return *this;\n}\n\ntemplate <typename KeyType, typename ValueType>\nMergeChannel<KeyType, ValueType>::~MergeChannel() {\n  if (processor_) {\n    std::move(*this).close(std::nullopt /* ex */);\n  }\n}\n\ntemplate <typename KeyType, typename ValueType>\nMergeChannel<KeyType, ValueType>::operator bool() const {\n  return processor_;\n}\n\ntemplate <typename KeyType, typename ValueType>\ntemplate <typename TReceiver>\nvoid MergeChannel<KeyType, ValueType>::addNewReceiver(\n    KeyType key, TReceiver receiver) {\n  processor_->addNewReceiver(key, std::move(receiver));\n}\n\ntemplate <typename KeyType, typename ValueType>\nvoid MergeChannel<KeyType, ValueType>::removeReceiver(KeyType key) {\n  processor_->removeReceiver(key);\n}\n\ntemplate <typename KeyType, typename ValueType>\nfolly::F14FastSet<KeyType> MergeChannel<KeyType, ValueType>::getReceiverKeys() {\n  return processor_->getReceiverKeys();\n}\n\ntemplate <typename KeyType, typename ValueType>\nvoid MergeChannel<KeyType, ValueType>::close(\n    std::optional<exception_wrapper> ex) && {\n  processor_->destroyHandle(\n      ex.has_value() ? detail::CloseResult(std::move(ex.value()))\n                     : detail::CloseResult());\n  processor_ = nullptr;\n}\n\nnamespace detail {\n\ntemplate <typename KeyType, typename ValueType>\nclass IMergeChannelProcessor : public IChannelCallback {\n public:\n  virtual void addNewReceiver(KeyType key, Receiver<ValueType> receiver) = 0;\n\n  virtual void removeReceiver(KeyType key) = 0;\n\n  virtual folly::F14FastSet<KeyType> getReceiverKeys() = 0;\n\n  virtual void destroyHandle(CloseResult closeResult) = 0;\n};\n\n/**\n * This object does the merging of values from the input receivers to the output\n * receiver. The lifetime of this object is described by the following state\n * machine.\n *\n * The sender and all active receivers can be in one of three conceptual states:\n * Active, CancellationTriggered, or CancellationProcessed (removed). When the\n * sender and all receivers reach the CancellationProcessed state AND the user's\n * MergeChannel object is deleted, this object is deleted.\n *\n * When an input receiver receives a value indicating that the channel has\n * been closed, the state of that receiver transitions from Active directly to\n * CancellationProcessed and the receiver is removed.\n *\n * If the receiver closed with an exception, the state of the sender and all\n * other receivers transitions from Active to CancellationTriggered. In that\n * case, once we receive callbacks indicating the cancellation signal has been\n * received for all other receivers and the sender, the state of the sender and\n * all other receivers transitions to CancellationProcessed (and this object\n * will be deleted once the user destroys their MergeChannel object).\n *\n * When the sender receives notification that the consumer of the output\n * receiver has stopped consuming, the state of the sender transitions from\n * Active directly to CancellationProcessed, and the state of all remaining\n * input receivers transitions from Active to CancellationTriggered. Once we\n * receive callbacks for all input receivers indicating that the cancellation\n * signal has been received, each such receiver is transitioned to the\n * CancellationProcessed state (and this object will be deleted once the user\n * destroys their MergeChannel object).\n *\n * When the user destroys their MergeChannel object, the state of the sender and\n * all remaining receivers transition from Active to CancellationTriggered. This\n * object will then be deleted once the sender and each remaining input receiver\n * transitions to the CancellationProcessed state (after we receive each\n * cancelled callback).\n */\ntemplate <typename KeyType, typename ValueType>\nclass MergeChannelProcessor\n    : public IMergeChannelProcessor<KeyType, ValueType> {\n private:\n  struct State {\n    explicit State(\n        ChannelBridgePtr<MergeChannelEvent<KeyType, ValueType>> _sender)\n        : sender(std::move(_sender)) {}\n\n    ChannelState getSenderState() {\n      return detail::getSenderState(sender.get());\n    }\n\n    // The output sender for the merge channel.\n    ChannelBridgePtr<MergeChannelEvent<KeyType, ValueType>> sender;\n\n    // A non-owning map from key to receiver.\n    folly::F14NodeMap<KeyType, ChannelBridge<ValueType>*> receiversByKey;\n\n    // The set of receivers that feed into this MergeChannel. This map \"owns\"\n    // its receivers. MergeChannelProcessor must free any receiver removed from\n    // this map.\n    folly::F14NodeMap<ChannelBridge<ValueType>*, const KeyType*> receivers;\n\n    // Whether or not the handle to the MergeChannel has been destroyed.\n    bool handleDestroyed{false};\n  };\n\n  using WLockedStatePtr = typename folly::Synchronized<State>::WLockedPtr;\n\n public:\n  MergeChannelProcessor(\n      Sender<MergeChannelEvent<KeyType, ValueType>> sender,\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n      : executor_(std::move(executor)),\n        state_(State(std::move(detail::senderGetBridge(sender)))) {\n    auto state = state_.wlock();\n    CHECK(state->sender->senderWait(this));\n  }\n\n  /**\n   * Adds a new receiver to be merged, along with a key to allow for later\n   * removal.\n   */\n  void addNewReceiver(KeyType key, Receiver<ValueType> receiver) {\n    auto state = state_.wlock();\n    if (state->getSenderState() != ChannelState::Active) {\n      return;\n    }\n    auto [unbufferedReceiver, buffer] =\n        detail::receiverUnbuffer(std::move(receiver));\n    auto existingReceiverIt = state->receiversByKey.find(key);\n    if (existingReceiverIt != state->receiversByKey.end()) {\n      CHECK(state->receivers.contains(existingReceiverIt->second));\n      if (!existingReceiverIt->second->isReceiverCancelled()) {\n        // We already have a receiver with the given key. Trigger cancellation\n        // on that previous receiver.\n        existingReceiverIt->second->receiverCancel();\n      }\n      auto keyToRemove = existingReceiverIt->first;\n      state->receivers[existingReceiverIt->second] = nullptr;\n      state->receiversByKey.erase(existingReceiverIt);\n      state->sender->senderPush(\n          MergeChannelEvent<KeyType, ValueType>{\n              keyToRemove, MergeChannelReceiverRemoved{}});\n    }\n    auto [it, _] = state->receiversByKey.insert(\n        std::make_pair(key, unbufferedReceiver.get()));\n    auto* receiverPtr = unbufferedReceiver.get();\n    state->receivers.insert(\n        std::make_pair(unbufferedReceiver.release(), &it->first));\n    state->sender->senderPush(\n        MergeChannelEvent<KeyType, ValueType>{\n            key, MergeChannelReceiverAdded{}});\n    processAllAvailableValues(state, receiverPtr, std::move(buffer));\n  }\n\n  /**\n   * Removes the receiver with the given key.\n   */\n  void removeReceiver(KeyType key) {\n    auto state = state_.wlock();\n    if (state->getSenderState() != ChannelState::Active) {\n      return;\n    }\n    auto receiverIt = state->receiversByKey.find(key);\n    if (receiverIt == state->receiversByKey.end()) {\n      return;\n    }\n    CHECK(state->receivers.contains(receiverIt->second));\n    if (!receiverIt->second->isReceiverCancelled()) {\n      receiverIt->second->receiverCancel();\n    }\n    auto keyToRemove = receiverIt->first;\n    state->receivers[receiverIt->second] = nullptr;\n    state->receiversByKey.erase(receiverIt);\n    state->sender->senderPush(\n        MergeChannelEvent<KeyType, ValueType>{\n            keyToRemove, MergeChannelReceiverRemoved{}});\n  }\n\n  folly::F14FastSet<KeyType> getReceiverKeys() {\n    auto state = state_.rlock();\n    auto receiverKeys = folly::F14FastSet<KeyType>();\n    receiverKeys.reserve(state->receiversByKey.size());\n    for (const auto& [key, _] : state->receiversByKey) {\n      receiverKeys.insert(key);\n    }\n    return receiverKeys;\n  }\n\n  template <typename K>\n  bool hasReceiverKey(const K& key) {\n    auto state = state_.rlock();\n    return state->receiversByKey.contains(key);\n  }\n\n  /**\n   * Called when the user's MergeChannel object is destroyed.\n   */\n  void destroyHandle(CloseResult closeResult) {\n    auto state = state_.wlock();\n    processHandleDestroyed(state, std::move(closeResult));\n  }\n\n  /**\n   * Called when one of the channels we are listening to has an update (either\n   * a value from an input receiver or a cancellation from the output receiver).\n   */\n  void consume(ChannelBridgeBase* bridge) override {\n    executor_->add([=, this]() {\n      auto state = state_.wlock();\n      if (bridge == state->sender.get()) {\n        // The consumer of the output receiver has stopped consuming.\n        CHECK(state->getSenderState() != ChannelState::CancellationProcessed);\n        state->sender->senderClose();\n        processSenderCancelled(state);\n      } else {\n        // One or more values are now available from an input receiver.\n        auto* receiver = static_cast<ChannelBridge<ValueType>*>(bridge);\n        CHECK(\n            getReceiverState(receiver) != ChannelState::CancellationProcessed);\n        processAllAvailableValues(state, receiver);\n      }\n    });\n  }\n\n  /**\n   * Called after we cancelled one of the channels we were listening to (either\n   * the sender or an input receiver).\n   */\n  void canceled(ChannelBridgeBase* bridge) override {\n    executor_->add([=, this]() {\n      auto state = state_.wlock();\n      if (bridge == state->sender.get()) {\n        // We previously cancelled the sender due to an input receiver closure\n        // with an exception (or the closure of all input receivers without an\n        // exception). Process the cancellation for the sender.\n        CHECK(state->getSenderState() == ChannelState::CancellationTriggered);\n        processSenderCancelled(state);\n      } else {\n        // We previously cancelled this input receiver, either because the\n        // consumer of the output receiver stopped consuming or because another\n        // input receiver received an exception. Process the cancellation for\n        // this input receiver.\n        auto* receiver = static_cast<ChannelBridge<ValueType>*>(bridge);\n        processReceiverCancelled(state, receiver, CloseResult());\n      }\n    });\n  }\n\n protected:\n  /**\n   * Processes all available values from the given input receiver channel\n   * (starting from the provided buffer, if present).\n   *\n   * If an value was received indicating that the input channel has been closed\n   * (or if the transform function indicated that channel should be closed), we\n   * will process cancellation for the input receiver.\n   */\n  void processAllAvailableValues(\n      WLockedStatePtr& state,\n      ChannelBridge<ValueType>* receiver,\n      std::optional<ReceiverQueue<ValueType>> buffer = std::nullopt) {\n    CHECK(state->receivers.contains(receiver));\n    const auto* key = state->receivers.at(receiver);\n    auto closeResult = receiver->isReceiverCancelled()\n        ? CloseResult()\n        : (buffer.has_value()\n               ? processValues(state, std::move(buffer.value()), key)\n               : std::nullopt);\n    while (!closeResult.has_value()) {\n      if (receiver->receiverWait(this)) {\n        // There are no more values available right now. We will stop processing\n        // until the channel fires the consume() callback (indicating that more\n        // values are available).\n        break;\n      }\n      auto values = receiver->receiverGetValues();\n      CHECK(!values.empty());\n      closeResult = processValues(state, std::move(values), key);\n    }\n    if (closeResult.has_value()) {\n      // The receiver received a value indicating channel closure.\n      receiver->receiverCancel();\n      processReceiverCancelled(state, receiver, std::move(closeResult.value()));\n    }\n  }\n\n  /**\n   * Processes the given set of values for an input receiver. Returns a\n   * CloseResult if the given channel was closed, so the caller can stop\n   * attempting to process values from it.\n   */\n  std::optional<CloseResult> processValues(\n      WLockedStatePtr& state,\n      ReceiverQueue<ValueType> values,\n      const KeyType* key) {\n    while (!values.empty()) {\n      auto inputResult = std::move(values.front());\n      values.pop();\n      if (inputResult.hasValue()) {\n        // We have received a normal value from an input receiver. Write it to\n        // the output receiver.\n        state->sender->senderPush(\n            MergeChannelEvent<KeyType, ValueType>{\n                *key, std::move(inputResult.value())});\n      } else {\n        // The input receiver was closed.\n        return inputResult.hasException()\n            ? CloseResult(std::move(inputResult.exception()))\n            : CloseResult();\n      }\n    }\n    return std::nullopt;\n  }\n\n  /**\n   * Processes the cancellation of an input receiver.\n   */\n  void processReceiverCancelled(\n      WLockedStatePtr& state,\n      ChannelBridge<ValueType>* receiver,\n      CloseResult closeResult) {\n    CHECK(getReceiverState(receiver) == ChannelState::CancellationTriggered);\n    auto* key = state->receivers.at(receiver);\n    if (key != nullptr) {\n      auto keyToRemove = *key;\n      CHECK_EQ(state->receiversByKey.erase(keyToRemove), 1);\n      if (state->getSenderState() == ChannelState::Active) {\n        state->sender->senderPush(\n            MergeChannelEvent<KeyType, ValueType>{\n                keyToRemove,\n                MergeChannelReceiverClosed{\n                    closeResult.exception.has_value()\n                        ? std::move(closeResult.exception.value())\n                        : exception_wrapper()}});\n      }\n    }\n    state->receivers.erase(receiver);\n    (ChannelBridgePtr<ValueType>(receiver));\n    maybeDelete(state);\n  }\n\n  /**\n   * Processes the cancellation of the sender (indicating that the consumer of\n   * the output receiver has stopped consuming). We will trigger cancellation\n   * for all input receivers not already cancelled.\n   */\n  void processSenderCancelled(WLockedStatePtr& state) {\n    CHECK(state->getSenderState() == ChannelState::CancellationTriggered);\n    state->sender = nullptr;\n    for (auto [receiver, _] : state->receivers) {\n      if (getReceiverState(receiver) == ChannelState::Active) {\n        receiver->receiverCancel();\n      }\n    }\n    maybeDelete(state);\n  }\n\n  /**\n   * Processes the destruction of the user's MergeChannel object.  We will\n   * close the sender and trigger cancellation for all input receivers not\n   * already cancelled.\n   */\n  void processHandleDestroyed(WLockedStatePtr& state, CloseResult closeResult) {\n    CHECK(!state->handleDestroyed);\n    state->handleDestroyed = true;\n    if (state->getSenderState() == ChannelState::Active) {\n      for (auto [key, receiver] : state->receiversByKey) {\n        state->receivers[receiver] = nullptr;\n        state->sender->senderPush(\n            MergeChannelEvent<KeyType, ValueType>{\n                key, MergeChannelReceiverRemoved{}});\n      }\n      if (closeResult.exception.has_value()) {\n        state->sender->senderClose(std::move(closeResult.exception.value()));\n      } else {\n        state->sender->senderClose();\n      }\n    }\n    for (auto [receiver, _] : state->receivers) {\n      if (getReceiverState(receiver) == ChannelState::Active) {\n        receiver->receiverCancel();\n      }\n    }\n    maybeDelete(state);\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * sender and all input receivers, and if the user's MergeChannel object was\n   * destroyed.\n   */\n  void maybeDelete(WLockedStatePtr& state) {\n    if (state->getSenderState() == ChannelState::CancellationProcessed &&\n        state->receivers.empty() && state->handleDestroyed) {\n      state.unlock();\n      delete this;\n    }\n  }\n\n  ChannelState getReceiverState(ChannelBridge<ValueType>* receiver) {\n    return detail::getReceiverState(receiver);\n  }\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  folly::Synchronized<State> state_;\n};\n} // namespace detail\n\ntemplate <typename KeyType, typename ValueType>\ntemplate <typename K>\nbool MergeChannel<KeyType, ValueType>::hasReceiverKey(const K& key) {\n  using Processor = detail::MergeChannelProcessor<KeyType, ValueType>;\n  return static_cast<Processor*>(processor_)->hasReceiverKey(key);\n}\n\ntemplate <typename KeyType, typename ValueType>\nstd::pair<\n    Receiver<MergeChannelEvent<KeyType, ValueType>>,\n    MergeChannel<KeyType, ValueType>>\ncreateMergeChannel(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor) {\n  auto [receiver, sender] =\n      Channel<MergeChannelEvent<KeyType, ValueType>>::create();\n  auto* processor = new detail::MergeChannelProcessor<KeyType, ValueType>(\n      std::move(sender), std::move(executor));\n  return std::make_pair(\n      std::move(receiver), MergeChannel<KeyType, ValueType>(processor));\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/MergeChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/container/F14Set.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\ntemplate <typename KeyType, typename ValueType>\nclass IMergeChannelProcessor;\n}\n\nstruct MergeChannelReceiverAdded {};\nstruct MergeChannelReceiverRemoved {};\nstruct MergeChannelReceiverClosed {\n  exception_wrapper exception;\n};\n\ntemplate <typename KeyType, typename ValueType>\nstruct MergeChannelEvent {\n  using EventType = std::variant<\n      ValueType,\n      MergeChannelReceiverAdded,\n      MergeChannelReceiverRemoved,\n      MergeChannelReceiverClosed>;\n\n  KeyType key;\n  EventType event;\n};\n\n/**\n * A merge channel allows one to merge multiple receivers into a single\n * output receiver. The set of receivers being merged can be changed at\n * runtime. Each receiver is added with a key that can be used to remove\n * the receiver at a later point.\n *\n * Example:\n *\n *  // Example function that returns a receiver for a given entity:\n *  Receiver<int> subscribe(std::string entity);\n *\n *  // Example function that returns an executor\n *  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *  auto [outputReceiver, mergeChannel]\n *      = createMergeChannel<std::string, int>(getExecutor());\n *  mergeChannel.addNewReceiver(\"abc\", subscribe(\"abc\"));\n *  mergeChannel.addNewReceiver(\"def\", subscribe(\"def\"));\n *  mergeChannel.removeReceiver(\"abc\");\n *  std::move(mergeChannel).close();\n */\ntemplate <typename KeyType, typename ValueType>\nclass MergeChannel {\n  using TProcessor = detail::IMergeChannelProcessor<KeyType, ValueType>;\n\n public:\n  explicit MergeChannel(\n      detail::IMergeChannelProcessor<KeyType, ValueType>* processor);\n  MergeChannel(MergeChannel&& other) noexcept;\n  MergeChannel& operator=(MergeChannel&& other) noexcept;\n  ~MergeChannel();\n\n  /**\n   * Returns whether this MergeChannel is a valid object.\n   */\n  explicit operator bool() const;\n\n  /**\n   * Adds a new receiver to be merged, along with a given key. If the key\n   * matches the key of an existing receiver, that existing receiver is replaced\n   * with the new one (and updates from the old receiver will no longer be\n   * merged). An added receiver can later be removed by passing the same key to\n   * removeReceiver.\n   */\n  template <typename TReceiver>\n  void addNewReceiver(KeyType key, TReceiver receiver);\n\n  /**\n   * Removes the receiver added with the given key. The receiver will be\n   * asynchronously removed, so the consumer may still receive some values from\n   * this receiver after this call.\n   */\n  void removeReceiver(KeyType key);\n\n  /**\n   * Returns a set of keys for receivers that are merged into this MergeChannel.\n   */\n  folly::F14FastSet<KeyType> getReceiverKeys();\n\n  /**\n   * Returns whether a receiver with the given key exists.\n   */\n  template <typename K>\n  bool hasReceiverKey(const K& key);\n\n  /**\n   * Closes the merge channel.\n   */\n  void close(std::optional<exception_wrapper> ex = std::nullopt) &&;\n\n private:\n  TProcessor* processor_;\n};\n\n/**\n * Creates a new merge channel.\n *\n * @param executor: The SequencedExecutor to use for merging values.\n */\ntemplate <typename KeyType, typename ValueType>\nstd::pair<\n    Receiver<MergeChannelEvent<KeyType, ValueType>>,\n    MergeChannel<KeyType, ValueType>>\ncreateMergeChannel(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor);\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/MergeChannel-inl.h>\n"
  },
  {
    "path": "folly/channels/MultiplexChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/MultiplexChannel.h>\n#include <folly/channels/RateLimiter.h>\n#include <folly/coro/FutureUtil.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/Promise.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename MultiplexerType>\nMultiplexedSubscriptions<MultiplexerType>::MultiplexedSubscriptions(\n    SubscriptionMap& subscriptions)\n    : subscriptions_(subscriptions) {}\n\ntemplate <typename MultiplexerType>\nbool MultiplexedSubscriptions<MultiplexerType>::hasSubscription(\n    const MultiplexedSubscriptions::KeyType& key) {\n  return subscriptions_.contains(key) && !closedSubscriptionKeys_.contains(key);\n}\n\ntemplate <typename MultiplexerType>\ntypename MultiplexedSubscriptions<MultiplexerType>::KeyContextType&\nMultiplexedSubscriptions<MultiplexerType>::getKeyContext(\n    const MultiplexedSubscriptions::KeyType& key) {\n  ensureKeyExists(key);\n  return std::get<KeyContextType>(subscriptions_.at(key));\n}\n\ntemplate <typename MultiplexerType>\ntemplate <typename U>\nvoid MultiplexedSubscriptions<MultiplexerType>::write(\n    const MultiplexedSubscriptions::KeyType& key, U&& value) {\n  ensureKeyExists(key);\n  auto& sender =\n      std::get<FanoutSender<OutputValueType>>(subscriptions_.at(key));\n  sender.write(std::forward<U>(value));\n}\n\ntemplate <typename MultiplexerType>\nvoid MultiplexedSubscriptions<MultiplexerType>::close(\n    const MultiplexedSubscriptions::KeyType& key, exception_wrapper ex) {\n  ensureKeyExists(key);\n  auto& sender =\n      std::get<FanoutSender<OutputValueType>>(subscriptions_.at(key));\n  if (ex) {\n    std::move(sender).close(std::move(ex));\n  } else {\n    std::move(sender).close();\n  }\n  // We do not erase from the subscriptions_ map yet, because we do not want\n  // to invalidate the view returned by getSubscriptionKeys.\n  closedSubscriptionKeys_.insert(key);\n}\n\ntemplate <typename MultiplexerType>\nvoid MultiplexedSubscriptions<MultiplexerType>::ensureKeyExists(\n    const KeyType& key) {\n  if (!subscriptions_.contains(key) || closedSubscriptionKeys_.contains(key)) {\n    throw std::runtime_error(\"Subscription with the given key does not exist.\");\n  }\n}\n\ntemplate <typename MultiplexerType>\nMultiplexChannel<MultiplexerType>::MultiplexChannel(TProcessor* processor)\n    : processor_(processor) {}\n\ntemplate <typename MultiplexerType>\nMultiplexChannel<MultiplexerType>::MultiplexChannel(\n    MultiplexChannel&& other) noexcept\n    : processor_(std::exchange(other.processor_, nullptr)) {}\n\ntemplate <typename MultiplexerType>\nMultiplexChannel<MultiplexerType>& MultiplexChannel<MultiplexerType>::operator=(\n    MultiplexChannel&& other) noexcept {\n  if (&other == this) {\n    return *this;\n  }\n  if (processor_) {\n    std::move(*this).close();\n  }\n  processor_ = std::exchange(other.processor_, nullptr);\n  return *this;\n}\n\ntemplate <typename MultiplexerType>\nMultiplexChannel<MultiplexerType>::~MultiplexChannel() {\n  if (processor_ != nullptr) {\n    std::move(*this).close(exception_wrapper());\n  }\n}\n\ntemplate <typename MultiplexerType>\nMultiplexChannel<MultiplexerType>::operator bool() const {\n  return processor_;\n}\n\ntemplate <typename MultiplexerType>\nReceiver<typename MultiplexChannel<MultiplexerType>::OutputValueType>\nMultiplexChannel<MultiplexerType>::subscribe(\n    KeyType key, SubscriptionArgType subscriptionArg) {\n  return processor_->subscribe(std::move(key), std::move(subscriptionArg));\n}\n\ntemplate <typename MultiplexerType>\nfolly::coro::Task<std::vector<std::pair<\n    typename MultiplexChannel<MultiplexerType>::KeyType,\n    typename MultiplexChannel<MultiplexerType>::KeyContextType>>>\nMultiplexChannel<MultiplexerType>::clearUnusedSubscriptions() {\n  co_return co_await processor_->clearUnusedSubscriptions();\n}\n\ntemplate <typename MultiplexerType>\nbool MultiplexChannel<MultiplexerType>::anySubscribers() const {\n  return processor_->anySubscribers();\n}\n\ntemplate <typename MultiplexerType>\nvoid MultiplexChannel<MultiplexerType>::close(exception_wrapper ex) && {\n  processor_->destroyHandle(\n      ex ? detail::CloseResult(std::move(ex)) : detail::CloseResult());\n  processor_ = nullptr;\n}\n\nnamespace detail {\n\n/**\n * This object fans out values from the input receiver to all output receivers.\n * The lifetime of this object is described by the following state machine.\n *\n * The input receiver can be in one of three conceptual states: Active,\n * CancellationTriggered, or CancellationProcessed (removed). When the input\n * receiver reaches the CancellationProcessed state AND the user's\n * MultiplexChannel object is deleted, this object is deleted.\n *\n * When an input receiver receives a value indicating that the channel has\n * been closed, the state of the input receiver transitions from Active directly\n * to CancellationProcessed (and this object will be deleted once the user\n * destroys their MultiplexChannel object).\n *\n * When the user destroys their MultiplexChannel object, the state of the input\n * receiver transitions from Active to CancellationTriggered. This object will\n * then be deleted once the input receiver transitions to the\n * CancellationProcessed state.\n */\ntemplate <typename MultiplexerType>\nclass MultiplexChannelProcessor : public IChannelCallback {\n private:\n  using MultiplexerTypeTraits = detail::MultiplexerTraits<MultiplexerType>;\n  using KeyType = typename MultiplexerTypeTraits::KeyType;\n  using KeyContextType = typename MultiplexerTypeTraits::KeyContextType;\n  using SubscriptionArgType =\n      typename MultiplexerTypeTraits::SubscriptionArgType;\n  using InputValueType = typename MultiplexerTypeTraits::InputValueType;\n  using OutputValueType = typename MultiplexerTypeTraits::OutputValueType;\n\n public:\n  explicit MultiplexChannelProcessor(MultiplexerType multiplexer)\n      : multiplexer_(std::move(multiplexer)),\n        totalSubscriptions_(0),\n        pendingAsyncCalls_(0) {}\n\n  /**\n   * Starts multiplexing values from the input receiver to to one or more keyed\n   * subscriptions.\n   */\n  void start(Receiver<InputValueType> inputReceiver) {\n    executeWithMutexWhenReady(\n        [this, inputReceiver = std::move(inputReceiver)]() mutable\n            -> folly::coro::Task<void> {\n          co_await processStart(std::move(inputReceiver));\n        });\n  }\n\n  Receiver<OutputValueType> subscribe(\n      KeyType key, SubscriptionArgType subscriptionArg) {\n    auto [receiver, sender] = Channel<OutputValueType>::create();\n    totalSubscriptions_.fetch_add(1);\n    executeWithMutexWhenReady(\n        [this,\n         key = std::move(key),\n         subscriptionArg = std::move(subscriptionArg),\n         sender_2 = std::move(sender)]() mutable -> folly::coro::Task<void> {\n          co_await processNewSubscription(\n              std::move(key), std::move(subscriptionArg), std::move(sender_2));\n        });\n    return std::move(receiver);\n  }\n\n  folly::coro::Task<std::vector<std::pair<KeyType, KeyContextType>>>\n  clearUnusedSubscriptions() {\n    auto [promise, future] = folly::coro::makePromiseContract<\n        std::vector<std::pair<KeyType, KeyContextType>>>();\n    executeWithMutexWhenReady(\n        [this,\n         promise_2 = std::move(promise)]() mutable -> folly::coro::Task<void> {\n          co_await processClearUnusedSubscriptions(std::move(promise_2));\n        });\n    return folly::coro::toTask(std::move(future));\n  }\n\n  bool anySubscribers() { return totalSubscriptions_.load() > 0; }\n\n  /**\n   * This is called when the user's MultiplexChannel object has been destroyed.\n   */\n  void destroyHandle(CloseResult closeResult) {\n    executeWithMutexWhenReady(\n        [this, closeResult = std::move(closeResult)]() mutable\n            -> folly::coro::Task<void> {\n          co_await processHandleDestroyed(std::move(closeResult));\n        });\n  }\n\n private:\n  /**\n   * Called when the input receiver has an update.\n   */\n  void consume(ChannelBridgeBase*) override {\n    executeWithMutexWhenReady([this]() -> folly::coro::Task<void> {\n      co_await processAllAvailableValues();\n    });\n  }\n\n  /**\n   * Called after we cancelled this input receiver, due to the destruction of\n   * the handle.\n   */\n  void canceled(ChannelBridgeBase*) override {\n    executeWithMutexWhenReady([this]() -> folly::coro::Task<void> {\n      auto closeResult = CloseResult(); // Declaring first due to GCC bug\n      co_await processReceiverCancelled(std::move(closeResult));\n    });\n  }\n\n  folly::coro::Task<void> processStart(Receiver<InputValueType> inputReceiver) {\n    auto [unbufferedInputReceiver, buffer] =\n        detail::receiverUnbuffer(std::move(inputReceiver));\n    receiver_ = std::move(unbufferedInputReceiver);\n\n    // Start processing new values that come in from the input receiver.\n    co_await processAllAvailableValues(std::move(buffer));\n  }\n\n  /**\n   * Processes all available values from the input receiver (starting from the\n   * provided buffer, if present).\n   *\n   * If an value was received indicating that the input channel has been closed\n   * (or if the transform function indicated that channel should be closed), we\n   * will process cancellation for the input receiver.\n   */\n  folly::coro::Task<void> processAllAvailableValues(\n      std::optional<ReceiverQueue<InputValueType>> buffer = std::nullopt) {\n    CHECK_NE(getReceiverState(), ChannelState::CancellationProcessed);\n    auto closeResult = receiver_->isReceiverCancelled()\n        ? CloseResult()\n        : (buffer.has_value()\n               ? co_await processValues(std::move(buffer.value()))\n               : std::nullopt);\n    while (!closeResult.has_value()) {\n      if (receiver_->receiverWait(this)) {\n        // There are no more values available right now. We will stop processing\n        // until the channel fires the consume() callback (indicating that more\n        // values are available).\n        break;\n      }\n      auto values = receiver_->receiverGetValues();\n      CHECK(!values.empty());\n      closeResult = co_await processValues(std::move(values));\n    }\n    if (closeResult.has_value()) {\n      // The receiver received a value indicating channel closure.\n      receiver_->receiverCancel();\n      co_await processReceiverCancelled(std::move(closeResult.value()));\n    }\n  }\n\n  /**\n   * Processes the given set of values for the input receiver. Returns a\n   * CloseResult if channel was closed, so the caller can stop attempting to\n   * process values from it.\n   */\n  folly::coro::Task<std::optional<CloseResult>> processValues(\n      ReceiverQueue<InputValueType> values) {\n    while (!values.empty()) {\n      auto inputResult = std::move(values.front());\n      values.pop();\n      bool inputClosed = !inputResult.hasValue();\n      auto subscriptions =\n          MultiplexedSubscriptions<MultiplexerType>(subscriptions_);\n      if (inputClosed && !inputResult.hasException()) {\n        // The input channel was closed. We will send an OnClosedException to\n        // onInputValue.\n        inputResult = Try<InputValueType>(\n            folly::make_exception_wrapper<OnClosedException>());\n      }\n\n      // Process the input value by calling onInputValue on the user's\n      // multiplexer.\n      auto onInputValueResult = co_await folly::coro::co_awaitTry(\n          multiplexer_.onInputValue(std::move(inputResult), subscriptions));\n\n      // If the user closed any subscriptions, erase them from the subscriptions\n      // map.\n      for (const auto& key : subscriptions.closedSubscriptionKeys_) {\n        subscriptions_.erase(key);\n      }\n      if (!subscriptions.closedSubscriptionKeys_.empty()) {\n        totalSubscriptions_.fetch_sub(\n            subscriptions.closedSubscriptionKeys_.size());\n        subscriptions.closedSubscriptionKeys_.clear();\n      }\n\n      if (inputClosed && onInputValueResult.hasValue()) {\n        // The input channel was closed, but the onInputValue function did not\n        // throw. We need to close all output receivers.\n        onInputValueResult =\n            Try<void>(folly::make_exception_wrapper<OnClosedException>());\n      }\n      if (!onInputValueResult.hasValue()) {\n        co_return onInputValueResult.template hasException<OnClosedException>()\n            ? CloseResult()\n            : CloseResult(std::move(onInputValueResult.exception()));\n      }\n    }\n    co_return std::nullopt;\n  }\n\n  /**\n   * Processes the cancellation of the input receiver. We will close all\n   * senders with the exception received from the input receiver (if any).\n   */\n  folly::coro::Task<void> processReceiverCancelled(CloseResult closeResult) {\n    CHECK_EQ(getReceiverState(), ChannelState::CancellationTriggered);\n    receiver_ = nullptr;\n    closeAllSubscriptions(std::move(closeResult));\n    co_return;\n  }\n\n  folly::coro::Task<void> processNewSubscription(\n      KeyType key,\n      SubscriptionArgType subscriptionArg,\n      Sender<OutputValueType> newSender) {\n    if (subscriptions_.contains(key)) {\n      // We already had a subscription for this key.\n      totalSubscriptions_.fetch_sub(1);\n    }\n    auto& [sender, context] = subscriptions_[key];\n    auto initialValues =\n        co_await folly::coro::co_awaitTry(multiplexer_.onNewSubscription(\n            key, context, std::move(subscriptionArg)));\n    if (initialValues.hasException()) {\n      std::move(newSender).close(initialValues.exception());\n      co_return;\n    }\n    for (auto& initialValue : initialValues.value()) {\n      newSender.write(std::move(initialValue));\n    }\n    sender.subscribe(std::move(newSender));\n  }\n\n  folly::coro::Task<void> processClearUnusedSubscriptions(\n      folly::coro::Promise<std::vector<std::pair<KeyType, KeyContextType>>>\n          promise) {\n    auto clearedSubscriptions =\n        std::vector<std::pair<KeyType, KeyContextType>>();\n    size_t subscriptionsToRemove = 0;\n    for (auto it = subscriptions_.begin(); it != subscriptions_.end();) {\n      auto& sender = std::get<FanoutSender<OutputValueType>>(it->second);\n      if (!sender.anySubscribers()) {\n        clearedSubscriptions.push_back(\n            std::make_pair(\n                it->first, std::move(std::get<KeyContextType>(it->second))));\n        it = subscriptions_.erase(it);\n        subscriptionsToRemove++;\n      } else {\n        ++it;\n      }\n    }\n\n    totalSubscriptions_.fetch_sub(subscriptionsToRemove);\n    promise.setValue(std::move(clearedSubscriptions));\n    co_return;\n  }\n\n  /**\n   * Processes the destruction of the user's MultiplexChannel object.  We will\n   * cancel the receiver and trigger cancellation for all senders not already\n   * cancelled.\n   */\n  folly::coro::Task<void> processHandleDestroyed(CloseResult closeResult) {\n    handleDeleted_ = true;\n    if (getReceiverState() == ChannelState::Active) {\n      receiver_->receiverCancel();\n    }\n    closeAllSubscriptions(std::move(closeResult));\n    co_return;\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * receiver and all senders, and if the user's MultiplexChannel object was\n   * destroyed.\n   */\n  void maybeDelete(std::unique_lock<folly::coro::Mutex>& lock) {\n    if (getReceiverState() == ChannelState::CancellationProcessed &&\n        handleDeleted_ && pendingAsyncCalls_ == 0) {\n      lock.unlock();\n      delete this;\n    }\n  }\n\n  void executeWithMutexWhenReady(\n      folly::Function<folly::coro::Task<void>()> func) {\n    pendingAsyncCalls_++;\n    auto rateLimiter = multiplexer_.getRateLimiter();\n    if (rateLimiter != nullptr) {\n      rateLimiter->executeWhenReady(\n          [this, func = std::move(func), executor = multiplexer_.getExecutor()](\n              std::unique_ptr<RateLimiter::Token> token) mutable {\n            co_withExecutor(\n                executor,\n                folly::coro::co_invoke(\n                    [this,\n                     token = std::move(token),\n                     func =\n                         std::move(func)]() mutable -> folly::coro::Task<void> {\n                      auto lock = co_await mutex_.co_scoped_lock();\n                      co_await func();\n                      pendingAsyncCalls_--;\n                      maybeDelete(lock);\n                    }))\n                .start();\n          },\n          multiplexer_.getExecutor());\n    } else {\n      co_withExecutor(\n          multiplexer_.getExecutor(),\n          folly::coro::co_invoke(\n              [this,\n               func = std::move(func)]() mutable -> folly::coro::Task<void> {\n                auto lock = co_await mutex_.co_scoped_lock();\n                co_await func();\n                pendingAsyncCalls_--;\n                maybeDelete(lock);\n              }))\n          .start();\n    }\n  }\n\n  ChannelState getReceiverState() {\n    return detail::getReceiverState(receiver_.get());\n  }\n\n  void closeAllSubscriptions(CloseResult closeResult) {\n    for (auto& [key, subscription] : subscriptions_) {\n      auto& sender = std::get<FanoutSender<OutputValueType>>(subscription);\n      std::move(sender).close(\n          closeResult.exception.has_value()\n              ? closeResult.exception.value()\n              : exception_wrapper());\n    }\n    totalSubscriptions_.fetch_sub(subscriptions_.size());\n    subscriptions_.clear();\n  }\n\n  using SubscriptionMap = folly::F14FastMap<\n      KeyType,\n      std::tuple<FanoutSender<OutputValueType>, KeyContextType>>;\n\n  coro::Mutex mutex_;\n\n  // The above coro mutex must be acquired before accessing this state.\n  ChannelBridgePtr<InputValueType> receiver_;\n  SubscriptionMap subscriptions_;\n  bool handleDeleted_{false};\n\n  // The above coro mutex does not need to be acquired before accessing this\n  // state.\n  MultiplexerType multiplexer_;\n  std::atomic<uint64_t> totalSubscriptions_; // Includes pending subscriptions\n  std::atomic<uint64_t> pendingAsyncCalls_;\n};\n} // namespace detail\n\ntemplate <typename MultiplexerType, typename InputReceiverType>\nMultiplexChannel<MultiplexerType> createMultiplexChannel(\n    MultiplexerType multiplexer, InputReceiverType inputReceiver) {\n  auto* processor = new detail::MultiplexChannelProcessor<MultiplexerType>(\n      std::move(multiplexer));\n  processor->start(std::move(inputReceiver));\n  return MultiplexChannel<MultiplexerType>(processor);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/MultiplexChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <range/v3/view/map.hpp>\n#include <folly/channels/Channel.h>\n#include <folly/channels/FanoutSender.h>\n#include <folly/channels/OnClosedException.h>\n#include <folly/container/F14Map.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/experimental/channels/detail/MultiplexerTraits.h>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename MultiplexerType>\nclass MultiplexChannel;\n\n/**\n * Creates a new multiplex channel that multiplexes updates from a single input\n * receiver to one or more keyed subscriptions.\n *\n * The creator of a multiplex channel must pass a Multiplexer class that\n * implements the following functions:\n *\n *   folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *   std::shared_ptr<RateLimiter> getRateLimiter(); // Can return nullptr\n *\n *   // This function is called for each call to subscribe on the multiplex\n *   // channel object. It returns a vector of output values that should be sent\n *   // directly to the new output receiver for that subscription.\n *   folly::coro::Task<std::vector<OutputValueType>> onNewSubscription(\n *       KeyType key,\n *       KeyContextType& keyContext,\n *       SubscriptionArgType subscriptionArg);\n *\n *   // This function is called with each value fromm the given input receiver.\n *   // This function sends any corresponding values to the relevant output\n *   // receivers, using the subscriptions parameter.\n *   folly::coro::Task<void> onInputValue(\n *       Try<InputValueType> inputValue,\n *       MultiplexedSubscriptions<MultiplexerType>& subscriptions);\n *\n * Example:\n *\n *   struct InputValue {\n *     std::string key;\n *     int64_t value;\n *   };\n *\n *   struct NoContext {}\n *   struct NoSubscriptionArg {};\n *\n *   class Multiplexer {\n *    public:\n *     explicit Multiplexer(\n *         folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n *         : executor_(std::move(executor)) {}\n *\n *    folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() {\n *      return executor_;\n *    }\n *\n *    std::shared_ptr<RateLimiter> getRateLimiter() {\n *      return nullptr; // No rate limiting\n *    }\n *\n *    folly::coro::Task<std::vector<OutputValueType>> onNewSubscription(\n *        std::string key,\n *        NoContext&,\n *        NoSubscriptionArg&) {\n *      co_return std::vector<int64_t>(); // No initial values\n *    }\n *\n *    folly::coro::Task<void> onInputValue(\n *        Try<InputValue> inputValue,\n *        MultiplexedSubscriptions<Multiplexer>& subscriptions) {\n *      if (subscriptions.hasSubscription(inputValue->key)) {\n *        subscriptions.write(inputValue->key, inputValue->value);\n *      }\n *      co_return;\n *    }\n *\n *    private:\n *     folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n *   }\n *\n *   // Function that returns a receiver:\n *   Receiver<InputValue> getInputReceiver();\n *\n *   // Function that returns an executor\n *   folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *   auto multiplexChannel = createMultiplexChannel(\n *       Multiplexer(getExecutor()),\n *       getInputReceiver());\n *\n *   auto receiver1a = multiplexChannel.subscribe(\"one\");\n *   auto receiver1b = multiplexChannel.subscribe(\"one\");\n *   auto receiver2a = multiplexChannel.subscribe(\"two\");\n */\ntemplate <typename MultiplexerType, typename InputReceiverType>\nMultiplexChannel<MultiplexerType> createMultiplexChannel(\n    MultiplexerType multiplexer, InputReceiverType inputReceiver);\n\nnamespace detail {\ntemplate <typename MultiplexerType>\nclass MultiplexChannelProcessor;\n} // namespace detail\n\n/**\n * A multiplex channel allows multiplexing updates from a single input receiver\n * to one or more keyed subscriptions.\n */\ntemplate <typename MultiplexerType>\nclass MultiplexChannel {\n  using TProcessor = detail::MultiplexChannelProcessor<MultiplexerType>;\n  using MultiplexerTypeTraits = detail::MultiplexerTraits<MultiplexerType>;\n\n  using KeyType = typename MultiplexerTypeTraits::KeyType;\n  using KeyContextType = typename MultiplexerTypeTraits::KeyContextType;\n  using SubscriptionArgType =\n      typename MultiplexerTypeTraits::SubscriptionArgType;\n  using OutputValueType = typename MultiplexerTypeTraits::OutputValueType;\n\n public:\n  MultiplexChannel(MultiplexChannel&& other) noexcept;\n  MultiplexChannel& operator=(MultiplexChannel&& other) noexcept;\n  ~MultiplexChannel();\n\n  /**\n   * Returns whether this MultiplexChannel is a valid object. This will return\n   * false if the object was moved from.\n   */\n  explicit operator bool() const;\n\n  /**\n   * Returns a new output receiver for the given key.\n   */\n  Receiver<OutputValueType> subscribe(\n      KeyType key, SubscriptionArgType subscriptionArg);\n\n  /**\n   * Removes keys with no subscribers, and returns their contexts.\n   */\n  folly::coro::Task<std::vector<std::pair<KeyType, KeyContextType>>>\n  clearUnusedSubscriptions();\n\n  /**\n   * Returns whether this multiplex channel has any subscribers for any keys.\n   * Note that if any output receivers returned from subscribe have been\n   * destroyed, such subscriptions will still be considered to exist until the\n   * clearUnusedSubscriptions function is called.\n   */\n  bool anySubscribers() const;\n\n  /**\n   * Closes the multiplex channel.\n   */\n  void close(exception_wrapper ex = exception_wrapper()) &&;\n\n private:\n  template <typename Multiplexer, typename InputValueType>\n  friend MultiplexChannel<Multiplexer> createMultiplexChannel(\n      Multiplexer, InputValueType);\n\n  explicit MultiplexChannel(TProcessor* processor);\n\n  TProcessor* processor_;\n};\n\n/**\n * A class that allows one to see which keys are subscribed, and to write\n * values for particular subscriptions. This is passed to the onInputValue\n * function of the user-provided Multiplexer class.\n */\ntemplate <typename MultiplexerType>\nclass MultiplexedSubscriptions {\n public:\n  using MultiplexerTypeTraits = detail::MultiplexerTraits<MultiplexerType>;\n  using KeyType = typename MultiplexerTypeTraits::KeyType;\n  using KeyContextType = typename MultiplexerTypeTraits::KeyContextType;\n  using OutputValueType = typename MultiplexerTypeTraits::OutputValueType;\n\n  friend class detail::MultiplexChannelProcessor<MultiplexerType>;\n\n  /**\n   * Returns whether or not a subscription exists for the given key.\n   */\n  bool hasSubscription(const KeyType& key);\n\n  /**\n   * Returns a reference to the context object for the given key.\n   */\n  KeyContextType& getKeyContext(const KeyType& key);\n\n  /**\n   * Sends a value to all subscribers of a given key.\n   */\n  template <typename U = OutputValueType>\n  void write(const KeyType& key, U&& value);\n\n  /**\n   * Closes all subscribers for the given key.\n   */\n  void close(const KeyType& key, exception_wrapper ex);\n\n  /**\n   * Returns a view containing a list of subscribed keys.\n   */\n  auto getAllSubscriptionKeys() { return subscriptions_ | ranges::views::keys; }\n\n private:\n  using SubscriptionMap = folly::F14FastMap<\n      KeyType,\n      std::tuple<FanoutSender<OutputValueType>, KeyContextType>>&;\n\n  explicit MultiplexedSubscriptions(SubscriptionMap& subscriptions);\n\n  void ensureKeyExists(const KeyType& key);\n\n  SubscriptionMap& subscriptions_;\n  folly::F14FastSet<KeyType> closedSubscriptionKeys_;\n};\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/MultiplexChannel-inl.h>\n"
  },
  {
    "path": "folly/channels/OnClosedException.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <exception>\n\nnamespace folly {\nnamespace channels {\n\n/**\n * An OnClosedException passed to a transform or multiplex callback indicates\n * that the input channel was closed. An OnClosedException can also be thrown by\n * a transform or multiplex callback, which will close the output channel.\n */\nstruct OnClosedException : public std::exception {\n  const char* what() const noexcept override {\n    return \"The channel has been closed.\";\n  }\n};\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Producer-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fmt/format.h>\n#include <folly/CancellationToken.h>\n#include <folly/channels/Channel.h>\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/Producer.h>\n#include <folly/coro/Task.h>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename TValue>\nProducer<TValue>::KeepAlive::KeepAlive(Producer<TValue>* ptr) : ptr_(ptr) {}\n\ntemplate <typename TValue>\nProducer<TValue>::KeepAlive::~KeepAlive() {\n  if (ptr_ && --ptr_->refCount_ == 0) {\n    auto deleteTask =\n        folly::coro::co_invoke([ptr = ptr_]() -> folly::coro::Task<void> {\n          delete ptr;\n          co_return;\n        });\n    co_withExecutor(ptr_->getExecutor(), std::move(deleteTask)).start();\n  }\n}\n\ntemplate <typename TValue>\nProducer<TValue>::KeepAlive::KeepAlive(\n    Producer<TValue>::KeepAlive&& other) noexcept\n    : ptr_(std::exchange(other.ptr_, nullptr)) {}\n\ntemplate <typename TValue>\ntypename Producer<TValue>::KeepAlive& Producer<TValue>::KeepAlive::operator=(\n    Producer<TValue>::KeepAlive&& other) noexcept {\n  if (&other == this) {\n    return *this;\n  }\n  ptr_ = std::exchange(other.ptr_, nullptr);\n  return *this;\n}\n\ntemplate <typename TValue>\nProducer<TValue>::Producer(\n    Sender<TValue> sender,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n    : sender_(std::move(detail::senderGetBridge(sender))),\n      executor_(std::move(executor)) {\n  CHECK(sender_->senderWait(this));\n}\n\ntemplate <typename TValue>\nvoid Producer<TValue>::write(TValue value) {\n  executor_->add([this, value = std::move(value)]() mutable {\n    sender_->senderPush(std::move(value));\n  });\n}\n\ntemplate <typename TValue>\nvoid Producer<TValue>::close(std::optional<exception_wrapper> ex) {\n  executor_->add([this, ex = std::move(ex)]() mutable {\n    if (ex.has_value()) {\n      sender_->senderClose(std::move(ex.value()));\n    } else {\n      sender_->senderClose();\n    }\n  });\n}\n\ntemplate <typename TValue>\nbool Producer<TValue>::isClosed() {\n  return sender_->isSenderClosed();\n}\n\ntemplate <typename TValue>\nfolly::Executor::KeepAlive<folly::SequencedExecutor>\nProducer<TValue>::getExecutor() {\n  return executor_;\n}\n\ntemplate <typename TValue>\ntypename Producer<TValue>::KeepAlive Producer<TValue>::getKeepAlive() {\n  refCount_.fetch_add(1, std::memory_order_relaxed);\n  return KeepAlive(this);\n}\n\ntemplate <typename TValue>\nvoid Producer<TValue>::consume(detail::ChannelBridgeBase*) {\n  co_withExecutor(getExecutor(), onClosed()).start([=, this](auto) {\n    // Decrement ref count\n    KeepAlive(this);\n  });\n}\n\ntemplate <typename TValue>\nvoid Producer<TValue>::canceled(detail::ChannelBridgeBase* bridge) {\n  consume(bridge);\n}\n\nnamespace detail {\ntemplate <typename TProducer>\nclass ProducerImpl : public TProducer {\n  template <typename ProducerType, typename... Args>\n  friend Receiver<typename ProducerType::ValueType> makeProducer(\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      Args&&... args);\n\n public:\n  using TProducer::TProducer;\n\n private:\n  void ensureMakeProducerUsedForCreation() override {}\n};\n} // namespace detail\n\ntemplate <typename TProducer, typename... Args>\nReceiver<typename TProducer::ValueType> makeProducer(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    Args&&... args) {\n  using TValue = typename TProducer::ValueType;\n  auto [receiver, sender] = Channel<TValue>::create();\n  new detail::ProducerImpl<TProducer>(\n      std::move(sender), std::move(executor), std::forward<Args>(args)...);\n  return std::move(receiver);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Producer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/ChannelCallbackHandle.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\n/**\n * A Producer is a base class for an object that produces a channel. The\n * subclass can call write to write a new value to the channel, and close to\n * close the channel. It is a useful way to generate output values for a\n * receiver, without having to keep alive an extraneous object that produces\n * those values.\n *\n * When the consumer of the channel stops consuming, the onClosed function will\n * be called. The subclass should cancel any ongoing work in this function.\n * After onCancelled is called, the object will be deleted once the last\n * outstanding KeepAlive is destroyed.\n *\n * Example:\n *   // Function that returns an executor\n *   folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *   // Function that returns output values\n *   std::vector<int> getLatestOutputValues();\n *\n *   // Example producer implementation\n *   class PollingProducer : public Producer<int> {\n *    public:\n *     PollingProducer(\n *         Sender<int> sender,\n *         folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n *         : Producer<int>(std::move(sender), std::move(executor)) {\n *       // Start polling for values.\n *       co_withExecutor(getExecutor(), folly::coro::co_withCancellation(\n *             cancelSource_.getToken(),\n *             [=, keepAlive = getKeepAlive()]() {\n *               return pollForOutputValues();\n *             })\n *         )\n *         .start();\n *     }\n *\n *     folly::coro::Task<void> onClosed() override {\n *       // The consumer has stopped consuming our values. Stop polling.\n *       cancelSource_.requestCancellation();\n *     }\n *\n *    private:\n *     folly::coro::Task<void> pollForOutputValues() {\n *       auto cancelToken = co_await folly::coro::co_current_cancellation_token;\n *       while (!cancelToken.isCancellationRequested()) {\n *         auto outputValues = getLatestOutputValues();\n *         for (auto& outputValue : outputValues) {\n *           write(std::move(outputValue));\n *         }\n *       }\n *       co_await folly::coro::sleep(std::chrono::seconds(1));\n *     }\n *\n *     folly::CancellationSource cancelSource_;\n *   };\n *\n *   // Producer usage\n *   Receiver<int> receiver = makeProducer<PollingProducer>(getExecutor());\n */\ntemplate <typename TValue>\nclass Producer : public detail::IChannelCallback {\n public:\n  using ValueType = TValue;\n\n protected:\n  /**\n   * This object will ensure that the corresponding Producer that created it\n   * will not be destroyed.\n   */\n  class KeepAlive {\n   public:\n    ~KeepAlive();\n    KeepAlive(KeepAlive&&) noexcept;\n    KeepAlive& operator=(KeepAlive&&) noexcept;\n\n   private:\n    friend class Producer<TValue>;\n\n    explicit KeepAlive(Producer<TValue>* ptr);\n\n    Producer<TValue>* ptr_;\n  };\n\n  Producer(\n      Sender<TValue> sender,\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor);\n  virtual ~Producer() override = default;\n\n  /**\n   * Writes a value into the channel.\n   */\n  void write(TValue value);\n\n  /**\n   * Closes the channel.\n   */\n  void close(std::optional<exception_wrapper> ex = std::nullopt);\n\n  /**\n   * Returns whether or not this producer is closed or cancelled.\n   */\n  bool isClosed();\n\n  /**\n   * Returns the executor used for this producer.\n   */\n  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n\n  /**\n   * Returns a KeepAlive object. This object will not be destroyed before all\n   * KeepAlive objects are destroyed.\n   */\n  KeepAlive getKeepAlive();\n\n  /**\n   * Called when the corresponding receiver is cancelled, or the sender is\n   * closed.\n   */\n  virtual folly::coro::Task<void> onClosed() { co_return; }\n\n  /**\n   * If you get an error that this function is not implemented, do not\n   * implement it. Instead, create your object with makeProducer\n   * below.\n   */\n  virtual void ensureMakeProducerUsedForCreation() = 0;\n\n private:\n  template <typename TProducer, typename... Args>\n  friend Receiver<typename TProducer::ValueType> makeProducer(\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      Args&&... args);\n\n  void consume(detail::ChannelBridgeBase* bridge) override;\n\n  void canceled(detail::ChannelBridgeBase* bridge) override;\n\n  detail::ChannelBridgePtr<TValue> sender_;\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  std::atomic<int> refCount_{1};\n};\n\n/**\n * Creates a new object that extends the Producer class, and returns a receiver.\n * The receiver will receive any values produced by the producer. See the\n * description of the Producer class for information on how to implement a\n * producer.\n */\ntemplate <typename TProducer, typename... Args>\nReceiver<typename TProducer::ValueType> makeProducer(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    Args&&... args);\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/Producer-inl.h>\n"
  },
  {
    "path": "folly/channels/ProxyChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/ProxyChannel.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename ValueType>\nProxyChannel<ValueType>::ProxyChannel(TProcessor* processor)\n    : processor_(processor) {}\n\ntemplate <typename ValueType>\nProxyChannel<ValueType>::ProxyChannel(ProxyChannel&& other) noexcept\n    : processor_(std::exchange(other.processor_, nullptr)) {}\n\ntemplate <typename ValueType>\nProxyChannel<ValueType>& ProxyChannel<ValueType>::operator=(\n    ProxyChannel&& other) noexcept {\n  if (&other == this) {\n    return *this;\n  }\n  if (processor_) {\n    std::move(*this).close();\n  }\n  processor_ = std::exchange(other.processor_, nullptr);\n  return *this;\n}\n\ntemplate <typename ValueType>\nProxyChannel<ValueType>::~ProxyChannel() {\n  if (processor_) {\n    std::move(*this).close();\n  }\n}\n\ntemplate <typename ValueType>\nProxyChannel<ValueType>::operator bool() const {\n  return processor_;\n}\n\ntemplate <typename ValueType>\nvoid ProxyChannel<ValueType>::setInputReceiver(Receiver<ValueType> receiver) {\n  processor_->setInputReceiver(std::move(receiver));\n}\n\ntemplate <typename ValueType>\nvoid ProxyChannel<ValueType>::removeInputReceiver() {\n  processor_->removeInputReceiver();\n}\n\ntemplate <typename ValueType>\nvoid ProxyChannel<ValueType>::close(folly::exception_wrapper&& ex) && {\n  processor_->destroyHandle(\n      ex ? detail::CloseResult(std::move(ex)) : detail::CloseResult());\n  processor_ = nullptr;\n}\n\nnamespace detail {\n\n/**\n * This object does the proxying of values from the input receiver to the output\n * receiver.\n */\ntemplate <typename ValueType>\nclass ProxyChannelProcessor : public IChannelCallback {\n private:\n  struct State {\n    explicit State(ChannelBridgePtr<ValueType> _sender)\n        : sender(std::move(_sender)) {}\n\n    ChannelState getSenderState() {\n      return detail::getSenderState(sender.get());\n    }\n\n    // The output sender for the proxy channel.\n    ChannelBridgePtr<ValueType> sender;\n\n    // The current input receiver for the proxy channel.\n    ChannelBridge<ValueType>* receiver{nullptr};\n\n    // The refcount for this proxy channel. The handle (if not yet destroyed),\n    // the sender (if not yet cancelled), the current input receiver (if any),\n    // and any previous input receivers not yet joined (if any) will contribute\n    // to this refcount. It starts at 2, since a new ProxyChannel always has\n    // one handle, one output receiver, and no input receivers.\n    size_t refCount{2};\n  };\n\n  using WLockedStatePtr = typename folly::Synchronized<State>::WLockedPtr;\n\n public:\n  ProxyChannelProcessor(\n      Sender<ValueType> sender,\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n      : executor_(std::move(executor)),\n        state_(State(std::move(detail::senderGetBridge(sender)))) {\n    auto state = state_.wlock();\n    CHECK(state->sender->senderWait(this));\n  }\n\n  /**\n   * Sets a new input receiver (removing the old input receiver, if any).\n   */\n  void setInputReceiver(Receiver<ValueType> receiver) {\n    auto state = state_.wlock();\n    if (state->getSenderState() != ChannelState::Active) {\n      return;\n    }\n    auto [unbufferedReceiver, buffer] =\n        detail::receiverUnbuffer(std::move(receiver));\n    cancelInputReceiverIfExists(state);\n    auto receiverPtr = unbufferedReceiver.release();\n    state->receiver = receiverPtr;\n    state->refCount++;\n    processAllAvailableValues(std::move(state), receiverPtr, std::move(buffer));\n  }\n\n  /**\n   * Removes the current input receiver.\n   */\n  void removeInputReceiver() {\n    auto state = state_.wlock();\n    if (state->getSenderState() != ChannelState::Active) {\n      return;\n    }\n    cancelInputReceiverIfExists(state);\n  }\n\n  /**\n   * Called when the user's ProxyChannel object is destroyed.\n   */\n  void destroyHandle(CloseResult closeResult) {\n    processHandleDestroyed(state_.wlock(), std::move(closeResult));\n  }\n\n  /**\n   * Called when one of the channels we are listening to has an update (either\n   * a value from an input receiver or a cancellation from the output receiver).\n   */\n  void consume(ChannelBridgeBase* bridge) override {\n    executor_->add([=, this]() {\n      auto state = state_.wlock();\n      if (bridge == state->sender.get()) {\n        // The consumer of the output receiver has stopped consuming.\n        state->sender->senderClose();\n        processSenderCancelled(std::move(state));\n      } else {\n        // One or more values are now available from an input receiver.\n        auto* receiver = static_cast<ChannelBridge<ValueType>*>(bridge);\n        processAllAvailableValues(std::move(state), receiver);\n      }\n    });\n  }\n\n  /**\n   * Called after we cancelled one of the channels we were listening to (either\n   * the sender or an input receiver).\n   */\n  void canceled(ChannelBridgeBase* bridge) override {\n    executor_->add([=, this]() {\n      auto state = state_.wlock();\n      if (bridge == state->sender.get()) {\n        // We previously cancelled the sender due to an input receiver closure.\n        // Process the cancellation for the sender.\n        CHECK(state->getSenderState() == ChannelState::CancellationTriggered);\n        processSenderCancelled(std::move(state));\n      } else {\n        // We previously cancelled this input receiver. Process the cancellation\n        // for this input receiver.\n        auto* receiver = static_cast<ChannelBridge<ValueType>*>(bridge);\n        processReceiverCancelled(std::move(state), receiver, CloseResult());\n      }\n    });\n  }\n\n protected:\n  /**\n   * Processes all available values from the current input receiver channel\n   * (starting from the provided buffer, if present).\n   *\n   * If an value was received indicating that the input channel has been closed\n   * we will process cancellation for the input receiver.\n   */\n  void processAllAvailableValues(\n      WLockedStatePtr state,\n      ChannelBridge<ValueType>* receiver,\n      std::optional<ReceiverQueue<ValueType>> buffer = std::nullopt) {\n    CHECK_NOTNULL(receiver);\n    if (!receiver->isReceiverCancelled()) {\n      CHECK_EQ(receiver, state->receiver);\n    }\n    auto closeResult = receiver->isReceiverCancelled()\n        ? CloseResult()\n        : (buffer.has_value() ? processValues(state, std::move(buffer.value()))\n                              : std::nullopt);\n    while (!closeResult.has_value()) {\n      if (receiver->receiverWait(this)) {\n        // There are no more values available right now. We will stop processing\n        // until the channel fires the consume() callback (indicating that more\n        // values are available).\n        break;\n      }\n      auto values = receiver->receiverGetValues();\n      CHECK(!values.empty());\n      closeResult = processValues(state, std::move(values));\n    }\n    if (closeResult.has_value()) {\n      // The receiver received a value indicating channel closure.\n      receiver->receiverCancel();\n      processReceiverCancelled(\n          std::move(state), receiver, std::move(closeResult.value()));\n    }\n  }\n\n  /**\n   * Processes the given set of values for an input receiver. Returns a\n   * CloseResult if the given channel was closed, so the caller can stop\n   * attempting to process values from it.\n   */\n  std::optional<CloseResult> processValues(\n      WLockedStatePtr& state, ReceiverQueue<ValueType> values) {\n    while (!values.empty()) {\n      auto inputResult = std::move(values.front());\n      values.pop();\n      if (inputResult.hasValue()) {\n        // We have received a normal value from an input receiver. Write it to\n        // the output receiver.\n        state->sender->senderPush(std::move(inputResult.value()));\n      } else {\n        // The input receiver was closed.\n        return inputResult.hasException()\n            ? CloseResult(std::move(inputResult.exception()))\n            : CloseResult();\n      }\n    }\n    return std::nullopt;\n  }\n\n  /**\n   * Processes the cancellation of an input receiver.\n   */\n  void processReceiverCancelled(\n      WLockedStatePtr state,\n      ChannelBridge<ValueType>* receiver,\n      CloseResult closeResult) {\n    CHECK(receiver->isReceiverCancelled());\n    if (receiver == state->receiver &&\n        state->getSenderState() == ChannelState::Active) {\n      if (closeResult.exception.has_value()) {\n        state->sender->senderClose(std::move(closeResult.exception.value()));\n      } else {\n        state->sender->senderClose();\n      }\n    }\n    if (state->receiver == receiver) {\n      state->receiver = nullptr;\n    }\n    (ChannelBridgePtr<ValueType>(receiver)); // Delete the receiver\n    state->refCount--;\n    maybeDelete(std::move(state));\n  }\n\n  /**\n   * Processes the cancellation of the sender (indicating that the consumer of\n   * the output receiver has stopped consuming). We will trigger cancellation\n   * for the input receiver if it is not already cancelled.\n   */\n  void processSenderCancelled(WLockedStatePtr state) {\n    CHECK(state->getSenderState() == ChannelState::CancellationTriggered);\n    state->sender.reset();\n    state->refCount--;\n    cancelInputReceiverIfExists(state);\n    maybeDelete(std::move(state));\n  }\n\n  /**\n   * Processes the destruction of the user's ProxyChannel object.  We will\n   * close the sender and trigger cancellation for the input receiver (if any).\n   */\n  void processHandleDestroyed(WLockedStatePtr state, CloseResult closeResult) {\n    if (state->getSenderState() == ChannelState::Active) {\n      if (closeResult.exception.has_value()) {\n        state->sender->senderClose(std::move(closeResult.exception.value()));\n      } else {\n        state->sender->senderClose();\n      }\n    }\n    cancelInputReceiverIfExists(state);\n    state->refCount--;\n    maybeDelete(std::move(state));\n  }\n\n  /**\n   * Cancels the current input receiver if it exists.\n   */\n  void cancelInputReceiverIfExists(WLockedStatePtr& state) {\n    if (state->receiver != nullptr) {\n      CHECK(!state->receiver->isReceiverCancelled());\n      state->receiver->receiverCancel();\n      state->receiver = nullptr;\n    }\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * sender, the current input receiver, and all previous input receivers, and\n   * if the user's ProxyChannel object was destroyed.\n   */\n  void maybeDelete(WLockedStatePtr state) {\n    if (state->refCount == 0) {\n      CHECK_EQ(state->sender.get(), static_cast<void*>(NULL));\n      CHECK_EQ(state->receiver, static_cast<void*>(NULL));\n      state.unlock();\n      delete this;\n    }\n  }\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  folly::Synchronized<State> state_;\n};\n} // namespace detail\n\ntemplate <typename ValueType>\nstd::pair<Receiver<ValueType>, ProxyChannel<ValueType>> createProxyChannel(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor) {\n  auto [receiver, sender] = Channel<ValueType>::create();\n  auto* processor = new detail::ProxyChannelProcessor<ValueType>(\n      std::move(sender), std::move(executor));\n  return std::make_pair(\n      std::move(receiver), ProxyChannel<ValueType>(processor));\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/ProxyChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\ntemplate <typename ValueType>\nclass ProxyChannelProcessor;\n}\n\n/**\n * A proxy allows one to create a channel whose input is proxied from the output\n * of another channel, which can change over time. This is more memory-efficient\n * than using a MergeChannel with one input receiver.\n *\n * Example:\n *\n *  // Example function that returns a receiver for a given entity:\n *  Receiver<int> subscribe(std::string entity);\n *\n *  // Example function that returns an executor\n *  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *  auto [outputReceiver, proxyChannel]\n *      = createProxyChannel<int>(getExecutor());\n *  proxyChannel.setInputReceiver(subscribe(\"abc\"));\n *  proxyChannel.setInputReceiver(subscribe(\"def\"));\n *  proxyChannel.removeInputReceiver();\n *  proxyChannel.setInputReceiver(subscribe(\"ghi\"));\n *  std::move(proxyChannel).close();\n */\ntemplate <typename ValueType>\nclass ProxyChannel {\n  using TProcessor = detail::ProxyChannelProcessor<ValueType>;\n\n public:\n  explicit ProxyChannel(detail::ProxyChannelProcessor<ValueType>* processor);\n  ProxyChannel(const ProxyChannel&) = delete;\n  ProxyChannel& operator=(const ProxyChannel&) = delete;\n  ProxyChannel(ProxyChannel&& other) noexcept;\n  ProxyChannel& operator=(ProxyChannel&& other) noexcept;\n  ~ProxyChannel();\n\n  /**\n   * Returns whether this ProxyChannel is a valid object.\n   */\n  explicit operator bool() const;\n\n  /**\n   * Sets a new input receiver. As soon as this function returns, values from\n   * the old input receiver (if any) will no longer be sent to the output\n   * receiver. Values from the new input receiver will start being sent to the\n   * output receiver, unless a previous input receiver was closed.\n   */\n  void setInputReceiver(Receiver<ValueType> receiver);\n\n  /**\n   * Removes the current input receiver (if any). As soon as this function\n   * returns, values from the old input receiver (if any) will no longer be sent\n   * to the output receiver.\n   */\n  void removeInputReceiver();\n\n  /**\n   * Closes the proxy channel.\n   */\n  void close(folly::exception_wrapper&& ex = {}) &&;\n\n private:\n  TProcessor* processor_;\n};\n\n/**\n * Creates a new proxy channel.\n *\n * @param executor: The SequencedExecutor to use for proxying values.\n */\ntemplate <typename ValueType>\nstd::pair<Receiver<ValueType>, ProxyChannel<ValueType>> createProxyChannel(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor);\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/ProxyChannel-inl.h>\n"
  },
  {
    "path": "folly/channels/RateLimiter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Function.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\n/**\n * A rate-limiter used by the channels framework to limit the number of\n * in-flight requests.\n *\n * A default implementation is provided in MaxConcurrentRateLimiter.h but users\n * can provide custom rate-limiters.\n */\nclass RateLimiter : public std::enable_shared_from_this<RateLimiter> {\n public:\n  class Token;\n  virtual ~RateLimiter() = default;\n\n  /**\n   * Executes the given function when there is capacity available in the\n   * rate-limiter.\n   *\n   * The function is considered finished when the token is destroyed.\n   */\n  virtual void executeWhenReady(\n      folly::Function<void(std::unique_ptr<Token>)> function,\n      Executor::KeepAlive<SequencedExecutor> executor) = 0;\n};\n\n/**\n * A token on destruction signals termination of the user provided function. So\n * it's expected that a derived class override the destructor to provide the\n * desired functionality.  Or piggyback on destruction of the compiler generated\n * overridden destructor.\n */\nclass RateLimiter::Token {\n public:\n  virtual ~Token() = default;\n};\n\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Transform-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/experimental/channels/detail/Utility.h>\n\nnamespace folly {\nnamespace channels {\n\nnamespace detail {\n\n/**\n * This object transforms values from the input receiver to the output receiver.\n * It is not an object that the user is aware of or holds a pointer to. The\n * lifetime of this object is described by the following state machine.\n *\n * Both the sender and receiver can be in one of three states: Active,\n * CancellationTriggered, or CancellationProcessed. When both the sender and\n * receiver reach the CancellationProcessed state, this object is deleted.\n *\n * When the input receiver receives a value indicating that the channel has been\n * closed, the state of the receiver transitions from Active directly to\n * CancellationProcessed and the state of the sender transitions from Active to\n * CancellationTriggered. Once we receive a callback indicating the sender's\n * cancellation signal has been received, the sender's state is transitioned\n * from CancellationTriggered to CancellationProcessed (and the object is\n * deleted).\n *\n * When the sender receives notification that the consumer of the output\n * receiver has stopped consuming, the state of the sender transitions from\n * Active directly to CancellationProcessed, and the state of the input receiver\n * transitions from Active to CancellationTriggered. Once we receive a callback\n * indicating that the input receiver's cancellation signal has been received,\n * the input receiver's state is transitioned from CancellationTriggered to\n * to CancellationProcessed (and the object is deleted).\n */\ntemplate <\n    typename InputValueType,\n    typename OutputValueType,\n    typename TransformerType>\nclass TransformProcessorBase : public IChannelCallback {\n public:\n  TransformProcessorBase(\n      Sender<OutputValueType> sender, TransformerType transformer)\n      : sender_(std::move(senderGetBridge(sender))),\n        transformer_(std::move(transformer)) {}\n\n  template <typename ReceiverType>\n  void startTransform(ReceiverType receiver) {\n    executeWhenReady([=, this, receiver = std::move(receiver)](\n                         std::unique_ptr<RateLimiter::Token> token) mutable {\n      runOperationWithSenderCancellation(\n          transformer_.getExecutor(),\n          this->sender_,\n          false /* alreadyStartedWaiting */,\n          this /* channelCallbackToRestore */,\n          startTransformImpl(std::move(receiver)),\n          std::move(token));\n    });\n  }\n\n protected:\n  /**\n   * Starts transforming values from the input receiver and sending the\n   * resulting transformed values to the output receiver.\n   *\n   * @param inputReceiver: The input receiver to transform values from.\n   */\n  folly::coro::Task<void> startTransformImpl(\n      Receiver<InputValueType> receiver) {\n    auto [unbufferedInputReceiver, buffer] =\n        detail::receiverUnbuffer(std::move(receiver));\n    receiver_ = std::move(unbufferedInputReceiver);\n    co_await processAllAvailableValues(std::move(buffer));\n  }\n\n  /**\n   * This is called when one of the channels we are listening to has an update\n   * (either a value from the input receiver or a cancellation signal from the\n   * sender).\n   */\n  void consume(ChannelBridgeBase* bridge) override {\n    executeWhenReady([=, this](std::unique_ptr<RateLimiter::Token> token) {\n      if (bridge == receiver_.get()) {\n        // We have received new values from the input receiver.\n        CHECK_NE(getReceiverState(), ChannelState::CancellationProcessed);\n        runOperationWithSenderCancellation(\n            transformer_.getExecutor(),\n            this->sender_,\n            true /* alreadyStartedWaiting */,\n            this /* channelCallbackToRestore */,\n            processAllAvailableValues(),\n            std::move(token));\n      } else {\n        CHECK_NE(getSenderState(), ChannelState::CancellationProcessed);\n        // The consumer of the output receiver has stopped consuming.\n        if (getSenderState() == ChannelState::Active) {\n          sender_->senderClose();\n        }\n        processSenderCancelled();\n      }\n    });\n  }\n\n  /**\n   * This is called after we explicitly cancel one of the channels we are\n   * listening to.\n   */\n  void canceled(ChannelBridgeBase* bridge) override {\n    executeWhenReady([=, this](std::unique_ptr<RateLimiter::Token> token) {\n      if (bridge == receiver_.get()) {\n        // We previously cancelled the input receiver (because the consumer of\n        // the output receiver stopped consuming). Process the cancellation for\n        // the input receiver.\n        CHECK_EQ(getReceiverState(), ChannelState::CancellationTriggered);\n        runOperationWithSenderCancellation(\n            transformer_.getExecutor(),\n            this->sender_,\n            true /* alreadyStartedWaiting */,\n            this /* channelCallbackToRestore */,\n            processReceiverCancelled(CloseResult()),\n            std::move(token));\n      } else {\n        // We previously cancelled the sender due to the closure of the input\n        // receiver. Process the cancellation for the sender.\n        CHECK_EQ(getSenderState(), ChannelState::CancellationTriggered);\n        processSenderCancelled();\n      }\n    });\n  }\n\n  /**\n   * Processes all available values from the input receiver (starting from the\n   * provided buffer, if present).\n   *\n   * If a value was received indicating that the input channel has been closed\n   * (or if the transform function indicated that channel should be closed), we\n   * will process cancellation for the input receiver.\n   */\n  folly::coro::Task<void> processAllAvailableValues(\n      std::optional<ReceiverQueue<InputValueType>> buffer = std::nullopt) {\n    auto closeResult = buffer.has_value()\n        ? co_await processValues(std::move(buffer.value()))\n        : std::nullopt;\n    while (!closeResult.has_value()) {\n      if (receiver_->receiverWait(this)) {\n        // There are no more values available right now, but more values may\n        // come in the future. We will stop processing for now, until we\n        // re-start processing when the consume() callback is fired.\n        break;\n      }\n      auto values = receiver_->receiverGetValues();\n      CHECK(!values.empty());\n      closeResult = co_await processValues(std::move(values));\n    }\n    if (closeResult.has_value()) {\n      // The output receiver should be closed (either because the input receiver\n      // was closed or the transform function desired the closure of the output\n      // receiver).\n      receiver_->receiverCancel();\n      co_await processReceiverCancelled(std::move(closeResult.value()));\n    }\n  }\n\n  /**\n   * Processes the given set of values for the input receiver. If the output\n   * receiver should be closed (either because the input receiver was closed or\n   * the transform function desired the closure of the output receiver), a\n   * CloseResult is returned containing the exception (if any) that should be\n   * used to close the output receiver.\n   */\n  folly::coro::Task<std::optional<CloseResult>> processValues(\n      ReceiverQueue<InputValueType> values) {\n    auto cancelToken = co_await folly::coro::co_current_cancellation_token;\n    while (!values.empty()) {\n      auto inputResult = std::move(values.front());\n      values.pop();\n      bool inputClosed = !inputResult.hasValue();\n      if (!inputResult.hasValue() && !inputResult.hasException()) {\n        inputResult = Try<InputValueType>(OnClosedException());\n      }\n      auto outputGen = folly::makeTryWith([&]() {\n        return transformer_.transformValue(std::move(inputResult));\n      });\n      if (!outputGen.hasValue()) {\n        // The transform function threw an exception and was not a coroutine.\n        // We will close the output receiver.\n        co_return outputGen.template hasException<OnClosedException>()\n            ? CloseResult()\n            : CloseResult(std::move(outputGen.exception()));\n      }\n      while (true) {\n        auto outputResult =\n            co_await folly::coro::co_awaitTry(outputGen->next());\n        if (!outputResult.hasException() && !outputResult->has_value()) {\n          break;\n        }\n        if (cancelToken.isCancellationRequested()) {\n          co_return CloseResult();\n        }\n        if (!outputResult.hasException()) {\n          sender_->senderPush(std::move(outputResult->value()));\n        } else {\n          // The transform coroutine threw an exception. We will close the\n          // output receiver.\n          co_return outputResult.template hasException<OnClosedException>()\n              ? CloseResult()\n              : CloseResult(std::move(outputResult.exception()));\n        }\n      }\n      if (inputClosed) {\n        // The input receiver was closed, and the transform function did not\n        // explicitly close the output receiver. We will therefore close it\n        // anyway, as it does not make sense to keep it open when no future\n        // values will arrive.\n        co_return CloseResult();\n      }\n    }\n    co_return std::nullopt;\n  }\n\n  /**\n   * Process cancellation for the input receiver.\n   */\n  virtual folly::coro::Task<void> processReceiverCancelled(\n      CloseResult closeResult, bool noRetriesAllowed = false) = 0;\n\n  /**\n   * Process cancellation for the sender.\n   */\n  void processSenderCancelled() {\n    CHECK_EQ(getSenderState(), ChannelState::CancellationTriggered);\n    sender_ = nullptr;\n    if (getReceiverState() == ChannelState::Active) {\n      receiver_->receiverCancel();\n    }\n    maybeDelete();\n  }\n\n  /**\n   * Deletes this object if we have already processed cancellation for the\n   * receiver and the sender.\n   */\n  void maybeDelete() {\n    if (getReceiverState() == ChannelState::CancellationProcessed &&\n        getSenderState() == ChannelState::CancellationProcessed) {\n      delete this;\n    }\n  }\n\n  ChannelState getReceiverState() {\n    return detail::getReceiverState(receiver_.get());\n  }\n\n  ChannelState getSenderState() {\n    return detail::getSenderState(sender_.get());\n  }\n\n  void executeWhenReady(\n      folly::Function<void(std::unique_ptr<RateLimiter::Token>)> func) {\n    auto rateLimiter = transformer_.getRateLimiter();\n    if (rateLimiter != nullptr) {\n      rateLimiter->executeWhenReady(\n          std::move(func), transformer_.getExecutor());\n    } else {\n      transformer_.getExecutor()->add([func = std::move(func)]() mutable {\n        func(std::unique_ptr<RateLimiter::Token>(nullptr));\n      });\n    }\n  }\n\n  ChannelBridgePtr<InputValueType> receiver_;\n  ChannelBridgePtr<OutputValueType> sender_;\n  TransformerType transformer_;\n};\n\n/**\n * This subclass is used for simple transformations triggered by a call to the\n * transform function (i.e. with a single input receiver and no initialization\n * function).\n */\ntemplate <\n    typename InputValueType,\n    typename OutputValueType,\n    typename TransformerType>\nclass TransformProcessor\n    : public TransformProcessorBase<\n          InputValueType,\n          OutputValueType,\n          TransformerType> {\n public:\n  using Base =\n      TransformProcessorBase<InputValueType, OutputValueType, TransformerType>;\n  using Base::Base;\n\n private:\n  /**\n   * Process cancellation for the input receiver.\n   */\n  folly::coro::Task<void> processReceiverCancelled(\n      CloseResult closeResult, bool /* noRetriesAllowed */) override {\n    CHECK_EQ(this->getReceiverState(), ChannelState::CancellationTriggered);\n    this->receiver_ = nullptr;\n    if (this->getSenderState() == ChannelState::Active) {\n      if (closeResult.exception.has_value()) {\n        this->sender_->senderClose(std::move(closeResult.exception.value()));\n      } else {\n        this->sender_->senderClose();\n      }\n    }\n    this->maybeDelete();\n    co_return;\n  }\n};\n\n/**\n * This subclass is used for resumable transformations triggered by a call to\n * the resumableTransform function.\n */\ntemplate <\n    typename InitializeArg,\n    typename InputValueType,\n    typename OutputValueType,\n    typename TransformerType>\nclass ResumableTransformProcessor\n    : public TransformProcessorBase<\n          InputValueType,\n          OutputValueType,\n          TransformerType> {\n public:\n  using Base =\n      TransformProcessorBase<InputValueType, OutputValueType, TransformerType>;\n  using Base::Base;\n\n  void initialize(InitializeArg initializeArg) {\n    this->executeWhenReady(\n        [=, this, initializeArg = std::move(initializeArg)](\n            std::unique_ptr<RateLimiter::Token> token) mutable {\n          runOperationWithSenderCancellation(\n              this->transformer_.getExecutor(),\n              this->sender_,\n              false /* currentlyWaiting */,\n              this /* channelCallbackToRestore */,\n              initializeImpl(std::move(initializeArg)),\n              std::move(token));\n        });\n  }\n\n private:\n  /**\n   * Runs the user-provided initialization function to get a set of initial\n   * values and a receiver to continue transforming. This is called when the\n   * resumableTransform is created, and again whenever the previous input\n   * receiver closed without an exception.\n   */\n  folly::coro::Task<void> initializeImpl(InitializeArg initializeArg) {\n    auto cancelToken = co_await folly::coro::co_current_cancellation_token;\n    auto initializeResult = co_await folly::coro::co_awaitTry(\n        this->transformer_.initializeTransform(std::move(initializeArg)));\n    if (initializeResult.hasException()) {\n      auto closeResult =\n          initializeResult.template hasException<OnClosedException>()\n          ? CloseResult()\n          : CloseResult(std::move(initializeResult.exception()));\n      co_await processReceiverCancelled(\n          std::move(closeResult), true /* noRetriesAllowed */);\n      co_return;\n    }\n    auto [initialValues, inputReceiver] = std::move(initializeResult.value());\n    CHECK(inputReceiver)\n        << \"The initialize function of a resumableTransform returned an \"\n           \"invalid receiver.\";\n    if (cancelToken.isCancellationRequested()) {\n      // The sender was closed before we finished running the initialization\n      // function. We will ignore the results from that function and proceed\n      // to process cancellation for the receiver.\n      co_await processReceiverCancelled(\n          CloseResult(), true /* noRetriesAllowed */);\n      co_return;\n    }\n    for (auto& initialValue : initialValues) {\n      this->sender_->senderPush(std::move(initialValue));\n    }\n    co_await this->startTransformImpl(std::move(inputReceiver));\n  }\n\n  /**\n   * Process cancellation for the input receiver.\n   */\n  folly::coro::Task<void> processReceiverCancelled(\n      CloseResult closeResult, bool noRetriesAllowed) override {\n    if (this->receiver_) {\n      CHECK_EQ(this->getReceiverState(), ChannelState::CancellationTriggered);\n      this->receiver_ = nullptr;\n    }\n    auto cancelToken = co_await folly::coro::co_current_cancellation_token;\n    if (this->getSenderState() == ChannelState::Active &&\n        !cancelToken.isCancellationRequested()) {\n      if (!closeResult.exception.has_value()) {\n        // We were closed without an exception. We will close the sender without\n        // an exception.\n        this->sender_->senderClose();\n      } else if (\n          noRetriesAllowed ||\n          !closeResult.exception\n               ->is_compatible_with<ReinitializeException<InitializeArg>>()) {\n        // We were closed with an exception. We will close the sender with that\n        // exception.\n        this->sender_->senderClose(std::move(closeResult.exception.value()));\n      } else {\n        // We were closed with a ReinitializeException. We will re-run the\n        // user's initialization function and resume the resumableTransform.\n        auto* reinitializeEx =\n            closeResult.exception\n                ->get_mutable_exception<ReinitializeException<InitializeArg>>();\n        co_await initializeImpl(std::move(reinitializeEx->initializeArg));\n        co_return;\n      }\n    }\n    this->maybeDelete();\n  }\n};\n\ntemplate <bool Enabled>\nclass RateLimiterHolder;\n\ntemplate <>\nclass RateLimiterHolder<true> {\n public:\n  explicit RateLimiterHolder(std::shared_ptr<RateLimiter> rateLimiter)\n      : rateLimiter_(std::move(rateLimiter)) {}\n\n  std::shared_ptr<RateLimiter> getRateLimiter() { return rateLimiter_; }\n\n private:\n  std::shared_ptr<RateLimiter> rateLimiter_;\n};\n\ntemplate <>\nclass RateLimiterHolder<false> {\n public:\n  explicit RateLimiterHolder(std::shared_ptr<RateLimiter> rateLimiter) {\n    CHECK_EQ(rateLimiter.get(), static_cast<void*>(NULL));\n  }\n\n  std::shared_ptr<RateLimiter> getRateLimiter() { return nullptr; }\n};\n\ntemplate <\n    typename InputValueType,\n    typename OutputValueType,\n    typename TransformValueFunc,\n    bool RateLimiterEnabled>\nclass DefaultTransformer : public RateLimiterHolder<RateLimiterEnabled> {\n public:\n  DefaultTransformer(\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      TransformValueFunc transformValue,\n      std::shared_ptr<RateLimiter> rateLimiter)\n      : RateLimiterHolder<RateLimiterEnabled>(std::move(rateLimiter)),\n        executor_(std::move(executor)),\n        transformValue_(std::move(transformValue)) {}\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() {\n    return executor_;\n  }\n\n  auto transformValue(Try<InputValueType> inputValue) {\n    return transformValue_(std::move(inputValue));\n  }\n\n private:\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  TransformValueFunc transformValue_;\n};\n\ntemplate <\n    typename InitializeArg,\n    typename InputValueType,\n    typename OutputValueType,\n    typename InitializeTransformFunc,\n    typename TransformValueFunc,\n    bool RateLimiterEnabled>\nclass DefaultResumableTransformer\n    : public DefaultTransformer<\n          InputValueType,\n          OutputValueType,\n          TransformValueFunc,\n          RateLimiterEnabled> {\n public:\n  using Base = DefaultTransformer<\n      InputValueType,\n      OutputValueType,\n      TransformValueFunc,\n      RateLimiterEnabled>;\n\n  DefaultResumableTransformer(\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      InitializeTransformFunc initializeTransform,\n      TransformValueFunc transformValue,\n      std::shared_ptr<RateLimiter> rateLimiter)\n      : Base(\n            std::move(executor),\n            std::move(transformValue),\n            std::move(rateLimiter)),\n        initializeTransform_(std::move(initializeTransform)) {}\n\n  auto initializeTransform(InitializeArg initializeArg) {\n    return initializeTransform_(std::move(initializeArg));\n  }\n\n private:\n  InitializeTransformFunc initializeTransform_;\n};\n} // namespace detail\n\ntemplate <\n    typename ReceiverType,\n    typename TransformValueFunc,\n    typename InputValueType,\n    typename OutputValueType>\nReceiver<OutputValueType> transform(\n    ReceiverType inputReceiver,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    TransformValueFunc transformValue,\n    std::shared_ptr<RateLimiter> rateLimiter) {\n  if (rateLimiter != nullptr) {\n    using TransformerType = detail::DefaultTransformer<\n        InputValueType,\n        OutputValueType,\n        TransformValueFunc,\n        true /* RateLimiterEnabled */>;\n    return transform(\n        std::move(inputReceiver),\n        TransformerType(\n            std::move(executor),\n            std::move(transformValue),\n            std::move(rateLimiter)));\n\n  } else {\n    using TransformerType = detail::DefaultTransformer<\n        InputValueType,\n        OutputValueType,\n        TransformValueFunc,\n        false /* RateLimiterEnabled */>;\n    return transform(\n        std::move(inputReceiver),\n        TransformerType(\n            std::move(executor),\n            std::move(transformValue),\n            nullptr /* rateLimiter */));\n  }\n}\n\ntemplate <\n    typename ReceiverType,\n    typename TransformerType,\n    typename InputValueType,\n    typename OutputValueType>\nReceiver<OutputValueType> transform(\n    ReceiverType inputReceiver, TransformerType transformer) {\n  auto [outputReceiver, outputSender] = Channel<OutputValueType>::create();\n  using TProcessor = detail::\n      TransformProcessor<InputValueType, OutputValueType, TransformerType>;\n  auto* processor =\n      new TProcessor(std::move(outputSender), std::move(transformer));\n  processor->startTransform(std::move(inputReceiver));\n  return std::move(outputReceiver);\n}\n\ntemplate <\n    typename InitializeArg,\n    typename InitializeTransformFunc,\n    typename TransformValueFunc,\n    typename ReceiverType,\n    typename InputValueType,\n    typename OutputValueType>\nReceiver<OutputValueType> resumableTransform(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    InitializeArg initializeArg,\n    InitializeTransformFunc initializeTransform,\n    TransformValueFunc transformValue,\n    std::shared_ptr<RateLimiter> rateLimiter) {\n  if (rateLimiter != nullptr) {\n    using TransformerType = detail::DefaultResumableTransformer<\n        InitializeArg,\n        InputValueType,\n        OutputValueType,\n        InitializeTransformFunc,\n        TransformValueFunc,\n        true /* RateLimiterEnabled */>;\n    return resumableTransform(\n        std::move(initializeArg),\n        TransformerType(\n            std::move(executor),\n            std::move(initializeTransform),\n            std::move(transformValue),\n            std::move(rateLimiter)));\n  } else {\n    using TransformerType = detail::DefaultResumableTransformer<\n        InitializeArg,\n        InputValueType,\n        OutputValueType,\n        InitializeTransformFunc,\n        TransformValueFunc,\n        false /* RateLimiterEnabled */>;\n    return resumableTransform(\n        std::move(initializeArg),\n        TransformerType(\n            std::move(executor),\n            std::move(initializeTransform),\n            std::move(transformValue),\n            nullptr /* rateLimiter */));\n  }\n}\n\ntemplate <\n    typename InitializeArg,\n    typename TransformerType,\n    typename ReceiverType,\n    typename InputValueType,\n    typename OutputValueType>\nReceiver<OutputValueType> resumableTransform(\n    InitializeArg initializeArg, TransformerType transformer) {\n  auto [outputReceiver, outputSender] = Channel<OutputValueType>::create();\n  using TProcessor = detail::ResumableTransformProcessor<\n      InitializeArg,\n      InputValueType,\n      OutputValueType,\n      TransformerType>;\n  auto* processor =\n      new TProcessor(std::move(outputSender), std::move(transformer));\n  processor->initialize(std::move(initializeArg));\n  return std::move(outputReceiver);\n}\n\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/Transform.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/channels/Channel.h>\n#include <folly/channels/OnClosedException.h>\n#include <folly/channels/RateLimiter.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\n\n/**\n * Returns an output receiver that applies a given transformation function to\n * each value from an input receiver.\n *\n * The TransformValue function takes a Try<InputValueType>, and returns a\n * folly::coro::AsyncGenerator<OutputValueType>.\n *\n *   - If the TransformValue function yields one or more output values, those\n *      output values are sent to the output receiver.\n *\n *   - If the TransformValue function throws an OnClosedException, the output\n *      receiver is closed (without an exception).\n *\n *   - If the TransformValue function throws any other type of exception, the\n *      output receiver is closed with that exception.\n *\n * If the input receiver was closed, the TransformValue function is called with\n * a Try containing an exception (either OnClosedException if the input receiver\n * was closed without an exception, or the closure exception if the input\n * receiver was closed with an exception). In this case, regardless of what the\n * TransformValue function returns, the output receiver will be closed\n * (potentially after receiving the last output values the TransformValue\n * function returned, if any).\n *\n * @param inputReceiver: The input receiver.\n *\n * @param executor: A folly::SequencedExecutor used to transform the values.\n *\n * @param transformValue: A function as described above.\n *\n * @param rateLimiter: An optional rate limiter. If specified, the given rate\n *     limiter will limit the number of transformation functions that are\n *     simultaneously running.\n *\n * Example:\n *\n *  // Function that returns a receiver\n *  Receiver<int> getInputReceiver();\n *\n *  // Function that returns an executor\n *  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *  Receiver<std::string> outputReceiver = transform(\n *      getInputReceiver(),\n *      getExecutor(),\n *      [](Try<int> try) -> folly::coro::AsyncGenerator<std::string&&> {\n *          co_yield folly::to<std::string>(try.value());\n *      });\n */\ntemplate <\n    typename ReceiverType,\n    typename TransformValueFunc,\n    typename InputValueType = typename ReceiverType::ValueType,\n    typename OutputValueType = typename folly::invoke_result_t< //\n        TransformValueFunc,\n        Try<InputValueType>>::value_type>\nReceiver<OutputValueType> transform(\n    ReceiverType inputReceiver,\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    TransformValueFunc transformValue,\n    std::shared_ptr<RateLimiter> rateLimiter = nullptr);\n\n/**\n * This overload accepts arguments in the form of a transformer object. The\n * transformer object must have the following functions:\n *\n * folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n * folly::coro::AsyncGenerator<OutputValueType&&> transformValue(\n *     Try<InputValueType> inputValue);\n *\n * std::shared_ptr<RateLimiter> getRateLimiter(); // Can return nullptr\n */\ntemplate <\n    typename ReceiverType,\n    typename TransformerType,\n    typename InputValueType = typename ReceiverType::ValueType,\n    typename OutputValueType =\n        typename decltype(std::declval<TransformerType>().transformValue(\n            std::declval<Try<InputValueType>>()))::value_type>\nReceiver<OutputValueType> transform(\n    ReceiverType inputReceiver, TransformerType transformer);\n\n/**\n * This function is similar to the above transform function. However, instead of\n * taking a single input receiver, it takes an initialization function that\n * accepts a value of type InitializeArg, and returns a\n * std::pair<std::vector<OutputValueType>, Receiver<InputValueType>>.\n *\n *  - If the InitializeTransform function returns successfully, the vector's\n *      output values will be immediately sent to the output receiver. The input\n *      receiver is then processed as described in the transform function's\n *      documentation, unless and until it throws a ReinitializeException. At\n *      that point, the InitializationTransform is re-run with the InitializeArg\n *      specified in the ReinitializeException, and the transform begins anew.\n *\n *  - If the InitializeTransform function or the TransformValue function throws\n *      an OnClosedException, the output receiver is closed (with no exception).\n *\n *  - If the InitializeTransform function or the TransformValue function throws\n *      any other type of exception, the output receiver is closed with that\n *      exception.\n *\n * @param executor: A folly::SequencedExecutor used to transform the values.\n *\n * @param initializeArg: The initial argument passed to the InitializeTransform\n *  function.\n *\n * @param initializeTransform: The InitializeTransform function as described\n *  above.\n *\n * @param transformValue: The TransformValue function as described above.\n *\n * @param rateLimiter: An optional rate limiter. If specified, the given rate\n *     limiter will limit the number of transformation functions that are\n *     simultaneously running.\n *\n * Example:\n *\n *  struct InitializeArg {\n *    std::string param;\n *  }\n *\n *  // Function that returns a receiver\n *  Receiver<int> getInputReceiver(InitializeArg initializeArg);\n *\n *  // Function that returns an executor\n *  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n *  Receiver<std::string> outputReceiver = resumableTransform(\n *      getExecutor(),\n *      InitializeArg{\"param\"},\n *      [](InitializeArg initializeArg) -> folly::coro::Task<\n *                  std::pair<std::vector<std::string>, Receiver<int>> {\n *          co_return std::make_pair(\n *              std::vector<std::string>({\"Initialized\"}),\n *              getInputReceiver(initializeArg));\n *      },\n *      [](Try<int> try) -> folly::coro::AsyncGenerator<std::string&&> {\n *          try {\n *            co_yield folly::to<std::string>(try.value());\n *          } catch (const SomeApplicationException& ex) {\n *            throw ReinitializeException(InitializeArg{ex.getParam()});\n *          }\n *      });\n *\n */\ntemplate <\n    typename InitializeArg,\n    typename InitializeTransformFunc,\n    typename TransformValueFunc,\n    typename ReceiverType = typename folly::invoke_result_t<\n        InitializeTransformFunc,\n        InitializeArg>::StorageType::second_type,\n    typename InputValueType = typename ReceiverType::ValueType,\n    typename OutputValueType = typename folly::invoke_result_t< //\n        TransformValueFunc,\n        Try<InputValueType>>::value_type>\nReceiver<OutputValueType> resumableTransform(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    InitializeArg initializeArg,\n    InitializeTransformFunc initializeTransform,\n    TransformValueFunc transformValue,\n    std::shared_ptr<RateLimiter> rateLimiter = nullptr);\n\n/**\n * This overload accepts arguments in the form of a transformer object. The\n * transformer object must have the following functions:\n *\n * folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor();\n *\n * std::pair<std::vector<OutputValueType>, Receiver<InputValueType>>\n * initializeTransform(InitializeArg initializeArg);\n *\n * folly::coro::AsyncGenerator<OutputValueType&&> transformValue(\n *     Try<InputValueType> inputValue);\n *\n * std::shared_ptr<RateLimiter> getRateLimiter(); // Can return nullptr\n */\ntemplate <\n    typename InitializeArg,\n    typename TransformerType,\n    typename ReceiverType =\n        typename decltype(std::declval<TransformerType>().initializeTransform(\n            std::declval<InitializeArg>()))::StorageType::second_type,\n    typename InputValueType = typename ReceiverType::ValueType,\n    typename OutputValueType =\n        typename decltype(std::declval<TransformerType>().transformValue(\n            std::declval<Try<InputValueType>>()))::value_type>\nReceiver<OutputValueType> resumableTransform(\n    InitializeArg initializeArg, TransformerType transformer);\n\n/**\n * A ReinitializeException thrown by a transform callback indicates that the\n * resumable transform needs to be re-initialized.\n */\ntemplate <typename InitializeArg>\nstruct ReinitializeException : public std::exception {\n  explicit ReinitializeException(InitializeArg _initializeArg)\n      : initializeArg(std::move(_initializeArg)) {}\n\n  const char* what() const noexcept override {\n    return \"This resumable transform should be re-initialized.\";\n  }\n\n  InitializeArg initializeArg;\n};\n} // namespace channels\n} // namespace folly\n\n#include <folly/channels/Transform-inl.h>\n"
  },
  {
    "path": "folly/channels/detail/AtomicQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <memory>\n#include <utility>\n#include <glog/logging.h>\n\n#include <folly/lang/Assume.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\ntemplate <typename T>\nclass Queue {\n public:\n  constexpr Queue() noexcept {}\n  constexpr Queue(Queue&& other) noexcept\n      : head_(std::exchange(other.head_, nullptr)) {}\n  Queue& operator=(Queue&& other) noexcept {\n    clear();\n    std::swap(head_, other.head_);\n    return *this;\n  }\n  ~Queue() { clear(); }\n\n  bool empty() const noexcept { return !head_; }\n\n  T& front() noexcept { return head_->value; }\n\n  void pop() noexcept {\n    std::unique_ptr<Node>(std::exchange(head_, head_->next));\n  }\n\n  void clear() {\n    while (!empty()) {\n      pop();\n    }\n  }\n\n  explicit operator bool() const { return !empty(); }\n\n  struct Node {\n    explicit Node(T&& t) : value(std::move(t)) {}\n\n    T value;\n    Node* next{nullptr};\n  };\n\n  constexpr explicit Queue(Node* head) noexcept : head_(head) {}\n  static Queue fromReversed(Node* tail) noexcept {\n    // Reverse a linked list.\n    Node* head{nullptr};\n    while (tail) {\n      head = std::exchange(tail, std::exchange(tail->next, head));\n    }\n    return Queue(head);\n  }\n\n  Node* head_{nullptr};\n};\n\ntemplate <typename Consumer, typename Message>\nclass AtomicQueue {\n public:\n  using MessageQueue = Queue<Message>;\n\n  AtomicQueue() { static_assert(alignof(Consumer) > kTypeMask); }\n  ~AtomicQueue() {\n    auto storage = storage_.load(std::memory_order_acquire);\n    auto type = static_cast<Type>(storage & kTypeMask);\n    auto ptr = storage & kPointerMask;\n    switch (type) {\n      case Type::EMPTY:\n      case Type::CLOSED:\n        return;\n      case Type::TAIL:\n        MessageQueue::fromReversed(\n            reinterpret_cast<typename MessageQueue::Node*>(ptr));\n        return;\n      case Type::CONSUMER:\n      default:\n        folly::assume_unreachable();\n    }\n  }\n  AtomicQueue(const AtomicQueue&) = delete;\n  AtomicQueue& operator=(const AtomicQueue&) = delete;\n\n  template <typename... ConsumerArgs>\n  void push(Message&& value, ConsumerArgs&&... consumerArgs) {\n    std::unique_ptr<typename MessageQueue::Node> node(\n        new typename MessageQueue::Node(std::move(value)));\n    assert(!(reinterpret_cast<intptr_t>(node.get()) & kTypeMask));\n\n    auto storage = storage_.load(std::memory_order_relaxed);\n    while (true) {\n      auto type = static_cast<Type>(storage & kTypeMask);\n      auto ptr = storage & kPointerMask;\n      switch (type) {\n        case Type::EMPTY:\n        case Type::TAIL:\n          node->next = reinterpret_cast<typename MessageQueue::Node*>(ptr);\n          if (storage_.compare_exchange_weak(\n                  storage,\n                  reinterpret_cast<intptr_t>(node.get()) |\n                      static_cast<intptr_t>(Type::TAIL),\n                  std::memory_order_release,\n                  std::memory_order_relaxed)) {\n            node.release();\n            return;\n          }\n          break;\n        case Type::CLOSED:\n          return;\n        case Type::CONSUMER:\n          node->next = nullptr;\n          if (storage_.compare_exchange_weak(\n                  storage,\n                  reinterpret_cast<intptr_t>(node.get()) |\n                      static_cast<intptr_t>(Type::TAIL),\n                  std::memory_order_acq_rel,\n                  std::memory_order_relaxed)) {\n            node.release();\n            auto consumer = reinterpret_cast<Consumer*>(ptr);\n            consumer->consume(std::forward<ConsumerArgs>(consumerArgs)...);\n            return;\n          }\n          break;\n        default:\n          folly::assume_unreachable();\n      }\n    }\n  }\n\n  template <typename... ConsumerArgs>\n  bool wait(Consumer* consumer, ConsumerArgs&&... consumerArgs) {\n    assert(!(reinterpret_cast<intptr_t>(consumer) & kTypeMask));\n    auto storage = storage_.load(std::memory_order_relaxed);\n    while (true) {\n      auto type = static_cast<Type>(storage & kTypeMask);\n      switch (type) {\n        case Type::EMPTY:\n          if (storage_.compare_exchange_weak(\n                  storage,\n                  reinterpret_cast<intptr_t>(consumer) |\n                      static_cast<intptr_t>(Type::CONSUMER),\n                  std::memory_order_release,\n                  std::memory_order_relaxed)) {\n            return true;\n          }\n          break;\n        case Type::CLOSED:\n          consumer->canceled(std::forward<ConsumerArgs>(consumerArgs)...);\n          return true;\n        case Type::TAIL:\n          return false;\n        case Type::CONSUMER:\n        default:\n          folly::assume_unreachable();\n      }\n    }\n  }\n\n  template <typename... ConsumerArgs>\n  void close(ConsumerArgs&&... consumerArgs) {\n    auto storage = storage_.exchange(\n        static_cast<intptr_t>(Type::CLOSED), std::memory_order_acquire);\n    auto type = static_cast<Type>(storage & kTypeMask);\n    auto ptr = storage & kPointerMask;\n    switch (type) {\n      case Type::EMPTY:\n        return;\n      case Type::TAIL:\n        MessageQueue::fromReversed(\n            reinterpret_cast<typename MessageQueue::Node*>(ptr));\n        return;\n      case Type::CONSUMER:\n        reinterpret_cast<Consumer*>(ptr)->canceled(\n            std::forward<ConsumerArgs>(consumerArgs)...);\n        return;\n      case Type::CLOSED:\n      default:\n        folly::assume_unreachable();\n    }\n  }\n\n  bool isClosed() {\n    auto type = static_cast<Type>(storage_ & kTypeMask);\n    return type == Type::CLOSED;\n  }\n\n  template <typename... ConsumerArgs>\n  MessageQueue getMessages(ConsumerArgs&&... consumerArgs) {\n    auto storage = storage_.exchange(\n        static_cast<intptr_t>(Type::EMPTY), std::memory_order_acquire);\n    auto type = static_cast<Type>(storage & kTypeMask);\n    auto ptr = storage & kPointerMask;\n    switch (type) {\n      case Type::TAIL:\n        return MessageQueue::fromReversed(\n            reinterpret_cast<typename MessageQueue::Node*>(ptr));\n      case Type::EMPTY:\n        return MessageQueue();\n      case Type::CLOSED:\n        // We accidentally re-opened the queue, so close it again.\n        // This is only safe to do because isClosed() can't be called\n        // concurrently with getMessages().\n        close(std::forward<ConsumerArgs>(consumerArgs)...);\n        return MessageQueue();\n      case Type::CONSUMER:\n      default:\n        folly::assume_unreachable();\n    }\n  }\n\n  Consumer* cancelCallback() {\n    auto storage = storage_.load(std::memory_order_acquire);\n    while (true) {\n      auto type = static_cast<Type>(storage & kTypeMask);\n      auto ptr = storage & kPointerMask;\n      switch (type) {\n        case Type::CONSUMER:\n          if (storage_.compare_exchange_weak(\n                  storage,\n                  static_cast<intptr_t>(Type::EMPTY),\n                  std::memory_order_relaxed,\n                  std::memory_order_relaxed)) {\n            return reinterpret_cast<Consumer*>(ptr);\n          }\n          break;\n        case Type::TAIL:\n        case Type::EMPTY:\n        case Type::CLOSED:\n        default:\n          return nullptr;\n      }\n    }\n  }\n\n private:\n  enum class Type : intptr_t { EMPTY = 0, CONSUMER = 1, TAIL = 2, CLOSED = 3 };\n\n  static constexpr intptr_t kTypeMask = 3;\n  static constexpr intptr_t kPointerMask = ~kTypeMask;\n\n  std::atomic<intptr_t> storage_{0};\n};\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"atomic_queue\",\n    headers = [\n        \"AtomicQueue.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/lang:assume\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"channel_bridge\",\n    headers = [\"ChannelBridge.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:try\",\n        \"//folly/experimental/channels/detail:atomic_queue\",\n    ],\n)\n\n# !!!! fbcode/folly/channels/detail/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"intrusive_ptr\",\n    headers = [\"IntrusivePtr.h\"],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"multiplexer_traits\",\n    headers = [\"MultiplexerTraits.h\"],\n    exported_deps = [\n        \"//folly:traits\",\n        \"//folly/functional:traits\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"pointer_variant\",\n    headers = [\"PointerVariant.h\"],\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:demangle\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"utility\",\n    headers = [\n        \"Utility.h\",\n    ],\n    exported_deps = [\n        \"//folly:exception_wrapper\",\n        \"//folly:function\",\n        \"//folly:scope_guard\",\n        \"//folly/channels:channel\",\n        \"//folly/channels:rate_limiter\",\n        \"//folly/coro:promise\",\n        \"//folly/coro:task\",\n        \"//folly/executors:sequenced_executor\",\n    ],\n)\n"
  },
  {
    "path": "folly/channels/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME atomic_queue\n  HEADERS\n    AtomicQueue.h\n  EXPORTED_DEPS\n    folly_lang_assume\n)\n\nfolly_add_library(\n  NAME channel_bridge\n  HEADERS\n    ChannelBridge.h\n  EXPORTED_DEPS\n    folly_experimental_channels_detail_atomic_queue\n    folly_try\n)\n\nfolly_add_library(\n  NAME intrusive_ptr\n  HEADERS\n    IntrusivePtr.h\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME multiplexer_traits\n  HEADERS\n    MultiplexerTraits.h\n  EXPORTED_DEPS\n    folly_functional_traits\n    folly_traits\n)\n\nfolly_add_library(\n  NAME pointer_variant\n  HEADERS\n    PointerVariant.h\n  EXPORTED_DEPS\n    folly_demangle\n)\n\nfolly_add_library(\n  NAME utility\n  HEADERS\n    Utility.h\n  EXPORTED_DEPS\n    folly_channels_channel\n    folly_channels_rate_limiter\n    folly_coro_promise\n    folly_coro_task\n    folly_exception_wrapper\n    folly_executors_sequenced_executor\n    folly_function\n    folly_scope_guard\n)\n"
  },
  {
    "path": "folly/channels/detail/ChannelBridge.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Try.h>\n#include <folly/experimental/channels/detail/AtomicQueue.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\nclass ChannelBridgeBase {};\n\nclass IChannelCallback {\n public:\n  virtual ~IChannelCallback() = default;\n\n  virtual void consume(ChannelBridgeBase* bridge) = 0;\n\n  virtual void canceled(ChannelBridgeBase* bridge) = 0;\n};\n\nusing SenderQueue = typename folly::channels::detail::Queue<Unit>;\n\ntemplate <typename TValue>\nusing ReceiverQueue = typename folly::channels::detail::Queue<Try<TValue>>;\n\ntemplate <typename TValue>\nclass ChannelBridge : public ChannelBridgeBase {\n public:\n  struct Deleter {\n    void operator()(ChannelBridge<TValue>* ptr) { ptr->decref(); }\n  };\n  using Ptr = std::unique_ptr<ChannelBridge<TValue>, Deleter>;\n\n  static Ptr create() { return Ptr(new ChannelBridge<TValue>()); }\n\n  Ptr copy() {\n    auto refCount = refCount_.fetch_add(1, std::memory_order_relaxed);\n    DCHECK(refCount > 0);\n    return Ptr(this);\n  }\n\n  // These should only be called from the sender thread\n\n  template <typename U = TValue>\n  void senderPush(U&& value) {\n    receiverQueue_.push(\n        Try<TValue>(std::forward<U>(value)),\n        static_cast<ChannelBridgeBase*>(this));\n  }\n\n  bool senderWait(IChannelCallback* callback) {\n    return senderQueue_.wait(callback, static_cast<ChannelBridgeBase*>(this));\n  }\n\n  IChannelCallback* cancelSenderWait() { return senderQueue_.cancelCallback(); }\n\n  void senderClose() {\n    if (!isSenderClosed()) {\n      receiverQueue_.push(Try<TValue>(), static_cast<ChannelBridgeBase*>(this));\n      senderQueue_.close(static_cast<ChannelBridgeBase*>(this));\n    }\n  }\n\n  void senderClose(exception_wrapper ex) {\n    if (!isSenderClosed()) {\n      receiverQueue_.push(\n          Try<TValue>(std::move(ex)), static_cast<ChannelBridgeBase*>(this));\n      senderQueue_.close(static_cast<ChannelBridgeBase*>(this));\n    }\n  }\n\n  bool isSenderClosed() { return senderQueue_.isClosed(); }\n\n  SenderQueue senderGetValues() {\n    return senderQueue_.getMessages(static_cast<ChannelBridgeBase*>(this));\n  }\n\n  // These should only be called from the receiver thread\n\n  void receiverCancel() {\n    if (!isReceiverCancelled()) {\n      senderQueue_.push(Unit(), static_cast<ChannelBridgeBase*>(this));\n      receiverQueue_.close(static_cast<ChannelBridgeBase*>(this));\n    }\n  }\n\n  bool isReceiverCancelled() { return receiverQueue_.isClosed(); }\n\n  bool receiverWait(IChannelCallback* callback) {\n    return receiverQueue_.wait(callback, static_cast<ChannelBridgeBase*>(this));\n  }\n\n  IChannelCallback* cancelReceiverWait() {\n    return receiverQueue_.cancelCallback();\n  }\n\n  ReceiverQueue<TValue> receiverGetValues() {\n    return receiverQueue_.getMessages(static_cast<ChannelBridgeBase*>(this));\n  }\n\n private:\n  using ReceiverAtomicQueue = typename folly::channels::detail::\n      AtomicQueue<IChannelCallback, Try<TValue>>;\n\n  using SenderAtomicQueue =\n      typename folly::channels::detail::AtomicQueue<IChannelCallback, Unit>;\n\n  void decref() {\n    if (refCount_.fetch_sub(1, std::memory_order_acq_rel) == 1) {\n      delete this;\n    }\n  }\n\n  ReceiverAtomicQueue receiverQueue_;\n  SenderAtomicQueue senderQueue_;\n  std::atomic<int8_t> refCount_{1};\n};\n\ntemplate <typename TValue>\nusing ChannelBridgePtr = typename ChannelBridge<TValue>::Ptr;\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/detail/IntrusivePtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <boost/intrusive_ptr.hpp>\n#include <boost/smart_ptr/intrusive_ref_counter.hpp>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\n/**\n * An intrusive_ptr is like an std::shared_ptr. However, unlike a shared_ptr,\n * the reference count for an intrusive_ptr lives on the object itself. This has\n * two advantages:\n *\n * 1. Each intrusive_ptr is 8 bytes instead of 16 bytes.\n *\n * 2. An intrusive_ptr can be created from a raw pointer/reference, unlike a\n *    shared_ptr.\n *\n * To use intrusive_ptr<T>, ensure that T inherits from IntrusivePtrBase<T>.\n */\n\ntemplate <typename T>\nusing intrusive_ptr = boost::intrusive_ptr<T>;\n\ntemplate <typename T>\nusing IntrusivePtrBase =\n    boost::intrusive_ref_counter<T, boost::thread_safe_counter>;\n\ntemplate <typename T, typename... Args>\nintrusive_ptr<T> make_intrusive(Args&&... args) {\n  return intrusive_ptr<T>(new T(std::forward<Args>(args)...));\n}\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/detail/MultiplexerTraits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Traits.h>\n#include <folly/functional/traits.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\ntemplate <typename MultiplexerType>\nstruct MultiplexerTraits {\n  using OnNewSubscriptionSig =\n      member_pointer_member_t<decltype(&MultiplexerType::onNewSubscription)>;\n\n  using OnInputValueSig =\n      member_pointer_member_t<decltype(&MultiplexerType::onInputValue)>;\n\n  // First parameter type of MultiplexerType::onNewSubscription\n  using KeyType = function_arguments_element_t<0, OnNewSubscriptionSig>;\n\n  // Second parameter type for MultiplexerType::onNewSubscription\n  using KeyContextType =\n      std::decay_t<function_arguments_element_t<1, OnNewSubscriptionSig>>;\n\n  // Third parameter type for MultiplexerType::onNewSubscription\n  using SubscriptionArgType =\n      function_arguments_element_t<2, OnNewSubscriptionSig>;\n\n  // First parameter value type of MultiplexerType::onInputValue\n  using InputValueType =\n      typename function_arguments_element_t<0, OnInputValueSig>::element_type;\n\n  // Element type of the returned vector from MultiplexerType::onNewSubscription\n  using OutputValueType =\n      typename function_result_t<OnNewSubscriptionSig>::StorageType::value_type;\n};\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/detail/PointerVariant.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fmt/format.h>\n#include <folly/Demangle.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\n/**\n * A PointerVariant stores a pointer of one of two possible types.\n */\ntemplate <typename FirstType, typename SecondType>\nclass PointerVariant {\n public:\n  template <typename T>\n  explicit PointerVariant(T* pointer) {\n    set(pointer);\n  }\n\n  PointerVariant(PointerVariant&& other) noexcept\n      : storage_(std::exchange(other.storage_, 0)) {}\n\n  PointerVariant& operator=(PointerVariant&& other) noexcept {\n    storage_ = std::exchange(other.storage_, 0);\n    return *this;\n  }\n\n  /**\n   * Returns the zero-based index of the type that is currently held.\n   */\n  size_t index() const { return static_cast<size_t>(storage_ & kTypeMask); }\n\n  /**\n   * Returns the pointer stored in the PointerVariant, if the type matches the\n   * first type. If the stored type does not match the first type, an exception\n   * will be thrown.\n   */\n  inline FirstType* get(folly::tag_t<FirstType>) const {\n    ensureCorrectType(false /* secondType */);\n    return reinterpret_cast<FirstType*>(storage_ & kPointerMask);\n  }\n\n  /**\n   * Returns the pointer stored in the PointerVariant, if the type matches the\n   * second type. If the stored type does not match the second type, an\n   * exception will be thrown.\n   */\n  inline SecondType* get(folly::tag_t<SecondType>) const {\n    ensureCorrectType(true /* secondType */);\n    return reinterpret_cast<SecondType*>(storage_ & kPointerMask);\n  }\n\n  /**\n   * Store a new pointer of type FirstType in the PointerVariant.\n   */\n  void set(FirstType* pointer) {\n    storage_ = reinterpret_cast<intptr_t>(pointer);\n  }\n\n  /**\n   * Store a new pointer of type SecondType in the PointerVariant.\n   */\n  void set(SecondType* pointer) {\n    storage_ = reinterpret_cast<intptr_t>(pointer) | kTypeMask;\n  }\n\n private:\n  void ensureCorrectType(bool secondType) const {\n    if (secondType != !!(storage_ & kTypeMask)) {\n      throw std::runtime_error(\n          fmt::format(\n              \"Incorrect type specified. Given: {}, Stored: {}\",\n              secondType ? folly::demangle(typeid(SecondType).name())\n                         : folly::demangle(typeid(FirstType).name()),\n              storage_ & kTypeMask\n                  ? folly::demangle(typeid(SecondType).name())\n                  : folly::demangle(typeid(FirstType).name())));\n    }\n  }\n\n  static constexpr intptr_t kTypeMask = 1;\n  static constexpr intptr_t kPointerMask = ~kTypeMask;\n\n  intptr_t storage_;\n};\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/detail/Utility.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <optional>\n#include <folly/ExceptionWrapper.h>\n#include <folly/Function.h>\n#include <folly/ScopeGuard.h>\n#include <folly/channels/Channel.h>\n#include <folly/channels/RateLimiter.h>\n#include <folly/coro/Promise.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\nstruct CloseResult {\n  CloseResult() {}\n\n  explicit CloseResult(exception_wrapper _exception)\n      : exception(std::move(_exception)) {}\n\n  std::optional<exception_wrapper> exception;\n};\n\nenum class ChannelState {\n  Active,\n  CancellationTriggered,\n  CancellationProcessed\n};\n\ntemplate <typename TSender>\nChannelState getSenderState(TSender* sender) {\n  if (sender == nullptr) {\n    return ChannelState::CancellationProcessed;\n  } else if (sender->isSenderClosed()) {\n    return ChannelState::CancellationTriggered;\n  } else {\n    return ChannelState::Active;\n  }\n}\n\ntemplate <typename TReceiver>\nChannelState getReceiverState(TReceiver* receiver) {\n  if (receiver == nullptr) {\n    return ChannelState::CancellationProcessed;\n  } else if (receiver->isReceiverCancelled()) {\n    return ChannelState::CancellationTriggered;\n  } else {\n    return ChannelState::Active;\n  }\n}\n\ninline std::ostream& operator<<(std::ostream& os, ChannelState state) {\n  switch (state) {\n    case ChannelState::Active:\n      return os << \"Active\";\n    case ChannelState::CancellationTriggered:\n      return os << \"CancellationTriggered\";\n    case ChannelState::CancellationProcessed:\n      return os << \"CancellationProcessed\";\n    default:\n      return os << \"Should never be hit\";\n  }\n}\n\n/**\n * A cancellation callback that wraps an existing channel callback. When the\n * callback is fired, this object will trigger cancellation on its cancellation\n * source (in addition to firing the wrapped callback).\n */\ntemplate <typename TSender>\nclass SenderCancellationCallback : public IChannelCallback {\n public:\n  explicit SenderCancellationCallback(\n      TSender& sender,\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      IChannelCallback* channelCallback)\n      : sender_(sender),\n        executor_(std::move(executor)),\n        channelCallback_(channelCallback),\n        callbackToFire_(folly::coro::makePromiseContract<CallbackToFire>()) {\n    if (channelCallback_ == nullptr) {\n      // The sender was already canceled runOperationWithSenderCancellation was\n      // even called. This means the cancelled callback already was fired, so\n      // we will not set the callback to fire here.\n      cancelSource_.requestCancellation();\n      return;\n    }\n    CHECK(sender_);\n    if (!sender_->senderWait(this)) {\n      // The sender was cancelled after runOperationWithSenderCancellation was\n      // called, but before we had a chance to start the operation. This means\n      // that the cancelled callback was never called. We will therefore set it\n      // to fire here, when the operation is complete.\n      cancelSource_.requestCancellation();\n      callbackToFire_.first.setValue(CallbackToFire::Consume);\n    }\n  }\n\n  folly::coro::Task<void> onTaskCompleted() {\n    if (!channelCallback_) {\n      co_return;\n    }\n    auto callbackToFire = std::optional<CallbackToFire>();\n    bool promiseSet = false;\n    if (callbackToFire_.second.isReady()) {\n      // The callback was fired.\n      promiseSet = true;\n      callbackToFire = co_await std::move(callbackToFire_.second);\n    } else {\n      // The callback has not yet been fired.\n      if (!sender_->cancelSenderWait()) {\n        // The sender has been cancelled, but the callback has not been called\n        // yet. Wait for the callback to be called.\n        promiseSet = true;\n        callbackToFire = co_await std::move(callbackToFire_.second);\n      } else if (!sender_->senderWait(channelCallback_)) {\n        // The sender was cancelled between the call to cancelSenderWait and\n        // the call to senderWait. This means that the cancelled callback was\n        // never called. We will therefore set it to fire here.\n        callbackToFire = CallbackToFire::Consume;\n      }\n    }\n    if (!promiseSet) {\n      // Set a default value here, so we don't need to waste time constructing a\n      // broken promise exception when the promise is destructed. This value\n      // will not be read.\n      callbackToFire_.first.setValue(CallbackToFire::Consume);\n    }\n    if (callbackToFire.has_value()) {\n      switch (callbackToFire.value()) {\n        case CallbackToFire::Consume:\n          channelCallback_->consume(sender_.get());\n          co_return;\n        case CallbackToFire::Canceled:\n          channelCallback_->canceled(sender_.get());\n          co_return;\n      }\n    }\n    // The sender has not yet been cancelled, and we are now back in the state\n    // where the sender is waiting on the user-provided callback. We are done.\n  }\n\n  /**\n   * Returns a cancellation token that will trigger when the sender\n   */\n  folly::CancellationToken getCancellationToken() {\n    return cancelSource_.getToken();\n  }\n\n  /**\n   * Requests cancellation, and triggers the consume function on the callback\n   * if the callback was not previously triggered.\n   */\n  void consume(ChannelBridgeBase*) override {\n    cancelSource_.requestCancellation();\n    executor_->add([this]() {\n      CHECK(!callbackToFire_.first.isFulfilled());\n      callbackToFire_.first.setValue(CallbackToFire::Consume);\n    });\n  }\n\n  /**\n   * Requests cancellation, and triggers the canceled function on the callback\n   * if the callback was not previously triggered.\n   */\n  void canceled(ChannelBridgeBase*) override {\n    cancelSource_.requestCancellation();\n    executor_->add([this]() {\n      CHECK(!callbackToFire_.first.isFulfilled());\n      callbackToFire_.first.setValue(CallbackToFire::Canceled);\n    });\n  }\n\n private:\n  enum class CallbackToFire { Consume, Canceled };\n\n  TSender& sender_;\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  IChannelCallback* channelCallback_;\n  folly::CancellationSource cancelSource_;\n  std::pair<\n      folly::coro::Promise<CallbackToFire>,\n      folly::coro::Future<CallbackToFire>>\n      callbackToFire_;\n};\n\n/**\n * Any object that produces an output receiver (transform, merge,\n * MergeChannel, etc) will listen for a cancellation signal from that output\n * receiver. Once the consumer of the output receiver stops consuming, a\n * callback will be called that triggers these objects to start cleaning\n * themselves up (and eventually destroy themselves).\n *\n * However, when one of these objects decides to run a user coroutine, they\n * would like that user coroutine to be able to get notified when that\n * cancellation signal is received. That allows the coroutine to stop any\n * long-running operations quickly, rather than running a long time when the\n * consumer of the output receiver no longer cares about the result.\n *\n * This function enables that behavior. It will run the provided operation\n * coroutine. While that coroutine is running, it will listen to cancellation\n * events from the output receiver (through its sender). If it receives a\n * cancellation signal from the sender, it will trigger cancellation of the\n * operation coroutine.\n *\n * Once the coroutine finishes, it will then call the given channel callback\n * to notify it of the cancellation event (the same way that callback would\n * have been notified if no coroutine had been started). It will also resume\n * waiting on the channel callback.\n *\n * @param executor: The executor to run the coroutine on.\n *\n * @param sender: The sender to use to listen for cancellation. If this is\n * null, we will assume that cancellation already occurred.\n *\n * @param alreadyStartedWaiting: Whether or not the caller already started\n * listening for a cancellation signal from the output receiver. If so, this\n * function will temporarily stop waiting with that callback (so it can listen\n * for the cancellation signal to stop the coroutine).\n *\n * @param channelCallbackToRestore: The channel callback to restore once the\n *  coroutine operation is complete.\n *\n * @param operation: The operation to run.\n *\n * @param token: The rate limiter token for this operation.\n */\ntemplate <typename TSender>\nvoid runOperationWithSenderCancellation(\n    folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n    TSender& sender,\n    bool alreadyStartedWaiting,\n    IChannelCallback* channelCallbackToRestore,\n    folly::coro::Task<void> operation,\n    std::unique_ptr<RateLimiter::Token> token) noexcept {\n  if (alreadyStartedWaiting && (!sender || !sender->cancelSenderWait())) {\n    // The output receiver was cancelled before starting this operation\n    // (indicating that the channel callback already ran).\n    channelCallbackToRestore = nullptr;\n  }\n  co_withExecutor(\n      executor,\n      folly::coro::co_invoke(\n          [&sender,\n           executor,\n           channelCallbackToRestore,\n           token = std::move(token),\n           operation =\n               std::move(operation)]() mutable -> folly::coro::Task<void> {\n            auto senderCancellationCallback = SenderCancellationCallback(\n                sender, executor, channelCallbackToRestore);\n            auto result = co_await folly::coro::co_awaitTry(\n                folly::coro::co_withCancellation(\n                    senderCancellationCallback.getCancellationToken(),\n                    std::move(operation)));\n            if (result.hasException()) {\n              LOG(FATAL) << fmt::format(\n                  \"Unexpected exception when running coroutine operation with \"\n                  \"sender cancellation: {}\",\n                  result.exception().what());\n            }\n            co_await senderCancellationCallback.onTaskCompleted();\n          }))\n      .start();\n}\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/detail/test/AtomicQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/experimental/channels/detail/AtomicQueue.h>\n\n#include <folly/portability/GTest.h>\n\n#include <folly/synchronization/Baton.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\nstatic int* getConsumerParam() {\n  return reinterpret_cast<int*>(1);\n}\n\nTEST(AtomicQueueTest, Basic) {\n  folly::Baton<> producerBaton;\n  folly::Baton<> consumerBaton;\n\n  struct Consumer {\n    void consume(int* consumerParam) {\n      EXPECT_EQ(consumerParam, getConsumerParam());\n      baton.post();\n    }\n    void canceled(int*) { ADD_FAILURE() << \"canceled() shouldn't be called\"; }\n    folly::Baton<> baton;\n  };\n  AtomicQueue<Consumer, int> atomicQueue;\n  Consumer consumer;\n\n  std::thread producerThread([&] {\n    producerBaton.wait();\n    producerBaton.reset();\n\n    atomicQueue.push(1, getConsumerParam());\n\n    producerBaton.wait();\n    producerBaton.reset();\n\n    atomicQueue.push(2, getConsumerParam());\n    atomicQueue.push(3, getConsumerParam());\n    consumerBaton.post();\n  });\n\n  EXPECT_TRUE(atomicQueue.wait(&consumer, getConsumerParam()));\n  producerBaton.post();\n  consumer.baton.wait();\n  consumer.baton.reset();\n\n  {\n    auto q = atomicQueue.getMessages(getConsumerParam());\n    EXPECT_FALSE(q.empty());\n    EXPECT_EQ(1, q.front());\n    q.pop();\n    EXPECT_TRUE(q.empty());\n  }\n\n  producerBaton.post();\n  consumerBaton.wait();\n  consumerBaton.reset();\n\n  EXPECT_FALSE(atomicQueue.wait(&consumer, getConsumerParam()));\n  {\n    auto q = atomicQueue.getMessages(getConsumerParam());\n    EXPECT_FALSE(q.empty());\n    EXPECT_EQ(2, q.front());\n    q.pop();\n    EXPECT_FALSE(q.empty());\n    EXPECT_EQ(3, q.front());\n    q.pop();\n    EXPECT_TRUE(q.empty());\n  }\n\n  EXPECT_TRUE(atomicQueue.wait(&consumer, getConsumerParam()));\n  EXPECT_EQ(atomicQueue.cancelCallback(), &consumer);\n\n  EXPECT_TRUE(atomicQueue.wait(&consumer, getConsumerParam()));\n  EXPECT_EQ(atomicQueue.cancelCallback(), &consumer);\n\n  EXPECT_EQ(atomicQueue.cancelCallback(), nullptr);\n\n  producerThread.join();\n}\n\nTEST(AtomicQueueTest, Canceled) {\n  struct alignas(int) Consumer {\n    void consume(int*) { ADD_FAILURE() << \"consume() shouldn't be called\"; }\n    void canceled(int* consumerParam) {\n      EXPECT_EQ(consumerParam, getConsumerParam());\n      canceledCalled = true;\n    }\n    bool canceledCalled{false};\n  };\n  AtomicQueue<Consumer, int> atomicQueue;\n  Consumer consumer;\n\n  EXPECT_TRUE(atomicQueue.wait(&consumer, getConsumerParam()));\n  atomicQueue.close(getConsumerParam());\n  EXPECT_TRUE(consumer.canceledCalled);\n  EXPECT_TRUE(atomicQueue.isClosed());\n\n  EXPECT_TRUE(atomicQueue.getMessages(getConsumerParam()).empty());\n  EXPECT_TRUE(atomicQueue.isClosed());\n\n  atomicQueue.push(42, getConsumerParam());\n\n  EXPECT_TRUE(atomicQueue.getMessages(getConsumerParam()).empty());\n  EXPECT_TRUE(atomicQueue.isClosed());\n}\n\nTEST(AtomicQueueTest, Stress) {\n  struct Consumer {\n    void consume(int* consumerParam) {\n      EXPECT_EQ(consumerParam, getConsumerParam());\n      baton.post();\n    }\n    void canceled(int*) { ADD_FAILURE() << \"canceled() shouldn't be called\"; }\n    folly::Baton<> baton;\n  };\n  AtomicQueue<Consumer, int> atomicQueue;\n  auto getNext = [&atomicQueue, queue = Queue<int>()]() mutable {\n    Consumer consumer;\n    if (queue.empty()) {\n      if (atomicQueue.wait(&consumer, getConsumerParam())) {\n        consumer.baton.wait();\n      }\n      queue = atomicQueue.getMessages(getConsumerParam());\n      EXPECT_FALSE(queue.empty());\n    }\n    auto next = queue.front();\n    queue.pop();\n    return next;\n  };\n\n  constexpr ssize_t kNumIters = 100000;\n  constexpr ssize_t kSynchronizeEvery = 1000;\n\n  std::atomic<ssize_t> producerIndex{0};\n  std::atomic<ssize_t> consumerIndex{0};\n\n  std::thread producerThread([&] {\n    for (producerIndex = 1; producerIndex <= kNumIters; ++producerIndex) {\n      atomicQueue.push(producerIndex, getConsumerParam());\n\n      if (producerIndex % kSynchronizeEvery == 0) {\n        while (producerIndex > consumerIndex.load(std::memory_order_relaxed)) {\n          std::this_thread::yield();\n        }\n      }\n    }\n  });\n\n  for (consumerIndex = 1; consumerIndex <= kNumIters; ++consumerIndex) {\n    EXPECT_EQ(consumerIndex, getNext());\n\n    if (consumerIndex % kSynchronizeEvery == 0) {\n      while (consumerIndex > producerIndex.load(std::memory_order_relaxed)) {\n        std::this_thread::yield();\n      }\n    }\n  }\n\n  producerThread.join();\n}\n\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/detail/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"atomic_queue_test\",\n    srcs = [\n        \"AtomicQueueTest.cpp\",\n    ],\n    deps = [\n        \"//folly/experimental/channels/detail:atomic_queue\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"pointer_variant_test\",\n    srcs = [\n        \"PointerVariantTest.cpp\",\n    ],\n    deps = [\n        \"//folly/experimental/channels/detail:pointer_variant\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/channels/detail/test/PointerVariantTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/experimental/channels/detail/PointerVariant.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\nnamespace detail {\n\nusing namespace std::string_literals;\n\nTEST(PointerVariantTest, Basic) {\n  int64_t intVal1 = 100;\n  int64_t intVal2 = 200;\n  std::string strVal1 = \"100\"s;\n  std::string strVal2 = \"200\"s;\n  PointerVariant<int64_t, std::string> var(&intVal1);\n\n  EXPECT_EQ(*var.get(folly::tag_t<int64_t>{}), 100);\n\n  var.set(&intVal2);\n\n  EXPECT_EQ(*var.get(folly::tag_t<int64_t>{}), 200);\n\n  var.set(&strVal1);\n\n  EXPECT_EQ(*var.get(folly::tag_t<std::string>{}), \"100\"s);\n\n  var.set(&strVal2);\n\n  EXPECT_EQ(*var.get(folly::tag_t<std::string>{}), \"200\"s);\n}\n\nTEST(PointerVariantTest, GetIncorrecttype) {\n  int64_t intVal = 100;\n  std::string strVal = \"100\"s;\n  PointerVariant<int64_t, std::string> var(&intVal);\n\n  EXPECT_THROW(var.get(folly::tag_t<std::string>{}), std::runtime_error);\n\n  var.set(&strVal);\n\n  EXPECT_THROW(var.get(folly::tag_t<int64_t>{}), std::runtime_error);\n}\n} // namespace detail\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"channel_processor_test\",\n    srcs = [\"ChannelProcessorTest.cpp\"],\n    deps = [\n        \"//folly/channels:channel_processor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gmock\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"channel_test\",\n    srcs = [\"ChannelTest.cpp\"],\n    labels = [\"slow\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/channels:channel\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"fanout_channel_test\",\n    srcs = [\"FanoutChannelTest.cpp\"],\n    deps = [\n        \"//folly/channels:consume_channel\",\n        \"//folly/channels:fanout_channel\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"fanout_sender_test\",\n    srcs = [\"FanoutSenderTest.cpp\"],\n    deps = [\n        \"//folly/channels:consume_channel\",\n        \"//folly/channels:fanout_sender\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"max_concurrent_rate_limiter_test\",\n    srcs = [\n        \"MaxConcurrentRateLimiterTest.cpp\",\n    ],\n    deps = [\n        \"//folly/channels:max_concurrent_rate_limiter\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"merge_channel_test\",\n    srcs = [\n        \"MergeChannelTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly/channels:consume_channel\",\n        \"//folly/channels:merge_channel\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"merge_test\",\n    srcs = [\"MergeTest.cpp\"],\n    deps = [\n        \"//folly/channels:consume_channel\",\n        \"//folly/channels:merge\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"multiplex_channel_test\",\n    srcs = [\"MultiplexChannelTest.cpp\"],\n    deps = [\n        \"//folly/channels:consume_channel\",\n        \"//folly/channels:max_concurrent_rate_limiter\",\n        \"//folly/channels:multiplex_channel\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"producer_test\",\n    srcs = [\"ProducerTest.cpp\"],\n    deps = [\n        \"//folly/channels:producer\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"proxy_channel_test\",\n    srcs = [\"ProxyChannelTest.cpp\"],\n    deps = [\n        \"//folly/channels:proxy_channel\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"transform_test\",\n    srcs = [\"TransformTest.cpp\"],\n    deps = [\n        \"//folly:scope_guard\",\n        \"//folly:synchronized\",\n        \"//folly/channels:consume_channel\",\n        \"//folly/channels:max_concurrent_rate_limiter\",\n        \"//folly/channels:transform\",\n        \"//folly/channels/test:channel_test_util\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:detach_on_cancel\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"channel_test_util\",\n    headers = [\"ChannelTestUtil.h\"],\n    exported_deps = [\n        \"//folly/channels:consume_channel\",\n        \"//folly/coro:detach_on_cancel\",\n        \"//folly/coro:sleep\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/futures:shared_promise\",\n        \"//folly/portability:gmock\",\n    ],\n)\n"
  },
  {
    "path": "folly/channels/test/ChannelProcessorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ChannelProcessor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GMock.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\n\nstruct MockCallback {\n  MOCK_METHOD2(onInputValue, void(const std::string& key, int value));\n};\n\nclass ChannelProcessorFixture : public Test {\n protected:\n  ChannelProcessorFixture()\n      : processor_(createChannelProcessor<std::string>(&executor_)) {}\n\n  folly::ManualExecutor executor_;\n  ChannelProcessor<std::string> processor_;\n  StrictMock<MockCallback> callback_;\n};\n\nTEST_F(ChannelProcessorFixture, SimpleChannel_ReceiveValues) {\n  auto [receiver, sender] = Channel<int>::create();\n  processor_.addChannel(\n      \"one\",\n      std::move(receiver),\n      [&](Try<int> value) -> folly::coro::Task<void> {\n        callback_.onInputValue(\"one\", *value);\n        co_return;\n      });\n\n  executor_.drain();\n\n  EXPECT_CALL(callback_, onInputValue(\"one\", 1));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 2));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 3));\n\n  sender.write(1);\n  sender.write(2);\n  sender.write(3);\n  executor_.drain();\n}\n\nTEST_F(ChannelProcessorFixture, SimpleChannel_ThrowOnClosedException) {\n  auto [receiver, sender] = Channel<int>::create();\n  processor_.addChannel(\n      \"one\",\n      std::move(receiver),\n      [&](Try<int> value) -> folly::coro::Task<void> {\n        if (*value == -1) {\n          throw folly::channels::OnClosedException();\n        }\n        callback_.onInputValue(\"one\", *value);\n        co_return;\n      });\n\n  executor_.drain();\n\n  EXPECT_CALL(callback_, onInputValue(\"one\", 1));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 2));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 3));\n\n  sender.write(1);\n  sender.write(2);\n  sender.write(3);\n  sender.write(-1);\n  sender.write(4);\n  executor_.drain();\n}\n\nTEST_F(\n    ChannelProcessorFixture, SimpleChannel_ThrowOperationCancelledException) {\n  auto [receiver, sender] = Channel<int>::create();\n  processor_.addChannel(\n      \"one\",\n      std::move(receiver),\n      [&](Try<int> value) -> folly::coro::Task<void> {\n        if (*value == -1) {\n          throw folly::OperationCancelled();\n        }\n        callback_.onInputValue(\"one\", *value);\n        co_return;\n      });\n\n  executor_.drain();\n\n  EXPECT_CALL(callback_, onInputValue(\"one\", 1));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 2));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 3));\n\n  sender.write(1);\n  sender.write(2);\n  sender.write(3);\n  sender.write(-1);\n  sender.write(4);\n  executor_.drain();\n}\n\nTEST_F(ChannelProcessorFixture, SimpleChannel_ThrowOtherException_Death) {\n  auto [receiver, sender] = Channel<int>::create();\n  processor_.addChannel(\n      \"one\",\n      std::move(receiver),\n      [&](Try<int> value) -> folly::coro::Task<void> {\n        if (*value == -1) {\n          throw std::runtime_error(\"Unhandled exception\");\n        }\n        callback_.onInputValue(\"one\", *value);\n        co_return;\n      });\n\n  executor_.drain();\n\n  EXPECT_CALL(callback_, onInputValue(\"one\", 1));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 2));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 3));\n\n  sender.write(1);\n  sender.write(2);\n  sender.write(3);\n  executor_.drain();\n\n  EXPECT_DEATH(\n      {\n        sender.write(-1);\n        executor_.drain();\n      },\n      \"Unhandled exception\");\n}\n\nTEST_F(ChannelProcessorFixture, SimpleChannel_RemoveChannel) {\n  auto [receiver, sender] = Channel<int>::create();\n  auto [promise, future] = folly::coro::makePromiseContract<void>();\n  bool waitForFuture = false;\n  bool cancelled = false;\n  processor_.addChannel(\n      \"one\",\n      std::move(receiver),\n      [&, &future_2 = future](Try<int> value) -> folly::coro::Task<void> {\n        if (waitForFuture) {\n          try {\n            co_await std::move(future_2);\n          } catch (const folly::OperationCancelled&) {\n            cancelled = true;\n            throw;\n          }\n        }\n        callback_.onInputValue(\"one\", *value);\n        co_return;\n      });\n\n  executor_.drain();\n\n  EXPECT_CALL(callback_, onInputValue(\"one\", 1));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 2));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 3));\n\n  sender.write(1);\n  sender.write(2);\n  sender.write(3);\n  executor_.drain();\n\n  waitForFuture = true;\n  sender.write(4);\n  executor_.drain();\n\n  processor_.removeChannel(\"one\");\n  promise.setValue();\n  executor_.drain();\n\n  EXPECT_TRUE(cancelled);\n}\n\nTEST_F(\n    ChannelProcessorFixture, ResumableChannel_ReceiveValues_ThenReinitializes) {\n  struct ReceiverInfo {\n    size_t index;\n  };\n\n  struct State {\n    size_t receiverIndex;\n  };\n\n  auto receivers = std::vector<Receiver<int>>();\n  auto [receiver0, sender0] = Channel<int>::create();\n  auto [receiver1, sender1] = Channel<int>::create();\n  receivers.push_back(std::move(receiver0));\n  receivers.push_back(std::move(receiver1));\n  processor_.addResumableChannelWithState(\n      \"one\",\n      ReceiverInfo{0},\n      [&](ReceiverInfo receiverInfo,\n          State& state) -> folly::coro::Task<Receiver<int>> {\n        state.receiverIndex = receiverInfo.index;\n        co_return std::move(receivers[receiverInfo.index]);\n      },\n      [&](Try<int> value, State& state) -> folly::coro::Task<void> {\n        if (*value == -1) {\n          state.receiverIndex++;\n          throw ReinitializeException(ReceiverInfo{state.receiverIndex});\n        }\n        callback_.onInputValue(\"one\", *value);\n        co_return;\n      },\n      State());\n\n  executor_.drain();\n\n  EXPECT_CALL(callback_, onInputValue(\"one\", 1));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 2));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 3));\n  EXPECT_CALL(callback_, onInputValue(\"one\", 4));\n\n  sender0.write(1);\n  sender0.write(2);\n  sender0.write(-1);\n  sender0.write(999); // Should not be received\n  sender1.write(3);\n  sender1.write(4);\n  executor_.drain();\n}\n\nTEST_F(\n    ChannelProcessorFixture,\n    ResumableChannel_InitializeThrowsOnClosedException) {\n  struct InitializeArg {};\n  struct State {};\n\n  bool stillProcessing = true;\n  auto guard = folly::makeGuard([&] { stillProcessing = false; });\n\n  processor_.addResumableChannelWithState(\n      \"one\",\n      InitializeArg{},\n      [guard = std::move(guard)](auto, auto&)\n          -> folly::coro::Task<Receiver<int>> { throw OnClosedException(); },\n      [&](auto, auto&) -> folly::coro::Task<void> { co_return; },\n      State());\n  executor_.drain();\n\n  EXPECT_FALSE(stillProcessing);\n}\n\nTEST_F(\n    ChannelProcessorFixture,\n    ResumableChannel_InitializeThrowsOperationCancelledException) {\n  struct InitializeArg {};\n  struct State {};\n\n  bool stillProcessing = true;\n  auto guard = folly::makeGuard([&] { stillProcessing = false; });\n\n  processor_.addResumableChannelWithState(\n      \"one\",\n      InitializeArg{},\n      [guard = std::move(guard)](auto, auto&)\n          -> folly::coro::Task<Receiver<int>> { throw OperationCancelled(); },\n      [&](auto, auto&) -> folly::coro::Task<void> { co_return; },\n      State());\n  executor_.drain();\n\n  EXPECT_FALSE(stillProcessing);\n}\n\nTEST_F(\n    ChannelProcessorFixture,\n    ResumableChannel_TransformValueThrowsOnClosedException) {\n  struct InitializeArg {};\n  struct State {};\n\n  int numInitializeCalls = 0;\n  int numTransformCalls = 0;\n  auto [receiver, sender] = Channel<int>::create();\n  processor_.addResumableChannelWithState(\n      \"one\",\n      InitializeArg{},\n      [&, &receiver_2 = receiver](\n          auto, auto&) -> folly::coro::Task<Receiver<int>> {\n        numInitializeCalls++;\n        co_return std::move(receiver_2);\n      },\n      [&](auto, auto&) -> folly::coro::Task<void> {\n        numTransformCalls++;\n        throw OnClosedException();\n      },\n      State());\n\n  executor_.drain();\n\n  EXPECT_EQ(numInitializeCalls, 1);\n  EXPECT_EQ(numTransformCalls, 0);\n\n  sender.write(1);\n  executor_.drain();\n\n  EXPECT_EQ(numInitializeCalls, 1);\n  EXPECT_EQ(numTransformCalls, 1);\n}\n\nTEST_F(\n    ChannelProcessorFixture,\n    ResumableChannel_TransformValueThrowsOperationCancelledException) {\n  struct InitializeArg {};\n  struct State {};\n\n  int numInitializeCalls = 0;\n  int numTransformCalls = 0;\n  auto [receiver, sender] = Channel<int>::create();\n  processor_.addResumableChannelWithState(\n      \"one\",\n      InitializeArg{},\n      [&, &receiver_2 = receiver](\n          auto, auto&) -> folly::coro::Task<Receiver<int>> {\n        numInitializeCalls++;\n        co_return std::move(receiver_2);\n      },\n      [&](auto, auto&) -> folly::coro::Task<void> {\n        numTransformCalls++;\n        throw OperationCancelled();\n      },\n      State());\n\n  executor_.drain();\n\n  EXPECT_EQ(numInitializeCalls, 1);\n  EXPECT_EQ(numTransformCalls, 0);\n\n  sender.write(1);\n  executor_.drain();\n\n  EXPECT_EQ(numInitializeCalls, 1);\n  EXPECT_EQ(numTransformCalls, 1);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/ChannelTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Channel.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\n\nclass ChannelFixture\n    : public Test,\n      public WithParamInterface<ConsumptionMode>,\n      public ChannelConsumerBase<int> {\n protected:\n  ChannelFixture() : ChannelConsumerBase(GetParam()) {}\n\n  ~ChannelFixture() override {\n    cancellationSource_.requestCancellation();\n    if (!continueConsuming_.isFulfilled()) {\n      continueConsuming_.setValue(false);\n    }\n    executor_.drain();\n  }\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() override {\n    return &executor_;\n  }\n\n  void onNext(Try<int> result) override { onNext_(std::move(result)); }\n\n  folly::ManualExecutor executor_;\n  StrictMock<MockNextCallback<int>> onNext_;\n};\n\nTEST_P(ChannelFixture, SingleWriteBeforeNext_ThenCancelled) {\n  auto [receiver, sender] = Channel<int>::create();\n  sender.write(1);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n\n  EXPECT_FALSE(sender.isReceiverCancelled());\n\n  cancellationSource_.requestCancellation();\n  executor_.drain();\n\n  EXPECT_TRUE(sender.isReceiverCancelled());\n}\n\nTEST_P(ChannelFixture, SingleWriteAfterNext_ThenCancelled) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  sender.write(1);\n  executor_.drain();\n\n  EXPECT_FALSE(sender.isReceiverCancelled());\n\n  cancellationSource_.requestCancellation();\n  executor_.drain();\n\n  EXPECT_TRUE(sender.isReceiverCancelled());\n}\n\nTEST_P(ChannelFixture, MultipleWrites_ThenStopConsumingByReturningFalse) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  sender.write(3);\n  sender.write(4);\n  continueConsuming_ = folly::SharedPromise<bool>();\n  continueConsuming_.setValue(false);\n  executor_.drain();\n}\n\nTEST_P(\n    ChannelFixture,\n    MultipleWrites_ThenStopConsumingByThrowingOperationCancelled) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  sender.write(3);\n  sender.write(4);\n  continueConsuming_ = folly::SharedPromise<bool>();\n  continueConsuming_.setException(folly::OperationCancelled());\n  executor_.drain();\n}\n\nTEST_P(ChannelFixture, Close_NoException_BeforeSubscribe) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  std::move(sender).close();\n\n  EXPECT_CALL(onNext_, onClosed());\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n}\n\nTEST_P(ChannelFixture, Close_NoException_AfterSubscribeAndWrite) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  sender.write(1);\n  std::move(sender).close();\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onClosed());\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n}\n\nTEST_P(ChannelFixture, Close_DueToDestruction_BeforeSubscribe) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  {\n    auto toDestroy = std::move(sender);\n  }\n\n  EXPECT_CALL(onNext_, onClosed());\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n}\n\nTEST_P(ChannelFixture, Close_DueToDestruction_AfterSubscribeAndWrite) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onClosed());\n\n  startConsuming(std::move(receiver));\n\n  sender.write(1);\n  {\n    auto toDestroy = std::move(sender);\n  }\n\n  executor_.drain();\n}\n\nTEST_P(ChannelFixture, Close_Exception_BeforeSubscribe) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  std::move(sender).close(std::runtime_error(\"Error\"));\n\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n}\n\nTEST_P(ChannelFixture, Close_Exception_AfterSubscribeAndWrite) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  sender.write(1);\n  std::move(sender).close(std::runtime_error(\"Error\"));\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  startConsuming(std::move(receiver));\n  executor_.drain();\n}\n\nTEST_P(ChannelFixture, CancellationRespected) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  EXPECT_CALL(onNext_, onValue(1));\n\n  continueConsuming_ = folly::SharedPromise<bool>();\n  sender.write(1);\n  startConsuming(std::move(receiver));\n  executor_.drain();\n\n  EXPECT_FALSE(sender.isReceiverCancelled());\n\n  cancellationSource_.requestCancellation();\n  executor_.drain();\n\n  EXPECT_TRUE(sender.isReceiverCancelled());\n}\n\nTEST(Channel, CancelNextWithoutClose) {\n  folly::ManualExecutor executor;\n  folly::CancellationSource cancelSource;\n  auto [receiver, sender] = Channel<int>::create();\n\n  sender.write(1);\n  EXPECT_EQ(folly::coro::blockingWait(receiver.next()).value(), 1);\n\n  auto nextTask =\n      co_withExecutor(\n          &executor,\n          folly::coro::co_withCancellation(\n              cancelSource.getToken(),\n              folly::coro::co_invoke(\n                  [&receiver_2 =\n                       receiver]() -> folly::coro::Task<std::optional<int>> {\n                    co_return co_await receiver_2.next(\n                        false /* closeOnCancel */);\n                  })))\n          .start();\n  executor.drain();\n\n  cancelSource.requestCancellation();\n  sender.write(2);\n  executor.drain();\n\n  EXPECT_THROW(\n      folly::coro::blockingWait(std::move(nextTask)),\n      folly::OperationCancelled);\n\n  EXPECT_EQ(folly::coro::blockingWait(receiver.next()).value(), 2);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    Channel_Coro_WithTry,\n    ChannelFixture,\n    testing::Values(ConsumptionMode::CoroWithTry));\n\nINSTANTIATE_TEST_SUITE_P(\n    Channel_Coro_WithoutTry,\n    ChannelFixture,\n    testing::Values(ConsumptionMode::CoroWithoutTry));\n\nINSTANTIATE_TEST_SUITE_P(\n    Channel_Callback_WithHandle,\n    ChannelFixture,\n    testing::Values(ConsumptionMode::CallbackWithHandle));\n\nclass ChannelFixtureStress\n    : public Test,\n      public WithParamInterface<ConsumptionMode> {\n protected:\n  ChannelFixtureStress()\n      : producer_(\n            std::make_unique<StressTestProducer<int>>([value = 0]() mutable {\n              return value++;\n            })),\n        consumer_(\n            std::make_unique<StressTestConsumer<int>>(\n                GetParam(), [lastReceived = -1](int value) mutable {\n                  EXPECT_EQ(value, ++lastReceived);\n                })) {}\n\n  static constexpr std::chrono::milliseconds kTestTimeout =\n      std::chrono::milliseconds{5000};\n\n  std::unique_ptr<StressTestProducer<int>> producer_;\n  std::unique_ptr<StressTestConsumer<int>> consumer_;\n};\n\nTEST_P(ChannelFixtureStress, Close_NoException) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n  consumer_->startConsuming(std::move(receiver));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  producer_->stopProducing();\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::NoException);\n}\n\nTEST_P(ChannelFixtureStress, Close_Exception) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  producer_->startProducing(std::move(sender), std::runtime_error(\"Error\"));\n  consumer_->startConsuming(std::move(receiver));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  producer_->stopProducing();\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::Exception);\n}\n\nTEST_P(ChannelFixtureStress, Cancelled) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n  consumer_->startConsuming(std::move(receiver));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n\n  consumer_->cancel();\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::Cancelled);\n  producer_->stopProducing();\n}\n\nTEST_P(ChannelFixtureStress, Close_NoException_ThenCancelledImmediately) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n  consumer_->startConsuming(std::move(receiver));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  producer_->stopProducing();\n  consumer_->cancel();\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n\nTEST_P(ChannelFixtureStress, Cancelled_ThenClosedImmediately_NoException) {\n  auto [receiver, sender] = Channel<int>::create();\n\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n  consumer_->startConsuming(std::move(receiver));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  consumer_->cancel();\n  producer_->stopProducing();\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    Channel_Coro_WithTry,\n    ChannelFixtureStress,\n    testing::Values(ConsumptionMode::CoroWithTry));\n\nINSTANTIATE_TEST_SUITE_P(\n    Channel_Coro_WithoutTry,\n    ChannelFixtureStress,\n    testing::Values(ConsumptionMode::CoroWithoutTry));\n\nINSTANTIATE_TEST_SUITE_P(\n    Channel_Callback_WithHandle,\n    ChannelFixtureStress,\n    testing::Values(ConsumptionMode::CallbackWithHandle));\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/ChannelTestUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/coro/DetachOnCancel.h>\n#include <folly/coro/Sleep.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/futures/SharedPromise.h>\n#include <folly/portability/GMock.h>\n\nnamespace folly {\nnamespace channels {\n\ntemplate <typename T, typename... Others>\nstd::vector<T> toVector(T firstItem, Others... items) {\n  std::vector<T> itemsVector;\n  itemsVector.push_back(std::move(firstItem));\n  (void(itemsVector.push_back(std::move(items))), ...);\n  return itemsVector;\n}\n\ntemplate <typename Key, typename Mapped, typename... Others>\nfolly::F14FastMap<std::remove_const_t<Key>, Mapped> toMap(\n    std::pair<Key, Mapped> firstPair, Others... items) {\n  folly::F14FastMap<std::remove_const_t<Key>, Mapped> itemsMap;\n  itemsMap.insert(std::move(firstPair));\n  (void(itemsMap.insert(std::move(items))), ...);\n  return itemsMap;\n}\n\ntemplate <typename TValue>\nclass MockNextCallback {\n public:\n  void operator()(Try<TValue> result) {\n    if (result.hasValue()) {\n      onValue(result.value());\n    } else if (result.template hasException<folly::OperationCancelled>()) {\n      onCancelled();\n    } else if (result.template hasException<std::runtime_error>()) {\n      onRuntimeError(result.exception().what().toStdString());\n    } else if (result.hasException()) {\n      LOG(FATAL) << \"Unexpected exception: \" << result.exception().what();\n    } else {\n      onClosed();\n    }\n  }\n\n  MOCK_METHOD(void, onValue, (TValue));\n  MOCK_METHOD(void, onClosed, ());\n  MOCK_METHOD(void, onCancelled, ());\n  MOCK_METHOD(void, onRuntimeError, (std::string));\n};\n\nenum class ConsumptionMode {\n  CoroWithTry,\n  CoroWithoutTry,\n  CallbackWithHandle,\n};\n\ntemplate <typename TValue>\nclass ChannelConsumerBase {\n public:\n  explicit ChannelConsumerBase(ConsumptionMode mode) : mode_(mode) {\n    continueConsuming_.setValue(true);\n  }\n\n  ChannelConsumerBase(ChannelConsumerBase&&) = default;\n  ChannelConsumerBase& operator=(ChannelConsumerBase&&) = default;\n\n  virtual ~ChannelConsumerBase() = default;\n\n  virtual folly::Executor::KeepAlive<folly::SequencedExecutor>\n  getExecutor() = 0;\n\n  virtual void onNext(Try<TValue> result) = 0;\n\n  void startConsuming(Receiver<TValue> receiver) {\n    co_withExecutor(\n        getExecutor(),\n        folly::coro::co_withCancellation(\n            cancellationSource_.getToken(),\n            processValuesCoro(std::move(receiver))))\n        .start();\n  }\n\n  folly::coro::Task<void> processValuesCoro(Receiver<TValue> receiver) {\n    if (mode_ == ConsumptionMode::CoroWithTry ||\n        mode_ == ConsumptionMode::CoroWithoutTry) {\n      do {\n        Try<TValue> resultTry;\n        if (mode_ == ConsumptionMode::CoroWithTry) {\n          resultTry = co_await folly::coro::co_awaitTry(receiver.next());\n        } else if (mode_ == ConsumptionMode::CoroWithoutTry) {\n          try {\n            auto result = co_await receiver.next();\n            if (result.has_value()) {\n              resultTry = Try<TValue>(result.value());\n            } else {\n              resultTry = Try<TValue>();\n            }\n          } catch (...) {\n            resultTry = Try<TValue>(exception_wrapper(current_exception()));\n          }\n        } else {\n          LOG(FATAL) << \"Unknown consumption mode\";\n        }\n        bool hasValue = resultTry.hasValue();\n        onNext(std::move(resultTry));\n        if (!hasValue) {\n          co_return;\n        }\n      } while (co_await folly::coro::detachOnCancel(\n          continueConsuming_.getSemiFuture()));\n    } else if (mode_ == ConsumptionMode::CallbackWithHandle) {\n      auto callbackHandle = consumeChannelWithCallback(\n          std::move(receiver),\n          getExecutor(),\n          [=, this](Try<TValue> resultTry) -> folly::coro::Task<bool> {\n            onNext(std::move(resultTry));\n            co_return co_await folly::coro::detachOnCancel(\n                continueConsuming_.getSemiFuture());\n          });\n      cancelCallback_ = std::make_unique<folly::CancellationCallback>(\n          co_await folly::coro::co_current_cancellation_token,\n          [=, handle = std::move(callbackHandle)]() mutable {\n            handle.reset();\n          });\n    } else {\n      LOG(FATAL) << \"Unknown consumption mode\";\n    }\n  }\n\n protected:\n  ConsumptionMode mode_;\n  folly::CancellationSource cancellationSource_;\n  folly::SharedPromise<bool> continueConsuming_;\n  std::unique_ptr<folly::CancellationCallback> cancelCallback_;\n};\n\nenum class CloseType { NoException, Exception, Cancelled };\n\ntemplate <typename TValue>\nclass StressTestConsumer : public ChannelConsumerBase<TValue> {\n public:\n  StressTestConsumer(\n      ConsumptionMode mode, folly::Function<void(TValue)> onValue)\n      : ChannelConsumerBase<TValue>(mode),\n        executor_(std::make_unique<folly::IOThreadPoolExecutor>(1)),\n        onValue_(std::move(onValue)) {}\n\n  StressTestConsumer(StressTestConsumer&&) = delete;\n  StressTestConsumer&& operator=(StressTestConsumer&&) = delete;\n\n  ~StressTestConsumer() override {\n    this->cancellationSource_.requestCancellation();\n    if (!this->continueConsuming_.isFulfilled()) {\n      this->continueConsuming_.setValue(false);\n    }\n    executor_.reset();\n  }\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() override {\n    return executor_->getEventBase();\n  }\n\n  void onNext(Try<TValue> result) override {\n    if (result.hasValue()) {\n      onValue_(std::move(result.value()));\n    } else if (result.template hasException<folly::OperationCancelled>()) {\n      closedType_.setValue(CloseType::Cancelled);\n    } else if (result.hasException()) {\n      EXPECT_TRUE(result.template hasException<std::runtime_error>());\n      closedType_.setValue(CloseType::Exception);\n    } else {\n      closedType_.setValue(CloseType::NoException);\n    }\n  }\n\n  void cancel() { this->cancellationSource_.requestCancellation(); }\n\n  folly::SemiFuture<CloseType> waitForClose() {\n    return closedType_.getSemiFuture();\n  }\n\n private:\n  std::unique_ptr<folly::IOThreadPoolExecutor> executor_;\n  folly::Function<void(TValue)> onValue_;\n  folly::Promise<CloseType> closedType_;\n};\n\ntemplate <typename TValue>\nclass StressTestProducer {\n public:\n  explicit StressTestProducer(folly::Function<TValue()> getNextValue)\n      : executor_(std::make_unique<folly::CPUThreadPoolExecutor>(1)),\n        getNextValue_(std::move(getNextValue)) {}\n\n  StressTestProducer(StressTestProducer&&) = delete;\n  StressTestProducer&& operator=(StressTestProducer&&) = delete;\n\n  ~StressTestProducer() {\n    if (executor_) {\n      stopProducing();\n      executor_.reset();\n    }\n  }\n\n  void startProducing(\n      Sender<TValue> sender, std::optional<exception_wrapper> closeException) {\n    auto produceTask = folly::coro::co_invoke(\n        [=,\n         this,\n         sender = std::move(sender),\n         ex = std::move(closeException)]() mutable -> folly::coro::Task<void> {\n          for (int i = 1; !stopped_.load(std::memory_order_relaxed); i++) {\n            if (i % 1000 == 0) {\n              co_await folly::coro::sleep(std::chrono::milliseconds(100));\n            }\n            sender.write(getNextValue_());\n          }\n          if (ex.has_value()) {\n            std::move(sender).close(std::move(ex.value()));\n          } else {\n            std::move(sender).close();\n          }\n          co_return;\n        });\n    co_withExecutor(executor_.get(), std::move(produceTask)).start();\n  }\n\n  void stopProducing() { stopped_.store(true); }\n\n private:\n  std::unique_ptr<folly::CPUThreadPoolExecutor> executor_;\n  folly::Function<TValue()> getNextValue_;\n  std::atomic<bool> stopped_{false};\n};\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/FanoutChannelTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/FanoutChannel.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\n\nclass FanoutChannelFixture : public Test {\n protected:\n  FanoutChannelFixture() {}\n\n  ~FanoutChannelFixture() { executor_.drain(); }\n\n  template <typename T>\n  using Callback = StrictMock<MockNextCallback<T>>;\n\n  template <typename T>\n  std::pair<ChannelCallbackHandle, Callback<T>*> processValues(\n      Receiver<T> receiver) {\n    auto callback = std::make_unique<Callback<T>>();\n    auto callbackPtr = callback.get();\n    auto handle = consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [cbk = std::move(callback)](\n            Try<T> resultTry) mutable -> folly::coro::Task<bool> {\n          (*cbk)(std::move(resultTry));\n          co_return true;\n        });\n    return std::make_pair(std::move(handle), callbackPtr);\n  }\n\n  StrictMock<MockNextCallback<std::string>> createCallback() {\n    return StrictMock<MockNextCallback<std::string>>();\n  }\n\n  folly::ManualExecutor executor_;\n};\n\nTEST_F(FanoutChannelFixture, ReceiveValue_FanoutBroadcastsValues) {\n  struct LatestVersion {\n    int version{-1};\n    size_t numSubscribers{0};\n\n    void update(int& newVersion, size_t newNumSubscribers) {\n      version = newVersion;\n      numSubscribers = newNumSubscribers;\n    }\n  };\n\n  auto [inputReceiver, sender] = Channel<int>::create();\n  auto fanoutChannel = createFanoutChannel(\n      std::move(inputReceiver), &executor_, LatestVersion());\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n\n  auto [handle1, callback1] = processValues(fanoutChannel.subscribe(\n      [](const auto&) { return toVector(100); } /* getInitialValues */));\n  auto [handle2, callback2] = processValues(fanoutChannel.subscribe(\n      [](const auto&) { return toVector(200); } /* getInitialValues */));\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n  EXPECT_CALL(*callback1, onValue(100));\n  EXPECT_CALL(*callback2, onValue(200));\n  executor_.drain();\n\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n  EXPECT_CALL(*callback1, onValue(2));\n  EXPECT_CALL(*callback2, onValue(2));\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  EXPECT_EQ(fanoutChannel.getContext().version, 2);\n\n  auto [handle3, callback3] = processValues(\n      fanoutChannel.subscribe([](const LatestVersion& latestVersion) {\n        EXPECT_EQ(latestVersion.numSubscribers, 2);\n        return toVector(latestVersion.version);\n      } /* getInitialValues */));\n\n  EXPECT_CALL(*callback3, onValue(2));\n  executor_.drain();\n\n  sender.write(3);\n  EXPECT_CALL(*callback1, onValue(3));\n  EXPECT_CALL(*callback2, onValue(3));\n  EXPECT_CALL(*callback3, onValue(3));\n\n  std::move(sender).close();\n  EXPECT_CALL(*callback1, onClosed());\n  EXPECT_CALL(*callback2, onClosed());\n  EXPECT_CALL(*callback3, onClosed());\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n}\n\nTEST_F(FanoutChannelFixture, InputClosed_AllOutputReceiversClose) {\n  auto [inputReceiver, sender] = Channel<int>::create();\n  auto fanoutChannel =\n      createFanoutChannel(std::move(inputReceiver), &executor_);\n\n  auto [handle1, callback1] = processValues(fanoutChannel.subscribe());\n  auto [handle2, callback2] = processValues(fanoutChannel.subscribe());\n\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n  EXPECT_CALL(*callback1, onClosed());\n  EXPECT_CALL(*callback2, onClosed());\n\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  sender.write(1);\n  executor_.drain();\n\n  std::move(sender).close();\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n}\n\nTEST_F(FanoutChannelFixture, InputThrows_AllOutputReceiversGetException) {\n  auto [inputReceiver, sender] = Channel<int>::create();\n  auto fanoutChannel =\n      createFanoutChannel(std::move(inputReceiver), &executor_);\n\n  auto [handle1, callback1] = processValues(fanoutChannel.subscribe());\n  auto [handle2, callback2] = processValues(fanoutChannel.subscribe());\n\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n  EXPECT_CALL(*callback1, onRuntimeError(\"std::runtime_error: Error\"));\n  EXPECT_CALL(*callback2, onRuntimeError(\"std::runtime_error: Error\"));\n\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  sender.write(1);\n  executor_.drain();\n\n  std::move(sender).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n}\n\nTEST_F(FanoutChannelFixture, ReceiversCancelled) {\n  auto [inputReceiver, sender] = Channel<int>::create();\n  auto fanoutChannel =\n      createFanoutChannel(std::move(inputReceiver), &executor_);\n\n  auto [handle1, callback1] = processValues(fanoutChannel.subscribe());\n  auto [handle2, callback2] = processValues(fanoutChannel.subscribe());\n\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n  EXPECT_CALL(*callback1, onCancelled());\n  EXPECT_CALL(*callback2, onValue(2));\n  EXPECT_CALL(*callback2, onCancelled());\n\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  sender.write(1);\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  handle1.reset();\n  sender.write(2);\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  handle2.reset();\n  sender.write(3);\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n\n  std::move(sender).close();\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n}\n\nTEST_F(FanoutChannelFixture, SubscribersClosed) {\n  auto [inputReceiver, sender] = Channel<int>::create();\n  auto fanoutChannel =\n      createFanoutChannel(std::move(inputReceiver), &executor_);\n\n  auto [handle1, callback1] = processValues(fanoutChannel.subscribe());\n  auto [handle2, callback2] = processValues(fanoutChannel.subscribe());\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n  sender.write(1);\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  EXPECT_CALL(*callback1, onClosed());\n  EXPECT_CALL(*callback2, onClosed());\n  fanoutChannel.closeSubscribers();\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n\n  auto [handle3, callback3] = processValues(fanoutChannel.subscribe());\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  EXPECT_CALL(*callback3, onValue(2));\n  sender.write(2);\n  executor_.drain();\n\n  EXPECT_CALL(*callback3, onClosed());\n  std::move(fanoutChannel).close();\n  executor_.drain();\n}\n\nTEST_F(FanoutChannelFixture, VectorBool) {\n  auto [inputReceiver, sender] = Channel<bool>::create();\n  auto fanoutChannel =\n      createFanoutChannel(std::move(inputReceiver), &executor_);\n\n  auto [handle1, callback1] = processValues(fanoutChannel.subscribe(\n      [](const auto&) { return toVector(true); } /* getInitialValues */));\n  auto [handle2, callback2] = processValues(fanoutChannel.subscribe(\n      [](const auto&) { return toVector(false); } /* getInitialValues */));\n\n  EXPECT_CALL(*callback1, onValue(true));\n  EXPECT_CALL(*callback2, onValue(false));\n\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutChannel.anySubscribers());\n\n  EXPECT_CALL(*callback1, onValue(true));\n  EXPECT_CALL(*callback2, onValue(true));\n  EXPECT_CALL(*callback1, onValue(false));\n  EXPECT_CALL(*callback2, onValue(false));\n\n  EXPECT_CALL(*callback1, onClosed());\n  EXPECT_CALL(*callback2, onClosed());\n\n  sender.write(true);\n  sender.write(false);\n  executor_.drain();\n\n  std::move(sender).close();\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutChannel.anySubscribers());\n}\n\nclass FanoutChannelFixtureStress : public Test {\n protected:\n  FanoutChannelFixtureStress()\n      : producer_(makeProducer()),\n        consumers_(toVector(makeConsumer(), makeConsumer(), makeConsumer())) {}\n\n  static std::unique_ptr<StressTestProducer<int>> makeProducer() {\n    return std::make_unique<StressTestProducer<int>>([value = 0]() mutable {\n      return value++;\n    });\n  }\n\n  static std::unique_ptr<StressTestConsumer<int>> makeConsumer() {\n    return std::make_unique<StressTestConsumer<int>>(\n        ConsumptionMode::CallbackWithHandle,\n        [lastReceived = -1](int value) mutable {\n          if (lastReceived == -1) {\n            lastReceived = value;\n          } else {\n            EXPECT_EQ(value, ++lastReceived);\n          }\n        });\n  }\n\n  static void sleepFor(std::chrono::milliseconds duration) {\n    /* sleep override */\n    std::this_thread::sleep_for(duration);\n  }\n\n  static constexpr std::chrono::milliseconds kTestTimeout =\n      std::chrono::milliseconds{10};\n\n  std::unique_ptr<StressTestProducer<int>> producer_;\n  std::vector<std::unique_ptr<StressTestConsumer<int>>> consumers_;\n};\n\nTEST_F(FanoutChannelFixtureStress, HandleClosed) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor fanoutChannelExecutor(1);\n  auto fanoutChannel = createFanoutChannel(\n      std::move(receiver),\n      folly::SerialExecutor::create(&fanoutChannelExecutor));\n\n  consumers_.at(0)->startConsuming(fanoutChannel.subscribe());\n  consumers_.at(1)->startConsuming(fanoutChannel.subscribe());\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(2)->startConsuming(fanoutChannel.subscribe());\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(0)->cancel();\n  EXPECT_EQ(consumers_.at(0)->waitForClose().get(), CloseType::Cancelled);\n\n  sleepFor(kTestTimeout / 3);\n\n  std::move(fanoutChannel).close();\n  EXPECT_EQ(consumers_.at(1)->waitForClose().get(), CloseType::NoException);\n  EXPECT_EQ(consumers_.at(2)->waitForClose().get(), CloseType::NoException);\n}\n\nTEST_F(FanoutChannelFixtureStress, InputChannelClosed) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor fanoutChannelExecutor(1);\n  auto fanoutChannel = createFanoutChannel(\n      std::move(receiver),\n      folly::SerialExecutor::create(&fanoutChannelExecutor));\n\n  consumers_.at(0)->startConsuming(fanoutChannel.subscribe());\n  consumers_.at(1)->startConsuming(fanoutChannel.subscribe());\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(2)->startConsuming(fanoutChannel.subscribe());\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(0)->cancel();\n  EXPECT_EQ(consumers_.at(0)->waitForClose().get(), CloseType::Cancelled);\n\n  sleepFor(kTestTimeout / 3);\n\n  producer_->stopProducing();\n  EXPECT_EQ(consumers_.at(1)->waitForClose().get(), CloseType::NoException);\n  EXPECT_EQ(consumers_.at(2)->waitForClose().get(), CloseType::NoException);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/FanoutSenderTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/FanoutSender.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\n\nclass FanoutSenderFixture : public Test {\n protected:\n  FanoutSenderFixture() {}\n\n  using TCallback = StrictMock<MockNextCallback<int>>;\n\n  std::pair<ChannelCallbackHandle, TCallback*> processValues(\n      Receiver<int> receiver) {\n    auto callback = std::make_unique<TCallback>();\n    auto callbackPtr = callback.get();\n    auto handle = consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [cbk = std::move(callback)](\n            Try<int> resultTry) mutable -> folly::coro::Task<bool> {\n          (*cbk)(std::move(resultTry));\n          co_return true;\n        });\n    return std::make_pair(std::move(handle), callbackPtr);\n  }\n\n  StrictMock<MockNextCallback<std::string>> createCallback() {\n    return StrictMock<MockNextCallback<std::string>>();\n  }\n\n  folly::ManualExecutor executor_;\n};\n\nTEST_F(FanoutSenderFixture, WriteValue_FanoutBroadcastsValues) {\n  auto fanoutSender = FanoutSender<int>();\n\n  fanoutSender.write(-1);\n\n  EXPECT_FALSE(fanoutSender.anySubscribers());\n\n  auto [handle1, callback1] =\n      processValues(fanoutSender.subscribe(toVector(100)));\n\n  EXPECT_CALL(*callback1, onValue(100));\n  EXPECT_CALL(*callback1, onValue(0));\n\n  fanoutSender.write(0);\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutSender.anySubscribers());\n\n  auto [handle2, callback2] =\n      processValues(fanoutSender.subscribe(toVector(200)));\n\n  EXPECT_CALL(*callback2, onValue(200));\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n  EXPECT_CALL(*callback1, onValue(2));\n  EXPECT_CALL(*callback2, onValue(2));\n\n  fanoutSender.write(1);\n  fanoutSender.write(2);\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutSender.anySubscribers());\n\n  EXPECT_CALL(*callback1, onClosed());\n  EXPECT_CALL(*callback2, onClosed());\n\n  std::move(fanoutSender).close();\n  executor_.drain();\n}\n\nTEST_F(FanoutSenderFixture, InputThrows_AllOutputReceiversGetException) {\n  auto fanoutSender = FanoutSender<int>();\n\n  auto [handle1, callback1] = processValues(fanoutSender.subscribe());\n  auto [handle2, callback2] = processValues(fanoutSender.subscribe());\n\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n  EXPECT_CALL(*callback1, onRuntimeError(\"std::runtime_error: Error\"));\n  EXPECT_CALL(*callback2, onRuntimeError(\"std::runtime_error: Error\"));\n\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutSender.anySubscribers());\n\n  fanoutSender.write(1);\n  executor_.drain();\n\n  std::move(fanoutSender).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n\n  fanoutSender = FanoutSender<int>();\n\n  std::tie(handle1, callback1) = processValues(fanoutSender.subscribe());\n  executor_.drain();\n\n  EXPECT_CALL(*callback1, onRuntimeError(\"std::runtime_error: Error\"));\n\n  std::move(fanoutSender).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n}\n\nTEST_F(FanoutSenderFixture, ReceiversCancelled) {\n  auto fanoutSender = FanoutSender<int>();\n\n  auto [handle1, callback1] = processValues(fanoutSender.subscribe());\n  auto [handle2, callback2] = processValues(fanoutSender.subscribe());\n\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutSender.anySubscribers());\n\n  EXPECT_CALL(*callback1, onValue(1));\n  EXPECT_CALL(*callback2, onValue(1));\n\n  fanoutSender.write(1);\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutSender.anySubscribers());\n\n  EXPECT_CALL(*callback1, onCancelled());\n\n  handle1.reset();\n  executor_.drain();\n\n  EXPECT_CALL(*callback2, onValue(2));\n\n  fanoutSender.write(2);\n  executor_.drain();\n\n  EXPECT_TRUE(fanoutSender.anySubscribers());\n\n  EXPECT_CALL(*callback2, onCancelled());\n\n  handle2.reset();\n  executor_.drain();\n\n  EXPECT_FALSE(fanoutSender.anySubscribers());\n\n  fanoutSender.write(3);\n  executor_.drain();\n\n  std::move(fanoutSender).close();\n  executor_.drain();\n}\n\nTEST_F(FanoutSenderFixture, ReceiverCancelled_DelayedCancellationCallback) {\n  auto fanoutSender = FanoutSender<int>();\n\n  auto receiver1 = fanoutSender.subscribe();\n  auto [handle2, callback2] = processValues(fanoutSender.subscribe());\n\n  auto [bridge1, _] = detail::receiverUnbuffer(std::move(receiver1));\n\n  // This call prevents the fanout sender from receiving the cancellation\n  // callback from receiver1. We will instead call that callback ourselves\n  // below, to simulate the case where the cancellation has occurred but has not\n  // yet been processed by the fanout sender at the time the fanout sender\n  // attempts to destroy its internal processor.\n  auto callback = bridge1->cancelSenderWait();\n\n  // This call actually cancels receiver1.\n  bridge1->receiverCancel();\n\n  EXPECT_CALL(*callback2, onValue(1));\n\n  fanoutSender.write(1);\n  executor_.drain();\n\n  // This call triggers the cancellation callback that would have occurred\n  // before, if we had not cancelled it. This should trigger the destruction of\n  // the FanoutSenderProcessor.\n  callback->consume(bridge1.get());\n\n  EXPECT_CALL(*callback2, onClosed());\n  std::move(fanoutSender).close();\n  executor_.drain();\n}\n\nTEST_F(FanoutSenderFixture, Close_DelayedCancellationCallback) {\n  auto fanoutSender = FanoutSender<int>();\n\n  auto receiver1 = fanoutSender.subscribe();\n  auto receiver2 = fanoutSender.subscribe();\n\n  auto [bridge1, _] = detail::receiverUnbuffer(std::move(receiver1));\n\n  // This call prevents the fanout sender from receiving the cancellation\n  // callback from receiver1. We will instead call that callback ourselves\n  // below, to simulate the case where the cancellation has occurred but has not\n  // yet been processed by the fanout sender at the time the fanout sender was\n  // closed.\n  auto callback = bridge1->cancelSenderWait();\n\n  // This call actually cancels receiver1.\n  bridge1->receiverCancel();\n\n  std::move(fanoutSender).close();\n\n  // This call triggers the cancellation callback that would have occurred\n  // before, if we had not cancelled it. This should trigger the destruction of\n  // the FanoutSenderProcessor.\n  callback->consume(bridge1.get());\n}\n\nTEST_F(FanoutSenderFixture, NumSubscribers) {\n  auto sender = FanoutSender<int>{};\n  EXPECT_EQ(sender.numSubscribers(), 0);\n\n  auto receiver1 = std::make_unique<Receiver<int>>(sender.subscribe());\n  EXPECT_EQ(sender.numSubscribers(), 1);\n\n  auto receiver2 = std::make_unique<Receiver<int>>(sender.subscribe());\n  EXPECT_EQ(sender.numSubscribers(), 2);\n\n  auto receiver3 = std::make_unique<Receiver<int>>(sender.subscribe());\n  EXPECT_EQ(sender.numSubscribers(), 3);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/MaxConcurrentRateLimiterTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/MaxConcurrentRateLimiter.h>\n#include <folly/executors/ManualExecutor.h>\n\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nusing namespace testing;\n\nnamespace folly {\nnamespace channels {\n\nclass MaxConcurrentRateLimiterTest : public Test {\n public:\n  MaxConcurrentRateLimiterTest() {\n    manualExecutor_ = std::make_unique<ManualExecutor>();\n\n    maxConcurrentRateLimiter_ = MaxConcurrentRateLimiter::create(5);\n  }\n\n  std::unique_ptr<ManualExecutor> manualExecutor_;\n  std::shared_ptr<MaxConcurrentRateLimiter> maxConcurrentRateLimiter_;\n};\n\nTEST_F(MaxConcurrentRateLimiterTest, Basic) {\n  auto tokens = std::vector<std::unique_ptr<RateLimiter::Token>>{};\n\n  maxConcurrentRateLimiter_->executeWhenReady(\n      [&](auto token) { tokens.push_back(std::move(token)); },\n      manualExecutor_.get());\n\n  manualExecutor_->drain();\n  EXPECT_EQ(tokens.size(), 1);\n}\n\nTEST_F(MaxConcurrentRateLimiterTest, UnderQuota) {\n  auto tokens = std::vector<std::unique_ptr<RateLimiter::Token>>{};\n\n  for (auto i = 0; i < 5; ++i) {\n    maxConcurrentRateLimiter_->executeWhenReady(\n        [&](auto token) { tokens.push_back(std::move(token)); },\n        manualExecutor_.get());\n  }\n\n  manualExecutor_->drain();\n  EXPECT_EQ(tokens.size(), 5);\n}\n\nTEST_F(MaxConcurrentRateLimiterTest, OverQuota) {\n  auto tokens = std::vector<std::unique_ptr<RateLimiter::Token>>{};\n\n  for (auto i = 0; i < 10; ++i) {\n    maxConcurrentRateLimiter_->executeWhenReady(\n        [&](auto token) { tokens.push_back(std::move(token)); },\n        manualExecutor_.get());\n  }\n\n  manualExecutor_->drain();\n  EXPECT_EQ(tokens.size(), 5);\n\n  for (auto i = 0; i < 5; ++i) {\n    tokens.pop_back();\n    manualExecutor_->drain();\n    EXPECT_EQ(tokens.size(), 5);\n  }\n\n  tokens.clear();\n  manualExecutor_->drain();\n\n  EXPECT_TRUE(tokens.empty());\n}\n\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/MergeChannelTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fmt/format.h>\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/MergeChannel.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace std::string_literals;\nusing namespace testing;\n\ntemplate <typename T, typename U>\nbool isMatch(\n    const std::string& type,\n    const T& actual,\n    const U& expected,\n    testing::MatchResultListener* resultListener) {\n  if (expected != actual) {\n    *resultListener << fmt::format(\n        \"{} mismatch: ({} != {})\", type, actual, expected);\n    return false;\n  }\n  return true;\n}\n\nMATCHER_P(ReceiverAdded, expectedKey, \"\") {\n  return isMatch(\n             \"Is receiver added event\",\n             std::holds_alternative<MergeChannelReceiverAdded>(arg.event),\n             true,\n             result_listener) &&\n      isMatch(\"Key\", arg.key, expectedKey, result_listener);\n}\n\nMATCHER_P(ReceiverRemoved, expectedKey, \"\") {\n  return isMatch(\n             \"Is receiver removed event\",\n             std::holds_alternative<MergeChannelReceiverRemoved>(arg.event),\n             true,\n             result_listener) &&\n      isMatch(\"Key\", arg.key, expectedKey, result_listener);\n}\n\nMATCHER_P2(ValueReceived, expectedKey, expectedValue, \"\") {\n  return isMatch(\n             \"Is value received event\",\n             std::holds_alternative<int>(arg.event),\n             true,\n             result_listener) &&\n      isMatch(\"Key\", arg.key, expectedKey, result_listener) &&\n      isMatch(\n             \"Value\", std::get<int>(arg.event), expectedValue, result_listener);\n}\n\nMATCHER_P(ReceiverClosed, expectedKey, \"\") {\n  return isMatch(\n             \"Is receiver closed event\",\n             std::holds_alternative<MergeChannelReceiverClosed>(arg.event),\n             true,\n             result_listener) &&\n      isMatch(\"Key\", arg.key, expectedKey, result_listener) &&\n      isMatch(\n             \"Exception present\",\n             !!std::get<MergeChannelReceiverClosed>(arg.event).exception,\n             false,\n             result_listener);\n}\n\nMATCHER_P(ReceiverClosedRuntimeError, expectedKey, \"\") {\n  if (!std::holds_alternative<MergeChannelReceiverClosed>(arg.event)) {\n    *result_listener << \"Event is not for a closed receiver\";\n    return false;\n  }\n  return isMatch(\"Key\", arg.key, expectedKey, result_listener) &&\n      isMatch(\n             \"Exception present\",\n             !!std::get<MergeChannelReceiverClosed>(arg.event).exception,\n             true,\n             result_listener) &&\n      isMatch(\n             \"Runtime error present\",\n             std::get<MergeChannelReceiverClosed>(arg.event)\n                 .exception.template is_compatible_with<std::runtime_error>(),\n             true,\n             result_listener);\n}\n\nclass MergeChannelFixture : public Test {\n protected:\n  MergeChannelFixture() {}\n\n  ~MergeChannelFixture() { executor_.drain(); }\n\n  using TCallback = StrictMock<MockNextCallback<int>>;\n\n  ChannelCallbackHandle processValues(\n      Receiver<MergeChannelEvent<std::string, int>> receiver) {\n    return consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [=, this](Try<MergeChannelEvent<std::string, int>> resultTry)\n            -> folly::coro::Task<bool> {\n          if (resultTry.hasValue()) {\n            std::visit(\n                [](const auto& eventType) {\n                  LOG(INFO) << \"Type: \" << typeid(eventType).name();\n                },\n                resultTry.value().event);\n          }\n          onNext_(std::move(resultTry));\n          co_return true;\n        });\n  }\n\n  folly::ManualExecutor executor_;\n  StrictMock<MockNextCallback<MergeChannelEvent<std::string, int>>> onNext_;\n};\n\nTEST_F(MergeChannelFixture, ReceiveValues_ReturnMergedValues) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub3\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 3)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub3\"s, 5)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub3\"s)));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(2);\n  executor_.drain();\n\n  mergeChannel.addNewReceiver(\"sub3\", std::move(receiver3));\n  mergeChannel.removeReceiver(\"sub2\");\n\n  sender1.write(3);\n  sender2.write(4);\n  sender3.write(5);\n  executor_.drain();\n\n  std::move(mergeChannel).close();\n  executor_.drain();\n}\n\nTEST_F(\n    MergeChannelFixture,\n    ReceiveValues_NewInputReceiver_WithSameSubscriptionId) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2a, sender2a] = Channel<int>::create();\n  auto [receiver2b, sender2b] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s))).RetiresOnSaturation();\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 3)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 5)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub2\"s))).RetiresOnSaturation();\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2a));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2a.write(2);\n  executor_.drain();\n\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2b));\n\n  sender1.write(3);\n  sender2a.write(4);\n  sender2b.write(5);\n  executor_.drain();\n\n  std::move(mergeChannel).close();\n  executor_.drain();\n}\n\nTEST_F(\n    MergeChannelFixture,\n    ReceiveValues_NewInputReceiver_WithSameSubscriptionId_AfterClose) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2a, sender2a] = Channel<int>::create();\n  auto [receiver2b, sender2b] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosed(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s))).RetiresOnSaturation();\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 3)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 5)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2a));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2a.write(2);\n  executor_.drain();\n\n  std::move(sender2a).close();\n  executor_.drain();\n\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2b));\n  executor_.drain();\n\n  sender1.write(3);\n  sender2b.write(5);\n  executor_.drain();\n\n  std::move(mergeChannel).close();\n  executor_.drain();\n}\n\nTEST_F(MergeChannelFixture, ReceiveValues_RemoveReceiver) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 3)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(2);\n  executor_.drain();\n\n  mergeChannel.removeReceiver(\"sub2\");\n\n  sender1.write(3);\n  sender2.write(4);\n  executor_.drain();\n\n  std::move(mergeChannel).close();\n  executor_.drain();\n}\n\nTEST_F(MergeChannelFixture, ReceiveValues_RemoveReceiver_AfterClose) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosed(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 3)));\n  EXPECT_CALL(onNext_, onValue(ReceiverRemoved(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(2);\n  executor_.drain();\n\n  std::move(sender2).close();\n  executor_.drain();\n\n  mergeChannel.removeReceiver(\"sub2\");\n  sender1.write(3);\n  executor_.drain();\n\n  std::move(mergeChannel).close();\n  executor_.drain();\n}\n\nTEST_F(MergeChannelFixture, OneInputClosed_ContinuesMerging) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub3\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub3\"s, 3)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosed(\"sub3\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 4)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 5)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosed(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosed(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2));\n  mergeChannel.addNewReceiver(\"sub3\", std::move(receiver3));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  std::move(sender3).close();\n  executor_.drain();\n\n  sender1.write(4);\n  sender2.write(5);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  executor_.drain();\n\n  std::move(mergeChannel).close();\n  executor_.drain();\n}\n\nTEST_F(MergeChannelFixture, OneInputThrows_ContinuesMerging) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub3\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub3\"s, 3)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosedRuntimeError(\"sub3\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 4)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 5)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosed(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverClosed(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2));\n  mergeChannel.addNewReceiver(\"sub3\", std::move(receiver3));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  std::move(sender3).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n\n  sender1.write(4);\n  sender2.write(5);\n  std::move(sender1).close();\n  std::move(sender2).close();\n  executor_.drain();\n\n  std::move(mergeChannel).close();\n  executor_.drain();\n}\n\nTEST_F(MergeChannelFixture, Cancelled) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto [mergedReceiver, mergeChannel] =\n      createMergeChannel<std::string, int>(&executor_);\n\n  mergeChannel.addNewReceiver(\"sub1\", std::move(receiver1));\n  mergeChannel.addNewReceiver(\"sub2\", std::move(receiver2));\n  mergeChannel.addNewReceiver(\"sub3\", std::move(receiver3));\n  executor_.drain();\n\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub1\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub2\"s)));\n  EXPECT_CALL(onNext_, onValue(ReceiverAdded(\"sub3\"s)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub1\"s, 1)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub2\"s, 2)));\n  EXPECT_CALL(onNext_, onValue(ValueReceived(\"sub3\"s, 3)));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  executor_.drain();\n\n  callbackHandle.reset();\n  executor_.drain();\n\n  sender1.write(4);\n  sender2.write(5);\n  sender3.write(6);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  std::move(sender3).close();\n  executor_.drain();\n}\n\nstruct ProducedValue {\n  int producerIndex;\n  int value;\n};\n\nclass MergeChannelFixtureStress : public Test {\n protected:\n  MergeChannelFixtureStress()\n      : producers_(toVector(makeProducer(0), makeProducer(1))),\n        consumer_(makeConsumer()) {}\n\n  static std::unique_ptr<StressTestProducer<ProducedValue>> makeProducer(\n      int index) {\n    return std::make_unique<StressTestProducer<ProducedValue>>(\n        [index, value = 0]() mutable { return ProducedValue{index, value++}; });\n  }\n\n  static std::unique_ptr<\n      StressTestConsumer<MergeChannelEvent<int, ProducedValue>>>\n  makeConsumer() {\n    return std::make_unique<\n        StressTestConsumer<MergeChannelEvent<int, ProducedValue>>>(\n        ConsumptionMode::CallbackWithHandle,\n        [lastReceived = toVector(-1, -1, -1)](\n            MergeChannelEvent<int, ProducedValue> result) mutable {\n          if (std::holds_alternative<ProducedValue>(result.event)) {\n            auto value = std::get<ProducedValue>(result.event);\n            EXPECT_EQ(value.value, ++lastReceived[value.producerIndex]);\n          }\n        });\n  }\n\n  static void sleepFor(std::chrono::milliseconds duration) {\n    /* sleep override */\n    std::this_thread::sleep_for(duration);\n  }\n\n  static constexpr std::chrono::milliseconds kTestTimeout =\n      std::chrono::milliseconds{5000};\n\n  std::vector<std::unique_ptr<StressTestProducer<ProducedValue>>> producers_;\n  std::unique_ptr<StressTestConsumer<MergeChannelEvent<int, ProducedValue>>>\n      consumer_;\n};\n\nTEST_F(MergeChannelFixtureStress, HandleClosed) {\n  folly::CPUThreadPoolExecutor mergeChannelExecutor(1);\n  auto [mergeReceiver, mergeChannel] = createMergeChannel<int, ProducedValue>(\n      folly::SerialExecutor::create(&mergeChannelExecutor));\n  consumer_->startConsuming(std::move(mergeReceiver));\n\n  auto [receiver0, sender0] = Channel<ProducedValue>::create();\n  producers_.at(0)->startProducing(std::move(sender0), std::nullopt);\n  mergeChannel.addNewReceiver(0 /* subscriptionId */, std::move(receiver0));\n\n  sleepFor(kTestTimeout / 3);\n\n  auto [receiver1, sender1] = Channel<ProducedValue>::create();\n  producers_.at(1)->startProducing(std::move(sender1), std::nullopt);\n  mergeChannel.addNewReceiver(1 /* subscriptionId */, std::move(receiver1));\n\n  sleepFor(kTestTimeout / 3);\n\n  mergeChannel.removeReceiver(0 /* subscriptionId */);\n\n  sleepFor(kTestTimeout / 3);\n\n  std::move(mergeChannel).close();\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::NoException);\n}\n\nTEST_F(MergeChannelFixtureStress, Cancelled) {\n  folly::CPUThreadPoolExecutor mergeChannelExecutor(1);\n  auto [mergeReceiver, mergeChannel] = createMergeChannel<int, ProducedValue>(\n      folly::SerialExecutor::create(&mergeChannelExecutor));\n  consumer_->startConsuming(std::move(mergeReceiver));\n\n  auto [receiver0, sender0] = Channel<ProducedValue>::create();\n  producers_.at(0)->startProducing(std::move(sender0), std::nullopt);\n  mergeChannel.addNewReceiver(0 /* subscriptionId */, std::move(receiver0));\n\n  sleepFor(kTestTimeout / 3);\n\n  auto [receiver1, sender1] = Channel<ProducedValue>::create();\n  producers_.at(1)->startProducing(std::move(sender1), std::nullopt);\n  mergeChannel.addNewReceiver(1 /* subscriptionId */, std::move(receiver1));\n\n  sleepFor(kTestTimeout / 3);\n\n  mergeChannel.removeReceiver(0 /* subscriptionId */);\n\n  sleepFor(kTestTimeout / 3);\n\n  consumer_->cancel();\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::Cancelled);\n}\n\nTEST_F(MergeChannelFixtureStress, Cancelled_ThenHandleClosedImmediately) {\n  folly::CPUThreadPoolExecutor mergeChannelExecutor(1);\n  auto [mergeReceiver, mergeChannel] = createMergeChannel<int, ProducedValue>(\n      folly::SerialExecutor::create(&mergeChannelExecutor));\n  consumer_->startConsuming(std::move(mergeReceiver));\n\n  auto [receiver0, sender0] = Channel<ProducedValue>::create();\n  producers_.at(0)->startProducing(std::move(sender0), std::nullopt);\n  mergeChannel.addNewReceiver(0 /* subscriptionId */, std::move(receiver0));\n\n  sleepFor(kTestTimeout / 3);\n\n  auto [receiver1, sender1] = Channel<ProducedValue>::create();\n  producers_.at(1)->startProducing(std::move(sender1), std::nullopt);\n  mergeChannel.addNewReceiver(1 /* subscriptionId */, std::move(receiver1));\n\n  sleepFor(kTestTimeout / 3);\n\n  mergeChannel.removeReceiver(0 /* subscriptionId */);\n\n  sleepFor(kTestTimeout / 3);\n\n  consumer_->cancel();\n  std::move(mergeChannel).close();\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n\nTEST_F(MergeChannelFixtureStress, HandleClosed_ThenCancelledImmediately) {\n  folly::CPUThreadPoolExecutor mergeChannelExecutor(1);\n  auto [mergeReceiver, mergeChannel] = createMergeChannel<int, ProducedValue>(\n      folly::SerialExecutor::create(&mergeChannelExecutor));\n  consumer_->startConsuming(std::move(mergeReceiver));\n\n  auto [receiver0, sender0] = Channel<ProducedValue>::create();\n  producers_.at(0)->startProducing(std::move(sender0), std::nullopt);\n  mergeChannel.addNewReceiver(0 /* subscriptionId */, std::move(receiver0));\n\n  sleepFor(kTestTimeout / 3);\n\n  auto [receiver1, sender1] = Channel<ProducedValue>::create();\n  producers_.at(1)->startProducing(std::move(sender1), std::nullopt);\n  mergeChannel.addNewReceiver(1 /* subscriptionId */, std::move(receiver1));\n\n  sleepFor(kTestTimeout / 3);\n\n  mergeChannel.removeReceiver(0 /* subscriptionId */);\n\n  sleepFor(kTestTimeout / 3);\n\n  std::move(mergeChannel).close();\n  consumer_->cancel();\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/MergeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/Merge.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\n\nclass MergeFixture : public Test {\n protected:\n  ~MergeFixture() { executor_.drain(); }\n\n  ChannelCallbackHandle processValues(Receiver<int> receiver) {\n    return consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [=, this](Try<int> resultTry) -> folly::coro::Task<bool> {\n          onNext_(std::move(resultTry));\n          co_return true;\n        });\n  }\n\n  folly::ManualExecutor executor_;\n  StrictMock<MockNextCallback<int>> onNext_;\n};\n\nTEST_F(MergeFixture, ReceiveValues_ReturnMergedValues) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto mergedReceiver = merge(\n      toVector(\n          std::move(receiver1), std::move(receiver2), std::move(receiver3)),\n      &executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onValue(4));\n  EXPECT_CALL(onNext_, onValue(5));\n  EXPECT_CALL(onNext_, onValue(6));\n  EXPECT_CALL(onNext_, onClosed());\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  executor_.drain();\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  sender1.write(4);\n  sender2.write(5);\n  sender3.write(6);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  std::move(sender3).close();\n  executor_.drain();\n}\n\nTEST_F(MergeFixture, OneInputClosed_WaitForAllInputsToClose_ContinuesMerging) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto mergedReceiver = merge(\n      toVector(\n          std::move(receiver1), std::move(receiver2), std::move(receiver3)),\n      &executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onValue(4));\n  EXPECT_CALL(onNext_, onValue(5));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  std::move(sender3).close();\n  executor_.drain();\n\n  sender1.write(4);\n  sender2.write(5);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  executor_.drain();\n}\n\nTEST_F(MergeFixture, OneInputClosed_DoNotWaitForAllInputsToClose_StopsMerging) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto mergedReceiver = merge(\n      toVector(\n          std::move(receiver1), std::move(receiver2), std::move(receiver3)),\n      &executor_,\n      false /* waitForAllInputsToClose */);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  std::move(sender3).close();\n  executor_.drain();\n\n  sender1.write(4);\n  executor_.drain();\n\n  sender2.write(5);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  executor_.drain();\n}\n\nTEST_F(MergeFixture, OneInputThrows_OutputClosedWithException) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto mergedReceiver = merge(\n      toVector(\n          std::move(receiver1), std::move(receiver2), std::move(receiver3)),\n      &executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  std::move(sender3).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n\n  sender1.write(4);\n  sender2.write(5);\n  std::move(sender1).close();\n  std::move(sender2).close();\n  executor_.drain();\n}\n\nTEST_F(MergeFixture, CancelledAfterStart) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto mergedReceiver = merge(\n      toVector(\n          std::move(receiver1), std::move(receiver2), std::move(receiver3)),\n      &executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  auto callbackHandle = processValues(std::move(mergedReceiver));\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  executor_.drain();\n\n  callbackHandle.reset();\n  executor_.drain();\n\n  sender1.write(4);\n  sender2.write(5);\n  sender3.write(6);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  std::move(sender3).close();\n  executor_.drain();\n}\n\nTEST_F(MergeFixture, CancelledBeforeStart) {\n  auto [receiver1, sender1] = Channel<int>::create();\n  auto [receiver2, sender2] = Channel<int>::create();\n  auto [receiver3, sender3] = Channel<int>::create();\n  auto mergedReceiver = merge(\n      toVector(\n          std::move(receiver1), std::move(receiver2), std::move(receiver3)),\n      &executor_);\n\n  EXPECT_CALL(onNext_, onValue(_)).Times(0);\n\n  {\n    auto toDestroy = std::move(mergedReceiver);\n  }\n\n  sender1.write(1);\n  sender2.write(2);\n  sender3.write(3);\n  executor_.drain();\n}\n\nstruct ProducedValue {\n  int producerIndex;\n  int value;\n};\n\nclass MergeFixtureStress : public Test {\n protected:\n  MergeFixtureStress()\n      : producers_(toVector(makeProducer(0), makeProducer(1), makeProducer(2))),\n        consumer_(makeConsumer()) {}\n\n  static std::unique_ptr<StressTestProducer<ProducedValue>> makeProducer(\n      int index) {\n    return std::make_unique<StressTestProducer<ProducedValue>>(\n        [index, value = 0]() mutable { return ProducedValue{index, value++}; });\n  }\n\n  static std::unique_ptr<StressTestConsumer<ProducedValue>> makeConsumer() {\n    return std::make_unique<StressTestConsumer<ProducedValue>>(\n        ConsumptionMode::CallbackWithHandle,\n        [lastReceived = toVector(-1, -1, -1)](ProducedValue value) mutable {\n          EXPECT_EQ(value.value, ++lastReceived[value.producerIndex]);\n        });\n  }\n\n  static constexpr std::chrono::milliseconds kTestTimeout =\n      std::chrono::milliseconds{5000};\n\n  std::vector<std::unique_ptr<StressTestProducer<ProducedValue>>> producers_;\n  std::unique_ptr<StressTestConsumer<ProducedValue>> consumer_;\n};\n\nTEST_F(MergeFixtureStress, Close_NoException) {\n  auto receivers = std::vector<Receiver<ProducedValue>>();\n  for (auto& producer : producers_) {\n    auto [receiver, sender] = Channel<ProducedValue>::create();\n    receivers.push_back(std::move(receiver));\n    producer->startProducing(std::move(sender), std::nullopt /* closeEx */);\n  }\n\n  folly::CPUThreadPoolExecutor mergeExecutor(1);\n  consumer_->startConsuming(merge(\n      std::move(receivers), folly::SerialExecutor::create(&mergeExecutor)));\n\n  for (auto& producer : producers_) {\n    /* sleep override */\n    std::this_thread::sleep_for(kTestTimeout / 3);\n    producer->stopProducing();\n  }\n\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::NoException);\n}\n\nTEST_F(MergeFixtureStress, Close_Exception) {\n  auto receivers = std::vector<Receiver<ProducedValue>>();\n  for (size_t i = 0; i < producers_.size(); i++) {\n    auto [receiver, sender] = Channel<ProducedValue>::create();\n    receivers.push_back(std::move(receiver));\n    producers_.at(i)->startProducing(\n        std::move(sender),\n        i == 1 ? std::make_optional(\n                     folly::make_exception_wrapper<std::runtime_error>(\"Error\"))\n               : std::nullopt /* closeEx */);\n  }\n\n  folly::CPUThreadPoolExecutor mergeExecutor(1);\n  consumer_->startConsuming(merge(\n      std::move(receivers), folly::SerialExecutor::create(&mergeExecutor)));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout / 2);\n  producers_.at(0)->stopProducing();\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout / 2);\n  producers_.at(1)->stopProducing();\n\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::Exception);\n}\n\nTEST_F(MergeFixtureStress, Cancelled) {\n  auto receivers = std::vector<Receiver<ProducedValue>>();\n  for (size_t i = 0; i < producers_.size(); i++) {\n    auto [receiver, sender] = Channel<ProducedValue>::create();\n    receivers.push_back(std::move(receiver));\n    producers_.at(i)->startProducing(\n        std::move(sender), std::nullopt /* closeEx */);\n  }\n\n  folly::CPUThreadPoolExecutor mergeExecutor(1);\n  consumer_->startConsuming(merge(\n      std::move(receivers), folly::SerialExecutor::create(&mergeExecutor)));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  consumer_->cancel();\n\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::Cancelled);\n}\n\nTEST_F(MergeFixtureStress, Close_NoException_ThenCancelledImmediately) {\n  auto receivers = std::vector<Receiver<ProducedValue>>();\n  for (size_t i = 0; i < producers_.size(); i++) {\n    auto [receiver, sender] = Channel<ProducedValue>::create();\n    receivers.push_back(std::move(receiver));\n    producers_.at(i)->startProducing(\n        std::move(sender), std::nullopt /* closeEx */);\n  }\n\n  folly::CPUThreadPoolExecutor mergeExecutor(1);\n  consumer_->startConsuming(merge(\n      std::move(receivers), folly::SerialExecutor::create(&mergeExecutor)));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  for (auto& producer : producers_) {\n    producer->stopProducing();\n  }\n  consumer_->cancel();\n\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n\nTEST_F(MergeFixtureStress, Cancelled_ThenClosedImmediately_NoException) {\n  auto receivers = std::vector<Receiver<ProducedValue>>();\n  for (size_t i = 0; i < producers_.size(); i++) {\n    auto [receiver, sender] = Channel<ProducedValue>::create();\n    receivers.push_back(std::move(receiver));\n    producers_.at(i)->startProducing(\n        std::move(sender), std::nullopt /* closeEx */);\n  }\n\n  folly::CPUThreadPoolExecutor mergeExecutor(1);\n  consumer_->startConsuming(merge(\n      std::move(receivers), folly::SerialExecutor::create(&mergeExecutor)));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  consumer_->cancel();\n  for (auto& producer : producers_) {\n    producer->stopProducing();\n  }\n\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/MultiplexChannelTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/MaxConcurrentRateLimiter.h>\n#include <folly/channels/MultiplexChannel.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\nusing namespace std::string_literals;\n\nclass MultiplexChannelFixture : public Test {\n protected:\n  MultiplexChannelFixture() {}\n\n  ~MultiplexChannelFixture() { executor_.drain(); }\n\n  template <typename T>\n  using Callback = StrictMock<MockNextCallback<T>>;\n\n  template <typename T>\n  std::pair<ChannelCallbackHandle, Callback<T>*> processValues(\n      Receiver<T> receiver) {\n    auto callback = std::make_unique<Callback<T>>();\n    auto callbackPtr = callback.get();\n    auto handle = consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [cbk = std::move(callback)](\n            Try<T> resultTry) mutable -> folly::coro::Task<bool> {\n          (*cbk)(std::move(resultTry));\n          co_return true;\n        });\n    return std::make_pair(std::move(handle), callbackPtr);\n  }\n\n  folly::ManualExecutor executor_;\n};\n\nstatic constexpr int kCloseSubscription = -1;\n\nstruct TestInputValue {\n  folly::F14FastMap<std::string, int> values;\n};\n\nstruct TestContext {\n  std::string contextValue;\n};\n\nstruct TestSubscriptionArg {\n  int initialValue;\n  bool firstSubscriptionForKey;\n  bool throwException{false};\n  folly::SemiFuture<Unit> waitForSubscription{folly::makeSemiFuture()};\n};\n\nstruct TestMultiplexer {\n public:\n  TestMultiplexer(\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n      std::shared_ptr<RateLimiter> rateLimiter = nullptr)\n      : executor_(std::move(executor)), rateLimiter_(std::move(rateLimiter)) {}\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() {\n    return executor_;\n  }\n\n  folly::coro::Task<std::vector<int>> onNewSubscription(\n      std::string key,\n      TestContext& subscriptionContext,\n      TestSubscriptionArg subscriptionArg) {\n    co_await std::move(subscriptionArg.waitForSubscription);\n    if (subscriptionArg.throwException) {\n      throw std::runtime_error(\"Error\");\n    }\n    if (subscriptionArg.firstSubscriptionForKey) {\n      subscriptionContext.contextValue = key;\n    }\n    EXPECT_EQ(subscriptionContext.contextValue, key);\n    co_return toVector(subscriptionArg.initialValue);\n  }\n\n  folly::coro::Task<void> onInputValue(\n      Try<TestInputValue> inputValue,\n      MultiplexedSubscriptions<TestMultiplexer>& subscriptions) {\n    for (auto& [key, value] : inputValue.value().values) {\n      if (!subscriptions.hasSubscription(key)) {\n        continue;\n      }\n      if (value == kCloseSubscription) {\n        subscriptions.close(key, {} /* ex */);\n      } else {\n        subscriptions.write(key, value);\n      }\n    }\n    co_return;\n  }\n\n  std::shared_ptr<RateLimiter> getRateLimiter() { return rateLimiter_; }\n\n private:\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n  std::shared_ptr<RateLimiter> rateLimiter_;\n};\n\nTEST_F(MultiplexChannelFixture, ReceiveValues_MultiplexesValues) {\n  auto [inputReceiver, inputSender] = Channel<TestInputValue>::create();\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexer(&executor_), std::move(inputReceiver));\n\n  EXPECT_FALSE(multiplexChannel.anySubscribers());\n\n  auto [handle1a, callback1a] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          100 /* initialValue */, true /* firstSubscriptionForKey */}));\n  auto [handle1b, callback1b] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          101 /* initialValue */, false /* firstSubscriptionForKey */}));\n  auto [handle2, callback2] = processValues(multiplexChannel.subscribe(\n      \"two\"s,\n      TestSubscriptionArg{\n          200 /* initialValue */, true /* firstSubscriptionForKey */}));\n\n  EXPECT_TRUE(multiplexChannel.anySubscribers());\n  EXPECT_CALL(*callback1a, onValue(100));\n  EXPECT_CALL(*callback1b, onValue(101));\n  EXPECT_CALL(*callback2, onValue(200));\n  executor_.drain();\n\n  EXPECT_CALL(*callback1a, onValue(110));\n  EXPECT_CALL(*callback1b, onValue(110));\n  inputSender.write(TestInputValue{toMap(std::make_pair(\"one\"s, 110))});\n  executor_.drain();\n\n  EXPECT_CALL(*callback2, onValue(210));\n  inputSender.write(TestInputValue{toMap(std::make_pair(\"two\"s, 210))});\n  executor_.drain();\n\n  EXPECT_CALL(*callback1a, onValue(120));\n  EXPECT_CALL(*callback1b, onValue(120));\n  EXPECT_CALL(*callback2, onValue(220));\n  inputSender.write(\n      TestInputValue{toMap(\n          std::make_pair(\"one\"s, 120),\n          std::make_pair(\"two\"s, 220),\n          std::make_pair(\"three\", 330))});\n  executor_.drain();\n\n  std::move(inputSender).close();\n  EXPECT_CALL(*callback1a, onClosed());\n  EXPECT_CALL(*callback1b, onClosed());\n  EXPECT_CALL(*callback2, onClosed());\n  executor_.drain();\n\n  EXPECT_FALSE(multiplexChannel.anySubscribers());\n}\n\nTEST_F(MultiplexChannelFixture, InputThrows_AllOutputReceiversGetException) {\n  auto [inputReceiver, inputSender] = Channel<TestInputValue>::create();\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexer(&executor_), std::move(inputReceiver));\n\n  auto [handle1a, callback1a] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          100 /* initialValue */, true /* firstSubscriptionForKey */}));\n  auto [handle1b, callback1b] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          101 /* initialValue */, false /* firstSubscriptionForKey */}));\n  auto [handle2, callback2] = processValues(multiplexChannel.subscribe(\n      \"two\"s,\n      TestSubscriptionArg{\n          200 /* initialValue */, true /* firstSubscriptionForKey */}));\n\n  EXPECT_CALL(*callback1a, onValue(100));\n  EXPECT_CALL(*callback1b, onValue(101));\n  EXPECT_CALL(*callback2, onValue(200));\n  executor_.drain();\n\n  std::move(inputSender)\n      .close(folly::make_exception_wrapper<std::runtime_error>(\"Error\"));\n  EXPECT_CALL(*callback1a, onRuntimeError(\"std::runtime_error: Error\"));\n  EXPECT_CALL(*callback1b, onRuntimeError(\"std::runtime_error: Error\"));\n  EXPECT_CALL(*callback2, onRuntimeError(\"std::runtime_error: Error\"));\n  executor_.drain();\n}\n\nTEST_F(MultiplexChannelFixture, ClearUnusedSubscriptions) {\n  auto [inputReceiver, inputSender] = Channel<TestInputValue>::create();\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexer(&executor_), std::move(inputReceiver));\n\n  EXPECT_FALSE(multiplexChannel.anySubscribers());\n\n  auto [handle1a, callback1a] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          100 /* initialValue */, true /* firstSubscriptionForKey */}));\n  auto [handle1b, callback1b] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          101 /* initialValue */, false /* firstSubscriptionForKey */}));\n  auto [handle2, callback2] = processValues(multiplexChannel.subscribe(\n      \"two\"s,\n      TestSubscriptionArg{\n          200 /* initialValue */, true /* firstSubscriptionForKey */}));\n\n  EXPECT_TRUE(multiplexChannel.anySubscribers());\n\n  EXPECT_CALL(*callback1a, onValue(100));\n  EXPECT_CALL(*callback1b, onValue(101));\n  EXPECT_CALL(*callback2, onValue(200));\n  executor_.drain();\n\n  auto clearedSubscriptions1Task =\n      co_withExecutor(&executor_, multiplexChannel.clearUnusedSubscriptions())\n          .start();\n  executor_.drain();\n  auto clearedSubscriptions1 =\n      folly::coro::blockingWait(std::move(clearedSubscriptions1Task));\n\n  EXPECT_TRUE(clearedSubscriptions1.empty());\n\n  EXPECT_CALL(*callback1a, onCancelled());\n  EXPECT_CALL(*callback2, onCancelled());\n  handle1a.reset();\n  handle2.reset();\n  executor_.drain();\n\n  auto clearedSubscriptions2Task =\n      co_withExecutor(&executor_, multiplexChannel.clearUnusedSubscriptions())\n          .start();\n  executor_.drain();\n  auto clearedSubscriptions2 =\n      folly::coro::blockingWait(std::move(clearedSubscriptions2Task));\n\n  EXPECT_EQ(clearedSubscriptions2.size(), 1);\n  EXPECT_EQ(clearedSubscriptions2[0].first, \"two\"s);\n  EXPECT_EQ(clearedSubscriptions2[0].second.contextValue, \"two\"s);\n\n  EXPECT_CALL(*callback1b, onCancelled());\n  handle1b.reset();\n  executor_.drain();\n\n  EXPECT_TRUE(multiplexChannel.anySubscribers());\n\n  auto clearedSubscriptions3Task =\n      co_withExecutor(&executor_, multiplexChannel.clearUnusedSubscriptions())\n          .start();\n  executor_.drain();\n  auto clearedSubscriptions3 =\n      folly::coro::blockingWait(std::move(clearedSubscriptions3Task));\n\n  EXPECT_EQ(clearedSubscriptions3.size(), 1);\n  EXPECT_EQ(clearedSubscriptions3[0].first, \"one\"s);\n  EXPECT_EQ(clearedSubscriptions3[0].second.contextValue, \"one\"s);\n  EXPECT_FALSE(multiplexChannel.anySubscribers());\n}\n\nTEST_F(MultiplexChannelFixture, OnNewSubscriptionThrows_OutputReceiverClosed) {\n  auto [inputReceiver, inputSender] = Channel<TestInputValue>::create();\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexer(&executor_), std::move(inputReceiver));\n\n  auto [handle1a, callback1a] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          100 /* initialValue */,\n          true /* firstSubscriptionForKey */,\n          false /* throwException */}));\n\n  EXPECT_CALL(*callback1a, onValue(100));\n  executor_.drain();\n\n  auto [handle1b, callback1b] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          101 /* initialValue */,\n          false /* firstSubscriptionForKey */,\n          true /* throwException */}));\n\n  EXPECT_CALL(*callback1b, onRuntimeError(\"std::runtime_error: Error\"));\n  executor_.drain();\n\n  inputSender.write(TestInputValue{toMap(std::make_pair(\"one\"s, 110))});\n\n  EXPECT_CALL(*callback1a, onValue(110));\n  executor_.drain();\n\n  std::move(inputSender).close();\n  EXPECT_CALL(*callback1a, onClosed());\n  executor_.drain();\n}\n\nTEST_F(MultiplexChannelFixture, HandleDestroyed) {\n  auto [inputReceiver, inputSender] = Channel<TestInputValue>::create();\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexer(&executor_), std::move(inputReceiver));\n\n  EXPECT_FALSE(multiplexChannel.anySubscribers());\n\n  auto [handle1a, callback1a] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          100 /* initialValue */,\n          true /* firstSubscriptionForKey */,\n          false /* throwException */}));\n\n  EXPECT_CALL(*callback1a, onValue(100));\n  executor_.drain();\n\n  {\n    auto toDestroy = std::move(multiplexChannel);\n  }\n  EXPECT_CALL(*callback1a, onClosed());\n  executor_.drain();\n}\n\nTEST_F(MultiplexChannelFixture, Subscribe_WithRateLimiter) {\n  auto rateLimiter = MaxConcurrentRateLimiter::create(1 /* maxConcurrent */);\n  auto [inputReceiver, inputSender] = Channel<TestInputValue>::create();\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexer(&executor_, std::move(rateLimiter)),\n      std::move(inputReceiver));\n\n  EXPECT_FALSE(multiplexChannel.anySubscribers());\n\n  auto promise1a = folly::Promise<Unit>();\n  auto [handle1a, callback1a] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          100 /* initialValue */,\n          true /* firstSubscriptionForKey */,\n          false /* throwException */,\n          promise1a.getSemiFuture() /* waitForSubscription */}));\n\n  executor_.drain();\n\n  auto promise1b = folly::Promise<Unit>();\n  auto [handle1b, callback1b] = processValues(multiplexChannel.subscribe(\n      \"one\"s,\n      TestSubscriptionArg{\n          101 /* initialValue */,\n          true /* firstSubscriptionForKey */,\n          false /* throwException */,\n          promise1b.getSemiFuture() /* waitForSubscription */}));\n\n  executor_.drain();\n\n  EXPECT_CALL(*callback1a, onValue(100));\n  promise1a.setValue();\n  executor_.drain();\n\n  EXPECT_CALL(*callback1b, onValue(101));\n  promise1b.setValue();\n  executor_.drain();\n\n  std::move(inputSender).close();\n  EXPECT_CALL(*callback1a, onClosed());\n  EXPECT_CALL(*callback1b, onClosed());\n  executor_.drain();\n}\n\nclass MultiplexChannelFixtureStress : public Test {\n protected:\n  MultiplexChannelFixtureStress()\n      : producer_(makeProducer()),\n        consumers_(\n            toVector(makeConsumer(0), makeConsumer(1), makeConsumer(2))) {}\n\n  static std::unique_ptr<StressTestProducer<int>> makeProducer() {\n    return std::make_unique<StressTestProducer<int>>([value = 0]() mutable {\n      return value++;\n    });\n  }\n\n  static std::unique_ptr<StressTestConsumer<int>> makeConsumer(int remainder) {\n    return std::make_unique<StressTestConsumer<int>>(\n        ConsumptionMode::CallbackWithHandle,\n        [remainder, lastReceived = -1](int value) mutable {\n          if (lastReceived == -1) {\n            lastReceived = value;\n            EXPECT_EQ(lastReceived % 3, remainder);\n          } else {\n            lastReceived += 3;\n            EXPECT_EQ(value, lastReceived);\n          }\n        });\n  }\n\n  static void sleepFor(std::chrono::milliseconds duration) {\n    /* sleep override */\n    std::this_thread::sleep_for(duration);\n  }\n\n  static constexpr std::chrono::milliseconds kTestTimeout =\n      std::chrono::milliseconds{10};\n\n  std::unique_ptr<StressTestProducer<int>> producer_;\n  std::vector<std::unique_ptr<StressTestConsumer<int>>> consumers_;\n};\n\nstruct NoContext {};\nstruct NoSubscriptionArg {};\n\nstruct TestMultiplexerStress {\n public:\n  explicit TestMultiplexerStress(\n      folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n      : executor_(std::move(executor)) {}\n\n  folly::Executor::KeepAlive<folly::SequencedExecutor> getExecutor() {\n    return executor_;\n  }\n\n  std::shared_ptr<RateLimiter> getRateLimiter() {\n    return nullptr; // No rate limiting\n  }\n\n  folly::coro::Task<std::vector<int>> onNewSubscription(\n      int, NoContext&, NoSubscriptionArg) {\n    co_return std::vector<int>(); // No initial values\n  }\n\n  folly::coro::Task<void> onInputValue(\n      Try<int> inputValue,\n      MultiplexedSubscriptions<TestMultiplexerStress>& subscriptions) {\n    if (subscriptions.hasSubscription(inputValue.value() % 3)) {\n      subscriptions.write(inputValue.value() % 3, inputValue.value());\n    }\n    co_return;\n  }\n\n private:\n  folly::Executor::KeepAlive<folly::SequencedExecutor> executor_;\n};\n\nTEST_F(MultiplexChannelFixtureStress, HandleClosed) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor multiplexChannelExecutor(1);\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexerStress(\n          folly::SerialExecutor::create(&multiplexChannelExecutor)),\n      std::move(receiver));\n\n  consumers_.at(0)->startConsuming(\n      multiplexChannel.subscribe(0 /* key */, NoSubscriptionArg()));\n  consumers_.at(1)->startConsuming(\n      multiplexChannel.subscribe(1 /* key */, NoSubscriptionArg()));\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(2)->startConsuming(\n      multiplexChannel.subscribe(2 /* key */, NoSubscriptionArg()));\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(0)->cancel();\n  EXPECT_EQ(consumers_.at(0)->waitForClose().get(), CloseType::Cancelled);\n\n  sleepFor(kTestTimeout / 3);\n\n  std::move(multiplexChannel).close();\n  EXPECT_EQ(consumers_.at(1)->waitForClose().get(), CloseType::NoException);\n  EXPECT_EQ(consumers_.at(2)->waitForClose().get(), CloseType::NoException);\n}\n\nTEST_F(MultiplexChannelFixtureStress, InputChannelClosed) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor multiplexChannelExecutor(1);\n  auto multiplexChannel = createMultiplexChannel(\n      TestMultiplexerStress(\n          folly::SerialExecutor::create(&multiplexChannelExecutor)),\n      std::move(receiver));\n\n  consumers_.at(0)->startConsuming(\n      multiplexChannel.subscribe(0 /* key */, NoSubscriptionArg()));\n  consumers_.at(1)->startConsuming(\n      multiplexChannel.subscribe(1 /* key */, NoSubscriptionArg()));\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(2)->startConsuming(\n      multiplexChannel.subscribe(2 /* key */, NoSubscriptionArg()));\n\n  sleepFor(kTestTimeout / 3);\n\n  consumers_.at(0)->cancel();\n  EXPECT_EQ(consumers_.at(0)->waitForClose().get(), CloseType::Cancelled);\n\n  sleepFor(kTestTimeout / 3);\n\n  producer_->stopProducing();\n  EXPECT_EQ(consumers_.at(1)->waitForClose().get(), CloseType::NoException);\n  EXPECT_EQ(consumers_.at(2)->waitForClose().get(), CloseType::NoException);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/ProducerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Producer.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\n\nclass ProducerFixture : public Test {\n protected:\n  ProducerFixture() {}\n\n  ~ProducerFixture() { executor_.drain(); }\n\n  ChannelCallbackHandle processValues(Receiver<int> receiver) {\n    return consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [=, this](Try<int> resultTry) -> folly::coro::Task<bool> {\n          onNext_(std::move(resultTry));\n          co_return true;\n        });\n  }\n\n  folly::ManualExecutor executor_;\n  StrictMock<MockNextCallback<int>> onNext_;\n};\n\nTEST_F(ProducerFixture, Write_ThenCloseWithoutException) {\n  class TestProducer : public Producer<int> {\n   public:\n    TestProducer(\n        Sender<int> sender,\n        folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n        : Producer<int>(std::move(sender), std::move(executor)) {\n      write(1);\n      close();\n    }\n  };\n\n  auto receiver = makeProducer<TestProducer>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(receiver));\n\n  executor_.drain();\n}\n\nTEST_F(ProducerFixture, Write_ThenCloseWithException) {\n  class TestProducer : public Producer<int> {\n   public:\n    TestProducer(\n        Sender<int> sender,\n        folly::Executor::KeepAlive<folly::SequencedExecutor> executor)\n        : Producer<int>(std::move(sender), std::move(executor)) {\n      write(1);\n      close(std::runtime_error(\"Error\"));\n    }\n  };\n\n  auto receiver = makeProducer<TestProducer>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(receiver));\n\n  executor_.drain();\n}\n\nTEST_F(ProducerFixture, KeepAliveExists_DelaysDestruction) {\n  class TestProducer : public Producer<int> {\n   public:\n    TestProducer(\n        Sender<int> sender,\n        folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n        folly::SemiFuture<Unit> future,\n        bool& destructed)\n        : Producer<int>(std::move(sender), std::move(executor)),\n          destructed_(destructed) {\n      co_withExecutor(\n          getExecutor(),\n          folly::coro::co_invoke(\n              [keepAlive = getKeepAlive(), future = std::move(future)]() mutable\n                  -> folly::coro::Task<void> { co_await std::move(future); }))\n          .start();\n    }\n\n    ~TestProducer() { destructed_ = true; }\n\n    bool& destructed_;\n  };\n\n  auto promise = folly::Promise<Unit>();\n  bool destructed = false;\n  auto receiver = makeProducer<TestProducer>(\n      &executor_, promise.getSemiFuture(), destructed);\n\n  EXPECT_CALL(onNext_, onCancelled());\n\n  auto callbackHandle = processValues(std::move(receiver));\n  executor_.drain();\n\n  callbackHandle.reset();\n  executor_.drain();\n\n  EXPECT_FALSE(destructed);\n\n  promise.setValue();\n  executor_.drain();\n\n  EXPECT_TRUE(destructed);\n}\n\nTEST_F(\n    ProducerFixture,\n    ConsumerStopsConsumingReceiver_OnCancelledCalled_ThenDestructed) {\n  class TestProducer : public Producer<int> {\n   public:\n    TestProducer(\n        Sender<int> sender,\n        folly::Executor::KeepAlive<folly::SequencedExecutor> executor,\n        folly::Promise<Unit> onCancelledStarted,\n        folly::SemiFuture<Unit> onCancelledCompleted,\n        bool& destructed)\n        : Producer<int>(std::move(sender), std::move(executor)),\n          onCancelledStarted_(std::move(onCancelledStarted)),\n          onCancelledCompleted_(std::move(onCancelledCompleted)),\n          destructed_(destructed) {}\n\n    folly::coro::Task<void> onClosed() override {\n      onCancelledStarted_.setValue();\n      co_await std::move(onCancelledCompleted_);\n    }\n\n    ~TestProducer() override { destructed_ = true; }\n\n    folly::Promise<Unit> onCancelledStarted_;\n    folly::SemiFuture<Unit> onCancelledCompleted_;\n    bool& destructed_;\n  };\n\n  auto onCancelledStartedPromise = folly::Promise<Unit>();\n  auto onCancelledStartedFuture = onCancelledStartedPromise.getSemiFuture();\n  auto onCancelledCompletedPromise = folly::Promise<Unit>();\n  auto onCancelledCompletedFuture = onCancelledCompletedPromise.getSemiFuture();\n  bool destructed = false;\n  auto receiver = makeProducer<TestProducer>(\n      &executor_,\n      std::move(onCancelledStartedPromise),\n      std::move(onCancelledCompletedFuture),\n      destructed);\n\n  EXPECT_CALL(onNext_, onCancelled());\n\n  auto callbackHandle = processValues(std::move(receiver));\n  executor_.drain();\n\n  EXPECT_FALSE(onCancelledStartedFuture.isReady());\n  EXPECT_FALSE(destructed);\n\n  callbackHandle.reset();\n  executor_.drain();\n\n  EXPECT_TRUE(onCancelledStartedFuture.isReady());\n  EXPECT_FALSE(destructed);\n\n  onCancelledCompletedPromise.setValue();\n  executor_.drain();\n\n  EXPECT_TRUE(destructed);\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/ProxyChannelTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ProxyChannel.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\n\nclass ProxyChannelFixture : public Test {\n protected:\n  ~ProxyChannelFixture() { executor_.drain(); }\n\n  ChannelCallbackHandle processValues(Receiver<int> receiver) {\n    return consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [=, this](folly::Try<int> resultTry) -> folly::coro::Task<bool> {\n          onNext_(std::move(resultTry));\n          co_return true;\n        });\n  }\n\n  folly::ManualExecutor executor_;\n  StrictMock<MockNextCallback<int>> onNext_;\n};\n\nTEST_F(ProxyChannelFixture, ReceiveValues) {\n  auto [inputReceiver, inputSender] = Channel<int>::create();\n  auto [outputReceiver, proxyChannel] = createProxyChannel<int>(&executor_);\n\n  inputSender.write(1);\n  inputSender.write(2);\n  executor_.drain();\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onValue(4));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(outputReceiver));\n  proxyChannel.setInputReceiver(std::move(inputReceiver));\n  executor_.drain();\n\n  inputSender.write(3);\n  inputSender.write(4);\n  std::move(inputSender).close();\n  executor_.drain();\n}\n\nTEST_F(ProxyChannelFixture, InputSenderClosedWithException) {\n  auto [inputReceiver, inputSender] = Channel<int>::create();\n  auto [outputReceiver, proxyChannel] = createProxyChannel<int>(&executor_);\n\n  inputSender.write(1);\n  inputSender.write(2);\n  executor_.drain();\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onValue(4));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(outputReceiver));\n  proxyChannel.setInputReceiver(std::move(inputReceiver));\n  executor_.drain();\n\n  inputSender.write(3);\n  inputSender.write(4);\n  std::move(inputSender)\n      .close(folly::make_exception_wrapper<std::runtime_error>(\"Error\"));\n  executor_.drain();\n}\n\nTEST_F(ProxyChannelFixture, ReplaceInputReceiver_BeforeFirstReceiverClosed) {\n  auto [inputReceiver1, inputSender1] = Channel<int>::create();\n  auto [inputReceiver2, inputSender2] = Channel<int>::create();\n  auto [outputReceiver, proxyChannel] = createProxyChannel<int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onValue(4));\n  EXPECT_CALL(onNext_, onValue(5));\n  EXPECT_CALL(onNext_, onValue(6));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(outputReceiver));\n  proxyChannel.setInputReceiver(std::move(inputReceiver1));\n  executor_.drain();\n\n  inputSender1.write(1);\n  inputSender1.write(2);\n  executor_.drain();\n\n  proxyChannel.setInputReceiver(std::move(inputReceiver2));\n  executor_.drain();\n\n  inputSender2.write(3);\n  inputSender2.write(4);\n  executor_.drain();\n\n  inputSender1.write(100);\n  inputSender1.write(200);\n  std::move(inputSender1).close();\n\n  inputSender2.write(5);\n  inputSender2.write(6);\n  executor_.drain();\n\n  std::move(inputSender2).close();\n  executor_.drain();\n}\n\nTEST_F(ProxyChannelFixture, ReplaceInputReceiver_AfterFirstReceiverClosed) {\n  auto [inputReceiver1, inputSender1] = Channel<int>::create();\n  auto [inputReceiver2, inputSender2] = Channel<int>::create();\n  auto [outputReceiver, proxyChannel] = createProxyChannel<int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(outputReceiver));\n  proxyChannel.setInputReceiver(std::move(inputReceiver1));\n  executor_.drain();\n\n  inputSender1.write(1);\n  inputSender1.write(2);\n  std::move(inputSender1).close();\n  executor_.drain();\n\n  proxyChannel.setInputReceiver(std::move(inputReceiver2));\n  executor_.drain();\n\n  inputSender2.write(3);\n  inputSender2.write(4);\n  std::move(inputSender2).close();\n  executor_.drain();\n}\n\nTEST_F(ProxyChannelFixture, ReplaceInputReceiver_AfterFirstReceiverRemoved) {\n  auto [inputReceiver1, inputSender1] = Channel<int>::create();\n  auto [inputReceiver2, inputSender2] = Channel<int>::create();\n  auto [outputReceiver, proxyChannel] = createProxyChannel<int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onValue(3));\n  EXPECT_CALL(onNext_, onValue(4));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(outputReceiver));\n  proxyChannel.setInputReceiver(std::move(inputReceiver1));\n  executor_.drain();\n\n  inputSender1.write(1);\n  inputSender1.write(2);\n  executor_.drain();\n\n  proxyChannel.removeInputReceiver();\n  executor_.drain();\n\n  inputSender1.write(100);\n  inputSender1.write(200);\n  std::move(inputSender1).close();\n\n  proxyChannel.setInputReceiver(std::move(inputReceiver2));\n  executor_.drain();\n\n  inputSender2.write(3);\n  inputSender2.write(4);\n  executor_.drain();\n\n  std::move(inputSender2).close();\n  executor_.drain();\n}\n\nTEST_F(\n    ProxyChannelFixture, ProxyChannelClosed_NoException_NoMoreValuesReceived) {\n  auto [inputReceiver, inputSender] = Channel<int>::create();\n  auto [outputReceiver, proxyChannel] = createProxyChannel<int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(outputReceiver));\n  proxyChannel.setInputReceiver(std::move(inputReceiver));\n  executor_.drain();\n\n  inputSender.write(1);\n  inputSender.write(2);\n  executor_.drain();\n\n  std::move(proxyChannel).close();\n  executor_.drain();\n\n  inputSender.write(3);\n  inputSender.write(4);\n  std::move(inputSender).close();\n  executor_.drain();\n}\n\nTEST_F(\n    ProxyChannelFixture,\n    ProxyChannelClosed_WithException_NoMoreValuesReceived) {\n  auto [inputReceiver, inputSender] = Channel<int>::create();\n  auto [outputReceiver, proxyChannel] = createProxyChannel<int>(&executor_);\n\n  EXPECT_CALL(onNext_, onValue(1));\n  EXPECT_CALL(onNext_, onValue(2));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(outputReceiver));\n  proxyChannel.setInputReceiver(std::move(inputReceiver));\n  executor_.drain();\n\n  inputSender.write(1);\n  inputSender.write(2);\n  executor_.drain();\n\n  std::move(proxyChannel)\n      .close(folly::make_exception_wrapper<std::runtime_error>(\"Error\"));\n  executor_.drain();\n\n  inputSender.write(3);\n  inputSender.write(4);\n  std::move(inputSender).close();\n  executor_.drain();\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/channels/test/TransformTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ScopeGuard.h>\n#include <folly/Synchronized.h>\n#include <folly/channels/ConsumeChannel.h>\n#include <folly/channels/MaxConcurrentRateLimiter.h>\n#include <folly/channels/Transform.h>\n#include <folly/channels/test/ChannelTestUtil.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/DetachOnCancel.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace channels {\n\nusing namespace testing;\nusing namespace std::string_literals;\n\nclass TransformFixture : public Test {\n protected:\n  TransformFixture() {}\n\n  ~TransformFixture() { executor_.drain(); }\n\n  ChannelCallbackHandle processValues(Receiver<std::string> receiver) {\n    return consumeChannelWithCallback(\n        std::move(receiver),\n        &executor_,\n        [=, this](Try<std::string> resultTry) -> folly::coro::Task<bool> {\n          onNext_(std::move(resultTry));\n          co_return true;\n        });\n  }\n\n  folly::ManualExecutor executor_;\n  StrictMock<MockNextCallback<std::string>> onNext_;\n};\n\nclass SimpleTransformFixture : public TransformFixture {};\n\nTEST_F(SimpleTransformFixture, ReceiveValue_ReturnTransformedValue) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (result.value() % 2 == 0) {\n          co_yield folly::to<std::string>(result.value());\n        } else {\n          co_return;\n        }\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onValue(\"4\"));\n  EXPECT_CALL(onNext_, onClosed());\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n\n  std::move(sender).close();\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, ReceiveValue_Close) {\n  bool close = false;\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (close) {\n          throw OnClosedException();\n        }\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  close = true;\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, ReceiveValue_Throw_InCoroutine) {\n  bool throwException = false;\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (throwException) {\n          throw std::runtime_error(\"Error\");\n        }\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  throwException = true;\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, ReceiveValue_Throw_InNonCoroutine) {\n  bool throwException = false;\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (throwException) {\n          throw std::runtime_error(\"Error\");\n        }\n        return folly::coro::co_invoke(\n            [=]() -> folly::coro::AsyncGenerator<std::string&&> {\n              co_yield folly::to<std::string>(result.value());\n            });\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  throwException = true;\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, ReceiveClosed_Close) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (result.hasException()) {\n          EXPECT_TRUE(result.hasException<OnClosedException>());\n        }\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close();\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, ReceiveClosed_Throw) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (result.hasException()) {\n          EXPECT_TRUE(result.hasException<OnClosedException>());\n          throw std::runtime_error(\"Error\");\n        }\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close();\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, ReceiveException_Close) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (result.hasException()) {\n          EXPECT_THROW(result.throwUnlessValue(), std::runtime_error);\n          // We will swallow the exception and move on.\n          throw OnClosedException();\n        }\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, ReceiveException_Throw) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (result.hasException()) {\n          EXPECT_THROW(result.throwUnlessValue(), std::runtime_error);\n        }\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, Cancelled) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = transform(\n      std::move(untransformedReceiver),\n      &executor_,\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  LOG(INFO) << \"Cancelling...\";\n  callbackHandle.reset();\n  executor_.drain();\n  LOG(INFO) << \"Finished cancelling\";\n}\n\nTEST_F(SimpleTransformFixture, Chained) {\n  auto [receiver, sender] = Channel<int>::create();\n  for (int i = 0; i < 10; i++) {\n    receiver = transform(\n        std::move(receiver),\n        &executor_,\n        [](Try<int> result) -> folly::coro::AsyncGenerator<int> {\n          co_yield result.value() + 1;\n        });\n  }\n  auto callbackHandle = processValues(transform(\n      std::move(receiver),\n      &executor_,\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        co_yield folly::to<std::string>(result.value());\n      }));\n  executor_.drain();\n\n  EXPECT_CALL(onNext_, onValue(\"11\"));\n  EXPECT_CALL(onNext_, onValue(\"12\"));\n  EXPECT_CALL(onNext_, onValue(\"13\"));\n  EXPECT_CALL(onNext_, onValue(\"14\"));\n  EXPECT_CALL(onNext_, onClosed());\n\n  sender.write(1);\n  sender.write(2);\n  sender.write(3);\n  sender.write(4);\n  std::move(sender).close();\n  executor_.drain();\n}\n\nTEST_F(SimpleTransformFixture, MultipleTransformsWithRateLimiter) {\n  auto rateLimiter = MaxConcurrentRateLimiter::create(1 /* maxConcurrent */);\n\n  auto [untransformedReceiver1, sender1] = Channel<int>::create();\n  auto [controlReceiver1, controlSender1] = Channel<Unit>::create();\n  int transform1Executions = 0;\n  auto transformedReceiver1 = transform(\n      std::move(untransformedReceiver1),\n      &executor_,\n      [&, &controlReceiver1_2 = controlReceiver1](\n          Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        transform1Executions++;\n        co_await controlReceiver1_2.next();\n        co_yield folly::to<std::string>(result.value());\n      },\n      rateLimiter);\n\n  auto [untransformedReceiver2, sender2] = Channel<int>::create();\n  auto [controlReceiver2, controlSender2] = Channel<Unit>::create();\n  int transform2Executions = 0;\n  auto transformedReceiver2 = transform(\n      std::move(untransformedReceiver2),\n      &executor_,\n      [&, &controlReceiver2_2 = controlReceiver2](\n          Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        transform2Executions++;\n        co_await controlReceiver2_2.next();\n        co_yield folly::to<std::string>(result.value());\n      },\n      rateLimiter);\n\n  auto callbackHandle1 = processValues(std::move(transformedReceiver1));\n  auto callbackHandle2 = processValues(std::move(transformedReceiver2));\n\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"1000\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onValue(\"2000\"));\n  EXPECT_CALL(onNext_, onClosed()).Times(2);\n\n  sender1.write(1);\n  sender2.write(1000);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 1);\n  EXPECT_EQ(transform2Executions, 0);\n  controlSender1.write(unit);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 1);\n  EXPECT_EQ(transform2Executions, 1);\n  controlSender2.write(unit);\n  executor_.drain();\n\n  sender2.write(2000);\n  sender1.write(2);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 1);\n  EXPECT_EQ(transform2Executions, 2);\n  controlSender2.write(unit);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 2);\n  EXPECT_EQ(transform2Executions, 2);\n  controlSender1.write(unit);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  std::move(controlSender1).close();\n  std::move(controlSender2).close();\n  executor_.drain();\n}\n\nclass TransformFixtureStress : public Test {\n protected:\n  TransformFixtureStress()\n      : producer_(\n            std::make_unique<StressTestProducer<int>>([value = 0]() mutable {\n              return value++;\n            })),\n        consumer_(\n            std::make_unique<StressTestConsumer<std::string>>(\n                ConsumptionMode::CallbackWithHandle,\n                [lastReceived = -1](std::string value) mutable {\n                  EXPECT_EQ(folly::to<int>(value), ++lastReceived);\n                })) {}\n\n  static constexpr std::chrono::milliseconds kTestTimeout =\n      std::chrono::milliseconds{5000};\n\n  std::unique_ptr<StressTestProducer<int>> producer_;\n  std::unique_ptr<StressTestConsumer<std::string>> consumer_;\n};\n\nTEST_F(TransformFixtureStress, Close) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor transformExecutor(1);\n  consumer_->startConsuming(transform(\n      std::move(receiver),\n      folly::SerialExecutor::create(&transformExecutor),\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string> {\n        co_yield folly::to<std::string>(std::move(result.value()));\n      }));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  producer_->stopProducing();\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::NoException);\n}\n\nTEST_F(TransformFixtureStress, Cancel) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor transformExecutor(1);\n  consumer_->startConsuming(transform(\n      std::move(receiver),\n      folly::SerialExecutor::create(&transformExecutor),\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string> {\n        co_yield folly::to<std::string>(std::move(result.value()));\n      }));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  consumer_->cancel();\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::Cancelled);\n}\n\nTEST_F(TransformFixtureStress, Close_ThenCancelImmediately) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor transformExecutor(1);\n  consumer_->startConsuming(transform(\n      std::move(receiver),\n      folly::SerialExecutor::create(&transformExecutor),\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string> {\n        co_yield folly::to<std::string>(std::move(result.value()));\n      }));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  producer_->stopProducing();\n  consumer_->cancel();\n\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n\nTEST_F(TransformFixtureStress, Cancel_ThenCloseImmediately) {\n  auto [receiver, sender] = Channel<int>::create();\n  producer_->startProducing(std::move(sender), std::nullopt /* closeEx */);\n\n  folly::CPUThreadPoolExecutor transformExecutor(1);\n  consumer_->startConsuming(transform(\n      std::move(receiver),\n      folly::SerialExecutor::create(&transformExecutor),\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string> {\n        co_yield folly::to<std::string>(std::move(result.value()));\n      }));\n\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout);\n  consumer_->cancel();\n  producer_->stopProducing();\n\n  EXPECT_THAT(\n      consumer_->waitForClose().get(),\n      AnyOf(Eq(CloseType::NoException), Eq(CloseType::Cancelled)));\n}\n\nclass ResumableTransformFixture : public TransformFixture {};\n\nTEST_F(\n    ResumableTransformFixture,\n    InitializesAndReturnsTransformedValues_ThenClosed) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = resumableTransform(\n      &executor_,\n      toVector(\"abc\"s, \"def\"s),\n      [receiver = std::move(untransformedReceiver)](\n          std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"abc\"));\n  EXPECT_CALL(onNext_, onValue(\"def\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onValue(\"3\"));\n  EXPECT_CALL(onNext_, onValue(\"4\"));\n  EXPECT_CALL(onNext_, onClosed());\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n\n  std::move(sender).close();\n  executor_.drain();\n}\n\nTEST_F(\n    ResumableTransformFixture,\n    InitializesAndReturnsTransformedValues_ThenCancelled) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = resumableTransform(\n      &executor_,\n      toVector(\"abc\"s, \"def\"s),\n      [receiver = std::move(untransformedReceiver)](\n          std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"abc\"));\n  EXPECT_CALL(onNext_, onValue(\"def\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onValue(\"3\"));\n  EXPECT_CALL(onNext_, onValue(\"4\"));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n\n  callbackHandle.reset();\n  executor_.drain();\n}\n\nTEST_F(\n    ResumableTransformFixture,\n    InitializesAndReturnsTransformedValues_ThenClosed_CancelledBeforeReinit) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = resumableTransform(\n      &executor_,\n      toVector(\"abc\"s, \"def\"s),\n      [receiver = std::move(untransformedReceiver)](\n          std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"abc\"));\n  EXPECT_CALL(onNext_, onValue(\"def\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close();\n  callbackHandle.reset();\n  executor_.drain();\n}\n\nTEST_F(\n    ResumableTransformFixture,\n    FirstReceiverCloses_ReinitializesWithNewReceiver_ThenClosed) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = resumableTransform(\n      &executor_,\n      toVector(\"abc1\"s),\n      [&receiver = untransformedReceiver](\n          std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [numReinitializations = 0](Try<int> result) mutable\n          -> folly::coro::AsyncGenerator<std::string&&> {\n        try {\n          co_yield folly::to<std::string>(result.value());\n        } catch (const OnClosedException&) {\n          if (numReinitializations >= 1) {\n            throw;\n          }\n          numReinitializations++;\n          throw ReinitializeException(toVector(\"abc2\"s));\n        }\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"abc1\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onValue(\"abc2\"));\n  EXPECT_CALL(onNext_, onValue(\"3\"));\n  EXPECT_CALL(onNext_, onValue(\"4\"));\n  EXPECT_CALL(onNext_, onClosed());\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close();\n  std::tie(untransformedReceiver, sender) = Channel<int>::create();\n  executor_.drain();\n\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n\n  std::move(sender).close();\n  executor_.drain();\n}\n\nTEST_F(\n    ResumableTransformFixture,\n    FirstReceiverClosesWithException_NoReinitialization_Rethrows) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = resumableTransform(\n      &executor_,\n      toVector(\"abc\"s),\n      [alreadyInitialized = false, receiver = std::move(untransformedReceiver)](\n          std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        CHECK(!alreadyInitialized);\n        alreadyInitialized = true;\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"abc\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close(std::runtime_error(\"Error\"));\n  executor_.drain();\n}\n\nTEST_F(\n    ResumableTransformFixture,\n    FirstReceiverClosesWithException_TransformSwallows_Reinitialization) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  auto transformedReceiver = resumableTransform(\n      &executor_,\n      toVector(\"abc1\"s),\n      [&receiver = untransformedReceiver](\n          std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (result.hasValue()) {\n          co_yield folly::to<std::string>(result.value());\n        } else {\n          EXPECT_THROW(result.throwUnlessValue(), std::runtime_error);\n          throw ReinitializeException(toVector(\"abc2\"s));\n        }\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"abc1\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onValue(\"abc2\"));\n  EXPECT_CALL(onNext_, onValue(\"3\"));\n  EXPECT_CALL(onNext_, onValue(\"4\"));\n  EXPECT_CALL(onNext_, onCancelled());\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  std::move(sender).close(std::runtime_error(\"Error\"));\n  std::tie(untransformedReceiver, sender) = Channel<int>::create();\n  executor_.drain();\n\n  sender.write(3);\n  sender.write(4);\n  executor_.drain();\n\n  callbackHandle.reset();\n  executor_.drain();\n}\n\nTEST_F(ResumableTransformFixture, TransformThrows_NoReinitialization_Rethrows) {\n  auto [untransformedReceiver, sender] = Channel<int>::create();\n  bool transformThrows = false;\n  auto transformedReceiver = resumableTransform(\n      &executor_,\n      toVector(\"abc\"s),\n      [alreadyInitialized = false, &receiver = untransformedReceiver](\n          std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        CHECK(!alreadyInitialized);\n        alreadyInitialized = true;\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        if (transformThrows) {\n          throw std::runtime_error(\"Error\");\n        }\n        co_yield folly::to<std::string>(result.value());\n      });\n\n  EXPECT_CALL(onNext_, onValue(\"abc\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onRuntimeError(\"std::runtime_error: Error\"));\n\n  auto callbackHandle = processValues(std::move(transformedReceiver));\n\n  sender.write(1);\n  sender.write(2);\n  executor_.drain();\n\n  transformThrows = true;\n  sender.write(100);\n  executor_.drain();\n}\n\nTEST_F(ResumableTransformFixture, MultipleResumableTransformsWithRateLimiter) {\n  auto rateLimiter = MaxConcurrentRateLimiter::create(1 /* maxConcurrent */);\n\n  auto [untransformedReceiver1, sender1] = Channel<int>::create();\n  auto [controlReceiver1, controlSender1] = Channel<Unit>::create();\n  int transform1Executions = 0;\n  auto transformedReceiver1 = resumableTransform(\n      &executor_,\n      toVector(\"init1\"s),\n      [&,\n       receiver = std::move(untransformedReceiver1),\n       &controlReceiver1_2 =\n           controlReceiver1](std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        transform1Executions++;\n        co_await controlReceiver1_2.next();\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [&, &controlReceiver1_2 = controlReceiver1](\n          Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        transform1Executions++;\n        co_await controlReceiver1_2.next();\n        co_yield folly::to<std::string>(result.value());\n      },\n      rateLimiter);\n\n  auto [untransformedReceiver2, sender2] = Channel<int>::create();\n  auto [controlReceiver2, controlSender2] = Channel<Unit>::create();\n  int transform2Executions = 0;\n  auto transformedReceiver2 = resumableTransform(\n      &executor_,\n      toVector(\"init2\"s),\n      [&,\n       receiver = std::move(untransformedReceiver2),\n       &controlReceiver2_2 =\n           controlReceiver2](std::vector<std::string> initializeArg) mutable\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        transform2Executions++;\n        co_await controlReceiver2_2.next();\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [&, &controlReceiver2_2 = controlReceiver2](\n          Try<int> result) -> folly::coro::AsyncGenerator<std::string&&> {\n        transform2Executions++;\n        co_await controlReceiver2_2.next();\n        co_yield folly::to<std::string>(result.value());\n      },\n      rateLimiter);\n\n  auto callbackHandle1 = processValues(std::move(transformedReceiver1));\n  auto callbackHandle2 = processValues(std::move(transformedReceiver2));\n\n  EXPECT_CALL(onNext_, onValue(\"init1\"));\n  EXPECT_CALL(onNext_, onValue(\"init2\"));\n  EXPECT_CALL(onNext_, onValue(\"1\"));\n  EXPECT_CALL(onNext_, onValue(\"1000\"));\n  EXPECT_CALL(onNext_, onValue(\"2\"));\n  EXPECT_CALL(onNext_, onValue(\"2000\"));\n  EXPECT_CALL(onNext_, onClosed()).Times(2);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 1);\n  EXPECT_EQ(transform2Executions, 0);\n  controlSender1.write(unit);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 1);\n  EXPECT_EQ(transform2Executions, 1);\n  controlSender2.write(unit);\n  executor_.drain();\n\n  sender1.write(1);\n  sender2.write(1000);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 2);\n  EXPECT_EQ(transform2Executions, 1);\n  controlSender1.write(unit);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 2);\n  EXPECT_EQ(transform2Executions, 2);\n  controlSender2.write(unit);\n  executor_.drain();\n\n  sender2.write(2000);\n  sender1.write(2);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 2);\n  EXPECT_EQ(transform2Executions, 3);\n  controlSender2.write(unit);\n  executor_.drain();\n\n  EXPECT_EQ(transform1Executions, 3);\n  EXPECT_EQ(transform2Executions, 3);\n  controlSender1.write(unit);\n  executor_.drain();\n\n  std::move(sender1).close();\n  std::move(sender2).close();\n  std::move(controlSender1).close();\n  std::move(controlSender2).close();\n  executor_.drain();\n}\n\nclass ResumableTransformFixtureStress : public Test {\n protected:\n  ResumableTransformFixtureStress()\n      : consumer_(\n            std::make_unique<StressTestConsumer<std::string>>(\n                ConsumptionMode::CallbackWithHandle,\n                [lastReceived = -1](std::string value) mutable {\n                  if (value == \"start\") {\n                    lastReceived = -1;\n                  } else {\n                    EXPECT_EQ(folly::to<int>(value), ++lastReceived);\n                  }\n                })) {}\n\n  std::unique_ptr<StressTestProducer<int>> makeProducer() {\n    return std::make_unique<StressTestProducer<int>>([value = 0]() mutable {\n      return value++;\n    });\n  }\n\n  void setProducer(std::unique_ptr<StressTestProducer<int>> producer) {\n    (*producer_.wlock()) = std::move(producer);\n    producerReady_.post();\n  }\n\n  void waitForProducer() {\n    producerReady_.wait();\n    LOG(INFO) << \"Finished waiting!\";\n    producerReady_.reset();\n  }\n\n  void stopProducing() { (*producer_.wlock())->stopProducing(); }\n\n  static constexpr std::chrono::milliseconds kTestTimeout =\n      std::chrono::milliseconds{10};\n\n  folly::Synchronized<std::unique_ptr<StressTestProducer<int>>> producer_;\n  folly::Baton<> producerReady_;\n  std::unique_ptr<StressTestConsumer<std::string>> consumer_;\n};\n\nTEST_F(ResumableTransformFixtureStress, Close) {\n  folly::CPUThreadPoolExecutor transformExecutor(1);\n  std::atomic<bool> close = false;\n  consumer_->startConsuming(resumableTransform(\n      folly::SerialExecutor::create(&transformExecutor),\n      toVector(\"start\"s),\n      [&](std::vector<std::string> initializeArg)\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        auto [receiver, sender] = Channel<int>::create();\n        auto newProducer = makeProducer();\n        newProducer->startProducing(\n            std::move(sender), std::nullopt /* closeEx */);\n        setProducer(std::move(newProducer));\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [&](Try<int> result) -> folly::coro::AsyncGenerator<std::string> {\n        try {\n          co_yield folly::to<std::string>(std::move(result.value()));\n        } catch (const OnClosedException&) {\n          if (close) {\n            throw;\n          } else {\n            throw ReinitializeException(toVector(\"start\"s));\n          }\n        }\n      }));\n\n  waitForProducer();\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout / 2);\n  stopProducing();\n\n  waitForProducer();\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout / 2);\n  close = true;\n  stopProducing();\n\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::NoException);\n}\n\nTEST_F(ResumableTransformFixtureStress, CancelDuringReinitialization) {\n  folly::CPUThreadPoolExecutor transformExecutor(1);\n  auto initializationStarted = folly::SharedPromise<Unit>();\n  auto initializationWait = folly::SharedPromise<Unit>();\n  auto resumableTransformDestroyed = folly::SharedPromise<Unit>();\n  auto guard = folly::makeGuard([&]() {\n    resumableTransformDestroyed.setValue();\n  });\n  consumer_->startConsuming(resumableTransform(\n      folly::SerialExecutor::create(&transformExecutor),\n      toVector(\"start\"s),\n      [&, g = std::move(guard)](std::vector<std::string> initializeArg)\n          -> folly::coro::Task<\n              std::pair<std::vector<std::string>, Receiver<int>>> {\n        initializationStarted.setValue(unit);\n        co_await folly::coro::detachOnCancel(\n            initializationWait.getSemiFuture());\n        initializationWait = folly::SharedPromise<Unit>();\n        auto [receiver, sender] = Channel<int>::create();\n        auto newProducer = makeProducer();\n        newProducer->startProducing(\n            std::move(sender), std::nullopt /* closeEx */);\n        setProducer(std::move(newProducer));\n        co_return std::make_pair(std::move(initializeArg), std::move(receiver));\n      },\n      [](Try<int> result) -> folly::coro::AsyncGenerator<std::string> {\n        try {\n          co_yield folly::to<std::string>(std::move(result.value()));\n        } catch (const OnClosedException&) {\n          throw ReinitializeException(toVector(\"start\"s));\n        }\n      }));\n\n  initializationStarted.getSemiFuture().get();\n  initializationStarted = folly::SharedPromise<Unit>();\n  initializationWait.setValue(unit);\n  waitForProducer();\n  /* sleep override */\n  std::this_thread::sleep_for(kTestTimeout / 2);\n  stopProducing();\n\n  initializationStarted.getSemiFuture().get();\n  initializationStarted = folly::SharedPromise<Unit>();\n  consumer_->cancel();\n  initializationWait.setValue(unit);\n\n  EXPECT_EQ(consumer_->waitForClose().get(), CloseType::Cancelled);\n  resumableTransformDestroyed.getSemiFuture().get();\n}\n} // namespace channels\n} // namespace folly\n"
  },
  {
    "path": "folly/chrono/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"clock\",\n    headers = [\"Clock.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"conv\",\n    headers = [\"Conv.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:constexpr_math\",\n        \"//folly:conv\",\n        \"//folly:expected\",\n        \"//folly:utility\",\n        \"//folly/portability:sys_time\",\n        \"//folly/portability:sys_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"hardware\",\n    headers = [\"Hardware.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\n# !!!! fbcode/folly/chrono/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n"
  },
  {
    "path": "folly/chrono/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME clock\n  HEADERS\n    Clock.h\n)\n\nfolly_add_library(\n  NAME conv\n  HEADERS\n    Conv.h\n  EXPORTED_DEPS\n    folly_constexpr_math\n    folly_conv\n    folly_expected\n    folly_portability_sys_time\n    folly_portability_sys_types\n    folly_utility\n)\n\nfolly_add_library(\n  NAME hardware\n  HEADERS\n    Hardware.h\n  EXPORTED_DEPS\n    folly_portability\n)\n"
  },
  {
    "path": "folly/chrono/Clock.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n\n/**\n * Namespace for folly chrono types.\n *\n * Using a separate namespace for clock types to minimize type conflicts in\n * tests and other code which may be using `using namespace folly` while also\n * having aliased chrono types.\n */\nnamespace folly::chrono {\n\n/**\n * Clock interface.\n *\n * Abstraction enables tests to control the current time.\n */\ntemplate <typename ClockType>\nclass Clock {\n public:\n  using TimePoint = typename ClockType::time_point;\n  Clock() = default;\n  virtual ~Clock() = default;\n\n  /**\n   * Returns current time.\n   */\n  [[nodiscard]] virtual TimePoint now() const = 0;\n};\n\n/**\n * Implementation of ClockInterface for given std::chrono ClockType.\n */\ntemplate <typename ClockType>\nclass ClockImpl : public Clock<ClockType> {\n public:\n  using TimePoint = typename ClockType::time_point;\n  ClockImpl() = default;\n  ~ClockImpl() override = default;\n  [[nodiscard]] TimePoint now() const override { return ClockType::now(); }\n};\n\nusing SteadyClock = Clock<std::chrono::steady_clock>;\nusing SteadyClockImpl = ClockImpl<std::chrono::steady_clock>;\nusing SystemClock = Clock<std::chrono::system_clock>;\nusing SystemClockImpl = ClockImpl<std::chrono::system_clock>;\n\n} // namespace folly::chrono\n"
  },
  {
    "path": "folly/chrono/Conv.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Conversions between std::chrono types and POSIX time types.\n *\n * These conversions will fail with a ConversionError if an overflow would\n * occur performing the conversion.  (e.g., if the input value cannot fit in\n * the destination type).  However they allow loss of precision (e.g.,\n * converting nanoseconds to a struct timeval which only has microsecond\n * granularity, or a struct timespec to std::chrono::minutes).\n */\n\n#pragma once\n\n#include <chrono>\n#include <limits>\n#include <type_traits>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Conv.h>\n#include <folly/Expected.h>\n#include <folly/Utility.h>\n#include <folly/portability/SysTime.h>\n#include <folly/portability/SysTypes.h>\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename T>\nstruct is_duration : std::false_type {};\ntemplate <typename Rep, typename Period>\nstruct is_duration<std::chrono::duration<Rep, Period>> : std::true_type {};\ntemplate <typename T>\nstruct is_time_point : std::false_type {};\ntemplate <typename Clock, typename Duration>\nstruct is_time_point<std::chrono::time_point<Clock, Duration>>\n    : std::true_type {};\ntemplate <typename T>\nstruct is_std_chrono_type {\n  static constexpr bool value =\n      is_duration<T>::value || is_time_point<T>::value;\n};\ntemplate <typename T>\nstruct is_posix_time_type {\n  static constexpr bool value = std::is_same<T, struct timespec>::value ||\n      std::is_same<T, struct timeval>::value;\n};\ntemplate <typename Tgt, typename Src>\nstruct is_chrono_conversion {\n  static constexpr bool value =\n      ((is_std_chrono_type<Tgt>::value && is_posix_time_type<Src>::value) ||\n       (is_posix_time_type<Tgt>::value && is_std_chrono_type<Src>::value));\n};\n\n/**\n * This converts a number in some input type to time_t while ensuring that it\n * fits in the range of numbers representable by time_t.\n *\n * This is similar to the normal folly::tryTo() behavior when converting\n * arithmetic types to an integer type, except that it does not complain about\n * floating point conversions losing precision.\n */\ntemplate <typename Src>\nExpected<time_t, ConversionCode> chronoRangeCheck(Src value) {\n  static_assert(\n      std::is_integral_v<time_t> && std::is_signed_v<time_t>,\n      \"This function is only implemented for time_t that are signed integrals. Please update it if you need to support a different time_t type.\");\n  if constexpr (std::is_floating_point_v<Src>) {\n    // time_t max converted to a floating point does not have\n    // an exact representation.\n    // 18446742974197923840 <- Largest float before time_t max\n    // 18446744073709549568 <- Largest double before time_t max\n    // 18446744073709551615 <- time_t max (when time_t is int64_t)\n    // 18446744073709551616 <- next representable float or double.\n    // The floating point value that gets chosen depends on the floating point\n    // implementation. IEEE arithmetic rounds to nearest.\n    static_assert(\n        std::numeric_limits<Src>::round_style == std::round_to_nearest,\n        \"This function is only implemented for IEEE round to nearest. Please update it if you need other round styles.\");\n    if (value >= static_cast<Src>(std::numeric_limits<time_t>::max())) {\n      return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);\n    }\n  } else {\n    constexpr bool isIntegralWithLargerRange = sizeof(Src) > sizeof(time_t) ||\n        (sizeof(Src) == sizeof(time_t) && std::is_unsigned_v<Src>);\n    if constexpr (isIntegralWithLargerRange) {\n      if (value > static_cast<Src>(std::numeric_limits<time_t>::max())) {\n        return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);\n      }\n    }\n  }\n  if (std::is_signed<Src>::value) {\n    // int64_t lowest converted to a floating point has\n    // has an exact representation because it is a power of 2.\n    if (value < std::numeric_limits<time_t>::lowest()) {\n      return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n    }\n  }\n\n  return static_cast<time_t>(value);\n}\n\n/**\n * Convert a std::chrono::duration with second granularity to a pair of\n * (seconds, subseconds)\n *\n * The SubsecondRatio template parameter specifies what type of subseconds to\n * return.  This must have a numerator of 1.\n */\ntemplate <typename SubsecondRatio, typename Rep>\nstatic Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(\n    const std::chrono::duration<Rep, std::ratio<1, 1>>& duration) {\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  auto sec = chronoRangeCheck(duration.count());\n  if (sec.hasError()) {\n    return makeUnexpected(sec.error());\n  }\n\n  time_t secValue = sec.value();\n  long subsec = 0L;\n  if (std::is_floating_point<Rep>::value) {\n    auto fraction = (duration.count() - secValue);\n    subsec = static_cast<long>(fraction * SubsecondRatio::den);\n    if (duration.count() < 0 && fraction < 0) {\n      if (secValue == std::numeric_limits<time_t>::lowest()) {\n        return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n      }\n      secValue -= 1;\n      subsec += SubsecondRatio::den;\n    }\n  }\n  return std::pair<time_t, long>{secValue, subsec};\n}\n\n/**\n * Convert a std::chrono::duration with subsecond granularity to a pair of\n * (seconds, subseconds)\n */\ntemplate <typename SubsecondRatio, typename Rep, std::intmax_t Denominator>\nstatic Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(\n    const std::chrono::duration<Rep, std::ratio<1, Denominator>>& duration) {\n  static_assert(Denominator != 1, \"special case expecting den != 1\");\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  auto sec = chronoRangeCheck(duration.count() / Denominator);\n  if (sec.hasError()) {\n    return makeUnexpected(sec.error());\n  }\n  auto secTimeT = static_cast<time_t>(sec.value());\n\n  auto remainder = duration.count() - (secTimeT * Denominator);\n  long subsec =\n      static_cast<long>((remainder * SubsecondRatio::den) / Denominator);\n  if (FOLLY_UNLIKELY(duration.count() < 0) && remainder != 0) {\n    if (secTimeT == std::numeric_limits<time_t>::lowest()) {\n      return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n    }\n    secTimeT -= 1;\n    subsec += SubsecondRatio::den;\n  }\n\n  return std::pair<time_t, long>{secTimeT, subsec};\n}\n\n/**\n * Convert a std::chrono::duration with coarser-than-second granularity to a\n * pair of (seconds, subseconds)\n */\ntemplate <typename SubsecondRatio, typename Rep, std::intmax_t Numerator>\nstatic Expected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(\n    const std::chrono::duration<Rep, std::ratio<Numerator, 1>>& duration) {\n  static_assert(Numerator != 1, \"special case expecting num!=1\");\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  constexpr auto maxValue = std::numeric_limits<time_t>::max() / Numerator;\n  constexpr auto minValue = std::numeric_limits<time_t>::lowest() / Numerator;\n  if (duration.count() > maxValue) {\n    return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);\n  }\n  if (duration.count() < minValue) {\n    return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n  }\n\n  // Note that we can't use chronoRangeCheck() here since we have to check\n  // if (duration.count() * Numerator) would overflow (which we do above).\n  auto secOriginalRep = (duration.count() * Numerator);\n  auto sec = static_cast<time_t>(secOriginalRep);\n\n  long subsec = 0L;\n  if (std::is_floating_point<Rep>::value) {\n    auto fraction = secOriginalRep - sec;\n    subsec = static_cast<long>(fraction * SubsecondRatio::den);\n    if (duration.count() < 0 && fraction < 0) {\n      if (sec == std::numeric_limits<time_t>::lowest()) {\n        return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n      }\n      sec -= 1;\n      subsec += SubsecondRatio::den;\n    }\n  }\n  return std::pair<time_t, long>{sec, subsec};\n}\n\n/*\n * Helper classes for picking an intermediate duration type to use\n * when doing conversions to/from durations where neither the numerator nor\n * denominator are 1.\n */\ntemplate <typename T, bool IsFloatingPoint, bool IsSigned>\nstruct IntermediateTimeRep {};\ntemplate <typename T, bool IsSigned>\nstruct IntermediateTimeRep<T, true, IsSigned> {\n  using type = T;\n};\ntemplate <typename T>\nstruct IntermediateTimeRep<T, false, true> {\n  using type = intmax_t;\n};\ntemplate <typename T>\nstruct IntermediateTimeRep<T, false, false> {\n  using type = uintmax_t;\n};\n// For IntermediateDuration we always use 1 as the numerator, and the original\n// Period denominator.  This ensures that we do not lose precision when\n// performing the conversion.\ntemplate <typename Rep, typename Period>\nusing IntermediateDuration = std::chrono::duration<\n    typename IntermediateTimeRep<\n        Rep,\n        std::is_floating_point<Rep>::value,\n        std::is_signed<Rep>::value>::type,\n    std::ratio<1, Period::den>>;\n\n/**\n * Convert a std::chrono::duration to a pair of (seconds, subseconds)\n *\n * This overload is only used for unusual durations where neither the numerator\n * nor denominator are 1.\n */\ntemplate <typename SubsecondRatio, typename Rep, typename Period>\nExpected<std::pair<time_t, long>, ConversionCode> durationToPosixTime(\n    const std::chrono::duration<Rep, Period>& duration) {\n  static_assert(Period::num != 1, \"should use special-case code when num==1\");\n  static_assert(Period::den != 1, \"should use special-case code when den==1\");\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  // Perform this conversion by first converting to a duration where the\n  // numerator is 1, then convert to the output type.\n  using IntermediateType = IntermediateDuration<Rep, Period>;\n  using IntermediateRep = typename IntermediateType::rep;\n\n  // Check to see if we would have overflow converting to the intermediate\n  // type.\n  constexpr auto maxInput =\n      std::numeric_limits<IntermediateRep>::max() / Period::num;\n  if (duration.count() > maxInput) {\n    return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);\n  }\n  constexpr auto minInput =\n      std::numeric_limits<IntermediateRep>::min() / Period::num;\n  if (duration.count() < minInput) {\n    return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n  }\n  auto intermediate = IntermediateType{\n      static_cast<IntermediateRep>(duration.count()) *\n      static_cast<IntermediateRep>(Period::num)};\n\n  return durationToPosixTime<SubsecondRatio>(intermediate);\n}\n\n/**\n * Check for overflow when converting to a duration type that is second\n * granularity or finer (e.g., nanoseconds, milliseconds, seconds)\n *\n * This assumes the input is normalized, with subseconds >= 0 and subseconds\n * less than 1 second.\n */\ntemplate <bool IsFloatingPoint>\nstruct CheckOverflowToDuration {\n  template <\n      typename Tgt,\n      typename SubsecondRatio,\n      typename Seconds,\n      typename Subseconds>\n  static ConversionCode check(Seconds seconds, Subseconds subseconds) {\n    static_assert(\n        Tgt::period::num == 1,\n        \"this implementation should only be used for subsecond granularity \"\n        \"duration types\");\n    static_assert(\n        !std::is_floating_point<typename Tgt::rep>::value, \"incorrect usage\");\n    static_assert(\n        SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n    if (FOLLY_LIKELY(seconds >= 0)) {\n      constexpr auto maxCount = std::numeric_limits<typename Tgt::rep>::max();\n      constexpr auto maxSeconds = maxCount / Tgt::period::den;\n\n      auto unsignedSeconds = to_unsigned(seconds);\n      if (FOLLY_LIKELY(unsignedSeconds < maxSeconds)) {\n        return ConversionCode::SUCCESS;\n      }\n\n      if (FOLLY_UNLIKELY(unsignedSeconds == maxSeconds)) {\n        constexpr auto maxRemainder =\n            maxCount - (maxSeconds * Tgt::period::den);\n        constexpr auto maxSubseconds =\n            (maxRemainder * SubsecondRatio::den) / Tgt::period::den;\n        if (subseconds <= 0) {\n          return ConversionCode::SUCCESS;\n        }\n        if (to_unsigned(subseconds) <= maxSubseconds) {\n          return ConversionCode::SUCCESS;\n        }\n      }\n      return ConversionCode::POSITIVE_OVERFLOW;\n    } else if (std::is_unsigned<typename Tgt::rep>::value) {\n      return ConversionCode::NEGATIVE_OVERFLOW;\n    } else {\n      constexpr auto minCount =\n          to_signed(std::numeric_limits<typename Tgt::rep>::lowest());\n      constexpr auto minSeconds = (minCount / Tgt::period::den);\n      if (FOLLY_LIKELY(seconds >= minSeconds)) {\n        return ConversionCode::SUCCESS;\n      }\n\n      if (FOLLY_UNLIKELY(seconds == minSeconds - 1)) {\n        constexpr auto maxRemainder =\n            minCount - (minSeconds * Tgt::period::den) + Tgt::period::den;\n        constexpr auto maxSubseconds =\n            (maxRemainder * SubsecondRatio::den) / Tgt::period::den;\n        if (subseconds <= 0) {\n          return ConversionCode::NEGATIVE_OVERFLOW;\n        }\n        if (subseconds >= maxSubseconds) {\n          return ConversionCode::SUCCESS;\n        }\n      }\n      return ConversionCode::NEGATIVE_OVERFLOW;\n    }\n  }\n};\n\ntemplate <>\nstruct CheckOverflowToDuration<true> {\n  template <\n      typename Tgt,\n      typename SubsecondRatio,\n      typename Seconds,\n      typename Subseconds>\n  static ConversionCode check(\n      Seconds /* seconds */, Subseconds /* subseconds */) {\n    static_assert(\n        std::is_floating_point<typename Tgt::rep>::value, \"incorrect usage\");\n    static_assert(\n        SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n    // We expect floating point types to have much a wider representable range\n    // than integer types, so we don't bother actually checking the input\n    // integer value here.\n    static_assert(\n        std::numeric_limits<typename Tgt::rep>::max() >=\n            std::numeric_limits<Seconds>::max(),\n        \"unusually limited floating point type\");\n    static_assert(\n        std::numeric_limits<typename Tgt::rep>::lowest() <=\n            std::numeric_limits<Seconds>::lowest(),\n        \"unusually limited floating point type\");\n\n    return ConversionCode::SUCCESS;\n  }\n};\n\n/**\n * Convert a timeval or a timespec to a std::chrono::duration with second\n * granularity.\n *\n * The SubsecondRatio template parameter specifies what type of subseconds to\n * return.  This must have a numerator of 1.\n *\n * The input must be in normalized form: the subseconds field must be greater\n * than or equal to 0, and less than SubsecondRatio::den (i.e., less than 1\n * second).\n */\ntemplate <\n    typename SubsecondRatio,\n    typename Seconds,\n    typename Subseconds,\n    typename Rep>\nauto posixTimeToDuration(\n    Seconds seconds,\n    Subseconds subseconds,\n    std::chrono::duration<Rep, std::ratio<1, 1>> dummy)\n    -> Expected<decltype(dummy), ConversionCode> {\n  using Tgt = decltype(dummy);\n  static_assert(Tgt::period::num == 1, \"special case expecting num==1\");\n  static_assert(Tgt::period::den == 1, \"special case expecting den==1\");\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  auto outputSeconds = tryTo<typename Tgt::rep>(seconds);\n  if (outputSeconds.hasError()) {\n    return makeUnexpected(outputSeconds.error());\n  }\n\n  if (std::is_floating_point<typename Tgt::rep>::value) {\n    return Tgt{\n        typename Tgt::rep(seconds) +\n        (typename Tgt::rep(subseconds) / SubsecondRatio::den)};\n  }\n\n  // If the value is negative, we have to round up a non-zero subseconds value\n  if (FOLLY_UNLIKELY(outputSeconds.value() < 0) && subseconds > 0) {\n    if (FOLLY_UNLIKELY(\n            outputSeconds.value() ==\n            std::numeric_limits<typename Tgt::rep>::lowest())) {\n      return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n    }\n    return Tgt{outputSeconds.value() + 1};\n  }\n\n  return Tgt{outputSeconds.value()};\n}\n\n/**\n * Convert a timeval or a timespec to a std::chrono::duration with subsecond\n * granularity\n */\ntemplate <\n    typename SubsecondRatio,\n    typename Seconds,\n    typename Subseconds,\n    typename Rep,\n    std::intmax_t Denominator>\nauto posixTimeToDuration(\n    Seconds seconds,\n    Subseconds subseconds,\n    std::chrono::duration<Rep, std::ratio<1, Denominator>> dummy)\n    -> Expected<decltype(dummy), ConversionCode> {\n  using Tgt = decltype(dummy);\n  static_assert(Tgt::period::num == 1, \"special case expecting num==1\");\n  static_assert(Tgt::period::den != 1, \"special case expecting den!=1\");\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  auto errorCode = detail::CheckOverflowToDuration<\n      std::is_floating_point<typename Tgt::rep>::value>::\n      template check<Tgt, SubsecondRatio>(seconds, subseconds);\n  if (errorCode != ConversionCode::SUCCESS) {\n    return makeUnexpected(errorCode);\n  }\n\n  if (FOLLY_LIKELY(seconds >= 0)) {\n    return std::chrono::duration_cast<Tgt>(\n               std::chrono::duration<typename Tgt::rep>{seconds}) +\n        std::chrono::duration_cast<Tgt>(\n               std::chrono::duration<typename Tgt::rep, SubsecondRatio>{\n                   subseconds});\n  } else {\n    // For negative numbers we have to round subseconds up towards zero, even\n    // though it is a positive value, since the overall value is negative.\n    return std::chrono::duration_cast<Tgt>(\n               std::chrono::duration<typename Tgt::rep>{seconds + 1}) -\n        std::chrono::duration_cast<Tgt>(\n               std::chrono::duration<typename Tgt::rep, SubsecondRatio>{\n                   SubsecondRatio::den - subseconds});\n  }\n}\n\n/**\n * Convert a timeval or a timespec to a std::chrono::duration with\n * granularity coarser than 1 second.\n */\ntemplate <\n    typename SubsecondRatio,\n    typename Seconds,\n    typename Subseconds,\n    typename Rep,\n    std::intmax_t Numerator>\nauto posixTimeToDuration(\n    Seconds seconds,\n    Subseconds subseconds,\n    std::chrono::duration<Rep, std::ratio<Numerator, 1>> dummy)\n    -> Expected<decltype(dummy), ConversionCode> {\n  using Tgt = decltype(dummy);\n  static_assert(Tgt::period::num != 1, \"special case expecting num!=1\");\n  static_assert(Tgt::period::den == 1, \"special case expecting den==1\");\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  if (FOLLY_UNLIKELY(seconds < 0) && subseconds > 0) {\n    // Increment seconds by one to handle truncation of negative numbers\n    // properly.\n    if (FOLLY_UNLIKELY(seconds == std::numeric_limits<Seconds>::lowest())) {\n      return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n    }\n    seconds += 1;\n  }\n\n  if (std::is_floating_point<typename Tgt::rep>::value) {\n    // Convert to the floating point type before performing the division\n    return Tgt{static_cast<typename Tgt::rep>(seconds) / Tgt::period::num};\n  } else {\n    // Perform the division as an integer, and check that the result fits in\n    // the output integer type\n    auto outputValue = (seconds / Tgt::period::num);\n    auto expectedOutput = tryTo<typename Tgt::rep>(outputValue);\n    if (expectedOutput.hasError()) {\n      return makeUnexpected(expectedOutput.error());\n    }\n\n    return Tgt{expectedOutput.value()};\n  }\n}\n\n/**\n * Convert a timeval or timespec to a std::chrono::duration\n *\n * This overload is only used for unusual durations where neither the numerator\n * nor denominator are 1.\n */\ntemplate <\n    typename SubsecondRatio,\n    typename Seconds,\n    typename Subseconds,\n    typename Rep,\n    std::intmax_t Denominator,\n    std::intmax_t Numerator>\nauto posixTimeToDuration(\n    Seconds seconds,\n    Subseconds subseconds,\n    std::chrono::duration<Rep, std::ratio<Numerator, Denominator>> dummy)\n    -> Expected<decltype(dummy), ConversionCode> {\n  using Tgt = decltype(dummy);\n  static_assert(\n      Tgt::period::num != 1, \"should use special-case code when num==1\");\n  static_assert(\n      Tgt::period::den != 1, \"should use special-case code when den==1\");\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  // Cast through an intermediate type with subsecond granularity.\n  // Note that this could fail due to overflow during the initial conversion\n  // even if the result is representable in the output POSIX-style types.\n  //\n  // Note that for integer type conversions going through this intermediate\n  // type can result in slight imprecision due to truncating the intermediate\n  // calculation to an integer.\n  using IntermediateType =\n      IntermediateDuration<typename Tgt::rep, typename Tgt::period>;\n  auto intermediate = posixTimeToDuration<SubsecondRatio>(\n      seconds, subseconds, IntermediateType{});\n  if (intermediate.hasError()) {\n    return makeUnexpected(intermediate.error());\n  }\n  // Now convert back to the target duration.  Use tryTo() to confirm that the\n  // result fits in the target representation type.\n  return tryTo<typename Tgt::rep>(\n             intermediate.value().count() / Tgt::period::num)\n      .then([](typename Tgt::rep tgt) { return Tgt{tgt}; });\n}\n\ntemplate <\n    typename Tgt,\n    typename SubsecondRatio,\n    typename Seconds,\n    typename Subseconds>\nExpected<Tgt, ConversionCode> tryPosixTimeToDuration(\n    Seconds seconds, Subseconds subseconds) {\n  static_assert(\n      SubsecondRatio::num == 1, \"subsecond numerator should always be 1\");\n\n  // Normalize the input if required\n  if (FOLLY_UNLIKELY(subseconds < 0)) {\n    const auto overflowSeconds = (subseconds / SubsecondRatio::den);\n    const auto remainder = (subseconds % SubsecondRatio::den);\n    if (std::numeric_limits<Seconds>::lowest() + 1 - overflowSeconds >\n        seconds) {\n      return makeUnexpected(ConversionCode::NEGATIVE_OVERFLOW);\n    }\n    seconds = seconds - 1 + overflowSeconds;\n    subseconds = to_narrow(remainder + SubsecondRatio::den);\n  } else if (FOLLY_UNLIKELY(subseconds >= SubsecondRatio::den)) {\n    const auto overflowSeconds = (subseconds / SubsecondRatio::den);\n    const auto remainder = (subseconds % SubsecondRatio::den);\n    if (std::numeric_limits<Seconds>::max() - overflowSeconds < seconds) {\n      return makeUnexpected(ConversionCode::POSITIVE_OVERFLOW);\n    }\n    seconds += overflowSeconds;\n    subseconds = to_narrow(remainder);\n  }\n\n  return posixTimeToDuration<SubsecondRatio>(seconds, subseconds, Tgt{});\n}\n\n} // namespace detail\n\n/**\n * struct timespec to std::chrono::duration\n */\ntemplate <typename Tgt>\ntypename std::enable_if<\n    detail::is_duration<Tgt>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const struct timespec& ts) {\n  return detail::tryPosixTimeToDuration<Tgt, std::nano>(ts.tv_sec, ts.tv_nsec);\n}\n\n/**\n * struct timeval to std::chrono::duration\n */\ntemplate <typename Tgt>\ntypename std::enable_if<\n    detail::is_duration<Tgt>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const struct timeval& tv) {\n  return detail::tryPosixTimeToDuration<Tgt, std::micro>(tv.tv_sec, tv.tv_usec);\n}\n\n/**\n * timespec or timeval to std::chrono::time_point\n */\ntemplate <typename Tgt, typename Src>\ntypename std::enable_if<\n    detail::is_time_point<Tgt>::value && detail::is_posix_time_type<Src>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const Src& value) {\n  return tryTo<typename Tgt::duration>(value).then(\n      [](typename Tgt::duration result) { return Tgt(result); });\n}\n\n/**\n * std::chrono::duration to struct timespec\n */\ntemplate <typename Tgt, typename Rep, typename Period>\ntypename std::enable_if<\n    std::is_same<Tgt, struct timespec>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const std::chrono::duration<Rep, Period>& duration) {\n  auto result = detail::durationToPosixTime<std::nano>(duration);\n  if (result.hasError()) {\n    return makeUnexpected(result.error());\n  }\n\n  struct timespec ts;\n  ts.tv_sec = result.value().first;\n  ts.tv_nsec = result.value().second;\n  return ts;\n}\n\n/**\n * std::chrono::duration to struct timeval\n */\ntemplate <typename Tgt, typename Rep, typename Period>\ntypename std::enable_if<\n    std::is_same<Tgt, struct timeval>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const std::chrono::duration<Rep, Period>& duration) {\n  auto result = detail::durationToPosixTime<std::micro>(duration);\n  if (result.hasError()) {\n    return makeUnexpected(result.error());\n  }\n\n  struct timeval tv;\n  tv.tv_sec = result.value().first;\n  tv.tv_usec = result.value().second;\n  return tv;\n}\n\n/**\n * std::chrono::time_point to timespec or timeval\n */\ntemplate <typename Tgt, typename Clock, typename Duration>\ntypename std::enable_if<\n    detail::is_posix_time_type<Tgt>::value,\n    Expected<Tgt, ConversionCode>>::type\ntryTo(const std::chrono::time_point<Clock, Duration>& timePoint) {\n  return tryTo<Tgt>(timePoint.time_since_epoch());\n}\n\n/**\n * For all chrono conversions, to() wraps tryTo()\n */\ntemplate <typename Tgt, typename Src>\ntypename std::enable_if<detail::is_chrono_conversion<Tgt, Src>::value, Tgt>::\n    type\n    to(const Src& value) {\n  return tryTo<Tgt>(value).thenOrThrow(\n      [](Tgt res) { return res; },\n      [&](ConversionCode e) { return makeConversionError(e, StringPiece{}); });\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/chrono/Hardware.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n\n#include <chrono>\n#include <cstdint>\n\n//  Description of and implementation of sequences for precise measurement:\n//    https://github.com/abseil/abseil-cpp/blob/20240116.2/absl/random/internal/nanobenchmark.cc#L164-L277\n\n#if defined(_MSC_VER) && !defined(__clang__) && \\\n    (defined(_M_IX86) || defined(_M_X64))\nextern \"C\" std::uint64_t __rdtsc();\nextern \"C\" std::uint64_t __rdtscp(unsigned int*);\nextern \"C\" void _ReadWriteBarrier();\nextern \"C\" void _mm_lfence();\n#pragma intrinsic(__rdtsc)\n#pragma intrinsic(__rdtscp)\n#pragma intrinsic(_ReadWriteBarrier)\n#pragma intrinsic(_mm_lfence)\n#endif\n\nnamespace folly {\n\ninline std::uint64_t hardware_timestamp() {\n#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))\n  return __rdtsc();\n#elif defined(__GNUC__) && (defined(__i386__) || FOLLY_X64)\n  return __builtin_ia32_rdtsc();\n#elif FOLLY_AARCH64 && !FOLLY_MOBILE\n  uint64_t cval;\n  asm volatile(\"mrs %0, cntvct_el0\" : \"=r\"(cval));\n  return cval;\n#else\n  // use steady_clock::now() as an approximation for the timestamp counter on\n  // non-x86 systems\n  return std::chrono::steady_clock::now().time_since_epoch().count();\n#endif\n}\n\n/// hardware_timestamp_measurement_start\n/// hardware_timestamp_measurement_stop\n///\n/// Suitable for beginning precise measurement of a region of code.\n///\n/// Prevents the compiler from reordering instructions across the call. This is\n/// in contrast with hardware_timestamp(), which may not prevent the compiler\n/// from reordering instructions across the call.\n///\n/// Prevents the processor from pipelining the call with loads. This is in\n/// contrast with hardware_timestamp(), which allows the processor to pipeline\n/// the call with surrounding loads.\n///\n/// Does not prevent instruction pipelines from continuing execution across the\n/// call. For example, does not prevent a store issued before the call from\n/// continuing execution (becoming globally visible) across the call. This means\n/// that this form may not be suitable for certain measurement use-cases which\n/// would require such prevention. However, this would be suitable for typical\n/// measurement use-cases which do not specifically need to measure background\n/// work such as store execution.\nstd::uint64_t hardware_timestamp_measurement_start() noexcept;\nstd::uint64_t hardware_timestamp_measurement_stop() noexcept;\n\ninline std::uint64_t hardware_timestamp_measurement_start() noexcept {\n#if defined(_MSC_VER) && !defined(__clang__) && \\\n    (defined(_M_IX86) || defined(_M_X64))\n  // msvc does not have embedded assembly\n  _ReadWriteBarrier();\n  _mm_lfence();\n  _ReadWriteBarrier();\n  auto const ret = __rdtsc();\n  _ReadWriteBarrier();\n  _mm_lfence();\n  _ReadWriteBarrier();\n  return ret;\n#elif defined(__GNUC__) && FOLLY_X64\n  uint64_t ret = 0;\n#if !defined(__clang_major__) || __clang_major__ >= 11\n  asm volatile inline(\n#else\n  asm volatile(\n#endif\n      \"lfence\\n\"\n      \"rdtsc\\n\" // loads 64-bit tsc into edx:eax\n      \"shl $32, %%rdx\\n\" // prep rdx for combine into rax\n      \"or %%rdx, %[ret]\\n\" // combine rdx into rax\n      \"lfence\\n\"\n      : [ret] \"=a\"(ret) // bind ret to rax for output\n      : // no inputs\n      : \"rdx\", // rdtsc loads into edx:eax\n        \"cc\", // shl clobbers condition-code\n        \"memory\" // memory clobber asks gcc/clang not to reorder\n  );\n  return ret;\n#else\n  // use steady_clock::now() as an approximation for the timestamp counter on\n  // non-x64 systems\n  return std::chrono::steady_clock::now().time_since_epoch().count();\n#endif\n}\n\ninline std::uint64_t hardware_timestamp_measurement_stop() noexcept {\n#if defined(_MSC_VER) && !defined(__clang__) && \\\n    (defined(_M_IX86) || defined(_M_X64))\n  // msvc does not have embedded assembly\n  _ReadWriteBarrier();\n  unsigned int aux;\n  auto const ret = __rdtscp(&aux);\n  _ReadWriteBarrier();\n  _mm_lfence();\n  _ReadWriteBarrier();\n  return ret;\n#elif defined(__GNUC__) && FOLLY_X64\n  uint64_t ret = 0;\n#if !defined(__clang_major__) || __clang_major__ >= 11\n  asm volatile inline(\n#else\n  asm volatile(\n#endif\n      \"rdtscp\\n\" // loads 64-bit tsc into edx:eax, clobbers ecx\n      \"shl $32, %%rdx\\n\" // prep rdx for combine into rax\n      \"or %%rdx, %[ret]\\n\" // combine rdx into rax\n      \"lfence\\n\"\n      : [ret] \"=a\"(ret) // bind ret to rax for output\n      : // no inputs\n      : \"rdx\", // rdtscp loads into edx:eax\n        \"rcx\", // rdtscp clobbers rcx\n        \"cc\", // shl clobbers condition-code\n        \"memory\" // memory clobber asks gcc/clang not to reorder\n  );\n  return ret;\n#else\n  // use steady_clock::now() as an approximation for the timestamp counter on\n  // non-x64 systems\n  return std::chrono::steady_clock::now().time_since_epoch().count();\n#endif\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/chrono/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"clock_test\",\n    srcs = [\"ClockTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/chrono:clock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"conv_test\",\n    srcs = [\"ConvTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/chrono:conv\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/chrono/test/ClockTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/chrono/Clock.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace std::chrono;\nusing namespace std::chrono_literals;\n\nTEST(ClockTest, SteadyClockSanity) {\n  std::unique_ptr<folly::chrono::SteadyClock> clock =\n      std::make_unique<folly::chrono::SteadyClockImpl>();\n\n  // stclock = system clock\n  // rstclock = reference system clock\n  const auto stclockTp1 = clock->now();\n  const auto rstclockTp1 = std::chrono::steady_clock::now();\n  const auto stclockTp2 = clock->now();\n  const auto rstclockTp2 = std::chrono::steady_clock::now();\n\n  EXPECT_LE(stclockTp1, rstclockTp1);\n  EXPECT_LE(rstclockTp1, stclockTp2);\n  EXPECT_LE(stclockTp2, rstclockTp2);\n\n  EXPECT_GE(rstclockTp2, stclockTp2);\n  EXPECT_GE(stclockTp2, rstclockTp1);\n  EXPECT_GE(rstclockTp1, stclockTp1);\n}\n\nTEST(ClockTest, SystemClockSanity) {\n  std::unique_ptr<folly::chrono::SystemClock> clock =\n      std::make_unique<folly::chrono::SystemClockImpl>();\n\n  // sclock = system clock\n  // rsclock = reference system clock\n  const auto syclockTp1 = clock->now();\n  const auto rsyclockTp1 = std::chrono::system_clock::now();\n  const auto syclockTp2 = clock->now();\n  const auto rsyclockTp2 = std::chrono::system_clock::now();\n\n  /**\n   * if system clock goes backwards during a test due to NTP correction, the\n   * following may fail — but we expect that to be a very rare occurrence; rare\n   * enough to not make this a problematic (flaky) test\n   */\n\n  EXPECT_LE(syclockTp1, rsyclockTp1);\n  EXPECT_LE(rsyclockTp1, syclockTp2);\n  EXPECT_LE(syclockTp2, rsyclockTp2);\n\n  EXPECT_GE(rsyclockTp2, syclockTp2);\n  EXPECT_GE(syclockTp2, rsyclockTp1);\n  EXPECT_GE(rsyclockTp1, syclockTp1);\n}\n"
  },
  {
    "path": "folly/chrono/test/ConvTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/chrono/Conv.h>\n\n#include <limits>\n\n#include <glog/logging.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing namespace std::chrono;\nusing namespace std::chrono_literals;\n\nnamespace {\n/**\n * A helper function to create a time_point even if the input duration type has\n * finer resolution than the clock duration type.\n */\ntemplate <typename Clock, typename Duration>\ntypename Clock::time_point createTimePoint(const Duration& d) {\n  return typename Clock::time_point(\n      std::chrono::duration_cast<typename Clock::duration>(d));\n}\n} // namespace\n\nTEST(Conv, timespecToStdChrono) {\n  struct timespec ts;\n\n  ts.tv_sec = 0;\n  ts.tv_nsec = 10;\n  EXPECT_EQ(10ns, to<nanoseconds>(ts));\n  EXPECT_EQ(0us, to<microseconds>(ts));\n  EXPECT_EQ(0ms, to<milliseconds>(ts));\n  EXPECT_EQ(0s, to<seconds>(ts));\n\n  ts.tv_sec = 1;\n  ts.tv_nsec = 10;\n  EXPECT_EQ(1000000010ns, to<nanoseconds>(ts));\n  EXPECT_EQ(1000000us, to<microseconds>(ts));\n  EXPECT_EQ(1000ms, to<milliseconds>(ts));\n  EXPECT_EQ(1s, to<seconds>(ts));\n  EXPECT_EQ(\n      createTimePoint<system_clock>(1000000010ns),\n      to<system_clock::time_point>(ts));\n  EXPECT_EQ(\n      createTimePoint<steady_clock>(1000000010ns),\n      to<steady_clock::time_point>(ts));\n\n  // Test a non-canonical value with tv_nsec larger than 1 second\n  ts.tv_sec = 5;\n  ts.tv_nsec = 3219876543;\n  // Beware about using std::chrono_literals suffixes with very literals:\n  // older versions of GCC are buggy and would truncate these to 32-bits.\n  EXPECT_EQ(8219876543LL, to<nanoseconds>(ts).count());\n  EXPECT_EQ(8219876us, to<microseconds>(ts));\n  EXPECT_EQ(8219ms, to<milliseconds>(ts));\n  EXPECT_EQ(8s, to<seconds>(ts));\n  EXPECT_EQ(\n      createTimePoint<system_clock>(nanoseconds(8219876543LL)),\n      to<system_clock::time_point>(ts));\n  EXPECT_EQ(\n      createTimePoint<steady_clock>(nanoseconds(8219876543LL)),\n      to<steady_clock::time_point>(ts));\n\n  // Test negative values\n  // When going to coarser grained types these should be rounded up towards 0.\n  ts.tv_sec = -5;\n  ts.tv_nsec = 123456;\n  EXPECT_EQ(-4999876544, to<nanoseconds>(ts).count());\n  EXPECT_EQ(-4999876544, duration_cast<nanoseconds>(-5s + 123456ns).count());\n  EXPECT_EQ(-4999876, to<microseconds>(ts).count());\n  EXPECT_EQ(-4999876, duration_cast<microseconds>(-5s + 123456ns).count());\n  EXPECT_EQ(-4999, to<milliseconds>(ts).count());\n  EXPECT_EQ(-4999, duration_cast<milliseconds>(-5s + 123456ns).count());\n  EXPECT_EQ(-4s, to<seconds>(ts));\n  EXPECT_EQ(-4, duration_cast<seconds>(-5s + 123456ns).count());\n  ts.tv_sec = -7200;\n  ts.tv_nsec = 123456;\n  EXPECT_EQ(-1h, to<hours>(ts));\n  EXPECT_EQ(\n      -1,\n      duration_cast<hours>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})\n          .count());\n  ts.tv_sec = -7000;\n  ts.tv_nsec = 123456;\n  EXPECT_EQ(-1h, to<hours>(ts));\n  EXPECT_EQ(\n      -1,\n      duration_cast<hours>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})\n          .count());\n  ts.tv_sec = -7201;\n  ts.tv_nsec = 123456;\n  EXPECT_EQ(-2h, to<hours>(ts));\n  EXPECT_EQ(\n      -2,\n      duration_cast<hours>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})\n          .count());\n\n  // Test converions to floating point durations\n  ts.tv_sec = 1;\n  ts.tv_nsec = 500000000;\n  EXPECT_EQ(1.5, to<duration<double>>(ts).count());\n  ts.tv_sec = -1;\n  ts.tv_nsec = 500000000;\n  EXPECT_EQ(-0.5, to<duration<double>>(ts).count());\n  ts.tv_sec = -1;\n  ts.tv_nsec = -500000000;\n  EXPECT_EQ(-1.5, to<duration<double>>(ts).count());\n  ts.tv_sec = 1;\n  ts.tv_nsec = 500000000;\n  auto doubleNanos = to<duration<double, std::nano>>(ts);\n  EXPECT_EQ(1500000000, doubleNanos.count());\n  ts.tv_sec = 90;\n  ts.tv_nsec = 0;\n  auto doubleMinutes = to<duration<double, std::ratio<60>>>(ts);\n  EXPECT_EQ(1.5, doubleMinutes.count());\n\n  // Test with unusual durations where neither the numerator nor denominator\n  // are 1.\n  using five_sevenths = std::chrono::duration<int64_t, std::ratio<5, 7>>;\n  ts.tv_sec = 1;\n  ts.tv_nsec = 0;\n  EXPECT_EQ(1, to<five_sevenths>(ts).count());\n  ts.tv_sec = 1;\n  ts.tv_nsec = 428571500;\n  EXPECT_EQ(2, to<five_sevenths>(ts).count());\n\n  using thirteen_thirds = std::chrono::duration<double, std::ratio<13, 3>>;\n  ts.tv_sec = 39;\n  ts.tv_nsec = 0;\n  EXPECT_NEAR(9.0, to<thirteen_thirds>(ts).count(), 0.000000001);\n  ts.tv_sec = 1;\n  ts.tv_nsec = 0;\n  EXPECT_NEAR(0.230769230, to<thirteen_thirds>(ts).count(), 0.000000001);\n}\n\nTEST(Conv, timespecToStdChronoOverflow) {\n  struct timespec ts;\n\n  // All of our boundary conditions below assume time_t is int64_t.\n  // This is true on most modern platforms.\n  if (!std::is_same<decltype(ts.tv_sec), int64_t>::value) {\n    LOG(INFO) << \"skipping most overflow tests: time_t is not int64_t\";\n  } else {\n    // Test the upper boundary of conversion to uint64_t nanoseconds\n    using nsec_u64 = std::chrono::duration<uint64_t, std::nano>;\n    ts.tv_sec = 18446744073;\n    ts.tv_nsec = 709551615;\n    EXPECT_EQ(std::numeric_limits<uint64_t>::max(), to<nsec_u64>(ts).count());\n\n    ts.tv_nsec += 1;\n    EXPECT_THROW(to<nsec_u64>(ts), std::range_error);\n\n    // Test the lower boundary of conversion to uint64_t nanoseconds\n    ts.tv_sec = 0;\n    ts.tv_nsec = 0;\n    EXPECT_EQ(0, to<nsec_u64>(ts).count());\n    ts.tv_sec = -1;\n    ts.tv_nsec = 0;\n    EXPECT_THROW(to<nsec_u64>(ts), std::range_error);\n\n    // Test the upper boundary of conversion to int64_t microseconds\n    using usec_i64 = std::chrono::duration<int64_t, std::micro>;\n    ts.tv_sec = 9223372036854LL;\n    ts.tv_nsec = 775807000;\n    EXPECT_EQ(std::numeric_limits<int64_t>::max(), to<usec_i64>(ts).count());\n\n    ts.tv_nsec += 1;\n    EXPECT_THROW(to<usec_i64>(ts), std::range_error);\n\n    // Test the lower boundary of conversion to int64_t microseconds\n    ts.tv_sec = -9223372036855LL;\n    ts.tv_nsec = 224192000;\n    EXPECT_EQ(std::numeric_limits<int64_t>::min(), to<usec_i64>(ts).count());\n\n    ts.tv_nsec -= 1;\n    EXPECT_THROW(to<usec_i64>(ts), std::range_error);\n\n    // Test the boundaries of conversion to int32_t seconds\n    using sec_i32 = std::chrono::duration<int32_t>;\n    ts.tv_sec = 2147483647;\n    ts.tv_nsec = 0;\n    EXPECT_EQ(std::numeric_limits<int32_t>::max(), to<sec_i32>(ts).count());\n    ts.tv_nsec = 1000000000;\n    EXPECT_THROW(to<sec_i32>(ts), std::range_error);\n    ts.tv_sec = -2147483648;\n    ts.tv_nsec = 0;\n    EXPECT_EQ(std::numeric_limits<int32_t>::min(), to<sec_i32>(ts).count());\n    ts.tv_sec = -2147483649;\n    ts.tv_nsec = 999999999;\n    EXPECT_THROW(to<sec_i32>(ts), std::range_error);\n    ts.tv_sec = -2147483649;\n    ts.tv_nsec = 0;\n    EXPECT_THROW(to<sec_i32>(ts), std::range_error);\n    ts.tv_sec = -2147483650;\n    ts.tv_nsec = 0;\n    EXPECT_THROW(to<sec_i32>(ts), std::range_error);\n\n    // Test the upper boundary of conversion to uint32_t hours\n    using hours_u32 = std::chrono::duration<uint32_t, std::ratio<3600>>;\n    ts.tv_sec = 15461882262000LL;\n    ts.tv_nsec = 0;\n    EXPECT_EQ(std::numeric_limits<uint32_t>::max(), to<hours_u32>(ts).count());\n    ts.tv_sec = 15461882265599LL;\n    EXPECT_EQ(std::numeric_limits<uint32_t>::max(), to<hours_u32>(ts).count());\n    ts.tv_sec = 15461882265600LL;\n    EXPECT_THROW(to<hours_u32>(ts), std::range_error);\n\n    using nsec_i64 = std::chrono::duration<int64_t, std::nano>;\n    ts.tv_sec = std::numeric_limits<int64_t>::max();\n    ts.tv_nsec = std::numeric_limits<int64_t>::max();\n    EXPECT_THROW(to<nsec_i64>(ts), std::range_error);\n\n    ts.tv_sec = std::numeric_limits<int64_t>::min();\n    ts.tv_nsec = std::numeric_limits<int64_t>::min();\n    EXPECT_THROW(to<nsec_i64>(ts), std::range_error);\n\n    // Test some non-normal inputs near the int64_t limit\n    ts.tv_sec = 0;\n    ts.tv_nsec = std::numeric_limits<int64_t>::min();\n    EXPECT_EQ(std::numeric_limits<int64_t>::min(), to<nsec_i64>(ts).count());\n    ts.tv_sec = -1;\n    ts.tv_nsec = std::numeric_limits<int64_t>::min() + std::nano::den;\n    EXPECT_EQ(std::numeric_limits<int64_t>::min(), to<nsec_i64>(ts).count());\n    ts.tv_sec = -1;\n    ts.tv_nsec = std::numeric_limits<int64_t>::min() + std::nano::den - 1;\n    EXPECT_THROW(to<nsec_i64>(ts), std::range_error);\n\n    ts.tv_sec = 0;\n    ts.tv_nsec = std::numeric_limits<int64_t>::max();\n    EXPECT_EQ(std::numeric_limits<int64_t>::max(), to<nsec_i64>(ts).count());\n    ts.tv_sec = 1;\n    ts.tv_nsec = std::numeric_limits<int64_t>::max() - std::nano::den;\n    EXPECT_EQ(std::numeric_limits<int64_t>::max(), to<nsec_i64>(ts).count());\n    ts.tv_sec = 1;\n    ts.tv_nsec = std::numeric_limits<int64_t>::max() - std::nano::den + 1;\n    EXPECT_THROW(to<nsec_i64>(ts), std::range_error);\n  }\n\n  // Theoretically conversion is representable in the output type,\n  // but we normalize the input first, and normalization would trigger an\n  // overflow.\n  using hours_u64 = std::chrono::duration<uint64_t, std::ratio<3600>>;\n  ts.tv_sec = std::numeric_limits<decltype(ts.tv_sec)>::max();\n  ts.tv_nsec = 1000000000;\n  EXPECT_THROW(to<hours_u64>(ts), std::range_error);\n  // If we drop it back down to the normal range it should succeed\n  ts.tv_nsec = 999999999;\n  EXPECT_EQ(\n      std::numeric_limits<decltype(ts.tv_sec)>::max() / 3600,\n      to<hours_u64>(ts).count());\n\n  // Test overflow with an unusual duration where neither the numerator nor\n  // denominator are 1.\n  using unusual_time = std::chrono::duration<int16_t, std::ratio<13, 3>>;\n  ts.tv_sec = 141994;\n  ts.tv_nsec = 666666666;\n  EXPECT_EQ(32767, to<unusual_time>(ts).count());\n  ts.tv_nsec = 666666667;\n  EXPECT_THROW(to<unusual_time>(ts), std::range_error);\n\n  ts.tv_sec = -141998;\n  ts.tv_nsec = 999999999;\n  EXPECT_EQ(-32768, to<unusual_time>(ts).count());\n  ts.tv_sec = -141999;\n  ts.tv_nsec = 0;\n  EXPECT_THROW(to<unusual_time>(ts), std::range_error);\n}\n\nTEST(Conv, timevalToStdChrono) {\n  struct timeval tv;\n\n  tv.tv_sec = 0;\n  tv.tv_usec = 10;\n  EXPECT_EQ(10000ns, to<nanoseconds>(tv));\n  EXPECT_EQ(10us, to<microseconds>(tv));\n  EXPECT_EQ(0ms, to<milliseconds>(tv));\n  EXPECT_EQ(0s, to<seconds>(tv));\n\n  tv.tv_sec = 1;\n  tv.tv_usec = 10;\n  EXPECT_EQ(1000010000ns, to<nanoseconds>(tv));\n  EXPECT_EQ(1000010us, to<microseconds>(tv));\n  EXPECT_EQ(1000ms, to<milliseconds>(tv));\n  EXPECT_EQ(1s, to<seconds>(tv));\n  EXPECT_EQ(\n      createTimePoint<system_clock>(1000010000ns),\n      to<system_clock::time_point>(tv));\n  EXPECT_EQ(\n      createTimePoint<steady_clock>(1000010000ns),\n      to<steady_clock::time_point>(tv));\n\n  // Test a non-canonical value with tv_usec larger than 1 second\n  tv.tv_sec = 5;\n  tv.tv_usec = 3219876;\n  EXPECT_EQ(8219876000LL, to<nanoseconds>(tv).count());\n  EXPECT_EQ(8219876us, to<microseconds>(tv));\n  EXPECT_EQ(8219ms, to<milliseconds>(tv));\n  EXPECT_EQ(8s, to<seconds>(tv));\n  EXPECT_EQ(\n      createTimePoint<system_clock>(nanoseconds(8219876000LL)),\n      to<system_clock::time_point>(tv));\n  EXPECT_EQ(\n      createTimePoint<steady_clock>(nanoseconds(8219876000LL)),\n      to<steady_clock::time_point>(tv));\n\n  // Test for overflow.\n  if (std::numeric_limits<decltype(tv.tv_sec)>::max() >=\n      std::numeric_limits<int64_t>::max()) {\n    // Use our own type alias here rather than std::chrono::nanoseconds\n    // to ensure we have 64-bit rep type.\n    using nsec_i64 = std::chrono::duration<int64_t, std::nano>;\n    tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::max();\n    tv.tv_usec = std::numeric_limits<decltype(tv.tv_usec)>::max();\n    EXPECT_THROW(to<nsec_i64>(tv), std::range_error);\n\n    tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min();\n    tv.tv_usec = std::numeric_limits<decltype(tv.tv_usec)>::max();\n    EXPECT_THROW(to<nsec_i64>(tv), std::range_error);\n  }\n}\n\nTEST(Conv, stdChronoToTimespec) {\n  auto ts = to<struct timespec>(10ns);\n  EXPECT_EQ(0, ts.tv_sec);\n  EXPECT_EQ(10, ts.tv_nsec);\n\n  // We don't use std::chrono_literals suffixes here since older\n  // gcc versions silently truncate the literals to 32-bits.\n  ts = to<struct timespec>(nanoseconds(987654321012LL));\n  EXPECT_EQ(987, ts.tv_sec);\n  EXPECT_EQ(654321012, ts.tv_nsec);\n\n  ts = to<struct timespec>(nanoseconds(-987654321012LL));\n  EXPECT_EQ(-988, ts.tv_sec);\n  EXPECT_EQ(345678988, ts.tv_nsec);\n\n  ts = to<struct timespec>(microseconds(987654321012LL));\n  EXPECT_EQ(987654, ts.tv_sec);\n  EXPECT_EQ(321012000, ts.tv_nsec);\n\n  ts = to<struct timespec>(milliseconds(987654321012LL));\n  EXPECT_EQ(987654321, ts.tv_sec);\n  EXPECT_EQ(12000000, ts.tv_nsec);\n\n  ts = to<struct timespec>(seconds(987654321012LL));\n  EXPECT_EQ(987654321012, ts.tv_sec);\n  EXPECT_EQ(0, ts.tv_nsec);\n\n  ts = to<struct timespec>(10h);\n  EXPECT_EQ(36000, ts.tv_sec);\n  EXPECT_EQ(0, ts.tv_nsec);\n\n  // Must select duration types from the clock for the createTimePoint tests\n  // since not all clocks on all platforms natively support nanoseconds:\n\n  constexpr auto const steady_duration = steady_clock::duration(123);\n  ts = to<struct timespec>(createTimePoint<steady_clock>(steady_duration));\n  EXPECT_EQ(0, ts.tv_sec);\n  EXPECT_EQ(nanoseconds(steady_duration).count(), ts.tv_nsec);\n\n  constexpr auto const system_duration = system_clock::duration(123);\n  ts = to<struct timespec>(createTimePoint<system_clock>(system_duration));\n  EXPECT_EQ(0, ts.tv_sec);\n  EXPECT_EQ(nanoseconds(system_duration).count(), ts.tv_nsec);\n\n  // Test with some unusual durations where neither the numerator nor\n  // denominator are 1.\n  using five_sevenths = std::chrono::duration<int64_t, std::ratio<5, 7>>;\n  ts = to<struct timespec>(five_sevenths(7));\n  EXPECT_EQ(5, ts.tv_sec);\n  EXPECT_EQ(0, ts.tv_nsec);\n  ts = to<struct timespec>(five_sevenths(19));\n  EXPECT_EQ(13, ts.tv_sec);\n  EXPECT_EQ(571428571, ts.tv_nsec);\n  using seven_fifths = std::chrono::duration<int64_t, std::ratio<7, 5>>;\n  ts = to<struct timespec>(seven_fifths(5));\n  EXPECT_EQ(7, ts.tv_sec);\n  EXPECT_EQ(0, ts.tv_nsec);\n}\n\nTEST(Conv, stdChronoToTimespecOverflow) {\n  EXPECT_THROW(to<uint8_t>(1234), std::range_error);\n\n  struct timespec ts;\n  if (!std::is_same<decltype(ts.tv_sec), int64_t>::value) {\n    LOG(INFO) << \"skipping most overflow tests: time_t is not int64_t\";\n  } else {\n    // Check for overflow converting from uint64_t seconds to time_t\n    using sec_u64 = duration<uint64_t>;\n    ts =\n        to<struct timespec>(sec_u64(9223372036854775807ULL)); // largest int64_t\n    EXPECT_EQ(ts.tv_sec, 9223372036854775807ULL);\n    EXPECT_EQ(ts.tv_nsec, 0);\n\n    EXPECT_THROW(\n        to<struct timespec>(sec_u64(9223372036854775808ULL)), std::range_error);\n\n    // Check for overflow converting from int64_t hours to time_t\n    using hours_i64 = duration<int64_t, std::ratio<3600>>;\n    ts = to<struct timespec>(hours_i64(2562047788015215LL));\n    EXPECT_EQ(ts.tv_sec, 9223372036854774000LL);\n    EXPECT_EQ(ts.tv_nsec, 0);\n    EXPECT_THROW(\n        to<struct timespec>(hours_i64(2562047788015216LL)), std::range_error);\n\n    // Test overflows from an unusual duration where neither the numerator nor\n    // denominator are 1.\n    using three_halves = std::chrono::duration<uint64_t, std::ratio<3, 2>>;\n    EXPECT_THROW(\n        to<struct timespec>(three_halves(6148914691236517206ULL)),\n        std::range_error);\n  }\n\n  // Test for overflow.\n  // Use a custom hours type using time_t as the underlying storage type to\n  // guarantee that we can overflow.\n  using hours_timet = std::chrono::duration<time_t, std::ratio<3600>>;\n  EXPECT_THROW(\n      to<struct timespec>(hours_timet(std::numeric_limits<time_t>::max())),\n      std::range_error);\n}\n\nTEST(Conv, stdChronoToTimeval) {\n  auto tv = to<struct timeval>(10ns);\n  EXPECT_EQ(0, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(10us);\n  EXPECT_EQ(0, tv.tv_sec);\n  EXPECT_EQ(10, tv.tv_usec);\n\n  tv = to<struct timeval>(nanoseconds(987654321012LL));\n  EXPECT_EQ(987, tv.tv_sec);\n  EXPECT_EQ(654321, tv.tv_usec);\n\n  tv = to<struct timeval>(nanoseconds(-987654321012LL));\n  EXPECT_EQ(-988, tv.tv_sec);\n  EXPECT_EQ(345679, tv.tv_usec);\n\n  tv = to<struct timeval>(microseconds(987654321012LL));\n  EXPECT_EQ(987654, tv.tv_sec);\n  EXPECT_EQ(321012, tv.tv_usec);\n\n  tv = to<struct timeval>(milliseconds(987654321012LL));\n  EXPECT_EQ(987654321, tv.tv_sec);\n  EXPECT_EQ(12000, tv.tv_usec);\n\n  tv = to<struct timeval>(seconds(987654321012LL));\n  EXPECT_EQ(987654321012, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  // Try converting fractional seconds\n  tv = to<struct timeval>(duration<double>{3.456789});\n  EXPECT_EQ(3, tv.tv_sec);\n  EXPECT_EQ(456789, tv.tv_usec);\n  tv = to<struct timeval>(duration<double>{-3.456789});\n  EXPECT_EQ(-4, tv.tv_sec);\n  EXPECT_EQ(543211, tv.tv_usec);\n\n  // Try converting integrals with width less than int64_t\n  tv = to<struct timeval>(\n      duration<unsigned char>{std::numeric_limits<unsigned char>::max()});\n  EXPECT_EQ(std::numeric_limits<unsigned char>::max(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<unsigned char>{0});\n  EXPECT_EQ(0, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<char>{std::numeric_limits<char>::max()});\n  EXPECT_EQ(std::numeric_limits<char>::max(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<char>{std::numeric_limits<char>::min()});\n  EXPECT_EQ(std::numeric_limits<char>::min(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(\n      duration<unsigned short>{std::numeric_limits<unsigned short>::max()});\n  EXPECT_EQ(std::numeric_limits<unsigned short>::max(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<short>{std::numeric_limits<short>::max()});\n  EXPECT_EQ(std::numeric_limits<short>::max(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<short>{std::numeric_limits<short>::min()});\n  EXPECT_EQ(std::numeric_limits<short>::min(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(\n      duration<unsigned int>{std::numeric_limits<unsigned int>::max()});\n  EXPECT_EQ(std::numeric_limits<unsigned int>::max(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<int>{std::numeric_limits<int>::max()});\n  EXPECT_EQ(std::numeric_limits<int>::max(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<int>{std::numeric_limits<int>::min()});\n  EXPECT_EQ(std::numeric_limits<int>::min(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  // Try converting integral types with a range that is greater than int64_t\n  tv = to<struct timeval>(duration<uint64_t>{9223372036854775807ULL});\n  EXPECT_EQ(9223372036854775807ULL, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n#if FOLLY_HAVE_INT128_T\n  tv = to<struct timeval>(duration<folly::uint128_t>{9223372036854775807ULL});\n  EXPECT_EQ(9223372036854775807ULL, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<folly::uint128_t>{0});\n  EXPECT_EQ(0, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<folly::int128_t>{9223372036854775807ULL});\n  EXPECT_EQ(9223372036854775807ULL, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(\n      duration<folly::int128_t>{std::numeric_limits<int64_t>::lowest()});\n  EXPECT_EQ(std::numeric_limits<int64_t>::lowest(), tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n#endif\n\n  // Try converting from the lowest floating point that int64_t can represent\n  tv = to<struct timeval>(duration<float>{-9223372036854775808.f});\n  EXPECT_EQ(-9223372036854775808.f, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<double>{-9223372036854775808.});\n  EXPECT_EQ(-9223372036854775808., tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  // Try converting the largest double that is in range of an int64_t\n  tv = to<struct timeval>(duration<double>{9223372036854774784.0});\n  EXPECT_EQ(9223372036854774784, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  // Try converting the largest float that is in range of an int64_t\n  tv = to<struct timeval>(duration<float>{9223371487098961920.0f});\n  EXPECT_EQ(9223371487098961920, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  // Try converting fractional hours\n  tv = to<struct timeval>(duration<double, std::ratio<3600>>{3.456789});\n  EXPECT_EQ(12444, tv.tv_sec);\n  // The usec field is generally off-by-one due to\n  // floating point rounding error\n  EXPECT_NEAR(440400, tv.tv_usec, 1);\n  tv = to<struct timeval>(duration<double, std::ratio<3600>>{-3.456789});\n  EXPECT_EQ(-12445, tv.tv_sec);\n  EXPECT_NEAR(559600, tv.tv_usec, 1);\n\n  // Try converting fractional milliseconds\n  tv = to<struct timeval>(duration<double, std::milli>{9123.456789});\n  EXPECT_EQ(9, tv.tv_sec);\n  EXPECT_EQ(123456, tv.tv_usec);\n  tv = to<struct timeval>(duration<double, std::milli>{-9123.456789});\n  EXPECT_EQ(-10, tv.tv_sec);\n  EXPECT_NEAR(876544, tv.tv_usec, 1);\n\n  tv = to<struct timeval>(duration<uint32_t, std::ratio<3600>>{3});\n  EXPECT_EQ(10800, tv.tv_sec);\n  EXPECT_EQ(0, tv.tv_usec);\n\n  tv = to<struct timeval>(duration<uint32_t, std::nano>{3123});\n  EXPECT_EQ(0, tv.tv_sec);\n  EXPECT_EQ(3, tv.tv_usec);\n  tv = to<struct timeval>(duration<int32_t, std::nano>{-3123});\n  EXPECT_EQ(-1, tv.tv_sec);\n  EXPECT_EQ(999997, tv.tv_usec);\n\n  tv = to<struct timeval>(createTimePoint<steady_clock>(123us));\n  EXPECT_EQ(0, tv.tv_sec);\n  EXPECT_EQ(123, tv.tv_usec);\n\n  tv = to<struct timeval>(createTimePoint<system_clock>(123us));\n  EXPECT_EQ(0, tv.tv_sec);\n  EXPECT_EQ(123, tv.tv_usec);\n}\n\nTEST(Conv, stdChronoToTimevalOverflow) {\n  // time_t max is 9223372036854775807\n  // converting it to a float or a double overflows to 9223372036854775808.0\n  EXPECT_THROW(\n      to<struct timeval>(duration<double>{9223372036854775808.}),\n      std::range_error);\n\n  EXPECT_THROW(\n      to<struct timeval>(duration<float>{9223372036854775808.f}),\n      std::range_error);\n\n  // Try the next float smaller than int64_t lowest\n  EXPECT_THROW(\n      to<struct timeval>(duration<double>{-9223372036854777856.}),\n      std::range_error);\n\n  // Try the next double smaller than int64_t lowest\n  EXPECT_THROW(\n      to<struct timeval>(duration<float>{-9223373136366403584.f}),\n      std::range_error);\n\n  // Test integrals with a larger range than time_t\n  EXPECT_THROW(\n      to<struct timeval>(duration<unsigned long>{9223372036854775808ULL}),\n      std::range_error);\n\n#if FOLLY_HAVE_INT128_T\n  EXPECT_THROW(\n      to<struct timeval>(duration<folly::uint128_t>{9223372036854775808ULL}),\n      std::range_error);\n\n  EXPECT_THROW(\n      to<struct timeval>(duration<folly::uint128_t>{\n          std::numeric_limits<folly::uint128_t>::max()}),\n      std::range_error);\n\n  EXPECT_THROW(\n      to<struct timeval>(duration<folly::int128_t>{9223372036854775808ULL}),\n      std::range_error);\n\n  EXPECT_THROW(\n      to<struct timeval>(duration<folly::int128_t>{\n          std::numeric_limits<folly::int128_t>::max()}),\n      std::range_error);\n\n  EXPECT_THROW(\n      to<struct timeval>(duration<folly::int128_t>{\n          static_cast<folly::int128_t>(std::numeric_limits<int64_t>::min()) -\n          1}),\n      std::range_error);\n#endif\n}\n"
  },
  {
    "path": "folly/cli/Args.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/cli/Args.h>\n\n#include <cerrno>\n#include <filesystem>\n#include <optional>\n#include <set>\n#include <string>\n#include <system_error>\n#include <vector>\n\n#include <fmt/core.h>\n\n#include <folly/FileUtil.h>\n\nnamespace folly {\n\nstd::vector<char const*> cli_args_strings_to_c_strings(\n    span<std::string const> args) {\n  std::vector<char const*> argv;\n  argv.reserve(args.size());\n  for (auto const& arg : args) {\n    argv.push_back(arg.c_str());\n  }\n  return argv;\n}\n\nnamespace {\n\nclass cli_apply_args_files_error_category : public std::error_category {\n public:\n  char const* name() const noexcept override { return \"cli_apply_args_files\"; }\n  std::string message(int ev) const override {\n    switch (static_cast<cli_apply_args_files_errc>(ev)) {\n      case cli_apply_args_files_errc::max_depth_exceeded:\n        return \"max depth exceeded\";\n      default:\n        return \"unknown error\";\n    }\n  }\n};\n\n} // namespace\n\nstd::error_code make_error_code(cli_apply_args_files_errc errc) {\n  static cli_apply_args_files_error_category const category;\n  return {static_cast<int>(errc), category};\n}\n\nnamespace {\n\nstruct cli_parsed_arg {\n  std::string value;\n  size_t start_offset;\n  size_t length;\n  size_t begin_line;\n  size_t begin_col;\n  size_t end_line;\n  size_t end_col;\n  bool starts_with_at_outside_quotes; // true if first char is @ outside quotes\n  bool starts_with_double_at_outside_quotes; // true if first two chars are @@\n};\n\nstruct cli_parse_error {\n  cli_apply_args_files_receiver::term_error code;\n  // Term location (where the problematic term begins)\n  size_t term_offset;\n  size_t term_begin_line;\n  size_t term_begin_col;\n  // Error location (where the error begins within the term)\n  size_t error_offset;\n  size_t error_begin_line;\n  size_t error_begin_col;\n};\n\nstruct cli_parse_result {\n  std::vector<cli_parsed_arg> args;\n  std::optional<cli_parse_error> error;\n};\n\n/// Check if a character is whitespace (as recognized by std::isspace in C\n/// locale) Whitespace characters: space, tab, newline, carriage return, form\n/// feed, vertical tab\nconstexpr bool cli_is_space(char c) noexcept {\n  return c == ' ' || c == '\\t' || c == '\\n' || c == '\\r' || c == '\\f' ||\n      c == '\\v';\n}\n\n/// Parse arguments from a file content string\n/// Handles whitespace separation, quotes (single and double), escaping, and\n/// comments\n///\n/// Quoting rules (simplified POSIX-like):\n/// - Single quotes: completely literal, no escape processing\n/// - Double quotes: process \\n \\t \\r \\\\ \\\" escapes\n/// - Unquoted: same escape processing as double quotes\n/// - Unclosed quotes return an error in the result\n///\n/// @ handling:\n/// - @ is meaningful only when it's the first character AND outside quotes\n/// - @@ escapes to @ only when the first two chars are @@ AND outside quotes\ncli_parse_result cli_parse_args_from_content(std::string_view content) {\n  using error_t = cli_apply_args_files_receiver::term_error;\n\n  cli_parse_result parse_result;\n  std::string current;\n  bool in_single_quotes = false;\n  bool in_double_quotes = false;\n  bool escaped = false;\n  bool in_comment = false;\n  size_t token_start = 0;\n  bool in_token = false;\n\n  // Track @ prefix state for the current token\n  // These are set when we see @ as the first char(s) outside quotes\n  bool first_char_is_at = false;\n  bool second_char_is_at = false;\n  size_t chars_in_token = 0; // count of chars added to current token\n\n  // Track where quotes started for error messages\n  size_t quote_start_line = 0;\n  size_t quote_start_col = 0;\n  size_t quote_start_offset = 0;\n\n  // Track line and column (1-based)\n  size_t line = 1;\n  size_t col = 1;\n  size_t token_start_line = 0;\n  size_t token_start_col = 0;\n  size_t token_end_line = 0;\n  size_t token_end_col = 0;\n\n  // Helper to finalize and push a token\n  auto push_token = [&](size_t end_pos) {\n    if (!current.empty()) {\n      size_t length = end_pos - token_start;\n      parse_result.args.push_back(\n          {std::move(current),\n           token_start,\n           length,\n           token_start_line,\n           token_start_col,\n           token_end_line,\n           token_end_col,\n           first_char_is_at,\n           first_char_is_at && second_char_is_at});\n      current.clear();\n    }\n    in_token = false;\n    first_char_is_at = false;\n    second_char_is_at = false;\n    chars_in_token = 0;\n  };\n\n  for (size_t i = 0; i < content.size(); ++i) {\n    char c = content[i];\n\n    if (in_comment) {\n      if (c == '\\n') {\n        in_comment = false;\n        line++;\n        col = 1;\n      } else {\n        col++;\n      }\n      continue;\n    }\n\n    // Inside single quotes: everything is literal except closing quote\n    if (in_single_quotes) {\n      if (c == '\\'') {\n        in_single_quotes = false;\n        token_end_line = line;\n        token_end_col = col;\n        col++;\n        continue;\n      }\n      // All characters are literal inside single quotes\n      current += c;\n      chars_in_token++;\n      token_end_line = line;\n      token_end_col = col;\n      if (c == '\\n') {\n        line++;\n        col = 1;\n      } else {\n        col++;\n      }\n      continue;\n    }\n\n    // Handle escape sequences (in double quotes or unquoted)\n    if (escaped) {\n      if (!in_token) {\n        token_start = i - 1;\n        token_start_line = line;\n        token_start_col = col - 1; // the backslash was the previous char\n        in_token = true;\n      }\n      token_end_line = line;\n      token_end_col = col;\n      switch (c) {\n        case 'n':\n          current += '\\n';\n          chars_in_token++;\n          break;\n        case 't':\n          current += '\\t';\n          chars_in_token++;\n          break;\n        case 'r':\n          current += '\\r';\n          chars_in_token++;\n          break;\n        case '\\\\':\n          current += '\\\\';\n          chars_in_token++;\n          break;\n        case '\"':\n          current += '\"';\n          chars_in_token++;\n          break;\n        case '#':\n          current += '#';\n          chars_in_token++;\n          break;\n        default:\n          // Unknown escape sequence: return error\n          parse_result.error = cli_parse_error{\n              error_t::unrecognized_escape_sequence,\n              token_start,\n              token_start_line,\n              token_start_col,\n              i - 1, // offset of the backslash\n              line,\n              col - 1}; // column of the backslash\n          return parse_result;\n      }\n      escaped = false;\n      col++;\n      continue;\n    }\n\n    if (c == '\\\\' && !in_single_quotes) {\n      if (!in_token) {\n        token_start = i;\n        token_start_line = line;\n        token_start_col = col;\n        in_token = true;\n      }\n      token_end_line = line;\n      token_end_col = col;\n      escaped = true;\n      col++;\n      continue;\n    }\n\n    if (c == '#' && !in_double_quotes) {\n      push_token(i);\n      in_comment = true;\n      col++;\n      continue;\n    }\n\n    if (c == '\\'' && !in_double_quotes) {\n      if (!in_token) {\n        token_start = i;\n        token_start_line = line;\n        token_start_col = col;\n        in_token = true;\n      }\n      token_end_line = line;\n      token_end_col = col;\n      in_single_quotes = true;\n      quote_start_line = line;\n      quote_start_col = col;\n      quote_start_offset = i;\n      col++;\n      continue;\n    }\n\n    if (c == '\"') {\n      if (!in_token) {\n        token_start = i;\n        token_start_line = line;\n        token_start_col = col;\n        in_token = true;\n      }\n      token_end_line = line;\n      token_end_col = col;\n      if (in_double_quotes) {\n        in_double_quotes = false;\n      } else {\n        in_double_quotes = true;\n        quote_start_line = line;\n        quote_start_col = col;\n        quote_start_offset = i;\n      }\n      col++;\n      continue;\n    }\n\n    if (cli_is_space(c) && !in_double_quotes) {\n      push_token(i);\n      if (c == '\\n') {\n        line++;\n        col = 1;\n      } else {\n        col++;\n      }\n      continue;\n    }\n\n    if (!in_token) {\n      token_start = i;\n      token_start_line = line;\n      token_start_col = col;\n      in_token = true;\n    }\n\n    // Only update end position if this is not trailing whitespace\n    if (!cli_is_space(c)) {\n      token_end_line = line;\n      token_end_col = col;\n    }\n\n    // Track @ prefix: only meaningful when outside quotes\n    if (!in_double_quotes && !in_single_quotes) {\n      if (chars_in_token == 0 && c == '@') {\n        first_char_is_at = true;\n      } else if (chars_in_token == 1 && first_char_is_at && c == '@') {\n        second_char_is_at = true;\n      }\n    }\n\n    current += c;\n    chars_in_token++;\n\n    // Update line/col tracking even when inside quotes\n    if (c == '\\n') {\n      line++;\n      col = 1;\n    } else {\n      col++;\n    }\n  }\n\n  // Check for unclosed quotes - return error instead of throwing\n  // clang-format off\n  auto error =\n      in_single_quotes ? error_t::unclosed_single_quote :\n      in_double_quotes ? error_t::unclosed_double_quote :\n      error_t::success;\n  // clang-format on\n\n  if (error != error_t::success) {\n    parse_result.error = cli_parse_error{\n        error,\n        token_start,\n        token_start_line,\n        token_start_col,\n        quote_start_offset,\n        quote_start_line,\n        quote_start_col};\n    return parse_result;\n  }\n\n  if (!current.empty()) {\n    // Strip trailing whitespace from the last token\n    // This handles cases where the file ends with whitespace inside quotes\n    // or simply trailing whitespace at the end\n    while (!current.empty()) {\n      char c = current.back();\n      if (cli_is_space(c)) {\n        current.pop_back();\n      } else {\n        break;\n      }\n    }\n\n    if (!current.empty()) {\n      size_t length = content.size() - token_start;\n      // Find the actual end of the token (excluding trailing whitespace)\n      for (size_t i = content.size(); i > token_start; --i) {\n        char c = content[i - 1];\n        if (!cli_is_space(c)) {\n          length = i - token_start;\n          break;\n        }\n      }\n\n      parse_result.args.push_back(\n          {std::move(current),\n           token_start,\n           length,\n           token_start_line,\n           token_start_col,\n           token_end_line,\n           token_end_col,\n           first_char_is_at,\n           first_char_is_at && second_char_is_at});\n    }\n  }\n\n  return parse_result;\n}\n\n/// Read file and return content, or error code if file cannot be read\nstd::pair<std::string, std::error_code> cli_read_file(\n    std::filesystem::path const& filename) {\n  std::string content;\n  bool success = folly::readFile(filename.string().c_str(), content);\n  if (!success) {\n    return {\"\", std::error_code(errno, std::generic_category())};\n  }\n  return {content, {}};\n}\n\n/// Abstraction over cli_parsed_arg vs raw string for unified processing\nstruct cli_arg_item {\n  std::string_view value;\n  bool starts_with_at;\n  bool starts_with_double_at;\n};\n\n/// Set of canonical paths for files currently being processed (for cycle\n/// detection)\nusing cli_active_files_set = std::set<std::filesystem::path>;\n\n/// Forward declaration for mutual recursion\nvoid cli_process_file_args(\n    cli_apply_args_files_receiver& receiver,\n    std::filesystem::path const& current_dir,\n    std::vector<cli_parsed_arg> const& file_args,\n    cli_active_files_set& active_files,\n    size_t depth,\n    size_t max_depth);\n\n/// Process a single arg item (handles @file references, @@ escapes, and regular\n/// terms). Returns control::go to continue processing, control::stop to halt.\ncli_apply_args_files_receiver::control cli_process_single_arg(\n    cli_apply_args_files_receiver& receiver,\n    std::filesystem::path const& current_dir,\n    cli_arg_item const& item,\n    cli_apply_args_files_receiver::location const& loc,\n    cli_active_files_set& active_files,\n    size_t depth,\n    size_t max_depth) {\n  // Check if this term starts with @ (args-file reference)\n  if (item.starts_with_at) {\n    // Check for @@ escape: leading @@ means regular term with one @ stripped\n    if (item.starts_with_double_at) {\n      std::string unescaped{item.value.substr(1)};\n      return receiver.on_term(std::move(unescaped), loc);\n    }\n\n    std::string filename{item.value.substr(1)};\n\n    // First, check if receiver wants to process this args-file\n    auto found_result = receiver.on_file_found(filename, loc);\n\n    switch (found_result) {\n      case cli_apply_args_files_receiver::found::stop:\n        return cli_apply_args_files_receiver::control::stop;\n      case cli_apply_args_files_receiver::found::skip:\n        return cli_apply_args_files_receiver::control::pass;\n      case cli_apply_args_files_receiver::found::dive:\n        if (depth >= max_depth) {\n          return receiver.on_file_error(\n              std::move(filename),\n              loc,\n              make_error_code(cli_apply_args_files_errc::max_depth_exceeded));\n        }\n        break; // Process this args-file normally\n      default:\n        break;\n    }\n\n    // Resolve relative paths against current_dir\n    std::filesystem::path full_path = filename;\n    if (full_path.is_relative()) {\n      full_path = current_dir / filename;\n    }\n\n    // Check for cycle using canonical path\n    std::error_code canonical_ec;\n    auto canonical_path = std::filesystem::canonical(full_path, canonical_ec);\n    if (!canonical_ec) {\n      // Successfully resolved canonical path; check for cycle\n      if (active_files.count(canonical_path) > 0) {\n        auto cycle_result = receiver.on_file_cycle(\n            std::move(filename), loc, std::move(canonical_path));\n        switch (cycle_result) {\n          case cli_apply_args_files_receiver::cycle::stop:\n            return cli_apply_args_files_receiver::control::stop;\n          case cli_apply_args_files_receiver::cycle::skip:\n          default:\n            return cli_apply_args_files_receiver::control::pass;\n        }\n      }\n    }\n    // If canonical() failed, the file likely doesn't exist; let cli_read_file\n    // handle the error\n\n    auto result = cli_read_file(full_path);\n    if (result.second) {\n      return receiver.on_file_error(std::move(filename), loc, result.second);\n    }\n    std::string_view content = result.first;\n\n    // Track this file as active (use canonical path if available, else\n    // full_path)\n    std::filesystem::path const& tracking_path =\n        canonical_ec ? full_path : canonical_path;\n    active_files.insert(tracking_path);\n\n    receiver.on_file_enter(std::move(filename), loc);\n\n    // Parse the file content\n    auto parse_result = cli_parse_args_from_content(content);\n\n    // Process successfully parsed args first\n    if (!parse_result.args.empty()) {\n      cli_process_file_args(\n          receiver,\n          current_dir,\n          parse_result.args,\n          active_files,\n          depth + 1,\n          max_depth);\n    }\n\n    // If there was a parse error, report it\n    if (parse_result.error) {\n      auto& err = *parse_result.error;\n      // Term location: where the problematic term begins\n      cli_apply_args_files_receiver::location term_loc{\n          parse_result.args.size(), // idx is the next arg index\n          err.term_offset,\n          content.size() - err.term_offset,\n          {err.term_begin_line, err.term_begin_col},\n          {0, 0} // end location not meaningful for error spanning to EOF\n      };\n      // Error location: where the error begins within the term\n      cli_apply_args_files_receiver::location error_loc{\n          parse_result.args.size(),\n          err.error_offset,\n          content.size() - err.error_offset,\n          {err.error_begin_line, err.error_begin_col},\n          {0, 0}};\n      receiver.on_term_error(err.code, term_loc, error_loc);\n    }\n\n    receiver.on_file_leave();\n    active_files.erase(tracking_path);\n    return cli_apply_args_files_receiver::control::pass;\n  }\n\n  // Regular term (no @ prefix)\n  return receiver.on_term(std::string{item.value}, loc);\n}\n\n/// Helper to recursively process args from files\nvoid cli_process_file_args(\n    cli_apply_args_files_receiver& receiver,\n    std::filesystem::path const& current_dir,\n    std::vector<cli_parsed_arg> const& file_args,\n    cli_active_files_set& active_files,\n    size_t depth,\n    size_t max_depth) {\n  for (size_t i = 0; i < file_args.size(); ++i) {\n    cli_parsed_arg const& parsed = file_args[i];\n    cli_apply_args_files_receiver::location loc{\n        i,\n        parsed.start_offset,\n        parsed.length,\n        {parsed.begin_line, parsed.begin_col},\n        {parsed.end_line, parsed.end_col}};\n\n    cli_arg_item item{\n        parsed.value,\n        parsed.starts_with_at_outside_quotes,\n        parsed.starts_with_double_at_outside_quotes};\n\n    auto control_result = cli_process_single_arg(\n        receiver, current_dir, item, loc, active_files, depth, max_depth);\n    if (control_result == cli_apply_args_files_receiver::control::stop) {\n      return;\n    }\n  }\n}\n\n} // namespace\n\nvoid cli_apply_args_files(\n    cli_apply_args_files_receiver& receiver,\n    std::filesystem::path const& current_dir,\n    span<std::string const> args,\n    cli_apply_args_files_options const& options) {\n  cli_active_files_set active_files;\n  for (size_t i = 0; i < args.size(); ++i) {\n    std::string_view arg = args[i];\n    cli_apply_args_files_receiver::location loc{i, 0, 0, {}, {}};\n\n    bool starts_with_at = arg.size() > 1 && arg[0] == '@';\n    bool starts_with_double_at = starts_with_at && arg[1] == '@';\n\n    cli_arg_item item{arg, starts_with_at, starts_with_double_at};\n\n    auto control_result = cli_process_single_arg(\n        receiver, current_dir, item, loc, active_files, 0, options.max_depth);\n    if (control_result == cli_apply_args_files_receiver::control::stop) {\n      return;\n    }\n  }\n}\n\nnamespace {\n\n/// Receiver that collects args into a vector and throws on any error.\nclass cli_throwing_receiver : public cli_apply_args_files_receiver {\n public:\n  explicit cli_throwing_receiver(std::vector<std::string>& result)\n      : result_(result) {}\n\n  control on_term(std::string arg, location /*loc*/) override {\n    result_.push_back(std::move(arg));\n    return control::pass;\n  }\n\n  static char const* on_term_error_msg(term_error error) {\n    switch (error) {\n      case term_error::success:\n        return nullptr;\n      case term_error::unclosed_single_quote:\n        return \"unclosed single quote\";\n      case term_error::unclosed_double_quote:\n        return \"unclosed double quote\";\n      case term_error::unrecognized_escape_sequence:\n        return \"unrecognized escape sequence\";\n      default:\n        return \"<unknown>\";\n    }\n  }\n\n  void on_term_error(\n      term_error error, location term_loc, location /*error_loc*/) override {\n    if (error == term_error::success) {\n      return; // Should not happen, but handle gracefully\n    }\n    throw cli_apply_args_files_error(\n        fmt::format(\n            \"error parsing args file '{}' at line {}, column {}: {}\",\n            current_file_,\n            term_loc.b.line,\n            term_loc.b.col,\n            on_term_error_msg(error)));\n  }\n\n  found on_file_found(cstring_view /*file*/, location /*loc*/) override {\n    return found::dive;\n  }\n\n  control on_file_error(\n      std::string file, location /*loc*/, std::error_code err) override {\n    throw cli_apply_args_files_error(\n        fmt::format(\"failed to read args file '{}': {}\", file, err.message()));\n  }\n\n  void on_file_enter(std::string file, location /*loc*/) override {\n    file_stack_.emplace_back(std::move(current_file_));\n    current_file_ = std::move(file);\n  }\n\n  void on_file_leave() override {\n    if (!file_stack_.empty()) {\n      current_file_ = std::move(file_stack_.back());\n      file_stack_.pop_back();\n    }\n  }\n\n  cycle on_file_cycle(\n      std::string file,\n      location /*loc*/,\n      std::filesystem::path /*canonical_path*/) override {\n    throw cli_apply_args_files_error(\n        fmt::format(\"cycle detected in args file '{}'\", file));\n  }\n\n private:\n  std::vector<std::string>& result_;\n  std::string current_file_;\n  std::vector<std::string> file_stack_;\n};\n\n} // namespace\n\nstd::vector<std::string> cli_apply_args_files(\n    std::filesystem::path const& current_dir, span<std::string const> args) {\n  std::vector<std::string> result;\n  cli_throwing_receiver receiver(result);\n  cli_apply_args_files(receiver, current_dir, args);\n  return result;\n}\n\nstd::vector<std::string> cli_apply_args_files(\n    std::filesystem::path const& current_dir,\n    int argc,\n    char const* const* argv) {\n  std::vector<std::string> args;\n  while (argc--) {\n    args.emplace_back(*argv++);\n  }\n  return cli_apply_args_files(current_dir, args);\n}\n\nstd::vector<std::string> cli_apply_args_files(\n    std::filesystem::path const& current_dir, int argc, char* const* argv) {\n  return cli_apply_args_files(\n      current_dir, argc, const_cast<char const* const*>(argv));\n}\n\nstd::vector<std::string> cli_apply_args_files(\n    int argc, char const* const* argv) {\n  auto current_dir = std::filesystem::current_path();\n  return cli_apply_args_files(current_dir, argc, argv);\n}\n\nstd::vector<std::string> cli_apply_args_files(int argc, char* const* argv) {\n  return cli_apply_args_files(argc, const_cast<char const* const*>(argv));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/Args.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <filesystem>\n#include <stdexcept>\n#include <string>\n#include <system_error>\n#include <vector>\n\n#include <folly/container/span.h>\n#include <folly/lang/cstring_view.h>\n\nnamespace folly {\n\nstd::vector<char const*> cli_args_strings_to_c_strings(\n    span<std::string const> args);\n\nclass cli_apply_args_files_receiver {\n public:\n  enum class control {\n    stop, // stop processing at this level, and recurse up\n    pass, // continue processing at this level\n  };\n\n  enum class found {\n    stop, // stop processing at this level, and recurse up\n    skip, // skip this arg\n    dive, // expand this arg like an args-file\n  };\n\n  enum class cycle {\n    stop, // stop processing at this level, and recurse up\n    skip, // skip this arg and continue processing\n  };\n\n  enum class term_error {\n    success,\n    unclosed_single_quote,\n    unclosed_double_quote,\n    unrecognized_escape_sequence,\n  };\n\n  struct human_location {\n    /// 1-base line number of this entry in the containing args-file (or zero)\n    size_t line = 0;\n\n    /// 1-base column number of this entry in the containing args-file (or zero)\n    size_t col = 0;\n\n    friend bool operator==(\n        human_location const& lhs, human_location const& rhs) noexcept {\n      return lhs.line == rhs.line && lhs.col == rhs.col;\n    }\n  };\n\n  struct location {\n    /// 0-base logical index of this entry in the arg-list or args-file\n    size_t idx = 0;\n\n    /// 0-base byte offset of this entry in the containing args-file (or zero)\n    /// if a beginning loc, offset of the beginning char\n    /// if an end loc, offset of the char after the end\n    size_t off = 0;\n\n    /// length of this entry in the containing args-file (or zero)\n    size_t len = 0;\n\n    /// line and col of the beginning char\n    human_location b;\n\n    /// line and col of the ending char (not past the end!)\n    human_location e;\n\n    friend bool operator==(location const& lhs, location const& rhs) noexcept {\n      return lhs.idx == rhs.idx && lhs.off == rhs.off && lhs.len == rhs.len &&\n          lhs.b == rhs.b && lhs.e == rhs.e;\n    }\n  };\n\n  virtual ~cli_apply_args_files_receiver() = default;\n\n  /// Called when an argument is just a string.\n  /// Is passed ownership of the string.\n  virtual control on_term(std::string arg, location loc) = 0;\n\n  /// Called when an argument fails to parse (e.g., unclosed quotes).\n  /// term_loc is the location of the term containing the error.\n  /// error_loc is the location where the error begins within that term.\n  /// Parsing of the current file stops, but parent files continue.\n  virtual void on_term_error(\n      term_error error, location term_loc, location error_loc) = 0;\n\n  /// Called when an argument is an args-file. Returns whether to expand it.\n  virtual found on_file_found(cstring_view file, location loc) = 0;\n\n  /// Called when an argument is an args-file, but the file could not be read.\n  /// Is passed ownership of the filename.\n  virtual control on_file_error(\n      std::string file, location loc, std::error_code err) = 0;\n\n  /// Called when an argument is an args-file, the file was read successfully,\n  /// and control recurses into the args-file.\n  /// Is passed ownership of the filename.\n  virtual void on_file_enter(std::string file, location loc) = 0;\n\n  /// Called when control recurses out of an args-file.\n  /// Calls to on_file_enter and on_file_leave are balanced in pairs.\n  virtual void on_file_leave() = 0;\n\n  /// Called when an argument is an args-file that would create a cycle\n  /// (directly or indirectly). The file parameter is the original\n  /// filename from the argument, and canonical_path is the resolved path that\n  /// was detected as part of a cycle.\n  virtual cycle on_file_cycle(\n      std::string file, location loc, std::filesystem::path canonical_path) = 0;\n};\n\n/// Exception thrown by the simple cli_apply_args_files overload.\nclass cli_apply_args_files_error : public std::runtime_error {\n public:\n  using std::runtime_error::runtime_error;\n};\n\nstruct cli_apply_args_files_options {\n  size_t max_depth = 64;\n};\n\nenum class cli_apply_args_files_errc : int {\n  max_depth_exceeded = 1,\n};\n\nstd::error_code make_error_code(cli_apply_args_files_errc errc);\n\n} // namespace folly\n\nnamespace std {\ntemplate <>\nstruct is_error_code_enum<folly::cli_apply_args_files_errc> : true_type {};\n} // namespace std\n\nnamespace folly {\n\n/// Applies args-file expansion to a list of arguments.\n/// Uses the receiver interface for full control over processing.\nvoid cli_apply_args_files(\n    cli_apply_args_files_receiver& receiver,\n    std::filesystem::path const& current_dir,\n    span<std::string const> args,\n    cli_apply_args_files_options const& options = {});\n\n/// Applies args-file expansion to a list of arguments.\n/// Returns the expanded list of arguments.\n/// Throws cli_apply_args_files_error on any error (file not found, parse\n/// error).\nstd::vector<std::string> cli_apply_args_files(\n    std::filesystem::path const& current_dir, span<std::string const> args);\n\nstd::vector<std::string> cli_apply_args_files(\n    std::filesystem::path const& current_dir,\n    int argc,\n    char const* const* argv);\n\nstd::vector<std::string> cli_apply_args_files(\n    std::filesystem::path const& current_dir, int argc, char* const* argv);\n\nstd::vector<std::string> cli_apply_args_files(\n    int argc, char const* const* argv);\n\nstd::vector<std::string> cli_apply_args_files(int argc, char* const* argv);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\n    \"@fbsource//tools/build_defs:platform_defs.bzl\",\n    \"ANDROID\",\n    \"APPLE\",\n    \"CXX\",\n    \"FBCODE\",\n    \"IOS\",\n    \"MACOSX\",\n    \"WINDOWS\",\n)\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat build rules\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"program_options\",\n    srcs = [\n        \"NestedCommandLineApp.cpp\",\n        \"ProgramOptions.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    fbandroid_deps = [\n        \"//third-party/gflags:gflags\",\n    ],\n    platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS),\n    raw_headers = [\n        \"NestedCommandLineApp.h\",\n        \"ProgramOptions.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/io:fs_util\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_program_options\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:file_util\",\n        \"//xplat/folly:format\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:string\",\n    ],\n)\n\n# fbcode build rules\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"args\",\n    srcs = [\n        \"Args.cpp\",\n    ],\n    headers = [\n        \"Args.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:file_util\",\n    ],\n    exported_deps = [\n        \"//folly/container:span\",\n        \"//folly/lang:cstring_view\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"program_options\",\n    srcs = [\n        \"NestedCommandLineApp.cpp\",\n        \"ProgramOptions.cpp\",\n    ],\n    headers = [\n        \"NestedCommandLineApp.h\",\n        \"ProgramOptions.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:conv\",\n        \"//folly:file_util\",\n        \"//folly:format\",\n        \"//folly:portability\",\n        \"//folly/io:fs_util\",\n    ],\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:optional\",\n        \"//folly:string\",\n        \"//folly/portability:gflags\",\n    ],\n    external_deps = [\n        \"boost\",\n        \"gflags\",\n    ],\n    exported_external_deps = [\n        (\"boost\", None, \"boost_program_options\"),\n    ],\n)\n"
  },
  {
    "path": "folly/cli/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\n# cli targets require gflags\nif (LIBGFLAGS_FOUND OR gflags_FOUND)\n\nfolly_add_library(\n  NAME args\n  SRCS\n    Args.cpp\n  HEADERS\n    Args.h\n  DEPS\n    folly_file_util\n  EXPORTED_DEPS\n    folly_container_span\n    folly_lang_cstring_view\n)\n\nfolly_add_library(\n  NAME program_options\n  SRCS\n    NestedCommandLineApp.cpp\n    ProgramOptions.cpp\n  HEADERS\n    NestedCommandLineApp.h\n    ProgramOptions.h\n  DEPS\n    folly_conv\n    folly_file_util\n    folly_format\n    folly_io_fs_util\n    folly_portability\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_optional\n    folly_portability_gflags\n    folly_string\n  EXTERNAL_DEPS\n    ${GFLAGS_LIBRARIES}\n    Boost::headers\n    Boost::program_options\n)\n\nendif() # LIBGFLAGS_FOUND OR gflags_FOUND\n"
  },
  {
    "path": "folly/cli/NestedCommandLineApp.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/cli/NestedCommandLineApp.h>\n\n#include <iostream>\n\n#include <fmt/format.h>\n#include <glog/logging.h>\n\n#include <folly/FileUtil.h>\n#include <folly/Format.h>\n#include <folly/io/FsUtil.h>\n\nnamespace po = ::boost::program_options;\n\nnamespace folly {\n\nnamespace {\n\n// Guess the program name as basename(executable)\nstd::string guessProgramName() {\n  try {\n    return fs::executable_path().filename().string();\n  } catch (const std::exception&) {\n    return \"UNKNOWN\";\n  }\n}\n\n} // namespace\n\nProgramExit::ProgramExit(int status, const std::string& msg)\n    : std::runtime_error(msg), status_(status) {\n  // Message is only allowed for non-zero exit status\n  CHECK(status_ != 0 || msg.empty());\n}\n\nNestedCommandLineApp::NestedCommandLineApp(\n    std::string programName,\n    std::string version,\n    std::string programHeading,\n    std::string programHelpFooter,\n    InitFunction initFunction)\n    : programName_(std::move(programName)),\n      programHeading_(std::move(programHeading)),\n      programHelpFooter_(std::move(programHelpFooter)),\n      version_(std::move(version)),\n      globalOptions_(\"Global options\"),\n      optionStyle_(po::command_line_style::default_style) {\n  addCommand(\n      kHelpCommand.str(),\n      \"[command]\",\n      \"Display help (globally or for a given command)\",\n      \"Displays help (globally or for a given command).\",\n      [this](\n          const po::variables_map& vm, const std::vector<std::string>& args) {\n        displayHelp(vm, args);\n      });\n  builtinCommands_.insert(kHelpCommand);\n  addAlias(kShortHelpCommand.str(), kHelpCommand.str());\n\n  addCommand(\n      kVersionCommand.str(),\n      \"[command]\",\n      \"Display version information\",\n      \"Displays version information.\",\n      [this](const po::variables_map&, const std::vector<std::string>&) {\n        displayVersion();\n      });\n  builtinCommands_.insert(kVersionCommand);\n\n  globalOptions_.add_options()(\n      (kHelpCommand.str() + \",\" + kShortHelpCommand.str()).c_str(),\n      \"Display help (globally or for a given command)\")(\n      kVersionCommand.str().c_str(), \"Display version information\");\n\n  if (initFunction) {\n    callbackFunctions_.emplace_back(std::move(initFunction));\n  }\n}\n\npo::options_description& NestedCommandLineApp::addCommand(\n    std::string name,\n    std::string argStr,\n    std::string shortHelp,\n    std::string fullHelp,\n    Command command,\n    folly::Optional<po::positional_options_description> positionalOptions) {\n  CommandInfo info{\n      std::move(argStr),\n      std::move(shortHelp),\n      std::move(fullHelp),\n      std::move(command),\n      po::options_description(fmt::format(\"Options for `{}'\", name)),\n      std::move(positionalOptions)};\n\n  auto p = commands_.emplace(std::move(name), std::move(info));\n  CHECK(p.second) << \"Command already exists\";\n\n  return p.first->second.options;\n}\n\nvoid NestedCommandLineApp::addAlias(std::string newName, std::string oldName) {\n  CHECK(aliases_.count(oldName) || commands_.count(oldName))\n      << \"Alias old name does not exist\";\n  CHECK(!aliases_.count(newName) && !commands_.count(newName))\n      << \"Alias new name already exists\";\n  aliases_.emplace(std::move(newName), std::move(oldName));\n}\n\nvoid NestedCommandLineApp::setOptionStyle(\n    boost::program_options::command_line_style::style_t style) {\n  optionStyle_ = style;\n}\n\nvoid NestedCommandLineApp::displayHelp(\n    const po::variables_map& /* globalOptions */,\n    const std::vector<std::string>& args) const {\n  if (args.empty()) {\n    // General help\n    printf(\n        \"%s\\nUsage: %s [global_options...] <command> [command_options...] \"\n        \"[command_args...]\\n\\n\",\n        programHeading_.c_str(),\n        programName_.c_str());\n    std::cout << globalOptions_;\n    printf(\"\\nAvailable commands:\\n\");\n\n    size_t maxLen = 0;\n    for (auto& p : commands_) {\n      maxLen = std::max(maxLen, p.first.size());\n    }\n    for (auto& p : aliases_) {\n      maxLen = std::max(maxLen, p.first.size());\n    }\n\n    for (auto& p : commands_) {\n      printf(\n          \"  %-*s    %s\\n\",\n          int(maxLen),\n          p.first.c_str(),\n          p.second.shortHelp.c_str());\n    }\n\n    if (!aliases_.empty()) {\n      printf(\"\\nAvailable aliases:\\n\");\n      for (auto& p : aliases_) {\n        printf(\n            \"  %-*s => %s\\n\",\n            int(maxLen),\n            p.first.c_str(),\n            resolveAlias(p.second).c_str());\n      }\n    }\n    std::cout << \"\\n\" << programHelpFooter_ << \"\\n\";\n  } else {\n    // Help for a given command\n    auto& p = findCommand(args.front());\n    if (p.first != args.front()) {\n      printf(\n          \"`%s' is an alias for `%s'; showing help for `%s'\\n\",\n          args.front().c_str(),\n          p.first.c_str(),\n          p.first.c_str());\n    }\n    auto& info = p.second;\n\n    printf(\n        \"Usage: %s [global_options...] %s%s%s%s\\n\\n\",\n        programName_.c_str(),\n        p.first.c_str(),\n        info.options.options().empty() ? \"\" : \" [command_options...]\",\n        info.argStr.empty() ? \"\" : \" \",\n        info.argStr.c_str());\n\n    printf(\"%s\\n\", info.fullHelp.c_str());\n\n    std::cout << globalOptions_;\n\n    if (!info.options.options().empty()) {\n      printf(\"\\n\");\n      std::cout << info.options;\n    }\n  }\n}\n\nvoid NestedCommandLineApp::displayVersion() const {\n  printf(\"%s %s\\n\", programName_.c_str(), version_.c_str());\n}\n\nconst std::string& NestedCommandLineApp::resolveAlias(\n    const std::string& name) const {\n  auto dest = &name;\n  for (;;) {\n    auto pos = aliases_.find(*dest);\n    if (pos == aliases_.end()) {\n      break;\n    }\n    dest = &pos->second;\n  }\n  return *dest;\n}\n\nauto NestedCommandLineApp::findCommand(const std::string& name) const\n    -> const std::pair<const std::string, CommandInfo>& {\n  auto pos = commands_.find(resolveAlias(name));\n  if (pos == commands_.end()) {\n    throw ProgramExit(\n        1,\n        fmt::format(\n            \"Command '{}' not found. Run '{} {}' for help.\",\n            name,\n            programName_,\n            kHelpCommand));\n  }\n  return *pos;\n}\n\nint NestedCommandLineApp::run(int argc, const char* const argv[]) {\n  if (programName_.empty()) {\n    programName_ = fs::path(argv[0]).filename().string();\n  }\n  return run(std::vector<std::string>(argv + 1, argv + argc));\n}\n\nint NestedCommandLineApp::run(const std::vector<std::string>& args) {\n  int status;\n  try {\n    doRun(args);\n    status = 0;\n  } catch (const ProgramExit& ex) {\n    if (ex.what()[0]) { // if not empty\n      fprintf(stderr, \"%s\\n\", ex.what());\n    }\n    status = ex.status();\n  } catch (const po::error& ex) {\n    fprintf(\n        stderr,\n        \"%s\",\n        fmt::format(\n            \"{}. Run '{} help' for {}.\\n\",\n            ex.what(),\n            programName_,\n            kHelpCommand)\n            .c_str());\n    status = 1;\n  }\n\n  if (status == 0) {\n    if (ferror(stdout)) {\n      fprintf(stderr, \"error on standard output\\n\");\n      status = 1;\n    } else if (fflush(stdout)) {\n      fprintf(\n          stderr,\n          \"standard output flush failed: %s\\n\",\n          errnoStr(errno).c_str());\n      status = 1;\n    }\n  }\n\n  return status;\n}\n\nvoid NestedCommandLineApp::doRun(const std::vector<std::string>& args) {\n  if (programName_.empty()) {\n    programName_ = guessProgramName();\n  }\n\n  bool not_clean = false;\n  std::vector<std::string> cleanArgs;\n  std::vector<std::string> endArgs;\n\n  for (auto& na : args) {\n    if (not_clean) {\n      endArgs.push_back(na);\n    } else if (na == \"--\") {\n      not_clean = true;\n    } else {\n      cleanArgs.push_back(na);\n    }\n  }\n\n  auto parsed = parseNestedCommandLine(cleanArgs, globalOptions_, optionStyle_);\n  po::variables_map vm;\n  po::store(parsed.options, vm);\n  if (vm.count(kHelpCommand.str())) {\n    std::vector<std::string> helpArgs;\n    if (parsed.command) {\n      helpArgs.push_back(*parsed.command);\n    }\n    displayHelp(vm, helpArgs);\n    return;\n  }\n\n  if (vm.count(kVersionCommand.str())) {\n    displayVersion();\n    return;\n  }\n\n  if (!parsed.command) {\n    throw ProgramExit(\n        1,\n        fmt::format(\n            \"Command not specified. Run '{} {}' for help.\",\n            programName_,\n            kHelpCommand));\n  }\n\n  auto& p = findCommand(*parsed.command);\n  auto& cmd = p.first;\n  auto& info = p.second;\n\n  auto parser =\n      po::command_line_parser(parsed.rest)\n          .options(info.options)\n          .style(optionStyle_);\n  if (info.positionalOptions) {\n    parser = parser.positional(*info.positionalOptions);\n  }\n  try {\n    auto cmdOptions = parser.run();\n\n    po::store(cmdOptions, vm);\n    po::notify(vm);\n\n    // If positional arguments are specified they should get mapped to a named\n    // arg and don't need to be double collected\n    auto cmdArgs = po::collect_unrecognized(\n        cmdOptions.options,\n        info.positionalOptions\n            ? po::exclude_positional\n            : po::include_positional);\n\n    cmdArgs.insert(cmdArgs.end(), endArgs.begin(), endArgs.end());\n\n    for (const auto& callback : callbackFunctions_) {\n      callback(cmd, vm, cmdArgs);\n    }\n\n    info.command(vm, cmdArgs);\n  } catch (const po::required_option& ex) {\n    // The top run() function won't be able to capture the command name.\n    // We include both program name and command name in the error message for\n    // better UX.\n    throw ProgramExit(\n        1,\n        fmt::format(\n            \"Missing required option: '{}'. Run '{} {} --help' for help.\",\n            ex.get_option_name(),\n            programName_,\n            cmd));\n  }\n}\n\nbool NestedCommandLineApp::isBuiltinCommand(const std::string& name) const {\n  return builtinCommands_.count(name);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/NestedCommandLineApp.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <set>\n#include <stdexcept>\n\n#include <folly/CPortability.h>\n#include <folly/String.h>\n#include <folly/cli/ProgramOptions.h>\n\nnamespace folly {\n\n/**\n * Exception that commands may throw to force the program to exit cleanly\n * with a given exit code. NestedCommandLineApp::run() catches this and\n * makes run() print the given message on stderr (followed by a newline, unless\n * empty; the message is only allowed when exiting with a non-zero status), and\n * return the exit code. (Other exceptions will propagate out of run())\n */\nclass FOLLY_EXPORT ProgramExit : public std::runtime_error {\n public:\n  explicit ProgramExit(int status, const std::string& msg = std::string());\n  int status() const { return status_; }\n\n private:\n  int status_;\n};\n\n/**\n * App that uses a nested command line, of the form:\n *\n * program [--global_options...] command [--command_options...] command_args...\n *\n * Note: Global options (including GFlags, if added using addGFlags()) are\n * recognized anywhere in the command line, and are prefix matched with higher\n * priority than command options. For example, a global option named \"--foobar\"\n * would be matched over a command option named \"--foo\", even if you specify\n * \"--foo\" on the command line. You can disable prefix matching with:\n *\n * int style = boost::program_options::command_line_style::default_style;\n * style &= ~boost::program_options::command_line_style::allow_guessing;\n * app.setOptionStyle(\n *   static_cast<boost::program_options::command_line_style::style_t>(style));\n */\nclass NestedCommandLineApp {\n public:\n  typedef std::function<void(\n      const std::string& command,\n      const boost::program_options::variables_map& options,\n      const std::vector<std::string>& args)>\n      InitFunction;\n\n  typedef std::function<void(\n      const boost::program_options::variables_map& options,\n      const std::vector<std::string>&)>\n      Command;\n\n  struct CommandInfo {\n    std::string argStr;\n    std::string shortHelp;\n    std::string fullHelp;\n    Command command;\n    boost::program_options::options_description options;\n    folly::Optional<boost::program_options::positional_options_description>\n        positionalOptions;\n  };\n\n  static constexpr StringPiece const kHelpCommand = \"help\";\n  static constexpr StringPiece const kShortHelpCommand = \"h\";\n  static constexpr StringPiece const kVersionCommand = \"version\";\n  /**\n   * Initialize the app.\n   *\n   * If programName is not set, we try to guess (readlink(\"/proc/self/exe\")).\n   *\n   * version is the version string printed when given the --version flag.\n   *\n   * initFunction, if specified, is called after parsing the command line,\n   * right before executing the command.\n   */\n  explicit NestedCommandLineApp(\n      std::string programName = std::string(),\n      std::string version = std::string(),\n      std::string programHeading = std::string(),\n      std::string programHelpFooter = std::string(),\n      InitFunction initFunction = InitFunction());\n\n#if FOLLY_HAVE_LIBGFLAGS && __has_include(<gflags/gflags.h>)\n  /**\n   * Add GFlags to the list of supported options with the given style.\n   */\n  void addGFlags(ProgramOptionsStyle style = ProgramOptionsStyle::GNU) {\n    globalOptions_.add(getGFlags(style));\n  }\n#endif\n\n  /**\n   * Return the global options object, so you can add options.\n   */\n  boost::program_options::options_description& globalOptions() {\n    return globalOptions_;\n  }\n\n  /**\n   * Return the commands map, so you can see the registered commands and get\n   * access to their respective options descriptions.\n   */\n  const std::map<std::string, CommandInfo>& commands() const {\n    return commands_;\n  }\n\n  /**\n   * Return the aliases map, so you can see the registered aliases.\n   */\n  const std::map<std::string, std::string>& aliases() const { return aliases_; }\n\n  /**\n   * Add a command.\n   *\n   * name:  command name\n   * argStr: description of arguments in help strings\n   *   (<filename> <N>)\n   * shortHelp: one-line summary help string\n   * fullHelp: full help string\n   * command: function to run\n   *\n   * Returns a reference to the options_description object that you can\n   * use to add options for this command.\n   */\n  boost::program_options::options_description& addCommand(\n      std::string name,\n      std::string argStr,\n      std::string shortHelp,\n      std::string fullHelp,\n      Command command,\n      folly::Optional<boost::program_options::positional_options_description>\n          positionalOptions = folly::none);\n\n  /**\n   * Add an alias; running the command newName will have the same effect\n   * as running oldName.\n   */\n  void addAlias(std::string newName, std::string oldName);\n\n  /**\n   * Sets the style in which options will be accepted by the parser.\n   */\n  void setOptionStyle(\n      boost::program_options::command_line_style::style_t style);\n\n  /**\n   * Run the command and return; the return code is 0 on success or\n   * non-zero on error, so it is idiomatic to call this at the end of main():\n   * return app.run(argc, argv);\n   *\n   * On successful exit, run() will check for errors on stdout (and flush\n   * it) to help command-line applications that need to write to stdout\n   * (failing to write to stdout is an error). If there is an error on stdout,\n   * we'll print a helpful message on stderr and return an error status (1).\n   */\n  int run(int argc, const char* const argv[]);\n  int run(const std::vector<std::string>& args);\n\n  /**\n   * Return true if name represent known built-in command (help, version)\n   */\n  bool isBuiltinCommand(const std::string& name) const;\n\n  /**\n   * Add a callback to be invoked after command-line arguments are parsed\n   */\n  void addCallback(InitFunction callback) {\n    callbackFunctions_.emplace_back(std::move(callback));\n  }\n\n private:\n  void doRun(const std::vector<std::string>& args);\n\n  const std::string& resolveAlias(const std::string& name) const;\n\n  const std::pair<const std::string, CommandInfo>& findCommand(\n      const std::string& name) const;\n\n  void displayHelp(\n      const boost::program_options::variables_map& options,\n      const std::vector<std::string>& args) const;\n\n  void displayVersion() const;\n\n  std::string programName_;\n  std::string programHeading_;\n  std::string programHelpFooter_;\n  std::string version_;\n  std::vector<InitFunction> callbackFunctions_;\n  boost::program_options::options_description globalOptions_;\n  boost::program_options::command_line_style::style_t optionStyle_;\n  std::map<std::string, CommandInfo> commands_;\n  std::map<std::string, std::string> aliases_;\n  std::set<folly::StringPiece> builtinCommands_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/ProgramOptions.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/cli/ProgramOptions.h>\n\n#include <unordered_map>\n#include <unordered_set>\n\n#include <boost/version.hpp>\n#include <glog/logging.h>\n\n#ifdef __ANDROID__\n#include <gflags/gflags.h>\n#endif\n\n#include <folly/Conv.h>\n#include <folly/Portability.h>\n#include <folly/portability/GFlags.h>\n\nnamespace po = ::boost::program_options;\n\nnamespace folly {\n\n#if FOLLY_HAVE_LIBGFLAGS && __has_include(<gflags/gflags.h>)\nnamespace {\n\n// Information about one GFlag. Handled via shared_ptr, as, in the case\n// of boolean flags, two boost::program_options options (--foo and --nofoo)\n// may share the same GFlag underneath.\n//\n// We're slightly abusing the boost::program_options interface; the first\n// time we (successfully) parse a value that matches this GFlag, we'll set\n// it and remember not to set it again; this prevents, for example, the\n// default value of --foo from overwriting the GFlag if --nofoo is set.\ntemplate <class T>\nclass GFlagInfo {\n public:\n  explicit GFlagInfo(folly::gflags::CommandLineFlagInfo info)\n      : info_(std::move(info)), isSet_(false) {}\n\n  void set(const T& value) {\n    if (isSet_) {\n      return;\n    }\n\n    auto strValue = folly::to<std::string>(value);\n    auto msg = folly::gflags::SetCommandLineOption(\n        info_.name.c_str(), strValue.c_str());\n    if (msg.empty()) {\n      throw po::invalid_option_value(strValue);\n    }\n    isSet_ = true;\n  }\n\n  T get() const {\n    std::string str;\n    CHECK(folly::gflags::GetCommandLineOption(info_.name.c_str(), &str));\n    return folly::to<T>(str);\n  }\n\n  const folly::gflags::CommandLineFlagInfo& info() const { return info_; }\n\n private:\n  folly::gflags::CommandLineFlagInfo info_;\n  bool isSet_;\n};\n\ntemplate <class T>\nclass GFlagValueSemanticBase : public po::value_semantic {\n public:\n  explicit GFlagValueSemanticBase(std::shared_ptr<GFlagInfo<T>> info)\n      : info_(std::move(info)) {}\n\n  std::string name() const override { return \"arg\"; }\n#if BOOST_VERSION >= 105900 && BOOST_VERSION <= 106400\n  bool adjacent_tokens_only() const override { return false; }\n#endif\n  bool is_composing() const override { return false; }\n  bool is_required() const override { return false; }\n  // We handle setting the GFlags from parse(), so notify() does nothing.\n  void notify(const boost::any& /* valueStore */) const override {}\n  bool apply_default(boost::any& valueStore) const override {\n    // We're using the *current* rather than *default* value here, and\n    // this is intentional; GFlags-using programs assign to FLAGS_foo\n    // before ParseCommandLineFlags() in order to change the default value,\n    // and we obey that.\n    auto val = info_->get();\n    this->transform(val);\n    valueStore = val;\n    return true;\n  }\n\n  void parse(\n      boost::any& valueStore,\n      const std::vector<std::string>& tokens,\n      bool /* utf8 */) const override;\n\n private:\n  virtual T parseValue(const std::vector<std::string>& tokens) const = 0;\n  virtual void transform(T& /* val */) const {}\n\n  mutable std::shared_ptr<GFlagInfo<T>> info_;\n};\n\ntemplate <class T>\nvoid GFlagValueSemanticBase<T>::parse(\n    boost::any& valueStore,\n    const std::vector<std::string>& tokens,\n    bool /* utf8 */) const {\n  T val;\n  try {\n    val = this->parseValue(tokens);\n    this->transform(val);\n  } catch (const std::exception&) {\n    throw po::invalid_option_value(\n        tokens.empty() ? std::string() : tokens.front());\n  }\n  this->info_->set(val);\n  valueStore = val;\n}\n\ntemplate <class T>\nclass GFlagValueSemantic : public GFlagValueSemanticBase<T> {\n public:\n  explicit GFlagValueSemantic(std::shared_ptr<GFlagInfo<T>> info)\n      : GFlagValueSemanticBase<T>(std::move(info)) {}\n\n  unsigned min_tokens() const override { return 1; }\n  unsigned max_tokens() const override { return 1; }\n\n  T parseValue(const std::vector<std::string>& tokens) const override {\n    DCHECK(tokens.size() == 1);\n    return folly::to<T>(tokens.front());\n  }\n};\n\nclass BoolGFlagValueSemantic : public GFlagValueSemanticBase<bool> {\n public:\n  explicit BoolGFlagValueSemantic(std::shared_ptr<GFlagInfo<bool>> info)\n      : GFlagValueSemanticBase<bool>(std::move(info)) {}\n\n  unsigned min_tokens() const override { return 0; }\n  unsigned max_tokens() const override { return 0; }\n\n  bool parseValue(const std::vector<std::string>& tokens) const override {\n    DCHECK(tokens.empty());\n    return true;\n  }\n};\n\nclass NegativeBoolGFlagValueSemantic : public BoolGFlagValueSemantic {\n public:\n  explicit NegativeBoolGFlagValueSemantic(std::shared_ptr<GFlagInfo<bool>> info)\n      : BoolGFlagValueSemantic(std::move(info)) {}\n\n private:\n  void transform(bool& val) const override { val = !val; }\n};\n\nconst std::string& getName(const std::string& name) {\n  static const std::unordered_map<std::string, std::string> gFlagOverrides{\n      // Allow -v in addition to --v\n      {\"v\", \"v,v\"},\n  };\n  auto pos = gFlagOverrides.find(name);\n  return pos != gFlagOverrides.end() ? pos->second : name;\n}\n\ntemplate <class T>\nvoid addGFlag(\n    folly::gflags::CommandLineFlagInfo&& flag,\n    po::options_description& desc,\n    ProgramOptionsStyle style) {\n  auto gflagInfo = std::make_shared<GFlagInfo<T>>(std::move(flag));\n  auto& info = gflagInfo->info();\n  auto name = getName(info.name);\n\n  switch (style) {\n    case ProgramOptionsStyle::GFLAGS:\n      break;\n    case ProgramOptionsStyle::GNU:\n      std::replace(name.begin(), name.end(), '_', '-');\n      break;\n  }\n  desc.add_options()(\n      name.c_str(),\n      new GFlagValueSemantic<T>(gflagInfo),\n      info.description.c_str());\n}\n\ntemplate <>\nvoid addGFlag<bool>(\n    folly::gflags::CommandLineFlagInfo&& flag,\n    po::options_description& desc,\n    ProgramOptionsStyle style) {\n  auto gflagInfo = std::make_shared<GFlagInfo<bool>>(std::move(flag));\n  auto& info = gflagInfo->info();\n  auto name = getName(info.name);\n  std::string negationPrefix;\n\n  switch (style) {\n    case ProgramOptionsStyle::GFLAGS:\n      negationPrefix = \"no\";\n      break;\n    case ProgramOptionsStyle::GNU:\n      std::replace(name.begin(), name.end(), '_', '-');\n      negationPrefix = \"no-\";\n      break;\n  }\n\n  // clang-format off\n  desc.add_options()\n    (name.c_str(),\n     new BoolGFlagValueSemantic(gflagInfo),\n     info.description.c_str())\n    ((negationPrefix + name).c_str(),\n     new NegativeBoolGFlagValueSemantic(gflagInfo),\n     folly::to<std::string>(\"(no) \", info.description).c_str());\n  // clang-format on\n}\n\nusing FlagAdder = void (*)(\n    folly::gflags::CommandLineFlagInfo&&,\n    po::options_description&,\n    ProgramOptionsStyle);\n\nconst std::unordered_map<std::string, FlagAdder> gFlagAdders = {\n#define X(NAME, TYPE) {NAME, addGFlag<TYPE>}\n    X(\"bool\", bool),\n    X(\"int32\", int32_t),\n    X(\"int64\", int64_t),\n    X(\"uint32\", uint32_t),\n    X(\"uint64\", uint64_t),\n    X(\"double\", double),\n    X(\"string\", std::string),\n#undef X\n};\n\n} // namespace\n\npo::options_description getGFlags(ProgramOptionsStyle style) {\n  static const std::unordered_set<std::string> gSkipFlags{\n      \"flagfile\",\n      \"fromenv\",\n      \"tryfromenv\",\n      \"undefok\",\n      \"help\",\n      \"helpful\",\n      \"helpshort\",\n      \"helpon\",\n      \"helpmatch\",\n      \"helppackage\",\n      \"helpxml\",\n      \"version\",\n      \"tab_completion_columns\",\n      \"tab_completion_word\",\n  };\n\n  po::options_description desc(\"GFlags\");\n\n  std::vector<folly::gflags::CommandLineFlagInfo> allFlags;\n  folly::gflags::GetAllFlags(&allFlags);\n\n  for (auto& f : allFlags) {\n    if (gSkipFlags.count(f.name)) {\n      continue;\n    }\n    auto pos = gFlagAdders.find(f.type);\n    CHECK(pos != gFlagAdders.end()) << \"Invalid flag type: \" << f.type;\n    (*pos->second)(std::move(f), desc, style);\n  }\n\n  return desc;\n}\n#endif\n\nnamespace {\n\nNestedCommandLineParseResult doParseNestedCommandLine(\n    po::command_line_parser&& parser,\n    const po::options_description& desc,\n    boost::program_options::command_line_style::style_t style) {\n  NestedCommandLineParseResult result;\n\n  result.options = parser.options(desc).style(style).allow_unregistered().run();\n\n  bool setCommand = true;\n  for (auto& opt : result.options.options) {\n    auto& tokens = opt.original_tokens;\n    auto tokensStart = tokens.begin();\n\n    if (setCommand && opt.position_key != -1) {\n      DCHECK(tokensStart != tokens.end());\n      result.command = *(tokensStart++);\n    }\n\n    if (opt.position_key != -1 || opt.unregistered) {\n      // If we see an unrecognized option before the first positional\n      // argument, assume we don't have a valid command name, because\n      // we don't know how to parse it otherwise.\n      //\n      // program --wtf foo bar\n      //\n      // Is \"foo\" an argument to \"--wtf\", or the command name?\n      setCommand = false;\n      result.rest.insert(result.rest.end(), tokensStart, tokens.end());\n    }\n  }\n\n  return result;\n}\n\n} // namespace\n\nNestedCommandLineParseResult parseNestedCommandLine(\n    int argc,\n    const char* const argv[],\n    const po::options_description& desc,\n    boost::program_options::command_line_style::style_t style) {\n  return doParseNestedCommandLine(\n      po::command_line_parser(argc, argv), desc, style);\n}\n\nNestedCommandLineParseResult parseNestedCommandLine(\n    const std::vector<std::string>& cmdline,\n    const po::options_description& desc,\n    boost::program_options::command_line_style::style_t style) {\n  return doParseNestedCommandLine(\n      po::command_line_parser(cmdline), desc, style);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/ProgramOptions.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <boost/program_options.hpp>\n\n#include <folly/Optional.h>\n#include <folly/portability/GFlags.h>\n\nnamespace folly {\n\n#if FOLLY_HAVE_LIBGFLAGS && __has_include(<gflags/gflags.h>)\nenum class ProgramOptionsStyle {\n  GFLAGS,\n  GNU,\n};\n\n// Add all GFlags to the given options_description.\n// Use this *instead of* gflags::ParseCommandLineFlags().\n//\n// in GFLAGS style, the flags are named as per gflags conventions:\n//   names_with_underscores\n//   boolean flags have a \"no\" prefix\n//\n// in GNU style, the flags are named as per GNU conventions:\n//   names-with-dashes\n//   boolean flags have a \"no-\" prefix\n//\n// Consider (for example) a boolean flag:\n//   DEFINE_bool(flying_pigs, false, \"...\");\n//\n// In GFLAGS style, the corresponding flags are named\n//   flying_pigs\n//   noflying_pigs\n//\n// In GNU style, the corresponding flags are named\n//   flying-pigs\n//   no-flying-pigs\n//\n// You may not pass arguments to boolean flags, so you must use the\n// \"no\" / \"no-\" prefix to set them to false; \"--flying_pigs false\"\n// and \"--flying_pigs=false\" are not allowed, to prevent ambiguity.\nboost::program_options::options_description getGFlags(\n    ProgramOptionsStyle style = ProgramOptionsStyle::GNU);\n#endif\n\n// Helper when parsing nested command lines:\n//\n// program [--common_options...] command [--command_options...] args\n//\n// The result has \"command\" set to the first positional argument, if any,\n// and \"rest\" set to the remaining options and arguments. Note that any\n// unrecognized flags must appear after the command name.\n//\n// You may pass \"rest\" to parseNestedCommandLine again, etc.\nstruct NestedCommandLineParseResult {\n  NestedCommandLineParseResult() {}\n\n  boost::program_options::parsed_options options{nullptr};\n\n  Optional<std::string> command;\n  std::vector<std::string> rest;\n};\n\nNestedCommandLineParseResult parseNestedCommandLine(\n    int argc,\n    const char* const argv[],\n    const boost::program_options::options_description& desc,\n    boost::program_options::command_line_style::style_t style =\n        boost::program_options::command_line_style::default_style);\n\nNestedCommandLineParseResult parseNestedCommandLine(\n    const std::vector<std::string>& cmdline,\n    const boost::program_options::options_description& desc,\n    boost::program_options::command_line_style::style_t style =\n        boost::program_options::command_line_style::default_style);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/test/ArgsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/cli/Args.h>\n\n#include <fstream>\n\n#include <folly/String.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/testing/TestUtil.h>\n\nusing namespace folly;\nusing testing::Contains;\nusing testing::ElementsAreArray;\n\nnamespace {\n\n// Test receiver that collects events for verification\n} // namespace\n\n// Test fixture class using folly::test::TemporaryDirectory\nclass ArgsTest : public ::testing::Test {\n protected:\n  using location = cli_apply_args_files_receiver::location;\n  using term_error = cli_apply_args_files_receiver::term_error;\n\n  struct Entry {\n    enum Type {\n      TERM,\n      TERM_ERROR,\n      FILE_FOUND,\n      FILE_ERROR,\n      FILE_ENTER,\n      FILE_LEAVE,\n      FILE_CYCLE\n    };\n    Type type{};\n    std::string value{};\n    term_error error_code{}; // For TERM_ERROR: the error code\n    location loc{};\n    location error_loc{}; // For TERM_ERROR: where the error begins\n    std::error_code err{};\n    size_t depth{};\n    std::filesystem::path canonical_path{}; // For FILE_CYCLE\n\n    static char const* type_to_s(Type t) noexcept {\n      constexpr char const* strings[] = {\n          \"term\",\n          \"term-error\",\n          \"file-found\",\n          \"file-error\",\n          \"file-enter\",\n          \"file-leave\",\n          \"file-cycle\",\n      };\n      return strings[t];\n    }\n\n    static char const* term_error_to_s(term_error e) noexcept {\n      switch (e) {\n        case term_error::success:\n          return \"success\";\n        case term_error::unclosed_single_quote:\n          return \"unclosed_single_quote\";\n        case term_error::unclosed_double_quote:\n          return \"unclosed_double_quote\";\n        case term_error::unrecognized_escape_sequence:\n          return \"unrecognized_escape_sequence\";\n        default:\n          return \"unknown\";\n      }\n    }\n\n    friend bool operator==(const Entry& lhs, const Entry& rhs) = default;\n\n    template <typename Char, typename Traits>\n    friend std::basic_ostream<Char, Traits>& operator<<(\n        std::basic_ostream<Char, Traits>& o, Entry const& e) {\n      o << \"entry{\" << \"type=\" << type_to_s(e.type) << \", \" << \"value=\\\"\"\n        << cEscape<std::string>(e.value) << \"\\\", \" << \"term_loc{\"\n        << \"idx=\" << e.loc.idx << \", \" << \"off=\" << e.loc.off << \", \"\n        << \"len=\" << e.loc.len << \", \" << \"b={line=\" << e.loc.b.line\n        << \", col=\" << e.loc.b.col << \"}, \" << \"e={line=\" << e.loc.e.line\n        << \", col=\" << e.loc.e.col << \"}\" << \"}\";\n      if (e.type == TERM_ERROR) {\n        o << \", error_code=\" << term_error_to_s(e.error_code);\n        o << \", error_loc{\" << \"idx=\" << e.error_loc.idx << \", \"\n          << \"off=\" << e.error_loc.off << \", \" << \"len=\" << e.error_loc.len\n          << \", \" << \"b={line=\" << e.error_loc.b.line\n          << \", col=\" << e.error_loc.b.col << \"}, \"\n          << \"e={line=\" << e.error_loc.e.line << \", col=\" << e.error_loc.e.col\n          << \"}\" << \"}\";\n      }\n      o << \", depth=\" << e.depth << \"}\";\n      return o;\n    }\n  };\n\n  class TestReceiver : public cli_apply_args_files_receiver {\n   public:\n    std::vector<Entry> entries;\n    std::vector<std::string> strings;\n    size_t current_depth = 0;\n    std::string stop_string;\n\n    control on_term(std::string arg, location loc) override {\n      strings.push_back(arg);\n      entries.push_back({\n          .type = Entry::TERM,\n          .value = std::string(arg),\n          .loc = loc,\n          .depth = current_depth,\n      });\n\n      if (!stop_string.empty() && arg == stop_string) {\n        return control::stop;\n      }\n      return control::pass;\n    }\n\n    void on_term_error(\n        term_error error, location term_loc, location error_loc) override {\n      entries.push_back({\n          .type = Entry::TERM_ERROR,\n          .error_code = error,\n          .loc = term_loc,\n          .error_loc = error_loc,\n          .depth = current_depth,\n      });\n    }\n\n    found on_file_found(cstring_view file, location loc) override {\n      entries.push_back({\n          .type = Entry::FILE_FOUND,\n          .value = std::string(file),\n          .loc = loc,\n          .depth = current_depth,\n      });\n\n      return found::dive;\n    }\n\n    control on_file_error(\n        std::string file, location loc, std::error_code err) override {\n      entries.push_back({\n          .type = Entry::FILE_ERROR,\n          .value = std::move(file),\n          .loc = loc,\n          .err = err,\n          .depth = current_depth,\n      });\n      return control::pass;\n    }\n\n    void on_file_enter(std::string file, location loc) override {\n      entries.push_back({\n          .type = Entry::FILE_ENTER,\n          .value = std::string(file),\n          .loc = loc,\n          .depth = current_depth,\n      });\n      current_depth++;\n    }\n\n    void on_file_leave() override {\n      current_depth--;\n      entries.push_back({\n          .type = Entry::FILE_LEAVE,\n          .depth = current_depth,\n      });\n    }\n\n    cycle on_file_cycle(\n        std::string file,\n        location loc,\n        std::filesystem::path canonical_path) override {\n      entries.push_back({\n          .type = Entry::FILE_CYCLE,\n          .value = std::move(file),\n          .loc = loc,\n          .depth = current_depth,\n          .canonical_path = std::move(canonical_path),\n      });\n      return cycle::skip; // Default: skip cycles\n    }\n\n    void set_stop_string(std::string const& str) { stop_string = str; }\n  };\n\n  // Helper function to write a file relative to the temporary directory\n  void write_file(const std::string& filename, const std::string& content) {\n    auto path = tempDir_.path() / filename;\n\n    std::ofstream file(path.string());\n    file << folly::stripLeftMargin(content);\n    file.close();\n  }\n\n  // Helper function to get the temporary directory path\n  std::filesystem::path temp_dir() const {\n    return std::filesystem::path{tempDir_.path().string()};\n  }\n\n  // Helper function to create a location for top-level entries\n  static location idx(size_t idx_val) {\n    return {.idx = idx_val, .off = 0, .len = 0, .b = {}, .e = {}};\n  }\n\n  // Helper functions for creating test entries\n  Entry entry_term(size_t depth, const std::string& value, location loc = {}) {\n    auto type = Entry::TERM;\n    return {\n        .type = type, .value = value, .loc = loc, .err = {}, .depth = depth};\n  }\n\n  Entry entry_file_found(\n      size_t depth, const std::string& value, location loc = {}) {\n    auto type = Entry::FILE_FOUND;\n    return {\n        .type = type, .value = value, .loc = loc, .err = {}, .depth = depth};\n  }\n\n  Entry entry_file_enter(\n      size_t depth, const std::string& value, location loc = {}) {\n    auto type = Entry::FILE_ENTER;\n    return {\n        .type = type, .value = value, .loc = loc, .err = {}, .depth = depth};\n  }\n\n  Entry entry_file_leave(size_t depth) {\n    auto type = Entry::FILE_LEAVE;\n    return {.type = type, .value = \"\", .loc = {}, .err = {}, .depth = depth};\n  }\n\n  Entry entry_file_error(\n      size_t depth,\n      const std::string& value,\n      location loc,\n      std::error_code err) {\n    auto type = Entry::FILE_ERROR;\n    return {\n        .type = type, .value = value, .loc = loc, .err = err, .depth = depth};\n  }\n\n  Entry entry_file_error_no_such_file(\n      size_t depth, const std::string& value, location loc) {\n    auto error = std::make_error_code(std::errc::no_such_file_or_directory);\n    return entry_file_error(depth, value, loc, error);\n  }\n\n  Entry entry_file_error_is_a_directory(\n      size_t depth, const std::string& value, location loc) {\n    auto error = std::make_error_code(std::errc::is_a_directory);\n    return entry_file_error(depth, value, loc, error);\n  }\n\n  Entry entry_file_error_max_depth(\n      size_t depth, const std::string& value, location loc) {\n    return entry_file_error(\n        depth,\n        value,\n        loc,\n        make_error_code(cli_apply_args_files_errc::max_depth_exceeded));\n  }\n\n  Entry entry_term_error(\n      size_t depth, term_error code, location term_loc, location error_loc) {\n    return {\n        .type = Entry::TERM_ERROR,\n        .error_code = code,\n        .loc = term_loc,\n        .error_loc = error_loc,\n        .depth = depth};\n  }\n\n  Entry entry_file_cycle(\n      size_t depth,\n      const std::string& value,\n      location loc,\n      std::filesystem::path canonical_path) {\n    return {\n        .type = Entry::FILE_CYCLE,\n        .value = value,\n        .loc = loc,\n        .depth = depth,\n        .canonical_path = std::move(canonical_path)};\n  }\n\n  folly::test::TemporaryDirectory tempDir_;\n};\n\nTEST_F(ArgsTest, BasicArguments) {\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"--flag\",\n      \"value\",\n      \"arg\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag\",\n          \"value\",\n          \"arg\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_term(0, \"--flag\", idx(1)),\n          entry_term(0, \"value\", idx(2)),\n          entry_term(0, \"arg\", idx(3)),\n      }));\n}\n\nTEST_F(ArgsTest, SimpleArgsFile) {\n  write_file(\"simple.args\", R\"(\n    --option1 value1 --option2\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@simple.args\",\n      \"trailing\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--option1\",\n          \"value1\",\n          \"--option2\",\n          \"trailing\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"simple.args\", idx(1)),\n          entry_file_enter(0, \"simple.args\", idx(1)),\n          entry_term(\n              1,\n              \"--option1\",\n              {.idx = 0, .off = 0, .len = 9, .b = {1, 1}, .e = {1, 9}}),\n          entry_term(\n              1,\n              \"value1\",\n              {.idx = 1, .off = 10, .len = 6, .b = {1, 11}, .e = {1, 16}}),\n          entry_term(\n              1,\n              \"--option2\",\n              {.idx = 2, .off = 17, .len = 9, .b = {1, 18}, .e = {1, 26}}),\n          entry_file_leave(0),\n          entry_term(0, \"trailing\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, QuotedArguments) {\n  write_file(\"quoted.args\", R\"(\n    --message \"hello world\" --path '/some/path with spaces'\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@quoted.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--message\",\n          \"hello world\",\n          \"--path\",\n          \"/some/path with spaces\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"quoted.args\", idx(1)),\n          entry_file_enter(0, \"quoted.args\", idx(1)),\n          entry_term(\n              1,\n              \"--message\",\n              {.idx = 0, .off = 0, .len = 9, .b = {1, 1}, .e = {1, 9}}),\n          entry_term(\n              1,\n              \"hello world\",\n              {.idx = 1, .off = 10, .len = 13, .b = {1, 11}, .e = {1, 23}}),\n          entry_term(\n              1,\n              \"--path\",\n              {.idx = 2, .off = 24, .len = 6, .b = {1, 25}, .e = {1, 30}}),\n          entry_term(\n              1,\n              \"/some/path with spaces\",\n              {.idx = 3, .off = 31, .len = 24, .b = {1, 32}, .e = {1, 55}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, EscapeSequences) {\n  write_file(\"escaped.args\", R\"(\n    --text \"Line 1\\nLine 2\" --quote \"She said \\\"hello\\\"\"\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@escaped.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--text\",\n          \"Line 1\\nLine 2\",\n          \"--quote\",\n          \"She said \\\"hello\\\"\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"escaped.args\", idx(1)),\n          entry_file_enter(0, \"escaped.args\", idx(1)),\n          entry_term(\n              1,\n              \"--text\",\n              {.idx = 0, .off = 0, .len = 6, .b = {1, 1}, .e = {1, 6}}),\n          entry_term(\n              1,\n              \"Line 1\\nLine 2\",\n              {.idx = 1, .off = 7, .len = 16, .b = {1, 8}, .e = {1, 23}}),\n          entry_term(\n              1,\n              \"--quote\",\n              {.idx = 2, .off = 24, .len = 7, .b = {1, 25}, .e = {1, 31}}),\n          entry_term(\n              1,\n              \"She said \\\"hello\\\"\",\n              {.idx = 3, .off = 32, .len = 20, .b = {1, 33}, .e = {1, 52}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, CommentLines) {\n  write_file(\"commented.args\", R\"(\n    # This is a comment\n    --verbose\n    # Another comment\n    --output result.txt\n    # Final comment\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@commented.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--verbose\",\n          \"--output\",\n          \"result.txt\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"commented.args\", idx(1)),\n          entry_file_enter(0, \"commented.args\", idx(1)),\n          entry_term(\n              1,\n              \"--verbose\",\n              {.idx = 0, .off = 20, .len = 9, .b = {2, 1}, .e = {2, 9}}),\n          entry_term(\n              1,\n              \"--output\",\n              {.idx = 1, .off = 48, .len = 8, .b = {4, 1}, .e = {4, 8}}),\n          entry_term(\n              1,\n              \"result.txt\",\n              {.idx = 2, .off = 57, .len = 10, .b = {4, 10}, .e = {4, 19}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, EmptyFile) {\n  write_file(\"empty.args\", R\"(\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@empty.args\",\n      \"--flag\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag\",\n      }));\n\n  // Should have file enter/leave entries even for empty file\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"empty.args\", idx(1)),\n          entry_file_enter(0, \"empty.args\", idx(1)),\n          entry_file_leave(0),\n          entry_term(0, \"--flag\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, NestedArgsFiles) {\n  write_file(\"inner.args\", R\"(\n    --inner-flag inner_value\n  )\");\n\n  write_file(\"outer.args\", R\"(\n    --outer-flag @inner.args outer_value\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@outer.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--outer-flag\",\n          \"--inner-flag\",\n          \"inner_value\",\n          \"outer_value\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"outer.args\", idx(1)),\n          entry_file_enter(0, \"outer.args\", idx(1)),\n          entry_term(\n              1,\n              \"--outer-flag\",\n              {.idx = 0, .off = 0, .len = 12, .b = {1, 1}, .e = {1, 12}}),\n          entry_file_found(\n              1,\n              \"inner.args\",\n              {.idx = 1, .off = 13, .len = 11, .b = {1, 14}, .e = {1, 24}}),\n          entry_file_enter(\n              1,\n              \"inner.args\",\n              {.idx = 1, .off = 13, .len = 11, .b = {1, 14}, .e = {1, 24}}),\n          entry_term(\n              2,\n              \"--inner-flag\",\n              {.idx = 0, .off = 0, .len = 12, .b = {1, 1}, .e = {1, 12}}),\n          entry_term(\n              2,\n              \"inner_value\",\n              {.idx = 1, .off = 13, .len = 11, .b = {1, 14}, .e = {1, 24}}),\n          entry_file_leave(1),\n          entry_term(\n              1,\n              \"outer_value\",\n              {.idx = 2, .off = 25, .len = 11, .b = {1, 26}, .e = {1, 36}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, MultipleArgsFiles) {\n  write_file(\"file1.args\", R\"(\n    --flag1 value1\n  )\");\n\n  write_file(\"file2.args\", R\"(\n    --flag2 value2\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@file1.args\",\n      \"middle\",\n      \"@file2.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag1\",\n          \"value1\",\n          \"middle\",\n          \"--flag2\",\n          \"value2\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"file1.args\", idx(1)),\n          entry_file_enter(0, \"file1.args\", idx(1)),\n          entry_term(\n              1,\n              \"--flag1\",\n              {.idx = 0, .off = 0, .len = 7, .b = {1, 1}, .e = {1, 7}}),\n          entry_term(\n              1,\n              \"value1\",\n              {.idx = 1, .off = 8, .len = 6, .b = {1, 9}, .e = {1, 14}}),\n          entry_file_leave(0),\n          entry_term(0, \"middle\", idx(2)),\n          entry_file_found(0, \"file2.args\", idx(3)),\n          entry_file_enter(0, \"file2.args\", idx(3)),\n          entry_term(\n              1,\n              \"--flag2\",\n              {.idx = 0, .off = 0, .len = 7, .b = {1, 1}, .e = {1, 7}}),\n          entry_term(\n              1,\n              \"value2\",\n              {.idx = 1, .off = 8, .len = 6, .b = {1, 9}, .e = {1, 14}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, ArgumentStartingWithAt) {\n  // An argument that is exactly @ should remain as-is\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@\",\n      \"normal_arg\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"@\",\n          \"normal_arg\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_term(0, \"@\", idx(1)),\n          entry_term(0, \"normal_arg\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, NonExistentFile) {\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@/nonexistent/file.txt\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"/nonexistent/file.txt\", idx(1)),\n          entry_file_error_no_such_file(0, \"/nonexistent/file.txt\", idx(1)),\n      }));\n}\n\nTEST_F(ArgsTest, ComplexRealWorldExample) {\n  write_file(\"config.args\", R\"(\n    # Configuration for build system\n    --build-type Release\n    --target \"MyApplication\"\n    --define \"VERSION=\\\"1.0.0\\\"\"\n    --include-dir \"/usr/local/include\"\n    --include-dir \"/opt/custom/include\"\n    # Output settings\n    --output-dir \"build/release\"\n    --verbose\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"build-tool\",\n      \"--config\",\n      \"debug.cfg\",\n      \"@config.args\",\n      \"--extra-flag\",\n      \"source.cpp\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"build-tool\",\n          \"--config\",\n          \"debug.cfg\",\n          \"--build-type\",\n          \"Release\",\n          \"--target\",\n          \"MyApplication\",\n          \"--define\",\n          \"VERSION=\\\"1.0.0\\\"\",\n          \"--include-dir\",\n          \"/usr/local/include\",\n          \"--include-dir\",\n          \"/opt/custom/include\",\n          \"--output-dir\",\n          \"build/release\",\n          \"--verbose\",\n          \"--extra-flag\",\n          \"source.cpp\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"build-tool\", idx(0)),\n          entry_term(0, \"--config\", idx(1)),\n          entry_term(0, \"debug.cfg\", idx(2)),\n          entry_file_found(0, \"config.args\", idx(3)),\n          entry_file_enter(0, \"config.args\", idx(3)),\n          entry_term(\n              1,\n              \"--build-type\",\n              {.idx = 0, .off = 33, .len = 12, .b = {2, 1}, .e = {2, 12}}),\n          entry_term(\n              1,\n              \"Release\",\n              {.idx = 1, .off = 46, .len = 7, .b = {2, 14}, .e = {2, 20}}),\n          entry_term(\n              1,\n              \"--target\",\n              {.idx = 2, .off = 54, .len = 8, .b = {3, 1}, .e = {3, 8}}),\n          entry_term(\n              1,\n              \"MyApplication\",\n              {.idx = 3, .off = 63, .len = 15, .b = {3, 10}, .e = {3, 24}}),\n          entry_term(\n              1,\n              \"--define\",\n              {.idx = 4, .off = 79, .len = 8, .b = {4, 1}, .e = {4, 8}}),\n          entry_term(\n              1,\n              \"VERSION=\\\"1.0.0\\\"\",\n              {.idx = 5, .off = 88, .len = 19, .b = {4, 10}, .e = {4, 28}}),\n          entry_term(\n              1,\n              \"--include-dir\",\n              {.idx = 6, .off = 108, .len = 13, .b = {5, 1}, .e = {5, 13}}),\n          entry_term(\n              1,\n              \"/usr/local/include\",\n              {.idx = 7, .off = 122, .len = 20, .b = {5, 15}, .e = {5, 34}}),\n          entry_term(\n              1,\n              \"--include-dir\",\n              {.idx = 8, .off = 143, .len = 13, .b = {6, 1}, .e = {6, 13}}),\n          entry_term(\n              1,\n              \"/opt/custom/include\",\n              {.idx = 9, .off = 157, .len = 21, .b = {6, 15}, .e = {6, 35}}),\n          entry_term(\n              1,\n              \"--output-dir\",\n              {.idx = 10, .off = 197, .len = 12, .b = {8, 1}, .e = {8, 12}}),\n          entry_term(\n              1,\n              \"build/release\",\n              {.idx = 11, .off = 210, .len = 15, .b = {8, 14}, .e = {8, 28}}),\n          entry_term(\n              1,\n              \"--verbose\",\n              {.idx = 12, .off = 226, .len = 9, .b = {9, 1}, .e = {9, 9}}),\n          entry_file_leave(0),\n          entry_term(0, \"--extra-flag\", idx(4)),\n          entry_term(0, \"source.cpp\", idx(5)),\n      }));\n}\n\nTEST_F(ArgsTest, TabsAndSpaces) {\n  write_file(\"tabs_spaces.args\", R\"(\n    --flag1\t\tvalue1    --flag2\t   value2\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@tabs_spaces.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag1\",\n          \"value1\",\n          \"--flag2\",\n          \"value2\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"tabs_spaces.args\", idx(1)),\n          entry_file_enter(0, \"tabs_spaces.args\", idx(1)),\n          entry_term(\n              1,\n              \"--flag1\",\n              {.idx = 0, .off = 0, .len = 7, .b = {1, 1}, .e = {1, 7}}),\n          entry_term(\n              1,\n              \"value1\",\n              {.idx = 1, .off = 9, .len = 6, .b = {1, 10}, .e = {1, 15}}),\n          entry_term(\n              1,\n              \"--flag2\",\n              {.idx = 2, .off = 19, .len = 7, .b = {1, 20}, .e = {1, 26}}),\n          entry_term(\n              1,\n              \"value2\",\n              {.idx = 3, .off = 30, .len = 6, .b = {1, 31}, .e = {1, 36}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, EscapesOutsideQuotes) {\n  write_file(\"escapes.args\", R\"(\n    --text Line\\nWithBackslash --other normal\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@escapes.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--text\",\n          \"Line\\nWithBackslash\",\n          \"--other\",\n          \"normal\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"escapes.args\", idx(1)),\n          entry_file_enter(0, \"escapes.args\", idx(1)),\n          entry_term(\n              1,\n              \"--text\",\n              {.idx = 0, .off = 0, .len = 6, .b = {1, 1}, .e = {1, 6}}),\n          entry_term(\n              1,\n              \"Line\\nWithBackslash\",\n              {.idx = 1, .off = 7, .len = 19, .b = {1, 8}, .e = {1, 26}}),\n          entry_term(\n              1,\n              \"--other\",\n              {.idx = 2, .off = 27, .len = 7, .b = {1, 28}, .e = {1, 34}}),\n          entry_term(\n              1,\n              \"normal\",\n              {.idx = 3, .off = 35, .len = 6, .b = {1, 36}, .e = {1, 41}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, EmptyInputVector) {\n  auto const args = std::vector<std::string>{};\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_TRUE(receiver.strings.empty());\n  EXPECT_TRUE(receiver.entries.empty());\n}\n\nTEST_F(ArgsTest, PositionTracking) {\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"--flag\",\n      \"value\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag\",\n          \"value\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_term(0, \"--flag\", idx(1)),\n          entry_term(0, \"value\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, FileErrorPosition) {\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"--flag\",\n      \"@nonexistent\",\n      \"more\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag\",\n          \"more\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_term(0, \"--flag\", idx(1)),\n          entry_file_found(0, \"nonexistent\", idx(2)),\n          entry_file_error_no_such_file(0, \"nonexistent\", idx(2)),\n          entry_term(0, \"more\", idx(3)),\n      }));\n}\n\nTEST_F(ArgsTest, MissingFileAtTopLevel) {\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@missing.args\",\n      \"after\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"after\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"missing.args\", idx(1)),\n          entry_file_error_no_such_file(0, \"missing.args\", idx(1)),\n          entry_term(0, \"after\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, MissingFileInNestedArgsFile) {\n  write_file(\"outer.args\", R\"(\n    --start @missing-inner.args --end\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@outer.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--start\",\n          \"--end\",\n          \"final\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"outer.args\", idx(1)),\n          entry_file_enter(0, \"outer.args\", idx(1)),\n          entry_term(\n              1,\n              \"--start\",\n              {.idx = 0, .off = 0, .len = 7, .b = {1, 1}, .e = {1, 7}}),\n          entry_file_found(\n              1,\n              \"missing-inner.args\",\n              {.idx = 1, .off = 8, .len = 19, .b = {1, 9}, .e = {1, 27}}),\n          entry_file_error_no_such_file(\n              1,\n              \"missing-inner.args\",\n              {.idx = 1, .off = 8, .len = 19, .b = {1, 9}, .e = {1, 27}}),\n          entry_term(\n              1,\n              \"--end\",\n              {.idx = 2, .off = 28, .len = 5, .b = {1, 29}, .e = {1, 33}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, MultipleMissingFilesAtSameLevel) {\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@missing1.args\",\n      \"middle\",\n      \"@missing2.args\",\n      \"end\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"middle\",\n          \"end\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"missing1.args\", idx(1)),\n          entry_file_error_no_such_file(0, \"missing1.args\", idx(1)),\n          entry_term(0, \"middle\", idx(2)),\n          entry_file_found(0, \"missing2.args\", idx(3)),\n          entry_file_error_no_such_file(0, \"missing2.args\", idx(3)),\n          entry_term(0, \"end\", idx(4)),\n      }));\n}\n\nTEST_F(ArgsTest, MissingFileInDeeplyNestedStructure) {\n  write_file(\"level1.args\", R\"(\n    --l1-start @level2.args --l1-end\n  )\");\n  write_file(\"level2.args\", R\"(\n    --l2-start @missing-level3.args --l2-end\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@level1.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--l1-start\",\n          \"--l2-start\",\n          \"--l2-end\",\n          \"--l1-end\",\n          \"final\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"level1.args\", idx(1)),\n          entry_file_enter(0, \"level1.args\", idx(1)),\n          entry_term(\n              1,\n              \"--l1-start\",\n              {.idx = 0, .off = 0, .len = 10, .b = {1, 1}, .e = {1, 10}}),\n          entry_file_found(\n              1,\n              \"level2.args\",\n              {.idx = 1, .off = 11, .len = 12, .b = {1, 12}, .e = {1, 23}}),\n          entry_file_enter(\n              1,\n              \"level2.args\",\n              {.idx = 1, .off = 11, .len = 12, .b = {1, 12}, .e = {1, 23}}),\n          entry_term(\n              2,\n              \"--l2-start\",\n              {.idx = 0, .off = 0, .len = 10, .b = {1, 1}, .e = {1, 10}}),\n          entry_file_found(\n              2,\n              \"missing-level3.args\",\n              {.idx = 1, .off = 11, .len = 20, .b = {1, 12}, .e = {1, 31}}),\n          entry_file_error_no_such_file(\n              2,\n              \"missing-level3.args\",\n              {.idx = 1, .off = 11, .len = 20, .b = {1, 12}, .e = {1, 31}}),\n          entry_term(\n              2,\n              \"--l2-end\",\n              {.idx = 2, .off = 32, .len = 8, .b = {1, 33}, .e = {1, 40}}),\n          entry_file_leave(1),\n          entry_term(\n              1,\n              \"--l1-end\",\n              {.idx = 2, .off = 24, .len = 8, .b = {1, 25}, .e = {1, 32}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, MixOfValidAndMissingFiles) {\n  write_file(\"valid1.args\", R\"(\n    --valid1-flag valid1-value\n  )\");\n  write_file(\"valid2.args\", R\"(\n    --valid2-flag valid2-value\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@valid1.args\",\n      \"@missing.args\",\n      \"@valid2.args\",\n      \"end\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--valid1-flag\",\n          \"valid1-value\",\n          \"--valid2-flag\",\n          \"valid2-value\",\n          \"end\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"valid1.args\", idx(1)),\n          entry_file_enter(0, \"valid1.args\", idx(1)),\n          entry_term(\n              1,\n              \"--valid1-flag\",\n              {.idx = 0, .off = 0, .len = 13, .b = {1, 1}, .e = {1, 13}}),\n          entry_term(\n              1,\n              \"valid1-value\",\n              {.idx = 1, .off = 14, .len = 12, .b = {1, 15}, .e = {1, 26}}),\n          entry_file_leave(0),\n          entry_file_found(0, \"missing.args\", idx(2)),\n          entry_file_error_no_such_file(0, \"missing.args\", idx(2)),\n          entry_file_found(0, \"valid2.args\", idx(3)),\n          entry_file_enter(0, \"valid2.args\", idx(3)),\n          entry_term(\n              1,\n              \"--valid2-flag\",\n              {.idx = 0, .off = 0, .len = 13, .b = {1, 1}, .e = {1, 13}}),\n          entry_term(\n              1,\n              \"valid2-value\",\n              {.idx = 1, .off = 14, .len = 12, .b = {1, 15}, .e = {1, 26}}),\n          entry_file_leave(0),\n          entry_term(0, \"end\", idx(4)),\n      }));\n}\n\nTEST_F(ArgsTest, MissingFileWithSpecialCharactersInName) {\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@missing-file_with.special-chars.args\",\n      \"end\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"end\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"missing-file_with.special-chars.args\", idx(1)),\n          entry_file_error_no_such_file(\n              0, \"missing-file_with.special-chars.args\", idx(1)),\n          entry_term(0, \"end\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, TopLevelArgumentsNotProcessedForQuotes) {\n  // Top-level arguments should be passed through as-is, not unquoted/unescaped\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"\\\"quoted string\\\"\",\n      \"'single quoted'\",\n      \"escaped\\\\nstring\",\n      \"arg with spaces\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"\\\"quoted string\\\"\", // quotes preserved\n          \"'single quoted'\", // quotes preserved\n          \"escaped\\\\nstring\", // escape preserved\n          \"arg with spaces\", // spaces preserved (already handled by shell)\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_term(0, \"\\\"quoted string\\\"\", idx(1)),\n          entry_term(0, \"'single quoted'\", idx(2)),\n          entry_term(0, \"escaped\\\\nstring\", idx(3)),\n          entry_term(0, \"arg with spaces\", idx(4)),\n      }));\n}\n\nTEST_F(ArgsTest, ArgsFileArgumentsProcessedForQuotes) {\n  // Args-file arguments should be unquoted/unescaped\n  write_file(\"processing.args\", R\"(\n    \"quoted string\"\n    'single quoted'\n    escaped\\nstring\n    \"arg with spaces\"\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@processing.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"quoted string\", // quotes removed\n          \"single quoted\", // quotes removed\n          \"escaped\\nstring\", // escape processed\n          \"arg with spaces\", // quotes removed, spaces preserved\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"processing.args\", idx(1)),\n          entry_file_enter(0, \"processing.args\", idx(1)),\n          entry_term(\n              1,\n              \"quoted string\",\n              {.idx = 0, .off = 0, .len = 15, .b = {1, 1}, .e = {1, 15}}),\n          entry_term(\n              1,\n              \"single quoted\",\n              {.idx = 1, .off = 16, .len = 15, .b = {2, 1}, .e = {2, 15}}),\n          entry_term(\n              1,\n              \"escaped\\nstring\",\n              {.idx = 2, .off = 32, .len = 15, .b = {3, 1}, .e = {3, 15}}),\n          entry_term(\n              1,\n              \"arg with spaces\",\n              {.idx = 3, .off = 48, .len = 17, .b = {4, 1}, .e = {4, 17}}),\n          entry_file_leave(0),\n      }));\n}\n\n// Test cases for stop strings\nTEST_F(ArgsTest, StopStringInTopLevelArgs) {\n  write_file(\"after_stop.args\", R\"(\n    --should-not-appear\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"--start\",\n      \"STOP\",\n      \"@after_stop.args\",\n      \"should-not-appear\",\n  };\n\n  TestReceiver receiver;\n  receiver.set_stop_string(\"STOP\");\n\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--start\",\n          \"STOP\", // Processing stops after this\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_term(0, \"--start\", idx(1)),\n          // Processing stops here\n          entry_term(0, \"STOP\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, StopStringInArgsFile) {\n  write_file(\"with_stop.args\", R\"(\n    --before-stop STOP --after-stop\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@with_stop.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  receiver.set_stop_string(\"STOP\");\n\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--before-stop\",\n          \"STOP\", // processing stops after this within the args file\n          \"final\", // continues at parent level\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"with_stop.args\", idx(1)),\n          entry_file_enter(0, \"with_stop.args\", idx(1)),\n          entry_term(\n              1,\n              \"--before-stop\",\n              {.idx = 0, .off = 0, .len = 13, .b = {1, 1}, .e = {1, 13}}),\n          // processing stops here in this file\n          entry_term(\n              1,\n              \"STOP\",\n              {.idx = 1, .off = 14, .len = 4, .b = {1, 15}, .e = {1, 18}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, StopStringInNestedArgsFile) {\n  write_file(\"outer_with_nested.args\", R\"(\n    --outer-start @inner_with_stop.args --outer-end\n  )\");\n  write_file(\"inner_with_stop.args\", R\"(\n    --inner-start STOP --inner-end\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@outer_with_nested.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  receiver.set_stop_string(\"STOP\");\n\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--outer-start\",\n          \"--inner-start\",\n          \"STOP\", // processing stops here in inner file\n          \"--outer-end\", // continues at outer level\n          \"final\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"outer_with_nested.args\", idx(1)),\n          entry_file_enter(0, \"outer_with_nested.args\", idx(1)),\n          entry_term(\n              1,\n              \"--outer-start\",\n              {.idx = 0, .off = 0, .len = 13, .b = {1, 1}, .e = {1, 13}}),\n          entry_file_found(\n              1,\n              \"inner_with_stop.args\",\n              {.idx = 1, .off = 14, .len = 21, .b = {1, 15}, .e = {1, 35}}),\n          entry_file_enter(\n              1,\n              \"inner_with_stop.args\",\n              {.idx = 1, .off = 14, .len = 21, .b = {1, 15}, .e = {1, 35}}),\n          entry_term(\n              2,\n              \"--inner-start\",\n              {.idx = 0, .off = 0, .len = 13, .b = {1, 1}, .e = {1, 13}}),\n          // stops processing in inner file\n          entry_term(\n              2,\n              \"STOP\",\n              {.idx = 1, .off = 14, .len = 4, .b = {1, 15}, .e = {1, 18}}),\n          entry_file_leave(1),\n          // continues at parent level\n          entry_term(\n              1,\n              \"--outer-end\",\n              {.idx = 2, .off = 36, .len = 11, .b = {1, 37}, .e = {1, 47}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, MixedQuotesInSingleToken) {\n  // Test shell-style quote parsing where quotes can appear in the middle of\n  // tokens and multiple quoted sections can be concatenated\n  write_file(\"mixed_quotes.args\", R\"(\n      foo'b'a\"r\"''''baz\n      pre'fix_'mid\"dle_\"suffix\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@mixed_quotes.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"foobarbaz\",\n          \"prefix_middle_suffix\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"mixed_quotes.args\", idx(1)),\n          entry_file_enter(0, \"mixed_quotes.args\", idx(1)),\n          entry_term(\n              1,\n              \"foobarbaz\",\n              {.idx = 0, .off = 0, .len = 17, .b = {1, 1}, .e = {1, 17}}),\n          entry_term(\n              1,\n              \"prefix_middle_suffix\",\n              {.idx = 1, .off = 18, .len = 24, .b = {2, 1}, .e = {2, 24}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, UnclosedSingleQuoteCallsOnTermError) {\n  // Unclosed single quotes should call on_term_error and continue with parent\n  write_file(\"unclosed_single.args\", R\"(\n      --flag 'unclosed\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@unclosed_single.args\",\n      \"after\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag\",\n          \"after\", // continues after error\n      }));\n\n  // File content after stripLeftMargin: \"--flag 'unclosed\\n\"\n  // --flag is at offset 0, len 6\n  // 'unclosed starts at offset 7 (the quote)\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"unclosed_single.args\", idx(1)),\n          entry_file_enter(0, \"unclosed_single.args\", idx(1)),\n          entry_term(\n              1,\n              \"--flag\",\n              {.idx = 0, .off = 0, .len = 6, .b = {1, 1}, .e = {1, 6}}),\n          entry_term_error(\n              1,\n              term_error::unclosed_single_quote,\n              // term_loc: where the problematic term begins (the quote)\n              {.idx = 1, .off = 7, .len = 10, .b = {1, 8}, .e = {0, 0}},\n              // error_loc: where the error begins (also the quote)\n              {.idx = 1, .off = 7, .len = 10, .b = {1, 8}, .e = {0, 0}}),\n          entry_file_leave(0),\n          entry_term(0, \"after\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, UnclosedDoubleQuoteCallsOnTermError) {\n  // Unclosed double quotes should call on_term_error and continue with parent\n  write_file(\"unclosed_double.args\", R\"(\n      --flag \"unclosed\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@unclosed_double.args\",\n      \"after\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag\",\n          \"after\", // continues after error\n      }));\n\n  // File content after stripLeftMargin: \"--flag \\\"unclosed\\n\"\n  // --flag is at offset 0, len 6\n  // \"unclosed starts at offset 7 (the quote)\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"unclosed_double.args\", idx(1)),\n          entry_file_enter(0, \"unclosed_double.args\", idx(1)),\n          entry_term(\n              1,\n              \"--flag\",\n              {.idx = 0, .off = 0, .len = 6, .b = {1, 1}, .e = {1, 6}}),\n          entry_term_error(\n              1,\n              term_error::unclosed_double_quote,\n              // term_loc: where the problematic term begins (the quote)\n              {.idx = 1, .off = 7, .len = 10, .b = {1, 8}, .e = {0, 0}},\n              // error_loc: where the error begins (also the quote)\n              {.idx = 1, .off = 7, .len = 10, .b = {1, 8}, .e = {0, 0}}),\n          entry_file_leave(0),\n          entry_term(0, \"after\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, UnclosedQuoteInNestedFileContinuesParent) {\n  // Unclosed quotes in nested file should continue processing parent file\n  write_file(\"outer.args\", R\"(\n      --outer-start @inner_bad.args --outer-end\n  )\");\n  write_file(\"inner_bad.args\", R\"(\n      --inner-start 'unclosed\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@outer.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--outer-start\",\n          \"--inner-start\",\n          \"--outer-end\", // continues after inner file error\n          \"final\",\n      }));\n\n  // outer.args content: \"--outer-start @inner_bad.args --outer-end\\n\"\n  // inner_bad.args content: \"--inner-start 'unclosed\\n\"\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"outer.args\", idx(1)),\n          entry_file_enter(0, \"outer.args\", idx(1)),\n          entry_term(\n              1,\n              \"--outer-start\",\n              {.idx = 0, .off = 0, .len = 13, .b = {1, 1}, .e = {1, 13}}),\n          entry_file_found(\n              1,\n              \"inner_bad.args\",\n              {.idx = 1, .off = 14, .len = 15, .b = {1, 15}, .e = {1, 29}}),\n          entry_file_enter(\n              1,\n              \"inner_bad.args\",\n              {.idx = 1, .off = 14, .len = 15, .b = {1, 15}, .e = {1, 29}}),\n          entry_term(\n              2,\n              \"--inner-start\",\n              {.idx = 0, .off = 0, .len = 13, .b = {1, 1}, .e = {1, 13}}),\n          entry_term_error(\n              2,\n              term_error::unclosed_single_quote,\n              // term_loc: where the problematic term begins (the quote)\n              {.idx = 1, .off = 14, .len = 10, .b = {1, 15}, .e = {0, 0}},\n              // error_loc: where the error begins (also the quote)\n              {.idx = 1, .off = 14, .len = 10, .b = {1, 15}, .e = {0, 0}}),\n          entry_file_leave(1),\n          entry_term(\n              1,\n              \"--outer-end\",\n              {.idx = 2, .off = 30, .len = 11, .b = {1, 31}, .e = {1, 41}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, SingleQuotesAreLiteral) {\n  // Single quotes should preserve everything literally, including backslashes\n  write_file(\"single_literal.args\", R\"(\n      'hello\\nworld'\n      'with\\\\backslash'\n      'with\"double'\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@single_literal.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"hello\\\\nworld\", // backslash-n preserved literally\n          \"with\\\\\\\\backslash\", // double backslash preserved\n          \"with\\\"double\", // double quote preserved\n      }));\n}\n\nTEST_F(ArgsTest, EscapedHash) {\n  // Test that \\# escape sequence works correctly\n  // \\# prevents the # from starting a comment\n  write_file(\"escaped_special.args\", R\"(\n      \\#not-a-comment\n      prefix\\#suffix\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@escaped_special.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"#not-a-comment\", // \\# becomes literal #\n          \"prefix#suffix\", // \\# in middle of token\n      }));\n}\n\nTEST_F(ArgsTest, EscapedHashInDoubleQuotes) {\n  // Test that \\# works inside double quotes\n  write_file(\"escaped_in_quotes.args\", R\"(\n      \"\\#hash\" \"prefix@suffix\"\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@escaped_in_quotes.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  // \\# produces literal #\n  // The @ in the middle of a term doesn't trigger args-file processing\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"#hash\",\n          \"prefix@suffix\",\n      }));\n}\n\nTEST_F(ArgsTest, UnknownEscapeCallsOnTermError) {\n  // Unknown escape sequences should call on_term_error\n  write_file(\"unknown_escape.args\", R\"(\n      \\x \\y \\z\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@unknown_escape.args\",\n      \"after\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"after\", // continues after error\n      }));\n\n  // File content: \"\\x \\y \\z\\n\"\n  // \\x starts at offset 0\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"unknown_escape.args\", idx(1)),\n          entry_file_enter(0, \"unknown_escape.args\", idx(1)),\n          entry_term_error(\n              1,\n              term_error::unrecognized_escape_sequence,\n              // term_loc: where the problematic term begins (the backslash)\n              {.idx = 0, .off = 0, .len = 9, .b = {1, 1}, .e = {0, 0}},\n              // error_loc: where the error begins (also the backslash)\n              {.idx = 0, .off = 0, .len = 9, .b = {1, 1}, .e = {0, 0}}),\n          entry_file_leave(0),\n          entry_term(0, \"after\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, DoubleAtEscapeAtTopLevel) {\n  // Test that @@ at the start of a top-level argument produces a literal @\n  // followed by the rest of the argument\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@@literal\",\n      \"@@file.args\",\n      \"@@\",\n      \"normal\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"@literal\", // @@ becomes @\n          \"@file.args\", // @@ becomes @, not treated as args-file\n          \"@\", // @@ becomes @\n          \"normal\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_term(0, \"@literal\", idx(1)),\n          entry_term(0, \"@file.args\", idx(2)),\n          entry_term(0, \"@\", idx(3)),\n          entry_term(0, \"normal\", idx(4)),\n      }));\n}\n\nTEST_F(ArgsTest, DoubleAtEscapeInArgsFile) {\n  // Test that @@ at the start of a term in an args-file produces a literal @\n  write_file(\"double_at.args\", R\"(\n      @@literal\n      @@file.args\n      @@\n      normal\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@double_at.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"@literal\", // @@ becomes @\n          \"@file.args\", // @@ becomes @, not treated as args-file\n          \"@\", // @@ becomes @\n          \"normal\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"double_at.args\", idx(1)),\n          entry_file_enter(0, \"double_at.args\", idx(1)),\n          entry_term(\n              1,\n              \"@literal\",\n              {.idx = 0, .off = 0, .len = 9, .b = {1, 1}, .e = {1, 9}}),\n          entry_term(\n              1,\n              \"@file.args\",\n              {.idx = 1, .off = 10, .len = 11, .b = {2, 1}, .e = {2, 11}}),\n          entry_term(\n              1,\n              \"@\",\n              {.idx = 2, .off = 22, .len = 2, .b = {3, 1}, .e = {3, 2}}),\n          entry_term(\n              1,\n              \"normal\",\n              {.idx = 3, .off = 25, .len = 6, .b = {4, 1}, .e = {4, 6}}),\n          entry_file_leave(0),\n      }));\n}\n\nTEST_F(ArgsTest, AtInsideQuotesNotArgsFile) {\n  // Test that @ inside quotes is not treated as args-file reference\n  // \"@foo\" => term \"@foo\" (not args-file)\n  // '@foo' => term \"@foo\" (not args-file)\n  write_file(\"at_in_quotes.args\", R\"(\n      \"@foo\"\n      '@bar'\n      prefix\"@middle\"suffix\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@at_in_quotes.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"@foo\", // @ inside double quotes, not args-file\n          \"@bar\", // @ inside single quotes, not args-file\n          \"prefix@middlesuffix\", // @ inside quotes in middle of term\n      }));\n}\n\nTEST_F(ArgsTest, AtOutsideQuotesThenQuotedFilename) {\n  // Test that @\"foo\" treats @ as args-file prefix and \"foo\" as quoted filename\n  // @\"foo\" => recursive args-file named \"foo\"\n  // @'bar' => recursive args-file named \"bar\"\n  write_file(\"foo\", \"from_foo\");\n  write_file(\"bar\", \"from_bar\");\n  write_file(\"at_then_quoted.args\", R\"(\n      @\"foo\"\n      @'bar'\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@at_then_quoted.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"from_foo\", // @\"foo\" => args-file \"foo\"\n          \"from_bar\", // @'bar' => args-file \"bar\"\n      }));\n}\n\nTEST_F(ArgsTest, DoubleAtInsideQuotesNotEscaped) {\n  // Test that @@ inside quotes is NOT escaped - it stays as @@\n  // \"@@foo\" => term \"@@foo\"\n  // '@@bar' => term \"@@bar\"\n  write_file(\"double_at_in_quotes.args\", R\"(\n      \"@@foo\"\n      '@@bar'\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@double_at_in_quotes.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"@@foo\", // @@ inside double quotes, not escaped\n          \"@@bar\", // @@ inside single quotes, not escaped\n      }));\n}\n\nTEST_F(ArgsTest, DoubleAtOutsideQuotesThenQuotedContent) {\n  // Test that @@\"foo\" has @@ outside quotes (escaped to @) then quoted content\n  // @@\"foo\" => term \"@foo\"\n  // @@'bar' => term \"@bar\"\n  write_file(\"double_at_then_quoted.args\", R\"(\n      @@\"foo\"\n      @@'bar'\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@double_at_then_quoted.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"@foo\", // @@\"foo\" => @@ escaped to @, then \"foo\" quoted\n          \"@bar\", // @@'bar' => @@ escaped to @, then 'bar' quoted\n      }));\n}\n\nTEST_F(ArgsTest, QuotedAtThenUnquoted) {\n  // Test that \"@\"foo has @ inside quotes (not special) then unquoted foo\n  // \"@\"foo => term \"@foo\"\n  write_file(\"quoted_at_then_unquoted.args\", R\"(\n      \"@\"foo\n      '@'bar\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@quoted_at_then_unquoted.args\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"@foo\", // \"@\"foo => @ inside quotes, then foo unquoted\n          \"@bar\", // '@'bar => @ inside quotes, then bar unquoted\n      }));\n}\n\nTEST_F(ArgsTest, BareAtInArgsFileTriggersFileError) {\n  // Test that bare @ in an args-file is treated as args-file with empty name\n  // This should trigger on_file_found and on_file_error (empty filename)\n  write_file(\"bare_at.args\", R\"(\n      --before\n      @\n      --after\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@bare_at.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--before\",\n          \"--after\", // continues after file error\n          \"final\",\n      }));\n\n  // Empty filename resolves to current directory, which fails with\n  // is_a_directory\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"bare_at.args\", idx(1)),\n          entry_file_enter(0, \"bare_at.args\", idx(1)),\n          entry_term(\n              1,\n              \"--before\",\n              {.idx = 0, .off = 0, .len = 8, .b = {1, 1}, .e = {1, 8}}),\n          entry_file_found(\n              1, \"\", {.idx = 1, .off = 9, .len = 1, .b = {2, 1}, .e = {2, 1}}),\n          entry_file_error_is_a_directory(\n              1, \"\", {.idx = 1, .off = 9, .len = 1, .b = {2, 1}, .e = {2, 1}}),\n          entry_term(\n              1,\n              \"--after\",\n              {.idx = 2, .off = 11, .len = 7, .b = {3, 1}, .e = {3, 7}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, DirectCyclicalReference) {\n  // Test that a file referencing itself is detected as cyclical\n  write_file(\"self.args\", R\"(\n    --before @self.args --after\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@self.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  // Should process --before, detect cyclical reference, skip it, then --after\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--before\",\n          \"--after\",\n          \"final\",\n      }));\n\n  // File content: \"--before @self.args --after\\n\"\n  // --before at offset 0, len 8\n  // @self.args at offset 9, len 10\n  // --after at offset 20, len 7\n  auto canonical = std::filesystem::canonical(temp_dir() / \"self.args\");\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"self.args\", idx(1)),\n          entry_file_enter(0, \"self.args\", idx(1)),\n          entry_term(\n              1,\n              \"--before\",\n              {.idx = 0, .off = 0, .len = 8, .b = {1, 1}, .e = {1, 8}}),\n          entry_file_found(\n              1,\n              \"self.args\",\n              {.idx = 1, .off = 9, .len = 10, .b = {1, 10}, .e = {1, 19}}),\n          entry_file_cycle(\n              1,\n              \"self.args\",\n              {.idx = 1, .off = 9, .len = 10, .b = {1, 10}, .e = {1, 19}},\n              canonical),\n          entry_term(\n              1,\n              \"--after\",\n              {.idx = 2, .off = 20, .len = 7, .b = {1, 21}, .e = {1, 27}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, IndirectCyclicalReference) {\n  // Test that A -> B -> A is detected as cyclical\n  write_file(\"a.args\", R\"(\n    --from-a @b.args --end-a\n  )\");\n  write_file(\"b.args\", R\"(\n    --from-b @a.args --end-b\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@a.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  // Should process a.args, then b.args, detect cyclical back to a.args\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--from-a\",\n          \"--from-b\",\n          \"--end-b\",\n          \"--end-a\",\n          \"final\",\n      }));\n\n  // a.args content: \"--from-a @b.args --end-a\\n\"\n  // --from-a at offset 0, len 8\n  // @b.args at offset 9, len 7\n  // --end-a at offset 17, len 7\n  // b.args content: \"--from-b @a.args --end-b\\n\"\n  // --from-b at offset 0, len 8\n  // @a.args at offset 9, len 7\n  // --end-b at offset 17, len 7\n  auto canonical_a = std::filesystem::canonical(temp_dir() / \"a.args\");\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"a.args\", idx(1)),\n          entry_file_enter(0, \"a.args\", idx(1)),\n          entry_term(\n              1,\n              \"--from-a\",\n              {.idx = 0, .off = 0, .len = 8, .b = {1, 1}, .e = {1, 8}}),\n          entry_file_found(\n              1,\n              \"b.args\",\n              {.idx = 1, .off = 9, .len = 7, .b = {1, 10}, .e = {1, 16}}),\n          entry_file_enter(\n              1,\n              \"b.args\",\n              {.idx = 1, .off = 9, .len = 7, .b = {1, 10}, .e = {1, 16}}),\n          entry_term(\n              2,\n              \"--from-b\",\n              {.idx = 0, .off = 0, .len = 8, .b = {1, 1}, .e = {1, 8}}),\n          entry_file_found(\n              2,\n              \"a.args\",\n              {.idx = 1, .off = 9, .len = 7, .b = {1, 10}, .e = {1, 16}}),\n          entry_file_cycle(\n              2,\n              \"a.args\",\n              {.idx = 1, .off = 9, .len = 7, .b = {1, 10}, .e = {1, 16}},\n              canonical_a),\n          entry_term(\n              2,\n              \"--end-b\",\n              {.idx = 2, .off = 17, .len = 7, .b = {1, 18}, .e = {1, 24}}),\n          entry_file_leave(1),\n          entry_term(\n              1,\n              \"--end-a\",\n              {.idx = 2, .off = 17, .len = 7, .b = {1, 18}, .e = {1, 24}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, LongCyclicalChain) {\n  // Test that A -> B -> C -> A is detected as cyclical\n  write_file(\"chain_a.args\", R\"(\n    --from-a @chain_b.args --end-a\n  )\");\n  write_file(\"chain_b.args\", R\"(\n    --from-b @chain_c.args --end-b\n  )\");\n  write_file(\"chain_c.args\", R\"(\n    --from-c @chain_a.args --end-c\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@chain_a.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--from-a\",\n          \"--from-b\",\n          \"--from-c\",\n          \"--end-c\",\n          \"--end-b\",\n          \"--end-a\",\n          \"final\",\n      }));\n\n  // chain_a.args content: \"--from-a @chain_b.args --end-a\\n\"\n  // chain_b.args content: \"--from-b @chain_c.args --end-b\\n\"\n  // chain_c.args content: \"--from-c @chain_a.args --end-c\\n\"\n  auto canonical_a = std::filesystem::canonical(temp_dir() / \"chain_a.args\");\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"chain_a.args\", idx(1)),\n          entry_file_enter(0, \"chain_a.args\", idx(1)),\n          entry_term(\n              1,\n              \"--from-a\",\n              {.idx = 0, .off = 0, .len = 8, .b = {1, 1}, .e = {1, 8}}),\n          entry_file_found(\n              1,\n              \"chain_b.args\",\n              {.idx = 1, .off = 9, .len = 13, .b = {1, 10}, .e = {1, 22}}),\n          entry_file_enter(\n              1,\n              \"chain_b.args\",\n              {.idx = 1, .off = 9, .len = 13, .b = {1, 10}, .e = {1, 22}}),\n          entry_term(\n              2,\n              \"--from-b\",\n              {.idx = 0, .off = 0, .len = 8, .b = {1, 1}, .e = {1, 8}}),\n          entry_file_found(\n              2,\n              \"chain_c.args\",\n              {.idx = 1, .off = 9, .len = 13, .b = {1, 10}, .e = {1, 22}}),\n          entry_file_enter(\n              2,\n              \"chain_c.args\",\n              {.idx = 1, .off = 9, .len = 13, .b = {1, 10}, .e = {1, 22}}),\n          entry_term(\n              3,\n              \"--from-c\",\n              {.idx = 0, .off = 0, .len = 8, .b = {1, 1}, .e = {1, 8}}),\n          entry_file_found(\n              3,\n              \"chain_a.args\",\n              {.idx = 1, .off = 9, .len = 13, .b = {1, 10}, .e = {1, 22}}),\n          entry_file_cycle(\n              3,\n              \"chain_a.args\",\n              {.idx = 1, .off = 9, .len = 13, .b = {1, 10}, .e = {1, 22}},\n              canonical_a),\n          entry_term(\n              3,\n              \"--end-c\",\n              {.idx = 2, .off = 23, .len = 7, .b = {1, 24}, .e = {1, 30}}),\n          entry_file_leave(2),\n          entry_term(\n              2,\n              \"--end-b\",\n              {.idx = 2, .off = 23, .len = 7, .b = {1, 24}, .e = {1, 30}}),\n          entry_file_leave(1),\n          entry_term(\n              1,\n              \"--end-a\",\n              {.idx = 2, .off = 23, .len = 7, .b = {1, 24}, .e = {1, 30}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, SameFileReferencedTwiceNotCyclical) {\n  // Test that referencing the same file twice (not nested) is NOT cyclical\n  // A -> B, then A -> B again (sequentially, not nested)\n  write_file(\"shared.args\", R\"(\n    --shared-content\n  )\");\n  write_file(\"uses_shared.args\", R\"(\n    --first @shared.args --second @shared.args --third\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@uses_shared.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  // shared.args should be processed twice (not circular since not nested)\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--first\",\n          \"--shared-content\",\n          \"--second\",\n          \"--shared-content\",\n          \"--third\",\n          \"final\",\n      }));\n\n  // uses_shared.args content: \"--first @shared.args --second @shared.args\n  // --third\\n\"\n  // --first at offset 0, len 7\n  // @shared.args at offset 8, len 12\n  // --second at offset 21, len 8\n  // @shared.args at offset 30, len 12\n  // --third at offset 43, len 7\n  // shared.args content: \"--shared-content\\n\"\n  // --shared-content at offset 0, len 16\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"uses_shared.args\", idx(1)),\n          entry_file_enter(0, \"uses_shared.args\", idx(1)),\n          entry_term(\n              1,\n              \"--first\",\n              {.idx = 0, .off = 0, .len = 7, .b = {1, 1}, .e = {1, 7}}),\n          entry_file_found(\n              1,\n              \"shared.args\",\n              {.idx = 1, .off = 8, .len = 12, .b = {1, 9}, .e = {1, 20}}),\n          entry_file_enter(\n              1,\n              \"shared.args\",\n              {.idx = 1, .off = 8, .len = 12, .b = {1, 9}, .e = {1, 20}}),\n          entry_term(\n              2,\n              \"--shared-content\",\n              {.idx = 0, .off = 0, .len = 16, .b = {1, 1}, .e = {1, 16}}),\n          entry_file_leave(1),\n          entry_term(\n              1,\n              \"--second\",\n              {.idx = 2, .off = 21, .len = 8, .b = {1, 22}, .e = {1, 29}}),\n          entry_file_found(\n              1,\n              \"shared.args\",\n              {.idx = 3, .off = 30, .len = 12, .b = {1, 31}, .e = {1, 42}}),\n          entry_file_enter(\n              1,\n              \"shared.args\",\n              {.idx = 3, .off = 30, .len = 12, .b = {1, 31}, .e = {1, 42}}),\n          entry_term(\n              2,\n              \"--shared-content\",\n              {.idx = 0, .off = 0, .len = 16, .b = {1, 1}, .e = {1, 16}}),\n          entry_file_leave(1),\n          entry_term(\n              1,\n              \"--third\",\n              {.idx = 4, .off = 43, .len = 7, .b = {1, 44}, .e = {1, 50}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\n// Tests for the simple cli_apply_args_files overload that returns vector and\n// throws\n\nTEST_F(ArgsTest, SimpleOverloadBasicArguments) {\n  // Test basic arguments without any @file references\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"--flag\",\n      \"value\",\n      \"arg\",\n  };\n\n  auto result = cli_apply_args_files(temp_dir(), args);\n\n  EXPECT_THAT(\n      result,\n      ElementsAreArray({\n          \"prog\",\n          \"--flag\",\n          \"value\",\n          \"arg\",\n      }));\n}\n\nTEST_F(ArgsTest, SimpleOverloadArgsFileExpansion) {\n  // Test that @file references are expanded\n  write_file(\"simple.args\", R\"(\n    --option1 value1 --option2\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@simple.args\",\n      \"trailing\",\n  };\n\n  auto result = cli_apply_args_files(temp_dir(), args);\n\n  EXPECT_THAT(\n      result,\n      ElementsAreArray({\n          \"prog\",\n          \"--option1\",\n          \"value1\",\n          \"--option2\",\n          \"trailing\",\n      }));\n}\n\nTEST_F(ArgsTest, SimpleOverloadNestedArgsFiles) {\n  // Test nested @file expansion\n  write_file(\"inner.args\", R\"(\n    --inner-flag inner_value\n  )\");\n\n  write_file(\"outer.args\", R\"(\n    --outer-flag @inner.args outer_value\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@outer.args\",\n  };\n\n  auto result = cli_apply_args_files(temp_dir(), args);\n\n  EXPECT_THAT(\n      result,\n      ElementsAreArray({\n          \"prog\",\n          \"--outer-flag\",\n          \"--inner-flag\",\n          \"inner_value\",\n          \"outer_value\",\n      }));\n}\n\nTEST_F(ArgsTest, SimpleOverloadDoubleAtEscape) {\n  // Test that @@ is escaped to @\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@@literal\",\n      \"@@file.args\",\n  };\n\n  auto result = cli_apply_args_files(temp_dir(), args);\n\n  EXPECT_THAT(\n      result,\n      ElementsAreArray({\n          \"prog\",\n          \"@literal\",\n          \"@file.args\",\n      }));\n}\n\nTEST_F(ArgsTest, SimpleOverloadThrowsOnMissingFile) {\n  // Test that missing file throws cli_apply_args_files_error\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@nonexistent.args\",\n  };\n\n  EXPECT_THROW(\n      cli_apply_args_files(temp_dir(), args), cli_apply_args_files_error);\n}\n\nTEST_F(ArgsTest, SimpleOverloadThrowsOnUnclosedQuote) {\n  // Test that unclosed quote in args file throws cli_apply_args_files_error\n  write_file(\"unclosed.args\", R\"(\n    --flag 'unclosed\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@unclosed.args\",\n  };\n\n  EXPECT_THROW(\n      cli_apply_args_files(temp_dir(), args), cli_apply_args_files_error);\n}\n\nTEST_F(ArgsTest, SimpleOverloadThrowsOnUnknownEscape) {\n  // Test that unknown escape sequence throws cli_apply_args_files_error\n  write_file(\"bad_escape.args\", R\"(\n    \\x\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@bad_escape.args\",\n  };\n\n  EXPECT_THROW(\n      cli_apply_args_files(temp_dir(), args), cli_apply_args_files_error);\n}\n\nTEST_F(ArgsTest, SimpleOverloadErrorMessageContainsFilename) {\n  // Test that error message contains the filename\n  write_file(\"error_file.args\", R\"(\n    'unclosed\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@error_file.args\",\n  };\n\n  try {\n    cli_apply_args_files(temp_dir(), args);\n    FAIL() << \"Expected cli_apply_args_files_error to be thrown\";\n  } catch (const cli_apply_args_files_error& e) {\n    std::string what = e.what();\n    EXPECT_THAT(what, testing::HasSubstr(\"error_file.args\"));\n    EXPECT_THAT(what, testing::HasSubstr(\"unclosed single quote\"));\n  }\n}\n\nTEST_F(ArgsTest, SimpleOverloadErrorMessageContainsLineCol) {\n  // Test that error message contains line and column info\n  write_file(\"line_col.args\", R\"(\n    --flag1 value1\n    --flag2 \"unclosed\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@line_col.args\",\n  };\n\n  try {\n    cli_apply_args_files(temp_dir(), args);\n    FAIL() << \"Expected cli_apply_args_files_error to be thrown\";\n  } catch (const cli_apply_args_files_error& e) {\n    std::string what = e.what();\n    // The unclosed quote is on line 2\n    EXPECT_THAT(what, testing::HasSubstr(\"line 2\"));\n  }\n}\n\n// Tests for circular reference detection\n\nTEST_F(ArgsTest, SimpleOverloadThrowsOnCircularReference) {\n  // Test that the simple overload throws on circular reference\n  write_file(\"circular.args\", R\"(\n    --before @circular.args --after\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@circular.args\",\n  };\n\n  EXPECT_THROW(\n      cli_apply_args_files(temp_dir(), args), cli_apply_args_files_error);\n}\n\nTEST_F(ArgsTest, SimpleOverloadCircularErrorMessage) {\n  // Test that circular reference error message is informative\n  write_file(\"loop.args\", R\"(\n    @loop.args\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@loop.args\",\n  };\n\n  try {\n    cli_apply_args_files(temp_dir(), args);\n    FAIL() << \"Expected cli_apply_args_files_error to be thrown\";\n  } catch (const cli_apply_args_files_error& e) {\n    std::string what = e.what();\n    EXPECT_THAT(what, testing::HasSubstr(\"cycle\"));\n    EXPECT_THAT(what, testing::HasSubstr(\"loop.args\"));\n  }\n}\n\n// Test cases for cli_apply_args_files_options max_depth\n\nTEST_F(ArgsTest, OptionsMaxDepthZeroSkipsAllFiles) {\n  write_file(\"file1.args\", R\"(\n    --from-file1\n  )\");\n  write_file(\"file2.args\", R\"(\n    --from-file2\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@file1.args\",\n      \"@file2.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args, {.max_depth = 0});\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"final\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          // on_file_found called, receiver returns dive, but library calls\n          // on_file_error due to max_depth\n          entry_file_found(0, \"file1.args\", idx(1)),\n          entry_file_error_max_depth(0, \"file1.args\", idx(1)),\n          entry_file_found(0, \"file2.args\", idx(2)),\n          entry_file_error_max_depth(0, \"file2.args\", idx(2)),\n          entry_term(0, \"final\", idx(3)),\n      }));\n}\n\nTEST_F(ArgsTest, OptionsMaxDepthOneAllowsTopLevelFiles) {\n  write_file(\"level1.args\", R\"(\n    --l1-start @level2.args --l1-end\n  )\");\n  write_file(\"level2.args\", R\"(\n    --l2-content\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@level1.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args, {.max_depth = 1});\n\n  // level1.args expanded (depth 0 -> 1), but level2.args skipped (depth 1 -> 2)\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--l1-start\",\n          \"--l1-end\",\n          \"final\",\n      }));\n\n  EXPECT_THAT(\n      receiver.entries,\n      ElementsAreArray({\n          entry_term(0, \"prog\", idx(0)),\n          entry_file_found(0, \"level1.args\", idx(1)),\n          entry_file_enter(0, \"level1.args\", idx(1)),\n          entry_term(\n              1,\n              \"--l1-start\",\n              {.idx = 0, .off = 0, .len = 10, .b = {1, 1}, .e = {1, 10}}),\n          // on_file_found called at depth 1, but library calls on_file_error\n          // due to max_depth\n          entry_file_found(\n              1,\n              \"level2.args\",\n              {.idx = 1, .off = 11, .len = 12, .b = {1, 12}, .e = {1, 23}}),\n          entry_file_error_max_depth(\n              1,\n              \"level2.args\",\n              {.idx = 1, .off = 11, .len = 12, .b = {1, 12}, .e = {1, 23}}),\n          entry_term(\n              1,\n              \"--l1-end\",\n              {.idx = 2, .off = 24, .len = 8, .b = {1, 25}, .e = {1, 32}}),\n          entry_file_leave(0),\n          entry_term(0, \"final\", idx(2)),\n      }));\n}\n\nTEST_F(ArgsTest, OptionsMaxDepthTwoAllowsTwoLevels) {\n  write_file(\"d1.args\", R\"(\n    --d1 @d2.args\n  )\");\n  write_file(\"d2.args\", R\"(\n    --d2 @d3.args\n  )\");\n  write_file(\"d3.args\", R\"(\n    --d3\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@d1.args\",\n      \"final\",\n  };\n\n  TestReceiver receiver;\n  cli_apply_args_files(receiver, temp_dir(), args, {.max_depth = 2});\n\n  // d1.args expanded (depth 0 -> 1), d2.args expanded (depth 1 -> 2),\n  // d3.args skipped (depth 2 -> 3)\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--d1\",\n          \"--d2\",\n          \"final\",\n      }));\n}\n\nTEST_F(ArgsTest, OptionsDefaultMaxDepthAllowsDeepNesting) {\n  // Default max_depth is 64, which should allow several levels of nesting\n  write_file(\"deep1.args\", R\"(\n    --d1 @deep2.args\n  )\");\n  write_file(\"deep2.args\", R\"(\n    --d2 @deep3.args\n  )\");\n  write_file(\"deep3.args\", R\"(\n    --d3 @deep4.args\n  )\");\n  write_file(\"deep4.args\", R\"(\n    --d4\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@deep1.args\",\n  };\n\n  TestReceiver receiver;\n  // Default options (max_depth = 64) — all 4 levels should expand\n  cli_apply_args_files(receiver, temp_dir(), args);\n\n  EXPECT_THAT(\n      receiver.strings,\n      ElementsAreArray({\n          \"prog\",\n          \"--d1\",\n          \"--d2\",\n          \"--d3\",\n          \"--d4\",\n      }));\n}\n\nTEST_F(ArgsTest, SimpleOverloadRespectsDefaultMaxDepth) {\n  // The simple overload uses default options (max_depth = 64)\n  // Create a chain of 3 levels — well within limit\n  write_file(\"s1.args\", R\"(\n    --s1 @s2.args\n  )\");\n  write_file(\"s2.args\", R\"(\n    --s2 @s3.args\n  )\");\n  write_file(\"s3.args\", R\"(\n    --s3\n  )\");\n\n  auto const args = std::vector<std::string>{\n      \"prog\",\n      \"@s1.args\",\n      \"end\",\n  };\n\n  auto result = cli_apply_args_files(temp_dir(), args);\n\n  EXPECT_THAT(\n      result,\n      ElementsAreArray({\n          \"prog\",\n          \"--s1\",\n          \"--s2\",\n          \"--s3\",\n          \"end\",\n      }));\n}\n"
  },
  {
    "path": "folly/cli/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"nested_command_line_app_test\",\n    srcs = [\"NestedCommandLineAppTest.cpp\"],\n    headers = [],\n    env = {\n        \"FOLLY_NESTED_CMDLINE_HELPER\": \"$(exe_target :nested_command_line_app_test_helper)\",\n    },\n    resources = [\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:subprocess\",\n        \"//folly/cli:program_options\",\n        \"//folly/io:fs_util\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"nested_command_line_app_test_helper\",\n    srcs = [\"NestedCommandLineAppTestHelper.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/cli:program_options\",\n        \"//folly/portability:gflags\",\n    ],\n    external_deps = [\n        # boost_atomic is required by boost_filesystem, which is required by\n        # boost_program_options, prior to C++20, for its implementation of\n        # atomic_ref; this is controlled by a preprocessory symbol\n        (\"boost\", None, \"boost_atomic\"),  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"program_options_test\",\n    srcs = [\"ProgramOptionsTest.cpp\"],\n    headers = [],\n    env = {\n        \"FOLLY_PROGRAM_OPTIONS_TEST_HELPER\": \"$(exe_target :program_options_test_helper)\",\n    },\n    deps = [\n        \"//folly:file_util\",\n        \"//folly:subprocess\",\n        \"//folly/cli:program_options\",\n        \"//folly/io:fs_util\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"program_options_test_helper\",\n    srcs = [\"ProgramOptionsTestHelper.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        # boost_atomic is required by boost_filesystem, which is required by\n        # boost_program_options, prior to C++20, for its implementation of\n        # atomic_ref; this is controlled by a preprocessory symbol\n        \"//folly:conv\",\n        \"//folly/cli:program_options\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_atomic\"),  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"args_test\",\n    srcs = [\"ArgsTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:string\",\n        \"//folly/cli:args\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"nested_command_line_app_example\",\n    srcs = [\"NestedCommandLineAppExample.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:scope_guard\",\n        \"//folly:string\",\n        \"//folly/cli:program_options\",\n    ],\n)\n"
  },
  {
    "path": "folly/cli/test/NestedCommandLineAppExample.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Example application using the nested command line parser.\n//\n// Implements two commands: \"cat\" and \"echo\", which behave similarly to their\n// Unix homonyms.\n\n#include <folly/ScopeGuard.h>\n#include <folly/String.h>\n#include <folly/cli/NestedCommandLineApp.h>\n#include <folly/cli/ProgramOptions.h>\n\nnamespace po = ::boost::program_options;\n\nnamespace {\n\nclass InputError : public std::runtime_error {\n public:\n  explicit InputError(const std::string& msg) : std::runtime_error(msg) {}\n};\n\nclass OutputError : public std::runtime_error {\n public:\n  explicit OutputError(const std::string& msg) : std::runtime_error(msg) {}\n};\n\nclass Concatenator {\n public:\n  explicit Concatenator(const po::variables_map& options)\n      : printLineNumbers_(options[\"number\"].as<bool>()) {}\n\n  void cat(const std::string& name);\n  void cat(FILE* file);\n\n private:\n  bool printLineNumbers_;\n  size_t lineNumber_ = 0;\n};\n\n// clang-format off\n[[noreturn]] void throwOutputError() {\n  throw OutputError(folly::errnoStr(errno));\n}\n\n[[noreturn]] void throwInputError() {\n  throw InputError(folly::errnoStr(errno));\n}\n// clang-format on\n\nvoid Concatenator::cat(FILE* file) {\n  char* lineBuf = nullptr;\n  size_t lineBufSize = 0;\n  SCOPE_EXIT {\n    free(lineBuf);\n  };\n\n  ssize_t n;\n  while ((n = getline(&lineBuf, &lineBufSize, file)) >= 0) {\n    ++lineNumber_;\n    if ((printLineNumbers_ && printf(\"%6zu  \", lineNumber_) < 0) ||\n        fwrite(lineBuf, 1, n, stdout) < size_t(n)) {\n      throwOutputError();\n    }\n  }\n\n  if (ferror(file)) {\n    throwInputError();\n  }\n}\n\nvoid Concatenator::cat(const std::string& name) {\n  auto file = fopen(name.c_str(), \"r\");\n  if (!file) {\n    throwInputError();\n  }\n\n  // Ignore error, as we might be processing an exception;\n  // during normal operation, we call fclose() directly further below\n  auto guard = folly::makeGuard([file] { fclose(file); });\n\n  cat(file);\n\n  guard.dismiss();\n  if (fclose(file)) {\n    throwInputError();\n  }\n}\n\nvoid runCat(\n    const po::variables_map& options, const std::vector<std::string>& args) {\n  Concatenator concatenator(options);\n  bool ok = true;\n  auto catFile = [&concatenator, &ok](const std::string& name) {\n    try {\n      if (name == \"-\") {\n        concatenator.cat(stdin);\n      } else {\n        concatenator.cat(name);\n      }\n    } catch (const InputError& e) {\n      ok = false;\n      fprintf(stderr, \"cat: %s: %s\\n\", name.c_str(), e.what());\n    }\n  };\n\n  try {\n    if (args.empty()) {\n      catFile(\"-\");\n    } else {\n      for (auto& name : args) {\n        catFile(name);\n      }\n    }\n  } catch (const OutputError& e) {\n    throw folly::ProgramExit(\n        1, folly::to<std::string>(\"cat: write error: \", e.what()));\n  }\n  if (!ok) {\n    throw folly::ProgramExit(1);\n  }\n}\n\nvoid runEcho(\n    const po::variables_map& options, const std::vector<std::string>& args) {\n  try {\n    const char* sep = \"\";\n    for (auto& arg : args) {\n      if (printf(\"%s%s\", sep, arg.c_str()) < 0) {\n        throw OutputError(folly::errnoStr(errno));\n      }\n      sep = \" \";\n    }\n    if (!options[\"-n\"].as<bool>()) {\n      if (putchar('\\n') == EOF) {\n        throw OutputError(folly::errnoStr(errno));\n      }\n    }\n  } catch (const OutputError& e) {\n    throw folly::ProgramExit(\n        1, folly::to<std::string>(\"echo: write error: \", e.what()));\n  }\n}\n\n} // namespace\n\nint main(int argc, char* argv[]) {\n  // Initialize a NestedCommandLineApp object.\n  //\n  // The first argument is the program name -- an empty string will cause the\n  // program name to be deduced from the executable name, which is usually\n  // fine. The second argument is a version string.\n  //\n  // You may also add an \"initialization function\" that is always called\n  // for every valid command before the command is executed.\n  folly::NestedCommandLineApp app(\"\", \"0.1\");\n\n  // Add any GFlags-defined flags. These are global flags, and so they should\n  // be valid for any command.\n  app.addGFlags();\n\n  // Add any commands. For our example, we'll implement simplified versions\n  // of \"cat\" and \"echo\". Note that addCommand() returns a reference to a\n  // boost::program_options object that you may use to add command-specific\n  // options.\n  // clang-format off\n  app.addCommand(\n      // command name\n      \"cat\",\n\n      // argument description\n      \"[file...]\",\n\n      // short help string\n      \"Concatenate files and print them on standard output\",\n\n      // Long help string\n      \"Concatenate files and print them on standard output.\",\n\n      // Function to execute\n      runCat)\n    .add_options()\n      (\"number,n\", po::bool_switch(), \"number all output lines\");\n  // clang-format on\n\n  // clang-format off\n  app.addCommand(\n      \"echo\",\n      \"[string...]\",\n      \"Display a line of text\",\n      \"Display a line of text.\",\n      runEcho)\n    .add_options()\n      (\",n\", po::bool_switch(), \"do not output the trailing newline\");\n  // clang-format on\n\n  // You may also add command aliases -- that is, multiple command names\n  // that do the same thing; see addAlias().\n\n  // app.run returns an appropriate error code\n  return app.run(argc, argv);\n}\n"
  },
  {
    "path": "folly/cli/test/NestedCommandLineAppTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/cli/NestedCommandLineApp.h>\n\n#include <folly/Subprocess.h>\n#include <folly/io/FsUtil.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\n#include <cstdlib>\n#include <glog/logging.h>\n\nusing namespace ::testing;\n\nnamespace folly {\nnamespace test {\n\nnamespace {\n\nstd::string getHelperPath() {\n  const auto* envPath = getenv(\"FOLLY_NESTED_CMDLINE_HELPER\");\n  if (envPath) {\n    return envPath;\n  }\n\n  const auto basename = \"nested_command_line_app_test_helper\";\n  auto path = fs::executable_path();\n  path.remove_filename() /= basename;\n  if (!fs::exists(path)) {\n    path = path.parent_path().parent_path() / basename / basename;\n  }\n  return path.string();\n}\n\nstd::string callHelper(\n    std::initializer_list<std::string> args,\n    int expectedExitCode = 0,\n    int stdoutFd = -1) {\n  static std::string helperPath = getHelperPath();\n\n  std::vector<std::string> allArgs;\n  allArgs.reserve(args.size() + 1);\n  allArgs.push_back(helperPath);\n  allArgs.insert(allArgs.end(), args.begin(), args.end());\n\n  Subprocess::Options options;\n  if (stdoutFd != -1) {\n    options.stdoutFd(stdoutFd);\n  } else {\n    options.pipeStdout();\n  }\n  options.pipeStderr();\n\n  Subprocess proc(allArgs, options);\n  auto p = proc.communicate();\n  EXPECT_EQ(expectedExitCode, proc.wait().exitStatus()) << p.second;\n\n  return p.first;\n}\n\n} // namespace\n\nTEST(ProgramOptionsTest, Errors) {\n  callHelper({}, 1);\n  callHelper({\"--wtf\", \"foo\"}, 1);\n  callHelper({\"qux\"}, 1);\n  callHelper({\"--global-foo\", \"x\", \"foo\"}, 1);\n}\n\nTEST(ProgramOptionsTest, Help) {\n  // Not actually checking help output, just verifying that help doesn't fail\n  callHelper({\"--version\"});\n  callHelper({\"-h\"});\n  callHelper({\"-h\", \"foo\"});\n  callHelper({\"-h\", \"bar\"});\n  callHelper({\"-h\", \"--\", \"bar\"});\n  callHelper({\"--help\"});\n  callHelper({\"--help\", \"foo\"});\n  callHelper({\"--help\", \"bar\"});\n  callHelper({\"--help\", \"--\", \"bar\"});\n  callHelper({\"help\"});\n  callHelper({\"help\", \"foo\"});\n  callHelper({\"help\", \"bar\"});\n\n  // wrong command name\n  callHelper({\"-h\", \"thunk\"}, 1);\n  callHelper({\"--help\", \"thunk\"}, 1);\n  callHelper({\"help\", \"thunk\"}, 1);\n\n  // anything after -- is parsed as arguments\n  callHelper({\"--\", \"help\", \"bar\"}, 1);\n}\n\nTEST(ProgramOptionsTest, DevFull) {\n  folly::File full(\"/dev/full\", O_RDWR);\n  callHelper({\"-h\"}, 1, full.fd());\n  callHelper({\"--help\"}, 1, full.fd());\n}\n\nTEST(ProgramOptionsTest, CutArguments) {\n  // anything after -- is parsed as arguments, including more --\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 43\\n\"\n      \"foo local-foo 42\\n\"\n      \"foo conflict-global 42\\n\"\n      \"foo conflict 42\\n\"\n      \"foo arg b\\n\"\n      \"foo arg --\\n\"\n      \"foo arg --local-foo\\n\"\n      \"foo arg 44\\n\"\n      \"foo arg a\\n\",\n      callHelper(\n          {\"foo\",\n           \"--global-foo\",\n           \"43\",\n           \"--\",\n           \"b\",\n           \"--\",\n           \"--local-foo\",\n           \"44\",\n           \"a\"}));\n}\n\nTEST(ProgramOptionsTest, Success) {\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 42\\n\"\n      \"foo local-foo 42\\n\"\n      \"foo conflict-global 42\\n\"\n      \"foo conflict 42\\n\",\n      callHelper({\"foo\"}));\n\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 43\\n\"\n      \"foo local-foo 44\\n\"\n      \"foo conflict-global 42\\n\"\n      \"foo conflict 42\\n\"\n      \"foo arg a\\n\"\n      \"foo arg b\\n\",\n      callHelper({\"--global-foo\", \"43\", \"foo\", \"--local-foo\", \"44\", \"a\", \"b\"}));\n\n  // Check that global flags can still be given after the command\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 43\\n\"\n      \"foo local-foo 44\\n\"\n      \"foo conflict-global 42\\n\"\n      \"foo conflict 42\\n\"\n      \"foo arg a\\n\"\n      \"foo arg b\\n\",\n      callHelper({\"foo\", \"--global-foo\", \"43\", \"--local-foo\", \"44\", \"a\", \"b\"}));\n}\n\nTEST(ProgramOptionsTest, Aliases) {\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 43\\n\"\n      \"foo local-foo 44\\n\"\n      \"foo conflict-global 42\\n\"\n      \"foo conflict 42\\n\"\n      \"foo arg a\\n\"\n      \"foo arg b\\n\",\n      callHelper({\"--global-foo\", \"43\", \"bar\", \"--local-foo\", \"44\", \"a\", \"b\"}));\n}\n\nTEST(ProgramOptionsTest, PositionalArgsSuccess) {\n  EXPECT_EQ(\n      \"running qux\\n\"\n      \"qux global-foo 43\\n\"\n      \"qux conflict-global 42\\n\"\n      \"qux optional-arg 41\\n\"\n      \"qux fred 44\\n\"\n      \"qux thud 21\\n\",\n      callHelper({\"--global-foo\", \"43\", \"qux\", \"44\", \"21\"}));\n\n  // Test with the optional arg and reposition the global flag after\n  // positional commands\n  EXPECT_EQ(\n      \"running qux\\n\"\n      \"qux global-foo 43\\n\"\n      \"qux conflict-global 42\\n\"\n      \"qux optional-arg 82\\n\"\n      \"qux fred 44\\n\"\n      \"qux thud 21\\n\",\n      callHelper(\n          {\"qux\", \"44\", \"21\", \"--global-foo\", \"43\", \"--optional-arg\", \"82\"}));\n\n  // Test specifying positional args via keyword\n  EXPECT_EQ(\n      \"running qux\\n\"\n      \"qux global-foo 43\\n\"\n      \"qux conflict-global 42\\n\"\n      \"qux optional-arg 82\\n\"\n      \"qux fred 21\\n\"\n      \"qux thud 11\\n\",\n      callHelper(\n          {\"qux\",\n           \"--optional-arg\",\n           \"82\",\n           \"--thud\",\n           \"11\",\n           \"--fred\",\n           \"21\",\n           \"--global-foo\",\n           \"43\"}));\n\n  // When a subcommand specifies positional args any extra positional\n  // args should cause an error\n  callHelper(\n      {\"qux\", \"44\", \"21\", \"--global-foo\", \"43\", \"--optional-arg\", \"82\", \"a\"},\n      1);\n  callHelper({\"--global-foo\", \"43\", \"qux\", \"44\", \"21\", \"a\"}, 1);\n  callHelper(\n      {\"--global-foo\", \"43\", \"qux\", \"--fred\", \"44\", \"--thud\", \"21\", \"10\"}, 1);\n}\n\nTEST(ProgramOptionsTest, BuiltinCommand) {\n  NestedCommandLineApp app;\n  ASSERT_TRUE(app.isBuiltinCommand(NestedCommandLineApp::kHelpCommand.str()));\n  ASSERT_TRUE(\n      app.isBuiltinCommand(NestedCommandLineApp::kVersionCommand.str()));\n  ASSERT_FALSE(app.isBuiltinCommand(\n      NestedCommandLineApp::kHelpCommand.str() + \"nonsense\"));\n}\n\nTEST(ProgramOptionsTest, ParseCallbacks) {\n  NestedCommandLineApp app;\n\n  std::vector<std::string> callbacks;\n  app.addCallback([&](auto&, auto&, auto&) { callbacks.emplace_back(\"1\"); });\n  app.addCallback([&](auto&, auto&, auto&) { callbacks.emplace_back(\"2\"); });\n\n  bool invoked = false;\n  app.addCommand(\"test\", \"\", \"\", \"\", [&](auto&, auto&) {\n    ASSERT_EQ((std::vector<std::string>{\"1\", \"2\"}), callbacks);\n    invoked = true;\n  });\n\n  std::vector<std::string> args{\"test\"};\n  app.run(args);\n\n  ASSERT_TRUE(invoked);\n  ASSERT_EQ((std::vector<std::string>{\"1\", \"2\"}), callbacks);\n}\n\nTEST(ProgramOptionsTest, ParseMissingRequiredOption) {\n  NestedCommandLineApp app;\n  auto& cmd = app.addCommand(\"test\", \"\", \"\", \"\", [&](auto&, auto&) {});\n  cmd.add_options()(\n      \"foo\", boost::program_options::value<std::string>()->required(), \"\");\n  std::vector<std::string> args{\"test\"};\n  testing::internal::CaptureStderr();\n  auto status = app.run(args);\n  auto err = testing::internal::GetCapturedStderr();\n  EXPECT_THAT(\n      err,\n      testing::HasSubstr(\n          \"Missing required option: '--foo'. Run 'nested_command_line_app_test test --help' for help\"));\n  ASSERT_EQ(1, status);\n}\n\nTEST(ProgramOptionsTest, ConflictingFlags) {\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 42\\n\"\n      \"foo local-foo 42\\n\"\n      \"foo conflict-global 43\\n\"\n      \"foo conflict 42\\n\",\n      callHelper({\"--conflict-global\", \"43\", \"foo\"}));\n\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 42\\n\"\n      \"foo local-foo 42\\n\"\n      \"foo conflict-global 43\\n\"\n      \"foo conflict 42\\n\",\n      callHelper({\"foo\", \"--conflict-global\", \"43\"}));\n\n  EXPECT_EQ(\n      \"running foo\\n\"\n      \"foo global-foo 42\\n\"\n      \"foo local-foo 42\\n\"\n      \"foo conflict-global 42\\n\"\n      \"foo conflict 43\\n\",\n      callHelper({\"foo\", \"--conflict\", \"43\"}));\n\n  callHelper({\"--conflict\", \"43\", \"foo\"}, 1);\n}\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/test/NestedCommandLineAppTestHelper.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/cli/NestedCommandLineApp.h>\n#include <folly/portability/GFlags.h>\n\nDEFINE_int32(global_foo, 42, \"Global foo\");\nDEFINE_int32(conflict_global, 42, \"Global conflict\");\n\nnamespace po = ::boost::program_options;\n\nnamespace {\n\nvoid init(\n    const std::string& cmd,\n    const po::variables_map& /* options */,\n    const std::vector<std::string>& /* args */) {\n  printf(\"running %s\\n\", cmd.c_str());\n}\n\nvoid foo(\n    const po::variables_map& options, const std::vector<std::string>& args) {\n  printf(\"foo global-foo %d\\n\", options[\"global-foo\"].as<int32_t>());\n  printf(\"foo local-foo %d\\n\", options[\"local-foo\"].as<int32_t>());\n  printf(\"foo conflict-global %d\\n\", options[\"conflict-global\"].as<int32_t>());\n  printf(\"foo conflict %d\\n\", options[\"conflict\"].as<int32_t>());\n  for (auto& arg : args) {\n    printf(\"foo arg %s\\n\", arg.c_str());\n  }\n}\n\nvoid qux(\n    const po::variables_map& options, const std::vector<std::string>& args) {\n  printf(\"qux global-foo %d\\n\", options[\"global-foo\"].as<int32_t>());\n  printf(\"qux conflict-global %d\\n\", options[\"conflict-global\"].as<int32_t>());\n  printf(\"qux optional-arg %d\\n\", options[\"optional-arg\"].as<int32_t>());\n  printf(\"qux fred %d\\n\", options[\"fred\"].as<int32_t>());\n  printf(\"qux thud %d\\n\", options[\"thud\"].as<int32_t>());\n  for (auto& arg : args) {\n    printf(\"qux arg %s\\n\", arg.c_str());\n  }\n}\n} // namespace\n\nint main(int argc, char* argv[]) {\n  folly::NestedCommandLineApp app(\"\", \"0.1\", \"\", \"\", init);\n\n  int style = po::command_line_style::default_style;\n  style &= ~po::command_line_style::allow_guessing;\n  app.setOptionStyle(static_cast<po::command_line_style::style_t>(style));\n\n  app.addGFlags();\n  // clang-format off\n  app.addCommand(\"foo\", \"[args...]\", \"Do some foo\", \"Does foo\", foo)\n    .add_options()\n      (\"local-foo\", po::value<int32_t>()->default_value(42), \"Local foo\")\n      (\"conflict\", po::value<int32_t>()->default_value(42), \"Local conflict\");\n\n  po::positional_options_description positionalArgs;\n  positionalArgs.add(\"fred\", /*max_count=*/1);\n  positionalArgs.add(\"thud\", /*max_count=*/1);\n  app.addCommand(\"qux\", \"[args...]\", \"Do some qux\", \"Does qux\", qux, positionalArgs)\n    .add_options()\n    (\"optional-arg\", po::value<int32_t>()->default_value(41), \"optional argument\")\n    (\"fred\", boost::program_options::value<int32_t>()->required(), \"fred positional arg\")\n    (\"thud\", boost::program_options::value<int32_t>()->required(), \"thud positional arg\");\n  // clang-format on\n  app.addAlias(\"bar\", \"foo\");\n  app.addAlias(\"baz\", \"bar\");\n  return app.run(argc, argv);\n}\n"
  },
  {
    "path": "folly/cli/test/ProgramOptionsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/cli/ProgramOptions.h>\n\n#include <folly/FileUtil.h>\n#include <folly/Subprocess.h>\n#include <folly/io/FsUtil.h>\n#include <folly/portability/GTest.h>\n\n#include <cstdlib>\n\nnamespace folly {\nnamespace test {\n\nnamespace {\n\nstd::string getHelperPath() {\n  const auto* envPath = getenv(\"FOLLY_PROGRAM_OPTIONS_TEST_HELPER\");\n  if (envPath) {\n    return envPath;\n  }\n\n  const auto basename = \"program_options_test_helper\";\n  auto path = fs::executable_path();\n  path.remove_filename() /= basename;\n  if (!fs::exists(path)) {\n    path = path.parent_path().parent_path() / basename / basename;\n  }\n  return path.string();\n}\n\nstd::string callHelper(\n    ProgramOptionsStyle style, std::initializer_list<std::string> args) {\n  static std::string helperPath = getHelperPath();\n\n  std::vector<std::string> allArgs;\n  allArgs.reserve(args.size() + 1);\n  allArgs.push_back(helperPath);\n  allArgs.insert(allArgs.end(), args.begin(), args.end());\n\n  std::vector<std::string> env;\n  switch (style) {\n    case ProgramOptionsStyle::GNU:\n      env.emplace_back(\"PROGRAM_OPTIONS_TEST_STYLE=GNU\");\n      break;\n    case ProgramOptionsStyle::GFLAGS:\n      env.emplace_back(\"PROGRAM_OPTIONS_TEST_STYLE=GFLAGS\");\n      break;\n  }\n\n  Subprocess proc(allArgs, Subprocess::Options().pipeStdout(), nullptr, &env);\n  auto p = proc.communicate();\n  EXPECT_EQ(0, proc.wait().exitStatus());\n\n  return p.first;\n}\n\n} // namespace\n\n// name value\n\nTEST(ProgramOptionsTest, GFlagsStyleDefaultValues) {\n  EXPECT_EQ(\n      \"flag_bool_true 1\\n\"\n      \"flag_bool_false 0\\n\"\n      \"flag_int 42\\n\"\n      \"flag_string foo\\n\",\n      callHelper(ProgramOptionsStyle::GFLAGS, {}));\n}\n\nTEST(ProgramOptionsTest, GFlagsStyleFlagsSet) {\n  EXPECT_EQ(\n      \"flag_bool_true 1\\n\"\n      \"flag_bool_false 1\\n\"\n      \"flag_int 43\\n\"\n      \"flag_string bar\\n\",\n      callHelper(\n          ProgramOptionsStyle::GFLAGS,\n          {\n              \"--flag_bool_true\",\n              \"--flag_bool_false\",\n              \"--flag_int\",\n              \"43\",\n              \"--flag_string\",\n              \"bar\",\n          }));\n}\n\nTEST(ProgramOptionsTest, GFlagsStyleBoolFlagsNegation) {\n  EXPECT_EQ(\n      \"flag_bool_true 0\\n\"\n      \"flag_bool_false 0\\n\"\n      \"flag_int 42\\n\"\n      \"flag_string foo\\n\",\n      callHelper(\n          ProgramOptionsStyle::GFLAGS,\n          {\n              \"--noflag_bool_true\",\n              \"--noflag_bool_false\",\n          }));\n}\n\nTEST(ProgramOptionsTest, GNUStyleDefaultValues) {\n  EXPECT_EQ(\n      \"flag-bool-true 1\\n\"\n      \"flag-bool-false 0\\n\"\n      \"flag-int 42\\n\"\n      \"flag-string foo\\n\",\n      callHelper(ProgramOptionsStyle::GNU, {}));\n}\n\nTEST(ProgramOptionsTest, GNUStyleFlagsSet) {\n  EXPECT_EQ(\n      \"flag-bool-true 1\\n\"\n      \"flag-bool-false 1\\n\"\n      \"flag-int 43\\n\"\n      \"flag-string bar\\n\",\n      callHelper(\n          ProgramOptionsStyle::GNU,\n          {\n              \"--flag-bool-true\",\n              \"--flag-bool-false\",\n              \"--flag-int\",\n              \"43\",\n              \"--flag-string\",\n              \"bar\",\n          }));\n}\n\nTEST(ProgramOptionsTest, GNUStyleBoolFlagsNegation) {\n  EXPECT_EQ(\n      \"flag-bool-true 0\\n\"\n      \"flag-bool-false 0\\n\"\n      \"flag-int 42\\n\"\n      \"flag-string foo\\n\",\n      callHelper(\n          ProgramOptionsStyle::GNU,\n          {\n              \"--no-flag-bool-true\",\n              \"--no-flag-bool-false\",\n          }));\n}\n\nTEST(ProgramOptionsTest, GNUStyleSubCommand) {\n  EXPECT_EQ(\n      \"flag-bool-true 1\\n\"\n      \"flag-bool-false 1\\n\"\n      \"flag-int 43\\n\"\n      \"flag-string foo\\n\"\n      \"command hello\\n\"\n      \"arg --wtf\\n\"\n      \"arg 100\\n\"\n      \"arg -x\\n\"\n      \"arg -xy\\n\",\n      callHelper(\n          ProgramOptionsStyle::GNU,\n          {\n              \"--flag-bool-false\",\n              \"hello\",\n              \"--wtf\",\n              \"--flag-int\",\n              \"43\",\n              \"100\",\n              \"-x\",\n              \"-xy\",\n          }));\n}\n\nTEST(ProgramOptionsTest, GNUStyleSubCommandUnrecognizedOptionFirst) {\n  EXPECT_EQ(\n      \"flag-bool-true 1\\n\"\n      \"flag-bool-false 1\\n\"\n      \"flag-int 43\\n\"\n      \"flag-string foo\\n\"\n      \"arg --wtf\\n\"\n      \"arg hello\\n\"\n      \"arg 100\\n\"\n      \"arg -x\\n\"\n      \"arg -xy\\n\",\n      callHelper(\n          ProgramOptionsStyle::GNU,\n          {\n              \"--flag-bool-false\",\n              \"--wtf\",\n              \"hello\",\n              \"--flag-int\",\n              \"43\",\n              \"100\",\n              \"-x\",\n              \"-xy\",\n          }));\n}\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/cli/test/ProgramOptionsTestHelper.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n\n#include <glog/logging.h>\n\n#include <folly/Conv.h>\n#include <folly/cli/ProgramOptions.h>\n\nDEFINE_bool(flag_bool_true, true, \"Bool with true default value\");\nDEFINE_bool(flag_bool_false, false, \"Bool with false default value\");\nDEFINE_int32(flag_int, 42, \"Integer flag\");\nDEFINE_string(flag_string, \"foo\", \"String flag\");\n\nnamespace po = ::boost::program_options;\n\nnamespace {\ntemplate <class T>\nvoid print(const po::variables_map& vm, const std::string& name) {\n  auto& v = vm[name];\n  printf(\"%s %s\\n\", name.c_str(), folly::to<std::string>(v.as<T>()).c_str());\n}\n} // namespace\n\nint main(int argc, char* argv[]) {\n  po::options_description desc;\n  auto styleEnv = getenv(\"PROGRAM_OPTIONS_TEST_STYLE\");\n\n  CHECK(styleEnv) << \"PROGRAM_OPTIONS_TEST_STYLE is required\";\n  bool gnuStyle = !strcmp(styleEnv, \"GNU\");\n  CHECK(gnuStyle || !strcmp(styleEnv, \"GFLAGS\"))\n      << \"Invalid value for PROGRAM_OPTIONS_TEST_STYLE\";\n\n  // clang-format off\n  desc.add(getGFlags(\n      gnuStyle ? folly::ProgramOptionsStyle::GNU :\n      folly::ProgramOptionsStyle::GFLAGS));\n  desc.add_options()\n    (\"help,h\", \"help\");\n  // clang-format on\n\n  po::variables_map vm;\n  auto result = folly::parseNestedCommandLine(argc, argv, desc);\n  po::store(result.options, vm);\n  po::notify(vm);\n\n  if (vm.contains(\"help\")) {\n    std::cout << desc;\n    return 1;\n  }\n\n  print<bool>(vm, gnuStyle ? \"flag-bool-true\" : \"flag_bool_true\");\n  print<bool>(vm, gnuStyle ? \"flag-bool-false\" : \"flag_bool_false\");\n  print<int32_t>(vm, gnuStyle ? \"flag-int\" : \"flag_int\");\n  print<std::string>(vm, gnuStyle ? \"flag-string\" : \"flag_string\");\n\n  if (result.command) {\n    printf(\"command %s\\n\", result.command->c_str());\n  }\n\n  for (auto& arg : result.rest) {\n    printf(\"arg %s\\n\", arg.c_str());\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/codec/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"hex\",\n    headers = [\"hex.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/lang:align\",\n        \"//folly/lang:assume\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"uuid\",\n    headers = [\"Uuid.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly/memory:uninitialized_memory_hacks\",\n    ],\n)\n"
  },
  {
    "path": "folly/codec/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME hex\n  HEADERS\n    hex.h\n  EXPORTED_DEPS\n    folly_lang_align\n    folly_lang_assume\n)\n\nfolly_add_library(\n  NAME uuid\n  HEADERS\n    Uuid.h\n  EXPORTED_DEPS\n    folly_memory_uninitialized_memory_hacks\n    folly_portability\n)\n"
  },
  {
    "path": "folly/codec/Uuid.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstring>\n#include <string>\n#include <string_view>\n\n#include <folly/Portability.h>\n#include <folly/memory/UninitializedMemoryHacks.h>\n\n#if FOLLY_X64\n#include <immintrin.h>\n#endif\n\nnamespace folly {\n\nenum class [[nodiscard]] UuidParseCode : unsigned char {\n  SUCCESS,\n  WRONG_LENGTH,\n  INVALID_CHAR,\n};\n\nnamespace detail {\ntemplate <auto buffer_to_buffer_func>\nFOLLY_ALWAYS_INLINE UuidParseCode\nuuid_parse_generic(std::string& out, std::string_view s) {\n  if (s.size() != 36) {\n    return UuidParseCode::WRONG_LENGTH;\n  }\n  folly::resizeWithoutInitialization(out, 16);\n  return buffer_to_buffer_func(out.data(), s.data());\n}\n\n#if FOLLY_X64 && defined(__AVX2__)\n\n// given a register full of hexadecimal digits (0-9, a-f, A-F),\n// compute a register where within each 16-byte lane, the first 8 bytes\n// have the values of the parsed 2-digit spans in the input.\n//\n// clang-format off\n// example:\n// in =  [790455cb98134298][87ec9ede1dc38e10] (ascii string starting with \"7904\")\n// out = [0x79, 0x04, 0x55, 0xcb, 0x98, 0x13, 0x42, 0x98, _, _, _, _, _, _, _, _]\n//       [0x87, 0xec, 0x9e, 0xde, 0x1d, 0xc3, 0x8e, 0x10, _, _, _, _, _, _, _, _]\n// where each '_' represents some arbitrary value\n// clang-format on\nFOLLY_ALWAYS_INLINE __m256i\nuuid_parse_parse_hex_without_validation_avx2(const __m256i in) {\n  const __m256i mask = _mm256_set1_epi8(0xf0);\n  const __m256i hi_nibble = _mm256_srli_epi16(_mm256_and_si256(in, mask), 4);\n  // 0-9 are 0x30-0x39, so we should subtract '0'\n  // A-F are 0x41-0x46, so we should subtract ('A' - 10)\n  // a-f are 0x61-0x66, so we should subtract ('a' - 10)\n  // clang-format off\n  const __m256i hi_nibble_to_offset = _mm256_setr_epi8(\n    0, 0, 0, '0', ('A' - 10), 0, ('a' - 10), 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, '0', ('A' - 10), 0, ('a' - 10), 0, 0, 0, 0, 0, 0, 0, 0, 0);\n  // clang-format on\n  const __m256i per_byte_offset =\n      _mm256_shuffle_epi8(hi_nibble_to_offset, hi_nibble);\n  const __m256i digits = _mm256_sub_epi8(in, per_byte_offset);\n\n  // shift left 12 bits\n  // that is, 0x01 0x02 0x03 0x04 -> 0x00 0x10 0x00 0x30\n  const __m256i out_hi_nibbles = _mm256_slli_epi16(digits, 12);\n  // 0x01 0x12 0x03 0x34 etc.\n  const __m256i out_combined = _mm256_or_si256(out_hi_nibbles, digits);\n  // clang-format off\n  const __m256i pack_odd = _mm256_setr_epi8(\n    1, 3, 5, 7, 9, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1, -1,\n    1, 3, 5, 7, 9, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1, -1);\n  // clang-format on\n  // 0x12 0x34 0x56 0x78 etc.\n  return _mm256_shuffle_epi8(out_combined, pack_odd);\n}\n\n// returns a bitmask of which input lanes were actually hex digits\nFOLLY_ALWAYS_INLINE unsigned int uuid_parse_validate_hex_avx2(\n    const __m256i in) {\n  const __m256i mask = _mm256_set1_epi8(0xf0);\n  const __m256i hi_nibble = _mm256_srli_epi16(_mm256_and_si256(in, mask), 4);\n  // We're using a technique based on \"Special case 1 — small sets\" from\n  // http://0x80.pl/notesen/2018-10-18-simd-byte-lookup.html#special-case-1-small-sets\n  // The technique is: actually this special case isn't for set of up to 8\n  // values. It's for a union U of up to 8 sets S_i where each set\n  // S_i = {all values with lo nibble in some set A and hi nibble in some set B}\n  // In our case U={0-9a-fA-F}, S_1={0x3}x{0x0-0x9}, S_2={0x4,0x6}x{0x1-0x6}\n  //\n  // Also, it's not necessary to extract the lower nibbles because none of the\n  // values we are looking for have the high bit set.\n  //\n  // clang-format off\n  const __m256i lo_nibble_lookup = _mm256_setr_epi8(\n    1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n    1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0);\n  const __m256i hi_nibble_lookup = _mm256_setr_epi8(\n    0, 0, 0, 1, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    0, 0, 0, 1, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n  // clang-format on\n\n  const __m256i hi_mask = _mm256_shuffle_epi8(hi_nibble_lookup, hi_nibble);\n  const __m256i lo_mask = _mm256_shuffle_epi8(lo_nibble_lookup, in);\n  const __m256i valid_input = _mm256_cmpgt_epi8(\n      _mm256_and_si256(lo_mask, hi_mask), _mm256_set1_epi8(0));\n  return _mm256_movemask_epi8(valid_input);\n}\n\nFOLLY_ALWAYS_INLINE UuidParseCode\nuuid_parse_buffer_to_buffer_avx2(char* out, const char* s) {\n  // clang-format off\n  // read the 36-byte input into two 32-byte values\n  // a = [790455cb-9813-42][98-87ec-9ede1dc3]\n  // b =     [55cb-9813-4298-8][7ec-9ede1dc38e10]\n  // clang-format on\n  const __m256i a = _mm256_loadu_si256((const __m256i_u*)(s + 0));\n  const __m256i b = _mm256_loadu_si256((const __m256i_u*)(s + 4));\n\n  // clang-format off\n  // merge the values into one, discarding the positions where we expect dashes\n  const __m256i shuffle_a = _mm256_setr_epi8(\n    0,  1,  2,  3,  4,  5,  6,  7,  9, 10, 11, 12, 14, 15, -1, -1,\n    3,  4,  5,  6,  8,  9, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1);\n  const __m256i shuffle_b = _mm256_setr_epi8(\n   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 13,\n   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 13, 14, 15);\n  // a  = [11111111-2222-33][44-5555-66666666]\n  // b  =     [1111-2222-3344-5][555-666666667777]\n  // sa = [11111111222233__][555566666666____]\n  // sb = [______________44][____________7777]\n  // c  = [1111111122223344][5555666666667777]\n  // clang-format on\n  const __m256i sa = _mm256_shuffle_epi8(a, shuffle_a);\n  const __m256i sb = _mm256_shuffle_epi8(b, shuffle_b);\n  const __m256i c = _mm256_or_si256(sa, sb);\n\n  // check that the dashes are in the right places in the input\n  const unsigned int my_dashes_mask =\n      _mm256_movemask_epi8(_mm256_cmpeq_epi8(a, _mm256_set1_epi8('-')));\n  const unsigned int desired_non_dashes_mask =\n      0b11111111011110111101111011111111u;\n  // dashes_mask should be all ones!\n  const unsigned int dashes_mask = my_dashes_mask ^ desired_non_dashes_mask;\n\n  const __m256i ret = uuid_parse_parse_hex_without_validation_avx2(c);\n  const unsigned int valid_hex_mask = uuid_parse_validate_hex_avx2(c);\n  if (~(dashes_mask & valid_hex_mask)) {\n    return UuidParseCode::INVALID_CHAR;\n  }\n\n  const unsigned long long retA = _mm256_extract_epi64(ret, 0);\n  const unsigned long long retB = _mm256_extract_epi64(ret, 2);\n  std::memcpy(out + 0, &retA, 8);\n  std::memcpy(out + 8, &retB, 8);\n\n  return UuidParseCode::SUCCESS;\n}\n\ninline UuidParseCode uuid_parse_avx2(std::string& out, std::string_view s) {\n  return uuid_parse_generic<uuid_parse_buffer_to_buffer_avx2>(out, s);\n}\n\n#endif // FOLLY_X64 && defined(__AVX2__)\n\n#if FOLLY_X64 && defined(__SSSE3__)\n\nFOLLY_ALWAYS_INLINE __m128i\nuuid_parse_parse_hex_without_validation_ssse3(const __m128i in) {\n  const __m128i mask = _mm_set1_epi8(0xf0);\n  const __m128i hi_nibble = _mm_srli_epi16(_mm_and_si128(in, mask), 4);\n  // clang-format off\n  const __m128i hi_nibble_to_offset = _mm_setr_epi8(\n    0, 0, 0, '0', ('A' - 10), 0, ('a' - 10), 0, 0, 0, 0, 0, 0, 0, 0, 0);\n  // clang-format on\n  const __m128i per_byte_offset =\n      _mm_shuffle_epi8(hi_nibble_to_offset, hi_nibble);\n  const __m128i digits = _mm_sub_epi8(in, per_byte_offset);\n\n  const __m128i out_hi_nibbles = _mm_slli_epi16(digits, 12);\n  const __m128i out_combined = _mm_or_si128(out_hi_nibbles, digits);\n  // clang-format off\n  const __m128i pack_odd = _mm_setr_epi8(\n    1, 3, 5, 7, 9, 11, 13, 15, -1, -1, -1, -1, -1, -1, -1, -1);\n  // clang-format on\n  return _mm_shuffle_epi8(out_combined, pack_odd);\n}\n\nFOLLY_ALWAYS_INLINE unsigned int uuid_parse_validate_hex_ssse3(\n    const __m128i in) {\n  const __m128i mask = _mm_set1_epi8(0xf0);\n  const __m128i hi_nibble = _mm_srli_epi16(_mm_and_si128(in, mask), 4);\n  // clang-format off\n  const __m128i lo_nibble_lookup = _mm_setr_epi8(\n    1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0);\n  const __m128i hi_nibble_lookup = _mm_setr_epi8(\n    0, 0, 0, 1, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n  // clang-format on\n\n  const __m128i hi_mask = _mm_shuffle_epi8(hi_nibble_lookup, hi_nibble);\n  const __m128i lo_mask = _mm_shuffle_epi8(lo_nibble_lookup, in);\n  const __m128i valid_input =\n      _mm_cmpgt_epi8(_mm_and_si128(lo_mask, hi_mask), _mm_set1_epi8(0));\n  return _mm_movemask_epi8(valid_input);\n}\n\nFOLLY_ALWAYS_INLINE UuidParseCode\nuuid_parse_buffer_to_buffer_ssse3(char* out, const char* s) {\n  // clang-format off\n  // read the 36-byte input into three 16-byte values\n  // a = [01234567-89ab-cd]\n  // b =         [-89ab-cdef-0123-]\n  // c =                     [123-456789abcdef]\n  // clang-format on\n  const __m128i a = _mm_loadu_si128((const __m128i_u*)(s + 0));\n  const __m128i b = _mm_loadu_si128((const __m128i_u*)(s + 8));\n  const __m128i c = _mm_loadu_si128((const __m128i_u*)(s + 20));\n\n  // clang-format off\n  // merge the values into one, discarding the positions where we expect dashes\n  const __m128i shuffle_b = _mm_setr_epi8(\n   11, 12, 13, 14, -1, -1, -1, -1, 1, 2, 3, 4, 6, 7, 8, 9);\n  // a  = [11111111-2222-33]\n  // b  =         [-2222-3344-5666-]\n  // c  =                     [666-777777777777]\n  // sb =             [5666____22223344]\n  // ascii_digits_a = [1111111122223344]\n  // ascii_digits_b = [5666777777777777]\n  // clang-format on\n  const __m128i sb = _mm_shuffle_epi8(b, shuffle_b);\n  const __m128i ascii_digits_a = _mm_castpd_si128( // no _mm_blend_epi64\n      _mm_blend_pd(_mm_castsi128_pd(a), _mm_castsi128_pd(sb), 0b10));\n  const __m128i ascii_digits_b = _mm_castps_si128( // _mm_blend_epi32 is AVX2\n      _mm_blend_ps(_mm_castsi128_ps(c), _mm_castsi128_ps(sb), 0b0001));\n\n  // check that the dashes are in the right places in the input\n  const unsigned int my_dashes_mask =\n      _mm_movemask_epi8(_mm_cmpeq_epi8(b, _mm_set1_epi8('-')));\n  const unsigned int desired_non_dashes_mask =\n      0b11111111111111110111101111011110u;\n  // dashes_mask should be all ones!\n  const unsigned int dashes_mask = my_dashes_mask ^ desired_non_dashes_mask;\n\n  const __m128i ret_a =\n      uuid_parse_parse_hex_without_validation_ssse3(ascii_digits_a);\n  const __m128i ret_b =\n      uuid_parse_parse_hex_without_validation_ssse3(ascii_digits_b);\n  const unsigned int valid_hex_mask_a =\n      uuid_parse_validate_hex_ssse3(ascii_digits_a);\n  const unsigned int valid_hex_mask_b =\n      uuid_parse_validate_hex_ssse3(ascii_digits_b);\n  const unsigned int valid_hex_mask =\n      (valid_hex_mask_a << 16) | valid_hex_mask_b;\n\n  if (~(dashes_mask & valid_hex_mask)) {\n    return UuidParseCode::INVALID_CHAR;\n  }\n\n  const unsigned long long ret_a_scalar = _mm_extract_epi64(ret_a, 0);\n  const unsigned long long ret_b_scalar = _mm_extract_epi64(ret_b, 0);\n  std::memcpy(out + 0, &ret_a_scalar, 8);\n  std::memcpy(out + 8, &ret_b_scalar, 8);\n  return UuidParseCode::SUCCESS;\n}\n\ninline UuidParseCode uuid_parse_ssse3(std::string& out, std::string_view s) {\n  return uuid_parse_generic<uuid_parse_buffer_to_buffer_ssse3>(out, s);\n}\n\n#endif // FOLLY_X64 && defined(__SSSE3__)\n\n// NOTE: add NEON or SVE?\n\n// scalar fallback\nconstexpr std::array<std::uint8_t, 256> generateValueTable() {\n  std::array<std::uint8_t, 256> table = {};\n  for (size_t i = 0; i < 256; ++i) {\n    if (i >= '0' && i <= '9') {\n      table[i] = static_cast<std::uint8_t>(i - '0');\n    } else if (i >= 'A' && i <= 'F') {\n      table[i] = static_cast<std::uint8_t>(i - 'A' + 10);\n    } else if (i >= 'a' && i <= 'f') {\n      table[i] = static_cast<std::uint8_t>(i - 'a' + 10);\n    } else {\n      table[i] = 16;\n    }\n  }\n  return table;\n}\ninline constexpr std::array<std::uint8_t, 256> value_table =\n    generateValueTable();\nstatic_assert(\n    value_table[0] == 16, \"value_table must be initialized at compile time\");\n\nFOLLY_ALWAYS_INLINE UuidParseCode\nuuid_parse_buffer_to_buffer_scalar(char* out, const char* s) {\n  int i = 0;\n  std::uint8_t tmp[32];\n  std::memcpy(tmp + 0, s + 0, 8);\n  std::memcpy(tmp + 8, s + 9, 4);\n  std::memcpy(tmp + 12, s + 14, 4);\n  std::memcpy(tmp + 16, s + 19, 4);\n  std::memcpy(tmp + 20, s + 24, 12);\n  if (s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') {\n    return UuidParseCode::INVALID_CHAR;\n  }\n  while (i < 32) {\n    const char hi = value_table[tmp[i++]];\n    const char lo = value_table[tmp[i++]];\n    if (hi == 16 || lo == 16) {\n      return UuidParseCode::INVALID_CHAR;\n    }\n    *(out++) = (hi << 4) | lo;\n  }\n  return UuidParseCode::SUCCESS;\n}\n\ninline UuidParseCode uuid_parse_scalar(std::string& out, std::string_view s) {\n  return uuid_parse_generic<uuid_parse_buffer_to_buffer_scalar>(out, s);\n}\n} // namespace detail\n\nFOLLY_ALWAYS_INLINE UuidParseCode\nuuid_parse_buffer_to_buffer(char* out, const char* s) {\n#if FOLLY_X64 && defined(__AVX2__)\n  return detail::uuid_parse_buffer_to_buffer_avx2(out, s);\n#elif FOLLY_X64 && defined(__SSSE3__)\n  return detail::uuid_parse_buffer_to_buffer_ssse3(out, s);\n#else\n  return detail::uuid_parse_buffer_to_buffer_scalar(out, s);\n#endif\n}\n\n// reads 36 bytes from s and writes 16 bytes to out\ninline UuidParseCode uuid_parse(std::uint8_t* out, const char* s) {\n  return uuid_parse_buffer_to_buffer((char*)out, s);\n}\n\n// checks that s is 36 bytes long and overwrites s with 16 bytes\ninline UuidParseCode uuid_parse(std::string& out, std::string_view s) {\n  return detail::uuid_parse_generic<uuid_parse_buffer_to_buffer>(out, s);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/codec/hex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <string_view>\n\n#include <folly/lang/Align.h>\n#include <folly/lang/Assume.h>\n\nnamespace folly {\n\n/// hex_alphabet_lower\nconstexpr auto hex_alphabet_lower = std::string_view(\"0123456789abcdef\");\n\n/// hex_alphabet_upper\nconstexpr auto hex_alphabet_upper = std::string_view(\"0123456789ABCDEF\");\n\nnamespace detail {\n\nconstexpr std::array<uint8_t, 256> make_hex_alphabet_table() {\n  std::array<uint8_t, 256> array{};\n  for (auto& v : array) {\n    v = ~0;\n  }\n  for (uint8_t i = 0; i < hex_alphabet_lower.size(); ++i) {\n    array[hex_alphabet_lower[i]] = i;\n  }\n  for (uint8_t i = 0; i < hex_alphabet_upper.size(); ++i) {\n    array[hex_alphabet_upper[i]] = i;\n  }\n  return array;\n}\n\n} // namespace detail\n\n/// hex_alphabet_table\nalignas(hardware_constructive_interference_size) //\n    constexpr auto hex_alphabet_table = detail::make_hex_alphabet_table();\n\n/// hex_decoded_digit_is_valid\nconstexpr bool hex_decoded_digit_is_valid(uint8_t const v) {\n  return !(v & 0x80);\n}\n\n/// hex_is_digit_table\nconstexpr bool hex_is_digit_table(char const h) {\n  return int8_t(hex_alphabet_table[uint8_t(h)]) >= 0;\n}\n\n/// hex_decode_digit_table\n///\n/// For characters which are in the lower or upper hex alphabets, returns the\n/// decoded value. For other characters, returns a value with the high bit set.\nconstexpr uint8_t hex_decode_digit_table(char const h) {\n  return hex_alphabet_table[uint8_t(h)];\n}\n\n/// hex_is_digit_flavor_x86_64\n///\n/// from: https://github.com/stedonet/chex\nconstexpr bool hex_is_digit_flavor_x86_64(char const h) {\n  uint8_t const uh = uint8_t(h);\n  uint8_t const n09 = uh - '0';\n  uint8_t const nAF = (uh | 0x20) - 'a';\n  return (n09 < 10) || (nAF < 6);\n}\n\n/// hex_decode_digit_raw_flavor_x86_64\n///\n/// from: https://github.com/stedonet/chex\nconstexpr uint8_t hex_decode_digit_raw_flavor_x86_64(char const h) {\n  return (h + (h >> 6) * 9) & 0xf;\n}\n\n/// hex_decode_digit_flavor_x86_64\n///\n/// from: https://github.com/stedonet/chex\nconstexpr uint8_t hex_decode_digit_flavor_x86_64(char const h) {\n  if (!hex_is_digit_flavor_x86_64(h)) {\n    return ~0;\n  }\n  auto const raw = hex_decode_digit_raw_flavor_x86_64(h);\n  assume(!(raw & 0xf0));\n  return raw;\n}\n\n/// hex_is_digit_flavor_aarch64\nconstexpr bool hex_is_digit_flavor_aarch64(char const h) {\n  auto const uh = uint8_t(h);\n  auto const cond = uh <= '9';\n  auto const base = uint8_t(cond ? uh - '0' : (uh | 0x20) - 'a');\n  auto const bound = uint8_t(cond ? 10 : 6);\n  return base < bound;\n}\n\n/// hex_decode_digit_raw_flavor_aarch64\nconstexpr uint8_t hex_decode_digit_raw_flavor_aarch64(char const h) {\n  auto const uh = uint8_t(h);\n  auto const cond = uh <= '9';\n  auto const base = uint8_t(cond ? uh - '0' : (uh | 0x20) - 'a');\n  auto const offset = uint8_t(cond ? 0 : 10);\n  return base + offset;\n}\n\n/// hex_decode_digit_flavor_aarch64\nconstexpr uint8_t hex_decode_digit_flavor_aarch64(char const h) {\n  if (!hex_is_digit_flavor_aarch64(h)) {\n    return ~0;\n  }\n  auto const raw = hex_decode_digit_raw_flavor_aarch64(h);\n  assume(!(raw & 0xf0));\n  return raw;\n}\n\n/// hex_is_digit\nconstexpr bool hex_is_digit(char const h) {\n  return kIsArchAArch64 //\n      ? hex_is_digit_flavor_aarch64(h)\n      : hex_is_digit_flavor_x86_64(h);\n}\n\n/// hex_decode_digit_raw\n///\n/// For characters which are in the lower or upper hex alphabets, returns the\n/// decoded value. For other characters, the behavior is unspecified and any\n/// value may be returned. This function cannot be used to distinguish between\n/// characters in and outside of the alphabets.\nconstexpr uint8_t hex_decode_digit_raw(char const h) {\n  return kIsArchAArch64 //\n      ? hex_decode_digit_raw_flavor_aarch64(h)\n      : hex_decode_digit_raw_flavor_x86_64(h);\n}\n\n/// hex_decode_digit\n///\n/// For characters which are in the lower or upper hex alphabets, returns the\n/// decoded value. For other characters, returns a value with the high bit set.\nconstexpr uint8_t hex_decode_digit(char const h) {\n  return kIsArchAArch64 //\n      ? hex_decode_digit_flavor_aarch64(h)\n      : hex_decode_digit_flavor_x86_64(h);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/codec/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\nload(\"../../defs.bzl\", \"folly_xplat_cxx_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_unittest(\n    name = \"hex_test\",\n    srcs = [\"hex_test.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly:likely\",\n        \"//folly/codec:hex\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"uuid_test\",\n    srcs = [\"UuidTest.cpp\"],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//third-party/fmt:fmt\",\n        \"//xplat/folly:random\",\n        \"//xplat/folly/codec:uuid\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"uuid_test\",\n    srcs = [\"UuidTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:random\",\n        \"//folly/codec:uuid\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"uuid_benchmark\",\n    srcs = [\"UuidBenchmark.cpp\"],\n    compiler_flags = [\"-flto\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/codec:uuid\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_lexical_cast\"),\n        (\"boost\", None, \"boost_uuid\"),\n        (\"util-linux\", None, \"uuid\"),\n    ],\n)\n"
  },
  {
    "path": "folly/codec/test/UuidBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n\n#include <string>\n#include <boost/lexical_cast.hpp>\n#include <boost/uuid/uuid.hpp>\n#include <boost/uuid/uuid_io.hpp>\n#include <uuid/uuid.h>\n\n#include <folly/codec/Uuid.h>\n\nstatic std::string generateRandomGuid() {\n  char guid[40];\n  uuid_t uuid_raw;\n  uuid_generate(uuid_raw);\n  uuid_unparse_lower(uuid_raw, guid);\n\n  return guid;\n}\n\ntemplate <auto folly_uuid_parse_func>\ninline void folly_uuid_parse_benchmark(size_t n) {\n  std::vector<std::string> sVec;\n  BENCHMARK_SUSPEND {\n    sVec.resize(n);\n    for (auto& s : sVec) {\n      s = generateRandomGuid();\n    }\n  }\n\n  std::string out;\n  for (size_t i = 0; i < n; ++i) {\n    folly::compiler_must_not_elide(folly_uuid_parse_func(out, sVec[i]));\n    folly::compiler_must_not_elide(out);\n  }\n}\n\n#if (FOLLY_X64 && defined(__AVX2__))\nBENCHMARK(uuid_parse_folly_avx2, n) {\n  folly_uuid_parse_benchmark<folly::detail::uuid_parse_avx2>(n);\n}\n#endif\n\n#if (FOLLY_X64 && defined(__SSSE3__))\nBENCHMARK(uuid_parse_folly_ssse3, n) {\n  folly_uuid_parse_benchmark<folly::detail::uuid_parse_ssse3>(n);\n}\n#endif\n\nBENCHMARK(uuid_parse_folly_scalar, n) {\n  folly_uuid_parse_benchmark<folly::detail::uuid_parse_scalar>(n);\n}\n\nBENCHMARK(uuid_parse_folly, n) {\n  using StrParseFunc = folly::UuidParseCode (*)(std::string&, std::string_view);\n  folly_uuid_parse_benchmark<static_cast<StrParseFunc>(folly::uuid_parse)>(n);\n}\n\nBENCHMARK(uuid_parse_glibc, n) {\n  std::vector<std::string> sVec;\n  BENCHMARK_SUSPEND {\n    sVec.resize(n);\n    for (auto& s : sVec) {\n      s = generateRandomGuid();\n    }\n  }\n  uuid_t uuid;\n  for (size_t i = 0; i < n; ++i) {\n    folly::compiler_must_not_elide(uuid_parse(sVec[i].c_str(), uuid));\n    folly::compiler_must_not_elide(uuid);\n  }\n}\n\nBENCHMARK(uuid_parse_boost, n) {\n  std::vector<std::string> sVec;\n  BENCHMARK_SUSPEND {\n    sVec.resize(n);\n    for (auto& s : sVec) {\n      s = generateRandomGuid();\n    }\n  }\n  boost::uuids::uuid uuid;\n  for (size_t i = 0; i < n; ++i) {\n    folly::compiler_must_not_elide(\n        uuid = boost::lexical_cast<boost::uuids::uuid>(sVec[i]));\n    folly::compiler_must_not_elide(uuid);\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/codec/test/UuidTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cctype>\n#include <cstdlib>\n#include <string>\n#include <string_view>\n#include <fmt/format.h>\n\n#include <folly/Random.h>\n#include <folly/codec/Uuid.h>\n#include <folly/portability/GTest.h>\n\n// Define a struct to hold our parse function pointers\nstruct UuidParseTestParam {\n  using ParseFuncType = folly::UuidParseCode(std::string&, std::string_view);\n\n  ParseFuncType* func;\n  std::string name;\n\n  UuidParseTestParam(ParseFuncType f, std::string n)\n      : func(f), name(std::move(n)) {}\n};\n\n// Define the test fixture\nclass UuidParseTest : public ::testing::TestWithParam<UuidParseTestParam> {\n public:\n  auto parse(std::string& output, const std::string& input) {\n    return GetParam().func(output, input);\n  }\n};\n\n// Test cases\nTEST_P(UuidParseTest, ValidUuid) {\n  std::string input = \"123e4567-e89b-12d3-a456-4266141740fc\";\n  ASSERT_EQ(36, input.size()); // 36 characters - the right length\n  auto expected = std::array<char, 16>{};\n  std::memcpy(\n      expected.data(),\n      \"\\x12\\x3e\\x45\\x67\\xe8\\x9b\\x12\\xd3\\xa4\\x56\\x42\\x66\\x14\\x17\\x40\\xfc\",\n      16);\n  std::string output{};\n  folly::UuidParseCode result = parse(output, input);\n  EXPECT_EQ(result, folly::UuidParseCode::SUCCESS);\n  EXPECT_EQ(output.size(), 16);\n  std::array<char, 16> ot{};\n  std::memcpy(ot.data(), output.data(), 16);\n  EXPECT_EQ(ot, expected);\n}\n\nTEST_P(UuidParseTest, InvalidUuidTooShort) {\n  std::string input = \"123e4567-e89b-12d3-a456-42661417400\";\n  ASSERT_EQ(35, input.size()); // 35 characters - too short\n  std::string output{};\n  folly::UuidParseCode result = parse(output, input);\n  EXPECT_EQ(result, folly::UuidParseCode::WRONG_LENGTH);\n}\n\nTEST_P(UuidParseTest, InvalidUuidTooLong) {\n  std::string input = \"123e4567-e89b-12d3-a456-4266141740000\";\n  ASSERT_EQ(37, input.size()); // 37 characters - too long\n  std::string output{};\n  folly::UuidParseCode result = parse(output, input);\n  EXPECT_EQ(result, folly::UuidParseCode::WRONG_LENGTH);\n}\n\nTEST_P(UuidParseTest, InvalidUuidWrongDashes) {\n  std::string input =\n      \"123e4567e-89b-12d3-a456-426614174000\"; // dash moved over by 1 byte\n  ASSERT_EQ(36, input.size()); // 36 characters - the right length\n  std::string output{};\n  folly::UuidParseCode result = parse(output, input);\n  EXPECT_EQ(result, folly::UuidParseCode::INVALID_CHAR);\n}\n\nTEST_P(UuidParseTest, InvalidUuidInvalidCharacters) {\n  const std::string good_input = \"123e4567-e89b-12d3-a456-42661417400f\";\n  std::string test_input{};\n  std::string output{};\n\n  ASSERT_EQ(36, good_input.size()); // 36 characters - the right length\n  EXPECT_EQ(parse(output, good_input), folly::UuidParseCode::SUCCESS)\n      << \"Good input wasn't actually good\";\n\n  // For each position in the UUID string\n  for (size_t i = 0; i < good_input.length(); ++i) {\n    test_input = good_input;\n\n    if (good_input[i] == '-') {\n      // For hyphen positions, try all non-hyphen bytes\n      for (int c = 0; c <= 255; ++c) {\n        if (c != '-') {\n          test_input[i] = static_cast<unsigned char>(c);\n          ASSERT_EQ(36, test_input.size()); // 36 characters - the right length\n          EXPECT_EQ(\n              parse(output, test_input), folly::UuidParseCode::INVALID_CHAR)\n              << \"Failed for position \" << i << \" with byte value \" << c;\n        }\n      }\n    } else {\n      // For hex positions, try all non-hex bytes\n      for (int c = 0; c <= 255; ++c) {\n        if (!isxdigit(static_cast<unsigned char>(c))) {\n          test_input[i] = static_cast<unsigned char>(c);\n          ASSERT_EQ(36, test_input.size()); // 36 characters - the right length\n          EXPECT_EQ(\n              parse(output, test_input), folly::UuidParseCode::INVALID_CHAR)\n              << \"Failed for position \" << i << \" with byte value \" << c;\n        }\n      }\n    }\n  }\n}\n\n// Create a vector of parse functions to test\nstatic std::vector<UuidParseTestParam> getUuidParseTestParams() {\n  std::vector<UuidParseTestParam> functions;\n\n  // Always add the scalar implementation\n  functions.emplace_back(folly::detail::uuid_parse_scalar, \"Scalar\");\n\n  // Add the default implementation\n  using StrParseFunc = folly::UuidParseCode (*)(std::string&, std::string_view);\n  functions.emplace_back(\n      static_cast<StrParseFunc>(folly::uuid_parse), \"Default\");\n\n  // Conditionally add SIMD implementations\n#if (FOLLY_X64 && defined(__AVX2__))\n  functions.emplace_back(folly::detail::uuid_parse_avx2, \"AVX2\");\n#endif\n\n#if (FOLLY_X64 && defined(__SSSE3__))\n  functions.emplace_back(folly::detail::uuid_parse_ssse3, \"SSSE3\");\n#endif\n\n  return functions;\n}\n\n// Instantiate the test suite\nINSTANTIATE_TEST_SUITE_P(\n    UuidParsers,\n    UuidParseTest,\n    ::testing::ValuesIn(getUuidParseTestParams()),\n    [](const ::testing::TestParamInfo<UuidParseTestParam>& info_) {\n      return info_.param.name;\n    });\n\n// Additional tests for comparing implementations\n#if (FOLLY_X64 && (defined(__SSSE3__) || defined(__AVX2__)))\nstatic std::string generateRandomGuid() {\n  // Generate random bytes of the form 123e4567-e89b-12d3-a456-4266141740fc\n  uint64_t a = folly::Random::rand64() & 0xffffffff;\n  uint64_t b = folly::Random::rand64() & 0xffff;\n  uint64_t c = folly::Random::rand64() & 0xffff;\n  uint64_t d = folly::Random::rand64() & 0xffff;\n  uint64_t e = folly::Random::rand64() & 0xffffffffffff;\n\n  // Format with proper UUID structure\n  if (folly::Random::oneIn(2)) {\n    return fmt::format(\"{:08X}-{:04X}-{:04X}-{:04X}-{:012X}\", a, b, c, d, e);\n  } else {\n    return fmt::format(\"{:08x}-{:04x}-{:04x}-{:04x}-{:012x}\", a, b, c, d, e);\n  }\n}\n\n// Test fixture for comparing SIMD implementations to scalar\nclass UuidParseComparisonTest\n    : public ::testing::TestWithParam<UuidParseTestParam> {};\n\nTEST_P(UuidParseComparisonTest, CompareToScalar) {\n  auto simd_parse_func = GetParam().func;\n\n  for (int i = 0; i < 1000; i++) {\n    std::string test_input = generateRandomGuid();\n    std::string scalar_output{};\n    std::string simd_output{};\n\n    EXPECT_EQ(\n        folly::detail::uuid_parse_scalar(scalar_output, test_input),\n        folly::UuidParseCode::SUCCESS);\n    EXPECT_EQ(\n        simd_parse_func(simd_output, test_input),\n        folly::UuidParseCode::SUCCESS);\n    EXPECT_EQ(scalar_output, simd_output);\n  }\n}\n\n// Get SIMD implementations only for comparison tests\nstatic std::vector<UuidParseTestParam> getSimdParseFunctions() {\n  std::vector<UuidParseTestParam> functions;\n\n#if (FOLLY_X64 && defined(__AVX2__))\n  functions.emplace_back(folly::detail::uuid_parse_avx2, \"AVX2\");\n#endif\n\n#if (FOLLY_X64 && defined(__SSSE3__))\n  functions.emplace_back(folly::detail::uuid_parse_ssse3, \"SSSE3\");\n#endif\n\n  return functions;\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    SimdVsScalar,\n    UuidParseComparisonTest,\n    ::testing::ValuesIn(getSimdParseFunctions()),\n    [](const ::testing::TestParamInfo<UuidParseTestParam>& info_) {\n      return \"CompareScalarTo\" + info_.param.name;\n    });\n#endif\n"
  },
  {
    "path": "folly/codec/test/hex_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/codec/hex.h>\n\n#include <folly/Likely.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals;\n\nextern \"C\" FOLLY_KEEP bool check_folly_hex_is_digit_table(char h) {\n  return folly::hex_is_digit_table(h);\n}\n\nextern \"C\" FOLLY_KEEP bool check_folly_hex_is_digit_flavor_aarch64(char h) {\n  return folly::hex_is_digit_flavor_aarch64(h);\n}\n\nextern \"C\" FOLLY_KEEP bool check_folly_hex_is_digit_flavor_x86_64(char h) {\n  return folly::hex_is_digit_flavor_x86_64(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_raw_flavor_aarch64(char h) {\n  return folly::hex_decode_digit_raw_flavor_aarch64(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_raw_flavor_x86_64(char h) {\n  return folly::hex_decode_digit_raw_flavor_x86_64(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t check_folly_hex_decode_digit_table(char h) {\n  return folly::hex_decode_digit_table(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_flavor_aarch64(char h) {\n  return folly::hex_decode_digit_flavor_aarch64(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_flavor_x86_64(char h) {\n  return folly::hex_decode_digit_flavor_x86_64(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_table_or_sink(char h) {\n  auto const v = folly::hex_decode_digit_table(h);\n  if (FOLLY_UNLIKELY(!folly::hex_decoded_digit_is_valid(v))) {\n    folly::detail::keep_sink_nx();\n  }\n  return v;\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_flavor_aarch64_or_sink(char h) {\n  auto const v = folly::hex_decode_digit_flavor_aarch64(h);\n  if (FOLLY_UNLIKELY(!folly::hex_decoded_digit_is_valid(v))) {\n    folly::detail::keep_sink_nx();\n  }\n  return v;\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_flavor_x86_64_or_sink(char h) {\n  auto const v = folly::hex_decode_digit_flavor_x86_64(h);\n  if (FOLLY_UNLIKELY(!folly::hex_decoded_digit_is_valid(v))) {\n    folly::detail::keep_sink_nx();\n  }\n  return v;\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_table_or_sink_split(char h) {\n  if (FOLLY_UNLIKELY(!folly::hex_is_digit_table(h))) {\n    folly::detail::keep_sink_nx();\n    return ~0;\n  }\n  return folly::hex_decode_digit_table(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_flavor_aarch64_or_sink_split(char h) {\n  if (FOLLY_UNLIKELY(!folly::hex_is_digit_flavor_aarch64(h))) {\n    folly::detail::keep_sink_nx();\n    return ~0;\n  }\n  return folly::hex_decode_digit_raw_flavor_aarch64(h);\n}\n\nextern \"C\" FOLLY_KEEP uint8_t\ncheck_folly_hex_decode_digit_flavor_x86_64_or_sink_split(char h) {\n  if (FOLLY_UNLIKELY(!folly::hex_is_digit_flavor_x86_64(h))) {\n    folly::detail::keep_sink_nx();\n    return ~0;\n  }\n  return folly::hex_decode_digit_raw_flavor_x86_64(h);\n}\n\nstruct HexTest : testing::Test {};\n\nTEST_F(HexTest, hex_decode_digit_lower_all) {\n  constexpr auto alpha = folly::hex_alphabet_lower;\n  for (size_t i = 0; i < alpha.size(); ++i) {\n    auto const h = alpha[i];\n    EXPECT_EQ(i, folly::hex_decode_digit(h));\n    EXPECT_EQ(i, folly::hex_decode_digit_table(h));\n    EXPECT_EQ(i, folly::hex_decode_digit_flavor_aarch64(h));\n    EXPECT_EQ(i, folly::hex_decode_digit_flavor_x86_64(h));\n  }\n}\n\nTEST_F(HexTest, hex_decode_digit_upper_all) {\n  constexpr auto alpha = folly::hex_alphabet_upper;\n  for (size_t i = 0; i < alpha.size(); ++i) {\n    auto const h = alpha[i];\n    EXPECT_EQ(i, folly::hex_decode_digit(h));\n    EXPECT_EQ(i, folly::hex_decode_digit_table(h));\n    EXPECT_EQ(i, folly::hex_decode_digit_flavor_aarch64(h));\n    EXPECT_EQ(i, folly::hex_decode_digit_flavor_x86_64(h));\n  }\n}\n\nTEST_F(HexTest, hex_decode_digit_full) {\n  for (size_t i = 0; i < 256; ++i) {\n    auto const c = static_cast<char>(i);\n    auto const d = folly::hex_is_digit_table(c);\n    EXPECT_EQ(d, folly::hex_is_digit(c));\n    EXPECT_EQ(d, folly::hex_is_digit_flavor_aarch64(c));\n    EXPECT_EQ(d, folly::hex_is_digit_flavor_x86_64(c));\n    auto const h = folly::hex_alphabet_table[i];\n    EXPECT_EQ(h, folly::hex_decode_digit(c));\n    EXPECT_EQ(h, folly::hex_decode_digit_flavor_aarch64(c));\n    EXPECT_EQ(h, folly::hex_decode_digit_flavor_x86_64(c));\n  }\n}\n"
  },
  {
    "path": "folly/compression/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"compression\",\n    srcs = [\n        \"Compression.cpp\",\n        \"Zlib.cpp\",\n        \"Zstd.cpp\",\n    ],\n    compiler_flags = [\n        \"-DLZ4_STATIC_LINKING_ONLY\",\n        \"-DLZ4_HC_STATIC_LINKING_ONLY\",\n        \"-DLZ4F_STATIC_LINKING_ONLY\",\n    ],\n    fbcode_deps = [\"fbsource//third-party/glog:glog\"],\n    fbcode_external_deps = [\n        \"snappy\",\n        (\"bzip2\", None, \"bz2\"),\n        (\"xz\", None, \"lzma\"),\n    ],\n    raw_headers = [\n        \"Compression.h\",\n        \"Utils.h\",\n        \"Zlib.h\",\n        \"Zstd.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/lz4:lz4\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \":compression_context_pool_singletons\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:random\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:stop_watch\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly:varint\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux\": [\n            \"//third-party/bzip2:bzlib2\",\n            \"//xplat/third-party/snappy:snappy\",\n            \"//xplat/third-party/xz:lzma\",\n        ],\n        \"ovr_config//os:windows\": [\n            \"//third-party/bzip2:bzlib2\",\n            \"//xplat/third-party/snappy:snappy\",\n            \"//xplat/third-party/xz:lzma\",\n        ],\n    }),\n    exported_deps = [\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/third-party/linker_lib:z\",\n        \"//xplat/third-party/zstd:zstd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"compression_context_pool\",\n    srcs = [],\n    headers = [\n        \"CompressionContextPool.h\",\n        \"CompressionCoreLocalContextPool.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:memory\",\n        \"//folly:synchronized\",\n        \"//folly/concurrency:cache_locality\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"compression_context_pool_singletons\",\n    srcs = [\"CompressionContextPoolSingletons.cpp\"],\n    cxx_deps = [\n        \"//xplat/folly/memory:jemalloc_huge_page_allocator\",\n    ],\n    fbandroid_preprocessor_flags = [\n        \"-DFOLLY_COMPRESSION_USE_HUGEPAGES=0\",\n    ],\n    fbcode_deps = [\n        \"//xplat/folly/memory:jemalloc_huge_page_allocator\",\n    ],\n    raw_headers = [\"CompressionContextPoolSingletons.h\"],\n    deps = [\n        \"//xplat/folly:portability\",\n        \"//xplat/folly/system:hardware_concurrency\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:config\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \":compression_context_pool\",\n        \"//xplat/folly/memory:malloc\",\n        \"//xplat/third-party/zstd:zstd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"quotient_multiset\",\n    srcs = [\n        \"QuotientMultiSet.cpp\",\n    ],\n    headers = [\n        \"QuotientMultiSet.h\",\n        \"QuotientMultiSet-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:math\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:format\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/compression:instructions\",\n        \"//folly/compression:select64\",\n        \"//folly/io:iobuf\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:bits_class\",\n        \"//folly/lang:safe_assert\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"instructions\",\n    headers = [\"Instructions.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:cpu_id\",\n        \"//folly:portability\",\n        \"//folly/lang:assume\",\n        \"//folly/portability:builtins\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"select64\",\n    srcs = [\"Select64.cpp\"],\n    headers = [\"Select64.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:constexpr_math\",\n        \"//folly:utility\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":instructions\",\n        \"//folly:portability\",\n    ],\n)\n\n# !!!! fbcode/folly/compression/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"compression\",\n    srcs = [\n        \"Compression.cpp\",\n        \"Zlib.cpp\",\n        \"Zstd.cpp\",\n    ],\n    headers = [\n        \"Compression.h\",\n        \"Utils.h\",\n        \"Zlib.h\",\n        \"Zstd.h\",\n    ],\n    compiler_flags = [\n        \"-DLZ4_STATIC_LINKING_ONLY\",\n        \"-DLZ4_HC_STATIC_LINKING_ONLY\",\n        \"-DLZ4F_STATIC_LINKING_ONLY\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//third-party/lz4:lz4\",\n        \":compression_context_pool_singletons\",\n        \"//folly:conv\",\n        \"//folly:random\",\n        \"//folly:scope_guard\",\n        \"//folly:utility\",\n        \"//folly:varint\",\n        \"//folly/portability:windows\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/zlib:z\",\n        \"fbsource//third-party/zstd:zstd\",\n        \"//folly:memory\",\n        \"//folly:optional\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/io:iobuf\",\n        \"//folly/lang:bits\",\n    ],\n    external_deps = [\n        \"snappy\",\n        (\"bzip2\", None, \"bz2\"),\n        (\"xz\", None, \"lzma\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"compression_context_pool_singletons\",\n    srcs = [\"CompressionContextPoolSingletons.cpp\"],\n    headers = [\"CompressionContextPoolSingletons.h\"],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/memory:jemalloc_huge_page_allocator\",\n        \"//folly/memory:malloc\",\n        \"//folly/system:hardware_concurrency\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/zstd:zstd\",\n        \":compression_context_pool\",\n        \"//folly/portability:config\",\n        \"//folly/portability:gflags\",\n    ],\n)\n"
  },
  {
    "path": "folly/compression/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME compression\n  SRCS\n    Compression.cpp\n    Zlib.cpp\n    Zstd.cpp\n  HEADERS\n    Compression.h\n    Utils.h\n    Zlib.h\n    Zstd.h\n  DEPS\n    folly_compression_compression_context_pool_singletons\n    folly_conv\n    folly_portability_windows\n    folly_random\n    folly_scope_guard\n    folly_utility\n    folly_varint\n  EXPORTED_DEPS\n    folly_io_iobuf\n    folly_lang_bits\n    folly_memory\n    folly_optional\n    folly_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME compression_context_pool\n  HEADERS\n    CompressionContextPool.h\n    CompressionCoreLocalContextPool.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_memory\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME compression_context_pool_singletons\n  SRCS\n    CompressionContextPoolSingletons.cpp\n  HEADERS\n    CompressionContextPoolSingletons.h\n  DEPS\n    folly_memory_jemalloc_huge_page_allocator\n    folly_memory_malloc\n    folly_portability\n    folly_system_hardware_concurrency\n  EXPORTED_DEPS\n    folly_compression_compression_context_pool\n    folly_portability_config\n    folly_portability_gflags\n)\n\nfolly_add_library(\n  NAME instructions\n  HEADERS\n    Instructions.h\n  EXPORTED_DEPS\n    folly_cpu_id\n    folly_lang_assume\n    folly_portability\n    folly_portability_builtins\n)\n\nfolly_add_library(\n  NAME quotient_multiset\n  SRCS\n    QuotientMultiSet.cpp\n  HEADERS\n    QuotientMultiSet-inl.h\n    QuotientMultiSet.h\n  DEPS\n    folly_math\n  EXPORTED_DEPS\n    folly_compression_instructions\n    folly_compression_select64\n    folly_format\n    folly_io_iobuf\n    folly_lang_bits\n    folly_lang_bits_class\n    folly_lang_safe_assert\n    folly_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME select64\n  SRCS\n    Select64.cpp\n  HEADERS\n    Select64.h\n  DEPS\n    folly_constexpr_math\n    folly_utility\n  EXPORTED_DEPS\n    folly_compression_instructions\n    folly_portability\n)\n\nadd_subdirectory(elias_fano)\n"
  },
  {
    "path": "folly/compression/Compression.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/Compression.h>\n\n#if FOLLY_HAVE_LIBLZ4\n#include <lz4.h>\n#include <lz4hc.h>\n#if LZ4_VERSION_NUMBER >= 10301\n#include <lz4frame.h>\n#endif\n#endif\n\n#include <glog/logging.h>\n\n#if FOLLY_HAVE_LIBSNAPPY\n#include <snappy-sinksource.h>\n#include <snappy.h>\n#endif\n\n#if FOLLY_HAVE_LIBZ\n#include <folly/compression/Zlib.h>\n#endif\n\n#if FOLLY_HAVE_LIBLZMA\n#include <lzma.h>\n#endif\n\n#if FOLLY_HAVE_LIBZSTD\n#include <folly/compression/Zstd.h>\n#endif\n\n#if FOLLY_HAVE_LIBBZ2\n#include <folly/portability/Windows.h>\n\n#include <bzlib.h>\n#endif\n\n#include <algorithm>\n#include <unordered_set>\n\n#include <folly/Conv.h>\n#include <folly/Memory.h>\n#include <folly/Portability.h>\n#include <folly/Random.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Utility.h>\n#include <folly/Varint.h>\n#include <folly/compression/Utils.h>\n#include <folly/io/Cursor.h>\n#include <folly/lang/Bits.h>\n\nusing folly::compression::detail::dataStartsWithLE;\nusing folly::compression::detail::prefixToStringLE;\n\nnamespace folly {\nnamespace compression {\n\nCodec::Codec(CodecType type, Optional<int> /* level */, StringPiece /* name */)\n    : type_(type) {}\n\n// Ensure consistent behavior in the nullptr case\nstd::unique_ptr<IOBuf> Codec::compress(const IOBuf* data) {\n  if (data == nullptr) {\n    throw std::invalid_argument(\"Codec: data must not be nullptr\");\n  }\n  const uint64_t len = data->computeChainDataLength();\n  if (len > maxUncompressedLength()) {\n    throw std::runtime_error(\"Codec: uncompressed length too large\");\n  }\n  return doCompress(data);\n}\n\nstd::string Codec::compress(const StringPiece data) {\n  const uint64_t len = data.size();\n  if (len > maxUncompressedLength()) {\n    throw std::runtime_error(\"Codec: uncompressed length too large\");\n  }\n  return doCompressString(data);\n}\n\nstd::unique_ptr<IOBuf> Codec::uncompress(\n    const IOBuf* data, Optional<uint64_t> uncompressedLength) {\n  if (data == nullptr) {\n    throw std::invalid_argument(\"Codec: data must not be nullptr\");\n  }\n  if (!uncompressedLength) {\n    if (needsUncompressedLength()) {\n      throw std::invalid_argument(\"Codec: uncompressed length required\");\n    }\n  } else if (*uncompressedLength > maxUncompressedLength()) {\n    throw std::runtime_error(\"Codec: uncompressed length too large\");\n  }\n\n  if (data->empty()) {\n    if (uncompressedLength.value_or(0) != 0) {\n      throw std::runtime_error(\"Codec: invalid uncompressed length\");\n    }\n    return IOBuf::create(0);\n  }\n\n  return doUncompress(data, uncompressedLength);\n}\n\nstd::string Codec::uncompress(\n    const StringPiece data, Optional<uint64_t> uncompressedLength) {\n  if (!uncompressedLength) {\n    if (needsUncompressedLength()) {\n      throw std::invalid_argument(\"Codec: uncompressed length required\");\n    }\n  } else if (*uncompressedLength > maxUncompressedLength()) {\n    throw std::runtime_error(\"Codec: uncompressed length too large\");\n  }\n\n  if (data.empty()) {\n    if (uncompressedLength.value_or(0) != 0) {\n      throw std::runtime_error(\"Codec: invalid uncompressed length\");\n    }\n    return \"\";\n  }\n\n  return doUncompressString(data, uncompressedLength);\n}\n\nbool Codec::needsUncompressedLength() const {\n  return doNeedsUncompressedLength();\n}\n\nuint64_t Codec::maxUncompressedLength() const {\n  return doMaxUncompressedLength();\n}\n\nbool Codec::doNeedsUncompressedLength() const {\n  return false;\n}\n\nuint64_t Codec::doMaxUncompressedLength() const {\n  return UNLIMITED_UNCOMPRESSED_LENGTH;\n}\n\nstd::vector<std::string> Codec::validPrefixes() const {\n  return {};\n}\n\nbool Codec::canUncompress(const IOBuf*, Optional<uint64_t>) const {\n  return false;\n}\n\nbool Codec::canUncompress(\n    StringPiece data, Optional<uint64_t> uncompressedLength) const {\n  auto buf = IOBuf::wrapBufferAsValue(data.data(), data.size());\n  return canUncompress(&buf, uncompressedLength);\n}\n\nstd::string Codec::doCompressString(const StringPiece data) {\n  const IOBuf inputBuffer{IOBuf::WRAP_BUFFER, data};\n  auto outputBuffer = doCompress(&inputBuffer);\n  return outputBuffer->to<std::string>();\n}\n\nstd::string Codec::doUncompressString(\n    const StringPiece data, Optional<uint64_t> uncompressedLength) {\n  const IOBuf inputBuffer{IOBuf::WRAP_BUFFER, data};\n  auto outputBuffer = doUncompress(&inputBuffer, uncompressedLength);\n  std::string output;\n  output.reserve(outputBuffer->computeChainDataLength());\n  for (auto range : *outputBuffer) {\n    output.append(reinterpret_cast<const char*>(range.data()), range.size());\n  }\n  return output;\n}\n\nuint64_t Codec::maxCompressedLength(uint64_t uncompressedLength) const {\n  return doMaxCompressedLength(uncompressedLength);\n}\n\nOptional<uint64_t> Codec::getUncompressedLength(\n    const folly::IOBuf* data, Optional<uint64_t> uncompressedLength) const {\n  auto const compressedLength = data->computeChainDataLength();\n  if (compressedLength == 0) {\n    if (uncompressedLength.value_or(0) != 0) {\n      throw std::runtime_error(\"Invalid uncompressed length\");\n    }\n    return 0;\n  }\n  return doGetUncompressedLength(data, uncompressedLength);\n}\n\nOptional<uint64_t> Codec::getUncompressedLength(\n    StringPiece data, Optional<uint64_t> uncompressedLength) const {\n  auto buf = IOBuf::wrapBufferAsValue(data.data(), data.size());\n  return getUncompressedLength(&buf, uncompressedLength);\n}\n\nOptional<uint64_t> Codec::doGetUncompressedLength(\n    const folly::IOBuf*, Optional<uint64_t> uncompressedLength) const {\n  return uncompressedLength;\n}\n\nbool StreamCodec::needsDataLength() const {\n  return doNeedsDataLength();\n}\n\nbool StreamCodec::doNeedsDataLength() const {\n  return false;\n}\n\nvoid StreamCodec::assertStateIs(State expected) const {\n  if (state_ != expected) {\n    throw std::logic_error(\n        folly::to<std::string>(\n            \"Codec: state is \", state_, \"; expected state \", expected));\n  }\n}\n\nvoid StreamCodec::resetStream(Optional<uint64_t> uncompressedLength) {\n  state_ = State::RESET;\n  uncompressedLength_ = uncompressedLength;\n  progressMade_ = true;\n  doResetStream();\n}\n\nbool StreamCodec::compressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {\n  if (state_ == State::RESET && input.empty() &&\n      flushOp == StreamCodec::FlushOp::END &&\n      uncompressedLength().value_or(0) != 0) {\n    throw std::runtime_error(\"Codec: invalid uncompressed length\");\n  }\n\n  if (!uncompressedLength() && needsDataLength()) {\n    throw std::runtime_error(\"Codec: uncompressed length required\");\n  }\n  if (state_ == State::RESET && !input.empty() &&\n      uncompressedLength() == uint64_t(0)) {\n    throw std::runtime_error(\"Codec: invalid uncompressed length\");\n  }\n  // Handle input state transitions\n  switch (flushOp) {\n    case StreamCodec::FlushOp::NONE:\n      if (state_ == State::RESET) {\n        state_ = State::COMPRESS;\n      }\n      assertStateIs(State::COMPRESS);\n      break;\n    case StreamCodec::FlushOp::FLUSH:\n      if (state_ == State::RESET || state_ == State::COMPRESS) {\n        state_ = State::COMPRESS_FLUSH;\n      }\n      assertStateIs(State::COMPRESS_FLUSH);\n      break;\n    case StreamCodec::FlushOp::END:\n      if (state_ == State::RESET || state_ == State::COMPRESS) {\n        state_ = State::COMPRESS_END;\n      }\n      assertStateIs(State::COMPRESS_END);\n      break;\n  }\n  size_t const inputSize = input.size();\n  size_t const outputSize = output.size();\n  bool const done = doCompressStream(input, output, flushOp);\n  if (!done && inputSize == input.size() && outputSize == output.size()) {\n    if (!progressMade_) {\n      throw std::runtime_error(\"Codec: No forward progress made\");\n    }\n    // Throw an exception if there is no progress again next time\n    progressMade_ = false;\n  } else {\n    progressMade_ = true;\n  }\n  // Handle output state transitions\n  if (done) {\n    if (state_ == State::COMPRESS_FLUSH) {\n      state_ = State::COMPRESS;\n    } else if (state_ == State::COMPRESS_END) {\n      state_ = State::END;\n    }\n    // Check internal invariants\n    DCHECK(input.empty());\n    DCHECK(flushOp != StreamCodec::FlushOp::NONE);\n  }\n  return done;\n}\n\nbool StreamCodec::uncompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {\n  if (state_ == State::RESET && input.empty()) {\n    return uncompressedLength().value_or(0) == 0;\n  }\n  // Handle input state transitions\n  if (state_ == State::RESET) {\n    state_ = State::UNCOMPRESS;\n  }\n  assertStateIs(State::UNCOMPRESS);\n  size_t const inputSize = input.size();\n  size_t const outputSize = output.size();\n  bool const done = doUncompressStream(input, output, flushOp);\n  if (!done && inputSize == input.size() && outputSize == output.size()) {\n    if (!progressMade_) {\n      throw std::runtime_error(\"Codec: no forward progress made\");\n    }\n    // Throw an exception if there is no progress again next time\n    progressMade_ = false;\n  } else {\n    progressMade_ = true;\n  }\n  // Handle output state transitions\n  if (done) {\n    state_ = State::END;\n  }\n  return done;\n}\n\nstatic std::unique_ptr<IOBuf> addOutputBuffer(\n    MutableByteRange& output, uint64_t size) {\n  DCHECK(output.empty());\n  auto buffer = IOBuf::create(size);\n  buffer->append(buffer->capacity());\n  output = {buffer->writableData(), buffer->length()};\n  return buffer;\n}\n\nstd::unique_ptr<IOBuf> StreamCodec::doCompress(IOBuf const* data) {\n  uint64_t const uncompressedLength = data->computeChainDataLength();\n  resetStream(uncompressedLength);\n  uint64_t const maxCompressedLen = maxCompressedLength(uncompressedLength);\n\n  auto constexpr kMaxSingleStepLength = uint64_t(64) << 20; // 64 MB\n  auto constexpr kDefaultBufferLength = uint64_t(4) << 20; // 4 MB\n\n  MutableByteRange output;\n  auto buffer = addOutputBuffer(\n      output,\n      maxCompressedLen <= kMaxSingleStepLength\n          ? maxCompressedLen\n          : kDefaultBufferLength);\n\n  // Compress the entire IOBuf chain into the IOBuf chain pointed to by buffer\n  IOBuf const* current = data;\n  ByteRange input{current->data(), current->length()};\n  StreamCodec::FlushOp flushOp = StreamCodec::FlushOp::NONE;\n  bool done = false;\n  while (!done) {\n    while (input.empty() && current->next() != data) {\n      current = current->next();\n      input = {current->data(), current->length()};\n    }\n    if (current->next() == data) {\n      // This is the last input buffer so end the stream\n      flushOp = StreamCodec::FlushOp::END;\n    }\n    if (output.empty()) {\n      buffer->prependChain(addOutputBuffer(output, kDefaultBufferLength));\n    }\n    done = compressStream(input, output, flushOp);\n    if (done) {\n      DCHECK(input.empty());\n      DCHECK(flushOp == StreamCodec::FlushOp::END);\n      DCHECK_EQ(current->next(), data);\n    }\n  }\n  buffer->prev()->trimEnd(output.size());\n  return buffer;\n}\n\nstatic uint64_t computeBufferLength(\n    uint64_t const compressedLength, uint64_t const blockSize) {\n  uint64_t constexpr kMaxBufferLength = uint64_t(4) << 20; // 4 MiB\n  uint64_t const goodBufferSize = 4 * std::max(blockSize, compressedLength);\n  return std::min(goodBufferSize, kMaxBufferLength);\n}\n\nstd::unique_ptr<IOBuf> StreamCodec::doUncompress(\n    IOBuf const* data, Optional<uint64_t> uncompressedLength) {\n  auto constexpr kMaxSingleStepLength = uint64_t(64) << 20; // 64 MB\n  auto constexpr kBlockSize = uint64_t(128) << 10;\n  auto const defaultBufferLength =\n      computeBufferLength(data->computeChainDataLength(), kBlockSize);\n\n  uncompressedLength = getUncompressedLength(data, uncompressedLength);\n  resetStream(uncompressedLength);\n\n  MutableByteRange output;\n  auto buffer = addOutputBuffer(\n      output,\n      (uncompressedLength && *uncompressedLength <= kMaxSingleStepLength\n           ? *uncompressedLength\n           : defaultBufferLength));\n\n  // Uncompress the entire IOBuf chain into the IOBuf chain pointed to by buffer\n  IOBuf const* current = data;\n  ByteRange input{current->data(), current->length()};\n  StreamCodec::FlushOp flushOp = StreamCodec::FlushOp::NONE;\n  bool done = false;\n  while (!done) {\n    while (input.empty() && current->next() != data) {\n      current = current->next();\n      input = {current->data(), current->length()};\n    }\n    if (current->next() == data) {\n      // Tell the uncompressor there is no more input (it may optimize)\n      flushOp = StreamCodec::FlushOp::END;\n    }\n    if (output.empty()) {\n      buffer->prependChain(addOutputBuffer(output, defaultBufferLength));\n    }\n    done = uncompressStream(input, output, flushOp);\n  }\n  if (!input.empty()) {\n    throw std::runtime_error(\"Codec: Junk after end of data\");\n  }\n\n  buffer->prev()->trimEnd(output.size());\n  if (uncompressedLength &&\n      *uncompressedLength != buffer->computeChainDataLength()) {\n    throw std::runtime_error(\"Codec: invalid uncompressed length\");\n  }\n\n  return buffer;\n}\n\nnamespace {\n\n/**\n * No compression\n */\nclass NoCompressionCodec final : public Codec {\n public:\n  static std::unique_ptr<Codec> create(int level, CodecType type);\n  explicit NoCompressionCodec(int level, CodecType type);\n\n private:\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n  std::unique_ptr<IOBuf> doCompress(const IOBuf* data) override;\n  std::unique_ptr<IOBuf> doUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) override;\n};\n\nstd::unique_ptr<Codec> NoCompressionCodec::create(int level, CodecType type) {\n  return std::make_unique<NoCompressionCodec>(level, type);\n}\n\nNoCompressionCodec::NoCompressionCodec(int level, CodecType type)\n    : Codec(type) {\n  DCHECK(type == CodecType::NO_COMPRESSION);\n  switch (level) {\n    case COMPRESSION_LEVEL_DEFAULT:\n    case COMPRESSION_LEVEL_FASTEST:\n    case COMPRESSION_LEVEL_BEST:\n      level = 0;\n  }\n  if (level != 0) {\n    throw std::invalid_argument(\n        to<std::string>(\"NoCompressionCodec: invalid level \", level));\n  }\n}\n\nuint64_t NoCompressionCodec::doMaxCompressedLength(\n    uint64_t uncompressedLength) const {\n  return uncompressedLength;\n}\n\nstd::unique_ptr<IOBuf> NoCompressionCodec::doCompress(const IOBuf* data) {\n  return data->clone();\n}\n\nstd::unique_ptr<IOBuf> NoCompressionCodec::doUncompress(\n    const IOBuf* data, Optional<uint64_t> uncompressedLength) {\n  if (uncompressedLength &&\n      data->computeChainDataLength() != *uncompressedLength) {\n    throw std::runtime_error(\n        to<std::string>(\"NoCompressionCodec: invalid uncompressed length\"));\n  }\n  return data->clone();\n}\n\n#if (FOLLY_HAVE_LIBLZ4 || FOLLY_HAVE_LIBLZMA)\n\nvoid encodeVarintToIOBuf(uint64_t val, folly::IOBuf* out) {\n  DCHECK_GE(out->tailroom(), kMaxVarintLength64);\n  out->append(encodeVarint(val, out->writableTail()));\n}\n\ninline uint64_t decodeVarintFromCursor(folly::io::Cursor& cursor) {\n  uint64_t val = 0;\n  int8_t b = 0;\n  for (int shift = 0; shift <= 63; shift += 7) {\n    b = cursor.read<int8_t>();\n    val |= static_cast<uint64_t>(b & 0x7f) << shift;\n    if (b >= 0) {\n      break;\n    }\n  }\n  if (b < 0) {\n    throw std::invalid_argument(\"Invalid varint value. Too big.\");\n  }\n  return val;\n}\n\n#endif // FOLLY_HAVE_LIBLZ4 || FOLLY_HAVE_LIBLZMA\n\n#if FOLLY_HAVE_LIBLZ4\n\n#if LZ4_VERSION_NUMBER >= 10802 && defined(LZ4_STATIC_LINKING_ONLY) && \\\n    defined(LZ4_HC_STATIC_LINKING_ONLY) && !defined(FOLLY_USE_LZ4_FAST_RESET)\n#define FOLLY_USE_LZ4_FAST_RESET 1\n#endif\n\n#if FOLLY_USE_LZ4_FAST_RESET\nvoid lz4_stream_t_deleter(LZ4_stream_t* ctx) {\n  LZ4_freeStream(ctx);\n}\n\nvoid lz4_streamhc_t_deleter(LZ4_streamHC_t* ctx) {\n  LZ4_freeStreamHC(ctx);\n}\n#endif\n\n/**\n * LZ4 compression\n */\nclass LZ4Codec final : public Codec {\n public:\n  static std::unique_ptr<Codec> create(int level, CodecType type);\n  explicit LZ4Codec(int level, CodecType type);\n\n private:\n  bool doNeedsUncompressedLength() const override;\n  uint64_t doMaxUncompressedLength() const override;\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n\n  bool encodeSize() const { return type() == CodecType::LZ4_VARINT_SIZE; }\n\n  std::unique_ptr<IOBuf> doCompress(const IOBuf* data) override;\n  std::unique_ptr<IOBuf> doUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) override;\n\n#if FOLLY_USE_LZ4_FAST_RESET\n  std::unique_ptr<\n      LZ4_stream_t,\n      folly::static_function_deleter<LZ4_stream_t, lz4_stream_t_deleter>>\n      ctx;\n  std::unique_ptr<\n      LZ4_streamHC_t,\n      folly::static_function_deleter<LZ4_streamHC_t, lz4_streamhc_t_deleter>>\n      hcctx;\n#endif\n\n  bool highCompression_;\n};\n\nstd::unique_ptr<Codec> LZ4Codec::create(int level, CodecType type) {\n  return std::make_unique<LZ4Codec>(level, type);\n}\n\nint lz4ConvertLevel(int level) {\n  switch (level) {\n    case 1:\n    case COMPRESSION_LEVEL_FASTEST:\n    case COMPRESSION_LEVEL_DEFAULT:\n      return 1;\n    case 2:\n    case COMPRESSION_LEVEL_BEST:\n      return 2;\n  }\n  throw std::invalid_argument(\n      to<std::string>(\"LZ4Codec: invalid level: \", level));\n}\n\nLZ4Codec::LZ4Codec(int level, CodecType type)\n    : Codec(type, lz4ConvertLevel(level)),\n      highCompression_(lz4ConvertLevel(level) > 1) {\n  DCHECK(type == CodecType::LZ4 || type == CodecType::LZ4_VARINT_SIZE);\n}\n\nbool LZ4Codec::doNeedsUncompressedLength() const {\n  return !encodeSize();\n}\n\n// The value comes from lz4.h in lz4-r117, but older versions of lz4 don't\n// define LZ4_MAX_INPUT_SIZE (even though the max size is the same), so do it\n// here.\n#ifndef LZ4_MAX_INPUT_SIZE\n#define LZ4_MAX_INPUT_SIZE 0x7E000000\n#endif\n\nuint64_t LZ4Codec::doMaxUncompressedLength() const {\n  return LZ4_MAX_INPUT_SIZE;\n}\n\nuint64_t LZ4Codec::doMaxCompressedLength(uint64_t uncompressedLength) const {\n  return LZ4_compressBound(uncompressedLength) +\n      (encodeSize() ? kMaxVarintLength64 : 0);\n}\n\nstd::unique_ptr<IOBuf> LZ4Codec::doCompress(const IOBuf* data) {\n  IOBuf clone;\n  if (data->isChained()) {\n    // LZ4 doesn't support streaming, so we have to coalesce\n    clone = data->cloneCoalescedAsValue();\n    data = &clone;\n  }\n\n  auto out = IOBuf::create(maxCompressedLength(data->length()));\n  if (encodeSize()) {\n    encodeVarintToIOBuf(data->length(), out.get());\n  }\n\n  int n;\n  auto input = reinterpret_cast<const char*>(data->data());\n  auto output = reinterpret_cast<char*>(out->writableTail());\n  const auto inputLength = data->length();\n\n#if FOLLY_USE_LZ4_FAST_RESET\n  if (!highCompression_ && !ctx) {\n    ctx.reset(LZ4_createStream());\n  }\n  if (highCompression_ && !hcctx) {\n    hcctx.reset(LZ4_createStreamHC());\n  }\n\n  if (highCompression_) {\n    n = LZ4_compress_HC_extStateHC_fastReset(\n        hcctx.get(), input, output, inputLength, out->tailroom(), 0);\n  } else {\n    n = LZ4_compress_fast_extState_fastReset(\n        ctx.get(), input, output, inputLength, out->tailroom(), 1);\n  }\n#elif LZ4_VERSION_NUMBER >= 10700\n  if (highCompression_) {\n    n = LZ4_compress_HC(input, output, inputLength, out->tailroom(), 0);\n  } else {\n    n = LZ4_compress_default(input, output, inputLength, out->tailroom());\n  }\n#else\n  if (highCompression_) {\n    n = LZ4_compressHC(input, output, inputLength);\n  } else {\n    n = LZ4_compress(input, output, inputLength);\n  }\n#endif\n\n  CHECK_GE(n, 0);\n  CHECK_LE(n, out->capacity());\n\n  out->append(n);\n  return out;\n}\n\nstd::unique_ptr<IOBuf> LZ4Codec::doUncompress(\n    const IOBuf* data, Optional<uint64_t> uncompressedLength) {\n  IOBuf clone;\n  if (data->isChained()) {\n    // LZ4 doesn't support streaming, so we have to coalesce\n    clone = data->cloneCoalescedAsValue();\n    data = &clone;\n  }\n\n  folly::io::Cursor cursor(data);\n  uint64_t actualUncompressedLength;\n  if (encodeSize()) {\n    actualUncompressedLength = decodeVarintFromCursor(cursor);\n    if (uncompressedLength && *uncompressedLength != actualUncompressedLength) {\n      throw std::runtime_error(\"LZ4Codec: invalid uncompressed length\");\n    }\n  } else {\n    // Invariants\n    DCHECK(uncompressedLength.has_value());\n    DCHECK(*uncompressedLength <= maxUncompressedLength());\n    actualUncompressedLength = *uncompressedLength;\n  }\n\n  auto sp = StringPiece{cursor.peekBytes()};\n  auto out = IOBuf::create(actualUncompressedLength);\n  int n = LZ4_decompress_safe(\n      sp.data(),\n      reinterpret_cast<char*>(out->writableTail()),\n      sp.size(),\n      actualUncompressedLength);\n\n  if (n < 0 || uint64_t(n) != actualUncompressedLength) {\n    throw std::runtime_error(\n        to<std::string>(\"LZ4 decompression returned invalid value \", n));\n  }\n  out->append(actualUncompressedLength);\n  return out;\n}\n\n#if LZ4_VERSION_NUMBER >= 10301\n\nclass LZ4FrameCodec final : public Codec {\n public:\n  static std::unique_ptr<Codec> create(int level, CodecType type);\n  explicit LZ4FrameCodec(int level, CodecType type);\n  ~LZ4FrameCodec() override;\n\n  std::vector<std::string> validPrefixes() const override;\n  bool canUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) const override;\n\n private:\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n\n  std::unique_ptr<IOBuf> doCompress(const IOBuf* data) override;\n  std::unique_ptr<IOBuf> doUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) override;\n\n  // Reset the dctx_ if it is dirty or null.\n  void resetDCtx();\n\n  int level_;\n#if FOLLY_USE_LZ4_FAST_RESET\n  LZ4F_compressionContext_t cctx_{nullptr};\n#endif\n  LZ4F_decompressionContext_t dctx_{nullptr};\n  bool dirty_{false};\n};\n\n/* static */ std::unique_ptr<Codec> LZ4FrameCodec::create(\n    int level, CodecType type) {\n  return std::make_unique<LZ4FrameCodec>(level, type);\n}\n\nconstexpr uint32_t kLZ4FrameMagicLE = 0x184D2204;\n\nstd::vector<std::string> LZ4FrameCodec::validPrefixes() const {\n  return {prefixToStringLE(kLZ4FrameMagicLE)};\n}\n\nbool LZ4FrameCodec::canUncompress(const IOBuf* data, Optional<uint64_t>) const {\n  return dataStartsWithLE(data, kLZ4FrameMagicLE);\n}\n\nuint64_t LZ4FrameCodec::doMaxCompressedLength(\n    uint64_t uncompressedLength) const {\n  LZ4F_preferences_t prefs{};\n  prefs.compressionLevel = level_;\n  prefs.frameInfo.contentSize = uncompressedLength;\n  return LZ4F_compressFrameBound(uncompressedLength, &prefs);\n}\n\nsize_t lz4FrameThrowOnError(size_t code) {\n  if (LZ4F_isError(code)) {\n    throw std::runtime_error(\n        to<std::string>(\"LZ4Frame error: \", LZ4F_getErrorName(code)));\n  }\n  return code;\n}\n\nvoid LZ4FrameCodec::resetDCtx() {\n  if (dctx_ && !dirty_) {\n    return;\n  }\n  if (dctx_) {\n    LZ4F_freeDecompressionContext(dctx_);\n  }\n  lz4FrameThrowOnError(LZ4F_createDecompressionContext(&dctx_, 100));\n  dirty_ = false;\n}\n\nint lz4fConvertLevel(int level) {\n  switch (level) {\n    case COMPRESSION_LEVEL_FASTEST:\n    case COMPRESSION_LEVEL_DEFAULT:\n      return 0;\n    case COMPRESSION_LEVEL_BEST:\n      return 16;\n  }\n  return level;\n}\n\nLZ4FrameCodec::LZ4FrameCodec(int level, CodecType type)\n    : Codec(type, lz4fConvertLevel(level)), level_(lz4fConvertLevel(level)) {\n  DCHECK(type == CodecType::LZ4_FRAME);\n}\n\nLZ4FrameCodec::~LZ4FrameCodec() {\n  if (dctx_) {\n    LZ4F_freeDecompressionContext(dctx_);\n  }\n#if FOLLY_USE_LZ4_FAST_RESET\n  if (cctx_) {\n    LZ4F_freeCompressionContext(cctx_);\n  }\n#endif\n}\n\nstd::unique_ptr<IOBuf> LZ4FrameCodec::doCompress(const IOBuf* data) {\n  // LZ4 Frame compression doesn't support streaming so we have to coalesce\n  IOBuf clone;\n  if (data->isChained()) {\n    clone = data->cloneCoalescedAsValue();\n    data = &clone;\n  }\n\n#if FOLLY_USE_LZ4_FAST_RESET\n  if (!cctx_) {\n    lz4FrameThrowOnError(LZ4F_createCompressionContext(&cctx_, LZ4F_VERSION));\n  }\n#endif\n\n  // Set preferences\n  const auto uncompressedLength = data->length();\n  LZ4F_preferences_t prefs{};\n  prefs.compressionLevel = level_;\n  prefs.frameInfo.contentSize = uncompressedLength;\n  // Compress\n  auto buf = IOBuf::create(maxCompressedLength(uncompressedLength));\n  const size_t written = lz4FrameThrowOnError(\n#if FOLLY_USE_LZ4_FAST_RESET\n      LZ4F_compressFrame_usingCDict(\n          cctx_,\n          buf->writableTail(),\n          buf->tailroom(),\n          data->data(),\n          data->length(),\n          nullptr,\n          &prefs)\n#else\n      LZ4F_compressFrame(\n          buf->writableTail(),\n          buf->tailroom(),\n          data->data(),\n          data->length(),\n          &prefs)\n#endif\n  );\n  buf->append(written);\n  return buf;\n}\n\nstd::unique_ptr<IOBuf> LZ4FrameCodec::doUncompress(\n    const IOBuf* data, Optional<uint64_t> uncompressedLength) {\n  // Reset the dctx if any errors have occurred\n  resetDCtx();\n  // Coalesce the data\n  ByteRange in = *data->begin();\n  IOBuf clone;\n  if (data->isChained()) {\n    clone = data->cloneCoalescedAsValue();\n    in = clone.coalesce();\n  }\n  data = nullptr;\n  // Select decompression options\n  LZ4F_decompressOptions_t options{};\n  options.stableDst = 1;\n  // Select blockSize and growthSize for the IOBufQueue\n  IOBufQueue queue(IOBufQueue::cacheChainLength());\n  auto blockSize = uint64_t{64} << 10;\n  auto growthSize = uint64_t{4} << 20;\n  if (uncompressedLength) {\n    // Allocate uncompressedLength in one chunk (up to 64 MB)\n    const auto allocateSize = std::min(*uncompressedLength, uint64_t{64} << 20);\n    queue.preallocate(allocateSize, allocateSize);\n    blockSize = std::min(*uncompressedLength, blockSize);\n    growthSize = std::min(*uncompressedLength, growthSize);\n  } else {\n    // Reduce growthSize for small data\n    const auto guessUncompressedLen =\n        4 * std::max<uint64_t>(blockSize, in.size());\n    growthSize = std::min(guessUncompressedLen, growthSize);\n  }\n  // Once LZ4_decompress() is called, the dctx_ cannot be reused until it\n  // returns 0\n  dirty_ = true;\n  // Decompress until the frame is over\n  size_t code = 0;\n  do {\n    // Allocate enough space to decompress at least a block\n    void* out;\n    size_t outSize;\n    std::tie(out, outSize) = queue.preallocate(blockSize, growthSize);\n    // Decompress\n    size_t inSize = in.size();\n    code = lz4FrameThrowOnError(\n        LZ4F_decompress(dctx_, out, &outSize, in.data(), &inSize, &options));\n    if (in.empty() && outSize == 0 && code != 0) {\n      // We passed no input, no output was produced, and the frame isn't over\n      // No more forward progress is possible\n      throw std::runtime_error(\"LZ4Frame error: Incomplete frame\");\n    }\n    in.uncheckedAdvance(inSize);\n    queue.postallocate(outSize);\n  } while (code != 0);\n  // At this point the decompression context can be reused\n  dirty_ = false;\n  if (uncompressedLength && queue.chainLength() != *uncompressedLength) {\n    throw std::runtime_error(\"LZ4Frame error: Invalid uncompressedLength\");\n  }\n  return queue.move();\n}\n\n#endif // LZ4_VERSION_NUMBER >= 10301\n#endif // FOLLY_HAVE_LIBLZ4\n\n#if FOLLY_HAVE_LIBSNAPPY\n\n/**\n * Snappy compression\n */\n\n/**\n * Implementation of snappy::Source that reads from a IOBuf chain.\n */\nclass IOBufSnappySource final : public snappy::Source {\n public:\n  explicit IOBufSnappySource(const IOBuf* data);\n  size_t Available() const override;\n  const char* Peek(size_t* len) override;\n  void Skip(size_t n) override;\n\n private:\n  size_t available_;\n  io::Cursor cursor_;\n};\n\nIOBufSnappySource::IOBufSnappySource(const IOBuf* data)\n    : available_(data->computeChainDataLength()), cursor_(data) {}\n\nsize_t IOBufSnappySource::Available() const {\n  return available_;\n}\n\nconst char* IOBufSnappySource::Peek(size_t* len) {\n  auto sp = StringPiece{cursor_.peekBytes()};\n  *len = sp.size();\n  return sp.data();\n}\n\nvoid IOBufSnappySource::Skip(size_t n) {\n  CHECK_LE(n, available_);\n  cursor_.skip(n);\n  available_ -= n;\n}\n\nclass SnappyCodec final : public Codec {\n public:\n  static std::unique_ptr<Codec> create(int level, CodecType type);\n  explicit SnappyCodec(int level, CodecType type);\n\n private:\n  uint64_t doMaxUncompressedLength() const override;\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n  std::unique_ptr<IOBuf> doCompress(const IOBuf* data) override;\n  std::unique_ptr<IOBuf> doUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) override;\n  folly::Optional<uint64_t> doGetUncompressedLength(\n      const folly::IOBuf* data,\n      folly::Optional<uint64_t> uncompressedLength) const override;\n};\n\nstd::unique_ptr<Codec> SnappyCodec::create(int level, CodecType type) {\n  return std::make_unique<SnappyCodec>(level, type);\n}\n\nSnappyCodec::SnappyCodec(int level, CodecType type) : Codec(type) {\n  DCHECK(type == CodecType::SNAPPY);\n  switch (level) {\n    case COMPRESSION_LEVEL_FASTEST:\n    case COMPRESSION_LEVEL_DEFAULT:\n    case COMPRESSION_LEVEL_BEST:\n      level = 1;\n  }\n  if (level != 1) {\n    throw std::invalid_argument(\n        to<std::string>(\"SnappyCodec: invalid level: \", level));\n  }\n}\n\nuint64_t SnappyCodec::doMaxUncompressedLength() const {\n  // snappy.h uses uint32_t for lengths, so there's that.\n  return std::numeric_limits<uint32_t>::max();\n}\n\nuint64_t SnappyCodec::doMaxCompressedLength(uint64_t uncompressedLength) const {\n  return snappy::MaxCompressedLength(uncompressedLength);\n}\n\nstd::unique_ptr<IOBuf> SnappyCodec::doCompress(const IOBuf* data) {\n  IOBufSnappySource source(data);\n  auto out = IOBuf::create(maxCompressedLength(source.Available()));\n\n  snappy::UncheckedByteArraySink sink(\n      reinterpret_cast<char*>(out->writableTail()));\n\n  size_t n = snappy::Compress(&source, &sink);\n\n  CHECK_LE(n, out->capacity());\n  out->append(n);\n  return out;\n}\n\nstd::unique_ptr<IOBuf> SnappyCodec::doUncompress(\n    const IOBuf* data, Optional<uint64_t> uncompressedLength) {\n  uint32_t actualUncompressedLength = 0;\n\n  {\n    IOBufSnappySource source(data);\n    if (!snappy::GetUncompressedLength(&source, &actualUncompressedLength)) {\n      throw std::runtime_error(\"snappy::GetUncompressedLength failed\");\n    }\n    if (uncompressedLength && *uncompressedLength != actualUncompressedLength) {\n      throw std::runtime_error(\"snappy: invalid uncompressed length\");\n    }\n  }\n\n  auto out = IOBuf::create(actualUncompressedLength);\n\n  {\n    IOBufSnappySource source(data);\n    if (!snappy::RawUncompress(\n            &source, reinterpret_cast<char*>(out->writableTail()))) {\n      throw std::runtime_error(\"snappy::RawUncompress failed\");\n    }\n  }\n\n  out->append(actualUncompressedLength);\n  return out;\n}\n\nfolly::Optional<uint64_t> SnappyCodec::doGetUncompressedLength(\n    const folly::IOBuf* data,\n    folly::Optional<uint64_t> uncompressedLength) const {\n  uint32_t actualUncompressedLength = 0;\n  IOBufSnappySource source(data);\n  if (!snappy::GetUncompressedLength(&source, &actualUncompressedLength)) {\n    throw std::runtime_error(\"snappy::GetUncompressedLength failed\");\n  }\n  if (uncompressedLength && *uncompressedLength != actualUncompressedLength) {\n    throw std::runtime_error(\"snappy: invalid uncompressed length\");\n  }\n\n  return actualUncompressedLength;\n}\n\n#endif // FOLLY_HAVE_LIBSNAPPY\n\n#if FOLLY_HAVE_LIBLZMA\n\n/**\n * LZMA2 compression\n */\nclass LZMA2StreamCodec final : public StreamCodec {\n public:\n  static std::unique_ptr<Codec> createCodec(int level, CodecType type);\n  static std::unique_ptr<StreamCodec> createStream(int level, CodecType type);\n  explicit LZMA2StreamCodec(int level, CodecType type);\n  ~LZMA2StreamCodec() override;\n\n  std::vector<std::string> validPrefixes() const override;\n  bool canUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) const override;\n\n private:\n  bool doNeedsDataLength() const override;\n  uint64_t doMaxUncompressedLength() const override;\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n\n  bool encodeSize() const { return type() == CodecType::LZMA2_VARINT_SIZE; }\n\n  void doResetStream() override;\n  bool doCompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flushOp) override;\n  bool doUncompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flushOp) override;\n\n  void resetCStream();\n  void resetDStream();\n\n  bool decodeAndCheckVarint(ByteRange& input);\n  bool flushVarintBuffer(MutableByteRange& output);\n  void resetVarintBuffer();\n\n  Optional<lzma_stream> cstream_{};\n  Optional<lzma_stream> dstream_{};\n\n  std::array<uint8_t, kMaxVarintLength64> varintBuffer_;\n  ByteRange varintToEncode_;\n  size_t varintBufferPos_{0};\n\n  int level_;\n  bool needReset_{true};\n  bool needDecodeSize_{false};\n};\n\nconstexpr uint64_t kLZMA2MagicLE = 0x005A587A37FD;\nconstexpr unsigned kLZMA2MagicBytes = 6;\n\nstd::vector<std::string> LZMA2StreamCodec::validPrefixes() const {\n  if (type() == CodecType::LZMA2_VARINT_SIZE) {\n    return {};\n  }\n  return {prefixToStringLE(kLZMA2MagicLE, kLZMA2MagicBytes)};\n}\n\nbool LZMA2StreamCodec::doNeedsDataLength() const {\n  return encodeSize();\n}\n\nbool LZMA2StreamCodec::canUncompress(\n    const IOBuf* data, Optional<uint64_t>) const {\n  if (type() == CodecType::LZMA2_VARINT_SIZE) {\n    return false;\n  }\n  // Returns false for all inputs less than 8 bytes.\n  // This is okay, because no valid LZMA2 streams are less than 8 bytes.\n  return dataStartsWithLE(data, kLZMA2MagicLE, kLZMA2MagicBytes);\n}\n\nstd::unique_ptr<Codec> LZMA2StreamCodec::createCodec(\n    int level, CodecType type) {\n  return std::make_unique<LZMA2StreamCodec>(level, type);\n}\n\nstd::unique_ptr<StreamCodec> LZMA2StreamCodec::createStream(\n    int level, CodecType type) {\n  return std::make_unique<LZMA2StreamCodec>(level, type);\n}\n\nLZMA2StreamCodec::LZMA2StreamCodec(int level, CodecType type)\n    : StreamCodec(type) {\n  DCHECK(type == CodecType::LZMA2 || type == CodecType::LZMA2_VARINT_SIZE);\n  switch (level) {\n    case COMPRESSION_LEVEL_FASTEST:\n      level = 0;\n      break;\n    case COMPRESSION_LEVEL_DEFAULT:\n      level = LZMA_PRESET_DEFAULT;\n      break;\n    case COMPRESSION_LEVEL_BEST:\n      level = 9;\n      break;\n  }\n  if (level < 0 || level > 9) {\n    throw std::invalid_argument(\n        to<std::string>(\"LZMA2Codec: invalid level: \", level));\n  }\n  level_ = level;\n}\n\nLZMA2StreamCodec::~LZMA2StreamCodec() {\n  if (cstream_) {\n    lzma_end(cstream_.get_pointer());\n    cstream_.reset();\n  }\n  if (dstream_) {\n    lzma_end(dstream_.get_pointer());\n    dstream_.reset();\n  }\n}\n\nuint64_t LZMA2StreamCodec::doMaxUncompressedLength() const {\n  // From lzma/base.h: \"Stream is roughly 8 EiB (2^63 bytes)\"\n  return uint64_t(1) << 63;\n}\n\nuint64_t LZMA2StreamCodec::doMaxCompressedLength(\n    uint64_t uncompressedLength) const {\n  return lzma_stream_buffer_bound(uncompressedLength) +\n      (encodeSize() ? kMaxVarintLength64 : 0);\n}\n\nvoid LZMA2StreamCodec::doResetStream() {\n  needReset_ = true;\n}\n\nvoid LZMA2StreamCodec::resetCStream() {\n  if (!cstream_) {\n    cstream_.assign(LZMA_STREAM_INIT);\n  }\n  lzma_ret const rc =\n      lzma_easy_encoder(cstream_.get_pointer(), level_, LZMA_CHECK_NONE);\n  if (rc != LZMA_OK) {\n    throw std::runtime_error(\n        folly::to<std::string>(\n            \"LZMA2StreamCodec: lzma_easy_encoder error: \", rc));\n  }\n}\n\nvoid LZMA2StreamCodec::resetDStream() {\n  if (!dstream_) {\n    dstream_.assign(LZMA_STREAM_INIT);\n  }\n  lzma_ret const rc = lzma_auto_decoder(\n      dstream_.get_pointer(), std::numeric_limits<uint64_t>::max(), 0);\n  if (rc != LZMA_OK) {\n    throw std::runtime_error(\n        folly::to<std::string>(\n            \"LZMA2StreamCodec: lzma_auto_decoder error: \", rc));\n  }\n}\n\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wswitch-enum\")\nlzma_ret lzmaThrowOnError(lzma_ret const rc) {\n  switch (rc) {\n    case LZMA_OK:\n    case LZMA_STREAM_END:\n    case LZMA_BUF_ERROR: // not fatal: returned if no progress was made twice\n      return rc;\n    case LZMA_NO_CHECK:\n    case LZMA_UNSUPPORTED_CHECK:\n    case LZMA_GET_CHECK:\n    case LZMA_MEM_ERROR:\n    case LZMA_MEMLIMIT_ERROR:\n    case LZMA_FORMAT_ERROR:\n    case LZMA_OPTIONS_ERROR:\n    case LZMA_DATA_ERROR:\n    case LZMA_PROG_ERROR:\n    default:\n      throw std::runtime_error(\n          to<std::string>(\"LZMA2StreamCodec: error: \", rc));\n  }\n}\nFOLLY_POP_WARNING\n\nlzma_action lzmaTranslateFlush(StreamCodec::FlushOp flush) {\n  switch (flush) {\n    case StreamCodec::FlushOp::NONE:\n      return LZMA_RUN;\n    case StreamCodec::FlushOp::FLUSH:\n      return LZMA_SYNC_FLUSH;\n    case StreamCodec::FlushOp::END:\n      return LZMA_FINISH;\n    default:\n      throw std::invalid_argument(\"LZMA2StreamCodec: Invalid flush\");\n  }\n}\n\n/**\n * Flushes the varint buffer.\n * Advances output by the number of bytes written.\n * Returns true when flushing is complete.\n */\nbool LZMA2StreamCodec::flushVarintBuffer(MutableByteRange& output) {\n  if (varintToEncode_.empty()) {\n    return true;\n  }\n  const size_t numBytesToCopy = std::min(varintToEncode_.size(), output.size());\n  if (numBytesToCopy > 0) {\n    memcpy(output.data(), varintToEncode_.data(), numBytesToCopy);\n  }\n  varintToEncode_.advance(numBytesToCopy);\n  output.advance(numBytesToCopy);\n  return varintToEncode_.empty();\n}\n\nbool LZMA2StreamCodec::doCompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {\n  if (needReset_) {\n    resetCStream();\n    if (encodeSize()) {\n      varintBufferPos_ = 0;\n      size_t const varintSize =\n          encodeVarint(*uncompressedLength(), varintBuffer_.data());\n      varintToEncode_ = {varintBuffer_.data(), varintSize};\n    }\n    needReset_ = false;\n  }\n\n  if (!flushVarintBuffer(output)) {\n    return false;\n  }\n\n  cstream_->next_in = const_cast<uint8_t*>(input.data());\n  cstream_->avail_in = input.size();\n  cstream_->next_out = output.data();\n  cstream_->avail_out = output.size();\n  SCOPE_EXIT {\n    input.uncheckedAdvance(input.size() - cstream_->avail_in);\n    output.uncheckedAdvance(output.size() - cstream_->avail_out);\n  };\n  lzma_ret const rc = lzmaThrowOnError(\n      lzma_code(cstream_.get_pointer(), lzmaTranslateFlush(flushOp)));\n  switch (flushOp) {\n    case StreamCodec::FlushOp::NONE:\n      return false;\n    case StreamCodec::FlushOp::FLUSH:\n      return cstream_->avail_in == 0 && cstream_->avail_out != 0;\n    case StreamCodec::FlushOp::END:\n      return rc == LZMA_STREAM_END;\n    default:\n      throw std::invalid_argument(\"LZMA2StreamCodec: invalid FlushOp\");\n  }\n}\n\n/**\n * Attempts to decode a varint from input.\n * The function advances input by the number of bytes read.\n *\n * If there are too many bytes and the varint is not valid, throw a\n * runtime_error.\n *\n * If the uncompressed length was provided and a decoded varint does not match\n * the provided length, throw a runtime_error.\n *\n * Returns true if the varint was successfully decoded and matches the\n * uncompressed length if provided, and false if more bytes are needed.\n */\nbool LZMA2StreamCodec::decodeAndCheckVarint(ByteRange& input) {\n  if (input.empty()) {\n    return false;\n  }\n  size_t const numBytesToCopy =\n      std::min(kMaxVarintLength64 - varintBufferPos_, input.size());\n  memcpy(varintBuffer_.data() + varintBufferPos_, input.data(), numBytesToCopy);\n\n  size_t const rangeSize = varintBufferPos_ + numBytesToCopy;\n  ByteRange range{varintBuffer_.data(), rangeSize};\n  auto const ret = tryDecodeVarint(range);\n\n  if (ret.hasValue()) {\n    size_t const varintSize = rangeSize - range.size();\n    input.advance(varintSize - varintBufferPos_);\n    if (uncompressedLength() && *uncompressedLength() != ret.value()) {\n      throw std::runtime_error(\"LZMA2StreamCodec: invalid uncompressed length\");\n    }\n    return true;\n  } else if (ret.error() == DecodeVarintError::TooManyBytes) {\n    throw std::runtime_error(\"LZMA2StreamCodec: invalid uncompressed length\");\n  } else {\n    // Too few bytes\n    input.advance(numBytesToCopy);\n    varintBufferPos_ += numBytesToCopy;\n    return false;\n  }\n}\n\nbool LZMA2StreamCodec::doUncompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {\n  if (needReset_) {\n    resetDStream();\n    needReset_ = false;\n    needDecodeSize_ = encodeSize();\n    if (encodeSize()) {\n      // Reset buffer\n      varintBufferPos_ = 0;\n    }\n  }\n\n  if (needDecodeSize_) {\n    // Try decoding the varint. If the input does not contain the entire varint,\n    // buffer the input. If the varint can not be decoded, fail.\n    if (!decodeAndCheckVarint(input)) {\n      return false;\n    }\n    needDecodeSize_ = false;\n  }\n\n  dstream_->next_in = const_cast<uint8_t*>(input.data());\n  dstream_->avail_in = input.size();\n  dstream_->next_out = output.data();\n  dstream_->avail_out = output.size();\n  SCOPE_EXIT {\n    input.advance(input.size() - dstream_->avail_in);\n    output.advance(output.size() - dstream_->avail_out);\n  };\n\n  lzma_ret rc;\n  switch (flushOp) {\n    case StreamCodec::FlushOp::NONE:\n    case StreamCodec::FlushOp::FLUSH:\n      rc = lzmaThrowOnError(lzma_code(dstream_.get_pointer(), LZMA_RUN));\n      break;\n    case StreamCodec::FlushOp::END:\n      rc = lzmaThrowOnError(lzma_code(dstream_.get_pointer(), LZMA_FINISH));\n      break;\n    default:\n      throw std::invalid_argument(\"LZMA2StreamCodec: invalid flush\");\n  }\n  return rc == LZMA_STREAM_END;\n}\n#endif // FOLLY_HAVE_LIBLZMA\n\n#if FOLLY_HAVE_LIBZSTD\n\n} // namespace\n\nint zstdConvertLevel(int level) {\n  switch (level) {\n    case COMPRESSION_LEVEL_FASTEST:\n      return 1;\n    case COMPRESSION_LEVEL_DEFAULT:\n      return 1;\n    case COMPRESSION_LEVEL_BEST:\n      return 19;\n  }\n  if (level < 1 || level > ZSTD_maxCLevel()) {\n    throw std::invalid_argument(\n        to<std::string>(\"ZSTD: invalid level: \", level));\n  }\n  return level;\n}\n\nnamespace {\n\nint zstdFastConvertLevel(int level) {\n  switch (level) {\n    case COMPRESSION_LEVEL_FASTEST:\n      return -5;\n    case COMPRESSION_LEVEL_DEFAULT:\n      return -1;\n    case COMPRESSION_LEVEL_BEST:\n      return -1;\n  }\n  if (level < 1) {\n    throw std::invalid_argument(\n        to<std::string>(\"ZSTD: invalid level: \", level));\n  }\n  return -level;\n}\n\nstd::unique_ptr<Codec> getZstdCodec(int level, CodecType type) {\n  DCHECK(type == CodecType::ZSTD);\n  return zstd::getCodec(zstd::Options(zstdConvertLevel(level)));\n}\n\nstd::unique_ptr<StreamCodec> getZstdStreamCodec(int level, CodecType type) {\n  DCHECK(type == CodecType::ZSTD);\n  return zstd::getStreamCodec(zstd::Options(zstdConvertLevel(level)));\n}\n\nstd::unique_ptr<Codec> getZstdFastCodec(int level, CodecType type) {\n  DCHECK(type == CodecType::ZSTD_FAST);\n  return zstd::getCodec(zstd::Options(zstdFastConvertLevel(level)));\n}\n\nstd::unique_ptr<StreamCodec> getZstdFastStreamCodec(int level, CodecType type) {\n  DCHECK(type == CodecType::ZSTD_FAST);\n  return zstd::getStreamCodec(zstd::Options(zstdFastConvertLevel(level)));\n}\n\n#endif // FOLLY_HAVE_LIBZSTD\n\n#if FOLLY_HAVE_LIBBZ2\n\nclass Bzip2StreamCodec final : public StreamCodec {\n public:\n  static std::unique_ptr<Codec> createCodec(int level, CodecType type);\n  static std::unique_ptr<StreamCodec> createStream(int level, CodecType type);\n  explicit Bzip2StreamCodec(int level, CodecType type);\n\n  ~Bzip2StreamCodec() override;\n\n  std::vector<std::string> validPrefixes() const override;\n  bool canUncompress(\n      IOBuf const* data, Optional<uint64_t> uncompressedLength) const override;\n\n private:\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n\n  void doResetStream() override;\n  bool doCompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flushOp) override;\n  bool doUncompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flushOp) override;\n\n  void resetCStream();\n  void resetDStream();\n\n  Optional<bz_stream> cstream_{};\n  Optional<bz_stream> dstream_{};\n\n  int level_;\n  bool needReset_{true};\n};\n\n/* static */ std::unique_ptr<Codec> Bzip2StreamCodec::createCodec(\n    int level, CodecType type) {\n  return createStream(level, type);\n}\n\n/* static */ std::unique_ptr<StreamCodec> Bzip2StreamCodec::createStream(\n    int level, CodecType type) {\n  return std::make_unique<Bzip2StreamCodec>(level, type);\n}\n\nBzip2StreamCodec::Bzip2StreamCodec(int level, CodecType type)\n    : StreamCodec(type) {\n  DCHECK(type == CodecType::BZIP2);\n  switch (level) {\n    case COMPRESSION_LEVEL_FASTEST:\n      level = 1;\n      break;\n    case COMPRESSION_LEVEL_DEFAULT:\n      level = 9;\n      break;\n    case COMPRESSION_LEVEL_BEST:\n      level = 9;\n      break;\n  }\n  if (level < 1 || level > 9) {\n    throw std::invalid_argument(\n        to<std::string>(\"Bzip2: invalid level: \", level));\n  }\n  level_ = level;\n}\n\nuint32_t constexpr kBzip2MagicLE = 0x685a42;\nuint64_t constexpr kBzip2MagicBytes = 3;\n\nstd::vector<std::string> Bzip2StreamCodec::validPrefixes() const {\n  return {prefixToStringLE(kBzip2MagicLE, kBzip2MagicBytes)};\n}\n\nbool Bzip2StreamCodec::canUncompress(\n    IOBuf const* data, Optional<uint64_t>) const {\n  return dataStartsWithLE(data, kBzip2MagicLE, kBzip2MagicBytes);\n}\n\nuint64_t Bzip2StreamCodec::doMaxCompressedLength(\n    uint64_t uncompressedLength) const {\n  // http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html#bzbufftobuffcompress\n  //   To guarantee that the compressed data will fit in its buffer, allocate an\n  //   output buffer of size 1% larger than the uncompressed data, plus six\n  //   hundred extra bytes.\n  return uncompressedLength + uncompressedLength / 100 + 600;\n}\n\nbz_stream createBzStream() {\n  bz_stream stream;\n  stream.bzalloc = nullptr;\n  stream.bzfree = nullptr;\n  stream.opaque = nullptr;\n  stream.next_in = stream.next_out = nullptr;\n  stream.avail_in = stream.avail_out = 0;\n  return stream;\n}\n\n// Throws on error condition, otherwise returns the code.\nint bzCheck(int const rc) {\n  switch (rc) {\n    case BZ_OK:\n    case BZ_RUN_OK:\n    case BZ_FLUSH_OK:\n    case BZ_FINISH_OK:\n    case BZ_STREAM_END:\n    // Allow BZ_PARAM_ERROR.\n    // It can get returned if no progress is made, but we handle that.\n    case BZ_PARAM_ERROR:\n      return rc;\n    default:\n      throw std::runtime_error(to<std::string>(\"Bzip2 error: \", rc));\n  }\n}\n\nBzip2StreamCodec::~Bzip2StreamCodec() {\n  if (cstream_) {\n    BZ2_bzCompressEnd(cstream_.get_pointer());\n    cstream_.reset();\n  }\n  if (dstream_) {\n    BZ2_bzDecompressEnd(dstream_.get_pointer());\n    dstream_.reset();\n  }\n}\n\nvoid Bzip2StreamCodec::doResetStream() {\n  needReset_ = true;\n}\n\nvoid Bzip2StreamCodec::resetCStream() {\n  if (cstream_) {\n    BZ2_bzCompressEnd(cstream_.get_pointer());\n  }\n  cstream_ = createBzStream();\n  bzCheck(BZ2_bzCompressInit(cstream_.get_pointer(), level_, 0, 0));\n}\n\nint bzip2TranslateFlush(StreamCodec::FlushOp flushOp) {\n  switch (flushOp) {\n    case StreamCodec::FlushOp::NONE:\n      return BZ_RUN;\n    case StreamCodec::FlushOp::END:\n      return BZ_FINISH;\n    case StreamCodec::FlushOp::FLUSH:\n      throw std::invalid_argument(\n          \"Bzip2StreamCodec: FlushOp::FLUSH not supported\");\n    default:\n      throw std::invalid_argument(\"Bzip2StreamCodec: Invalid flush\");\n  }\n}\n\nbool Bzip2StreamCodec::doCompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {\n  // Bzip2 uses uint32_t for sizes, so we can't compress more than 4GB at a time\n  return detail::chunkedStream(\n      detail::kDefaultChunkSizeFor32BitSizes,\n      input,\n      output,\n      flushOp,\n      [this](auto& input, auto& output, auto flushOp) {\n        if (needReset_) {\n          resetCStream();\n          needReset_ = false;\n        }\n        if (input.empty() && output.empty()) {\n          return false;\n        }\n\n        cstream_->next_in =\n            const_cast<char*>(reinterpret_cast<const char*>(input.data()));\n        cstream_->avail_in = to_narrow(input.size());\n        cstream_->next_out = reinterpret_cast<char*>(output.data());\n        cstream_->avail_out = to_narrow(output.size());\n        DCHECK_EQ(cstream_->avail_in, input.size());\n        DCHECK_EQ(cstream_->avail_out, output.size());\n        SCOPE_EXIT {\n          input.uncheckedAdvance(input.size() - cstream_->avail_in);\n          output.uncheckedAdvance(output.size() - cstream_->avail_out);\n        };\n        int const rc = bzCheck(BZ2_bzCompress(\n            cstream_.get_pointer(), bzip2TranslateFlush(flushOp)));\n        switch (flushOp) {\n          case StreamCodec::FlushOp::NONE:\n            return false;\n          case StreamCodec::FlushOp::FLUSH:\n            if (rc == BZ_RUN_OK) {\n              DCHECK_EQ(cstream_->avail_in, 0);\n              DCHECK(input.empty() || cstream_->avail_out != output.size());\n              return true;\n            }\n            return false;\n          case StreamCodec::FlushOp::END:\n            return rc == BZ_STREAM_END;\n          default:\n            throw std::invalid_argument(\"Bzip2StreamCodec: invalid FlushOp\");\n        }\n        return false;\n      });\n}\n\nvoid Bzip2StreamCodec::resetDStream() {\n  if (dstream_) {\n    BZ2_bzDecompressEnd(dstream_.get_pointer());\n  }\n  dstream_ = createBzStream();\n  bzCheck(BZ2_bzDecompressInit(dstream_.get_pointer(), 0, 0));\n}\n\nbool Bzip2StreamCodec::doUncompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {\n  // Bzip2 uses uint32_t for sizes, so we can't uncompress more than 4GB at a\n  // time\n  return detail::chunkedStream(\n      detail::kDefaultChunkSizeFor32BitSizes,\n      input,\n      output,\n      flushOp,\n      [this](auto& input, auto& output, auto flushOp) {\n        if (flushOp == StreamCodec::FlushOp::FLUSH) {\n          throw std::invalid_argument(\n              \"Bzip2StreamCodec: FlushOp::FLUSH not supported\");\n        }\n        if (needReset_) {\n          resetDStream();\n          needReset_ = false;\n        }\n\n        dstream_->next_in =\n            const_cast<char*>(reinterpret_cast<const char*>(input.data()));\n        dstream_->avail_in = to_narrow(input.size());\n        dstream_->next_out = reinterpret_cast<char*>(output.data());\n        dstream_->avail_out = to_narrow(output.size());\n        DCHECK_EQ(dstream_->avail_in, input.size());\n        DCHECK_EQ(dstream_->avail_out, output.size());\n        SCOPE_EXIT {\n          input.uncheckedAdvance(input.size() - dstream_->avail_in);\n          output.uncheckedAdvance(output.size() - dstream_->avail_out);\n        };\n        int const rc = bzCheck(BZ2_bzDecompress(dstream_.get_pointer()));\n        return rc == BZ_STREAM_END;\n      });\n}\n\n#endif // FOLLY_HAVE_LIBBZ2\n\n#if FOLLY_HAVE_LIBZ\n\nzlib::Options getZlibOptions(CodecType type) {\n  DCHECK(type == CodecType::GZIP || type == CodecType::ZLIB);\n  return type == CodecType::GZIP\n      ? zlib::defaultGzipOptions()\n      : zlib::defaultZlibOptions();\n}\n\nstd::unique_ptr<Codec> getZlibCodec(int level, CodecType type) {\n  return zlib::getCodec(getZlibOptions(type), level);\n}\n\nstd::unique_ptr<StreamCodec> getZlibStreamCodec(int level, CodecType type) {\n  return zlib::getStreamCodec(getZlibOptions(type), level);\n}\n\n#endif // FOLLY_HAVE_LIBZ\n\n/**\n * Automatic decompression\n */\nclass AutomaticCodec final : public Codec {\n public:\n  static std::unique_ptr<Codec> create(\n      std::vector<std::unique_ptr<Codec>> customCodecs,\n      std::unique_ptr<Codec> terminalCodec);\n  explicit AutomaticCodec(\n      std::vector<std::unique_ptr<Codec>> customCodecs,\n      std::unique_ptr<Codec> terminalCodec);\n\n  std::vector<std::string> validPrefixes() const override;\n  bool canUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) const override;\n\n private:\n  bool doNeedsUncompressedLength() const override;\n  uint64_t doMaxUncompressedLength() const override;\n\n  uint64_t doMaxCompressedLength(uint64_t) const override {\n    throw std::runtime_error(\n        \"AutomaticCodec error: maxCompressedLength() not supported.\");\n  }\n  std::unique_ptr<IOBuf> doCompress(const IOBuf*) override {\n    throw std::runtime_error(\"AutomaticCodec error: compress() not supported.\");\n  }\n  std::unique_ptr<IOBuf> doUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) override;\n\n  void addCodecIfSupported(CodecType type);\n\n  // Throws iff the codecs aren't compatible (very slow)\n  void checkCompatibleCodecs() const;\n\n  std::vector<std::unique_ptr<Codec>> codecs_;\n  std::unique_ptr<Codec> terminalCodec_;\n  bool needsUncompressedLength_;\n  uint64_t maxUncompressedLength_;\n};\n\nstd::vector<std::string> AutomaticCodec::validPrefixes() const {\n  std::unordered_set<std::string> prefixes;\n  for (const auto& codec : codecs_) {\n    const auto codecPrefixes = codec->validPrefixes();\n    prefixes.insert(codecPrefixes.begin(), codecPrefixes.end());\n  }\n  return std::vector<std::string>{prefixes.begin(), prefixes.end()};\n}\n\nbool AutomaticCodec::canUncompress(\n    const IOBuf* data, Optional<uint64_t> uncompressedLength) const {\n  return std::any_of(\n      codecs_.begin(),\n      codecs_.end(),\n      [data, uncompressedLength](std::unique_ptr<Codec> const& codec) {\n        return codec->canUncompress(data, uncompressedLength);\n      });\n}\n\nvoid AutomaticCodec::addCodecIfSupported(CodecType type) {\n  const bool present = std::any_of(\n      codecs_.begin(),\n      codecs_.end(),\n      [&type](std::unique_ptr<Codec> const& codec) {\n        return codec->type() == type;\n      });\n  bool const isTerminalType = terminalCodec_ && terminalCodec_->type() == type;\n  if (hasCodec(type) && !present && !isTerminalType) {\n    codecs_.push_back(getCodec(type));\n  }\n}\n\n/* static */ std::unique_ptr<Codec> AutomaticCodec::create(\n    std::vector<std::unique_ptr<Codec>> customCodecs,\n    std::unique_ptr<Codec> terminalCodec) {\n  return std::make_unique<AutomaticCodec>(\n      std::move(customCodecs), std::move(terminalCodec));\n}\n\nAutomaticCodec::AutomaticCodec(\n    std::vector<std::unique_ptr<Codec>> customCodecs,\n    std::unique_ptr<Codec> terminalCodec)\n    : Codec(CodecType::USER_DEFINED, folly::none, \"auto\"),\n      codecs_(std::move(customCodecs)),\n      terminalCodec_(std::move(terminalCodec)) {\n  // Fastest -> slowest\n  std::array<CodecType, 6> defaultTypes{{\n      CodecType::LZ4_FRAME,\n      CodecType::ZSTD,\n      CodecType::ZLIB,\n      CodecType::GZIP,\n      CodecType::LZMA2,\n      CodecType::BZIP2,\n  }};\n\n  for (auto type : defaultTypes) {\n    addCodecIfSupported(type);\n  }\n\n  if (kIsDebug) {\n    checkCompatibleCodecs();\n  }\n\n  // Check that none of the codecs are null\n  DCHECK(\n      std::none_of(\n          codecs_.begin(),\n          codecs_.end(),\n          [](std::unique_ptr<Codec> const& codec) {\n            return codec == nullptr;\n          }));\n\n  // Check that the terminal codec's type is not duplicated (with the exception\n  // of USER_DEFINED).\n  if (terminalCodec_) {\n    DCHECK(\n        std::none_of(\n            codecs_.begin(),\n            codecs_.end(),\n            [&](std::unique_ptr<Codec> const& codec) {\n              return codec->type() != CodecType::USER_DEFINED &&\n                  codec->type() == terminalCodec_->type();\n            }));\n  }\n\n  bool const terminalNeedsUncompressedLength =\n      terminalCodec_ && terminalCodec_->needsUncompressedLength();\n  needsUncompressedLength_ =\n      std::any_of(\n          codecs_.begin(),\n          codecs_.end(),\n          [](std::unique_ptr<Codec> const& codec) {\n            return codec->needsUncompressedLength();\n          }) ||\n      terminalNeedsUncompressedLength;\n\n  const auto it = std::max_element(\n      codecs_.begin(),\n      codecs_.end(),\n      [](std::unique_ptr<Codec> const& lhs, std::unique_ptr<Codec> const& rhs) {\n        return lhs->maxUncompressedLength() < rhs->maxUncompressedLength();\n      });\n  DCHECK(it != codecs_.end());\n  auto const terminalMaxUncompressedLength =\n      terminalCodec_ ? terminalCodec_->maxUncompressedLength() : 0;\n  maxUncompressedLength_ =\n      std::max((*it)->maxUncompressedLength(), terminalMaxUncompressedLength);\n}\n\nvoid AutomaticCodec::checkCompatibleCodecs() const {\n  // Keep track of all the possible headers.\n  std::unordered_set<std::string> headers;\n  // The empty header is not allowed.\n  headers.insert(\"\");\n  // Step 1:\n  // Construct a set of headers and check that none of the headers occur twice.\n  // Eliminate edge cases.\n  for (auto&& codec : codecs_) {\n    const auto codecHeaders = codec->validPrefixes();\n    // Codecs without any valid headers are not allowed.\n    if (codecHeaders.empty()) {\n      throw std::invalid_argument{\n          \"AutomaticCodec: validPrefixes() must not be empty.\"};\n    }\n    // Insert all the headers for the current codec.\n    const size_t beforeSize = headers.size();\n    headers.insert(codecHeaders.begin(), codecHeaders.end());\n    // Codecs are not compatible if any header occurred twice.\n    if (beforeSize + codecHeaders.size() != headers.size()) {\n      throw std::invalid_argument{\n          \"AutomaticCodec: Two valid prefixes collide.\"};\n    }\n  }\n  // Step 2:\n  // Check if any strict non-empty prefix of any header is a header.\n  for (const auto& header : headers) {\n    for (size_t i = 1; i < header.size(); ++i) {\n      if (headers.count(header.substr(0, i))) {\n        throw std::invalid_argument{\n            \"AutomaticCodec: One valid prefix is a prefix of another valid \"\n            \"prefix.\"};\n      }\n    }\n  }\n}\n\nbool AutomaticCodec::doNeedsUncompressedLength() const {\n  return needsUncompressedLength_;\n}\n\nuint64_t AutomaticCodec::doMaxUncompressedLength() const {\n  return maxUncompressedLength_;\n}\n\nstd::unique_ptr<IOBuf> AutomaticCodec::doUncompress(\n    const IOBuf* data, Optional<uint64_t> uncompressedLength) {\n  try {\n    for (auto&& codec : codecs_) {\n      if (codec->canUncompress(data, uncompressedLength)) {\n        return codec->uncompress(data, uncompressedLength);\n      }\n    }\n  } catch (std::exception const& e) {\n    if (!terminalCodec_) {\n      throw e;\n    }\n  }\n\n  // Try terminal codec\n  if (terminalCodec_) {\n    return terminalCodec_->uncompress(data, uncompressedLength);\n  }\n\n  throw std::runtime_error(\"AutomaticCodec error: Unknown compressed data\");\n}\n\nusing CodecFactory = std::unique_ptr<Codec> (*)(int, CodecType);\nusing StreamCodecFactory = std::unique_ptr<StreamCodec> (*)(int, CodecType);\nstruct Factory {\n  CodecFactory codec;\n  StreamCodecFactory stream;\n};\n\nconstexpr Factory codecFactories[static_cast<size_t>(\n    CodecType::NUM_CODEC_TYPES)] = {\n    {}, // USER_DEFINED\n    {NoCompressionCodec::create, nullptr},\n\n#if FOLLY_HAVE_LIBLZ4\n    {LZ4Codec::create, nullptr},\n#else\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBSNAPPY\n    {SnappyCodec::create, nullptr},\n#else\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBZ\n    {getZlibCodec, getZlibStreamCodec},\n#else\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBLZ4\n    {LZ4Codec::create, nullptr},\n#else\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBLZMA\n    {LZMA2StreamCodec::createCodec, LZMA2StreamCodec::createStream},\n    {LZMA2StreamCodec::createCodec, LZMA2StreamCodec::createStream},\n#else\n    {},\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBZSTD\n    {getZstdCodec, getZstdStreamCodec},\n#else\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBZ\n    {getZlibCodec, getZlibStreamCodec},\n#else\n    {},\n#endif\n\n#if (FOLLY_HAVE_LIBLZ4 && LZ4_VERSION_NUMBER >= 10301)\n    {LZ4FrameCodec::create, nullptr},\n#else\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBBZ2\n    {Bzip2StreamCodec::createCodec, Bzip2StreamCodec::createStream},\n#else\n    {},\n#endif\n\n#if FOLLY_HAVE_LIBZSTD\n    {getZstdFastCodec, getZstdFastStreamCodec},\n#else\n    {},\n#endif\n};\n\nFactory const& getFactory(CodecType type) {\n  auto const idx = static_cast<size_t>(type);\n  if (idx >= static_cast<size_t>(CodecType::NUM_CODEC_TYPES)) {\n    throw std::invalid_argument(\n        to<std::string>(\"Compression type \", idx, \" invalid\"));\n  }\n  return codecFactories[idx];\n}\n} // namespace\n\nbool hasCodec(CodecType type) {\n  return getFactory(type).codec != nullptr;\n}\n\nstd::unique_ptr<Codec> getCodec(CodecType type, int level) {\n  auto const factory = getFactory(type).codec;\n  if (!factory) {\n    throw std::invalid_argument(\n        to<std::string>(\"Compression type \", type, \" not supported\"));\n  }\n  auto codec = (*factory)(level, type);\n  DCHECK(codec->type() == type);\n  return codec;\n}\n\nbool hasStreamCodec(CodecType type) {\n  return getFactory(type).stream != nullptr;\n}\n\nstd::unique_ptr<StreamCodec> getStreamCodec(CodecType type, int level) {\n  auto const factory = getFactory(type).stream;\n  if (!factory) {\n    throw std::invalid_argument(\n        to<std::string>(\"Compression type \", type, \" not supported\"));\n  }\n  auto codec = (*factory)(level, type);\n  DCHECK(codec->type() == type);\n  return codec;\n}\n\nstd::unique_ptr<Codec> getAutoUncompressionCodec(\n    std::vector<std::unique_ptr<Codec>> customCodecs,\n    std::unique_ptr<Codec> terminalCodec) {\n  return AutomaticCodec::create(\n      std::move(customCodecs), std::move(terminalCodec));\n}\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/Compression.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <limits>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/io/IOBuf.h>\n\n/**\n * Compression / decompression over IOBufs\n */\n\nnamespace folly {\nnamespace compression {\n\nenum class CodecType {\n  /**\n   * This codec type is not defined; getCodec() will throw an exception\n   * if used. Useful if deriving your own classes from Codec without\n   * going through the getCodec() interface.\n   */\n  USER_DEFINED = 0,\n\n  /**\n   * Use no compression.\n   * Levels supported: 0\n   */\n  NO_COMPRESSION = 1,\n\n  /**\n   * Use LZ4 compression.\n   * Levels supported: 1 = fast, 2 = best; default = 1\n   */\n  LZ4 = 2,\n\n  /**\n   * Use Snappy compression.\n   * Levels supported: 1\n   */\n  SNAPPY = 3,\n\n  /**\n   * Use zlib compression.\n   * Levels supported: 0 = no compression, 1 = fast, ..., 9 = best; default = 6\n   * Streaming compression is supported.\n   */\n  ZLIB = 4,\n\n  /**\n   * Use LZ4 compression, prefixed with size (as Varint).\n   */\n  LZ4_VARINT_SIZE = 5,\n\n  /**\n   * Use LZMA2 compression.\n   * Levels supported: 0 = no compression, 1 = fast, ..., 9 = best; default = 6\n   * Streaming compression is supported.\n   */\n  LZMA2 = 6,\n  LZMA2_VARINT_SIZE = 7,\n\n  /**\n   * Use ZSTD compression.\n   * Levels supported: 1 = fast, ..., 19 = best; default = 1\n   * Use ZSTD_FAST for the fastest zstd compression (negative levels).\n   * Streaming compression is supported.\n   */\n  ZSTD = 8,\n\n  /**\n   * Use gzip compression.  This is the same compression algorithm as ZLIB but\n   * gzip-compressed files tend to be easier to work with from the command line.\n   * Levels supported: 0 = no compression, 1 = fast, ..., 9 = best; default = 6\n   * Streaming compression is supported.\n   */\n  GZIP = 9,\n\n  /**\n   * Use LZ4 frame compression.\n   * Levels supported: 0 = fast, 16 = best; default = 0\n   */\n  LZ4_FRAME = 10,\n\n  /**\n   * Use bzip2 compression.\n   * Levels supported: 1 = fast, 9 = best; default = 9\n   * Streaming compression is supported BUT FlushOp::FLUSH does NOT ensure that\n   * the decompressor can read all the data up to that point, due to a bug in\n   * the bzip2 library.\n   */\n  BZIP2 = 11,\n\n  /**\n   * Use ZSTD compression with a negative compression level (1=-1, 2=-2, ...).\n   * Higher compression levels mean faster.\n   * Level 1 is around the same speed as Snappy with better compression.\n   * Level 5 is around the same speed as LZ4 with slightly worse compression.\n   * Each level gains about 6-15% speed and loses 3-7% compression.\n   * Decompression speed improves for each level, and level 1 decompression\n   * speed is around 25% faster than ZSTD.\n   * This codec is fully compatible with ZSTD.\n   * Levels supported: 1 = best, ..., 5 = fast; default = 1\n   * Streaming compression is supported.\n   */\n  ZSTD_FAST = 12,\n\n  NUM_CODEC_TYPES = 13,\n};\n\nclass Codec {\n public:\n  virtual ~Codec() {}\n\n  static constexpr uint64_t UNLIMITED_UNCOMPRESSED_LENGTH = uint64_t(-1);\n  /**\n   * Return the maximum length of data that may be compressed with this codec.\n   * NO_COMPRESSION and ZLIB support arbitrary lengths;\n   * LZ4 supports up to 1.9GiB; SNAPPY supports up to 4GiB.\n   * May return UNLIMITED_UNCOMPRESSED_LENGTH if unlimited.\n   */\n  uint64_t maxUncompressedLength() const;\n\n  /**\n   * Return the codec's type.\n   */\n  CodecType type() const { return type_; }\n\n  /**\n   * Does this codec need the exact uncompressed length on decompression?\n   */\n  bool needsUncompressedLength() const;\n\n  /**\n   * Compress data, returning an IOBuf (which may share storage with data).\n   * Throws std::invalid_argument if data is larger than\n   * maxUncompressedLength().\n   */\n  std::unique_ptr<IOBuf> compress(const folly::IOBuf* data);\n\n  /**\n   * Compresses data. May involve additional copies compared to the overload\n   * that takes and returns IOBufs. Has the same error semantics as the IOBuf\n   * version.\n   */\n  std::string compress(StringPiece data);\n\n  /**\n   * Uncompress data. Throws std::runtime_error on decompression error.\n   *\n   * Some codecs (LZ4) require the exact uncompressed length; this is indicated\n   * by needsUncompressedLength().\n   *\n   * For other codes (zlib), knowing the exact uncompressed length ahead of\n   * time might be faster.\n   *\n   * Regardless of the behavior of the underlying compressor, uncompressing\n   * an empty IOBuf chain will return an empty IOBuf chain.\n   */\n  std::unique_ptr<IOBuf> uncompress(\n      const IOBuf* data,\n      folly::Optional<uint64_t> uncompressedLength = folly::none);\n\n  /**\n   * Uncompresses data. May involve additional copies compared to the overload\n   * that takes and returns IOBufs. Has the same error semantics as the IOBuf\n   * version.\n   */\n  std::string uncompress(\n      StringPiece data,\n      folly::Optional<uint64_t> uncompressedLength = folly::none);\n\n  /**\n   * Returns a bound on the maximum compressed length when compressing data with\n   * the given uncompressed length.\n   */\n  uint64_t maxCompressedLength(uint64_t uncompressedLength) const;\n\n  /**\n   * Extracts the uncompressed length from the compressed data if possible.\n   * If the codec doesn't store the uncompressed length, or the data is\n   * corrupted it returns the given uncompressedLength.\n   * If the uncompressed length is stored in the compressed data and\n   * uncompressedLength is not none and they do not match a std::runtime_error\n   * is thrown.\n   */\n  folly::Optional<uint64_t> getUncompressedLength(\n      const folly::IOBuf* data,\n      folly::Optional<uint64_t> uncompressedLength = folly::none) const;\n\n  /**\n   * Helper wrapper around getUncompressedLength(IOBuf)\n   */\n  folly::Optional<uint64_t> getUncompressedLength(\n      folly::StringPiece data,\n      folly::Optional<uint64_t> uncompressedLength = folly::none) const;\n\n protected:\n  Codec(\n      CodecType type,\n      folly::Optional<int> level = folly::none,\n      folly::StringPiece name = {});\n\n public:\n  /**\n   * Returns a superset of the set of prefixes for which canUncompress() will\n   * return true. A superset is allowed for optimizations in canUncompress()\n   * based on other knowledge such as length. None of the prefixes may be empty.\n   * default: No prefixes.\n   */\n  virtual std::vector<std::string> validPrefixes() const;\n\n  /**\n   * Returns true if the codec thinks it can uncompress the data.\n   * If a codec doesn't have magic bytes at the beginning, like LZ4 and Snappy,\n   * it can always return false.\n   * default: Returns false.\n   */\n  virtual bool canUncompress(\n      const folly::IOBuf* data,\n      folly::Optional<uint64_t> uncompressedLength = folly::none) const;\n\n  /**\n   * Helper wrapper around canUncompress(IOBuf)\n   */\n  bool canUncompress(\n      folly::StringPiece data,\n      folly::Optional<uint64_t> uncompressedLength = folly::none) const;\n\n private:\n  // default: no limits (save for special value UNKNOWN_UNCOMPRESSED_LENGTH)\n  virtual uint64_t doMaxUncompressedLength() const;\n  // default: doesn't need uncompressed length\n  virtual bool doNeedsUncompressedLength() const;\n  virtual std::unique_ptr<IOBuf> doCompress(const folly::IOBuf* data) = 0;\n  virtual std::unique_ptr<IOBuf> doUncompress(\n      const folly::IOBuf* data,\n      folly::Optional<uint64_t> uncompressedLength) = 0;\n  // default: an implementation is provided by default to wrap the strings into\n  // IOBufs and delegate to the IOBuf methods. This incurs a copy of the output\n  // from IOBuf to string. Implementers, at their discretion, can override\n  // these methods to avoid the copy.\n  virtual std::string doCompressString(StringPiece data);\n  virtual std::string doUncompressString(\n      StringPiece data, folly::Optional<uint64_t> uncompressedLength);\n\n  virtual uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const = 0;\n  // default: returns the passed uncompressedLength.\n  virtual folly::Optional<uint64_t> doGetUncompressedLength(\n      const folly::IOBuf* data,\n      folly::Optional<uint64_t> uncompressedLength) const;\n\n  CodecType type_;\n};\n\nclass StreamCodec : public Codec {\n public:\n  ~StreamCodec() override {}\n\n  /**\n   * Does the codec need the data length before compression streaming?\n   */\n  bool needsDataLength() const;\n\n  /*****************************************************************************\n   * Streaming API\n   *****************************************************************************\n   * A low-level stateful streaming API.\n   * Streaming operations can be started in two ways:\n   *   1. From a clean Codec on which no non-const methods have been called.\n   *   2. A call to resetStream(), which will reset any codec to a clean state.\n   * After a streaming operation has begun, either compressStream() or\n   * uncompressStream() must be called until the streaming operation ends.\n   * compressStream() ends when it returns true with flushOp END.\n   * uncompressStream() ends when it returns true. At this point the codec\n   * may be reused by calling resetStream().\n   *\n   * compress() and uncompress() can be called at any time, but they interrupt\n   * any ongoing streaming operations (state is lost and resetStream() must be\n   * called before another streaming operation).\n   */\n\n  /**\n   * Reset the state of the codec, and set the uncompressed length for the next\n   * streaming operation. If uncompressedLength is not none it must be exactly\n   * the uncompressed length. compressStream() must be passed exactly\n   * uncompressedLength input bytes before the stream is ended.\n   * uncompressStream() must be passed a compressed frame that uncompresses to\n   * uncompressedLength.\n   */\n  void resetStream(folly::Optional<uint64_t> uncompressedLength = folly::none);\n\n  enum class FlushOp { NONE, FLUSH, END };\n\n  /**\n   * Compresses some data from the input buffer and writes the compressed data\n   * into the output buffer. It may read input without producing any output,\n   * except when forced to flush.\n   *\n   * The input buffer is advanced to point to the range of data that hasn't yet\n   * been read. Compression will resume at this point for the next call to\n   * compressStream(). The output buffer is advanced one byte past the last byte\n   * written.\n   *\n   * The default flushOp is NONE, which allows compressStream() complete\n   * discretion in how much data to gather before writing any output.\n   * Always returns false in with FlushOp::NONE.\n   *\n   * If flushOp is END, all pending and input data is flushed to the output\n   * buffer, and the frame is ended. compressStream() must be called with the\n   * same input and flushOp END until it returns true. At this point the caller\n   * must call resetStream() to use the codec again.\n   *\n   * If flushOp is FLUSH, all pending and input data is flushed to the output\n   * buffer, but the frame is not ended. compressStream() must be called with\n   * the same input and flushOp FLUSH until it returns true. At this point the\n   * caller can continue to compressStream() with any input data and flushOp.\n   * The uncompressor, if passed all the produced output data, will be able to\n   * uncompress all the input data passed to compressStream() so far. Excessive\n   * use of flushOp FLUSH will deteriorate compression ratio. This is useful for\n   * stateful streaming across a network. Most users don't need to use this\n   * flushOp.\n   *\n   * A std::logic_error is thrown on incorrect usage of the API.\n   * A std::runtime_error is thrown upon error conditions or if no forward\n   * progress could be made twice in a row.\n   *\n   * @returns true iff @p flushOp is FLUSH or END and the entire @p input\n   * has been consumed and flushed into @p output. If @p flushOp is END, then\n   * the compressed frame is complete. Always returns false if @p flushOp is\n   * NONE.\n   */\n  bool compressStream(\n      folly::ByteRange& input,\n      folly::MutableByteRange& output,\n      FlushOp flushOp = StreamCodec::FlushOp::NONE);\n\n  /**\n   * Uncompresses some data from the input buffer and writes the uncompressed\n   * data into the output buffer. It may read input without producing any\n   * output.\n   *\n   * The input buffer is advanced to point to the range of data that hasn't yet\n   * been read. Uncompression will resume at this point for the next call to\n   * uncompressStream(). The output buffer is advanced one byte past the last\n   * byte written.\n   *\n   * The default flushOp is NONE, which allows uncompressStream() complete\n   * discretion in how much output data to flush. The uncompressor may not make\n   * maximum forward progress, but will make some forward progress when\n   * possible.\n   *\n   * If flushOp is END, the caller guarantees that no more input will be\n   * presented to uncompressStream(). uncompressStream() must be called with the\n   * same input and flushOp END until it returns true. This is not mandatory,\n   * but if the input is all available in one buffer, and there is enough output\n   * space to write the entire frame, codecs can uncompress faster.\n   *\n   * If flushOp is FLUSH, uncompressStream() is guaranteed to make the maximum\n   * amount of forward progress possible. When using this flushOp and\n   * uncompressStream() returns with `!output.empty()` the caller knows that all\n   * pending output has been flushed. This is useful for stateful streaming\n   * across a network, and it should be used in conjunction with\n   * compressStream() with flushOp FLUSH. Most users don't need to use this\n   * flushOp.\n   *\n   * A std::runtime_error is thrown upon error conditions or if no forward\n   * progress could be made upon two consecutive calls to the function (only the\n   * second call will throw an exception).\n   *\n   * @returns true at the end of a frame. At this point resetStream() must be\n   * called to reuse the codec.\n   */\n  bool uncompressStream(\n      folly::ByteRange& input,\n      folly::MutableByteRange& output,\n      FlushOp flushOp = StreamCodec::FlushOp::NONE);\n\n protected:\n  StreamCodec(\n      CodecType type,\n      folly::Optional<int> level = folly::none,\n      folly::StringPiece name = {})\n      : Codec(type, std::move(level), name) {}\n\n  // Returns the uncompressed length last passed to resetStream() or none if it\n  // hasn't been called yet.\n  folly::Optional<uint64_t> uncompressedLength() const {\n    return uncompressedLength_;\n  }\n\n private:\n  // default: Implemented using the streaming API.\n  std::unique_ptr<IOBuf> doCompress(const folly::IOBuf* data) override;\n  std::unique_ptr<IOBuf> doUncompress(\n      const folly::IOBuf* data,\n      folly::Optional<uint64_t> uncompressedLength) override;\n\n  // default: Returns false\n  virtual bool doNeedsDataLength() const;\n  virtual void doResetStream() = 0;\n  virtual bool doCompressStream(\n      folly::ByteRange& input,\n      folly::MutableByteRange& output,\n      FlushOp flushOp) = 0;\n  virtual bool doUncompressStream(\n      folly::ByteRange& input,\n      folly::MutableByteRange& output,\n      FlushOp flushOp) = 0;\n\n  enum class State {\n    RESET,\n    COMPRESS,\n    COMPRESS_FLUSH,\n    COMPRESS_END,\n    UNCOMPRESS,\n    END,\n  };\n  void assertStateIs(State expected) const;\n\n  State state_{State::RESET};\n  ByteRange previousInput_{};\n  folly::Optional<uint64_t> uncompressedLength_{};\n  bool progressMade_{true};\n};\n\nconstexpr int COMPRESSION_LEVEL_FASTEST = -1;\nconstexpr int COMPRESSION_LEVEL_DEFAULT = -2;\nconstexpr int COMPRESSION_LEVEL_BEST = -3;\n\n/**\n * Return a codec for the given type. Throws on error.  The level\n * is a non-negative codec-dependent integer indicating the level of\n * compression desired, or one of the following constants:\n *\n * COMPRESSION_LEVEL_FASTEST is fastest (uses least CPU / memory,\n *   worst compression)\n * COMPRESSION_LEVEL_DEFAULT is the default (likely a tradeoff between\n *   FASTEST and BEST)\n * COMPRESSION_LEVEL_BEST is the best compression (uses most CPU / memory,\n *   best compression)\n *\n * When decompressing, the compression level is ignored. All codecs will\n * decompress all data compressed with the a codec of the same type, regardless\n * of compression level.\n */\nstd::unique_ptr<Codec> getCodec(\n    CodecType type, int level = COMPRESSION_LEVEL_DEFAULT);\n\n/**\n * Return a codec for the given type. Throws on error.  The level\n * is a non-negative codec-dependent integer indicating the level of\n * compression desired, or one of the following constants:\n *\n * COMPRESSION_LEVEL_FASTEST is fastest (uses least CPU / memory,\n *   worst compression)\n * COMPRESSION_LEVEL_DEFAULT is the default (likely a tradeoff between\n *   FASTEST and BEST)\n * COMPRESSION_LEVEL_BEST is the best compression (uses most CPU / memory,\n *   best compression)\n *\n * When decompressing, the compression level is ignored. All codecs will\n * decompress all data compressed with the a codec of the same type, regardless\n * of compression level.\n */\nstd::unique_ptr<StreamCodec> getStreamCodec(\n    CodecType type, int level = COMPRESSION_LEVEL_DEFAULT);\n\n/**\n * Returns a codec that can uncompress any of the given codec types as well as\n * {LZ4_FRAME, ZSTD, ZLIB, GZIP, LZMA2, BZIP2}. Appends each default codec to\n * customCodecs in order, so long as a codec with the same type() isn't already\n * present in customCodecs or as the terminalCodec. When uncompress() is called,\n * each codec's canUncompress() is called in the order that they are given.\n * Appended default codecs are checked last.  uncompress() is called on the\n * first codec whose canUncompress() returns true.\n *\n * In addition, an optional `terminalCodec` can be provided. This codec's\n * uncompress() will be called either when no other codec canUncompress() the\n * data or the chosen codec throws an exception on the data. The terminalCodec\n * is intended for ambiguous headers, when canUncompress() is false for some\n * data it can actually uncompress. The terminalCodec does not need to override\n * validPrefixes() or canUncompress() and overriding these functions will have\n * no effect on the returned codec's validPrefixes() or canUncompress()\n * functions. The terminalCodec's needsUncompressedLength() and\n * maxUncompressedLength() will affect the returned codec's respective\n * functions. The terminalCodec must not be duplicated in customCodecs.\n *\n * An exception is thrown if no codec canUncompress() the data and either no\n * terminal codec was provided or a terminal codec was provided and it throws on\n * the data.\n * An exception is thrown if the chosen codec's uncompress() throws on the data\n * and either no terminal codec was provided or a terminal codec was provided\n * and it also throws on the data.\n * An exception is thrown if compress() is called on the returned codec.\n *\n * Requirements are checked in debug mode and are as follows:\n * Let headers be the concatenation of every codec's validPrefixes().\n *  1. Each codec must override validPrefixes() and canUncompress().\n *  2. No codec's validPrefixes() may be empty.\n *  3. No header in headers may be empty.\n *  4. headers must not contain any duplicate elements.\n *  5. No strict non-empty prefix of any header in headers may be in headers.\n *  6. The terminalCodec's type must not be the same as any other codec's type\n *     (with USER_DEFINED being the exception).\n */\nstd::unique_ptr<Codec> getAutoUncompressionCodec(\n    std::vector<std::unique_ptr<Codec>> customCodecs = {},\n    std::unique_ptr<Codec> terminalCodec = {});\n\n/**\n * Check if a specified codec is supported.\n */\nbool hasCodec(CodecType type);\n\n/**\n * Check if a specified codec is supported and supports streaming.\n */\nbool hasStreamCodec(CodecType type);\n\n/**\n * Convert a folly abstract compression level (COMPRESSION_LEVEL_FASTEST,\n * COMPRESSION_LEVEL_DEFAULT, COMPRESSION_LEVEL_BEST) to a raw ZSTD\n * compression level (1-22). Passes through raw levels in [1, ZSTD_maxCLevel()]\n * unchanged. Throws std::invalid_argument for out-of-range levels.\n */\nint zstdConvertLevel(int level);\n\n/**\n * Added here so users of folly can figure out whether the header\n * folly/compression/CompressionContextPoolSingletons.h is present, and\n * therefore whether it can be included.\n */\n#define FOLLY_COMPRESSION_HAS_CONTEXT_POOL_SINGLETONS\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/CompressionContextPool.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include <folly/Memory.h>\n#include <folly/Synchronized.h>\n\n/**\n * Temporary implementation detail:\n *\n * The compression context pool singletons are actually used during static\n * initialization time by other modules, so those singletons need to avoid\n * SIOF issues. They do that by being marked `constinit`, so that they have\n * already been constructed before static initialization / program execution\n * begins. That means that the context pools need `constexpr` constructors.\n * `std::vector` is supposed to have a `constexpr` constructor in C++20, but\n * we don't seem to have it (everywhere).\n *\n * So while we can't rely on `constexpr std::vector::vector()`, we wrap the\n * vector with a `std::optional`. When we can `constexpr` construct the vector,\n * we do so. Otherwise, we construct it as empty and have to add checks to\n * populate it later.\n *\n * When folly is only being built on platforms that have this, we can remove\n * the `std::optional` shim etc.\n */\n#ifndef FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR\n#if defined(__cpp_lib_constexpr_vector) && \\\n    __cpp_lib_constexpr_vector >= 201907L && !defined(_MSC_VER)\n#define FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR 1\n#else\n#define FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR 0\n#endif\n#endif\n\nnamespace folly {\nnamespace compression {\n\nstruct CompressionContextPoolDefaultCallback {\n  void operator()() const {}\n};\n\nconstexpr size_t COMPRESSION_CONTEXT_POOL_CALLBACK_INTERVAL = 1024;\n\n/**\n * This implementation is slow under contention. Except under uncontended\n * scenarios, you shouldn't use it directly. You likely want to use the\n * CompressionCoreLocalContextPool instead, which, behind the fast cache slots,\n * is backed by this implementation.\n */\ntemplate <\n    typename T,\n    typename Creator,\n    typename Deleter,\n    typename Resetter,\n    typename Sizeof,\n    typename Callback = CompressionContextPoolDefaultCallback>\nclass CompressionContextPool {\n private:\n  using InternalRef = std::unique_ptr<T, Deleter>;\n\n  class ReturnToPoolDeleter {\n   public:\n    using Pool =\n        CompressionContextPool<T, Creator, Deleter, Resetter, Sizeof, Callback>;\n\n    explicit ReturnToPoolDeleter(Pool* pool) : pool_(pool) { DCHECK(pool); }\n\n    void operator()(T* t) {\n      InternalRef ptr(t, pool_->deleter_);\n      pool_->add(std::move(ptr));\n    }\n\n   private:\n    Pool* pool_;\n  };\n\n public:\n  using Object = T;\n  using Ref = std::unique_ptr<T, ReturnToPoolDeleter>;\n\n  constexpr explicit CompressionContextPool(\n      Creator creator = Creator(),\n      Deleter deleter = Deleter(),\n      Resetter resetter = Resetter(),\n      Sizeof size_of = Sizeof(),\n      Callback callback = Callback())\n      : creator_(std::move(creator)),\n        deleter_(std::move(deleter)),\n        resetter_(std::move(resetter)),\n        size_of_(std::move(size_of)),\n        callback_(std::move(callback)),\n        state_(),\n        created_(0) {}\n\n  Ref get() {\n    bool do_cb = false;\n    Ref ref{nullptr, get_deleter()};\n    {\n      auto lock = state_.wlock();\n#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR\n      if (!lock->stack_) {\n        lock->stack_.emplace();\n      }\n#endif\n      auto& stack = *lock->stack_;\n      if (!stack.empty()) {\n        auto ptr = std::move(stack.back());\n        stack.pop_back();\n        do_cb = (lock->callback_counter_++ %\n                 COMPRESSION_CONTEXT_POOL_CALLBACK_INTERVAL) == 0;\n        if (!ptr) {\n          throw_exception<std::logic_error>(\n              \"A nullptr snuck into our context pool!?!?\");\n        }\n        ref = Ref(ptr.release(), get_deleter());\n      }\n    }\n    if (do_cb) {\n      callback_();\n    }\n    if (!ref) {\n      ref = create();\n    }\n    return ref;\n  }\n\n  size_t created_count() const { return created_.load(); }\n\n  size_t size() {\n    auto lock = state_.rlock();\n#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR\n    if (!lock->stack_) {\n      return 0;\n    }\n#endif\n    auto& stack = *lock->stack_;\n    return stack.size();\n  }\n\n  /// @returns the total bytes of memory used by the pool.\n  size_t bytes_used() const {\n    auto lock = state_.rlock();\n#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR\n    if (!lock->stack_) {\n      return 0;\n    }\n#endif\n    auto& stack = *lock->stack_;\n    size_t bytes = 0;\n    for (auto& ptr : stack) {\n      bytes += size_of_(ptr.get());\n    }\n    return bytes;\n  }\n\n  ReturnToPoolDeleter get_deleter() { return ReturnToPoolDeleter(this); }\n\n  const Resetter& get_resetter() { return resetter_; }\n\n  const Sizeof& get_sizeof() const { return size_of_; }\n\n  void flush_deep() {\n    flush_shallow();\n    // no backing stack, so deep == shallow\n  }\n\n  void flush_shallow() {\n    auto lock = state_.wlock();\n#if !FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR\n    if (!lock->stack_) {\n      return;\n    }\n#endif\n    auto& stack = *lock->stack_;\n    stack.resize(0);\n  }\n\n private:\n  void add(InternalRef ptr) {\n    DCHECK(ptr);\n    resetter_(ptr.get());\n    auto lock = state_.wlock();\n    // add() can only be called when we get a ref we created back, so the\n    // stack must already have been initialized. So we don't need to check.\n    auto& stack = *lock->stack_;\n    stack.push_back(std::move(ptr));\n  }\n\n  Ref create() {\n    T* t = creator_();\n    if (t == nullptr) {\n      throw_exception<std::bad_alloc>();\n    }\n    created_++;\n    return Ref(t, get_deleter());\n  }\n\n  const Creator creator_;\n  const Deleter deleter_;\n  const Resetter resetter_;\n  const Sizeof size_of_;\n  const Callback callback_;\n\n  struct SyncState {\n    explicit constexpr SyncState()\n        :\n#if FOLLY_COMPRESSION_HAS_CONSTEXPR_VECTOR\n          stack_(std::in_place, std::vector<InternalRef>{}),\n#else\n          stack_(),\n#endif\n          callback_counter_(0) {\n    }\n\n    std::optional<std::vector<InternalRef>> stack_;\n    size_t callback_counter_;\n  };\n\n  folly::Synchronized<SyncState> state_;\n\n  std::atomic<size_t> created_;\n};\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/CompressionContextPoolSingletons.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/CompressionContextPoolSingletons.h>\n\n#include <stdlib.h>\n\n#include <folly/Portability.h>\n#include <folly/memory/Malloc.h>\n#include <folly/system/HardwareConcurrency.h>\n\n#ifndef FOLLY_COMPRESSION_USE_HUGEPAGES\n#if defined(__linux__) && !defined(__ANDROID__)\n#define FOLLY_COMPRESSION_USE_HUGEPAGES 1\n#else\n#define FOLLY_COMPRESSION_USE_HUGEPAGES 0\n#endif\n#endif\n\n#if FOLLY_COMPRESSION_USE_HUGEPAGES\n#include <folly/memory/JemallocHugePageAllocator.h>\n#endif\n\n#if FOLLY_HAVE_LIBZSTD\n#ifndef ZSTD_STATIC_LINKING_ONLY\n#define ZSTD_STATIC_LINKING_ONLY\n#endif\n#include <zstd.h> // @donotremove\n#endif\n\n#if FOLLY_HAVE_WEAK_SYMBOLS\nFOLLY_ATTR_WEAK double\nfolly_zstd_cctx_pool_stripes_cpu_multiplier_default() noexcept;\nFOLLY_ATTR_WEAK double\nfolly_zstd_dctx_pool_stripes_cpu_multiplier_default() noexcept;\n#else\nstatic double (\n    *folly_zstd_cctx_pool_stripes_cpu_multiplier_default)() noexcept = nullptr;\nstatic double (\n    *folly_zstd_dctx_pool_stripes_cpu_multiplier_default)() noexcept = nullptr;\n#endif\n\nFOLLY_GFLAGS_DEFINE_double(\n    folly_zstd_cctx_pool_stripes_cpu_multiplier,\n    folly_zstd_cctx_pool_stripes_cpu_multiplier_default\n        ? folly_zstd_cctx_pool_stripes_cpu_multiplier_default()\n        : 0.0,\n    \"Number of stripes for compression context pool specified as a multiplier of hardware concurrency.\");\nFOLLY_GFLAGS_DEFINE_double(\n    folly_zstd_dctx_pool_stripes_cpu_multiplier,\n    folly_zstd_dctx_pool_stripes_cpu_multiplier_default\n        ? folly_zstd_dctx_pool_stripes_cpu_multiplier_default()\n        : 0.0,\n    \"Number of stripes for decompression context pool specified as a multiplier of hardware concurrency.\");\n\nnamespace folly {\nnamespace compression {\nnamespace contexts {\n\n#if FOLLY_HAVE_LIBZSTD\nnamespace {\n\n// These objects must be constinit in order to be SIOF-safe, since they are\n// accessed during the static initialization of other translation units.\nstruct ZSTD_cctx_pool_singleton : ZSTD_CCtx_Pool {\n  constexpr ZSTD_cctx_pool_singleton()\n      : ZSTD_CCtx_Pool{8, {}, {}, {}, {}, ZSTD_CCtx_Pool_Callback{this}} {}\n};\nstruct ZSTD_dctx_pool_singleton : ZSTD_DCtx_Pool {\n  constexpr ZSTD_dctx_pool_singleton()\n      : ZSTD_DCtx_Pool{8, {}, {}, {}, {}, ZSTD_DCtx_Pool_Callback{this}} {}\n};\n\nconstinit ZSTD_cctx_pool_singleton zstd_cctx_pool_singleton;\nconstinit ZSTD_dctx_pool_singleton zstd_dctx_pool_singleton;\n\n#if FOLLY_COMPRESSION_USE_HUGEPAGES\nconstexpr bool use_huge_pages = kIsArchAmd64;\n\nvoid* huge_page_alloc(void*, size_t size) {\n  if (size < 16 * 4096) {\n    // Arbritrary cutoff: ZSTD_CCtx'es only ever make two kinds of allocations:\n    // 1. one small one for the CCtx itself.\n    // 2. \"big\" ones for the workspace (ZSTD_cwksp)\n    // The CCtx allocation doesn't need to be in a huge page.\n    return malloc(size);\n  }\n  return JemallocHugePageAllocator::allocate(size);\n}\n\nvoid huge_page_free(void*, void* address) {\n  if (address != nullptr) {\n    if (JemallocHugePageAllocator::addressInArena(address)) {\n      JemallocHugePageAllocator::deallocate(address);\n    } else {\n      free(address);\n    }\n  }\n}\n\nconst ZSTD_customMem huge_page_custom_mem = (use_huge_pages && usingJEMalloc())\n    ? (ZSTD_customMem){huge_page_alloc, huge_page_free, nullptr}\n    : ZSTD_defaultCMem;\n#else\nconst ZSTD_customMem huge_page_custom_mem = ZSTD_defaultCMem;\n#endif\n\n} // anonymous namespace\n\nZSTD_CCtx* ZSTD_CCtx_Creator::operator()() const noexcept {\n  return ZSTD_createCCtx_advanced(huge_page_custom_mem);\n}\n\nZSTD_DCtx* ZSTD_DCtx_Creator::operator()() const noexcept {\n  return ZSTD_createDCtx_advanced(huge_page_custom_mem);\n}\n\nvoid ZSTD_CCtx_Deleter::operator()(ZSTD_CCtx* ctx) const noexcept {\n  ZSTD_freeCCtx(ctx);\n}\n\nvoid ZSTD_DCtx_Deleter::operator()(ZSTD_DCtx* ctx) const noexcept {\n  ZSTD_freeDCtx(ctx);\n}\n\nvoid ZSTD_CCtx_Resetter::operator()(ZSTD_CCtx* ctx) const noexcept {\n  size_t const err = ZSTD_CCtx_reset(ctx, ZSTD_reset_session_and_parameters);\n  assert(!ZSTD_isError(err)); // This function doesn't actually fail\n  (void)err;\n}\n\nvoid ZSTD_DCtx_Resetter::operator()(ZSTD_DCtx* ctx) const noexcept {\n  size_t const err = ZSTD_DCtx_reset(ctx, ZSTD_reset_session_and_parameters);\n  assert(!ZSTD_isError(err)); // This function doesn't actually fail\n  (void)err;\n}\n\nsize_t ZSTD_CCtx_Sizeof::operator()(const ZSTD_CCtx* ctx) const noexcept {\n  return ZSTD_sizeof_CCtx(ctx);\n}\n\nsize_t ZSTD_DCtx_Sizeof::operator()(const ZSTD_DCtx* ctx) const noexcept {\n  return ZSTD_sizeof_DCtx(ctx);\n}\n\nvoid ZSTD_CCtx_Pool_Callback::operator()() const {\n  if (pool_) {\n    if (auto multiplier = FLAGS_folly_zstd_cctx_pool_stripes_cpu_multiplier;\n        multiplier > 0.0) {\n      static const size_t num_cores = folly::available_concurrency();\n      const size_t num_stripes = std::min(\n          static_cast<size_t>(std::ceil(multiplier * num_cores)),\n          ZSTD_CCtx_Pool::kMaxNumStripes);\n      pool_->setSize(num_stripes);\n    }\n  }\n}\n\nvoid ZSTD_DCtx_Pool_Callback::operator()() const {\n  if (pool_) {\n    if (auto multiplier = FLAGS_folly_zstd_dctx_pool_stripes_cpu_multiplier;\n        multiplier > 0.0) {\n      static const size_t num_cores = folly::available_concurrency();\n      const size_t num_stripes = std::min(\n          static_cast<size_t>(std::ceil(multiplier * num_cores)),\n          ZSTD_DCtx_Pool::kMaxNumStripes);\n      pool_->setSize(num_stripes);\n    }\n  }\n}\n\nZSTD_CCtx_Pool::Ref getZSTD_CCtx() {\n  return zstd_cctx_pool_singleton.get();\n}\n\nZSTD_DCtx_Pool::Ref getZSTD_DCtx() {\n  return zstd_dctx_pool_singleton.get();\n}\n\nZSTD_CCtx_Pool::Ref getNULL_ZSTD_CCtx() {\n  return zstd_cctx_pool_singleton.getNull();\n}\n\nZSTD_DCtx_Pool::Ref getNULL_ZSTD_DCtx() {\n  return zstd_dctx_pool_singleton.getNull();\n}\n\nZSTD_CCtx_Pool& zstd_cctx_pool() {\n  return zstd_cctx_pool_singleton;\n}\n\nZSTD_DCtx_Pool& zstd_dctx_pool() {\n  return zstd_dctx_pool_singleton;\n}\n\nsize_t get_zstd_cctx_created_count() {\n  return zstd_cctx_pool_singleton.created_count();\n}\n\nsize_t get_zstd_dctx_created_count() {\n  return zstd_dctx_pool_singleton.created_count();\n}\n\n#endif // FOLLY_HAVE_LIBZSTD\n\n} // namespace contexts\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/CompressionContextPoolSingletons.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/portability/Config.h>\n\n#if FOLLY_HAVE_LIBZSTD\n#include <zstd.h>\n#endif\n\n#include <folly/compression/CompressionCoreLocalContextPool.h>\n#include <folly/portability/GFlags.h>\n\n// When this header is present, folly/compression/Compression.h defines\n// FOLLY_COMPRESSION_HAS_CONTEXT_POOL_SINGLETONS.\n\n// These flags allow for tuning the number of stripes in the singleton\n// context pool. This is expressed as a multiplier of CPU hardware\n// concurrency, e.g. a setting of 0.1 on a machine with 50 cores results\n// in 5 stripes in the context pool singleton.\nFOLLY_GFLAGS_DECLARE_double(folly_zstd_cctx_pool_stripes_cpu_multiplier);\nFOLLY_GFLAGS_DECLARE_double(folly_zstd_dctx_pool_stripes_cpu_multiplier);\n\nnamespace folly {\nnamespace compression {\nnamespace contexts {\n\n#if FOLLY_HAVE_LIBZSTD\n\n// Additional feature test macro for zstd singletons.\n#define FOLLY_COMPRESSION_HAS_ZSTD_CONTEXT_POOL_SINGLETONS\n\nstruct ZSTD_CCtx_Creator {\n  ZSTD_CCtx* operator()() const noexcept;\n};\n\nstruct ZSTD_DCtx_Creator {\n  ZSTD_DCtx* operator()() const noexcept;\n};\n\nstruct ZSTD_CCtx_Deleter {\n  void operator()(ZSTD_CCtx* ctx) const noexcept;\n};\n\nstruct ZSTD_DCtx_Deleter {\n  void operator()(ZSTD_DCtx* ctx) const noexcept;\n};\n\nstruct ZSTD_CCtx_Resetter {\n  void operator()(ZSTD_CCtx* ctx) const noexcept;\n};\n\nstruct ZSTD_DCtx_Resetter {\n  void operator()(ZSTD_DCtx* ctx) const noexcept;\n};\n\nstruct ZSTD_CCtx_Sizeof {\n  size_t operator()(const ZSTD_CCtx* ctx) const noexcept;\n};\n\nstruct ZSTD_DCtx_Sizeof {\n  size_t operator()(const ZSTD_DCtx* ctx) const noexcept;\n};\n\nclass ZSTD_CCtx_Pool_Callback {\n public:\n  explicit constexpr ZSTD_CCtx_Pool_Callback(\n      CompressionCoreLocalContextPoolBase* pool)\n      : pool_(pool) {}\n\n  void operator()() const;\n\n private:\n  CompressionCoreLocalContextPoolBase* pool_;\n};\n\nstruct ZSTD_DCtx_Pool_Callback {\n public:\n  explicit constexpr ZSTD_DCtx_Pool_Callback(\n      CompressionCoreLocalContextPoolBase* pool)\n      : pool_(pool) {}\n\n  void operator()() const;\n\n private:\n  CompressionCoreLocalContextPoolBase* pool_;\n};\n\nusing ZSTD_CCtx_Pool = CompressionCoreLocalContextPool<\n    ZSTD_CCtx,\n    ZSTD_CCtx_Creator,\n    ZSTD_CCtx_Deleter,\n    ZSTD_CCtx_Resetter,\n    ZSTD_CCtx_Sizeof,\n    ZSTD_CCtx_Pool_Callback>;\nusing ZSTD_DCtx_Pool = CompressionCoreLocalContextPool<\n    ZSTD_DCtx,\n    ZSTD_DCtx_Creator,\n    ZSTD_DCtx_Deleter,\n    ZSTD_DCtx_Resetter,\n    ZSTD_DCtx_Sizeof,\n    ZSTD_DCtx_Pool_Callback>;\n\n/**\n * Returns a clean ZSTD_CCtx.\n */\nZSTD_CCtx_Pool::Ref getZSTD_CCtx();\n\n/**\n * Returns a clean ZSTD_DCtx.\n */\nZSTD_DCtx_Pool::Ref getZSTD_DCtx();\n\nZSTD_CCtx_Pool::Ref getNULL_ZSTD_CCtx();\n\nZSTD_DCtx_Pool::Ref getNULL_ZSTD_DCtx();\n\nZSTD_CCtx_Pool& zstd_cctx_pool();\n\nZSTD_DCtx_Pool& zstd_dctx_pool();\n\nsize_t get_zstd_cctx_created_count();\n\nsize_t get_zstd_dctx_created_count();\n\n#endif // FOLLY_HAVE_LIBZSTD\n\n} // namespace contexts\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/CompressionCoreLocalContextPool.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/compression/CompressionContextPool.h>\n#include <folly/concurrency/CacheLocality.h>\n\nnamespace folly {\nnamespace compression {\n\n/**\n * Non-templated base class which allows for generic interaction with context\n * pool instances.\n */\nclass CompressionCoreLocalContextPoolBase {\n public:\n  virtual ~CompressionCoreLocalContextPoolBase() = default;\n\n  virtual void setSize(size_t size) = 0;\n};\n\n/**\n * This class is intended to reduce contention on reserving a compression\n * context and improve cache locality (but maybe not hotness) of the contexts\n * it manages.\n *\n * This class uses folly::AccessSpreader to spread the managed object across\n * NumStripes domains (which should correspond to a topologically close set of\n * hardware threads). This cache is still backed by the basic locked stack in\n * the folly::compression::CompressionContextPool.\n *\n * Note that there is a tradeoff in choosing the number of stripes. More stripes\n * make for less contention, but mean that a context is less likely to be hot\n * in cache.\n */\ntemplate <\n    typename T,\n    typename Creator,\n    typename Deleter,\n    typename Resetter,\n    typename Sizeof,\n    typename Callback = CompressionContextPoolDefaultCallback>\nclass CompressionCoreLocalContextPool\n    : public CompressionCoreLocalContextPoolBase {\n private:\n  /**\n   * Force each pointer to be on a different cache line.\n   */\n  class alignas(folly::hardware_destructive_interference_size) Storage {\n   public:\n    std::atomic<T*> ptr{nullptr};\n  };\n\n  class ReturnToPoolDeleter {\n   public:\n    using Pool = CompressionCoreLocalContextPool<\n        T,\n        Creator,\n        Deleter,\n        Resetter,\n        Sizeof,\n        Callback>;\n\n    explicit ReturnToPoolDeleter(Pool* pool) : pool_(pool) { DCHECK(pool_); }\n\n    void operator()(T* ptr) { pool_->store(ptr); }\n\n   private:\n    Pool* pool_;\n  };\n\n  using BackingPool =\n      CompressionContextPool<T, Creator, Deleter, Resetter, Sizeof, Callback>;\n  using BackingPoolRef = typename BackingPool::Ref;\n\n public:\n  /**\n   * The max size is derived from maximum stripes for folly::AccessSpreader.\n   */\n  static constexpr size_t kMaxNumStripes =\n      folly::detail::AccessSpreaderBase::kMaxCpus;\n\n  using Object = T;\n  using Ref = std::unique_ptr<T, ReturnToPoolDeleter>;\n\n  constexpr explicit CompressionCoreLocalContextPool(\n      size_t numStripes = 8,\n      Creator creator = Creator(),\n      Deleter deleter = Deleter(),\n      Resetter resetter = Resetter(),\n      Sizeof size_of = Sizeof(),\n      Callback callback = Callback())\n      : numStripes_(numStripes),\n        pool_(\n            std::move(creator),\n            std::move(deleter),\n            std::move(resetter),\n            std::move(size_of),\n            std::move(callback)),\n        caches_() {}\n\n  ~CompressionCoreLocalContextPool() override { flush_shallow(); }\n\n  Ref get() {\n    auto ptr = local().ptr.exchange(nullptr);\n    if (ptr == nullptr) {\n      // no local ctx, get from backing pool\n      ptr = pool_.get().release();\n      DCHECK(ptr);\n    }\n    return Ref(ptr, get_deleter());\n  }\n\n  Ref getNull() { return Ref(nullptr, get_deleter()); }\n\n  /**\n   * Update the number of stripes. This will flush the pool if the stripe count\n   * changes.\n   */\n  void setSize(size_t numStripes) override {\n    if (numStripes == 0) {\n      throw_exception<std::invalid_argument>(\n          \"CompressionCoreLocalContextPool must have at least 1 stripe\");\n    }\n    if (numStripes > kMaxNumStripes) {\n      DCHECK(false);\n      numStripes = kMaxNumStripes;\n    }\n\n    auto before = numStripes_.exchange(numStripes);\n    if (before != numStripes) {\n      flush_shallow();\n    }\n  }\n\n  size_t cacheSize() const { return numStripes_.load(); }\n\n  size_t created_count() const { return pool_.created_count(); }\n\n  void flush_deep() {\n    flush_shallow();\n    pool_.flush_deep();\n  }\n\n  void flush_shallow() {\n    for (auto& cache : caches_) {\n      // Return all cached contexts back to the backing pool.\n      auto ptr = cache.ptr.exchange(nullptr);\n      return_to_backing_pool(ptr);\n    }\n  }\n\n  size_t bytes_used() const {\n    size_t bytes = pool_.bytes_used();\n    for (const auto& cache : caches_) {\n      if (auto ptr = cache.ptr.load()) {\n        bytes += pool_.get_sizeof()(ptr);\n      }\n    }\n    return bytes;\n  }\n\n private:\n  ReturnToPoolDeleter get_deleter() { return ReturnToPoolDeleter(this); }\n\n  void store(T* ptr) {\n    DCHECK(ptr);\n    pool_.get_resetter()(ptr);\n    auto other = local().ptr.exchange(ptr);\n    if (other != nullptr) {\n      return_to_backing_pool(other);\n    }\n  }\n\n  void return_to_backing_pool(T* ptr) {\n    BackingPoolRef(ptr, pool_.get_deleter());\n  }\n\n  Storage& local() {\n    // Note that cachedCurrent(0) is valid, so this should be SIOF safe.\n    const auto idx = folly::AccessSpreader<>::cachedCurrent(numStripes_);\n    return caches_[idx];\n  }\n\n  relaxed_atomic<size_t> numStripes_;\n  BackingPool pool_;\n\n  /**\n   * context_pool_max_num_stripes number of stripes are allocated to the\n   * underlying stripes array. However, only the lower numStripes_ indices are\n   * used to actually stripe the pool. This allows us to statically create\n   * singletons while providing flexibility around how many stripes there are.\n   */\n  std::array<Storage, kMaxNumStripes> caches_;\n};\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/Instructions.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <glog/logging.h>\n\n#ifdef _MSC_VER\n#include <immintrin.h>\n#endif\n\n#include <string_view>\n\n#include <folly/CpuId.h>\n#include <folly/Portability.h>\n#include <folly/lang/Assume.h>\n#include <folly/portability/Builtins.h>\n\nnamespace folly {\nnamespace compression {\nnamespace instructions {\n\n// NOTE: It's recommended to compile EF coding with -msse4.2, starting\n// with Nehalem, Intel CPUs support POPCNT instruction and gcc will emit\n// it for __builtin_popcountll intrinsic.\n// But we provide an alternative way for the client code: it can switch to\n// the appropriate version of EliasFanoReader<> at runtime (client should\n// implement this switching logic itself) by specifying instruction set to\n// use explicitly.\n\nstruct Default {\n  static std::string_view name() noexcept { return \"Default\"; }\n  static bool supported(const folly::CpuId& /* cpuId */ = {}) { return true; }\n  static FOLLY_ALWAYS_INLINE uint64_t popcount(uint64_t value) {\n    return uint64_t(__builtin_popcountll(value));\n  }\n  static FOLLY_ALWAYS_INLINE int ctz(uint64_t value) {\n    DCHECK_GT(value, 0u);\n    return __builtin_ctzll(value);\n  }\n  static FOLLY_ALWAYS_INLINE int clz(uint64_t value) {\n    DCHECK_GT(value, 0u);\n    return __builtin_clzll(value);\n  }\n  static FOLLY_ALWAYS_INLINE uint64_t blsr(uint64_t value) {\n    return value & (value - 1);\n  }\n\n  // Extract `length` bits starting from `start` from value. Only bits [0:63]\n  // will be extracted. All higher order bits in the\n  // result will be zeroed. If no bits are extracted, return 0.\n  static FOLLY_ALWAYS_INLINE uint64_t\n  bextr(uint64_t value, uint32_t start, uint32_t length) {\n    if (start > 63) {\n      return 0ULL;\n    }\n    if (start + length > 64) {\n      length = 64 - start;\n    }\n\n    return (value >> start) &\n        ((length == 64) ? (~0ULL) : ((1ULL << length) - 1ULL));\n  }\n\n  // Clear high bits starting at position index.\n  static FOLLY_ALWAYS_INLINE uint64_t bzhi(uint64_t value, uint32_t index) {\n    if (index > 63) {\n      return 0;\n    }\n    return value & ((uint64_t(1) << index) - 1);\n  }\n};\n\n#if FOLLY_X64 || defined(__i386__)\nstruct Nehalem : public Default {\n  static std::string_view name() noexcept { return \"Nehalem\"; }\n\n  static bool supported(const folly::CpuId& cpuId = {}) {\n    return cpuId.popcnt();\n  }\n\n  static FOLLY_ALWAYS_INLINE uint64_t popcount(uint64_t value) {\n// POPCNT is supported starting with Intel Nehalem, AMD K10.\n#if defined(__GNUC__)\n    // GCC and Clang won't inline the intrinsics.\n    uint64_t result;\n    asm(\"popcntq %1, %0\" : \"=r\"(result) : \"r\"(value));\n    return result;\n#else\n    return uint64_t(_mm_popcnt_u64(value));\n#endif\n  }\n};\n\nstruct Haswell : public Nehalem {\n  static std::string_view name() noexcept { return \"Haswell\"; }\n\n  static bool supported(const folly::CpuId& cpuId = {}) {\n    return Nehalem::supported(cpuId) && cpuId.bmi1() && cpuId.bmi2();\n  }\n\n  static FOLLY_ALWAYS_INLINE uint64_t blsr(uint64_t value) {\n// BMI1 is supported starting with Intel Haswell, AMD Piledriver.\n// BLSR combines two instructions into one and reduces register pressure.\n#if defined(__GNUC__)\n    // GCC and Clang won't inline the intrinsics.\n    uint64_t result;\n    asm(\"blsrq %1, %0\" : \"=r\"(result) : \"r\"(value));\n    return result;\n#else\n    return _blsr_u64(value);\n#endif\n  }\n\n  static FOLLY_ALWAYS_INLINE uint64_t\n  bextr(uint64_t value, uint32_t start, uint32_t length) {\n#if defined(__GNUC__)\n    // GCC and Clang won't inline the intrinsics.\n    // Encode parameters in `pattern` where `pattern[0:7]` is `start` and\n    // `pattern[8:15]` is `length`.\n    // Ref: Intel Advanced Vector Extensions Programming Reference\n    uint64_t pattern = start & 0xFF;\n    pattern = pattern | ((length & 0xFF) << 8);\n    uint64_t result;\n    asm(\"bextrq %2, %1, %0\" : \"=r\"(result) : \"r\"(value), \"r\"(pattern));\n    return result;\n#else\n    return _bextr_u64(value, start, length);\n#endif\n  }\n\n  static FOLLY_ALWAYS_INLINE uint64_t bzhi(uint64_t value, uint32_t index) {\n#if defined(__GNUC__)\n    // GCC and Clang won't inline the intrinsics.\n    const uint64_t index64 = index;\n    uint64_t result;\n    asm(\"bzhiq %2, %1, %0\" : \"=r\"(result) : \"r\"(value), \"r\"(index64));\n    return result;\n#else\n    return _bzhi_u64(value, index);\n#endif\n  }\n};\n#endif\n\nenum class Type {\n  DEFAULT,\n  NEHALEM,\n  HASWELL,\n};\n\ninline Type detect() {\n  const static Type type = [] {\n#if FOLLY_X64 || defined(__i386)\n    if (instructions::Haswell::supported()) {\n      VLOG(2) << \"Will use folly::compression::instructions::Haswell\";\n      return Type::HASWELL;\n    } else if (instructions::Nehalem::supported()) {\n      VLOG(2) << \"Will use folly::compression::instructions::Nehalem\";\n      return Type::NEHALEM;\n    } else {\n      VLOG(2) << \"Will use folly::compression::instructions::Default\";\n      return Type::DEFAULT;\n    }\n#else\n    return Type::DEFAULT;\n#endif\n  }();\n  return type;\n}\n\ntemplate <class F>\nauto dispatch(Type type, F&& f) -> decltype(f(std::declval<Default>())) {\n#if FOLLY_X64 || defined(__i386)\n  switch (type) {\n    case Type::HASWELL:\n      return f(Haswell());\n    case Type::NEHALEM:\n      return f(Nehalem());\n    case Type::DEFAULT:\n      return f(Default());\n  }\n#else\n  (void)type;\n  return f(Default());\n#endif\n\n  assume_unreachable();\n}\n\ntemplate <class F>\nauto dispatch(F&& f) -> decltype(f(std::declval<Default>())) {\n  return dispatch(detect(), std::forward<F>(f));\n}\n\n} // namespace instructions\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/QuotientMultiSet-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/QuotientMultiSet.h>\n\n#include <folly/Format.h>\n#include <folly/Portability.h>\n#include <folly/compression/Select64.h>\n#include <folly/lang/Bits.h>\n#include <folly/lang/BitsClass.h>\n#include <folly/lang/SafeAssert.h>\n\n#include <glog/logging.h>\n\n#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n\nnamespace folly {\n\nnamespace qms_detail {\n\n/**\n * Reference: Faster Remainder by Direct Computation: Applications to Compilers\n * and Software Libraries, Software: Practice and Experience 49 (6), 2019.\n */\nFOLLY_ALWAYS_INLINE UInt64InverseType getInverse(uint64_t divisor) {\n  UInt64InverseType fraction = UInt64InverseType(-1);\n  fraction /= divisor;\n  fraction += 1;\n  return fraction;\n}\n\nFOLLY_ALWAYS_INLINE uint64_t\nmul128(UInt64InverseType lowbits, uint64_t divisor) {\n  UInt64InverseType bottomHalf =\n      (lowbits & UINT64_C(0xFFFFFFFFFFFFFFFF)) * divisor;\n  bottomHalf >>= 64;\n  UInt64InverseType topHalf = (lowbits >> 64) * divisor;\n  UInt64InverseType bothHalves = bottomHalf + topHalf;\n  bothHalves >>= 64;\n  return static_cast<uint64_t>(bothHalves);\n}\n\nFOLLY_ALWAYS_INLINE std::pair<uint64_t, uint64_t> getQuotientAndRemainder(\n    uint64_t dividend, uint64_t divisor, UInt64InverseType inverse) {\n  if (FOLLY_UNLIKELY(divisor == 1)) {\n    return {dividend, 0};\n  }\n  auto quotient = mul128(inverse, dividend);\n  auto remainder = dividend - quotient * divisor;\n  DCHECK_LT(remainder, divisor);\n  return {quotient, remainder};\n}\n\n// Max value for given bits.\nFOLLY_ALWAYS_INLINE uint64_t maxValue(uint32_t nbits) {\n  return nbits == 64\n      ? std::numeric_limits<uint64_t>::max()\n      : (uint64_t(1) << nbits) - 1;\n}\n\n} // namespace qms_detail\n\ntemplate <class Instructions>\nstruct QuotientMultiSet<Instructions>::Metadata {\n  // Total number of blocks.\n  uint64_t numBlocks;\n  uint64_t numKeys;\n  uint64_t divisor;\n  uint8_t keyBits;\n  uint8_t remainderBits;\n\n  std::string debugString() const {\n    return fmt::format(\n        \"Number of blocks: {}\\n\"\n        \"Number of elements: {}\\n\"\n        \"Divisor: {}\\n\"\n        \"Key bits: {}\\n\"\n        \"Remainder bits: {}\",\n        numBlocks,\n        numKeys,\n        divisor,\n        keyBits,\n        remainderBits);\n  }\n} FOLLY_PACK_ATTR;\n\ntemplate <class Instructions>\nstruct QuotientMultiSet<Instructions>::Block {\n  static const Block* get(const char* data) {\n    return reinterpret_cast<const Block*>(data);\n  }\n\n  static BlockPtr make(size_t remainderBits) {\n    auto ptr = reinterpret_cast<Block*>(calloc(blockSize(remainderBits), 1));\n    return {ptr, free};\n  }\n\n  uint64_t payload;\n  uint64_t occupieds;\n  uint64_t offset;\n  uint64_t runends;\n  char remainders[0];\n\n  static uint64_t blockSize(size_t remainderBits) {\n    return sizeof(Block) + remainderBits * 8;\n  }\n\n  FOLLY_ALWAYS_INLINE bool isOccupied(size_t offsetInBlock) const {\n    return ((occupieds >> offsetInBlock) & uint64_t(1)) != 0;\n  }\n\n  FOLLY_ALWAYS_INLINE bool isRunend(size_t offsetInBlock) const {\n    return ((runends >> offsetInBlock) & uint64_t(1)) != 0;\n  }\n\n  FOLLY_ALWAYS_INLINE uint64_t getRemainder(\n      size_t offsetInBlock, size_t remainderBits, size_t remainderMask) const {\n    DCHECK_LE(remainderBits, 56);\n    const size_t bitPos = offsetInBlock * remainderBits;\n    const uint64_t remainderWord =\n        loadUnaligned<uint64_t>(remainders + (bitPos / 8));\n    return (remainderWord >> (bitPos % 8)) & remainderMask;\n  }\n\n  void setOccupied(size_t offsetInBlock) {\n    occupieds |= uint64_t(1) << offsetInBlock;\n  }\n\n  void setRunend(size_t offsetInBlock) {\n    runends |= uint64_t(1) << offsetInBlock;\n  }\n\n  void setRemainder(\n      size_t offsetInBlock, size_t remainderBits, uint64_t remainder) {\n    DCHECK_LT(offsetInBlock, kBlockSize);\n    if (FOLLY_UNLIKELY(remainderBits == 0)) {\n      return;\n    }\n    Bits<uint64_t>::set(\n        reinterpret_cast<uint64_t*>(remainders),\n        offsetInBlock * remainderBits,\n        remainderBits,\n        remainder);\n  }\n} FOLLY_PACK_ATTR;\n\ntemplate <class Instructions>\nQuotientMultiSet<Instructions>::QuotientMultiSet(StringPiece data) {\n  static_assert(\n      kIsLittleEndian, \"QuotientMultiSet requires little endianness.\");\n  StringPiece sp = data;\n  CHECK_GE(sp.size(), sizeof(Metadata));\n  sp.advance(sp.size() - sizeof(Metadata));\n  metadata_ = reinterpret_cast<const Metadata*>(sp.data());\n  VLOG(2) << \"Metadata: \" << metadata_->debugString();\n\n  numBlocks_ = metadata_->numBlocks;\n  numSlots_ = numBlocks_ * kBlockSize;\n  divisor_ = metadata_->divisor;\n  fraction_ = qms_detail::getInverse(divisor_);\n  keyBits_ = metadata_->keyBits;\n  maxKey_ = qms_detail::maxValue(keyBits_);\n  remainderBits_ = metadata_->remainderBits;\n  remainderMask_ = qms_detail::maxValue(remainderBits_);\n  blockSize_ = Block::blockSize(remainderBits_);\n\n  CHECK_EQ(data.size(), numBlocks_ * blockSize_ + sizeof(Metadata));\n  data_ = data.data();\n}\n\ntemplate <class Instructions>\nauto QuotientMultiSet<Instructions>::equalRange(uint64_t key) const\n    -> SlotRange {\n  if (key > maxKey_) {\n    return {0, 0};\n  }\n  const auto qr = qms_detail::getQuotientAndRemainder(key, divisor_, fraction_);\n  const auto& quotient = qr.first;\n  const auto& remainder = qr.second;\n  const size_t blockIndex = quotient / kBlockSize;\n  const size_t offsetInBlock = quotient % kBlockSize;\n\n  if (FOLLY_UNLIKELY(blockIndex >= numBlocks_)) {\n    return {0, 0};\n  }\n\n  const auto* occBlock = getBlock(blockIndex);\n  __builtin_prefetch(reinterpret_cast<const char*>(&occBlock->occupieds) + 64);\n  const auto firstRunend = occBlock->offset;\n  if (!occBlock->isOccupied(offsetInBlock)) {\n    // Return a position that depends on the contents of the block so\n    // we can create a dependency in benchmarks.\n    return {firstRunend, firstRunend};\n  }\n\n  // Look for the right runend for the given key.\n  const uint64_t occupiedRank = Instructions::popcount(\n      Instructions::bzhi(occBlock->occupieds, offsetInBlock));\n  auto runend = findRunend(occupiedRank, firstRunend);\n  auto& slot = runend.first;\n  auto& block = runend.second;\n\n  // Iterates over the run backwards to find the slots whose remainder\n  // matches the key.\n  SlotRange range = {slot + 1, slot + 1};\n  while (true) {\n    uint64_t slotRemainder =\n        block->getRemainder(slot % kBlockSize, remainderBits_, remainderMask_);\n\n    if (slotRemainder > remainder) {\n      range.begin = slot;\n      range.end = slot;\n    } else if (slotRemainder == remainder) {\n      range.begin = slot;\n    } else {\n      break;\n    }\n\n    if (FOLLY_UNLIKELY(slot % kBlockSize == 0)) {\n      // Reached block start and the run starts from a prev block.\n      size_t slotBlockIndex = slot / kBlockSize;\n      if (slotBlockIndex > blockIndex) {\n        block = getBlock(slotBlockIndex - 1);\n      } else {\n        break;\n      }\n    }\n\n    --slot;\n    // Encounters the previous run.\n    if (block->isRunend(slot % kBlockSize)) {\n      break;\n    }\n  }\n\n  return range;\n}\n\ntemplate <class Instructions>\nauto QuotientMultiSet<Instructions>::findRunend(\n    uint64_t occupiedRank, uint64_t firstRunend) const\n    -> std::pair<uint64_t, const Block*> {\n  // Look for the right runend.\n  size_t slotBlockIndex = firstRunend / kBlockSize;\n  auto block = getBlock(slotBlockIndex);\n  uint64_t runendWord =\n      block->runends & (uint64_t(-1) << (firstRunend % kBlockSize));\n\n  while (true) {\n    DCHECK_LE(slotBlockIndex, numBlocks_);\n\n    const size_t numRuns = Instructions::popcount(runendWord);\n    if (FOLLY_LIKELY(numRuns > occupiedRank)) {\n      break;\n    }\n    occupiedRank -= numRuns;\n    ++slotBlockIndex;\n    block = getBlock(slotBlockIndex);\n    runendWord = block->runends;\n  }\n\n  return {\n      slotBlockIndex * kBlockSize +\n          select64<Instructions>(runendWord, occupiedRank),\n      block};\n}\n\ntemplate <class Instructions>\nuint64_t QuotientMultiSet<Instructions>::getBlockPayload(\n    uint64_t blockIndex) const {\n  DCHECK_LT(blockIndex, numBlocks_);\n  return getBlock(blockIndex)->payload;\n}\n\ntemplate <class Instructions>\nQuotientMultiSet<Instructions>::Iterator::Iterator(\n    const QuotientMultiSet<Instructions>* qms)\n    : qms_(qms) {}\n\ntemplate <class Instructions>\nbool QuotientMultiSet<Instructions>::Iterator::next() {\n  if (pos_ == size_t(-1) ||\n      qms_->getBlock(pos_ / kBlockSize)->isRunend(pos_ % kBlockSize)) {\n    // Move to start of next run.\n    if (!nextOccupied()) {\n      return setEnd();\n    }\n\n    // Next run either starts at pos + 1 or the start of block\n    // specified by occupied slot.\n    pos_ = std::max<uint64_t>(pos_ + 1, occBlockIndex_ * kBlockSize);\n  } else {\n    // Move to next slot since a run must be contiguous.\n    pos_++;\n  }\n\n  const Block* block = qms_->getBlock(pos_ / kBlockSize);\n  uint64_t quotient =\n      (occBlockIndex_ * kBlockSize + occOffsetInBlock_) * qms_->divisor_;\n  key_ = quotient +\n      block->getRemainder(\n          pos_ % kBlockSize, qms_->remainderBits_, qms_->remainderMask_);\n\n  DCHECK_LT(pos_, qms_->numBlocks_ * kBlockSize);\n  return true;\n}\n\ntemplate <class Instructions>\nbool QuotientMultiSet<Instructions>::Iterator::skipTo(uint64_t key) {\n  if (key > qms_->maxKey_) {\n    return setEnd();\n  }\n  const auto qr =\n      qms_detail::getQuotientAndRemainder(key, qms_->divisor_, qms_->fraction_);\n  const auto& quotient = qr.first;\n  occBlockIndex_ = quotient / kBlockSize;\n  occOffsetInBlock_ = quotient % kBlockSize;\n\n  if (FOLLY_UNLIKELY(occBlockIndex_ >= qms_->numBlocks_)) {\n    return setEnd();\n  }\n\n  occBlock_ = qms_->getBlock(occBlockIndex_);\n  occWord_ = occBlock_->occupieds & (uint64_t(-1) << occOffsetInBlock_);\n  if (!nextOccupied()) {\n    return setEnd();\n  }\n\n  // Search for the next runend.\n  uint64_t occupiedRank = Instructions::popcount(\n      Instructions::bzhi(occBlock_->occupieds, occOffsetInBlock_));\n  auto runend = qms_->findRunend(occupiedRank, occBlock_->offset);\n  auto& slot = runend.first;\n  auto& block = runend.second;\n  uint64_t slotBlockIndex = slot / kBlockSize;\n  uint64_t slotOffsetInBlock = slot % kBlockSize;\n  pos_ = slot;\n\n  uint64_t nextQuotient =\n      (occBlockIndex_ * kBlockSize + occOffsetInBlock_) * qms_->divisor_;\n  uint64_t nextRemainder = block->getRemainder(\n      slotOffsetInBlock, qms_->remainderBits_, qms_->remainderMask_);\n\n  if (nextQuotient + nextRemainder < key) {\n    // Lower bound element is at the start of next run.\n    return next();\n  }\n\n  // Iterate over the run backwards to find the first key that is larger than\n  // or equal to the given key.\n  while (true) {\n    uint64_t slotRemainder = block->getRemainder(\n        slotOffsetInBlock, qms_->remainderBits_, qms_->remainderMask_);\n    if (nextQuotient + slotRemainder < key) {\n      break;\n    }\n    pos_ = slot;\n    nextRemainder = slotRemainder;\n    if (FOLLY_UNLIKELY(slotOffsetInBlock == 0)) {\n      // Reached block start and the run starts from a prev block.\n      if (slotBlockIndex > occBlockIndex_) {\n        --slotBlockIndex;\n        block = qms_->getBlock(slotBlockIndex);\n        slotOffsetInBlock = kBlockSize;\n      } else {\n        break;\n      }\n    }\n    --slot;\n    --slotOffsetInBlock;\n\n    // Encounters the previous run.\n    if (block->isRunend(slotOffsetInBlock)) {\n      break;\n    }\n  }\n\n  key_ = nextQuotient + nextRemainder;\n  return true;\n}\n\ntemplate <class Instructions>\nbool QuotientMultiSet<Instructions>::Iterator::nextOccupied() {\n  while (FOLLY_UNLIKELY(occWord_ == 0)) {\n    if (FOLLY_UNLIKELY(++occBlockIndex_ >= qms_->numBlocks_)) {\n      return false;\n    }\n    occBlock_ = qms_->getBlock(occBlockIndex_);\n    occWord_ = occBlock_->occupieds;\n  }\n\n  occOffsetInBlock_ = Instructions::ctz(occWord_);\n  occWord_ = Instructions::blsr(occWord_);\n  return true;\n}\n\n} // namespace folly\n\n#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n"
  },
  {
    "path": "folly/compression/QuotientMultiSet.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/QuotientMultiSet.h>\n\n#include <cmath>\n\n#include <folly/Math.h>\n\n#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n\nnamespace folly {\n\nQuotientMultiSetBuilder::QuotientMultiSetBuilder(\n    size_t keyBits, size_t expectedElements, double loadFactor)\n    : keyBits_(keyBits), maxKey_(qms_detail::maxValue(keyBits_)) {\n  expectedElements = std::max<size_t>(expectedElements, 1);\n  uint64_t numSlots = to_integral(ceil(expectedElements / loadFactor));\n\n  // Make sure 1:1 mapping between key space and <divisor, remainder> pairs.\n  divisor_ = divCeil(maxKey_, numSlots);\n  remainderBits_ = findLastSet(divisor_ - 1);\n\n  // We only support remainders as long as 56 bits. If the set is very\n  // sparse, force the maximum allowed remainder size. This will waste\n  // up to 3 extra blocks (because of 8-bit quotients) but be correct.\n  if (remainderBits_ > 56) {\n    remainderBits_ = 56;\n    divisor_ = uint64_t(1) << remainderBits_;\n  }\n\n  blockSize_ = Block::blockSize(remainderBits_);\n  fraction_ = qms_detail::getInverse(divisor_);\n}\n\nQuotientMultiSetBuilder::~QuotientMultiSetBuilder() = default;\n\nbool QuotientMultiSetBuilder::maybeAllocateBlocks(size_t limitIndex) {\n  bool blockAllocated = false;\n  for (; numBlocks_ <= limitIndex; numBlocks_++) {\n    auto block = Block::make(remainderBits_);\n    blocks_.emplace_back(std::move(block), numBlocks_);\n    blockAllocated = true;\n  }\n  return blockAllocated;\n}\n\nbool QuotientMultiSetBuilder::insert(uint64_t key) {\n  FOLLY_SAFE_CHECK(key <= maxKey_, \"Invalid key\");\n  FOLLY_SAFE_CHECK(\n      key >= prevKey_, \"Keys need to be inserted in nondecreasing order\");\n  const auto qr = qms_detail::getQuotientAndRemainder(key, divisor_, fraction_);\n  const auto& quotient = qr.first;\n  const auto& remainder = qr.second;\n  const size_t blockIndex = quotient / kBlockSize;\n  const size_t offsetInBlock = quotient % kBlockSize;\n\n  bool newBlockAllocated = false;\n  // Allocate block for the given key if necessary.\n  newBlockAllocated |= maybeAllocateBlocks(\n      std::max<uint64_t>(blockIndex, nextSlot_ / kBlockSize));\n  auto block = getBlock(nextSlot_ / kBlockSize).block.get();\n\n  // Start a new run.\n  if (prevOccupiedQuotient_ != quotient) {\n    closePreviousRun();\n\n    if (blockIndex > nextSlot_ / kBlockSize) {\n      nextSlot_ = (blockIndex * kBlockSize);\n      newBlockAllocated |= maybeAllocateBlocks(blockIndex);\n      block = getBlock(blockIndex).block.get();\n    }\n\n    // Update previous run info.\n    prevRunStart_ = nextSlot_;\n    prevOccupiedQuotient_ = quotient;\n  }\n\n  block->setRemainder(nextSlot_ % kBlockSize, remainderBits_, remainder);\n\n  // Set occupied bit for the given key.\n  block = getBlock(blockIndex).block.get();\n  block->setOccupied(offsetInBlock);\n\n  nextSlot_++;\n  prevKey_ = key;\n  numKeys_++;\n  return newBlockAllocated;\n}\n\nvoid QuotientMultiSetBuilder::setBlockPayload(uint64_t payload) {\n  DCHECK(!blocks_.empty());\n  blocks_.back().block->payload = payload;\n}\n\nvoid QuotientMultiSetBuilder::closePreviousRun() {\n  if (FOLLY_UNLIKELY(nextSlot_ == 0)) {\n    return;\n  }\n\n  // Mark runend for previous run.\n  const auto runEnd = nextSlot_ - 1;\n  auto block = getBlock(runEnd / kBlockSize).block.get();\n  block->setRunend(runEnd % kBlockSize);\n  numRuns_++;\n\n  // Set the offset of previous block if this run is the first one in that\n  // block.\n  auto prevRunOccupiedBlock =\n      getBlock(prevOccupiedQuotient_ / kBlockSize).block.get();\n  if (isPowTwo(prevRunOccupiedBlock->occupieds)) {\n    prevRunOccupiedBlock->offset = runEnd;\n  }\n\n  // Update mark all blocks before prevOccupiedQuotient_ + 1 to be ready.\n  size_t limitIndex = (prevOccupiedQuotient_ + 1) / kBlockSize;\n  for (size_t idx = readyBlocks_; idx < blocks_.size(); idx++) {\n    if (blocks_[idx].index < limitIndex) {\n      blocks_[idx].ready = true;\n      readyBlocks_++;\n    } else {\n      break;\n    }\n  }\n}\n\nvoid QuotientMultiSetBuilder::moveReadyBlocks(IOBufQueue& buff) {\n  while (!blocks_.empty()) {\n    if (!blocks_.front().ready) {\n      break;\n    }\n    buff.append(\n        IOBuf::takeOwnership(blocks_.front().block.release(), blockSize_));\n    blocks_.pop_front();\n  }\n}\n\nvoid QuotientMultiSetBuilder::flush(IOBufQueue& buff) {\n  moveReadyBlocks(buff);\n  readyBlocks_ = 0;\n}\n\nvoid QuotientMultiSetBuilder::close(IOBufQueue& buff) {\n  closePreviousRun();\n\n  // Mark all blocks as ready.\n  for (auto iter = blocks_.rbegin(); iter != blocks_.rend(); iter++) {\n    if (iter->ready) {\n      break;\n    }\n    iter->ready = true;\n  }\n\n  moveReadyBlocks(buff);\n\n  // Add metadata trailer. This will also allows getRemainder() to access whole\n  // 64-bits at any position without bounds-checking.\n  static_assert(sizeof(Metadata) > 7, \"getRemainder() is not safe\");\n  auto metadata = reinterpret_cast<Metadata*>(calloc(1, sizeof(Metadata)));\n  metadata->numBlocks = numBlocks_;\n  metadata->numKeys = numKeys_;\n  metadata->divisor = divisor_;\n  metadata->keyBits = keyBits_;\n  metadata->remainderBits = remainderBits_;\n  VLOG(2) << \"Metadata: \" << metadata->debugString();\n  buff.append(IOBuf::takeOwnership(metadata, sizeof(Metadata)));\n}\n\n} // namespace folly\n\n#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n"
  },
  {
    "path": "folly/compression/QuotientMultiSet.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <deque>\n#include <utility>\n\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/compression/Instructions.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/IOBufQueue.h>\n\n// A 128-bit integer type is needed for fast division.\n#define FOLLY_QUOTIENT_MULTI_SET_SUPPORTED FOLLY_HAVE_INT128_T\n\n#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n\nnamespace folly {\n\nnamespace qms_detail {\n\nusing UInt64InverseType = __uint128_t;\n\n} // namespace qms_detail\n\n/**\n * A space-efficient static data structure to store a non-decreasing sequence of\n * b-bit integers. If the integers are uniformly distributed lookup is O(1)-time\n * and performs a single random memory lookup with high probability.\n *\n * Space for n keys is bounded by (5 + b - log(n / loadFactor)) / loadFactor\n * bits per element, which makes it particularly efficient for very dense\n * sets. Note that 1 bit is taken up by the user-provided block payloads, and 1\n * depends on how close the table size is to a power of 2. Experimentally,\n * performance is good up to load factor 95%.\n *\n * Lookup returns a range of positions in the table. The intended use case is to\n * store hashes, as the first layer of a multi-layer hash table. If b is sized\n * to floor(log(n)) + k, the probability of a false positive (a non-empty range\n * is returned for a non-existent key) is approximately 2^-k, which makes it\n * competitive with a Bloom filter for low FP probabilities, with the additional\n * benefit that it also returns a range of positions to restrict the search in\n * subsequent layers.\n *\n * The data structure is inspired by the Rank-Select Quotient Filter\n * introduced in\n *\n *   Prashant Pandey, Michael A. Bender, Rob Johnson and Robert Patro,\n *   A General-Purpose Counting Filter: Making Every Bit Count, SIGMOD, 2017\n *\n * Besides being static, QuotientMultiSet differs from the data structure from\n * the paper in the following ways:\n *\n * - The table size can be arbitrary, rather than just powers-of-2. This can\n *   waste up to a bit for each residual, but it prevents 2x overhead when the\n *   desired table size is slightly larger than a power of 2.\n *\n * - Within each block all the holes are moved at the end. This enables\n *   efficient iteration, and makes the returned positions a contiguous range\n *   for each block, which allows to use them to index into a secondary data\n *   structure. An arbitrary 64-bit payload can be attached to each block; for\n *   example, this can be used to store the number of elements up to that block,\n *   so that positions can be translated to the element rank. Alternatively, the\n *   payload can be used to address blocks in the secondary data structure.\n *\n * - Correctness does not depend on the keys being uniformly distributed.\n *   However, performance does, as for arbitrary keys the worst-case lookup time\n *   can be linear.\n *\n * Implemented by Matt Ma based on a prototype by Giuseppe Ottaviano and\n * Sebastiano Vigna.\n *\n * Data layout:\n * ------------------------------------------------------------------------\n * | Block | Block | Block | Block |  ...                         | Block |\n * ------------------------------------------------------------------------\n *                /        |\n * ------------------------------------------------------------------------\n * | Payload | Occupieds | Offset | Runends |       Remainders * 64       |\n * ------------------------------------------------------------------------\n *\n * Each block contains 64 slots. Keys mapping to the same slot are stored\n * contiguously in a run. The occupieds and runends bitvectors are the\n * concatenation of the corresponding words in each block.\n *\n * - Occupieds bit indicates whether there is a key mapping to this quotient.\n *\n * - Offset stores the position of the runend of the first run in this block.\n *\n * - Runends bit indicates whether the slot is the end of some run. 1s in\n *   occupieds and runends bits are in 1-1 correspondence: the i-th 1 in the\n *   runends vector marks the run end of the i-th 1 in the occupieds.\n */\n\ntemplate <class Instructions = compression::instructions::Default>\nclass QuotientMultiSet final {\n public:\n  explicit QuotientMultiSet(StringPiece data);\n\n  // Each block contains 64 elements.\n  static constexpr size_t kBlockSize = 64;\n\n  // Position range of given key. End is not included. Range can be empty if the\n  // key is not found, in which case the values of begin and end are\n  // unspecified.\n  struct SlotRange {\n    size_t begin = 0;\n    size_t end = 0;\n\n    explicit operator bool() const {\n      DCHECK_LE(begin, end);\n      return begin < end;\n    }\n  };\n\n  class Iterator;\n\n  // Get the position range for the given key.\n  SlotRange equalRange(uint64_t key) const;\n\n  // Get payload of given block.\n  uint64_t getBlockPayload(uint64_t blockIndex) const;\n\n  friend class QuotientMultiSetBuilder;\n\n private:\n  // Metadata to describe a quotient table.\n  struct Metadata;\n\n  // Block contains payload, occupieds, runends, offsets and 64 remainders.\n  struct Block;\n  using BlockPtr = std::unique_ptr<Block, decltype(free)*>;\n\n  const Block* getBlock(size_t blockIndex) const {\n    return Block::get(data_ + blockIndex * blockSize_);\n  }\n\n  FOLLY_ALWAYS_INLINE std::pair<uint64_t, const Block*> findRunend(\n      uint64_t occupiedRank, uint64_t startPos) const;\n\n  const Metadata* metadata_;\n  const char* data_;\n  // Total number of blocks.\n  size_t numBlocks_;\n  size_t numSlots_;\n  // Number of bytes per block.\n  size_t blockSize_;\n  // Divisor for mapping from keys to slots.\n  uint64_t divisor_;\n  // fraction_ = 1 / divisor_.\n  qms_detail::UInt64InverseType fraction_;\n  // Number of key bits.\n  size_t keyBits_;\n  uint64_t maxKey_;\n  // Number of remainder bits.\n  size_t remainderBits_;\n  uint64_t remainderMask_;\n};\n\ntemplate <class Instructions>\nclass QuotientMultiSet<Instructions>::Iterator {\n public:\n  explicit Iterator(const QuotientMultiSet<Instructions>* qms);\n\n  // Advance to the next key.\n  bool next();\n\n  // Skip forward to the first key >= the given key.\n  bool skipTo(uint64_t key);\n\n  bool done() const { return pos_ == qms_->numSlots_; }\n\n  // Return current key.\n  uint64_t key() const { return key_; }\n\n  // Return current position in quotient multiset.\n  size_t pos() const { return pos_; }\n\n private:\n  // Position the iterator at the end and return false.\n  // Shortcut for use when implementing doNext, etc: return setEnd();\n  bool setEnd() {\n    pos_ = qms_->numSlots_;\n    return false;\n  }\n\n  // Move to next occupied.\n  bool nextOccupied();\n\n  const QuotientMultiSet<Instructions>* qms_;\n  uint64_t key_ = 0;\n\n  // State members for the quotient occupied position.\n  // Block index of key_'s occupied slot.\n  size_t occBlockIndex_ = -1;\n  // Block offset of key_'s occupied slot.\n  uint64_t occOffsetInBlock_ = 0;\n  // Occupied words of the occupiedBlock_ after quotientBlockOffset_.\n  uint64_t occWord_ = 0;\n  // Block of the current occupied slot.\n  const Block* occBlock_ = nullptr;\n\n  // State member for the actual key position.\n  // Position of the current key_.\n  size_t pos_ = -1;\n};\n\n/**\n * Class to build a QuotientMultiSet.\n *\n * The builder requires inserting elements in non-decreasing order.\n * Example usage:\n *   QuotientMultiSetBuilder builder(...);\n *   while (...) {\n *     if (builder.insert(key)) {\n *       builder.setBlockPayload(payload);\n *     }\n *     if (builder.numReadyBlocks() > N) {\n *       buff = builder.flush();\n *       write(buff);\n *     }\n *   }\n *   buff = builder.close();\n *   write(buff)\n */\nclass QuotientMultiSetBuilder final {\n public:\n  QuotientMultiSetBuilder(\n      size_t keyBits,\n      size_t expectedElements,\n      double loadFactor = kDefaultMaxLoadFactor);\n  ~QuotientMultiSetBuilder();\n\n  using Metadata = QuotientMultiSet<>::Metadata;\n  using Block = QuotientMultiSet<>::Block;\n\n  // Keeps load factor <= 0.95.\n  constexpr static double kDefaultMaxLoadFactor = 0.95;\n\n  constexpr static size_t kBlockSize = QuotientMultiSet<>::kBlockSize;\n\n  // Returns whether the key's slot is in a newly created block.\n  // Only allows insert keys in nondecreasing order.\n  bool insert(uint64_t key);\n\n  // Set payload of the latest created block.\n  // Can only be called immediately after an add() that returns true.\n  void setBlockPayload(uint64_t payload);\n\n  // Return all ready blocks till now. The ownership of these blocks will be\n  // transferred to the caller.\n  void flush(IOBufQueue& buff);\n\n  // Return all remaining blocks since last flush call and the final quotient\n  // table metadata. The ownership of these blocks will be transferred to the\n  // caller.\n  void close(folly::IOBufQueue& buff);\n\n  size_t numReadyBlocks() { return readyBlocks_; }\n\n private:\n  using BlockPtr = QuotientMultiSet<>::BlockPtr;\n\n  struct BlockWithState {\n    BlockWithState(BlockPtr ptr, size_t idx)\n        : block(std::move(ptr)), index(idx), ready(false) {}\n\n    BlockPtr block;\n    size_t index;\n    bool ready;\n  };\n\n  // Allocate space for blocks until limitIndex (included).\n  bool maybeAllocateBlocks(size_t limitIndex);\n\n  // Close the previous run.\n  void closePreviousRun();\n\n  // Move ready blocks to given IOBufQueue.\n  void moveReadyBlocks(IOBufQueue& buff);\n\n  // Get block for given block index.\n  BlockWithState& getBlock(uint64_t blockIndex) {\n    CHECK_GE(blockIndex, blocks_.front().index);\n    return blocks_[blockIndex - blocks_.front().index];\n  }\n\n  // Number of key bits.\n  const size_t keyBits_;\n  const uint64_t maxKey_;\n\n  // Total number of blocks.\n  size_t numBlocks_ = 0;\n  // Number of bytes per block.\n  size_t blockSize_ = 0;\n  // Divisor for mapping from keys to slots.\n  uint64_t divisor_;\n  // fraction_ = 1 / divisor_.\n  qms_detail::UInt64InverseType fraction_;\n  // Number of remainder bits.\n  uint64_t remainderBits_;\n\n  size_t numKeys_ = 0;\n  size_t numRuns_ = 0;\n\n  uint64_t prevKey_ = 0;\n  // Next slot to be used.\n  size_t nextSlot_ = 0;\n  // The actual start of previous run.\n  size_t prevRunStart_ = 0;\n  // The quotient of previous run.\n  size_t prevOccupiedQuotient_ = 0;\n  // Number of ready blocks in deque.\n  size_t readyBlocks_ = 0;\n\n  // Contains blocks since last flush call.\n  std::deque<BlockWithState> blocks_;\n\n  IOBufQueue buff_;\n};\n\n} // namespace folly\n\n#include <folly/compression/QuotientMultiSet-inl.h>\n\n#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n"
  },
  {
    "path": "folly/compression/Select64.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/Select64.h>\n\n#include <cstdint>\n#include <utility>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Portability.h>\n#include <folly/Utility.h>\n\nnamespace folly {\nnamespace detail {\n\nnamespace {\n\nconstexpr std::uint8_t selectInByte(std::size_t i, std::size_t j) {\n  auto r = std::uint8_t(0);\n  while (j--) {\n    auto const s = folly::constexpr_find_first_set(i);\n    r += s;\n    i >>= s;\n  }\n  return i == 0 ? 8 : r + folly::constexpr_find_first_set(i) - 1;\n}\n\ntemplate <std::size_t... I, std::size_t J>\nconstexpr auto makeSelectInByteNestedArray(\n    std::index_sequence<I...>, index_constant<J>) {\n  return std::array<std::uint8_t, sizeof...(I)>{{selectInByte(I, J)...}};\n}\n\ntemplate <typename Is, std::size_t... J>\nconstexpr auto makeSelectInByteArray(Is is, std::index_sequence<J...>) {\n  using inner = std::array<std::uint8_t, Is::size()>;\n  using outer = std::array<inner, sizeof...(J)>;\n  return outer{{makeSelectInByteNestedArray(is, index_constant<J>{})...}};\n}\n\n} // namespace\n\nFOLLY_STORAGE_CONSTEXPR std::array<std::array<std::uint8_t, 256>, 8> const\n    kSelectInByte = makeSelectInByteArray(\n        std::make_index_sequence<256>{}, std::make_index_sequence<8>{});\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/Select64.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/compression/Instructions.h>\n\nnamespace folly {\n\nnamespace detail {\n\n//  kSelectInByte\n//\n//  Described in:\n//    http://dsiutils.di.unimi.it/docs/it/unimi/dsi/bits/Fast.html#selectInByte\n//\n//  A precomputed tabled containing the positions of the set bits in the binary\n//  representations of all 8-bit unsigned integers.\n//\n//  For i: [0, 256) ranging over all 8-bit unsigned integers and for j: [0, 8)\n//  ranging over all 0-based bit positions in an 8-bit unsigned integer, the\n//  table entry kSelectInByte[i][j] is the 0-based bit position of the j-th set\n//  bit in the binary representation of i, or 8 if it has fewer than j set bits.\n//\n//  Example: i: 17 (b00010001), j: [0, 8)\n//    kSelectInByte[b00010001][0] = 0\n//    kSelectInByte[b00010001][1] = 4\n//    kSelectInByte[b00010001][2] = 8\n//    ...\n//    kSelectInByte[b00010001][7] = 8\nextern std::array<std::array<std::uint8_t, 256>, 8> const kSelectInByte;\n\n} // namespace detail\n\n/**\n * Returns the position of the k-th 1 in the 64-bit word x.\n * k is 0-based, so k=0 returns the position of the first 1.\n *\n * Uses the broadword selection algorithm by Vigna [1], improved by Gog\n * and Petri [2] and Vigna [3].\n *\n * [1] Sebastiano Vigna. Broadword Implementation of Rank/Select\n *     Queries. WEA, 2008\n *\n * [2] Simon Gog, Matthias Petri. Optimized succinct data structures\n *     for massive data. Softw. Pract. Exper., 2014\n *\n * [3] Sebastiano Vigna. MG4J 5.2.1. http://mg4j.di.unimi.it/\n */\ntemplate <class Instructions>\ninline uint64_t select64(uint64_t x, uint64_t k) {\n  DCHECK_LT(k, Instructions::popcount(x));\n\n  constexpr uint64_t kOnesStep4 = 0x1111111111111111ULL;\n  constexpr uint64_t kOnesStep8 = 0x0101010101010101ULL;\n  constexpr uint64_t kMSBsStep8 = 0x80ULL * kOnesStep8;\n\n  auto s = x;\n  s = s - ((s & 0xA * kOnesStep4) >> 1);\n  s = (s & 0x3 * kOnesStep4) + ((s >> 2) & 0x3 * kOnesStep4);\n  s = (s + (s >> 4)) & 0xF * kOnesStep8;\n  uint64_t byteSums = s * kOnesStep8;\n\n  uint64_t kStep8 = k * kOnesStep8;\n  uint64_t geqKStep8 = (((kStep8 | kMSBsStep8) - byteSums) & kMSBsStep8);\n  uint64_t place = Instructions::popcount(geqKStep8) * 8;\n  uint64_t byteRank = k - (((byteSums << 8) >> place) & uint64_t(0xFF));\n  return place + detail::kSelectInByte[byteRank][((x >> place) & 0xFF)];\n}\n\n#if FOLLY_X64 || defined(__i386)\ntemplate <>\nFOLLY_ALWAYS_INLINE uint64_t\nselect64<compression::instructions::Haswell>(uint64_t x, uint64_t k) {\n#if defined(__GNUC__)\n  // GCC and Clang won't inline the intrinsics.\n  uint64_t result = uint64_t(1) << k;\n\n  asm(\"pdep %1, %0, %0\\n\\t\"\n      \"tzcnt %0, %0\"\n      : \"+r\"(result)\n      : \"r\"(x));\n\n  return result;\n#else\n  return _tzcnt_u64(_pdep_u64(1ULL << k, x));\n#endif\n}\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/Utils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/compression/Compression.h>\n#include <folly/io/Cursor.h>\n#include <folly/io/IOBuf.h>\n#include <folly/lang/Bits.h>\n\n/**\n * Helper functions for compression codecs.\n */\nnamespace folly {\nnamespace compression {\nnamespace detail {\n\n/**\n * Reads sizeof(T) bytes, and returns false if not enough bytes are available.\n * Returns true if the first n bytes are equal to prefix when interpreted as\n * a little endian T.\n */\ntemplate <typename T>\ntypename std::enable_if<std::is_unsigned<T>::value, bool>::type\ndataStartsWithLE(const IOBuf* data, T prefix, uint64_t n = sizeof(T)) {\n  DCHECK_GT(n, 0);\n  DCHECK_LE(n, sizeof(T));\n  T value;\n  io::Cursor cursor{data};\n  if (!cursor.tryReadLE(value)) {\n    return false;\n  }\n  const T mask = n == sizeof(T) ? T(-1) : (T(1) << (8 * n)) - 1;\n  return prefix == (value & mask);\n}\n\ntemplate <typename T>\ntypename std::enable_if<std::is_arithmetic<T>::value, std::string>::type\nprefixToStringLE(T prefix, uint64_t n = sizeof(T)) {\n  DCHECK_GT(n, 0);\n  DCHECK_LE(n, sizeof(T));\n  prefix = Endian::little(prefix);\n  std::string result;\n  result.resize(n);\n  memcpy(&result[0], &prefix, n);\n  return result;\n}\n\n/**\n * Calls @p streamFn in a loop, and guarantees that @p streamFn is never called\n * with `input.size() > chunkSize` or `output.size() > chunkSize`. It behaves\n * as-if streamFn(input, output, flushOp) was called.\n *\n * This function relies only on the rules defined by\n * StreamCodec::compressStream() and StreamCodec::uncompressStream() for\n * correctness.\n */\ntemplate <typename StreamFn>\nbool chunkedStream(\n    size_t chunkSize,\n    ByteRange& input,\n    MutableByteRange& output,\n    StreamCodec::FlushOp flushOp,\n    StreamFn&& streamFn) {\n  for (;;) {\n    const auto chunkInputSize = std::min<size_t>(input.size(), chunkSize);\n    const auto chunkOutputSize = std::min<size_t>(output.size(), chunkSize);\n    auto chunkInput = input.subpiece(0, chunkInputSize);\n    auto chunkOutput = output.subpiece(0, chunkOutputSize);\n\n    // If we're presenting the entire suffix of the input in this call, use the\n    // users flush op, otherwise use NONE.\n    const StreamCodec::FlushOp chunkFlushOp =\n        (chunkInput.size() == input.size())\n        ? flushOp\n        : StreamCodec::FlushOp::NONE;\n\n    const bool finished = streamFn(chunkInput, chunkOutput, chunkFlushOp);\n\n    // Update input / output buffers\n    const size_t inputConsumed = chunkInputSize - chunkInput.size();\n    const size_t outputProduced = chunkOutputSize - chunkOutput.size();\n    input.advance(inputConsumed);\n    output.advance(outputProduced);\n\n    // If the underlying streaming function returns true, we want to forward\n    // that to the caller.\n    // Compression: If flushOp == NONE, this is guaranteed not to happen.\n    // Decompression: This signals the end of a frame, and we must forward that\n    //                signal to the caller without consuming any more input.\n    if (finished) {\n      return true;\n    }\n\n    // We've consumed the entire input, which means we fulfilled our as-if\n    // guarantee.\n    if (input.empty()) {\n      DCHECK(!finished);\n      return false;\n    }\n\n    // Compression: Presenting more input bytes guarantees that there must be\n    //              more output bytes to produce. Therefore we've made maximal\n    //              forward progress.\n    // Decompression: The only flushOp that guarantees maximal forward progress\n    //                is FLUSH. Its signal that the flush is complete is\n    //                !output.empty(). So we can safely return if\n    //                output.empty().\n    if (output.empty()) {\n      DCHECK(!input.empty() && !finished);\n      return false;\n    }\n\n    // If we've failed to make forward progress, return to the caller.\n    // The underlying streaming function is always required to make some forward\n    // progress if any forward progress is possible. So forward progress must be\n    // impossible.\n    // This preserves StreamCodec's strong guarantee of forward progress, and\n    // protects us from infinite loops.\n    if (inputConsumed == 0 && outputProduced == 0) {\n      DCHECK(!output.empty() && !input.empty() && !finished);\n      return false;\n    }\n  }\n}\n\n// Some codecs use uint32_t for sizes so we need to limit the chunk size to 4GB.\n// Rather than allow 4GB inputs / outputs, which nearly never happens, limit to\n// 4MB, so we don't have complex logic that almost never runs.\nconstexpr size_t kDefaultChunkSizeFor32BitSizes = size_t(4) << 20;\n\n} // namespace detail\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/Zlib.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/Zlib.h>\n\n#if FOLLY_HAVE_LIBZ\n\n#include <folly/Conv.h>\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/ScopeGuard.h>\n#include <folly/compression/Compression.h>\n#include <folly/compression/Utils.h>\n#include <folly/io/Cursor.h>\n\nusing folly::compression::detail::dataStartsWithLE;\nusing folly::compression::detail::prefixToStringLE;\n\nnamespace folly {\nnamespace compression {\nnamespace zlib {\n\nnamespace {\n\nbool isValidStrategy(int strategy) {\n  std::array<int, 5> strategies{{\n      Z_DEFAULT_STRATEGY,\n      Z_FILTERED,\n      Z_HUFFMAN_ONLY,\n      Z_RLE,\n      Z_FIXED,\n  }};\n  return std::any_of(strategies.begin(), strategies.end(), [&](int i) {\n    return i == strategy;\n  });\n}\n\nint getWindowBits(Options::Format format, int windowSize) {\n  switch (format) {\n    case Options::Format::ZLIB:\n      return windowSize;\n    case Options::Format::GZIP:\n      return windowSize + 16;\n    case Options::Format::RAW:\n      return -windowSize;\n    case Options::Format::AUTO:\n      return windowSize + 32;\n    default:\n      return windowSize;\n  }\n}\n\nCodecType getCodecType(Options options) {\n  if (options.windowSize == 15 && options.format == Options::Format::ZLIB) {\n    return CodecType::ZLIB;\n  } else if (\n      options.windowSize == 15 && options.format == Options::Format::GZIP) {\n    return CodecType::GZIP;\n  } else {\n    return CodecType::USER_DEFINED;\n  }\n}\n\nclass ZlibStreamCodec final : public StreamCodec {\n public:\n  static std::unique_ptr<Codec> createCodec(Options options, int level);\n  static std::unique_ptr<StreamCodec> createStream(Options options, int level);\n\n  explicit ZlibStreamCodec(Options options, int level);\n  ~ZlibStreamCodec() override;\n\n  std::vector<std::string> validPrefixes() const override;\n  bool canUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) const override;\n\n private:\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n\n  void doResetStream() override;\n  bool doCompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flush) override;\n  bool doUncompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flush) override;\n\n  void resetDeflateStream();\n  void resetInflateStream();\n\n  Options options_;\n\n  Optional<z_stream> deflateStream_{};\n  Optional<z_stream> inflateStream_{};\n  int level_;\n  bool needReset_{true};\n};\nconstexpr uint16_t kGZIPMagicLE = 0x8B1F;\n\nstd::vector<std::string> ZlibStreamCodec::validPrefixes() const {\n  if (type() == CodecType::ZLIB) {\n    // Zlib streams start with a 2 byte header.\n    //\n    //   0   1\n    // +---+---+\n    // |CMF|FLG|\n    // +---+---+\n    //\n    // We won't restrict the values of any sub-fields except as described below.\n    //\n    // The lowest 4 bits of CMF is the compression method (CM).\n    // CM == 0x8 is the deflate compression method, which is currently the only\n    // supported compression method, so any valid prefix must have CM == 0x8.\n    //\n    // The lowest 5 bits of FLG is FCHECK.\n    // FCHECK must be such that the two header bytes are a multiple of 31 when\n    // interpreted as a big endian 16-bit number.\n    std::vector<std::string> result;\n    // 16 values for the first byte, 8 values for the second byte.\n    // There are also 4 combinations where both 0x00 and 0x1F work as FCHECK.\n    result.reserve(132);\n    // Select all values for the CMF byte that use the deflate algorithm 0x8.\n    for (uint32_t first = 0x0800; first <= 0xF800; first += 0x1000) {\n      // Select all values for the FLG, but leave FCHECK as 0 since it's fixed.\n      for (uint32_t second = 0x00; second <= 0xE0; second += 0x20) {\n        uint16_t prefix = first | second;\n        // Compute FCHECK.\n        prefix += 31 - (prefix % 31);\n        result.push_back(prefixToStringLE(Endian::big(prefix)));\n        // zlib won't produce this, but it is a valid prefix.\n        if ((prefix & 0x1F) == 31) {\n          prefix -= 31;\n          result.push_back(prefixToStringLE(Endian::big(prefix)));\n        }\n      }\n    }\n    return result;\n  } else if (type() == CodecType::GZIP) {\n    // The gzip frame starts with 2 magic bytes.\n    return {prefixToStringLE(kGZIPMagicLE)};\n  } else {\n    return {};\n  }\n}\n\nbool ZlibStreamCodec::canUncompress(\n    const IOBuf* data, Optional<uint64_t>) const {\n  if (type() == CodecType::ZLIB) {\n    uint16_t value;\n    io::Cursor cursor{data};\n    if (!cursor.tryReadBE(value)) {\n      return false;\n    }\n    // zlib compressed if using deflate and is a multiple of 31.\n    return (value & 0x0F00) == 0x0800 && value % 31 == 0;\n  } else if (type() == CodecType::GZIP) {\n    return dataStartsWithLE(data, kGZIPMagicLE);\n  } else {\n    return false;\n  }\n}\n\nuint64_t ZlibStreamCodec::doMaxCompressedLength(\n    uint64_t uncompressedLength) const {\n  // When passed a nullptr, deflateBound() adds 6 bytes for a zlib wrapper. A\n  // gzip wrapper is 18 bytes, so we add the 12 byte difference.\n  return deflateBound(nullptr, uncompressedLength) +\n      (options_.format == Options::Format::GZIP ? 12 : 0);\n}\n\nstd::unique_ptr<Codec> ZlibStreamCodec::createCodec(\n    Options options, int level) {\n  return std::make_unique<ZlibStreamCodec>(options, level);\n}\n\nstd::unique_ptr<StreamCodec> ZlibStreamCodec::createStream(\n    Options options, int level) {\n  return std::make_unique<ZlibStreamCodec>(options, level);\n}\n\nbool inBounds(int value, int low, int high) {\n  return (value >= low) && (value <= high);\n}\n\nint zlibConvertLevel(int level) {\n  switch (level) {\n    case COMPRESSION_LEVEL_FASTEST:\n      return 1;\n    case COMPRESSION_LEVEL_DEFAULT:\n      return 6;\n    case COMPRESSION_LEVEL_BEST:\n      return 9;\n  }\n  if (!inBounds(level, 0, 9)) {\n    throw std::invalid_argument(\n        to<std::string>(\"ZlibStreamCodec: invalid level: \", level));\n  }\n  return level;\n}\n\nZlibStreamCodec::ZlibStreamCodec(Options options, int level)\n    : StreamCodec(\n          getCodecType(options),\n          zlibConvertLevel(level),\n          getCodecType(options) == CodecType::GZIP ? \"gzip\" : \"zlib\"),\n      level_(zlibConvertLevel(level)) {\n  options_ = options;\n\n  // Although zlib allows a windowSize of 8..15, a value of 8 is not\n  // properly supported and is treated as a value of 9. This means data deflated\n  // with windowSize==8 can not be re-inflated with windowSize==8. windowSize==8\n  // is also not supported for gzip and raw deflation.\n  // Hence, the codec supports only 9..15.\n  if (!inBounds(options_.windowSize, 9, 15)) {\n    throw std::invalid_argument(\n        to<std::string>(\n            \"ZlibStreamCodec: invalid windowSize option: \",\n            options.windowSize));\n  }\n  if (!inBounds(options_.memLevel, 1, 9)) {\n    throw std::invalid_argument(\n        to<std::string>(\n            \"ZlibStreamCodec: invalid memLevel option: \", options.memLevel));\n  }\n  if (!isValidStrategy(options_.strategy)) {\n    throw std::invalid_argument(\n        to<std::string>(\n            \"ZlibStreamCodec: invalid strategy: \", options.strategy));\n  }\n}\n\nZlibStreamCodec::~ZlibStreamCodec() {\n  if (deflateStream_) {\n    deflateEnd(deflateStream_.get_pointer());\n    deflateStream_.reset();\n  }\n  if (inflateStream_) {\n    inflateEnd(inflateStream_.get_pointer());\n    inflateStream_.reset();\n  }\n}\n\nvoid ZlibStreamCodec::doResetStream() {\n  needReset_ = true;\n}\n\nvoid ZlibStreamCodec::resetDeflateStream() {\n  if (deflateStream_) {\n    int const rc = deflateReset(deflateStream_.get_pointer());\n    if (rc != Z_OK) {\n      deflateStream_.reset();\n      throw std::runtime_error(\n          to<std::string>(\"ZlibStreamCodec: deflateReset error: \", rc));\n    }\n    return;\n  }\n  deflateStream_ = z_stream{};\n\n  // The automatic header detection format is only for inflation.\n  // Use zlib for deflation if the format is auto.\n  int const windowBits = getWindowBits(\n      options_.format == Options::Format::AUTO\n          ? Options::Format::ZLIB\n          : options_.format,\n      options_.windowSize);\n\n  int const rc = deflateInit2(\n      deflateStream_.get_pointer(),\n      level_,\n      Z_DEFLATED,\n      windowBits,\n      options_.memLevel,\n      options_.strategy);\n  if (rc != Z_OK) {\n    deflateStream_.reset();\n    throw std::runtime_error(\n        to<std::string>(\"ZlibStreamCodec: deflateInit error: \", rc));\n  }\n}\n\nvoid ZlibStreamCodec::resetInflateStream() {\n  if (inflateStream_) {\n    int const rc = inflateReset(inflateStream_.get_pointer());\n    if (rc != Z_OK) {\n      inflateStream_.reset();\n      throw std::runtime_error(\n          to<std::string>(\"ZlibStreamCodec: inflateReset error: \", rc));\n    }\n    return;\n  }\n  inflateStream_ = z_stream{};\n  int const rc = inflateInit2(\n      inflateStream_.get_pointer(),\n      getWindowBits(options_.format, options_.windowSize));\n  if (rc != Z_OK) {\n    inflateStream_.reset();\n    throw std::runtime_error(\n        to<std::string>(\"ZlibStreamCodec: inflateInit error: \", rc));\n  }\n}\n\nint zlibTranslateFlush(StreamCodec::FlushOp flush) {\n  switch (flush) {\n    case StreamCodec::FlushOp::NONE:\n      return Z_NO_FLUSH;\n    case StreamCodec::FlushOp::FLUSH:\n      return Z_SYNC_FLUSH;\n    case StreamCodec::FlushOp::END:\n      return Z_FINISH;\n    default:\n      throw std::invalid_argument(\"ZlibStreamCodec: Invalid flush\");\n  }\n}\n\nint zlibThrowOnError(int rc) {\n  switch (rc) {\n    case Z_OK:\n    case Z_BUF_ERROR:\n    case Z_STREAM_END:\n      return rc;\n    default:\n      throw std::runtime_error(to<std::string>(\"ZlibStreamCodec: error: \", rc));\n  }\n}\n\nbool ZlibStreamCodec::doCompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flush) {\n  // Zlib uses uint32_t for sizes, so we can't compress more than 4GB at a time\n  return detail::chunkedStream(\n      detail::kDefaultChunkSizeFor32BitSizes,\n      input,\n      output,\n      flush,\n      [this](auto& input, auto& output, auto flush) {\n        if (needReset_) {\n          resetDeflateStream();\n          needReset_ = false;\n        }\n        DCHECK(deflateStream_.has_value());\n        // zlib will return Z_STREAM_ERROR if output.data() is null.\n        if (output.data() == nullptr) {\n          return false;\n        }\n        deflateStream_->next_in = const_cast<uint8_t*>(input.data());\n        deflateStream_->avail_in = to_narrow(input.size());\n        deflateStream_->next_out = output.data();\n        deflateStream_->avail_out = to_narrow(output.size());\n        DCHECK_EQ(deflateStream_->avail_in, input.size());\n        DCHECK_EQ(deflateStream_->avail_out, output.size());\n        SCOPE_EXIT {\n          input.uncheckedAdvance(input.size() - deflateStream_->avail_in);\n          output.uncheckedAdvance(output.size() - deflateStream_->avail_out);\n        };\n        int const rc = zlibThrowOnError(\n            deflate(deflateStream_.get_pointer(), zlibTranslateFlush(flush)));\n        switch (flush) {\n          case StreamCodec::FlushOp::NONE:\n            return false;\n          case StreamCodec::FlushOp::FLUSH:\n            return deflateStream_->avail_in == 0 &&\n                deflateStream_->avail_out != 0;\n          case StreamCodec::FlushOp::END:\n            return rc == Z_STREAM_END;\n          default:\n            throw std::invalid_argument(\"ZlibStreamCodec: Invalid flush\");\n        }\n      });\n}\n\nbool ZlibStreamCodec::doUncompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flush) {\n  // Zlib uses uint32_t for sizes, so we can't uncompress more than 4GB at a\n  // time.\n  return detail::chunkedStream(\n      detail::kDefaultChunkSizeFor32BitSizes,\n      input,\n      output,\n      flush,\n      [this](auto& input, auto& output, auto flush) {\n        if (needReset_) {\n          resetInflateStream();\n          needReset_ = false;\n        }\n        DCHECK(inflateStream_.has_value());\n        // zlib will return Z_STREAM_ERROR if output.data() is null.\n        if (output.data() == nullptr) {\n          return false;\n        }\n        inflateStream_->next_in = const_cast<uint8_t*>(input.data());\n        inflateStream_->avail_in = to_narrow(input.size());\n        inflateStream_->next_out = output.data();\n        inflateStream_->avail_out = to_narrow(output.size());\n        DCHECK_EQ(inflateStream_->avail_in, input.size());\n        DCHECK_EQ(inflateStream_->avail_out, output.size());\n        SCOPE_EXIT {\n          input.advance(input.size() - inflateStream_->avail_in);\n          output.advance(output.size() - inflateStream_->avail_out);\n        };\n        int const rc = zlibThrowOnError(\n            inflate(inflateStream_.get_pointer(), zlibTranslateFlush(flush)));\n        return rc == Z_STREAM_END;\n      });\n}\n\n} // namespace\n\nOptions defaultGzipOptions() {\n  return Options(Options::Format::GZIP);\n}\n\nOptions defaultZlibOptions() {\n  return Options(Options::Format::ZLIB);\n}\n\nstd::unique_ptr<Codec> getCodec(Options options, int level) {\n  return ZlibStreamCodec::createCodec(options, level);\n}\n\nstd::unique_ptr<StreamCodec> getStreamCodec(Options options, int level) {\n  return ZlibStreamCodec::createStream(options, level);\n}\n\n} // namespace zlib\n} // namespace compression\n} // namespace folly\n\n#endif // FOLLY_HAVE_LIBZ\n"
  },
  {
    "path": "folly/compression/Zlib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/compression/Compression.h>\n\n#if FOLLY_HAVE_LIBZ\n\n#include <zlib.h>\n\n/**\n * Interface for Zlib-specific codec initialization.\n */\nnamespace folly {\nnamespace compression {\nnamespace zlib {\n\nstruct Options {\n  /**\n   * ZLIB: default option -- write a zlib wrapper as documented in RFC 1950.\n   *\n   * GZIP: write a simple gzip header and trailer around the compressed data\n   * instead of a zlib wrapper.\n   *\n   * RAW: deflate will generate raw deflate data with no zlib header or\n   * trailer, and will not compute a check value.\n   *\n   * AUTO: enable automatic header detection for decoding gzip or zlib data.\n   * For deflation, ZLIB will be used.\n   */\n  enum class Format { ZLIB, GZIP, RAW, AUTO };\n\n  explicit Options(\n      Format format_ = Format::ZLIB,\n      int windowSize_ = 15,\n      int memLevel_ = 8,\n      int strategy_ = Z_DEFAULT_STRATEGY)\n      : format(format_),\n        windowSize(windowSize_),\n        memLevel(memLevel_),\n        strategy(strategy_) {}\n\n  Format format;\n\n  /**\n   * windowSize is the base two logarithm of the window size (the size of the\n   * history buffer). It should be in the range 9..15. Larger values of this\n   * parameter result in better compression at the expense of memory usage.\n   *\n   * The default value is 15.\n   *\n   * NB: when inflating/uncompressing data, the windowSize must be greater than\n   * or equal to the size used when deflating/compressing.\n   */\n  int windowSize;\n\n  /**\n   * \"The memLevel parameter specifies how much memory should be allocated for\n   * the internal compression state. memLevel=1 uses minimum memory but is slow\n   * and reduces compression ratio; memLevel=9 uses maximum memory for optimal\n   * speed. The default value is 8.\"\n   */\n  int memLevel;\n\n  /**\n   * The strategy parameter is used to tune the compression algorithm.\n   * Supported values:\n   * - Z_DEFAULT_STRATEGY: normal data\n   * - Z_FILTERED: data produced by a filter (or predictor)\n   * - Z_HUFFMAN_ONLY: force Huffman encoding only (no string match)\n   * - Z_RLE: limit match distances to one\n   * - Z_FIXED: prevents the use of dynamic Huffman codes\n   *\n   * The strategy parameter only affects the compression ratio but not the\n   * correctness of the compressed output.\n   */\n  int strategy;\n};\n\n/**\n * Get the default options for gzip compression.\n * A codec created with these options will have type CodecType::GZIP.\n */\nOptions defaultGzipOptions();\n\n/**\n * Get the default options for zlib compression.\n * A codec created with these options will have type CodecType::ZLIB.\n */\nOptions defaultZlibOptions();\n\n/**\n * Get a codec with the given options and compression level.\n *\n * If the windowSize is 15 and the format is Format::ZLIB or Format::GZIP, then\n * the type of the codec will be CodecType::ZLIB or CodecType::GZIP\n * respectively. Otherwise, the type will be CodecType::USER_DEFINED.\n *\n * Automatic uncompression is not supported with USER_DEFINED codecs.\n *\n * Levels supported: 0 = no compression, 1 = fast, ..., 9 = best; default = 6\n */\nstd::unique_ptr<Codec> getCodec(\n    Options options = Options(), int level = COMPRESSION_LEVEL_DEFAULT);\nstd::unique_ptr<StreamCodec> getStreamCodec(\n    Options options = Options(), int level = COMPRESSION_LEVEL_DEFAULT);\n\n} // namespace zlib\n} // namespace compression\n} // namespace folly\n\n#endif // FOLLY_HAVE_LIBZ\n"
  },
  {
    "path": "folly/compression/Zstd.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/Zstd.h>\n\n#if FOLLY_HAVE_LIBZSTD\n\n#include <stdexcept>\n#include <string>\n\n#include <folly/Conv.h>\n#include <folly/Range.h>\n#include <folly/ScopeGuard.h>\n#include <folly/compression/CompressionContextPoolSingletons.h>\n#include <folly/compression/Utils.h>\n\nstatic_assert(\n    ZSTD_VERSION_NUMBER >= 10400,\n    \"zstd-1.4.0 is the minimum supported zstd version.\");\n\nusing folly::compression::detail::dataStartsWithLE;\nusing folly::compression::detail::prefixToStringLE;\n\nusing namespace folly::compression::contexts;\n\nnamespace folly {\nnamespace compression {\nnamespace zstd {\nnamespace {\n\nsize_t zstdThrowIfError(size_t rc) {\n  if (!ZSTD_isError(rc)) {\n    return rc;\n  }\n  throw std::runtime_error(\n      to<std::string>(\"ZSTD returned an error: \", ZSTD_getErrorName(rc)));\n}\n\nZSTD_EndDirective zstdTranslateFlush(StreamCodec::FlushOp flush) {\n  switch (flush) {\n    case StreamCodec::FlushOp::NONE:\n      return ZSTD_e_continue;\n    case StreamCodec::FlushOp::FLUSH:\n      return ZSTD_e_flush;\n    case StreamCodec::FlushOp::END:\n      return ZSTD_e_end;\n    default:\n      throw std::invalid_argument(\"ZSTDStreamCodec: Invalid flush\");\n  }\n}\n\nclass ZSTDStreamCodec final : public StreamCodec {\n public:\n  explicit ZSTDStreamCodec(Options options);\n\n  std::vector<std::string> validPrefixes() const override;\n  bool canUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) const override;\n\n private:\n  bool doNeedsUncompressedLength() const override;\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override;\n  Optional<uint64_t> doGetUncompressedLength(\n      IOBuf const* data, Optional<uint64_t> uncompressedLength) const override;\n\n  void doResetStream() override;\n  bool doCompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flushOp) override;\n  bool doUncompressStream(\n      ByteRange& input,\n      MutableByteRange& output,\n      StreamCodec::FlushOp flushOp) override;\n\n  void resetCCtx();\n  void resetDCtx();\n\n  Options options_;\n  ZSTD_CCtx_Pool::Ref cctx_{getNULL_ZSTD_CCtx()};\n  ZSTD_DCtx_Pool::Ref dctx_{getNULL_ZSTD_DCtx()};\n};\n\nconstexpr uint32_t kZSTDMagicLE = 0xFD2FB528;\n\nstd::vector<std::string> ZSTDStreamCodec::validPrefixes() const {\n  return {prefixToStringLE(kZSTDMagicLE)};\n}\n\nbool ZSTDStreamCodec::canUncompress(\n    const IOBuf* data, Optional<uint64_t>) const {\n  return dataStartsWithLE(data, kZSTDMagicLE);\n}\n\nCodecType codecType(Options const& options) {\n  int const level = options.level();\n  DCHECK_NE(level, 0);\n  return level > 0 ? CodecType::ZSTD : CodecType::ZSTD_FAST;\n}\n\nZSTDStreamCodec::ZSTDStreamCodec(Options options)\n    : StreamCodec(codecType(options), options.level()),\n      options_(std::move(options)) {}\n\nbool ZSTDStreamCodec::doNeedsUncompressedLength() const {\n  return false;\n}\n\nuint64_t ZSTDStreamCodec::doMaxCompressedLength(\n    uint64_t uncompressedLength) const {\n  return ZSTD_compressBound(uncompressedLength);\n}\n\nOptional<uint64_t> ZSTDStreamCodec::doGetUncompressedLength(\n    IOBuf const* data, Optional<uint64_t> uncompressedLength) const {\n  // Read decompressed size from frame if available in first IOBuf.\n  auto const decompressedSize =\n      ZSTD_getFrameContentSize(data->data(), data->length());\n  if (decompressedSize == ZSTD_CONTENTSIZE_UNKNOWN ||\n      decompressedSize == ZSTD_CONTENTSIZE_ERROR) {\n    return uncompressedLength;\n  }\n  if (uncompressedLength && *uncompressedLength != decompressedSize) {\n    throw std::runtime_error(\"ZSTD: invalid uncompressed length\");\n  }\n  return decompressedSize;\n}\n\nvoid ZSTDStreamCodec::doResetStream() {\n  cctx_.reset(nullptr);\n  dctx_.reset(nullptr);\n}\n\nvoid ZSTDStreamCodec::resetCCtx() {\n  DCHECK(cctx_ == nullptr);\n  cctx_ = getZSTD_CCtx(); // Gives us a clean context\n  DCHECK(cctx_ != nullptr);\n  zstdThrowIfError(\n      ZSTD_CCtx_setParametersUsingCCtxParams(cctx_.get(), options_.params()));\n  zstdThrowIfError(ZSTD_CCtx_setPledgedSrcSize(\n      cctx_.get(), uncompressedLength().value_or(ZSTD_CONTENTSIZE_UNKNOWN)));\n}\n\nbool ZSTDStreamCodec::doCompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp flushOp) {\n  if (cctx_ == nullptr) {\n    resetCCtx();\n  }\n  ZSTD_inBuffer in = {input.data(), input.size(), 0};\n  ZSTD_outBuffer out = {output.data(), output.size(), 0};\n  SCOPE_EXIT {\n    input.uncheckedAdvance(in.pos);\n    output.uncheckedAdvance(out.pos);\n  };\n  size_t const rc = zstdThrowIfError(ZSTD_compressStream2(\n      cctx_.get(), &out, &in, zstdTranslateFlush(flushOp)));\n  switch (flushOp) {\n    case StreamCodec::FlushOp::NONE:\n      return false;\n    case StreamCodec::FlushOp::FLUSH:\n      return rc == 0;\n    case StreamCodec::FlushOp::END:\n      if (rc == 0) {\n        // Surrender our cctx_\n        doResetStream();\n      }\n      return rc == 0;\n    default:\n      throw std::invalid_argument(\"ZSTD: invalid FlushOp\");\n  }\n}\n\nvoid ZSTDStreamCodec::resetDCtx() {\n  DCHECK(dctx_ == nullptr);\n  dctx_ = getZSTD_DCtx(); // Gives us a clean context\n  DCHECK(dctx_ != nullptr);\n  if (options_.maxWindowSize() != 0) {\n    zstdThrowIfError(\n        ZSTD_DCtx_setMaxWindowSize(dctx_.get(), options_.maxWindowSize()));\n  }\n}\n\nbool ZSTDStreamCodec::doUncompressStream(\n    ByteRange& input, MutableByteRange& output, StreamCodec::FlushOp) {\n  if (dctx_ == nullptr) {\n    resetDCtx();\n  }\n  ZSTD_inBuffer in = {input.data(), input.size(), 0};\n  ZSTD_outBuffer out = {output.data(), output.size(), 0};\n  SCOPE_EXIT {\n    input.uncheckedAdvance(in.pos);\n    output.uncheckedAdvance(out.pos);\n  };\n  size_t const rc =\n      zstdThrowIfError(ZSTD_decompressStream(dctx_.get(), &out, &in));\n  if (rc == 0) {\n    // Surrender our dctx_\n    doResetStream();\n  }\n  return rc == 0;\n}\n\n} // namespace\n\nOptions::Options(int level) : params_(ZSTD_createCCtxParams()), level_(level) {\n  if (params_ == nullptr) {\n    throw std::bad_alloc{};\n  }\n  zstdThrowIfError(ZSTD_CCtxParams_init(params_.get(), level));\n}\n\nvoid Options::set(ZSTD_cParameter param, unsigned value) {\n  zstdThrowIfError(ZSTD_CCtxParams_setParameter(params_.get(), param, value));\n  if (param == ZSTD_c_compressionLevel) {\n    level_ = static_cast<int>(value);\n  }\n}\n\n/* static */ void Options::freeCCtxParams(ZSTD_CCtx_params* params) {\n  ZSTD_freeCCtxParams(params);\n}\n\nstd::unique_ptr<Codec> getCodec(Options options) {\n  return std::make_unique<ZSTDStreamCodec>(std::move(options));\n}\n\nstd::unique_ptr<StreamCodec> getStreamCodec(Options options) {\n  return std::make_unique<ZSTDStreamCodec>(std::move(options));\n}\n\n} // namespace zstd\n} // namespace compression\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/compression/Zstd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory.h>\n\n#include <folly/Memory.h>\n#include <folly/Portability.h>\n#include <folly/compression/Compression.h>\n\n#if FOLLY_HAVE_LIBZSTD\n\n#ifndef ZSTD_STATIC_LINKING_ONLY\n#define ZSTD_STATIC_LINKING_ONLY\n#endif\n#include <zstd.h>\n\nnamespace folly {\nnamespace compression {\nnamespace zstd {\n\n/**\n * Interface for zstd-specific codec initialization.\n */\nclass Options {\n public:\n  /* Create an Options struct with the default options for the given `level`.\n   * NOTE: This is the zstd level, COMPRESSION_LEVEL_DEFAULT and such aren't\n   *       supported, since zstd supports negative compression levels.\n   */\n  explicit Options(int level);\n\n  /**\n   * Set the compression `param` to `value`.\n   * See the zstd documentation for ZSTD_CCtx_setParameter() for details, this\n   * is just a thin wrapper.\n   */\n  void set(ZSTD_cParameter param, unsigned value);\n\n  /**\n   * Set the maximum allowed window size during decompression.\n   * `maxWindowSize == 0` means don't set the maximum window size.\n   * zstd's current default limit is 2^27.\n   * See the zstd documentation for ZSTD_DCtx_setMaxWindowSize() for details.\n   */\n  void setMaxWindowSize(size_t maxWindowSize) {\n    maxWindowSize_ = maxWindowSize;\n  }\n\n  /// Get a reference to the ZSTD_CCtx_params.\n  ZSTD_CCtx_params const* params() const { return params_.get(); }\n\n  /// Get the compression level.\n  int level() const { return level_; }\n\n  /// Get the maximum window size.\n  size_t maxWindowSize() const { return maxWindowSize_; }\n\n private:\n  static void freeCCtxParams(ZSTD_CCtx_params* params);\n  std::unique_ptr<\n      ZSTD_CCtx_params,\n      folly::static_function_deleter<ZSTD_CCtx_params, &freeCCtxParams>>\n      params_;\n  size_t maxWindowSize_{0};\n  int level_;\n};\n\n/// Get a zstd Codec with the given options.\nstd::unique_ptr<Codec> getCodec(Options options);\n/// Get a zstd StreamCodec with the given options.\nstd::unique_ptr<StreamCodec> getStreamCodec(Options options);\n\n} // namespace zstd\n} // namespace compression\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/compression/elias_fano/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"bit_vector_coding\",\n    headers = [\"BitVectorCoding.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:likely\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/compression:instructions\",\n        \"//folly/compression:select64\",\n        \"//folly/compression/elias_fano:coding_detail\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:bits_class\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"coding_detail\",\n    headers = [\"CodingDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"elias_fano_coding\",\n    headers = [\"EliasFanoCoding.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:likely\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/compression:instructions\",\n        \"//folly/compression:select64\",\n        \"//folly/compression/elias_fano:coding_detail\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:bits\",\n    ],\n)\n"
  },
  {
    "path": "folly/compression/elias_fano/BitVectorCoding.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdlib>\n#include <limits>\n#include <type_traits>\n\n#include <glog/logging.h>\n\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/compression/Instructions.h>\n#include <folly/compression/Select64.h>\n#include <folly/compression/elias_fano/CodingDetail.h>\n#include <folly/lang/Bits.h>\n#include <folly/lang/BitsClass.h>\n\nnamespace folly {\nnamespace compression {\n\nstatic_assert(kIsLittleEndian, \"BitVectorCoding.h requires little endianness\");\n\ntemplate <class Pointer>\nstruct BitVectorCompressedListBase {\n  BitVectorCompressedListBase() = default;\n\n  template <class OtherPointer>\n  BitVectorCompressedListBase(\n      const BitVectorCompressedListBase<OtherPointer>& other)\n      : size(other.size),\n        upperBound(other.upperBound),\n        data(other.data),\n        bits(reinterpret_cast<Pointer>(other.bits)),\n        skipPointers(reinterpret_cast<Pointer>(other.skipPointers)),\n        forwardPointers(reinterpret_cast<Pointer>(other.forwardPointers)) {}\n\n  template <class T = Pointer>\n  auto free() -> decltype(::free(T(nullptr))) {\n    return ::free(data.data());\n  }\n\n  size_t size = 0;\n  size_t upperBound = 0;\n\n  folly::Range<Pointer> data;\n\n  Pointer bits = nullptr;\n  Pointer skipPointers = nullptr;\n  Pointer forwardPointers = nullptr;\n};\n\nusing BitVectorCompressedList = BitVectorCompressedListBase<const uint8_t*>;\nusing MutableBitVectorCompressedList = BitVectorCompressedListBase<uint8_t*>;\n\ntemplate <\n    class Value,\n    class SkipValue,\n    size_t kSkipQuantum = 0,\n    size_t kForwardQuantum = 0>\nstruct BitVectorEncoder {\n  static_assert(\n      std::is_integral<Value>::value && std::is_unsigned<Value>::value,\n      \"Value should be unsigned integral\");\n\n  using CompressedList = BitVectorCompressedList;\n  using MutableCompressedList = MutableBitVectorCompressedList;\n\n  using ValueType = Value;\n  using SkipValueType = SkipValue;\n  struct Layout;\n\n  static constexpr size_t skipQuantum = kSkipQuantum;\n  static constexpr size_t forwardQuantum = kForwardQuantum;\n\n  template <class RandomAccessIterator>\n  static MutableCompressedList encode(\n      RandomAccessIterator begin, RandomAccessIterator end) {\n    if (begin == end) {\n      return MutableCompressedList();\n    }\n    BitVectorEncoder encoder(size_t(end - begin), *(end - 1));\n    for (; begin != end; ++begin) {\n      encoder.add(*begin);\n    }\n    return encoder.finish();\n  }\n\n  explicit BitVectorEncoder(const MutableCompressedList& result)\n      : bits_(result.bits),\n        skipPointers_(result.skipPointers),\n        forwardPointers_(result.forwardPointers),\n        result_(result) {\n    memset(result.data.data(), 0, result.data.size());\n  }\n\n  BitVectorEncoder(size_t size, ValueType upperBound)\n      : BitVectorEncoder(\n            Layout::fromUpperBoundAndSize(upperBound, size).allocList()) {}\n\n  void add(ValueType value) {\n    CHECK_LT(value, std::numeric_limits<ValueType>::max());\n    // Also works when lastValue_ == -1.\n    CHECK_GT(value + 1, lastValue_ + 1)\n        << \"BitVectorCoding only supports stricly monotone lists\";\n\n    auto block = bits_ + (value / 64) * sizeof(uint64_t);\n    size_t inner = value % 64;\n    folly::Bits<folly::Unaligned<uint64_t>>::set(\n        reinterpret_cast<folly::Unaligned<uint64_t>*>(block), inner);\n\n    if constexpr (skipQuantum != 0) {\n      size_t nextSkipPointerSize = value / skipQuantum;\n      while (skipPointersSize_ < nextSkipPointerSize) {\n        auto pos = skipPointersSize_++;\n        folly::storeUnaligned<SkipValueType>(\n            skipPointers_ + pos * sizeof(SkipValueType),\n            static_cast<SkipValueType>(size_));\n      }\n    }\n\n    if constexpr (forwardQuantum != 0) {\n      if (size_ != 0 && (size_ % forwardQuantum == 0)) {\n        const auto pos = size_ / forwardQuantum - 1;\n        folly::storeUnaligned<SkipValueType>(\n            forwardPointers_ + pos * sizeof(SkipValueType),\n            static_cast<SkipValueType>(value));\n      }\n    }\n\n    lastValue_ = value;\n    ++size_;\n  }\n\n  const MutableCompressedList& finish() const {\n    CHECK_EQ(size_, result_.size);\n    // TODO(ott): Relax this assumption.\n    CHECK_EQ(result_.upperBound, lastValue_);\n    return result_;\n  }\n\n private:\n  uint8_t* const bits_ = nullptr;\n  uint8_t* const skipPointers_ = nullptr;\n  uint8_t* const forwardPointers_ = nullptr;\n\n  ValueType lastValue_ = -1;\n  size_t size_ = 0;\n  size_t skipPointersSize_ = 0;\n\n  MutableCompressedList result_;\n};\n\ntemplate <\n    class Value,\n    class SkipValue,\n    size_t kSkipQuantum,\n    size_t kForwardQuantum>\nstruct BitVectorEncoder<Value, SkipValue, kSkipQuantum, kForwardQuantum>::\n    Layout {\n  static Layout fromUpperBoundAndSize(size_t upperBound, size_t size) {\n    Layout layout;\n    layout.size = size;\n    layout.upperBound = upperBound;\n\n    size_t bitVectorSizeInBytes = (upperBound / 8) + 1;\n    layout.bits = bitVectorSizeInBytes;\n\n    if constexpr (skipQuantum != 0) {\n      size_t numSkipPointers = upperBound / skipQuantum;\n      layout.skipPointers = numSkipPointers * sizeof(SkipValueType);\n    }\n    if constexpr (forwardQuantum != 0) {\n      size_t numForwardPointers = size / forwardQuantum;\n      layout.forwardPointers = numForwardPointers * sizeof(SkipValueType);\n    }\n\n    CHECK_LT(size, std::numeric_limits<SkipValueType>::max());\n\n    return layout;\n  }\n\n  size_t bytes() const { return bits + skipPointers + forwardPointers; }\n\n  template <class Range>\n  BitVectorCompressedListBase<typename Range::iterator> openList(\n      Range& buf) const {\n    BitVectorCompressedListBase<typename Range::iterator> result;\n    result.size = size;\n    result.upperBound = upperBound;\n    result.data = buf.subpiece(0, bytes());\n    auto advance = [&](size_t n) {\n      auto begin = buf.data();\n      buf.advance(n);\n      return begin;\n    };\n\n    result.bits = advance(bits);\n    result.skipPointers = advance(skipPointers);\n    result.forwardPointers = advance(forwardPointers);\n    CHECK_EQ(buf.data() - result.data.data(), bytes());\n\n    return result;\n  }\n\n  MutableCompressedList allocList() const {\n    uint8_t* buf = nullptr;\n    if (size > 0) {\n      buf = static_cast<uint8_t*>(malloc(bytes() + 7));\n    }\n    folly::MutableByteRange bufRange(buf, bytes());\n    return openList(bufRange);\n  }\n\n  size_t size = 0;\n  size_t upperBound = 0;\n\n  // Sizes in bytes.\n  size_t bits = 0;\n  size_t skipPointers = 0;\n  size_t forwardPointers = 0;\n};\n\ntemplate <\n    class Encoder,\n    class Instructions = instructions::Default,\n    bool kUnchecked = false>\nclass BitVectorReader\n    : detail::ForwardPointers<Encoder::forwardQuantum>,\n      detail::SkipPointers<Encoder::skipQuantum> {\n public:\n  using EncoderType = Encoder;\n  using ValueType = typename Encoder::ValueType;\n  // A bitvector can only be as large as its largest value.\n  using SizeType = typename Encoder::ValueType;\n  using SkipValueType = typename Encoder::SkipValueType;\n\n  explicit BitVectorReader(const typename Encoder::CompressedList& list)\n      : detail::ForwardPointers<Encoder::forwardQuantum>(list.forwardPointers),\n        detail::SkipPointers<Encoder::skipQuantum>(list.skipPointers),\n        bits_(list.bits),\n        size_(list.size),\n        upperBound_(kUnchecked || list.size == 0 ? 0 : list.upperBound) {\n    reset();\n  }\n\n  void reset() {\n    // Pretend the bitvector is prefixed by a block of zeroes.\n    block_ = 0;\n    position_ = static_cast<SizeType>(-1);\n    outer_ = static_cast<SizeType>(-sizeof(uint64_t));\n    value_ = kInvalidValue;\n  }\n\n  bool next() {\n    if (!kUnchecked && FOLLY_UNLIKELY(position() + 1 >= size_)) {\n      return setDone();\n    }\n\n    while (block_ == 0) {\n      outer_ += sizeof(uint64_t);\n      block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_);\n    }\n\n    ++position_;\n    auto inner = Instructions::ctz(block_);\n    block_ = Instructions::blsr(block_);\n\n    return setValue(inner);\n  }\n\n  bool skip(SizeType n) {\n    if (n == 0) {\n      return valid();\n    }\n\n    if (!kUnchecked && position() + n >= size_) {\n      return setDone();\n    }\n    // Small skip optimization.\n    if (FOLLY_LIKELY(n < kLinearScanThreshold)) {\n      for (size_t i = 0; i < n; ++i) {\n        next();\n      }\n      return true;\n    }\n\n    position_ += n;\n\n    // Use forward pointer.\n    if constexpr (Encoder::forwardQuantum > 0) {\n      if (n > Encoder::forwardQuantum) {\n        const size_t steps = position_ / Encoder::forwardQuantum;\n        const size_t dest = folly::loadUnaligned<SkipValueType>(\n            this->forwardPointers_ + (steps - 1) * sizeof(SkipValueType));\n\n        reposition(dest);\n        n = position_ + 1 - steps * Encoder::forwardQuantum;\n      }\n    }\n\n    size_t cnt;\n    // Find necessary block.\n    while ((cnt = Instructions::popcount(block_)) < n) {\n      n -= cnt;\n      outer_ += sizeof(uint64_t);\n      block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_);\n    }\n\n    // Skip to the n-th one in the block.\n    DCHECK_GT(n, 0);\n    auto inner = select64<Instructions>(block_, n - 1);\n    block_ &= (uint64_t(-1) << inner) << 1;\n\n    return setValue(inner);\n  }\n\n  template <bool kCanBeAtValue = true>\n  bool skipTo(ValueType v) {\n    // Also works when value_ == kInvalidValue.\n    if (v != kInvalidValue) {\n      DCHECK_GE(v + 1, value_ + 1);\n    }\n\n    if (!kUnchecked && v > upperBound_) {\n      return setDone();\n    } else if (kCanBeAtValue && v == value_) {\n      return true;\n    }\n\n    // Small skip optimization.\n    if (v - value_ < kLinearScanThreshold) {\n      do {\n        next();\n      } while (value() < v);\n\n      return true;\n    }\n\n    if constexpr (Encoder::skipQuantum > 0) {\n      if (v - value_ > Encoder::skipQuantum) {\n        size_t q = v / Encoder::skipQuantum;\n        auto skipPointer = folly::loadUnaligned<SkipValueType>(\n            this->skipPointers_ + (q - 1) * sizeof(SkipValueType));\n        position_ = static_cast<SizeType>(skipPointer) - 1;\n\n        reposition(q * Encoder::skipQuantum);\n      }\n    }\n\n    // Find the value.\n    size_t outer = v / 64 * sizeof(uint64_t);\n\n    while (outer_ != outer) {\n      position_ += Instructions::popcount(block_);\n      outer_ += sizeof(uint64_t);\n      block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_);\n      DCHECK_LE(outer_, outer);\n    }\n\n    uint64_t mask = ~((uint64_t(1) << (v % 64)) - 1);\n    position_ += Instructions::popcount(block_ & ~mask) + 1;\n    block_ &= mask;\n\n    while (block_ == 0) {\n      outer_ += sizeof(uint64_t);\n      block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_);\n    }\n\n    auto inner = Instructions::ctz(block_);\n    block_ = Instructions::blsr(block_);\n\n    setValue(inner);\n    return true;\n  }\n\n  SizeType size() const { return size_; }\n\n  bool valid() const {\n    return position() < size(); // Also checks that position() != -1.\n  }\n\n  SizeType position() const { return position_; }\n  ValueType value() const {\n    DCHECK(valid());\n    return value_;\n  }\n\n  bool jump(SizeType n) {\n    reset();\n    return skip(n + 1);\n  }\n\n  bool jumpTo(ValueType v) {\n    reset();\n    return skipTo(v);\n  }\n\n  bool setDone() {\n    value_ = kInvalidValue;\n    position_ = size_;\n    return false;\n  }\n\n private:\n  // Must hold kInvalidValue + 1 == 0.\n  constexpr static ValueType kInvalidValue = -1;\n\n  bool setValue(size_t inner) {\n    value_ = static_cast<ValueType>(8 * outer_ + inner);\n    return true;\n  }\n\n  void reposition(size_t dest) {\n    outer_ = dest / 64 * 8;\n    // We maintain the invariant that outer_ is divisible by 8.\n    block_ = folly::loadUnaligned<uint64_t>(bits_ + outer_);\n    block_ &= ~((uint64_t(1) << (dest % 64)) - 1);\n  }\n\n  constexpr static size_t kLinearScanThreshold = 4;\n\n  const uint8_t* const bits_;\n  uint64_t block_;\n  SizeType outer_;\n  SizeType position_;\n  ValueType value_;\n\n  const SizeType size_;\n  const ValueType upperBound_;\n};\n\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/elias_fano/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME bit_vector_coding\n  HEADERS\n    BitVectorCoding.h\n  EXPORTED_DEPS\n    folly_compression_elias_fano_coding_detail\n    folly_compression_instructions\n    folly_compression_select64\n    folly_lang_bits\n    folly_lang_bits_class\n    folly_likely\n    folly_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME coding_detail\n  HEADERS\n    CodingDetail.h\n)\n\nfolly_add_library(\n  NAME elias_fano_coding\n  HEADERS\n    EliasFanoCoding.h\n  EXPORTED_DEPS\n    folly_compression_elias_fano_coding_detail\n    folly_compression_instructions\n    folly_compression_select64\n    folly_lang_assume\n    folly_lang_bits\n    folly_likely\n    folly_portability\n    folly_range\n)\n"
  },
  {
    "path": "folly/compression/elias_fano/CodingDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Shared utils for BitVectorCoding.h and EliasFanoCoding.h.\n */\n\n#pragma once\n\n#include <stddef.h>\n\nnamespace folly {\nnamespace compression {\nnamespace detail {\n\n/**\n * Helpers to store pointers to forward and skip pointer arrays only\n * if they are used, that is, the quantum is nonzero. If it is 0, the\n * class is empty, and the member is static to keep the syntax valid,\n * thus it will take no space in a derived class thanks to empty base\n * class optimization.\n */\ntemplate <size_t>\nclass ForwardPointers {\n protected:\n  explicit ForwardPointers(const unsigned char* ptr) : forwardPointers_(ptr) {}\n  const unsigned char* const forwardPointers_;\n};\ntemplate <>\nclass ForwardPointers<0> {\n protected:\n  explicit ForwardPointers(const unsigned char*) {}\n  constexpr static const unsigned char* const forwardPointers_{};\n};\n\ntemplate <size_t>\nclass SkipPointers {\n protected:\n  explicit SkipPointers(const unsigned char* ptr) : skipPointers_(ptr) {}\n  const unsigned char* const skipPointers_;\n};\ntemplate <>\nclass SkipPointers<0> {\n protected:\n  explicit SkipPointers(const unsigned char*) {}\n  constexpr static const unsigned char* const skipPointers_{};\n};\n} // namespace detail\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/elias_fano/EliasFanoCoding.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Based on the paper by Sebastiano Vigna,\n * \"Quasi-succinct indices\" (arxiv:1206.4300).\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cstdlib>\n#include <cstring>\n#include <limits>\n#include <new>\n#include <type_traits>\n\n#include <glog/logging.h>\n\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/compression/Instructions.h>\n#include <folly/compression/Select64.h>\n#include <folly/compression/elias_fano/CodingDetail.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\nnamespace compression {\n\nstatic_assert(kIsLittleEndian, \"EliasFanoCoding.h requires little endianness\");\n\nconstexpr size_t kCacheLineSize = 64;\n\ntemplate <class Pointer>\nstruct EliasFanoCompressedListBase {\n  EliasFanoCompressedListBase() = default;\n\n  template <class OtherPointer>\n  EliasFanoCompressedListBase(\n      const EliasFanoCompressedListBase<OtherPointer>& other)\n      : size(other.size),\n        numLowerBits(other.numLowerBits),\n        upperSizeBytes(other.upperSizeBytes),\n        data(other.data),\n        skipPointers(reinterpret_cast<Pointer>(other.skipPointers)),\n        forwardPointers(reinterpret_cast<Pointer>(other.forwardPointers)),\n        lower(reinterpret_cast<Pointer>(other.lower)),\n        upper(reinterpret_cast<Pointer>(other.upper)) {}\n\n  template <class T = Pointer>\n  auto free() -> decltype(::free(T(nullptr))) {\n    return ::free(data.data());\n  }\n\n  size_t size = 0;\n  uint8_t numLowerBits = 0;\n  size_t upperSizeBytes = 0;\n\n  // WARNING: EliasFanoCompressedList has no ownership of data. The 7 bytes\n  // following the last byte should be readable if kUpperFirst = false, 8 bytes\n  // otherwise.\n  Range<Pointer> data;\n\n  Pointer skipPointers = nullptr;\n  Pointer forwardPointers = nullptr;\n  Pointer lower = nullptr;\n  Pointer upper = nullptr;\n};\n\nusing EliasFanoCompressedList = EliasFanoCompressedListBase<const uint8_t*>;\nusing MutableEliasFanoCompressedList = EliasFanoCompressedListBase<uint8_t*>;\n\ntemplate <\n    class Value,\n    // SkipValue must be wide enough to be able to represent the list length.\n    class SkipValue = uint64_t,\n    size_t kSkipQuantum = 0, // 0 = disabled\n    size_t kForwardQuantum = 0, // 0 = disabled\n    bool kUpperFirst = false>\nstruct EliasFanoEncoder {\n  static_assert(\n      std::is_integral_v<Value> && std::is_unsigned_v<Value>,\n      \"Value should be unsigned integral\");\n\n  using CompressedList = EliasFanoCompressedList;\n  using MutableCompressedList = MutableEliasFanoCompressedList;\n\n  using ValueType = Value;\n  using SkipValueType = SkipValue;\n  struct Layout;\n\n  static constexpr size_t skipQuantum = kSkipQuantum;\n  static constexpr size_t forwardQuantum = kForwardQuantum;\n\n  static uint8_t defaultNumLowerBits(size_t upperBound, size_t size) {\n    if (FOLLY_UNLIKELY(size == 0 || upperBound < size)) {\n      return 0;\n    }\n    // Result that should be returned is \"floor(log(upperBound / size))\".\n    // In order to avoid expensive division, we rely on\n    // \"floor(a) - floor(b) - 1 <= floor(a - b) <= floor(a) - floor(b)\".\n    // Assuming \"candidate = floor(log(upperBound)) - floor(log(upperBound))\",\n    // then result is either \"candidate - 1\" or \"candidate\".\n    auto candidate = findLastSet(upperBound) - findLastSet(size);\n    // NOTE: As size != 0, \"candidate\" is always < 64.\n    return (size > (upperBound >> candidate)) ? candidate - 1 : candidate;\n  }\n\n  // Requires: input range (begin, end) is sorted (encoding\n  // crashes if it's not).\n  // WARNING: encode() mallocates EliasFanoCompressedList::data. As\n  // EliasFanoCompressedList has no ownership of it, you need to call\n  // free() explicitly.\n  template <class RandomAccessIterator>\n  static MutableCompressedList encode(\n      RandomAccessIterator begin, RandomAccessIterator end) {\n    if (begin == end) {\n      return MutableCompressedList();\n    }\n    EliasFanoEncoder encoder(size_t(end - begin), *(end - 1));\n    for (; begin != end; ++begin) {\n      encoder.add(*begin);\n    }\n    return encoder.finish();\n  }\n\n  explicit EliasFanoEncoder(const MutableCompressedList& result)\n      : lower_(result.lower),\n        upper_(result.upper),\n        skipPointers_(reinterpret_cast<SkipValueType*>(result.skipPointers)),\n        forwardPointers_(\n            reinterpret_cast<SkipValueType*>(result.forwardPointers)),\n        result_(result) {\n    std::fill(result.data.begin(), result.data.end(), '\\0');\n  }\n\n  EliasFanoEncoder(size_t size, ValueType upperBound)\n      : EliasFanoEncoder(\n            Layout::fromUpperBoundAndSize(upperBound, size).allocList()) {}\n\n  void add(ValueType value) {\n    CHECK_GE(value, lastValue_);\n    CHECK_LT(size_, result_.size)\n        << \"add() called more times than the size specified in construction\";\n\n    const auto numLowerBits = result_.numLowerBits;\n    const ValueType upperBits = value >> numLowerBits;\n\n    // Upper sequence consists of upperBits 0-bits and (size_ + 1) 1-bits.\n    const size_t pos = upperBits + size_;\n    upper_[pos / 8] |= 1U << (pos % 8);\n    // Append numLowerBits bits to lower sequence.\n    if (numLowerBits != 0) {\n      const ValueType lowerBits = value & ((ValueType(1) << numLowerBits) - 1);\n      writeBits56(lower_, size_ * numLowerBits, numLowerBits, lowerBits);\n    }\n\n    fillSkipPointersUpTo(upperBits);\n\n    if constexpr (forwardQuantum != 0) {\n      if ((size_ + 1) % forwardQuantum == 0) {\n        DCHECK_LE(upperBits, std::numeric_limits<SkipValueType>::max());\n        const auto k = size_ / forwardQuantum;\n        // Store the number of preceding 0-bits.\n        forwardPointers_[k] = upperBits;\n      }\n    }\n\n    lastValue_ = value;\n    ++size_;\n  }\n\n  const MutableCompressedList& finish() {\n    CHECK_EQ(size_, result_.size)\n        << \"Number of add()s must be equal to the size specified in construction\";\n    const ValueType upperBitsUniverse =\n        (8 * result_.upperSizeBytes - result_.size);\n    // Populate skip pointers up to the universe upper bound (inclusive).\n    fillSkipPointersUpTo(upperBitsUniverse);\n    return result_;\n  }\n\n private:\n  void fillSkipPointersUpTo(ValueType fillBoundary) {\n    if constexpr (skipQuantum != 0) {\n      DCHECK_LE(size_, std::numeric_limits<SkipValueType>::max());\n      // The first skip pointer is omitted (it would always be 0), so the\n      // calculation is shifted by 1.\n      while ((skipPointersSize_ + 1) * skipQuantum <= fillBoundary) {\n        // Store the number of preceding 1-bits.\n        skipPointers_[skipPointersSize_++] = static_cast<SkipValueType>(size_);\n      }\n    }\n  }\n  // Writes value (with len up to 56 bits) to data starting at pos-th bit.\n  static void writeBits56(\n      unsigned char* data, size_t pos, uint8_t len, uint64_t value) {\n    DCHECK_LE(uint32_t(len), 56);\n    DCHECK_EQ(0, value & ~((uint64_t(1) << len) - 1));\n    unsigned char* const ptr = data + (pos / 8);\n    uint64_t ptrv = loadUnaligned<uint64_t>(ptr);\n    ptrv |= value << (pos % 8);\n    storeUnaligned<uint64_t>(ptr, ptrv);\n  }\n\n  unsigned char* lower_ = nullptr;\n  unsigned char* upper_ = nullptr;\n  SkipValueType* skipPointers_ = nullptr;\n  SkipValueType* forwardPointers_ = nullptr;\n\n  ValueType lastValue_ = 0;\n  size_t size_ = 0;\n  size_t skipPointersSize_ = 0;\n\n  MutableCompressedList result_;\n};\n\ntemplate <\n    class Value,\n    class SkipValue,\n    size_t kSkipQuantum,\n    size_t kForwardQuantum,\n    bool kUpperFirst>\nstruct EliasFanoEncoder<\n    Value,\n    SkipValue,\n    kSkipQuantum,\n    kForwardQuantum,\n    kUpperFirst>::Layout {\n  static Layout fromUpperBoundAndSize(size_t upperBound, size_t size) {\n    // numLowerBits can be at most 56 because of detail::writeBits56.\n    const uint8_t numLowerBits =\n        std::min(defaultNumLowerBits(upperBound, size), uint8_t(56));\n    // *** Upper bits.\n    // Upper bits are stored using unary delta encoding.\n    // For example, (3 5 5 9) will be encoded as 1000011001000_2.\n    const size_t upperSizeBits =\n        (upperBound >> numLowerBits) + // Number of 0-bits to be stored.\n        size; // 1-bits.\n    const size_t upper = (upperSizeBits + 7) / 8;\n\n    // *** Validity checks.\n    // Shift by numLowerBits must be valid.\n    CHECK_LT(static_cast<int>(numLowerBits), 8 * sizeof(Value));\n    CHECK_LE(\n        upperBound >> numLowerBits, std::numeric_limits<SkipValueType>::max());\n\n    return fromInternalSizes(numLowerBits, upper, size);\n  }\n\n  static Layout fromInternalSizes(\n      uint8_t numLowerBits, size_t upper, size_t size) {\n    Layout layout;\n    layout.size = size;\n    layout.numLowerBits = numLowerBits;\n\n    layout.lower = (numLowerBits * size + 7) / 8;\n    layout.upper = upper;\n\n    // *** Skip pointers.\n    // Store (1-indexed) position of every skipQuantum-th\n    // 0-bit in upper bits sequence.\n    if constexpr (skipQuantum != 0) {\n      // 8 * upper is used here instead of upperSizeBits, as that is\n      // more serialization-friendly way (upperSizeBits doesn't need\n      // to be known by this function, unlike upper).\n\n      size_t numSkipPointers = (8 * upper - size) / skipQuantum;\n      layout.skipPointers = numSkipPointers * sizeof(SkipValueType);\n    }\n\n    // *** Forward pointers.\n    // Store (1-indexed) position of every forwardQuantum-th\n    // 1-bit in upper bits sequence.\n    if constexpr (forwardQuantum != 0) {\n      size_t numForwardPointers = size / forwardQuantum;\n      layout.forwardPointers = numForwardPointers * sizeof(SkipValueType);\n    }\n\n    return layout;\n  }\n\n  size_t bytes() const {\n    return lower + upper + skipPointers + forwardPointers;\n  }\n\n  template <class Range>\n  EliasFanoCompressedListBase<typename Range::iterator> openList(\n      Range& buf) const {\n    EliasFanoCompressedListBase<typename Range::iterator> result;\n    result.size = size;\n    result.numLowerBits = numLowerBits;\n    result.upperSizeBytes = upper;\n    result.data = buf.subpiece(0, bytes());\n\n    auto advance = [&](size_t n) {\n      auto begin = buf.data();\n      buf.advance(n);\n      return begin;\n    };\n\n    result.skipPointers = advance(skipPointers);\n    result.forwardPointers = advance(forwardPointers);\n    if constexpr (kUpperFirst) {\n      result.upper = advance(upper);\n      result.lower = advance(lower);\n    } else {\n      result.lower = advance(lower);\n      result.upper = advance(upper);\n    }\n\n    return result;\n  }\n\n  MutableCompressedList allocList() const {\n    uint8_t* buf = nullptr;\n    // WARNING: Current read/write logic assumes that the 7 bytes\n    // following the upper bytes and the 8 bytes following the lower bytes\n    // sequences are readable (stored value doesn't matter and won't be\n    // changed), so we allocate additional 8 bytes, but do not include them in\n    // size of returned value.\n    if (size > 0) {\n      buf = static_cast<uint8_t*>(malloc(bytes() + 8));\n    }\n    MutableByteRange bufRange(buf, bytes());\n    return openList(bufRange);\n  }\n\n  size_t size = 0;\n  uint8_t numLowerBits = 0;\n\n  // Sizes in bytes.\n  size_t lower = 0;\n  size_t upper = 0;\n  size_t skipPointers = 0;\n  size_t forwardPointers = 0;\n};\n\nnamespace detail {\n\n// Add a and b in the domain of T. This guarantees that if T is a sub-int type,\n// we cast away the promotion to int, so that unsigned overflow and underflow\n// work as expected.\ntemplate <class T, class U>\nFOLLY_ALWAYS_INLINE T addT(T a, U b) {\n  static_assert(std::is_unsigned_v<T>);\n  return static_cast<T>(a + static_cast<T>(b));\n}\n\ntemplate <\n    class Encoder,\n    class Instructions,\n    class SizeType,\n    bool kUnchecked = false>\nclass UpperBitsReader\n    : ForwardPointers<Encoder::forwardQuantum>,\n      SkipPointers<Encoder::skipQuantum> {\n  using SkipValueType = typename Encoder::SkipValueType;\n\n public:\n  using ValueType = typename Encoder::ValueType;\n\n  static_assert(\n      std::is_integral_v<SizeType> && std::is_unsigned_v<SizeType>,\n      \"SizeType should be unsigned integral\");\n  // Functions like `jump()` and `next()` rely on this being the predecessor\n  // of 0.  `valid()` also needs it to be the largest possible `SizeType`.\n  static constexpr SizeType kBeforeFirstPos = -1;\n\n  explicit UpperBitsReader(const typename Encoder::CompressedList& list)\n      : ForwardPointers<Encoder::forwardQuantum>(list.forwardPointers),\n        SkipPointers<Encoder::skipQuantum>(list.skipPointers),\n        start_(list.upper),\n        size_(list.size),\n        upperBound_(estimateUpperBound(list)) {\n    reset();\n  }\n\n  void reset() {\n    // Pretend the bitvector is prefixed by a block of zeroes.\n    block_ = 0;\n    position_ = kBeforeFirstPos;\n    outer_ = static_cast<OuterType>(-sizeof(block_t));\n    value_ = 0;\n  }\n\n  FOLLY_ALWAYS_INLINE SizeType position() const { return position_; }\n\n  FOLLY_ALWAYS_INLINE ValueType value() const { return value_; }\n\n  FOLLY_ALWAYS_INLINE bool valid() const {\n    // SizeType is unsigned, so this also ensures position() != kBeforeFirstPos\n    return position() < size();\n  }\n\n  FOLLY_ALWAYS_INLINE SizeType size() const { return size_; }\n\n  FOLLY_ALWAYS_INLINE bool previous() {\n    if (!kUnchecked && FOLLY_UNLIKELY(position() == 0)) {\n      return false;\n    }\n\n    size_t inner;\n    block_t block;\n    DCHECK_GE(outer_, 0);\n    getPreviousInfo(block, inner, outer_); // Updates outer_.\n    block_ = loadUnaligned<block_t>(start_ + outer_);\n    block_ ^= block;\n    --position_;\n    return setValue(inner);\n  }\n\n  FOLLY_ALWAYS_INLINE bool next() {\n    if (!kUnchecked && FOLLY_UNLIKELY(addT(position(), 1) >= size())) {\n      return setDone();\n    }\n\n    // Skip to the first non-zero block.\n    while (FOLLY_UNLIKELY(block_ == 0)) {\n      outer_ += sizeof(block_t);\n      block_ = loadUnaligned<block_t>(start_ + outer_);\n    }\n\n    ++position_;\n    size_t inner = Instructions::ctz(block_);\n    block_ = Instructions::blsr(block_);\n\n    return setValue(inner);\n  }\n\n  FOLLY_ALWAYS_INLINE bool skip(SizeType n) {\n    DCHECK_GT(n, 0);\n    if (!kUnchecked && FOLLY_UNLIKELY(addT(position_, n) >= size())) {\n      return setDone();\n    }\n\n    position_ += n; // n 1-bits will be read.\n\n    // Use forward pointer.\n    if constexpr (Encoder::forwardQuantum > 0) {\n      if (FOLLY_UNLIKELY(n > Encoder::forwardQuantum)) {\n        const size_t steps = position_ / Encoder::forwardQuantum;\n        const size_t dest = loadUnaligned<SkipValueType>(\n            this->forwardPointers_ + (steps - 1) * sizeof(SkipValueType));\n\n        reposition(dest + steps * Encoder::forwardQuantum);\n        n = position_ + 1 - steps * Encoder::forwardQuantum; // n is > 0.\n      }\n    }\n\n    size_t cnt;\n    // Find necessary block.\n    while ((cnt = Instructions::popcount(block_)) < n) {\n      n -= cnt;\n      outer_ += sizeof(block_t);\n      block_ = loadUnaligned<block_t>(start_ + outer_);\n    }\n\n    // Skip to the n-th one in the block.\n    DCHECK_GT(n, 0);\n    size_t inner = select64<Instructions>(block_, n - 1);\n    block_ &= (block_t(-1) << inner) << 1;\n\n    return setValue(inner);\n  }\n\n  // Skip to the first element that is >= v and located *after* the current\n  // one (so even if current value equals v, position will be increased by 1).\n  FOLLY_ALWAYS_INLINE bool skipToNext(ValueType v) {\n    DCHECK_GE(v, value_);\n    if (!kUnchecked && FOLLY_UNLIKELY(v > upperBound_)) {\n      return setDone();\n    }\n\n    // Use skip pointer.\n    if constexpr (Encoder::skipQuantum > 0) {\n      // NOTE: The addition can overflow here, but that means value_ is within\n      // skipQuantum_ distance from the maximum representable value, and thus\n      // the last value, so the comparison is still correct.\n      if (FOLLY_UNLIKELY(v >= addT(value_, Encoder::skipQuantum))) {\n        const size_t steps = v / Encoder::skipQuantum;\n        const size_t dest = loadUnaligned<SkipValueType>(\n            this->skipPointers_ + (steps - 1) * sizeof(SkipValueType));\n\n        DCHECK_LE(dest, size());\n        if (!kUnchecked && FOLLY_UNLIKELY(dest == size())) {\n          return setDone();\n        }\n\n        reposition(dest + Encoder::skipQuantum * steps);\n        position_ = dest - 1;\n\n        // Correct value_ will be set during the next() call at the end.\n\n        // NOTE: Corresponding block of lower bits sequence may be\n        // prefetched here (via __builtin_prefetch), but experiments\n        // didn't show any significant improvements.\n      }\n    }\n\n    // Skip by blocks.\n    size_t cnt;\n    // outer_ and position_ rely on negative sentinel values. We enforce the\n    // overflown bits are dropped by explicitly casting the final value to\n    // SizeType first, followed by a potential implicit cast to size_t.\n    size_t skip = static_cast<SizeType>(v - (8 * outer_ - position_ - 1));\n\n    constexpr size_t kBitsPerBlock = 8 * sizeof(block_t);\n    while ((cnt = Instructions::popcount(~block_)) < skip) {\n      skip -= cnt;\n      position_ += kBitsPerBlock - cnt;\n      outer_ += sizeof(block_t);\n      DCHECK_LT(outer_, (static_cast<size_t>(upperBound_) + size() + 7) / 8);\n      block_ = loadUnaligned<block_t>(start_ + outer_);\n    }\n\n    if (FOLLY_LIKELY(skip)) {\n      auto inner = select64<Instructions>(~block_, skip - 1);\n      position_ += inner - skip + 1;\n      block_ &= block_t(-1) << inner;\n    }\n\n    DCHECK_LT(addT(position(), 1), addT(size(), 1));\n    return next();\n  }\n\n  /**\n   * Try to prepare to skip to value. This is a constant-time operation that\n   * will attempt to prefetch memory required for a subsequent skipTo(value)\n   * call if the value to skip to is within this list.\n   *\n   * Returns:\n   *   {true, position of the reader} if the skip is valid,\n   *   {false, size()} otherwise.\n   */\n  FOLLY_ALWAYS_INLINE std::pair<bool, SizeType> prepareSkipTo(\n      ValueType v) const {\n    if (!kUnchecked && FOLLY_UNLIKELY(v > upperBound_)) {\n      return std::make_pair(false, size());\n    }\n    auto position = position_;\n\n    if constexpr (Encoder::skipQuantum > 0) {\n      if (v >= addT(value_, Encoder::skipQuantum)) {\n        auto outer = outer_;\n        const size_t steps = v / Encoder::skipQuantum;\n        const size_t dest = loadUnaligned<SkipValueType>(\n            this->skipPointers_ + (steps - 1) * sizeof(SkipValueType));\n\n        DCHECK_LE(dest, size());\n        if (!kUnchecked && FOLLY_UNLIKELY(dest == size())) {\n          return std::make_pair(false, size());\n        }\n\n        position = dest - 1;\n        outer = (dest + Encoder::skipQuantum * steps) / 8;\n\n        // Prefetch up to the beginning of where we linear search. After that,\n        // hardware prefetching will outperform our own. In addition, this\n        // simplifies calculating what to prefetch as we don't have to calculate\n        // the entire destination address. Two cache lines are prefetched\n        // because this results in fewer cycles used (based on practical\n        // results) than one. However, three cache lines does not have any\n        // additional effect.\n        const auto addr = start_ + outer;\n        __builtin_prefetch(addr);\n        __builtin_prefetch(addr + kCacheLineSize);\n      }\n    }\n\n    return std::make_pair(true, position);\n  }\n\n  FOLLY_ALWAYS_INLINE ValueType previousValue() const {\n    block_t block;\n    size_t inner;\n    OuterType outer;\n    getPreviousInfo(block, inner, outer);\n    return static_cast<ValueType>(8 * outer + inner - (position_ - 1));\n  }\n\n  // Returns true if we're at the beginning of the list, or previousValue() !=\n  // value().\n  FOLLY_ALWAYS_INLINE bool isAtBeginningOfRun() const {\n    DCHECK_NE(position(), kBeforeFirstPos);\n    if (position_ == 0) {\n      return true;\n    }\n    size_t bitPos = size_t(value_) + position_ - 1;\n    return (start_[bitPos / 8] & (1 << (bitPos % 8))) == 0;\n  }\n\n private:\n  using block_t = uint64_t;\n  // The size in bytes of the upper bits is limited by n + universe / 8,\n  // so a type that can hold either sizes or values is sufficient.\n  using OuterType = typename std::common_type_t<ValueType, SizeType>;\n\n  static ValueType estimateUpperBound(\n      const typename Encoder::CompressedList& list) {\n    size_t upperBound = 8 * list.upperSizeBytes - list.size;\n    // The bitvector is byte-aligned, so we may be overestimating the universe\n    // size. Make sure it fits in ValueType.\n    return static_cast<ValueType>(std::min<size_t>(\n        upperBound,\n        std::numeric_limits<ValueType>::max() >> list.numLowerBits));\n  }\n\n  FOLLY_ALWAYS_INLINE bool setValue(size_t inner) {\n    value_ = static_cast<ValueType>(8 * outer_ + inner - position_);\n    return true;\n  }\n\n  FOLLY_ALWAYS_INLINE bool setDone() {\n    position_ = size_;\n    return false;\n  }\n\n  // NOTE: dest is a position in the bit vector, use size_t as SizeType may\n  // not be sufficient here.\n  FOLLY_ALWAYS_INLINE void reposition(size_t dest) {\n    outer_ = dest / 8;\n    DCHECK_LT(outer_, (static_cast<size_t>(upperBound_) + size() + 7) / 8);\n    block_ = loadUnaligned<block_t>(start_ + outer_);\n    block_ &= ~((block_t(1) << (dest % 8)) - 1);\n  }\n\n  FOLLY_ALWAYS_INLINE void getPreviousInfo(\n      block_t& block, size_t& inner, OuterType& outer) const {\n    DCHECK_GT(position(), 0);\n    DCHECK_LT(position(), size());\n\n    outer = outer_;\n    block = loadUnaligned<block_t>(start_ + outer);\n    inner = size_t(value_) - 8 * outer_ + position_;\n    block &= (block_t(1) << inner) - 1;\n    while (FOLLY_UNLIKELY(block == 0)) {\n      DCHECK_GT(outer, 0);\n      outer -= std::min<OuterType>(sizeof(block_t), outer);\n      block = loadUnaligned<block_t>(start_ + outer);\n    }\n    inner = 8 * sizeof(block_t) - 1 - Instructions::clz(block);\n  }\n\n  const unsigned char* const start_;\n  const SizeType size_; // Size of the list.\n  const ValueType upperBound_; // Upper bound of values in this list.\n  block_t block_;\n  SizeType position_; // Index of current value (= #reads - 1).\n  OuterType outer_; // Outer offset: number of consumed bytes in upper.\n  ValueType value_;\n};\n\n} // namespace detail\n\n// If kUnchecked = true the caller must guarantee that all the operations return\n// valid elements, i.e., they would never return false if checked.\n//\n// If the list length is known to be representable with a type narrower than the\n// SkipValueType used in the format, the reader footprint can be reduced by\n// passing the type as SizeType.\ntemplate <\n    class Encoder,\n    class Instructions = instructions::Default,\n    bool kUnchecked = false,\n    class SizeT = typename Encoder::SkipValueType>\nclass EliasFanoReader {\n  using UpperBitsReader =\n      detail::UpperBitsReader<Encoder, Instructions, SizeT, kUnchecked>;\n\n public:\n  using EncoderType = Encoder;\n  using ValueType = typename Encoder::ValueType;\n  using SizeType = SizeT;\n\n  explicit EliasFanoReader(const typename Encoder::CompressedList& list)\n      : upper_(list),\n        lower_(list.lower),\n        value_(),\n        numLowerBits_(list.numLowerBits) {\n    DCHECK_LE(list.size, std::numeric_limits<SizeType>::max());\n    DCHECK(Instructions::supported());\n  }\n\n  void reset() { upper_.reset(); }\n\n  bool previous() {\n    if (FOLLY_LIKELY(upper_.previous())) {\n      return setValue(readCurrentValue());\n    }\n    reset();\n    return false;\n  }\n\n  bool next() {\n    if (FOLLY_LIKELY(upper_.next())) {\n      return setValue(readCurrentValue());\n    }\n    return false;\n  }\n\n  /**\n   * Advances by n elements. n = 0 is allowed and has no effect. Returns false\n   * if the end of the list is reached. position() + n must be representable by\n   * SizeType.\n   */\n  bool skip(SizeType n) {\n    if (n == 0) {\n      return valid();\n    }\n    if (!upper_.skip(n)) {\n      return false;\n    }\n    return setValue(readCurrentValue());\n  }\n\n  /**\n   * Skips to the first element >= value whose position is greater or equal to\n   * the current position.\n   * Requires that value >= value() (or that the reader is positioned before the\n   * first element). Returns false if no such element exists.\n   * If kCanBeAtValue is false, the requirement above becomes value > value().\n   */\n  template <bool kCanBeAtValue = true>\n  bool skipTo(ValueType value) {\n    if (valid()) {\n      if constexpr (kCanBeAtValue) {\n        DCHECK_GE(value, value_);\n        if (FOLLY_UNLIKELY(value == value_)) {\n          return true;\n        }\n      } else {\n        DCHECK_GT(value, value_);\n      }\n    }\n\n    ValueType upperValue = value >> numLowerBits_;\n\n    if (FOLLY_UNLIKELY(!upper_.skipToNext(upperValue))) {\n      return false;\n    }\n\n    do {\n      if (auto cur = readCurrentValue(); FOLLY_LIKELY(cur >= value)) {\n        return setValue(cur);\n      }\n    } while (FOLLY_LIKELY(upper_.next()));\n\n    return false;\n  }\n\n  /**\n   * Prepare to skip to `value` by prefetching appropriate memory in both the\n   * upper and lower bits.\n   */\n  template <bool kCanBeAtValue = true>\n  void prepareSkipTo(ValueType value) const {\n    if (valid()) {\n      if constexpr (kCanBeAtValue) {\n        DCHECK_GE(value, value_);\n        if (FOLLY_UNLIKELY(value == value_)) {\n          return;\n        }\n      } else {\n        DCHECK_GT(value, value_);\n      }\n    }\n\n    // Do minimal computation required to prefetch address used in\n    // `readLowerPart()`.\n    ValueType upperValue = value >> numLowerBits_;\n    const auto [valid, upperPosition] = upper_.prepareSkipTo(upperValue);\n    if (!valid) {\n      return;\n    }\n    const auto addr = lower_ + (upperPosition * numLowerBits_ / 8);\n    __builtin_prefetch(addr);\n    __builtin_prefetch(addr + kCacheLineSize);\n  }\n\n  /**\n   * Jumps to the element at position n. The reader can be in any state. Returns\n   * false if n >= size().\n   */\n  bool jump(SizeType n) {\n    // Also works if position() == -1, since `kBeforeFirstPos + 1 == 0`.\n    if (detail::addT(n, 1) < detail::addT(position(), 1)) {\n      reset();\n      n += 1; // Initial position is -1.\n    } else {\n      n -= position();\n    }\n    return skip(n);\n  }\n\n  /**\n   * Jumps to the first element >= value. The reader can be in any\n   * state. Returns false if no such element exists.\n   *\n   * If all the values in the list can be assumed distinct, setting\n   * assumeDistinct = true can enable some optimizations.\n   */\n  bool jumpTo(ValueType value, bool assumeDistinct = false) {\n    if (valid() && value == value_) {\n      if (assumeDistinct == true) {\n        return true;\n      }\n\n      // We might be in the middle of a run of equal values, reposition by\n      // iterating backwards to its first element.\n      auto valueLower = Instructions::bzhi(value_, numLowerBits_);\n      while (!upper_.isAtBeginningOfRun() &&\n             readLowerPart(position() - 1) == valueLower) {\n        upper_.previous();\n      }\n      return true;\n    }\n\n    // We need to reset if we're not in the initial state and the jump is\n    // backwards.\n    if (position() != UpperBitsReader::kBeforeFirstPos &&\n        (position() == size() || value < value_)) {\n      reset();\n    }\n    return skipTo(value);\n  }\n\n  ValueType previousValue() const {\n    DCHECK_GT(position(), 0);\n    DCHECK_LT(position(), size());\n    return readLowerPart(position() - 1) |\n        (upper_.previousValue() << numLowerBits_);\n  }\n\n  SizeType size() const { return upper_.size(); }\n\n  bool valid() const { return upper_.valid(); }\n\n  SizeType position() const { return upper_.position(); }\n\n  ValueType value() const {\n    DCHECK(valid());\n    return value_;\n  }\n\n private:\n  FOLLY_ALWAYS_INLINE bool setValue(ValueType value) {\n    DCHECK(valid());\n    value_ = value;\n    return true;\n  }\n\n  FOLLY_ALWAYS_INLINE ValueType readLowerPart(SizeType i) const {\n    DCHECK_LT(i, size());\n    const size_t pos = i * numLowerBits_;\n    const unsigned char* ptr = lower_ + (pos / 8);\n    const uint64_t ptrv = loadUnaligned<uint64_t>(ptr);\n    // This removes the branch in the fallback implementation of\n    // bextr. The condition is verified at encoding time.\n    assume(numLowerBits_ < sizeof(ValueType) * 8);\n    assume((pos % 8) + numLowerBits_ < 64);\n    return Instructions::bextr(ptrv, pos % 8, numLowerBits_);\n  }\n\n  FOLLY_ALWAYS_INLINE ValueType readCurrentValue() {\n    return readLowerPart(position()) | (upper_.value() << numLowerBits_);\n  }\n\n  // Ordering of fields is counter-intuitive but it optimizes the layout.\n  UpperBitsReader upper_;\n  const uint8_t* const lower_;\n  ValueType value_;\n  const uint8_t numLowerBits_;\n};\n\n// Trailing bytes that must be readable past the end of each bit sequence,\n// required by loadUnaligned<uint64_t> reads in the upper/lower accessors.\nconstexpr size_t kUpperTrailingBytes = 7;\nconstexpr size_t kLowerTrailingBytes = 8;\n\ntemplate <typename MutableCompressedList, bool kUpperFirst>\nMutableCompressedList copyCompressedList(const MutableCompressedList& src) {\n  size_t dataSize = src.data.size();\n  // The last sequence in the buffer needs trailing padding for safe reads.\n  // When kUpperFirst, lower is last and needs kLowerTrailingBytes; otherwise\n  // upper is last and needs kUpperTrailingBytes.\n  const size_t extra = kUpperFirst ? kLowerTrailingBytes : kUpperTrailingBytes;\n  uint8_t* buf = static_cast<uint8_t*>(std::malloc(dataSize + extra));\n  if (FOLLY_UNLIKELY(buf == nullptr)) {\n    throw std::bad_alloc();\n  }\n  std::memcpy(buf, src.data.data(), dataSize);\n\n  MutableCompressedList dst(src);\n  const auto origBase = src.data.data();\n  auto rebase = [&](const uint8_t* ptr) { return buf + (ptr - origBase); };\n  dst.data = folly::Range<uint8_t*>(buf, dataSize);\n  dst.skipPointers = rebase(src.skipPointers);\n  dst.forwardPointers = rebase(src.forwardPointers);\n  dst.lower = rebase(src.lower);\n  dst.upper = rebase(src.upper);\n  return dst;\n}\n\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/elias_fano/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"bitvector_test\",\n    srcs = [\"BitVectorCodingTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:portability\",\n        \"//folly/compression:select64\",\n        \"//folly/compression/elias_fano:bit_vector_coding\",\n        \"//folly/experimental/test:coding_test_utils\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"eliasfano_test\",\n    srcs = [\"EliasFanoCodingTest.cpp\"],\n    headers = [],\n    labels = [\"slow\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/compression:select64\",\n        \"//folly/compression/elias_fano:elias_fano_coding\",\n        \"//folly/experimental/test:coding_test_utils\",\n        \"//folly/init:init\",\n    ],\n)\n"
  },
  {
    "path": "folly/compression/elias_fano/test/BitVectorCodingTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <numeric>\n#include <random>\n#include <vector>\n\n#include <folly/Portability.h>\n#if FOLLY_X64\n#include <folly/Benchmark.h>\n#include <folly/compression/Select64.h>\n#include <folly/compression/elias_fano/BitVectorCoding.h>\n#include <folly/experimental/test/CodingTestUtils.h>\n#include <folly/init/Init.h>\n\nusing namespace folly::compression;\n\nclass BitVectorCodingTest : public ::testing::Test {\n public:\n  void doTestEmpty() {\n    using Encoder = BitVectorEncoder<uint32_t, size_t>;\n    using Reader = BitVectorReader<Encoder, instructions::Default>;\n    testEmpty<Reader, Encoder>();\n  }\n\n  template <size_t kSkipQuantum, size_t kForwardQuantum>\n  void doTestAll() {\n    using Encoder =\n        BitVectorEncoder<uint32_t, uint32_t, kSkipQuantum, kForwardQuantum>;\n    using Reader = BitVectorReader<Encoder>;\n    testAll<Reader, Encoder>(generateRandomList(100 * 1000, 10 * 1000 * 1000));\n    testAll<Reader, Encoder>(generateSeqList(1, 100000, 100));\n  }\n};\n\nTEST_F(BitVectorCodingTest, Empty) {\n  doTestEmpty();\n}\n\nTEST_F(BitVectorCodingTest, Simple) {\n  doTestAll<0, 0>();\n}\n\nTEST_F(BitVectorCodingTest, SkipPointers) {\n  doTestAll<128, 0>();\n}\n\nTEST_F(BitVectorCodingTest, ForwardPointers) {\n  doTestAll<0, 128>();\n}\n\nTEST_F(BitVectorCodingTest, SkipForwardPointers) {\n  doTestAll<128, 128>();\n}\n\nnamespace bm {\n\nusing Encoder = BitVectorEncoder<uint32_t, uint32_t, 128, 128>;\n\nstd::vector<uint64_t> data;\nstd::vector<size_t> order;\n\nstd::vector<uint64_t> encodeSmallData;\nstd::vector<uint64_t> encodeLargeData;\n\ntypename Encoder::MutableCompressedList list;\n\nvoid init() {\n  std::mt19937 gen;\n\n  data = generateRandomList(100 * 1000, 10 * 1000 * 1000, gen);\n  list = Encoder::encode(data.begin(), data.end());\n\n  order.resize(data.size());\n  std::iota(order.begin(), order.end(), size_t());\n  std::shuffle(order.begin(), order.end(), gen);\n\n  encodeSmallData = generateRandomList(10, 100 * 1000, gen);\n  encodeLargeData = generateRandomList(1000 * 1000, 100 * 1000 * 1000, gen);\n}\n\nvoid free() {\n  list.free();\n}\n\n} // namespace bm\n\nBENCHMARK(Next, iters) {\n  dispatchInstructions([&](auto instructions) {\n    bmNext<BitVectorReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, iters);\n  });\n}\n\nsize_t Skip_ForwardQ128(size_t iters, size_t logAvgSkip) {\n  dispatchInstructions([&](auto instructions) {\n    bmSkip<BitVectorReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, logAvgSkip, iters);\n  });\n  return iters;\n}\n\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 1, 0)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 2, 1)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 4_pm_1, 2)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 16_pm_4, 4)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 64_pm_16, 6)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 256_pm_64, 8)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 1024_pm_256, 10)\n\nBENCHMARK(Jump_ForwardQ128, iters) {\n  dispatchInstructions([&](auto instructions) {\n    bmJump<BitVectorReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, bm::order, iters);\n  });\n}\n\nBENCHMARK_DRAW_LINE();\n\nsize_t SkipTo_SkipQ128(size_t iters, size_t logAvgSkip) {\n  dispatchInstructions([&](auto instructions) {\n    bmSkipTo<BitVectorReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, logAvgSkip, iters);\n  });\n  return iters;\n}\n\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 1, 0)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 2, 1)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 4_pm_1, 2)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 16_pm_4, 4)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 64_pm_16, 6)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 256_pm_64, 8)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 1024_pm_256, 10)\n\nBENCHMARK(JumpTo_SkipQ128, iters) {\n  dispatchInstructions([&](auto instructions) {\n    bmJumpTo<BitVectorReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, bm::order, iters);\n  });\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Encode_10) {\n  auto list = bm::Encoder::encode(\n      bm::encodeSmallData.begin(), bm::encodeSmallData.end());\n  list.free();\n}\n\nBENCHMARK(Encode) {\n  auto list = bm::Encoder::encode(\n      bm::encodeLargeData.begin(), bm::encodeLargeData.end());\n  list.free();\n}\n\n#if 0\n// Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz (turbo on),\n// Using GCC 5 with --bm_slice_usec 100000.\nV1008 12:32:25.863286 101188 Instructions.h:161] Will use folly::compression::instructions::Haswell\n============================================================================\nfolly/experimental/test/BitVectorCodingTest.cpp relative  time/iter  iters/s\n============================================================================\nNext                                                         9.52ns  104.99M\nSkip_ForwardQ128(1)                                         13.90ns   71.96M\nSkip_ForwardQ128(2)                                         25.02ns   39.97M\nSkip_ForwardQ128(4_pm_1)                                    28.25ns   35.40M\nSkip_ForwardQ128(16_pm_4)                                   39.64ns   25.23M\nSkip_ForwardQ128(64_pm_16)                                 112.19ns    8.91M\nSkip_ForwardQ128(256_pm_64)                                137.75ns    7.26M\nSkip_ForwardQ128(1024_pm_256)                              131.56ns    7.60M\nJump_ForwardQ128                                           133.30ns    7.50M\n----------------------------------------------------------------------------\nSkipTo_SkipQ128(1)                                          13.30ns   75.16M\nSkipTo_SkipQ128(2)                                          13.81ns   72.40M\nSkipTo_SkipQ128(4_pm_1)                                     12.23ns   81.80M\nSkipTo_SkipQ128(16_pm_4)                                    13.72ns   72.89M\nSkipTo_SkipQ128(64_pm_16)                                   21.18ns   47.22M\nSkipTo_SkipQ128(256_pm_64)                                  20.15ns   49.63M\nSkipTo_SkipQ128(1024_pm_256)                                21.86ns   45.74M\nJumpTo_SkipQ128                                             23.10ns   43.30M\n----------------------------------------------------------------------------\nEncode_10                                                  344.50ns    2.90M\nEncode                                                      10.88ms    91.90\n============================================================================\n#endif\n\nint main(int argc, char** argv) {\n  testing::InitGoogleTest(&argc, argv);\n  folly::Init init(&argc, &argv);\n\n  auto ret = RUN_ALL_TESTS();\n  if (ret == 0 && FLAGS_benchmark) {\n    bm::init();\n    folly::runBenchmarks();\n    bm::free();\n  }\n\n  return ret;\n}\n#endif\n"
  },
  {
    "path": "folly/compression/elias_fano/test/EliasFanoCodingTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <limits>\n#include <numeric>\n#include <random>\n#include <vector>\n\n#include <folly/Benchmark.h>\n#include <folly/Random.h>\n#include <folly/compression/Select64.h>\n#include <folly/compression/elias_fano/EliasFanoCoding.h>\n#include <folly/experimental/test/CodingTestUtils.h>\n#include <folly/init/Init.h>\n\nusing namespace folly::compression;\n\nnamespace {\n\nuint8_t slowDefaultNumLowerBits(size_t upperBound, size_t size) {\n  if (size == 0 || upperBound < size) {\n    return 0;\n  }\n  // floor(log(upperBound / size));\n  return uint8_t(folly::findLastSet(upperBound / size) - 1);\n}\n\n} // namespace\n\nTEST(EliasFanoCoding, defaultNumLowerBits) {\n  // Verify that slowDefaultNumLowerBits and optimized\n  // Encoder::defaultNumLowerBits agree.\n  static constexpr size_t kNumIterations = 750;\n  auto compare = [](size_t upperBound, size_t size) {\n    using Encoder = EliasFanoEncoder<size_t>;\n    EXPECT_EQ(\n        int(slowDefaultNumLowerBits(upperBound, size)),\n        int(Encoder::defaultNumLowerBits(upperBound, size)))\n        << upperBound << \" \" << size;\n  };\n  auto batch = [&compare](size_t initialUpperBound) {\n    for (size_t upperBound = initialUpperBound, i = 0; i < kNumIterations;\n         ++i, --upperBound) {\n      // Test \"size\" values close to \"upperBound\".\n      for (size_t size = upperBound, j = 0; j < kNumIterations; ++j, --size) {\n        compare(upperBound, size);\n      }\n      // Sample \"size\" values between [0, upperBound].\n      for (size_t size = upperBound; size > 1 + upperBound / kNumIterations;\n           size -= 1 + upperBound / kNumIterations) {\n        compare(upperBound, size);\n      }\n      // Test \"size\" values close to 0.\n      for (size_t size = 0; size < kNumIterations; ++size) {\n        compare(upperBound, size);\n      }\n    }\n  };\n  batch(std::numeric_limits<size_t>::max());\n  batch(kNumIterations + 1312213123);\n  batch(kNumIterations);\n\n  std::mt19937 gen;\n  std::uniform_int_distribution<size_t> distribution;\n  for (size_t i = 0; i < kNumIterations; ++i) {\n    const auto a = distribution(gen);\n    const auto b = distribution(gen);\n    compare(std::max(a, b), std::min(a, b));\n  }\n}\n\nclass EliasFanoCodingTest : public ::testing::Test {\n public:\n  void doTestEmpty() {\n    using Encoder = EliasFanoEncoder<uint32_t, size_t>;\n    using Reader = EliasFanoReader<Encoder>;\n    testEmpty<Reader, Encoder>();\n  }\n\n  template <\n      size_t kSkipQuantum,\n      size_t kForwardQuantum,\n      typename ValueType,\n      typename SkipValueType,\n      bool kUpperFirst>\n  void doTest() {\n    using Encoder = EliasFanoEncoder<\n        ValueType,\n        SkipValueType,\n        kSkipQuantum,\n        kForwardQuantum,\n        kUpperFirst>;\n    using Reader = EliasFanoReader<Encoder, instructions::Default, false>;\n    testAll<Reader, Encoder>({0});\n    testAll<Reader, Encoder>(generateRandomList(100 * 1000, 10 * 1000 * 1000));\n    // Test a list with size multiple of kForwardQuantum and universe multiple\n    // of kSkipQuantum, to exercise corner cases in the construction of forward\n    // and skip lists.\n    testAll<Reader, Encoder>(generateRandomList(\n        std::max<size_t>(8 * kForwardQuantum, 1024),\n        std::max<size_t>(16 * kSkipQuantum, 2048)));\n    testAll<Reader, Encoder>(generateRandomList(\n        100 * 1000, 10 * 1000 * 1000, /* withDuplicates */ true));\n    testAll<Reader, Encoder>(generateSeqList(1, 100000, 100));\n    testAll<Reader, Encoder>({0, 1, std::numeric_limits<uint32_t>::max()});\n    // Test data with additional trailing 0s in the upperBits by extending\n    // the upper bound.\n    constexpr uint64_t minUpperBoundExtension = 2;\n    constexpr uint64_t maxUpperBoundExtension = 1024;\n    testAll<Reader, Encoder>(\n        generateRandomList(100 * 1000, 10 * 1000 * 1000),\n        folly::Random::rand32(minUpperBoundExtension, maxUpperBoundExtension));\n  }\n\n  template <size_t kSkipQuantum, size_t kForwardQuantum, typename ValueType>\n  void doTestAll() {\n    doTest<kSkipQuantum, kForwardQuantum, ValueType, uint32_t, true>();\n    doTest<kSkipQuantum, kForwardQuantum, ValueType, uint32_t, false>();\n    doTest<kSkipQuantum, kForwardQuantum, ValueType, uint64_t, true>();\n    doTest<kSkipQuantum, kForwardQuantum, ValueType, uint64_t, false>();\n  }\n\n  // Test lists where values and sizes are close to the numeric limits of the\n  // corresponding types, by using 16-bit types for everything.\n  template <size_t kSkipQuantum, size_t kForwardQuantum, bool kUpperFirst>\n  void doTestDense() {\n    using Encoder = EliasFanoEncoder<\n        uint16_t,\n        uint16_t,\n        kSkipQuantum,\n        kForwardQuantum,\n        kUpperFirst>;\n    using Reader = EliasFanoReader<Encoder, instructions::Default, false>;\n    constexpr size_t kMaxU16 = std::numeric_limits<uint16_t>::max();\n\n    // Max SizeType value is reserved.\n    testAll<Reader, Encoder>(generateSeqList(1, kMaxU16 - 1));\n    // Test various sizes close to the limit.\n    for (size_t i = 1; i <= 16; ++i) {\n      testAll<Reader, Encoder>(\n          generateRandomList(kMaxU16 - i, kMaxU16, /* withDuplicates */ true));\n    }\n  }\n\n  template <size_t kSkipQuantum, size_t kForwardQuantum>\n  void doTestDenseAll() {\n    doTestDense<kSkipQuantum, kForwardQuantum, true>();\n    doTestDense<kSkipQuantum, kForwardQuantum, false>();\n  }\n};\n\nTEST_F(EliasFanoCodingTest, Empty) {\n  doTestEmpty();\n}\n\nTEST_F(EliasFanoCodingTest, Simple32Bit) {\n  doTestAll<0, 0, uint32_t>();\n}\nTEST_F(EliasFanoCodingTest, Simple64Bit) {\n  doTestAll<0, 0, uint64_t>();\n}\nTEST_F(EliasFanoCodingTest, SimpleDense) {\n  doTestDenseAll<0, 0>();\n}\n\nTEST_F(EliasFanoCodingTest, SkipPointers32Bit) {\n  doTestAll<128, 0, uint32_t>();\n}\nTEST_F(EliasFanoCodingTest, SkipPointers64Bit) {\n  doTestAll<128, 0, uint64_t>();\n}\nTEST_F(EliasFanoCodingTest, SkipPointersDense) {\n  doTestDenseAll<128, 0>();\n}\n\nTEST_F(EliasFanoCodingTest, ForwardPointers32Bit) {\n  doTestAll<0, 128, uint32_t>();\n}\nTEST_F(EliasFanoCodingTest, ForwardPointers64Bit) {\n  doTestAll<0, 128, uint64_t>();\n}\nTEST_F(EliasFanoCodingTest, ForwardPointersDense) {\n  doTestDenseAll<0, 128>();\n}\n\nTEST_F(EliasFanoCodingTest, SkipForwardPointers32Bit) {\n  doTestAll<128, 128, uint32_t>();\n}\nTEST_F(EliasFanoCodingTest, SkipForwardPointers64Bit) {\n  doTestAll<128, 128, uint64_t>();\n}\nTEST_F(EliasFanoCodingTest, SkipForwardPointersDense) {\n  doTestDenseAll<128, 128>();\n}\n\nTEST_F(EliasFanoCodingTest, BugLargeGapInUpperBits) { // t16274876\n  using Encoder = EliasFanoEncoder<uint32_t, uint32_t, 2, 2>;\n  using Reader = EliasFanoReader<Encoder, instructions::Default>;\n  constexpr uint32_t kLargeValue = 127;\n\n  // Build a list where the upper bits have a large gap after the\n  // first element, so that we need to reposition in the upper bits\n  // using skips to position the iterator on the second element.\n  std::vector<uint32_t> data = {0, kLargeValue};\n  for (uint32_t i = 0; i < kLargeValue; ++i) {\n    data.push_back(data.back() + 1);\n  }\n  auto list = Encoder::encode(data.begin(), data.end());\n\n  {\n    Reader reader(list);\n    ASSERT_TRUE(reader.skipTo(kLargeValue - 1));\n    ASSERT_EQ(kLargeValue, reader.value());\n    ASSERT_EQ(0, reader.previousValue());\n  }\n\n  list.free();\n}\n\nTEST(CopyCompressedList, BasicCopy) {\n  using Encoder = EliasFanoEncoder<uint32_t, uint32_t, 128, 128, false>;\n  using Reader = EliasFanoReader<Encoder>;\n\n  const std::vector<uint32_t> data = {1, 5, 10, 50, 100, 500, 1000};\n  auto src = Encoder::encode(data.begin(), data.end());\n  auto dst = copyCompressedList<Encoder::MutableCompressedList, false>(src);\n\n  Reader reader(dst);\n  for (size_t i = 0; i < data.size(); ++i) {\n    ASSERT_TRUE(reader.next());\n    EXPECT_EQ(data[i], reader.value());\n  }\n  EXPECT_FALSE(reader.next());\n\n  dst.free();\n  src.free();\n}\n\nTEST(CopyCompressedList, BasicCopyUpperFirst) {\n  using Encoder = EliasFanoEncoder<uint32_t, uint32_t, 128, 128, true>;\n  using Reader = EliasFanoReader<Encoder>;\n\n  const std::vector<uint32_t> data = {1, 5, 10, 50, 100, 500, 1000};\n  auto src = Encoder::encode(data.begin(), data.end());\n  auto dst = copyCompressedList<Encoder::MutableCompressedList, true>(src);\n\n  Reader reader(dst);\n  for (size_t i = 0; i < data.size(); ++i) {\n    ASSERT_TRUE(reader.next());\n    EXPECT_EQ(data[i], reader.value());\n  }\n  EXPECT_FALSE(reader.next());\n\n  dst.free();\n  src.free();\n}\n\nTEST(CopyCompressedList, CopyIsIndependent) {\n  using Encoder = EliasFanoEncoder<uint32_t, uint32_t, 0, 0, false>;\n  using Reader = EliasFanoReader<Encoder>;\n\n  const std::vector<uint32_t> data = {2, 4, 6, 8, 10};\n  auto src = Encoder::encode(data.begin(), data.end());\n  auto dst = copyCompressedList<Encoder::MutableCompressedList, false>(src);\n  src.free();\n\n  Reader reader(dst);\n  for (size_t i = 0; i < data.size(); ++i) {\n    ASSERT_TRUE(reader.next());\n    EXPECT_EQ(data[i], reader.value());\n  }\n  EXPECT_FALSE(reader.next());\n\n  dst.free();\n}\n\nTEST(CopyCompressedList, CopyPreservesMetadata) {\n  using Encoder = EliasFanoEncoder<uint32_t, uint32_t, 128, 128, false>;\n\n  const std::vector<uint32_t> data = {10, 20, 30, 40, 50};\n  auto src = Encoder::encode(data.begin(), data.end());\n  auto dst = copyCompressedList<Encoder::MutableCompressedList, false>(src);\n\n  EXPECT_EQ(src.size, dst.size);\n  EXPECT_EQ(src.numLowerBits, dst.numLowerBits);\n  EXPECT_EQ(src.upperSizeBytes, dst.upperSizeBytes);\n  EXPECT_EQ(src.data.size(), dst.data.size());\n\n  // Verify the data buffers are distinct allocations.\n  EXPECT_NE(src.data.data(), dst.data.data());\n\n  dst.free();\n  src.free();\n}\n\nnamespace bm {\n\nusing Encoder = EliasFanoEncoder<uint32_t, uint32_t, 128, 128>;\n\nstd::vector<uint64_t> data;\nstd::vector<size_t> order;\n\nstd::vector<uint64_t> encodeSmallData;\nstd::vector<uint64_t> encodeLargeData;\n\nstd::vector<std::pair<size_t, size_t>> numLowerBitsInput;\n\ntypename Encoder::MutableCompressedList list;\n\nvoid init() {\n  std::mt19937 gen;\n\n  data = generateRandomList(100 * 1000, 10 * 1000 * 1000, gen);\n  list = Encoder::encode(data.begin(), data.end());\n\n  order.resize(data.size());\n  std::iota(order.begin(), order.end(), size_t());\n  std::shuffle(order.begin(), order.end(), gen);\n\n  encodeSmallData = generateRandomList(10, 100 * 1000, gen);\n  encodeLargeData = generateRandomList(1000 * 1000, 100 * 1000 * 1000, gen);\n\n  std::uniform_int_distribution<size_t> distribution;\n  for (size_t i = 0; i < 10000; ++i) {\n    const auto a = distribution(gen);\n    const auto b = distribution(gen);\n    numLowerBitsInput.emplace_back(std::max(a, b), std::min(a, b));\n  }\n}\n\nvoid free() {\n  list.free();\n}\n\n} // namespace bm\n\nBENCHMARK(Next, iters) {\n  dispatchInstructions([&](auto instructions) {\n    bmNext<EliasFanoReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, iters);\n  });\n}\n\nsize_t Skip_ForwardQ128(size_t iters, size_t logAvgSkip) {\n  dispatchInstructions([&](auto instructions) {\n    bmSkip<EliasFanoReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, logAvgSkip, iters);\n  });\n  return iters;\n}\n\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 1, 0)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 2, 1)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 4_pm_1, 2)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 16_pm_4, 4)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 64_pm_16, 6)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 256_pm_64, 8)\nBENCHMARK_NAMED_PARAM_MULTI(Skip_ForwardQ128, 1024_pm_256, 10)\n\nBENCHMARK(Jump_ForwardQ128, iters) {\n  dispatchInstructions([&](auto instructions) {\n    bmJump<EliasFanoReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, bm::order, iters);\n  });\n}\n\nBENCHMARK_DRAW_LINE();\n\nsize_t SkipTo_SkipQ128(size_t iters, size_t logAvgSkip) {\n  dispatchInstructions([&](auto instructions) {\n    bmSkipTo<EliasFanoReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, logAvgSkip, iters);\n  });\n  return iters;\n}\n\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 1, 0)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 2, 1)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 4_pm_1, 2)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 16_pm_4, 4)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 64_pm_16, 6)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 256_pm_64, 8)\nBENCHMARK_NAMED_PARAM_MULTI(SkipTo_SkipQ128, 1024_pm_256, 10)\n\nBENCHMARK(JumpTo_SkipQ128, iters) {\n  dispatchInstructions([&](auto instructions) {\n    bmJumpTo<EliasFanoReader<bm::Encoder, decltype(instructions)>>(\n        bm::list, bm::data, bm::order, iters);\n  });\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Encode_10) {\n  auto list = bm::Encoder::encode(\n      bm::encodeSmallData.begin(), bm::encodeSmallData.end());\n  list.free();\n}\n\nBENCHMARK(Encode) {\n  auto list = bm::Encoder::encode(\n      bm::encodeLargeData.begin(), bm::encodeLargeData.end());\n  list.free();\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(defaultNumLowerBits, iters) {\n  using Encoder = EliasFanoEncoder<size_t>;\n\n  size_t i = 0;\n  while (iters--) {\n    const auto& p = bm::numLowerBitsInput[i];\n    folly::doNotOptimizeAway(Encoder::defaultNumLowerBits(p.first, p.second));\n    if (++i == bm::numLowerBitsInput.size()) {\n      i = 0;\n    }\n  }\n}\n\nBENCHMARK(slowDefaultNumLowerBits, iters) {\n  size_t i = 0;\n  while (iters--) {\n    const auto& p = bm::numLowerBitsInput[i];\n    folly::doNotOptimizeAway(slowDefaultNumLowerBits(p.first, p.second));\n    if (++i == bm::numLowerBitsInput.size()) {\n      i = 0;\n    }\n  }\n}\n\n#if 0\n// Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz, Clang 8.0.\n// $ eliasfano_test --benchmark --bm_slice_usec 200000\n============================================================================\nfolly/experimental/test/EliasFanoCodingTest.cpp relative  time/iter  iters/s\n============================================================================\nNext                                                         2.58ns  388.22M\nSkip_ForwardQ128(1)                                          4.81ns  207.72M\nSkip_ForwardQ128(2)                                          5.96ns  167.75M\nSkip_ForwardQ128(4_pm_1)                                     7.40ns  135.16M\nSkip_ForwardQ128(16_pm_4)                                    8.20ns  121.97M\nSkip_ForwardQ128(64_pm_16)                                  12.04ns   83.06M\nSkip_ForwardQ128(256_pm_64)                                 16.84ns   59.39M\nSkip_ForwardQ128(1024_pm_256)                               17.67ns   56.61M\nJump_ForwardQ128                                            25.37ns   39.41M\n----------------------------------------------------------------------------\nSkipTo_SkipQ128(1)                                           7.27ns  137.59M\nSkipTo_SkipQ128(2)                                          10.99ns   91.01M\nSkipTo_SkipQ128(4_pm_1)                                     13.53ns   73.93M\nSkipTo_SkipQ128(16_pm_4)                                    20.58ns   48.59M\nSkipTo_SkipQ128(64_pm_16)                                   32.08ns   31.18M\nSkipTo_SkipQ128(256_pm_64)                                  38.66ns   25.87M\nSkipTo_SkipQ128(1024_pm_256)                                42.32ns   23.63M\nJumpTo_SkipQ128                                             47.95ns   20.86M\n----------------------------------------------------------------------------\nEncode_10                                                  103.99ns    9.62M\nEncode                                                       7.60ms   131.53\n----------------------------------------------------------------------------\ndefaultNumLowerBits                                          3.59ns  278.69M\nslowDefaultNumLowerBits                                     10.88ns   91.90M\n============================================================================\n#endif\n\nint main(int argc, char** argv) {\n  testing::InitGoogleTest(&argc, argv);\n  folly::Init init(&argc, &argv);\n\n  auto ret = RUN_ALL_TESTS();\n  if (ret == 0 && FLAGS_benchmark) {\n    bm::init();\n    folly::runBenchmarks();\n    bm::free();\n  }\n\n  return ret;\n}\n"
  },
  {
    "path": "folly/compression/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"large_test\",\n    srcs = [\"LargeTest.cpp\"],\n    supports_static_listing = False,\n    target_compatible_with = [\"fbcode//opensource/macros:broken-in-oss\"],\n    deps = [\n        \"fbsource//third-party/googletest:gtest\",\n        \"//folly/compression:compression\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"compression_test\",\n    srcs = [\"CompressionTest.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    target_compatible_with = [\"fbcode//opensource/macros:broken-in-oss\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//third-party/zstd:zstd\",\n        \"//folly:random\",\n        \"//folly:varint\",\n        \"//folly/compression:compression\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"compression_context_pool_test\",\n    srcs = [\n        \"CompressionContextPoolTest.cpp\",\n    ],\n    labels = [\"case-isolation-failure\"],\n    deps = [\n        \"//folly/compression:compression_context_pool\",\n        \"//folly/compression:compression_context_pool_singletons\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"compression_context_pool_benchmark\",\n    srcs = [\"CompressionContextPoolBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly/compression:compression_context_pool\",\n        \"//folly/compression:compression_context_pool_singletons\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"quotient_multiset_benchmark\",\n    srcs = [\n        \"QuotientMultiSetBenchmark.cpp\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly:string\",\n        \"//folly/compression:quotient_multiset\",\n        \"//folly/compression/elias_fano:elias_fano_coding\",\n        \"//folly/container:enumerate\",\n        \"//folly/container:f14_hash\",\n        \"//folly/experimental/test:coding_test_utils\",\n        \"//folly/init:init\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_sort\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"quotient_multiset_test\",\n    srcs = [\n        \"QuotientMultiSetTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:random\",\n        \"//folly/compression:quotient_multiset\",\n        \"//folly/container:enumerate\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"instructions_test\",\n    srcs = [\"InstructionsTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/compression:instructions\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"select64_test\",\n    srcs = [\"Select64Test.cpp\"],\n    deps = [\n        \"//folly/compression:instructions\",\n        \"//folly/compression:select64\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"coding_test_utils\",\n    srcs = [\"CodingTestUtils.cpp\"],\n    headers = [\"CodingTestUtils.h\"],\n    deps = [\n        \"//folly/portability:gflags\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:likely\",\n        \"//folly:optional\",\n        \"//folly/compression:instructions\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/compression/test/CodingTestUtils.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/test/CodingTestUtils.h>\n\n#include <folly/portability/GFlags.h>\n\n#include <glog/logging.h>\n\nDEFINE_string(\n    coding_test_utils_instructions,\n    \"\",\n    \"If non empty, forces the instruction set. Choices: Default, Nehalem, Haswell\");\n\nnamespace folly {\nnamespace compression {\n\nfolly::Optional<instructions::Type> instructionsOverride() {\n  if (FLAGS_coding_test_utils_instructions.empty()) {\n    return folly::none;\n  }\n\n  instructions::Type type;\n  if (FLAGS_coding_test_utils_instructions == \"Default\") {\n    type = instructions::Type::DEFAULT;\n  } else if (FLAGS_coding_test_utils_instructions == \"Nehalem\") {\n    type = instructions::Type::NEHALEM;\n  } else if (FLAGS_coding_test_utils_instructions == \"Haswell\") {\n    type = instructions::Type::HASWELL;\n  } else {\n    LOG(FATAL) << \"Insupported instructions type \"\n               << FLAGS_coding_test_utils_instructions;\n  }\n\n  instructions::dispatch(type, [](auto instructions) {\n    CHECK(instructions.supported());\n  });\n\n  return type;\n}\n\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/test/CodingTestUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <fstream>\n#include <limits>\n#include <random>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/Likely.h>\n#include <folly/Optional.h>\n#include <folly/compression/Instructions.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace compression {\n\ntemplate <class URNG>\nstd::vector<uint64_t> generateRandomList(\n    size_t n, uint64_t maxId, URNG&& g, bool withDuplicates = false) {\n  CHECK_LT(n, 2 * maxId);\n  std::uniform_int_distribution<uint64_t> uid(1, maxId);\n  std::unordered_set<uint64_t> dataset;\n  while (dataset.size() < n) {\n    uint64_t value = uid(g);\n    if (dataset.count(value) == 0) {\n      dataset.insert(value);\n    }\n  }\n\n  std::vector<uint64_t> ids(dataset.begin(), dataset.end());\n  if (withDuplicates && n > 0) {\n    // Ensure 20% of the list has at least 1 duplicate, 10% has at least 2, and\n    // 5% is a run of the same value.\n    std::copy(ids.begin() + ids.size() * 8 / 10, ids.end(), ids.begin());\n    std::copy(ids.begin() + ids.size() * 9 / 10, ids.end(), ids.begin());\n    std::fill(ids.begin(), ids.begin() + ids.size() / 20, ids[0]);\n  }\n  std::sort(ids.begin(), ids.end());\n  return ids;\n}\n\ninline std::vector<uint64_t> generateRandomList(\n    size_t n, uint64_t maxId, bool withDuplicates = false) {\n  std::mt19937 gen;\n  return generateRandomList(n, maxId, gen, withDuplicates);\n}\n\ninline std::vector<uint64_t> generateSeqList(\n    uint64_t minId, uint64_t maxId, uint64_t step = 1) {\n  CHECK_LE(minId, maxId);\n  CHECK_GT(step, 0);\n  std::vector<uint64_t> ids;\n  ids.reserve((maxId - minId) / step + 1);\n  for (uint64_t i = minId; i <= maxId; i += step) {\n    ids.push_back(i);\n  }\n  return ids;\n}\n\ninline std::vector<uint64_t> loadList(const std::string& filename) {\n  std::ifstream fin(filename);\n  std::vector<uint64_t> result;\n  uint64_t id;\n  while (fin >> id) {\n    result.push_back(id);\n  }\n  return result;\n}\n\n// Test previousValue only if Reader has it.\ntemplate <class... Args>\nvoid maybeTestPreviousValue(Args&&...) {}\n\n// Make all the arguments template because if the types are not exact,\n// the above overload will be picked (for example i could be size_t or\n// ssize_t).\ntemplate <class Vector, class Reader, class Index>\nauto maybeTestPreviousValue(const Vector& data, Reader& reader, Index i)\n    -> decltype(reader.previousValue(), void()) {\n  if (i != 0) {\n    EXPECT_EQ(reader.previousValue(), data[i - 1]);\n  }\n}\n\n// Test previous only if Reader has it.\ntemplate <class... Args>\nvoid maybeTestPrevious(Args&&...) {}\n\n// Make all the arguments template because if the types are not exact,\n// the above overload will be picked (for example i could be size_t or\n// ssize_t).\ntemplate <class Vector, class Reader, class Index>\nauto maybeTestPrevious(const Vector& data, Reader& reader, Index i)\n    -> decltype(reader.previous(), void()) {\n  auto r = reader.previous();\n  if (i != 0) {\n    EXPECT_TRUE(r);\n    EXPECT_EQ(reader.value(), data[i - 1]);\n  } else {\n    EXPECT_FALSE(r);\n  }\n  reader.next();\n  EXPECT_EQ(reader.value(), data[i]);\n}\n\ntemplate <class Reader, class List>\nvoid testNext(const std::vector<uint64_t>& data, const List& list) {\n  Reader reader(list);\n  EXPECT_FALSE(reader.valid());\n\n  for (size_t i = 0; i < data.size(); ++i) {\n    EXPECT_TRUE(reader.next()) << i << \" \" << data.size();\n    EXPECT_TRUE(reader.valid()) << i << \" \" << data.size();\n    EXPECT_EQ(reader.value(), data[i]) << i << \" \" << data.size();\n    EXPECT_EQ(reader.position(), i) << i << \" \" << data.size();\n    maybeTestPreviousValue(data, reader, i);\n    maybeTestPrevious(data, reader, i);\n  }\n  EXPECT_FALSE(reader.next()) << data.size();\n  EXPECT_FALSE(reader.valid()) << data.size();\n  EXPECT_EQ(reader.position(), reader.size());\n}\n\ntemplate <class Reader, class List>\nvoid testSkip(\n    const std::vector<uint64_t>& data, const List& list, size_t skipStep) {\n  CHECK_GT(skipStep, 0);\n  Reader reader(list);\n\n  for (size_t i = skipStep - 1; i < data.size(); i += skipStep) {\n    // Destination must be representable.\n    if (i + skipStep > std::numeric_limits<typename Reader::SizeType>::max()) {\n      return;\n    }\n\n    // Also test that skip(0) stays in place.\n    for (auto step : {skipStep, size_t(0)}) {\n      EXPECT_TRUE(reader.skip(step));\n      EXPECT_TRUE(reader.valid());\n      EXPECT_EQ(reader.value(), data[i]);\n      EXPECT_EQ(reader.position(), i);\n    }\n\n    maybeTestPreviousValue(data, reader, i);\n    maybeTestPrevious(data, reader, i);\n  }\n  EXPECT_FALSE(reader.skip(skipStep));\n  EXPECT_FALSE(reader.valid());\n  EXPECT_EQ(reader.position(), reader.size());\n  EXPECT_FALSE(reader.next());\n}\n\ntemplate <class Reader, class List>\nvoid testSkip(const std::vector<uint64_t>& data, const List& list) {\n  for (size_t skipStep = 1; skipStep < 25; ++skipStep) {\n    testSkip<Reader, List>(data, list, skipStep);\n  }\n  for (size_t skipStep = 25; skipStep <= 500; skipStep += 25) {\n    testSkip<Reader, List>(data, list, skipStep);\n  }\n}\n\ntemplate <class Reader, class List>\nvoid testSkipTo(\n    const std::vector<uint64_t>& data, const List& list, size_t skipToStep) {\n  using ValueType = typename Reader::ValueType;\n\n  CHECK_GT(skipToStep, 0);\n  Reader reader(list);\n\n  const uint64_t delta = std::max<uint64_t>(1, data.back() / skipToStep);\n  ValueType target = delta;\n  auto it = data.begin();\n  while (true) {\n    it = std::lower_bound(it, data.end(), target);\n    if (it == data.end()) {\n      EXPECT_FALSE(reader.skipTo(target));\n      break;\n    }\n\n    EXPECT_TRUE(reader.skipTo(target));\n    // Test the whole group of equal values.\n    for (auto it2 = it; it2 != data.end() && *it2 == *it;\n         ++it2, reader.next()) {\n      ASSERT_TRUE(reader.valid());\n      EXPECT_EQ(reader.value(), *it2);\n      EXPECT_EQ(reader.position(), std::distance(data.begin(), it2));\n\n      // The reader should stay in place even if we're in the middle of a group.\n      EXPECT_TRUE(reader.skipTo(*it2));\n      EXPECT_EQ(reader.value(), *it2);\n      EXPECT_EQ(reader.position(), std::distance(data.begin(), it2));\n\n      maybeTestPreviousValue(data, reader, std::distance(data.begin(), it2));\n      maybeTestPrevious(data, reader, std::distance(data.begin(), it2));\n    }\n\n    // Reader is now past the group.\n    if (!reader.valid()) {\n      break;\n    }\n\n    target += delta;\n    if (target < reader.value()) {\n      // Value following the group is already greater than target, or delta is\n      // so large that target wrapped around.\n      target = reader.value();\n    }\n  }\n  EXPECT_FALSE(reader.valid());\n  EXPECT_EQ(reader.position(), reader.size());\n  EXPECT_FALSE(reader.next());\n}\n\ntemplate <class Reader, class List>\nvoid testSkipTo(const std::vector<uint64_t>& data, const List& list) {\n  for (size_t steps = 10; steps < 100; steps += 10) {\n    testSkipTo<Reader, List>(data, list, steps);\n  }\n  for (size_t steps = 100; steps <= 1000; steps += 100) {\n    testSkipTo<Reader, List>(data, list, steps);\n  }\n  testSkipTo<Reader, List>(data, list, std::numeric_limits<size_t>::max());\n  {\n    // Skip to the first element.\n    Reader reader(list);\n    EXPECT_TRUE(reader.skipTo(data[0]));\n    EXPECT_EQ(reader.value(), data[0]);\n    EXPECT_EQ(reader.position(), 0);\n  }\n\n  // Skip past the last element, when possible. Make sure to probe values far\n  // from the last element, as the reader implementation may keep an internal\n  // upper bound larger than that, and we need to make sure we exercise skipping\n  // both before and after that.\n  using ValueType = typename Reader::ValueType;\n  std::vector<ValueType> valuesPastTheEnd;\n  const auto lastValue = data.back();\n  const auto kMaxValue = std::numeric_limits<ValueType>::max();\n  // Keep doubling the distance from the last value until we overflow.\n  for (ValueType value = lastValue + 1; value > lastValue;\n       value += value - lastValue) {\n    valuesPastTheEnd.push_back(value);\n  }\n  if (kMaxValue != lastValue) {\n    valuesPastTheEnd.push_back(kMaxValue);\n  }\n\n  for (auto value : valuesPastTheEnd) {\n    Reader reader(list);\n    EXPECT_FALSE(reader.skipTo(value)) << value << \" \" << lastValue;\n    EXPECT_FALSE(reader.valid()) << value << \" \" << lastValue;\n    EXPECT_EQ(reader.position(), reader.size()) << value << \" \" << lastValue;\n    EXPECT_FALSE(reader.next()) << value << \" \" << lastValue;\n  }\n}\n\ntemplate <class Reader, class List>\nvoid testJump(const std::vector<uint64_t>& data, const List& list) {\n  std::mt19937 gen;\n  std::vector<size_t> is(data.size());\n  for (size_t i = 0; i < data.size(); ++i) {\n    is[i] = i;\n  }\n  std::shuffle(is.begin(), is.end(), gen);\n  if (Reader::EncoderType::forwardQuantum == 0) {\n    is.resize(std::min<size_t>(is.size(), 100));\n  }\n\n  Reader reader(list);\n  for (auto i : is) {\n    // Also test idempotency.\n    for (size_t round = 0; round < 2; ++round) {\n      EXPECT_TRUE(reader.jump(i)) << i << \" \" << data.size();\n      EXPECT_EQ(reader.value(), data[i]) << i << \" \" << data.size();\n      EXPECT_EQ(reader.position(), i) << i << \" \" << data.size();\n    }\n    maybeTestPreviousValue(data, reader, i);\n    maybeTestPrevious(data, reader, i);\n  }\n  EXPECT_FALSE(reader.jump(data.size()));\n  EXPECT_FALSE(reader.valid());\n  EXPECT_EQ(reader.position(), reader.size());\n}\n\ntemplate <class Reader, class List>\nvoid testJumpTo(const std::vector<uint64_t>& data, const List& list) {\n  using ValueType = typename Reader::ValueType;\n\n  CHECK(!data.empty());\n  Reader reader(list);\n\n  std::mt19937 gen;\n  std::uniform_int_distribution<ValueType> targets(0, data.back());\n  const size_t iters = Reader::EncoderType::skipQuantum == 0 ? 100 : 10000;\n  for (size_t i = 0; i < iters; ++i) {\n    uint64_t target;\n    // Force boundary targets interleaved with random targets.\n    if (i == 10) {\n      target = data.back();\n    } else if (i == 20) {\n      target = 0;\n    } else {\n      target = targets(gen);\n    }\n\n    auto it = std::lower_bound(data.begin(), data.end(), target);\n    CHECK(it != data.end());\n    EXPECT_TRUE(reader.jumpTo(target));\n    // Test the whole group of equal values.\n    for (auto it2 = it; it2 != data.end() && *it2 == *it;\n         ++it2, reader.next()) {\n      EXPECT_EQ(reader.value(), *it2);\n      EXPECT_EQ(reader.position(), std::distance(data.begin(), it2));\n    }\n    // Calling jumpTo() on the current value should reposition on the beginning\n    // of the group.\n    EXPECT_TRUE(reader.jumpTo(*it));\n    EXPECT_EQ(reader.position(), std::distance(data.begin(), it));\n  }\n\n  if (data.back() != std::numeric_limits<ValueType>::max()) {\n    EXPECT_FALSE(reader.jumpTo(data.back() + 1));\n    EXPECT_FALSE(reader.valid());\n    EXPECT_EQ(reader.position(), reader.size());\n  }\n}\n\ntemplate <class Reader, class Encoder>\nvoid testEmpty() {\n  const typename Encoder::ValueType* const data = nullptr;\n  auto list = Encoder::encode(data, data);\n  {\n    Reader reader(list);\n    EXPECT_FALSE(reader.next());\n    EXPECT_EQ(reader.size(), 0);\n  }\n  {\n    Reader reader(list);\n    EXPECT_FALSE(reader.skip(1));\n    EXPECT_FALSE(reader.skip(10));\n    EXPECT_FALSE(reader.jump(0));\n    EXPECT_FALSE(reader.jump(10));\n  }\n  {\n    Reader reader(list);\n    EXPECT_FALSE(reader.skipTo(1));\n    EXPECT_FALSE(reader.jumpTo(1));\n  }\n}\n\n// `upperBoundExtension` is required to inject additional 0-blocks\n// at the end of the list. This allows us to test lists with a large gap between\n// last element and universe upper bound, to exercise bounds-checking when\n// skipping past the last element\ntemplate <class Reader, class Encoder>\nvoid testAll(\n    const std::vector<uint64_t>& data, uint64_t upperBoundExtension = 0) {\n  SCOPED_TRACE(__PRETTY_FUNCTION__);\n\n  Encoder encoder(data.size(), data.back() + upperBoundExtension);\n  for (const auto value : data) {\n    encoder.add(value);\n  }\n  auto list = encoder.finish();\n  testNext<Reader>(data, list);\n  testSkip<Reader>(data, list);\n  testSkipTo<Reader>(data, list);\n  testJump<Reader>(data, list);\n  testJumpTo<Reader>(data, list);\n  list.free();\n}\n\ntemplate <class Reader, class List>\nvoid bmNext(const List& list, const std::vector<uint64_t>& data, size_t iters) {\n  if (data.empty()) {\n    return;\n  }\n\n  Reader reader(list);\n  for (size_t i = 0; i < iters; ++i) {\n    if (FOLLY_LIKELY(reader.next())) {\n      folly::doNotOptimizeAway(reader.value());\n    } else {\n      reader.reset();\n    }\n  }\n}\n\ntemplate <class Reader, class List>\nvoid bmSkip(\n    const List& list,\n    const std::vector<uint64_t>& /* data */,\n    size_t logAvgSkip,\n    size_t iters) {\n  size_t avg = (size_t(1) << logAvgSkip);\n  size_t base = avg - (avg >> 2);\n  size_t mask = (avg > 1) ? (avg >> 1) - 1 : 0;\n\n  Reader reader(list);\n  for (size_t i = 0; i < iters; ++i) {\n    size_t skip = base + (i & mask);\n    if (FOLLY_LIKELY(reader.skip(skip))) {\n      folly::doNotOptimizeAway(reader.value());\n    } else {\n      reader.reset();\n    }\n  }\n}\n\ntemplate <class Reader, class List>\nvoid bmSkipTo(\n    const List& list,\n    const std::vector<uint64_t>& data,\n    size_t logAvgSkip,\n    size_t iters) {\n  size_t avg = (size_t(1) << logAvgSkip);\n  size_t base = avg - (avg >> 2);\n  size_t mask = (avg > 1) ? (avg >> 1) - 1 : 0;\n\n  Reader reader(list);\n  for (size_t i = 0, j = -1; i < iters; ++i) {\n    size_t skip = base + (i & mask);\n    j += skip;\n    if (j >= data.size()) {\n      reader.reset();\n      j = -1;\n    }\n\n    reader.skipTo(data[j]);\n    folly::doNotOptimizeAway(reader.value());\n  }\n}\n\ntemplate <class Reader, class List>\nvoid bmJump(\n    const List& list,\n    const std::vector<uint64_t>& data,\n    const std::vector<size_t>& order,\n    size_t iters) {\n  CHECK(!data.empty());\n  CHECK_EQ(data.size(), order.size());\n\n  Reader reader(list);\n  for (size_t i = 0, j = 0; i < iters; ++i, ++j) {\n    if (j == order.size()) {\n      j = 0;\n    }\n    reader.jump(order[j]);\n    folly::doNotOptimizeAway(reader.value());\n  }\n}\n\ntemplate <class Reader, class List>\nvoid bmJumpTo(\n    const List& list,\n    const std::vector<uint64_t>& data,\n    const std::vector<size_t>& order,\n    size_t iters) {\n  CHECK(!data.empty());\n  CHECK_EQ(data.size(), order.size());\n\n  Reader reader(list);\n  for (size_t i = 0, j = 0; i < iters; ++i, ++j) {\n    if (j == order.size()) {\n      j = 0;\n    }\n    reader.jumpTo(data[order[j]]);\n    folly::doNotOptimizeAway(reader.value());\n  }\n}\n\nfolly::Optional<instructions::Type> instructionsOverride();\n\ntemplate <class F>\nauto dispatchInstructions(F&& f)\n    -> decltype(f(std::declval<instructions::Default>())) {\n  if (auto type = instructionsOverride()) {\n    return instructions::dispatch(*type, std::forward<F>(f));\n  } else {\n    return instructions::dispatch(std::forward<F>(f));\n  }\n}\n\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/test/CompressionContextPoolBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/CompressionContextPool.h>\n#include <folly/compression/CompressionContextPoolSingletons.h>\n#include <folly/compression/CompressionCoreLocalContextPool.h>\n\n#include <memory>\n#include <thread>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n\nnamespace {\n\nclass alignas(folly::hardware_destructive_interference_size) Foo {\n public:\n  void use() { used_ = true; }\n\n  void reset() { used_ = false; }\n\n private:\n  bool used_{false};\n};\n\nstruct FooCreator {\n  Foo* operator()() const { return new Foo(); }\n};\n\nstruct FooDeleter {\n  void operator()(Foo* f) const { delete f; }\n};\n\nstruct FooResetter {\n  void operator()(Foo* f) const { f->reset(); }\n};\n\nstruct FooSizeof {\n  size_t operator()(const Foo* f) const { return sizeof(*f); }\n};\n\nstruct FooCallback {\n  static size_t count;\n\n  void operator()() const { count++; }\n};\n\nsize_t FooCallback::count = 0;\n\nusing FooStackPool = folly::compression::CompressionContextPool<\n    Foo,\n    FooCreator,\n    FooDeleter,\n    FooResetter,\n    FooSizeof,\n    FooCallback>;\n\nusing FooCoreLocalPool = folly::compression::CompressionCoreLocalContextPool<\n    Foo,\n    FooCreator,\n    FooDeleter,\n    FooResetter,\n    FooSizeof,\n    FooCallback>;\n\ntemplate <typename Pool>\nsize_t multithreadedBench(size_t iters, size_t numThreads, Pool& pool) {\n  folly::BenchmarkSuspender startup_suspender;\n\n  iters *= 1024;\n  const size_t iters_per_thread = iters;\n\n  std::atomic<size_t> ready{0};\n  std::atomic<size_t> finished{0};\n  std::atomic<bool> go{false};\n  std::atomic<bool> exit{false};\n  std::atomic<size_t> total{0};\n\n  {\n    // Pre-init enough objects.\n    std::vector<typename Pool::Ref> refs;\n    while (refs.size() < numThreads) {\n      refs.push_back(pool.get());\n    }\n  }\n  pool.flush_shallow();\n\n  // if we happen to be using the tlsRoundRobin, then sequentially\n  // assigning the thread identifiers is the unlikely best-case scenario.\n  // We don't want to unfairly benefit or penalize.  Computing the exact\n  // maximum likelihood of the probability distributions is annoying, so\n  // I approximate as 2/5 of the ids that have no threads, 2/5 that have\n  // 1, 2/15 that have 2, and 1/15 that have 3.  We accomplish this by\n  // wrapping back to slot 0 when we hit 1/15 and 1/5.\n\n  std::vector<std::thread> threads;\n  while (threads.size() < numThreads) {\n    threads.push_back(std::thread([&, iters_per_thread]() {\n      size_t local_count = 0;\n\n      auto finish_loop_iters = iters_per_thread / 10;\n      if (finish_loop_iters < 10) {\n        finish_loop_iters = 10;\n      }\n\n      ready++;\n\n      while (!go.load()) {\n        std::this_thread::yield();\n      }\n\n      for (size_t i = iters_per_thread; i > 0; --i) {\n        auto ref = pool.get();\n        ref->use();\n        folly::doNotOptimizeAway(ref);\n      }\n\n      local_count += iters_per_thread;\n\n      finished++;\n\n      while (!exit.load()) {\n        // Keep accumulating iterations until all threads are finished.\n        for (size_t i = finish_loop_iters; i > 0; --i) {\n          auto ref = pool.get();\n          ref->use();\n          folly::doNotOptimizeAway(ref);\n        }\n        local_count += finish_loop_iters;\n      }\n\n      total += local_count;\n\n      // LOG(INFO) << local_count;\n    }));\n\n    if (threads.size() == numThreads / 15 || threads.size() == numThreads / 5) {\n      // create a few dummy threads to wrap back around to 0 mod numCpus\n      for (size_t i = threads.size(); i != numThreads; ++i) {\n        std::thread t([&]() {});\n        t.join();\n      }\n    }\n  }\n\n  while (ready < numThreads) {\n    std::this_thread::yield();\n  }\n  startup_suspender.dismiss();\n  go = true;\n\n  while (finished < numThreads) {\n    std::this_thread::yield();\n  }\n\n  exit = true;\n\n  folly::BenchmarkSuspender end_suspender;\n\n  for (auto& thr : threads) {\n    thr.join();\n  }\n\n  // LOG(INFO) << total.load();\n\n  return total.load() / numThreads;\n}\n\n#define POOL_BENCHMARK(Name, Pool, NumThreads)            \\\n  BENCHMARK_MULTI(Name, n) {                              \\\n    Pool pool;                                            \\\n    return multithreadedBench<Pool>(n, NumThreads, pool); \\\n  }\n\n#define REGULAR_POOL_BENCHMARK(NumThreads) \\\n  POOL_BENCHMARK(StackPool_##NumThreads##_threads, FooStackPool, NumThreads)\n\n#define STRIPED_POOL_BENCHMARK(Name, Pool, NumThreads, NumSlots) \\\n  BENCHMARK_MULTI(Name, n) {                                     \\\n    Pool pool(NumSlots);                                         \\\n    return multithreadedBench<Pool>(n, NumThreads, pool);        \\\n  }\n\n#define CORE_LOCAL_POOL_BENCHMARK(NumSlots, NumThreads) \\\n  STRIPED_POOL_BENCHMARK(                               \\\n      CLPool_##NumSlots##_slots_##NumThreads##_threads, \\\n      FooCoreLocalPool,                                 \\\n      NumThreads,                                       \\\n      NumSlots)\n\nREGULAR_POOL_BENCHMARK(1)\nREGULAR_POOL_BENCHMARK(2)\nREGULAR_POOL_BENCHMARK(4)\nREGULAR_POOL_BENCHMARK(8)\nREGULAR_POOL_BENCHMARK(16)\nREGULAR_POOL_BENCHMARK(32)\nREGULAR_POOL_BENCHMARK(64)\nREGULAR_POOL_BENCHMARK(128)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 1)\nCORE_LOCAL_POOL_BENCHMARK(2, 1)\nCORE_LOCAL_POOL_BENCHMARK(4, 1)\nCORE_LOCAL_POOL_BENCHMARK(8, 1)\nCORE_LOCAL_POOL_BENCHMARK(16, 1)\nCORE_LOCAL_POOL_BENCHMARK(32, 1)\nCORE_LOCAL_POOL_BENCHMARK(64, 1)\nCORE_LOCAL_POOL_BENCHMARK(128, 1)\nCORE_LOCAL_POOL_BENCHMARK(256, 1)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 2)\nCORE_LOCAL_POOL_BENCHMARK(2, 2)\nCORE_LOCAL_POOL_BENCHMARK(4, 2)\nCORE_LOCAL_POOL_BENCHMARK(8, 2)\nCORE_LOCAL_POOL_BENCHMARK(16, 2)\nCORE_LOCAL_POOL_BENCHMARK(32, 2)\nCORE_LOCAL_POOL_BENCHMARK(64, 2)\nCORE_LOCAL_POOL_BENCHMARK(128, 2)\nCORE_LOCAL_POOL_BENCHMARK(256, 2)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 4)\nCORE_LOCAL_POOL_BENCHMARK(2, 4)\nCORE_LOCAL_POOL_BENCHMARK(4, 4)\nCORE_LOCAL_POOL_BENCHMARK(8, 4)\nCORE_LOCAL_POOL_BENCHMARK(16, 4)\nCORE_LOCAL_POOL_BENCHMARK(32, 4)\nCORE_LOCAL_POOL_BENCHMARK(64, 4)\nCORE_LOCAL_POOL_BENCHMARK(128, 4)\nCORE_LOCAL_POOL_BENCHMARK(256, 4)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 8)\nCORE_LOCAL_POOL_BENCHMARK(2, 8)\nCORE_LOCAL_POOL_BENCHMARK(4, 8)\nCORE_LOCAL_POOL_BENCHMARK(8, 8)\nCORE_LOCAL_POOL_BENCHMARK(16, 8)\nCORE_LOCAL_POOL_BENCHMARK(32, 8)\nCORE_LOCAL_POOL_BENCHMARK(64, 8)\nCORE_LOCAL_POOL_BENCHMARK(128, 8)\nCORE_LOCAL_POOL_BENCHMARK(256, 8)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 16)\nCORE_LOCAL_POOL_BENCHMARK(2, 16)\nCORE_LOCAL_POOL_BENCHMARK(4, 16)\nCORE_LOCAL_POOL_BENCHMARK(8, 16)\nCORE_LOCAL_POOL_BENCHMARK(16, 16)\nCORE_LOCAL_POOL_BENCHMARK(32, 16)\nCORE_LOCAL_POOL_BENCHMARK(64, 16)\nCORE_LOCAL_POOL_BENCHMARK(128, 16)\nCORE_LOCAL_POOL_BENCHMARK(256, 16)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 32)\nCORE_LOCAL_POOL_BENCHMARK(2, 32)\nCORE_LOCAL_POOL_BENCHMARK(4, 32)\nCORE_LOCAL_POOL_BENCHMARK(8, 32)\nCORE_LOCAL_POOL_BENCHMARK(16, 32)\nCORE_LOCAL_POOL_BENCHMARK(32, 32)\nCORE_LOCAL_POOL_BENCHMARK(64, 32)\nCORE_LOCAL_POOL_BENCHMARK(128, 32)\nCORE_LOCAL_POOL_BENCHMARK(256, 32)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 64)\nCORE_LOCAL_POOL_BENCHMARK(2, 64)\nCORE_LOCAL_POOL_BENCHMARK(4, 64)\nCORE_LOCAL_POOL_BENCHMARK(8, 64)\nCORE_LOCAL_POOL_BENCHMARK(16, 64)\nCORE_LOCAL_POOL_BENCHMARK(32, 64)\nCORE_LOCAL_POOL_BENCHMARK(64, 64)\nCORE_LOCAL_POOL_BENCHMARK(128, 64)\nCORE_LOCAL_POOL_BENCHMARK(256, 64)\n\nBENCHMARK_DRAW_LINE();\n\nCORE_LOCAL_POOL_BENCHMARK(1, 128)\nCORE_LOCAL_POOL_BENCHMARK(2, 128)\nCORE_LOCAL_POOL_BENCHMARK(4, 128)\nCORE_LOCAL_POOL_BENCHMARK(8, 128)\nCORE_LOCAL_POOL_BENCHMARK(16, 128)\nCORE_LOCAL_POOL_BENCHMARK(32, 128)\nCORE_LOCAL_POOL_BENCHMARK(64, 128)\nCORE_LOCAL_POOL_BENCHMARK(128, 128)\nCORE_LOCAL_POOL_BENCHMARK(256, 128)\n\n} // anonymous namespace\n\nint main(int argc, char** argv) {\n  gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/compression/test/CompressionContextPoolTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/portability/GTest.h>\n\n#include <folly/compression/CompressionContextPool.h>\n#include <folly/compression/CompressionContextPoolSingletons.h>\n#include <folly/compression/CompressionCoreLocalContextPool.h>\n\nnamespace folly {\nnamespace compression {\n\nnamespace {\n\nstd::atomic<size_t> numFoos{0};\nstd::atomic<size_t> numDeleted{0};\n\n// Ensure sizeof(Foo) != 1 for bytes_used() test to be meaningful\nclass alignas(4) Foo {\n public:\n  void use() {\n    EXPECT_FALSE(used_);\n    used_ = true;\n  }\n\n  void reset() { used_ = false; }\n\n private:\n  bool used_{false};\n};\n\nstruct FooCreator {\n  Foo* operator()() const {\n    numFoos++;\n    return new Foo();\n  }\n};\n\nstruct BadFooCreator {\n  Foo* operator()() const {\n    numFoos++;\n    return nullptr;\n  }\n};\n\nstruct FooDeleter {\n  void operator()(Foo* f) const {\n    numDeleted++;\n    delete f;\n  }\n};\n\nstruct FooResetter {\n  void operator()(Foo* f) const { f->reset(); }\n};\n\nstruct FooSizeof {\n  size_t operator()(const Foo* f) const { return sizeof(*f); }\n};\n\nstruct FooCallback {\n  void operator()() const {}\n};\n\nusing Pool = CompressionContextPool<\n    Foo,\n    FooCreator,\n    FooDeleter,\n    FooResetter,\n    FooSizeof,\n    FooCallback>;\nusing BadPool = CompressionContextPool<\n    Foo,\n    BadFooCreator,\n    FooDeleter,\n    FooResetter,\n    FooSizeof,\n    FooCallback>;\n\n} // anonymous namespace\n\nclass CompressionContextPoolTest : public testing::Test {\n protected:\n  void SetUp() override { pool_ = std::make_unique<Pool>(); }\n\n  void TearDown() override { pool_.reset(); }\n\n  std::unique_ptr<Pool> pool_;\n};\n\nTEST_F(CompressionContextPoolTest, testGet) {\n  auto ptr = pool_->get();\n  EXPECT_TRUE(ptr);\n  EXPECT_EQ(pool_->created_count(), 1);\n}\n\nTEST_F(CompressionContextPoolTest, testSame) {\n  Pool::Object* tmp;\n  {\n    auto ptr = pool_->get();\n    tmp = ptr.get();\n  }\n  {\n    auto ptr = pool_->get();\n    EXPECT_EQ(tmp, ptr.get());\n  }\n  EXPECT_EQ(pool_->size(), 1);\n}\n\nTEST_F(CompressionContextPoolTest, testFree) {\n  EXPECT_EQ(pool_->size(), 0);\n  {\n    auto ptr = pool_->get();\n    EXPECT_TRUE(ptr);\n    EXPECT_EQ(pool_->size(), 0);\n  }\n  EXPECT_EQ(pool_->size(), 1);\n  EXPECT_EQ(numFoos.load(), 1);\n  EXPECT_EQ(numDeleted.load(), 0);\n  pool_.reset();\n  EXPECT_EQ(numFoos.load(), numDeleted.load());\n}\n\nTEST_F(CompressionContextPoolTest, testDifferent) {\n  {\n    auto ptr1 = pool_->get();\n    auto ptr2 = pool_->get();\n    EXPECT_NE(ptr1.get(), ptr2.get());\n  }\n  EXPECT_EQ(pool_->created_count(), 2);\n  EXPECT_EQ(pool_->size(), 2);\n}\n\nTEST_F(CompressionContextPoolTest, testLifo) {\n  auto ptr1 = pool_->get();\n  auto ptr2 = pool_->get();\n  auto ptr3 = pool_->get();\n  auto t1 = ptr1.get();\n  auto t2 = ptr2.get();\n  auto t3 = ptr3.get();\n  EXPECT_NE(t1, t2);\n  EXPECT_NE(t1, t3);\n  EXPECT_NE(t2, t3);\n\n  ptr3.reset();\n  ptr2.reset();\n  ptr1.reset();\n\n  ptr1 = pool_->get();\n  EXPECT_EQ(ptr1.get(), t1);\n  ptr1.reset();\n\n  ptr1 = pool_->get();\n  EXPECT_EQ(ptr1.get(), t1);\n  ptr2 = pool_->get();\n  EXPECT_EQ(ptr2.get(), t2);\n  ptr1.reset();\n  ptr2.reset();\n\n  ptr1 = pool_->get();\n  EXPECT_EQ(ptr1.get(), t2);\n  ptr2 = pool_->get();\n  EXPECT_EQ(ptr2.get(), t1);\n  ptr3 = pool_->get();\n  EXPECT_EQ(ptr3.get(), t3);\n\n  EXPECT_EQ(pool_->created_count(), numFoos.load());\n}\n\nTEST_F(CompressionContextPoolTest, testExplicitCreatorDeleter) {\n  pool_ = std::make_unique<Pool>(FooCreator(), FooDeleter());\n  auto ptr = pool_->get();\n  EXPECT_TRUE(ptr);\n}\n\nTEST_F(CompressionContextPoolTest, testMultithread) {\n  constexpr size_t numThreads = 64 / (folly::kIsSanitizeThread ? 4 : 1);\n  constexpr size_t numIters = (1 << 14) / (folly::kIsSanitizeThread ? 4 : 1);\n  std::vector<std::thread> ts;\n  for (size_t i = 0; i < numThreads; i++) {\n    ts.emplace_back([&pool = *pool_]() {\n      for (size_t n = 0; n < numIters; n++) {\n        auto ref = pool.get();\n        ref.get();\n        ref.reset();\n      }\n    });\n  }\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  EXPECT_LE(numFoos.load(), numThreads);\n  EXPECT_LE(numDeleted.load(), 0);\n  EXPECT_EQ(pool_->created_count(), numFoos.load());\n  EXPECT_EQ(pool_->created_count(), pool_->size());\n}\n\nTEST_F(CompressionContextPoolTest, testBadCreate) {\n  BadPool pool;\n  EXPECT_THROW(pool.get(), std::bad_alloc);\n}\n\nTEST_F(CompressionContextPoolTest, testReset) {\n  Pool::Object* tmp;\n  {\n    auto ptr = pool_->get();\n    ptr->use();\n    tmp = ptr.get();\n  }\n  {\n    auto ptr = pool_->get();\n    ptr->use();\n    EXPECT_EQ(ptr.get(), tmp);\n  }\n}\n\nTEST_F(CompressionContextPoolTest, testFlush) {\n  pool_->get();\n  pool_->flush_deep();\n  pool_->get();\n  EXPECT_EQ(pool_->created_count(), 2);\n}\n\nstruct FooIncrementCallback {\n  void operator()() const { ++count; }\n\n  static size_t count;\n};\n\nsize_t FooIncrementCallback::count = 0;\n\nusing TestCallbackPool = CompressionContextPool<\n    Foo,\n    FooCreator,\n    FooDeleter,\n    FooResetter,\n    FooSizeof,\n    FooIncrementCallback>;\n\nTEST_F(CompressionContextPoolTest, testCallback) {\n  auto pool = std::make_unique<TestCallbackPool>();\n  for (size_t i = 0; i < COMPRESSION_CONTEXT_POOL_CALLBACK_INTERVAL; ++i) {\n    pool->get();\n  }\n  EXPECT_EQ(FooIncrementCallback::count, 1);\n}\n\nTEST_F(CompressionContextPoolTest, testBytesUsed) {\n  static_assert(sizeof(Foo) != 1);\n  {\n    auto ptr1 = pool_->get();\n  }\n  EXPECT_EQ(pool_->created_count(), 1);\n  EXPECT_EQ(pool_->size(), 1);\n  EXPECT_EQ(pool_->bytes_used(), sizeof(Foo));\n  {\n    auto ptr1 = pool_->get();\n    auto ptr2 = pool_->get();\n    EXPECT_NE(ptr1.get(), ptr2.get());\n  }\n  EXPECT_EQ(pool_->created_count(), 2);\n  EXPECT_EQ(pool_->size(), 2);\n  EXPECT_EQ(pool_->bytes_used(), 2 * sizeof(Foo));\n}\n\nclass CompressionCoreLocalContextPoolTest : public testing::Test {\n protected:\n  using Pool = CompressionCoreLocalContextPool<\n      Foo,\n      FooCreator,\n      FooDeleter,\n      FooResetter,\n      FooSizeof,\n      FooCallback>;\n\n  void SetUp() override { pool_ = std::make_unique<Pool>(); }\n\n  void TearDown() override { pool_.reset(); }\n\n  std::unique_ptr<Pool> pool_;\n};\n\nTEST_F(CompressionCoreLocalContextPoolTest, testGet) {\n  auto ptr = pool_->get();\n  EXPECT_TRUE(ptr);\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testSame) {\n  Pool::Object* tmp;\n  {\n    auto ptr = pool_->get();\n    tmp = ptr.get();\n  }\n  {\n    auto ptr = pool_->get();\n    EXPECT_EQ(tmp, ptr.get());\n  }\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testDifferent) {\n  auto ptr1 = pool_->get();\n  auto ptr2 = pool_->get();\n  EXPECT_NE(ptr1.get(), ptr2.get());\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testSwap) {\n  auto ptr1 = pool_->get();\n  auto ptr2 = pool_->get();\n  EXPECT_NE(ptr1.get(), ptr2.get());\n  auto tmp1 = ptr1.get();\n  auto tmp2 = ptr2.get();\n  ptr1.reset();\n  ptr2.reset();\n  ptr2 = pool_->get();\n  EXPECT_EQ(ptr2.get(), tmp2);\n  ptr1 = pool_->get();\n  EXPECT_EQ(ptr1.get(), tmp1);\n  ptr2.reset();\n  ptr1.reset();\n  ptr1 = pool_->get();\n  EXPECT_EQ(ptr1.get(), tmp1);\n  ptr2 = pool_->get();\n  EXPECT_EQ(ptr2.get(), tmp2);\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testFlush) {\n  pool_->get();\n  pool_->flush_deep();\n  pool_->get();\n  EXPECT_EQ(pool_->created_count(), 2);\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testMultithread) {\n  constexpr size_t numThreads = 64;\n  constexpr size_t numIters = 1 << 14;\n  std::vector<std::thread> ts;\n  for (size_t i = 0; i < numThreads; i++) {\n    ts.emplace_back([&pool = *pool_]() {\n      for (size_t n = 0; n < numIters; n++) {\n        auto ref = pool.get();\n        CHECK(ref);\n        ref.reset();\n      }\n    });\n  }\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  EXPECT_LE(numFoos.load(), numThreads);\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testReset) {\n  Pool::Object* tmp1;\n  Pool::Object* tmp2;\n  {\n    auto ptr1 = pool_->get();\n    ptr1->use();\n    tmp1 = ptr1.get();\n  }\n  {\n    auto ptr1 = pool_->get();\n    auto ptr2 = pool_->get();\n    ptr1->use();\n    ptr2->use();\n    EXPECT_EQ(ptr1.get(), tmp1);\n    tmp2 = ptr2.get();\n  }\n  {\n    auto ptr1 = pool_->get();\n    auto ptr2 = pool_->get();\n    ptr1->use();\n    ptr2->use();\n    EXPECT_EQ(ptr1.get(), tmp1);\n    EXPECT_EQ(ptr2.get(), tmp2);\n  }\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testSetSize) {\n  size_t numStripes = 6;\n  pool_->setSize(numStripes);\n  EXPECT_EQ(pool_->cacheSize(), numStripes);\n}\n\n// To be safe from SIOF, pools should be functional with 0 stripes.\nTEST_F(CompressionCoreLocalContextPoolTest, testGetZeroStripes) {\n  Pool pool(0);\n  auto ptr = pool.get();\n  EXPECT_NE(ptr, nullptr);\n}\n\nTEST_F(CompressionCoreLocalContextPoolTest, testBytesUsed) {\n  static_assert(sizeof(Foo) != 1);\n  {\n    auto ptr1 = pool_->get();\n  }\n  EXPECT_EQ(pool_->created_count(), 1);\n  EXPECT_EQ(pool_->bytes_used(), sizeof(Foo));\n  {\n    auto ptr1 = pool_->get();\n    auto ptr2 = pool_->get();\n    EXPECT_NE(ptr1.get(), ptr2.get());\n  }\n  EXPECT_EQ(pool_->created_count(), 2);\n  EXPECT_EQ(pool_->bytes_used(), 2 * sizeof(Foo));\n}\n\n#ifdef FOLLY_COMPRESSION_HAS_ZSTD_CONTEXT_POOL_SINGLETONS\n\nTEST(CompressionContextPoolSingletonsTest, testSingletons) {\n  EXPECT_NE(contexts::getZSTD_CCtx(), nullptr);\n  EXPECT_NE(contexts::getZSTD_DCtx(), nullptr);\n}\n\nTEST(CompressionContextPoolSingletonsTest, testSingletonsNull) {\n  EXPECT_EQ(contexts::getNULL_ZSTD_CCtx(), nullptr);\n  EXPECT_EQ(contexts::getNULL_ZSTD_DCtx(), nullptr);\n}\n\n#endif // FOLLY_COMPRESSION_HAS_ZSTD_CONTEXT_POOL_SINGLETONS\n\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/test/CompressionTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/Compression.h>\n\n#include <algorithm>\n#include <random>\n#include <set>\n#include <thread>\n#include <unordered_map>\n#include <utility>\n\n#include <glog/logging.h>\n\n#include <folly/Random.h>\n#include <folly/Varint.h>\n#include <folly/io/IOBufQueue.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAVE_LIBZSTD\n#include <zstd.h>\n\n#include <folly/compression/Zstd.h>\n#endif\n\n#if FOLLY_HAVE_LIBZ\n#include <folly/compression/Zlib.h>\n\nnamespace zlib = folly::compression::zlib;\n#endif\n\nnamespace folly {\nnamespace compression {\nnamespace test {\n\nclass DataHolder {\n public:\n  DataHolder(const DataHolder&) = delete;\n  DataHolder& operator=(const DataHolder&) = delete;\n  uint64_t hash(size_t size) const;\n  ByteRange data(size_t size) const;\n\n protected:\n  explicit DataHolder(size_t sizeLog2);\n  const size_t size_;\n  std::unique_ptr<uint8_t[]> data_;\n  mutable std::unordered_map<uint64_t, uint64_t> hashCache_;\n};\n\nDataHolder::DataHolder(size_t sizeLog2)\n    : size_(size_t(1) << sizeLog2), data_(new uint8_t[size_]) {}\n\nuint64_t DataHolder::hash(size_t size) const {\n  CHECK_LE(size, size_);\n  auto p = hashCache_.find(size);\n  if (p != hashCache_.end()) {\n    return p->second;\n  }\n\n  uint64_t h = folly::hash::fnv64_buf_BROKEN(data_.get(), size);\n  hashCache_[size] = h;\n  return h;\n}\n\nByteRange DataHolder::data(size_t size) const {\n  CHECK_LE(size, size_);\n  return ByteRange(data_.get(), size);\n}\n\nuint64_t hashIOBuf(const IOBuf* buf) {\n  uint64_t h = folly::hash::fnv64_hash_start;\n  for (auto& range : *buf) {\n    h = folly::hash::fnv64_buf_BROKEN(range.data(), range.size(), h);\n  }\n  return h;\n}\n\nclass RandomDataHolder : public DataHolder {\n public:\n  explicit RandomDataHolder(size_t sizeLog2);\n};\n\nRandomDataHolder::RandomDataHolder(size_t sizeLog2) : DataHolder(sizeLog2) {\n  memset(data_.get(), 0, size_);\n\n  static constexpr size_t numThreadsLog2 = 3;\n  static constexpr size_t numThreads = size_t(1) << numThreadsLog2;\n\n  uint32_t seed = randomNumberSeed();\n\n  std::vector<std::thread> threads;\n  threads.reserve(numThreads);\n  for (size_t t = 0; t < numThreads; ++t) {\n    threads.emplace_back([this, seed, t, sizeLog2] {\n      std::mt19937 rng(seed + t);\n      size_t countLog2 = sizeLog2 - numThreadsLog2;\n      size_t start = size_t(t) << countLog2;\n      for (size_t i = 0; i < countLog2; ++i) {\n        this->data_[start + i] = static_cast<uint8_t>(rng());\n      }\n    });\n  }\n\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\nclass ConstantDataHolder : public DataHolder {\n public:\n  explicit ConstantDataHolder(size_t sizeLog2);\n};\n\nConstantDataHolder::ConstantDataHolder(size_t sizeLog2) : DataHolder(sizeLog2) {\n  memset(data_.get(), 'a', size_);\n}\n\nconstexpr size_t dataSizeLog2 = 27; // 128MiB\nRandomDataHolder randomDataHolder(dataSizeLog2);\nConstantDataHolder constantDataHolder(dataSizeLog2);\n\n// The intersection of the provided codecs & those that are compiled in.\nstatic std::vector<CodecType> supportedCodecs(std::vector<CodecType> const& v) {\n  std::vector<CodecType> supported;\n\n  std::copy_if(\n      std::begin(v), std::end(v), std::back_inserter(supported), hasCodec);\n\n  return supported;\n}\n\n// All compiled-in compression codecs.\nstatic std::vector<CodecType> availableCodecs() {\n  std::vector<CodecType> codecs;\n\n  for (size_t i = 0; i < static_cast<size_t>(CodecType::NUM_CODEC_TYPES); ++i) {\n    auto type = static_cast<CodecType>(i);\n    if (hasCodec(type)) {\n      codecs.push_back(type);\n    }\n  }\n\n  return codecs;\n}\n\nstatic std::vector<CodecType> availableStreamCodecs() {\n  std::vector<CodecType> codecs;\n\n  for (size_t i = 0; i < static_cast<size_t>(CodecType::NUM_CODEC_TYPES); ++i) {\n    auto type = static_cast<CodecType>(i);\n    if (hasStreamCodec(type)) {\n      codecs.push_back(type);\n    }\n  }\n\n  return codecs;\n}\n\nTEST(CompressionTestNeedsUncompressedLength, Simple) {\n  static const struct {\n    CodecType type;\n    bool needsUncompressedLength;\n  } expectations[] = {\n      {CodecType::NO_COMPRESSION, false},\n      {CodecType::LZ4, true},\n      {CodecType::SNAPPY, false},\n      {CodecType::ZLIB, false},\n      {CodecType::LZ4_VARINT_SIZE, false},\n      {CodecType::LZMA2, false},\n      {CodecType::LZMA2_VARINT_SIZE, false},\n      {CodecType::ZSTD, false},\n      {CodecType::GZIP, false},\n      {CodecType::LZ4_FRAME, false},\n      {CodecType::BZIP2, false},\n  };\n\n  for (auto const& test : expectations) {\n    if (hasCodec(test.type)) {\n      EXPECT_EQ(\n          getCodec(test.type)->needsUncompressedLength(),\n          test.needsUncompressedLength);\n    }\n  }\n}\n\nclass CompressionTest\n    : public testing::TestWithParam<std::tuple<int, int, CodecType>> {\n protected:\n  void SetUp() override {\n    auto tup = GetParam();\n    int lengthLog = std::get<0>(tup);\n    // Small hack to test empty data\n    uncompressedLength_ = (lengthLog < 0) ? 0 : uint64_t(1) << std::get<0>(tup);\n    chunks_ = std::get<1>(tup);\n    codec_ = getCodec(std::get<2>(tup));\n  }\n\n  void runSimpleIOBufTest(const DataHolder& dh);\n\n  void runSimpleStringTest(const DataHolder& dh);\n\n private:\n  std::unique_ptr<IOBuf> split(std::unique_ptr<IOBuf> data) const;\n\n  uint64_t uncompressedLength_;\n  size_t chunks_;\n  std::unique_ptr<Codec> codec_;\n};\n\nvoid CompressionTest::runSimpleIOBufTest(const DataHolder& dh) {\n  const auto original = split(IOBuf::wrapBuffer(dh.data(uncompressedLength_)));\n  const auto compressed = split(codec_->compress(original.get()));\n  EXPECT_LE(\n      compressed->computeChainDataLength(),\n      codec_->maxCompressedLength(uncompressedLength_));\n  if (!codec_->needsUncompressedLength()) {\n    auto uncompressed = codec_->uncompress(compressed.get());\n    EXPECT_EQ(uncompressedLength_, uncompressed->computeChainDataLength());\n    EXPECT_EQ(dh.hash(uncompressedLength_), hashIOBuf(uncompressed.get()));\n  }\n  {\n    auto uncompressed =\n        codec_->uncompress(compressed.get(), uncompressedLength_);\n    EXPECT_EQ(uncompressedLength_, uncompressed->computeChainDataLength());\n    EXPECT_EQ(dh.hash(uncompressedLength_), hashIOBuf(uncompressed.get()));\n  }\n}\n\nvoid CompressionTest::runSimpleStringTest(const DataHolder& dh) {\n  const auto original = std::string(\n      reinterpret_cast<const char*>(dh.data(uncompressedLength_).data()),\n      uncompressedLength_);\n  const auto compressed = codec_->compress(original);\n  EXPECT_LE(\n      compressed.length(), codec_->maxCompressedLength(uncompressedLength_));\n\n  if (!codec_->needsUncompressedLength()) {\n    auto uncompressed = codec_->uncompress(compressed);\n    EXPECT_EQ(uncompressedLength_, uncompressed.length());\n    EXPECT_EQ(uncompressed, original);\n  }\n  {\n    auto uncompressed = codec_->uncompress(compressed, uncompressedLength_);\n    EXPECT_EQ(uncompressedLength_, uncompressed.length());\n    EXPECT_EQ(uncompressed, original);\n  }\n}\n\n// Uniformly split data into (potentially empty) chunks.\nstd::unique_ptr<IOBuf> CompressionTest::split(\n    std::unique_ptr<IOBuf> data) const {\n  if (data->isChained()) {\n    data->coalesce();\n  }\n\n  const size_t size = data->computeChainDataLength();\n\n  std::multiset<size_t> splits;\n  for (size_t i = 1; i < chunks_; ++i) {\n    splits.insert(Random::rand64(size));\n  }\n\n  folly::IOBufQueue result;\n\n  size_t offset = 0;\n  for (size_t split : splits) {\n    result.append(IOBuf::copyBuffer(data->data() + offset, split - offset));\n    offset = split;\n  }\n  result.append(IOBuf::copyBuffer(data->data() + offset, size - offset));\n\n  return result.move();\n}\n\nTEST_P(CompressionTest, RandomData) {\n  runSimpleIOBufTest(randomDataHolder);\n}\n\nTEST_P(CompressionTest, ConstantData) {\n  runSimpleIOBufTest(constantDataHolder);\n}\n\nTEST_P(CompressionTest, RandomDataString) {\n  runSimpleStringTest(randomDataHolder);\n}\n\nTEST_P(CompressionTest, ConstantDataString) {\n  runSimpleStringTest(constantDataHolder);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    CompressionTest,\n    CompressionTest,\n    testing::Combine(\n        testing::Values(-1, 0, 1, 12, 22, 25, 27),\n        testing::Values(1, 2, 3, 8, 65),\n        testing::ValuesIn(availableCodecs())));\n\nclass CompressionVarintTest\n    : public testing::TestWithParam<std::tuple<int, CodecType>> {\n protected:\n  void SetUp() override {\n    auto tup = GetParam();\n    uncompressedLength_ = uint64_t(1) << std::get<0>(tup);\n    codec_ = getCodec(std::get<1>(tup));\n  }\n\n  void runSimpleTest(const DataHolder& dh);\n\n  uint64_t uncompressedLength_;\n  std::unique_ptr<Codec> codec_;\n};\n\ninline uint64_t oneBasedMsbPos(uint64_t number) {\n  uint64_t pos = 0;\n  for (; number > 0; ++pos, number >>= 1) {\n  }\n  return pos;\n}\n\nvoid CompressionVarintTest::runSimpleTest(const DataHolder& dh) {\n  auto original = IOBuf::wrapBuffer(dh.data(uncompressedLength_));\n  auto compressed = codec_->compress(original.get());\n  auto breakPoint =\n      1ULL +\n      Random::rand64(\n          std::max(uint64_t(9), oneBasedMsbPos(uncompressedLength_)) / 9ULL);\n  auto tinyBuf = IOBuf::copyBuffer(\n      compressed->data(), std::min<size_t>(compressed->length(), breakPoint));\n  compressed->trimStart(breakPoint);\n  tinyBuf->prependChain(std::move(compressed));\n  compressed = std::move(tinyBuf);\n\n  auto uncompressed = codec_->uncompress(compressed.get());\n\n  EXPECT_EQ(uncompressedLength_, uncompressed->computeChainDataLength());\n  EXPECT_EQ(dh.hash(uncompressedLength_), hashIOBuf(uncompressed.get()));\n}\n\nTEST_P(CompressionVarintTest, RandomData) {\n  runSimpleTest(randomDataHolder);\n}\n\nTEST_P(CompressionVarintTest, ConstantData) {\n  runSimpleTest(constantDataHolder);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    CompressionVarintTest,\n    CompressionVarintTest,\n    testing::Combine(\n        testing::Values(0, 1, 12, 22, 25, 27),\n        testing::ValuesIn(supportedCodecs({\n            CodecType::LZ4_VARINT_SIZE,\n            CodecType::LZMA2_VARINT_SIZE,\n        }))));\n\nTEST(LZMATest, UncompressBadVarint) {\n  if (hasStreamCodec(CodecType::LZMA2_VARINT_SIZE)) {\n    std::string const str(kMaxVarintLength64 * 2, '\\xff');\n    ByteRange input((folly::StringPiece(str)));\n    auto codec = getStreamCodec(CodecType::LZMA2_VARINT_SIZE);\n    auto buffer = IOBuf::create(16);\n    buffer->append(buffer->capacity());\n    MutableByteRange output{buffer->writableData(), buffer->length()};\n    EXPECT_THROW(codec->uncompressStream(input, output), std::runtime_error);\n  }\n}\n\nclass CompressionCorruptionTest : public testing::TestWithParam<CodecType> {\n protected:\n  void SetUp() override { codec_ = getCodec(GetParam()); }\n\n  void runSimpleTest(const DataHolder& dh);\n\n  std::unique_ptr<Codec> codec_;\n};\n\nvoid CompressionCorruptionTest::runSimpleTest(const DataHolder& dh) {\n  constexpr uint64_t uncompressedLength = 42;\n  auto original = IOBuf::wrapBuffer(dh.data(uncompressedLength));\n  auto compressed = codec_->compress(original.get());\n\n  if (!codec_->needsUncompressedLength()) {\n    auto uncompressed = codec_->uncompress(compressed.get());\n    EXPECT_EQ(uncompressedLength, uncompressed->computeChainDataLength());\n    EXPECT_EQ(dh.hash(uncompressedLength), hashIOBuf(uncompressed.get()));\n  }\n  {\n    auto uncompressed =\n        codec_->uncompress(compressed.get(), uncompressedLength);\n    EXPECT_EQ(uncompressedLength, uncompressed->computeChainDataLength());\n    EXPECT_EQ(dh.hash(uncompressedLength), hashIOBuf(uncompressed.get()));\n  }\n\n  EXPECT_THROW(\n      codec_->uncompress(compressed.get(), uncompressedLength + 1),\n      std::runtime_error);\n\n  auto corrupted = compressed->clone();\n  corrupted->unshare();\n  // Truncate the last character\n  corrupted->prev()->trimEnd(1);\n  if (!codec_->needsUncompressedLength()) {\n    EXPECT_THROW(codec_->uncompress(corrupted.get()), std::runtime_error);\n  }\n\n  EXPECT_THROW(\n      codec_->uncompress(corrupted.get(), uncompressedLength),\n      std::runtime_error);\n\n  corrupted = compressed->clone();\n  corrupted->unshare();\n  // Corrupt the first character\n  ++(corrupted->writableData()[0]);\n\n  if (!codec_->needsUncompressedLength()) {\n    EXPECT_THROW(codec_->uncompress(corrupted.get()), std::runtime_error);\n  }\n\n  EXPECT_THROW(\n      codec_->uncompress(corrupted.get(), uncompressedLength),\n      std::runtime_error);\n}\n\nTEST_P(CompressionCorruptionTest, RandomData) {\n  runSimpleTest(randomDataHolder);\n}\n\nTEST_P(CompressionCorruptionTest, ConstantData) {\n  runSimpleTest(constantDataHolder);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    CompressionCorruptionTest,\n    CompressionCorruptionTest,\n    testing::ValuesIn(\n        // NO_COMPRESSION can't detect corruption\n        // LZ4 can't detect corruption reliably (sigh)\n        supportedCodecs({\n            CodecType::SNAPPY,\n            CodecType::ZLIB,\n            CodecType::LZMA2,\n            CodecType::ZSTD,\n            CodecType::LZ4_FRAME,\n            CodecType::BZIP2,\n        })));\n\nstatic bool codecHasFlush(CodecType type) {\n  return type != CodecType::BZIP2;\n}\n\nclass StreamingUnitTest : public testing::TestWithParam<CodecType> {\n protected:\n  void SetUp() override { codec_ = getStreamCodec(GetParam()); }\n\n  bool hasFlush() const { return codecHasFlush(GetParam()); }\n\n  std::unique_ptr<StreamCodec> codec_;\n};\n\nTEST(StreamingUnitTest, needsDataLength) {\n  static const struct {\n    CodecType type;\n    bool needsDataLength;\n  } expectations[] = {\n      {CodecType::ZLIB, false},\n      {CodecType::GZIP, false},\n      {CodecType::LZMA2, false},\n      {CodecType::LZMA2_VARINT_SIZE, true},\n      {CodecType::ZSTD, false},\n  };\n\n  for (auto const& test : expectations) {\n    if (hasStreamCodec(test.type)) {\n      EXPECT_EQ(\n          getStreamCodec(test.type)->needsDataLength(), test.needsDataLength);\n    }\n  }\n}\n\nTEST_P(StreamingUnitTest, maxCompressedLength) {\n  for (uint64_t const length : {1, 10, 100, 1000, 10000, 100000, 1000000}) {\n    EXPECT_GE(codec_->maxCompressedLength(length), length);\n  }\n}\n\nTEST_P(StreamingUnitTest, getUncompressedLength) {\n  auto const empty = IOBuf::create(0);\n  EXPECT_EQ(uint64_t(0), codec_->getUncompressedLength(\"\"));\n  EXPECT_EQ(uint64_t(0), codec_->getUncompressedLength(empty.get()));\n  EXPECT_EQ(uint64_t(0), codec_->getUncompressedLength(\"\"));\n  EXPECT_EQ(uint64_t(0), codec_->getUncompressedLength(empty.get(), 0));\n  EXPECT_ANY_THROW(codec_->getUncompressedLength(empty.get(), 1));\n\n  auto const data = IOBuf::wrapBuffer(randomDataHolder.data(100));\n  auto const compressed = codec_->compress(data.get());\n  auto const compressedString =\n      StringPiece(ByteRange(data->data(), data->length()));\n\n  if (auto const length = codec_->getUncompressedLength(data.get())) {\n    EXPECT_EQ(100, *length);\n  }\n  if (auto const length = codec_->getUncompressedLength(compressedString)) {\n    EXPECT_EQ(100, *length);\n  }\n  EXPECT_EQ(uint64_t(100), codec_->getUncompressedLength(data.get(), 100));\n  EXPECT_EQ(\n      uint64_t(100), codec_->getUncompressedLength(compressedString, 100));\n  // If the uncompressed length is stored in the frame, then make sure it throws\n  // when it is given the wrong length.\n  if (codec_->getUncompressedLength(data.get()) == uint64_t(100)) {\n    EXPECT_ANY_THROW(codec_->getUncompressedLength(data.get(), 200));\n  }\n  if (codec_->getUncompressedLength(compressedString) == uint64_t(100)) {\n    EXPECT_ANY_THROW(codec_->getUncompressedLength(compressedString, 200));\n  }\n}\n\nTEST_P(StreamingUnitTest, emptyData) {\n  ByteRange input{};\n  auto buffer = IOBuf::create(codec_->maxCompressedLength(0));\n  buffer->append(buffer->capacity());\n  MutableByteRange output;\n\n  // Test compressing empty data in one pass\n  if (!codec_->needsDataLength()) {\n    output = {buffer->writableData(), buffer->length()};\n    EXPECT_TRUE(\n        codec_->compressStream(input, output, StreamCodec::FlushOp::END));\n  }\n  codec_->resetStream(0);\n  output = {buffer->writableData(), buffer->length()};\n  EXPECT_TRUE(codec_->compressStream(input, output, StreamCodec::FlushOp::END));\n\n  // Test uncompressing the compressed empty data is equivalent to the empty\n  // string\n  {\n    size_t compressedSize = buffer->length() - output.size();\n    auto const compressed =\n        IOBuf::copyBuffer(buffer->writableData(), compressedSize);\n    auto inputRange = compressed->coalesce();\n    codec_->resetStream(0);\n    output = {buffer->writableData(), buffer->length()};\n    EXPECT_TRUE(codec_->uncompressStream(\n        inputRange, output, StreamCodec::FlushOp::END));\n    EXPECT_EQ(output.size(), buffer->length());\n  }\n\n  // Test compressing empty data with multiple calls to compressStream()\n  {\n    auto largeBuffer = IOBuf::create(codec_->maxCompressedLength(0) * 2);\n    largeBuffer->append(largeBuffer->capacity());\n    codec_->resetStream(0);\n    output = {largeBuffer->writableData(), largeBuffer->length()};\n    EXPECT_FALSE(codec_->compressStream(input, output));\n    if (hasFlush()) {\n      EXPECT_TRUE(\n          codec_->compressStream(input, output, StreamCodec::FlushOp::FLUSH));\n    }\n    EXPECT_TRUE(\n        codec_->compressStream(input, output, StreamCodec::FlushOp::END));\n  }\n\n  // Test uncompressing empty data\n  output = {};\n  codec_->resetStream();\n  EXPECT_TRUE(codec_->uncompressStream(input, output));\n  if (hasFlush()) {\n    codec_->resetStream();\n    EXPECT_TRUE(\n        codec_->uncompressStream(input, output, StreamCodec::FlushOp::FLUSH));\n  }\n  codec_->resetStream();\n  EXPECT_TRUE(\n      codec_->uncompressStream(input, output, StreamCodec::FlushOp::END));\n  codec_->resetStream(0);\n  EXPECT_TRUE(codec_->uncompressStream(input, output));\n  if (hasFlush()) {\n    codec_->resetStream(0);\n    EXPECT_TRUE(\n        codec_->uncompressStream(input, output, StreamCodec::FlushOp::FLUSH));\n  }\n  codec_->resetStream(0);\n  EXPECT_TRUE(\n      codec_->uncompressStream(input, output, StreamCodec::FlushOp::END));\n}\n\nTEST_P(StreamingUnitTest, noForwardProgress) {\n  auto inBuffer = IOBuf::create(2);\n  inBuffer->writableData()[0] = 'a';\n  inBuffer->writableData()[1] = 'a';\n  inBuffer->append(2);\n  const auto compressed = codec_->compress(inBuffer.get());\n  auto outBuffer = IOBuf::create(codec_->maxCompressedLength(2));\n\n  ByteRange emptyInput;\n  MutableByteRange emptyOutput;\n\n  const std::array<StreamCodec::FlushOp, 3> flushOps = {{\n      StreamCodec::FlushOp::NONE,\n      StreamCodec::FlushOp::FLUSH,\n      StreamCodec::FlushOp::END,\n  }};\n\n  // No progress is not okay twice in a row for all flush operations when\n  // compressing\n  for (const auto flushOp : flushOps) {\n    if (flushOp == StreamCodec::FlushOp::FLUSH && !hasFlush()) {\n      continue;\n    }\n    if (codec_->needsDataLength()) {\n      codec_->resetStream(inBuffer->computeChainDataLength());\n    } else {\n      codec_->resetStream();\n    }\n    auto input = inBuffer->coalesce();\n    MutableByteRange output = {\n        outBuffer->writableTail(), outBuffer->tailroom()};\n    // Compress some data to avoid empty data special casing\n    while (!input.empty()) {\n      codec_->compressStream(input, output);\n    }\n    EXPECT_FALSE(codec_->compressStream(emptyInput, emptyOutput, flushOp));\n    EXPECT_THROW(\n        codec_->compressStream(emptyInput, emptyOutput, flushOp),\n        std::runtime_error);\n  }\n\n  // No progress is not okay twice in a row for all flush operations when\n  // uncompressing\n  for (const auto flushOp : flushOps) {\n    if (flushOp == StreamCodec::FlushOp::FLUSH && !hasFlush()) {\n      continue;\n    }\n    codec_->resetStream();\n    auto input = compressed->coalesce();\n    // Remove the last byte so the operation is incomplete\n    input.uncheckedSubtract(1);\n    MutableByteRange output = {inBuffer->writableData(), inBuffer->length()};\n    // Uncompress some data to avoid empty data special casing\n    while (!input.empty()) {\n      EXPECT_FALSE(codec_->uncompressStream(input, output));\n    }\n    EXPECT_FALSE(codec_->uncompressStream(emptyInput, emptyOutput, flushOp));\n    EXPECT_THROW(\n        codec_->uncompressStream(emptyInput, emptyOutput, flushOp),\n        std::runtime_error);\n  }\n}\n\nTEST_P(StreamingUnitTest, stateTransitions) {\n  auto inBuffer = IOBuf::create(2);\n  inBuffer->writableData()[0] = 'a';\n  inBuffer->writableData()[1] = 'a';\n  inBuffer->append(2);\n  auto compressed = codec_->compress(inBuffer.get());\n  ByteRange const in = compressed->coalesce();\n  auto outBuffer = IOBuf::create(codec_->maxCompressedLength(in.size()));\n  MutableByteRange const out{outBuffer->writableTail(), outBuffer->tailroom()};\n\n  auto compress = [&](StreamCodec::FlushOp flushOp = StreamCodec::FlushOp::NONE,\n                      bool empty = false) {\n    auto input = in;\n    auto output = empty ? MutableByteRange{} : out;\n    return codec_->compressStream(input, output, flushOp);\n  };\n  auto compress_all =\n      [&](bool expect,\n          StreamCodec::FlushOp flushOp = StreamCodec::FlushOp::NONE,\n          bool empty = false) {\n        auto input = in;\n        auto output = empty ? MutableByteRange{} : out;\n        while (!input.empty()) {\n          if (expect) {\n            EXPECT_TRUE(codec_->compressStream(input, output, flushOp));\n          } else {\n            EXPECT_FALSE(codec_->compressStream(input, output, flushOp));\n          }\n        }\n      };\n  auto uncompress =\n      [&](StreamCodec::FlushOp flushOp = StreamCodec::FlushOp::NONE,\n          bool empty = false) {\n        auto input = in;\n        auto output = empty ? MutableByteRange{} : out;\n        return codec_->uncompressStream(input, output, flushOp);\n      };\n\n  // compression flow\n  if (!codec_->needsDataLength()) {\n    codec_->resetStream();\n    EXPECT_FALSE(compress());\n    EXPECT_FALSE(compress());\n    if (hasFlush()) {\n      EXPECT_TRUE(compress(StreamCodec::FlushOp::FLUSH));\n    }\n    EXPECT_FALSE(compress());\n    EXPECT_TRUE(compress(StreamCodec::FlushOp::END));\n  }\n  codec_->resetStream(in.size() * 5);\n  compress_all(false);\n  compress_all(false);\n  if (hasFlush()) {\n    compress_all(true, StreamCodec::FlushOp::FLUSH);\n  }\n  compress_all(false);\n  compress_all(true, StreamCodec::FlushOp::END);\n\n  // uncompression flow\n  codec_->resetStream();\n  EXPECT_FALSE(uncompress(StreamCodec::FlushOp::NONE, true));\n  if (hasFlush()) {\n    codec_->resetStream();\n    EXPECT_FALSE(uncompress(StreamCodec::FlushOp::FLUSH, true));\n  }\n  codec_->resetStream();\n  EXPECT_FALSE(uncompress(StreamCodec::FlushOp::NONE, true));\n  codec_->resetStream();\n  EXPECT_FALSE(uncompress(StreamCodec::FlushOp::NONE, true));\n  if (hasFlush()) {\n    codec_->resetStream();\n    EXPECT_TRUE(uncompress(StreamCodec::FlushOp::FLUSH));\n  }\n  // compress -> uncompress\n  codec_->resetStream(in.size());\n  EXPECT_FALSE(compress());\n  EXPECT_THROW(uncompress(), std::logic_error);\n  // uncompress -> compress\n  codec_->resetStream(inBuffer->computeChainDataLength());\n  EXPECT_TRUE(uncompress(StreamCodec::FlushOp::NONE));\n  EXPECT_THROW(compress(), std::logic_error);\n  // end -> compress\n  if (!codec_->needsDataLength()) {\n    codec_->resetStream();\n    EXPECT_FALSE(compress());\n    EXPECT_TRUE(compress(StreamCodec::FlushOp::END));\n    EXPECT_THROW(compress(), std::logic_error);\n  }\n  codec_->resetStream(in.size() * 2);\n  compress_all(false);\n  compress_all(true, StreamCodec::FlushOp::END);\n  EXPECT_THROW(compress(), std::logic_error);\n  // end -> uncompress\n  codec_->resetStream();\n  EXPECT_TRUE(uncompress(StreamCodec::FlushOp::END));\n  EXPECT_THROW(uncompress(), std::logic_error);\n  // flush -> compress\n  if (hasFlush()) {\n    codec_->resetStream(in.size());\n    EXPECT_FALSE(compress(StreamCodec::FlushOp::FLUSH, true));\n    EXPECT_THROW(compress(), std::logic_error);\n  }\n  // flush -> end\n  if (hasFlush()) {\n    codec_->resetStream(in.size());\n    EXPECT_FALSE(compress(StreamCodec::FlushOp::FLUSH, true));\n    EXPECT_THROW(compress(StreamCodec::FlushOp::END), std::logic_error);\n  }\n  // undefined -> compress\n  codec_->compress(inBuffer.get());\n  EXPECT_THROW(compress(), std::logic_error);\n  codec_->uncompress(compressed.get(), inBuffer->computeChainDataLength());\n  EXPECT_THROW(compress(), std::logic_error);\n  // undefined -> undefined\n  codec_->uncompress(compressed.get());\n  codec_->compress(inBuffer.get());\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    StreamingUnitTest,\n    StreamingUnitTest,\n    testing::ValuesIn(availableStreamCodecs()));\n\nclass StreamingCompressionTest\n    : public testing::TestWithParam<std::tuple<int, int, CodecType>> {\n protected:\n  bool hasFlush() const { return codecHasFlush(std::get<2>(GetParam())); }\n\n  void SetUp() override {\n    auto const tup = GetParam();\n    uncompressedLength_ = uint64_t(1) << std::get<0>(tup);\n    chunkSize_ = size_t(1) << std::get<1>(tup);\n    codec_ = getStreamCodec(std::get<2>(tup));\n  }\n\n  void runResetStreamTest(DataHolder const& dh);\n  void runCompressStreamTest(DataHolder const& dh);\n  void runUncompressStreamTest(DataHolder const& dh);\n  void runFlushNoneTest(DataHolder const& dh);\n  void runFlushTest(DataHolder const& dh);\n\n private:\n  std::vector<ByteRange> split(ByteRange data) const;\n\n  uint64_t uncompressedLength_;\n  size_t chunkSize_;\n  std::unique_ptr<StreamCodec> codec_;\n};\n\nstd::vector<ByteRange> StreamingCompressionTest::split(ByteRange data) const {\n  size_t const pieces = std::max<size_t>(1, data.size() / chunkSize_);\n  std::vector<ByteRange> result;\n  result.reserve(pieces + 1);\n  while (!data.empty()) {\n    size_t const pieceSize = std::min(data.size(), chunkSize_);\n    result.push_back(data.subpiece(0, pieceSize));\n    data.uncheckedAdvance(pieceSize);\n  }\n  return result;\n}\n\nstatic std::unique_ptr<IOBuf> compressSome(\n    StreamCodec* codec,\n    ByteRange data,\n    uint64_t bufferSize,\n    StreamCodec::FlushOp flush) {\n  bool finished;\n  IOBufQueue queue;\n  for (;;) {\n    auto buffer = IOBuf::create(bufferSize);\n    buffer->append(buffer->capacity());\n    MutableByteRange output{buffer->writableData(), buffer->length()};\n\n    finished = codec->compressStream(data, output, flush);\n    buffer->trimEnd(output.size());\n    queue.append(std::move(buffer));\n\n    if (flush == StreamCodec::FlushOp::NONE) {\n      EXPECT_FALSE(finished)\n          << \"compressStream() must return false if flush == NONE\";\n      if (data.empty()) {\n        break;\n      }\n    }\n\n    if (finished) {\n      break;\n    }\n  }\n  EXPECT_TRUE(data.empty());\n  return queue.move();\n}\n\nstatic std::pair<bool, std::unique_ptr<IOBuf>> uncompressSome(\n    StreamCodec* codec,\n    ByteRange& data,\n    uint64_t bufferSize,\n    StreamCodec::FlushOp flush) {\n  bool result;\n  IOBufQueue queue;\n  do {\n    auto buffer = IOBuf::create(bufferSize);\n    buffer->append(buffer->capacity());\n    MutableByteRange output{buffer->writableData(), buffer->length()};\n\n    result = codec->uncompressStream(data, output, flush);\n    buffer->trimEnd(output.size());\n    queue.append(std::move(buffer));\n\n  } while (queue.tailroom() == 0 && !result);\n  return std::make_pair(result, queue.move());\n}\n\nvoid StreamingCompressionTest::runResetStreamTest(DataHolder const& dh) {\n  auto const input = dh.data(uncompressedLength_);\n  // Compress some but leave state unclean\n  codec_->resetStream(uncompressedLength_);\n  compressSome(codec_.get(), input, chunkSize_, StreamCodec::FlushOp::NONE);\n  // Reset stream and compress all\n  if (codec_->needsDataLength()) {\n    codec_->resetStream(uncompressedLength_);\n  } else {\n    codec_->resetStream();\n  }\n  auto compressed =\n      compressSome(codec_.get(), input, chunkSize_, StreamCodec::FlushOp::END);\n  auto const uncompressed = codec_->uncompress(compressed.get(), input.size());\n  EXPECT_EQ(dh.hash(uncompressedLength_), hashIOBuf(uncompressed.get()));\n}\n\nTEST_P(StreamingCompressionTest, resetStream) {\n  runResetStreamTest(constantDataHolder);\n  runResetStreamTest(randomDataHolder);\n}\n\nvoid StreamingCompressionTest::runCompressStreamTest(\n    const folly::compression::test::DataHolder& dh) {\n  auto const inputs = split(dh.data(uncompressedLength_));\n\n  IOBufQueue queue;\n  codec_->resetStream(uncompressedLength_);\n  // Compress many inputs in a row\n  for (auto const input : inputs) {\n    queue.append(compressSome(\n        codec_.get(), input, chunkSize_, StreamCodec::FlushOp::NONE));\n  }\n  // Finish the operation with empty input.\n  ByteRange empty;\n  queue.append(\n      compressSome(codec_.get(), empty, chunkSize_, StreamCodec::FlushOp::END));\n\n  auto const uncompressed = codec_->uncompress(queue.front());\n  EXPECT_EQ(dh.hash(uncompressedLength_), hashIOBuf(uncompressed.get()));\n}\n\nTEST_P(StreamingCompressionTest, compressStream) {\n  runCompressStreamTest(constantDataHolder);\n  runCompressStreamTest(randomDataHolder);\n}\n\nvoid StreamingCompressionTest::runUncompressStreamTest(\n    const folly::compression::test::DataHolder& dh) {\n  const auto flush =\n      hasFlush() ? StreamCodec::FlushOp::FLUSH : StreamCodec::FlushOp::NONE;\n  auto const data = IOBuf::wrapBuffer(dh.data(uncompressedLength_));\n  // Concatenate 3 compressed frames in a row\n  auto compressed = codec_->compress(data.get());\n  compressed->prependChain(codec_->compress(data.get()));\n  compressed->prependChain(codec_->compress(data.get()));\n  // Pass all 3 compressed frames in one input buffer\n  auto input = compressed->coalesce();\n  // Uncompress the first frame\n  codec_->resetStream(data->computeChainDataLength());\n  {\n    auto const result = uncompressSome(codec_.get(), input, chunkSize_, flush);\n    ASSERT_TRUE(result.first);\n    ASSERT_EQ(hashIOBuf(data.get()), hashIOBuf(result.second.get()));\n  }\n  // Uncompress the second frame\n  codec_->resetStream();\n  {\n    auto const result = uncompressSome(\n        codec_.get(), input, chunkSize_, StreamCodec::FlushOp::END);\n    ASSERT_TRUE(result.first);\n    ASSERT_EQ(hashIOBuf(data.get()), hashIOBuf(result.second.get()));\n  }\n  // Uncompress the third frame\n  codec_->resetStream();\n  {\n    auto const result = uncompressSome(codec_.get(), input, chunkSize_, flush);\n    ASSERT_TRUE(result.first);\n    ASSERT_EQ(hashIOBuf(data.get()), hashIOBuf(result.second.get()));\n  }\n  EXPECT_TRUE(input.empty());\n}\n\nTEST_P(StreamingCompressionTest, uncompressStream) {\n  runUncompressStreamTest(constantDataHolder);\n  runUncompressStreamTest(randomDataHolder);\n}\n\nvoid StreamingCompressionTest::runFlushTest(DataHolder const& dh) {\n  auto const inputs = split(dh.data(uncompressedLength_));\n  auto uncodec = getStreamCodec(codec_->type());\n\n  if (codec_->needsDataLength()) {\n    codec_->resetStream(uncompressedLength_);\n  } else {\n    codec_->resetStream();\n  }\n  for (auto input : inputs) {\n    // Compress some data and flush the stream\n    auto compressed = compressSome(\n        codec_.get(), input, chunkSize_, StreamCodec::FlushOp::FLUSH);\n    auto compressedRange = compressed->coalesce();\n    // Uncompress the compressed data\n    auto result = uncompressSome(\n        uncodec.get(),\n        compressedRange,\n        chunkSize_,\n        StreamCodec::FlushOp::FLUSH);\n    // All compressed data should have been consumed\n    EXPECT_TRUE(compressedRange.empty());\n    // The frame isn't complete\n    EXPECT_FALSE(result.first);\n    // The uncompressed data should be exactly the input data\n    EXPECT_EQ(input.size(), result.second->computeChainDataLength());\n    auto const data = IOBuf::wrapBuffer(input);\n    EXPECT_EQ(hashIOBuf(data.get()), hashIOBuf(result.second.get()));\n  }\n}\n\nTEST_P(StreamingCompressionTest, testFlush) {\n  if (!hasFlush()) {\n    return;\n  }\n  runFlushTest(constantDataHolder);\n  runFlushTest(randomDataHolder);\n}\n\nvoid StreamingCompressionTest::runFlushNoneTest(DataHolder const& dh) {\n  auto const inputs = split(dh.data(uncompressedLength_));\n  auto uncodec = getStreamCodec(codec_->type());\n\n  IOBufQueue queue;\n  if (codec_->needsDataLength()) {\n    codec_->resetStream(uncompressedLength_);\n  } else {\n    codec_->resetStream();\n  }\n  for (auto input : inputs) {\n    // Compress some data without flushing\n    // This tests that we never return true when flush == NONE\n    auto compressed = compressSome(\n        codec_.get(), input, chunkSize_, StreamCodec::FlushOp::NONE);\n    auto compressedRange = compressed->coalesce();\n    if (!compressedRange.empty()) {\n      // Uncompress the compressed data\n      auto [finished, uncompressed] = uncompressSome(\n          uncodec.get(),\n          compressedRange,\n          chunkSize_,\n          StreamCodec::FlushOp::NONE);\n      // All compressed data should have been consumed\n      ASSERT_TRUE(compressedRange.empty());\n      // The frame isn't complete\n      ASSERT_FALSE(finished);\n      queue.append(std::move(uncompressed));\n    }\n  }\n  auto compressed =\n      compressSome(codec_.get(), {}, chunkSize_, StreamCodec::FlushOp::END);\n  auto compressedRange = compressed->coalesce();\n  auto [finished, uncompressed] = uncompressSome(\n      uncodec.get(), compressedRange, chunkSize_, StreamCodec::FlushOp::END);\n  // All compressed data should have been consumed\n  ASSERT_TRUE(compressedRange.empty());\n  // The frame is complete\n  ASSERT_TRUE(finished);\n  queue.append(std::move(uncompressed));\n  EXPECT_EQ(hashIOBuf(queue.move().get()), dh.hash(uncompressedLength_));\n}\n\nTEST_P(StreamingCompressionTest, testFlushNone) {\n  runFlushNoneTest(constantDataHolder);\n  runFlushNoneTest(randomDataHolder);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    StreamingCompressionTest,\n    StreamingCompressionTest,\n    testing::Combine(\n        testing::Values(0, 1, 12, 22, 27),\n        testing::Values(12, 17, 20),\n        testing::ValuesIn(availableStreamCodecs())));\n\nnamespace {\n\n// Codec types included in the codec returned by getAutoUncompressionCodec() by\n// default.\nstd::vector<CodecType> autoUncompressionCodecTypes = {{\n    CodecType::LZ4_FRAME,\n    CodecType::ZSTD,\n    CodecType::ZLIB,\n    CodecType::GZIP,\n    CodecType::LZMA2,\n    CodecType::BZIP2,\n}};\n\n} // namespace\n\nclass AutomaticCodecTest : public testing::TestWithParam<CodecType> {\n protected:\n  void SetUp() override {\n    codecType_ = GetParam();\n    codec_ = getCodec(codecType_);\n    autoType_ = std::any_of(\n        autoUncompressionCodecTypes.begin(),\n        autoUncompressionCodecTypes.end(),\n        [&](CodecType o) { return codecType_ == o; });\n    // Add the codec with type codecType_ as the terminal codec if it is not in\n    // autoUncompressionCodecTypes.\n    auto_ = getAutoUncompressionCodec({}, getTerminalCodec());\n  }\n\n  void runSimpleTest(const DataHolder& dh);\n\n  std::unique_ptr<Codec> getTerminalCodec() {\n    return (autoType_ ? nullptr : getCodec(codecType_));\n  }\n\n  std::unique_ptr<Codec> codec_;\n  std::unique_ptr<Codec> auto_;\n  CodecType codecType_;\n  // true if codecType_ is in autoUncompressionCodecTypes\n  bool autoType_;\n};\n\nvoid AutomaticCodecTest::runSimpleTest(const DataHolder& dh) {\n  constexpr uint64_t uncompressedLength = 1000;\n  auto original = IOBuf::wrapBuffer(dh.data(uncompressedLength));\n  auto compressed = codec_->compress(original.get());\n\n  if (!codec_->needsUncompressedLength()) {\n    auto uncompressed = auto_->uncompress(compressed.get());\n    EXPECT_EQ(uncompressedLength, uncompressed->computeChainDataLength());\n    EXPECT_EQ(dh.hash(uncompressedLength), hashIOBuf(uncompressed.get()));\n  }\n  {\n    auto uncompressed = auto_->uncompress(compressed.get(), uncompressedLength);\n    EXPECT_EQ(uncompressedLength, uncompressed->computeChainDataLength());\n    EXPECT_EQ(dh.hash(uncompressedLength), hashIOBuf(uncompressed.get()));\n  }\n  ASSERT_GE(compressed->computeChainDataLength(), 8);\n  for (size_t i = 0; i < 8; ++i) {\n    auto split = compressed->clone();\n    auto rest = compressed->clone();\n    split->trimEnd(split->length() - i);\n    rest->trimStart(i);\n    split->insertAfterThisOne(std::move(rest));\n    auto uncompressed = auto_->uncompress(split.get(), uncompressedLength);\n    EXPECT_EQ(uncompressedLength, uncompressed->computeChainDataLength());\n    EXPECT_EQ(dh.hash(uncompressedLength), hashIOBuf(uncompressed.get()));\n  }\n}\n\nTEST_P(AutomaticCodecTest, RandomData) {\n  runSimpleTest(randomDataHolder);\n}\n\nTEST_P(AutomaticCodecTest, ConstantData) {\n  runSimpleTest(constantDataHolder);\n}\n\nTEST_P(AutomaticCodecTest, ValidPrefixes) {\n  const auto prefixes = codec_->validPrefixes();\n  for (const auto& prefix : prefixes) {\n    EXPECT_FALSE(prefix.empty());\n    // Ensure that all strings are at least 8 bytes for LZMA2.\n    // The bytes after the prefix should be ignored by `canUncompress()`.\n    IOBuf data{IOBuf::COPY_BUFFER, prefix, 0, 8};\n    data.append(8);\n    EXPECT_TRUE(codec_->canUncompress(&data));\n    EXPECT_TRUE(auto_->canUncompress(&data));\n  }\n}\n\nTEST_P(AutomaticCodecTest, NeedsUncompressedLength) {\n  if (codec_->needsUncompressedLength()) {\n    EXPECT_TRUE(auto_->needsUncompressedLength());\n  }\n}\n\nTEST_P(AutomaticCodecTest, maxUncompressedLength) {\n  EXPECT_LE(codec_->maxUncompressedLength(), auto_->maxUncompressedLength());\n}\n\nTEST_P(AutomaticCodecTest, DefaultCodec) {\n  const uint64_t length = 42;\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(getCodec(CodecType::ZSTD));\n  auto automatic =\n      getAutoUncompressionCodec(std::move(codecs), getTerminalCodec());\n  auto original = IOBuf::wrapBuffer(constantDataHolder.data(length));\n  auto compressed = codec_->compress(original.get());\n  std::unique_ptr<IOBuf> decompressed;\n\n  if (automatic->needsUncompressedLength()) {\n    decompressed = automatic->uncompress(compressed.get(), length);\n  } else {\n    decompressed = automatic->uncompress(compressed.get());\n  }\n\n  EXPECT_EQ(constantDataHolder.hash(length), hashIOBuf(decompressed.get()));\n}\n\nnamespace {\nclass CustomCodec : public Codec {\n public:\n  static std::unique_ptr<Codec> create(std::string prefix, CodecType type) {\n    return std::make_unique<CustomCodec>(std::move(prefix), type);\n  }\n  explicit CustomCodec(std::string prefix, CodecType type)\n      : Codec(CodecType::USER_DEFINED),\n        prefix_(std::move(prefix)),\n        codec_(getCodec(type)) {}\n\n private:\n  std::vector<std::string> validPrefixes() const override { return {prefix_}; }\n\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override {\n    return codec_->maxCompressedLength(uncompressedLength) + prefix_.size();\n  }\n\n  bool canUncompress(const IOBuf* data, Optional<uint64_t>) const override {\n    auto clone = data->cloneCoalescedAsValue();\n    if (clone.length() < prefix_.size()) {\n      return false;\n    }\n    return memcmp(clone.data(), prefix_.data(), prefix_.size()) == 0;\n  }\n\n  std::unique_ptr<IOBuf> doCompress(const IOBuf* data) override {\n    auto result = IOBuf::copyBuffer(prefix_);\n    result->appendToChain(codec_->compress(data));\n    EXPECT_TRUE(canUncompress(result.get(), data->computeChainDataLength()));\n    return result;\n  }\n\n  std::unique_ptr<IOBuf> doUncompress(\n      const IOBuf* data, Optional<uint64_t> uncompressedLength) override {\n    EXPECT_TRUE(canUncompress(data, uncompressedLength));\n    auto clone = data->cloneCoalescedAsValue();\n    clone.trimStart(prefix_.size());\n    return codec_->uncompress(&clone, uncompressedLength);\n  }\n\n  std::string prefix_;\n  std::unique_ptr<Codec> codec_;\n};\n} // namespace\n\nTEST_P(AutomaticCodecTest, CustomCodec) {\n  const uint64_t length = 42;\n  auto ab = CustomCodec::create(\"ab\", CodecType::ZSTD);\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"ab\", CodecType::ZSTD));\n  auto automatic =\n      getAutoUncompressionCodec(std::move(codecs), getTerminalCodec());\n  auto original = IOBuf::wrapBuffer(constantDataHolder.data(length));\n\n  auto abCompressed = ab->compress(original.get());\n  std::unique_ptr<IOBuf> abDecompressed;\n  if (automatic->needsUncompressedLength()) {\n    abDecompressed = automatic->uncompress(abCompressed.get(), length);\n  } else {\n    abDecompressed = automatic->uncompress(abCompressed.get());\n  }\n  EXPECT_TRUE(automatic->canUncompress(abCompressed.get()));\n  EXPECT_FALSE(auto_->canUncompress(abCompressed.get()));\n  EXPECT_EQ(constantDataHolder.hash(length), hashIOBuf(abDecompressed.get()));\n\n  auto compressed = codec_->compress(original.get());\n  std::unique_ptr<IOBuf> decompressed;\n  if (automatic->needsUncompressedLength()) {\n    decompressed = automatic->uncompress(compressed.get(), length);\n  } else {\n    decompressed = automatic->uncompress(compressed.get());\n  }\n  EXPECT_EQ(constantDataHolder.hash(length), hashIOBuf(decompressed.get()));\n}\n\nTEST_P(AutomaticCodecTest, CustomDefaultCodec) {\n  const uint64_t length = 42;\n  auto none = CustomCodec::create(\"none\", CodecType::NO_COMPRESSION);\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"none\", CodecType::NO_COMPRESSION));\n  codecs.push_back(getCodec(CodecType::LZ4_FRAME));\n  auto automatic =\n      getAutoUncompressionCodec(std::move(codecs), getTerminalCodec());\n  auto original = IOBuf::wrapBuffer(constantDataHolder.data(length));\n\n  auto noneCompressed = none->compress(original.get());\n  std::unique_ptr<IOBuf> noneDecompressed;\n  if (automatic->needsUncompressedLength()) {\n    noneDecompressed = automatic->uncompress(noneCompressed.get(), length);\n  } else {\n    noneDecompressed = automatic->uncompress(noneCompressed.get());\n  }\n  EXPECT_TRUE(automatic->canUncompress(noneCompressed.get()));\n  EXPECT_FALSE(auto_->canUncompress(noneCompressed.get()));\n  EXPECT_EQ(constantDataHolder.hash(length), hashIOBuf(noneDecompressed.get()));\n\n  auto compressed = codec_->compress(original.get());\n  std::unique_ptr<IOBuf> decompressed;\n  if (automatic->needsUncompressedLength()) {\n    decompressed = automatic->uncompress(compressed.get(), length);\n  } else {\n    decompressed = automatic->uncompress(compressed.get());\n  }\n  EXPECT_EQ(constantDataHolder.hash(length), hashIOBuf(decompressed.get()));\n}\n\nTEST_P(AutomaticCodecTest, canUncompressOneBytes) {\n  // No default codec can uncompress 1 bytes.\n  IOBuf buf{IOBuf::CREATE, 1};\n  buf.append(1);\n  EXPECT_FALSE(codec_->canUncompress(&buf, 1));\n  EXPECT_FALSE(codec_->canUncompress(&buf, folly::none));\n  EXPECT_FALSE(auto_->canUncompress(&buf, 1));\n  EXPECT_FALSE(auto_->canUncompress(&buf, folly::none));\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    AutomaticCodecTest,\n    AutomaticCodecTest,\n    testing::ValuesIn(availableCodecs()));\n\nnamespace {\n\n// Codec that always \"uncompresses\" to the same string.\nclass ConstantCodec : public Codec {\n public:\n  static std::unique_ptr<Codec> create(\n      std::string uncompressed, CodecType type) {\n    return std::make_unique<ConstantCodec>(std::move(uncompressed), type);\n  }\n  explicit ConstantCodec(std::string uncompressed, CodecType type)\n      : Codec(type), uncompressed_(std::move(uncompressed)) {}\n\n private:\n  uint64_t doMaxCompressedLength(uint64_t uncompressedLength) const override {\n    return uncompressedLength;\n  }\n\n  std::unique_ptr<IOBuf> doCompress(const IOBuf*) override {\n    throw std::runtime_error(\"ConstantCodec error: compress() not supported.\");\n  }\n\n  std::unique_ptr<IOBuf> doUncompress(\n      const IOBuf*, Optional<uint64_t>) override {\n    return IOBuf::copyBuffer(uncompressed_);\n  }\n\n  std::string uncompressed_;\n  std::unique_ptr<Codec> codec_;\n};\n\n} // namespace\n\nclass TerminalCodecTest : public testing::TestWithParam<CodecType> {\n protected:\n  void SetUp() override {\n    codecType_ = GetParam();\n    codec_ = getCodec(codecType_);\n    auto_ = getAutoUncompressionCodec();\n  }\n\n  CodecType codecType_;\n  std::unique_ptr<Codec> codec_;\n  std::unique_ptr<Codec> auto_;\n};\n\n// Test that the terminal codec's uncompress() function is called when the\n// default chosen automatic codec throws.\nTEST_P(TerminalCodecTest, uncompressIfDefaultThrows) {\n  std::string const original = \"abc\";\n  auto const compressed = codec_->compress(original);\n\n  // Sanity check: the automatic codec can uncompress the original string.\n  auto const uncompressed = auto_->uncompress(compressed);\n  EXPECT_EQ(uncompressed, original);\n\n  // Truncate the compressed string.\n  auto const truncated = compressed.substr(0, compressed.size() - 1);\n  auto const truncatedBuf =\n      IOBuf::wrapBuffer(truncated.data(), truncated.size());\n  EXPECT_TRUE(auto_->canUncompress(truncatedBuf.get()));\n  EXPECT_ANY_THROW(auto_->uncompress(truncated));\n\n  // Expect the terminal codec to successfully uncompress the string.\n  std::unique_ptr<Codec> terminal = getAutoUncompressionCodec(\n      {}, ConstantCodec::create(\"dummyString\", CodecType::USER_DEFINED));\n  EXPECT_TRUE(terminal->canUncompress(truncatedBuf.get()));\n  EXPECT_EQ(terminal->uncompress(truncated), \"dummyString\");\n}\n\n// If the terminal codec has one of the \"default types\" automatically added in\n// the AutomaticCodec, check that the default codec is no longer added.\nTEST_P(TerminalCodecTest, terminalOverridesDefaults) {\n  std::unique_ptr<Codec> terminal = getAutoUncompressionCodec(\n      {}, ConstantCodec::create(\"dummyString\", codecType_));\n  std::string const original = \"abc\";\n  auto const compressed = codec_->compress(original);\n  EXPECT_EQ(terminal->uncompress(compressed), \"dummyString\");\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    TerminalCodecTest,\n    TerminalCodecTest,\n    testing::ValuesIn(autoUncompressionCodecTypes));\n\nTEST(ValidPrefixesTest, CustomCodec) {\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"none\", CodecType::NO_COMPRESSION));\n  const auto none = getAutoUncompressionCodec(std::move(codecs));\n  const auto prefixes = none->validPrefixes();\n  const auto it = std::find(prefixes.begin(), prefixes.end(), \"none\");\n  EXPECT_TRUE(it != prefixes.end());\n}\n\n#define EXPECT_THROW_IF_DEBUG(statement, expected_exception) \\\n  do {                                                       \\\n    if (kIsDebug) {                                          \\\n      EXPECT_THROW((statement), expected_exception);         \\\n    } else {                                                 \\\n      EXPECT_NO_THROW((statement));                          \\\n    }                                                        \\\n  } while (false)\n\nTEST(CheckCompatibleTest, SimplePrefixSecond) {\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"abc\", CodecType::NO_COMPRESSION));\n  codecs.push_back(CustomCodec::create(\"ab\", CodecType::NO_COMPRESSION));\n  EXPECT_THROW_IF_DEBUG(\n      getAutoUncompressionCodec(std::move(codecs)), std::invalid_argument);\n}\n\nTEST(CheckCompatibleTest, SimplePrefixFirst) {\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"ab\", CodecType::NO_COMPRESSION));\n  codecs.push_back(CustomCodec::create(\"abc\", CodecType::NO_COMPRESSION));\n  EXPECT_THROW_IF_DEBUG(\n      getAutoUncompressionCodec(std::move(codecs)), std::invalid_argument);\n}\n\nTEST(CheckCompatibleTest, Empty) {\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"\", CodecType::NO_COMPRESSION));\n  EXPECT_THROW_IF_DEBUG(\n      getAutoUncompressionCodec(std::move(codecs)), std::invalid_argument);\n}\n\nTEST(CheckCompatibleTest, ZstdPrefix) {\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"\\x28\\xB5\\x2F\", CodecType::ZSTD));\n  EXPECT_THROW_IF_DEBUG(\n      getAutoUncompressionCodec(std::move(codecs)), std::invalid_argument);\n}\n\nTEST(CheckCompatibleTest, ZstdDuplicate) {\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"\\x28\\xB5\\x2F\\xFD\", CodecType::ZSTD));\n  EXPECT_THROW_IF_DEBUG(\n      getAutoUncompressionCodec(std::move(codecs)), std::invalid_argument);\n}\n\nTEST(CheckCompatibleTest, ZlibIsPrefix) {\n  std::vector<std::unique_ptr<Codec>> codecs;\n  codecs.push_back(CustomCodec::create(\"\\x18\\x76zzasdf\", CodecType::ZSTD));\n  EXPECT_THROW_IF_DEBUG(\n      getAutoUncompressionCodec(std::move(codecs)), std::invalid_argument);\n}\n\n#if FOLLY_HAVE_LIBZSTD\n\n#if ZSTD_VERSION_NUMBER < 10308\n#define ZSTD_c_contentSizeFlag ZSTD_p_contentSizeFlag\n#define ZSTD_c_checksumFlag ZSTD_p_checksumFlag\n#define ZSTD_c_windowLog ZSTD_p_windowLog\n#endif\n\nTEST(ZstdTest, BackwardCompatible) {\n  auto codec = getCodec(CodecType::ZSTD);\n  {\n    auto const data = IOBuf::wrapBuffer(randomDataHolder.data(size_t(1) << 20));\n    auto compressed = codec->compress(data.get());\n    compressed->coalesce();\n    EXPECT_EQ(\n        data->length(),\n        ZSTD_getFrameContentSize(compressed->data(), compressed->length()));\n  }\n  {\n    auto const data =\n        IOBuf::wrapBuffer(randomDataHolder.data(size_t(100) << 20));\n    auto compressed = codec->compress(data.get());\n    compressed->coalesce();\n    EXPECT_EQ(\n        data->length(),\n        ZSTD_getFrameContentSize(compressed->data(), compressed->length()));\n  }\n}\n\nTEST(ZstdTest, CustomOptions) {\n  auto test = [](const DataHolder& dh, unsigned contentSizeFlag) {\n    unsigned const wlog = 23;\n    zstd::Options options(1);\n    options.set(ZSTD_c_contentSizeFlag, contentSizeFlag);\n    options.set(ZSTD_c_checksumFlag, 1);\n    options.set(ZSTD_c_windowLog, wlog);\n    auto codec = zstd::getCodec(std::move(options));\n    size_t const uncompressedLength = (size_t)1 << 27;\n    auto const original = std::string(\n        reinterpret_cast<const char*>(dh.data(uncompressedLength).data()),\n        uncompressedLength);\n    auto const compressed = codec->compress(original);\n    auto const uncompressed = codec->uncompress(compressed);\n    EXPECT_EQ(uncompressed, original);\n    EXPECT_EQ(\n        codec->getUncompressedLength(\n            folly::IOBuf::wrapBuffer(compressed.data(), compressed.size())\n                .get()),\n        contentSizeFlag ? uncompressedLength : Optional<uint64_t>());\n    {\n      ZSTD_frameHeader zfh;\n      ZSTD_getFrameHeader(&zfh, compressed.data(), compressed.size());\n      EXPECT_EQ(zfh.checksumFlag, 1);\n      EXPECT_EQ(zfh.windowSize, 1ULL << wlog);\n      EXPECT_EQ(\n          zfh.frameContentSize,\n          contentSizeFlag ? uncompressedLength : ZSTD_CONTENTSIZE_UNKNOWN);\n    }\n  };\n  for (unsigned contentSizeFlag = 0; contentSizeFlag <= 1; ++contentSizeFlag) {\n    test(constantDataHolder, contentSizeFlag);\n    test(randomDataHolder, contentSizeFlag);\n  }\n}\n\nTEST(ZstdTest, NegativeLevels) {\n  EXPECT_EQ(zstd::Options(1).level(), 1);\n  EXPECT_EQ(zstd::Options(-1).level(), -1);\n  auto const original = std::string(\n      reinterpret_cast<const char*>(randomDataHolder.data(16348).data()),\n      16348);\n  auto const plusCompressed =\n      zstd::getCodec(zstd::Options(1))->compress(original);\n  auto const minusCompressed =\n      zstd::getCodec(zstd::Options(-100))->compress(original);\n  EXPECT_GT(minusCompressed.size(), plusCompressed.size());\n  auto codec = getCodec(CodecType::ZSTD);\n  auto const uncompressed = codec->uncompress(minusCompressed);\n  EXPECT_EQ(original, uncompressed);\n}\n\n#endif\n\n#if FOLLY_HAVE_LIBZ\n\nusing ZlibFormat = zlib::Options::Format;\n\nTEST(ZlibTest, Auto) {\n  size_t const uncompressedLength_ = (size_t)1 << 15;\n  auto const original = std::string(\n      reinterpret_cast<const char*>(\n          randomDataHolder.data(uncompressedLength_).data()),\n      uncompressedLength_);\n  auto optionCodec = zlib::getCodec(zlib::Options(ZlibFormat::AUTO));\n\n  // Test the codec can uncompress zlib data.\n  {\n    auto codec = getCodec(CodecType::ZLIB);\n    auto const compressed = codec->compress(original);\n    auto const uncompressed = optionCodec->uncompress(compressed);\n    EXPECT_EQ(original, uncompressed);\n  }\n\n  // Test the codec can uncompress gzip data.\n  {\n    auto codec = getCodec(CodecType::GZIP);\n    auto const compressed = codec->compress(original);\n    auto const uncompressed = optionCodec->uncompress(compressed);\n    EXPECT_EQ(original, uncompressed);\n  }\n}\n\nTEST(ZlibTest, DefaultOptions) {\n  size_t const uncompressedLength_ = (size_t)1 << 20;\n  auto const original = std::string(\n      reinterpret_cast<const char*>(\n          randomDataHolder.data(uncompressedLength_).data()),\n      uncompressedLength_);\n  {\n    auto codec = getCodec(CodecType::ZLIB);\n    auto optionCodec = zlib::getCodec(zlib::defaultZlibOptions());\n    auto const compressed = optionCodec->compress(original);\n    auto uncompressed = codec->uncompress(compressed);\n    EXPECT_EQ(original, uncompressed);\n    uncompressed = optionCodec->uncompress(compressed);\n    EXPECT_EQ(original, uncompressed);\n  }\n\n  {\n    auto codec = getCodec(CodecType::GZIP);\n    auto optionCodec = zlib::getCodec(zlib::defaultGzipOptions());\n    auto const compressed = optionCodec->compress(original);\n    auto uncompressed = codec->uncompress(compressed);\n    EXPECT_EQ(original, uncompressed);\n    uncompressed = optionCodec->uncompress(compressed);\n    EXPECT_EQ(original, uncompressed);\n  }\n}\n\nclass ZlibOptionsTest\n    : public testing::TestWithParam<std::tuple<ZlibFormat, int, int, int>> {\n protected:\n  void SetUp() override {\n    auto tup = GetParam();\n    options_.format = std::get<0>(tup);\n    options_.windowSize = std::get<1>(tup);\n    options_.memLevel = std::get<2>(tup);\n    options_.strategy = std::get<3>(tup);\n    codec_ = zlib::getStreamCodec(options_);\n  }\n\n  void runSimpleRoundTripTest(const DataHolder& dh);\n\n private:\n  zlib::Options options_;\n  std::unique_ptr<StreamCodec> codec_;\n};\n\nvoid ZlibOptionsTest::runSimpleRoundTripTest(const DataHolder& dh) {\n  size_t const uncompressedLength = (size_t)1 << 16;\n  auto const original = std::string(\n      reinterpret_cast<const char*>(dh.data(uncompressedLength).data()),\n      uncompressedLength);\n\n  auto const compressed = codec_->compress(original);\n  auto const uncompressed = codec_->uncompress(compressed);\n  EXPECT_EQ(uncompressed, original);\n}\n\nTEST_P(ZlibOptionsTest, simpleRoundTripTest) {\n  runSimpleRoundTripTest(constantDataHolder);\n  runSimpleRoundTripTest(randomDataHolder);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    ZlibOptionsTest,\n    ZlibOptionsTest,\n    testing::Combine(\n        testing::Values(\n            ZlibFormat::ZLIB,\n            ZlibFormat::GZIP,\n            ZlibFormat::RAW,\n            ZlibFormat::AUTO),\n        testing::Values(9, 12, 15),\n        testing::Values(1, 8, 9),\n        testing::Values(\n            Z_DEFAULT_STRATEGY, Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED)));\n\n#endif // FOLLY_HAVE_LIBZ\n\n} // namespace test\n} // namespace compression\n} // namespace folly\n"
  },
  {
    "path": "folly/compression/test/InstructionsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/Instructions.h>\n\n#include <glog/logging.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing namespace folly::compression::instructions;\n\ntemplate <typename Arch>\nstruct InstructionsTest : ::testing::Test {};\n\n#if FOLLY_X64\nusing InstructionsTestTypes = ::testing::Types<Default, Haswell>;\n#else\nusing InstructionsTestTypes = ::testing::Types<Default>;\n#endif\n\nTYPED_TEST_SUITE(InstructionsTest, InstructionsTestTypes);\n\nTYPED_TEST(InstructionsTest, BitExtraction) {\n  using Arch = TypeParam;\n\n  uint64_t value =\n      0b11111110'11011100'10111010'10011000'01110110'01010100'00110010'00010000;\n\n  if (!Arch::supported()) {\n    return;\n  }\n\n  LOG(INFO) << \"Testing \" << Arch::name() << \" Architecture\";\n\n  // Extract 4 bits a time, starting from bit 0\n  uint64_t expected = 0;\n  for (int i = 0; i < 64 - 4; i += 4) {\n    EXPECT_EQ(expected, Arch::bextr(value, i, 4));\n    ++expected;\n  }\n\n  // Extract 8 bits a time, starting from bit 1\n  uint64_t value2 = value << 1;\n  uint64_t lower = 0;\n  uint64_t upper = 1;\n  for (int i = 1; i < 64 - 8; i += 4) {\n    expected = (lower & 0xF) | ((upper & 0xF) << 4);\n    EXPECT_EQ(expected, Arch::bextr(value2, i, 8));\n    ++lower;\n    ++upper;\n  }\n\n  // Extract 16 bits a time, starting from bit 2\n  uint64_t value3 = value << 2;\n  uint64_t part0 = 0;\n  uint64_t part1 = 1;\n  uint64_t part2 = 2;\n  uint64_t part3 = 3;\n  for (int i = 2; i < 64 - 16; i += 4) {\n    expected = (part0 & 0xF) | ((part1 & 0xF) << 4) | ((part2 & 0xF) << 8) |\n        ((part3 & 0xF) << 12);\n    EXPECT_EQ(expected, Arch::bextr(value3, i, 16));\n    ++part0;\n    ++part1;\n    ++part2;\n    ++part3;\n  }\n\n  // Extract 32 bits\n  expected = 0b1011'1010'1001'1000'0111'0110'0101'0100;\n  EXPECT_EQ(expected, Arch::bextr(value, 16, 32));\n\n  // Extract all 64 bits\n  EXPECT_EQ(value, Arch::bextr(value, 0, 64));\n\n  // Extract 0 bits\n  EXPECT_EQ(0, Arch::bextr(value, 4, 0));\n\n  // Make sure only up to 63-th bits will be extracted\n  EXPECT_EQ(0b1111, Arch::bextr(value, 60, 5));\n\n  EXPECT_EQ(0, Arch::bextr(value, 64, 8));\n\n  EXPECT_EQ(value, Arch::bextr(value, 0, 65));\n}\n"
  },
  {
    "path": "folly/compression/test/LargeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include <limits>\n#include <string>\n\n#include <folly/compression/Compression.h>\n\nnamespace folly::compression::test {\nnamespace {\nconstexpr uint64_t kGB = 1024 * 1024 * 1024;\nstd::vector<CodecType> availableCodecs() {\n  std::vector<CodecType> codecs;\n\n  for (size_t i = 0; i < static_cast<size_t>(CodecType::NUM_CODEC_TYPES); ++i) {\n    auto type = static_cast<CodecType>(i);\n    if (hasCodec(type)) {\n      codecs.push_back(type);\n    }\n  }\n\n  return codecs;\n}\n} // namespace\n\nclass LargeTest : public testing::TestWithParam<CodecType> {};\n\nTEST_P(LargeTest, SingleLargeBuffer) {\n  auto codec = getCodec(GetParam(), COMPRESSION_LEVEL_FASTEST);\n  uint64_t size = std::min<uint64_t>(5 * kGB, codec->maxUncompressedLength());\n  size = std::min<uint64_t>(size, std::numeric_limits<size_t>::max());\n  std::string data(size, 'a');\n  auto compressed = codec->compress(data);\n  auto decompressed = codec->uncompress(\n      compressed,\n      codec->needsUncompressedLength()\n          ? folly::Optional<uint64_t>(size)\n          : folly::none);\n  EXPECT_EQ(data.size(), decompressed.size());\n  EXPECT_TRUE(data == decompressed);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    LargeTest,\n    LargeTest,\n    testing::ValuesIn(availableCodecs()),\n    [](const testing::TestParamInfo<CodecType>& info) {\n      return std::to_string(int(info.param));\n    });\n} // namespace folly::compression::test\n"
  },
  {
    "path": "folly/compression/test/QuotientMultiSetBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/QuotientMultiSet.h>\n\n#include <boost/sort/spreadsort/integer_sort.hpp>\n#include <fmt/format.h>\n\n#include <folly/Benchmark.h>\n#include <folly/Random.h>\n#include <folly/String.h>\n#include <folly/compression/elias_fano/EliasFanoCoding.h>\n#include <folly/container/Enumerate.h>\n#include <folly/container/F14Set.h>\n#include <folly/experimental/test/CodingTestUtils.h>\n#include <folly/init/Init.h>\n\nDEFINE_int64(\n    key_bits,\n    32,\n    \"The number of key bits used in quotient multiset. Should always <= 64\");\nDEFINE_uint64(\n    num_elements,\n    100000000,\n    \"The number of elements inserted into quotient multiset\");\nDEFINE_double(load_factor, 0.95, \"Load factor of the multiset\");\n\n#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n\nusing std::string;\n\nnamespace {\n\nstatic const unsigned int kRunsPerIteration = 5000000;\n\nstd::mt19937 rng;\n\n// Uniformly distributed keys.\nstd::vector<uint64_t> uniform;\nstring qmsData;\n\nuint64_t maxValue(uint32_t nbits) {\n  return nbits == 64\n      ? std::numeric_limits<uint64_t>::max()\n      : (uint64_t(1) << nbits) - 1;\n}\n\n// Hash 64 bits into 1.\nuint64_t mix64_to_1(uint64_t word) {\n  // Constant from MurmurHash3 final mixer.\n  return (word * UINT64_C(0xff51afd7ed558ccd)) >> 63;\n}\n\nvoid buildQuotientMultiSet(std::vector<uint64_t>& keys) {\n  folly::QuotientMultiSetBuilder builder(\n      FLAGS_key_bits, keys.size(), FLAGS_load_factor);\n  folly::IOBufQueue buff;\n  for (const auto& iter : folly::enumerate(keys)) {\n    if (builder.insert(*iter)) {\n      builder.setBlockPayload(iter.index);\n    }\n    if (builder.numReadyBlocks() >= 1) {\n      builder.flush(buff);\n    }\n  }\n  builder.close(buff);\n  qmsData = buff.move()->to<string>();\n}\n\nstd::vector<uint64_t> makeLookupKeys(size_t n, double hitRate) {\n  folly::BenchmarkSuspender guard;\n  std::vector<uint64_t> keys;\n  keys.reserve(n);\n  uint64_t maxKey = maxValue(FLAGS_key_bits);\n  for (uint64_t idx = 0; idx < n; idx++) {\n    keys.push_back(\n        (folly::Random::randDouble01(rng) < hitRate)\n            ? uniform[folly::Random::rand64(uniform.size(), rng)]\n            : folly::Random::rand64(rng) & maxKey);\n  }\n  return keys;\n}\n\nconst folly::F14FastSet<uint64_t>& getF14Baseline() {\n  folly::BenchmarkSuspender guard;\n  static const auto set = [] {\n    folly::F14FastSet<uint64_t> ret(uniform.begin(), uniform.end());\n    LOG(INFO) << fmt::format(\n        \"Built F14FastSet, size: {}, space: {}\",\n        ret.size(),\n        folly::prettyPrint(\n            ret.getAllocatedMemorySize(), folly::PrettyType::PRETTY_BYTES_IEC));\n    return ret;\n  }();\n  return set;\n}\n\nusing EFEncoder =\n    folly::compression::EliasFanoEncoder<uint64_t, uint64_t, 128, 128>;\n\nconst folly::compression::MutableEliasFanoCompressedList& getEFBaseline() {\n  folly::BenchmarkSuspender guard;\n  static auto list = [] {\n    auto ret = EFEncoder::encode(uniform.begin(), uniform.end());\n    LOG(INFO) << fmt::format(\n        \"Built Elias-Fano list, space: {}\",\n        folly::prettyPrint(\n            ret.data.size(), folly::PrettyType::PRETTY_BYTES_IEC));\n    return ret;\n  }();\n  return list;\n}\n\ntemplate <class Lookup>\nsize_t benchmarkHits(const Lookup& lookup) {\n  auto keys = makeLookupKeys(kRunsPerIteration, /* hitRate */ 1);\n  for (const auto& key : keys) {\n    auto found = lookup(key);\n    folly::doNotOptimizeAway(found);\n    DCHECK(found);\n  }\n  return kRunsPerIteration;\n}\n\ntemplate <class Lookup>\nsize_t benchmarkRandom(const Lookup& lookup) {\n  auto keys = makeLookupKeys(kRunsPerIteration, /* hitRate */ 0);\n  for (const auto& key : keys) {\n    auto found = lookup(key);\n    folly::doNotOptimizeAway(found);\n  }\n  return kRunsPerIteration;\n}\n\ntemplate <class Lookup>\nsize_t benchmarkMixtureSerialized(const Lookup& lookup) {\n  // Make the result unpredictable so we can use it to introduce a\n  // serializing dependency in the loop.\n  auto keys = makeLookupKeys(kRunsPerIteration * 2, /* hitRate */ 0.5);\n  size_t unpredictableBit = 0;\n  for (size_t i = 0; i < kRunsPerIteration * 2; i += 2) {\n    unpredictableBit = lookup(keys[i + unpredictableBit]) ? 1 : 0;\n    folly::doNotOptimizeAway(unpredictableBit);\n  }\n  return kRunsPerIteration;\n}\n\ntemplate <class Lookup>\nsize_t benchmarkSerializedOnResultBits(const Lookup& lookup, double hitRate) {\n  auto keys = makeLookupKeys(kRunsPerIteration * 2, hitRate);\n  size_t unpredictableBit = 0;\n  for (size_t i = 0; i < kRunsPerIteration * 2; i += 2) {\n    // When all keys are hits we can use a hash of the location of the\n    // found key to introduce a serializing dependency in the loop.\n    unpredictableBit = mix64_to_1(lookup(keys[i + unpredictableBit]));\n    folly::doNotOptimizeAway(unpredictableBit);\n  }\n  return kRunsPerIteration;\n}\n\ntemplate <class Lookup>\nsize_t benchmarkHitsSerialized(const Lookup& lookup) {\n  return benchmarkSerializedOnResultBits(lookup, /* hitRate */ 1);\n}\n\ntemplate <class Lookup>\nsize_t benchmarkRandomSerialized(const Lookup& lookup) {\n  return benchmarkSerializedOnResultBits(lookup, /* hitRate */ 0);\n}\n\ntemplate <class Lookup>\nsize_t benchmarkHitsSmallWorkingSet(const Lookup& lookup) {\n  // Loop over a small set of keys so that after the first iteration\n  // all the relevant parts of the data structure are in LLC cache.\n  constexpr size_t kLookupSetSize = 1 << 12; // Should be a power of 2.\n  auto keys = makeLookupKeys(kLookupSetSize, /* hitRate */ 1);\n  for (size_t i = 0; i < kRunsPerIteration; ++i) {\n    auto found = lookup(keys[i % kLookupSetSize]);\n    folly::doNotOptimizeAway(found);\n    DCHECK(found);\n  }\n  return kRunsPerIteration;\n}\n\n} // namespace\n\nvoid benchmarkSetup() {\n  rng.seed(UINT64_C(12345678));\n\n  uint64_t maxKey = maxValue(FLAGS_key_bits);\n  // Uniformly distributed keys.\n  const uint64_t size = FLAGS_num_elements;\n  for (uint64_t idx = 0; idx < size; idx++) {\n    uint64_t key = folly::Random::rand64(rng) & maxKey;\n    uniform.emplace_back(key);\n  }\n  boost::sort::spreadsort::integer_sort(uniform.begin(), uniform.end());\n  buildQuotientMultiSet(uniform);\n\n  LOG(INFO) << fmt::format(\n      \"Built QuotientMultiSet, space: {}\",\n      folly::prettyPrint(qmsData.size(), folly::PrettyType::PRETTY_BYTES_IEC));\n}\n\nBENCHMARK_MULTI(QuotientMultiSetGetHits) {\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = folly::QuotientMultiSet<decltype(instructions)>(qmsData);\n    ret = benchmarkHits([&](uint64_t key) { return reader.equalRange(key); });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(QuotientMultiSetGetRandom) {\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = folly::QuotientMultiSet<decltype(instructions)>(qmsData);\n    ret = benchmarkRandom([&](uint64_t key) { return reader.equalRange(key); });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(QuotientMultiSetGetHitsSmallWorkingSet) {\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = folly::QuotientMultiSet<decltype(instructions)>(qmsData);\n    ret = benchmarkHitsSmallWorkingSet([&](uint64_t key) {\n      return reader.equalRange(key);\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(QuotientMultiSetGetMixtureSerialized) {\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = folly::QuotientMultiSet<decltype(instructions)>(qmsData);\n    ret = benchmarkMixtureSerialized([&](uint64_t key) {\n      return reader.equalRange(key);\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(QuotientMultiSetGetHitsSerialized) {\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = folly::QuotientMultiSet<decltype(instructions)>(qmsData);\n    ret = benchmarkHitsSerialized([&](uint64_t key) {\n      return reader.equalRange(key).begin;\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(QuotientMultiSetGetRandomSerialized) {\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = folly::QuotientMultiSet<decltype(instructions)>(qmsData);\n    ret = benchmarkRandomSerialized([&](uint64_t key) {\n      // Even without a match, begin will depend on the whole\n      // computation.\n      return reader.equalRange(key).begin;\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_MULTI(F14MapHits) {\n  const auto& baseline = getF14Baseline();\n  return benchmarkHits([&](uint64_t key) {\n    return baseline.find(key) != baseline.end();\n  });\n}\n\nBENCHMARK_MULTI(F14MapRandom) {\n  const auto& baseline = getF14Baseline();\n  return benchmarkRandom([&](uint64_t key) {\n    return baseline.find(key) != baseline.end();\n  });\n}\n\nBENCHMARK_MULTI(F14MapHitsSmallWorkingSet) {\n  const auto& baseline = getF14Baseline();\n  return benchmarkHitsSmallWorkingSet([&](uint64_t key) {\n    return baseline.find(key) != baseline.end();\n  });\n}\n\nBENCHMARK_MULTI(F14MapMixtureSerialized) {\n  const auto& baseline = getF14Baseline();\n  return benchmarkMixtureSerialized([&](uint64_t key) {\n    return baseline.find(key) != baseline.end();\n  });\n}\n\nBENCHMARK_MULTI(F14MapHitsSerialized) {\n  const auto& baseline = getF14Baseline();\n  return benchmarkHitsSerialized([&](uint64_t key) {\n    return reinterpret_cast<uintptr_t>(&*baseline.find(key));\n  });\n}\n\n// benchmarkRandomSerialized() cannot be used with F14.\n\nBENCHMARK_DRAW_LINE();\n\nusing folly::compression::EliasFanoReader;\n\nBENCHMARK_MULTI(EliasFanoGetHits) {\n  auto list = getEFBaseline();\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = EliasFanoReader<EFEncoder, decltype(instructions)>(list);\n    ret = benchmarkHits([&](uint64_t key) {\n      return reader.jumpTo(key) && reader.value() == key;\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(EliasFanoGetRandom) {\n  auto list = getEFBaseline();\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = EliasFanoReader<EFEncoder, decltype(instructions)>(list);\n    ret = benchmarkRandom([&](uint64_t key) {\n      return reader.jumpTo(key) && reader.value() == key;\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(EliasFanoGetHitsSmallWorkingSet) {\n  auto list = getEFBaseline();\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = EliasFanoReader<EFEncoder, decltype(instructions)>(list);\n    ret = benchmarkHitsSmallWorkingSet([&](uint64_t key) {\n      return reader.jumpTo(key) && reader.value() == key;\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(EliasFanoGetMixtureSerialized) {\n  auto list = getEFBaseline();\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = EliasFanoReader<EFEncoder, decltype(instructions)>(list);\n    ret = benchmarkMixtureSerialized([&](uint64_t key) {\n      return reader.jumpTo(key) && reader.value() == key;\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(EliasFanoGetHitsSerialized) {\n  auto list = getEFBaseline();\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = EliasFanoReader<EFEncoder, decltype(instructions)>(list);\n    ret = benchmarkHitsSerialized([&](uint64_t key) {\n      reader.jumpTo(key);\n      DCHECK(reader.valid());\n      return reader.position();\n    });\n  });\n  return ret;\n}\n\nBENCHMARK_MULTI(EliasFanoGetRandomSerialized) {\n  auto list = getEFBaseline();\n  size_t ret = 0;\n  folly::compression::dispatchInstructions([&](auto instructions) {\n    auto reader = EliasFanoReader<EFEncoder, decltype(instructions)>(list);\n    ret = benchmarkRandomSerialized([&](uint64_t key) {\n      reader.jumpTo(key);\n      return reader.position();\n    });\n  });\n  return ret;\n}\n\n#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n\n#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n  benchmarkSetup();\n  folly::runBenchmarks();\n#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n}\n\n#if 0\n// Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz, built with Clang.\n$ buck run @mode/opt folly/experimental/test:quotient_multiset_benchmark -- --benchmark --bm_max_secs 3\n\nI0917 10:28:54.497815 2874547 QuotientMultiSetBenchmark.cpp:195] Built QuotientMultiSet, space: 124.9 MiB\n============================================================================\nfolly/experimental/test/QuotientMultiSetBenchmark.cpprelative  time/iter  iters/s\n============================================================================\nQuotientMultiSetGetHits                                    114.94ns    8.70M\nQuotientMultiSetGetRandom                                   72.87ns   13.72M\nQuotientMultiSetGetHitsSmallWorkingSet                      58.75ns   17.02M\nQuotientMultiSetGetMixtureSerialized                       138.73ns    7.21M\nQuotientMultiSetGetHitsSerialized                          138.56ns    7.22M\nQuotientMultiSetGetRandomSerialized                        130.11ns    7.69M\n----------------------------------------------------------------------------\nI0917 10:29:33.808831 2874547 QuotientMultiSetBenchmark.cpp:83] Built F14FastSet, size: 98843868, space: 1 GiB\nF14MapHits                                                  34.69ns   28.83M\nF14MapRandom                                                47.22ns   21.18M\nF14MapHitsSmallWorkingSet                                   14.42ns   69.34M\nF14MapMixtureSerialized                                     94.64ns   10.57M\nF14MapHitsSerialized                                       140.74ns    7.11M\n----------------------------------------------------------------------------\nI0917 10:29:52.722596 2874547 QuotientMultiSetBenchmark.cpp:100] Built Elias-Fano list, space: 101.5 MiB\nEliasFanoGetHits                                           258.08ns    3.87M\nEliasFanoGetRandom                                         247.18ns    4.05M\nEliasFanoGetHitsSmallWorkingSet                             81.54ns   12.26M\nEliasFanoGetMixtureSerialized                              253.63ns    3.94M\nEliasFanoGetHitsSerialized                                 161.57ns    6.19M\nEliasFanoGetRandomSerialized                               161.55ns    6.19M\n============================================================================\n#endif\n"
  },
  {
    "path": "folly/compression/test/QuotientMultiSetTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/QuotientMultiSet.h>\n\n#include <random>\n\n#include <fmt/format.h>\n\n#include <folly/Random.h>\n#include <folly/container/Enumerate.h>\n#include <folly/io/IOBufQueue.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n\nnamespace {\n\nclass QuotientMultiSetTest : public ::testing::Test {\n protected:\n  static constexpr uint64_t kBlockSize = folly::QuotientMultiSet<>::kBlockSize;\n\n  void SetUp() override { rng.seed(folly::randomNumberSeed()); }\n\n  void buildAndValidate(\n      std::vector<uint64_t>& keys, uint64_t keyBits, double loadFactor) {\n    // Elements must be added in ascending order.\n    std::sort(keys.begin(), keys.end());\n    folly::QuotientMultiSetBuilder builder(keyBits, keys.size(), loadFactor);\n    folly::IOBufQueue buff;\n    for (const auto& iter : folly::enumerate(keys)) {\n      if (builder.insert(*iter)) {\n        // Set payload to relative position of the first key in block.\n        builder.setBlockPayload(iter.index);\n      }\n      if (builder.numReadyBlocks() >= 1) {\n        builder.flush(buff);\n      }\n    }\n    builder.close(buff);\n    auto spBuf = buff.move();\n\n    folly::StringPiece data(spBuf->coalesce());\n    folly::QuotientMultiSet<> reader(data);\n\n    size_t index = 0;\n    folly::QuotientMultiSet<>::Iterator iter(&reader);\n    while (index < keys.size()) {\n      uint64_t start = index;\n      uint64_t& key = keys[start];\n      auto debugInfo = [&] {\n        return fmt::format(\"Index: {} Key: {}\", index, key);\n      };\n      while (index < keys.size() && keys[index] == key) {\n        index++;\n      }\n      auto range = reader.equalRange(key);\n      size_t pos = reader.getBlockPayload(range.begin / kBlockSize);\n      EXPECT_EQ(start, pos + range.begin % kBlockSize) << debugInfo();\n      pos = reader.getBlockPayload((range.end - 1) / kBlockSize);\n      EXPECT_EQ(index - 1, pos + (range.end - 1) % kBlockSize) << debugInfo();\n\n      EXPECT_TRUE(iter.skipTo(key));\n      EXPECT_EQ(key, iter.key()) << debugInfo();\n      EXPECT_EQ(range.begin, iter.pos()) << debugInfo();\n      EXPECT_EQ(\n          start,\n          reader.getBlockPayload(iter.pos() / kBlockSize) +\n              iter.pos() % kBlockSize)\n          << debugInfo();\n      if (start < keys.size() - 1) {\n        EXPECT_TRUE(iter.next());\n        EXPECT_EQ(keys[start + 1], iter.key());\n      }\n\n      // Verify getting keys not in keys returns false.\n      uint64_t prevKey = (start == 0 ? 0 : keys[start - 1]);\n      if (prevKey + 1 < key) {\n        uint64_t missedKey = folly::Random::rand64(prevKey + 1, key, rng);\n        EXPECT_FALSE(reader.equalRange(missedKey)) << key;\n        iter.skipTo(missedKey);\n        EXPECT_EQ(key, iter.key()) << debugInfo();\n        EXPECT_EQ(range.begin, iter.pos()) << debugInfo();\n        EXPECT_EQ(\n            start,\n            reader.getBlockPayload(iter.pos() / kBlockSize) +\n                iter.pos() % kBlockSize)\n            << debugInfo();\n        if (start < keys.size() - 1) {\n          EXPECT_TRUE(iter.next());\n          EXPECT_EQ(keys[start + 1], iter.key());\n        }\n      }\n    }\n\n    const auto maxKey = folly::qms_detail::maxValue(keyBits);\n    if (keys.back() < maxKey) {\n      uint64_t key = folly::Random::rand64(keys.back(), maxKey, rng) + 1;\n      EXPECT_FALSE(reader.equalRange(key)) << keys.back() << \" \" << key;\n      EXPECT_FALSE(iter.done());\n      EXPECT_FALSE(iter.skipTo(key));\n      EXPECT_TRUE(iter.done());\n    }\n\n    folly::QuotientMultiSet<>::Iterator nextIter(&reader);\n    for (const auto key : keys) {\n      EXPECT_TRUE(nextIter.next());\n      EXPECT_EQ(key, nextIter.key());\n    }\n    EXPECT_FALSE(nextIter.next());\n    EXPECT_TRUE(nextIter.done());\n\n    if (maxKey + 1 != 0) {\n      folly::QuotientMultiSet<>::Iterator skipToEndIter(&reader);\n      EXPECT_FALSE(skipToEndIter.done());\n      EXPECT_FALSE(skipToEndIter.skipTo(maxKey + 1));\n      EXPECT_TRUE(skipToEndIter.done());\n    }\n  }\n\n  std::mt19937 rng;\n};\n\n} // namespace\n\nTEST_F(QuotientMultiSetTest, Simple) {\n  std::vector<uint64_t> keys = {\n      100, 1000, 1 << 14, 10 << 14, 0, 10, 100 << 14, 1000 << 14};\n  buildAndValidate(keys, 32, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, Empty) {\n  folly::QuotientMultiSetBuilder builder(32, 1024);\n  folly::IOBufQueue buff;\n  builder.close(buff);\n  auto spBuf = buff.move();\n  folly::StringPiece data(spBuf->coalesce());\n  folly::QuotientMultiSet<> reader(data);\n\n  for (size_t idx = 0; idx < 1024; idx++) {\n    uint64_t key = folly::Random::rand32(rng);\n    EXPECT_FALSE(reader.equalRange(key));\n  }\n}\n\nTEST_F(QuotientMultiSetTest, ZeroKeyBits) {\n  std::vector<uint64_t> keys(67, 0);\n  buildAndValidate(keys, 0, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, Uniform) {\n  constexpr auto kLoadFactor =\n      folly::QuotientMultiSetBuilder::kDefaultMaxLoadFactor;\n  constexpr uint64_t kAvgSize = 1 << 16;\n  auto randSize = [&](uint64_t avgSize) {\n    return folly::Random::rand64(avgSize / 2, avgSize * 3 / 2, rng);\n  };\n  std::vector<std::tuple<int, uint64_t, double>> testCases = {\n      {1, randSize(1 << 9), kLoadFactor},\n      {8, randSize(1 << 10), kLoadFactor},\n      {9, randSize(1 << 11), kLoadFactor},\n      {12, randSize(kAvgSize), kLoadFactor},\n      {32, randSize(kAvgSize), kLoadFactor},\n      {48, randSize(kAvgSize), kLoadFactor},\n      {64, randSize(kAvgSize), kLoadFactor},\n      {32, randSize(kAvgSize), 1}, // Full\n      {12, 3800, kLoadFactor}, // Almost full\n      {64, randSize(16), kLoadFactor}, // Sparse, long keys.\n  };\n\n  for (const auto& testCase : testCases) {\n    const auto& [keyBits, size, loadFactor] = testCase;\n    SCOPED_TRACE(\n        fmt::format(\n            \"Key bits: {} Size: {} Load factor: {}\",\n            keyBits,\n            size,\n            loadFactor));\n    std::vector<uint64_t> keys;\n    for (uint64_t idx = 0; idx < size; idx++) {\n      keys.emplace_back(\n          folly::Random::rand64(rng) & folly::qms_detail::maxValue(keyBits));\n    }\n    buildAndValidate(keys, keyBits, loadFactor);\n  }\n}\n\nTEST_F(QuotientMultiSetTest, UniformDistributionFullLoadFactor) {\n  const uint64_t numElements = 1 << 16;\n  std::vector<uint64_t> keys;\n  for (uint64_t idx = 0; idx < numElements; idx++) {\n    uint64_t key = folly::Random::rand32(idx << 16, (idx + 1) << 16, rng);\n    keys.emplace_back(key);\n  }\n  buildAndValidate(keys, 32, 1.0);\n}\n\nTEST_F(QuotientMultiSetTest, Overflow) {\n  const uint64_t numElements = 1 << 12;\n  std::vector<uint64_t> keys;\n  for (uint64_t idx = 0; idx < numElements; idx++) {\n    keys.emplace_back(idx);\n    keys.emplace_back(idx);\n    keys.emplace_back(idx);\n  }\n  buildAndValidate(keys, 12, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, RandomLengthRuns) {\n  const uint64_t numElements = 1 << 16;\n  std::vector<uint64_t> keys;\n  for (uint64_t idx = 0; idx < (numElements >> 4); idx++) {\n    uint64_t key = folly::Random::rand32(rng);\n    uint64_t length = folly::Random::rand32(0, 10, rng);\n    for (uint64_t k = 0; k < length; k++) {\n      keys.emplace_back(key + k);\n    }\n  }\n  buildAndValidate(keys, 32, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, RunAcrossBlocks) {\n  const uint64_t numElements = 1 << 10;\n  std::vector<uint64_t> keys;\n  // Add keys with cluster size 137.\n  for (uint64_t idx = 0; idx < (numElements >> 4); idx++) {\n    uint64_t key = folly::Random::rand32(rng);\n    for (uint64_t k = 0; k < 136; k++) {\n      key += k;\n      keys.emplace_back(key);\n    }\n  }\n  buildAndValidate(keys, 32, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, PackAtHeadSlots) {\n  const uint64_t numElements = 1 << 12;\n  std::vector<uint64_t> keys;\n  for (uint64_t idx = 0; idx < numElements; idx++) {\n    uint64_t key = folly::Random::rand32(idx << 8, (idx + 1) << 8, rng);\n    keys.emplace_back(key);\n  }\n  buildAndValidate(keys, 32, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, PackAtTailSlots) {\n  const uint64_t numElements = 1 << 12;\n  std::vector<uint64_t> keys;\n  uint64_t key = (1 << 30);\n  for (uint64_t idx = 0; idx < numElements; idx++) {\n    keys.emplace_back(key + idx);\n  }\n  buildAndValidate(keys, 32, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, KeysOnlyInHeadAndTail) {\n  const uint64_t numElements = 1 << 11;\n  std::vector<uint64_t> keys;\n  for (uint64_t idx = 0; idx < numElements; idx++) {\n    keys.emplace_back(idx);\n  }\n  uint64_t key = (1 << 30);\n  for (uint64_t idx = 0; idx < numElements; idx++) {\n    keys.emplace_back(key + idx);\n  }\n  buildAndValidate(keys, 32, 0.95);\n}\n\nTEST_F(QuotientMultiSetTest, RunendRightBeforeFirstOccupiedRunend) {\n  std::vector<uint64_t> keys;\n  // 60 ranges [0, 67] with occupied slot 20.\n  for (size_t idx = 0; idx < 68; idx++) {\n    keys.push_back(60);\n  }\n  // 60 ranges [68, 68] with occupied slot 66.\n  for (size_t idx = 0; idx < 1; idx++) {\n    keys.push_back(200);\n  }\n  // 60 ranges [69, 88] with occupied slot 83.\n  for (size_t idx = 0; idx < 20; idx++) {\n    keys.push_back(250);\n  }\n  buildAndValidate(keys, 8, 0.95);\n}\n\n#endif // FOLLY_QUOTIENT_MULTI_SET_SUPPORTED\n"
  },
  {
    "path": "folly/compression/test/Select64Test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/Select64.h>\n\n#include <cstddef>\n#include <cstdint>\n\n#include <folly/compression/Instructions.h>\n#include <folly/portability/GTest.h>\n\n// for disassembling\nextern \"C\" uint64_t check_select64_default(uint64_t x, uint64_t k) {\n  return folly::select64<folly::compression::instructions::Default>(x, k);\n}\n#if FOLLY_X64 || defined(__i386)\nextern \"C\" uint64_t check_select64_haswell(uint64_t x, uint64_t k) {\n  return folly::select64<folly::compression::instructions::Haswell>(x, k);\n}\n#endif\n\nclass Select64Test : public testing::Test {};\n\nusing TestArch = folly::compression::instructions::Default;\n\nTEST_F(Select64Test, SelectInByteTable) {\n  for (size_t i = 0u; i < 256u; ++i) {\n    uint8_t decoded = 0;\n    for (size_t j = 0u; j < 8u; ++j) {\n      auto const entry = folly::detail::kSelectInByte[j][i];\n      decoded |= uint8_t(entry != 8) << entry;\n    }\n    EXPECT_EQ(i, decoded);\n  }\n}\n\nTEST_F(Select64Test, Select64) {\n  using instr = TestArch;\n  constexpr uint64_t kPrime = uint64_t(-59);\n  for (uint64_t x = kPrime, i = 0; i < (1 << 20); x *= kPrime, i += 1) {\n    auto const w = instr::popcount(x);\n    for (size_t k = 0; k < w; ++k) {\n      auto const pos = folly::select64<instr>(x, k);\n      CHECK_EQ((x >> pos) & 1, 1);\n      CHECK_EQ(instr::popcount(x & ((uint64_t(1) << pos) - 1)), k);\n    }\n  }\n}\n"
  },
  {
    "path": "folly/concurrency/AtomicSharedPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstdint>\n#include <thread>\n\n#include <folly/PackedSyncPtr.h>\n#include <folly/concurrency/detail/AtomicSharedPtr-detail.h>\n#include <folly/memory/SanitizeLeak.h>\n#include <folly/synchronization/AtomicStruct.h>\n#include <folly/synchronization/AtomicUtil.h>\n#include <folly/synchronization/detail/AtomicUtils.h>\n\n#if defined(__GLIBCXX__) && FOLLY_HAS_PACKED_SYNC_PTR\n#define FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED 1\n#else\n#define FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED 0\n#endif\n\nnamespace folly {\n\n#if FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED\n\n/*\n * This is an implementation of the std::atomic_shared_ptr TS\n * http://en.cppreference.com/w/cpp/experimental/atomic_shared_ptr\n * https://isocpp.org/files/papers/N4162.pdf\n *\n * AFAIK, the only other implementation is Anthony Williams from\n * Just::thread library:\n *\n * https://github.com/anthonywilliams/atomic_shared_ptr\n *\n * implementation details:\n *\n * Basically, three things need to be atomically exchanged to make this work:\n * * the local count\n * * the pointer to the control block\n * * the aliased pointer, if any.\n *\n * The Williams version does it with DWcas: 32 bits for local count, 64\n * bits for control block ptr, and he changes the shared_ptr\n * implementation to also store the aliased pointers using a linked list\n * like structure, and provides 32-bit index accessors to them (like\n * IndexedMemPool trick).\n *\n * This version instead stores the 48 bits of address, plus 16 bits of\n * local count in a single 8byte pointer.  This avoids 'lock cmpxchg16b',\n * which is much slower than 'lock xchg' in the normal 'store' case.  In\n * the less-common aliased pointer scenario, we just allocate it in a new\n * block, and store a pointer to that instead.\n *\n * Note that even if we only want to use the 3-bits of pointer alignment,\n * this trick should still work - Any more than 4 concurrent accesses\n * will have to go to an external map count instead (slower, but lots of\n * concurrent access will be slow anyway due to bouncing cachelines).\n *\n * As a perf optimization, we currently batch up local count and only\n * move it global every once in a while.  This means load() is usually\n * only a single atomic operation, instead of 3.  For this trick to work,\n * we probably need at least 8 bits to make batching worth it.\n */\n\n// A note on noexcept: If the pointer is an aliased pointer,\n// store() will allocate.  Otherwise is noexcept.\n\ntemplate <\n    typename T,\n    template <typename> class Atom = std::atomic,\n    typename CountedDetail = detail::shared_ptr_internals>\nclass atomic_shared_ptr {\n  using SharedPtr = typename CountedDetail::template CountedPtr<T>;\n  using BasePtr = typename CountedDetail::counted_base;\n  using PackedPtr = folly::PackedSyncPtr<BasePtr>;\n\n public:\n  atomic_shared_ptr() noexcept { init(); }\n  explicit atomic_shared_ptr(SharedPtr foo) /* noexcept */\n      : atomic_shared_ptr() {\n    store(std::move(foo));\n  }\n  atomic_shared_ptr(const atomic_shared_ptr<T>&) = delete;\n\n  ~atomic_shared_ptr() { store(SharedPtr(nullptr)); }\n  void operator=(SharedPtr desired) /* noexcept */ {\n    store(std::move(desired));\n  }\n  void operator=(const atomic_shared_ptr<T>&) = delete;\n\n  bool is_lock_free() const noexcept {\n    // lock free unless more than EXTERNAL_OFFSET threads are\n    // contending and they all get unlucky and scheduled out during\n    // load().\n    //\n    // TODO: Could use a lock-free external map to fix this\n    // corner case.\n    return true;\n  }\n\n  SharedPtr load(\n      std::memory_order order = std::memory_order_seq_cst) const noexcept {\n    auto local = takeOwnedBase(order);\n    return get_shared_ptr(local, false);\n  }\n\n  /* implicit */ operator SharedPtr() const { return load(); }\n\n  void store(\n      SharedPtr n,\n      std::memory_order order = std::memory_order_seq_cst) /* noexcept */ {\n    auto newptr = get_newptr(std::move(n));\n    auto old = ptr_.exchange(newptr, order);\n    release_external(old);\n  }\n\n  SharedPtr exchange(\n      SharedPtr n,\n      std::memory_order order = std::memory_order_seq_cst) /* noexcept */ {\n    auto newptr = get_newptr(std::move(n));\n    auto old = ptr_.exchange(newptr, order);\n\n    SharedPtr old_ptr;\n\n    if (old.get()) {\n      old_ptr = get_shared_ptr(old);\n      release_external(old);\n    }\n\n    return old_ptr;\n  }\n\n  bool compare_exchange_weak(\n      SharedPtr& expected,\n      const SharedPtr& n,\n      std::memory_order mo = std::memory_order_seq_cst) noexcept {\n    return compare_exchange_weak(\n        expected, n, mo, detail::default_failure_memory_order(mo));\n  }\n  bool compare_exchange_weak(\n      SharedPtr& expected,\n      const SharedPtr& n,\n      std::memory_order success,\n      std::memory_order failure) /* noexcept */ {\n    auto newptr = get_newptr(n);\n    PackedPtr oldptr, expectedptr;\n\n    oldptr = takeOwnedBase(success);\n    if (!owners_eq(oldptr, CountedDetail::get_counted_base(expected))) {\n      expected = get_shared_ptr(oldptr, false);\n      release_external(newptr);\n      return false;\n    }\n    expectedptr = oldptr; // Need oldptr to release if failed\n    if (ptr_.compare_exchange_weak(expectedptr, newptr, success, failure)) {\n      if (oldptr.get()) {\n        release_external(oldptr, -1);\n      }\n      return true;\n    } else {\n      if (oldptr.get()) {\n        expected = get_shared_ptr(oldptr, false);\n      } else {\n        expected = SharedPtr(nullptr);\n      }\n      release_external(newptr);\n      return false;\n    }\n  }\n  bool compare_exchange_weak(\n      SharedPtr& expected,\n      SharedPtr&& desired,\n      std::memory_order mo = std::memory_order_seq_cst) noexcept {\n    return compare_exchange_weak(\n        expected, desired, mo, detail::default_failure_memory_order(mo));\n  }\n  bool compare_exchange_weak(\n      SharedPtr& expected,\n      SharedPtr&& desired,\n      std::memory_order success,\n      std::memory_order failure) /* noexcept */ {\n    return compare_exchange_weak(expected, desired, success, failure);\n  }\n  bool compare_exchange_strong(\n      SharedPtr& expected,\n      const SharedPtr& n,\n      std::memory_order mo = std::memory_order_seq_cst) noexcept {\n    return compare_exchange_strong(\n        expected, n, mo, detail::default_failure_memory_order(mo));\n  }\n  bool compare_exchange_strong(\n      SharedPtr& expected,\n      const SharedPtr& n,\n      std::memory_order success,\n      std::memory_order failure) /* noexcept */ {\n    auto local_expected = expected;\n    do {\n      if (compare_exchange_weak(expected, n, success, failure)) {\n        return true;\n      }\n    } while (local_expected == expected);\n\n    return false;\n  }\n  bool compare_exchange_strong(\n      SharedPtr& expected,\n      SharedPtr&& desired,\n      std::memory_order mo = std::memory_order_seq_cst) noexcept {\n    return compare_exchange_strong(\n        expected, desired, mo, detail::default_failure_memory_order(mo));\n  }\n  bool compare_exchange_strong(\n      SharedPtr& expected,\n      SharedPtr&& desired,\n      std::memory_order success,\n      std::memory_order failure) /* noexcept */ {\n    return compare_exchange_strong(expected, desired, success, failure);\n  }\n\n private:\n  // Matches packed_sync_pointer.  Must be > max number of local\n  // counts.  This is the max number of threads that can access this\n  // atomic_shared_ptr at once before we start blocking.\n  static constexpr std::uint16_t EXTERNAL_OFFSET{0x2000};\n  // Bit signifying aliased constructor\n  static constexpr std::uint16_t ALIASED_PTR{0x4000};\n\n  static std::uint16_t get_local_count(const PackedPtr& p) {\n    return p.extra() & ~ALIASED_PTR;\n  }\n\n  static void add_external(BasePtr* res, int64_t c = 0) {\n    assert(res);\n    CountedDetail::inc_shared_count(res, EXTERNAL_OFFSET + c);\n    annotate_object_leaked(res);\n  }\n  static void release_external(PackedPtr& res, int64_t c = 0) {\n    if (!res.get()) {\n      return;\n    }\n    annotate_object_collected(res.get());\n    int64_t count = get_local_count(res) + c;\n    int64_t diff = EXTERNAL_OFFSET - count;\n    assert(diff >= 0);\n    CountedDetail::template release_shared<T>(res.get(), diff);\n  }\n\n  static PackedPtr get_newptr(const SharedPtr& n) {\n    return get_newptr_impl<false>(n);\n  }\n  static PackedPtr get_newptr(SharedPtr&& n) {\n    return get_newptr_impl<true>(std::move(n));\n  }\n  template <bool kOwn, class S>\n  static PackedPtr get_newptr_impl(S&& n) {\n    std::uint16_t count = 0;\n    BasePtr* newval = CountedDetail::get_counted_base(n);\n    if (!n && newval == nullptr) {\n      // n is default-constructed, nothing to do.\n    } else if (\n        newval == nullptr ||\n        n.get() != CountedDetail::template get_shared_ptr<T>(newval)) {\n      // This is an aliased sharedptr.  Make an un-aliased one by wrapping in\n      // *another* shared_ptr.\n      auto data =\n          CountedDetail::template make_ptr<SharedPtr>(std::forward<S>(n));\n      newval = CountedDetail::get_counted_base(data);\n      count = ALIASED_PTR;\n      CountedDetail::release_ptr(data);\n      add_external(newval, -1);\n    } else {\n      if constexpr (kOwn) {\n        CountedDetail::release_ptr(n);\n      }\n      add_external(newval, kOwn ? -1 : 0);\n    }\n\n    PackedPtr newptr;\n    newptr.init(newval, count);\n\n    return newptr;\n  }\n\n  void init() {\n    PackedPtr data;\n    data.init();\n    ptr_.store(data);\n  }\n\n  // Check pointer equality considering wrapped aliased pointers.\n  bool owners_eq(PackedPtr& p1, BasePtr* p2) {\n    bool aliased1 = p1.extra() & ALIASED_PTR;\n    if (aliased1) {\n      auto p1a = CountedDetail::template get_shared_ptr_from_counted_base<T>(\n          p1.get(), false);\n      return CountedDetail::get_counted_base(p1a) == p2;\n    }\n    return p1.get() == p2;\n  }\n\n  SharedPtr get_shared_ptr(const PackedPtr& p, bool inc = true) const {\n    bool aliased = p.extra() & ALIASED_PTR;\n\n    auto res = CountedDetail::template get_shared_ptr_from_counted_base<T>(\n        p.get(), inc);\n    if (aliased) {\n      auto aliasedp =\n          CountedDetail::template get_shared_ptr_from_counted_base<SharedPtr>(\n              p.get());\n      res = *aliasedp;\n    }\n    return res;\n  }\n\n  /* Get a reference to the pointer, either from the local batch or\n   * from the global count.\n   *\n   * return is the base ptr, and the previous local count, if it is\n   * needed for compare_and_swap later.\n   */\n  PackedPtr takeOwnedBase(std::memory_order order) const noexcept {\n    PackedPtr local, newlocal;\n    local = ptr_.load(std::memory_order_acquire);\n    while (true) {\n      if (!local.get()) {\n        return local;\n      }\n      newlocal = local;\n      if (get_local_count(newlocal) + 1 > EXTERNAL_OFFSET) {\n        // spinlock in the rare case we have more than\n        // EXTERNAL_OFFSET threads trying to access at once.\n        std::this_thread::yield();\n        // Force DeterministicSchedule to choose a different thread\n        local = ptr_.load(std::memory_order_acquire);\n      } else {\n        newlocal.setExtra(newlocal.extra() + 1);\n        assert(get_local_count(newlocal) > 0);\n        if (ptr_.compare_exchange_weak(local, newlocal, order)) {\n          break;\n        }\n      }\n    }\n\n    // Check if we need to push a batch from local -> global\n    std::uint16_t batchcount = EXTERNAL_OFFSET / 2;\n    if (get_local_count(newlocal) > batchcount) {\n      CountedDetail::inc_shared_count(newlocal.get(), batchcount);\n      putOwnedBase(newlocal.get(), batchcount, order);\n    }\n\n    return newlocal;\n  }\n\n  void putOwnedBase(\n      BasePtr* p, std::uint16_t count, std::memory_order mo) const noexcept {\n    PackedPtr local = ptr_.load(std::memory_order_acquire);\n    while (true) {\n      if (local.get() != p) {\n        break;\n      }\n      auto newlocal = local;\n      if (get_local_count(local) > count) {\n        newlocal.setExtra(local.extra() - count);\n      } else {\n        // Otherwise it may be the same pointer, but someone else won\n        // the compare_exchange below, local count was already made\n        // global.  We decrement the global count directly instead of\n        // the local one.\n        break;\n      }\n      if (ptr_.compare_exchange_weak(local, newlocal, mo)) {\n        return;\n      }\n    }\n\n    CountedDetail::template release_shared<T>(p, count);\n  }\n\n  mutable AtomicStruct<PackedPtr, Atom> ptr_;\n};\n\n#else\n\ntemplate <typename T>\nclass atomic_shared_ptr {\n private:\n  std::shared_ptr<T> rep_;\n\n public:\n  using value_type = std::shared_ptr<T>;\n\n  atomic_shared_ptr() = default;\n  atomic_shared_ptr(std::nullptr_t) noexcept {}\n  atomic_shared_ptr(std::shared_ptr<T> desired) noexcept\n      : rep_{std::move(desired)} {}\n\n  atomic_shared_ptr(atomic_shared_ptr const&) = delete;\n  atomic_shared_ptr(atomic_shared_ptr&&) = delete;\n\n  void operator=(std::nullptr_t) noexcept { store(nullptr); }\n  void operator=(std::shared_ptr<T> desired) noexcept {\n    store(std::move(desired));\n  }\n\n  void operator=(atomic_shared_ptr const&) = delete;\n  void operator=(atomic_shared_ptr&&) = delete;\n\n  /* implicit */ operator std::shared_ptr<T>() const noexcept { return load(); }\n\n  bool is_lock_free() const noexcept { return atomic_is_lock_free(&rep_); }\n\n  std::shared_ptr<T> load(\n      std::memory_order order = std::memory_order_seq_cst) const noexcept {\n    return atomic_load_explicit(&rep_, order);\n  }\n\n  void store(\n      std::shared_ptr<T> desired,\n      std::memory_order order = std::memory_order_seq_cst) noexcept {\n    atomic_store_explicit(&rep_, std::move(desired), order);\n  }\n\n  std::shared_ptr<T> exchange(\n      std::shared_ptr<T> desired,\n      std::memory_order order = std::memory_order_seq_cst) noexcept {\n    return atomic_exchange_explicit(&rep_, std::move(desired), order);\n  }\n\n  bool compare_exchange_weak(\n      std::shared_ptr<T>& expected,\n      std::shared_ptr<T> desired,\n      std::memory_order success,\n      std::memory_order failure) noexcept {\n    return atomic_compare_exchange_weak_explicit(\n        &rep_, &expected, std::move(desired), success, failure);\n  }\n\n  bool compare_exchange_weak(\n      std::shared_ptr<T>& expected,\n      std::shared_ptr<T> desired,\n      std::memory_order order = std::memory_order_seq_cst) noexcept {\n    return atomic_compare_exchange_weak_explicit(\n        &rep_, &expected, std::move(desired), order, memory_order_load(order));\n  }\n\n  bool compare_exchange_strong(\n      std::shared_ptr<T>& expected,\n      std::shared_ptr<T> desired,\n      std::memory_order success,\n      std::memory_order failure) noexcept {\n    return atomic_compare_exchange_strong_explicit(\n        &rep_, &expected, std::move(desired), success, failure);\n  }\n\n  bool compare_exchange_strong(\n      std::shared_ptr<T>& expected,\n      std::shared_ptr<T> desired,\n      std::memory_order order = std::memory_order_seq_cst) noexcept {\n    return atomic_compare_exchange_strong_explicit(\n        &rep_, &expected, std::move(desired), order, memory_order_load(order));\n  }\n};\n\n#endif // FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED\n\ntemplate <typename T>\natomic_shared_ptr(std::shared_ptr<T>) -> atomic_shared_ptr<T>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"core_cached_shared_ptr\",\n    headers = [\n        \"CoreCachedSharedPtr.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":cache_locality\",\n        \"//folly:cpp_attributes\",\n        \"//folly:portability\",\n        \"//folly:unit\",\n        \"//folly/synchronization:hazptr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"dynamic_bounded_queue\",\n    headers = [\n        \"DynamicBoundedQueue.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":cache_locality\",\n        \":unbounded_queue\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"unbounded_queue\",\n    headers = [\n        \"UnboundedQueue.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":cache_locality\",\n        \"//folly:constexpr_math\",\n        \"//folly:optional\",\n        \"//folly:traits\",\n        \"//folly/lang:align\",\n        \"//folly/synchronization:hazptr\",\n        \"//folly/synchronization:saturating_semaphore\",\n        \"//folly/synchronization:wait_options\",\n        \"//folly/synchronization/detail:spin\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"concurrent_hash_map\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"ConcurrentHashMap.h\",\n        \"detail/ConcurrentHashMap-detail.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/synchronization:hazptr\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly/container:heterogeneous_access\",\n        \"//xplat/folly/container/detail:f14_mask\",\n        \"//xplat/folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"deadlock_detector\",\n    srcs = [\"DeadlockDetector.cpp\"],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\"DeadlockDetector.h\"],\n    exported_deps = [\n        \"//xplat/folly:executor\",\n        \"//xplat/folly/executors:queue_observer\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"cache_locality\",\n    srcs = [\n        \"CacheLocality.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    cxx_deps = [\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n    fbandroid_deps = [\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n    raw_headers = [\n        \"CacheLocality.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:fmt_compile\",\n        \"fbsource//xplat/folly/portability:unistd\",\n        \"//third-party/fmt:fmt\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly/container:reserve\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/system:thread_id\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/synchronization:atomic_ref\",\n        \"//xplat/folly:indestructible\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly/detail:static_singleton_manager\",\n        \"//xplat/folly/lang:align\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"thread_cached_synchronized\",\n    headers = [\"ThreadCachedSynchronized.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:shared_mutex\",\n        \"//folly:thread_local\",\n        \"//folly:utility\",\n        \"//folly/lang:access\",\n        \"//folly/synchronization:lock\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"atomic_shared_ptr\",\n    headers = [\n        \"AtomicSharedPtr.h\",\n        \"detail/AtomicSharedPtr-detail.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:packed_sync_ptr\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/memory:sanitize_leak\",\n        \"//folly/synchronization:atomic_struct\",\n        \"//folly/synchronization:atomic_util\",\n        \"//folly/synchronization/detail:atomic_utils\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"priority_unbounded_queue_set\",\n    headers = [\n        \"PriorityUnboundedQueueSet.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":unbounded_queue\",\n        \"//folly:memory\",\n        \"//folly/lang:align\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"process_local_unique_id\",\n    srcs = [\"ProcessLocalUniqueId.cpp\"],\n    headers = [\"ProcessLocalUniqueId.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:likely\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"singleton_relaxed_counter\",\n    headers = [\n        \"SingletonRelaxedCounter.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:likely\",\n        \"//folly:portability\",\n        \"//folly:synchronized\",\n        \"//folly:utility\",\n        \"//folly/detail:static_singleton_manager\",\n        \"//folly/detail:thread_local_globals\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/synchronization:atomic_ref\",\n    ],\n)\n\n# !!!! fbcode/folly/concurrency/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"cache_locality\",\n    srcs = [\"CacheLocality.cpp\"],\n    headers = [\"CacheLocality.h\"],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:indestructible\",\n        \"//folly:memory\",\n        \"//folly:scope_guard\",\n        \"//folly/container:reserve\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:exception\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:fmt_compile\",\n        \"//folly/portability:unistd\",\n        \"//folly/system:thread_id\",\n    ],\n    exported_deps = [\n        \"//folly:likely\",\n        \"//folly:portability\",\n        \"//folly/lang:align\",\n        \"//folly/synchronization:atomic_ref\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"concurrent_hash_map\",\n    headers = [\n        \"ConcurrentHashMap.h\",\n        \"detail/ConcurrentHashMap-detail.h\",\n    ],\n    exported_deps = [\n        \"//folly:optional\",\n        \"//folly:scope_guard\",\n        \"//folly/container:heterogeneous_access\",\n        \"//folly/container/detail:f14_mask\",\n        \"//folly/lang:exception\",\n        \"//folly/synchronization:hazptr\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"deadlock_detector\",\n    srcs = [\"DeadlockDetector.cpp\"],\n    headers = [\n        \"DeadlockDetector.h\",\n    ],\n    exported_deps = [\n        \"//folly:executor\",\n        \"//folly/executors:queue_observer\",\n    ],\n)\n"
  },
  {
    "path": "folly/concurrency/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME atomic_shared_ptr\n  HEADERS\n    AtomicSharedPtr.h\n    detail/AtomicSharedPtr-detail.h\n  EXPORTED_DEPS\n    folly_lang_safe_assert\n    folly_memory_sanitize_leak\n    folly_packed_sync_ptr\n    folly_synchronization_atomic_struct\n    folly_synchronization_atomic_util\n    folly_synchronization_detail_atomic_utils\n)\n\nfolly_add_library(\n  NAME cache_locality\n  SRCS\n    CacheLocality.cpp\n  HEADERS\n    CacheLocality.h\n  DEPS\n    folly_container_reserve\n    folly_hash_hash\n    folly_indestructible\n    folly_lang_exception\n    folly_memory\n    folly_portability_fcntl\n    folly_portability_fmt_compile\n    folly_portability_unistd\n    folly_scope_guard\n    folly_system_thread_id\n  EXPORTED_DEPS\n    folly_lang_align\n    folly_likely\n    folly_portability\n    folly_synchronization_atomic_ref\n)\n\nfolly_add_library(\n  NAME concurrent_hash_map\n  HEADERS\n    ConcurrentHashMap.h\n    detail/ConcurrentHashMap-detail.h\n  EXPORTED_DEPS\n    folly_container_detail_f14_mask\n    folly_container_heterogeneous_access\n    folly_lang_exception\n    folly_optional\n    folly_scope_guard\n    folly_synchronization_hazptr\n)\n\nfolly_add_library(\n  NAME core_cached_shared_ptr\n  HEADERS\n    CoreCachedSharedPtr.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_cpp_attributes\n    folly_portability\n    folly_synchronization_hazptr\n    folly_unit\n)\n\nfolly_add_library(\n  NAME deadlock_detector\n  SRCS\n    DeadlockDetector.cpp\n  HEADERS\n    DeadlockDetector.h\n  EXPORTED_DEPS\n    folly_executor\n    folly_executors_queue_observer\n)\n\nfolly_add_library(\n  NAME dynamic_bounded_queue\n  HEADERS\n    DynamicBoundedQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_concurrency_unbounded_queue\n)\n\nfolly_add_library(\n  NAME priority_unbounded_queue_set\n  HEADERS\n    PriorityUnboundedQueueSet.h\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_lang_align\n    folly_memory\n)\n\nfolly_add_library(\n  NAME process_local_unique_id\n  SRCS\n    ProcessLocalUniqueId.cpp\n  HEADERS\n    ProcessLocalUniqueId.h\n  DEPS\n    folly_likely\n    folly_synchronization_relaxed_atomic\n)\n\nfolly_add_library(\n  NAME singleton_relaxed_counter\n  HEADERS\n    SingletonRelaxedCounter.h\n  EXPORTED_DEPS\n    folly_detail_static_singleton_manager\n    folly_detail_thread_local_globals\n    folly_lang_safe_assert\n    folly_likely\n    folly_portability\n    folly_synchronization_atomic_ref\n    folly_synchronized\n    folly_utility\n)\n\nfolly_add_library(\n  NAME thread_cached_synchronized\n  HEADERS\n    ThreadCachedSynchronized.h\n  EXPORTED_DEPS\n    folly_lang_access\n    folly_shared_mutex\n    folly_synchronization_lock\n    folly_synchronization_relaxed_atomic\n    folly_thread_local\n    folly_utility\n)\n\nfolly_add_library(\n  NAME unbounded_queue\n  HEADERS\n    UnboundedQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_constexpr_math\n    folly_lang_align\n    folly_optional\n    folly_synchronization_detail_spin\n    folly_synchronization_hazptr\n    folly_synchronization_saturating_semaphore\n    folly_synchronization_wait_options\n    folly_traits\n)\n\nadd_subdirectory(container)\nadd_subdirectory(memory)\n"
  },
  {
    "path": "folly/concurrency/CacheLocality.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/CacheLocality.h>\n\n#ifndef _MSC_VER\n#define _GNU_SOURCE 1 // for RTLD_NOLOAD\n#include <dlfcn.h>\n#endif\n#include <string.h>\n\n#include <filesystem>\n#include <fstream>\n#include <mutex>\n#include <numeric>\n#include <optional>\n#include <unordered_map>\n\n#include <fmt/core.h>\n#include <glog/logging.h>\n#include <folly/Indestructible.h>\n#include <folly/Memory.h>\n#include <folly/ScopeGuard.h>\n#include <folly/container/Reserve.h>\n#include <folly/hash/Hash.h>\n#include <folly/lang/Exception.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/FmtCompile.h>\n#include <folly/portability/Unistd.h>\n#include <folly/system/ThreadId.h>\n\nnamespace folly {\n\n///////////// CacheLocality\n\n/// Returns the CacheLocality information best for this machine\nCacheLocality CacheLocality::readSystemLocalityInfo() {\n  if (kIsLinux) {\n    if (kIsArchAmd64 || kIsArchX86) {\n      // First try to parse /proc/cpuinfo.\n      // But only on arch's where the file has cpu topology hints.\n      // If that fails, then try to parse /sys/devices/.\n      // The latter is slower but more accurate.\n      try {\n        return CacheLocality::readFromProcCpuinfo();\n      } catch (...) {\n        // /proc/cpuinfo might be non-standard\n        // lets try with sysfs /sys/devices/cpu\n      }\n    }\n\n    try {\n      return CacheLocality::readFromSysfs();\n    } catch (...) {\n      // keep trying\n    }\n  }\n\n  long numCpus = sysconf(_SC_NPROCESSORS_CONF);\n  if (numCpus <= 0) {\n    // This shouldn't happen, but if it does we should try to keep\n    // going.  We are probably not going to be able to parse /sys on\n    // this box either (although we will try), which means we are going\n    // to fall back to the SequentialThreadId splitter.  On my 16 core\n    // (x hyperthreading) dev box 16 stripes is enough to get pretty good\n    // contention avoidance with SequentialThreadId, and there is little\n    // improvement from going from 32 to 64.  This default gives us some\n    // wiggle room\n    numCpus = 32;\n  }\n  return CacheLocality::uniform(size_t(numCpus));\n}\n\ntemplate <>\nconst CacheLocality& CacheLocality::system<std::atomic>() {\n  static std::atomic<const CacheLocality*> cache;\n  auto value = cache.load(std::memory_order_acquire);\n  if (value != nullptr) {\n    return *value;\n  }\n  auto next = new CacheLocality(readSystemLocalityInfo());\n  if (cache.compare_exchange_strong(value, next, std::memory_order_acq_rel)) {\n    return *next;\n  }\n  delete next;\n  return *value;\n}\n\nCacheLocality::CacheLocality(std::vector<std::vector<size_t>> equivClasses) {\n  numCpus = equivClasses.size();\n\n  for (size_t cpu = 0; cpu < numCpus; ++cpu) {\n    for (size_t level = 0; level < equivClasses[cpu].size(); ++level) {\n      if (equivClasses[cpu][level] == cpu) {\n        // we only want to count the equiv classes once, so we do it when we\n        // are processing their representative.\n        while (numCachesByLevel.size() <= level) {\n          numCachesByLevel.push_back(0);\n        }\n        numCachesByLevel[level]++;\n      }\n    }\n  }\n\n  std::vector<size_t> cpus(numCpus);\n  std::iota(cpus.begin(), cpus.end(), 0);\n\n  std::sort(cpus.begin(), cpus.end(), [&](size_t lhs, size_t rhs) -> bool {\n    auto& lhsEquiv = equivClasses[lhs];\n    auto& rhsEquiv = equivClasses[rhs];\n\n    // If different cpus have different numbers of caches group first by number\n    // of caches to guarantee strict weak ordering, even though the resulting\n    // order may be sub-optimal.\n    if (lhsEquiv.size() != rhsEquiv.size()) {\n      return lhsEquiv.size() < rhsEquiv.size();\n    }\n\n    // Order by equiv class of cache with highest index, direction doesn't\n    // matter.\n    for (size_t i = lhsEquiv.size(); i > 0; --i) {\n      auto idx = i - 1;\n      if (lhsEquiv[idx] != rhsEquiv[idx]) {\n        return lhsEquiv[idx] < rhsEquiv[idx];\n      }\n    }\n\n    // Break ties deterministically by cpu.\n    return lhs < rhs;\n  });\n\n  // The cpus are now sorted by locality, with neighboring entries closer\n  // to each other than entries that are far away.  For striping we want\n  // the inverse map, since we are starting with the cpu.\n  localityIndexByCpu.resize(numCpus);\n  for (size_t i = 0; i < cpus.size(); ++i) {\n    localityIndexByCpu[cpus[i]] = i;\n  }\n\n  equivClassesByCpu = std::move(equivClasses);\n}\n\n// Each level of cache has sharing sets, which are the set of cpus that share a\n// common cache at that level.  These are available in a hex bitset form\n// (/sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_map, for example).\n// They are also available in human-readable form in the shared_cpu_list file in\n// the same directory.  The list is a comma-separated list of numbers and\n// ranges, where the ranges are pairs of decimal numbers separated by a '-'.\n//\n// To sort the cpus for optimum locality we don't really need to parse the\n// sharing sets, we just need a unique representative from the equivalence\n// class.  The smallest value works fine, and happens to be the first decimal\n// number in the file.  We load all of the equivalence class information from\n// all of the cpu*/index* directories, order the cpus first by increasing\n// last-level cache equivalence class, then by the smaller caches.  Finally, we\n// break ties with the cpu number itself.\n\n/// Returns the first decimal number in the line, or throws an exception if the\n/// line does not start with a number terminated by ',', '-', '\\n', or EOS.\nstatic size_t parseLeadingNumber(const std::string& line) {\n  auto raw = line.c_str();\n  char* end;\n  unsigned long val = strtoul(raw, &end, 10);\n  if (end == raw || (*end != ',' && *end != '-' && *end != '\\n' && *end != 0)) {\n    throw std::runtime_error(fmt::format(\"error parsing list '{}'\", line));\n  }\n  return val;\n}\n\nCacheLocality CacheLocality::readFromSysfsTree(std::string_view root) {\n#if defined(_WIN32)\n  // windows does not have openat and open flag constants\n  return CacheLocality::uniform(0);\n#else\n\n  // the list of cache equivalence classes, where equivalence classes\n  // are named by the smallest cpu in the class\n  std::vector<std::vector<size_t>> equivClassesByCpu;\n\n  auto checkNoEnt = [](std::string_view name) {\n    auto err = errno;\n    if (err != ENOENT) {\n      throw std::runtime_error(\n          fmt::format(\n              \"unexpected error while opening {}: {}\", name, strerror(err)));\n    }\n  };\n\n  // Reads the first 64 bytes of the file.\n  auto rdfile64 = [&](int dirfd, const std::string& name) {\n    const auto fd = ::openat(dirfd, name.c_str(), O_CLOEXEC, O_RDONLY);\n    if (fd < 0) {\n      checkNoEnt(name);\n      return std::string(); // stop condition for the inner loop below\n    }\n    SCOPE_EXIT {\n      ::close(fd);\n    };\n\n    alignas(64) char buf[64];\n    int ret = 0;\n    do {\n      ret = ::pread(fd, buf, sizeof(buf), 0);\n    } while (ret < 0 && errno == EINTR);\n    if (ret < 0) {\n      return std::string();\n    }\n    return std::string(buf, to_unsigned(ret));\n  };\n\n  auto subroot = std::filesystem::path(root) / \"sys/devices/system/cpu\";\n  const int allfd = ::open(subroot.c_str(), O_DIRECTORY | O_CLOEXEC, O_RDONLY);\n  if (allfd < 0) {\n    auto err = errno;\n    throw std::runtime_error(\n        fmt::format(\"unable to open sysfs: {}\", strerror(err)));\n  }\n  SCOPE_EXIT {\n    ::close(allfd);\n  };\n\n  size_t maxindex = 0;\n  for (size_t cpu = 0;; ++cpu) {\n    auto cpuroot = fmt::format(FMT_COMPILE(\"cpu{}/cache\"), cpu);\n    const int cpufd =\n        ::openat(allfd, cpuroot.c_str(), O_DIRECTORY | O_CLOEXEC, O_RDONLY);\n    if (cpufd < 0) {\n      checkNoEnt(cpuroot);\n      break;\n    }\n    SCOPE_EXIT {\n      ::close(cpufd);\n    };\n\n    std::vector<size_t> levels;\n    grow_capacity_by(levels, maxindex);\n    for (size_t index = 0;; ++index) {\n      auto dir = fmt::format(FMT_COMPILE(\"index{}/\"), index);\n      auto cacheType = rdfile64(cpufd, dir + \"type\");\n      if (cacheType.empty()) {\n        // no more caches\n        break;\n      }\n      if (cacheType[0] == 'I') {\n        // cacheType in { \"Data\", \"Instruction\", \"Unified\" }. skip icache\n        continue;\n      }\n      // only try to read the second file once we know we will need it\n      auto equivStr = rdfile64(cpufd, dir + \"shared_cpu_list\");\n      if (equivStr.empty()) {\n        // no more caches\n        break;\n      }\n      auto equiv = parseLeadingNumber(equivStr);\n      levels.push_back(equiv);\n    }\n    maxindex = std::max(maxindex, levels.size());\n\n    if (levels.empty()) {\n      // no levels at all for this cpu, we must be done\n      break;\n    }\n    equivClassesByCpu.emplace_back(std::move(levels));\n  }\n\n  if (equivClassesByCpu.empty()) {\n    throw std::runtime_error(\"unable to load cache sharing info\");\n  }\n\n  return CacheLocality{std::move(equivClassesByCpu)};\n\n#endif\n}\n\nCacheLocality CacheLocality::readFromSysfs() {\n  return readFromSysfsTree();\n}\n\nnamespace {\n\nstatic bool procCpuinfoLineRelevant(std::string const& line) {\n  return line.size() > 4 && (line[0] == 'p' || line[0] == 'c');\n}\n\nstd::vector<std::tuple<size_t, size_t, size_t>> parseProcCpuinfoLines(\n    std::vector<std::string> const& lines) {\n  std::vector<std::tuple<size_t, size_t, size_t>> cpus;\n  size_t physicalId = 0;\n  size_t coreId = 0;\n  size_t maxCpu = 0;\n  size_t numberOfPhysicalIds = 0;\n  size_t numberOfCoreIds = 0;\n  for (auto iter = lines.rbegin(); iter != lines.rend(); ++iter) {\n    auto& line = *iter;\n    if (!procCpuinfoLineRelevant(line)) {\n      continue;\n    }\n\n    auto sepIndex = line.find(':');\n    if (sepIndex == std::string::npos || sepIndex + 2 > line.size()) {\n      continue;\n    }\n    auto arg = line.substr(sepIndex + 2);\n\n    // \"physical id\" is socket, which is the most important locality\n    // context.  \"core id\" is a real core, so two \"processor\" entries with\n    // the same physical id and core id are hyperthreads of each other.\n    // \"processor\" is the top line of each record, so when we hit it in\n    // the reverse order then we can emit a record.\n    if (line.find(\"physical id\") == 0) {\n      physicalId = parseLeadingNumber(arg);\n      ++numberOfPhysicalIds;\n    } else if (line.find(\"core id\") == 0) {\n      coreId = parseLeadingNumber(arg);\n      ++numberOfCoreIds;\n    } else if (line.find(\"processor\") == 0) {\n      auto cpu = parseLeadingNumber(arg);\n      maxCpu = std::max(cpu, maxCpu);\n      cpus.emplace_back(physicalId, coreId, cpu);\n    }\n  }\n\n  if (cpus.empty()) {\n    throw std::runtime_error(\"no CPUs parsed from /proc/cpuinfo\");\n  }\n  if (maxCpu != cpus.size() - 1) {\n    throw std::runtime_error(\n        \"offline CPUs not supported for /proc/cpuinfo cache locality source\");\n  }\n  if (numberOfPhysicalIds == 0) {\n    throw std::runtime_error(\"no physical ids found\");\n  }\n  if (numberOfCoreIds == 0) {\n    throw std::runtime_error(\"no core ids found\");\n  }\n\n  return cpus;\n}\n\n} // namespace\n\nCacheLocality CacheLocality::readFromProcCpuinfoLines(\n    std::vector<std::string> const& lines) {\n  // (physicalId, coreId, cpu)\n  std::vector<std::tuple<size_t, size_t, size_t>> cpus =\n      parseProcCpuinfoLines(lines);\n  // Sort to make equivalence classes contiguous.\n  std::sort(cpus.begin(), cpus.end());\n\n  // We can't tell the real cache hierarchy from /proc/cpuinfo, but it works\n  // well enough to assume there are 3 levels, L1 and L2 per-core and L3 per\n  // socket.\n  std::vector<std::vector<size_t>> equivClassesByCpu(cpus.size());\n  size_t l1Equiv = 0;\n  size_t l3Equiv = 0;\n  for (size_t i = 0; i < cpus.size(); ++i) {\n    auto [physicalId, coreId, cpu] = cpus[i];\n    // The representative for each L1 and L3 equivalence class is the first cpu\n    // in the class.\n    if (i == 0 || physicalId != std::get<0>(cpus[i - 1]) ||\n        coreId != std::get<1>(cpus[i - 1])) {\n      l1Equiv = cpu;\n    }\n    if (i == 0 || physicalId != std::get<0>(cpus[i - 1])) {\n      l3Equiv = cpu;\n    }\n    equivClassesByCpu[cpu] = {l1Equiv, l1Equiv, l3Equiv};\n  }\n\n  return CacheLocality{std::move(equivClassesByCpu)};\n}\n\nCacheLocality CacheLocality::readFromProcCpuinfo() {\n  std::vector<std::string> lines;\n  {\n    std::ifstream xi(\"/proc/cpuinfo\");\n    if (xi.fail()) {\n      throw std::runtime_error(\"unable to open /proc/cpuinfo\");\n    }\n    char buf[8192];\n    while (xi.good() && lines.size() < 20000) {\n      xi.getline(buf, sizeof(buf));\n      std::string str(buf);\n      if (procCpuinfoLineRelevant(str)) {\n        lines.emplace_back(std::move(str));\n      }\n    }\n  }\n  return readFromProcCpuinfoLines(lines);\n}\n\nCacheLocality CacheLocality::uniform(size_t numCpus) {\n  // One cache shared by all cpus.\n  std::vector<std::vector<size_t>> equivClassesByCpu(numCpus, {0});\n  return CacheLocality{std::move(equivClassesByCpu)};\n}\n\n////////////// Getcpu\n\nGetcpu::Func Getcpu::resolveVdsoFunc() {\n#if !defined(FOLLY_HAVE_LINUX_VDSO) || defined(FOLLY_SANITIZE_MEMORY)\n  return nullptr;\n#else\n  void* h = dlopen(\"linux-vdso.so.1\", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);\n  if (h == nullptr) {\n    return nullptr;\n  }\n\n  auto func = Getcpu::Func(dlsym(h, \"__vdso_getcpu\"));\n  if (func == nullptr) {\n    // technically a null result could either be a failure or a successful\n    // lookup of a symbol with the null value, but the second can't actually\n    // happen for this symbol.  No point holding the handle forever if\n    // we don't need the code\n    dlclose(h);\n  }\n\n  return func;\n#endif\n}\n\n/////////////// SequentialThreadId\nunsigned SequentialThreadId::get() {\n  static std::atomic<unsigned> global{0};\n  static thread_local unsigned local{0};\n  return FOLLY_LIKELY(local) ? local : (local = ++global);\n}\n\n/////////////// HashingThreadId\nunsigned HashingThreadId::get() {\n  return hash::twang_32from64(getCurrentThreadID());\n}\n\nnamespace detail {\n\nint AccessSpreaderBase::degenerateGetcpu(unsigned* cpu, unsigned* node, void*) {\n  if (cpu != nullptr) {\n    *cpu = 0;\n  }\n  if (node != nullptr) {\n    *node = 0;\n  }\n  return 0;\n}\n\nstruct AccessSpreaderStaticInit {\n  static AccessSpreaderStaticInit instance;\n  AccessSpreaderStaticInit() { (void)AccessSpreader<>::current(~size_t(0)); }\n};\nAccessSpreaderStaticInit AccessSpreaderStaticInit::instance;\n\nbool AccessSpreaderBase::initialize(\n    GlobalState& state,\n    Getcpu::Func (&pickGetcpuFunc)(),\n    const CacheLocality& (&system)()) {\n  (void)AccessSpreaderStaticInit::instance; // ODR-use it so it is not dropped\n  constexpr auto relaxed = std::memory_order_relaxed;\n  auto& cacheLocality = system();\n  auto n = cacheLocality.numCpus;\n  for (size_t width = 0; width <= kMaxCpus; ++width) {\n    auto& row = state.table[width];\n    auto numStripes = std::max(size_t{1}, width);\n    for (size_t cpu = 0; cpu < kMaxCpus && cpu < n; ++cpu) {\n      auto index = cacheLocality.localityIndexByCpu[cpu];\n      assert(index < n);\n      // as index goes from 0..n, post-transform value goes from\n      // 0..numStripes\n      make_atomic_ref(row[cpu]).store(\n          static_cast<CompactStripe>((index * numStripes) / n), relaxed);\n      assert(make_atomic_ref(row[cpu]).load(relaxed) < numStripes);\n    }\n    size_t filled = n;\n    while (filled < kMaxCpus) {\n      size_t len = std::min(filled, kMaxCpus - filled);\n      for (size_t i = 0; i < len; ++i) {\n        make_atomic_ref(row[filled + i])\n            .store(make_atomic_ref(row[i]).load(relaxed), relaxed);\n      }\n      filled += len;\n    }\n    for (size_t cpu = n; cpu < kMaxCpus; ++cpu) {\n      assert(\n          make_atomic_ref(row[cpu]).load(relaxed) ==\n          make_atomic_ref(row[cpu - n]).load(relaxed));\n    }\n  }\n  state.getcpu.exchange(pickGetcpuFunc(), std::memory_order_acq_rel);\n  return true;\n}\n\n} // namespace detail\n\n/* static */ LLCAccessSpreader& LLCAccessSpreader::get() {\n  static folly::Indestructible<LLCAccessSpreader> instance(PrivateTag{});\n  return *instance;\n}\n\nLLCAccessSpreader::LLCAccessSpreader(PrivateTag) {\n#ifdef __linux__\n  auto getcpu = Getcpu::resolveVdsoFunc();\n  getcpu_ = getcpu ? getcpu : &FallbackGetcpuType::getcpu;\n\n  // /proc/cpuinfo does not have accurate LLC info, we need to force a read\n  // from sysfs.\n  auto cl = CacheLocality::readFromSysfs();\n  stripeByCpu_.resize(cl.numCpus);\n\n  // Only have stripes for LLCs that are accessible from the current process,\n  // and number them sequentially as we discover them.\n  std::unordered_map<size_t, size_t> cacheIdx;\n  cpu_set_t cpuset;\n  PCHECK(sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0);\n  for (size_t cpu = 0; cpu < cl.numCpus; ++cpu) {\n    if (!CPU_ISSET(cpu, &cpuset)) {\n      continue;\n    }\n\n    auto llcEquiv = cl.equivClassesByCpu[cpu].back();\n    auto [it, _] = cacheIdx.try_emplace(llcEquiv, cacheIdx.size());\n    stripeByCpu_[cpu] = it->second;\n  }\n\n  numStripes_ = cacheIdx.size();\n#else\n  numStripes_ = 1;\n  (void)getcpu_;\n  (void)stripeByCpu_;\n#endif // __linux__\n}\n\nsize_t LLCAccessSpreader::current() const {\n#ifdef __linux__\n  struct ThreadCache {\n    size_t usesLeft = 0;\n    size_t value;\n  };\n\n  // We expect that a thread doesn't migrate LLC often, so reuse the value a\n  // few times before refreshing it.\n  // TODO(ott): Re-evaluate once we can use rseq to get the current CPU.\n  thread_local ThreadCache tc;\n  if (tc.usesLeft-- == 0) {\n    tc.usesLeft = 16; // A small number is enough to amortize.\n    unsigned cpu;\n    getcpu_(&cpu, nullptr, nullptr);\n    // If the set of active CPUs can change at runtime just return the 0\n    // stripe. This should not happen in normal operations.\n    tc.value = cpu < stripeByCpu_.size() ? stripeByCpu_[cpu] : 0;\n  }\n\n  return tc.value;\n#else\n  return 0;\n#endif // __linux__\n}\n\nsize_t LLCAccessSpreader::numStripes() const {\n  return numStripes_;\n}\n\nnamespace {\n\n/**\n * A simple freelist allocator.  Allocates things of size sz, from slabs of size\n * kAllocSize.  Takes a lock on each allocation/deallocation.\n */\nclass SimpleAllocator {\n public:\n  // To support array aggregate initialization without an implicit constructor.\n  struct Ctor {};\n\n  SimpleAllocator(Ctor, size_t sz) : sz_(sz) {\n    static_assert(\n        sizeof(void*) <= 64,\n        \"SimpleAllocator assumes sizeof(void*) fits in maximum size class\");\n    if (sz_ < sizeof(void*)) {\n      folly::throw_exception<std::invalid_argument>(fmt::format(\n          \"SimpleAllocator size {} is too small (minimum: {})\",\n          sz_,\n          sizeof(void*)));\n    }\n  }\n\n  ~SimpleAllocator() {\n    std::lock_guard g(m_);\n    for (auto& block : blocks_) {\n      folly::aligned_free(block);\n    }\n  }\n\n  SimpleAllocator(const SimpleAllocator&) = delete;\n  SimpleAllocator& operator=(const SimpleAllocator&) = delete;\n  SimpleAllocator(SimpleAllocator&&) = delete;\n  SimpleAllocator& operator=(SimpleAllocator&&) = delete;\n\n  void* allocate() {\n    std::lock_guard g(m_);\n    // Freelist allocation.\n    if (freelist_) {\n      auto mem = freelist_;\n      freelist_ = *static_cast<void**>(freelist_);\n      return mem;\n    }\n\n    if (mem_) {\n      // Bump-ptr allocation.\n      if (intptr_t(mem_) % kMallocAlign == 0) {\n        // Avoid allocating pointers that may look like malloc\n        // pointers.\n        mem_ += std::min(sz_, max_align_v);\n      }\n      if (mem_ + sz_ <= end_) {\n        auto mem = mem_;\n        mem_ += sz_;\n\n        assert(intptr_t(mem) % kMallocAlign != 0);\n        return mem;\n      }\n    }\n\n    return allocateHard();\n  }\n\n  static void deallocate(void* ptr) {\n    assert(intptr_t(ptr) % kMallocAlign != 0);\n    // Find the allocator instance.\n    auto addr =\n        reinterpret_cast<void*>(intptr_t(ptr) & ~intptr_t(kAllocSize - 1));\n    auto allocator = *static_cast<SimpleAllocator**>(addr);\n\n    std::lock_guard g(allocator->m_);\n    *static_cast<void**>(ptr) = allocator->freelist_;\n    if constexpr (kIsSanitizeAddress) {\n      // If running under ASAN, scrub the memory on deallocation, so we don't\n      // leave pointers that could hide leaks at shutdown, since the backing\n      // slabs may not be deallocated if the instance is a leaky singleton.\n      auto* base = static_cast<char*>(ptr);\n      std::fill(\n          base + sizeof(void*), base + allocator->sz_, static_cast<char>(0));\n    }\n    allocator->freelist_ = ptr;\n  }\n\n  constexpr static size_t kMallocAlign =\n      std::max(size_t(128), hardware_destructive_interference_size);\n  static_assert(\n      kMallocAlign % hardware_destructive_interference_size == 0,\n      \"Large allocations should be cacheline-aligned\");\n\n private:\n  constexpr static size_t kAllocSize = 4096;\n\n  void* allocateHard() {\n    // Allocate a new slab.\n    mem_ = static_cast<uint8_t*>(folly::aligned_malloc(kAllocSize, kAllocSize));\n    if (!mem_) {\n      throw_exception<std::bad_alloc>();\n    }\n    end_ = mem_ + kAllocSize;\n    blocks_.push_back(mem_);\n\n    // Install a pointer to ourselves as the allocator.\n    *reinterpret_cast<SimpleAllocator**>(mem_) = this;\n    static_assert(\n        max_align_v >= sizeof(SimpleAllocator*), \"alignment too small\");\n    mem_ += std::min(sz_, max_align_v);\n\n    // New allocation.\n    auto mem = mem_;\n    mem_ += sz_;\n    assert(intptr_t(mem) % kMallocAlign != 0);\n    return mem;\n  }\n\n  std::mutex m_;\n  uint8_t* mem_{nullptr};\n  uint8_t* end_{nullptr};\n  void* freelist_{nullptr};\n  size_t sz_;\n  std::vector<void*> blocks_;\n};\n\nclass Allocator {\n public:\n  void* allocate(size_t size) {\n    if (auto cl = sizeClass(size)) {\n      return allocators_[*cl].allocate();\n    }\n\n    // Fall back to malloc, returning a kMallocAlign-aligned allocation so it\n    // can be distinguished from SimpleAllocator allocations.\n    size = size + (SimpleAllocator::kMallocAlign - 1);\n    size &= ~size_t(SimpleAllocator::kMallocAlign - 1);\n    void* mem = aligned_malloc(size, SimpleAllocator::kMallocAlign);\n    if (!mem) {\n      throw_exception<std::bad_alloc>();\n    }\n    return mem;\n  }\n\n  static void deallocate(void* ptr) {\n    if (!ptr) {\n      return;\n    }\n\n    // See if it came from SimpleAllocator or malloc.\n    if (intptr_t(ptr) % SimpleAllocator::kMallocAlign != 0) {\n      SimpleAllocator::deallocate(ptr);\n    } else {\n      aligned_free(ptr);\n    }\n  }\n\n private:\n  std::optional<uint8_t> sizeClass(size_t size) {\n    if (size <= 8) {\n      return 0;\n    } else if (size <= 16) {\n      return 1;\n    } else if (size <= 32) {\n      return 2;\n    } else if (size <= 64) {\n      return 3;\n    } else {\n      return std::nullopt;\n    }\n  }\n\n  std::array<SimpleAllocator, 4> allocators_{\n      {{SimpleAllocator::Ctor{}, 8},\n       {SimpleAllocator::Ctor{}, 16},\n       {SimpleAllocator::Ctor{}, 32},\n       {SimpleAllocator::Ctor{}, 64}}};\n};\n\n} // namespace\n\nvoid* coreMalloc(size_t size, size_t numStripes, size_t stripe) {\n  static folly::Indestructible<Allocator>\n      allocators[AccessSpreader<>::maxLocalityIndexValue()];\n  auto index = AccessSpreader<>::localityIndexForStripe(numStripes, stripe);\n  return allocators[index]->allocate(size);\n}\n\nvoid coreFree(void* ptr) {\n  Allocator::deallocate(ptr);\n}\n\nnamespace {\nthread_local CoreAllocatorGuard* gCoreAllocatorGuard = nullptr;\n}\n\nCoreAllocatorGuard::CoreAllocatorGuard(size_t numStripes, size_t stripe)\n    : numStripes_(numStripes), stripe_(stripe) {\n  CHECK(gCoreAllocatorGuard == nullptr)\n      << \"CoreAllocator::Guard cannot be used recursively\";\n  gCoreAllocatorGuard = this;\n}\n\nCoreAllocatorGuard::~CoreAllocatorGuard() {\n  gCoreAllocatorGuard = nullptr;\n}\n\nnamespace detail {\n\nvoid* coreMallocFromGuard(size_t size) {\n  CHECK(gCoreAllocatorGuard != nullptr)\n      << \"CoreAllocator::allocator called without an active Guard\";\n  return coreMalloc(\n      size, gCoreAllocatorGuard->numStripes_, gCoreAllocatorGuard->stripe_);\n}\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/CacheLocality.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <atomic>\n#include <cassert>\n#include <functional>\n#include <limits>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <vector>\n\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/lang/Align.h>\n#include <folly/synchronization/AtomicRef.h>\n\nnamespace folly {\n\n// This file contains several classes that might be useful if you are\n// trying to dynamically optimize cache locality: CacheLocality reads\n// cache sharing information from procfs or sysfs to determine how CPUs\n// should be grouped to minimize contention, Getcpu provides fast access\n// to the current CPU via __vdso_getcpu, and AccessSpreader uses these two\n// to optimally spread accesses among a predetermined number of stripes.\n//\n// AccessSpreader<>::current(n) microbenchmarks at 22 nanos, which is\n// substantially less than the cost of a cache miss.  This means that we\n// can effectively use it to reduce cache line ping-pong on striped data\n// structures such as IndexedMemPool or statistics counters.\n//\n// Because CacheLocality looks at all of the cache levels, it can be\n// used for different levels of optimization.  AccessSpreader(2) does\n// per-chip spreading on a dual socket system.  AccessSpreader(numCpus)\n// does perfect per-cpu spreading.  AccessSpreader(numCpus / 2) does\n// perfect L1 spreading in a system with hyperthreading enabled.\n\nstruct CacheLocality {\n  /// 1 more than the maximum value that can be returned from sched_getcpu\n  /// or getcpu.  This is the number of hardware thread contexts provided\n  /// by the processors.\n  size_t numCpus;\n\n  /// NOTE: The information below may be a heuristic approximation based on the\n  /// available mechanisms to parse cpu topology.\n\n  /// Holds the number of caches present at each cache level (0 is\n  /// the closest to the cpu).  This is the number of AccessSpreader\n  /// stripes needed to avoid cross-cache communication at the specified\n  /// layer.  numCachesByLevel.front() is the number of L1 caches and\n  /// numCachesByLevel.back() is the number of last-level caches.\n  std::vector<size_t> numCachesByLevel;\n\n  /// A map from cpu (from sched_getcpu or getcpu) to an index in the\n  /// range 0..numCpus-1, where neighboring locality indices are more\n  /// likely to share caches then indices far away.  All of the members\n  /// of a particular cache level be contiguous in their locality index.\n  /// For example, if numCpus is 32 and numCachesByLevel.back() is 2,\n  /// then cpus with a locality index < 16 will share one last-level\n  /// cache and cpus with a locality index >= 16 will share the other.\n  std::vector<size_t> localityIndexByCpu;\n\n  /// For each cpu, a list of cache identifiers following the same layout as\n  /// numCachesByLevel. The identifier itself is an arbitrary number: it only\n  /// signifies that cpus with the same identifier share a cache at that level.\n  std::vector<std::vector<size_t>> equivClassesByCpu;\n\n  /// Returns the best CacheLocality information available for the current\n  /// system, cached for fast access.  This will be loaded from procfs or\n  /// sysfs if possible, otherwise it will be correct in the number of CPUs\n  /// but not in their sharing structure.\n  ///\n  /// If you are into yo dawgs, this is a shared cache of the local\n  /// locality of the shared caches.\n  ///\n  /// The template parameter here is used to allow injection of a\n  /// repeatable CacheLocality structure during testing.  Rather than\n  /// inject the type of the CacheLocality provider into every data type\n  /// that transitively uses it, all components select between the default\n  /// procfs/sysfs implementation and a deterministic implementation by keying\n  /// off the type of the underlying atomic.  See DeterministicScheduler.\n  template <template <typename> class Atom = std::atomic>\n  static const CacheLocality& system();\n\n  /// Returns the best CacheLocality information available for the current\n  /// system.  This will be loaded from procfs or sysfs if possible, otherwise\n  /// it will be correct in the number of CPUs but not in their sharing\n  /// structure.\n  static CacheLocality readSystemLocalityInfo();\n\n  /// Reads CacheLocality information from a tree structured like\n  /// the sysfs filesystem.  The provided function will be evaluated\n  /// for each sysfs file that needs to be queried.  The function\n  /// should return a string containing the first line of the file\n  /// (not including the newline), or an empty string if the file does\n  /// not exist.  The function will be called with paths of the form\n  /// /sys/devices/system/cpu/cpu*/cache/index*/{type,shared_cpu_list} .\n  /// Throws an exception if no caches can be parsed at all.\n  static CacheLocality readFromSysfsTree(std::string_view root = \"/\");\n\n  /// Reads CacheLocality information from the real sysfs filesystem.\n  /// Throws an exception if no cache information can be loaded.\n  static CacheLocality readFromSysfs();\n\n  /// readFromProcCpuinfo(), except input is taken from memory rather\n  /// than the file system.\n  static CacheLocality readFromProcCpuinfoLines(\n      std::vector<std::string> const& lines);\n\n  /// Returns an estimate of the CacheLocality information by reading\n  /// /proc/cpuinfo.  This isn't as accurate as readFromSysfs(), but\n  /// is a lot faster because the info isn't scattered across\n  /// hundreds of files.  Throws an exception if no cache information\n  /// can be loaded.\n  static CacheLocality readFromProcCpuinfo();\n\n  /// Returns a usable (but probably not reflective of reality)\n  /// CacheLocality structure with the specified number of cpus and a\n  /// single cache level that associates one cpu per cache.\n  static CacheLocality uniform(size_t numCpus);\n\n private:\n  explicit CacheLocality(std::vector<std::vector<size_t>> equivClasses);\n};\n\n/// Knows how to derive a function pointer to the VDSO implementation of\n/// getcpu(2), if available\nstruct Getcpu {\n  /// Function pointer to a function with the same signature as getcpu(2).\n  using Func = int (*)(unsigned* cpu, unsigned* node, void* unused);\n\n  /// Returns a pointer to the VDSO implementation of getcpu(2), if\n  /// available, or nullptr otherwise.  This function may be quite\n  /// expensive, be sure to cache the result.\n  static Func resolveVdsoFunc();\n};\n\nstruct SequentialThreadId {\n  static unsigned get();\n};\n\nstruct HashingThreadId {\n  static unsigned get();\n};\n\n/// A class that lazily binds a unique (for each implementation of Atom)\n/// identifier to a thread.  This is a fallback mechanism for the access\n/// spreader if __vdso_getcpu can't be loaded\ntemplate <typename ThreadId>\nstruct FallbackGetcpu {\n  /// Fills the thread id into the cpu and node out params (if they\n  /// are non-null).  This method is intended to act like getcpu when a\n  /// fast-enough form of getcpu isn't available or isn't desired\n  static int getcpu(unsigned* cpu, unsigned* node, void* /* unused */) {\n    auto id = ThreadId::get();\n    if (cpu) {\n      *cpu = id;\n    }\n    if (node) {\n      *node = id;\n    }\n    return 0;\n  }\n};\n\nusing FallbackGetcpuType = FallbackGetcpu<\n    conditional_t<kIsMobile, HashingThreadId, SequentialThreadId>>;\n\nnamespace detail {\n\nclass AccessSpreaderBase {\n public:\n  /// If there are more cpus than this nothing will crash, but there\n  /// might be unnecessary sharing\n  enum {\n    // Android phones with 8 cores exist today; 16 for future-proofing.\n    kMaxCpus = kIsMobile ? 16 : 256,\n  };\n\n protected:\n  using CompactStripe = uint8_t;\n\n  static_assert(\n      (kMaxCpus & (kMaxCpus - 1)) == 0,\n      \"kMaxCpus should be a power of two so modulo is fast\");\n  static_assert(\n      kMaxCpus - 1 <= std::numeric_limits<CompactStripe>::max(),\n      \"stripeByCpu element type isn't wide enough\");\n\n  using CompactStripeTable = CompactStripe[kMaxCpus + 1][kMaxCpus];\n\n  struct GlobalState {\n    /// For each level of splitting up to kMaxCpus, maps the cpu (mod\n    /// kMaxCpus) to the stripe.  Rather than performing any inequalities\n    /// or modulo on the actual number of cpus, we just fill in the entire\n    /// array.\n    /// Keep as the first field to avoid extra + in the fastest path.\n    mutable CompactStripeTable table;\n\n    /// Points to the getcpu-like function we are using to obtain the\n    /// current cpu. It should not be assumed that the returned cpu value\n    /// is in range.\n    std::atomic<Getcpu::Func> getcpu; // nullptr -> not initialized\n  };\n\n  /// Always claims to be on CPU zero, node zero\n  static int degenerateGetcpu(unsigned* cpu, unsigned* node, void*);\n\n  static bool initialize(\n      GlobalState& out, Getcpu::Func (&)(), const CacheLocality& (&)());\n};\n\n} // namespace detail\n\n/// AccessSpreader arranges access to a striped data structure in such a\n/// way that concurrently executing threads are likely to be accessing\n/// different stripes.  It does NOT guarantee uncontended access.\n/// Your underlying algorithm must be thread-safe without spreading, this\n/// is merely an optimization.  AccessSpreader::current(n) is typically\n/// much faster than a cache miss (12 nanos on my dev box, tested fast\n/// in both 2.6 and 3.2 kernels).\n///\n/// If available (and not using the deterministic testing implementation)\n/// AccessSpreader uses the getcpu system call via VDSO and the\n/// precise locality information retrieved from sysfs by CacheLocality.\n/// This provides optimal anti-sharing at a fraction of the cost of a\n/// cache miss.\n///\n/// When there are not as many stripes as processors, we try to optimally\n/// place the cache sharing boundaries.  This means that if you have 2\n/// stripes and run on a dual-socket system, your 2 stripes will each get\n/// all of the cores from a single socket.  If you have 16 stripes on a\n/// 16 core system plus hyperthreading (32 cpus), each core will get its\n/// own stripe and there will be no cache sharing at all.\n///\n/// AccessSpreader has a fallback mechanism for when __vdso_getcpu can't be\n/// loaded, or for use during deterministic testing.  Using sched_getcpu\n/// or the getcpu syscall would negate the performance advantages of\n/// access spreading, so we use a thread-local value and a shared atomic\n/// counter to spread access out.  On systems lacking both a fast getcpu()\n/// and TLS, we hash the thread id to spread accesses.\n///\n/// AccessSpreader is templated on the template type that is used\n/// to implement atomics, as a way to instantiate the underlying\n/// heuristics differently for production use and deterministic unit\n/// testing.  See DeterministicScheduler for more.  If you aren't using\n/// DeterministicScheduler, you can just use the default template parameter\n/// all of the time.\ntemplate <template <typename> class Atom = std::atomic>\nstruct AccessSpreader : private detail::AccessSpreaderBase {\n private:\n  struct GlobalState : detail::AccessSpreaderBase::GlobalState {};\n  static_assert(\n      std::is_trivially_destructible<GlobalState>::value,\n      \"unsuitable for global state\");\n\n public:\n  FOLLY_EXPORT static GlobalState& state() {\n    static FOLLY_CONSTINIT GlobalState state{};\n    if (FOLLY_UNLIKELY(!state.getcpu.load(std::memory_order_acquire))) {\n      initialize(state);\n    }\n    return state;\n  }\n\n  /// Returns the stripe associated with the current CPU.  The returned\n  /// value will be < numStripes.\n  static size_t current(size_t numStripes, const GlobalState& s = state()) {\n    // s.table[0] will actually work okay (all zeros), but\n    // something's wrong with the caller\n    assert(numStripes > 0);\n\n    unsigned cpu;\n    s.getcpu.load(std::memory_order_relaxed)(&cpu, nullptr, nullptr);\n    cpu = cpu % kMaxCpus;\n    auto& ref = s.table[std::min(size_t(kMaxCpus), numStripes)][cpu];\n    return make_atomic_ref(ref).load(std::memory_order_relaxed);\n  }\n\n  /// Returns the stripe associated with the current CPU.  The returned\n  /// value will be < numStripes.\n  /// This function caches the current cpu in a thread-local variable for a\n  /// certain small number of calls, which can make the result imprecise, but\n  /// it is more efficient (amortized 2 ns on my dev box, compared to 12 ns for\n  /// current()).\n  static size_t cachedCurrent(\n      size_t numStripes, const GlobalState& s = state()) {\n    if (kIsMobile) {\n      return current(numStripes, s);\n    }\n    unsigned cpu = cpuCache().cpu(s);\n    auto& ref = s.table[std::min(size_t(kMaxCpus), numStripes)][cpu];\n    return make_atomic_ref(ref).load(std::memory_order_relaxed);\n  }\n\n  /// Forces the next cachedCurrent() call in this thread to re-probe the\n  /// current CPU.\n  static void invalidateCachedCurrent() {\n    if (kIsMobile) {\n      return;\n    }\n    cpuCache().invalidate();\n  }\n\n  /// Returns a canonical index in [0, maxLocalityIndexValue()) for each\n  /// stripe. This can be used to share global data structures accessed with\n  /// different stripings. For optimal spread, it is best for numStripes to be a\n  /// divisor of the number of L1 caches.\n  static size_t localityIndexForStripe(size_t numStripes, size_t stripe) {\n    assert(stripe < numStripes);\n    return stripe *\n        std::min(size_t(kMaxCpus), CacheLocality::system<Atom>().numCpus) /\n        numStripes;\n  }\n\n  /// Returns the maximum stripe value that can be returned under any\n  /// dynamic configuration, based on the current compile-time platform\n  static constexpr size_t maxStripeValue() { return kMaxCpus; }\n\n  /// Returns the maximum locality index value that can be returned under any\n  /// dynamic configuration, based on the current compile-time platform\n  static constexpr size_t maxLocalityIndexValue() { return kMaxCpus; }\n\n private:\n  /// Caches the current CPU and refreshes the cache every so often.\n  class CpuCache {\n   public:\n    unsigned cpu(GlobalState const& s) {\n      if (FOLLY_UNLIKELY(cachedCpuUses_-- == 0)) {\n        unsigned cpu;\n        s.getcpu.load(std::memory_order_relaxed)(&cpu, nullptr, nullptr);\n        cachedCpu_ = cpu % kMaxCpus;\n        cachedCpuUses_ = kMaxCachedCpuUses - 1;\n      }\n      return cachedCpu_;\n    }\n\n    void invalidate() { cachedCpuUses_ = 0; }\n\n   private:\n    static constexpr unsigned kMaxCachedCpuUses = 32;\n\n    unsigned cachedCpu_ = 0;\n    unsigned cachedCpuUses_ = 0;\n  };\n\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static CpuCache& cpuCache() {\n    static thread_local CpuCache cpuCache;\n    return cpuCache;\n  }\n\n  /// Returns the best getcpu implementation for Atom\n  static Getcpu::Func pickGetcpuFunc() {\n    auto best = Getcpu::resolveVdsoFunc();\n    return best ? best : &FallbackGetcpuType::getcpu;\n  }\n\n  // The function to call for fast lookup of getcpu is a singleton, as\n  // is the precomputed table of locality information.  AccessSpreader\n  // is used in very tight loops, however (we're trying to race an L1\n  // cache miss!), so the normal singleton mechanisms are noticeably\n  // expensive.  Even a not-taken branch guarding access to getcpuFunc\n  // slows AccessSpreader::current from 12 nanos to 14.  As a result, we\n  // populate the static members with simple (but valid) values that can\n  // be filled in by the linker, and then follow up with a normal static\n  // initializer call that puts in the proper version.  This means that\n  // when there are initialization order issues we will just observe a\n  // zero stripe.  Once a sanitizer gets smart enough to detect this as\n  // a race or undefined behavior, we can annotate it.\n\n  static bool initialize(GlobalState& state) {\n    return detail::AccessSpreaderBase::initialize(\n        state, pickGetcpuFunc, CacheLocality::system<Atom>);\n  }\n};\n\n/// Similar to AccessSpreader, but it has exactly one stripe for each last-level\n/// cache that is accessible by the current process.\n///\n/// Only supported on Linux; on other systems, numStripes() always returns 1\n/// and current() always returns 0.\nclass LLCAccessSpreader {\n  struct PrivateTag {};\n\n public:\n  static LLCAccessSpreader& get();\n\n  explicit LLCAccessSpreader(PrivateTag);\n\n  size_t current() const;\n  size_t numStripes() const;\n\n private:\n  Getcpu::Func getcpu_;\n  size_t numStripes_;\n  std::vector<size_t> stripeByCpu_;\n};\n\n/**\n * An allocator that can be used with AccessSpreader to allocate core-local\n * memory.\n *\n * There is actually nothing special about the memory itself (it is not bound to\n * NUMA nodes or anything), but the allocator guarantees that memory allocatd\n * from the same stripe will only come from cache lines also allocated to the\n * same stripe, for the given numStripes.  This means multiple things using\n * AccessSpreader can allocate memory in smaller-than cacheline increments, and\n * be assured that it won't cause more false sharing than it otherwise would.\n *\n * Allocations smaller than sizeof(void*) (typically 8 bytes) are automatically\n * rounded up to ensure correct internal bookkeeping.\n *\n * Note that allocation and deallocation takes a per-size-class lock.\n *\n * Memory allocated with coreMalloc() must be freed with coreFree().\n */\nvoid* coreMalloc(size_t size, size_t numStripes, size_t stripe);\nvoid coreFree(void* ptr);\n\nnamespace detail {\nvoid* coreMallocFromGuard(size_t size);\n}\n\n/**\n * An C++ allocator adapter for coreMalloc/Free. The allocator is stateless, to\n * avoid increasing the footprint of the container that uses it, so the stripe\n * needs to be passed out of band: allocate() can only be called while there is\n * an active CoreAllocatorGuard. deallocate() can instead be called at any\n * point.\n *\n * This makes CoreAllocator unsuitable for containers that can grow, and it is\n * meant for container where all allocations happen at construction time.\n */\ntemplate <typename T>\nclass CoreAllocator : private std::allocator<T> {\n public:\n  using value_type = T;\n\n  CoreAllocator() = default;\n\n  template <class U>\n  /* implicit */ CoreAllocator(const CoreAllocator<U>&) {}\n\n  T* allocate(std::size_t n) {\n    return reinterpret_cast<T*>(detail::coreMallocFromGuard(n * sizeof(T)));\n  }\n\n  void deallocate(T* p, std::size_t) { coreFree(p); }\n\n  friend bool operator==(const CoreAllocator&, const CoreAllocator&) noexcept {\n    return true;\n  }\n  friend bool operator!=(const CoreAllocator&, const CoreAllocator&) noexcept {\n    return false;\n  }\n\n  template <typename U>\n  struct rebind {\n    using other = CoreAllocator<U>;\n  };\n};\n\nclass [[nodiscard]] CoreAllocatorGuard {\n public:\n  CoreAllocatorGuard(size_t numStripes, size_t stripe);\n  ~CoreAllocatorGuard();\n\n  CoreAllocatorGuard(const CoreAllocatorGuard&) = delete;\n  CoreAllocatorGuard& operator=(const CoreAllocatorGuard&) = delete;\n  CoreAllocatorGuard(CoreAllocatorGuard&&) = delete;\n  CoreAllocatorGuard& operator=(CoreAllocatorGuard&&) = delete;\n\n private:\n  friend void* detail::coreMallocFromGuard(size_t size);\n\n  size_t numStripes_;\n  size_t stripe_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/ConcurrentHashMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <mutex>\n\n#include <folly/Optional.h>\n#include <folly/concurrency/detail/ConcurrentHashMap-detail.h>\n#include <folly/synchronization/Hazptr.h>\n\nnamespace folly {\n\n/**\n * Implementations of high-performance Concurrent Hashmaps that\n * support erase and update.\n *\n * Readers are always wait-free.\n * Writers are sharded, but take a lock that only locks part of the map.\n *\n * Multithreaded performance beats anything except the lock-free\n *      atomic maps (AtomicUnorderedMap, AtomicHashMap), BUT only\n *      if you can perfectly size the atomic maps, and you don't\n *      need erase().  If you don't know the size in advance or\n *      your workload needs erase(), this is the better choice.\n *\n * The interface is as close to std::unordered_map as possible, but there\n * are a handful of changes:\n *\n * * Iterators hold hazard pointers to the returned elements.  Elements can only\n *   be accessed while Iterators are still valid!\n *\n * * Therefore operator[] and at() return copies, since they do not\n *   return an iterator.  The returned value is const, to remind you\n *   that changes do not affect the value in the map.\n *\n * * erase() calls the hash function, and may fail if the hash\n *   function throws an exception.\n *\n * * clear() initializes new segments, and is not noexcept.\n *\n * * The interface adds assign_if_equal, since find() doesn't take a lock.\n *\n * * Only const version of find() is supported, and const iterators.\n *   Mutation must use functions provided, like assign().\n *\n * * iteration iterates over all the buckets in the table, unlike\n *   std::unordered_map which iterates over a linked list of elements.\n *   If the table is sparse, this may be more expensive.\n *\n * * Allocator must be stateless.\n *\n * 1: ConcurrentHashMap, based on Java's ConcurrentHashMap.\n *    Very similar to std::unordered_map in performance.\n *\n * 2: ConcurrentHashMapSIMD, based on F14ValueMap.  If the map is\n *    larger than the cache size, it has superior performance due to\n *    vectorized key lookup.\n *\n *\n *\n * USAGE FAQs\n *\n * Q: Is simultaneous iteration and erase() threadsafe?\n *       Example:\n *\n *       ConcurrentHashMap<int, int> map;\n *\n *       Thread 1: auto it = map.begin();\n *                   while (it != map.end()) {\n *                      // Do something with it\n *                      it++;\n *                   }\n *\n *       Thread 2:    map.insert(2, 2);  map.erase(2);\n *\n * A: Yes, this is safe.  However, the iterating thread is not\n * guaranteed to see (or not see) concurrent insertions and erasures.\n * Inserts may cause a rehash, but the old table is still valid as\n * long as any iterator pointing to it exists.\n *\n * Q: How do I update an existing object atomically?\n *\n * A: assign_if_equal is the recommended way - readers will see the\n * old value until the new value is completely constructed and\n * inserted.\n *\n * Q: Why do map.erase() and clear() not actually destroy elements?\n *\n * A: Hazard Pointers are used to improve the performance of\n * concurrent access.  They can be thought of as a simple Garbage\n * Collector.  To reduce the GC overhead, a GC pass is only run after\n * reaching a certain memory bound.  erase() will remove the element\n * from being accessed via the map, but actual destruction may happen\n * later, after iterators that may point to it have been deleted.\n *\n * The only guarantee is that a GC pass will be run on map destruction\n * - no elements will remain after map destruction.\n *\n * Q: Are pointers to values safe to access *without* holding an\n * iterator?\n *\n * A: The SIMD version guarantees that references to elements are\n * stable across rehashes, the non-SIMD version does *not*.  Note that\n * unless you hold an iterator, you need to ensure there are no\n * concurrent deletes/updates to that key if you are accessing it via\n * reference.\n */\n\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    typename HashFn = std::hash<KeyType>,\n    typename KeyEqual = std::equal_to<KeyType>,\n    typename Allocator = std::allocator<uint8_t>,\n    uint8_t ShardBits = 8,\n    template <typename> class Atom = std::atomic,\n    class Mutex = std::mutex,\n    template <\n        typename,\n        typename,\n        uint8_t,\n        typename,\n        typename,\n        typename,\n        template <typename> class,\n        class> class Impl = detail::concurrenthashmap::bucket::BucketTable>\nclass ConcurrentHashMap {\n  using SegmentT = detail::ConcurrentHashMapSegment<\n      KeyType,\n      ValueType,\n      ShardBits,\n      HashFn,\n      KeyEqual,\n      Allocator,\n      Atom,\n      Mutex,\n      Impl>;\n  using SegmentTAllocator = typename std::allocator_traits<\n      Allocator>::template rebind_alloc<SegmentT>;\n  template <typename K, typename T>\n  using EnableHeterogeneousFind = std::enable_if_t<\n      detail::EligibleForHeterogeneousFind<KeyType, HashFn, KeyEqual, K>::value,\n      T>;\n\n  float load_factor_ = SegmentT::kDefaultLoadFactor;\n\n  static constexpr uint64_t NumShards = (1 << ShardBits);\n\n public:\n  class ConstIterator;\n\n  using key_type = KeyType;\n  using mapped_type = ValueType;\n  using value_type = std::pair<const KeyType, ValueType>;\n  using size_type = std::size_t;\n  using hasher = HashFn;\n  using key_equal = KeyEqual;\n  using const_iterator = ConstIterator;\n\n private:\n  template <typename K, typename T>\n  using EnableHeterogeneousInsert = std::enable_if_t<\n      ::folly::detail::\n          EligibleForHeterogeneousInsert<KeyType, HashFn, KeyEqual, K>::value,\n      T>;\n\n  template <typename K>\n  using IsIter = std::is_same<ConstIterator, remove_cvref_t<K>>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousErase = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          KeyType,\n          HashFn,\n          KeyEqual,\n          std::conditional_t<IsIter<K>::value, KeyType, K>>::value &&\n          !IsIter<K>::value,\n      T>;\n\n public:\n  /*\n   * Construct a ConcurrentHashMap with 1 << ShardBits shards, size\n   * and max_size given.  Both size and max_size will be rounded up to\n   * the next power of two, if they are not already a power of two, so\n   * that we can index in to Shards efficiently.\n   *\n   * Insertion functions will throw bad_alloc if max_size is exceeded.\n   */\n  explicit ConcurrentHashMap(size_t size = 8, size_t max_size = 0) {\n    size_ = folly::nextPowTwo(size);\n    if (max_size != 0) {\n      max_size_ = folly::nextPowTwo(max_size);\n    }\n    CHECK(max_size_ == 0 || max_size_ >= size_);\n    for (uint64_t i = 0; i < NumShards; i++) {\n      segments_[i].store(nullptr, std::memory_order_relaxed);\n    }\n  }\n\n  ConcurrentHashMap(ConcurrentHashMap&& o) noexcept\n      : size_(o.size_), max_size_(o.max_size_) {\n    for (uint64_t i = 0; i < NumShards; i++) {\n      segments_[i].store(\n          o.segments_[i].load(std::memory_order_relaxed),\n          std::memory_order_relaxed);\n      o.segments_[i].store(nullptr, std::memory_order_relaxed);\n    }\n    cohort_.store(o.cohort(), std::memory_order_relaxed);\n    o.cohort_.store(nullptr, std::memory_order_relaxed);\n    beginSeg_.store(\n        o.beginSeg_.load(std::memory_order_relaxed), std::memory_order_relaxed);\n    o.beginSeg_.store(NumShards, std::memory_order_relaxed);\n    endSeg_.store(\n        o.endSeg_.load(std::memory_order_relaxed), std::memory_order_relaxed);\n    o.endSeg_.store(0, std::memory_order_relaxed);\n  }\n\n  ConcurrentHashMap& operator=(ConcurrentHashMap&& o) {\n    for (uint64_t i = 0; i < NumShards; i++) {\n      auto seg = segments_[i].load(std::memory_order_relaxed);\n      if (seg) {\n        seg->~SegmentT();\n        SegmentTAllocator().deallocate(seg, 1);\n      }\n      segments_[i].store(\n          o.segments_[i].load(std::memory_order_relaxed),\n          std::memory_order_relaxed);\n      o.segments_[i].store(nullptr, std::memory_order_relaxed);\n    }\n    size_ = o.size_;\n    max_size_ = o.max_size_;\n    cohort_shutdown_cleanup();\n    cohort_.store(o.cohort(), std::memory_order_relaxed);\n    o.cohort_.store(nullptr, std::memory_order_relaxed);\n    beginSeg_.store(\n        o.beginSeg_.load(std::memory_order_relaxed), std::memory_order_relaxed);\n    o.beginSeg_.store(NumShards, std::memory_order_relaxed);\n    endSeg_.store(\n        o.endSeg_.load(std::memory_order_relaxed), std::memory_order_relaxed);\n    o.endSeg_.store(0, std::memory_order_relaxed);\n    return *this;\n  }\n\n  ~ConcurrentHashMap() {\n    uint64_t begin = beginSeg_.load(std::memory_order_acquire);\n    uint64_t end = endSeg_.load(std::memory_order_acquire);\n    for (uint64_t i = begin; i < end; ++i) {\n      auto seg = segments_[i].load(std::memory_order_relaxed);\n      if (seg) {\n        seg->~SegmentT();\n        SegmentTAllocator().deallocate(seg, 1);\n      }\n    }\n    cohort_shutdown_cleanup();\n  }\n\n  bool empty() const noexcept {\n    uint64_t begin = beginSeg_.load(std::memory_order_acquire);\n    uint64_t end = endSeg_.load(std::memory_order_acquire);\n    // Note: beginSeg_ and endSeg_ are only conservative hints of the\n    // range of non-empty segments. This function cannot conclude that\n    // a map is nonempty merely because beginSeg_ < endSeg_.\n    for (uint64_t i = begin; i < end; ++i) {\n      auto seg = segments_[i].load(std::memory_order_acquire);\n      if (seg) {\n        if (!seg->empty()) {\n          return false;\n        }\n      }\n    }\n    return true;\n  }\n\n  ConstIterator find(const KeyType& k) const { return findImpl(k); }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  ConstIterator find(const K& k) const {\n    return findImpl(k);\n  }\n\n  /*\n   * Intentionally marked as deleted to guard against common misuse.\n   *\n   * Checking for key existence via contains() is unsafe in concurrent\n   * contexts. The key may be erased between contains() and subsequent\n   * operations. Use find() and its iterator to ensure safe access and\n   * prevent race conditions.\n   */\n  bool contains(const KeyType& k) const = delete;\n\n  ConstIterator cend() const noexcept { return ConstIterator(NumShards); }\n\n  ConstIterator cbegin() const noexcept { return ConstIterator(this); }\n\n  ConstIterator end() const noexcept { return cend(); }\n\n  ConstIterator begin() const noexcept { return cbegin(); }\n\n  std::pair<ConstIterator, bool> insert(\n      std::pair<key_type, mapped_type>&& foo) {\n    return insertImpl(std::move(foo));\n  }\n\n  template <typename Key, EnableHeterogeneousInsert<Key, int> = 0>\n  std::pair<ConstIterator, bool> insert(std::pair<Key, mapped_type>&& foo) {\n    return insertImpl(std::move(foo));\n  }\n\n  template <typename Key, typename Value>\n  std::pair<ConstIterator, bool> insert(Key&& k, Value&& v) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    std::pair<ConstIterator, bool> res(\n        std::piecewise_construct,\n        std::forward_as_tuple(this, segment),\n        std::forward_as_tuple(false));\n    res.second = ensureSegment(segment)->insert(\n        res.first.it_, h, std::forward<Key>(k), std::forward<Value>(v));\n    return res;\n  }\n\n  template <typename Key, typename... Args>\n  std::pair<ConstIterator, bool> try_emplace(Key&& k, Args&&... args) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    std::pair<ConstIterator, bool> res(\n        std::piecewise_construct,\n        std::forward_as_tuple(this, segment),\n        std::forward_as_tuple(false));\n    res.second = ensureSegment(segment)->try_emplace(\n        res.first.it_, h, std::forward<Key>(k), std::forward<Args>(args)...);\n    return res;\n  }\n\n  template <typename... Args>\n  std::pair<ConstIterator, bool> emplace(Args&&... args) {\n    using Node = typename SegmentT::Node;\n    detail::concurrenthashmap::AllocNodeGuard<Node, Allocator> g(\n        Allocator(), ensureCohort(), std::forward<Args>(args)...);\n    auto h = HashFn{}(g.node->getItem().first);\n    auto segment = pickSegment(h);\n    std::pair<ConstIterator, bool> res(\n        std::piecewise_construct,\n        std::forward_as_tuple(this, segment),\n        std::forward_as_tuple(false));\n    res.second = ensureSegment(segment)->emplace(\n        res.first.it_, h, g.node->getItem().first, g.node);\n    if (res.second) {\n      g.dismiss();\n    }\n    return res;\n  }\n\n  /*\n   * The bool component will always be true if the map has been updated via\n   * either insertion or assignment. Note that this is different from the\n   * std::map::insert_or_assign interface.\n   */\n  template <typename Key, typename Value>\n  std::pair<ConstIterator, bool> insert_or_assign(Key&& k, Value&& v) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    std::pair<ConstIterator, bool> res(\n        std::piecewise_construct,\n        std::forward_as_tuple(this, segment),\n        std::forward_as_tuple(false));\n    res.second = ensureSegment(segment)->insert_or_assign(\n        res.first.it_, h, std::forward<Key>(k), std::forward<Value>(v));\n    return res;\n  }\n\n  /*\n   * Insert desired if the key doesn't exist, or assign to desired if the\n   * predicate returns true for the current value. The bool component will\n   * always be true if the map has been updated via either insertion or\n   * assignment. Note that this is different from the std::map::insert_or_assign\n   * interface.\n   */\n  template <typename Key, typename Value, typename Predicate>\n  std::pair<ConstIterator, bool> insert_or_assign_if(\n      Key&& k, Value&& desired, Predicate&& predicate) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    std::pair<ConstIterator, bool> res(\n        std::piecewise_construct,\n        std::forward_as_tuple(this, segment),\n        std::forward_as_tuple(false));\n    res.second = ensureSegment(segment)->insert_or_assign_if(\n        res.first.it_,\n        h,\n        std::forward<Key>(k),\n        std::forward<Value>(desired),\n        std::forward<Predicate>(predicate));\n    return res;\n  }\n\n  template <typename Key, typename Value>\n  folly::Optional<ConstIterator> assign(Key&& k, Value&& v) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    ConstIterator res(this, segment);\n    auto seg = segments_[segment].load(std::memory_order_acquire);\n    if (!seg) {\n      return none;\n    } else {\n      auto r =\n          seg->assign(res.it_, h, std::forward<Key>(k), std::forward<Value>(v));\n      if (!r) {\n        return none;\n      }\n    }\n    return std::move(res);\n  }\n\n  // Assign to desired if and only if the predicate returns true\n  // for the current value.\n  template <typename Key, typename Value, typename Predicate>\n  folly::Optional<ConstIterator> assign_if(\n      Key&& k, Value&& desired, Predicate&& predicate) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    ConstIterator res(this, segment);\n    auto seg = segments_[segment].load(std::memory_order_acquire);\n    if (!seg) {\n      return none;\n    } else {\n      auto r = seg->assign_if(\n          res.it_,\n          h,\n          std::forward<Key>(k),\n          std::forward<Value>(desired),\n          std::forward<Predicate>(predicate));\n      if (!r) {\n        return none;\n      }\n    }\n    return std::move(res);\n  }\n\n  // Assign to desired if and only if current value is equal to expected\n  template <typename Key, typename Value>\n  folly::Optional<ConstIterator> assign_if_equal(\n      Key&& k, const ValueType& expected, Value&& desired) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    ConstIterator res(this, segment);\n    auto seg = segments_[segment].load(std::memory_order_acquire);\n    if (!seg) {\n      return none;\n    } else {\n      auto r = seg->assign_if_equal(\n          res.it_,\n          h,\n          std::forward<Key>(k),\n          expected,\n          std::forward<Value>(desired));\n      if (!r) {\n        return none;\n      }\n    }\n    return std::move(res);\n  }\n\n  // Copying wrappers around insert and find.\n  // Only available for copyable types.\n  const ValueType operator[](const KeyType& key) {\n    auto item = insert(key, ValueType());\n    return item.first->second;\n  }\n\n  template <typename Key, EnableHeterogeneousInsert<Key, int> = 0>\n  const ValueType operator[](const Key& key) {\n    auto item = insert(key, ValueType());\n    return item.first->second;\n  }\n\n  const ValueType at(const KeyType& key) const { return atImpl(key); }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  const ValueType at(const K& key) const {\n    return atImpl(key);\n  }\n\n  // TODO update assign interface, operator[], at\n\n  size_type erase(const key_type& k) { return eraseImpl(k); }\n\n  template <typename K, EnableHeterogeneousErase<K, int> = 0>\n  size_type erase(const K& k) {\n    return eraseImpl(k);\n  }\n\n  // Calls the hash function, and therefore may throw.\n  // This function doesn't necessarily delete the item that pos points to.\n  // It simply tries erasing the item associated with the same key.\n  // While this behavior can be confusing, erase(iterator) is often found in\n  // std data structures so we follow a similar pattern here.\n  ConstIterator erase(ConstIterator& pos) {\n    auto h = HashFn{}(pos->first);\n    auto segment = pickSegment(h);\n    ConstIterator res(this, segment);\n    ensureSegment(segment)->erase(res.it_, pos.it_, h);\n    res.advanceIfAtSegmentEnd();\n    return res;\n  }\n\n  // Erase if and only if key k is equal to expected\n  size_type erase_if_equal(const key_type& k, const ValueType& expected) {\n    return erase_key_if(k, detail::concurrenthashmap::EqualTo1{expected});\n  }\n\n  template <typename K, EnableHeterogeneousErase<K, int> = 0>\n  size_type erase_if_equal(const K& k, const ValueType& expected) {\n    return erase_key_if(k, detail::concurrenthashmap::EqualTo1{expected});\n  }\n\n  // Erase if predicate evaluates to true on the existing value\n  template <typename Predicate>\n  size_type erase_key_if(const key_type& k, Predicate&& predicate) {\n    return eraseKeyIfImpl(k, std::forward<Predicate>(predicate));\n  }\n\n  template <\n      typename K,\n      typename Predicate,\n      EnableHeterogeneousErase<K, int> = 0>\n  size_type erase_key_if(const K& k, Predicate&& predicate) {\n    return eraseKeyIfImpl(k, std::forward<Predicate>(predicate));\n  }\n\n  // NOT noexcept, initializes new shard segments vs.\n  void clear() {\n    uint64_t begin = beginSeg_.load(std::memory_order_acquire);\n    uint64_t end = endSeg_.load(std::memory_order_acquire);\n    for (uint64_t i = begin; i < end; ++i) {\n      auto seg = segments_[i].load(std::memory_order_acquire);\n      if (seg) {\n        seg->clear();\n      }\n    }\n  }\n\n  void reserve(size_t count) {\n    count = count >> ShardBits;\n    if (!count) {\n      return;\n    }\n    uint64_t begin = beginSeg_.load(std::memory_order_acquire);\n    uint64_t end = endSeg_.load(std::memory_order_acquire);\n    for (uint64_t i = begin; i < end; ++i) {\n      auto seg = segments_[i].load(std::memory_order_acquire);\n      if (seg) {\n        seg->rehash(count);\n      }\n    }\n  }\n\n  // This is a rolling size, and is not exact at any moment in time.\n  size_t size() const noexcept {\n    size_t res = 0;\n    uint64_t begin = beginSeg_.load(std::memory_order_acquire);\n    uint64_t end = endSeg_.load(std::memory_order_acquire);\n    for (uint64_t i = begin; i < end; ++i) {\n      auto seg = segments_[i].load(std::memory_order_acquire);\n      if (seg) {\n        res += seg->size();\n      }\n    }\n    return res;\n  }\n\n  float max_load_factor() const { return load_factor_; }\n\n  void max_load_factor(float factor) {\n    uint64_t begin = beginSeg_.load(std::memory_order_acquire);\n    uint64_t end = endSeg_.load(std::memory_order_acquire);\n    for (uint64_t i = begin; i < end; ++i) {\n      auto seg = segments_[i].load(std::memory_order_acquire);\n      if (seg) {\n        seg->max_load_factor(factor);\n      }\n    }\n  }\n\n  class ConstIterator {\n   public:\n    friend class ConcurrentHashMap;\n\n    const value_type& operator*() const { return *it_; }\n\n    const value_type* operator->() const { return &*it_; }\n\n    ConstIterator& operator++() {\n      ++it_;\n      advanceIfAtSegmentEnd();\n      return *this;\n    }\n\n    bool operator==(const ConstIterator& o) const {\n      return it_ == o.it_ && segment_ == o.segment_;\n    }\n\n    bool operator!=(const ConstIterator& o) const { return !(*this == o); }\n\n    ConstIterator& operator=(const ConstIterator& o) = delete;\n\n    ConstIterator& operator=(ConstIterator&& o) noexcept {\n      if (this != &o) {\n        it_ = std::move(o.it_);\n        segment_ = std::exchange(o.segment_, uint64_t(NumShards));\n        parent_ = std::exchange(o.parent_, nullptr);\n      }\n      return *this;\n    }\n\n    ConstIterator(const ConstIterator& o) = delete;\n\n    ConstIterator(ConstIterator&& o) noexcept\n        : it_(std::move(o.it_)),\n          segment_(std::exchange(o.segment_, uint64_t(NumShards))),\n          parent_(std::exchange(o.parent_, nullptr)) {}\n\n    ConstIterator(const ConcurrentHashMap* parent, uint64_t segment)\n        : segment_(segment), parent_(parent) {}\n\n   private:\n    // cbegin iterator\n    explicit ConstIterator(const ConcurrentHashMap* parent)\n        : it_(nullptr),\n          segment_(parent->beginSeg_.load(std::memory_order_acquire)),\n          parent_(parent) {\n      advanceToSegmentBegin();\n    }\n\n    // cend iterator\n    explicit ConstIterator(uint64_t shards) : it_(nullptr), segment_(shards) {}\n\n    void advanceIfAtSegmentEnd() {\n      DCHECK_LT(segment_, parent_->NumShards);\n      SegmentT* seg =\n          parent_->segments_[segment_].load(std::memory_order_acquire);\n      DCHECK(seg);\n      if (it_ == seg->cend()) {\n        ++segment_;\n        advanceToSegmentBegin();\n      }\n    }\n\n    FOLLY_ALWAYS_INLINE void advanceToSegmentBegin() {\n      // Advance to the beginning of the next nonempty segment\n      // starting from segment_.\n      uint64_t end = parent_->endSeg_.load(std::memory_order_acquire);\n      while (segment_ < end) {\n        SegmentT* seg =\n            parent_->segments_[segment_].load(std::memory_order_acquire);\n        if (seg) {\n          it_ = seg->cbegin();\n          if (it_ != seg->cend()) {\n            return;\n          }\n        }\n        ++segment_;\n      }\n      // All segments are empty. Advance to end.\n      segment_ = parent_->NumShards;\n    }\n\n    typename SegmentT::Iterator it_;\n    uint64_t segment_;\n    const ConcurrentHashMap* parent_;\n  };\n\n private:\n  template <typename K>\n  ConstIterator findImpl(const K& k) const {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    ConstIterator res(this, segment);\n    auto seg = segments_[segment].load(std::memory_order_acquire);\n    if (!seg || !seg->find(res.it_, h, k)) {\n      res.segment_ = NumShards;\n    }\n    return res;\n  }\n\n  template <typename K>\n  const ValueType atImpl(const K& k) const {\n    auto item = find(k);\n    if (item == cend()) {\n      throw_exception<std::out_of_range>(\"at(): key not in map\");\n    }\n    return item->second;\n  }\n\n  template <typename Key>\n  std::pair<ConstIterator, bool> insertImpl(std::pair<Key, mapped_type>&& foo) {\n    auto h = HashFn{}(foo.first);\n    auto segment = pickSegment(h);\n    std::pair<ConstIterator, bool> res(\n        std::piecewise_construct,\n        std::forward_as_tuple(this, segment),\n        std::forward_as_tuple(false));\n    res.second =\n        ensureSegment(segment)->insert(res.first.it_, h, std::move(foo));\n    return res;\n  }\n\n  template <typename K>\n  size_type eraseImpl(const K& k) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    auto seg = segments_[segment].load(std::memory_order_acquire);\n    if (!seg) {\n      return 0;\n    } else {\n      return seg->erase(h, k);\n    }\n  }\n\n  template <typename K, typename Predicate>\n  size_type eraseKeyIfImpl(const K& k, Predicate&& predicate) {\n    auto h = HashFn{}(k);\n    auto segment = pickSegment(h);\n    auto seg = segments_[segment].load(std::memory_order_acquire);\n    if (!seg) {\n      return 0;\n    }\n    return seg->erase_key_if(h, k, std::forward<Predicate>(predicate));\n  }\n\n  uint64_t pickSegment(size_t h) const {\n    // Use the lowest bits for our shard bits.\n    //\n    // This works well even if the hash function is biased towards the\n    // low bits: The sharding will happen in the segments_ instead of\n    // in the segment buckets, so we'll still get write sharding as\n    // well.\n    //\n    // Low-bit bias happens often for std::hash using small numbers,\n    // since the integer hash function is the identity function.\n    return h & (NumShards - 1);\n  }\n\n  SegmentT* ensureSegment(uint64_t i) const {\n    SegmentT* seg = segments_[i].load(std::memory_order_acquire);\n    if (!seg) {\n      auto b = ensureCohort();\n      SegmentT* newseg = SegmentTAllocator().allocate(1);\n      newseg = new (newseg)\n          SegmentT(size_ >> ShardBits, load_factor_, max_size_ >> ShardBits, b);\n      if (!segments_[i].compare_exchange_strong(seg, newseg)) {\n        // seg is updated with new value, delete ours.\n        newseg->~SegmentT();\n        SegmentTAllocator().deallocate(newseg, 1);\n      } else {\n        seg = newseg;\n        updateBeginAndEndSegments(i);\n      }\n    }\n    return seg;\n  }\n\n  void updateBeginAndEndSegments(uint64_t i) const {\n    uint64_t val = beginSeg_.load(std::memory_order_acquire);\n    while (i < val && !casSeg(beginSeg_, val, i)) {\n    }\n    val = endSeg_.load(std::memory_order_acquire);\n    while (i + 1 > val && !casSeg(endSeg_, val, i + 1)) {\n    }\n  }\n\n  bool casSeg(Atom<uint64_t>& seg, uint64_t& expval, uint64_t newval) const {\n    return seg.compare_exchange_weak(\n        expval, newval, std::memory_order_acq_rel, std::memory_order_acquire);\n  }\n\n  hazptr_obj_cohort<Atom>* cohort() const noexcept {\n    return cohort_.load(std::memory_order_acquire);\n  }\n\n  hazptr_obj_cohort<Atom>* ensureCohort() const {\n    auto b = cohort();\n    if (!b) {\n      auto storage = Allocator().allocate(sizeof(hazptr_obj_cohort<Atom>));\n      auto newcohort = new (storage) hazptr_obj_cohort<Atom>();\n      if (cohort_.compare_exchange_strong(b, newcohort)) {\n        b = newcohort;\n      } else {\n        newcohort->~hazptr_obj_cohort<Atom>();\n        Allocator().deallocate(storage, sizeof(hazptr_obj_cohort<Atom>));\n      }\n    }\n    return b;\n  }\n\n  void cohort_shutdown_cleanup() {\n    auto b = cohort();\n    if (b) {\n      b->~hazptr_obj_cohort<Atom>();\n      Allocator().deallocate((uint8_t*)b, sizeof(hazptr_obj_cohort<Atom>));\n    }\n  }\n\n  mutable Atom<SegmentT*> segments_[NumShards];\n  size_t size_{0};\n  size_t max_size_{0};\n  mutable Atom<hazptr_obj_cohort<Atom>*> cohort_{nullptr};\n  mutable Atom<uint64_t> beginSeg_{NumShards};\n  mutable Atom<uint64_t> endSeg_{0};\n};\n\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    typename HashFn = std::hash<KeyType>,\n    typename KeyEqual = std::equal_to<KeyType>,\n    typename Allocator = std::allocator<uint8_t>,\n    uint8_t ShardBits = 8,\n    template <typename> class Atom = std::atomic,\n    class Mutex = std::mutex>\nusing ConcurrentHashMapSIMD = ConcurrentHashMap<\n    KeyType,\n    ValueType,\n    HashFn,\n    KeyEqual,\n    Allocator,\n    ShardBits,\n    Atom,\n    Mutex,\n#if (                                                        \\\n    FOLLY_SSE_PREREQ(4, 2) ||                                \\\n    (FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE)) && \\\n    FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n    detail::concurrenthashmap::simd::SIMDTable\n#else\n    // fallback to regular impl\n    detail::concurrenthashmap::bucket::BucketTable\n#endif\n    >;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/CoreCachedSharedPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <atomic>\n#include <memory>\n\n#include <folly/CppAttributes.h>\n#include <folly/Portability.h>\n#include <folly/Unit.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/synchronization/Hazptr.h>\n\nnamespace folly {\n\n// On mobile we do not expect high concurrency, and memory is more important, so\n// use more conservative caching.\nconstexpr size_t kCoreCachedSharedPtrDefaultMaxSlots = kIsMobile ? 4 : 64;\n\nnamespace core_cached_shared_ptr_detail {\n\ntemplate <size_t kMaxSlots>\nclass SlotsConfig {\n public:\n  FOLLY_EXPORT static void initialize() {\n    [[maybe_unused]] static const Unit _ = [] {\n      // We need at most as many slots as the number of L1 caches, so we can\n      // avoid wasting memory if more slots are requested.\n      const auto l1Caches = CacheLocality::system().numCachesByLevel.front();\n      num_ = std::min(std::max<size_t>(1, l1Caches), kMaxSlots);\n      return unit;\n    }();\n  }\n\n  static size_t num() { return num_.load(std::memory_order_relaxed); }\n\n private:\n  static std::atomic<size_t> num_;\n};\n\n// Initialize with a valid num so that get() always returns a valid stripe, even\n// if initialize() has not been called yet.\ntemplate <size_t kMaxSlots>\nstd::atomic<size_t> SlotsConfig<kMaxSlots>::num_{1};\n\ntemplate <size_t kMaxSlots, class T>\nvoid makeSlots(std::shared_ptr<T> p, folly::Range<std::shared_ptr<T>*> slots) {\n  // Allocate each holder and its control block in a different CoreAllocator\n  // stripe to prevent false sharing.\n  for (size_t i = 0; i < slots.size(); ++i) {\n    CoreAllocatorGuard guard(slots.size(), i);\n    auto holder = std::allocate_shared<std::shared_ptr<T>>(\n        CoreAllocator<std::shared_ptr<T>>{});\n    auto ptr = p.get();\n    if (i != slots.size() - 1) {\n      *holder = p;\n    } else {\n      *holder = std::move(p);\n    }\n    slots[i] = std::shared_ptr<T>(std::move(holder), ptr);\n  }\n}\n\n// Check whether a shared_ptr is equivalent to default-constructed. Because of\n// aliasing constructors, there can be both nullptr with a managed object, and\n// non-nullptr with no managed object, so we need to check both.\ntemplate <class T>\nbool isDefault(const std::shared_ptr<T>& p) {\n  return p == nullptr && p.use_count() == 0;\n}\n\n} // namespace core_cached_shared_ptr_detail\n\n/**\n * This class creates core-local caches for a given shared_ptr, to\n * mitigate contention when acquiring/releasing it.\n *\n * It has the same thread-safety guarantees as shared_ptr: it is safe\n * to concurrently call get(), but reset()s must be synchronized with\n * reads and other reset()s.\n */\ntemplate <class T, size_t kMaxSlots = kCoreCachedSharedPtrDefaultMaxSlots>\nclass CoreCachedSharedPtr {\n  using SlotsConfig = core_cached_shared_ptr_detail::SlotsConfig<kMaxSlots>;\n\n public:\n  CoreCachedSharedPtr() = default;\n  explicit CoreCachedSharedPtr(std::shared_ptr<T> p) { reset(std::move(p)); }\n\n  void reset(std::shared_ptr<T> p = nullptr) {\n    SlotsConfig::initialize();\n\n    folly::Range<std::shared_ptr<T>*> slots{slots_.data(), SlotsConfig::num()};\n    for (auto& slot : slots) {\n      slot = {};\n    }\n    if (!core_cached_shared_ptr_detail::isDefault(p)) {\n      core_cached_shared_ptr_detail::makeSlots<kMaxSlots>(std::move(p), slots);\n    }\n  }\n\n  std::shared_ptr<T> get() const {\n    return slots_[AccessSpreader<>::cachedCurrent(SlotsConfig::num())];\n  }\n\n private:\n  template <class, size_t>\n  friend class CoreCachedWeakPtr;\n\n  std::array<std::shared_ptr<T>, kMaxSlots> slots_;\n};\n\ntemplate <class T, size_t kMaxSlots = kCoreCachedSharedPtrDefaultMaxSlots>\nclass CoreCachedWeakPtr {\n  using SlotsConfig = core_cached_shared_ptr_detail::SlotsConfig<kMaxSlots>;\n\n public:\n  CoreCachedWeakPtr() = default;\n  explicit CoreCachedWeakPtr(const CoreCachedSharedPtr<T, kMaxSlots>& p) {\n    reset(p);\n  }\n\n  void reset() { *this = {}; }\n  void reset(const CoreCachedSharedPtr<T, kMaxSlots>& p) {\n    SlotsConfig::initialize();\n    for (size_t i = 0; i < SlotsConfig::num(); ++i) {\n      slots_[i] = p.slots_[i];\n    }\n  }\n\n  std::weak_ptr<T> get() const {\n    return slots_[AccessSpreader<>::cachedCurrent(SlotsConfig::num())];\n  }\n\n  // Faster than get().lock(), as it avoid one weak count cycle.\n  std::shared_ptr<T> lock() const {\n    return slots_[AccessSpreader<>::cachedCurrent(SlotsConfig::num())].lock();\n  }\n\n private:\n  std::array<std::weak_ptr<T>, kMaxSlots> slots_;\n};\n\n/**\n * This class creates core-local caches for a given shared_ptr, to\n * mitigate contention when acquiring/releasing it.\n *\n * All methods are threadsafe.  Hazard pointers are used to avoid\n * use-after-free for concurrent reset() and get() operations.\n *\n * Concurrent reset()s are sequenced with respect to each other: the\n * sharded shared_ptrs will always all be set to the same value.\n * get()s will never see a newer pointer on one core, and an older\n * pointer on another after a subsequent thread migration.\n */\ntemplate <class T, size_t kMaxSlots = kCoreCachedSharedPtrDefaultMaxSlots>\nclass AtomicCoreCachedSharedPtr {\n  using SlotsConfig = core_cached_shared_ptr_detail::SlotsConfig<kMaxSlots>;\n\n public:\n  AtomicCoreCachedSharedPtr() = default;\n  explicit AtomicCoreCachedSharedPtr(std::shared_ptr<T> p) {\n    reset(std::move(p));\n  }\n\n  AtomicCoreCachedSharedPtr(AtomicCoreCachedSharedPtr&& other) noexcept\n      : slots_(other.slots_.load(std::memory_order_relaxed)) {\n    other.slots_.store(nullptr, std::memory_order_relaxed);\n  }\n  AtomicCoreCachedSharedPtr& operator=(AtomicCoreCachedSharedPtr&& other) =\n      delete;\n\n  ~AtomicCoreCachedSharedPtr() {\n    // Delete of AtomicCoreCachedSharedPtr must be synchronized, no\n    // need for slots->retire().\n    delete slots_.load(std::memory_order_acquire);\n  }\n\n  void reset(std::shared_ptr<T> p = nullptr) {\n    SlotsConfig::initialize();\n    std::unique_ptr<Slots> newslots;\n    if (!core_cached_shared_ptr_detail::isDefault(p)) {\n      newslots = std::make_unique<Slots>();\n      core_cached_shared_ptr_detail::makeSlots<kMaxSlots>(\n          std::move(p), {newslots->slots.data(), SlotsConfig::num()});\n    }\n\n    if (auto oldslots = slots_.exchange(newslots.release())) {\n      oldslots->retire();\n    }\n  }\n\n  std::shared_ptr<T> get() const {\n    // Avoid the hazptr cost if empty.\n    auto slots = slots_.load(std::memory_order_relaxed);\n    if (slots == nullptr) {\n      return nullptr;\n    }\n\n    folly::hazptr_local<1> hazptr;\n    while (!hazptr[0].try_protect(slots, slots_)) {\n      // Lost the update race, retry.\n    }\n    if (slots == nullptr) { // Need to check again, try_protect reloads slots.\n      return nullptr;\n    }\n    return slots->slots[AccessSpreader<>::cachedCurrent(SlotsConfig::num())];\n  }\n\n private:\n  struct Slots : folly::hazptr_obj_base<Slots> {\n    std::array<std::shared_ptr<T>, kMaxSlots> slots;\n  };\n  std::atomic<Slots*> slots_{nullptr};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/DeadlockDetector.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/DeadlockDetector.h>\n\nnamespace folly {\n/* static */ DeadlockDetectorFactory* DeadlockDetectorFactory::instance() {\n  if (get_deadlock_detector_factory_instance) {\n    return get_deadlock_detector_factory_instance();\n  }\n\n  return nullptr;\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/DeadlockDetector.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n#include <folly/executors/QueueObserver.h>\n\nnamespace folly {\nclass DeadlockDetector {\n public:\n  virtual ~DeadlockDetector() {}\n};\n\nclass DeadlockDetectorFactory {\n public:\n  virtual ~DeadlockDetectorFactory() {}\n  virtual std::unique_ptr<DeadlockDetector> create(\n      Executor* executor, const std::string& name) = 0;\n  static DeadlockDetectorFactory* instance();\n};\n\nusing GetDeadlockDetectorFactoryInstance = DeadlockDetectorFactory*();\n#if FOLLY_HAVE_WEAK_SYMBOLS\nFOLLY_ATTR_WEAK GetDeadlockDetectorFactoryInstance\n    get_deadlock_detector_factory_instance;\n#else\nconstexpr GetDeadlockDetectorFactoryInstance*\n    get_deadlock_detector_factory_instance = nullptr;\n#endif\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/DynamicBoundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/concurrency/UnboundedQueue.h>\n\n#include <glog/logging.h>\n\n#include <atomic>\n#include <chrono>\n\nnamespace folly {\n\n/// DynamicBoundedQueue supports:\n/// - Dynamic memory usage that grows and shrink in proportion to the\n///   number of elements in the queue.\n/// - Adjustable capacity that helps throttle pathological cases of\n///   producer-consumer imbalance that may lead to excessive memory\n///   usage.\n/// - The adjustable capacity can also help prevent deadlock by\n///   allowing users to temporarily increase capacity substantially to\n///   guarantee accommodating producer requests that cannot wait.\n/// - SPSC, SPMC, MPSC, MPMC variants.\n/// - Blocking and spinning-only variants.\n/// - Inter-operable non-waiting, timed until, timed for, and waiting\n///   variants of producer and consumer operations.\n/// - Optional variable element weights.\n///\n/// Element Weights\n/// - Queue elements may have variable weights (calculated using a\n///   template parameter) that are by default 1.\n/// - Element weights count towards the queue's capacity.\n/// - Elements weights are not priorities and do not affect element\n///   order. Queues with variable element weights follow FIFO order,\n///   the same as default queues.\n///\n/// When to use DynamicBoundedQueue:\n/// - If a small maximum capacity may lead to deadlock or performance\n///   degradation under bursty patterns and a larger capacity is\n///   sufficient.\n/// - If the typical queue size is expected to be much lower than the\n///   maximum capacity\n/// - If an unbounded queue is susceptible to growing too much.\n/// - If support for variable element weights is needed.\n///\n/// When not to use DynamicBoundedQueue?\n/// - If dynamic memory allocation is unacceptable or if the maximum\n///   capacity needs to be small, then use fixed-size MPMCQueue or (if\n///   non-blocking SPSC) ProducerConsumerQueue.\n/// - If there is no risk of the queue growing too much, then use\n///   UnboundedQueue.\n///\n/// Setting capacity\n/// - The general rule is to set the capacity as high as acceptable.\n///   The queue performs best when it is not near full capacity.\n/// - The implementation may allow extra slack in capacity (~10%) for\n///   amortizing some costly steps. Therefore, precise capacity is not\n///   guaranteed and cannot be relied on for synchronization; i.e.,\n///   this queue cannot be used as a semaphore.\n///\n/// Performance expectations:\n/// - As long as the queue size is below capacity in the common case,\n///   performance is comparable to MPMCQueue and better in cases of\n///   higher producer demand.\n/// - Performance degrades gracefully at full capacity.\n/// - It is recommended to measure performance with different variants\n///   when applicable, e.g., DMPMC vs DMPSC. Depending on the use\n///   case, sometimes the variant with the higher sequential overhead\n///   may yield better results due to, for example, more favorable\n///   producer-consumer balance or favorable timing for avoiding\n///   costly blocking.\n/// - See DynamicBoundedQueueTest.cpp for some benchmark results.\n///\n/// Template parameters:\n/// - T: element type\n/// - SingleProducer: true if there can be only one producer at a\n///   time.\n/// - SingleConsumer: true if there can be only one consumer at a\n///   time.\n/// - MayBlock: true if producers or consumers may block.\n/// - LgSegmentSize (default 8): Log base 2 of number of elements per\n///   UnboundedQueue segment.\n/// - LgAlign (default 7): Log base 2 of alignment directive; can be\n///   used to balance scalability (avoidance of false sharing) with\n///   memory efficiency.\n/// - WeightFn (DefaultWeightFn<T>): A customizable weight computing type\n///   for computing the weights of elements. The default weight is 1.\n///\n/// Template Aliases:\n///   DSPSCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///   DMPSCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///   DSPMCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///   DMPMCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///\n/// Functions:\n///   Constructor\n///     Takes a capacity value as an argument.\n///\n///   Producer functions:\n///     void enqueue(const T&);\n///     void enqueue(T&&);\n///         Adds an element to the end of the queue. Waits until\n///         capacity is available if necessary.\n///     bool try_enqueue(const T&);\n///     bool try_enqueue(T&&);\n///         Tries to add an element to the end of the queue if\n///         capacity allows it. Returns true if successful. Otherwise\n///         Returns false.\n///     bool try_enqueue_until(const T&, time_point& deadline);\n///     bool try_enqueue_until(T&&, time_point& deadline);\n///         Tries to add an element to the end of the queue if\n///         capacity allows it until the specified deadline. Returns\n///         true if successful, otherwise false.\n///     bool try_enqueue_for(const T&, duration&);\n///     bool try_enqueue_for(T&&, duration&);\n///         Tries to add an element to the end of the queue if\n///         capacity allows until the expiration of the specified\n///         duration. Returns true if successful, otherwise false.\n///\n///   Consumer functions:\n///     void dequeue(T&);\n///         Extracts an element from the front of the queue. Waits\n///         until an element is available if necessary.\n///     bool try_dequeue(T&);\n///         Tries to extracts an element from the front of the queue\n///         if available. Returns true if successful, otherwise false.\n///     bool try_dequeue_until(T&, time_point& deadline);\n///         Tries to extracts an element from the front of the queue\n///         if available until the specified daedline. Returns true\n///         if successful. Otherwise Returns false.\n///     bool try_dequeue_for(T&, duration&);\n///         Tries to extracts an element from the front of the queue\n///         if available until the expiration of the specified\n///         duration.  Returns true if successful. Otherwise Returns\n///         false.\n///\n///   Secondary functions:\n///     void reset_capacity(size_t capacity);\n///        Changes the capacity of the queue. Does not affect the\n///        current contents of the queue. Guaranteed only to affect\n///        subsequent enqueue operations. May or may not affect\n///        concurrent operations. Capacity must be at least 1000.\n///     Weight weight();\n///        Returns an estimate of the total weight of the elements in\n///        the queue.\n///     size_t size();\n///         Returns an estimate of the total number of elements.\n///     bool empty();\n///         Returns true only if the queue was empty during the call.\n///     Note: weight(), size(), and empty() are guaranteed to be\n///     accurate only if there are no concurrent changes to the queue.\n///\n/// Usage example with default weight:\n/// @code\n///   /* DMPSC, doesn't block, 1024 int elements per segment */\n///   DMPSCQueue<int, false, 10> q(100000);\n///   ASSERT_TRUE(q.empty());\n///   ASSERT_EQ(q.size(), 0);\n///   q.enqueue(1));\n///   ASSERT_TRUE(q.try_enqueue(2));\n///   ASSERT_TRUE(q.try_enqueue_until(3, deadline));\n///   ASSERT_TRUE(q.try_enqueue(4, duration));\n///   // ... enqueue more elements until capacity is full\n///   // See above comments about imprecise capacity guarantees\n///   ASSERT_FALSE(q.try_enqueue(100001)); // can't enqueue but can't wait\n///   size_t sz = q.size();\n///   ASSERT_GE(sz, 100000);\n///   q.reset_capacity(1000000000); // set huge capacity\n///   ASSERT_TRUE(q.try_enqueue(100001)); // now enqueue succeeds\n///   q.reset_capacity(100000); // set capacity back to 100,000\n///   ASSERT_FALSE(q.try_enqueue(100002));\n///   ASSERT_EQ(q.size(), sz + 1);\n///   int v;\n///   q.dequeue(v);\n///   ASSERT_EQ(v, 1);\n///   ASSERT_TRUE(q.try_dequeue(v));\n///   ASSERT_EQ(v, 2);\n///   ASSERT_TRUE(q.try_dequeue_until(v, deadline));\n///   ASSERT_EQ(v, 3);\n///   ASSERT_TRUE(q.try_dequeue_for(v, duration));\n///   ASSERT_EQ(v, 4);\n///   ASSERT_EQ(q.size(), sz - 3);\n/// @endcode\n///\n/// Usage example with custom weights:\n/// @code\n///   struct CustomWeightFn {\n///     uint64_t operator()(int val) { return val / 100; }\n///   };\n///   DMPMCQueue<int, false, 10, CustomWeightFn> q(20);\n///   ASSERT_TRUE(q.empty());\n///   q.enqueue(100);\n///   ASSERT_TRUE(q.try_enqueue(200));\n///   ASSERT_TRUE(q.try_enqueue_until(500, now() + seconds(1)));\n///   ASSERT_EQ(q.size(), 3);\n///   ASSERT_EQ(q.weight(), 8);\n///   ASSERT_FALSE(q.try_enqueue_for(1700, microseconds(1)));\n///   q.reset_capacity(1000000); // set capacity to 1000000 instead of 20\n///   ASSERT_TRUE(q.try_enqueue_for(1700, microseconds(1)));\n///   q.reset_capacity(20); // set capacity to 20 again\n///   ASSERT_FALSE(q.try_enqueue(100));\n///   ASSERT_EQ(q.size(), 4);\n///   ASSERT_EQ(q.weight(), 25);\n///   int v;\n///   q.dequeue(v);\n///   ASSERT_EQ(v, 100);\n///   ASSERT_TRUE(q.try_dequeue(v));\n///   ASSERT_EQ(v, 200);\n///   ASSERT_TRUE(q.try_dequeue_until(v, now() + seconds(1)));\n///   ASSERT_EQ(v, 500);\n///   ASSERT_EQ(q.size(), 1);\n///   ASSERT_EQ(q.weight(), 17);\n/// @endcode\n///\n/// Design:\n/// - The implementation is on top of UnboundedQueue.\n/// - The main FIFO functionality is in UnboundedQueue.\n///   DynamicBoundedQueue manages keeping the total queue weight\n///   within the specified capacity.\n/// - For the sake of scalability, the data structures are designed to\n///   minimize interference between producers on one side and\n///   consumers on the other.\n/// - Producers add to a debit variable the weight of the added\n///   element and check capacity.\n/// - Consumers add to a credit variable the weight of the removed\n///   element.\n/// - Producers, for the sake of scalability, use fetch_add to add to\n///   the debit variable and subtract if it exceeded capacity,\n///   rather than using compare_exchange to avoid overshooting.\n/// - Consumers, infrequently, transfer credit to a transfer variable\n///   and unblock any blocked producers. The transfer variable can be\n///   used by producers to decrease their debit when needed.\n/// - Note that a low capacity will trigger frequent credit transfer\n///   by consumers that may degrade performance. Capacity should not\n///   be set too low.\n/// - Transfer of credit by consumers is triggered when the amount of\n///   credit reaches a threshold (1/10 of capacity).\n/// - The waiting of consumers is handled in UnboundedQueue.\n///   The waiting of producers is handled in this template.\n/// - For a producer operation, if the difference between debit and\n///   capacity (plus some slack to account for the transfer threshold)\n///   does not accommodate the weight of the new element, it first\n///   tries to transfer credit that may have already been made\n///   available by consumers. If this is insufficient and MayBlock is\n///   true, then the producer uses a futex to block until new credit\n///   is transferred by a consumer.\n///\n/// Memory Usage:\n/// - Aside from three cache lines for managing capacity, the memory\n///   for queue elements is managed using UnboundedQueue and grows and\n///   shrinks dynamically with the number of elements.\n/// - The template parameter LgAlign can be used to reduce memory usage\n///   at the cost of increased chance of false sharing.\n\ntemplate <typename T>\nstruct DefaultWeightFn {\n  template <typename Arg>\n  uint64_t operator()(Arg&&) const noexcept {\n    return 1;\n  }\n};\n\ntemplate <\n    typename T,\n    bool SingleProducer,\n    bool SingleConsumer,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = 7,\n    typename WeightFn = DefaultWeightFn<T>,\n    template <typename> class Atom = std::atomic>\nclass DynamicBoundedQueue {\n  using Weight = uint64_t;\n\n  enum WaitingState : uint32_t {\n    NOTWAITING = 0,\n    WAITING = 1,\n  };\n\n  static constexpr bool SPSC = SingleProducer && SingleConsumer;\n  static constexpr size_t Align = 1u << LgAlign;\n\n  static_assert(LgAlign < 16, \"LgAlign must be < 16\");\n\n  /// Data members\n\n  // Read mostly by producers\n  alignas(Align) Atom<Weight> debit_; // written frequently only by producers\n  Atom<Weight> capacity_; // written rarely by capacity resets\n\n  // Read mostly by consumers\n  alignas(Align) Atom<Weight> credit_; // written frequently only by consumers\n  Atom<Weight> threshold_; // written rarely only by capacity resets\n\n  // Normally written and read rarely by producers and consumers\n  // May be read frequently by producers when capacity is full\n  alignas(Align) Atom<Weight> transfer_;\n  detail::Futex<Atom> waiting_;\n\n  // Underlying unbounded queue\n  UnboundedQueue<\n      T,\n      SingleProducer,\n      SingleConsumer,\n      MayBlock,\n      LgSegmentSize,\n      LgAlign,\n      Atom>\n      q_;\n\n public:\n  using value_type = T;\n  using size_type = size_t;\n\n  /** constructor */\n  explicit DynamicBoundedQueue(Weight capacity)\n      : debit_(0),\n        capacity_(capacity + threshold(capacity)), // capacity slack\n        credit_(0),\n        threshold_(threshold(capacity)),\n        transfer_(0),\n        waiting_(0) {}\n\n  /** destructor */\n  ~DynamicBoundedQueue() {}\n\n  /// Enqueue functions\n\n  /** enqueue */\n  FOLLY_ALWAYS_INLINE void enqueue(const T& v) { enqueueImpl(v); }\n\n  FOLLY_ALWAYS_INLINE void enqueue(T&& v) { enqueueImpl(std::move(v)); }\n\n  /** try_enqueue */\n  FOLLY_ALWAYS_INLINE bool try_enqueue(const T& v) { return tryEnqueueImpl(v); }\n\n  FOLLY_ALWAYS_INLINE bool try_enqueue(T&& v) {\n    return tryEnqueueImpl(std::move(v));\n  }\n\n  /** try_enqueue_until */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE bool try_enqueue_until(\n      const T& v, const std::chrono::time_point<Clock, Duration>& deadline) {\n    return tryEnqueueUntilImpl(v, deadline);\n  }\n\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE bool try_enqueue_until(\n      T&& v, const std::chrono::time_point<Clock, Duration>& deadline) {\n    return tryEnqueueUntilImpl(std::move(v), deadline);\n  }\n\n  /** try_enqueue_for */\n  template <typename Rep, typename Period>\n  FOLLY_ALWAYS_INLINE bool try_enqueue_for(\n      const T& v, const std::chrono::duration<Rep, Period>& duration) {\n    return tryEnqueueForImpl(v, duration);\n  }\n\n  template <typename Rep, typename Period>\n  FOLLY_ALWAYS_INLINE bool try_enqueue_for(\n      T&& v, const std::chrono::duration<Rep, Period>& duration) {\n    return tryEnqueueForImpl(std::move(v), duration);\n  }\n\n  /// Dequeue functions\n\n  /** dequeue */\n  FOLLY_ALWAYS_INLINE void dequeue(T& elem) {\n    q_.dequeue(elem);\n    addCredit(WeightFn()(elem));\n  }\n\n  /** try_dequeue */\n  FOLLY_ALWAYS_INLINE bool try_dequeue(T& elem) {\n    if (q_.try_dequeue(elem)) {\n      addCredit(WeightFn()(elem));\n      return true;\n    }\n    return false;\n  }\n\n  FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue() {\n    auto elem = q_.try_dequeue();\n    if (elem.hasValue()) {\n      addCredit(WeightFn()(*elem));\n    }\n    return elem;\n  }\n\n  /** try_dequeue_until */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE bool try_dequeue_until(\n      T& elem, const std::chrono::time_point<Clock, Duration>& deadline) {\n    if (q_.try_dequeue_until(elem, deadline)) {\n      addCredit(WeightFn()(elem));\n      return true;\n    }\n    return false;\n  }\n\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) {\n    auto elem = q_.try_dequeue_until(deadline);\n    if (elem.hasValue()) {\n      addCredit(WeightFn()(*elem));\n    }\n    return elem;\n  }\n\n  /** try_dequeue_for */\n  template <typename Rep, typename Period>\n  FOLLY_ALWAYS_INLINE bool try_dequeue_for(\n      T& elem, const std::chrono::duration<Rep, Period>& duration) {\n    if (q_.try_dequeue_for(elem, duration)) {\n      addCredit(WeightFn()(elem));\n      return true;\n    }\n    return false;\n  }\n\n  template <typename Rep, typename Period>\n  FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue_for(\n      const std::chrono::duration<Rep, Period>& duration) {\n    auto elem = q_.try_dequeue_for(duration);\n    if (elem.hasValue()) {\n      addCredit(WeightFn()(*elem));\n    }\n    return elem;\n  }\n\n  /// Secondary functions\n\n  /** reset_capacity */\n  void reset_capacity(Weight capacity) noexcept {\n    Weight thresh = threshold(capacity);\n    capacity_.store(capacity + thresh, std::memory_order_release);\n    threshold_.store(thresh, std::memory_order_release);\n  }\n\n  /** weight */\n  Weight weight() const noexcept {\n    auto d = getDebit();\n    auto c = getCredit();\n    auto t = getTransfer();\n    return d > (c + t) ? d - (c + t) : 0;\n  }\n\n  /** size */\n  size_t size() const noexcept { return q_.size(); }\n\n  /** empty */\n  bool empty() const noexcept { return q_.empty(); }\n\n private:\n  /// Private functions ///\n\n  // Calculation of threshold to move credits in bulk from consumers\n  // to producers\n  constexpr Weight threshold(Weight capacity) const noexcept {\n    return (capacity + 9) / 10;\n  }\n\n  // Functions called frequently by producers\n\n  template <typename Arg>\n  FOLLY_ALWAYS_INLINE void enqueueImpl(Arg&& v) {\n    tryEnqueueUntilImpl(\n        std::forward<Arg>(v), std::chrono::steady_clock::time_point::max());\n  }\n\n  template <typename Arg>\n  FOLLY_ALWAYS_INLINE bool tryEnqueueImpl(Arg&& v) {\n    return tryEnqueueUntilImpl(\n        std::forward<Arg>(v), std::chrono::steady_clock::time_point::min());\n  }\n\n  template <typename Clock, typename Duration, typename Arg>\n  FOLLY_ALWAYS_INLINE bool tryEnqueueUntilImpl(\n      Arg&& v, const std::chrono::time_point<Clock, Duration>& deadline) {\n    Weight weight = WeightFn()(std::forward<Arg>(v));\n    if (FOLLY_LIKELY(tryAddDebit(weight))) {\n      q_.enqueue(std::forward<Arg>(v));\n      return true;\n    }\n    return tryEnqueueUntilSlow(std::forward<Arg>(v), deadline);\n  }\n\n  template <typename Rep, typename Period, typename Arg>\n  FOLLY_ALWAYS_INLINE bool tryEnqueueForImpl(\n      Arg&& v, const std::chrono::duration<Rep, Period>& duration) {\n    if (FOLLY_LIKELY(tryEnqueueImpl(std::forward<Arg>(v)))) {\n      return true;\n    }\n    auto deadline = std::chrono::steady_clock::now() + duration;\n    return tryEnqueueUntilSlow(std::forward<Arg>(v), deadline);\n  }\n\n  FOLLY_ALWAYS_INLINE bool tryAddDebit(Weight weight) noexcept {\n    Weight capacity = getCapacity();\n    Weight before = fetchAddDebit(weight);\n    if (FOLLY_LIKELY(before + weight <= capacity)) {\n      return true;\n    } else {\n      subDebit(weight);\n      return false;\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE Weight getCapacity() const noexcept {\n    return capacity_.load(std::memory_order_acquire);\n  }\n\n  FOLLY_ALWAYS_INLINE Weight fetchAddDebit(Weight weight) noexcept {\n    Weight before;\n    if (SingleProducer) {\n      before = getDebit();\n      debit_.store(before + weight, std::memory_order_relaxed);\n    } else {\n      before = debit_.fetch_add(weight, std::memory_order_acq_rel);\n    }\n    return before;\n  }\n\n  FOLLY_ALWAYS_INLINE Weight getDebit() const noexcept {\n    return debit_.load(std::memory_order_acquire);\n  }\n\n  // Functions called frequently by consumers\n\n  FOLLY_ALWAYS_INLINE void addCredit(Weight weight) noexcept {\n    Weight before = fetchAddCredit(weight);\n    Weight thresh = getThreshold();\n    if (before + weight >= thresh && before < thresh) {\n      transferCredit();\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE Weight fetchAddCredit(Weight weight) noexcept {\n    Weight before;\n    if (SingleConsumer) {\n      before = getCredit();\n      credit_.store(before + weight, std::memory_order_relaxed);\n    } else {\n      before = credit_.fetch_add(weight, std::memory_order_acq_rel);\n    }\n    return before;\n  }\n\n  FOLLY_ALWAYS_INLINE Weight getCredit() const noexcept {\n    return credit_.load(std::memory_order_acquire);\n  }\n\n  FOLLY_ALWAYS_INLINE Weight getThreshold() const noexcept {\n    return threshold_.load(std::memory_order_acquire);\n  }\n\n  /** Functions called infrequently by producers */\n\n  void subDebit(Weight weight) noexcept {\n    Weight before;\n    if (SingleProducer) {\n      before = getDebit();\n      debit_.store(before - weight, std::memory_order_relaxed);\n    } else {\n      before = debit_.fetch_sub(weight, std::memory_order_acq_rel);\n    }\n    DCHECK_GE(before, weight);\n  }\n\n  template <typename Clock, typename Duration, typename Arg>\n  bool tryEnqueueUntilSlow(\n      Arg&& v, const std::chrono::time_point<Clock, Duration>& deadline) {\n    Weight weight = WeightFn()(std::forward<Arg>(v));\n    if (canEnqueue(deadline, weight)) {\n      q_.enqueue(std::forward<Arg>(v));\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  template <typename Clock, typename Duration>\n  bool canEnqueue(\n      const std::chrono::time_point<Clock, Duration>& deadline,\n      Weight weight) noexcept {\n    Weight capacity = getCapacity();\n    while (true) {\n      tryReduceDebit();\n      Weight debit = getDebit();\n      if ((debit + weight <= capacity) && tryAddDebit(weight)) {\n        return true;\n      }\n      if (deadline < Clock::time_point::max() && Clock::now() >= deadline) {\n        return false;\n      }\n      if (MayBlock) {\n        if (canBlock(weight, capacity)) {\n          detail::futexWaitUntil(&waiting_, WAITING, deadline);\n        }\n      } else {\n        asm_volatile_pause();\n      }\n    }\n  }\n\n  bool canBlock(Weight weight, Weight capacity) noexcept {\n    waiting_.store(WAITING, std::memory_order_relaxed);\n    std::atomic_thread_fence(std::memory_order_seq_cst);\n    tryReduceDebit();\n    Weight debit = getDebit();\n    return debit + weight > capacity;\n  }\n\n  bool tryReduceDebit() noexcept {\n    Weight w = takeTransfer();\n    if (w > 0) {\n      subDebit(w);\n    }\n    return w > 0;\n  }\n\n  Weight takeTransfer() noexcept {\n    Weight w = getTransfer();\n    if (w > 0) {\n      w = transfer_.exchange(0, std::memory_order_acq_rel);\n    }\n    return w;\n  }\n\n  Weight getTransfer() const noexcept {\n    return transfer_.load(std::memory_order_acquire);\n  }\n\n  /** Functions called infrequently by consumers */\n\n  void transferCredit() noexcept {\n    Weight credit = takeCredit();\n    transfer_.fetch_add(credit, std::memory_order_acq_rel);\n    if (MayBlock) {\n      std::atomic_thread_fence(std::memory_order_seq_cst);\n      waiting_.store(NOTWAITING, std::memory_order_relaxed);\n      detail::futexWake(&waiting_);\n    }\n  }\n\n  Weight takeCredit() noexcept {\n    Weight credit;\n    if (SingleConsumer) {\n      credit = credit_.load(std::memory_order_relaxed);\n      credit_.store(0, std::memory_order_relaxed);\n    } else {\n      credit = credit_.exchange(0, std::memory_order_acq_rel);\n    }\n    return credit;\n  }\n\n}; // DynamicBoundedQueue\n\n/// Aliases\n\n/** DSPSCQueue */\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = 7,\n    typename WeightFn = DefaultWeightFn<T>,\n    template <typename> class Atom = std::atomic>\nusing DSPSCQueue = DynamicBoundedQueue<\n    T,\n    true,\n    true,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    WeightFn,\n    Atom>;\n\n/** DMPSCQueue */\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = 7,\n    typename WeightFn = DefaultWeightFn<T>,\n    template <typename> class Atom = std::atomic>\nusing DMPSCQueue = DynamicBoundedQueue<\n    T,\n    false,\n    true,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    WeightFn,\n    Atom>;\n\n/** DSPMCQueue */\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = 7,\n    typename WeightFn = DefaultWeightFn<T>,\n    template <typename> class Atom = std::atomic>\nusing DSPMCQueue = DynamicBoundedQueue<\n    T,\n    true,\n    false,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    WeightFn,\n    Atom>;\n\n/** DMPMCQueue */\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = 7,\n    typename WeightFn = DefaultWeightFn<T>,\n    template <typename> class Atom = std::atomic>\nusing DMPMCQueue = DynamicBoundedQueue<\n    T,\n    false,\n    false,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    WeightFn,\n    Atom>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/PriorityUnboundedQueueSet.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <vector>\n\n#include <folly/Memory.h>\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/lang/Align.h>\n\nnamespace folly {\n\n/// PriorityUnboundedQueueSet\n///\n/// A set of per-priority queues, and an interface for accessing them.\n///\n/// Functions:\n///   Consumer operations:\n///     bool try_dequeue(T&);\n///     Optional<T> try_dequeue();\n///       Tries to extract an element from the front of the least-priority\n///       backing queue which has an element, if any.\n///     T const* try_peek();\n///       Returns a pointer to the element at the front of the least-priority\n///       backing queue which has an element, if any. Only allowed when\n///       SingleConsumer is true.\n///     Note:\n///       Queues at lower priority are tried before queues at higher priority.\n///\n///   Secondary functions:\n///     queue& at_priority(size_t);\n///     queue const& at_priority(size_t) const;\n///       Returns a reference to the owned queue at the given priority.\n///     size_t size() const;\n///       Returns an estimate of the total size of the owned queues.\n///     bool empty() const;\n///       Returns true only if all of the owned queues were empty during the\n///       call.\n///     Note: size() and empty() are guaranteed to be accurate only if the\n///       owned queues are not changed concurrently.\ntemplate <\n    typename T,\n    bool SingleProducer,\n    bool SingleConsumer,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nclass PriorityUnboundedQueueSet {\n public:\n  using queue = UnboundedQueue<\n      T,\n      SingleProducer,\n      SingleConsumer,\n      MayBlock,\n      LgSegmentSize,\n      LgAlign,\n      Atom>;\n\n  explicit PriorityUnboundedQueueSet(size_t priorities) : queues_(priorities) {}\n\n  PriorityUnboundedQueueSet(PriorityUnboundedQueueSet const&) = delete;\n  PriorityUnboundedQueueSet(PriorityUnboundedQueueSet&&) = delete;\n  PriorityUnboundedQueueSet& operator=(PriorityUnboundedQueueSet const&) =\n      delete;\n  PriorityUnboundedQueueSet& operator=(PriorityUnboundedQueueSet&&) = delete;\n\n  queue& at_priority(size_t priority) { return queues_.at(priority); }\n\n  queue const& at_priority(size_t priority) const {\n    return queues_.at(priority);\n  }\n\n  bool try_dequeue(T& item) noexcept {\n    for (auto& q : queues_) {\n      if (q.try_dequeue(item)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  Optional<T> try_dequeue() noexcept {\n    for (auto& q : queues_) {\n      if (auto item = q.try_dequeue()) {\n        return item;\n      }\n    }\n    return none;\n  }\n\n  T const* try_peek() noexcept {\n    DCHECK(SingleConsumer);\n    for (auto& q : queues_) {\n      if (auto ptr = q.try_peek()) {\n        return ptr;\n      }\n    }\n    return nullptr;\n  }\n\n  size_t size() const noexcept {\n    size_t size = 0;\n    for (auto& q : queues_) {\n      size += q.size();\n    }\n    return size;\n  }\n\n  bool empty() const noexcept {\n    for (auto& q : queues_) {\n      if (!q.empty()) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  size_t priorities() const noexcept { return queues_.size(); }\n\n private:\n  //  queue_alloc custom allocator is necessary until C++17\n  //    http://open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3396.htm\n  //    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65122\n  //    https://bugs.llvm.org/show_bug.cgi?id=22634\n  using queue_alloc = AlignedSysAllocator<queue, FixedAlign<alignof(queue)>>;\n  std::vector<queue, queue_alloc> queues_;\n}; // PriorityUnboundedQueueSet\n\n/* Aliases */\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing PriorityUSPSCQueueSet = PriorityUnboundedQueueSet<\n    T,\n    true,\n    true,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    Atom>;\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing PriorityUMPSCQueueSet = PriorityUnboundedQueueSet<\n    T,\n    false,\n    true,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    Atom>;\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing PriorityUSPMCQueueSet = PriorityUnboundedQueueSet<\n    T,\n    true,\n    false,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    Atom>;\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing PriorityUMPMCQueueSet = PriorityUnboundedQueueSet<\n    T,\n    false,\n    false,\n    MayBlock,\n    LgSegmentSize,\n    LgAlign,\n    Atom>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/ProcessLocalUniqueId.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Likely.h>\n#include <folly/concurrency/ProcessLocalUniqueId.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\n#include <atomic>\n\nnamespace folly {\n\nuint64_t processLocalUniqueId() {\n  constinit static relaxed_atomic<uint64_t> nextEpoch{0};\n  // Id format is <epoch: 48 bits> <counter: 16 bits>.\n  // Ephemeral threads, if any, can waste a whole epoch, so we keep epochs\n  // relatively small, but large enough to amortize the atomic epoch increment.\n  constexpr int kCounterBits = 16;\n  constexpr uint64_t kCounterMask = (uint64_t(1) << kCounterBits) - 1;\n  thread_local uint64_t next{0};\n\n  // If first call in thread, or counter wrapped around, start new epoch.\n  if (FOLLY_UNLIKELY((next & kCounterMask) == 0)) {\n    next = nextEpoch++ << kCounterBits;\n    // Skip 0 as per contract.\n    if (FOLLY_UNLIKELY(next == 0)) {\n      ++next;\n    }\n  }\n\n  return next++;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/ProcessLocalUniqueId.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n\nnamespace folly {\n\n/**\n * Generates a 64-bit id that is unique within the process. The returned ids\n * should not be persisted or passed to other processes, and there are no\n * ordering guarantees.\n *\n * It is guaranteed that 0 is never returned, hence 0 can be used as a sentinel\n * value, similarly to nullptr.\n *\n * The function is thread-safe.\n *\n * The uniqueness guarantee can be broken if enough ids are generated, but even\n * in the most pessimistic scenario it would take a few hundred years.\n */\nuint64_t processLocalUniqueId();\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/SingletonRelaxedCounter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <atomic>\n#include <type_traits>\n#include <unordered_map>\n\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Synchronized.h>\n#include <folly/Utility.h>\n#include <folly/detail/StaticSingletonManager.h>\n#include <folly/detail/thread_local_globals.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/synchronization/AtomicRef.h>\n\nnamespace folly {\n\nnamespace detail {\n\n//  SingletonRelaxedCounterBase\n//\n//  Extracts tag-independent functionality from SingletonRelaxedCounter below\n//  to avoid the monomorphization the compiler would otherwise perform.\n//\n//  Tricks:\n//  * Use constexpr vtables as the polymorphization mechanism.\n//  * Use double-noinline outer-only-noexcept definitions to shrink code size in\n//    inline or monomorphized slow paths.\ntemplate <typename Int>\nclass SingletonRelaxedCounterBase {\n protected:\n  using Signed = std::make_signed_t<Int>;\n  using Counter = Signed; // should be atomic but clang generates worse code\n\n  struct CounterAndCache {\n    Counter counter; // valid during LocalLifetime object lifetime\n    Counter* cache; // points to counter when counter is valid\n  };\n  static_assert(std::is_trivial_v<CounterAndCache>);\n\n  struct CounterRefAndLocal {\n    Counter* counter; // refers either to local counter or to global counter\n    bool local; // if true, definitely local; if false, could be global\n  };\n\n  struct LocalLifetime;\n\n  //  Global\n  //\n  //  Tracks all of the per-thread/per-dso counters and lifetimes and maintains\n  //  a global fallback counter.\n  struct Global {\n    struct Tracking {\n      std::unordered_map<LocalLifetime*, CounterAndCache*> lifetimes;\n    };\n\n    Counter fallback; // used instead of local during thread destruction\n    folly::Synchronized<Tracking> tracking;\n  };\n\n  using GetGlobal = Global&();\n  using GetLocal = CounterAndCache&();\n  using GetLifetime = LocalLifetime&();\n\n  struct Arg {\n    //  To be constexpr, MSVC requires these to be pointers.\n    GetGlobal* global;\n    GetLocal* local;\n    GetLifetime* lifetime;\n  };\n\n  //  LocalLifetime\n  //\n  //  Manages local().cache, global().tracking, and moving outstanding counts\n  //  from local().counter to global().counter during thread destruction and dso\n  //  unload.\n  //\n  //  The index map is within Global to reduce per-thread overhead for threads\n  //  which do not participate in counter mutations, rather than being a member\n  //  field of LocalLifetime. This comes at the cost of the slow path always\n  //  acquiring a unique lock on the global mutex.\n  struct LocalLifetime {\n    FOLLY_NOINLINE void destroy(GetGlobal& get_global) noexcept {\n      destroy_(get_global);\n    }\n    FOLLY_NOINLINE void destroy_(GetGlobal& get_global) {\n      auto& global = get_global();\n      auto const tracking = global.tracking.wlock();\n      auto& entry = tracking->lifetimes[this];\n      FOLLY_SAFE_CHECK(entry);\n      auto entry_counter = atomic_ref(entry->counter);\n      auto const current = entry_counter.load(std::memory_order_relaxed);\n      atomic_ref(global.fallback).fetch_add(current, std::memory_order_relaxed);\n      entry_counter.store(Signed(0), std::memory_order_relaxed);\n      entry->cache = nullptr;\n      tracking->lifetimes.erase(this);\n    }\n\n    FOLLY_NOINLINE void track(Global& global, CounterAndCache& state) noexcept {\n      track_(global, state);\n    }\n    FOLLY_NOINLINE void track_(Global& global, CounterAndCache& state) {\n      state.cache = &state.counter;\n      auto const tracking = global.tracking.wlock();\n      auto& entry = tracking->lifetimes[this];\n      FOLLY_SAFE_CHECK(!entry || &state == entry);\n      entry = &state;\n    }\n  };\n\n  FOLLY_NOINLINE static Int aggregate(GetGlobal& get_global) noexcept {\n    return aggregate_(get_global);\n  }\n  FOLLY_NOINLINE static Int aggregate_(GetGlobal& get_global) {\n    auto& global = get_global();\n    auto count = atomic_ref(global.fallback).load(std::memory_order_relaxed);\n    auto const tracking = global.tracking.rlock();\n    for (auto const& [_, entry] : tracking->lifetimes) {\n      FOLLY_SAFE_CHECK(entry);\n      count += atomic_ref(entry->counter).load(std::memory_order_relaxed);\n    }\n    return std::is_unsigned<Int>::value\n        ? to_unsigned(std::max(Signed(0), count))\n        : count;\n  }\n\n  FOLLY_ERASE static void mutate(Signed v, CounterRefAndLocal cl) {\n    auto c = atomic_ref(*cl.counter);\n    if (cl.local) {\n      //  splitting load/store on the local counter is faster than fetch-and-add\n      c.store(c.load(std::memory_order_relaxed) + v, std::memory_order_relaxed);\n    } else {\n      //  but is not allowed on the global counter because mutations may be lost\n      c.fetch_add(v, std::memory_order_relaxed);\n    }\n  }\n\n  FOLLY_NOINLINE static void mutate_slow(Signed v, Arg const& arg) noexcept {\n    mutate(v, counter(arg));\n  }\n\n  FOLLY_NOINLINE static Counter& counter_slow(Arg const& arg) noexcept {\n    auto& global = arg.global();\n    if (thread_is_dying()) {\n      return global.fallback;\n    }\n    auto& state = arg.local();\n    arg.lifetime().track(global, state); // idempotent\n    auto const cache = state.cache;\n    return FOLLY_LIKELY(!!cache) ? *cache : global.fallback;\n  }\n\n  FOLLY_ERASE static CounterRefAndLocal counter(Arg const& arg) {\n    auto& state = arg.local();\n    auto const cache = state.cache; // a copy! null before/after LocalLifetime\n    auto const counter = FOLLY_LIKELY(!!cache) ? cache : &counter_slow(arg);\n    //  cache is a stale nullptr after the first call to counter_slow; this is\n    //  intentional for the side-effect of shrinking the inline fast path\n    return CounterRefAndLocal{counter, !!cache};\n  }\n};\n\n} // namespace detail\n\n//  SingletonRelaxedCounter\n//\n//  A singleton-per-tag relaxed counter. Optimized for increment/decrement\n//  runtime performance under contention and inlined fast path code size.\n//\n//  The cost of computing the value of the counter is linear in the number of\n//  threads which perform increments/decrements, and computing the value of the\n//  counter is exclusive with thread exit and dlclose. The result of this\n//  computation is not a point-in-time snapshot of increments and decrements\n//  summed, but is an approximation which may exclude any subset of increments\n//  and decrements that do not happen before the start of the computation.\n//\n//  Templated over the integral types. When templated over an unsigned integral\n//  type, it is assumed that decrements do not exceed increments, and if within\n//  computation of the value of the counter more decrements are observed to\n//  exceed increments then the excess decrements are ignored. This avoids the\n//  scenario of incrementing and decrementing once each in different threads,\n//  and concurrently observing a computed value of the counter of 2^64 - 1.\n//\n//  Templated over the tag types. Each unique pair of integral type and tag type\n//  is a different counter.\n//\n//  Implementation:\n//  Uses a thread-local counter when possible to avoid contention, and a global\n//  counter as a fallback. The total count at any given time is computed by\n//  summing over the global counter plus all of the thread-local counters; since\n//  the total sum is not a snapshot of the value at any given point in time, it\n//  is a relaxed sum; when the system quiesces (i.e., when no concurrent\n//  increments or decrements are happening and no threads are going through\n//  thread exit phase), the sum is exact.\n//\n//  Most of the implementation is in SingletonRelaxedCounterBase to avoid excess\n//  monomorphization.\ntemplate <typename Int, typename Tag>\nclass SingletonRelaxedCounter\n    : private detail::SingletonRelaxedCounterBase<Int> {\n public:\n  static void add(Int value) { mutate(+to_signed(value)); }\n  static void sub(Int value) { mutate(-to_signed(value)); }\n  static Int count() { return aggregate(global); }\n\n private:\n  using Base = detail::SingletonRelaxedCounterBase<Int>;\n  using Base::aggregate;\n  using Base::mutate;\n  using Base::mutate_slow;\n  using typename Base::Arg;\n  using typename Base::CounterAndCache;\n  using typename Base::GetGlobal;\n  using typename Base::Global;\n  using typename Base::LocalLifetime;\n  using typename Base::Signed;\n\n  struct MonoLocalLifetime : Base::LocalLifetime {\n    ~MonoLocalLifetime() noexcept(false) {\n      Base::LocalLifetime::destroy(global);\n    }\n  };\n\n  //  It is an invariant that in a single call to mutate which calls mutate_slow\n  //  the thread_local local and the thread_local lifetime that are used are in\n  //  the same DSO as each other.\n  //\n  //  The following functions are all [[gnu::visibility(\"hidden\")]] in order to\n  //  ensure this invariant.\n\n  FOLLY_ERASE_NOINLINE static void mutate_slow(Signed v) noexcept {\n    static constexpr Arg arg{&global, &local, &lifetime};\n    mutate_slow(v, arg);\n  }\n\n  FOLLY_ERASE static void mutate(Signed v, void (&slow)(Signed) = mutate_slow) {\n    auto const cache = local().cache; // a copy! null before/after LocalLifetime\n    //  fun-ref to trick compiler into emitting a tail call\n    FOLLY_LIKELY(!!cache) ? mutate(v, {cache, true}) : slow(v);\n  }\n\n  static constexpr GetGlobal& global = folly::detail::createGlobal<Global, Tag>;\n\n  FOLLY_ERASE static CounterAndCache& local() {\n    //  this is a member function local instead of a class member because of\n    //  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66944\n    static thread_local CounterAndCache instance;\n    return instance;\n  }\n\n  FOLLY_ERASE static LocalLifetime& lifetime() {\n    static thread_local MonoLocalLifetime lifetime;\n    return lifetime;\n  }\n};\n\ntemplate <typename Counted>\nclass SingletonRelaxedCountableAccess;\n\n//  SingletonRelaxedCountable\n//\n//  A CRTP base class for making the instances of a type within a process be\n//  globally counted. The running counter is a relaxed counter.\n//\n//  To avoid adding any new names from the base class to the counted type, the\n//  count is exposed via a separate type SingletonRelaxedCountableAccess.\n//\n//  This type is a convenience interface around SingletonRelaxedCounter.\ntemplate <typename Counted>\nclass SingletonRelaxedCountable {\n public:\n  SingletonRelaxedCountable() noexcept {\n    static_assert(\n        std::is_base_of<SingletonRelaxedCountable, Counted>::value, \"non-crtp\");\n    Counter::add(1);\n  }\n  ~SingletonRelaxedCountable() noexcept {\n    static_assert(\n        std::is_base_of<SingletonRelaxedCountable, Counted>::value, \"non-crtp\");\n    Counter::sub(1);\n  }\n\n  SingletonRelaxedCountable(const SingletonRelaxedCountable&) noexcept\n      : SingletonRelaxedCountable() {}\n  SingletonRelaxedCountable(SingletonRelaxedCountable&&) noexcept\n      : SingletonRelaxedCountable() {}\n\n  SingletonRelaxedCountable& operator=(const SingletonRelaxedCountable&) =\n      default;\n  SingletonRelaxedCountable& operator=(SingletonRelaxedCountable&&) = default;\n\n private:\n  friend class SingletonRelaxedCountableAccess<Counted>;\n\n  struct Tag;\n  using Counter = SingletonRelaxedCounter<size_t, Tag>;\n};\n\n//  SingletonRelaxedCountableAccess\n//\n//  Provides access to the running count of instances of a type using the CRTP\n//  base class SingletonRelaxedCountable.\ntemplate <typename Counted>\nclass SingletonRelaxedCountableAccess {\n public:\n  static size_t count() {\n    return SingletonRelaxedCountable<Counted>::Counter::count();\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/ThreadCachedSynchronized.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <memory>\n#include <type_traits>\n#include <utility>\n\n#include <folly/SharedMutex.h>\n#include <folly/ThreadLocal.h>\n#include <folly/Utility.h>\n#include <folly/lang/Access.h>\n#include <folly/synchronization/Lock.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\nnamespace folly {\n\n//  thread_cached_synchronized\n//\n//  Roughly equivalent to Synchronized, but with a per-thread cache for\n//  acceleration.\n//\n//  Use in hot code when Synchronized alone, with its shared lock and unlock,\n//  would be too costly.\n//\n//  Avoid when acceleration is marginal since per-thread caches are expensive.\n//\n//  Example:\n//\n//    struct writer_and_readers {\n//      folly::thread_cached_synchronized<std::shared_ptr<data>> obj_;\n//      std::jthread background_writer_{std::bind(loop_update, this)};\n//\n//      std::shared_ptr<data> get_recent_data_fast() { return obj; }\n//\n//      data fetch_recent_data();\n//      bool needs_data_and_not_signaled_done();\n//      void loop_update() {\n//        while (needs_data_and_not_signaled_done()) {\n//          obj.exchange(folly::copy_to_shared_ptr(fetch_recent_data()));\n//        }\n//      }\n//    };\n//\n//  Note:\n//    A singleton variation of this with SingletonThreadLocal would remove one\n//    of the branches when looking up the per-thread cache but would introduce\n//    a new branch when looking up the global version.\ntemplate <typename T, typename Mutex = SharedMutex>\nclass thread_cached_synchronized {\n  static_assert(std::is_same<std::decay_t<T>, T>::value, \"not decayed\");\n  static_assert(std::is_copy_constructible<T>::value, \"not copy-constructible\");\n\n public:\n  using value_type = T;\n\n private:\n  using version_type = std::uint64_t; // 64 bits will not overflow\n\n  struct truth_state {\n    relaxed_atomic<version_type> version{0}; // tiny optimization if first field\n    Mutex mutex{}; // protects value and sometimes version\n    value_type value;\n\n    template <typename... A>\n    truth_state(A&&... a) noexcept(\n        std::is_nothrow_constructible<Mutex>{} &&\n        std::is_nothrow_constructible<value_type, A...>{})\n        : value{static_cast<A&&>(a)...} {}\n  };\n\n  struct cache_state {\n    version_type version{0};\n    value_type value;\n\n    template <typename... A>\n    cache_state(A&&... a) //\n        noexcept(std::is_nothrow_constructible<value_type, A...>{})\n        : value{static_cast<A&&>(a)...} {}\n  };\n  using tlp_cache_state = ThreadLocalPtr<cache_state>;\n\n  template <typename... A>\n  static constexpr bool nx =\n      noexcept(truth_state{std::in_place, FOLLY_DECLVAL(A)...});\n\n  template <bool C>\n  using if_ = std::enable_if_t<C, int>;\n\n  using swap_fn = access::swap_fn;\n\n public:\n  template <typename A = value_type, if_<std::is_constructible<A>{}> = 0>\n  thread_cached_synchronized() noexcept(nx<>) : truth_{std::in_place} {}\n  explicit thread_cached_synchronized(value_type const& a) //\n      noexcept(nx<value_type const&>)\n      : truth_{a} {}\n  explicit thread_cached_synchronized(value_type&& a) noexcept(nx<value_type&&>)\n      : truth_{static_cast<value_type&&>(a)} {}\n  template <typename A, if_<std::is_constructible<value_type, A>{}> = 0>\n  explicit thread_cached_synchronized(A&& a) noexcept(nx<A&&>)\n      : truth_{static_cast<A&&>(a)} {}\n  template <typename... A, if_<std::is_constructible<value_type, A...>{}> = 0>\n  explicit thread_cached_synchronized(std::in_place_t, A&&... a) noexcept(\n      nx<A&&...>)\n      : truth_{static_cast<A&&>(a)...} {}\n\n  template <typename A, if_<std::is_assignable<value_type&, A>{}> = 0>\n  thread_cached_synchronized& operator=(A && a) noexcept(false) {\n    store(static_cast<A&&>(a));\n    return *this;\n  }\n\n  template <typename A = value_type>\n  void store(A&& a = A{}) {\n    mutate([&](auto& value) { value = static_cast<A&&>(a); });\n  }\n\n  template <typename A = value_type>\n  value_type exchange(A&& a = A{}) {\n    return mutate([&](auto& value) { //\n      return std::exchange(value, static_cast<A&&>(a));\n    });\n  }\n\n  template <typename A>\n  bool compare_exchange(value_type& expected, A&& desired) {\n    return mutate_cx(expected, static_cast<A&&>(desired));\n  }\n\n  template <typename A>\n  void swap(A& that) noexcept(false) {\n    mutate([&](auto& value) { access::swap(value, that); });\n  }\n\n  template <typename A, if_<is_invocable_v<swap_fn, value_type&, A&>> = 0>\n  friend void swap(thread_cached_synchronized& self, A& that) noexcept(false) {\n    self.swap(that);\n  }\n\n  value_type const& operator*() const { return ref(); }\n  value_type const* operator->() const { return std::addressof(ref()); }\n  value_type load() const { return std::as_const(ref()); }\n  /* implicit */ operator value_type() const { return load(); }\n\n private:\n  void invalidate_caches() {\n    truth_.version = truth_.version + 1; // intentionally not +=\n  }\n\n  // TODO: past C++17, just use if-constexpr in mutate()\n  template <\n      typename F,\n      typename R = invoke_result_t<F, value_type&>,\n      if_<std::is_void<R>{}> = 0>\n  R mutate_locked(F f) {\n    f(truth_.value); // value first: mutation may throw\n    invalidate_caches();\n  }\n  template <\n      typename F,\n      typename R = invoke_result_t<F, value_type&>,\n      if_<!std::is_void<R>{}> = 0>\n  R mutate_locked(F f) {\n    decltype(auto) ret = f(truth_.value); // value first: mutation may throw\n    invalidate_caches();\n    return ret;\n  }\n  template <typename F, typename R = invoke_result_t<F, value_type&>>\n  R mutate(F f) {\n    unique_lock<Mutex> lock{truth_.mutex};\n    return mutate_locked(f);\n  }\n\n  template <typename A>\n  bool mutate_cx(value_type& expected, A&& desired) {\n    unique_lock<Mutex> lock{truth_.mutex};\n    auto const eq = std::as_const(truth_.value) == std::as_const(expected);\n    if (eq) {\n      truth_.value = // value first: mutation may throw\n          static_cast<A&&>(desired);\n      invalidate_caches();\n    } else {\n      expected = std::as_const(truth_.value);\n    }\n    return eq;\n  }\n\n  FOLLY_ERASE value_type& ref() const {\n    auto const cache = cache_.get();\n    auto const unexpired = cache && cache->version == truth_.version;\n    return FOLLY_LIKELY(unexpired) ? cache->value : get_slow();\n  }\n\n  FOLLY_NOINLINE value_type& get_slow() const {\n    hybrid_lock<Mutex> lock{truth_.mutex};\n    auto cache = cache_.get();\n    if (cache == nullptr) {\n      cache = new cache_state{truth_.value}; // value first: copy may throw\n      cache_.reset(cache);\n    } else {\n      cache->value = truth_.value; // value first: copy may throw\n    }\n    cache->version = truth_.version;\n    return cache->value;\n  }\n\n  mutable truth_state truth_;\n  mutable tlp_cache_state cache_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/UnboundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <chrono>\n#include <memory>\n\n#include <glog/logging.h>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Optional.h>\n#include <folly/Traits.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/lang/Align.h>\n#include <folly/synchronization/Hazptr.h>\n#include <folly/synchronization/SaturatingSemaphore.h>\n#include <folly/synchronization/WaitOptions.h>\n#include <folly/synchronization/detail/Spin.h>\n\nnamespace folly {\n\n/// UnboundedQueue supports a variety of options for unbounded\n/// dynamically expanding an shrinking queues, including variations of:\n/// - Single vs. multiple producers\n/// - Single vs. multiple consumers\n/// - Blocking vs. spin-waiting\n/// - Non-waiting, timed, and waiting consumer operations.\n/// Producer operations never wait or fail (unless out-of-memory).\n///\n/// Template parameters:\n/// - T: element type\n/// - SingleProducer: true if there can be only one producer at a\n///   time.\n/// - SingleConsumer: true if there can be only one consumer at a\n///   time.\n/// - MayBlock: true if consumers may block, false if they only\n///   spin. A performance tuning parameter.\n/// - LgSegmentSize (default 8): Log base 2 of number of elements per\n///   segment. A performance tuning parameter. See below.\n/// - LgAlign (default 7): Log base 2 of alignment directive; can be\n///   used to balance scalability (avoidance of false sharing) with\n///   memory efficiency.\n///\n/// When to use UnboundedQueue:\n/// - If a small bound may lead to deadlock or performance degradation\n///   under bursty patterns.\n/// - If there is no risk of the queue growing too much.\n///\n/// When not to use UnboundedQueue:\n/// - If there is risk of the queue growing too much and a large bound\n///   is acceptable, then use DynamicBoundedQueue.\n/// - If the queue must not allocate on enqueue or it must have a\n///   small bound, then use fixed-size MPMCQueue or (if non-blocking\n///   SPSC) ProducerConsumerQueue.\n///\n/// Template Aliases:\n///   USPSCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///   UMPSCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///   USPMCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///   UMPMCQueue<T, MayBlock, LgSegmentSize, LgAlign>\n///\n/// Functions:\n///   Producer operations never wait or fail (unless OOM)\n///     void enqueue(const T&);\n///     void enqueue(T&&);\n///         Adds an element to the end of the queue.\n///\n///   Consumer operations:\n///     void dequeue(T&);\n///     T dequeue();\n///         Extracts an element from the front of the queue. Waits\n///         until an element is available if needed.\n///     bool try_dequeue(T&);\n///     folly::Optional<T> try_dequeue();\n///         Tries to extract an element from the front of the queue\n///         if available.\n///     bool try_dequeue_until(T&, time_point& deadline);\n///     folly::Optional<T> try_dequeue_until(time_point& deadline);\n///         Tries to extract an element from the front of the queue\n///         if available until the specified deadline.\n///     bool try_dequeue_for(T&, duration&);\n///     folly::Optional<T> try_dequeue_for(duration&);\n///         Tries to extract an element from the front of the queue if\n///         available until the expiration of the specified duration.\n///     const T* try_peek();\n///         Returns pointer to the element at the front of the queue\n///         if available, or nullptr if the queue is empty. Only for\n///         SPSC and MPSC.\n///\n///   Secondary functions:\n///     size_t size();\n///         Returns an estimate of the size of the queue.\n///     bool empty();\n///         Returns true only if the queue was empty during the call.\n///     Note: size() and empty() are guaranteed to be accurate only if\n///     the queue is not changed concurrently.\n///\n/// Usage examples:\n/// @code\n///   /* UMPSC, doesn't block, 1024 int elements per segment */\n///   UMPSCQueue<int, false, 10> q;\n///   q.enqueue(1);\n///   q.enqueue(2);\n///   q.enqueue(3);\n///   ASSERT_FALSE(q.empty());\n///   ASSERT_EQ(q.size(), 3);\n///   int v;\n///   q.dequeue(v);\n///   ASSERT_EQ(v, 1);\n///   ASSERT_TRUE(try_dequeue(v));\n///   ASSERT_EQ(v, 2);\n///   ASSERT_TRUE(try_dequeue_until(v, now() + seconds(1)));\n///   ASSERT_EQ(v, 3);\n///   ASSERT_TRUE(q.empty());\n///   ASSERT_EQ(q.size(), 0);\n///   ASSERT_FALSE(try_dequeue(v));\n///   ASSERT_FALSE(try_dequeue_for(v, microseconds(100)));\n/// @endcode\n///\n/// Design:\n/// - The queue is composed of one or more segments. Each segment has\n///   a fixed size of 2^LgSegmentSize entries. Each segment is used\n///   exactly once.\n/// - Each entry is composed of a futex and a single element.\n/// - Each segment's array of entries is strided to avoid false sharing.\n///   I.e., to reduce any cacheline contention that might be induced by\n///   concurrent mutations to the queue that might happen to affect\n///   otherwise-adjacent locations that might happen to share cacheline.\n/// - The queue contains two 64-bit ticket variables. The producer\n///   ticket counts the number of producer tickets issued so far, and\n///   the same for the consumer ticket. Each ticket number corresponds\n///   to a specific entry in a specific segment.\n/// - The queue maintains two pointers, head and tail. Head points to\n///   the segment that corresponds to the current consumer\n///   ticket. Similarly, tail pointer points to the segment that\n///   corresponds to the producer ticket.\n/// - Segments are organized as a singly linked list.\n/// - The producer with the first ticket in the current producer\n///   segment has primary responsibility for allocating and linking\n///   the next segment. Other producers and connsumers may help do so\n///   when needed if that thread is delayed.\n/// - The producer with the last ticket in the current producer\n///   segment is primarily responsible for advancing the tail pointer\n///   to the next segment. Other producers and consumers may help do\n///   so when needed if that thread is delayed.\n/// - Similarly, the consumer with the last ticket in the current\n///   consumer segment is primarily responsible for advancing the head\n///   pointer to the next segment. Other consumers may help do so when\n///   needed if that thread is delayed.\n/// - The tail pointer must not lag behind the head pointer.\n///   Otherwise, the algorithm cannot be certain about the removal of\n///   segment and would have to incur higher costs to ensure safe\n///   reclamation.  Consumers must ensure that head never overtakes\n///   tail.\n///\n/// Memory Usage:\n/// - An empty queue contains one segment. A nonempty queue contains\n///   one or two more segment than fits its contents.\n/// - Removed segments are not reclaimed until there are no threads,\n///   producers or consumers, with references to them or their\n///   predecessors. That is, a lagging thread may delay the reclamation\n///   of a chain of removed segments.\n/// - The template parameter LgAlign can be used to reduce memory usage\n///   at the cost of increased chance of false sharing.\n///\n/// Performance considerations:\n/// - All operations take constant time, excluding the costs of\n///   allocation, reclamation, interference from other threads, and\n///   waiting for actions by other threads.\n/// - In general, using the single producer and or single consumer\n///   variants yield better performance than the MP and MC\n///   alternatives.\n/// - SPSC without blocking is the fastest configuration. It doesn't\n///   include any read-modify-write atomic operations, full fences, or\n///   system calls in the critical path.\n/// - MP adds a fetch_add to the critical path of each producer operation.\n/// - MC adds a fetch_add or compare_exchange to the critical path of\n///   each consumer operation.\n/// - The possibility of consumers blocking, even if they never do,\n///   adds a compare_exchange to the critical path of each producer\n///   operation.\n/// - MPMC, SPMC, MPSC require the use of a deferred reclamation\n///   mechanism to guarantee that segments removed from the linked\n///   list, i.e., unreachable from the head pointer, are reclaimed\n///   only after they are no longer needed by any lagging producers or\n///   consumers.\n/// - The overheads of segment allocation and reclamation are intended\n///   to be mostly out of the critical path of the queue's throughput.\n/// - If the template parameter LgSegmentSize is changed, it should be\n///   set adequately high to keep the amortized cost of allocation and\n///   reclamation low.\n/// - It is recommended to measure performance with different variants\n///   when applicable, e.g., UMPMC vs UMPSC. Depending on the use\n///   case, sometimes the variant with the higher sequential overhead\n///   may yield better results due to, for example, more favorable\n///   producer-consumer balance or favorable timing for avoiding\n///   costly blocking.\n///\n/// Guarantees:\n/// - The queues are linearizable:\n///   - For two enqueue operations q(A) and q(B), if q(A) < q(B) in\n///     the happens-before relation, then A precedes B in the queue.\n///   - For two dequeue operations d(A) and d(B), if d(A) < d(B) in\n///     the happens-before relation, then A preceded B in the queue.\n\ntemplate <\n    typename T,\n    bool SingleProducer,\n    bool SingleConsumer,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nclass UnboundedQueue {\n  using Ticket = uint64_t;\n  class Entry;\n  class Segment;\n\n  static constexpr bool SPSC = SingleProducer && SingleConsumer;\n  static constexpr size_t Stride = SPSC || (LgSegmentSize <= 1) ? 1 : 27;\n  static constexpr size_t SegmentSize = 1u << LgSegmentSize;\n  static constexpr size_t Align = 1u << LgAlign;\n\n  static_assert(\n      std::is_nothrow_destructible<T>::value, \"T must be nothrow_destructible\");\n  static_assert((Stride & 1) == 1, \"Stride must be odd\");\n  static_assert(LgSegmentSize < 32, \"LgSegmentSize must be < 32\");\n  static_assert(LgAlign < 16, \"LgAlign must be < 16\");\n\n  using Sem = folly::SaturatingSemaphore<MayBlock, Atom>;\n\n  struct Consumer {\n    Atom<Segment*> head;\n    Atom<Ticket> ticket;\n    hazptr_obj_cohort<Atom> cohort;\n    explicit Consumer(Segment* s) : head(s), ticket(0) {\n      s->set_cohort_no_tag(&cohort); // defined in hazptr_obj\n    }\n  };\n  struct Producer {\n    Atom<Segment*> tail;\n    Atom<Ticket> ticket;\n    explicit Producer(Segment* s) : tail(s), ticket(0) {}\n  };\n\n  alignas(Align) Consumer c_;\n  alignas(Align) Producer p_;\n\n public:\n  using value_type = T;\n  using size_type = size_t;\n\n  /** constructor */\n  UnboundedQueue()\n      : c_(new Segment(0)), p_(c_.head.load(std::memory_order_relaxed)) {}\n\n  /** destructor */\n  ~UnboundedQueue() {\n    cleanUpRemainingItems();\n    reclaimRemainingSegments();\n  }\n\n  /** enqueue */\n  FOLLY_ALWAYS_INLINE void enqueue(const T& arg) { enqueueImpl(arg); }\n\n  FOLLY_ALWAYS_INLINE void enqueue(T&& arg) { enqueueImpl(std::move(arg)); }\n\n  /** dequeue */\n  FOLLY_ALWAYS_INLINE void dequeue(T& item) noexcept { item = dequeueImpl(); }\n\n  FOLLY_ALWAYS_INLINE T dequeue() noexcept { return dequeueImpl(); }\n\n  /** try_dequeue */\n  FOLLY_ALWAYS_INLINE bool try_dequeue(T& item) noexcept {\n    auto o = try_dequeue();\n    if (FOLLY_LIKELY(o.has_value())) {\n      item = std::move(*o);\n      return true;\n    }\n    return false;\n  }\n\n  FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue() noexcept {\n    return tryDequeueUntil(std::chrono::steady_clock::time_point::min());\n  }\n\n  /** try_dequeue_until */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE bool try_dequeue_until(\n      T& item,\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    folly::Optional<T> o = try_dequeue_until(deadline);\n\n    if (FOLLY_LIKELY(o.has_value())) {\n      item = std::move(*o);\n      return true;\n    }\n\n    return false;\n  }\n\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    return tryDequeueUntil(deadline);\n  }\n\n  /** try_dequeue_for */\n  template <typename Rep, typename Period>\n  FOLLY_ALWAYS_INLINE bool try_dequeue_for(\n      T& item, const std::chrono::duration<Rep, Period>& duration) noexcept {\n    folly::Optional<T> o = try_dequeue_for(duration);\n\n    if (FOLLY_LIKELY(o.has_value())) {\n      item = std::move(*o);\n      return true;\n    }\n\n    return false;\n  }\n\n  template <typename Rep, typename Period>\n  FOLLY_ALWAYS_INLINE folly::Optional<T> try_dequeue_for(\n      const std::chrono::duration<Rep, Period>& duration) noexcept {\n    folly::Optional<T> o = try_dequeue();\n    if (FOLLY_LIKELY(o.has_value())) {\n      return o;\n    }\n    return tryDequeueUntil(std::chrono::steady_clock::now() + duration);\n  }\n\n  /** try_peek */\n  FOLLY_ALWAYS_INLINE const T* try_peek() noexcept {\n    static_assert(SingleConsumer, \"not single-consumer\");\n    return tryPeekUntil(std::chrono::steady_clock::time_point::min());\n  }\n\n  /** size */\n  size_t size() const noexcept {\n    auto p = producerTicket();\n    auto c = consumerTicket();\n    return p > c ? p - c : 0;\n  }\n\n  /** empty */\n  bool empty() const noexcept {\n    auto c = consumerTicket();\n    auto p = producerTicket();\n    return p <= c;\n  }\n\n private:\n  /** enqueueImpl */\n  template <typename Arg>\n  FOLLY_ALWAYS_INLINE void enqueueImpl(Arg&& arg) {\n    if (SPSC) {\n      Segment* s = tail();\n      enqueueCommon(s, std::forward<Arg>(arg));\n    } else {\n      // Using hazptr_holder instead of hazptr_local because it is\n      // possible that the T ctor happens to use hazard pointers.\n      hazptr_holder<Atom> hptr = make_hazard_pointer<Atom>();\n      Segment* s = hptr.protect(p_.tail);\n      enqueueCommon(s, std::forward<Arg>(arg));\n    }\n  }\n\n  /** enqueueCommon */\n  template <typename Arg>\n  FOLLY_ALWAYS_INLINE void enqueueCommon(Segment* s, Arg&& arg) {\n    Ticket t = fetchIncrementProducerTicket();\n    if (!SingleProducer) {\n      s = findSegment(s, t);\n    }\n    DCHECK_GE(t, s->minTicket());\n    DCHECK_LT(t, s->minTicket() + SegmentSize);\n    size_t idx = index(t);\n    Entry& e = s->entry(idx);\n    e.putItem(std::forward<Arg>(arg));\n    if (responsibleForAlloc(t)) {\n      allocNextSegment(s);\n    }\n    if (responsibleForAdvance(t)) {\n      advanceTail(s);\n    }\n  }\n\n  /** dequeueImpl */\n  FOLLY_ALWAYS_INLINE T dequeueImpl() noexcept {\n    if (SPSC) {\n      Segment* s = head();\n      return dequeueCommon(s);\n    } else {\n      // Using hazptr_holder instead of hazptr_local because it is\n      // possible to call the T dtor and it may happen to use hazard\n      // pointers.\n      hazptr_holder<Atom> hptr = make_hazard_pointer<Atom>();\n      Segment* s = hptr.protect(c_.head);\n      return dequeueCommon(s);\n    }\n  }\n\n  /** dequeueCommon */\n  FOLLY_ALWAYS_INLINE T dequeueCommon(Segment* s) noexcept {\n    Ticket t = fetchIncrementConsumerTicket();\n    if (!SingleConsumer) {\n      s = findSegment(s, t);\n    }\n    size_t idx = index(t);\n    Entry& e = s->entry(idx);\n    auto res = e.takeItem();\n    if (responsibleForAdvance(t)) {\n      advanceHead(s);\n    }\n    return res;\n  }\n\n  /** tryDequeueUntil */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE folly::Optional<T> tryDequeueUntil(\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    if (SingleConsumer) {\n      Segment* s = head();\n      return tryDequeueUntilSC(s, deadline);\n    } else {\n      // Using hazptr_holder instead of hazptr_local because it is\n      //  possible to call ~T() and it may happen to use hazard pointers.\n      hazptr_holder<Atom> hptr = make_hazard_pointer<Atom>();\n      Segment* s = hptr.protect(c_.head);\n      return tryDequeueUntilMC(s, deadline);\n    }\n  }\n\n  /** tryDequeueUntilSC */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE folly::Optional<T> tryDequeueUntilSC(\n      Segment* s,\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    Ticket t = consumerTicket();\n    DCHECK_GE(t, s->minTicket());\n    DCHECK_LT(t, (s->minTicket() + SegmentSize));\n    size_t idx = index(t);\n    Entry& e = s->entry(idx);\n    if (FOLLY_UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) {\n      return folly::Optional<T>();\n    }\n    setConsumerTicket(t + 1);\n    folly::Optional<T> ret = e.takeItem();\n    if (responsibleForAdvance(t)) {\n      advanceHead(s);\n    }\n    return ret;\n  }\n\n  /** tryDequeueUntilMC */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE folly::Optional<T> tryDequeueUntilMC(\n      Segment* s,\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    while (true) {\n      Ticket t = consumerTicket();\n      if (FOLLY_UNLIKELY(t >= (s->minTicket() + SegmentSize))) {\n        s = getAllocNextSegment(s, t);\n        DCHECK(s);\n        continue;\n      }\n      size_t idx = index(t);\n      Entry& e = s->entry(idx);\n      if (FOLLY_UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) {\n        return folly::Optional<T>();\n      }\n      if (!c_.ticket.compare_exchange_weak(\n              t, t + 1, std::memory_order_acq_rel, std::memory_order_acquire)) {\n        continue;\n      }\n      folly::Optional<T> ret = e.takeItem();\n      if (responsibleForAdvance(t)) {\n        advanceHead(s);\n      }\n      return ret;\n    }\n  }\n\n  /** tryDequeueWaitElem */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE bool tryDequeueWaitElem(\n      Entry& e,\n      Ticket t,\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    if (FOLLY_LIKELY(e.tryWaitUntil(deadline))) {\n      return true;\n    }\n    return t < producerTicket();\n  }\n\n  /** tryPeekUntil */\n  template <typename Clock, typename Duration>\n  FOLLY_ALWAYS_INLINE const T* tryPeekUntil(\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    // This function is supported only for USPSC and UMPSC queues.\n    DCHECK(SingleConsumer);\n    Segment* s = head();\n    Ticket t = consumerTicket();\n    DCHECK_GE(t, s->minTicket());\n    DCHECK_LT(t, (s->minTicket() + SegmentSize));\n    size_t idx = index(t);\n    Entry& e = s->entry(idx);\n    if (FOLLY_UNLIKELY(!tryDequeueWaitElem(e, t, deadline))) {\n      return nullptr;\n    }\n    return e.peekItem();\n  }\n\n  /** findSegment */\n  FOLLY_ALWAYS_INLINE\n  Segment* findSegment(Segment* s, const Ticket t) noexcept {\n    while (FOLLY_UNLIKELY(t >= (s->minTicket() + SegmentSize))) {\n      s = getAllocNextSegment(s, t);\n      DCHECK(s);\n    }\n    return s;\n  }\n\n  /** getAllocNextSegment */\n  Segment* getAllocNextSegment(Segment* s, Ticket t) noexcept {\n    Segment* next = s->nextSegment();\n    if (!next) {\n      DCHECK_GE(t, s->minTicket() + SegmentSize);\n      auto diff = t - (s->minTicket() + SegmentSize);\n      if (diff > 0) {\n        auto dur = std::chrono::microseconds(diff);\n        auto deadline = std::chrono::steady_clock::now() + dur;\n        WaitOptions opt;\n        opt.spin_max(dur);\n        detail::spin_pause_until(deadline, opt, [s] {\n          return s->nextSegment();\n        });\n        next = s->nextSegment();\n        if (next) {\n          return next;\n        }\n      }\n      next = allocNextSegment(s);\n    }\n    DCHECK(next);\n    return next;\n  }\n\n  /** allocNextSegment */\n  Segment* allocNextSegment(Segment* s) {\n    auto t = s->minTicket() + SegmentSize;\n    Segment* next = new Segment(t);\n    next->set_cohort_no_tag(&c_.cohort); // defined in hazptr_obj\n    next->acquire_ref_safe(); // defined in hazptr_obj_base_linked\n    if (!s->casNextSegment(next)) {\n      delete next;\n      next = s->nextSegment();\n    }\n    DCHECK(next);\n    return next;\n  }\n\n  /** advanceTail */\n  void advanceTail(Segment* s) noexcept {\n    if (SPSC) {\n      Segment* next = s->nextSegment();\n      DCHECK(next);\n      setTail(next);\n    } else {\n      Ticket t = s->minTicket() + SegmentSize;\n      advanceTailToTicket(t);\n    }\n  }\n\n  /** advanceTailToTicket */\n  void advanceTailToTicket(Ticket t) noexcept {\n    Segment* s = tail();\n    while (s->minTicket() < t) {\n      Segment* next = s->nextSegment();\n      if (!next) {\n        next = allocNextSegment(s);\n      }\n      DCHECK(next);\n      casTail(s, next);\n      s = tail();\n    }\n  }\n\n  /** advanceHead */\n  void advanceHead(Segment* s) noexcept {\n    if (SPSC) {\n      while (tail() == s) {\n        /* Wait for producer to advance tail. */\n        asm_volatile_pause();\n      }\n      Segment* next = s->nextSegment();\n      DCHECK(next);\n      setHead(next);\n      reclaimSegment(s);\n    } else {\n      Ticket t = s->minTicket() + SegmentSize;\n      advanceHeadToTicket(t);\n    }\n  }\n\n  /** advanceHeadToTicket */\n  void advanceHeadToTicket(Ticket t) noexcept {\n    /* Tail must not lag behind head. Otherwise, the algorithm cannot\n       be certain about removal of segments. */\n    advanceTailToTicket(t);\n    Segment* s = head();\n    if (SingleConsumer) {\n      DCHECK_EQ(s->minTicket() + SegmentSize, t);\n      Segment* next = s->nextSegment();\n      DCHECK(next);\n      setHead(next);\n      reclaimSegment(s);\n    } else {\n      while (s->minTicket() < t) {\n        Segment* next = s->nextSegment();\n        DCHECK(next);\n        if (casHead(s, next)) {\n          reclaimSegment(s);\n          s = next;\n        }\n      }\n    }\n  }\n\n  /** reclaimSegment */\n  void reclaimSegment(Segment* s) noexcept {\n    if (SPSC) {\n      delete s;\n    } else {\n      s->retire(); // defined in hazptr_obj_base_linked\n    }\n  }\n\n  /** cleanUpRemainingItems */\n  void cleanUpRemainingItems() {\n    auto end = producerTicket();\n    auto s = head();\n    for (auto t = consumerTicket(); t < end; ++t) {\n      if (t >= s->minTicket() + SegmentSize) {\n        s = s->nextSegment();\n      }\n      DCHECK_LT(t, (s->minTicket() + SegmentSize));\n      auto idx = index(t);\n      auto& e = s->entry(idx);\n      e.destroyItem();\n    }\n  }\n\n  /** reclaimRemainingSegments */\n  void reclaimRemainingSegments() {\n    auto h = head();\n    auto s = h->nextSegment();\n    h->setNextSegment(nullptr);\n    reclaimSegment(h);\n    while (s) {\n      auto next = s->nextSegment();\n      delete s;\n      s = next;\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE size_t index(Ticket t) const noexcept {\n    return (t * Stride) & (SegmentSize - 1);\n  }\n\n  FOLLY_ALWAYS_INLINE bool responsibleForAlloc(Ticket t) const noexcept {\n    return (t & (SegmentSize - 1)) == 0;\n  }\n\n  FOLLY_ALWAYS_INLINE bool responsibleForAdvance(Ticket t) const noexcept {\n    return (t & (SegmentSize - 1)) == (SegmentSize - 1);\n  }\n\n  FOLLY_ALWAYS_INLINE Segment* head() const noexcept {\n    return c_.head.load(std::memory_order_acquire);\n  }\n\n  FOLLY_ALWAYS_INLINE Segment* tail() const noexcept {\n    return p_.tail.load(std::memory_order_acquire);\n  }\n\n  FOLLY_ALWAYS_INLINE Ticket producerTicket() const noexcept {\n    return p_.ticket.load(std::memory_order_acquire);\n  }\n\n  FOLLY_ALWAYS_INLINE Ticket consumerTicket() const noexcept {\n    return c_.ticket.load(std::memory_order_acquire);\n  }\n\n  void setHead(Segment* s) noexcept {\n    DCHECK(SingleConsumer);\n    c_.head.store(s, std::memory_order_relaxed);\n  }\n\n  void setTail(Segment* s) noexcept {\n    DCHECK(SPSC);\n    p_.tail.store(s, std::memory_order_release);\n  }\n\n  bool casHead(Segment*& s, Segment* next) noexcept {\n    DCHECK(!SingleConsumer);\n    return c_.head.compare_exchange_strong(\n        s, next, std::memory_order_release, std::memory_order_acquire);\n  }\n\n  void casTail(Segment*& s, Segment* next) noexcept {\n    DCHECK(!SPSC);\n    p_.tail.compare_exchange_strong(\n        s, next, std::memory_order_release, std::memory_order_relaxed);\n  }\n\n  FOLLY_ALWAYS_INLINE void setProducerTicket(Ticket t) noexcept {\n    p_.ticket.store(t, std::memory_order_release);\n  }\n\n  FOLLY_ALWAYS_INLINE void setConsumerTicket(Ticket t) noexcept {\n    c_.ticket.store(t, std::memory_order_release);\n  }\n\n  FOLLY_ALWAYS_INLINE Ticket fetchIncrementConsumerTicket() noexcept {\n    if (SingleConsumer) {\n      Ticket oldval = consumerTicket();\n      setConsumerTicket(oldval + 1);\n      return oldval;\n    } else { // MC\n      return c_.ticket.fetch_add(1, std::memory_order_acq_rel);\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE Ticket fetchIncrementProducerTicket() noexcept {\n    if (SingleProducer) {\n      Ticket oldval = producerTicket();\n      setProducerTicket(oldval + 1);\n      return oldval;\n    } else { // MP\n      return p_.ticket.fetch_add(1, std::memory_order_acq_rel);\n    }\n  }\n\n  /**\n   *  Entry\n   */\n  class Entry {\n    Sem flag_;\n    aligned_storage_for_t<T> item_;\n\n   public:\n    template <typename Arg>\n    FOLLY_ALWAYS_INLINE void putItem(Arg&& arg) {\n      new (&item_) T(std::forward<Arg>(arg));\n      flag_.post();\n    }\n\n    FOLLY_ALWAYS_INLINE T takeItem() noexcept {\n      flag_.wait();\n      return getItem();\n    }\n\n    FOLLY_ALWAYS_INLINE const T* peekItem() noexcept {\n      flag_.wait();\n      return itemPtr();\n    }\n\n    template <typename Clock, typename Duration>\n    FOLLY_EXPORT FOLLY_ALWAYS_INLINE bool tryWaitUntil(\n        const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n      // wait-options from benchmarks on contended queues:\n      static constexpr auto const opt =\n          Sem::wait_options().spin_max(std::chrono::microseconds(10));\n      return flag_.try_wait_until(deadline, opt);\n    }\n\n    FOLLY_ALWAYS_INLINE void destroyItem() noexcept { itemPtr()->~T(); }\n\n   private:\n    FOLLY_ALWAYS_INLINE T getItem() noexcept {\n      T ret = std::move(*(itemPtr()));\n      destroyItem();\n      return ret;\n    }\n\n    FOLLY_ALWAYS_INLINE T* itemPtr() noexcept {\n      return static_cast<T*>(static_cast<void*>(&item_));\n    }\n  }; // Entry\n\n  /**\n   *  Segment\n   */\n  class Segment : public hazptr_obj_base_linked<Segment, Atom> {\n    Atom<Segment*> next_{nullptr};\n    const Ticket min_;\n    alignas(Align) Entry b_[SegmentSize];\n\n   public:\n    explicit Segment(const Ticket t) noexcept : min_(t) {}\n\n    Segment* nextSegment() const noexcept {\n      return next_.load(std::memory_order_acquire);\n    }\n\n    void setNextSegment(Segment* next) {\n      next_.store(next, std::memory_order_relaxed);\n    }\n\n    bool casNextSegment(Segment* next) noexcept {\n      Segment* expected = nullptr;\n      return next_.compare_exchange_strong(\n          expected, next, std::memory_order_release, std::memory_order_relaxed);\n    }\n\n    FOLLY_ALWAYS_INLINE Ticket minTicket() const noexcept {\n      DCHECK_EQ((min_ & (SegmentSize - 1)), Ticket(0));\n      return min_;\n    }\n\n    FOLLY_ALWAYS_INLINE Entry& entry(size_t index) noexcept {\n      return b_[index];\n    }\n\n    template <typename S>\n    void push_links(bool m, S& s) {\n      if (m == false) { // next_ is immutable\n        auto p = nextSegment();\n        if (p) {\n          s.push(p);\n        }\n      }\n    }\n  }; // Segment\n\n}; // UnboundedQueue\n\n/* Aliases */\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing USPSCQueue =\n    UnboundedQueue<T, true, true, MayBlock, LgSegmentSize, LgAlign, Atom>;\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing UMPSCQueue =\n    UnboundedQueue<T, false, true, MayBlock, LgSegmentSize, LgAlign, Atom>;\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing USPMCQueue =\n    UnboundedQueue<T, true, false, MayBlock, LgSegmentSize, LgAlign, Atom>;\n\ntemplate <\n    typename T,\n    bool MayBlock,\n    size_t LgSegmentSize = 8,\n    size_t LgAlign = constexpr_log2(hardware_destructive_interference_size),\n    template <typename> class Atom = std::atomic>\nusing UMPMCQueue =\n    UnboundedQueue<T, false, false, MayBlock, LgSegmentSize, LgAlign, Atom>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/container/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"atomic_grow_array\",\n    headers = [\n        \"atomic_grow_array.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:constexpr_math\",\n        \"//folly:likely\",\n        \"//folly:scope_guard\",\n        \"//folly/container:span\",\n        \"//folly/lang:align\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:new\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"flat_combining_priority_queue\",\n    headers = [\"FlatCombiningPriorityQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:optional\",\n        \"//folly/detail:futex\",\n        \"//folly/synchronization:flat_combining\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lock_free_ring_buffer\",\n    headers = [\"LockFreeRingBuffer.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly:traits\",\n        \"//folly/detail:turn_sequencer\",\n        \"//folly/portability:unistd\",\n        \"//folly/synchronization:sanitize_thread\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"relaxed_concurrent_priority_queue\",\n    headers = [\n        \"RelaxedConcurrentPriorityQueue.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:random\",\n        \"//folly:spin_lock\",\n        \"//folly:thread_local\",\n        \"//folly/detail:futex\",\n        \"//folly/lang:align\",\n        \"//folly/synchronization:hazptr\",\n        \"//folly/synchronization:wait_options\",\n        \"//folly/synchronization/detail:spin\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"single_writer_fixed_hash_map\",\n    headers = [\n        \"SingleWriterFixedHashMap.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/lang:bits\",\n    ],\n)\n"
  },
  {
    "path": "folly/concurrency/container/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME atomic_grow_array\n  HEADERS\n    atomic_grow_array.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_constexpr_math\n    folly_container_span\n    folly_lang_align\n    folly_lang_bits\n    folly_lang_new\n    folly_likely\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME flat_combining_priority_queue\n  HEADERS\n    FlatCombiningPriorityQueue.h\n  EXPORTED_DEPS\n    folly_detail_futex\n    folly_optional\n    folly_synchronization_flat_combining\n)\n\nfolly_add_library(\n  NAME lock_free_ring_buffer\n  HEADERS\n    LockFreeRingBuffer.h\n  EXPORTED_DEPS\n    folly_detail_turn_sequencer\n    folly_portability\n    folly_portability_unistd\n    folly_synchronization_sanitize_thread\n    folly_traits\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME relaxed_concurrent_priority_queue\n  HEADERS\n    RelaxedConcurrentPriorityQueue.h\n  EXPORTED_DEPS\n    folly_detail_futex\n    folly_lang_align\n    folly_random\n    folly_spin_lock\n    folly_synchronization_detail_spin\n    folly_synchronization_hazptr\n    folly_synchronization_wait_options\n    folly_thread_local\n)\n\nfolly_add_library(\n  NAME single_writer_fixed_hash_map\n  HEADERS\n    SingleWriterFixedHashMap.h\n  EXPORTED_DEPS\n    folly_lang_bits\n)\n"
  },
  {
    "path": "folly/concurrency/container/FlatCombiningPriorityQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <chrono>\n#include <memory>\n#include <mutex>\n#include <queue>\n\n#include <glog/logging.h>\n\n#include <folly/Optional.h>\n#include <folly/detail/Futex.h>\n#include <folly/synchronization/FlatCombining.h>\n\nnamespace folly {\n\n/// Thread-safe priority queue based on flat combining. If the\n/// constructor parameter maxSize is greater than 0 (default = 0),\n/// then the queue is bounded. This template provides blocking,\n/// non-blocking, and timed variants of each of push(), pop(), and\n/// peek() operations. The empty() and size() functions are inherently\n/// non-blocking.\n///\n/// PriorityQueue must support the interface of std::priority_queue,\n/// specifically empty(), size(), push(), top(), and pop().  Mutex\n/// must meet the standard Lockable requirements.\n///\n/// By default FlatCombining uses a dedicated combiner thread, which\n/// yields better latency and throughput under high contention but\n/// higher overheads under low contention. If the constructor\n/// parameter dedicated is false, then there will be no dedicated\n/// combiner thread and any requester may do combining of operations\n/// requested by other threads. For more details see the comments for\n/// FlatCombining.\n///\n/// Usage examples:\n/// @code\n///   FlatCombiningPriorityQueue<int> pq(1);\n///   CHECK(pq.empty());\n///   CHECK(pq.size() == 0);\n///   int v;\n///   CHECK(!try_pop(v));\n///   CHECK(!try_pop_until(v, now() + seconds(1)));\n///   CHECK(!try_peek(v));\n///   CHECK(!try_peek_until(v, now() + seconds(1)));\n///   pq.push(10);\n///   CHECK(!pq.empty());\n///   CHECK(pq.size() == 1);\n///   CHECK(!pq.try_push(20));\n///   CHECK(!pq.try_push_until(20), now() + seconds(1)));\n///   peek(v);\n///   CHECK_EQ(v, 10);\n///   CHECK(pq.size() == 1);\n///   pop(v);\n///   CHECK_EQ(v, 10);\n///   CHECK(pq.empty());\n/// @encode\n\ntemplate <\n    typename T,\n    typename PriorityQueue = std::priority_queue<T>,\n    typename Mutex = std::mutex,\n    template <typename> class Atom = std::atomic>\nclass FlatCombiningPriorityQueue\n    : public folly::FlatCombining<\n          FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>,\n          Mutex,\n          Atom> {\n  using FCPQ = FlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>;\n  using FC = folly::FlatCombining<FCPQ, Mutex, Atom>;\n\n public:\n  template <\n      typename... PQArgs,\n      typename = decltype(PriorityQueue(std::declval<PQArgs>()...))>\n  explicit FlatCombiningPriorityQueue(\n      // Concurrent priority queue parameter\n      const size_t maxSize = 0,\n      // Flat combining parameters\n      const bool dedicated = true,\n      const uint32_t numRecs = 0,\n      const uint32_t maxOps = 0,\n      // (Sequential) PriorityQueue Parameters\n      PQArgs... args)\n      : FC(dedicated, numRecs, maxOps),\n        maxSize_(maxSize),\n        pq_(std::forward<PQArgs>(args)...) {}\n\n  /// Returns true iff the priority queue is empty\n  bool empty() const {\n    bool res;\n    auto fn = [&] { res = pq_.empty(); };\n    const_cast<FCPQ*>(this)->requestFC(fn);\n    return res;\n  }\n\n  /// Returns the number of items in the priority queue\n  size_t size() const {\n    size_t res;\n    auto fn = [&] { res = pq_.size(); };\n    const_cast<FCPQ*>(this)->requestFC(fn);\n    return res;\n  }\n\n  /// Non-blocking push. Succeeds if there is space in the priority\n  /// queue to insert the new item. Tries once if no time point is\n  /// provided or until the provided time_point is reached. If\n  /// successful, inserts the provided item in the priority queue\n  /// according to its priority.\n  bool try_push(const T& val) {\n    return try_push_impl(\n        val, std::chrono::time_point<std::chrono::steady_clock>::min());\n  }\n\n  /// Non-blocking pop. Succeeds if the priority queue is\n  /// nonempty. Tries once if no time point is provided or until the\n  /// provided time_point is reached.  If successful, copies the\n  /// highest priority item and removes it from the priority queue.\n  bool try_pop(T& val) {\n    return try_pop_impl(\n        val, std::chrono::time_point<std::chrono::steady_clock>::min());\n  }\n\n  /// Non-blocking peek. Succeeds if the priority queue is\n  /// nonempty. Tries once if no time point is provided or until the\n  /// provided time_point is reached.  If successful, copies the\n  /// highest priority item without removing it.\n  bool try_peek(T& val) {\n    return try_peek_impl(\n        val, std::chrono::time_point<std::chrono::steady_clock>::min());\n  }\n\n  /// Blocking push. Inserts the provided item in the priority\n  /// queue. If it is full, this function blocks until there is space\n  /// for the new item.\n  void push(const T& val) {\n    try_push_impl(\n        val, std::chrono::time_point<std::chrono::steady_clock>::max());\n  }\n\n  /// Blocking pop. Copies the highest priority item and removes\n  /// it. If the priority queue is empty, this function blocks until\n  /// it is nonempty.\n  void pop(T& val) {\n    try_pop_impl(\n        val, std::chrono::time_point<std::chrono::steady_clock>::max());\n  }\n\n  /// Blocking peek. Copies the highest priority item without\n  /// removing it. If the priority queue is empty, this function\n  /// blocks until it is nonempty.\n  void peek(T& val) {\n    try_peek_impl(\n        val, std::chrono::time_point<std::chrono::steady_clock>::max());\n  }\n\n  folly::Optional<T> try_pop() {\n    T val;\n    if (try_pop(val)) {\n      return std::move(val);\n    }\n    return folly::none;\n  }\n\n  folly::Optional<T> try_peek() {\n    T val;\n    if (try_peek(val)) {\n      return std::move(val);\n    }\n    return folly::none;\n  }\n\n  template <typename Rep, typename Period>\n  folly::Optional<T> try_pop_for(\n      const std::chrono::duration<Rep, Period>& timeout) {\n    T val;\n    if (try_pop(val) ||\n        try_pop_impl(val, std::chrono::steady_clock::now() + timeout)) {\n      return std::move(val);\n    }\n    return folly::none;\n  }\n\n  template <typename Rep, typename Period>\n  bool try_push_for(\n      const T& val, const std::chrono::duration<Rep, Period>& timeout) {\n    return (\n        try_push(val) ||\n        try_push_impl(val, std::chrono::steady_clock::now() + timeout));\n  }\n\n  template <typename Rep, typename Period>\n  folly::Optional<T> try_peek_for(\n      const std::chrono::duration<Rep, Period>& timeout) {\n    T val;\n    if (try_peek(val) ||\n        try_peek_impl(val, std::chrono::steady_clock::now() + timeout)) {\n      return std::move(val);\n    }\n    return folly::none;\n  }\n\n  template <typename Clock, typename Duration>\n  folly::Optional<T> try_pop_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) {\n    T val;\n    if (try_pop_impl(val, deadline)) {\n      return std::move(val);\n    }\n    return folly::none;\n  }\n\n  template <typename Clock, typename Duration>\n  bool try_push_until(\n      const T& val, const std::chrono::time_point<Clock, Duration>& deadline) {\n    return try_push_impl(val, deadline);\n  }\n\n  template <typename Clock, typename Duration>\n  folly::Optional<T> try_peek_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) {\n    T val;\n    if (try_peek_impl(val, deadline)) {\n      return std::move(val);\n    }\n    return folly::none;\n  }\n\n private:\n  size_t maxSize_;\n  PriorityQueue pq_;\n  detail::Futex<Atom> empty_{};\n  detail::Futex<Atom> full_{};\n\n  bool isTrue(detail::Futex<Atom>& futex) {\n    return futex.load(std::memory_order_relaxed) != 0;\n  }\n\n  void setFutex(detail::Futex<Atom>& futex, uint32_t val) {\n    futex.store(val, std::memory_order_relaxed);\n  }\n\n  bool futexSignal(detail::Futex<Atom>& futex) {\n    if (isTrue(futex)) {\n      setFutex(futex, 0);\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  template <typename Clock, typename Duration>\n  bool try_push_impl(\n      const T& val, const std::chrono::time_point<Clock, Duration>& when);\n\n  template <typename Clock, typename Duration>\n  bool try_pop_impl(\n      T& val, const std::chrono::time_point<Clock, Duration>& when);\n\n  template <typename Clock, typename Duration>\n  bool try_peek_impl(\n      T& val, const std::chrono::time_point<Clock, Duration>& when);\n};\n\n/// Implementation\n\ntemplate <\n    typename T,\n    typename PriorityQueue,\n    typename Mutex,\n    template <typename> class Atom>\ntemplate <typename Clock, typename Duration>\ninline bool\nFlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_push_impl(\n    const T& val, const std::chrono::time_point<Clock, Duration>& when) {\n  while (true) {\n    bool res;\n    bool wake;\n\n    auto fn = [&] {\n      if (maxSize_ > 0 && pq_.size() == maxSize_) {\n        setFutex(full_, 1);\n        res = false;\n        return;\n      }\n      DCHECK(maxSize_ == 0 || pq_.size() < maxSize_);\n      try {\n        pq_.push(val);\n        wake = futexSignal(empty_);\n        res = true;\n        return;\n      } catch (const std::bad_alloc&) {\n        setFutex(full_, 1);\n        res = false;\n        return;\n      }\n    };\n    this->requestFC(fn);\n\n    if (res) {\n      if (wake) {\n        detail::futexWake(&empty_);\n      }\n      return true;\n    }\n    if (when == std::chrono::time_point<Clock>::min()) {\n      return false;\n    }\n    while (isTrue(full_)) {\n      if (when == std::chrono::time_point<Clock>::max()) {\n        detail::futexWait(&full_, 1);\n      } else {\n        if (Clock::now() > when) {\n          return false;\n        } else {\n          detail::futexWaitUntil(&full_, 1, when);\n        }\n      }\n    } // inner while loop\n  } // outer while loop\n}\n\ntemplate <\n    typename T,\n    typename PriorityQueue,\n    typename Mutex,\n    template <typename> class Atom>\ntemplate <typename Clock, typename Duration>\ninline bool\nFlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_pop_impl(\n    T& val, const std::chrono::time_point<Clock, Duration>& when) {\n  while (true) {\n    bool res;\n    bool wake;\n\n    auto fn = [&] {\n      res = !pq_.empty();\n      if (res) {\n        val = pq_.top();\n        pq_.pop();\n        wake = futexSignal(full_);\n      } else {\n        setFutex(empty_, 1);\n      }\n    };\n    this->requestFC(fn);\n\n    if (res) {\n      if (wake) {\n        detail::futexWake(&full_);\n      }\n      return true;\n    }\n    while (isTrue(empty_)) {\n      if (when == std::chrono::time_point<Clock>::max()) {\n        detail::futexWait(&empty_, 1);\n      } else {\n        if (Clock::now() > when) {\n          return false;\n        } else {\n          detail::futexWaitUntil(&empty_, 1, when);\n        }\n      }\n    } // inner while loop\n  } // outer while loop\n}\n\ntemplate <\n    typename T,\n    typename PriorityQueue,\n    typename Mutex,\n    template <typename> class Atom>\ntemplate <typename Clock, typename Duration>\ninline bool\nFlatCombiningPriorityQueue<T, PriorityQueue, Mutex, Atom>::try_peek_impl(\n    T& val, const std::chrono::time_point<Clock, Duration>& when) {\n  while (true) {\n    bool res;\n\n    auto fn = [&] {\n      res = !pq_.empty();\n      if (res) {\n        val = pq_.top();\n      } else {\n        setFutex(empty_, 1);\n      }\n    };\n    this->requestFC(fn);\n\n    if (res) {\n      return true;\n    }\n    while (isTrue(empty_)) {\n      if (when == std::chrono::time_point<Clock>::max()) {\n        detail::futexWait(&empty_, 1);\n      } else {\n        if (Clock::now() > when) {\n          return false;\n        } else {\n          detail::futexWaitUntil(&empty_, 1, when);\n        }\n      }\n    } // inner while loop\n  } // outer while loop\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/container/LockFreeRingBuffer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstring>\n#include <memory>\n#include <type_traits>\n#include <utility>\n\n#include <boost/operators.hpp>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/detail/TurnSequencer.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/SanitizeThread.h>\n\nnamespace folly {\nnamespace detail {\n\ntemplate <\n    typename T,\n    template <typename> class Atom,\n    template <typename> class Storage>\nclass RingBufferSlot;\ntemplate <typename T>\nclass RingBufferTrivialStorage;\ntemplate <typename T>\nclass RingBufferBrokenStorage;\n\n} // namespace detail\n\n/// LockFreeRingBuffer<T> is a fixed-size, concurrent ring buffer with the\n/// following semantics:\n///\n///  1. Writers cannot block on other writers UNLESS they are <capacity> writes\n///     apart from each other (writing to the same slot after a wrap-around)\n///  2. Writers cannot block on readers\n///  3. Readers can wait for writes that haven't occurred yet\n///  4. Readers can detect if they are lagging behind\n///\n/// In this sense, reads from this buffer are best-effort but writes\n/// are guaranteed.\n///\n/// Another way to think about this is as an unbounded stream of writes. The\n/// buffer contains the last <capacity> writes but readers can attempt to read\n/// any part of the stream, even outside this window. The read API takes a\n/// Cursor that can point anywhere in this stream of writes. Reads from the\n/// \"future\" can optionally block but reads from the \"past\" will always fail.\n///\n\ntemplate <\n    typename T,\n    template <typename> class Atom = std::atomic,\n    template <typename> class Storage = detail::RingBufferTrivialStorage>\nclass LockFreeRingBuffer {\n  static_assert(\n      std::is_nothrow_default_constructible<T>::value,\n      \"Element type must be nothrow default constructible\");\n\n public:\n  /// Opaque pointer to a past or future write.\n  /// Can be moved relative to its current location but not in absolute terms.\n  struct Cursor : boost::totally_ordered<Cursor> {\n    explicit Cursor(uint64_t initialTicket) noexcept : ticket(initialTicket) {}\n\n    /// Returns true if this cursor now points to a different\n    /// write, false otherwise.\n    bool moveForward(uint64_t steps = 1) noexcept {\n      uint64_t prevTicket = ticket;\n      ticket += steps;\n      return prevTicket != ticket;\n    }\n\n    /// Returns true if this cursor now points to a previous\n    /// write, false otherwise.\n    bool moveBackward(uint64_t steps = 1) noexcept {\n      uint64_t prevTicket = ticket;\n      if (steps > ticket) {\n        ticket = 0;\n      } else {\n        ticket -= steps;\n      }\n      return prevTicket != ticket;\n    }\n\n    bool operator==(const Cursor& that) const noexcept {\n      return ticket == that.ticket;\n    }\n\n    bool operator<(const Cursor& that) const noexcept {\n      return ticket < that.ticket;\n    }\n\n   protected: // for test visibility reasons\n    uint64_t ticket;\n    friend class LockFreeRingBuffer;\n  };\n\n  explicit LockFreeRingBuffer(uint32_t capacity) noexcept\n      : capacity_(capacity), slots_(new Slot[capacity]), ticket_(0) {}\n\n  LockFreeRingBuffer(const LockFreeRingBuffer&) = delete;\n  LockFreeRingBuffer& operator=(const LockFreeRingBuffer&) = delete;\n\n  uint32_t capacity() const noexcept { return capacity_; }\n\n  /// Perform a single write of an object of type T.\n  /// Writes can block iff a previous writer has not yet completed a write\n  /// for the same slot (before the most recent wrap-around).\n  template <typename V>\n  void write(const V& value) noexcept {\n    uint64_t ticket = ticket_.fetch_add(1);\n    slots_[idx(ticket)].write(turn(ticket), value);\n  }\n\n  /// Perform a single write of an object of type T.\n  /// Writes can block iff a previous writer has not yet completed a write\n  /// for the same slot (before the most recent wrap-around).\n  /// Returns a Cursor pointing to the just-written T.\n  template <typename V>\n  Cursor writeAndGetCursor(const V& value) noexcept {\n    uint64_t ticket = ticket_.fetch_add(1);\n    slots_[idx(ticket)].write(turn(ticket), value);\n    return Cursor(ticket);\n  }\n\n  /// Read the value at the cursor.\n  /// Returns true if the read succeeded, false otherwise. If the return\n  /// value is false, dest is to be considered partially read and in an\n  /// inconsistent state. Readers are advised to discard it.\n  template <typename V>\n  bool tryRead(V& dest, const Cursor& cursor) const noexcept {\n    return slots_[idx(cursor.ticket)].tryRead(dest, turn(cursor.ticket));\n  }\n\n  /// Read the value at the cursor or block if the write has not occurred yet.\n  /// Returns true if the read succeeded, false otherwise. If the return\n  /// value is false, dest is to be considered partially read and in an\n  /// inconsistent state. Readers are advised to discard it.\n  template <typename V>\n  bool waitAndTryRead(V& dest, const Cursor& cursor) noexcept {\n    return slots_[idx(cursor.ticket)].waitAndTryRead(dest, turn(cursor.ticket));\n  }\n\n  /// Returns a Cursor pointing to the first write that has not occurred yet.\n  Cursor currentHead() const noexcept { return Cursor(ticket_.load()); }\n\n  /// Returns a Cursor pointing to the earliest readable write.\n  Cursor currentTail() const noexcept {\n    uint64_t ticket = ticket_.load();\n\n    // can't go back more steps than we've taken\n    uint64_t backStep = std::min<uint64_t>(ticket, capacity_);\n\n    return Cursor(ticket - backStep);\n  }\n\n  /// Returns the address and length of the internal buffer.\n  /// Unsafe to inspect this region at runtime. And not useful.\n  /// Useful when using LockFreeRingBuffer to store data which must be retrieved\n  /// from a core dump after a crash if the given region is added to the list of\n  /// dumped memory regions.\n  std::pair<void const*, size_t> internalBufferLocation() const {\n    return std::make_pair(\n        static_cast<void const*>(slots_.get()), capacity_ * sizeof(Slot));\n  }\n\n private:\n  using Slot = detail::RingBufferSlot<T, Atom, Storage>;\n\n  const uint32_t capacity_;\n\n  const std::unique_ptr<Slot[]> slots_;\n\n  Atom<uint64_t> ticket_;\n\n  uint32_t idx(uint64_t ticket) const noexcept { return ticket % capacity_; }\n\n  uint32_t turn(uint64_t ticket) const noexcept {\n    return (uint32_t)(ticket / capacity_);\n  }\n}; // LockFreeRingBuffer\n\nnamespace detail {\ntemplate <\n    typename T,\n    template <typename> class Atom,\n    template <typename> class Storage>\nclass RingBufferSlot {\n public:\n  explicit RingBufferSlot() noexcept {}\n\n  template <typename V>\n  void write(const uint32_t turn, const V& value) noexcept {\n    Atom<uint32_t> cutoff(0);\n    sequencer_.waitForTurn(turn * 2, cutoff, false);\n\n    // Change to an odd-numbered turn to indicate write in process\n    sequencer_.completeTurn(turn * 2);\n\n    storage_.store(value);\n    sequencer_.completeTurn(turn * 2 + 1);\n    // At (turn + 1) * 2\n  }\n\n  template <typename V>\n  bool waitAndTryRead(V& dest, uint32_t turn) noexcept {\n    uint32_t desired_turn = (turn + 1) * 2;\n    Atom<uint32_t> cutoff(0);\n    if (sequencer_.tryWaitForTurn(desired_turn, cutoff, false) !=\n        TurnSequencer<Atom>::TryWaitResult::SUCCESS) {\n      return false;\n    }\n    storage_.load(dest);\n\n    // if it's still the same turn, we read the value successfully\n    return sequencer_.isTurn(desired_turn);\n  }\n\n  template <typename V>\n  bool tryRead(V& dest, uint32_t turn) const noexcept {\n    // The write that started at turn 0 ended at turn 2\n    if (!sequencer_.isTurn((turn + 1) * 2)) {\n      return false;\n    }\n    storage_.load(dest);\n\n    // if it's still the same turn, we read the value successfully\n    return sequencer_.isTurn((turn + 1) * 2);\n  }\n\n private:\n  TurnSequencer<Atom> sequencer_;\n  Storage<T> storage_;\n};\n\ntemplate <typename T>\nclass RingBufferTrivialStorage {\n  static_assert(std::is_trivially_copyable_v<T>, \"T must trivially copyable\");\n\n  // Note: If T fits in 8 bytes, folly::AtomicStruct could be used instead.\n\n public:\n  RingBufferTrivialStorage() noexcept {\n    annotate_benign_race_sized(\n        &data_,\n        sizeof(T),\n        \"T is trivial and sequencer is checked to determine validity\",\n        __FILE__,\n        __LINE__);\n  }\n\n  void store(const T& src) {\n    // technically undefined behavior: once p1478 is accepted in a future c++,\n    // this memcpy may be replaced with atomic_store_per_byte_memcpy\n    std::memcpy(&data_, &src, sizeof(T));\n    // The sequencer protects this store with its own state_ store-release\n  }\n\n  void load(T& dest) const {\n    // the sequencer protects this load with its own state_ load-acquire\n    // technically undefined behavior: once p1478 is accepted in a future c++,\n    // this memcpy may be replaced with atomic_store_per_byte_memcpy\n    std::memcpy(&dest, &data_, sizeof(T));\n  }\n\n private:\n  // No initialization is necessary because the sequencer is checked before data\n  // is returned.\n  T data_;\n};\n\ntemplate <typename T>\nclass [[deprecated(\n    \"It is UB to race loads and stores across multiple threads. \"\n    \"Use RingBufferTrivialStorage.\")]] RingBufferBrokenStorage {\n public:\n  void store(const T& src) { data_ = src; }\n\n  void load(T& dest) const { dest = data_; }\n\n private:\n  T data_{};\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/container/RelaxedConcurrentPriorityQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <climits>\n#include <cmath>\n#include <iomanip>\n#include <iostream>\n#include <mutex>\n\n#include <folly/Random.h>\n#include <folly/SpinLock.h>\n#include <folly/ThreadLocal.h>\n#include <folly/detail/Futex.h>\n#include <folly/lang/Align.h>\n#include <folly/synchronization/Hazptr.h>\n#include <folly/synchronization/WaitOptions.h>\n#include <folly/synchronization/detail/Spin.h>\n\n/// ------ Concurrent Priority Queue Implementation ------\n// The concurrent priority queue implementation is based on the\n// Mound data structure (Mounds: Array-Based Concurrent Priority Queues\n// by Yujie Liu and Michael Spear, ICPP 2012)\n//\n/// --- Overview ---\n// This relaxed implementation extends the Mound algorithm, and provides\n// following features:\n// - Arbitrary priorities.\n// - Unbounded size.\n// - Push, pop, empty, size functions. [TODO: Non-waiting and timed wait pop]\n// - Supports blocking.\n// - Fast and Scalable.\n//\n/// --- Mound ---\n// A Mound is a heap where each element is a sorted linked list.\n// First nodes in the lists maintain the heap property. Push randomly\n// selects a leaf at the bottom level, then uses binary search to find\n// a place to insert the new node to the head of the list. Pop gets\n// the node from the head of the list at the root, then swap the\n// list down until the heap feature holds. To use Mound in our\n// implementation, we need to solve the following problems:\n// - 1. Lack of general relaxed implementations. Mound is appealing\n// for relaxed priority queue implementation because pop the whole\n// list from the root is straightforward. One thread pops the list\n// and following threads can pop from the list until its empty.\n// Those pops only trigger one swap done operation. Thus reduce\n// the latency for pop and reduce the contention for Mound.\n// The difficulty is to provide a scalable and fast mechanism\n// to let threads concurrently get elements from the list.\n// - 2. Lack of control of list length. The length for every\n// lists is critical for the performance. Mound suffers from not\n// only the extreme cases(Push with increasing priorities, Mound\n// becomes a sorted linked list; Push with decreasing priorities,\n// Mound becomes to a regular heap), but also the common case(for\n// random generated priorities, Mound degrades to the regular heap\n// after millions of push/pop operations). The difficulty is to\n// stabilize the list length without losing the accuracy and performance.\n// - 3. Does not support blocking. Blocking is an important feature.\n// Mound paper does not mention it. Designing the new algorithm for\n// efficient blocking is challenging.\n// - 4. Memory management. Mound allows optimistic reads. We need to\n// protect the node from been reclaimed.\n//\n/// --- Design ---\n// Our implementation extends Mound algorithm to support\n// efficient relaxed pop. We employ a shared buffer algorithm to\n// share the popped list. Our algorithm makes popping from shared\n// buffer as fast as fetch_and_add. We improve the performance\n// and compact the heap structure by stabilizing the size of each list.\n// The implementation exposes the template parameter to set the\n// preferred list length. Under the hood, we provide algorithms for\n// fast inserting, pruning, and merging. The blocking algorithm is\n// tricky. It allows one producer only wakes one consumer at a time.\n// It also does not block the producer. For optimistic read, we use\n// hazard pointer to protect the node from been reclaimed. We optimize the\n// check-lock-check pattern by using test-test-and-set spin lock.\n\n/// --- Template Parameters: ---\n// 1. PopBatch could be 0 or a positive integer.\n// If it is 0, only pop one node at a time.\n// This is the strict implementation. It guarantees the return\n// priority is alway the highest.  If it is > 0, we keep\n// up to that number of nodes in a shared buffer to be consumed by\n// subsequent pop operations.\n//\n// 2. ListTargetSize represents the minimal length for the list. It\n// solves the problem when inserting to Mound with\n// decreasing priority order (degrade to a heap).  Moreover,\n// it maintains the Mound structure stable after trillions of\n// operations, which causes unbalanced problem in the original\n// Mound algorithm. We set the prunning length and merging lengtyh\n// based on this parameter.\n//\n/// --- Interface ---\n//  void push(const T& val)\n//  void pop(T& val)\n//  size_t size()\n//  bool empty()\n\nnamespace folly {\n\ntemplate <\n    typename T,\n    bool MayBlock = false,\n    bool SupportsSize = false,\n    size_t PopBatch = 16,\n    size_t ListTargetSize = 25,\n    typename Mutex = folly::SpinLock,\n    template <typename> class Atom = std::atomic>\nclass RelaxedConcurrentPriorityQueue {\n  // Max height of the tree\n  static constexpr uint32_t MAX_LEVELS = 32;\n  // The default minimum value\n  static constexpr T MIN_VALUE = std::numeric_limits<T>::min();\n\n  // Align size for the shared buffer node\n  static constexpr size_t Align = 1u << 7;\n  static constexpr int LevelForForceInsert = 3;\n  static constexpr int LevelForTraverseParent = 7;\n\n  static_assert(PopBatch <= 256, \"PopBatch must be <= 256\");\n  static_assert(\n      ListTargetSize >= 1 && ListTargetSize <= 256,\n      \"TargetSize must be in the range [1, 256]\");\n\n  // The maximal length for the list\n  static constexpr size_t PruningSize = ListTargetSize * 2;\n  // When pop from Mound, tree elements near the leaf\n  // level are likely be very small (the length of the list). When\n  // swapping down after pop a list, we check the size of the\n  // children to decide whether to merge them to their parent.\n  static constexpr size_t MergingSize = ListTargetSize;\n\n  /// List Node structure\n  struct Node : public folly::hazptr_obj_base<Node, Atom> {\n    Node* next;\n    T val;\n  };\n\n  /// Mound Element (Tree node), head points to a linked list\n  struct MoundElement {\n    // Reading (head, size) without acquiring the lock\n    Atom<Node*> head;\n    Atom<size_t> size;\n    alignas(Align) Mutex lock;\n    MoundElement() { // initializer\n      head.store(nullptr, std::memory_order_relaxed);\n      size.store(0, std::memory_order_relaxed);\n    }\n  };\n\n  /// The pos strcture simplify the implementation\n  struct Position {\n    uint32_t level;\n    uint32_t index;\n  };\n\n  /// Node for shared buffer should be aligned\n  struct BufferNode {\n    alignas(Align) Atom<Node*> pnode;\n  };\n\n  /// Data members\n\n  // Mound structure -> 2D array to represent a tree\n  MoundElement* levels_[MAX_LEVELS];\n  // Record the current leaf level (root is 0)\n  Atom<uint32_t> bottom_;\n  // It is used when expanding the tree\n  Atom<uint32_t> guard_;\n\n  // Mound with shared buffer\n  // Following two members are accessed by consumers\n  std::unique_ptr<BufferNode[]> shared_buffer_;\n  alignas(Align) Atom<int> top_loc_;\n\n  /// Blocking algorithm\n  // Numbers of futexs in the array\n  static constexpr size_t NumFutex = 128;\n  // The index gap for accessing futex in the array\n  static constexpr size_t Stride = 33;\n  std::unique_ptr<folly::detail::Futex<Atom>[]> futex_array_;\n  alignas(Align) Atom<uint32_t> cticket_;\n  alignas(Align) Atom<uint32_t> pticket_;\n\n  // Two counters to calculate size of the queue\n  alignas(Align) Atom<size_t> counter_p_;\n  alignas(Align) Atom<size_t> counter_c_;\n\n public:\n  /// Constructor\n  RelaxedConcurrentPriorityQueue()\n      : cticket_(1), pticket_(1), counter_p_(0), counter_c_(0) {\n    if (MayBlock) {\n      futex_array_.reset(new folly::detail::Futex<Atom>[NumFutex]);\n    }\n\n    if (PopBatch > 0) {\n      top_loc_ = -1;\n      shared_buffer_.reset(new BufferNode[PopBatch]);\n      for (size_t i = 0; i < PopBatch; i++) {\n        shared_buffer_[i].pnode = nullptr;\n      }\n    }\n    bottom_.store(0, std::memory_order_relaxed);\n    guard_.store(0, std::memory_order_relaxed);\n    // allocate the root MoundElement and initialize Mound\n    levels_[0] = new MoundElement[1]; // default MM for MoundElement\n    for (uint32_t i = 1; i < MAX_LEVELS; i++) {\n      levels_[i] = nullptr;\n    }\n  }\n\n  ~RelaxedConcurrentPriorityQueue() {\n    if (PopBatch > 0) {\n      deleteSharedBuffer();\n    }\n    if (MayBlock) {\n      futex_array_.reset();\n    }\n    Position pos;\n    pos.level = pos.index = 0;\n    deleteAllNodes(pos);\n    // default MM for MoundElement\n    for (int i = getBottomLevel(); i >= 0; i--) {\n      delete[] levels_[i];\n    }\n  }\n\n  void push(const T& val) {\n    moundPush(val);\n    if (SupportsSize) {\n      counter_p_.fetch_add(1, std::memory_order_relaxed);\n    }\n  }\n\n  void pop(T& val) {\n    moundPop(val);\n    if (SupportsSize) {\n      counter_c_.fetch_add(1, std::memory_order_relaxed);\n    }\n  }\n\n  /// Note: size() and empty() are guaranteed to be accurate only if\n  ///       the queue is not changed concurrently.\n  /// Returns an estimate of the size of the queue\n  size_t size() {\n    DCHECK(SupportsSize);\n    size_t p = counter_p_.load(std::memory_order_acquire);\n    size_t c = counter_c_.load(std::memory_order_acquire);\n    return (p > c) ? p - c : 0;\n  }\n\n  /// Returns true only if the queue was empty during the call.\n  bool empty() { return isEmpty(); }\n\n private:\n  uint32_t getBottomLevel() { return bottom_.load(std::memory_order_acquire); }\n\n  /// This function is only called by the destructor\n  void deleteSharedBuffer() {\n    DCHECK(PopBatch > 0);\n    // delete nodes in the buffer\n    int loc = top_loc_.load(std::memory_order_relaxed);\n    while (loc >= 0) {\n      Node* n = shared_buffer_[loc--].pnode.load(std::memory_order_relaxed);\n      delete n;\n    }\n    // delete buffer\n    shared_buffer_.reset();\n  }\n\n  /// This function is only called by the destructor\n  void deleteAllNodes(const Position& pos) {\n    if (getElementSize(pos) == 0) {\n      // current list is empty, do not need to check\n      // its children again.\n      return;\n    }\n\n    Node* curList = getList(pos);\n    setTreeNode(pos, nullptr);\n    while (curList != nullptr) { // reclaim nodes\n      Node* n = curList;\n      curList = curList->next;\n      delete n;\n    }\n\n    if (!isLeaf(pos)) {\n      deleteAllNodes(leftOf(pos));\n      deleteAllNodes(rightOf(pos));\n    }\n  }\n\n  /// Check the first node in TreeElement keeps the heap structure.\n  bool isHeap(const Position& pos) {\n    if (isLeaf(pos)) {\n      return true;\n    }\n    Position lchild = leftOf(pos);\n    Position rchild = rightOf(pos);\n    return isHeap(lchild) && isHeap(rchild) &&\n        readValue(pos) >= readValue(lchild) &&\n        readValue(pos) >= readValue(rchild);\n  }\n\n  /// Current position is leaf?\n  FOLLY_ALWAYS_INLINE bool isLeaf(const Position& pos) {\n    return pos.level == getBottomLevel();\n  }\n\n  /// Current element is the root?\n  FOLLY_ALWAYS_INLINE bool isRoot(const Position& pos) {\n    return pos.level == 0;\n  }\n\n  /// Locate the parent node\n  FOLLY_ALWAYS_INLINE Position parentOf(const Position& pos) {\n    Position res;\n    res.level = pos.level - 1;\n    res.index = pos.index / 2;\n    return res;\n  }\n\n  /// Locate the left child\n  FOLLY_ALWAYS_INLINE Position leftOf(const Position& pos) {\n    Position res;\n    res.level = pos.level + 1;\n    res.index = pos.index * 2;\n    return res;\n  }\n\n  /// Locate the right child\n  FOLLY_ALWAYS_INLINE Position rightOf(const Position& pos) {\n    Position res;\n    res.level = pos.level + 1;\n    res.index = pos.index * 2 + 1;\n    return res;\n  }\n\n  /// get the list size in current MoundElement\n  FOLLY_ALWAYS_INLINE size_t getElementSize(const Position& pos) {\n    return levels_[pos.level][pos.index].size.load(std::memory_order_relaxed);\n  }\n\n  /// Set the size of current MoundElement\n  FOLLY_ALWAYS_INLINE void setElementSize(\n      const Position& pos, const uint32_t& v) {\n    levels_[pos.level][pos.index].size.store(v, std::memory_order_relaxed);\n  }\n\n  /// Extend the tree level\n  void grow(uint32_t btm) {\n    while (true) {\n      if (guard_.fetch_add(1, std::memory_order_acq_rel) == 0) {\n        break;\n      }\n      // someone already expanded the tree\n      if (btm != getBottomLevel()) {\n        return;\n      }\n      std::this_thread::yield();\n    }\n    // double check the bottom has not changed yet\n    if (btm != getBottomLevel()) {\n      guard_.store(0, std::memory_order_release);\n      return;\n    }\n    // create and initialize the new level\n    uint32_t tmp_btm = getBottomLevel();\n    uint32_t size = 1 << (tmp_btm + 1);\n    MoundElement* new_level = new MoundElement[size]; // MM\n    levels_[tmp_btm + 1] = new_level;\n    bottom_.store(tmp_btm + 1, std::memory_order_release);\n    guard_.store(0, std::memory_order_release);\n  }\n\n  /// TODO: optimization\n  // This function is important, it selects a position to insert the\n  // node, there are two execution paths when this function returns.\n  // 1. It returns a position with head node has lower priority than the target.\n  // Thus it could be potentially used as the starting element to do the binary\n  // search to find the fit position.  (slow path)\n  // 2. It returns a position, which is not the best fit.\n  // But it prevents aggressively grow the Mound. (fast path)\n  Position selectPosition(\n      const T& val,\n      bool& path,\n      uint32_t& seed,\n      folly::hazptr_holder<Atom>& hptr) {\n    while (true) {\n      uint32_t b = getBottomLevel();\n      int bound = 1 << b; // number of elements in this level\n      int steps = 1 + b * b; // probe the length\n      ++seed;\n      uint32_t index = seed % bound;\n\n      for (int i = 0; i < steps; i++) {\n        int loc = (index + i) % bound;\n        Position pos;\n        pos.level = b;\n        pos.index = loc;\n        // the first round, we do the quick check\n        if (optimisticReadValue(pos, hptr) <= val) {\n          path = false;\n          seed = ++loc;\n          return pos;\n        } else if (\n            b > LevelForForceInsert && getElementSize(pos) < ListTargetSize) {\n          // [fast path] conservative implementation\n          // it makes sure every tree element should\n          // have more than the given number of nodes.\n          seed = ++loc;\n          path = true;\n          return pos;\n        }\n        if (b != getBottomLevel()) {\n          break;\n        }\n      }\n      // failed too many times grow\n      if (b == getBottomLevel()) {\n        grow(b);\n      }\n    }\n  }\n\n  /// Swap two Tree Elements (head, size)\n  void swapList(const Position& a, const Position& b) {\n    Node* tmp = getList(a);\n    setTreeNode(a, getList(b));\n    setTreeNode(b, tmp);\n\n    // need to swap the tree node meta-data\n    uint32_t sa = getElementSize(a);\n    uint32_t sb = getElementSize(b);\n    setElementSize(a, sb);\n    setElementSize(b, sa);\n  }\n\n  FOLLY_ALWAYS_INLINE void lockNode(const Position& pos) {\n    levels_[pos.level][pos.index].lock.lock();\n  }\n\n  FOLLY_ALWAYS_INLINE void unlockNode(const Position& pos) {\n    levels_[pos.level][pos.index].lock.unlock();\n  }\n\n  FOLLY_ALWAYS_INLINE bool trylockNode(const Position& pos) {\n    return levels_[pos.level][pos.index].lock.try_lock();\n  }\n\n  FOLLY_ALWAYS_INLINE T\n  optimisticReadValue(const Position& pos, folly::hazptr_holder<Atom>& hptr) {\n    Node* tmp = hptr.protect(levels_[pos.level][pos.index].head);\n    return (tmp == nullptr) ? MIN_VALUE : tmp->val;\n  }\n\n  // Get the value from the head of the list as the elementvalue\n  FOLLY_ALWAYS_INLINE T readValue(const Position& pos) {\n    Node* tmp = getList(pos);\n    return (tmp == nullptr) ? MIN_VALUE : tmp->val;\n  }\n\n  FOLLY_ALWAYS_INLINE Node* getList(const Position& pos) {\n    return levels_[pos.level][pos.index].head.load(std::memory_order_acquire);\n  }\n\n  FOLLY_ALWAYS_INLINE void setTreeNode(const Position& pos, Node* t) {\n    levels_[pos.level][pos.index].head.store(t, std::memory_order_release);\n  }\n\n  // Merge two sorted lists\n  Node* mergeList(Node* base, Node* source) {\n    if (base == nullptr) {\n      return source;\n    } else if (source == nullptr) {\n      return base;\n    }\n\n    Node *res, *p;\n    // choose the head node\n    if (base->val >= source->val) {\n      res = base;\n      base = base->next;\n      p = res;\n    } else {\n      res = source;\n      source = source->next;\n      p = res;\n    }\n\n    while (base != nullptr && source != nullptr) {\n      if (base->val >= source->val) {\n        p->next = base;\n        base = base->next;\n      } else {\n        p->next = source;\n        source = source->next;\n      }\n      p = p->next;\n    }\n    if (base == nullptr) {\n      p->next = source;\n    } else {\n      p->next = base;\n    }\n    return res;\n  }\n\n  /// Merge list t to the Element Position\n  void mergeListTo(const Position& pos, Node* t, const size_t& list_length) {\n    Node* head = getList(pos);\n    setTreeNode(pos, mergeList(head, t));\n    uint32_t ns = getElementSize(pos) + list_length;\n    setElementSize(pos, ns);\n  }\n\n  bool pruningLeaf(const Position& pos) {\n    if (getElementSize(pos) <= PruningSize) {\n      unlockNode(pos);\n      return true;\n    }\n\n    int b = getBottomLevel();\n    int leaves = 1 << b;\n    int cnodes = 0;\n    for (int i = 0; i < leaves; i++) {\n      Position tmp;\n      tmp.level = b;\n      tmp.index = i;\n      if (getElementSize(tmp) != 0) {\n        cnodes++;\n      }\n      if (cnodes > leaves * 2 / 3) {\n        break;\n      }\n    }\n\n    if (cnodes <= leaves * 2 / 3) {\n      unlockNode(pos);\n      return true;\n    }\n    return false;\n  }\n\n  /// Split the current list into two lists,\n  /// then split the tail list and merge to two children.\n  void startPruning(const Position& pos) {\n    if (isLeaf(pos) && pruningLeaf(pos)) {\n      return;\n    }\n\n    // split the list, record the tail\n    Node* pruning_head = getList(pos);\n    int steps = ListTargetSize; // keep in the original list\n    for (int i = 0; i < steps - 1; i++) {\n      pruning_head = pruning_head->next;\n    }\n    Node* t = pruning_head;\n    pruning_head = pruning_head->next;\n    t->next = nullptr;\n    int tail_length = getElementSize(pos) - steps;\n    setElementSize(pos, steps);\n\n    // split the tail list into two lists\n    // evenly merge to two children\n    if (pos.level != getBottomLevel()) {\n      // split the rest into two lists\n      int left_length = (tail_length + 1) / 2;\n      int right_length = tail_length - left_length;\n      Node *to_right, *to_left = pruning_head;\n      for (int i = 0; i < left_length - 1; i++) {\n        pruning_head = pruning_head->next;\n      }\n      to_right = pruning_head->next;\n      pruning_head->next = nullptr;\n\n      Position lchild = leftOf(pos);\n      Position rchild = rightOf(pos);\n      if (left_length != 0) {\n        lockNode(lchild);\n        mergeListTo(lchild, to_left, left_length);\n      }\n      if (right_length != 0) {\n        lockNode(rchild);\n        mergeListTo(rchild, to_right, right_length);\n      }\n      unlockNode(pos);\n      if (left_length != 0 && getElementSize(lchild) > PruningSize) {\n        startPruning(lchild);\n      } else if (left_length != 0) {\n        unlockNode(lchild);\n      }\n      if (right_length != 0 && getElementSize(rchild) > PruningSize) {\n        startPruning(rchild);\n      } else if (right_length != 0) {\n        unlockNode(rchild);\n      }\n    } else { // time to grow the Mound\n      grow(pos.level);\n      // randomly choose a child to insert\n      if (steps % 2 == 1) {\n        Position rchild = rightOf(pos);\n        lockNode(rchild);\n        mergeListTo(rchild, pruning_head, tail_length);\n        unlockNode(pos);\n        unlockNode(rchild);\n      } else {\n        Position lchild = leftOf(pos);\n        lockNode(lchild);\n        mergeListTo(lchild, pruning_head, tail_length);\n        unlockNode(pos);\n        unlockNode(lchild);\n      }\n    }\n  }\n\n  // This function insert the new node (always) at the head of the\n  // current list. It needs to lock the parent & current\n  // This function may cause the list becoming tooooo long, so we\n  // provide pruning algorithm.\n  bool regularInsert(const Position& pos, const T& val, Node* newNode) {\n    // insert to the root node\n    if (isRoot(pos)) {\n      lockNode(pos);\n      T nv = readValue(pos);\n      if (FOLLY_LIKELY(nv <= val)) {\n        newNode->next = getList(pos);\n        setTreeNode(pos, newNode);\n        uint32_t sz = getElementSize(pos);\n        setElementSize(pos, sz + 1);\n        if (FOLLY_UNLIKELY(sz > PruningSize)) {\n          startPruning(pos);\n        } else {\n          unlockNode(pos);\n        }\n        return true;\n      }\n      unlockNode(pos);\n      return false;\n    }\n\n    // insert to an inner node\n    Position parent = parentOf(pos);\n    if (!trylockNode(parent)) {\n      return false;\n    }\n    if (!trylockNode(pos)) {\n      unlockNode(parent);\n      return false;\n    }\n    T pv = readValue(parent);\n    T nv = readValue(pos);\n    if (FOLLY_LIKELY(pv > val && nv <= val)) {\n      // improve the accuracy by getting the node(R) with less priority than the\n      // new value from parent level, insert the new node to the parent list\n      // and insert R to the current list.\n      // It only happens at >= LevelForTraverseParent for reducing contention\n      uint32_t sz = getElementSize(pos);\n      if (pos.level >= LevelForTraverseParent) {\n        Node* start = getList(parent);\n        while (start->next != nullptr && start->next->val >= val) {\n          start = start->next;\n        }\n        if (start->next != nullptr) {\n          newNode->next = start->next;\n          start->next = newNode;\n          while (start->next->next != nullptr) {\n            start = start->next;\n          }\n          newNode = start->next;\n          start->next = nullptr;\n        }\n        unlockNode(parent);\n\n        Node* curList = getList(pos);\n        if (curList == nullptr) {\n          newNode->next = nullptr;\n          setTreeNode(pos, newNode);\n        } else {\n          Node* p = curList;\n          if (p->val <= newNode->val) {\n            newNode->next = curList;\n            setTreeNode(pos, newNode);\n          } else {\n            while (p->next != nullptr && p->next->val >= newNode->val) {\n              p = p->next;\n            }\n            newNode->next = p->next;\n            p->next = newNode;\n          }\n        }\n        setElementSize(pos, sz + 1);\n      } else {\n        unlockNode(parent);\n        newNode->next = getList(pos);\n        setTreeNode(pos, newNode);\n        setElementSize(pos, sz + 1);\n      }\n      if (FOLLY_UNLIKELY(sz > PruningSize)) {\n        startPruning(pos);\n      } else {\n        unlockNode(pos);\n      }\n      return true;\n    }\n    unlockNode(parent);\n    unlockNode(pos);\n    return false;\n  }\n\n  bool forceInsertToRoot(Node* newNode) {\n    Position pos;\n    pos.level = pos.index = 0;\n    std::unique_lock lck(levels_[pos.level][pos.index].lock, std::try_to_lock);\n    if (!lck.owns_lock()) {\n      return false;\n    }\n    uint32_t sz = getElementSize(pos);\n    if (sz >= ListTargetSize) {\n      return false;\n    }\n\n    Node* curList = getList(pos);\n    if (curList == nullptr) {\n      newNode->next = nullptr;\n      setTreeNode(pos, newNode);\n    } else {\n      Node* p = curList;\n      if (p->val <= newNode->val) {\n        newNode->next = curList;\n        setTreeNode(pos, newNode);\n      } else {\n        while (p->next != nullptr && p->next->val >= newNode->val) {\n          p = p->next;\n        }\n        newNode->next = p->next;\n        p->next = newNode;\n      }\n    }\n    setElementSize(pos, sz + 1);\n    return true;\n  }\n\n  // This function forces the new node inserting to the current position\n  // if the element does not hold the enough nodes. It is safe to\n  // lock just one position to insert, because it won't be the first\n  // node to sustain the heap structure.\n  bool forceInsert(const Position& pos, const T& val, Node* newNode) {\n    if (isRoot(pos)) {\n      return forceInsertToRoot(newNode);\n    }\n\n    while (true) {\n      std::unique_lock lck(\n          levels_[pos.level][pos.index].lock, std::try_to_lock);\n      if (!lck.owns_lock()) {\n        if (getElementSize(pos) < ListTargetSize && readValue(pos) >= val) {\n          continue;\n        } else {\n          return false;\n        }\n      }\n      T nv = readValue(pos);\n      uint32_t sz = getElementSize(pos);\n      // do not allow the new node to be the first one\n      // do not allow the list size tooooo big\n      if (FOLLY_UNLIKELY(nv < val || sz >= ListTargetSize)) {\n        return false;\n      }\n\n      Node* p = getList(pos);\n      // find a place to insert the node\n      while (p->next != nullptr && p->next->val > val) {\n        p = p->next;\n      }\n      newNode->next = p->next;\n      p->next = newNode;\n      // do not forget to change the metadata\n      setElementSize(pos, sz + 1);\n      return true;\n    }\n  }\n\n  void binarySearchPosition(\n      Position& cur, const T& val, folly::hazptr_holder<Atom>& hptr) {\n    Position parent, mid;\n    if (cur.level == 0) {\n      return;\n    }\n    // start from the root\n    parent.level = parent.index = 0;\n\n    while (true) { // binary search\n      mid.level = (cur.level + parent.level) / 2;\n      mid.index = cur.index >> (cur.level - mid.level);\n\n      T mv = optimisticReadValue(mid, hptr);\n      if (val < mv) {\n        parent = mid;\n      } else {\n        cur = mid;\n      }\n\n      if (mid.level == 0 || // the root\n          ((parent.level + 1 == cur.level) && parent.level != 0)) {\n        return;\n      }\n    }\n  }\n\n  // The push keeps the length of each element stable\n  void moundPush(const T& val) {\n    Position cur;\n    folly::hazptr_holder<Atom> hptr = folly::make_hazard_pointer<Atom>();\n    Node* newNode = new Node;\n    newNode->val = val;\n    uint32_t seed = folly::Random::rand32() % (1 << 21);\n\n    while (true) {\n      // shell we go the fast path?\n      bool go_fast_path = false;\n      // chooice the right node to start\n      cur = selectPosition(val, go_fast_path, seed, hptr);\n      if (go_fast_path) {\n        if (FOLLY_LIKELY(forceInsert(cur, val, newNode))) {\n          if (MayBlock) {\n            blockingPushImpl();\n          }\n          return;\n        } else {\n          continue;\n        }\n      }\n\n      binarySearchPosition(cur, val, hptr);\n      if (FOLLY_LIKELY(regularInsert(cur, val, newNode))) {\n        if (MayBlock) {\n          blockingPushImpl();\n        }\n        return;\n      }\n    }\n  }\n\n  int popToSharedBuffer(const uint32_t rsize, Node* head) {\n    Position pos;\n    pos.level = pos.index = 0;\n\n    int num = std::min(rsize, (uint32_t)PopBatch);\n    for (int i = num - 1; i >= 0; i--) {\n      // wait until this block is empty\n      while (\n          shared_buffer_[i].pnode.load(std::memory_order_relaxed) != nullptr) {\n        ;\n      }\n      shared_buffer_[i].pnode.store(head, std::memory_order_relaxed);\n      head = head->next;\n    }\n    if (num > 0) {\n      top_loc_.store(num - 1, std::memory_order_release);\n    }\n    setTreeNode(pos, head);\n    return rsize - num;\n  }\n\n  void mergeDown(const Position& pos) {\n    if (isLeaf(pos)) {\n      unlockNode(pos);\n      return;\n    }\n\n    // acquire locks for L and R and compare\n    Position lchild = leftOf(pos);\n    Position rchild = rightOf(pos);\n    lockNode(lchild);\n    lockNode(rchild);\n    // read values\n    T nv = readValue(pos);\n    T lv = readValue(lchild);\n    T rv = readValue(rchild);\n    if (nv >= lv && nv >= rv) {\n      unlockNode(pos);\n      unlockNode(lchild);\n      unlockNode(rchild);\n      return;\n    }\n\n    // If two children contains nodes less than the\n    // threshold, we merge two children to the parent\n    // and do merge down on both of them.\n    size_t sum =\n        getElementSize(rchild) + getElementSize(lchild) + getElementSize(pos);\n    if (sum <= MergingSize) {\n      Node* l1 = mergeList(getList(rchild), getList(lchild));\n      setTreeNode(pos, mergeList(l1, getList(pos)));\n      setElementSize(pos, sum);\n      setTreeNode(lchild, nullptr);\n      setElementSize(lchild, 0);\n      setTreeNode(rchild, nullptr);\n      setElementSize(rchild, 0);\n      unlockNode(pos);\n      mergeDown(lchild);\n      mergeDown(rchild);\n      return;\n    }\n    // pull from right\n    if (rv >= lv && rv > nv) {\n      swapList(rchild, pos);\n      unlockNode(pos);\n      unlockNode(lchild);\n      mergeDown(rchild);\n    } else if (lv >= rv && lv > nv) {\n      // pull from left\n      swapList(lchild, pos);\n      unlockNode(pos);\n      unlockNode(rchild);\n      mergeDown(lchild);\n    }\n  }\n\n  bool deferSettingRootSize(Position& pos) {\n    if (isLeaf(pos)) {\n      setElementSize(pos, 0);\n      unlockNode(pos);\n      return true;\n    }\n\n    // acquire locks for L and R and compare\n    Position lchild = leftOf(pos);\n    Position rchild = rightOf(pos);\n    lockNode(lchild);\n    lockNode(rchild);\n    if (getElementSize(lchild) == 0 && getElementSize(rchild) == 0) {\n      setElementSize(pos, 0);\n      unlockNode(pos);\n      unlockNode(lchild);\n      unlockNode(rchild);\n      return true;\n    } else {\n      // read values\n      T lv = readValue(lchild);\n      T rv = readValue(rchild);\n      if (lv >= rv) {\n        swapList(lchild, pos);\n        setElementSize(lchild, 0);\n        unlockNode(pos);\n        unlockNode(rchild);\n        pos = lchild;\n      } else {\n        swapList(rchild, pos);\n        setElementSize(rchild, 0);\n        unlockNode(pos);\n        unlockNode(lchild);\n        pos = rchild;\n      }\n      return false;\n    }\n  }\n\n  bool moundPopMany(T& val) {\n    // pop from the root\n    Position pos;\n    pos.level = pos.index = 0;\n    // the root is nullptr, return false\n    Node* head = getList(pos);\n    if (head == nullptr) {\n      unlockNode(pos);\n      return false;\n    }\n\n    // shared buffer already filled by other threads\n    if (PopBatch > 0 && top_loc_.load(std::memory_order_acquire) >= 0) {\n      unlockNode(pos);\n      return false;\n    }\n\n    uint32_t sz = getElementSize(pos);\n    // get the one node first\n    val = head->val;\n    Node* p = head;\n    head = head->next;\n    sz--;\n\n    if (PopBatch > 0) {\n      sz = popToSharedBuffer(sz, head);\n    } else {\n      setTreeNode(pos, head);\n    }\n\n    bool done = false;\n    if (FOLLY_LIKELY(sz == 0)) {\n      done = deferSettingRootSize(pos);\n    } else {\n      setElementSize(pos, sz);\n    }\n\n    if (FOLLY_LIKELY(!done)) {\n      mergeDown(pos);\n    }\n\n    p->retire();\n    return true;\n  }\n\n  void blockingPushImpl() {\n    auto p = pticket_.fetch_add(1, std::memory_order_acq_rel);\n    auto loc = getFutexArrayLoc(p);\n    uint32_t curfutex = futex_array_[loc].load(std::memory_order_acquire);\n\n    while (true) {\n      uint32_t ready = p << 1; // get the lower 31 bits\n      // avoid the situation that push has larger ticket already set the value\n      if (FOLLY_UNLIKELY(\n              ready + 1 < curfutex ||\n              ((curfutex > ready) && (curfutex - ready > 0x40000000)))) {\n        return;\n      }\n\n      if (futex_array_[loc].compare_exchange_strong(curfutex, ready)) {\n        if (curfutex &\n            1) { // One or more consumers may be blocked on this futex\n          detail::futexWake(&futex_array_[loc]);\n        }\n        return;\n      } else {\n        curfutex = futex_array_[loc].load(std::memory_order_acquire);\n      }\n    }\n  }\n\n  // This could guarentee the Mound is empty\n  FOLLY_ALWAYS_INLINE bool isMoundEmpty() {\n    Position pos;\n    pos.level = pos.index = 0;\n    return getElementSize(pos) == 0;\n  }\n\n  // Return true if the shared buffer is empty\n  FOLLY_ALWAYS_INLINE bool isSharedBufferEmpty() {\n    return top_loc_.load(std::memory_order_acquire) < 0;\n  }\n\n  FOLLY_ALWAYS_INLINE bool isEmpty() {\n    if (PopBatch > 0) {\n      return isMoundEmpty() && isSharedBufferEmpty();\n    }\n    return isMoundEmpty();\n  }\n\n  FOLLY_ALWAYS_INLINE bool futexIsReady(const size_t& curticket) {\n    auto loc = getFutexArrayLoc(curticket);\n    auto curfutex = futex_array_[loc].load(std::memory_order_acquire);\n    uint32_t short_cticket = curticket & 0x7FFFFFFF;\n    uint32_t futex_ready = curfutex >> 1;\n    // handle unsigned 31 bits overflow\n    return futex_ready >= short_cticket ||\n        short_cticket - futex_ready > 0x40000000;\n  }\n\n  template <typename Clock, typename Duration>\n  FOLLY_NOINLINE bool trySpinBeforeBlock(\n      const size_t& curticket,\n      const std::chrono::time_point<Clock, Duration>& deadline,\n      const folly::WaitOptions& opt = wait_options()) {\n    return folly::detail::spin_pause_until(deadline, opt, [=, this] {\n             return futexIsReady(curticket);\n           }) == folly::detail::spin_result::success;\n  }\n\n  void tryBlockingPop(const size_t& curticket) {\n    auto loc = getFutexArrayLoc(curticket);\n    auto curfutex = futex_array_[loc].load(std::memory_order_acquire);\n    if (curfutex &\n        1) { /// The last round consumers are still waiting, go to sleep\n      detail::futexWait(&futex_array_[loc], curfutex);\n    }\n    if (trySpinBeforeBlock(\n            curticket,\n            std::chrono::time_point<std::chrono::steady_clock>::max())) {\n      return; /// Spin until the push ticket is ready\n    }\n    while (true) {\n      curfutex = futex_array_[loc].load(std::memory_order_acquire);\n      if (curfutex &\n          1) { /// The last round consumers are still waiting, go to sleep\n        detail::futexWait(&futex_array_[loc], curfutex);\n      } else if (!futexIsReady(curticket)) { // current ticket < pop ticket\n        uint32_t blocking_futex = curfutex + 1;\n        if (futex_array_[loc].compare_exchange_strong(\n                curfutex, blocking_futex)) {\n          detail::futexWait(&futex_array_[loc], blocking_futex);\n        }\n      } else {\n        return;\n      }\n    }\n  }\n\n  void blockingPopImpl() {\n    auto ct = cticket_.fetch_add(1, std::memory_order_acq_rel);\n    // fast path check\n    if (futexIsReady(ct)) {\n      return;\n    }\n    // Blocking\n    tryBlockingPop(ct);\n  }\n\n  bool tryPopFromMound(T& val) {\n    if (isMoundEmpty()) {\n      return false;\n    }\n    Position pos;\n    pos.level = pos.index = 0;\n\n    // lock the root\n    if (trylockNode(pos)) {\n      return moundPopMany(val);\n    }\n    return false;\n  }\n\n  FOLLY_ALWAYS_INLINE static folly::WaitOptions wait_options() { return {}; }\n\n  template <typename Clock, typename Duration>\n  FOLLY_NOINLINE bool tryWait(\n      const std::chrono::time_point<Clock, Duration>& deadline,\n      const folly::WaitOptions& opt = wait_options()) {\n    // Fast path, by quick check the status\n    switch (folly::detail::spin_pause_until(deadline, opt, [=, this] {\n      return !isEmpty();\n    })) {\n      case folly::detail::spin_result::success:\n        return true;\n      case folly::detail::spin_result::timeout:\n        return false;\n      case folly::detail::spin_result::advance:\n        break;\n    }\n\n    // Spinning strategy\n    while (true) {\n      auto res = folly::detail::spin_yield_until(deadline, [=, this] {\n        return !isEmpty();\n      });\n      if (res == folly::detail::spin_result::success) {\n        return true;\n      } else if (res == folly::detail::spin_result::timeout) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  bool tryPopFromSharedBuffer(T& val) {\n    int get_or = -1;\n    if (!isSharedBufferEmpty()) {\n      get_or = top_loc_.fetch_sub(1, std::memory_order_acq_rel);\n      if (get_or >= 0) {\n        Node* c = shared_buffer_[get_or].pnode.load(std::memory_order_relaxed);\n        shared_buffer_[get_or].pnode.store(nullptr, std::memory_order_release);\n        val = c->val;\n        c->retire();\n        return true;\n      }\n    }\n    return false;\n  }\n\n  size_t getFutexArrayLoc(size_t s) {\n    return ((s - 1) * Stride) & (NumFutex - 1);\n  }\n\n  void moundPop(T& val) {\n    if (MayBlock) {\n      blockingPopImpl();\n    }\n\n    if (PopBatch > 0) {\n      if (tryPopFromSharedBuffer(val)) {\n        return;\n      }\n    }\n\n    while (true) {\n      if (FOLLY_LIKELY(tryPopFromMound(val))) {\n        return;\n      }\n      tryWait(std::chrono::time_point<std::chrono::steady_clock>::max());\n      if (PopBatch > 0 && tryPopFromSharedBuffer(val)) {\n        return;\n      }\n    }\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/container/SingleWriterFixedHashMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/lang/Bits.h>\n\n#include <glog/logging.h>\n\nnamespace folly {\n\n/// SingleWriterFixedHashMap:\n///\n/// Minimal single-writer fixed hash map implementation that supports:\n/// - Copy construction with optional capacity expansion.\n/// - Concurrent read-only lookup.\n/// - Concurrent read-only iteration.\n///\n/// Assumes that higher level code:\n/// - Checks availability of empty slots before calling insert\n/// - Manages expansion and/or cleanup of tombstones\n/// - Ensures no concurrent mutations to the copy constructor argument\n///\n/// Notes on algorithm:\n/// - Tombstones are used to mark previously occupied slots.\n/// - A slot with a tombstone can only be reused for the same key. The\n///   reason for that is to enforce that once a key occupies a slot,\n///   that key cannot use any other slot for the lifetime of the\n///   map. This is to guarantee that when readers iterate over the map\n///   they do not encounter any key more than once.\n///\n/// Writer-only operations:\n/// - insert()\n/// - erase()\n/// - used()\n/// - available()\n///\n/// This implementation guarantees that a copy from a map with\n/// tombstones will have at least one available empty element.\n///\ntemplate <typename Key, typename Value>\nclass SingleWriterFixedHashMap {\n  static_assert(\n      std::atomic<Value>::is_always_lock_free,\n      \"This implementation depends on having fast atomic \"\n      \"data-race-free loads and stores of Value type.\");\n  static_assert(\n      std::is_trivial<Key>::value,\n      \"This implementation depends on using a single key instance \"\n      \"for all insert and erase operations. The reason is to allow \"\n      \"readers to read keys data-race-free concurrently with possible \"\n      \"concurrent insert and erase operations on the keys.\");\n\n  class Elem;\n\n  enum class State : uint8_t { EMPTY, VALID, TOMBSTONE };\n\n  size_t capacity_;\n  size_t used_{0};\n  std::atomic<size_t> size_{0};\n  std::unique_ptr<Elem[]> elem_;\n\n public:\n  class Iterator;\n\n  explicit SingleWriterFixedHashMap(size_t capacity)\n      : capacity_(folly::nextPowTwo(capacity)) {}\n\n  explicit SingleWriterFixedHashMap(\n      size_t capacity, const SingleWriterFixedHashMap& o)\n      : capacity_(folly::nextPowTwo(capacity)) {\n    if (o.empty()) {\n      return;\n    }\n    elem_ = std::make_unique<Elem[]>(capacity_);\n    if (capacity_ == o.capacity_ &&\n        (o.used_ < o.capacity_ || o.size() == o.capacity_)) {\n      std::memcpy(\n          static_cast<void*>(elem_.get()),\n          static_cast<const void*>(o.elem_.get()),\n          capacity_ * sizeof(Elem));\n      used_ = o.used_;\n      setSize(o.size());\n      return;\n    }\n    for (size_t i = 0; i < o.capacity_; ++i) {\n      Elem& e = o.elem_[i];\n      if (e.valid()) {\n        insert(e.key(), e.value());\n      }\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE Iterator begin() const {\n    return empty() ? end() : Iterator(*this);\n  }\n\n  FOLLY_ALWAYS_INLINE Iterator end() const {\n    return Iterator(*this, capacity_);\n  }\n\n  size_t capacity() const { return capacity_; }\n\n  /* not data-race-free, to be called only by the single writer */\n  size_t used() const { return used_; }\n\n  /* not-data race-free, to be called only by the single writer */\n  size_t available() const { return capacity_ - used_; }\n\n  /* data-race-free, can be called by readers */\n  FOLLY_ALWAYS_INLINE size_t size() const {\n    return size_.load(std::memory_order_acquire);\n  }\n\n  FOLLY_ALWAYS_INLINE bool empty() const { return size() == 0; }\n\n  bool insert(Key key, Value value) {\n    if (!elem_) {\n      elem_ = std::make_unique<Elem[]>(capacity_);\n    }\n    DCHECK_LT(used_, capacity_);\n    if (writer_find(key) < capacity_) {\n      return false;\n    }\n    size_t index = hash(key);\n    auto attempts = capacity_;\n    size_t mask = capacity_ - 1;\n    while (attempts--) {\n      Elem& e = elem_[index];\n      auto state = e.state();\n      if (state == State::EMPTY ||\n          (state == State::TOMBSTONE && e.key() == key)) {\n        if (state == State::EMPTY) {\n          e.setKey(key);\n          ++used_;\n          DCHECK_LE(used_, capacity_);\n        }\n        e.setValue(value);\n        e.setValid();\n        setSize(size() + 1);\n        DCHECK_LE(size(), used_);\n        return true;\n      }\n      index = (index + 1) & mask;\n    }\n    CHECK(false) << \"No available slots\";\n    folly::assume_unreachable();\n  }\n\n  void erase(Iterator& it) {\n    DCHECK_NE(it, end());\n    Elem& e = elem_[it.index_];\n    erase_internal(e);\n  }\n\n  bool erase(Key key) {\n    size_t index = writer_find(key);\n    if (index == capacity_) {\n      return false;\n    }\n    Elem& e = elem_[index];\n    erase_internal(e);\n    return true;\n  }\n\n  FOLLY_ALWAYS_INLINE Iterator find(Key key) const {\n    size_t index = reader_find(key);\n    return Iterator(*this, index);\n  }\n\n  FOLLY_ALWAYS_INLINE bool contains(Key key) const {\n    return reader_find(key) < capacity_;\n  }\n\n private:\n  FOLLY_ALWAYS_INLINE size_t hash(Key key) const {\n    size_t mask = capacity_ - 1;\n    size_t index = std::hash<Key>()(key) & mask;\n    DCHECK_LT(index, capacity_);\n    return index;\n  }\n\n  void setSize(size_t size) { size_.store(size, std::memory_order_release); }\n\n  FOLLY_ALWAYS_INLINE size_t reader_find(Key key) const {\n    return find_internal(key);\n  }\n\n  size_t writer_find(Key key) { return find_internal(key); }\n\n  FOLLY_ALWAYS_INLINE size_t find_internal(Key key) const {\n    if (!empty()) {\n      size_t index = hash(key);\n      auto attempts = capacity_;\n      size_t mask = capacity_ - 1;\n      while (attempts--) {\n        Elem& e = elem_[index];\n        auto state = e.state();\n        if (state == State::VALID && e.key() == key) {\n          return index;\n        }\n        if (state == State::EMPTY) {\n          break;\n        }\n        index = (index + 1) & mask;\n      }\n    }\n    return capacity_;\n  }\n\n  void erase_internal(Elem& e) {\n    e.erase();\n    DCHECK_GT(size(), 0);\n    setSize(size() - 1);\n  }\n\n  /// Elem\n  class Elem {\n    std::atomic<State> state_;\n    Key key_;\n    std::atomic<Value> value_;\n\n   public:\n    Elem() : state_(State::EMPTY) {}\n\n    FOLLY_ALWAYS_INLINE State state() const {\n      return state_.load(std::memory_order_acquire);\n    }\n\n    FOLLY_ALWAYS_INLINE bool valid() const { return state() == State::VALID; }\n\n    FOLLY_ALWAYS_INLINE Key key() const { return key_; }\n\n    FOLLY_ALWAYS_INLINE Value value() const {\n      return value_.load(std::memory_order_relaxed);\n    }\n\n    void setKey(Key key) { key_ = key; }\n\n    void setValue(Value value) {\n      value_.store(value, std::memory_order_relaxed);\n    }\n\n    void setValid() { state_.store(State::VALID, std::memory_order_release); }\n\n    void erase() { state_.store(State::TOMBSTONE, std::memory_order_release); }\n  }; // Elem\n\n public:\n  /// Iterator\n  class Iterator {\n    Elem* elem_;\n    size_t capacity_;\n    size_t index_;\n\n   public:\n    FOLLY_ALWAYS_INLINE Key key() const {\n      DCHECK_LT(index_, capacity_);\n      Elem& e = elem_[index_];\n      return e.key();\n    }\n\n    FOLLY_ALWAYS_INLINE Value value() const {\n      DCHECK_LT(index_, capacity_);\n      Elem& e = elem_[index_];\n      return e.value();\n    }\n\n    FOLLY_ALWAYS_INLINE Iterator& operator++() {\n      DCHECK_LT(index_, capacity_);\n      ++index_;\n      next();\n      return *this;\n    }\n\n    FOLLY_ALWAYS_INLINE bool operator==(const Iterator& o) const {\n      DCHECK(elem_ == o.elem_ || elem_ == nullptr || o.elem_ == nullptr);\n      DCHECK_EQ(capacity_, o.capacity_);\n      DCHECK_LE(index_, capacity_);\n      return index_ == o.index_;\n    }\n\n    FOLLY_ALWAYS_INLINE bool operator!=(const Iterator& o) const {\n      return !(*this == o);\n    }\n\n   private:\n    friend class SingleWriterFixedHashMap;\n\n    explicit Iterator(const SingleWriterFixedHashMap& m, size_t i = 0)\n        : elem_(i == m.capacity_ ? nullptr : m.elem_.get()),\n          capacity_(m.capacity_),\n          index_(i) {\n      if (index_ < capacity_) {\n        next();\n      }\n    }\n\n    FOLLY_ALWAYS_INLINE void next() {\n      while (index_ < capacity_ && !elem_[index_].valid()) {\n        ++index_;\n      }\n    }\n  }; // Iterator\n}; // SingleWriterFixedHashMap\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/container/atomic_grow_array.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstddef>\n#include <new>\n#include <thread>\n\n#include <folly/CPortability.h>\n#include <folly/ConstexprMath.h>\n#include <folly/Likely.h>\n#include <folly/ScopeGuard.h>\n#include <folly/container/span.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/Bits.h>\n#include <folly/lang/New.h>\n\nnamespace folly {\n\n/// atomic_grow_array_policy_default\n///\n/// A default or example policy for use with atomic_grow_array.\ntemplate <typename Item, template <typename> class Atom = std::atomic>\nstruct atomic_grow_array_policy_default {\n  template <typename V>\n  using atom = Atom<V>;\n\n  std::size_t grow(\n      std::size_t /* const curr */, std::size_t const index) const noexcept {\n    return nextPowTwo(index + 1);\n  }\n  Item make() const noexcept(noexcept(Item())) { return Item(); }\n};\n\n/// atomic_grow_array\n///\n/// A specialized data structure roughly modeling an infinite heap-allocated\n/// array. The array is of course not actually infinite in size, but indexed\n/// access at any index is always permitted. The container features both\n/// reference-stability and iterator-stability.\n///\n/// Supports fast concurrent `operator[](size_t)`, which returns a reference to\n/// the element at the given position. Indexed access is wait-free, modulo the\n/// allocator algorithm and modulo element constructors. Indexed access has and\n/// is intended to have a fast, minimal instruction sequence.\n///\n/// The array capacity may only grow and not shrink. When array capacity grows,\n/// the array size grows to fill the capacity. This means that all the elements\n/// that would be required to fill the capacity are allocated and constructed\n/// during growth. Moreover, multiple threads may race to grow the capacity, in\n/// which case only one thread wins the race - the threads losing the race then\n/// destroy all elements they created in that round of racing to grow the array\n/// capacity.\n///\n/// With the default policy featuring power-of-two exponential growth, the\n/// number of outstanding allocations is:\n///   log2(capacity)\n/// And the outstanding allocations' sizes sum to:\n///   log2(capacity) * 2 * sizeof(void*) <--- array-segment metadata\n///   capacity * 2 * sizeof(void*) <--- array-segment pointers list\n///   capacity * sizeof(value_type) <--- array-segment elements slab\n/// Modulo allocator size-classes, of which this container makes no attempt to\n/// take advantage, and value-type alignment.\n///\n/// This is a concurrent array. These are the available operations. They are all\n/// safely concurrent with each other and they all provide the required memory\n/// orderings for safe concurrent use:\n///   operator[]\n///   size\n///   empty\n///   as_view\n///   as_ptr_span\n///\n/// The container has reference and iterator stability. The references, views,\n/// and pointer-spans returned by operator[], as_view, and as_ptr_span are valid\n/// for the lifetime of the container and will never become dangling.\n///\n/// The results of empty and size are monotonic: callers in happens-before order\n/// will only ever see nondecreasing values returned by empty or by size. These\n/// values serve as lower bounds valid for the lifetime of the container.\n///\n/// Example:\n///\n///   relaxed_atomic<size_t> count{0};\n///   atomic_grow_array<object> array;\n///\n///   object& next() { return array[count++]; }\n///\n///   template <typename func>\n///   void for_each(func f) { for (auto p : array.as_ptr_span(count)) f(*p); }\ntemplate <\n    typename Item,\n    typename Policy = atomic_grow_array_policy_default<Item>>\nclass atomic_grow_array : private Policy {\n public:\n  using size_type = std::size_t;\n  using value_type = Item;\n\n  using pointer_span = span<value_type* const>;\n  using const_pointer_span = span<value_type const* const>;\n\n  class iterator;\n  class const_iterator;\n\n private:\n  static constexpr bool is_nothrow_grow_v =\n      noexcept(FOLLY_DECLVAL(Policy const&).grow(0, 0)) &&\n      noexcept(FOLLY_DECLVAL(Policy const&).make()) &&\n      noexcept(::operator new(0));\n\n  struct array;\n\n  struct end_tag {};\n\n  template <bool>\n  class basic_view;\n\n  template <bool Const, typename Down>\n  class basic_iterator {\n   private:\n    template <bool, typename>\n    friend class basic_iterator;\n    template <bool>\n    friend class basic_view;\n\n    using self = basic_iterator;\n    using down = Down;\n    friend down;\n\n    template <typename T>\n    using maybe_add_const_t = conditional_t<Const, T const, T>;\n\n    array const* array_{};\n    size_type index_{};\n\n    basic_iterator(array const* const a, size_type const i) noexcept\n        : array_{a}, index_{i} {}\n    basic_iterator(array const* const a, end_tag) noexcept\n        : array_{a}, index_{a ? a->size : 0} {}\n\n    template <\n        bool ThatC,\n        typename ThatDown,\n        bool ThisC = Const,\n        typename = std::enable_if_t<!ThatC && ThisC>>\n    explicit basic_iterator(basic_iterator<ThatC, ThatDown> that) noexcept\n        : array_{that.array_}, index_{that.index_} {}\n\n    down& as_down() noexcept { return static_cast<down&>(*this); }\n    down const& as_down() const noexcept {\n      return static_cast<down const&>(*this);\n    }\n\n   public:\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = atomic_grow_array::value_type;\n    using difference_type = std::ptrdiff_t;\n    using pointer = maybe_add_const_t<value_type>*;\n    using reference = maybe_add_const_t<value_type>&;\n\n    basic_iterator() = default; // produces an invalid iterator\n\n    down& operator++() noexcept { return ++index_, as_down(); }\n    down operator++(int) noexcept { return down{array_, index_++}; }\n    down& operator+=(difference_type const n) noexcept {\n      return index_ += n, as_down();\n    }\n    down operator+(difference_type const n) noexcept {\n      return down{as_down()} += n;\n    }\n    down& operator-=(difference_type const n) noexcept {\n      return index_ -= n, as_down();\n    }\n    down operator-(difference_type const n) noexcept {\n      return down{as_down()} -= n;\n    }\n    friend difference_type operator-(down const lhs, down const rhs) noexcept {\n      return lhs.index_ - rhs.index_;\n    }\n    friend bool operator==(down const lhs, down const rhs) noexcept {\n      return lhs.index_ == rhs.index_;\n    }\n    friend bool operator!=(down const lhs, down const rhs) noexcept {\n      return lhs.index_ != rhs.index_;\n    }\n    friend bool operator<(down const lhs, down const rhs) noexcept {\n      return lhs.index < rhs.index_;\n    }\n    friend bool operator<=(down const lhs, down const rhs) noexcept {\n      return lhs.index <= rhs.index_;\n    }\n    friend bool operator>(down const lhs, down const rhs) noexcept {\n      return lhs.index > rhs.index_;\n    }\n    friend bool operator>=(down const lhs, down const rhs) noexcept {\n      return lhs.index >= rhs.index_;\n    }\n    reference operator*() noexcept { return *array_->list[index_]; }\n    reference operator[](difference_type const n) { return *(*this + n); }\n  };\n\n  template <bool Const>\n  class basic_view {\n   private:\n    friend atomic_grow_array;\n\n    template <typename T>\n    using maybe_add_const_t = conditional_t<Const, T const, T>;\n\n    using up = atomic_grow_array;\n\n    array const* array_{};\n\n    explicit basic_view(array const* arr) noexcept : array_{arr} {}\n\n    template <\n        bool ThatC,\n        bool ThisC = Const,\n        typename = std::enable_if_t<!ThatC && ThisC>>\n    explicit basic_view(basic_view<ThatC> that) noexcept\n        : array_{that.array_} {}\n\n   public:\n    using value_type = typename atomic_grow_array::value_type;\n    using size_type = typename atomic_grow_array::size_type;\n    using reference = maybe_add_const_t<value_type>&;\n    using const_reference = value_type const&;\n    using pointer = maybe_add_const_t<value_type>*;\n    using const_pointer = value_type const*;\n    using iterator = conditional_t<Const, up::const_iterator, up::iterator>;\n    using const_iterator = up::const_iterator;\n\n    basic_view() = default; // produces an invalid view\n\n    iterator begin() noexcept { return iterator{array_, 0}; }\n    const_iterator begin() const noexcept { return iterator{array_, 0}; }\n    const_iterator cbegin() const noexcept { return iterator{array_, 0}; }\n    iterator end() noexcept { return iterator{array_, end_tag{}}; }\n    const_iterator end() const noexcept { return iterator{array_, end_tag{}}; }\n    const_iterator cend() const noexcept { return iterator{array_, end_tag{}}; }\n\n    size_type size() const noexcept { return array_ ? array_->size : 0; }\n    bool empty() const noexcept { return !size(); }\n\n    reference operator[](size_type index) noexcept {\n      return *array_->list[index];\n    }\n    const_reference operator[](size_type index) const noexcept {\n      return *array_->list[index];\n    }\n\n    span<pointer const> as_ptr_span() noexcept {\n      using type = span<pointer const>;\n      return array_ ? type{array_->list, array_->size} : type{};\n    }\n    span<const_pointer const> as_ptr_span() const noexcept {\n      using type = span<const_pointer const>;\n      return array_ ? type{array_->list, array_->size} : type{};\n    }\n\n    span<pointer const> as_ptr_span(size_type const sz) noexcept {\n      auto ptrs = as_ptr_span();\n      return ptrs.subspan(0, std::min(ptrs.size(), sz));\n    }\n    span<const_pointer const> as_ptr_span(size_type const sz) const noexcept {\n      auto ptrs = as_ptr_span();\n      return ptrs.subspan(0, std::min(ptrs.size(), sz));\n    }\n  };\n\n public:\n  atomic_grow_array() = default;\n  explicit atomic_grow_array(Policy const& policy_) //\n      noexcept(noexcept(Policy{policy_}))\n      : Policy{policy_} {}\n  ~atomic_grow_array() { reset(); }\n\n  Policy const& policy() const noexcept {\n    return static_cast<Policy const&>(*this);\n  }\n\n  /// size\n  ///\n  /// A recent value of the true size.\n  ///\n  /// Always a lower bound of - ie, never larger than - the true size.\n  ///\n  /// Example:\n  ///\n  ///   atomic_grow_array& array = /* ... */;\n  ///   for (size_t i = 0; i < array.size(); ++i) {\n  ///     do_something_with(array[i]);\n  ///   }\n  size_t size() const noexcept { return size_.load(mo_acquire); }\n\n  /// empty\n  ///\n  /// Equivalent to size() == 0.\n  bool empty() const noexcept { return size() == 0; }\n\n  /// operator[]\n  ///\n  /// A reference to the element at the given index.\n  ///\n  /// Every index is always valid, modulo system memory size of course.\n  ///\n  /// The principal meaning is that it is not necessary to configure a capacity\n  /// limit in all deployed environments, to monitor the accuracies of those\n  /// capacity limit in all deployed environments, and to update those capacity\n  /// limits as their accuracies suffer.\n  ///\n  /// Intended for dense indexed access patterns and not for sparse indexed\n  /// access patterns. For the sparse case, at large sizes, a concurrent\n  /// unordered-map would have much less memory overhead and much less growth\n  /// compute overhead. The reason is that the memory overhead and growth\n  /// compute overhead in this data structure, with the default policy, is\n  /// proportional to the maximum index accessed, while for an unordered-map it\n  /// would be proportional to the number of accessed indices.\n  ///\n  /// Indexed access during element construction is forbidden but undiagnosed.\n  /// One likely scenario is stack overflow; another is memory exhaustion.\n  FOLLY_ALWAYS_INLINE value_type& operator[](size_type const index) //\n      noexcept(is_nothrow_grow_v) {\n    auto const x = index < size_.load(mo_acquire);\n    auto const p = FOLLY_LIKELY(x) ? array_.load(mo_acquire) : at_slow(index);\n    return *p->list[index];\n  }\n\n  /// iterator\n  ///\n  /// An iterator type used by view.\n  class iterator : private basic_iterator<false, iterator> {\n    using base = basic_iterator<false, iterator>;\n    friend base;\n    friend const_iterator;\n    template <bool>\n    friend class basic_view;\n\n   public:\n    using typename base::difference_type;\n    using typename base::iterator_category;\n    using typename base::pointer;\n    using typename base::reference;\n    using typename base::value_type;\n\n    using base::base;\n    using base::operator++;\n    using base::operator+;\n    using base::operator+=;\n    using base::operator-;\n    using base::operator-=;\n    using base::operator*;\n    using base::operator[];\n  };\n\n  /// const_iterator\n  ///\n  /// An iterator type used by view and const_view.\n  class const_iterator : private basic_iterator<true, const_iterator> {\n    using base = basic_iterator<true, const_iterator>;\n    friend base;\n    template <bool>\n    friend class basic_view;\n\n   public:\n    using typename base::difference_type;\n    using typename base::iterator_category;\n    using typename base::pointer;\n    using typename base::reference;\n    using typename base::value_type;\n\n    using base::base;\n    using base::operator++;\n    using base::operator+;\n    using base::operator+=;\n    using base::operator-;\n    using base::operator-=;\n    using base::operator*;\n    using base::operator[];\n\n    /* implicit */ const_iterator(iterator that) noexcept : base{that} {}\n  };\n\n  /// view\n  ///\n  /// Models std::ranges::range.\n  ///\n  /// Gives a view over all of the elements available at the time the view was\n  /// created. If the array capacity is later increased, the view will not cover\n  /// the new elements but it and its iterators will all remain valid.\n  class view : private basic_view<false> {\n    friend atomic_grow_array;\n    using base = basic_view<false>;\n\n   public:\n    using typename base::const_iterator;\n    using typename base::const_reference;\n    using typename base::iterator;\n    using typename base::reference;\n    using typename base::size_type;\n    using typename base::value_type;\n\n    using base::as_ptr_span;\n    using base::base;\n    using base::begin;\n    using base::cbegin;\n    using base::cend;\n    using base::empty;\n    using base::end;\n    using base::size;\n    using base::operator[];\n  };\n\n  /// const_view\n  ///\n  /// Models std::ranges::range.\n  ///\n  /// Gives a view over all of the elements available at the time the view was\n  /// created. If the array capacity is later increased, the view will not cover\n  /// the new elements but it and its iterators will all remain valid.\n  class const_view : private basic_view<true> {\n    friend atomic_grow_array;\n    using base = basic_view<true>;\n\n   public:\n    using typename base::const_iterator;\n    using typename base::const_reference;\n    using typename base::iterator;\n    using typename base::reference;\n    using typename base::size_type;\n    using typename base::value_type;\n\n    using base::as_ptr_span;\n    using base::base;\n    using base::begin;\n    using base::cbegin;\n    using base::cend;\n    using base::empty;\n    using base::end;\n    using base::size;\n    using base::operator[];\n\n    /* implicit */ const_view(view that) noexcept : base{that} {}\n  };\n\n  /// as_view\n  ///\n  /// Example:\n  ///\n  ///   atomic_grow_array& array = /* ... */;\n  ///   for (auto& item : array.as_view()) {\n  ///     do_something_with(item);\n  ///   }\n  ///\n  /// If the atomic_grow_array is grown after the call to as_view(), the\n  /// returned view will provide access only to the size and elements at the\n  /// time it was created and not to a later size or to elements created later.\n  ///\n  /// Notes:\n  /// * This exists since the choice is for atomic_grow_array not to model a\n  ///   range directly. Such a range could not be as performant and could have\n  ///   surprising behavior, such as this expression maybe evaluating to false:\n  ///     (array.begin() == array.end()) == (array.begin() == array.end())\n  /// * May be more performant than repeatedly indexing with operator[] up until\n  ///   size(), even when size() is gotten once and then cached.\n  view as_view() noexcept { return view{array_.load(mo_acquire)}; }\n  const_view as_view() const noexcept { return view{array_.load(mo_acquire)}; }\n\n  /// as_ptr_span\n  ///\n  /// Convenience wrapper for view::as_ptr_span.\n  pointer_span as_ptr_span() noexcept { return as_view().as_ptr_span(); }\n  const_pointer_span as_ptr_span() const noexcept {\n    return as_view().as_ptr_span();\n  }\n  pointer_span as_ptr_span(size_type const sz) noexcept {\n    return as_view().as_ptr_span(sz);\n  }\n  const_pointer_span as_ptr_span(size_type const sz) const noexcept {\n    return as_view().as_ptr_span(sz);\n  }\n\n private:\n  static constexpr auto mo_acquire = std::memory_order_acquire;\n  static constexpr auto mo_release = std::memory_order_release;\n  static constexpr auto mo_acq_rel = std::memory_order_acq_rel;\n\n  //  prefix of layout structure\n  //\n  //  missing alignment and suffix because there are two variable-sized array\n  //  fields\n  struct array {\n    array* next{}; // maybe null; const\n    size_type size{}; // size != 0; size > (next ? next->size : 0); const\n    value_type* list[]; // value_type* list[size]; const\n    // value_type slab[size - (next ? next->size : 0)]; non-const\n  };\n\n  FOLLY_NOINLINE array* at_slow(size_type const index) //\n      noexcept(is_nothrow_grow_v) {\n    //  uses optimistic concurrency in order to avoid the space cost of any\n    //  embedded mutex or the possible contention or deadlock from any global\n    //  mutex or global mutex slab\n    //\n    //  deadlock from a global mutex or global mutex slab may occur if the\n    //  value_type type constructor may also access the global mutex or global\n    //  mutex slab, whether directly or indirectly\n    array* p = array_.load(mo_acquire);\n    array* q = nullptr;\n    size_type const size = policy().grow(p ? p->size : 0, index);\n    assert(index < size);\n    do {\n      if (p && index < p->size) {\n        return p;\n      }\n      //  the race begins here\n      q = new_array(size, p);\n      if (!q) {\n        //  the race is lost early\n        continue;\n      }\n      //  this c/x only need success-release/failure-acquire, but c++\n      //  implementations have trouble with that; so, success-acq-rel\n      //  see: folly::atomic_compare_exchange_strong_explicit\n      if (array_.compare_exchange_strong(p, q, mo_acq_rel, mo_acquire)) {\n        //  the race is won\n        size_.store(size, mo_release);\n        return q;\n      }\n      //  the race is lost\n      del_array(q);\n    } while (1);\n  }\n\n  static constexpr size_type array_align() {\n    return folly::constexpr_max(folly::max_align_v, alignof(value_type));\n  }\n  static size_type array_size(size_type const size, size_type const base) {\n    constexpr auto a = array_align();\n    return //\n        folly::constexpr_ceil(sizeof(array) + size * sizeof(value_type*), a) +\n        folly::constexpr_ceil((size - base) * sizeof(value_type), a);\n  }\n  static value_type* array_slab(array* const curr) {\n    return reinterpret_cast<value_type*>(folly::constexpr_ceil(\n        reinterpret_cast<uintptr_t>(&curr->list[curr->size]),\n        static_cast<uintptr_t>(array_align())));\n  }\n\n  array* new_array(size_type const size, array*& next) {\n    auto const base = next ? next->size : 0;\n    assert(size > base);\n    array* curr = static_cast<array*>(\n        operator_new(array_size(size, base), std::align_val_t{array_align()}));\n    auto rollback =\n        folly::makeGuard(std::bind(&atomic_grow_array::del_array, this, curr));\n    curr->size = size;\n    curr->next = next;\n    auto const slab = array_slab(curr);\n    //  copy pointers to all pre-existing elements; cannot throw\n    for (size_type i = 0; i < base; ++i) {\n      curr->list[i] = next->list[i];\n    }\n    //  zero-initialize to new elements; cannot throw\n    for (size_type i = base; i < size; ++i) {\n      curr->list[i] = nullptr;\n    }\n    //  initialize new elements and the pointers to them; may throw\n    for (size_type i = base; i < size; ++i) {\n      //  detect race losses early\n      //  just need release, but acquire for consistency with c/x in at_slow\n      if (auto const p = array_.load(std::memory_order_acquire); p != next) {\n        next = p;\n        return nullptr;\n      }\n      //  no race loss yet\n      curr->list[i] = ::new (&slab[i - base]) value_type(policy().make());\n    }\n    rollback.dismiss();\n    return curr;\n  }\n\n  void del_array(array* const curr) {\n    assert(curr);\n    auto size = curr->size;\n    auto const next = curr->next;\n    auto const base = next ? next->size : 0;\n    assert(size > base);\n    //  skip past zero-initialized pointers at the end since their corresponding\n    //  elements were never created - this situation arises when initialization\n    //  of an element fails within new_array and the rollback calls del_array\n    while (size > base && !curr->list[size - 1]) {\n      --size;\n    }\n    //  destroy elements owned by this array only, and not elements owned by any\n    //  other arrays - those elements will be destroyed by del_array calls on\n    //  their owning arrays\n    for (size_type i = 0; i < size - base; ++i) {\n      curr->list[size - 1 - i]->~value_type();\n    }\n    operator_delete(\n        static_cast<void*>(curr),\n        array_size(curr->size, base),\n        std::align_val_t{array_align()});\n  }\n\n  void reset() {\n    auto curr = array_.load(mo_acquire);\n    while (curr) {\n      auto const next = curr->next;\n      del_array(curr);\n      curr = next;\n    }\n  }\n\n  typename Policy::template atom<size_type> size_{0};\n  typename Policy::template atom<array*> array_{nullptr};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/container/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\nload(\"../../../defs.bzl\", \"folly_xplat_cxx_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_unittest(\n    name = \"atomic_grow_array_test\",\n    srcs = [\"atomic_grow_array_test.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/concurrency/container:atomic_grow_array\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\n# !!!! fbcode/folly/concurrency/container/test/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"relaxed_concurrent_priority_queue_test\",\n    srcs = [\"RelaxedConcurrentPriorityQueueTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:random\",\n        \"//folly:spin_lock\",\n        \"//folly/concurrency/container:flat_combining_priority_queue\",\n        \"//folly/concurrency/container:relaxed_concurrent_priority_queue\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:deterministic_schedule\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_thread\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"lock_free_ring_buffer_test\",\n    srcs = [\"LockFreeRingBufferTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/concurrency/container:lock_free_ring_buffer\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:deterministic_schedule\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"flat_combining_priority_queue_test\",\n    srcs = [\"FlatCombiningPriorityQueueTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly/concurrency/container:flat_combining_priority_queue\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"single_writer_fixed_hash_map_test\",\n    srcs = [\"SingleWriterFixedHashMapTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/concurrency/container:single_writer_fixed_hash_map\",\n        \"//folly/container:array\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_thread\"),\n    ],\n)\n"
  },
  {
    "path": "folly/concurrency/container/test/FlatCombiningPriorityQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/container/FlatCombiningPriorityQueue.h>\n\n#include <condition_variable>\n#include <iomanip>\n#include <mutex>\n#include <queue>\n\n#include <folly/Benchmark.h>\n#include <folly/portability/GTest.h>\n\n#include <glog/logging.h>\n\nDEFINE_bool(bench, false, \"run benchmark\");\nDEFINE_int32(reps, 10, \"number of reps\");\nDEFINE_int32(ops, 100000, \"number of operations per rep\");\nDEFINE_int32(size, 64, \"initial size of the priority queue\");\nDEFINE_int32(work, 1000, \"amount of unrelated work per operation\");\n\nvoid doWork(int work) {\n  uint64_t a = 0;\n  for (int i = work; i > 0; --i) {\n    a += i;\n  }\n  folly::doNotOptimizeAway(a);\n}\n\n/// Baseline implementation represents a conventional single-lock\n/// implementation that supports cond var blocking.\ntemplate <\n    typename T,\n    typename PriorityQueue = std::priority_queue<T>,\n    typename Mutex = std::mutex>\nclass BaselinePQ {\n public:\n  template <\n      typename... PQArgs,\n      typename = decltype(PriorityQueue(std::declval<PQArgs>()...))>\n  explicit BaselinePQ(size_t maxSize = 0, PQArgs... args)\n      : maxSize_(maxSize), pq_(std::forward<PQArgs>(args)...) {}\n\n  bool empty() const {\n    std::lock_guard g(m_);\n    return pq_.empty();\n  }\n\n  size_t size() const {\n    std::lock_guard g(m_);\n    return pq_.size();\n  }\n\n  bool try_push(const T& val) {\n    std::lock_guard g(m_);\n    if (maxSize_ > 0 && pq_.size() == maxSize_) {\n      return false;\n    }\n    DCHECK(maxSize_ == 0 || pq_.size() < maxSize_);\n    try {\n      pq_.push(val);\n      notempty_.notify_one();\n      return true;\n    } catch (const std::bad_alloc&) {\n      return false;\n    }\n  }\n\n  bool try_pop(T& val) {\n    std::lock_guard g(m_);\n    if (!pq_.empty()) {\n      val = pq_.top();\n      pq_.pop();\n      notfull_.notify_one();\n      return true;\n    }\n    return false;\n  }\n\n  bool try_peek(T& val) {\n    std::lock_guard g(m_);\n    if (!pq_.empty()) {\n      val = pq_.top();\n      return true;\n    }\n    return false;\n  }\n\n private:\n  Mutex m_;\n  size_t maxSize_;\n  PriorityQueue pq_;\n  std::condition_variable notempty_;\n  std::condition_variable notfull_;\n};\n\nusing FCPQ = folly::FlatCombiningPriorityQueue<int>;\nusing Baseline = BaselinePQ<int>;\n\n#if FOLLY_SANITIZE_THREAD\nstatic std::vector<int> nthr = {1, 2, 3, 4, 6, 8, 12, 16};\n#else\nstatic std::vector<int> nthr = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64};\n#endif\nstatic uint32_t nthreads;\n\ntemplate <typename PriorityQueue, typename Func>\nstatic uint64_t run_once(PriorityQueue& pq, const Func& fn) {\n  int ops = FLAGS_ops;\n  int size = FLAGS_size;\n  std::atomic<bool> start{false};\n  std::atomic<uint32_t> started{0};\n\n  for (int i = 0; i < size; ++i) {\n    CHECK(pq.try_push(i * (ops / size)));\n  }\n\n  std::vector<std::thread> threads(nthreads);\n  for (uint32_t tid = 0; tid < nthreads; ++tid) {\n    threads[tid] = std::thread([&, tid] {\n      started.fetch_add(1);\n      while (!start.load()) {\n        /* nothing */;\n      }\n      fn(tid);\n    });\n  }\n\n  while (started.load() < nthreads) {\n    /* nothing */;\n  }\n  auto tbegin = std::chrono::steady_clock::now();\n\n  // begin time measurement\n  start.store(true);\n\n  for (auto& t : threads) {\n    t.join();\n  }\n\n  // end time measurement\n  uint64_t duration = 0;\n  auto tend = std::chrono::steady_clock::now();\n  duration = std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)\n                 .count();\n  return duration;\n}\n\nTEST(FCPriQueue, basic) {\n  FCPQ pq;\n  CHECK(pq.empty());\n  CHECK_EQ(pq.size(), 0);\n  int v;\n  CHECK(!pq.try_pop(v));\n  // try_pop() returns an Optional\n  EXPECT_FALSE(bool(pq.try_pop()));\n\n  CHECK(pq.try_push(1));\n  CHECK(pq.try_push(2));\n  CHECK(!pq.empty());\n  CHECK_EQ(pq.size(), 2);\n\n  pq.peek(v);\n  CHECK_EQ(v, 2); // higher value has higher priority\n  CHECK(pq.try_peek(v));\n  CHECK_EQ(v, 2);\n  CHECK(!pq.empty());\n  CHECK_EQ(pq.size(), 2);\n\n  CHECK(pq.try_pop(v));\n  CHECK_EQ(v, 2);\n  CHECK(!pq.empty());\n  CHECK_EQ(pq.size(), 1);\n\n  CHECK(pq.try_pop(v));\n  CHECK_EQ(v, 1);\n  CHECK(pq.empty());\n  CHECK_EQ(pq.size(), 0);\n\n  CHECK(pq.try_push(1));\n  CHECK(pq.try_push(2));\n\n  // check successful try_pop()\n  EXPECT_EQ(*pq.try_pop(), 2);\n  CHECK(!pq.empty());\n  CHECK_EQ(pq.size(), 1);\n\n  EXPECT_EQ(*pq.try_pop(), 1);\n  CHECK(pq.empty());\n  CHECK_EQ(pq.size(), 0);\n}\n\nTEST(FCPriQueue, bounded) {\n  FCPQ pq(1);\n  CHECK(pq.try_push(1));\n  CHECK(!pq.try_push(1));\n  CHECK_EQ(pq.size(), 1);\n  CHECK(!pq.empty());\n  int v;\n  CHECK(pq.try_pop(v));\n  CHECK_EQ(v, 1);\n  CHECK_EQ(pq.size(), 0);\n  CHECK(pq.empty());\n}\n\nTEST(FCPriQueue, timeout) {\n  FCPQ pq(1);\n  int v;\n  CHECK(!pq.try_peek(v));\n  CHECK(!pq.try_pop(v));\n  pq.push(10);\n  CHECK(!pq.try_push(20));\n\n  auto dur = std::chrono::microseconds(1000);\n  EXPECT_EQ(*pq.try_pop(), 10);\n  CHECK(pq.empty());\n  // check try_***_for\n  EXPECT_FALSE(bool(pq.try_pop_for(dur)));\n  EXPECT_FALSE(bool(pq.try_peek_for(dur)));\n  CHECK(pq.try_push_for(10, dur));\n  CHECK(!pq.try_push_for(20, dur));\n  EXPECT_EQ(*pq.try_peek_for(dur), 10);\n  EXPECT_EQ(*pq.try_pop_for(dur), 10);\n\n  CHECK(pq.empty());\n  // check try_***_until\n  EXPECT_FALSE(bool(pq.try_pop_until(std::chrono::steady_clock::now() + dur)));\n\n  EXPECT_FALSE(bool(pq.try_peek_until(std::chrono::steady_clock::now() + dur)));\n  CHECK(pq.try_push_until(10, std::chrono::steady_clock::now() + dur));\n  CHECK(!pq.try_push_until(20, std::chrono::steady_clock::now() + dur));\n  EXPECT_EQ(*pq.try_peek_until(std::chrono::steady_clock::now() + dur), 10);\n  EXPECT_EQ(*pq.try_pop_until(std::chrono::steady_clock::now() + dur), 10);\n  CHECK(pq.empty());\n}\n\nTEST(FCPriQueue, pushPop) {\n  int ops = 1000;\n  int work = 0;\n  std::chrono::steady_clock::time_point when =\n      std::chrono::steady_clock::now() + std::chrono::hours(24);\n  for (auto n : nthr) {\n    nthreads = n;\n    FCPQ pq(10000);\n    auto fn = [&](uint32_t tid) {\n      for (int i = tid; i < ops; i += nthreads) {\n        CHECK(pq.try_push(i));\n        CHECK(pq.try_push_until(i, when));\n        pq.push(i);\n        doWork(work);\n        int v;\n        CHECK(pq.try_pop(v));\n        EXPECT_NE(pq.try_pop_until(when), folly::none);\n        pq.pop(v);\n        doWork(work);\n      }\n    };\n    run_once(pq, fn);\n  }\n}\n\nenum Exp {\n  NoFC,\n  FCNonBlock,\n  FCBlock,\n  FCTimed,\n};\n\nstatic uint64_t test(std::string name, Exp exp, uint64_t base) {\n  int ops = FLAGS_ops;\n  int work = FLAGS_work;\n\n  uint64_t min = UINTMAX_MAX;\n  uint64_t max = 0;\n  uint64_t sum = 0;\n\n  for (int r = 0; r < FLAGS_reps; ++r) {\n    uint64_t dur;\n    switch (exp) {\n      case NoFC: {\n        Baseline pq;\n        auto fn = [&](uint32_t tid) {\n          for (int i = tid; i < ops; i += nthreads) {\n            CHECK(pq.try_push(i));\n            doWork(work);\n            int v;\n            CHECK(pq.try_pop(v));\n            doWork(work);\n          }\n        };\n        dur = run_once(pq, fn);\n        break;\n      }\n      case FCNonBlock: {\n        FCPQ pq;\n        auto fn = [&](uint32_t tid) {\n          for (int i = tid; i < ops; i += nthreads) {\n            CHECK(pq.try_push(i));\n            doWork(work);\n            int v;\n            CHECK(pq.try_pop(v));\n            doWork(work);\n          }\n        };\n        dur = run_once(pq, fn);\n        break;\n      }\n      case FCBlock: {\n        FCPQ pq;\n        auto fn = [&](uint32_t tid) {\n          for (int i = tid; i < ops; i += nthreads) {\n            pq.push(i);\n            doWork(work);\n            int v;\n            pq.pop(v);\n            doWork(work);\n          }\n        };\n        dur = run_once(pq, fn);\n        break;\n      }\n      case FCTimed: {\n        FCPQ pq;\n        auto fn = [&](uint32_t tid) {\n          std::chrono::steady_clock::time_point when =\n              std::chrono::steady_clock::now() + std::chrono::hours(24);\n          for (int i = tid; i < ops; i += nthreads) {\n            CHECK(pq.try_push_until(i, when));\n            doWork(work);\n            EXPECT_NE(pq.try_pop_until(when), folly::none);\n            doWork(work);\n          }\n        };\n        dur = run_once(pq, fn);\n        break;\n      }\n      default:\n        CHECK(false);\n    }\n\n    sum += dur;\n    min = std::min(min, dur);\n    max = std::max(max, dur);\n  }\n\n  uint64_t avg = sum / FLAGS_reps;\n  uint64_t res = min;\n  std::cout << name;\n  std::cout << \"   \" << std::setw(4) << max / FLAGS_ops << \" ns\";\n  std::cout << \"   \" << std::setw(4) << avg / FLAGS_ops << \" ns\";\n  std::cout << \"   \" << std::setw(4) << res / FLAGS_ops << \" ns\";\n  if (base) {\n    std::cout << \" \" << std::setw(3) << 100 * base / res << \"%\";\n  }\n  std::cout << std::endl;\n  return res;\n}\n\nTEST(FCPriQueue, bench) {\n  if (!FLAGS_bench) {\n    return;\n  }\n\n  std::cout << \"Test_name, Max time, Avg time, Min time, % base min / min\"\n            << std::endl;\n  for (int i : nthr) {\n    nthreads = i;\n    std::cout << \"\\n------------------------------------ Number of threads = \"\n              << i << std::endl;\n    uint64_t base = test(\"baseline                    \", NoFC, 0);\n    test(\"baseline - dup              \", NoFC, base);\n    std::cout << \"---- fc -------------------------------\" << std::endl;\n    test(\"fc non-blocking             \", FCNonBlock, base);\n    test(\"fc non-blocking - dup       \", FCNonBlock, base);\n    test(\"fc timed                    \", FCTimed, base);\n    test(\"fc timed - dup              \", FCTimed, base);\n    test(\"fc blocking                 \", FCBlock, base);\n    test(\"fc blocking - dup           \", FCBlock, base);\n  }\n}\n\n/*\n$ numactl -N 1 folly/experimental/test/fc_pri_queue_test --bench\n\n[ RUN      ] FCPriQueue.bench\nTest_name, Max time, Avg time, Min time, % base min / min\n\n------------------------------------ Number of threads = 1\nbaseline                        815 ns    793 ns    789 ns\nbaseline - dup                  886 ns    827 ns    789 ns  99%\n---- fc -------------------------------\nfc non-blocking                 881 ns    819 ns    789 ns  99%\nfc non-blocking - dup           833 ns    801 ns    786 ns 100%\nfc timed                        863 ns    801 ns    781 ns 100%\nfc timed - dup                  830 ns    793 ns    782 ns 100%\nfc blocking                    1043 ns    820 ns    789 ns  99%\nfc blocking - dup               801 ns    793 ns    789 ns 100%\n\n------------------------------------ Number of threads = 2\nbaseline                        579 ns    557 ns    540 ns\nbaseline - dup                  905 ns    621 ns    538 ns 100%\n---- fc -------------------------------\nfc non-blocking                 824 ns    642 ns    568 ns  95%\nfc non-blocking - dup           737 ns    645 ns    591 ns  91%\nfc timed                        654 ns    590 ns    542 ns  99%\nfc timed - dup                  666 ns    586 ns    534 ns 101%\nfc blocking                     622 ns    599 ns    575 ns  93%\nfc blocking - dup               677 ns    618 ns    570 ns  94%\n\n------------------------------------ Number of threads = 3\nbaseline                        740 ns    717 ns    699 ns\nbaseline - dup                  742 ns    716 ns    697 ns 100%\n---- fc -------------------------------\nfc non-blocking                 730 ns    689 ns    645 ns 108%\nfc non-blocking - dup           719 ns    695 ns    639 ns 109%\nfc timed                        695 ns    650 ns    597 ns 117%\nfc timed - dup                  694 ns    654 ns    624 ns 112%\nfc blocking                     711 ns    687 ns    669 ns 104%\nfc blocking - dup               716 ns    695 ns    624 ns 112%\n\n------------------------------------ Number of threads = 4\nbaseline                        777 ns    766 ns    750 ns\nbaseline - dup                  778 ns    752 ns    731 ns 102%\n---- fc -------------------------------\nfc non-blocking                 653 ns    615 ns    589 ns 127%\nfc non-blocking - dup           611 ns    593 ns    563 ns 133%\nfc timed                        597 ns    577 ns    569 ns 131%\nfc timed - dup                  618 ns    575 ns    546 ns 137%\nfc blocking                     603 ns    590 ns    552 ns 135%\nfc blocking - dup               614 ns    590 ns    556 ns 134%\n\n------------------------------------ Number of threads = 6\nbaseline                        925 ns    900 ns    869 ns\nbaseline - dup                  930 ns    895 ns    866 ns 100%\n---- fc -------------------------------\nfc non-blocking                 568 ns    530 ns    481 ns 180%\nfc non-blocking - dup           557 ns    521 ns    488 ns 177%\nfc timed                        516 ns    496 ns    463 ns 187%\nfc timed - dup                  517 ns    500 ns    474 ns 183%\nfc blocking                     559 ns    513 ns    450 ns 193%\nfc blocking - dup               564 ns    528 ns    466 ns 186%\n\n------------------------------------ Number of threads = 8\nbaseline                        999 ns    981 ns    962 ns\nbaseline - dup                  998 ns    984 ns    965 ns  99%\n---- fc -------------------------------\nfc non-blocking                 491 ns    386 ns    317 ns 303%\nfc non-blocking - dup           433 ns    344 ns    298 ns 322%\nfc timed                        445 ns    348 ns    294 ns 327%\nfc timed - dup                  446 ns    357 ns    292 ns 328%\nfc blocking                     505 ns    389 ns    318 ns 302%\nfc blocking - dup               416 ns    333 ns    293 ns 328%\n\n------------------------------------ Number of threads = 12\nbaseline                       1092 ns   1080 ns   1072 ns\nbaseline - dup                 1085 ns   1074 ns   1065 ns 100%\n---- fc -------------------------------\nfc non-blocking                 360 ns    283 ns    258 ns 415%\nfc non-blocking - dup           340 ns    278 ns    250 ns 427%\nfc timed                        271 ns    260 ns    249 ns 429%\nfc timed - dup                  397 ns    283 ns    253 ns 423%\nfc blocking                     331 ns    279 ns    258 ns 415%\nfc blocking - dup               358 ns    280 ns    259 ns 412%\n\n------------------------------------ Number of threads = 16\nbaseline                       1120 ns   1115 ns   1103 ns\nbaseline - dup                 1122 ns   1118 ns   1114 ns  99%\n---- fc -------------------------------\nfc non-blocking                 339 ns    297 ns    246 ns 448%\nfc non-blocking - dup           353 ns    301 ns    264 ns 417%\nfc timed                        326 ns    287 ns    247 ns 445%\nfc timed - dup                  338 ns    294 ns    259 ns 425%\nfc blocking                     329 ns    288 ns    247 ns 445%\nfc blocking - dup               375 ns    308 ns    265 ns 415%\n\n------------------------------------ Number of threads = 24\nbaseline                       1073 ns   1068 ns   1064 ns\nbaseline - dup                 1075 ns   1071 ns   1069 ns  99%\n---- fc -------------------------------\nfc non-blocking                 439 ns    342 ns    278 ns 382%\nfc non-blocking - dup           389 ns    318 ns    291 ns 364%\nfc timed                        368 ns    324 ns    266 ns 398%\nfc timed - dup                  412 ns    328 ns    302 ns 352%\nfc blocking                     425 ns    345 ns    275 ns 386%\nfc blocking - dup               429 ns    340 ns    269 ns 395%\n\n------------------------------------ Number of threads = 32\nbaseline                       1001 ns    990 ns    981 ns\nbaseline - dup                 1002 ns    992 ns    983 ns  99%\n---- fc -------------------------------\nfc non-blocking                 404 ns    342 ns    273 ns 359%\nfc non-blocking - dup           395 ns    316 ns    259 ns 378%\nfc timed                        379 ns    330 ns    258 ns 380%\nfc timed - dup                  392 ns    335 ns    274 ns 357%\nfc blocking                     423 ns    340 ns    277 ns 353%\nfc blocking - dup               445 ns    359 ns    275 ns 356%\n\n------------------------------------ Number of threads = 48\nbaseline                        978 ns    975 ns    971 ns\nbaseline - dup                  977 ns    974 ns    972 ns  99%\n---- fc -------------------------------\nfc non-blocking                 424 ns    327 ns    258 ns 375%\nfc non-blocking - dup           378 ns    317 ns    256 ns 379%\nfc timed                        368 ns    311 ns    277 ns 350%\nfc timed - dup                  385 ns    310 ns    251 ns 385%\nfc blocking                     422 ns    313 ns    255 ns 380%\nfc blocking - dup               406 ns    314 ns    258 ns 376%\n\n------------------------------------ Number of threads = 64\nbaseline                        993 ns    981 ns    974 ns\nbaseline - dup                  984 ns    979 ns    975 ns  99%\n---- fc -------------------------------\nfc non-blocking                 353 ns    301 ns    266 ns 365%\nfc non-blocking - dup           339 ns    301 ns    271 ns 358%\nfc timed                        399 ns    321 ns    259 ns 375%\nfc timed - dup                  381 ns    300 ns    263 ns 369%\nfc blocking                     390 ns    301 ns    251 ns 387%\nfc blocking - dup               345 ns    289 ns    259 ns 374%\n[       OK ] FCPriQueue.bench (112424 ms)\n\n$ lscpu\nArchitecture:          x86_64\nCPU op-mode(s):        32-bit, 64-bit\nByte Order:            Little Endian\nCPU(s):                32\nOn-line CPU(s) list:   0-31\nThread(s) per core:    2\nCore(s) per socket:    8\nSocket(s):             2\nNUMA node(s):          2\nVendor ID:             GenuineIntel\nCPU family:            6\nModel:                 45\nModel name:            Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz\nStepping:              6\nCPU MHz:               2200.000\nCPU max MHz:           2200.0000\nCPU min MHz:           1200.0000\nBogoMIPS:              4399.87\nVirtualization:        VT-x\nL1d cache:             32K\nL1i cache:             32K\nL2 cache:              256K\nL3 cache:              20480K\nNUMA node0 CPU(s):     0-7,16-23\nNUMA node1 CPU(s):     8-15,24-31\nFlags:                 fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca\n                       cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht\n                       tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc\n                       arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc\n                       aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl\n                       vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1\n                       sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx\n                       lahf_lm epb tpr_shadow vnmi flexpriority ept vpid\n                       xsaveopt dtherm arat pln pts\n\n */\n"
  },
  {
    "path": "folly/concurrency/container/test/LockFreeRingBufferTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <thread>\n\n#include <folly/concurrency/container/LockFreeRingBuffer.h>\n#include <folly/portability/GTest.h>\n#include <folly/test/DeterministicSchedule.h>\n\nnamespace folly {\n\nTEST(LockFreeRingBuffer, writeReadSequentially) {\n  const int capacity = 256;\n  const int turns = 4;\n\n  LockFreeRingBuffer<int> rb(capacity);\n  LockFreeRingBuffer<int>::Cursor cur = rb.currentHead();\n  for (unsigned int turn = 0; turn < turns; turn++) {\n    for (unsigned int write = 0; write < capacity; write++) {\n      int val = turn * capacity + write;\n      rb.write(val);\n    }\n\n    for (unsigned int write = 0; write < capacity; write++) {\n      int dest = 0;\n      ASSERT_TRUE(rb.tryRead(dest, cur));\n      ASSERT_EQ(turn * capacity + write, dest);\n      cur.moveForward();\n    }\n  }\n}\n\nTEST(LockFreeRingBuffer, writeReadSequentiallyBackward) {\n  const int capacity = 256;\n  const int turns = 4;\n\n  LockFreeRingBuffer<int> rb(capacity);\n  for (unsigned int turn = 0; turn < turns; turn++) {\n    for (unsigned int write = 0; write < capacity; write++) {\n      int val = turn * capacity + write;\n      rb.write(val);\n    }\n\n    LockFreeRingBuffer<int>::Cursor cur = rb.currentHead();\n    cur.moveBackward(1); /// last write\n    for (int write = capacity - 1; write >= 0; write--) {\n      int foo = 0;\n      ASSERT_TRUE(rb.tryRead(foo, cur));\n      ASSERT_EQ(turn * capacity + write, foo);\n      cur.moveBackward();\n    }\n  }\n}\n\nTEST(LockFreeRingBuffer, readsCanBlock) {\n  // Start a reader thread, confirm that reading can block\n  std::atomic<bool> readerHasRun(false);\n  LockFreeRingBuffer<int> rb(1);\n  auto cursor = rb.currentHead();\n  cursor.moveForward(3); // wait for the 4th write\n\n  const int sentinel = 0xfaceb00c;\n\n  auto reader = std::thread([&]() {\n    int val = 0;\n    EXPECT_TRUE(rb.waitAndTryRead(val, cursor));\n    readerHasRun = true;\n    EXPECT_EQ(sentinel, val);\n  });\n\n  for (int i = 0; i < 4; i++) {\n    EXPECT_FALSE(readerHasRun);\n    int val = sentinel;\n    rb.write(val);\n  }\n  reader.join();\n  EXPECT_TRUE(readerHasRun);\n}\n\n// expose the cursor raw value via a wrapper type\ntemplate <typename T, template <typename> class Atom>\nuint64_t value(const typename LockFreeRingBuffer<T, Atom>::Cursor& rbcursor) {\n  using RBCursor = typename LockFreeRingBuffer<T, Atom>::Cursor;\n\n  struct ExposedCursor : RBCursor {\n    ExposedCursor(const RBCursor& cursor) : RBCursor(cursor) {}\n    uint64_t value() { return this->ticket; }\n  };\n  return ExposedCursor(rbcursor).value();\n}\n\ntemplate <template <typename> class Atom>\nvoid runReader(\n    LockFreeRingBuffer<int, Atom>& rb, std::atomic<int32_t>& writes) {\n  int32_t idx;\n  while ((idx = writes--) > 0) {\n    rb.write(idx);\n  }\n}\n\ntemplate <template <typename> class Atom>\nvoid runWritesNeverFail(int capacity, int writes, int writers) {\n  using folly::test::DeterministicSchedule;\n\n  DeterministicSchedule sched(DeterministicSchedule::uniform(0));\n  LockFreeRingBuffer<int, Atom> rb(capacity);\n\n  std::atomic<int32_t> writes_remaining(writes);\n  std::vector<std::thread> threads(writers);\n\n  for (int i = 0; i < writers; i++) {\n    threads[i] = DeterministicSchedule::thread(\n        std::bind(runReader<Atom>, std::ref(rb), std::ref(writes_remaining)));\n  }\n\n  for (auto& thread : threads) {\n    DeterministicSchedule::join(thread);\n  }\n\n  EXPECT_EQ(writes, (value<int, Atom>)(rb.currentHead()));\n}\n\nTEST(LockFreeRingBuffer, writesNeverFail) {\n  using folly::detail::EmulatedFutexAtomic;\n  using folly::test::DeterministicAtomic;\n\n  runWritesNeverFail<DeterministicAtomic>(1, 100, 4);\n  runWritesNeverFail<DeterministicAtomic>(10, 100, 4);\n  runWritesNeverFail<DeterministicAtomic>(100, 1000, 8);\n  runWritesNeverFail<DeterministicAtomic>(1000, 10000, 16);\n\n  runWritesNeverFail<std::atomic>(1, 100, 4);\n  runWritesNeverFail<std::atomic>(10, 100, 4);\n  runWritesNeverFail<std::atomic>(100, 1000, 8);\n  runWritesNeverFail<std::atomic>(1000, 10000, 16);\n\n  runWritesNeverFail<EmulatedFutexAtomic>(1, 100, 4);\n  runWritesNeverFail<EmulatedFutexAtomic>(10, 100, 4);\n  runWritesNeverFail<EmulatedFutexAtomic>(100, 1000, 8);\n  runWritesNeverFail<EmulatedFutexAtomic>(1000, 10000, 16);\n}\n\nTEST(LockFreeRingBuffer, readerCanDetectSkips) {\n  const int capacity = 4;\n  const int rounds = 4;\n\n  LockFreeRingBuffer<int> rb(capacity);\n  auto cursor = rb.currentHead();\n  cursor.moveForward(1);\n\n  for (int round = 0; round < rounds; round++) {\n    for (int i = 0; i < capacity; i++) {\n      int val = round * capacity + i;\n      rb.write(val);\n    }\n  }\n\n  int result = -1;\n  EXPECT_FALSE(rb.tryRead(result, cursor));\n  EXPECT_FALSE(rb.waitAndTryRead(result, cursor));\n  EXPECT_EQ(-1, result);\n\n  cursor = rb.currentTail();\n  EXPECT_TRUE(rb.tryRead(result, cursor));\n  EXPECT_EQ(capacity * (rounds - 1), result);\n}\n\nTEST(LockFreeRingBuffer, cursorFromWrites) {\n  const int capacity = 3;\n  LockFreeRingBuffer<int> rb(capacity);\n\n  // Workaround for template deduction failure\n  auto (&cursorValue)(value<int, std::atomic>);\n\n  int val = 0xfaceb00c;\n  EXPECT_EQ(0, cursorValue(rb.writeAndGetCursor(val)));\n  EXPECT_EQ(1, cursorValue(rb.writeAndGetCursor(val)));\n  EXPECT_EQ(2, cursorValue(rb.writeAndGetCursor(val)));\n\n  // Check that rb is giving out actual cursors and not just\n  // pointing to the current slot.\n  EXPECT_EQ(3, cursorValue(rb.writeAndGetCursor(val)));\n}\n\nTEST(LockFreeRingBuffer, moveBackwardsCanFail) {\n  const int capacity = 3;\n  LockFreeRingBuffer<int> rb(capacity);\n\n  // Workaround for template deduction failure\n  auto (&cursorValue)(value<int, std::atomic>);\n\n  int val = 0xfaceb00c;\n  rb.write(val);\n  rb.write(val);\n\n  auto cursor = rb.currentHead(); // points to 2\n  EXPECT_EQ(2, cursorValue(cursor));\n  EXPECT_TRUE(cursor.moveBackward());\n  EXPECT_TRUE(cursor.moveBackward()); // now at 0\n  EXPECT_FALSE(cursor.moveBackward()); // moving back does nothing\n}\n\nnamespace {\n\nstruct S {\n  int x;\n  float y;\n  char c;\n};\n\n} // namespace\n\nTEST(LockFreeRingBuffer, contendedReadsAndWrites) {\n  LockFreeRingBuffer<S> rb{2};\n  std::atomic<bool> done{false};\n\n  std::vector<std::thread> threads;\n  for (int i = 0; i < 8; ++i) {\n    threads.emplace_back([&] {\n      while (!done.load(std::memory_order_relaxed)) {\n        S value{10, -5.5, 100};\n        rb.write(value);\n      }\n    });\n  }\n  for (int i = 0; i < 8; ++i) {\n    threads.emplace_back([&] {\n      S value;\n      while (!done.load(std::memory_order_relaxed)) {\n        if (rb.tryRead(value, rb.currentTail())) {\n          EXPECT_EQ(10, value.x);\n          EXPECT_EQ(-5.5, value.y);\n          EXPECT_EQ(100, value.c);\n        }\n      }\n    });\n  }\n\n  /* sleep override */ std::this_thread::sleep_for(std::chrono::seconds{1});\n  done.store(true, std::memory_order_relaxed);\n  for (auto& thread : threads) {\n    thread.join();\n  }\n}\n\nTEST(LockFreeRingBuffer, cursorComparison) {\n  LockFreeRingBuffer<int> rb{2};\n  rb.write(5);\n  EXPECT_TRUE(rb.currentHead() == rb.currentHead());\n  EXPECT_FALSE(rb.currentHead() == rb.currentTail());\n\n  EXPECT_TRUE(rb.currentHead() != rb.currentTail());\n  EXPECT_FALSE(rb.currentHead() != rb.currentHead());\n\n  EXPECT_TRUE(rb.currentHead() > rb.currentTail());\n  EXPECT_FALSE(rb.currentTail() > rb.currentHead());\n\n  EXPECT_TRUE(rb.currentTail() < rb.currentHead());\n  EXPECT_FALSE(rb.currentHead() < rb.currentTail());\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/container/test/RelaxedConcurrentPriorityQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iomanip>\n#include <thread>\n\n#include <boost/thread.hpp>\n\n#include <folly/Random.h>\n#include <folly/SpinLock.h>\n#include <folly/concurrency/container/FlatCombiningPriorityQueue.h>\n#include <folly/concurrency/container/RelaxedConcurrentPriorityQueue.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n#include <folly/test/DeterministicSchedule.h>\n\n#include <glog/logging.h>\n\nusing namespace folly;\n\nDEFINE_bool(bench, false, \"run benchmark\");\nDEFINE_int32(reps, 1, \"number of reps\");\nDEFINE_int64(ops, 32, \"number of operations per rep\");\nDEFINE_int64(elems, 64, \"number of elements\");\n\nstatic std::vector<int> nthr = {1, 2, 4, 8};\n// threads number\nstatic uint32_t nthreads;\n\ntemplate <class PriorityQueue>\nvoid basicOpsTest() {\n  int res;\n  PriorityQueue pq;\n\n  EXPECT_TRUE(pq.empty());\n  EXPECT_EQ(pq.size(), 0);\n  pq.push(1);\n  pq.push(2);\n  pq.push(3);\n\n  EXPECT_FALSE(pq.empty());\n  EXPECT_EQ(pq.size(), 3);\n  pq.pop(res);\n  EXPECT_EQ(res, 3);\n  pq.pop(res);\n  EXPECT_EQ(res, 2);\n  pq.pop(res);\n  EXPECT_EQ(res, 1);\n  EXPECT_TRUE(pq.empty());\n  EXPECT_EQ(pq.size(), 0);\n\n  pq.push(3);\n  pq.push(2);\n  pq.push(1);\n\n  pq.pop(res);\n  EXPECT_EQ(res, 3);\n  pq.pop(res);\n  EXPECT_EQ(res, 2);\n  pq.pop(res);\n  EXPECT_EQ(res, 1);\n  EXPECT_TRUE(pq.empty());\n  EXPECT_EQ(pq.size(), 0);\n}\n\nTEST(CPQ, BasicOpsTest) {\n  // Spinning\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, false, true>>();\n  // Strict\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, false, true, 0>>();\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, false, true, 0, 1>>();\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, false, true, 0, 3>>();\n  // Relaxed\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, false, true, 1, 1>>();\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, false, true, 2, 1>>();\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, false, true, 3, 3>>();\n  // Blocking\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, true, true>>();\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, true, true, 0>>();\n  basicOpsTest<RelaxedConcurrentPriorityQueue<int, true, true, 2>>();\n}\n\n/// execute the function for nthreads\ntemplate <typename Func>\nstatic uint64_t run_once(const Func& fn) {\n  boost::barrier barrier_start{nthreads + 1};\n  std::vector<std::thread> threads(nthreads);\n  for (uint32_t tid = 0; tid < nthreads; ++tid) {\n    threads[tid] = std::thread([&, tid] {\n      barrier_start.wait();\n      fn(tid);\n    });\n  }\n\n  barrier_start.wait(); // start the execution\n  auto tbegin = std::chrono::steady_clock::now();\n  for (auto& t : threads) {\n    t.join();\n  }\n\n  // end time measurement\n  uint64_t duration = 0;\n  auto tend = std::chrono::steady_clock::now();\n  duration = std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)\n                 .count();\n  return duration;\n}\n\ntemplate <class PriorityQueue>\nvoid singleThreadTest() {\n  PriorityQueue pq;\n\n  folly::Random::DefaultGenerator rng;\n  rng.seed(FLAGS_elems);\n  uint64_t expect_sum = 0;\n  // random push\n  for (int i = 0; i < FLAGS_elems; i++) {\n    int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n    expect_sum += val;\n    pq.push(val);\n  }\n\n  int val = 0;\n  int counter = 0;\n  uint64_t sum = 0;\n  while (counter < FLAGS_elems) {\n    pq.pop(val);\n    sum += val;\n    counter++;\n  }\n\n  EXPECT_EQ(sum, expect_sum);\n}\n\nTEST(CPQ, SingleThrStrictImplTest) {\n  // spinning\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 0>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 0, 1>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 0, 8>>();\n  // blocking\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, true, false, 0>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, true, false, 0, 1>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, true, false, 0, 8>>();\n}\n\nTEST(CPQ, SingleThrRelaxedImplTest) {\n  // spinning\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 1>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 8>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 8, 2>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 8, 8>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 2, 128>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, false, false, 100, 8>>();\n  // blocking\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, true, false, 1>>();\n  singleThreadTest<RelaxedConcurrentPriorityQueue<int, true, false, 8>>();\n}\n\n/// concurrent pop should made the queue empty\n/// with executing the eaqual elements pop function\ntemplate <class PriorityQueue>\nvoid concurrentPopforSharedBuffer() {\n  for (int t : nthr) {\n    PriorityQueue pq;\n\n    folly::Random::DefaultGenerator rng;\n    rng.seed(FLAGS_elems);\n    uint64_t check_sum = 0;\n    // random push\n    for (int i = 0; i < FLAGS_elems; i++) {\n      int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n      pq.push(val);\n      check_sum += val;\n    }\n\n    std::atomic<uint64_t> pop_sum(0);\n    std::atomic<int> to_end(0);\n\n    nthreads = t;\n    auto fn = [&](uint32_t tid) {\n      int val = tid;\n      while (true) {\n        int index = to_end.fetch_add(1, std::memory_order_acq_rel);\n        if (index < FLAGS_elems) {\n          pq.pop(val);\n        } else {\n          break;\n        }\n        pop_sum.fetch_add(val, std::memory_order_acq_rel);\n      }\n    };\n    run_once(fn);\n    // check the sum of returned values of successful pop\n    EXPECT_EQ(pop_sum, check_sum);\n  }\n}\n\nTEST(CPQ, ConcurrentPopStrictImplTest) {\n  // spinning\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, false, false, 0>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, false, false, 0, 1>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, false, false, 0, 128>>();\n  // blocking\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, true, false, 0>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, true, false, 0, 1>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, true, false, 0, 128>>();\n}\n\nTEST(CPQ, ConcurrentPopRelaxedImplTest) {\n  // spinning\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, false, false>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, false, false, 1, 8>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, false, false, 8, 2>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, false, false, 128, 2>>();\n  // blocking\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, true, false, 1>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, true, false, 128>>();\n  concurrentPopforSharedBuffer<\n      RelaxedConcurrentPriorityQueue<int, true, false, 8, 128>>();\n}\n\n/// executing fixed number of push, counting the\n/// element number & total value.\ntemplate <class PriorityQueue>\nvoid concurrentPush() {\n  for (int t : nthr) {\n    PriorityQueue pq;\n    std::atomic<uint64_t> counter_sum(0);\n    nthreads = t;\n\n    auto fn = [&](uint32_t tid) {\n      folly::Random::DefaultGenerator rng;\n      rng.seed(tid);\n      uint64_t local_sum = 0;\n      for (int i = tid; i < FLAGS_elems; i += nthreads) {\n        int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n        local_sum += val;\n        pq.push(val);\n      }\n      counter_sum.fetch_add(local_sum, std::memory_order_acq_rel);\n    };\n    run_once(fn);\n\n    // check the total number of elements\n    uint64_t actual_sum = 0;\n    for (int i = 0; i < FLAGS_elems; i++) {\n      int res = 0;\n      pq.pop(res);\n      actual_sum += res;\n    }\n    EXPECT_EQ(actual_sum, counter_sum);\n  }\n}\n\nTEST(CPQ, ConcurrentPushStrictImplTest) {\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false, 0>>();\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false, 0, 8>>();\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false, 0, 128>>();\n}\n\nTEST(CPQ, ConcurrentPushRelaxedImplTest) {\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false>>();\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false, 1, 8>>();\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false, 2, 128>>();\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false, 128, 8>>();\n  concurrentPush<RelaxedConcurrentPriorityQueue<int, false, false, 8, 8>>();\n}\n\ntemplate <class PriorityQueue>\nvoid concurrentOps(int ops) {\n  for (int t : nthr) {\n    PriorityQueue pq;\n    std::atomic<uint64_t> counter_push(0);\n    std::atomic<uint64_t> counter_pop(0);\n    nthreads = t;\n\n    boost::barrier rb0{nthreads};\n    boost::barrier rb1{nthreads};\n    boost::barrier rb2{nthreads};\n    std::atomic<int> to_end(0);\n\n    auto fn = [&](uint32_t tid) {\n      folly::Random::DefaultGenerator rng;\n      rng.seed(tid);\n      uint64_t local_push = 0;\n      uint64_t local_pop = 0;\n      int res;\n\n      /// initialize the queue\n      for (int i = tid; i < FLAGS_elems; i += nthreads) {\n        int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n        local_push++;\n        pq.push(val);\n      }\n      rb0.wait();\n\n      /// operations\n      for (int i = 0; i < ops; i++) {\n        if (ops % 2 == 0) {\n          int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n          local_push++;\n          pq.push(val);\n        } else {\n          pq.pop(res);\n          local_pop++;\n        }\n      }\n      rb1.wait();\n      // collecting the ops info for checking purpose\n      counter_push.fetch_add(local_push, std::memory_order_seq_cst);\n      counter_pop.fetch_add(local_pop, std::memory_order_seq_cst);\n      rb2.wait();\n      /// clean up work\n      uint64_t r = counter_push.load(std::memory_order_seq_cst) -\n          counter_pop.load(std::memory_order_seq_cst);\n      while (true) {\n        uint64_t index = to_end.fetch_add(1, std::memory_order_acq_rel);\n        if (index < r) {\n          pq.pop(res);\n        } else {\n          break;\n        }\n      }\n    };\n    run_once(fn);\n    // the total push and pop ops should be the same\n  }\n}\n\ntemplate <class PriorityQueue>\nvoid concurrentSizeTest(int ops) {\n  for (int t : nthr) {\n    PriorityQueue pq;\n    nthreads = t;\n    EXPECT_TRUE(pq.empty());\n    auto fn = [&](uint32_t tid) {\n      folly::Random::DefaultGenerator rng;\n      rng.seed(tid);\n      int res;\n\n      /// initialize the queue\n      for (int i = tid; i < FLAGS_elems; i += nthreads) {\n        int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n        pq.push(val);\n      }\n\n      /// operations\n      for (int i = 0; i < ops; i++) {\n        int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n        pq.push(val);\n        pq.pop(res);\n      }\n    };\n    run_once(fn);\n    // the total push and pop ops should be the same\n    EXPECT_EQ(pq.size(), FLAGS_elems);\n  }\n}\n\nstatic std::vector<int> sizes = {0, 1024};\n\nTEST(CPQ, ConcurrentMixedStrictImplTest) {\n  for (auto size : sizes) {\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, false, false, 0>>(size);\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, false, false, 0, 1>>(\n        size);\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, false, false, 0, 32>>(\n        size);\n  }\n}\n\nTEST(CPQ, ConcurrentMixedRelaxedImplTest) {\n  for (auto size : sizes) {\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, false, false>>(size);\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, false, false, 1, 32>>(\n        size);\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, false, false, 32, 1>>(\n        size);\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, false, false, 8, 8>>(\n        size);\n    concurrentOps<RelaxedConcurrentPriorityQueue<int, true, false, 8, 8>>(size);\n  }\n}\n\nTEST(CPQ, StrictImplSizeTest) {\n  for (auto size : sizes) {\n    concurrentSizeTest<RelaxedConcurrentPriorityQueue<int, false, true, 0>>(\n        size);\n    concurrentSizeTest<RelaxedConcurrentPriorityQueue<int, true, true, 0>>(\n        size);\n  }\n}\n\nTEST(CPQ, RelaxedImplSizeTest) {\n  for (auto size : sizes) {\n    concurrentSizeTest<RelaxedConcurrentPriorityQueue<int, false, true>>(size);\n    concurrentSizeTest<RelaxedConcurrentPriorityQueue<int, true, true, 2, 8>>(\n        size);\n    concurrentSizeTest<RelaxedConcurrentPriorityQueue<int, false, true, 8, 2>>(\n        size);\n  }\n}\n\ntemplate <class PriorityQueue>\nvoid multiPusherPopper(int PushThr, int PopThr) {\n  int ops = FLAGS_ops;\n  uint32_t total_threads = PushThr + PopThr;\n\n  PriorityQueue pq;\n  std::atomic<uint64_t> sum_push(0);\n  std::atomic<uint64_t> sum_pop(0);\n\n  auto fn_popthr = [&](uint32_t tid) {\n    for (int i = tid; i < ops; i += PopThr) {\n      int val;\n      pq.pop(val);\n      sum_pop.fetch_add(val, std::memory_order_acq_rel);\n    }\n  };\n\n  auto fn_pushthr = [&](uint32_t tid) {\n    folly::Random::DefaultGenerator rng_t;\n    rng_t.seed(tid);\n    for (int i = tid; i < ops; i += PushThr) {\n      int val = folly::Random::rand32(rng_t);\n      pq.push(val);\n      sum_push.fetch_add(val, std::memory_order_acq_rel);\n    }\n  };\n  boost::barrier barrier_start{total_threads + 1};\n\n  std::vector<std::thread> threads_push(PushThr);\n  for (int tid = 0; tid < PushThr; ++tid) {\n    threads_push[tid] = std::thread([&, tid] {\n      barrier_start.wait();\n      fn_pushthr(tid);\n    });\n  }\n  std::vector<std::thread> threads_pop(PopThr);\n  for (int tid = 0; tid < PopThr; ++tid) {\n    threads_pop[tid] = std::thread([&, tid] {\n      barrier_start.wait();\n      fn_popthr(tid);\n    });\n  }\n\n  barrier_start.wait(); // start the execution\n  // begin time measurement\n  for (auto& t : threads_push) {\n    t.join();\n  }\n  for (auto& t : threads_pop) {\n    t.join();\n  }\n  EXPECT_EQ(sum_pop, sum_push);\n}\n\nTEST(CPQ, PusherPopperBlockingTest) {\n  for (auto i : nthr) {\n    for (auto j : nthr) {\n      // Original\n      multiPusherPopper<RelaxedConcurrentPriorityQueue<int, true, false, 0, 1>>(\n          i, j);\n      // Relaxed\n      multiPusherPopper<RelaxedConcurrentPriorityQueue<int, true, false, 1, 8>>(\n          i, j);\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, true, false, 8, 128>>(i, j);\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, true, false, 128, 8>>(i, j);\n      multiPusherPopper<RelaxedConcurrentPriorityQueue<int, true, false>>(i, j);\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, true, false, 16, 16>>(i, j);\n    }\n  }\n}\n\nTEST(CPQ, PusherPopperSpinningTest) {\n  for (auto i : nthr) {\n    for (auto j : nthr) {\n      // Original\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, false, false, 0, 1>>(i, j);\n      // Relaxed\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, false, false, 1, 8>>(i, j);\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, false, false, 8, 128>>(i, j);\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, false, false, 128, 8>>(i, j);\n      multiPusherPopper<RelaxedConcurrentPriorityQueue<int, false, false>>(\n          i, j);\n      multiPusherPopper<\n          RelaxedConcurrentPriorityQueue<int, false, false, 16, 16>>(i, j);\n    }\n  }\n}\n\ntemplate <class PriorityQueue>\nvoid blockingFirst() {\n  PriorityQueue pq;\n  int nPop = 16;\n\n  boost::barrier b{static_cast<unsigned int>(nPop + 1)};\n  std::atomic<int> finished{0};\n\n  std::vector<std::thread> threads_pop(nPop);\n  for (int tid = 0; tid < nPop; ++tid) {\n    threads_pop[tid] = std::thread([&] {\n      int val;\n      b.wait();\n      pq.pop(val);\n      finished.fetch_add(1, std::memory_order_acq_rel);\n    });\n  }\n\n  b.wait();\n  int c = 0;\n  // push to Mound one by one\n  // the popping threads should wake up one by one\n  do {\n    pq.push(1);\n    c++;\n    while (finished.load(std::memory_order_acquire) != c) {\n      ;\n    }\n    EXPECT_EQ(finished.load(std::memory_order_acquire), c);\n  } while (c < nPop);\n\n  for (auto& t : threads_pop) {\n    t.join();\n  }\n}\n\ntemplate <class PriorityQueue>\nvoid concurrentBlocking() {\n  uint32_t nThrs = 16;\n\n  for (int iter = 0; iter < FLAGS_reps * 10; iter++) {\n    PriorityQueue pq;\n    boost::barrier b{static_cast<unsigned int>(nThrs + nThrs + 1)};\n    std::atomic<uint32_t> finished{0};\n    std::vector<std::thread> threads_pop(nThrs);\n    for (uint32_t tid = 0; tid < nThrs; ++tid) {\n      threads_pop[tid] = std::thread([&] {\n        b.wait();\n        int val;\n        pq.pop(val);\n        finished.fetch_add(1, std::memory_order_acq_rel);\n      });\n    }\n\n    std::vector<std::thread> threads_push(nThrs);\n    for (uint32_t tid = 0; tid < nThrs; ++tid) {\n      threads_push[tid] = std::thread([&, tid] {\n        b.wait();\n        pq.push(tid);\n        while (finished.load(std::memory_order_acquire) != nThrs) {\n          ;\n        }\n        EXPECT_EQ(finished.load(std::memory_order_acquire), nThrs);\n      });\n    }\n\n    b.wait();\n    for (auto& t : threads_pop) {\n      t.join();\n    }\n    for (auto& t : threads_push) {\n      t.join();\n    }\n  }\n}\n\nTEST(CPQ, PopBlockingTest) {\n  // strict\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, true, false, 0, 1>>();\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, true, false, 0, 16>>();\n  // relaxed\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, true, false>>();\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, true, false, 8, 8>>();\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, true, false, 16, 1>>();\n  // Spinning\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, false, false>>();\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, false, false, 8, 8>>();\n  blockingFirst<RelaxedConcurrentPriorityQueue<int, false, false, 16, 1>>();\n}\n\nTEST(CPQ, MixedBlockingTest) {\n  // strict\n  concurrentBlocking<RelaxedConcurrentPriorityQueue<int, true, false, 0, 1>>();\n  concurrentBlocking<RelaxedConcurrentPriorityQueue<int, true, false, 0, 16>>();\n  // relaxed\n  concurrentBlocking<RelaxedConcurrentPriorityQueue<int, true, false>>();\n  concurrentBlocking<RelaxedConcurrentPriorityQueue<int, true, false, 8, 8>>();\n  concurrentBlocking<RelaxedConcurrentPriorityQueue<int, true, false, 16, 1>>();\n  // Spinning\n  concurrentBlocking<RelaxedConcurrentPriorityQueue<int, false, false>>();\n  concurrentBlocking<RelaxedConcurrentPriorityQueue<int, false, false, 8, 8>>();\n  concurrentBlocking<\n      RelaxedConcurrentPriorityQueue<int, false, false, 16, 1>>();\n}\n\nusing DSched = folly::test::DeterministicSchedule;\nusing folly::test::DeterministicAtomic;\nusing folly::test::DeterministicMutex;\n\ntemplate <class PriorityQueue, template <typename> class Atom = std::atomic>\nstatic void DSchedMixedTest() {\n  for (int r = 0; r < FLAGS_reps; r++) {\n    // the thread number is  32\n    int thr = 8;\n    PriorityQueue pq;\n    folly::Random::DefaultGenerator rng;\n    rng.seed(thr);\n    uint64_t pre_sum = 0;\n    uint64_t pre_size = 0;\n    for (int i = 0; i < FLAGS_elems; i++) {\n      int val = folly::Random::rand32(rng) % FLAGS_elems + 1;\n      pq.push(val);\n      pre_sum += val;\n    }\n    pre_size = FLAGS_elems;\n    Atom<uint64_t> atom_push_sum{0};\n    Atom<uint64_t> atom_pop_sum{0};\n    std::vector<std::thread> threads(thr);\n    for (int tid = 0; tid < thr; ++tid) {\n      threads[tid] = DSched::thread([&]() {\n        folly::Random::DefaultGenerator tl_rng;\n        tl_rng.seed(thr);\n        uint64_t pop_sum = 0;\n        uint64_t push_sum = 0;\n        for (int i = 0; i < FLAGS_ops; i++) {\n          int val = folly::Random::rand32(tl_rng) % FLAGS_elems + 1;\n          pq.push(val);\n          push_sum += val;\n          pq.pop(val);\n          pop_sum += val;\n        }\n        atom_push_sum.fetch_add(push_sum, std::memory_order_acq_rel);\n        atom_pop_sum.fetch_add(pop_sum, std::memory_order_acq_rel);\n      });\n    }\n\n    for (auto& t : threads) {\n      DSched::join(t);\n    }\n\n    // It checks the number of elements remain in Mound\n    while (pre_size > 0) {\n      pre_size--;\n      int val = -1;\n      pq.pop(val);\n      atom_pop_sum += val;\n    }\n    // Check the accumulation of popped and pushed priorities\n    EXPECT_EQ(atom_pop_sum, pre_sum + atom_push_sum);\n  }\n}\n\nTEST(CPQ, DSchedMixedStrictTest) {\n  DSched sched(DSched::uniform(0));\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          false,\n          false,\n          0,\n          25,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          true,\n          false,\n          0,\n          25,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          false,\n          false,\n          0,\n          1,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          true,\n          false,\n          0,\n          1,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          false,\n          false,\n          0,\n          128,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          true,\n          false,\n          0,\n          128,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n}\n\nTEST(CPQ, DSchedMixedRelaxedTest) {\n  DSched sched(DSched::uniform(0));\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          false,\n          false,\n          16,\n          25,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          true,\n          false,\n          16,\n          25,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          false,\n          false,\n          1,\n          16,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          true,\n          false,\n          1,\n          16,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          false,\n          false,\n          16,\n          1,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          true,\n          false,\n          16,\n          1,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          false,\n          false,\n          16,\n          16,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n  DSchedMixedTest<\n      RelaxedConcurrentPriorityQueue<\n          int,\n          true,\n          false,\n          16,\n          16,\n          DeterministicMutex,\n          DeterministicAtomic>,\n      DeterministicAtomic>();\n}\n\ntemplate <typename T>\nclass Queue {\n  std::queue<T> q_;\n\n public:\n  void push(const T& val) { q_.push(val); }\n  void pop(T& val) {\n    val = q_.front();\n    q_.pop();\n  }\n};\n\ntemplate <typename T>\nclass GlobalLockPQ {\n  std::priority_queue<T> q_;\n  std::mutex m_;\n\n public:\n  void push(const T& val) {\n    std::lock_guard g(m_);\n    q_.push(val);\n  }\n  void pop(T& val) {\n    while (true) {\n      std::lock_guard g(m_);\n      if (q_.empty()) {\n        continue;\n      }\n      val = q_.top();\n      q_.pop();\n      return;\n    }\n  }\n};\n\ntemplate <class PriorityQueue>\nstatic uint64_t producer_consumer_test(\n    std::string name,\n    uint32_t PushThr,\n    uint32_t PopThr,\n    uint64_t initial_size) {\n  int ops = 1 << 18;\n  int reps = 15;\n  if (name.find(\"RCPQ\") != std::string::npos) {\n    ops <<= 3;\n  }\n  uint64_t min = UINTMAX_MAX;\n  uint64_t max = 0;\n  uint64_t sum = 0;\n  uint32_t total_threads = PushThr + PopThr;\n\n  for (int r = 0; r < reps; ++r) {\n    uint64_t dur;\n    PriorityQueue pq;\n\n    folly::Random::DefaultGenerator rng;\n    rng.seed(initial_size);\n    // initialize the queue according to initial_size\n    for (uint64_t i = 0; i < initial_size; i++) {\n      int val = folly::Random::rand32(rng) % ops;\n      pq.push(val);\n    }\n\n    auto fn_popthr = [&](uint32_t tid) {\n      for (int i = tid; i < ops; i += PopThr) {\n        int val;\n        pq.pop(val);\n      }\n    };\n\n    auto fn_pushthr = [&](uint32_t tid) {\n      folly::Random::DefaultGenerator rng_t;\n      rng_t.seed(tid);\n      for (int i = tid; i < ops; i += PushThr) {\n        int val = folly::Random::rand32(rng_t) % ops;\n        pq.push(val);\n      }\n    };\n    boost::barrier barrier_start{total_threads + 1};\n    std::vector<std::thread> threads_push(PushThr);\n    for (uint32_t tid = 0; tid < PushThr; ++tid) {\n      threads_push[tid] = std::thread([&, tid] {\n        barrier_start.wait();\n        fn_pushthr(tid);\n      });\n    }\n    std::vector<std::thread> threads_pop(PopThr);\n    for (uint32_t tid = 0; tid < PopThr; ++tid) {\n      threads_pop[tid] = std::thread([&, tid] {\n        barrier_start.wait();\n        fn_popthr(tid);\n      });\n    }\n    barrier_start.wait(); // start the execution\n    // begin time measurement\n    auto tbegin = std::chrono::steady_clock::now();\n    for (auto& t : threads_push) {\n      t.join();\n    }\n    for (auto& t : threads_pop) {\n      t.join();\n    }\n    // end time measurement\n    auto tend = std::chrono::steady_clock::now();\n    dur = std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)\n              .count();\n    sum += dur;\n    min = std::min(min, dur);\n    max = std::max(max, dur);\n  }\n  uint64_t avg = sum / reps;\n  std::cout << std::setw(12) << name;\n  std::cout << \"   \" << std::setw(8) << max / ops << \" ns\";\n  std::cout << \"   \" << std::setw(8) << avg / ops << \" ns\";\n  std::cout << \"   \" << std::setw(8) << min / ops << \" ns\";\n  std::cout << std::endl;\n  return min;\n}\n\ntemplate <class PriorityQueue>\nstatic uint64_t throughtput_test(std::string name, uint64_t initial_size) {\n  int ops = 1 << 18;\n  int reps = 15;\n  uint64_t min = UINTMAX_MAX;\n  uint64_t max = 0;\n  uint64_t sum = 0;\n\n  for (int r = 0; r < reps; ++r) {\n    uint64_t dur;\n    PriorityQueue pq;\n\n    folly::Random::DefaultGenerator rng;\n    rng.seed(initial_size);\n    // initialize the queue according to initial_size\n    for (uint64_t i = 0; i < initial_size; i++) {\n      int val = folly::Random::rand32(rng) % (ops + 1);\n      pq.push(val);\n    }\n\n    auto fn = [&](uint32_t tid) {\n      folly::Random::DefaultGenerator rng_tl;\n      rng_tl.seed(tid);\n      uint32_t counter = 0;\n      for (int i = tid; i < ops; i += nthreads) {\n        int val;\n        counter++;\n        if (counter % 2) {\n          val = folly::Random::rand32(rng_tl) % (ops + 1);\n          pq.push(val);\n        } else {\n          pq.pop(val);\n        }\n      }\n    };\n\n    dur = run_once(fn);\n    sum += dur;\n    min = std::min(min, dur);\n    max = std::max(max, dur);\n  }\n\n  uint64_t avg = sum / reps;\n  std::cout << std::setw(12) << name;\n  std::cout << \"   \" << std::setw(8) << max / ops << \" ns\";\n  std::cout << \"   \" << std::setw(8) << avg / ops << \" ns\";\n  std::cout << \"   \" << std::setw(8) << min / ops << \" ns\";\n  std::cout << std::endl;\n  return min;\n}\n\ntemplate <class PriorityQueue>\nstatic void accuracy_test(\n    std::string name, uint64_t initial_size, uint32_t top_percent) {\n  int avg = 0;\n  int reps = 15;\n  int valid = initial_size / top_percent;\n  if (valid < 1) {\n    return;\n  }\n  int target = initial_size - valid;\n  for (int r = 0; r < reps; ++r) {\n    PriorityQueue pq;\n    std::unordered_set<int> filter;\n    folly::Random::DefaultGenerator rng;\n    rng.seed(initial_size + r);\n\n    // initialize the queue according to initial_size\n    // eliminate repeated priorities\n    for (uint64_t i = 0; i < initial_size; i++) {\n      int val;\n      do {\n        val = folly::Random::rand32(rng) % initial_size;\n      } while (filter.find(val) != filter.end());\n      filter.insert(val);\n      pq.push(val);\n    }\n\n    int counter = 0;\n    int stop = valid;\n    for (uint64_t i = 0; i < initial_size; i++) {\n      int val;\n      pq.pop(val);\n      if (val >= target) {\n        stop--;\n      }\n      if (stop > 0 && val < target) {\n        counter++;\n      }\n      if (stop == 0) {\n        break;\n      }\n    }\n    avg += counter;\n  }\n  avg /= reps;\n  std::cout << std::setw(16) << name << \"  \";\n  std::cout << \"Lower priority popped: \" << avg;\n  std::cout << std::endl;\n}\n\nusing FCPQ = folly::FlatCombiningPriorityQueue<int>;\n\nTEST(CPQ, ThroughtputBench) {\n  if (!FLAGS_bench) {\n    return;\n  }\n  std::vector<int> test_sizes = {64, 512, 65536};\n  std::vector<int> nthrs = {1, 2, 4, 8, 12, 14, 16, 28, 32, 56};\n\n  std::cout\n      << \"Threads have equal chance to push and pop. \\n\"\n      << \"The bench caculates the avg execution time for\\n\"\n      << \"one operation (push OR pop).\\n\"\n      << \"GL : std::priority_queue protected by global lock\\n\"\n      << \"FL : flatcombinning priority queue\\n\"\n      << \"RCPQ: the relaxed concurrent priority queue\\n\"\n      << std::endl;\n  std::cout << \"\\nTest_name, Max time, Avg time, Min time\" << std::endl;\n  for (auto s : test_sizes) {\n    std::cout << \"\\n ------ Initial size: \" << s << \" ------\" << std::endl;\n    for (int i : nthrs) {\n      nthreads = i;\n      std::cout << \"Thread number: \" << i << std::endl;\n      throughtput_test<GlobalLockPQ<int>>(\"GL\", s);\n      throughtput_test<FCPQ>(\"FC\", s);\n      throughtput_test<RelaxedConcurrentPriorityQueue<int>>(\"RCPQ\", s);\n    }\n  }\n}\n\nTEST(CPQ, ProducerConsumerBench) {\n  if (!FLAGS_bench) {\n    return;\n  }\n  std::vector<int> test_sizes = {0, 512, 65536};\n  std::vector<int> nthrs = {1, 2, 4, 8, 12, 16, 24};\n\n  std::cout\n      << \"<Producer, Consumer> pattern \\n\"\n      << \"The bench caculates the avg execution time for\\n\"\n      << \"push AND pop pair(two operations).\\n\"\n      << \"GL : std::priority_queue protected by global lock\\n\"\n      << \"FL : flatcombinning priority queue\\n\"\n      << \"RCPQ SPN: RCPQ spinning\\n\"\n      << \"RCPQ BLK: RCPQ blocking\\n\"\n      << std::endl;\n  for (int s : test_sizes) {\n    std::cout << \"\\n ------ Scalability ------\" << std::endl;\n    for (int m : nthrs) {\n      for (int n : nthrs) {\n        if (m != n) {\n          continue;\n        }\n        std::cout << \"<\" << m << \" , \" << n << \"> , size = \" << s << \":\"\n                  << std::endl;\n        producer_consumer_test<GlobalLockPQ<int>>(\"GL\", m, n, s);\n        producer_consumer_test<FCPQ>(\"FC\", m, n, s);\n        producer_consumer_test<\n            RelaxedConcurrentPriorityQueue<int, false, false>>(\n            \"RCPQ SPN\", m, n, s);\n        producer_consumer_test<\n            RelaxedConcurrentPriorityQueue<int, true, false>>(\n            \"RCPQ BLK\", m, n, s);\n      }\n    }\n    std::cout << \"\\n ------ Unbalanced(Producer<Consumer) ------\" << std::endl;\n    for (int m : nthrs) {\n      for (int n : nthrs) {\n        if (m > 4 || n - 4 <= m) {\n          continue;\n        }\n        std::cout << \"<\" << m << \" , \" << n << \"> , size = \" << s << \":\"\n                  << std::endl;\n        producer_consumer_test<GlobalLockPQ<int>>(\"GL\", m, n, s);\n        producer_consumer_test<FCPQ>(\"FC\", m, n, s);\n        producer_consumer_test<\n            RelaxedConcurrentPriorityQueue<int, false, false>>(\n            \"RCPQ SPN\", m, n, s);\n        producer_consumer_test<\n            RelaxedConcurrentPriorityQueue<int, true, false>>(\n            \"RCPQ BLK\", m, n, s);\n      }\n    }\n\n    std::cout << \"\\n ------ Unbalanced(Producer>Consumer) ------\" << std::endl;\n    for (int m : nthrs) {\n      for (int n : nthrs) {\n        if (m <= 8 || n > m - 4 || n % 4 != 0) {\n          continue;\n        }\n        std::cout << \"<\" << m << \" , \" << n << \"> , size = \" << s << \":\"\n                  << std::endl;\n        producer_consumer_test<GlobalLockPQ<int>>(\"GL\", m, n, s);\n        producer_consumer_test<FCPQ>(\"FC\", m, n, s);\n        producer_consumer_test<\n            RelaxedConcurrentPriorityQueue<int, false, false>>(\n            \"RCPQ SPN\", m, n, s);\n        producer_consumer_test<\n            RelaxedConcurrentPriorityQueue<int, true, false>>(\n            \"RCPQ BLK\", m, n, s);\n      }\n    }\n  }\n}\n\nTEST(CPQ, Accuracy) {\n  if (!FLAGS_bench) {\n    return;\n  }\n  std::vector<int> test_sizes = {512, 65536, 1 << 20};\n  std::vector<int> rates = {1000, 100, 10};\n  for (auto s : test_sizes) {\n    for (auto p : rates) {\n      std::cout << \"\\n------ Size: \" << s << \"  Get top: \" << 100. / p << \"%\"\n                << \" (Num: \" << s / p << \")\" << \" ------\" << std::endl;\n      accuracy_test<Queue<int>>(\"FIFO Q\", s, p);\n      accuracy_test<RelaxedConcurrentPriorityQueue<int, false, false, 0>>(\n          \"RCPQ(strict)\", s, p);\n      accuracy_test<RelaxedConcurrentPriorityQueue<int, false, false, 2>>(\n          \"RCPQ(batch=2)\", s, p);\n      accuracy_test<RelaxedConcurrentPriorityQueue<int, false, false, 8>>(\n          \"RCPQ(batch=8)\", s, p);\n      accuracy_test<RelaxedConcurrentPriorityQueue<int, false, false, 16>>(\n          \"RCPQ(batch=16)\", s, p);\n      accuracy_test<RelaxedConcurrentPriorityQueue<int, false, false, 50>>(\n          \"RCPQ(batch=50)\", s, p);\n    }\n  }\n}\n\n/*\n *  The folly::SpinningLock use CAS directly for try_lock, which is not\nefficient in the\n *  experiment. The lock used in the experiment based on the test-test-and-set\nlock(Add\n *  check before doing CAS).\n *\nThreads have equal chance to push and pop.\nThe bench caculates the avg execution time for\none operation (push OR pop).\nGL : std::priority_queue protected by global lock\nFL : flatcombinning priority queue\nRCPQ: the relaxed concurrent priority queue\n\nTest_name, Max time, Avg time, Min time\n\n ------ Initial size: 64 ------\nThread number: 1\n          GL         30 ns         29 ns         27 ns\n          FC         47 ns         42 ns         40 ns\n        RCPQ         85 ns         81 ns         77 ns\nThread number: 2\n          GL        377 ns        274 ns        154 ns\n          FC        227 ns        187 ns        139 ns\n        RCPQ        108 ns        106 ns        102 ns\nThread number: 4\n          GL        244 ns        214 ns        191 ns\n          FC        212 ns        191 ns        173 ns\n        RCPQ         98 ns         95 ns         92 ns\nThread number: 8\n          GL        252 ns        221 ns        197 ns\n          FC        127 ns        112 ns        102 ns\n        RCPQ         78 ns         78 ns         76 ns\nThread number: 12\n          GL        251 ns        227 ns        217 ns\n          FC        104 ns         96 ns         88 ns\n        RCPQ         81 ns         79 ns         77 ns\nThread number: 14\n          GL        243 ns        232 ns        224 ns\n          FC        103 ns         96 ns         90 ns\n        RCPQ         84 ns         82 ns         81 ns\nThread number: 16\n          GL        254 ns        239 ns        229 ns\n          FC        105 ns         98 ns         92 ns\n        RCPQ         88 ns         85 ns         83 ns\nThread number: 28\n          GL        265 ns        261 ns        258 ns\n          FC        106 ns        100 ns         96 ns\n        RCPQ         93 ns         87 ns         68 ns\nThread number: 32\n          GL        274 ns        267 ns        261 ns\n          FC        110 ns         98 ns         37 ns\n        RCPQ         93 ns         80 ns         47 ns\nThread number: 56\n          GL        274 ns        263 ns        257 ns\n          FC         78 ns         50 ns         24 ns\n        RCPQ         85 ns         71 ns         45 ns\n\n ------ Initial size: 512 ------\nThread number: 1\n          GL         36 ns         35 ns         33 ns\n          FC         54 ns         49 ns         47 ns\n        RCPQ         79 ns         76 ns         72 ns\nThread number: 2\n          GL        248 ns        187 ns        151 ns\n          FC        228 ns        179 ns        147 ns\n        RCPQ         95 ns         92 ns         90 ns\nThread number: 4\n          GL        282 ns        260 ns        236 ns\n          FC        218 ns        199 ns        174 ns\n        RCPQ         85 ns         81 ns         79 ns\nThread number: 8\n          GL        306 ns        288 ns        270 ns\n          FC        188 ns        114 ns        104 ns\n        RCPQ         64 ns         62 ns         59 ns\nThread number: 12\n          GL        317 ns        296 ns        280 ns\n          FC        105 ns         99 ns         91 ns\n        RCPQ         59 ns         57 ns         52 ns\nThread number: 14\n          GL        331 ns        305 ns        293 ns\n          FC        109 ns         99 ns         92 ns\n        RCPQ         64 ns         57 ns         53 ns\nThread number: 16\n          GL        316 ns        308 ns        291 ns\n          FC        110 ns         99 ns         92 ns\n        RCPQ         58 ns         54 ns         52 ns\nThread number: 28\n          GL        348 ns        339 ns        333 ns\n          FC        109 ns        105 ns        100 ns\n        RCPQ         64 ns         62 ns         56 ns\nThread number: 32\n          GL        353 ns        347 ns        341 ns\n          FC        116 ns        102 ns         39 ns\n        RCPQ         62 ns         32 ns          3 ns\nThread number: 56\n          GL        360 ns        352 ns        342 ns\n          FC        101 ns         58 ns         41 ns\n        RCPQ         59 ns         43 ns         26 ns\n\n ------ Initial size: 65536 ------\nThread number: 1\n          GL         64 ns         60 ns         56 ns\n          FC         93 ns         72 ns         67 ns\n        RCPQ        293 ns        286 ns        281 ns\nThread number: 2\n          GL        262 ns        248 ns        231 ns\n          FC        318 ns        301 ns        288 ns\n        RCPQ        230 ns        216 ns        206 ns\nThread number: 4\n          GL        463 ns        452 ns        408 ns\n          FC        273 ns        265 ns        257 ns\n        RCPQ        141 ns        131 ns        126 ns\nThread number: 8\n          GL        582 ns        574 ns        569 ns\n          FC        152 ns        139 ns        131 ns\n        RCPQ         98 ns         81 ns         72 ns\nThread number: 12\n          GL        593 ns        586 ns        576 ns\n          FC        126 ns        123 ns        119 ns\n        RCPQ         85 ns         72 ns         62 ns\nThread number: 14\n          GL        599 ns        595 ns        588 ns\n          FC        138 ns        123 ns        119 ns\n        RCPQ         79 ns         70 ns         62 ns\nThread number: 16\n          GL        599 ns        592 ns        587 ns\n          FC        138 ns        123 ns        117 ns\n        RCPQ         75 ns         65 ns         56 ns\nThread number: 28\n          GL        611 ns        609 ns        608 ns\n          FC        147 ns        144 ns        137 ns\n        RCPQ         74 ns         70 ns         66 ns\nThread number: 32\n          GL        635 ns        630 ns        627 ns\n          FC        151 ns        143 ns         76 ns\n        RCPQ        199 ns         94 ns         59 ns\nThread number: 56\n          GL        637 ns        633 ns        627 ns\n          FC        176 ns        103 ns         41 ns\n        RCPQ        561 ns        132 ns         46 ns\n\n\n<Producer, Consumer> pattern\nThe bench caculates the avg execution time for\npush AND pop pair(two operations).\nGL : std::priority_queue protected by global lock\nFL : flatcombinning priority queue\nRCPQ SPN: RCPQ spinning\nRCPQ BLK: RCPQ blocking\n\n\n ------ Scalability ------\n<1 , 1> , size = 0:\n          GL        781 ns        735 ns        652 ns\n          FC        599 ns        535 ns        462 ns\n    RCPQ SPN        178 ns        166 ns        148 ns\n    RCPQ BLK        217 ns        201 ns        182 ns\n<2 , 2> , size = 0:\n          GL        686 ns        665 ns        619 ns\n          FC        487 ns        430 ns        398 ns\n    RCPQ SPN        281 ns        239 ns        139 ns\n    RCPQ BLK        405 ns        367 ns        181 ns\n<4 , 4> , size = 0:\n          GL       1106 ns       1082 ns       1050 ns\n          FC        278 ns        242 ns        208 ns\n    RCPQ SPN        114 ns        107 ns        103 ns\n    RCPQ BLK        169 ns        158 ns        148 ns\n<8 , 8> , size = 0:\n          GL       1169 ns       1156 ns       1144 ns\n          FC        236 ns        214 ns        197 ns\n    RCPQ SPN        121 ns        114 ns        110 ns\n    RCPQ BLK        154 ns        150 ns        141 ns\n<12 , 12> , size = 0:\n          GL       1191 ns       1185 ns       1178 ns\n          FC        232 ns        221 ns        201 ns\n    RCPQ SPN        802 ns        205 ns        123 ns\n    RCPQ BLK        218 ns        161 ns        147 ns\n<16 , 16> , size = 0:\n          GL       1236 ns       1227 ns       1221 ns\n          FC        269 ns        258 ns        243 ns\n    RCPQ SPN        826 ns        733 ns        655 ns\n    RCPQ BLK        172 ns        149 ns        137 ns\n<24 , 24> , size = 0:\n          GL       1269 ns       1262 ns       1255 ns\n          FC        280 ns        225 ns        171 ns\n    RCPQ SPN        931 ns        891 ns        836 ns\n    RCPQ BLK        611 ns        445 ns        362 ns\n\n ------ Unbalanced(Producer<Consumer) ------\n<1 , 8> , size = 0:\n          GL       1454 ns       1225 ns       1144 ns\n          FC       2141 ns       1974 ns       1811 ns\n    RCPQ SPN        597 ns        586 ns        573 ns\n    RCPQ BLK        663 ns        649 ns        636 ns\n<1 , 12> , size = 0:\n          GL       1763 ns       1658 ns       1591 ns\n          FC       3396 ns       3261 ns       3107 ns\n    RCPQ SPN        735 ns        714 ns        651 ns\n    RCPQ BLK        773 ns        761 ns        744 ns\n<1 , 16> , size = 0:\n          GL       2231 ns       2070 ns       1963 ns\n          FC       6305 ns       5771 ns       5603 ns\n    RCPQ SPN        787 ns        756 ns        694 ns\n    RCPQ BLK        828 ns        806 ns        775 ns\n<1 , 24> , size = 0:\n          GL       3802 ns       3545 ns       3229 ns\n          FC      10625 ns      10311 ns      10119 ns\n    RCPQ SPN        781 ns        756 ns        739 ns\n    RCPQ BLK        892 ns        882 ns        870 ns\n<2 , 8> , size = 0:\n          GL        873 ns        750 ns        718 ns\n          FC        815 ns        712 ns        659 ns\n    RCPQ SPN        720 ns        691 ns        673 ns\n    RCPQ BLK        738 ns        707 ns        694 ns\n<2 , 12> , size = 0:\n          GL       1061 ns        968 ns        904 ns\n          FC       1410 ns       1227 ns       1190 ns\n    RCPQ SPN        862 ns        829 ns        767 ns\n    RCPQ BLK        825 ns        804 ns        771 ns\n<2 , 16> , size = 0:\n          GL       1438 ns       1283 ns       1162 ns\n          FC       2095 ns       2012 ns       1909 ns\n    RCPQ SPN        763 ns        706 ns        628 ns\n    RCPQ BLK        833 ns        804 ns        777 ns\n<2 , 24> , size = 0:\n          GL       2031 ns       1972 ns       1872 ns\n          FC       4298 ns       4191 ns       4107 ns\n    RCPQ SPN        762 ns        709 ns        680 ns\n    RCPQ BLK        876 ns        859 ns        825 ns\n<4 , 12> , size = 0:\n          GL        696 ns        649 ns        606 ns\n          FC        561 ns        517 ns        480 ns\n    RCPQ SPN        759 ns        698 ns        498 ns\n    RCPQ BLK        823 ns        803 ns        786 ns\n<4 , 16> , size = 0:\n          GL        862 ns        800 ns        749 ns\n          FC        857 ns        824 ns        781 ns\n    RCPQ SPN        730 ns        679 ns        589 ns\n    RCPQ BLK        863 ns        824 ns        803 ns\n<4 , 24> , size = 0:\n          GL       1138 ns       1125 ns       1105 ns\n          FC       1635 ns       1576 ns       1540 ns\n    RCPQ SPN        756 ns        717 ns        668 ns\n    RCPQ BLK        865 ns        839 ns        812 ns\n\n ------ Unbalanced(Producer>Consumer) ------\n<12 , 4> , size = 0:\n          GL       1115 ns       1087 ns       1053 ns\n          FC        373 ns        355 ns        333 ns\n    RCPQ SPN        155 ns        147 ns        142 ns\n    RCPQ BLK        202 ns        190 ns        182 ns\n<12 , 8> , size = 0:\n          GL       1167 ns       1157 ns       1148 ns\n          FC        281 ns        256 ns        227 ns\n    RCPQ SPN        132 ns        126 ns        120 ns\n    RCPQ BLK        175 ns        164 ns        161 ns\n<16 , 4> , size = 0:\n          GL       1103 ns       1088 ns       1074 ns\n          FC        442 ns        380 ns        327 ns\n    RCPQ SPN        178 ns        162 ns        150 ns\n    RCPQ BLK        217 ns        200 ns        188 ns\n<16 , 8> , size = 0:\n          GL       1164 ns       1153 ns       1143 ns\n          FC        290 ns        268 ns        243 ns\n    RCPQ SPN        146 ns        138 ns        134 ns\n    RCPQ BLK        184 ns        175 ns        161 ns\n<16 , 12> , size = 0:\n          GL       1196 ns       1189 ns       1185 ns\n          FC        269 ns        260 ns        245 ns\n    RCPQ SPN        405 ns        172 ns        129 ns\n    RCPQ BLK        172 ns        165 ns        152 ns\n<24 , 4> , size = 0:\n          GL       1097 ns       1081 ns       1030 ns\n          FC        407 ns        369 ns        301 ns\n    RCPQ SPN        184 ns        176 ns        164 ns\n    RCPQ BLK        220 ns        211 ns        201 ns\n<24 , 8> , size = 0:\n          GL       1177 ns       1158 ns       1148 ns\n          FC        321 ns        297 ns        233 ns\n    RCPQ SPN        155 ns        148 ns        139 ns\n    RCPQ BLK        204 ns        188 ns        173 ns\n<24 , 12> , size = 0:\n          GL       1224 ns       1215 ns       1205 ns\n          FC        320 ns        287 ns        218 ns\n    RCPQ SPN        145 ns        141 ns        135 ns\n    RCPQ BLK        176 ns        167 ns        160 ns\n<24 , 16> , size = 0:\n          GL       1250 ns       1244 ns       1238 ns\n          FC        339 ns        257 ns        209 ns\n    RCPQ SPN        615 ns        480 ns        359 ns\n    RCPQ BLK        185 ns        151 ns        137 ns\n\n[ RUN      ] CPQ.Accuracy\nThe Accuracy test check how many pops return lower\npriority when popping the top X% priorities.\nThe default batch size is 16.\n\n------ Size: 512  Get top: 1% (Num: 5) ------\n          FIFO Q  Lower priority popped: 439\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 1\n   RCPQ(batch=8)  Lower priority popped: 10\n  RCPQ(batch=16)  Lower priority popped: 13\n  RCPQ(batch=50)  Lower priority popped: 11\n\n------ Size: 512  Get top: 10% (Num: 51) ------\n          FIFO Q  Lower priority popped: 451\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 15\n   RCPQ(batch=8)  Lower priority popped: 73\n  RCPQ(batch=16)  Lower priority popped: 147\n  RCPQ(batch=50)  Lower priority popped: 201\n\n------ Size: 65536  Get top: 0.1% (Num: 65) ------\n          FIFO Q  Lower priority popped: 64917\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 35\n   RCPQ(batch=8)  Lower priority popped: 190\n  RCPQ(batch=16)  Lower priority popped: 387\n  RCPQ(batch=50)  Lower priority popped: 655\n\n------ Size: 65536  Get top: 1% (Num: 655) ------\n          FIFO Q  Lower priority popped: 64793\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 122\n   RCPQ(batch=8)  Lower priority popped: 516\n  RCPQ(batch=16)  Lower priority popped: 1450\n  RCPQ(batch=50)  Lower priority popped: 3219\n\n------ Size: 65536  Get top: 10% (Num: 6553) ------\n          FIFO Q  Lower priority popped: 58977\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 174\n   RCPQ(batch=8)  Lower priority popped: 753\n  RCPQ(batch=16)  Lower priority popped: 1436\n  RCPQ(batch=50)  Lower priority popped: 3297\n\n------ Size: 1048576  Get top: 0.1% (Num: 1048) ------\n          FIFO Q  Lower priority popped: 1046345\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 124\n   RCPQ(batch=8)  Lower priority popped: 449\n  RCPQ(batch=16)  Lower priority popped: 1111\n  RCPQ(batch=50)  Lower priority popped: 3648\n\n------ Size: 1048576  Get top: 1% (Num: 10485) ------\n          FIFO Q  Lower priority popped: 1038012\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 297\n   RCPQ(batch=8)  Lower priority popped: 1241\n  RCPQ(batch=16)  Lower priority popped: 2489\n  RCPQ(batch=50)  Lower priority popped: 7764\n\n------ Size: 1048576  Get top: 10% (Num: 104857) ------\n          FIFO Q  Lower priority popped: 943706\n    RCPQ(strict)  Lower priority popped: 0\n   RCPQ(batch=2)  Lower priority popped: 1984\n   RCPQ(batch=8)  Lower priority popped: 8150\n  RCPQ(batch=16)  Lower priority popped: 15787\n  RCPQ(batch=50)  Lower priority popped: 42778\n\nThe experiment was running on 1 NUMA node,\nwhich is 14 cores.\n\nrchitecture:        x86_64\nCPU op-mode(s):      32-bit, 64-bit\nByte Order:          Little Endian\nCPU(s):              56\nOn-line CPU(s) list: 0-55\nThread(s) per core:  2\nCore(s) per socket:  14\nSocket(s):           2\nNUMA node(s):        2\nVendor ID:           GenuineIntel\nCPU family:          6\nModel:               79\nModel name:          Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz\nStepping:            1\nCPU MHz:             2401.000\nCPU max MHz:         2401.0000\nCPU min MHz:         1200.0000\nBogoMIPS:            4788.91\nVirtualization:      VT-x\nL1d cache:           32K\nL1i cache:           32K\nL2 cache:            256K\nL3 cache:            35840K\nNUMA node0 CPU(s):   0-13,28-41\nNUMA node1 CPU(s):   14-27,42-55\n*/\n"
  },
  {
    "path": "folly/concurrency/container/test/SingleWriterFixedHashMapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/container/SingleWriterFixedHashMap.h>\n\n#include <folly/Benchmark.h>\n#include <folly/container/Array.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/test/Barrier.h>\n\n#include <atomic>\n#include <iomanip>\n#include <thread>\n\nDEFINE_bool(bench, false, \"run benchmark\");\nDEFINE_int32(reps, 10, \"number of reps\");\nDEFINE_int32(ops, 1000000, \"number of operations per rep\");\n\nusing SWFHM = folly::SingleWriterFixedHashMap<int, int>;\n\nvoid basic_test() {\n  SWFHM m(32);\n\n  ASSERT_EQ(m.used(), 0);\n  ASSERT_FALSE(m.erase(0));\n  ASSERT_EQ(m.size(), 0);\n  ASSERT_EQ(m.find(0), m.end());\n  ASSERT_EQ(m.available(), 32);\n\n  ASSERT_TRUE(m.insert(1, 1));\n  ASSERT_EQ(m.size(), 1);\n  ASSERT_EQ(m.used(), 1);\n  ASSERT_NE(m.find(1), m.end());\n  ASSERT_TRUE(m.contains(1));\n  ASSERT_EQ(m.available(), 31);\n\n  ASSERT_TRUE(m.insert(2, 2));\n  ASSERT_EQ(m.size(), 2);\n  ASSERT_EQ(m.used(), 2);\n  ASSERT_NE(m.find(2), m.end());\n  ASSERT_EQ(m.available(), 30);\n\n  ASSERT_TRUE(m.erase(1));\n  ASSERT_TRUE(m.erase(2));\n  ASSERT_EQ(m.size(), 0);\n  ASSERT_EQ(m.used(), 2);\n  ASSERT_EQ(m.available(), 30);\n  ASSERT_EQ(m.find(1), m.end());\n  ASSERT_EQ(m.find(2), m.end());\n}\n\nTEST(SingleWriterFixedHashMap, basic) {\n  basic_test();\n}\n\nvoid iterator_test() {\n  SWFHM m(32);\n  for (int i = 0; i < 20; ++i) {\n    m.insert(i, i);\n  }\n  for (int i = 0; i < 10; ++i) {\n    m.erase(i);\n  }\n  int sum = 0;\n  for (auto it = m.begin(); it != m.end(); ++it) {\n    sum += it.value();\n  }\n  ASSERT_EQ(sum, 145);\n}\n\nTEST(SingleWriterFixedHashMap, iterator) {\n  iterator_test();\n}\n\nvoid copy_test() {\n  SWFHM m1(32);\n  for (int i = 0; i < 20; ++i) {\n    m1.insert(i, i);\n  }\n  SWFHM m2(64, m1);\n  for (int i = 0; i < 10; ++i) {\n    m2.erase(i);\n  }\n  int sum = 0;\n  for (auto it = m2.begin(); it != m2.end(); ++it) {\n    sum += it.value();\n  }\n  ASSERT_EQ(sum, 145);\n}\n\nTEST(SingleWriterFixedHashMap, copy) {\n  copy_test();\n}\n\nvoid drf_test() {\n  SWFHM m(32);\n  int nthr = 5;\n  folly::test::Barrier b1(nthr + 1);\n  std::atomic<bool> stop{false};\n\n  auto writer = std::thread([&] {\n    b1.wait();\n    for (int i = 0; i < 10000; ++i) {\n      for (int j = 0; j < 10; ++j) {\n        m.insert(j, j);\n      }\n      for (int j = 0; j < 10; ++j) {\n        m.erase(j);\n      }\n    }\n    stop.store(true);\n  });\n\n  std::vector<std::thread> readers(nthr - 1);\n  for (int i = 0; i < nthr - 1; ++i) {\n    readers[i] = std::thread([&] {\n      b1.wait();\n      while (!stop) {\n        int sum = 0;\n        for (auto it = m.begin(); it != m.end(); ++it) {\n          sum += it.value();\n        }\n        ASSERT_LE(sum, 45);\n      }\n    });\n  }\n\n  b1.wait();\n  writer.join();\n  for (int i = 0; i < nthr - 1; ++i) {\n    readers[i].join();\n  }\n}\n\nTEST(SingleWriterFixedHashMap, drf) {\n  drf_test();\n}\n\nvoid copy_tombstones_test() {\n  SWFHM* m = new SWFHM(4);\n  for (int i = 0; i < 100; ++i) {\n    SWFHM* m2 = new SWFHM(4, *m);\n    delete m;\n    ASSERT_LT(m2->used(), m2->capacity());\n    m2->insert(i, i);\n    m2->erase(i);\n    m = m2;\n  }\n  delete m;\n}\n\nTEST(SingleWriterFixedHashMap, copyTombstones) {\n  copy_tombstones_test();\n}\n\n// Benchmarks\n\ntemplate <typename Func>\ninline uint64_t run_once(int nthr, const Func& fn) {\n  folly::test::Barrier b1(nthr + 1);\n\n  std::vector<std::thread> thr(nthr);\n  for (int tid = 0; tid < nthr; ++tid) {\n    thr[tid] = std::thread([&, tid] {\n      b1.wait();\n      fn(tid);\n    });\n  }\n\n  b1.wait();\n  /* begin time measurement */\n  auto const tbegin = std::chrono::steady_clock::now();\n  /* wait for completion */\n  for (int i = 0; i < nthr; ++i) {\n    thr[i].join();\n  }\n  /* end time measurement */\n  auto const tend = std::chrono::steady_clock::now();\n  auto const dur = tend - tbegin;\n  return std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count();\n}\n\ntemplate <typename RepFunc>\nuint64_t runBench(int ops, const RepFunc& repFn) {\n  uint64_t reps = FLAGS_reps;\n  uint64_t min = UINTMAX_MAX;\n  uint64_t max = 0;\n  uint64_t sum = 0;\n  std::vector<uint64_t> durs(reps);\n  for (uint64_t r = 0; r < reps; ++r) {\n    uint64_t dur = repFn();\n    durs[r] = dur;\n    sum += dur;\n    min = std::min(min, dur);\n    max = std::max(max, dur);\n    // if each rep takes too long run at least 3 reps\n    const uint64_t minute = 60000000000ULL;\n    if (sum > minute && r >= 2) {\n      reps = r + 1;\n      break;\n    }\n  }\n  const std::string ns_unit = \" ns\";\n  uint64_t avg = sum / reps;\n  uint64_t res = min;\n  uint64_t varsum = 0;\n  for (uint64_t r = 0; r < reps; ++r) {\n    auto term = int64_t(reps * durs[r]) - int64_t(sum);\n    varsum += term * term;\n  }\n  uint64_t dev = uint64_t(std::sqrt(varsum) * std::pow(reps, -1.5));\n  std::cout << \"   \" << std::setw(4) << max / ops << ns_unit;\n  std::cout << \"   \" << std::setw(4) << avg / ops << ns_unit;\n  std::cout << \"   \" << std::setw(4) << dev / ops << ns_unit;\n  std::cout << \"   \" << std::setw(4) << res / ops << ns_unit;\n  std::cout << std::endl;\n  return res;\n}\n\nuint64_t bench_find(const int nthr, const uint64_t ops) {\n  auto repFn = [&] {\n    SWFHM m(64);\n    for (int i = 0; i < 10; ++i) {\n      m.insert(i, i);\n    }\n    auto fn = [&](int tid) {\n      for (uint64_t i = tid; i < 10 * ops; i += nthr) {\n        auto it = m.find(5);\n        if (it.value() != 5) {\n          ASSERT_EQ(it.value(), 5);\n        }\n      }\n    };\n    return run_once(nthr, fn);\n  };\n  return runBench(ops, repFn);\n}\n\nuint64_t bench_iterate(const int nthr, const uint64_t ops) {\n  auto repFn = [&] {\n    SWFHM m(16);\n    for (int i = 0; i < 10; ++i) {\n      m.insert(i, i);\n    }\n    auto fn = [&](int tid) {\n      for (uint64_t i = tid; i < ops; i += nthr) {\n        int sum = 0;\n        for (auto it = m.begin(); it != m.end(); ++it) {\n          sum += it.value();\n        }\n        if (sum != 55) {\n          ASSERT_EQ(sum, 45);\n        }\n      }\n    };\n    return run_once(nthr, fn);\n  };\n  return runBench(ops, repFn);\n}\n\nuint64_t bench_ctor_dtor(const int nthr, const uint64_t ops) {\n  auto repFn = [&] {\n    auto fn = [&](int tid) {\n      for (uint64_t i = tid; i < ops; i += nthr) {\n        SWFHM m(16);\n        folly::doNotOptimizeAway(m);\n      }\n    };\n    return run_once(nthr, fn);\n  };\n  return runBench(ops, repFn);\n}\n\nuint64_t bench_copy_empty_dtor(const int nthr, const uint64_t ops) {\n  auto repFn = [&] {\n    SWFHM m0(16);\n    auto fn = [&](int tid) {\n      for (uint64_t i = tid; i < ops; i += nthr) {\n        SWFHM m(m0.capacity(), m0);\n      }\n    };\n    return run_once(nthr, fn);\n  };\n  return runBench(ops, repFn);\n}\n\nuint64_t bench_copy_nonempty_dtor(const int nthr, const uint64_t ops) {\n  auto repFn = [&] {\n    SWFHM m0(16);\n    m0.insert(10, 10);\n    auto fn = [&](int tid) {\n      for (uint64_t i = tid; i < ops; i += nthr) {\n        SWFHM m(m0.capacity(), m0);\n      }\n    };\n    return run_once(nthr, fn);\n  };\n  return runBench(ops, repFn);\n}\n\nuint64_t bench_copy_resize_dtor(const int nthr, const uint64_t ops) {\n  auto repFn = [&] {\n    SWFHM m0(16);\n    m0.insert(10, 10);\n    auto fn = [&](int tid) {\n      for (uint64_t i = tid; i < ops; i += nthr) {\n        SWFHM m(2 * m0.capacity(), m0);\n      }\n    };\n    return run_once(nthr, fn);\n  };\n  return runBench(ops, repFn);\n}\n\nvoid dottedLine() {\n  std::cout\n      << \"........................................................................\"\n      << std::endl;\n}\n\nconstexpr auto nthr = folly::make_array<int>(1, 10);\n\nTEST(SingleWriterFixedHashMapBench, Bench) {\n  if (!FLAGS_bench) {\n    return;\n  }\n  std::cout\n      << \"========================================================================\"\n      << std::endl;\n  std::cout << std::setw(2) << FLAGS_reps << \" reps of \" << std::setw(8)\n            << FLAGS_ops << \" operations\\n\";\n  dottedLine();\n  std::cout << \"$ numactl -N 1 $dir/single_writer_hash_map_test --bench\\n\";\n  std::cout\n      << \"========================================================================\"\n      << std::endl;\n  std::cout\n      << \"Test name                         Max time  Avg time  Dev time  Min time\"\n      << std::endl;\n  for (int i : nthr) {\n    std::cout << \"============================== \" << std::setw(2) << i\n              << \" threads \" << \"==============================\" << std::endl;\n    const uint64_t ops = FLAGS_ops;\n    std::cout << \"10x find                       \";\n    bench_find(i, ops);\n    std::cout << \"iterate 10-element 32-slot map \";\n    bench_iterate(i, ops);\n    std::cout << \"construct / destruct           \";\n    bench_ctor_dtor(i, ops);\n    std::cout << \"copy empty / destruct          \";\n    bench_copy_empty_dtor(i, ops);\n    std::cout << \"copy nonempty / destruct       \";\n    bench_copy_nonempty_dtor(i, ops);\n    std::cout << \"copy resize / destruct         \";\n    bench_copy_resize_dtor(i, ops);\n  }\n  std::cout\n      << \"========================================================================\"\n      << std::endl;\n}\n\n/*\n========================================================================\n10 reps of  1000000 operations\n........................................................................\n$ numactl -N 1 $dir/single_writer_hash_map_test --bench\n========================================================================\nTest name                         Max time  Avg time  Dev time  Min time\n==============================  1 threads ==============================\n10x find                            36 ns     35 ns      0 ns     34 ns\niterate 10-element 32-slot map      20 ns     19 ns      0 ns     19 ns\nconstruct / destruct                 1 ns      1 ns      0 ns      1 ns\ncopy empty / destruct                5 ns      4 ns      0 ns      4 ns\ncopy nonempty / destruct            25 ns     24 ns      0 ns     23 ns\ncopy resize / destruct              40 ns     38 ns      0 ns     37 ns\n============================== 10 threads ==============================\n10x find                             6 ns      4 ns      1 ns      3 ns\niterate 10-element 32-slot map       3 ns      3 ns      0 ns      1 ns\nconstruct / destruct                 0 ns      0 ns      0 ns      0 ns\ncopy empty / destruct                0 ns      0 ns      0 ns      0 ns\ncopy nonempty / destruct             4 ns      3 ns      0 ns      2 ns\ncopy resize / destruct               8 ns      6 ns      1 ns      4 ns\n========================================================================\n */\n"
  },
  {
    "path": "folly/concurrency/container/test/atomic_grow_array_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/container/atomic_grow_array.h>\n\n#include <atomic>\n#include <numeric>\n\n#include <folly/lang/Keep.h>\n#include <folly/portability/GTest.h>\n\nextern \"C\" FOLLY_KEEP int check_folly_atomic_grow_array_index(\n    folly::atomic_grow_array<int>& array, size_t const index) {\n  return array[index];\n}\n\nstatic_assert( //\n    std::is_nothrow_default_constructible_v< //\n        folly::atomic_grow_array<int>::iterator>);\nstatic_assert( //\n    std::is_nothrow_default_constructible_v<\n        folly::atomic_grow_array<int>::const_iterator>);\nstatic_assert( //\n    std::is_nothrow_copy_constructible_v<\n        folly::atomic_grow_array<int>::iterator>);\nstatic_assert( //\n    std::is_nothrow_copy_constructible_v<\n        folly::atomic_grow_array<int>::const_iterator>);\nstatic_assert( //\n    std::is_nothrow_constructible_v<\n        folly::atomic_grow_array<int>::const_iterator,\n        folly::atomic_grow_array<int>::iterator const&>);\nstatic_assert( //\n    std::is_nothrow_copy_assignable_v< //\n        folly::atomic_grow_array<int>::iterator>);\nstatic_assert( //\n    std::is_nothrow_copy_assignable_v<\n        folly::atomic_grow_array<int>::const_iterator>);\nstatic_assert( //\n    std::is_nothrow_assignable_v<\n        folly::atomic_grow_array<int>::const_iterator,\n        folly::atomic_grow_array<int>::iterator const&>);\n\nstatic_assert( //\n    std::is_nothrow_default_constructible_v<\n        folly::atomic_grow_array<int>::view>);\nstatic_assert( //\n    std::is_nothrow_default_constructible_v<\n        folly::atomic_grow_array<int>::const_view>);\nstatic_assert( //\n    std::is_nothrow_copy_constructible_v< //\n        folly::atomic_grow_array<int>::view>);\nstatic_assert( //\n    std::is_nothrow_copy_constructible_v<\n        folly::atomic_grow_array<int>::const_view>);\nstatic_assert( //\n    std::is_nothrow_constructible_v<\n        folly::atomic_grow_array<int>::const_view,\n        folly::atomic_grow_array<int>::view const&>);\nstatic_assert( //\n    std::is_nothrow_copy_assignable_v< //\n        folly::atomic_grow_array<int>::view>);\nstatic_assert( //\n    std::is_nothrow_copy_assignable_v<\n        folly::atomic_grow_array<int>::const_view>);\nstatic_assert( //\n    std::is_nothrow_assignable_v<\n        folly::atomic_grow_array<int>::const_view,\n        folly::atomic_grow_array<int>::view const&>);\n\nstruct AtomicGrowArrayTest : testing::Test {\n  struct policy_base {\n    template <typename V>\n    using atom = std::atomic<V>;\n  };\n};\n\nTEST_F(AtomicGrowArrayTest, example) {\n  static constexpr size_t max_size = 36;\n  struct policy_t : policy_base {\n    mutable size_t size_counter{0};\n    mutable int item_counter{0};\n    size_t grow(size_t /* curr */, size_t /* index */) const {\n      if (size_counter < max_size) {\n        ++size_counter;\n      }\n      return size_counter;\n    }\n    int make() const { return item_counter++; }\n  };\n\n  folly::atomic_grow_array<int, policy_t> array{};\n  EXPECT_EQ(0, array.size());\n\n  for (size_t i = 0; i < max_size; ++i) {\n    EXPECT_EQ(i, array[i]);\n    EXPECT_LT(i, array.size());\n  }\n\n  EXPECT_EQ(max_size, array.size());\n  EXPECT_EQ(max_size, array.as_view().size());\n  EXPECT_EQ(max_size, std::as_const(array).as_view().size());\n  EXPECT_FALSE(array.empty());\n  EXPECT_FALSE(array.as_view().empty());\n  EXPECT_FALSE(std::as_const(array).as_view().empty());\n}\n\nTEST_F(AtomicGrowArrayTest, empty) {\n  folly::atomic_grow_array<std::atomic<int>> array;\n  EXPECT_EQ(0, array.size());\n  EXPECT_EQ(0, array.as_view().size());\n  EXPECT_EQ(0, std::as_const(array).as_view().size());\n  EXPECT_TRUE(array.empty());\n  EXPECT_TRUE(array.as_view().empty());\n  EXPECT_TRUE(std::as_const(array).as_view().empty());\n\n  auto const expected = 0;\n  auto const count = [](auto& v) {\n    return std::accumulate(v.begin(), v.end(), 0);\n  };\n  auto const countp = [](auto& v) {\n    auto const sum = [](auto a, auto* b) { return a + *b; };\n    return std::accumulate(v.begin(), v.end(), 0, sum);\n  };\n  {\n    auto view = array.as_view();\n    EXPECT_EQ(expected, count(view));\n    auto span = array.as_ptr_span();\n    EXPECT_EQ(expected, countp(span));\n  }\n  {\n    auto const view = array.as_view();\n    EXPECT_EQ(expected, count(view));\n    auto const span = array.as_ptr_span();\n    EXPECT_EQ(expected, countp(span));\n  }\n  {\n    auto view = std::as_const(array).as_view();\n    EXPECT_EQ(expected, count(view));\n    auto span = std::as_const(array).as_ptr_span();\n    EXPECT_EQ(expected, countp(span));\n  }\n  {\n    auto const view = std::as_const(array).as_view();\n    EXPECT_EQ(expected, count(view));\n    auto const span = std::as_const(array).as_ptr_span();\n    EXPECT_EQ(expected, countp(span));\n  }\n}\n\nTEST_F(AtomicGrowArrayTest, stress) {\n  constexpr size_t num_iters = size_t(1) << 6;\n  constexpr size_t num_threads = size_t(1) << 5;\n  constexpr size_t max_size = size_t(1) << 10;\n\n  for (size_t i = 0; i < num_iters; ++i) {\n    folly::atomic_grow_array<std::atomic<int>> array;\n    std::vector<std::thread> threads(num_threads);\n    for (auto& th : threads) {\n      th = std::thread([&] {\n        for (size_t j = 0; j < max_size; ++j) {\n          array[j]++;\n        }\n      });\n    }\n    for (auto& th : threads) {\n      th.join();\n    }\n    auto const expected = max_size * num_threads;\n    auto const count = [](auto& v) {\n      return std::accumulate(v.begin(), v.end(), 0);\n    };\n    {\n      auto view = array.as_view();\n      EXPECT_EQ(expected, count(view));\n    }\n    {\n      auto const view = array.as_view();\n      EXPECT_EQ(expected, count(view));\n    }\n    {\n      auto view = std::as_const(array).as_view();\n      EXPECT_EQ(expected, count(view));\n    }\n    {\n      auto const view = std::as_const(array).as_view();\n      EXPECT_EQ(expected, count(view));\n    }\n  }\n}\n"
  },
  {
    "path": "folly/concurrency/detail/AtomicSharedPtr-detail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <limits.h>\n#include <atomic>\n#include <memory>\n\n#include <folly/lang/SafeAssert.h>\n\n#if defined(__GLIBCXX__)\n\nnamespace folly {\nnamespace detail {\n\n// This implementation is specific to libstdc++, now accepting\n// diffs for other libraries.\n\n// Specifically, this adds support for two things:\n// 1) incrementing/decrementing the shared count by more than 1 at a time\n// 2) Getting the thing the shared_ptr points to, which may be different from\n//    the aliased pointer.\n\nclass shared_ptr_internals {\n public:\n  template <typename T, typename... Args>\n  static std::shared_ptr<T> make_ptr(Args&&... args) {\n    return std::make_shared<T>(std::forward<Args...>(args...));\n  }\n  using shared_count = std::__shared_count<std::_S_atomic>;\n  using counted_base = std::_Sp_counted_base<std::_S_atomic>;\n  template <typename T>\n  using CountedPtr = std::shared_ptr<T>;\n\n  template <typename T>\n  static counted_base* get_counted_base(const std::shared_ptr<T>& bar);\n\n  static void inc_shared_count(counted_base* base, long count);\n\n  template <typename T>\n  static void release_shared(counted_base* base, long count);\n\n  template <typename T>\n  static T* get_shared_ptr(counted_base* base);\n\n  template <typename T>\n  static T* release_ptr(std::shared_ptr<T>& p);\n\n  template <typename T>\n  static std::shared_ptr<T> get_shared_ptr_from_counted_base(\n      counted_base* base, bool inc = true);\n\n private:\n  /* Accessors for private members using explicit template instantiation */\n  struct access_shared_ptr {\n    using type = shared_count std::__shared_ptr<const void, std::_S_atomic>::*;\n    friend type fieldPtr(access_shared_ptr);\n  };\n\n  struct access_base {\n    using type = counted_base* shared_count::*;\n    friend type fieldPtr(access_base);\n  };\n\n  struct access_use_count {\n    using type = _Atomic_word counted_base::*;\n    friend type fieldPtr(access_use_count);\n  };\n\n  struct access_weak_count {\n    using type = _Atomic_word counted_base::*;\n    friend type fieldPtr(access_weak_count);\n  };\n\n  struct access_counted_ptr_ptr {\n    using type =\n        const void* std::_Sp_counted_ptr<const void*, std::_S_atomic>::*;\n    friend type fieldPtr(access_counted_ptr_ptr);\n  };\n\n  struct access_shared_ptr_ptr {\n    using type = const void* std::__shared_ptr<const void, std::_S_atomic>::*;\n    friend type fieldPtr(access_shared_ptr_ptr);\n  };\n\n  struct access_refcount {\n    using type = shared_count std::__shared_ptr<const void, std::_S_atomic>::*;\n    friend type fieldPtr(access_refcount);\n  };\n\n  template <typename Tag, typename Tag::type M>\n  struct Rob {\n    friend typename Tag::type fieldPtr(Tag) { return M; }\n  };\n};\n\ntemplate struct shared_ptr_internals::Rob<\n    shared_ptr_internals::access_shared_ptr,\n    &std::__shared_ptr<const void, std::_S_atomic>::_M_refcount>;\ntemplate struct shared_ptr_internals::Rob<\n    shared_ptr_internals::access_base,\n    &shared_ptr_internals::shared_count::_M_pi>;\ntemplate struct shared_ptr_internals::Rob<\n    shared_ptr_internals::access_use_count,\n    &shared_ptr_internals::counted_base::_M_use_count>;\ntemplate struct shared_ptr_internals::Rob<\n    shared_ptr_internals::access_weak_count,\n    &shared_ptr_internals::counted_base::_M_weak_count>;\ntemplate struct shared_ptr_internals::Rob<\n    shared_ptr_internals::access_counted_ptr_ptr,\n    &std::_Sp_counted_ptr<const void*, std::_S_atomic>::_M_ptr>;\ntemplate struct shared_ptr_internals::Rob<\n    shared_ptr_internals::access_shared_ptr_ptr,\n    &std::__shared_ptr<const void, std::_S_atomic>::_M_ptr>;\ntemplate struct shared_ptr_internals::Rob<\n    shared_ptr_internals::access_refcount,\n    &std::__shared_ptr<const void, std::_S_atomic>::_M_refcount>;\n\ntemplate <typename T>\ninline shared_ptr_internals::counted_base*\nshared_ptr_internals::get_counted_base(const std::shared_ptr<T>& bar) {\n  // reinterpret_pointer_cast<const void>\n  // Not quite C++ legal, but explicit template instantiation access to\n  // private members requires full type name (i.e. shared_ptr<const void>, not\n  // shared_ptr<T>)\n  const std::shared_ptr<const void>& ptr(\n      reinterpret_cast<const std::shared_ptr<const void>&>(bar));\n  return (ptr.*fieldPtr(access_shared_ptr{})).*fieldPtr(access_base{});\n}\n\ninline void shared_ptr_internals::inc_shared_count(\n    counted_base* base, long count) {\n  // Check that we don't exceed the maximum number of atomic_shared_ptrs.\n  // Consider setting EXTERNAL_COUNT lower if this CHECK is hit.\n  FOLLY_SAFE_CHECK(\n      base->_M_get_use_count() + count < INT_MAX, \"atomic_shared_ptr overflow\");\n  __gnu_cxx::__atomic_add_dispatch(\n      &(base->*fieldPtr(access_use_count{})), static_cast<int>(count));\n}\n\ntemplate <typename T>\ninline void shared_ptr_internals::release_shared(\n    counted_base* base, long count) {\n  // If count == 1, this is equivalent to base->_M_release()\n  if (__gnu_cxx::__exchange_and_add_dispatch(\n          &(base->*fieldPtr(access_use_count{})), -static_cast<int>(count)) ==\n      count) {\n    base->_M_dispose();\n\n    if (__gnu_cxx::__exchange_and_add_dispatch(\n            &(base->*fieldPtr(access_weak_count{})), -1) == 1) {\n      base->_M_destroy();\n    }\n  }\n}\n\ntemplate <typename T>\ninline T* shared_ptr_internals::get_shared_ptr(counted_base* base) {\n  // See if this was a make_shared allocation\n  auto inplace = base->_M_get_deleter(typeid(std::_Sp_make_shared_tag));\n  if (inplace) {\n    return (T*)inplace;\n  }\n  // Could also be a _Sp_counted_deleter, but the layout is the same\n  using derived_type = std::_Sp_counted_ptr<const void*, std::_S_atomic>;\n  auto ptr = reinterpret_cast<derived_type*>(base);\n  return (T*)(ptr->*fieldPtr(access_counted_ptr_ptr{}));\n}\n\ntemplate <typename T>\ninline T* shared_ptr_internals::release_ptr(std::shared_ptr<T>& p) {\n  auto res = p.get();\n  std::shared_ptr<const void>& ptr(\n      reinterpret_cast<std::shared_ptr<const void>&>(p));\n  ptr.*fieldPtr(access_shared_ptr_ptr{}) = nullptr;\n  (ptr.*fieldPtr(access_refcount{})).*fieldPtr(access_base{}) = nullptr;\n  return res;\n}\n\ntemplate <typename T>\ninline std::shared_ptr<T>\nshared_ptr_internals::get_shared_ptr_from_counted_base(\n    counted_base* base, bool inc) {\n  if (!base) {\n    return nullptr;\n  }\n  std::shared_ptr<const void> newp;\n  if (inc) {\n    inc_shared_count(base, 1);\n  }\n  newp.*fieldPtr(access_shared_ptr_ptr{}) =\n      get_shared_ptr<const void>(base); // _M_ptr\n  (newp.*fieldPtr(access_refcount{})).*fieldPtr(access_base{}) = base;\n  // reinterpret_pointer_cast<T>\n  auto res = reinterpret_cast<std::shared_ptr<T>*>(&newp);\n  return std::move(*res);\n}\n\n} // namespace detail\n} // namespace folly\n\n#endif // defined(__GLIBCXX__)\n"
  },
  {
    "path": "folly/concurrency/detail/ConcurrentHashMap-detail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <memory>\n#include <mutex>\n#include <new>\n\n#include <folly/ScopeGuard.h>\n#include <folly/container/HeterogeneousAccess.h>\n#include <folly/container/detail/F14Mask.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/Exception.h>\n#include <folly/synchronization/Hazptr.h>\n\n#if FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE\n#include <arm_acle.h>\n#include <arm_neon.h>\n\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n#include <arm_sve.h>\n\n#include <arm_neon_sve_bridge.h> // @manual\n#endif\n\n#elif FOLLY_SSE_PREREQ(4, 2) && !FOLLY_MOBILE\n#include <nmmintrin.h>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\nnamespace concurrenthashmap {\n\ntemplate <typename Class, typename ValueType, typename Allocator>\nclass alignas(alignof(ValueType)) AlignedStorage {\n  struct alignas(alignof(Class)) Dummy {};\n\n  using DummyAllocator =\n      typename std::allocator_traits<Allocator>::template rebind_alloc<Dummy>;\n  using DummyAllocTraits = std::allocator_traits<DummyAllocator>;\n\n  static size_t calculateNeededDummyCount(size_t count) {\n    const size_t sz = sizeof(Class) + sizeof(ValueType) * count;\n    static_assert(alignof(Dummy) % alignof(ValueType) == 0);\n    static_assert(alignof(Dummy) % alignof(Class) == 0);\n    const size_t aligned_sz = align_ceil(sz, alignof(Dummy));\n    return aligned_sz / sizeof(Dummy);\n  }\n\n public:\n  ValueType* array() noexcept {\n    return reinterpret_cast<ValueType*>(static_cast<Class*>(this) + 1);\n  }\n\n protected:\n  static Class* allocate(size_t count) {\n    auto allocator = DummyAllocator();\n    auto* buf =\n        DummyAllocTraits::allocate(allocator, calculateNeededDummyCount(count));\n    return reinterpret_cast<Class*>(buf);\n  }\n\n  void deallocate(size_t count) {\n    auto allocator = DummyAllocator();\n    DummyAllocTraits::deallocate(\n        allocator,\n        reinterpret_cast<Dummy*>(static_cast<Class*>(this)),\n        calculateNeededDummyCount(count));\n  }\n};\n\nenum class InsertType {\n  DOES_NOT_EXIST, // insert/emplace operations.  If key exists, return false.\n  MUST_EXIST, // assign operations.  If key does not exist, return false.\n  ANY, // insert_or_assign.\n  MATCH, // assign_if_equal (not in std).  For concurrent maps, a\n         // way to atomically change a value if equal to some other\n         // value.\n  MATCH_OR_DOES_NOT_EXIST, // behaves like MATCH if key exists, inserts if key\n                           // does not exist.\n};\n\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    typename Allocator,\n    template <typename> class Atom,\n    typename Enabled = void>\nclass ValueHolder {\n public:\n  using value_type = std::pair<const KeyType, ValueType>;\n\n  explicit ValueHolder(const ValueHolder& other) : item_(other.item_) {}\n\n  template <typename Arg, typename... Args>\n  ValueHolder(std::piecewise_construct_t, Arg&& k, Args&&... args)\n      : item_(\n            std::piecewise_construct,\n            std::forward_as_tuple(std::forward<Arg>(k)),\n            std::forward_as_tuple(std::forward<Args>(args)...)) {}\n  value_type& getItem() { return item_; }\n\n private:\n  value_type item_;\n};\n\n// If the ValueType is not copy constructible, we can instead add\n// an extra indirection.  Adds more allocations / deallocations and\n// pulls in an extra cacheline.\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    typename Allocator,\n    template <typename> class Atom>\nclass ValueHolder<\n    KeyType,\n    ValueType,\n    Allocator,\n    Atom,\n    std::enable_if_t<\n        !std::is_nothrow_copy_constructible<ValueType>::value ||\n        !std::is_nothrow_copy_constructible<KeyType>::value>> {\n  using value_type = std::pair<const KeyType, ValueType>;\n\n  struct CountedItem {\n    value_type kv_;\n    Atom<uint32_t> numlinks_{1}; // Number of incoming links\n\n    template <typename Arg, typename... Args>\n    CountedItem(std::piecewise_construct_t, Arg&& k, Args&&... args)\n        : kv_(std::piecewise_construct,\n              std::forward_as_tuple(std::forward<Arg>(k)),\n              std::forward_as_tuple(std::forward<Args>(args)...)) {}\n\n    value_type& getItem() { return kv_; }\n\n    void acquireLink() {\n      uint32_t count = numlinks_.fetch_add(1, std::memory_order_release);\n      DCHECK_GE(count, 1u);\n    }\n\n    bool releaseLink() {\n      uint32_t count = numlinks_.load(std::memory_order_acquire);\n      DCHECK_GE(count, 1u);\n      if (count > 1) {\n        count = numlinks_.fetch_sub(1, std::memory_order_acq_rel);\n      }\n      return count == 1;\n    }\n  }; // CountedItem\n  // Back to ValueHolder specialization\n\n  struct DeallocateFn { // Avoids dependent lambdas in function templates\n    ValueHolder& self;\n    void operator()() const noexcept {\n      Allocator().deallocate((uint8_t*)self.item_, sizeof(CountedItem));\n    }\n  };\n\n  CountedItem* item_; // Link to unique key-value item.\n\n public:\n  explicit ValueHolder(const ValueHolder& other) {\n    DCHECK(other.item_);\n    item_ = other.item_;\n    item_->acquireLink();\n  }\n\n  ValueHolder& operator=(const ValueHolder&) = delete;\n\n  template <typename Arg, typename... Args>\n  ValueHolder(std::piecewise_construct_t, Arg&& k, Args&&... args) {\n    item_ = (CountedItem*)Allocator().allocate(sizeof(CountedItem));\n    auto g = makeGuard(DeallocateFn{*this});\n    new (item_) CountedItem(\n        std::piecewise_construct,\n        std::forward<Arg>(k),\n        std::forward<Args>(args)...);\n    g.dismiss();\n  }\n\n  ~ValueHolder() {\n    DCHECK(item_);\n    if (item_->releaseLink()) {\n      item_->~CountedItem();\n      Allocator().deallocate((uint8_t*)item_, sizeof(CountedItem));\n    }\n  }\n\n  value_type& getItem() {\n    DCHECK(item_);\n    return item_->getItem();\n  }\n}; // ValueHolder specialization\n\ntemplate <typename Node, typename Allocator>\nstruct AllocNodeGuard : NonCopyableNonMovable {\n  struct DeallocateFn { // Avoids dependent lambdas in function templates\n    AllocNodeGuard& self;\n    void operator()() const noexcept {\n      self.alloc.deallocate((uint8_t*)self.node, sizeof(Node));\n    }\n  };\n\n  Allocator alloc;\n  Node* node{};\n\n  void dismiss() { node = nullptr; }\n  Node* release() { return std::exchange(node, nullptr); }\n\n  template <typename... Arg>\n  explicit AllocNodeGuard(Allocator alloc_, Arg&&... arg)\n      : alloc{std::move(alloc_)}, node{(Node*)alloc.allocate(sizeof(Node))} {\n    auto guard = makeGuard(DeallocateFn{*this});\n    new (node) Node(std::forward<Arg>(arg)...);\n    guard.dismiss();\n  }\n\n  ~AllocNodeGuard() {\n    if (node) {\n      node->~Node();\n      alloc.deallocate((uint8_t*)node, sizeof(Node));\n    }\n  }\n\n  template <typename... Arg>\n  static Node* make(Allocator alloc_, Arg&&... arg) {\n    return AllocNodeGuard(alloc_, std::forward<Arg>(arg)...).release();\n  }\n};\n\n// hazptr deleter that can use an allocator.\ntemplate <typename Allocator>\nclass HazptrDeleter {\n public:\n  template <typename Node>\n  void operator()(Node* node) {\n    node->~Node();\n    Allocator().deallocate((uint8_t*)node, sizeof(Node));\n  }\n};\n\nclass HazptrTableDeleter {\n  size_t count_;\n\n public:\n  HazptrTableDeleter(size_t count) : count_(count) {}\n  HazptrTableDeleter() = default;\n  template <typename Table>\n  void operator()(Table* table) {\n    table->destroy(count_);\n  }\n};\n\nnamespace bucket {\n\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    typename Allocator,\n    template <typename> class Atom = std::atomic>\nclass NodeT\n    : public hazptr_obj_base_linked<\n          NodeT<KeyType, ValueType, Allocator, Atom>,\n          Atom,\n          concurrenthashmap::HazptrDeleter<Allocator>> {\n public:\n  using value_type = std::pair<const KeyType, ValueType>;\n\n  explicit NodeT(hazptr_obj_cohort<Atom>* cohort, NodeT* other)\n      : item_(other->item_) {\n    init(cohort);\n  }\n\n  template <typename Arg, typename... Args>\n  NodeT(hazptr_obj_cohort<Atom>* cohort, Arg&& k, Args&&... args)\n      : item_(\n            std::piecewise_construct,\n            std::forward<Arg>(k),\n            std::forward<Args>(args)...) {\n    init(cohort);\n  }\n\n  void release() { this->unlink(); }\n\n  value_type& getItem() { return item_.getItem(); }\n\n  template <typename S>\n  void push_links(bool m, S& s) {\n    if (m) {\n      auto p = next_.load(std::memory_order_acquire);\n      if (p) {\n        s.push(p);\n      }\n    }\n  }\n\n  Atom<NodeT*> next_{nullptr};\n\n private:\n  void init(hazptr_obj_cohort<Atom>* cohort) {\n    DCHECK(cohort);\n    this->set_deleter( // defined in hazptr_obj\n        concurrenthashmap::HazptrDeleter<Allocator>());\n    this->set_cohort_tag(cohort); // defined in hazptr_obj\n    this->acquire_link_safe(); // defined in hazptr_obj_base_linked\n  }\n\n  ValueHolder<KeyType, ValueType, Allocator, Atom> item_;\n};\n\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    uint8_t ShardBits = 0,\n    typename HashFn = std::hash<KeyType>,\n    typename KeyEqual = std::equal_to<KeyType>,\n    typename Allocator = std::allocator<uint8_t>,\n    template <typename> class Atom = std::atomic,\n    class Mutex = std::mutex>\nclass alignas(64) BucketTable {\n public:\n  // Slightly higher than 1.0, in case hashing to shards isn't\n  // perfectly balanced, reserve(size) will still work without\n  // rehashing.\n  static constexpr float kDefaultLoadFactor = 1.05f;\n  using value_type = std::pair<const KeyType, ValueType>;\n\n  using Node =\n      concurrenthashmap::bucket::NodeT<KeyType, ValueType, Allocator, Atom>;\n  using InsertType = concurrenthashmap::InsertType;\n  class Iterator;\n\n  BucketTable(\n      size_t initial_buckets,\n      float load_factor,\n      size_t max_size,\n      hazptr_obj_cohort<Atom>* cohort)\n      : load_factor_(load_factor), max_size_(max_size) {\n    DCHECK(cohort);\n    initial_buckets = folly::nextPowTwo(initial_buckets);\n    DCHECK(\n        max_size_ == 0 ||\n        (isPowTwo(max_size_) &&\n         (folly::popcount(max_size_ - 1) + ShardBits <= 32)));\n    auto buckets = Buckets::create(initial_buckets, cohort);\n    buckets_.store(buckets, std::memory_order_release);\n    load_factor_nodes_ =\n        to_integral(static_cast<float>(initial_buckets) * load_factor_);\n    bucket_count_.store(initial_buckets, std::memory_order_relaxed);\n  }\n\n  ~BucketTable() {\n    auto buckets = buckets_.load(std::memory_order_relaxed);\n    // To catch use-after-destruction bugs in user code.\n    buckets_.store(nullptr, std::memory_order_release);\n    // We can delete and not retire() here, since users must have\n    // their own synchronization around destruction.\n    auto count = bucket_count_.load(std::memory_order_relaxed);\n    buckets->unlink_and_reclaim_nodes(count);\n    buckets->destroy(count);\n  }\n\n  size_t size() { return size_.load(std::memory_order_acquire); }\n\n  void clearSize() { size_.store(0, std::memory_order_release); }\n\n  void incSize() {\n    auto sz = size_.load(std::memory_order_relaxed);\n    size_.store(sz + 1, std::memory_order_release);\n  }\n\n  void decSize() {\n    auto sz = size_.load(std::memory_order_relaxed);\n    DCHECK_GT(sz, 0);\n    size_.store(sz - 1, std::memory_order_release);\n  }\n\n  bool empty() { return size() == 0; }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool insert(\n      Iterator& it,\n      size_t h,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      hazptr_obj_cohort<Atom>* cohort,\n      Args&&... args) {\n    return doInsert(\n        it, h, k, type, match, nullptr, cohort, std::forward<Args>(args)...);\n  }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool insert(\n      Iterator& it,\n      size_t h,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      Node* cur,\n      hazptr_obj_cohort<Atom>* cohort) {\n    return doInsert(it, h, k, type, match, cur, cohort, cur);\n  }\n\n  // Must hold lock.\n  void rehash(size_t bucket_count, hazptr_obj_cohort<Atom>* cohort) {\n    auto oldcount = bucket_count_.load(std::memory_order_relaxed);\n    // bucket_count must be a power of 2\n    DCHECK_EQ(bucket_count & (bucket_count - 1), 0);\n    if (bucket_count <= oldcount) {\n      return; // Rehash only if expanding.\n    }\n    auto buckets = buckets_.load(std::memory_order_relaxed);\n    DCHECK(buckets); // Use-after-destruction by user.\n    auto newbuckets = Buckets::create(bucket_count, cohort);\n    load_factor_nodes_ =\n        to_integral(static_cast<float>(bucket_count) * load_factor_);\n    for (size_t i = 0; i < oldcount; i++) {\n      auto bucket = &buckets->array()[i]();\n      auto node = bucket->load(std::memory_order_relaxed);\n      if (!node) {\n        continue;\n      }\n      auto h = HashFn()(node->getItem().first);\n      auto idx = getIdx(bucket_count, h);\n      // Reuse as long a chain as possible from the end.  Since the\n      // nodes don't have previous pointers, the longest last chain\n      // will be the same for both the previous hashmap and the new one,\n      // assuming all the nodes hash to the same bucket.\n      auto lastrun = node;\n      auto lastidx = idx;\n      auto last = node->next_.load(std::memory_order_relaxed);\n      for (; last != nullptr;\n           last = last->next_.load(std::memory_order_relaxed)) {\n        auto k = getIdx(bucket_count, HashFn()(last->getItem().first));\n        if (k != lastidx) {\n          lastidx = k;\n          lastrun = last;\n        }\n      }\n      // Set longest last run in new bucket, incrementing the refcount.\n      lastrun->acquire_link(); // defined in hazptr_obj_base_linked\n      newbuckets->array()[lastidx]().store(lastrun, std::memory_order_relaxed);\n      // Clone remaining nodes\n      for (; node != lastrun;\n           node = node->next_.load(std::memory_order_relaxed)) {\n        auto newnode = (Node*)Allocator().allocate(sizeof(Node));\n        new (newnode) Node(cohort, node);\n        auto k = getIdx(bucket_count, HashFn()(node->getItem().first));\n        auto prevhead = &newbuckets->array()[k]();\n        newnode->next_.store(prevhead->load(std::memory_order_relaxed));\n        prevhead->store(newnode, std::memory_order_relaxed);\n      }\n    }\n\n    auto oldbuckets = buckets_.load(std::memory_order_relaxed);\n    DCHECK(oldbuckets); // Use-after-destruction by user.\n    seqlock_.fetch_add(1, std::memory_order_release);\n    bucket_count_.store(bucket_count, std::memory_order_release);\n    buckets_.store(newbuckets, std::memory_order_release);\n    seqlock_.fetch_add(1, std::memory_order_release);\n    oldbuckets->retire(concurrenthashmap::HazptrTableDeleter(oldcount));\n  }\n\n  template <typename K>\n  bool find(Iterator& res, size_t h, const K& k) {\n    auto& hazcurr = res.hazptrs_[1];\n    auto& haznext = res.hazptrs_[2];\n    size_t bcount;\n    Buckets* buckets;\n    getBucketsAndCount(bcount, buckets, res.hazptrs_[0]);\n\n    auto idx = getIdx(bcount, h);\n    auto prev = &buckets->array()[idx]();\n    auto node = hazcurr.protect(*prev);\n    while (node) {\n      if (KeyEqual()(k, node->getItem().first)) {\n        res.setNode(node, buckets, bcount, idx);\n        return true;\n      }\n      node = haznext.protect(node->next_);\n      hazcurr.swap(haznext);\n    }\n    return false;\n  }\n\n  template <typename K, typename MatchFunc>\n  std::size_t erase(size_t h, const K& key, Iterator* iter, MatchFunc match) {\n    Node* node{nullptr};\n    {\n      std::lock_guard g(m_);\n\n      size_t bcount = bucket_count_.load(std::memory_order_relaxed);\n      auto buckets = buckets_.load(std::memory_order_relaxed);\n      DCHECK(buckets); // Use-after-destruction by user.\n      auto idx = getIdx(bcount, h);\n      auto head = &buckets->array()[idx]();\n      node = head->load(std::memory_order_relaxed);\n      Node* prev = nullptr;\n      while (node) {\n        if (KeyEqual()(key, node->getItem().first)) {\n          if (!match(node->getItem().second)) {\n            return 0;\n          }\n          auto next = node->next_.load(std::memory_order_relaxed);\n          if (next) {\n            next->acquire_link(); // defined in hazptr_obj_base_linked\n          }\n          if (prev) {\n            prev->next_.store(next, std::memory_order_release);\n          } else {\n            // Must be head of list.\n            head->store(next, std::memory_order_release);\n          }\n\n          if (iter) {\n            iter->hazptrs_[0].reset_protection(buckets);\n            iter->setNode(\n                node->next_.load(std::memory_order_acquire),\n                buckets,\n                bcount,\n                idx);\n            iter->next();\n          }\n          decSize();\n          break;\n        }\n        prev = node;\n        node = node->next_.load(std::memory_order_relaxed);\n      }\n    }\n    // Delete the node while not under the lock.\n    if (node) {\n      node->release();\n      return 1;\n    }\n\n    return 0;\n  }\n\n  void clear(hazptr_obj_cohort<Atom>* cohort) {\n    size_t bcount;\n    Buckets* buckets;\n    {\n      std::lock_guard g(m_);\n      bcount = bucket_count_.load(std::memory_order_relaxed);\n      auto newbuckets = Buckets::create(bcount, cohort);\n      buckets = buckets_.load(std::memory_order_relaxed);\n      buckets_.store(newbuckets, std::memory_order_release);\n      clearSize();\n    }\n    DCHECK(buckets); // Use-after-destruction by user.\n    buckets->retire(concurrenthashmap::HazptrTableDeleter(bcount));\n  }\n\n  void max_load_factor(float factor) {\n    std::lock_guard g(m_);\n    load_factor_ = factor;\n    load_factor_nodes_ =\n        bucket_count_.load(std::memory_order_relaxed) * load_factor_;\n  }\n\n  Iterator cbegin() {\n    Iterator res;\n    size_t bcount;\n    Buckets* buckets;\n    getBucketsAndCount(bcount, buckets, res.hazptrs_[0]);\n    res.setNode(nullptr, buckets, bcount, 0);\n    res.next();\n    return res;\n  }\n\n  Iterator cend() { return Iterator(nullptr); }\n\n private:\n  using BucketRoot = hazptr_root<Node, Atom>;\n  class Buckets\n      : public hazptr_obj_base<\n            Buckets,\n            Atom,\n            concurrenthashmap::HazptrTableDeleter>,\n        public AlignedStorage<Buckets, BucketRoot, Allocator> {\n    Buckets() = default;\n    ~Buckets() = default;\n\n   public:\n    static Buckets* create(size_t count, hazptr_obj_cohort<Atom>* cohort) {\n      auto* buf = Buckets::allocate(count);\n      auto* buckets = new (buf) Buckets();\n      DCHECK(cohort);\n      buckets->set_cohort_tag(cohort); // defined in hazptr_obj\n      for (size_t i = 0; i < count; i++) {\n        new (&buckets->array()[i]) BucketRoot;\n      }\n      return buckets;\n    }\n\n    void destroy(size_t count) {\n      for (size_t i = 0; i < count; i++) {\n        Buckets::array()[i].~BucketRoot();\n      }\n      this->~Buckets();\n      Buckets::deallocate(count);\n    }\n\n    void unlink_and_reclaim_nodes(size_t count) {\n      for (size_t i = 0; i < count; i++) {\n        auto node = Buckets::array()[i]().load(std::memory_order_relaxed);\n        if (node) {\n          Buckets::array()[i]().store(nullptr, std::memory_order_relaxed);\n          while (node) {\n            auto next = node->next_.load(std::memory_order_relaxed);\n            if (next) {\n              node->next_.store(nullptr, std::memory_order_relaxed);\n            }\n            node->unlink_and_reclaim_unchecked();\n            node = next;\n          }\n        }\n      }\n    }\n  };\n\n public:\n  class Iterator {\n   public:\n    FOLLY_ALWAYS_INLINE Iterator()\n        : hazptrs_(make_hazard_pointer_array<3, Atom>()) {}\n    FOLLY_ALWAYS_INLINE explicit Iterator(std::nullptr_t) : hazptrs_() {}\n    FOLLY_ALWAYS_INLINE ~Iterator() {}\n\n    void setNode(\n        Node* node, Buckets* buckets, size_t bucket_count, uint64_t idx) {\n      node_ = node;\n      buckets_ = buckets;\n      idx_ = idx;\n      bucket_count_ = bucket_count;\n    }\n\n    const value_type& operator*() const {\n      DCHECK(node_);\n      return node_->getItem();\n    }\n\n    const value_type* operator->() const {\n      DCHECK(node_);\n      return &(node_->getItem());\n    }\n\n    const Iterator& operator++() {\n      DCHECK(node_);\n      node_ = hazptrs_[2].protect(node_->next_);\n      hazptrs_[1].swap(hazptrs_[2]);\n      if (!node_) {\n        ++idx_;\n        next();\n      }\n      return *this;\n    }\n\n    void next() {\n      while (!node_) {\n        if (idx_ >= bucket_count_) {\n          break;\n        }\n        DCHECK(buckets_);\n        node_ = hazptrs_[1].protect(buckets_->array()[idx_]());\n        if (node_) {\n          break;\n        }\n        ++idx_;\n      }\n    }\n\n    bool operator==(const Iterator& o) const { return node_ == o.node_; }\n\n    bool operator!=(const Iterator& o) const { return !(*this == o); }\n\n    Iterator& operator=(const Iterator& o) = delete;\n\n    Iterator& operator=(Iterator&& o) noexcept {\n      if (this != &o) {\n        hazptrs_ = std::move(o.hazptrs_);\n        node_ = std::exchange(o.node_, nullptr);\n        buckets_ = std::exchange(o.buckets_, nullptr);\n        bucket_count_ = std::exchange(o.bucket_count_, 0);\n        idx_ = std::exchange(o.idx_, 0);\n      }\n      return *this;\n    }\n\n    Iterator(const Iterator& o) = delete;\n\n    Iterator(Iterator&& o) noexcept\n        : hazptrs_(std::move(o.hazptrs_)),\n          node_(std::exchange(o.node_, nullptr)),\n          buckets_(std::exchange(o.buckets_, nullptr)),\n          bucket_count_(std::exchange(o.bucket_count_, 0)),\n          idx_(std::exchange(o.idx_, 0)) {}\n\n    // These are accessed directly from the functions above\n    hazptr_array<3, Atom> hazptrs_;\n\n   private:\n    Node* node_{nullptr};\n    Buckets* buckets_{nullptr};\n    size_t bucket_count_{0};\n    uint64_t idx_{0};\n  };\n\n private:\n  // Shards have already used low ShardBits of the hash.\n  // Shift it over to use fresh bits.\n  uint64_t getIdx(size_t bucket_count, size_t hash) {\n    return (hash >> ShardBits) & (bucket_count - 1);\n  }\n  void getBucketsAndCount(\n      size_t& bcount, Buckets*& buckets, hazptr_holder<Atom>& hazptr) {\n    while (true) {\n      auto seqlock = seqlock_.load(std::memory_order_acquire);\n      bcount = bucket_count_.load(std::memory_order_acquire);\n      buckets = hazptr.protect(buckets_);\n      auto seqlock2 = seqlock_.load(std::memory_order_acquire);\n      if (!(seqlock & 1) && (seqlock == seqlock2)) {\n        break;\n      }\n    }\n    DCHECK(buckets) << \"Use-after-destruction by user.\";\n  }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool doInsert(\n      Iterator& it,\n      size_t h,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      Node* cur,\n      hazptr_obj_cohort<Atom>* cohort,\n      Args&&... args) {\n    std::unique_lock g(m_);\n\n    size_t bcount = bucket_count_.load(std::memory_order_relaxed);\n    auto buckets = buckets_.load(std::memory_order_relaxed);\n    // Check for rehash needed for DOES_NOT_EXIST\n    if (size() >= load_factor_nodes_ &&\n        (type == InsertType::DOES_NOT_EXIST ||\n         type == InsertType::MATCH_OR_DOES_NOT_EXIST)) {\n      if (max_size_ && size() << 1 > max_size_) {\n        // Would exceed max size.\n        throw_exception<std::bad_alloc>();\n      }\n      rehash(bcount << 1, cohort);\n      buckets = buckets_.load(std::memory_order_relaxed);\n      bcount = bucket_count_.load(std::memory_order_relaxed);\n    }\n\n    DCHECK(buckets) << \"Use-after-destruction by user.\";\n    auto idx = getIdx(bcount, h);\n    auto head = &buckets->array()[idx]();\n    auto node = head->load(std::memory_order_relaxed);\n    auto headnode = node;\n    auto prev = head;\n    auto& hazbuckets = it.hazptrs_[0];\n    auto& haznode = it.hazptrs_[1];\n    hazbuckets.reset_protection(buckets);\n    bool matched = false;\n    while (node) {\n      // Is the key found?\n      if (KeyEqual()(k, node->getItem().first)) {\n        it.setNode(node, buckets, bcount, idx);\n        haznode.reset_protection(node);\n        if (type == InsertType::MATCH ||\n            type == InsertType::MATCH_OR_DOES_NOT_EXIST) {\n          if (!match(node->getItem().second)) {\n            return false;\n          }\n          matched = true;\n        }\n        if (type == InsertType::DOES_NOT_EXIST ||\n            (type == InsertType::MATCH_OR_DOES_NOT_EXIST && !matched)) {\n          return false;\n        } else {\n          if (!cur) {\n            cur = AllocNodeGuard<Node, Allocator>::make(\n                Allocator(), cohort, std::forward<Args>(args)...);\n          }\n          auto next = node->next_.load(std::memory_order_relaxed);\n          cur->next_.store(next, std::memory_order_relaxed);\n          if (next) {\n            next->acquire_link(); // defined in hazptr_obj_base_linked\n          }\n          prev->store(cur, std::memory_order_release);\n          it.setNode(cur, buckets, bcount, idx);\n          haznode.reset_protection(cur);\n          g.unlock();\n          // Release not under lock.\n          node->release();\n          return true;\n        }\n      }\n\n      prev = &node->next_;\n      node = node->next_.load(std::memory_order_relaxed);\n    }\n    if (type != InsertType::DOES_NOT_EXIST &&\n        (type != InsertType::MATCH_OR_DOES_NOT_EXIST || matched) &&\n        type != InsertType::ANY) {\n      haznode.reset_protection();\n      hazbuckets.reset_protection();\n      return false;\n    }\n    // Node not found, check for rehash on ANY\n    if (size() >= load_factor_nodes_ && type == InsertType::ANY) {\n      if (max_size_ && size() << 1 > max_size_) {\n        // Would exceed max size.\n        throw_exception<std::bad_alloc>();\n      }\n      rehash(bcount << 1, cohort);\n\n      // Reload correct bucket.\n      buckets = buckets_.load(std::memory_order_relaxed);\n      DCHECK(buckets); // Use-after-destruction by user.\n      bcount <<= 1;\n      hazbuckets.reset_protection(buckets);\n      idx = getIdx(bcount, h);\n      head = &buckets->array()[idx]();\n      headnode = head->load(std::memory_order_relaxed);\n    }\n\n    // We found a slot to put the node.\n    incSize();\n    if (!cur) {\n      // InsertType::ANY\n      // OR DOES_NOT_EXIST, but only in the try_emplace case\n      DCHECK(\n          type == InsertType::ANY || type == InsertType::DOES_NOT_EXIST ||\n          (type == InsertType::MATCH_OR_DOES_NOT_EXIST && !matched));\n      cur = AllocNodeGuard<Node, Allocator>::make(\n          Allocator(), cohort, std::forward<Args>(args)...);\n    }\n    cur->next_.store(headnode, std::memory_order_relaxed);\n    head->store(cur, std::memory_order_release);\n    it.setNode(cur, buckets, bcount, idx);\n    haznode.reset_protection(cur);\n    return true;\n  }\n\n  Mutex m_;\n  float load_factor_;\n  size_t load_factor_nodes_;\n  Atom<size_t> size_{0};\n  size_t const max_size_;\n\n  // Fields needed for read-only access, on separate cacheline.\n  alignas(64) Atom<Buckets*> buckets_{nullptr};\n  std::atomic<uint64_t> seqlock_{0};\n  Atom<size_t> bucket_count_;\n};\n\n} // namespace bucket\n\n#if (                                                        \\\n    FOLLY_SSE_PREREQ(4, 2) ||                                \\\n    (FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE)) && \\\n    FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nnamespace simd {\n\nusing folly::f14::detail::DenseMaskIter;\nusing folly::f14::detail::FirstEmptyInMask;\nusing folly::f14::detail::FullMask;\nusing folly::f14::detail::MaskType;\nusing folly::f14::detail::SparseMaskIter;\n\nusing folly::hazptr_obj_base;\nusing folly::hazptr_obj_cohort;\n\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    typename Allocator,\n    template <typename> class Atom = std::atomic>\nclass NodeT\n    : public hazptr_obj_base<\n          NodeT<KeyType, ValueType, Allocator, Atom>,\n          Atom,\n          HazptrDeleter<Allocator>> {\n public:\n  using value_type = std::pair<const KeyType, ValueType>;\n\n  template <typename Arg, typename... Args>\n  NodeT(hazptr_obj_cohort<Atom>* cohort, Arg&& k, Args&&... args)\n      : item_(\n            std::piecewise_construct,\n            std::forward_as_tuple(std::forward<Arg>(k)),\n            std::forward_as_tuple(std::forward<Args>(args)...)) {\n    init(cohort);\n  }\n\n  value_type& getItem() { return item_; }\n\n private:\n  void init(hazptr_obj_cohort<Atom>* cohort) {\n    DCHECK(cohort);\n    this->set_deleter( // defined in hazptr_obj\n        HazptrDeleter<Allocator>());\n    this->set_cohort_tag(cohort); // defined in hazptr_obj\n  }\n\n  value_type item_;\n};\n\nconstexpr std::size_t kRequiredVectorAlignment =\n    constexpr_max(std::size_t{16}, alignof(max_align_t));\n\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    uint8_t ShardBits = 0,\n    typename HashFn = std::hash<KeyType>,\n    typename KeyEqual = std::equal_to<KeyType>,\n    typename Allocator = std::allocator<uint8_t>,\n    template <typename> class Atom = std::atomic,\n    class Mutex = std::mutex>\nclass alignas(64) SIMDTable {\n public:\n  using Node =\n      concurrenthashmap::simd::NodeT<KeyType, ValueType, Allocator, Atom>;\n\n private:\n  using HashPair = std::pair<std::size_t, std::size_t>;\n  struct alignas(kRequiredVectorAlignment) Chunk {\n    static constexpr unsigned kCapacity = 14;\n    static constexpr unsigned kDesiredCapacity = 12;\n\n    static constexpr MaskType kFullMask = FullMask<kCapacity>::value;\n\n   private:\n    // Non-empty tags have their top bit set.\n\n    // tags [0,8)\n    Atom<uint64_t> tags_low_;\n\n    // tags_hi_ holds tags [8,14), hostedOverflowCount and outboundOverflowCount\n\n    // hostedOverflowCount: the number of values in this chunk that were placed\n    // because they overflowed their desired chunk.\n\n    // outboundOverflowCount: num values that would have been placed into this\n    // chunk if there had been space, including values that also overflowed\n    // previous full chunks.  This value saturates; once it becomes 255 it no\n    // longer increases nor decreases.\n\n    // Note: more bits can be used for outboundOverflowCount if this\n    // optimization becomes useful\n    Atom<uint64_t> tags_hi_;\n\n    std::array<aligned_storage_for_t<Atom<Node*>>, kCapacity> rawItems_;\n\n   public:\n    void clear() {\n      for (size_t i = 0; i < kCapacity; i++) {\n        item(i).store(nullptr, std::memory_order_relaxed);\n      }\n      tags_low_.store(0, std::memory_order_relaxed);\n      tags_hi_.store(0, std::memory_order_relaxed);\n    }\n\n    std::size_t tag(std::size_t index) const {\n      std::size_t off = index % 8;\n      const Atom<uint64_t>& tag_src = off == index ? tags_low_ : tags_hi_;\n      uint64_t tags = tag_src.load(std::memory_order_relaxed);\n      tags >>= (off * 8);\n      return tags & 0xff;\n    }\n\n    void setTag(std::size_t index, std::size_t tag) {\n      std::size_t off = index % 8;\n      Atom<uint64_t>& old_tags = off == index ? tags_low_ : tags_hi_;\n      uint64_t new_tags = old_tags.load(std::memory_order_relaxed);\n      uint64_t mask = 0xffULL << (off * 8);\n      new_tags = (new_tags & ~mask) | (tag << (off * 8));\n      old_tags.store(new_tags, std::memory_order_release);\n    }\n\n    void setNodeAndTag(std::size_t index, Node* node, std::size_t tag) {\n      FOLLY_SAFE_DCHECK(\n          index < kCapacity && (tag == 0x0 || (tag >= 0x80 && tag <= 0xff)),\n          \"\");\n      item(index).store(node, std::memory_order_release);\n      setTag(index, tag);\n    }\n\n    void clearNodeAndTag(std::size_t index) {\n      setNodeAndTag(index, nullptr, 0);\n    }\n\n#ifdef __aarch64__\n\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n\n    ////////\n    // Tag filtering using SVE intrinsics\n\n    SparseMaskIter tagMatchIter(std::size_t needle, svbool_t& outPred) const {\n      FOLLY_SAFE_DCHECK(needle >= 0x80 && needle < 0x100, \"\");\n      uint64_t low = tags_low_.load(std::memory_order_acquire);\n      uint64_t hi = tags_hi_.load(std::memory_order_acquire);\n      svuint8_t needleV = svdup_n_u8(static_cast<uint8_t>(needle));\n      svbool_t pred = svwhilelt_b8_u32(0, kCapacity);\n      uint64x2_t vec;\n      vec[0] = low;\n      vec[1] = hi;\n      // test if any match is found\n      outPred = svmatch_u8(pred, svset_neonq_u8(svundef_u8(), vec), needleV);\n      // get info from every byte into the bottom half of every uint16_t\n      // by shifting right 4, then round to get it into a 64-bit vector\n      uint8x8_t maskV = vshrn_n_u16(\n          vreinterpretq_u16_u8(svget_neonq(svdup_n_u8_z(outPred, 17))), 4);\n      uint64_t mask = vreinterpret_u64_u8(maskV)[0];\n      return SparseMaskIter{mask};\n    }\n\n#else\n\n    ////////\n    // Tag filtering using NEON intrinsics\n\n    SparseMaskIter tagMatchIter(std::size_t needle) const {\n      FOLLY_SAFE_DCHECK(needle >= 0x80 && needle < 0x100, \"\");\n      uint64_t low = tags_low_.load(std::memory_order_acquire);\n      uint64_t hi = tags_hi_.load(std::memory_order_acquire);\n      uint8x16_t needleV = vdupq_n_u8(static_cast<uint8_t>(needle));\n      uint64x2_t vec;\n      vec[0] = low;\n      vec[1] = hi;\n      auto eqV = vceqq_u8(vreinterpretq_u8_u64(vec), needleV);\n      uint8x8_t maskV = vshrn_n_u16(vreinterpretq_u16_u8(eqV), 4);\n      uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(maskV), 0) & kFullMask;\n      return SparseMaskIter{mask};\n    }\n\n#endif\n\n    MaskType occupiedMask() const {\n      uint64_t low = tags_low_.load(std::memory_order_relaxed);\n      uint64_t hi = tags_hi_.load(std::memory_order_relaxed);\n      uint64x2_t vec;\n      vec[0] = low;\n      vec[1] = hi;\n      // signed shift extends top bit to all bits\n      auto occupiedV =\n          vreinterpretq_u8_s8(vshrq_n_s8(vreinterpretq_s8_u64(vec), 7));\n      uint8x8_t maskV = vshrn_n_u16(vreinterpretq_u16_u8(occupiedV), 4);\n      return vget_lane_u64(vreinterpret_u64_u8(maskV), 0) & kFullMask;\n    }\n\n#else\n\n    ////////\n    // Tag filtering using SSE2 intrinsics\n\n    SparseMaskIter tagMatchIter(std::size_t needle) const {\n      FOLLY_SAFE_DCHECK(needle >= 0x80 && needle < 0x100, \"\");\n      uint64_t low = tags_low_.load(std::memory_order_acquire);\n      uint64_t hi = tags_hi_.load(std::memory_order_acquire);\n      auto tagV = _mm_set_epi64x(hi, low);\n      auto needleV = _mm_set1_epi8(static_cast<uint8_t>(needle));\n      auto eqV = _mm_cmpeq_epi8(tagV, needleV);\n      auto mask = _mm_movemask_epi8(eqV) & kFullMask;\n      return SparseMaskIter{mask};\n    }\n\n    MaskType occupiedMask() const {\n      uint64_t low = tags_low_.load(std::memory_order_relaxed);\n      uint64_t hi = tags_hi_.load(std::memory_order_relaxed);\n      auto tagV = _mm_set_epi64x(hi, low);\n      return _mm_movemask_epi8(tagV) & kFullMask;\n    }\n\n#endif\n\n    DenseMaskIter occupiedIter() const {\n      // Currently only invoked when relaxed semantics are sufficient.\n      return DenseMaskIter{nullptr /*unused*/, occupiedMask()};\n    }\n\n    FirstEmptyInMask firstEmpty() const {\n      return FirstEmptyInMask{occupiedMask() ^ kFullMask};\n    }\n\n    Atom<Node*>* itemAddr(std::size_t i) const {\n      return static_cast<Atom<Node*>*>(\n          const_cast<void*>(static_cast<void const*>(&rawItems_[i])));\n    }\n\n    Atom<Node*>& item(size_t i) { return *std::launder(itemAddr(i)); }\n\n    static constexpr uint64_t kOutboundOverflowIndex = 7 * 8;\n    static constexpr uint64_t kSaturatedOutboundOverflowCount = 0xffULL\n        << kOutboundOverflowIndex;\n    static constexpr uint64_t kOutboundOverflowOperand = 0x1ULL\n        << kOutboundOverflowIndex;\n\n    unsigned outboundOverflowCount() const {\n      uint64_t count = tags_hi_.load(std::memory_order_relaxed);\n      return count >> kOutboundOverflowIndex;\n    }\n\n    void incrOutboundOverflowCount() {\n      uint64_t count = tags_hi_.load(std::memory_order_relaxed);\n      if (count < kSaturatedOutboundOverflowCount) {\n        tags_hi_.store(\n            count + kOutboundOverflowOperand, std::memory_order_relaxed);\n      }\n    }\n\n    void decrOutboundOverflowCount() {\n      uint64_t count = tags_hi_.load(std::memory_order_relaxed);\n      if (count < kSaturatedOutboundOverflowCount) {\n        tags_hi_.store(\n            count - kOutboundOverflowOperand, std::memory_order_relaxed);\n      }\n    }\n\n    static constexpr uint64_t kHostedOverflowIndex = 6 * 8;\n    static constexpr uint64_t kHostedOverflowOperand = 0x10ULL\n        << kHostedOverflowIndex;\n\n    unsigned hostedOverflowCount() const {\n      uint64_t control = tags_hi_.load(std::memory_order_relaxed);\n      return (control >> 52) & 0xf;\n    }\n\n    void incrHostedOverflowCount() {\n      tags_hi_.fetch_add(kHostedOverflowOperand, std::memory_order_relaxed);\n    }\n\n    void decrHostedOverflowCount() {\n      tags_hi_.fetch_sub(kHostedOverflowOperand, std::memory_order_relaxed);\n    }\n  };\n\n  class Chunks\n      : public hazptr_obj_base<Chunks, Atom, HazptrTableDeleter>,\n        public AlignedStorage<Chunks, Chunk, Allocator> {\n    Chunks() = default;\n    ~Chunks() = default;\n\n   public:\n    static Chunks* create(size_t count, hazptr_obj_cohort<Atom>* cohort) {\n      auto* buf = Chunks::allocate(count);\n      auto* chunks = new (buf) Chunks();\n      DCHECK(cohort);\n      chunks->set_cohort_tag(cohort); // defined in hazptr_obj\n      for (size_t i = 0; i < count; i++) {\n        new (&chunks->array()[i]) Chunk;\n        chunks->array()[i].clear();\n      }\n      return chunks;\n    }\n\n    void destroy(size_t count) {\n      for (size_t i = 0; i < count; i++) {\n        Chunks::array()[i].~Chunk();\n      }\n      this->~Chunks();\n      Chunks::deallocate(count);\n    }\n\n    void reclaim_nodes(size_t count) {\n      for (size_t i = 0; i < count; i++) {\n        Chunk& chunk = Chunks::array()[i];\n        auto occupied = chunk.occupiedIter();\n        while (occupied.hasNext()) {\n          auto idx = occupied.next();\n          chunk.setTag(idx, 0);\n          Node* node =\n              chunk.item(idx).exchange(nullptr, std::memory_order_relaxed);\n          // Tags and node ptrs should be in sync at this point.\n          DCHECK(node);\n          node->retire();\n        }\n      }\n    }\n\n    Chunk* getChunk(size_t index, size_t ccount) {\n      DCHECK(isPowTwo(ccount));\n      return &Chunks::array()[index & (ccount - 1)];\n    }\n  };\n\n public:\n  static constexpr float kDefaultLoadFactor =\n      Chunk::kDesiredCapacity / (float)Chunk::kCapacity;\n\n  using value_type = std::pair<const KeyType, ValueType>;\n\n  using InsertType = concurrenthashmap::InsertType;\n\n  class Iterator {\n   public:\n    FOLLY_ALWAYS_INLINE Iterator()\n        : hazptrs_(make_hazard_pointer_array<2, Atom>()) {}\n    FOLLY_ALWAYS_INLINE explicit Iterator(std::nullptr_t) : hazptrs_() {}\n    FOLLY_ALWAYS_INLINE ~Iterator() {}\n\n    void setNode(\n        Node* node,\n        Chunks* chunks,\n        size_t chunk_count,\n        uint64_t chunk_idx,\n        uint64_t tag_idx) {\n      DCHECK(chunk_idx < chunk_count || chunk_idx == 0);\n      DCHECK(isPowTwo(chunk_count));\n      node_ = node;\n      chunks_ = chunks;\n      chunk_count_ = chunk_count;\n      chunk_idx_ = chunk_idx;\n      tag_idx_ = tag_idx;\n    }\n\n    const value_type& operator*() const {\n      DCHECK(node_);\n      return node_->getItem();\n    }\n\n    const value_type* operator->() const {\n      DCHECK(node_);\n      return &(node_->getItem());\n    }\n\n    const Iterator& operator++() {\n      DCHECK(node_);\n      ++tag_idx_;\n      findNextNode();\n      return *this;\n    }\n\n    void next() {\n      if (node_) {\n        return;\n      }\n      findNextNode();\n    }\n\n    bool operator==(const Iterator& o) const { return node_ == o.node_; }\n\n    bool operator!=(const Iterator& o) const { return !(*this == o); }\n\n    Iterator& operator=(const Iterator& o) = delete;\n\n    Iterator& operator=(Iterator&& o) noexcept {\n      if (this != &o) {\n        hazptrs_ = std::move(o.hazptrs_);\n        node_ = std::exchange(o.node_, nullptr);\n        chunks_ = std::exchange(o.chunks_, nullptr);\n        chunk_count_ = std::exchange(o.chunk_count_, 0);\n        chunk_idx_ = std::exchange(o.chunk_idx_, 0);\n        tag_idx_ = std::exchange(o.tag_idx_, 0);\n      }\n      return *this;\n    }\n\n    Iterator(const Iterator& o) = delete;\n\n    Iterator(Iterator&& o) noexcept\n        : hazptrs_(std::move(o.hazptrs_)),\n          node_(std::exchange(o.node_, nullptr)),\n          chunks_(std::exchange(o.chunks_, nullptr)),\n          chunk_count_(std::exchange(o.chunk_count_, 0)),\n          chunk_idx_(std::exchange(o.chunk_idx_, 0)),\n          tag_idx_(std::exchange(o.tag_idx_, 0)) {}\n\n    // These are accessed directly from the functions above\n    hazptr_array<2, Atom> hazptrs_;\n\n   private:\n    void findNextNode() {\n      do {\n        if (tag_idx_ >= Chunk::kCapacity) {\n          tag_idx_ = 0;\n          ++chunk_idx_;\n        }\n        if (chunk_idx_ >= chunk_count_) {\n          node_ = nullptr;\n          break;\n        }\n        DCHECK(chunks_);\n        // Note that iteration could also be implemented with tag filtering\n        node_ = hazptrs_[1].protect(\n            chunks_->getChunk(chunk_idx_, chunk_count_)->item(tag_idx_));\n        if (node_) {\n          break;\n        }\n        ++tag_idx_;\n      } while (true);\n    }\n\n    Node* node_{nullptr};\n    Chunks* chunks_{nullptr};\n    size_t chunk_count_{0};\n    uint64_t chunk_idx_{0};\n    uint64_t tag_idx_{0};\n  };\n\n  SIMDTable(\n      size_t initial_size,\n      float load_factor,\n      size_t max_size,\n      hazptr_obj_cohort<Atom>* cohort)\n      : load_factor_(load_factor),\n        max_size_(max_size),\n        chunks_(nullptr),\n        chunk_count_(0) {\n    DCHECK(cohort);\n    DCHECK(\n        max_size_ == 0 ||\n        (isPowTwo(max_size_) &&\n         (folly::popcount(max_size_ - 1) + ShardBits <= 32)));\n    DCHECK(load_factor_ > 0.0);\n    load_factor_ = std::min<float>(load_factor_, 1.0);\n    rehash(initial_size, cohort);\n  }\n\n  ~SIMDTable() {\n    auto chunks = chunks_.load(std::memory_order_relaxed);\n    // To catch use-after-destruction bugs in user code.\n    chunks_.store(nullptr, std::memory_order_release);\n    // We can delete and not retire() here, since users must have\n    // their own synchronization around destruction.\n    auto count = chunk_count_.load(std::memory_order_relaxed);\n    chunks->reclaim_nodes(count);\n    chunks->destroy(count);\n  }\n\n  size_t size() { return size_.load(std::memory_order_acquire); }\n\n  void clearSize() { size_.store(0, std::memory_order_release); }\n\n  void incSize() {\n    auto sz = size_.load(std::memory_order_relaxed);\n    size_.store(sz + 1, std::memory_order_release);\n  }\n\n  void decSize() {\n    auto sz = size_.load(std::memory_order_relaxed);\n    DCHECK_GT(sz, 0);\n    size_.store(sz - 1, std::memory_order_release);\n  }\n\n  bool empty() { return size() == 0; }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool insert(\n      Iterator& it,\n      size_t h,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      hazptr_obj_cohort<Atom>* cohort,\n      Args&&... args) {\n    Node* node;\n    Chunks* chunks;\n    size_t ccount, chunk_idx, tag_idx;\n\n    auto hp = splitHash(h);\n\n    std::unique_lock g(m_);\n\n    if (!prepare_insert(\n            it,\n            k,\n            type,\n            match,\n            cohort,\n            chunk_idx,\n            tag_idx,\n            node,\n            chunks,\n            ccount,\n            hp)) {\n      return false;\n    }\n\n    auto cur = AllocNodeGuard<Node, Allocator>::make(\n        Allocator(), cohort, std::forward<Args>(args)...);\n\n    if (!node) {\n      std::tie(chunk_idx, tag_idx) =\n          findEmptyInsertLocation(chunks, ccount, hp);\n      incSize();\n    }\n\n    Chunk* chunk = chunks->getChunk(chunk_idx, ccount);\n    chunk->setNodeAndTag(tag_idx, cur, hp.second);\n    it.setNode(cur, chunks, ccount, chunk_idx, tag_idx);\n    it.hazptrs_[1].reset_protection(cur);\n\n    g.unlock();\n    // Retire not under lock\n    if (node) {\n      node->retire();\n    }\n    return true;\n  }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool insert(\n      Iterator& it,\n      size_t h,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      Node* cur,\n      hazptr_obj_cohort<Atom>* cohort) {\n    DCHECK(cur != nullptr);\n    Node* node;\n    Chunks* chunks;\n    size_t ccount, chunk_idx, tag_idx;\n\n    auto hp = splitHash(h);\n\n    std::unique_lock g(m_);\n\n    if (!prepare_insert(\n            it,\n            k,\n            type,\n            match,\n            cohort,\n            chunk_idx,\n            tag_idx,\n            node,\n            chunks,\n            ccount,\n            hp)) {\n      return false;\n    }\n\n    if (!node) {\n      std::tie(chunk_idx, tag_idx) =\n          findEmptyInsertLocation(chunks, ccount, hp);\n      incSize();\n    }\n\n    Chunk* chunk = chunks->getChunk(chunk_idx, ccount);\n    chunk->setNodeAndTag(tag_idx, cur, hp.second);\n    it.setNode(cur, chunks, ccount, chunk_idx, tag_idx);\n    it.hazptrs_[1].reset_protection(cur);\n\n    g.unlock();\n    // Retire not under lock\n    if (node) {\n      node->retire();\n    }\n    return true;\n  }\n\n  void rehash(size_t size, hazptr_obj_cohort<Atom>* cohort) {\n    size_t new_chunk_count = size == 0 ? 0 : (size - 1) / Chunk::kCapacity + 1;\n    rehash_internal(folly::nextPowTwo(new_chunk_count), cohort);\n  }\n\n  template <typename K>\n  bool find(Iterator& res, size_t h, const K& k) {\n    auto& hazz = res.hazptrs_[1];\n    auto hp = splitHash(h);\n    size_t ccount;\n    Chunks* chunks;\n    getChunksAndCount(ccount, chunks, res.hazptrs_[0]);\n\n    size_t step = probeDelta(hp);\n    auto& chunk_idx = hp.first;\n    for (size_t tries = 0; tries < ccount; ++tries) {\n      Chunk* chunk = chunks->getChunk(chunk_idx, ccount);\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n      svbool_t outPred;\n      auto hits = chunk->tagMatchIter(hp.second, outPred);\n      bool hasHits = svptest_any(outPred, outPred);\n      FOLLY_SAFE_DCHECK(hasHits == hits.hasNext());\n#else\n      auto hits = chunk->tagMatchIter(hp.second);\n      bool hasHits = hits.hasNext();\n#endif\n      if (hasHits) {\n        do {\n          size_t tag_idx = hits.next();\n          Node* node = hazz.protect(chunk->item(tag_idx));\n          if (FOLLY_LIKELY(node && KeyEqual()(k, node->getItem().first))) {\n            chunk_idx = chunk_idx & (ccount - 1);\n            res.setNode(node, chunks, ccount, chunk_idx, tag_idx);\n            return true;\n          }\n          hazz.reset_protection();\n        } while (hits.hasNext());\n      }\n\n      if (FOLLY_LIKELY(chunk->outboundOverflowCount() == 0)) {\n        break;\n      }\n      chunk_idx += step;\n    }\n    return false;\n  }\n\n  template <typename K, typename MatchFunc>\n  std::size_t erase(size_t h, const K& key, Iterator* iter, MatchFunc match) {\n    const HashPair hp = splitHash(h);\n\n    std::unique_lock g(m_);\n\n    size_t ccount = chunk_count_.load(std::memory_order_relaxed);\n    auto chunks = chunks_.load(std::memory_order_relaxed);\n    DCHECK(chunks); // Use-after-destruction by user.\n    size_t chunk_idx, tag_idx;\n\n    Node* node = find_internal(key, hp, chunks, ccount, chunk_idx, tag_idx);\n\n    if (!node) {\n      return 0;\n    }\n\n    if (!match(node->getItem().second)) {\n      return 0;\n    }\n\n    Chunk* chunk = chunks->getChunk(chunk_idx, ccount);\n\n    // Decrement any overflow counters\n    if (chunk->hostedOverflowCount() != 0) {\n      size_t index = hp.first;\n      size_t delta = probeDelta(hp);\n      bool preferredChunk = true;\n      while (true) {\n        Chunk* overflowChunk = chunks->getChunk(index, ccount);\n        if (chunk == overflowChunk) {\n          if (!preferredChunk) {\n            overflowChunk->decrHostedOverflowCount();\n          }\n          break;\n        }\n        overflowChunk->decrOutboundOverflowCount();\n        preferredChunk = false;\n        index += delta;\n      }\n    }\n\n    chunk->clearNodeAndTag(tag_idx);\n\n    decSize();\n    if (iter) {\n      iter->hazptrs_[0].reset_protection(chunks);\n      iter->setNode(nullptr, chunks, ccount, chunk_idx, tag_idx + 1);\n      iter->next();\n    }\n    // Retire the node while not under the lock.\n    g.unlock();\n    node->retire();\n    return 1;\n  }\n\n  void clear(hazptr_obj_cohort<Atom>* cohort) {\n    size_t ccount;\n    Chunks* chunks;\n    {\n      std::lock_guard g(m_);\n      ccount = chunk_count_.load(std::memory_order_relaxed);\n      auto newchunks = Chunks::create(ccount, cohort);\n      chunks = chunks_.load(std::memory_order_relaxed);\n      chunks_.store(newchunks, std::memory_order_release);\n      clearSize();\n    }\n    DCHECK(chunks); // Use-after-destruction by user.\n    chunks->reclaim_nodes(ccount);\n    chunks->retire(HazptrTableDeleter(ccount));\n  }\n\n  void max_load_factor(float factor) {\n    DCHECK(factor > 0.0);\n    if (factor > 1.0) {\n      throw_exception<std::invalid_argument>(\"load factor must be <= 1.0\");\n    }\n    std::lock_guard g(m_);\n    load_factor_ = factor;\n    auto ccount = chunk_count_.load(std::memory_order_relaxed);\n    grow_threshold_ = ccount * Chunk::kCapacity * load_factor_;\n  }\n\n  Iterator cbegin() {\n    Iterator res;\n    size_t ccount;\n    Chunks* chunks;\n    getChunksAndCount(ccount, chunks, res.hazptrs_[0]);\n    res.setNode(nullptr, chunks, ccount, 0, 0);\n    res.next();\n    return res;\n  }\n\n  Iterator cend() { return Iterator(nullptr); }\n\n private:\n  static HashPair splitHash(std::size_t hash) {\n#ifdef __aarch64__\n    std::size_t c = __crc32cd(0, hash);\n#else\n    std::size_t c = _mm_crc32_u64(0, hash);\n#endif\n    size_t tag = (c >> 24) | 0x80;\n    hash += c;\n    return std::make_pair(hash, tag);\n  }\n\n  static size_t probeDelta(HashPair hp) { return 2 * hp.second + 1; }\n\n  // Must hold lock.\n  template <typename K>\n  Node* find_internal(\n      const K& k,\n      const HashPair& hp,\n      Chunks* chunks,\n      size_t ccount,\n      size_t& chunk_idx,\n      size_t& tag_idx) {\n    // must be called with mutex held\n    size_t step = probeDelta(hp);\n    chunk_idx = hp.first;\n\n    for (size_t tries = 0; tries < ccount; ++tries) {\n      Chunk* chunk = chunks->getChunk(chunk_idx, ccount);\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n      svbool_t outPred;\n      auto hits = chunk->tagMatchIter(hp.second, outPred);\n      bool hasHits = svptest_any(outPred, outPred);\n      FOLLY_SAFE_DCHECK(hasHits == hits.hasNext());\n#else\n      auto hits = chunk->tagMatchIter(hp.second);\n      bool hasHits = hits.hasNext();\n#endif\n      if (hasHits) {\n        do {\n          tag_idx = hits.next();\n          Node* node = chunk->item(tag_idx).load(std::memory_order_acquire);\n          if (FOLLY_LIKELY(node && KeyEqual()(k, node->getItem().first))) {\n            chunk_idx = (chunk_idx & (ccount - 1));\n            return node;\n          }\n        } while (hits.hasNext());\n      }\n      if (FOLLY_LIKELY(chunk->outboundOverflowCount() == 0)) {\n        break;\n      }\n      chunk_idx += step;\n    }\n    return nullptr;\n  }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool prepare_insert(\n      Iterator& it,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      hazptr_obj_cohort<Atom>* cohort,\n      size_t& chunk_idx,\n      size_t& tag_idx,\n      Node*& node,\n      Chunks*& chunks,\n      size_t& ccount,\n      const HashPair& hp) {\n    ccount = chunk_count_.load(std::memory_order_relaxed);\n    chunks = chunks_.load(std::memory_order_relaxed);\n\n    if (size() >= grow_threshold_ &&\n        (type == InsertType::DOES_NOT_EXIST ||\n         type == InsertType::MATCH_OR_DOES_NOT_EXIST)) {\n      if (max_size_ && size() << 1 > max_size_) {\n        // Would exceed max size.\n        throw_exception<std::bad_alloc>();\n      }\n      rehash_internal(ccount << 1, cohort);\n      ccount = chunk_count_.load(std::memory_order_relaxed);\n      chunks = chunks_.load(std::memory_order_relaxed);\n    }\n\n    DCHECK(chunks); // Use-after-destruction by user.\n    node = find_internal(k, hp, chunks, ccount, chunk_idx, tag_idx);\n\n    it.hazptrs_[0].reset_protection(chunks);\n    if (node) {\n      it.hazptrs_[1].reset_protection(node);\n      it.setNode(node, chunks, ccount, chunk_idx, tag_idx);\n      if (type == InsertType::MATCH ||\n          type == InsertType::MATCH_OR_DOES_NOT_EXIST) {\n        if (!match(node->getItem().second)) {\n          return false;\n        }\n      } else if (type == InsertType::DOES_NOT_EXIST) {\n        return false;\n      }\n    } else {\n      if (type != InsertType::DOES_NOT_EXIST &&\n          type != InsertType::MATCH_OR_DOES_NOT_EXIST &&\n          type != InsertType::ANY) {\n        it.hazptrs_[0].reset_protection();\n        return false;\n      }\n      // Already checked for rehash on DOES_NOT_EXIST, now check on ANY\n      if (size() >= grow_threshold_ && type == InsertType::ANY) {\n        if (max_size_ && size() << 1 > max_size_) {\n          // Would exceed max size.\n          throw_exception<std::bad_alloc>();\n        }\n        rehash_internal(ccount << 1, cohort);\n        ccount = chunk_count_.load(std::memory_order_relaxed);\n        chunks = chunks_.load(std::memory_order_relaxed);\n        DCHECK(chunks); // Use-after-destruction by user.\n        it.hazptrs_[0].reset_protection(chunks);\n      }\n    }\n    return true;\n  }\n\n  void rehash_internal(\n      size_t new_chunk_count, hazptr_obj_cohort<Atom>* cohort) {\n    DCHECK(isPowTwo(new_chunk_count));\n    auto old_chunk_count = chunk_count_.load(std::memory_order_relaxed);\n    if (old_chunk_count >= new_chunk_count) {\n      return;\n    }\n    auto new_chunks = Chunks::create(new_chunk_count, cohort);\n    auto old_chunks = chunks_.load(std::memory_order_relaxed);\n    grow_threshold_ =\n        to_integral(new_chunk_count * Chunk::kCapacity * load_factor_);\n\n    for (size_t i = 0; i < old_chunk_count; i++) {\n      DCHECK(old_chunks); // Use-after-destruction by user.\n      Chunk* oldchunk = old_chunks->getChunk(i, old_chunk_count);\n      auto occupied = oldchunk->occupiedIter();\n      while (occupied.hasNext()) {\n        auto idx = occupied.next();\n        Node* node = oldchunk->item(idx).load(std::memory_order_relaxed);\n        size_t new_chunk_idx;\n        size_t new_tag_idx;\n        auto h = HashFn()(node->getItem().first);\n        auto hp = splitHash(h);\n        std::tie(new_chunk_idx, new_tag_idx) =\n            findEmptyInsertLocation(new_chunks, new_chunk_count, hp);\n        Chunk* newchunk = new_chunks->getChunk(new_chunk_idx, new_chunk_count);\n        newchunk->setNodeAndTag(new_tag_idx, node, hp.second);\n      }\n    }\n\n    seqlock_.fetch_add(1, std::memory_order_release);\n    chunk_count_.store(new_chunk_count, std::memory_order_release);\n    chunks_.store(new_chunks, std::memory_order_release);\n    seqlock_.fetch_add(1, std::memory_order_release);\n    if (old_chunks) {\n      old_chunks->retire(HazptrTableDeleter(old_chunk_count));\n    }\n  }\n\n  void getChunksAndCount(\n      size_t& ccount, Chunks*& chunks, hazptr_holder<Atom>& hazptr) {\n    while (true) {\n      auto seqlock = seqlock_.load(std::memory_order_acquire);\n      ccount = chunk_count_.load(std::memory_order_acquire);\n      chunks = hazptr.protect(chunks_);\n      auto seqlock2 = seqlock_.load(std::memory_order_acquire);\n      if (!(seqlock & 1) && (seqlock == seqlock2)) {\n        break;\n      }\n    }\n    DCHECK(chunks);\n  }\n\n  std::pair<size_t, size_t> findEmptyInsertLocation(\n      Chunks* chunks, size_t ccount, const HashPair& hp) {\n    size_t chunk_idx = hp.first;\n    Chunk* dst_chunk = chunks->getChunk(chunk_idx, ccount);\n    auto firstEmpty = dst_chunk->firstEmpty();\n\n    if (!firstEmpty.hasIndex()) {\n      size_t delta = probeDelta(hp);\n      do {\n        dst_chunk->incrOutboundOverflowCount();\n        chunk_idx += delta;\n        dst_chunk = chunks->getChunk(chunk_idx, ccount);\n        firstEmpty = dst_chunk->firstEmpty();\n      } while (!firstEmpty.hasIndex());\n      dst_chunk->incrHostedOverflowCount();\n    }\n    size_t dst_tag_idx = firstEmpty.index();\n    return std::make_pair(chunk_idx & (ccount - 1), dst_tag_idx);\n  }\n\n  Mutex m_;\n  float load_factor_; // ceil of 1.0\n  size_t grow_threshold_;\n  Atom<size_t> size_{0};\n  size_t const max_size_;\n\n  // Fields needed for read-only access, on separate cacheline.\n  alignas(64) Atom<Chunks*> chunks_{nullptr};\n  std::atomic<uint64_t> seqlock_{0};\n  Atom<size_t> chunk_count_;\n};\n} // namespace simd\n\n#endif // FOLLY_SSE_PREREQ(4, 2) && FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <typename ValueType>\nstruct EqualTo1 {\n  const ValueType& rhs;\n  bool operator()(const ValueType& lhs) const { return lhs == rhs; }\n};\ntemplate <typename ValueType>\nEqualTo1(const ValueType&) -> EqualTo1<ValueType>;\n\n} // namespace concurrenthashmap\n\n/* A Segment is a single shard of the ConcurrentHashMap.\n * All writes take the lock, while readers are all wait-free.\n * Readers always proceed in parallel with the single writer.\n *\n *\n * Possible additional optimizations:\n *\n * * insert / erase could be lock / wait free.  Would need to be\n *   careful that assign and rehash don't conflict (possibly with\n *   reader/writer lock, or microlock per node or per bucket, etc).\n *   Java 8 goes halfway, and does lock per bucket, except for the\n *   first item, that is inserted with a CAS (which is somewhat\n *   specific to java having a lock per object)\n *\n * * I tried using trylock() and find() to warm the cache for insert()\n *   and erase() similar to Java 7, but didn't have much luck.\n *\n * * We could order elements using split ordering, for faster rehash,\n *   and no need to ever copy nodes.  Note that a full split ordering\n *   including dummy nodes increases the memory usage by 2x, but we\n *   could split the difference and still require a lock to set bucket\n *   pointers.\n */\ntemplate <\n    typename KeyType,\n    typename ValueType,\n    uint8_t ShardBits = 0,\n    typename HashFn = std::hash<KeyType>,\n    typename KeyEqual = std::equal_to<KeyType>,\n    typename Allocator = std::allocator<uint8_t>,\n    template <typename> class Atom = std::atomic,\n    class Mutex = std::mutex,\n    template <\n        typename,\n        typename,\n        uint8_t,\n        typename,\n        typename,\n        typename,\n        template <typename> class,\n        class> class Impl = concurrenthashmap::bucket::BucketTable>\nclass alignas(64) ConcurrentHashMapSegment {\n  using ImplT = Impl<\n      KeyType,\n      ValueType,\n      ShardBits,\n      HashFn,\n      KeyEqual,\n      Allocator,\n      Atom,\n      Mutex>;\n\n public:\n  using key_type = KeyType;\n  using mapped_type = ValueType;\n  using value_type = std::pair<const KeyType, ValueType>;\n  using size_type = std::size_t;\n\n  using InsertType = concurrenthashmap::InsertType;\n  using Iterator = typename ImplT::Iterator;\n  using Node = typename ImplT::Node;\n  static constexpr float kDefaultLoadFactor = ImplT::kDefaultLoadFactor;\n\n  ConcurrentHashMapSegment(\n      size_t initial_buckets,\n      float load_factor,\n      size_t max_size,\n      hazptr_obj_cohort<Atom>* cohort)\n      : impl_(initial_buckets, load_factor, max_size, cohort), cohort_(cohort) {\n    DCHECK(cohort);\n  }\n\n  ~ConcurrentHashMapSegment() = default;\n\n  size_t size() { return impl_.size(); }\n\n  bool empty() { return impl_.empty(); }\n\n  template <typename Key>\n  bool insert(Iterator& it, size_t h, std::pair<Key, mapped_type>&& foo) {\n    return insert(it, h, std::move(foo.first), std::move(foo.second));\n  }\n\n  template <typename Key, typename Value>\n  bool insert(Iterator& it, size_t h, Key&& k, Value&& v) {\n    concurrenthashmap::AllocNodeGuard<Node, Allocator> g(\n        Allocator(), cohort_, std::forward<Key>(k), std::forward<Value>(v));\n    auto res = insert_internal(\n        it,\n        h,\n        g.node->getItem().first,\n        InsertType::DOES_NOT_EXIST,\n        variadic_constant_of<false>,\n        g.node);\n    if (res) {\n      g.dismiss();\n    }\n    return res;\n  }\n\n  template <typename Key, typename... Args>\n  bool try_emplace(Iterator& it, size_t h, Key&& k, Args&&... args) {\n    // Note: first key is only ever compared.  Second is moved in to\n    // create the node, and the first key is never touched again.\n    return insert_internal(\n        it,\n        h,\n        std::forward<Key>(k),\n        InsertType::DOES_NOT_EXIST,\n        variadic_constant_of<false>,\n        std::forward<Key>(k),\n        std::forward<Args>(args)...);\n  }\n\n  template <typename... Args>\n  bool emplace(Iterator& it, size_t h, const KeyType& k, Node* node) {\n    return insert_internal(\n        it,\n        h,\n        k,\n        InsertType::DOES_NOT_EXIST,\n        variadic_constant_of<false>,\n        node);\n  }\n\n  template <typename Key, typename Value>\n  bool insert_or_assign(Iterator& it, size_t h, Key&& k, Value&& v) {\n    concurrenthashmap::AllocNodeGuard<Node, Allocator> g(\n        Allocator(), cohort_, std::forward<Key>(k), std::forward<Value>(v));\n    auto res = insert_internal(\n        it,\n        h,\n        g.node->getItem().first,\n        InsertType::ANY,\n        variadic_constant_of<false>,\n        g.node);\n    if (res) {\n      g.dismiss();\n    }\n    return res;\n  }\n\n  template <typename Key, typename Value, typename Predicate>\n  bool insert_or_assign_if(\n      Iterator& it, size_t h, Key&& k, Value&& desired, Predicate&& predicate) {\n    concurrenthashmap::AllocNodeGuard<Node, Allocator> g(\n        Allocator(),\n        cohort_,\n        std::forward<Key>(k),\n        std::forward<Value>(desired));\n    auto res = insert_internal(\n        it,\n        h,\n        g.node->getItem().first,\n        InsertType::MATCH_OR_DOES_NOT_EXIST,\n        std::forward<Predicate>(predicate),\n        g.node);\n    if (res) {\n      g.dismiss();\n    }\n    return res;\n  }\n\n  template <typename Key, typename Value>\n  bool assign(Iterator& it, size_t h, Key&& k, Value&& v) {\n    concurrenthashmap::AllocNodeGuard<Node, Allocator> g(\n        Allocator(), cohort_, std::forward<Key>(k), std::forward<Value>(v));\n    auto res = insert_internal(\n        it,\n        h,\n        g.node->getItem().first,\n        InsertType::MUST_EXIST,\n        variadic_constant_of<false>,\n        g.node);\n    if (res) {\n      g.dismiss();\n    }\n    return res;\n  }\n\n  template <typename Key, typename Value, typename Predicate>\n  bool assign_if(\n      Iterator& it, size_t h, Key&& k, Value&& desired, Predicate&& predicate) {\n    concurrenthashmap::AllocNodeGuard<Node, Allocator> g(\n        Allocator(),\n        cohort_,\n        std::forward<Key>(k),\n        std::forward<Value>(desired));\n    auto res = insert_internal(\n        it,\n        h,\n        g.node->getItem().first,\n        InsertType::MATCH,\n        std::forward<Predicate>(predicate),\n        g.node);\n    if (res) {\n      g.dismiss();\n    }\n    return res;\n  }\n\n  template <typename Key, typename Value>\n  bool assign_if_equal(\n      Iterator& it,\n      size_t h,\n      Key&& k,\n      const ValueType& expected,\n      Value&& desired) {\n    return assign_if(\n        it,\n        h,\n        std::forward<Key>(k),\n        std::forward<Value>(desired),\n        concurrenthashmap::EqualTo1{expected});\n  }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool insert_internal(\n      Iterator& it,\n      size_t h,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      Args&&... args) {\n    return impl_.insert(\n        it, h, k, type, match, cohort_, std::forward<Args>(args)...);\n  }\n\n  template <typename MatchFunc, typename K, typename... Args>\n  bool insert_internal(\n      Iterator& it,\n      size_t h,\n      const K& k,\n      InsertType type,\n      MatchFunc match,\n      Node* cur) {\n    return impl_.insert(it, h, k, type, match, cur, cohort_);\n  }\n\n  // Must hold lock.\n  void rehash(size_t bucket_count) {\n    impl_.rehash(folly::nextPowTwo(bucket_count), cohort_);\n  }\n\n  template <typename K>\n  bool find(Iterator& res, size_t h, const K& k) {\n    return impl_.find(res, h, k);\n  }\n\n  // Listed separately because we need a prev pointer.\n  template <typename K>\n  size_type erase(size_t h, const K& key) {\n    return erase_internal(h, key, nullptr, variadic_constant_of<true>);\n  }\n\n  template <typename K, typename Predicate>\n  size_type erase_key_if(size_t h, const K& key, Predicate&& predicate) {\n    return erase_internal(h, key, nullptr, std::forward<Predicate>(predicate));\n  }\n\n  template <typename K, typename MatchFunc>\n  size_type erase_internal(\n      size_t h, const K& key, Iterator* iter, MatchFunc match) {\n    return impl_.erase(h, key, iter, match);\n  }\n\n  // Unfortunately because we are reusing nodes on rehash, we can't\n  // have prev pointers in the bucket chain.  We have to start the\n  // search from the bucket.\n  //\n  // This is a small departure from standard stl containers: erase may\n  // throw if hash or key_eq functions throw.\n  void erase(Iterator& res, Iterator& pos, size_t h) {\n    erase_internal(h, pos->first, &res, variadic_constant_of<true>);\n    // Invalidate the iterator.\n    pos = cend();\n  }\n\n  void clear() { impl_.clear(cohort_); }\n\n  void max_load_factor(float factor) { impl_.max_load_factor(factor); }\n\n  Iterator cbegin() { return impl_.cbegin(); }\n\n  Iterator cend() { return impl_.cend(); }\n\n private:\n  ImplT impl_;\n  hazptr_obj_cohort<Atom>* cohort_;\n};\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/memory/AtomicReadMostlyMainPtr.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/memory/AtomicReadMostlyMainPtr.h>\n\n#include <folly/executors/InlineExecutor.h>\n\nnamespace folly {\nnamespace detail {\n\nnamespace {\nstruct FailingExecutor : folly::Executor {\n  // We shouldn't be invoking any callbacks.\n  void add(Func func) override {\n    LOG(DFATAL)\n        << \"Added an RCU callback to the AtomicReadMostlyMainPtr executor.\";\n    InlineExecutor::instance().add(std::move(func));\n  }\n};\n} // namespace\n\n// *All* modifications of *all* AtomicReadMostlyMainPtrs use the same mutex and\n// domain. The first of these just shrinks the size of the individual objects a\n// little, but the second is necessary for correctness; we want to support\n// arbitrarily many AtomicReadMostlyMainPtrs.\nIndestructible<std::mutex> atomicReadMostlyMu;\nIndestructible<folly::rcu_domain> atomicReadMostlyDomain(new FailingExecutor);\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/memory/AtomicReadMostlyMainPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstdint>\n#include <memory>\n#include <mutex>\n\n#include <folly/Indestructible.h>\n#include <folly/concurrency/memory/ReadMostlySharedPtr.h>\n#include <folly/synchronization/Rcu.h>\n\nnamespace folly {\n\nnamespace detail {\nextern Indestructible<std::mutex> atomicReadMostlyMu;\nextern Indestructible<rcu_domain> atomicReadMostlyDomain;\n} // namespace detail\n\n/*\n * What atomic_shared_ptr is to shared_ptr, AtomicReadMostlyMainPtr is to\n * ReadMostlyMainPtr; it allows racy conflicting accesses to one. This gives\n * true shared_ptr-like semantics, including reclamation at the point where the\n * last pointer to an object goes away.\n *\n * It's about the same speed (slightly slower) as ReadMostlyMainPtr. The most\n * significant feature they share is avoiding reader-reader contention and\n * atomic RMWs in the absence of writes.\n */\ntemplate <typename T>\nclass AtomicReadMostlyMainPtr {\n public:\n  AtomicReadMostlyMainPtr() : curMainPtrIndex_(0) {}\n\n  explicit AtomicReadMostlyMainPtr(std::shared_ptr<T> ptr)\n      : curMainPtrIndex_(0) {\n    mainPtrs_[0] = ReadMostlyMainPtr<T>{std::move(ptr)};\n  }\n\n  void operator=(std::shared_ptr<T> desired) { store(std::move(desired)); }\n\n  bool is_lock_free() const { return false; }\n\n  ReadMostlySharedPtr<T> load(\n      std::memory_order order = std::memory_order_seq_cst) const {\n    detail::atomicReadMostlyDomain->lock();\n    // Synchronization point with the store in storeLocked().\n    auto index = curMainPtrIndex_.load(order);\n    auto result = mainPtrs_[index].getShared();\n    detail::atomicReadMostlyDomain->unlock();\n    return result;\n  }\n\n  void store(\n      std::shared_ptr<T> ptr,\n      std::memory_order order = std::memory_order_seq_cst) {\n    std::shared_ptr<T> old;\n    {\n      std::lock_guard lg(*detail::atomicReadMostlyMu);\n      old = exchangeLocked(std::move(ptr), order);\n    }\n    // If ~T() runs (triggered by the shared_ptr refcount decrement), it's here,\n    // after dropping the lock. This avoids a possible (albeit esoteric)\n    // deadlock if ~T() modifies the AtomicReadMostlyMainPtr that used to point\n    // to it.\n  }\n\n  std::shared_ptr<T> exchange(\n      std::shared_ptr<T> ptr,\n      std::memory_order order = std::memory_order_seq_cst) {\n    std::lock_guard lg(*detail::atomicReadMostlyMu);\n    return exchangeLocked(std::move(ptr), order);\n  }\n\n  bool compare_exchange_weak(\n      std::shared_ptr<T>& expected,\n      const std::shared_ptr<T>& desired,\n      std::memory_order successOrder = std::memory_order_seq_cst,\n      std::memory_order failureOrder = std::memory_order_seq_cst) {\n    return compare_exchange_strong(\n        expected, desired, successOrder, failureOrder);\n  }\n\n  bool compare_exchange_strong(\n      std::shared_ptr<T>& expected,\n      const std::shared_ptr<T>& desired,\n      std::memory_order successOrder = std::memory_order_seq_cst,\n      std::memory_order failureOrder = std::memory_order_seq_cst) {\n    // See the note at the end of store; we need to defer any destruction we\n    // might trigger until after the lock is released.\n    // This is not actually needed down the success path (the reference passed\n    // in as expected is another pointer to the same object, so we won't\n    // decrement the refcount to 0), but \"never decrement a refcount while\n    // holding a lock\" is an easier rule to keep in our heads, and costs us\n    // nothing.\n    std::shared_ptr<T> prev;\n    std::shared_ptr<T> expectedDup;\n    {\n      std::lock_guard lg(*detail::atomicReadMostlyMu);\n      auto index = curMainPtrIndex_.load(failureOrder);\n      ReadMostlyMainPtr<T>& oldMain = mainPtrs_[index];\n      if (oldMain.get() != expected.get()) {\n        expectedDup = std::move(expected);\n        expected = oldMain.getStdShared();\n        return false;\n      }\n      prev = exchangeLocked(desired, successOrder);\n    }\n    return true;\n  }\n\n private:\n  // Must hold the global mutex.\n  std::shared_ptr<T> exchangeLocked(\n      std::shared_ptr<T> ptr,\n      std::memory_order order = std::memory_order_seq_cst) {\n    // This is where the tricky bits happen; all modifications of the mainPtrs_\n    // and index happen here. We maintain the invariant that, on entry to this\n    // method, all read-side critical sections in progress are using the version\n    // indicated by curMainPtrIndex_, and the other version is nulled out.\n    // (Readers can still hold a ReadMostlySharedPtr to the thing the old\n    // version used to point to; they just can't access the old version to get\n    // that handle any more).\n    auto index = curMainPtrIndex_.load(std::memory_order_relaxed);\n    ReadMostlyMainPtr<T>& oldMain = mainPtrs_[index];\n    ReadMostlyMainPtr<T>& newMain = mainPtrs_[1 - index];\n    DCHECK(newMain.get() == nullptr)\n        << \"Invariant should ensure that at most one version is non-null\";\n    newMain.reset(std::move(ptr));\n    // If order is acq_rel, it should degrade to just release, and if acquire to\n    // relaxed, since this is a store rather than an RMW. (Of course, this is\n    // such a slow method that we don't really care, but precision is its own\n    // reward. If TSAN one day understands asymmetric barriers, this will also\n    // improve its error detection here). We get our \"acquire-y-ness\" from the\n    // mutex.\n    auto realOrder =\n        (order == std::memory_order_acq_rel ? std::memory_order_release\n             : order == std::memory_order_acquire\n             ? std::memory_order_relaxed\n             : order);\n    // After this, read-side critical sections can access both versions, but\n    // new ones will use newMain.\n    // This is also synchronization point with loads.\n    curMainPtrIndex_.store(1 - index, realOrder);\n    // Wait for all read-side critical sections using oldMain to finish.\n    detail::atomicReadMostlyDomain->synchronize();\n    // We've reestablished the first half of the invariant (all readers are\n    // using newMain), now let's establish the other one (that the other pointer\n    // is null).\n    auto result = oldMain.getStdShared();\n    oldMain.reset();\n    return result;\n  }\n\n  // The right way to think of this implementation is as an\n  // std::atomic<ReadMostlyMainPtr<T>*>, protected by RCU. There's only two\n  // tricky parts:\n  // 1. We give ourselves our own RCU domain, and synchronize on modification,\n  //    so that we don't do any batching of deallocations. This gives\n  //    shared_ptr-like eager reclamation semantics.\n  // 2. Instead of putting the ReadMostlyMainPtrs on the heap, we keep them as\n  //    part of the same object to improve locality.\n\n  // Really, just a 0/1 index. This is also the synchronization point for memory\n  // orders.\n  std::atomic<uint8_t> curMainPtrIndex_;\n\n  // Both the ReadMostlyMainPtrs themselves and the domain have nontrivial\n  // indirections even on the read path, and asymmetric barriers on the write\n  // path. Some of these could be fused as a later optimization, at the cost of\n  // having to put more tricky threading primitives in this class that are\n  // currently abstracted out by those.\n  ReadMostlyMainPtr<T> mainPtrs_[2];\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/memory/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"atomic_read_mostly_main_ptr\",\n    srcs = [\n        \"AtomicReadMostlyMainPtr.cpp\",\n    ],\n    headers = [\n        \"AtomicReadMostlyMainPtr.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/executors:inline_executor\",\n    ],\n    exported_deps = [\n        \"//folly:indestructible\",\n        \"//folly/concurrency/memory:read_mostly_shared_ptr\",\n        \"//folly/synchronization:rcu\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"read_mostly_shared_ptr\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"ReadMostlySharedPtr.h\",\n    ],\n    deps = [\n        \":tl_ref_count\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"primary_ptr\",\n    headers = [\"PrimaryPtr.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:function\",\n        \"//folly/futures:cleanup\",\n        \"//folly/futures:core\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"tl_ref_count\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"TLRefCount.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/synchronization:asymmetric_thread_fence\",\n        \"fbsource//xplat/folly/synchronization/detail:sleeper\",\n        \"//xplat/folly:thread_local\",\n    ],\n)\n\n# !!!! fbcode/folly/concurrency/memory/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"read_mostly_shared_ptr\",\n    headers = [\"ReadMostlySharedPtr.h\"],\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly/concurrency/memory:tl_ref_count\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"tl_ref_count\",\n    headers = [\"TLRefCount.h\"],\n    exported_deps = [\n        \"//folly:thread_local\",\n        \"//folly/synchronization:asymmetric_thread_fence\",\n        \"//folly/synchronization/detail:sleeper\",\n    ],\n)\n"
  },
  {
    "path": "folly/concurrency/memory/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME atomic_read_mostly_main_ptr\n  SRCS\n    AtomicReadMostlyMainPtr.cpp\n  HEADERS\n    AtomicReadMostlyMainPtr.h\n  DEPS\n    folly_executors_inline_executor\n  EXPORTED_DEPS\n    folly_concurrency_memory_read_mostly_shared_ptr\n    folly_indestructible\n    folly_synchronization_rcu\n)\n\nfolly_add_library(\n  NAME primary_ptr\n  HEADERS\n    PrimaryPtr.h\n  EXPORTED_DEPS\n    folly_function\n    folly_futures_cleanup\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME read_mostly_shared_ptr\n  HEADERS\n    ReadMostlySharedPtr.h\n  EXPORTED_DEPS\n    folly_concurrency_memory_tl_ref_count\n    folly_function\n)\n\nfolly_add_library(\n  NAME tl_ref_count\n  HEADERS\n    TLRefCount.h\n  EXPORTED_DEPS\n    folly_synchronization_asymmetric_thread_fence\n    folly_synchronization_detail_sleeper\n    folly_thread_local\n)\n"
  },
  {
    "path": "folly/concurrency/memory/PrimaryPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <mutex>\n\n#include <folly/Function.h>\n#include <folly/futures/Cleanup.h>\n#include <folly/futures/Future.h>\n\n#include <glog/logging.h>\n\nnamespace folly {\n\ntemplate <typename T>\nclass EnablePrimaryFromThis;\n\ntemplate <typename T>\nclass PrimaryPtr;\n\ntemplate <typename T>\nclass PrimaryPtrRef;\n\nnamespace detail {\nstruct publicallyDerivedFromEnablePrimaryFromThis_fn {\n  template <class T>\n  void operator()(const EnablePrimaryFromThis<T>&) const {}\n};\n} // namespace detail\n\ntemplate <class T>\nconstexpr bool is_enable_master_from_this_v = folly::\n    is_invocable_v<detail::publicallyDerivedFromEnablePrimaryFromThis_fn, T>;\n\ntemplate <typename T>\nusing is_enable_master_from_this =\n    std::bool_constant<is_enable_master_from_this_v<T>>;\n\n/**\n * EnablePrimaryFromThis provides an object with appropriate access to the\n * functionality of the PrimaryPtr holding this.\n */\ntemplate <typename T>\nclass EnablePrimaryFromThis {\n  // initializes members when the PrimaryPtr for this is constructed\n  //\n  // used by the PrimaryPtr for this, to invoke the EnablePrimaryFromThis base\n  // of T, if it exists.\n  template <\n      class O,\n      class Master,\n      std::enable_if_t<is_enable_master_from_this_v<O>, int> = 0>\n  static void set(EnablePrimaryFromThis<O>* that, Master& m) {\n    that->outerPtrWeak_ = m.outerPtrWeak_;\n  }\n\n  template <\n      class O,\n      class Master,\n      std::enable_if_t<!is_enable_master_from_this_v<O>, int> = 0>\n  static void set(O*, Master&) {}\n\n public:\n  // Gets a non-owning reference to the pointer. PrimaryPtr::join() and the\n  // PrimaryPtr::cleanup() work do *NOT* wait for outstanding PrimaryPtrRef\n  // objects to be released.\n  PrimaryPtrRef<T> masterRefFromThis() {\n    return PrimaryPtrRef<T>(outerPtrWeak_);\n  }\n\n  // Gets a non-owning const reference to the pointer. PrimaryPtr::join() and\n  // the PrimaryPtr::cleanup() work do *NOT* wait for outstanding PrimaryPtrRef\n  // objects to be released.\n  PrimaryPtrRef<const T> masterRefFromThis() const {\n    return PrimaryPtrRef<const T>(outerPtrWeak_);\n  }\n\n  // Attempts to lock a pointer. Returns null if pointer is not set or if\n  // PrimaryPtr::join() was called or the PrimaryPtr::cleanup() task was started\n  // (even if the call to PrimaryPtr::join() hasn't returned yet and the\n  // PrimaryPtr::cleanup() task has not completed yet).\n  std::shared_ptr<T> masterLockFromThis() {\n    if (auto outerPtr = outerPtrWeak_.lock()) {\n      return *outerPtr;\n    }\n    return nullptr;\n  }\n\n  // Attempts to lock a pointer. Returns null if pointer is not set or if\n  // PrimaryPtr::join() was called or the PrimaryPtr::cleanup() task was started\n  // (even if the call to PrimaryPtr::join() hasn't returned yet and the\n  // PrimaryPtr::cleanup() task has not completed yet).\n  std::shared_ptr<T const> masterLockFromThis() const {\n    if (!*this) {\n      return nullptr;\n    }\n    if (auto outerPtr = outerPtrWeak_.lock()) {\n      return *outerPtr;\n    }\n    return nullptr;\n  }\n\n private:\n  template <class>\n  friend class PrimaryPtr;\n\n  std::weak_ptr<std::shared_ptr<T>> outerPtrWeak_;\n};\n\n/**\n * PrimaryPtr should be used to achieve deterministic destruction of objects\n * with shared ownership. Once an object is managed by a PrimaryPtr, shared_ptrs\n * can be obtained pointing to that object. However destroying those shared_ptrs\n * will never call the object destructor inline. To destroy the object, join()\n * method must be called on PrimaryPtr or the task returned from cleanup() must\n * be completed, which will wait for all shared_ptrs to be released and then\n * call the object destructor on the caller supplied execution context.\n */\ntemplate <typename T>\nclass PrimaryPtr {\n  // retrieves nested cleanup() work from innerPtr_. Called when the PrimaryPtr\n  // cleanup() task has finished waiting for outstanding references\n  //\n  template <class Cleanup, std::enable_if_t<is_cleanup_v<Cleanup>, int> = 0>\n  static folly::SemiFuture<folly::Unit> getCleanup(Cleanup* cleanup) {\n    return std::move(*cleanup).cleanup();\n  }\n\n  template <class O, std::enable_if_t<!is_cleanup_v<O>, int> = 0>\n  static folly::SemiFuture<folly::Unit> getCleanup(O*) {\n    return folly::makeSemiFuture();\n  }\n\n public:\n  PrimaryPtr() = delete;\n  template <class T2, class Deleter>\n  PrimaryPtr(std::unique_ptr<T2, Deleter> ptr) {\n    set(std::move(ptr));\n  }\n\n  explicit PrimaryPtr(std::nullptr_t) {}\n\n  ~PrimaryPtr() {\n    if (*this) {\n      LOG(FATAL) << \"PrimaryPtr has to be joined explicitly.\";\n    }\n  }\n\n  PrimaryPtr(PrimaryPtr<T>&&) = default;\n\n  void swap(PrimaryPtr<T>& other) noexcept {\n    PrimaryPtr<T> temp = std::move(other);\n    other = std::move(*this);\n    *this = std::move(temp);\n  }\n\n  PrimaryPtr<T> exchange(PrimaryPtr<T>&& newVal) noexcept {\n    PrimaryPtr<T> oldVal = std::move(*this);\n    *this = std::move(newVal);\n    return oldVal;\n  }\n\n  explicit operator bool() const { return !!innerPtr_; }\n\n  // Attempts to lock a pointer. Returns null if pointer is not set or if join()\n  // was called or the cleanup() task was started (even if the call to join()\n  // hasn't returned yet and the cleanup() task has not completed yet).\n  std::shared_ptr<T> lock() const {\n    if (auto outerPtr = outerPtrWeak_.lock()) {\n      return *outerPtr;\n    }\n    return nullptr;\n  }\n\n  // Waits until all the references obtained via lock() are released. Then\n  // destroys the object in the current thread.\n  // Can not be called concurrently with set().\n  void join() {\n    if (!*this) {\n      return;\n    }\n    this->cleanup().get();\n  }\n\n  // Returns: a SemiFuture that waits until all the references obtained via\n  // lock() are released. Then destroys the object on the Executor provided to\n  // the SemiFuture.\n  //\n  // The returned SemiFuture must run to completion before calling set()\n  //\n  folly::SemiFuture<folly::Unit> cleanup() {\n    return folly::makeSemiFuture()\n        // clear outerPtrShared_ after cleanup is started\n        // to disable further calls to lock().\n        // then wait for outstanding references.\n        .deferValue([this](folly::Unit) {\n          if (!this->outerPtrShared_) {\n            LOG(FATAL)\n                << \"Cleanup already run - lock() was previously disabled.\";\n          }\n          this->outerPtrShared_.reset();\n          return std::move(this->unreferenced_);\n        })\n        // start cleanup tasks\n        .deferValue([this](folly::Unit) { return getCleanup(innerPtr_.get()); })\n        .defer([this](folly::Try<folly::Unit> r) {\n          if (r.hasException()) {\n            LOG(FATAL) << \"Cleanup actions must be noexcept.\";\n          }\n          this->innerPtr_.reset();\n        });\n  }\n\n  // Sets the pointer. Can not be called concurrently with lock() or join() or\n  // ref() or while the SemiFuture returned from cleanup() is running.\n  template <class T2, class Deleter>\n  void set(std::unique_ptr<T2, Deleter> ptr) {\n    if (*this) {\n      LOG(FATAL) << \"PrimaryPtr has to be joined before being set.\";\n    }\n\n    if (!ptr) {\n      return;\n    }\n\n    auto rawPtr = ptr.get();\n    innerPtr_ = std::unique_ptr<T, folly::Function<void(T*)>>{\n        ptr.release(),\n        [d = ptr.get_deleter(), rawPtr](T*) mutable { d(rawPtr); }};\n\n    auto [referencesPromise, referencesFuture] =\n        folly::makePromiseContract<folly::Unit>();\n    unreferenced_ = std::move(referencesFuture);\n\n    // The deleter object needs to be copyable in std::shared_ptr on some\n    // platform. To work around this limitation we can slightly tweak the\n    // semantics of deleter copy constructor and check we always use this\n    // object at most once.\n    class LastReference {\n     public:\n      LastReference(Promise<Unit>&& p) : p_(std::move(p)) {}\n      LastReference(LastReference&&) = default;\n      LastReference(LastReference& other) : LastReference(std::move(other)) {}\n      void operator()(T*) {\n        DCHECK(!p_.isFulfilled());\n        p_.setValue();\n      }\n\n     private:\n      Promise<Unit> p_;\n    };\n    auto innerPtrShared = std::shared_ptr<T>(\n        innerPtr_.get(), LastReference{std::move(referencesPromise)});\n\n    outerPtrWeak_ = outerPtrShared_ =\n        std::make_shared<std::shared_ptr<T>>(innerPtrShared);\n\n    // attaches optional EnablePrimaryFromThis base of innerPtr_ to this\n    // PrimaryPtr\n    EnablePrimaryFromThis<T>::set(innerPtr_.get(), *this);\n  }\n\n  // Gets a non-owning reference to the pointer. join() and the cleanup() work\n  // do *NOT* wait for outstanding PrimaryPtrRef objects to be released.\n  PrimaryPtrRef<T> ref() const { return PrimaryPtrRef<T>(outerPtrWeak_); }\n\n private:\n  // Making this private for now since non-null PrimaryPtr's must be explicitly\n  // joined before destruction.\n  PrimaryPtr<T>& operator=(PrimaryPtr<T>&& other) = default;\n\n  template <class>\n  friend class EnablePrimaryFromThis;\n  friend class PrimaryPtrRef<T>;\n\n  folly::SemiFuture<folly::Unit> unreferenced_;\n  std::shared_ptr<std::shared_ptr<T>> outerPtrShared_;\n  std::weak_ptr<std::shared_ptr<T>> outerPtrWeak_;\n  std::unique_ptr<T, folly::Function<void(T*)>> innerPtr_;\n};\n\ntemplate <typename T>\nvoid swap(PrimaryPtr<T>& x, PrimaryPtr<T>& y) noexcept {\n  x.swap(y);\n}\n\ntemplate <typename T>\nPrimaryPtr<T> exchange(PrimaryPtr<T>& x, PrimaryPtr<T>&& newVal) noexcept {\n  return x.exchange(std::move(newVal));\n}\n\n/**\n * PrimaryPtrRef is a non-owning reference to the pointer. PrimaryPtr::join()\n * and the PrimaryPtr::cleanup() work do *NOT* wait for outstanding\n * PrimaryPtrRef objects to be released.\n */\ntemplate <typename T>\nclass PrimaryPtrRef {\n public:\n  PrimaryPtrRef() = default;\n\n  // Attempts to lock a pointer. Returns null if pointer is not set or if\n  // join() was called or cleanup() work was started (even if the call to join()\n  // hasn't returned yet or the cleanup() work has not completed yet).\n  std::shared_ptr<T> lock() const {\n    if (auto outerPtr = outerPtrWeak_.lock()) {\n      return *outerPtr;\n    }\n    return nullptr;\n  }\n\n private:\n  template <class>\n  friend class EnablePrimaryFromThis;\n  template <class>\n  friend class PrimaryPtr;\n  /* implicit */ PrimaryPtrRef(std::weak_ptr<std::shared_ptr<T>> outerPtrWeak)\n      : outerPtrWeak_(std::move(outerPtrWeak)) {}\n\n  std::weak_ptr<std::shared_ptr<T>> outerPtrWeak_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/memory/ReadMostlySharedPtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/Function.h>\n#include <folly/concurrency/memory/TLRefCount.h>\n\nnamespace folly {\n\ntemplate <typename T, typename RefCount>\nclass ReadMostlyMainPtr;\ntemplate <typename T, typename RefCount>\nclass ReadMostlyWeakPtr;\ntemplate <typename T, typename RefCount>\nclass ReadMostlySharedPtr;\ntemplate <typename RefCount>\nclass ReadMostlyMainPtrDeleter;\n\nusing DefaultRefCount = TLRefCount;\n\nnamespace detail {\n\ntemplate <typename RefCount = DefaultRefCount>\nclass ReadMostlySharedPtrCore {\n public:\n  std::shared_ptr<const void> getShared() { return ptr_; }\n\n  bool incref() { return ++count_ > 0; }\n\n  void decref() {\n    if (--count_ == 0) {\n      ptr_.reset();\n\n      decrefWeak();\n    }\n  }\n\n  void increfWeak() {\n    auto value = ++weakCount_;\n    DCHECK_GT(value, 0);\n  }\n\n  void decrefWeak() {\n    if (--weakCount_ == 0) {\n      delete this;\n    }\n  }\n\n  size_t useCount() const { return *count_; }\n\n  ~ReadMostlySharedPtrCore() noexcept {\n    assert(*count_ == 0);\n    assert(*weakCount_ == 0);\n  }\n\n private:\n  template <typename T, typename RefCount2>\n  friend class folly::ReadMostlyMainPtr;\n  friend class ReadMostlyMainPtrDeleter<RefCount>;\n\n  explicit ReadMostlySharedPtrCore(std::shared_ptr<const void> ptr)\n      : ptr_(std::move(ptr)) {}\n\n  RefCount count_;\n  RefCount weakCount_;\n  std::shared_ptr<const void> ptr_;\n};\n\n} // namespace detail\n\ntemplate <typename T, typename RefCount = DefaultRefCount>\nclass ReadMostlyMainPtr {\n public:\n  ReadMostlyMainPtr() {}\n\n  explicit ReadMostlyMainPtr(std::shared_ptr<T> ptr) { reset(std::move(ptr)); }\n\n  ReadMostlyMainPtr(const ReadMostlyMainPtr&) = delete;\n  ReadMostlyMainPtr& operator=(const ReadMostlyMainPtr&) = delete;\n\n  ReadMostlyMainPtr(ReadMostlyMainPtr&& other) noexcept {\n    *this = std::move(other);\n  }\n\n  ReadMostlyMainPtr& operator=(ReadMostlyMainPtr&& other) noexcept {\n    std::swap(impl_, other.impl_);\n    std::swap(ptrRaw_, other.ptrRaw_);\n    return *this;\n  }\n\n  bool operator==(const ReadMostlyMainPtr<T, RefCount>& other) const {\n    return get() == other.get();\n  }\n\n  bool operator==(T* other) const { return get() == other; }\n\n  bool operator==(const ReadMostlySharedPtr<T, RefCount>& other) const {\n    return get() == other.get();\n  }\n\n  ~ReadMostlyMainPtr() noexcept { reset(); }\n\n  void reset() noexcept {\n    if (impl_) {\n      ptrRaw_ = nullptr;\n      impl_->count_.useGlobal();\n      impl_->weakCount_.useGlobal();\n      impl_->decref();\n      impl_ = nullptr;\n    }\n  }\n\n  void reset(std::shared_ptr<T> ptr) {\n    reset();\n    if (ptr) {\n      ptrRaw_ = ptr.get();\n      impl_ = new detail::ReadMostlySharedPtrCore<RefCount>(std::move(ptr));\n    }\n  }\n\n  T* get() const { return ptrRaw_; }\n\n  std::shared_ptr<T> getStdShared() const {\n    if (impl_) {\n      return {impl_->getShared(), ptrRaw_};\n    } else {\n      return {};\n    }\n  }\n\n  T& operator*() const { return *get(); }\n\n  T* operator->() const { return get(); }\n\n  ReadMostlySharedPtr<T, RefCount> getShared() const {\n    return ReadMostlySharedPtr<T, RefCount>(*this);\n  }\n\n  explicit operator bool() const { return impl_ != nullptr; }\n\n private:\n  template <typename U, typename RefCount2>\n  friend class ReadMostlyWeakPtr;\n  template <typename U, typename RefCount2>\n  friend class ReadMostlySharedPtr;\n  friend class ReadMostlyMainPtrDeleter<RefCount>;\n\n  detail::ReadMostlySharedPtrCore<RefCount>* impl_{nullptr};\n  T* ptrRaw_{nullptr};\n};\n\ntemplate <typename T, typename RefCount = DefaultRefCount>\nclass ReadMostlyWeakPtr {\n public:\n  ReadMostlyWeakPtr() {}\n\n  ReadMostlyWeakPtr(const ReadMostlyWeakPtr& other) { *this = other; }\n\n  ReadMostlyWeakPtr(ReadMostlyWeakPtr&& other) noexcept {\n    *this = std::move(other);\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlyWeakPtr(const ReadMostlyWeakPtr<T2, RefCount>& other) {\n    *this = other;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlyWeakPtr(ReadMostlyWeakPtr<T2, RefCount>&& other) noexcept {\n    *this = std::move(other);\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  explicit ReadMostlyWeakPtr(const ReadMostlyMainPtr<T2, RefCount>& other) {\n    *this = other;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  explicit ReadMostlyWeakPtr(const ReadMostlySharedPtr<T2, RefCount>& other) {\n    *this = other;\n  }\n\n  ReadMostlyWeakPtr& operator=(const ReadMostlyWeakPtr& other) {\n    reset(other.impl_, other.ptrRaw_);\n    return *this;\n  }\n\n  ReadMostlyWeakPtr& operator=(ReadMostlyWeakPtr&& other) noexcept {\n    std::swap(impl_, other.impl_);\n    std::swap(ptrRaw_, other.ptrRaw_);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlyWeakPtr& operator=(const ReadMostlyWeakPtr<T2, RefCount>& other) {\n    reset(other.impl_, other.ptrRaw_);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlyWeakPtr& operator=(\n      ReadMostlyWeakPtr<T2, RefCount>&& other) noexcept {\n    reset();\n    impl_ = std::exchange(other.impl_, nullptr);\n    ptrRaw_ = std::exchange(other.ptrRaw_, nullptr);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlyWeakPtr& operator=(const ReadMostlyMainPtr<T2, RefCount>& mainPtr) {\n    reset(mainPtr.impl_, mainPtr.ptrRaw_);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlyWeakPtr& operator=(\n      const ReadMostlySharedPtr<T2, RefCount>& mainPtr) {\n    reset(mainPtr.impl_, mainPtr.ptrRaw_);\n    return *this;\n  }\n\n  ~ReadMostlyWeakPtr() noexcept { reset(nullptr, nullptr); }\n\n  ReadMostlySharedPtr<T, RefCount> lock() {\n    return ReadMostlySharedPtr<T, RefCount>(*this);\n  }\n\n private:\n  template <typename U, typename RefCount2>\n  friend class ReadMostlyWeakPtr;\n  template <typename U, typename RefCount2>\n  friend class ReadMostlySharedPtr;\n\n  void reset(detail::ReadMostlySharedPtrCore<RefCount>* impl, T* ptrRaw) {\n    if (impl_ == impl) {\n      return;\n    }\n\n    if (impl_) {\n      impl_->decrefWeak();\n    }\n    impl_ = impl;\n    ptrRaw_ = ptrRaw;\n    if (impl_) {\n      impl_->increfWeak();\n    }\n  }\n\n  detail::ReadMostlySharedPtrCore<RefCount>* impl_{nullptr};\n  T* ptrRaw_{nullptr};\n};\n\ntemplate <typename T, typename RefCount = DefaultRefCount>\nclass ReadMostlySharedPtr {\n public:\n  ReadMostlySharedPtr() {}\n\n  ReadMostlySharedPtr(const ReadMostlySharedPtr& other) { *this = other; }\n\n  ReadMostlySharedPtr(ReadMostlySharedPtr&& other) noexcept {\n    *this = std::move(other);\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlySharedPtr(const ReadMostlySharedPtr<T2, RefCount>& other) {\n    *this = other;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlySharedPtr(ReadMostlySharedPtr<T2, RefCount>&& other) noexcept {\n    *this = std::move(other);\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  explicit ReadMostlySharedPtr(const ReadMostlyWeakPtr<T2, RefCount>& other) {\n    *this = other;\n  }\n\n  // Generally, this shouldn't be used.\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  explicit ReadMostlySharedPtr(const ReadMostlyMainPtr<T2, RefCount>& other) {\n    *this = other;\n  }\n\n  ReadMostlySharedPtr& operator=(const ReadMostlySharedPtr& other) {\n    reset(other.impl_, other.ptrRaw_);\n    return *this;\n  }\n\n  ReadMostlySharedPtr& operator=(ReadMostlySharedPtr&& other) noexcept {\n    std::swap(impl_, other.impl_);\n    std::swap(ptrRaw_, other.ptrRaw_);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlySharedPtr& operator=(\n      const ReadMostlySharedPtr<T2, RefCount>& other) {\n    reset(other.impl_, other.ptrRaw_);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlySharedPtr& operator=(\n      ReadMostlySharedPtr<T2, RefCount>&& other) noexcept {\n    reset();\n    impl_ = std::exchange(other.impl_, nullptr);\n    ptrRaw_ = std::exchange(other.ptrRaw_, nullptr);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlySharedPtr& operator=(const ReadMostlyWeakPtr<T2, RefCount>& other) {\n    reset(other.impl_, other.ptrRaw_);\n    return *this;\n  }\n\n  template <\n      typename T2,\n      typename = std::enable_if_t<std::is_convertible<T2*, T*>::value>>\n  ReadMostlySharedPtr& operator=(const ReadMostlyMainPtr<T2, RefCount>& other) {\n    reset(other.impl_, other.ptrRaw_);\n    return *this;\n  }\n\n  ~ReadMostlySharedPtr() noexcept { reset(nullptr, nullptr); }\n\n  bool operator==(const ReadMostlyMainPtr<T, RefCount>& other) const {\n    return get() == other.get();\n  }\n\n  bool operator==(T* other) const { return get() == other; }\n\n  bool operator==(const ReadMostlySharedPtr<T, RefCount>& other) const {\n    return get() == other.get();\n  }\n\n  void reset() { reset(nullptr, nullptr); }\n\n  T* get() const { return ptrRaw_; }\n\n  std::shared_ptr<T> getStdShared() const {\n    if (impl_) {\n      return {impl_->getShared(), ptrRaw_};\n    } else {\n      return {};\n    }\n  }\n\n  T& operator*() const { return *get(); }\n\n  T* operator->() const { return get(); }\n\n  size_t use_count() const { return impl_->useCount(); }\n\n  bool unique() const { return use_count() == 1; }\n\n  explicit operator bool() const { return impl_ != nullptr; }\n\n private:\n  template <typename U, typename RefCount2>\n  friend class ReadMostlyWeakPtr;\n  template <typename U, typename RefCount2>\n  friend class ReadMostlySharedPtr;\n\n  void reset(detail::ReadMostlySharedPtrCore<RefCount>* impl, T* ptrRaw) {\n    if (impl_ == impl) {\n      return;\n    }\n\n    if (impl_) {\n      impl_->decref();\n      impl_ = nullptr;\n      ptrRaw_ = nullptr;\n    }\n\n    if (impl && impl->incref()) {\n      impl_ = impl;\n      ptrRaw_ = ptrRaw;\n    }\n  }\n\n  T* ptrRaw_{nullptr};\n  detail::ReadMostlySharedPtrCore<RefCount>* impl_{nullptr};\n};\n\n/**\n * This can be used to destroy multiple ReadMostlyMainPtrs at once.\n */\ntemplate <typename RefCount = DefaultRefCount>\nclass ReadMostlyMainPtrDeleter {\n public:\n  ~ReadMostlyMainPtrDeleter() noexcept {\n    RefCount::useGlobal(refCounts_);\n    for (auto& decref : decrefs_) {\n      decref();\n    }\n  }\n\n  template <typename T>\n  void add(ReadMostlyMainPtr<T, RefCount> ptr) noexcept {\n    if (!ptr.impl_) {\n      return;\n    }\n\n    refCounts_.push_back(&ptr.impl_->count_);\n    refCounts_.push_back(&ptr.impl_->weakCount_);\n    decrefs_.push_back([impl = ptr.impl_] { impl->decref(); });\n    ptr.impl_ = nullptr;\n    ptr.ptrRaw_ = nullptr;\n  }\n\n private:\n  std::vector<RefCount*> refCounts_;\n  std::vector<folly::Function<void()>> decrefs_;\n};\n\ntemplate <typename T, typename RefCount>\ninline bool operator==(\n    const ReadMostlyMainPtr<T, RefCount>& ptr, std::nullptr_t) {\n  return ptr.get() == nullptr;\n}\n\ntemplate <typename T, typename RefCount>\ninline bool operator==(\n    std::nullptr_t, const ReadMostlyMainPtr<T, RefCount>& ptr) {\n  return ptr.get() == nullptr;\n}\n\ntemplate <typename T, typename RefCount>\ninline bool operator==(\n    const ReadMostlySharedPtr<T, RefCount>& ptr, std::nullptr_t) {\n  return ptr.get() == nullptr;\n}\n\ntemplate <typename T, typename RefCount>\ninline bool operator==(\n    std::nullptr_t, const ReadMostlySharedPtr<T, RefCount>& ptr) {\n  return ptr.get() == nullptr;\n}\n\ntemplate <typename T, typename RefCount>\ninline bool operator!=(\n    const ReadMostlyMainPtr<T, RefCount>& ptr, std::nullptr_t) {\n  return !(ptr == nullptr);\n}\n\ntemplate <typename T, typename RefCount>\ninline bool operator!=(\n    std::nullptr_t, const ReadMostlyMainPtr<T, RefCount>& ptr) {\n  return !(ptr == nullptr);\n}\n\ntemplate <typename T, typename RefCount>\ninline bool operator!=(\n    const ReadMostlySharedPtr<T, RefCount>& ptr, std::nullptr_t) {\n  return !(ptr == nullptr);\n}\n\ntemplate <typename T, typename RefCount>\ninline bool operator!=(\n    std::nullptr_t, const ReadMostlySharedPtr<T, RefCount>& ptr) {\n  return !(ptr == nullptr);\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/memory/TLRefCount.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ThreadLocal.h>\n#include <folly/synchronization/AsymmetricThreadFence.h>\n#include <folly/synchronization/detail/Sleeper.h>\n\nnamespace folly {\n\nclass TLRefCount {\n public:\n  using Int = int64_t;\n\n  TLRefCount()\n      : localCount_([&]() { return LocalRefCount(*this); }),\n        collectGuard_(this, [](void*) {}) {}\n\n  ~TLRefCount() noexcept {\n    assert(globalCount_.load() == 0);\n    assert(state_.load() == State::GLOBAL);\n  }\n\n  // This can't increment from 0.\n  Int operator++() noexcept {\n    auto& localCount = *localCount_;\n\n    if (++localCount) {\n      return 42;\n    }\n\n    if (state_.load() == State::GLOBAL_TRANSITION) {\n      std::lock_guard lg(globalMutex_);\n    }\n\n    assert(state_.load() == State::GLOBAL);\n\n    auto value = globalCount_.load();\n    do {\n      if (value == 0) {\n        return 0;\n      }\n    } while (!globalCount_.compare_exchange_weak(value, value + 1));\n\n    return value + 1;\n  }\n\n  Int operator--() noexcept {\n    auto& localCount = *localCount_;\n\n    if (--localCount) {\n      return 42;\n    }\n\n    if (state_.load() == State::GLOBAL_TRANSITION) {\n      std::lock_guard lg(globalMutex_);\n    }\n\n    assert(state_.load() == State::GLOBAL);\n\n    return globalCount_-- - 1;\n  }\n\n  Int operator*() const {\n    if (state_ != State::GLOBAL) {\n      return 42;\n    }\n    return globalCount_.load();\n  }\n\n  void useGlobal() noexcept {\n    std::array<TLRefCount*, 1> ptrs{{this}};\n    useGlobal(ptrs);\n  }\n\n  template <typename Container>\n  static void useGlobal(const Container& refCountPtrs) {\n#ifdef FOLLY_SANITIZE_THREAD\n    // TSAN has a limitation for the number of locks held concurrently, so it's\n    // safer to call useGlobal() serially.\n    if (refCountPtrs.size() > 1) {\n      for (auto refCountPtr : refCountPtrs) {\n        refCountPtr->useGlobal();\n      }\n      return;\n    }\n#endif\n\n    std::vector<std::unique_lock<std::mutex>> lgs_;\n    for (auto refCountPtr : refCountPtrs) {\n      lgs_.emplace_back(refCountPtr->globalMutex_);\n\n      refCountPtr->state_ = State::GLOBAL_TRANSITION;\n    }\n\n    asymmetric_thread_fence_heavy(std::memory_order_seq_cst);\n\n    for (auto refCountPtr : refCountPtrs) {\n      std::weak_ptr<void> collectGuardWeak = refCountPtr->collectGuard_;\n\n      // Make sure we can't create new LocalRefCounts\n      refCountPtr->collectGuard_.reset();\n\n      while (!collectGuardWeak.expired()) {\n        auto accessor = refCountPtr->localCount_.accessAllThreads();\n        for (auto& count : accessor) {\n          count.collect();\n        }\n      }\n\n      refCountPtr->state_ = State::GLOBAL;\n    }\n  }\n\n private:\n  using AtomicInt = std::atomic<Int>;\n\n  enum class State {\n    LOCAL,\n    GLOBAL_TRANSITION,\n    GLOBAL,\n  };\n\n  class LocalRefCount {\n   public:\n    explicit LocalRefCount(TLRefCount& refCount) : refCount_(refCount) {\n      std::lock_guard lg(refCount.globalMutex_);\n\n      collectGuard_ = refCount.collectGuard_;\n    }\n\n    ~LocalRefCount() { collect(); }\n\n    void collect() {\n      {\n        std::lock_guard lg(collectMutex_);\n\n        if (!collectGuard_) {\n          return;\n        }\n\n        collectCount_ = count_.load();\n        refCount_.globalCount_.fetch_add(collectCount_);\n        collectGuard_.reset();\n      }\n      // Once we exit collect(), it's possible TLRefCount may be deleted by our\n      // user since the global count may reach zero. We must therefore ensure\n      // that the thread corresponding to this LocalRefCount is not still\n      // executing the update() function. We wait on inUpdate_ to ensure this.\n      // We won't have to worry about further update() calls beyond this point,\n      // because the state is already non-LOCAL. We also don't need to worry\n      // about if a thread is in an update() call but have not gotten around to\n      // setting inUpdate_ to true yet, because then count_ has also not been\n      // updated and we couldn't reach global zero in that case.\n      folly::detail::Sleeper sleeper;\n      while (inUpdate_.load(std::memory_order_acquire)) {\n        sleeper.wait();\n      }\n    }\n\n    bool operator++() { return update(1); }\n\n    bool operator--() { return update(-1); }\n\n   private:\n    bool update(Int delta) {\n      if (FOLLY_UNLIKELY(refCount_.state_.load() != State::LOCAL)) {\n        return false;\n      }\n\n      // This is equivalent to atomic fetch_add. We know that this operation\n      // is always performed from a single thread.\n      // asymmetric_thread_fence_light() makes things faster than atomic\n      // fetch_add on platforms with native support.\n      auto count = count_.load(std::memory_order_relaxed) + delta;\n      inUpdate_.store(true, std::memory_order_relaxed);\n      SCOPE_EXIT {\n        inUpdate_.store(false, std::memory_order_release);\n      };\n      count_.store(count, std::memory_order_release);\n\n      asymmetric_thread_fence_light(std::memory_order_seq_cst);\n\n      if (FOLLY_UNLIKELY(refCount_.state_.load() != State::LOCAL)) {\n        std::lock_guard lg(collectMutex_);\n\n        if (collectGuard_) {\n          return true;\n        }\n        if (collectCount_ != count) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    AtomicInt count_{0};\n    std::atomic<bool> inUpdate_{false};\n    TLRefCount& refCount_;\n\n    std::mutex collectMutex_;\n    Int collectCount_{0};\n    std::shared_ptr<void> collectGuard_;\n  };\n\n  std::atomic<State> state_{State::LOCAL};\n  folly::ThreadLocal<LocalRefCount, TLRefCount> localCount_;\n  std::atomic<int64_t> globalCount_{1};\n  std::mutex globalMutex_;\n  std::shared_ptr<void> collectGuard_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/memory/test/AtomicReadMostlyMainPtrBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/memory/AtomicReadMostlyMainPtr.h>\n\n#include <folly/Benchmark.h>\n#include <folly/portability/GFlags.h>\n\nusing folly::AtomicReadMostlyMainPtr;\nusing folly::ReadMostlySharedPtr;\n\nBENCHMARK(SingleThreadedLoads, n) {\n  auto sharedInt = std::make_shared<int>(123);\n  AtomicReadMostlyMainPtr<int> data(sharedInt);\n  for (unsigned i = 0; i < n; ++i) {\n    ReadMostlySharedPtr<int> ptr = data.load();\n  }\n}\n\nBENCHMARK(SingleThreadedStores, n) {\n  auto sharedInt = std::make_shared<int>(123);\n  AtomicReadMostlyMainPtr<int> data(sharedInt);\n  for (unsigned i = 0; i < n; ++i) {\n    data.store(sharedInt);\n    ReadMostlySharedPtr<int> ptr = data.load();\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n\n/*\n * Output:\n============================================================================\nAtomicReadMostlyMainPtrBenchmark.cpp            relative  time/iter  iters/s\n============================================================================\nSingleThreadedLoads                                         14.36ns   69.65M\nSingleThreadedStores                                         5.88us  170.15K\n============================================================================\n */\n"
  },
  {
    "path": "folly/concurrency/memory/test/AtomicReadMostlyMainPtrTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/memory/AtomicReadMostlyMainPtr.h>\n\n#include <array>\n#include <functional>\n#include <memory>\n#include <thread>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\nusing folly::AtomicReadMostlyMainPtr;\nusing folly::ReadMostlySharedPtr;\n\nclass AtomicReadMostlyMainPtrSimpleTest : public testing::Test {\n protected:\n  AtomicReadMostlyMainPtrSimpleTest()\n      : sharedPtr123(std::make_shared<int>(123)),\n        sharedPtr123Dup(sharedPtr123),\n        sharedPtr456(std::make_shared<int>(456)),\n        sharedPtr456Dup(sharedPtr456) {}\n\n  AtomicReadMostlyMainPtr<int> data;\n  ReadMostlySharedPtr<int> readMostlySharedPtr;\n  std::shared_ptr<int> nullSharedPtr;\n  std::shared_ptr<int> sharedPtr123;\n  std::shared_ptr<int> sharedPtr123Dup;\n\n  std::shared_ptr<int> sharedPtr456;\n  std::shared_ptr<int> sharedPtr456Dup;\n\n  enum CasType {\n    kWeakCas,\n    kStrongCas,\n  };\n\n  template <typename T0, typename T1, typename T2>\n  bool cas(T0&& atom, T1&& expected, T2&& desired, CasType casType) {\n    if (casType == kWeakCas) {\n      return std::forward<T0>(atom).compare_exchange_weak(\n          std::forward<T1>(expected), std::forward<T2>(desired));\n    } else {\n      return std::forward<T0>(atom).compare_exchange_strong(\n          std::forward<T1>(expected), std::forward<T2>(desired));\n    }\n  }\n\n  void casSuccessTest(CasType casType) {\n    // Nominally, the weak compare exchange allows spurious failures, but we\n    // peek inside the implementation a little to realize that it doesn't.\n\n    // Null -> non-null\n    EXPECT_TRUE(cas(data, nullSharedPtr, sharedPtr123, casType));\n    EXPECT_EQ(sharedPtr123.get(), data.load().get());\n    EXPECT_EQ(nullptr, nullSharedPtr);\n    EXPECT_EQ(sharedPtr123, sharedPtr123Dup);\n\n    // Non-null -> non-null\n    EXPECT_TRUE(cas(data, sharedPtr123, sharedPtr456, casType));\n    EXPECT_EQ(sharedPtr456.get(), data.load().get());\n    EXPECT_EQ(sharedPtr123, sharedPtr123Dup);\n    EXPECT_EQ(sharedPtr456, sharedPtr456Dup);\n\n    // Non-null -> null\n    EXPECT_TRUE(cas(data, sharedPtr456, nullSharedPtr, casType));\n    EXPECT_EQ(nullptr, data.load().get());\n    EXPECT_EQ(sharedPtr123, sharedPtr123Dup);\n    EXPECT_EQ(nullSharedPtr, nullptr);\n  }\n\n  void casFailureTest(CasType casType) {\n    std::shared_ptr<int> expected;\n\n    // Null -> non-null\n    expected = std::make_shared<int>(789);\n    EXPECT_FALSE(cas(data, expected, sharedPtr123, casType));\n    EXPECT_EQ(nullptr, data.load().get());\n    EXPECT_EQ(nullptr, expected);\n    EXPECT_EQ(sharedPtr123, sharedPtr123Dup);\n\n    // Non-null -> non-null\n    expected = std::make_shared<int>(789);\n    data.store(sharedPtr123);\n    EXPECT_FALSE(cas(data, expected, sharedPtr456, casType));\n    EXPECT_EQ(sharedPtr123.get(), data.load().get());\n    EXPECT_EQ(sharedPtr123, expected);\n    EXPECT_EQ(sharedPtr456, sharedPtr456Dup);\n\n    // Non-null -> null\n    expected = std::make_shared<int>(789);\n    data.store(sharedPtr123);\n    EXPECT_FALSE(cas(data, expected, nullSharedPtr, casType));\n    EXPECT_EQ(sharedPtr123.get(), data.load().get());\n    EXPECT_EQ(sharedPtr123, expected);\n    EXPECT_EQ(nullptr, nullSharedPtr);\n  }\n};\n\nTEST_F(AtomicReadMostlyMainPtrSimpleTest, StartsNull) {\n  EXPECT_EQ(data.load(), nullptr);\n}\n\nTEST_F(AtomicReadMostlyMainPtrSimpleTest, Store) {\n  data.store(sharedPtr123);\n  EXPECT_EQ(sharedPtr123.get(), data.load().get());\n}\n\nTEST_F(AtomicReadMostlyMainPtrSimpleTest, Exchange) {\n  data.store(sharedPtr123);\n  auto prev = data.exchange(sharedPtr456);\n  EXPECT_EQ(sharedPtr123, prev);\n  EXPECT_EQ(sharedPtr456.get(), data.load().get());\n}\n\nTEST_F(AtomicReadMostlyMainPtrSimpleTest, CompareExchangeWeakSuccess) {\n  casSuccessTest(kWeakCas);\n}\n\nTEST_F(AtomicReadMostlyMainPtrSimpleTest, CompareExchangeStrongSuccess) {\n  casSuccessTest(kStrongCas);\n}\n\nTEST_F(AtomicReadMostlyMainPtrSimpleTest, CompareExchangeWeakFailure) {\n  casFailureTest(kWeakCas);\n}\n\nTEST_F(AtomicReadMostlyMainPtrSimpleTest, CompareExchangeStrongFailure) {\n  casFailureTest(kStrongCas);\n}\n\nclass AtomicReadMostlyMainPtrCounterTest : public testing::Test {\n protected:\n  struct InstanceCounter {\n    explicit InstanceCounter(int* counter_) : counter(counter_) { ++*counter; }\n\n    ~InstanceCounter() { --*counter; }\n\n    int* counter;\n  };\n\n  AtomicReadMostlyMainPtr<InstanceCounter> data;\n  int counter1 = 0;\n  int counter2 = 0;\n};\n\nTEST_F(AtomicReadMostlyMainPtrCounterTest, DestroysOldValuesSimple) {\n  data.store(std::make_shared<InstanceCounter>(&counter1));\n  EXPECT_EQ(1, counter1);\n  data.store(std::make_shared<InstanceCounter>(&counter2));\n  EXPECT_EQ(0, counter1);\n  EXPECT_EQ(1, counter2);\n}\n\nTEST_F(AtomicReadMostlyMainPtrCounterTest, DestroysOldValuesWithReuse) {\n  for (int i = 0; i < 100; ++i) {\n    data.store(std::make_shared<InstanceCounter>(&counter1));\n    EXPECT_EQ(1, counter1);\n    EXPECT_EQ(0, counter2);\n    data.store(std::make_shared<InstanceCounter>(&counter2));\n    EXPECT_EQ(0, counter1);\n    EXPECT_EQ(1, counter2);\n  }\n}\n\nTEST(AtomicReadMostlyMainPtrTest, HandlesDestructionModifications) {\n  struct RunOnDestruction {\n    explicit RunOnDestruction(std::function<void()> func) : func_(func) {}\n\n    ~RunOnDestruction() { func_(); }\n    std::function<void()> func_;\n  };\n\n  AtomicReadMostlyMainPtr<RunOnDestruction> data;\n\n  // All the ways to trigger the AtomicReadMostlyMainPtr's \"destroy the\n  // pointed-to object\" paths.\n  std::vector<std::function<void()>> outerAccesses = {\n      [&]() { data.store(nullptr); },\n      [&]() { data.exchange(nullptr); },\n      [&]() {\n        auto old = data.load().getStdShared();\n        EXPECT_TRUE(data.compare_exchange_strong(old, nullptr));\n      },\n      // We don't test CAS failure; unlike other accesses, it doesn't destroy\n      // the object pointed to by the AtomicReadMostlyMainPtr, it destroys an\n      // object pointed to by an external shared_ptr. (We'll test that later\n      // on).\n  };\n\n  // All the ways the destructor might access the AtomicReadMostlyMainPtr.\n  std::vector<std::function<void()>> innerAccesses = {\n      [&]() { data.load(); },\n      [&]() { data.store(std::make_shared<RunOnDestruction>([] {})); },\n      [&]() { data.exchange(std::make_shared<RunOnDestruction>([] {})); },\n      [&]() {\n        auto expected = data.load().getStdShared();\n        EXPECT_TRUE(data.compare_exchange_strong(\n            expected, std::make_shared<RunOnDestruction>([] {})));\n      },\n      [&]() {\n        auto notExpected = std::make_shared<RunOnDestruction>([] {});\n        EXPECT_FALSE(data.compare_exchange_strong(\n            notExpected, std::make_shared<RunOnDestruction>([] {})));\n      },\n  };\n\n  for (auto& outerAccess : outerAccesses) {\n    for (auto& innerAccess : innerAccesses) {\n      data.store(std::make_shared<RunOnDestruction>(innerAccess));\n      outerAccess();\n    }\n  }\n\n  // The case we left out is when the destroyed object is actually from the CAS\n  // failure path overwriting a pointer it doesn't hold.\n  for (auto& innerAccess : innerAccesses) {\n    data.store(std::make_shared<RunOnDestruction>([] {}));\n    auto expected = std::make_shared<RunOnDestruction>(innerAccess);\n    auto desired = std::make_shared<RunOnDestruction>([] {});\n    EXPECT_FALSE(data.compare_exchange_strong(expected, desired));\n  }\n}\n\nTEST(AtomicReadMostlyMainPtrTest, TakesMemoryOrders) {\n  // We don't really try to test the memory-order-y-ness of these; just that the\n  // code compiles and runs without anything falling over.\n  AtomicReadMostlyMainPtr<int> data;\n  std::shared_ptr<int> ptr;\n\n  data.load();\n  data.load(std::memory_order_relaxed);\n  data.load(std::memory_order_acquire);\n  data.load(std::memory_order_seq_cst);\n\n  data.store(ptr);\n  data.store(ptr, std::memory_order_relaxed);\n  data.store(ptr, std::memory_order_release);\n  data.store(ptr, std::memory_order_seq_cst);\n\n  data.exchange(ptr);\n  data.exchange(ptr, std::memory_order_relaxed);\n  data.exchange(ptr, std::memory_order_acquire);\n  data.exchange(ptr, std::memory_order_release);\n  data.exchange(ptr, std::memory_order_acq_rel);\n  data.exchange(ptr, std::memory_order_seq_cst);\n\n  auto successOrders = {\n      std::memory_order_relaxed,\n      std::memory_order_acquire,\n      std::memory_order_release,\n      std::memory_order_acq_rel,\n      std::memory_order_seq_cst,\n  };\n  auto failureOrders = {\n      std::memory_order_relaxed,\n      std::memory_order_acquire,\n      std::memory_order_seq_cst,\n  };\n\n  data.compare_exchange_weak(ptr, ptr);\n  data.compare_exchange_strong(ptr, ptr);\n\n  for (auto successOrder : successOrders) {\n    data.compare_exchange_weak(ptr, ptr, successOrder);\n    data.compare_exchange_strong(ptr, ptr, successOrder);\n    for (auto failureOrder : failureOrders) {\n      data.compare_exchange_weak(ptr, ptr, successOrder, failureOrder);\n      data.compare_exchange_strong(ptr, ptr, successOrder, failureOrder);\n    }\n  }\n}\n\nTEST(AtomicReadMostlyMainPtrTest, LongLivedReadsDoNotBlockWrites) {\n  std::vector<ReadMostlySharedPtr<int>> pointers;\n  AtomicReadMostlyMainPtr<int> mainPtr(std::make_shared<int>(123));\n  for (int i = 0; i < 10 * 1000; ++i) {\n    // Try several ways of trigger refcount modifications.\n    auto ptr = mainPtr.load();\n    pointers.push_back(ptr);\n    pointers.push_back(ptr);\n    pointers.emplace_back(std::move(ptr));\n  }\n\n  mainPtr.store(std::make_shared<int>(456));\n  EXPECT_EQ(*mainPtr.load(), 456);\n}\n\nTEST(AtomicReadMostlyMainPtrStressTest, ReadOnly) {\n  const int kReaders = 8;\n  const int kReads = 1000 * 1000;\n  const int kValue = 123;\n\n  AtomicReadMostlyMainPtr<int> data(std::make_shared<int>(kValue));\n  std::vector<std::thread> readers(kReaders);\n  for (auto& thread : readers) {\n    thread = std::thread([&] {\n      for (int i = 0; i < kReads; ++i) {\n        auto ptr = data.load();\n        ASSERT_EQ(*ptr, 123);\n      }\n    });\n  }\n  for (auto& thread : readers) {\n    thread.join();\n  }\n}\n\nTEST(AtomicReadMostlyMainPtrStressTest, ReadWriteConsistency) {\n  const static int kReaders = 4;\n  const static int kWriters = 4;\n  // This gives a test that runs for about 4 seconds on my machine; that's about\n  // the longest we should inflict on test runners.\n  const int kNumWrites = (folly::kIsDebug ? 10 * 1000 : 30 * 1000);\n\n  struct WriterAndData {\n    WriterAndData(int writer_, int data_) : writer(writer_), data(data_) {}\n\n    int writer;\n    int data;\n  };\n\n  struct ConsistencyTracker {\n    ConsistencyTracker() {\n      for (int& i : maxSeenValue) {\n        i = 0;\n      }\n    }\n\n    void update(const WriterAndData& pair) {\n      EXPECT_LE(maxSeenValue[pair.writer], pair.data);\n      maxSeenValue[pair.writer] = pair.data;\n    }\n\n    std::array<int, kWriters> maxSeenValue;\n  };\n\n  AtomicReadMostlyMainPtr<WriterAndData> writerAndData(\n      std::make_shared<WriterAndData>(0, 0));\n  std::atomic<int> numStoppedWriters(0);\n\n  std::vector<std::thread> readers(kReaders);\n  std::vector<std::thread> writers(kWriters);\n\n  for (auto& thread : readers) {\n    thread = std::thread([&] {\n      ConsistencyTracker consistencyTracker;\n      while (numStoppedWriters.load() != kWriters) {\n        auto ptr = writerAndData.load();\n        consistencyTracker.update(*ptr);\n      }\n    });\n  }\n\n  std::atomic<int> casFailures(0);\n\n  for (int threadId = 0; threadId < kWriters; ++threadId) {\n    writers[threadId] = std::thread([&, threadId] {\n      ConsistencyTracker consistencyTracker;\n\n      for (int j = 0; j < kNumWrites; j++) {\n        auto newValue = std::make_shared<WriterAndData>(threadId, j);\n        std::shared_ptr<WriterAndData> oldValue;\n        switch (j % 3) {\n          case 0:\n            writerAndData.store(newValue);\n            break;\n          case 1:\n            oldValue = writerAndData.exchange(newValue);\n            consistencyTracker.update(*oldValue);\n            break;\n          case 2:\n            oldValue = writerAndData.load().getStdShared();\n            consistencyTracker.update(*oldValue);\n            while (!writerAndData.compare_exchange_strong(oldValue, newValue)) {\n              consistencyTracker.update(*oldValue);\n              casFailures.fetch_add(1);\n            }\n        }\n      }\n      numStoppedWriters.fetch_add(1);\n    });\n  }\n\n  for (auto& thread : readers) {\n    thread.join();\n  }\n  for (auto& thread : writers) {\n    thread.join();\n  }\n\n  // This is a test of our test setup itself; we want to make sure it's\n  // sufficiently racy that we actually see concurrent conflicting operations.\n  // On a test on my machine, we see values in the thousands, so hopefully this\n  // is highly unflakey.\n  EXPECT_GE(casFailures.load(), 10);\n}\n\nTEST(AtomicReadMostlyMainPtrStressTest, ReadWriteTsan) {\n  // Do purely read and writes - we don't do anything else, to avoid\n  // extra synchronization that interferes with TSAN.\n  AtomicReadMostlyMainPtr<int> ptr(std::make_shared<int>(0));\n  std::atomic<bool> stop{false};\n  const static int kReaders = 4;\n  const static int kWriters = 4;\n\n  std::vector<std::thread> readers(kReaders);\n  std::vector<std::thread> writers(kWriters);\n\n  for (auto& thread : readers) {\n    thread = std::thread([&] {\n      while (!stop.load(std::memory_order_relaxed)) {\n        ptr.load(std::memory_order_relaxed);\n        std::this_thread::sleep_for(std::chrono::milliseconds(10));\n        break;\n      }\n    });\n  }\n\n  for (auto& thread : writers) {\n    thread = std::thread([&] {\n      while (!stop.load(std::memory_order_relaxed)) {\n        auto newValue = std::make_shared<int>(0);\n        ptr.store(newValue, std::memory_order_relaxed);\n        std::this_thread::sleep_for(std::chrono::milliseconds(15));\n        break;\n      }\n    });\n  }\n\n  std::this_thread::sleep_for(std::chrono::milliseconds(500));\n  stop.store(true, std::memory_order_relaxed);\n\n  for (auto& thread : readers) {\n    thread.join();\n  }\n  for (auto& thread : writers) {\n    thread.join();\n  }\n}\n"
  },
  {
    "path": "folly/concurrency/memory/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"atomic_read_mostly_main_ptr_benchmark\",\n    srcs = [\"AtomicReadMostlyMainPtrBenchmark.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/concurrency/memory:atomic_read_mostly_main_ptr\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"atomic_read_mostly_main_ptr_test\",\n    srcs = [\"AtomicReadMostlyMainPtrTest.cpp\"],\n    deps = [\n        \"//folly/concurrency/memory:atomic_read_mostly_main_ptr\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"read_mostly_shared_ptr_benchmark\",\n    srcs = [\"ReadMostlySharedPtrBenchmark.cpp\"],\n    headers = [],\n    args = [\n        \"--json\",\n    ],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:memory\",\n        \"//folly/concurrency/memory:read_mostly_shared_ptr\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"read_mostly_shared_ptr_stress_test\",\n    srcs = [\"ReadMostlySharedPtrStressTest.cpp\"],\n    headers = [],\n    labels = [\"heavyweight\"],\n    deps = [\n        \"//folly/concurrency/memory:read_mostly_shared_ptr\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"read_mostly_shared_ptr_test\",\n    srcs = [\"ReadMostlySharedPtrTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:memory\",\n        \"//folly/concurrency/memory:read_mostly_shared_ptr\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"primary_ptr_test\",\n    srcs = [\"PrimaryPtrTest.cpp\"],\n    headers = [],\n    labels = [\"oss-broken\"],\n    deps = [\n        \"//folly/concurrency/memory:primary_ptr\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"ref_count_benchmark\",\n    srcs = [\"RefCountBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/concurrency/memory:tl_ref_count\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ref_count_test\",\n    srcs = [\"RefCountTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/concurrency/memory:tl_ref_count\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n"
  },
  {
    "path": "folly/concurrency/memory/test/PrimaryPtrTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <future>\n\n#include <folly/concurrency/memory/PrimaryPtr.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace std::literals::chrono_literals;\n\nTEST(PrimaryPtrTest, Basic) {\n  EXPECT_TRUE(folly::is_cleanup_v<folly::PrimaryPtr<int>>);\n\n  auto ptr = std::make_unique<int>(42);\n  auto rawPtr = ptr.get();\n\n  folly::PrimaryPtr<int> primaryPtr(std::move(ptr));\n  auto primaryPtrRef = primaryPtr.ref();\n  EXPECT_TRUE(!!primaryPtr);\n\n  auto lockedPtr1 = primaryPtr.lock();\n  auto lockedPtr2 = primaryPtrRef.lock();\n  EXPECT_EQ(lockedPtr1.get(), rawPtr);\n  EXPECT_EQ(lockedPtr2.get(), rawPtr);\n\n  EXPECT_EQ(lockedPtr1.use_count(), 3);\n  EXPECT_EQ(lockedPtr2.use_count(), 3);\n\n  EXPECT_TRUE(!!primaryPtr);\n\n  auto joinFuture = std::async(std::launch::async, [&] {\n    primaryPtr.join();\n    EXPECT_TRUE(!primaryPtr);\n  });\n\n  auto lockFailFuture = std::async(std::launch::async, [&] {\n    while (primaryPtr.lock()) {\n      std::this_thread::yield();\n    }\n  });\n\n  EXPECT_EQ(\n      lockFailFuture.wait_for(std::chrono::milliseconds{100}),\n      std::future_status::ready);\n\n  EXPECT_EQ(lockedPtr1.use_count(), 2);\n  EXPECT_EQ(lockedPtr2.use_count(), 2);\n\n  EXPECT_EQ(primaryPtr.lock().get(), nullptr);\n  EXPECT_EQ(primaryPtrRef.lock().get(), nullptr);\n\n  EXPECT_EQ(\n      joinFuture.wait_for(std::chrono::milliseconds{100}),\n      std::future_status::timeout);\n\n  lockedPtr1.reset();\n  lockedPtr2.reset();\n\n  EXPECT_EQ(\n      joinFuture.wait_for(std::chrono::milliseconds{100}),\n      std::future_status::ready);\n\n  EXPECT_TRUE(!primaryPtr);\n\n  ptr = std::make_unique<int>(42);\n  rawPtr = ptr.get();\n  primaryPtr.set(std::move(ptr));\n  EXPECT_TRUE(!!primaryPtr);\n  lockedPtr1 = primaryPtr.lock();\n  EXPECT_EQ(lockedPtr1.get(), rawPtr);\n  lockedPtr1.reset();\n  primaryPtr.join();\n  EXPECT_EQ(primaryPtr.lock().get(), nullptr);\n  EXPECT_TRUE(!primaryPtr);\n}\n\nstruct Primed : folly::Cleanup, folly::EnablePrimaryFromThis<Primed> {\n  folly::PrimaryPtr<int> nested_;\n  folly::CPUThreadPoolExecutor pool_;\n  Primed() : nested_(std::make_unique<int>(42)), pool_(4) {\n    addCleanup(nested_);\n    addCleanup(folly::makeSemiFuture().defer([this](auto&&) {\n      this->pool_.join();\n    }));\n  }\n  using folly::Cleanup::addCleanup;\n  std::shared_ptr<Primed> get_shared() { return masterLockFromThis(); }\n};\n\nTEST(PrimaryPtrTest, BasicCleanup) {\n  auto ptr = std::make_unique<Primed>();\n\n  folly::PrimaryPtr<Primed> primaryPtr(std::move(ptr));\n  int phase = 0;\n  int index = 0;\n\n  primaryPtr.lock()->addCleanup(\n      folly::makeSemiFuture().deferValue([&, expected = index++](folly::Unit) {\n        EXPECT_EQ(phase, 1);\n        EXPECT_EQ(--index, expected);\n      }));\n  primaryPtr.lock()->addCleanup(\n      folly::makeSemiFuture().deferValue([&, expected = index++](folly::Unit) {\n        EXPECT_EQ(phase, 1);\n        EXPECT_EQ(--index, expected);\n      }));\n  EXPECT_EQ(index, 2);\n\n  folly::ManualExecutor exec;\n  phase = 1;\n  primaryPtr.cleanup()\n      .within(1s)\n      .via(folly::getKeepAliveToken(exec))\n      .getVia(&exec);\n  phase = 2;\n  EXPECT_EQ(index, 0);\n}\n\n#if defined(__has_feature)\n#if !__has_feature(address_sanitizer)\nTEST(PrimaryPtrTest, Errors) {\n  auto ptr = std::make_unique<Primed>();\n\n  auto primaryPtr = std::make_unique<folly::PrimaryPtr<Primed>>(std::move(ptr));\n\n  primaryPtr->lock()->addCleanup(\n      folly::makeSemiFuture().deferValue([](folly::Unit) {\n        EXPECT_TRUE(false);\n      }));\n\n  primaryPtr->lock()->addCleanup(\n      folly::makeSemiFuture<folly::Unit>(std::runtime_error(\"failed cleanup\")));\n\n  EXPECT_EXIT(\n      primaryPtr->set(std::unique_ptr<Primed>{}),\n      testing::KilledBySignal(SIGABRT),\n      \".*joined before.*\");\n\n  folly::ManualExecutor exec;\n  EXPECT_EXIT(\n      primaryPtr->cleanup()\n          .within(1s)\n          .via(folly::getKeepAliveToken(exec))\n          .getVia(&exec),\n      testing::KilledBySignal(SIGABRT),\n      \".*noexcept.*\");\n\n  EXPECT_EXIT(\n      primaryPtr.reset(), testing::KilledBySignal(SIGABRT), \".*PrimaryPtr.*\");\n\n  // must leak the PrimaryPtr as its destructor will abort.\n  (void)primaryPtr.release();\n}\n#endif\n#endif\n\nTEST(PrimaryPtrTest, Invariants) {\n  struct BadDerived : Primed {\n    ~BadDerived() {\n      EXPECT_EXIT(\n          addCleanup(folly::makeSemiFuture().deferValue([](folly::Unit) {\n            EXPECT_TRUE(false);\n          })),\n          testing::KilledBySignal(SIGABRT),\n          \".*addCleanup.*\");\n\n      EXPECT_EXIT(\n          addCleanup(folly::makeSemiFuture().deferValue([](folly::Unit) {\n            EXPECT_TRUE(false);\n          })),\n          testing::KilledBySignal(SIGABRT),\n          \".*addCleanup.*\");\n    }\n  };\n  auto ptr = std::make_unique<BadDerived>();\n\n  folly::PrimaryPtr<Primed> primaryPtr(std::move(ptr));\n\n  auto ranCleanup = false;\n  primaryPtr.lock()->addCleanup(\n      folly::makeSemiFuture().deferValue([&](folly::Unit) {\n        ranCleanup = true;\n      }));\n\n  EXPECT_FALSE(ranCleanup);\n\n  {\n    folly::ManualExecutor exec;\n    primaryPtr.cleanup()\n        .within(1s)\n        .via(folly::getKeepAliveToken(exec))\n        .getVia(&exec);\n  }\n\n  EXPECT_TRUE(ranCleanup);\n\n  {\n    folly::ManualExecutor exec;\n    EXPECT_EXIT(\n        primaryPtr.cleanup().via(folly::getKeepAliveToken(exec)).getVia(&exec),\n        testing::KilledBySignal(SIGABRT),\n        \".*already.*\");\n  }\n}\n\nstruct Derived : Primed {};\n\nTEST(PrimaryPtrTest, EnablePrimaryFromThis) {\n  auto ptr = std::make_unique<Derived>();\n  auto rawPtr = ptr.get();\n\n  auto primaryPtr = folly::PrimaryPtr<Primed>{std::move(ptr)};\n  auto primaryPtrRef = primaryPtr.ref();\n\n  auto lockedPtr1 = primaryPtr.lock();\n  auto lockedPtr2 = primaryPtrRef.lock();\n  EXPECT_EQ(lockedPtr1.get(), rawPtr);\n  EXPECT_EQ(lockedPtr2.get(), rawPtr);\n\n  EXPECT_EQ(lockedPtr1.use_count(), 3);\n  EXPECT_EQ(lockedPtr2.use_count(), 3);\n\n  auto lockedPtr3 = lockedPtr1->get_shared();\n  EXPECT_EQ(lockedPtr3.use_count(), 4);\n  EXPECT_EQ(lockedPtr3.get(), rawPtr);\n\n  auto cleanupFuture = std::async(std::launch::async, [&] {\n    folly::ManualExecutor exec;\n    primaryPtr.cleanup()\n        .within(1s)\n        .via(folly::getKeepAliveToken(exec))\n        .getVia(&exec);\n    EXPECT_TRUE(!primaryPtr);\n  });\n\n  EXPECT_EQ(\n      cleanupFuture.wait_for(std::chrono::milliseconds{100}),\n      std::future_status::timeout);\n\n  EXPECT_EQ(lockedPtr1.use_count(), 3);\n  EXPECT_EQ(lockedPtr2.use_count(), 3);\n  EXPECT_EQ(lockedPtr3.use_count(), 3);\n\n  EXPECT_EQ(primaryPtr.lock().get(), nullptr);\n  EXPECT_EQ(primaryPtrRef.lock().get(), nullptr);\n\n  EXPECT_EQ(\n      cleanupFuture.wait_for(std::chrono::milliseconds{100}),\n      std::future_status::timeout);\n\n  lockedPtr1.reset();\n  lockedPtr2.reset();\n  lockedPtr3.reset();\n\n  EXPECT_EQ(\n      cleanupFuture.wait_for(std::chrono::milliseconds{100}),\n      std::future_status::ready);\n\n  EXPECT_TRUE(!primaryPtr);\n}\n\nTEST(PrimaryPtrTest, Moves) {\n  folly::PrimaryPtr<int> a{std::make_unique<int>(42)};\n  folly::PrimaryPtr<int> b{std::make_unique<int>(0)};\n  folly::PrimaryPtr<int> c = exchange(a, std::move(b));\n\n  EXPECT_EQ(*c.lock(), 42);\n  EXPECT_EQ(*a.lock(), 0);\n  EXPECT_FALSE((bool)b);\n\n  swap(c, a);\n  EXPECT_EQ(*c.lock(), 0);\n  EXPECT_EQ(*a.lock(), 42);\n\n  a.join();\n  b.join();\n  c.join();\n}\n\nTEST(PrimaryPtrTest, DefaultConstructPtrRef) {\n  auto ptr = std::make_unique<Primed>();\n  auto primaryPtr = folly::PrimaryPtr<Primed>{std::move(ptr)};\n\n  folly::PrimaryPtrRef<Primed> primaryPtrRef;\n  EXPECT_FALSE((bool)primaryPtrRef.lock());\n\n  primaryPtrRef = primaryPtr.ref();\n  EXPECT_TRUE((bool)primaryPtrRef.lock());\n  EXPECT_EQ(primaryPtrRef.lock(), primaryPtr.lock());\n\n  primaryPtr.join();\n  EXPECT_FALSE((bool)primaryPtr);\n  EXPECT_EQ(primaryPtrRef.lock().get(), nullptr);\n}\n"
  },
  {
    "path": "folly/concurrency/memory/test/ReadMostlySharedPtrBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/memory/ReadMostlySharedPtr.h>\n\n#include <iostream>\n#include <thread>\n\n#include <folly/Benchmark.h>\n#include <folly/Memory.h>\n#include <folly/portability/GFlags.h>\n\ntemplate <\n    template <typename> class MainPtr,\n    template <typename> class WeakPtr,\n    size_t threadCount>\nvoid benchmark(size_t n) {\n  MainPtr<int> mainPtr(std::make_unique<int>(42));\n\n  std::vector<std::thread> ts;\n\n  for (size_t t = 0; t < threadCount; ++t) {\n    ts.emplace_back([&]() {\n      WeakPtr<int> weakPtr(mainPtr);\n      // Prevent the compiler from hoisting code out of the loop.\n      auto op = [&]() FOLLY_NOINLINE { weakPtr.lock(); };\n\n      for (size_t i = 0; i < n; ++i) {\n        op();\n      }\n    });\n  }\n\n  for (auto& t : ts) {\n    t.join();\n  }\n}\n\ntemplate <template <typename> class MainPtr>\nvoid constructorBenchmark(size_t n) {\n  folly::BenchmarkSuspender braces;\n  using deleter_fn = folly::identity_fn; // noop deleter\n  using uptr = std::unique_ptr<int, deleter_fn>;\n  int data = 42;\n  std::vector<MainPtr<int>> ptrs;\n  ptrs.reserve(n);\n\n  // Only measure the cost of constructing the MainPtr\n  braces.dismissing([&] {\n    for (size_t i = 0; i < n; ++i) {\n      ptrs.push_back(MainPtr<int>(uptr(&data)));\n    }\n  });\n}\n\ntemplate <template <typename> class MainPtr>\nvoid destructorBenchmark(size_t n) {\n  folly::BenchmarkSuspender braces;\n  std::vector<MainPtr<int>> ptrs;\n  using deleter_fn = folly::identity_fn; // noop deleter\n  using uptr = std::unique_ptr<int, deleter_fn>;\n  int data = 42;\n\n  ptrs.reserve(n);\n  for (size_t i = 0; i < n; ++i) {\n    ptrs.push_back(MainPtr<int>(uptr(&data)));\n  }\n  // We only want to measure cost of destructing the MainPtr\n  braces.dismissing([&] { ptrs.clear(); });\n}\n\ntemplate <typename T>\nusing TLMainPtr = folly::ReadMostlyMainPtr<T, folly::TLRefCount>;\ntemplate <typename T>\nusing TLWeakPtr = folly::ReadMostlyWeakPtr<T, folly::TLRefCount>;\n\nBENCHMARK(WeakPtrOneThread, n) {\n  benchmark<std::shared_ptr, std::weak_ptr, 1>(n);\n}\n\nBENCHMARK_RELATIVE(TLReadMostlyWeakPtrOneThread, n) {\n  benchmark<TLMainPtr, TLWeakPtr, 1>(n);\n}\n\nBENCHMARK(WeakPtrFourThreads, n) {\n  benchmark<std::shared_ptr, std::weak_ptr, 4>(n);\n}\n\nBENCHMARK_RELATIVE(TLReadMostlyWeakPtrFourThreads, n) {\n  benchmark<TLMainPtr, TLWeakPtr, 4>(n);\n}\n\n/**\n * ReadMostlyMainPtr construction/destruction is significantly more expensive\n * than std::shared_ptr. You should consider using ReadMostly pointers if the\n * MainPtr is created infrequently but shared pointers are copied frequently.\n */\n\nBENCHMARK(SharedPtrCtor, n) {\n  constructorBenchmark<std::shared_ptr>(n);\n}\n\nBENCHMARK_RELATIVE(TLReadMostlyMainPtrCtor, n) {\n  constructorBenchmark<TLMainPtr>(n);\n}\n\nBENCHMARK(SharedPtrDtor, n) {\n  destructorBenchmark<std::shared_ptr>(n);\n}\n\nBENCHMARK_RELATIVE(TLReadMostlyMainPtrDtor, n) {\n  destructorBenchmark<TLMainPtr>(n);\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_slice_usec\", \"100000\", folly::gflags::SET_FLAG_IF_DEFAULT);\n\n  folly::runBenchmarks();\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/concurrency/memory/test/ReadMostlySharedPtrStressTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n#include <thread>\n#include <vector>\n\n#include <folly/concurrency/memory/ReadMostlySharedPtr.h>\n#include <folly/portability/GTest.h>\n\nusing folly::ReadMostlyMainPtr;\n\nclass ReadMostlySharedPtrStressTest : public ::testing::Test {};\n\nTEST_F(ReadMostlySharedPtrStressTest, ReaderWriter) {\n  struct alignas(64) Data {\n    std::atomic<int> lastValue{};\n  };\n\n  const static int kNumReaders = 4;\n  const static int kNumIters = 100 * 1000;\n\n  Data writerDone;\n  writerDone.lastValue.store(0, std::memory_order_relaxed);\n\n  // How the readers tell the writer to proceed.\n  std::array<Data, kNumReaders> readerDone;\n  for (auto& data : readerDone) {\n    data.lastValue.store(0, std::memory_order_relaxed);\n  }\n\n  ReadMostlyMainPtr<int> rmmp(std::make_shared<int>(0));\n\n  std::vector<std::thread> readers(kNumReaders);\n  for (int threadId = 0; threadId < kNumReaders; ++threadId) {\n    readers[threadId] = std::thread([&, threadId] {\n      for (int i = 1; i < kNumIters; ++i) {\n        while (writerDone.lastValue.load(std::memory_order_acquire) != i) {\n          // Spin until the write has completed.\n        }\n        auto rmsp = rmmp.getShared();\n        readerDone[threadId].lastValue.store(i, std::memory_order_release);\n      }\n    });\n  }\n\n  for (int i = 1; i < kNumIters; ++i) {\n    auto sp = rmmp.getStdShared();\n    rmmp.reset(std::make_shared<int>(i));\n    writerDone.lastValue.store(i, std::memory_order_release);\n    for (int threadId = 0; threadId < kNumReaders; ++threadId) {\n      auto& myReaderDone = readerDone[threadId];\n      while (myReaderDone.lastValue.load(std::memory_order_acquire) != i) {\n        // Spin until reader has completed.\n      }\n    }\n  }\n\n  for (auto& thread : readers) {\n    thread.join();\n  }\n}\n"
  },
  {
    "path": "folly/concurrency/memory/test/ReadMostlySharedPtrTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n#include <condition_variable>\n#include <mutex>\n#include <thread>\n\n#include <folly/Memory.h>\n#include <folly/concurrency/memory/ReadMostlySharedPtr.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing folly::ReadMostlyMainPtr;\nusing folly::ReadMostlyMainPtrDeleter;\nusing folly::ReadMostlySharedPtr;\nusing folly::ReadMostlyWeakPtr;\n\n// send SIGALRM to test process after this many seconds\nconst unsigned int TEST_TIMEOUT = 10;\n\nclass ReadMostlySharedPtrTest : public ::testing::Test {\n public:\n  ReadMostlySharedPtrTest() { alarm(TEST_TIMEOUT); }\n};\n\nstruct TestObject {\n  int value;\n  std::atomic<int>& counter;\n\n  TestObject(int value_, std::atomic<int>& counter_)\n      : value(value_), counter(counter_) {\n    ++counter;\n  }\n\n  ~TestObject() {\n    assert(counter.load() > 0);\n    --counter;\n  }\n};\n\n// One side calls requestAndWait(), the other side calls waitForRequest(),\n// does something and calls completed().\nclass Coordinator {\n public:\n  void requestAndWait() {\n    requestBaton_.post();\n    completeBaton_.wait();\n  }\n\n  void waitForRequest() { requestBaton_.wait(); }\n\n  void completed() { completeBaton_.post(); }\n\n private:\n  folly::Baton<> requestBaton_;\n  folly::Baton<> completeBaton_;\n};\n\nTEST_F(ReadMostlySharedPtrTest, BasicStores) {\n  ReadMostlyMainPtr<TestObject> ptr;\n\n  // Store 1.\n  std::atomic<int> cnt1{0};\n  ptr.reset(std::make_unique<TestObject>(1, cnt1));\n  EXPECT_EQ(1, cnt1.load());\n\n  // Store 2, check that 1 is destroyed.\n  std::atomic<int> cnt2{0};\n  ptr.reset(std::make_unique<TestObject>(2, cnt2));\n  EXPECT_EQ(1, cnt2.load());\n  EXPECT_EQ(0, cnt1.load());\n\n  // Store nullptr, check that 2 is destroyed.\n  ptr.reset(nullptr);\n  EXPECT_EQ(0, cnt2.load());\n}\n\nTEST_F(ReadMostlySharedPtrTest, BasicLoads) {\n  std::atomic<int> cnt2{0};\n  ReadMostlySharedPtr<TestObject> x;\n\n  {\n    ReadMostlyMainPtr<TestObject> ptr;\n\n    // Check that ptr is initially nullptr.\n    EXPECT_EQ(ptr.get(), nullptr);\n\n    std::atomic<int> cnt1{0};\n    ptr.reset(std::make_unique<TestObject>(1, cnt1));\n    EXPECT_EQ(1, cnt1.load());\n\n    x = ptr;\n    EXPECT_EQ(1, x->value);\n\n    ptr.reset(std::make_unique<TestObject>(2, cnt2));\n    EXPECT_EQ(1, cnt2.load());\n    EXPECT_EQ(1, cnt1.load());\n\n    x = ptr;\n    EXPECT_EQ(2, x->value);\n    EXPECT_EQ(0, cnt1.load());\n\n    ptr.reset(nullptr);\n    EXPECT_EQ(1, cnt2.load());\n  }\n\n  EXPECT_EQ(1, cnt2.load());\n\n  x.reset();\n  EXPECT_EQ(0, cnt2.load());\n}\n\nTEST_F(ReadMostlySharedPtrTest, LoadsFromThreads) {\n  std::atomic<int> cnt{0};\n\n  {\n    ReadMostlyMainPtr<TestObject> ptr;\n    Coordinator loads[7];\n\n    std::thread t1([&] {\n      loads[0].waitForRequest();\n      EXPECT_EQ(ptr.getShared(), nullptr);\n      loads[0].completed();\n\n      loads[3].waitForRequest();\n      EXPECT_EQ(2, ptr.getShared()->value);\n      loads[3].completed();\n\n      loads[4].waitForRequest();\n      EXPECT_EQ(4, ptr.getShared()->value);\n      loads[4].completed();\n\n      loads[5].waitForRequest();\n      EXPECT_EQ(5, ptr.getShared()->value);\n      loads[5].completed();\n    });\n\n    std::thread t2([&] {\n      loads[1].waitForRequest();\n      EXPECT_EQ(1, ptr.getShared()->value);\n      loads[1].completed();\n\n      loads[2].waitForRequest();\n      EXPECT_EQ(2, ptr.getShared()->value);\n      loads[2].completed();\n\n      loads[6].waitForRequest();\n      EXPECT_EQ(5, ptr.getShared()->value);\n      loads[6].completed();\n    });\n\n    loads[0].requestAndWait();\n\n    ptr.reset(std::make_unique<TestObject>(1, cnt));\n    loads[1].requestAndWait();\n\n    ptr.reset(std::make_unique<TestObject>(2, cnt));\n    loads[2].requestAndWait();\n    loads[3].requestAndWait();\n\n    ptr.reset(std::make_unique<TestObject>(3, cnt));\n    ptr.reset(std::make_unique<TestObject>(4, cnt));\n    loads[4].requestAndWait();\n\n    ptr.reset(std::make_unique<TestObject>(5, cnt));\n    loads[5].requestAndWait();\n    loads[6].requestAndWait();\n\n    EXPECT_EQ(1, cnt.load());\n\n    t1.join();\n    t2.join();\n  }\n\n  EXPECT_EQ(0, cnt.load());\n}\n\nTEST_F(ReadMostlySharedPtrTest, Ctor) {\n  std::atomic<int> cnt1{0};\n  {\n    ReadMostlyMainPtr<TestObject> ptr(std::make_unique<TestObject>(1, cnt1));\n\n    EXPECT_EQ(1, ptr.getShared()->value);\n  }\n\n  EXPECT_EQ(0, cnt1.load());\n}\n\nTEST_F(ReadMostlySharedPtrTest, ClearingCache) {\n  std::atomic<int> cnt1{0};\n  std::atomic<int> cnt2{0};\n\n  ReadMostlyMainPtr<TestObject> ptr;\n\n  // Store 1.\n  ptr.reset(std::make_unique<TestObject>(1, cnt1));\n\n  Coordinator c;\n\n  std::thread t([&] {\n    // Cache the pointer for this thread.\n    ptr.getShared();\n    c.requestAndWait();\n  });\n\n  // Wait for the thread to cache pointer.\n  c.waitForRequest();\n  EXPECT_EQ(1, cnt1.load());\n\n  // Store 2 and check that 1 is destroyed.\n  ptr.reset(std::make_unique<TestObject>(2, cnt2));\n  EXPECT_EQ(0, cnt1.load());\n\n  // Unblock thread.\n  c.completed();\n  t.join();\n}\n\nsize_t useGlobalCalls = 0;\n\nclass TestRefCount {\n public:\n  ~TestRefCount() noexcept { DCHECK_EQ(count_.load(), 0); }\n\n  int64_t operator++() noexcept {\n    auto ret = ++count_;\n    DCHECK_GT(ret, 0);\n    return ret;\n  }\n\n  int64_t operator--() noexcept {\n    auto ret = --count_;\n    DCHECK_GE(ret, 0);\n    return ret;\n  }\n\n  int64_t operator*() noexcept { return count_.load(); }\n\n  void useGlobal() { ++useGlobalCalls; }\n\n  template <typename Container>\n  static void useGlobal(const Container&) {\n    ++useGlobalCalls;\n  }\n\n private:\n  std::atomic<int64_t> count_{1};\n};\n\nTEST_F(ReadMostlySharedPtrTest, ReadMostlyMainPtrDeleter) {\n  EXPECT_EQ(0, useGlobalCalls);\n  {\n    ReadMostlyMainPtr<int, TestRefCount> ptr1(std::make_shared<int>(42));\n    ReadMostlyMainPtr<int, TestRefCount> ptr2(std::make_shared<int>(42));\n  }\n\n  EXPECT_EQ(4, useGlobalCalls);\n\n  useGlobalCalls = 0;\n  {\n    ReadMostlyMainPtr<int, TestRefCount> ptr1(std::make_shared<int>(42));\n    ReadMostlyMainPtr<int, TestRefCount> ptr2(std::make_shared<int>(42));\n\n    ReadMostlyMainPtrDeleter<TestRefCount> deleter;\n    deleter.add(std::move(ptr1));\n    deleter.add(std::move(ptr2));\n  }\n\n  EXPECT_EQ(1, useGlobalCalls);\n}\n\nTEST_F(ReadMostlySharedPtrTest, nullptr) {\n  {\n    ReadMostlyMainPtr<int, TestRefCount> nptr;\n    EXPECT_TRUE(nptr == nullptr);\n    EXPECT_TRUE(nullptr == nptr);\n    EXPECT_EQ(nptr, nullptr);\n    EXPECT_EQ(nullptr, nptr);\n    EXPECT_FALSE(nptr);\n    EXPECT_TRUE(!nptr);\n\n    ReadMostlyMainPtr<int, TestRefCount> ptr(std::make_shared<int>(42));\n    EXPECT_FALSE(ptr == nullptr);\n    EXPECT_FALSE(nullptr == ptr);\n    EXPECT_NE(ptr, nullptr);\n    EXPECT_NE(nullptr, ptr);\n    EXPECT_FALSE(!ptr);\n    EXPECT_TRUE(ptr);\n  }\n  {\n    ReadMostlySharedPtr<int, TestRefCount> nptr;\n    EXPECT_TRUE(nptr == nullptr);\n    EXPECT_TRUE(nullptr == nptr);\n    EXPECT_EQ(nptr, nullptr);\n    EXPECT_EQ(nullptr, nptr);\n    EXPECT_FALSE(nptr);\n    EXPECT_TRUE(!nptr);\n\n    ReadMostlyMainPtr<int, TestRefCount> ptr(std::make_shared<int>(42));\n    EXPECT_FALSE(ptr == nullptr);\n    EXPECT_FALSE(nullptr == ptr);\n    EXPECT_NE(ptr, nullptr);\n    EXPECT_NE(nullptr, ptr);\n    EXPECT_FALSE(!ptr);\n    EXPECT_TRUE(ptr);\n  }\n}\n\nTEST_F(ReadMostlySharedPtrTest, getStdShared) {\n  const ReadMostlyMainPtr<int> rmmp1(std::make_shared<int>(42));\n\n  ReadMostlyMainPtr<int> rmmp2;\n  rmmp2.reset(rmmp1.getStdShared());\n\n  const ReadMostlySharedPtr<int> rmsp1 = rmmp1.getShared();\n  ReadMostlySharedPtr<int> rmsp2(rmsp1);\n\n  // No conditions to check; we just wanted to ensure this compiles.\n  SUCCEED();\n}\n\nstruct Base {\n  virtual ~Base() = default;\n\n  virtual std::string getName() const { return \"Base\"; }\n};\n\nstruct Derived : public Base {\n  std::string getName() const override { return \"Derived\"; }\n};\n\nTEST_F(ReadMostlySharedPtrTest, casts) {\n  ReadMostlyMainPtr<Derived> rmmp(std::make_shared<Derived>());\n  ReadMostlySharedPtr<Derived> rmsp(rmmp);\n  {\n    ReadMostlySharedPtr<Base> rmspbase(rmmp);\n    EXPECT_EQ(\"Derived\", rmspbase->getName());\n    EXPECT_EQ(\"Derived\", rmspbase.getStdShared()->getName());\n  }\n  {\n    ReadMostlySharedPtr<Base> rmspbase(rmsp);\n    EXPECT_EQ(\"Derived\", rmspbase->getName());\n    EXPECT_EQ(\"Derived\", rmspbase.getStdShared()->getName());\n  }\n  {\n    ReadMostlySharedPtr<Base> rmspbase;\n    rmspbase = rmsp;\n    EXPECT_EQ(\"Derived\", rmspbase->getName());\n    EXPECT_EQ(\"Derived\", rmspbase.getStdShared()->getName());\n  }\n  {\n    auto rmspcopy = rmsp;\n    ReadMostlySharedPtr<Base> rmspbase(std::move(rmspcopy));\n    EXPECT_EQ(\"Derived\", rmspbase->getName());\n    EXPECT_EQ(\"Derived\", rmspbase.getStdShared()->getName());\n  }\n  {\n    auto rmspcopy = rmsp;\n    ReadMostlySharedPtr<Base> rmspbase;\n    rmspbase = std::move(rmspcopy);\n    EXPECT_EQ(\"Derived\", rmspbase->getName());\n    EXPECT_EQ(\"Derived\", rmspbase.getStdShared()->getName());\n  }\n}\n"
  },
  {
    "path": "folly/concurrency/memory/test/RefCountBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/Benchmark.h>\n#include <folly/concurrency/memory/TLRefCount.h>\n\nnamespace folly {\n\ntemplate <typename Counter>\nvoid shutdown(Counter&) {}\n\nvoid shutdown(TLRefCount& c) {\n  c.useGlobal();\n  --c;\n}\n\ntemplate <typename Counter, size_t threadCount>\nvoid benchmark(size_t n) {\n  Counter x;\n\n  std::vector<std::thread> ts;\n\n  for (size_t t = 0; t < threadCount; ++t) {\n    ts.emplace_back([&]() {\n      for (size_t i = 0; i < n; ++i) {\n        ++x;\n      }\n      for (size_t i = 0; i < n; ++i) {\n        --x;\n      }\n    });\n  }\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  shutdown(x);\n}\n\nBENCHMARK(TLRefCountOneThread, n) {\n  benchmark<TLRefCount, 1>(n);\n}\n\nBENCHMARK(TLRefCountFourThreads, n) {\n  benchmark<TLRefCount, 4>(n);\n}\n\nBENCHMARK(TLRefCountTwentyFourThreads, n) {\n  benchmark<TLRefCount, 24>(n);\n}\n\nBENCHMARK(AtomicOneThread, n) {\n  benchmark<std::atomic<size_t>, 1>(n);\n}\n\nBENCHMARK(AtomicFourThreads, n) {\n  benchmark<std::atomic<size_t>, 4>(n);\n}\n\nBENCHMARK(AtomicTwentyFourThreads, n) {\n  benchmark<std::atomic<size_t>, 24>(n);\n}\n\n} // namespace folly\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_slice_usec\", \"100000\", folly::gflags::SET_FLAG_IF_DEFAULT);\n\n  folly::runBenchmarks();\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/concurrency/memory/test/RefCountTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/concurrency/memory/TLRefCount.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/test/Barrier.h>\n\nnamespace folly {\n\ntemplate <typename RefCount>\nvoid basicTest() {\n  constexpr size_t numIters = 100000;\n  constexpr size_t numThreads = 10;\n\n  size_t got0 = 0;\n\n  RefCount count;\n\n  folly::Baton<> b;\n\n  std::vector<std::thread> ts;\n  folly::Baton<> threadBatons[numThreads];\n  for (size_t t = 0; t < numThreads; ++t) {\n    ts.emplace_back([&count, &b, &got0, t, &threadBatons] {\n      for (size_t i = 0; i < numIters; ++i) {\n        auto ret = ++count;\n\n        EXPECT_TRUE(ret > 1);\n        if (i == 0) {\n          threadBatons[t].post();\n        }\n      }\n\n      if (t == 0) {\n        b.post();\n      }\n\n      for (size_t i = 0; i < numIters; ++i) {\n        auto ret = --count;\n\n        if (ret == 0) {\n          ++got0;\n          EXPECT_EQ(numIters - 1, i);\n        }\n      }\n    });\n  }\n\n  for (size_t t = 0; t < numThreads; ++t) {\n    threadBatons[t].wait();\n  }\n\n  b.wait();\n\n  count.useGlobal();\n  if (--count == 0) {\n    ++got0;\n  }\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  EXPECT_EQ(1, got0);\n\n  EXPECT_EQ(0, ++count);\n  EXPECT_EQ(0, ++count);\n}\n\ntemplate <typename RefCount>\nvoid stressTest(size_t itersCount) {\n  for (size_t i = 0; i < itersCount; ++i) {\n    RefCount count;\n    std::mutex mutex;\n    int a{1};\n\n    std::thread t1([&]() {\n      if (++count) {\n        {\n          std::lock_guard lg(mutex);\n          EXPECT_EQ(1, a);\n        }\n        --count;\n      }\n    });\n\n    std::thread t2([&]() {\n      count.useGlobal();\n      if (--count == 0) {\n        std::lock_guard lg(mutex);\n        a = 0;\n      }\n    });\n\n    t1.join();\n    t2.join();\n\n    EXPECT_EQ(0, ++count);\n  }\n}\n\nTEST(TLRefCount, Basic) {\n  basicTest<TLRefCount>();\n}\n\nTEST(TLRefCount, Stress) {\n  // This is absurdly slow, so we can't\n  // do it that many times.\n  stressTest<TLRefCount>(500);\n}\n\nTEST(TLRefCount, SafeToDeleteWhenReachingZero) {\n  // Tests that it is safe to delete a TLRefCount when it is already in global\n  // state and its ref count reaches zero. This is a reasonable assumption\n  // since data structures typically embed a TLRefCount object and delete\n  // themselves when the refcount reaches 0.\n  TLRefCount* count = new TLRefCount();\n\n  folly::test::Barrier done(2); // give time for TSAN to catch issues\n  folly::Baton<> batonUnref;\n  int times_deleted = 0;\n\n  std::thread t1([&] {\n    ++*count;\n    batonUnref.post();\n\n    if (--*count == 0) {\n      times_deleted++;\n      delete count;\n    }\n    done.wait();\n  });\n\n  std::thread t2([&] {\n    // Make sure thread 1 already grabbed a reference first, otherwise we might\n    // destroy it before thread 1 had a chance.\n    batonUnref.wait();\n\n    count->useGlobal();\n    if (--*count == 0) {\n      times_deleted++;\n      delete count;\n    }\n    done.wait();\n  });\n\n  t1.join();\n  t2.join();\n  EXPECT_EQ(times_deleted, 1);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/test/AtomicSharedPtrCounted.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstdint>\n#include <stdexcept>\n#include <type_traits>\n\n#include <glog/logging.h>\n\nstruct counted_shared_tag {};\ntemplate <template <typename> class Atom = std::atomic>\nstruct intrusive_shared_count {\n  intrusive_shared_count() { counts.store(0); }\n  void add_ref(uint64_t count = 1) { counts.fetch_add(count); }\n\n  uint64_t release_ref(uint64_t count = 1) { return counts.fetch_sub(count); }\n  Atom<uint64_t> counts;\n};\n\ntemplate <template <typename> class Atom = std::atomic>\nstruct counted_ptr_base {\n protected:\n  static intrusive_shared_count<Atom>* getRef(void* pt) {\n    char* p = (char*)pt;\n    p -= sizeof(intrusive_shared_count<Atom>);\n    return (intrusive_shared_count<Atom>*)p;\n  }\n};\n\n// basically shared_ptr, but only supports make_counted, and provides\n// access to add_ref / release_ref with a count.  Alias not supported.\ntemplate <typename T, template <typename> class Atom = std::atomic>\nclass counted_ptr : public counted_ptr_base<Atom> {\n public:\n  T* p_;\n  counted_ptr() : p_(nullptr) {}\n  counted_ptr(counted_shared_tag, T* p) : p_(p) {\n    if (p_) {\n      counted_ptr_base<Atom>::getRef(p_)->add_ref();\n    }\n  }\n\n  counted_ptr(const counted_ptr& o) : p_(o.p_) {\n    if (p_) {\n      counted_ptr_base<Atom>::getRef(p_)->add_ref();\n    }\n  }\n  counted_ptr& operator=(const counted_ptr& o) {\n    if (p_ && counted_ptr_base<Atom>::getRef(p_)->release_ref() == 1) {\n      p_->~T();\n      free(counted_ptr_base<Atom>::getRef(p_));\n    }\n    p_ = o.p_;\n    if (p_) {\n      counted_ptr_base<Atom>::getRef(p_)->add_ref();\n    }\n    return *this;\n  }\n  explicit counted_ptr(T* p) : p_(p) { CHECK(!p); }\n  ~counted_ptr() {\n    if (p_ && counted_ptr_base<Atom>::getRef(p_)->release_ref() == 1) {\n      p_->~T();\n      free(counted_ptr_base<Atom>::getRef(p_));\n    }\n  }\n  typename std::add_lvalue_reference<T>::type operator*() const { return *p_; }\n\n  T* get() const { return p_; }\n  T* operator->() const { return p_; }\n  explicit operator bool() const { return p_ == nullptr ? false : true; }\n  bool operator==(const counted_ptr<T, Atom>& p) const {\n    return get() == p.get();\n  }\n};\n\ntemplate <\n    template <typename> class Atom = std::atomic,\n    typename T,\n    typename... Args>\ncounted_ptr<T, Atom> make_counted(Args&&... args) {\n  char* mem = (char*)malloc(sizeof(T) + sizeof(intrusive_shared_count<Atom>));\n  if (!mem) {\n    throw std::bad_alloc();\n  }\n  new (mem) intrusive_shared_count<Atom>();\n  T* ptr = (T*)(mem + sizeof(intrusive_shared_count<Atom>));\n  new (ptr) T(std::forward<Args>(args)...);\n  return counted_ptr<T, Atom>(counted_shared_tag(), ptr);\n}\n\ntemplate <template <typename> class Atom = std::atomic>\nclass counted_ptr_internals : public counted_ptr_base<Atom> {\n public:\n  template <typename T, typename... Args>\n  static counted_ptr<T, Atom> make_ptr(Args&&... args) {\n    return make_counted<Atom, T>(std::forward<Args...>(args...));\n  }\n  template <typename T>\n  using CountedPtr = counted_ptr<T, Atom>;\n  using counted_base = void;\n\n  template <typename T>\n  static counted_base* get_counted_base(const counted_ptr<T, Atom>& bar) {\n    return bar.p_;\n  }\n\n  template <typename T>\n  static T* get_shared_ptr(counted_base* base) {\n    return (T*)base;\n  }\n\n  template <typename T>\n  static T* release_ptr(counted_ptr<T, Atom>& p) {\n    auto res = p.p_;\n    p.p_ = nullptr;\n    return res;\n  }\n\n  template <typename T>\n  static counted_ptr<T, Atom> get_shared_ptr_from_counted_base(\n      counted_base* base, bool inc = true) {\n    auto res = counted_ptr<T, Atom>(counted_shared_tag(), (T*)(base));\n    if (!inc) {\n      release_shared<T>(base, 1);\n    }\n    return res;\n  }\n\n  static void inc_shared_count(counted_base* base, int64_t count) {\n    counted_ptr_base<Atom>::getRef(base)->add_ref(count);\n  }\n\n  template <typename T>\n  static void release_shared(counted_base* base, uint64_t count) {\n    if (count == counted_ptr_base<Atom>::getRef(base)->release_ref(count)) {\n      ((T*)base)->~T();\n      free(counted_ptr_base<Atom>::getRef(base));\n    }\n  }\n};\n"
  },
  {
    "path": "folly/concurrency/test/AtomicSharedPtrPerformance.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/portability/Config.h>\n\n// AtomicSharedPtr-detail.h only works with libstdc++, so skip these tests for\n// other vendors\n#if defined(__GLIBCXX__)\n\n#include <folly/concurrency/AtomicSharedPtr.h>\n\n#include <atomic>\n#include <chrono>\n#include <condition_variable>\n#include <iostream>\n#include <mutex>\n#include <thread>\n#include <vector>\n\nusing std::atomic;\nusing std::cerr;\nusing std::condition_variable;\nusing std::cout;\nusing std::endl;\nusing std::is_same;\nusing std::make_shared;\nusing std::memory_order;\nusing std::memory_order_acq_rel;\nusing std::memory_order_acquire;\nusing std::memory_order_consume;\nusing std::memory_order_relaxed;\nusing std::memory_order_release;\nusing std::memory_order_seq_cst;\nusing std::move;\nusing std::mutex;\nusing std::ref;\nusing std::shared_ptr;\nusing std::thread;\nusing std::unique_lock;\nusing std::vector;\nusing std::chrono::duration_cast;\nusing std::chrono::microseconds;\nusing std::chrono::steady_clock;\n\nstatic uint64_t nowMicro() {\n  return duration_cast<microseconds>(steady_clock::now().time_since_epoch())\n      .count();\n}\n\nstatic const char* memoryOrder(memory_order order) {\n  switch (order) {\n    case memory_order_relaxed:\n      return \"relaxed\";\n    case memory_order_acquire:\n      return \"acquire\";\n    case memory_order_consume:\n      return \"consume\";\n    case memory_order_release:\n      return \"release\";\n    case memory_order_acq_rel:\n      return \"acq_rel\";\n    case memory_order_seq_cst:\n      return \"seq_cst\";\n    default:\n      return \"\";\n  }\n}\n\ntemplate <typename T>\nvoid uncontended_read_write(\n    size_t readers,\n    size_t writers,\n    memory_order readOrder = memory_order_seq_cst,\n    memory_order writeOrder = memory_order_seq_cst) {\n  std::shared_ptr<int> zero = std::make_shared<int>(0);\n  T a(zero);\n  auto time1 = nowMicro();\n  for (size_t i = 0; i < 10000000; ++i) {\n    for (size_t j = 0; j < readers; ++j) {\n      a.load(readOrder);\n    }\n    for (size_t j = 0; j < writers; ++j) {\n      a.store(zero, writeOrder);\n    }\n  }\n  auto time2 = nowMicro();\n  cout << \"Uncontended Read(\" << readers << \",\" << memoryOrder(readOrder)\n       << \")/Write(\" << writers << \",\" << memoryOrder(writeOrder)\n       << \"): \" << (time2 - time1) << \" \\u03BCs\" << endl;\n}\n\ntemplate <typename T>\nvoid read_asp(\n    unique_lock<mutex> lock,\n    condition_variable& cvar,\n    atomic<bool>& go,\n    T& aptr,\n    memory_order order) {\n  cvar.wait(lock, [&go]() {\n    return atomic_load_explicit(&go, memory_order_acquire);\n  });\n  lock.unlock();\n  for (size_t i = 0; i < 1000000; ++i) {\n    aptr.load(order);\n  }\n}\n\ntemplate <typename T>\nvoid write_asp(\n    unique_lock<mutex> lock,\n    condition_variable& cvar,\n    atomic<bool>& go,\n    T& aptr,\n    memory_order order) {\n  std::shared_ptr<int> zero = std::make_shared<int>(0);\n  cvar.wait(lock, [&go]() {\n    return atomic_load_explicit(&go, memory_order_acquire);\n  });\n  lock.unlock();\n  for (size_t i = 0; i < 1000000; ++i) {\n    aptr.store(zero, order);\n  }\n}\n\ntemplate <typename T>\nvoid contended_read_write(\n    size_t readers,\n    size_t writers,\n    memory_order readOrder = memory_order_seq_cst,\n    memory_order writeOrder = memory_order_seq_cst) {\n  vector<thread> threads;\n  mutex lock;\n  condition_variable cvar;\n  atomic<bool> go{false};\n  T aptr(std::make_shared<int>());\n  for (size_t i = 0; i < readers; ++i) {\n    unique_lock<mutex> ulock(lock);\n    threads.emplace_back(\n        &read_asp<T>,\n        std::move(ulock),\n        ref(cvar),\n        ref(go),\n        ref(aptr),\n        readOrder);\n  }\n  for (size_t i = 0; i < writers; ++i) {\n    unique_lock<mutex> ulock(lock);\n    threads.emplace_back(\n        &write_asp<T>,\n        std::move(ulock),\n        ref(cvar),\n        ref(go),\n        ref(aptr),\n        writeOrder);\n  }\n  unique_lock<mutex> ulock(lock);\n  ulock.unlock();\n  atomic_store_explicit(&go, true, memory_order_release);\n  auto time1 = nowMicro();\n  cvar.notify_all();\n  for (auto& thread : threads) {\n    thread.join();\n  }\n  auto time2 = nowMicro();\n  cout << \"Contended Read(\" << readers << \",\" << memoryOrder(readOrder)\n       << \")/Write(\" << writers << \",\" << memoryOrder(writeOrder)\n       << \"): \" << (time2 - time1) << \" \\u03BCs\" << endl;\n}\n\ntemplate <typename T>\nvoid document_noexcept() {\n  shared_ptr<int> ptr = make_shared<int>(0);\n  T aptr{};\n  cout << \"  ctor () is \" << (noexcept(T()) ? \"\" : \"not \") << \"noexcept.\"\n       << endl;\n  cout << \"  ctor (ptr) is \" << (noexcept(T(ptr)) ? \"\" : \"not \") << \"noexcept.\"\n       << endl;\n#define _(A)                                                                  \\\n  do {                                                                        \\\n    cout << \"  \" #A \" is \" << (noexcept(aptr.A) ? \"\" : \"not \") << \"noexcept.\" \\\n         << endl;                                                             \\\n  } while (0)\n  _(operator=(ptr));\n\n  _(is_lock_free());\n\n  _(store(ptr));\n  _(store(ptr, memory_order_seq_cst));\n\n  _(load());\n  _(load(memory_order_seq_cst));\n\n  _(exchange(ptr));\n  _(exchange(ptr, memory_order_seq_cst));\n\n  _(compare_exchange_strong(ptr, ptr));\n  _(compare_exchange_strong(ptr, ptr, memory_order_seq_cst));\n  _(compare_exchange_strong(\n      ptr, ptr, memory_order_seq_cst, memory_order_seq_cst));\n\n  _(compare_exchange_weak(ptr, ptr));\n  _(compare_exchange_weak(ptr, ptr, memory_order_seq_cst));\n  _(compare_exchange_weak(\n      ptr, ptr, memory_order_seq_cst, memory_order_seq_cst));\n\n#undef _\n  cout << \"  operator std::shared_ptr<T>() is \"\n       << (noexcept(ptr = aptr) ? \"\" : \"not \") << \"noexcept.\" << endl;\n}\n\ntemplate <typename T>\nvoid runSuite() {\n  document_noexcept<T>();\n  uncontended_read_write<T>(10, 0);\n  uncontended_read_write<T>(0, 10);\n  uncontended_read_write<T>(10, 10);\n  uncontended_read_write<T>(10, 10, memory_order_relaxed, memory_order_relaxed);\n  uncontended_read_write<T>(10, 10, memory_order_acquire, memory_order_release);\n  contended_read_write<T>(10, 0);\n  contended_read_write<T>(0, 10);\n  contended_read_write<T>(1, 1);\n  contended_read_write<T>(5, 1);\n  contended_read_write<T>(10, 1);\n  contended_read_write<T>(100, 1);\n  contended_read_write<T>(100, 1, memory_order_relaxed, memory_order_relaxed);\n  contended_read_write<T>(100, 1, memory_order_acquire, memory_order_release);\n}\n\nint main(int, char**) {\n  cout << endl << \"Folly implementation.  Is lock free: 1\" << endl;\n  runSuite<folly::atomic_shared_ptr<int>>();\n  return 0;\n}\n\n#else // defined(__GLIBCXX__)\n\nint main(int, char**) {\n  return 1;\n}\n\n#endif // defined(__GLIBCXX__)\n"
  },
  {
    "path": "folly/concurrency/test/AtomicSharedPtrTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/AtomicSharedPtr.h>\n\n#include <atomic>\n#include <memory>\n#include <thread>\n\n#include <folly/Portability.h>\n#include <folly/concurrency/test/AtomicSharedPtrCounted.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n#include <folly/test/DeterministicSchedule.h>\n\nusing namespace folly;\nusing namespace folly::test;\nusing namespace std;\nstatic int c_count{0};\nstatic int d_count{0};\n\nusing DSched = DeterministicSchedule;\n\nDEFINE_int64(seed, 0, \"Seed for random number generators\");\nDEFINE_int32(num_threads, 32, \"Number of threads\");\n\nstruct foo {\n  foo() { c_count++; }\n  ~foo() { d_count++; }\n};\n\nTEST(AtomicSharedPtr, operators) {\n  atomic_shared_ptr<int> fooptr;\n#if FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED\n  EXPECT_TRUE(fooptr.is_lock_free());\n#endif\n  auto i = new int(5);\n  std::shared_ptr<int> s(i);\n  fooptr.store(s);\n  shared_ptr<int> bar(fooptr);\n  EXPECT_TRUE(fooptr.compare_exchange_strong(s, nullptr));\n  s.reset();\n  bar.reset();\n}\n\nTEST(AtomicSharedPtr, exchange) {\n  atomic_shared_ptr<int> fooptr;\n  auto a = make_shared<int>(1);\n  fooptr.store(std::move(a));\n  auto b = fooptr.exchange(make_shared<int>());\n  EXPECT_EQ(*b, 1);\n}\n\nTEST(AtomicSharedPtr, foo) {\n  c_count = 0;\n  d_count = 0;\n  {\n    atomic_shared_ptr<foo> fooptr;\n    fooptr.store(make_shared<foo>());\n    EXPECT_EQ(1, c_count);\n    EXPECT_EQ(0, d_count);\n    {\n      auto res = fooptr.load();\n      EXPECT_EQ(1, c_count);\n      EXPECT_EQ(0, d_count);\n    }\n    EXPECT_EQ(1, c_count);\n    EXPECT_EQ(0, d_count);\n  }\n  EXPECT_EQ(1, c_count);\n  EXPECT_EQ(1, d_count);\n}\n\n#if FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED\n\nTEST(AtomicSharedPtr, counted) {\n  c_count = 0;\n  d_count = 0;\n  {\n    atomic_shared_ptr<foo, std::atomic, counted_ptr_internals<std::atomic>>\n        fooptr;\n    fooptr.store(make_counted<std::atomic, foo>());\n    EXPECT_EQ(1, c_count);\n    EXPECT_EQ(0, d_count);\n    {\n      auto res = fooptr.load();\n      EXPECT_EQ(1, c_count);\n      EXPECT_EQ(0, d_count);\n    }\n    EXPECT_EQ(1, c_count);\n    EXPECT_EQ(0, d_count);\n  }\n  EXPECT_EQ(1, c_count);\n  EXPECT_EQ(1, d_count);\n}\n\nTEST(AtomicSharedPtr, counted2) {\n  auto foo = make_counted<std::atomic, bool>();\n  atomic_shared_ptr<bool, std::atomic, counted_ptr_internals<std::atomic>>\n      fooptr(foo);\n  fooptr.store(foo);\n  fooptr.load();\n}\n\n#endif\n\nTEST(AtomicSharedPtr, ConstTest) {\n  const auto a(std::make_shared<foo>());\n  atomic_shared_ptr<foo> atom;\n  atom.store(a);\n\n  atomic_shared_ptr<const foo> catom;\n}\n\nTEST(AtomicSharedPtr, AliasingConstructorTest) {\n  c_count = 0;\n  d_count = 0;\n  auto a = std::make_shared<foo>();\n  auto b = new foo;\n  auto alias = std::shared_ptr<foo>(a, b);\n\n  atomic_shared_ptr<foo> asp;\n  asp.store(alias);\n  a.reset();\n  alias.reset();\n  auto res1 = asp.load();\n  auto res2 = asp.exchange(nullptr);\n  EXPECT_EQ(b, res1.get());\n  EXPECT_EQ(b, res2.get());\n  EXPECT_EQ(2, c_count);\n  EXPECT_EQ(0, d_count);\n  res1.reset();\n  res2.reset();\n  EXPECT_EQ(2, c_count);\n  EXPECT_EQ(1, d_count);\n  delete b;\n  EXPECT_EQ(2, c_count);\n  EXPECT_EQ(2, d_count);\n}\n\nTEST(AtomicSharedPtr, AliasingWithNoControlBlockConstructorTest) {\n  long value = 0;\n  atomic_shared_ptr<long> ptr{shared_ptr<long>{shared_ptr<long>{}, &value}};\n  EXPECT_EQ(ptr.load().get(), &value);\n}\n\nTEST(AtomicSharedPtr, AliasingWithNullptrConstructorTest) {\n  atomic_shared_ptr<foo> ptr{shared_ptr<foo>{std::make_shared<foo>(), nullptr}};\n  EXPECT_EQ(ptr.load().get(), nullptr);\n  // Verify that atomic_shared_ptr is holding the underlying object.\n  EXPECT_EQ(d_count, 0);\n  ptr.store({});\n  EXPECT_EQ(d_count, 1);\n}\n\n#if FOLLY_HAS_ATOMIC_SHARED_PTR_HOOKED\n\nTEST(AtomicSharedPtr, MaxPtrs) {\n  shared_ptr<long> p(new long);\n  constexpr size_t max_atomic_shared_ptrs = 262144;\n  atomic_shared_ptr<long> ptrs[max_atomic_shared_ptrs];\n  for (size_t i = 0; i < max_atomic_shared_ptrs - 1; i++) {\n    ptrs[i].store(p);\n  }\n  atomic_shared_ptr<long> fail;\n  EXPECT_DEATH(fail.store(p), \"\");\n}\n\nTEST(AtomicSharedPtr, DeterministicTest) {\n  DSched sched(DSched::uniform(FLAGS_seed));\n\n  auto foo = make_counted<DeterministicAtomic, bool>();\n  atomic_shared_ptr<\n      bool,\n      DeterministicAtomic,\n      counted_ptr_internals<DeterministicAtomic>>\n      fooptr(foo);\n  std::vector<std::thread> threads(FLAGS_num_threads);\n  for (int tid = 0; tid < FLAGS_num_threads; ++tid) {\n    threads[tid] = DSched::thread([&]() {\n      for (int i = 0; i < 1000; i++) {\n        auto l = fooptr.load();\n        EXPECT_TRUE(l.get() != nullptr);\n        fooptr.compare_exchange_strong(l, l);\n        fooptr.store(make_counted<DeterministicAtomic, bool>());\n        EXPECT_FALSE(fooptr.compare_exchange_strong(\n            l, make_counted<DeterministicAtomic, bool>()));\n      }\n    });\n  }\n  for (auto& t : threads) {\n    DSched::join(t);\n  }\n}\n\n#endif\n\nTEST(AtomicSharedPtr, StressTest) {\n  constexpr size_t kExternalOffset = 0x2000;\n\n  atomic_shared_ptr<bool> ptr;\n  std::atomic<size_t> num_loads = 0;\n  std::atomic<size_t> num_stores = 0;\n\n  // DeterministicSchedule is too slow for the number of iterations required\n  // here, and we need a test that exercises the native atomics.\n  std::vector<std::thread> threads;\n  for (int tid = 0; tid < FLAGS_num_threads; ++tid) {\n    threads.emplace_back([&] {\n      shared_ptr<bool> v;\n      for (size_t i = 0; i < 16 * kExternalOffset; ++i) {\n        // Each time we've gone through a few local -> global batches, replace\n        // the pointer to contend with other load()s. This also does the first\n        // initialization, and a few threads may see nullptr for a while.\n        if (num_loads++ % (kExternalOffset * 2) == 0) {\n          auto newv = std::make_shared<bool>();\n          // Alternate between store and CAS.\n          if (num_stores++ % 2 == 0) {\n            ptr.store(std::move(newv), std::memory_order_release);\n          } else {\n            v = ptr.load(std::memory_order_relaxed);\n            ptr.compare_exchange_strong(\n                v,\n                std::move(newv),\n                std::memory_order_acq_rel,\n                std::memory_order_relaxed);\n          }\n        }\n        // Increments the local count and decrements the external one,\n        // eventually forcing a batch transfer.\n        v = ptr.load(std::memory_order_acquire);\n      }\n    });\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\nTEST(AtomicSharedPtr, Leak) {\n  static auto& ptr = *new atomic_shared_ptr<int>();\n  ptr.store(std::make_shared<int>(3), std::memory_order_relaxed);\n  EXPECT_EQ(3, *ptr.load(std::memory_order_relaxed));\n}\n"
  },
  {
    "path": "folly/concurrency/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"cache_locality_benchmark\",\n    srcs = [\"CacheLocalityBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/lang:keep\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"cache_locality_init_main\",\n    srcs = [\"CacheLocalityInitMain.cpp\"],\n    deps = [\n        \"//folly/concurrency:cache_locality\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"cache_locality_test\",\n    srcs = [\"CacheLocalityTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sys_resource\",\n        \"//folly/portability:unistd\",\n        \"//folly/test:test_utils\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"atomic_shared_ptr_test\",\n    srcs = [\"AtomicSharedPtrTest.cpp\"],\n    headers = [\"AtomicSharedPtrCounted.h\"],\n    labels = [\"case-isolation-failure\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:portability\",\n        \"//folly/concurrency:atomic_shared_ptr\",\n        \"//folly/portability:config\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:deterministic_schedule\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"atomic_shared_ptr_performance\",\n    srcs = [\"AtomicSharedPtrPerformance.cpp\"],\n    deps = [\n        \"//folly/concurrency:atomic_shared_ptr\",\n        \"//folly/portability:config\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"core_cached_shared_ptr_test\",\n    srcs = [\"CoreCachedSharedPtrTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:portability\",\n        \"//folly/concurrency:atomic_shared_ptr\",\n        \"//folly/concurrency:core_cached_shared_ptr\",\n        \"//folly/concurrency:thread_cached_synchronized\",\n        \"//folly/concurrency/memory:read_mostly_shared_ptr\",\n        \"//folly/portability:config\",\n        \"//folly/portability:gtest\",\n        \"//folly/system:hardware_concurrency\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"concurrent_hash_map_bench\",\n    srcs = [\"ConcurrentHashMapBench.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark_util\",\n        \"//folly/concurrency:concurrent_hash_map\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization/test:barrier\",\n        \"//folly/system:hardware_concurrency\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"concurrent_hash_map_stress_test\",\n    srcs = [\"ConcurrentHashMapStressTest.cpp\"],\n    labels = [\n        # each test runs 2^16 threads, which is very slow and very heavyweight,\n        # and which interacts very poorly with any parallelism between tests\n        \"glacial\",\n        \"heavyweight\",\n        \"serialize\",\n        \"slow\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:traits\",\n        \"//folly/concurrency:concurrent_hash_map\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/synchronization:latch\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"concurrent_hash_map_test\",\n    srcs = [\"ConcurrentHashMapTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:traits\",\n        \"//folly/concurrency:concurrent_hash_map\",\n        \"//folly/container/test:tracking_types\",\n        \"//folly/hash:hash\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:latch\",\n        \"//folly/test:deterministic_schedule\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"concurrent_hash_map_stress_test_no_simd\",\n    srcs = [\"ConcurrentHashMapStressTest.cpp\"],\n    labels = [\n        # each test runs 2^16 threads, which is very slow and very heavyweight,\n        # and which interacts very poorly with any parallelism between tests\n        \"glacial\",\n        \"heavyweight\",\n        \"serialize\",\n        \"slow\",\n    ],\n    preprocessor_flags = [\"-DFOLLY_F14_FORCE_FALLBACK=1\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:traits\",\n        \"//folly/concurrency:concurrent_hash_map\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/synchronization:latch\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"concurrent_hash_map_test_no_simd\",\n    srcs = [\"ConcurrentHashMapTest.cpp\"],\n    preprocessor_flags = [\"-DFOLLY_F14_FORCE_FALLBACK=1\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:traits\",\n        \"//folly/concurrency:concurrent_hash_map\",\n        \"//folly/container/test:tracking_types\",\n        \"//folly/hash:hash\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:latch\",\n        \"//folly/test:deterministic_schedule\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"dynamic_bounded_queue_test\",\n    srcs = [\"DynamicBoundedQueueTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:mpmc_queue\",\n        \"//folly:producer_consumer_queue\",\n        \"//folly/concurrency:dynamic_bounded_queue\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"priority_unbounded_queue_set_test\",\n    srcs = [\"PriorityUnboundedQueueSetTest.cpp\"],\n    deps = [\n        \"//folly/concurrency:priority_unbounded_queue_set\",\n        \"//folly/container:enumerate\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"unbounded_queue_test\",\n    srcs = [\"UnboundedQueueTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:mpmc_queue\",\n        \"//folly:producer_consumer_queue\",\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"thread_cached_synchronized_bench\",\n    srcs = [\"ThreadCachedSynchronizedBench.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:synchronized\",\n        \"//folly/concurrency:thread_cached_synchronized\",\n        \"//folly/lang:keep\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"thread_cached_synchronized_test\",\n    srcs = [\"ThreadCachedSynchronizedTest.cpp\"],\n    deps = [\n        \"//folly/concurrency:thread_cached_synchronized\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"process_local_unique_id_test\",\n    srcs = [\"ProcessLocalUniqueIdTest.cpp\"],\n    deps = [\n        \"//folly:synchronized\",\n        \"//folly/concurrency:process_local_unique_id\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"singleton_relaxed_counter_bench\",\n    srcs = [\n        \"SingletonRelaxedCounterBench.cpp\",\n    ],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/concurrency:singleton_relaxed_counter\",\n        \"//folly/init:init\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"singleton_relaxed_counter_test\",\n    srcs = [\n        \"SingletonRelaxedCounterTest.cpp\",\n    ],\n    deps = [\n        \"//folly:thread_local\",\n        \"//folly/concurrency:singleton_relaxed_counter\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n"
  },
  {
    "path": "folly/concurrency/test/CacheLocalityBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/CacheLocality.h>\n\n#include <memory>\n#include <thread>\n#include <unordered_map>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/lang/Keep.h>\n\nusing namespace folly;\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_access_spreader_atomic_current(size_t numStripes) {\n  return AccessSpreader<>::current(numStripes);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_access_spreader_atomic_cached_current(size_t numStripes) {\n  return AccessSpreader<>::cachedCurrent(numStripes);\n}\n\n#define DECLARE_SPREADER_TAG(tag, locality, func)      \\\n  namespace {                                          \\\n  template <typename dummy>                            \\\n  struct tag {};                                       \\\n  }                                                    \\\n  namespace folly {                                    \\\n  template <>                                          \\\n  const CacheLocality& CacheLocality::system<tag>() {  \\\n    static auto* inst = new CacheLocality(locality);   \\\n    return *inst;                                      \\\n  }                                                    \\\n  template <>                                          \\\n  Getcpu::Func AccessSpreader<tag>::pickGetcpuFunc() { \\\n    return func;                                       \\\n  }                                                    \\\n  template struct AccessSpreader<tag>;                 \\\n  }\n\nDECLARE_SPREADER_TAG(\n    ThreadLocalTag,\n    CacheLocality::system<>(),\n    folly::FallbackGetcpu<SequentialThreadId>::getcpu)\nDECLARE_SPREADER_TAG(\n    PthreadSelfTag,\n    CacheLocality::system<>(),\n    folly::FallbackGetcpu<HashingThreadId>::getcpu)\n\n// Allow us to run cachedCurrent() in the benchmark function easily.\nnamespace {\ntemplate <typename dummy>\nstruct CachedCurrentTag {};\n} // namespace\nnamespace folly {\ntemplate <>\nconst CacheLocality& CacheLocality::system<CachedCurrentTag>() {\n  return CacheLocality::system<>();\n}\ntemplate <>\nsize_t AccessSpreader<CachedCurrentTag>::current(\n    size_t numStripes, const GlobalState& state) {\n  auto& alter = reinterpret_cast<const AccessSpreader::GlobalState&>(state);\n  return AccessSpreader::cachedCurrent(numStripes, alter);\n}\n} // namespace folly\n\nBENCHMARK(AccessSpreaderUse, iters) {\n  for (unsigned long i = 0; i < iters; ++i) {\n    auto x = AccessSpreader<>::current(16);\n    folly::doNotOptimizeAway(x);\n  }\n}\n\nBENCHMARK(StateAccessSpreaderUse, iters) {\n  auto& state = AccessSpreader<>::state();\n  for (unsigned long i = 0; i < iters; ++i) {\n    auto x = AccessSpreader<>::current(16, state);\n    folly::doNotOptimizeAway(x);\n  }\n}\n\nBENCHMARK(CachedAccessSpreaderUse, iters) {\n  for (unsigned long i = 0; i < iters; ++i) {\n    auto x = AccessSpreader<>::cachedCurrent(16);\n    folly::doNotOptimizeAway(x);\n  }\n}\n\nBENCHMARK(LLCAccessSpreaderUse, iters) {\n  for (unsigned long i = 0; i < iters; ++i) {\n    auto x = LLCAccessSpreader::get().current();\n    folly::doNotOptimizeAway(x);\n  }\n}\n\nBENCHMARK(StateCachedAccessSpreaderUse, iters) {\n  auto& state = AccessSpreader<>::state();\n  for (unsigned long i = 0; i < iters; ++i) {\n    auto x = AccessSpreader<>::cachedCurrent(16, state);\n    folly::doNotOptimizeAway(x);\n  }\n}\n\nBENCHMARK(BaselineAtomicIncrement, iters) {\n  std::atomic<int> value;\n  for (unsigned long i = 0; i < iters; ++i) {\n    ++value;\n    folly::doNotOptimizeAway(value);\n  }\n}\n\nBENCHMARK(CachedAccessSpreaderAtomicIncrement, iters) {\n  std::array<std::atomic<int>, 64> values;\n  for (unsigned long i = 0; i < iters; ++i) {\n    auto x = AccessSpreader<>::cachedCurrent(64);\n    ++values[x];\n    folly::doNotOptimizeAway(values[x]);\n  }\n}\n\nBENCHMARK(StateCachedAccessSpreaderAtomicIncrement, iters) {\n  auto& state = AccessSpreader<>::state();\n  std::array<std::atomic<int>, 64> values;\n  for (unsigned long i = 0; i < iters; ++i) {\n    auto x = AccessSpreader<>::cachedCurrent(64, state);\n    ++values[x];\n    folly::doNotOptimizeAway(values[x]);\n  }\n}\n\n// Benchmark scores here reflect the time for 32 threads to perform an\n// atomic increment on a dual-socket E5-2660 @ 2.2Ghz.  Surprisingly,\n// if we don't separate the counters onto unique 128 byte stripes the\n// 1_stripe and 2_stripe results are identical, even though the L3 is\n// claimed to have 64 byte cache lines.\n//\n// Getcpu refers to the vdso getcpu implementation.  ThreadLocal refers\n// to execution using SequentialThreadId, the fallback if the vdso\n// getcpu isn't available.  PthreadSelf hashes the value returned from\n// getCurrentThreadID() as a fallback-fallback for systems that don't have\n// thread-local support.\n//\n// At 16_stripe_0_work and 32_stripe_0_work there is only L1 traffic,\n// so since the stripe selection is 12 nanos the atomic increments in\n// the L1 is ~17 nanos.  At width 8_stripe_0_work the line is expected\n// to ping-pong almost every operation, since the loops have the same\n// duration.  Widths 4 and 2 have the same behavior, but each tour of the\n// cache line is 4 and 8 cores long, respectively.  These all suggest a\n// lower bound of 60 nanos for intra-chip handoff and increment between\n// the L1s.\n//\n// With 420 nanos of busywork per contended increment, the system can\n// hide all of the latency of a tour of length 4, but not quite one of\n// length 8.  I was a bit surprised at how much worse the non-striped\n// version got.  It seems that the inter-chip traffic also interferes\n// with the L1-only localWork.load().  When the local work is doubled\n// to about 1 microsecond we see that the inter-chip contention is still\n// very important, but subdivisions on the same chip don't matter.\n//\n// sudo nice -n -20 buck-out/gen/folly/test/cache_locality_test\n//     --benchmark --bm_min_iters=1000000\n// ============================================================================\n// folly/concurrency/test/CacheLocalityBenchmark.cpprelative  time/iter  iters/s\n// ============================================================================\n// AccessSpreaderUse                                           11.51ns   86.87M\n// CachedAccessSpreaderUse                                      1.98ns  490.03M\n// BaselineAtomicIncrement                                     10.37ns   96.43M\n// CachedAccessSpreaderAtomicIncrement                         11.43ns   87.50M\n// ----------------------------------------------------------------------------\n// contentionAtWidthGetcpu(1_stripe_0_work)                   993.13ns    1.01M\n// contentionAtWidthGetcpu(2_stripe_0_work)                   551.45ns    1.81M\n// contentionAtWidthGetcpu(4_stripe_0_work)                   302.36ns    3.31M\n// contentionAtWidthGetcpu(8_stripe_0_work)                   156.57ns    6.39M\n// contentionAtWidthGetcpu(16_stripe_0_work)                   81.34ns   12.29M\n// contentionAtWidthGetcpu(32_stripe_0_work)                   37.90ns   26.39M\n// contentionAtWidthGetcpu(64_stripe_0_work)                   36.02ns   27.76M\n// contentionAtWidthCached(2_stripe_0_work)                   310.64ns    3.22M\n// contentionAtWidthCached(4_stripe_0_work)                   180.41ns    5.54M\n// contentionAtWidthCached(8_stripe_0_work)                    87.84ns   11.38M\n// contentionAtWidthCached(16_stripe_0_work)                   45.04ns   22.20M\n// contentionAtWidthCached(32_stripe_0_work)                   19.92ns   50.20M\n// contentionAtWidthCached(64_stripe_0_work)                   19.21ns   52.06M\n// contentionAtWidthThreadLocal(2_stripe_0_work)              321.14ns    3.11M\n// contentionAtWidthThreadLocal(4_stripe_0_work)              244.41ns    4.09M\n// contentionAtWidthThreadLocal(8_stripe_0_work)              103.47ns    9.66M\n// contentionAtWidthThreadLocal(16_stripe_0_work)              79.82ns   12.53M\n// contentionAtWidthThreadLocal(32_stripe_0_work)              20.41ns   49.01M\n// contentionAtWidthThreadLocal(64_stripe_0_work)              22.13ns   45.18M\n// contentionAtWidthPthreadSelf(2_stripe_0_work)              373.46ns    2.68M\n// contentionAtWidthPthreadSelf(4_stripe_0_work)              208.18ns    4.80M\n// contentionAtWidthPthreadSelf(8_stripe_0_work)              105.99ns    9.43M\n// contentionAtWidthPthreadSelf(16_stripe_0_work)             105.67ns    9.46M\n// contentionAtWidthPthreadSelf(32_stripe_0_work)              76.01ns   13.16M\n// contentionAtWidthPthreadSelf(64_stripe_0_work)              76.04ns   13.15M\n// atomicIncrBaseline(local_incr_0_work)                       13.43ns   74.47M\n// ----------------------------------------------------------------------------\n// contentionAtWidthGetcpu(1_stripe_500_work)                   1.76us  567.20K\n// contentionAtWidthGetcpu(2_stripe_500_work)                   1.16us  863.67K\n// contentionAtWidthGetcpu(4_stripe_500_work)                 604.74ns    1.65M\n// contentionAtWidthGetcpu(8_stripe_500_work)                 524.16ns    1.91M\n// contentionAtWidthGetcpu(16_stripe_500_work)                478.92ns    2.09M\n// contentionAtWidthGetcpu(32_stripe_500_work)                480.64ns    2.08M\n// atomicIncrBaseline(local_incr_500_work)                    395.17ns    2.53M\n// ----------------------------------------------------------------------------\n// contentionAtWidthGetcpu(1_stripe_1000_work)                  2.16us  462.06K\n// contentionAtWidthGetcpu(2_stripe_1000_work)                  1.31us  764.80K\n// contentionAtWidthGetcpu(4_stripe_1000_work)                895.33ns    1.12M\n// contentionAtWidthGetcpu(8_stripe_1000_work)                833.98ns    1.20M\n// contentionAtWidthGetcpu(16_stripe_1000_work)               765.10ns    1.31M\n// contentionAtWidthGetcpu(32_stripe_1000_work)               646.85ns    1.55M\n// atomicIncrBaseline(local_incr_1000_work)                   656.15ns    1.52M\n// ============================================================================\ntemplate <template <typename> class Tag>\nstatic void contentionAtWidth(size_t iters, size_t stripes, size_t work) {\n  const size_t counterAlignment = 128;\n  const size_t numThreads = 32;\n\n  folly::BenchmarkSuspender braces;\n\n  std::atomic<size_t> ready(0);\n  std::atomic<bool> go(false);\n\n  // while in theory the cache line size is 64 bytes, experiments show\n  // that we get contention on 128 byte boundaries for Ivy Bridge.  The\n  // extra indirection adds 1 or 2 nanos\n  assert(counterAlignment >= sizeof(std::atomic<size_t>));\n  std::vector<char> raw(counterAlignment * stripes);\n\n  // if we happen to be using the tlsRoundRobin, then sequentially\n  // assigning the thread identifiers is the unlikely best-case scenario.\n  // We don't want to unfairly benefit or penalize.  Computing the exact\n  // maximum likelihood of the probability distributions is annoying, so\n  // I approximate as 2/5 of the ids that have no threads, 2/5 that have\n  // 1, 2/15 that have 2, and 1/15 that have 3.  We accomplish this by\n  // wrapping back to slot 0 when we hit 1/15 and 1/5.\n\n  std::vector<std::thread> threads;\n  while (threads.size() < numThreads) {\n    threads.push_back(std::thread([&, iters, stripes, work]() {\n      auto counters = std::vector<std::atomic<size_t>*>(stripes);\n      for (size_t i = 0; i < stripes; ++i) {\n        counters[i] =\n            new (raw.data() + counterAlignment * i) std::atomic<size_t>();\n      }\n\n      ready++;\n      while (!go.load()) {\n        std::this_thread::yield();\n      }\n      std::atomic<int> localWork(0);\n      for (size_t i = iters; i > 0; --i) {\n        ++*(counters[AccessSpreader<Tag>::current(stripes)]);\n        for (size_t j = work; j > 0; --j) {\n          auto x = localWork.load();\n          folly::doNotOptimizeAway(x);\n        }\n      }\n    }));\n\n    if (threads.size() == numThreads / 15 || threads.size() == numThreads / 5) {\n      // create a few dummy threads to wrap back around to 0 mod numCpus\n      for (size_t i = threads.size(); i != numThreads; ++i) {\n        std::thread t([&]() {\n          auto x = AccessSpreader<Tag>::current(stripes);\n          folly::doNotOptimizeAway(x);\n        });\n        t.join();\n      }\n    }\n  }\n\n  while (ready < numThreads) {\n    std::this_thread::yield();\n  }\n  braces.dismiss();\n  go = true;\n\n  for (auto& thr : threads) {\n    thr.join();\n  }\n}\n\nstatic void atomicIncrBaseline(\n    size_t iters, size_t work, size_t numThreads = 32) {\n  folly::BenchmarkSuspender braces;\n\n  std::atomic<bool> go(false);\n\n  std::vector<std::thread> threads;\n  while (threads.size() < numThreads) {\n    threads.emplace_back([&]() {\n      while (!go.load()) {\n        std::this_thread::yield();\n      }\n      std::atomic<size_t> localCounter(0);\n      std::atomic<int> localWork(0);\n      for (size_t i = iters; i > 0; --i) {\n        localCounter++;\n        for (size_t j = work; j > 0; --j) {\n          auto x = localWork.load();\n          folly::doNotOptimizeAway(x);\n        }\n      }\n    });\n  }\n\n  braces.dismiss();\n  go = true;\n\n  for (auto& thr : threads) {\n    thr.join();\n  }\n}\n\nstatic void contentionAtWidthGetcpu(size_t iters, size_t stripes, size_t work) {\n  contentionAtWidth<std::atomic>(iters, stripes, work);\n}\n\nstatic void contentionAtWidthThreadLocal(\n    size_t iters, size_t stripes, size_t work) {\n  contentionAtWidth<ThreadLocalTag>(iters, stripes, work);\n}\n\nstatic void contentionAtWidthPthreadSelf(\n    size_t iters, size_t stripes, size_t work) {\n  contentionAtWidth<PthreadSelfTag>(iters, stripes, work);\n}\n\nstatic void contentionAtWidthCached(size_t iters, size_t stripes, size_t work) {\n  contentionAtWidth<CachedCurrentTag>(iters, stripes, work);\n}\n\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 1_stripe_0_work, 1, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 2_stripe_0_work, 2, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 4_stripe_0_work, 4, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 8_stripe_0_work, 8, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 16_stripe_0_work, 16, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 32_stripe_0_work, 32, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 64_stripe_0_work, 64, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthCached, 2_stripe_0_work, 2, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthCached, 4_stripe_0_work, 4, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthCached, 8_stripe_0_work, 8, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthCached, 16_stripe_0_work, 16, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthCached, 32_stripe_0_work, 32, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthCached, 64_stripe_0_work, 64, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthThreadLocal, 2_stripe_0_work, 2, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthThreadLocal, 4_stripe_0_work, 4, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthThreadLocal, 8_stripe_0_work, 8, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthThreadLocal, 16_stripe_0_work, 16, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthThreadLocal, 32_stripe_0_work, 32, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthThreadLocal, 64_stripe_0_work, 64, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthPthreadSelf, 2_stripe_0_work, 2, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthPthreadSelf, 4_stripe_0_work, 4, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthPthreadSelf, 8_stripe_0_work, 8, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthPthreadSelf, 16_stripe_0_work, 16, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthPthreadSelf, 32_stripe_0_work, 32, 0)\nBENCHMARK_NAMED_PARAM(contentionAtWidthPthreadSelf, 64_stripe_0_work, 64, 0)\nBENCHMARK_NAMED_PARAM(atomicIncrBaseline, local_incr_0_work, 0)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 1_stripe_500_work, 1, 500)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 2_stripe_500_work, 2, 500)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 4_stripe_500_work, 4, 500)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 8_stripe_500_work, 8, 500)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 16_stripe_500_work, 16, 500)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 32_stripe_500_work, 32, 500)\nBENCHMARK_NAMED_PARAM(atomicIncrBaseline, local_incr_500_work, 500)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 1_stripe_1000_work, 1, 1000)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 2_stripe_1000_work, 2, 1000)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 4_stripe_1000_work, 4, 1000)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 8_stripe_1000_work, 8, 1000)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 16_stripe_1000_work, 16, 1000)\nBENCHMARK_NAMED_PARAM(contentionAtWidthGetcpu, 32_stripe_1000_work, 32, 1000)\nBENCHMARK_NAMED_PARAM(atomicIncrBaseline, local_incr_1000_work, 1000)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(readSystemLocalityInfo, iters) {\n  while (iters--) {\n    auto cl = folly::CacheLocality::readSystemLocalityInfo();\n    folly::compiler_must_not_elide(cl);\n  }\n}\n\nBENCHMARK(readFromProcCpuinfo, iters) {\n  while (iters--) {\n    try {\n      auto cl = folly::CacheLocality::readFromProcCpuinfo();\n      folly::compiler_must_not_elide(cl);\n    } catch (...) {\n    }\n  }\n}\n\nBENCHMARK(readFromSysfs, iters) {\n  while (iters--) {\n    try {\n      auto cl = folly::CacheLocality::readFromSysfs();\n      folly::compiler_must_not_elide(cl);\n    } catch (...) {\n    }\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/concurrency/test/CacheLocalityInitMain.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/CacheLocality.h>\n\nint main() {\n  (void)folly::CacheLocality::system();\n}\n"
  },
  {
    "path": "folly/concurrency/test/CacheLocalityTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/CacheLocality.h>\n\n#include <fstream>\n#include <memory>\n#include <thread>\n#include <unordered_map>\n\n#include <fmt/format.h>\n#include <fmt/ranges.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/SysResource.h>\n#include <folly/portability/Unistd.h>\n#include <folly/test/TestUtils.h>\n#include <folly/testing/TestUtil.h>\n\n#include <glog/logging.h>\n\nusing namespace folly;\n\n#if !defined(_WIN32)\n\n/// This is the relevant nodes from a production box's sysfs tree.  If you\n/// think this map is ugly you should see the version of this test that\n/// used a real directory tree.  To reduce the chance of testing error\n/// I haven't tried to remove the common prefix\nstatic std::unordered_map<std::string, std::string> fakeSysfsTree = {\n    {\"/sys/devices/system/cpu/cpu0/cache/index0/shared_cpu_list\", \"0,17\"},\n    {\"/sys/devices/system/cpu/cpu0/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu0/cache/index1/shared_cpu_list\", \"0,17\"},\n    {\"/sys/devices/system/cpu/cpu0/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu0/cache/index2/shared_cpu_list\", \"0,17\"},\n    {\"/sys/devices/system/cpu/cpu0/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu0/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu0/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index0/shared_cpu_list\", \"1,18\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index1/shared_cpu_list\", \"1,18\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index2/shared_cpu_list\", \"1,18\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu1/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index0/shared_cpu_list\", \"2,19\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index1/shared_cpu_list\", \"2,19\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index2/shared_cpu_list\", \"2,19\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu2/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index0/shared_cpu_list\", \"3,20\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index1/shared_cpu_list\", \"3,20\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index2/shared_cpu_list\", \"3,20\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu3/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index0/shared_cpu_list\", \"4,21\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index1/shared_cpu_list\", \"4,21\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index2/shared_cpu_list\", \"4,21\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu4/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index0/shared_cpu_list\", \"5-6\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index1/shared_cpu_list\", \"5-6\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index2/shared_cpu_list\", \"5-6\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu5/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index0/shared_cpu_list\", \"5-6\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index1/shared_cpu_list\", \"5-6\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index2/shared_cpu_list\", \"5-6\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu6/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index0/shared_cpu_list\", \"7,22\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index1/shared_cpu_list\", \"7,22\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index2/shared_cpu_list\", \"7,22\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu7/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index0/shared_cpu_list\", \"8,23\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index1/shared_cpu_list\", \"8,23\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index2/shared_cpu_list\", \"8,23\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu8/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index0/shared_cpu_list\", \"9,24\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index1/shared_cpu_list\", \"9,24\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index2/shared_cpu_list\", \"9,24\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index3/shared_cpu_list\", \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu9/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index0/shared_cpu_list\", \"10,25\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index1/shared_cpu_list\", \"10,25\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index2/shared_cpu_list\", \"10,25\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu10/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index0/shared_cpu_list\", \"11,26\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index1/shared_cpu_list\", \"11,26\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index2/shared_cpu_list\", \"11,26\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu11/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index0/shared_cpu_list\", \"12,27\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index1/shared_cpu_list\", \"12,27\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index2/shared_cpu_list\", \"12,27\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu12/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index0/shared_cpu_list\", \"13,28\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index1/shared_cpu_list\", \"13,28\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index2/shared_cpu_list\", \"13,28\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu13/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index0/shared_cpu_list\", \"14,29\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index1/shared_cpu_list\", \"14,29\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index2/shared_cpu_list\", \"14,29\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu14/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index0/shared_cpu_list\", \"15,30\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index1/shared_cpu_list\", \"15,30\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index2/shared_cpu_list\", \"15,30\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu15/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index0/shared_cpu_list\", \"16,31\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index1/shared_cpu_list\", \"16,31\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index2/shared_cpu_list\", \"16,31\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu16/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index0/shared_cpu_list\", \"0,17\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index1/shared_cpu_list\", \"0,17\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index2/shared_cpu_list\", \"0,17\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu17/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index0/shared_cpu_list\", \"1,18\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index1/shared_cpu_list\", \"1,18\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index2/shared_cpu_list\", \"1,18\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu18/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index0/shared_cpu_list\", \"2,19\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index1/shared_cpu_list\", \"2,19\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index2/shared_cpu_list\", \"2,19\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu19/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index0/shared_cpu_list\", \"3,20\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index1/shared_cpu_list\", \"3,20\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index2/shared_cpu_list\", \"3,20\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu20/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index0/shared_cpu_list\", \"4,21\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index1/shared_cpu_list\", \"4,21\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index2/shared_cpu_list\", \"4,21\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu21/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index0/shared_cpu_list\", \"7,22\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index1/shared_cpu_list\", \"7,22\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index2/shared_cpu_list\", \"7,22\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu22/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index0/shared_cpu_list\", \"8,23\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index1/shared_cpu_list\", \"8,23\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index2/shared_cpu_list\", \"8,23\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index3/shared_cpu_list\", \"0-8,17-23\"},\n    {\"/sys/devices/system/cpu/cpu23/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index0/shared_cpu_list\", \"9,24\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index1/shared_cpu_list\", \"9,24\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index2/shared_cpu_list\", \"9,24\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu24/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index0/shared_cpu_list\", \"10,25\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index1/shared_cpu_list\", \"10,25\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index2/shared_cpu_list\", \"10,25\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu25/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index0/shared_cpu_list\", \"11,26\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index1/shared_cpu_list\", \"11,26\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index2/shared_cpu_list\", \"11,26\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu26/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index0/shared_cpu_list\", \"12,27\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index1/shared_cpu_list\", \"12,27\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index2/shared_cpu_list\", \"12,27\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu27/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index0/shared_cpu_list\", \"13,28\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index1/shared_cpu_list\", \"13,28\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index2/shared_cpu_list\", \"13,28\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu28/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index0/shared_cpu_list\", \"14,29\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index1/shared_cpu_list\", \"14,29\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index2/shared_cpu_list\", \"14,29\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu29/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index0/shared_cpu_list\", \"15,30\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index1/shared_cpu_list\", \"15,30\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index2/shared_cpu_list\", \"15,30\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu30/cache/index3/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index0/shared_cpu_list\", \"16,31\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index0/type\", \"Data\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index1/shared_cpu_list\", \"16,31\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index1/type\", \"Instruction\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index2/shared_cpu_list\", \"16,31\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index2/type\", \"Unified\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index3/shared_cpu_list\",\n     \"9-16,24-31\"},\n    {\"/sys/devices/system/cpu/cpu31/cache/index3/type\", \"Unified\"}};\n\nTEST(CacheLocality, FakeSysfs) {\n  folly::test::TemporaryDirectory tmpdir;\n  for (const auto& [k, v] : fakeSysfsTree) {\n    auto path = tmpdir.path() / k;\n    fs::create_directories(path.parent_path());\n    std::ofstream(path.string()) << v << std::endl;\n  }\n  auto root = tmpdir.path();\n  auto parsed = CacheLocality::readFromSysfsTree(tmpdir.path().string());\n\n  size_t expectedNumCpus = 32;\n  std::vector<size_t> expectedNumCachesByLevel = {16, 16, 2};\n  std::vector<size_t> expectedLocalityIndexByCpu = {\n      0,  2, 4, 6, 8, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28,\n      30, 1, 3, 5, 7, 9,  13, 15, 17, 19, 21, 23, 25, 27, 29, 31};\n\n  EXPECT_EQ(expectedNumCpus, parsed.numCpus);\n  EXPECT_EQ(expectedNumCachesByLevel, parsed.numCachesByLevel);\n  EXPECT_EQ(expectedLocalityIndexByCpu, parsed.localityIndexByCpu);\n}\n\n#endif\n\nstatic const std::vector<std::string> fakeProcCpuinfo = {\n    \"processor\t: 0\",\n    \"vendor_id\t: GenuineIntel\",\n    \"cpu family\t: 6\",\n    \"model\t\t: 79\",\n    \"model name\t: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz\",\n    \"stepping\t: 1\",\n    \"microcode\t: 0xb00001b\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"siblings\t: 28\",\n    \"core id\t\t: 0\",\n    \"cpu cores\t: 14\",\n    \"apicid\t\t: 0\",\n    \"initial apicid\t: 0\",\n    \"fpu\t\t: yes\",\n    \"fpu_exception\t: yes\",\n    \"cpuid level\t: 20\",\n    \"wp\t\t: yes\",\n    \"flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts\",\n    \"bugs\t\t:\",\n    \"bogomips\t: 4788.90\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"address sizes\t: 46 bits physical, 48 bits virtual\",\n    \"power management:\",\n    \"\",\n    \"processor\t: 1\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 1\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 2\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 2\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 3\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 3\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 4\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 4\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 5\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 5\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 6\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 6\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 7\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 8\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 8\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 9\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 9\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 10\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 10\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 11\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 11\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 12\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 12\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 13\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 13\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 14\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 14\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 0\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 15\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 1\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 16\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 2\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 17\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 3\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 18\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 4\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 19\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 5\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 20\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 6\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 21\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 8\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 22\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 9\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 23\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 10\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 24\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 11\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 25\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 12\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 26\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 13\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 27\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 14\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 28\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 0\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 29\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 1\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 30\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 2\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 31\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 3\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 32\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 4\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 33\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 5\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 34\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 6\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 35\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 8\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 36\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 9\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 37\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 10\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 38\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 11\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 39\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 12\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 40\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 13\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 41\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 0\",\n    \"core id\t\t: 14\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 42\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 0\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 43\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 1\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 44\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 2\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 45\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 3\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 46\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 4\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 47\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 5\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 48\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 6\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 49\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 8\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 50\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 9\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 51\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 10\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 52\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 11\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 53\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 12\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 54\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 13\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n    \"processor\t: 55\",\n    \"cpu family\t: 6\",\n    \"cpu MHz\t\t: 2401.000\",\n    \"cache size\t: 35840 KB\",\n    \"physical id\t: 1\",\n    \"core id\t\t: 14\",\n    \"cpu cores\t: 14\",\n    \"cpuid level\t: 20\",\n    \"clflush size\t: 64\",\n    \"cache_alignment\t: 64\",\n    \"power management:\",\n};\n\nTEST(CacheLocality, ProcCpu) {\n  auto parsed = CacheLocality::readFromProcCpuinfoLines(fakeProcCpuinfo);\n\n  size_t expectedNumCpus = 56;\n  std::vector<size_t> expectedNumCachesByLevel = {28, 28, 2};\n  std::vector<size_t> expectedLocalityIndexByCpu = {\n      0,  2,  4,  6,  8,  10, 12, 14, 16, 18, 20, 22, 24, 26,\n      28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,\n      1,  3,  5,  7,  9,  11, 13, 15, 17, 19, 21, 23, 25, 27,\n      29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55};\n\n  EXPECT_EQ(expectedNumCpus, parsed.numCpus);\n  EXPECT_EQ(expectedNumCachesByLevel, parsed.numCachesByLevel);\n  EXPECT_EQ(expectedLocalityIndexByCpu, parsed.localityIndexByCpu);\n}\n\nTEST(CacheLocality, LinuxActual) {\n  if (!kIsLinux) {\n    return;\n  }\n\n  // CacheLocality reports the topology for all cpus, even those that are not\n  // available in the current environment (for example a container with pinned\n  // CPUs), so we can't use _SC_NPROCESSORS_ONLN or std::hardware_concurrency().\n  auto expectedNumCpus = sysconf(_SC_NPROCESSORS_CONF);\n\n  if (kIsArchArm || kIsArchAArch64) {\n    // As of Linux v6.9, Arm CPU's have a very basic /proc/cpuinfo\n    // they are missing many fields typically included in x86.\n    // https://github.com/torvalds/linux/blob/77f587896757708780a7e8792efe62939f25a5ab/arch/arm64/kernel/cpuinfo.c#L193\n    //\n    // Example ARM /proc/cpuinfo\n    // processor  : 0\n    // BogoMIPS   : 2000.00\n    // Features   : [..omitted..]\n    // CPU implementer    : 0x41\n    // CPU architecture: 8\n    // CPU variant    : 0x0\n    // CPU part   : 0xd4f\n    // CPU revision   : 0\n    //\n    // ...\n    //\n\n    EXPECT_THROW(CacheLocality::readFromProcCpuinfo(), std::runtime_error);\n\n    auto parsed2 = CacheLocality::readFromSysfs();\n    EXPECT_EQ(parsed2.numCpus, expectedNumCpus);\n\n    LOG(INFO) << fmt::format(\n        \"[sysfs] numCachesByLevel={}\", parsed2.numCachesByLevel);\n    LOG(INFO) << fmt::format(\n        \"[sysfs] equivClassesByCpu={}\", parsed2.equivClassesByCpu);\n\n    return;\n  }\n\n  auto parsed1 = CacheLocality::readFromProcCpuinfo();\n  EXPECT_EQ(parsed1.numCpus, expectedNumCpus);\n  auto parsed2 = CacheLocality::readFromSysfs();\n  EXPECT_EQ(parsed2.numCpus, expectedNumCpus);\n\n  LOG(INFO) << fmt::format(\n      \"[cpuinfo] numCachesByLevel={}\", parsed1.numCachesByLevel);\n  LOG(INFO) << fmt::format(\n      \"[sysfs] numCachesByLevel={}\", parsed2.numCachesByLevel);\n\n  LOG(INFO) << fmt::format(\n      \"[cpuinfo] equivClassesByCpu={}\", parsed1.equivClassesByCpu);\n  LOG(INFO) << fmt::format(\n      \"[sysfs] equivClassesByCpu={}\", parsed2.equivClassesByCpu);\n\n  EXPECT_EQ(parsed1.localityIndexByCpu, parsed2.localityIndexByCpu)\n      << fmt::format(\n             \"\\tcpuinfo: {}\\n\\tsysfs:   {}\",\n             parsed1.localityIndexByCpu,\n             parsed2.localityIndexByCpu);\n}\n\nTEST(CacheLocality, LogSystem) {\n  auto& sys = CacheLocality::system<>();\n  LOG(INFO) << fmt::format(\"numCpus={}\", sys.numCpus);\n  LOG(INFO) << fmt::format(\"numCachesByLevel={}\", sys.numCachesByLevel);\n  LOG(INFO) << fmt::format(\"localityIndexByCpu={}\", sys.localityIndexByCpu);\n  LOG(INFO) << fmt::format(\"equivClassesByCpu={}\", sys.equivClassesByCpu);\n}\n\n#ifdef RUSAGE_THREAD\nstatic uint64_t micros(struct timeval& tv) {\n  return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;\n}\n\ntemplate <typename F>\nstatic void logRusageFor(std::string name, F func) {\n  struct rusage before;\n  getrusage(RUSAGE_THREAD, &before);\n  auto beforeNow = std::chrono::steady_clock::now();\n  func();\n  auto afterNow = std::chrono::steady_clock::now();\n  struct rusage after;\n  getrusage(RUSAGE_THREAD, &after);\n  LOG(INFO)\n      << name << \": real: \"\n      << std::chrono::duration_cast<std::chrono::microseconds>(\n             afterNow - beforeNow)\n             .count()\n      << \" usec, user: \" << (micros(after.ru_utime) - micros(before.ru_utime))\n      << \" usec, sys: \" << (micros(after.ru_stime) - micros(before.ru_stime))\n      << \" usec\";\n}\n\nTEST(CacheLocality, BenchmarkProcCpuinfo) {\n  // See note about Arm in LinuxActual test\n  SKIP_IF(kIsArchArm || kIsArchAArch64);\n  logRusageFor(\"readFromProcCpuinfo\", CacheLocality::readFromProcCpuinfo);\n}\n\nTEST(CacheLocality, BenchmarkSysfs) {\n  logRusageFor(\"readFromSysfs\", CacheLocality::readFromSysfs);\n}\n#endif\n\n#if defined(FOLLY_HAVE_LINUX_VDSO) && !defined(FOLLY_SANITIZE_MEMORY)\nTEST(Getcpu, VdsoGetcpu) {\n  Getcpu::Func func = Getcpu::resolveVdsoFunc();\n  SKIP_IF(func == nullptr);\n\n  unsigned cpu;\n  func(&cpu, nullptr, nullptr);\n\n  EXPECT_TRUE(cpu < CPU_SETSIZE);\n}\n#endif\n\nTEST(ThreadId, SimpleTls) {\n  unsigned cpu = 0;\n  auto rv =\n      folly::FallbackGetcpu<SequentialThreadId>::getcpu(&cpu, nullptr, nullptr);\n  EXPECT_EQ(rv, 0);\n  EXPECT_TRUE(cpu > 0);\n  unsigned again;\n  folly::FallbackGetcpu<SequentialThreadId>::getcpu(&again, nullptr, nullptr);\n  EXPECT_EQ(cpu, again);\n}\n\nTEST(ThreadId, SimplePthread) {\n  unsigned cpu = 0;\n  auto rv =\n      folly::FallbackGetcpu<HashingThreadId>::getcpu(&cpu, nullptr, nullptr);\n  EXPECT_EQ(rv, 0);\n  EXPECT_TRUE(cpu > 0);\n  unsigned again;\n  folly::FallbackGetcpu<HashingThreadId>::getcpu(&again, nullptr, nullptr);\n  EXPECT_EQ(cpu, again);\n}\n\nstatic thread_local unsigned testingCpu = 0;\n\nstatic int testingGetcpu(unsigned* cpu, unsigned* node, void* /* unused */) {\n  if (cpu != nullptr) {\n    *cpu = testingCpu;\n  }\n  if (node != nullptr) {\n    *node = testingCpu;\n  }\n  return 0;\n}\n\nTEST(AccessSpreader, Simple) {\n  for (size_t s = 1; s < 200; ++s) {\n    EXPECT_LT(AccessSpreader<>::current(s), s);\n  }\n}\n\nTEST(AccessSpreader, SimpleCached) {\n  for (size_t s = 1; s < 200; ++s) {\n    EXPECT_LT(AccessSpreader<>::cachedCurrent(s), s);\n  }\n}\n\nTEST(AccessSpreader, ConcurrentAccessCached) {\n  std::vector<std::thread> threads;\n  for (size_t i = 0; i < 4; ++i) {\n    threads.emplace_back([]() {\n      for (size_t s : {16, 32, 64}) {\n        for (size_t j = 1; j < 200; ++j) {\n          EXPECT_LT(AccessSpreader<>::cachedCurrent(s), s);\n          EXPECT_LT(AccessSpreader<>::cachedCurrent(s), s);\n        }\n        std::this_thread::yield();\n      }\n    });\n  }\n  for (auto& thread : threads) {\n    thread.join();\n  }\n}\n\n#define DECLARE_SPREADER_TAG(tag, locality, func)      \\\n  namespace {                                          \\\n  template <typename dummy>                            \\\n  struct tag {};                                       \\\n  }                                                    \\\n  namespace folly {                                    \\\n  template <>                                          \\\n  const CacheLocality& CacheLocality::system<tag>() {  \\\n    static auto* inst = new CacheLocality(locality);   \\\n    return *inst;                                      \\\n  }                                                    \\\n  template <>                                          \\\n  Getcpu::Func AccessSpreader<tag>::pickGetcpuFunc() { \\\n    return func;                                       \\\n  }                                                    \\\n  template struct AccessSpreader<tag>;                 \\\n  }\n\nDECLARE_SPREADER_TAG(ManualTag, CacheLocality::uniform(16), testingGetcpu)\n\nTEST(AccessSpreader, Wrapping) {\n  // this test won't pass unless locality.numCpus divides kMaxCpus\n  auto numCpus = CacheLocality::system<ManualTag>().numCpus;\n  EXPECT_EQ(0, 128 % numCpus);\n  for (size_t s = 1; s < 200; ++s) {\n    for (size_t c = 0; c < 400; ++c) {\n      testingCpu = c;\n      auto observed = AccessSpreader<ManualTag>::current(s);\n      testingCpu = c % numCpus;\n      auto expected = AccessSpreader<ManualTag>::current(s);\n      EXPECT_EQ(expected, observed)\n          << \"numCpus=\" << numCpus << \", s=\" << s << \", c=\" << c;\n      EXPECT_LE(observed, AccessSpreader<ManualTag>::maxStripeValue());\n    }\n  }\n}\n\nTEST(CoreAllocator, Basic) {\n  constexpr size_t kNumStripes = 32;\n\n  auto res = coreMalloc(8, kNumStripes, 0);\n  memset(res, 0, 8);\n  coreFree(res);\n\n  res = coreMalloc(8, kNumStripes, 0);\n  EXPECT_EQ(0, (intptr_t)res % 8); // check alignment\n  memset(res, 0, 8);\n  coreFree(res);\n  res = coreMalloc(12, kNumStripes, 0);\n  if (alignof(std::max_align_t) >= 16) {\n    EXPECT_EQ(0, (intptr_t)res % 16); // check alignment\n  }\n  memset(res, 0, 12);\n  coreFree(res);\n  res = coreMalloc(257, kNumStripes, 0);\n  memset(res, 0, 257);\n  coreFree(res);\n\n  CoreAllocator<int> a;\n  std::vector<int*> mems;\n  for (int i = 0; i < 10000; i++) {\n    CoreAllocatorGuard g(kNumStripes, i % kNumStripes);\n    mems.push_back(a.allocate(1));\n  }\n  for (auto& mem : mems) {\n    a.deallocate(mem, 1);\n  }\n  mems.clear();\n}\n\nTEST(CoreAllocator, MinimumAllocationSize) {\n  // coreMalloc should handle sizes smaller than sizeof(void*) by rounding up\n  constexpr size_t kNumStripes = 32;\n\n  // Test that small allocations (< 8 bytes) work correctly\n  // The Allocator class should round these up to 8 bytes\n  auto res1 = coreMalloc(1, kNumStripes, 0);\n  EXPECT_NE(nullptr, res1);\n  memset(res1, 0xFF, 1); // Should not crash\n  coreFree(res1);\n\n  auto res4 = coreMalloc(4, kNumStripes, 0);\n  EXPECT_NE(nullptr, res4);\n  memset(res4, 0xFF, 4); // Should not crash\n  coreFree(res4);\n\n  // Verify that 8-byte allocation works (minimum valid size)\n  auto res8 = coreMalloc(8, kNumStripes, 0);\n  EXPECT_NE(nullptr, res8);\n  memset(res8, 0xFF, 8);\n  coreFree(res8);\n}\n"
  },
  {
    "path": "folly/concurrency/test/ConcurrentHashMapBench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/ConcurrentHashMap.h>\n\n#include <iomanip>\n#include <iostream>\n#include <thread>\n\n#include <folly/BenchmarkUtil.h>\n#include <folly/portability/GFlags.h>\n#include <folly/synchronization/test/Barrier.h>\n#include <folly/system/HardwareConcurrency.h>\n\nDEFINE_int32(reps, 10, \"number of reps\");\nDEFINE_int32(ops, 1000 * 1000, \"number of operations per rep\");\nDEFINE_int64(size, 10 * 1000 * 1000, \"size\");\n\ntemplate <typename Func, typename EndFunc>\ninline uint64_t run_once(int nthr, const Func& fn, const EndFunc& endFn) {\n  folly::test::Barrier b(nthr + 1);\n  std::vector<std::thread> thr(nthr);\n  for (int tid = 0; tid < nthr; ++tid) {\n    thr[tid] = std::thread([&, tid] {\n      b.wait();\n      b.wait();\n      fn(tid);\n    });\n  }\n  b.wait();\n  // begin time measurement\n  auto tbegin = std::chrono::steady_clock::now();\n  b.wait();\n  /* wait for completion */\n  for (int i = 0; i < nthr; ++i) {\n    thr[i].join();\n  }\n  /* end time measurement */\n  auto tend = std::chrono::steady_clock::now();\n  endFn();\n  return std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)\n      .count();\n}\n\ntemplate <typename RepFunc>\nuint64_t runBench(const std::string& name, int ops, const RepFunc& repFn) {\n  int reps = FLAGS_reps;\n  uint64_t min = UINTMAX_MAX;\n  uint64_t max = 0;\n  uint64_t sum = 0;\n\n  repFn(); // sometimes first run is outlier\n  for (int r = 0; r < reps; ++r) {\n    uint64_t dur = repFn();\n    sum += dur;\n    min = std::min(min, dur);\n    max = std::max(max, dur);\n    // if each rep takes too long run at least 3 reps\n    const uint64_t minute = 60000000000UL;\n    if (sum > minute && r >= 2) {\n      reps = r + 1;\n      break;\n    }\n  }\n\n  const std::string unit = \" ns\";\n  uint64_t avg = sum / reps;\n  uint64_t res = min;\n  std::cout << name;\n  std::cout << \"   \" << std::setw(4) << (max + ops / 2) / ops << unit;\n  std::cout << \"   \" << std::setw(4) << (avg + ops / 2) / ops << unit;\n  std::cout << \"   \" << std::setw(4) << (min + ops / 2) / ops << unit;\n  std::cout << std::endl;\n  return res;\n}\n\nuint64_t bench_ctor_dtor(\n    const int nthr, const int size, const std::string& name) {\n  int ops = FLAGS_ops;\n  auto repFn = [&] {\n    auto fn = [&](int) {\n      for (int i = 0; i < ops; ++i) {\n        folly::ConcurrentHashMap<int, int> m;\n        for (int j = 0; j < size; ++j) {\n          folly::doNotOptimizeAway(m.insert(j, j));\n        }\n      }\n    };\n    auto endfn = [&] {};\n    return run_once(nthr, fn, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\nuint64_t bench_find(\n    const int nthr, const bool sameItem, const std::string& name) {\n  int ops = FLAGS_ops;\n  folly::ConcurrentHashMap<int, int> m;\n  for (int j = 0; j < FLAGS_size; ++j) {\n    m.insert(j, j);\n  }\n  int key = FLAGS_size / 2;\n  auto repFn = [&] {\n    auto fn = [&](int) {\n      if (sameItem) {\n        for (int i = 0; i < ops; ++i) {\n          folly::doNotOptimizeAway(m.find(key));\n        }\n      } else {\n        for (int i = 0; i < ops; ++i) {\n          folly::doNotOptimizeAway(m.find(i));\n        }\n      }\n    };\n    auto endfn = [&] {};\n    return run_once(nthr, fn, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\nuint64_t bench_iter(const int nthr, int size, const std::string& name) {\n  int reps = size == 0 ? 1000000 : size < 1000000 ? 1000000 / size : 1;\n  int ops = size == 0 ? reps : size * reps;\n  folly::ConcurrentHashMap<int, int> m;\n  for (int j = 0; j < size; ++j) {\n    m.insert(j, j);\n  }\n  auto repFn = [&] {\n    auto fn = [&](int) {\n      for (int i = 0; i < reps; ++i) {\n        for (auto it = m.begin(); it != m.end(); doNotOptimizeAway(++it)) {\n        }\n      }\n    };\n    auto endfn = [&] {};\n    return run_once(nthr, fn, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\nuint64_t bench_begin(const int nthr, int size, const std::string& name) {\n  int ops = FLAGS_ops;\n  folly::ConcurrentHashMap<int, int> m;\n  for (int j = 0; j < size; ++j) {\n    m.insert(j, j);\n  }\n  auto repFn = [&] {\n    auto fn = [&](int) {\n      for (int i = 0; i < ops; ++i) {\n        folly::doNotOptimizeAway(m.begin());\n      }\n    };\n    auto endfn = [&] {};\n    return run_once(nthr, fn, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\nuint64_t bench_empty(const int nthr, int size, const std::string& name) {\n  int ops = FLAGS_ops;\n  folly::ConcurrentHashMap<int, int> m;\n  for (int j = 0; j < size; ++j) {\n    m.insert(j, j);\n  }\n  auto repFn = [&] {\n    auto fn = [&](int) {\n      for (int i = 0; i < ops; ++i) {\n        folly::doNotOptimizeAway(m.empty());\n      }\n    };\n    auto endfn = [&] {};\n    return run_once(nthr, fn, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\nuint64_t bench_size(const int nthr, int size, const std::string& name) {\n  int ops = FLAGS_ops;\n  folly::ConcurrentHashMap<int, int> m;\n  for (int j = 0; j < size; ++j) {\n    m.insert(j, j);\n  }\n  auto repFn = [&] {\n    auto fn = [&](int) {\n      for (int i = 0; i < ops; ++i) {\n        folly::doNotOptimizeAway(m.size());\n      }\n    };\n    auto endfn = [&] {};\n    return run_once(nthr, fn, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\nvoid dottedLine() {\n  std::cout << \"..............................................................\"\n            << std::endl;\n}\n\nvoid benches() {\n  std::cout << \"==============================================================\"\n            << std::endl;\n  std::cout << \"Test name                         Max time  Avg time  Min time\"\n            << std::endl;\n  const int maxThreads = folly::available_concurrency();\n  for (int nthr = 1; nthr <= maxThreads;) {\n    std::cout << \"========================= \" << std::setw(2) << nthr\n              << \" threads\" << \" =========================\" << std::endl;\n    bench_ctor_dtor(nthr, 0, \"CHM ctor/dtor -- empty          \");\n    bench_ctor_dtor(nthr, 1, \"CHM ctor/dtor -- 1 item         \");\n    dottedLine();\n    bench_find(nthr, false, \"CHM find() -- 10M items         \");\n    bench_find(nthr, true, \"CHM find() -- 1 of 10M items    \");\n    dottedLine();\n    bench_begin(nthr, 0, \"CHM begin() -- empty            \");\n    bench_begin(nthr, 1, \"CHM begin() -- 1 item           \");\n    bench_begin(nthr, 10000, \"CHM begin() -- 10K items        \");\n    bench_begin(nthr, 10000000, \"CHM begin() -- 10M items        \");\n    dottedLine();\n    bench_iter(nthr, 0, \"CHM iterate -- empty            \");\n    bench_iter(nthr, 1, \"CHM iterate -- 1 item           \");\n    bench_iter(nthr, 10, \"CHM iterate -- 10 items         \");\n    bench_iter(nthr, 100, \"CHM iterate -- 100 items        \");\n    bench_iter(nthr, 1000, \"CHM iterate -- 1K items         \");\n    bench_iter(nthr, 10000, \"CHM iterate -- 10K items        \");\n    bench_iter(nthr, 100000, \"CHM iterate -- 100K items       \");\n    bench_iter(nthr, 1000000, \"CHM iterate -- 1M items         \");\n    bench_iter(nthr, 10000000, \"CHM iterate -- 10M items        \");\n    dottedLine();\n    bench_empty(nthr, 0, \"CHM empty() -- empty            \");\n    bench_empty(nthr, 1, \"CHM empty() -- 1 item           \");\n    bench_empty(nthr, 10000, \"CHM empty() -- 10K items        \");\n    bench_empty(nthr, 10000000, \"CHM empty() -- 10M items        \");\n    dottedLine();\n    bench_size(nthr, 0, \"CHM size() -- empty             \");\n    bench_size(nthr, 1, \"CHM size() -- 1 item            \");\n    bench_size(nthr, 10, \"CHM size() -- 10 items          \");\n    bench_size(nthr, 100, \"CHM size() -- 100 items         \");\n    bench_size(nthr, 1000, \"CHM size() -- 1K items          \");\n    bench_size(nthr, 10000, \"CHM size() -- 10K items         \");\n    bench_size(nthr, 100000, \"CHM size() -- 100K items        \");\n    bench_size(nthr, 1000000, \"CHM size() -- 1M items          \");\n    bench_size(nthr, 10000000, \"CHM size() -- 10M items         \");\n\n    if (nthr == maxThreads) {\n      break;\n    }\n    int nextNthr = nthr * 4;\n    nthr = (nextNthr > maxThreads) ? maxThreads : nextNthr;\n  }\n  std::cout << \"==============================================================\"\n            << std::endl;\n}\n\nint main() {\n  benches();\n}\n"
  },
  {
    "path": "folly/concurrency/test/ConcurrentHashMapStressTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/ConcurrentHashMap.h>\n\n#include <atomic>\n#include <limits>\n#include <thread>\n#include <vector>\n\n#include <folly/portability/SysMman.h>\n\n#include <folly/Traits.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Latch.h>\n\ntemplate <typename T>\nclass ConcurrentHashMapStressTest : public ::testing::Test {};\nTYPED_TEST_SUITE_P(ConcurrentHashMapStressTest);\n\ntemplate <template <\n    typename,\n    typename,\n    uint8_t,\n    typename,\n    typename,\n    typename,\n    template <typename> class,\n    class> class Impl>\nstruct MapFactory {\n  template <\n      typename KeyType,\n      typename ValueType,\n      typename HashFn = std::hash<KeyType>,\n      typename KeyEqual = std::equal_to<KeyType>,\n      typename Allocator = std::allocator<uint8_t>,\n      uint8_t ShardBits = 8,\n      template <typename> class Atom = std::atomic,\n      class Mutex = std::mutex>\n  using MapT = folly::ConcurrentHashMap<\n      KeyType,\n      ValueType,\n      HashFn,\n      KeyEqual,\n      Allocator,\n      ShardBits,\n      Atom,\n      Mutex,\n      Impl>;\n};\n\n#define CHM typename TypeParam::template MapT\n\nTYPED_TEST_P(ConcurrentHashMapStressTest, StressTestReclamation) {\n  // Create a map where we keep reclaiming a lot of objects that are linked to\n  // one node.\n\n  // Ensure all entries are mapped to a single segment.\n  struct constant_hash {\n    uint64_t operator()(unsigned long) const noexcept { return 0; }\n  };\n  using Map = CHM<unsigned long, unsigned long, constant_hash>;\n  static constexpr unsigned long key_prev =\n      0; // A key that the test key has a link to - to guard against immediate\n         // reclamation.\n  static constexpr unsigned long key_test =\n      1; // A key that keeps being reclaimed repeatedly.\n  static constexpr unsigned long key_link_explosion =\n      2; // A key that is linked to the test key.\n\n  Map map;\n  EXPECT_TRUE(map.insert(std::make_pair(key_prev, 0)).second);\n  EXPECT_TRUE(map.insert(std::make_pair(key_test, 0)).second);\n  EXPECT_TRUE(map.insert(std::make_pair(key_link_explosion, 0)).second);\n\n  struct alignas(32) ThreadInfo {\n    pthread_t id;\n    uint64_t idx;\n    Map* map;\n    folly::Latch* start;\n  };\n\n  // Test with (2^16)+ threads, enough to overflow a 16 bit integer.\n  // It should be uncommon to have more than 2^32 concurrent accesses.\n  static constexpr uint64_t num_threads = 1u << 16; // 2^16\n  static constexpr uint64_t iters = 100;\n  // Separately allocating the thread args would be costly. This cost can be\n  // optimized by allocating them all together.\n  std::vector<ThreadInfo> threads;\n  threads.reserve(num_threads);\n  folly::Latch start(num_threads);\n\n  // Separately mapping each thread stack would be costly. This cost can be\n  // optimized by mapping all thread stacks together at once. Separately\n  // faulting in each top page of each thread stack is costly. This cost can be\n  // optimized by prefaulting the entire mapping. There will still be a TLB miss\n  // in each thread but perhaps that would be tolerable.\n  constexpr size_t stack_size = 1u << 16; // 64KiB\n#if !defined(_WIN32)\n  const auto mm_len = stack_size * num_threads;\n  const auto mm_prot = PROT_READ | PROT_WRITE;\n  const auto mm_flags =\n      MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_POPULATE;\n  const auto stack_array = ::mmap(nullptr, mm_len, mm_prot, mm_flags, -1, 0);\n  ASSERT_NE(stack_array, MAP_FAILED);\n  SCOPE_EXIT {\n    ::munmap(stack_array, stack_size * num_threads);\n  };\n#endif\n\n  pthread_attr_t attr;\n  pthread_attr_init(&attr);\n  pthread_attr_setstacksize(&attr, stack_size);\n#if !defined(_WIN32)\n  // Since the thread stacks are all allocated together, there are no guard\n  // pages.\n  pthread_attr_setguardsize(&attr, 0);\n#endif\n\n  auto thfunc = +[](void* arg) -> void* {\n    auto info = *static_cast<ThreadInfo*>(arg);\n    auto t = info.idx;\n    auto& map = *info.map;\n    auto& start = *info.start;\n\n    start.arrive_and_wait();\n    static constexpr uint64_t progress_report_pct =\n        (iters / 20); // Every 5% we log progress\n    for (uint64_t i = 0; i < iters; i++) {\n      if (t == 0 && (i % progress_report_pct) == 0) {\n        // To a casual observer - to know that the test is progressing, even\n        // if slowly\n        LOG(INFO) << \"Progress: \" << (i * 100 / iters);\n      }\n\n      map.insert_or_assign(key_test, i * num_threads);\n    }\n    return nullptr;\n  };\n\n  for (uint64_t t = 0; t < num_threads; t++) {\n#if !defined(_WIN32)\n    void* stack_base = (char*)stack_array + (t * stack_size);\n    pthread_attr_setstack(&attr, stack_base, stack_size);\n#endif\n    auto& tharg = threads.emplace_back(\n        ThreadInfo{.id = {}, .idx = t, .map = &map, .start = &start});\n    auto ret = pthread_create(&tharg.id, &attr, thfunc, &tharg);\n    PCHECK(ret == 0);\n  }\n  for (auto& t : threads) {\n    void* retval = nullptr;\n    auto ret = pthread_join(t.id, &retval);\n    PCHECK(ret == 0);\n    DCHECK(retval == nullptr);\n  }\n\n  pthread_attr_destroy(&attr);\n}\n\nREGISTER_TYPED_TEST_SUITE_P( //\n    ConcurrentHashMapStressTest,\n    StressTestReclamation);\n\nusing folly::detail::concurrenthashmap::bucket::BucketTable;\n\n#if (                                                        \\\n    FOLLY_SSE_PREREQ(4, 2) ||                                \\\n    (FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE)) && \\\n    FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nusing folly::detail::concurrenthashmap::simd::SIMDTable;\nusing MapFactoryTypes =\n    ::testing::Types<MapFactory<BucketTable>, MapFactory<SIMDTable>>;\n#else\nusing MapFactoryTypes = ::testing::Types<MapFactory<BucketTable>>;\n#endif\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    MapFactoryTypesInstantiation, ConcurrentHashMapStressTest, MapFactoryTypes);\n"
  },
  {
    "path": "folly/concurrency/test/ConcurrentHashMapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/ConcurrentHashMap.h>\n\n#include <atomic>\n#include <limits>\n#include <memory>\n#include <thread>\n#include <vector>\n\n#include <folly/Traits.h>\n#include <folly/container/test/TrackingTypes.h>\n#include <folly/hash/Hash.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Latch.h>\n#include <folly/test/DeterministicSchedule.h>\n\nusing namespace folly::test;\nusing namespace folly;\nusing namespace std;\n\nDEFINE_int64(seed, 0, \"Seed for random number generators\");\n\ntemplate <typename T>\nclass ConcurrentHashMapTest : public ::testing::Test {};\nTYPED_TEST_SUITE_P(ConcurrentHashMapTest);\n\ntemplate <template <\n    typename,\n    typename,\n    uint8_t,\n    typename,\n    typename,\n    typename,\n    template <typename> class,\n    class> class Impl>\nstruct MapFactory {\n  template <\n      typename KeyType,\n      typename ValueType,\n      typename HashFn = std::hash<KeyType>,\n      typename KeyEqual = std::equal_to<KeyType>,\n      typename Allocator = std::allocator<uint8_t>,\n      uint8_t ShardBits = 8,\n      template <typename> class Atom = std::atomic,\n      class Mutex = std::mutex>\n  using MapT = ConcurrentHashMap<\n      KeyType,\n      ValueType,\n      HashFn,\n      KeyEqual,\n      Allocator,\n      ShardBits,\n      Atom,\n      Mutex,\n      Impl>;\n};\n\n#define CHM typename TypeParam::template MapT\n\nTYPED_TEST_P(ConcurrentHashMapTest, MapTest) {\n  CHM<uint64_t, uint64_t> foomap(3);\n  EXPECT_TRUE(foomap.empty());\n  EXPECT_EQ(foomap.find(1), foomap.cend());\n  auto r = foomap.insert(1, 0);\n  EXPECT_TRUE(r.second);\n  auto r2 = foomap.insert(1, 0);\n  EXPECT_EQ(r.first->second, 0);\n  EXPECT_EQ(r.first->first, 1);\n  EXPECT_EQ(r2.first->second, 0);\n  EXPECT_EQ(r2.first->first, 1);\n  EXPECT_EQ(r.first, r2.first);\n  EXPECT_TRUE(r.second);\n  EXPECT_FALSE(r2.second);\n  EXPECT_FALSE(foomap.empty());\n  EXPECT_TRUE(foomap.insert(std::make_pair(2, 0)).second);\n  EXPECT_TRUE(foomap.insert_or_assign(2, 0).second);\n  EXPECT_EQ(foomap.size(), 2);\n  EXPECT_TRUE(foomap.assign_if_equal(2, 0, 3));\n  EXPECT_FALSE(foomap.assign_if(2, 4, [](auto&&) { return false; }));\n  EXPECT_TRUE(foomap.insert(3, 0).second);\n  EXPECT_FALSE(foomap.erase_if_equal(3, 1));\n  EXPECT_TRUE(foomap.erase_if_equal(3, 0));\n  EXPECT_TRUE(foomap.insert(3, 0).second);\n  EXPECT_NE(foomap.find(1), foomap.cend());\n  EXPECT_NE(foomap.find(2), foomap.cend());\n  EXPECT_EQ(foomap.find(2)->second, 3);\n  EXPECT_EQ(foomap[2], 3);\n  EXPECT_EQ(foomap[20], 0);\n  EXPECT_EQ(foomap.at(20), 0);\n  EXPECT_FALSE(foomap.insert(1, 0).second);\n  auto l = foomap.find(1);\n  foomap.erase(l);\n  EXPECT_FALSE(foomap.erase(1));\n  EXPECT_EQ(foomap.find(1), foomap.cend());\n  auto res = foomap.find(2);\n  EXPECT_NE(res, foomap.cend());\n  EXPECT_EQ(3, res->second);\n  EXPECT_FALSE(foomap.empty());\n  foomap.clear();\n  EXPECT_TRUE(foomap.empty());\n  EXPECT_TRUE(foomap.insert(3, 0).second);\n  EXPECT_FALSE(foomap.empty());\n  foomap.erase(3);\n  EXPECT_TRUE(foomap.empty());\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MaxSizeTest) {\n  CHM<uint64_t, uint64_t> foomap(2, 16);\n  bool insert_failed = false;\n  for (int i = 0; i < 32; i++) {\n    auto res = foomap.insert(0, 0);\n    if (!res.second) {\n      insert_failed = true;\n    }\n  }\n  EXPECT_TRUE(insert_failed);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MoveTest) {\n  CHM<uint64_t, uint64_t> foomap(2, 16);\n  auto other = std::move(foomap);\n  auto other2 = std::move(other);\n  other = std::move(other2);\n}\n\nstruct foo {\n  static int moved;\n  static int copied;\n  foo(foo&&) noexcept { moved++; }\n  foo& operator=(foo&&) {\n    moved++;\n    return *this;\n  }\n  foo& operator=(const foo&) {\n    copied++;\n    return *this;\n  }\n  foo(const foo&) { copied++; }\n  foo() {}\n};\nint foo::moved{0};\nint foo::copied{0};\n\nTYPED_TEST_P(ConcurrentHashMapTest, EmplaceTest) {\n  CHM<uint64_t, foo> foomap(200);\n  foo bar; // Make sure to test copy\n  foomap.insert(1, bar);\n  EXPECT_EQ(foo::moved, 0);\n  EXPECT_EQ(foo::copied, 1);\n  foo::copied = 0;\n  // The difference between emplace and try_emplace:\n  // If insertion fails, try_emplace does not move its argument\n  foomap.try_emplace(1, foo());\n  EXPECT_EQ(foo::moved, 0);\n  EXPECT_EQ(foo::copied, 0);\n  foomap.emplace(1, foo());\n  EXPECT_EQ(foo::moved, 1);\n  EXPECT_EQ(foo::copied, 0);\n  // Reset the counters. Repeated tests are allowed\n  foo::moved = 0;\n  foo::copied = 0;\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MapInsertIteratorValueTest) {\n  CHM<uint64_t, uint64_t> foomap(2);\n  for (uint64_t i = 0; i < 1 << 16; i++) {\n    auto ret = foomap.insert(i, i + 1);\n    EXPECT_TRUE(ret.second);\n    EXPECT_EQ(ret.first->second, i + 1);\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MapResizeTest) {\n  CHM<uint64_t, uint64_t> foomap(2);\n  EXPECT_EQ(foomap.find(1), foomap.cend());\n  EXPECT_TRUE(foomap.insert(1, 0).second);\n  EXPECT_TRUE(foomap.insert(2, 0).second);\n  EXPECT_TRUE(foomap.insert(3, 0).second);\n  EXPECT_TRUE(foomap.insert(4, 0).second);\n  foomap.reserve(512);\n  EXPECT_NE(foomap.find(1), foomap.cend());\n  EXPECT_NE(foomap.find(2), foomap.cend());\n  EXPECT_FALSE(foomap.insert(1, 0).second);\n  EXPECT_TRUE(foomap.erase(1));\n  EXPECT_EQ(foomap.find(1), foomap.cend());\n  auto res = foomap.find(2);\n  EXPECT_NE(res, foomap.cend());\n  if (res != foomap.cend()) {\n    EXPECT_EQ(0, res->second);\n  }\n  foomap.reserve(0);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ReserveTest) {\n  CHM<uint64_t, uint64_t> foomap;\n  int64_t insert_count = 0;\n  int64_t actual_count = 0;\n\n  for (int64_t i = 0; i < 1000; i++) {\n    if (i % 100 == 0) {\n      foomap.reserve(i + 100);\n    }\n    foomap.insert(std::make_pair(i, i));\n    insert_count++;\n  }\n\n  for (auto it = foomap.begin(); it != foomap.cend(); ++it) {\n    actual_count++;\n  }\n\n  EXPECT_EQ(insert_count, actual_count);\n}\n\n// Ensure we can insert objects without copy constructors.\nTYPED_TEST_P(ConcurrentHashMapTest, MapNoCopiesTest) {\n  struct Uncopyable {\n    int i_;\n    Uncopyable(int i) { i_ = i; }\n    Uncopyable(const Uncopyable& that) = delete;\n    bool operator==(const Uncopyable& o) const { return i_ == o.i_; }\n  };\n  struct Hasher {\n    size_t operator()(const Uncopyable&) const { return 0; }\n  };\n  CHM<Uncopyable, Uncopyable, Hasher> foomap(2);\n  EXPECT_TRUE(foomap.try_emplace(1, 1).second);\n  EXPECT_TRUE(foomap.try_emplace(2, 2).second);\n  auto res = foomap.find(2);\n  EXPECT_NE(res, foomap.cend());\n\n  EXPECT_TRUE(foomap.try_emplace(3, 3).second);\n\n  auto res2 = foomap.find(2);\n  EXPECT_NE(res2, foomap.cend());\n  EXPECT_EQ(&(res->second), &(res2->second));\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MapMovableKeysTest) {\n  struct Movable {\n    int i_;\n    Movable(int i) { i_ = i; }\n    Movable(const Movable&) = delete;\n    Movable(Movable&& o) {\n      i_ = o.i_;\n      o.i_ = 0;\n    }\n    bool operator==(const Movable& o) const { return i_ == o.i_; }\n  };\n  struct Hasher {\n    size_t operator()(const Movable&) const { return 0; }\n  };\n  CHM<Movable, Movable, Hasher> foomap(2);\n  EXPECT_TRUE(foomap.insert(std::make_pair(Movable(10), Movable(1))).second);\n  EXPECT_TRUE(foomap.assign(Movable(10), Movable(2)));\n  EXPECT_TRUE(foomap.insert(Movable(11), Movable(1)).second);\n  EXPECT_TRUE(foomap.emplace(Movable(12), Movable(1)).second);\n  EXPECT_TRUE(foomap.insert_or_assign(Movable(10), Movable(3)).second);\n  EXPECT_TRUE(foomap.assign_if_equal(Movable(10), Movable(3), Movable(4)));\n  EXPECT_TRUE(foomap.assign_if(Movable(10), Movable(5), [](auto&&) {\n    return true;\n  }));\n  EXPECT_FALSE(foomap.try_emplace(Movable(10), Movable(3)).second);\n  EXPECT_TRUE(foomap.try_emplace(Movable(13), Movable(3)).second);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MapUpdateTest) {\n  CHM<uint64_t, uint64_t> foomap(2);\n  EXPECT_TRUE(foomap.insert(1, 10).second);\n  EXPECT_TRUE(bool(foomap.assign(1, 11)));\n  auto res = foomap.find(1);\n  EXPECT_NE(res, foomap.cend());\n  EXPECT_EQ(11, res->second);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MapIterateTest2) {\n  CHM<uint64_t, uint64_t> foomap(2);\n  auto begin = foomap.cbegin();\n  auto end = foomap.cend();\n  EXPECT_EQ(begin, end);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MapIterateTest) {\n  CHM<uint64_t, uint64_t> foomap(2);\n  EXPECT_EQ(foomap.cbegin(), foomap.cend());\n  EXPECT_TRUE(foomap.insert(1, 1).second);\n  EXPECT_TRUE(foomap.insert(2, 2).second);\n  auto iter = foomap.cbegin();\n  EXPECT_NE(iter, foomap.cend());\n  EXPECT_EQ(iter->first, 1);\n  EXPECT_EQ(iter->second, 1);\n  ++iter;\n  EXPECT_NE(iter, foomap.cend());\n  EXPECT_EQ(iter->first, 2);\n  EXPECT_EQ(iter->second, 2);\n  ++iter;\n  EXPECT_EQ(iter, foomap.cend());\n\n  int count = 0;\n  for (auto it = foomap.cbegin(); it != foomap.cend(); ++it) {\n    count++;\n  }\n  EXPECT_EQ(count, 2);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, MoveIterateAssignIterate) {\n  using Map = CHM<int, int>;\n  Map tmp;\n  Map map{std::move(tmp)};\n\n  map.insert(0, 0);\n  ++map.cbegin();\n  CHM<int, int> other;\n  other.insert(0, 0);\n  map = std::move(other);\n  ++map.cbegin();\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, EraseTest) {\n  CHM<uint64_t, uint64_t> foomap(3);\n  foomap.insert(1, 0);\n  auto f1 = foomap.find(1);\n  EXPECT_EQ(1, foomap.erase(1));\n  foomap.erase(f1);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, EraseIfEqualTest) {\n  CHM<uint64_t, uint64_t> foomap(3);\n  foomap.insert(1, 0);\n  EXPECT_FALSE(foomap.erase_if_equal(1, 1));\n  auto f1 = foomap.find(1);\n  EXPECT_EQ(0, f1->second);\n  EXPECT_TRUE(foomap.erase_if_equal(1, 0));\n  EXPECT_EQ(foomap.find(1), foomap.cend());\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, EraseIfTest) {\n  CHM<uint64_t, uint64_t> foomap(3);\n  foomap.insert(1, 0);\n  EXPECT_FALSE(foomap.erase_key_if(1, [](const uint64_t& value) {\n    return value == 1;\n  }));\n  auto f1 = foomap.find(1);\n  EXPECT_EQ(0, f1->second);\n  EXPECT_TRUE(foomap.erase_key_if(1, [](const uint64_t& value) {\n    return value == 0;\n  }));\n  EXPECT_EQ(foomap.find(1), foomap.cend());\n\n  CHM<std::string, std::weak_ptr<uint64_t>> barmap(3);\n  auto shared = std::make_shared<uint64_t>(123);\n  barmap.insert(\"test\", shared);\n  EXPECT_FALSE(barmap.erase_key_if(\n      \"test\",\n      [](const std::weak_ptr<uint64_t>& ptr) { return ptr.expired(); }));\n  EXPECT_EQ(*barmap.find(\"test\")->second.lock(), 123);\n  shared.reset();\n  EXPECT_TRUE(barmap.erase_key_if(\n      \"test\",\n      [](const std::weak_ptr<uint64_t>& ptr) { return ptr.expired(); }));\n  EXPECT_EQ(barmap.find(\"test\"), barmap.cend());\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, CopyIterator) {\n  CHM<int, int> map;\n  map.insert(0, 0);\n  for (auto cit = map.cbegin(); cit != map.cend(); ++cit) {\n    std::pair<int const, int> const ckv{0, 0};\n    EXPECT_EQ(*cit, ckv);\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, EraseInIterateTest) {\n  CHM<uint64_t, uint64_t> foomap(3);\n  for (uint64_t k = 0; k < 10; ++k) {\n    foomap.insert(k, k);\n  }\n  EXPECT_EQ(10, foomap.size());\n  for (auto it = foomap.cbegin(); it != foomap.cend();) {\n    if (it->second > 3) {\n      it = foomap.erase(it);\n    } else {\n      ++it;\n    }\n  }\n  EXPECT_EQ(4, foomap.size());\n  for (auto it = foomap.cbegin(); it != foomap.cend(); ++it) {\n    EXPECT_GE(3, it->second);\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, AssignIfTest) {\n  CHM<uint64_t, uint64_t> foomap(3);\n  foomap.insert(1, 0);\n\n  bool canAssignFlag = false;\n  EXPECT_FALSE(\n      foomap.assign_if(1, 1, [canAssignFlag](auto&&) { return canAssignFlag; })\n          .has_value());\n\n  canAssignFlag = true;\n  auto f1 = foomap.assign_if(1, 2, [canAssignFlag](auto&&) {\n    return canAssignFlag;\n  });\n  EXPECT_TRUE(f1.has_value());\n  EXPECT_EQ(2, f1.value()->second);\n\n  // Assign based on the current value.\n  auto f2 = foomap.assign_if(1, 3, [](auto&& val) { return val == 2; });\n  EXPECT_TRUE(f2.has_value());\n  EXPECT_EQ(3, f2.value()->second);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, InsertOrAssignIfTest) {\n  CHM<uint64_t, uint64_t> foomap(3);\n\n  bool canAssignFlag = false;\n  auto r = foomap.insert_or_assign_if(0, 0, [canAssignFlag](auto&&) {\n    return canAssignFlag;\n  });\n  EXPECT_TRUE(r.second);\n  EXPECT_EQ(0, r.first->second);\n  EXPECT_EQ(0, foomap.find(0)->second);\n\n  canAssignFlag = true;\n  r = foomap.insert_or_assign_if(1, 0, [canAssignFlag](auto&&) {\n    return canAssignFlag;\n  });\n  EXPECT_TRUE(r.second);\n  EXPECT_EQ(0, r.first->second);\n  EXPECT_EQ(0, foomap.find(1)->second);\n\n  // assign_if test equivalent\n  canAssignFlag = false;\n  r = foomap.insert_or_assign_if(1, 1, [canAssignFlag](auto&&) {\n    return canAssignFlag;\n  });\n  EXPECT_FALSE(r.second);\n  EXPECT_EQ(0, r.first->second);\n  EXPECT_EQ(0, foomap.find(1)->second);\n\n  canAssignFlag = true;\n  r = foomap.insert_or_assign_if(1, 2, [canAssignFlag](auto&&) {\n    return canAssignFlag;\n  });\n  EXPECT_TRUE(r.second);\n  EXPECT_EQ(2, r.first->second);\n  EXPECT_EQ(2, foomap.find(1)->second);\n\n  // Assign based on the current value.\n  r = foomap.insert_or_assign_if(1, 3, [](auto&& val) { return val == 2; });\n  EXPECT_TRUE(r.second);\n  EXPECT_EQ(3, r.first->second);\n  EXPECT_EQ(3, foomap.find(1)->second);\n\n  r = foomap.insert_or_assign_if(1, 4, [](auto&& val) { return val == 2; });\n  EXPECT_FALSE(r.second);\n  EXPECT_EQ(3, r.first->second);\n  EXPECT_EQ(3, foomap.find(1)->second);\n}\n\n// TODO: hazptrs must support DeterministicSchedule\n\n#define Atom std::atomic // DeterministicAtomic\n#define Mutex std::mutex // DeterministicMutex\n#define lib std // DeterministicSchedule\n#define join t.join() // DeterministicSchedule::join(t)\n// #define Atom DeterministicAtomic\n// #define Mutex DeterministicMutex\n// #define lib DeterministicSchedule\n// #define join DeterministicSchedule::join(t)\n\nTYPED_TEST_P(ConcurrentHashMapTest, UpdateStressTest) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n\n  // size must match iters for this test.\n  unsigned size = 128 * 128;\n  unsigned iters = size;\n  CHM<unsigned long,\n      unsigned long,\n      std::hash<unsigned long>,\n      std::equal_to<unsigned long>,\n      std::allocator<uint8_t>,\n      8,\n      Atom,\n      Mutex>\n      m(2);\n\n  for (uint32_t i = 0; i < size; i++) {\n    m.insert(i, i);\n  }\n  std::vector<std::thread> threads;\n  unsigned int num_threads = 32;\n  threads.reserve(num_threads);\n  for (uint32_t t = 0; t < num_threads; t++) {\n    threads.push_back(lib::thread([&, t]() {\n      int offset = (iters * t / num_threads);\n      for (uint32_t i = 0; i < iters / num_threads; i++) {\n        unsigned long k = folly::hash::jenkins_rev_mix32((i + offset));\n        k = k % (iters / num_threads) + offset;\n        unsigned long val = 3;\n        {\n          auto res = m.find(k);\n          EXPECT_NE(res, m.cend());\n          EXPECT_EQ(k, res->second);\n          auto r = m.assign(k, res->second);\n          EXPECT_TRUE(r);\n        }\n        {\n          auto res = m.find(k);\n          EXPECT_NE(res, m.cend());\n          EXPECT_EQ(k, res->second);\n        }\n        // Another random insertion to force table resizes\n        val = size + i + offset;\n        EXPECT_TRUE(m.insert(val, val).second);\n      }\n    }));\n  }\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, EraseStressTest) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n\n  unsigned size = 2;\n  unsigned iters = size * 128 * 2;\n  CHM<unsigned long,\n      unsigned long,\n      std::hash<unsigned long>,\n      std::equal_to<unsigned long>,\n      std::allocator<uint8_t>,\n      8,\n      Atom,\n      Mutex>\n      m(2);\n\n  for (uint32_t i = 0; i < size; i++) {\n    unsigned long k = folly::hash::jenkins_rev_mix32(i);\n    m.insert(k, k);\n  }\n  std::vector<std::thread> threads;\n  unsigned int num_threads = 32;\n  threads.reserve(num_threads);\n  for (uint32_t t = 0; t < num_threads; t++) {\n    threads.push_back(lib::thread([&, t]() {\n      int offset = (iters * t / num_threads);\n      for (uint32_t i = 0; i < iters / num_threads; i++) {\n        unsigned long k = folly::hash::jenkins_rev_mix32((i + offset));\n        auto res = m.insert(k, k).second;\n        if (res) {\n          if (i % 2 == 0) {\n            res = m.erase(k);\n          } else {\n            res = m.erase_if_equal(k, k);\n          }\n          if (!res) {\n            printf(\"Faulre to erase thread %i val %li\\n\", t, k);\n            exit(0);\n          }\n          EXPECT_TRUE(res);\n        }\n        res = m.insert(k, k).second;\n        if (res) {\n          res = bool(m.assign(k, k));\n          if (!res) {\n            printf(\"Thread %i update fail %li res%i\\n\", t, k, res);\n            exit(0);\n          }\n          EXPECT_TRUE(res);\n          auto result = m.find(k);\n          if (result == m.cend()) {\n            printf(\"Thread %i lookup fail %li\\n\", t, k);\n            exit(0);\n          }\n          EXPECT_EQ(k, result->second);\n        }\n      }\n    }));\n  }\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, TryEmplaceEraseStressTest) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n  std::atomic<int> iterations{10000};\n  std::vector<std::thread> threads;\n  unsigned int num_threads = 32;\n  threads.reserve(num_threads);\n  CHM<int, int> map;\n  for (uint32_t t = 0; t < num_threads; t++) {\n    threads.push_back(lib::thread([&]() {\n      while (--iterations >= 0) {\n        auto it = map.try_emplace(1, 101);\n        map.erase(1);\n        EXPECT_EQ(it.first->second, 101);\n      }\n    }));\n  }\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, InsertOrAssignStressTest) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n  std::atomic<int> iterations{10000};\n  std::vector<std::thread> threads;\n  unsigned int num_threads = 32;\n  threads.reserve(num_threads);\n  CHM<int, int> map;\n  for (uint32_t t = 0; t < num_threads; t++) {\n    threads.push_back(lib::thread([&]() {\n      int i = 0;\n      while (--iterations >= 0) {\n        auto res = map.insert_or_assign(0, ++i);\n        ASSERT_TRUE(res.second);\n        auto v = res.first->second;\n        ASSERT_EQ(v, i);\n      }\n    }));\n  }\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, IterateStressTest) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n\n  unsigned size = 2;\n  unsigned iters = size * 128 * 2;\n  CHM<unsigned long,\n      unsigned long,\n      std::hash<unsigned long>,\n      std::equal_to<unsigned long>,\n      std::allocator<uint8_t>,\n      8,\n      Atom,\n      Mutex>\n      m(2);\n\n  for (uint32_t i = 0; i < size; i++) {\n    unsigned long k = folly::hash::jenkins_rev_mix32(i);\n    m.insert(k, k);\n  }\n  for (uint32_t i = 0; i < 10; i++) {\n    m.insert(i, i);\n  }\n  std::vector<std::thread> threads;\n  unsigned int num_threads = 32;\n  for (uint32_t t = 0; t < num_threads; t++) {\n    threads.push_back(lib::thread([&, t]() {\n      int offset = (iters * t / num_threads);\n      for (uint32_t i = 0; i < iters / num_threads; i++) {\n        unsigned long k = folly::hash::jenkins_rev_mix32((i + offset));\n        auto res = m.insert(k, k).second;\n        if (res) {\n          if (i % 2 == 0) {\n            res = m.erase(k);\n          } else {\n            res = m.erase_if_equal(k, k);\n          }\n          EXPECT_TRUE(res);\n        }\n        int count = 0;\n        for (auto it = m.cbegin(); it != m.cend(); ++it) {\n          if (it->first < 10) {\n            count++;\n          }\n        }\n        EXPECT_EQ(count, 10);\n      }\n    }));\n  }\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, insertStressTest) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n\n  unsigned size = 2;\n  unsigned iters = size * 64 * 4;\n  CHM<unsigned long,\n      unsigned long,\n      std::hash<unsigned long>,\n      std::equal_to<unsigned long>,\n      std::allocator<uint8_t>,\n      8,\n      Atom,\n      Mutex>\n      m(2);\n\n  EXPECT_TRUE(m.insert(0, 0).second);\n  EXPECT_FALSE(m.insert(0, 0).second);\n  std::vector<std::thread> threads;\n  unsigned int num_threads = 32;\n  for (uint32_t t = 0; t < num_threads; t++) {\n    threads.push_back(lib::thread([&, t]() {\n      int offset = (iters * t / num_threads);\n      for (uint32_t i = 0; i < iters / num_threads; i++) {\n        auto var = offset + i + 1;\n        EXPECT_TRUE(m.insert(var, var).second);\n        EXPECT_FALSE(m.insert(0, 0).second);\n      }\n    }));\n  }\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, assignStressTest) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n\n  unsigned size = 2;\n  unsigned iters = size * 64 * 4;\n  struct big_value {\n    uint64_t v1;\n    uint64_t v2;\n    uint64_t v3;\n    uint64_t v4;\n    uint64_t v5;\n    uint64_t v6;\n    uint64_t v7;\n    uint64_t v8;\n    void set(uint64_t v) { v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v; }\n    void check() const {\n      auto v = v1;\n      EXPECT_EQ(v, v8);\n      EXPECT_EQ(v, v7);\n      EXPECT_EQ(v, v6);\n      EXPECT_EQ(v, v5);\n      EXPECT_EQ(v, v4);\n      EXPECT_EQ(v, v3);\n      EXPECT_EQ(v, v2);\n    }\n  };\n  CHM<unsigned long,\n      big_value,\n      std::hash<unsigned long>,\n      std::equal_to<unsigned long>,\n      std::allocator<uint8_t>,\n      8,\n      Atom,\n      Mutex>\n      m(2);\n\n  for (uint32_t i = 0; i < iters; i++) {\n    big_value a;\n    a.set(i);\n    m.insert(i, a);\n  }\n\n  std::vector<std::thread> threads;\n  unsigned int num_threads = 32;\n  for (uint32_t t = 0; t < num_threads; t++) {\n    threads.push_back(lib::thread([&]() {\n      for (uint32_t i = 0; i < iters; i++) {\n        auto res = m.find(i);\n        EXPECT_NE(res, m.cend());\n        res->second.check();\n        big_value b;\n        b.set(res->second.v1 + 1);\n        m.assign(i, b);\n      }\n    }));\n  }\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, RefcountTest) {\n  struct badhash {\n    size_t operator()(uint64_t) const { return 0; }\n  };\n  CHM<uint64_t,\n      uint64_t,\n      badhash,\n      std::equal_to<uint64_t>,\n      std::allocator<uint8_t>,\n      0>\n      foomap(3);\n  foomap.insert(0, 0);\n  foomap.insert(1, 1);\n  foomap.insert(2, 2);\n  for (int32_t i = 0; i < 300; ++i) {\n    foomap.insert_or_assign(1, i);\n  }\n}\n\nstruct Wrapper {\n  explicit Wrapper(bool& del_) : del(del_) {}\n  ~Wrapper() { del = true; }\n\n  bool& del;\n};\n\nTYPED_TEST_P(ConcurrentHashMapTest, Deletion) {\n  bool del{false};\n\n  {\n    CHM<int, std::shared_ptr<Wrapper>> map;\n\n    map.insert(0, std::make_shared<Wrapper>(del));\n  }\n\n  folly::hazptr_cleanup();\n\n  EXPECT_TRUE(del);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, DeletionWithErase) {\n  bool del{false};\n\n  {\n    CHM<int, std::shared_ptr<Wrapper>> map;\n\n    map.insert(0, std::make_shared<Wrapper>(del));\n    map.erase(0);\n  }\n\n  folly::hazptr_cleanup();\n\n  EXPECT_TRUE(del);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, DeletionWithIterator) {\n  bool del{false};\n\n  {\n    CHM<int, std::shared_ptr<Wrapper>> map;\n\n    map.insert(0, std::make_shared<Wrapper>(del));\n    auto it = map.find(0);\n    map.erase(it);\n  }\n\n  folly::hazptr_cleanup();\n\n  EXPECT_TRUE(del);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, DeletionWithForLoop) {\n  bool del{false};\n\n  {\n    CHM<int, std::shared_ptr<Wrapper>> map;\n\n    map.insert(0, std::make_shared<Wrapper>(del));\n    for (auto it = map.cbegin(); it != map.cend(); ++it) {\n      EXPECT_EQ(it->first, 0);\n    }\n  }\n\n  folly::hazptr_cleanup();\n\n  EXPECT_TRUE(del);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, DeletionMultiple) {\n  bool del1{false}, del2{false};\n\n  {\n    CHM<int, std::shared_ptr<Wrapper>> map;\n\n    map.insert(0, std::make_shared<Wrapper>(del1));\n    map.insert(1, std::make_shared<Wrapper>(del2));\n  }\n\n  folly::hazptr_cleanup();\n\n  EXPECT_TRUE(del1);\n  EXPECT_TRUE(del2);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, DeletionAssigned) {\n  bool del1{false}, del2{false};\n\n  {\n    CHM<int, std::shared_ptr<Wrapper>> map;\n\n    map.insert(0, std::make_shared<Wrapper>(del1));\n    map.insert_or_assign(0, std::make_shared<Wrapper>(del2));\n  }\n\n  folly::hazptr_cleanup();\n\n  EXPECT_TRUE(del1);\n  EXPECT_TRUE(del2);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, DeletionMultipleMaps) {\n  bool del1{false}, del2{false};\n\n  {\n    CHM<int, std::shared_ptr<Wrapper>> map1;\n    CHM<int, std::shared_ptr<Wrapper>> map2;\n\n    map1.insert(0, std::make_shared<Wrapper>(del1));\n    map2.insert(0, std::make_shared<Wrapper>(del2));\n  }\n\n  folly::hazptr_cleanup();\n\n  EXPECT_TRUE(del1);\n  EXPECT_TRUE(del2);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ForEachLoop) {\n  CHM<int, int> map;\n  map.insert(1, 2);\n  size_t iters = 0;\n  for (const auto& kv : map) {\n    EXPECT_EQ(kv.first, 1);\n    EXPECT_EQ(kv.second, 2);\n    ++iters;\n  }\n  EXPECT_EQ(iters, 1);\n}\n\ntemplate <typename T>\nstruct FooBase {\n  typename T::ConstIterator it;\n  explicit FooBase(typename T::ConstIterator&& it_) : it(std::move(it_)) {}\n  FooBase(FooBase&&) = default;\n  FooBase& operator=(FooBase&&) = default;\n};\n\nTYPED_TEST_P(ConcurrentHashMapTest, IteratorMove) {\n  using Foo = FooBase<CHM<int, int>>;\n  CHM<int, int> map;\n  int k = 111;\n  int v = 999999;\n  map.insert(k, v);\n  Foo foo(map.find(k));\n  ASSERT_EQ(foo.it->second, v);\n  Foo foo2(map.find(0));\n  foo2 = std::move(foo);\n  ASSERT_EQ(foo2.it->second, v);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, IteratorLoop) {\n  CHM<std::string, int> map;\n  static constexpr size_t kNum = 4000;\n  for (size_t i = 0; i < kNum; ++i) {\n    map.insert(to<std::string>(i), i);\n  }\n\n  size_t count = 0;\n  for (auto it = map.begin(); it != map.end(); ++it) {\n    ASSERT_LT(count, kNum);\n    ++count;\n  }\n\n  EXPECT_EQ(count, kNum);\n}\n\nnamespace {\ntemplate <typename T, typename Arg>\nusing detector_find = decltype(std::declval<T>().find(std::declval<Arg>()));\n\ntemplate <typename T, typename Arg>\nusing detector_erase = decltype(std::declval<T>().erase(std::declval<Arg>()));\n} // namespace\n\nTYPED_TEST_P(ConcurrentHashMapTest, HeterogeneousLookup) {\n  using Hasher = folly::transparent<folly::hasher<folly::StringPiece>>;\n  using KeyEqual = folly::transparent<std::equal_to<folly::StringPiece>>;\n  using M = CHM<std::string, bool, Hasher, KeyEqual>;\n\n  constexpr auto hello = \"hello\"_sp;\n  constexpr auto buddy = \"buddy\"_sp;\n  constexpr auto world = \"world\"_sp;\n\n  M map;\n  map.emplace(hello, true);\n  map.emplace(world, false);\n\n  auto checks = [hello, buddy](auto& ref) {\n    // find\n    EXPECT_TRUE(ref.end() == ref.find(buddy));\n    EXPECT_EQ(hello, ref.find(hello)->first);\n\n    // at\n    EXPECT_TRUE(ref.at(hello));\n    EXPECT_THROW(ref.at(buddy), std::out_of_range);\n\n    // invocability checks\n    static_assert(\n        !is_detected_v<detector_find, decltype(ref), int>,\n        \"there shouldn't be a find() overload for this string map with an int param\");\n  };\n\n  checks(map);\n  checks(std::as_const(map));\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, HeterogeneousInsert) {\n  using Hasher = folly::transparent<folly::hasher<folly::StringPiece>>;\n  using KeyEqual = folly::transparent<std::equal_to<folly::StringPiece>>;\n  using P = std::pair<StringPiece, std::string>;\n  using CP = std::pair<const StringPiece, std::string>;\n\n  CHM<std::string, std::string, Hasher, KeyEqual> map;\n  P p{\"foo\", \"hello\"};\n  StringPiece foo{\"foo\"};\n  StringPiece bar{\"bar\"};\n\n  map.insert(\"foo\", \"hello\");\n  map.insert(foo, \"hello\");\n  // TODO(T31574848): the list-initialization below does not work on libstdc++\n  // versions (e.g., GCC < 6) with no implementation of N4387 (\"perfect\n  // initialization\" for pairs and tuples).\n  //   StringPiece sp{\"foo\"};\n  //   map.insert({sp, \"hello\"});\n  map.insert({\"foo\", \"hello\"});\n  map.insert(P(\"foo\", \"hello\"));\n  map.insert(CP(\"foo\", \"hello\"));\n  map.insert(std::move(p));\n  map.insert_or_assign(\"foo\", \"hello\");\n  map.insert_or_assign(StringPiece{\"foo\"}, \"hello\");\n\n  map.erase(StringPiece{\"foo\"});\n  map.erase(foo);\n  map.erase(\"\");\n  EXPECT_TRUE(map.empty());\n\n  map.insert(\"foo\", \"hello\");\n  map.insert(\"bar\", \"world\");\n  map.erase_if_equal(StringPiece{\"foo\"}, \"hello\");\n  map.erase_key_if(bar, [](const std::string& s) { return s == \"world\"; });\n  map.erase(\"\");\n  EXPECT_TRUE(map.empty());\n\n  map.insert(\"foo\", \"baz\");\n  EXPECT_TRUE(map.assign(foo, \"hello2\"));\n  auto mbIt = map.assign_if_equal(\"foo\", \"hello2\", \"hello\");\n  EXPECT_TRUE(mbIt);\n  EXPECT_EQ(mbIt.value()->second, \"hello\");\n  EXPECT_EQ(map[foo], \"hello\");\n  auto mbIt2 = map.assign_if(\"foo\", \"hello2\", [](auto&& val) {\n    return val == \"hello\";\n  });\n  EXPECT_TRUE(mbIt);\n  EXPECT_EQ(mbIt2.value()->second, \"hello2\");\n  EXPECT_EQ(map[foo], \"hello2\");\n  auto it = map.find(foo);\n  map.erase(it);\n  EXPECT_TRUE(map.empty());\n\n  map.try_emplace(foo);\n  map.try_emplace(foo, \"hello\");\n  map.try_emplace(StringPiece{\"foo\"}, \"hello\");\n  map.try_emplace(foo, \"hello\");\n  map.try_emplace(foo);\n  map.try_emplace(\"foo\");\n  map.try_emplace(\"foo\", \"hello\");\n  map.try_emplace(\"bar\", /* count */ 20, 'x');\n  EXPECT_EQ(map[bar], std::string(20, 'x'));\n\n  map.emplace(StringPiece{\"foo\"}, \"hello\");\n  map.emplace(\"foo\", \"hello\");\n\n  // invocability checks\n  static_assert(\n      !is_detected_v<detector_erase, decltype(map), int>,\n      \"there shouldn't be an erase() overload for this string map with an int param\");\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, InsertOrAssignIterator) {\n  CHM<int, int> map;\n  auto [itr1, insert1] = map.insert_or_assign(1, 1);\n  auto [itr2, insert2] = map.insert_or_assign(1, 2);\n  auto itr3 = map.find(1);\n  EXPECT_EQ(itr3->second, 2);\n  EXPECT_EQ(itr2->second, 2);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, EraseClonedNonCopyable) {\n  // Using a non-copyable value type to use the node structure with an\n  // extra level of indirection to key-value items.\n  using Value = std::unique_ptr<int>;\n  // [TODO] Fix the SIMD version to pass this test, then change the\n  // map type to CHM.\n  ConcurrentHashMap<int, Value> map;\n  int cloned = 32; // The item that will end up being cloned.\n  for (int i = 0; i < cloned; i++) {\n    map.try_emplace(256 * i, std::make_unique<int>(0));\n  }\n  auto [iter, _] = map.try_emplace(256 * cloned, std::make_unique<int>(0));\n  // Add more items to cause rehash that clones the item.\n  int num = 10000;\n  for (int i = cloned + 1; i < num; i++) {\n    map.try_emplace(256 * i, std::make_unique<int>(0));\n  }\n  // Erase items to invoke hazard pointer asynchronous reclamation.\n  for (int i = 0; i < num; i++) {\n    map.erase(256 * i);\n  }\n  // The cloned node and the associated key-value item should still be\n  // protected by iter from being reclaimed.\n  EXPECT_EQ(iter->first, 256 * cloned);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ConcurrentInsertClear) {\n  DeterministicSchedule sched(DeterministicSchedule::uniform(FLAGS_seed));\n\n  /* 8192 and 8x / 9x multipliers are values that tend to reproduce\n   * race condition (fixed by this change) more frequently.\n   * Test keeps CHM size limited to chm_max_size and clear() if it exceeds.\n   * Key space for CHM is limited to chm_max_key and exceeds max size by\n   * small margin.\n   * Test imitates serial traversal over key space by multiple threads,\n   * triggering clear() by multiple threads at once.\n   */\n  constexpr unsigned long chm_base_size = 8192;\n  constexpr unsigned long chm_max_size = chm_base_size * 8;\n  constexpr unsigned long chm_max_key = chm_base_size * 9;\n  CHM<unsigned long,\n      unsigned long,\n      std::hash<unsigned long>,\n      std::equal_to<unsigned long>,\n      std::allocator<uint8_t>,\n      8,\n      Atom,\n      Mutex>\n      m(chm_base_size);\n\n  std::vector<std::thread> threads;\n  /* 32 threads and 50k rounds are a compromise between trying to create\n   * race conditions in insert()/clear(), and finishing test in reasonable\n   * time */\n  constexpr int load_divisor = folly::kIsSanitizeThread ? 4 : 1;\n  constexpr size_t num_threads = 32 / load_divisor;\n  constexpr size_t rounds_per_thread = 50000 / load_divisor;\n  threads.reserve(num_threads);\n  for (size_t t = 0; t < num_threads; t++) {\n    threads.emplace_back([&, t]() {\n      for (size_t i = 0; i < rounds_per_thread; ++i) {\n        /* Code simulates traversal over whole key space by all threads\n         * combined, with each thread contributing to semi-unique set of keys.\n         * Each thread is assigned its own key sequence,\n         * with thread_X traversing values X, 32+X, 32*2+X, 32*3+X, ...\n         * 32*N+X mod chm_max_key.\n         */\n        const unsigned long k = (i * num_threads + t) % chm_max_key;\n        if (m.size() >= chm_max_size) {\n          m.clear();\n        }\n        m.insert_or_assign(k, k);\n      }\n    });\n  }\n\n  for (auto& t : threads) {\n    join;\n  }\n}\n\nstruct Wrong : std::exception {\n  char const* what() const noexcept { return \"wrong!\"; }\n};\nstruct ValueMaybeThrow {\n  int value = 0;\n  explicit ValueMaybeThrow(int i) : value{i} {\n    if (!i) {\n      throw Wrong();\n    }\n  }\n};\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowInsertSeparate) {\n  CHM<int, ValueMaybeThrow> map;\n  EXPECT_THROW(map.insert(0, 0), Wrong);\n  EXPECT_FALSE(get_ptr(map, 0));\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowTryEmplaceFail) {\n  CHM<int, ValueMaybeThrow> map;\n  map.try_emplace(0, 1);\n  map.try_emplace(0, 0);\n  EXPECT_TRUE(get_ptr(map, 0));\n  EXPECT_EQ(1, get_ptr(map, 0)->value);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowTryEmplaceSucc) {\n  CHM<int, ValueMaybeThrow> map;\n  EXPECT_THROW(map.try_emplace(0, 0), Wrong);\n  EXPECT_FALSE(get_ptr(map, 0));\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowEmplace) {\n  CHM<int, ValueMaybeThrow> map;\n  EXPECT_THROW(map.emplace(0, 0), Wrong);\n  EXPECT_FALSE(get_ptr(map, 0));\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowInsertOrAssignInsert) {\n  CHM<int, ValueMaybeThrow> map;\n  EXPECT_THROW(map.insert_or_assign(0, 0), Wrong);\n  EXPECT_FALSE(get_ptr(map, 0));\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowInsertOrAssignAssign) {\n  CHM<int, ValueMaybeThrow> map;\n  map.emplace(0, 1);\n  EXPECT_THROW(map.insert_or_assign(0, 0), Wrong);\n  ASSERT_TRUE(get_ptr(map, 0));\n  EXPECT_EQ(1, get_ptr(map, 0)->value);\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowAssignAbsent) {\n  CHM<int, ValueMaybeThrow> map;\n  map.assign(0, 0);\n  EXPECT_FALSE(get_ptr(map, 0));\n}\n\nTYPED_TEST_P(ConcurrentHashMapTest, ValueMaybeThrowAssignPresent) {\n  CHM<int, ValueMaybeThrow> map;\n  map.emplace(0, 1);\n  EXPECT_THROW(map.assign(0, 0), Wrong);\n  ASSERT_TRUE(get_ptr(map, 0));\n  EXPECT_EQ(1, get_ptr(map, 0)->value);\n}\n\nREGISTER_TYPED_TEST_SUITE_P(\n    ConcurrentHashMapTest,\n    MapTest,\n    MaxSizeTest,\n    MoveTest,\n    EmplaceTest,\n    MapResizeTest,\n    ReserveTest,\n    MapNoCopiesTest,\n    MapMovableKeysTest,\n    MapUpdateTest,\n    MapIterateTest2,\n    MapIterateTest,\n    MoveIterateAssignIterate,\n    MapInsertIteratorValueTest,\n    CopyIterator,\n    Deletion,\n    DeletionAssigned,\n    DeletionMultiple,\n    DeletionMultipleMaps,\n    DeletionWithErase,\n    DeletionWithForLoop,\n    DeletionWithIterator,\n    EraseIfEqualTest,\n    EraseIfTest,\n    EraseInIterateTest,\n    AssignIfTest,\n    InsertOrAssignIfTest,\n    EraseStressTest,\n    EraseTest,\n    ForEachLoop,\n    TryEmplaceEraseStressTest,\n    InsertOrAssignStressTest,\n    IterateStressTest,\n    RefcountTest,\n    UpdateStressTest,\n    assignStressTest,\n    insertStressTest,\n    IteratorMove,\n    IteratorLoop,\n    HeterogeneousLookup,\n    HeterogeneousInsert,\n    InsertOrAssignIterator,\n    EraseClonedNonCopyable,\n    ConcurrentInsertClear,\n    ValueMaybeThrowInsertSeparate,\n    ValueMaybeThrowTryEmplaceFail,\n    ValueMaybeThrowTryEmplaceSucc,\n    ValueMaybeThrowEmplace,\n    ValueMaybeThrowInsertOrAssignInsert,\n    ValueMaybeThrowInsertOrAssignAssign,\n    ValueMaybeThrowAssignAbsent,\n    ValueMaybeThrowAssignPresent);\n\nusing folly::detail::concurrenthashmap::bucket::BucketTable;\n\n#if (                                                        \\\n    FOLLY_SSE_PREREQ(4, 2) ||                                \\\n    (FOLLY_AARCH64 && FOLLY_F14_CRC_INTRINSIC_AVAILABLE)) && \\\n    FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nusing folly::detail::concurrenthashmap::simd::SIMDTable;\nusing MapFactoryTypes =\n    ::testing::Types<MapFactory<BucketTable>, MapFactory<SIMDTable>>;\n#else\nusing MapFactoryTypes = ::testing::Types<MapFactory<BucketTable>>;\n#endif\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    MapFactoryTypesInstantiation, ConcurrentHashMapTest, MapFactoryTypes);\n"
  },
  {
    "path": "folly/concurrency/test/CoreCachedSharedPtrTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// AtomicSharedPtr-detail.h only works with libstdc++, so skip these tests for\n// other vendors\n// PackedSyncPtr requires x64, ppc64 or aarch64, skip these tests for\n// other arches\n#include <folly/Portability.h>\n#include <folly/portability/Config.h>\n#if defined(__GLIBCXX__) && (FOLLY_X64 || FOLLY_PPC64 || FOLLY_AARCH64)\n\n#include <atomic>\n#include <memory>\n#include <thread>\n#include <vector>\n\n#include <folly/Benchmark.h>\n#include <folly/concurrency/AtomicSharedPtr.h>\n#include <folly/concurrency/CoreCachedSharedPtr.h>\n#include <folly/concurrency/ThreadCachedSynchronized.h>\n#include <folly/concurrency/memory/ReadMostlySharedPtr.h>\n#include <folly/portability/GTest.h>\n#include <folly/system/HardwareConcurrency.h>\n\nnamespace {\n\ntemplate <class Operation>\nvoid parallelRun(\n    Operation op, size_t numThreads = folly::available_concurrency()) {\n  std::vector<std::thread> threads;\n  for (size_t t = 0; t < numThreads; ++t) {\n    threads.emplace_back([&, t] { op(t); });\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\n} // namespace\n\nTEST(CoreCachedSharedPtr, Basic) {\n  auto p = std::make_shared<int>(1);\n  std::weak_ptr<int> wp(p);\n\n  folly::CoreCachedSharedPtr<int> cached(p);\n  folly::CoreCachedWeakPtr<int> wcached(cached);\n\n  std::shared_ptr<int> p2 = cached.get();\n  std::weak_ptr<int> wp2 = wcached.get();\n  ASSERT_TRUE(p2 != nullptr);\n  ASSERT_EQ(*p2, 1);\n  ASSERT_FALSE(wp2.expired());\n\n  // Check that other cores get the correct shared_ptr too.\n  parallelRun([&](size_t) {\n    EXPECT_TRUE(cached.get().get() == p.get());\n    EXPECT_EQ(*cached.get(), 1);\n    EXPECT_EQ(*wcached.lock(), 1);\n  });\n\n  p.reset();\n  cached.reset();\n  // p2 should survive.\n  ASSERT_FALSE(wp.expired());\n  // Here we don't know anything about wp2: could be expired even if\n  // there is a living reference to the main object.\n\n  p2.reset();\n  ASSERT_TRUE(wp.expired());\n  ASSERT_TRUE(wp2.expired());\n}\n\nTEST(CoreCachedSharedPtr, AtomicCoreCachedSharedPtr) {\n  constexpr size_t kIters = 2000;\n  {\n    folly::AtomicCoreCachedSharedPtr<size_t> p;\n    parallelRun([&](size_t) {\n      for (size_t i = 0; i < kIters; ++i) {\n        p.reset(std::make_shared<size_t>(i));\n        EXPECT_TRUE(p.get());\n        // Just read the value, and ensure that ASAN/TSAN do not complain.\n        EXPECT_GE(*p.get(), 0);\n      }\n    });\n  }\n  {\n    // One writer thread, all other readers, verify consistency.\n    std::atomic<size_t> largestValueObserved{0};\n    folly::AtomicCoreCachedSharedPtr<size_t> p{std::make_shared<size_t>(0)};\n    parallelRun([&](size_t t) {\n      if (t == 0) {\n        for (size_t i = 0; i < kIters; ++i) {\n          p.reset(std::make_shared<size_t>(i + 1));\n        }\n      } else {\n        while (true) {\n          auto exp = largestValueObserved.load();\n          auto value = *p.get();\n          EXPECT_GE(value, exp);\n          // Maintain the maximum value observed so far. As soon as one thread\n          // observes an update, they all should observe it.\n          while (value > exp &&\n                 !largestValueObserved.compare_exchange_weak(exp, value)) {\n          }\n          if (exp == kIters) {\n            break;\n          }\n        }\n      }\n    });\n  }\n}\n\nnamespace {\n\ntemplate <class Holder>\nvoid testAliasingCornerCases() {\n  {\n    Holder h;\n    auto p1 = std::make_shared<int>(1);\n    std::weak_ptr<int> w1 = p1;\n    // Aliasing constructor, p2 is nullptr but still manages the object in p1.\n    std::shared_ptr<int> p2(p1, nullptr);\n    // And now it's the only reference left.\n    p1.reset();\n    EXPECT_FALSE(w1.expired());\n\n    // Pass the ownership to the Holder.\n    h.reset(p2);\n    p2.reset();\n\n    // Object should still be alive.\n    EXPECT_FALSE(w1.expired());\n\n    // And resetting the holder will destroy the managed object.\n    h.reset();\n    // Except in the case of AtomicCoreCachedSharedPtr, where the object is only\n    // scheduled for reclamation, so we have to wait for a hazptr scan.\n    folly::hazptr_cleanup();\n    EXPECT_TRUE(w1.expired());\n  }\n\n  {\n    Holder h;\n    int x = 1;\n    // p points to x, but has no managed object.\n    std::shared_ptr<int> p(std::shared_ptr<int>{}, &x);\n    h.reset(p);\n    EXPECT_TRUE(h.get().get() == &x);\n  }\n}\n\n} // namespace\n\nTEST(CoreCachedSharedPtr, AliasingCornerCases) {\n  testAliasingCornerCases<folly::CoreCachedSharedPtr<int>>();\n}\n\nTEST(CoreCachedSharedPtr, AliasingCornerCasesAtomic) {\n  testAliasingCornerCases<folly::AtomicCoreCachedSharedPtr<int>>();\n}\n\nnamespace {\n\ntemplate <class Operation>\nsize_t benchmarkParallelRun(Operation op, size_t numThreads) {\n  constexpr size_t kIters = 2 * 1000 * 1000;\n\n  std::vector<std::thread> threads;\n\n  // Prevent the compiler from hoisting code out of the loop.\n  auto opNoinline = [&]() FOLLY_NOINLINE { op(); };\n\n  std::atomic<size_t> ready = 0;\n  std::atomic<bool> done = false;\n  std::atomic<size_t> iters = 0;\n\n  for (size_t t = 0; t < numThreads; ++t) {\n    threads.emplace_back([&] {\n      ++ready;\n      while (ready.load() < numThreads) {\n      }\n      size_t i = 0;\n      // Interrupt all workers as soon as the first one is done, so the overall\n      // wall time is close to the time spent in each thread.\n      for (; !done.load(std::memory_order_acquire) && i < kIters; ++i) {\n        opNoinline();\n      }\n      done = true;\n      iters += i;\n    });\n  }\n\n  for (auto& t : threads) {\n    t.join();\n  }\n\n  return iters / numThreads;\n}\n\nsize_t benchmarkSharedPtrAcquire(size_t numThreads) {\n  auto p = std::make_shared<int>(1);\n  return benchmarkParallelRun([&] { return p; }, numThreads);\n}\n\nsize_t benchmarkWeakPtrLock(size_t numThreads) {\n  auto p = std::make_shared<int>(1);\n  std::weak_ptr<int> wp = p;\n  return benchmarkParallelRun([&] { return wp.lock(); }, numThreads);\n}\n\nsize_t benchmarkAtomicSharedPtrAcquire(size_t numThreads) {\n  auto s = std::make_shared<int>(1);\n  folly::atomic_shared_ptr<int> p;\n  p.store(s);\n  return benchmarkParallelRun([&] { return p.load(); }, numThreads);\n}\n\nsize_t benchmarkCoreCachedSharedPtrAcquire(size_t numThreads) {\n  folly::CoreCachedSharedPtr<int> p(std::make_shared<int>(1));\n  return benchmarkParallelRun([&] { return p.get(); }, numThreads);\n}\n\nsize_t benchmarkCoreCachedWeakPtrLock(size_t numThreads) {\n  folly::CoreCachedSharedPtr<int> p(std::make_shared<int>(1));\n  folly::CoreCachedWeakPtr<int> wp(p);\n  return benchmarkParallelRun([&] { return wp.lock(); }, numThreads);\n}\n\nsize_t benchmarkAtomicCoreCachedSharedPtrAcquire(size_t numThreads) {\n  folly::AtomicCoreCachedSharedPtr<int> p(std::make_shared<int>(1));\n  return benchmarkParallelRun([&] { return p.get(); }, numThreads);\n}\n\nsize_t benchmarkReadMostlySharedPtrAcquire(size_t numThreads) {\n  folly::ReadMostlyMainPtr<int> p{std::make_shared<int>(1)};\n  return benchmarkParallelRun([&] { return p.getShared(); }, numThreads);\n}\n\nsize_t benchmarkReadMostlyWeakPtrLock(size_t numThreads) {\n  folly::ReadMostlyMainPtr<int> p{std::make_shared<int>(1)};\n  folly::ReadMostlyWeakPtr<int> w{p};\n  return benchmarkParallelRun([&] { return w.lock(); }, numThreads);\n}\n\nsize_t benchmarkThreadCachedSynchronizedAcquire(size_t numThreads) {\n  folly::thread_cached_synchronized<std::shared_ptr<int>> p{\n      std::make_shared<int>(1)};\n  return benchmarkParallelRun([&] { return *p; }, numThreads);\n}\n\n} // namespace\n\n#define BENCHMARK_THREADS(THREADS)                                       \\\n  BENCHMARK_MULTI(SharedPtrAcquire_##THREADS##Threads) {                 \\\n    return benchmarkSharedPtrAcquire(THREADS);                           \\\n  }                                                                      \\\n  BENCHMARK_MULTI(WeakPtrLock_##THREADS##Threads) {                      \\\n    return benchmarkWeakPtrLock(THREADS);                                \\\n  }                                                                      \\\n  BENCHMARK_MULTI(AtomicSharedPtrAcquire_##THREADS##Threads) {           \\\n    return benchmarkAtomicSharedPtrAcquire(THREADS);                     \\\n  }                                                                      \\\n  BENCHMARK_MULTI(ThreadCachedSynchronizedAcquire_##THREADS##Threads) {  \\\n    return benchmarkThreadCachedSynchronizedAcquire(THREADS);            \\\n  }                                                                      \\\n  BENCHMARK_MULTI(CoreCachedSharedPtrAcquire_##THREADS##Threads) {       \\\n    return benchmarkCoreCachedSharedPtrAcquire(THREADS);                 \\\n  }                                                                      \\\n  BENCHMARK_MULTI(CoreCachedWeakPtrLock_##THREADS##Threads) {            \\\n    return benchmarkCoreCachedWeakPtrLock(THREADS);                      \\\n  }                                                                      \\\n  BENCHMARK_MULTI(AtomicCoreCachedSharedPtrAcquire_##THREADS##Threads) { \\\n    return benchmarkAtomicCoreCachedSharedPtrAcquire(THREADS);           \\\n  }                                                                      \\\n  BENCHMARK_MULTI(ReadMostlySharedPtrAcquire_##THREADS##Threads) {       \\\n    return benchmarkReadMostlySharedPtrAcquire(THREADS);                 \\\n  }                                                                      \\\n  BENCHMARK_MULTI(ReadMostlyWeakPtrLock_##THREADS##Threads) {            \\\n    return benchmarkReadMostlyWeakPtrLock(THREADS);                      \\\n  }                                                                      \\\n  BENCHMARK_DRAW_LINE();\n\nBENCHMARK_THREADS(1)\nBENCHMARK_THREADS(4)\nBENCHMARK_THREADS(8)\nBENCHMARK_THREADS(16)\nBENCHMARK_THREADS(32)\nBENCHMARK_THREADS(64)\n\nBENCHMARK_MULTI(SharedPtrSingleThreadReset) {\n  auto p = std::make_shared<int>(1);\n  return benchmarkParallelRun([&] { p = std::make_shared<int>(1); }, 1);\n}\nBENCHMARK_MULTI(AtomicSharedPtrSingleThreadReset) {\n  auto s = std::make_shared<int>(1);\n  folly::atomic_shared_ptr<int> p;\n  p.store(s);\n  return benchmarkParallelRun([&] { p.store(std::make_shared<int>(1)); }, 1);\n}\nBENCHMARK_MULTI(CoreCachedSharedPtrSingleThreadReset) {\n  folly::CoreCachedSharedPtr<int> p(std::make_shared<int>(1));\n  return benchmarkParallelRun([&] { p.reset(std::make_shared<int>(1)); }, 1);\n}\nBENCHMARK_MULTI(AtomicCoreCachedSharedPtrSingleThreadReset) {\n  folly::AtomicCoreCachedSharedPtr<int> p(std::make_shared<int>(1));\n  return benchmarkParallelRun([&] { p.reset(std::make_shared<int>(1)); }, 1);\n}\n\nint main(int argc, char** argv) {\n  testing::InitGoogleTest(&argc, argv);\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n\n  auto ret = RUN_ALL_TESTS();\n  if (ret == 0 && FLAGS_benchmark) {\n    folly::runBenchmarks();\n  }\n\n  return ret;\n}\n\n#endif // defined(__GLIBCXX__)\n\n#if 0\n// On Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz, 2 sockets, 40 cores, 80 threads.\n$ buck-out/gen/folly/concurrency/test/core_cached_shared_ptr_test --benchmark --bm_slice_usec 500000\n\n============================================================================\n[...]ency/test/CoreCachedSharedPtrTest.cpp     relative  time/iter   iters/s\n============================================================================\nSharedPtrAcquire_1Threads                                  19.86ns    50.36M\nWeakPtrLock_1Threads                                       21.23ns    47.10M\nAtomicSharedPtrAcquire_1Threads                            30.92ns    32.34M\nThreadCachedSynchronizedAcquire_1Threads                   21.61ns    46.27M\nCoreCachedSharedPtrAcquire_1Threads                        21.87ns    45.72M\nCoreCachedWeakPtrLock_1Threads                             22.09ns    45.27M\nAtomicCoreCachedSharedPtrAcquire_1Threads                  24.47ns    40.87M\nReadMostlySharedPtrAcquire_1Threads                        15.49ns    64.57M\nReadMostlyWeakPtrLock_1Threads                             15.32ns    65.27M\n----------------------------------------------------------------------------\nSharedPtrAcquire_4Threads                                 255.09ns     3.92M\nWeakPtrLock_4Threads                                      287.98ns     3.47M\nAtomicSharedPtrAcquire_4Threads                           709.64ns     1.41M\nThreadCachedSynchronizedAcquire_4Threads                  228.03ns     4.39M\nCoreCachedSharedPtrAcquire_4Threads                        22.68ns    44.09M\nCoreCachedWeakPtrLock_4Threads                             23.62ns    42.34M\nAtomicCoreCachedSharedPtrAcquire_4Threads                  24.35ns    41.07M\nReadMostlySharedPtrAcquire_4Threads                        16.86ns    59.31M\nReadMostlyWeakPtrLock_4Threads                             16.25ns    61.55M\n----------------------------------------------------------------------------\nSharedPtrAcquire_8Threads                                 488.90ns     2.05M\nWeakPtrLock_8Threads                                      577.98ns     1.73M\nAtomicSharedPtrAcquire_8Threads                             1.78us   561.16K\nThreadCachedSynchronizedAcquire_8Threads                  483.47ns     2.07M\nCoreCachedSharedPtrAcquire_8Threads                        23.45ns    42.64M\nCoreCachedWeakPtrLock_8Threads                             23.81ns    41.99M\nAtomicCoreCachedSharedPtrAcquire_8Threads                  24.49ns    40.83M\nReadMostlySharedPtrAcquire_8Threads                        17.10ns    58.49M\nReadMostlyWeakPtrLock_8Threads                             16.73ns    59.76M\n----------------------------------------------------------------------------\nSharedPtrAcquire_16Threads                                  1.08us   929.56K\nWeakPtrLock_16Threads                                       1.56us   642.75K\nAtomicSharedPtrAcquire_16Threads                            2.94us   340.19K\nThreadCachedSynchronizedAcquire_16Threads                   1.02us   981.43K\nCoreCachedSharedPtrAcquire_16Threads                       25.58ns    39.10M\nCoreCachedWeakPtrLock_16Threads                            24.79ns    40.34M\nAtomicCoreCachedSharedPtrAcquire_16Threads                 25.97ns    38.50M\nReadMostlySharedPtrAcquire_16Threads                       18.11ns    55.20M\nReadMostlyWeakPtrLock_16Threads                            17.91ns    55.85M\n----------------------------------------------------------------------------\nSharedPtrAcquire_32Threads                                  2.25us   444.85K\nWeakPtrLock_32Threads                                       3.29us   304.08K\nAtomicSharedPtrAcquire_32Threads                            6.86us   145.76K\nThreadCachedSynchronizedAcquire_32Threads                   2.30us   434.67K\nCoreCachedSharedPtrAcquire_32Threads                       25.35ns    39.45M\nCoreCachedWeakPtrLock_32Threads                            25.37ns    39.41M\nAtomicCoreCachedSharedPtrAcquire_32Threads                 26.69ns    37.46M\nReadMostlySharedPtrAcquire_32Threads                       19.32ns    51.75M\nReadMostlyWeakPtrLock_32Threads                            18.37ns    54.43M\n----------------------------------------------------------------------------\nSharedPtrAcquire_64Threads                                  4.92us   203.42K\nWeakPtrLock_64Threads                                      10.90us    91.77K\nAtomicSharedPtrAcquire_64Threads                           14.06us    71.12K\nThreadCachedSynchronizedAcquire_64Threads                   4.64us   215.59K\nCoreCachedSharedPtrAcquire_64Threads                       33.87ns    29.53M\nCoreCachedWeakPtrLock_64Threads                            48.94ns    20.43M\nAtomicCoreCachedSharedPtrAcquire_64Threads                 35.85ns    27.89M\nReadMostlySharedPtrAcquire_64Threads                       32.08ns    31.17M\nReadMostlyWeakPtrLock_64Threads                            29.83ns    33.52M\n----------------------------------------------------------------------------\nSharedPtrSingleThreadReset                                 12.52ns    79.89M\nAtomicSharedPtrSingleThreadReset                           48.73ns    20.52M\nCoreCachedSharedPtrSingleThreadReset                        2.94us   340.29K\nAtomicCoreCachedSharedPtrSingleThreadReset                  5.15us   194.14K\n============================================================================\n#endif\n"
  },
  {
    "path": "folly/concurrency/test/DynamicBoundedQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/DynamicBoundedQueue.h>\n\n#include <folly/MPMCQueue.h>\n#include <folly/ProducerConsumerQueue.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\n#include <glog/logging.h>\n\n#include <atomic>\n#include <iomanip>\n#include <thread>\n\nDEFINE_bool(bench, false, \"run benchmark\");\nDEFINE_int32(reps, 10, \"number of reps\");\nDEFINE_int32(ops, 1000000, \"number of operations per rep\");\nDEFINE_int64(capacity, 1000000, \"capacity\");\n\ntemplate <typename T, bool MayBlock, typename WeightFn>\nusing DSPSC = folly::DSPSCQueue<T, MayBlock, 8, 7, WeightFn>;\n\ntemplate <typename T, bool MayBlock, typename WeightFn>\nusing DMPSC = folly::DMPSCQueue<T, MayBlock, 8, 7, WeightFn>;\n\ntemplate <typename T, bool MayBlock, typename WeightFn>\nusing DSPMC = folly::DSPMCQueue<T, MayBlock, 8, 7, WeightFn>;\n\ntemplate <typename T, bool MayBlock, typename WeightFn>\nusing DMPMC = folly::DMPMCQueue<T, MayBlock, 8, 7, WeightFn>;\n\ntemplate <template <typename, bool, typename> class Q, bool MayBlock>\nvoid basic_test() {\n  auto dur = std::chrono::microseconds(100);\n  auto deadline = std::chrono::steady_clock::now() + dur;\n\n  struct CustomWeightFn {\n    uint64_t operator()(int val) { return val * 100; }\n  };\n\n  Q<int, MayBlock, CustomWeightFn> q(10000);\n\n  ASSERT_TRUE(q.empty());\n  ASSERT_EQ(q.size(), 0);\n  int v;\n  ASSERT_FALSE(q.try_dequeue(v));\n  ASSERT_FALSE(q.try_dequeue().hasValue());\n\n  q.enqueue(1);\n  ASSERT_TRUE(q.try_enqueue(2));\n  ASSERT_TRUE(q.try_enqueue_until(3, deadline));\n  ASSERT_TRUE(q.try_enqueue_for(4, dur));\n  q.enqueue(5);\n  q.enqueue(6);\n  q.enqueue(7);\n\n  ASSERT_EQ(q.size(), 7);\n  ASSERT_EQ(q.weight(), 2800);\n  ASSERT_FALSE(q.empty());\n\n  q.dequeue(v);\n  ASSERT_EQ(v, 1);\n  ASSERT_TRUE(q.try_dequeue(v));\n  ASSERT_EQ(v, 2);\n  ASSERT_TRUE(q.try_dequeue_until(v, deadline));\n  ASSERT_EQ(v, 3);\n  ASSERT_TRUE(q.try_dequeue_for(v, dur));\n  ASSERT_EQ(v, 4);\n  ASSERT_EQ(*q.try_dequeue(), 5);\n  ASSERT_EQ(*q.try_dequeue_until(deadline), 6);\n  ASSERT_EQ(*q.try_dequeue_for(dur), 7);\n\n  ASSERT_TRUE(q.empty());\n  ASSERT_EQ(q.size(), 0);\n  ASSERT_EQ(q.weight(), 0);\n}\n\nTEST(DynamicBoundedQueue, basic) {\n  basic_test<DSPSC, false>();\n  basic_test<DMPSC, false>();\n  basic_test<DSPMC, false>();\n  basic_test<DMPMC, false>();\n  basic_test<DSPSC, true>();\n  basic_test<DMPSC, true>();\n  basic_test<DSPMC, true>();\n  basic_test<DMPMC, true>();\n}\n\ntemplate <template <typename, bool, typename> class Q, bool MayBlock>\nvoid move_test() {\n  struct Foo {\n    int v_;\n    explicit Foo(int v) noexcept : v_(v) {}\n    Foo(const Foo&) = delete;\n    Foo& operator=(const Foo&) = delete;\n    Foo(Foo&& other) noexcept : v_(other.v_) {}\n    Foo& operator=(Foo&& other) noexcept {\n      v_ = other.v_;\n      return *this;\n    }\n  };\n\n  struct CustomWeightFn {\n    uint64_t operator()(Foo&&) { return 10; }\n  };\n\n  auto dur = std::chrono::microseconds(100);\n  auto deadline = std::chrono::steady_clock::now() + dur;\n\n  Q<Foo, MayBlock, CustomWeightFn> q(100);\n  Foo v(1);\n  q.enqueue(std::move(v));\n  ASSERT_TRUE(q.try_enqueue(std::move(v)));\n  ASSERT_TRUE(q.try_enqueue_until(std::move(v), deadline));\n  ASSERT_TRUE(q.try_enqueue_for(std::move(v), dur));\n\n  ASSERT_EQ(q.size(), 4);\n  ASSERT_EQ(q.weight(), 40);\n}\n\nTEST(DynamicBoundedQueue, move) {\n  move_test<DSPSC, false>();\n  move_test<DMPSC, false>();\n  move_test<DSPMC, false>();\n  move_test<DMPMC, false>();\n  move_test<DSPSC, true>();\n  move_test<DMPSC, true>();\n  move_test<DSPMC, true>();\n  move_test<DMPMC, true>();\n}\n\ntemplate <template <typename, bool, typename> class Q, bool MayBlock>\nvoid capacity_test() {\n  struct CustomWeightFn {\n    uint64_t operator()(int val) { return val; }\n  };\n\n  Q<int, MayBlock, CustomWeightFn> q(1000);\n  ASSERT_EQ(q.weight(), 0);\n  int v;\n  q.enqueue(100);\n  ASSERT_EQ(q.weight(), 100);\n  q.enqueue(300);\n  ASSERT_EQ(q.weight(), 400);\n  ASSERT_FALSE(q.try_enqueue(1200));\n  q.reset_capacity(2000); // reset capacityy to 2000\n  ASSERT_TRUE(q.try_enqueue(1200));\n  ASSERT_EQ(q.weight(), 1600);\n  ASSERT_EQ(q.size(), 3);\n  q.reset_capacity(1000); // reset capacity back to 1000\n  ASSERT_FALSE(q.try_enqueue(100));\n  q.dequeue(v);\n  ASSERT_EQ(v, 100);\n  ASSERT_EQ(q.weight(), 1500);\n  q.dequeue(v);\n  ASSERT_EQ(v, 300);\n  ASSERT_EQ(q.weight(), 1200);\n}\n\nTEST(DynamicBoundedQueue, capacity) {\n  capacity_test<DSPSC, false>();\n  capacity_test<DMPSC, false>();\n  capacity_test<DSPMC, false>();\n  capacity_test<DMPMC, false>();\n  capacity_test<DSPSC, true>();\n  capacity_test<DMPSC, true>();\n  capacity_test<DSPMC, true>();\n  capacity_test<DMPMC, true>();\n}\n\ntemplate <typename ProdFunc, typename ConsFunc, typename EndFunc>\ninline uint64_t run_once(\n    int nprod,\n    int ncons,\n    const ProdFunc& prodFn,\n    const ConsFunc& consFn,\n    const EndFunc& endFn) {\n  std::atomic<bool> start{false};\n  std::atomic<int> ready{0};\n\n  /* producers */\n  std::vector<std::thread> prodThr(nprod);\n  for (int tid = 0; tid < nprod; ++tid) {\n    prodThr[tid] = std::thread([&, tid] {\n      ++ready;\n      while (!start.load()) {\n        /* spin */;\n      }\n      prodFn(tid);\n    });\n  }\n\n  /* consumers */\n  std::vector<std::thread> consThr(ncons);\n  for (int tid = 0; tid < ncons; ++tid) {\n    consThr[tid] = std::thread([&, tid] {\n      ++ready;\n      while (!start.load()) {\n        /* spin */;\n      }\n      consFn(tid);\n    });\n  }\n\n  /* wait for all producers and consumers to be ready */\n  while (ready.load() < (nprod + ncons)) {\n    /* spin */;\n  }\n\n  /* begin time measurement */\n  auto tbegin = std::chrono::steady_clock::now();\n  start.store(true);\n\n  /* wait for completion */\n  for (int i = 0; i < nprod; ++i) {\n    prodThr[i].join();\n  }\n  for (int i = 0; i < ncons; ++i) {\n    consThr[i].join();\n  }\n\n  /* end time measurement */\n  auto tend = std::chrono::steady_clock::now();\n  endFn();\n  return std::chrono::duration_cast<std::chrono::nanoseconds>(tend - tbegin)\n      .count();\n}\n\ntemplate <bool SingleProducer, bool SingleConsumer, bool MayBlock>\nvoid enq_deq_test(const int nprod, const int ncons) {\n  if (SingleProducer) {\n    ASSERT_EQ(nprod, 1);\n  }\n  if (SingleConsumer) {\n    ASSERT_EQ(ncons, 1);\n  }\n\n  int ops = 1000;\n  folly::DynamicBoundedQueue<int, SingleProducer, SingleConsumer, MayBlock, 2>\n      q(10);\n  std::atomic<uint64_t> sum(0);\n\n  auto prod = [&](int tid) {\n    for (int i = tid; i < ops; i += nprod) {\n      if ((i % 3) == 0) {\n        while (!q.try_enqueue(i)) {\n          /* keep trying */;\n        }\n      } else if ((i % 3) == 1) {\n        auto dur = std::chrono::microseconds(100);\n        while (!q.try_enqueue_for(i, dur)) {\n          /* keep trying */;\n        }\n      } else {\n        q.enqueue(i);\n      }\n    }\n  };\n\n  auto cons = [&](int tid) {\n    uint64_t mysum = 0;\n    for (int i = tid; i < ops; i += ncons) {\n      int v;\n      if ((i % 3) == 0) {\n        while (!q.try_dequeue(v)) {\n          /* keep trying */;\n        }\n      } else if ((i % 3) == 1) {\n        auto dur = std::chrono::microseconds(100);\n        while (!q.try_dequeue_for(v, dur)) {\n          /* keep trying */;\n        }\n      } else {\n        q.dequeue(v);\n      }\n      if (nprod == 1 && ncons == 1) {\n        ASSERT_EQ(v, i);\n      }\n      mysum += v;\n    }\n    sum.fetch_add(mysum);\n  };\n\n  auto endfn = [&] {\n    uint64_t expected = ops;\n    expected *= ops - 1;\n    expected /= 2;\n    ASSERT_EQ(sum.load(), expected);\n  };\n  run_once(nprod, ncons, prod, cons, endfn);\n}\n\nTEST(DynamicBoundedQueue, enqDeq) {\n  /* SPSC */\n  enq_deq_test<true, true, false>(1, 1);\n  enq_deq_test<true, true, true>(1, 1);\n  /* MPSC */\n  enq_deq_test<false, true, false>(1, 1);\n  enq_deq_test<false, true, true>(1, 1);\n  enq_deq_test<false, true, false>(2, 1);\n  enq_deq_test<false, true, true>(2, 1);\n  enq_deq_test<false, true, false>(10, 1);\n  enq_deq_test<false, true, true>(10, 1);\n  /* SPMC */\n  enq_deq_test<true, false, false>(1, 1);\n  enq_deq_test<true, false, true>(1, 1);\n  enq_deq_test<true, false, false>(1, 2);\n  enq_deq_test<true, false, true>(1, 2);\n  enq_deq_test<true, false, false>(1, 10);\n  enq_deq_test<true, false, true>(1, 10);\n  /* MPMC */\n  enq_deq_test<false, false, false>(1, 1);\n  enq_deq_test<false, false, true>(1, 1);\n  enq_deq_test<false, false, false>(2, 1);\n  enq_deq_test<false, false, true>(2, 1);\n  enq_deq_test<false, false, false>(10, 1);\n  enq_deq_test<false, false, true>(10, 1);\n  enq_deq_test<false, false, false>(1, 2);\n  enq_deq_test<false, false, true>(1, 2);\n  enq_deq_test<false, false, false>(1, 10);\n  enq_deq_test<false, false, true>(1, 10);\n  enq_deq_test<false, false, false>(2, 2);\n  enq_deq_test<false, false, true>(2, 2);\n  enq_deq_test<false, false, false>(10, 10);\n  enq_deq_test<false, false, true>(10, 10);\n}\n\ntemplate <typename RepFunc>\nuint64_t runBench(const std::string& name, int ops, const RepFunc& repFn) {\n  int reps = FLAGS_reps;\n  uint64_t min = UINTMAX_MAX;\n  uint64_t max = 0;\n  uint64_t sum = 0;\n\n  repFn(); // sometimes first run is outlier\n  for (int r = 0; r < reps; ++r) {\n    uint64_t dur = repFn();\n    sum += dur;\n    min = std::min(min, dur);\n    max = std::max(max, dur);\n    // if each rep takes too long run at least 2 reps\n    const uint64_t minute = 60000000000ULL;\n    if (sum > minute && r >= 1) {\n      reps = r + 1;\n      break;\n    }\n  }\n\n  const std::string unit = \" ns\";\n  uint64_t avg = sum / reps;\n  uint64_t res = min;\n  std::cout << name;\n  std::cout << \"   \" << std::setw(4) << max / ops << unit;\n  std::cout << \"   \" << std::setw(4) << avg / ops << unit;\n  std::cout << \"   \" << std::setw(4) << res / ops << unit;\n  std::cout << std::endl;\n  return res;\n}\n\ntemplate <template <typename, bool, typename> class Q, typename T, int Op>\nuint64_t bench(const int nprod, const int ncons, const std::string& name) {\n  int ops = FLAGS_ops;\n  auto repFn = [&] {\n    Q<T, Op == 3 || Op == 4 || Op == 5, folly::DefaultWeightFn<T>> q(\n        FLAGS_capacity);\n    std::atomic<uint64_t> sum(0);\n    auto prod = [&](int tid) {\n      for (int i = tid; i < ops; i += nprod) {\n        if (Op == 0 || Op == 3) {\n          while (!q.try_enqueue(i)) {\n            /* keep trying */;\n          }\n        } else if (Op == 1 || Op == 4) {\n          while (!q.try_enqueue_for(i, std::chrono::microseconds(1000))) {\n            /* keep trying */;\n          }\n        } else {\n          q.enqueue(i);\n        }\n      }\n    };\n    auto cons = [&](int tid) {\n      uint64_t mysum = 0;\n      T v = -1;\n      for (int i = tid; i < ops; i += ncons) {\n        if (Op == 0 || Op == 3) {\n          while (!q.try_dequeue(v)) {\n            /* keep trying */;\n          }\n        } else if (Op == 1 || Op == 4) {\n          while (!q.try_dequeue_for(v, std::chrono::microseconds(1000))) {\n            /* keep trying */;\n          }\n        } else {\n          q.dequeue(v);\n        }\n        if (nprod == 1 && ncons == 1) {\n          DCHECK_EQ(int(v), i);\n        }\n        mysum += v;\n      }\n      sum.fetch_add(mysum);\n    };\n    auto endfn = [&] {\n      uint64_t expected = ops;\n      expected *= ops - 1;\n      expected /= 2;\n      ASSERT_EQ(sum.load(), expected);\n    };\n    return run_once(nprod, ncons, prod, cons, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\n/* For performance comparison */\ntemplate <typename T>\nclass MPMC {\n  folly::MPMCQueue<T> q_;\n\n public:\n  explicit MPMC(uint64_t capacity) : q_(capacity) {}\n\n  void enqueue(const T& v) { q_.blockingWrite(v); }\n\n  void enqueue(T&& v) { q_.blockingWrite(std::move(v)); }\n\n  bool try_enqueue(const T& v) { return q_.write(v); }\n\n  bool try_enqueue(const T&& v) { return q_.write(std::move(v)); }\n\n  template <typename Rep, typename Period>\n  bool try_enqueue_for(\n      const T& v, const std::chrono::duration<Rep, Period>& duration) {\n    return q_.tryWriteUntil(std::chrono::steady_clock::now() + duration, v);\n  }\n\n  void dequeue(T& item) { q_.blockingRead(item); }\n\n  bool try_dequeue(T& item) { return q_.read(item); }\n\n  template <typename Rep, typename Period>\n  bool try_dequeue_for(\n      T& item, const std::chrono::duration<Rep, Period>& duration) {\n    return q_.tryReadUntil(std::chrono::steady_clock::now() + duration, item);\n  }\n};\n\ntemplate <typename T, bool, typename>\nusing FMPMC = MPMC<T>;\n\ntemplate <typename T>\nclass PCQ {\n  folly::ProducerConsumerQueue<T> q_;\n\n public:\n  explicit PCQ(uint64_t capacity) : q_(capacity) {}\n\n  void enqueue(const T&) { ASSERT_TRUE(false); }\n\n  bool try_enqueue(const T& v) { return q_.write(v); }\n\n  bool try_enqueue(T&& v) { return q_.write(std::move(v)); }\n\n  template <typename Rep, typename Period>\n  bool try_enqueue_for(const T&, const std::chrono::duration<Rep, Period>&) {\n    return false;\n  }\n\n  void dequeue(T&) { ASSERT_TRUE(false); }\n\n  bool try_dequeue(T& item) { return q_.read(item); }\n\n  template <typename Rep, typename Period>\n  bool try_dequeue_for(T&, const std::chrono::duration<Rep, Period>&) {\n    return false;\n  }\n};\n\ntemplate <typename T, bool, typename>\nusing FPCQ = PCQ<T>;\n\ntemplate <size_t M>\nstruct IntArray {\n  int a[M];\n  IntArray() {}\n  /* implicit */ IntArray(int v) {\n    for (size_t i = 0; i < M; ++i) {\n      a[i] = v;\n    }\n  }\n  operator int() { return a[0]; }\n};\n\nvoid dottedLine() {\n  std::cout << \"..............................................................\"\n            << std::endl;\n}\n\ntemplate <typename T>\nvoid type_benches(const int np, const int nc, const std::string& name) {\n  std::cout << name\n            << \"===========================================\" << std::endl;\n  if (np == 1 && nc == 1) {\n    bench<DSPSC, T, 0>(1, 1, \"DSPSC try   spin only           \");\n    bench<DSPSC, T, 1>(1, 1, \"DSPSC timed spin only           \");\n    bench<DSPSC, T, 2>(1, 1, \"DSPSC wait  spin only           \");\n    bench<DSPSC, T, 3>(1, 1, \"DSPSC try   may block           \");\n    bench<DSPSC, T, 4>(1, 1, \"DSPSC timed may block           \");\n    bench<DSPSC, T, 5>(1, 1, \"DSPSC wait  may block           \");\n    dottedLine();\n  }\n  if (nc == 1) {\n    bench<DMPSC, T, 0>(np, 1, \"DMPSC try   spin only           \");\n    bench<DMPSC, T, 1>(np, 1, \"DMPSC timed spin only           \");\n    bench<DMPSC, T, 2>(np, 1, \"DMPSC wait  spin only           \");\n    bench<DMPSC, T, 3>(np, 1, \"DMPSC try   may block           \");\n    bench<DMPSC, T, 4>(np, 1, \"DMPSC timed may block           \");\n    bench<DMPSC, T, 5>(np, 1, \"DMPSC wait  may block           \");\n    dottedLine();\n  }\n  if (np == 1) {\n    bench<DSPMC, T, 0>(1, nc, \"DSPMC try   spin only           \");\n    bench<DSPMC, T, 1>(1, nc, \"DSPMC timed spin only           \");\n    bench<DSPMC, T, 2>(1, nc, \"DSPMC wait  spin only           \");\n    bench<DSPMC, T, 3>(1, nc, \"DSPMC try   may block           \");\n    bench<DSPMC, T, 4>(1, nc, \"DSPMC timed may block           \");\n    bench<DSPMC, T, 5>(1, nc, \"DSPMC wait  may block           \");\n    dottedLine();\n  }\n  bench<DMPMC, T, 0>(np, nc, \"DMPMC try   spin only           \");\n  bench<DMPMC, T, 1>(np, nc, \"DMPMC timed spin only           \");\n  bench<DMPMC, T, 2>(np, nc, \"DMPMC wait  spin only           \");\n  bench<DMPMC, T, 3>(np, nc, \"DMPMC try   may block           \");\n  bench<DMPMC, T, 4>(np, nc, \"DMPMC timed may block           \");\n  bench<DMPMC, T, 5>(np, nc, \"DMPMC wait  may block           \");\n  dottedLine();\n  if (np == 1 && nc == 1) {\n    bench<FPCQ, T, 0>(1, 1, \"folly::PCQ  read                \");\n    dottedLine();\n  }\n  bench<FMPMC, T, 3>(np, nc, \"folly::MPMC  read               \");\n  bench<FMPMC, T, 4>(np, nc, \"folly::MPMC  tryReadUntil       \");\n  bench<FMPMC, T, 5>(np, nc, \"folly::MPMC  blockingRead       \");\n  std::cout << \"==============================================================\"\n            << std::endl;\n}\n\nvoid benches(const int np, const int nc) {\n  std::cout << \"====================== \" << std::setw(2) << np << \" prod\"\n            << \"  \" << std::setw(2) << nc << \" cons\"\n            << \" ======================\" << std::endl;\n  type_benches<uint32_t>(np, nc, \"=== uint32_t ======\");\n  // Benchmarks for other element sizes can be added as follows:\n  //   type_benches<IntArray<4>>(np, nc, \"=== IntArray<4> ===\");\n}\n\nTEST(DynamicBoundedQueue, bench) {\n  if (!FLAGS_bench) {\n    return;\n  }\n  std::cout << \"==============================================================\"\n            << std::endl;\n  std::cout << std::setw(2) << FLAGS_reps << \" reps of \" << std::setw(8)\n            << FLAGS_ops << \" handoffs\\n\";\n  dottedLine();\n  std::cout << \"Using capacity \" << FLAGS_capacity << \" for all queues\\n\";\n  std::cout << \"==============================================================\"\n            << std::endl;\n  std::cout << \"Test name                         Max time  Avg time  Min time\"\n            << std::endl;\n\n  for (int np : {1, 8, 32}) {\n    for (int nc : {1, 8, 32}) {\n      benches(np, nc);\n    }\n  }\n}\n\n/*\n$ numactl -N 1 dynamic_bounded_queue_test --bench --capacity=1000000\n==============================================================\n10 reps of  1000000 handoffs\n..............................................................\nUsing capacity 1000000 for all queues\n==============================================================\nTest name                         Max time  Avg time  Min time\n======================  1 prod   1 cons ======================\n=== uint32_t =================================================\nDSPSC try   spin only                 7 ns      7 ns      7 ns\nDSPSC timed spin only                 9 ns      9 ns      9 ns\nDSPSC wait  spin only                 7 ns      7 ns      7 ns\nDSPSC try   may block                39 ns     36 ns     33 ns\nDSPSC timed may block                39 ns     38 ns     37 ns\nDSPSC wait  may block                37 ns     34 ns     33 ns\n..............................................................\nDMPSC try   spin only                54 ns     53 ns     52 ns\nDMPSC timed spin only                53 ns     52 ns     51 ns\nDMPSC wait  spin only                53 ns     52 ns     51 ns\nDMPSC try   may block                67 ns     65 ns     64 ns\nDMPSC timed may block                64 ns     62 ns     60 ns\nDMPSC wait  may block                64 ns     62 ns     60 ns\n..............................................................\nDSPMC try   spin only                25 ns     24 ns     23 ns\nDSPMC timed spin only                24 ns     23 ns     23 ns\nDSPMC wait  spin only                22 ns     21 ns     21 ns\nDSPMC try   may block                30 ns     26 ns     21 ns\nDSPMC timed may block                25 ns     24 ns     24 ns\nDSPMC wait  may block                22 ns     22 ns     21 ns\n..............................................................\nDMPMC try   spin only                48 ns     45 ns     39 ns\nDMPMC timed spin only                31 ns     30 ns     24 ns\nDMPMC wait  spin only                49 ns     47 ns     43 ns\nDMPMC try   may block                63 ns     62 ns     61 ns\nDMPMC timed may block                64 ns     60 ns     46 ns\nDMPMC wait  may block                61 ns     60 ns     58 ns\n..............................................................\nfolly::PCQ  read                      8 ns      7 ns      7 ns\n..............................................................\nfolly::MPMC  read                    53 ns     51 ns     49 ns\nfolly::MPMC  tryReadUntil           112 ns    106 ns    103 ns\nfolly::MPMC  blockingRead            50 ns     47 ns     46 ns\n==============================================================\n======================  1 prod   8 cons ======================\n=== uint32_t =================================================\nDSPMC try   spin only               166 ns    159 ns    153 ns\nDSPMC timed spin only               169 ns    163 ns    156 ns\nDSPMC wait  spin only                60 ns     57 ns     54 ns\nDSPMC try   may block               170 ns    163 ns    153 ns\nDSPMC timed may block               165 ns    157 ns    150 ns\nDSPMC wait  may block                94 ns     78 ns     52 ns\n..............................................................\nDMPMC try   spin only               170 ns    161 ns    149 ns\nDMPMC timed spin only               167 ns    158 ns    149 ns\nDMPMC wait  spin only                93 ns     80 ns     51 ns\nDMPMC try   may block               164 ns    161 ns    154 ns\nDMPMC timed may block               163 ns    156 ns    145 ns\nDMPMC wait  may block               117 ns    102 ns     87 ns\n..............................................................\nfolly::MPMC  read                   176 ns    168 ns    159 ns\nfolly::MPMC  tryReadUntil          1846 ns    900 ns    521 ns\nfolly::MPMC  blockingRead           219 ns    193 ns    178 ns\n==============================================================\n======================  1 prod  32 cons ======================\n=== uint32_t =================================================\nDSPMC try   spin only               224 ns    213 ns    204 ns\nDSPMC timed spin only               215 ns    209 ns    199 ns\nDSPMC wait  spin only               334 ns    114 ns     52 ns\nDSPMC try   may block               240 ns    215 ns    202 ns\nDSPMC timed may block               245 ns    221 ns    200 ns\nDSPMC wait  may block               215 ns    151 ns     98 ns\n..............................................................\nDMPMC try   spin only               348 ns    252 ns    204 ns\nDMPMC timed spin only               379 ns    244 ns    198 ns\nDMPMC wait  spin only               173 ns    116 ns     89 ns\nDMPMC try   may block               362 ns    231 ns    173 ns\nDMPMC timed may block               282 ns    236 ns    206 ns\nDMPMC wait  may block               252 ns    172 ns    134 ns\n..............................................................\nfolly::MPMC  read                   540 ns    290 ns    186 ns\nfolly::MPMC  tryReadUntil          24946 ns   24280 ns   23113 ns\nfolly::MPMC  blockingRead          1345 ns   1297 ns   1265 ns\n==============================================================\n======================  8 prod   1 cons ======================\n=== uint32_t =================================================\nDMPSC try   spin only                68 ns     64 ns     60 ns\nDMPSC timed spin only                69 ns     66 ns     61 ns\nDMPSC wait  spin only                67 ns     65 ns     62 ns\nDMPSC try   may block                77 ns     73 ns     67 ns\nDMPSC timed may block                75 ns     74 ns     68 ns\nDMPSC wait  may block                76 ns     73 ns     69 ns\n..............................................................\nDMPMC try   spin only                76 ns     66 ns     60 ns\nDMPMC timed spin only                77 ns     68 ns     63 ns\nDMPMC wait  spin only                68 ns     65 ns     63 ns\nDMPMC try   may block                76 ns     72 ns     64 ns\nDMPMC timed may block                82 ns     74 ns     67 ns\nDMPMC wait  may block                77 ns     72 ns     68 ns\n..............................................................\nfolly::MPMC  read                   170 ns    166 ns    161 ns\nfolly::MPMC  tryReadUntil           184 ns    179 ns    173 ns\nfolly::MPMC  blockingRead            79 ns     73 ns     53 ns\n==============================================================\n======================  8 prod   8 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               181 ns    169 ns    133 ns\nDMPMC timed spin only               179 ns    168 ns    148 ns\nDMPMC wait  spin only                77 ns     76 ns     71 ns\nDMPMC try   may block               180 ns    179 ns    176 ns\nDMPMC timed may block               174 ns    166 ns    153 ns\nDMPMC wait  may block                79 ns     78 ns     75 ns\n..............................................................\nfolly::MPMC  read                   219 ns    206 ns    183 ns\nfolly::MPMC  tryReadUntil           262 ns    244 ns    213 ns\nfolly::MPMC  blockingRead            61 ns     58 ns     54 ns\n==============================================================\n======================  8 prod  32 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               265 ns    217 ns    203 ns\nDMPMC timed spin only               236 ns    215 ns    202 ns\nDMPMC wait  spin only                93 ns     83 ns     77 ns\nDMPMC try   may block               325 ns    234 ns    200 ns\nDMPMC timed may block               206 ns    202 ns    193 ns\nDMPMC wait  may block               139 ns     93 ns     76 ns\n..............................................................\nfolly::MPMC  read                   259 ns    214 ns    201 ns\nfolly::MPMC  tryReadUntil           281 ns    274 ns    267 ns\nfolly::MPMC  blockingRead            62 ns     59 ns     57 ns\n==============================================================\n====================== 32 prod   1 cons ======================\n=== uint32_t =================================================\nDMPSC try   spin only                95 ns     57 ns     45 ns\nDMPSC timed spin only                94 ns     52 ns     46 ns\nDMPSC wait  spin only               104 ns     54 ns     43 ns\nDMPSC try   may block                59 ns     54 ns     51 ns\nDMPSC timed may block                86 ns     58 ns     52 ns\nDMPSC wait  may block                76 ns     57 ns     53 ns\n..............................................................\nDMPMC try   spin only                68 ns     64 ns     60 ns\nDMPMC timed spin only               137 ns     73 ns     61 ns\nDMPMC wait  spin only                86 ns     65 ns     58 ns\nDMPMC try   may block                89 ns     71 ns     65 ns\nDMPMC timed may block                82 ns     69 ns     65 ns\nDMPMC wait  may block                84 ns     71 ns     66 ns\n..............................................................\nfolly::MPMC  read                   222 ns    203 ns    192 ns\nfolly::MPMC  tryReadUntil           239 ns    232 ns    191 ns\nfolly::MPMC  blockingRead            78 ns     68 ns     64 ns\n==============================================================\n====================== 32 prod   8 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               183 ns    138 ns    107 ns\nDMPMC timed spin only               237 ns    158 ns     98 ns\nDMPMC wait  spin only                87 ns     70 ns     58 ns\nDMPMC try   may block               169 ns    132 ns     92 ns\nDMPMC timed may block               172 ns    133 ns     79 ns\nDMPMC wait  may block               166 ns     89 ns     66 ns\n..............................................................\nfolly::MPMC  read                   221 ns    194 ns    183 ns\nfolly::MPMC  tryReadUntil           258 ns    244 ns    230 ns\nfolly::MPMC  blockingRead            60 ns     54 ns     47 ns\n==============================================================\n====================== 32 prod  32 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               419 ns    252 ns    181 ns\nDMPMC timed spin only               252 ns    212 ns    179 ns\nDMPMC wait  spin only               153 ns     79 ns     62 ns\nDMPMC try   may block               302 ns    236 ns    182 ns\nDMPMC timed may block               266 ns    213 ns    170 ns\nDMPMC wait  may block               262 ns    120 ns     64 ns\n..............................................................\nfolly::MPMC  read                   269 ns    245 ns    199 ns\nfolly::MPMC  tryReadUntil           257 ns    245 ns    235 ns\nfolly::MPMC  blockingRead            53 ns     48 ns     45 ns\n==============================================================\n\n$ numactl -N 1 dynamic_bounded_queue_test --bench --capacity=1000\n==============================================================\n10 reps of  1000000 handoffs\n..............................................................\nUsing capacity 1000 for all queues\n==============================================================\nTest name                         Max time  Avg time  Min time\n======================  1 prod   1 cons ======================\n=== uint32_t =================================================\nDSPSC try   spin only                 7 ns      7 ns      7 ns\nDSPSC timed spin only                 9 ns      9 ns      9 ns\nDSPSC wait  spin only                 7 ns      7 ns      7 ns\nDSPSC try   may block                34 ns     33 ns     31 ns\nDSPSC timed may block                34 ns     34 ns     33 ns\nDSPSC wait  may block                30 ns     30 ns     29 ns\n..............................................................\nDMPSC try   spin only                60 ns     57 ns     55 ns\nDMPSC timed spin only                55 ns     52 ns     51 ns\nDMPSC wait  spin only                57 ns     54 ns     52 ns\nDMPSC try   may block                66 ns     62 ns     39 ns\nDMPSC timed may block                67 ns     64 ns     62 ns\nDMPSC wait  may block                67 ns     65 ns     64 ns\n..............................................................\nDSPMC try   spin only                27 ns     25 ns     24 ns\nDSPMC timed spin only                25 ns     25 ns     24 ns\nDSPMC wait  spin only                23 ns     23 ns     22 ns\nDSPMC try   may block                31 ns     26 ns     24 ns\nDSPMC timed may block                33 ns     30 ns     30 ns\nDSPMC wait  may block                37 ns     29 ns     28 ns\n..............................................................\nDMPMC try   spin only                55 ns     53 ns     51 ns\nDMPMC timed spin only                36 ns     31 ns     26 ns\nDMPMC wait  spin only                54 ns     53 ns     51 ns\nDMPMC try   may block                68 ns     64 ns     51 ns\nDMPMC timed may block                66 ns     63 ns     60 ns\nDMPMC wait  may block                68 ns     63 ns     60 ns\n..............................................................\nfolly::PCQ  read                     15 ns     13 ns     11 ns\n..............................................................\nfolly::MPMC  read                    60 ns     56 ns     51 ns\nfolly::MPMC  tryReadUntil           134 ns    112 ns    102 ns\nfolly::MPMC  blockingRead            57 ns     51 ns     48 ns\n==============================================================\n======================  1 prod   8 cons ======================\n=== uint32_t =================================================\nDSPMC try   spin only               169 ns    162 ns    151 ns\nDSPMC timed spin only               178 ns    166 ns    149 ns\nDSPMC wait  spin only                59 ns     55 ns     54 ns\nDSPMC try   may block               173 ns    163 ns    153 ns\nDSPMC timed may block               171 ns    166 ns    156 ns\nDSPMC wait  may block                71 ns     57 ns     51 ns\n..............................................................\nDMPMC try   spin only               172 ns    164 ns    158 ns\nDMPMC timed spin only               173 ns    164 ns    156 ns\nDMPMC wait  spin only                77 ns     62 ns     53 ns\nDMPMC try   may block               181 ns    163 ns    152 ns\nDMPMC timed may block               174 ns    165 ns    151 ns\nDMPMC wait  may block                91 ns     72 ns     52 ns\n..............................................................\nfolly::MPMC  read                   178 ns    167 ns    161 ns\nfolly::MPMC  tryReadUntil           991 ns    676 ns    423 ns\nfolly::MPMC  blockingRead           154 ns    129 ns     96 ns\n==============================================================\n======================  1 prod  32 cons ======================\n=== uint32_t =================================================\nDSPMC try   spin only               462 ns    288 ns    201 ns\nDSPMC timed spin only               514 ns    283 ns    201 ns\nDSPMC wait  spin only               100 ns     60 ns     45 ns\nDSPMC try   may block               531 ns    318 ns    203 ns\nDSPMC timed may block              1379 ns    891 ns    460 ns\nDSPMC wait  may block               148 ns    111 ns     82 ns\n..............................................................\nDMPMC try   spin only               404 ns    312 ns    205 ns\nDMPMC timed spin only               337 ns    253 ns    219 ns\nDMPMC wait  spin only               130 ns     97 ns     72 ns\nDMPMC try   may block               532 ns    265 ns    201 ns\nDMPMC timed may block               846 ns    606 ns    412 ns\nDMPMC wait  may block               158 ns    112 ns     87 ns\n..............................................................\nfolly::MPMC  read                   880 ns    419 ns    284 ns\nfolly::MPMC  tryReadUntil          23432 ns   23184 ns   23007 ns\nfolly::MPMC  blockingRead          1353 ns   1308 ns   1279 ns\n==============================================================\n======================  8 prod   1 cons ======================\n=== uint32_t =================================================\nDMPSC try   spin only                67 ns     63 ns     51 ns\nDMPSC timed spin only                69 ns     65 ns     63 ns\nDMPSC wait  spin only                67 ns     65 ns     61 ns\nDMPSC try   may block                73 ns     69 ns     63 ns\nDMPSC timed may block                72 ns     69 ns     64 ns\nDMPSC wait  may block                71 ns     70 ns     68 ns\n..............................................................\nDMPMC try   spin only                70 ns     64 ns     59 ns\nDMPMC timed spin only                76 ns     66 ns     53 ns\nDMPMC wait  spin only                68 ns     66 ns     64 ns\nDMPMC try   may block                71 ns     68 ns     66 ns\nDMPMC timed may block                72 ns     70 ns     67 ns\nDMPMC wait  may block                73 ns     70 ns     67 ns\n..............................................................\nfolly::MPMC  read                   193 ns    167 ns    153 ns\nfolly::MPMC  tryReadUntil           497 ns    415 ns    348 ns\nfolly::MPMC  blockingRead           163 ns    134 ns    115 ns\n==============================================================\n======================  8 prod   8 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               216 ns    203 ns    196 ns\nDMPMC timed spin only               199 ns    186 ns    178 ns\nDMPMC wait  spin only                63 ns     60 ns     58 ns\nDMPMC try   may block               212 ns    198 ns    183 ns\nDMPMC timed may block               180 ns    170 ns    162 ns\nDMPMC wait  may block                72 ns     68 ns     65 ns\n..............................................................\nfolly::MPMC  read                   225 ns    201 ns    188 ns\nfolly::MPMC  tryReadUntil           255 ns    248 ns    232 ns\nfolly::MPMC  blockingRead            52 ns     48 ns     42 ns\n==============================================================\n======================  8 prod  32 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               360 ns    302 ns    195 ns\nDMPMC timed spin only               350 ns    272 ns    218 ns\nDMPMC wait  spin only                92 ns     72 ns     61 ns\nDMPMC try   may block               352 ns    263 ns    223 ns\nDMPMC timed may block               218 ns    213 ns    209 ns\nDMPMC wait  may block                98 ns     77 ns     70 ns\n..............................................................\nfolly::MPMC  read                   611 ns    461 ns    339 ns\nfolly::MPMC  tryReadUntil           270 ns    260 ns    253 ns\nfolly::MPMC  blockingRead            89 ns     84 ns     80 ns\n==============================================================\n====================== 32 prod   1 cons ======================\n=== uint32_t =================================================\nDMPSC try   spin only               389 ns    248 ns    149 ns\nDMPSC timed spin only               356 ns    235 ns    120 ns\nDMPSC wait  spin only               343 ns    242 ns    125 ns\nDMPSC try   may block               412 ns    294 ns    168 ns\nDMPSC timed may block               332 ns    271 ns    189 ns\nDMPSC wait  may block               280 ns    252 ns    199 ns\n..............................................................\nDMPMC try   spin only               393 ns    269 ns    105 ns\nDMPMC timed spin only               328 ns    240 ns    112 ns\nDMPMC wait  spin only               502 ns    266 ns    107 ns\nDMPMC try   may block               514 ns    346 ns    192 ns\nDMPMC timed may block               339 ns    318 ns    278 ns\nDMPMC wait  may block               319 ns    307 ns    292 ns\n..............................................................\nfolly::MPMC  read                   948 ns    517 ns    232 ns\nfolly::MPMC  tryReadUntil          9649 ns   7567 ns   4140 ns\nfolly::MPMC  blockingRead          1365 ns   1316 ns   1131 ns\n==============================================================\n====================== 32 prod   8 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               436 ns    257 ns    115 ns\nDMPMC timed spin only               402 ns    272 ns    121 ns\nDMPMC wait  spin only               136 ns     78 ns     55 ns\nDMPMC try   may block               454 ns    227 ns     78 ns\nDMPMC timed may block               155 ns    137 ns    116 ns\nDMPMC wait  may block                62 ns     59 ns     57 ns\n..............................................................\nfolly::MPMC  read                   677 ns    497 ns    336 ns\nfolly::MPMC  tryReadUntil           268 ns    262 ns    258 ns\nfolly::MPMC  blockingRead            87 ns     85 ns     82 ns\n==============================================================\n====================== 32 prod  32 cons ======================\n=== uint32_t =================================================\nDMPMC try   spin only               786 ns    381 ns    142 ns\nDMPMC timed spin only               795 ns    346 ns    126 ns\nDMPMC wait  spin only               334 ns    107 ns     55 ns\nDMPMC try   may block               535 ns    317 ns    144 ns\nDMPMC timed may block               197 ns    192 ns    183 ns\nDMPMC wait  may block               189 ns     75 ns     60 ns\n..............................................................\nfolly::MPMC  read                  1110 ns    919 ns    732 ns\nfolly::MPMC  tryReadUntil           214 ns    210 ns    206 ns\nfolly::MPMC  blockingRead            53 ns     52 ns     51 ns\n==============================================================\n\n$ lscpu\nArchitecture:        x86_64\nCPU op-mode(s):      32-bit, 64-bit\nByte Order:          Little Endian\nCPU(s):              32\nOn-line CPU(s) list: 0-31\nThread(s) per core:  2\nCore(s) per socket:  8\nSocket(s):           2\nNUMA node(s):        2\nVendor ID:           GenuineIntel\nCPU family:          6\nModel:               45\nModel name:          Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz\nStepping:            6\nCPU MHz:             2200.000\nCPU max MHz:         2200.0000\nCPU min MHz:         1200.0000\nBogoMIPS:            4399.92\nVirtualization:      VT-x\nL1d cache:           32K\nL1i cache:           32K\nL2 cache:            256K\nL3 cache:            20480K\nNUMA node0 CPU(s):   0-7,16-23\nNUMA node1 CPU(s):   8-15,24-31\n\nFlags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr\n                     pge mca cmov pat pse36 clflush dts acpi mmx fxsr\n                     sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp\n                     lm constant_tsc arch_perfmon pebs bts rep_good\n                     nopl xtopology nonstop_tsc aperfmperf eagerfpu\n                     pni pclmulqdq dtes64 monitor ds_cpl vmx smx est\n                     tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2\n                     x2apic popcnt tsc_deadline_timer aes xsave avx\n                     lahf_lm epb tpr_shadow vnmi flexpriority ept vpid\n                     xsaveopt dtherm arat pln pts\n */\n"
  },
  {
    "path": "folly/concurrency/test/PriorityUnboundedQueueSetTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n\n#include <folly/concurrency/PriorityUnboundedQueueSet.h>\n#include <folly/container/Enumerate.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nclass PriorityUnboundedQueueSetTest : public testing::Test {};\n\nTEST_F(PriorityUnboundedQueueSetTest, try_dequeue) {\n  PriorityUSPSCQueueSet<int, false> q(3);\n  q.at_priority(1).enqueue(42);\n  EXPECT_EQ(42, q.try_dequeue().value());\n}\n\nTEST_F(PriorityUnboundedQueueSetTest, try_peek) {\n  PriorityUSPSCQueueSet<int, false> q(3);\n  q.at_priority(1).enqueue(42);\n  EXPECT_EQ(42, *q.try_peek());\n  q.at_priority(1).enqueue(42);\n  EXPECT_EQ(42, q.try_dequeue().value());\n}\n\nTEST_F(PriorityUnboundedQueueSetTest, size_empty) {\n  PriorityUSPSCQueueSet<int, false> q(3);\n  EXPECT_TRUE(q.empty());\n  EXPECT_EQ(0, q.size());\n\n  q.at_priority(1).enqueue(42);\n  EXPECT_FALSE(q.empty());\n  EXPECT_EQ(1, q.size());\n\n  EXPECT_EQ(42, *q.try_peek());\n  EXPECT_FALSE(q.empty());\n  EXPECT_EQ(1, q.size());\n\n  EXPECT_EQ(42, q.try_dequeue().value());\n  EXPECT_TRUE(q.empty());\n  EXPECT_EQ(0, q.size());\n}\n\nTEST_F(PriorityUnboundedQueueSetTest, priority_order) {\n  PriorityUSPSCQueueSet<int, false> q(3);\n  EXPECT_EQ(0, q.size());\n  q.at_priority(1).enqueue(55);\n  q.at_priority(2).enqueue(42);\n  q.at_priority(0).enqueue(12);\n  q.at_priority(1).enqueue(27);\n  EXPECT_EQ(4, q.size());\n  EXPECT_EQ(12, q.try_dequeue().value());\n  EXPECT_EQ(55, q.try_dequeue().value());\n  EXPECT_EQ(27, q.try_dequeue().value());\n  EXPECT_EQ(42, q.try_dequeue().value());\n  EXPECT_EQ(0, q.size());\n}\n"
  },
  {
    "path": "folly/concurrency/test/ProcessLocalUniqueIdTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/ProcessLocalUniqueId.h>\n\n#include <algorithm>\n#include <thread>\n#include <vector>\n\n#include <folly/Synchronized.h>\n#include <folly/portability/GTest.h>\n\nTEST(ProcessLocalUniqueIdTest, Uniqueness) {\n  const size_t kNumThreads = 16;\n  // Ensure each thread wraps around the counter a few times.\n  constexpr static size_t kNumIdsPerThread = 1 << 18;\n  const size_t kNumIds = kNumThreads * kNumIdsPerThread;\n\n  folly::Synchronized<std::vector<uint64_t>> idsAccum;\n  idsAccum.wlock()->reserve(kNumIds);\n\n  std::vector<std::thread> threads;\n  for (size_t tid = 0; tid < kNumThreads; ++tid) {\n    threads.emplace_back([&idsAccum] {\n      std::vector<uint64_t> threadIds;\n      threadIds.reserve(kNumIdsPerThread);\n      for (size_t iter = 0; iter < kNumIdsPerThread; ++iter) {\n        threadIds.push_back(folly::processLocalUniqueId());\n      }\n      std::sort(threadIds.begin(), threadIds.end());\n      auto idsAccumLocked = idsAccum.wlock();\n      auto mid = idsAccumLocked->size();\n      idsAccumLocked->insert(\n          idsAccumLocked->end(), threadIds.begin(), threadIds.end());\n      std::inplace_merge(\n          idsAccumLocked->begin(),\n          idsAccumLocked->begin() + mid,\n          idsAccumLocked->end());\n    });\n  }\n\n  for (auto& t : threads) {\n    t.join();\n  }\n\n  auto ids = idsAccum.exchange({});\n  ASSERT_TRUE(std::is_sorted(ids.begin(), ids.end()));\n  auto end = std::unique(ids.begin(), ids.end());\n  // All ids should be distinct.\n  EXPECT_EQ(end - ids.begin(), kNumIds);\n  // 0 should not be returned.\n  EXPECT_NE(ids[0], 0);\n}\n"
  },
  {
    "path": "folly/concurrency/test/SingletonRelaxedCounterBench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/SingletonRelaxedCounter.h>\n\n#include <cstddef>\n#include <thread>\n\n#include <folly/synchronization/test/Barrier.h>\n\n#include <folly/Benchmark.h>\n#include <folly/init/Init.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GFlags.h>\n\nDEFINE_int32(num_threads, 4, \"No. of threads allocting the objects\");\n\nnamespace {\nstruct PrivateTag {};\n} // namespace\n\nusing Counter = folly::SingletonRelaxedCounter<size_t, PrivateTag>;\n\n// small wrappers around the functions being benchmarked\n// useful for looking at the inlined native code of the fast path\nextern \"C\" FOLLY_KEEP void check_singleton_relaxed_counter_add_1() {\n  Counter::add(1);\n}\nextern \"C\" FOLLY_KEEP void check_singleton_relaxed_counter_add(size_t i) {\n  Counter::add(i);\n}\n\nnamespace folly {\n\n// a noop which the compiler cannot see through\n// used to prevent some compiler optimizations which could skew benchmarks\n[[gnu::weak]] void noop() noexcept {}\n\nBENCHMARK(MultiThreadPerformance, iters) {\n  BenchmarkSuspender braces;\n\n  folly::test::Barrier barrier(1 + FLAGS_num_threads);\n\n  std::vector<std::thread> threads(FLAGS_num_threads);\n\n  for (auto& thread : threads) {\n    thread = std::thread([&] {\n      auto once = [] {\n        noop();\n        Counter::add(1);\n        noop();\n        Counter::sub(1);\n      };\n\n      // in case the first call does expensive setup, such as take a global lock\n      once();\n\n      barrier.wait(); // A - wait for thread start\n\n      barrier.wait(); // B - init the work\n\n      for (auto i = 0u; i < iters; ++i) {\n        once();\n      }\n\n      barrier.wait(); // C - join the work\n    });\n  }\n\n  barrier.wait(); // A - wait for thread start\n\n  // we want to exclude thread start/stop operations from measurement\n  braces.dismissing([&] {\n    barrier.wait(); // B - init the work\n    barrier.wait(); // C - join the work\n  });\n\n  for (auto& thread : threads) {\n    thread.join();\n  }\n}\n} // namespace folly\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/concurrency/test/SingletonRelaxedCounterTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/SingletonRelaxedCounter.h>\n\n#include <cstddef>\n#include <thread>\n\n#include <folly/synchronization/test/Barrier.h>\n\n#include <folly/ThreadLocal.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nnamespace {\nstruct PrivateTag {};\n} // namespace\n\nusing Counter = SingletonRelaxedCounter<size_t, PrivateTag>;\n\nclass SingletonRelaxedCounterTest : public testing::Test {};\n\nTEST_F(SingletonRelaxedCounterTest, Basic) {\n  EXPECT_EQ(0, Counter::count());\n  Counter::add(3);\n  EXPECT_EQ(3, Counter::count());\n  Counter::sub(3);\n  EXPECT_EQ(0, Counter::count());\n}\n\nTEST_F(SingletonRelaxedCounterTest, CompatibilityWithThreadLocal) {\n  struct CounterNoOpInDtor {\n    ~CounterNoOpInDtor() { Counter::add(0); }\n  };\n  ThreadLocal<CounterNoOpInDtor> tl;\n  auto thread = std::thread([&] { //\n    std::ignore = *tl;\n  });\n  thread.join();\n  EXPECT_EQ(0, Counter::count());\n}\n\nTEST_F(SingletonRelaxedCounterTest, CompatibilityWithThreadLocalMany) {\n  struct CounterNoOpInDtor {\n    ~CounterNoOpInDtor() { Counter::add(0); }\n  };\n  ThreadLocal<CounterNoOpInDtor> tl;\n  std::vector<std::thread> threads(16);\n  for (auto& thread : threads) {\n    thread = std::thread([&] { //\n      std::ignore = *tl;\n    });\n  }\n  for (auto& thread : threads) {\n    thread.join();\n  }\n  EXPECT_EQ(0, Counter::count());\n}\n\nTEST_F(SingletonRelaxedCounterTest, MultithreadCorrectness) {\n  static constexpr size_t const kPerThreadIncrements = 1000000;\n  static constexpr size_t const kNumThreads = 24;\n  static constexpr size_t const kNumCounters = 6;\n  static constexpr size_t const kNumIters = 2;\n\n  std::atomic<bool> stop{false};\n  std::vector<std::thread> counters(kNumCounters);\n\n  for (auto& counter : counters) {\n    counter = std::thread([&] {\n      while (!stop.load(std::memory_order_relaxed)) {\n        std::this_thread::yield();\n        Counter::count();\n      }\n    });\n  }\n\n  for (size_t j = 0; j < kNumIters; ++j) {\n    std::vector<std::thread> threads(kNumThreads);\n\n    folly::test::Barrier barrier{kNumThreads + 1};\n\n    for (auto& thread : threads) {\n      thread = std::thread([&] {\n        barrier.wait(); // A\n        barrier.wait(); // B\n        for (size_t i = 0; i < kPerThreadIncrements; ++i) {\n          Counter::add(1);\n        }\n        barrier.wait(); // C\n        barrier.wait(); // D\n        for (size_t i = 0; i < kPerThreadIncrements; ++i) {\n          Counter::sub(1);\n        }\n        barrier.wait(); // E\n        barrier.wait(); // F\n      });\n    }\n\n    barrier.wait(); // A\n    EXPECT_EQ(0, Counter::count());\n\n    barrier.wait(); // B\n    barrier.wait(); // C\n    EXPECT_EQ(kPerThreadIncrements * kNumThreads, Counter::count());\n\n    barrier.wait(); // D\n    barrier.wait(); // E\n    EXPECT_EQ(0, Counter::count());\n\n    barrier.wait(); // F\n\n    for (auto& thread : threads) {\n      thread.join();\n    }\n  }\n\n  stop.store(true, std::memory_order_relaxed);\n  for (auto& counter : counters) {\n    counter.join();\n  }\n\n  Counter::count();\n}\n\nTEST_F(SingletonRelaxedCounterTest, Countable) {\n  struct Object : SingletonRelaxedCountable<Object> {\n    std::shared_ptr<int> contained;\n  };\n  using Access = SingletonRelaxedCountableAccess<Object>;\n\n  EXPECT_EQ(0, Access::count());\n\n  {\n    Object a;\n    EXPECT_EQ(1, Access::count());\n  }\n  EXPECT_EQ(0, Access::count());\n\n  {\n    Object a;\n    Object b(a);\n    EXPECT_EQ(2, Access::count());\n  }\n  EXPECT_EQ(0, Access::count());\n\n  {\n    Object a;\n    Object b(std::move(a));\n    EXPECT_EQ(2, Access::count());\n  }\n  EXPECT_EQ(0, Access::count());\n\n  {\n    Object a;\n    Object b;\n    b = a;\n    EXPECT_EQ(2, Access::count());\n  }\n  EXPECT_EQ(0, Access::count());\n\n  {\n    Object a;\n    Object b;\n    b = std::move(a);\n    EXPECT_EQ(2, Access::count());\n  }\n  EXPECT_EQ(0, Access::count());\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/concurrency/test/ThreadCachedSynchronizedBench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/ThreadCachedSynchronized.h>\n\n#include <folly/Benchmark.h>\n#include <folly/Synchronized.h>\n#include <folly/lang/Keep.h>\n\nextern \"C\" FOLLY_KEEP int check_thread_cached_synchronized_int_get(\n    folly::thread_cached_synchronized<int> const& ptr) {\n  return ptr;\n}\n\nstruct load_fn {\n  template <typename T>\n  T operator()(folly::relaxed_atomic<T> const& obj) const {\n    return obj;\n  }\n  template <typename T>\n  T operator()(folly::Synchronized<T> const& obj) const {\n    return obj.copy();\n  }\n  template <typename T>\n  T operator()(folly::thread_cached_synchronized<T> const& obj) const {\n    return obj;\n  }\n};\ninline constexpr load_fn load{};\n\ntemplate <typename Obj>\nFOLLY_NOINLINE void bm_read_int(Obj& obj, std::size_t iters) {\n  std::size_t sum = 0;\n  while (iters--) {\n    auto loaded = load(obj);\n    folly::makeUnpredictable(loaded);\n    sum += loaded;\n  }\n  folly::doNotOptimizeAway(sum);\n}\n\nBENCHMARK(read_int_atomic, iters) {\n  folly::relaxed_atomic<int> obj{3};\n  bm_read_int(obj, iters);\n}\n\nBENCHMARK(read_int_synchronized, iters) {\n  folly::BenchmarkSuspender braces;\n  folly::Synchronized<int> obj{3};\n  braces.dismissing([&] { bm_read_int(obj, iters); });\n}\n\nBENCHMARK(read_int_thread_cached_synchronized, iters) {\n  folly::BenchmarkSuspender braces;\n  folly::thread_cached_synchronized<int> obj{3};\n  braces.dismissing([&] { bm_read_int(obj, iters); });\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/concurrency/test/ThreadCachedSynchronizedTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/ThreadCachedSynchronized.h>\n\n#include <folly/portability/GTest.h>\n\nclass ThreadCachedSynchronizedTest : public testing::Test {};\n\nTEST_F(ThreadCachedSynchronizedTest, load_store) {\n  folly::thread_cached_synchronized obj{0};\n  EXPECT_EQ(0, obj);\n  EXPECT_EQ(0, obj.load());\n\n  obj = 7;\n  EXPECT_EQ(7, obj);\n\n  obj.store();\n  EXPECT_EQ(0, obj);\n\n  obj.store(7);\n  EXPECT_EQ(7, obj);\n\n  int const& ref = obj.operator*();\n  EXPECT_EQ(7, ref);\n\n  int const* ptr = obj.operator->();\n  EXPECT_EQ(7, *ptr);\n}\n\nTEST_F(ThreadCachedSynchronizedTest, exchange) {\n  folly::thread_cached_synchronized obj{9};\n  EXPECT_EQ(9, obj.exchange(3));\n  EXPECT_EQ(3, obj);\n}\n\nTEST_F(ThreadCachedSynchronizedTest, compare_exchange_failure) {\n  folly::thread_cached_synchronized obj{12};\n  int val = 4;\n  EXPECT_FALSE(obj.compare_exchange(val, 32));\n  EXPECT_EQ(12, val);\n  EXPECT_EQ(12, obj);\n}\n\nTEST_F(ThreadCachedSynchronizedTest, compare_exchange_success) {\n  folly::thread_cached_synchronized obj{12};\n  int val = 12;\n  EXPECT_TRUE(obj.compare_exchange(val, 32));\n  EXPECT_EQ(12, val);\n  EXPECT_EQ(32, obj);\n}\n\nTEST_F(ThreadCachedSynchronizedTest, swap_member) {\n  folly::thread_cached_synchronized obj{3};\n  int val = 9;\n  obj.swap(val);\n  EXPECT_EQ(3, val);\n  EXPECT_EQ(9, obj);\n}\n\nTEST_F(ThreadCachedSynchronizedTest, swap_free) {\n  folly::thread_cached_synchronized obj{9};\n  int val = 3;\n  swap(obj, val);\n  EXPECT_EQ(9, val);\n  EXPECT_EQ(3, obj);\n}\n\nTEST_F(ThreadCachedSynchronizedTest, not_default_constructible) {\n  struct foo {\n    int value;\n    foo() = delete;\n    explicit foo(int value_) noexcept : value{value_} {}\n    foo(foo const&) = default;\n    foo& operator=(foo const&) = default;\n  };\n  folly::thread_cached_synchronized obj{foo{3}};\n  EXPECT_EQ(3, foo(obj).value);\n}\n"
  },
  {
    "path": "folly/concurrency/test/UnboundedQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/UnboundedQueue.h>\n\n#include <folly/MPMCQueue.h>\n#include <folly/ProducerConsumerQueue.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\n#include <glog/logging.h>\n#include <folly/synchronization/test/Barrier.h>\n\n#include <atomic>\n#include <iomanip>\n#include <thread>\n\nDEFINE_bool(bench, false, \"run benchmark\");\nDEFINE_int32(reps, 10, \"number of reps\");\nDEFINE_int32(ops, 1000000, \"number of operations per rep\");\nDEFINE_int64(capacity, 256 * 1024, \"capacity\");\n\ntemplate <typename T, bool MayBlock>\nusing USPSC = folly::USPSCQueue<T, MayBlock>;\n\ntemplate <typename T, bool MayBlock>\nusing UMPSC = folly::UMPSCQueue<T, MayBlock>;\n\ntemplate <typename T, bool MayBlock>\nusing USPMC = folly::USPMCQueue<T, MayBlock>;\n\ntemplate <typename T, bool MayBlock>\nusing UMPMC = folly::UMPMCQueue<T, MayBlock>;\n\n// check functions for inspecting inlined native code\n\nFOLLY_ATTR_WEAK void noop(folly::Optional<int>&&) {}\n\nextern \"C\" FOLLY_KEEP void check_uspsc_mayblock_dequeue_ref(\n    USPSC<int, true>& queue, int& item) {\n  queue.dequeue(item);\n}\nextern \"C\" FOLLY_KEEP bool check_uspsc_mayblock_try_dequeue_ref(\n    USPSC<int, true>& queue, int& item) {\n  return queue.try_dequeue(item);\n}\nextern \"C\" FOLLY_KEEP bool check_uspsc_mayblock_try_dequeue_for_ref(\n    USPSC<int, true>& queue,\n    int& item,\n    std::chrono::milliseconds const& timeout) {\n  return queue.try_dequeue_for(item, timeout);\n}\nextern \"C\" FOLLY_KEEP bool check_uspsc_mayblock_try_dequeue_until_ref(\n    USPSC<int, true>& queue,\n    int& item,\n    std::chrono::steady_clock::time_point const& deadline) {\n  return queue.try_dequeue_until(item, deadline);\n}\n\nextern \"C\" FOLLY_KEEP int check_uspsc_mayblock_dequeue_ret(\n    USPSC<int, true>& queue) {\n  return queue.dequeue();\n}\nextern \"C\" FOLLY_KEEP void check_uspsc_mayblock_try_dequeue_ret(\n    USPSC<int, true>& queue) {\n  noop(queue.try_dequeue());\n}\nextern \"C\" FOLLY_KEEP void check_uspsc_mayblock_try_dequeue_for_ret(\n    USPSC<int, true>& queue, std::chrono::milliseconds const& timeout) {\n  noop(queue.try_dequeue_for(timeout));\n}\nextern \"C\" FOLLY_KEEP void check_uspsc_mayblock_try_dequeue_until_ret(\n    USPSC<int, true>& queue,\n    std::chrono::steady_clock::time_point const& deadline) {\n  noop(queue.try_dequeue_until(deadline));\n}\n\nextern \"C\" FOLLY_KEEP void check_umpmc_mayblock_dequeue_ref(\n    UMPMC<int, true>& queue, int& item) {\n  queue.dequeue(item);\n}\nextern \"C\" FOLLY_KEEP bool check_umpmc_mayblock_try_dequeue_ref(\n    UMPMC<int, true>& queue, int& item) {\n  return queue.try_dequeue(item);\n}\nextern \"C\" FOLLY_KEEP bool check_umpmc_mayblock_try_dequeue_for_ref(\n    UMPMC<int, true>& queue,\n    int& item,\n    std::chrono::milliseconds const& timeout) {\n  return queue.try_dequeue_for(item, timeout);\n}\nextern \"C\" FOLLY_KEEP bool check_umpmc_mayblock_try_dequeue_until_ref(\n    UMPMC<int, true>& queue,\n    int& item,\n    std::chrono::steady_clock::time_point const& deadline) {\n  return queue.try_dequeue_until(item, deadline);\n}\n\nextern \"C\" FOLLY_KEEP int check_umpmc_mayblock_dequeue_ret(\n    UMPMC<int, true>& queue) {\n  return queue.dequeue();\n}\nextern \"C\" FOLLY_KEEP void check_umpmc_mayblock_try_dequeue_ret(\n    UMPMC<int, true>& queue) {\n  noop(queue.try_dequeue());\n}\nextern \"C\" FOLLY_KEEP void check_umpmc_mayblock_try_dequeue_for_ret(\n    UMPMC<int, true>& queue, std::chrono::milliseconds const& timeout) {\n  noop(queue.try_dequeue_for(timeout));\n}\nextern \"C\" FOLLY_KEEP void check_umpmc_mayblock_try_dequeue_until_ret(\n    UMPMC<int, true>& queue,\n    std::chrono::steady_clock::time_point const& deadline) {\n  noop(queue.try_dequeue_until(deadline));\n}\n\ntemplate <template <typename, bool> class Q, bool MayBlock>\nvoid basic_test() {\n  Q<int, MayBlock> q;\n  ASSERT_TRUE(q.empty());\n  ASSERT_EQ(q.size(), 0);\n  int v = -1;\n  ASSERT_FALSE(q.try_dequeue(v));\n\n  q.enqueue(1);\n  ASSERT_FALSE(q.empty());\n  ASSERT_EQ(q.size(), 1);\n\n  q.enqueue(2);\n  ASSERT_EQ(q.size(), 2);\n  ASSERT_FALSE(q.empty());\n\n  ASSERT_TRUE(q.try_dequeue(v));\n  ASSERT_EQ(v, 1);\n  ASSERT_FALSE(q.empty());\n  ASSERT_EQ(q.size(), 1);\n\n  ASSERT_TRUE(q.try_dequeue(v));\n  ASSERT_EQ(v, 2);\n  ASSERT_TRUE(q.empty());\n  ASSERT_EQ(q.size(), 0);\n}\n\nTEST(UnboundedQueue, basic) {\n  basic_test<USPSC, false>();\n  basic_test<UMPSC, false>();\n  basic_test<USPMC, false>();\n  basic_test<UMPMC, false>();\n  basic_test<USPSC, true>();\n  basic_test<UMPSC, true>();\n  basic_test<USPMC, true>();\n  basic_test<UMPMC, true>();\n}\n\ntemplate <template <typename, bool> class Q, bool MayBlock>\nvoid timeout_test() {\n  Q<int, MayBlock> q;\n  int v;\n  ASSERT_FALSE(q.try_dequeue_until(\n      v, std::chrono::steady_clock::now() + std::chrono::microseconds(1)));\n  ASSERT_FALSE(q.try_dequeue_for(v, std::chrono::microseconds(1)));\n  q.enqueue(10);\n  ASSERT_TRUE(q.try_dequeue_until(\n      v, std::chrono::steady_clock::now() + std::chrono::microseconds(1)));\n  ASSERT_EQ(v, 10);\n}\n\nTEST(UnboundedQueue, timeout) {\n  timeout_test<USPSC, false>();\n  timeout_test<UMPSC, false>();\n  timeout_test<USPMC, false>();\n  timeout_test<UMPMC, false>();\n  timeout_test<USPSC, true>();\n  timeout_test<UMPSC, true>();\n  timeout_test<USPMC, true>();\n  timeout_test<UMPMC, true>();\n}\n\ntemplate <template <typename, bool> class Q, bool MayBlock>\nvoid peek_test() {\n  Q<int, MayBlock> q;\n  auto res = q.try_peek();\n  ASSERT_FALSE(res);\n  for (int i = 0; i < 1000; ++i) {\n    q.enqueue(i);\n  }\n  for (int i = 0; i < 700; ++i) {\n    int v;\n    q.dequeue(v);\n  }\n  res = q.try_peek();\n  ASSERT_TRUE(res);\n  ASSERT_EQ(*res, 700);\n}\n\nTEST(UnboundedQueue, peek) {\n  peek_test<USPSC, false>();\n  peek_test<UMPSC, false>();\n  peek_test<USPSC, true>();\n  peek_test<UMPSC, true>();\n}\n\nTEST(UnboundedQueue, cleanupOnDestruction) {\n  struct Foo {\n    int* p_{nullptr};\n    explicit Foo(int* p) : p_(p) {}\n    Foo(Foo&& o) noexcept : p_(std::exchange(o.p_, nullptr)) {}\n    ~Foo() {\n      if (p_) {\n        ++(*p_);\n      }\n    }\n    [[maybe_unused]] Foo& operator=(Foo&& o) noexcept {\n      p_ = std::exchange(o.p_, nullptr);\n      return *this;\n    }\n  };\n  int count = 0;\n  int num = 3;\n  {\n    folly::UMPMCQueue<Foo, false> q;\n    for (int i = 0; i < num; ++i) {\n      Foo foo(&count);\n      q.enqueue(std::move(foo));\n    }\n  }\n  EXPECT_EQ(count, num);\n}\n\ntemplate <typename ProdFunc, typename ConsFunc, typename EndFunc>\ninline uint64_t run_once(\n    int nprod,\n    int ncons,\n    const ProdFunc& prodFn,\n    const ConsFunc& consFn,\n    const EndFunc& endFn) {\n  folly::test::Barrier barrier(1 + nprod + ncons);\n\n  /* producers */\n  std::vector<std::thread> prodThr(nprod);\n  for (int tid = 0; tid < nprod; ++tid) {\n    prodThr[tid] = std::thread([&, tid] {\n      barrier.wait(); // A - wait for thread start\n      barrier.wait(); // B - init the work\n      prodFn(tid);\n      barrier.wait(); // C - join the work\n    });\n  }\n\n  /* consumers */\n  std::vector<std::thread> consThr(ncons);\n  for (int tid = 0; tid < ncons; ++tid) {\n    consThr[tid] = std::thread([&, tid] {\n      barrier.wait(); // A - wait for thread start\n      barrier.wait(); // B - init the work\n      consFn(tid);\n      barrier.wait(); // C - join the work\n    });\n  }\n\n  barrier.wait(); // A - wait for thread start\n\n  /* begin time measurement */\n  auto const tbegin = std::chrono::steady_clock::now();\n\n  barrier.wait(); // B - init the work\n  barrier.wait(); // C - join the work\n\n  /* end time measurement */\n  auto const tend = std::chrono::steady_clock::now();\n\n  /* wait for completion */\n  for (int i = 0; i < nprod; ++i) {\n    prodThr[i].join();\n  }\n  for (int i = 0; i < ncons; ++i) {\n    consThr[i].join();\n  }\n\n  endFn();\n  auto const dur = tend - tbegin;\n  return std::chrono::duration_cast<std::chrono::nanoseconds>(dur).count();\n}\n\ntemplate <bool SingleProducer, bool SingleConsumer, bool MayBlock>\nvoid enq_deq_test(const int nprod, const int ncons) {\n  if (SingleProducer) {\n    ASSERT_EQ(nprod, 1);\n  }\n  if (SingleConsumer) {\n    ASSERT_EQ(ncons, 1);\n  }\n\n  int ops = 1000;\n  folly::UnboundedQueue<int, SingleProducer, SingleConsumer, MayBlock, 4> q;\n  std::atomic<uint64_t> sum(0);\n\n  auto prod = [&](int tid) {\n    for (int i = tid; i < ops; i += nprod) {\n      q.enqueue(i);\n    }\n  };\n\n  auto cons = [&](int tid) {\n    uint64_t mysum = 0;\n    for (int i = tid; i < ops; i += ncons) {\n      int v = -1;\n      int vpeek = -1;\n\n      if constexpr (SingleConsumer) {\n        while (true) {\n          auto res = q.try_peek();\n          if (res) {\n            vpeek = *res;\n            break;\n          }\n        }\n      }\n      if ((i % 3) == 0) {\n        while (!q.try_dequeue(v)) {\n          /* keep trying */;\n        }\n      } else if ((i % 3) == 1) {\n        auto duration = std::chrono::milliseconds(1);\n        while (!q.try_dequeue_for(v, duration)) {\n          /* keep trying */;\n        }\n      } else {\n        q.dequeue(v);\n      }\n      if (nprod == 1 && ncons == 1) {\n        ASSERT_EQ(v, i);\n      }\n      if (SingleConsumer) {\n        ASSERT_EQ(v, vpeek);\n      }\n      mysum += v;\n    }\n    sum.fetch_add(mysum);\n  };\n\n  auto endfn = [&] {\n    uint64_t expected = (ops) * (ops - 1) / 2;\n    uint64_t actual = sum.load();\n    ASSERT_EQ(expected, actual);\n  };\n  run_once(nprod, ncons, prod, cons, endfn);\n}\n\nTEST(UnboundedQueue, enqDeq) {\n  /* SPSC */\n  enq_deq_test<true, true, false>(1, 1);\n  enq_deq_test<true, true, true>(1, 1);\n  /* MPSC */\n  enq_deq_test<false, true, false>(1, 1);\n  enq_deq_test<false, true, true>(1, 1);\n  enq_deq_test<false, true, false>(2, 1);\n  enq_deq_test<false, true, true>(2, 1);\n  enq_deq_test<false, true, false>(10, 1);\n  enq_deq_test<false, true, true>(10, 1);\n  /* SPMC */\n  enq_deq_test<true, false, false>(1, 1);\n  enq_deq_test<true, false, true>(1, 1);\n  enq_deq_test<true, false, false>(1, 2);\n  enq_deq_test<true, false, true>(1, 2);\n  enq_deq_test<true, false, false>(1, 10);\n  enq_deq_test<true, false, true>(1, 10);\n  /* MPMC */\n  enq_deq_test<false, false, false>(1, 1);\n  enq_deq_test<false, false, true>(1, 1);\n  enq_deq_test<false, false, false>(2, 1);\n  enq_deq_test<false, false, true>(2, 1);\n  enq_deq_test<false, false, false>(10, 1);\n  enq_deq_test<false, false, true>(10, 1);\n  enq_deq_test<false, false, false>(1, 2);\n  enq_deq_test<false, false, true>(1, 2);\n  enq_deq_test<false, false, false>(1, 10);\n  enq_deq_test<false, false, true>(1, 10);\n  enq_deq_test<false, false, false>(2, 2);\n  enq_deq_test<false, false, true>(2, 2);\n  enq_deq_test<false, false, false>(10, 10);\n  enq_deq_test<false, false, true>(10, 10);\n}\n\ntemplate <typename RepFunc>\nuint64_t runBench(const std::string& name, uint64_t ops, const RepFunc& repFn) {\n  uint64_t reps = FLAGS_reps;\n  uint64_t min = UINTMAX_MAX;\n  uint64_t max = 0;\n  uint64_t sum = 0;\n\n  std::vector<uint64_t> durs(reps);\n\n  repFn(); // sometimes first run is outlier\n  for (uint64_t r = 0; r < reps; ++r) {\n    uint64_t dur = repFn();\n    durs[r] = dur;\n    sum += dur;\n    min = std::min(min, dur);\n    max = std::max(max, dur);\n    // if each rep takes too long run at least 3 reps\n    const uint64_t minute = 60000000000ULL;\n    if (sum > minute && r >= 2) {\n      reps = r + 1;\n      break;\n    }\n  }\n\n  const std::string unit = \" ns\";\n  uint64_t avg = sum / reps;\n  uint64_t res = min;\n  uint64_t varsum = 0;\n  for (uint64_t r = 0; r < reps; ++r) {\n    auto term = int64_t(reps * durs[r]) - int64_t(sum);\n    varsum += term * term;\n  }\n  uint64_t dev = uint64_t(std::sqrt(varsum) * std::pow(reps, -1.5));\n  std::cout << name;\n  std::cout << \"   \" << std::setw(4) << max / ops << unit;\n  std::cout << \"   \" << std::setw(4) << avg / ops << unit;\n  std::cout << \"   \" << std::setw(4) << dev / ops << unit;\n  std::cout << \"   \" << std::setw(4) << res / ops << unit;\n  std::cout << std::endl;\n  return res;\n}\n\ntemplate <template <typename, bool> class Q, typename T, int Op>\nuint64_t bench(const int nprod, const int ncons, const std::string& name) {\n  const uint64_t ops = FLAGS_ops;\n  auto repFn = [&, ops] {\n    Q<T, Op == 3 || Op == 4 || Op == 5> q;\n    std::atomic<uint64_t> sum(0);\n    auto prod = [&](int tid) {\n      for (uint64_t i = tid; i < ops; i += nprod) {\n        q.enqueue(i);\n      }\n    };\n    auto cons = [&](int tid) {\n      uint64_t mysum = 0;\n      for (uint64_t i = tid; i < ops; i += ncons) {\n        T v;\n        if (Op == 0 || Op == 3) {\n          while (FOLLY_UNLIKELY(!q.try_dequeue(v))) {\n            /* keep trying */;\n          }\n        } else if (Op == 1 || Op == 4) {\n          auto duration = std::chrono::microseconds(1000);\n          while (FOLLY_UNLIKELY(!q.try_dequeue_for(v, duration))) {\n            /* keep trying */;\n          }\n        } else {\n          DCHECK(Op == 2 || Op == 5);\n          q.dequeue(v);\n        }\n        if (nprod == 1 && ncons == 1) {\n          DCHECK_EQ(int(v), i);\n        }\n        mysum += v;\n      }\n      sum.fetch_add(mysum);\n    };\n    auto endfn = [&] {\n      uint64_t expected = (ops) * (ops - 1) / 2;\n      uint64_t actual = sum.load();\n      DCHECK_EQ(expected, actual);\n    };\n    return run_once(nprod, ncons, prod, cons, endfn);\n  };\n  return runBench(name, ops, repFn);\n}\n\n/* For performance comparison */\ntemplate <typename T>\nclass MPMC {\n  folly::MPMCQueue<T> q_;\n\n public:\n  MPMC() : q_(FLAGS_capacity) {}\n\n  template <typename... Args>\n  void enqueue(Args&&... args) {\n    q_.blockingWrite(std::forward<Args>(args)...);\n  }\n\n  void dequeue(T& item) { q_.blockingRead(item); }\n\n  bool try_dequeue(T& item) { return q_.read(item); }\n\n  template <typename Rep, typename Period>\n  bool try_dequeue_for(\n      T& item, const std::chrono::duration<Rep, Period>& duration) noexcept {\n    auto deadline = std::chrono::steady_clock::now() + duration;\n    return q_.tryReadUntil(deadline, item);\n  }\n};\n\ntemplate <typename T, bool ignore>\nusing FMPMC = MPMC<T>;\n\ntemplate <typename T>\nclass PCQ {\n  folly::ProducerConsumerQueue<T> q_;\n\n public:\n  PCQ() : q_(FLAGS_capacity) {}\n\n  template <typename... Args>\n  void enqueue(Args&&... args) {\n    while (!q_.write(std::forward<Args>(args)...)) {\n      /* keep trying*/;\n    }\n  }\n\n  void dequeue(T&) { ASSERT_TRUE(false); }\n\n  bool try_dequeue(T& item) { return q_.read(item); }\n\n  template <typename Rep, typename Period>\n  bool try_dequeue_for(T&, const std::chrono::duration<Rep, Period>&) noexcept {\n    return false;\n  }\n};\n\ntemplate <typename T, bool ignore>\nusing FPCQ = PCQ<T>;\n\ntemplate <size_t M>\nstruct IntArray {\n  int a[M];\n  IntArray() {}\n  /* implicit */ IntArray(int v) {\n    for (size_t i = 0; i < M; ++i) {\n      a[i] = v;\n    }\n  }\n  operator int() { return a[0]; }\n};\n\nvoid dottedLine() {\n  std::cout\n      << \"........................................................................\"\n      << std::endl;\n}\n\ntemplate <typename T>\nvoid type_benches(const int np, const int nc, const std::string& name) {\n  std::cout << name << \"=====================================================\"\n            << std::endl;\n  if (np == 1 && nc == 1) {\n    bench<USPSC, T, 0>(1, 1, \"Unbounded SPSC try   spin only  \");\n    bench<USPSC, T, 1>(1, 1, \"Unbounded SPSC timed spin only  \");\n    bench<USPSC, T, 2>(1, 1, \"Unbounded SPSC wait  spin only  \");\n    bench<USPSC, T, 3>(1, 1, \"Unbounded SPSC try   may block  \");\n    bench<USPSC, T, 4>(1, 1, \"Unbounded SPSC timed may block  \");\n    bench<USPSC, T, 5>(1, 1, \"Unbounded SPSC wait  may block  \");\n    dottedLine();\n  }\n  if (nc == 1) {\n    bench<UMPSC, T, 0>(np, 1, \"Unbounded MPSC try   spin only  \");\n    bench<UMPSC, T, 1>(np, 1, \"Unbounded MPSC timed spin only  \");\n    bench<UMPSC, T, 2>(np, 1, \"Unbounded MPSC wait  spin only  \");\n    bench<UMPSC, T, 3>(np, 1, \"Unbounded MPSC try   may block  \");\n    bench<UMPSC, T, 4>(np, 1, \"Unbounded MPSC timed may block  \");\n    bench<UMPSC, T, 5>(np, 1, \"Unbounded MPSC wait  may block  \");\n    dottedLine();\n  }\n  if (np == 1) {\n    bench<USPMC, T, 0>(1, nc, \"Unbounded SPMC try   spin only  \");\n    bench<USPMC, T, 1>(1, nc, \"Unbounded SPMC timed spin only  \");\n    bench<USPMC, T, 2>(1, nc, \"Unbounded SPMC wait  spin only  \");\n    bench<USPMC, T, 3>(1, nc, \"Unbounded SPMC try   may block  \");\n    bench<USPMC, T, 4>(1, nc, \"Unbounded SPMC timed may block  \");\n    bench<USPMC, T, 5>(1, nc, \"Unbounded SPMC wait  may block  \");\n    dottedLine();\n  }\n  bench<UMPMC, T, 0>(np, nc, \"Unbounded MPMC try   spin only  \");\n  bench<UMPMC, T, 1>(np, nc, \"Unbounded MPMC timed spin only  \");\n  bench<UMPMC, T, 2>(np, nc, \"Unbounded MPMC wait  spin only  \");\n  bench<UMPMC, T, 3>(np, nc, \"Unbounded MPMC try   may block  \");\n  bench<UMPMC, T, 4>(np, nc, \"Unbounded MPMC timed may block  \");\n  bench<UMPMC, T, 5>(np, nc, \"Unbounded MPMC wait  may block  \");\n  dottedLine();\n  if (np == 1 && nc == 1) {\n    bench<FPCQ, T, 0>(1, 1, \"folly::PCQ  read                \");\n    dottedLine();\n  }\n  bench<FMPMC, T, 3>(np, nc, \"folly::MPMC  read               \");\n  bench<FMPMC, T, 4>(np, nc, \"folly::MPMC  tryReadUntil       \");\n  bench<FMPMC, T, 5>(np, nc, \"folly::MPMC  blockingRead       \");\n  std::cout\n      << \"========================================================================\"\n      << std::endl;\n}\n\nvoid benches(const int np, const int nc) {\n  std::cout << \"====================== \" << std::setw(2) << np << \" prod\"\n            << \"  \" << std::setw(2) << nc << \" cons\"\n            << \" ================================\" << std::endl;\n  type_benches<uint32_t>(np, nc, \"=== uint32_t ======\");\n  // Benchmarks for other element sizes can be added as follows:\n  //   type_benches<IntArray<4>>(np, nc, \"=== IntArray<4> ===\");\n}\n\nTEST(UnboundedQueue, bench) {\n  if (!FLAGS_bench) {\n    return;\n  }\n  std::cout\n      << \"========================================================================\"\n      << std::endl;\n  std::cout << std::setw(2) << FLAGS_reps << \" reps of \" << std::setw(8)\n            << FLAGS_ops << \" handoffs\\n\";\n  dottedLine();\n  std::cout << \"$ numactl -N 1 $dir/unbounded_queue_test --bench\\n\";\n  dottedLine();\n  std::cout << \"Using capacity \" << FLAGS_capacity\n            << \" for folly::ProducerConsumerQueue and\\n\"\n            << \"folly::MPMCQueue\\n\";\n  std::cout\n      << \"========================================================================\"\n      << std::endl;\n  std::cout\n      << \"Test name                         Max time  Avg time  Dev time  Min time\"\n      << std::endl;\n\n  for (int nc : {1, 2, 4, 8, 16, 32}) {\n    int np = 1;\n    benches(np, nc);\n  }\n\n  for (int np : {1, 2, 4, 8, 16, 32}) {\n    int nc = 1;\n    benches(np, nc);\n  }\n\n  for (int np : {2, 4, 8, 16, 32}) {\n    for (int nc : {2, 4, 8, 16, 32}) {\n      benches(np, nc);\n    }\n  }\n}\n\n/*\n==============================================================\n10 reps of  1000000 handoffs\n..............................................................\n$ numactl -N 1 $dir/unbounded_queue_test --bench\n..............................................................\nUsing capacity 262144 for folly::ProducerConsumerQueue and\nfolly::MPMCQueue\n==============================================================\nTest name                         Max time  Avg time  Min time\n======================  1 prod   1 cons ======================\n=== uint32_t =================================================\nUnbounded SPSC try   spin only        5 ns      5 ns      5 ns\nUnbounded SPSC timed spin only        5 ns      5 ns      5 ns\nUnbounded SPSC wait  spin only        6 ns      6 ns      5 ns\nUnbounded SPSC try   may block       38 ns     37 ns     35 ns\nUnbounded SPSC timed may block       38 ns     36 ns     34 ns\nUnbounded SPSC wait  may block       34 ns     34 ns     33 ns\n..............................................................\nUnbounded MPSC try   spin only       45 ns     43 ns     42 ns\nUnbounded MPSC timed spin only       47 ns     43 ns     42 ns\nUnbounded MPSC wait  spin only       45 ns     43 ns     41 ns\nUnbounded MPSC try   may block       55 ns     52 ns     51 ns\nUnbounded MPSC timed may block       54 ns     52 ns     51 ns\nUnbounded MPSC wait  may block       51 ns     50 ns     49 ns\n..............................................................\nUnbounded SPMC try   spin only       18 ns     17 ns     16 ns\nUnbounded SPMC timed spin only       23 ns     21 ns     18 ns\nUnbounded SPMC wait  spin only       22 ns     19 ns     16 ns\nUnbounded SPMC try   may block       30 ns     26 ns     22 ns\nUnbounded SPMC timed may block       32 ns     24 ns     20 ns\nUnbounded SPMC wait  may block       49 ns     35 ns     29 ns\n..............................................................\nUnbounded MPMC try   spin only       25 ns     24 ns     24 ns\nUnbounded MPMC timed spin only       38 ns     35 ns     30 ns\nUnbounded MPMC wait  spin only       41 ns     39 ns     37 ns\nUnbounded MPMC try   may block       53 ns     52 ns     51 ns\nUnbounded MPMC timed may block       52 ns     51 ns     49 ns\nUnbounded MPMC wait  may block       53 ns     51 ns     50 ns\n..............................................................\nfolly::PCQ  read                     16 ns     11 ns      7 ns\n..............................................................\nfolly::MPMC  read                    52 ns     52 ns     51 ns\nfolly::MPMC  tryReadUntil            96 ns     90 ns     55 ns\nfolly::MPMC  blockingRead            61 ns     56 ns     50 ns\n==============================================================\n======================  1 prod   2 cons ======================\n=== uint32_t =================================================\nUnbounded SPMC try   spin only       76 ns     68 ns     53 ns\nUnbounded SPMC timed spin only       79 ns     71 ns     65 ns\nUnbounded SPMC wait  spin only       39 ns     35 ns     32 ns\nUnbounded SPMC try   may block       83 ns     81 ns     76 ns\nUnbounded SPMC timed may block       86 ns     63 ns     23 ns\nUnbounded SPMC wait  may block       38 ns     36 ns     34 ns\n..............................................................\nUnbounded MPMC try   spin only       86 ns     79 ns     64 ns\nUnbounded MPMC timed spin only       84 ns     77 ns     74 ns\nUnbounded MPMC wait  spin only       36 ns     35 ns     34 ns\nUnbounded MPMC try   may block       83 ns     79 ns     75 ns\nUnbounded MPMC timed may block       83 ns     76 ns     63 ns\nUnbounded MPMC wait  may block       56 ns     48 ns     36 ns\n..............................................................\nfolly::MPMC  read                   103 ns     93 ns     68 ns\nfolly::MPMC  tryReadUntil           109 ns    102 ns     91 ns\nfolly::MPMC  blockingRead            61 ns     58 ns     54 ns\n==============================================================\n======================  1 prod   4 cons ======================\n=== uint32_t =================================================\nUnbounded SPMC try   spin only      116 ns    109 ns     97 ns\nUnbounded SPMC timed spin only      117 ns    111 ns    108 ns\nUnbounded SPMC wait  spin only       43 ns     40 ns     37 ns\nUnbounded SPMC try   may block      127 ns    113 ns     98 ns\nUnbounded SPMC timed may block      116 ns    109 ns     97 ns\nUnbounded SPMC wait  may block       45 ns     43 ns     40 ns\n..............................................................\nUnbounded MPMC try   spin only      121 ns    113 ns    102 ns\nUnbounded MPMC timed spin only      118 ns    108 ns     88 ns\nUnbounded MPMC wait  spin only       45 ns     41 ns     34 ns\nUnbounded MPMC try   may block      117 ns    108 ns     96 ns\nUnbounded MPMC timed may block      118 ns    109 ns     99 ns\nUnbounded MPMC wait  may block       62 ns     53 ns     43 ns\n..............................................................\nfolly::MPMC  read                   139 ns    130 ns    111 ns\nfolly::MPMC  tryReadUntil           205 ns    135 ns    115 ns\nfolly::MPMC  blockingRead           104 ns     74 ns     54 ns\n==============================================================\n======================  1 prod   8 cons ======================\n=== uint32_t =================================================\nUnbounded SPMC try   spin only      169 ns    163 ns    157 ns\nUnbounded SPMC timed spin only      167 ns    158 ns    133 ns\nUnbounded SPMC wait  spin only       44 ns     39 ns     36 ns\nUnbounded SPMC try   may block      170 ns    165 ns    156 ns\nUnbounded SPMC timed may block      172 ns    163 ns    153 ns\nUnbounded SPMC wait  may block       49 ns     40 ns     35 ns\n..............................................................\nUnbounded MPMC try   spin only      166 ns    158 ns    149 ns\nUnbounded MPMC timed spin only      171 ns    161 ns    145 ns\nUnbounded MPMC wait  spin only       62 ns     52 ns     42 ns\nUnbounded MPMC try   may block      169 ns    161 ns    149 ns\nUnbounded MPMC timed may block      170 ns    160 ns    147 ns\nUnbounded MPMC wait  may block       70 ns     63 ns     61 ns\n..............................................................\nfolly::MPMC  read                   174 ns    167 ns    159 ns\nfolly::MPMC  tryReadUntil           349 ns    171 ns    148 ns\nfolly::MPMC  blockingRead           182 ns    138 ns    115 ns\n==============================================================\n======================  1 prod  16 cons ======================\n=== uint32_t =================================================\nUnbounded SPMC try   spin only      219 ns    198 ns    190 ns\nUnbounded SPMC timed spin only      202 ns    198 ns    193 ns\nUnbounded SPMC wait  spin only       36 ns     36 ns     35 ns\nUnbounded SPMC try   may block      202 ns    195 ns    190 ns\nUnbounded SPMC timed may block      208 ns    197 ns    190 ns\nUnbounded SPMC wait  may block       96 ns     77 ns     64 ns\n..............................................................\nUnbounded MPMC try   spin only      204 ns    198 ns    194 ns\nUnbounded MPMC timed spin only      202 ns    195 ns    190 ns\nUnbounded MPMC wait  spin only       61 ns     59 ns     57 ns\nUnbounded MPMC try   may block      206 ns    196 ns    191 ns\nUnbounded MPMC timed may block      204 ns    198 ns    192 ns\nUnbounded MPMC wait  may block      100 ns     88 ns     84 ns\n..............................................................\nfolly::MPMC  read                   210 ns    191 ns    182 ns\nfolly::MPMC  tryReadUntil           574 ns    248 ns    192 ns\nfolly::MPMC  blockingRead          1400 ns   1319 ns   1227 ns\n==============================================================\n======================  1 prod  32 cons ======================\n=== uint32_t =================================================\nUnbounded SPMC try   spin only      209 ns    205 ns    199 ns\nUnbounded SPMC timed spin only      208 ns    205 ns    200 ns\nUnbounded SPMC wait  spin only      175 ns     51 ns     33 ns\nUnbounded SPMC try   may block      215 ns    203 ns    186 ns\nUnbounded SPMC timed may block      453 ns    334 ns    204 ns\nUnbounded SPMC wait  may block      110 ns     87 ns     55 ns\n..............................................................\nUnbounded MPMC try   spin only      328 ns    218 ns    197 ns\nUnbounded MPMC timed spin only      217 ns    206 ns    200 ns\nUnbounded MPMC wait  spin only      147 ns     85 ns     58 ns\nUnbounded MPMC try   may block      310 ns    223 ns    199 ns\nUnbounded MPMC timed may block      461 ns    275 ns    196 ns\nUnbounded MPMC wait  may block      148 ns    111 ns     78 ns\n..............................................................\nfolly::MPMC  read                   280 ns    215 ns    194 ns\nfolly::MPMC  tryReadUntil          28740 ns   13508 ns    212 ns\nfolly::MPMC  blockingRead          1343 ns   1293 ns   1269 ns\n==============================================================\n======================  1 prod   1 cons ======================\n=== uint32_t =================================================\nUnbounded SPSC try   spin only        5 ns      5 ns      5 ns\nUnbounded SPSC timed spin only        8 ns      6 ns      6 ns\nUnbounded SPSC wait  spin only        6 ns      6 ns      5 ns\nUnbounded SPSC try   may block       37 ns     36 ns     35 ns\nUnbounded SPSC timed may block       37 ns     36 ns     35 ns\nUnbounded SPSC wait  may block       35 ns     35 ns     34 ns\n..............................................................\nUnbounded MPSC try   spin only       43 ns     42 ns     41 ns\nUnbounded MPSC timed spin only       45 ns     42 ns     42 ns\nUnbounded MPSC wait  spin only       44 ns     43 ns     42 ns\nUnbounded MPSC try   may block       55 ns     51 ns     50 ns\nUnbounded MPSC timed may block       61 ns     52 ns     50 ns\nUnbounded MPSC wait  may block       54 ns     52 ns     50 ns\n..............................................................\nUnbounded SPMC try   spin only       18 ns     17 ns     17 ns\nUnbounded SPMC timed spin only       23 ns     19 ns     17 ns\nUnbounded SPMC wait  spin only       20 ns     17 ns     15 ns\nUnbounded SPMC try   may block       30 ns     23 ns     19 ns\nUnbounded SPMC timed may block       23 ns     19 ns     17 ns\nUnbounded SPMC wait  may block       36 ns     31 ns     26 ns\n..............................................................\nUnbounded MPMC try   spin only       25 ns     23 ns     17 ns\nUnbounded MPMC timed spin only       37 ns     34 ns     25 ns\nUnbounded MPMC wait  spin only       40 ns     38 ns     36 ns\nUnbounded MPMC try   may block       51 ns     49 ns     48 ns\nUnbounded MPMC timed may block       53 ns     50 ns     48 ns\nUnbounded MPMC wait  may block       53 ns     49 ns     34 ns\n..............................................................\nfolly::PCQ  read                     15 ns     12 ns      7 ns\n..............................................................\nfolly::MPMC  read                    53 ns     51 ns     50 ns\nfolly::MPMC  tryReadUntil           100 ns     96 ns     90 ns\nfolly::MPMC  blockingRead            75 ns     59 ns     52 ns\n==============================================================\n======================  2 prod   1 cons ======================\n=== uint32_t =================================================\nUnbounded MPSC try   spin only       49 ns     49 ns     46 ns\nUnbounded MPSC timed spin only       52 ns     50 ns     49 ns\nUnbounded MPSC wait  spin only       53 ns     52 ns     51 ns\nUnbounded MPSC try   may block       63 ns     60 ns     57 ns\nUnbounded MPSC timed may block       64 ns     61 ns     54 ns\nUnbounded MPSC wait  may block       62 ns     59 ns     35 ns\n..............................................................\nUnbounded MPMC try   spin only       44 ns     41 ns     38 ns\nUnbounded MPMC timed spin only       50 ns     49 ns     49 ns\nUnbounded MPMC wait  spin only       51 ns     49 ns     49 ns\nUnbounded MPMC try   may block       63 ns     60 ns     57 ns\nUnbounded MPMC timed may block       62 ns     60 ns     57 ns\nUnbounded MPMC wait  may block       62 ns     60 ns     58 ns\n..............................................................\nfolly::MPMC  read                    78 ns     57 ns     52 ns\nfolly::MPMC  tryReadUntil            78 ns     72 ns     70 ns\nfolly::MPMC  blockingRead            56 ns     54 ns     52 ns\n==============================================================\n======================  4 prod   1 cons ======================\n=== uint32_t =================================================\nUnbounded MPSC try   spin only       48 ns     47 ns     46 ns\nUnbounded MPSC timed spin only       47 ns     47 ns     46 ns\nUnbounded MPSC wait  spin only       49 ns     47 ns     47 ns\nUnbounded MPSC try   may block       61 ns     59 ns     55 ns\nUnbounded MPSC timed may block       62 ns     58 ns     46 ns\nUnbounded MPSC wait  may block       62 ns     61 ns     59 ns\n..............................................................\nUnbounded MPMC try   spin only       42 ns     42 ns     40 ns\nUnbounded MPMC timed spin only       48 ns     47 ns     45 ns\nUnbounded MPMC wait  spin only       48 ns     47 ns     46 ns\nUnbounded MPMC try   may block       63 ns     62 ns     61 ns\nUnbounded MPMC timed may block       63 ns     61 ns     51 ns\nUnbounded MPMC wait  may block       62 ns     61 ns     59 ns\n..............................................................\nfolly::MPMC  read                    56 ns     55 ns     54 ns\nfolly::MPMC  tryReadUntil           112 ns    106 ns     97 ns\nfolly::MPMC  blockingRead            47 ns     47 ns     45 ns\n==============================================================\n======================  8 prod   1 cons ======================\n=== uint32_t =================================================\nUnbounded MPSC try   spin only       44 ns     43 ns     42 ns\nUnbounded MPSC timed spin only       45 ns     44 ns     40 ns\nUnbounded MPSC wait  spin only       45 ns     44 ns     41 ns\nUnbounded MPSC try   may block       61 ns     60 ns     58 ns\nUnbounded MPSC timed may block       61 ns     59 ns     56 ns\nUnbounded MPSC wait  may block       61 ns     59 ns     56 ns\n..............................................................\nUnbounded MPMC try   spin only       43 ns     40 ns     36 ns\nUnbounded MPMC timed spin only       45 ns     44 ns     41 ns\nUnbounded MPMC wait  spin only       45 ns     43 ns     41 ns\nUnbounded MPMC try   may block       62 ns     60 ns     58 ns\nUnbounded MPMC timed may block       62 ns     59 ns     56 ns\nUnbounded MPMC wait  may block       61 ns     58 ns     54 ns\n..............................................................\nfolly::MPMC  read                   147 ns    119 ns     63 ns\nfolly::MPMC  tryReadUntil           152 ns    130 ns     97 ns\nfolly::MPMC  blockingRead           135 ns    101 ns     48 ns\n==============================================================\n====================== 16 prod   1 cons ======================\n=== uint32_t =================================================\nUnbounded MPSC try   spin only       47 ns     38 ns     35 ns\nUnbounded MPSC timed spin only       36 ns     36 ns     35 ns\nUnbounded MPSC wait  spin only       46 ns     37 ns     35 ns\nUnbounded MPSC try   may block       58 ns     47 ns     45 ns\nUnbounded MPSC timed may block       46 ns     46 ns     45 ns\nUnbounded MPSC wait  may block       47 ns     45 ns     45 ns\n..............................................................\nUnbounded MPMC try   spin only       41 ns     39 ns     35 ns\nUnbounded MPMC timed spin only       45 ns     41 ns     38 ns\nUnbounded MPMC wait  spin only       43 ns     40 ns     38 ns\nUnbounded MPMC try   may block       51 ns     49 ns     47 ns\nUnbounded MPMC timed may block       52 ns     49 ns     47 ns\nUnbounded MPMC wait  may block       59 ns     50 ns     46 ns\n..............................................................\nfolly::MPMC  read                   924 ns    839 ns    664 ns\nfolly::MPMC  tryReadUntil           968 ns    865 ns    678 ns\nfolly::MPMC  blockingRead           929 ns    727 ns    487 ns\n==============================================================\n====================== 32 prod   1 cons ======================\n=== uint32_t =================================================\nUnbounded MPSC try   spin only       90 ns     44 ns     36 ns\nUnbounded MPSC timed spin only       91 ns     43 ns     35 ns\nUnbounded MPSC wait  spin only       92 ns     55 ns     36 ns\nUnbounded MPSC try   may block       87 ns     52 ns     45 ns\nUnbounded MPSC timed may block       70 ns     48 ns     45 ns\nUnbounded MPSC wait  may block      109 ns     60 ns     45 ns\n..............................................................\nUnbounded MPMC try   spin only       47 ns     42 ns     37 ns\nUnbounded MPMC timed spin only       50 ns     46 ns     38 ns\nUnbounded MPMC wait  spin only       50 ns     42 ns     36 ns\nUnbounded MPMC try   may block      103 ns     59 ns     50 ns\nUnbounded MPMC timed may block       56 ns     52 ns     47 ns\nUnbounded MPMC wait  may block       59 ns     51 ns     46 ns\n..............................................................\nfolly::MPMC  read                  1029 ns    911 ns    694 ns\nfolly::MPMC  tryReadUntil          1023 ns    969 ns    907 ns\nfolly::MPMC  blockingRead          1024 ns    921 ns    790 ns\n==============================================================\n======================  2 prod   2 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only       83 ns     66 ns     24 ns\nUnbounded MPMC timed spin only       84 ns     74 ns     49 ns\nUnbounded MPMC wait  spin only       50 ns     49 ns     47 ns\nUnbounded MPMC try   may block       86 ns     81 ns     77 ns\nUnbounded MPMC timed may block       82 ns     74 ns     59 ns\nUnbounded MPMC wait  may block       62 ns     59 ns     56 ns\n..............................................................\nfolly::MPMC  read                    98 ns     85 ns     63 ns\nfolly::MPMC  tryReadUntil           105 ns     94 ns     83 ns\nfolly::MPMC  blockingRead            59 ns     56 ns     54 ns\n==============================================================\n======================  2 prod   4 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      114 ns    105 ns     91 ns\nUnbounded MPMC timed spin only      119 ns    107 ns    102 ns\nUnbounded MPMC wait  spin only       54 ns     53 ns     52 ns\nUnbounded MPMC try   may block      114 ns    106 ns     93 ns\nUnbounded MPMC timed may block      111 ns    100 ns     92 ns\nUnbounded MPMC wait  may block       70 ns     64 ns     60 ns\n..............................................................\nfolly::MPMC  read                   133 ns    125 ns    120 ns\nfolly::MPMC  tryReadUntil           130 ns    125 ns    114 ns\nfolly::MPMC  blockingRead            69 ns     68 ns     66 ns\n==============================================================\n======================  2 prod   8 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      169 ns    160 ns    152 ns\nUnbounded MPMC timed spin only      165 ns    158 ns    149 ns\nUnbounded MPMC wait  spin only       59 ns     54 ns     45 ns\nUnbounded MPMC try   may block      166 ns    158 ns    131 ns\nUnbounded MPMC timed may block      168 ns    163 ns    158 ns\nUnbounded MPMC wait  may block       73 ns     66 ns     60 ns\n..............................................................\nfolly::MPMC  read                   170 ns    167 ns    160 ns\nfolly::MPMC  tryReadUntil           163 ns    154 ns    146 ns\nfolly::MPMC  blockingRead            82 ns     73 ns     60 ns\n==============================================================\n======================  2 prod  16 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      207 ns    198 ns    191 ns\nUnbounded MPMC timed spin only      211 ns    198 ns    192 ns\nUnbounded MPMC wait  spin only       57 ns     55 ns     54 ns\nUnbounded MPMC try   may block      197 ns    193 ns    188 ns\nUnbounded MPMC timed may block      201 ns    195 ns    188 ns\nUnbounded MPMC wait  may block       89 ns     78 ns     70 ns\n..............................................................\nfolly::MPMC  read                   196 ns    189 ns    181 ns\nfolly::MPMC  tryReadUntil           202 ns    184 ns    173 ns\nfolly::MPMC  blockingRead           267 ns    100 ns     76 ns\n==============================================================\n======================  2 prod  32 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      228 ns    207 ns    193 ns\nUnbounded MPMC timed spin only      210 ns    205 ns    198 ns\nUnbounded MPMC wait  spin only      102 ns     71 ns     56 ns\nUnbounded MPMC try   may block      268 ns    211 ns    198 ns\nUnbounded MPMC timed may block      226 ns    205 ns    183 ns\nUnbounded MPMC wait  may block      502 ns    164 ns     67 ns\n..............................................................\nfolly::MPMC  read                   228 ns    205 ns    195 ns\nfolly::MPMC  tryReadUntil           207 ns    200 ns    192 ns\nfolly::MPMC  blockingRead           830 ns    612 ns    192 ns\n==============================================================\n======================  4 prod   2 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only       87 ns     65 ns     33 ns\nUnbounded MPMC timed spin only       79 ns     60 ns     36 ns\nUnbounded MPMC wait  spin only       47 ns     46 ns     44 ns\nUnbounded MPMC try   may block       87 ns     77 ns     52 ns\nUnbounded MPMC timed may block       86 ns     79 ns     57 ns\nUnbounded MPMC wait  may block       62 ns     61 ns     60 ns\n..............................................................\nfolly::MPMC  read                   110 ns     95 ns     60 ns\nfolly::MPMC  tryReadUntil           108 ns    104 ns     96 ns\nfolly::MPMC  blockingRead            60 ns     57 ns     47 ns\n==============================================================\n======================  4 prod   4 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      110 ns    100 ns     86 ns\nUnbounded MPMC timed spin only      113 ns    104 ns     93 ns\nUnbounded MPMC wait  spin only       49 ns     46 ns     45 ns\nUnbounded MPMC try   may block      115 ns    105 ns     84 ns\nUnbounded MPMC timed may block      119 ns    108 ns     89 ns\nUnbounded MPMC wait  may block       63 ns     61 ns     54 ns\n..............................................................\nfolly::MPMC  read                   140 ns    131 ns    113 ns\nfolly::MPMC  tryReadUntil           132 ns    129 ns    121 ns\nfolly::MPMC  blockingRead            58 ns     53 ns     48 ns\n==============================================================\n======================  4 prod   8 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      170 ns    162 ns    151 ns\nUnbounded MPMC timed spin only      174 ns    158 ns    139 ns\nUnbounded MPMC wait  spin only       51 ns     50 ns     48 ns\nUnbounded MPMC try   may block      164 ns    160 ns    154 ns\nUnbounded MPMC timed may block      165 ns    158 ns    144 ns\nUnbounded MPMC wait  may block       67 ns     62 ns     52 ns\n..............................................................\nfolly::MPMC  read                   174 ns    166 ns    156 ns\nfolly::MPMC  tryReadUntil           165 ns    160 ns    150 ns\nfolly::MPMC  blockingRead            58 ns     56 ns     49 ns\n==============================================================\n======================  4 prod  16 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      200 ns    195 ns    181 ns\nUnbounded MPMC timed spin only      200 ns    195 ns    191 ns\nUnbounded MPMC wait  spin only       51 ns     49 ns     45 ns\nUnbounded MPMC try   may block      198 ns    192 ns    188 ns\nUnbounded MPMC timed may block      199 ns    190 ns    182 ns\nUnbounded MPMC wait  may block       77 ns     66 ns     60 ns\n..............................................................\nfolly::MPMC  read                   195 ns    186 ns    175 ns\nfolly::MPMC  tryReadUntil           204 ns    187 ns    167 ns\nfolly::MPMC  blockingRead            66 ns     60 ns     57 ns\n==============================================================\n======================  4 prod  32 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      246 ns    210 ns    195 ns\nUnbounded MPMC timed spin only      217 ns    207 ns    199 ns\nUnbounded MPMC wait  spin only       66 ns     52 ns     46 ns\nUnbounded MPMC try   may block      250 ns    207 ns    197 ns\nUnbounded MPMC timed may block      208 ns    202 ns    195 ns\nUnbounded MPMC wait  may block       80 ns     66 ns     56 ns\n..............................................................\nfolly::MPMC  read                   231 ns    201 ns    190 ns\nfolly::MPMC  tryReadUntil           202 ns    199 ns    196 ns\nfolly::MPMC  blockingRead            65 ns     61 ns     57 ns\n==============================================================\n======================  8 prod   2 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only       50 ns     41 ns     39 ns\nUnbounded MPMC timed spin only       73 ns     49 ns     40 ns\nUnbounded MPMC wait  spin only       46 ns     43 ns     39 ns\nUnbounded MPMC try   may block       81 ns     62 ns     56 ns\nUnbounded MPMC timed may block       75 ns     61 ns     53 ns\nUnbounded MPMC wait  may block       61 ns     57 ns     50 ns\n..............................................................\nfolly::MPMC  read                   120 ns    102 ns     58 ns\nfolly::MPMC  tryReadUntil           119 ns    112 ns    103 ns\nfolly::MPMC  blockingRead            85 ns     71 ns     58 ns\n==============================================================\n======================  8 prod   4 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      104 ns     87 ns     39 ns\nUnbounded MPMC timed spin only      109 ns     89 ns     40 ns\nUnbounded MPMC wait  spin only       46 ns     45 ns     43 ns\nUnbounded MPMC try   may block      121 ns    101 ns     74 ns\nUnbounded MPMC timed may block      116 ns    103 ns     72 ns\nUnbounded MPMC wait  may block       62 ns     57 ns     52 ns\n..............................................................\nfolly::MPMC  read                   136 ns    130 ns    118 ns\nfolly::MPMC  tryReadUntil           132 ns    127 ns    118 ns\nfolly::MPMC  blockingRead            68 ns     61 ns     51 ns\n==============================================================\n======================  8 prod   8 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      175 ns    171 ns    162 ns\nUnbounded MPMC timed spin only      177 ns    169 ns    159 ns\nUnbounded MPMC wait  spin only       49 ns     47 ns     45 ns\nUnbounded MPMC try   may block      175 ns    171 ns    156 ns\nUnbounded MPMC timed may block      180 ns    170 ns    162 ns\nUnbounded MPMC wait  may block       63 ns     62 ns     59 ns\n..............................................................\nfolly::MPMC  read                   177 ns    162 ns    147 ns\nfolly::MPMC  tryReadUntil           170 ns    162 ns    148 ns\nfolly::MPMC  blockingRead            57 ns     53 ns     49 ns\n==============================================================\n======================  8 prod  16 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      203 ns    192 ns    185 ns\nUnbounded MPMC timed spin only      199 ns    193 ns    185 ns\nUnbounded MPMC wait  spin only       48 ns     46 ns     44 ns\nUnbounded MPMC try   may block      204 ns    194 ns    182 ns\nUnbounded MPMC timed may block      198 ns    187 ns    171 ns\nUnbounded MPMC wait  may block       63 ns     61 ns     57 ns\n..............................................................\nfolly::MPMC  read                   193 ns    185 ns    167 ns\nfolly::MPMC  tryReadUntil           199 ns    188 ns    164 ns\nfolly::MPMC  blockingRead            57 ns     52 ns     49 ns\n==============================================================\n======================  8 prod  32 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      222 ns    208 ns    198 ns\nUnbounded MPMC timed spin only      234 ns    212 ns    203 ns\nUnbounded MPMC wait  spin only       89 ns     58 ns     45 ns\nUnbounded MPMC try   may block      234 ns    207 ns    196 ns\nUnbounded MPMC timed may block      205 ns    203 ns    197 ns\nUnbounded MPMC wait  may block       65 ns     63 ns     61 ns\n..............................................................\nfolly::MPMC  read                   240 ns    204 ns    194 ns\nfolly::MPMC  tryReadUntil           205 ns    202 ns    199 ns\nfolly::MPMC  blockingRead            56 ns     52 ns     49 ns\n==============================================================\n====================== 16 prod   2 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only       52 ns     40 ns     34 ns\nUnbounded MPMC timed spin only       63 ns     47 ns     36 ns\nUnbounded MPMC wait  spin only       45 ns     39 ns     36 ns\nUnbounded MPMC try   may block       62 ns     51 ns     47 ns\nUnbounded MPMC timed may block       77 ns     52 ns     46 ns\nUnbounded MPMC wait  may block       63 ns     50 ns     46 ns\n..............................................................\nfolly::MPMC  read                   114 ns    103 ns     77 ns\nfolly::MPMC  tryReadUntil           116 ns    106 ns     85 ns\nfolly::MPMC  blockingRead            85 ns     79 ns     63 ns\n==============================================================\n====================== 16 prod   4 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      106 ns     68 ns     33 ns\nUnbounded MPMC timed spin only       88 ns     56 ns     36 ns\nUnbounded MPMC wait  spin only       46 ns     39 ns     35 ns\nUnbounded MPMC try   may block       95 ns     66 ns     47 ns\nUnbounded MPMC timed may block       80 ns     57 ns     46 ns\nUnbounded MPMC wait  may block       52 ns     48 ns     45 ns\n..............................................................\nfolly::MPMC  read                   121 ns    113 ns    104 ns\nfolly::MPMC  tryReadUntil           119 ns    110 ns    101 ns\nfolly::MPMC  blockingRead            65 ns     62 ns     57 ns\n==============================================================\n====================== 16 prod   8 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      153 ns    109 ns     46 ns\nUnbounded MPMC timed spin only      167 ns    110 ns     36 ns\nUnbounded MPMC wait  spin only       43 ns     39 ns     36 ns\nUnbounded MPMC try   may block      159 ns    125 ns    100 ns\nUnbounded MPMC timed may block      127 ns     82 ns     52 ns\nUnbounded MPMC wait  may block       51 ns     50 ns     46 ns\n..............................................................\nfolly::MPMC  read                   149 ns    139 ns    129 ns\nfolly::MPMC  tryReadUntil           141 ns    134 ns    112 ns\nfolly::MPMC  blockingRead            59 ns     54 ns     49 ns\n==============================================================\n====================== 16 prod  16 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      193 ns    169 ns    148 ns\nUnbounded MPMC timed spin only      221 ns    175 ns    106 ns\nUnbounded MPMC wait  spin only       45 ns     41 ns     37 ns\nUnbounded MPMC try   may block      204 ns    171 ns    133 ns\nUnbounded MPMC timed may block      184 ns    162 ns    104 ns\nUnbounded MPMC wait  may block       61 ns     52 ns     49 ns\n..............................................................\nfolly::MPMC  read                   181 ns    164 ns    157 ns\nfolly::MPMC  tryReadUntil           185 ns    173 ns    157 ns\nfolly::MPMC  blockingRead            56 ns     50 ns     45 ns\n==============================================================\n====================== 16 prod  32 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      255 ns    217 ns    181 ns\nUnbounded MPMC timed spin only      225 ns    205 ns    182 ns\nUnbounded MPMC wait  spin only      115 ns     57 ns     40 ns\nUnbounded MPMC try   may block      215 ns    199 ns    184 ns\nUnbounded MPMC timed may block      218 ns    196 ns    179 ns\nUnbounded MPMC wait  may block       63 ns     54 ns     47 ns\n..............................................................\nfolly::MPMC  read                   260 ns    205 ns    185 ns\nfolly::MPMC  tryReadUntil           205 ns    200 ns    192 ns\nfolly::MPMC  blockingRead            53 ns     48 ns     43 ns\n==============================================================\n====================== 32 prod   2 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only       95 ns     66 ns     45 ns\nUnbounded MPMC timed spin only       95 ns     62 ns     45 ns\nUnbounded MPMC wait  spin only       56 ns     44 ns     36 ns\nUnbounded MPMC try   may block      123 ns     86 ns     50 ns\nUnbounded MPMC timed may block      109 ns     73 ns     47 ns\nUnbounded MPMC wait  may block       95 ns     58 ns     47 ns\n..............................................................\nfolly::MPMC  read                   445 ns    380 ns    315 ns\nfolly::MPMC  tryReadUntil           459 ns    341 ns    153 ns\nfolly::MPMC  blockingRead           351 ns    286 ns    218 ns\n==============================================================\n====================== 32 prod   4 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      114 ns     92 ns     59 ns\nUnbounded MPMC timed spin only      135 ns     99 ns     47 ns\nUnbounded MPMC wait  spin only      139 ns     55 ns     38 ns\nUnbounded MPMC try   may block      165 ns    113 ns     72 ns\nUnbounded MPMC timed may block      119 ns     94 ns     51 ns\nUnbounded MPMC wait  may block       61 ns     52 ns     47 ns\n..............................................................\nfolly::MPMC  read                   127 ns    112 ns     93 ns\nfolly::MPMC  tryReadUntil           116 ns    107 ns     96 ns\nfolly::MPMC  blockingRead            67 ns     59 ns     51 ns\n==============================================================\n====================== 32 prod   8 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      226 ns    140 ns     57 ns\nUnbounded MPMC timed spin only      176 ns    126 ns     61 ns\nUnbounded MPMC wait  spin only       86 ns     50 ns     39 ns\nUnbounded MPMC try   may block      170 ns    131 ns     76 ns\nUnbounded MPMC timed may block      201 ns    141 ns    110 ns\nUnbounded MPMC wait  may block       94 ns     55 ns     47 ns\n..............................................................\nfolly::MPMC  read                   148 ns    131 ns    120 ns\nfolly::MPMC  tryReadUntil           132 ns    126 ns    121 ns\nfolly::MPMC  blockingRead            59 ns     54 ns     51 ns\n==============================================================\n====================== 32 prod  16 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      209 ns    174 ns    146 ns\nUnbounded MPMC timed spin only      214 ns    189 ns    154 ns\nUnbounded MPMC wait  spin only      138 ns     51 ns     38 ns\nUnbounded MPMC try   may block      247 ns    191 ns    144 ns\nUnbounded MPMC timed may block      245 ns    180 ns    123 ns\nUnbounded MPMC wait  may block       74 ns     51 ns     46 ns\n..............................................................\nfolly::MPMC  read                   164 ns    148 ns    135 ns\nfolly::MPMC  tryReadUntil           156 ns    149 ns    140 ns\nfolly::MPMC  blockingRead            55 ns     50 ns     47 ns\n==============================================================\n====================== 32 prod  32 cons ======================\n=== uint32_t =================================================\nUnbounded MPMC try   spin only      255 ns    212 ns    179 ns\nUnbounded MPMC timed spin only      391 ns    223 ns    147 ns\nUnbounded MPMC wait  spin only       78 ns     44 ns     38 ns\nUnbounded MPMC try   may block      516 ns    249 ns    195 ns\nUnbounded MPMC timed may block      293 ns    210 ns    171 ns\nUnbounded MPMC wait  may block       54 ns     51 ns     48 ns\n..............................................................\nfolly::MPMC  read                   195 ns    183 ns    164 ns\nfolly::MPMC  tryReadUntil           191 ns    175 ns    159 ns\nfolly::MPMC  blockingRead            49 ns     45 ns     43 ns\n==============================================================\n\n$ lscpu\nArchitecture:        x86_64\nCPU op-mode(s):      32-bit, 64-bit\nByte Order:          Little Endian\nCPU(s):              32\nOn-line CPU(s) list: 0-31\nThread(s) per core:  2\nCore(s) per socket:  8\nSocket(s):           2\nNUMA node(s):        2\nVendor ID:           GenuineIntel\nCPU family:          6\nModel:               45\nModel name:          Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz\nStepping:            6\nCPU MHz:             2200.000\nCPU max MHz:         2200.0000\nCPU min MHz:         1200.0000\nBogoMIPS:            4399.92\nVirtualization:      VT-x\nL1d cache:           32K\nL1i cache:           32K\nL2 cache:            256K\nL3 cache:            20480K\nNUMA node0 CPU(s):   0-7,16-23\nNUMA node1 CPU(s):   8-15,24-31\n\nFlags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr\n                     pge mca cmov pat pse36 clflush dts acpi mmx fxsr\n                     sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp\n                     lm constant_tsc arch_perfmon pebs bts rep_good\n                     nopl xtopology nonstop_tsc aperfmperf eagerfpu\n                     pni pclmulqdq dtes64 monitor ds_cpl vmx smx est\n                     tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2\n                     x2apic popcnt tsc_deadline_timer aes xsave avx\n                     lahf_lm epb tpr_shadow vnmi flexpriority ept vpid\n                     xsaveopt dtherm arat pln pts\n */\n"
  },
  {
    "path": "folly/container/Access.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\n\nnamespace access {\n\n/// size_fn\n/// size\n///\n/// Invokes unqualified size with std::size in scope.\nFOLLY_CREATE_FREE_INVOKER_SUITE(size, std);\n\n/// empty_fn\n/// empty\n///\n/// Invokes unqualified empty with std::empty in scope.\nFOLLY_CREATE_FREE_INVOKER_SUITE(empty, std);\n\n/// data_fn\n/// data\n///\n/// Invokes unqualified data with std::data in scope.\nFOLLY_CREATE_FREE_INVOKER_SUITE(data, std);\n\n/// begin_fn\n/// begin\n///\n/// Invokes unqualified begin with std::begin in scope.\nFOLLY_CREATE_FREE_INVOKER_SUITE(begin, std);\n\n/// end_fn\n/// end\n///\n/// Invokes unqualified end with std::end in scope.\nFOLLY_CREATE_FREE_INVOKER_SUITE(end, std);\n\n} // namespace access\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/Array.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * Helper functions to create std::arrays.\n *\n * @file container/Array.h\n * @refcode folly/docs/examples/folly/container/Array.cpp\n */\n\n#pragma once\n\n#include <array>\n#include <type_traits>\n#include <utility>\n\n#include <folly/CPortability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n\nnamespace folly {\n\nnamespace array_detail {\ntemplate <class T>\nusing is_ref_wrapper = is_instantiation_of<std::reference_wrapper, T>;\n\ntemplate <typename T>\nusing not_ref_wrapper =\n    folly::Negation<is_ref_wrapper<typename std::decay<T>::type>>;\n\ntemplate <typename D, typename...>\nstruct return_type_helper {\n  using type = D;\n};\ntemplate <typename... TList>\nstruct return_type_helper<void, TList...> {\n  static_assert(\n      folly::Conjunction<not_ref_wrapper<TList>...>::value,\n      \"TList cannot contain reference_wrappers when D is void\");\n  using type = typename std::common_type<TList...>::type;\n};\n\ntemplate <typename D, typename... TList>\nusing return_type = std::\n    array<typename return_type_helper<D, TList...>::type, sizeof...(TList)>;\n} // namespace array_detail\n\n/// Constructs a std::array with the given argument list.\n///\n/// @param t  The values to be put in the array.\ntemplate <typename D = void, typename... TList>\nconstexpr array_detail::return_type<D, TList...> make_array(TList&&... t) {\n  using value_type =\n      typename array_detail::return_type_helper<D, TList...>::type;\n  return {{static_cast<value_type>(std::forward<TList>(t))...}};\n}\n\nnamespace array_detail {\ntemplate <typename MakeItem, std::size_t... Index>\nFOLLY_ERASE constexpr auto make_array_with_(\n    MakeItem const& make, std::index_sequence<Index...>) {\n  return std::array<decltype(make(0)), sizeof...(Index)>{{make(Index)...}};\n}\n} // namespace array_detail\n\n/// Generates a std::array<..., Size> with elements m(i) for i in [0, Size).\n///\n/// @tparam Size  The size of the array\n/// @param make  The generator that makes the array elements. ret[i] = make(i)\ntemplate <std::size_t Size, typename MakeItem>\nconstexpr auto make_array_with(MakeItem const& make) {\n  return array_detail::make_array_with_(make, std::make_index_sequence<Size>{});\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../defs.bzl\",\n    \"FBANDROID_CPPFLAGS\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"f14_hash\",\n    fbandroid_exported_preprocessor_flags = FBANDROID_CPPFLAGS,\n    raw_headers = [\n        \"F14Map.h\",\n        \"F14Set.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:traits\",\n        \"//xplat/folly/container:f14_hash_fwd\",\n        \"//xplat/folly/container:view\",\n        \"//xplat/folly/container/detail:f14_hash_detail\",\n        \"//xplat/folly/container/detail:util\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/lang:safe_assert\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"heterogeneous_access\",\n    headers = [\n        \"HeterogeneousAccess.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":heterogeneous_access_fwd\",\n        \"//folly:range\",\n        \"//folly:traits\",\n        \"//folly/hash:hash\",\n        \"//folly/hash:rapidhash\",\n        \"//folly/hash/detail:random_seed\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"heterogeneous_access_fwd\",\n    headers = [\n        \"HeterogeneousAccess-fwd.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"enumerate\",\n    headers = [\"Enumerate.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly/portability:sys_types\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"sparse_byte_set\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"SparseByteSet.h\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"merge\",\n    headers = [\"Merge.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"evicting_cache_map\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"EvictingCacheMap.h\",\n    ],\n    exported_deps = [\n        \"//third-party/boost:boost\",\n        \"//xplat/folly/container:f14_hash\",\n        \"//xplat/folly/container:heterogeneous_access\",\n        \"//xplat/folly/lang:exception\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"weighted_evicting_cache_map\",\n    headers = [\n        \"WeightedEvictingCacheMap.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container:evicting_cache_map\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"view\",\n    headers = [\"View.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:customization_point\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"foreach\",\n    headers = [\n        \"Foreach.h\",\n        \"Foreach-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":access\",\n        \"//folly:portability\",\n        \"//folly:preprocessor\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/functional:invoke\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"f14_hash_fwd\",\n    headers = [\n        \"F14Map-fwd.h\",\n        \"F14Set-fwd.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container/detail:f14_defaults\",\n        \"//folly/memory:memory_resource\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"iterator\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"Iterator.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/container:access\",\n        \"//xplat/folly/functional:invoke\",\n        \"//xplat/folly/lang:rvalue_reference_wrapper\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"bit_iterator\",\n    headers = [\"BitIterator.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly/container/detail:bit_iterator_detail\",\n        \"//folly/lang:bits\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"array\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"Array.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:utility\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"heap_vector_types\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"heap_vector_types.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/container:iterator\",\n        \"//xplat/folly/functional:invoke\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/memory:memory_resource\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"access\",\n    headers = [\"Access.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/functional:invoke\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"intrusive_heap\",\n    headers = [\n        \"IntrusiveHeap.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:portability\",\n        \"//folly/lang:builtin\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"range_traits\",\n    headers = [\"range_traits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:traits\",\n        \"//folly:utility\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"regex_match_cache\",\n    srcs = [\"RegexMatchCache.cpp\"],\n    headers = [\"RegexMatchCache.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:map_util\",\n        \"//folly:string\",\n        \"//folly/portability:windows\",\n        \"//folly/synchronization:atomic_util\",\n    ],\n    exported_deps = [\n        \":f14_hash\",\n        \":reserve\",\n        \"//folly:chrono\",\n        \"//folly:function\",\n        \"//folly/hash:unique_hash_key\",\n        \"//folly/lang:bits\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_regex\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"span\",\n    headers = [\"span.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":access\",\n        \":iterator\",\n        \"//folly:cpp_attributes\",\n        \"//folly:portability\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/functional:invoke\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"tape\",\n    headers = [\n        \"tape.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":iterator\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/container/detail:tape_detail\",\n        \"//folly/memory:uninitialized_memory_hacks\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"reserve\",\n    headers = [\n        \"Reserve.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:likely\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"sorted_vector_types\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\"sorted_vector_types.h\"],\n    exported_deps = [\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:small_vector\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/lang:access\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/memory:memory_resource\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"std_bitset\",\n    headers = [\"StdBitset.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:likely\",\n        \"//folly:portability\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"fbvector\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"FBVector.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:format_traits\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly/lang:checked_math\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/lang:hint\",\n        \"//xplat/folly/memory:malloc\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"intrusive_list\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"IntrusiveList.h\",\n    ],\n    exported_deps = [\n        \"//third-party/boost:boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"map_util\",\n    headers = [\"MapUtil.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly/functional:invoke\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"small_vector\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"small_vector.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/portability:malloc\",\n        \"//third-party/boost:boost\",\n        \"//xplat/folly:constexpr_math\",\n        \"//xplat/folly:format_traits\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly/functional:invoke\",\n        \"//xplat/folly/lang:align\",\n        \"//xplat/folly/lang:assume\",\n        \"//xplat/folly/lang:checked_math\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/memory:malloc\",\n        \"//xplat/folly/memory:sanitize_leak\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"vector_bool\",\n    headers = [\"vector_bool.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container/detail:bool_wrapper\",\n        \"//folly/memory:memory_resource\",\n    ],\n)\n\n# !!!! fbcode/folly/container/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"array\",\n    headers = [\"Array.h\"],\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"iterator\",\n    headers = [\"Iterator.h\"],\n    exported_deps = [\n        \":access\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:rvalue_reference_wrapper\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"evicting_cache_map\",\n    headers = [\"EvictingCacheMap.h\"],\n    exported_deps = [\n        \"//folly/container:f14_hash\",\n        \"//folly/container:heterogeneous_access\",\n        \"//folly/lang:exception\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"f14_hash\",\n    headers = [\n        \"F14Map.h\",\n        \"F14Set.h\",\n    ],\n    exported_deps = [\n        \":f14_hash_fwd\",\n        \":iterator\",\n        \":view\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly:traits\",\n        \"//folly/container/detail:f14_hash_detail\",\n        \"//folly/container/detail:util\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:safe_assert\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"heap_vector_types\",\n    headers = [\n        \"heap_vector_types.h\",\n    ],\n    exported_deps = [\n        \":iterator\",\n        \"//folly:range\",\n        \"//folly:scope_guard\",\n        \"//folly:small_vector\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:exception\",\n        \"//folly/memory:memory_resource\",\n        \"//folly/portability:builtins\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"sparse_byte_set\",\n    headers = [\"SparseByteSet.h\"],\n    exported_deps = [\n        \"//folly:c_portability\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"sorted_vector_types\",\n    headers = [\n        \"sorted_vector_types.h\",\n    ],\n    exported_deps = [\n        \"//folly:scope_guard\",\n        \"//folly:small_vector\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/lang:access\",\n        \"//folly/lang:exception\",\n        \"//folly/memory:memory_resource\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"fbvector\",\n    headers = [\"FBVector.h\"],\n    exported_deps = [\n        \"//folly:format_traits\",\n        \"//folly:likely\",\n        \"//folly:scope_guard\",\n        \"//folly:traits\",\n        \"//folly/lang:checked_math\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:hint\",\n        \"//folly/memory:malloc\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"intrusive_list\",\n    headers = [\"IntrusiveList.h\"],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"small_vector\",\n    headers = [\"small_vector.h\"],\n    exported_deps = [\n        \"//folly:constexpr_math\",\n        \"//folly:format_traits\",\n        \"//folly:likely\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:traits\",\n        \"//folly/functional:invoke\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:align\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:checked_math\",\n        \"//folly/lang:exception\",\n        \"//folly/memory:malloc\",\n        \"//folly/memory:sanitize_leak\",\n        \"//folly/portability:malloc\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"collection_util\",\n    headers = [\"CollectionUtil.h\"],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"irange\",\n    headers = [\"irange.h\"],\n    deps = [],\n    exported_deps = [],\n)\n"
  },
  {
    "path": "folly/container/BitIterator.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * BitIterator\n *    Wrapper around an iterator over an integral type that iterates\n *    over its underlying bits in MSb to LSb order\n *\n * findFirstSet(BitIterator begin, BitIterator end)\n *    return a BitIterator pointing to the first 1 bit in [begin, end), or\n *    end if all bits in [begin, end) are 0\n */\n\n#pragma once\n\n#include <cassert>\n#include <cinttypes>\n#include <cstdint>\n#include <cstring>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n\n#include <boost/iterator/iterator_adaptor.hpp>\n\n#include <folly/Portability.h>\n#include <folly/container/detail/BitIteratorDetail.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\n\n/**\n * Fast bit iteration facility.\n */\n\ntemplate <class BaseIter>\nclass BitIterator;\ntemplate <class BaseIter>\nBitIterator<BaseIter> findFirstSet(\n    BitIterator<BaseIter>, BitIterator<BaseIter>);\n/**\n * Wrapper around an iterator over an integer type that iterates\n * over its underlying bits in LSb to MSb order.\n *\n * BitIterator models the same iterator concepts as the base iterator.\n */\ntemplate <class BaseIter>\nclass BitIterator : public bititerator_detail::BitIteratorBase<BaseIter>::type {\n public:\n  /**\n   * Return the number of bits in an element of the underlying iterator.\n   */\n  static unsigned int bitsPerBlock() {\n    return std::numeric_limits<typename std::make_unsigned<\n        typename std::iterator_traits<BaseIter>::value_type>::type>::digits;\n  }\n\n  /**\n   * Construct a BitIterator that points at a given bit offset (default 0)\n   * in iter.\n   */\n  explicit BitIterator(const BaseIter& iter, size_t bitOff = 0)\n      : bititerator_detail::BitIteratorBase<BaseIter>::type(iter),\n        bitOffset_(bitOff) {\n    assert(bitOffset_ < bitsPerBlock());\n  }\n\n  size_t bitOffset() const { return bitOffset_; }\n\n  void advanceToNextBlock() {\n    bitOffset_ = 0;\n    ++this->base_reference();\n  }\n\n  BitIterator& operator=(const BaseIter& other) {\n    this->~BitIterator();\n    new (this) BitIterator(other);\n    return *this;\n  }\n\n private:\n  friend class boost::iterator_core_access;\n  friend BitIterator findFirstSet<>(BitIterator, BitIterator);\n\n  typedef bititerator_detail::BitReference<\n      typename std::iterator_traits<BaseIter>::reference,\n      typename std::iterator_traits<BaseIter>::value_type>\n      BitRef;\n\n  void advanceInBlock(size_t n) {\n    bitOffset_ += n;\n    assert(bitOffset_ < bitsPerBlock());\n  }\n\n  BitRef dereference() const {\n    return BitRef(*this->base_reference(), bitOffset_);\n  }\n\n  void advance(ssize_t n) {\n    size_t bpb = bitsPerBlock();\n    ssize_t blocks = n / ssize_t(bpb);\n    bitOffset_ += n % bpb;\n    if (bitOffset_ >= bpb) {\n      bitOffset_ -= bpb;\n      ++blocks;\n    }\n    this->base_reference() += blocks;\n  }\n\n  void increment() {\n    if (++bitOffset_ == bitsPerBlock()) {\n      advanceToNextBlock();\n    }\n  }\n\n  void decrement() {\n    if (bitOffset_-- == 0) {\n      bitOffset_ = bitsPerBlock() - 1;\n      --this->base_reference();\n    }\n  }\n\n  bool equal(const BitIterator& other) const {\n    return (\n        bitOffset_ == other.bitOffset_ &&\n        this->base_reference() == other.base_reference());\n  }\n\n  ssize_t distance_to(const BitIterator& other) const {\n    return ssize_t(\n        (other.base_reference() - this->base_reference()) * bitsPerBlock() +\n        other.bitOffset_ - bitOffset_);\n  }\n\n  size_t bitOffset_;\n};\n\n/**\n * Helper function, so you can write\n * auto bi = makeBitIterator(container.begin());\n */\ntemplate <class BaseIter>\nBitIterator<BaseIter> makeBitIterator(const BaseIter& iter) {\n  return BitIterator<BaseIter>(iter);\n}\n\n/**\n * Find first bit set in a range of bit iterators.\n * 4.5x faster than the obvious std::find(begin, end, true);\n */\ntemplate <class BaseIter>\nBitIterator<BaseIter> findFirstSet(\n    BitIterator<BaseIter> begin, BitIterator<BaseIter> end) {\n  // shortcut to avoid ugly static_cast<>\n  static const typename std::iterator_traits<BaseIter>::value_type one = 1;\n\n  while (begin.base() != end.base()) {\n    typename std::iterator_traits<BaseIter>::value_type v = *begin.base();\n    // mask out the bits that don't matter (< begin.bitOffset)\n    v &= ~((one << begin.bitOffset()) - 1);\n    size_t firstSet = findFirstSet(v);\n    if (firstSet) {\n      --firstSet; // now it's 0-based\n      assert(firstSet >= begin.bitOffset());\n      begin.advanceInBlock(firstSet - begin.bitOffset());\n      return begin;\n    }\n    begin.advanceToNextBlock();\n  }\n\n  // now begin points to the same block as end\n  if (end.bitOffset() != 0) { // assume end is dereferenceable\n    typename std::iterator_traits<BaseIter>::value_type v = *begin.base();\n    // mask out the bits that don't matter (< begin.bitOffset)\n    v &= ~((one << begin.bitOffset()) - 1);\n    // mask out the bits that don't matter (>= end.bitOffset)\n    v &= (one << end.bitOffset()) - 1;\n    size_t firstSet = findFirstSet(v);\n    if (firstSet) {\n      --firstSet; // now it's 0-based\n      assert(firstSet >= begin.bitOffset());\n      begin.advanceInBlock(firstSet - begin.bitOffset());\n      return begin;\n    }\n  }\n\n  return end;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME access\n  HEADERS\n    Access.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n)\n\nfolly_add_library(\n  NAME array\n  HEADERS\n    Array.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME bit_iterator\n  HEADERS\n    BitIterator.h\n  EXPORTED_DEPS\n    folly_container_detail_bit_iterator_detail\n    folly_lang_bits\n    folly_portability\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME collection_util\n  HEADERS\n    CollectionUtil.h\n)\n\nfolly_add_library(\n  NAME enumerate\n  HEADERS\n    Enumerate.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_portability_sys_types\n)\n\nfolly_add_library(\n  NAME evicting_cache_map\n  HEADERS\n    EvictingCacheMap.h\n  EXPORTED_DEPS\n    folly_container_f14_hash\n    folly_container_heterogeneous_access\n    folly_lang_exception\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME f14_hash\n  HEADERS\n    F14Map.h\n    F14Set.h\n  EXPORTED_DEPS\n    folly_container_detail_f14_hash_detail\n    folly_container_detail_util\n    folly_container_f14_hash_fwd\n    folly_container_iterator\n    folly_container_view\n    folly_lang_exception\n    folly_lang_safe_assert\n    folly_portability\n    folly_range\n    folly_traits\n)\n\nfolly_add_library(\n  NAME f14_hash_fwd\n  HEADERS\n    F14Map-fwd.h\n    F14Set-fwd.h\n  EXPORTED_DEPS\n    folly_container_detail_f14_defaults\n    folly_memory_memory_resource\n)\n\nfolly_add_library(\n  NAME fbvector\n  HEADERS\n    FBVector.h\n  EXPORTED_DEPS\n    folly_format_traits\n    folly_lang_checked_math\n    folly_lang_exception\n    folly_lang_hint\n    folly_likely\n    folly_memory_malloc\n    folly_scope_guard\n    folly_traits\n)\n\nfolly_add_library(\n  NAME foreach\n  HEADERS\n    Foreach-inl.h\n    Foreach.h\n  EXPORTED_DEPS\n    folly_container_access\n    folly_functional_invoke\n    folly_portability\n    folly_preprocessor\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME heap_vector_types\n  HEADERS\n    heap_vector_types.h\n  EXPORTED_DEPS\n    folly_container_iterator\n    folly_functional_invoke\n    folly_lang_exception\n    folly_memory_memory_resource\n    folly_portability_builtins\n    folly_range\n    folly_scope_guard\n    folly_small_vector\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME heterogeneous_access\n  HEADERS\n    HeterogeneousAccess.h\n  EXPORTED_DEPS\n    folly_container_heterogeneous_access_fwd\n    folly_hash_detail_random_seed\n    folly_hash_hash\n    folly_hash_rapidhash\n    folly_range\n    folly_traits\n)\n\nfolly_add_library(\n  NAME heterogeneous_access_fwd\n  HEADERS\n    HeterogeneousAccess-fwd.h\n)\n\nfolly_add_library(\n  NAME intrusive_heap\n  HEADERS\n    IntrusiveHeap.h\n  EXPORTED_DEPS\n    folly_lang_builtin\n    folly_portability\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME intrusive_list\n  HEADERS\n    IntrusiveList.h\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME irange\n  HEADERS\n    irange.h\n)\n\nfolly_add_library(\n  NAME iterator\n  HEADERS\n    Iterator.h\n  EXPORTED_DEPS\n    folly_container_access\n    folly_functional_invoke\n    folly_lang_rvalue_reference_wrapper\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME map_util\n  HEADERS\n    MapUtil.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_optional\n    folly_range\n)\n\nfolly_add_library(\n  NAME merge\n  HEADERS\n    Merge.h\n)\n\nfolly_add_library(\n  NAME range_traits\n  HEADERS\n    range_traits.h\n  EXPORTED_DEPS\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME regex_match_cache\n  SRCS\n    RegexMatchCache.cpp\n  HEADERS\n    RegexMatchCache.h\n  DEPS\n    folly_map_util\n    folly_portability_windows\n    folly_string\n    folly_synchronization_atomic_util\n  EXPORTED_DEPS\n    folly_chrono\n    folly_container_f14_hash\n    folly_container_reserve\n    folly_function\n    folly_hash_unique_hash_key\n    folly_lang_bits\n  EXTERNAL_DEPS\n    Boost::regex\n)\n\nfolly_add_library(\n  NAME reserve\n  HEADERS\n    Reserve.h\n  EXPORTED_DEPS\n    folly_lang_exception\n    folly_likely\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME small_vector\n  HEADERS\n    small_vector.h\n  EXPORTED_DEPS\n    folly_constexpr_math\n    folly_format_traits\n    folly_functional_invoke\n    folly_hash_hash\n    folly_lang_align\n    folly_lang_assume\n    folly_lang_checked_math\n    folly_lang_exception\n    folly_likely\n    folly_memory_malloc\n    folly_memory_sanitize_leak\n    folly_portability\n    folly_portability_malloc\n    folly_scope_guard\n    folly_traits\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME sorted_vector_types\n  HEADERS\n    sorted_vector_types.h\n  EXPORTED_DEPS\n    folly_lang_access\n    folly_lang_exception\n    folly_memory_memory_resource\n    folly_scope_guard\n    folly_small_vector\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME span\n  HEADERS\n    span.h\n  EXPORTED_DEPS\n    folly_container_access\n    folly_container_iterator\n    folly_cpp_attributes\n    folly_functional_invoke\n    folly_portability\n    folly_portability_constexpr\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME sparse_byte_set\n  HEADERS\n    SparseByteSet.h\n  EXPORTED_DEPS\n    folly_c_portability\n)\n\nfolly_add_library(\n  NAME std_bitset\n  HEADERS\n    StdBitset.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_likely\n    folly_portability\n)\n\nfolly_add_library(\n  NAME tape\n  HEADERS\n    tape.h\n  EXPORTED_DEPS\n    folly_container_detail_tape_detail\n    folly_container_iterator\n    folly_memory_uninitialized_memory_hacks\n    folly_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME vector_bool\n  HEADERS\n    vector_bool.h\n  EXPORTED_DEPS\n    folly_container_detail_bool_wrapper\n    folly_memory_memory_resource\n)\n\nfolly_add_library(\n  NAME view\n  HEADERS\n    View.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_lang_customization_point\n    folly_portability\n)\n\nfolly_add_library(\n  NAME weighted_evicting_cache_map\n  HEADERS\n    WeightedEvictingCacheMap.h\n  EXPORTED_DEPS\n    folly_container_evicting_cache_map\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/container/CollectionUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <concepts>\n\nnamespace folly {\n\nnamespace detail {\ntemplate <typename T, typename K>\nconcept HasContains = requires(T& t, const K& k) {\n  { t.contains(k) };\n};\n\ntemplate <typename T, typename K>\nconcept HasFind = requires(T& t, const K& k) {\n  { t.find(k) } -> std::convertible_to<typename T::const_iterator>;\n};\n} // namespace detail\n/**\n * This function check whether container contains given value.\n * Can be used for non-associative containers.\n * Notes:\n * Runtime = O(n)\n * Work with vector, map.\n */\ntemplate <class C, class V = typename C::value_type>\n  requires(!detail::HasContains<C, V> && !detail::HasFind<C, V>)\nbool contains(const C& container, const V& value) {\n  const auto e = std::end(container);\n  return std::find(std::begin(container), e, value) != e;\n}\n\n/**\n * This function checks whether container contains given key.\n * Use container specific .contains() implementation if available,\n * otherwise uses .find() implementation if available, otherwise\n * fallback to O(n) std::find() implementation.\n */\ntemplate <class C, class K = typename C::key_type>\nbool contains(const C& container, const K& key) {\n  if constexpr (detail::HasContains<C, K>) {\n    return container.contains(key);\n  } else if constexpr (detail::HasFind<C, K>) {\n    return container.find(key) != container.end();\n  } else {\n    // Fallback: use generic and possibly slower std::find otherwise.\n    return contains(container, key);\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/Enumerate.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <iterator>\n#include <memory>\n\n#include <folly/CPortability.h>\n#include <folly/portability/SysTypes.h>\n\n/**\n * Similar to Python's enumerate(), folly::enumerate() can be used to\n * iterate a range with a for-range loop, and it also allows to\n * retrieve the count of iterations so far. Can be used in constexpr\n * context.\n *\n * For example:\n *\n * for (auto&& [index, element] : folly::enumerate(vec)) {\n *   // index is a const reference to a size_t containing the iteration count.\n *   // element is a reference to the type contained within vec, mutable\n *   // unless vec is const.\n * }\n *\n * If the binding is const, the element reference is too.\n *\n * for (const auto&& [index, element] : folly::enumerate(vec)) {\n *   // element is always a const reference.\n * }\n *\n * It can also be used as follows:\n *\n * for (auto&& it : folly::enumerate(vec)) {\n *   // *it is a reference to the current element. Mutable unless vec is const.\n *   // it->member can be used as well.\n *   // it.index contains the iteration count.\n * }\n *\n * As before, const auto&& it can also be used.\n */\n\nnamespace folly {\n\nnamespace detail {\n\ntemplate <class T>\nstruct MakeConst {\n  using type = const T;\n};\ntemplate <class T>\nstruct MakeConst<T&> {\n  using type = const T&;\n};\ntemplate <class T>\nstruct MakeConst<T*> {\n  using type = const T*;\n};\n\ntemplate <class Iterator>\nclass Enumerator {\n public:\n  constexpr explicit Enumerator(Iterator it) : it_(std::move(it)) {}\n\n  class Proxy {\n   public:\n    using difference_type = ssize_t;\n    using value_type = typename std::iterator_traits<Iterator>::value_type;\n    using reference = typename std::iterator_traits<Iterator>::reference;\n    using pointer = typename std::iterator_traits<Iterator>::pointer;\n    using iterator_category = std::input_iterator_tag;\n\n    FOLLY_ALWAYS_INLINE constexpr explicit Proxy(const Enumerator& e)\n        : index(e.idx_), element(*e.it_) {}\n\n    // Non-const Proxy: Forward constness from Iterator.\n    FOLLY_ALWAYS_INLINE constexpr reference operator*() { return element; }\n    FOLLY_ALWAYS_INLINE constexpr pointer operator->() {\n      return std::addressof(element);\n    }\n\n    // Const Proxy: Force const references.\n    FOLLY_ALWAYS_INLINE constexpr typename MakeConst<reference>::type\n    operator*() const {\n      return element;\n    }\n    FOLLY_ALWAYS_INLINE constexpr typename MakeConst<pointer>::type operator->()\n        const {\n      return std::addressof(element);\n    }\n\n   public:\n    const size_t index;\n    reference element;\n  };\n\n  FOLLY_ALWAYS_INLINE constexpr Proxy operator*() const { return Proxy(*this); }\n\n  FOLLY_ALWAYS_INLINE constexpr Enumerator& operator++() {\n    ++it_;\n    ++idx_;\n    return *this;\n  }\n\n  template <typename OtherIterator>\n  FOLLY_ALWAYS_INLINE constexpr bool operator==(\n      const Enumerator<OtherIterator>& rhs) const {\n    return it_ == rhs.it_;\n  }\n\n  template <typename OtherIterator>\n  FOLLY_ALWAYS_INLINE constexpr bool operator!=(\n      const Enumerator<OtherIterator>& rhs) const {\n    return !(it_ == rhs.it_);\n  }\n\n private:\n  template <typename OtherIterator>\n  friend class Enumerator;\n\n  Iterator it_;\n  size_t idx_ = 0;\n};\n\ntemplate <class Range>\nclass RangeEnumerator {\n  Range r_;\n  using BeginIteratorType = decltype(std::declval<Range>().begin());\n  using EndIteratorType = decltype(std::declval<Range>().end());\n\n public:\n  constexpr explicit RangeEnumerator(Range&& r) : r_(std::forward<Range>(r)) {}\n\n  constexpr Enumerator<BeginIteratorType> begin() {\n    return Enumerator<BeginIteratorType>(r_.begin());\n  }\n  constexpr Enumerator<EndIteratorType> end() {\n    return Enumerator<EndIteratorType>(r_.end());\n  }\n};\n\n} // namespace detail\n\ntemplate <class Range>\nconstexpr detail::RangeEnumerator<Range> enumerate(Range&& r) {\n  return detail::RangeEnumerator<Range>(std::forward<Range>(r));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/EvictingCacheMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <exception>\n#include <functional>\n\n#include <boost/intrusive/list.hpp>\n#include <boost/iterator/iterator_adaptor.hpp>\n\n#include <folly/container/F14Set.h>\n#include <folly/container/HeterogeneousAccess.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\n/**\n * A general purpose LRU evicting cache designed to support constant time\n * set/get/insert/erase ops. The only required configuration parameter is the\n * `maxSize`, which is the maximum number of entries held by the cache, which\n * is also dynamically changeable. Insertion will evict (and destroy with ~TKey\n * and ~TValue) existing entries in LRU order as needed to keep number of\n * entries less than maxSize. When automatic eviction is triggered, the\n * minimum number of evictions is `clearSize`, which is configurable with a\n * default of 1. If a callback is specified with setPruneHook, it is invoked\n * for each eviction. However, the prune hook cannot manage object lifetimes\n * because it is not invoked on erase nor cache destruction.\n *\n * This is NOT a thread-safe implementation.\n *\n * Iterators and references are only invalidated when the referenced entry\n * might have been removed (pruned or erased), like std::map.\n *\n * NOTE: maxSize==0 is a special case that disables automatic evictions.\n * prune() can be used for manually trimming down the number of entries.\n *\n * Implementation: Maintains a doubly linked list (`lru_`) of entry nodes in\n * LRU order, which are also connected to hash table index (`index_`). The\n * access order is maintained on the list by moving an element to the front\n * of list on a get, and adding to the front on insert. Assuming quality\n * hashing, set/get are both constant time operations.\n *\n * NOTE: Previous versions of this structure used a hash table size that was\n * fixed at creation time, but that limitation is no longer present.\n */\ntemplate <\n    class TKey,\n    class TValue,\n    class THash = HeterogeneousAccessHash<TKey>,\n    class TKeyEqual = HeterogeneousAccessEqualTo<TKey>>\nclass EvictingCacheMap {\n private:\n  // typedefs for brevity\n  struct Node;\n  struct NodeList;\n  struct KeyHasher;\n  struct KeyValueEqual;\n  using NodeMap = F14VectorSet<Node*, KeyHasher, KeyValueEqual>;\n  using TPair = std::pair<const TKey, TValue>;\n\n public:\n  using PruneHookCall = std::function<void(TKey, TValue&&)>;\n\n  // iterator base : returns TPair on dereference\n  template <typename Value, typename TIterator>\n  class iterator_base\n      : public boost::iterator_adaptor<\n            iterator_base<Value, TIterator>,\n            TIterator,\n            Value,\n            boost::bidirectional_traversal_tag> {\n   public:\n    iterator_base() {}\n\n    explicit iterator_base(TIterator it)\n        : iterator_base::iterator_adaptor_(it) {}\n\n    template <\n        typename V,\n        typename I,\n        std::enable_if_t<\n            std::is_same<V const, Value>::value &&\n                std::is_convertible<I, TIterator>::value,\n            int> = 0>\n    /* implicit */ iterator_base(iterator_base<V, I> const& other)\n        : iterator_base::iterator_adaptor_(other.base()) {}\n\n    Value& dereference() const { return this->base_reference()->pr; }\n  };\n\n  // iterators\n  using iterator = iterator_base<TPair, typename NodeList::iterator>;\n  using const_iterator =\n      iterator_base<const TPair, typename NodeList::const_iterator>;\n  using reverse_iterator =\n      iterator_base<TPair, typename NodeList::reverse_iterator>;\n  using const_reverse_iterator =\n      iterator_base<const TPair, typename NodeList::const_reverse_iterator>;\n\n  // public type aliases for convenience\n  using key_type = TKey;\n  using mapped_type = TValue;\n  using hasher = THash;\n\n  /*\n   * Approximate size of memory used by each entry added to the cache,\n   * including the shallow bits (sizeof) of TKey and TValue, but not the deep\n   * bits. Using 128 (bytes per chunk) / 10 (avg entries per chunk) as\n   * approximate F14 index entry size.\n   */\n  static constexpr std::size_t kApproximateEntryMemUsage = 13 + sizeof(Node);\n\n private:\n  template <typename K, typename T>\n  using EnableHeterogeneousFind = std::enable_if_t<\n      detail::EligibleForHeterogeneousFind<TKey, THash, TKeyEqual, K>::value,\n      T>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousInsert = std::enable_if_t<\n      detail::EligibleForHeterogeneousInsert<TKey, THash, TKeyEqual, K>::value,\n      T>;\n\n  template <typename K>\n  using IsIter = Disjunction<\n      std::is_same<iterator, remove_cvref_t<K>>,\n      std::is_same<const_iterator, remove_cvref_t<K>>>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousErase = std::enable_if_t<\n      detail::EligibleForHeterogeneousFind<\n          TKey,\n          THash,\n          TKeyEqual,\n          std::conditional_t<IsIter<K>::value, TKey, K>>::value &&\n          !IsIter<K>::value,\n      T>;\n\n public:\n  /**\n   * Construct a EvictingCacheMap\n   * @param maxSize maximum size of the cache map.  Once the map size exceeds\n   *     maxSize, the map will begin to evict.\n   * @param clearSize the number of elements to clear at a time when automatic\n   *     eviction on insert is triggered.\n   */\n  explicit EvictingCacheMap(\n      std::size_t maxSize,\n      std::size_t clearSize = 1,\n      const THash& keyHash = THash(),\n      const TKeyEqual& keyEqual = TKeyEqual())\n      : keyHash_(keyHash),\n        keyEqual_(keyEqual),\n        index_(maxSize + /*transient*/ 1, keyHash_, keyEqual_),\n        maxSize_(maxSize),\n        clearSize_(clampClearSize(clearSize)) {}\n\n  EvictingCacheMap(const EvictingCacheMap&) = delete;\n  EvictingCacheMap& operator=(const EvictingCacheMap&) = delete;\n  EvictingCacheMap(EvictingCacheMap&&) = default;\n  EvictingCacheMap& operator=(EvictingCacheMap&&) = default;\n\n  ~EvictingCacheMap() { assert(lru_.size() == index_.size()); }\n\n  /**\n   * Adjust the max size of EvictingCacheMap, evicting as needed to ensure the\n   * new max is not exceeded.\n   *\n   * Calling this function with an argument of 0 removes the limit on the cache\n   * size and elements are not evicted unless clients explicitly call prune.\n   *\n   * @param maxSize new maximum size of the cache map.\n   * @param pruneHook eviction callback to use INSTEAD OF the configured one\n   */\n  void setMaxSize(size_t maxSize, PruneHookCall pruneHook = nullptr) {\n    if (maxSize != 0 && maxSize < size()) {\n      // Prune the excess elements with our new constraints.\n      prune(std::max(size() - maxSize, clearSize_), pruneHook);\n    }\n    maxSize_ = maxSize;\n  }\n\n  std::size_t getMaxSize() const { return maxSize_; }\n\n  void setClearSize(std::size_t clearSize) {\n    clearSize_ = clampClearSize(clearSize);\n  }\n\n  /**\n   * Check for existence of a specific key in the map.  This operation has\n   *     no effect on LRU order.\n   * @param key key to search for\n   * @return true if exists, false otherwise\n   */\n  bool exists(const TKey& key) const { return existsImpl(key); }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  bool exists(const K& key) const {\n    return existsImpl(key);\n  }\n\n  /**\n   * Get the value associated with a specific key.  This function always\n   *     promotes a found value to the head of the LRU.\n   * @param key key associated with the value\n   * @return the value if it exists\n   * @throw std::out_of_range exception of the key does not exist\n   */\n  TValue& get(const TKey& key) { return getImpl(key); }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  TValue& get(const K& key) {\n    return getImpl(key);\n  }\n\n  /**\n   * Get the iterator associated with a specific key.  This function always\n   *     promotes a found value to the head of the LRU.\n   * @param key key to associate with value\n   * @return the iterator of the object (a std::pair of const TKey, TValue) or\n   *     end() if it does not exist\n   */\n  iterator find(const TKey& key) { return findImpl(*this, key); }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  iterator find(const K& key) {\n    return findImpl(*this, key);\n  }\n\n  /**\n   * Get the value associated with a specific key.  This function never\n   *     promotes a found value to the head of the LRU.\n   * @param key key associated with the value\n   * @return the value if it exists\n   * @throw std::out_of_range exception of the key does not exist\n   */\n  const TValue& getWithoutPromotion(const TKey& key) const {\n    return getWithoutPromotionImpl(*this, key);\n  }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  const TValue& getWithoutPromotion(const K& key) const {\n    return getWithoutPromotionImpl(*this, key);\n  }\n\n  TValue& getWithoutPromotion(const TKey& key) {\n    return getWithoutPromotionImpl(*this, key);\n  }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  TValue& getWithoutPromotion(const K& key) {\n    return getWithoutPromotionImpl(*this, key);\n  }\n\n  /**\n   * Get the iterator associated with a specific key.  This function never\n   *     promotes a found value to the head of the LRU.\n   * @param key key to associate with value\n   * @return the iterator of the object (a std::pair of const TKey, TValue) or\n   *     end() if it does not exist\n   */\n  const_iterator findWithoutPromotion(const TKey& key) const {\n    return findWithoutPromotionImpl(*this, key);\n  }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  const_iterator findWithoutPromotion(const K& key) const {\n    return findWithoutPromotionImpl(*this, key);\n  }\n\n  iterator findWithoutPromotion(const TKey& key) {\n    return findWithoutPromotionImpl(*this, key);\n  }\n\n  template <typename K, EnableHeterogeneousFind<K, int> = 0>\n  iterator findWithoutPromotion(const K& key) {\n    return findWithoutPromotionImpl(*this, key);\n  }\n\n  /**\n   * Erase the key-value pair associated with key if it exists. Prune hook\n   * is not called unless one passed in here.\n   * @param key key associated with the value\n   * @param eraseHook callback to use with erased entry (similar to a prune\n   * hook)\n   * @return true if the key existed and was erased, else false\n   */\n  bool erase(const TKey& key, PruneHookCall eraseHook = nullptr) {\n    return eraseKeyImpl(key, eraseHook);\n  }\n\n  template <typename K, EnableHeterogeneousErase<K, int> = 0>\n  bool erase(const K& key, PruneHookCall eraseHook = nullptr) {\n    return eraseKeyImpl(key, eraseHook);\n  }\n\n  /**\n   * Erase the key-value pair associated with pos. Prune hook is not called\n   * unless one passed in here.\n   * @param pos iterator to the element to be erased\n   * @param eraseHook callback to use with erased entry (similar to a prune\n   * hook)\n   * @return iterator to the following element or end() if pos was the last\n   *     element\n   */\n  iterator erase(const_iterator pos, PruneHookCall eraseHook = nullptr) {\n    return iterator(\n        eraseImpl(const_cast<Node*>(&(*pos.base())), pos.base(), eraseHook));\n  }\n\n  /**\n   * Set a key-value pair in the dictionary\n   * @param key key to associate with value\n   * @param value value to associate with the key\n   * @param promote boolean flag indicating whether or not to move something\n   *     to the front of an LRU.  This only really matters if you're setting\n   *     a value that already exists.\n   * @param pruneHook eviction callback to use INSTEAD OF the configured one\n   */\n  void set(\n      const TKey& key,\n      TValue&& value,\n      bool promote = true,\n      PruneHookCall pruneHook = nullptr) {\n    setImpl(key, std::move(value), promote, pruneHook);\n  }\n\n  void set(\n      const TKey& key,\n      const TValue& value,\n      bool promote = true,\n      PruneHookCall pruneHook = nullptr) {\n    TValue tmp{value}; // can't yet rely on temporary materialization\n    setImpl(key, std::move(tmp), promote, pruneHook);\n  }\n\n  template <typename K, EnableHeterogeneousInsert<K, int> = 0>\n  void set(\n      const K& key,\n      TValue&& value,\n      bool promote = true,\n      PruneHookCall pruneHook = nullptr) {\n    setImpl(key, std::move(value), promote, pruneHook);\n  }\n\n  template <typename K, EnableHeterogeneousInsert<K, int> = 0>\n  void set(\n      const K& key,\n      const TValue& value,\n      bool promote = true,\n      PruneHookCall pruneHook = nullptr) {\n    TValue tmp{value}; // can't yet rely on temporary materialization\n    setImpl(key, std::move(tmp), promote, pruneHook);\n  }\n\n  /**\n   * Insert a new key-value pair in the dictionary if no element exists for key\n   * @param key key to associate with value\n   * @param value value to associate with the key\n   * @param pruneHook eviction callback to use INSTEAD OF the configured one\n   * @return a pair consisting of an iterator to the inserted element (or to the\n   *     element that prevented the insertion) and a bool denoting whether the\n   *     insertion took place.\n   */\n  std::pair<iterator, bool> insert(\n      const TKey& key, TValue&& value, PruneHookCall pruneHook = nullptr) {\n    return insertImpl(key, std::move(value), pruneHook);\n  }\n\n  std::pair<iterator, bool> insert(\n      const TKey& key, const TValue& value, PruneHookCall pruneHook = nullptr) {\n    TValue tmp{value}; // can't yet rely on temporary materialization\n    return insertImpl(key, std::move(tmp), pruneHook);\n  }\n\n  template <typename K, EnableHeterogeneousInsert<K, int> = 0>\n  std::pair<iterator, bool> insert(\n      const K& key, TValue&& value, PruneHookCall pruneHook = nullptr) {\n    return insertImpl(key, std::move(value), pruneHook);\n  }\n\n  template <typename K, EnableHeterogeneousInsert<K, int> = 0>\n  std::pair<iterator, bool> insert(\n      const K& key, const TValue& value, PruneHookCall pruneHook = nullptr) {\n    TValue tmp{value}; // can't yet rely on temporary materialization\n    return insertImpl(key, std::move(tmp), pruneHook);\n  }\n\n  /**\n   * Emplace a new key-value pair in the dictionary if no element exists for\n   * key, utilizing the configured prunehook\n   * @param key key to associate with value\n   * @param args args to construct TValue in place, to associate with the key\n   * @return a pair consisting of an iterator to the inserted element (or to the\n   *     element that prevented the insertion) and a bool denoting whether the\n   *     insertion took place.\n   */\n  template <typename K, typename... Args>\n  std::pair<iterator, bool> try_emplace(const K& key, Args&&... args) {\n    return emplaceWithPruneHook<K, Args...>(\n        key, std::forward<Args>(args)..., nullptr);\n  }\n\n  /**\n   * Emplace a new key-value pair in the dictionary if no element exists for key\n   * @param key key to associate with value\n   * @param args args to construct TValue in place, to associate with the key\n   * @param pruneHook eviction callback to use INSTEAD OF the configured one\n   * @return a pair consisting of an iterator to the inserted element (or to the\n   *     element that prevented the insertion) and a bool denoting whether the\n   *     insertion took place.\n   */\n  template <typename K, typename... Args>\n  std::pair<iterator, bool> emplaceWithPruneHook(\n      const K& key, Args&&... args, PruneHookCall pruneHook) {\n    return insertImpl<K>(\n        std::make_unique<Node>(\n            std::piecewise_construct, key, std::forward<Args>(args)...),\n        pruneHook);\n  }\n\n  /**\n   * Get the number of elements in the dictionary\n   * @return the size of the dictionary\n   */\n  std::size_t size() const {\n    assert(index_.size() == lru_.size());\n    return index_.size();\n  }\n\n  /**\n   * Typical empty function\n   * @return true if empty, false otherwise\n   */\n  bool empty() const { return index_.empty(); }\n\n  /**\n   * Remove all entries (as if all evicted)\n   * @param pruneHook eviction callback to use INSTEAD OF the configured one\n   */\n  void clear(PruneHookCall pruneHook = nullptr) { prune(size(), pruneHook); }\n\n  /**\n   * Set the prune hook, which is the function invoked on the key and value\n   *     on each eviction. An operation will throw if the pruneHook throws.\n   *     Note that this prune hook is not automatically called on entries\n   *     explicitly erase()ed nor on remaining entries at destruction time.\n   * @param pruneHook eviction callback to set as default, or nullptr to clear\n   */\n  void setPruneHook(PruneHookCall pruneHook) { pruneHook_ = pruneHook; }\n\n  PruneHookCall getPruneHook() { return pruneHook_; }\n\n  /**\n   * Prune the minimum of pruneSize and size() from the back of the LRU.\n   * Will throw if pruneHook throws.\n   * @param pruneSize minimum number of elements to prune\n   * @param pruneHook eviction callback to use INSTEAD OF the configured one\n   */\n  void prune(std::size_t pruneSize, PruneHookCall pruneHook = nullptr) {\n    auto& ph = (nullptr == pruneHook) ? pruneHook_ : pruneHook;\n\n    for (std::size_t i = 0; i < pruneSize && !lru_.empty(); i++) {\n      auto* node = &(*lru_.rbegin());\n      std::unique_ptr<Node> node_owner(node);\n\n      lru_.erase(lru_.iterator_to(*node));\n      index_.erase(node);\n      if (ph) {\n        // NOTE: might throw, so we are in an exception-safe state\n        ph(node->pr.first, std::move(node->pr.second));\n      }\n    }\n  }\n\n  // Iterators and such\n  iterator begin() { return iterator(lru_.begin()); }\n  iterator end() { return iterator(lru_.end()); }\n  const_iterator begin() const { return const_iterator(lru_.begin()); }\n  const_iterator end() const { return const_iterator(lru_.end()); }\n\n  const_iterator cbegin() const { return const_iterator(lru_.cbegin()); }\n  const_iterator cend() const { return const_iterator(lru_.cend()); }\n\n  reverse_iterator rbegin() { return reverse_iterator(lru_.rbegin()); }\n  reverse_iterator rend() { return reverse_iterator(lru_.rend()); }\n\n  const_reverse_iterator rbegin() const {\n    return const_reverse_iterator(lru_.rbegin());\n  }\n  const_reverse_iterator rend() const {\n    return const_reverse_iterator(lru_.rend());\n  }\n\n  const_reverse_iterator crbegin() const {\n    return const_reverse_iterator(lru_.crbegin());\n  }\n  const_reverse_iterator crend() const {\n    return const_reverse_iterator(lru_.crend());\n  }\n\n private:\n  struct Node\n      : public boost::intrusive::list_base_hook<\n            boost::intrusive::link_mode<boost::intrusive::safe_link>> {\n    template <typename K>\n    Node(const K& key, TValue&& value) : pr(key, std::move(value)) {}\n\n    template <typename Key, typename... Args>\n    explicit Node(std::piecewise_construct_t, Key&& k, Args&&... args)\n        : pr(std::piecewise_construct,\n             std::forward_as_tuple(std::forward<Key>(k)),\n             std::forward_as_tuple(std::forward<Args>(args)...)) {}\n    TPair pr;\n  };\n  using NodePtr = Node*;\n\n  // NOTE: deriving from boost::intrusive::list is likely discouraged. This is\n  // simply an alternative to an ugly explicit move operator for\n  // EvictingCacheMap. Change to that if this derivation proves problematic.\n  struct NodeList : public boost::intrusive::list<Node> {\n    NodeList() {}\n    NodeList& operator=(NodeList&& that) noexcept {\n      // Clear the moved-from rather than swap, for consistency with NodeMap\n      clear_nodes();\n      // Now invoke base class move operator without using static_cast\n      boost::intrusive::list<Node>& this_parent = *this;\n      boost::intrusive::list<Node>&& that_parent = std::move(that);\n      this_parent = std::move(that_parent);\n      return *this;\n    }\n    NodeList(NodeList&& that) noexcept { *this = std::move(that); }\n    ~NodeList() {\n      // Adds leak-free final destruction to the intrusive container\n      clear_nodes();\n    }\n\n   private:\n    void clear_nodes() {\n      boost::intrusive::list<Node>::clear_and_dispose([](Node* ptr) {\n        delete ptr;\n      });\n    }\n  };\n\n  struct KeyHasher : THash {\n    static_assert(std::is_nothrow_copy_constructible_v<THash>);\n    template <typename K>\n    static inline constexpr bool nx =\n        is_nothrow_invocable_v<THash const&, K const&>;\n\n    using is_transparent = void;\n    using folly_is_avalanching = IsAvalanchingHasher<THash, TKey>;\n\n    using THash::THash;\n\n    explicit KeyHasher(THash const& that) noexcept : THash(that) {}\n\n    template <typename K>\n    std::size_t operator()(const K& key) const noexcept(nx<K>) {\n      return THash::operator()(key);\n    }\n    std::size_t operator()(const NodePtr& node) const noexcept(nx<TKey>) {\n      return THash::operator()(node->pr.first);\n    }\n  };\n\n  struct KeyValueEqual : private TKeyEqual {\n    static_assert(std::is_nothrow_copy_constructible_v<TKeyEqual>);\n    template <typename L, typename R>\n    static inline constexpr bool nx =\n        is_nothrow_invocable_v<TKeyEqual const&, L const&, R const&>;\n\n    using is_transparent = void;\n\n    using TKeyEqual::TKeyEqual;\n\n    explicit KeyValueEqual(TKeyEqual const& that) noexcept : TKeyEqual(that) {}\n\n    template <typename K>\n    bool operator()(const K& lhs, const NodePtr& rhs) const\n        noexcept(nx<K, TKey>) {\n      return TKeyEqual::operator()(lhs, rhs->pr.first);\n    }\n    template <typename K>\n    bool operator()(const NodePtr& lhs, const K& rhs) const\n        noexcept(nx<TKey, K>) {\n      return TKeyEqual::operator()(lhs->pr.first, rhs);\n    }\n    bool operator()(const NodePtr& lhs, const NodePtr& rhs) const\n        noexcept(nx<TKey, TKey>) {\n      return TKeyEqual::operator()(lhs->pr.first, rhs->pr.first);\n    }\n  };\n\n  template <typename K>\n  bool existsImpl(const K& key) const {\n    return findInIndex(key) != nullptr;\n  }\n\n  template <typename K>\n  TValue& getImpl(const K& key) {\n    auto it = findImpl(*this, key);\n    if (it == end()) {\n      throw_exception<std::out_of_range>(\"Key does not exist\");\n    }\n    return it->second;\n  }\n\n  template <typename Self>\n  using self_iterator_t =\n      std::conditional_t<std::is_const<Self>::value, const_iterator, iterator>;\n\n  template <typename Self, typename K>\n  static auto findImpl(Self& self, const K& key) {\n    Node* ptr = self.findInIndex(key);\n    if (!ptr) {\n      return self.end();\n    }\n    self.lru_.splice(self.lru_.begin(), self.lru_, self.lru_.iterator_to(*ptr));\n    return self_iterator_t<Self>(self.lru_.iterator_to(*ptr));\n  }\n\n  template <typename Self, typename K>\n  static auto& getWithoutPromotionImpl(Self& self, const K& key) {\n    auto it = self.findWithoutPromotion(key);\n    if (it == self.end()) {\n      throw_exception<std::out_of_range>(\"Key does not exist\");\n    }\n    return it->second;\n  }\n\n  template <typename Self, typename K>\n  static auto findWithoutPromotionImpl(Self& self, const K& key) {\n    Node* ptr = self.findInIndex(key);\n    return ptr\n        ? self_iterator_t<Self>(self.lru_.iterator_to(*ptr))\n        : self.end();\n  }\n\n  typename NodeList::iterator eraseImpl(\n      Node* ptr,\n      typename NodeList::const_iterator base_iter,\n      PruneHookCall eraseHook) {\n    std::unique_ptr<Node> node_owner(ptr);\n    index_.erase(ptr);\n    auto next_base_iter = lru_.erase(base_iter);\n    if (eraseHook) {\n      // NOTE: might throw, so we are in an exception-safe state\n      eraseHook(ptr->pr.first, std::move(ptr->pr.second));\n    }\n    return next_base_iter;\n  }\n\n  template <typename K>\n  bool eraseKeyImpl(const K& key, PruneHookCall eraseHook) {\n    Node* ptr = findInIndex(key);\n    if (ptr) {\n      eraseImpl(ptr, lru_.iterator_to(*ptr), eraseHook);\n      return true;\n    }\n    return false;\n  }\n\n  template <typename K>\n  void setImpl(\n      const K& key, TValue&& value, bool promote, PruneHookCall pruneHook) {\n    Node* ptr = findInIndex(key);\n    if (ptr) {\n      ptr->pr.second = std::move(value);\n      if (promote) {\n        lru_.splice(lru_.begin(), lru_, lru_.iterator_to(*ptr));\n      }\n    } else {\n      auto node = new Node(key, std::move(value));\n      index_.insert(node);\n      lru_.push_front(*node);\n\n      // no evictions if maxSize_ is 0 i.e. unlimited capacity\n      if (maxSize_ > 0 && size() > maxSize_) {\n        prune(clearSize_, pruneHook);\n      }\n    }\n  }\n\n  template <typename K>\n  auto insertImpl(const K& key, TValue&& value, PruneHookCall pruneHook) {\n    auto node_owner = std::make_unique<Node>(key, std::move(value));\n    return insertImpl<K>(std::move(node_owner), std::move(pruneHook));\n  }\n\n  template <typename K>\n  auto insertImpl(std::unique_ptr<Node> nodeOwner, PruneHookCall pruneHook) {\n    Node* node = nodeOwner.get();\n    {\n      auto pair = index_.insert(node);\n      if (!pair.second) {\n        // No change. Abandon/destroy new node.\n        return std::pair<iterator, bool>(lru_.iterator_to(**pair.first), false);\n      }\n\n      // upcoming prune might invalidate iterator\n      assert(*pair.first == node);\n    }\n\n    // Complete insertion\n    lru_.push_front(*nodeOwner.release());\n\n    // no evictions if maxSize_ is 0 i.e. unlimited capacity\n    if (maxSize_ > 0 && size() > maxSize_) {\n      prune(clearSize_, pruneHook);\n    }\n\n    return std::pair<iterator, bool>(lru_.iterator_to(*node), true);\n  }\n\n  template <typename K>\n  Node* findInIndex(const K& key) const {\n    auto it = index_.find(key);\n    if (it != index_.end()) {\n      return *it;\n    } else {\n      return nullptr;\n    }\n  }\n\n  // A zero clear size doesn't make sense. If you want to disable clearing, set\n  // maxSize to 0.\n  static std::size_t clampClearSize(std::size_t clearSize) {\n    return std::max(clearSize, std::size_t{1});\n  }\n\n  PruneHookCall pruneHook_;\n  KeyHasher keyHash_;\n  KeyValueEqual keyEqual_;\n  NodeMap index_;\n  NodeList lru_;\n  std::size_t maxSize_;\n  std::size_t clearSize_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/F14.md",
    "content": "# F14 Hash Table\n\nF14 is a 14-way probing hash table that resolves collisions by double\nhashing.  Up to 14 keys are stored in a chunk at a single hash table\nposition.  Vector instructions (SSE2 on x86_64, NEON on aarch64)\nare used to filter within a chunk; intra-chunk search takes only a\nhandful of instructions.  **F14** refers to the fact that the algorithm\n**F**ilters up to **14** keys at a time.  This strategy allows the hash\ntable to be operated at a high maximum load factor (12/14) while still\nkeeping probe chains very short.\n\nF14 provides compelling replacements for most of the hash tables we use in\nproduction at Facebook.  Switching to it can improve memory efficiency\nand performance at the same time.  The hash table implementations\nwidely deployed in C++ at Facebook exist along a spectrum of space/time\ntradeoffs.  The fastest is the least memory efficient, and the most\nmemory efficient (`google::sparse_hash_map`) is much slower than the rest.\nF14 moves the curve, simultaneously improving memory efficiency and\nperformance when compared to most of the existing algorithms.\n\n## F14 VARIANTS\n\nThe core hash table implementation has a pluggable storage strategy,\nwith three policies provided:\n\n`F14NodeMap` stores values indirectly, calling malloc on each insert like\n`std::unordered_map`.  This implementation is the most memory efficient\nfor medium and large keys.  It provides the same iterator and reference\nstability guarantees as the standard map while being faster and more\nmemory efficient, so you can substitute `F14NodeMap` for `std::unordered_map`\nsafely in production code.  F14's filtering substantially reduces\nindirection (and cache misses) when compared to `std::unordered_map`.\n\n`F14ValueMap` stores values inline, like `google::dense_hash_map`.\nInline storage is the most memory efficient for small values, but for\nmedium and large values it wastes space.  Because it can tolerate a much\nhigher load factor, `F14ValueMap` is almost twice as memory efficient as\n`dense_hash_map` while also faster for most workloads.\n\n`F14VectorMap` keeps values packed in a contiguous array.  The main hash\narray stores 32-bit indexes into the value vector.  Compared to the\nexisting internal implementations that use a similar strategy, F14 is\nslower for simple keys and small or medium-sized tables (because of the\ncost of bit mixing), faster for complex keys and large tables, and saves\nabout 16 bytes per entry on average.\n\nWe also provide:\n\n`F14FastMap` inherits from either F14ValueMap or F14VectorMap depending\non entry size. When the key and mapped_type are less than 24 bytes, it\ninherits from `F14ValueMap`. For medium and large entries, it inherits\nfrom `F14VectorMap`. This strategy provides the best performance, while\nalso providing better memory efficiency than `dense_hash_map` or the other\nhash tables in use at Facebook that don't individually allocate nodes.\n\n## WHICH F14 VARIANT IS RIGHT FOR ME?\n\nF14FastMap is a good default choice. If you care more about memory\nefficiency than performance, F14NodeMap is better for medium and\nlarge entries.  F14NodeMap is the only F14 variant that doesn't move\nits elements, so in the rare case that you need reference stability you\nshould use it.\n\n## HETEROGENEOUS KEY TYPE WITH TRANSPARENT HASH AND EQUALITY\n\nIn some cases it makes sense to define hash and key equality across\ntypes.  For example, `StringPiece`'s hash and equality are capable of\naccepting `std::string` (because `std::string` is implicitly convertible\nto `StringPiece`).  If you mark the hash functor and key equality functor\nas _transparent_, then F14 will allow you to search the table directly\nusing any of the accepted key types without converting the key.\n\nFor example, using `H =\nfolly::transparent<folly::hasher<folly::StringPiece>>` and\n`E = folly::transparent<std::equal_to<folly::StringPiece>>`, an\n`F14FastSet<std::string, H, E>` will allow you to use a `StringPiece` key\nwithout the need to construct a `std::string`.\n\nHeterogeneous lookup and erase works for any key types that can be passed\nto operator() on the hasher and key_equal functors.  For operations\nsuch as operator[] that might insert there is an additional constraint,\nwhich is that the passed-in key must be explicitly convertible to the\ntable's key_type.  F14 maps understand all possible forms that can be\nused to construct the underlying `std::pair<key_type const, value_type)`,\nso heterogeneous keys can be used even with insert and emplace.\n\n## RANDOMIZED BEHAVIOR IN DEBUG BUILDS\n\nF14 introduces randomness into its behavior in debug builds and when\nthe address sanitizer (ASAN) is in use.  This randomness is designed to\nexpose bugs during test that might otherwise only occur in production.\nBugs are exposed probabilistically, they may appear only some of the time.\n\nIn debug builds `F14ValueMap` and `F14NodeMap` randomize the relationship\nbetween insertion and iteration order.  This means that adding the same\nk1 and k2 to two empty maps (or the same map twice after clearing it)\ncan produce the iteration order k1,k2 or k2,k1.  Unit tests will\nfail if they assume that the iteration order is the same between\nidentically constructed maps, even in the same process.  This also\naffects `folly::dynamic`'s object mode.\n\nWhen the address sanitizer is enabled all of the F14 variants perform some\nrandomized extra rehashes on insert, which exposes iterator and reference\nstability issues.  If reserve(size()+n) (or a non-zero initialCapacity)\nis called then the following n insertions are exempt from the spurious\nfailures.  Tracking is per-thread rather than per-table so this heuristic\ncould lead to false positives, although we haven't seen any yet.\n(Please let us know if you encounter this problem.)\n\n## WHY CHUNKS?\n\nAssuming that you have a magic wand that lets you search all of the keys\nin a chunk in a single step (our wand is called _mm_cmpeq_epi8), then\nusing chunks fundamentally improves the load factor/collision tradeoff.\nThe cost is proportional only to the number of chunks visited to find\nthe key.\n\nIt's kind of like the birthday paradox in reverse.  In a room with 23\npeople there is a 50/50 chance that two of them have the same birthday\n(overflowing a chunk with capacity 1), but the chance that 8 of them\nwere born in the same week (overflowing a chunk with capacity 7) is\nvery small.  Even though the chance of any two people being born in\nthe same week is higher (1/52 instead of 1/365), the larger number of\ncoincidences required means that the final probability is much lower\n(less than 1 in a million). It would require 160 people to reach a 50/50\nchance that 8 of them were born in the same week.\n\n## WHY PROBING?\n\nChaining to a new chunk on collision is not very memory efficient,\nbecause the new chunk is almost certain to be under-filled.  We tried\nchaining to individual entries, but that bloated the lookup code and\ncan't match the performance of a probing strategy.\n\nAt our max load factor of 12/14, the expected probe length when searching\nfor an existing key (find hit) is 1.04, and fewer than 1% of keys are\nnot found in one of the first 3 chunks.  When searching for a key that is\nnot in the map (find miss) the expected probe length at max load factor\nis 1.275 and the P99 probe length is 4.\n\n## CHUNK OVERFLOW COUNTS: REFERENCE-COUNTED TOMBSTONES\n\nHash tables with a complex probing strategy (quadratic or double-hashing)\ntypically use a tombstone on erase, because it is very difficult to\nfind the keys that might have been displaced by a full bucket (i.e.,\nchunk in F14).  If the probing strategy allows only a small number of\npotential destinations for a displaced key (linear probing, Robin Hood\nhashing, or Cuckoo hashing), it is also an option to find a displaced key,\nrelocate it, and then recursively repair the new hole.\n\nTombstones must be eventually reclaimed to deal with workloads that\ncontinuously insert and erase.  `google::dense_hash_map` eventually triggers\na rehash in this case, for example.  Unfortunately, to avoid quadratic\nbehavior this rehash may have to halve the max load factor of the table,\nresulting in a huge decrease in memory efficiency.\n\nAlthough most probing algorithms just keep probing until they find an\nempty slot, probe lengths can be substantially reduced if you track\nwhether a bucket has actually rejected a key.  This \"overflow bit\"\nis set when an attempt is made to place a key into the bucket but the\nbucket was full.  (An especially unlucky key might have to try several\nbuckets, setting the overflow bit in each.)  Amble and Knuth describe an\noverflow bit in the \"Further development\" section of \"Ordered hash tables\"\n(https://academic.oup.com/comjnl/article/17/2/135/525363).\n\nThe overflow bit subsumes the role of a tombstone, since a tombstone's\nonly effect is to cause a probe search to continue.  Unlike a tombstone,\nhowever, the overflow bit is a property of the keys that were displaced\nrather than the key that was erased.  It's only a small step to turn\nthis into a counter that records the number of displaced keys, and that\ncan be decremented on erase.  Overflow counts give us both an earlier\nexit from probing and the effect of a reference-counted tombstone.\nThey automatically clean themselves up in a steady-state insert and\nerase workload, giving us the upsides of double-hashing without the\nnormal downsides of tombstones.\n\n## HOW DOES VECTOR FILTERING WORK?\n\nF14 computes a secondary hash value for each key, which we call the key's\ntag.  Tags are 1 byte: 7 bits of entropy with the top bit set.  The 14\ntags are joined with 2 additional bytes of metadata to form a 16-byte\naligned __m128i at the beginning of the chunk.  When we're looking for a\nkey we can compare the needle's tag to all 14 tags in a chunk in parallel.\nThe result of the comparison is a bitmask that identifies only slots in\na chunk that might have a non-empty matching key.  Failing searches are\nunlikely to perform any key comparisons, successful searches are likely\nto perform exactly 1 comparison, and all of the resulting branches are\npretty predictable.\n\nThe vector search is coded using SIMD intrinsics, SSE2 on x86_64 and\nNEON on aarch64.  These instructions are a non-optional part of those\nplatforms (unlike later SIMD instruction sets like AVX2 or SVE), so no\nspecial compilation flags are required.  The exact vector operations\nperformed differs between x86_64 and aarch64 because aarch64 lacks a\nmovemask instruction, but the F14 algorithm is the same.\n\n## WHAT ABOUT MEMORY OVERHEAD FOR SMALL TABLES?\n\nThe F14 algorithm works well for large tables, because the tags can\nfit in cache even when the keys and values can't.  Tiny hash tables are\nby far the most numerous, however, so it's important that we minimize\nthe footprint when the table is empty or has only 1 or 2 elements.\nConveniently, tags cause keys to be densely packed into the bottom of\na chunk and filter all memory accesses to the portions of a chunk that\nare not used.  That means that we can also support capacities that are\na fraction of 1 chunk with no change to any of the search and insertion\nalgorithms.  The only change required is in the check to see if a rehash\nis required.  F14's first three capacities all use one chunk and one\n16-byte metadata vector, but allocate space for 2, 6, and then 14 keys.\n\n## MEMORY OVERHEAD WITH EXPLICIT CAPACITY REQUEST\n\nWhen using the vector storage strategy F14 has the option of sizing the\nmain hash array and the value_type storage array independently. In the\ncase that an explicit capacity has been requested (`initialCapacity`\npassed to a constructor or a call to `reserve` or `rehash`) we take\nadvantage of this, trying to exactly fit the capacity to the requested\nquantity. We also try to exactly fit the capacity for the other storage\nstrategies if there is only a single chunk. To avoid pathologies from\ncode that calls `reserve` a lot, doubling is performed for increases\nif there is not at least a 1/8 increase in capacity and decreases are\nignored, with the exception of `reserve(n)` for `n <= size()`, which\nbehaves like `shrink_to_fit`.\n\n## IS F14NODEMAP FULLY STANDARDS-COMPLIANT?\n\nNo.  F14 does provide full support for stateful allocators, fancy\npointers, and as many parts of the C++ standard for unordered associative\ncontainers as it can, but it is not fully standards-compliant.\n\nWe don't know of a way to efficiently implement the full bucket API\nin a table that uses double-hashed probing, in particular size_type\nbucket(key_type const&).  This function must compute the bucket index\nfor any key, even before it is inserted into the table.  That means\nthat a local_iterator range can't partition the key space by the chunk\nthat terminated probing during insert; the only partition choice with\nreasonable locality would be the first-choice chunk.  The probe sequence\nfor a key in double-hashing depends on the key, not the first-choice\nchunk, however, so it is infeasible to search for all of the displaced\nkeys given only their first-choice location.  We're unwilling to use an\ninferior probing strategy or dedicate space to the required metadata just\nto support the full bucket API.  Implementing the rest of the bucket API,\nsuch as local_iterator begin(size_type), would not be difficult.\n\nF14 does not allow max_load_factor to be adjusted.  Probing tables\ncan't support load factors greater than 1, so the standards-required\nability to temporarily disable rehashing by temporarily setting a very\nhigh max load factor just isn't possible.  We have also measured that\nthere is no performance advantage to forcing a low load factor, so it's\nbetter just to omit the field and save space in every F14 instance.\nThis is part of the way we get empty maps down to 32 bytes.  The void\nmax_load_factor(float) method is still present, but does nothing.  We use\nthe default max_load_factor of 1.0f all of the time, adjusting the value\nreturned from size_type bucket_count() so that the externally-visible\nload factor reaches 1 just as the actual internal load factor reaches\nour threshold of 12/14.\n\nThe standard requires that a hash table be iterable in O(size()) time\nregardless of its load factor (rather than O(bucket_count()).  That means\nif you insert 1 million keys then erase all but 10, iteration should\nbe O(10).  For `std::unordered_map` the cost of supporting this scenario\nis an extra level of indirection in every read and every write, which is\npart of why we can improve substantially on its performance.  Low load\nfactor iteration occurs in practice when erasing keys during iteration\n(for example by repeatedly calling map.erase(map.begin())), so we provide\nthe weaker guarantee that iteration is O(size()) after erasing any prefix\nof the iteration order.  F14VectorMap doesn't have this problem.\n\nThe standard requires that the order of the elements that are not erased be\npreserved (since c++14). This is a stricter requirement than necessary to make\nerasing individual elements while iterating through the container possible,\nsince all of the patterns we have seen for doing this don't care about order for\nelements before the erased one. All F14 maps and sets support erase during\niteration, but F14Fast and F14Vector don't guarantee to preserve the relative\norder of elements earlier in the iteration order than the erased element.\n\nThe standard requires that clear() be O(size()), which has the practical\neffect of prohibiting a change to bucket_count.  F14 deallocates\nall memory during clear() if it has space for more than 100 keys, to\navoid leaving a large table that will be expensive to iterate (see the\nprevious paragraph).  `google::dense_hash_map` works around this tradeoff\nby providing both clear() and clear_no_resize(); we could do something\nsimilar.\n\nAs stated above, `F14NodeMap` and `F14NodeSet` are the only F14 variants\nthat provides reference stability.  When running under ASAN the other\nstorage policies will probabilistically perform extra rehashes, which\nmakes it likely that reference stability problems will be found by the\naddress sanitizer.\n\nAn additional subtlety for hash tables that don't provide reference\nstability is whether they rehash before evaluating the arguments passed\nto `insert()`.  F14 tables may rehash before evaluating the arguments\nto a method that causes an insertion, so it's not safe to write\nsomething like `map.insert(k2, map[k1])` with `F14FastMap`, `F14ValueMap`,\nor `F14VectorMap`.  This behavior matches `google::dense_hash_map` and the\nexcellent `absl::flat_hash_map`.\n\n`F14NodeMap` does not currently support the C++17 node API, but it could\nbe trivially added.\n"
  },
  {
    "path": "folly/container/F14Map-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <utility>\n\n#include <folly/container/detail/F14Defaults.h>\n#include <folly/memory/MemoryResource.h>\n\nnamespace folly {\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>>\nclass F14NodeMap;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>>\nclass F14ValueMap;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>>\nclass F14VectorMap;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<Key const, Mapped>>>\nclass F14FastMap;\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nnamespace pmr {\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14NodeMap = folly::F14NodeMap<\n    Key,\n    Mapped,\n    Hasher,\n    KeyEqual,\n    std::pmr::polymorphic_allocator<std::pair<Key const, Mapped>>>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14ValueMap = folly::F14ValueMap<\n    Key,\n    Mapped,\n    Hasher,\n    KeyEqual,\n    std::pmr::polymorphic_allocator<std::pair<Key const, Mapped>>>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14VectorMap = folly::F14VectorMap<\n    Key,\n    Mapped,\n    Hasher,\n    KeyEqual,\n    std::pmr::polymorphic_allocator<std::pair<Key const, Mapped>>>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14FastMap = folly::F14FastMap<\n    Key,\n    Mapped,\n    Hasher,\n    KeyEqual,\n    std::pmr::polymorphic_allocator<std::pair<Key const, Mapped>>>;\n} // namespace pmr\n#endif // FOLLY_HAS_MEMORY_RESOURCE\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/F14Map.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n/**\n * F14NodeMap, F14ValueMap, and F14VectorMap\n *\n * F14FastMap conditionally works like F14ValueMap or F14VectorMap\n *\n * See F14.md\n */\n\n#include <cstddef>\n#include <initializer_list>\n#include <stdexcept>\n#include <tuple>\n\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/Traits.h>\n#include <folly/container/View.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/SafeAssert.h>\n\n#include <folly/container/F14Map-fwd.h>\n#include <folly/container/Iterator.h>\n#include <folly/container/detail/F14Policy.h>\n#include <folly/container/detail/F14Table.h>\n#include <folly/container/detail/Util.h>\n\n// If !FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE, fallback definitions are exported\n// in this file\n#include <folly/container/detail/F14MapFallback.h> // IWYU pragma: export\n\nnamespace folly {\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\n//////// Common case for supported platforms\n\nnamespace f14 {\nnamespace detail {\n\ntemplate <typename Policy>\nclass F14BasicMap {\n  template <typename K, typename T>\n  using EnableHeterogeneousFind = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          typename Policy::Key,\n          typename Policy::Hasher,\n          typename Policy::KeyEqual,\n          K>::value,\n      T>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousInsert = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousInsert<\n          typename Policy::Key,\n          typename Policy::Hasher,\n          typename Policy::KeyEqual,\n          K>::value,\n      T>;\n\n  template <typename K>\n  using IsIter = Disjunction<\n      std::is_same<typename Policy::Iter, remove_cvref_t<K>>,\n      std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousErase = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          typename Policy::Key,\n          typename Policy::Hasher,\n          typename Policy::KeyEqual,\n          std::conditional_t<IsIter<K>::value, typename Policy::Key, K>>::\n              value &&\n          !IsIter<K>::value,\n      T>;\n\n public:\n  //// PUBLIC - Member types\n\n  using key_type = typename Policy::Key;\n  using mapped_type = typename Policy::Mapped;\n  using value_type = typename Policy::Value;\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n  using hash_token_type = F14HashToken;\n  using hasher = typename Policy::Hasher;\n  using key_equal = typename Policy::KeyEqual;\n  using hashed_key_type = F14HashedKey<key_type, hasher, key_equal>;\n  using allocator_type = typename Policy::Alloc;\n  using reference = value_type&;\n  using const_reference = value_type const&;\n  using pointer = typename Policy::AllocTraits::pointer;\n  using const_pointer = typename Policy::AllocTraits::const_pointer;\n  using iterator = typename Policy::Iter;\n  using const_iterator = typename Policy::ConstIter;\n\n private:\n  using ItemIter = typename Policy::ItemIter;\n\n public:\n  //// PUBLIC - Member functions\n\n  F14BasicMap() noexcept(Policy::kDefaultConstructIsNoexcept) : table_{} {}\n\n  explicit F14BasicMap(\n      std::size_t initialCapacity,\n      hasher const& hash = hasher{},\n      key_equal const& eq = key_equal{},\n      allocator_type const& alloc = allocator_type{})\n      : table_{initialCapacity, hash, eq, alloc} {}\n\n  explicit F14BasicMap(std::size_t initialCapacity, allocator_type const& alloc)\n      : F14BasicMap(initialCapacity, hasher{}, key_equal{}, alloc) {}\n\n  explicit F14BasicMap(\n      std::size_t initialCapacity,\n      hasher const& hash,\n      allocator_type const& alloc)\n      : F14BasicMap(initialCapacity, hash, key_equal{}, alloc) {}\n\n  explicit F14BasicMap(allocator_type const& alloc)\n      : F14BasicMap(0, hasher{}, key_equal{}, alloc) {}\n\n  template <typename InputIt>\n  F14BasicMap(\n      InputIt first,\n      InputIt last,\n      std::size_t initialCapacity = 0,\n      hasher const& hash = hasher{},\n      key_equal const& eq = key_equal{},\n      allocator_type const& alloc = allocator_type{})\n      : table_{initialCapacity, hash, eq, alloc} {\n    initialInsert(std::move(first), std::move(last), initialCapacity);\n  }\n\n  template <typename InputIt>\n  F14BasicMap(\n      InputIt first,\n      InputIt last,\n      std::size_t initialCapacity,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hasher{}, key_equal{}, alloc} {\n    initialInsert(std::move(first), std::move(last), initialCapacity);\n  }\n\n  template <typename InputIt>\n  F14BasicMap(\n      InputIt first,\n      InputIt last,\n      std::size_t initialCapacity,\n      hasher const& hash,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hash, key_equal{}, alloc} {\n    initialInsert(std::move(first), std::move(last), initialCapacity);\n  }\n\n  F14BasicMap(F14BasicMap const& rhs) = default;\n\n  F14BasicMap(F14BasicMap const& rhs, allocator_type const& alloc)\n      : table_{rhs.table_, alloc} {}\n\n  F14BasicMap(F14BasicMap&& rhs) = default;\n\n  F14BasicMap(F14BasicMap&& rhs, allocator_type const& alloc) noexcept(\n      Policy::kAllocIsAlwaysEqual)\n      : table_{std::move(rhs.table_), alloc} {}\n\n  /* implicit */ F14BasicMap(\n      std::initializer_list<value_type> init,\n      std::size_t initialCapacity = 0,\n      hasher const& hash = hasher{},\n      key_equal const& eq = key_equal{},\n      allocator_type const& alloc = allocator_type{})\n      : table_{initialCapacity, hash, eq, alloc} {\n    initialInsert(init.begin(), init.end(), initialCapacity);\n  }\n\n  F14BasicMap(\n      std::initializer_list<value_type> init,\n      std::size_t initialCapacity,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hasher{}, key_equal{}, alloc} {\n    initialInsert(init.begin(), init.end(), initialCapacity);\n  }\n\n  F14BasicMap(\n      std::initializer_list<value_type> init,\n      std::size_t initialCapacity,\n      hasher const& hash,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hash, key_equal{}, alloc} {\n    initialInsert(init.begin(), init.end(), initialCapacity);\n  }\n\n  F14BasicMap& operator=(F14BasicMap const&) = default;\n\n  F14BasicMap& operator=(F14BasicMap&&) = default;\n\n  F14BasicMap& operator=(std::initializer_list<value_type> ilist) {\n    clear();\n    bulkInsert(ilist.begin(), ilist.end(), true);\n    return *this;\n  }\n\n  /// Get the allocator for this container.\n  /// @methodset Allocator\n  allocator_type get_allocator() const noexcept { return table_.alloc(); }\n\n  //// PUBLIC - Iterators\n\n  /// Get an iterator to the beginning\n  /// @methodset Iterators\n  iterator begin() noexcept { return table_.makeIter(table_.begin()); }\n  const_iterator begin() const noexcept { return cbegin(); }\n  /// Get an iterator to the beginning\n  /// @methodset Iterators\n  const_iterator cbegin() const noexcept {\n    return table_.makeConstIter(table_.begin());\n  }\n\n  /// Get an iterator to the end\n  /// @methodset Iterators\n  iterator end() noexcept { return table_.makeIter(table_.end()); }\n  const_iterator end() const noexcept { return cend(); }\n  /// Get an iterator to the end\n  /// @methodset Iterators\n  const_iterator cend() const noexcept {\n    return table_.makeConstIter(table_.end());\n  }\n\n  //// PUBLIC - Capacity\n\n  /// Check if this container has any elements.\n  /// @methodset Capacity\n  bool empty() const noexcept { return table_.empty(); }\n\n  /// Number of elements in this container.\n  /// @methodset Capacity\n  std::size_t size() const noexcept { return table_.size(); }\n\n  /// The maximum size of this container.\n  /// @methodset Capacity\n  std::size_t max_size() const noexcept { return table_.max_size(); }\n\n  //// PUBLIC - Modifiers\n\n  /**\n   * Remove all elements.\n   * @methodset Modifiers\n   *\n   * Frees heap-allocated memory; bucket_count is returned to 0.\n   */\n  void clear() noexcept { table_.clear(); }\n\n  /**\n   * Add a single element.\n   * @overloadbrief Add elements.\n   * @methodset Modifiers\n   */\n  std::pair<iterator, bool> insert(value_type const& value) {\n    return emplace(value);\n  }\n\n  /// Add a single element, of a heterogeneous type.\n  template <typename P>\n  std::enable_if_t<\n      std::is_constructible<value_type, P&&>::value,\n      std::pair<iterator, bool>>\n  insert(P&& value) {\n    return emplace(std::forward<P>(value));\n  }\n\n  /**\n   * Add a single element, of a heterogeneous type.\n   *\n   * TODO(T31574848): Work around libstdc++ versions (e.g., GCC < 6) with no\n   * implementation of N4387 (\"perfect initialization\" for pairs and tuples).\n   */\n  template <typename U1, typename U2>\n  std::enable_if_t<\n      std::is_constructible<key_type, U1 const&>::value &&\n          std::is_constructible<mapped_type, U2 const&>::value,\n      std::pair<iterator, bool>>\n  insert(std::pair<U1, U2> const& value) {\n    return emplace(value);\n  }\n\n  /**\n   * Add a single element, of a heterogeneous type.\n   *\n   * TODO(T31574848): Work around libstdc++ versions (e.g., GCC < 6) with no\n   * implementation of N4387 (\"perfect initialization\" for pairs and tuples).\n   */\n  template <typename U1, typename U2>\n  std::enable_if_t<\n      std::is_constructible<key_type, U1&&>::value &&\n          std::is_constructible<mapped_type, U2&&>::value,\n      std::pair<iterator, bool>>\n  insert(std::pair<U1, U2>&& value) {\n    return emplace(std::move(value));\n  }\n\n  /// Add a single element.\n  std::pair<iterator, bool> insert(value_type&& value) {\n    return emplace(std::move(value));\n  }\n\n  // std::unordered_map's hinted insertion API is misleading.  No\n  // implementation I've seen actually uses the hint.  Code restructuring\n  // by the caller to use the hinted API is at best unnecessary, and at\n  // worst a pessimization.  It is used, however, so we provide it.\n\n  /// Add a single element, with a locational hint.\n  iterator insert(const_iterator /*hint*/, value_type const& value) {\n    return insert(value).first;\n  }\n\n  /// Add a single element, of heterogeneous type, with a locational hint.\n  template <typename P>\n  std::enable_if_t<std::is_constructible<value_type, P&&>::value, iterator>\n  insert(const_iterator /*hint*/, P&& value) {\n    return insert(std::forward<P>(value)).first;\n  }\n\n  /// Add a single element, with a locational hint.\n  iterator insert(const_iterator /*hint*/, value_type&& value) {\n    return insert(std::move(value)).first;\n  }\n\n  /// Emplace with hint.\n  /// @methodset Modifiers\n  template <class... Args>\n  iterator emplace_hint(const_iterator /*hint*/, Args&&... args) {\n    return emplace(std::forward<Args>(args)...).first;\n  }\n\n private:\n  template <class InputIt>\n  FOLLY_ALWAYS_INLINE void bulkInsert(\n      InputIt first, InputIt last, bool autoReserve) {\n    if (autoReserve) {\n      auto n = std::distance(first, last);\n      if (n == 0) {\n        return;\n      }\n      table_.reserveForInsert(n);\n    }\n    while (first != last) {\n      insert(*first);\n      ++first;\n    }\n  }\n\n  template <class InputIt>\n  void initialInsert(InputIt first, InputIt last, std::size_t initialCapacity) {\n    FOLLY_SAFE_DCHECK(empty() && bucket_count() >= initialCapacity, \"\");\n\n    // It's possible that there are a lot of duplicates in first..last and\n    // so we will oversize ourself.  The common case, however, is that\n    // we can avoid a lot of rehashing if we pre-expand.  The behavior\n    // is easy to disable at a particular call site by asking for an\n    // initialCapacity of 1.\n    bool autoReserve =\n        std::is_base_of<\n            std::random_access_iterator_tag,\n            typename std::iterator_traits<InputIt>::iterator_category>::value &&\n        initialCapacity == 0;\n    bulkInsert(std::move(first), std::move(last), autoReserve);\n  }\n\n public:\n  /// Add elements\n  template <class InputIt>\n  void insert(InputIt first, InputIt last) {\n    // Bulk reserve is a heuristic choice, so it can backfire.  We restrict\n    // ourself to situations that mimic bulk construction without an\n    // explicit initialCapacity.\n    bool autoReserve =\n        std::is_base_of<\n            std::random_access_iterator_tag,\n            typename std::iterator_traits<InputIt>::iterator_category>::value &&\n        bucket_count() == 0;\n    bulkInsert(std::move(first), std::move(last), autoReserve);\n  }\n\n  /// Add elements from an initializer list\n  void insert(std::initializer_list<value_type> ilist) {\n    insert(ilist.begin(), ilist.end());\n  }\n\n  /// Insert if the key is missing, overwrite using operator= if present.\n  /// @methodset Modifiers\n  template <typename M>\n  std::pair<iterator, bool> insert_or_assign(key_type const& key, M&& obj) {\n    auto rv = try_emplace(key, std::forward<M>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M>(obj);\n    }\n    return rv;\n  }\n\n  template <typename M>\n  std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj) {\n    auto rv = try_emplace(std::move(key), std::forward<M>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M>(obj);\n    }\n    return rv;\n  }\n\n  template <typename M>\n  std::pair<iterator, bool> insert_or_assign(\n      F14HashToken const& token, key_type const& key, M&& obj) {\n    auto rv = try_emplace_token(token, key, std::forward<M>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M>(obj);\n    }\n    return rv;\n  }\n\n  template <typename M>\n  std::pair<iterator, bool> insert_or_assign(\n      F14HashToken const& token, key_type&& key, M&& obj) {\n    auto rv = try_emplace_token(token, std::move(key), std::forward<M>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M>(obj);\n    }\n    return rv;\n  }\n\n  template <typename M>\n  iterator insert_or_assign(\n      const_iterator /*hint*/, key_type const& key, M&& obj) {\n    return insert_or_assign(key, std::forward<M>(obj)).first;\n  }\n\n  template <typename M>\n  iterator insert_or_assign(const_iterator /*hint*/, key_type&& key, M&& obj) {\n    return insert_or_assign(std::move(key), std::forward<M>(obj)).first;\n  }\n\n  template <typename K, typename M>\n  EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert_or_assign(\n      K&& key, M&& obj) {\n    auto rv = try_emplace(std::forward<K>(key), std::forward<M>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M>(obj);\n    }\n    return rv;\n  }\n\n  template <typename K, typename M>\n  EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert_or_assign(\n      F14HashToken const& token, K&& key, M&& obj) {\n    auto rv =\n        try_emplace_token(token, std::forward<K>(key), std::forward<M>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M>(obj);\n    }\n    return rv;\n  }\n\n private:\n  template <typename Arg>\n  using UsableAsKey = ::folly::detail::\n      EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;\n\n public:\n  /**\n   * Add an element by constructing it in-place.\n   * @overloadbrief Add elements in-place.\n   * @methodset Modifiers\n   */\n  template <typename... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    auto rv =\n        folly::detail::callWithExtractedKey<key_type, mapped_type, UsableAsKey>(\n            table_.alloc(),\n            [&](auto&&... inner) {\n              return table_.tryEmplaceValue(\n                  std::forward<decltype(inner)>(inner)...);\n            },\n            std::forward<Args>(args)...);\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  /**\n   * Add an element by constructing it in-place.\n   * @methodset Modifiers\n   *\n   * Does nothing if the key already exists.\n   */\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace(key_type const& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValue(\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(key),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace(key_type&& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValue(\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(std::move(key)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  /// @copydoc try_emplace\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace_token(\n      F14HashToken const& token, key_type const& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValueWithToken(\n        token,\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(key),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace_token(\n      F14HashToken const& token, key_type&& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValueWithToken(\n        token,\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(std::move(key)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  template <typename... Args>\n  iterator try_emplace(\n      const_iterator /*hint*/, key_type const& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValue(\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(key),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return table_.makeIter(rv.first);\n  }\n\n  template <typename... Args>\n  iterator try_emplace(\n      const_iterator /*hint*/, key_type&& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValue(\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(std::move(key)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return table_.makeIter(rv.first);\n  }\n\n  template <typename K, typename... Args>\n  EnableHeterogeneousInsert<K, std::pair<iterator, bool>> try_emplace(\n      K&& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValue(\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(std::forward<K>(key)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  template <typename K, typename... Args>\n  EnableHeterogeneousInsert<K, std::pair<iterator, bool>> try_emplace_token(\n      F14HashToken const& token, K&& key, Args&&... args) {\n    auto rv = table_.tryEmplaceValueWithToken(\n        token,\n        key,\n        std::piecewise_construct,\n        std::forward_as_tuple(std::forward<K>(key)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  /**\n   * Remove element at a specific position (iterator).\n   * @overloadbrief Remove elements.\n   * @methodset Modifiers\n   */\n  FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) {\n    return eraseInto(pos, variadic_noop);\n  }\n\n  /**\n   * Remove element at a specific position (iterator).\n   *\n   * This form avoids ambiguity when key_type has a templated constructor\n   * that accepts const_iterator\n   */\n  FOLLY_ALWAYS_INLINE iterator erase(iterator pos) {\n    return eraseInto(pos, variadic_noop);\n  }\n\n  /// Remove a range of elements.\n  iterator erase(const_iterator first, const_iterator last) {\n    return eraseInto(first, last, variadic_noop);\n  }\n\n  /// Remove a specific key.\n  size_type erase(key_type const& key) { return eraseInto(key, variadic_noop); }\n\n  /// Remove a key, using a heterogeneous representation.\n  template <typename K>\n  EnableHeterogeneousErase<K, size_type> erase(K const& key) {\n    return eraseInto(key, variadic_noop);\n  }\n\n protected:\n  template <typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE void tableEraseIterInto(\n      ItemIter pos, BeforeDestroy&& beforeDestroy) {\n    table_.eraseIterInto(pos, [&](value_type&& v) {\n      auto p = Policy::moveValue(v);\n      beforeDestroy(std::move(p.first), std::move(p.second));\n    });\n  }\n\n  template <typename K, typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE std::size_t tableEraseKeyInto(\n      K const& key, BeforeDestroy&& beforeDestroy) {\n    return table_.eraseKeyInto(key, [&](value_type&& v) {\n      auto p = Policy::moveValue(v);\n      beforeDestroy(std::move(p.first), std::move(p.second));\n    });\n  }\n\n public:\n  /**\n   * Callback-erase a single iterator.\n   * @overloadbrief Erase with pre-destruction callback.\n   * @methodset Modifiers\n   *\n   * eraseInto contains the same overloads as erase but provides\n   * an additional callback argument which is called with an rvalue\n   * reference (not const) to the key and an rvalue reference to the\n   * mapped value directly before it is destroyed. This can be used\n   * to extract an entry out of a F14Map while avoiding a copy.\n   */\n  template <typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE iterator\n  eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {\n    // If we are inlined then gcc and clang can optimize away all of the\n    // work of itemPos.advance() if our return value is discarded.\n    auto itemPos = table_.unwrapIter(pos);\n    tableEraseIterInto(itemPos, beforeDestroy);\n    itemPos.advanceLikelyDead();\n    return table_.makeIter(itemPos);\n  }\n\n  /// This form avoids ambiguity when key_type has a templated constructor\n  /// that accepts const_iterator\n  template <typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE iterator\n  eraseInto(iterator pos, BeforeDestroy&& beforeDestroy) {\n    const_iterator cpos{pos};\n    return eraseInto(cpos, beforeDestroy);\n  }\n\n  /// Callback-erase a range of values.\n  template <typename BeforeDestroy>\n  iterator eraseInto(\n      const_iterator first,\n      const_iterator last,\n      BeforeDestroy&& beforeDestroy) {\n    auto itemFirst = table_.unwrapIter(first);\n    auto itemLast = table_.unwrapIter(last);\n    while (itemFirst != itemLast) {\n      tableEraseIterInto(itemFirst, beforeDestroy);\n      itemFirst.advance();\n    }\n    return table_.makeIter(itemFirst);\n  }\n\n  template <typename BeforeDestroy>\n  size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {\n    return tableEraseKeyInto(key, beforeDestroy);\n  }\n\n  /// Callback-erase a specific key, using a heterogeneous representation.\n  template <typename K, typename BeforeDestroy>\n  EnableHeterogeneousErase<K, size_type> eraseInto(\n      K const& key, BeforeDestroy&& beforeDestroy) {\n    return tableEraseKeyInto(key, beforeDestroy);\n  }\n\n  //// PUBLIC - Lookup\n\n  /// Get a value for a key\n  /// @methodset Element Access\n  FOLLY_ALWAYS_INLINE mapped_type& at(key_type const& key) {\n    return at(*this, key);\n  }\n\n  FOLLY_ALWAYS_INLINE mapped_type const& at(key_type const& key) const {\n    return at(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, mapped_type&> at(K const& key) {\n    return at(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, mapped_type const&> at(K const& key) const {\n    return at(*this, key);\n  }\n\n  /// Get a value for a key; create the value if it doesn't already exist\n  /// @methodset Element Access\n  mapped_type& operator[](key_type const& key) {\n    return try_emplace(key).first->second;\n  }\n\n  mapped_type& operator[](key_type&& key) {\n    return try_emplace(std::move(key)).first->second;\n  }\n\n  template <typename K>\n  EnableHeterogeneousInsert<K, mapped_type&> operator[](K&& key) {\n    return try_emplace(std::forward<K>(key)).first->second;\n  }\n\n  /// @overloadbrief Number of elements matching the given key.\n  /// @methodset Lookup\n  FOLLY_ALWAYS_INLINE size_type count(key_type const& key) const {\n    return contains(key) ? 1 : 0;\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, size_type> count(\n      K const& key) const {\n    return contains(key) ? 1 : 0;\n  }\n\n  /**\n   * @overloadbrief Prehash a key.\n   * @methodset Lookup\n   *\n   * prehash(key) does the work of evaluating hash_function()(key)\n   * (including additional bit-mixing for non-avalanching hash functions),\n   * and wraps the result of that work in a token for later reuse.\n   *\n   * The returned token may be used at any time, may be used more than\n   * once, and may be used in other F14 sets and maps.  Tokens are\n   * transferrable between any F14 containers (maps and sets) with the\n   * same key_type and equal hash_function()s.\n   *\n   * Hash tokens are not hints -- it is a bug to call any method on this\n   * class with a token t and key k where t isn't the result of a call\n   * to prehash(k2) with k2 == k.\n   */\n  F14HashToken prehash(key_type const& key) const {\n    return table_.prehash(key);\n  }\n  /// @copydoc prehash\n  F14HashToken prehash(key_type const& key, std::size_t hash) const {\n    return table_.prehash(key, hash);\n  }\n\n  /// @copydoc prehash\n  template <typename K>\n  EnableHeterogeneousFind<K, F14HashToken> prehash(K const& key) const {\n    return table_.prehash(key);\n  }\n  /// @copydoc prehash\n  template <typename K>\n  EnableHeterogeneousFind<K, F14HashToken> prehash(\n      K const& key, std::size_t hash) const {\n    return table_.prehash(key, hash);\n  }\n\n  /**\n   * @overloadbrief Prefetch cachelines associated with a key.\n   * @methodset Lookup\n   *\n   * prefetch(token) begins prefetching the first steps of looking for key into\n   * the local CPU cache.\n   *\n   * Example Scenario: Loading 2 values from a cold map.\n   * You have a map that is cold, meaning it is out of the local CPU cache,\n   * and you want to load two values from the map. This can be extended to\n   * load N values, but we're loading 2 for simplicity.\n   *\n   * When the map is cold the dominating factor in the latency is loading the\n   * cache line of the entry into the local CPU cache. Using prehash() will\n   * issue these cache line fetches in parallel.  That means that by the time we\n   * finish map.find(token1, key1) the cache lines needed by map.find(token2,\n   * key2) may already be in the local CPU cache. In the best case this will\n   * half the latency.\n   *\n   * It is always okay to call prefetch() before a find() or other lookup\n   * operation, as it only prefetches cache lines that are guaranteed to be\n   * needed by the lookup.\n   *\n   *   std::pair<iterator, iterator> find2(\n   *       auto& map, key_type const& key1, key_type const& key2) {\n   *     auto const token1 = map.prehash(key1);\n   *     map.prefetch(token1);\n   *     auto const token2 = map.prehash(key2);\n   *     map.prefetch(token2);\n   *     return std::make_pair(map.find(token1, key1), map.find(token2, key2));\n   *   }\n   */\n  void prefetch(F14HashToken const& token) const { table_.prefetch(token); }\n\n  /// @overloadbrief Get the iterator for a key.\n  /// @methodset Lookup\n  FOLLY_ALWAYS_INLINE iterator find(key_type const& key) {\n    return table_.makeIter(table_.find(key));\n  }\n\n  /// Get the iterator for a key.\n  FOLLY_ALWAYS_INLINE const_iterator find(key_type const& key) const {\n    return table_.makeConstIter(table_.find(key));\n  }\n\n  FOLLY_ALWAYS_INLINE iterator\n  find(F14HashToken const& token, key_type const& key) {\n    return table_.makeIter(table_.find(token, key));\n  }\n\n  FOLLY_ALWAYS_INLINE const_iterator\n  find(F14HashToken const& token, key_type const& key) const {\n    return table_.makeConstIter(table_.find(token, key));\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(K const& key) {\n    return table_.makeIter(table_.find(key));\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find(\n      K const& key) const {\n    return table_.makeConstIter(table_.find(key));\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(\n      F14HashToken const& token, K const& key) {\n    return table_.makeIter(table_.find(token, key));\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find(\n      F14HashToken const& token, K const& key) const {\n    return table_.makeConstIter(table_.find(token, key));\n  }\n\n  /**\n   * @overloadbrief Checks if the container contains an element with the\n   * specific key.\n   * @methodset Lookup\n   */\n  FOLLY_ALWAYS_INLINE bool contains(key_type const& key) const {\n    return !table_.find(key).atEnd();\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains(\n      K const& key) const {\n    return !table_.find(key).atEnd();\n  }\n\n  FOLLY_ALWAYS_INLINE bool contains(\n      F14HashToken const& token, key_type const& key) const {\n    return !table_.find(token, key).atEnd();\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains(\n      F14HashToken const& token, K const& key) const {\n    return !table_.find(token, key).atEnd();\n  }\n\n  /// @overloadbrief Returns the range of elements matching a specific key.\n  /// @methodset Lookup\n  std::pair<iterator, iterator> equal_range(key_type const& key) {\n    return equal_range(*this, key);\n  }\n\n  std::pair<const_iterator, const_iterator> equal_range(\n      key_type const& key) const {\n    return equal_range(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, std::pair<iterator, iterator>> equal_range(\n      K const& key) {\n    return equal_range(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, std::pair<const_iterator, const_iterator>>\n  equal_range(K const& key) const {\n    return equal_range(*this, key);\n  }\n\n  //// PUBLIC - Bucket interface\n\n  /// The number of buckets in this container.\n  /// @methodset Bucket interface\n  std::size_t bucket_count() const noexcept { return table_.bucket_count(); }\n\n  /// The maximum number of buckets for this container.\n  /// @methodset Bucket interface\n  std::size_t max_bucket_count() const noexcept {\n    return table_.max_bucket_count();\n  }\n\n  //// PUBLIC - Hash policy\n\n  /// Load factor of the underlying hashtable.\n  /// @methodset Hash policy\n  float load_factor() const noexcept { return table_.load_factor(); }\n\n  /**\n   * @overloadbrief Load factor control.\n   * Get the maximum load factor for this container.\n   * @methodset Hash policy\n   */\n  float max_load_factor() const noexcept { return table_.max_load_factor(); }\n\n  /// Set the maximum load factor for this container.\n  /// @methodset Hash policy\n  void max_load_factor(float v) { table_.max_load_factor(v); }\n\n  /**\n   * Rehash this container.\n   * @methodset Hash policy\n   *\n   * This function is provided for compliance with C++'s requirements for\n   * hashtables, but is no better than a simple `reserve` call for F14.\n   *\n   * @param bucketCapacity  The desired capacity across all buckets.\n   */\n  void rehash(std::size_t bucketCapacity) {\n    // The standard's rehash() requires understanding the max load factor,\n    // which is easy to get wrong.  Since we don't actually allow adjustment\n    // of max_load_factor there is no difference.\n    reserve(bucketCapacity);\n  }\n\n  /**\n   * Pre-allocate space for at least this many elements.\n   * @methodset Capacity\n   *\n   * @param capacity  The number of elements to pre-allocate space for.\n   */\n  void reserve(std::size_t capacity) { table_.reserve(capacity); }\n\n  //// PUBLIC - Observers\n\n  /// Get the hasher.\n  /// @methodset Observers\n  hasher hash_function() const { return table_.hasher(); }\n\n  /// Get the key_equal.\n  /// @methodset Observers\n  key_equal key_eq() const { return table_.keyEqual(); }\n\n  //// PUBLIC - F14 Extensions\n\n  /**\n   * Checks for a value using operator==\n   * @methodset Lookup\n   *\n   * containsEqualValue returns true iff there is an element in the map\n   * that compares equal to value using operator==.  It is undefined\n   * behavior to call this function if operator== on key_type can ever\n   * return true when the same keys passed to key_eq() would return false\n   * (the opposite is allowed).\n   */\n  bool containsEqualValue(value_type const& value) const {\n    auto it = table_.findMatching(value.first, [&](auto& key) {\n      return value.first == key;\n    });\n    return !it.atEnd() && value.second == table_.valueAtItem(it.citem()).second;\n  }\n\n  /// Get memory footprint, not including sizeof(*this).\n  /// @methodset Capacity\n  std::size_t getAllocatedMemorySize() const {\n    return table_.getAllocatedMemorySize();\n  }\n\n  /**\n   * In-depth memory analysis.\n   * @methodset Capacity\n   *\n   * Enumerates classes of allocated memory blocks currently owned\n   * by this table, calling visitor(allocationSize, allocationCount).\n   * This can be used to get a more accurate indication of memory footprint\n   * than getAllocatedMemorySize() if you have some way of computing the\n   * internal fragmentation of the allocator, such as JEMalloc's nallocx.\n   * The visitor might be called twice with the same allocationSize. The\n   * visitor's computation should produce the same result for visitor(8,\n   * 2) as for two calls to visitor(8, 1), for example.  The visitor may\n   * be called with a zero allocationCount.\n   */\n  template <typename V>\n  void visitAllocationClasses(V&& visitor) const {\n    return table_.visitAllocationClasses(visitor);\n  }\n\n  /**\n   * Visit contiguous ranges of elements.\n   * @methodset Iterators\n   *\n   * Calls visitor with two value_type const*, b and e, such that every\n   * entry in the table is included in exactly one of the ranges [b,e).\n   * This can be used to efficiently iterate elements in bulk when crossing\n   * an API boundary that supports contiguous blocks of items.\n   */\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const;\n\n  /// Get stats.\n  /// @methodset Hash policy\n  F14TableStats computeStats() const noexcept { return table_.computeStats(); }\n\n private:\n  template <typename Self, typename K>\n  FOLLY_ALWAYS_INLINE static auto& at(Self& self, K const& key) {\n    auto iter = self.find(key);\n    if (iter == self.end()) {\n      throw_exception<std::out_of_range>(\"at() did not find key\");\n    }\n    return iter->second;\n  }\n\n  template <typename Self, typename K>\n  static auto equal_range(Self& self, K const& key) {\n    auto first = self.find(key);\n    auto last = first;\n    if (last != self.end()) {\n      ++last;\n    }\n    return std::make_pair(first, last);\n  }\n\n protected:\n  F14Table<Policy> table_;\n};\n} // namespace detail\n} // namespace f14\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14ValueMap\n    : public f14::detail::F14BasicMap<f14::detail::MapPolicyWithDefaults<\n          f14::detail::ValueContainerPolicy,\n          Key,\n          Mapped,\n          Hasher,\n          KeyEqual,\n          Alloc>> {\n protected:\n  using Policy = f14::detail::MapPolicyWithDefaults<\n      f14::detail::ValueContainerPolicy,\n      Key,\n      Mapped,\n      Hasher,\n      KeyEqual,\n      Alloc>;\n\n private:\n  using Super = f14::detail::F14BasicMap<Policy>;\n\n public:\n  using typename Super::value_type;\n\n  F14ValueMap() = default;\n\n  using Super::Super;\n\n  F14ValueMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  /// Swaps contained objects with another F14Map\n  /// @methodset Modifiers\n  void swap(F14ValueMap& rhs) noexcept(Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n\n  /**\n   * Visit contiguous ranges of elements.\n   * @methodset Iterators\n   *\n   * Calls visitor with two value_type const*, b and e, such that every\n   * entry in the table is included in exactly one of the ranges [b,e).\n   * This can be used to efficiently iterate elements in bulk when crossing\n   * an API boundary that supports contiguous blocks of items.\n   */\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    this->table_.visitContiguousItemRanges(visitor);\n  }\n};\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    // Next two constraints are necessary to disambiguate from next constructor\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueMap(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14ValueMap<\n        iterator_key_type_t<InputIt>,\n        iterator_mapped_type_t<InputIt>,\n        Hasher,\n        KeyEqual,\n        Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueMap(InputIt, InputIt, std::size_t, Alloc) -> F14ValueMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14ValueMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<const Key, Mapped>>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueMap(\n    std::initializer_list<std::pair<Key, Mapped>>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueMap(std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Alloc)\n    -> F14ValueMap<\n        Key,\n        Mapped,\n        f14::DefaultHasher<Key>,\n        f14::DefaultKeyEqual<Key>,\n        Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueMap(\n    std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Hasher, Alloc)\n    -> F14ValueMap<Key, Mapped, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14NodeMap\n    : public f14::detail::F14BasicMap<f14::detail::MapPolicyWithDefaults<\n          f14::detail::NodeContainerPolicy,\n          Key,\n          Mapped,\n          Hasher,\n          KeyEqual,\n          Alloc>> {\n protected:\n  using Policy = f14::detail::MapPolicyWithDefaults<\n      f14::detail::NodeContainerPolicy,\n      Key,\n      Mapped,\n      Hasher,\n      KeyEqual,\n      Alloc>;\n\n private:\n  using Super = f14::detail::F14BasicMap<Policy>;\n\n public:\n  using typename Super::value_type;\n\n  F14NodeMap() = default;\n\n  using Super::Super;\n\n  F14NodeMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  /// @methodset Modifiers\n  void swap(F14NodeMap& rhs) noexcept(Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n\n  /**\n   * Visit contiguous ranges of elements.\n   * @methodset Iterators\n   *\n   * Calls visitor with two value_type const*, b and e, such that every\n   * entry in the table is included in exactly one of the ranges [b,e).\n   * This can be used to efficiently iterate elements in bulk when crossing\n   * an API boundary that supports contiguous blocks of items.\n   */\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    this->table_.visitItems([&](typename Policy::Item ptr) {\n      value_type const* b = std::addressof(*ptr);\n      visitor(b, b + 1);\n    });\n  }\n\n  // TODO extract and node_handle insert\n};\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeMap(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14NodeMap<\n        iterator_key_type_t<InputIt>,\n        iterator_mapped_type_t<InputIt>,\n        Hasher,\n        KeyEqual,\n        Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeMap(InputIt, InputIt, std::size_t, Alloc) -> F14NodeMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14NodeMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<const Key, Mapped>>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeMap(\n    std::initializer_list<std::pair<Key, Mapped>>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14NodeMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeMap(std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Alloc)\n    -> F14NodeMap<\n        Key,\n        Mapped,\n        f14::DefaultHasher<Key>,\n        f14::DefaultKeyEqual<Key>,\n        Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeMap(\n    std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Hasher, Alloc)\n    -> F14NodeMap<Key, Mapped, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nnamespace f14 {\nnamespace detail {\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc,\n    typename EligibleForPerturbedInsertionOrder>\nclass F14VectorMapImpl\n    : public F14BasicMap<MapPolicyWithDefaults<\n          VectorContainerPolicy,\n          Key,\n          Mapped,\n          Hasher,\n          KeyEqual,\n          Alloc,\n          EligibleForPerturbedInsertionOrder>> {\n protected:\n  using Policy = MapPolicyWithDefaults<\n      VectorContainerPolicy,\n      Key,\n      Mapped,\n      Hasher,\n      KeyEqual,\n      Alloc,\n      EligibleForPerturbedInsertionOrder>;\n\n private:\n  using Super = F14BasicMap<Policy>;\n\n  template <typename K>\n  using IsIter = Disjunction<\n      std::is_same<typename Policy::Iter, remove_cvref_t<K>>,\n      std::is_same<typename Policy::ConstIter, remove_cvref_t<K>>,\n      std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>,\n      std::is_same<typename Policy::ConstReverseIter, remove_cvref_t<K>>>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousVectorErase = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          Key,\n          Hasher,\n          KeyEqual,\n          std::conditional_t<IsIter<K>::value, Key, K>>::value &&\n          !IsIter<K>::value,\n      T>;\n\n public:\n  using typename Super::const_iterator;\n  using typename Super::iterator;\n  using typename Super::key_type;\n  using typename Super::mapped_type;\n  using typename Super::value_type;\n\n  F14VectorMapImpl() = default;\n\n  // inherit constructors\n  using Super::Super;\n\n  F14VectorMapImpl& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  /// @methodset Iterators\n  iterator begin() { return this->table_.linearBegin(this->size()); }\n  /// @methodset Iterators\n  const_iterator begin() const { return cbegin(); }\n  /// @methodset Iterators\n  const_iterator cbegin() const {\n    return this->table_.linearBegin(this->size());\n  }\n\n  /// @methodset Iterators\n  iterator end() { return this->table_.linearEnd(); }\n  /// @methodset Iterators\n  const_iterator end() const { return cend(); }\n  /// @methodset Iterators\n  const_iterator cend() const { return this->table_.linearEnd(); }\n\n private:\n  template <typename BeforeDestroy>\n  void eraseUnderlying(\n      typename Policy::ItemIter underlying, BeforeDestroy&& beforeDestroy) {\n    Alloc& a = this->table_.alloc();\n    auto values = this->table_.values_;\n\n    // Remove the ptr from the base table and destroy the value.\n    auto index = underlying.item();\n    // The item still needs to be hashable during this call, so we must destroy\n    // the value _afterwards_.\n    this->tableEraseIterInto(underlying, beforeDestroy);\n    Policy::AllocTraits::destroy(a, std::addressof(values[index]));\n\n    // move the last element in values_ down and fix up the inbound index\n    auto tailIndex = this->size();\n    if (tailIndex != index) {\n      auto tail = this->table_.find(\n          VectorContainerIndexSearch{static_cast<uint32_t>(tailIndex)});\n      tail.item() = index;\n      auto p = std::addressof(values[index]);\n      assume(p != nullptr);\n      this->table_.transfer(a, std::addressof(values[tailIndex]), p, 1);\n    }\n  }\n\n  template <typename K, typename BeforeDestroy>\n  std::size_t eraseUnderlyingKey(K const& key, BeforeDestroy&& beforeDestroy) {\n    auto underlying = this->table_.find(key);\n    if (underlying.atEnd()) {\n      return 0;\n    } else {\n      eraseUnderlying(underlying, beforeDestroy);\n      return 1;\n    }\n  }\n\n public:\n  /**\n   * Remove element at a specific position (iterator).\n   * @overloadbrief Remove elements.\n   * @methodset Modifiers\n   */\n  FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) {\n    return eraseInto(pos, variadic_noop);\n  }\n\n  // This form avoids ambiguity when key_type has a templated constructor\n  // that accepts const_iterator\n  FOLLY_ALWAYS_INLINE iterator erase(iterator pos) {\n    return eraseInto(pos, variadic_noop);\n  }\n\n  /// Remove a range of elements.\n  iterator erase(const_iterator first, const_iterator last) {\n    return eraseInto(first, last, variadic_noop);\n  }\n\n  /// Remove a specific key.\n  std::size_t erase(key_type const& key) {\n    return eraseInto(key, variadic_noop);\n  }\n\n  /// Remove a key, using a heterogeneous representation.\n  template <typename K>\n  EnableHeterogeneousVectorErase<K, std::size_t> erase(K const& key) {\n    return eraseInto(key, variadic_noop);\n  }\n\n  /**\n   * Callback-erase a single iterator.\n   * @overloadbrief Erase with pre-destruction callback.\n   * @methodset Modifiers\n   *\n   * Like erase, but with an additional callback argument which is called with\n   * an rvalue reference to the item directly before it is destroyed. This can\n   * be used to extract an item out of a F14Set while avoiding a copy.\n   */\n  template <typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE iterator\n  eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {\n    auto index = this->table_.iterToIndex(pos);\n    auto underlying = this->table_.find(VectorContainerIndexSearch{index});\n    eraseUnderlying(underlying, beforeDestroy);\n    return index == 0 ? end() : this->table_.indexToIter(index - 1);\n  }\n\n  // This form avoids ambiguity when key_type has a templated constructor\n  // that accepts const_iterator\n  template <typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE iterator\n  eraseInto(iterator pos, BeforeDestroy&& beforeDestroy) {\n    const_iterator cpos{pos};\n    return eraseInto(cpos, beforeDestroy);\n  }\n\n  /// Callback-erase a range of values.\n  template <typename BeforeDestroy>\n  iterator eraseInto(\n      const_iterator first,\n      const_iterator last,\n      BeforeDestroy&& beforeDestroy) {\n    while (first != last) {\n      first = eraseInto(first, beforeDestroy);\n    }\n    auto index = this->table_.iterToIndex(first);\n    return index == 0 ? end() : this->table_.indexToIter(index - 1);\n  }\n\n  /// Callback-erase a specific key.\n  template <typename BeforeDestroy>\n  std::size_t eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseUnderlyingKey(key, beforeDestroy);\n  }\n\n  /// Callback-erase a specific key, using a heterogeneous representation.\n  template <typename K, typename BeforeDestroy>\n  EnableHeterogeneousVectorErase<K, std::size_t> eraseInto(\n      K const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseUnderlyingKey(key, beforeDestroy);\n  }\n\n  /**\n   * Visit contiguous ranges of elements.\n   * @methodset Iterators\n   *\n   * Calls visitor with two value_type const*, b and e, such that every\n   * entry in the table is included in exactly one of the ranges [b,e).\n   * This can be used to efficiently iterate elements in bulk when crossing\n   * an API boundary that supports contiguous blocks of items.\n   */\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    auto n = this->table_.size();\n    if (n > 0) {\n      value_type const* b = std::addressof(this->table_.values_[0]);\n      visitor(b, b + n);\n    }\n  }\n};\n} // namespace detail\n} // namespace f14\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14VectorMap\n    : public f14::detail::F14VectorMapImpl<\n          Key,\n          Mapped,\n          Hasher,\n          KeyEqual,\n          Alloc,\n          std::false_type> {\n  using Super = f14::detail::\n      F14VectorMapImpl<Key, Mapped, Hasher, KeyEqual, Alloc, std::false_type>;\n\n public:\n  using typename Super::const_iterator;\n  using typename Super::iterator;\n  using typename Super::value_type;\n  using reverse_iterator = typename Super::Policy::ReverseIter;\n  using const_reverse_iterator = typename Super::Policy::ConstReverseIter;\n\n  F14VectorMap() = default;\n\n  // inherit constructors\n  using Super::Super;\n\n  F14VectorMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  /// @methodset Modifiers\n  void swap(F14VectorMap& rhs) noexcept(Super::Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n\n  // ITERATION ORDER\n  //\n  // Deterministic iteration order for insert-only workloads is part of\n  // F14VectorMap's supported API: iterator is LIFO and reverse_iterator\n  // is FIFO.\n  //\n  // If there have been no calls to erase() then iterator and\n  // const_iterator enumerate entries in the opposite of insertion order.\n  // begin()->first is the key most recently inserted.  reverse_iterator\n  // and reverse_const_iterator, therefore, enumerate in LIFO (insertion)\n  // order for insert-only workloads.  Deterministic iteration order is\n  // only guaranteed if no keys were removed since the last time the\n  // map was empty.  Iteration order is preserved across rehashes and\n  // F14VectorMap copies and moves.\n  //\n  // iterator uses LIFO order so that erasing while iterating with begin()\n  // and end() is safe using the erase(it++) idiom, which is supported\n  // by std::map and std::unordered_map.  erase(iter) invalidates iter\n  // and all iterators before iter in the non-reverse iteration order.\n  // Every successful erase invalidates all reverse iterators.\n  //\n  // No erase is provided for reverse_iterator or const_reverse_iterator\n  // to make it harder to shoot yourself in the foot by erasing while\n  // reverse-iterating.  You can write that as map.erase(map.iter(riter))\n  // if you really need it.\n\n  /// @methodset Iterators\n  reverse_iterator rbegin() { return this->table_.values_; }\n  /// @methodset Iterators\n  const_reverse_iterator rbegin() const { return crbegin(); }\n  /// @methodset Iterators\n  const_reverse_iterator crbegin() const { return this->table_.values_; }\n\n  /// @methodset Iterators\n  reverse_iterator rend() { return this->table_.values_ + this->table_.size(); }\n  /// @methodset Iterators\n  const_reverse_iterator rend() const { return crend(); }\n  /// @methodset Iterators\n  const_reverse_iterator crend() const {\n    return this->table_.values_ + this->table_.size();\n  }\n\n  /// Explicit conversions between iterator and reverse_iterator\n  /// @methodset Iterators\n  iterator iter(reverse_iterator riter) { return this->table_.iter(riter); }\n  const_iterator iter(const_reverse_iterator riter) const {\n    return this->table_.iter(riter);\n  }\n\n  /// @copydoc iter\n  reverse_iterator riter(iterator it) { return this->table_.riter(it); }\n  const_reverse_iterator riter(const_iterator it) const {\n    return this->table_.riter(it);\n  }\n\n  friend Range<const_reverse_iterator> tag_invoke(\n      order_preserving_reinsertion_view_fn, F14VectorMap const& c) noexcept {\n    return {c.rbegin(), c.rend()};\n  }\n};\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorMap(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14VectorMap<\n        iterator_key_type_t<InputIt>,\n        iterator_mapped_type_t<InputIt>,\n        Hasher,\n        KeyEqual,\n        Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorMap(InputIt, InputIt, std::size_t, Alloc) -> F14VectorMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14VectorMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<const Key, Mapped>>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorMap(\n    std::initializer_list<std::pair<Key, Mapped>>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14VectorMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorMap(std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Alloc)\n    -> F14VectorMap<\n        Key,\n        Mapped,\n        f14::DefaultHasher<Key>,\n        f14::DefaultKeyEqual<Key>,\n        Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorMap(\n    std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Hasher, Alloc)\n    -> F14VectorMap<Key, Mapped, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n/**\n * F14FastMap is, under the hood, either an F14ValueMap or an F14VectorMap.\n * F14FastMap chooses which of these two representations to use based on the\n * size of a node.\n */\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14FastMap\n    : public std::conditional_t<\n          (sizeof(std::pair<Key const, Mapped>) < 24),\n          F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>,\n          f14::detail::F14VectorMapImpl<\n              Key,\n              Mapped,\n              Hasher,\n              KeyEqual,\n              Alloc,\n              std::true_type>> {\n  using Super = std::conditional_t<\n      sizeof(std::pair<Key const, Mapped>) < 24,\n      F14ValueMap<Key, Mapped, Hasher, KeyEqual, Alloc>,\n      f14::detail::F14VectorMapImpl<\n          Key,\n          Mapped,\n          Hasher,\n          KeyEqual,\n          Alloc,\n          std::true_type>>;\n\n public:\n  using typename Super::value_type;\n\n  F14FastMap() = default;\n\n  using Super::Super;\n\n  F14FastMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  /// @methodset Modifiers\n  void swap(F14FastMap& rhs) noexcept(Super::Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n};\n#endif // if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastMap(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14FastMap<\n        iterator_key_type_t<InputIt>,\n        iterator_mapped_type_t<InputIt>,\n        Hasher,\n        KeyEqual,\n        Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastMap(InputIt, InputIt, std::size_t, Alloc) -> F14FastMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    f14::DefaultHasher<iterator_key_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastMap(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14FastMap<\n    iterator_key_type_t<InputIt>,\n    iterator_mapped_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_key_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<std::pair<const Key, Mapped>>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastMap(\n    std::initializer_list<std::pair<Key, Mapped>>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14FastMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastMap(std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Alloc)\n    -> F14FastMap<\n        Key,\n        Mapped,\n        f14::DefaultHasher<Key>,\n        f14::DefaultKeyEqual<Key>,\n        Alloc>;\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastMap(\n    std::initializer_list<std::pair<Key, Mapped>>, std::size_t, Hasher, Alloc)\n    -> F14FastMap<Key, Mapped, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n} // namespace folly\n\nnamespace folly {\nnamespace f14 {\nnamespace detail {\ntemplate <typename M>\nbool mapsEqual(M const& lhs, M const& rhs) {\n  if (lhs.size() != rhs.size()) {\n    return false;\n  }\n  for (auto& kv : lhs) {\n    if (!rhs.containsEqualValue(kv)) {\n      return false;\n    }\n  }\n  return true;\n}\n} // namespace detail\n} // namespace f14\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator==(\n    F14ValueMap<K, M, H, E, A> const& lhs,\n    F14ValueMap<K, M, H, E, A> const& rhs) {\n  return mapsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator!=(\n    F14ValueMap<K, M, H, E, A> const& lhs,\n    F14ValueMap<K, M, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator==(\n    F14NodeMap<K, M, H, E, A> const& lhs,\n    F14NodeMap<K, M, H, E, A> const& rhs) {\n  return mapsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator!=(\n    F14NodeMap<K, M, H, E, A> const& lhs,\n    F14NodeMap<K, M, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator==(\n    F14VectorMap<K, M, H, E, A> const& lhs,\n    F14VectorMap<K, M, H, E, A> const& rhs) {\n  return mapsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator!=(\n    F14VectorMap<K, M, H, E, A> const& lhs,\n    F14VectorMap<K, M, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator==(\n    F14FastMap<K, M, H, E, A> const& lhs,\n    F14FastMap<K, M, H, E, A> const& rhs) {\n  return mapsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nbool operator!=(\n    F14FastMap<K, M, H, E, A> const& lhs,\n    F14FastMap<K, M, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nvoid swap(\n    F14ValueMap<K, M, H, E, A>& lhs,\n    F14ValueMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nvoid swap(\n    F14NodeMap<K, M, H, E, A>& lhs,\n    F14NodeMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nvoid swap(\n    F14VectorMap<K, M, H, E, A>& lhs,\n    F14VectorMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <typename K, typename M, typename H, typename E, typename A>\nvoid swap(\n    F14FastMap<K, M, H, E, A>& lhs,\n    F14FastMap<K, M, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <\n    typename K,\n    typename M,\n    typename H,\n    typename E,\n    typename A,\n    typename Pred>\nstd::size_t erase_if(F14ValueMap<K, M, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\ntemplate <\n    typename K,\n    typename M,\n    typename H,\n    typename E,\n    typename A,\n    typename Pred>\nstd::size_t erase_if(F14NodeMap<K, M, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\ntemplate <\n    typename K,\n    typename M,\n    typename H,\n    typename E,\n    typename A,\n    typename Pred>\nstd::size_t erase_if(F14VectorMap<K, M, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\ntemplate <\n    typename K,\n    typename M,\n    typename H,\n    typename E,\n    typename A,\n    typename Pred>\nstd::size_t erase_if(F14FastMap<K, M, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/F14Set-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/container/detail/F14Defaults.h>\n#include <folly/memory/MemoryResource.h>\n\nnamespace folly {\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>>\nclass F14NodeSet;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>>\nclass F14ValueSet;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>>\nclass F14VectorSet;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>>\nclass F14FastSet;\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nnamespace pmr {\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14NodeSet = folly::\n    F14NodeSet<Key, Hasher, KeyEqual, std::pmr::polymorphic_allocator<Key>>;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14ValueSet = folly::\n    F14ValueSet<Key, Hasher, KeyEqual, std::pmr::polymorphic_allocator<Key>>;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14VectorSet = folly::\n    F14VectorSet<Key, Hasher, KeyEqual, std::pmr::polymorphic_allocator<Key>>;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>>\nusing F14FastSet = folly::\n    F14FastSet<Key, Hasher, KeyEqual, std::pmr::polymorphic_allocator<Key>>;\n} // namespace pmr\n#endif // FOLLY_HAS_MEMORY_RESOURCE\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/F14Set.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_f14nodeset\n//\n\n#pragma once\n\n/**\n * F14NodeSet, F14ValueSet, and F14VectorSet\n *\n * F14FastSet conditionally works like F14ValueSet or F14VectorSet\n *\n * See F14.md\n */\n\n#include <cstddef>\n#include <initializer_list>\n#include <tuple>\n\n#include <folly/Portability.h>\n#include <folly/container/View.h>\n#include <folly/lang/SafeAssert.h>\n\n#include <folly/container/F14Set-fwd.h>\n#include <folly/container/Iterator.h>\n#include <folly/container/detail/F14Policy.h>\n#include <folly/container/detail/F14SetFallback.h>\n#include <folly/container/detail/F14Table.h>\n#include <folly/container/detail/Util.h>\n\nnamespace folly {\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\n//////// Common case for supported platforms\n\nnamespace f14 {\nnamespace detail {\n\ntemplate <typename Policy>\nclass F14BasicSet {\n  template <typename K, typename T>\n  using EnableHeterogeneousFind = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          typename Policy::Value,\n          typename Policy::Hasher,\n          typename Policy::KeyEqual,\n          K>::value,\n      T>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousInsert = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousInsert<\n          typename Policy::Value,\n          typename Policy::Hasher,\n          typename Policy::KeyEqual,\n          K>::value,\n      T>;\n\n  template <typename K>\n  using IsIter = std::is_same<typename Policy::Iter, remove_cvref_t<K>>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousErase = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          typename Policy::Value,\n          typename Policy::Hasher,\n          typename Policy::KeyEqual,\n          std::conditional_t<IsIter<K>::value, typename Policy::Value, K>>::\n              value &&\n          !IsIter<K>::value,\n      T>;\n\n public:\n  //// PUBLIC - Member types\n\n  using key_type = typename Policy::Value;\n  using value_type = key_type;\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n  using hash_token_type = F14HashToken;\n  using hasher = typename Policy::Hasher;\n  using key_equal = typename Policy::KeyEqual;\n  using hashed_key_type = F14HashedKey<key_type, hasher, key_equal>;\n  using allocator_type = typename Policy::Alloc;\n  using reference = value_type&;\n  using const_reference = value_type const&;\n  using pointer = typename Policy::AllocTraits::pointer;\n  using const_pointer = typename Policy::AllocTraits::const_pointer;\n  using iterator = typename Policy::Iter;\n  using const_iterator = iterator;\n\n private:\n  using ItemIter = typename Policy::ItemIter;\n\n public:\n  //// PUBLIC - Member functions\n\n  F14BasicSet() noexcept(Policy::kDefaultConstructIsNoexcept) : table_{} {}\n\n  explicit F14BasicSet(\n      std::size_t initialCapacity,\n      hasher const& hash = hasher{},\n      key_equal const& eq = key_equal{},\n      allocator_type const& alloc = allocator_type{})\n      : table_{initialCapacity, hash, eq, alloc} {}\n\n  explicit F14BasicSet(std::size_t initialCapacity, allocator_type const& alloc)\n      : F14BasicSet(initialCapacity, hasher{}, key_equal{}, alloc) {}\n\n  explicit F14BasicSet(\n      std::size_t initialCapacity,\n      hasher const& hash,\n      allocator_type const& alloc)\n      : F14BasicSet(initialCapacity, hash, key_equal{}, alloc) {}\n\n  explicit F14BasicSet(allocator_type const& alloc)\n      : F14BasicSet(0, hasher{}, key_equal{}, alloc) {}\n\n  template <typename InputIt>\n  F14BasicSet(\n      InputIt first,\n      InputIt last,\n      std::size_t initialCapacity = 0,\n      hasher const& hash = hasher{},\n      key_equal const& eq = key_equal{},\n      allocator_type const& alloc = allocator_type{})\n      : table_{initialCapacity, hash, eq, alloc} {\n    initialInsert(first, last, initialCapacity);\n  }\n\n  template <typename InputIt>\n  F14BasicSet(\n      InputIt first,\n      InputIt last,\n      std::size_t initialCapacity,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hasher{}, key_equal{}, alloc} {\n    initialInsert(first, last, initialCapacity);\n  }\n\n  template <typename InputIt>\n  F14BasicSet(\n      InputIt first,\n      InputIt last,\n      std::size_t initialCapacity,\n      hasher const& hash,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hash, key_equal{}, alloc} {\n    initialInsert(first, last, initialCapacity);\n  }\n\n  F14BasicSet(F14BasicSet const& rhs) = default;\n\n  F14BasicSet(F14BasicSet const& rhs, allocator_type const& alloc)\n      : table_(rhs.table_, alloc) {}\n\n  F14BasicSet(F14BasicSet&& rhs) = default;\n\n  F14BasicSet(F14BasicSet&& rhs, allocator_type const& alloc) noexcept(\n      Policy::kAllocIsAlwaysEqual)\n      : table_{std::move(rhs.table_), alloc} {}\n\n  /* implicit */ F14BasicSet(\n      std::initializer_list<value_type> init,\n      std::size_t initialCapacity = 0,\n      hasher const& hash = hasher{},\n      key_equal const& eq = key_equal{},\n      allocator_type const& alloc = allocator_type{})\n      : table_{initialCapacity, hash, eq, alloc} {\n    initialInsert(init.begin(), init.end(), initialCapacity);\n  }\n\n  F14BasicSet(\n      std::initializer_list<value_type> init,\n      std::size_t initialCapacity,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hasher{}, key_equal{}, alloc} {\n    initialInsert(init.begin(), init.end(), initialCapacity);\n  }\n\n  F14BasicSet(\n      std::initializer_list<value_type> init,\n      std::size_t initialCapacity,\n      hasher const& hash,\n      allocator_type const& alloc)\n      : table_{initialCapacity, hash, key_equal{}, alloc} {\n    initialInsert(init.begin(), init.end(), initialCapacity);\n  }\n\n  F14BasicSet& operator=(F14BasicSet const&) = default;\n\n  F14BasicSet& operator=(F14BasicSet&&) = default;\n\n  F14BasicSet& operator=(std::initializer_list<value_type> ilist) {\n    clear();\n    bulkInsert(ilist.begin(), ilist.end(), true);\n    return *this;\n  }\n\n  /// Get the allocator for this container.\n  allocator_type get_allocator() const noexcept { return table_.alloc(); }\n\n  //// PUBLIC - Iterators\n\n  /// @methodset Iterators\n  iterator begin() noexcept { return cbegin(); }\n  const_iterator begin() const noexcept { return cbegin(); }\n  /// @methodset Iterators\n  const_iterator cbegin() const noexcept {\n    return table_.makeIter(table_.begin());\n  }\n\n  /// @methodset Iterators\n  iterator end() noexcept { return cend(); }\n  const_iterator end() const noexcept { return cend(); }\n  /// @methodset Iterators\n  const_iterator cend() const noexcept { return table_.makeIter(table_.end()); }\n\n  //// PUBLIC - Capacity\n\n  /**\n   * Check if this container has any elements.\n   * @methodset Capacity\n   */\n  bool empty() const noexcept { return table_.empty(); }\n\n  /**\n   * Number of elements in this container.\n   * @methodset Capacity\n   */\n  std::size_t size() const noexcept { return table_.size(); }\n\n  /**\n   * The maximum size of this container.\n   * @methodset Capacity\n   */\n  std::size_t max_size() const noexcept { return table_.max_size(); }\n\n  //// PUBLIC - Modifiers\n\n  /**\n   * Remove all elements.\n   *\n   * Frees heap-allocated memory; bucket_count is returned to 0.\n   *\n   * @methodset Modifiers\n   */\n  void clear() noexcept { table_.clear(); }\n\n  /**\n   * Add a single element.\n   *\n   * @overloadbrief Add elements.\n   * @methodset Modifiers\n   */\n  std::pair<iterator, bool> insert(value_type const& value) {\n    return emplace(value);\n  }\n\n  /// Add a single element.\n  std::pair<iterator, bool> insert(value_type&& value) {\n    return emplace(std::move(value));\n  }\n\n  /**\n   * Add a single element, with a locational hint.\n   *\n   * std::unordered_set's hinted insertion API is misleading.  No implementation\n   * I've seen actually uses the hint.  Code restructuring by the caller to use\n   * the hinted API is at best unnecessary, and at worst a pessimization.  It is\n   * used, however, so we provide it.\n   */\n  iterator insert(const_iterator /*hint*/, value_type const& value) {\n    return insert(value).first;\n  }\n\n  /// Add a single element, with a locational hint.\n  iterator insert(const_iterator /*hint*/, value_type&& value) {\n    return insert(std::move(value)).first;\n  }\n\n  /// Add a single element, of a heterognous type.\n  template <typename K>\n  EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert(K&& value) {\n    return emplace(std::forward<K>(value));\n  }\n\n private:\n  template <class InputIt>\n  FOLLY_ALWAYS_INLINE void bulkInsert(\n      InputIt first, InputIt last, bool autoReserve) {\n    if (autoReserve) {\n      auto n = std::distance(first, last);\n      if (n == 0) {\n        return;\n      }\n      table_.reserveForInsert(n);\n    }\n    while (first != last) {\n      insert(*first);\n      ++first;\n    }\n  }\n\n  template <class InputIt>\n  void initialInsert(InputIt first, InputIt last, std::size_t initialCapacity) {\n    FOLLY_SAFE_DCHECK(empty() && bucket_count() >= initialCapacity, \"\");\n\n    // It's possible that there are a lot of duplicates in first..last and\n    // so we will oversize ourself.  The common case, however, is that\n    // we can avoid a lot of rehashing if we pre-expand.  The behavior\n    // is easy to disable at a particular call site by asking for an\n    // initialCapacity of 1.\n    bool autoReserve =\n        std::is_base_of<\n            std::random_access_iterator_tag,\n            typename std::iterator_traits<InputIt>::iterator_category>::value &&\n        initialCapacity == 0;\n    bulkInsert(first, last, autoReserve);\n  }\n\n public:\n  /// Add a range of elements.\n  template <class InputIt>\n  void insert(InputIt first, InputIt last) {\n    // Bulk reserve is a heuristic choice, so it can backfire.  We restrict\n    // ourself to situations that mimic bulk construction without an\n    // explicit initialCapacity.\n    bool autoReserve =\n        std::is_base_of<\n            std::random_access_iterator_tag,\n            typename std::iterator_traits<InputIt>::iterator_category>::value &&\n        bucket_count() == 0;\n    bulkInsert(first, last, autoReserve);\n  }\n\n  /// Add elements from an initializer list.\n  void insert(std::initializer_list<value_type> ilist) {\n    insert(ilist.begin(), ilist.end());\n  }\n\n private:\n  template <typename Arg>\n  using UsableAsKey = ::folly::detail::\n      EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;\n\n public:\n  /**\n   * Add an element by constructing it in-place.\n   *\n   * @overloadbrief Add elements in-place.\n   * @methodset Modifiers\n   */\n  template <class... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    auto rv = folly::detail::callWithConstructedKey<key_type, UsableAsKey>(\n        table_.alloc(),\n        [&](auto&&... inner) {\n          return table_.tryEmplaceValue(\n              std::forward<decltype(inner)>(inner)...);\n        },\n        std::forward<Args>(args)...);\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  // Emplace with prehash token\n  template <class... Args>\n  std::pair<iterator, bool> emplace_token(\n      F14HashToken const& token, Args&&... args) {\n    auto rv = folly::detail::callWithConstructedKey<key_type, UsableAsKey>(\n        table_.alloc(),\n        [&](auto&&... inner) {\n          return table_.tryEmplaceValueWithToken(\n              token, std::forward<decltype(inner)>(inner)...);\n        },\n        std::forward<Args>(args)...);\n    return std::make_pair(table_.makeIter(rv.first), rv.second);\n  }\n\n  /// Emplace with hint.\n  template <class... Args>\n  iterator emplace_hint(const_iterator /*hint*/, Args&&... args) {\n    return emplace(std::forward<Args>(args)...).first;\n  }\n\n  /**\n   * Remove element at a specific position (iterator).\n   * @overloadbrief Remove elements.\n   * @methodset Modifiers\n   */\n  FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) {\n    return eraseInto(pos, variadic_noop);\n  }\n\n  /// Remove a range of elements.\n  iterator erase(const_iterator first, const_iterator last) {\n    return eraseInto(first, last, variadic_noop);\n  }\n\n  /// Remove a specific key.\n  size_type erase(key_type const& key) { return eraseInto(key, variadic_noop); }\n\n  /// Remove a key, using a heterogeneous representation.\n  template <typename K>\n  EnableHeterogeneousErase<K, size_type> erase(K const& key) {\n    return eraseInto(key, variadic_noop);\n  }\n\n  /**\n   * Callback-erase a single iterator.\n   *\n   * Like erase, but with an additional callback argument which is called with\n   * an rvalue reference to the item directly before it is destroyed. This can\n   * be used to extract an item out of a F14Set while avoiding a copy.\n   *\n   * @overloadbrief Erase with pre-destruction callback.\n   * @methodset Modifiers\n   */\n  template <typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE iterator\n  eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {\n    auto itemPos = table_.unwrapIter(pos);\n    table_.eraseIterInto(itemPos, beforeDestroy);\n\n    // If we are inlined then gcc and clang can optimize away all of the\n    // work of ++pos if the caller discards it.\n    itemPos.advanceLikelyDead();\n    return table_.makeIter(itemPos);\n  }\n\n  /// Callback-erase a range of values.\n  template <typename BeforeDestroy>\n  iterator eraseInto(\n      const_iterator first,\n      const_iterator last,\n      BeforeDestroy&& beforeDestroy) {\n    while (first != last) {\n      first = eraseInto(first, beforeDestroy);\n    }\n    return first;\n  }\n\n  /// Callback-erase a specific key.\n  template <typename BeforeDestroy>\n  size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {\n    return table_.eraseKeyInto(key, beforeDestroy);\n  }\n\n  /// Callback-erase a specific key, using a heterogeneous representation.\n  template <typename K, typename BeforeDestroy>\n  EnableHeterogeneousErase<K, size_type> eraseInto(\n      K const& key, BeforeDestroy&& beforeDestroy) {\n    return table_.eraseKeyInto(key, beforeDestroy);\n  }\n\n  //// PUBLIC - Lookup\n\n  /**\n   * Number of elements matching the given key.\n   * @methodset Lookup\n   */\n  FOLLY_ALWAYS_INLINE size_type count(key_type const& key) const {\n    return contains(key) ? 1 : 0;\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, size_type> count(\n      K const& key) const {\n    return contains(key) ? 1 : 0;\n  }\n\n  /**\n   * @overloadbrief Prehash a key.\n   * @methodset Lookup\n   *\n   * prehash(key) does the work of evaluating hash_function()(key)\n   * (including additional bit-mixing for non-avalanching hash functions),\n   * and wraps the result of that work in a token for later reuse.\n   *\n   * The returned token may be used at any time, may be used more than\n   * once, and may be used in other F14 sets and maps.  Tokens are\n   * transferrable between any F14 containers (maps and sets) with the\n   * same key_type and equal hash_function()s.\n   *\n   * Hash tokens are not hints -- it is a bug to call any method on this\n   * class with a token t and key k where t isn't the result of a call\n   * to prehash(k2) with k2 == k.\n   */\n  F14HashToken prehash(key_type const& key) const {\n    return table_.prehash(key);\n  }\n  /// @copydoc prehash\n  F14HashToken prehash(key_type const& key, std::size_t hash) const {\n    return table_.prehash(key, hash);\n  }\n\n  /// @copydoc prehash\n  template <typename K>\n  EnableHeterogeneousFind<K, F14HashToken> prehash(K const& key) const {\n    return table_.prehash(key);\n  }\n  /// @copydoc prehash\n  template <typename K>\n  EnableHeterogeneousFind<K, F14HashToken> prehash(\n      K const& key, std::size_t hash) const {\n    return table_.prehash(key, hash);\n  }\n\n  /**\n   * @overloadbrief Prefetch cachelines associated with a key.\n   * @methodset Lookup\n   *\n   * prefetch(token) begins prefetching the first steps of looking for key into\n   * the local CPU cache.\n   *\n   * Example Scenario: Loading 2 values from a cold map.\n   * You have a map that is cold, meaning it is out of the local CPU cache,\n   * and you want to load two values from the map. This can be extended to\n   * load N values, but we're loading 2 for simplicity.\n   *\n   * When the map is cold the dominating factor in the latency is loading the\n   * cache line of the entry into the local CPU cache. Using prehash() will\n   * issue these cache line fetches in parallel.  That means that by the time we\n   * finish map.find(token1, key1) the cache lines needed by map.find(token2,\n   * key2) may already be in the local CPU cache. In the best case this will\n   * half the latency.\n   *\n   * It is always okay to call prefetch() before a find() or other lookup\n   * operation, as it only prefetches cache lines that are guaranteed to be\n   * needed by the lookup.\n   *\n   *   std::pair<iterator, iterator> find2(\n   *       auto& set, key_type const& key1, key_type const& key2) {\n   *     auto const token1 = set.prehash(key1);\n   *     set.prefetch(token1);\n   *     auto const token2 = set.prehash(key2);\n   *     set.prefetch(token2);\n   *     return std::make_pair(set.find(token1, key1), set.find(token2, key2));\n   *   }\n   */\n  void prefetch(F14HashToken const& token) const { table_.prefetch(token); }\n\n  /**\n   * @overloadbrief Get the iterator for a key.\n   * @methodset Lookup\n   */\n  FOLLY_ALWAYS_INLINE iterator find(key_type const& key) {\n    return const_cast<F14BasicSet const*>(this)->find(key);\n  }\n\n  FOLLY_ALWAYS_INLINE const_iterator find(key_type const& key) const {\n    return table_.makeIter(table_.find(key));\n  }\n\n  FOLLY_ALWAYS_INLINE iterator\n  find(F14HashToken const& token, key_type const& key) {\n    return const_cast<F14BasicSet const*>(this)->find(token, key);\n  }\n\n  FOLLY_ALWAYS_INLINE const_iterator\n  find(F14HashToken const& token, key_type const& key) const {\n    return table_.makeIter(table_.find(token, key));\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(K const& key) {\n    return const_cast<F14BasicSet const*>(this)->find(key);\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find(\n      K const& key) const {\n    return table_.makeIter(table_.find(key));\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(\n      F14HashToken const& token, K const& key) {\n    return const_cast<F14BasicSet const*>(this)->find(token, key);\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find(\n      F14HashToken const& token, K const& key) const {\n    return table_.makeIter(table_.find(token, key));\n  }\n\n  /**\n   * @overloadbrief Checks if the container contains an element with the\n   * specific key.\n   * @methodset Lookup\n   */\n  FOLLY_ALWAYS_INLINE bool contains(key_type const& key) const {\n    return !table_.find(key).atEnd();\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains(\n      K const& key) const {\n    return !table_.find(key).atEnd();\n  }\n\n  FOLLY_ALWAYS_INLINE bool contains(\n      F14HashToken const& token, key_type const& key) const {\n    return !table_.find(token, key).atEnd();\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, bool> contains(\n      F14HashToken const& token, K const& key) const {\n    return !table_.find(token, key).atEnd();\n  }\n\n  /**\n   * @overloadbrief Returns the range of elements matching a specific key.\n   * @methodset Lookup\n   */\n  std::pair<iterator, iterator> equal_range(key_type const& key) {\n    return equal_range(*this, key);\n  }\n\n  std::pair<const_iterator, const_iterator> equal_range(\n      key_type const& key) const {\n    return equal_range(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, std::pair<iterator, iterator>> equal_range(\n      K const& key) {\n    return equal_range(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, std::pair<const_iterator, const_iterator>>\n  equal_range(K const& key) const {\n    return equal_range(*this, key);\n  }\n\n  //// PUBLIC - Bucket interface\n\n  /**\n   * The number of buckets in this container.\n   * @methodset Bucket interface\n   */\n  std::size_t bucket_count() const noexcept { return table_.bucket_count(); }\n\n  /**\n   * The maximum number of buckets for this container.\n   * @methodset Bucket interface\n   */\n  std::size_t max_bucket_count() const noexcept {\n    return table_.max_bucket_count();\n  }\n\n  //// PUBLIC - Hash policy\n\n  /**\n   * Load factor of the underlying hashtable.\n   * @methodset Hash policy\n   */\n  float load_factor() const noexcept { return table_.load_factor(); }\n\n  /**\n   * @overloadbrief Load factor control.\n   * Get the maximum load factor for this container.\n   * @methodset Hash policy\n   */\n  float max_load_factor() const noexcept { return table_.max_load_factor(); }\n\n  /**\n   * Set the maximum load factor for this container.\n   * @methodset Hash policy\n   */\n  void max_load_factor(float v) { table_.max_load_factor(v); }\n\n  /**\n   * Rehash this container.\n   *\n   * This function is provided for compliance with C++'s requirements for\n   * hashtables, but is no better than a simple `reserve` call for F14.\n   *\n   * @param bucketCapacity  The desired capacity across all buckets.\n   *\n   * @methodset Hash policy\n   */\n  void rehash(std::size_t bucketCapacity) {\n    // The standard's rehash() requires understanding the max load factor,\n    // which is easy to get wrong.  Since we don't actually allow adjustment\n    // of max_load_factor there is no difference.\n    reserve(bucketCapacity);\n  }\n\n  /**\n   * Pre-allocate space for at least this many elements.\n   *\n   * @param capacity  The number of elements to pre-allocate space for.\n   *\n   * @methodset Capacity\n   */\n  void reserve(std::size_t capacity) { table_.reserve(capacity); }\n\n  //// PUBLIC - Observers\n\n  /**\n   * Get the hasher.\n   * @methodset Observers\n   */\n  hasher hash_function() const { return table_.hasher(); }\n\n  /**\n   * Get the key_equal.\n   * @methodset Observers\n   */\n  key_equal key_eq() const { return table_.keyEqual(); }\n\n  //// PUBLIC - F14 Extensions\n\n  /**\n   * Checks for a value using operator==\n   *\n   * returns true iff there is an element in the set\n   * that compares equal to key using operator==.  It is undefined\n   * behavior to call this function if operator== on key_type can ever\n   * return true when the same keys passed to key_eq() would return false\n   * (the opposite is allowed).  When using the default key_eq this function\n   * is equivalent to contains().\n   *\n   * @methodset Lookup\n   */\n  bool containsEqualValue(value_type const& value) const {\n    return !table_.findMatching(value, [&](auto& k) { return value == k; })\n                .atEnd();\n  }\n\n  /**\n   * Get memory footprint, not including sizeof(*this).\n   * @methodset Capacity\n   */\n  std::size_t getAllocatedMemorySize() const {\n    return table_.getAllocatedMemorySize();\n  }\n\n  /**\n   * In-depth memory analysis.\n   *\n   * Enumerates classes of allocated memory blocks currently owned\n   * by this table, calling visitor(allocationSize, allocationCount).\n   * This can be used to get a more accurate indication of memory footprint\n   * than getAllocatedMemorySize() if you have some way of computing the\n   * internal fragmentation of the allocator, such as JEMalloc's nallocx.\n   * The visitor might be called twice with the same allocationSize. The\n   * visitor's computation should produce the same result for visitor(8,\n   * 2) as for two calls to visitor(8, 1), for example.  The visitor may\n   * be called with a zero allocationCount.\n   *\n   * @methodset Capacity\n   */\n  template <typename V>\n  void visitAllocationClasses(V&& visitor) const {\n    return table_.visitAllocationClasses(visitor);\n  }\n\n  /**\n   * Visit contiguous ranges of elements.\n   *\n   * Calls visitor with two value_type const*, b and e, such that every\n   * entry in the table is included in exactly one of the ranges [b,e).\n   * This can be used to efficiently iterate elements in bulk when crossing\n   * an API boundary that supports contiguous blocks of items.\n   *\n   * @methodset Iterators\n   */\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const;\n\n  /**\n   * Get stats.\n   * @methodset Hash policy\n   */\n  F14TableStats computeStats() const noexcept { return table_.computeStats(); }\n\n private:\n  template <typename Self, typename K>\n  static auto equal_range(Self& self, K const& key) {\n    auto first = self.find(key);\n    auto last = first;\n    if (last != self.end()) {\n      ++last;\n    }\n    return std::make_pair(first, last);\n  }\n\n protected:\n  F14Table<Policy> table_;\n};\n} // namespace detail\n} // namespace f14\n\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14ValueSet\n    : public f14::detail::F14BasicSet<f14::detail::SetPolicyWithDefaults<\n          f14::detail::ValueContainerPolicy,\n          Key,\n          Hasher,\n          KeyEqual,\n          Alloc>> {\n protected:\n  friend struct F14ValueSetTester;\n  using Policy = f14::detail::SetPolicyWithDefaults<\n      f14::detail::ValueContainerPolicy,\n      Key,\n      Hasher,\n      KeyEqual,\n      Alloc>;\n\n private:\n  using Super = f14::detail::F14BasicSet<Policy>;\n\n public:\n  using typename Super::value_type;\n\n  F14ValueSet() = default;\n\n  using Super::Super;\n\n  F14ValueSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  void swap(F14ValueSet& rhs) noexcept(Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    this->table_.visitContiguousItemRanges(std::forward<V>(visitor));\n  }\n};\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueSet(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14ValueSet<iterator_value_type_t<InputIt>, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueSet(InputIt, InputIt, std::size_t, Alloc) -> F14ValueSet<\n    iterator_value_type_t<InputIt>,\n    f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14ValueSet<\n    iterator_value_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueSet(\n    std::initializer_list<Key>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14ValueSet<Key, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14ValueSet<\n    Key,\n    f14::DefaultHasher<Key>,\n    f14::DefaultKeyEqual<Key>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14ValueSet(std::initializer_list<Key>, std::size_t, Hasher, Alloc)\n    -> F14ValueSet<Key, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14NodeSet\n    : public f14::detail::F14BasicSet<f14::detail::SetPolicyWithDefaults<\n          f14::detail::NodeContainerPolicy,\n          Key,\n          Hasher,\n          KeyEqual,\n          Alloc>> {\n protected:\n  using Policy = f14::detail::SetPolicyWithDefaults<\n      f14::detail::NodeContainerPolicy,\n      Key,\n      Hasher,\n      KeyEqual,\n      Alloc>;\n\n private:\n  using Super = f14::detail::F14BasicSet<Policy>;\n\n public:\n  using typename Super::value_type;\n\n  F14NodeSet() = default;\n\n  using Super::Super;\n\n  F14NodeSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  void swap(F14NodeSet& rhs) noexcept(Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    this->table_.visitItems([&](typename Policy::Item ptr) {\n      value_type const* b = std::addressof(*ptr);\n      visitor(b, b + 1);\n    });\n  }\n};\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeSet(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14NodeSet<iterator_value_type_t<InputIt>, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeSet(InputIt, InputIt, std::size_t, Alloc) -> F14NodeSet<\n    iterator_value_type_t<InputIt>,\n    f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14NodeSet<\n    iterator_value_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeSet(\n    std::initializer_list<Key>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14NodeSet<Key, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14NodeSet<\n    Key,\n    f14::DefaultHasher<Key>,\n    f14::DefaultKeyEqual<Key>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14NodeSet(std::initializer_list<Key>, std::size_t, Hasher, Alloc)\n    -> F14NodeSet<Key, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nnamespace f14 {\nnamespace detail {\ntemplate <\n    typename Key,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc,\n    typename EligibleForPerturbedInsertionOrder>\nclass F14VectorSetImpl\n    : public F14BasicSet<SetPolicyWithDefaults<\n          VectorContainerPolicy,\n          Key,\n          Hasher,\n          KeyEqual,\n          Alloc,\n          EligibleForPerturbedInsertionOrder>> {\n protected:\n  using Policy = SetPolicyWithDefaults<\n      VectorContainerPolicy,\n      Key,\n      Hasher,\n      KeyEqual,\n      Alloc,\n      EligibleForPerturbedInsertionOrder>;\n\n private:\n  using Super = F14BasicSet<Policy>;\n\n  template <typename K>\n  using IsIter = Disjunction<\n      std::is_same<typename Policy::Iter, remove_cvref_t<K>>,\n      std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousVectorErase = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          typename Policy::Value,\n          typename Policy::Hasher,\n          typename Policy::KeyEqual,\n          std::conditional_t<IsIter<K>::value, typename Policy::Value, K>>::\n              value &&\n          !IsIter<K>::value,\n      T>;\n\n public:\n  using typename Super::const_iterator;\n  using typename Super::iterator;\n  using typename Super::key_type;\n  using typename Super::value_type;\n\n  F14VectorSetImpl() = default;\n\n  using Super::Super;\n\n  F14VectorSetImpl& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  iterator begin() { return cbegin(); }\n  const_iterator begin() const { return cbegin(); }\n  const_iterator cbegin() const {\n    return this->table_.linearBegin(this->size());\n  }\n\n  iterator end() { return cend(); }\n  const_iterator end() const { return cend(); }\n  const_iterator cend() const { return this->table_.linearEnd(); }\n\n private:\n  template <typename BeforeDestroy>\n  void eraseUnderlying(\n      typename Policy::ItemIter underlying, BeforeDestroy&& beforeDestroy) {\n    Alloc& a = this->table_.alloc();\n    auto values = this->table_.values_;\n\n    // destroy the value and remove the ptr from the base table\n    auto index = underlying.item();\n    this->table_.eraseIterInto(underlying, beforeDestroy);\n    Policy::AllocTraits::destroy(a, std::addressof(values[index]));\n\n    // move the last element in values_ down and fix up the inbound index\n    auto tailIndex = this->size();\n    if (tailIndex != index) {\n      auto tail = this->table_.find(\n          VectorContainerIndexSearch{static_cast<uint32_t>(tailIndex)});\n      tail.item() = index;\n      auto p = std::addressof(values[index]);\n      assume(p != nullptr);\n      this->table_.transfer(a, std::addressof(values[tailIndex]), p, 1);\n    }\n  }\n\n  template <typename K, typename BeforeDestroy>\n  std::size_t eraseUnderlyingKey(K const& key, BeforeDestroy&& beforeDestroy) {\n    auto underlying = this->table_.find(key);\n    if (underlying.atEnd()) {\n      return 0;\n    } else {\n      eraseUnderlying(underlying, beforeDestroy);\n      return 1;\n    }\n  }\n\n public:\n  FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) {\n    return eraseInto(pos, variadic_noop);\n  }\n\n  iterator erase(const_iterator first, const_iterator last) {\n    return eraseInto(first, last, variadic_noop);\n  }\n\n  std::size_t erase(key_type const& key) {\n    return eraseInto(key, variadic_noop);\n  }\n\n  template <typename K>\n  EnableHeterogeneousVectorErase<K, std::size_t> erase(K const& key) {\n    return eraseInto(key, variadic_noop);\n  }\n\n  template <typename BeforeDestroy>\n  FOLLY_ALWAYS_INLINE iterator\n  eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {\n    auto underlying = this->table_.find(\n        VectorContainerIndexSearch{this->table_.iterToIndex(pos)});\n    eraseUnderlying(underlying, beforeDestroy);\n    return ++pos;\n  }\n\n  template <typename BeforeDestroy>\n  iterator eraseInto(\n      const_iterator first,\n      const_iterator last,\n      BeforeDestroy&& beforeDestroy) {\n    while (first != last) {\n      first = eraseInto(first, beforeDestroy);\n    }\n    return first;\n  }\n\n  template <typename BeforeDestroy>\n  std::size_t eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseUnderlyingKey(key, beforeDestroy);\n  }\n\n  template <typename K, typename BeforeDestroy>\n  EnableHeterogeneousVectorErase<K, std::size_t> eraseInto(\n      K const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseUnderlyingKey(key, beforeDestroy);\n  }\n\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    auto n = this->table_.size();\n    if (n > 0) {\n      value_type const* b = std::addressof(this->table_.values_[0]);\n      visitor(b, b + n);\n    }\n  }\n};\n} // namespace detail\n} // namespace f14\n\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14VectorSet\n    : public f14::detail::\n          F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::false_type> {\n  using Super = f14::detail::\n      F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::false_type>;\n\n public:\n  using typename Super::const_iterator;\n  using typename Super::iterator;\n  using typename Super::value_type;\n  using reverse_iterator = typename Super::Policy::ReverseIter;\n  using const_reverse_iterator = reverse_iterator;\n\n  F14VectorSet() = default;\n\n  using Super::Super;\n\n  F14VectorSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  void swap(F14VectorSet& rhs) noexcept(Super::Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n\n  // ITERATION ORDER\n  //\n  // Deterministic iteration order for insert-only workloads is part of\n  // F14VectorSet's supported API: iterator is LIFO and reverse_iterator\n  // is FIFO.\n  //\n  // If there have been no calls to erase() then iterator and\n  // const_iterator enumerate entries in the opposite of insertion order.\n  // begin()->first is the key most recently inserted.  reverse_iterator\n  // and reverse_const_iterator, therefore, enumerate in LIFO (insertion)\n  // order for insert-only workloads.  Deterministic iteration order is\n  // only guaranteed if no keys were removed since the last time the\n  // set was empty.  Iteration order is preserved across rehashes and\n  // F14VectorSet copies and moves.\n  //\n  // iterator uses LIFO order so that erasing while iterating with begin()\n  // and end() is safe using the erase(it++) idiom, which is supported\n  // by std::set and std::unordered_set.  erase(iter) invalidates iter\n  // and all iterators before iter in the non-reverse iteration order.\n  // Every successful erase invalidates all reverse iterators.\n  //\n  // No erase is provided for reverse_iterator (AKA const_reverse_iterator)\n  // to make it harder to shoot yourself in the foot by erasing while\n  // reverse-iterating.  You can write that as set.erase(set.iter(riter))\n  // if you need it.\n\n  reverse_iterator rbegin() { return this->table_.values_; }\n  const_reverse_iterator rbegin() const { return crbegin(); }\n  const_reverse_iterator crbegin() const { return this->table_.values_; }\n\n  reverse_iterator rend() { return this->table_.values_ + this->table_.size(); }\n  const_reverse_iterator rend() const { return crend(); }\n  const_reverse_iterator crend() const {\n    return this->table_.values_ + this->table_.size();\n  }\n\n  // explicit conversions between iterator and reverse_iterator\n  iterator iter(reverse_iterator riter) { return this->table_.iter(riter); }\n  const_iterator iter(const_reverse_iterator riter) const {\n    return this->table_.iter(riter);\n  }\n\n  reverse_iterator riter(iterator it) { return this->table_.riter(it); }\n  const_reverse_iterator riter(const_iterator it) const {\n    return this->table_.riter(it);\n  }\n\n  friend Range<const_reverse_iterator> tag_invoke(\n      order_preserving_reinsertion_view_fn, F14VectorSet const& c) noexcept {\n    return {c.rbegin(), c.rend()};\n  }\n};\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorSet(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14VectorSet<iterator_value_type_t<InputIt>, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorSet(InputIt, InputIt, std::size_t, Alloc) -> F14VectorSet<\n    iterator_value_type_t<InputIt>,\n    f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14VectorSet<\n    iterator_value_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorSet(\n    std::initializer_list<Key>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14VectorSet<Key, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14VectorSet<\n    Key,\n    f14::DefaultHasher<Key>,\n    f14::DefaultKeyEqual<Key>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14VectorSet(std::initializer_list<Key>, std::size_t, Hasher, Alloc)\n    -> F14VectorSet<Key, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14FastSet\n    : public std::conditional_t<\n          sizeof(Key) < 24,\n          F14ValueSet<Key, Hasher, KeyEqual, Alloc>,\n          f14::detail::\n              F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::true_type>> {\n  using Super = std::conditional_t<\n      sizeof(Key) < 24,\n      F14ValueSet<Key, Hasher, KeyEqual, Alloc>,\n      f14::detail::\n          F14VectorSetImpl<Key, Hasher, KeyEqual, Alloc, std::true_type>>;\n\n public:\n  using typename Super::value_type;\n\n  F14FastSet() = default;\n\n  using Super::Super;\n\n  F14FastSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n\n  void swap(F14FastSet& rhs) noexcept(Super::Policy::kSwapIsNoexcept) {\n    this->table_.swap(rhs.table_);\n  }\n};\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <\n    typename InputIt,\n    typename Hasher = f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    typename KeyEqual = f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    typename Alloc = f14::DefaultAlloc<iterator_value_type_t<InputIt>>,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastSet(\n    InputIt, InputIt, std::size_t = {}, Hasher = {}, KeyEqual = {}, Alloc = {})\n    -> F14FastSet<iterator_value_type_t<InputIt>, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastSet(InputIt, InputIt, std::size_t, Alloc) -> F14FastSet<\n    iterator_value_type_t<InputIt>,\n    f14::DefaultHasher<iterator_value_type_t<InputIt>>,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename InputIt,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireInputIterator<InputIt>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastSet(InputIt, InputIt, std::size_t, Hasher, Alloc) -> F14FastSet<\n    iterator_value_type_t<InputIt>,\n    Hasher,\n    f14::DefaultKeyEqual<iterator_value_type_t<InputIt>>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher = f14::DefaultHasher<Key>,\n    typename KeyEqual = f14::DefaultKeyEqual<Key>,\n    typename Alloc = f14::DefaultAlloc<Key>,\n    typename = detail::RequireNotAllocator<Hasher>,\n    typename = detail::RequireNotAllocator<KeyEqual>,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastSet(\n    std::initializer_list<Key>,\n    std::size_t = {},\n    Hasher = {},\n    KeyEqual = {},\n    Alloc = {}) -> F14FastSet<Key, Hasher, KeyEqual, Alloc>;\n\ntemplate <\n    typename Key,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastSet(std::initializer_list<Key>, std::size_t, Alloc) -> F14FastSet<\n    Key,\n    f14::DefaultHasher<Key>,\n    f14::DefaultKeyEqual<Key>,\n    Alloc>;\n\ntemplate <\n    typename Key,\n    typename Hasher,\n    typename Alloc,\n    typename = detail::RequireAllocator<Alloc>>\nF14FastSet(std::initializer_list<Key>, std::size_t, Hasher, Alloc)\n    -> F14FastSet<Key, Hasher, f14::DefaultKeyEqual<Key>, Alloc>;\n\n} // namespace folly\n\nnamespace folly {\nnamespace f14 {\nnamespace detail {\ntemplate <typename S>\nbool setsEqual(S const& lhs, S const& rhs) {\n  if (lhs.size() != rhs.size()) {\n    return false;\n  }\n  for (auto& k : lhs) {\n    if (!rhs.containsEqualValue(k)) {\n      return false;\n    }\n  }\n  return true;\n}\n} // namespace detail\n} // namespace f14\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator==(\n    F14ValueSet<K, H, E, A> const& lhs, F14ValueSet<K, H, E, A> const& rhs) {\n  return setsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator!=(\n    F14ValueSet<K, H, E, A> const& lhs, F14ValueSet<K, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator==(\n    F14NodeSet<K, H, E, A> const& lhs, F14NodeSet<K, H, E, A> const& rhs) {\n  return setsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator!=(\n    F14NodeSet<K, H, E, A> const& lhs, F14NodeSet<K, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator==(\n    F14VectorSet<K, H, E, A> const& lhs, F14VectorSet<K, H, E, A> const& rhs) {\n  return setsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator!=(\n    F14VectorSet<K, H, E, A> const& lhs, F14VectorSet<K, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator==(\n    F14FastSet<K, H, E, A> const& lhs, F14FastSet<K, H, E, A> const& rhs) {\n  return setsEqual(lhs, rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nbool operator!=(\n    F14FastSet<K, H, E, A> const& lhs, F14FastSet<K, H, E, A> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nvoid swap(F14ValueSet<K, H, E, A>& lhs, F14ValueSet<K, H, E, A>& rhs) noexcept(\n    noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nvoid swap(F14NodeSet<K, H, E, A>& lhs, F14NodeSet<K, H, E, A>& rhs) noexcept(\n    noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nvoid swap(\n    F14VectorSet<K, H, E, A>& lhs,\n    F14VectorSet<K, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A>\nvoid swap(F14FastSet<K, H, E, A>& lhs, F14FastSet<K, H, E, A>& rhs) noexcept(\n    noexcept(lhs.swap(rhs))) {\n  lhs.swap(rhs);\n}\n\ntemplate <typename K, typename H, typename E, typename A, typename Pred>\nstd::size_t erase_if(F14ValueSet<K, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\ntemplate <typename K, typename H, typename E, typename A, typename Pred>\nstd::size_t erase_if(F14NodeSet<K, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\ntemplate <typename K, typename H, typename E, typename A, typename Pred>\nstd::size_t erase_if(F14VectorSet<K, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\ntemplate <typename K, typename H, typename E, typename A, typename Pred>\nstd::size_t erase_if(F14FastSet<K, H, E, A>& c, Pred pred) {\n  return f14::detail::erase_if_impl(c, pred);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/FBVector.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Nicholas Ormrod      (njormrod)\n * Andrei Alexandrescu  (aalexandre)\n *\n * FBVector is Facebook's drop-in implementation of std::vector. It has special\n * optimizations for use with relocatable types and jemalloc.\n */\n\n#pragma once\n\n//=============================================================================\n// headers\n\n#include <algorithm>\n#include <cassert>\n#include <iterator>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <folly/FormatTraits.h>\n#include <folly/Likely.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/lang/CheckedMath.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Hint.h>\n#include <folly/memory/Malloc.h>\n\n//=============================================================================\n// forward declaration\n\nnamespace folly {\ntemplate <class T, class Allocator = std::allocator<T>>\nclass fbvector;\n} // namespace folly\n\n//=============================================================================\n// unrolling\n\n#define FOLLY_FBV_UNROLL_PTR(first, last, OP)     \\\n  do {                                            \\\n    for (; (last) - (first) >= 4; (first) += 4) { \\\n      OP(((first) + 0));                          \\\n      OP(((first) + 1));                          \\\n      OP(((first) + 2));                          \\\n      OP(((first) + 3));                          \\\n    }                                             \\\n    for (; (first) != (last); ++(first))          \\\n      OP((first));                                \\\n  } while (0)\n\n//=============================================================================\n///////////////////////////////////////////////////////////////////////////////\n//                                                                           //\n//                              fbvector class                               //\n//                                                                           //\n///////////////////////////////////////////////////////////////////////////////\n\nnamespace folly {\n\nnamespace detail {\ninline void* thunk_return_nullptr() {\n  return nullptr;\n}\n} // namespace detail\n\ntemplate <class T, class Allocator>\nclass fbvector {\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // implementation\n private:\n  typedef std::allocator_traits<Allocator> A;\n\n  struct Impl : public Allocator {\n    // typedefs\n    typedef typename A::pointer pointer;\n    typedef typename A::size_type size_type;\n\n    // data\n    pointer b_, e_, z_;\n\n    // constructors\n    Impl() : Allocator(), b_(nullptr), e_(nullptr), z_(nullptr) {}\n    /* implicit */ Impl(const Allocator& alloc)\n        : Allocator(alloc), b_(nullptr), e_(nullptr), z_(nullptr) {}\n    /* implicit */ Impl(Allocator&& alloc)\n        : Allocator(std::move(alloc)), b_(nullptr), e_(nullptr), z_(nullptr) {}\n\n    /* implicit */ Impl(size_type n, const Allocator& alloc = Allocator())\n        : Allocator(alloc) {\n      init(n);\n    }\n\n    Impl(Impl&& other) noexcept\n        : Allocator(std::move(other)),\n          b_(other.b_),\n          e_(other.e_),\n          z_(other.z_) {\n      other.b_ = other.e_ = other.z_ = nullptr;\n    }\n\n    // destructor\n    ~Impl() { destroy(); }\n\n    // allocation\n    // note that 'allocate' and 'deallocate' are inherited from Allocator\n    T* D_allocate(size_type n) {\n      if constexpr (kUsingStdAllocator) {\n        return static_cast<T*>(checkedMalloc(n * sizeof(T)));\n      } else {\n        return std::allocator_traits<Allocator>::allocate(*this, n);\n      }\n    }\n\n    void D_deallocate(T* p, size_type n) noexcept {\n      if constexpr (kUsingStdAllocator) {\n        free(p);\n      } else {\n        std::allocator_traits<Allocator>::deallocate(*this, p, n);\n      }\n    }\n\n    // helpers\n    void swapData(Impl& other) {\n      std::swap(b_, other.b_);\n      std::swap(e_, other.e_);\n      std::swap(z_, other.z_);\n    }\n\n    // data ops\n    inline void destroy() noexcept {\n      if (b_) {\n        // THIS DISPATCH CODE IS DUPLICATED IN fbvector::D_destroy_range_a.\n        // It has been inlined here for speed. It calls the static fbvector\n        //  methods to perform the actual destruction.\n        if constexpr (kUsingStdAllocator) {\n          S_destroy_range(b_, e_);\n        } else {\n          S_destroy_range_a(*this, b_, e_);\n        }\n\n        D_deallocate(b_, size_type(z_ - b_));\n      }\n    }\n\n    void init(size_type n) {\n      if (FOLLY_UNLIKELY(n == 0)) {\n        b_ = e_ = z_ = nullptr;\n      } else {\n        size_type sz = folly::goodMallocSize(n * sizeof(T)) / sizeof(T);\n        b_ = D_allocate(sz);\n        e_ = b_;\n        z_ = b_ + sz;\n      }\n    }\n\n    void set(pointer newB, size_type newSize, size_type newCap) {\n      z_ = newB + newCap;\n      e_ = newB + newSize;\n      b_ = newB;\n    }\n\n    void reset(size_type newCap) {\n      destroy();\n      auto rollback = makeGuard([&] { init(0); });\n      init(newCap);\n      rollback.dismiss();\n    }\n    void reset() { // same as reset(0)\n      destroy();\n      b_ = e_ = z_ = nullptr;\n    }\n  } impl_;\n\n  static void swap(Impl& a, Impl& b) {\n    using std::swap;\n    if constexpr (!kUsingStdAllocator) {\n      swap(static_cast<Allocator&>(a), static_cast<Allocator&>(b));\n    }\n    a.swapData(b);\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // types and constants\n public:\n  typedef T value_type;\n  typedef value_type& reference;\n  typedef const value_type& const_reference;\n  typedef T* iterator;\n  typedef const T* const_iterator;\n  typedef size_t size_type;\n  typedef typename std::make_signed<size_type>::type difference_type;\n  typedef Allocator allocator_type;\n  typedef typename A::pointer pointer;\n  typedef typename A::const_pointer const_pointer;\n  typedef std::reverse_iterator<iterator> reverse_iterator;\n  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;\n\n private:\n  static constexpr bool should_pass_by_value =\n      std::is_trivially_copyable<T>::value &&\n      sizeof(T) <= 16; // don't force large structures to be passed by value\n  typedef typename std::conditional<should_pass_by_value, T, const T&>::type VT;\n  typedef typename std::conditional<should_pass_by_value, T, T&&>::type MT;\n\n  static constexpr bool kUsingStdAllocator =\n      std::is_same<Allocator, std::allocator<T>>::value;\n  typedef std::bool_constant<\n      kUsingStdAllocator || A::propagate_on_container_move_assignment::value>\n      moveIsSwap;\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // allocator helpers\n\n  //---------------------------------------------------------------------------\n  // allocate\n\n  T* M_allocate(size_type n) { return impl_.D_allocate(n); }\n\n  //---------------------------------------------------------------------------\n  // deallocate\n\n  void M_deallocate(T* p, size_type n) noexcept { impl_.D_deallocate(p, n); }\n\n  //---------------------------------------------------------------------------\n  // construct\n\n  // GCC is very sensitive to the exact way that construct is called. For\n  //  that reason there are several different specializations of construct.\n\n  template <typename U, typename... Args>\n  void M_construct(U* p, Args&&... args) {\n    if constexpr (kUsingStdAllocator) {\n      new (p) U(std::forward<Args>(args)...);\n    } else {\n      std::allocator_traits<Allocator>::construct(\n          impl_, p, std::forward<Args>(args)...);\n    }\n  }\n\n  template <typename U, typename... Args>\n  static void S_construct(U* p, Args&&... args) {\n    new (p) U(std::forward<Args>(args)...);\n  }\n\n  template <typename U, typename... Args>\n  static void S_construct_a(Allocator& a, U* p, Args&&... args) {\n    std::allocator_traits<Allocator>::construct(\n        a, p, std::forward<Args>(args)...);\n  }\n\n  // scalar optimization\n  // TODO we can expand this optimization to: default copyable and assignable\n  template <\n      typename U,\n      typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type>\n  void M_construct(U* p, U arg) {\n    if constexpr (kUsingStdAllocator) {\n      *p = arg;\n    } else {\n      std::allocator_traits<Allocator>::construct(impl_, p, arg);\n    }\n  }\n\n  template <\n      typename U,\n      typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type>\n  static void S_construct(U* p, U arg) {\n    *p = arg;\n  }\n\n  template <\n      typename U,\n      typename Enable = typename std::enable_if<std::is_scalar<U>::value>::type>\n  static void S_construct_a(Allocator& a, U* p, U arg) {\n    std::allocator_traits<Allocator>::construct(a, p, arg);\n  }\n\n  // const& optimization\n  template <\n      typename U,\n      typename Enable =\n          typename std::enable_if<!std::is_scalar<U>::value>::type>\n  void M_construct(U* p, const U& value) {\n    if constexpr (kUsingStdAllocator) {\n      new (p) U(value);\n    } else {\n      std::allocator_traits<Allocator>::construct(impl_, p, value);\n    }\n  }\n\n  template <\n      typename U,\n      typename Enable =\n          typename std::enable_if<!std::is_scalar<U>::value>::type>\n  static void S_construct(U* p, const U& value) {\n    new (p) U(value);\n  }\n\n  template <\n      typename U,\n      typename Enable =\n          typename std::enable_if<!std::is_scalar<U>::value>::type>\n  static void S_construct_a(Allocator& a, U* p, const U& value) {\n    std::allocator_traits<Allocator>::construct(a, p, value);\n  }\n\n  //---------------------------------------------------------------------------\n  // destroy\n\n  void M_destroy(T* p) noexcept {\n    if constexpr (kUsingStdAllocator) {\n      if constexpr (!std::is_trivially_destructible<T>::value) {\n        p->~T();\n      }\n    } else {\n      std::allocator_traits<Allocator>::destroy(impl_, p);\n    }\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // algorithmic helpers\n private:\n  //---------------------------------------------------------------------------\n  // destroy_range\n\n  // wrappers\n  void M_destroy_range_e(T* pos) noexcept {\n    D_destroy_range_a(pos, impl_.e_);\n    impl_.e_ = pos;\n  }\n\n  // dispatch\n  // THIS DISPATCH CODE IS DUPLICATED IN IMPL. SEE IMPL FOR DETAILS.\n  void D_destroy_range_a(T* first, T* last) noexcept {\n    if constexpr (kUsingStdAllocator) {\n      S_destroy_range(first, last);\n    } else {\n      S_destroy_range_a(impl_, first, last);\n    }\n  }\n\n  // allocator\n  static void S_destroy_range_a(Allocator& a, T* first, T* last) noexcept {\n    for (; first != last; ++first) {\n      std::allocator_traits<Allocator>::destroy(a, first);\n    }\n  }\n\n  // optimized\n  static void S_destroy_range(T* first, T* last) noexcept {\n    if constexpr (!std::is_trivially_destructible<T>::value) {\n#define FOLLY_FBV_OP(p) (p)->~T()\n      // EXPERIMENTAL DATA on fbvector<vector<int>> (where each vector<int> has\n      //  size 0), were vector<int> to be relocatable.\n      // The unrolled version seems to work faster for small to medium sized\n      //  fbvectors. It gets a 10% speedup on fbvectors of size 1024, 64, and\n      //  16.\n      // The simple loop version seems to work faster for large fbvectors. The\n      //  unrolled version is about 6% slower on fbvectors on size 16384.\n      // The two methods seem tied for very large fbvectors. The unrolled\n      //  version is about 0.5% slower on size 262144.\n\n      // for (; first != last; ++first) first->~T();\n      FOLLY_FBV_UNROLL_PTR(first, last, FOLLY_FBV_OP);\n#undef FOLLY_FBV_OP\n    }\n  }\n\n  //---------------------------------------------------------------------------\n  // uninitialized_fill_n\n\n  // wrappers\n  void M_uninitialized_fill_n_e(size_type sz) {\n    D_uninitialized_fill_n_a(impl_.e_, sz);\n    impl_.e_ += sz;\n  }\n\n  void M_uninitialized_fill_n_e(size_type sz, VT value) {\n    D_uninitialized_fill_n_a(impl_.e_, sz, value);\n    impl_.e_ += sz;\n  }\n\n  // dispatch\n  void D_uninitialized_fill_n_a(T* dest, size_type sz) {\n    if constexpr (kUsingStdAllocator) {\n      S_uninitialized_fill_n(dest, sz);\n    } else {\n      S_uninitialized_fill_n_a(impl_, dest, sz);\n    }\n  }\n\n  void D_uninitialized_fill_n_a(T* dest, size_type sz, VT value) {\n    if constexpr (kUsingStdAllocator) {\n      S_uninitialized_fill_n(dest, sz, value);\n    } else {\n      S_uninitialized_fill_n_a(impl_, dest, sz, value);\n    }\n  }\n\n  // allocator\n  template <typename... Args>\n  static void S_uninitialized_fill_n_a(\n      Allocator& a, T* dest, size_type sz, Args&&... args) {\n    auto b = dest;\n    T* e = nullptr;\n    if (!folly::checked_add(&e, dest, sz)) {\n      throw_exception<std::length_error>(\"FBVector exceeded max size.\");\n    }\n    auto rollback = makeGuard([&] { S_destroy_range_a(a, dest, b); });\n    for (; b != e; ++b) {\n      std::allocator_traits<Allocator>::construct(\n          a, b, std::forward<Args>(args)...);\n    }\n    rollback.dismiss();\n  }\n\n  // optimized\n  static void S_uninitialized_fill_n(T* dest, size_type n) {\n    if constexpr (folly::IsZeroInitializable<T>::value) {\n      if (FOLLY_LIKELY(n != 0)) {\n        T* sz = nullptr;\n        if (!folly::checked_add(&sz, dest, n)) {\n          throw_exception<std::length_error>(\"FBVector exceeded max size.\");\n        }\n        std::memset((void*)dest, 0, sizeof(T) * n);\n      }\n    } else {\n      auto b = dest;\n      T* e = nullptr;\n      if (!folly::checked_add(&e, dest, n)) {\n        throw_exception<std::length_error>(\"FBVector exceeded max size.\");\n      }\n      auto rollback = makeGuard([&] {\n        --b;\n        for (; b >= dest; --b) {\n          b->~T();\n        }\n      });\n      for (; b != e; ++b) {\n        S_construct(b);\n      }\n      rollback.dismiss();\n    }\n  }\n\n  static void S_uninitialized_fill_n(T* dest, size_type n, const T& value) {\n    auto b = dest;\n    T* e = nullptr;\n    if (!folly::checked_add(&e, dest, n)) {\n      throw_exception<std::length_error>(\"FBVector exceeded max size.\");\n    }\n    auto rollback = makeGuard([&] { S_destroy_range(dest, b); });\n    for (; b != e; ++b) {\n      S_construct(b, value);\n    }\n    rollback.dismiss();\n  }\n\n  //---------------------------------------------------------------------------\n  // uninitialized_copy\n\n  // it is possible to add an optimization for the case where\n  // It = move(T*) and IsRelocatable<T> and Is0Initializeable<T>\n\n  // wrappers\n  template <typename It>\n  void M_uninitialized_copy_e(It first, It last) {\n    D_uninitialized_copy_a(impl_.e_, first, last);\n    impl_.e_ += std::distance(first, last);\n  }\n\n  template <typename It>\n  void M_uninitialized_move_e(It first, It last) {\n    D_uninitialized_move_a(impl_.e_, first, last);\n    impl_.e_ += std::distance(first, last);\n  }\n\n  // dispatch\n  template <typename It>\n  void D_uninitialized_copy_a(T* dest, It first, It last) {\n    if constexpr (kUsingStdAllocator) {\n      if constexpr (std::is_trivially_copyable<T>::value) {\n        S_uninitialized_copy_bits(dest, first, last);\n      } else {\n        S_uninitialized_copy(dest, first, last);\n      }\n    } else {\n      S_uninitialized_copy_a(impl_, dest, first, last);\n    }\n  }\n\n  template <typename It>\n  void D_uninitialized_move_a(T* dest, It first, It last) {\n    D_uninitialized_copy_a(\n        dest, std::make_move_iterator(first), std::make_move_iterator(last));\n  }\n\n  // allocator\n  template <typename It>\n  static void S_uninitialized_copy_a(Allocator& a, T* dest, It first, It last) {\n    auto b = dest;\n    auto rollback = makeGuard([&] { S_destroy_range_a(a, dest, b); });\n    for (; first != last; ++first, ++b) {\n      std::allocator_traits<Allocator>::construct(a, b, *first);\n    }\n    rollback.dismiss();\n  }\n\n  // optimized\n  template <typename It>\n  static void S_uninitialized_copy(T* dest, It first, It last) {\n    auto b = dest;\n    auto rollback = makeGuard([&] { S_destroy_range(dest, b); });\n    for (; first != last; ++first, ++b) {\n      S_construct(b, *first);\n    }\n    rollback.dismiss();\n  }\n\n  static void S_uninitialized_copy_bits(\n      T* dest, const T* first, const T* last) {\n    if (last != first) {\n      std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T));\n    }\n  }\n\n  static void S_uninitialized_copy_bits(\n      T* dest, std::move_iterator<T*> first, std::move_iterator<T*> last) {\n    T* bFirst = first.base();\n    T* bLast = last.base();\n    if (bLast != bFirst) {\n      std::memcpy((void*)dest, (void*)bFirst, (bLast - bFirst) * sizeof(T));\n    }\n  }\n\n  template <typename It>\n  static void S_uninitialized_copy_bits(T* dest, It first, It last) {\n    S_uninitialized_copy(dest, first, last);\n  }\n\n  //---------------------------------------------------------------------------\n  // copy_n\n\n  // This function is \"unsafe\": it assumes that the iterator can be advanced at\n  //  least n times. However, as a private function, that unsafety is managed\n  //  wholly by fbvector itself.\n\n  template <typename It>\n  static It S_copy_n(T* dest, It first, size_type n) {\n    auto e = dest + n;\n    for (; dest != e; ++dest, ++first) {\n      *dest = *first;\n    }\n    return first;\n  }\n\n  static const T* S_copy_n(T* dest, const T* first, size_type n) {\n    if constexpr (std::is_trivially_copyable<T>::value) {\n      std::memcpy((void*)dest, (void*)first, n * sizeof(T));\n      return first + n;\n    } else {\n      return S_copy_n<const T*>(dest, first, n);\n    }\n  }\n\n  static std::move_iterator<T*> S_copy_n(\n      T* dest, std::move_iterator<T*> mIt, size_type n) {\n    if constexpr (std::is_trivially_copyable<T>::value) {\n      T* first = mIt.base();\n      std::memcpy((void*)dest, (void*)first, n * sizeof(T));\n      return std::make_move_iterator(first + n);\n    } else {\n      return S_copy_n<std::move_iterator<T*>>(dest, mIt, n);\n    }\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // relocation helpers\n private:\n  // Relocation is divided into three parts:\n  //\n  //  1: relocate_move\n  //     Performs the actual movement of data from point a to point b.\n  //\n  //  2: relocate_done\n  //     Destroys the old data.\n  //\n  //  3: relocate_undo\n  //     Destroys the new data and restores the old data.\n  //\n  // The three steps are used because there may be an exception after part 1\n  //  has completed. If that is the case, then relocate_undo can nullify the\n  //  initial move. Otherwise, relocate_done performs the last bit of tidying\n  //  up.\n  //\n  // The relocation trio may use either memcpy, move, or copy. It is decided\n  //  by the following case statement:\n  //\n  //  IsRelocatable && kUsingStdAllocator    -> memcpy\n  //  has_nothrow_move && kUsingStdAllocator -> move\n  //  cannot copy                            -> move\n  //  default                                -> copy\n  //\n  // If the class is non-copyable then it must be movable. However, if the\n  //  move constructor is not noexcept, i.e. an error could be thrown, then\n  //  relocate_undo will be unable to restore the old data, for fear of a\n  //  second exception being thrown. This is a known and unavoidable\n  //  deficiency. In lieu of a strong exception guarantee, relocate_undo does\n  //  the next best thing: it provides a weak exception guarantee by\n  //  destroying the new data, but leaving the old data in an indeterminate\n  //  state. Note that that indeterminate state will be valid, since the\n  //  old data has not been destroyed; it has merely been the source of a\n  //  move, which is required to leave the source in a valid state.\n\n  // wrappers\n  void M_relocate(T* newB) {\n    relocate_move(newB, impl_.b_, impl_.e_);\n    relocate_done(newB, impl_.b_, impl_.e_);\n  }\n\n  // dispatch type trait\n  typedef std::bool_constant<\n      folly::IsRelocatable<T>::value && kUsingStdAllocator>\n      relocate_use_memcpy;\n\n  typedef std::bool_constant<\n      (std::is_nothrow_move_constructible<T>::value && kUsingStdAllocator) ||\n      !std::is_copy_constructible<T>::value>\n      relocate_use_move;\n\n  // move\n  void relocate_move(T* dest, T* first, T* last) {\n    relocate_move_or_memcpy(dest, first, last, relocate_use_memcpy());\n  }\n\n  void relocate_move_or_memcpy(T* dest, T* first, T* last, std::true_type) {\n    if (first != nullptr) {\n      std::memcpy((void*)dest, (void*)first, (last - first) * sizeof(T));\n    }\n  }\n\n  void relocate_move_or_memcpy(T* dest, T* first, T* last, std::false_type) {\n    relocate_move_or_copy(dest, first, last, relocate_use_move());\n  }\n\n  void relocate_move_or_copy(T* dest, T* first, T* last, std::true_type) {\n    D_uninitialized_move_a(dest, first, last);\n  }\n\n  void relocate_move_or_copy(T* dest, T* first, T* last, std::false_type) {\n    D_uninitialized_copy_a(dest, first, last);\n  }\n\n  // done\n  void relocate_done(T* /*dest*/, T* first, T* last) noexcept {\n    if constexpr (folly::IsRelocatable<T>::value && kUsingStdAllocator) {\n      // used memcpy; data has been relocated, do not call destructor\n    } else {\n      D_destroy_range_a(first, last);\n    }\n  }\n\n  // undo\n  void relocate_undo(T* dest, T* first, T* last) noexcept {\n    if constexpr (folly::IsRelocatable<T>::value && kUsingStdAllocator) {\n      // used memcpy, old data is still valid, nothing to do\n    } else if constexpr (\n        std::is_nothrow_move_constructible<T>::value && kUsingStdAllocator) {\n      // noexcept move everything back, aka relocate_move\n      relocate_move(first, dest, dest + (last - first));\n    } else if constexpr (!std::is_copy_constructible<T>::value) {\n      // weak guarantee\n      D_destroy_range_a(dest, dest + (last - first));\n    } else {\n      // used copy, old data is still valid\n      D_destroy_range_a(dest, dest + (last - first));\n    }\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // construct/copy/destroy\n public:\n  fbvector() = default;\n\n  explicit fbvector(const Allocator& a) : impl_(a) {}\n\n  explicit fbvector(size_type n, const Allocator& a = Allocator())\n      : impl_(n, a) {\n    M_uninitialized_fill_n_e(n);\n  }\n\n  fbvector(size_type n, VT value, const Allocator& a = Allocator())\n      : impl_(n, a) {\n    M_uninitialized_fill_n_e(n, value);\n  }\n\n  template <\n      class It,\n      class Category = typename std::iterator_traits<It>::iterator_category>\n  fbvector(It first, It last, const Allocator& a = Allocator())\n      : fbvector(first, last, a, Category()) {}\n\n  fbvector(const fbvector& other)\n      : impl_(\n            other.size(),\n            A::select_on_container_copy_construction(other.impl_)) {\n    M_uninitialized_copy_e(other.begin(), other.end());\n  }\n\n  fbvector(fbvector&& other) noexcept : impl_(std::move(other.impl_)) {}\n\n  fbvector(const fbvector& other, const Allocator& a)\n      : fbvector(other.begin(), other.end(), a) {}\n\n  /* may throw */ fbvector(fbvector&& other, const Allocator& a) : impl_(a) {\n    if (impl_ == other.impl_) {\n      impl_.swapData(other.impl_);\n    } else {\n      impl_.init(other.size());\n      M_uninitialized_move_e(other.begin(), other.end());\n    }\n  }\n\n  fbvector(std::initializer_list<T> il, const Allocator& a = Allocator())\n      : fbvector(il.begin(), il.end(), a) {}\n\n  ~fbvector() = default; // the cleanup occurs in impl_\n\n  fbvector& operator=(const fbvector& other) {\n    if (FOLLY_UNLIKELY(this == &other)) {\n      return *this;\n    }\n\n    if constexpr (\n        !kUsingStdAllocator &&\n        A::propagate_on_container_copy_assignment::value) {\n      if (impl_ != other.impl_) {\n        // can't use other's different allocator to clean up self\n        impl_.reset();\n      }\n      (Allocator&)impl_ = (Allocator&)other.impl_;\n    }\n\n    assign(other.begin(), other.end());\n    return *this;\n  }\n\n  fbvector& operator=(fbvector&& other) {\n    if (FOLLY_UNLIKELY(this == &other)) {\n      return *this;\n    }\n    moveFrom(std::move(other), moveIsSwap());\n    return *this;\n  }\n\n  fbvector& operator=(std::initializer_list<T> il) {\n    assign(il.begin(), il.end());\n    return *this;\n  }\n\n  template <\n      class It,\n      class Category = typename std::iterator_traits<It>::iterator_category>\n  void assign(It first, It last) {\n    assign(first, last, Category());\n  }\n\n  void assign(size_type n, VT value) {\n    if (n > capacity()) {\n      // Not enough space. Do not reserve in place, since we will\n      // discard the old values anyways.\n      if (dataIsInternalAndNotVT(value)) {\n        T copy(std::move(value));\n        impl_.reset(n);\n        M_uninitialized_fill_n_e(n, copy);\n      } else {\n        impl_.reset(n);\n        M_uninitialized_fill_n_e(n, value);\n      }\n    } else if (n <= size()) {\n      auto newE = impl_.b_ + n;\n      std::fill(impl_.b_, newE, value);\n      M_destroy_range_e(newE);\n    } else {\n      std::fill(impl_.b_, impl_.e_, value);\n      M_uninitialized_fill_n_e(n - size(), value);\n    }\n  }\n\n  void assign(std::initializer_list<T> il) { assign(il.begin(), il.end()); }\n\n  allocator_type get_allocator() const noexcept { return impl_; }\n\n private:\n  // contract dispatch for iterator types fbvector(It first, It last)\n  template <class ForwardIterator>\n  fbvector(\n      ForwardIterator first,\n      ForwardIterator last,\n      const Allocator& a,\n      std::forward_iterator_tag)\n      : impl_(size_type(std::distance(first, last)), a) {\n    M_uninitialized_copy_e(first, last);\n  }\n\n  template <class InputIterator>\n  fbvector(\n      InputIterator first,\n      InputIterator last,\n      const Allocator& a,\n      std::input_iterator_tag)\n      : impl_(a) {\n    for (; first != last; ++first) {\n      emplace_back(*first);\n    }\n  }\n\n  // contract dispatch for allocator movement in operator=(fbvector&&)\n  void moveFrom(fbvector&& other, std::true_type) { swap(impl_, other.impl_); }\n  void moveFrom(fbvector&& other, std::false_type) {\n    if (impl_ == other.impl_) {\n      impl_.swapData(other.impl_);\n    } else {\n      impl_.reset(other.size());\n      M_uninitialized_move_e(other.begin(), other.end());\n    }\n  }\n\n  // contract dispatch for iterator types in assign(It first, It last)\n  template <class ForwardIterator>\n  void assign(\n      ForwardIterator first, ForwardIterator last, std::forward_iterator_tag) {\n    const auto newSize = size_type(std::distance(first, last));\n    if (newSize > capacity()) {\n      impl_.reset(newSize);\n      M_uninitialized_copy_e(first, last);\n    } else if (newSize <= size()) {\n      auto newEnd = std::copy(first, last, impl_.b_);\n      M_destroy_range_e(newEnd);\n    } else {\n      auto mid = S_copy_n(impl_.b_, first, size());\n      M_uninitialized_copy_e<decltype(last)>(mid, last);\n    }\n  }\n\n  template <class InputIterator>\n  void assign(\n      InputIterator first, InputIterator last, std::input_iterator_tag) {\n    auto p = impl_.b_;\n    for (; first != last && p != impl_.e_; ++first, ++p) {\n      *p = *first;\n    }\n    if (p != impl_.e_) {\n      M_destroy_range_e(p);\n    } else {\n      for (; first != last; ++first) {\n        emplace_back(*first);\n      }\n    }\n  }\n\n  // contract dispatch for aliasing under VT optimization\n  bool dataIsInternalAndNotVT(const T& t) {\n    if constexpr (should_pass_by_value) {\n      return false;\n    } else {\n      return dataIsInternal(t);\n    }\n  }\n  bool dataIsInternal(const T& t) {\n    return FOLLY_UNLIKELY(\n        impl_.b_ <= std::addressof(t) && std::addressof(t) < impl_.e_);\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // iterators\n public:\n  iterator begin() noexcept { return impl_.b_; }\n  const_iterator begin() const noexcept { return impl_.b_; }\n  iterator end() noexcept { return impl_.e_; }\n  const_iterator end() const noexcept { return impl_.e_; }\n  reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }\n  const_reverse_iterator rbegin() const noexcept {\n    return const_reverse_iterator(end());\n  }\n  reverse_iterator rend() noexcept { return reverse_iterator(begin()); }\n  const_reverse_iterator rend() const noexcept {\n    return const_reverse_iterator(begin());\n  }\n\n  const_iterator cbegin() const noexcept { return impl_.b_; }\n  const_iterator cend() const noexcept { return impl_.e_; }\n  const_reverse_iterator crbegin() const noexcept {\n    return const_reverse_iterator(end());\n  }\n  const_reverse_iterator crend() const noexcept {\n    return const_reverse_iterator(begin());\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // capacity\n public:\n  size_type size() const noexcept { return size_type(impl_.e_ - impl_.b_); }\n\n  size_type max_size() const noexcept {\n    // good luck gettin' there\n    return ~size_type(0);\n  }\n\n  void resize(size_type n) {\n    if (n <= size()) {\n      M_destroy_range_e(impl_.b_ + n);\n    } else {\n      reserve(n);\n      M_uninitialized_fill_n_e(n - size());\n    }\n  }\n\n  void resize(size_type n, VT t) {\n    if (n <= size()) {\n      M_destroy_range_e(impl_.b_ + n);\n    } else if (dataIsInternalAndNotVT(t) && n > capacity()) {\n      T copy(t);\n      reserve(n);\n      M_uninitialized_fill_n_e(n - size(), copy);\n    } else {\n      reserve(n);\n      M_uninitialized_fill_n_e(n - size(), t);\n    }\n  }\n\n  size_type capacity() const noexcept { return size_type(impl_.z_ - impl_.b_); }\n\n  bool empty() const noexcept { return impl_.b_ == impl_.e_; }\n\n  void reserve(size_type n) {\n    if (n <= capacity()) {\n      return;\n    }\n    if (impl_.b_ && reserve_in_place(n)) {\n      return;\n    }\n\n    auto newCap = folly::goodMallocSize(n * sizeof(T)) / sizeof(T);\n    auto newB = M_allocate(newCap);\n    {\n      auto rollback = makeGuard([&] { M_deallocate(newB, newCap); });\n      M_relocate(newB);\n      rollback.dismiss();\n    }\n    if (impl_.b_) {\n      M_deallocate(impl_.b_, size_type(impl_.z_ - impl_.b_));\n    }\n    impl_.z_ = newB + newCap;\n    impl_.e_ = newB + (impl_.e_ - impl_.b_);\n    impl_.b_ = newB;\n  }\n\n  void shrink_to_fit() noexcept {\n    if (empty()) {\n      impl_.reset();\n      return;\n    }\n\n    auto const newCapacityBytes = folly::goodMallocSize(size() * sizeof(T));\n    auto const newCap = newCapacityBytes / sizeof(T);\n    auto const oldCap = capacity();\n\n    if (newCap >= oldCap) {\n      return;\n    }\n\n    void* p = impl_.b_;\n    // xallocx() will shrink to precisely newCapacityBytes (which was generated\n    // by goodMallocSize()) if it successfully shrinks in place.\n    if ((usingJEMalloc() && kUsingStdAllocator) &&\n        newCapacityBytes >= folly::jemallocMinInPlaceExpandable &&\n        xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) {\n      impl_.z_ += newCap - oldCap;\n    } else {\n      T* newB = static_cast<T*>(catch_exception(\n          [&] { return M_allocate(newCap); }, //\n          &detail::thunk_return_nullptr));\n      if (!newB) {\n        return;\n      }\n      if (!catch_exception(\n              [&] { return M_relocate(newB), true; },\n              [&] { return M_deallocate(newB, newCap), false; })) {\n        return;\n      }\n      if (impl_.b_) {\n        M_deallocate(impl_.b_, size_type(impl_.z_ - impl_.b_));\n      }\n      impl_.z_ = newB + newCap;\n      impl_.e_ = newB + (impl_.e_ - impl_.b_);\n      impl_.b_ = newB;\n    }\n  }\n\n private:\n  bool reserve_in_place(size_type n) {\n    if (!kUsingStdAllocator || !usingJEMalloc()) {\n      return false;\n    }\n\n    // jemalloc can never grow in place blocks smaller than 4096 bytes.\n    if ((impl_.z_ - impl_.b_) * sizeof(T) <\n        folly::jemallocMinInPlaceExpandable) {\n      return false;\n    }\n\n    auto const newCapacityBytes = folly::goodMallocSize(n * sizeof(T));\n    void* p = impl_.b_;\n    if (xallocx(p, newCapacityBytes, 0, 0) == newCapacityBytes) {\n      impl_.z_ = impl_.b_ + newCapacityBytes / sizeof(T);\n      return true;\n    }\n    return false;\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // element access\n public:\n  reference operator[](size_type n) {\n    assert(n < size());\n    return impl_.b_[n];\n  }\n  const_reference operator[](size_type n) const {\n    assert(n < size());\n    return impl_.b_[n];\n  }\n  const_reference at(size_type n) const {\n    if (FOLLY_UNLIKELY(n >= size())) {\n      throw_exception<std::out_of_range>(\n          \"fbvector: index is greater than size.\");\n    }\n    return (*this)[n];\n  }\n  reference at(size_type n) {\n    auto const& cThis = *this;\n    return const_cast<reference>(cThis.at(n));\n  }\n  reference front() {\n    assert(!empty());\n    return *impl_.b_;\n  }\n  const_reference front() const {\n    assert(!empty());\n    return *impl_.b_;\n  }\n  reference back() {\n    assert(!empty());\n    return impl_.e_[-1];\n  }\n  const_reference back() const {\n    assert(!empty());\n    return impl_.e_[-1];\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // data access\n public:\n  T* data() noexcept { return impl_.b_; }\n  const T* data() const noexcept { return impl_.b_; }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // modifiers (common)\n public:\n  template <class... Args>\n  reference emplace_back(Args&&... args) {\n    if (impl_.e_ != impl_.z_) {\n      M_construct(impl_.e_, std::forward<Args>(args)...);\n      ++impl_.e_;\n    } else {\n      emplace_back_aux(std::forward<Args>(args)...);\n    }\n    return back();\n  }\n\n  void push_back(const T& value) {\n    if (impl_.e_ != impl_.z_) {\n      M_construct(impl_.e_, value);\n      ++impl_.e_;\n    } else {\n      emplace_back_aux(value);\n    }\n  }\n\n  void push_back(T&& value) {\n    if (impl_.e_ != impl_.z_) {\n      M_construct(impl_.e_, std::move(value));\n      ++impl_.e_;\n    } else {\n      emplace_back_aux(std::move(value));\n    }\n  }\n\n  void pop_back() {\n    assert(!empty());\n    --impl_.e_;\n    M_destroy(impl_.e_);\n  }\n\n  void swap(fbvector& other) noexcept {\n    if constexpr (\n        !kUsingStdAllocator && A::propagate_on_container_swap::value) {\n      swap(impl_, other.impl_);\n    } else {\n      impl_.swapData(other.impl_);\n    }\n  }\n\n  void clear() noexcept { M_destroy_range_e(impl_.b_); }\n\n private:\n  // std::vector implements a similar function with a different growth\n  //  strategy: empty() ? 1 : capacity() * 2.\n  //\n  // fbvector grows differently on two counts:\n  //\n  // (1) initial size\n  //     Instead of growing to size 1 from empty, fbvector allocates at least\n  //     64 bytes. You may still use reserve to reserve a lesser amount of\n  //     memory.\n  // (2) 1.5x\n  //     For medium-sized vectors, the growth strategy is 1.5x. See the docs\n  //     for details.\n  //     This does not apply to very small or very large fbvectors. This is a\n  //     heuristic.\n  //     A nice addition to fbvector would be the capability of having a user-\n  //     defined growth strategy, probably as part of the allocator.\n  //\n\n  size_type computePushBackCapacity() const {\n    if (capacity() == 0) {\n      return std::max(64 / sizeof(T), size_type(1));\n    }\n    if (capacity() < folly::jemallocMinInPlaceExpandable / sizeof(T)) {\n      return capacity() * 2;\n    }\n    if (capacity() > 4096 * 32 / sizeof(T)) {\n      return capacity() * 2;\n    }\n    return (capacity() * 3 + 1) / 2;\n  }\n\n  static bool emplace_back_aux_xallocx(\n      size_type& byte_sz,\n      size_type current_size,\n      size_type push_back_capacity,\n      pointer& z,\n      pointer b) {\n    byte_sz = folly::goodMallocSize(push_back_capacity * sizeof(T));\n    if (kUsingStdAllocator && usingJEMalloc() &&\n        ((z - b) * sizeof(T) >= folly::jemallocMinInPlaceExpandable)) {\n      // Try to reserve in place.\n      // Ask xallocx to allocate in place at least size()+1 and at most sz\n      //  space.\n      // xallocx will allocate as much as possible within that range, which\n      //  is the best possible outcome: if sz space is available, take it all,\n      //  otherwise take as much as possible. If nothing is available, then\n      //  fail.\n      // In this fashion, we never relocate if there is a possibility of\n      //  expanding in place, and we never reallocate by less than the desired\n      //  amount unless we cannot expand further. Hence we will not reallocate\n      //  sub-optimally twice in a row (modulo the blocking memory being freed).\n      size_type lower =\n          folly::goodMallocSize(sizeof(T) + current_size * sizeof(T));\n      size_type upper = byte_sz;\n      size_type extra = upper - lower;\n\n      void* p = b;\n      size_t actual;\n\n      if ((actual = xallocx(p, lower, extra, 0)) >= lower) {\n        z = b + actual / sizeof(T);\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  template <class... Args>\n  void emplace_back_aux(Args&&... args) {\n    // Try to reserve in place.\n    size_type byte_sz;\n    if (emplace_back_aux_xallocx(\n            byte_sz, size(), computePushBackCapacity(), impl_.z_, impl_.b_)) {\n      M_construct(impl_.e_, std::forward<Args>(args)...);\n      ++impl_.e_;\n      return;\n    }\n\n    // Reallocation failed. Perform a manual relocation.\n    size_type sz = byte_sz / sizeof(T);\n    auto newB = M_allocate(sz);\n    auto newE = newB + size();\n    {\n      auto rollback1 = makeGuard([&] { M_deallocate(newB, sz); });\n      if constexpr (folly::IsRelocatable<T>::value && kUsingStdAllocator) {\n        // For linear memory access, relocate before construction.\n        // By the test condition, relocate is noexcept.\n        // Note that there is no cleanup to do if M_construct throws - that's\n        //  one of the beauties of relocation.\n        // Benchmarks for this code have high variance, and seem to be close.\n        relocate_move(newB, impl_.b_, impl_.e_);\n        M_construct(newE, std::forward<Args>(args)...);\n        ++newE;\n      } else {\n        M_construct(newE, std::forward<Args>(args)...);\n        ++newE;\n        auto rollback2 = makeGuard([&] { M_destroy(newE - 1); });\n        M_relocate(newB);\n        rollback2.dismiss();\n      }\n      rollback1.dismiss();\n    }\n    if (impl_.b_) {\n      M_deallocate(impl_.b_, size());\n    }\n    impl_.b_ = newB;\n    impl_.e_ = newE;\n    impl_.z_ = newB + sz;\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // modifiers (erase)\n public:\n  iterator erase(const_iterator position) {\n    return erase(position, position + 1);\n  }\n\n  iterator erase(const_iterator first, const_iterator last) {\n    assert(isValid(first) && isValid(last));\n    assert(first <= last);\n    if (first != last) {\n      if (last == end()) {\n        M_destroy_range_e((iterator)first);\n      } else {\n        if constexpr (folly::IsRelocatable<T>::value && kUsingStdAllocator) {\n          D_destroy_range_a((iterator)first, (iterator)last);\n          if (last - first >= cend() - last) {\n            std::memcpy((void*)first, (void*)last, (cend() - last) * sizeof(T));\n          } else {\n            std::memmove(\n                (void*)first, (void*)last, (cend() - last) * sizeof(T));\n          }\n          impl_.e_ -= (last - first);\n        } else {\n          std::copy(\n              std::make_move_iterator((iterator)last),\n              std::make_move_iterator(end()),\n              (iterator)first);\n          auto newEnd = impl_.e_ - std::distance(first, last);\n          M_destroy_range_e(newEnd);\n        }\n      }\n    }\n    return (iterator)first;\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // modifiers (insert)\n private: // we have the private section first because it defines some macros\n  bool isValid(const_iterator it) { return cbegin() <= it && it <= cend(); }\n\n  size_type computeInsertCapacity(size_type n) {\n    size_type nc = std::max(computePushBackCapacity(), size() + n);\n    size_type ac = folly::goodMallocSize(nc * sizeof(T)) / sizeof(T);\n    return ac;\n  }\n\n  //---------------------------------------------------------------------------\n  //\n  // make_window takes an fbvector, and creates an uninitialized gap (a\n  //  window) at the given position, of the given size. The fbvector must\n  //  have enough capacity.\n  //\n  // Explanation by picture.\n  //\n  //    123456789______\n  //        ^\n  //        make_window here of size 3\n  //\n  //    1234___56789___\n  //\n  // If something goes wrong and the window must be destroyed, use\n  //  undo_window to provide a weak exception guarantee. It destroys\n  //  the right ledge.\n  //\n  //    1234___________\n  //\n  //---------------------------------------------------------------------------\n  //\n  // wrap_frame takes an inverse window and relocates an fbvector around it.\n  //  The fbvector must have at least as many elements as the left ledge.\n  //\n  // Explanation by picture.\n  //\n  //        START\n  //    fbvector:             inverse window:\n  //    123456789______       _____abcde_______\n  //                          [idx][ n ]\n  //\n  //        RESULT\n  //    _______________       12345abcde6789___\n  //\n  //---------------------------------------------------------------------------\n  //\n  // insert_use_fresh_memory returns true iff the fbvector should use a fresh\n  //  block of memory for the insertion. If the fbvector does not have enough\n  //  spare capacity, then it must return true. Otherwise either true or false\n  //  may be returned.\n  //\n  //---------------------------------------------------------------------------\n  //\n  // These three functions, make_window, wrap_frame, and\n  //  insert_use_fresh_memory, can be combined into a uniform interface.\n  // Since that interface involves a lot of case-work, it is built into\n  //  some macros: FOLLY_FBVECTOR_INSERT_(PRE|START|TRY|END)\n  // Macros are used in an attempt to let GCC perform better optimizations,\n  //  especially control flow optimization.\n  //\n\n  //---------------------------------------------------------------------------\n  // window\n\n  void make_window(iterator position, size_type n) {\n    // The result is guaranteed to be non-negative, so use an unsigned type:\n    size_type tail = size_type(std::distance(position, impl_.e_));\n\n    if (tail <= n) {\n      relocate_move(position + n, position, impl_.e_);\n      relocate_done(position + n, position, impl_.e_);\n      impl_.e_ += n;\n    } else {\n      if constexpr (folly::IsRelocatable<T>::value && kUsingStdAllocator) {\n        compiler_may_unsafely_assume(position != nullptr);\n        std::memmove((void*)(position + n), (void*)position, tail * sizeof(T));\n        impl_.e_ += n;\n      } else {\n        D_uninitialized_move_a(impl_.e_, impl_.e_ - n, impl_.e_);\n        {\n          auto rollback = makeGuard([&] {\n            D_destroy_range_a(impl_.e_ - n, impl_.e_ + n);\n            impl_.e_ -= n;\n          });\n          std::copy_backward(\n              std::make_move_iterator(position),\n              std::make_move_iterator(impl_.e_ - n),\n              impl_.e_);\n          rollback.dismiss();\n        }\n        impl_.e_ += n;\n        D_destroy_range_a(position, position + n);\n      }\n    }\n  }\n\n  void undo_window(iterator position, size_type n) noexcept {\n    D_destroy_range_a(position + n, impl_.e_);\n    impl_.e_ = position;\n  }\n\n  //---------------------------------------------------------------------------\n  // frame\n\n  void wrap_frame(T* ledge, size_type idx, size_type n) {\n    assert(size() >= idx);\n    assert(n != 0);\n\n    relocate_move(ledge, impl_.b_, impl_.b_ + idx);\n    {\n      auto rollback = makeGuard([&] { //\n        relocate_undo(ledge, impl_.b_, impl_.b_ + idx);\n      });\n      relocate_move(ledge + idx + n, impl_.b_ + idx, impl_.e_);\n      rollback.dismiss();\n    }\n    relocate_done(ledge, impl_.b_, impl_.b_ + idx);\n    relocate_done(ledge + idx + n, impl_.b_ + idx, impl_.e_);\n  }\n\n  //---------------------------------------------------------------------------\n  // use fresh?\n\n  bool insert_use_fresh(bool at_end, size_type n) {\n    if (at_end) {\n      if (size() + n <= capacity()) {\n        return false;\n      }\n      if (reserve_in_place(size() + n)) {\n        return false;\n      }\n      return true;\n    }\n\n    if (size() + n > capacity()) {\n      return true;\n    }\n\n    return false;\n  }\n\n  //---------------------------------------------------------------------------\n  // interface\n\n  template <\n      typename IsInternalFunc,\n      typename InsertInternalFunc,\n      typename ConstructFunc,\n      typename DestroyFunc>\n  iterator do_real_insert(\n      const_iterator cpos,\n      size_type n,\n      IsInternalFunc&& isInternalFunc,\n      InsertInternalFunc&& insertInternalFunc,\n      ConstructFunc&& constructFunc,\n      DestroyFunc&& destroyFunc) {\n    if (n == 0) {\n      return iterator(cpos);\n    }\n    bool at_end = cpos == cend();\n    bool fresh = insert_use_fresh(at_end, n);\n    if (!at_end) {\n      if (!fresh && isInternalFunc()) {\n        // check for internal data (technically not required by the standard)\n        return insertInternalFunc();\n      }\n      assert(isValid(cpos));\n    }\n    T* position = const_cast<T*>(cpos);\n    size_type idx = size_type(std::distance(impl_.b_, position));\n    T* b;\n    size_type newCap; /* intentionally uninitialized */\n\n    if (fresh) {\n      newCap = computeInsertCapacity(n);\n      b = M_allocate(newCap);\n    } else {\n      if (!at_end) {\n        make_window(position, n);\n      } else {\n        impl_.e_ += n;\n      }\n      b = impl_.b_;\n    }\n\n    T* start = b + idx;\n    {\n      auto rollback = makeGuard([&] {\n        if (fresh) {\n          M_deallocate(b, newCap);\n        } else {\n          if (!at_end) {\n            undo_window(position, n);\n          } else {\n            impl_.e_ -= n;\n          }\n        }\n      });\n      // construct the inserted elements\n      constructFunc(start);\n      rollback.dismiss();\n    }\n\n    if (fresh) {\n      {\n        auto rollback = makeGuard([&] {\n          // delete the inserted elements (exception has been thrown)\n          destroyFunc(start);\n          M_deallocate(b, newCap);\n        });\n        wrap_frame(b, idx, n);\n        rollback.dismiss();\n      }\n      if (impl_.b_) {\n        M_deallocate(impl_.b_, capacity());\n      }\n      impl_.set(b, size() + n, newCap);\n      return impl_.b_ + idx;\n    } else {\n      return position;\n    }\n  }\n\n public:\n  template <class... Args>\n  iterator emplace(const_iterator cpos, Args&&... args) {\n    return do_real_insert(\n        cpos,\n        1,\n        [&] { return false; },\n        [&] { return iterator{}; },\n        [&](iterator start) {\n          M_construct(start, std::forward<Args>(args)...);\n        },\n        [&](iterator start) { M_destroy(start); });\n  }\n\n  iterator insert(const_iterator cpos, const T& value) {\n    return do_real_insert(\n        cpos,\n        1,\n        [&] { return dataIsInternal(value); },\n        [&] { return insert(cpos, T(value)); },\n        [&](iterator start) { M_construct(start, value); },\n        [&](iterator start) { M_destroy(start); });\n  }\n\n  iterator insert(const_iterator cpos, T&& value) {\n    return do_real_insert(\n        cpos,\n        1,\n        [&] { return dataIsInternal(value); },\n        [&] { return insert(cpos, T(std::move(value))); },\n        [&](iterator start) { M_construct(start, std::move(value)); },\n        [&](iterator start) { M_destroy(start); });\n  }\n\n  iterator insert(const_iterator cpos, size_type n, VT value) {\n    return do_real_insert(\n        cpos,\n        n,\n        [&] { return dataIsInternalAndNotVT(value); },\n        [&] { return insert(cpos, n, T(value)); },\n        [&](iterator start) { D_uninitialized_fill_n_a(start, n, value); },\n        [&](iterator start) { D_destroy_range_a(start, start + n); });\n  }\n\n  template <\n      class It,\n      class Category = typename std::iterator_traits<It>::iterator_category>\n  iterator insert(const_iterator cpos, It first, It last) {\n    return insert(cpos, first, last, Category());\n  }\n\n  iterator insert(const_iterator cpos, std::initializer_list<T> il) {\n    return insert(cpos, il.begin(), il.end());\n  }\n\n  //---------------------------------------------------------------------------\n  // insert dispatch for iterator types\n private:\n  template <class FIt>\n  iterator insert(\n      const_iterator cpos, FIt first, FIt last, std::forward_iterator_tag) {\n    size_type n = size_type(std::distance(first, last));\n    return do_real_insert(\n        cpos,\n        n,\n        [&] { return false; },\n        [&] { return iterator{}; },\n        [&](iterator start) { D_uninitialized_copy_a(start, first, last); },\n        [&](iterator start) { D_destroy_range_a(start, start + n); });\n  }\n\n  template <class IIt>\n  iterator insert(\n      const_iterator cpos, IIt first, IIt last, std::input_iterator_tag) {\n    T* position = const_cast<T*>(cpos);\n    assert(isValid(position));\n    size_type idx = std::distance(begin(), position);\n\n    fbvector storage(\n        std::make_move_iterator(position),\n        std::make_move_iterator(end()),\n        A::select_on_container_copy_construction(impl_));\n    M_destroy_range_e(position);\n    for (; first != last; ++first) {\n      emplace_back(*first);\n    }\n    insert(\n        cend(),\n        std::make_move_iterator(storage.begin()),\n        std::make_move_iterator(storage.end()));\n    return impl_.b_ + idx;\n  }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // lexicographical functions\n public:\n  bool operator==(const fbvector& other) const {\n    return size() == other.size() && std::equal(begin(), end(), other.begin());\n  }\n\n  bool operator!=(const fbvector& other) const { return !(*this == other); }\n\n  bool operator<(const fbvector& other) const {\n    return std::lexicographical_compare(\n        begin(), end(), other.begin(), other.end());\n  }\n\n  bool operator>(const fbvector& other) const { return other < *this; }\n\n  bool operator<=(const fbvector& other) const { return !(*this > other); }\n\n  bool operator>=(const fbvector& other) const { return !(*this < other); }\n\n  //===========================================================================\n  //---------------------------------------------------------------------------\n  // friends\n private:\n  template <class _T, class _A>\n  friend _T* relinquish(fbvector<_T, _A>&);\n\n  template <class _T, class _A>\n  friend void attach(fbvector<_T, _A>&, _T* data, size_t sz, size_t cap);\n\n}; // class fbvector\n\n//=============================================================================\n//-----------------------------------------------------------------------------\n// specialized functions\n\ntemplate <class T, class A>\nvoid swap(fbvector<T, A>& lhs, fbvector<T, A>& rhs) noexcept {\n  lhs.swap(rhs);\n}\n\n//=============================================================================\n//-----------------------------------------------------------------------------\n// other\n\nnamespace detail {\n\n// Format support.\ntemplate <class T, class A>\nstruct IndexableTraits<fbvector<T, A>>\n    : public IndexableTraitsSeq<fbvector<T, A>> {};\n\n} // namespace detail\n\ntemplate <class T, class A>\nvoid compactResize(fbvector<T, A>* v, size_t sz) {\n  v->resize(sz);\n  v->shrink_to_fit();\n}\n\n// DANGER\n//\n// relinquish and attach are not a members function specifically so that it is\n//  awkward to call them. It is very easy to shoot yourself in the foot with\n//  these functions.\n//\n// If you call relinquish, then it is your responsibility to free the data\n//  and the storage, both of which may have been generated in a non-standard\n//  way through the fbvector's allocator.\n//\n// If you call attach, it is your responsibility to ensure that the fbvector\n//  is fresh (size and capacity both zero), and that the supplied data is\n//  capable of being manipulated by the allocator.\n// It is acceptable to supply a stack pointer IF:\n//  (1) The vector's data does not outlive the stack pointer. This includes\n//      extension of the data's life through a move operation.\n//  (2) The pointer has enough capacity that the vector will never be\n//      relocated.\n//  (3) Insert is not called on the vector; these functions have leeway to\n//      relocate the vector even if there is enough capacity.\n//  (4) A stack pointer is compatible with the fbvector's allocator.\n//\n\ntemplate <class T, class A>\nT* relinquish(fbvector<T, A>& v) {\n  T* ret = v.data();\n  v.impl_.b_ = v.impl_.e_ = v.impl_.z_ = nullptr;\n  return ret;\n}\n\ntemplate <class T, class A>\nvoid attach(fbvector<T, A>& v, T* data, size_t sz, size_t cap) {\n  assert(v.data() == nullptr);\n  v.impl_.b_ = data;\n  v.impl_.e_ = data + sz;\n  v.impl_.z_ = data + cap;\n}\n\ntemplate <\n    class InputIt,\n    class Allocator =\n        std::allocator<typename std::iterator_traits<InputIt>::value_type>>\nfbvector(InputIt, InputIt, Allocator = Allocator())\n    -> fbvector<typename std::iterator_traits<InputIt>::value_type, Allocator>;\n\ntemplate <class T, class A, class U>\nvoid erase(fbvector<T, A>& v, U value) {\n  v.erase(std::remove(v.begin(), v.end(), value), v.end());\n}\n\ntemplate <class T, class A, class Predicate>\nvoid erase_if(fbvector<T, A>& v, Predicate predicate) {\n  v.erase(std::remove_if(v.begin(), v.end(), std::ref(predicate)), v.end());\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/container/Foreach-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cassert>\n#include <cstdint>\n#include <initializer_list>\n#include <iterator>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/container/Access.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\n\nnamespace for_each_detail {\n\nnamespace adl {\n\n/* using override */\nusing std::get;\n\n/**\n * The adl_ functions below lookup the function name in the namespace of the\n * type of the object being passed into the function. If no function with that\n * name exists for the passed object then the default std:: versions are going\n * to be called\n */\ntemplate <std::size_t Index, typename Type>\nauto adl_get(Type&& instance) -> decltype(get<Index>(std::declval<Type>())) {\n  return get<Index>(std::forward<Type>(instance));\n}\n\n} // namespace adl\n\n/**\n * Enable if the tuple supports fetching via a member get<>()\n */\ntemplate <typename T>\nusing EnableIfMemberGetFound =\n    void_t<decltype(std::declval<T>().template get<0>())>;\ntemplate <typename, typename T>\nstruct IsMemberGetFound : std::bool_constant<!require_sizeof<T>> {};\ntemplate <typename T>\nstruct IsMemberGetFound<EnableIfMemberGetFound<T>, T> : std::true_type {};\n\n/**\n * A get that tries member get<> first and if that is not found tries ADL get<>.\n * This mechanism is as found in the structured bindings proposal here 11.5.3.\n * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf\n */\ntemplate <\n    std::size_t Index,\n    typename Type,\n    std::enable_if_t<!IsMemberGetFound<void, Type>::value, int> = 0>\nauto get_impl(Type&& instance)\n    -> decltype(adl::adl_get<Index>(static_cast<Type&&>(instance))) {\n  return adl::adl_get<Index>(static_cast<Type&&>(instance));\n}\ntemplate <\n    std::size_t Index,\n    typename Type,\n    std::enable_if_t<IsMemberGetFound<void, Type>::value, int> = 0>\nauto get_impl(Type&& instance)\n    -> decltype(static_cast<Type&&>(instance).template get<Index>()) {\n  return static_cast<Type&&>(instance).template get<Index>();\n}\n\n/**\n * Check if the sequence is a tuple\n */\ntemplate <typename Type, typename T = typename std::decay<Type>::type>\nusing EnableIfTuple = void_t<\n    decltype(get_impl<0>(std::declval<T>())),\n    decltype(std::tuple_size<T>::value)>;\ntemplate <typename, typename T>\nstruct IsTuple : std::bool_constant<!require_sizeof<T>> {};\ntemplate <typename T>\nstruct IsTuple<EnableIfTuple<T>, T> : std::true_type {};\n\n/**\n * Check if the sequence is a range\n */\ntemplate <typename Type, typename T = typename std::decay<Type>::type>\nusing EnableIfRange = void_t<\n    decltype(access::begin(std::declval<T&>())),\n    decltype(access::end(std::declval<T&>()))>;\ntemplate <typename, typename T>\nstruct IsRange : std::bool_constant<!require_sizeof<T>> {};\ntemplate <typename T>\nstruct IsRange<EnableIfRange<T>, T> : std::true_type {};\n\nstruct TupleTag {};\nstruct RangeTag {};\n\n/**\n * Should ideally check if it is a tuple and if not return void, but msvc fails\n */\ntemplate <typename Sequence>\nusing SequenceTag =\n    std::conditional_t<IsRange<void, Sequence>::value, RangeTag, TupleTag>;\n\nstruct BeginAddTag {};\nstruct IndexingTag {};\n\ntemplate <typename Func, typename Item, typename Iter>\nusing ForEachImplTag = std::conditional_t<\n    is_invocable_v<Func, Item, index_constant<0>, Iter>,\n    index_constant<3>,\n    std::conditional_t<\n        is_invocable_v<Func, Item, index_constant<0>>,\n        index_constant<2>,\n        std::conditional_t<\n            is_invocable_v<Func, Item>,\n            index_constant<1>,\n            void>>>;\n\ntemplate <\n    typename Func,\n    typename... Args,\n    std::enable_if_t<is_invocable_r_v<LoopControl, Func, Args...>, int> = 0>\nLoopControl invoke_returning_loop_control(Func&& f, Args&&... a) {\n  return static_cast<Func&&>(f)(static_cast<Args&&>(a)...);\n}\ntemplate <\n    typename Func,\n    typename... Args,\n    std::enable_if_t<!is_invocable_r_v<LoopControl, Func, Args...>, int> = 0>\nLoopControl invoke_returning_loop_control(Func&& f, Args&&... a) {\n  static_assert(\n      std::is_void<invoke_result_t<Func, Args...>>::value,\n      \"return either LoopControl or void\");\n  return static_cast<Func&&>(f)(static_cast<Args&&>(a)...), loop_continue;\n}\n\n/**\n * Implementations for the runtime function\n */\ntemplate <typename Sequence, typename Func>\nvoid for_each_range_impl(index_constant<3>, Sequence&& range, Func& func) {\n  auto first = access::begin(range);\n  auto last = access::end(range);\n  for (auto index = std::size_t{0}; first != last; ++index) {\n    auto next = std::next(first);\n    auto control = invoke_returning_loop_control(func, *first, index, first);\n    if (loop_break == control) {\n      break;\n    }\n    first = next;\n  }\n}\ntemplate <typename Sequence, typename Func>\nvoid for_each_range_impl(index_constant<2>, Sequence&& range, Func& func) {\n  // make a three arg adaptor for the function passed in so that the main\n  // implementation function can be used\n  auto three_arg_adaptor =\n      [&func](auto&& ele, auto index, auto) -> decltype(auto) {\n    return func(std::forward<decltype(ele)>(ele), index);\n  };\n  for_each_range_impl(\n      index_constant<3>{}, std::forward<Sequence>(range), three_arg_adaptor);\n}\n\ntemplate <typename Sequence, typename Func>\nvoid for_each_range_impl(index_constant<1>, Sequence&& range, Func& func) {\n  // make a three argument adaptor for the function passed in that just ignores\n  // the second and third argument\n  auto three_arg_adaptor = [&func](auto&& ele, auto, auto) -> decltype(auto) {\n    return func(std::forward<decltype(ele)>(ele));\n  };\n  for_each_range_impl(\n      index_constant<3>{}, std::forward<Sequence>(range), three_arg_adaptor);\n}\n\n/**\n * Handlers for iteration\n */\ntemplate <typename Sequence, typename Func, std::size_t... Indices>\nvoid for_each_tuple_impl(\n    std::index_sequence<Indices...>, Sequence&& seq, Func& func) {\n  using _ = int[];\n\n  // unroll the loop in an initializer list construction parameter expansion\n  // pack\n  auto control = loop_continue;\n\n  // cast to void to ignore the result; use the int[] initialization to do the\n  // loop execution, the ternary conditional will decide whether or not to\n  // evaluate the result\n  //\n  // if func does not return loop-control, expect the optimizer to see through\n  // invoke_returning_loop_control always returning loop_continue\n  void(_{(\n      ((control == loop_continue)\n           ? (control = invoke_returning_loop_control(\n                  func,\n                  get_impl<Indices>(std::forward<Sequence>(seq)),\n                  index_constant<Indices>{}))\n           : (loop_continue)),\n      0)...});\n}\n\n/**\n * The two top level compile time loop iteration functions handle the dispatch\n * based on the number of arguments the passed in function can be passed, if 2\n * arguments can be passed then the implementation dispatches work further to\n * the implementation classes above. If not then an adaptor is constructed\n * which is passed on to the 2 argument specialization, which then in turn\n * forwards implementation to the implementation classes above\n */\ntemplate <typename Sequence, typename Func>\nvoid for_each_tuple_impl(index_constant<2>, Sequence&& seq, Func& func) {\n  // pass the length as an index sequence to the implementation as an\n  // optimization over manual template \"tail recursion\" unrolling\n  using size = std::tuple_size<typename std::decay<Sequence>::type>;\n  for_each_tuple_impl(\n      std::make_index_sequence<size::value>{},\n      std::forward<Sequence>(seq),\n      func);\n}\ntemplate <typename Sequence, typename Func>\nvoid for_each_tuple_impl(index_constant<1>, Sequence&& seq, Func& func) {\n  // make an adaptor for the function passed in, in case it can only be passed\n  // on argument\n  auto two_arg_adaptor = [&func](auto&& ele, auto) -> decltype(auto) {\n    return func(std::forward<decltype(ele)>(ele));\n  };\n  for_each_tuple_impl(\n      index_constant<2>{}, std::forward<Sequence>(seq), two_arg_adaptor);\n}\n\n/**\n * Top level handlers for the for_each loop, with one overload for tuples and\n * one overload for ranges\n *\n * This implies that if type is both a range and a tuple, it is treated as a\n * range rather than as a tuple\n */\ntemplate <typename Sequence, typename Func>\nvoid for_each_impl(TupleTag, Sequence&& range, Func& func) {\n  using type = decltype(get_impl<0>(std::declval<Sequence>()));\n  using tag = ForEachImplTag<Func, type, void>;\n  static_assert(!std::is_same<tag, void>::value, \"unknown invocability\");\n  for_each_tuple_impl(tag{}, std::forward<Sequence>(range), func);\n}\ntemplate <typename Sequence, typename Func>\nvoid for_each_impl(RangeTag, Sequence&& range, Func& func) {\n  using iter = decltype(access::begin(std::declval<Sequence>()));\n  using type = decltype(*std::declval<iter>());\n  using tag = ForEachImplTag<Func, type, iter>;\n  static_assert(!std::is_same<tag, void>::value, \"unknown invocability\");\n  for_each_range_impl(tag{}, std::forward<Sequence>(range), func);\n}\n\ntemplate <typename Sequence, typename Index>\ndecltype(auto) fetch_impl(IndexingTag, Sequence&& sequence, Index&& index) {\n  return std::forward<Sequence>(sequence)[std::forward<Index>(index)];\n}\ntemplate <typename Sequence, typename Index>\ndecltype(auto) fetch_impl(BeginAddTag, Sequence&& sequence, Index index) {\n  return *(access::begin(std::forward<Sequence>(sequence)) + index);\n}\n\ntemplate <typename Sequence, typename Index>\ndecltype(auto) fetch_impl(TupleTag, Sequence&& sequence, Index index) {\n  return get_impl<index>(std::forward<Sequence>(sequence));\n}\ntemplate <typename Sequence, typename Index>\ndecltype(auto) fetch_impl(RangeTag, Sequence&& sequence, Index&& index) {\n  using iter = decltype(access::begin(std::declval<Sequence>()));\n  using iter_traits = std::iterator_traits<remove_cvref_t<iter>>;\n  using iter_cat = typename iter_traits::iterator_category;\n  using tag = std::conditional_t<\n      std::is_same<iter_cat, std::random_access_iterator_tag>::value,\n      BeginAddTag,\n      IndexingTag>;\n  return fetch_impl(\n      tag{}, std::forward<Sequence>(sequence), std::forward<Index>(index));\n}\n\n} // namespace for_each_detail\n\ntemplate <typename Sequence, typename Func>\nconstexpr Func for_each(Sequence&& sequence, Func func) {\n  namespace fed = for_each_detail;\n  using tag = fed::SequenceTag<Sequence>;\n  fed::for_each_impl(tag{}, std::forward<Sequence>(sequence), func);\n  return func;\n}\n\ntemplate <typename Sequence, typename Index>\nconstexpr decltype(auto) fetch(Sequence&& sequence, Index&& index) {\n  namespace fed = for_each_detail;\n  using tag = fed::SequenceTag<Sequence>;\n  return for_each_detail::fetch_impl(\n      tag{}, std::forward<Sequence>(sequence), std::forward<Index>(index));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/Foreach.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/Preprocessor.h>\n\n#include <type_traits>\n\nnamespace folly {\n\n/**\n * @function for_each\n *\n * folly::for_each is a generalized iteration algorithm. Example:\n *\n *  auto one = std::make_tuple(1, 2, 3);\n *  auto two = std::vector<int>{1, 2, 3};\n *  auto func = [](auto element, auto index) {\n *    cout << index << \" : \" << element << endl;\n *  };\n *  folly::for_each(one, func);\n *  folly::for_each(two, func);\n *\n * The for_each function allows iteration through sequences, these can either be\n * runtime sequences (i.e. entities for which std::begin and std::end work) or\n * compile time sequences (as deemed by the presence of std::tuple_length<> and\n * member get<> or ADL get<> functions).\n *\n * If a sequence type is both a runtime sequence (aka range) and a compile-time\n * sequence (aka tuple), then it is treated as a range in preference to a tuple.\n * An example of such a type is std::array.\n *\n * The function is made to provide a convenient library based alternative to the\n * proposal p0589r0, which aims to generalize the range based for loop even\n * further to work with compile time sequences.\n *\n * A drawback of using range based for loops is that sometimes you do not have\n * access to the index within the range. This provides easy access to that, even\n * with compile time sequences.\n *\n * And breaking out is easy:\n *\n *  auto range_one = std::vector<int>{1, 2, 3};\n *  auto range_two = std::make_tuple(1, 2, 3);\n *  auto func = [](auto ele, auto index) {\n *    cout << \"Element at index \" << index << \" : \" << ele;\n *    if (index == 1) {\n *      return folly::loop_break;\n *    }\n *    return folly::loop_continue;\n *  };\n *  folly::for_each(range_one, func);\n *  folly::for_each(range_two, func);\n *\n * A simple use case would be when using futures, if the user was doing calls to\n * n servers then they would accept the callback with the futures like this:\n *\n *  auto vec = std::vector<std::future<int>>{request_one(), ...};\n *  when_all(vec.begin(), vec.end()).then([](auto futures) {\n *    folly::for_each(futures, [](auto& fut) { ... });\n *  });\n *\n * Now when this code switches to use tuples instead of the runtime std::vector,\n * then the loop does not need to change, the code will still work just fine:\n *\n *  when_all(future_one, future_two, future_three).then([](auto futures) {\n *    folly::for_each(futures, [](auto& fut) { ... });\n *  });\n */\ntemplate <typename Range, typename Func>\nconstexpr Func for_each(Range&& range, Func func);\n\n/**\n * The user should return loop_break and loop_continue if they want to iterate\n * in such a way that they can preemptively stop the loop and break out when\n * certain conditions are met.\n */\nnamespace for_each_detail {\nenum class LoopControl : bool { BREAK, CONTINUE };\n} // namespace for_each_detail\n\nconstexpr auto loop_break = for_each_detail::LoopControl::BREAK;\nconstexpr auto loop_continue = for_each_detail::LoopControl::CONTINUE;\n\n/**\n * Utility method to help access elements of a sequence with one uniform\n * interface.\n *\n * This can be useful for example when you are looping through a sequence and\n * want to modify another sequence based on the information in the current\n * sequence:\n *\n *  auto range_one = std::make_tuple(1, 2, 3);\n *  auto range_two = std::make_tuple(4, 5, 6);\n *  folly::for_each(range_one, [&range_two](auto ele, auto index) {\n *    folly::fetch(range_two, index) = ele;\n *  });\n *\n * For ranges, this works by first trying to use the iterator class if the\n * iterator has been marked to be a random access iterator. This should be\n * inspectable via the std::iterator_traits traits class. If the iterator class\n * is not present or is not a random access iterator then the implementation\n * falls back to trying to use the indexing operator (operator[]) to fetch the\n * required element.\n */\ntemplate <typename Sequence, typename Index>\nconstexpr decltype(auto) fetch(Sequence&& sequence, Index&& index);\n\n} // namespace folly\n\n/**\n * Everything below is deprecated.\n */\n\n/*\n * Form a local variable name from \"FOR_EACH_\" x __LINE__, so that\n * FOR_EACH can be nested without creating shadowed declarations.\n */\n#define _FE_ANON(x) FB_CONCATENATE(FOR_EACH_, FB_CONCATENATE(x, __LINE__))\n\n/*\n * If you just want the element values, please use:\n *\n *    for (auto&& element : collection)\n *\n * If you need access to the iterators please write an explicit iterator loop\n */\n#define FOR_EACH(i, c)                                                     \\\n  if (bool _FE_ANON(s1_) = false) {                                        \\\n  } else                                                                   \\\n    for (auto&& _FE_ANON(s2_) = (c); !_FE_ANON(s1_); _FE_ANON(s1_) = true) \\\n      for (auto i = _FE_ANON(s2_).begin(); i != _FE_ANON(s2_).end(); ++i)\n\n/*\n * If you just want the element values, please use this (ranges-v3) construct:\n *\n *    for (auto&& element : collection | views::reverse)\n *\n * If you need access to the iterators please write an explicit iterator loop\n */\n#define FOR_EACH_R(i, c)                                                   \\\n  if (bool _FE_ANON(s1_) = false) {                                        \\\n  } else                                                                   \\\n    for (auto&& _FE_ANON(s2_) = (c); !_FE_ANON(s1_); _FE_ANON(s1_) = true) \\\n      for (auto i = _FE_ANON(s2_).rbegin(); i != _FE_ANON(s2_).rend(); ++i)\n\nnamespace folly {\nnamespace detail {\n\n/**\n * notThereYet helps the FOR_EACH_RANGE macro by opportunistically\n * using \"<\" instead of \"!=\" whenever available when checking for loop\n * termination. This makes e.g. examples such as FOR_EACH_RANGE (i,\n * 10, 5) execute zero iterations instead of looping virtually\n * forever. At the same time, some iterator types define \"!=\" but not\n * \"<\". The notThereYet function will dispatch differently for those.\n *\n * The code below uses `<` for a conservative subset of types for which\n * it is known to be valid.\n */\n\ntemplate <class T, class U>\ntypename std::enable_if<\n    (std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||\n        (std::is_pointer<T>::value && std::is_pointer<U>::value),\n    bool>::type\nnotThereYet(T& iter, const U& end) {\n  return iter < end;\n}\n\ntemplate <class T, class U>\ntypename std::enable_if<\n    !((std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||\n      (std::is_pointer<T>::value && std::is_pointer<U>::value)),\n    bool>::type\nnotThereYet(T& iter, const U& end) {\n  return iter != end;\n}\n\n} // namespace detail\n} // namespace folly\n\n/*\n * Look at the Ranges-v3 views and you'll probably find an easier way to build\n * the view you want but the equivalent is roughly:\n *\n *    for (auto& element : make_subrange(begin, end))\n */\n#define FOR_EACH_RANGE(i, begin, end)          \\\n  for (auto i = (true ? (begin) : (end));      \\\n       ::folly::detail::notThereYet(i, (end)); \\\n       ++i)\n\n#include <folly/container/Foreach-inl.h>\n"
  },
  {
    "path": "folly/container/HeterogeneousAccess-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly {\n\ntemplate <typename T, typename Enable = void>\nstruct HeterogeneousAccessEqualTo;\n\ntemplate <typename T, typename Enable = void>\nstruct HeterogeneousAccessHash;\n\ntemplate <typename CharT>\nstruct TransparentStringEqualTo;\n\ntemplate <typename CharT>\nstruct TransparentStringHash;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/HeterogeneousAccess.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <string>\n#include <string_view>\n\n#include <folly/Range.h>\n#include <folly/Traits.h>\n#include <folly/container/HeterogeneousAccess-fwd.h>\n#include <folly/hash/Hash.h>\n#include <folly/hash/detail/RandomSeed.h>\n#include <folly/hash/rapidhash.h>\n\nnamespace folly {\n\n// folly::HeterogeneousAccessEqualTo<T>, and\n// folly::HeterogeneousAccessHash<T> are functors suitable as defaults\n// for containers that support heterogeneous access.  When possible, they\n// will be marked as transparent.  When no transparent implementation\n// is available then they fall back to std::equal_to and std::hash\n// respectively.  Since the fallbacks are not marked as transparent,\n// heterogeneous lookup won't be available in that case.  A corresponding\n// HeterogeneousAccessLess<T> could be easily added if desired.\n//\n// If T can be implicitly converted to a StringPiece or\n// to a Range<T::value_type const*> that is hashable, then\n// HeterogeneousAccess{EqualTo,Hash}<T> will be transparent without any\n// additional work.  In practice this is true for T that can be convered to\n// StringPiece or Range<IntegralType const*>.  This includes std::string,\n// std::string_view (when available), std::array, folly::Range,\n// std::vector, and folly::small_vector.\n//\n// Additional specializations of HeterogeneousAccess*<T> should go in\n// the header that declares T.  Don't forget to typedef is_transparent to\n// void and folly_is_avalanching to std::true_type in the specializations.\n//\n// Hash values of folly::HeterogeneousAccessHash are not guaranteed to be\n// stable across different program runs, so do not persist hash values\n// anywhere.\n\ntemplate <typename T, typename Enable>\nstruct HeterogeneousAccessEqualTo : std::equal_to<T> {};\n\ntemplate <typename T, typename Enable>\nstruct HeterogeneousAccessHash : std::hash<T> {\n  using folly_is_avalanching = IsAvalanchingHasher<std::hash<T>, T>;\n};\n\n//////// strings\n\nnamespace detail {\n\ntemplate <typename T, typename Enable = void>\nstruct ValueTypeForTransparentConversionToRange {\n  using type = char;\n};\n\n// We assume that folly::hasher<folly::Range<T const*>> won't be enabled\n// when it would be lower quality than std::hash<U> for a U that is\n// convertible to folly::Range<T const*>.\ntemplate <typename T>\nstruct ValueTypeForTransparentConversionToRange<\n    T,\n    void_t<\n        decltype(std::declval<hasher<Range<typename T::value_type const*>>>()(\n            std::declval<Range<typename T::value_type const*>>()))>> {\n  using type = std::remove_const_t<typename T::value_type>;\n};\n\ntemplate <typename T>\nusing TransparentlyConvertibleToRange = std::is_convertible<\n    T,\n    Range<typename ValueTypeForTransparentConversionToRange<T>::type const*>>;\n\ntemplate <typename T>\nstruct TransparentRangeEqualTo {\n  using is_transparent = void;\n\n  template <typename TOtherHash>\n  using is_compatible =\n      std::is_base_of<detail::TransparentRangeEqualTo<T>, TOtherHash>;\n\n  template <typename U1, typename U2>\n  bool operator()(U1 const& lhs, U2 const& rhs) const {\n    return Range<T const*>{lhs} == Range<T const*>{rhs};\n  }\n\n  // This overload is not required for functionality, but\n  // guarantees that replacing std::equal_to<std::string> with\n  // HeterogeneousAccessEqualTo<std::string> is truly zero overhead\n  bool operator()(std::string const& lhs, std::string const& rhs) const {\n    return lhs == rhs;\n  }\n};\n\ntemplate <typename T>\nstruct TransparentRangeHash {\n  using is_transparent = void;\n  using folly_is_avalanching = std::true_type;\n\n  template <typename TOtherHash>\n  using is_compatible =\n      std::is_base_of<detail::TransparentRangeHash<T>, TOtherHash>;\n\n  template <typename U>\n  std::size_t operator()(U const& stringish) const {\n    return hasher<Range<T const*>>{}(Range<T const*>{stringish});\n  }\n};\n\ntemplate <>\nstruct TransparentRangeHash<char> {\n  using is_transparent = void;\n  using folly_is_avalanching = std::true_type;\n\n  template <typename TOtherHash>\n  using is_compatible =\n      std::is_base_of<detail::TransparentRangeHash<char>, TOtherHash>;\n\n  template <typename U>\n  std::size_t operator()(U const& stringish) const {\n    auto sp = StringPiece{stringish};\n    return static_cast<std::size_t>(folly::hash::rapidhashNano_with_seed(\n        sp.data(), sp.size(), hash::detail::RandomSeed::seed()));\n  }\n};\n\ntemplate <\n    typename TableKey,\n    typename Hasher,\n    typename KeyEqual,\n    typename ArgKey>\nstruct EligibleForHeterogeneousFind\n    : Conjunction<\n          is_transparent<Hasher>,\n          is_transparent<KeyEqual>,\n          is_invocable<Hasher, ArgKey const&>,\n          is_invocable<KeyEqual, ArgKey const&, TableKey const&>> {};\n\ntemplate <\n    typename TableKey,\n    typename Hasher,\n    typename KeyEqual,\n    typename ArgKey>\nusing EligibleForHeterogeneousInsert = Conjunction<\n    EligibleForHeterogeneousFind<TableKey, Hasher, KeyEqual, ArgKey>,\n    std::is_constructible<TableKey, ArgKey>>;\n\ntemplate <typename T, typename OtherT, typename Enable = void>\nstruct HasCompatibleTest : std::false_type {};\n\ntemplate <typename T, typename OtherT>\nstruct HasCompatibleTest<\n    T,\n    OtherT,\n    void_t<typename T::template is_compatible<OtherT>>> : std::true_type {};\n\n} // namespace detail\n\ntemplate <typename T>\nstruct HeterogeneousAccessEqualTo<\n    T,\n    std::enable_if_t<detail::TransparentlyConvertibleToRange<T>::value>>\n    : detail::TransparentRangeEqualTo<\n          typename detail::ValueTypeForTransparentConversionToRange<T>::type> {\n};\n\ntemplate <typename T>\nstruct HeterogeneousAccessHash<\n    T,\n    std::enable_if_t<detail::TransparentlyConvertibleToRange<T>::value>>\n    : detail::TransparentRangeHash<\n          typename detail::ValueTypeForTransparentConversionToRange<T>::type> {\n};\n\ntemplate <typename Hash1, typename Hash2, typename Enable = void>\nstruct HeterogeneousPreHashCompatible : std::is_same<Hash1, Hash2> {};\n\ntemplate <typename Hash1, typename Hash2>\nstruct HeterogeneousPreHashCompatible<\n    Hash1,\n    Hash2,\n    std::enable_if_t<\n        detail::HasCompatibleTest<Hash1, Hash2>::value &&\n        detail::HasCompatibleTest<Hash2, Hash1>::value>>\n    : Disjunction<\n          typename Hash1::template is_compatible<Hash2>,\n          typename Hash2::template is_compatible<Hash1>> {};\n\ntemplate <typename Hash1, typename Hash2>\nstruct HeterogeneousPreHashCompatible<\n    Hash1,\n    Hash2,\n    std::enable_if_t<\n        detail::HasCompatibleTest<Hash1, Hash2>::value &&\n        !detail::HasCompatibleTest<Hash2, Hash1>::value>>\n    : Hash1::template is_compatible<Hash2> {};\n\ntemplate <typename Hash1, typename Hash2>\nstruct HeterogeneousPreHashCompatible<\n    Hash1,\n    Hash2,\n    std::enable_if_t<\n        !detail::HasCompatibleTest<Hash1, Hash2>::value &&\n        detail::HasCompatibleTest<Hash2, Hash1>::value>>\n    : Hash2::template is_compatible<Hash1> {};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/IntrusiveHeap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Skew heap [1] implementation using top-down meld.\n *\n * [1] D.D. Sleator, R.E. Tarjan, Self-Adjusting Heaps,\n * SIAM Journal of Computing, 15(1): 52-69, 1986.\n * http://www.cs.cmu.edu/~sleator/papers/adjusting-heaps.pdf\n */\n\n#pragma once\n\n#include <functional>\n#include <utility>\n\n#include <boost/intrusive/parent_from_member.hpp>\n#include <boost/noncopyable.hpp>\n#include <glog/logging.h>\n#include <folly/Portability.h>\n#include <folly/lang/Builtin.h>\n\nnamespace folly {\n\n/**\n * Base class for items to be inserted into IntrusiveHeap<..., Tag> storing\n * pointers for internal use.\n */\ntemplate <class Tag = void>\nclass IntrusiveHeapNode : private boost::noncopyable {\n public:\n  bool isLinked() const { return parent_ != kUnlinked; }\n\n private:\n  template <class, class, class, class>\n  friend class IntrusiveHeap;\n  friend class IntrusiveHeapTest;\n\n  static IntrusiveHeapNode* const kUnlinked;\n\n  /**\n   * If this is in a heap, (parent_ == nullptr) <=> (this == heap_.root_).\n   * Otherwise, parent_ is kUnlinked.\n   */\n  IntrusiveHeapNode* parent_ = kUnlinked;\n\n  /**\n   * If this is in a heap, left_ and right_ point to subheaps or nullptr.\n   * Otherwise, these are undefined.\n   */\n  IntrusiveHeapNode* left_;\n  IntrusiveHeapNode* right_;\n};\n\ntemplate <class Tag>\nIntrusiveHeapNode<Tag>* const IntrusiveHeapNode<Tag>::kUnlinked =\n    reinterpret_cast<IntrusiveHeapNode*>(1);\n\ntemplate <class T, class Tag>\nstruct DerivedNodeTraits {\n  static IntrusiveHeapNode<Tag>* asNode(T* x) { return x; }\n  static T* asT(IntrusiveHeapNode<Tag>* n) { return static_cast<T*>(n); }\n};\n\ntemplate <class T, class Tag, IntrusiveHeapNode<Tag> T::* PtrToMember>\nstruct MemberNodeTraits {\n  static IntrusiveHeapNode<Tag>* asNode(T* x) { return &(x->*PtrToMember); }\n  static T* asT(IntrusiveHeapNode<Tag>* n) {\n    return boost::intrusive::get_parent_from_member(n, PtrToMember);\n  }\n};\n\n/**\n * IntrusiveHeap implements a skew heap with intrusive pointers to provide\n * O(log(n)) operations on any node in the heap with no separately allocated\n * node type.\n *\n * - To be inserted into an IntrusiveHeap<T, Compare, Tag>, T must inherit from\n *   IntrusiveHeapNode<Tag>, or have a member of type IntrusiveHeapNode<Tag> and\n *   use MemberNodeTraits.\n *\n * - An instance of T may only be included in one IntrusiveHeap for each Tag\n *   type. It may be included in more than one IntrusiveHeap by inheriting from\n *   IntrusiveHeapNode again with a different tag type, or by using composition\n *   with different members.\n */\ntemplate <\n    class T,\n    class Compare = std::less<>,\n    class Tag = void,\n    class NodeTraitsType = DerivedNodeTraits<T, Tag>>\nclass IntrusiveHeap {\n public:\n  using Node = IntrusiveHeapNode<Tag>;\n  using NodeTraits = NodeTraitsType;\n  using Value = T;\n\n  IntrusiveHeap() {}\n\n  IntrusiveHeap(const IntrusiveHeap&) = delete;\n  IntrusiveHeap& operator=(const IntrusiveHeap&) = delete;\n\n  IntrusiveHeap(IntrusiveHeap&& other) noexcept\n      : root_(std::exchange(other.root_, nullptr)) {}\n  IntrusiveHeap& operator=(IntrusiveHeap&&) = delete;\n\n  /**\n   * Returns a pointer to the maximum value in the heap, or nullptr if the heap\n   * is empty.\n   */\n  T* top() const { return root_ != nullptr ? asT(root_) : nullptr; }\n\n  bool empty() const { return root_ == nullptr; }\n\n  /**\n   * Removes the maximum value from the heap and returns it, or nullptr if the\n   * heap is empty.\n   */\n  T* pop() {\n    if (root_ == nullptr) {\n      return nullptr;\n    }\n    Node* top = root_;\n    merge(top->left_, top->right_, nullptr, &root_);\n    top->parent_ = Node::kUnlinked;\n    return asT(top);\n  }\n\n  /**\n   * Visits all items in the heap.\n   */\n  template <class Visitor>\n  void visit(const Visitor& visitor) const {\n    visit(visitor, root_);\n  }\n\n  /**\n   * Updates the heap to reflect a change in a given value.\n   */\n  void update(T* x) {\n    erase(x);\n    push(x);\n  }\n\n  void push(T* x) {\n    DCHECK(x);\n    auto n = asNode(x);\n    DCHECK(!n->isLinked());\n    n->parent_ = nullptr;\n    n->left_ = nullptr;\n    n->right_ = nullptr;\n    merge(n, root_, nullptr, &root_);\n  }\n\n  void erase(T* x) {\n    auto n = asNode(x);\n    DCHECK(n->isLinked());\n    DCHECK(contains(x));\n    auto parent = n->parent_;\n    Node** out;\n    if (parent == nullptr) {\n      out = &root_;\n    } else if (parent->left_ == n) {\n      out = &parent->left_;\n    } else {\n      DCHECK_EQ(parent->right_, n);\n      out = &parent->right_;\n    }\n\n    merge(n->left_, n->right_, parent, out);\n    n->parent_ = Node::kUnlinked;\n  }\n\n  /**\n   * Check whether this node is included in this heap. Primarily meant for\n   * assertions, as containment should be externally tracked.\n   */\n  bool contains(const T* x) const {\n    DCHECK(x);\n    auto n = asNode(x);\n    while (n->parent_) {\n      n = n->parent_;\n    }\n    return n == root_;\n  }\n\n  /**\n   * Moves the contents of other into *this.\n   */\n  void merge(IntrusiveHeap other) {\n    merge(root_, other.root_, nullptr, &root_);\n  }\n\n private:\n  friend class IntrusiveHeapTest;\n\n  template <class Visitor>\n  static void visit(const Visitor& visitor, Node* x) {\n    for (; x != nullptr; x = x->right_) {\n      visitor(asT(x));\n      visit(visitor, x->left_);\n    }\n  }\n\n  /**\n   * Merges two subtrees, assigns a parent, populates *out with new subtree.\n   */\n  FOLLY_ALWAYS_INLINE static void merge(\n      Node* a, Node* b, Node* parent, Node** out) {\n    DCHECK(out);\n    if (a == nullptr || b == nullptr) {\n      *out = a ? a : b;\n      if (*out) {\n        (*out)->parent_ = parent;\n      }\n      return;\n    }\n    do {\n      Node* grandparent = parent;\n      if (FOLLY_BUILTIN_UNPREDICTABLE(compare(a, b))) {\n        parent = b;\n      } else {\n        parent = a;\n        a = b;\n      }\n      b = parent->right_;\n      *out = parent;\n      out = &parent->left_;\n      parent->right_ = parent->left_;\n      parent->parent_ = grandparent;\n      DCHECK(a);\n    } while (b != nullptr);\n    *out = a;\n    a->parent_ = parent;\n  }\n\n  static Node* asNode(T* x) { return NodeTraits::asNode(x); }\n\n  static const Node* asNode(const T* x) {\n    return NodeTraits::asNode(const_cast<T*>(x));\n  }\n\n  static T* asT(Node* n) { return NodeTraits::asT(n); }\n\n  static const T* asT(const Node* n) {\n    return NodeTraits::asT(const_cast<Node*>(n));\n  }\n\n  static bool compare(const Node* a, const Node* b) {\n    return Compare()(*asT(a), *asT(b));\n  }\n\n  Node* root_ = nullptr;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/IntrusiveList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n/*\n * This file contains convenience aliases that make boost::intrusive::list\n * easier to use.\n */\n\n#include <boost/intrusive/list.hpp>\n\nnamespace folly {\n\n/**\n * An auto-unlink intrusive list hook.\n */\nusing IntrusiveListHook = boost::intrusive::list_member_hook<\n    boost::intrusive::link_mode<boost::intrusive::auto_unlink>>;\n\n/**\n * An intrusive list.\n *\n * An IntrusiveList always uses an auto-unlink hook.\n * Beware that IntrusiveList::size() is an O(n) operation, since it has to walk\n * the entire list.\n *\n * Example usage:\n *\n *   class Foo {\n *     // Note that the listHook member variable needs to be visible\n *     // to the code that defines the IntrusiveList instantiation.\n *     // The list hook can be made public, or you can make the other class a\n *     // friend.\n *     IntrusiveListHook listHook;\n *   };\n *\n *   using FooList = IntrusiveList<Foo, &Foo::listHook>;\n *\n *   Foo *foo = new Foo();\n *   FooList myList;\n *   myList.push_back(*foo);\n *\n * Note that each IntrusiveListHook can only be part of a single list at any\n * given time.  If you need the same object to be stored in two lists at once,\n * you need to use two different IntrusiveListHook member variables.\n *\n * The elements stored in the list must contain an IntrusiveListHook member\n * variable.\n */\ntemplate <typename T, IntrusiveListHook T::* PtrToMember>\nusing IntrusiveList = boost::intrusive::list<\n    T,\n    boost::intrusive::member_hook<T, IntrusiveListHook, PtrToMember>,\n    boost::intrusive::constant_time_size<false>>;\n\n/**\n * A safe-link intrusive list hook.\n */\nusing SafeIntrusiveListHook = boost::intrusive::list_member_hook<\n    boost::intrusive::link_mode<boost::intrusive::safe_link>>;\n\n/**\n * A safe intrusive list.\n *\n * This is like IntrusiveList but always uses the safe-link hook which ensures\n * that the hook is initialised to an unlinked state on construction and reset\n * an unlinked state upon removing it from a list.\n */\ntemplate <typename T, SafeIntrusiveListHook T::* PtrToMember>\nusing SafeIntrusiveList = boost::intrusive::list<\n    T,\n    boost::intrusive::member_hook<T, SafeIntrusiveListHook, PtrToMember>,\n    boost::intrusive::constant_time_size<false>>;\n\n/**\n * An intrusive list with const-time size() method.\n *\n * A CountedIntrusiveList always uses a safe-link hook.\n * CountedIntrusiveList::size() is an O(1) operation. Users of this type\n * of lists need to remove a member from a list by calling one of the\n * methods on the list (e.g., erase(), pop_front(), etc.), rather than\n * calling unlink on the member's list hook. Given references to a\n * list and a member, a constant-time removal operation can be\n * accomplished by list.erase(list.iterator_to(member)). Also, when a\n * member is destroyed, it is NOT automatically removed from the list.\n *\n * Example usage:\n *\n *   class Foo {\n *     // Note that the listHook member variable needs to be visible\n *     // to the code that defines the CountedIntrusiveList instantiation.\n *     // The list hook can be made public, or you can make the other class a\n *     // friend.\n *     SafeIntrusiveListHook listHook;\n *   };\n *\n *   using FooList = CountedIntrusiveList<Foo, &Foo::listHook> FooList;\n *\n *   Foo *foo = new Foo();\n *   FooList myList;\n *   myList.push_back(*foo);\n *   myList.pop_front();\n *\n * Note that each SafeIntrusiveListHook can only be part of a single list at any\n * given time.  If you need the same object to be stored in two lists at once,\n * you need to use two different SafeIntrusiveListHook member variables.\n *\n * The elements stored in the list must contain an SafeIntrusiveListHook member\n * variable.\n */\ntemplate <typename T, SafeIntrusiveListHook T::* PtrToMember>\nusing CountedIntrusiveList = boost::intrusive::list<\n    T,\n    boost::intrusive::member_hook<T, SafeIntrusiveListHook, PtrToMember>,\n    boost::intrusive::constant_time_size<true>>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/Iterator.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <iterator>\n#include <memory>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/container/Access.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/RValueReferenceWrapper.h>\n\nnamespace folly {\n\n//  iterator_has_known_distance_v\n//\n//  Whether std::distance over a pair of iterators is reasonably known to give\n//  the distance without advancing the iterators or copies of them.\ntemplate <typename Iter, typename SentinelIter>\ninline constexpr bool iterator_has_known_distance_v =\n    !require_sizeof<Iter> || !require_sizeof<SentinelIter>;\ntemplate <typename Iter>\ninline constexpr bool iterator_has_known_distance_v<Iter, Iter> =\n    std::is_base_of<\n        std::random_access_iterator_tag,\n        typename std::iterator_traits<Iter>::iterator_category>::value;\n\n//  range_has_known_distance_v\n//\n//  Whether std::distance over the begin and end iterators is reasonably known\n//  to give the distance without advancing the iterators or copies of them.\n//\n//  Useful for conditionally reserving memory in advance of iterating the range.\n//\n//  Note: Many use-cases are better served by range-v3 or std::ranges.\n//\n//  Example:\n//\n//      std::vector<result_type> results;\n//      auto elems = /* some range */;\n//      auto const elemsb = folly::access::begin(elems);\n//      auto const elemse = folly::access::end(elems);\n//\n//      if constexpr (range_has_known_distance_v<decltype(elems)>) {\n//        auto const dist = std::distance(elemsb, elemse);\n//        results.reserve(static_cast<std::size_t>(dist));\n//      }\n//\n//      for (auto elemsi = elemsb; elemsi != elemsi; ++i) {\n//        results.push_back(do_work(*elemsi));\n//      }\n//      return results;\ntemplate <typename Range>\ninline constexpr bool range_has_known_distance_v =\n    iterator_has_known_distance_v<\n        invoke_result_t<access::begin_fn, Range>,\n        invoke_result_t<access::end_fn, Range>>;\n\n//  iterator_category_t\n//\n//  Extracts iterator_category from an iterator.\ntemplate <typename Iter>\nusing iterator_category_t =\n    typename std::iterator_traits<Iter>::iterator_category;\n\nnamespace detail {\n\ntemplate <typename Iter, typename Category, typename = void>\ninline constexpr bool iterator_category_matches_v_ =\n    !require_sizeof<Iter> || !require_sizeof<Category>;\ntemplate <typename Iter, typename Category>\ninline constexpr bool iterator_category_matches_v_<\n    Iter,\n    Category,\n    void_t<iterator_category_t<Iter>>> =\n    std::is_convertible<iterator_category_t<Iter>, Category>::value;\n\n} // namespace detail\n\n//  iterator_category_matches_v\n//\n//  Whether an iterator's category matches Category (std::input_iterator_tag,\n//  std::output_iterator_tag, etc). Defined for non-iterator types as well.\n//\n//  Useful for containers deduction guides implementation.\ntemplate <typename Iter, typename Category>\ninline constexpr bool iterator_category_matches_v =\n    detail::iterator_category_matches_v_<Iter, Category>;\n\n//  iterator_value_type_t\n//\n//  Extracts a value type from an iterator.\ntemplate <typename Iter>\nusing iterator_value_type_t = typename std::iterator_traits<Iter>::value_type;\n\n//  iterator_reference_type_t\n//\n//  Extracts reference from an iterator (C++20 iter_reference_t backported)\ntemplate <typename Iter>\nusing iterator_reference_t = decltype(*std::declval<Iter&>());\n\n//  iterator_key_type_t\n//\n//  Extracts a key type from an iterator, leverages the knowledge that\n//  key/value containers usually use std::pair<const K, V> as a value_type.\ntemplate <typename Iter>\nusing iterator_key_type_t =\n    remove_cvref_t<typename iterator_value_type_t<Iter>::first_type>;\n\n//  iterator_mapped_type_t\n//\n//  Extracts a mapped type from an iterator.\ntemplate <typename Iter>\nusing iterator_mapped_type_t =\n    typename iterator_value_type_t<Iter>::second_type;\n\n/**\n * Argument tuple for variadic emplace/constructor calls. Stores arguments by\n * (decayed) value. Restores original argument types with reference qualifiers\n * and adornments at unpack time to emulate perfect forwarding.\n *\n * Uses inheritance instead of a type alias to std::tuple so that emplace\n * iterators with implicit unpacking disabled can distinguish between\n * emplace_args and std::tuple parameters.\n *\n * @seealso folly::make_emplace_args\n * @seealso folly::get_emplace_arg\n */\ntemplate <typename... Args>\nstruct emplace_args : public std::tuple<std::decay_t<Args>...> {\n  using storage_type = std::tuple<std::decay_t<Args>...>;\n  using storage_type::storage_type;\n};\n\n/**\n * Pack arguments in a tuple for assignment to a folly::emplace_iterator,\n * folly::front_emplace_iterator, or folly::back_emplace_iterator. The\n * iterator's operator= will unpack the tuple and pass the unpacked arguments\n * to the container's emplace function, which in turn forwards the arguments to\n * the (multi-argument) constructor of the target class.\n *\n * Argument tuples generated with folly::make_emplace_args will be unpacked\n * before being passed to the container's emplace function, even for iterators\n * where implicit_unpack is set to false (so they will not implicitly unpack\n * std::pair or std::tuple arguments to operator=).\n *\n * Arguments are copied (lvalues) or moved (rvalues). To avoid copies and moves,\n * wrap references using std::ref(), std::cref(), and folly::rref(). Beware of\n * dangling references, especially references to temporary objects created with\n * folly::rref().\n *\n * Note that an argument pack created with folly::make_emplace_args is different\n * from an argument pack created with std::make_pair or std::make_tuple.\n * Specifically, passing a std::pair&& or std::tuple&& to an emplace iterator's\n * operator= will pass rvalue references to all fields of that tuple to the\n * container's emplace function, while passing an emplace_args&& to operator=\n * will cast those field references to the exact argument types as passed to\n * folly::make_emplace_args previously. If all arguments have been wrapped by\n * std::reference_wrappers or folly::rvalue_reference_wrappers, the result will\n * be the same as if the container's emplace function had been called directly\n * (perfect forwarding), with no temporary copies of the arguments.\n *\n * @seealso folly::rref\n *\n * @example\n *   class Widget { Widget(int, int); };\n *   std::vector<Widget> makeWidgets(const std::vector<int>& in) {\n *     std::vector<Widget> out;\n *     std::transform(\n *         in.begin(),\n *         in.end(),\n *         folly::back_emplacer(out),\n *         [](int i) { return folly::make_emplace_args(i, i); });\n *     return out;\n *   }\n */\ntemplate <typename... Args>\nemplace_args<Args...> make_emplace_args(Args&&... args) noexcept(\n    noexcept(emplace_args<Args...>(std::forward<Args>(args)...))) {\n  return emplace_args<Args...>(std::forward<Args>(args)...);\n}\n\nnamespace detail {\ntemplate <typename Arg>\ndecltype(auto) unwrap_emplace_arg(Arg&& arg) noexcept {\n  return std::forward<Arg>(arg);\n}\ntemplate <typename Arg>\ndecltype(auto) unwrap_emplace_arg(std::reference_wrapper<Arg> arg) noexcept {\n  return arg.get();\n}\ntemplate <typename Arg>\ndecltype(auto) unwrap_emplace_arg(\n    folly::rvalue_reference_wrapper<Arg> arg) noexcept {\n  return std::move(arg).get();\n}\n} // namespace detail\n\n/**\n * Getter function for unpacking a single emplace argument.\n *\n * Calling get_emplace_arg on an emplace_args rvalue reference results in\n * perfect forwarding of the original input types. A special case are\n * std::reference_wrapper and folly::rvalue_reference_wrapper objects within\n * folly::emplace_args. These are also unwrapped so that the bare reference is\n * returned.\n *\n * std::get is not a customization point in the standard library, so the\n * cleanest solution was to define our own getter function.\n */\ntemplate <size_t I, typename... Args>\ndecltype(auto) get_emplace_arg(emplace_args<Args...>&& args) noexcept {\n  using Out = std::tuple<Args...>;\n  return detail::unwrap_emplace_arg(\n      std::forward<std::tuple_element_t<I, Out>>(std::get<I>(args)));\n}\ntemplate <size_t I, typename... Args>\ndecltype(auto) get_emplace_arg(emplace_args<Args...>& args) noexcept {\n  return detail::unwrap_emplace_arg(std::get<I>(args));\n}\ntemplate <size_t I, typename... Args>\ndecltype(auto) get_emplace_arg(const emplace_args<Args...>& args) noexcept {\n  return detail::unwrap_emplace_arg(std::get<I>(args));\n}\ntemplate <size_t I, typename Args>\ndecltype(auto) get_emplace_arg(Args&& args) noexcept {\n  return std::get<I>(std::move(args));\n}\ntemplate <size_t I, typename Args>\ndecltype(auto) get_emplace_arg(Args& args) noexcept {\n  return std::get<I>(args);\n}\ntemplate <size_t I, typename Args>\ndecltype(auto) get_emplace_arg(const Args& args) noexcept {\n  return std::get<I>(args);\n}\n\nnamespace detail {\n/**\n * Emplace implementation class for folly::emplace_iterator.\n */\ntemplate <typename Container>\nstruct Emplace {\n  Emplace(Container& c, typename Container::iterator i)\n      : container(std::addressof(c)), iter(std::move(i)) {}\n  template <typename... Args>\n  void emplace(Args&&... args) {\n    iter = container->emplace(iter, std::forward<Args>(args)...);\n    ++iter;\n  }\n  Container* container;\n  typename Container::iterator iter;\n};\n\n/**\n * Emplace implementation class for folly::hint_emplace_iterator.\n */\ntemplate <typename Container>\nstruct EmplaceHint {\n  EmplaceHint(Container& c, typename Container::iterator i)\n      : container(std::addressof(c)), iter(std::move(i)) {}\n  template <typename... Args>\n  void emplace(Args&&... args) {\n    iter = container->emplace_hint(iter, std::forward<Args>(args)...);\n    ++iter;\n  }\n  Container* container;\n  typename Container::iterator iter;\n};\n\n/**\n * Emplace implementation class for folly::front_emplace_iterator.\n */\ntemplate <typename Container>\nstruct EmplaceFront {\n  explicit EmplaceFront(Container& c) : container(std::addressof(c)) {}\n  template <typename... Args>\n  void emplace(Args&&... args) {\n    container->emplace_front(std::forward<Args>(args)...);\n  }\n  Container* container;\n};\n\n/**\n * Emplace implementation class for folly::back_emplace_iterator.\n */\ntemplate <typename Container>\nstruct EmplaceBack {\n  explicit EmplaceBack(Container& c) : container(std::addressof(c)) {}\n  template <typename... Args>\n  void emplace(Args&&... args) {\n    container->emplace_back(std::forward<Args>(args)...);\n  }\n  Container* container;\n};\n\n/**\n * Generic base class and implementation of all emplace iterator classes.\n *\n * Uses the curiously recurring template pattern (CRTP) to cast `this*` to\n * `Derived*`; i.e., to implement covariant return types in a generic manner.\n */\ntemplate <typename Derived, typename EmplaceImpl, bool implicit_unpack>\nclass emplace_iterator_base;\n\n/**\n * Partial specialization of emplace_iterator_base with implicit unpacking\n * disabled.\n */\ntemplate <typename Derived, typename EmplaceImpl>\nclass emplace_iterator_base<Derived, EmplaceImpl, false>\n    : protected EmplaceImpl /* protected implementation inheritance */ {\n public:\n  // Iterator traits.\n  using iterator_category = std::output_iterator_tag;\n  using value_type = void;\n  using difference_type = void;\n  using pointer = void;\n  using reference = void;\n  using container_type =\n      std::remove_reference_t<decltype(*EmplaceImpl::container)>;\n\n  using EmplaceImpl::EmplaceImpl;\n\n  /**\n   * Canonical output operator. Forwards single argument straight to container's\n   * emplace function.\n   */\n  template <typename T>\n  Derived& operator=(T&& arg) {\n    this->emplace(std::forward<T>(arg));\n    return static_cast<Derived&>(*this);\n  }\n\n  /**\n   * Special output operator for packed arguments. Unpacks args and performs\n   * variadic call to container's emplace function.\n   */\n  template <typename... Args>\n  Derived& operator=(emplace_args<Args...>& args) {\n    return unpackAndEmplace(args, std::index_sequence_for<Args...>{});\n  }\n  template <typename... Args>\n  Derived& operator=(const emplace_args<Args...>& args) {\n    return unpackAndEmplace(args, std::index_sequence_for<Args...>{});\n  }\n  template <typename... Args>\n  Derived& operator=(emplace_args<Args...>&& args) {\n    return unpackAndEmplace(\n        std::move(args), std::index_sequence_for<Args...>{});\n  }\n\n  // No-ops.\n  Derived& operator*() { return static_cast<Derived&>(*this); }\n  Derived& operator++() { return static_cast<Derived&>(*this); }\n  Derived& operator++(int) { return static_cast<Derived&>(*this); }\n\n  // We need all of these explicit defaults because the custom operator=\n  // overloads disable implicit generation of these functions.\n  emplace_iterator_base(const emplace_iterator_base&) = default;\n  emplace_iterator_base(emplace_iterator_base&&) noexcept = default;\n  emplace_iterator_base& operator=(emplace_iterator_base&) = default;\n  emplace_iterator_base& operator=(const emplace_iterator_base&) = default;\n  emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default;\n\n protected:\n  template <typename Args, std::size_t... I>\n  Derived& unpackAndEmplace(Args& args, std::index_sequence<I...>) {\n    this->emplace(get_emplace_arg<I>(args)...);\n    return static_cast<Derived&>(*this);\n  }\n  template <typename Args, std::size_t... I>\n  Derived& unpackAndEmplace(const Args& args, std::index_sequence<I...>) {\n    this->emplace(get_emplace_arg<I>(args)...);\n    return static_cast<Derived&>(*this);\n  }\n  template <typename Args, std::size_t... I>\n  Derived& unpackAndEmplace(Args&& args, std::index_sequence<I...>) {\n    this->emplace(get_emplace_arg<I>(std::move(args))...);\n    return static_cast<Derived&>(*this);\n  }\n};\n\n/**\n * Partial specialization of emplace_iterator_base with implicit unpacking\n * enabled.\n *\n * Uses inheritance rather than SFINAE. operator= requires a single argument,\n * which makes it very tricky to use std::enable_if or similar.\n */\ntemplate <typename Derived, typename EmplaceImpl>\nclass emplace_iterator_base<Derived, EmplaceImpl, true>\n    : public emplace_iterator_base<Derived, EmplaceImpl, false> {\n private:\n  using Base = emplace_iterator_base<Derived, EmplaceImpl, false>;\n\n public:\n  using Base::Base;\n  using Base::operator=;\n\n  /**\n   * Special output operator for arguments packed into a std::pair. Unpacks\n   * the pair and performs variadic call to container's emplace function.\n   */\n  template <typename... Args>\n  Derived& operator=(std::pair<Args...>& args) {\n    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});\n  }\n  template <typename... Args>\n  Derived& operator=(const std::pair<Args...>& args) {\n    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});\n  }\n  template <typename... Args>\n  Derived& operator=(std::pair<Args...>&& args) {\n    return this->unpackAndEmplace(\n        std::move(args), std::index_sequence_for<Args...>{});\n  }\n\n  /**\n   * Special output operator for arguments packed into a std::tuple. Unpacks\n   * the tuple and performs variadic call to container's emplace function.\n   */\n  template <typename... Args>\n  Derived& operator=(std::tuple<Args...>& args) {\n    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});\n  }\n  template <typename... Args>\n  Derived& operator=(const std::tuple<Args...>& args) {\n    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});\n  }\n  template <typename... Args>\n  Derived& operator=(std::tuple<Args...>&& args) {\n    return this->unpackAndEmplace(\n        std::move(args), std::index_sequence_for<Args...>{});\n  }\n\n  // We need all of these explicit defaults because the custom operator=\n  // overloads disable implicit generation of these functions.\n  emplace_iterator_base(const emplace_iterator_base&) = default;\n  emplace_iterator_base(emplace_iterator_base&&) noexcept = default;\n  emplace_iterator_base& operator=(emplace_iterator_base&) = default;\n  emplace_iterator_base& operator=(const emplace_iterator_base&) = default;\n  emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default;\n};\n\n/**\n * Concrete instantiation of emplace_iterator_base. All emplace iterator\n * classes; folly::emplace_iterator, folly::hint_emplace_iterator,\n * folly::front_emplace_iterator, and folly::back_emplace_iterator; are just\n * type aliases of this class.\n *\n * It is not possible to alias emplace_iterator_base directly, because type\n * aliases cannot be used for CRTP.\n */\ntemplate <\n    template <typename> class EmplaceImplT,\n    typename Container,\n    bool implicit_unpack>\nclass emplace_iterator_impl\n    : public emplace_iterator_base<\n          emplace_iterator_impl<EmplaceImplT, Container, implicit_unpack>,\n          EmplaceImplT<Container>,\n          implicit_unpack> {\n private:\n  using Base = emplace_iterator_base<\n      emplace_iterator_impl,\n      EmplaceImplT<Container>,\n      implicit_unpack>;\n\n public:\n  using Base::Base;\n  using Base::operator=;\n\n  // We need all of these explicit defaults because the custom operator=\n  // overloads disable implicit generation of these functions.\n  emplace_iterator_impl(const emplace_iterator_impl&) = default;\n  emplace_iterator_impl(emplace_iterator_impl&&) noexcept = default;\n  emplace_iterator_impl& operator=(emplace_iterator_impl&) = default;\n  emplace_iterator_impl& operator=(const emplace_iterator_impl&) = default;\n  emplace_iterator_impl& operator=(emplace_iterator_impl&&) noexcept = default;\n};\n} // namespace detail\n\n/**\n * Behaves just like std::insert_iterator except that it calls emplace()\n * instead of insert(). Uses perfect forwarding.\n */\ntemplate <typename Container, bool implicit_unpack = true>\nusing emplace_iterator =\n    detail::emplace_iterator_impl<detail::Emplace, Container, implicit_unpack>;\n\n/**\n * Behaves just like std::insert_iterator except that it calls emplace_hint()\n * instead of insert(). Uses perfect forwarding.\n */\ntemplate <typename Container, bool implicit_unpack = true>\nusing hint_emplace_iterator = detail::\n    emplace_iterator_impl<detail::EmplaceHint, Container, implicit_unpack>;\n\n/**\n * Behaves just like std::front_insert_iterator except that it calls\n * emplace_front() instead of insert(). Uses perfect forwarding.\n */\ntemplate <typename Container, bool implicit_unpack = true>\nusing front_emplace_iterator = detail::\n    emplace_iterator_impl<detail::EmplaceFront, Container, implicit_unpack>;\n\n/**\n * Behaves just like std::back_insert_iterator except that it calls\n * emplace_back() instead of insert(). Uses perfect forwarding.\n */\ntemplate <typename Container, bool implicit_unpack = true>\nusing back_emplace_iterator = detail::\n    emplace_iterator_impl<detail::EmplaceBack, Container, implicit_unpack>;\n\n/**\n * Convenience function to construct a folly::emplace_iterator, analogous to\n * std::inserter().\n *\n * Setting implicit_unpack to false will disable implicit unpacking of\n * single std::pair and std::tuple arguments to the iterator's operator=. That\n * may be desirable in case of constructors that expect a std::pair or\n * std::tuple argument.\n */\ntemplate <bool implicit_unpack = true, typename Container>\nemplace_iterator<Container, implicit_unpack> emplacer(\n    Container& c, typename Container::iterator i) {\n  return emplace_iterator<Container, implicit_unpack>(c, std::move(i));\n}\n\n/**\n * Convenience function to construct a folly::hint_emplace_iterator, analogous\n * to std::inserter().\n *\n * Setting implicit_unpack to false will disable implicit unpacking of\n * single std::pair and std::tuple arguments to the iterator's operator=. That\n * may be desirable in case of constructors that expect a std::pair or\n * std::tuple argument.\n */\ntemplate <bool implicit_unpack = true, typename Container>\nhint_emplace_iterator<Container, implicit_unpack> hint_emplacer(\n    Container& c, typename Container::iterator i) {\n  return hint_emplace_iterator<Container, implicit_unpack>(c, std::move(i));\n}\n\n/**\n * Convenience function to construct a folly::front_emplace_iterator, analogous\n * to std::front_inserter().\n *\n * Setting implicit_unpack to false will disable implicit unpacking of\n * single std::pair and std::tuple arguments to the iterator's operator=. That\n * may be desirable in case of constructors that expect a std::pair or\n * std::tuple argument.\n */\ntemplate <bool implicit_unpack = true, typename Container>\nfront_emplace_iterator<Container, implicit_unpack> front_emplacer(\n    Container& c) {\n  return front_emplace_iterator<Container, implicit_unpack>(c);\n}\n\n/**\n * Convenience function to construct a folly::back_emplace_iterator, analogous\n * to std::back_inserter().\n *\n * Setting implicit_unpack to false will disable implicit unpacking of\n * single std::pair and std::tuple arguments to the iterator's operator=. That\n * may be desirable in case of constructors that expect a std::pair or\n * std::tuple argument.\n */\ntemplate <bool implicit_unpack = true, typename Container>\nback_emplace_iterator<Container, implicit_unpack> back_emplacer(Container& c) {\n  return back_emplace_iterator<Container, implicit_unpack>(c);\n}\n\nnamespace detail {\n\n// An accepted way to make operator-> work\n// https://quuxplusone.github.io/blog/2019/02/06/arrow-proxy/\ntemplate <typename Ref>\nstruct arrow_proxy {\n  Ref res;\n  Ref* operator->() { return &res; }\n\n  explicit arrow_proxy(Ref* ref) : res(*ref) {}\n};\n\nstruct index_iterator_access_at {\n  template <typename Container, typename Index>\n  constexpr decltype(auto) operator()(Container& container, Index index) const {\n    if constexpr (folly::is_tag_invocable_v<\n                      index_iterator_access_at,\n                      Container&,\n                      Index>) {\n      return folly::tag_invoke(*this, container, index);\n    } else {\n      return container[index];\n    }\n  }\n};\n\n} // namespace detail\n\nFOLLY_DEFINE_CPO(detail::index_iterator_access_at, index_iterator_access_at)\n\n/**\n * index_iterator\n *\n * An iterator class for random access data structures that provide an\n * access by index via `operator[](size_type)`.\n *\n * Requires a `value_type` defined in a container (we cannot\n * get the value type from reference).\n *\n * Example:\n *  class Container {\n *   public:\n *    using value_type = <*>;  // we need value_type to be defined.\n *    using iterator = folly::index_iterator<Container>;\n *    using const_iterator = folly::index_iterator<const Container>;\n *    using reverse_iterator = std::reverse_iterator<iterator>;\n *    using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n *\n *    some_ref_type  operator[](std::size_t index);\n *    some_cref_type operator[](std::size_t index) const;\n *   ...\n *  };\n *\n *  Note that `some_ref_type` can be any proxy reference, as long as the\n *  algorithms support that (for example from range-v3).\n *\n * NOTE: if `operator[]` doesn't work for you for some reason\n *       you can specify:\n *\n * ```\n *  friend some_ref_type tag_invoke(\n *    folly::cpo_t<index_iterator_access_at>,\n *    Container& c,\n *    std::size_t index);\n *\n *  friend some_cref_type tag_invoke(\n *    folly::cpo_t<index_iterator_access_at>,\n *    const Container& c,\n *    std::size_t index);\n * ```\n **/\n\ntemplate <typename Container>\nclass index_iterator {\n  template <typename T>\n  using get_size_type_t = typename std::remove_cv_t<T>::size_type;\n\n  template <typename T>\n  using get_difference_type_t = typename std::remove_cv_t<T>::difference_type;\n\n  template <typename IndexType>\n  constexpr static decltype(auto) get_reference_by_index(\n      Container& container, IndexType index) {\n    return index_iterator_access_at(container, index);\n  }\n\n public:\n  // index iterator specific types\n\n  using container_type = Container;\n  using size_type = detected_or_t<std::size_t, get_size_type_t, Container>;\n\n  // iterator types\n\n  using value_type = typename std::remove_const_t<container_type>::value_type;\n  using iterator_category = std::random_access_iterator_tag;\n  using reference = decltype(get_reference_by_index(\n      FOLLY_DECLVAL(container_type&), size_type{}));\n  using difference_type =\n      detected_or_t<std::ptrdiff_t, get_difference_type_t, Container>;\n\n  using pointer = std::conditional_t<\n      std::is_reference<reference>::value,\n      std::remove_reference_t<reference>*,\n      detail::arrow_proxy<reference>>;\n\n  static_assert(\n      std::is_signed<difference_type>::value, \"difference_type must be signed\");\n\n  // accessors\n\n  // instance of `index_iterator_accessor`\n  container_type* get_container() const { return container_; }\n  difference_type get_index() const { return index_; }\n\n  constexpr index_iterator() = default;\n\n  constexpr index_iterator(container_type& container, size_type index)\n      : container_(&container), index_(index) {}\n\n  // converting constructors --\n\n  template <\n      typename OtherContainer,\n      typename = std::enable_if_t<\n          std::is_same<std::remove_const_t<container_type>, OtherContainer>::\n              value &&\n          std::is_const<container_type>::value>>\n  /* implicit */ constexpr index_iterator(index_iterator<OtherContainer> other)\n      : container_(other.get_container()), index_(other.get_index()) {}\n\n  // access ---\n\n  constexpr reference operator*() const {\n    return get_reference_by_index(*container_, index_);\n  }\n\n  pointer operator->() const {\n    // It's equivalent to pointer{&**this} but compiler stops\n    // compilation on taking an address of a temporary.\n    // In this case `arrow_proxy` will copy the temporary and there is no\n    // issue.\n    auto&& ref = **this;\n    pointer res{&ref};\n    return res;\n  }\n\n  constexpr reference operator[](difference_type n) const {\n    return *(*this + n);\n  }\n\n  // operator++/--\n\n  constexpr index_iterator& operator++() {\n    ++index_;\n    return *this;\n  }\n\n  constexpr index_iterator operator++(int) {\n    auto tmp = *this;\n    ++*this;\n    return tmp;\n  }\n\n  constexpr index_iterator& operator--() {\n    --index_;\n    return *this;\n  }\n\n  constexpr index_iterator operator--(int) {\n    auto tmp = *this;\n    --*this;\n    return tmp;\n  }\n\n  // operator+/-\n\n  constexpr index_iterator& operator+=(difference_type n) {\n    auto signed_index = static_cast<difference_type>(index_) + n;\n    index_ = static_cast<size_type>(signed_index);\n    return *this;\n  }\n\n  constexpr index_iterator& operator-=(difference_type n) {\n    index_ += -n;\n    return *this;\n  }\n\n  constexpr friend index_iterator operator+(\n      index_iterator x, difference_type n) {\n    return x += n;\n  }\n\n  constexpr friend index_iterator operator+(\n      difference_type n, index_iterator x) {\n    return x + n;\n  }\n\n  constexpr friend index_iterator operator-(\n      index_iterator x, difference_type n) {\n    return x -= n;\n  }\n\n  constexpr friend difference_type operator-(\n      index_iterator x, index_iterator y) {\n    assert(x.container_ == y.container_);\n    return static_cast<difference_type>(x.index_) -\n        static_cast<difference_type>(y.index_);\n  }\n\n  // comparisons\n  friend constexpr bool operator==(\n      const index_iterator& x, const index_iterator& y) {\n    assert(x.container_ == y.container_);\n    return x.index_ == y.index_;\n  }\n\n  friend constexpr bool operator!=(\n      const index_iterator& x, const index_iterator& y) {\n    return !(x == y);\n  }\n\n  friend constexpr bool operator<(\n      const index_iterator& x, const index_iterator& y) {\n    assert(x.container_ == y.container_);\n    return x.index_ < y.index_;\n  }\n\n  friend constexpr bool operator<=(\n      const index_iterator& x, const index_iterator& y) {\n    return !(y < x);\n  }\n\n  friend constexpr bool operator>=(\n      const index_iterator& x, const index_iterator& y) {\n    return !(x < y);\n  }\n\n  friend constexpr bool operator>(\n      const index_iterator& x, const index_iterator& y) {\n    return y < x;\n  }\n\n private:\n  container_type* container_ = nullptr;\n  size_type index_ = 0;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/MapUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * MapUtil provides convenience functions to get a value from a map.\n *\n * @refcode folly/docs/examples/folly/MapUtil.cpp\n * @file MapUtil.h\n */\n\n#pragma once\n\n#include <tuple>\n\n#include <fmt/format.h>\n\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\n\n/**\n * Given a map and a key, return the value corresponding to the key in the map,\n * or a given default value if the key doesn't exist in the map.\n */\ntemplate <typename Map, typename Key = typename Map::key_type>\ntypename Map::mapped_type get_default(const Map& map, const Key& key) {\n  auto pos = map.find(key);\n  return (pos != map.end()) ? (pos->second) : (typename Map::mapped_type{});\n}\ntemplate <\n    class Map,\n    typename Key = typename Map::key_type,\n    typename Value = typename Map::mapped_type,\n    typename std::enable_if<!is_invocable_v<Value>>::type* = nullptr>\ntypename Map::mapped_type get_default(\n    const Map& map, const Key& key, Value&& dflt) {\n  using M = typename Map::mapped_type;\n  auto pos = map.find(key);\n  return (pos != map.end())\n      ? pos->second\n      : static_cast<M>(static_cast<Value&&>(dflt));\n}\n\n/**\n * Give a map and a key, return the value corresponding to the key in the map,\n * or a given default value if the key doesn't exist in the map.\n */\ntemplate <\n    class Map,\n    typename Key = typename Map::key_type,\n    typename Func,\n    typename = typename std::enable_if<\n        is_invocable_r_v<typename Map::mapped_type, Func>>::type>\ntypename Map::mapped_type get_default(\n    const Map& map, const Key& key, Func&& dflt) {\n  auto pos = map.find(key);\n  return pos != map.end() ? pos->second : dflt();\n}\n\n/**\n * Given a map and a key, return the value corresponding to the key in the map,\n * or throw an exception of the specified type.\n */\ntemplate <\n    class E = std::out_of_range,\n    class Map,\n    typename Key = typename Map::key_type>\nconst typename Map::mapped_type& get_or_throw(\n    const Map& map,\n    const Key& key,\n    const StringPiece& exceptionStrPrefix = StringPiece()) {\n  auto pos = map.find(key);\n  if (pos != map.end()) {\n    return pos->second;\n  }\n  throw_exception<E>(fmt::format(\"{}{}\", exceptionStrPrefix, key));\n}\n\ntemplate <\n    class E = std::out_of_range,\n    class Map,\n    typename Key = typename Map::key_type>\ntypename Map::mapped_type& get_or_throw(\n    Map& map,\n    const Key& key,\n    const StringPiece& exceptionStrPrefix = StringPiece()) {\n  auto pos = map.find(key);\n  if (pos != map.end()) {\n    return pos->second;\n  }\n  throw_exception<E>(fmt::format(\"{}{}\", exceptionStrPrefix, key));\n}\n\n/**\n * Given a map and a key, return a Optional<V> if the key exists and None if the\n * key does not exist in the map.\n */\ntemplate <\n    template <typename> class Optional = folly::Optional,\n    class Map,\n    typename Key = typename Map::key_type>\nOptional<typename Map::mapped_type> get_optional(\n    const Map& map, const Key& key) {\n  auto pos = map.find(key);\n  if (pos != map.end()) {\n    return Optional<typename Map::mapped_type>(pos->second);\n  } else {\n    return {};\n  }\n}\n\n/**\n * Given a map and a key, return a OptionalValue if the key exists and None if\n * the key does not exist in the map.\n */\ntemplate <class OptionalValue, class Map, typename Key = typename Map::key_type>\nOptionalValue get_optional(const Map& map, const Key& key) {\n  auto pos = map.find(key);\n  if (pos != map.end()) {\n    return OptionalValue(pos->second);\n  } else {\n    return {};\n  }\n}\n\n/**\n * Given a map and a key, return a reference to the value corresponding to the\n * key in the map, or the given default reference if the key doesn't exist in\n * the map.\n */\ntemplate <class Map, typename Key = typename Map::key_type>\nconst typename Map::mapped_type& get_ref_default(\n    const Map& map, const Key& key, const typename Map::mapped_type& dflt) {\n  auto pos = map.find(key);\n  return (pos != map.end() ? pos->second : dflt);\n}\n\n/**\n * Passing a temporary default value returns a dangling reference when it is\n * returned. Lifetime extension is broken by the indirection.\n * The caller must ensure that the default value outlives the reference returned\n * by get_ref_default().\n */\ntemplate <class Map, typename Key = typename Map::key_type>\nconst typename Map::mapped_type& get_ref_default(\n    const Map& map, const Key& key, typename Map::mapped_type&& dflt) = delete;\n\ntemplate <class Map, typename Key = typename Map::key_type>\nconst typename Map::mapped_type& get_ref_default(\n    const Map& map, const Key& key, const typename Map::mapped_type&& dflt) =\n    delete;\n\n/**\n * Given a map and a key, return a reference to the value corresponding to the\n * key in the map, or the given default reference if the key doesn't exist in\n * the map.\n */\ntemplate <\n    class Map,\n    typename Key = typename Map::key_type,\n    typename Func,\n    typename = typename std::enable_if<\n        is_invocable_r_v<const typename Map::mapped_type&, Func>>::type,\n    typename = typename std::enable_if<\n        std::is_reference<invoke_result_t<Func>>::value>::type>\nconst typename Map::mapped_type& get_ref_default(\n    const Map& map, const Key& key, Func&& dflt) {\n  auto pos = map.find(key);\n  return (pos != map.end() ? pos->second : dflt());\n}\n\n/**\n * @brief Given a map and a key, return a pointer to the value corresponding to\n * the key in the map, or nullptr if the key doesn't exist in the map.\n */\ntemplate <class Map, typename Key = typename Map::key_type>\nauto get_ptr(const Map& map, const Key& key) {\n  auto pos = map.find(key);\n  return (pos != map.end() ? &pos->second : nullptr);\n}\ntemplate <class Map, typename Key = typename Map::key_type>\nconst typename Map::mapped_type* FOLLY_NULLABLE\nget_ptr(const Map* FOLLY_NULLABLE map, const Key& key) {\n  return map ? get_ptr(*map, key) : nullptr;\n}\n\n/**\n * Non-const overload of the above.\n */\ntemplate <class Map, typename Key = typename Map::key_type>\nauto get_ptr(Map& map, const Key& key) {\n  auto pos = map.find(key);\n  return (pos != map.end() ? &pos->second : nullptr);\n}\n\ntemplate <class Map, typename Key = typename Map::key_type>\ntypename Map::mapped_type* FOLLY_NULLABLE\nget_ptr(Map* FOLLY_NULLABLE map, const Key& key) {\n  return map ? get_ptr(*map, key) : nullptr;\n}\n\n/**\n * Same as `get_ptr` but for `find` variants that search for two keys at once.\n */\ntemplate <class Map, typename Key = typename Map::key_type>\nstd::pair<const typename Map::mapped_type*, const typename Map::mapped_type*>\nget_ptr2(const Map& map, const Key& key0, const Key& key1) {\n  const auto& iter_pair = map.find(key0, key1);\n  auto iter0 = iter_pair.first;\n  auto iter1 = iter_pair.second;\n  auto end = map.end();\n  return std::make_pair(\n      iter0 != end ? &iter0->second : nullptr,\n      iter1 != end ? &iter1->second : nullptr);\n}\n\n/**\n * Same as `get_ptr` but for `find` variants that search for two keys at once.\n */\ntemplate <class Map, typename Key = typename Map::key_type>\nstd::pair<typename Map::mapped_type*, typename Map::mapped_type*> get_ptr2(\n    Map& map, const Key& key0, const Key& key1) {\n  const auto& iter_pair = map.find(key0, key1);\n  auto iter0 = iter_pair.first;\n  auto iter1 = iter_pair.second;\n  auto end = map.end();\n  return std::make_pair(\n      iter0 != end ? &iter0->second : nullptr,\n      iter1 != end ? &iter1->second : nullptr);\n}\n\n// TODO: Remove the return type computations when clang 3.5 and gcc 5.1 are\n// the minimum supported versions.\nnamespace detail {\ntemplate <\n    class T,\n    size_t pathLength,\n    class = typename std::enable_if<(pathLength > 0)>::type>\nstruct NestedMapType {\n  using type =\n      typename NestedMapType<std::remove_pointer_t<T>, pathLength - 1>::type::\n          mapped_type;\n};\n\ntemplate <class T>\nstruct NestedMapType<T, 1> {\n  using type = typename T::mapped_type;\n};\n\ntemplate <typename... KeysDefault>\nstruct DefaultType;\n\ntemplate <typename Default>\nstruct DefaultType<Default> {\n  using type = Default;\n};\n\ntemplate <typename Key, typename... KeysDefault>\nstruct DefaultType<Key, KeysDefault...> {\n  using type = typename DefaultType<KeysDefault...>::type;\n};\n\ntemplate <class... KeysDefault>\nauto extract_default(const KeysDefault&... keysDefault) ->\n    typename DefaultType<KeysDefault...>::type const& {\n  return std::get<sizeof...(KeysDefault) - 1>(std::tie(keysDefault...));\n}\n} // namespace detail\n\n/**\n * Given a map of maps and a path of keys, return a Optional<V> if the nested\n * key exists and None if the nested keys does not exist in the map.\n */\ntemplate <\n    template <typename> class Optional = folly::Optional,\n    class Map,\n    class Key1,\n    class Key2,\n    class... Keys>\nauto get_optional(\n    const Map& map, const Key1& key1, const Key2& key2, const Keys&... keys)\n    -> Optional<\n        typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type> {\n  auto pos = map.find(key1);\n  if (pos != map.end()) {\n    return get_optional<Optional>(pos->second, key2, keys...);\n  } else {\n    return {};\n  }\n}\n\n/**\n * Given a map of maps and a path of keys, return a pointer to the nested value,\n * or nullptr if the key doesn't exist in the map.\n */\ntemplate <class Map, class Key1, class Key2, class... Keys>\nauto get_ptr(\n    const Map& map, const Key1& key1, const Key2& key2, const Keys&... keys) ->\n    typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type\n    const* FOLLY_NULLABLE {\n  auto pos = map.find(key1);\n  return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr;\n}\n\ntemplate <class Map, class Key1, class Key2, class... Keys>\nauto get_ptr(\n    const Map* FOLLY_NULLABLE map,\n    const Key1& key1,\n    const Key2& key2,\n    const Keys&... keys) ->\n    typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::type\n    const* FOLLY_NULLABLE {\n  return map ? get_ptr(*map, key1, key2, keys...) : nullptr;\n}\n\ntemplate <class Map, class Key1, class Key2, class... Keys>\nauto get_ptr(Map& map, const Key1& key1, const Key2& key2, const Keys&... keys)\n    -> typename detail::NestedMapType<Map, 2 + sizeof...(Keys)>::\n        type* FOLLY_NULLABLE {\n  auto pos = map.find(key1);\n  return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr;\n}\n\ntemplate <class Map, class Key1, class Key2, class... Keys>\nauto get_ptr(\n    Map* FOLLY_NULLABLE map,\n    const Key1& key1,\n    const Key2& key2,\n    const Keys&... keys) -> typename detail::\n    NestedMapType<Map, 2 + sizeof...(Keys)>::type* FOLLY_NULLABLE {\n  return map ? get_ptr(*map, key1, key2, keys...) : nullptr;\n}\n\n/**\n * Given a map and a path of keys, return the value corresponding to the nested\n * value, or a given default value if the path doesn't exist in the map.\n * The default value is the last parameter, and is copied when returned.\n */\ntemplate <\n    class Map,\n    class Key1,\n    class Key2,\n    class... KeysDefault,\n    typename = typename std::enable_if<sizeof...(KeysDefault) != 0>::type>\nauto get_default(\n    const Map& map,\n    const Key1& key1,\n    const Key2& key2,\n    const KeysDefault&... keysDefault) ->\n    typename detail::NestedMapType<Map, 1 + sizeof...(KeysDefault)>::type {\n  if (const auto* ptr = get_ptr(map, key1)) {\n    return get_default(*ptr, key2, keysDefault...);\n  }\n  return detail::extract_default(keysDefault...);\n}\n\n/**\n * Given a map and a path of keys, return a reference to the value corresponding\n * to the nested value, or the given default reference if the path doesn't exist\n * in the map.\n * The default value is the last parameter, and must be a lvalue reference.\n */\ntemplate <\n    class Map,\n    class Key1,\n    class Key2,\n    class... KeysDefault,\n    typename = typename std::enable_if<sizeof...(KeysDefault) != 0>::type,\n    typename = typename std::enable_if<std::is_lvalue_reference<\n        typename detail::DefaultType<KeysDefault...>::type>::value>::type>\nauto get_ref_default(\n    const Map& map,\n    const Key1& key1,\n    const Key2& key2,\n    KeysDefault&&... keysDefault) ->\n    typename detail::NestedMapType<Map, 1 + sizeof...(KeysDefault)>::type\n    const& {\n  if (const auto* ptr = get_ptr(map, key1)) {\n    return get_ref_default(*ptr, key2, keysDefault...);\n  }\n  return detail::extract_default(keysDefault...);\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/container/Merge.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * folly::merge() is an implementation of std::merge with one additional\n * guarantee: if the input ranges overlap, the order that values *from the two\n * different ranges* appear in the output is well defined (std::merge only\n * guarantees relative ordering is maintained within a single input range).\n * This semantic is very useful when the output container removes duplicates\n * (such as std::map) to guarantee that elements from b override elements from\n * a.\n *\n * ex. Let's say we have two vector<pair<int, int>> as input, and we are\n * merging into a vector<pair<int, int>>. The comparator is returns true if the\n * first argument has a lesser 'first' value in the pair.\n *\n * a = {{1, 1}, {2, 2}, {3, 3}};\n * b = {{1, 2}, {2, 3}};\n *\n * folly::merge<...>(a.begin(), a.end(), b.begin(), b.end(), outputIter) is\n * guaranteed to produce {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}}. That is,\n * if comp(it_a, it_b) == comp(it_b, it_a) == false, we first insert the element\n * from a.\n */\n\n#pragma once\n\n#include <algorithm>\n\nnamespace folly {\n\ntemplate <class InputIt1, class InputIt2, class OutputIt, class Compare>\nOutputIt merge(\n    InputIt1 first1,\n    InputIt1 last1,\n    InputIt2 first2,\n    InputIt2 last2,\n    OutputIt d_first,\n    Compare comp) {\n  for (; first1 != last1; ++d_first) {\n    if (first2 == last2) {\n      return std::copy(first1, last1, d_first);\n    }\n    if (comp(*first2, *first1)) {\n      *d_first = *first2;\n      ++first2;\n    } else {\n      *d_first = *first1;\n      ++first1;\n    }\n  }\n  return std::copy(first2, last2, d_first);\n}\n\ntemplate <class InputIt1, class InputIt2, class OutputIt>\nOutputIt merge(\n    InputIt1 first1,\n    InputIt1 last1,\n    InputIt2 first2,\n    InputIt2 last2,\n    OutputIt d_first) {\n  for (; first1 != last1; ++d_first) {\n    if (first2 == last2) {\n      return std::copy(first1, last1, d_first);\n    }\n    if (*first2 < *first1) {\n      *d_first = *first2;\n      ++first2;\n    } else {\n      *d_first = *first1;\n      ++first1;\n    }\n  }\n  return std::copy(first2, last2, d_first);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/RegexMatchCache.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/RegexMatchCache.h>\n\n#include <folly/portability/Windows.h>\n\n#include <ostream>\n\n#include <boost/regex.hpp>\n#include <fmt/format.h>\n#include <glog/logging.h>\n\n#include <folly/MapUtil.h>\n#include <folly/String.h>\n#include <folly/container/Reserve.h>\n#include <folly/synchronization/AtomicUtil.h>\n\nnamespace folly {\n\nstatic std::string quote(std::string_view const s) {\n  return fmt::format(\"\\\"{}\\\"\", cEscape<std::string>(s));\n}\n\nclass RegexMatchCache::RegexObject {\n private:\n  boost::regex object;\n\n public:\n  explicit RegexObject(std::string_view const regex)\n      : object{std::string(regex)} {}\n\n  bool operator()(std::string_view const string) const {\n    return boost::regex_match(std::string(string), object);\n  }\n};\n\nvoid RegexMatchCache::repair() noexcept {\n  stringQueueReverse_.clear();\n  stringQueueForward_.clear();\n  for (auto& [match, entry] : cacheMatchToRegex_) {\n    entry.regexes.reset();\n  }\n  cacheRegexToMatch_.clear();\n  regexVector_.clear();\n}\n\nRegexMatchCache::KeyMap::~KeyMap() = default;\n\nvoid RegexMatchCache::InspectView::print(std::ostream& o) const {\n  auto regexToString = [&](auto const& regex) {\n    return quote(keys_.lookup(regex));\n  };\n  o << \"cache-regex-to-match[\" << ref_.cacheRegexToMatch_.size()\n    << \"]:\" << std::endl;\n  for (auto const& [regex, entry] : ref_.cacheRegexToMatch_) {\n    o << \"  \" << regexToString(regex) << \":\" << std::endl;\n    for (auto const match : entry.matches) {\n      o << \"    \" << quote(*match) << std::endl;\n    }\n  }\n  o << \"cache-match-to-regex[\" << ref_.cacheMatchToRegex_.size()\n    << \"]:\" << std::endl;\n  for (auto const& [match, entry] : ref_.cacheMatchToRegex_) {\n    o << \"  \" << quote(*match) << \":\" << std::endl;\n    for (auto const regexi : entry.regexes.as_index_set_view()) {\n      auto const regex = ref_.regexVector_.value_at_index(regexi);\n      o << \"    \" << regexToString(*regex) << std::endl;\n    }\n  }\n  o << \"string-queue-forward[\" << ref_.stringQueueForward_.size()\n    << \"]:\" << std::endl;\n  for (auto const& [string, entry] : ref_.stringQueueForward_) {\n    o << \"  \" << quote(*string) << \":\" << std::endl;\n    for (auto const regexi : entry.regexes.as_index_set_view()) {\n      auto const regex = ref_.regexVector_.value_at_index(regexi);\n      o << \"    \" << regexToString(*regex) << std::endl;\n    }\n  }\n  o << \"string-queue-reverse[\" << ref_.stringQueueReverse_.size()\n    << \"]:\" << std::endl;\n  for (auto const& [regex, entry] : ref_.stringQueueReverse_) {\n    o << \"  \" << regexToString(*regex) << \":\" << std::endl;\n    for (auto const string : entry.strings) {\n      o << \"    \" << quote(*string) << std::endl;\n    }\n  }\n}\n\nstruct RegexMatchCache::ConsistencyReportMatcher::state {\n  std::unordered_map<regex_key, RegexObject> cache;\n};\n\nRegexMatchCache::ConsistencyReportMatcher::ConsistencyReportMatcher()\n    : state_{std::make_unique<state>()} {}\n\nRegexMatchCache::ConsistencyReportMatcher::~ConsistencyReportMatcher() =\n    default;\n\nbool RegexMatchCache::ConsistencyReportMatcher::match(\n    KeyMap const& keys, regex_key const regex, string_pointer const string) {\n  auto const [iter, inserted] =\n      state_->cache.try_emplace(regex, keys.lookup(regex));\n  return iter->second(*string);\n}\n\nRegexMatchCache::RegexMatchCache() noexcept = default;\n\nRegexMatchCache::~RegexMatchCache() = default;\n\nstd::vector<std::string_view> RegexMatchCache::getRegexList(\n    KeyMap const& keys) const {\n  std::vector<std::string_view> result;\n  result.reserve(cacheRegexToMatch_.size());\n  for (auto const& [regex, entry] : cacheRegexToMatch_) {\n    result.push_back(keys.lookup(regex));\n  }\n  return result;\n}\n\nstd::vector<RegexMatchCache::string_pointer> RegexMatchCache::getStringList()\n    const {\n  std::vector<string_pointer> result;\n  result.reserve(cacheMatchToRegex_.size());\n  for (auto const& [match, entry] : cacheMatchToRegex_) {\n    result.push_back(match);\n  }\n  return result;\n}\n\nvoid RegexMatchCache::consistency(\n    ConsistencyReportMatcher& matcher,\n    KeyMap const& keys,\n    FunctionRef<void(std::string)> const report) const {\n  auto const q = [](std::string_view const s) { return quote(s); };\n  auto const h = report;\n\n  auto const k = [&](regex_key const r) { return keys.lookup(r); };\n\n  if (cacheRegexToMatch_.empty() || cacheMatchToRegex_.empty()) {\n    if (!stringQueueForward_.empty()) {\n      h(\"string-queue-forward not empty\");\n    }\n    if (!stringQueueReverse_.empty()) {\n      h(\"string-queue-reverse not empty\");\n    }\n  }\n\n  //  check that caches are accurate\n  //  check that caches are bidi-consistent\n  //  check that missing cache entries are found in string-queues\n  for (auto const& [regex, rtmentry] : cacheRegexToMatch_) {\n    auto const regexs = k(regex);\n    auto const regexi = regexVector_.index_of_value(&regex);\n    for (auto const& [match, mtrentry] : cacheMatchToRegex_) {\n      auto const rtmcontains = rtmentry.matches.contains(match);\n      auto const mtrcontains = mtrentry.regexes.get_value(regexi);\n      if (rtmcontains && !mtrcontains) {\n        h(fmt::format( //\n            \"cache-regex-to-match[{}] wild {}\",\n            q(regexs),\n            q(*match)));\n      }\n      if (mtrcontains && !rtmcontains) {\n        h(fmt::format( //\n            \"cache-match-to-regex[{}] wild {}\",\n            q(*match),\n            q(regexs)));\n      }\n      auto const result = matcher.match(keys, regex, match);\n      auto const queues = result && (!rtmcontains || !mtrcontains);\n      auto const sqfptr =\n          !queues ? nullptr : get_ptr(stringQueueForward_, match);\n      auto const sqfhas = sqfptr && sqfptr->regexes.get_value(regexi);\n      auto const sqrptr =\n          !queues ? nullptr : get_ptr(stringQueueReverse_, &regex);\n      auto const sqrhas = sqrptr && sqrptr->strings.contains(match);\n      if (rtmcontains && !result) {\n        h(fmt::format( //\n            \"cache-regex-to-match[{}] wild {}\",\n            q(regexs),\n            q(*match)));\n      }\n      if (result && !rtmcontains) {\n        if (!sqfhas || !sqrhas) {\n          h(fmt::format( //\n              \"cache-regex-to-match[{}] missing {}\",\n              q(regexs),\n              q(*match)));\n        }\n      }\n      if (mtrcontains && !result) {\n        h(fmt::format( //\n            \"cache-match-to-regex[{}] wild {}\",\n            q(*match),\n            q(regexs)));\n      }\n      if (result && !mtrcontains) {\n        if (!sqfhas || !sqrhas) {\n          h(fmt::format( //\n              \"cache-match-to-regex[{}] missing {}\",\n              q(*match),\n              q(regexs)));\n        }\n      }\n    }\n  }\n\n  //  check that string-queues are bidi-consistent\n  //  check that string-queue keys are subsets of caches\n  //  check that string-queue entries are not in caches\n  for (auto const& [string, entry] : stringQueueForward_) {\n    auto const mtrptr = get_ptr(cacheMatchToRegex_, string);\n    if (!mtrptr) {\n      h(fmt::format( //\n          \"string-queue-forward has string[{}]\",\n          q(*string)));\n    }\n    for (auto const regexi : entry.regexes.as_index_set_view()) {\n      auto const regex = regexVector_.value_at_index(regexi);\n      auto const sqrptr = get_ptr(stringQueueReverse_, regex);\n      if (!sqrptr) {\n        h(fmt::format( //\n            \"string-queue-reverse none regex[{}]\",\n            q(k(*regex))));\n      } else if (!sqrptr->strings.contains(string)) {\n        h(fmt::format( //\n            \"string-queue-reverse[{}] none string[{}]\",\n            q(k(*regex)),\n            q(*string)));\n      }\n      auto const mtrhas = mtrptr && mtrptr->regexes.get_value(regexi);\n      auto const rtmptr = get_ptr(cacheRegexToMatch_, *regex);\n      auto const rtmhas = rtmptr && rtmptr->matches.count(string);\n      if (mtrhas || rtmhas) {\n        h(fmt::format( //\n            \"string-queue-forward[{}] has regex[{}]\",\n            q(*string),\n            q(k(*regex))));\n      }\n    }\n  }\n  for (auto const& [regex, entry] : stringQueueReverse_) {\n    auto const regexi = regexVector_.index_of_value(regex);\n    auto const rtmptr = get_ptr(cacheRegexToMatch_, *regex);\n    for (auto const string : entry.strings) {\n      auto const sqfptr = get_ptr(stringQueueForward_, string);\n      if (!sqfptr) {\n        h(fmt::format( //\n            \"string-queue-forward none string[{}]\",\n            q(*string)));\n      } else if (!sqfptr->regexes.get_value(regexi)) {\n        h(fmt::format( //\n            \"string-queue-forward[{}] none regex[{}]\",\n            q(*string),\n            q(k(*regex))));\n      }\n      auto const mtrptr = get_ptr(cacheMatchToRegex_, string);\n      auto const mtrhas = mtrptr && mtrptr->regexes.get_value(regexi);\n      auto const rtmhas = rtmptr && rtmptr->matches.count(string);\n      if (mtrhas || rtmhas) {\n        h(fmt::format( //\n            \"string-queue-reverse[{}] has string[{}]\",\n            q(k(*regex)),\n            q(*string)));\n      }\n    }\n  }\n}\n\nbool RegexMatchCache::hasRegex(regex_key const& regex) const noexcept {\n  return cacheRegexToMatch_.contains(regex);\n}\n\nvoid RegexMatchCache::addRegex(regex_key const& regex) {\n  auto const [rtmiter, rtminserted] = cacheRegexToMatch_.try_emplace(regex);\n  if (!rtminserted) {\n    return;\n  }\n  auto guard = makeGuard(std::bind(&RegexMatchCache::repair, this));\n  auto const regexp = &rtmiter->first;\n  auto const regexi = regexVector_.insert_value(regexp).first;\n  if (cacheMatchToRegex_.empty()) {\n    guard.dismiss();\n    return;\n  }\n  auto const [sqriter, sqrinserted] = stringQueueReverse_.try_emplace(regexp);\n  CHECK(sqrinserted) << \"string already in string-queue-reverse\";\n  auto& sqrentry = sqriter->second;\n  for (auto const& [string, mtrentry] : cacheMatchToRegex_) {\n    stringQueueForward_[string].regexes.set_value(regexi, true);\n    sqrentry.strings.insert(string);\n  }\n  guard.dismiss();\n}\n\nvoid RegexMatchCache::eraseRegex(regex_key const& regex) {\n  auto const rtmiter = cacheRegexToMatch_.find(regex);\n  if (rtmiter == cacheRegexToMatch_.end()) {\n    return;\n  }\n  auto guard = makeGuard(std::bind(&RegexMatchCache::repair, this));\n  auto const regexp = &rtmiter->first;\n  auto const regexi = regexVector_.index_of_value(regexp);\n  for (auto const match : rtmiter->second.matches) {\n    get_ptr(cacheMatchToRegex_, match)->regexes.set_value(regexi, false);\n  }\n  auto const sqriter = stringQueueReverse_.find(regexp);\n  if (sqriter != stringQueueReverse_.end()) {\n    for (auto const string : sqriter->second.strings) {\n      auto const sqfiter = stringQueueForward_.find(string);\n      CHECK(sqfiter != stringQueueForward_.end());\n      sqfiter->second.regexes.set_value(regexi, false);\n      if (sqfiter->second.regexes.as_index_set_view().empty()) {\n        stringQueueForward_.erase(sqfiter);\n      }\n    }\n    stringQueueReverse_.erase(sqriter);\n  }\n  regexVector_.erase_value(regexp);\n  cacheRegexToMatch_.erase(rtmiter);\n  guard.dismiss();\n}\n\nbool RegexMatchCache::hasString(string_pointer const string) const noexcept {\n  return //\n      cacheMatchToRegex_.contains(string) ||\n      stringQueueForward_.contains(string);\n}\n\nvoid RegexMatchCache::addString(string_pointer const string) {\n  //  return-early if already added\n  if (!cacheMatchToRegex_.try_emplace(string).second) {\n    return;\n  }\n  if (cacheRegexToMatch_.empty()) {\n    return;\n  }\n  auto guard = makeGuard(std::bind(&RegexMatchCache::repair, this));\n  auto const [sqfiter, sqfinserted] = stringQueueForward_.try_emplace(string);\n  CHECK(sqfinserted) << \"string already in string-queue-forward\";\n\n  //  add to string-queue-forward and string-queue-reverse\n  auto& sqfentry = sqfiter->second;\n  for (auto const& [regex, entry] : cacheRegexToMatch_) {\n    auto regexi = regexVector_.index_of_value(&regex);\n    sqfentry.regexes.set_value(regexi, true);\n    stringQueueReverse_[&regex].strings.insert(string);\n  }\n  guard.dismiss();\n}\n\nvoid RegexMatchCache::eraseString(string_pointer const string) {\n  auto guard = makeGuard(std::bind(&RegexMatchCache::repair, this));\n\n  //  erase from string-queue-forward and string-queue-reverse\n  auto const sqfiter = stringQueueForward_.find(string);\n  if (sqfiter != stringQueueForward_.end()) {\n    for (auto const regexi : sqfiter->second.regexes.as_index_set_view()) {\n      auto const regexp = regexVector_.value_at_index(regexi);\n      auto const sqriter = stringQueueReverse_.find(regexp);\n      sqriter->second.strings.erase(string);\n      if (sqriter->second.strings.empty()) {\n        stringQueueReverse_.erase(sqriter);\n      }\n    }\n    stringQueueForward_.erase(sqfiter);\n  }\n\n  //  erase from cache-regex-to-match and cache-match-to-regex\n  auto const mtriter = cacheMatchToRegex_.find(string);\n  if (mtriter != cacheMatchToRegex_.end()) {\n    for (auto const regexi : mtriter->second.regexes.as_index_set_view()) {\n      auto const regex = regexVector_.value_at_index(regexi);\n      get_ptr(cacheRegexToMatch_, *regex)->matches.erase(string);\n    }\n    cacheMatchToRegex_.erase(mtriter);\n  }\n\n  guard.dismiss();\n}\n\nstd::vector<std::string const*> RegexMatchCache::findMatchesUncached(\n    std::string_view const regex) const {\n  std::vector<std::string const*> result;\n  RegexObject robject{regex};\n  for (auto const& [string, _] : cacheMatchToRegex_) {\n    if (robject(*string)) {\n      result.push_back(string);\n    }\n  }\n  return result;\n}\n\nbool RegexMatchCache::isReadyToFindMatches(\n    regex_key const& regex) const noexcept {\n  auto const rtmiter = cacheRegexToMatch_.find(regex);\n  return //\n      rtmiter != cacheRegexToMatch_.end() &&\n      !stringQueueReverse_.contains(&rtmiter->first);\n}\n\nvoid RegexMatchCache::prepareToFindMatches(regex_key_and_view const& regex) {\n  auto guard = makeGuard(std::bind(&RegexMatchCache::repair, this));\n  auto const [rtmiter, inserted] = cacheRegexToMatch_.try_emplace(regex);\n  auto const regexp = &rtmiter->first;\n  auto& rtmentry = rtmiter->second;\n  auto const [regexi, rvinserted] = regexVector_.insert_value(regexp);\n  CHECK_EQ(rvinserted, inserted);\n\n  if (inserted) {\n    //  evaluate new regex over matches\n    CHECK(!stringQueueReverse_.contains(regexp));\n    if (cacheMatchToRegex_.empty()) {\n      CHECK(stringQueueForward_.empty());\n      CHECK(stringQueueReverse_.empty());\n      guard.dismiss();\n      return;\n    }\n    RegexObject robject{regex};\n    for (auto& [string, mtrentry] : cacheMatchToRegex_) {\n      if (robject(*string)) {\n        rtmentry.matches.insert(string);\n        mtrentry.regexes.set_value(regexi, true);\n      }\n    }\n  } else {\n    //  evaluate old regex over queue\n    auto const sqriter = stringQueueReverse_.find(regexp);\n    if (sqriter == stringQueueReverse_.end()) {\n      //  was actually ready-to-find-matches for regex\n      guard.dismiss();\n      return;\n    }\n    auto const strings = std::move(sqriter->second.strings);\n    CHECK(!strings.empty());\n    stringQueueReverse_.erase(sqriter);\n    RegexObject robject{regex};\n    for (auto const string : strings) {\n      auto const sqfiter = stringQueueForward_.find(string);\n      CHECK(sqfiter != stringQueueForward_.end());\n      CHECK(sqfiter->second.regexes.get_value(regexi));\n      sqfiter->second.regexes.set_value(regexi, false);\n      if (sqfiter->second.regexes.as_index_set_view().empty()) {\n        stringQueueForward_.erase(sqfiter);\n      }\n\n      auto const mtriter = cacheMatchToRegex_.find(string);\n      CHECK(mtriter != cacheMatchToRegex_.end());\n      auto& mtrentry = mtriter->second;\n      if (robject(*string)) {\n        rtmentry.matches.insert(string);\n        mtrentry.regexes.set_value(regexi, true);\n      }\n    }\n  }\n  guard.dismiss();\n}\n\nRegexMatchCache::FindMatchesUnsafeResult RegexMatchCache::findMatchesUnsafe(\n    regex_key const& regex, time_point const now) const {\n  if (kIsDebug && !isReadyToFindMatches(regex)) {\n    throw std::logic_error(\"not ready to find matches\");\n  }\n  auto const& rtmentry = cacheRegexToMatch_.at(regex);\n  atomic_fetch_modify(\n      rtmentry.accessed_at,\n      [now](auto const val) { return std::max(now, val); },\n      std::memory_order_relaxed);\n  return {rtmentry.matches};\n}\n\nstd::vector<RegexMatchCache::string_pointer> RegexMatchCache::findMatches(\n    regex_key const& regex, time_point const now) const {\n  auto const matches = findMatchesUnsafe(regex, now);\n  return {matches.begin(), matches.end()};\n}\n\nbool RegexMatchCache::hasItemsToPurge(time_point const expiry) const noexcept {\n  for (auto const& [regex, entry] : cacheRegexToMatch_) {\n    auto const accessed_at = entry.accessed_at.load(std::memory_order_relaxed);\n    if (accessed_at <= expiry) {\n      return true;\n    }\n  }\n  return false;\n}\n\nvoid RegexMatchCache::clear() {\n  std::exchange(stringQueueReverse_, {});\n  std::exchange(stringQueueForward_, {});\n  std::exchange(cacheMatchToRegex_, {});\n  std::exchange(cacheRegexToMatch_, {});\n  std::exchange(regexVector_, {});\n}\n\nvoid RegexMatchCache::purge(time_point const expiry) {\n  std::vector<regex_key> regexes;\n  for (auto const& [regex, entry] : cacheRegexToMatch_) {\n    auto const accessed_at = entry.accessed_at.load(std::memory_order_relaxed);\n    if (accessed_at <= expiry) {\n      regexes.push_back(regex);\n    }\n  }\n  for (auto const& regex : regexes) {\n    eraseRegex(regex);\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/RegexMatchCache.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cassert>\n#include <chrono>\n#include <iosfwd>\n#include <string>\n#include <string_view>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <vector>\n\n#include <folly/Chrono.h>\n#include <folly/Function.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/container/Reserve.h>\n#include <folly/hash/UniqueHashKey.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\n\n/// RegexMatchCacheDynamicBitset\n///\n/// A dynamic bitset for use within, and optimized for, RegexMatchCache.\n/// * Small, having the same size and alignment as a pointer.\n/// * Optimistically non-allocating, using in-situ storage for small bitsets.\n///\n/// Intended for use only within RegexMatchCache.\n///\n/// Incomplete as a generic container.\nclass RegexMatchCacheDynamicBitset {\n private:\n  template <typename Word>\n  struct bit_span {\n    Word* data;\n    size_t size;\n\n    bit_span(Word* const data_, size_t const size_) noexcept\n        : data{data_}, size{size_} {}\n    bit_span(bit_span const&) = default;\n    bit_span& operator=(bit_span const&) = default;\n\n    auto as_tuple() const noexcept { return std::tuple{data, size}; }\n\n    friend bool operator==(bit_span const& a, bit_span const& b) noexcept {\n      return a.as_tuple() == b.as_tuple();\n    }\n    friend bool operator!=(bit_span const& a, bit_span const& b) noexcept {\n      return a.as_tuple() != b.as_tuple();\n    }\n  };\n\n public:\n  RegexMatchCacheDynamicBitset() = default;\n\n  RegexMatchCacheDynamicBitset(RegexMatchCacheDynamicBitset const&) = delete;\n  RegexMatchCacheDynamicBitset(RegexMatchCacheDynamicBitset&& that) noexcept\n      : data_{std::exchange(that.data_, {})} {}\n\n  ~RegexMatchCacheDynamicBitset() { reset_(); }\n\n  void operator=(RegexMatchCacheDynamicBitset const&) = delete;\n  RegexMatchCacheDynamicBitset& operator=(\n      RegexMatchCacheDynamicBitset&& that) noexcept {\n    reset_();\n    data_ = std::exchange(that.data_, {});\n    return *this;\n  }\n\n  bool get_value(size_t const index) const noexcept {\n    auto data = get_bit_span_();\n    if (!(index < data.size)) {\n      return false;\n    }\n    return get_value_(data, index);\n  }\n\n  void set_value(size_t const index, bool const value) {\n    constexpr auto wordbits = sizeof(uintptr_t) * 8;\n    auto data = get_bit_span_();\n\n    if (!(index < data.size) ||\n        (data.size == wordbits && index == wordbits - 1)) {\n      if (!value) {\n        return;\n      }\n      data = reserve_(index);\n    }\n    assert(index < data.size);\n    set_value_(data, index, value);\n  }\n\n  void reset() noexcept { reset_(); }\n\n  class index_set_view {\n   private:\n    friend RegexMatchCacheDynamicBitset;\n    bit_span<uintptr_t const> bitset_;\n\n    explicit index_set_view(RegexMatchCacheDynamicBitset const& bitset) noexcept\n        : bitset_{bitset.get_bit_span_()} {}\n\n   public:\n    using value_type = size_t;\n\n    class const_iterator {\n     public:\n      using value_type = size_t;\n      using difference_type = ptrdiff_t;\n      using pointer = void;\n      using iterator_category = std::forward_iterator_tag;\n\n      struct reference {\n       private:\n        friend class const_iterator;\n\n        size_t const index_;\n\n        explicit reference(size_t const index) noexcept : index_{index} {}\n\n       public:\n        operator size_t() const noexcept { return index_; }\n      };\n\n     private:\n      using self = const_iterator;\n\n      bit_span<uintptr_t const> const data_;\n      size_t index_;\n\n      size_t ceil_valid_index(size_t index) const noexcept {\n        constexpr auto wordbits = sizeof(uintptr_t) * 8;\n        while (index < data_.size) {\n          auto const wordidx = index / wordbits;\n          auto const wordoff = index % wordbits;\n          if (auto const word = data_.data[wordidx] >> wordoff) {\n            return index + findFirstSet(word) - 1;\n          }\n          index = (wordidx + 1) * wordbits;\n        }\n        return index;\n      }\n\n     public:\n      const_iterator(\n          bit_span<uintptr_t const> const data, size_t const index) noexcept\n          : data_{data}, index_{ceil_valid_index(index)} {}\n\n      reference operator*() const noexcept { return reference{index_}; }\n      const_iterator& operator++() noexcept {\n        index_ = ceil_valid_index(index_ + 1);\n        return *this;\n      }\n\n      friend bool operator==(self const& a, self const& b) noexcept {\n        return a.index_ == b.index_;\n      }\n      friend bool operator!=(self const& a, self const& b) noexcept {\n        return a.index_ != b.index_;\n      }\n    };\n\n    const_iterator begin() const noexcept { return const_iterator{bitset_, 0}; }\n    const_iterator end() const noexcept {\n      return const_iterator{bitset_, bitset_.size};\n    }\n\n    bool empty() const noexcept { return begin() == end(); }\n  };\n\n  index_set_view as_index_set_view() const noexcept {\n    return index_set_view{*this};\n  }\n\n private:\n  bool has_capacity_(size_t const index) const noexcept {\n    constexpr auto wordbits = sizeof(uintptr_t) * 8;\n    auto const buf = get_bit_span_();\n    return index < buf.size && !(buf.size == wordbits && index == wordbits - 1);\n  }\n\n  bit_span<uintptr_t> reserve_(size_t const index) {\n    assert(!has_capacity_(index));\n    constexpr auto wordbits = sizeof(uintptr_t) * 8;\n    constexpr auto minsize = wordbits * 2; // min growth from in-situ to on-heap\n    auto const newsize = std::max(strictNextPowTwo(index), minsize);\n    assert(newsize >= minsize);\n    assert(newsize % wordbits == 0);\n    auto const newdata = new uintptr_t[newsize / 8];\n    auto const buf = get_bit_span_();\n    auto const buf2size = nextPowTwo(buf.size);\n    std::memcpy(newdata, buf.data, buf2size / 8);\n    std::memset(newdata + buf2size / wordbits, 0, (newsize - buf2size) / 8);\n    if (!(to_signed(data_) < 0)) {\n      auto const data = new bit_span<uintptr_t>{newdata, newsize};\n      assert(!(reinterpret_cast<uintptr_t>(data) & 1));\n      data_ = (reinterpret_cast<uintptr_t>(data) >> 1) | ~(~uintptr_t(0) >> 1);\n      return *data;\n    } else {\n      auto const data = reinterpret_cast<bit_span<uintptr_t>*>(data_ << 1);\n      delete[] data->data;\n      *data = {newdata, newsize};\n      return *data;\n    }\n  }\n\n  void reset_() {\n    if (!(to_signed(data_) < 0)) {\n      data_ = 0;\n    } else {\n      auto const data = reinterpret_cast<bit_span<uintptr_t>*>(data_ << 1);\n      delete[] data->data;\n      delete data;\n      data_ = 0;\n    }\n  }\n\n  template <typename Word>\n  static bool get_value_(\n      bit_span<Word> const buf, size_t const index) noexcept {\n    assert(index < buf.size);\n    constexpr auto wordbits = sizeof(Word) * 8;\n    auto const wordidx = index / wordbits;\n    auto const wordoff = index % wordbits;\n    auto const mask = Word(1) << wordoff;\n    auto& word = buf.data[wordidx];\n    return word & mask;\n  }\n\n  template <typename Word>\n  static void set_value_(\n      bit_span<Word> const buf, size_t const index, bool const value) noexcept {\n    assert(index < buf.size);\n    constexpr auto wordbits = sizeof(Word) * 8;\n    assert(buf.size != wordbits || index != wordbits - 1);\n    auto const wordidx = index / wordbits;\n    auto const wordoff = index % wordbits;\n    auto const mask = Word(1) << wordoff;\n    auto& word = buf.data[wordidx];\n    word = value ? word | mask : word & ~mask;\n  }\n\n  bit_span<uintptr_t const> get_bit_span_() const noexcept {\n    if (!(to_signed(data_) < 0)) {\n      return {&data_, sizeof(data_) * 8};\n    } else {\n      return *reinterpret_cast<bit_span<uintptr_t const> const*>(data_ << 1);\n    }\n  }\n\n  bit_span<uintptr_t> get_bit_span_() noexcept {\n    if (!(to_signed(data_) < 0)) {\n      return {&data_, sizeof(data_) * 8};\n    } else {\n      return *reinterpret_cast<bit_span<uintptr_t> const*>(data_ << 1);\n    }\n  }\n\n  uintptr_t data_{};\n};\n\n/// RegexMatchCacheIndexedVector\n///\n/// An indexed vector, which is a vector for which the index of any element can\n/// be found efficiently.\n///\n/// Intended for use only within RegexMatchCache.\n///\n/// Incomplete as a generic container.\ntemplate <typename Value>\nclass RegexMatchCacheIndexedVector {\n public:\n  size_t size() const noexcept { return forward_.size(); }\n\n  bool contains_index(size_t index) const noexcept {\n    return reverse_.contains(index);\n  }\n\n  bool contains_value(Value const& value) const noexcept {\n    return forward_.contains(value);\n  }\n\n  std::pair<size_t, bool> insert_value(Value const& value) {\n    auto [iter, inserted] = forward_.try_emplace(value);\n    if (inserted) {\n      auto rollback_forward = makeGuard([&, iter_ = iter] {\n        forward_.erase(iter_);\n      });\n      if (free_.capacity() < forward_.size()) {\n        grow_capacity_by(free_, forward_.size() - free_.size());\n      }\n      assert(!(free_.capacity() < forward_.size()));\n      auto const from_free = !free_.empty();\n      auto const index = from_free ? free_.back() : forward_.size() - 1;\n      from_free ? free_.pop_back() : void();\n      iter->second = index;\n      auto rollback_free = makeGuard([&] {\n        from_free ? free_.push_back(index) : void();\n      });\n      assert(!reverse_.contains(index));\n      reverse_[index] = value;\n      rollback_free.dismiss();\n      rollback_forward.dismiss();\n    }\n    return {iter->second, inserted};\n  }\n\n  bool erase_value(Value const& value) noexcept {\n    auto iter = forward_.find(value);\n    if (iter == forward_.end()) {\n      return false;\n    }\n    assert(free_.size() < free_.capacity());\n    auto index = iter->second;\n    free_.push_back(index);\n    forward_.erase(iter);\n    reverse_.erase(index);\n    return true;\n  }\n\n  void clear() noexcept {\n    reverse_.clear();\n    forward_.clear();\n    free_.clear();\n  }\n\n  Value const& value_at_index(size_t index) const { return reverse_.at(index); }\n\n  size_t index_of_value(Value const& value) const { return forward_.at(value); }\n\n  class forward_view {\n   private:\n    friend RegexMatchCacheIndexedVector;\n    using map_t = folly::F14FastMap<Value, size_t>;\n    map_t const& map;\n    explicit forward_view(map_t const& map_) noexcept : map{map_} {}\n\n   public:\n    using value_type = typename map_t::value_type;\n    using size_type = typename map_t::size_type;\n    using iterator = typename map_t::const_iterator;\n\n    size_t size() const noexcept { return map.size(); }\n    iterator begin() const noexcept { return map.begin(); }\n    iterator end() const noexcept { return map.end(); }\n  };\n\n  forward_view as_forward_view() const noexcept {\n    return forward_view{forward_};\n  }\n\n private:\n  std::vector<size_t> free_;\n  folly::F14FastMap<Value, size_t> forward_;\n  folly::F14FastMap<size_t, Value> reverse_;\n};\n\nusing RegexMatchCacheKeyBase = unique_hash_key_strong_sha256<32>;\n\n/// RegexMatchCacheKey\n///\n/// A key derived from a string. Used with RegexMatchCache.\n///\n/// Intended for use only with RegexMatchCache.\n///\n/// Incomplete as a generic facility.\nclass RegexMatchCacheKey : public RegexMatchCacheKeyBase {\n public:\n  explicit RegexMatchCacheKey(std::string_view regex) noexcept\n      : RegexMatchCacheKeyBase{std::tuple(regex)} {}\n};\n\n} // namespace folly\n\nnamespace std {\n\ntemplate <>\nstruct hash<::folly::RegexMatchCacheKey>\n    : hash<::folly::RegexMatchCacheKeyBase> {};\n\n} // namespace std\n\nnamespace folly {\n\n/// RegexMatchCacheKeyAndView\n///\n/// A composite key and view derived from a string. Used with RegexMatchCache.\n///\n/// Intended for use only with RegexMatchCache.\n///\n/// Incomplete as a generic facility.\nclass RegexMatchCacheKeyAndView {\n public:\n  using regex_key = RegexMatchCacheKey;\n\n  regex_key const key;\n  std::string_view const view;\n\n  explicit RegexMatchCacheKeyAndView(std::string_view regex) noexcept\n      : key{regex}, view{regex} {}\n\n  /* implicit */ operator RegexMatchCacheKey const&() const noexcept {\n    return key;\n  }\n  /* implicit */ operator std::string_view const&() const noexcept {\n    return view;\n  }\n\n private:\n  RegexMatchCacheKeyAndView(\n      regex_key const& k, std::string_view const v) noexcept\n      : key{k}, view{v} {}\n};\n\n/// RegexMatchCache\n///\n/// A cache around boost::regex_match(string, regex).\n///\n/// For efficiency, assumes several constraints and makes several guarantees.\n///\n/// The data structure owns regexes but does not own strings. The lifetimes of\n/// all strings in the cache must surround their additions to the cache and\n/// their subsequent removals from the cache or destruction of the cache.\n///\n/// The data structure is in two parts:\n/// * A bidirectional match-cache contains all known matches.\n/// * a bidirectional string-queue contains unknown, hypothetical matches.\n///\n/// Cached lookup operates only over the match-cache. When the string-queue for\n/// a given regex is not empty, that regex is said to be uncoalesced. Cached\n/// lookups are not permitted for an uncoalesced regex; that regex must first be\n/// coalesced.\n///\n/// Addition of a string adds the string to the string-queue corresponding to\n/// all known regexes. It does not perform any regex-match operations.\n///\n/// Addition and coalesce of a regex performs regex-matches for that regex only.\n/// The string-queue for the given regex is removed and all elements matched\n/// against the regex, and matching strings are added to the match-cache.\n///\n/// Lookup must follow a pattern like this:\n///\n///    if (!cache.isReadyToFindMatches(regex)) { // const\n///      cache.prepareToFindMatches(regex); // non-const\n///    }\n///    auto matches = cache.findMatches(regex); // const\n///\n/// This is to support concurrent lookups, where the cache is protected by a\n/// shared mutex.\n///\n/// The data structure is exception-safe in a sense. If an exception is thrown\n/// within any non-const member function and escapes, the data structure may\n/// purge all cached regexes while leaving all strings. In most such member\n/// functions, only a memory-allocation failure would cause an exception to be\n/// thrown. But in prepareToFindMatches, the provided regex may be syntactically\n/// invalid and parsing it may throw, or it may be pathological and evaluating\n/// it over a string may throw. In any event, the resolution is to clear out all\n/// added regexes and to leave only the added strings. The reason is that this\n/// resolution is simple and likely to be correct, while any other mechanism\n/// would be complex and would be likely to have bugs.\nclass RegexMatchCache {\n public:\n  using clock = folly::chrono::coarse_steady_clock;\n  using time_point = clock::time_point;\n\n  using regex_key = RegexMatchCacheKey;\n  using regex_key_and_view = RegexMatchCacheKeyAndView;\n\n private:\n  using regex_pointer = regex_key const*;\n  using string_pointer = std::string const*;\n\n  class RegexObject;\n\n  struct RegexToMatchEntry : MoveOnly {\n    mutable std::atomic<time_point> accessed_at{};\n\n    folly::F14VectorSet<string_pointer> matches;\n  };\n\n  struct MatchToRegexEntry : MoveOnly {\n    RegexMatchCacheDynamicBitset regexes;\n  };\n\n  struct StringQueueForwardEntry : MoveOnly {\n    RegexMatchCacheDynamicBitset regexes;\n  };\n\n  struct StringQueueReverseEntry : MoveOnly {\n    folly::F14VectorSet<string_pointer> strings;\n  };\n\n  RegexMatchCacheIndexedVector<regex_pointer> regexVector_;\n\n  /// cacheRegexToMatch_\n  ///\n  /// A match-cache map from regexes to the sets of matching strings.\n  ///\n  /// The set of matching strings for a given regex may be incomplete. This\n  /// happens when strings are added to the universe but have not yet been\n  /// coalesced for the given regex. The set of uncoalesced strings for a\n  /// given regex is in stringQueueReverse_.\n  ///\n  /// For each regex, includes a last-accessed-at timestamp. This timestamp\n  /// is used when purging old regexes from the cache, for the caller's own\n  /// definition of old.\n  folly::F14NodeMap<regex_key, RegexToMatchEntry> cacheRegexToMatch_;\n\n  /// cacheMatchToRegex_\n  ///\n  /// A match-cache map from strings to the sets of matching regexes.\n  ///\n  /// The set of matching regexes for a given string may be incomplete. This\n  /// happens when strings are added to the universe but have not yet been\n  /// coalesced for all regexes in the universe. The set of regexes for which\n  /// a given string has not yet been coalesced is in stringQueueForward_.\n  folly::F14FastMap<string_pointer, MatchToRegexEntry> cacheMatchToRegex_;\n\n  /// stringQueueForward_\n  ///\n  /// A pending-coalesce map from strings to regexes for which the strings have\n  /// not yet been coalesced, that is, for which it is not yet known that the\n  /// strings do or do not match the given regexes.\n  ///\n  /// In a steady-state when all strings have been coalesced for all regexes,\n  /// this map would be empty.\n  folly::F14FastMap<string_pointer, StringQueueForwardEntry>\n      stringQueueForward_;\n\n  /// stringQueueReverse_\n  ///\n  /// A pending-coalesce map from regexes to strings which have not yet been\n  /// coalesced for the given regex, that is, for which it is not yet known that\n  /// the strings do or do not match the given regexes.\n  ///\n  /// In a steady-state when all strings have been coalesced for all regexes,\n  /// this map would be empty.\n  folly::F14FastMap<regex_pointer, StringQueueReverseEntry> stringQueueReverse_;\n\n  void repair() noexcept;\n\n public:\n  class KeyMap {\n   public:\n    using regex_key = RegexMatchCacheKey;\n    using regex_key_and_view = RegexMatchCacheKeyAndView;\n\n    virtual ~KeyMap() = 0;\n\n    virtual std::string_view lookup(regex_key const& regex) const = 0;\n  };\n\n  class InspectView {\n    friend RegexMatchCache;\n\n   private:\n    RegexMatchCache const& ref_;\n    KeyMap const& keys_;\n\n    explicit InspectView(\n        RegexMatchCache const& ref, KeyMap const& keys) noexcept\n        : ref_{ref}, keys_{keys} {}\n\n    void print(std::ostream& o) const;\n\n   public:\n    friend std::ostream& operator<<(std::ostream& o, InspectView const view) {\n      return (view.print(o), o);\n    }\n  };\n\n  class ConsistencyReportMatcher {\n   private:\n    struct state;\n    std::unique_ptr<state> state_;\n\n   public:\n    using regex_key = RegexMatchCache::regex_key;\n    using regex_key_and_view = RegexMatchCache::regex_key_and_view;\n    using string_pointer = RegexMatchCache::string_pointer;\n\n    ConsistencyReportMatcher();\n    virtual ~ConsistencyReportMatcher();\n\n    virtual bool match(\n        KeyMap const& keys, regex_key regex, string_pointer string);\n  };\n\n  class FindMatchesUnsafeResult {\n   private:\n    friend class RegexMatchCache;\n\n    using map_t = folly::F14VectorSet<string_pointer>;\n\n    map_t const& matches_;\n\n    /* implicit */ FindMatchesUnsafeResult(map_t const& matches) noexcept\n        : matches_{matches} {}\n\n   public:\n    using value_type = map_t::value_type;\n\n    auto size() const noexcept { return matches_.size(); }\n    auto begin() const noexcept { return matches_.begin(); }\n    auto end() const noexcept { return matches_.end(); }\n  };\n\n  RegexMatchCache() noexcept;\n  ~RegexMatchCache();\n\n  std::vector<std::string_view> getRegexList(KeyMap const& keys) const;\n  std::vector<string_pointer> getStringList() const;\n  InspectView inspect(KeyMap const& keys) const noexcept {\n    return InspectView{*this, keys};\n  }\n  void consistency(\n      ConsistencyReportMatcher& crcache,\n      KeyMap const& keys,\n      FunctionRef<void(std::string)> report) const;\n\n  bool hasRegex(regex_key const& regex) const noexcept;\n  void addRegex(regex_key const& regex);\n  void eraseRegex(regex_key const& regex);\n\n  bool hasString(string_pointer string) const noexcept;\n  void addString(string_pointer string);\n  void eraseString(string_pointer string);\n\n  std::vector<string_pointer> findMatchesUncached(std::string_view regex) const;\n\n  bool isReadyToFindMatches(regex_key const& regex) const noexcept;\n  void prepareToFindMatches(regex_key_and_view const& regex);\n  FindMatchesUnsafeResult findMatchesUnsafe(\n      regex_key const& regex, time_point now) const;\n  std::vector<string_pointer> findMatches(\n      regex_key const& regex, time_point now) const;\n\n  bool hasItemsToPurge(time_point expiry) const noexcept;\n\n  void clear();\n  void purge(time_point expiry);\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/Reserve.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdexcept>\n\n#include <folly/Likely.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nnamespace detail {\n\ntemplate <typename C>\nusing detect_capacity = decltype(FOLLY_DECLVAL(C).capacity());\n\ntemplate <typename C>\nusing detect_bucket_count = decltype(FOLLY_DECLVAL(C).bucket_count());\n\ntemplate <typename C>\nusing detect_max_load_factor = decltype(FOLLY_DECLVAL(C).max_load_factor());\n\ntemplate <typename C, typename... A>\nusing detect_reserve = decltype(FOLLY_DECLVAL(C).reserve(FOLLY_DECLVAL(A)...));\n\ntemplate <typename C>\nusing container_detect_reserve =\n    detect_reserve<C, typename remove_cvref_t<C>::size_type>;\n\n} // namespace detail\n\n/**\n * Avoids quadratic behavior that could arise from c.reserve(c.size() + N).\n *\n * May reserve more than N in order to effect geometric growth.  Useful when\n * c.size() is unknown, but more elements must be added by discrete operations.\n * For example: N emplace calls in a loop, or a series of range inserts, the sum\n * of their sizes being N.  Behaves like reserve() if the container is empty.\n */\nstruct grow_capacity_by_fn {\n  template <typename C>\n  constexpr void operator()(C& c, typename C::size_type const n) const {\n    const size_t sz = c.size();\n\n    if (FOLLY_UNLIKELY(c.max_size() - sz < n)) {\n      folly::throw_exception<std::length_error>(\"max_size exceeded\");\n    }\n\n    if constexpr (folly::is_detected_v<detail::detect_capacity, C&>) {\n      if (sz + n <= c.capacity()) {\n        return;\n      }\n    } else if constexpr (\n        folly::is_detected_v<detail::detect_bucket_count, C&> &&\n        folly::is_detected_v<detail::detect_max_load_factor, C&>) {\n      if (sz + n <= c.bucket_count() * c.max_load_factor()) {\n        return;\n      }\n    } else {\n      static_assert(folly::always_false<C>, \"unexpected container type\");\n    }\n\n    auto const ra = sz * 2;\n    auto const rb = sz + n;\n    c.reserve(rb < ra ? ra : rb);\n  }\n};\n\ninline constexpr grow_capacity_by_fn grow_capacity_by{};\n\n/**\n * Useful when writing generic code that handles containers.\n *\n * Examples:\n *  - std::unordered_map provides reserve(), but std::map does not\n *  - std::vector provides reserve(), but std::deque and std::list do not\n */\nstruct reserve_if_available_fn {\n  template <typename C>\n  constexpr auto operator()(C& c, typename C::size_type const n) const\n      noexcept(!folly::is_detected_v<detail::container_detect_reserve, C&>) {\n    constexpr auto match =\n        folly::is_detected_v<detail::container_detect_reserve, C&>;\n    if constexpr (match) {\n      c.reserve(n);\n    }\n    return std::bool_constant<match>{};\n  }\n};\n\ninline constexpr reserve_if_available_fn reserve_if_available{};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/SparseByteSet.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstdint>\n\n#include <folly/CPortability.h>\n\nnamespace folly {\n\n/***\n *  SparseByteSet\n *\n *  A special-purpose data structure representing a set of bytes.\n *  May have better performance than std::bitset<256>, depending on workload.\n *\n *  Operations:\n *  - add(byte)\n *  - remove(byte)\n *  - contains(byte)\n *  - clear()\n *\n *  Performance:\n *  - The entire capacity of the set is inline; the set never allocates.\n *  - The constructor zeros only the first two bytes of the object.\n *  - add and contains both run in constant time w.r.t. the size of the set.\n *    Constant time - not amortized constant - and with small constant factor.\n *\n *  This data structure is ideal for on-stack use.\n *\n *  Aho, Hopcroft, and Ullman refer to this trick in \"The Design and Analysis\n *  of Computer Algorithms\" (1974), but the best description is here:\n *  http://research.swtch.com/sparse\n *  http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.7319\n */\nclass SparseByteSet {\n public:\n  //  There are this many possible values:\n  static constexpr uint16_t kCapacity = 256;\n\n  //  No init of byte-arrays required!\n  SparseByteSet() : size_(0) {}\n\n  /***\n   *  add(byte)\n   *\n   *  O(1), non-amortized.\n   */\n  inline bool add(uint8_t i) {\n    bool r = !contains(i);\n    if (r) {\n      assert(size_ < kCapacity);\n      dense_[size_] = i;\n      sparse_[i] = uint8_t(size_);\n      size_++;\n    }\n    return r;\n  }\n\n  /***\n   *  remove(byte)\n   *\n   *  O(1), non-amortized.\n   */\n  inline bool remove(uint8_t i) {\n    bool r = contains(i);\n    if (r) {\n      if (dense_[size_ - 1] != i) {\n        uint8_t last_element = dense_[size_ - 1];\n        dense_[sparse_[i]] = last_element;\n        sparse_[last_element] = sparse_[i];\n      }\n      --size_;\n    }\n    return r;\n  }\n\n  /***\n   *  contains(byte)\n   *\n   *  O(1), non-amortized.\n   */\n  inline bool contains(uint8_t i) const FOLLY_DISABLE_MEMORY_SANITIZER {\n    return sparse_[i] < size_ && dense_[sparse_[i]] == i;\n  }\n\n  /***\n   *  clear()\n   *\n   *  O(1), non-amortized.\n   */\n  inline void clear() { size_ = 0; }\n\n  /***\n   *  size()\n   *\n   *  O(1), non-amortized.\n   */\n  inline uint16_t size() { return size_; }\n\n private:\n  uint16_t size_; // can't use uint8_t because it would overflow if all\n                  // possible values were inserted.\n  uint8_t sparse_[kCapacity];\n  uint8_t dense_[kCapacity];\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/StdBitset.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CPortability.h>\n#include <folly/Portability.h>\n\n#include <bit>\n#include <bitset>\n#include <cassert>\n#include <folly/Likely.h>\n\nnamespace folly {\n\n#ifdef _LIBCPP_VERSION\n\nconstexpr size_t kWordSize = 64;\n\nnamespace libcpp_detail {\nstruct std_bit_reference_layout {\n  size_t* address;\n  size_t mask;\n};\n\n// Return the content of the 64-bit word, given an index.\ntemplate <size_t N>\nsize_t* get_underlying_data_region(const std::bitset<N>& bitset) {\n  // You are only getting the __bit_reference as the return if you access\n  // with operator[], which is not const.\n  auto& bitset_ref = const_cast<std::bitset<N>&>(bitset);\n  std::__bit_reference bitref = bitset_ref[0];\n  auto* br =\n      reinterpret_cast<std_bit_reference_layout*>(std::addressof(bitref));\n  return br->address;\n}\n\ntemplate <size_t N>\nsize_t std_bitset_find_next(const std::bitset<N>& bitset, size_t start) {\n  constexpr size_t max_words = N / kWordSize + 1;\n  if constexpr (N == 0) {\n    return 1;\n  }\n\n  // Check starting in the middle of a word\n  size_t start_in_word = start % kWordSize;\n  size_t word_idx = start / kWordSize;\n  size_t* data = get_underlying_data_region(bitset);\n\n  if (start_in_word) {\n    // Invariant: start != 0, 1 <= start_in_word <= 63\n    // test the remaining bits in the same word as previous\n    size_t mask = (~size_t(0)) << start_in_word;\n    size_t partial_word = data[word_idx] & mask;\n\n    if (partial_word) {\n      size_t idx_in_word = std::countr_zero(partial_word);\n      // We found the next\n      return start - start_in_word + idx_in_word;\n    }\n    // Special treatment of the partial first word was done. Skip it in the\n    // next loop for whole words.\n    word_idx += 1;\n  }\n\n  while (word_idx < max_words) {\n    size_t word = data[word_idx];\n    if (FOLLY_UNLIKELY(word)) {\n      size_t idx_in_word = std::countr_zero(word);\n      // we found the first\n      return word_idx * kWordSize + idx_in_word;\n    }\n    word_idx += 1;\n  }\n\n  return N;\n}\n} // namespace libcpp_detail\n\n#endif // _LIBCPP_VERSION\n\nstruct std_bitset_find_first_fn {\n  /// Return the index of the first set bit in a bitset, or bitset.size() if\n  /// none.\n  template <size_t N>\n  FOLLY_ALWAYS_INLINE size_t\n  operator()(const std::bitset<N>& bitset) const noexcept {\n#if defined(__GLIBCXX__)\n    // GNU provides non-standard (its a hold over from the original SGI\n    // implementation) _Find_first(), which efficiently returns the index of\n    // the first set bit.\n    return bitset._Find_first();\n#elif defined(_LIBCPP_VERSION)\n    return folly::libcpp_detail::std_bitset_find_next(bitset, 0);\n#endif\n\n    for (size_t i = 0; i < bitset.size(); ++i) {\n      if (bitset[i]) {\n        return i;\n      }\n    }\n\n    return bitset.size();\n  }\n};\n\ninline constexpr std_bitset_find_first_fn std_bitset_find_first{};\n\nstruct std_bitset_find_next_fn {\n  /// Return the index of the first set bit in a bitset after the given index,\n  /// or bitset.size() if none.\n  template <size_t N>\n  FOLLY_ALWAYS_INLINE size_t\n  operator()(const std::bitset<N>& bitset, size_t prev) const noexcept {\n    assert(prev < bitset.size());\n\n#if defined(__GLIBCXX__)\n    // GNU provides non-standard (its a hold over from the original SGI\n    // implementation) _Find_next(), which given an index, efficiently returns\n    // the index of the first set bit after the index.\n    return bitset._Find_next(prev);\n#elif defined(_LIBCPP_VERSION)\n    return folly::libcpp_detail::std_bitset_find_next(bitset, prev + 1);\n#endif\n\n    for (size_t i = prev + 1; i < bitset.size(); ++i) {\n      if (bitset[i]) {\n        return i;\n      }\n    }\n\n    return bitset.size();\n  }\n};\n\ninline constexpr std_bitset_find_next_fn std_bitset_find_next{};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/View.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/CustomizationPoint.h>\n\nnamespace folly {\n\n//  order_preserving_reinsertion_view_fn\n//  order_preserving_reinsertion_view\n//\n//  Extension point for containers to provide an order such that if entries are\n//  inserted into a new instance in that order, iteration order of the new\n//  instance matches the original's. This can be useful for containers that have\n//  defined but non-FIFO iteration order, such as F14Vector*.\n//\n//  Should return an iterable view (a type that provides begin() and end()).\n//\n//  Containers should provide overloads via tag-invoke.\nstruct order_preserving_reinsertion_view_fn {\n private:\n  using fn = order_preserving_reinsertion_view_fn;\n\n public:\n  template <typename Container>\n  FOLLY_ERASE constexpr auto operator()(Container const& container) const\n      noexcept(is_nothrow_tag_invocable_v<fn, Container const&>)\n          -> tag_invoke_result_t<fn, Container const&> {\n    return tag_invoke(*this, container);\n  }\n};\nFOLLY_DEFINE_CPO(\n    order_preserving_reinsertion_view_fn, order_preserving_reinsertion_view)\n\n//  order_preserving_reinsertion_view_or_default_fn\n//  order_preserving_reinsertion_view_or_default\n//\n//  If a tag-invoke extension of order_preserving_reinsertion_view is available\n//  over the given argument, forwards to that. Otherwise, returns the argument.\nstruct order_preserving_reinsertion_view_or_default_fn {\n private:\n  using fn = order_preserving_reinsertion_view_fn;\n\n public:\n  template <\n      typename Container,\n      std::enable_if_t<is_tag_invocable_v<fn, Container const&>, int> = 0>\n  FOLLY_ERASE constexpr auto operator()(Container const& container) const\n      noexcept(is_nothrow_tag_invocable_v<fn, Container const&>)\n          -> tag_invoke_result_t<fn, Container const&> {\n    return tag_invoke(fn{}, container);\n  }\n  template <\n      typename Container,\n      std::enable_if_t<!is_tag_invocable_v<fn, Container const&>, int> = 0>\n  FOLLY_ERASE constexpr Container const& operator()(\n      Container const& container) const noexcept {\n    return container;\n  }\n};\nFOLLY_DEFINE_CPO(\n    order_preserving_reinsertion_view_or_default_fn,\n    order_preserving_reinsertion_view_or_default)\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/WeightedEvictingCacheMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <new>\n#include <type_traits>\n#include <folly/container/EvictingCacheMap.h>\n\nnamespace folly {\n\n/**\n * A variant of EvictingCacheMap that assigns weights to entries and\n * evicts entries in LRU order to ensure the total weight of all entries\n * stays below some set maximum. ImplicitlyWeighted means this variant\n * derives the weights from the key-values using a chosen function. TWeightFn\n * must be a type implementing `size_t operator()(const TKey&, const Tvalue&)`\n *\n * Example usage: if TKey and TValue are std::string, the weight could be the\n * sum of the string sizes (already stored in the key and value) so that the\n * total weight approximates the total memory usage.\n *\n * Also consider WeightedEvictingCacheMap below, which tracks weights\n * explicitly, along with keys and values.\n *\n * TValue must be either movable or copyable. TKey must be copyable.\n *\n * IMPORTANT NOTES:\n * * Returned references, pointers, or iterators are potentially invalid\n * after any pruning operation (set, insert, etc. that might increase total\n * weight), or any set/insert on the same key (which are allowed to create a\n * new entry or modify an existing entry becoming obsolete).\n * * Operations are more restrictive than EvictingCacheMap to reduce the\n * risk of modifying a value in a way that changes its weight without proper\n * tracking. TValue can be a const type if appropriate.\n * * This is NOT a thread-safe structure.\n * * For simplicity, functions taking a key implicitly inherit type\n * constraints from EvictingCacheMap. (Must either match TKey or\n * EligibleForHeterogeneousFind/Insert.)\n *\n * This implementation has not been highly optimized and is a wrapper around\n * EvictingCacheMap.\n */\ntemplate <\n    class TKey,\n    class TValue,\n    class TWeightFn,\n    class THash = HeterogeneousAccessHash<TKey>,\n    class TKeyEqual = HeterogeneousAccessEqualTo<TKey>>\nclass ImplicitlyWeightedEvictingCacheMap {\n private: // typedefs\n  using ECM = EvictingCacheMap<TKey, TValue, THash, TKeyEqual>;\n\n public:\n  using PruneHookCall = std::function<void(TKey, TValue&&)>;\n\n  explicit ImplicitlyWeightedEvictingCacheMap(\n      std::size_t maxTotalWeight,\n      const TWeightFn& weightFn = TWeightFn(),\n      const THash& keyHash = THash(),\n      const TKeyEqual& keyEqual = TKeyEqual())\n      : ecm_(/* no max size*/ 0, 1, keyHash, keyEqual),\n        weightFn_(weightFn),\n        maxTotalWeight_(maxTotalWeight),\n        currentTotalWeight_(0) {\n    setupPruneHook();\n  }\n\n  // Like EvictingCacheMap\n  ImplicitlyWeightedEvictingCacheMap(\n      const ImplicitlyWeightedEvictingCacheMap&) = delete;\n  ImplicitlyWeightedEvictingCacheMap& operator=(\n      const ImplicitlyWeightedEvictingCacheMap&) = delete;\n  ImplicitlyWeightedEvictingCacheMap& operator=(\n      ImplicitlyWeightedEvictingCacheMap&& that) {\n    // Put moved-from in a valid but empty state\n    ecm_ = std::move(that.ecm_);\n    that.ecm_.clear();\n    weightFn_ = std::move(that.weightFn_);\n    that.weightFn_ = TWeightFn();\n    maxTotalWeight_ = std::move(that.maxTotalWeight_);\n    that.maxTotalWeight_ = 0;\n    currentTotalWeight_ = std::move(that.currentTotalWeight_);\n    that.currentTotalWeight_ = 0;\n    // Set prune hook for this 'this' (not that this)\n    setupPruneHook();\n    return *this;\n  }\n  ImplicitlyWeightedEvictingCacheMap(ImplicitlyWeightedEvictingCacheMap&& that)\n      : ecm_(/* no max size*/ 0) {\n    *this = std::move(that);\n  }\n\n  ~ImplicitlyWeightedEvictingCacheMap() {\n#ifndef NDEBUG\n    // Verify remaining total weight is properly tracked. This can break if\n    // improperly mutating the TValues that changes the result of TWeightFn.\n    std::size_t actual_total_weight = 0;\n    for (auto& e : ecm_) {\n      actual_total_weight += weightFn_(e.first, e.second);\n    }\n    assert(actual_total_weight == currentTotalWeight_);\n#endif\n  }\n\n  static constexpr std::size_t kApproximateEntryMemUsage =\n      ECM::kApproximateEntryMemUsage;\n\n  // iterators. NOTE: mutable iterators not included because of challenges and\n  // confusion over writes affecting the implicit weights and whether entries\n  // are promoted or potentially invalidated due to pruning.\n  using const_iterator = typename ECM::const_iterator;\n  using const_reverse_iterator = typename ECM::const_reverse_iterator;\n\n  const_iterator begin() const { return ecm_.begin(); }\n  const_iterator end() const { return ecm_.end(); }\n\n  const_reverse_iterator rbegin() const { return ecm_.rbegin(); }\n  const_reverse_iterator rend() const { return ecm_.rend(); }\n\n  /**\n   * Get the number of elements in the dictionary\n   * @return the size of the dictionary\n   */\n  std::size_t size() const { return ecm_.size(); }\n\n  /**\n   * Typical empty function\n   * @return true if empty, false otherwise\n   */\n  bool empty() const { return ecm_.empty(); }\n\n  /**\n   * Returns total weight of all entries currently in the cache.\n   */\n  std::size_t getCurrentTotalWeight() const { return currentTotalWeight_; }\n\n  /**\n   * Returns the maximum allowed total weight of all entries in\n   * the cache.\n   */\n  std::size_t getMaxTotalWeight() const { return maxTotalWeight_; }\n\n  /**\n   * Sets the maximum allowed total weight of all entries in\n   * the cache, evicting entries as needed for the new limit.\n   */\n  void setMaxTotalWeight(std::size_t newMaxTotalWeight) {\n    maxTotalWeight_ = newMaxTotalWeight;\n    pruneToMaxTotalWeight();\n  }\n\n  /**\n   * Check for existence of a specific key in the map.  This operation has\n   *     no effect on LRU order.\n   * @param key key to search for\n   * @return true if exists, false otherwise\n   */\n  template <typename K>\n  bool exists(const K& key) const {\n    return ecm_.exists(key);\n  }\n\n  /**\n   * Get the value associated with a specific key.  This function always\n   * promotes a found value to the head of the LRU. The returned reference\n   * is const and might be inavlidated by many subsequent operations. See\n   * IMPORTANT NOTES above. See also replace().\n   *\n   * @param key key to search for\n   * @return the value if it exists\n   * @throw std::out_of_range exception of the key does not exist\n   */\n  template <typename K>\n  const TValue& get(const K& key) {\n    return ecm_.get(key);\n  }\n\n  // Same but without LRU promotion\n  template <typename K>\n  const TValue& getWithoutPromotion(const K& key) const {\n    return ecm_.getWithoutPromotion(key);\n  }\n\n  /**\n   * Set a key-value pair in the dictionary with a given weight. If its weight\n   * is more than maxTotalWeight, the entry is inserted anyway and all other\n   * entries are evicted, so that get() after set() always succeeds. The\n   * structure can be temporarily over max weight until the next modification.\n   * The new or modified entry is always inserted at or promoted to the head\n   * of the LRU.\n   *\n   * @param key key to associate with value\n   * @param value value to associate with the key\n   */\n  template <typename K>\n  void set(const K& key, TValue&& value) {\n    std::size_t new_weight = weightFn_(key, value);\n    std::size_t old_weight = 0;\n    auto it = find(key); // Does promotion\n    if (it != end()) {\n      using TMutableValue = std::remove_const_t<TValue>;\n\n      // Existing entry\n      old_weight = weightFn_(it->first, it->second);\n      auto ptr = const_cast<TMutableValue*>(&it->second);\n      // Overwrite\n      // Would be this, but need to work around\n      // const values, which don't allow move-assignment:\n      //   it->second = std::move(value);\n      // Below hack is OK because we reserve the right to \"entirely new\n      // entry\" semantics for set() (invalidate pointers/iterators/etc.) but\n      // optimize with \"replace in place\" implementation.\n      ptr->~TValue();\n      new (ptr) TValue(std::move(value));\n    } else {\n      // No existing entry\n      ecm_.insert(key, std::move(value));\n    }\n    // Protect the entry we just put at the head of the LRU\n    entryWeightUpdated(old_weight, new_weight, /*protect_one*/ true);\n  }\n\n  template <typename K>\n  void set(const K& key, const TValue& value) {\n    TValue tmp{value}; // can't yet rely on C++17 temporary materialization\n    set(key, std::move(tmp));\n  }\n\n  // TODO: insert() functions, which would refuse insert if new entry weight\n  // exceeds max (or existing entry for key)\n  // template <typename K>\n  // std::pair<const_iterator, bool> insert(const K& key, TValue&& value) {}\n\n  /**\n   * Erases any entry with given key or iterator.\n   *\n   * @param key_or_it key to search for or iterator\n   */\n  template <typename KI>\n  void erase(const KI& key_or_it) {\n    // Use prune hook as erase hook to keep total weight updated\n    ecm_.erase(key_or_it, ecm_.getPruneHook());\n  }\n\n  /**\n   * Get the iterator associated with a specific key.  This function always\n   * promotes a found value to the head of the LRU. See IMPORTANT NOTES above\n   * for why this only returns a const_iterator. See also replace().\n   * @param key key to associate with value\n   * @return the const_iterator of the object (a std::pair of const TKey,\n   *     TValue) or end() if it does not exist\n   */\n  template <typename K>\n  const_iterator find(const K& key) {\n    return const_iterator(ecm_.find(key).base());\n  }\n\n  // Same but without LRU promotion\n  template <typename K>\n  const_iterator findWithoutPromotion(const K& key) const {\n    return ecm_.findWithoutPromotion(key);\n  }\n\n  /**\n   * Replace the value associated with an entry from a const_iterator, in a\n   * safe way that tracks any modification to the weight. Entries are evicted\n   * so that max total weight it not exceeded, except this function protects\n   * the entry at the head of the LRU list from eviction. Thus, for find()\n   * immediately followed by replace(), the modified entry is protected\n   * against eviction even if exceeding max total weight (like set(), iterator\n   * remains valid). The iterator also remains valid if the weight does not\n   * increase. Otherwise, the iterator is potentially invalidated by eviction\n   * during this operation.\n   *\n   * If TValue is a const type, this function is invalid.\n   * @param it const_iterator for the entry to modify, which must come from\n   * this cache map\n   * @param value replacement value\n   */\n  void replace(const_iterator it, TValue&& value) {\n    assert(it != end());\n    size_t old_weight = weightFn_(it->first, it->second);\n    size_t new_weight = weightFn_(it->first, value);\n    // Overwrite in place\n    const_cast<TValue&>(it->second) = std::move(value);\n    // Evict as needed (possibly including this entry, unless it's LRU head)\n    entryWeightUpdated(old_weight, new_weight, /*protect_one*/ true);\n  }\n\n  void replace(const_iterator it, const TValue& value) {\n    TValue tmp{value}; // can't yet rely on C++17 temporary materialization\n    replace(it, std::move(tmp));\n  }\n\n  /**\n   * Clear the cache to an empty state.\n   */\n  void clear() {\n    ecm_.clear();\n    assert(currentTotalWeight_ == 0);\n  }\n\n  /**\n   * Set the prune hook, which is the function invoked on the key and value\n   *     on each eviction. An operation will throw if the pruneHook throws.\n   *     Note that this prune hook is not automatically called on entries\n   *     explicitly erase()ed nor on remaining entries at destruction time.\n   * @param pruneHook eviction callback to set as default, or nullptr to clear\n   */\n  void setPruneHook(PruneHookCall pruneHook) { pruneHook_ = pruneHook; }\n\n private: // fns\n  void setupPruneHook() {\n    ecm_.setPruneHook([this](const TKey& key, TValue&& value) {\n      std::size_t weight = weightFn_(key, value);\n      assert(currentTotalWeight_ >= weight);\n      currentTotalWeight_ -= weight;\n      if (pruneHook_) {\n        pruneHook_(key, std::move(value));\n      }\n    });\n  }\n\n  void entryWeightUpdated(\n      std::size_t old_weight,\n      std::size_t new_weight,\n      bool protect_one = false) {\n    assert(old_weight <= currentTotalWeight_);\n    currentTotalWeight_ += new_weight - old_weight;\n    pruneToMaxTotalWeight(protect_one);\n  }\n\n  void pruneToMaxTotalWeight(bool protect_one = false) {\n    // NOTE: Avoid infinite loop even in the case of weight tracking bug\n    size_t min_count = protect_one ? 1 : 0;\n    while (currentTotalWeight_ > maxTotalWeight_ && ecm_.size() > min_count) {\n      ecm_.prune(1);\n    }\n  }\n\n  template <class _TKey, class _TValue, class _THash, class _TKeyEqual>\n  friend class WeightedEvictingCacheMap;\n\n private: // data\n  PruneHookCall pruneHook_;\n  ECM ecm_;\n  TWeightFn weightFn_;\n  std::size_t maxTotalWeight_;\n  std::size_t currentTotalWeight_;\n};\n\n/**\n * A variant of EvictingCacheMap that tracks weights for entries and\n * evicts entries in LRU order to ensure the total weight of all entries\n * stays below some set maximum. Weights are stored as a size_t with each\n * entry.\n *\n * Example usage: if TKey is std::string and TValue is some large, complex\n * object type, the weight could be the estimated memory size of the key\n * and complex object. Tracking the weight explicitly minimizes costly\n * recomputation of the estimated memory size. Thus, the total weight of all\n * entries approximates the total memory usage.\n *\n * TValue must be either movable or copyable. TKey must be copyable.\n *\n * IMPORTANT NOTES:\n * * Returned references, pointers, or iterators are potentially invalid\n * after any pruning operation (set, insert, etc. that might increase total\n * weight), or any set/insert on the same key (which are allowed to create a\n * new entry or modify an existing entry becoming obsolete).\n * * This is NOT a thread-safe structure.\n * * For simplicity, functions taking a key implicitly inherit type\n * constraints from EvictingCacheMap. (Must either match TKey or\n * EligibleForHeterogeneousFind/Insert.)\n *\n * This implementation has not been highly optimized.\n */\ntemplate <\n    class TKey,\n    class TValue,\n    class THash = HeterogeneousAccessHash<TKey>,\n    class TKeyEqual = HeterogeneousAccessEqualTo<TKey>>\nclass WeightedEvictingCacheMap {\n public: // types\n  struct ValueAndWeight {\n    /*implicit*/ ValueAndWeight(TValue&& _value, std::size_t _weight)\n        : value(std::move(_value)), weight(_weight) {}\n    // Value is mutable through non-const iterator\n    TValue value;\n    // Weight is not mutable to ensure proper tracking. See updateWeight().\n    const std::size_t weight;\n  };\n\n private: // types\n  struct WeightFn {\n    std::size_t operator()(const TKey& /*key*/, const ValueAndWeight& p) {\n      return p.weight;\n    }\n  };\n  using IWECM = ImplicitlyWeightedEvictingCacheMap<\n      TKey,\n      ValueAndWeight,\n      WeightFn,\n      THash,\n      TKeyEqual>;\n\n public:\n  using PruneHookCall = std::function<void(TKey, TValue&&, size_t)>;\n\n  explicit WeightedEvictingCacheMap(\n      std::size_t maxTotalWeight,\n      const THash& keyHash = THash(),\n      const TKeyEqual& keyEqual = TKeyEqual())\n      : iwecm_(maxTotalWeight, WeightFn(), keyHash, keyEqual) {}\n\n  // Like EvictingCacheMap\n  WeightedEvictingCacheMap(const WeightedEvictingCacheMap&) = delete;\n  WeightedEvictingCacheMap& operator=(const WeightedEvictingCacheMap&) = delete;\n  WeightedEvictingCacheMap(WeightedEvictingCacheMap&&) = default;\n  WeightedEvictingCacheMap& operator=(WeightedEvictingCacheMap&&) = default;\n\n  static constexpr std::size_t kApproximateEntryMemUsage =\n      IWECM::kApproximateEntryMemUsage;\n\n  // iterators that dereference to ValueAndWeight\n  using iterator = typename IWECM::ECM::iterator;\n  using reverse_iterator = typename IWECM::ECM::reverse_iterator;\n  using const_iterator = typename IWECM::const_iterator;\n  using const_reverse_iterator = typename IWECM::const_reverse_iterator;\n\n  iterator begin() { return iwecm_.ecm_.begin(); }\n  iterator end() { return iwecm_.ecm_.end(); }\n\n  const_iterator begin() const { return cbegin(); }\n  const_iterator end() const { return cend(); }\n\n  const_iterator cbegin() const { return iwecm_.begin(); }\n  const_iterator cend() const { return iwecm_.end(); }\n\n  reverse_iterator rbegin() { return iwecm_.ecm_.rbegin(); }\n  reverse_iterator rend() { return iwecm_.ecm_.rend(); }\n\n  const_reverse_iterator rbegin() const { return crbegin(); }\n  const_reverse_iterator rend() const { return crend(); }\n\n  const_reverse_iterator crbegin() const { return iwecm_.rbegin(); }\n  const_reverse_iterator crend() const { return iwecm_.rend(); }\n\n  /**\n   * Get the number of elements in the dictionary\n   * @return the size of the dictionary\n   */\n  std::size_t size() const { return iwecm_.size(); }\n\n  /**\n   * Typical empty function\n   * @return true if empty, false otherwise\n   */\n  bool empty() const { return iwecm_.empty(); }\n\n  /**\n   * Returns total weight of all entries currently in the cache.\n   */\n  std::size_t getCurrentTotalWeight() const {\n    return iwecm_.getCurrentTotalWeight();\n  }\n\n  /**\n   * Returns the maximum allowed total weight of all entries in\n   * the cache.\n   */\n  std::size_t getMaxTotalWeight() const { return iwecm_.getMaxTotalWeight(); }\n\n  /**\n   * Sets the maximum allowed total weight of all entries in\n   * the cache, evicting entries as needed for the new limit.\n   */\n  void setMaxTotalWeight(std::size_t newMaxTotalWeight) {\n    iwecm_.setMaxTotalWeight(newMaxTotalWeight);\n  }\n\n  /**\n   * Check for existence of a specific key in the map.  This operation has\n   *     no effect on LRU order.\n   * @param key key to search for\n   * @return true if exists, false otherwise\n   */\n  template <typename K>\n  bool exists(const K& key) const {\n    return iwecm_.exists(key);\n  }\n\n  /**\n   * Get the value associated with a specific key.  This function always\n   * promotes a found value to the head of the LRU. The TValue can be\n   * modified in place through the reference, keeping in mind the reference\n   * can easily be invalidated (IMPORTANT NOTES above).\n   * @param key key to search for\n   * @return the value if it exists\n   * @throw std::out_of_range exception of the key does not exist\n   */\n  template <typename K>\n  TValue& get(const K& key) {\n    return const_cast<TValue&>(iwecm_.get(key).value);\n  }\n  // Same but without LRU promotion\n  template <typename K>\n  TValue& getWithoutPromotion(const K& key) {\n    return const_cast<TValue&>(iwecm_.getWithoutPromotion(key).value);\n  }\n  template <typename K>\n  const TValue& getWithoutPromotion(const K& key) const {\n    return iwecm_.getWithoutPromotion(key).value;\n  }\n\n  /**\n   * Set a key-value pair in the dictionary with a given weight. If its weight\n   * is more than maxTotalWeight, the entry is inserted anyway and all other\n   * entries are evicted, so that get() after set() always succeeds. The\n   * structure can be temporarily over max weight until the next modification.\n   * The new or modified entry is always inserted at or promoted to the head\n   * of the LRU.\n   *\n   * @param key key to associate with value\n   * @param value value to associate with the key\n   * @param weight weight to associate with the key-value entry\n   */\n  template <typename K>\n  void set(const K& key, TValue&& value, std::size_t weight) {\n    ValueAndWeight tmp{std::move(value), weight};\n    return iwecm_.set(key, std::move(tmp));\n  }\n\n  template <typename K>\n  void set(const K& key, const TValue& value, std::size_t weight) {\n    TValue tmp{value}; // can't yet rely on C++17 temporary materialization\n    return set(key, std::move(tmp), weight);\n  }\n\n  // TODO: insert() functions, which would refuse insert if new entry weight\n  // exceeds max (or existing entry for key)\n  // template <typename K>\n  // std::pair<const_iterator, bool> insert(const K& key, TValue&& value) {}\n\n  /**\n   * Erases any entry with given key or iterator.\n   *\n   * @param key_or_it key to search for or iterator\n   */\n  template <typename KI>\n  void erase(const KI& key_or_it) {\n    iwecm_.erase(key_or_it);\n  }\n\n  /**\n   * Get the iterator associated with a specific key. This function always\n   * promotes a found value to the head of the LRU. Although values can be\n   * modified through iterators, weights are const. See updateWeight().\n   * @param key key to associate with value\n   * @return the iterator of std::pair<const TKey, ValueAndWeight>, or\n   *     end() if it does not exist\n   */\n  template <typename K>\n  iterator find(const K& key) {\n    return iwecm_.ecm_.find(key);\n  }\n\n  // Same but without LRU promotion\n  template <typename K>\n  iterator findWithoutPromotion(const K& key) {\n    return iwecm_.ecm_.findWithoutPromotion(key);\n  }\n  template <typename K>\n  const_iterator findWithoutPromotion(const K& key) const {\n    return iwecm_.findWithoutPromotion(key);\n  }\n\n  /**\n   * Overwrite the weight associated with a specific entry. As usual, entries\n   * are evicted so that max total weight it not exceeded, except this\n   * function protects the entry at the head of the LRU list from eviction.\n   * Thus, for find() immediately followed by updateWeight(), the modified\n   * entry is protected against eviction even if exceeding max total weight\n   * (like set(), iterator remains valid). The iterator also remains valid if\n   * the weight does not increase. Otherwise, the iterator is potentially\n   * invalidated by eviction during this operation.\n   *\n   * @param it iterator for the entry to modify, which must come from\n   * this cache map\n   * @param new_weight the updated weight to assign\n   */\n  void updateWeight(const_iterator it, std::size_t new_weight) {\n    updateWeightImpl(it, new_weight);\n  }\n  void updateWeight(iterator it, std::size_t new_weight) {\n    updateWeightImpl(it, new_weight);\n  }\n\n  /**\n   * Clear the cache to an empty state.\n   */\n  void clear() { iwecm_.clear(); }\n\n  /**\n   * Set the prune hook, which is the function invoked on the key and value\n   *     on each eviction. An operation will throw if the pruneHook throws.\n   *     Note that this prune hook is not automatically called on entries\n   *     explicitly erase()ed nor on remaining entries at destruction time.\n   * @param pruneHook eviction callback to set as default, or nullptr to clear\n   */\n  void setPruneHook(PruneHookCall pruneHook) {\n    iwecm_.setPruneHook([pruneHook = std::move(pruneHook)](\n                            const TKey& key, ValueAndWeight&& valueAndWeight) {\n      if (!pruneHook) {\n        return;\n      }\n      pruneHook(key, std::move(valueAndWeight.value), valueAndWeight.weight);\n    });\n  }\n\n private:\n  // Like IWECM::replace\n  template <typename It>\n  void updateWeightImpl(It it, std::size_t new_weight) {\n    assert(it != end());\n    size_t old_weight = it->second.weight;\n    // Overwrite in place\n    const_cast<std::size_t&>(it->second.weight) = new_weight;\n    // Evict as needed (possibly including this entry, unless it's LRU head)\n    iwecm_.entryWeightUpdated(old_weight, new_weight, /*protect_one*/ true);\n  }\n\n  PruneHookCall pruneHook_;\n  IWECM iwecm_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"FBANDROID_CPPFLAGS\",\n    \"folly_xplat_library\",\n)\n\noncall(\"ios_app_infra\")\n\nfb_dirsync_cpp_library(\n    name = \"bit_iterator_detail\",\n    headers = [\"BitIteratorDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/portability:sys_types\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"f14_intrinsics_availability\",\n    exported_preprocessor_flags = select({\n        \"//xplat/folly/buck_config:folly-f14-fallback-disabled\": [\n            \"-DFOLLY_F14_FALLBACK_DISABLED=1\",\n        ],\n        \"DEFAULT\": [],\n    }),\n    raw_headers = [\n        \"F14IntrinsicsAvailability.h\",\n    ],\n    deps = [\n        \"//xplat/folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"f14_hash_detail\",\n    srcs = [\n        \"F14Table.cpp\",\n    ],\n    headers = [\n        \"F14MapFallback.h\",\n        \"F14Policy.h\",\n        \"F14SetFallback.h\",\n        \"F14Table.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":f14_defaults\",\n        \":f14_intrinsics_availability\",\n        \":f14_mask\",\n        \":util\",\n        \"//folly:bits\",\n        \"//folly:constexpr_math\",\n        \"//folly:likely\",\n        \"//folly:memory\",\n        \"//folly:optional\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:traits\",\n        \"//folly:unit\",\n        \"//folly/container:heterogeneous_access\",\n        \"//folly/functional:invoke\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:align\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:pretty\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/memory:malloc\",\n        \"//folly/portability:builtins\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"util\",\n    headers = [\n        \"Util.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:traits\",\n        \"//folly/container:iterator\",\n        \"//folly/functional:apply_tuple\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"f14_mask\",\n    fbandroid_exported_preprocessor_flags = FBANDROID_CPPFLAGS,\n    raw_headers = [\n        \"F14Mask.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:bits\",\n        \"//xplat/folly:constexpr_math\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly/lang:assume\",\n        \"//xplat/folly/lang:safe_assert\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"f14_defaults\",\n    headers = [\n        \"F14Defaults.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container:heterogeneous_access_fwd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"tape_detail\",\n    headers = [\n        \"tape_detail.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/container:iterator\",\n        \"//folly/container:range_traits\",\n        \"//folly/lang:hint\",\n        \"//folly/memory:uninitialized_memory_hacks\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"bool_wrapper\",\n    headers = [\n        \"BoolWrapper.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\n# !!!! fbcode/folly/container/detail/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"f14_intrinsics_availability\",\n    headers = [\n        \"F14IntrinsicsAvailability.h\",\n    ],\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"f14_mask\",\n    headers = [\n        \"F14Mask.h\",\n    ],\n    exported_deps = [\n        \":f14_intrinsics_availability\",\n        \"//folly:bits\",\n        \"//folly:constexpr_math\",\n        \"//folly:likely\",\n        \"//folly:portability\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:safe_assert\",\n    ],\n)\n"
  },
  {
    "path": "folly/container/detail/BitIteratorDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <iterator>\n#include <type_traits>\n\n#include <boost/iterator/iterator_adaptor.hpp>\n\n#include <folly/portability/SysTypes.h>\n\nnamespace folly {\n\ntemplate <class BaseIter>\nclass BitIterator;\n\nnamespace bititerator_detail {\n\n// Reference to a bit.\n// Templatize on both parent reference and value types to capture\n// const-ness correctly and to work with the case where Ref is a\n// reference-like type (not T&), just like our BitReference here.\ntemplate <class Ref, class Value>\nclass BitReference {\n public:\n  BitReference(Ref r, size_t bit) : ref_(r), bit_(bit) {}\n\n  /* implicit */ operator bool() const { return ref_ & (one_ << bit_); }\n\n  BitReference& operator=(bool b) {\n    if (b) {\n      set();\n    } else {\n      clear();\n    }\n    return *this;\n  }\n\n  void set() { ref_ |= (one_ << bit_); }\n\n  void clear() { ref_ &= ~(one_ << bit_); }\n\n  void flip() { ref_ ^= (one_ << bit_); }\n\n private:\n  // shortcut to avoid writing static_cast everywhere\n  const static Value one_ = 1;\n\n  Ref ref_;\n  size_t bit_;\n};\n\ntemplate <class BaseIter>\nstruct BitIteratorBase {\n  static_assert(\n      std::is_integral<\n          typename std::iterator_traits<BaseIter>::value_type>::value,\n      \"BitIterator may only be used with integral types\");\n  typedef boost::iterator_adaptor<\n      BitIterator<BaseIter>, // Derived\n      BaseIter, // Base\n      bool, // Value\n      typename std::iterator_traits<\n          BaseIter>::iterator_category, // CategoryOrTraversal\n      bititerator_detail::BitReference<\n          typename std::iterator_traits<BaseIter>::reference,\n          typename std::iterator_traits<BaseIter>::value_type>, // Reference\n      ssize_t>\n      type;\n};\n\n} // namespace bititerator_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/BoolWrapper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly {\nnamespace detail {\n\nclass BoolWrapper {\n public:\n  constexpr /* implicit */ BoolWrapper(bool value = false) noexcept\n      : value_(value) {}\n\n  constexpr /* implicit */ operator bool() const noexcept { return value_; }\n\n  constexpr BoolWrapper operator!() const noexcept {\n    return BoolWrapper(!value_);\n  }\n\n private:\n  bool value_;\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME bit_iterator_detail\n  HEADERS\n    BitIteratorDetail.h\n  EXPORTED_DEPS\n    folly_portability_sys_types\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME bool_wrapper\n  HEADERS\n    BoolWrapper.h\n)\n\nfolly_add_library(\n  NAME f14_defaults\n  HEADERS\n    F14Defaults.h\n  EXPORTED_DEPS\n    folly_container_heterogeneous_access_fwd\n)\n\nfolly_add_library(\n  NAME f14_hash_detail\n  SRCS\n    F14Table.cpp\n  HEADERS\n    F14MapFallback.h\n    F14Policy.h\n    F14SetFallback.h\n    F14Table.h\n  EXPORTED_DEPS\n    folly_bits\n    folly_constexpr_math\n    folly_container_detail_f14_defaults\n    folly_container_detail_f14_intrinsics_availability\n    folly_container_detail_f14_mask\n    folly_container_detail_util\n    folly_container_heterogeneous_access\n    folly_functional_invoke\n    folly_hash_hash\n    folly_lang_align\n    folly_lang_assume\n    folly_lang_exception\n    folly_lang_pretty\n    folly_lang_safe_assert\n    folly_likely\n    folly_memory\n    folly_memory_malloc\n    folly_optional\n    folly_portability\n    folly_portability_builtins\n    folly_scope_guard\n    folly_traits\n    folly_unit\n)\n\nfolly_add_library(\n  NAME f14_intrinsics_availability\n  HEADERS\n    F14IntrinsicsAvailability.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME f14_mask\n  HEADERS\n    F14Mask.h\n  EXPORTED_DEPS\n    folly_bits\n    folly_constexpr_math\n    folly_container_detail_f14_intrinsics_availability\n    folly_lang_assume\n    folly_lang_safe_assert\n    folly_likely\n    folly_portability\n)\n\nfolly_add_library(\n  NAME tape_detail\n  HEADERS\n    tape_detail.h\n  EXPORTED_DEPS\n    folly_container_iterator\n    folly_container_range_traits\n    folly_lang_hint\n    folly_memory_uninitialized_memory_hacks\n    folly_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME util\n  HEADERS\n    Util.h\n  EXPORTED_DEPS\n    folly_container_iterator\n    folly_functional_apply_tuple\n    folly_traits\n)\n"
  },
  {
    "path": "folly/container/detail/F14Defaults.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/container/HeterogeneousAccess-fwd.h>\n\nnamespace folly {\nnamespace f14 {\ntemplate <typename T>\nusing DefaultHasher = HeterogeneousAccessHash<T>;\n\ntemplate <typename T>\nusing DefaultKeyEqual = HeterogeneousAccessEqualTo<T>;\n\ntemplate <typename T>\nusing DefaultAlloc = std::allocator<T>;\n} // namespace f14\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/F14IntrinsicsAvailability.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n\n#if defined(FOLLY_F14_FALLBACK_DISABLED) && FOLLY_F14_FALLBACK_DISABLED == 1\n#define FOLLY_F14_VECTOR_INTRINSICS_CONFIGURED 1\n#elif !FOLLY_MOBILE\n#define FOLLY_F14_VECTOR_INTRINSICS_CONFIGURED 1\n#else\n#define FOLLY_F14_VECTOR_INTRINSICS_CONFIGURED 0\n#endif\n\n// F14 has been implemented for SSE2 and NEON (so far).\n//\n// This platform detection is a bit of a mess because it combines the\n// detection of supported platforms (FOLLY_SSE >= 2 || FOLLY_NEON) with\n// the selection of platforms on which we want to use it.\n//\n// Currently no 32-bit ARM versions are desired because we don't want to\n// need a separate build for chips that don't have NEON.  AARCH64 support\n// is enabled for non-mobile platforms, but on mobile platforms there\n// are downstream iteration order effects that have not yet been resolved.\n//\n// If FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE differs across compilation\n// units the program will fail to link due to a missing definition of\n// folly::container::detail::F14LinkCheck<X>::check() for some X.\n#if (FOLLY_SSE >= 2 || (FOLLY_NEON && FOLLY_AARCH64) || FOLLY_RISCV64) && \\\n    FOLLY_F14_VECTOR_INTRINSICS_CONFIGURED &&                             \\\n    !(defined(FOLLY_F14_FORCE_FALLBACK) && FOLLY_F14_FORCE_FALLBACK)\n#define FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE 1\n#else\n#define FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE 0\n#endif\n\n#if FOLLY_SSE_PREREQ(4, 2) || FOLLY_ARM_FEATURE_CRC32\n#define FOLLY_F14_CRC_INTRINSIC_AVAILABLE 1\n#else\n#define FOLLY_F14_CRC_INTRINSIC_AVAILABLE 0\n#endif\n\nnamespace folly {\nnamespace f14 {\nnamespace detail {\n\nenum class F14IntrinsicsMode { None, Simd, SimdAndCrc };\n\nstatic constexpr F14IntrinsicsMode getF14IntrinsicsMode() {\n#if !FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n  return F14IntrinsicsMode::None;\n#elif !FOLLY_F14_CRC_INTRINSIC_AVAILABLE\n  return F14IntrinsicsMode::Simd;\n#else\n  return F14IntrinsicsMode::SimdAndCrc;\n#endif\n}\n\n} // namespace detail\n} // namespace f14\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/F14MapFallback.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <type_traits>\n#include <unordered_map>\n\n#include <folly/Optional.h>\n#include <folly/lang/Assume.h>\n\n#include <folly/container/detail/F14Table.h>\n#include <folly/container/detail/Util.h>\n\n/**\n * This file is intended to be included only by F14Map.h. It contains fallback\n * implementations of F14Map types for platforms that do not support the\n * required SIMD instructions, based on std::unordered_map.\n */\n\n#if !FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nnamespace folly {\n\nnamespace f14 {\nnamespace detail {\ntemplate <typename K, typename M, typename H, typename E, typename A>\nclass F14BasicMap : public std::unordered_map<K, M, H, E, A> {\n  using Super = std::unordered_map<K, M, H, E, A>;\n\n  template <typename K2, typename T>\n  using EnableHeterogeneousFind = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<K, H, E, K2>::value,\n      T>;\n\n  template <typename K2, typename T>\n  using EnableHeterogeneousInsert = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousInsert<K, H, E, K2>::value,\n      T>;\n\n  template <typename K2>\n  using IsIter = Disjunction<\n      std::is_same<typename Super::iterator, remove_cvref_t<K2>>,\n      std::is_same<typename Super::const_iterator, remove_cvref_t<K2>>>;\n\n  template <typename K2, typename T>\n  using EnableHeterogeneousErase = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          K,\n          H,\n          E,\n          std::conditional_t<IsIter<K2>::value, K, K2>>::value &&\n          !IsIter<K2>::value,\n      T>;\n\n public:\n  using typename Super::const_iterator;\n  using typename Super::hasher;\n  using typename Super::iterator;\n  using typename Super::key_equal;\n  using typename Super::key_type;\n  using typename Super::mapped_type;\n  using typename Super::pointer;\n  using typename Super::size_type;\n  using typename Super::value_type;\n\n  F14BasicMap() = default;\n\n  using Super::Super;\n\n  //// PUBLIC - Modifiers\n\n  std::pair<iterator, bool> insert(value_type const& value) {\n    return emplace(value);\n  }\n\n  template <typename P>\n  std::enable_if_t<\n      std::is_constructible<value_type, P&&>::value,\n      std::pair<iterator, bool>>\n  insert(P&& value) {\n    return emplace(std::forward<P>(value));\n  }\n\n  // TODO(T31574848): Work around libstdc++ versions (e.g., GCC < 6) with no\n  // implementation of N4387 (\"perfect initialization\" for pairs and tuples).\n  template <typename U1, typename U2>\n  std::enable_if_t<\n      std::is_constructible<key_type, U1 const&>::value &&\n          std::is_constructible<mapped_type, U2 const&>::value,\n      std::pair<iterator, bool>>\n  insert(std::pair<U1, U2> const& value) {\n    return emplace(value);\n  }\n\n  // TODO(T31574848)\n  template <typename U1, typename U2>\n  std::enable_if_t<\n      std::is_constructible<key_type, U1&&>::value &&\n          std::is_constructible<mapped_type, U2&&>::value,\n      std::pair<iterator, bool>>\n  insert(std::pair<U1, U2>&& value) {\n    return emplace(std::move(value));\n  }\n\n  std::pair<iterator, bool> insert(value_type&& value) {\n    return emplace(std::move(value));\n  }\n\n  iterator insert(const_iterator /*hint*/, value_type const& value) {\n    return insert(value).first;\n  }\n\n  template <typename P>\n  std::enable_if_t<std::is_constructible<value_type, P&&>::value, iterator>\n  insert(const_iterator /*hint*/, P&& value) {\n    return insert(std::forward<P>(value)).first;\n  }\n\n  iterator insert(const_iterator /*hint*/, value_type&& value) {\n    return insert(std::move(value)).first;\n  }\n\n  template <class... Args>\n  iterator emplace_hint(const_iterator /*hint*/, Args&&... args) {\n    return emplace(std::forward<Args>(args)...).first;\n  }\n\n  template <class InputIt>\n  void insert(InputIt first, InputIt last) {\n    while (first != last) {\n      insert(*first);\n      ++first;\n    }\n  }\n\n  void insert(std::initializer_list<value_type> ilist) {\n    insert(ilist.begin(), ilist.end());\n  }\n\n  template <typename M2>\n  std::pair<iterator, bool> insert_or_assign(key_type const& key, M2&& obj) {\n    auto rv = try_emplace(key, std::forward<M2>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M2>(obj);\n    }\n    return rv;\n  }\n\n  template <typename M2>\n  std::pair<iterator, bool> insert_or_assign(key_type&& key, M2&& obj) {\n    auto rv = try_emplace(std::move(key), std::forward<M2>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M2>(obj);\n    }\n    return rv;\n  }\n\n  template <typename M2>\n  iterator insert_or_assign(\n      const_iterator /*hint*/, key_type const& key, M2&& obj) {\n    return insert_or_assign(key, std::forward<M2>(obj)).first;\n  }\n\n  template <typename M2>\n  iterator insert_or_assign(const_iterator /*hint*/, key_type&& key, M2&& obj) {\n    return insert_or_assign(std::move(key), std::forward<M2>(obj)).first;\n  }\n\n  template <typename K2, typename M2>\n  EnableHeterogeneousInsert<K2, std::pair<iterator, bool>> insert_or_assign(\n      K2&& key, M2&& obj) {\n    auto rv = try_emplace(std::forward<K2>(key), std::forward<M2>(obj));\n    if (!rv.second) {\n      rv.first->second = std::forward<M2>(obj);\n    }\n    return rv;\n  }\n\n private:\n  template <typename Arg>\n  using UsableAsKey = ::folly::detail::\n      EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;\n\n public:\n  template <typename... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    auto alloc = this->get_allocator();\n    return folly::detail::\n        callWithExtractedKey<key_type, mapped_type, UsableAsKey>(\n            alloc,\n            [&](auto& key, auto&&... inner) {\n              auto it = find(key);\n              if (it != this->end()) {\n                return std::make_pair(it, false);\n              }\n              auto rv = Super::emplace(std::forward<decltype(inner)>(inner)...);\n              FOLLY_SAFE_DCHECK(\n                  rv.second, \"post-find emplace should always insert\");\n              return rv;\n            },\n            std::forward<Args>(args)...);\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace(key_type const& key, Args&&... args) {\n    return emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(key),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace(key_type&& key, Args&&... args) {\n    return emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(std::move(key)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n  }\n\n  template <typename... Args>\n  iterator try_emplace(\n      const_iterator /*hint*/, key_type const& key, Args&&... args) {\n    return emplace(\n               std::piecewise_construct,\n               std::forward_as_tuple(key),\n               std::forward_as_tuple(std::forward<Args>(args)...))\n        .first;\n  }\n\n  template <typename... Args>\n  iterator try_emplace(\n      const_iterator /*hint*/, key_type&& key, Args&&... args) {\n    return emplace(\n               std::piecewise_construct,\n               std::forward_as_tuple(std::move(key)),\n               std::forward_as_tuple(std::forward<Args>(args)...))\n        .first;\n  }\n\n  template <typename K2, typename... Args>\n  EnableHeterogeneousInsert<K2, std::pair<iterator, bool>> try_emplace(\n      K2&& key, Args&&... args) {\n    return emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(std::forward<K2>(key)),\n        std::forward_as_tuple(std::forward<Args>(args)...));\n  }\n\n  using Super::erase;\n\n  template <typename K2>\n  EnableHeterogeneousErase<K2, size_type> erase(K2 const& key) {\n    auto it = find(key);\n    if (it != this->end()) {\n      erase(it);\n      return 1;\n    } else {\n      return 0;\n    }\n  }\n\n  //// PUBLIC - Lookup\n\n private:\n  template <typename K2>\n  struct BottomKeyEqual {\n    [[noreturn]] bool operator()(K2 const&, K2 const&) const {\n      assume_unreachable();\n    }\n  };\n\n  template <typename Self, typename K2>\n  static auto findLocal(Self& self, K2 const& key)\n      -> folly::Optional<decltype(self.begin(0))> {\n    if (self.empty()) {\n      return none;\n    }\n    using A2 = typename std::allocator_traits<A>::template rebind_alloc<\n        std::pair<K2 const, M>>;\n    using E2 = BottomKeyEqual<K2>;\n    // this is exceedingly wicked!\n    auto slot =\n        reinterpret_cast<std::unordered_map<K2, M, H, E2, A2> const&>(self)\n            .bucket(key);\n    auto b = self.begin(slot);\n    auto e = self.end(slot);\n    while (b != e) {\n      if (self.key_eq()(key, b->first)) {\n        return b;\n      }\n      ++b;\n    }\n    FOLLY_SAFE_DCHECK(\n        self.size() > 3 ||\n            std::none_of(\n                self.begin(),\n                self.end(),\n                [&](auto const& kv) { return self.key_eq()(key, kv.first); }),\n        \"\");\n    return none;\n  }\n\n  template <typename Self, typename K2>\n  static auto& atImpl(Self& self, K2 const& key) {\n    auto it = findLocal(self, key);\n    if (!it) {\n      throw_exception<std::out_of_range>(\"at() did not find key\");\n    }\n    return (*it)->second;\n  }\n\n public:\n  mapped_type& at(key_type const& key) { return Super::at(key); }\n\n  mapped_type const& at(key_type const& key) const { return Super::at(key); }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, mapped_type&> at(K2 const& key) {\n    return atImpl(*this, key);\n  }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, mapped_type const&> at(K2 const& key) const {\n    return atImpl(*this, key);\n  }\n\n  using Super::operator[];\n\n  template <typename K2>\n  EnableHeterogeneousInsert<K2, mapped_type&> operator[](K2&& key) {\n    return try_emplace(std::forward<K2>(key)).first->second;\n  }\n\n  size_type count(key_type const& key) const { return Super::count(key); }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, size_type> count(K2 const& key) const {\n    return !findLocal(*this, key) ? 0 : 1;\n  }\n\n  bool contains(key_type const& key) const { return count(key) != 0; }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, bool> contains(K2 const& key) const {\n    return count(key) != 0;\n  }\n\n private:\n  template <typename Iter, typename LocalIter>\n  static std::\n      enable_if_t<std::is_constructible<Iter, LocalIter const&>::value, Iter>\n      fromLocal(LocalIter const& src, int = 0) {\n    return Iter(src);\n  }\n\n  template <typename Iter, typename LocalIter>\n  static std::\n      enable_if_t<!std::is_constructible<Iter, LocalIter const&>::value, Iter>\n      fromLocal(LocalIter const& src) {\n    Iter dst;\n    static_assert(sizeof(dst) <= sizeof(src));\n    std::memcpy(std::addressof(dst), std::addressof(src), sizeof(dst));\n    FOLLY_SAFE_CHECK(\n        std::addressof(*src) == std::addressof(*dst),\n        \"ABI-assuming local_iterator to iterator conversion failed\");\n    return dst;\n  }\n\n  template <typename Iter, typename Self, typename K2>\n  static Iter findImpl(Self& self, K2 const& key) {\n    auto optLocalIt = findLocal(self, key);\n    if (!optLocalIt) {\n      return self.end();\n    } else {\n      return fromLocal<Iter>(*optLocalIt);\n    }\n  }\n\n public:\n  iterator find(key_type const& key) { return Super::find(key); }\n\n  const_iterator find(key_type const& key) const { return Super::find(key); }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, iterator> find(K2 const& key) {\n    return findImpl<iterator>(*this, key);\n  }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, const_iterator> find(K2 const& key) const {\n    return findImpl<const_iterator>(*this, key);\n  }\n\n private:\n  template <typename Self, typename K2>\n  static auto equalRangeImpl(Self& self, K2 const& key) {\n    auto first = self.find(key);\n    auto last = first;\n    if (last != self.end()) {\n      ++last;\n    }\n    return std::make_pair(first, last);\n  }\n\n public:\n  using Super::equal_range;\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, std::pair<iterator, iterator>> equal_range(\n      K2 const& key) {\n    return equalRangeImpl(*this, key);\n  }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, std::pair<const_iterator, const_iterator>>\n  equal_range(K2 const& key) const {\n    return equalRangeImpl(*this, key);\n  }\n\n  //// PUBLIC - F14 Extensions\n\n  template <typename BeforeDestroy>\n  iterator eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {\n    iterator it = erase(pos, pos);\n    FOLLY_SAFE_CHECK(std::addressof(*it) == std::addressof(*pos), \"\");\n    return eraseInto(it, beforeDestroy);\n  }\n\n  template <typename BeforeDestroy>\n  iterator eraseInto(iterator pos, BeforeDestroy&& beforeDestroy) {\n    const_iterator prev{pos};\n    ++pos;\n    auto nh = this->extract(prev);\n    FOLLY_SAFE_CHECK(!nh.empty(), \"\");\n    beforeDestroy(std::move(nh.key()), std::move(nh.mapped()));\n    return pos;\n  }\n\n  template <typename BeforeDestroy>\n  iterator eraseInto(\n      const_iterator first,\n      const_iterator last,\n      BeforeDestroy&& beforeDestroy) {\n    iterator pos = erase(first, first);\n    FOLLY_SAFE_CHECK(std::addressof(*pos) == std::addressof(*first), \"\");\n    while (pos != last) {\n      pos = eraseInto(pos, beforeDestroy);\n    }\n    return pos;\n  }\n\n private:\n  template <typename K2, typename BeforeDestroy>\n  size_type eraseIntoImpl(K2 const& key, BeforeDestroy& beforeDestroy) {\n    auto it = find(key);\n    if (it != this->end()) {\n      eraseInto(it, beforeDestroy);\n      return 1;\n    } else {\n      return 0;\n    }\n  }\n\n public:\n  template <typename BeforeDestroy>\n  size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseIntoImpl(key, beforeDestroy);\n  }\n\n  template <typename K2, typename BeforeDestroy>\n  EnableHeterogeneousErase<K2, size_type> eraseInto(\n      K2 const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseIntoImpl(key, beforeDestroy);\n  }\n\n  bool containsEqualValue(value_type const& value) const {\n    // bucket isn't valid if bucket_count is zero\n    if (this->empty()) {\n      return false;\n    }\n    auto slot = this->bucket(value.first);\n    auto e = this->end(slot);\n    for (auto b = this->begin(slot); b != e; ++b) {\n      if (b->first == value.first) {\n        return b->second == value.second;\n      }\n    }\n    return false;\n  }\n\n  // exact for libstdc++, approximate for others\n  std::size_t getAllocatedMemorySize() const {\n    std::size_t rv = 0;\n    visitAllocationClasses([&](std::size_t bytes, std::size_t n) {\n      rv += bytes * n;\n    });\n    return rv;\n  }\n\n  // exact for libstdc++, approximate for others\n  template <typename V>\n  void visitAllocationClasses(V&& visitor) const {\n    auto bc = this->bucket_count();\n    if (bc > 1) {\n      visitor(bc * sizeof(pointer), 1);\n    }\n    if (this->size() > 0) {\n      visitor(sizeof(StdNodeReplica<K, value_type, H>), this->size());\n    }\n  }\n\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    for (value_type const& entry : *this) {\n      value_type const* b = std::addressof(entry);\n      visitor(b, b + 1);\n    }\n  }\n\n  /// F14HashToken interface\n  template <typename V>\n  std::pair<iterator, bool> insert_or_assign(\n      F14HashToken const&, key_type const& key, V&& obj) {\n    return insert_or_assign(key, std::forward<V>(obj));\n  }\n\n  template <typename V>\n  std::pair<iterator, bool> insert_or_assign(\n      F14HashToken const&, key_type&& key, V&& obj) {\n    return insert_or_assign(std::move(key), std::forward<V>(obj));\n  }\n\n  template <typename K2, typename V>\n  EnableHeterogeneousInsert<K2, std::pair<iterator, bool>> insert_or_assign(\n      F14HashToken const&, K2&& key, V&& obj) {\n    return insert_or_assign(std::forward<K2>(key), std::forward<V>(obj));\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace_token(\n      F14HashToken const&, key_type const& key, Args&&... args) {\n    return try_emplace(key, std::forward<Args>(args)...);\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace_token(\n      F14HashToken const&, key_type&& key, Args&&... args) {\n    return try_emplace(std::move(key), std::forward<Args>(args)...);\n  }\n\n  template <typename K2, typename... Args>\n  EnableHeterogeneousInsert<K2, std::pair<iterator, bool>> try_emplace_token(\n      F14HashToken const&, K2&& key, Args&&... args) {\n    return try_emplace(std::forward<K2>(key), std::forward<Args>(args)...);\n  }\n\n  F14HashToken prehash(key_type const&) const {\n    return {}; // Ignored.\n  }\n  F14HashToken prehash(key_type const&, std::size_t) const {\n    return {}; // Ignored.\n  }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, F14HashToken> prehash(K2 const&) const {\n    return {}; // Ignored.\n  }\n  template <typename K2>\n  EnableHeterogeneousFind<K2, F14HashToken> prehash(\n      K2 const&, std::size_t) const {\n    return {}; // Ignored.\n  }\n\n  void prefetch(F14HashToken const&) const {}\n\n  iterator find(F14HashToken const&, key_type const& key) { return find(key); }\n\n  const_iterator find(F14HashToken const&, key_type const& key) const {\n    return find(key);\n  }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, iterator> find(\n      F14HashToken const&, K2 const& key) {\n    return find(key);\n  }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, const_iterator> find(\n      F14HashToken const&, K2 const& key) const {\n    return find(key);\n  }\n\n  bool contains(F14HashToken const&, key_type const& key) const {\n    return contains(key);\n  }\n\n  template <typename K2>\n  EnableHeterogeneousFind<K2, bool> contains(\n      F14HashToken const&, K2 const& key) const {\n    return contains(key);\n  }\n};\n} // namespace detail\n} // namespace f14\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14ValueMap\n    : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::value_type;\n\n  F14ValueMap() = default;\n\n  using Super::Super;\n\n  F14ValueMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14NodeMap\n    : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::value_type;\n\n  F14NodeMap() = default;\n\n  using Super::Super;\n\n  F14NodeMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14VectorMap\n    : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::const_iterator;\n  using typename Super::iterator;\n  using typename Super::value_type;\n\n  F14VectorMap() = default;\n\n  using Super::Super;\n\n  F14VectorMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc>\nclass F14FastMap\n    : public f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicMap<Key, Mapped, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::value_type;\n\n  F14FastMap() = default;\n\n  using Super::Super;\n\n  F14FastMap& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\n} // namespace folly\n\n#endif // !if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n"
  },
  {
    "path": "folly/container/detail/F14Mask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cstdint>\n\n#include <folly/Bits.h>\n#include <folly/ConstexprMath.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/container/detail/F14IntrinsicsAvailability.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/SafeAssert.h>\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nnamespace folly {\nnamespace f14 {\nnamespace detail {\n\ntemplate <typename T>\nFOLLY_ALWAYS_INLINE static unsigned findFirstSetNonZero(T mask) {\n  assume(mask != 0);\n  if (sizeof(mask) == sizeof(unsigned)) {\n    return __builtin_ctz(static_cast<unsigned>(mask));\n  } else {\n    return __builtin_ctzll(mask);\n  }\n}\n\ntemplate <typename T>\nFOLLY_ALWAYS_INLINE static unsigned findLastSetNonZero(T mask) {\n  assume(mask != 0);\n  if (sizeof(mask) == sizeof(unsigned)) {\n    return __builtin_clz(static_cast<unsigned>(mask));\n  } else {\n    return __builtin_clzll(mask);\n  }\n}\n\n#if FOLLY_NEON\nusing MaskType = uint64_t;\n\nconstexpr unsigned kMaskSpacing = 4;\n#else // FOLLY_SSE >= 2 || FOLLY_RISCV64\nusing MaskType = uint32_t;\n\nconstexpr unsigned kMaskSpacing = 1;\n#endif\n\ntemplate <unsigned BitCount>\nstruct FullMask {\n  static constexpr MaskType value =\n      (FullMask<BitCount - 1>::value << kMaskSpacing) + 1;\n};\n\ntemplate <>\nstruct FullMask<1> : std::integral_constant<MaskType, 1> {};\n\n#if FOLLY_ARM\n// Mask iteration is different for ARM because that is the only platform\n// for which the mask is bigger than a register.\n\n// Iterates a mask, optimized for the case that only a few bits are set\nclass SparseMaskIter {\n  static_assert(kMaskSpacing == 4);\n\n  uint32_t interleavedMask_;\n\n public:\n  explicit SparseMaskIter(MaskType mask)\n      : interleavedMask_{static_cast<uint32_t>(((mask >> 32) << 2) | mask)} {}\n\n  bool hasNext() { return interleavedMask_ != 0; }\n\n  unsigned next() {\n    FOLLY_SAFE_DCHECK(hasNext(), \"\");\n    unsigned i = findFirstSetNonZero(interleavedMask_);\n    interleavedMask_ &= (interleavedMask_ - 1);\n    return ((i >> 2) | (i << 2)) & 0xf;\n  }\n};\n\n// Iterates a mask, optimized for the case that most bits are set\nclass DenseMaskIter {\n  static_assert(kMaskSpacing == 4);\n\n  std::size_t count_;\n  unsigned index_;\n  uint8_t const* tags_;\n\n public:\n  explicit DenseMaskIter(uint8_t const* tags, MaskType mask) {\n    if (mask == 0) {\n      count_ = 0;\n    } else {\n      count_ = popcount(static_cast<uint32_t>(((mask >> 32) << 2) | mask));\n      if (FOLLY_LIKELY((mask & 1) != 0)) {\n        index_ = 0;\n      } else {\n        index_ = findFirstSetNonZero(mask) / kMaskSpacing;\n      }\n      tags_ = tags;\n    }\n  }\n\n  bool hasNext() { return count_ > 0; }\n\n  unsigned next() {\n    auto rv = index_;\n    --count_;\n    if (count_ > 0) {\n      do {\n        ++index_;\n      } while ((tags_[index_] & 0x80) == 0);\n    }\n    FOLLY_SAFE_DCHECK(index_ < 16, \"\");\n    return rv;\n  }\n};\n\n#else\n// Iterates a mask, optimized for the case that only a few bits are set\nclass SparseMaskIter {\n  MaskType mask_;\n\n public:\n\n#if FOLLY_AARCH64\n  explicit SparseMaskIter(MaskType mask) : mask_{bitReverse(mask)} {}\n#else\n  explicit SparseMaskIter(MaskType mask) : mask_{mask} {}\n#endif\n\n  bool hasNext() { return mask_ != 0; }\n\n  unsigned next() {\n    FOLLY_SAFE_DCHECK(hasNext(), \"\");\n    constexpr uint64_t lo63 = 0x7FFFFFFFFFFFFFFFull;\n    static_assert(lo63 == (~0ull >> 1));\n    unsigned i =\n        kIsArchAArch64 ? findLastSetNonZero(mask_) : findFirstSetNonZero(mask_);\n    mask_ &= kIsArchAArch64 ? (lo63 >> i) : (mask_ - 1);\n    if constexpr (kIsArchAArch64 && (kMaskSpacing == 4)) {\n      // The result of this function is often used as an index on an 8-byte\n      // element array. In this case, the index needs to be shifted left by 3 to\n      // access the desired memory position. The return statement of this\n      // function contains i >> 2. The compiler is simplifying the shifts by\n      // only issuing a lsl 1 while ommitting the lsr 2. However, it then ANDs\n      // the shifted value by 0xf8, to ensure correctness when i is not a\n      // multiple of 4. We do know that i will always be a multiple of 4. We add\n      // the assume clause so the compiler avoids emitting the &0xf8\n      auto loadIndex = i << 1;\n      assume(loadIndex == (loadIndex & 0xf8));\n    }\n    return i / kMaskSpacing;\n  }\n};\n\n// A variant of SparseMaskIter but using tzcnt (x86-64/bmi1) which is more\n// efficient.\ntemplate <unsigned BitCount>\nclass BoundedMaskIter {\n  MaskType mask_;\n\n public:\n  explicit BoundedMaskIter(MaskType mask) : mask_{mask} {}\n\n  bool hasNext() {\n    unsigned firstSet =\n        mask_ == 0 ? (sizeof(MaskType) * 8) : findFirstSetNonZero(mask_);\n    return firstSet < BitCount;\n  }\n\n  unsigned next() {\n    FOLLY_SAFE_DCHECK(hasNext());\n    unsigned i = findFirstSetNonZero(mask_);\n    mask_ &= mask_ - 1;\n    return i;\n  }\n};\n\n// Iterates a mask, optimized for the case that most bits are set\nclass DenseMaskIter {\n  MaskType mask_;\n  unsigned index_{0};\n\n public:\n  explicit DenseMaskIter(uint8_t const*, MaskType mask) : mask_{mask} {}\n\n  bool hasNext() { return mask_ != 0; }\n\n  unsigned next() {\n    FOLLY_SAFE_DCHECK(hasNext(), \"\");\n    if (FOLLY_LIKELY((mask_ & 1) != 0)) {\n      mask_ >>= kMaskSpacing;\n      return index_++;\n    } else {\n      unsigned s = findFirstSetNonZero(mask_);\n      unsigned rv = index_ + (s / kMaskSpacing);\n      mask_ >>= (s + kMaskSpacing);\n      index_ = rv + 1;\n      return rv;\n    }\n  }\n};\n#endif\n\n// Iterates a mask, returning pairs of [begin,end) index covering blocks\n// of set bits\nclass MaskRangeIter {\n  MaskType mask_;\n  unsigned shift_{0};\n\n public:\n  explicit MaskRangeIter(MaskType mask) {\n    // If kMaskSpacing is > 1 then there will be empty bits even for\n    // contiguous ranges.  Fill them in.\n    mask_ = mask * ((1 << kMaskSpacing) - 1);\n  }\n\n  bool hasNext() { return mask_ != 0; }\n\n  std::pair<unsigned, unsigned> next() {\n    FOLLY_SAFE_DCHECK(hasNext(), \"\");\n    auto s = shift_;\n    unsigned b = findFirstSetNonZero(mask_);\n    unsigned e = findFirstSetNonZero(~(mask_ | (mask_ - 1)));\n    mask_ >>= e;\n    shift_ = s + e;\n    return std::make_pair((s + b) / kMaskSpacing, (s + e) / kMaskSpacing);\n  }\n};\n\n// Holds the result of an index query that has an optional result,\n// interpreting a mask of 0 to be the empty answer and the index of the\n// last set bit to be the non-empty answer\nclass LastOccupiedInMask {\n  MaskType mask_;\n\n public:\n  explicit LastOccupiedInMask(MaskType mask) : mask_{mask} {}\n\n  bool hasIndex() const { return mask_ != 0; }\n\n  unsigned index() const {\n    assume(mask_ != 0);\n    return (findLastSet(mask_) - 1) / kMaskSpacing;\n  }\n};\n\n// Holds the result of an index query that has an optional result,\n// interpreting a mask of 0 to be the empty answer and the index of the\n// first set bit to be the non-empty answer\nclass FirstEmptyInMask {\n  MaskType mask_;\n\n public:\n  explicit FirstEmptyInMask(MaskType mask) : mask_{mask} {}\n\n  bool hasIndex() const { return mask_ != 0; }\n\n  unsigned index() const {\n    FOLLY_SAFE_DCHECK(mask_ != 0, \"\");\n    return findFirstSetNonZero(mask_) / kMaskSpacing;\n  }\n};\n\n} // namespace detail\n} // namespace f14\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/container/detail/F14Policy.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <new>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Memory.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Unit.h>\n#include <folly/container/HeterogeneousAccess.h>\n#include <folly/container/detail/F14Table.h>\n#include <folly/hash/Hash.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/memory/Malloc.h>\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nnamespace folly {\nnamespace f14 {\nnamespace detail {\n\ntemplate <typename Ptr>\nusing NonConstPtr = typename std::pointer_traits<Ptr>::template rebind<\n    std::remove_const_t<typename std::pointer_traits<Ptr>::element_type>>;\n\ntemplate <typename KeyType, typename MappedType>\nusing MapValueType = std::pair<KeyType const, MappedType>;\n\ntemplate <typename KeyType, typename MappedTypeOrVoid>\nusing SetOrMapValueType = std::conditional_t<\n    std::is_same<MappedTypeOrVoid, void>::value,\n    KeyType,\n    MapValueType<KeyType, MappedTypeOrVoid>>;\n\ntemplate <typename T>\nusing IsNothrowMoveAndDestroy = Conjunction<\n    std::is_nothrow_move_constructible<T>,\n    std::is_nothrow_destructible<T>>;\n\n// Used to enable EBO for Hasher, KeyEqual, and Alloc.  std::tuple of\n// all empty objects is empty in libstdc++ but not libc++.\ntemplate <\n    char Tag,\n    typename T,\n    bool Inherit = std::is_empty<T>::value && !std::is_final<T>::value>\nstruct ObjectHolder {\n  T value_;\n\n  template <typename... Args>\n  ObjectHolder(Args&&... args) : value_{std::forward<Args>(args)...} {}\n\n  T& operator*() { return value_; }\n  T const& operator*() const { return value_; }\n};\n\ntemplate <char Tag, typename T>\nstruct ObjectHolder<Tag, T, true> : T {\n  template <typename... Args>\n  ObjectHolder(Args&&... args) : T{std::forward<Args>(args)...} {}\n\n  T& operator*() { return *this; }\n  T const& operator*() const { return *this; }\n};\n\n// Policy provides the functionality of hasher, key_equal, and\n// allocator_type.  In addition, it can add indirection to the values\n// contained in the base table by defining a non-trivial value() method.\n//\n// To facilitate stateful implementations it is guaranteed that there\n// will be a 1:1 relationship between BaseTable and Policy instance:\n// policies will only be copied when their owning table is copied, and\n// they will only be moved when their owning table is moved.\n//\n// Key equality will have the user-supplied search key as its first\n// argument and the table contents as its second.  Heterogeneous lookup\n// should be handled on the first argument.\n//\n// Item is the data stored inline in the hash table's chunks.  The policy\n// controls how this is mapped to the corresponding Value.\n//\n// The policies defined in this file work for either set or map types.\n// Most of the functionality is identical. A few methods detect the\n// collection type by checking to see if MappedType is void, and then use\n// SFINAE to select the appropriate implementation.\ntemplate <\n    typename KeyType,\n    typename MappedTypeOrVoid,\n    typename HasherOrVoid,\n    typename KeyEqualOrVoid,\n    typename AllocOrVoid,\n    typename ItemType>\nstruct FOLLY_MSVC_DECLSPEC(empty_bases) BasePolicy\n    : private ObjectHolder<\n          'H',\n          Defaulted<HasherOrVoid, DefaultHasher<KeyType>>>,\n      private ObjectHolder<\n          'E',\n          Defaulted<KeyEqualOrVoid, DefaultKeyEqual<KeyType>>>,\n      private ObjectHolder<\n          'A',\n          Defaulted<\n              AllocOrVoid,\n              DefaultAlloc<SetOrMapValueType<KeyType, MappedTypeOrVoid>>>> {\n  //////// user-supplied types\n\n  using Key = KeyType;\n  using Mapped = MappedTypeOrVoid;\n  using Value = SetOrMapValueType<Key, Mapped>;\n  using Item = ItemType;\n  using Hasher = Defaulted<HasherOrVoid, DefaultHasher<Key>>;\n  using KeyEqual = Defaulted<KeyEqualOrVoid, DefaultKeyEqual<Key>>;\n  using Alloc = Defaulted<AllocOrVoid, DefaultAlloc<Value>>;\n  using AllocTraits = std::allocator_traits<Alloc>;\n\n  using ByteAlloc = typename AllocTraits::template rebind_alloc<uint8_t>;\n  using ByteAllocTraits = typename std::allocator_traits<ByteAlloc>;\n  using BytePtr = typename ByteAllocTraits::pointer;\n\n  //////// info about user-supplied types\n\n  static_assert(\n      std::is_same<typename AllocTraits::value_type, Value>::value,\n      \"wrong allocator value_type\");\n\n private:\n  using HasherHolder = ObjectHolder<'H', Hasher>;\n  using KeyEqualHolder = ObjectHolder<'E', KeyEqual>;\n  using AllocHolder = ObjectHolder<'A', Alloc>;\n\n  // emulate c++17's std::allocator_traits<A>::is_always_equal\n\n  template <typename A, typename = void>\n  struct AllocIsAlwaysEqual : std::is_empty<A> {};\n\n  template <typename A>\n  struct AllocIsAlwaysEqual<A, typename A::is_always_equal>\n      : A::is_always_equal {};\n\n public:\n  static constexpr bool kAllocIsAlwaysEqual = AllocIsAlwaysEqual<Alloc>::value;\n\n  static constexpr bool kDefaultConstructIsNoexcept =\n      std::is_nothrow_default_constructible<Hasher>::value &&\n      std::is_nothrow_default_constructible<KeyEqual>::value &&\n      std::is_nothrow_default_constructible<Alloc>::value;\n\n  static constexpr bool kSwapIsNoexcept = kAllocIsAlwaysEqual &&\n      std::is_nothrow_swappable_v<Hasher> &&\n      std::is_nothrow_swappable_v<KeyEqual>;\n\n  static constexpr bool isAvalanchingHasher() {\n    return IsAvalanchingHasher<Hasher, Key>::value;\n  }\n\n  static constexpr bool shouldAssume32BitHash() {\n    return ShouldAssume32BitHash<Hasher>::value;\n  }\n\n  //////// internal types and constants\n\n  using InternalSizeType = std::size_t;\n\n  // if false, F14Table will be smaller but F14Table::begin() won't work\n  static constexpr bool kEnableItemIteration = true;\n\n  using Chunk = F14Chunk<Item>;\n  using ChunkPtr = typename std::pointer_traits<\n      typename AllocTraits::pointer>::template rebind<Chunk>;\n  using ItemIter = F14ItemIter<ChunkPtr>;\n\n  static constexpr std::size_t kChunkAllocAlignment = alignof(Chunk);\n\n  static constexpr bool kIsMap = !std::is_same<Key, Value>::value;\n  static_assert(\n      kIsMap == !std::is_void<MappedTypeOrVoid>::value,\n      \"Assumption for the kIsMap check violated.\");\n\n  using MappedOrBool = std::conditional_t<kIsMap, Mapped, bool>;\n\n  // if true, bucket_count() after reserve(n) will be as close as possible\n  // to n for multi-chunk tables\n  static constexpr bool kContinuousCapacity = false;\n\n  //////// methods\n\n  BasePolicy(Hasher const& hasher, KeyEqual const& keyEqual, Alloc const& alloc)\n      : HasherHolder{hasher}, KeyEqualHolder{keyEqual}, AllocHolder{alloc} {}\n\n  BasePolicy(BasePolicy const& rhs)\n      : HasherHolder{rhs.hasher()},\n        KeyEqualHolder{rhs.keyEqual()},\n        AllocHolder{\n            AllocTraits::select_on_container_copy_construction(rhs.alloc())} {}\n\n  BasePolicy(BasePolicy const& rhs, Alloc const& alloc)\n      : HasherHolder{rhs.hasher()},\n        KeyEqualHolder{rhs.keyEqual()},\n        AllocHolder{alloc} {}\n\n  BasePolicy(BasePolicy&& rhs) noexcept\n      : HasherHolder{std::move(rhs.hasher())},\n        KeyEqualHolder{std::move(rhs.keyEqual())},\n        AllocHolder{std::move(rhs.alloc())} {}\n\n  BasePolicy(BasePolicy&& rhs, Alloc const& alloc) noexcept\n      : HasherHolder{std::move(rhs.hasher())},\n        KeyEqualHolder{std::move(rhs.keyEqual())},\n        AllocHolder{alloc} {}\n\n private:\n  template <typename Src>\n  void maybeAssignAlloc(std::true_type, Src&& src) {\n    alloc() = std::forward<Src>(src);\n  }\n\n  template <typename Src>\n  void maybeAssignAlloc(std::false_type, Src&&) {}\n\n  template <typename A>\n  void maybeSwapAlloc(std::true_type, A& rhs) {\n    using std::swap;\n    swap(alloc(), rhs);\n  }\n\n  template <typename A>\n  void maybeSwapAlloc(std::false_type, A&) {}\n\n public:\n  BasePolicy& operator=(BasePolicy const& rhs) {\n    hasher() = rhs.hasher();\n    keyEqual() = rhs.keyEqual();\n    maybeAssignAlloc(\n        typename AllocTraits::propagate_on_container_copy_assignment{},\n        rhs.alloc());\n    return *this;\n  }\n\n  BasePolicy& operator=(BasePolicy&& rhs) noexcept {\n    hasher() = std::move(rhs.hasher());\n    keyEqual() = std::move(rhs.keyEqual());\n    maybeAssignAlloc(\n        typename AllocTraits::propagate_on_container_move_assignment{},\n        std::move(rhs.alloc()));\n    return *this;\n  }\n\n  void swapBasePolicy(BasePolicy& rhs) {\n    using std::swap;\n    swap(hasher(), rhs.hasher());\n    swap(keyEqual(), rhs.keyEqual());\n    maybeSwapAlloc(\n        typename AllocTraits::propagate_on_container_swap{}, rhs.alloc());\n  }\n\n  Hasher& hasher() { return *static_cast<HasherHolder&>(*this); }\n  Hasher const& hasher() const {\n    return *static_cast<HasherHolder const&>(*this);\n  }\n  KeyEqual& keyEqual() { return *static_cast<KeyEqualHolder&>(*this); }\n  KeyEqual const& keyEqual() const {\n    return *static_cast<KeyEqualHolder const&>(*this);\n  }\n  Alloc& alloc() { return *static_cast<AllocHolder&>(*this); }\n  Alloc const& alloc() const { return *static_cast<AllocHolder const&>(*this); }\n\n  template <typename K>\n  std::size_t computeKeyHash(K const& key) const {\n    static_assert(\n        isAvalanchingHasher() == IsAvalanchingHasher<Hasher, K>::value);\n    static_assert(\n        !isAvalanchingHasher() ||\n            sizeof(decltype(hasher()(key))) >= sizeof(std::size_t),\n        \"hasher is not avalanching if it doesn't return enough bits\");\n    return hasher()(key);\n  }\n\n  Key const& keyForValue(Key const& v) const { return v; }\n  Key const& keyForValue(std::pair<Key const, MappedOrBool> const& p) const {\n    return p.first;\n  }\n  Key const& keyForValue(std::pair<Key&&, MappedOrBool&&> const& p) const {\n    return p.first;\n  }\n\n  // map's choice of pair<K const, T> as value_type is unfortunate,\n  // because it means we either need a proxy iterator, a pointless key\n  // copy when moving items during rehash, or some sort of UB hack.\n  //\n  // This code implements the hack.  Use moveValue(v) instead of\n  // std::move(v) as the source of a move construction.  enable_if_t is\n  // used so that this works for maps while being a no-op for sets.\n  template <typename Dummy = int>\n  static std::pair<Key&&, MappedOrBool&&> moveValue(\n      std::pair<Key const, MappedOrBool>& value,\n      std::enable_if_t<kIsMap, Dummy> = 0) {\n    return {std::move(const_cast<Key&>(value.first)), std::move(value.second)};\n  }\n\n  template <typename Dummy = int>\n  static Value&& moveValue(Value& value, std::enable_if_t<!kIsMap, Dummy> = 0) {\n    return std::move(value);\n  }\n\n  template <typename P>\n  bool beforeBuild(\n      std::size_t /*size*/, std::size_t /*capacity*/, P&& /*rhs*/) {\n    return false;\n  }\n\n  template <typename P>\n  void afterBuild(\n      bool /*undoState*/,\n      bool /*success*/,\n      std::size_t /*size*/,\n      std::size_t /*capacity*/,\n      P&& /*rhs*/) {}\n\n  std::size_t alignedAllocSize(std::size_t n) const {\n    if (kChunkAllocAlignment <= alignof(max_align_t) ||\n        std::is_same<ByteAlloc, std::allocator<uint8_t>>::value) {\n      return n;\n    } else {\n      return n + kChunkAllocAlignment;\n    }\n  }\n\n  bool beforeRehash(\n      std::size_t /*size*/,\n      std::size_t /*oldCapacity*/,\n      std::size_t /*newCapacity*/,\n      std::size_t chunkAllocSize,\n      BytePtr& outChunkAllocation) {\n    outChunkAllocation = allocateOverAligned<ByteAlloc, kChunkAllocAlignment>(\n        ByteAlloc{alloc()}, chunkAllocSize);\n    return false;\n  }\n\n  void afterRehash(\n      bool /*undoState*/,\n      bool /*success*/,\n      std::size_t /*size*/,\n      std::size_t /*oldCapacity*/,\n      std::size_t /*newCapacity*/,\n      BytePtr chunkAllocation,\n      std::size_t chunkAllocSize) {\n    // on success, this will be the old allocation, on failure the new one\n    if (chunkAllocation != nullptr) {\n      deallocateOverAligned<ByteAlloc, kChunkAllocAlignment>(\n          ByteAlloc{alloc()}, chunkAllocation, chunkAllocSize);\n    }\n  }\n\n  void beforeClear(std::size_t /*size*/, std::size_t /*capacity*/) {}\n\n  void afterClear(std::size_t /*size*/, std::size_t /*capacity*/) {}\n\n  void beforeReset(std::size_t /*size*/, std::size_t /*capacity*/) {}\n\n  void afterReset(\n      std::size_t /*size*/,\n      std::size_t /*capacity*/,\n      BytePtr chunkAllocation,\n      std::size_t chunkAllocSize) {\n    deallocateOverAligned<ByteAlloc, kChunkAllocAlignment>(\n        ByteAlloc{alloc()}, chunkAllocation, chunkAllocSize);\n  }\n\n  void prefetchValue(Item const&) const {\n    // Subclass should disable with prefetchBeforeRehash(),\n    // prefetchBeforeCopy(), and prefetchBeforeDestroy().  if they don't\n    // override this method, because neither gcc nor clang can figure\n    // out that DenseMaskIter with an empty body can be elided.\n    FOLLY_SAFE_DCHECK(false, \"should be disabled\");\n  }\n\n  void afterDestroyWithoutDeallocate(Value* addr, std::size_t n) {\n    if (kIsLibrarySanitizeAddress) {\n      memset(static_cast<void*>(addr), 0x66, sizeof(Value) * n);\n    }\n  }\n};\n\n// BaseIter is a convenience for concrete set and map implementations\ntemplate <typename ValuePtr, typename Item>\nclass BaseIter {\n private:\n  using pointee = typename std::pointer_traits<ValuePtr>::element_type;\n\n public:\n  using iterator_category = std::forward_iterator_tag;\n  using value_type = std::remove_const_t<pointee>;\n  using difference_type = std::ptrdiff_t;\n  using pointer = ValuePtr;\n  using reference = pointee&;\n\n protected:\n  using Chunk = F14Chunk<Item>;\n  using ChunkPtr =\n      typename std::pointer_traits<ValuePtr>::template rebind<Chunk>;\n  using ItemIter = F14ItemIter<ChunkPtr>;\n\n  using ValueConstPtr = typename std::pointer_traits<ValuePtr>::template rebind<\n      std::add_const_t<typename std::pointer_traits<ValuePtr>::element_type>>;\n};\n\n//////// ValueContainer\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename HasherOrVoid,\n    typename KeyEqualOrVoid,\n    typename AllocOrVoid>\nclass ValueContainerPolicy;\n\ntemplate <typename ValuePtr>\nusing ValueContainerIteratorBase = BaseIter<\n    ValuePtr,\n    std::remove_const_t<typename std::pointer_traits<ValuePtr>::element_type>>;\n\ntemplate <typename ValuePtr>\nclass ValueContainerIterator : public ValueContainerIteratorBase<ValuePtr> {\n  using Super = ValueContainerIteratorBase<ValuePtr>;\n  using ItemIter = typename Super::ItemIter;\n  using ValueConstPtr = typename Super::ValueConstPtr;\n\n public:\n  using pointer = typename Super::pointer;\n  using reference = typename Super::reference;\n  using value_type = typename Super::value_type;\n\n  ValueContainerIterator() = default;\n  ValueContainerIterator(ValueContainerIterator const&) = default;\n  ValueContainerIterator(ValueContainerIterator&&) = default;\n  ValueContainerIterator& operator=(ValueContainerIterator const&) = default;\n  ValueContainerIterator& operator=(ValueContainerIterator&&) = default;\n  ~ValueContainerIterator() = default;\n\n  /*implicit*/ operator ValueContainerIterator<ValueConstPtr>() const {\n    return ValueContainerIterator<ValueConstPtr>{underlying_};\n  }\n\n  reference operator*() const { return underlying_.item(); }\n\n  pointer operator->() const {\n    return std::pointer_traits<pointer>::pointer_to(**this);\n  }\n\n  ValueContainerIterator& operator++() {\n    underlying_.advance();\n    return *this;\n  }\n\n  ValueContainerIterator operator++(int) {\n    auto cur = *this;\n    ++*this;\n    return cur;\n  }\n\n  friend bool operator==(\n      ValueContainerIterator const& lhs, ValueContainerIterator const& rhs) {\n    return lhs.underlying_ == rhs.underlying_;\n  }\n  friend bool operator!=(\n      ValueContainerIterator const& lhs, ValueContainerIterator const& rhs) {\n    return !(lhs == rhs);\n  }\n\n private:\n  ItemIter underlying_;\n\n  explicit ValueContainerIterator(ItemIter const& underlying)\n      : underlying_{underlying} {}\n\n  template <typename K, typename M, typename H, typename E, typename A>\n  friend class ValueContainerPolicy;\n\n  template <typename P>\n  friend class ValueContainerIterator;\n};\n\ntemplate <\n    typename Key,\n    typename MappedTypeOrVoid,\n    typename HasherOrVoid,\n    typename KeyEqualOrVoid,\n    typename AllocOrVoid>\nclass ValueContainerPolicy\n    : public BasePolicy<\n          Key,\n          MappedTypeOrVoid,\n          HasherOrVoid,\n          KeyEqualOrVoid,\n          AllocOrVoid,\n          SetOrMapValueType<Key, MappedTypeOrVoid>> {\n public:\n  using Super = BasePolicy<\n      Key,\n      MappedTypeOrVoid,\n      HasherOrVoid,\n      KeyEqualOrVoid,\n      AllocOrVoid,\n      SetOrMapValueType<Key, MappedTypeOrVoid>>;\n  using Alloc = typename Super::Alloc;\n  using AllocTraits = typename Super::AllocTraits;\n  using Item = typename Super::Item;\n  using ItemIter = typename Super::ItemIter;\n  using Value = typename Super::Value;\n  using KeyEqual = typename Super::KeyEqual;\n  using Hasher = typename Super::Hasher;\n  using Mapped = typename Super::Mapped;\n\n  using Super::kChunkAllocAlignment;\n  static constexpr bool kDefaultConstructIsNoexcept =\n      Super::kDefaultConstructIsNoexcept;\n  static constexpr bool kAllocIsAlwaysEqual = Super::kAllocIsAlwaysEqual;\n  static constexpr bool kEnableItemIteration = Super::kEnableItemIteration;\n  static constexpr bool kContinuousCapacity = Super::kContinuousCapacity;\n  static constexpr auto isAvalanchingHasher = Super::isAvalanchingHasher;\n  static constexpr bool kSwapIsNoexcept = Super::kSwapIsNoexcept;\n\n private:\n  using ByteAlloc = typename Super::ByteAlloc;\n\n  using Super::kIsMap;\n\n public:\n  using ConstIter = ValueContainerIterator<typename AllocTraits::const_pointer>;\n  using Iter = std::conditional_t<\n      kIsMap,\n      ValueContainerIterator<typename AllocTraits::pointer>,\n      ConstIter>;\n\n  //////// F14Table policy\n\n  static constexpr bool prefetchBeforeRehash() { return false; }\n\n  static constexpr bool prefetchBeforeCopy() { return false; }\n\n  static constexpr bool prefetchBeforeDestroy() { return false; }\n\n  static constexpr bool destroyItemOnClear() {\n    return !std::is_trivially_destructible<Item>::value ||\n        !AllocatorHasDefaultObjectDestroy<Alloc, Item>::value;\n  }\n\n  // inherit constructors\n  using Super::Super;\n\n  void swapPolicy(ValueContainerPolicy& rhs) { this->swapBasePolicy(rhs); }\n\n  using Super::keyForValue;\n  static_assert(\n      std::is_same<Item, Value>::value,\n      \"Item and Value should be the same type for ValueContainerPolicy.\");\n\n  std::size_t computeItemHash(Item const& item) const {\n    return this->computeKeyHash(keyForValue(item));\n  }\n\n  template <typename K>\n  bool keyMatchesItem(K const& key, Item const& item) const {\n    return this->keyEqual()(key, keyForValue(item));\n  }\n\n  Value const& buildArgForItem(Item const& item) const& { return item; }\n\n  // buildArgForItem(Item&)&& is used when moving between unequal allocators\n  decltype(auto) buildArgForItem(Item& item) && {\n    return Super::moveValue(item);\n  }\n\n  Value const& valueAtItem(Item const& item) const { return item; }\n\n  Value&& valueAtItemForExtract(Item& item) { return std::move(item); }\n\n  template <typename Table, typename... Args>\n  void constructValueAtItem(Table&&, Item* itemAddr, Args&&... args) {\n    Alloc& a = this->alloc();\n    // GCC < 6 doesn't use the fact that itemAddr came from a reference\n    // to avoid a null-check in the placement new.  folly::assume-ing it\n    // here gets rid of that branch.  The branch is very predictable,\n    // but spoils some further optimizations.  All clang versions that\n    // compile folly seem to be okay.\n    //\n    // TODO(T31574848): clean up assume-s used to optimize placement new\n    assume(itemAddr != nullptr);\n    AllocTraits::construct(a, itemAddr, std::forward<Args>(args)...);\n  }\n\n  template <typename T>\n  std::enable_if_t<IsNothrowMoveAndDestroy<T>::value>\n  complainUnlessNothrowMoveAndDestroy() {}\n\n  template <typename T>\n  [[deprecated(\n      \"mark {key_type,mapped_type} {move constructor,destructor} noexcept, or use F14Node* if they aren't\")]] std::\n      enable_if_t<!IsNothrowMoveAndDestroy<T>::value>\n      complainUnlessNothrowMoveAndDestroy() {}\n\n  void moveItemDuringRehash(Item* itemAddr, Item& src) {\n    complainUnlessNothrowMoveAndDestroy<Key>();\n    complainUnlessNothrowMoveAndDestroy<lift_unit_t<MappedTypeOrVoid>>();\n\n    constructValueAtItem(0, itemAddr, Super::moveValue(src));\n    if (destroyItemOnClear()) {\n      if (kIsMap) {\n        // Laundering in the standard is only described as a solution\n        // for changes to const fields due to the creation of a new\n        // object lifetime (destroy and then placement new in the same\n        // location), but it seems highly likely that it will also cause\n        // the compiler to drop such assumptions that are violated due\n        // to our UB const_cast in moveValue.\n        destroyItem(*std::launder(std::addressof(src)));\n      } else {\n        destroyItem(src);\n      }\n    }\n  }\n\n  void destroyItem(Item& item) noexcept {\n    Alloc& a = this->alloc();\n    auto ptr = std::addressof(item);\n    AllocTraits::destroy(a, ptr);\n    this->afterDestroyWithoutDeallocate(ptr, 1);\n  }\n\n  template <typename V>\n  void visitPolicyAllocationClasses(\n      std::size_t chunkAllocSize,\n      std::size_t /*size*/,\n      std::size_t /*capacity*/,\n      V&& visitor) const {\n    if (chunkAllocSize > 0) {\n      visitor(\n          allocationBytesForOverAligned<ByteAlloc, kChunkAllocAlignment>(\n              chunkAllocSize),\n          1);\n    }\n  }\n\n  //////// F14BasicMap/Set policy\n\n  FOLLY_ALWAYS_INLINE Iter makeIter(ItemIter const& underlying) const {\n    return Iter{underlying};\n  }\n  ConstIter makeConstIter(ItemIter const& underlying) const {\n    return ConstIter{underlying};\n  }\n  ItemIter const& unwrapIter(ConstIter const& iter) const {\n    return iter.underlying_;\n  }\n};\n\n//////// NodeContainer\n\ntemplate <\n    typename Key,\n    typename Mapped,\n    typename HasherOrVoid,\n    typename KeyEqualOrVoid,\n    typename AllocOrVoid>\nclass NodeContainerPolicy;\n\ntemplate <typename ValuePtr>\nclass NodeContainerIterator : public BaseIter<ValuePtr, NonConstPtr<ValuePtr>> {\n  using Super = BaseIter<ValuePtr, NonConstPtr<ValuePtr>>;\n  using ItemIter = typename Super::ItemIter;\n  using ValueConstPtr = typename Super::ValueConstPtr;\n\n public:\n  using pointer = typename Super::pointer;\n  using reference = typename Super::reference;\n  using value_type = typename Super::value_type;\n\n  NodeContainerIterator() = default;\n  NodeContainerIterator(NodeContainerIterator const&) = default;\n  NodeContainerIterator(NodeContainerIterator&&) = default;\n  NodeContainerIterator& operator=(NodeContainerIterator const&) = default;\n  NodeContainerIterator& operator=(NodeContainerIterator&&) = default;\n  ~NodeContainerIterator() = default;\n\n  /*implicit*/ operator NodeContainerIterator<ValueConstPtr>() const {\n    return NodeContainerIterator<ValueConstPtr>{underlying_};\n  }\n\n  reference operator*() const { return *underlying_.item(); }\n\n  pointer operator->() const {\n    return std::pointer_traits<pointer>::pointer_to(**this);\n  }\n\n  NodeContainerIterator& operator++() {\n    underlying_.advance();\n    return *this;\n  }\n\n  NodeContainerIterator operator++(int) {\n    auto cur = *this;\n    ++*this;\n    return cur;\n  }\n\n  friend bool operator==(\n      NodeContainerIterator const& lhs, NodeContainerIterator const& rhs) {\n    return lhs.underlying_ == rhs.underlying_;\n  }\n  friend bool operator!=(\n      NodeContainerIterator const& lhs, NodeContainerIterator const& rhs) {\n    return !(lhs == rhs);\n  }\n\n private:\n  ItemIter underlying_;\n\n  explicit NodeContainerIterator(ItemIter const& underlying)\n      : underlying_{underlying} {}\n\n  template <typename K, typename M, typename H, typename E, typename A>\n  friend class NodeContainerPolicy;\n\n  template <typename P>\n  friend class NodeContainerIterator;\n};\n\ntemplate <\n    typename Key,\n    typename MappedTypeOrVoid,\n    typename HasherOrVoid,\n    typename KeyEqualOrVoid,\n    typename AllocOrVoid>\nclass NodeContainerPolicy\n    : public BasePolicy<\n          Key,\n          MappedTypeOrVoid,\n          HasherOrVoid,\n          KeyEqualOrVoid,\n          AllocOrVoid,\n          typename std::allocator_traits<Defaulted<\n              AllocOrVoid,\n              DefaultAlloc<std::conditional_t<\n                  std::is_void<MappedTypeOrVoid>::value,\n                  Key,\n                  MapValueType<Key, MappedTypeOrVoid>>>>>::pointer> {\n public:\n  using Super = BasePolicy<\n      Key,\n      MappedTypeOrVoid,\n      HasherOrVoid,\n      KeyEqualOrVoid,\n      AllocOrVoid,\n      typename std::allocator_traits<Defaulted<\n          AllocOrVoid,\n          DefaultAlloc<std::conditional_t<\n              std::is_void<MappedTypeOrVoid>::value,\n              Key,\n              MapValueType<Key, MappedTypeOrVoid>>>>>::pointer>;\n  using Alloc = typename Super::Alloc;\n  using AllocTraits = typename Super::AllocTraits;\n  using Item = typename Super::Item;\n  using ItemIter = typename Super::ItemIter;\n  using Value = typename Super::Value;\n\n private:\n  using ByteAlloc = typename Super::ByteAlloc;\n\n  using Super::kChunkAllocAlignment;\n  using Super::kIsMap;\n\n public:\n  using ConstIter = NodeContainerIterator<typename AllocTraits::const_pointer>;\n  using Iter = std::conditional_t<\n      kIsMap,\n      NodeContainerIterator<typename AllocTraits::pointer>,\n      ConstIter>;\n\n  //////// F14Table policy\n\n  static constexpr bool prefetchBeforeRehash() { return true; }\n\n  static constexpr bool prefetchBeforeCopy() { return true; }\n\n  static constexpr bool prefetchBeforeDestroy() {\n    return !std::is_trivially_destructible<Value>::value;\n  }\n\n  static constexpr bool destroyItemOnClear() { return true; }\n\n  // inherit constructors\n  using Super::Super;\n\n  void swapPolicy(NodeContainerPolicy& rhs) { this->swapBasePolicy(rhs); }\n\n  using Super::keyForValue;\n\n  std::size_t computeItemHash(Item const& item) const {\n    return this->computeKeyHash(keyForValue(*item));\n  }\n\n  template <typename K>\n  bool keyMatchesItem(K const& key, Item const& item) const {\n    return this->keyEqual()(key, keyForValue(*item));\n  }\n\n  Value const& buildArgForItem(Item const& item) const& { return *item; }\n\n  // buildArgForItem(Item&)&& is used when moving between unequal allocators\n  decltype(auto) buildArgForItem(Item& item) && {\n    return Super::moveValue(*item);\n  }\n\n  Value const& valueAtItem(Item const& item) const { return *item; }\n\n  Value&& valueAtItemForExtract(Item& item) { return std::move(*item); }\n\n  template <typename Table, typename... Args>\n  void constructValueAtItem(Table&&, Item* itemAddr, Args&&... args) {\n    Alloc& a = this->alloc();\n    // TODO(T31574848): clean up assume-s used to optimize placement new\n    assume(itemAddr != nullptr);\n    new (itemAddr) Item{AllocTraits::allocate(a, 1)};\n    auto p = std::addressof(**itemAddr);\n    // TODO(T31574848): clean up assume-s used to optimize placement new\n    assume(p != nullptr);\n    auto rollback = makeGuard([&] { AllocTraits::deallocate(a, p, 1); });\n    AllocTraits::construct(a, p, std::forward<Args>(args)...);\n    rollback.dismiss();\n  }\n\n  void moveItemDuringRehash(Item* itemAddr, Item& src) {\n    // This is basically *itemAddr = src; src = nullptr, but allowing\n    // for fancy pointers.\n    // TODO(T31574848): clean up assume-s used to optimize placement new\n    assume(itemAddr != nullptr);\n    new (itemAddr) Item{std::move(src)};\n    src = nullptr;\n    src.~Item();\n  }\n\n  void prefetchValue(Item const& item) const {\n    prefetchAddr(std::addressof(*item));\n  }\n\n  template <typename T>\n  std::enable_if_t<std::is_nothrow_destructible<T>::value>\n  complainUnlessNothrowDestroy() {}\n\n  template <typename T>\n  [[deprecated(\"Mark key and mapped type destructor nothrow\")]] std::\n      enable_if_t<!std::is_nothrow_destructible<T>::value>\n      complainUnlessNothrowDestroy() {}\n\n  void destroyItem(Item& item) noexcept {\n    complainUnlessNothrowDestroy<Key>();\n    complainUnlessNothrowDestroy<lift_unit_t<MappedTypeOrVoid>>();\n    if (item != nullptr) {\n      Alloc& a = this->alloc();\n      AllocTraits::destroy(a, std::addressof(*item));\n      AllocTraits::deallocate(a, item, 1);\n    }\n    item.~Item();\n  }\n\n  template <typename V>\n  void visitPolicyAllocationClasses(\n      std::size_t chunkAllocSize,\n      std::size_t size,\n      std::size_t /*capacity*/,\n      V&& visitor) const {\n    if (chunkAllocSize > 0) {\n      visitor(\n          allocationBytesForOverAligned<ByteAlloc, kChunkAllocAlignment>(\n              chunkAllocSize),\n          1);\n    }\n    if (size > 0) {\n      visitor(sizeof(Value), size);\n    }\n  }\n\n  //////// F14BasicMap/Set policy\n\n  FOLLY_ALWAYS_INLINE Iter makeIter(ItemIter const& underlying) const {\n    return Iter{underlying};\n  }\n  ConstIter makeConstIter(ItemIter const& underlying) const {\n    return Iter{underlying};\n  }\n  ItemIter const& unwrapIter(ConstIter const& iter) const {\n    return iter.underlying_;\n  }\n};\n\n//////// VectorContainer\n\ntemplate <\n    typename Key,\n    typename MappedTypeOrVoid,\n    typename HasherOrVoid,\n    typename KeyEqualOrVoid,\n    typename AllocOrVoid,\n    typename EligibleForPerturbedInsertionOrder>\nclass VectorContainerPolicy;\n\ntemplate <typename ValuePtr>\nclass VectorContainerIterator : public BaseIter<ValuePtr, uint32_t> {\n  using Super = BaseIter<ValuePtr, uint32_t>;\n  using ValueConstPtr = typename Super::ValueConstPtr;\n\n public:\n  using pointer = typename Super::pointer;\n  using reference = typename Super::reference;\n  using value_type = typename Super::value_type;\n\n  VectorContainerIterator() = default;\n  VectorContainerIterator(VectorContainerIterator const&) = default;\n  VectorContainerIterator(VectorContainerIterator&&) = default;\n  VectorContainerIterator& operator=(VectorContainerIterator const&) = default;\n  VectorContainerIterator& operator=(VectorContainerIterator&&) = default;\n  ~VectorContainerIterator() = default;\n\n  /*implicit*/ operator VectorContainerIterator<ValueConstPtr>() const {\n    return VectorContainerIterator<ValueConstPtr>{current_, lowest_};\n  }\n\n  reference operator*() const { return *current_; }\n\n  pointer operator->() const { return current_; }\n\n  VectorContainerIterator& operator++() {\n    if (FOLLY_UNLIKELY(current_ == lowest_)) {\n      current_ = nullptr;\n    } else {\n      --current_;\n    }\n    return *this;\n  }\n\n  VectorContainerIterator operator++(int) {\n    auto cur = *this;\n    ++*this;\n    return cur;\n  }\n\n  friend bool operator==(\n      VectorContainerIterator const& lhs, VectorContainerIterator const& rhs) {\n    return lhs.current_ == rhs.current_;\n  }\n  friend bool operator!=(\n      VectorContainerIterator const& lhs, VectorContainerIterator const& rhs) {\n    return !(lhs == rhs);\n  }\n\n private:\n  ValuePtr current_;\n  ValuePtr lowest_;\n\n  explicit VectorContainerIterator(ValuePtr current, ValuePtr lowest)\n      : current_(current), lowest_(lowest) {}\n\n  std::size_t index() const { return current_ - lowest_; }\n\n  template <\n      typename K,\n      typename M,\n      typename H,\n      typename E,\n      typename A,\n      typename P>\n  friend class VectorContainerPolicy;\n\n  template <typename P>\n  friend class VectorContainerIterator;\n};\n\nstruct VectorContainerIndexSearch {\n  uint32_t index_;\n};\n\ntemplate <\n    typename Key,\n    typename MappedTypeOrVoid,\n    typename HasherOrVoid,\n    typename KeyEqualOrVoid,\n    typename AllocOrVoid,\n    typename EligibleForPerturbedInsertionOrder>\nclass VectorContainerPolicy\n    : public BasePolicy<\n          Key,\n          MappedTypeOrVoid,\n          HasherOrVoid,\n          KeyEqualOrVoid,\n          AllocOrVoid,\n          uint32_t> {\n public:\n  using Super = BasePolicy<\n      Key,\n      MappedTypeOrVoid,\n      HasherOrVoid,\n      KeyEqualOrVoid,\n      AllocOrVoid,\n      uint32_t>;\n  using Value = typename Super::Value;\n  using Alloc = typename Super::Alloc;\n  using AllocTraits = typename Super::AllocTraits;\n  using ByteAlloc = typename Super::ByteAlloc;\n  using ByteAllocTraits = typename Super::ByteAllocTraits;\n  using BytePtr = typename Super::BytePtr;\n  using Hasher = typename Super::Hasher;\n  using Item = typename Super::Item;\n  using ItemIter = typename Super::ItemIter;\n  using KeyEqual = typename Super::KeyEqual;\n\n  using Super::kAllocIsAlwaysEqual;\n\n private:\n  using Super::kIsMap;\n\n  // Allocation alignment must satisfy both chunk alignment (for SIMD tag\n  // loads) and value alignment (values are placed after chunks).\n  static constexpr std::size_t kAllocAlignment =\n      constexpr_max(Super::kChunkAllocAlignment, alignof(Value));\n\n public:\n  static constexpr bool kEnableItemIteration = false;\n\n  static constexpr bool kContinuousCapacity = true;\n\n  using InternalSizeType = Item;\n\n  using ConstIter =\n      VectorContainerIterator<typename AllocTraits::const_pointer>;\n  using Iter = std::conditional_t<\n      kIsMap,\n      VectorContainerIterator<typename AllocTraits::pointer>,\n      ConstIter>;\n  using ConstReverseIter = typename AllocTraits::const_pointer;\n  using ReverseIter = std::\n      conditional_t<kIsMap, typename AllocTraits::pointer, ConstReverseIter>;\n\n  using ValuePtr = typename AllocTraits::pointer;\n\n  //////// F14Table policy\n\n  static constexpr bool prefetchBeforeRehash() { return true; }\n\n  static constexpr bool prefetchBeforeCopy() { return false; }\n\n  static constexpr bool prefetchBeforeDestroy() { return false; }\n\n  static constexpr bool destroyItemOnClear() { return false; }\n\n private:\n  static constexpr bool valueIsTriviallyCopyable() {\n    return AllocatorHasDefaultObjectConstruct<Alloc, Value, Value>::value &&\n        AllocatorHasDefaultObjectDestroy<Alloc, Value>::value &&\n        std::is_trivially_copyable<Value>::value;\n  }\n\n public:\n  VectorContainerPolicy(\n      Hasher const& hasher, KeyEqual const& keyEqual, Alloc const& alloc)\n      : Super{hasher, keyEqual, alloc} {}\n\n  VectorContainerPolicy(VectorContainerPolicy const& rhs) : Super{rhs} {\n    // values_ will get allocated later to do the copy\n  }\n\n  VectorContainerPolicy(VectorContainerPolicy const& rhs, Alloc const& alloc)\n      : Super{rhs, alloc} {\n    // values_ will get allocated later to do the copy\n  }\n\n  VectorContainerPolicy(VectorContainerPolicy&& rhs) noexcept\n      : Super{std::move(rhs)}, values_{rhs.values_} {\n    rhs.values_ = nullptr;\n  }\n\n  VectorContainerPolicy(\n      VectorContainerPolicy&& rhs, Alloc const& alloc) noexcept\n      : Super{std::move(rhs), alloc} {\n    if (kAllocIsAlwaysEqual || this->alloc() == rhs.alloc()) {\n      // common case\n      values_ = rhs.values_;\n      rhs.values_ = nullptr;\n    } else {\n      // table must be constructed in new memory\n      values_ = nullptr;\n    }\n  }\n\n  VectorContainerPolicy& operator=(VectorContainerPolicy const& rhs) {\n    if (this != &rhs) {\n      FOLLY_SAFE_DCHECK(values_ == nullptr, \"\");\n      Super::operator=(rhs);\n    }\n    return *this;\n  }\n\n  VectorContainerPolicy& operator=(VectorContainerPolicy&& rhs) noexcept {\n    if (this != &rhs) {\n      FOLLY_SAFE_DCHECK(values_ == nullptr, \"\");\n      bool transfer =\n          AllocTraits::propagate_on_container_move_assignment::value ||\n          kAllocIsAlwaysEqual || this->alloc() == rhs.alloc();\n      Super::operator=(std::move(rhs));\n      if (transfer) {\n        values_ = rhs.values_;\n        rhs.values_ = nullptr;\n      }\n    }\n    return *this;\n  }\n\n  void swapPolicy(VectorContainerPolicy& rhs) {\n    using std::swap;\n    this->swapBasePolicy(rhs);\n    swap(values_, rhs.values_);\n  }\n\n  template <typename K>\n  std::size_t computeKeyHash(K const& key) const {\n    static_assert(\n        Super::isAvalanchingHasher() == IsAvalanchingHasher<Hasher, K>::value);\n    return this->hasher()(key);\n  }\n\n  std::size_t computeKeyHash(VectorContainerIndexSearch const& key) const {\n    return computeItemHash(key.index_);\n  }\n\n  using Super::keyForValue;\n\n  std::size_t computeItemHash(Item const& item) const {\n    return this->computeKeyHash(keyForValue(values_[item]));\n  }\n\n  bool keyMatchesItem(\n      VectorContainerIndexSearch const& key, Item const& item) const {\n    return key.index_ == item;\n  }\n\n  template <typename K>\n  bool keyMatchesItem(K const& key, Item const& item) const {\n    return this->keyEqual()(key, keyForValue(values_[item]));\n  }\n\n  Key const& keyForValue(VectorContainerIndexSearch const& arg) const {\n    return keyForValue(values_[arg.index_]);\n  }\n\n  VectorContainerIndexSearch buildArgForItem(Item const& item) const {\n    return {item};\n  }\n\n  Value const& valueAtItem(Item const& item) const { return values_[item]; }\n\n  Value&& valueAtItemForExtract(Item& item) { return std::move(values_[item]); }\n\n  template <typename Table>\n  void constructValueAtItem(\n      Table&&, Item* itemAddr, VectorContainerIndexSearch arg) {\n    *itemAddr = arg.index_;\n  }\n\n  template <typename Table, typename... Args>\n  void constructValueAtItem(Table&& table, Item* itemAddr, Args&&... args) {\n    Alloc& a = this->alloc();\n    auto size = static_cast<InternalSizeType>(table.size());\n    FOLLY_SAFE_DCHECK(\n        table.size() < std::numeric_limits<InternalSizeType>::max(), \"\");\n    *itemAddr = size;\n    auto dst = std::addressof(values_[size]);\n    // TODO(T31574848): clean up assume-s used to optimize placement new\n    assume(dst != nullptr);\n    AllocTraits::construct(a, dst, std::forward<Args>(args)...);\n\n    constexpr bool perturb = FOLLY_F14_PERTURB_INSERTION_ORDER;\n    if (EligibleForPerturbedInsertionOrder::value && perturb &&\n        !tlsPendingSafeInserts()) {\n      // Pick a random victim. We have to do this post-construction\n      // because the item and tag are already set in the table before\n      // calling constructValueAtItem, so if there is a tag collision\n      // find may evaluate values_[size] during the search.\n      auto i = static_cast<InternalSizeType>(tlsMinstdRand(size + 1));\n      if (i != size) {\n        auto& lhsItem = *itemAddr;\n        auto rhsIter = table.find(\n            VectorContainerIndexSearch{static_cast<InternalSizeType>(i)});\n        FOLLY_SAFE_DCHECK(!rhsIter.atEnd(), \"\");\n        auto& rhsItem = rhsIter.item();\n        FOLLY_SAFE_DCHECK(lhsItem == size, \"\");\n        FOLLY_SAFE_DCHECK(rhsItem == i, \"\");\n\n        aligned_storage_for_t<Value> tmp;\n        Value* tmpValue = static_cast<Value*>(static_cast<void*>(&tmp));\n        transfer(a, std::addressof(values_[i]), tmpValue, 1);\n        transfer(\n            a, std::addressof(values_[size]), std::addressof(values_[i]), 1);\n        transfer(a, tmpValue, std::addressof(values_[size]), 1);\n        lhsItem = i;\n        rhsItem = size;\n      }\n    }\n  }\n\n  void moveItemDuringRehash(Item* itemAddr, Item& src) { *itemAddr = src; }\n\n  void prefetchValue(Item const& item) const {\n    prefetchAddr(std::addressof(values_[item]));\n  }\n\n  void destroyItem(Item&) noexcept {}\n\n  template <typename T>\n  std::enable_if_t<IsNothrowMoveAndDestroy<T>::value>\n  complainUnlessNothrowMoveAndDestroy() {}\n\n  template <typename T>\n  [[deprecated(\n      \"mark {key_type,mapped_type} {move constructor,destructor} noexcept, or use F14Node* if they aren't\")]] std::\n      enable_if_t<!IsNothrowMoveAndDestroy<T>::value>\n      complainUnlessNothrowMoveAndDestroy() {}\n\n  void transfer(Alloc& a, Value* src, Value* dst, std::size_t n) {\n    complainUnlessNothrowMoveAndDestroy<Key>();\n    complainUnlessNothrowMoveAndDestroy<lift_unit_t<MappedTypeOrVoid>>();\n\n    auto origSrc = src;\n    if (valueIsTriviallyCopyable()) {\n      std::memcpy(\n          static_cast<void*>(dst),\n          static_cast<void const*>(src),\n          n * sizeof(Value));\n    } else {\n      for (std::size_t i = 0; i < n; ++i, ++src, ++dst) {\n        // TODO(T31574848): clean up assume-s used to optimize placement new\n        assume(dst != nullptr);\n        AllocTraits::construct(a, dst, Super::moveValue(*src));\n        if (kIsMap) {\n          AllocTraits::destroy(a, std::launder(src));\n        } else {\n          AllocTraits::destroy(a, src);\n        }\n      }\n    }\n    this->afterDestroyWithoutDeallocate(origSrc, n);\n  }\n\n  template <typename P, typename V>\n  bool beforeBuildImpl(std::size_t size, P&& rhs, V const& constructorArgFor) {\n    Alloc& a = this->alloc();\n\n    FOLLY_SAFE_DCHECK(values_ != nullptr, \"\");\n\n    auto src = std::addressof(rhs.values_[0]);\n    Value* dst = std::addressof(values_[0]);\n\n    if (valueIsTriviallyCopyable()) {\n      std::memcpy(\n          static_cast<void*>(dst),\n          static_cast<void const*>(src),\n          size * sizeof(Value));\n    } else {\n      for (std::size_t i = 0; i < size; ++i, ++src, ++dst) {\n        try {\n          // TODO(T31574848): clean up assume-s used to optimize placement new\n          assume(dst != nullptr);\n          AllocTraits::construct(a, dst, constructorArgFor(*src));\n        } catch (...) {\n          for (Value* cleanup = std::addressof(values_[0]); cleanup != dst;\n               ++cleanup) {\n            AllocTraits::destroy(a, cleanup);\n          }\n          throw;\n        }\n      }\n    }\n    return true;\n  }\n\n  bool beforeBuild(\n      std::size_t size,\n      std::size_t /*capacity*/,\n      VectorContainerPolicy const& rhs) {\n    return beforeBuildImpl(size, rhs, [](Value const& v) { return v; });\n  }\n\n  bool beforeBuild(\n      std::size_t size, std::size_t /*capacity*/, VectorContainerPolicy&& rhs) {\n    return beforeBuildImpl(size, rhs, [](Value& v) {\n      return Super::moveValue(v);\n    });\n  }\n\n  template <typename P>\n  void afterBuild(\n      bool /*undoState*/,\n      bool success,\n      std::size_t /*size*/,\n      std::size_t /*capacity*/,\n      P&& /*rhs*/) {\n    // buildArgForItem can be used to construct a new item trivially,\n    // so no failure between beforeBuild and afterBuild should be possible\n    FOLLY_SAFE_DCHECK(success, \"\");\n  }\n\n private:\n  // Returns the byte offset of the first Value in a unified allocation\n  // that first holds prefixBytes of data, where prefixBytes comes from\n  // Chunk storage and may be only 4-byte aligned due to sub-chunk\n  // allocation.\n  static std::size_t valuesOffset(std::size_t prefixBytes) {\n    FOLLY_SAFE_DCHECK((prefixBytes % alignof(Item)) == 0, \"\");\n    if (alignof(Value) > alignof(Item)) {\n      prefixBytes = -(-prefixBytes & ~(alignof(Value) - 1));\n    }\n    FOLLY_SAFE_DCHECK((prefixBytes % alignof(Value)) == 0, \"\");\n    return prefixBytes;\n  }\n\n  // Returns the total number of bytes that should be allocated to store\n  // prefixBytes of Chunks and valueCapacity values.\n  static std::size_t allocSize(\n      std::size_t prefixBytes, std::size_t valueCapacity) {\n    return valuesOffset(prefixBytes) + sizeof(Value) * valueCapacity;\n  }\n\n public:\n  ValuePtr beforeRehash(\n      std::size_t size,\n      std::size_t oldCapacity,\n      std::size_t newCapacity,\n      std::size_t chunkAllocSize,\n      BytePtr& outChunkAllocation) {\n    FOLLY_SAFE_DCHECK(\n        size <= oldCapacity && ((values_ == nullptr) == (oldCapacity == 0)) &&\n            newCapacity > 0 &&\n            newCapacity <= (std::numeric_limits<Item>::max)(),\n        \"\");\n\n    outChunkAllocation = allocateOverAligned<ByteAlloc, kAllocAlignment>(\n        ByteAlloc{Super::alloc()}, allocSize(chunkAllocSize, newCapacity));\n\n    ValuePtr before = values_;\n    ValuePtr after = std::pointer_traits<ValuePtr>::pointer_to(\n        *static_cast<Value*>(static_cast<void*>(\n            &*outChunkAllocation + valuesOffset(chunkAllocSize))));\n\n    if (size > 0) {\n      Alloc& a = this->alloc();\n      transfer(a, std::addressof(before[0]), std::addressof(after[0]), size);\n    }\n\n    values_ = after;\n    return before;\n  }\n\n  FOLLY_NOINLINE void afterFailedRehash(ValuePtr state, std::size_t size) {\n    // state holds the old storage\n    Alloc& a = this->alloc();\n    if (size > 0) {\n      transfer(a, std::addressof(values_[0]), std::addressof(state[0]), size);\n    }\n    values_ = state;\n  }\n\n  void afterRehash(\n      ValuePtr state,\n      bool success,\n      std::size_t size,\n      std::size_t oldCapacity,\n      std::size_t newCapacity,\n      BytePtr chunkAllocation,\n      std::size_t chunkAllocSize) {\n    if (!success) {\n      afterFailedRehash(state, size);\n    }\n\n    // on success, chunkAllocation is the old allocation, on failure it is the\n    // new one\n    if (chunkAllocation != nullptr) {\n      deallocateOverAligned<ByteAlloc, kAllocAlignment>(\n          ByteAlloc{Super::alloc()},\n          chunkAllocation,\n          allocSize(chunkAllocSize, (success ? oldCapacity : newCapacity)));\n    }\n  }\n\n  void beforeClear(std::size_t size, std::size_t capacity) {\n    FOLLY_SAFE_DCHECK(\n        size <= capacity && ((values_ == nullptr) == (capacity == 0)), \"\");\n    Alloc& a = this->alloc();\n    for (std::size_t i = 0; i < size; ++i) {\n      AllocTraits::destroy(a, std::addressof(values_[i]));\n    }\n  }\n\n  void beforeReset(std::size_t size, std::size_t capacity) {\n    beforeClear(size, capacity);\n  }\n\n  void afterReset(\n      std::size_t /*size*/,\n      std::size_t capacity,\n      BytePtr chunkAllocation,\n      std::size_t chunkAllocSize) {\n    if (chunkAllocation != nullptr) {\n      deallocateOverAligned<ByteAlloc, kAllocAlignment>(\n          ByteAlloc{Super::alloc()},\n          chunkAllocation,\n          allocSize(chunkAllocSize, capacity));\n      values_ = nullptr;\n    }\n  }\n\n  template <typename V>\n  void visitPolicyAllocationClasses(\n      std::size_t chunkAllocSize,\n      std::size_t /*size*/,\n      std::size_t capacity,\n      V&& visitor) const {\n    FOLLY_SAFE_DCHECK((chunkAllocSize == 0) == (capacity == 0), \"\");\n    if (chunkAllocSize > 0) {\n      visitor(\n          allocationBytesForOverAligned<ByteAlloc, kAllocAlignment>(\n              allocSize(chunkAllocSize, capacity)),\n          1);\n    }\n  }\n\n  // Iterator stuff\n\n  Iter linearBegin(std::size_t size) const {\n    return size > 0\n        ? Iter{values_ + size - 1, values_}\n        : Iter{nullptr, nullptr};\n  }\n\n  Iter linearEnd() const { return Iter{nullptr, nullptr}; }\n\n  //////// F14BasicMap/Set policy\n\n  Iter makeIter(ItemIter const& underlying) const {\n    if (underlying.atEnd()) {\n      return linearEnd();\n    } else {\n      assume(values_ + underlying.item() != nullptr);\n      assume(values_ != nullptr);\n      return Iter{values_ + underlying.item(), values_};\n    }\n  }\n\n  ConstIter makeConstIter(ItemIter const& underlying) const {\n    return makeIter(underlying);\n  }\n\n  Item iterToIndex(ConstIter const& iter) const {\n    auto n = iter.index();\n    assume(n <= std::numeric_limits<Item>::max());\n    return static_cast<Item>(n);\n  }\n\n  Iter indexToIter(Item index) const { return Iter{values_ + index, values_}; }\n\n  Iter iter(ReverseIter it) { return Iter{it, values_}; }\n\n  ConstIter iter(ConstReverseIter it) const { return ConstIter{it, values_}; }\n\n  ReverseIter riter(Iter it) { return it.current_; }\n\n  ConstReverseIter riter(ConstIter it) const { return it.current_; }\n\n  ValuePtr values_{nullptr};\n};\n\ntemplate <\n    template <\n        typename,\n        typename,\n        typename,\n        typename,\n        typename,\n        typename...> class Policy,\n    typename Key,\n    typename Mapped,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc,\n    typename... Args>\nusing MapPolicyWithDefaults = Policy<\n    Key,\n    Mapped,\n    VoidDefault<Hasher, DefaultHasher<Key>>,\n    VoidDefault<KeyEqual, DefaultKeyEqual<Key>>,\n    VoidDefault<Alloc, DefaultAlloc<std::pair<Key const, Mapped>>>,\n    Args...>;\n\ntemplate <\n    template <\n        typename,\n        typename,\n        typename,\n        typename,\n        typename,\n        typename...> class Policy,\n    typename Key,\n    typename Hasher,\n    typename KeyEqual,\n    typename Alloc,\n    typename... Args>\nusing SetPolicyWithDefaults = Policy<\n    Key,\n    void,\n    VoidDefault<Hasher, DefaultHasher<Key>>,\n    VoidDefault<KeyEqual, DefaultKeyEqual<Key>>,\n    VoidDefault<Alloc, DefaultAlloc<Key>>,\n    Args...>;\n\n} // namespace detail\n} // namespace f14\n} // namespace folly\n\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n"
  },
  {
    "path": "folly/container/detail/F14SetFallback.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <type_traits>\n#include <unordered_set>\n\n#include <folly/container/detail/F14Table.h>\n#include <folly/container/detail/Util.h>\n\n/**\n * This file is intended to be included only by F14Set.h. It contains fallback\n * implementations of F14Set types for platforms that do not support the\n * required SIMD instructions, based on std::unordered_set.\n */\n\n#if !FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nnamespace folly {\n\nnamespace f14 {\nnamespace detail {\ntemplate <typename KeyType, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14BasicSet\n    : public std::unordered_set<KeyType, Hasher, KeyEqual, Alloc> {\n  using Super = std::unordered_set<KeyType, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::allocator_type;\n  using typename Super::const_iterator;\n  using typename Super::hasher;\n  using typename Super::iterator;\n  using typename Super::key_equal;\n  using typename Super::key_type;\n  using typename Super::pointer;\n  using typename Super::size_type;\n  using typename Super::value_type;\n\n private:\n  template <typename K, typename T>\n  using EnableHeterogeneousFind = std::enable_if_t<\n      ::folly::detail::\n          EligibleForHeterogeneousFind<key_type, hasher, key_equal, K>::value,\n      T>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousInsert = std::enable_if_t<\n      ::folly::detail::\n          EligibleForHeterogeneousInsert<key_type, hasher, key_equal, K>::value,\n      T>;\n\n  template <typename K>\n  using IsIter = Disjunction<\n      std::is_same<iterator, remove_cvref_t<K>>,\n      std::is_same<const_iterator, remove_cvref_t<K>>>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousErase = std::enable_if_t<\n      ::folly::detail::EligibleForHeterogeneousFind<\n          key_type,\n          hasher,\n          key_equal,\n          std::conditional_t<IsIter<K>::value, key_type, K>>::value &&\n          !IsIter<K>::value,\n      T>;\n\n public:\n  F14BasicSet() = default;\n\n  using Super::Super;\n\n  //// PUBLIC - Modifiers\n\n  using Super::insert;\n\n  template <typename K>\n  EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert(K&& value) {\n    return emplace(std::forward<K>(value));\n  }\n\n  template <class InputIt>\n  void insert(InputIt first, InputIt last) {\n    while (first != last) {\n      insert(*first);\n      ++first;\n    }\n  }\n\n private:\n  template <typename Arg>\n  using UsableAsKey = ::folly::detail::\n      EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;\n\n public:\n  template <class... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    auto a = this->get_allocator();\n    return folly::detail::callWithConstructedKey<key_type, UsableAsKey>(\n        a,\n        [&](auto const&, auto&& key) {\n          if (!std::is_same<key_type, remove_cvref_t<decltype(key)>>::value) {\n            // this is a heterogeneous emplace\n            auto it = find(key);\n            if (it != this->end()) {\n              return std::make_pair(it, false);\n            }\n            auto rv = Super::emplace(std::forward<decltype(key)>(key));\n            FOLLY_SAFE_DCHECK(\n                rv.second, \"post-find emplace should always insert\");\n            return rv;\n          } else {\n            return Super::emplace(std::forward<decltype(key)>(key));\n          }\n        },\n        std::forward<Args>(args)...);\n  }\n\n  template <class... Args>\n  iterator emplace_hint(const_iterator /*hint*/, Args&&... args) {\n    return emplace(std::forward<Args>(args)...).first;\n  }\n\n  using Super::erase;\n\n  template <typename K>\n  EnableHeterogeneousErase<K, size_type> erase(K const& key) {\n    auto it = find(key);\n    if (it != this->end()) {\n      erase(it);\n      return 1;\n    } else {\n      return 0;\n    }\n  }\n\n  //// PUBLIC - Lookup\n\n private:\n  // BottomKeyEqual must have same size, alignment, emptiness, and finality as\n  // KeyEqual\n  struct BottomKeyEqualEmpty {};\n  template <size_t S, size_t A>\n  struct BottomKeyEqualNonEmpty {\n    alignas(A) char data[S];\n  };\n  using BottomKeyEqualBase = conditional_t<\n      std::is_empty<KeyEqual>::value,\n      BottomKeyEqualEmpty,\n      BottomKeyEqualNonEmpty<sizeof(KeyEqual), alignof(KeyEqual)>>;\n  template <bool IsFinal, typename K>\n  struct BottomKeyEqualCond : BottomKeyEqualBase {\n    [[noreturn]] bool operator()(K const&, K const&) const {\n      assume_unreachable();\n    }\n  };\n  template <typename K>\n  struct BottomKeyEqualCond<true, K> final : BottomKeyEqualCond<false, K> {};\n  template <typename K>\n  using BottomKeyEqual = BottomKeyEqualCond<\n      std::is_final<KeyEqual>::value || std::is_union<KeyEqual>::value,\n      K>;\n  using BottomTest = BottomKeyEqual<char>;\n  static_assert(sizeof(BottomTest) == sizeof(KeyEqual), \"mismatch size\");\n  static_assert(alignof(BottomTest) == alignof(KeyEqual), \"mismatch align\");\n  static_assert(\n      std::is_empty<BottomTest>::value == std::is_empty<KeyEqual>::value,\n      \"mismatch is-empty\");\n  static_assert(\n      (std::is_final<BottomTest>::value || std::is_union<BottomTest>::value) ==\n          (std::is_final<KeyEqual>::value || std::is_union<KeyEqual>::value),\n      \"mismatch is-final\");\n\n  template <typename Iter, typename LocalIter>\n  static std::\n      enable_if_t<std::is_constructible<Iter, LocalIter const&>::value, Iter>\n      fromLocal(LocalIter const& src, int = 0) {\n    return Iter(src);\n  }\n\n  template <typename Iter, typename LocalIter>\n  static std::\n      enable_if_t<!std::is_constructible<Iter, LocalIter const&>::value, Iter>\n      fromLocal(LocalIter const& src) {\n    Iter dst;\n    static_assert(sizeof(dst) <= sizeof(src));\n    std::memcpy(std::addressof(dst), std::addressof(src), sizeof(dst));\n    FOLLY_SAFE_CHECK(\n        std::addressof(*src) == std::addressof(*dst),\n        \"ABI-assuming local_iterator to iterator conversion failed\");\n    return dst;\n  }\n\n  template <typename Iter, typename Self, typename K>\n  static Iter findImpl(Self& self, K const& key) {\n    if (self.empty()) {\n      return self.end();\n    }\n    using A = typename std::allocator_traits<\n        allocator_type>::template rebind_alloc<K>;\n    using E = BottomKeyEqual<K>;\n    // this is exceedingly wicked!\n    auto slot =\n        reinterpret_cast<std::unordered_set<K, hasher, E, A> const&>(self)\n            .bucket(key);\n    auto b = self.begin(slot);\n    auto e = self.end(slot);\n    while (b != e) {\n      if (self.key_eq()(key, *b)) {\n        return fromLocal<Iter>(b);\n      }\n      ++b;\n    }\n    FOLLY_SAFE_DCHECK(\n        self.size() > 3 ||\n            std::none_of(\n                self.begin(),\n                self.end(),\n                [&](auto const& k) { return self.key_eq()(key, k); }),\n        \"\");\n    return self.end();\n  }\n\n public:\n  using Super::count;\n\n  template <typename K>\n  EnableHeterogeneousFind<K, size_type> count(K const& key) const {\n    return contains(key) ? 1 : 0;\n  }\n\n  using Super::find;\n\n  template <typename K>\n  EnableHeterogeneousFind<K, iterator> find(K const& key) {\n    return findImpl<iterator>(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, const_iterator> find(K const& key) const {\n    return findImpl<const_iterator>(*this, key);\n  }\n\n  bool contains(key_type const& key) const { return find(key) != this->end(); }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, bool> contains(K const& key) const {\n    return find(key) != this->end();\n  }\n\n private:\n  template <typename Self, typename K>\n  static auto equalRangeImpl(Self& self, K const& key) {\n    auto first = self.find(key);\n    auto last = first;\n    if (last != self.end()) {\n      ++last;\n    }\n    return std::make_pair(first, last);\n  }\n\n public:\n  using Super::equal_range;\n\n  template <typename K>\n  EnableHeterogeneousFind<K, std::pair<iterator, iterator>> equal_range(\n      K const& key) {\n    return equalRangeImpl(*this, key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, std::pair<const_iterator, const_iterator>>\n  equal_range(K const& key) const {\n    return equalRangeImpl(*this, key);\n  }\n\n  //// PUBLIC - F14 Extensions\n\n private:\n  // converts const_iterator to iterator when they are different types\n  // such as in libstdc++\n  template <typename... Args>\n  iterator citerToIter(const_iterator cit, Args&&...) {\n    iterator it = erase(cit, cit);\n    FOLLY_SAFE_CHECK(std::addressof(*it) == std::addressof(*cit), \"\");\n    return it;\n  }\n\n  // converts const_iterator to iterator when they are the same type\n  // such as in libc++\n  iterator citerToIter(iterator it) { return it; }\n\n public:\n  template <typename BeforeDestroy>\n  iterator eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {\n    iterator next = citerToIter(pos);\n    ++next;\n    auto nh = this->extract(pos);\n    if (!nh.empty()) {\n      beforeDestroy(std::move(nh.value()));\n    }\n    return next;\n  }\n\n  template <typename BeforeDestroy>\n  iterator eraseInto(\n      const_iterator first,\n      const_iterator last,\n      BeforeDestroy&& beforeDestroy) {\n    iterator pos = citerToIter(first);\n    while (pos != last) {\n      pos = eraseInto(pos, beforeDestroy);\n    }\n    return pos;\n  }\n\n private:\n  template <typename K, typename BeforeDestroy>\n  size_type eraseIntoImpl(K const& key, BeforeDestroy& beforeDestroy) {\n    auto it = find(key);\n    if (it != this->end()) {\n      eraseInto(it, beforeDestroy);\n      return 1;\n    } else {\n      return 0;\n    }\n  }\n\n public:\n  template <typename BeforeDestroy>\n  size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseIntoImpl(key, beforeDestroy);\n  }\n\n  template <typename K, typename BeforeDestroy>\n  EnableHeterogeneousErase<K, size_type> eraseInto(\n      K const& key, BeforeDestroy&& beforeDestroy) {\n    return eraseIntoImpl(key, beforeDestroy);\n  }\n\n  bool containsEqualValue(value_type const& value) const {\n    // bucket is only valid if bucket_count is non-zero\n    if (this->empty()) {\n      return false;\n    }\n    auto slot = this->bucket(value);\n    auto e = this->end(slot);\n    for (auto b = this->begin(slot); b != e; ++b) {\n      if (*b == value) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  // exact for libstdc++, approximate for others\n  std::size_t getAllocatedMemorySize() const {\n    std::size_t rv = 0;\n    visitAllocationClasses([&](std::size_t bytes, std::size_t n) {\n      rv += bytes * n;\n    });\n    return rv;\n  }\n\n  // exact for libstdc++, approximate for others\n  template <typename V>\n  void visitAllocationClasses(V&& visitor) const {\n    auto bc = this->bucket_count();\n    if (bc > 1) {\n      visitor(bc * sizeof(pointer), 1);\n    }\n    if (this->size() > 0) {\n      visitor(\n          sizeof(StdNodeReplica<key_type, value_type, hasher>), this->size());\n    }\n  }\n\n  template <typename V>\n  void visitContiguousRanges(V&& visitor) const {\n    for (value_type const& entry : *this) {\n      value_type const* b = std::addressof(entry);\n      visitor(b, b + 1);\n    }\n  }\n\n  /// F14HashToken interface\n  template <class... Args>\n  std::pair<iterator, bool> emplace_token(F14HashToken const&, Args&&... args) {\n    return emplace(std::forward<Args>(args)...);\n  }\n\n  F14HashToken prehash(key_type const& /*key*/) const {\n    return {}; // Ignored.\n  }\n  F14HashToken prehash(key_type const& /*key*/, std::size_t /*hash*/) const {\n    return {}; // Ignored.\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, F14HashToken> prehash(K const& /*key*/) const {\n    return {};\n  }\n  template <typename K>\n  EnableHeterogeneousFind<K, F14HashToken> prehash(\n      K const& /*key*/, std::size_t /*hash*/) const {\n    return {};\n  }\n\n  void prefetch(F14HashToken const& /*token*/) const {}\n\n  iterator find(F14HashToken const&, key_type const& key) { return find(key); }\n\n  const_iterator find(F14HashToken const&, key_type const& key) const {\n    return find(key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, iterator> find(F14HashToken const&, K const& key) {\n    return find(key);\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, const_iterator> find(\n      F14HashToken const&, K const& key) const {\n    return find(key);\n  }\n\n  bool contains(F14HashToken const&, key_type const& key) const {\n    return find(key) != this->end();\n  }\n\n  template <typename K>\n  EnableHeterogeneousFind<K, bool> contains(\n      F14HashToken const&, K const& key) const {\n    return find(key) != this->end();\n  }\n};\n} // namespace detail\n} // namespace f14\n\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14NodeSet\n    : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::value_type;\n\n  F14NodeSet() = default;\n\n  using Super::Super;\n\n  F14NodeSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14ValueSet\n    : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::value_type;\n\n  F14ValueSet() : Super() {}\n\n  using Super::Super;\n\n  F14ValueSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14VectorSet\n    : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::value_type;\n\n  F14VectorSet() = default;\n\n  using Super::Super;\n\n  F14VectorSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\ntemplate <typename Key, typename Hasher, typename KeyEqual, typename Alloc>\nclass F14FastSet\n    : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> {\n  using Super = f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc>;\n\n public:\n  using typename Super::value_type;\n\n  F14FastSet() = default;\n\n  using Super::Super;\n\n  F14FastSet& operator=(std::initializer_list<value_type> ilist) {\n    Super::operator=(ilist);\n    return *this;\n  }\n};\n\n} // namespace folly\n\n#endif // !if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n"
  },
  {
    "path": "folly/container/detail/F14Table.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/detail/F14Table.h>\n\n#include <atomic>\n#include <chrono>\n\nnamespace folly {\nnamespace f14 {\nnamespace detail {\n\n// If you get a link failure that leads you here, your build has varying\n// compiler flags across compilation units in a way that would break F14.\n// SIMD (SSE2 or NEON) needs to be either on everywhere or off everywhere\n// that uses F14.  If SIMD is on then hardware CRC needs to be enabled\n// everywhere or disabled everywhere.\nvoid F14LinkCheck<getF14IntrinsicsMode()>::check() noexcept {}\n\n//// Debug and ASAN stuff\n\nbool tlsPendingSafeInserts(std::ptrdiff_t delta) {\n  static std::atomic<size_t> value_non_tl{0};\n  static thread_local std::atomic<size_t> value_tl{0};\n  auto& value = kIsDebug || kIsLibrarySanitizeAddress ? value_tl : value_non_tl;\n\n  FOLLY_SAFE_DCHECK(delta >= -1, \"\");\n  std::size_t v = value.load(std::memory_order_acquire);\n  if (delta > 0 || (delta == -1 && v > 0)) {\n    v += delta;\n    v = std::min(std::numeric_limits<std::size_t>::max() / 2, v);\n    value.store(v, std::memory_order_release);\n  }\n  return v != 0;\n}\n\nstd::size_t tlsMinstdRand(std::size_t n) {\n  static std::atomic<uint32_t> state_non_tl{0};\n  static thread_local std::atomic<uint32_t> state_tl{0};\n  auto& state = kIsDebug || kIsLibrarySanitizeAddress ? state_tl : state_non_tl;\n\n  FOLLY_SAFE_DCHECK(n > 0, \"\");\n\n  auto s = state.load(std::memory_order_acquire);\n  if (s == 0) {\n    uint64_t seed = static_cast<uint64_t>(\n        std::chrono::steady_clock::now().time_since_epoch().count());\n    s = hash::twang_32from64(seed);\n  }\n\n  s = static_cast<uint32_t>((s * uint64_t{48271}) % uint64_t{2147483647});\n  state.store(s, std::memory_order_release);\n  return std::size_t{s} % n;\n}\n\n} // namespace detail\n} // namespace f14\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/F14Table.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <cstring>\n\n#include <array>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <new>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/Bits.h>\n#include <folly/ConstexprMath.h>\n#include <folly/Likely.h>\n#include <folly/Memory.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Pretty.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/portability/Builtins.h>\n\n#include <folly/container/HeterogeneousAccess.h>\n#include <folly/container/detail/F14Defaults.h>\n#include <folly/container/detail/F14IntrinsicsAvailability.h>\n#include <folly/container/detail/F14Mask.h>\n\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n#include <arm_neon_sve_bridge.h> // @manual\n#include <arm_sve.h>\n#endif\n\n#if __has_include(<concepts>)\n#include <concepts>\n#endif\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\n#if FOLLY_F14_CRC_INTRINSIC_AVAILABLE\n#if FOLLY_NEON\n#include <arm_acle.h> // __crc32cd\n#else\n#include <nmmintrin.h> // _mm_crc32_u64\n#endif\n#else\n#ifdef _WIN32\n#include <intrin.h> // _mul128 in fallback bit mixer\n#endif\n#endif\n\n#if FOLLY_NEON\n#include <arm_neon.h> // uint8x16t intrinsics\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n#include <arm_neon_sve_bridge.h> // @manual\n#include <arm_sve.h>\n#endif\n#elif FOLLY_SSE >= 2 // SSE2\n#include <emmintrin.h> // _mm_set1_epi8\n#include <immintrin.h> // __m128i intrinsics\n#include <xmmintrin.h> // _mm_prefetch\n#endif\n\n#ifndef FOLLY_F14_PERTURB_INSERTION_ORDER\n#define FOLLY_F14_PERTURB_INSERTION_ORDER folly::kIsDebug\n#endif\n\n#else // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\n#ifndef FOLLY_F14_PERTURB_INSERTION_ORDER\n#define FOLLY_F14_PERTURB_INSERTION_ORDER false\n#endif\n\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nnamespace folly {\n\nstruct F14TableStats {\n  char const* policy;\n  std::size_t size{0};\n  std::size_t valueSize{0};\n  std::size_t bucketCount{0};\n  std::size_t chunkCount{0};\n  std::vector<std::size_t> chunkOccupancyHisto;\n  std::vector<std::size_t> chunkOutboundOverflowHisto;\n  std::vector<std::size_t> chunkHostedOverflowHisto;\n  std::vector<std::size_t> keyProbeLengthHisto;\n  std::vector<std::size_t> missProbeLengthHisto;\n  std::size_t totalBytes{0};\n  std::size_t overheadBytes{0};\n\n private:\n  template <typename T>\n  static auto computeHelper(T const* m) -> decltype(m->computeStats()) {\n    return m->computeStats();\n  }\n\n  static F14TableStats computeHelper(...) { return {}; }\n\n public:\n  template <typename T>\n  static F14TableStats compute(T const& m) {\n    return computeHelper(&m);\n  }\n};\n\nnamespace f14 {\nnamespace detail {\n\ntemplate <F14IntrinsicsMode>\nstruct F14LinkCheck {};\n\ntemplate <>\nstruct F14LinkCheck<getF14IntrinsicsMode()> {\n  // The purpose of this method is to trigger a link failure if\n  // compilation flags vary across compilation units.  The definition\n  // is in F14Table.cpp, so only one of F14LinkCheck<None>::check,\n  // F14LinkCheck<Simd>::check, or F14LinkCheck<SimdAndCrc>::check will\n  // be available at link time.\n  //\n  // To cause a link failure the function must be invoked in code that\n  // is not optimized away, so we call it on a couple of cold paths\n  // (exception handling paths in copy construction and rehash).  LTO may\n  // remove it entirely, but that's fine.\n  static void check() noexcept;\n};\n\nbool tlsPendingSafeInserts(std::ptrdiff_t delta = 0);\nstd::size_t tlsMinstdRand(std::size_t n);\n\n#if defined(_LIBCPP_VERSION)\n\ntemplate <typename K, typename V, typename H>\nstruct StdNodeReplica {\n  void* next;\n  std::size_t hash;\n  V value;\n};\n\n#elif defined(__GLIBCXX__)\n\ntemplate <typename K, typename H>\nconstexpr bool kStdNodeContainsHash =\n    !std::__is_fast_hash<H>::value || !is_nothrow_invocable_v<H, K const&>;\n\n// mimic internal node of unordered containers in STL to estimate the size\ntemplate <typename K, typename V, typename H, typename Enable = void>\nstruct StdNodeReplica {\n  void* next;\n  V value;\n};\ntemplate <typename K, typename V, typename H>\nstruct StdNodeReplica<K, V, H, std::enable_if_t<kStdNodeContainsHash<K, H>>> {\n  void* next;\n  V value;\n  std::size_t hash;\n};\n\n#else\n\ntemplate <typename K, typename V, typename H>\nstruct StdNodeReplica {\n  void* next;\n  V value;\n};\n\n#endif\n\ntemplate <class Container, class Predicate>\ntypename Container::size_type erase_if_impl(\n    Container& c, Predicate& predicate) {\n  auto const old_size = c.size();\n  for (auto i = c.begin(), last = c.end(); i != last;) {\n    auto prev = i++;\n    if (predicate(*prev)) {\n      c.erase(prev);\n    }\n  }\n  return old_size - c.size();\n}\n\n} // namespace detail\n} // namespace f14\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nnamespace f14 {\nnamespace detail {\ntemplate <typename Policy>\nclass F14Table;\n} // namespace detail\n} // namespace f14\n\nclass F14HashToken final {\n public:\n  constexpr F14HashToken() = default;\n\n  friend constexpr bool operator==(\n      F14HashToken const& a, F14HashToken const& b) noexcept {\n    return a.hp_.first == b.hp_.first; // processed hash but not tag\n  }\n  friend constexpr bool operator!=(\n      F14HashToken const& a, F14HashToken const& b) noexcept {\n    return !(a == b);\n  }\n\n private:\n  using HashPair = std::pair<std::size_t, std::size_t>;\n\n  constexpr explicit F14HashToken(HashPair hp) noexcept : hp_(hp) {}\n  constexpr explicit operator HashPair() const noexcept { return hp_; }\n\n  HashPair hp_;\n\n  template <typename Policy>\n  friend class f14::detail::F14Table;\n\n  template <typename Key, typename Hasher, typename KeyEqual>\n  friend class F14HashedKey;\n};\n\n#else\nclass F14HashToken final {\n  friend constexpr bool operator==(\n      F14HashToken const&, F14HashToken const&) noexcept {\n    return true;\n  }\n  friend constexpr bool operator!=(\n      F14HashToken const&, F14HashToken const&) noexcept {\n    return false;\n  }\n};\n#endif\n\n#if defined(__cpp_concepts) && __cpp_concepts && __has_include(<concepts>)\nstatic_assert(std::regular<F14HashToken>);\n#endif\n\nnamespace f14 {\nnamespace detail {\n\n// Detection for folly_assume_32bit_hash\n\ntemplate <typename Hasher, typename Void = void>\nstruct ShouldAssume32BitHash : std::bool_constant<!require_sizeof<Hasher>> {};\n\ntemplate <typename Hasher>\nstruct ShouldAssume32BitHash<\n    Hasher,\n    void_t<typename Hasher::folly_assume_32bit_hash>>\n    : std::bool_constant<Hasher::folly_assume_32bit_hash::value> {};\n\n//////// hash helpers\n\n// Hash values are used to compute the desired position, which is the\n// chunk index at which we would like to place a value (if there is no\n// overflow), and the tag, which is an additional 7 bits of entropy.\n//\n// The standard's definition of hash function quality only refers to\n// the probability of collisions of the entire hash value, not to the\n// probability of collisions of the results of shifting or masking the\n// hash value.  Some hash functions, however, provide this stronger\n// guarantee (not quite the same as the definition of avalanching,\n// but similar).\n//\n// If the user-supplied hasher is an avalanching one (each bit of the\n// hash value has a 50% chance of being the same for differing hash\n// inputs), then we can just take 7 bits of the hash value for the tag\n// and the rest for the desired position.  Avalanching hashers also\n// let us map hash value to array index position with just a bitmask\n// without risking clumping.  (Many hash tables just accept the risk\n// and do it regardless.)\n//\n// std::hash<std::string> avalanches in all implementations we've\n// examined: libstdc++-v3 uses MurmurHash2, and libc++ uses CityHash\n// or MurmurHash2.  The other std::hash specializations, however, do not\n// have this property.  std::hash for integral and pointer values is the\n// identity function on libstdc++-v3 and libc++, in particular.  In our\n// experience it is also fairly common for user-defined specializations\n// of std::hash to combine fields in an ad-hoc way that does not evenly\n// distribute entropy among the bits of the result (a + 37 * b, for\n// example, where a and b are integer fields).\n//\n// For hash functions we don't trust to avalanche, we repair things by\n// applying a bit mixer to the user-supplied hash.\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n#if FOLLY_X64 || FOLLY_AARCH64 || FOLLY_RISCV64\n// 64-bit\ntemplate <typename Hasher, typename Key>\nstd::pair<std::size_t, std::size_t> splitHashImpl(std::size_t hash) {\n  static_assert(sizeof(std::size_t) == sizeof(uint64_t));\n  std::size_t tag;\n  if (!IsAvalanchingHasher<Hasher, Key>::value) {\n#if FOLLY_F14_CRC_INTRINSIC_AVAILABLE\n#if FOLLY_SSE_PREREQ(4, 2)\n    // SSE4.2 CRC\n    std::size_t c = _mm_crc32_u64(0, hash);\n    tag = (c >> 24) | 0x80;\n    hash += c;\n#else\n    // CRC is optional on armv8 (-march=armv8-a+crc), standard on armv8.1\n    std::size_t c = __crc32cd(0, hash);\n    tag = (c >> 24) | 0x80;\n    hash += c;\n#endif\n#else\n    // The mixer below is not fully avalanching for all 64 bits of\n    // output, but looks quite good for bits 18..63 and puts plenty\n    // of entropy even lower when considering multiple bits together\n    // (like the tag).  Importantly, when under register pressure it\n    // uses fewer registers, instructions, and immediate constants\n    // than the alternatives, resulting in compact code that is more\n    // easily inlinable.  In one instantiation a modified Murmur mixer\n    // was 48 bytes of assembly (even after using the same multiplicand\n    // for both steps) and this one was 27 bytes, for example.\n    auto const kMul = 0xc4ceb9fe1a85ec53ULL;\n#ifdef _WIN32\n    __int64 signedHi;\n    __int64 signedLo = _mul128(\n        static_cast<__int64>(hash), static_cast<__int64>(kMul), &signedHi);\n    auto hi = static_cast<uint64_t>(signedHi);\n    auto lo = static_cast<uint64_t>(signedLo);\n#else\n    auto hi = static_cast<uint64_t>(\n        (static_cast<unsigned __int128>(hash) * kMul) >> 64);\n    auto lo = hash * kMul;\n#endif\n    hash = hi ^ lo;\n    hash *= kMul;\n    tag = ((hash >> 15) & 0x7f) | 0x80;\n    hash >>= 22;\n#endif\n  } else {\n    // F14 uses the bottom bits of the hash to form the index, so for maps\n    // with less than 16.7 million entries, it's safe to have a 32-bit hash,\n    // and use the bottom 24 bits for the index and leave the top 8 for the\n    // tag.\n    //\n    // | 0x80 sets the top bit in the tag.\n    // We need to avoid 0 tag for a non-empty value.\n    // In some places we also rely on the top bit\n    // being 1 for all non-empty values.\n    if (ShouldAssume32BitHash<Hasher>::value) {\n      tag = ((hash >> 24) | 0x80) & 0xFF;\n      // Explicitly mask off the top 32-bits so that the compiler can\n      // optimize away whatever is populating the top 32-bits, which is likely\n      // just the lower 32-bits duplicated.\n      hash = hash & 0xFFFF'FFFF;\n    } else {\n      tag = (hash >> 56) | 0x80;\n    }\n  }\n  return std::make_pair(hash, tag);\n}\n#else\n// 32-bit\ntemplate <typename Hasher, typename Key>\nstd::pair<std::size_t, std::size_t> splitHashImpl(std::size_t hash) {\n  static_assert(sizeof(std::size_t) == sizeof(uint32_t));\n  uint8_t tag;\n  if (!IsAvalanchingHasher<Hasher, Key>::value) {\n#if FOLLY_F14_CRC_INTRINSIC_AVAILABLE\n#if FOLLY_SSE_PREREQ(4, 2)\n    // SSE4.2 CRC\n    auto c = _mm_crc32_u32(0, hash);\n    tag = static_cast<uint8_t>(~(c >> 25));\n    hash += c;\n#else\n    auto c = __crc32cw(0, hash);\n    tag = static_cast<uint8_t>(~(c >> 25));\n    hash += c;\n#endif\n#else\n    // finalizer for 32-bit murmur2\n    hash ^= hash >> 13;\n    hash *= 0x5bd1e995;\n    hash ^= hash >> 15;\n    tag = static_cast<uint8_t>(~(hash >> 25));\n#endif\n  } else {\n    // | 0x80 sets the top bit in the tag.\n    // We need to avoid 0 tag for a non-empty value.\n    // In some places we also rely on the top bit\n    // being 1 for all non-empty values.\n    tag = (hash >> 24) | 0x80;\n  }\n  return std::make_pair(hash, tag);\n}\n#endif\n#endif\n} // namespace detail\n} // namespace f14\n\ntemplate <\n    typename TKeyType,\n    typename Hasher = f14::DefaultHasher<TKeyType>,\n    typename KeyEqual = f14::DefaultKeyEqual<TKeyType>>\nclass F14HashedKey final {\n private:\n  template <typename K>\n  using EligibleForHeterogeneousCompare =\n      detail::EligibleForHeterogeneousFind<TKeyType, Hasher, KeyEqual, K>;\n\n  template <typename K, typename T>\n  using EnableHeterogeneousCompare =\n      std::enable_if_t<EligibleForHeterogeneousCompare<K>::value, T>;\n\n  static constexpr void checkTemplateParamContract() {\n    static_assert(is_constexpr_default_constructible_v<Hasher>);\n    static_assert(is_constexpr_default_constructible_v<KeyEqual>);\n    static_assert(std::is_trivially_copyable_v<Hasher>);\n    static_assert(std::is_trivially_copyable_v<KeyEqual>);\n    static_assert(std::is_empty_v<Hasher>);\n    static_assert(std::is_empty_v<KeyEqual>);\n    // When `Hasher` or `KeyEqual` is not transparent, `F14HashedKey` will\n    // behave like `TKeyType` without any performance effect, it is most likely\n    // not what is expected.\n    static_assert(is_transparent_v<Hasher>);\n    static_assert(is_transparent_v<KeyEqual>);\n  }\n\n public:\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n  template <typename... Args>\n  explicit F14HashedKey(Args&&... args)\n      : key_(std::forward<Args>(args)...),\n        hash_(f14::detail::splitHashImpl<Hasher, TKeyType>(Hasher{}(key_))) {\n    checkTemplateParamContract();\n  }\n#else\n  F14HashedKey() = delete;\n#endif\n\n  const TKeyType& getKey() const { return key_; }\n  const F14HashToken& getHashToken() const { return hash_; }\n  // We want the conversion to the key to be implicit - the hashed key should\n  // seamlessly behave as the key itself.\n  /* implicit */ operator const TKeyType&() const { return key_; }\n  explicit operator const F14HashToken&() const { return hash_; }\n\n  template <typename T>\n  using IsRangeConvertible =\n      std::enable_if_t<detail::TransparentlyConvertibleToRange<T>::value>;\n\n  template <typename T>\n  using RangeT =\n      Range<typename detail::ValueTypeForTransparentConversionToRange<\n          T>::type const*>;\n\n  template <typename K = TKeyType, typename Enable = IsRangeConvertible<K>>\n  constexpr explicit operator RangeT<K>() const {\n    return key_;\n  }\n\n  friend bool operator==(const F14HashedKey& a, const F14HashedKey& b) {\n    return KeyEqual{}(a.key_, b.key_);\n  }\n  friend bool operator!=(const F14HashedKey& a, const F14HashedKey& b) {\n    return !(a == b);\n  }\n  friend bool operator==(const F14HashedKey& a, const TKeyType& b) {\n    return KeyEqual{}(a.key_, b);\n  }\n  friend bool operator!=(const F14HashedKey& a, const TKeyType& b) {\n    return !(a == b);\n  }\n  friend bool operator==(const TKeyType& a, const F14HashedKey& b) {\n    return KeyEqual{}(a, b.key_);\n  }\n  friend bool operator!=(const TKeyType& a, const F14HashedKey& b) {\n    return !(a == b);\n  }\n  template <typename K>\n  friend EnableHeterogeneousCompare<K, bool> operator==(\n      const F14HashedKey& a, const K& b) {\n    return KeyEqual{}(a.key_, b);\n  }\n  template <typename K>\n  friend EnableHeterogeneousCompare<K, bool> operator!=(\n      const F14HashedKey& a, const K& b) {\n    return !(a == b);\n  }\n  template <typename K>\n  friend EnableHeterogeneousCompare<K, bool> operator==(\n      const K& a, const F14HashedKey& b) {\n    return KeyEqual{}(a, b.key_);\n  }\n  template <typename K>\n  friend EnableHeterogeneousCompare<K, bool> operator!=(\n      const K& a, const F14HashedKey& b) {\n    return !(a == b);\n  }\n\n private:\n  TKeyType key_;\n  F14HashToken hash_;\n};\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nnamespace f14 {\nnamespace detail {\n\n//// Defaults should be selected using void\ntemplate <typename Arg, typename Default>\nusing VoidDefault =\n    std::conditional_t<std::is_same<Arg, Default>::value, void, Arg>;\n\ntemplate <typename Arg, typename Default>\nusing Defaulted =\n    std::conditional_t<std::is_same<Arg, void>::value, Default, Arg>;\n\n////////////////\n\n/// Prefetch the first cache line of the object at ptr.\ntemplate <typename T>\nFOLLY_ALWAYS_INLINE static void prefetchAddr(T const* ptr) {\n#ifndef _WIN32\n  FOLLY_PUSH_WARNING\n  FOLLY_GNU_DISABLE_WARNING(\"-Warray-bounds\")\n  /// The argument ptr is permitted to be wild, since wild pointers are allowed\n  /// for prefetching on every architecture. While this behavior is technically\n  /// undefined (forbidden) in C and C++, we need this behavior in order to\n  /// avoid extra cost in the callers. Recent versions of GCC warn when they\n  /// detect uses of pointers which may be wild. So we suppress the warning.\n  __builtin_prefetch(static_cast<void const*>(ptr));\n  FOLLY_POP_WARNING\n#elif FOLLY_NEON\n  __prefetch(static_cast<void const*>(ptr));\n#elif FOLLY_SSE >= 2\n  _mm_prefetch(\n      static_cast<char const*>(static_cast<void const*>(ptr)), _MM_HINT_T0);\n#endif\n}\n\n#if FOLLY_NEON\nusing TagVector = uint8x16_t;\n#elif FOLLY_SSE >= 2\nusing TagVector = __m128i;\n#elif FOLLY_HAVE_INT128_T\nusing TagVector = __uint128_t;\n#endif\n\n// We could use unaligned loads to relax this requirement, but that\n// would be both a performance penalty and require a bulkier packed\n// ItemIter format\nconstexpr std::size_t kRequiredVectorAlignment =\n    constexpr_max(std::size_t{16}, alignof(max_align_t));\n\nstruct alignas(kRequiredVectorAlignment) F14EmptyTagVector {\n  std::array<uint8_t, 15> bytes_{};\n  uint8_t marker_{255}; // coincides with outboundOverflowCount_\n};\n\nFOLLY_EXPORT inline F14EmptyTagVector& getF14EmptyTagVector() noexcept {\n  static constexpr F14EmptyTagVector instance;\n  auto const raw = reinterpret_cast<uintptr_t>(&instance);\n  FOLLY_SAFE_DCHECK(\n      (raw % kRequiredVectorAlignment) == 0,\n      raw,\n      \" not aligned to \",\n      kRequiredVectorAlignment);\n  return const_cast<F14EmptyTagVector&>(instance);\n}\n\ntemplate <typename ItemType>\nstruct alignas(constexpr_max(kRequiredVectorAlignment, alignof(ItemType)))\n    F14Chunk {\n  using Item = ItemType;\n\n  // Alignment required for chunks, which must satisfy both the SIMD tag\n  // vector alignment and the stored item alignment.\n  static constexpr std::size_t kRequiredChunkAlignment =\n      constexpr_max(kRequiredVectorAlignment, alignof(Item));\n\n  // For our 16 byte vector alignment (and assuming alignof(Item) >=\n  // 4) kCapacity of 14 is the most space efficient.  Slightly smaller\n  // or larger capacities can help with cache alignment in a couple of\n  // cases without wasting too much space, but once the items are larger\n  // then we're unlikely to get much benefit anyway.  The only case we\n  // optimize is using kCapacity of 12 for 4 byte items, which makes the\n  // chunk take exactly 1 cache line, and adding 16 bytes of padding for\n  // 16 byte items so that a chunk takes exactly 4 cache lines.\n  static constexpr unsigned kCapacity = sizeof(Item) == 4 ? 12 : 14;\n\n  static constexpr unsigned kDesiredCapacity = kCapacity - 2;\n\n  static constexpr unsigned kAllocatedCapacity =\n      kCapacity + (sizeof(Item) == 16 ? 1 : 0);\n\n  // If kCapacity == 12 then we get 16 bits of capacityScale by using\n  // tag 12 and 13, otherwise we only get 4 bits of control_\n  static constexpr std::size_t kCapacityScaleBits = kCapacity == 12 ? 16 : 4;\n  static constexpr std::size_t kCapacityScaleShift = kCapacityScaleBits - 4;\n\n  static constexpr MaskType kFullMask = FullMask<kCapacity>::value;\n\n  static constexpr std::uint8_t kOutboundOverflowMax = 254;\n  static constexpr std::uint8_t kOutboundOverflowEmpty = 255;\n\n  // Non-empty tags have their top bit set.  tags_ array might be bigger\n  // than kCapacity to keep alignment of first item.\n  std::array<uint8_t, 14> tags_;\n\n  // Bits 0..3 of chunk 0 record the scaling factor between the number of\n  // chunks and the max size without rehash.  Bits 4-7 in any chunk are a\n  // 4-bit counter of the number of values in this chunk that were placed\n  // because they overflowed their desired chunk (hostedOverflowCount).\n  uint8_t control_;\n\n  // The number of values that would have been placed into this chunk if\n  // there had been space, including values that also overflowed previous\n  // full chunks.  This value saturates; once it becomes 254 it no longer\n  // increases nor decreases.\n  uint8_t outboundOverflowCount_;\n\n  // Items are stored at a fixed byte offset from the start of the chunk,\n  // accessed via pointer arithmetic rather than a member array.  This keeps\n  // sizeof(F14Chunk) small (header only), which satisfies UBSAN's\n  // object-size check even for single-chunk tables that allocate fewer\n  // than kAllocatedCapacity items.  The offset is the chunk alignment to\n  // ensure items are properly aligned.\n  static constexpr std::size_t kItemsOffset = kRequiredChunkAlignment;\n\n  // Stride between consecutive chunks in a multi-chunk allocation.\n  // This equals the header (sizeof(F14Chunk)) + items, rounded up\n  // to alignment.\n  static constexpr std::size_t kChunkStride = align_ceil(\n      kItemsOffset + kAllocatedCapacity * sizeof(Item),\n      kRequiredChunkAlignment);\n\n  // Navigate between chunks using kChunkStride instead of sizeof(F14Chunk).\n private:\n  template <typename ChunkType>\n  static ChunkType* chunkRawAtImpl(ChunkType* base, std::size_t i) {\n    auto* bytePtr = reinterpret_cast<char*>(base);\n    auto offset = static_cast<std::ptrdiff_t>(i) *\n        static_cast<std::ptrdiff_t>(kChunkStride);\n    return reinterpret_cast<ChunkType*>(bytePtr + offset);\n  }\n\n  template <typename ChunkType>\n  static ChunkType* chunkRawOffsetImpl(ChunkType* c, std::ptrdiff_t delta) {\n    auto* bytePtr = reinterpret_cast<char*>(c);\n    auto offset = delta * static_cast<std::ptrdiff_t>(kChunkStride);\n    return reinterpret_cast<ChunkType*>(bytePtr + offset);\n  }\n\n public:\n  static F14Chunk* chunkRawAt(F14Chunk* base, std::size_t i) {\n    return chunkRawAtImpl(base, i);\n  }\n  static F14Chunk const* chunkRawAt(F14Chunk const* base, std::size_t i) {\n    return chunkRawAtImpl(base, i);\n  }\n\n  static F14Chunk* prevChunkRaw(F14Chunk* c) {\n    return chunkRawOffsetImpl(c, -1);\n  }\n  static F14Chunk const* prevChunkRaw(F14Chunk const* c) {\n    return chunkRawOffsetImpl(c, -1);\n  }\n\n  static F14Chunk* nextChunkRaw(F14Chunk* c) {\n    return chunkRawOffsetImpl(c, 1);\n  }\n  static F14Chunk const* nextChunkRaw(F14Chunk const* c) {\n    return chunkRawOffsetImpl(c, 1);\n  }\n\n  static std::size_t chunkRawIndex(\n      F14Chunk const* base, F14Chunk const* target) {\n    auto const* baseBytes = reinterpret_cast<char const*>(base);\n    auto const* targetBytes = reinterpret_cast<char const*>(target);\n    auto const byteOffset = targetBytes - baseBytes;\n    return static_cast<std::size_t>(byteOffset) / kChunkStride;\n  }\n\n  FOLLY_EXPORT static F14Chunk* getSomeEmptyInstance() noexcept {\n    return reinterpret_cast<F14Chunk*>(&getF14EmptyTagVector());\n  }\n\n  static bool isEmptyInstance(F14Chunk const* const chunk) noexcept {\n    auto const empty = reinterpret_cast<F14EmptyTagVector const*>(chunk);\n    return empty->marker_ == kOutboundOverflowEmpty;\n  }\n\n  void clear() {\n    // tags_ = {}; control_ = 0; outboundOverflowCount_ = 0;\n\n    // gcc < 6 doesn't exploit chunk alignment to generate the optimal\n    // SSE clear from memset.  This is very hot code, so it is worth\n    // handling that case specially.\n    std::memset(&tags_[0], '\\0', 16);\n  }\n\n  void copyOverflowInfoFrom(F14Chunk const& rhs) {\n    FOLLY_SAFE_DCHECK(!isEmptyInstance(&rhs));\n    FOLLY_SAFE_DCHECK(hostedOverflowCount() == 0, \"\");\n    control_ += static_cast<uint8_t>(rhs.control_ & 0xf0);\n    outboundOverflowCount_ = rhs.outboundOverflowCount_;\n  }\n\n  unsigned hostedOverflowCount() const { return control_ >> 4; }\n\n  static constexpr uint8_t kIncrHostedOverflowCount = 0x10;\n  static constexpr uint8_t kDecrHostedOverflowCount =\n      static_cast<uint8_t>(-0x10);\n\n  void adjustHostedOverflowCount(uint8_t op) { control_ += op; }\n\n  bool eof() const { return capacityScale(this) != 0; }\n\n  static std::size_t capacityScale(F14Chunk const* const chunk) {\n    auto const empty = reinterpret_cast<F14EmptyTagVector const*>(chunk);\n    if constexpr (kCapacityScaleBits == 4) {\n      return empty->bytes_[offsetof(F14Chunk, control_)] & 0xf;\n    } else {\n      uint16_t v;\n      std::memcpy(&v, &empty->bytes_[12], 2);\n      return v;\n    }\n  }\n\n  void setCapacityScale(std::size_t scale) {\n    FOLLY_SAFE_DCHECK(\n        !isEmptyInstance(this) && scale > 0 &&\n            scale < (std::size_t{1} << kCapacityScaleBits),\n        \"\");\n    if constexpr (kCapacityScaleBits == 4) {\n      control_ = static_cast<uint8_t>((control_ & ~0xf) | scale);\n    } else {\n      uint16_t v = static_cast<uint16_t>(scale);\n      std::memcpy(&tags_[12], &v, 2);\n    }\n  }\n\n  void markEof(std::size_t scale) {\n    folly::assume(control_ == 0);\n    setCapacityScale(scale);\n  }\n\n  unsigned outboundOverflowCount() const { return outboundOverflowCount_; }\n\n  void incrOutboundOverflowCount() {\n    if (outboundOverflowCount_ != kOutboundOverflowMax) {\n      ++outboundOverflowCount_;\n    }\n  }\n\n  void decrOutboundOverflowCount() {\n    FOLLY_SAFE_DCHECK(outboundOverflowCount_ != 0);\n    if (outboundOverflowCount_ != kOutboundOverflowMax) {\n      --outboundOverflowCount_;\n    }\n  }\n\n  std::size_t tag(std::size_t index) const { return tags_[index]; }\n\n  void setTag(std::size_t index, std::size_t tag) {\n    FOLLY_SAFE_DCHECK(!isEmptyInstance(this) && tag >= 0x80 && tag <= 0xff, \"\");\n    FOLLY_SAFE_CHECK(tags_[index] == 0, \"\");\n    tags_[index] = static_cast<uint8_t>(tag);\n  }\n\n  void clearTag(std::size_t index) {\n    FOLLY_SAFE_CHECK((tags_[index] & 0x80) != 0, \"\");\n    tags_[index] = 0;\n  }\n\n#if FOLLY_NEON\n\n  ////////\n  // Tag filtering using NEON/SVE intrinsics\n\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n\n  SparseMaskIter tagMatchIter(\n      uint8x16_t needleV, svbool_t inPred, svbool_t& outPred) const {\n    svuint8_t tagV = svld1_u8(inPred, &tags_[0]);\n    // test if any match is found\n    outPred = svmatch_u8(inPred, tagV, svset_neonq_u8(svundef_u8(), needleV));\n    // get info from every byte into the bottom half of every uint16_t\n    // by shifting right 4, then round to get it into a 64-bit vector\n    uint8x8_t maskV = vshrn_n_u16(\n        vreinterpretq_u16_u8(svget_neonq(svdup_n_u8_z(outPred, 17))), 4);\n    uint64_t mask = vreinterpret_u64_u8(maskV)[0];\n    return SparseMaskIter(mask);\n  }\n\n#else\n\n  SparseMaskIter tagMatchIter(uint8x16_t needleV) const {\n    uint8x16_t tagV = vld1q_u8(&tags_[0]);\n    auto eqV = vceqq_u8(tagV, needleV);\n    // get info from every byte into the bottom half of every uint16_t\n    // by shifting right 4, then round to get it into a 64-bit vector\n    uint8x8_t maskV = vshrn_n_u16(vreinterpretq_u16_u8(eqV), 4);\n    uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(maskV), 0) & kFullMask;\n    return SparseMaskIter(mask);\n  }\n\n#endif\n\n  MaskType occupiedMask() const {\n    uint8x16_t tagV = vld1q_u8(&tags_[0]);\n    // signed shift extends top bit to all bits\n    auto occupiedV =\n        vreinterpretq_u8_s8(vshrq_n_s8(vreinterpretq_s8_u8(tagV), 7));\n    uint8x8_t maskV = vshrn_n_u16(vreinterpretq_u16_u8(occupiedV), 4);\n    return vget_lane_u64(vreinterpret_u64_u8(maskV), 0) & kFullMask;\n  }\n#elif FOLLY_SSE >= 2\n  ////////\n  // Tag filtering using SSE2 intrinsics\n\n  TagVector const* tagVector() const {\n    return static_cast<TagVector const*>(static_cast<void const*>(&tags_[0]));\n  }\n\n  auto tagMatchIter(__m128i needleV) const {\n    auto tagV = _mm_load_si128(tagVector());\n\n    auto eqV = _mm_cmpeq_epi8(tagV, needleV);\n    uint32_t mask = _mm_movemask_epi8(eqV);\n    if constexpr (kIsArchAmd64) {\n      return BoundedMaskIter<kCapacity>{mask};\n    } else {\n      return SparseMaskIter{mask & kFullMask};\n    }\n  }\n\n  MaskType occupiedMask() const {\n    auto tagV = _mm_load_si128(tagVector());\n    return _mm_movemask_epi8(tagV) & kFullMask;\n  }\n#elif FOLLY_HAVE_INT128_T\n  ////////\n  // Tag filtering using plain C/C++\n\n  SparseMaskIter tagMatchIter(std::size_t needle) const {\n    FOLLY_SAFE_DCHECK(needle >= 0x80 && needle < 0x100, \"\");\n    auto tagV = static_cast<uint8_t const*>(&tags_[0]);\n    MaskType mask = 0;\n    FOLLY_PRAGMA_UNROLL_N(16)\n    for (auto i = 0u; i < kCapacity; i++) {\n      mask |= ((tagV[i] == static_cast<uint8_t>(needle)) ? 1 : 0) << i;\n    }\n    return SparseMaskIter{mask & kFullMask};\n  }\n\n  MaskType occupiedMask() const {\n    auto tagV = static_cast<uint8_t const*>(&tags_[0]);\n    MaskType mask = 0;\n    FOLLY_PRAGMA_UNROLL_N(16)\n    for (auto i = 0u; i < kCapacity; i++) {\n      mask |= ((tagV[i] & 0x80) ? 1 : 0) << i;\n    }\n    return mask & kFullMask;\n  }\n#endif\n\n  auto occupiedIter() const {\n    if constexpr (kIsArchAArch64) {\n      return SparseMaskIter{this->occupiedMask()};\n    } else {\n      return DenseMaskIter{&tags_[0], this->occupiedMask()};\n    }\n  }\n  MaskRangeIter occupiedRangeIter() const {\n    return MaskRangeIter{this->occupiedMask()};\n  }\n\n  LastOccupiedInMask lastOccupied() const {\n    return LastOccupiedInMask{this->occupiedMask()};\n  }\n\n  FirstEmptyInMask firstEmpty() const {\n    return FirstEmptyInMask{this->occupiedMask() ^ kFullMask};\n  }\n\n  bool occupied(std::size_t index) const {\n    FOLLY_SAFE_DCHECK(tags_[index] == 0 || (tags_[index] & 0x80) != 0, \"\");\n    return tags_[index] != 0;\n  }\n\n  /// Permitted to return a wild pointer, which is allowed for prefetching on\n  /// every architecture. This behavior is technically undefined (forbidden) in\n  /// C and C++, but we violate the rule in order to avoid extra cost in the\n  /// prefetch paths. The wild pointer that may be returned is whatever follows\n  /// any empty-instance global in the memory of any DSO.\n  Item* itemAddr(std::size_t i) const {\n    auto const* chunkBytes = reinterpret_cast<unsigned char const*>(this);\n    auto const itemOffset = kItemsOffset + i * sizeof(Item);\n    auto const* itemBytes = chunkBytes + itemOffset;\n    return static_cast<Item*>(\n        const_cast<void*>(static_cast<void const*>(itemBytes)));\n  }\n\n  Item& item(std::size_t i) {\n    FOLLY_SAFE_DCHECK(this->occupied(i), \"\");\n    compiler_may_unsafely_assume(this != getSomeEmptyInstance());\n    return *std::launder(itemAddr(i));\n  }\n\n  Item const& citem(std::size_t i) const {\n    FOLLY_SAFE_DCHECK(this->occupied(i), \"\");\n    return *std::launder(itemAddr(i));\n  }\n\n  static F14Chunk& owner(Item& item, std::size_t index) {\n    auto rawAddr =\n        static_cast<uint8_t*>(static_cast<void*>(std::addressof(item))) -\n        kItemsOffset - index * sizeof(Item);\n    auto chunkAddr = static_cast<F14Chunk*>(static_cast<void*>(rawAddr));\n    FOLLY_SAFE_DCHECK(std::addressof(item) == chunkAddr->itemAddr(index), \"\");\n    return *chunkAddr;\n  }\n};\n\n////////////////\n\n// PackedChunkItemPtr points to an Item in an F14Chunk, allowing both the\n// Item& and its index to be recovered.  It sorts by the address of the\n// item, and it only works for items that are in a properly-aligned chunk.\n\n// generic form, not actually packed\ntemplate <typename Ptr>\nclass PackedChunkItemPtr {\n public:\n  PackedChunkItemPtr(Ptr p, std::size_t i) noexcept : ptr_{p}, index_{i} {\n    FOLLY_SAFE_DCHECK(ptr_ != nullptr || index_ == 0, \"\");\n  }\n\n  Ptr ptr() const { return ptr_; }\n\n  std::size_t index() const { return index_; }\n\n  bool operator<(PackedChunkItemPtr const& rhs) const {\n    FOLLY_SAFE_DCHECK(ptr_ != rhs.ptr_ || index_ == rhs.index_, \"\");\n    return ptr_ < rhs.ptr_;\n  }\n\n  bool operator==(PackedChunkItemPtr const& rhs) const {\n    FOLLY_SAFE_DCHECK(ptr_ != rhs.ptr_ || index_ == rhs.index_, \"\");\n    return ptr_ == rhs.ptr_;\n  }\n\n  bool operator!=(PackedChunkItemPtr const& rhs) const {\n    return !(*this == rhs);\n  }\n\n private:\n  Ptr ptr_;\n  std::size_t index_;\n};\n\n// Bare pointer form, packed into a uintptr_t.  Uses only bits wasted by\n// alignment, so it works on 32-bit and 64-bit platforms\ntemplate <typename T>\nclass PackedChunkItemPtr<T*> {\n  static_assert((alignof(F14Chunk<T>) % 16) == 0);\n\n  // Chunks are 16-byte aligned, so we can maintain a packed pointer to a\n  // chunk item by packing the 4-bit item index into the least significant\n  // bits of a pointer to the chunk itself.  This makes ItemIter::pack\n  // more expensive, however, since it has to compute the chunk address.\n  //\n  // Chunk items have varying alignment constraints, so it would seem\n  // to be that we can't do a similar trick while using only bit masking\n  // operations on the Item* itself.  It happens to be, however, that if\n  // sizeof(Item) is not a multiple of 16 then we can recover a portion\n  // of the index bits from the knowledge that the Item-s are stored in\n  // an array that is itself 16-byte aligned.\n  //\n  // If kAlignBits is the number of trailing zero bits in sizeof(Item)\n  // (up to 4), then we can borrow those bits to store kAlignBits of the\n  // index directly.  We can recover (4 - kAlignBits) bits of the index\n  // from the item pointer itself, by defining/observing that\n  //\n  // A = kAlignBits                  (A <= 4)\n  //\n  // S = (sizeof(Item) % 16) >> A    (shifted-away bits are all zero)\n  //\n  // R = (itemPtr % 16) >> A         (shifted-away bits are all zero)\n  //\n  // M = 16 >> A\n  //\n  // itemPtr % 16   = (index * sizeof(Item)) % 16\n  //\n  // (R * 2^A) % 16 = (index * (sizeof(Item) % 16)) % 16\n  //\n  // (R * 2^A) % 16 = (index * 2^A * S) % 16\n  //\n  // R % M          = (index * S) % M\n  //\n  // S is relatively prime with M, so a multiplicative inverse is easy\n  // to compute\n  //\n  // Sinv = S^(M - 1) % M\n  //\n  // (R * Sinv) % M = index % M\n  //\n  // This lets us recover the bottom bits of the index.  When sizeof(T)\n  // is 8-byte aligned kSizeInverse will always be 1.  When sizeof(T)\n  // is 4-byte aligned kSizeInverse will be either 1 or 3.\n\n  // returns pow(x, y) % m\n  static constexpr uintptr_t powerMod(uintptr_t x, uintptr_t y, uintptr_t m) {\n    return y == 0 ? 1 : (x * powerMod(x, y - 1, m)) % m;\n  }\n\n  static constexpr uintptr_t kIndexBits = 4;\n  static constexpr uintptr_t kIndexMask = (uintptr_t{1} << kIndexBits) - 1;\n\n  static constexpr uintptr_t kAlignBits = constexpr_min(\n      uintptr_t{4}, constexpr_find_first_set(uintptr_t{sizeof(T)}) - 1);\n\n  static constexpr uintptr_t kAlignMask = (uintptr_t{1} << kAlignBits) - 1;\n\n  static constexpr uintptr_t kModulus = uintptr_t{1}\n      << (kIndexBits - kAlignBits);\n  static constexpr uintptr_t kSizeInverse =\n      powerMod(sizeof(T) >> kAlignBits, kModulus - 1, kModulus);\n\n public:\n  PackedChunkItemPtr(T* p, std::size_t i) noexcept {\n    uintptr_t encoded = i >> (kIndexBits - kAlignBits);\n    assume((encoded & ~kAlignMask) == 0);\n    raw_ = reinterpret_cast<uintptr_t>(p) | encoded;\n    FOLLY_SAFE_DCHECK(p == ptr(), \"\");\n    FOLLY_SAFE_DCHECK(i == index(), \"\");\n  }\n\n  T* ptr() const { return reinterpret_cast<T*>(raw_ & ~kAlignMask); }\n\n  std::size_t index() const {\n    auto encoded = (raw_ & kAlignMask) << (kIndexBits - kAlignBits);\n    auto deduced =\n        ((raw_ >> kAlignBits) * kSizeInverse) & (kIndexMask >> kAlignBits);\n    return encoded | deduced;\n  }\n\n  bool operator<(PackedChunkItemPtr const& rhs) const {\n    return raw_ < rhs.raw_;\n  }\n  bool operator==(PackedChunkItemPtr const& rhs) const {\n    return raw_ == rhs.raw_;\n  }\n  bool operator!=(PackedChunkItemPtr const& rhs) const {\n    return !(*this == rhs);\n  }\n\n private:\n  uintptr_t raw_;\n};\n\ntemplate <typename ChunkPtr>\nclass F14ItemIter {\n private:\n  using Chunk = typename std::pointer_traits<ChunkPtr>::element_type;\n\n public:\n  using Item = typename Chunk::Item;\n  using ItemPtr = typename std::pointer_traits<ChunkPtr>::template rebind<Item>;\n  using ItemConstPtr =\n      typename std::pointer_traits<ChunkPtr>::template rebind<Item const>;\n\n  using Packed = PackedChunkItemPtr<ItemPtr>;\n\n  //// PUBLIC\n\n  F14ItemIter() noexcept : itemPtr_{nullptr}, index_{0} {}\n\n  // default copy and move constructors and assignment operators are correct\n\n  explicit F14ItemIter(Packed const& packed)\n      : itemPtr_{packed.ptr()}, index_{packed.index()} {}\n\n  F14ItemIter(ChunkPtr chunk, std::size_t index)\n      : itemPtr_{std::pointer_traits<ItemPtr>::pointer_to(chunk->item(index))},\n        index_{index} {\n    FOLLY_SAFE_DCHECK(index < Chunk::kCapacity, \"\");\n    assume(\n        std::pointer_traits<ItemPtr>::pointer_to(chunk->item(index)) !=\n        nullptr);\n    assume(itemPtr_ != nullptr);\n  }\n\n  FOLLY_ALWAYS_INLINE void advanceImpl(bool checkEof, bool likelyDead) {\n    auto c = chunk();\n\n    // common case is packed entries\n    while (index_ > 0) {\n      --index_;\n      --itemPtr_;\n      if (FOLLY_LIKELY(c->occupied(index_))) {\n        return;\n      }\n    }\n\n    // It's fairly common for an iterator to be advanced and then become\n    // dead, for example in the return value from erase(iter) or in\n    // the last step of a loop.  We'd like to make sure that the entire\n    // advance() method can be eliminated by the compiler's dead code\n    // elimination pass.  To do that it must eliminate the loops, which\n    // requires it to prove that they have no side effects.  It's easy\n    // to show that there are no escaping stores, but at the moment\n    // compilers also consider an infinite loop to be a side effect.\n    // (There are parts of the standard that would allow them to treat\n    // this as undefined behavior, but at the moment they don't exploit\n    // those clauses.)\n    //\n    // The following loop should really be a while loop, which would\n    // save a register, some instructions, and a conditional branch,\n    // but by writing it as a for loop the compiler can prove to itself\n    // that it will eventually terminate.  (No matter that even if the\n    // loop executed in a single cycle it would take about 200 years to\n    // run all 2^64 iterations.)\n    //\n    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82776 has the bug we\n    // filed about the issue.  while (true) {\n    for (std::size_t i = 1; !likelyDead || i != 0; ++i) {\n      if (checkEof) {\n        // exhausted the current chunk\n        if (FOLLY_UNLIKELY(c->eof())) {\n          FOLLY_SAFE_DCHECK(index_ == 0, \"\");\n          itemPtr_ = nullptr;\n          return;\n        }\n      } else {\n        FOLLY_SAFE_DCHECK(!c->eof(), \"\");\n      }\n      c = std::pointer_traits<ChunkPtr>::pointer_to(*Chunk::prevChunkRaw(&*c));\n      auto last = c->lastOccupied();\n      if (checkEof && !likelyDead) {\n        prefetchAddr(Chunk::prevChunkRaw(&*c));\n      }\n      if (FOLLY_LIKELY(last.hasIndex())) {\n        index_ = last.index();\n        itemPtr_ = std::pointer_traits<ItemPtr>::pointer_to(c->item(index_));\n        return;\n      }\n    }\n  }\n\n  void precheckedAdvance() { advanceImpl(false, false); }\n\n  FOLLY_ALWAYS_INLINE void advance() { advanceImpl(true, false); }\n\n  FOLLY_ALWAYS_INLINE void advanceLikelyDead() { advanceImpl(true, true); }\n\n  ChunkPtr chunk() const {\n    return std::pointer_traits<ChunkPtr>::pointer_to(\n        Chunk::owner(*itemPtr_, index_));\n  }\n\n  std::size_t index() const { return index_; }\n\n  Item* itemAddr() const { return std::addressof(*itemPtr_); }\n  Item& item() const { return *itemPtr_; }\n  Item const& citem() const { return *itemPtr_; }\n\n  bool atEnd() const { return itemPtr_ == nullptr; }\n\n  Packed pack() const { return Packed{itemPtr_, static_cast<uint8_t>(index_)}; }\n\n  bool operator==(F14ItemIter const& rhs) const {\n    // this form makes iter == end() into a single null check after inlining\n    // and constant propagation\n    return itemPtr_ == rhs.itemPtr_;\n  }\n\n  bool operator!=(F14ItemIter const& rhs) const { return !(*this == rhs); }\n\n private:\n  ItemPtr itemPtr_;\n  std::size_t index_;\n};\n\n////////////////\n\nstruct PackedSizeAndChunkShift {\n private:\n  // F14Table's chunk count is a power of 2 that is at least 1 and at\n  // most 1 << 63. In order to encode it efficiently, we store its base 2\n  // logarithm `n`, so that (1UL << n) is the chunkCount. Despite\n  // needing only 6 bits to encode the shift, we reserve 8 bits to\n  // avoid extra masking, leaving 56 bits for the size, which is\n  // plenty.\n\n  // We use the least significant bits of\n  // packedSizeAndChunkShift_ for the shift so that size access\n  // is just a single shift and shift access is a normal 8-bit load.\n  static constexpr uint32_t kSizeShift = 8;\n  static constexpr uint32_t kChunkCountShiftMask = (1 << kSizeShift) - 1;\n  uint64_t packedSizeAndChunkShift_{0};\n\n public:\n  // subtract 1 because we can't represent 1 << 64, triggering UBSAN.\n  static constexpr uint8_t kMaxSupportedChunkShift =\n      std::numeric_limits<uint64_t>::digits - 1;\n\n  static constexpr std::size_t kMaxSize =\n      (size_t(1) << (std::numeric_limits<std::size_t>::digits - kSizeShift)) -\n      1;\n\n  uint64_t size() const noexcept {\n    return packedSizeAndChunkShift_ >> kSizeShift;\n  }\n\n  uint8_t chunkShift() const noexcept {\n    return packedSizeAndChunkShift_ & kChunkCountShiftMask;\n  }\n\n  std::size_t chunkCount() const noexcept {\n    const auto chunkCountShift = chunkShift();\n    return std::size_t(1) << chunkCountShift;\n  }\n\n  void setSize(std::size_t sz) noexcept {\n    packedSizeAndChunkShift_ =\n        chunkShift() | (static_cast<uint64_t>(sz) << kSizeShift);\n  }\n\n  void setChunkCount(std::size_t newCount) {\n    FOLLY_SAFE_DCHECK(\n        folly::isPowTwo(newCount), newCount); // note that this forbids 0!\n    const auto shift = findFirstSet(newCount) - 1; // firstSet is 1-based.\n    packedSizeAndChunkShift_ =\n        (static_cast<uint64_t>(size()) << kSizeShift) | shift;\n    FOLLY_SAFE_DCHECK(chunkCount() == newCount, \"\");\n  }\n\n  void swap(PackedSizeAndChunkShift& rhs) noexcept {\n    std::swap(packedSizeAndChunkShift_, rhs.packedSizeAndChunkShift_);\n  }\n};\n\nstruct UnpackedSizeAndChunkShift {\n private:\n  // Simple implementation for 32-bit systems: just use two words.\n  std::size_t size_ = 0;\n  uint8_t chunkShift_ = 0;\n\n public:\n  // subtract 1 because we can't represent 1 << 32, triggering UBSAN.\n  static constexpr uint8_t kMaxSupportedChunkShift =\n      std::numeric_limits<std::size_t>::digits - 1;\n\n  static constexpr std::size_t kMaxSize =\n      std::numeric_limits<std::size_t>::max();\n\n  uint64_t size() const noexcept { return size_; }\n\n  uint8_t chunkShift() const noexcept { return chunkShift_; }\n\n  std::size_t chunkCount() const noexcept {\n    const auto chunkCountShift = chunkShift();\n    return std::size_t(1) << chunkCountShift;\n  }\n\n  void setSize(std::size_t sz) noexcept { size_ = sz; }\n\n  void setChunkCount(std::size_t newCount) {\n    FOLLY_SAFE_DCHECK(\n        folly::isPowTwo(newCount), newCount); // note that this forbids 0!\n    const auto shift = findFirstSet(newCount) - 1; // firstSet is 1-based.\n    FOLLY_SAFE_DCHECK(shift <= std::numeric_limits<uint8_t>::max(), \"\");\n    chunkShift_ = static_cast<uint8_t>(shift);\n    FOLLY_SAFE_DCHECK(chunkCount() == newCount, \"\");\n  }\n\n  void swap(UnpackedSizeAndChunkShift& rhs) noexcept {\n    std::swap(size_, rhs.size_);\n    std::swap(chunkShift_, rhs.chunkShift_);\n  }\n};\n\nusing SizeAndChunkShift = std::conditional_t<\n    sizeof(uint64_t) == sizeof(std::size_t),\n    PackedSizeAndChunkShift,\n    UnpackedSizeAndChunkShift>;\n\ntemplate <typename ItemIter, bool EnablePackedItemIter>\nstruct SizeAndChunkShiftAndPackedBegin {\n private:\n  SizeAndChunkShift sizeAndChunkShift_;\n\n  typename ItemIter::Packed packedBegin_{ItemIter{}.pack()};\n\n public:\n  auto size() const { return sizeAndChunkShift_.size(); }\n\n  auto chunkShift() const { return sizeAndChunkShift_.chunkShift(); }\n\n  auto chunkCount() const { return sizeAndChunkShift_.chunkCount(); }\n\n  void setSize(uint64_t sz) { sizeAndChunkShift_.setSize(sz); }\n\n  void incrementSize() { sizeAndChunkShift_.setSize(size() + 1); }\n\n  void decrementSize() { sizeAndChunkShift_.setSize(size() - 1); }\n\n  void setChunkCount(std::size_t count) {\n    sizeAndChunkShift_.setChunkCount(count);\n  }\n\n  typename ItemIter::Packed& packedBegin() { return packedBegin_; }\n\n  typename ItemIter::Packed const& packedBegin() const { return packedBegin_; }\n\n  void swap(SizeAndChunkShiftAndPackedBegin& rhs) noexcept {\n    sizeAndChunkShift_.swap(rhs.sizeAndChunkShift_);\n    std::swap(packedBegin_, rhs.packedBegin_);\n  }\n};\n\ntemplate <typename ItemIter>\nstruct SizeAndChunkShiftAndPackedBegin<ItemIter, false> {\n private:\n  SizeAndChunkShift sizeAndChunkShift_;\n\n public:\n  auto size() const { return sizeAndChunkShift_.size(); }\n\n  auto chunkShift() const { return sizeAndChunkShift_.chunkShift(); }\n\n  auto chunkCount() const { return sizeAndChunkShift_.chunkCount(); }\n\n  void setSize(uint64_t sz) { sizeAndChunkShift_.setSize(sz); }\n\n  void incrementSize() { sizeAndChunkShift_.setSize(size() + 1); }\n\n  void decrementSize() { sizeAndChunkShift_.setSize(size() - 1); }\n\n  void setChunkCount(std::size_t count) {\n    sizeAndChunkShift_.setChunkCount(count);\n  }\n\n  [[noreturn]] typename ItemIter::Packed& packedBegin() {\n    assume_unreachable();\n  }\n\n  [[noreturn]] typename ItemIter::Packed const& packedBegin() const {\n    assume_unreachable();\n  }\n};\n\ntemplate <typename Policy>\nclass F14Table : public Policy {\n public:\n  using Item = typename Policy::Item;\n\n  using value_type = typename Policy::Value;\n  using allocator_type = typename Policy::Alloc;\n\n private:\n  using Alloc = typename Policy::Alloc;\n  using AllocTraits = typename Policy::AllocTraits;\n  using Hasher = typename Policy::Hasher;\n  using InternalSizeType = typename Policy::InternalSizeType;\n  using KeyEqual = typename Policy::KeyEqual;\n\n  using Policy::kAllocIsAlwaysEqual;\n  using Policy::kContinuousCapacity;\n  using Policy::kDefaultConstructIsNoexcept;\n  using Policy::kEnableItemIteration;\n  using Policy::kSwapIsNoexcept;\n\n  using Policy::destroyItemOnClear;\n  using Policy::isAvalanchingHasher;\n  using Policy::prefetchBeforeCopy;\n  using Policy::prefetchBeforeDestroy;\n  using Policy::prefetchBeforeRehash;\n  using Policy::shouldAssume32BitHash;\n\n  using ByteAlloc = typename AllocTraits::template rebind_alloc<uint8_t>;\n  using BytePtr = typename std::allocator_traits<ByteAlloc>::pointer;\n\n  using Chunk = F14Chunk<Item>;\n  using ChunkPtr =\n      typename std::pointer_traits<BytePtr>::template rebind<Chunk>;\n\n  using HashPair = typename F14HashToken::HashPair;\n\n public:\n  using ItemIter = F14ItemIter<ChunkPtr>;\n\n private:\n  //////// begin fields\n\n  ChunkPtr chunks_{Chunk::getSomeEmptyInstance()};\n  SizeAndChunkShiftAndPackedBegin<ItemIter, kEnableItemIteration>\n      sizeAndChunkShiftAndPackedBegin_;\n\n  //////// end fields\n\n  auto chunkShift() const {\n    return sizeAndChunkShiftAndPackedBegin_.chunkShift();\n  }\n\n  auto chunkCount() const {\n    return sizeAndChunkShiftAndPackedBegin_.chunkCount();\n  }\n\n  std::size_t moduloByChunkCount(std::size_t index) const {\n#ifdef __BMI2__\n    // TODO: remove once the compiler selects BZHI on its own.\n    // see codegen for: codeSize_find_F14value, defined in:\n    // folly/container/test/F14SmallOverheads.cpp\n    return _bzhi_u64(index, chunkShift());\n#else\n    return index & ((std::size_t(1) << chunkShift()) - 1);\n#endif\n  }\n\n  ChunkPtr chunkAt(std::size_t i) const {\n    return std::pointer_traits<ChunkPtr>::pointer_to(\n        *Chunk::chunkRawAt(access::to_address(chunks_), i));\n  }\n\n  std::size_t chunkIndex(Chunk const* chunk) const {\n    return Chunk::chunkRawIndex(access::to_address(chunks_), chunk);\n  }\n\n  void swapContents(F14Table& rhs) noexcept {\n    using std::swap;\n    swap(chunks_, rhs.chunks_);\n    swap(\n        sizeAndChunkShiftAndPackedBegin_, rhs.sizeAndChunkShiftAndPackedBegin_);\n  }\n\n public:\n  // Equivalent to F14Table(0, ...), but implemented separately to avoid forcing\n  // a reserve() instantiation in the common case.\n  F14Table() noexcept(Policy::kDefaultConstructIsNoexcept)\n      : Policy{Hasher{}, KeyEqual{}, Alloc{}} {}\n\n  F14Table(\n      std::size_t initialCapacity,\n      Hasher const& hasher,\n      KeyEqual const& keyEqual,\n      Alloc const& alloc)\n      : Policy{hasher, keyEqual, alloc} {\n    debugModeOnReserve(initialCapacity);\n    initialReserve(initialCapacity);\n  }\n\n  F14Table(F14Table const& rhs) : Policy{rhs} { buildFromF14Table(rhs); }\n\n  F14Table(F14Table const& rhs, Alloc const& alloc) : Policy{rhs, alloc} {\n    buildFromF14Table(rhs);\n  }\n\n  F14Table(F14Table&& rhs) noexcept(\n      std::is_nothrow_move_constructible<Hasher>::value &&\n      std::is_nothrow_move_constructible<KeyEqual>::value &&\n      std::is_nothrow_move_constructible<Alloc>::value)\n      : Policy{std::move(rhs)} {\n    swapContents(rhs);\n  }\n\n  F14Table(F14Table&& rhs, Alloc const& alloc) noexcept(kAllocIsAlwaysEqual)\n      : Policy{std::move(rhs), alloc} {\n    // if-constexpr allows avoiding dependence on usable Hasher etc.\n    if constexpr (kAllocIsAlwaysEqual) {\n      // move storage (common case)\n      swapContents(rhs);\n    } else if (this->alloc() == rhs.alloc()) {\n      // move storage (common case)\n      swapContents(rhs);\n    } else {\n      // new storage because allocators unequal, move values (rare case)\n      buildFromF14Table(std::move(rhs));\n    }\n  }\n\n  F14Table& operator=(F14Table const& rhs) {\n    if (this != &rhs) {\n      reset();\n      static_cast<Policy&>(*this) = rhs;\n      buildFromF14Table(rhs);\n    }\n    return *this;\n  }\n\n  F14Table& operator=(F14Table&& rhs) noexcept(\n      std::is_nothrow_move_assignable<Hasher>::value &&\n      std::is_nothrow_move_assignable<KeyEqual>::value &&\n      (kAllocIsAlwaysEqual ||\n       (AllocTraits::propagate_on_container_move_assignment::value &&\n        std::is_nothrow_move_assignable<Alloc>::value))) {\n    if (this != &rhs) {\n      reset();\n      static_cast<Policy&>(*this) = std::move(rhs);\n      // if-constexpr allows avoiding dependence on usable Hasher etc.\n      if constexpr (\n          AllocTraits::propagate_on_container_move_assignment::value ||\n          kAllocIsAlwaysEqual) {\n        // move storage (common case)\n        swapContents(rhs);\n      } else if (this->alloc() == rhs.alloc()) {\n        // move storage (common case)\n        swapContents(rhs);\n      } else {\n        // new storage because allocators unequal, move values (rare case)\n        buildFromF14Table(std::move(rhs));\n      }\n    }\n    return *this;\n  }\n\n  ~F14Table() { reset(); }\n\n  void swap(F14Table& rhs) noexcept(kSwapIsNoexcept) {\n    // If propagate_on_container_swap is false and allocators are\n    // not equal, the only way to accomplish a swap would be to do\n    // dynamic allocation and then move (or swap) each contained value.\n    // AllocatorAwareContainer-s are not supposed to attempt this, but\n    // rather are supposed to have undefined behavior in that case.\n    FOLLY_SAFE_CHECK(\n        AllocTraits::propagate_on_container_swap::value ||\n            kAllocIsAlwaysEqual || this->alloc() == rhs.alloc(),\n        \"swap is undefined for unequal non-propagating allocators\");\n    this->swapPolicy(rhs);\n    swapContents(rhs);\n  }\n\n private:\n  static HashPair splitHash(std::size_t hash) {\n    return f14::detail::splitHashImpl<Hasher, value_type>(hash);\n  }\n  //////// memory management helpers\n\n  static std::size_t computeCapacity(\n      std::size_t chunkCount, std::size_t scale) {\n    FOLLY_SAFE_DCHECK(!(chunkCount > 1 && scale == 0), \"\");\n    FOLLY_SAFE_DCHECK(\n        scale < (std::size_t{1} << Chunk::kCapacityScaleBits), \"\");\n    FOLLY_SAFE_DCHECK((chunkCount & (chunkCount - 1)) == 0, \"\");\n    return (((chunkCount - 1) >> Chunk::kCapacityScaleShift) + 1) * scale;\n  }\n\n  std::pair<std::size_t, std::size_t> computeChunkCountAndScale(\n      std::size_t desiredCapacity,\n      bool continuousSingleChunkCapacity,\n      bool continuousMultiChunkCapacity) const {\n    if (desiredCapacity <= Chunk::kCapacity) {\n      // we can go to 100% capacity in a single chunk with no problem\n      if (!continuousSingleChunkCapacity) {\n        if (desiredCapacity <= 2) {\n          desiredCapacity = 2;\n        } else if (desiredCapacity <= 6) {\n          desiredCapacity = 6;\n        } else {\n          desiredCapacity = Chunk::kCapacity;\n        }\n      }\n      auto rv = std::make_pair(std::size_t{1}, desiredCapacity);\n      FOLLY_SAFE_DCHECK(\n          computeCapacity(rv.first, rv.second) == desiredCapacity, \"\");\n      return rv;\n    } else {\n      std::size_t minChunks =\n          (desiredCapacity - 1) / Chunk::kDesiredCapacity + 1;\n      std::size_t chunkPow = findLastSet(minChunks - 1);\n      if (chunkPow == 8 * sizeof(std::size_t)) {\n        throw_exception<std::bad_alloc>();\n      }\n\n      std::size_t chunkCount = std::size_t{1} << chunkPow;\n\n      // Let cc * scale be the actual capacity.\n      // cc = ((chunkCount - 1) >> kCapacityScaleShift) + 1.\n      // If chunkPow >= kCapacityScaleShift, then cc = chunkCount >>\n      // kCapacityScaleShift = 1 << (chunkPow - kCapacityScaleShift),\n      // otherwise it equals 1 = 1 << 0.  Let cc = 1 << ss.\n      std::size_t ss = chunkPow >= Chunk::kCapacityScaleShift\n          ? chunkPow - Chunk::kCapacityScaleShift\n          : 0;\n\n      std::size_t scale;\n      if (continuousMultiChunkCapacity) {\n        // (1 << ss) * scale >= desiredCapacity\n        scale = ((desiredCapacity - 1) >> ss) + 1;\n      } else {\n        // (1 << ss) * scale == chunkCount * kDesiredCapacity\n        scale = Chunk::kDesiredCapacity << (chunkPow - ss);\n      }\n\n      std::size_t actualCapacity = computeCapacity(chunkCount, scale);\n      FOLLY_SAFE_DCHECK(actualCapacity >= desiredCapacity, \"\");\n      if (actualCapacity > max_size()) {\n        throw_exception<std::bad_alloc>();\n      }\n\n      return std::make_pair(chunkCount, scale);\n    }\n  }\n\n  static std::size_t chunkAllocSize(\n      std::size_t chunkCount, std::size_t capacityScale) {\n    FOLLY_SAFE_DCHECK(chunkCount > 0, \"\");\n    FOLLY_SAFE_DCHECK(!(chunkCount > 1 && capacityScale == 0), \"\");\n    if (chunkCount == 1) {\n      static_assert(sizeof(Chunk) == Chunk::kItemsOffset);\n      return Chunk::kItemsOffset +\n          sizeof(Item) * computeCapacity(1, capacityScale);\n    } else {\n      return Chunk::kChunkStride * chunkCount;\n    }\n  }\n\n  ChunkPtr initializeChunks(\n      BytePtr raw, std::size_t chunkCount, std::size_t capacityScale) {\n    static_assert(std::is_trivial<Chunk>::value, \"F14Chunk should be POD\");\n    auto chunks = static_cast<Chunk*>(static_cast<void*>(&*raw));\n    for (std::size_t i = 0; i < chunkCount; ++i) {\n      Chunk::chunkRawAt(chunks, i)->clear();\n    }\n    chunks->markEof(capacityScale);\n    return std::pointer_traits<ChunkPtr>::pointer_to(*chunks);\n  }\n\n  std::size_t itemCount() const noexcept {\n    if (chunkShift() == 0) {\n      return computeCapacity(\n          1, Chunk::capacityScale(access::to_address(chunks_)));\n    } else {\n      return chunkCount() * Chunk::kCapacity;\n    }\n  }\n\n public:\n  ItemIter begin() const noexcept {\n    FOLLY_SAFE_DCHECK(kEnableItemIteration, \"\");\n    return ItemIter{sizeAndChunkShiftAndPackedBegin_.packedBegin()};\n  }\n\n  ItemIter end() const noexcept { return ItemIter{}; }\n\n  bool empty() const noexcept { return size() == 0; }\n\n  std::size_t size() const noexcept {\n    return to_narrow(sizeAndChunkShiftAndPackedBegin_.size());\n  }\n\n  std::size_t max_size() const noexcept {\n    auto& a = this->alloc();\n    return std::min<std::size_t>(\n        {SizeAndChunkShift::kMaxSize,\n         std::numeric_limits<InternalSizeType>::max(),\n         AllocTraits::max_size(a)});\n  }\n\n  std::size_t bucket_count() const noexcept {\n    return computeCapacity(\n        chunkCount(), Chunk::capacityScale(access::to_address(chunks_)));\n  }\n\n  std::size_t max_bucket_count() const noexcept { return max_size(); }\n\n  float load_factor() const noexcept {\n    return empty()\n        ? 0.0f\n        : static_cast<float>(size()) / static_cast<float>(bucket_count());\n  }\n\n  float max_load_factor() const noexcept { return 1.0f; }\n\n  void max_load_factor(float) noexcept {\n    // Probing hash tables can't run load factors >= 1 (unlike chaining\n    // tables).  In addition, we have measured that there is little or\n    // no performance advantage to running a smaller load factor (cache\n    // locality losses outweigh the small reduction in probe lengths,\n    // often making it slower).  Therefore, we've decided to just fix\n    // max_load_factor at 1.0f regardless of what the user requests.\n    // This has an additional advantage that we don't have to store it.\n    // Taking alignment into consideration this makes every F14 table\n    // 8 bytes smaller, and is part of the reason an empty F14NodeMap\n    // is almost half the size of an empty std::unordered_map (32 vs\n    // 56 bytes).\n    //\n    // I don't have a strong opinion on whether we should remove this\n    // method or leave a stub, let ngbronson or xshi know if you have a\n    // compelling argument either way.\n  }\n\n private:\n  // Our probe strategy is to advance through additional chunks with\n  // a stride that is key-specific.  This is called double hashing,\n  // and is a well known and high quality probing strategy.  So long as\n  // the stride and the chunk count are relatively prime, we will visit\n  // every chunk once and then return to the original chunk, letting us\n  // detect and end the cycle.  The chunk count is a power of two, so\n  // we can satisfy the relatively prime part by choosing an odd stride.\n  // We've already computed a high quality secondary hash value for the\n  // tag, so we just use it for the second probe hash as well.\n  //\n  // At the maximum load factor of 12/14, expected probe length for a\n  // find hit is 1.041, with 99% of keys found in the first three chunks.\n  // Expected probe length for a find miss (or insert) is 1.275, with a\n  // p99 probe length of 4 (fewer than 1% of failing find look at 5 or\n  // more chunks).\n  //\n  // This code is structured so you can try various ways of encoding\n  // the current probe state.  For example, at the moment the probe's\n  // state is the position in the cycle and the resulting chunk index is\n  // computed from that inside probeCurrentIndex.  We could also make the\n  // probe state the chunk index, and then increment it by hp.second *\n  // 2 + 1 in probeAdvance.  Wrapping can be applied early or late as\n  // well.  This particular code seems to be easier for the optimizer\n  // to understand.\n  //\n  // We could also implement probing strategies that resulted in the same\n  // tour for every key initially assigned to a chunk (linear probing or\n  // quadratic), but that results in longer probe lengths.  In particular,\n  // the cache locality wins of linear probing are not worth the increase\n  // in probe lengths (extra work and less branch predictability) in\n  // our experiments.\n\n  std::size_t probeDelta(HashPair hp) const { return 2 * hp.second + 1; }\n\n  // TRICKY!  It may seem strange to have a std::size_t needle and narrow\n  // it at the last moment, rather than making HashPair::second be a\n  // uint8_t, but the latter choice sometimes leads to a performance\n  // problem.\n  //\n  // On architectures with SSE2 but not AVX2, _mm_set1_epi8 expands\n  // to multiple instructions.  One of those is a MOVD of either 4 or\n  // 8 byte width.  Only the bottom byte of that move actually affects\n  // the result, but if a 1-byte needle has been spilled then this will\n  // be a 4 byte load.  GCC 5.5 has been observed to reload needle\n  // (or perhaps fuse a reload and part of a previous static_cast)\n  // needle using a MOVZX with a 1 byte load in parallel with the MOVD.\n  // This combination causes a failure of store-to-load forwarding,\n  // which has a big performance penalty (60 nanoseconds per find on\n  // a microbenchmark).  Keeping needle >= 4 bytes avoids the problem\n  // and also happens to result in slightly more compact assembly.\n\n  FOLLY_ALWAYS_INLINE auto loadNeedleV(std::size_t needle) const {\n#if FOLLY_NEON\n    return vdupq_n_u8(static_cast<uint8_t>(needle));\n#elif FOLLY_SSE >= 2\n    return _mm_set1_epi8(static_cast<uint8_t>(needle));\n#else\n    return needle;\n#endif\n  }\n\n  enum class Prefetch { DISABLED, ENABLED };\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE ItemIter\n  findImpl(HashPair hp, K const& key, Prefetch prefetch) const {\n    FOLLY_SAFE_DCHECK(hp.second >= 0x80 && hp.second < 0x100, \"\");\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n    svbool_t pred = svwhilelt_b8_u32(0, Chunk::kCapacity);\n#endif\n    std::size_t index = hp.first;\n    std::size_t step = probeDelta(hp);\n    auto needleV = loadNeedleV(hp.second);\n    for (std::size_t tries = chunkCount(); tries > 0;) {\n      ChunkPtr chunk = chunkAt(moduloByChunkCount(index));\n      if (prefetch == Prefetch::ENABLED && Chunk::kChunkStride > 64) {\n        prefetchAddr(chunk->itemAddr(8));\n      }\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n      svbool_t outPred;\n      auto hits = chunk->tagMatchIter(needleV, pred, outPred);\n      bool nonzero = svptest_any(pred, outPred);\n      FOLLY_SAFE_DCHECK(nonzero == hits.hasNext());\n#else\n      auto hits = chunk->tagMatchIter(needleV);\n      bool nonzero = hits.hasNext();\n#endif\n      if (nonzero) {\n        do {\n          auto i = hits.next();\n          if (FOLLY_LIKELY(this->keyMatchesItem(key, chunk->item(i)))) {\n            // Tag match and key match were both successful.  The chance\n            // of a false tag match is 1/128 for each key in the chunk\n            // (with a proper hash function).\n            return ItemIter{chunk, i};\n          }\n        } while (hits.hasNext());\n      }\n      if (FOLLY_LIKELY(chunk->outboundOverflowCount() == 0)) {\n        // No keys that wanted to be placed in this chunk were denied\n        // entry, so our search is over.  This is the common case.\n        break;\n      }\n      --tries;\n      index += step;\n    }\n    // Loop exit because tries is exhausted is rare, but possible.\n    // That means that for every chunk there is currently a key present\n    // in the map that visited that chunk on its probe search but ended\n    // up somewhere else, and we have searched every chunk.\n    return ItemIter{};\n  }\n\n  template <typename K>\n  HashPair computeHash(K const& key) const {\n    return splitHash(this->computeKeyHash(key));\n  }\n\n  template <typename HKKey, typename HKHasher, typename HKEqual>\n  HashPair computeHash(\n      F14HashedKey<HKKey, HKHasher, HKEqual> const& hashedKey) const {\n    static_assert(HeterogeneousPreHashCompatible<Hasher, HKHasher>::value);\n    static_assert(HeterogeneousPreHashCompatible<HKEqual, KeyEqual>::value);\n    return static_cast<HashPair>(hashedKey.getHashToken());\n  }\n\n public:\n  // prehash()/prefetch() split the work of find(key) into three calls, enabling\n  // you to manually implement loop pipelining for hot bulk lookups. prehash()\n  // computes the hash and prefetch() prefetches the first computed memory\n  // location, and the two-arg find(F14HashToken, K) performs the rest of the\n  // search.\n  template <typename K>\n  F14HashToken prehash(K const& key) const {\n    return F14HashToken{computeHash(key)};\n  }\n\n  template <typename K>\n  F14HashToken prehash(K const& key, std::size_t hash) const {\n    FOLLY_SAFE_DCHECK(hash == this->computeKeyHash(key));\n    return F14HashToken{splitHash(hash)};\n  }\n\n  void prefetch(F14HashToken const& token) const {\n    FOLLY_SAFE_DCHECK(chunks_ != nullptr, \"\");\n    ChunkPtr firstChunk = chunkAt(moduloByChunkCount(token.hp_.first));\n    prefetchAddr(firstChunk);\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE ItemIter find(K const& key) const {\n    const auto sz = size();\n    if (sz <= 1) {\n      if (sz == 0) {\n        return ItemIter{};\n      }\n      // There is no easy way to obtain a begin iterator with the current policy\n      // design.\n      // As a result, F14Vector* containers (kEnableItemIteration == false) do\n      // not benefit from this optimization yet.\n      if constexpr (kEnableItemIteration) {\n        ItemIter beg = begin();\n        return this->keyMatchesItem(key, beg.citem()) ? beg : ItemIter{};\n      }\n    }\n    auto hp = computeHash(key);\n    return findImpl(hp, key, Prefetch::ENABLED);\n  }\n\n  template <typename K>\n  FOLLY_ALWAYS_INLINE ItemIter\n  find(F14HashToken const& token, K const& key) const {\n    FOLLY_SAFE_DCHECK(computeHash(key) == static_cast<HashPair>(token), \"\");\n    return findImpl(static_cast<HashPair>(token), key, Prefetch::DISABLED);\n  }\n\n  // Searches for a key using a key predicate that is a refinement\n  // of key equality.  func(k) should return true only if k is equal\n  // to key according to key_eq(), but is allowed to apply additional\n  // constraints.\n  template <typename K, typename F>\n  FOLLY_ALWAYS_INLINE ItemIter findMatching(K const& key, F&& func) const {\n    auto hp = computeHash(key);\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n    svbool_t pred = svwhilelt_b8_u32(0, Chunk::kCapacity);\n#endif\n    std::size_t index = hp.first;\n    auto needleV = loadNeedleV(hp.second);\n    std::size_t step = probeDelta(hp);\n    for (std::size_t tries = chunkCount(); tries > 0; --tries) {\n      ChunkPtr chunk = chunkAt(moduloByChunkCount(index));\n      if (Chunk::kChunkStride > 64) {\n        prefetchAddr(chunk->itemAddr(8));\n      }\n#if FOLLY_ARM_FEATURE_NEON_SVE_BRIDGE\n      svbool_t outPred;\n      auto hits = chunk->tagMatchIter(needleV, pred, outPred);\n      bool nonzero = svptest_any(pred, outPred);\n      FOLLY_SAFE_DCHECK(nonzero == hits.hasNext());\n#else\n      auto hits = chunk->tagMatchIter(needleV);\n      bool nonzero = hits.hasNext();\n#endif\n      if (nonzero) {\n        do {\n          auto i = hits.next();\n          if (FOLLY_LIKELY(\n                  func(this->keyForValue(this->valueAtItem(chunk->item(i)))))) {\n            return ItemIter{chunk, i};\n          }\n        } while (hits.hasNext());\n      }\n      if (FOLLY_LIKELY(chunk->outboundOverflowCount() == 0)) {\n        break;\n      }\n      index += step;\n    }\n    return ItemIter{};\n  }\n\n private:\n  void adjustSizeAndBeginAfterInsert(ItemIter iter) {\n    if constexpr (kEnableItemIteration) {\n      // packedBegin is the max of all valid ItemIter::pack()\n      auto packed = iter.pack();\n      if (sizeAndChunkShiftAndPackedBegin_.packedBegin() < packed) {\n        sizeAndChunkShiftAndPackedBegin_.packedBegin() = packed;\n      }\n    }\n\n    sizeAndChunkShiftAndPackedBegin_.incrementSize();\n  }\n\n  // Ignores hp if pos.chunk()->hostedOverflowCount() == 0\n  void eraseBlank(ItemIter iter, HashPair hp) {\n    iter.chunk()->clearTag(iter.index());\n\n    if (iter.chunk()->hostedOverflowCount() != 0) {\n      // clean up\n      std::size_t index = hp.first;\n      std::size_t delta = probeDelta(hp);\n      uint8_t hostedOp = 0;\n      while (true) {\n        ChunkPtr chunk = chunkAt(moduloByChunkCount(index));\n        if (chunk == iter.chunk()) {\n          chunk->adjustHostedOverflowCount(hostedOp);\n          break;\n        }\n        chunk->decrOutboundOverflowCount();\n        hostedOp = Chunk::kDecrHostedOverflowCount;\n        index += delta;\n      }\n    }\n  }\n\n  [[FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE static void eraseBlankCold(\n      F14Table* self, ItemIter iter, HashPair hp) {\n    self->eraseBlank(iter, hp);\n    rethrow_current_exception();\n  }\n\n  void adjustSizeAndBeginBeforeErase(ItemIter iter) {\n    sizeAndChunkShiftAndPackedBegin_.decrementSize();\n    if constexpr (kEnableItemIteration) {\n      if (iter.pack() == sizeAndChunkShiftAndPackedBegin_.packedBegin()) {\n        if (size() == 0) {\n          iter = ItemIter{};\n        } else {\n          iter.precheckedAdvance();\n        }\n        sizeAndChunkShiftAndPackedBegin_.packedBegin() = iter.pack();\n      }\n    }\n  }\n\n  template <typename... Args>\n  void insertAtBlank(ItemIter pos, HashPair hp, Args&&... args) {\n    auto dst = pos.itemAddr();\n    catch_exception(\n        [&] {\n          this->constructValueAtItem(*this, dst, std::forward<Args>(args)...);\n        },\n        &eraseBlankCold,\n        this,\n        pos,\n        hp);\n    adjustSizeAndBeginAfterInsert(pos);\n  }\n\n  ItemIter allocateTag(uint8_t* fullness, HashPair hp) {\n    ChunkPtr chunk;\n    std::size_t index = hp.first;\n    std::size_t delta = probeDelta(hp);\n    uint8_t hostedOp = 0;\n    while (true) {\n      index = moduloByChunkCount(index);\n      chunk = chunkAt(index);\n      if (FOLLY_LIKELY(fullness[index] < Chunk::kCapacity)) {\n        break;\n      }\n      chunk->incrOutboundOverflowCount();\n      hostedOp = Chunk::kIncrHostedOverflowCount;\n      index += delta;\n    }\n    unsigned itemIndex = fullness[index]++;\n    FOLLY_SAFE_DCHECK(!chunk->occupied(itemIndex), \"\");\n    chunk->setTag(itemIndex, hp.second);\n    chunk->adjustHostedOverflowCount(hostedOp);\n    return ItemIter{chunk, itemIndex};\n  }\n\n  ChunkPtr lastOccupiedChunk() const {\n    FOLLY_SAFE_DCHECK(size() > 0, \"\");\n    if constexpr (kEnableItemIteration) {\n      return begin().chunk();\n    } else {\n      return chunkAt(chunkCount() - 1);\n    }\n  }\n\n  template <typename T>\n  void directBuildFrom(T&& src) {\n    FOLLY_SAFE_DCHECK(src.size() > 0 && chunkShift() == src.chunkShift(), \"\");\n\n    // We use std::forward<T> to allow portions of src to be moved out by\n    // either beforeBuild or afterBuild, but we are just relying on good\n    // behavior of our Policy superclass to ensure that any particular\n    // field of this is a donor at most once.\n\n    auto undoState =\n        this->beforeBuild(src.size(), bucket_count(), std::forward<T>(src));\n    bool success = false;\n    SCOPE_EXIT {\n      this->afterBuild(\n          undoState, success, src.size(), bucket_count(), std::forward<T>(src));\n    };\n\n    // Copy can fail part-way through if a Value copy constructor throws.\n    // Failing afterBuild is limited in its cleanup power in this case,\n    // because it can't enumerate the items that were actually copied.\n    // Fortunately we can divide the situation into cases where all of\n    // the state is owned by the table itself (F14Node and F14Value),\n    // for which clearImpl() can do partial cleanup, and cases where all\n    // of the values are owned by the policy (F14Vector), in which case\n    // partial failure should not occur.  Sorry for the subtle invariants\n    // in the Policy API.\n\n    if (std::is_trivially_copyable<Item>::value &&\n        !this->destroyItemOnClear() && itemCount() == src.itemCount()) {\n      FOLLY_SAFE_DCHECK(chunkShift() == src.chunkShift(), \"\");\n\n      auto scale = Chunk::capacityScale(access::to_address(chunks_));\n\n      // most happy path\n      auto n = chunkAllocSize(chunkCount(), scale);\n      std::memcpy(\n          access::to_address(chunks_), access::to_address(src.chunks_), n);\n      sizeAndChunkShiftAndPackedBegin_.setSize(src.size());\n      if constexpr (kEnableItemIteration) {\n        auto srcBegin = src.begin();\n        auto srcChunkIdx = Chunk::chunkRawIndex(\n            access::to_address(src.chunks_), &*srcBegin.chunk());\n        sizeAndChunkShiftAndPackedBegin_.packedBegin() =\n            ItemIter{chunkAt(srcChunkIdx), srcBegin.index()}.pack();\n      }\n      if constexpr (kContinuousCapacity) {\n        // capacityScale might not match even if itemCount matches\n        chunks_->setCapacityScale(scale);\n      }\n    } else {\n      // Happy path, no rehash but pack items toward bottom of chunk\n      // and use copy constructor.  Don't try to optimize by using\n      // lastOccupiedChunk() because there may be higher unoccupied chunks\n      // with the overflow bit set.\n      auto* srcChunk =\n          Chunk::chunkRawAt(access::to_address(src.chunks_), chunkCount() - 1);\n      Chunk* dstChunk =\n          Chunk::chunkRawAt(access::to_address(chunks_), chunkCount() - 1);\n      do {\n        dstChunk->copyOverflowInfoFrom(*srcChunk);\n\n        auto iter = srcChunk->occupiedIter();\n        if (prefetchBeforeCopy()) {\n          for (auto piter = iter; piter.hasNext();) {\n            this->prefetchValue(srcChunk->citem(piter.next()));\n          }\n        }\n\n        std::size_t dstI = 0;\n        for (; iter.hasNext(); ++dstI) {\n          auto srcI = iter.next();\n          auto&& srcArg =\n              std::forward<T>(src).buildArgForItem(srcChunk->item(srcI));\n          auto dst = dstChunk->itemAddr(dstI);\n          this->constructValueAtItem(\n              0, dst, std::forward<decltype(srcArg)>(srcArg));\n          dstChunk->setTag(dstI, srcChunk->tag(srcI));\n          sizeAndChunkShiftAndPackedBegin_.incrementSize();\n        }\n\n        srcChunk = Chunk::prevChunkRaw(srcChunk);\n        dstChunk = Chunk::prevChunkRaw(dstChunk);\n      } while (size() != src.size());\n\n      // reset doesn't care about packedBegin, so we don't fix it until the end\n      if constexpr (kEnableItemIteration) {\n        std::size_t maxChunkIndex = Chunk::chunkRawIndex(\n            access::to_address(src.chunks_), &*src.lastOccupiedChunk());\n        sizeAndChunkShiftAndPackedBegin_.packedBegin() =\n            ItemIter{\n                chunkAt(maxChunkIndex),\n                chunkAt(maxChunkIndex)->lastOccupied().index()}\n                .pack();\n      }\n    }\n\n    success = true;\n  }\n\n  template <typename T>\n  void rehashBuildFrom(T&& src) {\n    FOLLY_SAFE_DCHECK(src.chunkCount() > chunkCount(), \"\");\n\n    // 1 byte per chunk means < 1 bit per value temporary overhead\n    std::array<uint8_t, 256> stackBuf;\n    uint8_t* fullness;\n    auto cc = chunkCount();\n    if (cc <= stackBuf.size()) {\n      fullness = stackBuf.data();\n    } else {\n      ByteAlloc a{this->alloc()};\n      fullness = &*std::allocator_traits<ByteAlloc>::allocate(a, cc);\n    }\n    SCOPE_EXIT {\n      if (cc > stackBuf.size()) {\n        ByteAlloc a{this->alloc()};\n        std::allocator_traits<ByteAlloc>::deallocate(\n            a,\n            std::pointer_traits<typename std::allocator_traits<\n                ByteAlloc>::pointer>::pointer_to(*fullness),\n            cc);\n      }\n    };\n    std::memset(fullness, '\\0', cc);\n\n    // We use std::forward<T> to allow portions of src to be moved out by\n    // either beforeBuild or afterBuild, but we are just relying on good\n    // behavior of our Policy superclass to ensure that any particular\n    // field of this is a donor at most once.\n\n    // Exception safety requires beforeBuild to happen after all of the\n    // allocate() calls.\n    auto undoState =\n        this->beforeBuild(src.size(), bucket_count(), std::forward<T>(src));\n    bool success = false;\n    SCOPE_EXIT {\n      this->afterBuild(\n          undoState, success, src.size(), bucket_count(), std::forward<T>(src));\n    };\n\n    // The current table is at a valid state at all points for policies\n    // in which non-trivial values are owned by the main table (F14Node\n    // and F14Value), so reset() will clean things up properly if we\n    // fail partway through.  For the case that the policy manages value\n    // lifecycle (F14Vector) then nothing after beforeBuild can throw and\n    // we don't have to worry about partial failure.\n\n    std::size_t srcChunkIndex = Chunk::chunkRawIndex(\n        access::to_address(src.chunks_), &*src.lastOccupiedChunk());\n    while (true) {\n      auto* srcChunk =\n          Chunk::chunkRawAt(access::to_address(src.chunks_), srcChunkIndex);\n      auto iter = srcChunk->occupiedIter();\n      if (prefetchBeforeRehash()) {\n        for (auto piter = iter; piter.hasNext();) {\n          this->prefetchValue(srcChunk->item(piter.next()));\n        }\n      }\n      if (srcChunk->hostedOverflowCount() == 0) {\n        // all items are in their preferred chunk (no probing), so we\n        // don't need to compute any hash values\n        while (iter.hasNext()) {\n          auto i = iter.next();\n          auto& srcItem = srcChunk->item(i);\n          auto&& srcArg = std::forward<T>(src).buildArgForItem(srcItem);\n          HashPair hp{srcChunkIndex, srcChunk->tag(i)};\n          insertAtBlank(\n              allocateTag(fullness, hp),\n              hp,\n              std::forward<decltype(srcArg)>(srcArg));\n        }\n      } else {\n        // any chunk's items might be in here\n        while (iter.hasNext()) {\n          auto i = iter.next();\n          auto& srcItem = srcChunk->item(i);\n          auto&& srcArg = std::forward<T>(src).buildArgForItem(srcItem);\n          auto const& srcKey = src.keyForValue(srcArg);\n          auto hp = computeHash(srcKey);\n          FOLLY_SAFE_CHECK(hp.second == srcChunk->tag(i), \"\");\n          insertAtBlank(\n              allocateTag(fullness, hp),\n              hp,\n              std::forward<decltype(srcArg)>(srcArg));\n        }\n      }\n      if (srcChunkIndex == 0) {\n        break;\n      }\n      --srcChunkIndex;\n    }\n\n    success = true;\n  }\n\n  [[FOLLY_ATTR_GNU_COLD]] FOLLY_NOINLINE static void buildFromF14TableCatchCold(\n      F14Table* self) {\n    self->reset();\n    F14LinkCheck<getF14IntrinsicsMode()>::check();\n    rethrow_current_exception();\n  }\n\n  template <typename T>\n  FOLLY_NOINLINE void buildFromF14Table(T&& src) {\n    FOLLY_SAFE_DCHECK(bucket_count() == 0, \"\");\n    if (src.size() == 0) {\n      return;\n    }\n\n    // Use the source's capacity, unless it is oversized.\n    auto upperLimit = computeChunkCountAndScale(src.size(), false, false);\n    auto ccas = std::make_pair(\n        src.chunkCount(),\n        Chunk::capacityScale(access::to_address(src.chunks_)));\n    FOLLY_SAFE_DCHECK(\n        ccas.first >= upperLimit.first,\n        \"rounded chunk count can't be bigger than actual\");\n    if (ccas.first > upperLimit.first || ccas.second > upperLimit.second) {\n      ccas = upperLimit;\n    }\n    rehashImpl(0, 1, 0, ccas.first, ccas.second);\n\n    catch_exception(\n        [&] {\n          if (chunkShift() == src.chunkShift()) {\n            directBuildFrom(std::forward<T>(src));\n          } else {\n            rehashBuildFrom(std::forward<T>(src));\n          }\n        },\n        &F14Table::buildFromF14TableCatchCold,\n        this);\n  }\n\n  void maybeRehash(std::size_t desiredCapacity, bool attemptExact) {\n    auto origChunkCount = chunkCount();\n    auto origCapacityScale = Chunk::capacityScale(access::to_address(chunks_));\n    auto origCapacity = computeCapacity(origChunkCount, origCapacityScale);\n\n    std::size_t newChunkCount;\n    std::size_t newCapacityScale;\n    std::tie(newChunkCount, newCapacityScale) = computeChunkCountAndScale(\n        desiredCapacity, attemptExact, kContinuousCapacity && attemptExact);\n    auto newCapacity = computeCapacity(newChunkCount, newCapacityScale);\n\n    if (origCapacity != newCapacity) {\n      rehashImpl(\n          size(),\n          origChunkCount,\n          origCapacityScale,\n          newChunkCount,\n          newCapacityScale);\n    }\n  }\n\n  void reserveImpl(std::size_t requestedCapacity) {\n    const size_t targetCapacity =\n        std::max<std::size_t>(requestedCapacity, size());\n    if (targetCapacity == 0) {\n      reset();\n      return;\n    }\n\n    // Special case reserve(n) for n <= size() (pseudo \"shrink_to_fit\")\n    if (requestedCapacity <= size()) {\n      maybeRehash(targetCapacity, /*attemptExact*/ true);\n      return;\n    }\n\n    auto origCapacity = bucket_count();\n\n    // Never shrink in order to avoid O(n^2) behavior of repeated reserves\n    if (targetCapacity <= origCapacity) {\n      return;\n    }\n\n    // Large increase? Good chance the capacity is exactly right\n    bool attemptExact =\n        targetCapacity > origCapacity + ((origCapacity + 7) / 8);\n    maybeRehash(targetCapacity, attemptExact);\n  }\n\n  FOLLY_NOINLINE void reserveForInsertImpl(\n      std::size_t capacityMinusOne,\n      std::size_t origChunkCount,\n      std::size_t origCapacityScale,\n      std::size_t origCapacity) {\n    FOLLY_SAFE_DCHECK(capacityMinusOne >= size(), \"\");\n    std::size_t capacity = capacityMinusOne + 1;\n\n    // we want to grow by between 2^0.5 and 2^1.5 ending at a \"good\"\n    // size, so we grow by 2^0.5 and then round up\n\n    // 1.01101_2 = 1.40625\n    std::size_t minGrowth = origCapacity + (origCapacity >> 2) +\n        (origCapacity >> 3) + (origCapacity >> 5);\n    capacity = std::max<std::size_t>(capacity, minGrowth);\n\n    std::size_t newChunkCount;\n    std::size_t newCapacityScale;\n    std::tie(newChunkCount, newCapacityScale) =\n        computeChunkCountAndScale(capacity, false, false);\n\n    FOLLY_SAFE_DCHECK(\n        computeCapacity(newChunkCount, newCapacityScale) > origCapacity, \"\");\n\n    rehashImpl(\n        size(),\n        origChunkCount,\n        origCapacityScale,\n        newChunkCount,\n        newCapacityScale);\n  }\n\n  void initialReserve(std::size_t desiredCapacity) {\n    FOLLY_SAFE_DCHECK(size() == 0, \"\");\n    FOLLY_SAFE_DCHECK(chunkShift() == 0, \"\");\n    FOLLY_SAFE_DCHECK(!!chunks_);\n    FOLLY_SAFE_DCHECK(Chunk::isEmptyInstance(access::to_address(chunks_)), \"\");\n    if (desiredCapacity == 0) {\n      return;\n    }\n\n    std::size_t newChunkCount;\n    std::size_t newCapacityScale;\n    std::tie(newChunkCount, newCapacityScale) = computeChunkCountAndScale(\n        desiredCapacity,\n        /*continuousSingleChunkCapacity=*/true,\n        kContinuousCapacity);\n    auto newCapacity = computeCapacity(newChunkCount, newCapacityScale);\n    auto newAllocSize = chunkAllocSize(newChunkCount, newCapacityScale);\n\n    BytePtr rawAllocation;\n    auto undoState =\n        this->beforeRehash(0, 0, newCapacity, newAllocSize, rawAllocation);\n\n    chunks_ = initializeChunks(rawAllocation, newChunkCount, newCapacityScale);\n\n    sizeAndChunkShiftAndPackedBegin_.setChunkCount(newChunkCount);\n\n    this->afterRehash(\n        std::move(undoState), true, 0, 0, newCapacity, nullptr, 0);\n  }\n\n  void rehashImpl(\n      std::size_t origSize,\n      std::size_t origChunkCount,\n      std::size_t origCapacityScale,\n      std::size_t newChunkCount,\n      std::size_t newCapacityScale) {\n    auto origChunks = chunks_;\n    auto origCapacity = computeCapacity(origChunkCount, origCapacityScale);\n    auto origAllocSize = chunkAllocSize(origChunkCount, origCapacityScale);\n    auto newCapacity = computeCapacity(newChunkCount, newCapacityScale);\n    auto newAllocSize = chunkAllocSize(newChunkCount, newCapacityScale);\n\n    BytePtr rawAllocation;\n    auto undoState = this->beforeRehash(\n        origSize, origCapacity, newCapacity, newAllocSize, rawAllocation);\n    chunks_ = initializeChunks(rawAllocation, newChunkCount, newCapacityScale);\n\n    sizeAndChunkShiftAndPackedBegin_.setChunkCount(newChunkCount);\n\n    bool success = false;\n    SCOPE_EXIT {\n      // this SCOPE_EXIT reverts chunks_ and chunkShift if necessary\n      BytePtr finishedRawAllocation = nullptr;\n      std::size_t finishedAllocSize = 0;\n      if (FOLLY_LIKELY(success)) {\n        if (origCapacity > 0) {\n          finishedRawAllocation = std::pointer_traits<BytePtr>::pointer_to(\n              *static_cast<uint8_t*>(static_cast<void*>(&*origChunks)));\n          finishedAllocSize = origAllocSize;\n        }\n      } else {\n        finishedRawAllocation = rawAllocation;\n        finishedAllocSize = newAllocSize;\n        chunks_ = origChunks;\n        sizeAndChunkShiftAndPackedBegin_.setChunkCount(origChunkCount);\n        F14LinkCheck<getF14IntrinsicsMode()>::check();\n      }\n\n      this->afterRehash(\n          std::move(undoState),\n          success,\n          origSize,\n          origCapacity,\n          newCapacity,\n          finishedRawAllocation,\n          finishedAllocSize);\n    };\n\n    if (origSize == 0) {\n      // nothing to do\n    } else if (origChunkCount == 1 && newChunkCount == 1) {\n      // no mask, no chunk scan, no hash computation, no probing\n      auto srcChunk = origChunks;\n      auto dstChunk = chunks_;\n      std::size_t srcI = 0;\n      std::size_t dstI = 0;\n      while (dstI < origSize) {\n        if (FOLLY_LIKELY(srcChunk->occupied(srcI))) {\n          dstChunk->setTag(dstI, srcChunk->tag(srcI));\n          this->moveItemDuringRehash(\n              dstChunk->itemAddr(dstI), srcChunk->item(srcI));\n          ++dstI;\n        }\n        ++srcI;\n      }\n      if constexpr (kEnableItemIteration) {\n        sizeAndChunkShiftAndPackedBegin_.packedBegin() =\n            ItemIter{dstChunk, dstI - 1}.pack();\n      }\n    } else {\n      // 1 byte per chunk means < 1 bit per value temporary overhead\n      std::array<uint8_t, 256> stackBuf;\n      uint8_t* fullness;\n      if (newChunkCount <= stackBuf.size()) {\n        fullness = stackBuf.data();\n      } else {\n        ByteAlloc a{this->alloc()};\n        // may throw\n        fullness =\n            &*std::allocator_traits<ByteAlloc>::allocate(a, newChunkCount);\n      }\n      std::memset(fullness, '\\0', newChunkCount);\n      SCOPE_EXIT {\n        if (newChunkCount > stackBuf.size()) {\n          ByteAlloc a{this->alloc()};\n          std::allocator_traits<ByteAlloc>::deallocate(\n              a,\n              std::pointer_traits<typename std::allocator_traits<\n                  ByteAlloc>::pointer>::pointer_to(*fullness),\n              newChunkCount);\n        }\n      };\n\n      auto* srcChunk =\n          Chunk::chunkRawAt(access::to_address(origChunks), origChunkCount - 1);\n      std::size_t remaining = origSize;\n      while (remaining > 0) {\n        auto iter = srcChunk->occupiedIter();\n        if (prefetchBeforeRehash()) {\n          for (auto piter = iter; piter.hasNext();) {\n            this->prefetchValue(srcChunk->item(piter.next()));\n          }\n        }\n        while (iter.hasNext()) {\n          --remaining;\n          auto srcI = iter.next();\n          Item& srcItem = srcChunk->item(srcI);\n          auto hp = splitHash(\n              this->computeItemHash(const_cast<Item const&>(srcItem)));\n          FOLLY_SAFE_CHECK(hp.second == srcChunk->tag(srcI), \"\");\n\n          auto dstIter = allocateTag(fullness, hp);\n          this->moveItemDuringRehash(dstIter.itemAddr(), srcItem);\n        }\n        srcChunk = Chunk::prevChunkRaw(srcChunk);\n      }\n\n      if constexpr (kEnableItemIteration) {\n        // this code replaces size invocations of adjustSizeAndBeginAfterInsert\n        std::size_t i = chunkCount() - 1;\n        while (fullness[i] == 0) {\n          --i;\n        }\n        sizeAndChunkShiftAndPackedBegin_.packedBegin() =\n            ItemIter{chunkAt(i), std::size_t{fullness[i]} - 1}.pack();\n      }\n    }\n\n    success = true;\n  }\n\n  // Randomization to help expose bugs when running tests in debug or\n  // sanitizer builds\n\n  FOLLY_ALWAYS_INLINE void debugModeOnReserve(std::size_t capacity) {\n    if constexpr (kIsLibrarySanitizeAddress || kIsDebug) {\n      if (capacity > size()) {\n        tlsPendingSafeInserts(static_cast<std::ptrdiff_t>(capacity - size()));\n      }\n    }\n  }\n\n  void debugModeSpuriousRehash() {\n    auto cc = chunkCount();\n    auto ss = Chunk::capacityScale(access::to_address(chunks_));\n    rehashImpl(size(), cc, ss, cc, ss);\n  }\n\n  FOLLY_ALWAYS_INLINE void debugModeBeforeInsert() {\n    // When running under ASAN, we add a spurious rehash with 1/size()\n    // probability before every insert.  This means that finding reference\n    // stability problems for F14Value and F14Vector is much more likely.\n    // The most common pattern that causes this is\n    //\n    //   auto& ref = map[k1]; map[k2] = foo(ref);\n    //\n    // One way to fix this is to call map.reserve(N) before such a\n    // sequence, where N is the number of keys that might be inserted\n    // within the section that retains references plus the existing size.\n    if constexpr (kIsLibrarySanitizeAddress) {\n      if (!tlsPendingSafeInserts() && size() > 0 &&\n          tlsMinstdRand(size()) == 0) {\n        debugModeSpuriousRehash();\n      }\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE void debugModeAfterInsert() {\n    if constexpr (kIsLibrarySanitizeAddress || kIsDebug) {\n      tlsPendingSafeInserts(-1);\n    }\n  }\n\n  FOLLY_ALWAYS_INLINE void debugModePerturbSlotInsertOrder(\n      ChunkPtr chunk, std::size_t& itemIndex) {\n    FOLLY_SAFE_DCHECK(!chunk->occupied(itemIndex), \"\");\n    constexpr bool perturbSlot = FOLLY_F14_PERTURB_INSERTION_ORDER;\n    if (perturbSlot && !tlsPendingSafeInserts()) {\n      std::size_t e = chunkShift() == 0 ? bucket_count() : Chunk::kCapacity;\n      std::size_t i = itemIndex + tlsMinstdRand(e - itemIndex);\n      if (!chunk->occupied(i)) {\n        itemIndex = i;\n      }\n    }\n  }\n\n public:\n  // user has no control over max_load_factor\n\n  void rehash(std::size_t capacity) { reserve(capacity); }\n\n  void reserve(std::size_t capacity) {\n    // We want to support the pattern\n    //   map.reserve(map.size() + 2); auto& r1 = map[k1]; auto& r2 = map[k2];\n    debugModeOnReserve(capacity);\n    FOLLY_SAFE_DCHECK(!!chunks_);\n    if (Chunk::isEmptyInstance(access::to_address(chunks_))) {\n      initialReserve(capacity);\n    } else {\n      reserveImpl(capacity);\n    }\n  }\n\n  void reserveForInsert(size_t incoming = 1) {\n    FOLLY_SAFE_DCHECK(incoming > 0, \"\");\n\n    auto needed = size() + incoming;\n    auto chunkCount_ = chunkCount();\n    auto scale = Chunk::capacityScale(access::to_address(chunks_));\n    auto existing = computeCapacity(chunkCount_, scale);\n    if (needed - 1 >= existing) {\n      reserveForInsertImpl(needed - 1, chunkCount_, scale, existing);\n    }\n  }\n\n  // Returns pos,true if construct, pos,false if found.  key is only used\n  // during the search; all constructor args for an inserted value come\n  // from args...  key won't be accessed after args are touched.\n  template <typename K, typename... Args>\n  std::pair<ItemIter, bool> tryEmplaceValue(K const& key, Args&&... args) {\n    const auto hp = computeHash(key);\n    return tryEmplaceValueImpl(hp, key, std::forward<Args>(args)...);\n  }\n\n  template <typename K, typename... Args>\n  std::pair<ItemIter, bool> tryEmplaceValueWithToken(\n      F14HashToken const& token, K const& key, Args&&... args) {\n    FOLLY_SAFE_DCHECK(computeHash(key) == static_cast<HashPair>(token), \"\");\n    return tryEmplaceValueImpl(\n        static_cast<HashPair>(token), key, std::forward<Args>(args)...);\n  }\n\n  template <typename K, typename... Args>\n  std::pair<ItemIter, bool> tryEmplaceValueImpl(\n      HashPair hp, K const& key, Args&&... args) {\n    if (size() > 0) {\n      auto existing = findImpl(hp, key, Prefetch::ENABLED);\n      if (!existing.atEnd()) {\n        return std::make_pair(existing, false);\n      }\n    }\n\n    debugModeBeforeInsert();\n\n    reserveForInsert();\n\n    std::size_t index = hp.first;\n    ChunkPtr chunk = chunkAt(moduloByChunkCount(index));\n    auto firstEmpty = chunk->firstEmpty();\n\n    if (!firstEmpty.hasIndex()) {\n      std::size_t delta = probeDelta(hp);\n      do {\n        chunk->incrOutboundOverflowCount();\n        index += delta;\n        chunk = chunkAt(moduloByChunkCount(index));\n        firstEmpty = chunk->firstEmpty();\n      } while (!firstEmpty.hasIndex());\n      chunk->adjustHostedOverflowCount(Chunk::kIncrHostedOverflowCount);\n    }\n    std::size_t itemIndex = firstEmpty.index();\n\n    debugModePerturbSlotInsertOrder(chunk, itemIndex);\n\n    chunk->setTag(itemIndex, hp.second);\n    ItemIter iter{chunk, itemIndex};\n\n    // insertAtBlank will clear the tag if the constructor throws\n    insertAtBlank(iter, hp, std::forward<Args>(args)...);\n\n    debugModeAfterInsert();\n\n    return std::make_pair(iter, true);\n  }\n\n private:\n  template <bool Reset>\n  void clearImpl() noexcept {\n    FOLLY_SAFE_DCHECK(!!chunks_);\n    if (Chunk::isEmptyInstance(access::to_address(chunks_))) {\n      FOLLY_SAFE_DCHECK(empty() && bucket_count() == 0, \"\");\n      return;\n    }\n\n    // turn clear into reset if the table is >= 16 chunks so that\n    // we don't get too low a load factor\n    bool willReset = Reset || chunkCount() >= 16;\n\n    auto origSize = size();\n    auto origCapacity = bucket_count();\n    if (willReset) {\n      this->beforeReset(origSize, origCapacity);\n    } else {\n      this->beforeClear(origSize, origCapacity);\n    }\n\n    if (!empty()) {\n      if (destroyItemOnClear()) {\n        for (std::size_t ci = 0; ci < chunkCount(); ++ci) {\n          ChunkPtr chunk = chunkAt(ci);\n          auto iter = chunk->occupiedIter();\n          if (prefetchBeforeDestroy()) {\n            for (auto piter = iter; piter.hasNext();) {\n              this->prefetchValue(chunk->item(piter.next()));\n            }\n          }\n          while (iter.hasNext()) {\n            this->destroyItem(chunk->item(iter.next()));\n          }\n        }\n      }\n      if (!willReset) {\n        // It's okay to do this in a separate loop because we only do it\n        // when the chunk count is small.  That avoids a branch when we\n        // are promoting a clear to a reset for a large table.\n        auto scale = Chunk::capacityScale(access::to_address(chunks_));\n        for (std::size_t ci = 0; ci < chunkCount(); ++ci) {\n          chunkAt(ci)->clear();\n        }\n        chunkAt(0)->markEof(scale);\n      }\n      if constexpr (kEnableItemIteration) {\n        sizeAndChunkShiftAndPackedBegin_.packedBegin() = ItemIter{}.pack();\n      }\n      sizeAndChunkShiftAndPackedBegin_.setSize(0);\n    }\n\n    if (willReset) {\n      BytePtr rawAllocation = std::pointer_traits<BytePtr>::pointer_to(\n          *static_cast<uint8_t*>(static_cast<void*>(&*chunks_)));\n      std::size_t rawSize = chunkAllocSize(\n          chunkCount(), Chunk::capacityScale(access::to_address(chunks_)));\n\n      chunks_ = Chunk::getSomeEmptyInstance();\n      sizeAndChunkShiftAndPackedBegin_.setChunkCount(1);\n\n      this->afterReset(origSize, origCapacity, rawAllocation, rawSize);\n    } else {\n      this->afterClear(origSize, origCapacity);\n    }\n  }\n\n  void eraseImpl(ItemIter pos, HashPair hp) {\n    this->destroyItem(pos.item());\n    adjustSizeAndBeginBeforeErase(pos);\n    eraseBlank(pos, hp);\n  }\n\n public:\n  // The item needs to still be hashable during this call.  If you want\n  // to intercept the value before it is destroyed (to extract it, for\n  // example), do so in the beforeDestroy callback.\n  template <typename BeforeDestroy>\n  void eraseIterInto(ItemIter pos, BeforeDestroy&& beforeDestroy) {\n    HashPair hp{};\n    if (pos.chunk()->hostedOverflowCount() != 0) {\n      hp = splitHash(this->computeItemHash(pos.citem()));\n    }\n    beforeDestroy(this->valueAtItemForExtract(pos.item()));\n    eraseImpl(pos, hp);\n  }\n\n  template <typename K, typename BeforeDestroy>\n  std::size_t eraseKeyInto(K const& key, BeforeDestroy&& beforeDestroy) {\n    if (FOLLY_UNLIKELY(size() == 0)) {\n      return 0;\n    }\n    auto hp = computeHash(key);\n    auto iter = findImpl(hp, key, Prefetch::ENABLED);\n    if (!iter.atEnd()) {\n      beforeDestroy(this->valueAtItemForExtract(iter.item()));\n      eraseImpl(iter, hp);\n      return 1;\n    } else {\n      return 0;\n    }\n  }\n\n  void clear() noexcept {\n    if constexpr (kIsLibrarySanitizeAddress) {\n      // force recycling of heap memory\n      auto bc = bucket_count();\n      reset();\n      catch_exception<std::bad_alloc const&>(\n          [this, bc]() { reserveImpl(bc); },\n          &folly::detail::thunk::noop<std::bad_alloc const&>);\n    } else {\n      clearImpl<false>();\n    }\n  }\n\n  // Like clear(), but always frees all dynamic storage allocated\n  // by the table.\n  void reset() noexcept { clearImpl<true>(); }\n\n  // Get memory footprint, not including sizeof(*this).\n  std::size_t getAllocatedMemorySize() const {\n    std::size_t sum = 0;\n    visitAllocationClasses([&sum](std::size_t bytes, std::size_t n) {\n      sum += bytes * n;\n    });\n    return sum;\n  }\n\n  // Enumerates classes of allocated memory blocks currently owned\n  // by this table, calling visitor(allocationSize, allocationCount).\n  // This can be used to get a more accurate indication of memory footprint\n  // than getAllocatedMemorySize() if you have some way of computing the\n  // internal fragmentation of the allocator, such as JEMalloc's nallocx.\n  // The visitor might be called twice with the same allocationSize. The\n  // visitor's computation should produce the same result for visitor(8,\n  // 2) as for two calls to visitor(8, 1), for example.  The visitor may\n  // be called with a zero allocationCount.\n  template <typename V>\n  void visitAllocationClasses(V&& visitor) const {\n    auto scale = Chunk::capacityScale(access::to_address(chunks_));\n    this->visitPolicyAllocationClasses(\n        scale == 0 ? 0 : chunkAllocSize(chunkCount(), scale),\n        size(),\n        bucket_count(),\n        visitor);\n  }\n\n  // visitor should take an Item const&\n  template <typename V>\n  void visitItems(V&& visitor) const {\n    if (empty()) {\n      return;\n    }\n    std::size_t maxChunkIndex = chunkIndex(&*lastOccupiedChunk());\n    auto* chunk = access::to_address(chunks_);\n    for (std::size_t i = 0; i <= maxChunkIndex;\n         ++i, chunk = Chunk::nextChunkRaw(chunk)) {\n      auto iter = chunk->occupiedIter();\n      if (prefetchBeforeCopy()) {\n        for (auto piter = iter; piter.hasNext();) {\n          this->prefetchValue(chunk->citem(piter.next()));\n        }\n      }\n      while (iter.hasNext()) {\n        visitor(chunk->citem(iter.next()));\n      }\n    }\n  }\n\n  // visitor should take two Item const*\n  template <typename V>\n  void visitContiguousItemRanges(V&& visitor) const {\n    if (empty()) {\n      return;\n    }\n    std::size_t maxChunkIndex = chunkIndex(&*lastOccupiedChunk());\n    auto* chunk = access::to_address(chunks_);\n    for (std::size_t i = 0; i <= maxChunkIndex;\n         ++i, chunk = Chunk::nextChunkRaw(chunk)) {\n      for (auto iter = chunk->occupiedRangeIter(); iter.hasNext();) {\n        auto be = iter.next();\n        FOLLY_SAFE_DCHECK(\n            chunk->occupied(be.first) && chunk->occupied(be.second - 1), \"\");\n        Item const* b = chunk->itemAddr(be.first);\n        visitor(b, b + (be.second - be.first));\n      }\n    }\n  }\n\n private:\n  static std::size_t& histoAt(\n      std::vector<std::size_t>& histo, std::size_t index) {\n    if (histo.size() <= index) {\n      histo.resize(index + 1);\n    }\n    return histo.at(index);\n  }\n\n public:\n  // Expensive\n  F14TableStats computeStats() const {\n    F14TableStats stats;\n\n    if constexpr (kIsDebug && kEnableItemIteration) {\n      // validate iteration\n      std::size_t n = 0;\n      ItemIter prev;\n      for (auto iter = begin(); iter != end(); iter.advance()) {\n        FOLLY_SAFE_DCHECK(n == 0 || iter.pack() < prev.pack(), \"\");\n        ++n;\n        prev = iter;\n      }\n      FOLLY_SAFE_DCHECK(n == size(), \"\");\n    }\n\n    FOLLY_SAFE_DCHECK(\n        (Chunk::isEmptyInstance(access::to_address(chunks_))) ==\n            (bucket_count() == 0),\n        \"\");\n\n    std::size_t n1 = 0;\n    std::size_t n2 = 0;\n    auto cc = bucket_count() == 0 ? 0 : chunkCount();\n    for (std::size_t ci = 0; ci < cc; ++ci) {\n      ChunkPtr chunk = chunkAt(ci);\n      FOLLY_SAFE_DCHECK(chunk->eof() == (ci == 0), \"\");\n\n      auto iter = chunk->occupiedIter();\n\n      std::size_t chunkOccupied = 0;\n      for (auto piter = iter; piter.hasNext(); piter.next()) {\n        ++chunkOccupied;\n      }\n      n1 += chunkOccupied;\n\n      histoAt(stats.chunkOccupancyHisto, chunkOccupied)++;\n      histoAt(\n          stats.chunkOutboundOverflowHisto, chunk->outboundOverflowCount())++;\n      histoAt(stats.chunkHostedOverflowHisto, chunk->hostedOverflowCount())++;\n\n      while (iter.hasNext()) {\n        auto ii = iter.next();\n        ++n2;\n\n        {\n          auto& item = chunk->citem(ii);\n          auto hp = splitHash(this->computeItemHash(item));\n          FOLLY_SAFE_DCHECK(chunk->tag(ii) == hp.second, \"\");\n\n          std::size_t dist = 1;\n          std::size_t index = hp.first;\n          std::size_t delta = probeDelta(hp);\n          while (moduloByChunkCount(index) != ci) {\n            index += delta;\n            ++dist;\n          }\n\n          histoAt(stats.keyProbeLengthHisto, dist)++;\n        }\n\n        // misses could have any tag, so we do the dumb but accurate\n        // thing and just try them all\n        for (std::size_t ti = 0; ti < 256; ++ti) {\n          uint8_t tag = static_cast<uint8_t>(ti == 0 ? 1 : 0);\n          HashPair hp{ci, tag};\n\n          std::size_t dist = 1;\n          std::size_t index = hp.first;\n          std::size_t delta = probeDelta(hp);\n          for (std::size_t tries = 0; tries >> chunkShift() == 0 &&\n               chunkAt(moduloByChunkCount(index))->outboundOverflowCount() != 0;\n               ++tries) {\n            index += delta;\n            ++dist;\n          }\n\n          histoAt(stats.missProbeLengthHisto, dist)++;\n        }\n      }\n    }\n\n    FOLLY_SAFE_DCHECK(n1 == size(), \"\");\n    FOLLY_SAFE_DCHECK(n2 == size(), \"\");\n\n    stats.policy = pretty_name<Policy>();\n    stats.size = size();\n    stats.valueSize = sizeof(value_type);\n    stats.bucketCount = bucket_count();\n    stats.chunkCount = cc;\n\n    stats.totalBytes = sizeof(*this) + getAllocatedMemorySize();\n    stats.overheadBytes = stats.totalBytes - size() * sizeof(value_type);\n\n    return stats;\n  }\n};\n} // namespace detail\n} // namespace f14\n\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nnamespace f14 {\nnamespace test {\ninline void disableInsertOrderRandomization() {\n  if constexpr (kIsLibrarySanitizeAddress || kIsDebug) {\n    detail::tlsPendingSafeInserts(\n        static_cast<std::ptrdiff_t>(\n            (std::numeric_limits<std::size_t>::max)() / 2));\n  }\n}\n} // namespace test\n} // namespace f14\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/Util.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Traits.h>\n#include <folly/container/Iterator.h>\n#include <folly/functional/ApplyTuple.h>\n\n// Utility functions for container implementors\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename KeyType, typename Alloc>\nstruct TemporaryEmplaceKey {\n  TemporaryEmplaceKey(TemporaryEmplaceKey const&) = delete;\n  TemporaryEmplaceKey(TemporaryEmplaceKey&&) = delete;\n\n  template <typename... Args>\n  TemporaryEmplaceKey(Alloc& a, std::tuple<Args...>&& args) : alloc_(a) {\n    auto p = &value();\n    apply(\n        [&, p](auto&&... inner) {\n          std::allocator_traits<Alloc>::construct(\n              alloc_, p, std::forward<decltype(inner)>(inner)...);\n        },\n        std::move(args));\n  }\n\n  ~TemporaryEmplaceKey() {\n    std::allocator_traits<Alloc>::destroy(alloc_, &value());\n  }\n\n  KeyType& value() { return *static_cast<KeyType*>(static_cast<void*>(&raw_)); }\n\n  Alloc& alloc_;\n  folly::aligned_storage_for_t<KeyType> raw_;\n};\n\n// A map's emplace(args...) function takes arguments that can be used to\n// construct a pair<key_type const, mapped_type>, but that construction\n// only needs to take place if the key is not present in the container.\n// callWithExtractedKey helps to handle this efficiently by looking for a\n// reference to the key within the args list.  If the search is successful\n// then the search can be performed without constructing any temporaries.\n// If the search is not successful then callWithExtractedKey constructs\n// a temporary key_type and a new argument list suitable for constructing\n// the entire value_type if necessary.\n//\n// callWithExtractedKey(a, f, args...) will call f(k, args'...), where\n// k is the key and args'... is an argument list that can be used to\n// construct a pair of key and mapped value.  Note that this means f gets\n// the key twice.\n//\n// In some cases a temporary key must be constructed.  This is accomplished\n// with std::allocator_traits<>::construct, and the temporary will be\n// destroyed with std::allocator_traits<>::destroy.  Using the allocator's\n// construct method reduces unnecessary copies for pmr allocators.\n//\n// callWithExtractedKey supports heterogeneous lookup with the UsableAsKey\n// template parameter.  If a single key argument of type K is found in\n// args... then it will be passed directly to f if it is either KeyType or\n// if UsableAsKey<remove_cvref_t<K>>::value is true.  If you don't care\n// about heterogeneous lookup you can just pass a single-arg template\n// that extends std::false_type.\n\n// TODO(T31574848): We can remove the std::enable_if_t once we no longer\n// target platforms without N4387 (\"perfect initialization\" for pairs\n// and tuples).  libstdc++ at gcc-6.1.0 is the first release that contains\n// the improved set of pair constructors.\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    typename Func,\n    typename UsableKeyType,\n    typename Arg1,\n    typename Arg2,\n    std::enable_if_t<\n        std::is_constructible<\n            std::pair<KeyType const, MappedType>,\n            Arg1&&,\n            Arg2&&>::value,\n        int> = 0>\nauto callWithKeyAndPairArgs(\n    Func&& f,\n    UsableKeyType const& key,\n    std::tuple<Arg1>&& first_args,\n    std::tuple<Arg2>&& second_args) {\n  return f(\n      key,\n      std::forward<Arg1>(std::get<0>(first_args)),\n      std::forward<Arg2>(std::get<0>(second_args)));\n}\n\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    typename Func,\n    typename UsableKeyType,\n    typename... Args1,\n    typename... Args2>\nauto callWithKeyAndPairArgs(\n    Func&& f,\n    UsableKeyType const& key,\n    std::tuple<Args1...>&& first_args,\n    std::tuple<Args2...>&& second_args) {\n  return f(\n      key,\n      std::piecewise_construct,\n      std::move(first_args),\n      std::move(second_args));\n}\n\ntemplate <typename>\nusing ExactKeyMatchOnly = std::false_type;\n\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func,\n    typename Arg1,\n    typename... Args2,\n    std::enable_if_t<\n        std::is_same<remove_cvref_t<Arg1>, KeyType>::value ||\n            UsableAsKey<remove_cvref_t<Arg1>>::value,\n        int> = 0>\nauto callWithExtractedKey(\n    Alloc& /*unused*/,\n    Func&& f,\n    std::piecewise_construct_t /*unused*/,\n    std::tuple<Arg1>&& first_args,\n    std::tuple<Args2...>&& second_args) {\n  // we found a usable key in the args :)\n  auto const& key = std::get<0>(first_args);\n  return callWithKeyAndPairArgs<KeyType, MappedType>(\n      std::forward<Func>(f),\n      key,\n      std::tuple<Arg1&&>(std::move(first_args)),\n      std::tuple<Args2&&...>(std::move(second_args)));\n}\n\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func,\n    typename... Args1,\n    typename... Args2>\nauto callWithExtractedKey(\n    Alloc& a,\n    Func&& f,\n    std::piecewise_construct_t /*unused*/,\n    std::tuple<Args1...>&& first_args,\n    std::tuple<Args2...>&& second_args) {\n  // we will need to materialize a temporary key :(\n  TemporaryEmplaceKey<KeyType, Alloc> key(\n      a, std::tuple<Args1&&...>(std::move(first_args)));\n  return callWithKeyAndPairArgs<KeyType, MappedType>(\n      std::forward<Func>(f),\n      const_cast<KeyType const&>(key.value()),\n      std::forward_as_tuple(std::move(key.value())),\n      std::tuple<Args2&&...>(std::move(second_args)));\n}\n\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func>\nauto callWithExtractedKey(Alloc& a, Func&& f) {\n  return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(\n      a,\n      std::forward<Func>(f),\n      std::piecewise_construct,\n      std::tuple<>{},\n      std::tuple<>{});\n}\n\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func,\n    typename U1,\n    typename U2>\nauto callWithExtractedKey(Alloc& a, Func&& f, U1&& x, U2&& y) {\n  return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(\n      a,\n      std::forward<Func>(f),\n      std::piecewise_construct,\n      std::forward_as_tuple(std::forward<U1>(x)),\n      std::forward_as_tuple(std::forward<U2>(y)));\n}\n\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func,\n    typename U1,\n    typename U2>\nauto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2> const& p) {\n  return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(\n      a,\n      std::forward<Func>(f),\n      std::piecewise_construct,\n      std::forward_as_tuple(p.first),\n      std::forward_as_tuple(p.second));\n}\n\ntemplate <\n    typename KeyType,\n    typename MappedType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func,\n    typename U1,\n    typename U2>\nauto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2>&& p) {\n  // std::move(p.first) is wrong because if U1 is an lvalue reference the\n  // result will incorrectly be an rvalue ref.  static_cast here allows\n  // proper ref collapsing\n  return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(\n      a,\n      std::forward<Func>(f),\n      std::piecewise_construct,\n      std::forward_as_tuple(static_cast<U1&&>(p.first)),\n      std::forward_as_tuple(static_cast<U2&&>(p.second)));\n}\n\n// callWithConstructedKey is the set container analogue of\n// callWithExtractedKey\n\ntemplate <\n    typename KeyType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func,\n    typename Arg,\n    std::enable_if_t<\n        std::is_same<remove_cvref_t<Arg>, KeyType>::value ||\n            UsableAsKey<remove_cvref_t<Arg>>::value,\n        int> = 0>\nauto callWithConstructedKey(Alloc& /*unused*/, Func&& f, Arg&& arg) {\n  // we found a usable key in the args :)\n  auto const& key = arg;\n  return f(key, std::forward<Arg>(arg));\n}\n\ntemplate <\n    typename KeyType,\n    template <typename> class UsableAsKey = ExactKeyMatchOnly,\n    typename Alloc,\n    typename Func,\n    typename... Args>\nauto callWithConstructedKey(Alloc& a, Func&& f, Args&&... args) {\n  // we will need to materialize a temporary key :(\n  TemporaryEmplaceKey<KeyType, Alloc> key(\n      a, std::forward_as_tuple(std::forward<Args>(args)...));\n  return f(const_cast<KeyType const&>(key.value()), std::move(key.value()));\n}\n\n// Traits to simplify deduction guides implementation for containers.\n\n// SFINAE constraint to test whether a type is an allocator according to\n// is_allocator trait.\ntemplate <typename T>\nusing RequireAllocator = std::enable_if_t<is_allocator_v<T>, T>;\n\ntemplate <typename T>\nusing RequireNotAllocator = std::enable_if_t<!is_allocator_v<T>, T>;\n\ntemplate <typename T>\nusing RequireInputIterator =\n    std::enable_if_t<iterator_category_matches_v<T, std::input_iterator_tag>>;\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/tape_detail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/container/Iterator.h>\n#include <folly/container/range_traits.h>\n#include <folly/lang/Hint.h>\n#include <folly/memory/UninitializedMemoryHacks.h>\n\n#include <cstddef>\n#include <iterator>\n#include <memory>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename Container, bool = is_contiguous_range_v<Container>>\nstruct tape_reference_traits {\n  using iterator = typename Container::const_iterator;\n  using reference = Range<iterator>;\n\n  static constexpr reference make(iterator f, iterator l) {\n    return reference{f, l};\n  }\n};\n\ntemplate <typename Container>\nstruct tape_reference_traits<Container, true> {\n  using iterator = typename Container::const_iterator;\n  using value_type = typename std::iterator_traits<iterator>::value_type;\n  using reference = Range<const value_type*>;\n\n  static constexpr auto* get_address(iterator it) {\n    // std::to_address is only available since C++20\n    if constexpr (std::is_pointer_v<iterator>) {\n      return it;\n    } else {\n      return it.operator->();\n    }\n  }\n\n  static constexpr reference make(iterator f, iterator l) {\n    return reference{get_address(f), get_address(l)};\n  }\n};\n\ntemplate <typename R>\nusing get_range_const_iterator_t =\n    decltype(std::cbegin(std::declval<const R&>()));\n\nstruct fake_type {};\n\ntemplate <typename R>\nusing maybe_range_const_iterator_t =\n    detected_or_t<fake_type*, get_range_const_iterator_t, R>;\n\ntemplate <typename R>\nusing maybe_range_value_t =\n    iterator_value_type_t<maybe_range_const_iterator_t<R>>;\n\n// This is a big function to inline but it's used insie a big function too\ntemplate <typename I, typename S>\nauto compute_total_tape_len_if_possible(I f, S l) {\n  using success = std::pair<std::size_t, std::size_t>;\n  using failure = fake_type;\n  if constexpr (!iterator_category_matches_v<I, std::forward_iterator_tag>) {\n    return failure{};\n  }\n  // We have to special case StringPiece to special case `const char*` and\n  // `char[]`\n  else if constexpr (\n      std::is_convertible_v<iterator_value_type_t<I>, folly::StringPiece>) {\n    std::size_t records_size = 0U;\n    std::size_t flat_size = 0U;\n\n    for (I i = f; i != l; ++i) {\n      ++records_size;\n      flat_size += folly::StringPiece(*i).size();\n    }\n    return success{records_size, flat_size};\n  } else if constexpr (!range_has_known_distance_v<iterator_value_type_t<I>>) {\n    return failure{};\n  } else {\n    std::size_t records_size = 0U;\n    std::size_t flat_size = 0U;\n\n    for (I i = f; i != l; ++i) {\n      ++records_size;\n      flat_size +=\n          static_cast<std::size_t>(std::distance(std::begin(*i), std::end(*i)));\n    }\n    return success{records_size, flat_size};\n  }\n}\n\ntemplate <typename Container, typename I, typename S>\nvoid append_range_unsafe(Container& c, I f, S l) {\n  if constexpr (\n      !iterator_category_matches_v<I, std::random_access_iterator_tag> ||\n      !std::is_trivially_copy_constructible_v<iterator_value_type_t<I>> ||\n      !(is_instantiation_of_v<std::vector, Container> ||\n        is_instantiation_of_v<std::basic_string, Container>)) {\n    c.insert(c.end(), f, l);\n  } else {\n    folly::compiler_may_unsafely_assume(l >= f);\n    auto old_size = c.size();\n    detail::unsafeVectorSetLargerSize(c, c.size() + (l - f));\n    std::copy(f, l, c.begin() + old_size);\n  }\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_detail_test\",\n    srcs = [\n        \"F14DetailTest.cpp\",\n    ],\n    deps = [\n        \"//folly:traits\",\n        \"//folly/container/detail:f14_hash_detail\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"bool_wrapper_test\",\n    srcs = [\n        \"BoolWrapperTest.cpp\",\n    ],\n    deps = [\n        \"//folly/container/detail:bool_wrapper\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/container/detail/test/BoolWrapperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/detail/BoolWrapper.h>\n\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nTEST(BoolWrapperTest, Default) {\n  EXPECT_FALSE(folly::detail::BoolWrapper{});\n}\n\nTEST(BoolWrapperTest, BoolConversion) {\n  folly::detail::BoolWrapper t = true;\n  EXPECT_TRUE(t);\n\n  folly::detail::BoolWrapper f = false;\n  EXPECT_FALSE(f);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/detail/test/F14DetailTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/detail/F14Table.h>\n\n#include <folly/Traits.h>\n#include <folly/portability/GTest.h>\n\ntemplate <typename A, typename B>\nusing detect_op_eq = decltype(FOLLY_DECLVAL(A) == FOLLY_DECLVAL(B));\n\nstruct F14HashedKeyTest : testing::Test {};\n\nTEST_F(F14HashedKeyTest, string) {\n  using namespace std::literals;\n  folly::F14HashedKey<std::string> key{\"foo\"};\n\n  EXPECT_EQ(\"foo\", key);\n  EXPECT_EQ(key, \"foo\");\n  EXPECT_NE(\"bar\", key);\n  EXPECT_NE(key, \"bar\");\n\n  EXPECT_EQ(\"foo\"s, key);\n  EXPECT_EQ(key, \"foo\"s);\n  EXPECT_NE(\"bar\"s, key);\n  EXPECT_NE(key, \"bar\"s);\n\n  EXPECT_EQ(\"foo\"sv, key);\n  EXPECT_EQ(key, \"foo\"sv);\n  EXPECT_NE(\"bar\"sv, key);\n  EXPECT_NE(key, \"bar\"sv);\n}\n\nTEST_F(F14HashedKeyTest, transparent) {\n  struct Key {\n    int num{};\n    Key(double, int num_) : num{num_} {}\n  };\n  static_assert(!std::is_constructible_v<Key, int>);\n  static_assert(!std::is_constructible_v<int, Key>);\n  static_assert(!folly::is_detected_v<detect_op_eq, Key, Key>);\n  static_assert(!folly::is_detected_v<detect_op_eq, Key, int>);\n  static_assert(!folly::is_detected_v<detect_op_eq, int, Key>);\n  struct KeyHash {\n    using is_transparent = void;\n    size_t operator()(Key key) const { return key.num; }\n    [[maybe_unused]] size_t operator()(int key) const;\n  };\n  struct KeyEqual {\n    using is_transparent = void;\n    bool operator()(Key a, Key b) const { return a.num == b.num; }\n    bool operator()(Key a, int b) const { return a.num == b; }\n    bool operator()(int a, Key b) const { return a == b.num; }\n  };\n  using HKey = folly::F14HashedKey<Key, KeyHash, KeyEqual>;\n\n  HKey key{Key{0., 7}};\n\n  EXPECT_TRUE(key == Key(0., 7));\n  EXPECT_FALSE(key == Key(0., 8));\n  EXPECT_TRUE(Key(0., 7) == key);\n  EXPECT_FALSE(Key(0., 8) == key);\n\n  EXPECT_TRUE(key == 7);\n  EXPECT_FALSE(key == 8);\n  EXPECT_TRUE(7 == key);\n  EXPECT_FALSE(8 == key);\n\n  EXPECT_TRUE(key == HKey(Key(0., 7)));\n  EXPECT_FALSE(key == HKey(Key(0., 8)));\n  EXPECT_TRUE(HKey(Key(0., 7)) == key);\n  EXPECT_FALSE(HKey(Key(0., 8)) == key);\n}\n\nTEST(F14SizeAndChunkShift, packed) {\n  folly::f14::detail::SizeAndChunkShift sz;\n  static_assert(sizeof(sz) == sizeof(size_t));\n  EXPECT_EQ(sz.size(), 0);\n  EXPECT_EQ(sz.chunkShift(), 0);\n\n  sz.setSize(12345678);\n  EXPECT_EQ(sz.size(), 12345678);\n  EXPECT_EQ(sz.chunkShift(), 0);\n\n  sz.setChunkCount(1);\n  EXPECT_EQ(sz.size(), 12345678);\n  EXPECT_EQ(sz.chunkCount(), 1);\n  EXPECT_EQ(sz.chunkShift(), 0);\n\n  for (int shift = 0;\n       shift <= folly::f14::detail::SizeAndChunkShift::kMaxSupportedChunkShift;\n       ++shift) {\n    const auto count = (1ULL << shift);\n    sz.setChunkCount(count);\n    EXPECT_EQ(sz.size(), 12345678);\n    EXPECT_EQ(sz.chunkCount(), count);\n    EXPECT_EQ(sz.chunkShift(), shift);\n  }\n}\n"
  },
  {
    "path": "folly/container/heap_vector_types.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * This header defines two new containers, heap_vector_set and heap_vector_map\n * classes. These containers are designed to be a drop-in replacement of\n * sorted_vector_set and sorted_vector_map. Similarly to sorted_vector_map/set,\n * heap_vector_map/set models AssociativeContainers. Below, we list important\n * differences from std::set and std::map (also documented in\n * folly/sorted_vector_types.h):\n *\n *   - insert() and erase() invalidate iterators and references.\n *   - erase(iterator) returns an iterator pointing to the next valid element.\n *   - insert() and erase() are O(N)\n *   - our iterators model RandomAccessIterator\n *   - heap_vector_map::value_type is pair<K,V>, not pair<const K,V>.\n *     (This is basically because we want to store the value_type in\n *     std::vector<>, which requires it to be Assignable.)\n *   - insert() single key variants, emplace(), and emplace_hint() only provide\n *     the strong exception guarantee (unchanged when exception is thrown) when\n *     std::is_nothrow_move_constructible<value_type>::value is true.\n *\n * heap_vector_map/set have exactly the same size as sorted_vector_map/set.\n * These containers utilizes a vector container (e.g. std::vector) to store the\n * values. Heap containers (similarly to sorted containers) have no additional\n * memory overhead. They lay out the data in an optimal way w.r.t locality (see\n * https://algorithmica.org/en/eytzinger), called eytzinger or heap order. For\n * example in a sorted_vector_set, the underlying vector contains:\n *              index    0   1   2   3   4   5   6   7   8   9\n *       vector[index]   0, 10, 20, 30, 40, 50, 60, 70, 80, 90\n * while in a heap_vector_set, the underlying vector contains:\n *              index    0   1   2   3   4   5   6   7   8   9\n *       vector[index]  60, 30, 80, 10, 50, 70, 90,  0, 20, 40\n * Lookup elements in sorted vector containers relies on binary search,\n * std::lower_bound. While in heap containers, lookup operation has two\n * benefits:\n *\n * 1. Cache locality, the container is traversed sequentially instead of binary\n * search that jumps around the sorted vector.\n * 2. The branches in a binary search are mispredicted resulting in hardware\n * penalty while using heap lookup search the branch can be avoided by using\n * cmov instruction. We observerd look up operations are up to 2X faster than\n * sorted_vector_map.\n *\n * However, Insertion/deletion operations are much slower. If insertions and\n * deletions are rare operations for your use case then heap containers might\n * be the right choice for you. Also, to minimize impact of insertions while\n * creating heap containers, we recommend not to insert element by element\n * instead first collect elements in a vector, then construct the heap map from\n * it.\n *\n * Another substantial trade off, inorder traversal of heap container elements\n * is slower than sorted vector containers. This is expected as heap map needs\n * to jump around to access map elements in order. A remedy is to use underlying\n * vector iterators. This works only when the order is irrelevant. For example,\n * using heap container iterator:\n *         for (auto& e: HeapSet)\n *           std::cout << e << \", \";\n * Prints:  0, 10, 20, 30, 40, 50, 60, 70, 80, 90\n * and using underlying vector container iterator:\n *         for (auto& e : HeapSet.iterate())\n *           std::cout << e << \", \";\n * Prints:  60, 30, 80, 10, 50, 70, 90,  0, 20, 40\n * The latter loop is the fastest traversal.\n *\n * Finally The main benefit of heap containers is a compact representation\n * that achieves fast random lookup. Use this map when lookup is the\n * dominant operation and at the same time saving memory is important.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cassert>\n#include <functional>\n#include <initializer_list>\n#include <iterator>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/Range.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/container/Iterator.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Exception.h>\n#include <folly/memory/MemoryResource.h>\n#include <folly/portability/Builtins.h>\n#include <folly/small_vector.h>\n\nnamespace folly {\ntemplate <\n    typename Key,\n    typename Value,\n    typename Compare,\n    typename Allocator,\n    typename GrowthPolicy,\n    typename Container>\nclass heap_vector_map;\n\nnamespace detail {\n\nnamespace heap_vector_detail {\n\n/*\n * Heap Containers Helper Functions\n * ---------------------------------\n * Terminology:\n * - offset means the index at which element is stored in the container vector,\n *   following the heap order.\n * - index means the element rank following the container compare order.\n *\n * Introduction:\n * Heap order can be constructed from a sorted input vector using below\n * naive algorithm.\n *\n *     heapify(input, output, index = 0, offset = 1) {\n *       if (offset <= size) {\n *         index = heapify(input, output, i, 2 * offset);\n *         output[offset - 1] = input[index++];\n *         index = heapify(input, output, i, 2 * offset + 1);\n *       }\n *       return index;\n *     }\n *\n * Helper functions below implement efficient algorithms to:\n *  - Find offsets of the smallest/greatest elements.\n *  - Given an offset, calculate the offset where next/previous element is\n *  stored if it exists.\n *  - Given a start and an end offsets, calculate distance between their\n *  corresponding indexes.\n *  - Fast inplace heapification.\n *  - Insert a new element while preserving heap order.\n *  - Delete an element and preserve heap order.\n *  - find lower/upper bound of a key following container compare order.\n */\n\n// Returns the offset of the smallest element in the heap container.\n// The samllest element is stored at:\n//     vector[2^n - 1] where 2^n <= size < 2^(n+1).\n// firstOffset returns 2^n - 1 if size > 0, 0 otherwise\ntemplate <typename size_type>\nsize_type firstOffset(size_type size) {\n  if (size) {\n    return (1\n            << (CHAR_BIT * sizeof(unsigned long) -\n                __builtin_clzl((unsigned long)size) - 1)) -\n        1;\n  } else {\n    return 0;\n  }\n}\n\n// Returns the offset of greatest element in heap container.\n// the greatest element is storted at:\n//     vector[2^n - 2] if 2^n - 1 <= size < 2^(n+1)\n// lastOffset returns 2^n - 2 if size > 0, 0 otherwise\ntemplate <typename size_type>\nsize_type lastOffset(size_type size) {\n  if (size) {\n    return ((size & (size + 1)) == 0 ? size : firstOffset(size)) - 1;\n  }\n  return 0;\n}\n\n// Returns the offset of the next element. It is calculated based on the\n// size of the map.\n// To simplify implementation, offset must be 1-based\n// return value is also 1-based.\ntemplate <typename size_type>\nsize_type next(size_type offset, size_type size) {\n  auto next = (2 * offset);\n  if (next >= size) {\n    return offset >> __builtin_ffsl((unsigned long)~offset);\n  } else {\n    next += 1;\n    for (auto n = 2 * next; n <= size; n *= 2) {\n      next = n;\n    }\n    return next;\n  }\n}\n\n// Returns the offset of the previous element. It is calculated based on\n// the size of the map.\n// To simplify implementation, offset must be 1-based\n// return value is also 1-based.\ntemplate <typename size_type>\nsize_type prev(size_type offset, size_type size) {\n  auto prev = 2 * offset;\n  if (prev <= size) {\n    for (auto p = 2 * prev + 1; p <= size; p = 2 * p + 1) {\n      if (2 * p >= size) {\n        return p;\n      }\n    }\n    return prev;\n  }\n  return offset >> __builtin_ffsl((unsigned long)offset);\n}\n\n// To avoid scanning all offsets, skip offsets that cannot be within the\n// range of offset1 and offset2.\n// Note We could compute least common ancestor of offset1 and offset2\n// to further minimize scanning. However it is not profitable when container\n// is relatively small.\ntemplate <typename size_type>\nsize_type getStartOffsetToScan(size_type offset1, size_type offset2) {\n  while ((offset1 & (offset1 - 1)) != 0) {\n    offset1 >>= 1;\n  }\n  if (offset1 > 1) {\n    while ((offset2 & (offset2 - 1)) != 0) {\n      offset2 >>= 1;\n    }\n    offset1 = std::min(offset1, offset2);\n  }\n  return offset1;\n}\n\n// Given a start and end offsets, returns the distance between\n// their corresponding indexes.\ntemplate <typename Container, typename size_type>\ntypename Container::difference_type distance(\n    Container& cont, size_type start, size_type end) {\n  using difference_type = typename Container::difference_type;\n  difference_type dist = 0;\n  size_type size = cont.size();\n  // To simplify logic base start and end from one.\n  start++;\n  end++;\n  std::function<bool(size_type, size_type)> calculateDistance =\n      [&](size_type offset, size_type lb) {\n        if (offset > size) {\n          return false;\n        }\n        for (; offset <= size; offset <<= 1) {\n          ;\n        }\n        offset >>= 1;\n        for (; offset > lb; offset >>= 1) {\n          if (offset == start) {\n            if (dist) {\n              dist *= -1;\n              return true;\n            }\n            dist = 1;\n          } else if (offset == end) {\n            if (dist) {\n              return true;\n            }\n            dist = 1;\n          } else if (dist) {\n            dist++;\n          }\n          if (calculateDistance(2 * offset + 1, offset)) {\n            return true;\n          }\n        }\n        return false;\n      };\n  auto offset = getStartOffsetToScan(start, end);\n  calculateDistance(offset, size_type(0));\n  // Handle start == end()\n  if (start > size) {\n    dist *= -1;\n  }\n\n  return dist;\n}\n\n// Returns the offset for each index in heap container\n// for example if size = 7 then\n//       index     0  1  2  3  4  5  6\n//     offsets = { 3, 1, 4, 0, 5, 2, 6 }\n// The smallest element (index = 0) of heap container is stored at cont[3] and\n// so on.\ntemplate <typename size_type, typename Offsets>\nvoid getOffsets(size_type size, Offsets& offsets) {\n  size_type i = 0;\n  size_type offset = 0;\n  size_type index = size;\n  do {\n    for (size_type o = offset; o < size; o = 2 * o + 2) {\n      offsets[i++] = o;\n    }\n    offset = offsets[--i];\n    offsets[--index] = offset;\n    offset = 2 * offset + 1;\n  } while (i || offset < size);\n}\n\n// Inplace conversion of a sorted vector to heap layout\n// This algorithm utilizes circular swaps to position each element in its heap\n// order offset in the vector. For example, given a sorted vector below:\n//     cont = { 0, 10, 20, 30, 40, 50, 60, 70 }\n// getOffsets returns:\n//       index     0  1  2  3  4  5  6  7\n//     offsets = { 4, 2, 6, 1, 3, 5, 7, 0 }\n//\n// The algorithm moves elements circularly:\n// cont[4]->cont[0]->cont[7]->cont[6]->cont[2]->cont[1]->cont[3]-> cont[4]\n// cont[5] remains inplace\n// returns:\n// cont = { 40, 20, 60, 10, 30, 50, 70, 0 }\ntemplate <class Container>\nvoid heapify(Container& cont) {\n  using size_type = typename Container::size_type;\n  size_type size = cont.size();\n  std::vector<size_type> offsets;\n  offsets.resize(size);\n  getOffsets(size, offsets);\n\n  std::function<void(size_type, size_type)> rotate =\n      [&](size_type next, size_type index) {\n        std::vector<size_type> worklist;\n        while (index != next) {\n          worklist.push_back(next);\n          next = offsets[next];\n        }\n        while (!worklist.empty()) {\n          auto cur = worklist.back();\n          worklist.pop_back();\n          cont[offsets[cur]] = std::move(cont[cur]);\n          offsets[cur] = size;\n        }\n      };\n\n  for (size_type index = 0; index < size; index++) {\n    // already moved\n    if (offsets[index] == size) {\n      continue;\n    }\n    size_type next = offsets[index];\n    if (next == index) {\n      continue;\n    }\n    // Subtlety: operator[] returns a Container::reference. Because\n    // Container::reference can be a proxy, using bare `auto` is not\n    // sufficient to remove the \"reference nature\" of\n    // Container::reference and force a move out of the container;\n    // instead, we need Container::value_type.\n    typename Container::value_type tmp = std::move(cont[index]);\n    rotate(next, index);\n    cont[next] = std::move(tmp);\n  }\n}\n\n// Below helper functions to implement inplace insertion/deletion.\n\n// Returns the sequence of offsets that need to be moved. This sequence\n// is the range between size-1 and the offset of the inserted element.\n// There are two cases: {size - 1, ..., offset} or {offset, ..., size - 1}\n// For example if 45 is inserted at offset == 0 and size == 9.\n// Before insertion:\n//     element    0 10   20 30 40     50 60 70\n//     offset     7  3    1  4  0      5  2  6\n// After inserting 45:\n//     element    0 10   20 30 40 45  50 60 70\n//     offset     7  3    8  1  4  0   5  2  6\n// This function returns:\n//     offsets = { 8, 1, 4, 0 }\ntemplate <typename size_type, typename Offsets>\nbool getOffsetRange(\n    size_type size, Offsets& offsets, size_type offset, size_type current) {\n  for (; current <= size; current <<= 1) {\n    if (getOffsetRange(size, offsets, offset, 2 * current + 1)) {\n      return true;\n    }\n    if (offsets.empty()) {\n      if (offset == current || size == current) {\n        // Start recording offsets that need to be moved.\n        offsets.push_back(current - 1);\n      }\n    } else {\n      // record offset\n      offsets.push_back(current - 1);\n      if (offset == current || size == current) {\n        // Stop recording offsets\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\ntemplate <typename size_type, typename Offsets>\nvoid getOffsetRange(size_type size, Offsets& offsets, size_type offset) {\n  auto start = getStartOffsetToScan(offset, size);\n  getOffsetRange(size, offsets, offset, start);\n}\n\n// Insert a new element in heap order\n// Assumption: the inserted element is already pushed at the back of the\n// container vector (i.e. located at vector[size - 1]).\ntemplate <typename size_type, class Container>\nsize_type insert(size_type offset, Container& cont) {\n  size_type size = cont.size();\n  if (size == 1) {\n    return 0;\n  }\n  size_type adjust = 1;\n  if (offset == size - 1) {\n    adjust = 0;\n    auto last = lastOffset(size);\n    if (last == offset) {\n      return offset;\n    }\n    offset = last;\n  }\n  std::vector<size_type> offsets;\n  offsets.reserve(size);\n  getOffsetRange(size, offsets, offset + 1);\n  typename Container::value_type v = std::move(cont[size - 1]);\n  if (offsets[0] != offset) {\n    for (size_type i = 1, e = offsets.size(); i < e; ++i) {\n      cont[offsets[i - 1]] = std::move(cont[offsets[i]]);\n    }\n    cont[offset] = std::move(v);\n    return offset;\n  }\n  for (size_type i = offsets.size() - 1; i > adjust; --i) {\n    cont[offsets[i]] = std::move(cont[offsets[i - 1]]);\n  }\n  cont[offsets[adjust]] = std::move(v);\n  return offsets[adjust];\n}\n\n// Erase one element and preserve heap order\ntemplate <typename size_type, class Container>\nsize_type erase(size_type offset, Container& cont) {\n  size_type size = cont.size();\n  if (offset + 1 == size) {\n    auto ret = next(offset + 1, size);\n    cont.resize(size - 1);\n    return ret ? ret - 1 : size - 1;\n  }\n  std::vector<size_type> offsets;\n  offsets.reserve(size);\n  getOffsetRange(size, offsets, offset + 1);\n  if (offsets[0] == offset) {\n    for (size_type i = 1, e = offsets.size(); i < e; i++) {\n      cont[offsets[i - 1]] = std::move(cont[offsets[i]]);\n    }\n  } else {\n    for (size_type i = offsets.size() - 1; i > 0; --i) {\n      cont[offsets[i]] = std::move(cont[offsets[i - 1]]);\n    }\n  }\n  cont.resize(size - 1);\n  return offset;\n}\n\n// Search lower bound in a container sorted in heap order.\n// To speed up lower_bound for small containers, peel four iterations and use\n// reverse compare to exit quickly.\n// The branch inside the loop is converted to a cmov by the compiler. cmov are\n// more efficient when the branch is unpredictable.\ntemplate <typename Compare, typename RCompare, typename Container>\ntypename Container::size_type lower_bound(\n    Container& cont, Compare cmp, RCompare reverseCmp) {\n  using size_type = typename Container::size_type;\n  size_type size = cont.size();\n  auto last = size;\n  size_type offset = 0;\n  if (size) {\n    if (cmp(cont[offset])) {\n      offset = 2 * offset + 2;\n    } else {\n      if (!reverseCmp(cont[offset])) {\n        return offset;\n      }\n      last = offset;\n      offset = 2 * offset + 1;\n    }\n    if (offset < size) {\n      if (cmp(cont[offset])) {\n        offset = 2 * offset + 2;\n      } else {\n        if (!reverseCmp(cont[offset])) {\n          return offset;\n        }\n        last = offset;\n        offset = 2 * offset + 1;\n      }\n      if (offset < size) {\n        if (cmp(cont[offset])) {\n          offset = 2 * offset + 2;\n        } else {\n          if (!reverseCmp(cont[offset])) {\n            return offset;\n          }\n          last = offset;\n          offset = 2 * offset + 1;\n        }\n        if (offset < size) {\n          if (cmp(cont[offset])) {\n            offset = 2 * offset + 2;\n          } else {\n            if (!reverseCmp(cont[offset])) {\n              return offset;\n            }\n            last = offset;\n            offset = 2 * offset + 1;\n          }\n          for (; offset < size; offset++) {\n            if (cmp(cont[offset])) {\n              offset = 2 * offset + 1;\n            } else {\n              last = offset;\n              offset = 2 * offset;\n            }\n          }\n        }\n      }\n    }\n  }\n  return last;\n}\n\ntemplate <typename Compare, typename Container>\ntypename Container::size_type upper_bound(Container& cont, Compare cmp) {\n  using size_type = typename Container::size_type;\n  auto size = cont.size();\n  auto last = size;\n  for (size_type offset = 0; offset < size; offset++) {\n    if (!cmp(cont[offset])) {\n      offset = 2 * offset + 1;\n    } else {\n      last = offset;\n      offset = 2 * offset;\n    }\n  }\n  return last;\n}\n\n// Helper functions below are similar to sorted containers. Wherever\n// applicable renamed to heap containers.\ntemplate <typename, typename Compare, typename Key, typename T>\nstruct heap_vector_enable_if_is_transparent {};\n\ntemplate <typename Compare, typename Key, typename T>\nstruct heap_vector_enable_if_is_transparent<\n    void_t<typename Compare::is_transparent>,\n    Compare,\n    Key,\n    T> {\n  using type = T;\n};\n\n// This wrapper goes around a GrowthPolicy and provides iterator\n// preservation semantics, but only if the growth policy is not the\n// default (i.e. nothing).\ntemplate <class Policy>\nstruct growth_policy_wrapper : private Policy {\n  template <class Container, class Iterator>\n  Iterator increase_capacity(Container& c, Iterator desired_insertion) {\n    using diff_t = typename Container::difference_type;\n    diff_t d = desired_insertion - c.begin();\n    Policy::increase_capacity(c);\n    return c.begin() + d;\n  }\n};\ntemplate <>\nstruct growth_policy_wrapper<void> {\n  template <class Container, class Iterator>\n  Iterator increase_capacity(Container&, Iterator it) {\n    return it;\n  }\n};\n\ntemplate <class OurContainer, class Container, class InputIterator>\nvoid bulk_insert(\n    OurContainer& sorted,\n    Container& cont,\n    InputIterator first,\n    InputIterator last) {\n  assert(first != last);\n\n  auto const prev_size = cont.size();\n  cont.insert(cont.end(), first, last);\n  auto const middle = cont.begin() + prev_size;\n\n  auto const& cmp(sorted.value_comp());\n  if (!std::is_sorted(middle, cont.end(), cmp)) {\n    std::sort(middle, cont.end(), cmp);\n  }\n  if (middle != cont.begin() && !cmp(*(middle - 1), *middle)) {\n    std::inplace_merge(cont.begin(), middle, cont.end(), cmp);\n  }\n  cont.erase(\n      std::unique(\n          cont.begin(),\n          cont.end(),\n          [&](typename OurContainer::value_type const& a,\n              typename OurContainer::value_type const& b) {\n            return !cmp(a, b) && !cmp(b, a);\n          }),\n      cont.end());\n  heapify(cont);\n}\n\ntemplate <typename Container, typename Compare>\nbool is_sorted_unique(Container const& container, Compare const& comp) {\n  if (container.empty()) {\n    return true;\n  }\n  auto const e = container.end();\n  for (auto a = container.begin(), b = std::next(a); b != e; ++a, ++b) {\n    if (!comp(*a, *b)) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename Container, typename Compare>\nContainer&& as_sorted_unique(Container&& container, Compare const& comp) {\n  std::sort(container.begin(), container.end(), comp);\n  container.erase(\n      std::unique(\n          container.begin(),\n          container.end(),\n          [&](auto const& a, auto const& b) {\n            return !comp(a, b) && !comp(b, a);\n          }),\n      container.end());\n  return static_cast<Container&&>(container);\n}\n\n// class value_compare_map is used to compare map elements.\ntemplate <class Compare>\nstruct value_compare_map : Compare {\n  template <typename... value_type>\n  auto operator()(const value_type&... a) const\n      noexcept(is_nothrow_invocable_v<const Compare&, decltype((a.first))...>)\n          -> invoke_result_t<const Compare&, decltype((a.first))...> {\n    return Compare::operator()(a.first...);\n  }\n\n  template <typename value_type>\n  const auto& getKey(const value_type& a) const noexcept {\n    return a.first;\n  }\n\n  explicit value_compare_map(const Compare& c) noexcept(\n      std::is_nothrow_copy_constructible<Compare>::value)\n      : Compare(c) {}\n};\n\n// wrapper class value_compare_set for set elements.\ntemplate <class Compare>\nstruct value_compare_set : Compare {\n  using Compare::operator();\n\n  template <typename value_type>\n  value_type& getKey(value_type& a) const noexcept {\n    return a;\n  }\n\n  explicit value_compare_set(const Compare& c) noexcept(\n      std::is_nothrow_copy_constructible<Compare>::value)\n      : Compare(c) {}\n};\n\n/**\n * A heap_vector_container is a container similar to std::set<>, but\n * implemented as a heap array with std::vector<>.\n * This class contains shared implementation between set and map. It\n * fully implements set methods and used as base class for map.\n *\n * @tparam T               Data type to store\n * @tparam Compare         Comparison function that imposes a\n *                              strict weak ordering over instances of T\n * @tparam Allocator       allocation policy\n * @tparam GrowthPolicy    policy object to control growth\n * @tparam Container       underlying vector where elements are stored\n * @tparam KeyT            key type, for set it is same as T.\n * @tparam ValueCompare    wrapper class to compare Container::value_type\n *\n */\ntemplate <\n    class T,\n    class Compare = std::less<T>,\n    class Allocator = std::allocator<T>,\n    class GrowthPolicy = void,\n    class Container = std::vector<T, Allocator>,\n    class KeyT = T,\n    class ValueCompare = value_compare_set<Compare>>\nclass heap_vector_container : growth_policy_wrapper<GrowthPolicy> {\n protected:\n  growth_policy_wrapper<GrowthPolicy>& get_growth_policy() { return *this; }\n\n  template <typename K, typename V, typename C = Compare>\n  using if_is_transparent =\n      _t<heap_vector_enable_if_is_transparent<void, C, K, V>>;\n\n  struct EBO;\n\n public:\n  using key_type = KeyT;\n  using value_type = T;\n  using key_compare = Compare;\n  using value_compare = ValueCompare;\n  using allocator_type = Allocator;\n  using container_type = Container;\n  using pointer = typename Container::pointer;\n  using reference = typename Container::reference;\n  using const_reference = typename Container::const_reference;\n  using difference_type = typename Container::difference_type;\n  using size_type = typename Container::size_type;\n\n  // Defines inorder iterator for heap set.\n  template <typename Iter>\n  struct heap_iterator {\n    using iterator_category = std::random_access_iterator_tag;\n    using size_type = typename Container::size_type;\n    using difference_type =\n        typename std::iterator_traits<Iter>::difference_type;\n    using value_type = typename std::iterator_traits<Iter>::value_type;\n    using pointer = typename std::iterator_traits<Iter>::pointer;\n    using reference = typename std::iterator_traits<Iter>::reference;\n\n    heap_iterator() = default;\n    template <typename C>\n    heap_iterator(Iter ptr, C* cont) {\n      ptr_ = ptr;\n      cont_ = const_cast<Container*>(cont);\n    }\n\n    template <\n        typename I2,\n        typename = typename std::enable_if<\n            std::is_same<typename Container::iterator, I2>::value>::type>\n    /* implicit */ heap_iterator(const heap_iterator<I2>& rawIterator)\n        : ptr_(rawIterator.ptr_), cont_(rawIterator.cont_) {}\n\n    ~heap_iterator() = default;\n\n    heap_iterator(const heap_iterator& rawIterator) = default;\n\n    heap_iterator& operator=(const heap_iterator& rawIterator) = default;\n    heap_iterator& operator=(Iter ptr) {\n      assert(\n          (ptr - cont_->begin()) >= 0 &&\n          (ptr - cont_->begin()) <= (difference_type)cont_->size());\n      ptr_ = ptr;\n      return (*this);\n    }\n\n    bool operator==(const heap_iterator& rawIterator) const {\n      return ptr_ == rawIterator.ptr_;\n    }\n    bool operator!=(const heap_iterator& rawIterator) const {\n      return !operator==(rawIterator);\n    }\n\n    heap_iterator& operator+=(const difference_type& movement) {\n      size_type offset = ptr_ - cont_->begin() + 1;\n      auto size = cont_->size();\n      if (movement < 0) {\n        difference_type i = 0;\n\n        if (offset - 1 == size) {\n          // handle --end()\n          offset = heap_vector_detail::lastOffset(size) + 1;\n          i = -1;\n        }\n        for (; i > movement; i--) {\n          offset = heap_vector_detail::prev(offset, size);\n        }\n      } else {\n        for (difference_type i = 0; i < movement; i++) {\n          offset = heap_vector_detail::next(offset, size);\n        }\n      }\n      ptr_ = cont_->begin() + (offset == 0 ? cont_->size() : offset - 1);\n      return (*this);\n    }\n\n    heap_iterator& operator-=(const difference_type& movement) {\n      return operator+=(-movement);\n    }\n    heap_iterator& operator++() { return operator+=(1); }\n    heap_iterator& operator--() { return operator-=(1); }\n    heap_iterator operator++(int) {\n      auto temp(*this);\n      operator+=(1);\n      return temp;\n    }\n    heap_iterator operator--(int) {\n      auto temp(*this);\n      operator-=(1);\n      return temp;\n    }\n    heap_iterator operator+(const difference_type& movement) {\n      auto temp(*this);\n      temp += movement;\n      return temp;\n    }\n    heap_iterator operator+(const difference_type& movement) const {\n      auto temp(*this);\n      temp += movement;\n      return temp;\n    }\n\n    heap_iterator operator-(const difference_type& movement) {\n      auto temp(*this);\n      temp -= movement;\n      return temp;\n    }\n\n    heap_iterator operator-(const difference_type& movement) const {\n      auto temp(*this);\n      temp -= movement;\n      return temp;\n    }\n\n    difference_type operator-(const heap_iterator& rawIterator) {\n      assert(cont_ == rawIterator.cont_);\n      size_type offset0 = ptr_ - cont_->begin();\n      size_type offset1 = rawIterator.ptr_ - cont_->begin();\n      if (offset1 == offset0) {\n        return 0;\n      }\n      return heap_vector_detail::distance(*cont_, offset1, offset0);\n    }\n\n    difference_type operator-(const heap_iterator& rawIterator) const {\n      assert(cont_ == rawIterator.cont_);\n      size_type offset0 = ptr_ - cont_->begin();\n      size_type offset1 = rawIterator.ptr_ - cont_->begin();\n      if (offset1 == offset0) {\n        return 0;\n      }\n      return heap_vector_detail::distance(*cont_, offset1, offset0);\n    }\n\n    reference operator*() const { return *ptr_; }\n    pointer operator->() const {\n      if constexpr (std::is_pointer_v<Iter>) {\n        return ptr_;\n      } else {\n        return ptr_.operator->();\n      }\n    }\n\n   protected:\n    template <typename I2>\n    friend struct heap_iterator;\n\n    template <\n        typename T2,\n        typename Compare2,\n        typename Allocator2,\n        typename GrowthPolicy2,\n        typename Container2,\n        typename KeyT2,\n        typename ValueCompare2>\n    friend class heap_vector_container;\n\n    template <\n        typename Key2,\n        typename Value2,\n        typename Compare2,\n        typename Allocator2,\n        typename GrowthPolicy2,\n        typename Container2>\n    friend class ::folly::heap_vector_map;\n\n    Iter ptr_;\n    Container* cont_;\n  };\n\n  using iterator = heap_iterator<typename Container::iterator>;\n  using const_iterator = heap_iterator<typename Container::const_iterator>;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  heap_vector_container() : m_(value_compare(Compare()), Allocator()) {}\n\n  heap_vector_container(const heap_vector_container&) = default;\n\n  heap_vector_container(\n      const heap_vector_container& other, const Allocator& alloc)\n      : m_(other.m_, alloc) {}\n\n  heap_vector_container(heap_vector_container&&) = default;\n\n  heap_vector_container(\n      heap_vector_container&& other,\n      const Allocator&\n          alloc) noexcept(std::\n                              is_nothrow_constructible<\n                                  EBO,\n                                  EBO&&,\n                                  const Allocator&>::value)\n      : m_(std::move(other.m_), alloc) {}\n\n  explicit heap_vector_container(const Allocator& alloc)\n      : m_(value_compare(Compare()), alloc) {}\n\n  explicit heap_vector_container(\n      const Compare& comp, const Allocator& alloc = Allocator())\n      : m_(value_compare(comp), alloc) {}\n\n  template <class InputIterator>\n  explicit heap_vector_container(\n      InputIterator first,\n      InputIterator last,\n      const Compare& comp = Compare(),\n      const Allocator& alloc = Allocator())\n      : m_(value_compare(comp), alloc) {\n    insert(first, last);\n  }\n\n  template <class InputIterator>\n  heap_vector_container(\n      InputIterator first, InputIterator last, const Allocator& alloc)\n      : m_(value_compare(Compare()), alloc) {\n    insert(first, last);\n  }\n\n  /* implicit */ heap_vector_container(\n      std::initializer_list<value_type> list,\n      const Compare& comp = Compare(),\n      const Allocator& alloc = Allocator())\n      : m_(value_compare(comp), alloc) {\n    insert(list.begin(), list.end());\n  }\n\n  heap_vector_container(\n      std::initializer_list<value_type> list, const Allocator& alloc)\n      : m_(value_compare(Compare()), alloc) {\n    insert(list.begin(), list.end());\n  }\n\n  // Construct a heap_vector_container by stealing the storage of a prefilled\n  // container. The container need not be sorted already. This supports\n  // bulk construction of heap_vector_container with zero allocations, not\n  // counting those performed by the caller.\n  // Note that `heap_vector_container(const Container& container)` is not\n  // provided, since the purpose of this constructor is to avoid an unnecessary\n  // copy.\n  explicit heap_vector_container(\n      Container&& container,\n      const Compare& comp =\n          Compare()) noexcept(std::\n                                  is_nothrow_constructible<\n                                      EBO,\n                                      value_compare,\n                                      Container&&>::value)\n      : heap_vector_container(\n            sorted_unique,\n            heap_vector_detail::as_sorted_unique(\n                std::move(container), value_compare(comp)),\n            comp) {}\n\n  // Construct a heap_vector_container by stealing the storage of a prefilled\n  // container. Its elements must be sorted and unique, as sorted_unique_t\n  // hints. Supports bulk construction of heap_vector_container with zero\n  // allocations, not counting those performed by the caller.\n  // Note that `heap_vector_container(sorted_unique_t, const Container&\n  // container)` is not provided, since the purpose of this constructor is to\n  // avoid an extra copy.\n  heap_vector_container(\n      sorted_unique_t /* unused */,\n      Container&& container,\n      const Compare& comp =\n          Compare()) noexcept(std::\n                                  is_nothrow_constructible<\n                                      EBO,\n                                      value_compare,\n                                      Container&&>::value)\n      : m_(value_compare(comp), std::move(container)) {\n    assert(heap_vector_detail::is_sorted_unique(m_.cont_, value_comp()));\n    heap_vector_detail::heapify(m_.cont_);\n  }\n\n  Allocator get_allocator() const { return m_.cont_.get_allocator(); }\n\n  const Container& get_container() const noexcept { return m_.cont_; }\n\n  /**\n   * Directly swap the container. Similar to swap()\n   */\n  void swap_container(Container& newContainer) {\n    heap_vector_detail::as_sorted_unique(newContainer, value_comp());\n    heap_vector_detail::heapify(newContainer);\n    using std::swap;\n    swap(m_.cont_, newContainer);\n  }\n  void swap_container(sorted_unique_t, Container& newContainer) {\n    assert(heap_vector_detail::is_sorted_unique(newContainer, value_comp()));\n    heap_vector_detail::heapify(newContainer);\n    using std::swap;\n    swap(m_.cont_, newContainer);\n  }\n\n  heap_vector_container& operator=(const heap_vector_container& other) =\n      default;\n\n  heap_vector_container& operator=(heap_vector_container&& other) = default;\n\n  heap_vector_container& operator=(std::initializer_list<value_type> ilist) {\n    clear();\n    insert(ilist.begin(), ilist.end());\n    return *this;\n  }\n\n  key_compare key_comp() const { return m_; }\n  value_compare value_comp() const { return m_; }\n\n  iterator begin() {\n    if (size()) {\n      return iterator(\n          m_.cont_.begin() + heap_vector_detail::firstOffset(size()),\n          &m_.cont_);\n    }\n    return iterator(m_.cont_.begin(), &m_.cont_);\n  }\n  iterator end() { return iterator(m_.cont_.end(), &m_.cont_); }\n  const_iterator cbegin() const {\n    if (size()) {\n      return const_iterator(\n          m_.cont_.cbegin() + heap_vector_detail::firstOffset(size()),\n          &m_.cont_);\n    }\n    return const_iterator(m_.cont_.cbegin(), &m_.cont_);\n  }\n  const_iterator begin() const {\n    if (size()) {\n      return const_iterator(\n          m_.cont_.cbegin() + heap_vector_detail::firstOffset(size()),\n          &m_.cont_);\n    }\n    return const_iterator(m_.cont_.begin(), &m_.cont_);\n  }\n  const_iterator cend() const {\n    return const_iterator(m_.cont_.cend(), &m_.cont_);\n  }\n  const_iterator end() const {\n    return const_iterator(m_.cont_.end(), &m_.cont_);\n  }\n  reverse_iterator rbegin() { return reverse_iterator(end()); }\n  reverse_iterator rend() { return reverse_iterator(begin()); }\n  const_reverse_iterator rbegin() const {\n    return const_reverse_iterator(end());\n  }\n  const_reverse_iterator rend() const {\n    return const_reverse_iterator(begin());\n  }\n  const_reverse_iterator crbegin() const {\n    return const_reverse_iterator(end());\n  }\n  const_reverse_iterator crend() const {\n    return const_reverse_iterator(begin());\n  }\n\n  void clear() { return m_.cont_.clear(); }\n  size_type size() const { return m_.cont_.size(); }\n  size_type max_size() const { return m_.cont_.max_size(); }\n  bool empty() const { return m_.cont_.empty(); }\n  void reserve(size_type s) { return m_.cont_.reserve(s); }\n  void shrink_to_fit() { m_.cont_.shrink_to_fit(); }\n  size_type capacity() const { return m_.cont_.capacity(); }\n  const value_type* data() const noexcept { return m_.cont_.data(); }\n\n  std::pair<iterator, bool> insert(const value_type& value) {\n    iterator it = lower_bound(m_.getKey(value));\n    if (it == end() || value_comp()(value, *it)) {\n      auto offset = it.ptr_ - m_.cont_.begin();\n      get_growth_policy().increase_capacity(*this, it);\n      m_.cont_.push_back(value);\n      offset = heap_vector_detail::insert(offset, m_.cont_);\n      it = m_.cont_.begin() + offset;\n      return std::make_pair(it, true);\n    }\n    return std::make_pair(it, false);\n  }\n\n  std::pair<iterator, bool> insert(value_type&& value) {\n    iterator it = lower_bound(m_.getKey(value));\n    if (it == end() || value_comp()(value, *it)) {\n      auto offset = it.ptr_ - m_.cont_.begin();\n      get_growth_policy().increase_capacity(*this, it);\n      m_.cont_.push_back(std::move(value));\n      offset = heap_vector_detail::insert(offset, m_.cont_);\n      it = m_.cont_.begin() + offset;\n      return std::make_pair(it, true);\n    }\n    return std::make_pair(it, false);\n  }\n  /* There is no benefit of using hint. Keep it for compatibility\n   * Ignore and insert */\n  iterator insert(const_iterator /* hint */, const value_type& value) {\n    return insert(value).first;\n  }\n\n  iterator insert(const_iterator /* hint */, value_type&& value) {\n    return insert(std::move(value)).first;\n  }\n\n  template <class InputIterator>\n  void insert(InputIterator first, InputIterator last) {\n    if (first == last) {\n      return;\n    }\n    if (iterator_has_known_distance_v<InputIterator, InputIterator> &&\n        std::distance(first, last) == 1) {\n      insert(*first);\n      return;\n    }\n    std::sort(m_.cont_.begin(), m_.cont_.end(), value_comp());\n    heap_vector_detail::bulk_insert(*this, m_.cont_, first, last);\n  }\n\n  void insert(std::initializer_list<value_type> ilist) {\n    insert(ilist.begin(), ilist.end());\n  }\n  // emplace isn't better than insert for heap_vector_container, but aids\n  // compatibility\n  template <typename... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    folly::aligned_storage_for_t<value_type> b;\n    auto* p = static_cast<value_type*>(static_cast<void*>(&b));\n    auto a = get_allocator();\n    std::allocator_traits<allocator_type>::construct(\n        a, p, std::forward<Args>(args)...);\n    auto g = makeGuard([&]() {\n      std::allocator_traits<allocator_type>::destroy(a, p);\n    });\n    return insert(std::move(*p));\n  }\n\n  std::pair<iterator, bool> emplace(const value_type& value) {\n    return insert(value);\n  }\n\n  std::pair<iterator, bool> emplace(value_type&& value) {\n    return insert(std::move(value));\n  }\n\n  // emplace_hint isn't better than insert for heap_vector_container, but aids\n  // compatibility\n  template <typename... Args>\n  iterator emplace_hint(const_iterator /* hint */, Args&&... args) {\n    return emplace(std::forward<Args>(args)...).first;\n  }\n\n  iterator emplace_hint(const_iterator hint, const value_type& value) {\n    return insert(hint, value);\n  }\n\n  iterator emplace_hint(const_iterator hint, value_type&& value) {\n    return insert(hint, std::move(value));\n  }\n\n  size_type erase(const key_type& key) {\n    iterator it = find(key);\n    if (it == end()) {\n      return 0;\n    }\n    heap_vector_detail::erase(it.ptr_ - m_.cont_.begin(), m_.cont_);\n    return 1;\n  }\n\n  iterator erase(const_iterator it) {\n    auto offset =\n        heap_vector_detail::erase(it.ptr_ - m_.cont_.begin(), m_.cont_);\n    iterator ret = end();\n    ret = m_.cont_.begin() + offset;\n    return ret;\n  }\n\n  iterator erase(const_iterator first, const_iterator last) {\n    if (first == last) {\n      return end();\n    }\n    auto dist = last - first;\n    if (dist <= 0) {\n      return end();\n    }\n    if (dist == 1) {\n      return erase(first);\n    }\n    auto it = m_.cont_.begin() + (first - begin());\n    std::sort(m_.cont_.begin(), m_.cont_.end(), value_comp());\n    it = m_.cont_.erase(it, it + dist);\n    heap_vector_detail::heapify(m_.cont_);\n    return begin() + (it - m_.cont_.begin());\n  }\n\n  iterator find(const key_type& key) { return find_(*this, key); }\n\n  const_iterator find(const key_type& key) const { return find_(*this, key); }\n\n  template <typename K>\n  if_is_transparent<K, iterator> find(const K& key) {\n    return find_(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> find(const K& key) const {\n    return find_(*this, key);\n  }\n\n  size_type count(const key_type& key) const {\n    return find(key) == end() ? 0 : 1;\n  }\n\n  template <typename K>\n  if_is_transparent<K, size_type> count(const K& key) const {\n    return find(key) == end() ? 0 : 1;\n  }\n\n  bool contains(const key_type& key) const { return find(key) != end(); }\n\n  template <typename K>\n  if_is_transparent<K, bool> contains(const K& key) const {\n    return find(key) != end();\n  }\n\n  iterator lower_bound(const key_type& key) { return lower_bound(*this, key); }\n\n  const_iterator lower_bound(const key_type& key) const {\n    return lower_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, iterator> lower_bound(const K& key) {\n    return lower_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> lower_bound(const K& key) const {\n    return lower_bound(*this, key);\n  }\n\n  iterator upper_bound(const key_type& key) { return upper_bound(*this, key); }\n\n  const_iterator upper_bound(const key_type& key) const {\n    return upper_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, iterator> upper_bound(const K& key) {\n    return upper_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> upper_bound(const K& key) const {\n    return upper_bound(*this, key);\n  }\n\n  std::pair<iterator, iterator> equal_range(const key_type& key) {\n    return {lower_bound(key), upper_bound(key)};\n  }\n\n  std::pair<const_iterator, const_iterator> equal_range(\n      const key_type& key) const {\n    return {lower_bound(key), upper_bound(key)};\n  }\n\n  template <typename K>\n  if_is_transparent<K, std::pair<iterator, iterator>> equal_range(\n      const K& key) {\n    return {lower_bound(key), upper_bound(key)};\n  }\n\n  template <typename K>\n  if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range(\n      const K& key) const {\n    return {lower_bound(key), upper_bound(key)};\n  }\n\n  void swap(heap_vector_container& o) noexcept(\n      std::is_nothrow_swappable<Compare>::value &&\n      noexcept(std::declval<Container&>().swap(std::declval<Container&>()))) {\n    using std::swap; // Allow ADL for swap(); fall back to std::swap().\n    Compare& a = m_;\n    Compare& b = o.m_;\n    swap(a, b);\n    m_.cont_.swap(o.m_.cont_);\n  }\n\n  bool operator==(const heap_vector_container& other) const {\n    return m_.cont_ == other.m_.cont_;\n  }\n\n  bool operator!=(const heap_vector_container& other) const {\n    return !operator==(other);\n  }\n\n  bool operator<(const heap_vector_container& other) const {\n    return std::lexicographical_compare(\n        begin(), end(), other.begin(), other.end(), value_comp());\n  }\n  bool operator>(const heap_vector_container& other) const {\n    return other < *this;\n  }\n  bool operator<=(const heap_vector_container& other) const {\n    return !operator>(other);\n  }\n  bool operator>=(const heap_vector_container& other) const {\n    return !operator<(other);\n  }\n\n  // Use underlying vector iterators to quickly traverse heap container.\n  // Note elements are traversed following the heap order, i.e., memory\n  // storage order.\n  Range<typename Container::iterator> iterate() noexcept {\n    return Range<typename Container::iterator>(\n        m_.cont_.begin(), m_.cont_.end());\n  }\n\n  const Range<typename Container::const_iterator> iterate() const noexcept {\n    return Range<typename Container::const_iterator>(\n        m_.cont_.begin(), m_.cont_.end());\n  }\n\n protected:\n  // This is to get the empty base optimization\n  struct EBO : value_compare {\n    explicit EBO(const value_compare& c, const Allocator& alloc) noexcept(\n        std::is_nothrow_default_constructible<Container>::value)\n        : value_compare(c), cont_(alloc) {}\n    EBO(const EBO& other, const Allocator& alloc) noexcept(\n        std::is_nothrow_constructible<\n            Container,\n            const Container&,\n            const Allocator&>::value)\n        : value_compare(static_cast<const value_compare&>(other)),\n          cont_(other.cont_, alloc) {}\n    EBO(EBO&& other, const Allocator& alloc) noexcept(\n        std::is_nothrow_constructible<\n            Container,\n            Container&&,\n            const Allocator&>::value)\n        : value_compare(static_cast<value_compare&&>(other)),\n          cont_(std::move(other.cont_), alloc) {}\n    EBO(const Compare& c, Container&& cont) noexcept(\n        std::is_nothrow_move_constructible<Container>::value)\n        : value_compare(c), cont_(std::move(cont)) {}\n    Container cont_;\n  } m_;\n\n  template <typename Self>\n  using self_iterator_t = typename std::\n      conditional<std::is_const<Self>::value, const_iterator, iterator>::type;\n\n  template <typename Self, typename K>\n  static self_iterator_t<Self> find_(Self& self, K const& key) {\n    self_iterator_t<Self> end = self.end();\n    self_iterator_t<Self> it = self.lower_bound(key);\n    if (it == end || !self.key_comp()(key, self.m_.getKey(*it))) {\n      return it;\n    }\n    return end;\n  }\n\n  template <typename Self, typename K>\n  static self_iterator_t<Self> lower_bound(Self& self, K const& key) {\n    auto c = self.key_comp();\n    auto cmp = [&](auto const& a) { return c(self.m_.getKey(a), key); };\n    auto reverseCmp = [&](auto const& a) { return c(key, self.m_.getKey(a)); };\n    auto offset =\n        heap_vector_detail::lower_bound(self.m_.cont_, cmp, reverseCmp);\n    self_iterator_t<Self> ret = self.end();\n    ret = self.m_.cont_.begin() + offset;\n    return ret;\n  }\n\n  template <typename Self, typename K>\n  static self_iterator_t<Self> upper_bound(Self& self, K const& key) {\n    auto c = self.key_comp();\n    auto cmp = [&](auto const& a) { return c(key, self.m_.getKey(a)); };\n    auto offset = heap_vector_detail::upper_bound(self.m_.cont_, cmp);\n    self_iterator_t<Self> ret = self.end();\n    ret = self.m_.cont_.begin() + offset;\n    return ret;\n  }\n};\n\n} // namespace heap_vector_detail\n\n} // namespace detail\n\n/* heap_vector_set is a specialization of heap_vector_container\n *\n * @tparam T               Data type to store\n * @tparam Compare         Comparison function that imposes a\n *                              strict weak ordering over instances of T\n * @tparam Allocator       allocation policy\n * @tparam GrowthPolicy    policy object to control growth\n * @tparam Container       underlying vector where elements are stored\n */\ntemplate <\n    class T,\n    class Compare = std::less<T>,\n    class Allocator = std::allocator<T>,\n    class GrowthPolicy = void,\n    class Container = std::vector<T, Allocator>>\nclass heap_vector_set\n    : public detail::heap_vector_detail::heap_vector_container<\n          T,\n          Compare,\n          Allocator,\n          GrowthPolicy,\n          Container,\n          T,\n          detail::heap_vector_detail::value_compare_set<Compare>> {\n private:\n  using heap_vector_container =\n      detail::heap_vector_detail::heap_vector_container<\n          T,\n          Compare,\n          Allocator,\n          GrowthPolicy,\n          Container,\n          T,\n          detail::heap_vector_detail::value_compare_set<Compare>>;\n\n public:\n  using heap_vector_container::heap_vector_container;\n};\n\n// Swap function that can be found using ADL.\ntemplate <class T, class C, class A, class G>\ninline void swap(\n    heap_vector_set<T, C, A, G>& a, heap_vector_set<T, C, A, G>& b) noexcept {\n  return a.swap(b);\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\n\nnamespace pmr {\n\ntemplate <\n    class T,\n    class Compare = std::less<T>,\n    class GrowthPolicy = void,\n    class Container = std::vector<T, std::pmr::polymorphic_allocator<T>>>\nusing heap_vector_set = folly::heap_vector_set<\n    T,\n    Compare,\n    std::pmr::polymorphic_allocator<T>,\n    GrowthPolicy,\n    Container>;\n\n} // namespace pmr\n\n#endif\n\n//////////////////////////////////////////////////////////////////////\n\n/**\n * A heap_vector_map based on heap layout.\n *\n * @tparam Key           Key type\n * @tparam Value         Value type\n * @tparam Compare       Function that can compare key types and impose\n *                            a strict weak ordering over them.\n * @tparam Allocator     allocation policy\n * @tparam GrowthPolicy  policy object to control growth\n *\n */\n\ntemplate <\n    class Key,\n    class Value,\n    class Compare = std::less<Key>,\n    class Allocator = std::allocator<std::pair<Key, Value>>,\n    class GrowthPolicy = void,\n    class Container = std::vector<std::pair<Key, Value>, Allocator>>\nclass heap_vector_map\n    : public detail::heap_vector_detail::heap_vector_container<\n          typename Container::value_type,\n          Compare,\n          Allocator,\n          GrowthPolicy,\n          Container,\n          Key,\n          detail::heap_vector_detail::value_compare_map<Compare>> {\n public:\n  using key_type = Key;\n  using mapped_type = Value;\n  using value_type = typename Container::value_type;\n  using key_compare = Compare;\n  using allocator_type = Allocator;\n  using container_type = Container;\n  using pointer = typename Container::pointer;\n  using reference = typename Container::reference;\n  using const_reference = typename Container::const_reference;\n  using difference_type = typename Container::difference_type;\n  using size_type = typename Container::size_type;\n  using value_compare = detail::heap_vector_detail::value_compare_map<Compare>;\n\n protected:\n  using heap_vector_container =\n      detail::heap_vector_detail::heap_vector_container<\n          value_type,\n          key_compare,\n          Allocator,\n          GrowthPolicy,\n          Container,\n          key_type,\n          value_compare>;\n  using heap_vector_container::get_growth_policy;\n  using heap_vector_container::m_;\n\n public:\n  using iterator = typename heap_vector_container::iterator;\n  using const_iterator = typename heap_vector_container::const_iterator;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  // Since heap_vector_container methods are publicly available through\n  // inheritance, just expose method used within this class.\n  using heap_vector_container::end;\n  using heap_vector_container::find;\n  using heap_vector_container::heap_vector_container;\n  using heap_vector_container::key_comp;\n  using heap_vector_container::lower_bound;\n\n  mapped_type& at(const key_type& key) {\n    iterator it = find(key);\n    if (it != end()) {\n      return it->second;\n    }\n    throw_exception<std::out_of_range>(\"heap_vector_map::at\");\n  }\n\n  const mapped_type& at(const key_type& key) const {\n    const_iterator it = find(key);\n    if (it != end()) {\n      return it->second;\n    }\n    throw_exception<std::out_of_range>(\"heap_vector_map::at\");\n  }\n\n  mapped_type& operator[](const key_type& key) {\n    iterator it = lower_bound(key);\n    if (it == end() || key_comp()(key, it->first)) {\n      auto offset = it.ptr_ - m_.cont_.begin();\n      get_growth_policy().increase_capacity(*this, it);\n      m_.cont_.emplace_back(key, mapped_type());\n      offset = detail::heap_vector_detail::insert(offset, m_.cont_);\n      it = m_.cont_.begin() + offset;\n      return it->second;\n    }\n    return it->second;\n  }\n};\n\n// Swap function that can be found using ADL.\ntemplate <class K, class V, class C, class A, class G>\ninline void swap(\n    heap_vector_map<K, V, C, A, G>& a,\n    heap_vector_map<K, V, C, A, G>& b) noexcept {\n  return a.swap(b);\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\n\nnamespace pmr {\n\ntemplate <\n    class Key,\n    class Value,\n    class Compare = std::less<Key>,\n    class GrowthPolicy = void,\n    class Container = std::vector<\n        std::pair<Key, Value>,\n        std::pmr::polymorphic_allocator<std::pair<Key, Value>>>>\nusing heap_vector_map = folly::heap_vector_map<\n    Key,\n    Value,\n    Compare,\n    std::pmr::polymorphic_allocator<std::pair<Key, Value>>,\n    GrowthPolicy,\n    Container>;\n\n} // namespace pmr\n\n#endif\n\n// Specialize heap_vector_map to integral key type and std::less comparaison.\n// small_heap_map achieve a very fast find for small map < 200 elements.\ntemplate <\n    typename Key,\n    typename Value,\n    typename SizeType = uint32_t,\n    class Container = folly::small_vector<\n        std::pair<Key, Value>,\n        0,\n        folly::small_vector_policy::policy_size_type<SizeType>>,\n    typename = std::enable_if_t<\n        std::is_integral<Key>::value || std::is_enum<Key>::value>>\nclass small_heap_vector_map\n    : public folly::heap_vector_map<\n          Key,\n          Value,\n          std::less<Key>,\n          typename Container::allocator_type,\n          void,\n          Container> {\n public:\n  using key_type = Key;\n  using mapped_type = Value;\n  using value_type = typename Container::value_type;\n  using key_compare = std::less<Key>;\n  using allocator_type = typename Container::allocator_type;\n  using container_type = Container;\n  using pointer = typename Container::pointer;\n  using reference = typename Container::reference;\n  using const_reference = typename Container::const_reference;\n  using difference_type = typename Container::difference_type;\n  using size_type = typename Container::size_type;\n  using value_compare =\n      detail::heap_vector_detail::value_compare_map<std::less<Key>>;\n\n private:\n  using heap_vector_map = folly::\n      heap_vector_map<Key, Value, key_compare, allocator_type, void, Container>;\n\n  using heap_vector_map::m_;\n\n public:\n  using iterator = typename heap_vector_map::iterator;\n  using const_iterator = typename heap_vector_map::const_iterator;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  using heap_vector_map::begin;\n  using heap_vector_map::heap_vector_map;\n  using heap_vector_map::size;\n  iterator find(const key_type key) {\n    auto offset = find_(*this, key);\n    iterator ret = begin();\n    ret = m_.cont_.begin() + offset;\n    return ret;\n  }\n\n  const_iterator find(const key_type key) const {\n    auto offset = find_(*this, key);\n    const_iterator ret = begin();\n    ret = m_.cont_.begin() + offset;\n    return ret;\n  }\n\n private:\n  template <typename Self, typename K>\n  static inline size_type find_(Self& self, K const key) {\n    auto size = self.size();\n    if (!size) {\n      return 0;\n    }\n    auto& cont = self.m_.cont_;\n    size_type offset = 1;\n    auto cur_k = self.m_.getKey(cont[0]);\n    for (int i = 0; i < 6; i++) {\n      auto o = offset;\n      offset = 2 * offset;\n      if (cur_k <= key) {\n        ++offset;\n        if (cur_k == key) {\n          return o - 1;\n        }\n      }\n      if (offset > size) {\n        return size;\n      }\n      cur_k = self.m_.getKey(cont[offset - 1]);\n    }\n    while (true) {\n      auto lt = cur_k < key;\n      if (cur_k == key) {\n        return offset - 1;\n      }\n      offset = 2 * offset + lt;\n      if (offset > size) {\n        return size;\n      }\n      cur_k = self.m_.getKey(cont[offset - 1]);\n    }\n    return size;\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/irange.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <concepts>\n#include <ranges>\n#include <type_traits>\n\nnamespace folly {\n\nnamespace detail {\ntemplate <typename T>\nconcept IRangeNonBoolIntegral = std::integral<T> && !std::same_as<T, bool>;\n}\n\n/// Creates an integer range for the half-open interval [begin, end)\n/// If end<=begin, then the range is empty.\n/// The range has the type of the `end` integer; `begin` integer is\n/// cast to this type.\n///\n/// Instead of\n///   for (int i = 3; i < 10; ++i) {\n/// use\n///   for (const auto i : irange(3, 10)) {\ntemplate <\n    detail::IRangeNonBoolIntegral Integer1,\n    detail::IRangeNonBoolIntegral Integer2>\nconstexpr std::ranges::iota_view<\n    std::common_type_t<Integer1, Integer2>,\n    std::common_type_t<Integer1, Integer2>>\nirange(Integer1 begin, Integer2 end) {\n  // If end<=begin then the range is empty; we can achieve this effect by\n  // choosing the larger of {begin, end} as the loop terminator\n  using otype = std::common_type_t<Integer1, Integer2>;\n  return {\n      static_cast<otype>(begin),\n      std::max(static_cast<otype>(begin), static_cast<otype>(end))};\n}\n\n/// Creates an integer range for the half-open interval [0, end)\n///\n/// Instead of\n///   for (int i = 0; i < 10; ++i) {\n/// use\n///   for (const auto i : irange(10)) {\n///\n/// NOTE! This behaviour differs from `iota_view(N)` which iterates\n/// starts at `N` and loops over the full range of valus of `N`'s data\n/// type forever.\ntemplate <detail::IRangeNonBoolIntegral Integer>\nconstexpr std::ranges::iota_view<Integer, Integer> irange(Integer end) {\n  return {Integer(), end};\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/range_traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n\n#if defined(__cpp_lib_ranges)\n#include <ranges>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\n// clang-format off\ntemplate <\n    typename R,\n    typename S = typename R::size_type,\n    typename V = typename R::value_type,\n    typename TD = decltype(FOLLY_DECLVAL(R&).data()),\n    typename TCD = decltype(FOLLY_DECLVAL(R const&).data()),\n    typename TCS = decltype(FOLLY_DECLVAL(R const&).size())>\nusing is_contiguous_range_fallback_impl_ = std::bool_constant<(true\n    && (std::is_same_v<V*, TD> || std::is_same_v<V const*, TD>)\n    && std::is_same_v<V const*, TCD>\n    && std::is_same_v<S, TCS>)>;\n// clang-format on\n\ntemplate <\n    typename R,\n    bool RUser = std::is_union_v<R> || std::is_class_v<R>>\nconstexpr bool is_contiguous_range_v_ = //\n    !require_sizeof<conditional_t<RUser, R, int>>\n#if defined(__cpp_lib_ranges)\n    || std::ranges::contiguous_range<R>\n#endif\n    || is_bounded_array_v<R> //\n    || detected_or_t<std::false_type, is_contiguous_range_fallback_impl_, R>{};\n\n} // namespace detail\n\n//  is_contiguous_range_v\n//\n//  True when any of:\n//  * std::ranges::contiguous_range holds, if available\n//  * is_bounded_array_v holds\n//  * certain conditions using member types or type aliases size_type and\n//    value_type and member functions size() and data()\n//  Otherwise false.\n//\n//  Necessarily true if the given type is any of:\n//  * T[S], ie a bounded array\n//  * std::array\n//  * std::basic_string\n//  * std::basic_string_view\n//  * std::span\n//  * std::vector\n//\n//  Rejects incomplete class/union types, even if they would be accepted when\n//  completed.\ntemplate <typename R>\nconstexpr bool is_contiguous_range_v = detail::is_contiguous_range_v_<R>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/small_vector.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * For high-level documentation and usage examples see\n * folly/docs/small_vector.md\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cassert>\n#include <cstdlib>\n#include <cstring>\n#include <iterator>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include <boost/operators.hpp>\n\n#include <folly/ConstexprMath.h>\n#include <folly/FormatTraits.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/hash/Hash.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/CheckedMath.h>\n#include <folly/lang/Exception.h>\n#include <folly/memory/Malloc.h>\n#include <folly/memory/SanitizeLeak.h>\n#include <folly/portability/Malloc.h>\n\n#if (FOLLY_X64 || FOLLY_PPC64 || FOLLY_AARCH64 || FOLLY_RISCV64)\n#define FOLLY_SV_PACK_ATTR FOLLY_PACK_ATTR\n#define FOLLY_SV_PACK_PUSH FOLLY_PACK_PUSH\n#define FOLLY_SV_PACK_POP FOLLY_PACK_POP\n#else\n#define FOLLY_SV_PACK_ATTR\n#define FOLLY_SV_PACK_PUSH\n#define FOLLY_SV_PACK_POP\n#endif\n\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n\nnamespace folly {\n\nnamespace small_vector_policy {\n\nnamespace detail {\n\nstruct item_size_type {\n  template <typename T>\n  using get = typename T::size_type;\n  template <typename T>\n  struct set {\n    using size_type = T;\n  };\n};\n\nstruct item_in_situ_only {\n  template <typename T>\n  using get = typename T::in_situ_only;\n  template <typename T>\n  struct set {\n    using in_situ_only = T;\n  };\n};\n\ntemplate <template <typename> class F, typename... T>\nconstexpr size_t last_matching_() {\n  bool const values[] = {is_detected_v<F, T>..., false};\n  for (size_t i = 0; i < sizeof...(T); ++i) {\n    auto const j = sizeof...(T) - 1 - i;\n    if (values[j]) {\n      return j;\n    }\n  }\n  return sizeof...(T);\n}\n\ntemplate <size_t M, typename I, typename... P>\nstruct merge_ //\n    : I::template set<typename I::template get<\n          type_pack_element_t<sizeof...(P) - M, P...>>> {};\ntemplate <typename I, typename... P>\nstruct merge_<0, I, P...> {};\ntemplate <typename I, typename... P>\nusing merge =\n    merge_<sizeof...(P) - last_matching_<I::template get, P...>(), I, P...>;\n\n} // namespace detail\n\ntemplate <typename... Policy>\nstruct merge //\n    : detail::merge<detail::item_size_type, Policy...>,\n      detail::merge<detail::item_in_situ_only, Policy...> {};\n\ntemplate <typename SizeType>\nstruct policy_size_type {\n  using size_type = SizeType;\n};\n\ntemplate <bool Value>\nstruct policy_in_situ_only {\n  using in_situ_only = std::bool_constant<Value>;\n};\n\n} // namespace small_vector_policy\n\n//////////////////////////////////////////////////////////////////////\n\ntemplate <class T, std::size_t M, class P>\nclass small_vector;\n\n//////////////////////////////////////////////////////////////////////\n\nnamespace detail {\n\nnamespace small_vector_detail {\n\n/*\n * Just because a type is trivially copyable doesn't mean we should copy\n * it. A copy constructor that is deleted is still considered trivial.\n *\n * If a type is not move constructible, it shouldn't be relocated. We\n * should avoid resizing in general.\n */\ntemplate <typename T>\ninline constexpr bool should_trivially_copy =\n    std::is_trivially_copyable_v<T> && std::is_move_constructible_v<T>;\n\n/*\n * Move objects in memory to the right into some uninitialized memory, where\n * the region overlaps. Then call create() for each hole in reverse order.\n *\n * This doesn't just use std::move_backward because move_backward only works\n * if all the memory is initialized to type T already.\n *\n * The create function should return a reference type, to avoid\n * extra copies and moves for non-trivial types.\n */\ntemplate <class T, class Create>\ntypename std::enable_if<!should_trivially_copy<T>>::type\nmoveObjectsRightAndCreate(\n    T* const first,\n    T* const lastConstructed,\n    T* const realLast,\n    Create&& create) {\n  if (lastConstructed == realLast) {\n    return;\n  }\n\n  T* out = realLast;\n  T* in = lastConstructed;\n  {\n    auto rollback = makeGuard([&] {\n      // We want to make sure the same stuff is uninitialized memory\n      // if we exit via an exception (this is to make sure we provide\n      // the basic exception safety guarantee for insert functions).\n      if (out < lastConstructed) {\n        out = lastConstructed - 1;\n      }\n      std::destroy(out + 1, realLast);\n    });\n    // Decrement the pointers only when it is known that the resulting pointer\n    // is within the boundaries of the object. Decrementing past the beginning\n    // of the object is UB. Note that this is asymmetric wrt forward iteration,\n    // as past-the-end pointers are explicitly allowed.\n    for (; in != first && out > lastConstructed;) {\n      // Out must be decremented before an exception can be thrown so that\n      // the rollback guard knows where to start.\n      --out;\n      new (out) T(std::move(*(--in)));\n    }\n    for (; in != first;) {\n      --out;\n      *out = std::move(*(--in));\n    }\n    for (; out > lastConstructed;) {\n      --out;\n      new (out) T(create());\n    }\n    for (; out != first;) {\n      --out;\n      *out = create();\n    }\n    rollback.dismiss();\n  }\n}\n\n// Specialization for trivially copyable types.  The call to\n// std::move_backward here will just turn into a memmove.\n// This must only be used with trivially copyable types because some of the\n// memory may be uninitialized, and std::move_backward() won't work when it\n// can't memmove().\ntemplate <class T, class Create>\ntypename std::enable_if<should_trivially_copy<T>>::type\nmoveObjectsRightAndCreate(\n    T* const first,\n    T* const lastConstructed,\n    T* const realLast,\n    Create&& create) {\n  std::move_backward(first, lastConstructed, realLast);\n  T* const end = first - 1;\n  T* out = first + (realLast - lastConstructed) - 1;\n  for (; out != end; --out) {\n    *out = create();\n  }\n}\n\n/*\n * Populate a region of memory using `op' to construct elements.  If\n * anything throws, undo what we did.\n */\ntemplate <class T, class Function>\nvoid populateMemForward(T* mem, std::size_t n, Function const& op) {\n  std::size_t idx = 0;\n  {\n    auto rollback = makeGuard([&] { std::destroy_n(mem, idx); });\n    for (size_t i = 0; i < n; ++i) {\n      op(&mem[idx]);\n      ++idx;\n    }\n    rollback.dismiss();\n  }\n}\n\n/*\n * Copies `fromSize` elements from `from' to `to', where `to' is only\n * initialized up to `toSize`, but has enough storage for `fromSize'. If\n * `toSize' > `fromSize', the extra elements are destructed.\n */\ntemplate <class Iterator1, class Iterator2>\nvoid partiallyUninitializedCopy(\n    Iterator1 from, size_t fromSize, Iterator2 to, size_t toSize) {\n  const size_t minSize = std::min(fromSize, toSize);\n  std::copy(from, from + minSize, to);\n  if (fromSize > toSize) {\n    std::uninitialized_copy(from + minSize, from + fromSize, to + minSize);\n  } else {\n    std::destroy(to + minSize, to + toSize);\n  }\n}\n\n} // namespace small_vector_detail\n\ntemplate <class SizeType, bool ShouldUseHeap, bool AlwaysUseHeap>\nstruct IntegralSizePolicyBase {\n  using InternalSizeType = SizeType;\n\n  IntegralSizePolicyBase() : size_(0) {}\n\n protected:\n  static constexpr std::size_t policyMaxSize() { return SizeType(~kClearMask); }\n\n  std::size_t doSize() const {\n    return AlwaysUseHeap ? size_ : size_ & ~kClearMask;\n  }\n\n  std::size_t isExtern() const { return AlwaysUseHeap || kExternMask & size_; }\n\n  void setExtern(bool b) {\n    if (AlwaysUseHeap) {\n      return;\n    }\n    if (b) {\n      size_ |= kExternMask;\n    } else {\n      size_ &= ~kExternMask;\n    }\n  }\n\n  std::size_t isHeapifiedCapacity() const {\n    return AlwaysUseHeap || kCapacityMask & size_;\n  }\n\n  void setHeapifiedCapacity(bool b) {\n    if (AlwaysUseHeap) {\n      return;\n    }\n    if (b) {\n      size_ |= kCapacityMask;\n    } else {\n      size_ &= ~kCapacityMask;\n    }\n  }\n  void setSize(std::size_t sz) {\n    assert(sz <= policyMaxSize());\n    size_ = AlwaysUseHeap ? sz : (kClearMask & size_) | SizeType(sz);\n  }\n\n  void incrementSize(std::size_t n) {\n    // We can safely increment size without overflowing into mask bits because\n    // we always check new size is less than maxPolicySize (see\n    // makeSizeInternal). To be sure, added assertion to verify it.\n    assert(doSize() + n <= policyMaxSize());\n    size_ += SizeType(n);\n  }\n  std::size_t getInternalSize() { return size_; }\n\n  void swapSizePolicy(IntegralSizePolicyBase& o) { std::swap(size_, o.size_); }\n\n  void resetSizePolicy() { size_ = 0; }\n\n protected:\n  static bool constexpr kShouldUseHeap = ShouldUseHeap || AlwaysUseHeap;\n  static bool constexpr kAlwaysUseHeap = AlwaysUseHeap;\n\n private:\n  // We reserve two most significant bits of size_.\n  static SizeType constexpr kExternMask =\n      kShouldUseHeap ? SizeType(1) << (sizeof(SizeType) * 8 - 1) : 0;\n\n  static SizeType constexpr kCapacityMask =\n      kShouldUseHeap ? SizeType(1) << (sizeof(SizeType) * 8 - 2) : 0;\n\n  static SizeType constexpr kClearMask =\n      kShouldUseHeap ? SizeType(3) << (sizeof(SizeType) * 8 - 2) : 0;\n\n  SizeType size_;\n};\n\ntemplate <class SizeType, bool ShouldUseHeap, bool AlwaysUseHeap>\nstruct IntegralSizePolicy;\n\ntemplate <class SizeType, bool AlwaysUseHeap>\nstruct IntegralSizePolicy<SizeType, true, AlwaysUseHeap>\n    : public IntegralSizePolicyBase<SizeType, true, AlwaysUseHeap> {\n public:\n  /*\n   * Move a range to a range of uninitialized memory.  Assumes the\n   * ranges don't overlap.\n   */\n  template <class T>\n  typename std::enable_if<\n      !detail::small_vector_detail::should_trivially_copy<T>>::type\n  moveToUninitialized(T* first, T* last, T* out) {\n    std::size_t idx = 0;\n    {\n      auto rollback = makeGuard([&] {\n        // Even for callers trying to give the strong guarantee\n        // (e.g. push_back) it's ok to assume here that we don't have to\n        // move things back and that it was a copy constructor that\n        // threw: if someone throws from a move constructor the effects\n        // are unspecified.\n        std::destroy_n(out, idx);\n      });\n      for (; first != last; ++first, ++idx) {\n        new (&out[idx]) T(std::move(*first));\n      }\n      rollback.dismiss();\n    }\n  }\n\n  // Specialization for trivially copyable types.\n  template <class T>\n  typename std::enable_if<\n      detail::small_vector_detail::should_trivially_copy<T>>::type\n  moveToUninitialized(T* first, T* last, T* out) {\n    std::memmove(\n        static_cast<void*>(out),\n        static_cast<void const*>(first),\n        (last - first) * sizeof *first);\n  }\n\n  /*\n   * Move a range to a range of uninitialized memory. Assumes the\n   * ranges don't overlap. Inserts an element at out + pos using\n   * emplaceFunc(). out will contain (end - begin) + 1 elements on success and\n   * none on failure. If emplaceFunc() throws [begin, end) is unmodified.\n   */\n  template <class T, class EmplaceFunc>\n  void moveToUninitializedEmplace(\n      T* begin, T* end, T* out, SizeType pos, EmplaceFunc&& emplaceFunc) {\n    // Must be called first so that if it throws [begin, end) is unmodified.\n    // We have to support the strong exception guarantee for emplace_back().\n    emplaceFunc(out + pos);\n    // move old elements to the left of the new one\n    FOLLY_PUSH_WARNING\n    FOLLY_MSVC_DISABLE_WARNING(4702) {\n      auto rollback = makeGuard([&] { //\n        std::destroy_at(out + pos);\n      });\n      if (begin) {\n        this->moveToUninitialized(begin, begin + pos, out);\n      }\n      rollback.dismiss();\n    }\n    // move old elements to the right of the new one\n    {\n      auto rollback = makeGuard([&] { std::destroy_n(out, pos + 1); });\n      if (begin + pos < end) {\n        this->moveToUninitialized(begin + pos, end, out + pos + 1);\n      }\n      rollback.dismiss();\n    }\n    FOLLY_POP_WARNING\n  }\n};\n\ntemplate <class SizeType, bool AlwaysUseHeap>\nstruct IntegralSizePolicy<SizeType, false, AlwaysUseHeap>\n    : public IntegralSizePolicyBase<SizeType, false, AlwaysUseHeap> {\n public:\n  template <class T>\n  void moveToUninitialized(T* /*first*/, T* /*last*/, T* /*out*/) {\n    assume_unreachable();\n  }\n  template <class T, class EmplaceFunc>\n  void moveToUninitializedEmplace(\n      T* /* begin */,\n      T* /* end */,\n      T* /* out */,\n      SizeType /* pos */,\n      EmplaceFunc&& /* emplaceFunc */) {\n    assume_unreachable();\n  }\n};\n\n/*\n * If you're just trying to use this class, ignore everything about\n * this next small_vector_base class thing.\n *\n * The purpose of this junk is to minimize sizeof(small_vector<>)\n * and allow specifying the template parameters in whatever order is\n * convenient for the user.  There's a few extra steps here to try\n * to keep the error messages at least semi-reasonable.\n *\n * Apologies for all the black magic.\n */\ntemplate <class Value, std::size_t RequestedMaxInline, class InPolicy>\nstruct small_vector_base {\n  static_assert(!std::is_integral<InPolicy>::value, \"legacy\");\n  using Policy = small_vector_policy::merge<\n      small_vector_policy::policy_size_type<size_t>,\n      small_vector_policy::policy_in_situ_only<false>,\n      conditional_t<std::is_void<InPolicy>::value, tag_t<>, InPolicy>>;\n\n  /*\n   * Make the real policy base classes.\n   */\n  using ActualSizePolicy = IntegralSizePolicy<\n      typename Policy::size_type,\n      !Policy::in_situ_only::value,\n      RequestedMaxInline == 0>;\n\n  /*\n   * Now inherit from them all.  This is done in such a convoluted\n   * way to make sure we get the empty base optimization on all these\n   * types to keep sizeof(small_vector<>) minimal.\n   */\n  using type = boost::totally_ordered1<\n      small_vector<Value, RequestedMaxInline, InPolicy>,\n      ActualSizePolicy>;\n};\n\nnamespace small_vector_detail {\n\ninline void* unshiftPointer(void* p, size_t sizeBytes) {\n  return static_cast<char*>(p) - sizeBytes;\n}\n\n} // namespace small_vector_detail\n\nnamespace small_vector_detail {\n\ninline void* shiftPointer(void* p, size_t sizeBytes) {\n  return static_cast<char*>(p) + sizeBytes;\n}\n\n} // namespace small_vector_detail\n\n// No backward compatibility using declarations needed\n} // namespace detail\n\n//////////////////////////////////////////////////////////////////////\ntemplate <class Value, std::size_t RequestedMaxInline = 1, class Policy = void>\nclass small_vector\n    : public detail::small_vector_base<Value, RequestedMaxInline, Policy>::\n          type {\n  using BaseType = typename detail::\n      small_vector_base<Value, RequestedMaxInline, Policy>::type;\n  using InternalSizeType = typename BaseType::InternalSizeType;\n\n  /*\n   * Figure out the max number of elements we should inline.  (If\n   * the user asks for less inlined elements than we can fit unioned\n   * into our value_type*, we will inline more than they asked.)\n   */\n  static constexpr auto kSizeOfValuePtr = sizeof(Value*);\n  static constexpr auto kSizeOfValue = sizeof(Value);\n  static constexpr std::size_t MaxInline{\n      RequestedMaxInline == 0\n          ? 0\n          : constexpr_max(kSizeOfValuePtr / kSizeOfValue, RequestedMaxInline)};\n\n public:\n  using size_type = std::size_t;\n  using value_type = Value;\n  using allocator_type = std::allocator<Value>;\n  using reference = value_type&;\n  using const_reference = value_type const&;\n  using iterator = value_type*;\n  using pointer = value_type*;\n  using const_iterator = value_type const*;\n  using const_pointer = value_type const*;\n  using difference_type = std::ptrdiff_t;\n\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  small_vector() = default;\n  // Allocator is unused here. It is taken in for compatibility with std::vector\n  // interface, but it will be ignored.\n  small_vector(const std::allocator<Value>&) {}\n\n  small_vector(small_vector const& o) {\n    if constexpr (kShouldCopyWholeInlineStorageTrivial) {\n      if (!o.isExtern()) {\n        copyWholeInlineStorageTrivial(o);\n        return;\n      }\n    }\n\n    auto n = o.size();\n    makeSize</* IgnoreExistingData */ true>(n);\n    {\n      auto rollback = makeGuard([&] { freeHeap(); });\n      std::uninitialized_copy(o.begin(), o.begin() + n, begin());\n      rollback.dismiss();\n    }\n    this->setSize(n);\n  }\n\n  small_vector(small_vector&& o) noexcept(\n      std::is_nothrow_move_constructible<Value>::value) {\n    if (o.isExtern()) {\n      this->u.pdata_.heap_ = o.u.pdata_.heap_;\n      o.u.pdata_.heap_ = nullptr;\n      this->swapSizePolicy(o);\n      if (kHasInlineCapacity) {\n        this->u.setCapacity(o.u.getCapacity());\n      }\n    } else {\n      if constexpr (kShouldCopyWholeInlineStorageTrivial) {\n        copyWholeInlineStorageTrivial(o);\n        o.resetSizePolicy();\n      } else if constexpr (IsRelocatable<Value>::value) {\n        moveInlineStorageRelocatable(std::move(o));\n      } else {\n        auto n = o.size();\n        std::uninitialized_copy(\n            std::make_move_iterator(o.begin()),\n            std::make_move_iterator(o.end()),\n            begin());\n        this->setSize(n);\n        o.clear();\n      }\n    }\n  }\n\n  small_vector(std::initializer_list<value_type> il) {\n    constructImpl(il.begin(), il.end(), std::false_type());\n  }\n\n  explicit small_vector(size_type n) {\n    FOLLY_PUSH_WARNING\n    FOLLY_GCC_DISABLE_WARNING(\"-Warray-bounds\")\n    doConstruct(n, [&](void* p) { new (p) value_type(); });\n    FOLLY_POP_WARNING\n  }\n\n  small_vector(size_type n, value_type const& t) {\n    FOLLY_PUSH_WARNING\n    FOLLY_GCC_DISABLE_WARNING(\"-Warray-bounds\")\n    doConstruct(n, [&](void* p) { new (p) value_type(t); });\n    FOLLY_POP_WARNING\n  }\n\n  template <class Arg>\n  explicit small_vector(Arg arg1, Arg arg2) {\n    // Forward using std::is_arithmetic to get to the proper\n    // implementation; this disambiguates between the iterators and\n    // (size_t, value_type) meaning for this constructor.\n    constructImpl(arg1, arg2, std::is_arithmetic<Arg>());\n  }\n\n  ~small_vector() { destroy(); }\n\n  small_vector& operator=(small_vector const& o) {\n    if (FOLLY_LIKELY(this != &o)) {\n      if constexpr (kShouldCopyWholeInlineStorageTrivial) {\n        if (!this->isExtern() && !o.isExtern()) {\n          copyWholeInlineStorageTrivial(o);\n          return *this;\n        }\n      }\n      if (o.size() < capacity()) {\n        const size_t oSize = o.size();\n        detail::small_vector_detail::partiallyUninitializedCopy(\n            o.begin(), oSize, begin(), size());\n        this->setSize(oSize);\n      } else {\n        assign(o.begin(), o.end());\n      }\n    }\n    return *this;\n  }\n\n  small_vector& operator=(small_vector&& o) noexcept(\n      std::is_nothrow_move_constructible<Value>::value) {\n    if (FOLLY_LIKELY(this != &o)) {\n      // If either is external, reduce to the default-constructed case for this,\n      // since there is nothing that we can move in-place.\n      if (this->isExtern() || o.isExtern()) {\n        reset();\n      }\n\n      if (!o.isExtern()) {\n        if constexpr (kShouldCopyWholeInlineStorageTrivial) {\n          copyWholeInlineStorageTrivial(o);\n          o.resetSizePolicy();\n        } else if constexpr (IsRelocatable<Value>::value) {\n          std::destroy_n(u.buffer(), size());\n          moveInlineStorageRelocatable(std::move(o));\n        } else {\n          const size_t oSize = o.size();\n          detail::small_vector_detail::partiallyUninitializedCopy(\n              std::make_move_iterator(o.u.buffer()),\n              oSize,\n              this->u.buffer(),\n              size());\n          this->setSize(oSize);\n          o.clear();\n        }\n      } else {\n        this->u.pdata_.heap_ = o.u.pdata_.heap_;\n        o.u.pdata_.heap_ = nullptr;\n        // this was already reset above, so it's empty and internal.\n        this->swapSizePolicy(o);\n        if (kHasInlineCapacity) {\n          this->u.setCapacity(o.u.getCapacity());\n        }\n      }\n    }\n    return *this;\n  }\n\n  bool operator==(small_vector const& o) const {\n    return size() == o.size() && std::equal(begin(), end(), o.begin());\n  }\n\n  bool operator<(small_vector const& o) const {\n    return std::lexicographical_compare(begin(), end(), o.begin(), o.end());\n  }\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n  template <typename U = value_type>\n  friend auto operator<=>(const small_vector& lhs, const small_vector& rhs)\n      -> decltype(std::declval<const U&>() <=> std::declval<const U&>()) {\n    return std::lexicographical_compare_three_way(\n        lhs.begin(), lhs.end(), rhs.begin(), rhs.end());\n  }\n#endif // FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n\n  static constexpr size_type max_size() {\n    return !BaseType::kShouldUseHeap\n        ? static_cast<size_type>(MaxInline)\n        : BaseType::policyMaxSize();\n  }\n\n  allocator_type get_allocator() const { return {}; }\n\n  size_type size() const { return this->doSize(); }\n  bool empty() const { return !size(); }\n\n  iterator begin() { return data(); }\n  iterator end() { return data() + size(); }\n  const_iterator begin() const { return data(); }\n  const_iterator end() const { return data() + size(); }\n  const_iterator cbegin() const { return begin(); }\n  const_iterator cend() const { return end(); }\n\n  reverse_iterator rbegin() { return reverse_iterator(end()); }\n  reverse_iterator rend() { return reverse_iterator(begin()); }\n\n  const_reverse_iterator rbegin() const {\n    return const_reverse_iterator(end());\n  }\n\n  const_reverse_iterator rend() const {\n    return const_reverse_iterator(begin());\n  }\n\n  const_reverse_iterator crbegin() const { return rbegin(); }\n  const_reverse_iterator crend() const { return rend(); }\n\n  /*\n   * Usually one of the simplest functions in a Container-like class\n   * but a bit more complex here.  We have to handle all combinations\n   * of in-place vs. heap between this and o.\n   */\n  void swap(small_vector& o) noexcept(\n      std::is_nothrow_move_constructible<Value>::value &&\n      std::is_nothrow_swappable_v<Value>) {\n    using std::swap; // Allow ADL on swap for our value_type.\n\n    if (this->isExtern() && o.isExtern()) {\n      this->swapSizePolicy(o);\n\n      // Cannot use std::swap() because pdata_ is packed.\n      auto* tmp = u.pdata_.heap_;\n      u.pdata_.heap_ = o.u.pdata_.heap_;\n      o.u.pdata_.heap_ = tmp;\n\n      if (kHasInlineCapacity) {\n        const auto currentCapacity = this->u.getCapacity();\n        this->setCapacity(o.u.getCapacity());\n        o.u.setCapacity(currentCapacity);\n      }\n\n      return;\n    }\n\n    if (!this->isExtern() && !o.isExtern()) {\n      auto& oldSmall = size() < o.size() ? *this : o;\n      auto& oldLarge = size() < o.size() ? o : *this;\n\n      for (size_type i = 0; i < oldSmall.size(); ++i) {\n        swap(oldSmall[i], oldLarge[i]);\n      }\n\n      size_type i = oldSmall.size();\n      const size_type ci = i;\n      {\n        auto rollback = makeGuard([&] {\n          oldSmall.setSize(i);\n          std::destroy(oldLarge.begin() + i, oldLarge.end());\n          oldLarge.setSize(ci);\n        });\n        for (; i < oldLarge.size(); ++i) {\n          auto addr = oldSmall.begin() + i;\n          new (addr) value_type(std::move(oldLarge[i]));\n          std::destroy_at(oldLarge.data() + i);\n        }\n        rollback.dismiss();\n      }\n      oldSmall.setSize(i);\n      oldLarge.setSize(ci);\n      return;\n    }\n\n    // isExtern != o.isExtern()\n    auto& oldExtern = o.isExtern() ? o : *this;\n    auto& oldIntern = o.isExtern() ? *this : o;\n\n    auto oldExternCapacity = oldExtern.capacity();\n    auto oldExternHeap = oldExtern.u.pdata_.heap_;\n\n    auto buff = oldExtern.u.buffer();\n    size_type i = 0;\n    {\n      auto rollback = makeGuard([&] {\n        std::destroy_n(buff, i);\n        std::destroy(oldIntern.begin() + i, oldIntern.end());\n        oldIntern.resetSizePolicy();\n        oldExtern.u.pdata_.heap_ = oldExternHeap;\n        oldExtern.setCapacity(oldExternCapacity);\n      });\n      for (; i < oldIntern.size(); ++i) {\n        new (&buff[i]) value_type(std::move(oldIntern[i]));\n        std::destroy_at(oldIntern.data() + i);\n      }\n      rollback.dismiss();\n    }\n    oldIntern.u.pdata_.heap_ = oldExternHeap;\n    this->swapSizePolicy(o);\n    oldIntern.setCapacity(oldExternCapacity);\n  }\n\n  void resize(size_type sz) {\n    if (sz <= size()) {\n      downsize(sz);\n      return;\n    }\n    auto extra = sz - size();\n    makeSize(sz);\n    detail::small_vector_detail::populateMemForward(\n        begin() + size(), extra, [&](void* p) { new (p) value_type(); });\n    this->incrementSize(extra);\n  }\n\n  void resize(size_type sz, value_type const& v) {\n    if (sz < size()) {\n      FOLLY_PUSH_WARNING\n      FOLLY_GCC_DISABLE_WARNING(\"-Warray-bounds\")\n      erase(begin() + sz, end());\n      FOLLY_POP_WARNING\n      return;\n    }\n    auto extra = sz - size();\n    makeSize(sz);\n    detail::small_vector_detail::populateMemForward(\n        begin() + size(), extra, [&](void* p) { new (p) value_type(v); });\n    this->incrementSize(extra);\n  }\n\n  value_type* data() noexcept {\n    return this->isExtern() ? u.heap() : u.buffer();\n  }\n\n  value_type const* data() const noexcept {\n    return this->isExtern() ? u.heap() : u.buffer();\n  }\n\n  template <class... Args>\n  iterator emplace(const_iterator p, Args&&... args) {\n    if (p == cend()) {\n      emplace_back(std::forward<Args>(args)...);\n      return end() - 1;\n    }\n\n    /*\n     * We implement emplace at places other than at the back with a\n     * temporary for exception safety reasons.  It is possible to\n     * avoid having to do this, but it becomes hard to maintain the\n     * basic exception safety guarantee (unless you respond to a copy\n     * constructor throwing by clearing the whole vector).\n     *\n     * The reason for this is that otherwise you have to destruct an\n     * element before constructing this one in its place---if the\n     * constructor throws, you either need a nothrow default\n     * constructor or a nothrow copy/move to get something back in the\n     * \"gap\", and the vector requirements don't guarantee we have any\n     * of these.  Clearing the whole vector is a legal response in\n     * this situation, but it seems like this implementation is easy\n     * enough and probably better.\n     */\n    return insert(p, value_type(std::forward<Args>(args)...));\n  }\n\n  void reserve(size_type sz) { makeSize(sz); }\n\n  size_type capacity() const {\n    struct Unreachable {\n      size_t operator()(void*) const { assume_unreachable(); }\n    };\n    using AllocationSizeOrUnreachable =\n        conditional_t<kMustTrackHeapifiedCapacity, Unreachable, AllocationSize>;\n    if (this->isExtern()) {\n      if (hasCapacity()) {\n        return u.getCapacity();\n      }\n      return AllocationSizeOrUnreachable{}(u.pdata_.heap_) / sizeof(value_type);\n    }\n    return MaxInline;\n  }\n\n  void shrink_to_fit() {\n    if (!this->isExtern() || size() == capacity()) {\n      return;\n    }\n\n    small_vector old = std::exchange(*this, {});\n    reserve(old.size());\n    std::move(old.begin(), old.end(), std::back_inserter(*this));\n  }\n\n  template <class... Args>\n  reference emplace_back(Args&&... args) {\n    auto isize_ = this->getInternalSize();\n    if (isize_ < MaxInline) {\n      new (u.buffer() + isize_) value_type(std::forward<Args>(args)...);\n      this->incrementSize(1);\n      return *(u.buffer() + isize_);\n    }\n    if (!BaseType::kShouldUseHeap) {\n      throw_exception<std::length_error>(\"max_size exceeded in small_vector\");\n    }\n    auto currentSize = size();\n    auto currentCapacity = capacity();\n    if (currentCapacity == currentSize) {\n      // Any of args may be references into the vector.\n      // When we are reallocating, we have to be careful to construct the new\n      // element before modifying the data in the old buffer.\n      makeSize(\n          currentSize + 1,\n          [&](void* p) { new (p) value_type(std::forward<Args>(args)...); },\n          currentSize);\n    } else {\n      // We know the vector is stored in the heap.\n      new (u.heap() + currentSize) value_type(std::forward<Args>(args)...);\n    }\n    this->incrementSize(1);\n    return *(u.heap() + currentSize);\n  }\n\n  void push_back(value_type&& t) { emplace_back(std::move(t)); }\n\n  void push_back(value_type const& t) { emplace_back(t); }\n\n  void pop_back() {\n    // ideally this would be implemented in terms of erase(end() - 1) to reuse\n    // the higher-level abstraction, but neither Clang or GCC are able to\n    // optimize it away. if you change this, please verify (with disassembly)\n    // that the generated code on -O3 (and ideally -O2) stays short\n    downsize(size() - 1);\n  }\n\n  iterator insert(const_iterator constp, value_type&& t) {\n    iterator p = unconst(constp);\n    if (p == end()) {\n      push_back(std::move(t));\n      return end() - 1;\n    }\n\n    auto offset = p - begin();\n    auto currentSize = size();\n    if (capacity() == currentSize) {\n      makeSize(\n          currentSize + 1,\n          [&t](void* ptr) { new (ptr) value_type(std::move(t)); },\n          offset);\n      this->incrementSize(1);\n    } else {\n      detail::small_vector_detail::moveObjectsRightAndCreate(\n          data() + offset,\n          data() + currentSize,\n          data() + currentSize + 1,\n          [&]() mutable -> value_type&& { return std::move(t); });\n      this->incrementSize(1);\n    }\n    return begin() + offset;\n  }\n\n  iterator insert(const_iterator p, value_type const& t) {\n    // Make a copy and forward to the rvalue value_type&& overload\n    // above.\n    //\n    // std::move() is necessary to avoid an MSVC compiler bug which will\n    // issue this warning when used with unsigned int:\n    // warning C4717 : 'folly::small_vector<unsigned int,192,void>::insert':\n    // recursive on all control paths, function will cause runtime stack\n    // overflow\n    return insert(p, std::move(value_type(t)));\n  }\n\n  iterator insert(const_iterator pos, size_type n, value_type const& val) {\n    auto offset = pos - begin();\n    if (n != 0) {\n      auto currentSize = size();\n      makeSize(currentSize + n);\n      detail::small_vector_detail::moveObjectsRightAndCreate(\n          data() + offset,\n          data() + currentSize,\n          data() + currentSize + n,\n          [&]() mutable -> value_type const& { return val; });\n      this->incrementSize(n);\n    }\n    return begin() + offset;\n  }\n\n  template <class Arg>\n  iterator insert(const_iterator p, Arg arg1, Arg arg2) {\n    // Forward using std::is_arithmetic to get to the proper\n    // implementation; this disambiguates between the iterators and\n    // (size_t, value_type) meaning for this function.\n    return insertImpl(unconst(p), arg1, arg2, std::is_arithmetic<Arg>());\n  }\n\n  iterator insert(const_iterator p, std::initializer_list<value_type> il) {\n    return insert(p, il.begin(), il.end());\n  }\n\n  iterator erase(const_iterator q) {\n    // ideally this would be implemented in terms of erase(q, q + 1) to reuse\n    // the higher-level abstraction, but neither Clang or GCC are able to\n    // optimize it away. if you change this, please verify (with disassembly)\n    // that the generated code on -O3 (and ideally -O2) stays short\n    std::move(unconst(q) + 1, end(), unconst(q));\n    downsize(size() - 1);\n    return unconst(q);\n  }\n\n  iterator erase(const_iterator q1, const_iterator q2) {\n    if (q1 == q2) {\n      return unconst(q1);\n    }\n    std::move(unconst(q2), end(), unconst(q1));\n    downsize(size() - std::distance(q1, q2));\n    return unconst(q1);\n  }\n\n  void clear() {\n    // ideally this would be implemented in terms of erase(begin(), end()) to\n    // reuse the higher-level abstraction, but neither Clang or GCC are able to\n    // optimize it away. if you change this, please verify (with disassembly)\n    // that the generated code on -O3 (and ideally -O2) stays short\n    downsize(0);\n  }\n\n  template <class Arg>\n  void assign(Arg first, Arg last) {\n    clear();\n    insert(end(), first, last);\n  }\n\n  void assign(std::initializer_list<value_type> il) {\n    assign(il.begin(), il.end());\n  }\n\n  void assign(size_type n, const value_type& t) {\n    clear();\n    insert(end(), n, t);\n  }\n\n  reference front() {\n    assert(!empty());\n    return *begin();\n  }\n  reference back() {\n    assert(!empty());\n    return *(end() - 1);\n  }\n  const_reference front() const {\n    assert(!empty());\n    return *begin();\n  }\n  const_reference back() const {\n    assert(!empty());\n    return *(end() - 1);\n  }\n\n  reference operator[](size_type i) {\n    assert(i < size());\n    return *(begin() + i);\n  }\n\n  const_reference operator[](size_type i) const {\n    assert(i < size());\n    return *(begin() + i);\n  }\n\n  reference at(size_type i) {\n    if (i >= size()) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    return (*this)[i];\n  }\n\n  const_reference at(size_type i) const {\n    if (i >= size()) {\n      throw_exception<std::out_of_range>(\"index out of range\");\n    }\n    return (*this)[i];\n  }\n\n private:\n  static iterator unconst(const_iterator it) {\n    return const_cast<iterator>(it);\n  }\n\n  void downsize(size_type sz) {\n    assert(sz <= size());\n    std::destroy(begin() + sz, end());\n    this->setSize(sz);\n  }\n\n  void copyWholeInlineStorageTrivial(small_vector const& o) {\n    static_assert(detail::small_vector_detail::should_trivially_copy<Value>);\n    FOLLY_PUSH_WARNING\n    FOLLY_GCC_DISABLE_WARNING(\"-Warray-bounds\")\n    std::copy(o.u.buffer(), o.u.buffer() + MaxInline, u.buffer());\n    FOLLY_POP_WARNING\n    this->setSize(o.size());\n  }\n\n  void moveInlineStorageRelocatable(small_vector&& o) {\n    static_assert(IsRelocatable<Value>::value);\n    const auto n = o.size();\n    FOLLY_PUSH_WARNING\n    FOLLY_GCC_DISABLE_WARNING(\"-Wclass-memaccess\")\n    if constexpr (kMayCopyWholeInlineStorage) {\n      std::memcpy(\n          reinterpret_cast<void*>(u.buffer()),\n          reinterpret_cast<const void*>(o.u.buffer()),\n          MaxInline * kSizeOfValue);\n    } else {\n      std::memcpy(\n          reinterpret_cast<void*>(u.buffer()),\n          reinterpret_cast<const void*>(o.u.buffer()),\n          n * kSizeOfValue);\n    }\n    FOLLY_POP_WARNING\n    this->setSize(n);\n    o.resetSizePolicy();\n  }\n\n  void reset() {\n    clear();\n    freeHeap();\n    this->resetSizePolicy();\n  }\n\n  // The std::false_type argument is part of disambiguating the\n  // iterator insert functions from integral types (see insert().)\n  template <class It>\n  iterator insertImpl(iterator pos, It first, It last, std::false_type) {\n    if (first == last) {\n      return pos;\n    }\n    using categ = typename std::iterator_traits<It>::iterator_category;\n    using it_ref = typename std::iterator_traits<It>::reference;\n    if (std::is_same<categ, std::input_iterator_tag>::value) {\n      auto offset = pos - begin();\n      while (first != last) {\n        pos = insert(pos, *first++);\n        ++pos;\n      }\n      return begin() + offset;\n    }\n\n    auto const distance = std::distance(first, last);\n    auto const offset = pos - begin();\n    auto currentSize = size();\n    assert(distance >= 0);\n    assert(offset >= 0);\n    makeSize(currentSize + distance);\n    detail::small_vector_detail::moveObjectsRightAndCreate(\n        data() + offset,\n        data() + currentSize,\n        data() + currentSize + distance,\n        [&, in = last]() mutable -> it_ref { return *--in; });\n    this->incrementSize(distance);\n    return begin() + offset;\n  }\n\n  iterator insertImpl(\n      iterator pos, size_type n, const value_type& val, std::true_type) {\n    // The true_type means this should call the size_t,value_type\n    // overload.  (See insert().)\n    return insert(pos, n, val);\n  }\n\n  void destroy() {\n    std::destroy(begin(), end());\n    freeHeap();\n  }\n\n  // The std::false_type argument came from std::is_arithmetic as part\n  // of disambiguating an overload (see the comment in the\n  // constructor).\n  template <class It>\n  void constructImpl(It first, It last, std::false_type) {\n    using categ = typename std::iterator_traits<It>::iterator_category;\n    if (std::is_same<categ, std::input_iterator_tag>::value) {\n      // With iterators that only allow a single pass, we can't really\n      // do anything sane here.\n      auto rollback = makeGuard([&] { destroy(); });\n      while (first != last) {\n        emplace_back(*first++);\n      }\n      rollback.dismiss();\n      return;\n    }\n    size_type distance = std::distance(first, last);\n    if (distance <= MaxInline) {\n      this->incrementSize(distance);\n      detail::small_vector_detail::populateMemForward(\n          u.buffer(), distance, [&](void* p) { new (p) value_type(*first++); });\n      return;\n    }\n    makeSize(distance);\n    this->incrementSize(distance);\n    {\n      auto rollback = makeGuard([&] { freeHeap(); });\n      detail::small_vector_detail::populateMemForward(\n          u.heap(), distance, [&](void* p) { new (p) value_type(*first++); });\n      rollback.dismiss();\n    }\n  }\n\n  template <typename InitFunc>\n  void doConstruct(size_type n, InitFunc&& func) {\n    makeSize</* IgnoreExistingData */ true>(n);\n    assert(size() == 0);\n    this->incrementSize(n);\n    {\n      auto rollback = makeGuard([&] { freeHeap(); });\n      detail::small_vector_detail::populateMemForward(\n          data(), n, std::forward<InitFunc>(func));\n      rollback.dismiss();\n    }\n  }\n\n  // The true_type means we should forward to the size_t,value_type\n  // overload.\n  void constructImpl(size_type n, value_type const& val, std::true_type) {\n    FOLLY_PUSH_WARNING\n    FOLLY_GCC_DISABLE_WARNING(\"-Warray-bounds\")\n    doConstruct(n, [&](void* p) { new (p) value_type(val); });\n    FOLLY_POP_WARNING\n  }\n\n  /*\n   * Compute the size after growth.\n   */\n  size_type computeNewSize() const {\n    size_t c = capacity();\n    if (!checked_mul(&c, c, size_t(3))) {\n      throw_exception<std::length_error>(\n          \"Requested new size exceeds size representable by size_type\");\n    }\n    c = (c / 2) + 1;\n    return static_cast<size_type>(std::min<size_t>(c, max_size()));\n  }\n\n  template <bool IgnoreExistingData = false>\n  void makeSize(size_type newSize) {\n    if (newSize <= capacity()) {\n      return;\n    }\n    auto emplaceFunc = [](void*) { assume_unreachable(); };\n    makeSizeInternal<\n        /* Insert */ false,\n        IgnoreExistingData,\n        decltype(emplaceFunc)>(newSize, std::move(emplaceFunc), 0);\n  }\n\n  template <typename EmplaceFunc>\n  void makeSize(size_type newSize, EmplaceFunc&& emplaceFunc, size_type pos) {\n    assert(size() == capacity());\n\n    makeSizeInternal<\n        /* Insert */ true,\n        /* IgnoreExistingData */ false,\n        EmplaceFunc>(newSize, std::forward<EmplaceFunc>(emplaceFunc), pos);\n  }\n\n  /*\n   * Ensure we have a large enough memory region to be size `newSize'.\n   * Will move/copy elements if we are spilling to heap_ or needed to\n   * allocate a new region, but if resized in place doesn't initialize\n   * anything in the new region.  In any case doesn't change size().\n   * Supports insertion of new element during reallocation by given\n   * pointer to new element and position of new element.\n   * NOTE: If reallocation is not needed, insert must be false,\n   * because we only know how to emplace elements into new memory.\n   */\n  template <bool Insert, bool IgnoreExistingData, typename EmplaceFunc>\n  void makeSizeInternal(\n      size_type newSize, EmplaceFunc&& emplaceFunc, size_type pos) {\n    if (newSize > max_size()) {\n      throw_exception<std::length_error>(\"max_size exceeded in small_vector\");\n    }\n    assert(this->kShouldUseHeap);\n    // This branch isn't needed for correctness, but allows the optimizer to\n    // skip generating code for the rest of this function in in-situ-only\n    // small_vectors.\n    if (!this->kShouldUseHeap) {\n      return;\n    }\n\n    newSize = std::max(newSize, computeNewSize());\n\n    size_t needBytes = newSize;\n    if (!checked_mul(&needBytes, needBytes, sizeof(value_type))) {\n      throw_exception<std::length_error>(\n          \"Requested new size exceeds size representable by size_type\");\n    }\n    // If the capacity isn't explicitly stored inline, but the heap\n    // allocation is grown to over some threshold, we should store\n    // a capacity at the front of the heap allocation.\n    const bool heapifyCapacity =\n        !kHasInlineCapacity && needBytes >= kHeapifyCapacityThreshold;\n    const size_t allocationExtraBytes =\n        heapifyCapacity ? kHeapifyCapacitySize : 0;\n    size_t needAllocSizeBytes = needBytes;\n    if (!checked_add(\n            &needAllocSizeBytes, needAllocSizeBytes, allocationExtraBytes)) {\n      throw_exception<std::length_error>(\n          \"Requested new size exceeds size representable by size_type\");\n    }\n    const size_t goodAllocationSizeBytes = goodMallocSize(needAllocSizeBytes);\n    const size_t goodAllocationNewCapacity =\n        (goodAllocationSizeBytes - allocationExtraBytes) / sizeof(value_type);\n    const size_t newCapacity = std::min(goodAllocationNewCapacity, max_size());\n    // Make sure that the allocation request has a size computable from the\n    // capacity, instead of using goodAllocationSizeBytes, so that we can do\n    // sized deallocation. If goodMallocSize() gives us extra bytes that are not\n    // a multiple of the value size we cannot use them anyway.\n    const size_t sizeBytes =\n        newCapacity * sizeof(value_type) + allocationExtraBytes;\n    void* newh = checkedMalloc(sizeBytes);\n    value_type* newp = static_cast<value_type*>(\n        heapifyCapacity\n            ? detail::small_vector_detail::shiftPointer(\n                  newh, kHeapifyCapacitySize)\n            : newh);\n\n    {\n      auto rollback = makeGuard([&] { //\n        sizedFree(newh, sizeBytes);\n      });\n      if constexpr (Insert) {\n        static_assert(!IgnoreExistingData);\n        // move and insert the new element\n        this->moveToUninitializedEmplace(\n            begin(), end(), newp, pos, std::forward<EmplaceFunc>(emplaceFunc));\n      } else if constexpr (!IgnoreExistingData) {\n        // move without inserting new element\n        if (data()) {\n          this->moveToUninitialized(begin(), end(), newp);\n        }\n      }\n      rollback.dismiss();\n    }\n    annotate_object_leaked(newh);\n    std::destroy(begin(), end());\n    freeHeap();\n    // Store shifted pointer if capacity is heapified\n    u.pdata_.heap_ = newp;\n    this->setHeapifiedCapacity(heapifyCapacity);\n    this->setExtern(true);\n    this->setCapacity(newCapacity);\n  }\n\n  /*\n   * This will set the capacity field, stored inline in the storage_ field\n   * if there is sufficient room to store it.\n   */\n  void setCapacity(size_type newCapacity) {\n    assert(this->isExtern());\n    if (hasCapacity()) {\n      assert(newCapacity < std::numeric_limits<InternalSizeType>::max());\n      u.setCapacity(newCapacity);\n    }\n  }\n\n private:\n  // These internal classes are packed to minimize total memory usage,\n  // however, it is important that we don't pack the class as a whole\n  // otherwise the inline storage may not have the correct alignment\n  // for the value type.\n  FOLLY_SV_PACK_PUSH\n  struct HeapPtrWithCapacity {\n    value_type* heap_;\n    InternalSizeType capacity_;\n\n    InternalSizeType getCapacity() const {\n      FOLLY_PUSH_WARNING\n      FOLLY_GCC_DISABLE_WARNING(\"-Wmaybe-uninitialized\")\n      return capacity_;\n      FOLLY_POP_WARNING\n    }\n    void setCapacity(InternalSizeType c) { capacity_ = c; }\n    size_t allocationExtraBytes() const { return 0; }\n  } FOLLY_SV_PACK_ATTR;\n  FOLLY_SV_PACK_POP\n\n  FOLLY_SV_PACK_PUSH\n  struct HeapPtr {\n    // heap[-kHeapifyCapacitySize] contains capacity\n    value_type* heap_;\n\n    InternalSizeType getCapacity() const {\n      return heap_\n          ? *static_cast<InternalSizeType*>(\n                detail::small_vector_detail::unshiftPointer(\n                    heap_, kHeapifyCapacitySize))\n          : 0;\n    }\n    void setCapacity(InternalSizeType c) {\n      *static_cast<InternalSizeType*>(\n          detail::small_vector_detail::unshiftPointer(\n              heap_, kHeapifyCapacitySize)) = c;\n    }\n    size_t allocationExtraBytes() const { return kHeapifyCapacitySize; }\n  } FOLLY_SV_PACK_ATTR;\n  FOLLY_SV_PACK_POP\n\n  static constexpr size_t kMaxInlineNonZero = MaxInline ? MaxInline : 1u;\n  using InlineStorageDataType =\n      aligned_storage_for_t<value_type[kMaxInlineNonZero]>;\n\n  using InlineStorageType = typename std::conditional<\n      sizeof(value_type) * MaxInline != 0,\n      InlineStorageDataType,\n      char>::type;\n\n  // If the storage is small enough, it is usually faster to copy it entirely,\n  // instead of just size() values, to make the loop fixed-size and\n  // unrollable. Limit is half of a cache line, to minimize probability of\n  // crossing a cache line and thus introducing an unnecessary cache miss.\n  static constexpr bool kMayCopyWholeInlineStorage =\n      sizeof(InlineStorageType) <= hardware_constructive_interference_size / 2;\n\n  static constexpr bool kShouldCopyWholeInlineStorageTrivial =\n      detail::small_vector_detail::should_trivially_copy<Value> &&\n      kMayCopyWholeInlineStorage;\n\n  static bool constexpr kHasInlineCapacity = !BaseType::kAlwaysUseHeap &&\n      sizeof(HeapPtrWithCapacity) < sizeof(InlineStorageType);\n\n  // This value should we multiple of word size.\n  static size_t constexpr kHeapifyCapacitySize = sizeof(\n      folly::aligned_storage_t<sizeof(InternalSizeType), alignof(value_type)>);\n\n  struct AllocationSize {\n    auto operator()(void* ptr) const {\n      (void)ptr;\n#if defined(FOLLY_HAVE_MALLOC_USABLE_SIZE)\n      return malloc_usable_size(ptr);\n#endif\n      // it is important that this method not return a size_t if we can't call\n      // malloc_usable_size! kMustTrackHeapifiedCapacity uses the deduced return\n      // type of this function in order to decide whether small_vector must\n      // track its own capacity or not.\n    }\n  };\n\n  static bool constexpr kMustTrackHeapifiedCapacity =\n      BaseType::kAlwaysUseHeap ||\n      !is_invocable_r_v<size_t, AllocationSize, void*>;\n\n  // Threshold to control capacity heapifying.\n  static size_t constexpr kHeapifyCapacityThreshold =\n      (kMustTrackHeapifiedCapacity ? 0 : 100) * kHeapifyCapacitySize;\n\n  static bool constexpr kAlwaysHasCapacity =\n      kHasInlineCapacity || kMustTrackHeapifiedCapacity;\n\n  using PointerType = typename std::\n      conditional<kHasInlineCapacity, HeapPtrWithCapacity, HeapPtr>::type;\n\n  bool hasCapacity() const {\n    return kAlwaysHasCapacity || !kHeapifyCapacityThreshold ||\n        this->isHeapifiedCapacity();\n  }\n\n  void freeHeap() {\n    if (!this->isExtern() || !u.pdata_.heap_) {\n      return;\n    }\n\n    if (hasCapacity()) {\n      auto extraBytes = u.pdata_.allocationExtraBytes();\n      auto vp = detail::small_vector_detail::unshiftPointer(\n          u.pdata_.heap_, extraBytes);\n      annotate_object_collected(vp);\n      sizedFree(vp, u.getCapacity() * sizeof(value_type) + extraBytes);\n    } else {\n      auto vp = u.pdata_.heap_;\n      annotate_object_collected(vp);\n      free(vp);\n    }\n  }\n\n  union Data {\n    explicit Data() { pdata_.heap_ = nullptr; }\n\n    PointerType pdata_;\n    InlineStorageType storage_;\n\n    value_type* buffer() noexcept {\n      void* vp = &storage_;\n      return static_cast<value_type*>(vp);\n    }\n    value_type const* buffer() const noexcept {\n      return const_cast<Data*>(this)->buffer();\n    }\n    value_type* heap() noexcept { return pdata_.heap_; }\n    value_type const* heap() const noexcept { return pdata_.heap_; }\n\n    InternalSizeType getCapacity() const { return pdata_.getCapacity(); }\n    void setCapacity(InternalSizeType c) { pdata_.setCapacity(c); }\n\n  } u;\n};\n\n//////////////////////////////////////////////////////////////////////\n\n// Basic guarantee only, or provides the nothrow guarantee iff T has a\n// nothrow move or copy constructor.\ntemplate <class T, std::size_t MaxInline, class P>\nvoid swap(small_vector<T, MaxInline, P>& a, small_vector<T, MaxInline, P>& b) {\n  a.swap(b);\n}\n\ntemplate <class T, std::size_t MaxInline, class P, class U>\nvoid erase(small_vector<T, MaxInline, P>& v, U value) {\n  v.erase(std::remove(v.begin(), v.end(), value), v.end());\n}\n\ntemplate <class T, std::size_t MaxInline, class P, class Predicate>\nvoid erase_if(small_vector<T, MaxInline, P>& v, Predicate predicate) {\n  v.erase(std::remove_if(v.begin(), v.end(), std::ref(predicate)), v.end());\n}\n\n//////////////////////////////////////////////////////////////////////\n\nnamespace detail {\n\n// Format support.\ntemplate <class T, size_t M, class P>\nstruct IndexableTraits<small_vector<T, M, P>>\n    : public IndexableTraitsSeq<small_vector<T, M, P>> {};\n\n} // namespace detail\n\ntemplate <typename>\nstruct is_small_vector : std::false_type {};\n\ntemplate <class Value, size_t N, class Policy>\nstruct is_small_vector<small_vector<Value, N, Policy>> : std::true_type {};\n\ntemplate <typename T>\ninline constexpr bool is_small_vector_v = is_small_vector<T>::value;\n\n} // namespace folly\n\nFOLLY_POP_WARNING\n\n#undef FOLLY_SV_PACK_ATTR\n#undef FOLLY_SV_PACK_PUSH\n#undef FOLLY_SV_PACK_POP\n\nnamespace std {\n\ntemplate <class T, std::size_t M, class P>\nstruct hash<folly::small_vector<T, M, P>> {\n  size_t operator()(const folly::small_vector<T, M, P>& v) const {\n    return folly::hash::hash_range(v.begin(), v.end());\n  }\n};\n\n} // namespace std\n"
  },
  {
    "path": "folly/container/sorted_vector_types.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * This header defines two classes that very nearly model\n * AssociativeContainer (but not quite).  These implement set-like and\n * map-like behavior on top of a sorted vector, instead of using\n * rb-trees like std::set and std::map.\n *\n * This is potentially useful in cases where the number of elements in\n * the set or map is small, or when you want to avoid using more\n * memory than necessary and insertions/deletions are much more rare\n * than lookups (these classes have O(N) insertions/deletions).\n *\n * In the interest of using these in conditions where the goal is to\n * minimize memory usage, they support a GrowthPolicy parameter, which\n * is a class defining a single function called increase_capacity,\n * which will be called whenever we are about to insert something: you\n * can then decide to call reserve() based on the current capacity()\n * and size() of the passed in vector-esque Container type.  An\n * example growth policy that grows one element at a time:\n *\n *    struct OneAtATimePolicy {\n *      template <class Container>\n *      void increase_capacity(Container& c) {\n *        if (c.size() == c.capacity()) {\n *          c.reserve(c.size() + 1);\n *        }\n *      }\n *    };\n *\n *    typedef sorted_vector_set<int,\n *                              std::less<int>,\n *                              std::allocator<int>,\n *                              OneAtATimePolicy>\n *            OneAtATimeIntSet;\n *\n * Important differences from std::set and std::map:\n *   - insert() and erase() invalidate iterators and references.\n       erase(iterator) returns an iterator pointing to the next valid element.\n *   - insert() and erase() are O(N)\n *   - our iterators model RandomAccessIterator\n *   - sorted_vector_map::value_type is pair<K,V>, not pair<const K,V>.\n *     (This is basically because we want to store the value_type in\n *     std::vector<>, which requires it to be Assignable.)\n *   - insert() single key variants, emplace(), and emplace_hint() only provide\n *     the strong exception guarantee (unchanged when exception is thrown) when\n *     std::is_nothrow_move_constructible<value_type>::value is true.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cassert>\n#include <initializer_list>\n#include <iterator>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/lang/Access.h>\n#include <folly/lang/Exception.h>\n#include <folly/memory/MemoryResource.h>\n#include <folly/small_vector.h>\n\nnamespace folly {\n\n//////////////////////////////////////////////////////////////////////\n\nnamespace detail {\n\ntemplate <typename, typename Compare, typename Key, typename T>\nstruct sorted_vector_enable_if_is_transparent {};\n\ntemplate <typename Compare, typename Key, typename T>\nstruct sorted_vector_enable_if_is_transparent<\n    void_t<typename Compare::is_transparent>,\n    Compare,\n    Key,\n    T> {\n  using type = T;\n};\n\n// This wrapper goes around a GrowthPolicy and provides iterator\n// preservation semantics, but only if the growth policy is not the\n// default (i.e. nothing).\ntemplate <class Policy>\nstruct growth_policy_wrapper : private Policy {\n  template <class Container, class Iterator>\n  Iterator increase_capacity(Container& c, Iterator desired_insertion) {\n    using diff_t = typename Container::difference_type;\n    diff_t d = desired_insertion - c.begin();\n    Policy::increase_capacity(c);\n    return c.begin() + d;\n  }\n};\ntemplate <>\nstruct growth_policy_wrapper<void> {\n  template <class Container, class Iterator>\n  Iterator increase_capacity(Container&, Iterator it) {\n    return it;\n  }\n};\n\ntemplate <class OurContainer, class Vector, class GrowthPolicy, class Value>\ntypename OurContainer::iterator insert_with_hint(\n    OurContainer& sorted,\n    Vector& cont,\n    typename OurContainer::const_iterator hint,\n    Value&& value,\n    GrowthPolicy& po) {\n  const typename OurContainer::value_compare& cmp(sorted.value_comp());\n  if (hint == cont.end() || cmp(value, *hint)) {\n    if (hint == cont.begin() || cmp(*(hint - 1), value)) {\n      hint = po.increase_capacity(cont, hint);\n      return cont.emplace(hint, std::forward<Value>(value));\n    } else {\n      return sorted.emplace(std::forward<Value>(value)).first;\n    }\n  }\n\n  if (cmp(*hint, value)) {\n    if (hint + 1 == cont.end() || cmp(value, *(hint + 1))) {\n      hint = po.increase_capacity(cont, hint + 1);\n      return cont.emplace(hint, std::forward<Value>(value));\n    } else {\n      return sorted.emplace(std::forward<Value>(value)).first;\n    }\n  }\n\n  // Value and *hint did not compare, so they are equal keys.\n  return sorted.begin() + std::distance(sorted.cbegin(), hint);\n}\n\ntemplate <typename Iterator, typename Compare>\nbool is_sorted_unique(Iterator begin, Iterator end, Compare const& comp) {\n  if (begin == end) {\n    return true;\n  }\n  for (auto next = std::next(begin); next != end; ++begin, ++next) {\n    if (!comp(*begin, *next)) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename Container, typename Compare>\nContainer&& as_sorted_unique(Container&& container, Compare const& comp) {\n  std::sort(container.begin(), container.end(), comp);\n  container.erase(\n      std::unique(\n          container.begin(),\n          container.end(),\n          [&](auto const& a, auto const& b) {\n            return !comp(a, b) && !comp(b, a);\n          }),\n      container.end());\n  return static_cast<Container&&>(container);\n}\n\ntemplate <typename Container, typename Compare>\nclass DirectMutationGuard {\n public:\n  DirectMutationGuard(\n      Container& container, const Compare& comp, bool isSortedUnique)\n      : container_(container), comp_(comp), isSortedUnique_(isSortedUnique) {}\n\n  ~DirectMutationGuard() noexcept(false) {\n    if (isSortedUnique_) {\n      assert(\n          detail::is_sorted_unique(\n              container_.begin(), container_.end(), comp_));\n      return;\n    }\n    as_sorted_unique(container_, comp_);\n  }\n\n  Container& get() { return container_; }\n\n private:\n  Container& container_;\n  const Compare comp_;\n  const bool isSortedUnique_;\n};\n\ntemplate <class OurContainer, class Vector, class InputIterator>\nvoid bulk_insert(\n    OurContainer& sorted,\n    Vector& cont,\n    InputIterator first,\n    InputIterator last,\n    bool range_is_sorted_unique = false) {\n  // Prevent deref of middle where middle == cont.end().\n  if (first == last) {\n    return;\n  }\n\n  auto const prev_size = cont.size();\n  cont.insert(cont.end(), first, last);\n  auto const middle = cont.begin() + prev_size;\n\n  auto const& cmp(sorted.value_comp());\n  if (range_is_sorted_unique) {\n    assert(is_sorted_unique(middle, cont.end(), cmp));\n  } else if (!std::is_sorted(middle, cont.end(), cmp)) {\n    std::sort(middle, cont.end(), cmp);\n  }\n\n  // We do not need to consider elements strictly smaller than the smallest new\n  // element in merge/unique.\n  auto merge_begin = middle;\n  while (merge_begin != cont.begin() && !cmp(*(merge_begin - 1), *middle)) {\n    --merge_begin;\n  }\n\n  if (merge_begin != middle) {\n    std::inplace_merge(cont.begin(), middle, cont.end(), cmp);\n  } else if (range_is_sorted_unique) {\n    // Old and new elements are already disjoint and unique. This includes the\n    // case when cont is initially empty.\n    return;\n  }\n\n  cont.erase(\n      std::unique(\n          merge_begin,\n          cont.end(),\n          [&](typename OurContainer::value_type const& a,\n              typename OurContainer::value_type const& b) {\n            return !cmp(a, b);\n          }),\n      cont.end());\n}\n\n} // namespace detail\n\n//////////////////////////////////////////////////////////////////////\n\n/**\n * A sorted_vector_set is a container similar to std::set<>, but\n * implemented as a sorted array with std::vector<>.\n *\n * @tparam T               Data type to store\n * @tparam Compare         Comparison function that imposes a\n *                              strict weak ordering over instances of T\n * @tparam Allocator       allocation policy\n * @tparam GrowthPolicy    policy object to control growth\n */\ntemplate <\n    class T,\n    class Compare = std::less<T>,\n    class Allocator = std::allocator<T>,\n    class GrowthPolicy = void,\n    class Container = std::vector<T, Allocator>>\nclass sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> {\n  detail::growth_policy_wrapper<GrowthPolicy>& get_growth_policy() {\n    return *this;\n  }\n\n  template <typename K, typename V, typename C = Compare>\n  using if_is_transparent =\n      _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>;\n\n  struct EBO;\n\n public:\n  using value_type = T;\n  using key_type = T;\n  using key_compare = Compare;\n  using value_compare = Compare;\n  using allocator_type = Allocator;\n  using container_type = Container;\n\n  using pointer = typename Container::pointer;\n  using reference = typename Container::reference;\n  using const_reference = typename Container::const_reference;\n  using const_pointer = typename Container::const_pointer;\n  /*\n   * XXX: Our normal iterator ought to also be a constant iterator\n   * (cf. Defect Report 103 for std::set), but this is a bit more of a\n   * pain.\n   */\n  using iterator = typename Container::iterator;\n  using const_iterator = typename Container::const_iterator;\n  using difference_type = typename Container::difference_type;\n  using size_type = typename Container::size_type;\n  using reverse_iterator = typename Container::reverse_iterator;\n  using const_reverse_iterator = typename Container::const_reverse_iterator;\n  using direct_mutation_guard =\n      detail::DirectMutationGuard<Container, value_compare>;\n\n  sorted_vector_set() : m_(Compare(), Allocator()) {}\n\n  sorted_vector_set(const sorted_vector_set&) = default;\n\n  sorted_vector_set(const sorted_vector_set& other, const Allocator& alloc)\n      : m_(other.m_, alloc) {}\n\n  sorted_vector_set(sorted_vector_set&&) = default;\n\n  sorted_vector_set(sorted_vector_set&& other, const Allocator& alloc) noexcept(\n      std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value)\n      : m_(std::move(other.m_), alloc) {}\n\n  explicit sorted_vector_set(const Allocator& alloc) : m_(Compare(), alloc) {}\n\n  explicit sorted_vector_set(\n      const Compare& comp, const Allocator& alloc = Allocator())\n      : m_(comp, alloc) {}\n\n  template <class InputIterator>\n  sorted_vector_set(\n      InputIterator first,\n      InputIterator last,\n      const Compare& comp = Compare(),\n      const Allocator& alloc = Allocator())\n      : m_(comp, alloc) {\n    // This is linear if [first, last) is already sorted (and if we\n    // can figure out the distance between the two iterators).\n    insert(first, last);\n  }\n\n  template <class InputIterator>\n  sorted_vector_set(\n      InputIterator first, InputIterator last, const Allocator& alloc)\n      : m_(Compare(), alloc) {\n    // This is linear if [first, last) is already sorted (and if we\n    // can figure out the distance between the two iterators).\n    insert(first, last);\n  }\n\n  /* implicit */ sorted_vector_set(\n      std::initializer_list<value_type> list,\n      const Compare& comp = Compare(),\n      const Allocator& alloc = Allocator())\n      : m_(comp, alloc) {\n    insert(list.begin(), list.end());\n  }\n\n  sorted_vector_set(\n      std::initializer_list<value_type> list, const Allocator& alloc)\n      : m_(Compare(), alloc) {\n    insert(list.begin(), list.end());\n  }\n\n  // Construct a sorted_vector_set by stealing the storage of a prefilled\n  // container. The container need not be sorted already. This supports\n  // bulk construction of sorted_vector_set with zero allocations, not counting\n  // those performed by the caller. (The iterator range constructor performs at\n  // least one allocation).\n  //\n  // Note that `sorted_vector_set(const Container& container)` is not provided,\n  // since the purpose of this constructor is to avoid an unnecessary copy.\n  explicit sorted_vector_set(\n      Container&& container, const Compare& comp = Compare())\n      : sorted_vector_set(\n            sorted_unique,\n            detail::as_sorted_unique(std::move(container), comp),\n            comp) {}\n\n  // Construct a sorted_vector_set by stealing the storage of a prefilled\n  // container. Its elements must be sorted and unique, as sorted_unique_t\n  // hints. Supports bulk construction of sorted_vector_set with zero\n  // allocations, not counting those performed by the caller. (The iterator\n  // range constructor performs at least one allocation).\n  //\n  // Note that `sorted_vector_set(sorted_unique_t, const Container& container)`\n  // is not provided, since the purpose of this constructor is to avoid an extra\n  // copy.\n  sorted_vector_set(\n      sorted_unique_t,\n      Container&& container,\n      const Compare& comp =\n          Compare()) noexcept(std::\n                                  is_nothrow_constructible<\n                                      EBO,\n                                      const Compare&,\n                                      Container&&>::value)\n      : m_(comp, std::move(container)) {\n    assert(\n        detail::is_sorted_unique(\n            m_.cont_.begin(), m_.cont_.end(), value_comp()));\n  }\n\n  Allocator get_allocator() const { return m_.cont_.get_allocator(); }\n\n  const Container& get_container() const noexcept { return m_.cont_; }\n\n  /**\n   * Directly mutate the container.\n   *\n   * Get a guarded reference to the underlying container for direct mutation.\n   * sorted_unique_t signals that user will make sure that after the\n   * modification the container will have its values as sorted-unique\n   * (conforming to container's value_comp). Violating this assumption will\n   * result in undefined behavior.\n   *\n   * This function is not safe to use concurrently with other functions.\n   */\n  direct_mutation_guard get_container_for_direct_mutation(\n      sorted_unique_t) noexcept {\n    return direct_mutation_guard{\n        m_.cont_, value_comp(), /* range_is_sorted_unique */ true};\n  }\n\n  /**\n   * Directly mutate the container.\n   *\n   * Get a guarded reference to the underlying container for direct mutation.\n   * The container will initially be sorted and unique. You are not required to\n   * maintain the sorted-unique invariant while mutating. When the guard is\n   * released, it will sort and unique-ify the container.\n   *\n   * This function is not safe to use concurrently with other functions.\n   */\n  direct_mutation_guard get_container_for_direct_mutation() noexcept {\n    return direct_mutation_guard{\n        m_.cont_, value_comp(), /* range_is_sorted_unique */ false};\n  }\n\n  /**\n   * Directly swap the container. Similar to swap()\n   */\n  void swap_container(Container& newContainer) {\n    detail::as_sorted_unique(newContainer, value_comp());\n    using std::swap;\n    swap(m_.cont_, newContainer);\n  }\n  void swap_container(sorted_unique_t, Container& newContainer) {\n    assert(\n        detail::is_sorted_unique(\n            newContainer.begin(), newContainer.end(), value_comp()));\n    using std::swap;\n    swap(m_.cont_, newContainer);\n  }\n\n  sorted_vector_set& operator=(const sorted_vector_set& other) = default;\n\n  sorted_vector_set& operator=(sorted_vector_set&& other) = default;\n\n  sorted_vector_set& operator=(std::initializer_list<value_type> ilist) {\n    clear();\n    insert(ilist.begin(), ilist.end());\n    return *this;\n  }\n\n  key_compare key_comp() const { return m_; }\n  value_compare value_comp() const { return m_; }\n\n  iterator begin() { return m_.cont_.begin(); }\n  iterator end() { return m_.cont_.end(); }\n  const_iterator cbegin() const { return m_.cont_.cbegin(); }\n  const_iterator begin() const { return m_.cont_.begin(); }\n  const_iterator cend() const { return m_.cont_.cend(); }\n  const_iterator end() const { return m_.cont_.end(); }\n  reverse_iterator rbegin() { return m_.cont_.rbegin(); }\n  reverse_iterator rend() { return m_.cont_.rend(); }\n  const_reverse_iterator rbegin() const { return m_.cont_.rbegin(); }\n  const_reverse_iterator rend() const { return m_.cont_.rend(); }\n\n  void clear() { return m_.cont_.clear(); }\n  size_type size() const { return m_.cont_.size(); }\n  size_type max_size() const { return m_.cont_.max_size(); }\n  bool empty() const { return m_.cont_.empty(); }\n  void reserve(size_type s) { return m_.cont_.reserve(s); }\n  void shrink_to_fit() { m_.cont_.shrink_to_fit(); }\n  size_type capacity() const { return m_.cont_.capacity(); }\n\n  std::pair<iterator, bool> insert(const value_type& value) {\n    iterator it = lower_bound(value);\n    if (it == end() || value_comp()(value, *it)) {\n      it = get_growth_policy().increase_capacity(m_.cont_, it);\n      return std::make_pair(m_.cont_.emplace(it, value), true);\n    }\n    return std::make_pair(it, false);\n  }\n\n  std::pair<iterator, bool> insert(value_type&& value) {\n    iterator it = lower_bound(value);\n    if (it == end() || value_comp()(value, *it)) {\n      it = get_growth_policy().increase_capacity(m_.cont_, it);\n      return std::make_pair(m_.cont_.emplace(it, std::move(value)), true);\n    }\n    return std::make_pair(it, false);\n  }\n\n  iterator insert(const_iterator hint, const value_type& value) {\n    return detail::insert_with_hint(\n        *this, m_.cont_, hint, value, get_growth_policy());\n  }\n\n  iterator insert(const_iterator hint, value_type&& value) {\n    return detail::insert_with_hint(\n        *this, m_.cont_, hint, std::move(value), get_growth_policy());\n  }\n\n  template <class InputIterator>\n  void insert(InputIterator first, InputIterator last) {\n    detail::bulk_insert(*this, m_.cont_, first, last);\n  }\n\n  // If [first, last) is known to be sorted and unique according to the\n  // comparator (for example if the range comes from a sorted container of the\n  // same type) this version can save unnecessary operations, especially if\n  // *this is empty.\n  template <class InputIterator>\n  void insert(sorted_unique_t, InputIterator first, InputIterator last) {\n    detail::bulk_insert(\n        *this, m_.cont_, first, last, /* range_is_sorted_unique */ true);\n  }\n\n  void insert(std::initializer_list<value_type> ilist) {\n    insert(ilist.begin(), ilist.end());\n  }\n\n  // emplace isn't better than insert for sorted_vector_set, but aids\n  // compatibility\n  template <typename... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    folly::aligned_storage_for_t<value_type> b;\n    value_type* p = static_cast<value_type*>(static_cast<void*>(&b));\n    auto a = get_allocator();\n    std::allocator_traits<allocator_type>::construct(\n        a, p, std::forward<Args>(args)...);\n    auto g = makeGuard([&]() {\n      std::allocator_traits<allocator_type>::destroy(a, p);\n    });\n    return insert(std::move(*p));\n  }\n\n  std::pair<iterator, bool> emplace(const value_type& value) {\n    return insert(value);\n  }\n\n  std::pair<iterator, bool> emplace(value_type&& value) {\n    return insert(std::move(value));\n  }\n\n  // emplace_hint isn't better than insert for sorted_vector_set, but aids\n  // compatibility\n  template <typename... Args>\n  iterator emplace_hint(const_iterator hint, Args&&... args) {\n    folly::aligned_storage_for_t<value_type> b;\n    value_type* p = static_cast<value_type*>(static_cast<void*>(&b));\n    auto a = get_allocator();\n    std::allocator_traits<allocator_type>::construct(\n        a, p, std::forward<Args>(args)...);\n    auto g = makeGuard([&]() {\n      std::allocator_traits<allocator_type>::destroy(a, p);\n    });\n    return insert(hint, std::move(*p));\n  }\n\n  iterator emplace_hint(const_iterator hint, const value_type& value) {\n    return insert(hint, value);\n  }\n\n  iterator emplace_hint(const_iterator hint, value_type&& value) {\n    return insert(hint, std::move(value));\n  }\n\n  size_type erase(const key_type& key) {\n    iterator it = find(key);\n    if (it == end()) {\n      return 0;\n    }\n    m_.cont_.erase(it);\n    return 1;\n  }\n\n  iterator erase(const_iterator it) { return m_.cont_.erase(it); }\n\n  iterator erase(const_iterator first, const_iterator last) {\n    return m_.cont_.erase(first, last);\n  }\n\n  template <class Predicate>\n  friend size_type erase_if(sorted_vector_set& container, Predicate predicate) {\n    auto& c = container.m_.cont_;\n    const auto preEraseSize = c.size();\n    c.erase(std::remove_if(c.begin(), c.end(), std::ref(predicate)), c.end());\n    return preEraseSize - c.size();\n  }\n\n  iterator find(const key_type& key) { return find_(*this, key); }\n\n  const_iterator find(const key_type& key) const { return find_(*this, key); }\n\n  template <typename K>\n  if_is_transparent<K, iterator> find(const K& key) {\n    return find_(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> find(const K& key) const {\n    return find_(*this, key);\n  }\n\n  size_type count(const key_type& key) const {\n    return find(key) == end() ? 0 : 1;\n  }\n\n  std::pair<iterator, iterator> find(\n      const key_type& key1, const key_type& key2) {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  std::pair<const_iterator, const_iterator> find(\n      const key_type& key1, const key_type& key2) const {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  template <typename K>\n  std::pair<if_is_transparent<K, iterator>, if_is_transparent<K, iterator>>\n  find(const K& key1, const K& key2) {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  template <typename K>\n  std::pair<\n      if_is_transparent<K, const_iterator>,\n      if_is_transparent<K, const_iterator>>\n  find(const K& key1, const K& key2) const {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  template <typename K>\n  if_is_transparent<K, size_type> count(const K& key) const {\n    return find(key) == end() ? 0 : 1;\n  }\n\n  bool contains(const key_type& key) const { return find(key) != end(); }\n\n  template <typename K>\n  if_is_transparent<K, bool> contains(const K& key) const {\n    return find(key) != end();\n  }\n\n  iterator lower_bound(const key_type& key) {\n    return std::lower_bound(begin(), end(), key, key_comp());\n  }\n\n  const_iterator lower_bound(const key_type& key) const {\n    return std::lower_bound(begin(), end(), key, key_comp());\n  }\n\n  template <typename K>\n  if_is_transparent<K, iterator> lower_bound(const K& key) {\n    return std::lower_bound(begin(), end(), key, key_comp());\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> lower_bound(const K& key) const {\n    return std::lower_bound(begin(), end(), key, key_comp());\n  }\n\n  iterator upper_bound(const key_type& key) {\n    return std::upper_bound(begin(), end(), key, key_comp());\n  }\n\n  const_iterator upper_bound(const key_type& key) const {\n    return std::upper_bound(begin(), end(), key, key_comp());\n  }\n\n  template <typename K>\n  if_is_transparent<K, iterator> upper_bound(const K& key) {\n    return std::upper_bound(begin(), end(), key, key_comp());\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> upper_bound(const K& key) const {\n    return std::upper_bound(begin(), end(), key, key_comp());\n  }\n\n  std::pair<iterator, iterator> equal_range(const key_type& key) {\n    return std::equal_range(begin(), end(), key, key_comp());\n  }\n\n  std::pair<const_iterator, const_iterator> equal_range(\n      const key_type& key) const {\n    return std::equal_range(begin(), end(), key, key_comp());\n  }\n\n  template <typename K>\n  if_is_transparent<K, std::pair<iterator, iterator>> equal_range(\n      const K& key) {\n    return std::equal_range(begin(), end(), key, key_comp());\n  }\n\n  template <typename K>\n  if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range(\n      const K& key) const {\n    return std::equal_range(begin(), end(), key, key_comp());\n  }\n\n  void swap(sorted_vector_set& o) noexcept(\n      std::is_nothrow_swappable_v<Compare> &&\n      noexcept(std::declval<Container&>().swap(o.m_.cont_))) {\n    using std::swap; // Allow ADL for swap(); fall back to std::swap().\n    Compare& a = m_;\n    Compare& b = o.m_;\n    swap(a, b);\n    m_.cont_.swap(o.m_.cont_);\n  }\n\n  bool operator==(const sorted_vector_set& other) const {\n    return other.m_.cont_ == m_.cont_;\n  }\n  bool operator!=(const sorted_vector_set& other) const {\n    return !operator==(other);\n  }\n\n  bool operator<(const sorted_vector_set& other) const {\n    return m_.cont_ < other.m_.cont_;\n  }\n  bool operator>(const sorted_vector_set& other) const { return other < *this; }\n  bool operator<=(const sorted_vector_set& other) const {\n    return !operator>(other);\n  }\n  bool operator>=(const sorted_vector_set& other) const {\n    return !operator<(other);\n  }\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_impl_three_way_comparison)\n  template <typename U = Container>\n  friend auto operator<=>(\n      const sorted_vector_set& lhs, const sorted_vector_set& rhs)\n      -> decltype(std::declval<const U&>() <=> std::declval<const U&>()) {\n    return lhs.m_.cont_ <=> rhs.m_.cont_;\n  }\n#endif // FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_impl_three_way_comparison)\n\n  const value_type* data() const noexcept { return m_.cont_.data(); }\n\n private:\n  /*\n   * This structure derives from the comparison object in order to\n   * make use of the empty base class optimization if our comparison\n   * functor is an empty class (usual case).\n   *\n   * Wrapping up this member like this is better than deriving from\n   * the Compare object ourselves (there are some perverse edge cases\n   * involving virtual functions).\n   *\n   * More info:  http://www.cantrip.org/emptyopt.html\n   */\n  struct EBO : Compare {\n    explicit EBO(const Compare& c, const Allocator& alloc) noexcept(\n        std::is_nothrow_default_constructible<Container>::value)\n        : Compare(c), cont_(alloc) {}\n    EBO(const EBO& other, const Allocator& alloc) noexcept(\n        std::is_nothrow_constructible<\n            Container,\n            const Container&,\n            const Allocator&>::value)\n        : Compare(static_cast<const Compare&>(other)),\n          cont_(other.cont_, alloc) {}\n    EBO(EBO&& other, const Allocator& alloc) noexcept(\n        std::is_nothrow_constructible<\n            Container,\n            Container&&,\n            const Allocator&>::value)\n        : Compare(static_cast<Compare&&>(other)),\n          cont_(std::move(other.cont_), alloc) {}\n    EBO(const Compare& c, Container&& cont) noexcept(\n        std::is_nothrow_move_constructible<Container>::value)\n        : Compare(c), cont_(std::move(cont)) {}\n    Container cont_;\n  } m_;\n\n  template <typename Self>\n  using self_iterator_t = _t<\n      std::conditional<std::is_const<Self>::value, const_iterator, iterator>>;\n\n  template <typename Self, typename K>\n  static self_iterator_t<Self> find_(Self& self, K const& key) {\n    auto end = self.end();\n    auto it = self.lower_bound(key);\n    if (it == end || !self.key_comp()(key, *it)) {\n      return it;\n    }\n    return end;\n  }\n  template <typename Self, typename K>\n  static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> lower_bound2_(\n      Self& self, K const& key1, K const& key2) {\n    auto len = self.size();\n    auto first = self.begin(), second = self.begin();\n    auto c = self.key_comp();\n    assert(!c(key2, key1));\n    while (true) {\n      if (len == 0) {\n        return std::make_pair(first, first);\n      }\n      auto half = len / 2;\n      auto middle = first + half;\n      if (c(*middle, key1)) {\n        first = middle + 1;\n        half = len - half - 1;\n      } else if (c(*middle, key2)) {\n        second = middle + (len & 1);\n        len = half;\n        break;\n      }\n      len = half;\n    }\n    while (len) {\n      auto half = len / 2;\n      auto middle1 = first + half;\n      auto middle2 = second + half;\n      if (c(*middle1, key1)) {\n        first = middle1 + (len & 1);\n      }\n      if (c(*middle2, key2)) {\n        second = middle2 + (len & 1);\n      }\n      len = half;\n    }\n    return std::make_pair(first, second);\n  }\n\n  template <typename Self, typename K>\n  static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> find2_(\n      Self& self, K const& key1, K const& key2) {\n    auto end = self.end();\n    auto its = lower_bound2_(self, key1, key2);\n    if (its.second != end) {\n      if (self.key_comp()(key1, *its.first)) {\n        its.first = end;\n      }\n      if (self.key_comp()(key2, *its.second)) {\n        its.second = end;\n      }\n    } else if (its.first != end && self.key_comp()(key1, *its.first)) {\n      its.first = end;\n    }\n    return its;\n  }\n};\n\n// Swap function that can be found using ADL.\ntemplate <class T, class C, class A, class G>\ninline void swap(\n    sorted_vector_set<T, C, A, G>& a, sorted_vector_set<T, C, A, G>& b) {\n  return a.swap(b);\n}\n\ntemplate <typename T>\ninline constexpr bool is_sorted_vector_set_v =\n    is_instantiation_of_v<sorted_vector_set, T>;\n\ntemplate <typename T>\nstruct is_sorted_vector_set : std::bool_constant<is_sorted_vector_set_v<T>> {};\n\ntemplate <\n    class T,\n    size_t N = 1,\n    class Compare = std::less<T>,\n    class Allocator = std::allocator<T>,\n    class GrowthPolicy = void,\n    class SmallVectorPolicy = void>\nusing small_sorted_vector_set = sorted_vector_set<\n    T,\n    Compare,\n    Allocator,\n    GrowthPolicy,\n    folly::small_vector<T, N, SmallVectorPolicy>>;\n\ntemplate <typename T>\ninline constexpr bool is_small_sorted_vector_set_v =\n    is_sorted_vector_set_v<T> && is_small_vector_v<typename T::container_type>;\n\ntemplate <typename T>\nstruct is_small_sorted_vector_set\n    : std::bool_constant<is_small_sorted_vector_set_v<T>> {};\n\n#if FOLLY_HAS_MEMORY_RESOURCE\n\nnamespace pmr {\n\ntemplate <\n    class T,\n    class Compare = std::less<T>,\n    class GrowthPolicy = void,\n    class Container = std::vector<T, std::pmr::polymorphic_allocator<T>>>\nusing sorted_vector_set = folly::sorted_vector_set<\n    T,\n    Compare,\n    std::pmr::polymorphic_allocator<T>,\n    GrowthPolicy,\n    Container>;\n\n} // namespace pmr\n\n#endif\n\n//////////////////////////////////////////////////////////////////////\n\n/**\n * A sorted_vector_map is similar to a sorted_vector_set but stores\n * <key,value> pairs instead of single elements.\n *\n * @tparam Key           Key type\n * @tparam Value         Value type\n * @tparam Compare       Function that can compare key types and impose\n *                            a strict weak ordering over them.\n * @tparam Allocator     allocation policy\n * @tparam GrowthPolicy  policy object to control growth\n */\ntemplate <\n    class Key,\n    class Value,\n    class Compare = std::less<Key>,\n    class Allocator = std::allocator<std::pair<Key, Value>>,\n    class GrowthPolicy = void,\n    class Container = std::vector<std::pair<Key, Value>, Allocator>>\nclass sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> {\n  detail::growth_policy_wrapper<GrowthPolicy>& get_growth_policy() {\n    return *this;\n  }\n\n  template <typename K, typename V, typename C = Compare>\n  using if_is_transparent =\n      _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>;\n\n  struct EBO;\n\n public:\n  using key_type = Key;\n  using mapped_type = Value;\n  using value_type = typename Container::value_type;\n  using key_compare = Compare;\n  using allocator_type = Allocator;\n  using container_type = Container;\n\n  struct value_compare : private Compare {\n    bool operator()(const value_type& a, const value_type& b) const {\n      return Compare::operator()(a.first, b.first);\n    }\n\n   protected:\n    friend class sorted_vector_map;\n    explicit value_compare(const Compare& c) : Compare(c) {}\n  };\n\n  using pointer = typename Container::pointer;\n  using const_pointer = typename Container::const_pointer;\n  using reference = typename Container::reference;\n  using const_reference = typename Container::const_reference;\n  using iterator = typename Container::iterator;\n  using const_iterator = typename Container::const_iterator;\n  using difference_type = typename Container::difference_type;\n  using size_type = typename Container::size_type;\n  using reverse_iterator = typename Container::reverse_iterator;\n  using const_reverse_iterator = typename Container::const_reverse_iterator;\n  using direct_mutation_guard =\n      detail::DirectMutationGuard<Container, value_compare>;\n\n  sorted_vector_map() noexcept(\n      std::is_nothrow_constructible<EBO, value_compare, Allocator>::value)\n      : m_(value_compare(Compare()), Allocator()) {}\n\n  sorted_vector_map(const sorted_vector_map&) = default;\n\n  sorted_vector_map(const sorted_vector_map& other, const Allocator& alloc)\n      : m_(other.m_, alloc) {}\n\n  sorted_vector_map(sorted_vector_map&&) = default;\n\n  sorted_vector_map(sorted_vector_map&& other, const Allocator& alloc) noexcept(\n      std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value)\n      : m_(std::move(other.m_), alloc) {}\n\n  explicit sorted_vector_map(const Allocator& alloc)\n      : m_(value_compare(Compare()), alloc) {}\n\n  explicit sorted_vector_map(\n      const Compare& comp, const Allocator& alloc = Allocator())\n      : m_(value_compare(comp), alloc) {}\n\n  template <class InputIterator>\n  explicit sorted_vector_map(\n      InputIterator first,\n      InputIterator last,\n      const Compare& comp = Compare(),\n      const Allocator& alloc = Allocator())\n      : m_(value_compare(comp), alloc) {\n    insert(first, last);\n  }\n\n  template <class InputIterator>\n  sorted_vector_map(\n      InputIterator first, InputIterator last, const Allocator& alloc)\n      : m_(value_compare(Compare()), alloc) {\n    insert(first, last);\n  }\n\n  /* implicit */ sorted_vector_map(\n      std::initializer_list<value_type> list,\n      const Compare& comp = Compare(),\n      const Allocator& alloc = Allocator())\n      : m_(value_compare(comp), alloc) {\n    insert(list.begin(), list.end());\n  }\n\n  sorted_vector_map(\n      std::initializer_list<value_type> list, const Allocator& alloc)\n      : m_(value_compare(Compare()), alloc) {\n    insert(list.begin(), list.end());\n  }\n\n  // Construct a sorted_vector_map by stealing the storage of a prefilled\n  // container. The container need not be sorted already. This supports\n  // bulk construction of sorted_vector_map with zero allocations, not counting\n  // those performed by the caller. (The iterator range constructor performs at\n  // least one allocation).\n  //\n  // Note that `sorted_vector_map(const Container& container)` is not provided,\n  // since the purpose of this constructor is to avoid an unnecessary copy.\n  explicit sorted_vector_map(\n      Container&& container, const Compare& comp = Compare())\n      : sorted_vector_map(\n            sorted_unique,\n            detail::as_sorted_unique(std::move(container), value_compare(comp)),\n            comp) {}\n\n  // Construct a sorted_vector_map by stealing the storage of a prefilled\n  // container. Its elements must be sorted and unique, as sorted_unique_t\n  // hints. Supports bulk construction of sorted_vector_map with zero\n  // allocations, not counting those performed by the caller. (The iterator\n  // range constructor performs at least one allocation).\n  //\n  // Note that `sorted_vector_map(sorted_unique_t, const Container& container)`\n  // is not provided, since the purpose of this constructor is to avoid an extra\n  // copy.\n  sorted_vector_map(\n      sorted_unique_t,\n      Container&& container,\n      const Compare& comp =\n          Compare()) noexcept(std::\n                                  is_nothrow_constructible<\n                                      EBO,\n                                      value_compare,\n                                      Container&&>::value)\n      : m_(value_compare(comp), std::move(container)) {\n    assert(\n        detail::is_sorted_unique(\n            m_.cont_.begin(), m_.cont_.end(), value_comp()));\n  }\n\n  Allocator get_allocator() const { return m_.cont_.get_allocator(); }\n\n  const Container& get_container() const noexcept { return m_.cont_; }\n\n  /**\n   * Directly mutate the container.\n   *\n   * Get a guarded reference to the underlying container for direct mutation.\n   * sorted_unique_t signals that user will make sure that after the\n   * modification the container will have its values as sorted-unique\n   * (conforming to container's value_comp). Violating this assumption will\n   * result in undefined behavior.\n   *\n   * This function is not safe to use concurrently with other functions.\n   */\n  direct_mutation_guard get_container_for_direct_mutation(\n      sorted_unique_t) noexcept {\n    return direct_mutation_guard{\n        m_.cont_, value_comp(), /* range_is_sorted_unique */ true};\n  }\n\n  /**\n   * Directly mutate the container.\n   *\n   * Get a guarded reference to the underlying container for direct mutation.\n   * The container will initially be sorted and unique. You are not required to\n   * maintain the sorted-unique invariant while mutating. When the guard is\n   * released, it will sort and unique-ify the container.\n   *\n   * This function is not safe to use concurrently with other functions.\n   */\n  direct_mutation_guard get_container_for_direct_mutation() noexcept {\n    return direct_mutation_guard{\n        m_.cont_, value_comp(), /* range_is_sorted_unique */ false};\n  }\n\n  /**\n   * Directly swap the container. Similar to swap()\n   */\n  void swap_container(Container& newContainer) {\n    detail::as_sorted_unique(newContainer, value_comp());\n    using std::swap;\n    swap(m_.cont_, newContainer);\n  }\n  void swap_container(sorted_unique_t, Container& newContainer) {\n    assert(\n        detail::is_sorted_unique(\n            newContainer.begin(), newContainer.end(), value_comp()));\n    using std::swap;\n    swap(m_.cont_, newContainer);\n  }\n\n  sorted_vector_map& operator=(const sorted_vector_map& other) = default;\n\n  sorted_vector_map& operator=(sorted_vector_map&& other) = default;\n\n  sorted_vector_map& operator=(std::initializer_list<value_type> ilist) {\n    clear();\n    insert(ilist.begin(), ilist.end());\n    return *this;\n  }\n\n  key_compare key_comp() const { return m_; }\n  value_compare value_comp() const { return m_; }\n\n  iterator begin() { return m_.cont_.begin(); }\n  iterator end() { return m_.cont_.end(); }\n  const_iterator cbegin() const { return m_.cont_.cbegin(); }\n  const_iterator begin() const { return m_.cont_.begin(); }\n  const_iterator cend() const { return m_.cont_.cend(); }\n  const_iterator end() const { return m_.cont_.end(); }\n  reverse_iterator rbegin() { return m_.cont_.rbegin(); }\n  reverse_iterator rend() { return m_.cont_.rend(); }\n  const_reverse_iterator crbegin() const { return m_.cont_.crbegin(); }\n  const_reverse_iterator rbegin() const { return m_.cont_.rbegin(); }\n  const_reverse_iterator crend() const { return m_.cont_.crend(); }\n  const_reverse_iterator rend() const { return m_.cont_.rend(); }\n\n  void clear() { return m_.cont_.clear(); }\n  size_type size() const { return m_.cont_.size(); }\n  size_type max_size() const { return m_.cont_.max_size(); }\n  bool empty() const { return m_.cont_.empty(); }\n  void reserve(size_type s) { return m_.cont_.reserve(s); }\n  void shrink_to_fit() { m_.cont_.shrink_to_fit(); }\n  size_type capacity() const { return m_.cont_.capacity(); }\n\n  std::pair<iterator, bool> insert(const value_type& value) {\n    iterator it = lower_bound(value.first);\n    if (it == end() || value_comp()(value, *it)) {\n      it = get_growth_policy().increase_capacity(m_.cont_, it);\n      return std::make_pair(m_.cont_.emplace(it, value), true);\n    }\n    return std::make_pair(it, false);\n  }\n\n  std::pair<iterator, bool> insert(value_type&& value) {\n    iterator it = lower_bound(value.first);\n    if (it == end() || value_comp()(value, *it)) {\n      it = get_growth_policy().increase_capacity(m_.cont_, it);\n      return std::make_pair(m_.cont_.emplace(it, std::move(value)), true);\n    }\n    return std::make_pair(it, false);\n  }\n\n  iterator insert(const_iterator hint, const value_type& value) {\n    return detail::insert_with_hint(\n        *this, m_.cont_, hint, value, get_growth_policy());\n  }\n\n  iterator insert(const_iterator hint, value_type&& value) {\n    return detail::insert_with_hint(\n        *this, m_.cont_, hint, std::move(value), get_growth_policy());\n  }\n\n  template <class InputIterator>\n  void insert(InputIterator first, InputIterator last) {\n    detail::bulk_insert(*this, m_.cont_, first, last);\n  }\n\n  // If [first, last) is known to be sorted and unique according to the\n  // comparator (for example if the range comes from a sorted container of the\n  // same type) this version can save unnecessary operations, especially if\n  // *this is empty.\n  template <class InputIterator>\n  void insert(sorted_unique_t, InputIterator first, InputIterator last) {\n    detail::bulk_insert(\n        *this, m_.cont_, first, last, /* range_is_sorted_unique */ true);\n  }\n\n  void insert(std::initializer_list<value_type> ilist) {\n    insert(ilist.begin(), ilist.end());\n  }\n\n  // emplace isn't better than insert for sorted_vector_map, but aids\n  // compatibility\n  template <typename... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    folly::aligned_storage_for_t<value_type> b;\n    value_type* p = static_cast<value_type*>(static_cast<void*>(&b));\n    auto a = get_allocator();\n    std::allocator_traits<allocator_type>::construct(\n        a, p, std::forward<Args>(args)...);\n    auto g = makeGuard([&]() {\n      std::allocator_traits<allocator_type>::destroy(a, p);\n    });\n    return insert(std::move(*p));\n  }\n\n  std::pair<iterator, bool> emplace(const value_type& value) {\n    return insert(value);\n  }\n\n  std::pair<iterator, bool> emplace(value_type&& value) {\n    return insert(std::move(value));\n  }\n\n  // emplace_hint isn't better than insert for sorted_vector_set, but aids\n  // compatibility\n  template <typename... Args>\n  iterator emplace_hint(const_iterator hint, Args&&... args) {\n    folly::aligned_storage_for_t<value_type> b;\n    value_type* p = static_cast<value_type*>(static_cast<void*>(&b));\n    auto a = get_allocator();\n    std::allocator_traits<allocator_type>::construct(\n        a, p, std::forward<Args>(args)...);\n    auto g = makeGuard([&]() {\n      std::allocator_traits<allocator_type>::destroy(a, p);\n    });\n    return insert(hint, std::move(*p));\n  }\n\n  iterator emplace_hint(const_iterator hint, const value_type& value) {\n    return insert(hint, value);\n  }\n\n  iterator emplace_hint(const_iterator hint, value_type&& value) {\n    return insert(hint, std::move(value));\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {\n    return try_emplace_impl(std::move(k), std::forward<Args>(args)...);\n  }\n\n  template <typename... Args>\n  std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {\n    return try_emplace_impl(k, std::forward<Args>(args)...);\n  }\n\n  template <typename M>\n  std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {\n    auto itAndInserted = try_emplace(k, std::forward<M>(obj));\n    if (!itAndInserted.second) {\n      itAndInserted.first->second = std::forward<M>(obj);\n    }\n    return itAndInserted;\n  }\n\n  template <typename M>\n  std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {\n    auto itAndInserted = try_emplace(std::move(k), std::forward<M>(obj));\n    if (!itAndInserted.second) {\n      itAndInserted.first->second = std::forward<M>(obj);\n    }\n    return itAndInserted;\n  }\n\n  template <class M>\n  iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {\n    return insert_or_assign_impl(hint, k, std::forward<M>(obj));\n  }\n\n  template <class M>\n  iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {\n    return insert_or_assign_impl(hint, std::move(k), std::forward<M>(obj));\n  }\n\n  size_type erase(const key_type& key) {\n    iterator it = find(key);\n    if (it == end()) {\n      return 0;\n    }\n    m_.cont_.erase(it);\n    return 1;\n  }\n\n  iterator erase(const_iterator it) { return m_.cont_.erase(it); }\n\n  iterator erase(const_iterator first, const_iterator last) {\n    return m_.cont_.erase(first, last);\n  }\n\n  template <class Predicate>\n  friend size_type erase_if(sorted_vector_map& container, Predicate predicate) {\n    auto& c = container.m_.cont_;\n    const auto preEraseSize = c.size();\n    c.erase(std::remove_if(c.begin(), c.end(), std::ref(predicate)), c.end());\n    return preEraseSize - c.size();\n  }\n\n  iterator find(const key_type& key) { return find_(*this, key); }\n\n  const_iterator find(const key_type& key) const { return find_(*this, key); }\n\n  template <typename K>\n  if_is_transparent<K, iterator> find(const K& key) {\n    return find_(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> find(const K& key) const {\n    return find_(*this, key);\n  }\n\n  std::pair<iterator, iterator> find(\n      const key_type& key1, const key_type& key2) {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  std::pair<const_iterator, const_iterator> find(\n      const key_type& key1, const key_type& key2) const {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  template <typename K>\n  std::pair<if_is_transparent<K, iterator>, if_is_transparent<K, iterator>>\n  find(const K& key1, const K& key2) {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  template <typename K>\n  std::pair<\n      if_is_transparent<K, const_iterator>,\n      if_is_transparent<K, const_iterator>>\n  find(const K& key1, const K& key2) const {\n    if (key_comp()(key2, key1)) {\n      auto iterators = find2_(*this, key2, key1);\n      access::swap(iterators.first, iterators.second);\n      return iterators;\n    } else {\n      return find2_(*this, key1, key2);\n    }\n  }\n\n  mapped_type& at(const key_type& key) {\n    iterator it = find(key);\n    if (it != end()) {\n      return it->second;\n    }\n    throw_exception<std::out_of_range>(\"sorted_vector_map::at\");\n  }\n\n  const mapped_type& at(const key_type& key) const {\n    const_iterator it = find(key);\n    if (it != end()) {\n      return it->second;\n    }\n    throw_exception<std::out_of_range>(\"sorted_vector_map::at\");\n  }\n\n  size_type count(const key_type& key) const {\n    return find(key) == end() ? 0 : 1;\n  }\n\n  template <typename K>\n  if_is_transparent<K, size_type> count(const K& key) const {\n    return find(key) == end() ? 0 : 1;\n  }\n\n  bool contains(const key_type& key) const { return find(key) != end(); }\n\n  template <typename K>\n  if_is_transparent<K, bool> contains(const K& key) const {\n    return find(key) != end();\n  }\n\n  iterator lower_bound(const key_type& key) { return lower_bound(*this, key); }\n\n  const_iterator lower_bound(const key_type& key) const {\n    return lower_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, iterator> lower_bound(const K& key) {\n    return lower_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> lower_bound(const K& key) const {\n    return lower_bound(*this, key);\n  }\n\n  iterator upper_bound(const key_type& key) { return upper_bound(*this, key); }\n\n  const_iterator upper_bound(const key_type& key) const {\n    return upper_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, iterator> upper_bound(const K& key) {\n    return upper_bound(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, const_iterator> upper_bound(const K& key) const {\n    return upper_bound(*this, key);\n  }\n\n  std::pair<iterator, iterator> equal_range(const key_type& key) {\n    return equal_range(*this, key);\n  }\n\n  std::pair<const_iterator, const_iterator> equal_range(\n      const key_type& key) const {\n    return equal_range(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, std::pair<iterator, iterator>> equal_range(\n      const K& key) {\n    return equal_range(*this, key);\n  }\n\n  template <typename K>\n  if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range(\n      const K& key) const {\n    return equal_range(*this, key);\n  }\n\n  // Nothrow as long as swap() on the Compare type is nothrow.\n  void swap(sorted_vector_map& o) {\n    using std::swap; // Allow ADL for swap(); fall back to std::swap().\n    Compare& a = m_;\n    Compare& b = o.m_;\n    swap(a, b);\n    m_.cont_.swap(o.m_.cont_);\n  }\n\n  mapped_type& operator[](const key_type& key) {\n    iterator it = lower_bound(key);\n    if (it == end() || key_comp()(key, it->first)) {\n      return insert(it, value_type(key, mapped_type()))->second;\n    }\n    return it->second;\n  }\n\n  bool operator==(const sorted_vector_map& other) const {\n    return m_.cont_ == other.m_.cont_;\n  }\n  bool operator!=(const sorted_vector_map& other) const {\n    return !operator==(other);\n  }\n\n  bool operator<(const sorted_vector_map& other) const {\n    return m_.cont_ < other.m_.cont_;\n  }\n  bool operator>(const sorted_vector_map& other) const { return other < *this; }\n  bool operator<=(const sorted_vector_map& other) const {\n    return !operator>(other);\n  }\n  bool operator>=(const sorted_vector_map& other) const {\n    return !operator<(other);\n  }\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_impl_three_way_comparison)\n  template <typename U = Container>\n  friend auto operator<=>(\n      const sorted_vector_map& lhs, const sorted_vector_map& rhs)\n      -> decltype(std::declval<const U&>() <=> std::declval<const U&>()) {\n    return lhs.m_.cont_ <=> rhs.m_.cont_;\n  }\n#endif // FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_impl_three_way_comparison)\n\n  const value_type* data() const noexcept { return m_.cont_.data(); }\n\n private:\n  // This is to get the empty base optimization; see the comment in\n  // sorted_vector_set.\n  struct EBO : value_compare {\n    explicit EBO(const value_compare& c, const Allocator& alloc) noexcept(\n        std::is_nothrow_default_constructible<Container>::value)\n        : value_compare(c), cont_(alloc) {}\n    EBO(const EBO& other, const Allocator& alloc) noexcept(\n        std::is_nothrow_constructible<\n            Container,\n            const Container&,\n            const Allocator&>::value)\n        : value_compare(static_cast<const value_compare&>(other)),\n          cont_(other.cont_, alloc) {}\n    EBO(EBO&& other, const Allocator& alloc) noexcept(\n        std::is_nothrow_constructible<\n            Container,\n            Container&&,\n            const Allocator&>::value)\n        : value_compare(static_cast<value_compare&&>(other)),\n          cont_(std::move(other.cont_), alloc) {}\n    EBO(const Compare& c, Container&& cont) noexcept(\n        std::is_nothrow_move_constructible<Container>::value)\n        : value_compare(c), cont_(std::move(cont)) {}\n    Container cont_;\n  } m_;\n\n  template <typename Self>\n  using self_iterator_t = _t<\n      std::conditional<std::is_const<Self>::value, const_iterator, iterator>>;\n\n  template <typename Self, typename K>\n  static self_iterator_t<Self> find_(Self& self, K const& key) {\n    auto end = self.end();\n    auto it = self.lower_bound(key);\n    if (it == end || !self.key_comp()(key, it->first)) {\n      return it;\n    }\n    return end;\n  }\n\n  template <typename Self, typename K>\n  static self_iterator_t<Self> lower_bound(Self& self, K const& key) {\n    auto f = [c = self.key_comp()](auto const& a, K const& b) {\n      return c(a.first, b);\n    };\n    return std::lower_bound(self.begin(), self.end(), key, f);\n  }\n\n  template <typename Self, typename K>\n  static self_iterator_t<Self> upper_bound(Self& self, K const& key) {\n    auto f = [c = self.key_comp()](K const& a, auto const& b) {\n      return c(a, b.first);\n    };\n    return std::upper_bound(self.begin(), self.end(), key, f);\n  }\n\n  template <typename Self, typename K>\n  static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> equal_range(\n      Self& self, K const& key) {\n    // Note: std::equal_range can't be passed a functor that takes\n    // argument types different from the iterator value_type, so we\n    // have to do this.\n    return {lower_bound(self, key), upper_bound(self, key)};\n  }\n\n  template <typename Self, typename K>\n  static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> lower_bound2_(\n      Self& self, K const& key1, K const& key2) {\n    auto len = self.size();\n    auto first = self.begin(), second = self.begin();\n    auto c = self.key_comp();\n    assert(!c(key2, key1));\n    while (true) {\n      if (len == 0) {\n        return std::make_pair(first, first);\n      }\n      auto half = len / 2;\n      auto middle = first + half;\n      if (c(middle->first, key1)) {\n        first = middle + 1;\n        half = len - half - 1;\n      } else if (c(middle->first, key2)) {\n        second = middle + (len & 1);\n        len = half;\n        break;\n      }\n      len = half;\n    }\n    while (len) {\n      auto half = len / 2;\n      auto middle1 = first + half;\n      auto middle2 = second + half;\n      if (c(middle1->first, key1)) {\n        first = middle1 + (len & 1);\n      }\n      if (c(middle2->first, key2)) {\n        second = middle2 + (len & 1);\n      }\n      len = half;\n    }\n    return std::make_pair(first, second);\n  }\n\n  template <typename Self, typename K>\n  static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> find2_(\n      Self& self, K const& key1, K const& key2) {\n    auto end = self.end();\n    auto its = lower_bound2_(self, key1, key2);\n    if (its.second != end) {\n      if (self.key_comp()(key1, its.first->first)) {\n        its.first = end;\n      }\n      if (self.key_comp()(key2, its.second->first)) {\n        its.second = end;\n      }\n    } else if (its.first != end && self.key_comp()(key1, its.first->first)) {\n      its.first = end;\n    }\n    return its;\n  }\n\n  template <typename K, typename... Args>\n  std::pair<iterator, bool> try_emplace_impl(K&& key, Args&&... args) {\n    iterator it = lower_bound(key);\n    if (it == end() || key_comp()(key, it->first)) {\n      return std::make_pair(\n          emplace_hint(\n              it,\n              std::piecewise_construct,\n              std::forward_as_tuple(std::forward<K>(key)),\n              std::forward_as_tuple(std::forward<Args>(args)...)),\n          true);\n    }\n    return std::make_pair(it, false);\n  }\n\n  template <class K, class M>\n  iterator insert_or_assign_impl(const_iterator hint, K&& k, M&& obj) {\n    if (hint == end() || key_comp()(k, hint->first)) {\n      if (hint == begin() || key_comp()((hint - 1)->first, k)) {\n        auto it = get_growth_policy().increase_capacity(m_.cont_, hint);\n        return m_.cont_.emplace(\n            it, std::make_pair(std::forward<K>(k), std::forward<M>(obj)));\n      } else {\n        return insert_or_assign(std::forward<K>(k), std::forward<M>(obj)).first;\n      }\n    }\n\n    if (key_comp()(hint->first, k)) {\n      if (hint + 1 == end() || key_comp()(k, (hint + 1)->first)) {\n        auto it = get_growth_policy().increase_capacity(m_.cont_, hint + 1);\n        return m_.cont_.emplace(\n            it, std::make_pair(std::forward<K>(k), std::forward<M>(obj)));\n      } else {\n        return insert_or_assign(std::forward<K>(k), std::forward<M>(obj)).first;\n      }\n    }\n\n    // Value and *hint did not compare, so they are equal keys.\n    auto it = begin() + std::distance(cbegin(), hint);\n    it->second = std::forward<M>(obj);\n    return it;\n  }\n};\n\n// Swap function that can be found using ADL.\ntemplate <class K, class V, class C, class A, class G>\ninline void swap(\n    sorted_vector_map<K, V, C, A, G>& a, sorted_vector_map<K, V, C, A, G>& b) {\n  return a.swap(b);\n}\n\ntemplate <typename T>\ninline constexpr bool is_sorted_vector_map_v =\n    is_instantiation_of_v<sorted_vector_map, T>;\n\ntemplate <typename T>\nstruct is_sorted_vector_map : std::bool_constant<is_sorted_vector_map_v<T>> {};\n\ntemplate <\n    class Key,\n    class Value,\n    size_t N = 1,\n    class Compare = std::less<Key>,\n    class Allocator = std::allocator<std::pair<Key, Value>>,\n    class GrowthPolicy = void,\n    class SmallVectorPolicy = void>\nusing small_sorted_vector_map = sorted_vector_map<\n    Key,\n    Value,\n    Compare,\n    Allocator,\n    GrowthPolicy,\n    folly::small_vector<std::pair<Key, Value>, N, SmallVectorPolicy>>;\n\ntemplate <typename T>\ninline constexpr bool is_small_sorted_vector_map_v =\n    is_sorted_vector_map_v<T> && is_small_vector_v<typename T::container_type>;\n\ntemplate <typename T>\nstruct is_small_sorted_vector_map\n    : std::bool_constant<is_small_sorted_vector_map_v<T>> {};\n\n#if FOLLY_HAS_MEMORY_RESOURCE\n\nnamespace pmr {\n\ntemplate <\n    class Key,\n    class Value,\n    class Compare = std::less<Key>,\n    class GrowthPolicy = void,\n    class Container = std::vector<\n        std::pair<Key, Value>,\n        std::pmr::polymorphic_allocator<std::pair<Key, Value>>>>\nusing sorted_vector_map = folly::sorted_vector_map<\n    Key,\n    Value,\n    Compare,\n    std::pmr::polymorphic_allocator<std::pair<Key, Value>>,\n    GrowthPolicy,\n    Container>;\n\n} // namespace pmr\n\n#endif\n\n//////////////////////////////////////////////////////////////////////\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/span.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cassert>\n#include <cstddef>\n#include <limits>\n#include <type_traits>\n\n#include <folly/CppAttributes.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/container/Access.h>\n#include <folly/container/Iterator.h>\n#include <folly/functional/Invoke.h>\n#include <folly/portability/Constexpr.h>\n\n#if __cpp_lib_span >= 202002L\n#include <span>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\nnamespace fallback_span {\n\ninline constexpr auto dynamic_extent = std::size_t(-1);\n\ntemplate <std::size_t N>\nstruct span_extent {\n  constexpr span_extent() = default;\n  explicit constexpr span_extent(\n      [[maybe_unused]] std::size_t const e) noexcept {\n    assert(e == N);\n  }\n  constexpr span_extent(span_extent const&) = default;\n  constexpr span_extent& operator=(span_extent const&) = default;\n\n  /* implicit */ constexpr operator std::size_t() const noexcept { return N; }\n};\n\ntemplate <>\nstruct span_extent<dynamic_extent> {\n  std::size_t extent{};\n\n  constexpr span_extent() = default;\n  explicit constexpr span_extent(std::size_t const e) noexcept : extent{e} {}\n  constexpr span_extent(span_extent const&) = default;\n  constexpr span_extent& operator=(span_extent const&) = default;\n\n  /* implicit */ constexpr operator std::size_t() const noexcept {\n    return extent;\n  }\n};\n\n/// span\n///\n/// mimic: std::span, C++20\ntemplate <typename T, std::size_t Extent = dynamic_extent>\nclass span {\n public:\n  static_assert(!std::is_reference_v<T>);\n  static_assert(!std::is_void_v<T>);\n  static_assert(!std::is_function_v<T>);\n  static_assert(sizeof(T) < size_t(-1));\n  static_assert(!std::is_abstract_v<T>);\n\n  using element_type = T;\n  using value_type = std::remove_cv_t<element_type>;\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n  using pointer = element_type*;\n  using const_pointer = element_type const*;\n  using reference = element_type&;\n  using const_reference = element_type const&;\n  using iterator = pointer;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n\n  static inline constexpr std::size_t extent = Extent;\n\n private:\n  template <bool C>\n  using if_ = std::enable_if_t<C, int>;\n\n  template <typename U, typename V = std::remove_cv_t<U>>\n  static inline constexpr bool array_element_match_v =\n      std::is_same_v<V, value_type> && std::is_convertible_v<U*, pointer>;\n\n  template <\n      typename Rng,\n      typename Size = remove_cvref_t<invoke_result_t<access::size_fn, Rng>>,\n      typename Data = invoke_result_t<access::data_fn, Rng>,\n      typename U = std::remove_pointer_t<Data>>\n  static constexpr bool is_range_v =\n      !std::is_same_v<bool, Size> && std::is_unsigned_v<Size> &&\n      std::is_pointer_v<Data> && array_element_match_v<U>;\n\n  static constexpr size_type subspan_extent(\n      size_type const offset, size_type const count) {\n    // clang-format off\n    return\n        count != dynamic_extent ? count :\n        extent != dynamic_extent ? extent - offset :\n        dynamic_extent;\n    // clang-format on\n  }\n\n  pointer data_;\n  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] span_extent<extent> extent_;\n\n public:\n  template <size_type E = extent, if_<E == dynamic_extent || E == 0> = 0>\n  constexpr span() noexcept : data_{}, extent_{} {}\n\n  constexpr span(pointer const first, size_type const count)\n      : data_{first}, extent_{count} {}\n\n  constexpr span(pointer const first, pointer const last)\n      : data_{first}, extent_{to_unsigned(last - first)} {\n    assert(!(last < first));\n  }\n\n  template <\n      std::size_t N,\n      std::size_t E = extent,\n      if_<E == dynamic_extent || E == N> = 0>\n  /* implicit */ constexpr span(type_t<element_type> (&arr)[N]) noexcept\n      : data_{arr}, extent_{N} {}\n\n  template <\n      typename U,\n      std::size_t N,\n      std::size_t E = extent,\n      if_<E == dynamic_extent || E == N> = 0,\n      if_<array_element_match_v<U>> = 0>\n  /* implicit */ constexpr span(std::array<U, N>& arr) noexcept\n      : data_{arr.data()}, extent_{N} {}\n\n  template <\n      typename U,\n      std::size_t N,\n      std::size_t E = extent,\n      if_<E == dynamic_extent || E == N> = 0,\n      if_<array_element_match_v<U const>> = 0>\n  /* implicit */ constexpr span(std::array<U, N> const& arr) noexcept\n      : data_{arr.data()}, extent_{N} {}\n\n  template <typename Rng, if_<is_range_v<Rng&>> = 0>\n  /* implicit */ constexpr span(Rng&& range)\n      : data_{access::data(range)}, extent_{access::size(range)} {}\n\n  constexpr span(span const&) = default;\n\n  constexpr span& operator=(span const&) = default;\n\n  constexpr pointer data() const noexcept { return data_; }\n  constexpr size_type size() const noexcept { return extent_; }\n  constexpr size_type size_bytes() const noexcept {\n    return size() * sizeof(element_type);\n  }\n  constexpr bool empty() const noexcept { return size() == 0; }\n\n  constexpr iterator begin() const noexcept { return data_; }\n  constexpr iterator end() const noexcept { return data_ + size(); }\n  constexpr reverse_iterator rbegin() const noexcept {\n    return std::make_reverse_iterator(end());\n  }\n  constexpr reverse_iterator rend() const noexcept {\n    return std::make_reverse_iterator(begin());\n  }\n\n  constexpr reference front() const {\n    assert(!empty());\n    return data_[0];\n  }\n  constexpr reference back() const {\n    assert(!empty());\n    return data_[size() - 1];\n  }\n  constexpr reference operator[](size_type const idx) const {\n    assert(idx < size());\n    return data_[idx];\n  }\n\n  template <\n      size_type Offset,\n      size_type Count = dynamic_extent,\n      typename...,\n      size_type E = subspan_extent(Offset, Count)>\n  constexpr span<element_type, E> subspan() const {\n    static_assert(!(Extent < Offset));\n    static_assert(Count == dynamic_extent || !(extent - Offset < Count));\n    assert(!(size() < Offset));\n    assert(Count == dynamic_extent || !(size() - Offset < Count));\n    return {data_ + Offset, Count == dynamic_extent ? size() - Offset : Count};\n  }\n\n  constexpr span<element_type, dynamic_extent> subspan(\n      size_type const offset, size_type const count = dynamic_extent) const {\n    assert(!(extent < offset));\n    assert(count == dynamic_extent || !(extent - offset < count));\n    assert(!(size() < offset));\n    assert(count == dynamic_extent || !(size() - offset < count));\n    return {data_ + offset, count == dynamic_extent ? size() - offset : count};\n  }\n\n  template <size_type Count>\n  constexpr span<element_type, Count> first() const {\n    static_assert(!(extent < Count));\n    assert(!(size() < Count));\n    return {data_, Count};\n  }\n  constexpr span<element_type, dynamic_extent> first(\n      size_type const count) const {\n    assert(!(extent < count));\n    assert(!(size() < count));\n    return {data_, count};\n  }\n\n  template <size_type Count>\n  constexpr span<element_type, Count> last() const {\n    static_assert(!(extent < Count));\n    assert(!(size() < Count));\n    return {data_ + size() - Count, Count};\n  }\n  constexpr span<element_type, dynamic_extent> last(\n      size_type const count) const {\n    assert(!(extent < count));\n    assert(!(size() < count));\n    return {data_ + size() - count, count};\n  }\n};\n\ntemplate <typename T, typename EndOrSize>\nspan(T*, EndOrSize) -> span<T>;\n\ntemplate <typename T, std::size_t N>\nspan(T (&)[N]) -> span<T, N>;\n\ntemplate <typename T, std::size_t N>\nspan(std::array<T, N>&) -> span<T, N>;\n\ntemplate <typename T, std::size_t N>\nspan(const std::array<T, N>&) -> span<const T, N>;\n\ntemplate <typename R>\nspan(R&&) -> span<std::remove_reference_t<\n    iterator_reference_t<decltype(std::begin(std::declval<R&>()))>>>;\n\n} // namespace fallback_span\n\n} // namespace detail\n\n#if __cpp_lib_span >= 202002L\n\nusing std::dynamic_extent;\nusing std::span;\n\n#else\n\nusing detail::fallback_span::dynamic_extent;\nusing detail::fallback_span::span;\n\n#endif\n\nnamespace detail {\n\nstruct span_cast_impl_fn {\n  template <\n      template <typename, std::size_t> class Span,\n      typename U,\n      typename T,\n      std::size_t Extent>\n  constexpr auto operator()(Span<T, Extent> in, U* castData) const {\n    assert(\n        static_cast<void const*>(in.data()) ==\n        static_cast<void const*>(castData));\n\n    // check alignment\n    if (!folly::is_constant_evaluated_or(true)) {\n      assert(reinterpret_cast<std::uintptr_t>(in.data()) % sizeof(U) == 0);\n    }\n\n    if constexpr (Extent == dynamic_extent) {\n      assert(in.size() * sizeof(T) % sizeof(U) == 0);\n      return Span<U, dynamic_extent>(\n          castData, in.size() * sizeof(T) / sizeof(U));\n    } else {\n      static_assert(Extent * sizeof(T) % sizeof(U) == 0);\n      constexpr std::size_t kResSize = Extent * sizeof(T) / sizeof(U);\n      return Span<U, kResSize>(castData, kResSize);\n    }\n  }\n};\n\ninline constexpr span_cast_impl_fn span_cast_impl;\n\n} // namespace detail\n\n/// static_span_cast\n/// static_span_cast_fn\n/// reinterpret_span_cast\n/// reinterpret_span_cast_fn\n/// const_span_cast\n/// const_span_cast_fn\n///\n/// Casts a span to a different span. The result is a span referring to the same\n/// region in memory but as a different type.\n///\n/// Example:\n///\n///   std::span<std::byte> bytes = ...\n///   std::span<int> ints = folly::reinterpret_span_cast<int>(bytes);\n\ntemplate <typename U>\nstruct static_span_cast_fn {\n  template <typename T, std::size_t Extent>\n  constexpr auto operator()(detail::fallback_span::span<T, Extent> in) const {\n    return detail::span_cast_impl(in, static_cast<U*>(in.data()));\n  }\n#if __cpp_lib_span >= 202002L\n  template <typename T, std::size_t Extent>\n  constexpr auto operator()(std::span<T, Extent> in) const {\n    return detail::span_cast_impl(in, static_cast<U*>(in.data()));\n  }\n#endif\n};\ntemplate <typename U>\ninline constexpr static_span_cast_fn<U> static_span_cast;\n\ntemplate <typename U>\nstruct reinterpret_span_cast_fn {\n  template <typename T, std::size_t Extent>\n  constexpr auto operator()(detail::fallback_span::span<T, Extent> in) const {\n    return detail::span_cast_impl(in, reinterpret_cast<U*>(in.data()));\n  }\n#if __cpp_lib_span >= 202002L\n  template <typename T, std::size_t Extent>\n  constexpr auto operator()(std::span<T, Extent> in) const {\n    return detail::span_cast_impl(in, reinterpret_cast<U*>(in.data()));\n  }\n#endif\n};\ntemplate <typename U>\ninline constexpr reinterpret_span_cast_fn<U> reinterpret_span_cast;\n\ntemplate <typename U>\nstruct const_span_cast_fn {\n  template <typename T, std::size_t Extent>\n  constexpr auto operator()(detail::fallback_span::span<T, Extent> in) const {\n    return detail::span_cast_impl(in, const_cast<U*>(in.data()));\n  }\n#if __cpp_lib_span >= 202002L\n  template <typename T, std::size_t Extent>\n  constexpr auto operator()(std::span<T, Extent> in) const {\n    return detail::span_cast_impl(in, const_cast<U*>(in.data()));\n  }\n#endif\n};\ntemplate <typename U>\ninline constexpr const_span_cast_fn<U> const_span_cast;\n\nnamespace detail {\n\nnamespace fallback_span {\n\n/// as_bytes\n///\n/// mimic: std::as_bytes, C++20\ntemplate <typename T, std::size_t Extent>\nauto as_bytes(span<T, Extent> s) noexcept {\n  return reinterpret_span_cast<std::byte const>(s);\n}\n\n/// as_writable_bytes\n///\n/// mimic: std::as_writable_bytes, C++20\ntemplate <\n    typename T,\n    std::size_t Extent,\n    std::enable_if_t<!std::is_const_v<T>, int> = 0>\nauto as_writable_bytes(span<T, Extent> s) noexcept {\n  return reinterpret_span_cast<std::byte>(s);\n}\n\n} // namespace fallback_span\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/tape.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/container/Iterator.h>\n#include <folly/container/detail/tape_detail.h>\n#include <folly/memory/UninitializedMemoryHacks.h>\n\n#include <algorithm>\n#include <cassert>\n#include <initializer_list>\n#include <iterator>\n#include <numeric>\n#include <string_view>\n#include <type_traits>\n#include <vector>\n\n#if defined(__cpp_lib_ranges)\n#include <ranges>\n#endif\n\nnamespace folly {\n\n#if defined(__cpp_lib_ranges)\n#define FOLLY_TAPE_CONTAINER_REQUIRES std::ranges::random_access_range\n#else\n#define FOLLY_TAPE_CONTAINER_REQUIRES typename\n#endif\n\n/* # Tape\n *\n * A container adapter, that builds a version of `vector<vector>` on top of a\n * random access underlying container.\n *\n * Instead of having a container of containers it's more efficient to have\n * a single container and store where the separators are.\n *\n * [string second string third string]\n *  ^      ^             ^\n *\n * One subrange of internal elements we call a `record`.\n *\n * You can `push` a new `record` or pop one from the back.\n * We also support an `erase` like `std::vector` but `insert` only for one\n * element. (there is no reason for limitation, except it's not implemented).\n *\n * NOTE: for when you don't have the `record` ready, you can use a\n * `record_builder` interface.\n *\n * Existing `records` can be accessed by index.\n * Existing `records` cannot be mutated, except for the last record (see record\n * builder).\n *\n * ## tape<tape>\n *\n * tape<tape> is supported, though not all of the APIs.\n * More apis can be implemented if/when needed.\n * Use `record_builder`.\n *\n * ## PERFORMANCE CHARACTERISTICS (folly/container/test/tape_bench):\n *\n * Reading (cache miss):\n * Container performs much better for access than vector<vector>/vector<string>\n * for cases where the data is out of cache.\n * If the data is in cache, reading is roughly the same.\n *\n * Construction\n * If you know for a fact that all the elements are fitting into SSO buffer,\n * and you always have complete records (not building) then `tape` does not help\n * you, or can even be a slight regression.\n *\n * Otherwise tape can give you good speedups, especially if you need to\n * `push_back` on individual records.\n *\n * Potential future perf improvements.\n * * it is possible to do a tape with one allocation for both metadata and\n *   data (in special cases).\n * * when converting indexes to pointers, compiler has to shift.\n *   For contigious containers we can store offsets in bytes.\n *\n * ## Exception safety\n * We provide only basic exception safety: the object is destructible or\n * assignable.\n * `std::bad_alloc` is assumed to never happen (a function that uses malloc can\n * be marked noexcept).\n *\n * ## NAME TAPE\n *\n * Name tape is taken from a lecture by Alexander Stepanov but we are not 100%\n * sure if this is the container he had in mind.\n */\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\nclass tape;\n\n// string_tape - a common usecase.\nusing string_tape = tape<std::vector<char>>;\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\nclass tape {\n  using ref_traits = detail::tape_reference_traits<Container>;\n\n public:\n  using container_type = Container;\n\n  using const_reference = typename ref_traits::reference;\n  using reference = const_reference;\n\n  // value_type for tape does not make much sense.\n  // The best we found is to make reference type to be value type.\n  // This does not quite make sense but works well enough.\n  using value_type = const_reference;\n  using scalar_value_type = detail::maybe_range_value_t<container_type>;\n\n  using size_type = typename Container::size_type;\n  using difference_type = typename Container::difference_type;\n\n  using iterator = folly::index_iterator<const tape>;\n  using const_iterator = iterator;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  // concepts ------\n\n  template <typename I>\n  static constexpr bool iterator_of_scalars =\n      std::is_convertible_v<iterator_value_type_t<I>, scalar_value_type>;\n\n  template <typename I>\n  static constexpr bool range_of_scalars =\n      iterator_of_scalars<detail::maybe_range_const_iterator_t<I>>;\n\n  template <typename I>\n  static constexpr bool iterator_of_records =\n      range_of_scalars<iterator_value_type_t<I>>;\n\n  template <typename I>\n  static constexpr bool range_of_records =\n      iterator_of_records<detail::maybe_range_const_iterator_t<I>>;\n\n  // rule of 5\n  tape(const tape&) = default;\n  tape& operator=(const tape&) = default;\n  ~tape() = default;\n  tape(tape&&) noexcept;\n  tape& operator=(tape&&) noexcept;\n\n  // constructors -----\n  tape() noexcept = default;\n\n  template <\n      typename I,\n      typename S,\n      typename = std::enable_if_t<iterator_of_records<I>>>\n  explicit tape(I f, S l) {\n    range_constructor(f, l);\n  }\n\n  template <\n      typename R,\n      typename = std::enable_if_t<\n          std::is_convertible_v<R, const_reference> || // const char*\n          range_of_records<R>>>\n  explicit tape(std::initializer_list<R> il) {\n    range_constructor(il.begin(), il.end());\n  }\n\n  // access ------\n\n  [[nodiscard]] const_reference operator[](size_type i) const noexcept {\n    return ref_traits::make(\n        data_.begin() + markers_[i], data_.begin() + markers_[i + 1]);\n  }\n\n  [[nodiscard]] const_reference at(size_type i) const {\n    if (FOLLY_UNLIKELY(i >= size())) {\n      // libc++ doesn't provide index. This helps optimizations.\n      throw std::out_of_range(\"tape\");\n    }\n    return operator[](i);\n  }\n\n  [[nodiscard]] bool empty() const noexcept { return size() == 0; }\n  [[nodiscard]] size_type size() const noexcept { return markers_.size() - 1; }\n  [[nodiscard]] size_type size_flat() const noexcept { return data_.size(); }\n\n  [[nodiscard]] const_reference front() const noexcept { return operator[](0); }\n  [[nodiscard]] const_reference back() const noexcept {\n    return operator[](size() - 1);\n  }\n\n  // iterators ----\n\n  [[nodiscard]] const_iterator begin() const noexcept { return {*this, 0}; }\n  [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); }\n\n  [[nodiscard]] const_iterator end() const noexcept { return {*this, size()}; }\n  [[nodiscard]] const_iterator cend() const noexcept { return end(); }\n\n  [[nodiscard]] auto rbegin() const noexcept {\n    return const_reverse_iterator{end()};\n  }\n  [[nodiscard]] auto crbegin() const noexcept { return rbegin(); }\n\n  [[nodiscard]] auto rend() const noexcept {\n    return const_reverse_iterator{begin()};\n  }\n  [[nodiscard]] auto crend() const noexcept { return rend(); }\n\n  // push / emplace_back --------\n\n  template <typename I, typename S>\n  auto push_back(I f, S l) -> std::enable_if_t<iterator_of_scalars<I>> {\n    data_.insert(data_.end(), f, l);\n    markers_.push_back(static_cast<difference_type>(data_.size()));\n  }\n\n  template <typename R>\n  auto push_back(R&& r) -> std::enable_if_t<\n      range_of_scalars<R> &&\n      !std::is_convertible_v<R, const_reference>> // handle \\0 separately\n  {\n    push_back(std::begin(r), std::end(r));\n  }\n\n  void push_back(const_reference r) { push_back(r.begin(), r.end()); }\n\n  void push_back(std::initializer_list<scalar_value_type> r) {\n    push_back(r.begin(), r.end());\n  }\n\n  void emplace_back() { push_back({}); }\n\n  template <typename... Args>\n  void emplace_back(Args&&... args) {\n    push_back(std::forward<Args>(args)...);\n  }\n\n  // push_back_unsafe --------\n  // like push_back but requires you to have enough capacity for added range.\n  // happened to give a 2x performance improvements on certain benchmarks.\n\n  // requires to have enough capacity\n  template <typename I, typename S>\n  auto push_back_unsafe(I f, S l) -> std::enable_if_t<iterator_of_scalars<I>> {\n    // basic exception guarantee is preserved here.\n    detail::append_range_unsafe(data_, f, l);\n    markers_.push_back(static_cast<difference_type>(data_.size()));\n  }\n\n  template <typename R>\n  auto push_back_unsafe(R&& r) -> std::enable_if_t<\n      range_of_scalars<R> &&\n      !std::is_convertible_v<R, const_reference>> // handle \\0 separately\n  {\n    push_back_unsafe(std::begin(r), std::end(r));\n  }\n\n  void push_back_unsafe(const_reference r) {\n    push_back_unsafe(r.begin(), r.end());\n  }\n\n  // record builder (constructing last record) -------\n\n  class record_builder;\n\n  // get a record builder.\n  // new_record_builder starts a builder for a new record.\n  // last_record_builder allows you to append/mutate the last record.\n  [[nodiscard]] record_builder new_record_builder();\n  [[nodiscard]] record_builder last_record_builder();\n\n  // insert one record ----------\n\n  template <typename I, typename S>\n  auto insert(const_iterator pos, I f, S l)\n      -> std::enable_if_t<iterator_of_scalars<I>, iterator>;\n\n  template <typename R>\n  auto insert(const_iterator pos, R&& r) -> std::enable_if_t<\n      range_of_scalars<R> && !std::is_convertible_v<R, const_reference>,\n      iterator> {\n    return insert(pos, std::begin(r), std::end(r));\n  }\n\n  iterator insert(\n      const_iterator pos, std::initializer_list<scalar_value_type> r) {\n    return insert(pos, r.begin(), r.end());\n  }\n\n  iterator insert(const_iterator pos, const_reference r) {\n    return insert(pos, r.begin(), r.end());\n  }\n\n  // capacity ------\n  void reserve(size_type records, size_type elements) {\n    markers_.reserve(records + 1);\n    data_.reserve(elements);\n  }\n\n  // assumes that 1 element per record. This is likely to help a bit.\n  void reserve(size_type records) {\n    markers_.reserve(records + 1);\n    data_.reserve(records);\n  }\n\n  void shrink_to_fit() {\n    markers_.shrink_to_fit();\n    data_.shrink_to_fit();\n  }\n\n  // resize/clear -------\n\n  // same args as for push_back/emplace back are accepted\n  template <typename... Args>\n  void resize(size_type new_size, const Args&... args);\n\n  void clear() noexcept {\n    markers_.resize(1);\n    data_.clear();\n  }\n\n  // erase -------\n\n  void pop_back() noexcept {\n    assert(!empty());\n    data_.resize(data_.size() - back().size());\n    markers_.pop_back();\n  }\n\n  // note: same behaviour as for std::vector, erasing end() is UB\n  iterator erase(const_iterator pos) {\n    assert(pos != end());\n    return erase(pos, pos + 1);\n  }\n\n  iterator erase(const_iterator f, const_iterator l);\n\n  // ordering --------\n\n  friend bool operator==(const tape& x, const tape& y) {\n    return x.markers_ == y.markers_ && x.data_ == y.data_;\n  }\n\n  friend bool operator!=(const tape& x, const tape& y) { return !(x == y); }\n\n  friend bool operator<(const tape& x, const tape& y) {\n    return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());\n  }\n\n  friend bool operator>(const tape& x, const tape& y) { return y < x; }\n  friend bool operator<=(const tape& x, const tape& y) { return !(y < x); }\n  friend bool operator>=(const tape& x, const tape& y) { return !(x < y); }\n\n  folly::Range<const difference_type*> markers() const { return markers_; }\n  reference scalars() const {\n    return ref_traits::make(data_.begin(), data_.end());\n  }\n\n private:\n  template <typename I, typename S>\n  void range_constructor(I f, S l);\n\n  // NOTE: using container difference_type might be too much here but,\n  // on the other hand, there should be reasonably few items on the tape and\n  // this makes interface simpler.\n  std::vector<difference_type> markers_ = {0};\n  container_type data_;\n};\n\n// Provides a way to construct a last record similar\n// to how you would `std::vector`.\n// Typical workflow is you `push_back` a bunch of individual elements and then\n// `commit()`.\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\nclass tape<Container>::record_builder {\n public:\n  record_builder(const record_builder&) = delete;\n  record_builder(record_builder&&) = delete;\n  record_builder& operator=(const record_builder&) = delete;\n  record_builder& operator=(record_builder&&) = delete;\n\n  using iterator = typename container_type::iterator;\n  using const_iterator = typename container_type::const_iterator;\n  using reference = typename std::iterator_traits<iterator>::reference;\n  using const_reference =\n      typename std::iterator_traits<const_iterator>::reference;\n  using size_type = typename container_type::size_type;\n  using difference_type =\n      typename std::iterator_traits<iterator>::difference_type;\n\n  // mutators ---\n\n  void push_back(scalar_value_type x) { self_->data_.push_back(std::move(x)); }\n\n  template <typename... Args>\n  reference emplace_back(Args&&... args) {\n    self_->data_.emplace_back(std::forward<Args>(args)...);\n    // cannot rely on the container doing the right thing here.\n    return self_->data_.back();\n  }\n\n  // constructed record is added to the tape.\n  void commit() { self_->markers_.push_back(self_->data_.size()); }\n\n  // discards elements of the constructed record. (automatic on destruction)\n  void abort() { self_->data_.resize(self_->markers_.back()); }\n\n  // iterators -----\n\n  [[nodiscard]] iterator begin() noexcept {\n    return self_->data_.begin() + self_->markers_.back();\n  }\n  [[nodiscard]] const_iterator begin() const noexcept {\n    return self_->data_.cbegin() + self_->markers_.back();\n  }\n  [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); }\n\n  [[nodiscard]] iterator end() noexcept { return self_->data_.end(); }\n  [[nodiscard]] const_iterator end() const noexcept {\n    return self_->data_.cend();\n  }\n  [[nodiscard]] const_iterator cend() const noexcept { return end(); }\n\n  // sometimes functions (like fmt) optimize for vector back inserter.\n  // so better expose that.\n  [[nodiscard]] auto back_inserter() noexcept {\n    return std::back_inserter(self_->data_);\n  }\n\n  // access ---\n\n  [[nodiscard]] bool empty() const noexcept { return begin() == end(); }\n\n  [[nodiscard]] size_type size() const noexcept {\n    return static_cast<size_type>(end() - begin());\n  }\n\n  [[nodiscard]] reference operator[](size_type i) noexcept {\n    return begin()[static_cast<difference_type>(i)];\n  }\n\n  [[nodiscard]] const_reference operator[](size_type i) const noexcept {\n    return begin()[static_cast<difference_type>(i)];\n  }\n\n  [[nodiscard]] reference at(size_type i) {\n    if (FOLLY_UNLIKELY(i >= size())) {\n      // libc++ doesn't provide index. This helps optimizations.\n      throw std::out_of_range(\"tape::scoped_record_builder\");\n    }\n    return operator[](i);\n  }\n\n  [[nodiscard]] const_reference at(size_type i) const {\n    if (FOLLY_UNLIKELY(i >= size())) {\n      // libc++ doesn't provide index. This helps optimizations.\n      throw std::out_of_range(\"tape::scoped_record_builder\");\n    }\n    return operator[](i);\n  }\n\n  [[nodiscard]] reference back() { return self_->data_.back(); }\n  [[nodiscard]] const_reference back() const { return self_->data_.back(); }\n\n  ~record_builder() noexcept { abort(); }\n\n private:\n  friend class tape;\n\n  explicit record_builder(tape& self) : self_(&self) {}\n\n  tape* self_;\n};\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\nauto tape<Container>::new_record_builder() -> record_builder {\n  return record_builder{*this};\n}\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\nauto tape<Container>::last_record_builder() -> record_builder {\n  assert(!empty());\n  markers_.pop_back();\n  return new_record_builder();\n}\n\n// tape methods -----\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\ntape<Container>::tape(tape&& x) noexcept\n    : markers_(std::move(x.markers_)), data_(std::move(x.data_)) {\n  // we assume that allocations never fail\n  x.markers_ = {0};\n  x.data_.clear();\n}\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\ntape<Container>& tape<Container>::operator=(tape&& x) noexcept {\n  if (this != &x) {\n    markers_ = std::move(x.markers_);\n    data_ = std::move(x.data_);\n  }\n  // we assume that allocations never fail\n  x.markers_ = {0};\n  x.data_.clear();\n  return *this;\n}\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\ntemplate <typename I, typename S>\nvoid tape<Container>::range_constructor(I f, S l) {\n  if constexpr (auto maybe = detail::compute_total_tape_len_if_possible(f, l);\n                std::is_same_v<decltype(maybe), detail::fake_type>) {\n    while (f != l) {\n      push_back(*f);\n      ++f;\n    }\n  } else {\n    auto [nrecords, total_len] = maybe;\n    reserve(nrecords, total_len);\n\n    while (f != l) {\n      push_back_unsafe(*f);\n      ++f;\n    }\n  }\n}\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\ntemplate <typename... Args>\nvoid tape<Container>::resize(size_type new_size, const Args&... args) {\n  if (new_size >= size()) {\n    new_size -= size();\n    while (new_size--) {\n      emplace_back(args...);\n    }\n    return;\n  }\n\n  data_.resize(markers_[new_size]);\n  markers_.resize(new_size + 1);\n}\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\ntemplate <typename I, typename S>\nauto tape<Container>::insert(const_iterator pos, I f, S l)\n    -> std::enable_if_t<iterator_of_scalars<I>, iterator> {\n  auto data_pos = data_.begin() + markers_[pos.get_index()];\n  size_type old_size = data_.size();\n  data_.insert(data_pos, f, l);\n\n  auto inserted_len = static_cast<difference_type>(data_.size() - old_size);\n\n  difference_type start = markers_[pos.get_index()];\n\n  auto markers_tail =\n      markers_.insert(markers_.begin() + pos.get_index(), start);\n  ++markers_tail;\n\n  std::transform(\n      markers_tail, markers_.end(), markers_tail, [&](difference_type m) {\n        return m + inserted_len;\n      });\n\n  // both tape* and index stayed the same\n  return pos;\n}\n\ntemplate <FOLLY_TAPE_CONTAINER_REQUIRES Container>\nauto tape<Container>::erase(const_iterator f, const_iterator l) -> iterator {\n  difference_type from = f.get_index();\n  difference_type to = l.get_index();\n\n  auto markers_f = markers_.begin() + from;\n  auto markers_l = markers_.begin() + to;\n  auto data_f = data_.begin() + *markers_f;\n  auto data_l = data_.begin() + *markers_l;\n\n  std::ptrdiff_t removed_length = data_l - data_f;\n  std::transform(markers_l, markers_.end(), markers_l, [&](difference_type m) {\n    return m - removed_length;\n  });\n\n  markers_.erase(markers_f, markers_l);\n  data_.erase(data_f, data_l);\n\n  // both tape* and index stayed the same\n  return f;\n}\n\n#undef FOLLY_TAPE_CONTAINER_REQUIRES\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/test/.clang-tidy",
    "content": "# NOTE there must be no spaces before the '-', so put the comma after.\n# When making changes, be sure to verify the output of the following command to ensure\n# the desired checks are enabled (run from the directory containing a .clang-tidy file):\n# clang-tidy -list-checks\n# NOTE: Please don't disable inheritance from the parent to make sure that common checks get propagated.\n---\nInheritParentConfig: true\nChecks: '\n-bugprone-use-after-move,\n-facebook-expensive-flat-container-operation,\n'\n...\n"
  },
  {
    "path": "folly/container/test/AccessTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/Access.h>\n\n#include <array>\n#include <initializer_list>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\nclass AccessTest : public testing::Test {};\n\nTEST_F(AccessTest, size_vector) {\n  EXPECT_EQ(3, folly::access::size(std::vector<int>{1, 2, 3}));\n}\n\nTEST_F(AccessTest, size_array) {\n  constexpr auto const a = std::array<int, 3>{{1, 2, 3}};\n  constexpr auto const size = folly::access::size(a);\n  EXPECT_EQ(3, size);\n}\n\nTEST_F(AccessTest, size_carray) {\n  constexpr int const a[3] = {1, 2, 3};\n  constexpr auto const size = folly::access::size(a);\n  EXPECT_EQ(3, size);\n}\n\nTEST_F(AccessTest, size_initializer_list) {\n  EXPECT_EQ(3, folly::access::size(std::initializer_list<int>{1, 2, 3}));\n}\n\nTEST_F(AccessTest, empty_vector) {\n  EXPECT_FALSE(folly::access::empty(std::vector<int>{1, 2, 3}));\n  EXPECT_TRUE(folly::access::empty(std::vector<int>{}));\n}\n\nTEST_F(AccessTest, empty_array) {\n  {\n    constexpr auto const a = std::array<int, 3>{{1, 2, 3}};\n    constexpr auto const empty = folly::access::empty(a);\n    EXPECT_FALSE(empty);\n  }\n  {\n    constexpr auto const a = std::array<int, 0>{{}};\n    constexpr auto const empty = folly::access::empty(a);\n    EXPECT_TRUE(empty);\n  }\n}\n\nTEST_F(AccessTest, empty_carray) {\n  constexpr int const a[3] = {1, 2, 3};\n  constexpr auto const empty = folly::access::empty(a);\n  EXPECT_FALSE(empty);\n  //  zero-length arrays are not allowed in the language\n}\n\nTEST_F(AccessTest, empty_initializer_list) {\n  EXPECT_FALSE(folly::access::empty(std::initializer_list<int>{1, 2, 3}));\n  EXPECT_TRUE(folly::access::empty(std::initializer_list<int>{}));\n}\n\nTEST_F(AccessTest, data_vector) {\n  EXPECT_EQ(1, *folly::access::data(std::vector<int>{1, 2, 3}));\n  auto v = std::vector<int>{1, 2, 3};\n  *folly::access::data(v) = 4;\n  EXPECT_EQ(4, v[0]);\n}\n\nTEST_F(AccessTest, data_array) {\n  constexpr auto const a = std::array<int, 3>{{1, 2, 3}};\n  auto const data = folly::access::data(a); // not constexpr until C++17\n  EXPECT_EQ(1, *data);\n}\n\nTEST_F(AccessTest, data_carray) {\n  constexpr int const a[3] = {1, 2, 3};\n  auto const data = folly::access::data(a); // not constexpr until C++17\n  EXPECT_EQ(1, *data);\n}\n\nTEST_F(AccessTest, data_initializer_list) {\n  EXPECT_EQ(1, *folly::access::data(std::initializer_list<int>{1, 2, 3}));\n}\n\nTEST_F(AccessTest, begin_vector) {\n  auto v = std::vector<int>{1, 2, 3};\n  EXPECT_EQ(v.begin(), folly::access::begin(v));\n}\n\nTEST_F(AccessTest, begin_array) {\n  constexpr auto const a = std::array<int, 3>{{1, 2, 3}};\n  EXPECT_EQ(a.begin(), folly::access::begin(a));\n}\n\nTEST_F(AccessTest, begin_carray) {\n  constexpr int const a[3] = {1, 2, 3};\n  EXPECT_EQ(a + 0, folly::access::begin(a));\n}\n\nTEST_F(AccessTest, begin_initializer_list) {\n  auto i = std::initializer_list<int>{1, 2, 3};\n  EXPECT_EQ(i.begin(), folly::access::begin(i));\n}\n\nTEST_F(AccessTest, end_vector) {\n  auto v = std::vector<int>{1, 2, 3};\n  EXPECT_EQ(v.end(), folly::access::end(v));\n}\n\nTEST_F(AccessTest, end_array) {\n  constexpr auto const a = std::array<int, 3>{{1, 2, 3}};\n  EXPECT_EQ(a.end(), folly::access::end(a));\n}\n\nTEST_F(AccessTest, end_carray) {\n  constexpr int const a[3] = {1, 2, 3};\n  EXPECT_EQ(a + 3, folly::access::end(a));\n}\n\nTEST_F(AccessTest, end_initializer_list) {\n  auto i = std::initializer_list<int>{1, 2, 3};\n  EXPECT_EQ(i.end(), folly::access::end(i));\n}\n"
  },
  {
    "path": "folly/container/test/ArrayTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/Array.h>\n\n#include <string>\n\n#include <folly/portability/GTest.h>\n\nusing namespace std;\nusing folly::make_array;\n\nTEST(makeArray, baseCase) {\n  auto arr = make_array<int>();\n  static_assert(\n      is_same<typename decltype(arr)::value_type, int>::value,\n      \"Wrong array type\");\n  EXPECT_EQ(arr.size(), 0);\n}\n\nTEST(makeArray, deduceSizePrimitive) {\n  auto arr = make_array<int>(1, 2, 3, 4, 5);\n  static_assert(\n      is_same<typename decltype(arr)::value_type, int>::value,\n      \"Wrong array type\");\n  EXPECT_EQ(arr.size(), 5);\n}\n\nTEST(makeArray, deduceSizeClass) {\n  auto arr = make_array<string>(string{\"foo\"}, string{\"bar\"});\n  static_assert(\n      is_same<typename decltype(arr)::value_type, std::string>::value,\n      \"Wrong array type\");\n  EXPECT_EQ(arr.size(), 2);\n  EXPECT_EQ(arr[1], \"bar\");\n}\n\nTEST(makeArray, deduceEverything) {\n  auto arr = make_array(string{\"foo\"}, string{\"bar\"});\n  static_assert(\n      is_same<typename decltype(arr)::value_type, std::string>::value,\n      \"Wrong array type\");\n  EXPECT_EQ(arr.size(), 2);\n  EXPECT_EQ(arr[1], \"bar\");\n}\n\nTEST(makeArray, fixedCommonType) {\n  auto arr = make_array<double>(1.0, 2.5f, 3, 4, 5);\n  static_assert(\n      is_same<typename decltype(arr)::value_type, double>::value,\n      \"Wrong array type\");\n  EXPECT_EQ(arr.size(), 5);\n}\n\nTEST(makeArray, deducedCommonType) {\n  auto arr = make_array(1.0, 2.5f, 3, 4, 5);\n  static_assert(\n      is_same<typename decltype(arr)::value_type, double>::value,\n      \"Wrong array type\");\n  EXPECT_EQ(arr.size(), 5);\n}\n\nTEST(makeArrayWith, example) {\n  struct make_item {\n    constexpr int operator()(size_t index) const { return index + 4; }\n  };\n\n  constexpr auto actual = folly::make_array_with<3>(make_item{});\n  constexpr auto expected = make_array<int>(4, 5, 6);\n  EXPECT_EQ(expected, actual);\n}\n"
  },
  {
    "path": "folly/container/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\n    \"@fbsource//tools/build_defs:platform_defs.bzl\",\n    \"ANDROID\",\n    \"APPLE\",\n    \"CXX\",\n    \"WINDOWS\",\n)\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\nload(\n    \"../../defs.bzl\",\n    \"FBANDROID_CXXFLAGS\",\n    \"FBOBJC_CXXFLAGS\",\n    \"folly_xplat_cxx_test\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"f14_set-test\",\n    srcs = [\n        \"F14SetTest.cpp\",\n    ],\n    fbandroid_compiler_flags = FBANDROID_CXXFLAGS,\n    fbobjc_compiler_flags = FBOBJC_CXXFLAGS + [\n        \"-Wno-format\",\n        \"-Wno-missing-prototypes\",\n        \"-Wno-shadow\",\n    ],\n    include_directories = [\n        \"..\",\n    ],\n    platforms = (ANDROID, APPLE, CXX, WINDOWS),\n    use_instrumentation_test = True,\n    deps = [\n        \"fbsource//third-party/boost:boost\",\n        \"fbsource//third-party/boost:boost_filesystem\",\n        \"fbsource//third-party/boost:boost_system\",\n        \"fbsource//third-party/double-conversion:double-conversion\",\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//xplat/folly:atomic_hash_map\",\n        \"fbsource//xplat/folly:atomic_linked_list\",\n        \"fbsource//xplat/folly:c_portability\",\n        \"fbsource//xplat/folly:chrono\",\n        \"fbsource//xplat/folly:config\",\n        \"fbsource//xplat/folly:constexpr_math\",\n        \"fbsource//xplat/folly:conv\",\n        \"fbsource//xplat/folly:cpp_attributes\",\n        \"fbsource//xplat/folly:demangle\",\n        \"fbsource//xplat/folly:discriminated_ptr\",\n        \"fbsource//xplat/folly:dynamic\",\n        \"fbsource//xplat/folly:exception\",\n        \"fbsource//xplat/folly:exception_string\",\n        \"fbsource//xplat/folly:executor\",\n        \"fbsource//xplat/folly:expected\",\n        \"fbsource//xplat/folly:fbstring\",\n        \"fbsource//xplat/folly:fbvector\",\n        \"fbsource//xplat/folly:file\",\n        \"fbsource//xplat/folly:format\",\n        \"fbsource//xplat/folly:format_traits\",\n        \"fbsource//xplat/folly:function\",\n        \"fbsource//xplat/folly:glog\",\n        \"fbsource//xplat/folly:indestructible\",\n        \"fbsource//xplat/folly:indexed_mem_pool\",\n        \"fbsource//xplat/folly:intrusive_list\",\n        \"fbsource//xplat/folly:likely\",\n        \"fbsource//xplat/folly:math\",\n        \"fbsource//xplat/folly:memory\",\n        \"fbsource//xplat/folly:micro_lock\",\n        \"fbsource//xplat/folly:move_wrapper\",\n        \"fbsource//xplat/folly:mpmc_queue\",\n        \"fbsource//xplat/folly:observer_container\",\n        \"fbsource//xplat/folly:optional\",\n        \"fbsource//xplat/folly:overload\",\n        \"fbsource//xplat/folly:portability\",\n        \"fbsource//xplat/folly:preprocessor\",\n        \"fbsource//xplat/folly:random\",\n        \"fbsource//xplat/folly:range\",\n        \"fbsource//xplat/folly:scope_guard\",\n        \"fbsource//xplat/folly:shared_mutex\",\n        \"fbsource//xplat/folly:singleton\",\n        \"fbsource//xplat/folly:singleton_thread_local\",\n        \"fbsource//xplat/folly:small_vector\",\n        \"fbsource//xplat/folly:sorted_vector_types\",\n        \"fbsource//xplat/folly:spin_lock\",\n        \"fbsource//xplat/folly:stop_watch\",\n        \"fbsource//xplat/folly:synchronized\",\n        \"fbsource//xplat/folly:thread_local\",\n        \"fbsource//xplat/folly:traits\",\n        \"fbsource//xplat/folly:unit\",\n        \"fbsource//xplat/folly/concurrency:cache_locality\",\n        \"fbsource//xplat/folly/concurrency:concurrent_hash_map\",\n        \"fbsource//xplat/folly/concurrency:priority_unbounded_queue_set\",\n        \"fbsource//xplat/folly/concurrency:unbounded_queue\",\n        \"fbsource//xplat/folly/container:array\",\n        \"fbsource//xplat/folly/container:bit_iterator\",\n        \"fbsource//xplat/folly/container:enumerate\",\n        \"fbsource//xplat/folly/container:evicting_cache_map\",\n        \"fbsource//xplat/folly/container:f14_hash\",\n        \"fbsource//xplat/folly/container:foreach\",\n        \"fbsource//xplat/folly/container:heap_vector_types\",\n        \"fbsource//xplat/folly/container:iterator\",\n        \"fbsource//xplat/folly/container:sparse_byte_set\",\n        \"fbsource//xplat/folly/detail:discriminated_ptr_detail\",\n        \"fbsource//xplat/folly/detail:futex\",\n        \"fbsource//xplat/folly/detail:socket_fast_open\",\n        \"fbsource//xplat/folly/detail:turn_sequencer\",\n        \"fbsource//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:global_executor\",\n        \"fbsource//xplat/folly/executors:inline_executor\",\n        \"fbsource//xplat/folly/executors:io_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:serial_executor\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/fibers:core\",\n        \"fbsource//xplat/folly/fibers:event_base_loop_controller\",\n        \"fbsource//xplat/folly/fibers:fiber_manager_map\",\n        \"fbsource//xplat/folly/functional:apply_tuple\",\n        \"fbsource//xplat/folly/functional:invoke\",\n        \"fbsource//xplat/folly/functional:partial\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:typed_io_buf\",\n        \"fbsource//xplat/folly/io/async:async_base\",\n        \"fbsource//xplat/folly/io/async:async_pipe\",\n        \"fbsource//xplat/folly/io/async:async_signal_handler\",\n        \"fbsource//xplat/folly/io/async:async_socket\",\n        \"fbsource//xplat/folly/io/async:async_socket_base\",\n        \"fbsource//xplat/folly/io/async:async_socket_exception\",\n        \"fbsource//xplat/folly/io/async:async_ssl_socket\",\n        \"fbsource//xplat/folly/io/async:async_transport\",\n        \"fbsource//xplat/folly/io/async:async_transport_certificate\",\n        \"fbsource//xplat/folly/io/async:async_udp_server_socket\",\n        \"fbsource//xplat/folly/io/async:async_udp_socket\",\n        \"fbsource//xplat/folly/io/async:decorated_async_transport_wrapper\",\n        \"fbsource//xplat/folly/io/async:destructor_check\",\n        \"fbsource//xplat/folly/io/async:scoped_event_base_thread\",\n        \"fbsource//xplat/folly/io/async:server_socket\",\n        \"fbsource//xplat/folly/io/async:ssl_context\",\n        \"fbsource//xplat/folly/io/async:ssl_options\",\n        \"fbsource//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"fbsource//xplat/folly/io/async/ssl:openssl_utils\",\n        \"fbsource//xplat/folly/io/async/ssl:ssl_errors\",\n        \"fbsource//xplat/folly/io/async/ssl:tls_definitions\",\n        \"fbsource//xplat/folly/lang:bits\",\n        \"fbsource//xplat/folly/lang:checked_math\",\n        \"fbsource//xplat/folly/lang:exception\",\n        \"fbsource//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"fbsource//xplat/folly/lang:uncaught_exceptions\",\n        \"fbsource//xplat/folly/logging:init\",\n        \"fbsource//xplat/folly/logging:init_weak\",\n        \"fbsource//xplat/folly/logging:log_handler\",\n        \"fbsource//xplat/folly/logging:log_level\",\n        \"fbsource//xplat/folly/logging:log_name\",\n        \"fbsource//xplat/folly/logging:logging\",\n        \"fbsource//xplat/folly/logging:rate_limiter\",\n        \"fbsource//xplat/folly/memory:arena\",\n        \"fbsource//xplat/folly/memory:malloc\",\n        \"fbsource//xplat/folly/net:tcpinfo\",\n        \"fbsource//xplat/folly/net:tcpinfo_dispatcher\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:atomic\",\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"fbsource//xplat/folly/portability:dirent\",\n        \"fbsource//xplat/folly/portability:event\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:iovec\",\n        \"fbsource//xplat/folly/portability:math\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:stdlib\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/portability:sys_stat\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:sys_time\",\n        \"fbsource//xplat/folly/portability:syslog\",\n        \"fbsource//xplat/folly/portability:time\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \"fbsource//xplat/folly/ssl:openssl_cert_utils\",\n        \"fbsource//xplat/folly/ssl:openssl_hash\",\n        \"fbsource//xplat/folly/ssl:openssl_ptr_types\",\n        \"fbsource//xplat/folly/ssl:ssl_session\",\n        \"fbsource//xplat/folly/ssl:ssl_session_manager\",\n        \"fbsource//xplat/folly/synchronization:call_once\",\n        \"fbsource//xplat/folly/synchronization:hazptr\",\n        \"fbsource//xplat/folly/synchronization:lifo_sem\",\n        \"fbsource//xplat/folly/synchronization:micro_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:pico_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:rcu\",\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"fbsource//xplat/folly/system:thread_id\",\n        \"fbsource//xplat/folly/system:thread_name\",\n        \"fbsource//xplat/folly/tracing:scoped_trace_section\",\n        \"fbsource//xplat/folly/tracing:static_tracepoint\",\n        \"fbsource//xplat/third-party/event:event\",\n        \"fbsource//xplat/third-party/linker_lib:atomic\",\n        \"fbsource//xplat/third-party/openssl:crypto\",\n        \"fbsource//xplat/third-party/openssl:ssl\",\n        \":f14_test_util\",\n        \":tracking_types\",\n        \"//xplat/folly:benchmark\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:fbstring\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:test-headers\",\n        \"//xplat/folly:test_test_utils\",\n        \"//xplat/folly/container:f14_hash\",\n        \"//xplat/folly/lang:keep\",\n        \"//xplat/third-party/linker_lib:atomic\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"//xplat/folly:function\",\n        \"//xplat/folly/container/detail:f14_hash_detail\",\n        \"//xplat/folly/lang:safe_assert\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"f14_map-test\",\n    srcs = [\n        \"F14MapTest.cpp\",\n    ],\n    fbandroid_compiler_flags = FBANDROID_CXXFLAGS,\n    fbobjc_compiler_flags = FBOBJC_CXXFLAGS + [\n        \"-Wno-format\",\n        \"-Wno-missing-prototypes\",\n        \"-Wno-shadow\",\n    ],\n    include_directories = [\n        \"..\",\n    ],\n    platforms = (ANDROID, APPLE, CXX, WINDOWS),\n    use_instrumentation_test = True,\n    deps = [\n        \"fbsource//third-party/boost:boost\",\n        \"fbsource//third-party/boost:boost_filesystem\",\n        \"fbsource//third-party/boost:boost_system\",\n        \"fbsource//third-party/double-conversion:double-conversion\",\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//xplat/folly:atomic_hash_map\",\n        \"fbsource//xplat/folly:atomic_linked_list\",\n        \"fbsource//xplat/folly:c_portability\",\n        \"fbsource//xplat/folly:chrono\",\n        \"fbsource//xplat/folly:config\",\n        \"fbsource//xplat/folly:constexpr_math\",\n        \"fbsource//xplat/folly:conv\",\n        \"fbsource//xplat/folly:cpp_attributes\",\n        \"fbsource//xplat/folly:demangle\",\n        \"fbsource//xplat/folly:discriminated_ptr\",\n        \"fbsource//xplat/folly:dynamic\",\n        \"fbsource//xplat/folly:exception\",\n        \"fbsource//xplat/folly:exception_string\",\n        \"fbsource//xplat/folly:executor\",\n        \"fbsource//xplat/folly:expected\",\n        \"fbsource//xplat/folly:fbstring\",\n        \"fbsource//xplat/folly:fbvector\",\n        \"fbsource//xplat/folly:file\",\n        \"fbsource//xplat/folly:format\",\n        \"fbsource//xplat/folly:format_traits\",\n        \"fbsource//xplat/folly:function\",\n        \"fbsource//xplat/folly:glog\",\n        \"fbsource//xplat/folly:indestructible\",\n        \"fbsource//xplat/folly:indexed_mem_pool\",\n        \"fbsource//xplat/folly:intrusive_list\",\n        \"fbsource//xplat/folly:likely\",\n        \"fbsource//xplat/folly:math\",\n        \"fbsource//xplat/folly:memory\",\n        \"fbsource//xplat/folly:micro_lock\",\n        \"fbsource//xplat/folly:move_wrapper\",\n        \"fbsource//xplat/folly:mpmc_queue\",\n        \"fbsource//xplat/folly:observer_container\",\n        \"fbsource//xplat/folly:optional\",\n        \"fbsource//xplat/folly:overload\",\n        \"fbsource//xplat/folly:portability\",\n        \"fbsource//xplat/folly:preprocessor\",\n        \"fbsource//xplat/folly:random\",\n        \"fbsource//xplat/folly:range\",\n        \"fbsource//xplat/folly:scope_guard\",\n        \"fbsource//xplat/folly:shared_mutex\",\n        \"fbsource//xplat/folly:singleton\",\n        \"fbsource//xplat/folly:singleton_thread_local\",\n        \"fbsource//xplat/folly:small_vector\",\n        \"fbsource//xplat/folly:sorted_vector_types\",\n        \"fbsource//xplat/folly:spin_lock\",\n        \"fbsource//xplat/folly:stop_watch\",\n        \"fbsource//xplat/folly:synchronized\",\n        \"fbsource//xplat/folly:thread_local\",\n        \"fbsource//xplat/folly:traits\",\n        \"fbsource//xplat/folly:unit\",\n        \"fbsource//xplat/folly/concurrency:cache_locality\",\n        \"fbsource//xplat/folly/concurrency:concurrent_hash_map\",\n        \"fbsource//xplat/folly/concurrency:priority_unbounded_queue_set\",\n        \"fbsource//xplat/folly/concurrency:unbounded_queue\",\n        \"fbsource//xplat/folly/container:array\",\n        \"fbsource//xplat/folly/container:bit_iterator\",\n        \"fbsource//xplat/folly/container:enumerate\",\n        \"fbsource//xplat/folly/container:evicting_cache_map\",\n        \"fbsource//xplat/folly/container:f14_hash\",\n        \"fbsource//xplat/folly/container:foreach\",\n        \"fbsource//xplat/folly/container:heap_vector_types\",\n        \"fbsource//xplat/folly/container:iterator\",\n        \"fbsource//xplat/folly/container:sparse_byte_set\",\n        \"fbsource//xplat/folly/detail:discriminated_ptr_detail\",\n        \"fbsource//xplat/folly/detail:futex\",\n        \"fbsource//xplat/folly/detail:socket_fast_open\",\n        \"fbsource//xplat/folly/detail:turn_sequencer\",\n        \"fbsource//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:global_executor\",\n        \"fbsource//xplat/folly/executors:inline_executor\",\n        \"fbsource//xplat/folly/executors:io_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:serial_executor\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/fibers:core\",\n        \"fbsource//xplat/folly/fibers:event_base_loop_controller\",\n        \"fbsource//xplat/folly/fibers:fiber_manager_map\",\n        \"fbsource//xplat/folly/functional:apply_tuple\",\n        \"fbsource//xplat/folly/functional:invoke\",\n        \"fbsource//xplat/folly/functional:partial\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:typed_io_buf\",\n        \"fbsource//xplat/folly/io/async:async_base\",\n        \"fbsource//xplat/folly/io/async:async_pipe\",\n        \"fbsource//xplat/folly/io/async:async_signal_handler\",\n        \"fbsource//xplat/folly/io/async:async_socket\",\n        \"fbsource//xplat/folly/io/async:async_socket_base\",\n        \"fbsource//xplat/folly/io/async:async_socket_exception\",\n        \"fbsource//xplat/folly/io/async:async_ssl_socket\",\n        \"fbsource//xplat/folly/io/async:async_transport\",\n        \"fbsource//xplat/folly/io/async:async_transport_certificate\",\n        \"fbsource//xplat/folly/io/async:async_udp_server_socket\",\n        \"fbsource//xplat/folly/io/async:async_udp_socket\",\n        \"fbsource//xplat/folly/io/async:decorated_async_transport_wrapper\",\n        \"fbsource//xplat/folly/io/async:destructor_check\",\n        \"fbsource//xplat/folly/io/async:scoped_event_base_thread\",\n        \"fbsource//xplat/folly/io/async:server_socket\",\n        \"fbsource//xplat/folly/io/async:ssl_context\",\n        \"fbsource//xplat/folly/io/async:ssl_options\",\n        \"fbsource//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"fbsource//xplat/folly/io/async/ssl:openssl_utils\",\n        \"fbsource//xplat/folly/io/async/ssl:ssl_errors\",\n        \"fbsource//xplat/folly/io/async/ssl:tls_definitions\",\n        \"fbsource//xplat/folly/lang:bits\",\n        \"fbsource//xplat/folly/lang:checked_math\",\n        \"fbsource//xplat/folly/lang:exception\",\n        \"fbsource//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"fbsource//xplat/folly/lang:uncaught_exceptions\",\n        \"fbsource//xplat/folly/logging:init\",\n        \"fbsource//xplat/folly/logging:init_weak\",\n        \"fbsource//xplat/folly/logging:log_handler\",\n        \"fbsource//xplat/folly/logging:log_level\",\n        \"fbsource//xplat/folly/logging:log_name\",\n        \"fbsource//xplat/folly/logging:logging\",\n        \"fbsource//xplat/folly/logging:rate_limiter\",\n        \"fbsource//xplat/folly/memory:arena\",\n        \"fbsource//xplat/folly/memory:malloc\",\n        \"fbsource//xplat/folly/net:tcpinfo\",\n        \"fbsource//xplat/folly/net:tcpinfo_dispatcher\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:atomic\",\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"fbsource//xplat/folly/portability:dirent\",\n        \"fbsource//xplat/folly/portability:event\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:iovec\",\n        \"fbsource//xplat/folly/portability:math\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:stdlib\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/portability:sys_stat\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:sys_time\",\n        \"fbsource//xplat/folly/portability:syslog\",\n        \"fbsource//xplat/folly/portability:time\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \"fbsource//xplat/folly/ssl:openssl_cert_utils\",\n        \"fbsource//xplat/folly/ssl:openssl_hash\",\n        \"fbsource//xplat/folly/ssl:openssl_ptr_types\",\n        \"fbsource//xplat/folly/ssl:ssl_session\",\n        \"fbsource//xplat/folly/ssl:ssl_session_manager\",\n        \"fbsource//xplat/folly/synchronization:call_once\",\n        \"fbsource//xplat/folly/synchronization:hazptr\",\n        \"fbsource//xplat/folly/synchronization:lifo_sem\",\n        \"fbsource//xplat/folly/synchronization:micro_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:pico_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:rcu\",\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"fbsource//xplat/folly/system:thread_id\",\n        \"fbsource//xplat/folly/system:thread_name\",\n        \"fbsource//xplat/folly/tracing:scoped_trace_section\",\n        \"fbsource//xplat/folly/tracing:static_tracepoint\",\n        \"fbsource//xplat/third-party/event:event\",\n        \"fbsource//xplat/third-party/linker_lib:atomic\",\n        \"fbsource//xplat/third-party/openssl:crypto\",\n        \"fbsource//xplat/third-party/openssl:ssl\",\n        \":f14_test_util\",\n        \":tracking_types\",\n        \"//xplat/folly:benchmark\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:fbstring\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:test-headers\",\n        \"//xplat/folly:test_test_utils\",\n        \"//xplat/folly/container:f14_hash\",\n        \"//xplat/third-party/linker_lib:atomic\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"//xplat/folly:function\",\n        \"//xplat/folly/container/detail:f14_hash_detail\",\n        \"//xplat/folly/lang:safe_assert\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"iterator_test\",\n    srcs = [\"IteratorTest.cpp\"],\n    headers = [],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/container:iterator\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"range_traits_test\",\n    srcs = [\"range_traits_test.cpp\"],\n    headers = [],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/container:range_traits\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"regex_match_cache_test\",\n    srcs = [\"RegexMatchCacheTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:portability\",\n        \"//folly:utility\",\n        \"//folly/container:f14_hash\",\n        \"//folly/container:regex_match_cache\",\n        \"//folly/container:sorted_vector_types\",\n        \"//folly/container:span\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"span_test\",\n    srcs = [\"span_test.cpp\"],\n    deps = [\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly/container:span\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"std_bitset_test\",\n    srcs = [\"StdBitsetTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/container:std_bitset\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"tape_test\",\n    srcs = [\"tape_test.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly:small_vector\",\n        \"//folly/container:tape\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"vector_bool_test\",\n    srcs = [\"vector_bool_test.cpp\"],\n    headers = [],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly:memory\",\n        \"//folly/container:vector_bool\",\n        \"//folly/container/detail:bool_wrapper\",\n        \"//folly/memory:memory_resource\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\n# !!!! fbcode/folly/container/test/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"access_test\",\n    srcs = [\"AccessTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/container:access\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"array_test\",\n    srcs = [\"ArrayTest.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/container:array\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"bit_iterator_bench\",\n    srcs = [\"BitIteratorBench.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:small_vector\",\n        \"//folly/container:bit_iterator\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"bit_iterator_test\",\n    srcs = [\"BitIteratorTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/container:bit_iterator\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"enumerate_test\",\n    srcs = [\"EnumerateTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:range\",\n        \"//folly/container:enumerate\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"evicting_cache_map_bench\",\n    srcs = [\"EvictingCacheMapBench.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/container:evicting_cache_map\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"evicting_cache_map_test\",\n    srcs = [\"EvictingCacheMapTest.cpp\"],\n    headers = [],\n    allocator = \"jemalloc_debug\",\n    deps = [\n        \"//folly/container:evicting_cache_map\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"f14_test_util\",\n    headers = [\n        \"F14TestUtil.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/container/detail:f14_hash_detail\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_fwd_test\",\n    srcs = [\n        \"F14FwdTest.cpp\",\n    ],\n    deps = [\n        \"//folly/container:f14_hash_fwd\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_map_test\",\n    srcs = [\n        \"F14MapTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":f14_test_util\",\n        \":tracking_types\",\n        \"//folly:benchmark\",\n        \"//folly:conv\",\n        \"//folly:fbstring\",\n        \"//folly:portability\",\n        \"//folly/container:f14_hash\",\n        \"//folly/hash:hash\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_map_fallback_test\",\n    srcs = [\n        \"F14MapTest.cpp\",\n    ],\n    preprocessor_flags = [\"-DFOLLY_F14_FORCE_FALLBACK=1\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":f14_test_util\",\n        \":tracking_types\",\n        \"//folly:benchmark\",\n        \"//folly:conv\",\n        \"//folly:fbstring\",\n        \"//folly:portability\",\n        \"//folly/container:f14_hash\",\n        \"//folly/hash:hash\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_policy_test\",\n    srcs = [\n        \"F14PolicyTest.cpp\",\n    ],\n    deps = [\n        \"//folly/container:f14_hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_set_test\",\n    srcs = [\n        \"F14SetTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":f14_test_util\",\n        \":tracking_types\",\n        \"//folly:benchmark\",\n        \"//folly:conv\",\n        \"//folly:fbstring\",\n        \"//folly:portability\",\n        \"//folly/container:f14_hash\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_set_fallback_test\",\n    srcs = [\n        \"F14SetTest.cpp\",\n    ],\n    preprocessor_flags = [\"-DFOLLY_F14_FORCE_FALLBACK=1\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":f14_test_util\",\n        \":tracking_types\",\n        \"//folly:benchmark\",\n        \"//folly:conv\",\n        \"//folly:fbstring\",\n        \"//folly:portability\",\n        \"//folly/container:f14_hash\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"heap_vector_types_test\",\n    srcs = [\"heap_vector_types_test.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:random\",\n        \"//folly:range\",\n        \"//folly:small_vector\",\n        \"//folly:utility\",\n        \"//folly/container:heap_vector_types\",\n        \"//folly/memory:malloc\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_interprocess_test\",\n    srcs = [\n        \"F14InterprocessTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:random\",\n        \"//folly:traits\",\n        \"//folly/container:f14_hash\",\n        \"//folly/portability:gtest\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_interprocess\"),\n        (\"glibc\", None, \"rt\"),  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"f14_small_overheads\",\n    srcs = [\n        \"F14SmallOverheads.cpp\",\n    ],\n    deps = [\n        \"//folly/container:f14_hash\",\n        \"//folly/lang:keep\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"f14_asan_support_test\",\n    srcs = [\n        \"F14AsanSupportTest.cpp\",\n    ],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/container:f14_hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"foreach_benchmark\",\n    srcs = [\"ForeachBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/container:enumerate\",\n        \"//folly/container:foreach\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"foreach_test\",\n    srcs = [\"ForeachTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/container:foreach\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"heterogeneous_access_test\",\n    srcs = [\n        \"HeterogeneousAccessTest.cpp\",\n    ],\n    deps = [\n        \"//folly:fbstring\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly:small_vector\",\n        \"//folly:traits\",\n        \"//folly/container:heterogeneous_access\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"merge_test\",\n    srcs = [\"MergeTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/container:merge\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"span_test\",\n    srcs = [\"span_test.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/container:span\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"sparse_byte_set_benchmark\",\n    srcs = [\"SparseByteSetBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:benchmark\",\n        \"//folly:format\",\n        \"//folly/container:sparse_byte_set\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"sparse_byte_set_test\",\n    srcs = [\"SparseByteSetTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/container:sparse_byte_set\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"std_bitset_benchmark\",\n    srcs = [\"StdBitsetBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/container:std_bitset\",\n        \"//folly/init:init\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"tracking_types\",\n    headers = [\n        \"TrackingTypes.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/portability:asm\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"tape_bench\",\n    srcs = [\"tape_bench.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/container:tape\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"util_test\",\n    srcs = [\"UtilTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly/container/detail:util\",\n        \"//folly/container/test:tracking_types\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"weighted_evicting_cache_map_test\",\n    srcs = [\"WeightedEvictingCacheMapTest.cpp\"],\n    headers = [],\n    allocator = \"jemalloc_debug\",\n    deps = [\n        \"//folly/container:weighted_evicting_cache_map\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"intrusive_heap_test\",\n    srcs = [\"IntrusiveHeapTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:random\",\n        \"//folly/container:intrusive_heap\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"hash_maps_bench\",\n    srcs = [\"HashMapsBench.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:conv\",\n        \"//folly:format\",\n        \"//folly:function\",\n        \"//folly:random\",\n        \"//folly/container:f14_hash\",\n        \"//folly/hash:hash\",\n        \"//folly/init:init\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"reserve_test\",\n    srcs = [\"ReserveTest.cpp\"],\n    deps = [\n        \"//folly/container:f14_hash\",\n        \"//folly/container:reserve\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"fbvector_benchmark\",\n    srcs = [\"FBVectorBenchmark.cpp\"],\n    headers = [\"FBVectorBenchmarks.cpp.h\"],\n    args = [\n        \"--json\",\n    ],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:fbvector\",\n        \"//folly:small_vector\",\n        \"//folly:traits\",\n        \"//folly/container:foreach\",\n        \"//folly/portability:gflags\",\n        \"//folly/test:fbvector_test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"fbvector_test\",\n    srcs = [\"FBVectorTest.cpp\"],\n    headers = [\n        \"FBVectorTests.cpp.h\",\n    ],\n    deps = [\n        \"//folly:fbstring\",\n        \"//folly:fbvector\",\n        \"//folly:random\",\n        \"//folly:traits\",\n        \"//folly/container:foreach\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:fbvector_test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"map_util_test\",\n    srcs = [\"MapUtilTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:map_util\",\n        \"//folly:sorted_vector_types\",\n        \"//folly:traits\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"small_vector_test\",\n    srcs = [\"small_vector_test.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:conv\",\n        \"//folly:small_vector\",\n        \"//folly:sorted_vector_types\",\n        \"//folly:traits\",\n        \"//folly/container:iterator\",\n        \"//folly/portability:gtest\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_algorithm\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"sorted_vector_types_test\",\n    srcs = [\"sorted_vector_test.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly:sorted_vector_types\",\n        \"//folly:utility\",\n        \"//folly/memory:malloc\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"collection_util_test\",\n    srcs = [\"CollectionUtilTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:small_vector\",\n        \"//folly:sorted_vector_types\",\n        \"//folly/container:collection_util\",\n        \"//folly/container:f14_hash\",\n        \"//folly/json:dynamic\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"irange_test\",\n    srcs = [\"IRangeTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/googletest:gtest\",\n        \"//folly/container:irange\",\n    ],\n)\n"
  },
  {
    "path": "folly/container/test/BitIteratorBench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/BitIterator.h>\n\n#include <algorithm>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/init/Init.h>\n#include <folly/small_vector.h>\n\nusing namespace folly;\n\nnamespace {\n\ntemplate <class BaseIter>\nBitIterator<BaseIter> simpleFFS(\n    BitIterator<BaseIter> begin, BitIterator<BaseIter> end) {\n  return std::find(begin, end, true);\n}\n\ntemplate <class FFS>\nvoid runFFSTest(FFS fn) {\n  constexpr size_t const maxblocks = 3;\n  constexpr size_t const bpb = 8 * sizeof(uint64_t);\n  for (size_t nblocks = 1; nblocks <= maxblocks; ++nblocks) {\n    small_vector<uint64_t, maxblocks> data(nblocks, 0);\n    size_t nbits = nblocks * bpb;\n    auto begin = makeBitIterator(data.cbegin());\n    auto end = makeBitIterator(data.cend());\n    DCHECK_EQ(nbits, end - begin);\n    DCHECK(begin != end);\n\n    // Try every possible combination of first bit set (including none),\n    // start bit, end bit\n    for (size_t firstSet = 0; firstSet <= nbits; ++firstSet) {\n      data.assign(nblocks, 0);\n      if (firstSet) {\n        size_t b = firstSet - 1;\n        data[b / bpb] |= (1ULL << (b % bpb));\n      }\n      for (size_t startBit = 0; startBit <= nbits; ++startBit) {\n        for (size_t endBit = startBit; endBit <= nbits; ++endBit) {\n          auto p = begin + startBit;\n          auto q = begin + endBit;\n          p = fn(p, q);\n          doNotOptimizeAway(p);\n          if (firstSet < startBit + 1 || firstSet >= endBit + 1) {\n            DCHECK_EQ(endBit, p - begin)\n                << \"  firstSet=\" << firstSet << \" startBit=\" << startBit\n                << \" endBit=\" << endBit << \" nblocks=\" << nblocks;\n          } else {\n            DCHECK_EQ(firstSet - 1, p - begin)\n                << \"  firstSet=\" << firstSet << \" startBit=\" << startBit\n                << \" endBit=\" << endBit << \" nblocks=\" << nblocks;\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid runSimpleFFSTest(int iters) {\n  while (iters--) {\n    runFFSTest([](auto first, auto last) { return simpleFFS(first, last); });\n  }\n}\n\nvoid runRealFFSTest(int iters) {\n  while (iters--) {\n    runFFSTest([](auto first, auto last) { return findFirstSet(first, last); });\n  }\n}\n\n} // namespace\n\nBENCHMARK(SimpleFFSTest, iters) {\n  runSimpleFFSTest(iters);\n}\nBENCHMARK(RealFFSTest, iters) {\n  runRealFFSTest(iters);\n}\n\n/* --bm_min_iters=10 --bm_max_iters=100\n\nBenchmark                               Iters   Total t    t/iter iter/sec\n------------------------------------------------------------------------------\nrunSimpleFFSTest                           10   4.82 s     482 ms  2.075\nrunRealFFSTest                             19  2.011 s   105.9 ms  9.447\n\n*/\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/container/test/BitIteratorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/BitIterator.h>\n\n#include <forward_list>\n#include <limits>\n#include <list>\n#include <type_traits>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nnamespace {\n\ntemplate <class INT, class IT>\nvoid checkIt(INT exp, IT& it) {\n  using utype = typename std::make_unsigned<INT>::type;\n  size_t bits = std::numeric_limits<utype>::digits;\n  utype uexp = exp;\n  for (size_t i = 0; i < bits; ++i) {\n    bool e = uexp & 1;\n    EXPECT_EQ(e, *it++);\n    uexp >>= 1;\n  }\n}\n\ntemplate <class INT, class IT>\nvoid checkRange(INT exp, IT begin, IT end) {\n  using utype = typename std::make_unsigned<INT>::type;\n  utype uexp = exp;\n  size_t i = 0;\n  auto bitEnd = makeBitIterator(end);\n  for (BitIterator<IT> it = makeBitIterator(begin); it != bitEnd; ++it, ++i) {\n    bool e = uexp & 1;\n    EXPECT_EQ(e, *it);\n    uexp >>= 1;\n  }\n}\n\n} // namespace\n\nTEST(BitIterator, Simple) {\n  std::vector<int> v;\n  v.push_back(0x10);\n  v.push_back(0x42);\n  auto bi(makeBitIterator(v.begin()));\n  checkIt(0x10, bi);\n  checkIt(0x42, bi);\n  checkRange(0x0000004200000010ULL, v.begin(), v.end());\n\n  v[0] = 0;\n  bi = v.begin();\n  *bi++ = true; // 1\n  *bi++ = false;\n  *bi++ = true; // 4\n  *bi++ = false;\n  *bi++ = false;\n  *bi++ = true; // 32\n  *++bi = true; // 128 (note pre-increment)\n\n  EXPECT_EQ(165, v[0]);\n}\n\nTEST(BitIterator, Const) {\n  std::vector<int> v;\n  v.push_back(0x10);\n  v.push_back(0x42);\n  auto bi(makeBitIterator(v.cbegin()));\n  checkIt(0x10, bi);\n  checkIt(0x42, bi);\n}\n\nTEST(BitIterator, IteratorCategory) {\n  EXPECT_TRUE( //\n      (std::is_same<\n          std::iterator_traits<BitIterator<uint64_t*>>::iterator_category,\n          std::random_access_iterator_tag>::value));\n  EXPECT_TRUE(\n      (std::is_same<\n          std::iterator_traits<\n              BitIterator<std::vector<uint64_t>::iterator>>::iterator_category,\n          std::random_access_iterator_tag>::value));\n  EXPECT_TRUE(\n      (std::is_same<\n          std::iterator_traits<\n              BitIterator<std::list<uint64_t>::iterator>>::iterator_category,\n          std::bidirectional_iterator_tag>::value));\n  EXPECT_TRUE( //\n      (std::is_same<\n          std::iterator_traits<BitIterator<\n              std::forward_list<uint64_t>::iterator>>::iterator_category,\n          std::forward_iterator_tag>::value));\n}\n"
  },
  {
    "path": "folly/container/test/CollectionUtilTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/CollectionUtil.h>\n\n#include <deque>\n#include <list>\n#include <map>\n#include <set>\n#include <string>\n#include <unordered_map>\n#include <vector>\n#include <gtest/gtest.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/json/dynamic.h>\n#include <folly/small_vector.h>\n#include <folly/sorted_vector_types.h>\n\nusing namespace folly;\n\nTEST(CollectionUtilTest, simpleContains) {\n  auto map_test = [](auto&& m) {\n    m[\"key\"] = \"value\";\n    EXPECT_TRUE(folly::contains(m, \"key\"));\n    EXPECT_FALSE(folly::contains(m, \"value\"));\n  };\n  map_test(std::map<std::string, std::string>{});\n  map_test(std::unordered_map<std::string, std::string>{});\n  map_test(folly::F14FastMap<std::string, std::string>{});\n  map_test(folly::sorted_vector_map<std::string, std::string>{});\n\n  // test set types\n  auto set_test = [](auto&& s) {\n    s.insert(\"key\");\n    EXPECT_TRUE(folly::contains(s, \"key\"));\n    EXPECT_FALSE(folly::contains(s, \"value\"));\n  };\n  set_test(std::set<std::string>{});\n  set_test(std::unordered_set<std::string>{});\n  set_test(folly::F14FastSet<std::string>{});\n  set_test(folly::sorted_vector_set<std::string>{});\n\n  // test string type\n  std::string s = \"aloha\";\n  EXPECT_TRUE(folly::contains(s, 'h'));\n  EXPECT_FALSE(folly::contains(s, 'x'));\n\n  // test array types\n  std::array<int, 5> arr{1, 2, 3, 4, 5};\n  int raw_array[5]{1, 2, 3, 4, 5};\n  EXPECT_TRUE(folly::contains(arr, 4));\n  EXPECT_TRUE(folly::contains(raw_array, 4));\n  EXPECT_FALSE(folly::contains(arr, 100));\n  EXPECT_FALSE(folly::contains(raw_array, 100));\n}\n\nTEST(CollectionUtilTest, vectorContains) {\n  auto vector_test = [](auto&& vec) {\n    vec.push_back(5);\n    vec.push_back(1);\n    EXPECT_TRUE(folly::contains(vec, 5));\n    EXPECT_TRUE(folly::contains(vec, 1));\n    EXPECT_FALSE(folly::contains(vec, 0));\n  };\n  vector_test(std::vector<int>{});\n  vector_test(folly::small_vector<int, 10>{});\n  vector_test(std::list<int>{});\n  vector_test(std::deque<int>{});\n}\n\nTEST(CollectionUtilTest, hasContains) {\n  static_assert(detail::HasContains<std::map<int, int>, int>);\n  static_assert(detail::HasContains<std::unordered_map<int, int>, int>);\n  static_assert(detail::HasContains<folly::F14FastMap<int, int>, int>);\n  static_assert(detail::HasContains<folly::sorted_vector_map<int, int>, int>);\n  static_assert(detail::HasContains<std::set<int>, int>);\n  static_assert(detail::HasContains<std::unordered_set<int>, int>);\n  static_assert(detail::HasContains<folly::F14FastSet<int>, int>);\n  static_assert(detail::HasContains<folly::sorted_vector_set<int>, int>);\n\n  static_assert(\n      !detail::HasContains<std::vector<int>, int> &&\n      !detail::HasFind<std::vector<int>, int>);\n  static_assert(\n      !detail::HasContains<folly::small_vector<int>, int> &&\n      !detail::HasFind<folly::small_vector<int>, int>);\n  static_assert(\n      !detail::HasContains<folly::small_vector<int>, int> &&\n      !detail::HasFind<folly::small_vector<int>, int>);\n  static_assert(\n      !detail::HasContains<std::list<int>, int> &&\n      !detail::HasFind<std::list<int>, int>);\n  static_assert(\n      !detail::HasContains<std::deque<int>, int> &&\n      !detail::HasFind<std::deque<int>, int>);\n}\n\ntemplate <typename K, typename V>\nclass OnlyHasFind {\n public:\n  using const_iterator = typename std::map<K, V>::const_iterator;\n  void insert(K key, V value) {\n    map_.emplace(std::move(key), std::move(value));\n  }\n\n  const_iterator find(const K& key) const { return map_.find(key); }\n  const_iterator end() const { return map_.end(); }\n\n private:\n  std::map<K, V> map_;\n};\n\nTEST(CollectionUtilTest, hasFind) {\n  static_assert(\n      detail::HasFind<OnlyHasFind<int, int>, int> &&\n      !detail::HasContains<OnlyHasFind<int, int>, int>);\n\n  OnlyHasFind<int, int> myMap;\n  myMap.insert(1, 2);\n  EXPECT_TRUE(folly::contains(myMap, 1));\n  EXPECT_FALSE(folly::contains(myMap, 2));\n\n  OnlyHasFind<int, int> myMap2;\n  EXPECT_FALSE(folly::contains(myMap, 0));\n}\n\nTEST(CollectionUtilTest, dynamicContains) {\n  // folly::dynamic has unusual semantics. Test contains() explicitly with\n  // dynamic maps and arrays.\n\n  dynamic obj = dynamic::object(\"a\", 1)(\"b\", 2)(\"c\", 3);\n  EXPECT_TRUE(contains(obj, \"b\"));\n  EXPECT_FALSE(contains(obj, \"d\"));\n\n  dynamic arr = dynamic::array(1, 3, 5, 7);\n  EXPECT_TRUE(contains(arr, 5));\n  EXPECT_FALSE(contains(arr, 6));\n}\n"
  },
  {
    "path": "folly/container/test/EnumerateTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <string>\n#include <vector>\n\n#include <folly/Range.h>\n#include <folly/container/Enumerate.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\n\ntemplate <class T>\nstruct IsConstReference {\n  constexpr static bool value = false;\n};\ntemplate <class T>\nstruct IsConstReference<const T&> {\n  constexpr static bool value = true;\n};\n\nconstexpr int basicSum(const std::array<int, 3>& test) {\n  int sum = 0;\n  for (auto it : folly::enumerate(test)) {\n    sum += *it;\n  }\n  return sum;\n}\n\nconstexpr int cpp17StructuredBindingSum(const std::array<int, 3>& test) {\n  int sum = 0;\n  for (auto&& [_, integer] : folly::enumerate(test)) {\n    sum += integer;\n  }\n  return sum;\n}\n\n} // namespace\n\nTEST(Enumerate, Basic) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (auto it : folly::enumerate(v)) {\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n\n    /* Test mutability. */\n    std::string newValue = \"x\";\n    *it = newValue;\n    EXPECT_EQ(newValue, v[i]);\n\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, BasicRRef) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (auto&& it : folly::enumerate(v)) {\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n\n    /* Test mutability. */\n    std::string newValue = \"x\";\n    *it = newValue;\n    EXPECT_EQ(newValue, v[i]);\n\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, BasicConst) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (const auto it : folly::enumerate(v)) {\n    static_assert(IsConstReference<decltype(*it)>::value, \"Const enumeration\");\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, BasicConstRef) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (const auto& it : folly::enumerate(v)) {\n    static_assert(IsConstReference<decltype(*it)>::value, \"Const enumeration\");\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, BasicConstRRef) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (const auto&& it : folly::enumerate(v)) {\n    static_assert(IsConstReference<decltype(*it)>::value, \"Const enumeration\");\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, BasicVecBool) {\n  std::vector<bool> v = {true, false, false, true};\n  size_t i = 0;\n  for (auto it : folly::enumerate(v)) {\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, BasicVecBoolRRef) {\n  std::vector<bool> v = {true, false, false, true};\n  size_t i = 0;\n  for (auto it : folly::enumerate(v)) {\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, Temporary) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (auto&& it : folly::enumerate(decltype(v)(v))) { // Copy v.\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, BasicConstArg) {\n  const std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (auto&& it : folly::enumerate(v)) {\n    static_assert(\n        IsConstReference<decltype(*it)>::value, \"Enumerating a const vector\");\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, TemporaryConstEnumerate) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (const auto&& it : folly::enumerate(decltype(v)(v))) { // Copy v.\n    static_assert(IsConstReference<decltype(*it)>::value, \"Const enumeration\");\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, RangeSupport) {\n  std::vector<std::string> v = {\"abc\", \"a\", \"ab\"};\n  size_t i = 0;\n  for (const auto&& it : folly::enumerate(folly::range(v))) {\n    EXPECT_EQ(it.index, i);\n    EXPECT_EQ(*it, v[i]);\n    EXPECT_EQ(it->size(), v[i].size());\n    ++i;\n  }\n\n  EXPECT_EQ(i, v.size());\n}\n\nTEST(Enumerate, EmptyRange) {\n  std::vector<std::string> v;\n  for (auto&& it : folly::enumerate(v)) {\n    (void)it; // Silence warnings.\n    ADD_FAILURE();\n  }\n}\n\nclass CStringRange {\n  const char* cstr;\n\n public:\n  struct Sentinel {};\n\n  explicit CStringRange(const char* cstr_) : cstr(cstr_) {}\n\n  const char* begin() const { return cstr; }\n  Sentinel end() const { return Sentinel{}; }\n};\n\nbool operator==(const char* c, CStringRange::Sentinel) {\n  return *c == 0;\n}\n\nTEST(Enumerate, Cpp17Support) {\n  std::array<char, 5> test = {\"test\"};\n  for (const auto&& it : folly::enumerate(CStringRange{test.data()})) {\n    ASSERT_LT(it.index, test.size());\n    EXPECT_EQ(*it, test[it.index]);\n  }\n}\n\nTEST(Enumerate, Cpp17StructuredBindingConstRef) {\n  std::vector<std::string> test = {\"abc\", \"a\", \"ab\"};\n  for (const auto& [index, str] : folly::enumerate(test)) {\n    ASSERT_LT(index, test.size());\n    EXPECT_EQ(str, test[index]);\n  }\n}\n\nTEST(Enumerate, Cpp17StructuredBindingConstRRef) {\n  std::vector<std::string> test = {\"abc\", \"a\", \"ab\"};\n  for (const auto&& [index, str] : folly::enumerate(test)) {\n    ASSERT_LT(index, test.size());\n    EXPECT_EQ(str, test[index]);\n  }\n}\n\nTEST(Enumerate, Cpp17StructuredBindingConstVector) {\n  const std::vector<std::string> test = {\"abc\", \"a\", \"ab\"};\n  for (auto&& [index, str] : folly::enumerate(test)) {\n    static_assert(\n        IsConstReference<decltype(str)>::value, \"Enumerating const vector\");\n    ASSERT_LT(index, test.size());\n    EXPECT_EQ(str, test[index]);\n  }\n}\n\nTEST(Enumerate, Cpp17StructuredBindingModify) {\n  std::vector<int> test = {1, 2, 3, 4, 5};\n  for (auto&& [index, integer] : folly::enumerate(test)) {\n    integer = 0;\n  }\n\n  for (const auto& integer : test) {\n    EXPECT_EQ(integer, 0);\n  }\n}\n\nTEST(Enumerate, BasicConstexpr) {\n  constexpr std::array<int, 3> test = {1, 2, 3};\n  static_assert(basicSum(test) == 6, \"Basic enumerating is not constexpr\");\n  EXPECT_EQ(basicSum(test), 6);\n}\n\nTEST(Enumerate, Cpp17StructuredBindingConstexpr) {\n  constexpr std::array<int, 3> test = {1, 2, 3};\n  static_assert(\n      cpp17StructuredBindingSum(test) == 6,\n      \"C++17 structured binding enumerating is not constexpr\");\n  EXPECT_EQ(cpp17StructuredBindingSum(test), 6);\n}\n"
  },
  {
    "path": "folly/container/test/EvictingCacheMapBench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/EvictingCacheMap.h>\n\n#include <folly/Benchmark.h>\n\nusing namespace folly;\n\ninline uint64_t key(size_t i) {\n  // Sensitive to non-avalanched hasher\n  uint64_t marker = uint64_t{12345} << 48;\n  uint64_t tmp = marker ^ i;\n  int shift = ((i >> 8) & 7) * 8;\n  return (tmp << shift) | (tmp >> (64 - shift));\n}\n\n// This function creates a cache of size `numElts` and scans through it `n`\n// times in order to cause the most churn in the LRU structure as possible.\n// This is meant to exercise the performance of the lookup path in the cache,\n// most notably promotions within the LRU.\nvoid scanCache(uint32_t n, size_t numElts) {\n  BenchmarkSuspender suspender;\n\n  EvictingCacheMap<uint64_t, size_t> m(numElts);\n  for (size_t i = 0; i < numElts; ++i) {\n    m.insert(key(i), i);\n  }\n\n  suspender.dismiss();\n  for (uint32_t i = 0; i < n; ++i) {\n    for (size_t j = 0; j < numElts; ++j) {\n      m.get(key(j));\n    }\n  }\n}\n\nvoid insertCache(uint32_t n, size_t numElts) {\n  // Under-size to force cache evictions\n  EvictingCacheMap<uint64_t, size_t> m(numElts * 9 / 10);\n  for (uint32_t i = 0; i < n; ++i) {\n    for (size_t j = 0; j < numElts; ++j) {\n      m.insert(key(j), i);\n    }\n  }\n}\n\n// Increment by factor of 4 * golden ratio to vary distance between\n// powers of 2\nBENCHMARK_PARAM(scanCache, 1000)\nBENCHMARK_PARAM(scanCache, 6472)\nBENCHMARK_PARAM(scanCache, 41889)\nBENCHMARK_PARAM(scanCache, 271108)\nBENCHMARK_PARAM(scanCache, 1754650) // 1.75M\nBENCHMARK_PARAM(scanCache, 11356334) // 11.3M\n\nBENCHMARK_PARAM(insertCache, 1000)\nBENCHMARK_PARAM(insertCache, 6472)\nBENCHMARK_PARAM(insertCache, 41889)\nBENCHMARK_PARAM(insertCache, 271108)\nBENCHMARK_PARAM(insertCache, 1754650) // 1.75M\nBENCHMARK_PARAM(insertCache, 11356334) // 11.3M\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  runBenchmarks();\n}\n"
  },
  {
    "path": "folly/container/test/EvictingCacheMapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/EvictingCacheMap.h>\n\n#include <set>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(EvictingCacheMap, SanityTest) {\n  EvictingCacheMap<int, int> map(0);\n\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(1));\n  map.set(1, 1);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  EXPECT_EQ(1, map.get(1));\n  EXPECT_TRUE(map.exists(1));\n  map.set(1, 2);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  EXPECT_EQ(2, map.get(1));\n  EXPECT_TRUE(map.exists(1));\n  map.erase(1);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(1));\n\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(1));\n  map.set(1, 1);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  EXPECT_EQ(1, map.get(1));\n  EXPECT_TRUE(map.exists(1));\n  map.set(1, 2);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  EXPECT_EQ(2, map.get(1));\n  EXPECT_TRUE(map.exists(1));\n\n  EXPECT_FALSE(map.exists(2));\n  map.set(2, 1);\n  EXPECT_TRUE(map.exists(2));\n  EXPECT_EQ(2, map.size());\n  EXPECT_FALSE(map.empty());\n  EXPECT_EQ(1, map.get(2));\n  map.set(2, 2);\n  EXPECT_EQ(2, map.size());\n  EXPECT_FALSE(map.empty());\n  EXPECT_EQ(2, map.get(2));\n  EXPECT_TRUE(map.exists(2));\n  map.erase(2);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  EXPECT_FALSE(map.exists(2));\n  map.erase(1);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(1));\n}\n\nTEST(EvictingCacheMap, PruneTest) {\n  EvictingCacheMap<int, int> map(0);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(1000000);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(100);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(99);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 99; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  EXPECT_TRUE(map.exists(99));\n  EXPECT_EQ(99, map.get(99));\n\n  map.prune(100);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(90);\n  EXPECT_EQ(10, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 90; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  for (int i = 90; i < 100; i++) {\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n}\n\nTEST(EvictingCacheMap, PruneHookTest) {\n  EvictingCacheMap<int, int> map(0);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  int sum = 0;\n  auto pruneCb = [&](int&& k, int&& v) {\n    EXPECT_EQ(k, v);\n    sum += k;\n  };\n\n  EXPECT_FALSE(map.getPruneHook());\n  map.setPruneHook(pruneCb);\n  EXPECT_TRUE(map.getPruneHook());\n  {\n    int v = 42;\n    map.getPruneHook()(42, std::move(v));\n  }\n  EXPECT_EQ(42, sum);\n  sum = 0;\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(1000000);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  EXPECT_EQ((99 * 100) / 2, sum);\n  sum = 0;\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(100);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  EXPECT_EQ((99 * 100) / 2, sum);\n  sum = 0;\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(99);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 99; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  EXPECT_TRUE(map.exists(99));\n  EXPECT_EQ(99, map.get(99));\n\n  EXPECT_EQ((98 * 99) / 2, sum);\n  sum = 0;\n\n  map.prune(100);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  EXPECT_EQ(99, sum);\n  sum = 0;\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  map.prune(90);\n  EXPECT_EQ(10, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 90; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  for (int i = 90; i < 100; i++) {\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n  EXPECT_EQ((89 * 90) / 2, sum);\n  sum = 0;\n\n  // Erase does not call prune hook (NOTE: possible source of usage bugs)\n  map.erase(99);\n\n  EXPECT_EQ(0, sum);\n\n  // But can provide your own hook\n  map.erase(98, pruneCb);\n\n  EXPECT_EQ(98, sum);\n  sum = 0;\n\n  // And with iterator\n  auto it1 = map.find(96);\n  auto it2 = map.find(97);\n\n  auto it3 = map.erase(it2, pruneCb);\n  EXPECT_EQ(it1, it3);\n  EXPECT_EQ(97, sum);\n  sum = 0;\n\n  // Destructor does not call prune hook (NOTE: possibly source of usage bugs)\n  map.~EvictingCacheMap<int, int>();\n  // Re-enter clean state\n  new (&map) EvictingCacheMap<int, int>(0);\n\n  EXPECT_EQ(0, sum);\n}\n\nTEST(EvictingCacheMap, SetMaxSize) {\n  EvictingCacheMap<int, int> map(100, 20);\n  for (int i = 0; i < 90; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n  }\n\n  EXPECT_EQ(90, map.size());\n  map.setMaxSize(50);\n  EXPECT_EQ(map.size(), 50);\n\n  for (int i = 0; i < 90; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n  }\n  EXPECT_EQ(40, map.size());\n  map.setMaxSize(0);\n  EXPECT_EQ(40, map.size());\n  map.setMaxSize(10);\n  EXPECT_EQ(10, map.size());\n}\n\nTEST(EvictingCacheMap, SetClearSize) {\n  EvictingCacheMap<int, int> map(100, 20);\n  for (int i = 0; i < 90; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n  }\n\n  EXPECT_EQ(90, map.size());\n  map.setClearSize(40);\n  map.setMaxSize(50);\n  EXPECT_EQ(map.size(), 50);\n\n  for (int i = 0; i < 90; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n  }\n  EXPECT_EQ(20, map.size());\n  map.setMaxSize(0);\n  EXPECT_EQ(20, map.size());\n  map.setMaxSize(10);\n  EXPECT_EQ(0, map.size());\n}\n\nTEST(EvictingCacheMap, DestructorInvocationTest) {\n  struct SumInt {\n    SumInt(int val_, int* ref_) : val(val_), ref(ref_) {}\n    ~SumInt() { *ref += val; }\n\n    SumInt(SumInt const&) = delete;\n    SumInt& operator=(SumInt const&) = delete;\n\n    SumInt(SumInt&& other) : val(std::exchange(other.val, 0)), ref(other.ref) {}\n    SumInt& operator=(SumInt&& other) {\n      std::swap(val, other.val);\n      std::swap(ref, other.ref);\n      return *this;\n    }\n\n    int val;\n    int* ref;\n  };\n\n  int sum;\n  EvictingCacheMap<int, SumInt> map(0);\n\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, SumInt(i, &sum));\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i).val);\n  }\n\n  sum = 0;\n  map.prune(1000000);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  EXPECT_EQ((99 * 100) / 2, sum);\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, SumInt(i, &sum));\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i).val);\n  }\n\n  sum = 0;\n  map.prune(100);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  EXPECT_EQ((99 * 100) / 2, sum);\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, SumInt(i, &sum));\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i).val);\n  }\n\n  sum = 0;\n  map.prune(99);\n  EXPECT_EQ(1, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 99; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  EXPECT_TRUE(map.exists(99));\n  EXPECT_EQ(99, map.get(99).val);\n\n  EXPECT_EQ((98 * 99) / 2, sum);\n\n  sum = 0;\n  map.prune(100);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  EXPECT_EQ(99, sum);\n  for (int i = 0; i < 100; i++) {\n    map.set(i, SumInt(i, &sum));\n    EXPECT_EQ(i + 1, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i).val);\n  }\n\n  sum = 0;\n  map.prune(90);\n  EXPECT_EQ(10, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 90; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  for (int i = 90; i < 100; i++) {\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i).val);\n  }\n  EXPECT_EQ((89 * 90) / 2, sum);\n\n  sum = 0;\n  for (int i = 0; i < 90; i++) {\n    auto pair = map.insert(i, SumInt(i + 1, &sum));\n    EXPECT_EQ(i + 1, pair.first->second.val);\n    EXPECT_TRUE(pair.second);\n    EXPECT_TRUE(map.exists(i));\n  }\n  EXPECT_EQ(0, sum);\n  for (int i = 90; i < 100; i++) {\n    auto pair = map.insert(i, SumInt(i + 1, &sum));\n    EXPECT_EQ(i, pair.first->second.val);\n    EXPECT_FALSE(pair.second);\n    EXPECT_TRUE(map.exists(i));\n  }\n  EXPECT_EQ((10 * 191) / 2, sum);\n  sum = 0;\n  map.prune(100);\n  EXPECT_EQ((90 * 91) / 2 + (10 * 189) / 2, sum);\n\n  sum = 0;\n  map.set(3, SumInt(3, &sum));\n  map.set(2, SumInt(2, &sum));\n  map.set(1, SumInt(1, &sum));\n  EXPECT_EQ(0, sum);\n  EXPECT_EQ(2, map.erase(map.find(1))->second.val);\n  EXPECT_EQ(1, sum);\n  EXPECT_EQ(map.end(), map.erase(map.findWithoutPromotion(3)));\n  EXPECT_EQ(4, sum);\n  map.prune(1);\n  EXPECT_EQ(6, sum);\n}\n\nTEST(EvictingCacheMap, LruSanityTest) {\n  EvictingCacheMap<int, int> map(10);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_GE(10, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  EXPECT_EQ(10, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 90; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  for (int i = 90; i < 100; i++) {\n    EXPECT_TRUE(map.exists(i));\n  }\n}\n\nTEST(EvictingCacheMap, LruPromotionTest) {\n  EvictingCacheMap<int, int> map(10);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_GE(10, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n    for (int j = 0; j < std::min(i + 1, 9); j++) {\n      EXPECT_TRUE(map.exists(j));\n      EXPECT_EQ(j, map.get(j));\n    }\n  }\n\n  EXPECT_EQ(10, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 9; i++) {\n    EXPECT_TRUE(map.exists(i));\n  }\n  EXPECT_TRUE(map.exists(99));\n  for (int i = 10; i < 99; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n}\n\nTEST(EvictingCacheMap, LruNoPromotionTest) {\n  EvictingCacheMap<int, int> map(10);\n  EXPECT_EQ(0, map.size());\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < 100; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n\n  for (int i = 0; i < 100; i++) {\n    map.set(i, i);\n    EXPECT_GE(10, map.size());\n    EXPECT_FALSE(map.empty());\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n    for (int j = 0; j < std::min(i + 1, 9); j++) {\n      if (map.exists(j)) {\n        EXPECT_EQ(j, map.getWithoutPromotion(j));\n      }\n    }\n  }\n\n  EXPECT_EQ(10, map.size());\n  EXPECT_FALSE(map.empty());\n  for (int i = 0; i < 90; i++) {\n    EXPECT_FALSE(map.exists(i));\n  }\n  for (int i = 90; i < 100; i++) {\n    EXPECT_TRUE(map.exists(i));\n  }\n}\n\nTEST(EvictingCacheMap, IteratorSanityTest) {\n  const int nItems = 1000;\n  EvictingCacheMap<int, int> map(nItems);\n  EXPECT_TRUE(map.begin() == map.end());\n  for (int i = 0; i < nItems; i++) {\n    EXPECT_FALSE(map.exists(i));\n    map.set(i, i * 2);\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i * 2, map.get(i));\n  }\n\n  std::set<int> seen;\n  for (auto& it : map) {\n    EXPECT_FALSE(seen.contains(it.first));\n    seen.insert(it.first);\n    EXPECT_EQ(it.first * 2, it.second);\n  }\n  EXPECT_EQ(nItems, seen.size());\n}\n\nTEST(EvictingCacheMap, FindTest) {\n  const int nItems = 1000;\n  EvictingCacheMap<int, int> map(nItems);\n  for (int i = 0; i < nItems; i++) {\n    map.set(i * 2, i * 2);\n    EXPECT_TRUE(map.exists(i * 2));\n    EXPECT_EQ(i * 2, map.get(i * 2));\n  }\n  for (int i = 0; i < nItems * 2; i++) {\n    if (i % 2 == 0) {\n      auto it = map.find(i);\n      EXPECT_FALSE(it == map.end());\n      EXPECT_EQ(i, it->first);\n      EXPECT_EQ(i, it->second);\n    } else {\n      EXPECT_TRUE(map.find(i) == map.end());\n    }\n  }\n  for (int i = nItems * 2 - 1; i >= 0; i--) {\n    if (i % 2 == 0) {\n      auto it = map.find(i);\n      EXPECT_FALSE(it == map.end());\n      EXPECT_EQ(i, it->first);\n      EXPECT_EQ(i, it->second);\n    } else {\n      EXPECT_TRUE(map.find(i) == map.end());\n    }\n  }\n  EXPECT_EQ(0, map.begin()->first);\n}\n\nTEST(EvictingCacheMap, FindWithoutPromotionTest) {\n  const int nItems = 1000;\n  EvictingCacheMap<int, int> map(nItems);\n  for (int i = 0; i < nItems; i++) {\n    map.set(i * 2, i * 2);\n    EXPECT_TRUE(map.exists(i * 2));\n    EXPECT_EQ(i * 2, map.get(i * 2));\n  }\n  for (int i = nItems * 2 - 1; i >= 0; i--) {\n    if (i % 2 == 0) {\n      auto it = map.findWithoutPromotion(i);\n      EXPECT_FALSE(it == map.end());\n      EXPECT_EQ(i, it->first);\n      EXPECT_EQ(i, it->second);\n    } else {\n      EXPECT_TRUE(map.findWithoutPromotion(i) == map.end());\n    }\n  }\n  EXPECT_EQ((nItems - 1) * 2, map.begin()->first);\n}\n\nTEST(EvictingCacheMap, IteratorOrderingTest) {\n  const int nItems = 1000;\n  EvictingCacheMap<int, int> map(nItems);\n  for (int i = 0; i < nItems; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  int expected = nItems - 1;\n  for (auto it = map.begin(); it != map.end(); ++it) {\n    EXPECT_EQ(expected, it->first);\n    expected--;\n  }\n\n  expected = 0;\n  for (auto it = map.rbegin(); it != map.rend(); ++it) {\n    EXPECT_EQ(expected, it->first);\n    expected++;\n  }\n\n  {\n    auto it = map.end();\n    expected = 0;\n    EXPECT_TRUE(it != map.begin());\n    do {\n      --it;\n      EXPECT_EQ(expected, it->first);\n      expected++;\n    } while (it != map.begin());\n    EXPECT_EQ(nItems, expected);\n  }\n\n  {\n    auto it = map.rend();\n    expected = nItems - 1;\n    do {\n      --it;\n      EXPECT_EQ(expected, it->first);\n      expected--;\n    } while (it != map.rbegin());\n    EXPECT_EQ(-1, expected);\n  }\n}\n\nTEST(EvictingCacheMap, MoveTest) {\n  const int nItems = 1000;\n  EvictingCacheMap<int, int> map(nItems);\n  for (int i = 0; i < nItems; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n  }\n\n  // Move to empty\n  EvictingCacheMap<int, int> map2 = std::move(map);\n  EXPECT_TRUE(map.empty());\n  for (int i = 0; i < nItems; i++) {\n    EXPECT_TRUE(map2.exists(i));\n    EXPECT_EQ(i, map2.get(i));\n  }\n\n  // Move to non-empty\n  EvictingCacheMap<int, int> map3(1);\n  map3.set(1, 1);\n  EXPECT_EQ(1, map3.size());\n  map3 = std::move(map2);\n  EXPECT_TRUE(map2.empty());\n  EXPECT_EQ(nItems, map3.size());\n}\n\nTEST(EvictingCacheMap, CustomKeyEqual) {\n  const int nItems = 100;\n  struct Eq {\n    bool operator()(const int& a, const int& b) const {\n      return (a % mod) == (b % mod);\n    }\n    int mod;\n  };\n  struct Hash {\n    size_t operator()(const int& a) const { return std::hash<int>()(a % mod); }\n    int mod;\n  };\n  EvictingCacheMap<int, int, Hash, Eq> map(\n      nItems, 1 /* clearSize */, Hash{nItems}, Eq{nItems});\n  for (int i = 0; i < nItems; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n    EXPECT_EQ(i, map.get(i));\n    EXPECT_TRUE(map.exists(i + nItems));\n    EXPECT_EQ(i, map.get(i + nItems));\n  }\n}\n\nTEST(EvictingCacheMap, InvalidHashPartlyUsable) {\n  // Some uses of EvictingCacheMap only use constructor+destructor in a header\n  // file where the hasher is invalid. (Only fully defined in cpp file.)\n  struct BadHash {};\n  struct BadEq {};\n  EvictingCacheMap<int, int, BadHash, BadEq> map{42};\n  // Also move ctor and operator\n  EvictingCacheMap<int, int, BadHash, BadEq> map2{std::move(map)};\n  map = std::move(map2);\n}\n\nTEST(EvictingCacheMap, IteratorConversion) {\n  using type = EvictingCacheMap<int, int>;\n  using i = type::iterator;\n  using ci = type::const_iterator;\n  using ri = type::reverse_iterator;\n  using cri = type::const_reverse_iterator;\n\n  EXPECT_TRUE((std::is_convertible<i, i>::value));\n  EXPECT_TRUE((std::is_convertible<i, ci>::value));\n  EXPECT_FALSE((std::is_convertible<ci, i>::value));\n  EXPECT_TRUE((std::is_convertible<ci, ci>::value));\n\n  EXPECT_TRUE((std::is_convertible<ri, ri>::value));\n  EXPECT_TRUE((std::is_convertible<ri, cri>::value));\n  EXPECT_FALSE((std::is_convertible<cri, ri>::value));\n  EXPECT_TRUE((std::is_convertible<cri, cri>::value));\n}\n\nTEST(EvictingCacheMap, HeterogeneousAccess) {\n  constexpr std::array pieces{\n      std::pair{\"one\"_sp, 1},\n      std::pair{\"two\"_sp, 2},\n      std::pair{\"three\"_sp, 3},\n  };\n  constexpr std::array charstars{\n      std::pair{\"four\", 4},\n      std::pair{\"five\", 5},\n      std::pair{\"six\", 6},\n      std::pair{\"seven\", 7},\n  };\n\n  EvictingCacheMap<std::string, int> map(0);\n  for (auto&& [key, value] : pieces) {\n    auto [_, inserted] = map.insert(key, value);\n    EXPECT_TRUE(inserted);\n  }\n  for (auto&& [key, value] : charstars) {\n    map.set(key, value);\n  }\n\n  for (auto&& [key, value] : pieces) {\n    auto exists = map.exists(key);\n    EXPECT_TRUE(exists);\n    auto iter = map.find(key);\n    EXPECT_TRUE(iter != map.end());\n    EXPECT_EQ(iter->second, value);\n    iter = map.findWithoutPromotion(key);\n    EXPECT_TRUE(iter != map.end());\n    EXPECT_EQ(iter->second, value);\n  }\n  for (auto&& [key, value] : charstars) {\n    auto result = map.get(key);\n    EXPECT_EQ(result, value);\n    result = map.getWithoutPromotion(key);\n    EXPECT_EQ(result, value);\n  }\n\n  for (auto&& [key, _] : pieces) {\n    auto erased = map.erase(key);\n    EXPECT_TRUE(erased);\n    erased = map.erase(key);\n    EXPECT_FALSE(erased);\n  }\n  for (auto&& [key, _] : charstars) {\n    map.erase(map.findWithoutPromotion(key));\n  }\n  EXPECT_TRUE(map.empty());\n}\n\nTEST(EvictingCacheMap, ApproximateEntryMemUsage) {\n  // Entry (without weight) should be\n  // * two pointers for LRU list\n  // * sizeof(key) and sizeof(value)\n  // * roughly 1.5 pointers for F14 index\n  // And sizeof(std::unique_ptr<char[]>) should usually be size of raw pointer\n  EXPECT_EQ(sizeof(std::unique_ptr<char[]>), sizeof(char*));\n  EXPECT_LE(\n      (EvictingCacheMap<uint64_t, std::unique_ptr<char[]>>::\n           kApproximateEntryMemUsage),\n      48U);\n}\n\nTEST(EvictingCacheMap, PiecewiseConstructTest) {\n  EvictingCacheMap<uint64_t, std::string> map(1);\n  uint64_t i = 1;\n  auto [iter, inserted] = map.try_emplace(i, \"test\");\n  EXPECT_TRUE(inserted);\n  EXPECT_EQ(iter->second, \"test\");\n}\n\nTEST(EvictingCacheMap, ZeroClearSizeClampingTest) {\n  // Test that clearSize = 0 gets clamped to 1\n  constexpr size_t kMaxSize = 5;\n  constexpr size_t kClearSize = 0;\n  EvictingCacheMap<int, int> map(kMaxSize, kClearSize);\n\n  // Fill the cache to maxSize\n  for (int i = 0; i < 5; i++) {\n    map.set(i, i);\n    EXPECT_TRUE(map.exists(i));\n  }\n  EXPECT_EQ(5, map.size());\n\n  // Adding one more should trigger eviction\n  // Even though clearSize was set to 0, it should be clamped to 1\n  map.set(5, 5);\n  EXPECT_EQ(5, map.size()); // Size should still be maxSize\n  EXPECT_TRUE(map.exists(5)); // New element should exist\n  EXPECT_FALSE(map.exists(0)); // Oldest element should be evicted\n\n  // Verify that exactly 1 element was cleared (not 0)\n  for (int i = 1; i < 6; i++) {\n    EXPECT_TRUE(map.exists(i));\n  }\n}\n\nTEST(EvictingCacheMap, SetClearSizeZeroClampingTest) {\n  constexpr size_t kMaxSize = 10;\n  constexpr size_t kClearSize = 5;\n  EvictingCacheMap<int, int> map(kMaxSize, kClearSize);\n\n  // Fill cache\n  for (int i = 0; i < 10; i++) {\n    map.set(i, i);\n  }\n  EXPECT_EQ(10, map.size());\n\n  // Set clearSize to 0 - should be clamped to 1\n  map.setClearSize(0);\n\n  // Trigger eviction by reducing maxSize\n  map.setMaxSize(5);\n  EXPECT_EQ(5, map.size());\n\n  // Add more elements to test that clearSize=1 is being used\n  map.set(10, 10);\n  EXPECT_EQ(5, map.size());\n}\n"
  },
  {
    "path": "folly/container/test/F14AsanSupportTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n#include <folly/container/F14Set.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\ntemplate <typename S>\nvoid useIteratorAfterInsertSmall() {\n  S set;\n  auto iter = set.insert(1).first;\n  set.insert(2);\n  EXPECT_EQ(*iter, 1);\n}\n\ntemplate <typename S>\nvoid useIteratorAfterInsertLarge() {\n  S set;\n  set.insert(10);\n  for (int i = 0; i < 1000; ++i) {\n    auto iter = set.find(10);\n    set.insert(i);\n    if (i > 500) {\n      EXPECT_EQ(*iter, 10);\n    }\n  }\n}\n\ntemplate <typename S>\nvoid useReferenceAfterInsertSmall() {\n  S set;\n  auto& ref = *set.insert(1).first;\n  set.insert(2);\n  EXPECT_EQ(ref, 1);\n}\n\ntemplate <typename S>\nvoid useReferenceAfterInsertLarge() {\n  S set;\n  set.insert(10);\n  for (int i = 0; i < 1000; ++i) {\n    auto& ref = *set.find(10);\n    set.insert(i);\n    if (i > 500) {\n      EXPECT_EQ(ref, 10);\n    }\n  }\n}\n\ntemplate <typename F>\nvoid repeat(F const& func) {\n  for (int i = 0; i < 32; ++i) {\n    func();\n  }\n}\n\nbool asanFailuresExpected() {\n  return kIsLibrarySanitizeAddress &&\n      f14::detail::getF14IntrinsicsMode() !=\n      f14::detail::F14IntrinsicsMode::None;\n}\n\ntemplate <typename F>\nvoid expectAsanFailure(F const& func) {\n  if (asanFailuresExpected()) {\n    EXPECT_EXIT(\n        repeat(func), testing::ExitedWithCode(1), \".*heap-use-after-free.*\");\n  }\n}\n\nTEST(F14AsanSupportTest, F14ValueIterInsertSmall) {\n  expectAsanFailure(useIteratorAfterInsertSmall<F14ValueSet<int>>);\n}\nTEST(F14AsanSupportTest, F14NodeIterInsertSmall) {\n  expectAsanFailure(useIteratorAfterInsertSmall<F14NodeSet<int>>);\n}\nTEST(F14AsanSupportTest, F14VectorIterInsertSmall) {\n  expectAsanFailure(useIteratorAfterInsertSmall<F14VectorSet<int>>);\n}\nTEST(F14AsanSupportTest, F14FastIterInsertSmall) {\n  expectAsanFailure(useIteratorAfterInsertSmall<F14FastSet<int>>);\n}\n\nTEST(F14AsanSupportTest, F14ValueIterInsertLarge) {\n  expectAsanFailure(useIteratorAfterInsertLarge<F14ValueSet<int>>);\n}\nTEST(F14AsanSupportTest, F14NodeIterInsertLarge) {\n  expectAsanFailure(useIteratorAfterInsertLarge<F14NodeSet<int>>);\n}\nTEST(F14AsanSupportTest, F14VectorIterInsertLarge) {\n  expectAsanFailure(useIteratorAfterInsertLarge<F14VectorSet<int>>);\n}\nTEST(F14AsanSupportTest, F14FastIterInsertLarge) {\n  expectAsanFailure(useIteratorAfterInsertLarge<F14FastSet<int>>);\n}\n\nTEST(F14AsanSupportTest, F14ValueRefInsertSmall) {\n  expectAsanFailure(useReferenceAfterInsertSmall<F14ValueSet<int>>);\n}\nTEST(F14AsanSupportTest, F14VectorRefInsertSmall) {\n  expectAsanFailure(useReferenceAfterInsertSmall<F14VectorSet<int>>);\n}\nTEST(F14AsanSupportTest, F14FastRefInsertSmall) {\n  expectAsanFailure(useReferenceAfterInsertSmall<F14FastSet<int>>);\n}\n\nTEST(F14AsanSupportTest, F14ValueRefInsertLarge) {\n  expectAsanFailure(useReferenceAfterInsertLarge<F14ValueSet<int>>);\n}\nTEST(F14AsanSupportTest, F14VectorRefInsertLarge) {\n  expectAsanFailure(useReferenceAfterInsertLarge<F14VectorSet<int>>);\n}\nTEST(F14AsanSupportTest, F14FastRefInsertLarge) {\n  expectAsanFailure(useReferenceAfterInsertLarge<F14FastSet<int>>);\n}\n\nTEST(F14AsanSupportTest, F14VectorErase) {\n  F14VectorSet<int> set;\n  set.insert(1);\n  set.insert(2);\n  set.insert(3);\n  auto& v = *set.begin();\n  EXPECT_EQ(v, 3);\n  set.erase(2);\n  if (asanFailuresExpected()) {\n    EXPECT_NE(v, 3);\n  }\n}\n"
  },
  {
    "path": "folly/container/test/F14FwdTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/F14Map-fwd.h>\n#include <folly/container/F14Set-fwd.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\ntemplate <typename TContainer>\nvoid foo(TContainer*) {}\n} // namespace\n\nTEST(F14Fwd, simple) {\n  using namespace folly;\n  foo<F14NodeMap<int, int>>(nullptr);\n  foo<F14ValueMap<int, int>>(nullptr);\n  foo<F14VectorMap<int, int>>(nullptr);\n  foo<F14FastMap<int, int>>(nullptr);\n\n  foo<F14NodeSet<int>>(nullptr);\n  foo<F14ValueSet<int>>(nullptr);\n  foo<F14VectorSet<int>>(nullptr);\n  foo<F14FastSet<int>>(nullptr);\n}\n"
  },
  {
    "path": "folly/container/test/F14InterprocessTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <ios>\n#include <iostream>\n#include <memory>\n#include <scoped_allocator>\n#include <string>\n#include <vector>\n\n#include <boost/interprocess/allocators/adaptive_pool.hpp>\n#include <boost/interprocess/managed_shared_memory.hpp>\n\n#include <fmt/core.h>\n\n#include <folly/Random.h>\n#include <folly/Traits.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/portability/GTest.h>\n\nusing namespace boost::interprocess;\n\ntemplate <typename T>\nusing ShmAllocator = adaptive_pool<T, managed_shared_memory::segment_manager>;\n\ntemplate <typename K, typename M>\nusing ShmF14ValueMap = folly::F14ValueMap<\n    K,\n    M,\n    folly::f14::DefaultHasher<K>,\n    folly::f14::DefaultKeyEqual<K>,\n    ShmAllocator<std::pair<K const, M>>>;\n\ntemplate <typename K, typename M>\nusing ShmF14NodeMap = folly::F14NodeMap<\n    K,\n    M,\n    folly::f14::DefaultHasher<K>,\n    folly::f14::DefaultKeyEqual<K>,\n    ShmAllocator<std::pair<K const, M>>>;\n\ntemplate <typename K, typename M>\nusing ShmF14VectorMap = folly::F14VectorMap<\n    K,\n    M,\n    folly::f14::DefaultHasher<K>,\n    folly::f14::DefaultKeyEqual<K>,\n    ShmAllocator<std::pair<K const, M>>>;\n\ntemplate <typename K>\nusing ShmF14ValueSet = folly::F14ValueSet<\n    K,\n    folly::f14::DefaultHasher<K>,\n    folly::f14::DefaultKeyEqual<K>,\n    ShmAllocator<K>>;\n\ntemplate <typename K>\nusing ShmF14NodeSet = folly::F14NodeSet<\n    K,\n    folly::f14::DefaultHasher<K>,\n    folly::f14::DefaultKeyEqual<K>,\n    ShmAllocator<K>>;\n\ntemplate <typename K>\nusing ShmF14VectorSet = folly::F14VectorSet<\n    K,\n    folly::f14::DefaultHasher<K>,\n    folly::f14::DefaultKeyEqual<K>,\n    ShmAllocator<K>>;\n\nusing ShmVI = std::vector<int, ShmAllocator<int>>;\nusing ShmVVI =\n    std::vector<ShmVI, std::scoped_allocator_adaptor<ShmAllocator<ShmVI>>>;\n\nusing ShmF14ValueI2VVI = folly::F14ValueMap<\n    int,\n    ShmVVI,\n    folly::f14::DefaultHasher<int>,\n    folly::f14::DefaultKeyEqual<int>,\n    std::scoped_allocator_adaptor<ShmAllocator<std::pair<int const, ShmVVI>>>>;\n\nusing ShmF14NodeI2VVI = folly::F14NodeMap<\n    int,\n    ShmVVI,\n    folly::f14::DefaultHasher<int>,\n    folly::f14::DefaultKeyEqual<int>,\n    std::scoped_allocator_adaptor<ShmAllocator<std::pair<int const, ShmVVI>>>>;\n\nusing ShmF14VectorI2VVI = folly::F14VectorMap<\n    int,\n    ShmVVI,\n    folly::f14::DefaultHasher<int>,\n    folly::f14::DefaultKeyEqual<int>,\n    std::scoped_allocator_adaptor<ShmAllocator<std::pair<int const, ShmVVI>>>>;\n\nnamespace {\nstd::string makeRandomName() {\n  return fmt::format(\"f14test_{}\", folly::Random::rand64());\n}\n\nstd::shared_ptr<managed_shared_memory> makeShmSegment(\n    std::size_t n, std::string name = makeRandomName()) {\n  auto deleter = [=](managed_shared_memory* p) {\n    delete p;\n    shared_memory_object::remove(name.c_str());\n  };\n\n  auto segment = new managed_shared_memory(create_only, name.c_str(), n);\n  return std::shared_ptr<managed_shared_memory>(segment, deleter);\n}\n} // namespace\n\ntemplate <typename M>\nvoid runSimpleMapTest() {\n  auto segment = makeShmSegment(8192);\n  auto mgr = segment->get_segment_manager();\n  ShmAllocator<std::pair<int const, int>> alloc{mgr};\n  M m{alloc};\n  for (int i = 0; i < 20; ++i) {\n    m[i] = i * 10;\n  }\n  EXPECT_EQ(m.size(), 20);\n  for (int i = 0; i < 20; ++i) {\n    EXPECT_EQ(m[i], i * 10);\n  }\n}\n\ntemplate <typename S>\nvoid runSimpleSetTest() {\n  auto segment = makeShmSegment(8192);\n  auto mgr = segment->get_segment_manager();\n  S s{typename S::allocator_type{mgr}};\n  for (int i = 0; i < 20; ++i) {\n    s.insert(i);\n  }\n  EXPECT_EQ(s.size(), 20);\n  for (int i = 0; i < 40; ++i) {\n    EXPECT_EQ(s.contains(i), (i < 20 ? 1 : 0));\n  }\n}\n\nTEST(ShmF14ValueMap, simple) {\n  runSimpleMapTest<ShmF14ValueMap<int, int>>();\n}\nTEST(ShmF14NodeMap, simple) {\n  runSimpleMapTest<ShmF14NodeMap<int, int>>();\n}\nTEST(ShmF14VectorMap, simple) {\n  runSimpleMapTest<ShmF14VectorMap<int, int>>();\n}\nTEST(ShmF14ValueSet, simple) {\n  runSimpleSetTest<ShmF14ValueSet<int>>();\n}\nTEST(ShmF14NodeSet, simple) {\n  runSimpleSetTest<ShmF14NodeSet<int>>();\n}\nTEST(ShmF14VectorSet, simple) {\n  runSimpleSetTest<ShmF14VectorSet<int>>();\n}\n\ntemplate <typename M>\nvoid runSimultaneousAccessMapTest() {\n  using namespace folly::f14::detail;\n\n  // fallback std::unordered_map on libstdc++ doesn't pass this test\n  if (getF14IntrinsicsMode() != F14IntrinsicsMode::None) {\n    auto name = makeRandomName();\n    auto segment1 = makeShmSegment(8192, name);\n    auto segment2 =\n        std::make_shared<managed_shared_memory>(open_only, name.c_str());\n\n    auto m1 = segment1->construct<M>(\"m\")(\n        typename M::allocator_type{segment1->get_segment_manager()});\n    auto m2 = segment2->find<M>(\"m\").first;\n\n    std::cout << \"m in segment1 @ \" << (uintptr_t)m1 << \"\\n\";\n    std::cout << \"m in segment2 @ \" << (uintptr_t)m2 << \"\\n\";\n\n    EXPECT_NE(&*m1, &*m2);\n\n    (*m1)[1] = 10;\n    EXPECT_EQ(m2->contains(0), 0);\n    EXPECT_EQ((*m2)[1], 10);\n    (*m2)[2] = 20;\n    EXPECT_EQ(m1->size(), 2);\n    EXPECT_EQ(m1->find(2)->second, 20);\n    (*m1)[3] = 30;\n    EXPECT_EQ(m2->size(), 3);\n    EXPECT_FALSE(m2->emplace(std::make_pair(3, 33)).second);\n  }\n}\n\nTEST(ShmF14ValueMap, simultaneous) {\n  runSimultaneousAccessMapTest<ShmF14ValueMap<int, int>>();\n}\nTEST(ShmF14NodeMap, simultaneous) {\n  runSimultaneousAccessMapTest<ShmF14NodeMap<int, int>>();\n}\nTEST(ShmF14VectorMap, simultaneous) {\n  runSimultaneousAccessMapTest<ShmF14VectorMap<int, int>>();\n}\n\ntemplate <typename T>\nvoid checkSingleLocation(\n    std::string name,\n    std::shared_ptr<managed_shared_memory> const& segment,\n    T const& val) {\n  auto beginAddr = reinterpret_cast<uintptr_t>(segment->get_address());\n  auto endAddr = beginAddr + segment->get_size();\n  auto addr = reinterpret_cast<uintptr_t>(&val);\n  EXPECT_TRUE(beginAddr <= addr && addr + sizeof(T) <= endAddr)\n      << name << \": begin @\" << std::hex << beginAddr << \", val @\" << std::hex\n      << addr << \", size\" << std::hex << sizeof(T) << \", end @\" << std::hex\n      << endAddr;\n}\n\nvoid checkLocation(\n    std::string name,\n    std::shared_ptr<managed_shared_memory> const& segment,\n    int const& val);\n\ntemplate <typename A, typename B>\nvoid checkLocation(\n    std::string name,\n    std::shared_ptr<managed_shared_memory> const& segment,\n    std::pair<A, B> const& val);\n\ntemplate <typename T>\nauto checkLocation(\n    std::string name,\n    std::shared_ptr<managed_shared_memory> const& segment,\n    T const& val) -> folly::void_t<decltype(val.begin())>;\n\nvoid checkLocation(\n    std::string name,\n    std::shared_ptr<managed_shared_memory> const& segment,\n    int const& val) {\n  checkSingleLocation(name, segment, val);\n}\n\ntemplate <typename A, typename B>\nvoid checkLocation(\n    std::string name,\n    std::shared_ptr<managed_shared_memory> const& segment,\n    std::pair<A, B> const& val) {\n  checkSingleLocation(name, segment, val);\n  checkLocation(name + \".first\", segment, val.first);\n  checkLocation(name + \".second\", segment, val.second);\n}\n\ntemplate <typename T>\nauto checkLocation(\n    std::string name,\n    std::shared_ptr<managed_shared_memory> const& segment,\n    T const& val) -> folly::void_t<decltype(val.begin())> {\n  typename T::allocator_type alloc{segment->get_segment_manager()};\n  EXPECT_TRUE(alloc == val.get_allocator());\n  checkSingleLocation(name, segment, val);\n  for (auto&& v : val) {\n    checkLocation(name + \"[]\", segment, v);\n  }\n}\n\ntemplate <typename M>\nvoid runScopedAllocatorTest() {\n  auto segment = makeShmSegment(8192);\n  auto mgr = segment->get_segment_manager();\n\n  auto vi = segment->construct<ShmVI>(anonymous_instance)(\n      typename ShmVI::allocator_type{mgr});\n  vi->push_back(10);\n  checkLocation(\"vi\", segment, *vi);\n\n  auto vvi = segment->construct<ShmVVI>(anonymous_instance)(\n      typename ShmVVI::allocator_type{mgr});\n  vvi->resize(1);\n  vvi->at(0).push_back(2);\n  checkLocation(\"vvi\", segment, *vvi);\n\n  auto m = segment->construct<M>(anonymous_instance)(\n      typename M::allocator_type{mgr});\n  (*m)[1].emplace_back();\n  (*m)[1][0].push_back(3);\n\n  checkLocation(\"m\", segment, *m);\n  m->clear();\n}\n\nTEST(ShmF14ValueI2VVI, scopedAllocator) {\n  runScopedAllocatorTest<ShmF14ValueI2VVI>();\n}\nTEST(ShmF14NodeI2VVI, scopedAllocator) {\n  runScopedAllocatorTest<ShmF14NodeI2VVI>();\n}\nTEST(ShmF14VectorI2VVI, scopedAllocator) {\n  runScopedAllocatorTest<ShmF14VectorI2VVI>();\n}\n\ntemplate <typename M>\nvoid runMultiScopeTest() {\n  auto segment1 = makeShmSegment(8192);\n  auto mgr1 = segment1->get_segment_manager();\n\n  auto segment2 = makeShmSegment(8192);\n  auto mgr2 = segment2->get_segment_manager();\n\n  auto a1 = segment1->construct<M>(anonymous_instance)(\n      typename M::allocator_type{mgr1});\n  (*a1)[1].emplace_back();\n  (*a1)[1][0].push_back(3);\n  auto b1 = segment1->construct<M>(anonymous_instance)(*a1);\n  auto c1 = segment1->construct<M>(anonymous_instance)(std::move(*a1));\n\n  auto d2 = segment2->construct<M>(anonymous_instance)(\n      typename M::allocator_type{mgr2});\n  (*d2)[10].emplace_back();\n  (*d2)[10][0].push_back(6);\n  auto e2 = segment2->construct<M>(anonymous_instance)(*d2);\n  auto f2 = segment2->construct<M>(anonymous_instance)(\n      *b1, typename M::allocator_type{mgr2});\n\n  checkLocation(\"a1\", segment1, *a1);\n  checkLocation(\"b1\", segment1, *b1);\n  checkLocation(\"c1\", segment1, *c1);\n  checkLocation(\"d2\", segment2, *d2);\n  checkLocation(\"e2\", segment2, *e2);\n  checkLocation(\"f2\", segment2, *f2);\n\n  EXPECT_EQ(a1->size(), 0);\n  EXPECT_FALSE(*a1 == *b1);\n  EXPECT_TRUE(*b1 == *c1);\n  EXPECT_TRUE(*b1 == *f2);\n  EXPECT_EQ(d2->size(), 1);\n  EXPECT_EQ(e2->size(), 1);\n  EXPECT_FALSE(*e2 == *f2);\n\n  checkLocation(\"d2\", segment2, *d2);\n\n  EXPECT_TRUE(*d2 == *e2);\n\n  f2->clear();\n  *f2 = std::move(*d2);\n\n  checkLocation(\"d2\", segment2, *d2);\n  checkLocation(\"f2\", segment2, *f2);\n\n  EXPECT_TRUE(*f2 == *e2);\n  EXPECT_TRUE(d2->empty());\n\n  {\n    using std::swap;\n    swap(*a1, *b1);\n  }\n  checkLocation(\"a1\", segment1, *a1);\n  checkLocation(\"b1\", segment1, *b1);\n  EXPECT_TRUE(*a1 == *c1);\n\n  *a1 = std::move(*e2);\n\n  EXPECT_TRUE(*f2 == *a1);\n\n  checkLocation(\"a1\", segment1, *a1);\n  checkLocation(\"e2\", segment2, *e2);\n\n  auto g2 = segment2->construct<M>(anonymous_instance)(\n      std::move(*a1), typename M::allocator_type{mgr2});\n\n  EXPECT_TRUE(*f2 == *g2);\n\n  checkLocation(\"f2\", segment2, *f2);\n  checkLocation(\"g2\", segment2, *g2);\n\n  segment1->destroy_ptr(a1);\n  segment1->destroy_ptr(b1);\n  segment1->destroy_ptr(c1);\n  segment2->destroy_ptr(d2);\n  segment2->destroy_ptr(e2);\n  segment2->destroy_ptr(f2);\n  segment2->destroy_ptr(g2);\n}\n\nTEST(ShmF14ValueI2VVI, multiScope) {\n  runMultiScopeTest<ShmF14ValueI2VVI>();\n}\nTEST(ShmF14NodeI2VVI, multiScope) {\n  runMultiScopeTest<ShmF14NodeI2VVI>();\n}\nTEST(ShmF14VectorI2VVI, multiScope) {\n  runMultiScopeTest<ShmF14VectorI2VVI>();\n}\n"
  },
  {
    "path": "folly/container/test/F14MapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/F14Map.h>\n\n#include <algorithm>\n#include <random>\n#include <string>\n#include <type_traits>\n#include <typeinfo>\n#include <unordered_map>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/Conv.h>\n#include <folly/FBString.h>\n#include <folly/Portability.h>\n#include <folly/container/test/F14TestUtil.h>\n#include <folly/container/test/TrackingTypes.h>\n#include <folly/hash/Hash.h>\n#include <folly/portability/GTest.h>\n#include <folly/test/TestUtils.h>\n\nusing namespace folly;\nusing namespace folly::f14;\nusing namespace folly::string_piece_literals;\nusing namespace folly::test;\n\ntemplate <typename A, typename B>\nusing detect_op_eq = decltype(FOLLY_DECLVAL(A) == FOLLY_DECLVAL(B));\n\nstatic constexpr bool kFallback = folly::f14::detail::getF14IntrinsicsMode() ==\n    folly::f14::detail::F14IntrinsicsMode::None;\n\ntemplate <typename T>\nvoid runSanityChecks(T const& t) {\n  (void)t;\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n  F14TableStats::compute(t);\n#endif\n}\n\ntemplate <\n    template <typename, typename, typename, typename, typename> class TMap>\nvoid testCustomSwap() {\n  using std::swap;\n\n  TMap<\n      int,\n      int,\n      DefaultHasher<int>,\n      DefaultKeyEqual<int>,\n      SwapTrackingAlloc<std::pair<int const, int>>>\n      m0, m1;\n  resetTracking();\n  swap(m0, m1);\n\n  EXPECT_EQ(0, Tracked<0>::counts().dist(Counts{0, 0, 0, 0}));\n}\n\nTEST(F14Map, customSwap) {\n  testCustomSwap<F14ValueMap>();\n  testCustomSwap<F14NodeMap>();\n  testCustomSwap<F14VectorMap>();\n  testCustomSwap<F14FastMap>();\n}\n\ntemplate <\n    template <typename, typename, typename, typename, typename> class TMap,\n    typename K,\n    typename V>\nvoid runAllocatedMemorySizeTest() {\n  using A = SwapTrackingAlloc<std::pair<const K, V>>;\n\n  resetTracking();\n  {\n    TMap<K, V, DefaultHasher<K>, DefaultKeyEqual<K>, A> m;\n\n    // if F14 intrinsics are not available then we fall back to using\n    // std::unordered_map underneath, but in that case the allocation\n    // info is only best effort\n    if (!kFallback) {\n      EXPECT_EQ(testAllocatedMemorySize(), 0);\n      EXPECT_EQ(m.getAllocatedMemorySize(), 0);\n    }\n    auto emptyMapAllocatedMemorySize = testAllocatedMemorySize();\n    auto emptyMapAllocatedBlockCount = testAllocatedBlockCount();\n\n    for (size_t i = 0; i < 1000; ++i) {\n      m.insert(std::make_pair(to<K>(i), V{}));\n      m.erase(to<K>(i / 10 + 2));\n      if (!kFallback) {\n        EXPECT_EQ(testAllocatedMemorySize(), m.getAllocatedMemorySize());\n      }\n      EXPECT_GE(m.getAllocatedMemorySize(), sizeof(std::pair<K, V>) * m.size());\n      std::size_t size = 0;\n      std::size_t count = 0;\n      m.visitAllocationClasses([&](std::size_t, std::size_t) mutable {});\n      m.visitAllocationClasses([&](std::size_t bytes, std::size_t n) {\n        size += bytes * n;\n        count += n;\n      });\n      if (!kFallback) {\n        EXPECT_EQ(testAllocatedMemorySize(), size);\n        EXPECT_EQ(testAllocatedBlockCount(), count);\n      }\n    }\n\n    m = decltype(m){};\n    EXPECT_EQ(testAllocatedMemorySize(), emptyMapAllocatedMemorySize);\n    EXPECT_EQ(testAllocatedBlockCount(), emptyMapAllocatedBlockCount);\n\n    m.reserve(5);\n    EXPECT_GT(testAllocatedMemorySize(), 0);\n    m = {};\n    if (!kFallback) {\n      EXPECT_GT(testAllocatedMemorySize(), 0);\n    }\n  }\n  EXPECT_EQ(testAllocatedMemorySize(), 0);\n  EXPECT_EQ(testAllocatedBlockCount(), 0);\n}\n\ntemplate <typename K, typename V>\nvoid runAllocatedMemorySizeTests() {\n  runAllocatedMemorySizeTest<F14ValueMap, K, V>();\n  runAllocatedMemorySizeTest<F14NodeMap, K, V>();\n  runAllocatedMemorySizeTest<F14VectorMap, K, V>();\n  runAllocatedMemorySizeTest<F14FastMap, K, V>();\n}\n\nTEST(F14Map, getAllocatedMemorySize) {\n  runAllocatedMemorySizeTests<bool, bool>();\n  runAllocatedMemorySizeTests<int, int>();\n  runAllocatedMemorySizeTests<bool, std::string>();\n  runAllocatedMemorySizeTests<double, std::string>();\n  runAllocatedMemorySizeTests<std::string, int>();\n  runAllocatedMemorySizeTests<std::string, std::string>();\n  runAllocatedMemorySizeTests<fbstring, long>();\n}\n\ntemplate <typename M>\nvoid runVisitContiguousRangesTest(int n) {\n  M map;\n\n  for (int i = 0; i < n; ++i) {\n    makeUnpredictable(i);\n    map[i] = i;\n    map.erase(i / 2);\n  }\n\n  std::unordered_map<uintptr_t, bool> visited;\n  for (auto& entry : map) {\n    visited[reinterpret_cast<uintptr_t>(&entry)] = false;\n  }\n\n  map.visitContiguousRanges([&](auto b, auto e) {\n    for (auto i = b; i != e; ++i) {\n      auto iter = visited.find(reinterpret_cast<uintptr_t>(i));\n      ASSERT_TRUE(iter != visited.end());\n      EXPECT_FALSE(iter->second);\n      iter->second = true;\n    }\n  });\n\n  // ensure no entries were skipped\n  for (auto& e : visited) {\n    EXPECT_TRUE(e.second);\n  }\n}\n\ntemplate <typename M>\nvoid runVisitContiguousRangesTest() {\n  runVisitContiguousRangesTest<M>(0); // empty\n  runVisitContiguousRangesTest<M>(5); // single chunk\n  runVisitContiguousRangesTest<M>(1000); // many chunks\n}\n\nTEST(F14ValueMap, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14ValueMap<int, int>>();\n}\n\nTEST(F14NodeMap, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14NodeMap<int, int>>();\n}\n\nTEST(F14VectorMap, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14VectorMap<int, int>>();\n}\n\nTEST(F14FastMap, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14FastMap<int, int>>();\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nTEST(F14Map, pmrEmpty) {\n  pmr::F14ValueMap<int, int> m1;\n  pmr::F14NodeMap<int, int> m2;\n  pmr::F14VectorMap<int, int> m3;\n  pmr::F14FastMap<int, int> m4;\n  EXPECT_TRUE(m1.empty() && m2.empty() && m3.empty() && m4.empty());\n}\n#endif\n\nnamespace {\nstruct NestedHash {\n  template <typename N>\n  std::size_t operator()(N const& v) const;\n};\n\ntemplate <template <class...> class TMap>\nstruct Nested {\n  std::unique_ptr<TMap<Nested, int, NestedHash>> map_;\n\n  explicit Nested(int depth)\n      : map_(std::make_unique<TMap<Nested, int, NestedHash>>()) {\n    if (depth > 0) {\n      map_->emplace(Nested{depth - 1}, 0);\n    }\n  }\n};\n\ntemplate <typename N>\nstd::size_t NestedHash::operator()(N const& v) const {\n  std::size_t rv = 0;\n  for (auto& kv : *v.map_) {\n    rv += Hash{}(operator()(kv.first), kv.second);\n  }\n  return Hash{}(rv);\n}\n\ntemplate <template <class...> class TMap>\nbool operator==(Nested<TMap> const& lhs, Nested<TMap> const& rhs) {\n  return *lhs.map_ == *rhs.map_;\n}\n\ntemplate <template <class...> class TMap>\nbool operator!=(Nested<TMap> const& lhs, Nested<TMap> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <template <class...> class TMap>\nvoid testNestedMapEquality() {\n  auto n1 = Nested<TMap>(100);\n  auto n2 = Nested<TMap>(100);\n  auto n3 = Nested<TMap>(99);\n  EXPECT_TRUE(n1 == n1);\n  EXPECT_TRUE(n1 == n2);\n  EXPECT_FALSE(n1 == n3);\n  EXPECT_FALSE(n1 != n1);\n  EXPECT_FALSE(n1 != n2);\n  EXPECT_TRUE(n1 != n3);\n}\n\ntemplate <template <class...> class TMap>\nvoid testEqualityRefinement() {\n  TMap<std::pair<int, int>, int, HashFirst, EqualFirst> m1;\n  TMap<std::pair<int, int>, int, HashFirst, EqualFirst> m2;\n  m1[std::make_pair(0, 0)] = 0;\n  m1[std::make_pair(1, 1)] = 1;\n  EXPECT_FALSE(m1.insert(std::make_pair(std::make_pair(0, 2), 0)).second);\n  EXPECT_EQ(m1.size(), 2);\n  EXPECT_EQ(m1.count(std::make_pair(0, 10)), 1);\n  for (auto& kv : m1) {\n    m2.emplace(std::make_pair(kv.first.first, kv.first.second + 1), kv.second);\n  }\n  EXPECT_EQ(m1.size(), m2.size());\n  for (auto& kv : m1) {\n    EXPECT_EQ(m2.count(kv.first), 1);\n  }\n  EXPECT_FALSE(m1 == m2);\n  EXPECT_TRUE(m1 != m2);\n}\n} // namespace\n\nTEST(F14Map, nestedMapEquality) {\n  testNestedMapEquality<F14ValueMap>();\n  testNestedMapEquality<F14NodeMap>();\n  testNestedMapEquality<F14VectorMap>();\n  testNestedMapEquality<F14FastMap>();\n}\n\nTEST(F14Map, equalityRefinement) {\n  testEqualityRefinement<F14ValueMap>();\n  testEqualityRefinement<F14NodeMap>();\n  testEqualityRefinement<F14VectorMap>();\n  testEqualityRefinement<F14FastMap>();\n}\n\nnamespace {\nstd::string s(char const* p) {\n  return p;\n}\n} // namespace\n\ntemplate <typename T>\nvoid runSimple() {\n  T h;\n\n  EXPECT_EQ(h.size(), 0);\n  h.reserve(0);\n  std::vector<std::pair<std::string const, std::string>> v(\n      {{\"abc\", \"first\"}, {\"abc\", \"second\"}});\n  h.insert(v.begin(), v.begin());\n  EXPECT_EQ(h.size(), 0);\n  if (!kFallback) {\n    EXPECT_EQ(h.bucket_count(), 0);\n  }\n  h.insert(v.begin(), v.end());\n  EXPECT_EQ(h.size(), 1);\n  EXPECT_EQ(h[\"abc\"], s(\"first\"));\n  h = T{};\n  if (!kFallback) {\n    EXPECT_EQ(h.bucket_count(), 0);\n  }\n\n  h.insert(std::make_pair(s(\"abc\"), s(\"ABC\")));\n  EXPECT_TRUE(h.find(s(\"def\")) == h.end());\n  EXPECT_FALSE(h.find(s(\"abc\")) == h.end());\n  EXPECT_EQ(h[s(\"abc\")], s(\"ABC\"));\n  h[s(\"ghi\")] = s(\"GHI\");\n  EXPECT_EQ(h.size(), 2);\n  h.erase(h.find(s(\"abc\")));\n  EXPECT_EQ(h.size(), 1);\n\n  T h2(std::move(h));\n  EXPECT_EQ(h.size(), 0);\n  EXPECT_TRUE(h.begin() == h.end());\n  EXPECT_EQ(h2.size(), 1);\n\n  EXPECT_TRUE(h2.find(s(\"abc\")) == h2.end());\n  EXPECT_EQ(h2.begin()->first, s(\"ghi\"));\n  {\n    auto i = h2.begin();\n    EXPECT_FALSE(i == h2.end());\n    ++i;\n    EXPECT_TRUE(i == h2.end());\n  }\n\n  T h3;\n  h3.try_emplace(s(\"xxx\"));\n  h3.insert_or_assign(s(\"yyy\"), s(\"YYY\"));\n  h3 = std::move(h2);\n  EXPECT_EQ(h2.size(), 0);\n  EXPECT_EQ(h3.size(), 1);\n  EXPECT_TRUE(h3.find(s(\"xxx\")) == h3.end());\n\n  for (uint64_t i = 0; i < 1000; ++i) {\n    h[to<std::string>(i * i * i)] = s(\"x\");\n    EXPECT_EQ(h.size(), i + 1);\n  }\n  {\n    using std::swap;\n    swap(h, h2);\n  }\n  for (uint64_t i = 0; i < 1000; ++i) {\n    EXPECT_TRUE(h2.find(to<std::string>(i * i * i)) != h2.end());\n    EXPECT_EQ(\n        h2.find(to<std::string>(i * i * i))->first, to<std::string>(i * i * i));\n    EXPECT_TRUE(h2.find(to<std::string>(i * i * i + 2)) == h2.end());\n  }\n\n  T h4{h2};\n  EXPECT_EQ(h2.size(), 1000);\n  EXPECT_EQ(h4.size(), 1000);\n\n  T h5{std::move(h2)};\n  T h6;\n  h6 = h4;\n  T h7 = h4;\n  T h8({{s(\"abc\"), s(\"ABC\")}, {s(\"def\"), s(\"DEF\")}});\n  T h9({{s(\"abc\"), s(\"ABD\")}, {s(\"def\"), s(\"DEF\")}});\n  EXPECT_EQ(h8.size(), 2);\n  EXPECT_EQ(h8.count(s(\"abc\")), 1);\n  EXPECT_EQ(h8.count(s(\"xyz\")), 0);\n  EXPECT_TRUE(h8.contains(s(\"abc\")));\n  EXPECT_FALSE(h8.contains(s(\"xyz\")));\n\n  EXPECT_TRUE(h7 != h8);\n  EXPECT_TRUE(h8 != h9);\n\n  h8 = std::move(h7);\n  // h2 and h7 are moved from, h4, h5, h6, and h8 should be identical\n\n  EXPECT_TRUE(h4 == h8);\n\n  EXPECT_TRUE(h2.empty());\n  EXPECT_TRUE(h7.empty());\n  for (uint64_t i = 0; i < 1000; ++i) {\n    auto k = to<std::string>(i * i * i);\n    EXPECT_EQ(h4.count(k), 1);\n    EXPECT_EQ(h5.count(k), 1);\n    EXPECT_EQ(h6.count(k), 1);\n    EXPECT_EQ(h8.count(k), 1);\n    EXPECT_TRUE(h4.contains(k));\n    EXPECT_TRUE(h5.contains(k));\n    EXPECT_TRUE(h6.contains(k));\n    EXPECT_TRUE(h8.contains(k));\n  }\n\n  EXPECT_TRUE(h2 == h7);\n  EXPECT_TRUE(h4 != h7);\n\n  EXPECT_EQ(h3.at(s(\"ghi\")), s(\"GHI\"));\n  EXPECT_THROW(h3.at(s(\"abc\")), std::out_of_range);\n\n  h8.clear();\n  h8.emplace(s(\"abc\"), s(\"ABC\"));\n  EXPECT_GE(h8.bucket_count(), 1);\n  h8 = {};\n  if (!kFallback) {\n    EXPECT_GE(h8.bucket_count(), 1);\n  }\n  h9 = {{s(\"abc\"), s(\"ABD\")}, {s(\"def\"), s(\"DEF\")}};\n  EXPECT_TRUE(h8.empty());\n  EXPECT_EQ(h9.size(), 2);\n\n  auto expectH8 = [&h8](T& ref) { EXPECT_EQ(&ref, &h8); };\n  expectH8((h8 = h2));\n  expectH8((h8 = std::move(h2)));\n  expectH8((h8 = {}));\n\n  runSanityChecks(h);\n  runSanityChecks(h2);\n  runSanityChecks(h3);\n  runSanityChecks(h4);\n  runSanityChecks(h5);\n  runSanityChecks(h6);\n  runSanityChecks(h7);\n  runSanityChecks(h8);\n  runSanityChecks(h9);\n}\n\ntemplate <typename T>\nvoid runEraseWhileIterating() {\n  constexpr int kNumElements = 1000;\n\n  // mul and kNumElements should be relatively prime\n  for (int mul : {1, 3, 17, 137, kNumElements - 1}) {\n    for (int interval : {1, 3, 5, kNumElements / 2}) {\n      T h;\n      for (auto i = 0; i < kNumElements; ++i) {\n        EXPECT_TRUE(h.emplace((i * mul) % kNumElements, i).second);\n      }\n\n      int sum = 0;\n      for (auto it = h.begin(); it != h.end();) {\n        sum += it->second;\n        if (it->first % interval == 0) {\n          it = h.erase(it);\n        } else {\n          ++it;\n        }\n      }\n      EXPECT_EQ(kNumElements * (kNumElements - 1) / 2, sum);\n    }\n  }\n}\n\ntemplate <typename T>\nvoid runRehash() {\n  unsigned n = 10000;\n  T h;\n  auto b = h.bucket_count();\n  for (unsigned i = 0; i < n; ++i) {\n    h.insert(std::make_pair(to<std::string>(i), s(\"\")));\n    if (b != h.bucket_count()) {\n      runSanityChecks(h);\n      b = h.bucket_count();\n    }\n  }\n  EXPECT_EQ(h.size(), n);\n  runSanityChecks(h);\n}\n\n// T should be a map from uint64_t to Tracked<1> that uses SwapTrackingAlloc\ntemplate <typename T>\nvoid runRandom() {\n  using R = std::unordered_map<uint64_t, Tracked<2>>;\n\n  resetTracking();\n\n  std::mt19937_64 gen(0);\n  std::uniform_int_distribution<> pctDist(0, 100);\n  std::uniform_int_distribution<uint64_t> bitsBitsDist(1, 6);\n  {\n    T t0;\n    T t1;\n    R r0;\n    R r1;\n    std::size_t rollbacks = 0;\n    std::size_t resizingSmallRollbacks = 0;\n    std::size_t resizingLargeRollbacks = 0;\n\n    for (std::size_t reps = 0; reps < 100000 ||\n         (!kFallback &&\n          (rollbacks < 10 || resizingSmallRollbacks < 1 ||\n           resizingLargeRollbacks < 1));\n         ++reps) {\n      if (!kFallback && pctDist(gen) < 20) {\n        // 10% chance allocator will fail after 0 to 3 more allocations.\n        // Skip rollback tests for fallback impl because gcc 4.9's\n        // libstdc++ doesn't pass them.\n        limitTestAllocations(gen() & 3);\n      } else {\n        unlimitTestAllocations();\n      }\n      bool leakCheckOnly = false;\n\n      // discardBits will be from 0 to 62\n      auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2;\n      auto k = gen() >> discardBits;\n      auto v = gen();\n      auto pct = pctDist(gen);\n\n      try {\n        EXPECT_EQ(t0.empty(), r0.empty());\n        EXPECT_EQ(t0.size(), r0.size());\n        EXPECT_EQ(t0.size() + t1.size(), Tracked<1>::counts().liveCount());\n        EXPECT_EQ(r0.size() + r1.size(), Tracked<2>::counts().liveCount());\n        if (pct < 15) {\n          // insert\n          auto t = t0.insert(std::make_pair(k, v));\n          auto r = r0.insert(std::make_pair(k, v));\n          EXPECT_EQ(t.first->first, r.first->first);\n          EXPECT_EQ(t.first->second.val_, r.first->second.val_);\n          EXPECT_EQ(t.second, r.second);\n        } else if (pct < 25) {\n          // emplace\n          auto t = t0.emplace(k, v);\n          auto r = r0.emplace(k, v);\n          EXPECT_EQ(t.first->first, r.first->first);\n          EXPECT_EQ(t.first->second.val_, r.first->second.val_);\n          EXPECT_EQ(t.second, r.second);\n        } else if (pct < 30) {\n          // bulk insert\n          leakCheckOnly = true;\n          t0.insert(t1.begin(), t1.end());\n          r0.insert(r1.begin(), r1.end());\n        } else if (pct < 40) {\n          // erase by key\n          auto t = t0.erase(k);\n          auto r = r0.erase(k);\n          EXPECT_EQ(t, r);\n        } else if (pct < 47) {\n          // erase by iterator\n          if (t0.size() > 0) {\n            auto r = r0.find(k);\n            if (r == r0.end()) {\n              r = r0.begin();\n            }\n            k = r->first;\n            auto t = t0.find(k);\n            t = t0.erase(t);\n            if (t != t0.end()) {\n              EXPECT_NE(t->first, k);\n            }\n            r = r0.erase(r);\n            if (r != r0.end()) {\n              EXPECT_NE(r->first, k);\n            }\n          }\n        } else if (pct < 50) {\n          // bulk erase\n          if (t0.size() > 0) {\n            auto r = r0.find(k);\n            if (r == r0.end()) {\n              r = r0.begin();\n            }\n            k = r->first;\n            auto t = t0.find(k);\n            auto firstt = t;\n            auto lastt = ++t;\n            t = t0.erase(firstt, lastt);\n            if (t != t0.end()) {\n              EXPECT_NE(t->first, k);\n            }\n            auto firstr = r;\n            auto lastr = ++r;\n            r = r0.erase(firstr, lastr);\n            if (r != r0.end()) {\n              EXPECT_NE(r->first, k);\n            }\n          }\n        } else if (pct < 58) {\n          // find\n          auto t = t0.find(k);\n          auto r = r0.find(k);\n          EXPECT_EQ((t == t0.end()), (r == r0.end()));\n          if (t != t0.end() && r != r0.end()) {\n            EXPECT_EQ(t->first, r->first);\n            EXPECT_EQ(t->second.val_, r->second.val_);\n          }\n          EXPECT_EQ(t0.count(k), r0.count(k));\n          // TODO: When std::unordered_map supports c++20:\n          // EXPECT_EQ(t0.contains(k), r0.contains(k));\n        } else if (pct < 60) {\n          // equal_range\n          auto t = t0.equal_range(k);\n          auto r = r0.equal_range(k);\n          EXPECT_EQ((t.first == t.second), (r.first == r.second));\n          if (t.first != t.second && r.first != r.second) {\n            EXPECT_EQ(t.first->first, r.first->first);\n            EXPECT_EQ(t.first->second.val_, r.first->second.val_);\n            t.first++;\n            r.first++;\n            EXPECT_TRUE(t.first == t.second);\n            EXPECT_TRUE(r.first == r.second);\n          }\n        } else if (pct < 65) {\n          // iterate\n          uint64_t t = 0;\n          for (auto& e : t0) {\n            t += e.first * 37 + e.second.val_ + 1000;\n          }\n          uint64_t r = 0;\n          for (auto& e : r0) {\n            r += e.first * 37 + e.second.val_ + 1000;\n          }\n          EXPECT_EQ(t, r);\n        } else if (pct < 69) {\n          // swap\n          using std::swap;\n          swap(t0, t1);\n          swap(r0, r1);\n        } else if (pct < 70) {\n          // swap\n          t0.swap(t1);\n          r0.swap(r1);\n        } else if (pct < 72) {\n          // default construct\n          t0.~T();\n          new (&t0) T();\n          r0.~R();\n          new (&r0) R();\n        } else if (pct < 74) {\n          // default construct with capacity\n          std::size_t capacity = k & 0xffff;\n          T t(capacity);\n          t0 = std::move(t);\n          R r(capacity);\n          r0 = std::move(r);\n        } else if (pct < 80) {\n          // bulk iterator construct\n          t0 = T{t1.begin(), t1.end()};\n          r0 = R{r1.begin(), r1.end()};\n        } else if (pct < 82) {\n          // initializer list construct\n          auto k2 = gen() >> discardBits;\n          auto v2 = gen();\n          T t({{k, v}, {k2, v}, {k2, v2}});\n          t0 = std::move(t);\n          R r({{k, v}, {k2, v}, {k2, v2}});\n          r0 = std::move(r);\n        } else if (pct < 85) {\n          // copy construct\n          T t(t1);\n          t0 = std::move(t);\n          R r(r1);\n          r0 = std::move(r);\n        } else if (pct < 88) {\n          // copy construct\n          T t(t1, t1.get_allocator());\n          t0 = std::move(t);\n          R r(r1, r1.get_allocator());\n          r0 = std::move(r);\n        } else if (pct < 89) {\n          // move construct\n          t0.~T();\n          new (&t0) T(std::move(t1));\n          r0.~R();\n          new (&r0) R(std::move(r1));\n        } else if (pct < 90) {\n          // move construct\n          t0.~T();\n          auto ta = t1.get_allocator();\n          new (&t0) T(std::move(t1), ta);\n          r0.~R();\n          auto ra = r1.get_allocator();\n          new (&r0) R(std::move(r1), ra);\n        } else if (pct < 94) {\n          // copy assign\n          leakCheckOnly = true;\n          t0 = t1;\n          r0 = r1;\n        } else if (pct < 96) {\n          // move assign\n          t0 = std::move(t1);\n          r0 = std::move(r1);\n        } else if (pct < 98) {\n          // operator==\n          EXPECT_EQ((t0 == t1), (r0 == r1));\n        } else if (pct < 99) {\n          // clear\n          runSanityChecks(t0);\n          t0.clear();\n          r0.clear();\n        } else if (pct < 100) {\n          // reserve\n          auto scale = std::uniform_int_distribution<>(0, 8)(gen);\n          auto delta = std::uniform_int_distribution<>(-2, 2)(gen);\n          std::ptrdiff_t target = (t0.size() * scale) / 4 + delta;\n          if (target >= 0) {\n            t0.reserve(static_cast<std::size_t>(target));\n            r0.reserve(static_cast<std::size_t>(target));\n          }\n        }\n      } catch (std::bad_alloc const&) {\n        ++rollbacks;\n\n        runSanityChecks(t0);\n\n        if (leakCheckOnly) {\n          unlimitTestAllocations();\n          t0.clear();\n          for (auto&& kv : r0) {\n            t0[kv.first] = kv.second.val_;\n          }\n        }\n\n        if (t0.bucket_count() == t0.size() && t0.size() > 0) {\n          if (t0.size() < 10) {\n            ++resizingSmallRollbacks;\n          } else {\n            ++resizingLargeRollbacks;\n          }\n        }\n\n        assert(t0.size() == r0.size());\n        for (auto&& kv : r0) {\n          auto t = t0.find(kv.first);\n          EXPECT_TRUE(\n              t != t0.end() && t->first == kv.first &&\n              t->second.val_ == kv.second.val_);\n        }\n      }\n    }\n  }\n\n  EXPECT_EQ(testAllocatedMemorySize(), 0);\n}\n\nTEST(F14ValueMap, simple) {\n  runSimple<F14ValueMap<std::string, std::string>>();\n}\n\nTEST(F14NodeMap, simple) {\n  runSimple<F14NodeMap<std::string, std::string>>();\n}\n\nTEST(F14VectorMap, simple) {\n  runSimple<F14VectorMap<std::string, std::string>>();\n}\n\nTEST(F14FastMap, simple) {\n  runSimple<F14FastMap<std::string, std::string>>();\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nTEST(F14ValueMap, pmrSimple) {\n  runSimple<pmr::F14ValueMap<std::string, std::string>>();\n}\n\nTEST(F14NodeMap, pmrSimple) {\n  runSimple<pmr::F14NodeMap<std::string, std::string>>();\n}\n\nTEST(F14VectorMap, pmrSimple) {\n  runSimple<pmr::F14VectorMap<std::string, std::string>>();\n}\n\nTEST(F14FastMap, pmrSimple) {\n  runSimple<pmr::F14FastMap<std::string, std::string>>();\n}\n#endif\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nTEST(F14VectorMap, reverseIterator) {\n  using TMap = F14VectorMap<uint64_t, uint64_t>;\n  auto populate = [](TMap& h, uint64_t lo, uint64_t hi) {\n    for (auto i = lo; i < hi; ++i) {\n      h.emplace(i, i);\n    }\n  };\n  auto verify = [](TMap const& h, uint64_t lo, uint64_t hi) {\n    auto loIt = h.find(lo);\n    EXPECT_NE(h.end(), loIt);\n    uint64_t val = lo;\n    for (auto rit = h.riter(loIt); rit != h.rend(); ++rit) {\n      EXPECT_EQ(val, rit->first);\n      EXPECT_EQ(val, rit->second);\n      TMap::const_iterator it = h.iter(rit);\n      EXPECT_EQ(val, it->first);\n      EXPECT_EQ(val, it->second);\n      val++;\n    }\n    EXPECT_EQ(hi, val);\n  };\n  TMap h;\n  size_t prevSize = 0;\n  size_t newSize = 1;\n  // verify iteration order across rehashes, copies, and moves\n  while (newSize < 10'000) {\n    populate(h, prevSize, newSize);\n    verify(h, 0, newSize);\n    verify(h, newSize / 2, newSize);\n\n    TMap h2{h};\n    verify(h2, 0, newSize);\n\n    h = std::move(h2);\n    verify(h, 0, newSize);\n    prevSize = newSize;\n    newSize *= 10;\n  }\n}\n\nTEST(F14VectorMap, OrderPreservingReinsertionView) {\n  F14VectorMap<int, int> m1;\n  for (size_t i = 0; i < 5; ++i) {\n    m1.emplace(i, i);\n  }\n\n  F14VectorMap<int, int> m2;\n  for (const auto& kv : order_preserving_reinsertion_view(m1)) {\n    m2.insert(kv);\n  }\n\n  EXPECT_EQ(asVector(m1), asVector(m2));\n}\n#endif\n\nTEST(F14ValueMap, eraseWhileIterating) {\n  runEraseWhileIterating<F14ValueMap<int, int>>();\n}\n\nTEST(F14NodeMap, eraseWhileIterating) {\n  runEraseWhileIterating<F14NodeMap<int, int>>();\n}\n\nTEST(F14VectorMap, eraseWhileIterating) {\n  runEraseWhileIterating<F14VectorMap<int, int>>();\n}\n\nTEST(F14FastMap, eraseWhileIterating) {\n  runEraseWhileIterating<F14FastMap<int, int>>();\n}\n\nTEST(F14ValueMap, rehash) {\n  runRehash<F14ValueMap<std::string, std::string>>();\n}\n\nTEST(F14NodeMap, rehash) {\n  runRehash<F14NodeMap<std::string, std::string>>();\n}\n\nTEST(F14VectorMap, rehash) {\n  runRehash<F14VectorMap<std::string, std::string>>();\n}\n\nTEST(F14FastMap, rehash) {\n  runRehash<F14VectorMap<std::string, std::string>>();\n}\n\ntemplate <typename T>\nvoid runPrehash() {\n  T h;\n\n  EXPECT_EQ(h.size(), 0);\n\n  h.insert(std::make_pair(s(\"abc\"), s(\"ABC\")));\n  EXPECT_TRUE(h.find(s(\"def\")) == h.end());\n  EXPECT_FALSE(h.find(s(\"abc\")) == h.end());\n\n  auto t1 = h.prehash(s(\"def\"));\n  F14HashToken t2;\n  t2 = h.prehash(s(\"abc\"));\n  h.prefetch(t2);\n  EXPECT_TRUE(h.find(t1, s(\"def\")) == h.end());\n  EXPECT_FALSE(h.find(t2, s(\"abc\")) == h.end());\n  h.prefetch(t1);\n\n  {\n    auto const key3 = s(\"def\");\n    auto hv3 = h.hash_function()(key3);\n    auto t3 = h.prehash(key3, hv3);\n    EXPECT_EQ(t3, h.prehash(key3));\n    EXPECT_TRUE(h.find(t3, key3) == h.end());\n  }\n  {\n    auto const key3 = s(\"abc\");\n    auto hv3 = h.hash_function()(key3);\n    auto t3 = h.prehash(key3, hv3);\n    EXPECT_EQ(t3, h.prehash(key3));\n    EXPECT_FALSE(h.find(t3, key3) == h.end());\n  }\n}\nTEST(F14ValueMap, prehash) {\n  runPrehash<F14ValueMap<std::string, std::string>>();\n}\n\nTEST(F14NodeMap, prehash) {\n  runPrehash<F14NodeMap<std::string, std::string>>();\n}\n\nTEST(F14VectorMap, prehash) {\n  runPrehash<F14VectorMap<std::string, std::string>>();\n}\n\nTEST(F14FastMap, prehash) {\n  runPrehash<F14FastMap<std::string, std::string>>();\n}\n\nTEST(F14ValueMap, random) {\n  runRandom<F14ValueMap<\n      uint64_t,\n      Tracked<1>,\n      std::hash<uint64_t>,\n      std::equal_to<uint64_t>,\n      SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();\n}\n\nTEST(F14NodeMap, random) {\n  runRandom<F14NodeMap<\n      uint64_t,\n      Tracked<1>,\n      std::hash<uint64_t>,\n      std::equal_to<uint64_t>,\n      SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();\n}\n\nTEST(F14VectorMap, random) {\n  runRandom<F14VectorMap<\n      uint64_t,\n      Tracked<1>,\n      std::hash<uint64_t>,\n      std::equal_to<uint64_t>,\n      SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();\n}\n\nTEST(F14FastMap, random) {\n  runRandom<F14FastMap<\n      uint64_t,\n      Tracked<1>,\n      std::hash<uint64_t>,\n      std::equal_to<uint64_t>,\n      SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();\n}\n\nTEST(F14ValueMap, growStats) {\n  F14ValueMap<uint64_t, uint64_t> h;\n  for (unsigned i = 1; i <= 3072; ++i) {\n    h[i]++;\n  }\n  // F14ValueMap just before rehash\n  runSanityChecks(h);\n  h[0]++;\n  // F14ValueMap just after rehash\n  runSanityChecks(h);\n}\n\nTEST(F14ValueMap, steadyStateStats) {\n  // 10k keys, 14% probability of insert, 90% chance of erase, so the\n  // table should converge to 1400 size without triggering the rehash\n  // that would occur at 1536.\n  F14ValueMap<uint64_t, uint64_t> h;\n  std::mt19937_64 gen(0);\n  std::uniform_int_distribution<> dist(0, 10000);\n  for (std::size_t i = 0; i < 100000; ++i) {\n    auto key = dist(gen);\n    if (dist(gen) < 1400) {\n      h.insert_or_assign(key, i);\n    } else {\n      h.erase(key);\n    }\n    if (((i + 1) % 10000) == 0) {\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n      auto stats = F14TableStats::compute(h);\n      // Verify that average miss probe length is bounded despite continued\n      // erase + reuse.  p99 of the average across 10M random steps is 4.69,\n      // average is 2.96.\n      EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);\n#endif\n    }\n  }\n  // F14ValueMap at steady state\n  runSanityChecks(h);\n}\n\nTEST(F14VectorMap, steadyStateStats) {\n  // 10k keys, 14% probability of insert, 90% chance of erase, so the\n  // table should converge to 1400 size without triggering the rehash\n  // that would occur at 1536.\n  F14VectorMap<std::string, uint64_t> h;\n  std::mt19937_64 gen(0);\n  std::uniform_int_distribution<> dist(0, 10000);\n  for (std::size_t i = 0; i < 100000; ++i) {\n    auto key = \"0123456789ABCDEFGHIJKLMNOPQ\" + to<std::string>(dist(gen));\n    if (dist(gen) < 1400) {\n      h.insert_or_assign(key, i);\n    } else {\n      h.erase(key);\n    }\n    if (((i + 1) % 10000) == 0) {\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n      auto stats = F14TableStats::compute(h);\n      // Verify that average miss probe length is bounded despite continued\n      // erase + reuse.  p99 of the average across 10M random steps is 4.69,\n      // average is 2.96.\n      EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);\n#endif\n    }\n  }\n  // F14ValueMap at steady state\n  runSanityChecks(h);\n}\n\nTEST(Tracked, baseline) {\n  Tracked<0> a0;\n\n  {\n    resetTracking();\n    Tracked<0> b0{a0};\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{1, 0, 0, 0}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{1, 0, 0, 0}));\n  }\n  {\n    resetTracking();\n    Tracked<0> b0{std::move(a0)};\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 1, 0, 0}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{0, 1, 0, 0}));\n  }\n  {\n    resetTracking();\n    Tracked<1> b1{a0};\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 1, 0}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 1, 0}));\n  }\n  {\n    resetTracking();\n    Tracked<1> b1{std::move(a0)};\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 1}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 0, 1}));\n  }\n  {\n    Tracked<0> b0;\n    resetTracking();\n    b0 = a0;\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 0, 1, 0}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{0, 0, 0, 0, 1, 0}));\n  }\n  {\n    Tracked<0> b0;\n    resetTracking();\n    b0 = std::move(a0);\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 0, 0, 1}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{0, 0, 0, 0, 0, 1}));\n  }\n  {\n    Tracked<1> b1;\n    resetTracking();\n    b1 = a0;\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 1, 0, 0, 1, 0, 1}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 1, 0, 0, 1, 0, 1}));\n  }\n  {\n    Tracked<1> b1;\n    resetTracking();\n    b1 = std::move(a0);\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 1, 0, 1, 0, 1}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 0, 1, 0, 1, 0, 1}));\n  }\n}\n\n// M should be a map from Tracked<0> to Tracked<1>.  F should take a map\n// and a pair const& or pair&& and cause it to be inserted\ntemplate <typename M, typename F>\nvoid runInsertCases(\n    std::string const& name, F const& insertFunc, uint64_t expectedDist = 0) {\n  static_assert(std::is_same<typename M::key_type, Tracked<0>>::value);\n  static_assert(std::is_same<typename M::mapped_type, Tracked<1>>::value);\n  {\n    typename M::value_type p{0, 0};\n    M m;\n    resetTracking();\n    insertFunc(m, p);\n    // fresh key, value_type const& ->\n    // copy is expected\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{1, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{1, 0, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    typename M::value_type p{0, 0};\n    M m;\n    resetTracking();\n    insertFunc(m, std::move(p));\n    // fresh key, value_type&& ->\n    // key copy is unfortunate but required\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{1, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 1, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    M m;\n    resetTracking();\n    insertFunc(m, p);\n    // fresh key, pair<key_type,mapped_type> const& ->\n    // 1 copy is required\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{1, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{1, 0, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    M m;\n    resetTracking();\n    insertFunc(m, std::move(p));\n    // fresh key, pair<key_type,mapped_type>&& ->\n    // this is the happy path for insert(make_pair(.., ..))\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 1, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 1, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    M m;\n    resetTracking();\n    insertFunc(m, p);\n    // fresh key, convertible const& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts;\n\n    // There are three strategies that could be optimal for particular\n    // ratios of cost:\n    //\n    // - convert key and value in place to final position, destroy if\n    //   insert fails. This is the strategy used by std::unordered_map\n    //   and FBHashMap\n    //\n    // - convert key and default value in place to final position,\n    //   convert value only if insert succeeds.  Nobody uses this strategy\n    //\n    // - convert key to a temporary, move key and convert value if\n    //   insert succeeds.  This is the strategy used by F14 and what is\n    //   EXPECT_EQ here.\n\n    // The expectedDist * 3 is just a hack for the emplace-pieces-by-value\n    // test, whose test harness copies the original pair and then uses\n    // move conversion instead of copy conversion.\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 1, 1, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 1, 0}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist * 3);\n  }\n  {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    M m;\n    resetTracking();\n    insertFunc(m, std::move(p));\n    // fresh key, convertible&& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts;\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 1, 0, 1}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 1}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  if (!kFallback) {\n    typename M::value_type p{0, 0};\n    M m;\n    m[0] = 0;\n    resetTracking();\n    insertFunc(m, p);\n    // duplicate key, value_type const&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  if (!kFallback) {\n    typename M::value_type p{0, 0};\n    M m;\n    m[0] = 0;\n    resetTracking();\n    insertFunc(m, std::move(p));\n    // duplicate key, value_type&&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  if (!kFallback) {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    M m;\n    m[0] = 0;\n    resetTracking();\n    insertFunc(m, p);\n    // duplicate key, pair<key_type,mapped_type> const&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  if (!kFallback) {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    M m;\n    m[0] = 0;\n    resetTracking();\n    insertFunc(m, std::move(p));\n    // duplicate key, pair<key_type,mapped_type>&&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  if (!kFallback) {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    M m;\n    m[0] = 0;\n    resetTracking();\n    insertFunc(m, p);\n    // duplicate key, convertible const& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts;\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 1, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist * 2);\n  }\n  if (!kFallback) {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    M m;\n    m[0] = 0;\n    resetTracking();\n    insertFunc(m, std::move(p));\n    // duplicate key, convertible&& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts;\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 1}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n}\n\nstruct DoInsert {\n  template <typename M, typename P>\n  void operator()(M& m, P&& p) const {\n    m.insert(std::forward<P>(p));\n  }\n};\n\nstruct DoEmplace1 {\n  template <typename M, typename P>\n  void operator()(M& m, P&& p) const {\n    m.emplace(std::forward<P>(p));\n  }\n};\n\nstruct DoEmplace2 {\n  template <typename M, typename U1, typename U2>\n  void operator()(M& m, std::pair<U1, U2> const& p) const {\n    m.emplace(p.first, p.second);\n  }\n\n  template <typename M, typename U1, typename U2>\n  void operator()(M& m, std::pair<U1, U2>&& p) const {\n    m.emplace(std::move(p.first), std::move(p.second));\n  }\n};\n\nstruct DoEmplace3 {\n  template <typename M, typename U1, typename U2>\n  void operator()(M& m, std::pair<U1, U2> const& p) const {\n    m.emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(p.first),\n        std::forward_as_tuple(p.second));\n  }\n\n  template <typename M, typename U1, typename U2>\n  void operator()(M& m, std::pair<U1, U2>&& p) const {\n    m.emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(std::move(p.first)),\n        std::forward_as_tuple(std::move(p.second)));\n  }\n};\n\n// Simulates use of piecewise_construct without proper use of\n// forward_as_tuple.  This code doesn't yield the normal pattern, but\n// it should have exactly 1 additional move or copy of the key and 1\n// additional move or copy of the mapped value.\nstruct DoEmplace3Value {\n  template <typename M, typename U1, typename U2>\n  void operator()(M& m, std::pair<U1, U2> const& p) const {\n    m.emplace(\n        std::piecewise_construct,\n        std::tuple<U1>{p.first},\n        std::tuple<U2>{p.second});\n  }\n\n  template <typename M, typename U1, typename U2>\n  void operator()(M& m, std::pair<U1, U2>&& p) const {\n    m.emplace(\n        std::piecewise_construct,\n        std::tuple<U1>{std::move(p.first)},\n        std::tuple<U2>{std::move(p.second)});\n  }\n};\n\ntemplate <typename M>\nvoid runInsertAndEmplace(std::string const& name) {\n  runInsertCases<M>(name + \" insert\", DoInsert{});\n  runInsertCases<M>(name + \" emplace pair\", DoEmplace1{});\n  runInsertCases<M>(name + \" emplace k,v\", DoEmplace2{});\n  runInsertCases<M>(name + \" emplace pieces\", DoEmplace3{});\n  runInsertCases<M>(name + \" emplace pieces by value\", DoEmplace3Value{}, 2);\n\n  // Calling the default pair constructor via emplace is valid, but not\n  // very useful in real life.  Verify that it works.\n  M m;\n  typename M::key_type k;\n  EXPECT_EQ(m.count(k), 0);\n  EXPECT_FALSE(m.contains(k));\n  m.emplace();\n  EXPECT_EQ(m.count(k), 1);\n  EXPECT_TRUE(m.contains(k));\n  m.emplace();\n  EXPECT_EQ(m.count(k), 1);\n  EXPECT_TRUE(m.contains(k));\n}\n\nTEST(F14ValueMap, destructuring) {\n  runInsertAndEmplace<F14ValueMap<Tracked<0>, Tracked<1>>>(\"f14value\");\n}\n\nTEST(F14NodeMap, destructuring) {\n  runInsertAndEmplace<F14NodeMap<Tracked<0>, Tracked<1>>>(\"f14node\");\n}\n\nTEST(F14VectorMap, destructuring) {\n  runInsertAndEmplace<F14VectorMap<Tracked<0>, Tracked<1>>>(\"f14vector\");\n}\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nTEST(F14VectorMap, destructuringErase) {\n  using M = F14VectorMap<Tracked<0>, Tracked<1>>;\n  typename M::value_type p1{0, 0};\n  typename M::value_type p2{2, 2};\n  M m;\n  m.insert(p1);\n  m.insert(p2);\n\n  resetTracking();\n  m.erase(p1.first);\n  LOG(INFO) << \"erase -> \" << \"key_type ops \" << Tracked<0>::counts()\n            << \", mapped_type ops \" << Tracked<1>::counts();\n  // deleting p1 will cause p2 to be moved to the front of the values array\n  EXPECT_EQ(\n      Tracked<0>::counts().dist(Counts{0, 1, 0, 0}) +\n          Tracked<1>::counts().dist(Counts{0, 1, 0, 0}),\n      0);\n}\n\nTEST(F14ValueMap, maxSize) {\n  F14ValueMap<int, int> m;\n  EXPECT_EQ(\n      m.max_size(),\n      std::min(\n          folly::f14::detail::SizeAndChunkShift::kMaxSize,\n          std::allocator_traits<decltype(m)::allocator_type>::max_size(\n              m.get_allocator())));\n}\n\nTEST(F14NodeMap, maxSize) {\n  F14NodeMap<int, int> m;\n  EXPECT_EQ(\n      m.max_size(),\n      std::min(\n          folly::f14::detail::SizeAndChunkShift::kMaxSize,\n          std::allocator_traits<decltype(m)::allocator_type>::max_size(\n              m.get_allocator())));\n}\n\nTEST(F14VectorMap, vectorMaxSize) {\n  F14VectorMap<int, int> m;\n  EXPECT_EQ(\n      m.max_size(),\n      std::min(\n          {folly::f14::detail::SizeAndChunkShift::kMaxSize,\n           std::size_t{std::numeric_limits<uint32_t>::max()},\n           std::allocator_traits<decltype(m)::allocator_type>::max_size(\n               m.get_allocator())}));\n}\n\n#endif\n\ntemplate <typename M>\nvoid runMoveOnlyTest() {\n  M t0;\n  t0[10] = 20;\n  t0.emplace(30, 40);\n  t0.insert(std::make_pair(50, 60));\n  M t1{std::move(t0)};\n  EXPECT_TRUE(t0.empty());\n  M t2;\n  EXPECT_TRUE(t2.empty());\n  t2 = std::move(t1);\n  EXPECT_EQ(t2.size(), 3);\n}\n\nTEST(F14ValueMap, moveOnly) {\n  runMoveOnlyTest<F14ValueMap<MoveOnlyTestInt, int>>();\n  runMoveOnlyTest<F14ValueMap<int, MoveOnlyTestInt>>();\n  runMoveOnlyTest<F14ValueMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14NodeMap, moveOnly) {\n  runMoveOnlyTest<F14NodeMap<MoveOnlyTestInt, int>>();\n  runMoveOnlyTest<F14NodeMap<int, MoveOnlyTestInt>>();\n  runMoveOnlyTest<F14NodeMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14VectorMap, moveOnly) {\n  runMoveOnlyTest<F14VectorMap<MoveOnlyTestInt, int>>();\n  runMoveOnlyTest<F14VectorMap<int, MoveOnlyTestInt>>();\n  runMoveOnlyTest<F14VectorMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14FastMap, moveOnly) {\n  runMoveOnlyTest<F14FastMap<MoveOnlyTestInt, int>>();\n  runMoveOnlyTest<F14FastMap<int, MoveOnlyTestInt>>();\n  runMoveOnlyTest<F14FastMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\ntemplate <typename M>\nvoid runEraseIntoTest() {\n  M t0;\n  M t1;\n\n  auto emplaceIntoT0 = [&t0](auto&& key, auto&& value) {\n    EXPECT_FALSE(key.destroyed);\n    t0.emplace(std::move(key), std::move(value));\n  };\n  auto emplaceIntoT0Mut =\n      [&](typename M::key_type&& key, typename M::mapped_type&& value) mutable {\n        emplaceIntoT0(std::move(key), std::move(value));\n      };\n\n  t0.emplace(10, 0);\n  t1.emplace(20, 0);\n  t1.eraseInto(t1.begin(), emplaceIntoT0);\n  EXPECT_TRUE(t1.empty());\n  EXPECT_EQ(t0.size(), 2);\n  EXPECT_TRUE(t0.find(10) != t0.end());\n  EXPECT_TRUE(t0.find(20) != t0.end());\n\n  t1.emplace(20, 0);\n  t1.emplace(30, 0);\n  t1.emplace(40, 0);\n  t1.eraseInto(t1.begin(), t1.end(), emplaceIntoT0Mut);\n  EXPECT_TRUE(t1.empty());\n  EXPECT_EQ(t0.size(), 4);\n  EXPECT_TRUE(t0.find(30) != t0.end());\n  EXPECT_TRUE(t0.find(40) != t0.end());\n\n  t1.emplace(50, 0);\n  size_t erased = t1.eraseInto(t1.find(50)->first, emplaceIntoT0);\n  EXPECT_EQ(erased, 1);\n  EXPECT_TRUE(t1.empty());\n  EXPECT_EQ(t0.size(), 5);\n  EXPECT_TRUE(t0.find(50) != t0.end());\n\n  typename M::key_type key{60};\n  erased = t1.eraseInto(key, emplaceIntoT0Mut);\n  EXPECT_EQ(erased, 0);\n  EXPECT_EQ(t0.size(), 5);\n}\n\nTEST(F14ValueMap, eraseInto) {\n  runEraseIntoTest<F14ValueMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14NodeMap, eraseInto) {\n  runEraseIntoTest<F14NodeMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14VectorMap, eraseInto) {\n  runEraseIntoTest<F14VectorMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14FastMap, eraseInto) {\n  runEraseIntoTest<F14FastMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\ntemplate <typename M>\nvoid runEraseIntoEmptyFromEraseTest() {\n  M m;\n  m.emplace(0, 0);\n  m.erase(0);\n\n  EXPECT_GT(m.bucket_count(), 0);\n  EXPECT_TRUE(m.empty());\n\n  m.eraseInto(m.begin(), m.end(), [](auto&&, auto&&) {});\n\n  EXPECT_GT(m.bucket_count(), 0);\n  EXPECT_TRUE(m.empty());\n}\n\nTEST(F14ValueMap, eraseIntoEmptyFromErase) {\n  runEraseIntoEmptyFromEraseTest<\n      F14ValueMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14NodeMap, eraseIntoEmptyFromErase) {\n  runEraseIntoEmptyFromEraseTest<\n      F14NodeMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14VectorMap, eraseIntoEmptyFromErase) {\n  runEraseIntoEmptyFromEraseTest<\n      F14VectorMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14FastMap, eraseIntoEmptyFromErase) {\n  runEraseIntoEmptyFromEraseTest<\n      F14FastMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\ntemplate <typename M>\nvoid runEraseIntoEmptyFromReserveTest() {\n  M m;\n  m.reserve(1);\n\n  EXPECT_GT(m.bucket_count(), 0);\n  EXPECT_TRUE(m.empty());\n\n  m.eraseInto(m.begin(), m.end(), [](auto&&, auto&&) {});\n\n  EXPECT_GT(m.bucket_count(), 0);\n  EXPECT_TRUE(m.empty());\n}\n\nTEST(F14ValueMap, eraseIntoEmptyFromReserve) {\n  runEraseIntoEmptyFromReserveTest<\n      F14ValueMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14NodeMap, eraseIntoEmptyFromReserve) {\n  runEraseIntoEmptyFromReserveTest<\n      F14NodeMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14VectorMap, eraseIntoEmptyFromReserve) {\n  runEraseIntoEmptyFromReserveTest<\n      F14VectorMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\nTEST(F14FastMap, eraseIntoEmptyFromReserve) {\n  runEraseIntoEmptyFromReserveTest<\n      F14FastMap<MoveOnlyTestInt, MoveOnlyTestInt>>();\n}\n\ntemplate <typename M>\nvoid runPermissiveConstructorTest() {\n  M t;\n  M const& ct{t};\n\n  for (int i = 10; i <= 100; i += 10) {\n    t[i] = i;\n  }\n  t.erase(10);\n  EXPECT_EQ(t.size(), 9);\n  t.erase(t.find(20));\n  EXPECT_EQ(t.size(), 8);\n  t.erase(ct.find(30));\n  EXPECT_EQ(t.size(), 7);\n  t.eraseInto(40, [](auto&&, auto&&) {});\n  EXPECT_EQ(t.size(), 6);\n  t.eraseInto(t.find(50), [](auto&&, auto&&) {});\n  EXPECT_EQ(t.size(), 5);\n  t.eraseInto(ct.find(60), [](auto&&, auto&&) {});\n  EXPECT_EQ(t.size(), 4);\n}\n\nTEST(F14ValueMap, permissiveConstructor) {\n  runPermissiveConstructorTest<F14ValueMap<\n      PermissiveConstructorTestInt,\n      PermissiveConstructorTestInt>>();\n}\n\nTEST(F14NodeMap, permissiveConstructor) {\n  runPermissiveConstructorTest<\n      F14NodeMap<PermissiveConstructorTestInt, PermissiveConstructorTestInt>>();\n}\n\nTEST(F14VectorMap, permissiveConstructor) {\n  runPermissiveConstructorTest<F14VectorMap<\n      PermissiveConstructorTestInt,\n      PermissiveConstructorTestInt>>();\n}\n\nTEST(F14FastMap, permissiveConstructor) {\n  runPermissiveConstructorTest<\n      F14FastMap<PermissiveConstructorTestInt, PermissiveConstructorTestInt>>();\n}\n\nTEST(F14ValueMap, heterogeneousLookup) {\n  using Hasher = folly::transparent<folly::hasher<folly::StringPiece>>;\n  using KeyEqual = folly::transparent<std::equal_to<folly::StringPiece>>;\n\n  constexpr auto hello = \"hello\"_sp;\n  constexpr auto buddy = \"buddy\"_sp;\n  constexpr auto world = \"world\"_sp;\n\n  F14ValueMap<std::string, bool, Hasher, KeyEqual> map;\n  map.emplace(hello, true);\n  map.emplace(world, false);\n\n  auto checks = [hello, buddy](auto& ref) {\n    // count\n    EXPECT_EQ(0, ref.count(buddy));\n    EXPECT_EQ(1, ref.count(hello));\n\n    // find\n    EXPECT_TRUE(ref.end() == ref.find(buddy));\n    EXPECT_EQ(hello, ref.find(hello)->first);\n\n    const auto buddyHashToken = ref.prehash(buddy);\n    const auto helloHashToken = ref.prehash(hello);\n\n    EXPECT_TRUE(\n        buddyHashToken == ref.prehash(buddy, ref.hash_function()(buddy)));\n    EXPECT_TRUE(\n        helloHashToken == ref.prehash(hello, ref.hash_function()(hello)));\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n    EXPECT_FALSE(\n        buddyHashToken == ref.prehash(hello, ref.hash_function()(hello)));\n#endif\n\n    // prehash + find\n    EXPECT_TRUE(ref.end() == ref.find(buddyHashToken, buddy));\n    EXPECT_EQ(hello, ref.find(helloHashToken, hello)->first);\n\n    // contains\n    EXPECT_FALSE(ref.contains(buddy));\n    EXPECT_TRUE(ref.contains(hello));\n\n    // contains with prehash\n    EXPECT_FALSE(ref.contains(buddyHashToken, buddy));\n    EXPECT_TRUE(ref.contains(helloHashToken, hello));\n\n    // equal_range\n    EXPECT_TRUE(std::make_pair(ref.end(), ref.end()) == ref.equal_range(buddy));\n    EXPECT_TRUE(\n        std::make_pair(ref.find(hello), ++ref.find(hello)) ==\n        ref.equal_range(hello));\n  };\n\n  checks(map);\n  checks(std::as_const(map));\n}\n\ntemplate <typename M>\nvoid runStatefulFunctorTest() {\n  bool ranHasher = false;\n  bool ranEqual = false;\n  bool ranAlloc = false;\n  bool ranDealloc = false;\n\n  auto hasher = [&](int x) {\n    ranHasher = true;\n    return x;\n  };\n  auto equal = [&](int x, int y) {\n    ranEqual = true;\n    return x == y;\n  };\n  auto alloc = [&](std::size_t n) {\n    ranAlloc = true;\n    return std::malloc(n);\n  };\n  auto dealloc = [&](void* p, std::size_t) {\n    ranDealloc = true;\n    std::free(p);\n  };\n\n  {\n    M map(0, hasher, equal, {alloc, dealloc});\n    map[10]++;\n    map[10]++;\n    EXPECT_EQ(map[10], 2);\n\n    M map2(map);\n    M map3(std::move(map));\n    map = map2;\n    map2.clear();\n    map2 = std::move(map3);\n  }\n  EXPECT_TRUE(ranHasher);\n  EXPECT_TRUE(ranEqual);\n  EXPECT_TRUE(ranAlloc);\n  EXPECT_TRUE(ranDealloc);\n}\n\nTEST(F14ValueMap, statefulFunctors) {\n  runStatefulFunctorTest<F14ValueMap<\n      int,\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<std::pair<int const, int>>>>();\n}\n\nTEST(F14NodeMap, statefulFunctors) {\n  runStatefulFunctorTest<F14NodeMap<\n      int,\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<std::pair<int const, int>>>>();\n}\n\nTEST(F14VectorMap, statefulFunctors) {\n  runStatefulFunctorTest<F14VectorMap<\n      int,\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<std::pair<int const, int>>>>();\n}\n\nTEST(F14FastMap, statefulFunctors) {\n  runStatefulFunctorTest<F14FastMap<\n      int,\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<std::pair<int const, int>>>>();\n}\n\ntemplate <typename M>\nvoid runHeterogeneousInsertTest() {\n  M map;\n\n  resetTracking();\n  EXPECT_EQ(map.count(10), 0);\n  EXPECT_FALSE(map.contains(10));\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts();\n\n  resetTracking();\n  map[10] = 20;\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 1}), 0)\n      << Tracked<1>::counts();\n\n  resetTracking();\n  std::pair<int, int> p(10, 30);\n  std::vector<std::pair<int, int>> v({p});\n  map[10] = 30;\n  map.insert(std::pair<int, int>(10, 30));\n  map.insert(std::pair<int const, int>(10, 30));\n  map.insert(p);\n  map.insert(v.begin(), v.end());\n  map.insert(\n      std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));\n  map.insert_or_assign(10, 40);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts();\n\n  resetTracking();\n  map.emplace(10, 30);\n  map.emplace(\n      std::piecewise_construct,\n      std::forward_as_tuple(10),\n      std::forward_as_tuple(30));\n  map.emplace(p);\n  map.try_emplace(10, 30);\n  map.try_emplace(10);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts();\n\n  resetTracking();\n  map.erase(10);\n  EXPECT_EQ(map.size(), 0);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts();\n\n  map.emplace(10, 40);\n  resetTracking();\n  map.erase(map.find(10));\n  EXPECT_EQ(map.size(), 0);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts();\n\n  map.emplace(10, 40);\n  resetTracking();\n  map.eraseInto(10, [](auto&&, auto&&) {});\n  EXPECT_EQ(map.size(), 0);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts();\n\n  const auto t = map.prehash(10);\n  resetTracking();\n  map.try_emplace_token(t, 10, 40);\n  EXPECT_TRUE(map.contains(t, 10));\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 1}), 0)\n      << Tracked<1>::counts();\n  resetTracking();\n  map.erase(map.find(t, 10));\n  EXPECT_EQ(map.size(), 0);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts();\n}\n\ntemplate <typename M>\nvoid runHeterogeneousInsertStringTest() {\n  using P = std::pair<StringPiece, std::string>;\n  using CP = std::pair<const StringPiece, std::string>;\n\n  M map;\n  P p{\"foo\", \"hello\"};\n  std::vector<P> v{p};\n  StringPiece foo{\"foo\"};\n\n  map.insert(P(\"foo\", \"hello\"));\n  // TODO(T31574848): the list-initialization below does not work on libstdc++\n  // versions (e.g., GCC < 6) with no implementation of N4387 (\"perfect\n  // initialization\" for pairs and tuples).\n  //   StringPiece sp{\"foo\"};\n  //   map.insert({sp, \"hello\"});\n  map.insert({\"foo\", \"hello\"});\n  map.insert(CP(\"foo\", \"hello\"));\n  map.insert(p);\n  map.insert(v.begin(), v.end());\n  map.insert(\n      std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));\n  map.insert_or_assign(\"foo\", \"hello\");\n  map.insert_or_assign(StringPiece{\"foo\"}, \"hello\");\n  EXPECT_EQ(map[\"foo\"], \"hello\");\n\n  map.emplace(StringPiece{\"foo\"}, \"hello\");\n  map.emplace(\"foo\", \"hello\");\n  map.emplace(p);\n  map.emplace();\n  map.emplace(\n      std::piecewise_construct,\n      std::forward_as_tuple(StringPiece{\"foo\"}),\n      std::forward_as_tuple(/* count */ 20, 'x'));\n  map.try_emplace(StringPiece{\"foo\"}, \"hello\");\n  map.try_emplace(foo, \"hello\");\n  map.try_emplace(foo);\n  map.try_emplace(\"foo\");\n  map.try_emplace(\"foo\", \"hello\");\n  map.try_emplace(\"foo\", /* count */ 20, 'x');\n\n  map.erase(StringPiece{\"foo\"});\n  map.erase(foo);\n  map.erase(\"\");\n  EXPECT_TRUE(map.empty());\n\n  map.try_emplace(foo);\n  map.erase(map.find(foo));\n  map.try_emplace(foo);\n  typename M::const_iterator it = map.find(foo);\n  map.erase(it);\n}\n\nTEST(F14ValueMap, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14ValueMap<\n      Tracked<1>,\n      int,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14ValueMap<\n      std::string,\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14ValueMap<std::string, std::string>>();\n}\n\nTEST(F14NodeMap, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14NodeMap<\n      Tracked<1>,\n      int,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14NodeMap<\n      std::string,\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14NodeMap<std::string, std::string>>();\n}\n\nTEST(F14VectorMap, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14VectorMap<\n      Tracked<1>,\n      int,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14VectorMap<\n      std::string,\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14VectorMap<std::string, std::string>>();\n}\n\nTEST(F14FastMap, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14FastMap<\n      Tracked<1>,\n      int,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14FastMap<\n      std::string,\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14FastMap<std::string, std::string>>();\n}\n\nnamespace {\n\n// std::is_convertible is not transitive :( Problem scenario: B<T> is\n// implicitly convertible to A, so hasher that takes A can be used as a\n// transparent hasher for a map with key of type B<T>. C is implicitly\n// convertible to any B<T>, but we have to disable heterogeneous find\n// for C.  There is no way to infer the T of the intermediate type so C\n// can't be used to explicitly construct A.\n\nstruct A {\n  int value;\n\n  friend bool operator==(A const&, A const&) = default;\n};\n\nstruct AHasher {\n  std::size_t operator()(A const& v) const { return v.value; }\n};\n\ntemplate <typename T>\nstruct B {\n  int value;\n\n  explicit B(int v) : value(v) {}\n\n  /* implicit */ B(A const& v) : value(v.value) {}\n\n  /* implicit */ operator A() const { return A{value}; }\n};\n\nstruct C {\n  int value;\n\n  template <typename T>\n  /* implicit */ operator B<T>() const {\n    return B<T>{value};\n  }\n};\n} // namespace\n\nTEST(F14FastMap, disabledDoubleTransparent) {\n  static_assert(std::is_convertible<B<char>, A>::value);\n  static_assert(std::is_convertible<C, B<char>>::value);\n  static_assert(!std::is_convertible<C, A>::value);\n\n  F14FastMap<\n      B<char>,\n      int,\n      folly::transparent<AHasher>,\n      folly::transparent<std::equal_to<A>>>\n      map;\n  map[A{10}] = 10;\n\n  EXPECT_TRUE(map.find(C{10}) != map.end());\n  EXPECT_TRUE(map.find(C{20}) == map.end());\n}\n\ntemplate <typename M>\nvoid runRandomInsertOrderTest() {\n  if (FOLLY_F14_PERTURB_INSERTION_ORDER) {\n    std::string prev;\n    bool diffFound = false;\n    for (int tries = 0; tries < 100; ++tries) {\n      M m;\n      for (char x = '0'; x <= '7'; ++x) {\n        m.try_emplace(x);\n      }\n      m.reserve(10);\n      auto it = m.try_emplace('8').first;\n      auto addr = &*it;\n      m.try_emplace('9');\n      EXPECT_TRUE(it == m.find('8'));\n      EXPECT_TRUE(addr = &*m.find('8'));\n      std::string s;\n      for (auto&& e : m) {\n        s.push_back(e.first);\n      }\n      LOG(INFO) << s << \"\\n\";\n      if (prev.empty()) {\n        prev = s;\n        continue;\n      }\n      if (prev != s) {\n        diffFound = true;\n        break;\n      }\n    }\n    EXPECT_TRUE(diffFound) << \"no randomness found in insert order\";\n  }\n}\n\nTEST(F14Map, randomInsertOrder) {\n  runRandomInsertOrderTest<F14ValueMap<char, char>>();\n  runRandomInsertOrderTest<F14FastMap<char, char>>();\n  runRandomInsertOrderTest<F14FastMap<char, std::string>>();\n}\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <typename M>\nvoid runContinuousCapacityTest(std::size_t minSize, std::size_t maxSize) {\n  using K = typename M::key_type;\n  for (std::size_t n = minSize; n <= maxSize; ++n) {\n    M m1;\n    m1.reserve(n);\n    auto cap = m1.bucket_count();\n    double ratio = cap * 1.0 / n;\n    // worst case scenario is that rehash just occurred and capacityScale\n    // is 5*2^12\n    EXPECT_TRUE(ratio < 1 + 1.0 / (5 << 12))\n        << ratio << \", \" << cap << \", \" << n;\n    m1[0];\n    M m2;\n    m2 = m1;\n    EXPECT_LE(m2.bucket_count(), 2);\n    for (K i = 1; i < n; ++i) {\n      folly::makeUnpredictable(i);\n      m1[i];\n    }\n    EXPECT_EQ(m1.bucket_count(), cap);\n    M m3 = m1;\n    EXPECT_EQ(m3.bucket_count(), cap);\n    for (K i = n; i <= cap; ++i) {\n      folly::makeUnpredictable(i);\n      m1[i];\n    }\n    EXPECT_GT(m1.bucket_count(), cap);\n    EXPECT_LE(m1.bucket_count(), 3 * cap);\n\n    M m4;\n    for (K i = 0; i < n; ++i) {\n      folly::makeUnpredictable(i);\n      m4[i];\n    }\n    // reserve(0) works like shrink_to_fit.  Note that tight fit (1/8\n    // waste bound) only applies for vector policy or single-chunk, which\n    // might not apply to m1.  m3 should already have been optimally sized.\n    m1.reserve(0);\n    m3.reserve(0);\n    m4.reserve(0);\n    EXPECT_GT(m1.load_factor(), 0.5);\n    EXPECT_GE(m3.load_factor(), 0.875);\n    EXPECT_EQ(m3.bucket_count(), cap);\n    EXPECT_GE(m4.load_factor(), 0.875);\n  }\n}\n\nTEST(F14Map, continuousCapacitySmall0) {\n  runContinuousCapacityTest<F14NodeMap<std::size_t, std::string>>(1, 14);\n}\n\nTEST(F14Map, continuousCapacitySmall1) {\n  runContinuousCapacityTest<F14ValueMap<std::size_t, std::string>>(1, 14);\n}\n\nTEST(F14Map, continuousCapacitySmall2) {\n  runContinuousCapacityTest<F14VectorMap<std::size_t, std::string>>(1, 100);\n}\n\nTEST(F14Map, continuousCapacitySmall3) {\n  runContinuousCapacityTest<F14FastMap<std::size_t, std::string>>(1, 14);\n}\n\nTEST(F14Map, continuousCapacityBig0) {\n  runContinuousCapacityTest<F14VectorMap<std::size_t, std::string>>(\n      1000000 - 1, 1000000 - 1);\n}\n\nTEST(F14Map, continuousCapacityBig1) {\n  runContinuousCapacityTest<F14VectorMap<std::size_t, std::string>>(\n      1000000, 1000000);\n}\n\nTEST(F14Map, continuousCapacityBig2) {\n  runContinuousCapacityTest<F14VectorMap<std::size_t, std::string>>(\n      1000000 + 1, 1000000 + 1);\n}\n\nTEST(F14Map, continuousCapacityBig3) {\n  runContinuousCapacityTest<F14VectorMap<std::size_t, std::string>>(\n      1000000 + 2, 1000000 + 2);\n}\n\nTEST(F14Map, continuousCapacityF12) {\n  runContinuousCapacityTest<F14VectorMap<uint16_t, uint16_t>>(0xfff0, 0xfffe);\n}\n\n#endif\n\ntemplate <template <class...> class TMap>\nvoid testContainsWithPrecomputedHash() {\n  TMap<int, int> m{};\n  const auto key{1};\n  m.insert({key, 1});\n  const auto hashToken = m.prehash(key);\n  EXPECT_TRUE(m.contains(hashToken, key));\n  const auto otherKey{2};\n  const auto hashTokenNotFound = m.prehash(otherKey);\n  EXPECT_FALSE(m.contains(hashTokenNotFound, otherKey));\n\n  m.prefetch(hashToken);\n  m.prefetch(hashTokenNotFound);\n}\n\nTEST(F14Map, containsWithPrecomputedHash) {\n  testContainsWithPrecomputedHash<F14ValueMap>();\n  testContainsWithPrecomputedHash<F14VectorMap>();\n  testContainsWithPrecomputedHash<F14NodeMap>();\n  testContainsWithPrecomputedHash<F14FastMap>();\n}\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\ntemplate <template <class...> class TMap>\nvoid testFindHashedKey() {\n  TMap<std::string, int> m{};\n  std::string key{\"hello\"};\n  m.insert({key, 1});\n\n  F14HashedKey<std::string> hashedKey{key};\n  EXPECT_NE(m.find(hashedKey), m.end());\n\n  std::string otherKey{\"folly\"};\n  F14HashedKey<std::string> hashedKeyNotFound{otherKey};\n  EXPECT_EQ(m.find(hashedKeyNotFound), m.end());\n}\n\nTEST(F14Map, findHashedKey) {\n  testFindHashedKey<F14ValueMap>();\n  testFindHashedKey<F14VectorMap>();\n  testFindHashedKey<F14NodeMap>();\n  testFindHashedKey<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testFindHashedKeyTransparent() {\n  struct Hasher {\n    using is_transparent = void;\n    size_t operator()(int key) const { return key; }\n  };\n  struct Equal {\n    using is_transparent = void;\n    bool operator()(int a, int b) const { return a == b; }\n  };\n\n  TMap<int, int, Hasher, Equal> m{};\n  int key{0};\n  m.insert({key, 1});\n\n  F14HashedKey<int, Hasher, Equal> hashedKey{key};\n  EXPECT_NE(m.find(hashedKey), m.end());\n\n  int otherKey{1};\n  F14HashedKey<int, Hasher, Equal> hashedKeyNotFound{otherKey};\n  EXPECT_EQ(m.find(hashedKeyNotFound), m.end());\n}\n\nTEST(F14Map, findHashedKeyTransparent) {\n  testFindHashedKeyTransparent<F14ValueMap>();\n  testFindHashedKeyTransparent<F14VectorMap>();\n  testFindHashedKeyTransparent<F14NodeMap>();\n  testFindHashedKeyTransparent<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testContainsHashedKey() {\n  TMap<std::string, int> m{};\n  std::string key{\"hello\"};\n  m.insert({key, 1});\n\n  F14HashedKey<std::string> hashedKey{key};\n  EXPECT_TRUE(m.contains(hashedKey));\n\n  std::string otherKey{\"folly\"};\n  F14HashedKey<std::string> hashedKeyNotFound{otherKey};\n  EXPECT_FALSE(m.contains(hashedKeyNotFound));\n}\n\nTEST(F14Map, containsHashedKey) {\n  testContainsHashedKey<F14ValueMap>();\n  testContainsHashedKey<F14VectorMap>();\n  testContainsHashedKey<F14NodeMap>();\n  testContainsHashedKey<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testContainsHeterogeneousHashedKey() {\n  TMap<std::string, int> m{};\n  std::string key{\"hello\"};\n  m.insert({key, 1});\n\n  F14HashedKey<std::string_view> hashedKey{key};\n  EXPECT_TRUE(m.contains(hashedKey));\n\n  std::string otherKey{\"folly\"};\n  F14HashedKey<std::string_view> hashedKeyNotFound{otherKey};\n  EXPECT_FALSE(m.contains(hashedKeyNotFound));\n}\n\nTEST(F14Map, containsHeterogeneousHashedKey) {\n  testContainsHeterogeneousHashedKey<F14ValueMap>();\n  testContainsHeterogeneousHashedKey<F14VectorMap>();\n  testContainsHeterogeneousHashedKey<F14NodeMap>();\n  testContainsHeterogeneousHashedKey<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testInsertOrAssignHashedKey() {\n  TMap<std::string, int> m{};\n  std::string key{\"hello\"};\n  m.insert({key, 1});\n\n  F14HashedKey<std::string> hashedKey{key};\n  EXPECT_FALSE(m.insert_or_assign(hashedKey, 2).second);\n\n  std::string otherKey{\"folly\"};\n  F14HashedKey<std::string> hashedKeyNotFound{otherKey};\n  EXPECT_TRUE(m.insert_or_assign(hashedKeyNotFound, 3).second);\n}\n\nTEST(F14Map, insertOrAssignHashedKey) {\n  testInsertOrAssignHashedKey<F14ValueMap>();\n  testInsertOrAssignHashedKey<F14VectorMap>();\n  testInsertOrAssignHashedKey<F14NodeMap>();\n  testInsertOrAssignHashedKey<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testInsertOrAssignRValueHashedKey() {\n  TMap<std::string, int> m{};\n  std::string key{\"hello\"};\n  m.insert({key, 1});\n\n  F14HashedKey<std::string> hashedKey{key};\n  EXPECT_FALSE(m.insert_or_assign(std::move(hashedKey), 2).second);\n\n  std::string otherKey{\"folly\"};\n  F14HashedKey<std::string> hashedKeyNotFound{otherKey};\n  EXPECT_TRUE(m.insert_or_assign(std::move(hashedKeyNotFound), 3).second);\n}\n\nTEST(F14Map, insertOrAssignRValueHashedKey) {\n  testInsertOrAssignRValueHashedKey<F14ValueMap>();\n  testInsertOrAssignRValueHashedKey<F14VectorMap>();\n  testInsertOrAssignRValueHashedKey<F14NodeMap>();\n  testInsertOrAssignRValueHashedKey<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testContainsWithPrecomputedHashKeyWrapperTransparent() {\n  struct Key {\n    int num{};\n    Key(double, int num_) : num{num_} {}\n  };\n  static_assert(!std::is_constructible_v<Key, int>);\n  static_assert(!std::is_constructible_v<int, Key>);\n  static_assert(!folly::is_detected_v<detect_op_eq, Key, Key>);\n  static_assert(!folly::is_detected_v<detect_op_eq, Key, int>);\n  static_assert(!folly::is_detected_v<detect_op_eq, int, Key>);\n  struct KeyHash {\n    using is_transparent = void;\n    size_t operator()(Key key) const { return key.num; }\n    size_t operator()(int key) const { return key; }\n  };\n  struct KeyEqual {\n    using is_transparent = void;\n    bool operator()(Key a, Key b) const { return a.num == b.num; }\n    bool operator()(Key a, int b) const { return a.num == b; }\n    bool operator()(int a, Key b) const { return a == b.num; }\n  };\n  using Map = TMap<Key, const char*, KeyHash, KeyEqual>;\n  using HKey = typename Map::hashed_key_type;\n  static_assert(std::is_same_v<HKey, F14HashedKey<Key, KeyHash, KeyEqual>>);\n\n  int num = 3;\n  Key key{0., num};\n  HKey hkey{key};\n\n  Map m{};\n  EXPECT_FALSE(m.count(key));\n  EXPECT_FALSE(m.count(num));\n  EXPECT_FALSE(m.count(hkey));\n\n  m.insert({key, \"hello\"});\n  EXPECT_TRUE(m.count(key));\n  EXPECT_TRUE(m.count(num));\n  EXPECT_TRUE(m.count(hkey));\n  EXPECT_STREQ(\"hello\", m.find(key)->second);\n  EXPECT_STREQ(\"hello\", m.find(num)->second);\n  EXPECT_STREQ(\"hello\", m.find(hkey)->second);\n  m.clear();\n\n  m.insert({hkey, \"world\"});\n  EXPECT_TRUE(m.count(key));\n  EXPECT_TRUE(m.count(num));\n  EXPECT_TRUE(m.count(hkey));\n  EXPECT_STREQ(\"world\", m.find(key)->second);\n  EXPECT_STREQ(\"world\", m.find(num)->second);\n  EXPECT_STREQ(\"world\", m.find(hkey)->second);\n  m.clear();\n}\n\nTEST(F14Map, containsWithPrecomputedHashKeyWrapperTransparent) {\n  testContainsWithPrecomputedHashKeyWrapperTransparent<F14ValueMap>();\n  testContainsWithPrecomputedHashKeyWrapperTransparent<F14VectorMap>();\n  testContainsWithPrecomputedHashKeyWrapperTransparent<F14NodeMap>();\n  testContainsWithPrecomputedHashKeyWrapperTransparent<F14FastMap>();\n}\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <template <class...> class TMap>\nvoid testEraseIf() {\n  TMap<int, int> m{{1, 1}, {2, 2}, {3, 3}, {4, 4}};\n  const auto isEvenKey = [](const auto& p) { return p.first % 2 == 0; };\n  EXPECT_EQ(2u, erase_if(m, isEvenKey));\n  ASSERT_EQ(2u, m.size());\n  EXPECT_TRUE(m.contains(1));\n  EXPECT_TRUE(m.contains(3));\n}\n\nTEST(F14Map, eraseIf) {\n  testEraseIf<F14ValueMap>();\n  testEraseIf<F14VectorMap>();\n  testEraseIf<F14NodeMap>();\n  testEraseIf<F14FastMap>();\n}\n\nnamespace {\ntemplate <std::size_t N>\nstruct DivideBy {\n  // this is a lie for testing purposes\n  using folly_is_avalanching = std::true_type;\n\n  std::size_t operator()(std::size_t v) const { return v / N; }\n};\n} // namespace\n\ntemplate <template <class...> class TMap>\nvoid testCopyAfterRemovedCollisions() {\n  // Insert 11 things into chunks 0, 1, and 2, 15 into chunk 3, then\n  // remove all but the last one from chunk 1 and see if we can find that\n  // one in a copy of the map.\n  TMap<std::size_t, bool, DivideBy<16>> map;\n  map.reserve(48);\n  for (std::size_t k = 0; k < 11; ++k) {\n    map[k] = true;\n    map[k + 16] = true;\n    map[k + 32] = true;\n  }\n  for (std::size_t k = 0; k < 14; ++k) {\n    map[k + 48] = true;\n  }\n  map[14 + 48] = true;\n  for (std::size_t k = 0; k < 14; ++k) {\n    map.erase(k + 48);\n  }\n  auto copy = map;\n  EXPECT_EQ(copy.count(14 + 48), 1);\n}\n\nTEST(F14Map, copyAfterRemovedCollisions) {\n  testCopyAfterRemovedCollisions<F14ValueMap>();\n  testCopyAfterRemovedCollisions<F14VectorMap>();\n  testCopyAfterRemovedCollisions<F14NodeMap>();\n  testCopyAfterRemovedCollisions<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testIterDeductionGuide() {\n  TMap<int, double> source({{1, 2.0}, {3, 4.0}});\n\n  TMap dest1(source.begin(), source.end());\n  static_assert(std::is_same_v<decltype(dest1), decltype(source)>);\n  EXPECT_EQ(dest1, source);\n\n  TMap dest2(source.begin(), source.end(), 2);\n  static_assert(std::is_same_v<decltype(dest2), decltype(source)>);\n  EXPECT_EQ(dest2, source);\n\n  TMap dest3(source.begin(), source.end(), 2, f14::DefaultHasher<int>{});\n  static_assert(std::is_same_v<decltype(dest3), decltype(source)>);\n  EXPECT_EQ(dest3, source);\n\n  TMap dest4(\n      source.begin(),\n      source.end(),\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultKeyEqual<int>{});\n  static_assert(std::is_same_v<decltype(dest4), decltype(source)>);\n  EXPECT_EQ(dest4, source);\n\n  TMap dest5(\n      source.begin(),\n      source.end(),\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultKeyEqual<int>{},\n      f14::DefaultAlloc<std::pair<const int, double>>{});\n  static_assert(std::is_same_v<decltype(dest5), decltype(source)>);\n  EXPECT_EQ(dest5, source);\n\n  TMap dest6(\n      source.begin(),\n      source.end(),\n      2,\n      f14::DefaultAlloc<std::pair<const int, double>>{});\n  static_assert(std::is_same_v<decltype(dest6), decltype(source)>);\n  EXPECT_EQ(dest6, source);\n\n  TMap dest7(\n      source.begin(),\n      source.end(),\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultAlloc<std::pair<const int, double>>{});\n  static_assert(std::is_same_v<decltype(dest7), decltype(source)>);\n  EXPECT_EQ(dest7, source);\n}\n\nTEST(F14Map, iterDeductionGuide) {\n  testIterDeductionGuide<F14ValueMap>();\n  testIterDeductionGuide<F14NodeMap>();\n  testIterDeductionGuide<F14VectorMap>();\n  testIterDeductionGuide<F14FastMap>();\n}\n\ntemplate <template <class...> class TMap>\nvoid testInitializerListDeductionGuide() {\n  TMap<int, double> source({{1, 2.0}, {3, 4.0}});\n\n#if !defined(__GNUC__) || __GNUC__ > 12 || defined(__clang__)\n  // some versions of gcc, including until at least gcc v12, fail here\n  TMap dest1{std::pair{1, 2.0}, {3, 4.0}};\n  static_assert(std::is_same_v<decltype(dest1), decltype(source)>);\n  EXPECT_EQ(dest1, source);\n#endif\n\n  TMap dest2({std::pair{1, 2.0}, {3, 4.0}});\n  static_assert(std::is_same_v<decltype(dest2), decltype(source)>);\n  EXPECT_EQ(dest2, source);\n\n  TMap dest3({std::pair{1, 2.0}, {3, 4.0}}, 2);\n  static_assert(std::is_same_v<decltype(dest3), decltype(source)>);\n  EXPECT_EQ(dest3, source);\n\n  TMap dest4({std::pair{1, 2.0}, {3, 4.0}}, 2, f14::DefaultHasher<int>{});\n  static_assert(std::is_same_v<decltype(dest4), decltype(source)>);\n  EXPECT_EQ(dest4, source);\n\n  TMap dest5(\n      {std::pair{1, 2.0}, {3, 4.0}},\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultKeyEqual<int>{});\n  static_assert(std::is_same_v<decltype(dest5), decltype(source)>);\n  EXPECT_EQ(dest5, source);\n\n  TMap dest6(\n      {std::pair{1, 2.0}, {3, 4.0}},\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultKeyEqual<int>{},\n      f14::DefaultAlloc<std::pair<const int, double>>{});\n  static_assert(std::is_same_v<decltype(dest6), decltype(source)>);\n  EXPECT_EQ(dest6, source);\n\n  TMap dest7(\n      {std::pair{1, 2.0}, {3, 4.0}},\n      2,\n      f14::DefaultAlloc<std::pair<const int, double>>{});\n  static_assert(std::is_same_v<decltype(dest7), decltype(source)>);\n  EXPECT_EQ(dest7, source);\n\n  TMap dest8(\n      {std::pair{1, 2.0}, {3, 4.0}},\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultAlloc<std::pair<const int, double>>{});\n  static_assert(std::is_same_v<decltype(dest8), decltype(source)>);\n  EXPECT_EQ(dest8, source);\n}\n\nTEST(F14Map, initializerListDeductionGuide) {\n  testInitializerListDeductionGuide<F14ValueMap>();\n  testInitializerListDeductionGuide<F14NodeMap>();\n  testInitializerListDeductionGuide<F14VectorMap>();\n  testInitializerListDeductionGuide<F14FastMap>();\n}\n\nnamespace {\n\nstruct TracedMovable {\n public:\n  TracedMovable() = default;\n\n  TracedMovable& operator=(TracedMovable&& other) noexcept {\n    if (this != &other) {\n      n = std::exchange(other.n, 0);\n    }\n    return *this;\n  }\n\n  TracedMovable(TracedMovable&& other) noexcept {\n    n = std::exchange(other.n, 0);\n  }\n\n  int32_t n{10};\n};\n\n} // namespace\n\ntemplate <template <class...> class TMap>\nvoid testInsertOrAssignUnchangedIfNoInsert() {\n  TMap<int32_t, TracedMovable> m;\n\n  m[0] = TracedMovable{};\n  EXPECT_EQ(m[0].n, 10);\n\n  m.insert_or_assign(0, TracedMovable{});\n  EXPECT_EQ(m[0].n, 10);\n}\n\nTEST(F14Map, insertOrAssignUnchangedIfNoInsert) {\n  testInsertOrAssignUnchangedIfNoInsert<F14ValueMap>();\n  testInsertOrAssignUnchangedIfNoInsert<F14NodeMap>();\n  testInsertOrAssignUnchangedIfNoInsert<F14VectorMap>();\n  testInsertOrAssignUnchangedIfNoInsert<F14FastMap>();\n}\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <typename M>\nvoid runSimpleShrinkToFitTest(float expectedLoadFactor) {\n  using K = typename M::key_type;\n  for (int n = 1; n <= 1000; ++n) {\n    M m;\n    for (K k = 0; k < n; ++k) {\n      m[k];\n    }\n    m.reserve(0); // reserve(0) works like shrink_to_fit\n    EXPECT_GE(m.load_factor(), expectedLoadFactor);\n  }\n}\n\nTEST(F14Map, shrinkToFit) {\n  runSimpleShrinkToFitTest<F14NodeMap<int, int>>(0.5);\n  runSimpleShrinkToFitTest<F14ValueMap<int, int>>(0.5);\n  runSimpleShrinkToFitTest<F14VectorMap<int, int>>(0.875);\n}\n\ntemplate <typename M>\nvoid runDefactoShrinkToFitTest(float expectedLoadFactor) {\n  for (int n = 1; n <= 1000; n += (n + 9) / 10) {\n    M m1;\n    for (int k = 0; k < n; ++k) {\n      m1[k];\n    }\n    for (int j = 0; j <= n; ++j) {\n      auto m2 = m1;\n      // any argument < size is a \"defacto\" shrink_to_fit\n      m2.reserve(m2.size() - j);\n      EXPECT_GE(m2.load_factor(), expectedLoadFactor);\n    }\n  }\n}\n\nTEST(F14Map, defactoShrinkToFit) {\n  runDefactoShrinkToFitTest<F14NodeMap<int, int>>(0.5);\n  runDefactoShrinkToFitTest<F14ValueMap<int, int>>(0.5);\n  runDefactoShrinkToFitTest<F14VectorMap<int, int>>(0.875);\n}\n\ntemplate <typename M>\nvoid runInitialReserveTest(float expectedLoadFactor) {\n  auto initBucketsCtor = [](int initBuckets) { return M(initBuckets); };\n  auto defaultCtorAndReserve = [](int initBuckets) {\n    M m;\n    m.reserve(initBuckets);\n    return m;\n  };\n\n  auto fill = [](M& m, int n) {\n    using K = typename M::key_type;\n    auto initBuckets = m.bucket_count();\n    for (K k = 0; k < n; ++k) {\n      m[k];\n      EXPECT_EQ(m.bucket_count(), initBuckets);\n    }\n  };\n\n  using MakeFuncs = std::initializer_list<std::function<M(int)>>;\n  for (const auto& make : MakeFuncs{initBucketsCtor, defaultCtorAndReserve}) {\n    for (int n = 1; n <= 1000; ++n) {\n      auto m = make(n);\n      fill(m, n);\n      EXPECT_GE(m.load_factor(), expectedLoadFactor);\n    }\n  }\n}\n\nTEST(F14Map, initialReserve) {\n  runInitialReserveTest<F14NodeMap<int, int>>(0.5);\n  runInitialReserveTest<F14ValueMap<int, int>>(0.5);\n  runInitialReserveTest<F14VectorMap<int, int>>(0.875);\n}\n\ntemplate <typename M>\nvoid runReserveMoreTest(int n) {\n  constexpr int kIters = 1000;\n  M m;\n  int k = 0;\n  for (int i = 0; i < kIters; ++i) {\n    auto bc = m.bucket_count();\n    m.reserve(m.size() + n);\n    EXPECT_GE(m.bucket_count(), bc); // should never shrink\n    for (int j = 0; j < n; ++j) {\n      bc = m.bucket_count();\n      m[k++];\n      EXPECT_EQ(m.bucket_count(), bc);\n    }\n  }\n}\n\nTEST(F14Map, reserveMoreNeverShrinks) {\n  runReserveMoreTest<F14NodeMap<int, int>>(1);\n  runReserveMoreTest<F14ValueMap<int, int>>(1);\n  runReserveMoreTest<F14VectorMap<int, int>>(1);\n  runReserveMoreTest<F14NodeMap<int, int>>(10);\n  runReserveMoreTest<F14ValueMap<int, int>>(10);\n  runReserveMoreTest<F14VectorMap<int, int>>(10);\n}\n\nTEST(F14Map, reserveBadAlloc) {\n  SKIP_IF(\n      std::numeric_limits<size_t>::max() <=\n      std::numeric_limits<uint32_t>::max());\n  EXPECT_THROW(\n      (F14VectorMap<int, int>().reserve(\n          std::size_t{std::numeric_limits<uint32_t>::max()} + 1)),\n      std::bad_alloc);\n}\n\n#endif\n\nTEST(F14Map, InsertOrAssignShouldNotMoveTheData) {\n  F14FastMap<int, std::vector<int>> map;\n  std::vector<int> data = {1, 2, 3};\n  map.insert_or_assign(0, data);\n  map.insert_or_assign(0, data);\n  EXPECT_EQ(data.size(), 3);\n}\n"
  },
  {
    "path": "folly/container/test/F14PolicyTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/F14Set.h>\n\n#include <functional>\n#include <type_traits>\n\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nstruct NormalHash {\n  std::size_t operator()(int i) const { return std::hash<int>()(i); }\n};\n\nstruct Force32BitHash {\n  using folly_assume_32bit_hash = std::true_type;\n\n  std::size_t operator()(int i) const { return std::hash<int>()(i); }\n};\n\nstruct F14ValueSetTester {\n  static void test() {\n    static_assert(\n        !F14FastSet<int, NormalHash>::Policy::shouldAssume32BitHash());\n    static_assert(\n        F14FastSet<int, Force32BitHash>::Policy::shouldAssume32BitHash());\n  }\n};\n\nTEST(F14Policy, assume32BitTag) {\n  F14ValueSetTester::test();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/test/F14SetTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n// Allow tests for keys that throw in copy/move constructors. This\n// warning has to be disabled before the templates are defined in the\n// header to have any effect.\nFOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\")\n\n// clang-format off:\n#include <folly/container/F14Set.h>\n// clang-format on\n\n#include <numeric>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/Conv.h>\n#include <folly/FBString.h>\n#include <folly/container/test/F14TestUtil.h>\n#include <folly/container/test/TrackingTypes.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GTest.h>\n#include <folly/test/TestUtils.h>\n\nusing namespace folly;\nusing namespace folly::f14;\nusing namespace folly::string_piece_literals;\nusing namespace folly::test;\n\nextern \"C\" FOLLY_KEEP int check_std_unordered_set_int_accumulate(\n    std::unordered_set<int> const& set) {\n  return std::accumulate(set.begin(), set.end(), 0);\n}\nextern \"C\" FOLLY_KEEP int check_folly_f14_node_set_int_accumulate(\n    folly::F14NodeSet<int> const& set) {\n  return std::accumulate(set.begin(), set.end(), 0);\n}\nextern \"C\" FOLLY_KEEP int check_folly_f14_vector_set_int_accumulate(\n    folly::F14VectorSet<int> const& set) {\n  return std::accumulate(set.begin(), set.end(), 0);\n}\nextern \"C\" FOLLY_KEEP int check_folly_f14_value_set_int_accumulate(\n    folly::F14ValueSet<int> const& set) {\n  return std::accumulate(set.begin(), set.end(), 0);\n}\n\nextern \"C\" FOLLY_KEEP size_t\ncheck_std_unordered_set_int_count(std::unordered_set<int> const& set, int key) {\n  return set.count(key);\n}\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_node_set_int_count(folly::F14NodeSet<int> const& set, int key) {\n  return set.count(key);\n}\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_vector_set_int_count(folly::F14VectorSet<int> const& set, int key) {\n  return set.count(key);\n}\nextern \"C\" FOLLY_KEEP size_t\ncheck_folly_value_set_int_count(folly::F14ValueSet<int> const& set, int key) {\n  return set.count(key);\n}\n\nstatic constexpr bool kFallback = folly::f14::detail::getF14IntrinsicsMode() ==\n    folly::f14::detail::F14IntrinsicsMode::None;\n\ntemplate <typename T>\nvoid runSanityChecks(T const& t) {\n  (void)t;\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n  F14TableStats::compute(t);\n#endif\n}\n\ntemplate <template <typename, typename, typename, typename> class TSet>\nvoid testCustomSwap() {\n  using std::swap;\n\n  TSet<int, DefaultHasher<int>, DefaultKeyEqual<int>, SwapTrackingAlloc<int>>\n      m0, m1;\n  resetTracking();\n  swap(m0, m1);\n\n  EXPECT_EQ(0, Tracked<0>::counts().dist(Counts{0, 0, 0, 0}));\n}\n\nTEST(F14Set, customSwap) {\n  testCustomSwap<F14ValueSet>();\n  testCustomSwap<F14NodeSet>();\n  testCustomSwap<F14VectorSet>();\n  testCustomSwap<F14FastSet>();\n}\n\nnamespace {\ntemplate <\n    template <typename, typename, typename, typename> class TSet,\n    typename K>\nvoid runAllocatedMemorySizeTest() {\n  using A = SwapTrackingAlloc<K>;\n\n  resetTracking();\n  {\n    TSet<K, DefaultHasher<K>, DefaultKeyEqual<K>, A> s;\n\n    // if F14 intrinsics are not available then we fall back to using\n    // std::unordered_set underneath, but in that case the allocation\n    // info is only best effort\n    if (!kFallback) {\n      EXPECT_EQ(testAllocatedMemorySize(), 0);\n      EXPECT_EQ(s.getAllocatedMemorySize(), 0);\n    }\n    auto emptySetAllocatedMemorySize = testAllocatedMemorySize();\n    auto emptySetAllocatedBlockCount = testAllocatedBlockCount();\n\n    for (size_t i = 0; i < 1000; ++i) {\n      s.insert(to<K>(i));\n      s.erase(to<K>(i / 10 + 2));\n      if (!kFallback) {\n        EXPECT_EQ(testAllocatedMemorySize(), s.getAllocatedMemorySize());\n      }\n      EXPECT_GE(s.getAllocatedMemorySize(), sizeof(K) * s.size());\n      std::size_t size = 0;\n      std::size_t count = 0;\n      s.visitAllocationClasses([&](std::size_t, std::size_t) mutable {});\n      s.visitAllocationClasses([&](std::size_t bytes, std::size_t n) {\n        size += bytes * n;\n        count += n;\n      });\n      if (!kFallback) {\n        EXPECT_EQ(testAllocatedMemorySize(), size);\n        EXPECT_EQ(testAllocatedBlockCount(), count);\n      }\n    }\n\n    s = decltype(s){};\n    EXPECT_EQ(testAllocatedMemorySize(), emptySetAllocatedMemorySize);\n    EXPECT_EQ(testAllocatedBlockCount(), emptySetAllocatedBlockCount);\n\n    s.reserve(5);\n    EXPECT_GT(testAllocatedMemorySize(), 0);\n    s = {};\n    if (!kFallback) {\n      EXPECT_GT(testAllocatedMemorySize(), 0);\n    }\n  }\n  EXPECT_EQ(testAllocatedMemorySize(), 0);\n  EXPECT_EQ(testAllocatedBlockCount(), 0);\n}\n\ntemplate <typename K>\nvoid runAllocatedMemorySizeTests() {\n  runAllocatedMemorySizeTest<F14ValueSet, K>();\n  runAllocatedMemorySizeTest<F14NodeSet, K>();\n  runAllocatedMemorySizeTest<F14VectorSet, K>();\n  runAllocatedMemorySizeTest<F14FastSet, K>();\n}\n} // namespace\n\nTEST(F14Set, getAllocatedMemorySize) {\n  runAllocatedMemorySizeTests<bool>();\n  runAllocatedMemorySizeTests<int>();\n  runAllocatedMemorySizeTests<long>();\n  runAllocatedMemorySizeTests<double>();\n  runAllocatedMemorySizeTests<std::string>();\n  runAllocatedMemorySizeTests<fbstring>();\n}\n\ntemplate <typename S>\nvoid runVisitContiguousRangesTest(int n) {\n  S set;\n\n  for (int i = 0; i < n; ++i) {\n    makeUnpredictable(i);\n    set.insert(i);\n    set.erase(i / 2);\n  }\n\n  std::unordered_map<uintptr_t, bool> visited;\n  for (auto& entry : set) {\n    visited[reinterpret_cast<uintptr_t>(&entry)] = false;\n  }\n\n  set.visitContiguousRanges([&](auto b, auto e) {\n    for (auto i = b; i != e; ++i) {\n      auto iter = visited.find(reinterpret_cast<uintptr_t>(i));\n      ASSERT_TRUE(iter != visited.end());\n      EXPECT_FALSE(iter->second);\n      iter->second = true;\n    }\n  });\n\n  // ensure no entries were skipped\n  for (auto& e : visited) {\n    EXPECT_TRUE(e.second);\n  }\n}\n\ntemplate <typename S>\nvoid runVisitContiguousRangesTest() {\n  runVisitContiguousRangesTest<S>(0); // empty\n  runVisitContiguousRangesTest<S>(5); // single chunk\n  runVisitContiguousRangesTest<S>(1000); // many chunks\n}\n\nTEST(F14ValueSet, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14ValueSet<int>>();\n}\n\nTEST(F14NodeSet, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14NodeSet<int>>();\n}\n\nTEST(F14VectorSet, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14VectorSet<int>>();\n}\n\nTEST(F14FastSet, visitContiguousRanges) {\n  runVisitContiguousRangesTest<F14FastSet<int>>();\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nTEST(F14Set, pmrEmpty) {\n  pmr::F14ValueSet<int> s1;\n  pmr::F14NodeSet<int> s2;\n  pmr::F14VectorSet<int> s3;\n  pmr::F14FastSet<int> s4;\n  EXPECT_TRUE(s1.empty() && s2.empty() && s3.empty() && s4.empty());\n}\n#endif\n\nnamespace {\nstruct NestedHash {\n  template <typename N>\n  std::size_t operator()(N const& v) const;\n};\n\ntemplate <template <class...> class TSet>\nstruct Nested {\n  std::unique_ptr<TSet<Nested, NestedHash>> set_;\n\n  explicit Nested(int depth)\n      : set_(std::make_unique<TSet<Nested, NestedHash>>()) {\n    if (depth > 0) {\n      set_->emplace(depth - 1);\n    }\n  }\n};\n\ntemplate <typename N>\nstd::size_t NestedHash::operator()(N const& v) const {\n  std::size_t rv = 0;\n  for (auto& k : *v.set_) {\n    rv += operator()(k);\n  }\n  return Hash{}(rv);\n}\n\ntemplate <template <class...> class TSet>\nbool operator==(Nested<TSet> const& lhs, Nested<TSet> const& rhs) {\n  return *lhs.set_ == *rhs.set_;\n}\n\ntemplate <template <class...> class TSet>\nbool operator!=(Nested<TSet> const& lhs, Nested<TSet> const& rhs) {\n  return !(lhs == rhs);\n}\n\ntemplate <template <class...> class TSet>\nvoid testNestedSetEquality() {\n  auto n1 = Nested<TSet>(100);\n  auto n2 = Nested<TSet>(100);\n  auto n3 = Nested<TSet>(99);\n  EXPECT_TRUE(n1 == n1);\n  EXPECT_TRUE(n1 == n2);\n  EXPECT_FALSE(n1 == n3);\n  EXPECT_FALSE(n1 != n1);\n  EXPECT_FALSE(n1 != n2);\n  EXPECT_TRUE(n1 != n3);\n}\n\ntemplate <template <class...> class TSet>\nvoid testEqualityRefinement() {\n  TSet<std::pair<int, int>, HashFirst, EqualFirst> s1;\n  TSet<std::pair<int, int>, HashFirst, EqualFirst> s2;\n  s1.insert(std::make_pair(0, 0));\n  s1.insert(std::make_pair(1, 1));\n  EXPECT_FALSE(s1.insert(std::make_pair(0, 2)).second);\n  EXPECT_EQ(s1.size(), 2);\n  EXPECT_EQ(s1.count(std::make_pair(0, 10)), 1);\n  for (auto& k : s1) {\n    s2.emplace(k.first, k.second + 1);\n  }\n  EXPECT_EQ(s1.size(), s2.size());\n  for (auto& k : s1) {\n    EXPECT_EQ(s2.count(k), 1);\n  }\n  EXPECT_FALSE(s1 == s2);\n  EXPECT_TRUE(s1 != s2);\n}\n} // namespace\n\nTEST(F14Set, nestedSetEquality) {\n  testNestedSetEquality<F14ValueSet>();\n  testNestedSetEquality<F14NodeSet>();\n  testNestedSetEquality<F14VectorSet>();\n  testNestedSetEquality<F14FastSet>();\n}\n\nTEST(F14Set, equalityRefinement) {\n  testEqualityRefinement<F14ValueSet>();\n  testEqualityRefinement<F14NodeSet>();\n  testEqualityRefinement<F14VectorSet>();\n  testEqualityRefinement<F14FastSet>();\n}\n\nnamespace {\nstd::string s(char const* p) {\n  return p;\n}\n} // namespace\n\ntemplate <typename T>\nvoid runSimple() {\n  T h;\n\n  EXPECT_EQ(h.size(), 0);\n  h.reserve(0);\n  std::vector<std::string> v({\"abc\", \"abc\"});\n  h.insert(v.begin(), v.begin());\n  EXPECT_EQ(h.size(), 0);\n  if (!kFallback) {\n    EXPECT_EQ(h.bucket_count(), 0);\n  }\n  h.insert(v.begin(), v.end());\n  EXPECT_EQ(h.size(), 1);\n  h = T{};\n  if (!kFallback) {\n    EXPECT_EQ(h.bucket_count(), 0);\n  }\n\n  h.insert(s(\"abc\"));\n  EXPECT_TRUE(h.find(s(\"def\")) == h.end());\n  EXPECT_FALSE(h.find(s(\"abc\")) == h.end());\n  h.insert(s(\"ghi\"));\n  EXPECT_EQ(h.size(), 2);\n  h.erase(h.find(s(\"abc\")));\n  EXPECT_EQ(h.size(), 1);\n\n  T h2(std::move(h));\n  EXPECT_EQ(h.size(), 0);\n  EXPECT_TRUE(h.begin() == h.end());\n  EXPECT_EQ(h2.size(), 1);\n\n  EXPECT_TRUE(h2.find(s(\"abc\")) == h2.end());\n  EXPECT_EQ(*h2.begin(), s(\"ghi\"));\n  {\n    auto i = h2.begin();\n    EXPECT_FALSE(i == h2.end());\n    ++i;\n    EXPECT_TRUE(i == h2.end());\n  }\n\n  T h3;\n  h3.insert(s(\"xxx\"));\n  h3.insert(s(\"yyy\"));\n  h3 = std::move(h2);\n  EXPECT_EQ(h2.size(), 0);\n  EXPECT_EQ(h3.size(), 1);\n  EXPECT_TRUE(h3.find(s(\"xxx\")) == h3.end());\n\n  for (uint64_t i = 0; i < 1000; ++i) {\n    h.insert(std::move(to<std::string>(i * i * i)));\n    EXPECT_EQ(h.size(), i + 1);\n  }\n  {\n    using std::swap;\n    swap(h, h2);\n  }\n  for (uint64_t i = 0; i < 1000; ++i) {\n    EXPECT_TRUE(h2.find(to<std::string>(i * i * i)) != h2.end());\n    EXPECT_EQ(*h2.find(to<std::string>(i * i * i)), to<std::string>(i * i * i));\n    EXPECT_TRUE(h2.find(to<std::string>(i * i * i + 2)) == h2.end());\n  }\n\n  T h4{h2};\n  EXPECT_EQ(h2.size(), 1000);\n  EXPECT_EQ(h4.size(), 1000);\n\n  T h5{std::move(h2)};\n  T h6;\n  h6 = h4;\n  T h7 = h4;\n\n  T h8({s(\"abc\"), s(\"def\")});\n  T h9({s(\"abd\"), s(\"def\")});\n  EXPECT_EQ(h8.size(), 2);\n  EXPECT_EQ(h8.count(s(\"abc\")), 1);\n  EXPECT_EQ(h8.count(s(\"xyz\")), 0);\n  EXPECT_TRUE(h8.contains(s(\"abc\")));\n  EXPECT_FALSE(h8.contains(s(\"xyz\")));\n\n  EXPECT_TRUE(h7 != h8);\n  EXPECT_TRUE(h8 != h9);\n\n  h8 = std::move(h7);\n  // h2 and h7 are moved from, h4, h5, h6, and h8 should be identical\n\n  EXPECT_TRUE(h4 == h8);\n\n  EXPECT_TRUE(h2.empty());\n  EXPECT_TRUE(h7.empty());\n  for (uint64_t i = 0; i < 1000; ++i) {\n    auto k = to<std::string>(i * i * i);\n    EXPECT_EQ(h4.count(k), 1);\n    EXPECT_EQ(h5.count(k), 1);\n    EXPECT_EQ(h6.count(k), 1);\n    EXPECT_EQ(h8.count(k), 1);\n    EXPECT_TRUE(h4.contains(k));\n    EXPECT_TRUE(h5.contains(k));\n    EXPECT_TRUE(h6.contains(k));\n    EXPECT_TRUE(h8.contains(k));\n  }\n\n  h8.clear();\n  h8.emplace(s(\"abc\"));\n  if (!kFallback) {\n    EXPECT_GT(h8.bucket_count(), 1);\n  }\n  h8 = {};\n  if (!kFallback) {\n    EXPECT_GT(h8.bucket_count(), 1);\n  }\n  h9 = {s(\"abc\"), s(\"def\")};\n  EXPECT_TRUE(h8.empty());\n  EXPECT_EQ(h9.size(), 2);\n\n  auto expectH8 = [&h8](T& ref) { EXPECT_EQ(&ref, &h8); };\n  expectH8((h8 = h2));\n  expectH8((h8 = std::move(h2)));\n  expectH8((h8 = {}));\n\n  runSanityChecks(h);\n  runSanityChecks(h2);\n  runSanityChecks(h3);\n  runSanityChecks(h4);\n  runSanityChecks(h5);\n  runSanityChecks(h6);\n  runSanityChecks(h7);\n  runSanityChecks(h8);\n}\n\ntemplate <typename T>\nvoid runEraseWhileIterating() {\n  constexpr int kNumElements = 1000;\n\n  // mul and kNumElements should be relatively prime\n  for (int mul : {1, 3, 17, 137, kNumElements - 1}) {\n    for (int interval : {1, 3, 5, kNumElements / 2}) {\n      T h;\n      for (auto i = 0; i < kNumElements; ++i) {\n        EXPECT_TRUE(h.emplace((i * mul) % kNumElements).second);\n      }\n\n      int sum = 0;\n      for (auto it = h.begin(); it != h.end();) {\n        sum += *it;\n        if (*it % interval == 0) {\n          it = h.erase(it);\n        } else {\n          ++it;\n        }\n      }\n      EXPECT_EQ(kNumElements * (kNumElements - 1) / 2, sum);\n    }\n  }\n}\n\ntemplate <typename T>\nvoid runRehash() {\n  unsigned n = 10000;\n  T h;\n  for (unsigned i = 0; i < n; ++i) {\n    h.insert(to<std::string>(i));\n  }\n  EXPECT_EQ(h.size(), n);\n  runSanityChecks(h);\n}\n\n// T should be a set of uint64_t\ntemplate <typename T>\nvoid runRandom() {\n  using R = std::unordered_set<uint64_t>;\n\n  std::mt19937_64 gen(0);\n  std::uniform_int_distribution<> pctDist(0, 100);\n  std::uniform_int_distribution<uint64_t> bitsBitsDist(1, 6);\n  T t0;\n  T t1;\n  R r0;\n  R r1;\n\n  for (std::size_t reps = 0; reps < 100000; ++reps) {\n    // discardBits will be from 0 to 62\n    auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2;\n    auto k = gen() >> discardBits;\n    auto pct = pctDist(gen);\n\n    EXPECT_EQ(t0.size(), r0.size());\n    if (pct < 15) {\n      // insert\n      auto t = t0.insert(k);\n      auto r = r0.insert(k);\n      EXPECT_EQ(t.second, r.second);\n      EXPECT_EQ(*t.first, *r.first);\n    } else if (pct < 25) {\n      // emplace\n      auto t = t0.emplace(k);\n      auto r = r0.emplace(k);\n      EXPECT_EQ(t.second, r.second);\n      EXPECT_EQ(*t.first, *r.first);\n    } else if (pct < 30) {\n      // bulk insert\n      t0.insert(t1.begin(), t1.end());\n      r0.insert(r1.begin(), r1.end());\n    } else if (pct < 40) {\n      // erase by key\n      auto t = t0.erase(k);\n      auto r = r0.erase(k);\n      EXPECT_EQ(t, r);\n    } else if (pct < 47) {\n      // erase by iterator\n      if (t0.size() > 0) {\n        auto r = r0.find(k);\n        if (r == r0.end()) {\n          r = r0.begin();\n        }\n        k = *r;\n        auto t = t0.find(k);\n        t = t0.erase(t);\n        if (t != t0.end()) {\n          EXPECT_NE(*t, k);\n        }\n        r = r0.erase(r);\n        if (r != r0.end()) {\n          EXPECT_NE(*r, k);\n        }\n      }\n    } else if (pct < 50) {\n      // bulk erase\n      if (t0.size() > 0) {\n        auto r = r0.find(k);\n        if (r == r0.end()) {\n          r = r0.begin();\n        }\n        k = *r;\n        auto t = t0.find(k);\n        auto firstt = t;\n        auto lastt = ++t;\n        t = t0.erase(firstt, lastt);\n        if (t != t0.end()) {\n          EXPECT_NE(*t, k);\n        }\n        auto firstr = r;\n        auto lastr = ++r;\n        r = r0.erase(firstr, lastr);\n        if (r != r0.end()) {\n          EXPECT_NE(*r, k);\n        }\n      }\n    } else if (pct < 58) {\n      // find\n      auto t = t0.find(k);\n      auto r = r0.find(k);\n      EXPECT_EQ((t == t0.end()), (r == r0.end()));\n      if (t != t0.end() && r != r0.end()) {\n        EXPECT_EQ(*t, *r);\n      }\n      EXPECT_EQ(t0.count(k), r0.count(k));\n      // TODO: When std::unordered_set supports c++20:\n      // EXPECT_EQ(t0.contains(k), r0.contains(k));\n    } else if (pct < 60) {\n      // equal_range\n      auto t = t0.equal_range(k);\n      auto r = r0.equal_range(k);\n      EXPECT_EQ((t.first == t.second), (r.first == r.second));\n      if (t.first != t.second && r.first != r.second) {\n        EXPECT_EQ(*t.first, *r.first);\n        t.first++;\n        r.first++;\n        EXPECT_TRUE(t.first == t.second);\n        EXPECT_TRUE(r.first == r.second);\n      }\n    } else if (pct < 65) {\n      // iterate\n      uint64_t t = 0;\n      for (auto& e : t0) {\n        t += e + 1000;\n      }\n      uint64_t r = 0;\n      for (auto& e : r0) {\n        r += e + 1000;\n      }\n      EXPECT_EQ(t, r);\n    } else if (pct < 69) {\n      // swap\n      using std::swap;\n      swap(t0, t1);\n      swap(r0, r1);\n    } else if (pct < 70) {\n      // swap\n      t0.swap(t1);\n      r0.swap(r1);\n    } else if (pct < 72) {\n      // default construct\n      t0.~T();\n      new (&t0) T();\n      r0.~R();\n      new (&r0) R();\n    } else if (pct < 74) {\n      // default construct with capacity\n      std::size_t capacity = k & 0xffff;\n      t0.~T();\n      new (&t0) T(capacity);\n      r0.~R();\n      new (&r0) R(capacity);\n    } else if (pct < 80) {\n      // bulk iterator construct\n      t0.~T();\n      new (&t0) T(r1.begin(), r1.end());\n      r0.~R();\n      new (&r0) R(r1.begin(), r1.end());\n    } else if (pct < 82) {\n      // initializer list construct\n      auto k2 = gen() >> discardBits;\n      t0.~T();\n      new (&t0) T({k, k, k2});\n      r0.~R();\n      new (&r0) R({k, k, k2});\n    } else if (pct < 88) {\n      // copy construct\n      t0.~T();\n      new (&t0) T(t1);\n      r0.~R();\n      new (&r0) R(r1);\n    } else if (pct < 90) {\n      // move construct\n      t0.~T();\n      new (&t0) T(std::move(t1));\n      r0.~R();\n      new (&r0) R(std::move(r1));\n    } else if (pct < 94) {\n      // copy assign\n      t0 = t1;\n      r0 = r1;\n    } else if (pct < 96) {\n      // move assign\n      t0 = std::move(t1);\n      r0 = std::move(r1);\n    } else if (pct < 98) {\n      // operator==\n      EXPECT_EQ((t0 == t1), (r0 == r1));\n    } else if (pct < 99) {\n      // clear\n      runSanityChecks(t0);\n      t0.clear();\n      r0.clear();\n    } else if (pct < 100) {\n      // reserve\n      auto scale = std::uniform_int_distribution<>(0, 8)(gen);\n      auto delta = std::uniform_int_distribution<>(-2, 2)(gen);\n      std::ptrdiff_t target = (t0.size() * scale) / 4 + delta;\n      if (target >= 0) {\n        t0.reserve(static_cast<std::size_t>(target));\n        r0.reserve(static_cast<std::size_t>(target));\n      }\n    }\n  }\n}\n\nTEST(F14ValueSet, simple) {\n  runSimple<F14ValueSet<std::string>>();\n}\n\nTEST(F14NodeSet, simple) {\n  runSimple<F14NodeSet<std::string>>();\n}\n\nTEST(F14VectorSet, simple) {\n  runSimple<F14VectorSet<std::string>>();\n}\n\nTEST(F14FastSet, simple) {\n  // F14FastSet internally uses a conditional typedef. Verify it compiles.\n  runRandom<F14FastSet<uint64_t>>();\n  runSimple<F14FastSet<std::string>>();\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nTEST(F14ValueSet, pmrSimple) {\n  runSimple<pmr::F14ValueSet<std::string>>();\n}\n\nTEST(F14NodeSet, pmrSimple) {\n  runSimple<pmr::F14NodeSet<std::string>>();\n}\n\nTEST(F14VectorSet, pmrSimple) {\n  runSimple<pmr::F14VectorSet<std::string>>();\n}\n\nTEST(F14FastSet, pmrSimple) {\n  // F14FastSet internally uses a conditional typedef. Verify it compiles.\n  runRandom<pmr::F14FastSet<uint64_t>>();\n  runSimple<pmr::F14FastSet<std::string>>();\n}\n#endif\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nTEST(F14Set, ContainerSize) {\n  {\n    F14ValueSet<int> set;\n    set.insert(10);\n    EXPECT_EQ(sizeof(set), 3 * sizeof(void*));\n    if (alignof(folly::max_align_t) == 16) {\n      // chunks will be allocated as 2 max_align_t-s\n      EXPECT_EQ(set.getAllocatedMemorySize(), 32);\n    } else {\n      // chunks will be allocated using aligned_malloc with the true size\n      EXPECT_EQ(set.getAllocatedMemorySize(), 24);\n    }\n  }\n  {\n    F14NodeSet<int> set;\n    set.insert(10);\n    EXPECT_EQ(sizeof(set), 3 * sizeof(void*));\n    if (alignof(folly::max_align_t) == 16) {\n      // chunks will be allocated as 2 max_align_t-s\n      EXPECT_EQ(set.getAllocatedMemorySize(), 36);\n    } else {\n      // chunks will be allocated using aligned_malloc with the true size\n      EXPECT_EQ(set.getAllocatedMemorySize(), 20 + 2 * sizeof(void*));\n    }\n  }\n  {\n    F14VectorSet<int> set;\n    set.insert(10);\n    EXPECT_EQ(sizeof(set), 8 + 2 * sizeof(void*));\n    EXPECT_EQ(set.getAllocatedMemorySize(), 32);\n  }\n}\n\nTEST(F14VectorMap, reverseIterator) {\n  using TSet = F14VectorSet<uint64_t>;\n  auto populate = [](TSet& h, uint64_t lo, uint64_t hi) {\n    for (auto i = lo; i < hi; ++i) {\n      h.insert(i);\n    }\n  };\n  auto verify = [](TSet const& h, uint64_t lo, uint64_t hi) {\n    auto loIt = h.find(lo);\n    EXPECT_NE(h.end(), loIt);\n    uint64_t val = lo;\n    for (auto rit = h.riter(loIt); rit != h.rend(); ++rit) {\n      EXPECT_EQ(val, *rit);\n      TSet::const_iterator it = h.iter(rit);\n      EXPECT_EQ(val, *it);\n      val++;\n    }\n    EXPECT_EQ(hi, val);\n  };\n\n  TSet h;\n  size_t prevSize = 0;\n  size_t newSize = 1;\n  // verify iteration order across rehashes, copies, and moves\n  while (newSize < 10'000) {\n    populate(h, prevSize, newSize);\n    verify(h, 0, newSize);\n    verify(h, newSize / 2, newSize);\n\n    TSet h2{h};\n    verify(h2, 0, newSize);\n\n    h = std::move(h2);\n    verify(h, 0, newSize);\n    prevSize = newSize;\n    newSize *= 10;\n  }\n}\n\nTEST(F14VectorSet, OrderPreservingReinsertionView) {\n  F14VectorSet<int> s1;\n  for (size_t i = 0; i < 5; ++i) {\n    s1.emplace(i);\n  }\n\n  F14VectorSet<int> s2;\n  for (const auto& k : order_preserving_reinsertion_view(s1)) {\n    s2.insert(k);\n  }\n\n  EXPECT_EQ(asVector(s1), asVector(s2));\n}\n\n#endif\n\nTEST(F14ValueSet, eraseWhileIterating) {\n  runEraseWhileIterating<F14ValueSet<int>>();\n}\n\nTEST(F14NodeSet, eraseWhileIterating) {\n  runEraseWhileIterating<F14NodeSet<int>>();\n}\n\nTEST(F14VectorSet, eraseWhileIterating) {\n  runEraseWhileIterating<F14VectorSet<int>>();\n}\n\nTEST(F14ValueSet, rehash) {\n  runRehash<F14ValueSet<std::string>>();\n}\n\nTEST(F14NodeSet, rehash) {\n  runRehash<F14NodeSet<std::string>>();\n}\n\nTEST(F14VectorSet, rehash) {\n  runRehash<F14VectorSet<std::string>>();\n}\n\nTEST(F14ValueSet, random) {\n  runRandom<F14ValueSet<uint64_t>>();\n}\n\nTEST(F14NodeSet, random) {\n  runRandom<F14NodeSet<uint64_t>>();\n}\n\nTEST(F14VectorSet, random) {\n  runRandom<F14VectorSet<uint64_t>>();\n}\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\nTEST(F14ValueSet, growStats) {\n  F14ValueSet<uint64_t> h;\n  for (unsigned i = 1; i <= 3072; ++i) {\n    h.insert(i);\n  }\n  // F14ValueSet just before rehash\n  runSanityChecks(h);\n  h.insert(0);\n  // F14ValueSet just after rehash\n  runSanityChecks(h);\n}\n\nTEST(F14ValueSet, steadyStateStats) {\n  // 10k keys, 14% probability of insert, 90% chance of erase, so the\n  // table should converge to 1400 size without triggering the rehash\n  // that would occur at 1536.\n  F14ValueSet<uint64_t> h;\n  std::mt19937 gen(0);\n  std::uniform_int_distribution<> dist(0, 10000);\n  for (std::size_t i = 0; i < 100000; ++i) {\n    auto key = dist(gen);\n    if (dist(gen) < 1400) {\n      h.insert(key);\n    } else {\n      h.erase(key);\n    }\n    if (((i + 1) % 10000) == 0) {\n      auto stats = F14TableStats::compute(h);\n      // Verify that average miss probe length is bounded despite continued\n      // erase + reuse.  p99 of the average across 10M random steps is 4.69,\n      // average is 2.96.\n      EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);\n    }\n  }\n  // F14ValueSet at steady state\n  runSanityChecks(h);\n}\n\n#endif\n\n// S should be a set of Tracked<0>.  F should take a set\n// and a key_type const& or key_type&& and cause it to be inserted\ntemplate <typename S, typename F>\nvoid runInsertCases(std::string const& /* name */, F const& insertFunc) {\n  static_assert(std::is_same<typename S::value_type, Tracked<0>>::value);\n  {\n    typename S::value_type k{0};\n    S s;\n    resetTracking();\n    insertFunc(s, k);\n    // fresh key, value_type const& ->\n    // copy is expected\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{1, 0, 0, 0}), 0);\n  }\n  {\n    typename S::value_type k{0};\n    S s;\n    resetTracking();\n    insertFunc(s, std::move(k));\n    // fresh key, value_type&& ->\n    // move is expected\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 0}), 0);\n  }\n}\n\nstruct DoInsert {\n  template <typename M, typename P>\n  void operator()(M& m, P&& p) const {\n    m.insert(std::forward<P>(p));\n  }\n};\n\nstruct DoEmplace1 {\n  template <typename M, typename P>\n  void operator()(M& m, P&& p) const {\n    m.emplace(std::forward<P>(p));\n  }\n};\n\ntemplate <typename S>\nvoid runInsertAndEmplace() {\n  {\n    typename S::value_type k1{0};\n    typename S::value_type k2{0};\n    S s;\n    resetTracking();\n    EXPECT_TRUE(s.insert(k1).second);\n    // copy is expected on successful insert\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{1, 0, 0, 0}), 0);\n\n    resetTracking();\n    EXPECT_FALSE(s.insert(k2).second);\n    // no copies or moves on failing insert\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0);\n  }\n  {\n    typename S::value_type k1{0};\n    typename S::value_type k2{0};\n    S s;\n    resetTracking();\n    EXPECT_TRUE(s.insert(std::move(k1)).second);\n    // move is expected on successful insert\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 0}), 0);\n\n    resetTracking();\n    EXPECT_FALSE(s.insert(std::move(k2)).second);\n    // no copies or moves on failing insert\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0);\n  }\n  {\n    typename S::value_type k1{0};\n    typename S::value_type k2{0};\n    uint64_t k3 = 0;\n    S s;\n    resetTracking();\n    EXPECT_TRUE(s.emplace(k1).second);\n    // copy is expected on successful emplace\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{1, 0, 0, 0}), 0)\n        << Tracked<0>::counts();\n\n    resetTracking();\n    EXPECT_FALSE(s.emplace(k2).second);\n    if (!kFallback) {\n      // no copies or moves on failing emplace with value_type\n      EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0)\n          << Tracked<0>::counts();\n    }\n\n    resetTracking();\n    EXPECT_FALSE(s.emplace(k3).second);\n    if (!kFallback) {\n      // copy convert expected for failing emplace with wrong type\n      EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 1, 0}), 0)\n          << Tracked<0>::counts();\n    }\n\n    s.clear();\n    resetTracking();\n    EXPECT_TRUE(s.emplace(k3).second);\n    // copy convert + move expected for successful emplace with wrong type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 1, 0}), 0)\n        << Tracked<0>::counts();\n  }\n  {\n    typename S::value_type k1{0};\n    typename S::value_type k2{0};\n    uint64_t k3 = 0;\n    S s;\n    resetTracking();\n    EXPECT_TRUE(s.emplace(std::move(k1)).second);\n    // move is expected on successful emplace\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 0}), 0)\n        << Tracked<0>::counts();\n\n    resetTracking();\n    EXPECT_FALSE(s.emplace(std::move(k2)).second);\n    if (!kFallback) {\n      // no copies or moves on failing emplace with value_type\n      EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0)\n          << Tracked<0>::counts();\n    }\n\n    resetTracking();\n    EXPECT_FALSE(s.emplace(std::move(k3)).second);\n    if (!kFallback) {\n      // move convert expected for failing emplace with wrong type\n      EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 1}), 0)\n          << Tracked<0>::counts();\n    }\n\n    s.clear();\n    resetTracking();\n    EXPECT_TRUE(s.emplace(std::move(k3)).second);\n    // move convert + move expected for successful emplace with wrong type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 1}), 0)\n        << Tracked<0>::counts();\n  }\n\n  // Calling the default pair constructor via emplace is valid, but not\n  // very useful in real life.  Verify that it works.\n  S s;\n  typename S::value_type k;\n  EXPECT_EQ(s.count(k), 0);\n  EXPECT_FALSE(s.contains(k));\n  s.emplace();\n  EXPECT_EQ(s.count(k), 1);\n  EXPECT_TRUE(s.contains(k));\n  s.emplace();\n  EXPECT_EQ(s.count(k), 1);\n  EXPECT_TRUE(s.contains(k));\n}\n\nTEST(F14ValueSet, destructuring) {\n  runInsertAndEmplace<F14ValueSet<Tracked<0>>>();\n}\n\nTEST(F14NodeSet, destructuring) {\n  runInsertAndEmplace<F14NodeSet<Tracked<0>>>();\n}\n\nTEST(F14VectorSet, destructuring) {\n  runInsertAndEmplace<F14VectorSet<Tracked<0>>>();\n}\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\nTEST(F14ValueSet, maxSize) {\n  F14ValueSet<int> s;\n  EXPECT_EQ(\n      s.max_size(),\n      std::min(\n          folly::f14::detail::SizeAndChunkShift::kMaxSize,\n          std::allocator_traits<decltype(s)::allocator_type>::max_size(\n              s.get_allocator())));\n}\n\nTEST(F14NodeSet, maxSize) {\n  F14NodeSet<int> s;\n  EXPECT_EQ(\n      s.max_size(),\n      std::min(\n          folly::f14::detail::SizeAndChunkShift::kMaxSize,\n          std::allocator_traits<decltype(s)::allocator_type>::max_size(\n              s.get_allocator())));\n}\n\nTEST(F14VectorSet, maxSize) {\n  F14VectorSet<int> s;\n  EXPECT_EQ(\n      s.max_size(),\n      std::min(\n          {folly::f14::detail::SizeAndChunkShift::kMaxSize,\n           std::size_t{std::numeric_limits<uint32_t>::max()},\n           std::allocator_traits<decltype(s)::allocator_type>::max_size(\n               s.get_allocator())}));\n}\n#endif\n\ntemplate <typename S>\nvoid runMoveOnlyTest() {\n  S t0;\n  t0.emplace(10);\n  t0.insert(20);\n  S t1{std::move(t0)};\n  EXPECT_TRUE(t0.empty());\n  S t2;\n  EXPECT_TRUE(t2.empty());\n  t2 = std::move(t1);\n  EXPECT_EQ(t2.size(), 2);\n}\n\nTEST(F14ValueSet, moveOnly) {\n  runMoveOnlyTest<F14ValueSet<MoveOnlyTestInt>>();\n}\n\nTEST(F14NodeSet, moveOnly) {\n  runMoveOnlyTest<F14NodeSet<MoveOnlyTestInt>>();\n}\n\nTEST(F14VectorSet, moveOnly) {\n  runMoveOnlyTest<F14VectorSet<MoveOnlyTestInt>>();\n}\n\nTEST(F14FastSet, moveOnly) {\n  runMoveOnlyTest<F14FastSet<MoveOnlyTestInt>>();\n}\n\ntemplate <typename S>\nvoid runEraseIntoTest() {\n  S t0;\n  S t1;\n\n  auto insertIntoT0 = [&t0](auto&& value) {\n    EXPECT_FALSE(value.destroyed);\n    t0.emplace(std::move(value));\n  };\n  auto insertIntoT0Mut = [&](typename S::value_type&& value) mutable {\n    insertIntoT0(std::move(value));\n  };\n\n  t0.insert(10);\n  t1.insert(20);\n  t1.eraseInto(t1.begin(), insertIntoT0);\n  EXPECT_TRUE(t1.empty());\n  EXPECT_EQ(t0.size(), 2);\n  EXPECT_TRUE(t0.find(10) != t0.end());\n  EXPECT_TRUE(t0.find(20) != t0.end());\n\n  t1.insert(20);\n  t1.eraseInto(t1.cbegin(), insertIntoT0);\n  EXPECT_TRUE(t1.empty());\n\n  t1.insert(20);\n  t1.insert(30);\n  t1.insert(40);\n  t1.eraseInto(t1.begin(), t1.end(), insertIntoT0Mut);\n  EXPECT_TRUE(t1.empty());\n  EXPECT_EQ(t0.size(), 4);\n  EXPECT_TRUE(t0.find(30) != t0.end());\n  EXPECT_TRUE(t0.find(40) != t0.end());\n\n  t1.insert(50);\n  size_t erased = t1.eraseInto(*t1.find(50), insertIntoT0);\n  EXPECT_EQ(erased, 1);\n  EXPECT_TRUE(t1.empty());\n  EXPECT_EQ(t0.size(), 5);\n  EXPECT_TRUE(t0.find(50) != t0.end());\n\n  typename S::value_type key{60};\n  erased = t1.eraseInto(key, insertIntoT0Mut);\n  EXPECT_EQ(erased, 0);\n  EXPECT_EQ(t0.size(), 5);\n}\n\nTEST(F14ValueSet, eraseInto) {\n  runEraseIntoTest<F14ValueSet<MoveOnlyTestInt>>();\n}\n\nTEST(F14NodeSet, eraseInto) {\n  runEraseIntoTest<F14NodeSet<MoveOnlyTestInt>>();\n}\n\nTEST(F14VectorSet, eraseInto) {\n  runEraseIntoTest<F14VectorSet<MoveOnlyTestInt>>();\n}\n\nTEST(F14FastSet, eraseInto) {\n  runEraseIntoTest<F14FastSet<MoveOnlyTestInt>>();\n}\n\nTEST(F14ValueSet, heterogeneous) {\n  // note: std::string is implicitly convertible to but not from StringPiece\n  using Hasher = transparent<hasher<StringPiece>>;\n  using KeyEqual = transparent<std::equal_to<StringPiece>>;\n\n  constexpr auto hello = \"hello\"_sp;\n  constexpr auto buddy = \"buddy\"_sp;\n  constexpr auto world = \"world\"_sp;\n\n  F14ValueSet<std::string, Hasher, KeyEqual> set;\n  set.emplace(hello);\n  set.emplace(world);\n\n  auto checks = [hello, buddy](auto& ref) {\n    // count\n    EXPECT_EQ(0, ref.count(buddy));\n    EXPECT_EQ(1, ref.count(hello));\n\n    // find\n    EXPECT_TRUE(ref.end() == ref.find(buddy));\n    EXPECT_EQ(hello, *ref.find(hello));\n\n    const auto buddyHashToken = ref.prehash(buddy);\n    const auto helloHashToken = ref.prehash(hello);\n\n    EXPECT_TRUE(\n        buddyHashToken == ref.prehash(buddy, ref.hash_function()(buddy)));\n    EXPECT_TRUE(\n        helloHashToken == ref.prehash(hello, ref.hash_function()(hello)));\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n    EXPECT_FALSE(\n        buddyHashToken == ref.prehash(hello, ref.hash_function()(hello)));\n#endif\n\n    // prehash + find\n    EXPECT_TRUE(ref.end() == ref.find(buddyHashToken, buddy));\n    EXPECT_EQ(hello, *ref.find(helloHashToken, hello));\n\n    // contains\n    EXPECT_FALSE(ref.contains(buddy));\n    EXPECT_TRUE(ref.contains(hello));\n\n    // contains with prehash\n    EXPECT_FALSE(ref.contains(buddyHashToken, buddy));\n    EXPECT_TRUE(ref.contains(helloHashToken, hello));\n\n    // equal_range\n    EXPECT_TRUE(std::make_pair(ref.end(), ref.end()) == ref.equal_range(buddy));\n    EXPECT_TRUE(\n        std::make_pair(ref.find(hello), ++ref.find(hello)) ==\n        ref.equal_range(hello));\n  };\n\n  checks(set);\n  checks(std::as_const(set));\n}\n\ntemplate <typename S>\nvoid runStatefulFunctorTest() {\n  bool ranHasher = false;\n  bool ranEqual = false;\n  bool ranAlloc = false;\n  bool ranDealloc = false;\n\n  auto hasher = [&](int x) {\n    ranHasher = true;\n    return x;\n  };\n  auto equal = [&](int x, int y) {\n    ranEqual = true;\n    return x == y;\n  };\n  auto alloc = [&](std::size_t n) {\n    ranAlloc = true;\n    return std::malloc(n);\n  };\n  auto dealloc = [&](void* p, std::size_t) {\n    ranDealloc = true;\n    std::free(p);\n  };\n\n  {\n    S set(0, hasher, equal, {alloc, dealloc});\n    set.insert(10);\n    set.insert(10);\n    EXPECT_EQ(set.size(), 1);\n\n    S set2(set);\n    S set3(std::move(set));\n    set = set2;\n    set2.clear();\n    set2 = std::move(set3);\n  }\n  EXPECT_TRUE(ranHasher);\n  EXPECT_TRUE(ranEqual);\n  EXPECT_TRUE(ranAlloc);\n  EXPECT_TRUE(ranDealloc);\n}\n\nTEST(F14ValueSet, statefulFunctors) {\n  runStatefulFunctorTest<F14ValueSet<\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<int>>>();\n}\n\nTEST(F14NodeSet, statefulFunctors) {\n  runStatefulFunctorTest<F14NodeSet<\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<int>>>();\n}\n\nTEST(F14VectorSet, statefulFunctors) {\n  runStatefulFunctorTest<F14VectorSet<\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<int>>>();\n}\n\nTEST(F14FastSet, statefulFunctors) {\n  runStatefulFunctorTest<F14FastSet<\n      int,\n      GenericHasher<int>,\n      GenericEqual<int>,\n      GenericAlloc<int>>>();\n}\n\ntemplate <typename S>\nvoid runHeterogeneousInsertTest() {\n  S set;\n\n  resetTracking();\n  EXPECT_EQ(set.count(10), 0);\n  EXPECT_FALSE(set.contains(10));\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts;\n\n  resetTracking();\n  set.insert(10);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 1}), 0)\n      << Tracked<1>::counts;\n\n  resetTracking();\n  int k = 10;\n  std::vector<int> v({10});\n  set.insert(10);\n  set.insert(k);\n  set.insert(v.begin(), v.end());\n  set.insert(\n      std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));\n  set.emplace(10);\n  set.emplace(k);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts;\n\n  resetTracking();\n  set.erase(20);\n  EXPECT_EQ(set.size(), 1);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts;\n\n  resetTracking();\n  set.erase(10);\n  EXPECT_EQ(set.size(), 0);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts;\n\n  set.insert(10);\n  resetTracking();\n  set.erase(set.find(10));\n  EXPECT_EQ(set.size(), 0);\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts;\n\n  set.insert(10);\n  resetTracking();\n  set.eraseInto(10, [](auto&&) {});\n  EXPECT_EQ(Tracked<1>::counts().dist(Counts{0, 0, 0, 0}), 0)\n      << Tracked<1>::counts;\n}\n\ntemplate <typename S>\nvoid runHeterogeneousInsertStringTest() {\n  S set;\n  StringPiece k{\"foo\"};\n  std::vector<StringPiece> v{k};\n\n  set.insert(k);\n  set.insert(\"foo\");\n  set.insert(StringPiece{\"foo\"});\n  set.insert(v.begin(), v.end());\n  set.insert(\n      std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));\n\n  set.emplace();\n  set.emplace(k);\n  set.emplace(\"foo\");\n  set.emplace(StringPiece(\"foo\"));\n\n  set.erase(\"\");\n  set.erase(k);\n  set.erase(StringPiece{\"foo\"});\n  EXPECT_TRUE(set.empty());\n\n  set.insert(k);\n  set.erase(set.find(k));\n  set.insert(k);\n  typename S::const_iterator it = set.find(k);\n  set.erase(it);\n}\n\nTEST(F14ValueSet, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14ValueSet<\n      Tracked<1>,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14ValueSet<\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14ValueSet<std::string>>();\n}\n\nTEST(F14NodeSet, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14NodeSet<\n      Tracked<1>,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14NodeSet<\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14NodeSet<std::string>>();\n}\n\nTEST(F14VectorSet, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14VectorSet<\n      Tracked<1>,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14VectorSet<\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14VectorSet<std::string>>();\n}\n\nTEST(F14FastSet, heterogeneousInsert) {\n  runHeterogeneousInsertTest<F14FastSet<\n      Tracked<1>,\n      TransparentTrackedHash<1>,\n      TransparentTrackedEqual<1>>>();\n  runHeterogeneousInsertStringTest<F14FastSet<\n      std::string,\n      transparent<hasher<StringPiece>>,\n      transparent<DefaultKeyEqual<StringPiece>>>>();\n  runHeterogeneousInsertStringTest<F14FastSet<std::string>>();\n}\n\nnamespace {\n\n// std::is_convertible is not transitive :( Problem scenario: B<T> is\n// implicitly convertible to A, so hasher that takes A can be used as a\n// transparent hasher for a map with key of type B<T>. C is implicitly\n// convertible to any B<T>, but we have to disable heterogeneous find\n// for C.  There is no way to infer the T of the intermediate type so C\n// can't be used to explicitly construct A.\n\nstruct A {\n  int value;\n\n  friend bool operator==(A const&, A const&) = default;\n};\n\nstruct AHasher {\n  std::size_t operator()(A const& v) const { return v.value; }\n};\n\ntemplate <typename T>\nstruct B {\n  int value;\n\n  explicit B(int v) : value(v) {}\n\n  /* implicit */ B(A const& v) : value(v.value) {}\n\n  /* implicit */ operator A() const { return A{value}; }\n};\n\nstruct C {\n  int value;\n\n  template <typename T>\n  /* implicit */ operator B<T>() const {\n    return B<T>{value};\n  }\n};\n} // namespace\n\nTEST(F14FastSet, disabledDoubleTransparent) {\n  static_assert(std::is_convertible<B<char>, A>::value);\n  static_assert(std::is_convertible<C, B<char>>::value);\n  static_assert(!std::is_convertible<C, A>::value);\n\n  F14FastSet<B<char>, transparent<AHasher>, transparent<std::equal_to<A>>> set;\n  set.emplace(A{10});\n\n  EXPECT_TRUE(set.find(C{10}) != set.end());\n  EXPECT_TRUE(set.find(C{20}) == set.end());\n}\n\nnamespace {\nstruct CharArrayHasher {\n  template <std::size_t N>\n  std::size_t operator()(std::array<char, N> const& value) const {\n    return Hash{}(StringPiece{value.data(), &value.data()[value.size()]});\n  }\n};\n\ntemplate <\n    template <typename, typename, typename, typename> class S,\n    std::size_t N>\nstruct RunAllValueSizeTests {\n  void operator()() const {\n    using Key = std::array<char, N>;\n    static_assert(sizeof(Key) == N);\n    S<Key, CharArrayHasher, std::equal_to<Key>, std::allocator<Key>> set;\n\n    for (int i = 0; i < 100; ++i) {\n      Key key{{static_cast<char>(i)}};\n      set.insert(key);\n    }\n    while (!set.empty()) {\n      set.erase(set.begin());\n    }\n\n    RunAllValueSizeTests<S, N - 1>{}();\n  }\n};\n\ntemplate <template <typename, typename, typename, typename> class S>\nstruct RunAllValueSizeTests<S, 0> {\n  void operator()() const {}\n};\n} // namespace\n\nTEST(F14ValueSet, valueSize) {\n  RunAllValueSizeTests<F14ValueSet, 32>{}();\n}\n\ntemplate <typename S, typename F>\nvoid runRandomInsertOrderTest(F&& func) {\n  if (FOLLY_F14_PERTURB_INSERTION_ORDER) {\n    std::string prev;\n    bool diffFound = false;\n    for (int tries = 0; tries < 100; ++tries) {\n      S set;\n      for (char x = '0'; x <= '9'; ++x) {\n        set.emplace(func(x));\n      }\n      std::string s;\n      for (auto&& e : set) {\n        s += e;\n      }\n      LOG(INFO) << s << \"\\n\";\n      if (prev.empty()) {\n        prev = s;\n        continue;\n      }\n      if (prev != s) {\n        diffFound = true;\n        break;\n      }\n    }\n    EXPECT_TRUE(diffFound) << \"no randomness found in insert order\";\n  }\n}\n\nTEST(F14Set, randomInsertOrder) {\n  runRandomInsertOrderTest<F14ValueSet<char>>([](char x) { return x; });\n  runRandomInsertOrderTest<F14FastSet<char>>([](char x) { return x; });\n  runRandomInsertOrderTest<F14FastSet<std::string>>([](char x) {\n    return std::string{std::size_t{1}, x};\n  });\n}\n\ntemplate <template <class...> class TSet>\nvoid testContainsWithPrecomputedHash() {\n  TSet<int> m{};\n  const auto key{1};\n  m.insert(key);\n  const auto hashToken = m.prehash(key);\n  EXPECT_TRUE(m.contains(hashToken, key));\n  const auto otherKey{2};\n  const auto hashTokenNotFound = m.prehash(otherKey);\n  EXPECT_FALSE(m.contains(hashTokenNotFound, otherKey));\n  m.prefetch(hashToken);\n  m.prefetch(hashTokenNotFound);\n}\n\nTEST(F14Set, containsWithPrecomputedHash) {\n  testContainsWithPrecomputedHash<F14ValueSet>();\n  testContainsWithPrecomputedHash<F14NodeSet>();\n  testContainsWithPrecomputedHash<F14VectorSet>();\n  testContainsWithPrecomputedHash<F14FastSet>();\n}\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\ntemplate <template <class...> class TSet>\nvoid testFindHashedKey() {\n  TSet<std::string> s{};\n  std::string key{\"hello\"};\n  s.insert(key);\n\n  F14HashedKey<std::string> hashedKey{key};\n  EXPECT_NE(s.find(hashedKey), s.end());\n\n  std::string otherKey{\"folly\"};\n  F14HashedKey<std::string> hashedKeyNotFound{otherKey};\n  EXPECT_EQ(s.find(hashedKeyNotFound), s.end());\n}\n\nTEST(F14Set, findHashedKey) {\n  testFindHashedKey<F14ValueSet>();\n  testFindHashedKey<F14NodeSet>();\n  testFindHashedKey<F14VectorSet>();\n  testFindHashedKey<F14FastSet>();\n}\n\ntemplate <template <class...> class TSet>\nvoid testContainsHashedKey() {\n  TSet<std::string> s{};\n  std::string key{\"hello\"};\n  s.insert(key);\n\n  F14HashedKey<std::string> hashedKey{key};\n  EXPECT_TRUE(s.contains(hashedKey));\n\n  std::string otherKey{\"folly\"};\n  F14HashedKey<std::string> hashedKeyNotFound{otherKey};\n  EXPECT_FALSE(s.contains(hashedKeyNotFound));\n}\n\nTEST(F14Set, containsHashedKey) {\n  testContainsHashedKey<F14ValueSet>();\n  testContainsHashedKey<F14NodeSet>();\n  testContainsHashedKey<F14VectorSet>();\n  testContainsHashedKey<F14FastSet>();\n}\n\n#endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\n\ntemplate <template <class...> class TSet>\nvoid testEraseIf() {\n  TSet<int> s{1, 2, 3, 4};\n  const auto isEvenKey = [](const auto& key) { return key % 2 == 0; };\n  EXPECT_EQ(2u, erase_if(s, isEvenKey));\n  ASSERT_EQ(2u, s.size());\n  EXPECT_TRUE(s.contains(1));\n  EXPECT_TRUE(s.contains(3));\n}\n\nTEST(F14Set, eraseIf) {\n  testEraseIf<F14ValueSet>();\n  testEraseIf<F14FastSet>();\n  testEraseIf<F14VectorSet>();\n  testEraseIf<F14NodeSet>();\n}\n\ntemplate <template <class...> class TSet>\nvoid testExceptionOnInsert() {\n  TSet<ThrowOnCopyTestInt> m{};\n  ThrowOnCopyTestInt key;\n  EXPECT_THROW(m.insert(key), std::exception);\n  EXPECT_TRUE(m.empty());\n}\n\nTEST(F14Set, ExceptionOnInsert) {\n  testExceptionOnInsert<F14ValueSet>();\n  testExceptionOnInsert<F14NodeSet>();\n  testExceptionOnInsert<F14VectorSet>();\n  testExceptionOnInsert<F14FastSet>();\n}\n\ntemplate <template <class...> class TSet>\nvoid testIterDeductionGuide() {\n  TSet<int> source({1, 2});\n\n  TSet dest1(source.begin(), source.end());\n  static_assert(std::is_same_v<decltype(dest1), decltype(source)>);\n  EXPECT_EQ(dest1, source);\n\n  TSet dest2(source.begin(), source.end(), 2);\n  static_assert(std::is_same_v<decltype(dest2), decltype(source)>);\n  EXPECT_EQ(dest2, source);\n\n  TSet dest3(source.begin(), source.end(), 2, f14::DefaultHasher<int>{});\n  static_assert(std::is_same_v<decltype(dest3), decltype(source)>);\n  EXPECT_EQ(dest3, source);\n\n  TSet dest4(\n      source.begin(),\n      source.end(),\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultKeyEqual<int>{});\n  static_assert(std::is_same_v<decltype(dest4), decltype(source)>);\n  EXPECT_EQ(dest4, source);\n\n  TSet dest5(\n      source.begin(),\n      source.end(),\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultKeyEqual<int>{},\n      f14::DefaultAlloc<int>{});\n  static_assert(std::is_same_v<decltype(dest5), decltype(source)>);\n  EXPECT_EQ(dest5, source);\n\n  TSet dest6(source.begin(), source.end(), 2, f14::DefaultAlloc<int>{});\n  static_assert(std::is_same_v<decltype(dest6), decltype(source)>);\n  EXPECT_EQ(dest6, source);\n\n  TSet dest7(\n      source.begin(),\n      source.end(),\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultAlloc<int>{});\n  static_assert(std::is_same_v<decltype(dest7), decltype(source)>);\n  EXPECT_EQ(dest7, source);\n}\n\nTEST(F14Set, iterDeductionGuide) {\n  testIterDeductionGuide<F14ValueSet>();\n  testIterDeductionGuide<F14NodeSet>();\n  testIterDeductionGuide<F14VectorSet>();\n  testIterDeductionGuide<F14FastSet>();\n}\n\ntemplate <template <class...> class TSet>\nvoid testInitializerListDeductionGuide() {\n  TSet<int> source({1, 2});\n\n  TSet dest1({1, 2}, 2);\n  static_assert(std::is_same_v<decltype(dest1), decltype(source)>);\n  EXPECT_EQ(dest1, source);\n\n  TSet dest2({1, 2}, 2, f14::DefaultHasher<int>{});\n  static_assert(std::is_same_v<decltype(dest2), decltype(source)>);\n  EXPECT_EQ(dest2, source);\n\n  TSet dest3({1, 2}, 2, f14::DefaultHasher<int>{}, f14::DefaultKeyEqual<int>{});\n  static_assert(std::is_same_v<decltype(dest3), decltype(source)>);\n  EXPECT_EQ(dest3, source);\n\n  TSet dest4(\n      {1, 2},\n      2,\n      f14::DefaultHasher<int>{},\n      f14::DefaultKeyEqual<int>{},\n      f14::DefaultAlloc<int>{});\n  static_assert(std::is_same_v<decltype(dest4), decltype(source)>);\n  EXPECT_EQ(dest4, source);\n\n  TSet dest5({1, 2}, 2, f14::DefaultAlloc<int>{});\n  static_assert(std::is_same_v<decltype(dest5), decltype(source)>);\n  EXPECT_EQ(dest5, source);\n\n  TSet dest6({1, 2}, 2, f14::DefaultHasher<int>{}, f14::DefaultAlloc<int>{});\n  static_assert(std::is_same_v<decltype(dest6), decltype(source)>);\n  EXPECT_EQ(dest6, source);\n}\n\nTEST(F14Set, initializerListDeductionGuide) {\n  testInitializerListDeductionGuide<F14ValueSet>();\n  testInitializerListDeductionGuide<F14NodeSet>();\n  testInitializerListDeductionGuide<F14VectorSet>();\n  testInitializerListDeductionGuide<F14FastSet>();\n}\n"
  },
  {
    "path": "folly/container/test/F14SmallOverheads.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <functional>\n#include <iostream>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include <folly/container/F14Map.h>\n#include <folly/lang/Keep.h>\n\nusing namespace std;\nusing namespace folly;\n\ntemplate <typename T>\nstruct LoggingAlloc {\n  using value_type = T;\n\n  LoggingAlloc() {}\n\n  template <typename A>\n  explicit LoggingAlloc(A&&) {}\n\n  T* allocate(std::size_t n) {\n    cout << \"allocate \" << n << \" values, \" << n * sizeof(T) << \" bytes\\n\";\n    return std::allocator<T>{}.allocate(n);\n  }\n\n  void deallocate(T* ptr, std::size_t n) {\n    cout << \"deallocate \" << n << \" values, \" << n * sizeof(T) << \" bytes\\n\";\n    std::allocator<T>{}.deallocate(ptr, n);\n  }\n\n  bool operator==(LoggingAlloc<T> const&) const { return true; }\n  bool operator!=(LoggingAlloc<T> const&) const { return false; }\n\n  // Everything below here is optional when properly using\n  // allocator_traits, but dense_hash_map doesn't use allocator_traits yet\n\n  using pointer = T*;\n  using const_pointer = T const*;\n  using reference = T&;\n  using const_reference = T const&;\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n\n  template <typename U>\n  struct rebind {\n    using other = LoggingAlloc<U>;\n  };\n\n  T* address(T& v) const { return &v; }\n  T const* address(T const& v) const { return &v; }\n  std::size_t max_size() const {\n    return std::numeric_limits<std::size_t>::max();\n  }\n};\n\ntemplate <typename K, typename V, template <typename> class A>\nusing StdUnorderedMapTable = std::unordered_map<\n    K,\n    V,\n    std::hash<K>,\n    std::equal_to<K>,\n    A<std::pair<K const, V>>>;\n\ntemplate <typename K, typename V, template <typename> class A>\nusing F14ValueMapTable =\n    F14ValueMap<K, V, std::hash<K>, std::equal_to<K>, A<std::pair<K const, V>>>;\n\ntemplate <typename K, typename V, template <typename> class A>\nusing F14NodeMapTable =\n    F14NodeMap<K, V, std::hash<K>, std::equal_to<K>, A<std::pair<K const, V>>>;\n\ntemplate <typename K, typename V, template <typename> class A>\nusing F14VectorMapTable = F14VectorMap<\n    K,\n    V,\n    std::hash<K>,\n    std::equal_to<K>,\n    A<std::pair<K const, V>>>;\n\ntemplate <typename M>\nvoid runSingleInsert(std::string const& name) {\n  cout << \"----------------------\\n\";\n  cout << name << \"\\n\";\n  cout << \"SIZE = \" << sizeof(M) << \"\\n\";\n  cout << \"CONSTRUCTING\\n\";\n  {\n    M map;\n    cout << \"INSERTING 1 VALUE\\n\";\n    typename M::key_type k{};\n    map[k];\n    cout << \"DESTROYING\\n\";\n  }\n  cout << \"\\n\";\n}\n\ntemplate <template <typename, typename, template <typename> class> class T>\nvoid runSingleInserts(std::string const& name) {\n  runSingleInsert<T<uint64_t, array<char, 8>, LoggingAlloc>>(\n      name + \" uint64_t 8\");\n  runSingleInsert<T<string, array<char, 8>, LoggingAlloc>>(name + \" string 8\");\n  runSingleInsert<T<uint64_t, array<char, 128>, LoggingAlloc>>(\n      name + \" uint64_t 128\");\n  runSingleInsert<T<string, array<char, 128>, LoggingAlloc>>(\n      name + \" string 128\");\n}\n\nFOLLY_KEEP int codeSize_find_Std(\n    std::unordered_map<int16_t, float>& m, int16_t k) {\n  auto i = m.find(k);\n  return i != m.end() ? 1 : 0;\n}\n\nFOLLY_KEEP int codeSize_find_F14Value(\n    F14ValueMap<int16_t, float>& m, int16_t k) {\n  auto i = m.find(k);\n  return i != m.end() ? 1 : 0;\n}\n\nFOLLY_KEEP int codeSize_find_F14Node(F14NodeMap<int16_t, float>& m, int16_t k) {\n  auto i = m.find(k);\n  return i != m.end() ? 1 : 0;\n}\n\nFOLLY_KEEP int codeSize_find_F14Vector(\n    F14VectorMap<int16_t, float>& m, int16_t k) {\n  auto i = m.find(k);\n  return i != m.end() ? 1 : 0;\n}\n\nFOLLY_KEEP void codeSize_bracket_Std(\n    std::unordered_map<int16_t, uint32_t>& m, int16_t k, uint32_t v) {\n  m[k] = v;\n}\n\nFOLLY_KEEP void codeSize_bracket_F14Value(\n    F14ValueMap<int16_t, uint32_t>& m, int16_t k, uint32_t v) {\n  m[k] = v;\n}\n\nFOLLY_KEEP void codeSize_bracket_F14Node(\n    F14NodeMap<int16_t, uint32_t>& m, int16_t k, uint32_t v) {\n  m[k] = v;\n}\n\nFOLLY_KEEP void codeSize_bracket_F14Vector(\n    F14VectorMap<int16_t, uint32_t>& m, int16_t k, uint32_t v) {\n  m[k] = v;\n}\n\nFOLLY_KEEP void codeSize_erase_Std(\n    std::unordered_map<int16_t, uint32_t>& m,\n    std::unordered_map<int16_t, uint32_t>::iterator iter) {\n  m.erase(iter);\n}\n\nFOLLY_KEEP void codeSize_erase_F14Value(\n    F14ValueMap<int16_t, uint32_t>& m,\n    F14ValueMap<int16_t, uint32_t>::iterator iter) {\n  m.erase(iter);\n}\n\nFOLLY_KEEP void codeSize_erase_F14Node(\n    F14NodeMap<int16_t, uint32_t>& m,\n    F14NodeMap<int16_t, uint32_t>::iterator iter) {\n  m.erase(iter);\n}\n\nFOLLY_KEEP void codeSize_erase_F14Vector(\n    F14VectorMap<int16_t, uint32_t>& m,\n    F14VectorMap<int16_t, uint32_t>::iterator iter) {\n  m.erase(iter);\n}\n\nint main(int, char**) {\n  (void)codeSize_find_Std;\n  (void)codeSize_find_F14Value;\n  (void)codeSize_find_F14Node;\n  (void)codeSize_find_F14Vector;\n\n  (void)codeSize_bracket_Std;\n  (void)codeSize_bracket_F14Value;\n  (void)codeSize_bracket_F14Node;\n  (void)codeSize_bracket_F14Vector;\n\n  (void)codeSize_erase_Std;\n  (void)codeSize_erase_F14Value;\n  (void)codeSize_erase_F14Node;\n  (void)codeSize_erase_F14Vector;\n\n  runSingleInserts<StdUnorderedMapTable>(\"std\");\n  runSingleInserts<F14ValueMapTable>(\"f14value\");\n  runSingleInserts<F14NodeMapTable>(\"f14node\");\n  runSingleInserts<F14VectorMapTable>(\"f14vector\");\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/container/test/F14TestUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n#include <type_traits>\n#include <vector>\n\n#include <folly/container/detail/F14Policy.h>\n#include <folly/container/detail/F14Table.h>\n\nnamespace folly {\nnamespace f14 {\n\nstruct Histo {\n  std::vector<std::size_t> const& data;\n};\n\ninline std::ostream& operator<<(std::ostream& xo, Histo const& histo) {\n  xo << \"[\";\n  size_t sum = 0;\n  for (auto v : histo.data) {\n    sum += v;\n  }\n  auto const dsum = static_cast<double>(sum);\n  size_t partial = 0;\n  for (size_t i = 0; i < histo.data.size(); ++i) {\n    if (i > 0) {\n      xo << \", \";\n    }\n    partial += histo.data[i];\n    if (histo.data[i] > 0) {\n      xo << i << \": \" << histo.data[i] << \" (\"\n         << (static_cast<double>(partial) * 100.0 / dsum) << \"%)\";\n    }\n  }\n  xo << \"]\";\n  return xo;\n}\n\ninline double expectedProbe(std::vector<std::size_t> const& probeLengths) {\n  std::size_t sum = 0;\n  std::size_t count = 0;\n  for (std::size_t i = 1; i < probeLengths.size(); ++i) {\n    sum += i * probeLengths[i];\n    count += probeLengths[i];\n  }\n  return static_cast<double>(sum) / static_cast<double>(count);\n}\n\n// Returns i such that probeLengths elements 0 to i (inclusive) account\n// for at least 99% of the samples.\ninline std::size_t p99Probe(std::vector<std::size_t> const& probeLengths) {\n  std::size_t count = 0;\n  for (std::size_t i = 1; i < probeLengths.size(); ++i) {\n    count += probeLengths[i];\n  }\n  std::size_t rv = probeLengths.size();\n  std::size_t suffix = 0;\n  while ((suffix + probeLengths[rv - 1]) * 100 <= count) {\n    --rv;\n  }\n  return rv;\n}\n\ninline std::ostream& operator<<(std::ostream& xo, F14TableStats const& stats) {\n  xo << \"{ \" << std::endl;\n  xo << \"  policy: \" << stats.policy << std::endl;\n  xo << \"  size: \" << stats.size << std::endl;\n  xo << \"  valueSize: \" << stats.valueSize << std::endl;\n  xo << \"  bucketCount: \" << stats.bucketCount << std::endl;\n  xo << \"  chunkCount: \" << stats.chunkCount << std::endl;\n  xo << \"  chunkOccupancyHisto\" << Histo{stats.chunkOccupancyHisto}\n     << std::endl;\n  xo << \"  chunkOutboundOverflowHisto\"\n     << Histo{stats.chunkOutboundOverflowHisto} << std::endl;\n  xo << \"  chunkHostedOverflowHisto\" << Histo{stats.chunkHostedOverflowHisto}\n     << std::endl;\n  xo << \"  keyProbeLengthHisto\" << Histo{stats.keyProbeLengthHisto}\n     << std::endl;\n  xo << \"  missProbeLengthHisto\" << Histo{stats.missProbeLengthHisto}\n     << std::endl;\n  xo << \"  totalBytes: \" << stats.totalBytes << std::endl;\n  xo << \"  valueBytes: \" << (stats.size * stats.valueSize) << std::endl;\n  xo << \"  overheadBytes: \" << stats.overheadBytes << std::endl;\n  if (stats.size > 0) {\n    xo << \"  overheadBytesPerKey: \"\n       << (static_cast<double>(stats.overheadBytes) /\n           static_cast<double>(stats.size))\n       << std::endl;\n  }\n  xo << \"}\";\n  return xo;\n}\n\ntemplate <typename Container>\nstd::vector<typename std::decay_t<Container>::value_type> asVector(\n    const Container& c) {\n  return {c.begin(), c.end()};\n}\n\n} // namespace f14\n} // namespace folly\n"
  },
  {
    "path": "folly/container/test/FBVectorBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <deque>\n#include <list>\n#include <memory>\n#include <string>\n\n#include <folly/Benchmark.h>\n#include <folly/FBVector.h>\n#include <folly/Traits.h>\n#include <folly/container/Foreach.h>\n#include <folly/portability/GFlags.h>\n#include <folly/small_vector.h>\n#include <folly/test/FBVectorTestUtil.h>\n\nusing namespace std;\nusing namespace folly;\nusing namespace folly::test::detail;\n\nusing IntVector = vector<int>;\nusing IntFBVector = fbvector<int>;\nusing IntList = list<int>;\nusing IntDeque = deque<int>;\nusing IntSmallVector = small_vector<int>;\n\nusing StringVector = vector<std::string>;\nusing StringFBVector = fbvector<std::string>;\nusing StringList = list<std::string>;\nusing StringDeque = deque<std::string>;\nusing StringSmallVector = small_vector<std::string>;\n\nusing FBStringVector = vector<folly::fbstring>;\nusing FBStringFBVector = fbvector<folly::fbstring>;\n\n#define VECTOR IntVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR IntFBVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR IntSmallVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR IntList\n#define SKIP_RESERVE\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef SKIP_RESERVE\n#undef VECTOR\n#define VECTOR IntDeque\n#define SKIP_RESERVE\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef SKIP_RESERVE\n#undef VECTOR\n\n#define VECTOR StringVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR StringFBVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR StringSmallVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR StringList\n#define SKIP_RESERVE\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef SKIP_RESERVE\n#undef VECTOR\n#define VECTOR StringDeque\n#define SKIP_RESERVE\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef SKIP_RESERVE\n#undef VECTOR\n\n#define VECTOR FBStringVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR FBStringFBVector\n#include <folly/container/test/FBVectorBenchmarks.cpp.h> // nolint\n#undef VECTOR\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_max_iters\", \"1000000\", folly::gflags::SET_FLAG_IF_DEFAULT);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_min_iters\", \"100000\", folly::gflags::SET_FLAG_IF_DEFAULT);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_max_secs\", \"1\", folly::gflags::SET_FLAG_IF_DEFAULT);\n\n  folly::runBenchmarks();\n  return 0;\n}\n\n// clang-format off\n/*\n============================================================================\nbuck-out/opt/gen/folly/test/fbvector_benchmark#gcc-5-glibc-2.23,private-headers/folly/container/test/FBVectorBenchmarks.cpp.hrelative  time/iter  iters/s\n============================================================================\nBM_zzInitRNG_IntVector                                       1.05us  951.24K\nBM_defaultCtor_IntVector                                     1.31ns  765.93M\nBM_sizeCtor_IntVector(16)                                   19.33ns   51.73M\nBM_sizeCtor_IntVector(128)                                  42.11ns   23.75M\nBM_sizeCtor_IntVector(1024)                                 60.90ns   16.42M\nBM_fillCtor_IntVector(16)                                   30.67ns   32.61M\nBM_fillCtor_IntVector(128)                                  41.22ns   24.26M\nBM_fillCtor_IntVector(1024)                                133.70ns    7.48M\nBM_reserve_IntVector(16)                                    40.27ns   24.83M\nBM_reserve_IntVector(128)                                   40.20ns   24.88M\nBM_reserve_IntVector(1024)                                  40.17ns   24.90M\nBM_insertFront_IntVector(16)                                 7.90us  126.52K\nBM_insertFront_IntVector(128)                                8.12us  123.09K\nBM_insertFront_IntVector(1024)                               8.30us  120.46K\nBM_insertFront_IntVector(10240)                             10.14us   98.67K\nBM_insertFront_IntVector(102400)                            30.71us   32.56K\nBM_insertFront_IntVector(1024000)                          220.69us    4.53K\nBM_pushBack_IntVector(16)                                  776.38ps    1.29G\nBM_pushBack_IntVector(128)                                 775.89ps    1.29G\nBM_pushBack_IntVector(1024)                                742.50ps    1.35G\nBM_pushBack_IntVector(10240)                               787.75ps    1.27G\nBM_pushBack_IntVector(102400)                              714.07ps    1.40G\nBM_pushBack_IntVector(1024000)                               3.15ns  317.26M\nBM_zzInitRNG_IntFBVector                                     1.17us  853.35K\nBM_defaultCtor_IntFBVector                                 989.76ps    1.01G\nBM_sizeCtor_IntFBVector(16)                                 27.19ns   36.78M\nBM_sizeCtor_IntFBVector(128)                                46.73ns   21.40M\nBM_sizeCtor_IntFBVector(1024)                               69.03ns   14.49M\nBM_fillCtor_IntFBVector(16)                                 35.97ns   27.80M\nBM_fillCtor_IntFBVector(128)                                55.11ns   18.15M\nBM_fillCtor_IntFBVector(1024)                              147.89ns    6.76M\nBM_reserve_IntFBVector(16)                                  54.18ns   18.46M\nBM_reserve_IntFBVector(128)                                 54.24ns   18.44M\nBM_reserve_IntFBVector(1024)                                54.24ns   18.44M\nBM_insertFront_IntFBVector(16)                               8.41us  118.86K\nBM_insertFront_IntFBVector(128)                              8.45us  118.41K\nBM_insertFront_IntFBVector(1024)                             8.56us  116.80K\nBM_insertFront_IntFBVector(10240)                           10.72us   93.32K\nBM_insertFront_IntFBVector(102400)                          30.83us   32.43K\nBM_insertFront_IntFBVector(1024000)                        217.31us    4.60K\nBM_pushBack_IntFBVector(16)                                  2.05ns  488.26M\nBM_pushBack_IntFBVector(128)                                 1.99ns  503.65M\nBM_pushBack_IntFBVector(1024)                                2.16ns  462.50M\nBM_pushBack_IntFBVector(10240)                               2.13ns  468.48M\nBM_pushBack_IntFBVector(102400)                              1.93ns  517.23M\nBM_pushBack_IntFBVector(1024000)                             1.89ns  529.29M\nBM_zzInitRNG_IntSmallVector                                  1.17us  855.04K\nBM_defaultCtor_IntSmallVector                              698.82ps    1.43G\nBM_sizeCtor_IntSmallVector(16)                              37.59ns   26.60M\nBM_sizeCtor_IntSmallVector(128)                             85.90ns   11.64M\nBM_sizeCtor_IntSmallVector(1024)                           401.37ns    2.49M\nBM_fillCtor_IntSmallVector(16)                              48.22ns   20.74M\nBM_fillCtor_IntSmallVector(128)                             99.99ns   10.00M\nBM_fillCtor_IntSmallVector(1024)                           458.71ns    2.18M\nBM_reserve_IntSmallVector(16)                               44.30ns   22.57M\nBM_reserve_IntSmallVector(128)                              44.29ns   22.58M\nBM_reserve_IntSmallVector(1024)                             45.15ns   22.15M\nBM_insertFront_IntSmallVector(16)                            8.40us  119.11K\nBM_insertFront_IntSmallVector(128)                           7.74us  129.25K\nBM_insertFront_IntSmallVector(1024)                          8.17us  122.47K\nBM_insertFront_IntSmallVector(10240)                        10.17us   98.34K\nBM_insertFront_IntSmallVector(102400)                       29.60us   33.79K\nBM_insertFront_IntSmallVector(1024000)                     208.82us    4.79K\nBM_pushBack_IntSmallVector(16)                               2.92ns  342.66M\nBM_pushBack_IntSmallVector(128)                              2.91ns  343.36M\nBM_pushBack_IntSmallVector(1024)                             2.76ns  362.74M\nBM_pushBack_IntSmallVector(10240)                            2.71ns  369.18M\nBM_pushBack_IntSmallVector(102400)                           3.04ns  329.36M\nBM_pushBack_IntSmallVector(1024000)                          4.90ns  204.21M\nBM_zzInitRNG_IntList                                         1.04us  958.67K\nBM_defaultCtor_IntList                                     911.25ps    1.10G\nBM_sizeCtor_IntList(16)                                    264.10ns    3.79M\nBM_sizeCtor_IntList(128)                                     2.08us  481.87K\nBM_sizeCtor_IntList(1024)                                   35.52us   28.15K\nBM_fillCtor_IntList(16)                                    269.86ns    3.71M\nBM_fillCtor_IntList(128)                                     2.12us  470.70K\nBM_fillCtor_IntList(1024)                                   46.59us   21.47K\nBM_insertFront_IntList(16)                                  18.88ns   52.95M\nBM_insertFront_IntList(128)                                 19.67ns   50.85M\nBM_insertFront_IntList(1024)                                18.79ns   53.22M\nBM_insertFront_IntList(10240)                               20.47ns   48.85M\nBM_insertFront_IntList(102400)                              17.43ns   57.37M\nBM_insertFront_IntList(1024000)                             17.65ns   56.65M\nBM_pushBack_IntList(16)                                     20.45ns   48.89M\nBM_pushBack_IntList(128)                                    21.54ns   46.42M\nBM_pushBack_IntList(1024)                                   20.14ns   49.64M\nBM_pushBack_IntList(10240)                                  21.21ns   47.15M\nBM_pushBack_IntList(102400)                                 18.53ns   53.98M\nBM_pushBack_IntList(1024000)                                22.16ns   45.12M\nBM_zzInitRNG_IntDeque                                        1.14us  879.33K\nBM_defaultCtor_IntDeque                                     33.14ns   30.18M\nBM_sizeCtor_IntDeque(16)                                    44.34ns   22.56M\nBM_sizeCtor_IntDeque(128)                                   81.28ns   12.30M\nBM_sizeCtor_IntDeque(1024)                                 338.93ns    2.95M\nBM_fillCtor_IntDeque(16)                                    52.18ns   19.16M\nBM_fillCtor_IntDeque(128)                                   76.01ns   13.16M\nBM_fillCtor_IntDeque(1024)                                 329.99ns    3.03M\nBM_insertFront_IntDeque(16)                                  2.56ns  390.51M\nBM_insertFront_IntDeque(128)                                 2.48ns  403.57M\nBM_insertFront_IntDeque(1024)                                2.31ns  432.60M\nBM_insertFront_IntDeque(10240)                               2.30ns  434.90M\nBM_insertFront_IntDeque(102400)                              2.32ns  431.00M\nBM_insertFront_IntDeque(1024000)                             2.36ns  423.26M\nBM_pushBack_IntDeque(16)                                   935.50ps    1.07G\nBM_pushBack_IntDeque(128)                                  935.72ps    1.07G\nBM_pushBack_IntDeque(1024)                                 942.23ps    1.06G\nBM_pushBack_IntDeque(10240)                                934.27ps    1.07G\nBM_pushBack_IntDeque(102400)                               947.61ps    1.06G\nBM_pushBack_IntDeque(1024000)                              993.47ps    1.01G\nBM_zzInitRNG_StringVector                                    1.03us  966.54K\nBM_defaultCtor_StringVector                                911.27ps    1.10G\nBM_sizeCtor_StringVector(16)                                35.94ns   27.83M\nBM_sizeCtor_StringVector(128)                              233.07ns    4.29M\nBM_sizeCtor_StringVector(1024)                               1.83us  546.61K\nBM_fillCtor_StringVector(16)                                10.30us   97.07K\nBM_fillCtor_StringVector(128)                               21.56us   46.37K\nBM_fillCtor_StringVector(1024)                             128.63us    7.77K\nBM_reserve_StringVector(16)                                 45.76ns   21.85M\nBM_reserve_StringVector(128)                                60.52ns   16.52M\nBM_reserve_StringVector(1024)                               59.59ns   16.78M\nBM_insertFront_StringVector(16)                            124.99us    8.00K\nBM_insertFront_StringVector(128)                           120.57us    8.29K\nBM_insertFront_StringVector(1024)                          126.47us    7.91K\nBM_insertFront_StringVector(10240)                         153.43us    6.52K\nBM_insertFront_StringVector(102400)                        380.73us    2.63K\nBM_insertFront_StringVector(1024000)                         3.96ms   252.31\nBM_pushBack_StringVector(16)                                40.16ns   24.90M\nBM_pushBack_StringVector(128)                               41.94ns   23.85M\nBM_pushBack_StringVector(1024)                              36.92ns   27.08M\nBM_pushBack_StringVector(10240)                             18.19ns   54.99M\nBM_pushBack_StringVector(102400)                            41.21ns   24.27M\nBM_pushBack_StringVector(1024000)                          234.95ns    4.26M\nBM_zzInitRNG_StringFBVector                                  1.05us  956.06K\nBM_defaultCtor_StringFBVector                              911.25ps    1.10G\nBM_sizeCtor_StringFBVector(16)                              38.40ns   26.04M\nBM_sizeCtor_StringFBVector(128)                            202.10ns    4.95M\nBM_sizeCtor_StringFBVector(1024)                             1.68us  593.56K\nBM_fillCtor_StringFBVector(16)                               6.65us  150.29K\nBM_fillCtor_StringFBVector(128)                             14.76us   67.76K\nBM_fillCtor_StringFBVector(1024)                           117.60us    8.50K\nBM_reserve_StringFBVector(16)                               60.40ns   16.56M\nBM_reserve_StringFBVector(128)                              62.28ns   16.06M\nBM_reserve_StringFBVector(1024)                             66.76ns   14.98M\nBM_insertFront_StringFBVector(16)                          126.51us    7.90K\nBM_insertFront_StringFBVector(128)                         121.29us    8.24K\nBM_insertFront_StringFBVector(1024)                        129.81us    7.70K\nBM_insertFront_StringFBVector(10240)                       148.77us    6.72K\nBM_insertFront_StringFBVector(102400)                      380.46us    2.63K\nBM_insertFront_StringFBVector(1024000)                       3.73ms   268.02\nBM_pushBack_StringFBVector(16)                              11.89ns   84.13M\nBM_pushBack_StringFBVector(128)                             20.32ns   49.20M\nBM_pushBack_StringFBVector(1024)                            47.91ns   20.87M\nBM_pushBack_StringFBVector(10240)                           39.74ns   25.16M\nBM_pushBack_StringFBVector(102400)                          36.86ns   27.13M\nBM_pushBack_StringFBVector(1024000)                        285.22ns    3.51M\nBM_zzInitRNG_StringSmallVector                               1.04us  965.73K\nBM_defaultCtor_StringSmallVector                           607.54ps    1.65G\nBM_sizeCtor_StringSmallVector(16)                           44.30ns   22.57M\nBM_sizeCtor_StringSmallVector(128)                         234.40ns    4.27M\nBM_sizeCtor_StringSmallVector(1024)                          1.96us  510.33K\nBM_fillCtor_StringSmallVector(16)                            6.12us  163.46K\nBM_fillCtor_StringSmallVector(128)                          18.65us   53.63K\nBM_fillCtor_StringSmallVector(1024)                        132.36us    7.56K\nBM_reserve_StringSmallVector(16)                            43.86ns   22.80M\nBM_reserve_StringSmallVector(128)                           51.03ns   19.60M\nBM_reserve_StringSmallVector(1024)                          48.61ns   20.57M\nBM_insertFront_StringSmallVector(16)                       127.32us    7.85K\nBM_insertFront_StringSmallVector(128)                      118.93us    8.41K\nBM_insertFront_StringSmallVector(1024)                     130.04us    7.69K\nBM_insertFront_StringSmallVector(10240)                    143.89us    6.95K\nBM_insertFront_StringSmallVector(102400)                   386.40us    2.59K\nBM_insertFront_StringSmallVector(1024000)                    3.74ms   267.73\nBM_pushBack_StringSmallVector(16)                           50.77ns   19.70M\nBM_pushBack_StringSmallVector(128)                          44.12ns   22.67M\nBM_pushBack_StringSmallVector(1024)                         45.62ns   21.92M\nBM_pushBack_StringSmallVector(10240)                        69.06ns   14.48M\nBM_pushBack_StringSmallVector(102400)                      139.62ns    7.16M\nBM_pushBack_StringSmallVector(1024000)                     445.65ns    2.24M\nBM_zzInitRNG_StringList                                      1.17us  854.00K\nBM_defaultCtor_StringList                                  911.39ps    1.10G\nBM_sizeCtor_StringList(16)                                 309.90ns    3.23M\nBM_sizeCtor_StringList(128)                                  3.18us  314.57K\nBM_sizeCtor_StringList(1024)                                41.72us   23.97K\nBM_fillCtor_StringList(16)                                   7.12us  140.54K\nBM_fillCtor_StringList(128)                                 19.22us   52.04K\nBM_fillCtor_StringList(1024)                               160.20us    6.24K\nBM_insertFront_StringList(16)                               27.71ns   36.09M\nBM_insertFront_StringList(128)                              51.34ns   19.48M\nBM_insertFront_StringList(1024)                             55.53ns   18.01M\nBM_insertFront_StringList(10240)                            24.62ns   40.62M\nBM_insertFront_StringList(102400)                           25.63ns   39.02M\nBM_insertFront_StringList(1024000)                         341.85ns    2.93M\nBM_pushBack_StringList(16)                                  28.69ns   34.85M\nBM_pushBack_StringList(128)                                 29.11ns   34.36M\nBM_pushBack_StringList(1024)                                33.28ns   30.05M\nBM_pushBack_StringList(10240)                               26.47ns   37.78M\nBM_pushBack_StringList(102400)                              48.51ns   20.62M\nBM_pushBack_StringList(1024000)                             75.97ns   13.16M\nBM_zzInitRNG_StringDeque                                     1.17us  852.21K\nBM_defaultCtor_StringDeque                                  39.44ns   25.36M\nBM_sizeCtor_StringDeque(16)                                 88.29ns   11.33M\nBM_sizeCtor_StringDeque(128)                               444.53ns    2.25M\nBM_sizeCtor_StringDeque(1024)                                6.20us  161.17K\nBM_fillCtor_StringDeque(16)                                  6.82us  146.73K\nBM_fillCtor_StringDeque(128)                                16.95us   58.99K\nBM_fillCtor_StringDeque(1024)                              121.97us    8.20K\nBM_insertFront_StringDeque(16)                              10.75ns   92.98M\nBM_insertFront_StringDeque(128)                             40.83ns   24.49M\nBM_insertFront_StringDeque(1024)                            10.26ns   97.43M\nBM_insertFront_StringDeque(10240)                           37.85ns   26.42M\nBM_insertFront_StringDeque(102400)                          34.75ns   28.78M\nBM_insertFront_StringDeque(1024000)                         39.31ns   25.44M\nBM_pushBack_StringDeque(16)                                 11.32ns   88.31M\nBM_pushBack_StringDeque(128)                                11.93ns   83.80M\nBM_pushBack_StringDeque(1024)                               10.41ns   96.02M\nBM_pushBack_StringDeque(10240)                               9.83ns  101.72M\nBM_pushBack_StringDeque(102400)                             64.98ns   15.39M\nBM_pushBack_StringDeque(1024000)                            33.45ns   29.89M\nBM_zzInitRNG_FBStringVector                                  1.17us  855.50K\nBM_defaultCtor_FBStringVector                              989.77ps    1.01G\nBM_sizeCtor_FBStringVector(16)                              35.38ns   28.26M\nBM_sizeCtor_FBStringVector(128)                            180.30ns    5.55M\nBM_sizeCtor_FBStringVector(1024)                             1.21us  823.15K\nBM_fillCtor_FBStringVector(16)                               6.42us  155.85K\nBM_fillCtor_FBStringVector(128)                              8.90us  112.32K\nBM_fillCtor_FBStringVector(1024)                            36.57us   27.35K\nBM_reserve_FBStringVector(16)                               50.12ns   19.95M\nBM_reserve_FBStringVector(128)                              50.09ns   19.96M\nBM_reserve_FBStringVector(1024)                             53.58ns   18.66M\nBM_insertFront_FBStringVector(16)                          105.90us    9.44K\nBM_insertFront_FBStringVector(128)                         102.06us    9.80K\nBM_insertFront_FBStringVector(1024)                        103.67us    9.65K\nBM_insertFront_FBStringVector(10240)                       122.63us    8.15K\nBM_insertFront_FBStringVector(102400)                      312.48us    3.20K\nBM_insertFront_FBStringVector(1024000)                       2.30ms   434.80\nBM_pushBack_FBStringVector(16)                              10.18ns   98.26M\nBM_pushBack_FBStringVector(128)                             10.13ns   98.75M\nBM_pushBack_FBStringVector(1024)                            10.14ns   98.62M\nBM_pushBack_FBStringVector(10240)                           11.60ns   86.19M\nBM_pushBack_FBStringVector(102400)                           8.47ns  118.02M\nBM_pushBack_FBStringVector(1024000)                         88.01ns   11.36M\nBM_zzInitRNG_FBStringFBVector                                1.03us  971.03K\nBM_defaultCtor_FBStringFBVector                            911.25ps    1.10G\nBM_sizeCtor_FBStringFBVector(16)                            33.53ns   29.82M\nBM_sizeCtor_FBStringFBVector(128)                          135.17ns    7.40M\nBM_sizeCtor_FBStringFBVector(1024)                         951.05ns    1.05M\nBM_fillCtor_FBStringFBVector(16)                             5.71us  175.27K\nBM_fillCtor_FBStringFBVector(128)                            8.11us  123.37K\nBM_fillCtor_FBStringFBVector(1024)                          37.95us   26.35K\nBM_reserve_FBStringFBVector(16)                             54.53ns   18.34M\nBM_reserve_FBStringFBVector(128)                            51.41ns   19.45M\nBM_reserve_FBStringFBVector(1024)                           55.52ns   18.01M\nBM_insertFront_FBStringFBVector(16)                         58.80us   17.01K\nBM_insertFront_FBStringFBVector(128)                        58.45us   17.11K\nBM_insertFront_FBStringFBVector(1024)                       59.08us   16.93K\nBM_insertFront_FBStringFBVector(10240)                      69.85us   14.32K\nBM_insertFront_FBStringFBVector(102400)                    176.99us    5.65K\nBM_insertFront_FBStringFBVector(1024000)                     4.07ms   245.84\nBM_pushBack_FBStringFBVector(16)                             4.19ns  238.39M\nBM_pushBack_FBStringFBVector(128)                            3.76ns  265.90M\nBM_pushBack_FBStringFBVector(1024)                           4.68ns  213.66M\nBM_pushBack_FBStringFBVector(10240)                          3.24ns  309.08M\nBM_pushBack_FBStringFBVector(102400)                         3.17ns  315.07M\nBM_pushBack_FBStringFBVector(1024000)                       25.88ns   38.65M\n============================================================================\n*/\n// clang-format on\n"
  },
  {
    "path": "folly/container/test/FBVectorBenchmarks.cpp.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * This file is supposed to be included from within\n * FBVectorBenchmark. Do not use otherwise.\n */\n\nBENCHMARK(BENCHFUN(zzInitRNG)) {\n  srand(seed);\n}\n\nBENCHMARK(BENCHFUN(defaultCtor), iters) {\n  FOR_EACH_RANGE (i, 0, iters) {\n    VECTOR v;\n    doNotOptimizeAway(&v);\n  }\n}\n\nvoid BENCHFUN(sizeCtor)(int iters, int size) {\n  FOR_EACH_RANGE (i, 0, iters) {\n    VECTOR v(size);\n    doNotOptimizeAway(&v);\n  }\n}\nBENCHMARK_PARAM(BENCHFUN(sizeCtor), 16)\nBENCHMARK_PARAM(BENCHFUN(sizeCtor), 128)\nBENCHMARK_PARAM(BENCHFUN(sizeCtor), 1024)\n\nvoid BENCHFUN(fillCtor)(int iters, int size) {\n  FOR_EACH_RANGE (i, 0, iters) {\n    VECTOR v(size_t(size), randomObject<VECTOR::value_type>());\n    doNotOptimizeAway(&v);\n  }\n}\nBENCHMARK_PARAM(BENCHFUN(fillCtor), 16)\nBENCHMARK_PARAM(BENCHFUN(fillCtor), 128)\nBENCHMARK_PARAM(BENCHFUN(fillCtor), 1024)\n\n#ifndef SKIP_RESERVE\nvoid BENCHFUN(reserve)(int iters, int size) {\n  auto const obj = randomObject<VECTOR::value_type>();\n  FOR_EACH_RANGE (i, 0, iters) {\n    VECTOR v(random(0U, 1U), obj);\n    v.reserve(size);\n  }\n}\nBENCHMARK_PARAM(BENCHFUN(reserve), 16)\nBENCHMARK_PARAM(BENCHFUN(reserve), 128)\nBENCHMARK_PARAM(BENCHFUN(reserve), 1024)\n#endif\n\nvoid BENCHFUN(insertFront)(int iters, int initialSize) {\n  BenchmarkSuspender braces;\n  auto const obj = randomObject<VECTOR::value_type>();\n  VECTOR v(initialSize, obj);\n  braces.dismissing([&]() {\n    FOR_EACH_RANGE (i, 0, iters) {\n      v.insert(v.begin(), obj);\n    }\n  });\n}\n\nBENCHMARK_PARAM(BENCHFUN(insertFront), 16)\nBENCHMARK_PARAM(BENCHFUN(insertFront), 128)\nBENCHMARK_PARAM(BENCHFUN(insertFront), 1024)\nBENCHMARK_PARAM(BENCHFUN(insertFront), 10240)\nBENCHMARK_PARAM(BENCHFUN(insertFront), 102400)\nBENCHMARK_PARAM(BENCHFUN(insertFront), 1024000)\n\nvoid BENCHFUN(pushBack)(int iters, int initialSize) {\n  BenchmarkSuspender braces;\n  auto const obj = randomObject<VECTOR::value_type>();\n  VECTOR v(initialSize, obj);\n  braces.dismissing([&]() {\n    FOR_EACH_RANGE (i, 0, iters) {\n      v.push_back(obj);\n    }\n  });\n}\n\nBENCHMARK_PARAM(BENCHFUN(pushBack), 16)\nBENCHMARK_PARAM(BENCHFUN(pushBack), 128)\nBENCHMARK_PARAM(BENCHFUN(pushBack), 1024)\nBENCHMARK_PARAM(BENCHFUN(pushBack), 10240)\nBENCHMARK_PARAM(BENCHFUN(pushBack), 102400)\nBENCHMARK_PARAM(BENCHFUN(pushBack), 1024000)\n"
  },
  {
    "path": "folly/container/test/FBVectorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/FBVector.h>\n\n#include <list>\n#include <map>\n#include <memory>\n#include <numeric>\n\n#include <folly/FBString.h>\n#include <folly/Random.h>\n#include <folly/Traits.h>\n#include <folly/container/Foreach.h>\n#include <folly/portability/GTest.h>\n#include <folly/test/FBVectorTestUtil.h>\n\nusing namespace std;\nusing namespace folly;\nusing namespace folly::test::detail;\n\nusing IntFBVector = fbvector<int>;\nusing FBStringFBVector = fbvector<fbstring>;\n\n#define VECTOR IntFBVector\n#include <folly/container/test/FBVectorTests.cpp.h> // nolint\n#undef VECTOR\n#define VECTOR FBStringFBVector\n#include <folly/container/test/FBVectorTests.cpp.h> // nolint\n#undef VECTOR\n\nTEST(fbvector, clause233613Ambiguity) {\n  fbvector<int> v(10, 20);\n  EXPECT_EQ(v.size(), 10);\n  for (const auto& element : v) {\n    EXPECT_EQ(element, 20);\n  }\n}\n\nTEST(fbvector, clause2336111Ambiguity) {\n  fbvector<int> v;\n  v.assign(10, 20);\n  EXPECT_EQ(v.size(), 10);\n  for (const auto& element : v) {\n    EXPECT_EQ(element, 20);\n  }\n}\n\nTEST(fbvector, clause233626) {\n  fbvector<int> v;\n  auto const n = random(0U, 10000U);\n  v.reserve(n);\n  auto const n1 = random(0U, 10000U);\n  auto const obj = randomObject<int>();\n  v.assign(n1, obj);\n  v.shrink_to_fit();\n  // Nothing to verify except that the call made it through\n}\n\nTEST(fbvector, clause23364Ambiguity) {\n  fbvector<int> v;\n  fbvector<int>::const_iterator it = v.end();\n  v.insert(it, 10, 20);\n  EXPECT_EQ(v.size(), 10);\n  for (auto i : v) {\n    EXPECT_EQ(i, 20);\n  }\n}\n\nTEST(fbvector, composition) {\n  fbvector<fbvector<double>> matrix(100, fbvector<double>(100));\n}\n\nTEST(fbvector, worksWithStdString) {\n  fbvector<std::string> v(10, \"hello\");\n  EXPECT_EQ(v.size(), 10);\n  v.push_back(\"world\");\n}\n\nnamespace {\nstruct UserDefinedType {\n  int whatevs_;\n};\n} // namespace\n\nFOLLY_ASSUME_FBVECTOR_COMPATIBLE(UserDefinedType)\n\nTEST(fbvector, worksWithUserDefinedType) {\n  fbvector<UserDefinedType> v(10);\n  EXPECT_EQ(v.size(), 10);\n  v.push_back(UserDefinedType());\n}\n\nTEST(fbvector, moveConstruction) {\n  fbvector<int> v1(100, 100);\n  fbvector<int> v2;\n  EXPECT_EQ(v1.size(), 100);\n  EXPECT_EQ(v1.front(), 100);\n  EXPECT_EQ(v2.size(), 0);\n  v2 = std::move(v1);\n  EXPECT_EQ(v1.size(), 0);\n  EXPECT_EQ(v2.size(), 100);\n  EXPECT_EQ(v2.front(), 100);\n\n  v1.assign(100, 100);\n  auto other = std::move(v1);\n  EXPECT_EQ(v1.size(), 0);\n  EXPECT_EQ(other.size(), 100);\n  EXPECT_EQ(other.front(), 100);\n}\n\nTEST(fbvector, emplace) {\n  fbvector<std::string> s(12, \"asd\");\n  EXPECT_EQ(s.size(), 12);\n  EXPECT_EQ(s.front(), \"asd\");\n  const auto& emplaced = s.emplace_back(\"funk\");\n  EXPECT_EQ(emplaced, \"funk\");\n  EXPECT_EQ(s.back(), \"funk\");\n  EXPECT_EQ(std::addressof(emplaced), std::addressof(s.back()));\n}\n\nTEST(fbvector, initializerLists) {\n  fbvector<int> vec = {1, 2, 3};\n  EXPECT_EQ(vec.size(), 3);\n  EXPECT_EQ(vec[0], 1);\n  EXPECT_EQ(vec[1], 2);\n  EXPECT_EQ(vec[2], 3);\n\n  vec = {0, 0, 12, 16};\n  EXPECT_EQ(vec.size(), 4);\n  EXPECT_EQ(vec[0], 0);\n  EXPECT_EQ(vec[1], 0);\n  EXPECT_EQ(vec[2], 12);\n  EXPECT_EQ(vec[3], 16);\n\n  vec.insert(vec.begin() + 1, {23, 23});\n  EXPECT_EQ(vec.size(), 6);\n  EXPECT_EQ(vec[0], 0);\n  EXPECT_EQ(vec[1], 23);\n  EXPECT_EQ(vec[2], 23);\n  EXPECT_EQ(vec[3], 0);\n  EXPECT_EQ(vec[4], 12);\n  EXPECT_EQ(vec[5], 16);\n}\n\nTEST(fbvector, uniquePtr) {\n  fbvector<std::unique_ptr<int>> v(12);\n  std::unique_ptr<int> p(new int(12));\n  v.push_back(std::move(p));\n  EXPECT_EQ(*v.back(), 12);\n\n  v[0] = std::move(p);\n  EXPECT_FALSE(v[0].get());\n  v[0] = std::make_unique<int>(32);\n  std::unique_ptr<int> somePtr;\n  v.insert(v.begin(), std::move(somePtr));\n  EXPECT_EQ(*v[1], 32);\n}\n\nTEST(FBVector, task858056) {\n  fbvector<fbstring> cycle;\n  cycle.push_back(\"foo\");\n  cycle.push_back(\"bar\");\n  cycle.push_back(\"baz\");\n  fbstring message(\"Cycle detected: \");\n  FOR_EACH_R (node_name, cycle) {\n    message += \"[\";\n    message += *node_name;\n    message += \"] \";\n  }\n  EXPECT_EQ(\"Cycle detected: [baz] [bar] [foo] \", message);\n}\n\nTEST(FBVector, moveIterator) {\n  fbvector<int> base = {0, 1, 2};\n\n  auto cp1 = base;\n  fbvector<int> fbvi1(\n      std::make_move_iterator(cp1.begin()), std::make_move_iterator(cp1.end()));\n  EXPECT_EQ(fbvi1, base);\n\n  auto cp2 = base;\n  fbvector<int> fbvi2;\n  fbvi2.assign(\n      std::make_move_iterator(cp2.begin()), std::make_move_iterator(cp2.end()));\n  EXPECT_EQ(fbvi2, base);\n\n  auto cp3 = base;\n  fbvector<int> fbvi3;\n  fbvi3.insert(\n      fbvi3.end(),\n      std::make_move_iterator(cp3.begin()),\n      std::make_move_iterator(cp3.end()));\n  EXPECT_EQ(fbvi3, base);\n}\n\nTEST(FBVector, reserveConsistency) {\n  struct S {\n    int64_t a, b, c, d;\n  };\n\n  fbvector<S> fb1;\n  for (size_t i = 0; i < 1000; ++i) {\n    fb1.reserve(1);\n    EXPECT_EQ(fb1.size(), 0);\n    fb1.shrink_to_fit();\n  }\n}\n\nTEST(FBVector, vectorOfMaps) {\n  fbvector<std::map<std::string, std::string>> v;\n\n  v.push_back(std::map<std::string, std::string>());\n  v.push_back(std::map<std::string, std::string>());\n\n  EXPECT_EQ(2, v.size());\n\n  v[1][\"hello\"] = \"world\";\n  EXPECT_EQ(0, v[0].size());\n  EXPECT_EQ(1, v[1].size());\n\n  v[0][\"foo\"] = \"bar\";\n  EXPECT_EQ(1, v[0].size());\n  EXPECT_EQ(1, v[1].size());\n}\n\nTEST(FBVector, shrinkToFitAfterClear) {\n  fbvector<int> fb1;\n  fb1.push_back(42);\n  fb1.push_back(1337);\n  fb1.clear();\n  fb1.shrink_to_fit();\n  EXPECT_EQ(fb1.size(), 0);\n  EXPECT_EQ(fb1.capacity(), 0);\n}\n\nTEST(FBVector, zeroLen) {\n  fbvector<int> fb1(0);\n  fbvector<int> fb2(0, 10);\n  fbvector<int> fb3(std::move(fb1));\n  fbvector<int> fb4;\n  fb4 = std::move(fb2);\n  fbvector<int> fb5 = fb3;\n  fbvector<int> fb6;\n  fb6 = fb4;\n  std::initializer_list<int> il = {};\n  fb6 = il;\n  fbvector<int> fb7(fb6.begin(), fb6.end());\n}\n\nTEST(FBVector, deductionGuides) {\n  fbvector<int> v(3);\n\n  fbvector x(v.begin(), v.end());\n  EXPECT_TRUE((std::is_same_v<fbvector<int>, decltype(x)>));\n\n  fbvector y{v.begin(), v.end()};\n  EXPECT_TRUE((std::is_same_v<fbvector<fbvector<int>::iterator>, decltype(y)>));\n}\n\nTEST(FBVector, erase) {\n  fbvector<int> v(3);\n  std::iota(v.begin(), v.end(), 1);\n  v.push_back(2);\n  erase(v, 2);\n  ASSERT_EQ(2u, v.size());\n  EXPECT_EQ(1u, v[0]);\n  EXPECT_EQ(3u, v[1]);\n}\n\nTEST(FBVector, eraseIf) {\n  fbvector<int> v(6);\n  std::iota(v.begin(), v.end(), 1);\n  erase_if(v, [](const auto& x) { return x % 2 == 0; });\n  ASSERT_EQ(3u, v.size());\n  EXPECT_EQ(1u, v[0]);\n  EXPECT_EQ(3u, v[1]);\n  EXPECT_EQ(5u, v[2]);\n}\n\nTEST(FBVector, overflowConstruct) {\n  EXPECT_THROW(\n      folly::fbvector<std::string>(SIZE_MAX / sizeof(std::string) + 1),\n      std::length_error);\n}\n\nTEST(FBVector, overflowResize) {\n  folly::fbvector<std::string> vec;\n  EXPECT_THROW(vec.resize(SIZE_MAX / sizeof(string) + 1), std::length_error);\n}\n\nTEST(FBVector, overflowAssign) {\n  folly::fbvector<std::string> vec;\n  EXPECT_THROW(\n      vec.assign(SIZE_MAX / sizeof(std::string) + 1, \"hello\"),\n      std::length_error);\n}\n\n#ifndef _MSC_VER\nTEST(FBVector, zeroInit) {\n  // This is a higher-level version of TEST(Traits, zeroInit).\n  struct S1 {\n    int i_;\n  };\n  struct S3 {\n    int S1::* mp_;\n  };\n  folly::fbvector<S3> vec(4);\n  vec.resize(10);\n  EXPECT_EQ(vec[0].mp_, nullptr);\n  EXPECT_EQ(vec[8].mp_, nullptr);\n}\n#endif\n"
  },
  {
    "path": "folly/container/test/FBVectorTests.cpp.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * This file is supposed to be included from within\n * FBVectorTest. Do not use otherwise.\n */\n\nTESTFUN(clause_23_3_6_1_1) {\n  VECTOR v;\n  EXPECT_TRUE(v.empty());\n  VECTOR::allocator_type a;\n  VECTOR v1(a);\n  EXPECT_TRUE(v1.empty());\n}\n\nTESTFUN(clause_23_3_6_1_3) {\n  auto const n = random(0U, 10000U);\n  VECTOR v(n);\n  EXPECT_EQ(v.size(), n);\n  FOR_EACH (i, v) {\n    EXPECT_EQ(*i, VECTOR::value_type());\n  }\n}\n\nTESTFUN(clause_23_3_6_1_9) {\n  // Insert with iterators\n  list<VECTOR::value_type> lst;\n  auto const n = random(0U, 10000U);\n  FOR_EACH_RANGE (i, 0, n) {\n    lst.push_back(randomObject<VECTOR::value_type>());\n  }\n  VECTOR v(lst.begin(), lst.end());\n  EXPECT_EQ(v.size(), lst.size());\n  size_t j = 0;\n  FOR_EACH (i, lst) {\n    EXPECT_EQ(v[j], *i);\n    j++;\n  }\n}\n\nTESTFUN(clause_23_3_6_1_11) {\n  // assign with iterators\n  list<VECTOR::value_type> lst;\n  auto const n = random(0U, 10000U);\n  FOR_EACH_RANGE (i, 0, n) {\n    lst.push_back(randomObject<VECTOR::value_type>());\n  }\n  VECTOR v;\n  v.assign(lst.begin(), lst.end());\n  EXPECT_EQ(v.size(), lst.size());\n  size_t j = 0;\n  FOR_EACH (i, lst) {\n    EXPECT_EQ(v[j], *i);\n    j++;\n  }\n\n  // aliased assign\n  v.assign(v.begin(), v.begin() + v.size() / 2);\n  EXPECT_EQ(v.size(), lst.size() / 2);\n  j = 0;\n  FOR_EACH (i, lst) {\n    if (j == v.size()) {\n      break;\n    }\n    EXPECT_EQ(v[j], *i);\n    j++;\n  }\n}\n\nTESTFUN(clause_23_3_6_1_12) {\n  VECTOR v;\n  auto const n = random(0U, 10000U);\n  auto const obj = randomObject<VECTOR::value_type>();\n  v.assign(n, obj);\n  EXPECT_EQ(v.size(), n);\n  FOR_EACH (i, v) {\n    EXPECT_EQ(*i, obj);\n  }\n}\n\nTESTFUN(clause_23_3_6_2_1) {\n  VECTOR v;\n  auto const n = random(0U, 10000U);\n  v.reserve(n);\n  EXPECT_GE(v.capacity(), n);\n}\n\nTESTFUN(clause_23_3_6_2_7) {\n  auto const n1 = random(0U, 10000U);\n  auto const n2 = random(0U, 10000U);\n  auto const obj1 = randomObject<VECTOR::value_type>();\n  auto const obj2 = randomObject<VECTOR::value_type>();\n  VECTOR v1(n1, obj1), v2(n2, obj2);\n  v1.swap(v2);\n  EXPECT_EQ(v1.size(), n2);\n  EXPECT_EQ(v2.size(), n1);\n  FOR_EACH (i, v1) {\n    EXPECT_EQ(*i, obj2);\n  }\n  FOR_EACH (i, v2) {\n    EXPECT_EQ(*i, obj1);\n  }\n}\n\nTESTFUN(clause_23_3_6_2_9) {\n  VECTOR v;\n  auto const n1 = random(0U, 10000U);\n  v.resize(n1);\n  FOR_EACH (i, v) {\n    EXPECT_EQ(*i, VECTOR::value_type());\n  }\n  FOR_EACH (i, v) {\n    EXPECT_EQ(*i, VECTOR::value_type());\n  }\n}\n\nTESTFUN(clause_23_3_6_2_11) {\n  VECTOR v;\n  auto const n1 = random(0U, 10000U);\n  auto const obj1 = randomObject<VECTOR::value_type>();\n  v.resize(n1, obj1);\n  FOR_EACH (i, v) {\n    EXPECT_EQ(*i, obj1);\n  }\n  auto const n2 = random(0U, 10000U);\n  auto const obj2 = randomObject<VECTOR::value_type>();\n  v.resize(n2, obj2);\n  if (n1 < n2) {\n    FOR_EACH_RANGE (i, n1, n2) {\n      EXPECT_EQ(v[i], obj2);\n    }\n  }\n}\n\nTESTFUN(clause_absent_element_access) {\n  VECTOR v;\n  auto const n1 = random(1U, 10000U);\n  auto const obj1 = randomObject<VECTOR::value_type>();\n  v.resize(n1, obj1);\n  auto const n = random(0U, v.size() - 1);\n  EXPECT_EQ(v[n], v.at(n));\n  auto const obj2 = randomObject<VECTOR::value_type>();\n  v[n] = obj2;\n  EXPECT_EQ(v[n], v.at(n));\n  EXPECT_EQ(v[n], obj2);\n  auto const obj3 = randomObject<VECTOR::value_type>();\n  v.at(n) = obj3;\n  EXPECT_EQ(v[n], v.at(n));\n  EXPECT_EQ(v[n], obj3);\n}\n\nTESTFUN(clause_23_3_6_3_1) {\n  VECTOR v;\n  auto const n1 = random(1U, 10000U);\n  auto const obj1 = randomObject<VECTOR::value_type>();\n  v.resize(n1, obj1);\n  EXPECT_EQ(v.data(), &v.front());\n}\n\nTESTFUN(clause_23_3_6_4_1_a) {\n  VECTOR v, w;\n  auto const n1 = random(1U, 10000U);\n  FOR_EACH_RANGE (i, 0, n1) {\n    auto const obj1 = randomObject<VECTOR::value_type>();\n    v.push_back(obj1);\n    w.push_back(obj1);\n  }\n  auto const n2 = random(0U, n1 - 1);\n  auto pos = v.begin() + n2;\n  auto const obj2 = randomObject<VECTOR::value_type>();\n\n  auto r = v.insert(pos, obj2);\n\n  EXPECT_EQ(v.size(), w.size() + 1);\n  EXPECT_EQ(r - v.begin(), n2);\n  EXPECT_EQ(*r, obj2);\n  FOR_EACH_RANGE (i, 0, r - v.begin()) {\n    EXPECT_EQ(v[i], w[i]);\n  }\n  FOR_EACH_RANGE (i, r - v.begin() + 1, v.size()) {\n    EXPECT_EQ(v[i], w[i - 1]);\n  }\n}\n\nTESTFUN(clause_23_3_6_4_1_c) {\n  // This test only works for fbvector\n  fbvector<VECTOR::value_type> v, w;\n  auto const n1 = random(1U, 10000U);\n  FOR_EACH_RANGE (i, 0, n1) {\n    auto const obj1 = randomObject<VECTOR::value_type>();\n    v.push_back(obj1);\n    w.push_back(obj1);\n  }\n  auto const n2 = random(0U, n1 - 1);\n  auto pos = v.begin() + n2;\n  auto const obj2 = randomObject<VECTOR::value_type>();\n  auto const n3 = random(0U, 10000U);\n\n  auto r = v.insert(pos, n3, obj2);\n\n  EXPECT_EQ(v.size(), w.size() + n3);\n  EXPECT_EQ(r - v.begin(), n2);\n  FOR_EACH_RANGE (i, 0, r - v.begin()) {\n    EXPECT_EQ(v[i], w[i]);\n  }\n  FOR_EACH_RANGE (i, r - v.begin(), r - v.begin() + n3) {\n    EXPECT_EQ(v[i], obj2);\n  }\n  FOR_EACH_RANGE (i, r - v.begin() + n3, v.size()) {\n    EXPECT_EQ(v[i], w[i - n3]);\n  }\n}\n\nTESTFUN(clause_23_3_6_4_1_d) {\n  VECTOR v, w;\n  auto const n1 = random(0U, 10000U);\n  FOR_EACH_RANGE (i, 0, n1) {\n    auto const obj1 = randomObject<VECTOR::value_type>();\n    v.push_back(obj1);\n    w.push_back(obj1);\n  }\n  EXPECT_EQ(v.size(), n1);\n\n  auto const obj2 = randomObject<VECTOR::value_type>();\n  v.push_back(obj2);\n  EXPECT_EQ(v.back(), obj2);\n  EXPECT_EQ(v.size(), w.size() + 1);\n\n  FOR_EACH_RANGE (i, 0, w.size()) {\n    EXPECT_EQ(v[i], w[i]);\n  }\n}\n\nTESTFUN(clause_23_3_6_4_3) {\n  VECTOR v, w;\n  auto const n1 = random(1U, 10000U);\n  FOR_EACH_RANGE (i, 0, n1) {\n    auto const obj1 = randomObject<VECTOR::value_type>();\n    v.push_back(obj1);\n    w.push_back(obj1);\n  }\n  EXPECT_EQ(v.size(), n1);\n\n  auto const n2 = random(0U, n1 - 1);\n  auto it = v.erase(v.begin() + n2);\n  EXPECT_EQ(v.size() + 1, w.size());\n\n  FOR_EACH_RANGE (i, 0, it - v.begin()) {\n    EXPECT_EQ(v[i], w[i]);\n  }\n\n  FOR_EACH_RANGE (i, it - v.begin(), v.size()) {\n    EXPECT_EQ(v[i], w[i + 1]);\n  }\n}\n\nTESTFUN(clause_23_3_6_4_4) {\n  VECTOR v, w;\n  auto const n1 = random(1U, 10000U);\n  FOR_EACH_RANGE (i, 0, n1) {\n    auto const obj1 = randomObject<VECTOR::value_type>();\n    v.push_back(obj1);\n    w.push_back(obj1);\n  }\n  EXPECT_EQ(v.size(), n1);\n\n  auto const n2 = random(0U, n1 - 1);\n  auto const n3 = random(n2, n1 - 1);\n  auto it = v.erase(v.begin() + n2, v.begin() + n3);\n  EXPECT_EQ(v.size() + (n3 - n2), w.size());\n\n  FOR_EACH_RANGE (i, 0, it - v.begin()) {\n    EXPECT_EQ(v[i], w[i]);\n  }\n\n  FOR_EACH_RANGE (i, it - v.begin(), v.size()) {\n    EXPECT_EQ(v[i], w[i + (n3 - n2)]);\n  }\n}\n\nTESTFUN(clause_23_3_6_4_clear) {\n  VECTOR v;\n  v.clear();\n  EXPECT_TRUE(v.empty());\n  v.resize(random(0U, 10000U));\n  auto c = v.capacity();\n  v.clear();\n  EXPECT_TRUE(v.empty());\n  EXPECT_EQ(v.capacity(), c);\n}\n"
  },
  {
    "path": "folly/container/test/ForeachBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <map>\n\n#include <folly/Benchmark.h>\n#include <folly/Random.h>\n#include <folly/container/Enumerate.h>\n#include <folly/container/Foreach.h>\n#include <folly/init/Init.h>\n\nusing namespace folly;\n\n// Benchmarks:\n// 1. Benchmark iterating through the man with FOR_EACH, and also assign\n//    iter->first and iter->second to local vars inside the FOR_EACH loop.\n// 2. Benchmark iterating through the man with FOR_EACH, but use iter->first and\n//    iter->second as is, without assigning to local variables.\n\n// For use in benchmarks below.\nstd::map<int, std::string> bmMap;\nstd::vector<int> vec_one;\nstd::vector<int> vec_two;\n\n// Smallest type to isolate iteration overhead.\nstd::vector<char> vec_char;\n\nvoid setupBenchmark(size_t iters) {\n  bmMap.clear();\n  for (size_t i = 0; i < iters; ++i) {\n    bmMap[i] = \"teststring\";\n  }\n\n  vec_one.clear();\n  vec_two.clear();\n  vec_one.resize(iters);\n  vec_two.resize(iters);\n}\n\nvoid setupCharVecBenchmark(size_t iters) {\n  vec_char.resize(iters);\n  std::generate(vec_char.begin(), vec_char.end(), [] {\n    return Random::rand32(128);\n  });\n}\n\nBENCHMARK(ForEachFunctionNoAssign, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    folly::for_each(bmMap, [&](auto& key_val_pair) {\n      sumKeys += key_val_pair.first;\n      sumValues += key_val_pair.second;\n    });\n    doNotOptimizeAway(sumKeys);\n  });\n}\n\nBENCHMARK(StdForEachFunctionNoAssign, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {\n      sumKeys += key_val_pair.first;\n      sumValues += key_val_pair.second;\n    });\n    doNotOptimizeAway(sumKeys);\n  });\n}\n\nBENCHMARK(RangeBasedForLoopNoAssign, iters) {\n  BenchmarkSuspender suspender;\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    for (auto& key_val_pair : bmMap) {\n      sumKeys += key_val_pair.first;\n      sumValues += key_val_pair.second;\n    }\n    doNotOptimizeAway(sumKeys);\n  });\n}\n\nBENCHMARK(ManualLoopNoAssign, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {\n      sumKeys += iter->first;\n      sumValues += iter->second;\n    }\n    doNotOptimizeAway(sumKeys);\n  });\n}\n\nBENCHMARK(ForEachFunctionAssign, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    folly::for_each(bmMap, [&](auto& key_val_pair) {\n      const int k = key_val_pair.first;\n      const std::string v = key_val_pair.second;\n      sumKeys += k;\n      sumValues += v;\n    });\n  });\n}\n\nBENCHMARK(StdForEachFunctionAssign, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {\n      const int k = key_val_pair.first;\n      const std::string v = key_val_pair.second;\n      sumKeys += k;\n      sumValues += v;\n    });\n  });\n}\n\nBENCHMARK(RangeBasedForLoopAssign, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    for (auto& key_val_pair : bmMap) {\n      const int k = key_val_pair.first;\n      const std::string v = key_val_pair.second;\n      sumKeys += k;\n      sumValues += v;\n    }\n  });\n}\n\nBENCHMARK(ManualLoopAssign, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {\n      const int k = iter->first;\n      const std::string v = iter->second;\n      sumKeys += k;\n      sumValues += v;\n    }\n  });\n}\n\nBENCHMARK(ForEachFunctionNoAssignWithIndexManipulation, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {\n      sumKeys += key_val_pair.first;\n      sumValues += key_val_pair.second;\n      sumValues += index;\n    });\n  });\n}\n\nBENCHMARK(StdForEachFunctionNoAssignWithIndexManipulation, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    auto index = std::size_t{0};\n    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {\n      sumKeys += key_val_pair.first;\n      sumValues += key_val_pair.second;\n      sumValues += index;\n      ++index;\n    });\n  });\n}\n\nBENCHMARK(RangeBasedForLoopNoAssignWithIndexManipulation, iters) {\n  BenchmarkSuspender suspender;\n\n  int sumKeys = 0;\n  std::string sumValues;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    auto index = std::size_t{0};\n    for (auto& key_val_pair : bmMap) {\n      sumKeys += key_val_pair.first;\n      sumValues += key_val_pair.second;\n      sumValues += index;\n    }\n  });\n}\n\nBENCHMARK(ForEachFunctionFetch, iters) {\n  BenchmarkSuspender suspender;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {\n      folly::fetch(vec_one, index) = key_val_pair.first;\n    });\n  });\n}\n\nBENCHMARK(StdForEachFunctionFetch, iters) {\n  BenchmarkSuspender suspender;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    auto index = std::size_t{0};\n    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {\n      *(vec_one.begin() + index++) = key_val_pair.first;\n    });\n  });\n}\n\nBENCHMARK(ForLoopFetch, iters) {\n  BenchmarkSuspender suspender;\n  setupBenchmark(iters);\n\n  suspender.dismissing([&]() {\n    auto index = std::size_t{0};\n    for (auto& key_val_pair : bmMap) {\n      *(vec_one.begin() + index++) = key_val_pair.first;\n    }\n  });\n}\n\nBENCHMARK(ForEachKVNoMacroAssign, iters) {\n  [[maybe_unused]] int sumKeys = 0;\n  std::string sumValues;\n\n  BENCHMARK_SUSPEND {\n    setupBenchmark(iters);\n  }\n\n  FOR_EACH (iter, bmMap) {\n    const int k = iter->first;\n    const std::string v = iter->second;\n    sumKeys += k;\n    sumValues += v;\n  }\n}\n\nBENCHMARK(ForEachKVNoMacroNoAssign, iters) {\n  [[maybe_unused]] int sumKeys = 0;\n  std::string sumValues;\n\n  BENCHMARK_SUSPEND {\n    setupBenchmark(iters);\n  }\n\n  FOR_EACH (iter, bmMap) {\n    sumKeys += iter->first;\n    sumValues += iter->second;\n  }\n}\n\nBENCHMARK(ForEachManual, iters) {\n  int sum = 1;\n  for (size_t i = 1; i < iters; ++i) {\n    sum *= i;\n  }\n  doNotOptimizeAway(sum);\n}\n\nBENCHMARK(ForEachRange, iters) {\n  int sum = 1;\n  FOR_EACH_RANGE (i, 1, iters) {\n    sum *= i;\n  }\n  doNotOptimizeAway(sum);\n}\n\nBENCHMARK(ForEachDescendingManual, iters) {\n  int sum = 1;\n  for (size_t i = iters; i-- > 1;) {\n    sum *= i;\n  }\n  doNotOptimizeAway(sum);\n}\n\nBENCHMARK(CharVecForRange, iters) {\n  BENCHMARK_SUSPEND {\n    setupCharVecBenchmark(iters);\n  }\n  size_t sum = 0;\n  for (auto& c : vec_char) {\n    sum += c;\n  }\n  doNotOptimizeAway(sum);\n}\n\nBENCHMARK(CharVecForRangeExplicitIndex, iters) {\n  BENCHMARK_SUSPEND {\n    setupCharVecBenchmark(iters);\n  }\n  size_t sum = 0;\n  size_t index = 0;\n  for (auto& c : vec_char) {\n    sum += c * index;\n    ++index;\n  }\n  doNotOptimizeAway(sum);\n}\n\nBENCHMARK(CharVecForEach, iters) {\n  BENCHMARK_SUSPEND {\n    setupCharVecBenchmark(iters);\n  }\n  size_t sum = 0;\n  folly::for_each(vec_char, [&](auto& c) { sum += c; });\n  doNotOptimizeAway(sum);\n}\n\nBENCHMARK(CharVecForEachIndex, iters) {\n  BENCHMARK_SUSPEND {\n    setupCharVecBenchmark(iters);\n  }\n  size_t sum = 0;\n  folly::for_each(vec_char, [&](auto& c, auto index) { sum += c * index; });\n  doNotOptimizeAway(sum);\n}\n\nBENCHMARK(CharVecForRangeEnumerate, iters) {\n  BENCHMARK_SUSPEND {\n    setupCharVecBenchmark(iters);\n  }\n  size_t sum = 0;\n  for (auto&& it : enumerate(vec_char)) {\n    sum += *it * it.index;\n  }\n  doNotOptimizeAway(sum);\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/container/test/ForeachTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/Foreach.h>\n\n#include <array>\n#include <initializer_list>\n#include <iterator>\n#include <list>\n#include <map>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include <folly/Traits.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nnamespace folly {\nnamespace test {\n\nclass TestRValueConstruct {\n public:\n  TestRValueConstruct() = default;\n  TestRValueConstruct(TestRValueConstruct&&) noexcept {\n    this->constructed_from_rvalue = true;\n  }\n  TestRValueConstruct(const TestRValueConstruct&) {\n    this->constructed_from_rvalue = false;\n  }\n  TestRValueConstruct& operator=(const TestRValueConstruct&) = delete;\n  TestRValueConstruct& operator=(TestRValueConstruct&&) = delete;\n\n  bool constructed_from_rvalue{false};\n};\n\nclass TestAdlIterable {\n public:\n  std::vector<int> vec{0, 1, 2, 3};\n};\n\nauto begin(TestAdlIterable& instance) {\n  return instance.vec.begin();\n}\nauto begin(const TestAdlIterable& instance) {\n  return instance.vec.begin();\n}\nauto end(TestAdlIterable& instance) {\n  return instance.vec.end();\n}\nauto end(const TestAdlIterable& instance) {\n  return instance.vec.end();\n}\n\nclass TestBothIndexingAndIter {\n public:\n  class Iterator {\n   public:\n    using difference_type = std::size_t;\n    using value_type = int;\n    using pointer = int*;\n    using reference = int&;\n    using iterator_category = std::random_access_iterator_tag;\n    int& operator*() { return this->val; }\n    Iterator operator+(int) { return *this; }\n    explicit Iterator(int& val_in) : val{val_in} {}\n    int& val;\n  };\n  auto begin() {\n    this->called_begin = true;\n    return Iterator{val};\n  }\n  auto end() { return Iterator{val}; }\n  int& operator[](int) { return this->val; }\n\n  int val{0};\n  bool called_begin = false;\n};\n} // namespace test\n} // namespace folly\n\nTEST(Foreach, ForEachFunctionBasic) {\n  auto range = std::make_tuple(1, 2, 3);\n  auto result_range = std::vector<int>{};\n  auto correct_result_range = std::vector<int>{1, 2, 3};\n\n  folly::for_each(range, [&](auto ele) { result_range.push_back(ele); });\n\n  EXPECT_TRUE(\n      std::equal(\n          result_range.begin(),\n          result_range.end(),\n          correct_result_range.begin()));\n}\n\nTEST(Foreach, ForEachFunctionBasicRuntimeOneArg) {\n  auto range = std::vector<int>{1, 2, 3};\n  auto current = 0;\n  folly::for_each(range, [&](auto ele) {\n    if (current == 0) {\n      EXPECT_EQ(ele, 1);\n    } else if (current == 1) {\n      EXPECT_EQ(ele, 2);\n    } else {\n      EXPECT_EQ(ele, 3);\n    }\n    ++current;\n  });\n}\n\nTEST(Foreach, ForEachFunctionBasicRuntimeTwoArg) {\n  auto range = std::vector<int>{1, 2, 3};\n  folly::for_each(range, [](auto ele, auto index) {\n    EXPECT_TRUE(index < 3);\n    if (index == 0) {\n      EXPECT_EQ(ele, 1);\n    } else if (index == 1) {\n      EXPECT_EQ(ele, 2);\n    } else if (index == 2) {\n      EXPECT_EQ(ele, 3);\n    }\n  });\n}\n\nTEST(Foreach, ForEachFunctionBasicRuntimeThreeArg) {\n  auto range = std::list<int>{1, 2, 3};\n  auto result_range = std::list<int>{1, 3};\n  folly::for_each(range, [&](auto ele, auto, auto iter) {\n    if (ele == 2) {\n      range.erase(iter);\n    }\n  });\n  EXPECT_TRUE(std::equal(range.begin(), range.end(), result_range.begin()));\n}\n\nTEST(Foreach, ForEachFunctionBasicTupleOneArg) {\n  auto range = std::make_tuple(1, 2, 3);\n  auto current = 0;\n  folly::for_each(range, [&](auto ele) {\n    if (current == 0) {\n      EXPECT_EQ(ele, 1);\n    } else if (current == 1) {\n      EXPECT_EQ(ele, 2);\n    } else {\n      EXPECT_EQ(ele, 3);\n    }\n    ++current;\n  });\n}\n\nTEST(Foreach, ForEachFunctionBasicTupleTwoArg) {\n  auto range = std::make_tuple(1, 2, 3);\n  folly::for_each(range, [](auto ele, auto index) {\n    EXPECT_TRUE(index < 3);\n    if (index == 0) {\n      EXPECT_EQ(ele, 1);\n    } else if (index == 1) {\n      EXPECT_EQ(ele, 2);\n    } else if (index == 2) {\n      EXPECT_EQ(ele, 3);\n    }\n  });\n}\n\nTEST(Foreach, ForEachFunctionBreakRuntimeOneArg) {\n  auto range = std::vector<int>{1, 2, 3};\n  auto iterations = 0;\n  folly::for_each(range, [&](auto) {\n    ++iterations;\n    if (iterations == 1) {\n      return folly::loop_break;\n    }\n    return folly::loop_continue;\n  });\n  EXPECT_EQ(iterations, 1);\n}\n\nTEST(Foreach, ForEachFunctionBreakRuntimeTwoArg) {\n  auto range = std::vector<int>{1, 2, 3};\n  auto iterations = 0;\n  folly::for_each(range, [&](auto, auto index) {\n    ++iterations;\n    if (index == 1) {\n      return folly::loop_break;\n    }\n    return folly::loop_continue;\n  });\n  EXPECT_EQ(iterations, 2);\n}\n\nTEST(Foreach, ForEachFunctionBreakRuntimeThreeArg) {\n  auto range = std::vector<int>{1, 2, 3};\n  auto iterations = 0;\n  folly::for_each(range, [&](auto, auto index, auto) {\n    ++iterations;\n    if (index == 1) {\n      return folly::loop_break;\n    }\n    return folly::loop_continue;\n  });\n  EXPECT_EQ(iterations, 2);\n}\n\nTEST(Foreach, ForEachFunctionBreakTupleOneArg) {\n  auto range = std::vector<int>{1, 2, 3};\n  auto iterations = 0;\n  folly::for_each(range, [&](auto) {\n    ++iterations;\n    if (iterations == 1) {\n      return folly::loop_break;\n    }\n    return folly::loop_continue;\n  });\n  EXPECT_EQ(iterations, 1);\n}\n\nTEST(Foreach, ForEachFunctionBreakTupleTwoArg) {\n  auto range = std::vector<int>{1, 2, 3};\n  auto iterations = 0;\n  folly::for_each(range, [&](auto, auto index) {\n    ++iterations;\n    if (index == 1) {\n      return folly::loop_break;\n    }\n    return folly::loop_continue;\n  });\n  EXPECT_EQ(iterations, 2);\n}\n\nTEST(Foreach, ForEachFunctionArray) {\n  auto range = std::array<int, 3>{{1, 2, 3}};\n  auto iterations = 0;\n  folly::for_each(range, [&](auto, auto index) {\n    ++iterations;\n    if (index == 1) {\n      return folly::loop_break;\n    }\n    return folly::loop_continue;\n  });\n  EXPECT_EQ(iterations, 2);\n}\n\nTEST(Foreach, ForEachFunctionInitializerListBasic) {\n  folly::for_each(std::initializer_list<int>{1, 2, 3}, [](auto ele) { ++ele; });\n}\n\nTEST(Foreach, ForEachFunctionTestForward) {\n  using folly::test::TestRValueConstruct;\n  auto range_one = std::vector<TestRValueConstruct>{};\n  range_one.resize(3);\n\n  folly::for_each(std::move(range_one), [](auto ele) {\n    EXPECT_FALSE(ele.constructed_from_rvalue);\n  });\n\n  folly::for_each(\n      std::make_tuple(TestRValueConstruct{}, TestRValueConstruct{}),\n      [](auto ele) { EXPECT_TRUE(ele.constructed_from_rvalue); });\n}\n\nTEST(Foreach, ForEachFunctionAdlIterable) {\n  auto range = test::TestAdlIterable{};\n  auto iterations = 0;\n  folly::for_each(range, [&](auto ele, auto index) {\n    ++iterations;\n    EXPECT_EQ(ele, index);\n  });\n  EXPECT_EQ(iterations, 4);\n}\n\nTEST(ForEach, FetchRandomAccessIterator) {\n  auto vec = std::vector<int>{1, 2, 3};\n  auto& second = folly::fetch(vec, 1);\n  EXPECT_EQ(second, 2);\n  second = 3;\n  EXPECT_EQ(second, 3);\n}\n\nTEST(ForEach, FetchIndexing) {\n  auto mp = std::map<int, int>{{1, 2}};\n  auto& ele = folly::fetch(mp, 1);\n  EXPECT_EQ(ele, 2);\n  ele = 3;\n  EXPECT_EQ(ele, 3);\n}\n\nTEST(ForEach, FetchTuple) {\n  auto mp = std::make_tuple(1, 2, 3);\n  auto& ele = folly::fetch(mp, std::integral_constant<int, 1>{});\n  EXPECT_EQ(ele, 2);\n  ele = 3;\n  EXPECT_EQ(ele, 3);\n}\n\nTEST(ForEach, FetchTestPreferIterator) {\n  auto range = test::TestBothIndexingAndIter{};\n  auto& ele = folly::fetch(range, 0);\n  EXPECT_TRUE(range.called_begin);\n  EXPECT_EQ(ele, 0);\n  ele = 2;\n  EXPECT_EQ(folly::fetch(range, 0), 2);\n}\n\ntemplate <typename...>\nstruct LargeTuple {};\ntemplate <size_t I, typename... T>\nauto& get(LargeTuple<T...>&) {\n  using Elem = folly::type_pack_element_t<I, T...>;\n  static Elem elem;\n  return elem;\n}\nnamespace std {\ntemplate <typename... T>\nstruct tuple_size<LargeTuple<T...>>\n    : std::integral_constant<size_t, sizeof...(T)> {};\n} // namespace std\n\ntemplate <typename>\nstruct LargeTupleTestHelper;\ntemplate <size_t... I>\nstruct LargeTupleTestHelper<std::index_sequence<I...>> {\n  template <size_t J>\n  using type_ = int;\n  using type = LargeTuple<type_<I>...>;\n};\n\nTEST(ForEach, LargeTuple) {\n  using tup = LargeTupleTestHelper<std::make_index_sequence<1024>>::type;\n  tup arr{};\n  int s = 0;\n  folly::for_each(arr, [&](auto i) { s += i; });\n  EXPECT_EQ(0, s);\n}\n\nTEST(Foreach, ForEachRvalue) {\n  const char* const hello = \"hello\";\n  int n = 0;\n  FOR_EACH (it, std::string(hello)) {\n    ++n;\n  }\n  EXPECT_EQ(strlen(hello), n);\n  FOR_EACH_R (it, std::string(hello)) {\n    --n;\n    EXPECT_EQ(hello[n], *it);\n  }\n  EXPECT_EQ(0, n);\n}\n\nTEST(Foreach, ForEachNested) {\n  const std::string hello = \"hello\";\n  size_t n = 0;\n  FOR_EACH (i, hello) {\n    FOR_EACH (j, hello) {\n      ++n;\n    }\n  }\n  auto len = hello.size();\n  EXPECT_EQ(len * len, n);\n}\n"
  },
  {
    "path": "folly/container/test/HashMapsBench.cpp",
    "content": "#include <fmt/format.h>\n\n/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstddef>\n#include <functional>\n#include <map>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include <glog/logging.h>\n#include <folly/Benchmark.h>\n#include <folly/Conv.h>\n#include <folly/Format.h>\n#include <folly/Function.h>\n#include <folly/Random.h>\n#include <folly/hash/Hash.h>\n#include <folly/init/Init.h>\n#include <folly/portability/GFlags.h>\n\n#include <folly/container/F14Map.h>\n\nusing namespace folly;\n\n// Depending the max load factor and the rehashing policy, the map size could\n// affect benchmark results quite a bit. For example, a map that's just rehashed\n// could have a lower probability of collisions, but the rehash time would get\n// counted toward the operation that triggered the rehash.\nDEFINE_int32(\n    map_size_min,\n    11,\n    \"min number of entries to benchmark each map with (inclusive)\");\nDEFINE_int32(\n    map_size_max,\n    100000,\n    \"max number of entries to benchmark each map with (inclusive)\");\nDEFINE_int32(\n    map_size_step, 32, \"multiplier for each benchmark between iterations\");\n\n//////// Key related preparation ////////\n\nnamespace {\nstatic const std::string kPadding(\"0123456789012345678901234567890123\");\nstruct NonSSOString : public std::string {\n  template <typename... Args>\n  explicit NonSSOString(Args&&... args)\n      : std::string(std::string(std::forward<Args>(args)...) + kPadding) {}\n\n  NonSSOString(NonSSOString const&) = default;\n  NonSSOString& operator=(NonSSOString const&) = default;\n  NonSSOString(NonSSOString&&) = default;\n  NonSSOString& operator=(NonSSOString&&) = default;\n};\n} // namespace\n\nnamespace std {\ntemplate <>\nstruct hash<NonSSOString> {\n  size_t operator()(const NonSSOString& s) const { return hash<string>()(s); }\n};\n\n#ifdef __GLIBCXX__\ntemplate <>\nstruct __is_fast_hash<hash<NonSSOString>> : public std::false_type {};\n\nstatic_assert(\n    __cache_default<string, hash<string>>::value ==\n        __cache_default<NonSSOString, hash<NonSSOString>>::value,\n    \"To draw a fair comparison, the policy of whether to cache hash codes \"\n    \"for NonSSOString keys should be the same as for std::string keys.\");\n#endif\n} // namespace std\n\nnamespace folly {\ntemplate <typename K>\nstruct IsAvalanchingHasher<std::hash<NonSSOString>, K> : std::true_type {};\n} // namespace folly\n\nconst int kSalt = 0x619abc7e;\n\ntemplate <typename K>\nK keyGen(uint32_t key) {\n  return folly::to<K>(hash::jenkins_rev_mix32(key ^ kSalt));\n}\n\ntemplate <>\nNonSSOString keyGen<NonSSOString>(uint32_t key) {\n  return NonSSOString(to<std::string>(key));\n}\n\ntemplate <class K>\nstd::vector<K>& keyList(int /*max*/ = 0) {\n  static std::vector<K> keys;\n  return keys;\n}\n\ntemplate <class K>\nvoid prepare(size_t max) {\n  auto& keys = keyList<K>();\n  if (keys.size() < max) {\n    for (auto key = keys.size(); key < max; ++key) {\n      keys.push_back(keyGen<K>(key));\n    }\n  }\n}\n\ntemplate <class K>\nconst K& key(int index) {\n  return keyList<K>()[index];\n}\n\ntemplate <typename TArray>\nTArray value(int i) {\n  // quick and dirty hack, since we don't test for correctness, ignore any\n  // narrowing that might happen.\n  return {static_cast<typename TArray::value_type>(i)};\n}\n\ntemplate <template <class, class> class Map, class K, class V, class Test>\nvoid benchmarkFilledMap(int runs, int size, const Test& test, int div = 1) {\n  BenchmarkSuspender braces;\n  Map<K, V> map(size);\n  prepare<K>(size);\n  for (int i = 0; i < size; i += div) {\n    auto& k = key<K>(i);\n    map.insert(std::pair<K, V>(k, value<V>(i * 3)));\n  }\n  folly::makeUnpredictable(map);\n  braces.dismissing([&] {\n    for (int r = 0; r < runs; ++r) {\n      test(map);\n    }\n  });\n}\n\nstatic auto& getRNG() {\n  static std::mt19937 rng{42};\n  return rng;\n}\n\ntemplate <template <class, class> class Map, class K, class V, class Test>\nvoid benchmarkManyFilledMapsByKey(\n    int runs, int size, const Test& test, int div = 1) {\n  BenchmarkSuspender braces;\n  Map<K, V> maps[64];\n  std::vector<K> toInsert;\n  prepare<K>(size);\n  for (int i = 0; i < size; ++i) {\n    auto& k = key<K>(i);\n    toInsert.push_back(k);\n  }\n  for (int i = 0; i * div < size; ++i) {\n    maps[i % 64].insert(std::pair<K, V>(toInsert[i], value<V>(i * 3)));\n  }\n  std::shuffle(toInsert.begin(), toInsert.end(), getRNG());\n  folly::makeUnpredictable(maps);\n  folly::makeUnpredictable(toInsert);\n  braces.dismissing([&] {\n    for (int r = 0; r < runs; ++r) {\n      for (int i = 0; i < size; i += div) {\n        test(maps[i % 64], toInsert[i]);\n      }\n    }\n  });\n}\n\ntemplate <template <class, class> class Map, class K, class V, class Test>\nvoid benchmarkFilledMapByKey(\n    int runs, int size, const Test& test, int div = 1) {\n  BenchmarkSuspender braces;\n  Map<K, V> map(size);\n  std::vector<K> toInsert;\n  prepare<K>(size);\n  for (int i = 0; i < size; ++i) {\n    auto& k = key<K>(i);\n    toInsert.push_back(k);\n  }\n  for (int i = 0; i * div < size; ++i) {\n    map.insert(std::pair<K, V>(toInsert[i], value<V>(i * 3)));\n  }\n  std::shuffle(toInsert.begin(), toInsert.end(), getRNG());\n  folly::makeUnpredictable(map);\n  folly::makeUnpredictable(toInsert);\n  braces.dismissing([&] {\n    for (int r = 0; r < runs; ++r) {\n      for (int i = 0; i < size; i += div) {\n        test(map, toInsert[i]);\n      }\n    }\n  });\n}\n\ntemplate <\n    template <class, class> class Map,\n    class K,\n    class V,\n    class... Args,\n    class Test>\nvoid benchmarkFromEmptyArgs(int runs, int size, Test test, Args... args) {\n  BenchmarkSuspender braces;\n  prepare<K>(size);\n  for (int r = 0; r < runs; ++r) {\n    Map<K, V> map{args...};\n    folly::makeUnpredictable(map);\n    braces.dismissing([&] {\n      for (int i = 0; i < size; ++i) {\n        test(map, i);\n      }\n    });\n  }\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkInsert(int runs, int size) {\n  benchmarkFromEmptyArgs<Map, K, V>(\n      runs,\n      size,\n      [](Map<K, V>& m, int i) {\n        m.insert(std::pair<K, V>(key<K>(i), value<V>(i)));\n      },\n      unsigned(size));\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkInsertGrow(int runs, int size) {\n  benchmarkFromEmptyArgs<Map, K, V>(runs, size, [](Map<K, V>& m, int i) {\n    m.insert(std::pair<K, V>(key<K>(i), value<V>(i)));\n  });\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkInsertSqBr(int runs, int size) {\n  benchmarkFromEmptyArgs<Map, K, V>(runs, size, [](Map<K, V>& m, int i) {\n    m[key<K>(i)] = value<V>(i);\n  });\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkFind(int runs, int size) {\n  int x = 0;\n  benchmarkFilledMapByKey<Map, K, V>(\n      runs,\n      size,\n      [&](Map<K, V>& m, const K& key) {\n        auto found = m.find(key);\n        if (found != m.end()) {\n          x ^= found->second[0];\n          folly::doNotOptimizeAway(x);\n        }\n      },\n      2);\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkManyFind(int runs, int size) {\n  int x = 0;\n  benchmarkManyFilledMapsByKey<Map, K, V>(\n      runs,\n      size,\n      [&](Map<K, V>& m, const K& key) {\n        auto found = m.find(key);\n        if (found != m.end()) {\n          x ^= found->second[0];\n          folly::doNotOptimizeAway(x);\n        }\n      },\n      2);\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkSqBrFind(int runs, int size) {\n  int x = 0;\n  benchmarkFilledMapByKey<Map, K, V>(\n      runs,\n      size,\n      [&](Map<K, V>& m, const K& key) {\n        x ^= m[key][0];\n        folly::doNotOptimizeAway(x);\n      },\n      2);\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkErase(int runs, int size) {\n  for (int i = 0; i < runs; ++i) {\n    benchmarkFilledMapByKey<Map, K, V>(\n        1, size, [&](Map<K, V>& m, const K& key) { m.erase(key); }, 2);\n  }\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkDtor(int runs, int size) {\n  for (int i = 0; i < runs; ++i) {\n    benchmarkFilledMap<Map, K, V>(\n        1,\n        size,\n        [&](Map<K, V>& m) {\n          Map<K, V> toDestruct;\n          swap(m, toDestruct);\n        },\n        2);\n  }\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkClear(int runs, int size) {\n  for (int i = 0; i < runs; ++i) {\n    benchmarkFilledMap<Map, K, V>(1, size, [&](Map<K, V>& m) { m.clear(); }, 1);\n  }\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkCopyCtor(int runs, int size) {\n  Map<K, V> n;\n  for (int i = 0; i < runs; ++i) {\n    benchmarkFilledMap<Map, K, V>(1, size, [&](Map<K, V>& m) { n = m; }, 1);\n    folly::doNotOptimizeAway(n);\n  }\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkIter(int runs, int size) {\n  int x = 0;\n  benchmarkFilledMap<Map, K, V>(runs, size, [&](Map<K, V>& m) {\n    for (auto& entry : m) {\n      x ^= entry.second[0];\n    }\n  });\n  folly::doNotOptimizeAway(x);\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkSparseIter(int runs, int size) {\n  int x = 0;\n  benchmarkFilledMap<Map, K, V>(\n      runs,\n      size,\n      [&](Map<K, V>& m) {\n        for (auto& entry : m) {\n          x ^= entry.second[0];\n        }\n      },\n      8);\n  folly::doNotOptimizeAway(x);\n}\n\ntemplate <template <class, class> class Map, class K, class V>\nvoid benchmarkCtorWithCapacity(int runs, int size) {\n  for (int ii = 0; ii < runs; ++ii) {\n    Map<K, V> map(size);\n    folly::doNotOptimizeAway(map);\n  }\n}\n\ntemplate <class K, class V>\nusing unord = std::unordered_map<K, V>;\ntemplate <class K, class V>\nusing f14val = F14ValueMap<K, V>;\ntemplate <class K, class V>\nusing f14node = F14NodeMap<K, V>;\ntemplate <class K, class V>\nusing f14vec = F14VectorMap<K, V>;\n\nvoid runAllHashMapTests() {\n  using std::map;\n  using std::string;\n  std::vector<string> testOrder;\n  map<size_t,\n      map<string,\n          map<string, map<string, map<string, std::function<int(int)>>>>>>\n      tests;\n\n#define Z(test, map, key, value_size)                                          \\\n  for (auto size = FLAGS_map_size_min; size <= FLAGS_map_size_max;             \\\n       size *= FLAGS_map_size_step) {                                          \\\n    auto value = fmt::format(\"a[{}]\", #value_size);                            \\\n    tests[size][#test][#key][value][#map] = [=](int iters) {                   \\\n      benchmark##test<map, key, std::array<uint8_t, value_size>>(iters, size); \\\n      return iters;                                                            \\\n    };                                                                         \\\n  }\n\n#define Y(test, map)             \\\n  Z(test, map, uint64_t, 1)      \\\n  Z(test, map, std::string, 1)   \\\n  Z(test, map, NonSSOString, 1)  \\\n  Z(test, map, uint64_t, 128)    \\\n  Z(test, map, std::string, 128) \\\n  Z(test, map, NonSSOString, 128)\n\n#define X(test)               \\\n  testOrder.push_back(#test); \\\n  Y(test, unord)              \\\n  Y(test, f14val)             \\\n  Y(test, f14node)            \\\n  Y(test, f14vec)\n\n  X(Insert);\n  X(InsertSqBr);\n  X(InsertGrow);\n  X(Find);\n  X(ManyFind);\n  X(SqBrFind);\n  X(Erase);\n  X(Iter);\n  X(SparseIter);\n  X(Clear);\n  X(Dtor);\n  X(CopyCtor);\n  X(CtorWithCapacity);\n\n#undef X\n#undef Y\n#undef Z\n  for (auto& size : tests) {\n    for (auto& test : testOrder) {\n      for (auto& key : size.second[test]) {\n        for (auto& value : key.second) {\n          bool isBaseline = true;\n          for (auto& map : value.second) {\n            addBenchmark(\n                __FILE__,\n                fmt::format(\n                    \"{}{} {:>8}<{}, {}>[{}]\",\n                    isBaseline ? \"\" : \"%\",\n                    test,\n                    map.first,\n                    key.first,\n                    value.first,\n                    size.first),\n                std::move(map.second));\n            isBaseline = false;\n          }\n          addBenchmark(__FILE__, \"-\", [](int iters) { return iters; });\n        }\n      }\n    }\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_max_iters\", \"100000\", folly::gflags::SET_FLAG_IF_DEFAULT);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_min_iters\", \"10000\", folly::gflags::SET_FLAG_IF_DEFAULT);\n  folly::gflags::SetCommandLineOptionWithMode(\n      \"bm_max_secs\", \"1\", folly::gflags::SET_FLAG_IF_DEFAULT);\n  LOG(INFO) << \"Preparing benchmark...\";\n  runAllHashMapTests();\n  LOG(INFO) << \"Running benchmark, which could take tens of minutes...\";\n  runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/container/test/HeterogeneousAccessTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/HeterogeneousAccess.h>\n\n#include <set>\n#include <string_view>\n#include <vector>\n\n#include <folly/FBString.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/Traits.h>\n#include <folly/portability/GTest.h>\n#include <folly/small_vector.h>\n\nusing namespace folly;\n\nnamespace {\n\ntemplate <typename T>\nvoid checkTransparent() {\n  static_assert(is_transparent_v<HeterogeneousAccessEqualTo<T>>);\n  static_assert(is_transparent_v<HeterogeneousAccessHash<T>>);\n}\n\ntemplate <typename T>\nvoid checkNotTransparent() {\n  static_assert(!is_transparent_v<HeterogeneousAccessEqualTo<T>>);\n  static_assert(!is_transparent_v<HeterogeneousAccessHash<T>>);\n}\n\nstruct StringVector {\n  std::vector<std::string> data_;\n\n  [[maybe_unused]] /* implicit */ operator Range<std::string const*>() const {\n    return {&data_[0], data_.size()};\n  }\n};\n\n} // namespace\n\nnamespace std {\ntemplate <>\nstruct hash<StringVector> {\n  [[maybe_unused]] std::size_t operator()(StringVector const& value) const {\n    return folly::hash::hash_range(value.data_.begin(), value.data_.end());\n  }\n};\n} // namespace std\n\nTEST(HeterogeneousAccess, transparentIsSelected) {\n  checkTransparent<std::string>();\n  checkTransparent<std::wstring>();\n  checkTransparent<std::u16string>();\n  checkTransparent<std::u32string>();\n\n  checkTransparent<std::string_view>();\n  checkTransparent<std::wstring_view>();\n  checkTransparent<std::u16string_view>();\n  checkTransparent<std::u32string_view>();\n\n  checkTransparent<fbstring>();\n\n  checkTransparent<StringPiece>();\n  checkTransparent<MutableStringPiece>();\n\n  checkTransparent<Range<char const*>>();\n  checkTransparent<Range<wchar_t const*>>();\n  checkTransparent<Range<char16_t const*>>();\n  checkTransparent<Range<char32_t const*>>();\n  checkTransparent<Range<int const*>>();\n\n  checkTransparent<Range<char*>>();\n  checkTransparent<Range<wchar_t*>>();\n  checkTransparent<Range<char16_t*>>();\n  checkTransparent<Range<char32_t*>>();\n  checkTransparent<Range<int*>>();\n\n  checkTransparent<std::vector<char>>();\n  checkTransparent<std::vector<wchar_t>>();\n  checkTransparent<std::vector<char16_t>>();\n  checkTransparent<std::vector<char32_t>>();\n  checkTransparent<std::vector<int>>();\n\n  checkTransparent<std::array<char const, 2>>();\n  checkTransparent<std::array<wchar_t const, 2>>();\n  checkTransparent<std::array<char16_t const, 2>>();\n  checkTransparent<std::array<char32_t const, 2>>();\n  checkTransparent<std::array<int const, 2>>();\n\n  checkTransparent<std::array<char, 2>>();\n  checkTransparent<std::array<wchar_t, 2>>();\n  checkTransparent<std::array<char16_t, 2>>();\n  checkTransparent<std::array<char32_t, 2>>();\n  checkTransparent<std::array<int, 2>>();\n}\n\nTEST(HeterogeneousAccess, transparentIsNotSelected) {\n  checkNotTransparent<char>();\n  checkNotTransparent<int>();\n  checkNotTransparent<float>();\n  checkNotTransparent<std::pair<StringPiece, StringPiece>>();\n  checkNotTransparent<StringVector>(); // no folly::hasher for Range\n}\n\ntemplate <typename L, typename R, typename S>\nvoid runTestMatches2(S src) {\n  S smaller{src};\n  smaller.resize(smaller.size() - 1);\n\n  using RangeType = Range<typename S::value_type*>;\n\n  L lhs1{RangeType{&src[0], src.size()}};\n  L lhs2{RangeType{&smaller[0], smaller.size()}};\n  R rhs1{RangeType{&src[0], src.size()}};\n  R rhs2{RangeType{&smaller[0], smaller.size()}};\n\n  HeterogeneousAccessEqualTo<L> equalTo;\n  HeterogeneousAccessHash<L> hash;\n\n  EXPECT_TRUE(equalTo(lhs1, rhs1));\n  EXPECT_FALSE(equalTo(lhs1, rhs2));\n  EXPECT_FALSE(equalTo(lhs2, rhs1));\n  EXPECT_TRUE(equalTo(lhs2, rhs2));\n\n  EXPECT_EQ(hash(lhs1), hash(rhs1));\n  EXPECT_NE(hash(lhs1), hash(rhs2)); // technically only low probability\n  EXPECT_NE(hash(lhs2), hash(rhs1)); // technically only low probability\n  EXPECT_EQ(hash(lhs2), hash(rhs2));\n\n  auto v0 = smaller[0];\n  std::array<decltype(v0), 1> a{{v0}};\n  EXPECT_FALSE(equalTo(a, lhs1));\n  EXPECT_FALSE(equalTo(a, rhs1));\n\n  smaller.resize(1);\n  EXPECT_FALSE(equalTo(a, lhs1));\n  EXPECT_FALSE(equalTo(a, lhs2));\n  EXPECT_TRUE(equalTo(a, smaller));\n\n  EXPECT_EQ(hash(a), hash(smaller));\n}\n\ntemplate <typename S>\nvoid runTestMatches(S const& src) {\n  using SP = Range<typename S::value_type const*>;\n  using MSP = Range<typename S::value_type*>;\n  using SV = std::basic_string_view<typename S::value_type>;\n  using V = std::vector<typename S::value_type>;\n\n  runTestMatches2<S, S>(src);\n  runTestMatches2<S, SP>(src);\n  runTestMatches2<S, MSP>(src);\n  runTestMatches2<S, SV>(src);\n  runTestMatches2<S, V>(src);\n  runTestMatches2<SP, S>(src);\n  runTestMatches2<SP, SP>(src);\n  runTestMatches2<SP, MSP>(src);\n  runTestMatches2<SP, SV>(src);\n  runTestMatches2<SP, V>(src);\n  runTestMatches2<MSP, S>(src);\n  runTestMatches2<MSP, SP>(src);\n  runTestMatches2<MSP, MSP>(src);\n  runTestMatches2<MSP, SV>(src);\n  runTestMatches2<MSP, V>(src);\n  runTestMatches2<SV, S>(src);\n  runTestMatches2<SV, SP>(src);\n  runTestMatches2<SV, MSP>(src);\n  runTestMatches2<SV, SV>(src);\n  runTestMatches2<SV, V>(src);\n  runTestMatches2<V, S>(src);\n  runTestMatches2<V, SP>(src);\n  runTestMatches2<V, MSP>(src);\n  runTestMatches2<V, SV>(src);\n  runTestMatches2<V, V>(src);\n}\n\nRange<int const*> foo(small_vector<int, 2> const& sv) {\n  return sv;\n}\n\nTEST(HeterogeneousAccess, transparentMatches) {\n  runTestMatches<std::string>(\"abcd\");\n#if !defined(__cpp_lib_char8_t) || __cpp_lib_char8_t < 201907\n  runTestMatches<std::string>(u8\"abcd\");\n#else\n  runTestMatches<std::u8string>(u8\"abcd\");\n#endif\n  runTestMatches<std::wstring>(L\"abcd\");\n  runTestMatches<std::u16string>(u\"abcd\");\n  runTestMatches<std::u32string>(U\"abcd\");\n  runTestMatches<fbstring>(\"abcd\");\n  runTestMatches<std::vector<int>>({1, 2, 3, 4});\n\n  static_assert(\n      std::is_convertible<small_vector<int, 2>, Range<int const*>>::value);\n  runTestMatches<small_vector<int, 2>>({1, 2, 3, 4});\n}\n"
  },
  {
    "path": "folly/container/test/IRangeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/irange.h>\n\n#include <gtest/gtest.h>\n\nTEST(IRangeTest, SingleArg) {\n  int sum = 0;\n  for (const auto i : folly::irange(10)) {\n    sum += i;\n  }\n  EXPECT_EQ(sum, 45);\n}\n\nTEST(IRangeTest, TwoArg) {\n  int sum = 0;\n  for (const auto i : folly::irange(4, 10)) {\n    sum += i;\n  }\n  EXPECT_EQ(sum, 39);\n}\n\nTEST(IRangeTest, BigFirst) {\n  int sum = 0;\n  for (const auto i : folly::irange(11, 10)) {\n    sum += i;\n  }\n  EXPECT_EQ(sum, 0);\n}\n\nTEST(IRangeTest, FirstConvertsBig) {\n  int sum = 0;\n  for (const auto i : folly::irange(-1u, 10u)) {\n    sum += i;\n  }\n  EXPECT_EQ(sum, 0);\n}\n"
  },
  {
    "path": "folly/container/test/IntrusiveHeapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <memory>\n#include <ostream>\n#include <random>\n#include <type_traits>\n#include <vector>\n\n#include <folly/Random.h>\n#include <folly/container/IntrusiveHeap.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\nDEFINE_int32(fuzz_count, 10000, \"Number of operations to fuzz\");\n\nnamespace folly {\n\nnamespace {\n\nstruct A;\nstruct B;\n\nstruct TaskBase {\n  explicit TaskBase(char id) : id(id) {}\n\n  bool operator<(const TaskBase& other) const { return pri < other.pri; }\n\n  friend std::ostream& operator<<(std::ostream& os, const TaskBase& t) {\n    return os << t.pri << \": \" << t.id;\n  }\n\n  char id;\n  int pri = 0;\n};\n\nstruct Task\n    : public TaskBase,\n      public IntrusiveHeapNode<A>,\n      public IntrusiveHeapNode<B> {\n  using TaskBase::TaskBase;\n};\n\nstruct TrackedTask : public Task {\n  using Task::Task;\n\n  bool& in(IntrusiveHeap<TrackedTask, std::less<>, A>&) { return inA; }\n  bool& in(IntrusiveHeap<TrackedTask, std::less<>, B>&) { return inB; }\n\n  bool inA = false;\n  bool inB = false;\n};\n\nclass TaskWithComposition : public TaskBase {\n public:\n  using TaskBase::TaskBase;\n\n private:\n  IntrusiveHeapNode<void> node_;\n\n public:\n  using NodeTraits =\n      MemberNodeTraits<TaskWithComposition, void, &TaskWithComposition::node_>;\n};\n\n} // namespace\n\nclass IntrusiveHeapTest {\n public:\n  template <class Heap>\n  static void print(const Heap& heap, std::ostream& os) {\n    if (heap.root_ == nullptr) {\n      os << \"EMPTY\";\n      return;\n    }\n    print<Heap>(0, heap.root_, os << '\\n');\n  }\n\n  template <class Heap>\n  static void check(const Heap& heap) {\n    check(heap, heap.root_, nullptr);\n  }\n\n private:\n  template <class Heap>\n  static void check(\n      const Heap& heap,\n      const typename Heap::Node* node,\n      const typename Heap::Node* parent) {\n    if (node == nullptr) {\n      return;\n    }\n    CHECK_EQ(node->parent_, parent) << \"on node \" << node << \" in \" << heap;\n    if (parent != nullptr) {\n      CHECK(!Heap::compare(parent, node))\n          << \"on node \" << node << \" in \" << heap;\n    }\n    check(heap, node->left_, node);\n    check(heap, node->right_, node);\n  }\n\n  template <class Heap>\n  static void print(\n      int indent, const typename Heap::Node* node, std::ostream& os) {\n    if (node == nullptr) {\n      return;\n    }\n    // Right first so it looks like a top-down tree, but with left := up.\n    print<Heap>(indent + 2, node->right_, os);\n    os << std::string(indent, ' ') << *Heap::asT(node) << \" (\" << node\n       << \"; parent: \" << node->parent_ << \"; left: \" << node->left_\n       << \"; right: \" << node->right_ << \")\\n\";\n    print<Heap>(indent + 2, node->left_, os);\n  }\n};\n\ntemplate <class T, class Compare, class Tag, class Traits>\nstd::ostream& operator<<(\n    std::ostream& os, const IntrusiveHeap<T, Compare, Tag, Traits>& heap) {\n  IntrusiveHeapTest::print(heap, os);\n  return os;\n}\n\nTEST(IntrusiveHeap, Static) {\n  Task x('x');\n  IntrusiveHeapNode<A>* na = &x;\n  IntrusiveHeapNode<B>* nb = &x;\n  EXPECT_EQ(static_cast<Task*>(na), &x);\n  EXPECT_EQ(static_cast<Task*>(nb), &x);\n  EXPECT_NE(static_cast<void*>(na), static_cast<void*>(nb));\n  static_assert(!std::is_copy_assignable_v<Task>);\n  static_assert(!std::is_move_assignable_v<Task>);\n  static_assert(!std::is_copy_constructible_v<Task>);\n  static_assert(!std::is_move_constructible_v<Task>);\n}\n\ntemplate <class T, class Heap>\nvoid testBasic() {\n  T a('a'), b('b'), c('c'), d('d');\n  Heap heap;\n  const auto isLinked = [](T& x) {\n    return Heap::NodeTraits::asNode(&x)->isLinked();\n  };\n\n  EXPECT_FALSE(isLinked(a));\n  heap.push(&a);\n  EXPECT_TRUE(isLinked(a));\n  heap.push(&b);\n  heap.push(&c);\n  heap.push(&d);\n  b.pri = 3;\n  heap.update(&b);\n  EXPECT_TRUE(isLinked(b));\n  EXPECT_EQ(heap.top(), &b) << heap;\n  d.pri = 4;\n  heap.update(&d);\n  EXPECT_EQ(heap.top(), &d) << heap;\n  d.pri = 2;\n  heap.update(&d);\n  EXPECT_EQ(heap.pop(), &b) << heap;\n  EXPECT_FALSE(isLinked(b));\n  a.pri = 1;\n  heap.update(&a);\n  EXPECT_EQ(heap.pop(), &d) << heap;\n  EXPECT_FALSE(isLinked(d));\n  EXPECT_EQ(heap.pop(), &a) << heap;\n  EXPECT_FALSE(isLinked(a));\n  EXPECT_EQ(heap.pop(), &c) << heap;\n  EXPECT_FALSE(isLinked(c));\n  EXPECT_EQ(heap.top(), nullptr);\n}\n\nTEST(IntrusiveHeap, BasicDerived) {\n  testBasic<Task, IntrusiveHeap<Task, std::less<>, A>>();\n}\n\nTEST(IntrusiveHeap, BasicComposition) {\n  testBasic<\n      TaskWithComposition,\n      IntrusiveHeap<\n          TaskWithComposition,\n          std::less<>,\n          void,\n          TaskWithComposition::NodeTraits>>();\n}\n\nTEST(IntrusiveHeap, Fuzz) {\n  std::default_random_engine rng(1729); // Deterministic seed.\n  std::vector<std::unique_ptr<TrackedTask>> tasks;\n\n  IntrusiveHeap<TrackedTask, std::less<>, A> aHeap;\n  IntrusiveHeap<TrackedTask, std::less<>, B> bHeap;\n  for (char id = 'a'; id <= 'z'; ++id) {\n    tasks.push_back(std::make_unique<TrackedTask>(id));\n  }\n  for (auto i = FLAGS_fuzz_count; i-- > 0;) {\n    auto item = tasks[folly::Random::rand32(0, tasks.size(), rng)].get();\n    const auto fuzzHeap = [&](auto& heap) {\n      using Heap = std::decay_t<decltype(heap)>;\n      if (folly::Random::oneIn(2, rng)) {\n        if (bool& contained = item->in(heap)) {\n          VLOG(1) << \"removing \" << *item << \" from \" << heap;\n          EXPECT_TRUE(Heap::NodeTraits::asNode(item)->isLinked());\n          heap.erase(item);\n          EXPECT_FALSE(Heap::NodeTraits::asNode(item)->isLinked());\n          contained = false;\n        } else {\n          VLOG(1) << \"pushing \" << *item << \" to \" << heap;\n          heap.push(item);\n          contained = true;\n        }\n      }\n      IntrusiveHeapTest::check(heap);\n      VLOG(1) << heap;\n      if (folly::Random::oneIn(50, rng)) {\n        const auto compare = [](auto& a, auto& b) { return *a < *b; };\n        std::sort(tasks.begin(), tasks.end(), compare);\n        if (TrackedTask* top = heap.top()) {\n          auto lb = std::lower_bound(tasks.begin(), tasks.end(), top, compare);\n          ASSERT_TRUE(lb != tasks.end());\n          for (auto it = lb; it != tasks.end(); ++it) {\n            auto t = it->get();\n            if (t->in(heap)) {\n              ASSERT_EQ(top->pri, t->pri);\n            }\n          }\n        }\n      }\n    };\n    if (folly::Random::oneIn(2, rng)) {\n      fuzzHeap(aHeap);\n    } else {\n      fuzzHeap(bHeap);\n    }\n    const auto dp = static_cast<int>(folly::Random::rand32(0, 7, rng)) - 3;\n    VLOG(1) << \"adjusting \" << *item << \" by \" << dp;\n    item->pri += dp;\n    if (item->in(aHeap)) {\n      aHeap.update(item);\n      IntrusiveHeapTest::check(aHeap);\n      VLOG(1) << aHeap;\n    }\n    if (item->in(bHeap)) {\n      bHeap.update(item);\n      IntrusiveHeapTest::check(bHeap);\n      VLOG(1) << bHeap;\n    }\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/test/IteratorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/Iterator.h>\n\n#include <algorithm>\n#include <cassert>\n#include <cstddef>\n#include <deque>\n#include <functional>\n#include <iterator>\n#include <list>\n#include <map>\n#include <numeric>\n#include <set>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\n#if defined(__cpp_lib_concepts)\n#include <concepts>\n#endif\n\nclass IteratorTest : public testing::Test {};\n\nTEST_F(IteratorTest, iterator_has_known_distance_v) {\n  EXPECT_FALSE((folly::iterator_has_known_distance_v<int*, int const*>));\n  EXPECT_TRUE((folly::iterator_has_known_distance_v<int*, int*>));\n}\n\nTEST_F(IteratorTest, range_has_known_distance_v) {\n  EXPECT_FALSE(folly::range_has_known_distance_v<std::list<int>&>);\n  EXPECT_TRUE(folly::range_has_known_distance_v<std::vector<int>&>);\n}\n\nTEST_F(IteratorTest, iterator_category_t) {\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          folly::iterator_category_t<\n              std::iterator<std::input_iterator_tag, int>>,\n          std::input_iterator_tag>));\n  EXPECT_FALSE(( //\n      std::is_same_v<\n          folly::iterator_category_t<\n              std::iterator<std::input_iterator_tag, int>>,\n          std::output_iterator_tag>));\n}\n\nTEST_F(IteratorTest, iterator_category_matches_v) {\n  EXPECT_TRUE(( //\n      folly::iterator_category_matches_v<\n          std::iterator<std::input_iterator_tag, int>,\n          std::input_iterator_tag>));\n  EXPECT_FALSE(( //\n      folly::iterator_category_matches_v<\n          std::iterator<std::input_iterator_tag, int>,\n          std::output_iterator_tag>));\n  EXPECT_FALSE(( //\n      folly::iterator_category_matches_v<int, std::input_iterator_tag>));\n}\n\nTEST_F(IteratorTest, iterator_value_type_t) {\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          std::map<int, double>::value_type,\n          folly::iterator_value_type_t<std::map<int, double>::iterator>>));\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          std::map<std::reference_wrapper<int>, double&>::value_type,\n          folly::iterator_value_type_t<\n              std::map<std::reference_wrapper<int>, double&>::iterator>>));\n  EXPECT_FALSE(( //\n      std::is_same_v<\n          std::map<int, float>::value_type,\n          folly::iterator_value_type_t<std::map<int, double>::iterator>>));\n}\n\nTEST_F(IteratorTest, iterator_key_type_t) {\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          std::map<int, double>::key_type,\n          folly::iterator_key_type_t<std::map<int, double>::iterator>>));\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          std::map<std::reference_wrapper<int>, double&>::key_type,\n          folly::iterator_key_type_t<\n              std::map<std::reference_wrapper<int>, double&>::iterator>>));\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          int,\n          folly::iterator_key_type_t<std::iterator<\n              std::input_iterator_tag,\n              std::pair<const int&, double>>>>));\n  EXPECT_FALSE(( //\n      std::is_same_v<\n          std::map<char, double>::key_type,\n          folly::iterator_key_type_t<std::map<int, double>::iterator>>));\n}\n\nTEST_F(IteratorTest, iterator_mapped_type_t) {\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          std::map<int, double>::mapped_type,\n          folly::iterator_mapped_type_t<std::map<int, double>::iterator>>));\n  EXPECT_TRUE(( //\n      std::is_same_v<\n          std::map<std::reference_wrapper<int>, double&>::mapped_type,\n          folly::iterator_mapped_type_t<\n              std::map<std::reference_wrapper<int>, double&>::iterator>>));\n  EXPECT_FALSE(( //\n      std::is_same_v<\n          std::map<int, float>::mapped_type,\n          folly::iterator_mapped_type_t<std::map<int, double>::iterator>>));\n}\n\nnamespace {\n/**\n * Container type used for unit tests.\n */\ntemplate <typename T>\nusing Container = std::deque<T>;\n\n// Constructor and assignment operator call counters for struct Object.\nstd::size_t gDefaultCtrCnt;\nstd::size_t gCopyCtrCnt;\nstd::size_t gMoveCtrCnt;\nstd::size_t gExplicitCtrCnt;\nstd::size_t gMultiargCtrCnt;\nstd::size_t gCopyOpCnt;\nstd::size_t gMoveOpCnt;\nstd::size_t gConvertOpCnt;\n\n/**\n * Class that increases various counters to keep track of how objects have\n * been constructed or assigned to, to verify iterator behavior.\n */\nstruct Object {\n  Object() { ++gDefaultCtrCnt; }\n  Object(const Object&) { ++gCopyCtrCnt; }\n  Object(Object&&) noexcept { ++gMoveCtrCnt; }\n  explicit Object(int) { ++gExplicitCtrCnt; }\n  explicit Object(int, int) { ++gMultiargCtrCnt; }\n  Object& operator=(const Object&) {\n    ++gCopyOpCnt;\n    return *this;\n  }\n  Object& operator=(Object&&) noexcept {\n    ++gMoveOpCnt;\n    return *this;\n  }\n  [[maybe_unused]] Object& operator=(int) noexcept {\n    ++gConvertOpCnt;\n    return *this;\n  }\n};\n\n/**\n * Reset all call counters to 0.\n */\nvoid init_counters() {\n  gDefaultCtrCnt = gCopyCtrCnt = gMoveCtrCnt = gExplicitCtrCnt =\n      gMultiargCtrCnt = gCopyOpCnt = gMoveOpCnt = gConvertOpCnt = 0;\n}\n\n/**\n * Test for iterator copy and move.\n */\ntemplate <typename Iterator>\nvoid copy_and_move_test(Container<int>& q, Iterator it) {\n  assert(q.empty());\n  const auto it2(it); // copy construct\n  it = it2; // copy assign from const\n  FOLLY_PUSH_WARNING\n  FOLLY_CLANG_DISABLE_WARNING(\"-Wself-assign-overloaded\")\n  it = it; // self assign\n  FOLLY_POP_WARNING\n  auto it3(std::move(it)); // move construct\n  it = std::move(it3); // move assign\n  // Make sure iterator still works.\n  it = 4711; // emplace\n  EXPECT_EQ(q, Container<int>{4711});\n}\n\n/**\n * Test for emplacement with perfect forwarding.\n */\ntemplate <typename Iterator>\nvoid emplace_test(Container<Object>& q, Iterator it) {\n  using folly::make_emplace_args;\n  assert(q.empty());\n  init_counters();\n  it = Object{}; // default construct + move construct\n  Object obj; // default construct\n  it = obj; // copy construct\n  it = std::move(obj); // move construct\n  const Object obj2; // default construct\n  it = obj2; // copy construct from const\n  it = std::move(obj2); // copy construct (const defeats move)\n  it = 0; // explicit construct\n  it = make_emplace_args(0, 0); // explicit multiarg construct\n  it = std::make_pair(0, 0); // implicit multiarg construct\n  it = std::make_tuple(0, 0); // implicit multiarg construct\n  auto args = make_emplace_args(Object{}); // default construct + move construct\n  it = args; // copy construct\n  it = const_cast<const decltype(args)&>(args); // copy construct from const\n  it = std::move(args); // move construct\n  auto args2 = std::make_tuple(Object{}); // default construct + move construct\n  it = args2; // (implicit multiarg) copy construct\n  it = std::move(args2); // (implicit multiarg) move construct\n  auto args3 = std::make_pair(0, 0);\n  it = args3; // implicit multiarg construct\n  it = std::move(args3); // implicit multiarg construct\n  ASSERT_EQ(q.size(), 16);\n  EXPECT_EQ(gDefaultCtrCnt, 5);\n  EXPECT_EQ(gCopyCtrCnt, 6);\n  EXPECT_EQ(gMoveCtrCnt, 6);\n  EXPECT_EQ(gExplicitCtrCnt, 1);\n  EXPECT_EQ(gMultiargCtrCnt, 5);\n  EXPECT_EQ(gCopyOpCnt, 0);\n  EXPECT_EQ(gMoveOpCnt, 0);\n  EXPECT_EQ(gConvertOpCnt, 0);\n}\n} // namespace\n\nusing namespace folly;\n\n/**\n * Basic tests for folly::emplace_iterator.\n */\nTEST(EmplaceIterator, EmplacerTest) {\n  {\n    Container<int> q;\n    copy_and_move_test(q, emplacer(q, q.begin()));\n  }\n  {\n    Container<Object> q;\n    emplace_test(q, emplacer(q, q.begin()));\n  }\n  {\n    Container<int> q;\n    auto it = emplacer(q, q.begin());\n    it = 0;\n    it = 1;\n    it = 2;\n    it = emplacer(q, q.begin());\n    it = 3;\n    it = 4;\n    EXPECT_EQ(q, Container<int>({3, 4, 0, 1, 2}));\n  }\n}\n\n/**\n * Basic tests for folly::front_emplace_iterator.\n */\nTEST(EmplaceIterator, FrontEmplacerTest) {\n  {\n    Container<int> q;\n    copy_and_move_test(q, front_emplacer(q));\n  }\n  {\n    Container<Object> q;\n    emplace_test(q, front_emplacer(q));\n  }\n  {\n    Container<int> q;\n    auto it = front_emplacer(q);\n    it = 0;\n    it = 1;\n    it = 2;\n    it = front_emplacer(q);\n    it = 3;\n    it = 4;\n    EXPECT_EQ(q, Container<int>({4, 3, 2, 1, 0}));\n  }\n}\n\n/**\n * Basic tests for folly::back_emplace_iterator.\n */\nTEST(EmplaceIterator, BackEmplacerTest) {\n  {\n    Container<int> q;\n    copy_and_move_test(q, back_emplacer(q));\n  }\n  {\n    Container<Object> q;\n    emplace_test(q, back_emplacer(q));\n  }\n  {\n    Container<int> q;\n    auto it = back_emplacer(q);\n    it = 0;\n    it = 1;\n    it = 2;\n    it = back_emplacer(q);\n    it = 3;\n    it = 4;\n    EXPECT_EQ(q, Container<int>({0, 1, 2, 3, 4}));\n  }\n}\n\n/**\n * Basic tests for folly::hint_emplace_iterator.\n */\nTEST(EmplaceIterator, HintEmplacerTest) {\n  {\n    init_counters();\n    std::map<int, Object> m;\n    auto it = hint_emplacer(m, m.end());\n    it = make_emplace_args(\n        std::piecewise_construct,\n        std::forward_as_tuple(0),\n        std::forward_as_tuple(0));\n    it = make_emplace_args(\n        std::piecewise_construct,\n        std::forward_as_tuple(1),\n        std::forward_as_tuple(0, 0));\n    it = make_emplace_args(\n        std::piecewise_construct,\n        std::forward_as_tuple(2),\n        std::forward_as_tuple(Object{}));\n    ASSERT_EQ(m.size(), 3);\n    EXPECT_EQ(gDefaultCtrCnt, 1);\n    EXPECT_EQ(gCopyCtrCnt, 0);\n    EXPECT_EQ(gMoveCtrCnt, 1);\n    EXPECT_EQ(gExplicitCtrCnt, 1);\n    EXPECT_EQ(gMultiargCtrCnt, 1);\n    EXPECT_EQ(gCopyOpCnt, 0);\n    EXPECT_EQ(gMoveOpCnt, 0);\n    EXPECT_EQ(gConvertOpCnt, 0);\n  }\n  {\n    struct O {\n      explicit O(int i_) : i(i_) {}\n      bool operator<(const O& other) const { return i < other.i; }\n      bool operator==(const O& other) const { return i == other.i; }\n      int i;\n    };\n    std::vector<int> v1 = {0, 1, 2, 3, 4};\n    std::vector<int> v2 = {0, 2, 4};\n    std::set<O> diff;\n    std::set_difference(\n        v1.begin(),\n        v1.end(),\n        v2.begin(),\n        v2.end(),\n        hint_emplacer(diff, diff.end()));\n    std::set<O> expected = {O(1), O(3)};\n    ASSERT_EQ(diff, expected);\n  }\n}\n\n/**\n * Test std::copy() with explicit conversion. This would not compile with a\n * std::back_insert_iterator, because the constructor of Object that takes a\n * single int is explicit.\n */\nTEST(EmplaceIterator, Copy) {\n  init_counters();\n  Container<int> in({0, 1, 2});\n  Container<Object> out;\n  std::copy(in.begin(), in.end(), back_emplacer(out));\n  EXPECT_EQ(3, out.size());\n  EXPECT_EQ(gDefaultCtrCnt, 0);\n  EXPECT_EQ(gCopyCtrCnt, 0);\n  EXPECT_EQ(gMoveCtrCnt, 0);\n  EXPECT_EQ(gExplicitCtrCnt, 3);\n  EXPECT_EQ(gMultiargCtrCnt, 0);\n  EXPECT_EQ(gCopyOpCnt, 0);\n  EXPECT_EQ(gMoveOpCnt, 0);\n  EXPECT_EQ(gConvertOpCnt, 0);\n}\n\n/**\n * Test std::transform() with multi-argument constructors. This would require\n * a temporary Object with std::back_insert_iterator.\n */\nTEST(EmplaceIterator, Transform) {\n  init_counters();\n  Container<int> in({0, 1, 2});\n  Container<Object> out;\n  std::transform(in.begin(), in.end(), back_emplacer(out), [](int i) {\n    return make_emplace_args(i, i);\n  });\n  EXPECT_EQ(3, out.size());\n  EXPECT_EQ(gDefaultCtrCnt, 0);\n  EXPECT_EQ(gCopyCtrCnt, 0);\n  EXPECT_EQ(gMoveCtrCnt, 0);\n  EXPECT_EQ(gExplicitCtrCnt, 0);\n  EXPECT_EQ(gMultiargCtrCnt, 3);\n  EXPECT_EQ(gCopyOpCnt, 0);\n  EXPECT_EQ(gMoveOpCnt, 0);\n  EXPECT_EQ(gConvertOpCnt, 0);\n}\n\n/**\n * Test multi-argument store and forward.\n */\nTEST(EmplaceIterator, EmplaceArgs) {\n  Object o1;\n  const Object o2;\n  Object& o3 = o1;\n  const Object& o4 = o3;\n  Object o5;\n\n  {\n    // Test copy construction.\n    auto args = make_emplace_args(0, o1, o2, o3, o4, Object{}, std::cref(o2));\n    init_counters();\n    auto args2 = args;\n    EXPECT_EQ(gDefaultCtrCnt, 0);\n    EXPECT_EQ(gCopyCtrCnt, 5);\n    EXPECT_EQ(gMoveCtrCnt, 0);\n    EXPECT_EQ(gExplicitCtrCnt, 0);\n    EXPECT_EQ(gMultiargCtrCnt, 0);\n    EXPECT_EQ(gCopyOpCnt, 0);\n    EXPECT_EQ(gMoveOpCnt, 0);\n    EXPECT_EQ(gConvertOpCnt, 0);\n\n    // Test copy assignment.\n    init_counters();\n    args = args2;\n    EXPECT_EQ(gDefaultCtrCnt, 0);\n    EXPECT_EQ(gCopyCtrCnt, 0);\n    EXPECT_EQ(gMoveCtrCnt, 0);\n    EXPECT_EQ(gExplicitCtrCnt, 0);\n    EXPECT_EQ(gMultiargCtrCnt, 0);\n    EXPECT_EQ(gCopyOpCnt, 5);\n    EXPECT_EQ(gMoveOpCnt, 0);\n    EXPECT_EQ(gConvertOpCnt, 0);\n  }\n\n  {\n    // Test RVO.\n    init_counters();\n    auto args = make_emplace_args(\n        0, o1, o2, o3, o4, Object{}, std::cref(o2), rref(std::move(o5)));\n    EXPECT_EQ(gDefaultCtrCnt, 1);\n    EXPECT_EQ(gCopyCtrCnt, 4);\n    EXPECT_EQ(gMoveCtrCnt, 1);\n    EXPECT_EQ(gExplicitCtrCnt, 0);\n    EXPECT_EQ(gMultiargCtrCnt, 0);\n    EXPECT_EQ(gCopyOpCnt, 0);\n    EXPECT_EQ(gMoveOpCnt, 0);\n    EXPECT_EQ(gConvertOpCnt, 0);\n\n    // Test move construction.\n    init_counters();\n    auto args2 = std::move(args);\n    EXPECT_EQ(gDefaultCtrCnt, 0);\n    EXPECT_EQ(gCopyCtrCnt, 0);\n    EXPECT_EQ(gMoveCtrCnt, 5);\n    EXPECT_EQ(gExplicitCtrCnt, 0);\n    EXPECT_EQ(gMultiargCtrCnt, 0);\n    EXPECT_EQ(gCopyOpCnt, 0);\n    EXPECT_EQ(gMoveOpCnt, 0);\n    EXPECT_EQ(gConvertOpCnt, 0);\n\n    // Test move assignment.\n    init_counters();\n    args = std::move(args2);\n    EXPECT_EQ(gDefaultCtrCnt, 0);\n    EXPECT_EQ(gCopyCtrCnt, 0);\n    EXPECT_EQ(gMoveCtrCnt, 0);\n    EXPECT_EQ(gExplicitCtrCnt, 0);\n    EXPECT_EQ(gMultiargCtrCnt, 0);\n    EXPECT_EQ(gCopyOpCnt, 0);\n    EXPECT_EQ(gMoveOpCnt, 5);\n    EXPECT_EQ(gConvertOpCnt, 0);\n\n    // Make sure arguments are stored correctly. lvalues by reference, rvalues\n    // by (moved) copy. Rvalues cannot be stored by reference because they may\n    // refer to an expired temporary by the time they are accessed.\n    static_assert(\n        std::is_same<\n            int,\n            std::tuple_element_t<0, decltype(args)::storage_type>>::value);\n    static_assert(\n        std::is_same<\n            Object,\n            std::tuple_element_t<1, decltype(args)::storage_type>>::value);\n    static_assert(\n        std::is_same<\n            Object,\n            std::tuple_element_t<2, decltype(args)::storage_type>>::value);\n    static_assert(\n        std::is_same<\n            Object,\n            std::tuple_element_t<3, decltype(args)::storage_type>>::value);\n    static_assert(\n        std::is_same<\n            Object,\n            std::tuple_element_t<4, decltype(args)::storage_type>>::value);\n    static_assert(\n        std::is_same<\n            Object,\n            std::tuple_element_t<5, decltype(args)::storage_type>>::value);\n    static_assert(\n        std::is_same<\n            std::reference_wrapper<const Object>,\n            std::tuple_element_t<6, decltype(args)::storage_type>>::value);\n    static_assert(\n        std::is_same<\n            rvalue_reference_wrapper<Object>,\n            std::tuple_element_t<7, decltype(args)::storage_type>>::value);\n\n    // Check whether args.get() restores the original argument type for\n    // rvalue references to emplace_args.\n    static_assert(\n        std::is_same<int&&, decltype(get_emplace_arg<0>(std::move(args)))>::\n            value);\n    static_assert(\n        std::is_same<Object&, decltype(get_emplace_arg<1>(std::move(args)))>::\n            value);\n    static_assert(\n        std::is_same<\n            const Object&,\n            decltype(get_emplace_arg<2>(std::move(args)))>::value);\n    static_assert(\n        std::is_same<Object&, decltype(get_emplace_arg<3>(std::move(args)))>::\n            value);\n    static_assert(\n        std::is_same<\n            const Object&,\n            decltype(get_emplace_arg<4>(std::move(args)))>::value);\n    static_assert(\n        std::is_same<Object&&, decltype(get_emplace_arg<5>(std::move(args)))>::\n            value);\n    static_assert(\n        std::is_same<\n            const Object&,\n            decltype(get_emplace_arg<6>(std::move(args)))>::value);\n    static_assert(\n        std::is_same<Object&&, decltype(get_emplace_arg<7>(std::move(args)))>::\n            value);\n\n    // lvalue references to emplace_args should behave mostly like std::tuples.\n    // Note that get_emplace_arg<7>(args) does not compile, because\n    // folly::rvalue_reference_wrappers can only be unwrapped through an rvalue\n    // reference.\n    static_assert(\n        std::is_same<int&, decltype(get_emplace_arg<0>(args))>::value);\n    static_assert(\n        std::is_same<Object&, decltype(get_emplace_arg<1>(args))>::value);\n    static_assert(\n        std::is_same<Object&, decltype(get_emplace_arg<2>(args))>::value);\n    static_assert(\n        std::is_same<Object&, decltype(get_emplace_arg<3>(args))>::value);\n    static_assert(\n        std::is_same<Object&, decltype(get_emplace_arg<4>(args))>::value);\n    static_assert(\n        std::is_same<Object&, decltype(get_emplace_arg<5>(args))>::value);\n    static_assert(\n        std::is_same<const Object&, decltype(get_emplace_arg<6>(args))>::value);\n  }\n}\n\n/**\n * Test implicit unpacking.\n */\nTEST(EmplaceIterator, ImplicitUnpack) {\n  static std::size_t multiCtrCnt;\n  static std::size_t pairCtrCnt;\n  static std::size_t tupleCtrCnt;\n\n  struct Object2 {\n    Object2(int, int) { ++multiCtrCnt; }\n    explicit Object2(const std::pair<int, int>&) { ++pairCtrCnt; }\n    explicit Object2(const std::tuple<int, int>&) { ++tupleCtrCnt; }\n  };\n\n  auto test = [](auto&& it, bool expectUnpack) {\n    multiCtrCnt = pairCtrCnt = tupleCtrCnt = 0;\n    it = std::make_pair(0, 0);\n    it = std::make_tuple(0, 0);\n    if (expectUnpack) {\n      EXPECT_EQ(multiCtrCnt, 2);\n      EXPECT_EQ(pairCtrCnt, 0);\n      EXPECT_EQ(tupleCtrCnt, 0);\n    } else {\n      EXPECT_EQ(multiCtrCnt, 0);\n      EXPECT_EQ(pairCtrCnt, 1);\n      EXPECT_EQ(tupleCtrCnt, 1);\n    }\n  };\n\n  Container<Object2> q;\n\n  test(emplacer(q, q.begin()), true);\n  test(emplacer<false>(q, q.begin()), false);\n  test(front_emplacer(q), true);\n  test(front_emplacer<false>(q), false);\n  test(back_emplacer(q), true);\n  test(back_emplacer<false>(q), false);\n}\n\n// IndexIterator -------------\n\nnamespace index_iterator_type_tests {\nnamespace {\n\nstruct WeirdContainer {\n  std::vector<bool> body_;\n\n  using value_type = bool;\n  using size_type = std::uint32_t;\n  using difference_type = std::int32_t;\n\n  auto operator[](size_type idx) { return body_[idx]; }\n  auto operator[](size_type idx) const { return body_[idx]; }\n};\n\n// iterator concepts ------------------------\n#if defined(__cpp_lib_concepts)\n\nusing vit = folly::index_iterator<std::vector<int>>;\nusing cvit = folly::index_iterator<const std::vector<int>>;\nusing vit_weird = folly::index_iterator<WeirdContainer>;\nusing cvit_weird = folly::index_iterator<const WeirdContainer>;\n\nstatic_assert(std::random_access_iterator<vit>);\nstatic_assert(std::random_access_iterator<cvit>);\nstatic_assert(std::random_access_iterator<vit_weird>);\nstatic_assert(std::random_access_iterator<cvit_weird>);\n\n#endif\n\n// dependent type computations ------------------------\nstruct ref_type {};\nstruct cref_type {};\n\nstruct just_operator {\n  using value_type = int;\n  ref_type operator[](std::size_t) { return {}; }\n  cref_type operator[](std::size_t) const { return {}; }\n};\n\nstruct has_size_type : just_operator {\n  using size_type = std::uint32_t;\n};\n\nstruct has_difference_type : just_operator {\n  using difference_type = std::int32_t;\n};\n\nstruct has_both_size_and_difference_type : just_operator {\n  using size_type = std::uint32_t;\n  using difference_type = std::int32_t;\n};\n\nstruct no_operator {\n  using value_type = int;\n\n  friend ref_type tag_invoke(\n      folly::cpo_t<index_iterator_access_at>, no_operator&, std::size_t) {\n    return {};\n  }\n\n  friend cref_type tag_invoke(\n      folly::cpo_t<index_iterator_access_at>, const no_operator&, std::size_t) {\n    return {};\n  }\n\n  [[maybe_unused]] int operator[](std::size_t);\n  [[maybe_unused]] int operator[](std::size_t) const;\n};\n\nstruct no_operator_private {\n  using value_type = int;\n\n private:\n  friend ref_type tag_invoke(\n      folly::cpo_t<index_iterator_access_at>,\n      no_operator_private&,\n      std::size_t) {\n    return {};\n  }\n\n  friend cref_type tag_invoke(\n      folly::cpo_t<index_iterator_access_at>,\n      const no_operator_private&,\n      std::size_t) {\n    return {};\n  }\n};\n\ntemplate <typename T>\nusing all_types = std::tuple<\n    typename std::iterator_traits<folly::index_iterator<T>>::value_type,\n    typename std::iterator_traits<folly::index_iterator<T>>::reference,\n    typename std::iterator_traits<folly::index_iterator<T>>::difference_type,\n    typename folly::index_iterator<T>::size_type>;\n\n// gives better error messages than std::is_same\ntemplate <typename T>\nvoid is_same_test(T, T) {}\n\n} // namespace\n\nTEST(IndexIterator, Types) {\n  is_same_test(\n      all_types<just_operator>{},\n      std::tuple<int, ref_type, std::ptrdiff_t, std::size_t>{});\n  is_same_test(\n      all_types<const just_operator>{},\n      std::tuple<int, cref_type, std::ptrdiff_t, std::size_t>{});\n  is_same_test(\n      all_types<has_size_type>{},\n      std::tuple<int, ref_type, std::ptrdiff_t, std::uint32_t>{});\n  is_same_test(\n      all_types<const has_size_type>{},\n      std::tuple<int, cref_type, std::ptrdiff_t, std::uint32_t>{});\n  is_same_test(\n      all_types<has_difference_type>{},\n      std::tuple<int, ref_type, std::int32_t, std::size_t>{});\n  is_same_test(\n      all_types<const has_difference_type>{},\n      std::tuple<int, cref_type, std::int32_t, std::size_t>{});\n  is_same_test(\n      all_types<has_both_size_and_difference_type>{},\n      std::tuple<int, ref_type, std::int32_t, std::uint32_t>{});\n  is_same_test(\n      all_types<const has_both_size_and_difference_type>{},\n      std::tuple<int, cref_type, std::int32_t, std::uint32_t>{});\n  is_same_test(\n      all_types<no_operator>{},\n      std::tuple<int, ref_type, std::ptrdiff_t, std::size_t>{});\n  is_same_test(\n      all_types<const no_operator>{},\n      std::tuple<int, cref_type, std::ptrdiff_t, std::size_t>{});\n  is_same_test(\n      all_types<no_operator_private>{},\n      std::tuple<int, ref_type, std::ptrdiff_t, std::size_t>{});\n  is_same_test(\n      all_types<const no_operator_private>{},\n      std::tuple<int, cref_type, std::ptrdiff_t, std::size_t>{});\n}\n\n} // namespace index_iterator_type_tests\n\nTEST(IndexIterator, Sort) {\n  std::vector<int> v(100, 0);\n  std::iota(v.rbegin(), v.rend(), 0);\n\n  std::sort(\n      index_iterator<std::vector<int>>{v, 0},\n      index_iterator<std::vector<int>>{v, v.size()});\n\n  std::vector<int> expected(100, 0);\n  std::iota(expected.begin(), expected.end(), 0);\n  ASSERT_EQ(expected, v);\n}\n\nnamespace {\n\nconstexpr bool accessTest() {\n  std::array<int, 2> a{0, 1};\n  const std::array<int, 2> b{0, 1};\n\n  const index_iterator<std::array<int, 2>> mutI{a, 0};\n  const index_iterator<const std::array<int, 2>> constI{b, 0};\n\n  if (*mutI != 0 || *constI != 0) {\n    return false;\n  }\n\n  if (mutI[1] != 1 || constI[1] != 1) {\n    return false;\n  }\n\n  return true;\n}\n\nstatic_assert(accessTest());\n\ntemplate <typename I>\nconstexpr bool mutationsTest(I i0, I i1) {\n  auto tmp = i0;\n  if (++tmp != i1) {\n    return false;\n  }\n  tmp = i0;\n  if (tmp++ != i0) {\n    return false;\n  }\n  if (tmp != i1) {\n    return false;\n  }\n\n  tmp = i1;\n  if (--tmp != i0) {\n    return false;\n  }\n  tmp = i1;\n  if (tmp-- != i1) {\n    return false;\n  }\n  if (tmp != i0) {\n    return false;\n  }\n\n  return true;\n}\n\nstatic constexpr std::array<int, 2> kA{0, 1};\n\nstatic constexpr index_iterator<const std::array<int, 2>> kI0{kA, 0};\nstatic constexpr index_iterator<const std::array<int, 2>> kI1{kA, 1};\n\nstatic_assert(kI0 == kI0);\nstatic_assert(kI0 != kI1);\nstatic_assert(kI0 < kI1);\nstatic_assert(kI0 <= kI0);\nstatic_assert(kI0 <= kI1);\nstatic_assert(kI1 >= kI1);\nstatic_assert(kI1 >= kI0);\nstatic_assert(kI1 > kI0);\n\nstatic_assert(mutationsTest(kI0, kI1));\nstatic_assert(kI0 + 1 == kI1);\nstatic_assert(kI1 - 1 == kI0);\nstatic_assert(kI1 - kI0 == 1);\nstatic_assert(kI0 - kI1 == -1);\n\nstruct IndexedVector {\n  std::vector<int> v_;\n\n  std::pair<int, int&> operator[](std::size_t i) {\n    return {static_cast<int>(i), v_[i]};\n  }\n\n  std::pair<int, const int&> operator[](std::size_t i) const {\n    return {static_cast<int>(i), v_[i]};\n  }\n\n  using value_type = std::pair<int, int>;\n\n  using iterator = folly::index_iterator<IndexedVector>;\n  using const_iterator = folly::index_iterator<const IndexedVector>;\n\n  iterator begin() { return {*this, 0}; }\n  const_iterator begin() const { return cbegin(); }\n  const_iterator cbegin() const { return {*this, 0}; }\n\n  iterator end() { return {*this, v_.size()}; }\n  const_iterator end() const { return cend(); }\n  const_iterator cend() const { return {*this, v_.size()}; }\n};\n\nstruct CustomMapWithIndexedIterator {\n public:\n  using size_type = std::uint32_t;\n  using value_type = std::pair<int, int>;\n\n public:\n  using iterator = folly::index_iterator<CustomMapWithIndexedIterator>;\n  using const_iterator =\n      folly::index_iterator<const CustomMapWithIndexedIterator>;\n\n  iterator begin() { return {*this, 0}; }\n  const_iterator cbegin() const { return {*this, 0}; }\n\n  std::vector<int> keys;\n  std::vector<int> values;\n\n private:\n  friend std::pair<int, int> tag_invoke(\n      folly::cpo_t<index_iterator_access_at>,\n      const CustomMapWithIndexedIterator& self,\n      size_type idx) {\n    return {self.keys[idx], self.values[idx]};\n  }\n};\n\n} // namespace\n\nTEST(IndexIterator, UseProxyReferences) {\n  IndexedVector iv{{0, 1, 2, 3}};\n  const IndexedVector& civ = iv;\n\n  using it = decltype(iv.begin());\n  using cit = decltype(civ.begin());\n\n  using it_ref_t = typename std::iterator_traits<it>::reference;\n  using it_cref_t = typename std::iterator_traits<cit>::reference;\n\n  static_assert(std::is_same<it_ref_t, std::pair<int, int&>>::value);\n  static_assert(std::is_same<it_cref_t, std::pair<int, const int&>>::value);\n\n  static_assert(std::is_same<decltype(it{}->first), int>::value);\n  static_assert(std::is_same<decltype(it{}->second), int&>::value);\n  static_assert(std::is_same<decltype(cit{}->first), int>::value);\n  static_assert(std::is_same<decltype(cit{}->second), const int&>::value);\n\n  ASSERT_EQ(4, (std::count_if(civ.begin(), civ.end(), [](auto&& pair) {\n              return pair.first == pair.second;\n            })));\n  ASSERT_EQ(4, (std::count_if(iv.begin(), iv.end(), [](auto&& pair) {\n              return pair.first == pair.second;\n            })));\n  cit conversion = iv.begin() + 1;\n  ASSERT_EQ(&iv, conversion.get_container());\n  ASSERT_EQ(1, conversion.get_index());\n\n  static_assert(!std::is_convertible<cit, it>::value);\n\n  for (auto&& x : iv) {\n    x.second = -1;\n  }\n\n  std::vector<int> expected{-1, -1, -1, -1};\n  ASSERT_EQ(expected, iv.v_);\n\n  // testing pointer proxies\n  for (auto f = iv.begin(); f != iv.end(); ++f) {\n    f->second = 1;\n  }\n\n  expected = {1, 1, 1, 1};\n  ASSERT_EQ(expected, iv.v_);\n}\n\nTEST(IndexIterator, OperatorArrowForNonProxies) {\n  using v_t = std::vector<std::array<int, 2>>;\n  using iterator = folly::index_iterator<v_t>;\n\n  static_assert(std::is_same<iterator::pointer, v_t::pointer>::value);\n\n  v_t v;\n  v.resize(3);\n  iterator f{v, 0};\n  iterator l{v, v.size()};\n\n  f->at(0) = 1;\n  (f + 1)->at(0) = 2;\n  (f + 2)->at(0) = 3;\n\n  v_t expected{{1, 0}, {2, 0}, {3, 0}};\n\n  ASSERT_EQ(expected, v);\n}\n\nTEST(IndexIterator, NoOperatorIntegration) {\n  CustomMapWithIndexedIterator m{.keys{1, 2, 3}, .values{4, 5, 6}};\n\n  auto it = m.begin();\n  ASSERT_EQ((std::pair{1, 4}), it[0]);\n  ASSERT_EQ((std::pair{2, 5}), it[1]);\n  ASSERT_EQ((std::pair{3, 6}), it[2]);\n\n  auto cit = m.cbegin();\n  ASSERT_EQ((std::pair{1, 4}), cit[0]);\n  ASSERT_EQ((std::pair{2, 5}), cit[1]);\n  ASSERT_EQ((std::pair{3, 6}), cit[2]);\n}\n"
  },
  {
    "path": "folly/container/test/MapUtilTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/MapUtil.h>\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <unordered_map>\n\n#include <folly/Traits.h>\n#include <folly/portability/GTest.h>\n#include <folly/sorted_vector_types.h>\n\nusing namespace folly;\n\nTEST(MapUtil, getDefault) {\n  std::map<int, int> m;\n  m[1] = 2;\n  EXPECT_EQ(2, get_default(m, 1, 42));\n  EXPECT_EQ(42, get_default(m, 2, 42));\n  EXPECT_EQ(0, get_default(m, 3));\n}\n\nTEST(MapUtil, getDefaultFunction) {\n  std::map<int, int> m;\n  m[1] = 2;\n  EXPECT_EQ(2, get_default(m, 1, [] { return 42; }));\n  EXPECT_EQ(42, get_default(m, 2, [] { return 42; }));\n  EXPECT_EQ(0, get_default(m, 3));\n}\n\nTEST(MapUtil, getOrThrow) {\n  std::map<int, int> m;\n  m[1] = 2;\n  EXPECT_EQ(2, get_or_throw(m, 1));\n  EXPECT_THROW(get_or_throw(m, 2), std::out_of_range);\n  EXPECT_EQ(&m[1], &get_or_throw(m, 1));\n  get_or_throw(m, 1) = 3;\n  EXPECT_EQ(3, get_or_throw(m, 1));\n  const auto& cm = m;\n  EXPECT_EQ(&m[1], &get_or_throw(cm, 1));\n  EXPECT_EQ(3, get_or_throw(cm, 1));\n  EXPECT_THROW(get_or_throw(cm, 2), std::out_of_range);\n}\n\nTEST(MapUtil, getOrThrowSpecified) {\n  std::map<int, int> m;\n  m[1] = 2;\n  EXPECT_EQ(2, get_or_throw<std::runtime_error>(m, 1));\n  EXPECT_THROW(get_or_throw<std::runtime_error>(m, 2), std::runtime_error);\n}\n\nTEST(MapUtil, getOptional) {\n  std::map<int, int> m;\n  m[1] = 2;\n  EXPECT_TRUE(get_optional(m, 1).has_value());\n  EXPECT_EQ(2, get_optional(m, 1).value());\n  EXPECT_FALSE(get_optional(m, 2).has_value());\n}\n\nTEST(MapUtil, getOptionalView) {\n  std::map<int, std::string> m;\n  m[1] = \"2\";\n  EXPECT_TRUE(get_optional<std::optional<std::string_view>>(m, 1).has_value());\n  EXPECT_EQ(\"2\", get_optional<std::optional<std::string_view>>(m, 1).value());\n  EXPECT_FALSE(get_optional<std::optional<std::string_view>>(m, 2).has_value());\n}\n\nTEST(MapUtil, getOptionalPathSimple) {\n  using std::map;\n  map<int, map<int, map<int, map<int, int>>>> m{{1, {{2, {{3, {{4, 5}}}}}}}};\n  EXPECT_EQ(folly::Optional<int>(5), get_optional(m, 1, 2, 3, 4));\n  EXPECT_TRUE(get_optional(m, 1, 2, 3, 4));\n  EXPECT_FALSE(get_optional(m, 1, 2, 3, 0));\n  EXPECT_TRUE(get_optional(m, 1, 2, 3));\n  EXPECT_FALSE(get_optional(m, 1, 2, 0));\n  EXPECT_TRUE(get_optional(m, 1, 2));\n  EXPECT_FALSE(get_optional(m, 1, 0));\n  EXPECT_TRUE(get_optional(m, 1));\n  EXPECT_FALSE(get_optional(m, 0));\n}\n\nTEST(MapUtil, getOptionalPathMixed) {\n  using std::map;\n  using std::string;\n  using std::unordered_map;\n  unordered_map<string, map<int, map<string, int>>> m{{\"a\", {{1, {{\"b\", 2}}}}}};\n  EXPECT_EQ(folly::Optional<int>(2), get_optional(m, \"a\", 1, \"b\"));\n  EXPECT_TRUE(get_optional(m, \"a\", 1, \"b\"));\n  EXPECT_FALSE(get_optional(m, \"b\", 1, \"b\"));\n  EXPECT_FALSE(get_optional(m, \"a\", 2, \"b\"));\n  EXPECT_FALSE(get_optional(m, \"a\", 1, \"c\"));\n  EXPECT_TRUE(get_optional(m, \"a\", 1));\n  EXPECT_TRUE(get_optional(m, \"a\"));\n}\n\nTEST(MapUtil, getOptionalStd) {\n  std::map<int, int> m;\n  m[1] = 2;\n  EXPECT_TRUE(get_optional<std::optional>(m, 1).has_value());\n  EXPECT_EQ(2, get_optional<std::optional>(m, 1).value());\n  EXPECT_FALSE(get_optional<std::optional>(m, 2).has_value());\n\n  std::map<int, std::map<int, int>> m2{{1, {{2, 3}}}};\n  EXPECT_TRUE(get_optional<std::optional>(m2, 1, 2).has_value());\n  EXPECT_EQ(3, get_optional<std::optional>(m2, 1, 2).value());\n  EXPECT_FALSE(get_optional<std::optional>(m2, 1, 3).has_value());\n}\n\nTEST(MapUtil, getRefDefault) {\n  std::map<int, int> m;\n  m[1] = 2;\n  const int i = 42;\n  EXPECT_EQ(2, get_ref_default(m, 1, i));\n  EXPECT_EQ(42, get_ref_default(m, 2, i));\n  EXPECT_EQ(std::addressof(i), std::addressof(get_ref_default(m, 2, i)));\n}\n\nTEST(MapUtil, getRefDefaultFunction) {\n  std::map<int, int> m;\n  m[1] = 2;\n  const int i = 42;\n  EXPECT_EQ(2, get_ref_default(m, 1, [&i]() -> const int& { return i; }));\n  EXPECT_EQ(42, get_ref_default(m, 2, [&i]() -> const int& { return i; }));\n  EXPECT_EQ(\n      std::addressof(i),\n      std::addressof(get_ref_default(m, 2, [&i]() -> const int& {\n        return i;\n      })));\n  // statically disallowed:\n  // get_ref_default(m, 2, [] { return 7; });\n}\n\nTEST(MapUtil, getPtr) {\n  std::map<int, int> m;\n  m[1] = 2;\n  EXPECT_EQ(2, *get_ptr(m, 1));\n  EXPECT_TRUE(get_ptr(m, 2) == nullptr);\n  *get_ptr(m, 1) = 4;\n  EXPECT_EQ(4, m.at(1));\n  EXPECT_EQ(4, *get_ptr(&m, 1));\n\n  std::map<int, int>* nullMap = nullptr;\n  EXPECT_EQ(nullptr, get_ptr(nullMap, 2));\n}\n\nTEST(MapUtil, getPtr2) {\n  folly::sorted_vector_map<int, int> m;\n  m[1] = 7;\n  m[4] = 9;\n  auto r0 = get_ptr2(m, 1, 4);\n  EXPECT_EQ(7, *r0.first);\n  EXPECT_EQ(9, *r0.second);\n  auto r1 = get_ptr2(m, 1, 5);\n  EXPECT_EQ(7, *r1.first);\n  EXPECT_EQ(nullptr, r1.second);\n  auto r2 = get_ptr2(m, 2, 4);\n  EXPECT_EQ(nullptr, r2.first);\n  EXPECT_EQ(9, *r2.second);\n  auto r3 = get_ptr2(m, 2, 5);\n  EXPECT_EQ(nullptr, r3.first);\n  EXPECT_EQ(nullptr, r3.second);\n}\n\nTEST(MapUtil, getPtrReferenceValueType) {\n  std::map<int, const std::string&> testMap;\n  EXPECT_EQ(nullptr, folly::get_ptr(testMap, 3));\n\n  std::string someString = \"some\";\n  testMap.emplace(3, someString);\n  EXPECT_EQ(&someString, folly::get_ptr(testMap, 3));\n}\n\nTEST(MapUtil, getPtrPathSimple) {\n  using std::map;\n  map<int, map<int, map<int, map<int, int>>>> m{{1, {{2, {{3, {{4, 5}}}}}}}};\n  EXPECT_EQ(5, *get_ptr(m, 1, 2, 3, 4));\n  EXPECT_TRUE(get_ptr(m, 1, 2, 3, 4));\n  EXPECT_FALSE(get_ptr(m, 1, 2, 3, 0));\n  EXPECT_TRUE(get_ptr(m, 1, 2, 3));\n  EXPECT_FALSE(get_ptr(m, 1, 2, 0));\n  EXPECT_TRUE(get_ptr(m, 1, 2));\n  EXPECT_FALSE(get_ptr(m, 1, 0));\n  EXPECT_TRUE(get_ptr(m, 1));\n  EXPECT_FALSE(get_ptr(m, 0));\n  const auto& cm = m;\n  ++*get_ptr(m, 1, 2, 3, 4);\n  EXPECT_EQ(6, *get_ptr(cm, 1, 2, 3, 4));\n  EXPECT_TRUE(get_ptr(cm, 1, 2, 3, 4));\n  EXPECT_FALSE(get_ptr(cm, 1, 2, 3, 0));\n\n  EXPECT_EQ(6, *get_ptr(&cm, 1, 2, 3, 4));\n\n  map<int, map<int, map<int, map<int, int>>>>* nullMap = nullptr;\n  EXPECT_EQ(nullptr, get_ptr(nullMap, 1, 2, 3, 4));\n}\n\nTEST(MapUtil, getPtrPathMixed) {\n  using std::map;\n  using std::string;\n  using std::unordered_map;\n  unordered_map<string, map<int, map<string, int>>> m{{\"a\", {{1, {{\"b\", 7}}}}}};\n  EXPECT_EQ(7, *get_ptr(m, \"a\", 1, \"b\"));\n  EXPECT_TRUE(get_ptr(m, \"a\", 1, \"b\"));\n  EXPECT_FALSE(get_ptr(m, \"b\", 1, \"b\"));\n  EXPECT_FALSE(get_ptr(m, \"a\", 2, \"b\"));\n  EXPECT_FALSE(get_ptr(m, \"a\", 1, \"c\"));\n  EXPECT_TRUE(get_ptr(m, \"a\", 1));\n  EXPECT_TRUE(get_ptr(m, \"a\"));\n  const auto& cm = m;\n  ++*get_ptr(m, \"a\", 1, \"b\");\n  EXPECT_EQ(8, *get_ptr(cm, \"a\", 1, \"b\"));\n  EXPECT_TRUE(get_ptr(cm, \"a\", 1, \"b\"));\n  EXPECT_FALSE(get_ptr(cm, \"b\", 1, \"b\"));\n}\n\nnamespace {\ntemplate <typename T>\nstruct element_type {\n  using type = typename std::decay<T>::type;\n};\n\ntemplate <typename T>\nstruct element_type<T()> {\n  using type = T;\n};\n\ntemplate <typename T>\nusing element_type_t = typename element_type<T>::type;\n\ntemplate <typename T, typename = void>\nstruct Compiles : std::false_type {};\n\ntemplate <typename T>\nstruct Compiles<\n    T,\n    void_t<decltype(get_ref_default(\n        std::declval<std::map<int, element_type_t<T>>>(),\n        std::declval<int>(),\n        std::declval<T>()))>> : std::true_type {};\n} // namespace\n\nTEST(MapUtil, getDefaultTemporary) {\n  EXPECT_TRUE(Compiles<const int&>::value);\n  EXPECT_TRUE(Compiles<int&>::value);\n  EXPECT_FALSE(Compiles<const int&&>::value);\n  EXPECT_FALSE(Compiles<int&&>::value);\n\n  EXPECT_TRUE(Compiles<const int&()>::value);\n  EXPECT_TRUE(Compiles<int&()>::value);\n  EXPECT_FALSE(Compiles<int()>::value);\n}\n\nTEST(MapUtil, getDefaultPath) {\n  using std::map;\n  map<int, map<int, int>> m;\n  m[4][2] = 42;\n  EXPECT_EQ(42, get_default(m, 4, 2, 42));\n  EXPECT_EQ(42, get_default(m, 1, 3, 42));\n}\n\nTEST(MapUtil, getDefaultPathMixed) {\n  using std::map;\n  using std::string;\n  using std::unordered_map;\n  map<int, unordered_map<string, StringPiece>> m;\n  int key1 = 42;\n  const string key2 = \"hello\";\n  constexpr StringPiece value = \"world\";\n  constexpr StringPiece dflt = \"default\";\n  m[key1][key2] = value;\n  EXPECT_EQ(value, get_default(m, 42, key2, dflt));\n  EXPECT_EQ(value, get_default(m, key1, \"hello\", dflt));\n  EXPECT_EQ(dflt, get_default(m, 0, key2, dflt));\n  EXPECT_EQ(dflt, get_default(m, key1, \"bad\", \"default\"));\n}\n\nTEST(MapUtil, getRefDefaultPath) {\n  using std::map;\n  map<int, map<int, int>> m;\n  m[4][2] = 42;\n  const int dflt = 13;\n  EXPECT_EQ(42, get_ref_default(m, 4, 2, dflt));\n  EXPECT_EQ(dflt, get_ref_default(m, 1, 3, dflt));\n}\n\nTEST(MapUtil, getRefDefaultPathMixed) {\n  using std::map;\n  using std::string;\n  using std::unordered_map;\n  map<int, unordered_map<string, StringPiece>> m;\n  int key1 = 42;\n  const string key2 = \"hello\";\n  constexpr StringPiece value = \"world\";\n  constexpr StringPiece dflt = \"default\";\n  m[key1][key2] = value;\n  EXPECT_EQ(value, get_ref_default(m, 42, key2, dflt));\n  EXPECT_EQ(value, get_ref_default(m, key1, \"hello\", dflt));\n  EXPECT_EQ(dflt, get_ref_default(m, 0, key2, dflt));\n  EXPECT_EQ(dflt, get_ref_default(m, key1, \"bad\", dflt));\n}\n\nnamespace {\ntemplate <typename T, typename = void>\nstruct GetRefDefaultPathCompiles : std::false_type {};\n\ntemplate <typename T>\nstruct GetRefDefaultPathCompiles<\n    T,\n    void_t<decltype(get_ref_default(\n        std::declval<std::map<int, std::map<int, element_type_t<T>>>>(),\n        std::declval<int>(),\n        std::declval<int>(),\n        std::declval<T>()))>> : std::true_type {};\n} // namespace\n\nTEST(MapUtil, getRefDefaultPathTemporary) {\n  EXPECT_TRUE(GetRefDefaultPathCompiles<const int&>::value);\n  EXPECT_TRUE(GetRefDefaultPathCompiles<int&>::value);\n  EXPECT_FALSE(GetRefDefaultPathCompiles<const int&&>::value);\n  EXPECT_FALSE(GetRefDefaultPathCompiles<int&&>::value);\n}\n\nnamespace {\n\nclass TestConstruction {\n public:\n  [[maybe_unused]] TestConstruction() { EXPECT_TRUE(false); }\n  [[maybe_unused]] TestConstruction(TestConstruction&&) { EXPECT_TRUE(false); }\n  TestConstruction(const TestConstruction&) { EXPECT_TRUE(false); }\n\n  explicit TestConstruction(std::string&& string)\n      : string_{std::move(string)} {}\n  explicit TestConstruction(int&& integer) : integer_{integer} {}\n\n  TestConstruction& operator=(const TestConstruction&) = delete;\n  TestConstruction& operator=(TestConstruction&&) = delete;\n\n  int integer_{};\n  std::string string_{};\n};\n\n} // namespace\n\nTEST(MapUtil, testGetDefaultDeferredConstruction) {\n  auto map = std::unordered_map<int, TestConstruction>{};\n  map.emplace(\n      std::piecewise_construct,\n      std::forward_as_tuple(1),\n      std::forward_as_tuple(1));\n\n  EXPECT_EQ(map.at(1).integer_, 1);\n\n  {\n    auto val = get_default(map, 0, 1);\n    EXPECT_EQ(val.integer_, 1);\n    EXPECT_EQ(val.string_, \"\");\n  }\n\n  {\n    auto val = get_default(map, 0, \"something\");\n    EXPECT_EQ(val.integer_, 0);\n    EXPECT_EQ(val.string_, \"something\");\n  }\n}\n"
  },
  {
    "path": "folly/container/test/MergeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/Merge.h>\n\n#include <map>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\nTEST(MergeTest, NonOverlapping) {\n  std::vector<int> a = {0, 2, 4, 6};\n  std::vector<int> b = {1, 3, 5, 7};\n  std::vector<int> c;\n\n  folly::merge(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(c));\n  EXPECT_EQ(8, c.size());\n  for (size_t i = 0; i < 8; ++i) {\n    EXPECT_EQ(i, c[i]);\n  }\n}\n\nTEST(MergeTest, OverlappingInSingleInputRange) {\n  std::vector<std::pair<int, int>> a = {{0, 0}, {0, 1}};\n  std::vector<std::pair<int, int>> b = {{2, 2}, {3, 3}};\n  std::map<int, int> c;\n\n  folly::merge(\n      a.begin(), a.end(), b.begin(), b.end(), std::inserter(c, c.begin()));\n  EXPECT_EQ(3, c.size());\n\n  // First value is inserted, second is not\n  EXPECT_EQ(c[0], 0);\n\n  EXPECT_EQ(c[2], 2);\n  EXPECT_EQ(c[3], 3);\n}\n\nTEST(MergeTest, OverlappingInDifferentInputRange) {\n  std::vector<std::pair<int, int>> a = {{0, 0}, {1, 1}};\n  std::vector<std::pair<int, int>> b = {{0, 2}, {3, 3}};\n  std::map<int, int> c;\n\n  folly::merge(\n      a.begin(), a.end(), b.begin(), b.end(), std::inserter(c, c.begin()));\n  EXPECT_EQ(3, c.size());\n\n  // Value from a is inserted, value from b is not.\n  EXPECT_EQ(c[0], 0);\n\n  EXPECT_EQ(c[1], 1);\n  EXPECT_EQ(c[3], 3);\n}\n"
  },
  {
    "path": "folly/container/test/RegexMatchCacheTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/RegexMatchCache.h>\n\n#include <chrono>\n#include <numeric>\n#include <random>\n#include <string>\n#include <string_view>\n\n#include <fmt/format.h>\n#include <fmt/ranges.h>\n\n#include <folly/Portability.h>\n#include <folly/Utility.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/container/sorted_vector_types.h>\n#include <folly/container/span.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals;\n\nusing folly::RegexMatchCache;\nusing folly::RegexMatchCacheDynamicBitset;\nusing folly::RegexMatchCacheKey;\nusing folly::RegexMatchCacheKeyAndView;\n\nstatic_assert(\n    !std::is_move_assignable_v<RegexMatchCache::ConsistencyReportMatcher>);\n\nextern \"C\" FOLLY_KEEP size_t check_folly_regex_match_cache_dynamic_bitset_count(\n    RegexMatchCacheDynamicBitset const& bitset) {\n  auto const view = bitset.as_index_set_view();\n  return std::accumulate(view.begin(), view.end(), size_t(0));\n}\n\n/// test_ref\n///\n/// A formattable variation of std::reference_wrapper.\n///\n/// TODO: Since fmt-v12, remove this and just use std::reference_wrapper.\ntemplate <typename T>\nstruct test_ref : std::reference_wrapper<T> {\n  using base = std::reference_wrapper<T>;\n  using base::base;\n};\ntemplate <typename T>\ntest_ref(T&) -> test_ref<T>;\n\n/// TODO: Since fmt-v9, remove this and just define an overload of format_as.\nnamespace fmt {\ntemplate <typename T>\nstruct formatter<test_ref<T>> : private formatter<std::remove_const_t<T>> {\n  using base = formatter<std::remove_const_t<T>>;\n  using base::parse;\n\n#if FMT_VERSION >= 80000\n  template <typename Context>\n  auto format(test_ref<T> const ref, Context& ctx) const {\n    return base::format(ref.get(), ctx);\n  }\n#else\n  template <typename Context>\n  auto format(test_ref<T> const ref, Context& ctx) {\n    return base::format(ref.get(), ctx);\n  }\n#endif\n};\n} // namespace fmt\n\n#if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE\ntemplate <typename T>\nusing unordered_vector_set_base = folly::F14VectorSet<T>;\n#else\ntemplate <typename T>\nusing unordered_vector_set_base = folly::sorted_vector_set<T>;\n#endif\n\ntemplate <typename T>\nstruct unordered_vector_set : private unordered_vector_set_base<T> {\n private:\n  using base = unordered_vector_set_base<T>;\n\n  static folly::span<T const> to_span(\n      folly::F14VectorSet<T> const& set) noexcept {\n    auto const size = set.size();\n    auto const data = size ? &*set.begin() + 1 - size : nullptr;\n    return {data, size};\n  }\n  static folly::span<T const> to_span(\n      folly::sorted_vector_set<T> const& set) noexcept {\n    return {&*set.begin(), &*set.end()};\n  }\n  folly::span<T const> to_span() const noexcept { return to_span(*this); }\n\n public:\n  using base::contains;\n  using base::empty;\n  using base::size;\n\n  auto begin() const noexcept { return to_span().begin(); }\n  auto end() const noexcept { return to_span().end(); }\n\n  using base::clear;\n\n  void insert(T const& value) { base::insert(value); }\n  void erase(T const& value) { base::erase(value); }\n};\n\nstruct RegexMatchCacheDynamicBitsetTest : testing::Test {};\n\nTEST_F(RegexMatchCacheDynamicBitsetTest, example) {\n  folly::RegexMatchCacheDynamicBitset bitset;\n  EXPECT_FALSE(bitset.get_value(3));\n  EXPECT_FALSE(bitset.get_value(14));\n  EXPECT_TRUE(bitset.as_index_set_view().empty());\n  EXPECT_THAT(bitset.as_index_set_view(), testing::ElementsAre());\n\n  bitset.set_value(3, false);\n  EXPECT_FALSE(bitset.get_value(3));\n  EXPECT_FALSE(bitset.get_value(2));\n  EXPECT_FALSE(bitset.get_value(14));\n  EXPECT_FALSE(bitset.get_value(99));\n  EXPECT_TRUE(bitset.as_index_set_view().empty());\n  EXPECT_THAT(bitset.as_index_set_view(), testing::ElementsAre());\n\n  bitset.set_value(3, true);\n  EXPECT_TRUE(bitset.get_value(3));\n  EXPECT_FALSE(bitset.get_value(2));\n  EXPECT_FALSE(bitset.get_value(14));\n  EXPECT_FALSE(bitset.get_value(99));\n  EXPECT_FALSE(bitset.as_index_set_view().empty());\n  EXPECT_THAT(bitset.as_index_set_view(), testing::ElementsAre(3));\n\n  bitset.set_value(99, true);\n  EXPECT_TRUE(bitset.get_value(3));\n  EXPECT_FALSE(bitset.get_value(2));\n  EXPECT_FALSE(bitset.get_value(14));\n  EXPECT_TRUE(bitset.get_value(99));\n  EXPECT_FALSE(bitset.as_index_set_view().empty());\n  EXPECT_THAT(bitset.as_index_set_view(), testing::ElementsAre(3, 99));\n}\n\nTEST_F(RegexMatchCacheDynamicBitsetTest, combinatorics) {\n  constexpr size_t opt = folly::kIsOptimize;\n  constexpr size_t san = folly::kIsSanitize;\n  constexpr size_t rounds = 1u << (1 + size_t(opt) + size_t(!san));\n  constexpr size_t ops = 1u << (8 + size_t(opt) + size_t(!san));\n  std::mt19937 rng;\n  unordered_vector_set<size_t> set;\n  folly::RegexMatchCacheDynamicBitset dyn;\n  for (size_t r = 0; r < rounds; ++r) {\n    set.clear();\n    dyn.reset();\n    for (size_t op = 0; op < ops; ++op) {\n      auto const what = rng() % 128;\n      SCOPED_TRACE(fmt::format(\"round[{}] op[{}] what[{}]\", r, op, what));\n      size_t value = 0;\n      if (what < 16) {\n        if (!set.empty()) {\n          auto dist = std::uniform_int_distribution<size_t>{0, set.size() - 1};\n          value = folly::span<size_t const>{set.begin(), set.end()}[dist(rng)];\n          ASSERT_TRUE(dyn.get_value(value));\n          set.erase(value);\n          dyn.set_value(value, false);\n          ASSERT_FALSE(dyn.get_value(value));\n        }\n      } else if (what < 48) {\n        auto dist = std::uniform_int_distribution<size_t>{0, op};\n        value = dist(rng);\n        ASSERT_EQ(set.contains(value), dyn.get_value(value));\n        set.erase(value);\n        ASSERT_FALSE(set.contains(value));\n        dyn.set_value(value, false);\n        ASSERT_FALSE(dyn.get_value(value));\n      } else {\n        auto dist = std::uniform_int_distribution<size_t>{0, op};\n        value = dist(rng);\n        ASSERT_EQ(set.contains(value), dyn.get_value(value));\n        set.insert(value);\n        ASSERT_TRUE(set.contains(value));\n        dyn.set_value(value, true);\n        ASSERT_TRUE(dyn.get_value(value));\n      }\n      ASSERT_EQ(set.size(), set.end() - set.begin());\n      std::vector<size_t> els{set.begin(), set.end()};\n      std::sort(els.begin(), els.end());\n      ASSERT_THAT(dyn.as_index_set_view(), testing::ElementsAreArray(els));\n    }\n  }\n}\n\nstruct RegexMatchCacheIndexedVectorTest : testing::Test {};\n\nTEST_F(RegexMatchCacheIndexedVectorTest, example) {\n  folly::RegexMatchCacheIndexedVector<std::string> vec;\n  EXPECT_EQ(0, vec.size());\n\n  EXPECT_THAT(\n      vec.as_forward_view(),\n      testing::UnorderedElementsAreArray(\n          std::initializer_list<std::pair<std::string, size_t>>{\n              //\n          }));\n\n  EXPECT_EQ(std::pair(size_t(0), true), vec.insert_value(\"hello\"));\n  EXPECT_THAT(\n      vec.as_forward_view(),\n      testing::UnorderedElementsAreArray(\n          std::initializer_list<std::pair<std::string, size_t>>{\n              {\"hello\", 0},\n          }));\n\n  EXPECT_EQ(std::pair(size_t(1), true), vec.insert_value(\"world\"));\n  EXPECT_THAT(\n      vec.as_forward_view(),\n      testing::UnorderedElementsAreArray(\n          std::initializer_list<std::pair<std::string, size_t>>{\n              {\"hello\", 0},\n              {\"world\", 1},\n          }));\n\n  //  erase an element, leaving a hole in the index space to be filled\n  EXPECT_TRUE(vec.erase_value(\"hello\"));\n  EXPECT_THAT(\n      vec.as_forward_view(),\n      testing::UnorderedElementsAreArray(\n          std::initializer_list<std::pair<std::string, size_t>>{\n              {\"world\", 1},\n          }));\n\n  //  fill the hole in the index space\n  EXPECT_EQ(std::pair(size_t(0), true), vec.insert_value(\"water\"));\n  EXPECT_THAT(\n      vec.as_forward_view(),\n      testing::UnorderedElementsAreArray(\n          std::initializer_list<std::pair<std::string, size_t>>{\n              {\"water\", 0},\n              {\"world\", 1},\n          }));\n}\n\nstruct RegexMatchCacheTest : testing::Test {\n  using clock = RegexMatchCache::clock;\n  using time_point = RegexMatchCache::time_point;\n\n  class KeyMap final : public RegexMatchCache::KeyMap {\n   private:\n    folly::F14FastMap<regex_key, std::string> store_;\n\n   public:\n    void add(regex_key_and_view const& regex) {\n      auto const to_value = [&] { return std::string{regex}; };\n      store_.try_emplace(regex, folly::invocable_to(to_value));\n    }\n\n    std::string_view lookup(regex_key const& regex) const override {\n      return store_.at(regex);\n    }\n  };\n\n  KeyMap keys;\n\n  class ConsistencyReportCache\n      : public RegexMatchCache::ConsistencyReportMatcher {\n   private:\n    using base = RegexMatchCache::ConsistencyReportMatcher;\n    using key = std::tuple<regex_key, string_pointer>;\n    using map = std::unordered_map<key, bool>;\n    map cache_;\n\n   public:\n    bool empty() const noexcept { return cache_.empty(); }\n\n    size_t size() const noexcept { return cache_.size(); }\n\n    bool match(\n        RegexMatchCache::KeyMap const& keymap,\n        regex_key const regex,\n        string_pointer const string) final {\n      auto const key = std::tuple{regex, string};\n      auto const [iter, inserted] = cache_.try_emplace(key, false);\n      auto& entry = iter->second;\n      return !inserted ? entry : (entry = base::match(keymap, regex, string));\n    }\n\n    void clear() { cache_.clear(); }\n\n    map const& get_map() const noexcept { return cache_; }\n\n    void eraseRegex(regex_key const regex) {\n      auto const end = cache_.end();\n      auto it = cache_.begin();\n      while (it != end) {\n        auto const match = std::get<0>(it->first) == regex;\n        it = match ? cache_.erase(it) : std::next(it);\n      }\n    }\n  };\n\n  ConsistencyReportCache crcache;\n\n  class ConsistencyReport {\n    friend RegexMatchCacheTest;\n\n   private:\n    std::vector<std::string> lines_;\n\n    ConsistencyReport() = default;\n    explicit ConsistencyReport(std::vector<std::string>&& lines) noexcept\n        : lines_{std::move(lines)} {}\n\n    void print(std::ostream& o) const {\n      if (lines_.empty()) {\n        o << \"consistent\" << std::endl;\n      } else {\n        o << \"inconsistencies:\" << std::endl;\n        for (auto const& line : lines_) {\n          o << \"  \" << line << std::endl;\n        }\n      }\n    }\n\n   public:\n    bool consistent() const noexcept { return lines_.empty(); }\n\n    friend std::ostream& operator<<(\n        std::ostream& o, ConsistencyReport const& report) {\n      return (report.print(o), o);\n    }\n  };\n\n  void checkConsistency(RegexMatchCache const& cache) {\n    cache.consistency(crcache, keys, [&](auto const line) { //\n      throw std::logic_error(line);\n    });\n  }\n  ConsistencyReport getConsistencyReport(RegexMatchCache const& cache) {\n    ConsistencyReport out;\n    cache.consistency(crcache, keys, [&](auto&& line) { //\n      out.lines_.push_back(std::move(line));\n    });\n    return out;\n  }\n\n  auto inspect(RegexMatchCache const& cache) const {\n    return cache.inspect(keys);\n  }\n\n  auto getRegexList(RegexMatchCache const& cache) const {\n    std::vector<std::string> ret;\n    for (auto const item : cache.getRegexList(keys)) {\n      ret.emplace_back(item);\n    }\n    return ret;\n  }\n\n  auto lookup(RegexMatchCache& cache, std::string_view regex, time_point now) {\n    auto key = RegexMatchCacheKeyAndView(regex);\n    keys.add(key);\n    auto uncached = std::as_const(cache).findMatchesUncached(regex);\n    if (!std::as_const(cache).isReadyToFindMatches(key)) {\n      cache.prepareToFindMatches(key);\n    }\n    auto matches = std::as_const(cache).findMatches(key, now);\n    EXPECT_THAT(matches, testing::UnorderedElementsAreArray(uncached));\n    return matches;\n  }\n};\n\nTEST_F(RegexMatchCacheTest, clean) {\n  RegexMatchCache cache;\n  checkConsistency(cache);\n}\n\nTEST_F(RegexMatchCacheTest, clean_lookup) {\n  RegexMatchCache cache;\n\n  EXPECT_THAT(\n      lookup(cache, \"foo|bar\", time_point() + 5s), //\n      testing::UnorderedElementsAre())\n      << inspect(cache);\n  checkConsistency(cache);\n}\n\nTEST_F(RegexMatchCacheTest, add_strings_erase_add) {\n  auto const foo = \"foo\"s;\n  auto const bar = \"bar\"s;\n  RegexMatchCache cache;\n\n  cache.addString(&foo);\n  checkConsistency(cache);\n  cache.addString(&bar);\n  checkConsistency(cache);\n\n  cache.eraseString(&bar);\n  checkConsistency(cache);\n  EXPECT_THAT(cache.getStringList(), testing::UnorderedElementsAre(&foo));\n\n  cache.addString(&bar);\n  checkConsistency(cache);\n  EXPECT_THAT(cache.getStringList(), testing::UnorderedElementsAre(&foo, &bar));\n}\n\nTEST_F(RegexMatchCacheTest, add_strings_lookup) {\n  auto const foo = \"foo\"s;\n  auto const bar = \"bar\"s;\n  RegexMatchCache cache;\n\n  cache.addString(&foo);\n  checkConsistency(cache);\n  cache.addString(&bar);\n  checkConsistency(cache);\n\n  EXPECT_THAT(\n      lookup(cache, \"foo|qux\", time_point() + 5s), //\n      testing::UnorderedElementsAre(&foo))\n      << inspect(cache);\n  checkConsistency(cache);\n}\n\nTEST_F(RegexMatchCacheTest, add_strings_lookup_erase_string) {\n  auto const foo = \"foo\"s;\n  auto const bar = \"bar\"s;\n  RegexMatchCache cache;\n\n  cache.addString(&foo);\n  cache.addString(&bar);\n  EXPECT_THAT(\n      lookup(cache, \"foo|qux\", time_point() + 5s), //\n      testing::UnorderedElementsAre(&foo))\n      << inspect(cache);\n\n  cache.eraseString(&foo);\n  checkConsistency(cache);\n\n  EXPECT_THAT(\n      lookup(cache, \"foo|qux\", time_point() + 5s), //\n      testing::UnorderedElementsAre())\n      << inspect(cache);\n  checkConsistency(cache);\n}\n\nTEST_F(RegexMatchCacheTest, add_strings_lookup_repeat) {\n  auto const foo = \"foo\"s;\n  auto const bar = \"bar\"s;\n  auto const cat = \"cat\"s;\n  auto const dog = \"dog\"s;\n  RegexMatchCache cache;\n\n  cache.addString(&foo);\n  cache.addString(&bar);\n  EXPECT_THAT(\n      lookup(cache, \"foo|qux\", time_point() + 5s), //\n      testing::UnorderedElementsAre(&foo))\n      << inspect(cache);\n\n  cache.addString(&cat);\n  checkConsistency(cache);\n  cache.addString(&dog);\n  checkConsistency(cache);\n  EXPECT_THAT(\n      lookup(cache, \"foo|qux\", time_point() + 5s), //\n      testing::UnorderedElementsAre(&foo))\n      << inspect(cache);\n  checkConsistency(cache);\n  EXPECT_THAT(\n      lookup(cache, \"foo|qux|dog\", time_point() + 5s), //\n      testing::UnorderedElementsAre(&dog, &foo))\n      << inspect(cache);\n  checkConsistency(cache);\n}\n\nTEST_F(RegexMatchCacheTest, add_string) {\n  auto const foo = \"foo\"s;\n  RegexMatchCache cache;\n  checkConsistency(cache);\n  EXPECT_FALSE(cache.hasString(&foo));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre());\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre());\n\n  cache.addString(&foo);\n  checkConsistency(cache);\n  EXPECT_TRUE(cache.hasString(&foo));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre(&foo));\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre());\n\n  cache.eraseString(&foo);\n  checkConsistency(cache);\n  EXPECT_FALSE(cache.hasString(&foo));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre());\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre());\n}\n\nTEST_F(RegexMatchCacheTest, add_regex) {\n  constexpr auto xFooOrBar = \"foo|bar\"sv;\n  RegexMatchCache cache;\n  checkConsistency(cache);\n  EXPECT_FALSE(cache.hasRegex(RegexMatchCacheKey(xFooOrBar)));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre());\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre());\n\n  keys.add(RegexMatchCacheKeyAndView(xFooOrBar));\n  cache.addRegex(RegexMatchCacheKey(xFooOrBar));\n  checkConsistency(cache);\n  EXPECT_TRUE(cache.hasRegex(RegexMatchCacheKey(xFooOrBar)));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre());\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre(xFooOrBar));\n\n  cache.eraseRegex(RegexMatchCacheKey(xFooOrBar));\n  checkConsistency(cache);\n  EXPECT_FALSE(cache.hasRegex(RegexMatchCacheKey(xFooOrBar)));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre());\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre());\n}\n\nTEST_F(RegexMatchCacheTest, add_regex_add_string) {\n  const auto xFoo = \"foo\"s;\n  constexpr auto xFooOrBar = \"foo|bar\"sv;\n  RegexMatchCache cache;\n  checkConsistency(cache);\n  EXPECT_FALSE(cache.hasRegex(RegexMatchCacheKey(xFooOrBar)));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre());\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre());\n\n  keys.add(RegexMatchCacheKeyAndView(xFooOrBar));\n  cache.addRegex(RegexMatchCacheKey(xFooOrBar));\n  checkConsistency(cache);\n  EXPECT_TRUE(cache.hasRegex(RegexMatchCacheKey(xFooOrBar)));\n  EXPECT_THAT(cache.getStringList(), testing::ElementsAre());\n  EXPECT_THAT(getRegexList(cache), testing::ElementsAre(xFooOrBar));\n\n  cache.addString(&xFoo);\n  checkConsistency(cache);\n}\n\nTEST_F(RegexMatchCacheTest, combinatorics) {\n  constexpr size_t opt = folly::kIsOptimize;\n  constexpr size_t san = folly::kIsSanitize;\n  constexpr size_t rounds = 1u << (1 + size_t(opt) + size_t(!san));\n  constexpr size_t ops = 1u << (8 + size_t(opt) + size_t(!san));\n  std::mt19937 rng;\n  std::vector const source{\"foo\"s, \"bar\"s, \"cat\"s, \"dog\"s, \"qux\"s, \"nit\"s};\n  RegexMatchCache::time_point now{};\n  RegexMatchCache cache;\n\n  auto const contains = [](auto const& c, auto const& k) {\n    return std::count(c.begin(), c.end(), k) > 0;\n  };\n  auto const rand_size = [&] {\n    auto const r = rng() % 256;\n    return size_t(1) + (r >= 128) + (r >= 192) + (r >= 224);\n  };\n\n  for (size_t r = 0; r < rounds; ++r) {\n    std::vector const strings = source;\n    auto const nstrings = strings.size();\n    SCOPE_EXIT {\n      crcache.clear();\n      cache.clear();\n    };\n    auto strings_dist = std::uniform_int_distribution<size_t>{0, nstrings - 1};\n    auto const rand_string = [&] { return &strings.at(strings_dist(rng)); };\n    auto const rand_strings = [&]() {\n      std::vector<test_ref<std::string const>> ret;\n      auto const out = std::back_inserter(ret);\n      std::sample(strings.begin(), strings.end(), out, rand_size(), rng);\n      return ret;\n    };\n    for (size_t i = 0; i < ops; ++i) {\n      auto const what = rng() % (1u << 5);\n      SCOPED_TRACE(fmt::format(\"round[{}] iter[{}] what[{}]\", r, i, what));\n      if (what < 1) {\n        crcache.clear();\n        cache.purge(now);\n        now += 1s;\n      } else if (what < 2) {\n        if (auto const regexes = getRegexList(cache); !regexes.empty()) {\n          auto const nregexes = regexes.size();\n          auto dist = std::uniform_int_distribution<size_t>{0, nregexes - 1};\n          auto const regex = std::string{regexes.at(dist(rng))};\n\n          auto const key = RegexMatchCacheKey{regex};\n          ASSERT_TRUE(cache.hasRegex(key));\n          crcache.eraseRegex(key);\n          cache.eraseRegex(key);\n          ASSERT_FALSE(cache.hasRegex(key));\n        }\n      } else if (what < 8) {\n        auto const str = rand_string();\n        cache.addString(str);\n      } else if (what < 10) {\n        auto const str = rand_string();\n        cache.eraseString(str);\n      }\n      if (what < 10) {\n        auto const report = getConsistencyReport(cache);\n        ASSERT_TRUE(report.consistent()) << inspect(cache) << report;\n      }\n      auto const chosen_strings = rand_strings();\n      auto const regex = fmt::format(\"{}\", fmt::join(chosen_strings, \"|\"));\n      auto const key = RegexMatchCacheKeyAndView(regex);\n      keys.add(key);\n      if (!cache.isReadyToFindMatches(key)) {\n        cache.prepareToFindMatches(key);\n      }\n      ASSERT_TRUE(cache.hasRegex(key));\n      {\n        auto const report = getConsistencyReport(cache);\n        ASSERT_TRUE(report.consistent()) << inspect(cache) << report;\n      }\n      auto const mlist = cache.findMatchesUnsafe(key, now);\n      for (auto const ref : chosen_strings) {\n        ASSERT_EQ(cache.hasString(&ref.get()), contains(mlist, &ref.get()));\n      }\n      ASSERT_THAT(\n          cache.findMatchesUncached(regex),\n          testing::UnorderedElementsAreArray(mlist));\n    }\n  }\n}\n"
  },
  {
    "path": "folly/container/test/ReserveTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/Reserve.h>\n\n#include <list>\n#include <memory>\n#include <unordered_map>\n#include <vector>\n\n#include <folly/container/F14Map.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\n\ntemplate <class T>\nclass CountingAlloc : private std::allocator<T> {\n public:\n  using value_type = T;\n\n  CountingAlloc() = default;\n\n  template <class U>\n  explicit CountingAlloc(const CountingAlloc<U>& /*unused*/) noexcept\n      : CountingAlloc() {} // count each type separately\n\n  int getCount() const { return *counter_; }\n\n  T* allocate(size_t size) {\n    (*counter_)++;\n    return std::allocator<T>::allocate(size);\n  }\n\n  void deallocate(T* p, size_t size) { std::allocator<T>::deallocate(p, size); }\n\n private:\n  std::shared_ptr<int> counter_ = std::make_shared<int>(0);\n};\n\n} // namespace\n\nconstexpr int kIters = 1000;\n\nTEST(ReserveUtil, VectorGrowBy) {\n  using Alloc = CountingAlloc<int>;\n  for (const int initSize : {0, 2, 3, 5, 7, 11, 13, 17}) {\n    for (const int numToAdd : {2, 3, 5, 7, 11, 13, 17}) {\n      std::vector<int, Alloc> v1(initSize);\n      std::vector<int, Alloc> v2(initSize);\n      for (int i = 0; i < kIters; ++i) {\n        v1.insert(v1.end(), numToAdd, 0); // range insert at end\n        folly::grow_capacity_by(v2, numToAdd);\n        for (int j = 0; j < numToAdd; ++j) {\n          v2.emplace_back();\n        }\n      }\n      const auto rangeInsertAllocs = v1.get_allocator().getCount();\n      const auto growByAllocs = v2.get_allocator().getCount();\n      EXPECT_EQ(rangeInsertAllocs, growByAllocs);\n    }\n  }\n}\n\nnamespace {\n\nauto getUniqueId(int i, int j) {\n  return (i * kIters) + j;\n}\n\ntemplate <class MapT>\nvoid testMapGrowBy() {\n  for (const int initSize : {0, 2, 3, 5, 7, 11, 13, 17}) {\n    for (const int numToAdd : {2, 3, 5, 7, 11, 13, 17}) {\n      MapT m1(initSize);\n      MapT m2(initSize);\n      MapT m3(initSize);\n      for (int i = 0; i < kIters; ++i) {\n        for (int j = 0; j < numToAdd; ++j) {\n          const auto& [_, inserted] = m1.emplace(getUniqueId(i, j), 0);\n          EXPECT_TRUE(inserted);\n        }\n        m2.reserve(m2.size() + numToAdd);\n        for (int j = 0; j < numToAdd; ++j) {\n          const auto& [_, inserted] = m2.emplace(getUniqueId(i, j), 0);\n          EXPECT_TRUE(inserted);\n        }\n        folly::grow_capacity_by(m3, numToAdd);\n        for (int j = 0; j < numToAdd; ++j) {\n          const auto& [_, inserted] = m3.emplace(getUniqueId(i, j), 0);\n          EXPECT_TRUE(inserted);\n        }\n      }\n      const auto reserveAllocs = m2.get_allocator().getCount();\n      const auto growByAllocs = m3.get_allocator().getCount();\n      EXPECT_GE(reserveAllocs, growByAllocs);\n      const auto noReserveCapacity = m1.bucket_count() * m1.max_load_factor();\n      const auto growByCapacity = m3.bucket_count() * m3.max_load_factor();\n      EXPECT_GE(2 * noReserveCapacity, growByCapacity);\n    }\n  }\n}\n\nusing F14VectorMap = folly::F14VectorMap<\n    int,\n    int,\n    std::hash<int>,\n    std::equal_to<>,\n    CountingAlloc<std::pair<const int, int>>>;\n\nusing F14ValueMap = folly::F14ValueMap<\n    int,\n    int,\n    std::hash<int>,\n    std::equal_to<>,\n    CountingAlloc<std::pair<const int, int>>>;\n\nusing F14NodeMap = folly::F14NodeMap<\n    int,\n    int,\n    std::hash<int>,\n    std::equal_to<>,\n    CountingAlloc<std::pair<const int, int>>>;\n\nusing unordered_map = std::unordered_map<\n    int,\n    int,\n    std::hash<int>,\n    std::equal_to<>,\n    CountingAlloc<std::pair<const int, int>>>;\n\n} // namespace\n\nTEST(ReserveUtil, F14VectorMapGrowBy) {\n  testMapGrowBy<F14VectorMap>();\n}\n\nTEST(ReserveUtil, F14ValueMapGrowBy) {\n  testMapGrowBy<F14ValueMap>();\n}\n\nTEST(ReserveUtil, F14NodeMapGrowBy) {\n  testMapGrowBy<F14NodeMap>();\n}\n\nTEST(ReserveUtil, UnorderedMapGrowBy) {\n  testMapGrowBy<unordered_map>();\n}\n\nTEST(ReserveUtil, ReserveIfAvailableVector) {\n  std::vector<int> v;\n  auto r = folly::reserve_if_available(v, 42);\n  static_assert(std::is_same_v<decltype(r), std::true_type>);\n  EXPECT_GE(v.capacity(), 42);\n}\n\nTEST(ReserveUtil, ReserveIfAvailableList) {\n  std::list<int> l;\n  auto r = folly::reserve_if_available(l, 42);\n  static_assert(std::is_same_v<decltype(r), std::false_type>);\n}\n"
  },
  {
    "path": "folly/container/test/SparseByteSetBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/***\n *  A benchmark comparing SparseByteSet to bitset<256> and bool[256].\n */\n\n#include <folly/container/SparseByteSet.h>\n\n#include <bitset>\n#include <random>\n#include <vector>\n\n#include <fmt/core.h>\n#include <folly/Benchmark.h>\n#include <folly/Format.h>\n#include <folly/portability/GFlags.h>\n\nusing namespace std;\nusing namespace folly;\n\nnamespace {\n\n//  Interface-identical to SparseByteSet. So that we can do compile-time\n//  polymorphism.\nclass BitSetWrapper {\n public:\n  inline bool add(uint8_t i) {\n    auto r = !contains(i);\n    if (r) {\n      rep_[i] = true;\n    }\n    return r;\n  }\n  inline bool contains(uint8_t i) { return rep_[i]; }\n\n private:\n  bitset<256> rep_;\n};\nclass BoolArraySet {\n public:\n  BoolArraySet() { memset(rep_, 0, sizeof(rep_)); }\n  inline bool add(uint8_t i) {\n    auto r = !contains(i);\n    if (r) {\n      rep_[i] = true;\n    }\n    return r;\n  }\n  inline bool contains(uint8_t i) { return rep_[i]; }\n\n private:\n  bool rep_[256];\n};\n\ntemplate <typename Coll>\nvoid rand_bench(int iters, size_t size_add, size_t size_contains) {\n  BenchmarkSuspender braces;\n  vector<uint8_t> seq_add;\n  vector<uint8_t> seq_contains;\n  mt19937 rng;\n  uniform_int_distribution<uint16_t> dist{\n      0, std::numeric_limits<uint8_t>::max()};\n  for (size_t i = 0; i < size_add; ++i) {\n    seq_add.push_back(to_narrow(dist(rng)));\n  }\n  for (size_t i = 0; i < size_contains; ++i) {\n    seq_contains.push_back(to_narrow(dist(rng)));\n  }\n  braces.dismissing([&] {\n    while (iters--) {\n      Coll coll;\n      for (auto b : seq_add) {\n        coll.add(b);\n      }\n      bool q{};\n      for (auto b : seq_contains) {\n        q ^= coll.contains(b);\n      }\n      doNotOptimizeAway(q);\n    }\n  });\n}\n\nvoid setup_rand_bench() {\n  vector<pair<size_t, size_t>> rand_bench_params = {\n      {4, 4},\n      {4, 16},\n      {4, 64},\n      {4, 256},\n      {16, 4},\n      {16, 16},\n      {16, 64},\n      {16, 256},\n      {64, 4},\n      {64, 16},\n      {64, 64},\n      {64, 256},\n      {256, 4},\n      {256, 16},\n      {256, 64},\n      {256, 256},\n  };\n  for (auto kvp : rand_bench_params) {\n    size_t size_add, size_contains;\n    tie(size_add, size_contains) = kvp;\n    addBenchmark(\n        __FILE__,\n        fmt::format(\"bitset_rand_bench({}, {})\", size_add, size_contains),\n        [=](int iters) {\n          rand_bench<BitSetWrapper>(iters, size_add, size_contains);\n          return iters;\n        });\n    addBenchmark(\n        __FILE__,\n        fmt::format(\n            \"%bool_array_set_rand_bench({}, {})\", size_add, size_contains),\n        [=](int iters) {\n          rand_bench<BoolArraySet>(iters, size_add, size_contains);\n          return iters;\n        });\n    addBenchmark(\n        __FILE__,\n        fmt::format(\n            \"%sparse_byte_set_rand_bench({}, {})\", size_add, size_contains),\n        [=](int iters) {\n          rand_bench<SparseByteSet>(iters, size_add, size_contains);\n          return iters;\n        });\n    addBenchmark(__FILE__, \"-\", [](int) { return 0; });\n  }\n}\n\n} // namespace\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  setup_rand_bench();\n  runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/container/test/SparseByteSetTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/SparseByteSet.h>\n\n#include <cstdint>\n#include <limits>\n#include <random>\n#include <set>\n\n#include <folly/portability/GTest.h>\n\nusing namespace std;\nusing namespace folly;\n\nnamespace {\n\nclass SparseByteSetTest : public testing::Test {\n protected:\n  using lims = numeric_limits<uint8_t>;\n  SparseByteSet s;\n};\n\n} // namespace\n\nTEST_F(SparseByteSetTest, empty) {\n  for (auto c = lims::min(); c < lims::max(); ++c) {\n    EXPECT_FALSE(s.contains(c));\n  }\n}\n\nTEST_F(SparseByteSetTest, each) {\n  for (auto c = lims::min(); c < lims::max(); ++c) {\n    EXPECT_TRUE(s.add(c));\n    EXPECT_TRUE(s.contains(c));\n  }\n  for (auto c = lims::min(); c < lims::max(); ++c) {\n    EXPECT_FALSE(s.add(c));\n    EXPECT_TRUE(s.contains(c));\n  }\n}\n\nTEST_F(SparseByteSetTest, each_random) {\n  mt19937 rng;\n  uniform_int_distribution<uint16_t> dist{lims::min(), lims::max()};\n  set<uint8_t> added;\n  while (added.size() <= lims::max()) {\n    auto c = uint8_t(dist(rng));\n    EXPECT_EQ(added.contains(c), s.contains(c));\n    EXPECT_EQ(!added.contains(c), s.add(c));\n    added.insert(c);\n    EXPECT_TRUE(added.contains(c)); // sanity\n    EXPECT_TRUE(s.contains(c));\n  }\n}\n\nTEST_F(SparseByteSetTest, clear) {\n  for (auto c = lims::min(); c < lims::max(); ++c) {\n    EXPECT_TRUE(s.add(c));\n  }\n  s.clear();\n  for (auto c = lims::max() - 1; c > lims::min(); --c) {\n    EXPECT_FALSE(s.contains(c));\n    EXPECT_TRUE(s.add(c));\n  }\n}\n\nTEST_F(SparseByteSetTest, remove) {\n  for (auto c = lims::min(); c < lims::max(); ++c) {\n    EXPECT_TRUE(s.add(c));\n  }\n  for (auto c = lims::min(); c < lims::max() / 2; ++c) {\n    EXPECT_TRUE(s.remove(c));\n    EXPECT_FALSE(s.contains(c));\n  }\n\n  // did not corrupt rest data\n  for (auto c = lims::max() / 2; c < lims::max(); ++c) {\n    EXPECT_TRUE(s.contains(c));\n  }\n\n  // check deleting last elements\n  for (auto c = lims::max() - 1; c >= lims::max() / 2; --c) {\n    EXPECT_TRUE(s.remove(c));\n    EXPECT_FALSE(s.contains(c));\n  }\n}\n\nTEST_F(SparseByteSetTest, remove_nop) {\n  bool r = s.remove(12);\n  EXPECT_FALSE(r);\n}\n\nTEST_F(SparseByteSetTest, size) {\n  EXPECT_EQ(s.size(), 0);\n\n  s.add(1);\n  s.add(2);\n  s.add(3);\n  EXPECT_EQ(s.size(), 3);\n\n  s.remove(1);\n  EXPECT_EQ(s.size(), 2);\n}\n"
  },
  {
    "path": "folly/container/test/StdBitsetBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <bitset>\n#include <random>\n\n#include <folly/Benchmark.h>\n#include <folly/container/StdBitset.h>\n#include <folly/init/Init.h>\n\nusing namespace folly;\n\ntemplate <size_t N>\nstd::bitset<N> createRandomBitset(size_t seed, double density = 0.5) {\n  std::bitset<N> bitset;\n  std::mt19937 gen(seed);\n  std::bernoulli_distribution dist(density);\n\n  for (size_t i = 0; i < N; ++i) {\n    if (dist(gen)) {\n      bitset.set(i);\n    }\n  }\n  return bitset;\n}\n\ntemplate <size_t N>\nstd::bitset<N> createSparseBitset(size_t seed) {\n  std::bitset<N> bitset;\n  std::mt19937 gen(seed);\n  std::uniform_int_distribution<size_t> dist(0, N - 1);\n\n  size_t numBits = std::max(1UL, N / 100);\n  for (size_t i = 0; i < numBits; ++i) {\n    bitset.set(dist(gen));\n  }\n  return bitset;\n}\n\ntemplate <size_t N>\nstd::bitset<N> createDenseBitset(size_t seed) {\n  auto bitset = createSparseBitset<N>(seed);\n  bitset.flip();\n  return bitset;\n}\n\nBENCHMARK(StdBitsetFindFirst_64_Sparse, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<64> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createSparseBitset<64>(i + 12345);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_64_Dense, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<64> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createDenseBitset<64>(i + 23456);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_64_Random, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<64> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createRandomBitset<64>(i + 34567);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_256_Sparse, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<256> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createSparseBitset<256>(i + 45678);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_256_Dense, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<256> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createDenseBitset<256>(i + 56789);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_256_Random, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<256> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createRandomBitset<256>(i + 67890);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_1024_Sparse, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<1024> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createSparseBitset<1024>(i + 78901);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_1024_Dense, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<1024> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createDenseBitset<1024>(i + 89012);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindFirst_1024_Random, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<1024> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createRandomBitset<1024>(i + 90123);\n    }\n    auto result = std_bitset_find_first(bitset);\n    doNotOptimizeAway(result);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_64_Sparse, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<64> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createSparseBitset<64>(i + 11111);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_64_Dense, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<64> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createDenseBitset<64>(i + 22222);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_64_Random, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<64> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createRandomBitset<64>(i + 33333);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_256_Sparse, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<256> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createSparseBitset<256>(i + 44444);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_256_Dense, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<256> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createDenseBitset<256>(i + 55555);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_256_Random, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<256> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createRandomBitset<256>(i + 66666);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_1024_Sparse, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<1024> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createSparseBitset<1024>(i + 77777);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_1024_Dense, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<1024> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createDenseBitset<1024>(i + 88888);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetFindNext_1024_Random, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<1024> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createRandomBitset<1024>(i + 99999);\n    }\n    size_t pos = 0;\n    size_t count = 0;\n    while (pos < bitset.size() && count < 10) {\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n      if (pos < bitset.size()) {\n        ++pos;\n        ++count;\n      }\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nBENCHMARK(StdBitsetIterateAllBits_1024, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    std::bitset<1024> bitset;\n    BENCHMARK_SUSPEND {\n      bitset = createRandomBitset<1024>(i);\n    }\n\n    size_t count = 0;\n    size_t pos = std_bitset_find_first(bitset);\n    makeUnpredictable(pos);\n    while (pos < bitset.size()) {\n      ++count;\n      pos = std_bitset_find_next(bitset, pos);\n      makeUnpredictable(pos);\n    }\n    doNotOptimizeAway(count);\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/container/test/StdBitsetTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/StdBitset.h>\n\n#include <gtest/gtest.h>\n\n#include <bitset>\n\nusing namespace ::testing;\n\nTEST(BitSetTest, FindFirst) {\n  {\n    std::string bit_string = \"110010\";\n    std::bitset<8> objectUnderTest(bit_string); // [0,0,1,1,0,0,1,0]\n\n    size_t actual{folly::std_bitset_find_first(objectUnderTest)};\n    size_t expected{1};\n    ASSERT_EQ(expected, actual);\n  }\n\n  {\n    std::bitset<8> objectUnderTest; // [0,0,0,0,0,0,0,0]\n\n    size_t actual{folly::std_bitset_find_first(objectUnderTest)};\n    size_t expected{8};\n    ASSERT_EQ(expected, actual);\n  }\n}\n\nTEST(BitSetTest, FindNext) {\n  std::string bit_string = \"110010\";\n  std::bitset<8> objectUnderTest(bit_string); // [0,0,1,1,0,0,1,0]\n\n  {\n    size_t actual{folly::std_bitset_find_next(objectUnderTest, 0)};\n    size_t expected{1};\n    ASSERT_EQ(expected, actual);\n  }\n\n  {\n    size_t actual{folly::std_bitset_find_next(objectUnderTest, 1)};\n    size_t expected{4};\n    ASSERT_EQ(expected, actual);\n  }\n\n  {\n    size_t actual{folly::std_bitset_find_next(objectUnderTest, 2)};\n    size_t expected{4};\n    ASSERT_EQ(expected, actual);\n  }\n\n  {\n    size_t actual{folly::std_bitset_find_next(objectUnderTest, 4)};\n    size_t expected{5};\n    ASSERT_EQ(expected, actual);\n  }\n\n  {\n    size_t actual{folly::std_bitset_find_next(objectUnderTest, 5)};\n    size_t expected{8};\n    ASSERT_EQ(expected, actual);\n  }\n}\n"
  },
  {
    "path": "folly/container/test/TrackingTypes.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <ostream>\n\n#include <folly/Function.h>\n#include <folly/hash/Hash.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/portability/Asm.h>\n\nnamespace folly {\nnamespace test {\n\nstruct MoveOnlyTestInt {\n  int x;\n  bool destroyed{false};\n\n  MoveOnlyTestInt() noexcept : x(0) {}\n  /* implicit */ MoveOnlyTestInt(int x0) : x(x0) {}\n  MoveOnlyTestInt(MoveOnlyTestInt&& rhs) noexcept : x(rhs.x) {}\n  MoveOnlyTestInt(MoveOnlyTestInt const&) = delete;\n  MoveOnlyTestInt& operator=(MoveOnlyTestInt&& rhs) noexcept {\n    FOLLY_SAFE_CHECK(!rhs.destroyed, \"\");\n    x = rhs.x;\n    return *this;\n  }\n  MoveOnlyTestInt& operator=(MoveOnlyTestInt const&) = delete;\n\n  ~MoveOnlyTestInt() {\n    FOLLY_SAFE_CHECK(!destroyed, \"\");\n    destroyed = true;\n    asm_volatile_memory(); // try to keep compiler from eliding the store\n  }\n\n  bool operator==(MoveOnlyTestInt const& rhs) const {\n    FOLLY_SAFE_CHECK(!destroyed, \"\");\n    FOLLY_SAFE_CHECK(!rhs.destroyed, \"\");\n    return x == rhs.x && destroyed == rhs.destroyed;\n  }\n  bool operator!=(MoveOnlyTestInt const& rhs) const { return !(*this == rhs); }\n};\n\nstruct ThrowOnCopyTestInt {\n  int x{0};\n\n  ThrowOnCopyTestInt() {}\n\n  [[noreturn]] ThrowOnCopyTestInt(const ThrowOnCopyTestInt& other)\n      : x(other.x) {\n    throw std::exception{};\n  }\n\n  ThrowOnCopyTestInt& operator=(const ThrowOnCopyTestInt&) {\n    throw std::exception{};\n  }\n\n  bool operator==(const ThrowOnCopyTestInt& other) const {\n    return x == other.x;\n  }\n\n  bool operator!=(const ThrowOnCopyTestInt& other) const {\n    return !(x == other.x);\n  }\n};\n\nstruct PermissiveConstructorTestInt {\n  int x;\n\n  PermissiveConstructorTestInt() noexcept : x(0) {}\n  /* implicit */ PermissiveConstructorTestInt(int x0) : x(x0) {}\n\n  template <typename T>\n  /* implicit */ PermissiveConstructorTestInt(T&& src)\n      : x(std::forward<T>(src)) {}\n\n  PermissiveConstructorTestInt(PermissiveConstructorTestInt&& rhs) noexcept\n      : x(rhs.x) {}\n  PermissiveConstructorTestInt(PermissiveConstructorTestInt const&) = delete;\n  PermissiveConstructorTestInt& operator=(\n      PermissiveConstructorTestInt&& rhs) noexcept {\n    x = rhs.x;\n    return *this;\n  }\n  PermissiveConstructorTestInt& operator=(PermissiveConstructorTestInt const&) =\n      delete;\n\n  bool operator==(PermissiveConstructorTestInt const& rhs) const {\n    return x == rhs.x;\n  }\n  bool operator!=(PermissiveConstructorTestInt const& rhs) const {\n    return !(*this == rhs);\n  }\n};\n\n// Tracked is implicitly constructible across tags\nstruct Counts {\n  uint64_t copyConstruct{0};\n  uint64_t moveConstruct{0};\n  uint64_t copyConvert{0};\n  uint64_t moveConvert{0};\n  uint64_t copyAssign{0};\n  uint64_t moveAssign{0};\n  uint64_t defaultConstruct{0};\n  uint64_t destroyed{0};\n\n  explicit Counts(\n      uint64_t copConstr = 0,\n      uint64_t movConstr = 0,\n      uint64_t copConv = 0,\n      uint64_t movConv = 0,\n      uint64_t copAssign = 0,\n      uint64_t movAssign = 0,\n      uint64_t def = 0,\n      uint64_t destr = 0)\n      : copyConstruct{copConstr},\n        moveConstruct{movConstr},\n        copyConvert{copConv},\n        moveConvert{movConv},\n        copyAssign{copAssign},\n        moveAssign{movAssign},\n        defaultConstruct{def},\n        destroyed{destr} {}\n\n  int64_t liveCount() const {\n    return copyConstruct + moveConstruct + copyConvert + moveConvert +\n        defaultConstruct - destroyed;\n  }\n\n  // dist ignores destroyed count\n  uint64_t dist(Counts const& rhs) const {\n    auto d = [](uint64_t x, uint64_t y) { return (x - y) * (x - y); };\n    return d(copyConstruct, rhs.copyConstruct) +\n        d(moveConstruct, rhs.moveConstruct) + d(copyConvert, rhs.copyConvert) +\n        d(moveConvert, rhs.moveConvert) + d(copyAssign, rhs.copyAssign) +\n        d(moveAssign, rhs.moveAssign) +\n        d(defaultConstruct, rhs.defaultConstruct);\n  }\n\n  bool operator==(Counts const& rhs) const {\n    return dist(rhs) == 0 && destroyed == rhs.destroyed;\n  }\n  bool operator!=(Counts const& rhs) const { return !(*this == rhs); }\n};\n\ninline std::ostream& operator<<(std::ostream& xo, Counts const& counts) {\n  xo << \"[\";\n  std::string glue;\n  if (counts.copyConstruct > 0) {\n    xo << glue << counts.copyConstruct << \" copy\";\n    glue = \", \";\n  }\n  if (counts.moveConstruct > 0) {\n    xo << glue << counts.moveConstruct << \" move\";\n    glue = \", \";\n  }\n  if (counts.copyConvert > 0) {\n    xo << glue << counts.copyConvert << \" copy convert\";\n    glue = \", \";\n  }\n  if (counts.moveConvert > 0) {\n    xo << glue << counts.moveConvert << \" move convert\";\n    glue = \", \";\n  }\n  if (counts.copyAssign > 0) {\n    xo << glue << counts.copyAssign << \" copy assign\";\n    glue = \", \";\n  }\n  if (counts.moveAssign > 0) {\n    xo << glue << counts.moveAssign << \" move assign\";\n    glue = \", \";\n  }\n  if (counts.defaultConstruct > 0) {\n    xo << glue << counts.defaultConstruct << \" default construct\";\n    glue = \", \";\n  }\n  if (counts.destroyed > 0) {\n    xo << glue << counts.destroyed << \" destroyed\";\n    glue = \", \";\n  }\n  xo << \"]\";\n  return xo;\n}\n\ninline Counts& sumCounts() {\n  static thread_local Counts value{};\n  return value;\n}\n\ntemplate <int Tag>\nstruct Tracked {\n  static_assert(Tag <= 5, \"Need to extend Tracked<Tag> in TestUtil.cpp\");\n\n  static Counts& counts() {\n    static thread_local Counts value{};\n    return value;\n  }\n\n  uint64_t val_;\n\n  Tracked() : val_{0} {\n    sumCounts().defaultConstruct++;\n    counts().defaultConstruct++;\n  }\n  /* implicit */ Tracked(uint64_t const& val) : val_{val} {\n    sumCounts().copyConvert++;\n    counts().copyConvert++;\n  }\n  /* implicit */ Tracked(uint64_t&& val) : val_{val} {\n    sumCounts().moveConvert++;\n    counts().moveConvert++;\n  }\n  Tracked(Tracked const& rhs) : val_{rhs.val_} {\n    sumCounts().copyConstruct++;\n    counts().copyConstruct++;\n  }\n  Tracked(Tracked&& rhs) noexcept : val_{rhs.val_} {\n    sumCounts().moveConstruct++;\n    counts().moveConstruct++;\n  }\n  Tracked& operator=(Tracked const& rhs) {\n    val_ = rhs.val_;\n    sumCounts().copyAssign++;\n    counts().copyAssign++;\n    return *this;\n  }\n  Tracked& operator=(Tracked&& rhs) noexcept {\n    val_ = rhs.val_;\n    sumCounts().moveAssign++;\n    counts().moveAssign++;\n    return *this;\n  }\n\n  template <int T>\n  /* implicit */ Tracked(Tracked<T> const& rhs) : val_{rhs.val_} {\n    sumCounts().copyConvert++;\n    counts().copyConvert++;\n  }\n\n  template <int T>\n  /* implicit */ Tracked(Tracked<T>&& rhs) : val_{rhs.val_} {\n    sumCounts().moveConvert++;\n    counts().moveConvert++;\n  }\n\n  ~Tracked() {\n    sumCounts().destroyed++;\n    counts().destroyed++;\n  }\n\n  bool operator==(Tracked const& rhs) const { return val_ == rhs.val_; }\n  bool operator!=(Tracked const& rhs) const { return !(*this == rhs); }\n};\n\ntemplate <int Tag>\nstruct TransparentTrackedHash {\n  using is_transparent = void;\n\n  size_t operator()(Tracked<Tag> const& tracked) const {\n    return tracked.val_ ^ Tag;\n  }\n  size_t operator()(uint64_t v) const { return v ^ Tag; }\n};\n\ntemplate <int Tag>\nstruct TransparentTrackedEqual {\n  using is_transparent = void;\n\n  uint64_t unwrap(Tracked<Tag> const& v) const { return v.val_; }\n  uint64_t unwrap(uint64_t v) const { return v; }\n\n  template <typename A, typename B>\n  bool operator()(A const& lhs, B const& rhs) const {\n    return unwrap(lhs) == unwrap(rhs);\n  }\n};\n\ninline size_t& testAllocatedMemorySize() {\n  static thread_local size_t value{0};\n  return value;\n}\n\ninline size_t& testAllocatedBlockCount() {\n  static thread_local size_t value{0};\n  return value;\n}\n\ninline size_t& testAllocationCount() {\n  static thread_local size_t value{0};\n  return value;\n}\n\ninline size_t& testAllocationMaxCount() {\n  static thread_local size_t value{std::numeric_limits<std::size_t>::max()};\n  return value;\n}\n\ninline void limitTestAllocations(std::size_t allocationsBeforeException = 0) {\n  testAllocationMaxCount() = testAllocationCount() + allocationsBeforeException;\n}\n\ninline void unlimitTestAllocations() {\n  testAllocationMaxCount() = std::numeric_limits<std::size_t>::max();\n}\n\ninline void resetTracking() {\n  sumCounts() = Counts{};\n  Tracked<0>::counts() = Counts{};\n  Tracked<1>::counts() = Counts{};\n  Tracked<2>::counts() = Counts{};\n  Tracked<3>::counts() = Counts{};\n  Tracked<4>::counts() = Counts{};\n  Tracked<5>::counts() = Counts{};\n  testAllocatedMemorySize() = 0;\n  testAllocatedBlockCount() = 0;\n  testAllocationCount() = 0;\n  testAllocationMaxCount() = std::numeric_limits<std::size_t>::max();\n}\n\ntemplate <class T>\nclass SwapTrackingAlloc {\n public:\n  using Alloc = std::allocator<T>;\n  using AllocTraits = std::allocator_traits<Alloc>;\n  using value_type = typename AllocTraits::value_type;\n\n  using pointer = typename AllocTraits::pointer;\n  using const_pointer = typename AllocTraits::const_pointer;\n  using reference = value_type&;\n  using const_reference = value_type const&;\n  using size_type = typename AllocTraits::size_type;\n\n  using propagate_on_container_swap = std::true_type;\n  using propagate_on_container_copy_assignment = std::true_type;\n  using propagate_on_container_move_assignment = std::true_type;\n\n  SwapTrackingAlloc() {}\n\n  template <class U>\n  /* implicit */ SwapTrackingAlloc(SwapTrackingAlloc<U> const& other) noexcept\n      : a_(other.a_), t_(other.t_) {}\n\n  template <class U>\n  SwapTrackingAlloc& operator=(SwapTrackingAlloc<U> const& other) noexcept {\n    a_ = other.a_;\n    t_ = other.t_;\n    return *this;\n  }\n\n  template <class U>\n  /* implicit */ SwapTrackingAlloc(SwapTrackingAlloc<U>&& other) noexcept\n      : a_(std::move(other.a_)), t_(std::move(other.t_)) {}\n\n  template <class U>\n  SwapTrackingAlloc& operator=(SwapTrackingAlloc<U>&& other) noexcept {\n    a_ = std::move(other.a_);\n    t_ = std::move(other.t_);\n    return *this;\n  }\n\n  T* allocate(size_t n) {\n    if (testAllocationCount() >= testAllocationMaxCount()) {\n      throw std::bad_alloc();\n    }\n    ++testAllocationCount();\n    testAllocatedMemorySize() += n * sizeof(T);\n    ++testAllocatedBlockCount();\n    std::size_t extra =\n        std::max<std::size_t>(1, sizeof(std::size_t) / sizeof(T));\n    T* p = a_.allocate(extra + n);\n    void* raw = static_cast<void*>(p);\n    *static_cast<std::size_t*>(raw) = n;\n    return p + extra;\n  }\n  void deallocate(T* p, size_t n) {\n    testAllocatedMemorySize() -= n * sizeof(T);\n    --testAllocatedBlockCount();\n    std::size_t extra =\n        std::max<std::size_t>(1, sizeof(std::size_t) / sizeof(T));\n    std::size_t check;\n    void* raw = static_cast<void*>(p - extra);\n    check = *static_cast<std::size_t*>(raw);\n    FOLLY_SAFE_CHECK(check == n, \"\");\n    a_.deallocate(p - extra, n + extra);\n  }\n\n private:\n  std::allocator<T> a_;\n  Tracked<0> t_;\n\n  template <class U>\n  friend class SwapTrackingAlloc;\n};\n\ntemplate <class T>\nvoid swap(SwapTrackingAlloc<T>&, SwapTrackingAlloc<T>&) noexcept {\n  // For argument dependent lookup:\n  // This function will be called if the custom swap functions of a container\n  // is used. Otherwise, std::swap() will do 1 move construct and 2 move\n  // assigns which will get tracked by t_.\n}\n\ntemplate <class T1, class T2>\nbool operator==(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {\n  return true;\n}\n\ntemplate <class T1, class T2>\nbool operator!=(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {\n  return false;\n}\n\ntemplate <class T>\nclass GenericAlloc {\n public:\n  using value_type = T;\n\n  using pointer = T*;\n  using const_pointer = T const*;\n  using reference = T&;\n  using const_reference = T const&;\n  using size_type = std::size_t;\n\n  using propagate_on_container_swap = std::true_type;\n  using propagate_on_container_copy_assignment = std::true_type;\n  using propagate_on_container_move_assignment = std::true_type;\n\n  using AllocBytesFunc = folly::Function<void*(std::size_t)>;\n  using DeallocBytesFunc = folly::Function<void(void*, std::size_t)>;\n\n  GenericAlloc() = delete;\n\n  template <typename A, typename D>\n  GenericAlloc(A&& alloc, D&& dealloc)\n      : alloc_{std::make_shared<AllocBytesFunc>(std::forward<A>(alloc))},\n        dealloc_{std::make_shared<DeallocBytesFunc>(std::forward<D>(dealloc))} {\n  }\n\n  template <class U>\n  /* implicit */ GenericAlloc(GenericAlloc<U> const& other) noexcept\n      : alloc_{other.alloc_}, dealloc_{other.dealloc_} {}\n\n  template <class U>\n  GenericAlloc& operator=(GenericAlloc<U> const& other) noexcept {\n    alloc_ = other.alloc_;\n    dealloc_ = other.dealloc_;\n    return *this;\n  }\n\n  template <class U>\n  /* implicit */ GenericAlloc(GenericAlloc<U>&& other) noexcept\n      : alloc_(std::move(other.alloc_)), dealloc_(std::move(other.dealloc_)) {}\n\n  template <class U>\n  GenericAlloc& operator=(GenericAlloc<U>&& other) noexcept {\n    alloc_ = std::move(other.alloc_);\n    dealloc_ = std::move(other.dealloc_);\n    return *this;\n  }\n\n  T* allocate(size_t n) { return static_cast<T*>((*alloc_)(n * sizeof(T))); }\n  void deallocate(T* p, size_t n) {\n    (*dealloc_)(static_cast<void*>(p), n * sizeof(T));\n  }\n\n  template <typename U>\n  bool operator==(GenericAlloc<U> const& rhs) const {\n    return alloc_ == rhs.alloc_;\n  }\n\n  template <typename U>\n  bool operator!=(GenericAlloc<U> const& rhs) const {\n    return !(*this == rhs);\n  }\n\n private:\n  std::shared_ptr<AllocBytesFunc> alloc_;\n  std::shared_ptr<DeallocBytesFunc> dealloc_;\n\n  template <class U>\n  friend class GenericAlloc;\n};\n\ntemplate <typename T>\nclass GenericEqual {\n public:\n  using EqualFunc = folly::Function<bool(T const&, T const&)>;\n\n  GenericEqual() = delete;\n\n  template <typename E>\n  /* implicit */ GenericEqual(E&& equal)\n      : equal_{std::make_shared<EqualFunc>(std::forward<E>(equal))} {}\n\n  bool operator()(T const& lhs, T const& rhs) const {\n    return (*equal_)(lhs, rhs);\n  }\n\n private:\n  std::shared_ptr<EqualFunc> equal_;\n};\n\ntemplate <typename T>\nclass GenericHasher {\n public:\n  using HasherFunc = folly::Function<std::size_t(T const&)>;\n\n  GenericHasher() = delete;\n\n  template <typename H>\n  /* implicit */ GenericHasher(H&& hasher)\n      : hasher_{std::make_shared<HasherFunc>(std::forward<H>(hasher))} {}\n\n  std::size_t operator()(T const& val) const { return (*hasher_)(val); }\n\n private:\n  std::shared_ptr<HasherFunc> hasher_;\n};\n\nstruct HashFirst {\n  template <typename P>\n  std::size_t operator()(P const& p) const {\n    return folly::Hash{}(p.first);\n  }\n};\n\nstruct EqualFirst {\n  template <typename P>\n  bool operator()(P const& lhs, P const& rhs) const {\n    return lhs.first == rhs.first;\n  }\n};\n\n} // namespace test\n} // namespace folly\n\nnamespace std {\ntemplate <>\nstruct hash<folly::test::MoveOnlyTestInt> {\n  std::size_t operator()(folly::test::MoveOnlyTestInt const& val) const {\n    FOLLY_SAFE_CHECK(!val.destroyed, \"\");\n    return val.x;\n  }\n};\n\ntemplate <>\nstruct hash<folly::test::ThrowOnCopyTestInt> {\n  std::size_t operator()(folly::test::ThrowOnCopyTestInt const& val) const {\n    return val.x;\n  }\n};\n\ntemplate <>\nstruct hash<folly::test::PermissiveConstructorTestInt> {\n  std::size_t operator()(\n      folly::test::PermissiveConstructorTestInt const& val) const {\n    return val.x;\n  }\n};\n\ntemplate <int Tag>\nstruct hash<folly::test::Tracked<Tag>> {\n  size_t operator()(folly::test::Tracked<Tag> const& tracked) const {\n    return tracked.val_ ^ Tag;\n  }\n};\n} // namespace std\n"
  },
  {
    "path": "folly/container/test/UtilTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/detail/Util.h>\n\n#include <glog/logging.h>\n\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/container/test/TrackingTypes.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::test;\n\nnamespace folly {\n\nTEST(Tracked, baseline) {\n  // this is a test that Tracked works like we expect\n  Tracked<0> a0;\n\n  {\n    resetTracking();\n    Tracked<0> b0{a0};\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{1, 0, 0, 0}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{1, 0, 0, 0}));\n  }\n  {\n    resetTracking();\n    Tracked<0> b0{std::move(a0)};\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 1, 0, 0}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{0, 1, 0, 0}));\n  }\n  {\n    resetTracking();\n    Tracked<1> b1{a0};\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 1, 0}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 1, 0}));\n  }\n  {\n    resetTracking();\n    Tracked<1> b1{std::move(a0)};\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 1}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 0, 1}));\n  }\n  {\n    Tracked<0> b0;\n    resetTracking();\n    b0 = a0;\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 0, 1, 0}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{0, 0, 0, 0, 1, 0}));\n  }\n  {\n    Tracked<0> b0;\n    resetTracking();\n    b0 = std::move(a0);\n    EXPECT_EQ(a0.val_, b0.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 0, 0, 1}));\n    EXPECT_EQ(Tracked<0>::counts(), (Counts{0, 0, 0, 0, 0, 1}));\n  }\n  {\n    Tracked<1> b1;\n    resetTracking();\n    b1 = a0;\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 1, 0, 0, 1, 0, 1}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 1, 0, 0, 1, 0, 1}));\n  }\n  {\n    Tracked<1> b1;\n    resetTracking();\n    b1 = std::move(a0);\n    EXPECT_EQ(a0.val_, b1.val_);\n    EXPECT_EQ(sumCounts(), (Counts{0, 0, 0, 1, 0, 1, 0, 1}));\n    EXPECT_EQ(Tracked<1>::counts(), (Counts{0, 0, 0, 1, 0, 1, 0, 1}));\n  }\n}\n\n// F should take a templatized func and a pair const& or pair&& and\n// call the function using callWithExtractedKey\ntemplate <typename F>\nvoid runKeyExtractCases(\n    std::string const& name, F const& func, uint64_t expectedDist = 0) {\n  Optional<std::pair<Tracked<0> const, Tracked<1>>> sink;\n  auto sinkFunc = [&sink](Tracked<0> const& key, auto&&... args) {\n    if (!sink.hasValue() || sink.value().first != key) {\n      sink.emplace(std::forward<decltype(args)>(args)...);\n    }\n  };\n\n  {\n    std::pair<Tracked<0> const, Tracked<1>> p{0, 0};\n    sink.reset();\n    resetTracking();\n    func(sinkFunc, p);\n    // fresh key, value_type const& ->\n    // copy is expected\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{1, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{1, 0, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    std::pair<Tracked<0> const, Tracked<1>> p{0, 0};\n    sink.reset();\n    resetTracking();\n    func(sinkFunc, std::move(p));\n    // fresh key, value_type&& ->\n    // key copy is unfortunate but required\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{1, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 1, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    sink.reset();\n    resetTracking();\n    func(sinkFunc, p);\n    // fresh key, pair<key_type,mapped_type> const& ->\n    // 1 copy is required\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{1, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{1, 0, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    sink.reset();\n    resetTracking();\n    func(sinkFunc, std::move(p));\n    // fresh key, pair<key_type,mapped_type>&& ->\n    // this is the happy path for insert(make_pair(.., ..))\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 1, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 1, 0, 0}),\n        expectedDist)\n        << name << \"\\n0 -> \" << Tracked<0>::counts() << \"\\n1 -> \"\n        << Tracked<1>::counts();\n  }\n  {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    sink.reset();\n    resetTracking();\n    func(sinkFunc, p);\n    // fresh key, convertible const& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts();\n\n    // There are three strategies that could be optimal for particular\n    // ratios of cost:\n    //\n    // - convert key and value in place to final position, destroy if\n    //   insert fails. This is the strategy used by std::unordered_map\n    //   and FBHashMap\n    //\n    // - convert key and default value in place to final position,\n    //   convert value only if insert succeeds.  Nobody uses this strategy\n    //\n    // - convert key to a temporary, move key and convert value if\n    //   insert succeeds.  This is the strategy used by F14 and what is\n    //   EXPECT_EQ here.\n\n    // The expectedDist * 3 is just a hack for the emplace-pieces-by-value\n    // test, whose test harness copies the original pair and then uses\n    // move conversion instead of copy conversion.\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 1, 1, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 1, 0}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist * 3);\n  }\n  {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    sink.reset();\n    resetTracking();\n    func(sinkFunc, std::move(p));\n    // fresh key, convertible&& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts();\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 1, 0, 1}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 1}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  {\n    std::pair<Tracked<0> const, Tracked<1>> p{0, 0};\n    sink.reset();\n    sink.emplace(0, 0);\n    resetTracking();\n    func(sinkFunc, p);\n    // duplicate key, value_type const&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  {\n    std::pair<Tracked<0> const, Tracked<1>> p{0, 0};\n    sink.reset();\n    sink.emplace(0, 0);\n    resetTracking();\n    func(sinkFunc, std::move(p));\n    // duplicate key, value_type&&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    sink.reset();\n    sink.emplace(0, 0);\n    resetTracking();\n    func(sinkFunc, p);\n    // duplicate key, pair<key_type,mapped_type> const&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  {\n    std::pair<Tracked<0>, Tracked<1>> p{0, 0};\n    sink.reset();\n    sink.emplace(0, 0);\n    resetTracking();\n    func(sinkFunc, std::move(p));\n    // duplicate key, pair<key_type,mapped_type>&&\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n  {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    sink.reset();\n    sink.emplace(0, 0);\n    resetTracking();\n    func(sinkFunc, p);\n    // duplicate key, convertible const& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts();\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 1, 0}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist * 2);\n  }\n  {\n    std::pair<Tracked<2>, Tracked<3>> p{0, 0};\n    sink.reset();\n    sink.emplace(0, 0);\n    resetTracking();\n    func(sinkFunc, std::move(p));\n    // duplicate key, convertible&& ->\n    //   key_type ops: Tracked<0>::counts\n    //   mapped_type ops: Tracked<1>::counts\n    //   key_src ops: Tracked<2>::counts\n    //   mapped_src ops: Tracked<3>::counts();\n    EXPECT_EQ(\n        Tracked<0>::counts().dist(Counts{0, 0, 0, 1}) +\n            Tracked<1>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<2>::counts().dist(Counts{0, 0, 0, 0}) +\n            Tracked<3>::counts().dist(Counts{0, 0, 0, 0}),\n        expectedDist);\n  }\n}\n\nstruct DoEmplace1 {\n  template <typename F, typename P>\n  void operator()(F&& f, P&& p) const {\n    std::allocator<char> a;\n    detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n        a, std::forward<F>(f), std::forward<P>(p));\n  }\n};\n\nstruct DoEmplace2 {\n  template <typename F, typename U1, typename U2>\n  void operator()(F&& f, std::pair<U1, U2> const& p) const {\n    std::allocator<char> a;\n    detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n        a, std::forward<F>(f), p.first, p.second);\n  }\n\n  template <typename F, typename U1, typename U2>\n  void operator()(F&& f, std::pair<U1, U2>&& p) const {\n    std::allocator<char> a;\n    detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n        a, std::forward<F>(f), std::move(p.first), std::move(p.second));\n  }\n};\n\nstruct DoEmplace3 {\n  template <typename F, typename U1, typename U2>\n  void operator()(F&& f, std::pair<U1, U2> const& p) const {\n    std::allocator<char> a;\n    detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n        a,\n        std::forward<F>(f),\n        std::piecewise_construct,\n        std::forward_as_tuple(p.first),\n        std::forward_as_tuple(p.second));\n  }\n\n  template <typename F, typename U1, typename U2>\n  void operator()(F&& f, std::pair<U1, U2>&& p) const {\n    std::allocator<char> a;\n    detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n        a,\n        std::forward<F>(f),\n        std::piecewise_construct,\n        std::forward_as_tuple(std::move(p.first)),\n        std::forward_as_tuple(std::move(p.second)));\n  }\n};\n\n// Simulates use of piecewise_construct without proper use of\n// forward_as_tuple.  This code doesn't yield the normal pattern, but\n// it should have exactly 1 additional move or copy of the key and 1\n// additional move or copy of the mapped value.\nstruct DoEmplace3Value {\n  template <typename F, typename U1, typename U2>\n  void operator()(F&& f, std::pair<U1, U2> const& p) const {\n    std::allocator<char> a;\n    detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n        a,\n        std::forward<F>(f),\n        std::piecewise_construct,\n        std::tuple<U1>{p.first},\n        std::tuple<U2>{p.second});\n  }\n\n  template <typename F, typename U1, typename U2>\n  void operator()(F&& f, std::pair<U1, U2>&& p) const {\n    std::allocator<char> a;\n    detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n        a,\n        std::forward<F>(f),\n        std::piecewise_construct,\n        std::tuple<U1>{std::move(p.first)},\n        std::tuple<U2>{std::move(p.second)});\n  }\n};\n\nTEST(Util, callWithExtractedKey) {\n  runKeyExtractCases(\"emplace pair\", DoEmplace1{});\n  runKeyExtractCases(\"emplace k,v\", DoEmplace2{});\n  runKeyExtractCases(\"emplace pieces\", DoEmplace3{});\n  runKeyExtractCases(\"emplace pieces by value\", DoEmplace3Value{}, 2);\n\n  // Calling the default pair constructor via emplace is valid, but not\n  // very useful in real life.  Verify that it works.\n  std::allocator<char> a;\n  detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(\n      a, [](Tracked<0> const& key, auto&&... args) {\n        EXPECT_TRUE(key == 0);\n        std::pair<Tracked<0> const, Tracked<1>> p(\n            std::forward<decltype(args)>(args)...);\n        EXPECT_TRUE(p.first == 0);\n        EXPECT_TRUE(p.second == 0);\n      });\n}\n\nTEST(Util, callWithConstructedKey) {\n  Optional<Tracked<0>> sink;\n  auto sinkFunc = [&](Tracked<0> const& key, auto&&... args) {\n    if (!sink.hasValue()) {\n      sink.emplace(std::forward<decltype(args)>(args)...);\n    } else {\n      EXPECT_TRUE(sink.value() == key);\n    }\n  };\n  std::allocator<char> a;\n\n  {\n    Tracked<0> k1{0};\n    Tracked<0> k2{0};\n    uint64_t k3 = 0;\n    sink.reset();\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k1);\n    // copy is expected on successful emplace\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{1, 0, 0, 0}), 0);\n\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k2);\n    // no copies or moves on failing emplace with value_type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0);\n\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k3);\n    // copy convert expected for failing emplace with wrong type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 1, 0}), 0);\n\n    sink.reset();\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k3);\n    // copy convert + move expected for successful emplace with wrong type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 1, 0}), 0);\n  }\n  {\n    Tracked<0> k1{0};\n    Tracked<0> k2{0};\n    uint64_t k3 = 0;\n    sink.reset();\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k1));\n    // move is expected on successful emplace\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 0}), 0);\n\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k2));\n    // no copies or moves on failing emplace with value_type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0);\n\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k3));\n    // move convert expected for failing emplace with wrong type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 1}), 0);\n\n    sink.reset();\n    resetTracking();\n    detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k3));\n    // move convert + move expected for successful emplace with wrong type\n    EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 1}), 0);\n  }\n\n  // Calling the default pair constructor via emplace is valid, but not\n  // very useful in real life.  Verify that it works.\n  sink.reset();\n  detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc);\n  EXPECT_TRUE(sink.has_value());\n}\n\n// We're deliberately allowing only a subset of the desired heterogeneous\n// string behaviors with this functor so that we can verify that\n// conversions will still be applied if the heterogeneous test fails.\ntemplate <typename T>\nusing IsStringPiece = std::is_same<T, StringPiece>;\n\ntemplate <typename KeyType, typename Arg1, typename Arg2>\nstruct ExpectArgTypes {\n  int which{0};\n  KeyType const* expectedAddr{nullptr};\n\n  template <typename K, typename... Args>\n  void operator()(K const&, Args&&... args) {\n    // avoid static_assert to ensure we don't affect SFINAE\n    EXPECT_TRUE((std::is_same<K, KeyType>::value)) << which;\n    using T = std::tuple<Args&&...>;\n    EXPECT_EQ(std::tuple_size<T>::value, 2) << which;\n    EXPECT_TRUE((std::is_same<std::tuple_element_t<0, T>, Arg1>::value))\n        << which;\n    EXPECT_TRUE((std::is_same<std::tuple_element_t<1, T>, Arg2>::value))\n        << which;\n\n    auto t = std::forward_as_tuple(std::forward<Args>(args)...);\n    EXPECT_TRUE(expectedAddr == nullptr || expectedAddr == &std::get<0>(t))\n        << which;\n  }\n};\n\nTEST(Util, callWithExtractedHeterogeneousKey) {\n  std::allocator<char> a;\n  std::string str{\"key\"};\n  StringPiece sp{\"key\"};\n  char const* ptr{\"key\"};\n  auto strPair = std::make_pair(str, 0);\n  std::pair<std::string&, int> strLRefPair(str, 0);\n  std::pair<std::string&&, int> strRRefPair(std::move(str), 0);\n  auto spPair = std::make_pair(sp, 0);\n  auto ptrPair = std::make_pair(ptr, 0);\n\n  // none of the std::move below actually get consumed\n\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a, ExpectArgTypes<std::string, std::string&, int&&>{0, &str}, str, 0);\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<std::string, std::string const&, int const&>{\n          1, &strPair.first},\n      strPair);\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<std::string, std::string&, int&&>{2, &str},\n      std::move(strLRefPair));\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<std::string, std::string&, int const&>{10, &str},\n      strLRefPair);\n\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<std::string, std::string&&, int&&>{3, &str},\n      std::move(str),\n      0);\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<std::string, std::string&&, int&&>{4, &strPair.first},\n      std::move(strPair));\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<std::string, std::string&, int const&>{5, &str},\n      strRRefPair);\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<std::string, std::string&&, int&&>{11, &str},\n      std::move(strRRefPair));\n\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a, ExpectArgTypes<StringPiece, StringPiece&, int&&>{6, &sp}, sp, 0);\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a,\n      ExpectArgTypes<StringPiece, StringPiece const&, int const&>{\n          7, &spPair.first},\n      spPair);\n\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a, ExpectArgTypes<std::string, std::string&&, int&&>{8}, ptr, 0);\n  detail::callWithExtractedKey<std::string, int, IsStringPiece>(\n      a, ExpectArgTypes<std::string, std::string&&, int const&>{9}, ptrPair);\n}\n\nTEST(Util, callWithConstructedHeterogeneousKey) {\n  std::allocator<char> a;\n  std::string str{\"key\"};\n  StringPiece sp{\"key\"};\n  char const* ptr{\"key\"};\n  detail::callWithConstructedKey<std::string, IsStringPiece>(\n      a,\n      [](auto const& key, auto&&... args) {\n        // avoid static_assert to ensure we don't affect SFINAE\n        EXPECT_TRUE((std::is_same<decltype(key), std::string const&>::value));\n        using T = std::tuple<decltype(args)&&...>;\n        EXPECT_EQ(std::tuple_size<T>::value, 1);\n        EXPECT_TRUE(\n            (std::is_same<std::tuple_element_t<0, T>, std::string&>::value));\n      },\n      str);\n  detail::callWithConstructedKey<std::string, IsStringPiece>(\n      a,\n      [](auto const& key, auto&&... args) {\n        EXPECT_TRUE((std::is_same<decltype(key), std::string const&>::value));\n        using T = std::tuple<decltype(args)&&...>;\n        EXPECT_EQ(std::tuple_size<T>::value, 1);\n        EXPECT_TRUE(\n            (std::is_same<std::tuple_element_t<0, T>, std::string&&>::value));\n      },\n      std::move(str));\n  detail::callWithConstructedKey<std::string, IsStringPiece>(\n      a,\n      [](auto const& key, auto&&... args) {\n        EXPECT_TRUE((std::is_same<decltype(key), StringPiece const&>::value));\n        using T = std::tuple<decltype(args)&&...>;\n        EXPECT_EQ(std::tuple_size<T>::value, 1);\n        EXPECT_TRUE(\n            (std::is_same<std::tuple_element_t<0, T>, StringPiece&>::value));\n      },\n      sp);\n  detail::callWithConstructedKey<std::string, IsStringPiece>(\n      a,\n      [](auto const& key, auto&&... args) {\n        // avoid static_assert to ensure we don't affect SFINAE\n        EXPECT_TRUE((std::is_same<decltype(key), std::string const&>::value));\n        using T = std::tuple<decltype(args)&&...>;\n        EXPECT_EQ(std::tuple_size<T>::value, 1);\n        EXPECT_TRUE(\n            (std::is_same<std::tuple_element_t<0, T>, std::string&&>::value));\n        auto t = std::forward_as_tuple(std::forward<decltype(args)>(args)...);\n        EXPECT_EQ(&key, &std::get<0>(t));\n      },\n      ptr);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/test/WeightedEvictingCacheMapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/WeightedEvictingCacheMap.h>\n\n#include <string>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nstruct ValueStringLength {\n  std::size_t operator()(const std::string& /*key*/, const std::string& value) {\n    return value.size();\n  }\n};\n\nTEST(ImplicitlyWeightedEvictingCacheMap, Misc) {\n  ImplicitlyWeightedEvictingCacheMap<\n      std::string,\n      std::string,\n      ValueStringLength>\n      map{42};\n\n  auto end = map.end();\n\n  EXPECT_EQ(0, map.size());\n  EXPECT_EQ(0, map.getCurrentTotalWeight());\n  EXPECT_EQ(42, map.getMaxTotalWeight());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_EQ(map.find(\"x\"), end);\n\n  std::string four(\"four\");\n  map.set(\"x\", four);\n\n  EXPECT_EQ(1, map.size());\n  EXPECT_EQ(4, map.getCurrentTotalWeight());\n  EXPECT_FALSE(map.empty());\n\n  EXPECT_EQ(map.get(\"x\"), four);\n  EXPECT_NE(map.find(\"x\"), end);\n  EXPECT_EQ(map.find(\"x\")->second, four);\n\n  EXPECT_TRUE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"y\"));\n\n  std::string twenty(20, '2');\n  map.set(\"y\", twenty);\n\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(24, map.getCurrentTotalWeight());\n\n  EXPECT_EQ(map.get(\"y\"), twenty);\n  EXPECT_NE(map.find(\"y\"), end);\n  EXPECT_EQ(map.find(\"y\")->second, twenty);\n\n  EXPECT_TRUE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n\n  // This time use non-heterogeneous overloads.\n  // Evicts x\n  map.set(std::string(\"z\"), twenty);\n\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(40, map.getCurrentTotalWeight());\n\n  EXPECT_EQ(map.get(std::string(\"z\")), twenty);\n  EXPECT_NE(map.find(std::string(\"z\")), end);\n  EXPECT_EQ(map.find(std::string(\"z\"))->second, twenty);\n\n  EXPECT_FALSE(map.exists(std::string(\"x\")));\n  EXPECT_TRUE(map.exists(std::string(\"y\")));\n  EXPECT_TRUE(map.exists(std::string(\"z\")));\n\n  // And move semantics for value, with and without\n  // heterogeneous key\n  std::string one1(\"a\");\n  map.set(std::string(\"a\"), std::move(one1));\n\n  std::string one2(\"b\");\n  map.set(\"b\", std::move(one2));\n\n  EXPECT_EQ(4, map.size());\n  EXPECT_EQ(42, map.getCurrentTotalWeight());\n\n  // Use y to move it up in LRU list\n  EXPECT_EQ(map.get(\"y\"), twenty);\n\n  // Evicts z\n  map.set(\"c\", \"c\");\n  EXPECT_EQ(4, map.size());\n  EXPECT_EQ(23, map.getCurrentTotalWeight());\n\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n\n  // Can also overwrite a value with set()\n  map.set(\"a\", twenty);\n\n  EXPECT_EQ(4, map.size());\n  EXPECT_EQ(42, map.getCurrentTotalWeight());\n\n  // Which might evict (evicts y)\n  map.set(\"b\", twenty);\n\n  EXPECT_EQ(3, map.size());\n  EXPECT_EQ(41, map.getCurrentTotalWeight());\n\n  // Can also overwrite with replace() (evicts a)\n  map.replace(map.find(\"c\"), twenty);\n\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(40, map.getCurrentTotalWeight());\n\n  // Try erase\n  map.erase(\"c\");\n  EXPECT_EQ(1, map.size());\n  EXPECT_EQ(20, map.getCurrentTotalWeight());\n  EXPECT_FALSE(map.exists(\"c\"));\n  // Re-add\n  map.set(\"c\", twenty);\n\n  // No effect if not present\n  map.erase(\"a\");\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(40, map.getCurrentTotalWeight());\n\n  // Can also evict by setting max total weight (evicts b)\n  map.setMaxTotalWeight(39);\n  EXPECT_EQ(1, map.size());\n  map.setMaxTotalWeight(20);\n  EXPECT_EQ(1, map.size());\n  EXPECT_TRUE(map.exists(\"c\"));\n\n  // Check clear\n  map.clear();\n\n  EXPECT_EQ(0, map.size());\n  EXPECT_EQ(0, map.getCurrentTotalWeight());\n  EXPECT_EQ(20, map.getMaxTotalWeight());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(\"a\"));\n  EXPECT_FALSE(map.exists(\"b\"));\n  EXPECT_FALSE(map.exists(\"c\"));\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n}\n\nTEST(ImplicitlyWeightedEvictingCacheMap, ConstAndMove) {\n  using MyMap = ImplicitlyWeightedEvictingCacheMap<\n      std::string,\n      const std::string,\n      ValueStringLength>;\n  MyMap map{10};\n\n  std::string four(\"four\");\n  map.set(\"x\", four);\n  map.set(\"y\", four);\n  map.set(\"z\", four);\n  EXPECT_EQ(map.size(), 2);\n  EXPECT_EQ(map.get(\"y\"), four);\n  EXPECT_EQ(map.get(\"z\"), four);\n\n  {\n    MyMap map2{100};\n    map2.set(\"xx\", four);\n    map2.set(\"yy\", four);\n    map2.set(\"zz\", four);\n    EXPECT_EQ(map2.size(), 3);\n\n    map = std::move(map2);\n    // Does not swap; moved-from is empty\n    EXPECT_EQ(map2.size(), 0);\n  }\n  EXPECT_EQ(map.size(), 3);\n  EXPECT_EQ(map.get(\"xx\"), four);\n  EXPECT_EQ(map.getMaxTotalWeight(), 100);\n  // Verify prune hook after move\n  map.erase(\"xx\");\n  EXPECT_EQ(map.size(), 2);\n\n  // Move ctor\n  MyMap map3{std::move(map)};\n  EXPECT_EQ(map3.size(), 2);\n  EXPECT_EQ(map.size(), 0);\n}\n\nTEST(ImplicitlyWeightedEvictingCacheMap, Scalars) {\n  struct SumKV {\n    size_t operator()(size_t a, size_t b) { return a + b; }\n  };\n\n  ImplicitlyWeightedEvictingCacheMap<size_t, size_t, SumKV> map{10000};\n  map.set(1, 10);\n  map.set(100, 1000);\n  EXPECT_EQ(1111, map.getCurrentTotalWeight());\n  EXPECT_EQ(map.get(100), 1000);\n}\n\nTEST(ImplicitlyWeightedEvictingCacheMap, NonCopyableAndIterator) {\n  struct DerefUniqueWeight {\n    size_t operator()(\n        const std::string& /*key*/, const std::unique_ptr<size_t>& value) {\n      return *value;\n    }\n  };\n  ImplicitlyWeightedEvictingCacheMap<\n      std::string,\n      std::unique_ptr<size_t>,\n      DerefUniqueWeight>\n      map{6};\n\n  map.set(\"x\", std::make_unique<size_t>(1));\n  map.set(\"y\", std::make_unique<size_t>(2));\n  map.set(\"z\", std::make_unique<size_t>(3));\n\n  auto it = map.begin();\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"z\");\n  EXPECT_EQ(*it->second, 3);\n  ++it;\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"y\");\n  EXPECT_EQ(*it->second, 2);\n  ++it;\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"x\");\n  EXPECT_EQ(*it->second, 1);\n  ++it;\n  EXPECT_EQ(it, map.end());\n\n  it = map.findWithoutPromotion(\"x\");\n  EXPECT_EQ(*map.getWithoutPromotion(\"x\"), 1);\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"x\");\n  EXPECT_EQ(*it->second, 1);\n  ++it;\n  EXPECT_EQ(it, map.end());\n\n  it = map.find(\"x\");\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"x\");\n  EXPECT_EQ(*it->second, 1);\n  ++it;\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"z\");\n  EXPECT_EQ(*it->second, 3);\n  ++it;\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"y\");\n  EXPECT_EQ(*it->second, 2);\n  ++it;\n  EXPECT_EQ(it, map.end());\n\n  it = map.findWithoutPromotion(\"z\");\n  // Promote y to get it out of the way, ensure iterator to z still valid\n  EXPECT_EQ(*map.get(\"y\"), 2);\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"z\");\n  EXPECT_EQ(*it->second, 3);\n  auto it2 = it;\n  it2++;\n  EXPECT_EQ(it2, map.end());\n\n  map.erase(map.find(\"x\"));\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"z\");\n  EXPECT_EQ(*it->second, 3);\n  it2 = it;\n  it2++;\n  EXPECT_EQ(it2, map.end());\n\n  EXPECT_EQ(*map.get(\"z\"), 3);\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"z\");\n  EXPECT_EQ(*it->second, 3);\n  ++it;\n  ASSERT_NE(it, map.end());\n  EXPECT_EQ(it->first, \"y\");\n  EXPECT_EQ(*it->second, 2);\n  ++it;\n  EXPECT_EQ(it, map.end());\n\n  auto rit = map.rbegin();\n  ASSERT_NE(rit, map.rend());\n  EXPECT_EQ(rit->first, \"y\");\n  EXPECT_EQ(*rit->second, 2);\n  ++rit;\n  ASSERT_NE(rit, map.rend());\n  EXPECT_EQ(rit->first, \"z\");\n  EXPECT_EQ(*rit->second, 3);\n  ++rit;\n  EXPECT_EQ(rit, map.rend());\n\n  it = map.find(\"z\");\n  // Temporarily over limit, evicts y\n  map.replace(it, std::make_unique<size_t>(100));\n  EXPECT_EQ(*it->second, 100);\n  EXPECT_EQ(*map.get(\"z\"), 100);\n  EXPECT_EQ(map.size(), 1);\n\n  // Any modification evicts the over limit z\n  map.set(\"x\", std::make_unique<size_t>(0));\n  EXPECT_EQ(map.size(), 1);\n  EXPECT_EQ(map.find(\"z\"), map.end());\n}\n\nTEST(ImplicitlyWeightedEvictingCacheMap, PruneHook) {\n  struct ValueAndWeight {\n    size_t value;\n    size_t weight;\n  };\n  struct GetWeight {\n    size_t operator()(const size_t& /*key*/, const ValueAndWeight& value) {\n      return value.weight;\n    }\n  };\n  ImplicitlyWeightedEvictingCacheMap<size_t, ValueAndWeight, GetWeight> map{10};\n  std::vector<std::pair<size_t, ValueAndWeight>> prunedValues;\n  map.setPruneHook([&](const size_t& key, ValueAndWeight&& value) {\n    prunedValues.emplace_back(key, std::move(value));\n  });\n  map.set(1, ValueAndWeight{2, 3});\n  map.set(4, ValueAndWeight{5, 6});\n  // Evicts others\n  map.set(6, ValueAndWeight{7, 8});\n  EXPECT_EQ(prunedValues.size(), 2);\n  EXPECT_EQ(prunedValues[0].first, 1);\n  EXPECT_EQ(prunedValues[0].second.value, 2);\n  EXPECT_EQ(prunedValues[0].second.weight, 3);\n  EXPECT_EQ(prunedValues[1].first, 4);\n  EXPECT_EQ(prunedValues[1].second.value, 5);\n  EXPECT_EQ(prunedValues[1].second.weight, 6);\n}\n\nTEST(WeightedEvictingCacheMap, Misc) {\n  WeightedEvictingCacheMap<std::string, std::string> map{42};\n  auto end = map.end();\n\n  EXPECT_EQ(0, map.size());\n  EXPECT_EQ(0, map.getCurrentTotalWeight());\n  EXPECT_EQ(42, map.getMaxTotalWeight());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_EQ(map.find(\"x\"), end);\n\n  map.set(\"x\", \"X\", 4);\n\n  EXPECT_EQ(1, map.size());\n  EXPECT_EQ(4, map.getCurrentTotalWeight());\n  EXPECT_FALSE(map.empty());\n\n  EXPECT_EQ(map.get(\"x\"), \"X\");\n  EXPECT_NE(map.find(\"x\"), end);\n  EXPECT_EQ(map.find(\"x\")->second.value, \"X\");\n\n  EXPECT_TRUE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"y\"));\n\n  map.set(\"y\", \"Y\", 20);\n\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(24, map.getCurrentTotalWeight());\n\n  EXPECT_EQ(map.get(\"y\"), \"Y\");\n  EXPECT_NE(map.find(\"y\"), end);\n  EXPECT_EQ(map.find(\"y\")->second.value, \"Y\");\n\n  EXPECT_TRUE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n\n  // This time use non-heterogeneous overloads.\n  // Evicts x\n  map.set(std::string(\"z\"), \"Z\", 20);\n\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(40, map.getCurrentTotalWeight());\n\n  EXPECT_EQ(map.get(std::string(\"z\")), \"Z\");\n  EXPECT_NE(map.find(std::string(\"z\")), end);\n  EXPECT_EQ(map.find(std::string(\"z\"))->second.value, \"Z\");\n\n  EXPECT_FALSE(map.exists(std::string(\"x\")));\n  EXPECT_TRUE(map.exists(std::string(\"y\")));\n  EXPECT_TRUE(map.exists(std::string(\"z\")));\n\n  // And move semantics for value, with and without\n  // heterogeneous key\n  map.set(std::string(\"a\"), \"A\", 1);\n\n  std::string b_val = \"B\";\n  map.set(\"b\", std::move(b_val), 1);\n\n  EXPECT_EQ(4, map.size());\n  EXPECT_EQ(42, map.getCurrentTotalWeight());\n\n  // Use y to move it up in LRU list\n  EXPECT_EQ(map.get(\"y\"), \"Y\");\n\n  // Evicts z\n  map.set(\"c\", \"C\", 1);\n  EXPECT_EQ(4, map.size());\n  EXPECT_EQ(23, map.getCurrentTotalWeight());\n\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n\n  // Can also overwrite a value\n  map.set(\"a\", \"AA\", 20);\n\n  EXPECT_EQ(4, map.size());\n  EXPECT_EQ(42, map.getCurrentTotalWeight());\n\n  // Which might evict (evicts y)\n  map.set(\"b\", \"BB\", 20);\n\n  EXPECT_EQ(3, map.size());\n  EXPECT_EQ(41, map.getCurrentTotalWeight());\n\n  // Can use get to change a value\n  map.get(\"c\") = \"CC\";\n\n  // Change weight in place (no eviction yet)\n  map.updateWeight(map.find(\"c\"), 2);\n  EXPECT_EQ(3, map.size());\n  EXPECT_EQ(42, map.getCurrentTotalWeight());\n\n  // Change weight in place, non-heterogeneous key (evicts a)\n  map.updateWeight(map.find(std::string(\"c\")), 20);\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(40, map.getCurrentTotalWeight());\n\n  // Can also evict by setting max total weight (evicts b)\n  map.setMaxTotalWeight(39);\n  EXPECT_EQ(1, map.size());\n  map.setMaxTotalWeight(20);\n  EXPECT_EQ(1, map.size());\n  EXPECT_TRUE(map.exists(\"c\"));\n\n  // Check clear\n  map.clear();\n\n  EXPECT_EQ(0, map.size());\n  EXPECT_EQ(0, map.getCurrentTotalWeight());\n  EXPECT_EQ(20, map.getMaxTotalWeight());\n  EXPECT_TRUE(map.empty());\n  EXPECT_FALSE(map.exists(\"a\"));\n  EXPECT_FALSE(map.exists(\"b\"));\n  EXPECT_FALSE(map.exists(\"c\"));\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n}\n\nTEST(WeightedEvictingCacheMap, OverMaxWeight) {\n  struct Unit {};\n  WeightedEvictingCacheMap<std::string, Unit> map{42};\n\n  map.set(\"x\", {}, 5);\n  map.set(\"y\", {}, 5);\n  map.set(\"z\", {}, 5);\n\n  // Setting weight too high evicts the entry (if it's not most recently used)\n  // and anything used less recently\n  map.updateWeight(map.findWithoutPromotion(\"y\"), 100);\n\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"y\"));\n  EXPECT_TRUE(map.exists(\"z\"));\n  EXPECT_EQ(1, map.size());\n\n  map.set(\"x\", {}, 5);\n  map.set(\"y\", {}, 5);\n  map.set(\"z\", {}, 5);\n\n  // If it's most recently used, it is protected from immediate eviction\n  map.updateWeight(map.findWithoutPromotion(\"z\"), 100);\n\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"y\"));\n  EXPECT_TRUE(map.exists(\"z\"));\n  EXPECT_EQ(1, map.size());\n\n  map.set(\"x\", {}, 5);\n  EXPECT_EQ(1, map.size()); // over-weight entry evicted\n  map.set(\"y\", {}, 5);\n  map.set(\"z\", {}, 5);\n\n  // If it's most recently used, it is protected from immediate eviction\n  map.updateWeight(map.find(\"y\"), 100);\n\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n  EXPECT_EQ(1, map.size());\n\n  // Forces the entry (beyond capacity)\n  map.set(\"z\", {}, 100);\n\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"y\"));\n  EXPECT_TRUE(map.exists(\"z\"));\n  EXPECT_EQ(1, map.size());\n  EXPECT_EQ(42, map.getMaxTotalWeight());\n\n  // It is not protected from eviction in subsequent write ops\n  map.setMaxTotalWeight(42); // Same as before\n  EXPECT_EQ(0, map.size());\n\n  // Another force set\n  map.set(\"z\", {}, 100);\n\n  // Can be evicted by a zero weight insert\n  map.set(\"x\", {}, 0);\n\n  EXPECT_TRUE(map.exists(\"x\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n  EXPECT_EQ(0, map.getCurrentTotalWeight());\n\n  // Zero weight not evicted spuriously\n  map.set(\"z\", {}, 42);\n\n  EXPECT_TRUE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"z\"));\n\n  // But is evicted in LRU order\n  map.set(\"y\", {}, 5);\n\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n\n  map.set(\"x\", {}, 5);\n\n  EXPECT_TRUE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n  EXPECT_EQ(10, map.getCurrentTotalWeight());\n\n  // Another force set\n  map.set(\"y\", {}, 100);\n\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n  EXPECT_EQ(100, map.getCurrentTotalWeight());\n\n  // Can decrease weight (within max) without eviction\n  {\n    auto it = map.find(\"y\");\n    map.updateWeight(it, 42);\n    // Iterator known valid\n    EXPECT_EQ(42, it->second.weight);\n  }\n\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_TRUE(map.exists(\"y\"));\n  EXPECT_FALSE(map.exists(\"z\"));\n  EXPECT_EQ(42, map.getCurrentTotalWeight());\n\n  // Reset\n  map.set(\"x\", {}, 5);\n  map.set(\"y\", {}, 5);\n  EXPECT_EQ(10, map.getCurrentTotalWeight());\n\n  // Try erase\n  map.erase(\"y\");\n  EXPECT_EQ(1, map.size());\n  EXPECT_EQ(5, map.getCurrentTotalWeight());\n  // Re-add\n  map.set(\"y\", {}, 5);\n  EXPECT_EQ(2, map.size());\n  EXPECT_EQ(10, map.getCurrentTotalWeight());\n}\n\nTEST(WeightedEvictingCacheMap, ConstAndIterator) {\n  using MyMap = WeightedEvictingCacheMap<std::string, const std::string>;\n  MyMap map{10};\n\n  map.set(\"x\", \"X\", 3);\n  map.set(\"y\", \"Y\", 4);\n  map.set(\"z\", \"Z\", 5); // evicts x\n  EXPECT_EQ(map.size(), 2);\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_EQ(map.get(\"y\"), \"Y\");\n  EXPECT_EQ(map.find(\"z\")->second.value, \"Z\");\n\n  const MyMap& cmap = map;\n  {\n    MyMap::iterator it = map.begin();\n    ASSERT_NE(it, map.end());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(it->second.value, \"Z\");\n    EXPECT_EQ(it->second.weight, 5);\n    ++it;\n    ASSERT_NE(it, map.end());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(it->second.value, \"Y\");\n    EXPECT_EQ(it->second.weight, 4);\n    ++it;\n    EXPECT_EQ(it, map.end());\n  }\n  {\n    MyMap::const_iterator it = cmap.begin();\n    ASSERT_NE(it, cmap.end());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(it->second.value, \"Z\");\n    EXPECT_EQ(it->second.weight, 5);\n    ++it;\n    ASSERT_NE(it, cmap.end());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(it->second.value, \"Y\");\n    EXPECT_EQ(it->second.weight, 4);\n    ++it;\n    EXPECT_EQ(it, cmap.end());\n  }\n  {\n    MyMap::const_iterator it = map.cbegin();\n    ASSERT_NE(it, map.cend());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(it->second.value, \"Z\");\n    EXPECT_EQ(it->second.weight, 5);\n    ++it;\n    ASSERT_NE(it, map.cend());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(it->second.value, \"Y\");\n    EXPECT_EQ(it->second.weight, 4);\n    ++it;\n    EXPECT_EQ(it, map.cend());\n  }\n  map.get(\"y\"); // swap entry order, reverse iterator sees old order\n  {\n    MyMap::reverse_iterator it = map.rbegin();\n    ASSERT_NE(it, map.rend());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(it->second.value, \"Z\");\n    EXPECT_EQ(it->second.weight, 5);\n    ++it;\n    ASSERT_NE(it, map.rend());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(it->second.value, \"Y\");\n    EXPECT_EQ(it->second.weight, 4);\n    ++it;\n    EXPECT_EQ(it, map.rend());\n  }\n  {\n    MyMap::const_reverse_iterator it = cmap.rbegin();\n    ASSERT_NE(it, cmap.rend());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(it->second.value, \"Z\");\n    EXPECT_EQ(it->second.weight, 5);\n    ++it;\n    ASSERT_NE(it, cmap.rend());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(it->second.value, \"Y\");\n    EXPECT_EQ(it->second.weight, 4);\n    ++it;\n    EXPECT_EQ(it, cmap.rend());\n  }\n  {\n    MyMap::const_reverse_iterator it = map.crbegin();\n    ASSERT_NE(it, map.crend());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(it->second.value, \"Z\");\n    EXPECT_EQ(it->second.weight, 5);\n    ++it;\n    ASSERT_NE(it, map.crend());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(it->second.value, \"Y\");\n    EXPECT_EQ(it->second.weight, 4);\n    ++it;\n    EXPECT_EQ(it, map.crend());\n  }\n}\n\nTEST(WeightedEvictingCacheMap, NonCopyableAndIteratorMutation) {\n  using MyMap = WeightedEvictingCacheMap<std::string, std::unique_ptr<int>>;\n  MyMap map{10};\n\n  map.set(\"x\", std::make_unique<int>(1), 3);\n  map.set(\"y\", std::make_unique<int>(2), 4);\n  map.set(\"z\", std::make_unique<int>(3), 5);\n  EXPECT_EQ(map.size(), 2);\n  EXPECT_FALSE(map.exists(\"x\"));\n  EXPECT_EQ(*map.get(\"y\"), 2);\n  EXPECT_EQ(*map.find(\"z\")->second.value, 3);\n  EXPECT_EQ(map.find(\"z\")->second.weight, 5);\n\n  {\n    MyMap::iterator it = map.begin();\n    ASSERT_NE(it, map.end());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(*it->second.value, 3);\n    EXPECT_EQ(it->second.weight, 5);\n    // Now mutate with move semantics\n    std::unique_ptr<int> tmp{new int(42)};\n    std::swap(tmp, it->second.value);\n    EXPECT_EQ(*it->second.value, 42);\n    EXPECT_EQ(*tmp, 3);\n    EXPECT_EQ(*map.get(\"z\"), 42);\n    ++it;\n    ASSERT_NE(it, map.end());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(*it->second.value, 2);\n    EXPECT_EQ(it->second.weight, 4);\n    ++it;\n    EXPECT_EQ(it, map.end());\n  }\n  {\n    MyMap::reverse_iterator it = map.rbegin();\n    ASSERT_NE(it, map.rend());\n    EXPECT_EQ(it->first, \"y\");\n    EXPECT_EQ(*it->second.value, 2);\n    EXPECT_EQ(it->second.weight, 4);\n    // Now mutate with move semantics\n    it->second.value = std::make_unique<int>(43);\n    EXPECT_EQ(*map.getWithoutPromotion(\"y\"), 43);\n    ++it;\n    ASSERT_NE(it, map.rend());\n    EXPECT_EQ(it->first, \"z\");\n    EXPECT_EQ(*it->second.value, 42);\n    EXPECT_EQ(it->second.weight, 5);\n    ++it;\n    EXPECT_EQ(it, map.rend());\n  }\n}\n\nTEST(WeightedEvictingCacheMap, Scalars) {\n  WeightedEvictingCacheMap<size_t, size_t> map{10};\n  map.set(1, 2, 3);\n  map.set(4, 5, 6);\n  EXPECT_EQ(9, map.getCurrentTotalWeight());\n  EXPECT_EQ(map.get(1), 2U);\n  EXPECT_EQ(map.get(4), 5U);\n  // Evicts others\n  map.set(6, 7, 8);\n  EXPECT_EQ(8, map.getCurrentTotalWeight());\n  EXPECT_EQ(map.find(1), map.end());\n  EXPECT_FALSE(map.exists(4));\n  EXPECT_EQ(map.find(6)->second.value, 7U);\n}\n\nTEST(WeightedEvictingCacheMap, ApproximateEntryMemUsage) {\n  // See test EvictingCacheMap::ApproximateEntryMemUsage\n  EXPECT_LE(\n      (ImplicitlyWeightedEvictingCacheMap<\n          uint64_t,\n          std::unique_ptr<char[]>,\n          ValueStringLength>::kApproximateEntryMemUsage),\n      48U);\n\n  // Add explicit weight\n  EXPECT_LE(\n      (WeightedEvictingCacheMap<uint64_t, std::unique_ptr<char[]>>::\n           kApproximateEntryMemUsage),\n      54U);\n\n  // Example with weight = approximate memory usage\n  size_t kValueSize = 40;\n  WeightedEvictingCacheMap<uint64_t, std::unique_ptr<char[]>> map{100};\n  std::unique_ptr<char[]> value(new char[kValueSize]{});\n  size_t weight = kValueSize + map.kApproximateEntryMemUsage;\n  map.set(42U, std::move(value), weight);\n}\n\nTEST(WeightedEvictingCacheMap, PruneHook) {\n  WeightedEvictingCacheMap<size_t, size_t> map{10};\n  std::vector<std::tuple<size_t, size_t, size_t>> prunedValues;\n  map.setPruneHook([&](const size_t& key, size_t&& value, size_t weight) {\n    prunedValues.emplace_back(key, value, weight);\n  });\n  map.set(1, 2, 3);\n  map.set(4, 5, 6);\n  // Evicts others\n  map.set(6, 7, 8);\n  EXPECT_EQ(prunedValues.size(), 2);\n  EXPECT_EQ(std::get<0>(prunedValues[0]), 1);\n  EXPECT_EQ(std::get<1>(prunedValues[0]), 2);\n  EXPECT_EQ(std::get<2>(prunedValues[0]), 3);\n  EXPECT_EQ(std::get<0>(prunedValues[1]), 4);\n  EXPECT_EQ(std::get<1>(prunedValues[1]), 5);\n  EXPECT_EQ(std::get<2>(prunedValues[1]), 6);\n}\n"
  },
  {
    "path": "folly/container/test/heap_vector_types_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iterator>\n#include <list>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <folly/Random.h>\n#include <folly/Range.h>\n#include <folly/Utility.h>\n#include <folly/container/heap_vector_types.h>\n#include <folly/memory/Malloc.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/small_vector.h>\n\nusing folly::heap_vector_map;\nusing folly::heap_vector_set;\nusing folly::small_heap_vector_map;\n\nnamespace {\n\ntemplate <class T>\nstruct less_invert {\n  bool operator()(const T& a, const T& b) const { return b < a; }\n};\n\ntemplate <class Container>\nvoid check_invariant(Container& c) {\n  auto it = c.begin();\n  auto end = c.end();\n  if (it == end) {\n    return;\n  }\n  auto prev = it;\n  ++it;\n  for (; it != end; ++it, ++prev) {\n    EXPECT_TRUE(c.value_comp()(*prev, *it));\n  }\n}\n\nstruct OneAtATimePolicy {\n  template <class Container>\n  void increase_capacity(Container& c) {\n    if (c.size() == c.capacity()) {\n      c.reserve(c.size() + 1);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct CountingAllocator : std::allocator<T> {\n  T* allocate(std::size_t n) {\n    nAllocations += 1;\n    return std::allocator<T>::allocate(n);\n  }\n  int nAllocations{0};\n\n  template <typename U>\n  struct rebind {\n    using other = CountingAllocator<U>;\n  };\n};\n\nstruct CountCopyCtor {\n  explicit CountCopyCtor() : val_(0), count_(0) {}\n\n  explicit CountCopyCtor(int val) : val_(val), count_(0) {}\n\n  CountCopyCtor(const CountCopyCtor& c) noexcept\n      : val_(c.val_), count_(c.count_ + 1) {\n    ++gCount_;\n  }\n\n  CountCopyCtor& operator=(const CountCopyCtor&) = default;\n\n  bool operator<(const CountCopyCtor& o) const { return val_ < o.val_; }\n\n  int val_;\n  int count_;\n  static int gCount_;\n};\n\nint CountCopyCtor::gCount_ = 0;\n\nstruct KeyCopiedException : public std::exception {};\n/**\n * Key that may throw on copy when throwOnCopy is set, but never on move.\n * Use clone() to copy without throwing.\n */\nstruct KeyThatThrowsOnCopies {\n  int32_t key{};\n  bool throwOnCopy{};\n\n  /* implicit */ KeyThatThrowsOnCopies(int32_t key) noexcept\n      : key(key), throwOnCopy(false) {}\n  KeyThatThrowsOnCopies(int32_t key, bool throwOnCopy) noexcept\n      : key(key), throwOnCopy(throwOnCopy) {}\n\n  ~KeyThatThrowsOnCopies() noexcept {}\n\n  KeyThatThrowsOnCopies(KeyThatThrowsOnCopies const& other)\n      : key(other.key), throwOnCopy(other.throwOnCopy) {\n    if (throwOnCopy) {\n      throw KeyCopiedException{};\n    }\n  }\n\n  KeyThatThrowsOnCopies(KeyThatThrowsOnCopies&& other) noexcept = default;\n\n  KeyThatThrowsOnCopies& operator=(KeyThatThrowsOnCopies const& other) {\n    key = other.key;\n    throwOnCopy = other.throwOnCopy;\n    if (throwOnCopy) {\n      throw KeyCopiedException{};\n    }\n    return *this;\n  }\n\n  KeyThatThrowsOnCopies& operator=(KeyThatThrowsOnCopies&& other) noexcept =\n      default;\n\n  bool operator<(const KeyThatThrowsOnCopies& other) const {\n    return key < other.key;\n  }\n};\n\nstatic_assert(\n    std::is_nothrow_move_constructible<KeyThatThrowsOnCopies>::value &&\n        std::is_nothrow_move_assignable<KeyThatThrowsOnCopies>::value,\n    \"non-noexcept move-constructible or move-assignable\");\n\n} // namespace\n\nTEST(HeapVectorTypes, SetAssignmentInitListTest) {\n  heap_vector_set<int> s{3, 4, 5};\n  EXPECT_THAT(s, testing::ElementsAreArray({3, 4, 5}));\n  s = {}; // empty ilist assignment\n  EXPECT_THAT(s, testing::IsEmpty());\n  s = {7, 8, 9}; // non-empty ilist assignment\n  EXPECT_THAT(s, testing::ElementsAreArray({7, 8, 9}));\n}\n\nTEST(HeapVectorTypes, SimpleSetTest) {\n  heap_vector_set<int> s;\n  EXPECT_TRUE(s.empty());\n  for (int i = 0; i < 1000; ++i) {\n    s.insert(folly::Random::rand32() % 100000);\n  }\n  EXPECT_FALSE(s.empty());\n  check_invariant(s);\n\n  heap_vector_set<int> s2;\n  s2.insert(s.begin(), s.end());\n  check_invariant(s2);\n  EXPECT_TRUE(s == s2);\n\n  auto it = s2.lower_bound(32);\n  if (*it == 32) {\n    s2.erase(it);\n    it = s2.lower_bound(32);\n  }\n  check_invariant(s2);\n  auto oldSz = s2.size();\n  s2.insert(it, 32);\n  EXPECT_TRUE(s2.size() == oldSz + 1);\n  check_invariant(s2);\n\n  const heap_vector_set<int>& cs2 = s2;\n  auto range = cs2.equal_range(32);\n  auto lbound = cs2.lower_bound(32);\n  auto ubound = cs2.upper_bound(32);\n  EXPECT_TRUE(range.first == lbound);\n  EXPECT_TRUE(range.second == ubound);\n  EXPECT_TRUE(range.first != cs2.end());\n  EXPECT_TRUE(range.second != cs2.end());\n  EXPECT_TRUE(cs2.count(32) == 1);\n  EXPECT_FALSE(cs2.find(32) == cs2.end());\n  EXPECT_TRUE(cs2.contains(32));\n\n  // Bad insert hint.\n  s2.insert(s2.begin() + 3, 33);\n  EXPECT_TRUE(s2.find(33) != s2.begin());\n  EXPECT_TRUE(s2.find(33) != s2.end());\n  check_invariant(s2);\n  s2.erase(33);\n  check_invariant(s2);\n\n  it = s2.find(32);\n  EXPECT_FALSE(it == s2.end());\n  s2.erase(it);\n  EXPECT_FALSE(cs2.contains(32));\n  EXPECT_TRUE(s2.size() == oldSz);\n  check_invariant(s2);\n\n  heap_vector_set<int> cpy(s);\n  check_invariant(cpy);\n  EXPECT_TRUE(cpy == s);\n  heap_vector_set<int> cpy2(s);\n  cpy2.insert(100001);\n  EXPECT_TRUE(cpy2 != cpy);\n  EXPECT_TRUE(cpy2 != s);\n  check_invariant(cpy2);\n  EXPECT_TRUE(cpy2.count(100001) == 1);\n  s.swap(cpy2);\n  check_invariant(cpy2);\n  check_invariant(s);\n  EXPECT_TRUE(s != cpy);\n  EXPECT_TRUE(s != cpy2);\n  EXPECT_TRUE(cpy2 == cpy);\n\n  heap_vector_set<int> s3 = {};\n  s3.insert({1, 2, 3});\n  s3.emplace(4);\n  EXPECT_EQ(s3.size(), 4);\n\n  heap_vector_set<std::string> s4;\n  s4.emplace(\"foobar\", 3);\n  EXPECT_EQ(s4.count(\"foo\"), 1);\n}\n\nTEST(HeapVectorTypes, TransparentSetTest) {\n  using namespace folly::string_piece_literals;\n  using Compare = folly::transparent<std::less<folly::StringPiece>>;\n\n  constexpr auto buddy = \"buddy\"_sp;\n  constexpr auto hello = \"hello\"_sp;\n  constexpr auto stake = \"stake\"_sp;\n  constexpr auto world = \"world\"_sp;\n  constexpr auto zebra = \"zebra\"_sp;\n\n  heap_vector_set<std::string, Compare> const s({hello.str(), world.str()});\n\n  // find\n  EXPECT_TRUE(s.end() == s.find(buddy));\n  EXPECT_EQ(hello, *s.find(hello));\n  EXPECT_TRUE(s.end() == s.find(stake));\n  EXPECT_EQ(world, *s.find(world));\n  EXPECT_TRUE(s.end() == s.find(zebra));\n\n  // count\n  EXPECT_EQ(0, s.count(buddy));\n  EXPECT_EQ(1, s.count(hello));\n  EXPECT_EQ(0, s.count(stake));\n  EXPECT_EQ(1, s.count(world));\n  EXPECT_EQ(0, s.count(zebra));\n\n  // contains\n  EXPECT_FALSE(s.contains(buddy));\n  EXPECT_TRUE(s.contains(hello));\n  EXPECT_FALSE(s.contains(stake));\n  EXPECT_TRUE(s.contains(world));\n  EXPECT_FALSE(s.contains(zebra));\n\n  // lower_bound\n  EXPECT_TRUE(s.find(hello) == s.lower_bound(buddy));\n  EXPECT_TRUE(s.find(hello) == s.lower_bound(hello));\n  EXPECT_TRUE(s.find(world) == s.lower_bound(stake));\n  EXPECT_TRUE(s.find(world) == s.lower_bound(world));\n  EXPECT_TRUE(s.end() == s.lower_bound(zebra));\n\n  // upper_bound\n  EXPECT_TRUE(s.find(hello) == s.upper_bound(buddy));\n  EXPECT_TRUE(s.find(world) == s.upper_bound(hello));\n  EXPECT_TRUE(s.find(world) == s.upper_bound(stake));\n  EXPECT_TRUE(s.end() == s.upper_bound(world));\n  EXPECT_TRUE(s.end() == s.upper_bound(zebra));\n\n  // equal_range\n  for (auto value : {buddy, hello, stake, world, zebra}) {\n    EXPECT_TRUE(\n        std::make_pair(s.lower_bound(value), s.upper_bound(value)) ==\n        s.equal_range(value))\n        << value;\n  }\n}\n\nTEST(HeapVectorTypes, BadHints) {\n  for (int toInsert = -1; toInsert <= 7; ++toInsert) {\n    for (int hintPos = 0; hintPos <= 4; ++hintPos) {\n      heap_vector_set<int> s;\n      for (int i = 0; i <= 3; ++i) {\n        s.insert(i * 2);\n      }\n      s.insert(s.begin() + hintPos, toInsert);\n      size_t expectedSize = (toInsert % 2) == 0 ? 4 : 5;\n      EXPECT_EQ(s.size(), expectedSize);\n      check_invariant(s);\n    }\n  }\n}\n\nTEST(HeapVectorTypes, MapAssignmentInitListTest) {\n  using v = std::pair<int, const char*>;\n  v p = {3, \"a\"}, q = {4, \"b\"}, r = {5, \"c\"};\n  {\n    heap_vector_map<int, const char*> m{p, q, r};\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n    m = {}; // empty ilist assignment\n    EXPECT_THAT(m, testing::IsEmpty());\n    m = {p, q, r}; // non-empty ilist assignment\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n  }\n  {\n    small_heap_vector_map<int, const char*> m{p, q, r};\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n    m = {}; // empty ilist assignment\n    EXPECT_THAT(m, testing::IsEmpty());\n    m = {p, q, r}; // non-empty ilist assignment\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n  }\n}\n\nTEST(HeapVectorTypes, MapAssignmentInitListTestEnum) {\n  enum class E : int { a = 3, b = 4, c = 5 };\n  using v = std::pair<E, const char*>;\n  v p = {E::a, \"a\"}, q = {E::b, \"b\"}, r = {E::c, \"c\"};\n  {\n    heap_vector_map<E, const char*> m{p, q, r};\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n    m = {}; // empty ilist assignment\n    EXPECT_THAT(m, testing::IsEmpty());\n    m = {p, q, r}; // non-empty ilist assignment\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n  }\n  {\n    small_heap_vector_map<E, const char*> m{p, q, r};\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n    m = {}; // empty ilist assignment\n    EXPECT_THAT(m, testing::IsEmpty());\n    m = {p, q, r}; // non-empty ilist assignment\n    EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n  }\n}\n\nTEST(HeapVectorTypes, MapBadHints) {\n  for (int toInsert = -1; toInsert <= 7; ++toInsert) {\n    for (int hintPos = 0; hintPos <= 4; ++hintPos) {\n      heap_vector_map<int, int> s;\n      for (int i = 0; i <= 3; ++i) {\n        s.emplace(i * 2, i);\n      }\n      s.emplace_hint(s.begin() + hintPos, toInsert, toInsert);\n      size_t expectedSize = (toInsert % 2) == 0 ? 4 : 5;\n      EXPECT_EQ(s.size(), expectedSize);\n      check_invariant(s);\n    }\n  }\n\n  for (int toInsert = -1; toInsert <= 7; ++toInsert) {\n    for (int hintPos = 0; hintPos <= 4; ++hintPos) {\n      small_heap_vector_map<int, int> s;\n      for (int i = 0; i <= 3; ++i) {\n        s.emplace(i * 2, i);\n      }\n      s.emplace_hint(s.begin() + hintPos, toInsert, toInsert);\n      size_t expectedSize = (toInsert % 2) == 0 ? 4 : 5;\n      EXPECT_EQ(s.size(), expectedSize);\n      check_invariant(s);\n    }\n  }\n}\n\nTEST(HeapVectorTypes, FromVector) {\n  {\n    folly::heap_vector_map<int, float>::container_type vec;\n    vec.emplace_back(3, 3.0f);\n    vec.emplace_back(1, 1.0f);\n    vec.emplace_back(2, 2.0f);\n\n    heap_vector_map<int, float> m(std::move(vec));\n\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_EQ(vec.size(), 0);\n    EXPECT_EQ(m.size(), 3);\n    EXPECT_EQ(m[1], 1.0f);\n    EXPECT_EQ(m[2], 2.0f);\n    EXPECT_EQ(m[3], 3.0f);\n  }\n  {\n    folly::small_heap_vector_map<int, float>::container_type vec;\n    vec.push_back(std::make_pair(3, 3.0f));\n    vec.push_back(std::make_pair(1, 1.0f));\n    vec.push_back(std::make_pair(2, 2.0f));\n\n    small_heap_vector_map<int, float> m(std::move(vec));\n\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_EQ(vec.size(), 0);\n    EXPECT_EQ(m.size(), 3);\n    EXPECT_EQ(m[1], 1.0f);\n    EXPECT_EQ(m[2], 2.0f);\n    EXPECT_EQ(m[3], 3.0f);\n  }\n}\n\nTEST(HeapVectorTypes, IterateOverVectorWithinMap) {\n  const int size = 10;\n  {\n    folly::heap_vector_map<int, int> m;\n    int heap_order[size] = {6, 3, 8, 1, 5, 7, 9, 0, 2, 4};\n\n    for (int i = 0; i < size; i++) {\n      m[i] = i;\n    }\n\n    // Iterate over underlying container. Fastest\n    int i = 0;\n    for (auto& e : m.iterate()) {\n      EXPECT_EQ(e.second, heap_order[i++]);\n    }\n    // Iterate inorder using heap_vector_map iterator\n    i = 0;\n    for (auto& e : m) {\n      EXPECT_EQ(e.first, i++);\n    }\n  }\n\n  {\n    folly::small_heap_vector_map<int, int> m;\n    int heap_order[size] = {6, 3, 8, 1, 5, 7, 9, 0, 2, 4};\n\n    for (int i = 0; i < size; i++) {\n      m[i] = i;\n    }\n\n    // Iterate over underlying container. Fastest\n    int i = 0;\n    for (auto& e : m.iterate()) {\n      EXPECT_EQ(e.second, heap_order[i++]);\n    }\n    // Iterate inorder using small_heap_vector_map iterator\n    i = 0;\n    for (auto& e : m) {\n      EXPECT_EQ(e.first, i++);\n    }\n  }\n}\n\nTEST(HeapVectorTypes, SimpleMapTest) {\n  heap_vector_map<int, float> m;\n  for (int i = 0; i < 1000; ++i) {\n    m[i] = float(i / 1000.0);\n  }\n  check_invariant(m);\n\n  m[32] = 100.0f;\n  check_invariant(m);\n  EXPECT_TRUE(m.contains(32));\n  EXPECT_DOUBLE_EQ(100.0, m.at(32));\n  EXPECT_FALSE(m.find(32) == m.end());\n  EXPECT_TRUE(m.contains(32));\n  m.erase(32);\n  EXPECT_TRUE(m.find(32) == m.end());\n  EXPECT_FALSE(m.contains(32));\n  check_invariant(m);\n  EXPECT_THROW(m.at(32), std::out_of_range);\n\n  heap_vector_map<int, float> m2 = m;\n  EXPECT_TRUE(m2 == m);\n  EXPECT_FALSE(m2 != m);\n  auto it = m2.lower_bound(1 << 20);\n  EXPECT_TRUE(it == m2.end());\n  m2.insert(it, std::make_pair(1 << 20, 10.0f));\n  check_invariant(m2);\n  EXPECT_TRUE(m2.contains(1 << 20));\n  EXPECT_TRUE(m < m2);\n  EXPECT_TRUE(m <= m2);\n\n  const heap_vector_map<int, float>& cm = m;\n  auto range = cm.equal_range(42);\n  auto lbound = cm.lower_bound(42);\n  auto ubound = cm.upper_bound(42);\n  EXPECT_TRUE(range.first == lbound);\n  EXPECT_TRUE(range.second == ubound);\n  EXPECT_FALSE(range.first == cm.end());\n  EXPECT_FALSE(range.second == cm.end());\n  m.erase(m.lower_bound(42));\n  check_invariant(m);\n\n  heap_vector_map<int, float> m3;\n  m3.insert(m2.begin(), m2.end());\n  check_invariant(m3);\n  EXPECT_TRUE(m3 == m2);\n  EXPECT_FALSE(m3 == m);\n\n  heap_vector_map<int, float> m4;\n  m4.emplace(1, 2.0f);\n  m4.emplace(3, 1.0f);\n  m4.emplace(2, 1.5f);\n  check_invariant(m4);\n  EXPECT_TRUE(m4.size() == 3);\n\n  heap_vector_map<int, float> m5;\n  for (auto& kv : m2) {\n    m5.emplace(kv);\n  }\n  check_invariant(m5);\n  EXPECT_TRUE(m5 == m2);\n  EXPECT_FALSE(m5 == m);\n\n  EXPECT_TRUE(m != m2);\n  EXPECT_TRUE(m2 == m3);\n  EXPECT_TRUE(m3 != m);\n  m.swap(m3);\n  check_invariant(m);\n  check_invariant(m2);\n  check_invariant(m3);\n  EXPECT_TRUE(m3 != m2);\n  EXPECT_TRUE(m3 != m);\n  EXPECT_TRUE(m == m2);\n\n  // Bad insert hint.\n  m.insert(m.begin() + 3, std::make_pair(1 << 15, 1.0f));\n  check_invariant(m);\n\n  heap_vector_map<int, float> m6 = {};\n  m6.insert({{1, 1.0f}, {2, 2.0f}, {1, 2.0f}});\n  EXPECT_EQ(m6.at(2), 2.0f);\n}\n\nTEST(HeapVectorTypes, SimpleSmallMapTest) {\n  small_heap_vector_map<int, float> m;\n  for (int i = 0; i < 160; ++i) {\n    m[i] = float(i / 1000.0);\n  }\n  check_invariant(m);\n\n  m[32] = 100.0f;\n  check_invariant(m);\n  EXPECT_TRUE(m.contains(32));\n  EXPECT_DOUBLE_EQ(100.0, m.at(32));\n  EXPECT_FALSE(m.find(32) == m.end());\n  EXPECT_TRUE(m.contains(32));\n  m.erase(32);\n  EXPECT_TRUE(m.find(32) == m.end());\n  EXPECT_FALSE(m.contains(32));\n  check_invariant(m);\n  EXPECT_THROW(m.at(32), std::out_of_range);\n\n  small_heap_vector_map<int, float> m2 = m;\n  EXPECT_TRUE(m2 == m);\n  EXPECT_FALSE(m2 != m);\n  auto it = m2.lower_bound(1 << 20);\n  EXPECT_TRUE(it == m2.end());\n  m2.insert(it, std::make_pair(1 << 20, 10.0f));\n  check_invariant(m2);\n  EXPECT_TRUE(m2.contains(1 << 20));\n  EXPECT_TRUE(m < m2);\n  EXPECT_TRUE(m <= m2);\n\n  const small_heap_vector_map<int, float>& cm = m;\n  auto range = cm.equal_range(42);\n  auto lbound = cm.lower_bound(42);\n  auto ubound = cm.upper_bound(42);\n  EXPECT_TRUE(range.first == lbound);\n  EXPECT_TRUE(range.second == ubound);\n  EXPECT_FALSE(range.first == cm.end());\n  EXPECT_FALSE(range.second == cm.end());\n  m.erase(m.lower_bound(42));\n  check_invariant(m);\n\n  small_heap_vector_map<int, float> m3;\n  m3.insert(m2.begin(), m2.end());\n  check_invariant(m3);\n  EXPECT_TRUE(m3 == m2);\n  EXPECT_FALSE(m3 == m);\n\n  small_heap_vector_map<int, float> m4;\n  m4.emplace(1, 2.0f);\n  m4.emplace(3, 1.0f);\n  m4.emplace(2, 1.5f);\n  check_invariant(m4);\n  EXPECT_TRUE(m4.size() == 3);\n\n  small_heap_vector_map<int, float> m5;\n  for (auto& kv : m2) {\n    m5.emplace(kv);\n  }\n  check_invariant(m5);\n  EXPECT_TRUE(m5 == m2);\n  EXPECT_FALSE(m5 == m);\n\n  EXPECT_TRUE(m != m2);\n  EXPECT_TRUE(m2 == m3);\n  EXPECT_TRUE(m3 != m);\n  m.swap(m3);\n  check_invariant(m);\n  check_invariant(m2);\n  check_invariant(m3);\n  EXPECT_TRUE(m3 != m2);\n  EXPECT_TRUE(m3 != m);\n  EXPECT_TRUE(m == m2);\n\n  // Bad insert hint.\n  m.insert(m.begin() + 3, std::make_pair(1 << 15, 1.0f));\n  check_invariant(m);\n\n  small_heap_vector_map<int, float> m6 = {};\n  m6.insert({{1, 1.0f}, {2, 2.0f}, {1, 2.0f}});\n  EXPECT_EQ(m6.at(2), 2.0f);\n}\n\nTEST(HeapVectorTypes, TransparentMapTest) {\n  using namespace folly::string_piece_literals;\n  using Compare = folly::transparent<std::less<folly::StringPiece>>;\n\n  constexpr auto buddy = \"buddy\"_sp;\n  constexpr auto hello = \"hello\"_sp;\n  constexpr auto stake = \"stake\"_sp;\n  constexpr auto world = \"world\"_sp;\n  constexpr auto zebra = \"zebra\"_sp;\n\n  heap_vector_map<std::string, float, Compare> const m(\n      {{hello.str(), -1.0f}, {world.str(), +1.0f}});\n\n  // find\n  EXPECT_TRUE(m.end() == m.find(buddy));\n  EXPECT_EQ(hello, m.find(hello)->first);\n  EXPECT_TRUE(m.end() == m.find(stake));\n  EXPECT_EQ(world, m.find(world)->first);\n  EXPECT_TRUE(m.end() == m.find(zebra));\n\n  // count\n  EXPECT_FALSE(m.contains(buddy));\n  EXPECT_TRUE(m.contains(hello));\n  EXPECT_FALSE(m.contains(stake));\n  EXPECT_TRUE(m.contains(world));\n  EXPECT_FALSE(m.contains(zebra));\n\n  // lower_bound\n  EXPECT_TRUE(m.find(hello) == m.lower_bound(buddy));\n  EXPECT_TRUE(m.find(hello) == m.lower_bound(hello));\n  EXPECT_TRUE(m.find(world) == m.lower_bound(stake));\n  EXPECT_TRUE(m.find(world) == m.lower_bound(world));\n  EXPECT_TRUE(m.end() == m.lower_bound(zebra));\n\n  // upper_bound\n  EXPECT_TRUE(m.find(hello) == m.upper_bound(buddy));\n  EXPECT_TRUE(m.find(world) == m.upper_bound(hello));\n  EXPECT_TRUE(m.find(world) == m.upper_bound(stake));\n  EXPECT_TRUE(m.end() == m.upper_bound(world));\n  EXPECT_TRUE(m.end() == m.upper_bound(zebra));\n\n  // equal_range\n  for (auto value : {buddy, hello, stake, world, zebra}) {\n    EXPECT_TRUE(\n        std::make_pair(m.lower_bound(value), m.upper_bound(value)) ==\n        m.equal_range(value))\n        << value;\n  }\n}\n\nTEST(HeapVectorTypes, Sizes) {\n  EXPECT_EQ(sizeof(heap_vector_set<int>), sizeof(std::vector<int>));\n  EXPECT_EQ(\n      sizeof(heap_vector_map<int, int>),\n      sizeof(std::vector<std::pair<int, int>>));\n  EXPECT_EQ(\n      sizeof(small_heap_vector_map<int, int>),\n      sizeof( //\n          folly::small_vector<\n              std::pair<int, int>,\n              0,\n              folly::small_vector_policy::policy_size_type<uint32_t>>));\n\n  using SetT = heap_vector_set<\n      int,\n      std::less<int>,\n      std::allocator<int>,\n      OneAtATimePolicy>;\n\n  using MapT = heap_vector_map<\n      int,\n      int,\n      std::less<int>,\n      std::allocator<std::pair<int, int>>,\n      OneAtATimePolicy>;\n\n  EXPECT_EQ(sizeof(SetT), sizeof(std::vector<int>));\n  EXPECT_EQ(sizeof(MapT), sizeof(std::vector<std::pair<int, int>>));\n}\n\nTEST(HeapVectorTypes, Iterators) {\n  heap_vector_set<int> s;\n\n  heap_vector_map<int, int> m;\n  m[0] = 0;\n  m[1] = 1;\n  EXPECT_EQ(m.size(), 2);\n  EXPECT_EQ(\n      (char*)&*m.iterate().end() - (char*)&*m.iterate().begin(),\n      2 * sizeof(std::pair<int, int>));\n  small_heap_vector_map<int, int> m2;\n  m2[0] = 0;\n  m2[1] = 1;\n  EXPECT_EQ(m2.size(), 2);\n  EXPECT_EQ(\n      (char*)&*m2.iterate().end() - (char*)&*m2.iterate().begin(),\n      2 * sizeof(std::pair<int, int>));\n\n  heap_vector_set<int>::iterator setI = s.begin();\n  // verify iterator -> const_iterator works. Reverse produce compiler error.\n  heap_vector_set<int>::const_iterator csetI(setI);\n\n  heap_vector_map<int, int>::iterator mapI = m.begin();\n  heap_vector_map<int, int>::const_iterator cmapI(mapI);\n\n  small_heap_vector_map<int, int>::iterator mapI2 = m2.begin();\n  small_heap_vector_map<int, int>::const_iterator cmapI2(mapI2);\n}\n\nTEST(HeapVectorTypes, IteratorsCombinatorics) {\n  heap_vector_set<size_t> c;\n  ASSERT_EQ(0, c.size());\n  ASSERT_EQ(c.begin(), c.end());\n  ASSERT_EQ(c.find(0), c.end());\n  for (size_t i = 0; i < 36; ++i) {\n    auto [ii, inserted] = c.insert(i);\n    auto bi = c.begin();\n    auto ei = c.end();\n    ASSERT_EQ(i, *ii);\n    ASSERT_EQ(ii, c.find(i));\n    ASSERT_TRUE(inserted);\n    ASSERT_EQ(i + 1, c.size());\n    ASSERT_NE(bi, ei);\n    EXPECT_EQ(c.size(), std::distance(bi, ei));\n    EXPECT_EQ(ei, bi + i + 1);\n    EXPECT_EQ(1, std::distance(bi + i, ei));\n    EXPECT_NE(ii, ei);\n    EXPECT_EQ(ii, bi + i);\n    EXPECT_EQ(bi, ii - i);\n    EXPECT_EQ(i, std::distance(bi, ii));\n    EXPECT_EQ(ei, ii + 1);\n    EXPECT_EQ(ii, ei - 1);\n    EXPECT_EQ(1, std::distance(ii, ei));\n    for (size_t j = 0; j <= i; ++j) {\n      auto ji = c.find(j);\n      ASSERT_NE(ji, ei);\n      ASSERT_EQ(j, *ji);\n      EXPECT_EQ(bi, ji - j);\n      EXPECT_EQ(ji, bi + j);\n      for (size_t k = 0; k <= i; ++k) {\n        auto ki = c.find(k);\n        ASSERT_NE(ki, ei);\n        EXPECT_EQ(j == k, ji == ki);\n        EXPECT_EQ(ji, ki + (int(j) - int(k)));\n        EXPECT_EQ(ji, ki - (int(k) - int(j)));\n        EXPECT_EQ(int(k) - int(j), ki - ji);\n        EXPECT_EQ(int(k) - int(j), std::distance(ji, ki));\n      }\n    }\n  }\n}\n\nTEST(HeapVectorTypes, InitializerLists) {\n  heap_vector_set<int> empty_initialized_set{};\n  EXPECT_TRUE(empty_initialized_set.empty());\n\n  heap_vector_set<int> singleton_initialized_set{1};\n  EXPECT_EQ(1, singleton_initialized_set.size());\n  EXPECT_EQ(1, *singleton_initialized_set.begin());\n\n  heap_vector_set<int> forward_initialized_set{1, 2};\n  heap_vector_set<int> backward_initialized_set{2, 1};\n  EXPECT_EQ(2, forward_initialized_set.size());\n  EXPECT_EQ(1, *forward_initialized_set.begin());\n  EXPECT_EQ(2, *forward_initialized_set.rbegin());\n  EXPECT_TRUE(forward_initialized_set == backward_initialized_set);\n  {\n    heap_vector_map<int, int> empty_initialized_map{};\n    EXPECT_TRUE(empty_initialized_map.empty());\n\n    heap_vector_map<int, int> singleton_initialized_map{{1, 10}};\n    EXPECT_EQ(1, singleton_initialized_map.size());\n    EXPECT_EQ(10, singleton_initialized_map[1]);\n\n    heap_vector_map<int, int> forward_initialized_map{{1, 10}, {2, 20}};\n    heap_vector_map<int, int> backward_initialized_map{{2, 20}, {1, 10}};\n    EXPECT_EQ(2, forward_initialized_map.size());\n    EXPECT_EQ(10, forward_initialized_map[1]);\n    EXPECT_EQ(20, forward_initialized_map[2]);\n    EXPECT_TRUE(forward_initialized_map == backward_initialized_map);\n  }\n  {\n    small_heap_vector_map<int, int> empty_initialized_map{};\n    EXPECT_TRUE(empty_initialized_map.empty());\n\n    small_heap_vector_map<int, int> singleton_initialized_map{{1, 10}};\n    EXPECT_EQ(1, singleton_initialized_map.size());\n    EXPECT_EQ(10, singleton_initialized_map[1]);\n\n    small_heap_vector_map<int, int> forward_initialized_map{{1, 10}, {2, 20}};\n    small_heap_vector_map<int, int> backward_initialized_map{{2, 20}, {1, 10}};\n    EXPECT_EQ(2, forward_initialized_map.size());\n    EXPECT_EQ(10, forward_initialized_map[1]);\n    EXPECT_EQ(20, forward_initialized_map[2]);\n    EXPECT_TRUE(forward_initialized_map == backward_initialized_map);\n  }\n}\n\nTEST(HeapVectorTypes, CustomCompare) {\n  heap_vector_set<int, less_invert<int>> s;\n  for (int i = 0; i < 200; ++i) {\n    s.insert(i);\n  }\n  check_invariant(s);\n  {\n    heap_vector_map<int, float, less_invert<int>> m;\n    for (int i = 0; i < 200; ++i) {\n      m[i] = 12.0f;\n    }\n    check_invariant(m);\n  }\n}\n\nTEST(HeapVectorTypes, GrowthPolicy) {\n  using SetT = heap_vector_set<\n      CountCopyCtor,\n      std::less<CountCopyCtor>,\n      CountingAllocator<CountCopyCtor>,\n      OneAtATimePolicy>;\n\n  SetT a;\n  for (int i = 0; i < 20; ++i) {\n    a.insert(CountCopyCtor(i));\n  }\n  check_invariant(a);\n  SetT::iterator it = a.begin();\n  ASSERT_FALSE(it == a.end());\n  EXPECT_EQ(it->count_, 20);\n  EXPECT_EQ(a.get_container().get_allocator().nAllocations, 20);\n\n  std::list<CountCopyCtor> v;\n  for (int i = 0; i < 20; ++i) {\n    v.emplace_back(20 + i);\n  }\n  a.insert(v.begin(), v.end());\n  check_invariant(a);\n  EXPECT_EQ(a.get_container().get_allocator().nAllocations, 21);\n}\n\nTEST(HeapVectorTest, EmptyTest) {\n  heap_vector_set<int> emptySet;\n  EXPECT_TRUE(emptySet.lower_bound(10) == emptySet.end());\n  EXPECT_TRUE(emptySet.find(10) == emptySet.end());\n  {\n    heap_vector_map<int, int> emptyMap;\n    EXPECT_TRUE(emptyMap.lower_bound(10) == emptyMap.end());\n    EXPECT_TRUE(emptyMap.find(10) == emptyMap.end());\n    EXPECT_THROW(emptyMap.at(10), std::out_of_range);\n  }\n  {\n    small_heap_vector_map<int, int> emptyMap;\n    EXPECT_TRUE(emptyMap.lower_bound(10) == emptyMap.end());\n    EXPECT_TRUE(emptyMap.find(10) == emptyMap.end());\n    EXPECT_THROW(emptyMap.at(10), std::out_of_range);\n  }\n}\n\nTEST(HeapVectorTest, MoveTest) {\n  heap_vector_set<std::unique_ptr<int>> s;\n  s.insert(std::make_unique<int>(5));\n  s.insert(s.end(), std::make_unique<int>(10));\n  EXPECT_EQ(s.size(), 2);\n\n  for (const auto& p : s) {\n    EXPECT_TRUE(*p == 5 || *p == 10);\n  }\n  {\n    heap_vector_map<int, std::unique_ptr<int>> m;\n    m.insert(std::make_pair(5, std::make_unique<int>(5)));\n    m.insert(m.end(), std::make_pair(10, std::make_unique<int>(10)));\n\n    EXPECT_EQ(*m[5], 5);\n    EXPECT_EQ(*m[10], 10);\n  }\n  {\n    small_heap_vector_map<int, std::unique_ptr<int>> m;\n    m.insert(std::make_pair(5, std::make_unique<int>(5)));\n    m.insert(m.end(), std::make_pair(10, std::make_unique<int>(10)));\n\n    EXPECT_EQ(*m[5], 5);\n    EXPECT_EQ(*m[10], 10);\n  }\n}\n\nTEST(HeapVectorTest, ShrinkTest) {\n  heap_vector_map<int, int> s;\n  int i = 0;\n  // Hopefully your resize policy doubles when capacity is full, or this will\n  // hang forever :(\n  while (s.capacity() == s.size()) {\n    s.insert(std::make_pair(i++, i));\n  }\n  s.shrink_to_fit();\n  // The standard does not actually enforce that this be true, but assume that\n  // vector::shrink_to_fit respects the caller.\n  EXPECT_EQ(s.capacity(), s.size());\n}\n\nTEST(HeapVectorTypes, EraseTest) {\n  heap_vector_set<int> s;\n  for (int i = 0; i < 1000; ++i) {\n    s.insert(i);\n  }\n\n  auto it = s.lower_bound(32);\n  EXPECT_EQ(*it, 32);\n  it = s.erase(it);\n  EXPECT_NE(s.end(), it);\n  EXPECT_EQ(*it, 33);\n  it = s.erase(it, it + 5);\n  EXPECT_EQ(*it, 38);\n\n  it = s.begin();\n  while (it != s.end()) {\n    if (*it >= 5) {\n      it = s.erase(it);\n    } else {\n      it++;\n    }\n  }\n  EXPECT_EQ(it, s.end());\n  EXPECT_EQ(s.size(), 5);\n\n  {\n    heap_vector_map<int, int> m;\n    m.insert(std::make_pair(1, 1));\n    heap_vector_map<int, int> m2(m);\n    EXPECT_EQ(0, m.erase(0));\n    EXPECT_EQ(m2, m);\n  }\n\n  {\n    small_heap_vector_map<int, int> m;\n    m.insert(std::make_pair(1, 1));\n    small_heap_vector_map<int, int> m2(m);\n    EXPECT_EQ(0, m.erase(0));\n    EXPECT_EQ(m2, m);\n  }\n}\n\nTEST(HeapVectorTypes, EraseTest2) {\n  heap_vector_set<int> s;\n  for (int i = 0; i < 1000; ++i) {\n    s.insert(i);\n  }\n\n  auto it = s.lower_bound(32);\n  EXPECT_EQ(*it, 32);\n  it = s.erase(it);\n  EXPECT_NE(s.end(), it);\n  EXPECT_EQ(*it, 33);\n  it = s.erase(it, it + 5);\n  EXPECT_EQ(*it, 38);\n\n  it = s.begin();\n  while (it != s.end()) {\n    if (*it >= 5) {\n      it = s.erase(it);\n    } else {\n      it++;\n    }\n  }\n  EXPECT_EQ(it, s.end());\n  EXPECT_EQ(s.size(), 5);\n\n  {\n    heap_vector_map<int, int> m;\n    for (int i = 0; i < 1000; ++i) {\n      m.insert(std::make_pair(i, i));\n    }\n\n    auto it2 = m.lower_bound(32);\n    EXPECT_EQ(it2->first, 32);\n    it2 = m.erase(it2);\n    EXPECT_NE(m.end(), it2);\n    EXPECT_EQ(it2->first, 33);\n    it2 = m.erase(it2, it2 + 5);\n    EXPECT_EQ(it2->first, 38);\n\n    it2 = m.begin();\n    while (it2 != m.end()) {\n      if (it2->first >= 5) {\n        it2 = m.erase(it2);\n      } else {\n        it2++;\n      }\n    }\n    EXPECT_EQ(it2, m.end());\n    EXPECT_EQ(m.size(), 5);\n  }\n\n  {\n    small_heap_vector_map<int, int> m;\n    for (int i = 0; i < 100; ++i) {\n      m.insert(std::make_pair(i, i));\n    }\n\n    auto it2 = m.lower_bound(32);\n    EXPECT_EQ(it2->first, 32);\n    it2 = m.erase(it2);\n    EXPECT_NE(m.end(), it2);\n    EXPECT_EQ(it2->first, 33);\n    it2 = m.erase(it2, it2 + 5);\n    EXPECT_EQ(it2->first, 38);\n\n    it2 = m.begin();\n    while (it2 != m.end()) {\n      if (it2->first >= 5) {\n        it2 = m.erase(it2);\n      } else {\n        it2++;\n      }\n    }\n    EXPECT_EQ(it2, m.end());\n    EXPECT_EQ(m.size(), 5);\n  }\n}\n\nTEST(HeapVectorTypes, TestSetBulkInsertionSortMerge) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will have to be merged in.\n  s = std::vector<int>({10, 7, 5, 1});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n\n  EXPECT_THAT(vset, testing::ElementsAreArray({1, 2, 4, 5, 6, 7, 8, 10}));\n}\n\nTEST(HeapVectorTypes, TestBulkInsertionUncopyableTypes) {\n  {\n    std::vector<std::pair<int, std::unique_ptr<int>>> s;\n    s.emplace_back(1, std::make_unique<int>(0));\n\n    heap_vector_map<int, std::unique_ptr<int>> vmap(\n        std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n\n    s.clear();\n    s.emplace_back(3, std::make_unique<int>(0));\n    vmap.insert(\n        std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n  }\n  {\n    std::vector<std::pair<int, std::unique_ptr<int>>> s;\n    s.emplace_back(1, std::make_unique<int>(0));\n\n    small_heap_vector_map<int, std::unique_ptr<int>> vmap(\n        std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n\n    s.clear();\n    s.emplace_back(3, std::make_unique<int>(0));\n    vmap.insert(\n        std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n  }\n}\n\nTEST(HeapVectorTypes, TestSetBulkInsertionMiddleValuesEqualDuplication) {\n  auto s = std::vector<int>({4, 6, 8});\n\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  s = std::vector<int>({8, 10, 12});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n\n  EXPECT_THAT(vset, testing::ElementsAreArray({4, 6, 8, 10, 12}));\n}\n\nTEST(HeapVectorTypes, TestSetBulkInsertionSortMergeDups) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will have to be merged in.\n  s = std::vector<int>({10, 6, 5, 2});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 5, 6, 8, 10}));\n}\n\nTEST(HeapVectorTypes, TestSetInsertionDupsOneByOne) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will have to be merged in.\n  s = std::vector<int>({10, 6, 5, 2});\n\n  for (const auto& elem : s) {\n    vset.insert(elem);\n  }\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 5, 6, 8, 10}));\n}\n\nTEST(HeapVectorTypes, TestSetBulkInsertionSortNoMerge) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will not have to be merged in.\n  s = std::vector<int>({20, 15, 16, 13});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 6, 8, 13, 15, 16, 20}));\n}\n\nTEST(HeapVectorTypes, TestSetBulkInsertionNoSortMerge) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add a sorted range that will have to be merged in.\n  s = std::vector<int>({1, 3, 5, 9});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({1, 2, 3, 4, 5, 6, 8, 9}));\n}\n\nTEST(HeapVectorTypes, TestSetBulkInsertionNoSortNoMerge) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add a sorted range that will not have to be merged in.\n  s = std::vector<int>({21, 22, 23, 24});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 6, 8, 21, 22, 23, 24}));\n}\n\nTEST(HeapVectorTypes, TestSetBulkInsertionEmptyRange) {\n  std::vector<int> s;\n  EXPECT_TRUE(s.empty());\n\n  // insertion of empty range into empty container.\n  heap_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  s = std::vector<int>({6, 4, 8, 2});\n\n  vset.insert(s.begin(), s.end());\n\n  // insertion of empty range into non-empty container.\n  s.clear();\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 6, 8}));\n}\n\n// A moveable and copyable struct, which we use to make sure that no copy\n// operations are performed during bulk insertion if moving is an option.\nstruct Movable {\n  int x_;\n  explicit Movable(int x) : x_(x) {}\n  Movable(const Movable&) { ADD_FAILURE() << \"Copy ctor should not be called\"; }\n  Movable& operator=(const Movable&) {\n    ADD_FAILURE() << \"Copy assignment should not be called\";\n    return *this;\n  }\n\n  Movable(Movable&&) = default;\n  Movable& operator=(Movable&&) = default;\n};\n\nTEST(HeapVectorTypes, TestBulkInsertionMovableTypes) {\n  std::vector<std::pair<int, Movable>> s;\n  s.emplace_back(3, Movable(2));\n  s.emplace_back(1, Movable(0));\n\n  heap_vector_map<int, Movable> vmap(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n\n  s.clear();\n  s.emplace_back(4, Movable(3));\n  s.emplace_back(2, Movable(1));\n  vmap.insert(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n}\n\nTEST(HeapVectorTypes, TestBulkInsertionMovableTypesSmall) {\n  std::vector<std::pair<int, Movable>> s;\n  s.emplace_back(3, Movable(2));\n  s.emplace_back(1, Movable(0));\n\n  small_heap_vector_map<int, Movable> vmap(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n\n  s.clear();\n  s.emplace_back(4, Movable(3));\n  s.emplace_back(2, Movable(1));\n  vmap.insert(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n}\n\nTEST(HeapVectorTypes, TestSetCreationFromVector) {\n  std::vector<int> vec = {3, 1, -1, 5, 0};\n  heap_vector_set<int> vset(std::move(vec));\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({-1, 0, 1, 3, 5}));\n}\n\nTEST(HeapVectorTypes, TestMapCreationFromVector) {\n  std::vector<std::pair<int, int>> vec = {\n      {3, 1}, {1, 5}, {-1, 2}, {5, 3}, {0, 3}};\n  heap_vector_map<int, int> vmap(std::move(vec));\n  check_invariant(vmap);\n  auto contents = std::vector<std::pair<int, int>>(vmap.begin(), vmap.end());\n  auto expected_contents = std::vector<std::pair<int, int>>({\n      {-1, 2},\n      {0, 3},\n      {1, 5},\n      {3, 1},\n      {5, 3},\n  });\n  EXPECT_EQ(contents, expected_contents);\n\n  // test very large vector\n  std::vector<std::pair<int, int>> vec2;\n  for (int i = 0; i < 100000; i++) {\n    vec2.emplace_back(i, i);\n  }\n  heap_vector_map<int, int> vmap2(std::move(vec2));\n  check_invariant(vmap2);\n}\n\nTEST(HeapVectorTypes, TestMapCreationFromVectorSmall) {\n  // TODO: Add a constructor to steal std::vector. For small_heap_vector_map\n  // it is better to steal from small_vector.\n  folly::small_vector<\n      std::pair<int, int>,\n      0,\n      folly::small_vector_policy::policy_size_type<uint32_t>>\n      vec = {{3, 1}, {1, 5}, {-1, 2}, {5, 3}, {0, 3}};\n  small_heap_vector_map<int, int> vmap(std::move(vec));\n  check_invariant(vmap);\n  auto contents = std::vector<std::pair<int, int>>(vmap.begin(), vmap.end());\n  auto expected_contents = std::vector<std::pair<int, int>>({\n      {-1, 2},\n      {0, 3},\n      {1, 5},\n      {3, 1},\n      {5, 3},\n  });\n  EXPECT_EQ(contents, expected_contents);\n}\n\nTEST(HeapVectorTypes, TestSetCreationFromSmallVector) {\n  using smvec = folly::small_vector<int, 5>;\n  smvec vec = {3, 1, -1, 5, 0};\n  heap_vector_set<\n      int,\n      std::less<int>,\n      std::allocator<std::pair<int, int>>,\n      void,\n      smvec>\n      vset(std::move(vec));\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({-1, 0, 1, 3, 5}));\n}\n\nTEST(HeapVectorTypes, TestMapCreationFromSmallVector) {\n  using smvec = folly::small_vector<std::pair<int, int>, 5>;\n  smvec vec = {{3, 1}, {1, 5}, {-1, 2}, {5, 3}, {0, 3}};\n  heap_vector_map<\n      int,\n      int,\n      std::less<int>,\n      std::allocator<std::pair<int, int>>,\n      void,\n      smvec>\n      vmap(std::move(vec));\n  check_invariant(vmap);\n  auto contents = std::vector<std::pair<int, int>>(vmap.begin(), vmap.end());\n  auto expected_contents = std::vector<std::pair<int, int>>({\n      {-1, 2},\n      {0, 3},\n      {1, 5},\n      {3, 1},\n      {5, 3},\n  });\n  EXPECT_EQ(contents, expected_contents);\n}\n\nTEST(HeapVectorTypes, TestBulkInsertionWithDuplicatesIntoEmptySet) {\n  heap_vector_set<int> set;\n  {\n    std::vector<int> const vec = {0, 1, 0, 1};\n    set.insert(vec.begin(), vec.end());\n  }\n  EXPECT_THAT(set, testing::ElementsAreArray({0, 1}));\n}\n\nTEST(HeapVectorTypes, TestBulkInsertionWithDuplicatesIntoEmptyMap) {\n  std::vector<std::pair<int, int>> const vec = {{0, 0}, {1, 1}, {0, 2}, {1, 3}};\n\n  const heap_vector_map<int, int> m(vec.begin(), vec.end());\n  EXPECT_EQ(m.size(), 2);\n  EXPECT_EQ(m.at(0), 0);\n  EXPECT_EQ(m.at(1), 1);\n\n  heap_vector_map<int, int> m2;\n  m2[2] = 2;\n  m2[-1] = -1;\n\n  // merge two heap maps.\n  m2.insert(\n      std::make_move_iterator(m.iterate().begin()),\n      std::make_move_iterator(m.iterate().end()));\n\n  EXPECT_EQ(m2.size(), 4);\n  EXPECT_EQ(m2[0], 0);\n  EXPECT_EQ(m2[1], 1);\n}\n\nTEST(HeapVectorTypes, TestBulkInsertionWithDuplicatesIntoEmptyMapSmall) {\n  std::vector<std::pair<int, int>> const vec = {{0, 0}, {1, 1}, {0, 2}, {1, 3}};\n\n  small_heap_vector_map<int, int> m(vec.begin(), vec.end());\n  EXPECT_EQ(m.size(), 2);\n  EXPECT_EQ(m[0], 0);\n  EXPECT_EQ(m[1], 1);\n\n  small_heap_vector_map<int, int> m2;\n  m2[2] = 2;\n  m2[-1] = -1;\n\n  // merge two heap maps.\n  m2.insert(\n      std::make_move_iterator(m.iterate().begin()),\n      std::make_move_iterator(m.iterate().end()));\n\n  EXPECT_EQ(m2.size(), 4);\n  EXPECT_EQ(m2[0], 0);\n  EXPECT_EQ(m2[1], 1);\n}\n\nTEST(HeapVectorTypes, TestDataPointsToFirstElement) {\n  heap_vector_map<int, int> map;\n\n  map[0] = 0;\n  // works if map has a single element. otherwise data points to middle element\n  EXPECT_EQ(&*map.iterate().data(), &*map.begin());\n\n  map[1] = 1;\n  // data() does not point to begin()!\n  // A major difference between heap_vector_map and sorted_vector_map.\n  EXPECT_NE(&*map.iterate().data(), &*map.begin());\n}\n\nTEST(HeapVectorTypes, TestDataPointsToFirstElementSmall) {\n  small_heap_vector_map<int, int> map;\n\n  map[0] = 0;\n  // works if map has a single element. otherwise data points to middle element\n  EXPECT_EQ(&*map.iterate().data(), &*map.begin());\n\n  map[1] = 1;\n  // data() does not point to begin()!\n  // A major difference between heap_vector_map and sorted_vector_map.\n  EXPECT_NE(&*map.iterate().data(), &*map.begin());\n}\n\nTEST(HeapVectorTypes, TestEmplaceHint) {\n  heap_vector_map<int, int> map;\n\n  for (size_t i = 0; i < 4; ++i) {\n    const std::pair<int, int> k00(0, 0);\n    const std::pair<int, int> k10(1, 0);\n    const std::pair<int, int> k1i(1, i % 2);\n    const std::pair<int, int> k20(2, 0);\n    const std::pair<int, int> k2i(2, i % 2);\n\n    EXPECT_EQ(*map.emplace_hint(map.begin(), 0, i % 2), k00);\n    EXPECT_EQ(*map.emplace_hint(map.begin(), k1i), k10);\n    EXPECT_EQ(*map.emplace_hint(map.begin(), folly::copy(k2i)), k20);\n\n    check_invariant(map);\n  }\n}\n\nTEST(HeapVectorTypes, TestExceptionSafety) {\n  std::initializer_list<std::pair<KeyThatThrowsOnCopies, int>> const\n      sortedUnique = {\n          {0, 0}, {1, 1}, {4, 4}, {7, 7}, {9, 9}, {11, 11}, {15, 15}};\n  heap_vector_map<KeyThatThrowsOnCopies, int> map = {sortedUnique};\n  EXPECT_EQ(map.size(), 7);\n\n  // Verify that we successfully insert when no exceptions are thrown.\n  KeyThatThrowsOnCopies key1(96, false);\n  auto hint1 = map.find(96);\n  map.emplace_hint(hint1, key1, 96);\n  EXPECT_EQ(map.size(), 8);\n\n  // Verify that we don't add a key at the end if copying throws\n  KeyThatThrowsOnCopies key2(99, true);\n  auto hint2 = map.find(99);\n  try {\n    map.emplace_hint(hint2, key2, 99);\n  } catch (const KeyCopiedException&) {\n    // swallow\n  }\n  EXPECT_EQ(map.size(), 8);\n\n  // Verify that we don't add a key in the middle if copying throws\n  KeyThatThrowsOnCopies key3(47, true);\n  auto hint3 = map.find(47);\n  try {\n    map.emplace_hint(hint3, key3, 47);\n  } catch (const KeyCopiedException&) {\n    // swallow\n  }\n  EXPECT_EQ(map.size(), 8);\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\n\nusing std::pmr::memory_resource;\nusing std::pmr::new_delete_resource;\nusing std::pmr::null_memory_resource;\nusing std::pmr::polymorphic_allocator;\n\nnamespace {\n\nstruct test_resource : public memory_resource {\n  void* do_allocate(size_t bytes, size_t /* alignment */) override {\n    return folly::checkedMalloc(bytes);\n  }\n\n  void do_deallocate(\n      void* p, size_t /* bytes */, size_t /* alignment */) noexcept override {\n    free(p);\n  }\n\n  bool do_is_equal(const memory_resource& other) const noexcept override {\n    return this == &other;\n  }\n};\n\n} // namespace\n\nTEST(HeapVectorTypes, TestPmrAllocatorSimple) {\n  namespace pmr = folly::pmr;\n\n  pmr::heap_vector_set<std::pair<int, int>> s(null_memory_resource());\n  EXPECT_THROW(s.emplace(42, 42), std::bad_alloc);\n\n  pmr::heap_vector_map<int, int> m(null_memory_resource());\n  EXPECT_THROW(m.emplace(42, 42), std::bad_alloc);\n}\n\nTEST(HeapVectorTypes, TestPmrCopyConstructSameAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r;\n  polymorphic_allocator<std::byte> a1(&r), a2(&r);\n  EXPECT_EQ(a1, a2);\n\n  {\n    pmr::heap_vector_set<int> s1(a1);\n    s1.emplace(42);\n\n    pmr::heap_vector_set<int> s2(s1, a2);\n    EXPECT_EQ(s1.get_allocator(), s2.get_allocator());\n    EXPECT_EQ(s2.count(42), 1);\n  }\n\n  {\n    pmr::heap_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n\n    pmr::heap_vector_map<int, int> m2(m1, a2);\n    EXPECT_EQ(m1.get_allocator(), m2.get_allocator());\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\nTEST(HeapVectorTypes, TestPmrCopyConstructDifferentAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r1, r2;\n  polymorphic_allocator<std::byte> a1(&r1), a2(&r2);\n  EXPECT_NE(a1, a2);\n\n  {\n    pmr::heap_vector_set<int> s1(a1);\n    s1.emplace(42);\n\n    pmr::heap_vector_set<int> s2(s1, a2);\n    EXPECT_NE(s1.get_allocator(), s2.get_allocator());\n    EXPECT_EQ(s2.count(42), 1);\n  }\n  {\n    pmr::heap_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n\n    pmr::heap_vector_map<int, int> m2(m1, a2);\n    EXPECT_NE(m1.get_allocator(), m2.get_allocator());\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\nTEST(HeapVectorTypes, TestPmrMoveConstructSameAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r;\n  polymorphic_allocator<std::byte> a1(&r), a2(&r);\n  EXPECT_EQ(a1, a2);\n\n  {\n    pmr::heap_vector_set<int> s1(a1);\n    s1.emplace(42);\n    auto d = s1.iterate().data();\n\n    pmr::heap_vector_set<int> s2(std::move(s1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_EQ(s1.get_allocator(), s2.get_allocator());\n    EXPECT_EQ(s2.iterate().data(), d);\n    EXPECT_EQ(s2.count(42), 1);\n  }\n  {\n    pmr::heap_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n    auto d = m1.iterate().data();\n\n    pmr::heap_vector_map<int, int> m2(std::move(m1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_EQ(m1.get_allocator(), m2.get_allocator());\n    EXPECT_EQ(m2.iterate().data(), d);\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\nTEST(HeapVectorTypes, TestPmrMoveConstructDifferentAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r1, r2;\n  polymorphic_allocator<std::byte> a1(&r1), a2(&r2);\n  EXPECT_NE(a1, a2);\n\n  {\n    pmr::heap_vector_set<int> s1(a1);\n    s1.emplace(42);\n    auto d = s1.iterate().data();\n\n    pmr::heap_vector_set<int> s2(std::move(s1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_NE(s1.get_allocator(), s2.get_allocator());\n    EXPECT_NE(s2.iterate().data(), d);\n    EXPECT_EQ(s2.count(42), 1);\n  }\n  {\n    pmr::heap_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n    auto d = m1.iterate().data();\n\n    pmr::heap_vector_map<int, int> m2(std::move(m1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_NE(m1.get_allocator(), m2.get_allocator());\n    EXPECT_NE(m2.iterate().data(), d);\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\ntemplate <typename T>\nusing pmr_vector = std::vector<T, std::pmr::polymorphic_allocator<T>>;\n\nTEST(HeapVectorTypes, TestCreationFromPmrVector) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n  test_resource r;\n  polymorphic_allocator<std::byte> a(&r);\n\n  {\n    pmr_vector<int> c({1, 2, 3}, a);\n    auto d = c.data();\n    pmr::heap_vector_set<int> s(std::move(c));\n    EXPECT_EQ(s.get_allocator(), a);\n    EXPECT_EQ(&*s.iterate().data(), d);\n  }\n\n  {\n    pmr_vector<int> c({2, 1, 3}, a);\n    auto d = c.data();\n    pmr::heap_vector_set<int> s(std::move(c));\n    EXPECT_EQ(s.get_allocator(), a);\n    EXPECT_EQ(&*s.iterate().data(), d);\n  }\n\n  {\n    pmr_vector<std::pair<int, int>> c({{1, 1}, {2, 2}, {3, 3}}, a);\n    auto d = c.data();\n    pmr::heap_vector_map<int, int> m(std::move(c));\n    EXPECT_EQ(m.get_allocator(), a);\n    EXPECT_EQ(&*m.iterate().data(), d);\n  }\n\n  {\n    pmr_vector<std::pair<int, int>> c({{2, 2}, {1, 1}, {3, 3}}, a);\n    auto d = c.data();\n    pmr::heap_vector_map<int, int> m(std::move(c));\n    EXPECT_EQ(m.get_allocator(), a);\n    EXPECT_EQ(&*m.iterate().data(), d);\n  }\n}\n\nTEST(HeapVectorTypes, TestPmrAllocatorScoped) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n  polymorphic_allocator<std::byte> alloc(new_delete_resource());\n\n  {\n    pmr::heap_vector_set<pmr_vector<int>> s(alloc);\n    s.emplace(1);\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::heap_vector_set<pmr_vector<int>> s(alloc);\n    s.emplace_hint(s.begin(), 1);\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::heap_vector_map<int, pmr_vector<int>> m(alloc);\n    m.emplace(1, 1);\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::heap_vector_map<int, pmr_vector<int>> m(alloc);\n    m.emplace_hint(m.begin(), 1, 1);\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::heap_vector_map<int, pmr::heap_vector_map<int, int>> m(alloc);\n    m.emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(42),\n        std::forward_as_tuple(\n            std::initializer_list<std::pair<int, int>>{{42, 42}}));\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::heap_vector_map<int, pmr::heap_vector_map<int, int>> m(alloc);\n    m.emplace_hint(\n        m.begin(),\n        std::piecewise_construct,\n        std::forward_as_tuple(42),\n        std::forward_as_tuple(\n            std::initializer_list<std::pair<int, int>>{{42, 42}}));\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::heap_vector_map<int, pmr::heap_vector_map<int, int>> m(alloc);\n    m[42][42] = 42;\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n}\n\n#endif\n\nTEST(HeapVectorTypes, TestInsertHintCopy) {\n  heap_vector_set<CountCopyCtor> set;\n  heap_vector_map<CountCopyCtor, int> map;\n  CountCopyCtor skey;\n  std::pair<CountCopyCtor, int> mkey;\n\n  CountCopyCtor::gCount_ = 0;\n  set.insert(set.end(), skey);\n  map.insert(map.end(), mkey);\n  EXPECT_EQ(CountCopyCtor::gCount_, 2);\n\n  set.emplace(CountCopyCtor(1));\n  map.emplace(CountCopyCtor(1), 1);\n\n  CountCopyCtor::gCount_ = 0;\n  for (size_t i = 0; i <= map.size(); ++i) {\n    auto sit = set.begin();\n    auto mit = map.begin();\n    std::advance(sit, i);\n    std::advance(mit, i);\n    set.insert(sit, skey);\n    map.insert(mit, mkey);\n  }\n  EXPECT_EQ(CountCopyCtor::gCount_, 0);\n}\n\nTEST(HeapVectorTypes, TestIterator) {\n  heap_vector_map<int, int> m;\n  const int size = 11;\n  for (int i = 0; i < size; i++) {\n    m[i] = i;\n  }\n\n  // Test C++ generics, idioms\n\n  // distance\n  EXPECT_EQ(std::distance(++m.begin(), m.end()), 10);\n  EXPECT_EQ(std::distance(m.begin(), --m.end()), 10);\n  EXPECT_EQ(std::distance(m.end(), m.begin()), -11);\n  EXPECT_EQ(std::distance(--m.end(), m.begin()), -10);\n\n  // std::copy\n  std::vector<std::pair<int, int>> v;\n  v.resize(size);\n  std::copy(m.begin(), m.end(), v.begin());\n  for (int i = 0; i < size; i++) {\n    EXPECT_EQ(v[i].first, i);\n  }\n\n  // rbegin\n  auto i = size;\n  for (auto I = m.rbegin(); I != m.rend(); ++I) {\n    EXPECT_EQ(I->first, --i);\n  }\n}\n\nTEST(HeapVectorTypes, TestGetContainer) {\n  heap_vector_map<int, int> m;\n  EXPECT_TRUE(m.get_container().empty());\n  small_heap_vector_map<int, int> m2;\n  EXPECT_TRUE(m2.get_container().empty());\n  heap_vector_set<int> s;\n  EXPECT_TRUE(s.get_container().empty());\n}\n\nTEST(HeapVectorTypes, TestSwapContainer) {\n  heap_vector_set<int> set{1, 2, 3};\n  std::vector<int> swapped{6, 5, 4};\n  set.swap_container(swapped);\n  EXPECT_EQ(swapped, (std::vector<int>{2, 1, 3}));\n  EXPECT_EQ(set.get_container(), (std::vector<int>{5, 4, 6}));\n  swapped = {1, 3, 5};\n  set.swap_container(folly::sorted_unique, swapped);\n  EXPECT_EQ(swapped, (std::vector<int>{5, 4, 6}));\n  EXPECT_EQ(set.get_container(), (std::vector<int>{3, 1, 5}));\n\n  heap_vector_map<int, int> map{{1, 1}, {2, 2}, {3, 3}};\n  std::vector<std::pair<int, int>> swappedMap{{6, 6}, {5, 5}, {4, 4}};\n  map.swap_container(swappedMap);\n  EXPECT_EQ(\n      swappedMap, (std::vector<std::pair<int, int>>{{2, 2}, {1, 1}, {3, 3}}));\n  EXPECT_EQ(\n      map.get_container(),\n      (std::vector<std::pair<int, int>>{{5, 5}, {4, 4}, {6, 6}}));\n  swappedMap = {{1, 1}, {3, 3}, {5, 5}};\n  map.swap_container(folly::sorted_unique, swappedMap);\n  EXPECT_EQ(\n      swappedMap, (std::vector<std::pair<int, int>>{{5, 5}, {4, 4}, {6, 6}}));\n  EXPECT_EQ(\n      map.get_container(),\n      (std::vector<std::pair<int, int>>{{3, 3}, {1, 1}, {5, 5}}));\n}\n"
  },
  {
    "path": "folly/container/test/range_traits_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/range_traits.h>\n\n#include <array>\n#include <list>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#if __has_include(<span>)\n#include <span>\n#endif\n\n#include <folly/portability/GTest.h>\n\nstruct RangeTraitsTest : testing::Test {};\n\nTEST_F(RangeTraitsTest, is_contiguous_range_v) {\n  enum some_enum {};\n\n  EXPECT_FALSE((folly::is_contiguous_range_v<void>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<int>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<int&>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<int[]>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<int const[]>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<int*>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<int const*>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<int()>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<some_enum>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<std::list<int>>));\n\n  EXPECT_TRUE((folly::is_contiguous_range_v<int[7]>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<int const[7]>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::array<int, 7>>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::string>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::string_view>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::vector<int>>));\n  EXPECT_FALSE((folly::is_contiguous_range_v<std::vector<bool>>));\n#if __has_include(<span>)\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::span<int>>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::span<int const>>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::span<int, 7>>));\n  EXPECT_TRUE((folly::is_contiguous_range_v<std::span<int const, 7>>));\n#endif\n}\n"
  },
  {
    "path": "folly/container/test/small_vector_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/small_vector.h>\n\n#include <iostream>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <numeric>\n#include <sstream>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include <boost/algorithm/string.hpp>\n#include <fmt/format.h>\n\n#include <folly/Conv.h>\n#include <folly/Traits.h>\n#include <folly/container/Iterator.h>\n#include <folly/portability/GTest.h>\n#include <folly/sorted_vector_types.h>\n\nusing folly::small_vector;\n\nusing folly::small_vector_policy::policy_in_situ_only;\nusing folly::small_vector_policy::policy_size_type;\n\n#if FOLLY_X64 || FOLLY_PPC64 || FOLLY_AARCH64 || FOLLY_RISCV64\n\ntemplate <typename...>\nstruct same_;\ntemplate <typename T>\nstruct same_<T, T> {};\n\nauto s0 = same_<\n    folly::small_vector_policy::\n        merge<policy_size_type<uint32_t>, policy_size_type<uint8_t>>::size_type,\n    uint8_t>{};\n\n// Fast access. Heap only small vector\nstatic_assert(\n    sizeof(small_vector<int, 0>) == 16,\n    \"Object size is not what we expect for small_vector<int, 0>\");\n\nstatic_assert(\n    sizeof(small_vector<int, 0, policy_size_type<uint32_t>>) ==\n        4 /* size_ */ + 8,\n    \"Object size is not what we expect for small_vector<int, 0, uint32_t>\");\n\nstatic_assert(\n    sizeof(small_vector<double, 0, policy_size_type<uint8_t>>) == 9,\n    \"Object size is not what we expect for small_vector<double, 0, uint32_t>\");\n\nstatic_assert(\n    sizeof(small_vector<\n           std::pair<int64_t, int64_t>,\n           0,\n           policy_size_type<uint32_t>>) == 12,\n    \"Object size is not what we expect for small_vector<pair<int64_t, int64_t>, 0, uint32_t>\");\n\nstatic_assert(\n    sizeof(small_vector<int>) == 16,\n    \"Object size is not what we expect for small_vector<int>\");\nstatic_assert(\n    sizeof(small_vector<int32_t, 2>) == 16,\n    \"Object size is not what we expect for \"\n    \"small_vector<int32_t,2>\");\nstatic_assert(\n    sizeof(small_vector<int, 10>) == 10 * sizeof(int) + sizeof(std::size_t),\n    \"Object size is not what we expect for small_vector<int,10>\");\n\nstatic_assert(\n    sizeof(small_vector<int32_t, 1, policy_size_type<uint32_t>>) == 8 + 4,\n    \"small_vector<int32_t,1,uint32_t> is wrong size\");\n\n// Extra 2 bytes needed for alignment.\nstatic_assert(\n    sizeof(small_vector<int32_t, 1, policy_size_type<uint16_t>>) == 8 + 2 + 2,\n    \"small_vector<int32_t,1,uint16_t> is wrong size\");\nstatic_assert(\n    alignof(small_vector<int32_t, 1, policy_size_type<uint16_t>>) >= 4,\n    \"small_vector not aligned correctly\");\n\n// Extra 3 bytes needed for alignment.\nstatic_assert(\n    sizeof(small_vector<int32_t, 1, policy_size_type<uint8_t>>) == 8 + 1 + 3,\n    \"small_vector<int32_t,1,uint8_t> is wrong size\");\nstatic_assert(\n    alignof(small_vector<int32_t, 1, policy_size_type<uint8_t>>) >= 4,\n    \"small_vector not aligned correctly\");\n\nstatic_assert(\n    sizeof(small_vector<int16_t, 4, policy_size_type<uint16_t>>) == 10,\n    \"Sizeof unexpectedly large\");\n\n#endif\n\nstatic_assert(\n    !std::is_trivially_copyable<std::unique_ptr<int>>::value,\n    \"std::unique_ptr<> is trivially copyable\");\n\nstatic_assert(\n    alignof(small_vector<folly::aligned_storage_t<32, 32>, 4>) == 32,\n    \"small_vector not aligned correctly\");\n\nnamespace {\n\ntemplate <typename Key, typename Value, size_t N>\nusing noheap_sorted_vector_map = folly::sorted_vector_map<\n    Key,\n    Value,\n    std::less<Key>,\n    std::allocator<std::pair<Key, Value>>,\n    void,\n    folly::small_vector<std::pair<Key, Value>, N, policy_in_situ_only<true>>>;\n\ntemplate <typename T, size_t N>\nusing noheap_sorted_vector_set = folly::sorted_vector_set<\n    T,\n    std::less<T>,\n    std::allocator<T>,\n    void,\n    folly::small_vector<T, N, policy_in_situ_only<true>>>;\n\nstruct NontrivialType {\n  static int ctored;\n  explicit NontrivialType() : a(0) {}\n\n  /* implicit */ NontrivialType(int a_) : a(a_) { ++ctored; }\n\n  NontrivialType(NontrivialType const& /* s */) { ++ctored; }\n\n  NontrivialType& operator=(NontrivialType const& o) {\n    a = o.a;\n    return *this;\n  }\n\n  int32_t a;\n};\nstatic_assert(\n    !std::is_trivially_copyable<NontrivialType>::value,\n    \"NontrivialType is trivially copyable\");\n\nint NontrivialType::ctored = 0;\n\nstruct TestException {};\n\nint throwCounter = 1;\nvoid MaybeThrow() {\n  if (!--throwCounter) {\n    throw TestException();\n  }\n}\n\nconst int kMagic = 0xdeadbeef;\nstruct Thrower {\n  static int alive;\n\n  Thrower() : magic(kMagic) {\n    EXPECT_EQ(magic, kMagic);\n    MaybeThrow();\n    ++alive;\n  }\n  Thrower(Thrower const& other) : magic(other.magic) {\n    EXPECT_EQ(magic, kMagic);\n    MaybeThrow();\n    ++alive;\n  }\n  ~Thrower() noexcept {\n    EXPECT_EQ(magic, kMagic);\n    magic = 0;\n    --alive;\n  }\n\n  Thrower& operator=(Thrower const& /* other */) {\n    EXPECT_EQ(magic, kMagic);\n    MaybeThrow();\n    return *this;\n  }\n\n  // This is just to try to make sure we don't get our member\n  // functions called on uninitialized memory.\n  int magic;\n};\n\nint Thrower::alive = 0;\n\n// Type that counts how many exist and doesn't support copy\n// construction.\nstruct NoncopyableCounter {\n  static int alive;\n  NoncopyableCounter() { ++alive; }\n  ~NoncopyableCounter() { --alive; }\n  NoncopyableCounter(NoncopyableCounter&&) noexcept { ++alive; }\n  NoncopyableCounter(NoncopyableCounter const&) = delete;\n  NoncopyableCounter& operator=(NoncopyableCounter const&) const = delete;\n  NoncopyableCounter& operator=(NoncopyableCounter&&) { return *this; }\n};\nint NoncopyableCounter::alive = 0;\n\nstatic_assert(\n    !std::is_trivially_copyable<NoncopyableCounter>::value,\n    \"NoncopyableCounter is trivially copyable\");\n\n// Check that throws don't break the basic guarantee for some cases.\n// Uses the method for testing exception safety described at\n// http://www.boost.org/community/exception_safety.html, to force all\n// throwing code paths to occur.\ntemplate <int N>\nstruct TestBasicGuarantee {\n  folly::small_vector<Thrower, N> vec;\n  int const prepopulate;\n\n  explicit TestBasicGuarantee(int prepopulate_) : prepopulate(prepopulate_) {\n    throwCounter = 1000;\n    for (int i = 0; i < prepopulate; ++i) {\n      vec.emplace_back();\n    }\n  }\n\n  ~TestBasicGuarantee() { throwCounter = 1000; }\n\n  template <class Operation>\n  void operator()(int insertCount, Operation const& op) {\n    bool done = false;\n\n    std::unique_ptr<folly::small_vector<Thrower, N>> workingVec;\n    for (int counter = 1; !done; ++counter) {\n      throwCounter = 1000;\n      workingVec = std::make_unique<folly::small_vector<Thrower, N>>(vec);\n      throwCounter = counter;\n      EXPECT_EQ(Thrower::alive, prepopulate * 2);\n      try {\n        op(*workingVec);\n        done = true;\n      } catch (...) {\n        // Note that the size of the vector can change if we were\n        // inserting somewhere other than the end (it's a basic only\n        // guarantee).  All we're testing here is that we have the\n        // right amount of uninitialized vs initialized memory.\n        EXPECT_EQ(Thrower::alive, workingVec->size() + vec.size());\n        continue;\n      }\n\n      // If things succeeded.\n      EXPECT_EQ(workingVec->size(), prepopulate + insertCount);\n      EXPECT_EQ(Thrower::alive, prepopulate * 2 + insertCount);\n    }\n  }\n};\n\n} // namespace\n\ntemplate <int N>\nvoid testBasicGuarantee() {\n  for (int prepop = 1; prepop < 30; ++prepop) {\n    (TestBasicGuarantee<N>(prepop))( // parens or a mildly vexing parse :(\n        1,\n        [&](folly::small_vector<Thrower, N>& v) { v.emplace_back(); });\n\n    EXPECT_EQ(Thrower::alive, 0);\n\n    (TestBasicGuarantee<N>(prepop))(1, [&](folly::small_vector<Thrower, N>& v) {\n      v.insert(v.begin(), Thrower());\n    });\n\n    EXPECT_EQ(Thrower::alive, 0);\n\n    (TestBasicGuarantee<N>(prepop))(1, [&](folly::small_vector<Thrower, N>& v) {\n      v.insert(v.begin() + 1, Thrower());\n    });\n\n    EXPECT_EQ(Thrower::alive, 0);\n  }\n\n  TestBasicGuarantee<N>(4)(3, [&](folly::small_vector<Thrower, N>& v) {\n    std::vector<Thrower> b;\n    b.emplace_back();\n    b.emplace_back();\n    b.emplace_back();\n\n    /*\n     * Apparently if you do the following initializer_list instead\n     * of the above push_back's, and one of the Throwers throws,\n     * g++4.6 doesn't destruct the previous ones.  Heh.\n     */\n    // b = { Thrower(), Thrower(), Thrower() };\n    v.insert(v.begin() + 1, b.begin(), b.end());\n  });\n\n  TestBasicGuarantee<N>(2)(6, [&](folly::small_vector<Thrower, N>& v) {\n    std::vector<Thrower> b;\n    for (int i = 0; i < 6; ++i) {\n      b.emplace_back();\n    }\n\n    v.insert(v.begin() + 1, b.begin(), b.end());\n  });\n}\n\nTEST(smallVector, BasicGuarantee) {\n  testBasicGuarantee<3>();\n\n  EXPECT_EQ(Thrower::alive, 0);\n  try {\n    throwCounter = 4;\n    folly::small_vector<Thrower, 1> p(14, Thrower());\n  } catch (...) {\n  }\n  EXPECT_EQ(Thrower::alive, 0);\n\n  // Heap only small_vector\n  testBasicGuarantee<0>();\n}\n\n// Run this with.\n// MALLOC_CONF=prof_leak:true\n// LD_PRELOAD=${JEMALLOC_PATH}/lib/libjemalloc.so.2\n// LD_PRELOAD=\"$LD_PRELOAD:\"${UNWIND_PATH}/lib/libunwind.so.7\nTEST(smallVector, leakTest) {\n  for (int j = 0; j < 1000; ++j) {\n    folly::small_vector<int, 10> someVec(300);\n    for (int i = 0; i < 10000; ++i) {\n      someVec.push_back(12);\n    }\n  }\n\n  for (int j = 0; j < 1000; ++j) {\n    folly::small_vector<int, 0> someVec(300);\n    for (int i = 0; i < 10000; ++i) {\n      someVec.push_back(12);\n    }\n  }\n}\n\nTEST(smallVector, leakTestWithTracking) {\n  constexpr size_t size = 97;\n  constexpr auto count = folly::annotate_object_count_leaked_uncollected;\n  auto const base = count();\n  small_vector<int> vec;\n  vec.resize(size);\n  EXPECT_EQ(size, vec.size());\n  EXPECT_EQ(base + size_t(folly::kIsSanitizeAddress), count());\n  vec.resize(0);\n  EXPECT_EQ(0, vec.size());\n  EXPECT_EQ(base + size_t(folly::kIsSanitizeAddress), count());\n  vec.shrink_to_fit();\n  EXPECT_LT(vec.capacity(), size);\n  EXPECT_EQ(base, count());\n}\n\nTEST(smallVector, leakTestWithLeakedObject) {\n  {\n    // this case does not actually need leak-tracking\n    using vec_t = folly::small_vector<int, 1>;\n    constexpr size_t size = 97;\n    static auto& vec = *new vec_t();\n    vec.resize(size);\n    EXPECT_EQ(size, vec.size());\n  }\n\n  {\n    // this case does need leak-tracking\n    using policy_t = folly::small_vector_policy::policy_size_type<uint32_t>;\n    using vec_t = folly::small_vector<int, 1, policy_t>;\n    constexpr size_t size = 97;\n    static auto& vec = *new vec_t();\n    vec.resize(size);\n    EXPECT_EQ(size, vec.size());\n  }\n}\n\nTEST(smallVector, InsertTrivial) {\n  folly::small_vector<int> someVec(3, 3);\n  someVec.insert(someVec.begin(), 12, 12);\n  EXPECT_EQ(someVec.size(), 15);\n  for (size_t i = 0; i < someVec.size(); ++i) {\n    if (i < 12) {\n      EXPECT_EQ(someVec[i], 12);\n    } else {\n      EXPECT_EQ(someVec[i], 3);\n    }\n  }\n\n  // Make sure we insert a larger range so we can test placement new\n  // and move inserts\n  auto oldSize = someVec.size();\n  someVec.insert(someVec.begin() + 1, 30, 30);\n  EXPECT_EQ(someVec.size(), oldSize + 30);\n  EXPECT_EQ(someVec[0], 12);\n  EXPECT_EQ(someVec[1], 30);\n  EXPECT_EQ(someVec[31], 12);\n}\n\nTEST(smallVector, InsertNontrivial) {\n  folly::small_vector<std::string> v1(6, \"asd\"), v2(7, \"wat\");\n  v1.insert(v1.begin() + 1, v2.begin(), v2.end());\n  EXPECT_TRUE(v1.size() == 6 + 7);\n  EXPECT_EQ(v1.front(), \"asd\");\n  EXPECT_EQ(v1[1], \"wat\");\n\n  // Insert without default constructor\n  class TestClass {\n   public:\n    // explicit TestClass() = default;\n    explicit TestClass(std::string s_) : s(s_) {}\n    std::string s;\n  };\n  folly::small_vector<TestClass> v3(5, TestClass(\"asd\"));\n  folly::small_vector<TestClass> v4(10, TestClass(\"wat\"));\n  v3.insert(v3.begin() + 1, v4.begin(), v4.end());\n  EXPECT_TRUE(v3.size() == 5 + 10);\n  EXPECT_EQ(v3[0].s, \"asd\");\n  EXPECT_EQ(v3[1].s, \"wat\");\n  EXPECT_EQ(v3[10].s, \"wat\");\n  EXPECT_EQ(v3[11].s, \"asd\");\n}\n\nTEST(smallVector, InsertFromBidirectionalList) {\n  folly::small_vector<std::string> v(6, \"asd\");\n  std::list<std::string> l(6, \"wat\");\n  v.insert(v.end(), l.begin(), l.end());\n  EXPECT_EQ(v[0], \"asd\");\n  EXPECT_EQ(v[5], \"asd\");\n  EXPECT_EQ(v[6], \"wat\");\n  EXPECT_EQ(v[11], \"wat\");\n}\n\ntemplate <int N>\nvoid testSwap() {\n  folly::small_vector<int, N> somethingVec, emptyVec;\n  somethingVec.push_back(1);\n  somethingVec.push_back(2);\n  somethingVec.push_back(3);\n  somethingVec.push_back(4);\n\n  // Swapping intern'd with intern'd.\n  auto vec = somethingVec;\n  EXPECT_TRUE(vec == somethingVec);\n  EXPECT_FALSE(vec == emptyVec);\n  EXPECT_FALSE(somethingVec == emptyVec);\n\n  // Swapping a heap vector with an intern vector.\n  folly::small_vector<int, N> junkVec;\n  junkVec.assign(12, 12);\n  EXPECT_EQ(junkVec.size(), 12);\n  for (auto i : junkVec) {\n    EXPECT_EQ(i, 12);\n  }\n  swap(junkVec, vec);\n  EXPECT_TRUE(junkVec == somethingVec);\n  EXPECT_EQ(vec.size(), 12);\n  for (auto i : vec) {\n    EXPECT_EQ(i, 12);\n  }\n\n  // Swapping two heap vectors.\n  folly::small_vector<int, N> moreJunk(15, 15);\n  EXPECT_EQ(moreJunk.size(), 15);\n  for (auto i : moreJunk) {\n    EXPECT_EQ(i, 15);\n  }\n  swap(vec, moreJunk);\n  EXPECT_EQ(moreJunk.size(), 12);\n  for (auto i : moreJunk) {\n    EXPECT_EQ(i, 12);\n  }\n  EXPECT_EQ(vec.size(), 15);\n  for (auto i : vec) {\n    EXPECT_EQ(i, 15);\n  }\n}\n\nTEST(smallVector, Swap) {\n  testSwap<10>();\n\n  // heap only small_vector\n  testSwap<0>();\n\n  // Making a vector heap, then smaller than another non-heap vector,\n  // then swapping.\n  folly::small_vector<int, 5> shrinker, other(4, 10);\n  shrinker = {0, 1, 2, 3, 4, 5, 6, 7, 8};\n  shrinker.erase(shrinker.begin() + 2, shrinker.end());\n  EXPECT_LT(shrinker.size(), other.size());\n  swap(shrinker, other);\n  EXPECT_EQ(shrinker.size(), 4);\n  EXPECT_TRUE(boost::all(shrinker, boost::is_any_of(std::vector<int>{10})));\n  EXPECT_TRUE((other == small_vector<int, 5>{0, 1}));\n}\n\ntemplate <int N>\nvoid testEmplace() {\n  NontrivialType::ctored = 0;\n\n  folly::small_vector<NontrivialType, N> vec;\n  vec.reserve(1024);\n  {\n    auto& emplaced = vec.emplace_back(12);\n    EXPECT_EQ(NontrivialType::ctored, 1);\n    EXPECT_EQ(vec.front().a, 12);\n    EXPECT_TRUE(std::addressof(emplaced) == std::addressof(vec.back()));\n  }\n  {\n    auto& emplaced = vec.emplace_back(13);\n    EXPECT_EQ(vec.front().a, 12);\n    EXPECT_EQ(vec.back().a, 13);\n    EXPECT_EQ(NontrivialType::ctored, 2);\n    EXPECT_TRUE(std::addressof(emplaced) == std::addressof(vec.back()));\n  }\n\n  NontrivialType::ctored = 0;\n  for (int i = 0; i < 120; ++i) {\n    auto& emplaced = vec.emplace_back(i);\n    EXPECT_TRUE(std::addressof(emplaced) == std::addressof(vec.back()));\n  }\n  EXPECT_EQ(NontrivialType::ctored, 120);\n  EXPECT_EQ(vec[0].a, 12);\n  EXPECT_EQ(vec[1].a, 13);\n  EXPECT_EQ(vec.back().a, 119);\n\n  // We implement emplace() with a temporary (see the implementation\n  // for a comment about why), so this should make 2 ctor calls.\n  NontrivialType::ctored = 0;\n  vec.emplace(vec.begin(), 12);\n  EXPECT_EQ(NontrivialType::ctored, 2);\n}\n\nTEST(smallVector, Emplace) {\n  testEmplace<1>();\n  // heap only\n  testEmplace<0>();\n}\n\nTEST(smallVector, Erase) {\n  folly::small_vector<int, 4> notherVec = {1, 2, 3, 4, 5};\n  EXPECT_EQ(notherVec.front(), 1);\n  EXPECT_EQ(notherVec.size(), 5);\n  notherVec.erase(notherVec.begin());\n  EXPECT_EQ(notherVec.front(), 2);\n  EXPECT_EQ(notherVec.size(), 4);\n  EXPECT_EQ(notherVec[2], 4);\n  EXPECT_EQ(notherVec[3], 5);\n  notherVec.erase(notherVec.begin() + 2);\n  EXPECT_EQ(notherVec.size(), 3);\n  EXPECT_EQ(notherVec[2], 5);\n\n  folly::small_vector<int, 2> vec2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};\n  vec2.erase(vec2.begin() + 1, vec2.end() - 1);\n  folly::small_vector<int, 2> expected = {1, 10};\n  EXPECT_TRUE(vec2 == expected);\n\n  folly::small_vector<std::string, 3> v(102, \"ASD\");\n  v.resize(1024, \"D\");\n  EXPECT_EQ(v.size(), 1024);\n  EXPECT_EQ(v.back(), \"D\");\n  EXPECT_EQ(v.front(), \"ASD\");\n  v.resize(1);\n  EXPECT_EQ(v.front(), \"ASD\");\n  EXPECT_EQ(v.size(), 1);\n  v.resize(0);\n  EXPECT_TRUE(v.empty());\n\n  // test heap only\n  folly::small_vector<int, 0> vec3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};\n  vec3.erase(vec3.begin() + 1, vec3.end() - 1);\n  folly::small_vector<int, 0> expected3 = {1, 10};\n  EXPECT_TRUE(vec3 == expected3);\n}\n\ntemplate <int N>\nvoid testGrowShrinkGrow() {\n  folly::small_vector<NontrivialType, N> vec = {1, 2, 3, 4, 5};\n  std::generate_n(std::back_inserter(vec), 102, std::rand);\n\n  auto capacity = vec.capacity();\n\n  auto oldSize = vec.size();\n  for (size_t i = 0; i < oldSize; ++i) {\n    vec.erase(vec.begin() + (std::rand() % vec.size()));\n    EXPECT_EQ(vec.capacity(), capacity);\n  }\n  EXPECT_TRUE(vec.empty());\n\n  EXPECT_EQ(vec.capacity(), capacity);\n  std::generate_n(std::back_inserter(vec), 102, std::rand);\n  EXPECT_EQ(vec.capacity(), capacity);\n\n  std::generate_n(std::back_inserter(vec), 4096, std::rand);\n  EXPECT_GT(vec.capacity(), capacity);\n\n  vec.resize(10);\n  vec.shrink_to_fit();\n  EXPECT_LT(vec.capacity(), capacity);\n  auto cap = vec.capacity();\n  vec.resize(4);\n  vec.shrink_to_fit();\n  if (N > 4) {\n    EXPECT_EQ(vec.capacity(), N); // in situ size\n  } else {\n    EXPECT_LT(vec.capacity(), cap); // on heap\n  }\n}\n\nTEST(smallVector, GrowShrinkGrow) {\n  testGrowShrinkGrow<7>();\n\n  testGrowShrinkGrow<0>();\n}\n\nTEST(smallVector, ShrinkToFitMoveOnly) {\n  folly::small_vector<std::unique_ptr<int>> vec;\n  vec.reserve(100);\n  for (int i = 0; i < 3; ++i) {\n    vec.push_back(std::make_unique<int>(i));\n  }\n  vec.shrink_to_fit();\n  EXPECT_LT(vec.capacity(), 100);\n  ASSERT_EQ(vec.size(), 3);\n  for (int i = 0; i < 3; ++i) {\n    ASSERT_NE(vec[i], nullptr);\n    EXPECT_EQ(*vec[i], i);\n  }\n}\n\nTEST(smallVector, Iteration) {\n  folly::small_vector<std::string, 3> vec = {\"foo\", \"bar\"};\n  vec.push_back(\"blah\");\n  vec.push_back(\"blah2\");\n  vec.push_back(\"blah3\");\n  vec.erase(vec.begin() + 2);\n\n  std::vector<std::string> otherVec;\n  for (auto& s : vec) {\n    otherVec.push_back(s);\n  }\n  EXPECT_EQ(otherVec.size(), vec.size());\n  if (otherVec.size() == vec.size()) {\n    EXPECT_TRUE(std::equal(otherVec.begin(), otherVec.end(), vec.begin()));\n  }\n\n  std::reverse(otherVec.begin(), otherVec.end());\n  auto oit = otherVec.begin();\n  auto rit = vec.crbegin();\n  for (; rit != vec.crend(); ++oit, ++rit) {\n    EXPECT_EQ(*oit, *rit);\n  }\n}\n\nTEST(smallVector, NonCopyableType) {\n  folly::small_vector<NontrivialType, 2> vec;\n\n  for (int i = 0; i < 10; ++i) {\n    vec.emplace(vec.begin(), 13);\n  }\n  EXPECT_EQ(vec.size(), 10);\n  auto vec2 = std::move(vec);\n  EXPECT_EQ(vec.size(), 0);\n  EXPECT_EQ(vec2.size(), 10);\n  vec2.clear();\n\n  folly::small_vector<NoncopyableCounter, 3> vec3;\n  for (int i = 0; i < 10; ++i) {\n    EXPECT_EQ(vec3.size(), i);\n    EXPECT_EQ(NoncopyableCounter::alive, i);\n    vec3.insert(vec3.begin(), NoncopyableCounter());\n  }\n  EXPECT_EQ(vec3.size(), 10);\n  EXPECT_EQ(NoncopyableCounter::alive, 10);\n\n  vec3.insert(vec3.begin() + 3, NoncopyableCounter());\n  EXPECT_EQ(NoncopyableCounter::alive, 11);\n  auto vec4 = std::move(vec3);\n  EXPECT_EQ(NoncopyableCounter::alive, 11);\n  vec4.resize(30);\n  EXPECT_EQ(NoncopyableCounter::alive, 30);\n  vec4.erase(vec4.begin(), vec4.end());\n  EXPECT_EQ(vec4.size(), 0);\n  EXPECT_EQ(NoncopyableCounter::alive, 0);\n\n  {\n    folly::small_vector<NontrivialType, 0> vec0;\n\n    for (int i = 0; i < 10; ++i) {\n      vec0.emplace(vec0.begin(), 13);\n    }\n    EXPECT_EQ(vec0.size(), 10);\n    auto vec02 = std::move(vec0);\n    EXPECT_EQ(vec0.size(), 0);\n    EXPECT_EQ(vec02.size(), 10);\n    vec02.clear();\n  }\n}\n\ntemplate <int N>\nvoid testMoveConstructor() {\n  folly::small_vector<std::string, N> v1;\n  v1.push_back(\"asd\");\n  v1.push_back(\"bsd\");\n  auto v2 = std::move(v1);\n  EXPECT_EQ(v2.size(), 2);\n  EXPECT_EQ(v2[0], \"asd\");\n  EXPECT_EQ(v2[1], \"bsd\");\n\n  v1 = std::move(v2);\n  EXPECT_EQ(v1.size(), 2);\n  EXPECT_EQ(v1[0], \"asd\");\n  EXPECT_EQ(v1[1], \"bsd\");\n}\n\nTEST(smallVector, MoveConstructor) {\n  testMoveConstructor<10>();\n  testMoveConstructor<0>();\n}\n\nTEST(smallVector, NoHeap) {\n  using Vector =\n      folly::small_vector<std::string, 10, policy_in_situ_only<true>>;\n\n  Vector v;\n  static_assert(v.max_size() == 10, \"max_size is incorrect\");\n\n  for (int i = 0; i < 10; ++i) {\n    v.push_back(folly::to<std::string>(i));\n    EXPECT_EQ(v.size(), i + 1);\n  }\n\n  bool caught = false;\n  try {\n    v.insert(v.begin(), \"ha\");\n  } catch (const std::length_error&) {\n    caught = true;\n  }\n  EXPECT_TRUE(caught);\n\n  // Check max_size works right with various policy combinations.\n  folly::small_vector<std::string, 32, policy_size_type<uint32_t>> v4;\n  EXPECT_EQ(v4.max_size(), (uint32_t(1) << 30) - 1);\n\n  /*\n   * Test that even when we ask for a small number inlined it'll still\n   * inline at least as much as it takes to store the value_type\n   * pointer.\n   */\n  folly::small_vector<char, 1, policy_in_situ_only<true>> notsosmall;\n  static_assert(\n      notsosmall.max_size() == sizeof(char*), \"max_size is incorrect\");\n  caught = false;\n  try {\n    notsosmall.push_back(12);\n    notsosmall.push_back(13);\n    notsosmall.push_back(14);\n  } catch (const std::length_error&) {\n    caught = true;\n  }\n  EXPECT_FALSE(caught);\n}\n\nTEST(smallVector, MaxSize) {\n  folly::small_vector<int, 2, policy_size_type<uint8_t>> vec;\n  EXPECT_EQ(vec.max_size(), 63);\n  folly::small_vector<int, 2, policy_size_type<uint16_t>> vec2;\n  EXPECT_EQ(vec2.max_size(), (1 << 14) - 1);\n}\n\nTEST(smallVector, AllHeap) {\n  // Use something bigger than the pointer so it can't get inlined.\n  struct SomeObj {\n    double a, b, c, d, e;\n    int val;\n    SomeObj(int val_) : val(val_) {}\n    bool operator==(SomeObj const& o) const { return o.val == val; }\n  };\n\n  folly::small_vector<SomeObj, 0> vec = {1};\n  EXPECT_EQ(vec.size(), 1);\n  if (!vec.empty()) {\n    EXPECT_TRUE(vec[0] == 1);\n  }\n  vec.insert(vec.begin(), {0, 1, 2, 3});\n  EXPECT_EQ(vec.size(), 5);\n  EXPECT_TRUE((vec == folly::small_vector<SomeObj, 0>{0, 1, 2, 3, 1}));\n}\ntemplate <int N>\nvoid testBasic() {\n  using Vector = folly::small_vector<int, N, policy_size_type<uint32_t>>;\n\n  Vector a;\n\n  a.push_back(12);\n  EXPECT_EQ(a.front(), 12);\n  EXPECT_EQ(a.size(), 1);\n  a.push_back(13);\n  EXPECT_EQ(a.size(), 2);\n  EXPECT_EQ(a.front(), 12);\n  EXPECT_EQ(a.back(), 13);\n\n  a.emplace(a.end(), 32);\n  EXPECT_EQ(a.back(), 32);\n\n  a.emplace(a.begin(), 12);\n  EXPECT_EQ(a.front(), 12);\n  EXPECT_EQ(a.back(), 32);\n  a.erase(a.end() - 1);\n  EXPECT_EQ(a.back(), 13);\n\n  a.push_back(12);\n  EXPECT_EQ(a.back(), 12);\n  a.pop_back();\n  EXPECT_EQ(a.back(), 13);\n\n  const int s = 12;\n  a.push_back(s); // lvalue reference\n\n  Vector b, c;\n  b = a;\n  EXPECT_TRUE(b == a);\n  c = std::move(b);\n  EXPECT_TRUE(c == a);\n  EXPECT_TRUE(c != b && b != a);\n\n  EXPECT_GT(c.size(), 0);\n  c.resize(1);\n  EXPECT_EQ(c.size(), 1);\n\n  Vector intCtor(12);\n}\n\nTEST(smallVector, Basic) {\n  testBasic<3>();\n  testBasic<0>();\n}\n\nTEST(smallVector, Capacity) {\n  folly::small_vector<uint64_t, 1> vec;\n  EXPECT_EQ(vec.size(), 0);\n  EXPECT_EQ(vec.capacity(), 1);\n\n  vec.push_back(0);\n  EXPECT_EQ(vec.size(), 1);\n  EXPECT_EQ(vec.capacity(), 1);\n\n  vec.push_back(1);\n  EXPECT_EQ(vec.size(), 2);\n  EXPECT_GT(vec.capacity(), 1);\n\n  folly::small_vector<uint64_t, 2> vec2;\n  EXPECT_EQ(vec2.size(), 0);\n  EXPECT_EQ(vec2.capacity(), 2);\n\n  vec2.push_back(0);\n  vec2.push_back(1);\n  EXPECT_EQ(vec2.size(), 2);\n  EXPECT_EQ(vec2.capacity(), 2);\n\n  vec2.push_back(2);\n  EXPECT_EQ(vec2.size(), 3);\n  EXPECT_GT(vec2.capacity(), 2);\n\n  // Test capacity heapifying logic\n  folly::small_vector<unsigned char, 1> vec3;\n  const size_t hc_size = 100000;\n  for (size_t i = 0; i < hc_size; ++i) {\n    auto v = (unsigned char)i;\n    vec3.push_back(v);\n    EXPECT_EQ(vec3[i], v);\n    EXPECT_EQ(vec3.size(), i + 1);\n    EXPECT_GT(vec3.capacity(), i);\n  }\n  for (auto i = hc_size; i > 0; --i) {\n    auto v = (unsigned char)(i - 1);\n    EXPECT_EQ(vec3.back(), v);\n    vec3.pop_back();\n    EXPECT_EQ(vec3.size(), i - 1);\n  }\n}\n\ntemplate <int N>\nvoid testSelfPushBack() {\n  for (int i = 1; i < 33; ++i) {\n    folly::small_vector<std::string, N> vec;\n    for (int j = 0; j < i; ++j) {\n      vec.push_back(\"abc\");\n    }\n    EXPECT_EQ(vec.size(), i);\n    vec.push_back(std::move(vec[0]));\n    EXPECT_EQ(vec.size(), i + 1);\n\n    EXPECT_EQ(vec[i], \"abc\");\n  }\n}\n\nTEST(smallVector, SelfPushBack) {\n  testSelfPushBack<1>();\n  testSelfPushBack<0>();\n}\n\ntemplate <int N>\nvoid testSelfEmplaceBack() {\n  for (int i = 1; i < 33; ++i) {\n    folly::small_vector<std::string, N> vec;\n    for (int j = 0; j < i; ++j) {\n      vec.emplace_back(\"abc\");\n    }\n    EXPECT_EQ(vec.size(), i);\n    vec.emplace_back(std::move(vec[0]));\n    EXPECT_EQ(vec.size(), i + 1);\n\n    EXPECT_EQ(vec[i], \"abc\");\n  }\n}\n\nTEST(smallVector, SelfEmplaceBack) {\n  testSelfEmplaceBack<1>();\n  testSelfEmplaceBack<0>();\n}\n\ntemplate <int N>\nvoid testSelfInsert() {\n  // end insert\n  for (int i = 1; i < 33; ++i) {\n    folly::small_vector<std::string, N> vec;\n    for (int j = 0; j < i; ++j) {\n      vec.push_back(\"abc\");\n    }\n    EXPECT_EQ(vec.size(), i);\n    vec.insert(vec.end(), std::move(vec[0]));\n    EXPECT_EQ(vec.size(), i + 1);\n\n    EXPECT_EQ(vec[i], \"abc\");\n    EXPECT_EQ(vec[vec.size() - 1], \"abc\");\n  }\n\n  // middle insert\n  for (int i = 2; i < 33; ++i) {\n    folly::small_vector<std::string, N> vec;\n    for (int j = 0; j < i; ++j) {\n      vec.push_back(\"abc\");\n    }\n    EXPECT_EQ(vec.size(), i);\n    vec.insert(vec.end() - 1, std::move(vec[0]));\n    EXPECT_EQ(vec.size(), i + 1);\n\n    EXPECT_EQ(vec[i - 1], \"abc\");\n    EXPECT_EQ(vec[i], \"abc\");\n  }\n\n  // range insert\n  for (int i = 2; i < 33; ++i) {\n    folly::small_vector<std::string, N> vec;\n    // reserve 2 * i space so we don't grow and invalidate references.\n    vec.reserve(2 * i);\n    for (int j = 0; j < i; ++j) {\n      vec.push_back(\"abc\");\n    }\n    EXPECT_EQ(vec.size(), i);\n    vec.insert(vec.end() - 1, vec.begin(), vec.end() - 1);\n    EXPECT_EQ(vec.size(), 2 * i - 1);\n\n    for (auto const& val : vec) {\n      EXPECT_EQ(val, \"abc\");\n    }\n  }\n}\n\nTEST(smallVector, SelfInsert) {\n  testSelfInsert<1>();\n\n  testSelfInsert<0>();\n}\n\nstruct CheckedInt {\n  static const int DEFAULT_VALUE = (int)0xdeadbeef;\n  CheckedInt() : value(DEFAULT_VALUE) {}\n  explicit CheckedInt(int value_) : value(value_) {}\n  CheckedInt(const CheckedInt& rhs, int) : value(rhs.value) {}\n  CheckedInt(const CheckedInt& rhs) : value(rhs.value) {}\n  CheckedInt(CheckedInt&& rhs) noexcept : value(rhs.value) {\n    rhs.value = DEFAULT_VALUE;\n  }\n  CheckedInt& operator=(const CheckedInt& rhs) {\n    value = rhs.value;\n    return *this;\n  }\n  CheckedInt& operator=(CheckedInt&& rhs) noexcept {\n    value = rhs.value;\n    rhs.value = DEFAULT_VALUE;\n    return *this;\n  }\n  ~CheckedInt() {}\n  int value;\n};\n\nTEST(smallVector, ForwardingEmplaceInsideVector) {\n  folly::small_vector<CheckedInt> v;\n  v.push_back(CheckedInt(1));\n  for (int i = 1; i < 20; ++i) {\n    v.emplace_back(v[0], 42);\n    ASSERT_EQ(1, v.back().value);\n  }\n}\n\nTEST(smallVector, LVEmplaceInsideVector) {\n  folly::small_vector<CheckedInt> v;\n  v.push_back(CheckedInt(1));\n  for (int i = 1; i < 20; ++i) {\n    v.emplace_back(v[0]);\n    ASSERT_EQ(1, v.back().value);\n  }\n}\n\nTEST(smallVector, CLVEmplaceInsideVector) {\n  folly::small_vector<CheckedInt> v;\n  const folly::small_vector<CheckedInt>& cv = v;\n  v.push_back(CheckedInt(1));\n  for (int i = 1; i < 20; ++i) {\n    v.emplace_back(cv[0]);\n    ASSERT_EQ(1, v.back().value);\n  }\n}\n\nTEST(smallVector, RVEmplaceInsideVector) {\n  folly::small_vector<CheckedInt> v;\n  v.push_back(CheckedInt(0));\n  for (int i = 1; i < 20; ++i) {\n    v[0] = CheckedInt(1);\n    v.emplace_back(std::move(v[0]));\n    ASSERT_EQ(1, v.back().value);\n  }\n}\n\nTEST(smallVector, LVPushValueInsideVector) {\n  folly::small_vector<CheckedInt> v;\n  v.push_back(CheckedInt(1));\n  for (int i = 1; i < 20; ++i) {\n    v.push_back(v[0]);\n    ASSERT_EQ(1, v.back().value);\n  }\n}\n\nTEST(smallVector, RVPushValueInsideVector) {\n  folly::small_vector<CheckedInt> v;\n  v.push_back(CheckedInt(0));\n  for (int i = 1; i < 20; ++i) {\n    v[0] = CheckedInt(1);\n    v.push_back(v[0]);\n    ASSERT_EQ(1, v.back().value);\n  }\n}\n\nTEST(smallVector, EmplaceIterCtor) {\n  std::vector<int*> v{new int(1), new int(2)};\n  std::vector<std::unique_ptr<int>> uv(v.begin(), v.end());\n\n  std::vector<int*> w{new int(1), new int(2)};\n  small_vector<std::unique_ptr<int>> uw(w.begin(), w.end());\n}\n\nTEST(smallVector, InputIterator) {\n  std::vector<int> expected{125, 320, 512, 750, 333};\n  std::string values = \"125 320 512 750 333\";\n  std::istringstream is1(values);\n  std::istringstream is2(values);\n\n  std::vector<int> stdV{\n      std::istream_iterator<int>(is1), std::istream_iterator<int>()};\n  ASSERT_EQ(stdV.size(), expected.size());\n  for (size_t i = 0; i < expected.size(); i++) {\n    ASSERT_EQ(stdV[i], expected[i]);\n  }\n\n  small_vector<int> smallV{\n      std::istream_iterator<int>(is2), std::istream_iterator<int>()};\n  ASSERT_EQ(smallV.size(), expected.size());\n  for (size_t i = 0; i < expected.size(); i++) {\n    ASSERT_EQ(smallV[i], expected[i]);\n  }\n}\n\nTEST(smallVector, NoCopyCtor) {\n  struct Tester {\n    Tester() = default;\n    Tester(const Tester&) = delete;\n    [[maybe_unused]] Tester(Tester&&) = default;\n\n    int field = 42;\n  };\n\n  small_vector<Tester> test(10);\n  ASSERT_EQ(test.size(), 10);\n  for (const auto& element : test) {\n    EXPECT_EQ(element.field, 42);\n  }\n}\n\nTEST(smallVector, ZeroInitializable) {\n  small_vector<int> test(10);\n  ASSERT_EQ(test.size(), 10);\n  for (const auto& element : test) {\n    EXPECT_EQ(element, 0);\n  }\n}\n\nTEST(smallVector, InsertMoreThanGrowth) {\n  small_vector<int, 10> test;\n  test.insert(test.end(), 30, 0);\n  for (auto element : test) {\n    EXPECT_EQ(element, 0);\n  }\n}\n\nTEST(smallVector, EmplaceBackExponentialGrowth) {\n  small_vector<std::pair<int, int>> test;\n  std::vector<size_t> capacities;\n  capacities.push_back(test.capacity());\n  for (int i = 0; i < 10000; ++i) {\n    test.emplace_back(0, 0);\n    if (test.capacity() != capacities.back()) {\n      capacities.push_back(test.capacity());\n    }\n  }\n  EXPECT_LE(capacities.size(), 25);\n}\n\nTEST(smallVector, InsertExponentialGrowth) {\n  small_vector<std::pair<int, int>> test;\n  std::vector<size_t> capacities;\n  capacities.push_back(test.capacity());\n  for (int i = 0; i < 10000; ++i) {\n    test.insert(test.begin(), std::make_pair(0, 0));\n    if (test.capacity() != capacities.back()) {\n      capacities.push_back(test.capacity());\n    }\n  }\n  EXPECT_LE(capacities.size(), 25);\n}\n\nTEST(smallVector, InsertNExponentialGrowth) {\n  small_vector<int> test;\n  std::vector<size_t> capacities;\n  capacities.push_back(test.capacity());\n  for (int i = 0; i < 10000; ++i) {\n    test.insert(test.begin(), 100, 0);\n    if (test.capacity() != capacities.back()) {\n      capacities.push_back(test.capacity());\n    }\n  }\n  EXPECT_LE(capacities.size(), 25);\n}\n\nnamespace {\nstruct Counts {\n  size_t copyCount{0};\n  size_t moveCount{0};\n};\n\nclass Counter {\n  Counts* counts;\n\n public:\n  explicit Counter(Counts& counts_) : counts(&counts_) {}\n  Counter(Counter const& other) noexcept : counts(other.counts) {\n    ++counts->copyCount;\n  }\n  Counter(Counter&& other) noexcept : counts(other.counts) {\n    ++counts->moveCount;\n  }\n  Counter& operator=(Counter const& rhs) noexcept {\n    EXPECT_EQ(counts, rhs.counts);\n    ++counts->copyCount;\n    return *this;\n  }\n  [[maybe_unused]] Counter& operator=(Counter&& rhs) noexcept {\n    EXPECT_EQ(counts, rhs.counts);\n    ++counts->moveCount;\n    return *this;\n  }\n};\n} // namespace\n\nTEST(smallVector, EmplaceBackEfficiency) {\n  small_vector<Counter, 2> test;\n  Counts counts;\n  for (size_t i = 1; i <= test.capacity(); ++i) {\n    test.emplace_back(counts);\n    EXPECT_EQ(0, counts.copyCount);\n    EXPECT_EQ(0, counts.moveCount);\n  }\n  EXPECT_EQ(test.size(), test.capacity());\n  test.emplace_back(counts);\n  // Every element except the last has to be moved to the new position\n  EXPECT_EQ(0, counts.copyCount);\n  EXPECT_EQ(test.size() - 1, counts.moveCount);\n  EXPECT_LT(test.size(), test.capacity());\n}\n\nTEST(smallVector, RVPushBackEfficiency) {\n  small_vector<Counter, 2> test;\n  Counts counts;\n  for (size_t i = 1; i <= test.capacity(); ++i) {\n    test.push_back(Counter(counts));\n    // 1 copy for each push_back()\n    EXPECT_EQ(0, counts.copyCount);\n    EXPECT_EQ(i, counts.moveCount);\n  }\n  EXPECT_EQ(test.size(), test.capacity());\n  test.push_back(Counter(counts));\n  // 1 move for each push_back()\n  // Every element except the last has to be moved to the new position\n  EXPECT_EQ(0, counts.copyCount);\n  EXPECT_EQ(test.size() + test.size() - 1, counts.moveCount);\n  EXPECT_LT(test.size(), test.capacity());\n}\n\nTEST(smallVector, CLVPushBackEfficiency) {\n  small_vector<Counter, 2> test;\n  Counts counts;\n  Counter const counter(counts);\n  for (size_t i = 1; i <= test.capacity(); ++i) {\n    test.push_back(counter);\n    // 1 copy for each push_back()\n    EXPECT_EQ(i, counts.copyCount);\n    EXPECT_EQ(0, counts.moveCount);\n  }\n  EXPECT_EQ(test.size(), test.capacity());\n  test.push_back(counter);\n  // 1 copy for each push_back()\n  EXPECT_EQ(test.size(), counts.copyCount);\n  // Every element except the last has to be moved to the new position\n  EXPECT_EQ(test.size() - 1, counts.moveCount);\n  EXPECT_LT(test.size(), test.capacity());\n}\n\nTEST(smallVector, StorageForSortedVectorMap) {\n  folly::small_sorted_vector_map<int32_t, int32_t, 2> test;\n  test.insert(std::make_pair(10, 10));\n  EXPECT_EQ(test.size(), 1);\n  test.insert(std::make_pair(10, 10));\n  EXPECT_EQ(test.size(), 1);\n  test.insert(std::make_pair(20, 10));\n  EXPECT_EQ(test.size(), 2);\n  test.insert(std::make_pair(30, 10));\n  EXPECT_EQ(test.size(), 3);\n}\n\nTEST(smallVector, NoHeapStorageForSortedVectorMap) {\n  noheap_sorted_vector_map<int32_t, int32_t, 2> test;\n  test.insert(std::make_pair(10, 10));\n  EXPECT_EQ(test.size(), 1);\n  test.insert(std::make_pair(10, 10));\n  EXPECT_EQ(test.size(), 1);\n  test.insert(std::make_pair(20, 10));\n  EXPECT_EQ(test.size(), 2);\n  EXPECT_THROW(test.insert(std::make_pair(30, 10)), std::length_error);\n  EXPECT_EQ(test.size(), 2);\n}\n\nTEST(smallVector, StorageForSortedVectorSet) {\n  folly::small_sorted_vector_set<int32_t, 2> test;\n  test.insert(10);\n  EXPECT_EQ(test.size(), 1);\n  test.insert(10);\n  EXPECT_EQ(test.size(), 1);\n  test.insert(20);\n  EXPECT_EQ(test.size(), 2);\n  test.insert(30);\n  EXPECT_EQ(test.size(), 3);\n}\n\nTEST(smallVector, NoHeapStorageForSortedVectorSet) {\n  noheap_sorted_vector_set<int32_t, 2> test;\n  test.insert(10);\n  EXPECT_EQ(test.size(), 1);\n  test.insert(10);\n  EXPECT_EQ(test.size(), 1);\n  test.insert(20);\n  EXPECT_EQ(test.size(), 2);\n  EXPECT_THROW(test.insert(30), std::length_error);\n  EXPECT_EQ(test.size(), 2);\n}\n\nTEST(smallVector, SelfMoveAssignmentForVectorOfPair) {\n  folly::small_vector<std::pair<int, int>, 2> test;\n  test.emplace_back(13, 2);\n  EXPECT_EQ(test.size(), 1);\n  EXPECT_EQ(test[0].first, 13);\n  test = std::move(std::move(test)); // suppress self-move warning\n  EXPECT_EQ(test.size(), 1);\n  EXPECT_EQ(test[0].first, 13);\n}\n\nTEST(smallVector, SelfCopyAssignmentForVectorOfPair) {\n  folly::small_vector<std::pair<int, int>, 2> test;\n  test.emplace_back(13, 2);\n  EXPECT_EQ(test.size(), 1);\n  EXPECT_EQ(test[0].first, 13);\n  test = static_cast<decltype(test)&>(test); // suppress self-assign warning\n  EXPECT_EQ(test.size(), 1);\n  EXPECT_EQ(test[0].first, 13);\n}\n\nnamespace {\nstruct NonAssignableType {\n  int const i_{};\n};\n} // namespace\n\nTEST(smallVector, PopBackNonAssignableType) {\n  small_vector<NonAssignableType> v;\n  v.emplace_back();\n  EXPECT_EQ(1, v.size());\n  v.pop_back();\n  EXPECT_EQ(0, v.size());\n}\n\nTEST(smallVector, erase) {\n  small_vector<int> v(3);\n  std::iota(v.begin(), v.end(), 1);\n  v.push_back(2);\n  erase(v, 2);\n  ASSERT_EQ(2u, v.size());\n  EXPECT_EQ(1u, v[0]);\n  EXPECT_EQ(3u, v[1]);\n}\n\nTEST(smallVector, eraseIf) {\n  small_vector<int> v(6);\n  std::iota(v.begin(), v.end(), 1);\n  erase_if(v, [](const auto& x) { return x % 2 == 0; });\n  ASSERT_EQ(3u, v.size());\n  EXPECT_EQ(1u, v[0]);\n  EXPECT_EQ(3u, v[1]);\n  EXPECT_EQ(5u, v[2]);\n}\n\nnamespace {\n\nclass NonTrivialInt {\n public:\n  NonTrivialInt() {}\n  /* implicit */ NonTrivialInt(int value)\n      : value_(std::make_shared<int>(value)) {}\n\n  operator int() const { return *value_; }\n\n private:\n  std::shared_ptr<const int> value_;\n};\n\n// Move and copy constructor and assignment have several special cases depending\n// on relative sizes, so test all combinations.\ntemplate <class T, size_t N>\nvoid testMoveAndCopy() {\n  const auto fill = [](auto& v, size_t n) {\n    v.resize(n);\n    std::iota(v.begin(), v.end(), 0);\n  };\n  const auto verify = [](const auto& v, size_t n) {\n    ASSERT_EQ(v.size(), n);\n    for (size_t i = 0; i < n; ++i) {\n      EXPECT_EQ(v[i], i);\n    }\n  };\n\n  using Vec = small_vector<T, N>;\n  SCOPED_TRACE(fmt::format(\"N = {}\", N));\n\n  const size_t kMinCapacity = Vec{}.capacity();\n\n  for (size_t from = 0; from < 16; ++from) {\n    SCOPED_TRACE(fmt::format(\"from = {}\", from));\n\n    {\n      SCOPED_TRACE(\"Move-construction\");\n      Vec a;\n      fill(a, from);\n      const auto aCapacity = a.capacity();\n      Vec b = std::move(a);\n      verify(b, from);\n      EXPECT_EQ(b.capacity(), aCapacity);\n      verify(a, 0);\n      EXPECT_EQ(a.capacity(), kMinCapacity);\n    }\n    {\n      SCOPED_TRACE(\"Copy-construction\");\n      Vec a;\n      fill(a, from);\n      Vec b = a;\n      verify(b, from);\n      verify(a, from);\n    }\n\n    for (size_t to = 0; to < 16; ++to) {\n      SCOPED_TRACE(fmt::format(\"to = {}\", to));\n      {\n        SCOPED_TRACE(\"Move-assignment\");\n        Vec a;\n        fill(a, from);\n        Vec b;\n        fill(b, to);\n\n        const auto aCapacity = a.capacity();\n        b = std::move(a);\n        verify(b, from);\n        EXPECT_EQ(b.capacity(), aCapacity);\n        verify(a, 0);\n        EXPECT_EQ(a.capacity(), kMinCapacity);\n      }\n      {\n        SCOPED_TRACE(\"Copy-assignment\");\n        Vec a;\n        fill(a, from);\n        Vec b;\n        fill(b, to);\n\n        b = a;\n        verify(b, from);\n        verify(a, from);\n      }\n      {\n        SCOPED_TRACE(\"swap\");\n        Vec a;\n        fill(a, from);\n        Vec b;\n        fill(b, to);\n\n        const auto aCapacity = a.capacity();\n        const auto bCapacity = b.capacity();\n        swap(a, b);\n        verify(b, from);\n        EXPECT_EQ(b.capacity(), aCapacity);\n        verify(a, to);\n        EXPECT_EQ(a.capacity(), bCapacity);\n      }\n    }\n  }\n}\n\n} // namespace\n\nTEST(smallVector, MoveAndCopyTrivial) {\n  testMoveAndCopy<int, 0>();\n  // Capacity does not fit inline.\n  testMoveAndCopy<int, 2>();\n  // Capacity does fits inline.\n  testMoveAndCopy<int, 4>();\n}\n\nTEST(smallVector, MoveAndCopyNonTrivial) {\n  testMoveAndCopy<NonTrivialInt, 0>();\n  testMoveAndCopy<NonTrivialInt, 4>();\n}\n\nTEST(smallVector, PolicyMaxSizeExceeded) {\n  struct Obj {\n    char a[350];\n  };\n\n  small_vector<Obj, 10, policy_size_type<uint8_t>> v;\n\n  EXPECT_THROW(\n      for (size_t i = 0; i < 0x100; ++i) { v.push_back(Obj()); },\n      std::length_error);\n}\n\nTEST(smallVector, Hashable) {\n  small_vector<int> v0{{0, 1}};\n  small_vector<int> v1{{1, 2}};\n  std::unordered_set<small_vector<int>> s;\n  s.insert(v0);\n  EXPECT_NE(s.end(), s.find(v0));\n  EXPECT_EQ(s.end(), s.find(v1));\n}\n\nTEST(smallVector, overflowConstruct) {\n  EXPECT_THROW(\n      folly::small_vector<std::string>(SIZE_MAX / sizeof(std::string) + 1),\n      std::length_error);\n}\n\nTEST(smallVector, overflowResize) {\n  folly::small_vector<std::string> vec;\n  EXPECT_THROW(\n      vec.resize(SIZE_MAX / sizeof(std::string) + 1), std::length_error);\n}\n\nTEST(smallVector, overflowAssign) {\n  folly::small_vector<std::string> vec;\n  EXPECT_THROW(\n      vec.assign(SIZE_MAX / sizeof(std::string) + 1, \"hello\"),\n      std::length_error);\n}\n\nTEST(smallVector, assignZeroElementsNoInline) {\n  folly::small_vector<int, 0> v;\n  v.assign(0, 42);\n  EXPECT_TRUE(v.empty());\n}\n\nnamespace {\nstruct MaybeThrowOnCopy {\n  std::unique_ptr<bool> throwOnCopy;\n\n  explicit MaybeThrowOnCopy(bool t) : throwOnCopy(std::make_unique<bool>(t)) {}\n  MaybeThrowOnCopy(const MaybeThrowOnCopy& other)\n      : throwOnCopy(std::make_unique<bool>(other.throwOnCopy)) {\n    if (*other.throwOnCopy) {\n      throw std::runtime_error{\"test\"};\n    }\n  }\n};\n} // namespace\n\nTEST(smallVector, rangeConstructorForwardIteratorThrows) {\n  std::array<MaybeThrowOnCopy, 3> arr = {\n      MaybeThrowOnCopy{false}, MaybeThrowOnCopy{true}, MaybeThrowOnCopy{false}};\n\n  using ForwardIterator =\n      folly::index_iterator<std::array<MaybeThrowOnCopy, 3>>;\n\n  auto first = ForwardIterator{arr, 0};\n  auto last = ForwardIterator{arr, arr.size()};\n\n  using SV1 = small_vector<MaybeThrowOnCopy, 1>;\n  using SV3 = small_vector<MaybeThrowOnCopy, 3>;\n\n  EXPECT_THROW(SV1(first, last), std::runtime_error);\n  EXPECT_THROW(SV3(first, last), std::runtime_error);\n}\n\nTEST(smallVector, rangeConstructorInputIteratorThrows) {\n  std::array<MaybeThrowOnCopy, 3> arr = {\n      MaybeThrowOnCopy{false}, MaybeThrowOnCopy{true}, MaybeThrowOnCopy{false}};\n\n  class InputIterator\n      : public folly::index_iterator<std::array<MaybeThrowOnCopy, 3>> {\n   public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = MaybeThrowOnCopy const;\n    using reference = MaybeThrowOnCopy const&;\n    using pointer = MaybeThrowOnCopy const*;\n    using iterator_category = std::input_iterator_tag;\n\n    using index_iterator<std::array<MaybeThrowOnCopy, 3>>::index_iterator;\n  };\n\n  auto first = InputIterator{arr, 0};\n  auto last = InputIterator{arr, arr.size()};\n\n  using SV1 = small_vector<MaybeThrowOnCopy, 1>;\n  using SV3 = small_vector<MaybeThrowOnCopy, 3>;\n\n  EXPECT_THROW(SV1(first, last), std::runtime_error);\n  EXPECT_THROW(SV3(first, last), std::runtime_error);\n}\n\nTEST(smallVector, comparisons) {\n  folly::small_vector<int, 3> vec1 = {1, 2, 3, 4, 5};\n  folly::small_vector<int, 3> vec2 = {1, 2, 3, 4, 5};\n  EXPECT_EQ(vec1, vec2);\n  EXPECT_FALSE(vec1 < vec2);\n  EXPECT_TRUE(vec1 <= vec2);\n  EXPECT_FALSE(vec1 > vec2);\n  EXPECT_TRUE(vec1 >= vec2);\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n  EXPECT_EQ(vec1 <=> vec2, std::strong_ordering::equal);\n#endif\n  vec1.pop_back();\n  EXPECT_NE(vec1, vec2);\n  EXPECT_TRUE(vec1 < vec2);\n  EXPECT_TRUE(vec1 <= vec2);\n  EXPECT_FALSE(vec1 > vec2);\n  EXPECT_FALSE(vec1 >= vec2);\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n  EXPECT_EQ(vec1 <=> vec2, std::strong_ordering::less);\n  EXPECT_EQ(vec2 <=> vec1, std::strong_ordering::greater);\n#endif\n}\n\nstruct NontrivialImmovable {\n  NontrivialImmovable() {}\n  NontrivialImmovable(const NontrivialImmovable&) = default;\n  NontrivialImmovable(NontrivialImmovable&&) = delete;\n  ~NontrivialImmovable() = default;\n\n  // Make it non trivial to copy\n  NontrivialImmovable& operator=(const NontrivialImmovable&) { return *this; }\n};\n\nstatic_assert(!std::is_trivially_copyable_v<NontrivialImmovable>);\n\nstruct TrivialImmovable {\n  TrivialImmovable() {}\n  TrivialImmovable(const TrivialImmovable&) = default;\n  TrivialImmovable(TrivialImmovable&&) = delete;\n};\n\nstatic_assert(std::is_trivially_copyable_v<TrivialImmovable>);\n\nstruct TrivialNonCopyableNorMovable {\n  TrivialNonCopyableNorMovable() {}\n  TrivialNonCopyableNorMovable(const TrivialNonCopyableNorMovable&) = delete;\n  TrivialNonCopyableNorMovable(TrivialNonCopyableNorMovable&&) = delete;\n};\n\nstatic_assert(std::is_trivially_copyable_v<TrivialNonCopyableNorMovable>);\n\nTEST(smallVector, ImmovableTypes) {\n  // Immovable types can be used to create small_vectors as long as no use to\n  // resizing operations is present. We need to make sure that creation of the\n  // small_vector with the sized constructor works whether or not they are\n  // trivially copyable.\n  {\n    folly::small_vector<NontrivialImmovable> sv{10};\n  }\n  {\n    folly::small_vector<TrivialImmovable> sv{10};\n  }\n  {\n    folly::small_vector<TrivialNonCopyableNorMovable> sv{10};\n  }\n  SUCCEED();\n}\n"
  },
  {
    "path": "folly/container/test/sorted_vector_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <functional>\n#include <iterator>\n#include <list>\n#include <map>\n#include <memory>\n#include <set>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/Utility.h>\n#include <folly/memory/Malloc.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/sorted_vector_types.h>\n\nusing folly::sorted_vector_map;\nusing folly::sorted_vector_set;\n\nnamespace {\n\nstatic_assert(\n    folly::is_sorted_vector_map_v<folly::sorted_vector_map<int, double>>);\nstatic_assert(\n    folly::is_sorted_vector_map_v<folly::sorted_vector_map<\n        int,\n        double,\n        /* Compare */ std::greater<int>,\n        /* Allocator */ std::allocator<std::pair<int, double>>,\n        /* GrowthPolicy */ void,\n        /* Container */ folly::small_vector<std::pair<int, double>, 10>>>);\nstatic_assert(!folly::is_sorted_vector_map_v<std::map<int, double>>);\nstatic_assert(!folly::is_sorted_vector_map_v<std::unordered_map<int, double>>);\nstatic_assert(\n    !folly::is_sorted_vector_map_v<std::vector<std::pair<int, double>>>);\n\nstatic_assert(folly::is_sorted_vector_set_v<folly::sorted_vector_set<int>>);\nstatic_assert(\n    folly::is_sorted_vector_set_v<folly::sorted_vector_set<\n        int,\n        /* Compare */ std::greater<int>,\n        /* Allocator */ std::allocator<int>,\n        /* GrowthPolicy */ void,\n        /* Container */ folly::small_vector<int, 20>>>);\nstatic_assert(!folly::is_sorted_vector_set_v<std::set<int>>);\nstatic_assert(!folly::is_sorted_vector_set_v<std::unordered_set<int>>);\nstatic_assert(!folly::is_sorted_vector_set_v<std::vector<int>>);\n\nstatic_assert(\n    std::is_same_v<folly::sorted_vector_set<int>::const_pointer, const int*>);\n\nstatic_assert(std::is_same_v<\n              folly::sorted_vector_map<int, double>::pointer,\n              std::pair<int, double>*>);\nstatic_assert(std::is_same_v<\n              folly::sorted_vector_map<int, double>::const_pointer,\n              const std::pair<int, double>*>);\n\nstatic_assert(folly::is_small_sorted_vector_map_v<\n              folly::small_sorted_vector_map<int, double, 5>>);\nstatic_assert(folly::is_small_sorted_vector_set_v<\n              folly::small_sorted_vector_set<int, 5>>);\n\ntemplate <class T>\nstruct less_invert {\n  bool operator()(const T& a, const T& b) const { return b < a; }\n};\n\ntemplate <class Container>\nvoid check_invariant(Container& c) {\n  auto it = c.begin();\n  auto end = c.end();\n  if (it == end) {\n    return;\n  }\n  auto prev = it;\n  ++it;\n  for (; it != end; ++it, ++prev) {\n    EXPECT_TRUE(c.value_comp()(*prev, *it));\n  }\n}\n\nstruct OneAtATimePolicy {\n  template <class Container>\n  void increase_capacity(Container& c) {\n    if (c.size() == c.capacity()) {\n      c.reserve(c.size() + 1);\n    }\n  }\n};\n\nstruct CountCopyCtor {\n  explicit CountCopyCtor() : val_(0), count_(0) {}\n\n  explicit CountCopyCtor(int val) : val_(val), count_(0) {}\n\n  CountCopyCtor(const CountCopyCtor& c) noexcept\n      : val_(c.val_), count_(c.count_ + 1) {\n    ++gCount_;\n  }\n\n  CountCopyCtor& operator=(const CountCopyCtor&) = default;\n\n  bool operator<(const CountCopyCtor& o) const { return val_ < o.val_; }\n\n  int val_;\n  int count_;\n  static int gCount_;\n};\n\nint CountCopyCtor::gCount_ = 0;\n\nstruct KeyCopiedException : public std::exception {};\n/**\n * Key that may throw on copy when throwOnCopy is set, but never on move.\n * Use clone() to copy without throwing.\n */\nstruct KeyThatThrowsOnCopies {\n  int32_t key{};\n  bool throwOnCopy{};\n\n  [[maybe_unused]] KeyThatThrowsOnCopies() {}\n\n  /* implicit */ KeyThatThrowsOnCopies(int32_t key) noexcept\n      : key(key), throwOnCopy(false) {}\n  KeyThatThrowsOnCopies(int32_t key, bool throwOnCopy) noexcept\n      : key(key), throwOnCopy(throwOnCopy) {}\n\n  ~KeyThatThrowsOnCopies() noexcept {}\n\n  KeyThatThrowsOnCopies(KeyThatThrowsOnCopies const& other)\n      : key(other.key), throwOnCopy(other.throwOnCopy) {\n    if (throwOnCopy) {\n      throw KeyCopiedException{};\n    }\n  }\n\n  KeyThatThrowsOnCopies(KeyThatThrowsOnCopies&& other) noexcept = default;\n\n  KeyThatThrowsOnCopies& operator=(KeyThatThrowsOnCopies const& other) {\n    key = other.key;\n    throwOnCopy = other.throwOnCopy;\n    if (throwOnCopy) {\n      throw KeyCopiedException{};\n    }\n    return *this;\n  }\n\n  KeyThatThrowsOnCopies& operator=(KeyThatThrowsOnCopies&& other) noexcept =\n      default;\n\n  bool operator<(const KeyThatThrowsOnCopies& other) const {\n    return key < other.key;\n  }\n};\n\nstatic_assert(\n    std::is_nothrow_move_constructible<KeyThatThrowsOnCopies>::value &&\n        std::is_nothrow_move_assignable<KeyThatThrowsOnCopies>::value,\n    \"non-noexcept move-constructible or move-assignable\");\n\n} // namespace\n\nTEST(SortedVectorTypes, SetAssignmentInitListTest) {\n  sorted_vector_set<int> s{3, 4, 5};\n  EXPECT_THAT(s, testing::ElementsAreArray({3, 4, 5}));\n  s = {}; // empty ilist assignment\n  EXPECT_THAT(s, testing::IsEmpty());\n  s = {7, 8, 9}; // non-empty ilist assignment\n  EXPECT_THAT(s, testing::ElementsAreArray({7, 8, 9}));\n}\n\nTEST(SortedVectorTypes, SetUnderlyingContainerDirectFillInWithGuardTest) {\n  sorted_vector_set<int> s;\n  {\n    auto guard = s.get_container_for_direct_mutation();\n    guard.get() = {5, 4, 3};\n  }\n  EXPECT_THAT(s, testing::ElementsAreArray({3, 4, 5}));\n  s = {}; // empty ilist assignment\n  EXPECT_THAT(s, testing::IsEmpty());\n  s = {7, 8, 9}; // non-empty ilist assignment\n  EXPECT_THAT(s, testing::ElementsAreArray({7, 8, 9}));\n}\n\nTEST(\n    SortedVectorTypes,\n    SetUnderlyingContainerDirectFillInSortedUniqueWithGuardTest) {\n  sorted_vector_set<int> s;\n  {\n    auto guard = s.get_container_for_direct_mutation(folly::sorted_unique);\n    guard.get() = {3, 4, 5};\n  }\n  EXPECT_THAT(s, testing::ElementsAreArray({3, 4, 5}));\n  s = {}; // empty ilist assignment\n  EXPECT_THAT(s, testing::IsEmpty());\n  s = {7, 8, 9}; // non-empty ilist assignment\n  EXPECT_THAT(s, testing::ElementsAreArray({7, 8, 9}));\n}\n\nTEST(SortedVectorTypes, MapAssignmentInitListTest) {\n  using v = std::pair<int, const char*>;\n  v p = {3, \"a\"}, q = {4, \"b\"}, r = {5, \"c\"};\n  sorted_vector_map<int, const char*> m{p, q, r};\n  EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n  m = {}; // empty ilist assignment\n  EXPECT_THAT(m, testing::IsEmpty());\n  m = {p, q, r}; // non-empty ilist assignment\n  EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n}\n\nTEST(SortedVectorTypes, MapUnderlyingContainerDirectFillInWithGuardTest) {\n  using v = std::pair<int, const char*>;\n  v p = {3, \"a\"}, q = {4, \"b\"}, r = {5, \"c\"};\n  sorted_vector_map<int, const char*> m;\n  {\n    auto guard = m.get_container_for_direct_mutation();\n    guard.get() = {r, q, p};\n  }\n  EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n  m = {}; // empty ilist assignment\n  EXPECT_THAT(m, testing::IsEmpty());\n  m = {p, q, r}; // non-empty ilist assignment\n  EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n}\n\nTEST(\n    SortedVectorTypes,\n    MapUnderlyingContainerDirectFillInSortedUniqueWithGuardTest) {\n  using v = std::pair<int, const char*>;\n  v p = {3, \"a\"}, q = {4, \"b\"}, r = {5, \"c\"};\n  sorted_vector_map<int, const char*> m;\n  {\n    auto guard = m.get_container_for_direct_mutation(folly::sorted_unique);\n    guard.get() = {p, q, r};\n  }\n  EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n  m = {}; // empty ilist assignment\n  EXPECT_THAT(m, testing::IsEmpty());\n  m = {p, q, r}; // non-empty ilist assignment\n  EXPECT_THAT(m, testing::ElementsAreArray({p, q, r}));\n}\n\nTEST(SortedVectorTypes, SimpleSetTest) {\n  sorted_vector_set<int> s;\n  EXPECT_TRUE(s.empty());\n  for (int i = 0; i < 1000; ++i) {\n    s.insert(rand() % 100000);\n  }\n  EXPECT_FALSE(s.empty());\n  check_invariant(s);\n\n  sorted_vector_set<int> s2;\n  s2.insert(s.begin(), s.end());\n  check_invariant(s2);\n  EXPECT_TRUE(s == s2);\n\n  auto it = s2.lower_bound(32);\n  if (*it == 32) {\n    s2.erase(it);\n    it = s2.lower_bound(32);\n  }\n  check_invariant(s2);\n  auto oldSz = s2.size();\n  s2.insert(it, 32);\n  EXPECT_TRUE(s2.size() == oldSz + 1);\n  check_invariant(s2);\n\n  const sorted_vector_set<int>& cs2 = s2;\n  auto range = cs2.equal_range(32);\n  auto lbound = cs2.lower_bound(32);\n  auto ubound = cs2.upper_bound(32);\n  EXPECT_TRUE(range.first == lbound);\n  EXPECT_TRUE(range.second == ubound);\n  EXPECT_TRUE(range.first != cs2.end());\n  EXPECT_TRUE(range.second != cs2.end());\n  EXPECT_TRUE(cs2.contains(32));\n  EXPECT_FALSE(cs2.find(32) == cs2.end());\n  EXPECT_TRUE(cs2.contains(32));\n\n  // Bad insert hint.\n  s2.insert(s2.begin() + 3, 33);\n  EXPECT_TRUE(s2.find(33) != s2.begin());\n  EXPECT_TRUE(s2.find(33) != s2.end());\n  check_invariant(s2);\n  s2.erase(33);\n  check_invariant(s2);\n\n  it = s2.find(32);\n  EXPECT_FALSE(it == s2.end());\n  s2.erase(it);\n  EXPECT_FALSE(cs2.contains(32));\n  EXPECT_TRUE(s2.size() == oldSz);\n  check_invariant(s2);\n\n  sorted_vector_set<int> cpy(s);\n  check_invariant(cpy);\n  EXPECT_TRUE(cpy == s);\n  sorted_vector_set<int> cpy2(s);\n  cpy2.insert(100001);\n  EXPECT_TRUE(cpy2 != cpy);\n  EXPECT_TRUE(cpy2 != s);\n  check_invariant(cpy2);\n  EXPECT_TRUE(cpy2.contains(100001));\n  s.swap(cpy2);\n  check_invariant(cpy2);\n  check_invariant(s);\n  EXPECT_TRUE(s != cpy);\n  EXPECT_TRUE(s != cpy2);\n  EXPECT_TRUE(cpy2 == cpy);\n\n  sorted_vector_set<int> s3 = {};\n  s3.insert({1, 2, 3});\n  s3.emplace(4);\n  EXPECT_EQ(s3.size(), 4);\n\n  sorted_vector_set<std::string> s4;\n  s4.emplace(\"foobar\", 3);\n  EXPECT_TRUE(s4.contains(\"foo\"));\n}\n\nTEST(SortedVectorTypes, TransparentSetTest) {\n  using namespace folly::string_piece_literals;\n  using Compare = folly::transparent<std::less<folly::StringPiece>>;\n\n  constexpr auto buddy = \"buddy\"_sp;\n  constexpr auto hello = \"hello\"_sp;\n  constexpr auto stake = \"stake\"_sp;\n  constexpr auto world = \"world\"_sp;\n  constexpr auto zebra = \"zebra\"_sp;\n\n  sorted_vector_set<std::string, Compare> const s({hello.str(), world.str()});\n\n  // find\n  EXPECT_TRUE(s.end() == s.find(buddy));\n  EXPECT_EQ(hello, *s.find(hello));\n  EXPECT_TRUE(s.end() == s.find(stake));\n  EXPECT_EQ(world, *s.find(world));\n  EXPECT_TRUE(s.end() == s.find(zebra));\n\n  // find 2 keys\n  constexpr folly::StringPiece values[] = {buddy, hello, stake, world, zebra};\n  for (auto& v0 : values) {\n    for (auto& v1 : values) {\n      auto [it0, it1] = s.find(v0, v1);\n      EXPECT_TRUE(s.find(v0) == it0);\n      EXPECT_TRUE(s.find(v1) == it1);\n    }\n  }\n\n  // count\n  EXPECT_FALSE(s.contains(buddy));\n  EXPECT_TRUE(s.contains(hello));\n  EXPECT_FALSE(s.contains(stake));\n  EXPECT_TRUE(s.contains(world));\n  EXPECT_FALSE(s.contains(zebra));\n\n  // contains\n  EXPECT_FALSE(s.contains(buddy));\n  EXPECT_TRUE(s.contains(hello));\n  EXPECT_FALSE(s.contains(stake));\n  EXPECT_TRUE(s.contains(world));\n  EXPECT_FALSE(s.contains(zebra));\n\n  // lower_bound\n  EXPECT_TRUE(s.find(hello) == s.lower_bound(buddy));\n  EXPECT_TRUE(s.find(hello) == s.lower_bound(hello));\n  EXPECT_TRUE(s.find(world) == s.lower_bound(stake));\n  EXPECT_TRUE(s.find(world) == s.lower_bound(world));\n  EXPECT_TRUE(s.end() == s.lower_bound(zebra));\n\n  // upper_bound\n  EXPECT_TRUE(s.find(hello) == s.upper_bound(buddy));\n  EXPECT_TRUE(s.find(world) == s.upper_bound(hello));\n  EXPECT_TRUE(s.find(world) == s.upper_bound(stake));\n  EXPECT_TRUE(s.end() == s.upper_bound(world));\n  EXPECT_TRUE(s.end() == s.upper_bound(zebra));\n\n  // equal_range\n  for (auto value : {buddy, hello, stake, world, zebra}) {\n    EXPECT_TRUE(\n        std::make_pair(s.lower_bound(value), s.upper_bound(value)) ==\n        s.equal_range(value))\n        << value;\n  }\n}\n\nTEST(SortedVectorTypes, BadHints) {\n  for (int toInsert = -1; toInsert <= 7; ++toInsert) {\n    for (int hintPos = 0; hintPos <= 4; ++hintPos) {\n      sorted_vector_set<int> s;\n      for (int i = 0; i <= 3; ++i) {\n        s.insert(i * 2);\n      }\n      s.insert(s.begin() + hintPos, toInsert);\n      size_t expectedSize = (toInsert % 2) == 0 ? 4 : 5;\n      EXPECT_EQ(s.size(), expectedSize);\n      check_invariant(s);\n    }\n  }\n}\n\nTEST(SortedVectorTypes, SimpleMapTest) {\n  sorted_vector_map<int, float> m;\n  for (int i = 0; i < 1000; ++i) {\n    m[i] = i / 1000.0;\n  }\n  check_invariant(m);\n\n  m[32] = 100.0;\n  check_invariant(m);\n  EXPECT_TRUE(m.contains(32));\n  EXPECT_DOUBLE_EQ(100.0, m.at(32));\n  EXPECT_FALSE(m.find(32) == m.end());\n  EXPECT_TRUE(m.contains(32));\n  m.erase(32);\n  EXPECT_TRUE(m.find(32) == m.end());\n  EXPECT_FALSE(m.contains(32));\n  check_invariant(m);\n  EXPECT_THROW(m.at(32), std::out_of_range);\n\n  sorted_vector_map<int, float> m2 = m;\n  EXPECT_TRUE(m2 == m);\n  EXPECT_FALSE(m2 != m);\n  auto it = m2.lower_bound(1 << 20);\n  EXPECT_TRUE(it == m2.end());\n  m2.insert(it, std::make_pair(1 << 20, 10.0f));\n  check_invariant(m2);\n  EXPECT_TRUE(m2.contains(1 << 20));\n  EXPECT_TRUE(m < m2);\n  EXPECT_TRUE(m <= m2);\n\n  const sorted_vector_map<int, float>& cm = m;\n  auto range = cm.equal_range(42);\n  auto lbound = cm.lower_bound(42);\n  auto ubound = cm.upper_bound(42);\n  EXPECT_TRUE(range.first == lbound);\n  EXPECT_TRUE(range.second == ubound);\n  EXPECT_FALSE(range.first == cm.end());\n  EXPECT_FALSE(range.second == cm.end());\n  m.erase(m.lower_bound(42));\n  check_invariant(m);\n\n  sorted_vector_map<int, float> m3;\n  m3.insert(m2.begin(), m2.end());\n  check_invariant(m3);\n  EXPECT_TRUE(m3 == m2);\n  EXPECT_FALSE(m3 == m);\n\n  sorted_vector_map<int, float> m4;\n  m4.emplace(1, 2.0f);\n  m4.emplace(3, 1.0f);\n  m4.emplace(2, 1.5f);\n  check_invariant(m4);\n  EXPECT_TRUE(m4.size() == 3);\n\n  sorted_vector_map<int, float> m5;\n  for (auto& kv : m2) {\n    m5.emplace(kv);\n  }\n  check_invariant(m5);\n  EXPECT_TRUE(m5 == m2);\n  EXPECT_FALSE(m5 == m);\n\n  EXPECT_TRUE(m != m2);\n  EXPECT_TRUE(m2 == m3);\n  EXPECT_TRUE(m3 != m);\n  m.swap(m3);\n  check_invariant(m);\n  check_invariant(m2);\n  check_invariant(m3);\n  EXPECT_TRUE(m3 != m2);\n  EXPECT_TRUE(m3 != m);\n  EXPECT_TRUE(m == m2);\n\n  // Bad insert hint.\n  m.insert(m.begin() + 3, std::make_pair(1 << 15, 1.0f));\n  check_invariant(m);\n\n  sorted_vector_map<int, float> m6 = {};\n  m6.insert({{1, 1.0f}, {2, 2.0f}, {1, 2.0f}});\n  EXPECT_EQ(m6.at(2), 2.0f);\n}\n\nTEST(SortedVectorTypes, TransparentMapTest) {\n  using namespace folly::string_piece_literals;\n  using Compare = folly::transparent<std::less<folly::StringPiece>>;\n\n  constexpr auto buddy = \"buddy\"_sp;\n  constexpr auto hello = \"hello\"_sp;\n  constexpr auto stake = \"stake\"_sp;\n  constexpr auto world = \"world\"_sp;\n  constexpr auto zebra = \"zebra\"_sp;\n\n  sorted_vector_map<std::string, float, Compare> const m(\n      {{hello.str(), -1.}, {world.str(), +1.}});\n\n  // find\n  EXPECT_TRUE(m.end() == m.find(buddy));\n  EXPECT_EQ(hello, m.find(hello)->first);\n  EXPECT_TRUE(m.end() == m.find(stake));\n  EXPECT_EQ(world, m.find(world)->first);\n  EXPECT_TRUE(m.end() == m.find(zebra));\n\n  // count\n  EXPECT_FALSE(m.contains(buddy));\n  EXPECT_TRUE(m.contains(hello));\n  EXPECT_FALSE(m.contains(stake));\n  EXPECT_TRUE(m.contains(world));\n  EXPECT_FALSE(m.contains(zebra));\n\n  // lower_bound\n  EXPECT_TRUE(m.find(hello) == m.lower_bound(buddy));\n  EXPECT_TRUE(m.find(hello) == m.lower_bound(hello));\n  EXPECT_TRUE(m.find(world) == m.lower_bound(stake));\n  EXPECT_TRUE(m.find(world) == m.lower_bound(world));\n  EXPECT_TRUE(m.end() == m.lower_bound(zebra));\n\n  // upper_bound\n  EXPECT_TRUE(m.find(hello) == m.upper_bound(buddy));\n  EXPECT_TRUE(m.find(world) == m.upper_bound(hello));\n  EXPECT_TRUE(m.find(world) == m.upper_bound(stake));\n  EXPECT_TRUE(m.end() == m.upper_bound(world));\n  EXPECT_TRUE(m.end() == m.upper_bound(zebra));\n\n  // equal_range\n  for (auto value : {buddy, hello, stake, world, zebra}) {\n    EXPECT_TRUE(\n        std::make_pair(m.lower_bound(value), m.upper_bound(value)) ==\n        m.equal_range(value))\n        << value;\n  }\n}\n\nTEST(SortedVectorTypes, Find2) {\n  size_t sizes[] = {0, 1, 2, 31, 32, 33};\n  for (auto size : sizes) {\n    sorted_vector_set<int> s;\n    for (size_t i = 0; i < size; i++) {\n      s.insert(2 * i + 1);\n    }\n    // test find2 with every possible combination of pair of keys\n    for (size_t i = 0; i < 2 * size + 2; i++) {\n      for (size_t j = 0; j < 2 * size + 2; j++) {\n        auto expected0 = s.find(i);\n        auto expected1 = s.find(j);\n        auto [it0, it1] = s.find(i, j);\n        EXPECT_EQ(it0, expected0);\n        EXPECT_EQ(it1, expected1);\n      }\n    }\n  }\n\n  for (auto size : sizes) {\n    sorted_vector_map<int, int> m;\n    for (size_t i = 0; i < size; i++) {\n      m.emplace(2 * i + 1, i);\n    }\n    // test find2 with every possible combination of pair of keys\n    for (size_t i = 0; i < 2 * size + 2; i++) {\n      for (size_t j = 0; j < 2 * size + 2; j++) {\n        auto expected0 = m.find(i);\n        auto expected1 = m.find(j);\n        auto [it0, it1] = m.find(i, j);\n        EXPECT_EQ(it0, expected0);\n        EXPECT_EQ(it1, expected1);\n      }\n    }\n  }\n}\n\nTEST(SortedVectorTypes, Sizes) {\n  EXPECT_EQ(sizeof(sorted_vector_set<int>), sizeof(std::vector<int>));\n  EXPECT_EQ(\n      sizeof(sorted_vector_map<int, int>),\n      sizeof(std::vector<std::pair<int, int>>));\n\n  typedef sorted_vector_set<\n      int,\n      std::less<int>,\n      std::allocator<int>,\n      OneAtATimePolicy>\n      SetT;\n  typedef sorted_vector_map<\n      int,\n      int,\n      std::less<int>,\n      std::allocator<std::pair<int, int>>,\n      OneAtATimePolicy>\n      MapT;\n\n  EXPECT_EQ(sizeof(SetT), sizeof(std::vector<int>));\n  EXPECT_EQ(sizeof(MapT), sizeof(std::vector<std::pair<int, int>>));\n}\n\nTEST(SortedVectorTypes, InitializerLists) {\n  sorted_vector_set<int> empty_initialized_set{};\n  EXPECT_TRUE(empty_initialized_set.empty());\n\n  sorted_vector_set<int> singleton_initialized_set{1};\n  EXPECT_EQ(1, singleton_initialized_set.size());\n  EXPECT_EQ(1, *singleton_initialized_set.begin());\n\n  sorted_vector_set<int> forward_initialized_set{1, 2};\n  sorted_vector_set<int> backward_initialized_set{2, 1};\n  EXPECT_EQ(2, forward_initialized_set.size());\n  EXPECT_EQ(1, *forward_initialized_set.begin());\n  EXPECT_EQ(2, *forward_initialized_set.rbegin());\n  EXPECT_TRUE(forward_initialized_set == backward_initialized_set);\n\n  sorted_vector_map<int, int> empty_initialized_map{};\n  EXPECT_TRUE(empty_initialized_map.empty());\n\n  sorted_vector_map<int, int> singleton_initialized_map{{1, 10}};\n  EXPECT_EQ(1, singleton_initialized_map.size());\n  EXPECT_EQ(10, singleton_initialized_map[1]);\n\n  sorted_vector_map<int, int> forward_initialized_map{{1, 10}, {2, 20}};\n  sorted_vector_map<int, int> backward_initialized_map{{2, 20}, {1, 10}};\n  EXPECT_EQ(2, forward_initialized_map.size());\n  EXPECT_EQ(10, forward_initialized_map[1]);\n  EXPECT_EQ(20, forward_initialized_map[2]);\n  EXPECT_TRUE(forward_initialized_map == backward_initialized_map);\n}\n\nTEST(SortedVectorTypes, CustomCompare) {\n  sorted_vector_set<int, less_invert<int>> s;\n  for (int i = 0; i < 200; ++i) {\n    s.insert(i);\n  }\n  check_invariant(s);\n\n  sorted_vector_map<int, float, less_invert<int>> m;\n  for (int i = 0; i < 200; ++i) {\n    m[i] = 12.0;\n  }\n  check_invariant(m);\n}\n\nTEST(SortedVectorTypes, GrowthPolicy) {\n  typedef sorted_vector_set<\n      CountCopyCtor,\n      std::less<CountCopyCtor>,\n      std::allocator<CountCopyCtor>,\n      OneAtATimePolicy>\n      SetT;\n\n  SetT a;\n  for (int i = 0; i < 20; ++i) {\n    a.insert(CountCopyCtor(i));\n  }\n  check_invariant(a);\n  SetT::iterator it = a.begin();\n  EXPECT_FALSE(it == a.end());\n  if (it != a.end()) {\n    EXPECT_EQ(it->val_, 0);\n    // 1 copy for the initial insertion, 19 more for reallocs on the\n    // additional insertions.\n    EXPECT_EQ(it->count_, 20);\n  }\n\n  std::list<CountCopyCtor> v;\n  for (int i = 0; i < 20; ++i) {\n    v.emplace_back(20 + i);\n  }\n  a.insert(v.begin(), v.end());\n  check_invariant(a);\n\n  it = a.begin();\n  EXPECT_FALSE(it == a.end());\n  if (it != a.end()) {\n    EXPECT_EQ(it->val_, 0);\n    // Should be only 1 more copy for inserting this above range.\n    EXPECT_EQ(it->count_, 21);\n  }\n}\n\nTEST(SortedVectorTest, EmptyTest) {\n  sorted_vector_set<int> emptySet;\n  EXPECT_TRUE(emptySet.lower_bound(10) == emptySet.end());\n  EXPECT_TRUE(emptySet.find(10) == emptySet.end());\n\n  sorted_vector_map<int, int> emptyMap;\n  EXPECT_TRUE(emptyMap.lower_bound(10) == emptyMap.end());\n  EXPECT_TRUE(emptyMap.find(10) == emptyMap.end());\n  EXPECT_THROW(emptyMap.at(10), std::out_of_range);\n}\n\nTEST(SortedVectorTest, MoveTest) {\n  sorted_vector_set<std::unique_ptr<int>> s;\n  s.insert(std::make_unique<int>(5));\n  s.insert(s.end(), std::make_unique<int>(10));\n  EXPECT_EQ(s.size(), 2);\n\n  for (const auto& p : s) {\n    EXPECT_TRUE(*p == 5 || *p == 10);\n  }\n\n  sorted_vector_map<int, std::unique_ptr<int>> m;\n  m.insert(std::make_pair(5, std::make_unique<int>(5)));\n  m.insert(m.end(), std::make_pair(10, std::make_unique<int>(10)));\n\n  EXPECT_EQ(*m[5], 5);\n  EXPECT_EQ(*m[10], 10);\n}\n\nTEST(SortedVectorTest, ShrinkTest) {\n  sorted_vector_set<int> s;\n  int i = 0;\n  // Hopefully your resize policy doubles when capacity is full, or this will\n  // hang forever :(\n  while (s.capacity() == s.size()) {\n    s.insert(i++);\n  }\n  s.shrink_to_fit();\n  // The standard does not actually enforce that this be true, but assume that\n  // vector::shrink_to_fit respects the caller.\n  EXPECT_EQ(s.capacity(), s.size());\n}\n\nTEST(SortedVectorTypes, EraseTest) {\n  sorted_vector_set<int> s1;\n  s1.insert(1);\n  sorted_vector_set<int> s2(s1);\n  EXPECT_EQ(0, s1.erase(0));\n  EXPECT_EQ(s2, s1);\n}\n\nTEST(SortedVectorTypes, EraseTest2) {\n  sorted_vector_set<int> s;\n  for (int i = 0; i < 1000; ++i) {\n    s.insert(i);\n  }\n\n  auto it = s.lower_bound(32);\n  EXPECT_EQ(*it, 32);\n  it = s.erase(it);\n  EXPECT_NE(s.end(), it);\n  EXPECT_EQ(*it, 33);\n  it = s.erase(it, it + 5);\n  EXPECT_EQ(*it, 38);\n\n  it = s.begin();\n  while (it != s.end()) {\n    if (*it >= 5) {\n      it = s.erase(it);\n    } else {\n      it++;\n    }\n  }\n  EXPECT_EQ(it, s.end());\n  EXPECT_EQ(s.size(), 5);\n\n  sorted_vector_map<int, int> m;\n  for (int i = 0; i < 1000; ++i) {\n    m.insert(std::make_pair(i, i));\n  }\n\n  auto it2 = m.lower_bound(32);\n  EXPECT_EQ(it2->first, 32);\n  it2 = m.erase(it2);\n  EXPECT_NE(m.end(), it2);\n  EXPECT_EQ(it2->first, 33);\n  it2 = m.erase(it2, it2 + 5);\n  EXPECT_EQ(it2->first, 38);\n\n  it2 = m.begin();\n  while (it2 != m.end()) {\n    if (it2->first >= 5) {\n      it2 = m.erase(it2);\n    } else {\n      it2++;\n    }\n  }\n  EXPECT_EQ(it2, m.end());\n  EXPECT_EQ(m.size(), 5);\n}\n\nTEST(SortedVectorTypes, EraseIfTest) {\n  sorted_vector_set<int> s1{1, 2, 3, 4, 5, 6, 7, 8, 9};\n  EXPECT_EQ(erase_if(s1, [](int i) { return i % 3 == 0; }), 3);\n  EXPECT_EQ(s1, sorted_vector_set<int>({1, 2, 4, 5, 7, 8}));\n\n  sorted_vector_map<int, int> m1{{1, 10}, {2, 20}, {3, 30}, {4, 44}};\n  EXPECT_EQ(\n      erase_if(m1, [](const auto& kv) { return kv.second == 10 * kv.first; }),\n      3);\n  EXPECT_EQ(m1.size(), 1);\n  EXPECT_EQ(*m1.begin(), std::make_pair(4, 44));\n}\n\nTEST(SortedVectorTypes, TestSetBulkInsertionSortMerge) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  sorted_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will have to be merged in.\n  s = std::vector<int>({10, 7, 5, 1});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n\n  EXPECT_THAT(vset, testing::ElementsAreArray({1, 2, 4, 5, 6, 7, 8, 10}));\n}\n\nTEST(SortedVectorTypes, TestSetBulkInsertionMiddleValuesEqualDuplication) {\n  auto s = std::vector<int>({4, 6, 8});\n\n  sorted_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  s = std::vector<int>({8, 10, 12});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n\n  EXPECT_THAT(vset, testing::ElementsAreArray({4, 6, 8, 10, 12}));\n}\n\nTEST(SortedVectorTypes, TestSetBulkInsertionSortMergeDups) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  sorted_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will have to be merged in.\n  s = std::vector<int>({10, 6, 5, 2});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 5, 6, 8, 10}));\n}\n\nTEST(SortedVectorTypes, TestSetInsertionDupsOneByOne) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  sorted_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will have to be merged in.\n  s = std::vector<int>({10, 6, 5, 2});\n\n  for (const auto& elem : s) {\n    vset.insert(elem);\n  }\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 5, 6, 8, 10}));\n}\n\nTEST(SortedVectorTypes, TestSetBulkInsertionSortNoMerge) {\n  auto s = std::vector<int>({6, 4, 8, 2});\n\n  sorted_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  // Add an unsorted range that will not have to be merged in.\n  s = std::vector<int>({20, 15, 16, 13});\n\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 6, 8, 13, 15, 16, 20}));\n}\n\nTEST(SortedVectorTypes, TestSetBulkInsertionNoSortMerge) {\n  auto test = [](bool withSortedUnique) {\n    auto s = std::vector<int>({6, 4, 8, 2});\n\n    sorted_vector_set<int> vset(s.begin(), s.end());\n    check_invariant(vset);\n\n    // Add a sorted range that will have to be merged in.\n    s = std::vector<int>({1, 2, 3, 5, 9});\n\n    if (withSortedUnique) {\n      vset.insert(s.begin(), s.end());\n    } else {\n      vset.insert(folly::sorted_unique, s.begin(), s.end());\n    }\n    check_invariant(vset);\n    EXPECT_THAT(vset, testing::ElementsAreArray({1, 2, 3, 4, 5, 6, 8, 9}));\n  };\n\n  test(false);\n  test(true);\n}\n\nTEST(SortedVectorTypes, TestSetBulkInsertionNoSortNoMerge) {\n  auto test = [](bool withSortedUnique) {\n    auto s = std::vector<int>({6, 4, 8, 2});\n\n    sorted_vector_set<int> vset(s.begin(), s.end());\n    check_invariant(vset);\n\n    // Add a sorted range that will not have to be merged in.\n    s = std::vector<int>({21, 22, 23, 24});\n\n    if (withSortedUnique) {\n      vset.insert(s.begin(), s.end());\n    } else {\n      vset.insert(folly::sorted_unique, s.begin(), s.end());\n    }\n    check_invariant(vset);\n    EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 6, 8, 21, 22, 23, 24}));\n  };\n\n  test(false);\n  test(true);\n}\n\nTEST(SortedVectorTypes, TestSetBulkInsertionEmptyRange) {\n  std::vector<int> s;\n  EXPECT_TRUE(s.empty());\n\n  // insertion of empty range into empty container.\n  sorted_vector_set<int> vset(s.begin(), s.end());\n  check_invariant(vset);\n\n  s = std::vector<int>({6, 4, 8, 2});\n\n  vset.insert(s.begin(), s.end());\n\n  // insertion of empty range into non-empty container.\n  s.clear();\n  vset.insert(s.begin(), s.end());\n  check_invariant(vset);\n\n  EXPECT_THAT(vset, testing::ElementsAreArray({2, 4, 6, 8}));\n}\n\n// This is a test of compilation - the behavior has already been tested\n// extensively above.\nTEST(SortedVectorTypes, TestBulkInsertionUncopyableTypes) {\n  std::vector<std::pair<int, std::unique_ptr<int>>> s;\n  s.emplace_back(1, std::make_unique<int>(0));\n\n  sorted_vector_map<int, std::unique_ptr<int>> vmap(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n\n  s.clear();\n  s.emplace_back(3, std::make_unique<int>(0));\n  vmap.insert(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n}\n\n// A moveable and copyable struct, which we use to make sure that no copy\n// operations are performed during bulk insertion if moving is an option.\nstruct Movable {\n  int x_;\n  explicit Movable(int x) : x_(x) {}\n  Movable(const Movable&) { ADD_FAILURE() << \"Copy ctor should not be called\"; }\n  Movable& operator=(const Movable&) {\n    ADD_FAILURE() << \"Copy assignment should not be called\";\n    return *this;\n  }\n\n  Movable(Movable&&) = default;\n  Movable& operator=(Movable&&) = default;\n};\n\nTEST(SortedVectorTypes, TestBulkInsertionMovableTypes) {\n  std::vector<std::pair<int, Movable>> s;\n  s.emplace_back(3, Movable(2));\n  s.emplace_back(1, Movable(0));\n\n  sorted_vector_map<int, Movable> vmap(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n\n  s.clear();\n  s.emplace_back(4, Movable(3));\n  s.emplace_back(2, Movable(1));\n  vmap.insert(\n      std::make_move_iterator(s.begin()), std::make_move_iterator(s.end()));\n}\n\nTEST(SortedVectorTypes, TestSetCreationFromVector) {\n  std::vector<int> vec = {3, 1, -1, 5, 0};\n  sorted_vector_set<int> vset(std::move(vec));\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({-1, 0, 1, 3, 5}));\n}\n\nTEST(SortedVectorTypes, TestMapCreationFromVector) {\n  std::vector<std::pair<int, int>> vec = {\n      {3, 1}, {1, 5}, {-1, 2}, {5, 3}, {0, 3}};\n  sorted_vector_map<int, int> vmap(std::move(vec));\n  check_invariant(vmap);\n  auto contents = std::vector<std::pair<int, int>>(vmap.begin(), vmap.end());\n  auto expected_contents = std::vector<std::pair<int, int>>({\n      {-1, 2},\n      {0, 3},\n      {1, 5},\n      {3, 1},\n      {5, 3},\n  });\n  EXPECT_EQ(contents, expected_contents);\n}\n\nTEST(SortedVectorTypes, TestSetCreationFromSmallVector) {\n  using ssvs = folly::small_sorted_vector_set<int, 5>;\n  ssvs::container_type vec = {3, 1, -1, 5, 0};\n  ssvs vset(std::move(vec));\n  check_invariant(vset);\n  EXPECT_THAT(vset, testing::ElementsAreArray({-1, 0, 1, 3, 5}));\n}\n\nTEST(SortedVectorTypes, TestMapCreationFromSmallVector) {\n  using ssvm = folly::small_sorted_vector_map<int, int, 5>;\n  ssvm::container_type vec = {{3, 1}, {1, 5}, {-1, 2}, {5, 3}, {0, 3}};\n  ssvm vmap(std::move(vec));\n  check_invariant(vmap);\n  auto contents = std::vector<std::pair<int, int>>(vmap.begin(), vmap.end());\n  auto expected_contents = std::vector<std::pair<int, int>>({\n      {-1, 2},\n      {0, 3},\n      {1, 5},\n      {3, 1},\n      {5, 3},\n  });\n  EXPECT_EQ(contents, expected_contents);\n}\n\nTEST(SortedVectorTypes, TestBulkInsertionWithDuplicatesIntoEmptySet) {\n  sorted_vector_set<int> set;\n  {\n    std::vector<int> const vec = {0, 1, 0, 1};\n    set.insert(vec.begin(), vec.end());\n  }\n  EXPECT_THAT(set, testing::ElementsAreArray({0, 1}));\n}\n\nTEST(SortedVectorTypes, TestDataPointsToFirstElement) {\n  sorted_vector_set<int> set;\n  sorted_vector_map<int, int> map;\n\n  set.insert(0);\n  map[0] = 0;\n  EXPECT_EQ(set.data(), &*set.begin());\n  EXPECT_EQ(map.data(), &*map.begin());\n\n  set.insert(1);\n  map[1] = 1;\n  EXPECT_EQ(set.data(), &*set.begin());\n  EXPECT_EQ(map.data(), &*map.begin());\n}\n\nTEST(SortedVectorTypes, TestEmplaceHint) {\n  sorted_vector_set<std::pair<int, int>> set;\n  sorted_vector_map<int, int> map;\n\n  for (size_t i = 0; i < 4; ++i) {\n    const std::pair<int, int> k00(0, 0);\n    const std::pair<int, int> k0i(0, i % 2);\n    const std::pair<int, int> k10(1, 0);\n    const std::pair<int, int> k1i(1, i % 2);\n    const std::pair<int, int> k20(2, 0);\n    const std::pair<int, int> k2i(2, i % 2);\n\n    EXPECT_EQ(*set.emplace_hint(set.begin(), 0, i % 2), k0i);\n    EXPECT_EQ(*map.emplace_hint(map.begin(), 0, i % 2), k00);\n\n    EXPECT_EQ(*set.emplace_hint(set.begin(), k1i), k1i);\n    EXPECT_EQ(*map.emplace_hint(map.begin(), k1i), k10);\n\n    EXPECT_EQ(*set.emplace_hint(set.begin(), folly::copy(k2i)), k2i);\n    EXPECT_EQ(*map.emplace_hint(map.begin(), folly::copy(k2i)), k20);\n\n    check_invariant(set);\n    check_invariant(map);\n  }\n}\n\nTEST(SortedVectorTypes, TestExceptionSafety) {\n  std::initializer_list<KeyThatThrowsOnCopies> const sortedUnique = {\n      0, 1, 4, 7, 9, 11, 15};\n  sorted_vector_set<KeyThatThrowsOnCopies> set = {sortedUnique};\n  EXPECT_EQ(set.size(), 7);\n\n  // Verify that we successfully insert when no exceptions are thrown.\n  KeyThatThrowsOnCopies key1(96, false);\n  auto hint1 = set.find(96);\n  set.insert(hint1, key1);\n  EXPECT_EQ(set.size(), 8);\n\n  // Verify that we don't add a key at the end if copying throws\n  KeyThatThrowsOnCopies key2(99, true);\n  auto hint2 = set.find(99);\n  try {\n    set.insert(hint2, key2);\n  } catch (const KeyCopiedException&) {\n    // swallow\n  }\n  EXPECT_EQ(set.size(), 8);\n\n  // Verify that we don't add a key in the middle if copying throws\n  KeyThatThrowsOnCopies key3(47, true);\n  auto hint3 = set.find(47);\n  try {\n    set.insert(hint3, key3);\n  } catch (const KeyCopiedException&) {\n    // swallow\n  }\n  EXPECT_EQ(set.size(), 8);\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\n\nusing std::pmr::memory_resource;\nusing std::pmr::new_delete_resource;\nusing std::pmr::null_memory_resource;\nusing std::pmr::polymorphic_allocator;\n\nnamespace {\n\nstruct test_resource : public memory_resource {\n  void* do_allocate(size_t bytes, size_t /* alignment */) override {\n    return folly::checkedMalloc(bytes);\n  }\n\n  void do_deallocate(\n      void* p, size_t /* bytes */, size_t /* alignment */) noexcept override {\n    free(p);\n  }\n\n  bool do_is_equal(const memory_resource& other) const noexcept override {\n    return this == &other;\n  }\n};\n\n} // namespace\n\nTEST(SortedVectorTypes, TestPmrAllocatorSimple) {\n  namespace pmr = folly::pmr;\n\n  pmr::sorted_vector_set<std::pair<int, int>> s(null_memory_resource());\n  EXPECT_THROW(s.emplace(42, 42), std::bad_alloc);\n\n  pmr::sorted_vector_map<int, int> m(null_memory_resource());\n  EXPECT_THROW(m.emplace(42, 42), std::bad_alloc);\n}\n\nTEST(SortedVectorTypes, TestPmrCopyConstructSameAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r;\n  polymorphic_allocator<std::byte> a1(&r), a2(&r);\n  EXPECT_EQ(a1, a2);\n\n  {\n    pmr::sorted_vector_set<int> s1(a1);\n    s1.emplace(42);\n\n    pmr::sorted_vector_set<int> s2(s1, a2);\n    EXPECT_EQ(s1.get_allocator(), s2.get_allocator());\n    EXPECT_TRUE(s2.contains(42));\n  }\n\n  {\n    pmr::sorted_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n\n    pmr::sorted_vector_map<int, int> m2(m1, a2);\n    EXPECT_EQ(m1.get_allocator(), m2.get_allocator());\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\nTEST(SortedVectorTypes, TestPmrCopyConstructDifferentAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r1, r2;\n  polymorphic_allocator<std::byte> a1(&r1), a2(&r2);\n  EXPECT_NE(a1, a2);\n\n  {\n    pmr::sorted_vector_set<int> s1(a1);\n    s1.emplace(42);\n\n    pmr::sorted_vector_set<int> s2(s1, a2);\n    EXPECT_NE(s1.get_allocator(), s2.get_allocator());\n    EXPECT_TRUE(s2.contains(42));\n  }\n\n  {\n    pmr::sorted_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n\n    pmr::sorted_vector_map<int, int> m2(m1, a2);\n    EXPECT_NE(m1.get_allocator(), m2.get_allocator());\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\nTEST(SortedVectorTypes, TestPmrMoveConstructSameAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r;\n  polymorphic_allocator<std::byte> a1(&r), a2(&r);\n  EXPECT_EQ(a1, a2);\n\n  {\n    pmr::sorted_vector_set<int> s1(a1);\n    s1.emplace(42);\n    auto d = s1.data();\n\n    pmr::sorted_vector_set<int> s2(std::move(s1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_EQ(s1.get_allocator(), s2.get_allocator());\n    EXPECT_EQ(s2.data(), d);\n    EXPECT_TRUE(s2.contains(42));\n  }\n\n  {\n    pmr::sorted_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n    auto d = m1.data();\n\n    pmr::sorted_vector_map<int, int> m2(std::move(m1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_EQ(m1.get_allocator(), m2.get_allocator());\n    EXPECT_EQ(m2.data(), d);\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\nTEST(SortedVectorTypes, TestPmrMoveConstructDifferentAlloc) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n\n  test_resource r1, r2;\n  polymorphic_allocator<std::byte> a1(&r1), a2(&r2);\n  EXPECT_NE(a1, a2);\n\n  {\n    pmr::sorted_vector_set<int> s1(a1);\n    s1.emplace(42);\n    auto d = s1.data();\n\n    pmr::sorted_vector_set<int> s2(std::move(s1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_NE(s1.get_allocator(), s2.get_allocator());\n    EXPECT_NE(s2.data(), d);\n    EXPECT_TRUE(s2.contains(42));\n  }\n\n  {\n    pmr::sorted_vector_map<int, int> m1(a1);\n    m1.emplace(42, 42);\n    auto d = m1.data();\n\n    pmr::sorted_vector_map<int, int> m2(std::move(m1), a2);\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    EXPECT_NE(m1.get_allocator(), m2.get_allocator());\n    EXPECT_NE(m2.data(), d);\n    EXPECT_EQ(m2.at(42), 42);\n  }\n}\n\ntemplate <typename T>\nusing pmr_vector = std::vector<T, std::pmr::polymorphic_allocator<T>>;\n\nTEST(SortedVectorTypes, TestCreationFromPmrVector) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n  test_resource r;\n  polymorphic_allocator<std::byte> a(&r);\n\n  {\n    pmr_vector<int> c({1, 2, 3}, a);\n    auto d = c.data();\n    pmr::sorted_vector_set<int> s(std::move(c));\n    EXPECT_EQ(s.get_allocator(), a);\n    EXPECT_EQ(s.data(), d);\n  }\n\n  {\n    pmr_vector<int> c({2, 1, 3}, a);\n    auto d = c.data();\n    pmr::sorted_vector_set<int> s(std::move(c));\n    EXPECT_EQ(s.get_allocator(), a);\n    EXPECT_EQ(s.data(), d);\n  }\n\n  {\n    pmr_vector<std::pair<int, int>> c({{1, 1}, {2, 2}, {3, 3}}, a);\n    auto d = c.data();\n    pmr::sorted_vector_map<int, int> m(std::move(c));\n    EXPECT_EQ(m.get_allocator(), a);\n    EXPECT_EQ(m.data(), d);\n  }\n\n  {\n    pmr_vector<std::pair<int, int>> c({{2, 2}, {1, 1}, {3, 3}}, a);\n    auto d = c.data();\n    pmr::sorted_vector_map<int, int> m(std::move(c));\n    EXPECT_EQ(m.get_allocator(), a);\n    EXPECT_EQ(m.data(), d);\n  }\n}\n\nTEST(SortedVectorTypes, TestPmrAllocatorScoped) {\n  namespace pmr = folly::pmr;\n\n  set_default_resource(null_memory_resource());\n  polymorphic_allocator<std::byte> alloc(new_delete_resource());\n\n  {\n    pmr::sorted_vector_set<pmr_vector<int>> s(alloc);\n    s.emplace(1);\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_set<pmr_vector<int>> s(alloc);\n    s.emplace_hint(s.begin(), 1);\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_map<int, pmr_vector<int>> m(alloc);\n    m.emplace(1, 1);\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_map<int, pmr_vector<int>> m(alloc);\n    m.emplace_hint(m.begin(), 1, 1);\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_set<pmr::sorted_vector_map<int, int>> s(alloc);\n    s.emplace(std::initializer_list<std::pair<int, int>>{{42, 42}});\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_set<pmr::sorted_vector_map<int, int>> s(alloc);\n    s.emplace_hint(\n        s.begin(), std::initializer_list<std::pair<int, int>>{{42, 42}});\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_set<pmr::sorted_vector_set<int>> s(alloc);\n    s.emplace(std::initializer_list<int>{1});\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_set<pmr::sorted_vector_set<int>> s(alloc);\n    s.emplace_hint(s.begin(), std::initializer_list<int>{1});\n    EXPECT_EQ(s.begin()->get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_map<int, pmr::sorted_vector_map<int, int>> m(alloc);\n    m.emplace(\n        std::piecewise_construct,\n        std::forward_as_tuple(42),\n        std::forward_as_tuple(\n            std::initializer_list<std::pair<int, int>>{{42, 42}}));\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_map<int, pmr::sorted_vector_map<int, int>> m(alloc);\n    m.emplace_hint(\n        m.begin(),\n        std::piecewise_construct,\n        std::forward_as_tuple(42),\n        std::forward_as_tuple(\n            std::initializer_list<std::pair<int, int>>{{42, 42}}));\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n\n  {\n    pmr::sorted_vector_map<int, pmr::sorted_vector_map<int, int>> m(alloc);\n    m[42][42] = 42;\n    EXPECT_EQ(m.begin()->second.get_allocator(), alloc);\n  }\n}\n\n#endif\n\nTEST(SortedVectorTypes, TestInsertHintCopy) {\n  sorted_vector_set<CountCopyCtor> set;\n  sorted_vector_map<CountCopyCtor, int> map;\n  CountCopyCtor skey;\n  std::pair<CountCopyCtor, int> mkey;\n\n  CountCopyCtor::gCount_ = 0;\n  set.insert(set.end(), skey);\n  map.insert(map.end(), mkey);\n  EXPECT_EQ(CountCopyCtor::gCount_, 2);\n\n  set.emplace(CountCopyCtor(1));\n  map.emplace(CountCopyCtor(1), 1);\n\n  CountCopyCtor::gCount_ = 0;\n  for (size_t i = 0; i <= set.size(); ++i) {\n    auto sit = set.begin();\n    auto mit = map.begin();\n    std::advance(sit, i);\n    std::advance(mit, i);\n    set.insert(sit, skey);\n    map.insert(mit, mkey);\n  }\n  EXPECT_EQ(CountCopyCtor::gCount_, 0);\n}\n\nTEST(SortedVectorTypes, TestTryEmplace) {\n  // folly::Optional becomes empty after move.\n  sorted_vector_map<folly::Optional<int>, folly::Optional<std::string>> map;\n  {\n    auto k = folly::make_optional<int>(1);\n    auto v = folly::make_optional<std::string>(\"1\");\n    const auto& [it, inserted] = map.try_emplace(std::move(k), v);\n    EXPECT_TRUE(inserted);\n    EXPECT_EQ(it->first, 1);\n    EXPECT_EQ(it->second, \"1\");\n    EXPECT_EQ(map.size(), 1);\n  }\n  {\n    auto k = folly::make_optional<int>(1);\n    auto v = folly::make_optional<std::string>(\"another 1\");\n    const auto& [it, inserted] = map.try_emplace(std::move(k), v);\n    EXPECT_FALSE(inserted);\n    EXPECT_EQ(it->first, 1);\n    EXPECT_EQ(it->second, \"1\");\n    EXPECT_EQ(map.size(), 1);\n  }\n  {\n    auto k = folly::make_optional<int>(2);\n    auto v = folly::make_optional<std::string>(\"2\");\n    const auto& [it, inserted] = map.try_emplace(k, v);\n    EXPECT_TRUE(inserted);\n    EXPECT_EQ(it->first, 2);\n    EXPECT_EQ(it->second, \"2\");\n    EXPECT_EQ(k, 2);\n    EXPECT_EQ(v, \"2\");\n    EXPECT_EQ(map.size(), 2);\n  }\n  {\n    auto k = folly::make_optional<int>(3);\n    const auto& [it, inserted] = map.try_emplace(std::move(k));\n    EXPECT_TRUE(inserted);\n    EXPECT_EQ(it->first, 3);\n    EXPECT_EQ(it->second, folly::none);\n    EXPECT_EQ(map.size(), 3);\n  }\n}\n\nTEST(SortedVectorTypes, TestInsertOrAssign) {\n  // folly::Optional becomes empty after move.\n  sorted_vector_map<folly::Optional<int>, folly::Optional<std::string>> map;\n  {\n    auto k = folly::make_optional<int>(1);\n    auto v = folly::make_optional<std::string>(\"1\");\n    const auto& [it, inserted] = map.insert_or_assign(std::move(k), v);\n    EXPECT_TRUE(inserted);\n    EXPECT_EQ(it->first, 1);\n    EXPECT_EQ(it->second, \"1\");\n    EXPECT_EQ(map.size(), 1);\n  }\n  {\n    auto k = folly::make_optional<int>(1);\n    auto v = folly::make_optional<std::string>(\"another 1\");\n    const auto& [it, inserted] = map.insert_or_assign(std::move(k), v);\n    EXPECT_FALSE(inserted);\n    EXPECT_EQ(it->first, 1);\n    EXPECT_EQ(it->second, \"another 1\");\n    EXPECT_EQ(map.size(), 1);\n  }\n  {\n    auto k = folly::make_optional<int>(2);\n    auto v = folly::make_optional<std::string>(\"2\");\n    const auto& [it, inserted] = map.insert_or_assign(k, v);\n    EXPECT_TRUE(inserted);\n    EXPECT_EQ(it->first, 2);\n    EXPECT_EQ(it->second, \"2\");\n    EXPECT_EQ(k, 2);\n    EXPECT_EQ(v, \"2\");\n    EXPECT_EQ(map.size(), 2);\n  }\n}\n\nTEST(SortedVectorTypes, TestInsertOrAssignWithHintExtensive) {\n  for (int sz = 0; sz < 5; ++sz) {\n    for (int hint_pos = 0; hint_pos <= sz; ++hint_pos) {\n      for (int key = 0; key <= 2 * sz; ++key) {\n        sorted_vector_map<int, int> m;\n        for (int i = 0; i < sz; ++i) {\n          m[2 * i + 1] = 2 * i + 1;\n        }\n        auto dupe = m;\n        auto hint = m.cbegin() + hint_pos;\n\n        m.insert_or_assign(hint, key, 100);\n        dupe.insert_or_assign(key, 100);\n        EXPECT_EQ(m, dupe);\n      }\n    }\n  }\n}\n\nTEST(SortedVectorTypes, TestInsertOrAssignWithHint) {\n  // folly::Optional becomes empty after move.\n  sorted_vector_map<folly::Optional<int>, folly::Optional<std::string>> map;\n  {\n    auto k = folly::make_optional<int>(1);\n    auto v = folly::make_optional<std::string>(\"1\");\n    const auto& it = map.insert_or_assign(map.end(), std::move(k), v);\n    EXPECT_EQ(it->first, 1);\n    EXPECT_EQ(it->second, \"1\");\n    EXPECT_EQ(map.size(), 1);\n  }\n  {\n    auto k = folly::make_optional<int>(1);\n    auto v = folly::make_optional<std::string>(\"another 1\");\n    const auto& it = map.insert_or_assign(map.begin(), std::move(k), v);\n    EXPECT_EQ(it->first, 1);\n    EXPECT_EQ(it->second, \"another 1\");\n    EXPECT_EQ(map.size(), 1);\n  }\n  // insert should work when hint is wrong\n  {\n    auto k = folly::make_optional<int>(2);\n    auto v = folly::make_optional<std::string>(\"2\");\n    const auto& it = map.insert_or_assign(map.begin(), k, v);\n    EXPECT_EQ(it->first, 2);\n    EXPECT_EQ(it->second, \"2\");\n    EXPECT_EQ(k, 2);\n    EXPECT_EQ(map.size(), 2);\n  }\n  {\n    auto k = folly::make_optional<int>(1);\n    auto v = folly::make_optional<std::string>(\"yet another 1\");\n    const auto& it = map.insert_or_assign(map.end(), k, v);\n    EXPECT_EQ(it->first, 1);\n    EXPECT_EQ(it->second, \"yet another 1\");\n    EXPECT_EQ(k, 1);\n    EXPECT_EQ(v, \"yet another 1\");\n    EXPECT_EQ(map.size(), 2);\n  }\n}\n\nTEST(SortedVectorTypes, TestGetContainer) {\n  sorted_vector_set<int> set;\n  sorted_vector_map<int, int> map;\n  EXPECT_TRUE(set.get_container().empty());\n  EXPECT_TRUE(map.get_container().empty());\n}\n\nTEST(SortedVectorTypes, Comparisons) {\n  sorted_vector_set<int> set1{1, 2, 3};\n  sorted_vector_set<int> set2{1, 2, 3};\n\n  EXPECT_EQ(set1, set2);\n  EXPECT_FALSE(set1 != set2);\n  EXPECT_FALSE(set1 < set2);\n  EXPECT_TRUE(set1 <= set2);\n  EXPECT_FALSE(set1 > set2);\n  EXPECT_TRUE(set1 >= set2);\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n  EXPECT_EQ(set1 <=> set2, std::strong_ordering::equal);\n#endif\n\n  set2.insert(4);\n  EXPECT_NE(set1, set2);\n  EXPECT_FALSE(set1 == set2);\n  EXPECT_TRUE(set1 < set2);\n  EXPECT_TRUE(set1 <= set2);\n  EXPECT_FALSE(set1 > set2);\n  EXPECT_FALSE(set1 >= set2);\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n  EXPECT_EQ(set1 <=> set2, std::strong_ordering::less);\n  EXPECT_EQ(set2 <=> set1, std::strong_ordering::greater);\n#endif\n\n  sorted_vector_map<int, int> map1{{1, 1}, {2, 2}, {3, 3}};\n  sorted_vector_map<int, int> map2{{1, 1}, {2, 2}, {3, 3}};\n\n  EXPECT_EQ(map1, map2);\n  EXPECT_FALSE(map1 != map2);\n  EXPECT_FALSE(map1 < map2);\n  EXPECT_TRUE(map1 <= map2);\n  EXPECT_FALSE(map1 > map2);\n  EXPECT_TRUE(map1 >= map2);\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n  EXPECT_EQ(map1 <=> map2, std::strong_ordering::equal);\n#endif\n  map1.insert({4, 4});\n  map2.insert({4, 5});\n\n  EXPECT_NE(map1, map2);\n  EXPECT_FALSE(map1 == map2);\n  EXPECT_TRUE(map1 < map2);\n  EXPECT_TRUE(map1 <= map2);\n  EXPECT_FALSE(map1 > map2);\n  EXPECT_FALSE(map1 >= map2);\n\n#if FOLLY_CPLUSPLUS >= 202002L && defined(__cpp_lib_three_way_comparison)\n  EXPECT_EQ(map1 <=> map2, std::strong_ordering::less);\n  EXPECT_EQ(map2 <=> map1, std::strong_ordering::greater);\n#endif\n}\n\nTEST(SortedVectorTypes, TestSwapContainer) {\n  sorted_vector_set<int> set{1, 2, 3};\n  std::vector<int> swapped{6, 5, 4};\n  set.swap_container(swapped);\n  EXPECT_EQ(swapped, (std::vector<int>{1, 2, 3}));\n  EXPECT_EQ(set.get_container(), (std::vector<int>{4, 5, 6}));\n  swapped = {1, 3};\n  set.swap_container(folly::sorted_unique, swapped);\n  EXPECT_EQ(swapped, (std::vector<int>{4, 5, 6}));\n  EXPECT_EQ(set.get_container(), (std::vector<int>{1, 3}));\n\n  sorted_vector_map<int, int> map{{1, 1}, {2, 2}, {3, 3}};\n  std::vector<std::pair<int, int>> swappedMap{{6, 6}, {5, 5}, {4, 4}};\n  map.swap_container(swappedMap);\n  EXPECT_EQ(\n      swappedMap, (std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {3, 3}}));\n  EXPECT_EQ(\n      map.get_container(),\n      (std::vector<std::pair<int, int>>{{4, 4}, {5, 5}, {6, 6}}));\n  swappedMap = {{1, 1}, {3, 3}};\n  map.swap_container(folly::sorted_unique, swappedMap);\n  EXPECT_EQ(\n      swappedMap, (std::vector<std::pair<int, int>>{{4, 4}, {5, 5}, {6, 6}}));\n  EXPECT_EQ(\n      map.get_container(), (std::vector<std::pair<int, int>>{{1, 1}, {3, 3}}));\n}\n"
  },
  {
    "path": "folly/container/test/span_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/span.h>\n\n#include <array>\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\n#if __cpp_lib_span >= 202002L\n#include <span>\n#endif\n\n#if __cpp_lib_span >= 202002L\n\nstatic_assert(\n    std::dynamic_extent == folly::detail::fallback_span::dynamic_extent);\n\n#endif\n\ntemplate <typename Param>\nstruct SpanTest : public testing::TestWithParam<Param> {};\nTYPED_TEST_SUITE_P(SpanTest);\n\ntemplate <template <typename T, size_t N> typename Span>\nstruct quote {\n  template <typename T, size_t N>\n  using apply = Span<T, N>;\n};\n\nTYPED_TEST_P(SpanTest, dyn_traits) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  EXPECT_TRUE(std::is_trivially_copy_constructible_v<span>);\n  EXPECT_TRUE(std::is_trivially_copy_assignable_v<span>);\n  EXPECT_TRUE(std::is_trivially_destructible_v<span>);\n}\n\nTYPED_TEST_P(SpanTest, dyn_ctor_default) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  auto const obj = span{};\n  EXPECT_TRUE(obj.empty());\n  EXPECT_EQ(0, obj.size());\n  EXPECT_EQ(nullptr, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAre());\n}\n\nTYPED_TEST_P(SpanTest, dyn_ctor_pointer_size) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data + 1, 1};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(1, obj.size());\n  EXPECT_EQ(data + 1, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray({6}));\n}\n\nTYPED_TEST_P(SpanTest, dyn_ctor_pointer_pointer) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data + 1, data + 2};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(1, obj.size());\n  EXPECT_EQ(data + 1, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray({6}));\n}\n\nTYPED_TEST_P(SpanTest, dyn_ctor_c_array) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(3, obj.size());\n  EXPECT_EQ(data, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray(data));\n}\n\nTYPED_TEST_P(SpanTest, dyn_ctor_std_array) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  std::array<int, 3> array{5, 6, 7};\n  auto const obj = span{array};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(3, obj.size());\n  EXPECT_EQ(array.data(), obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray(array));\n}\n\nTYPED_TEST_P(SpanTest, dyn_ctor_std_vector) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  std::vector<int> vector{5, 6, 7};\n  auto const obj = span{vector};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(3, vector.size());\n  EXPECT_EQ(vector.data(), obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray(vector));\n}\n\nTYPED_TEST_P(SpanTest, dyn_access) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data};\n  EXPECT_EQ(data, &obj.front());\n  EXPECT_EQ(data + 2, &obj.back());\n  EXPECT_EQ(data + 1, &obj[1]);\n  EXPECT_EQ(12, obj.size_bytes());\n}\n\nTYPED_TEST_P(SpanTest, dyn_static_subspan) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[5] = {5, 6, 7, 8, 9};\n  auto const obj = span{data};\n  EXPECT_THAT((obj.template subspan<0>()), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT((obj.template subspan<1>()), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT((obj.template subspan<5>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template subspan<0, 4>()), testing::ElementsAre(5, 6, 7, 8));\n  EXPECT_THAT((obj.template subspan<1, 4>()), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT((obj.template subspan<3, 0>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template first<0>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template first<2>()), testing::ElementsAre(5, 6));\n  EXPECT_THAT((obj.template first<5>()), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT((obj.template last<0>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template last<2>()), testing::ElementsAre(8, 9));\n  EXPECT_THAT((obj.template last<5>()), testing::ElementsAre(5, 6, 7, 8, 9));\n}\n\nTYPED_TEST_P(SpanTest, dyn_dynamic_subspan) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[5] = {5, 6, 7, 8, 9};\n  auto const obj = span{data};\n  EXPECT_THAT(obj.subspan(0), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT(obj.subspan(1), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT(obj.subspan(5), testing::ElementsAre());\n  EXPECT_THAT(obj.subspan(0, 4), testing::ElementsAre(5, 6, 7, 8));\n  EXPECT_THAT(obj.subspan(1, 4), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT(obj.subspan(3, 0), testing::ElementsAre());\n  EXPECT_THAT(obj.first(0), testing::ElementsAre());\n  EXPECT_THAT(obj.first(2), testing::ElementsAre(5, 6));\n  EXPECT_THAT(obj.first(5), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT(obj.last(0), testing::ElementsAre());\n  EXPECT_THAT(obj.last(2), testing::ElementsAre(8, 9));\n  EXPECT_THAT(obj.last(5), testing::ElementsAre(5, 6, 7, 8, 9));\n}\n\nTYPED_TEST_P(SpanTest, dyn_as_bytes) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[5] = {5, 6, 7, 8, 9};\n  auto const obj = span{data};\n  auto const bytes = as_bytes(obj);\n  EXPECT_EQ(static_cast<void*>(data), bytes.data());\n  EXPECT_EQ(20, bytes.size());\n  auto const wbytes = as_bytes(obj);\n  EXPECT_EQ(static_cast<void*>(data), wbytes.data());\n  EXPECT_EQ(20, wbytes.size());\n}\n\nTYPED_TEST_P(SpanTest, nul_traits) {\n  using span = typename TypeParam::template apply<int, 0>;\n\n  EXPECT_TRUE(std::is_trivially_copy_constructible_v<span>);\n  EXPECT_TRUE(std::is_trivially_copy_assignable_v<span>);\n  EXPECT_TRUE(std::is_trivially_destructible_v<span>);\n}\n\nTYPED_TEST_P(SpanTest, nul_ctor_default) {\n  using span = typename TypeParam::template apply<int, 0>;\n\n  auto const obj = span{};\n  EXPECT_TRUE(obj.empty());\n  EXPECT_EQ(0, obj.size());\n  EXPECT_EQ(nullptr, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAre());\n}\n\nTYPED_TEST_P(SpanTest, fix_traits) {\n  using span = typename TypeParam::template apply<int, 3>;\n\n  EXPECT_TRUE(std::is_trivially_copy_constructible_v<span>);\n  EXPECT_TRUE(std::is_trivially_copy_assignable_v<span>);\n  EXPECT_TRUE(std::is_trivially_destructible_v<span>);\n}\n\nTYPED_TEST_P(SpanTest, fix_ctor_pointer_size) {\n  using span = typename TypeParam::template apply<int, 1>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data + 1, 1};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(1, obj.size());\n  EXPECT_EQ(data + 1, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray({6}));\n}\n\nTYPED_TEST_P(SpanTest, fix_ctor_pointer_pointer) {\n  using span = typename TypeParam::template apply<int, 1>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data + 1, data + 2};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(1, obj.size());\n  EXPECT_EQ(data + 1, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray({6}));\n}\n\nTYPED_TEST_P(SpanTest, fix_ctor_c_array) {\n  using span = typename TypeParam::template apply<int, 3>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(3, obj.size());\n  EXPECT_EQ(data, obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray(data));\n}\n\nTYPED_TEST_P(SpanTest, fix_ctor_std_array) {\n  using span = typename TypeParam::template apply<int, 3>;\n\n  std::array<int, 3> array{5, 6, 7};\n  auto const obj = span{array};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(3, obj.size());\n  EXPECT_EQ(array.data(), obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray(array));\n}\n\nTYPED_TEST_P(SpanTest, fix_ctor_std_vector) {\n  using span = typename TypeParam::template apply<int, 3>;\n\n  std::vector<int> vector{5, 6, 7};\n  auto const obj = span{vector};\n  EXPECT_FALSE(obj.empty());\n  EXPECT_EQ(3, vector.size());\n  EXPECT_EQ(vector.data(), obj.data());\n  EXPECT_THAT(obj, testing::ElementsAreArray(vector));\n}\n\nTYPED_TEST_P(SpanTest, fix_access) {\n  using span = typename TypeParam::template apply<int, 3>;\n\n  int data[3] = {5, 6, 7};\n  auto const obj = span{data};\n  EXPECT_EQ(data, &obj.front());\n  EXPECT_EQ(data + 2, &obj.back());\n  EXPECT_EQ(data + 1, &obj[1]);\n  EXPECT_EQ(12, obj.size_bytes());\n}\n\nTYPED_TEST_P(SpanTest, fix_static_subspan) {\n  using span = typename TypeParam::template apply<int, 5>;\n\n  int data[5] = {5, 6, 7, 8, 9};\n  auto const obj = span{data};\n  EXPECT_THAT((obj.template subspan<0>()), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT((obj.template subspan<1>()), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT((obj.template subspan<5>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template subspan<0, 4>()), testing::ElementsAre(5, 6, 7, 8));\n  EXPECT_THAT((obj.template subspan<1, 4>()), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT((obj.template subspan<3, 0>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template first<0>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template first<2>()), testing::ElementsAre(5, 6));\n  EXPECT_THAT((obj.template first<5>()), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT((obj.template last<0>()), testing::ElementsAre());\n  EXPECT_THAT((obj.template last<2>()), testing::ElementsAre(8, 9));\n  EXPECT_THAT((obj.template last<5>()), testing::ElementsAre(5, 6, 7, 8, 9));\n}\n\nTYPED_TEST_P(SpanTest, fix_dynamic_subspan) {\n  using span = typename TypeParam::template apply<int, 5>;\n\n  int data[5] = {5, 6, 7, 8, 9};\n  auto const obj = span{data};\n  EXPECT_THAT(obj.subspan(0), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT(obj.subspan(1), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT(obj.subspan(5), testing::ElementsAre());\n  EXPECT_THAT(obj.subspan(0, 4), testing::ElementsAre(5, 6, 7, 8));\n  EXPECT_THAT(obj.subspan(1, 4), testing::ElementsAre(6, 7, 8, 9));\n  EXPECT_THAT(obj.subspan(3, 0), testing::ElementsAre());\n  EXPECT_THAT(obj.first(0), testing::ElementsAre());\n  EXPECT_THAT(obj.first(2), testing::ElementsAre(5, 6));\n  EXPECT_THAT(obj.first(5), testing::ElementsAre(5, 6, 7, 8, 9));\n  EXPECT_THAT(obj.last(0), testing::ElementsAre());\n  EXPECT_THAT(obj.last(2), testing::ElementsAre(8, 9));\n  EXPECT_THAT(obj.last(5), testing::ElementsAre(5, 6, 7, 8, 9));\n}\n\nTYPED_TEST_P(SpanTest, fix_as_bytes) {\n  using span = typename TypeParam::template apply<int, 5>;\n\n  int data[5] = {5, 6, 7, 8, 9};\n  auto const obj = span{data};\n  auto const bytes = as_bytes(obj);\n  EXPECT_EQ(static_cast<void*>(data), bytes.data());\n  EXPECT_EQ(20, bytes.size());\n  auto const wbytes = as_bytes(obj);\n  EXPECT_EQ(static_cast<void*>(data), wbytes.data());\n  EXPECT_EQ(20, wbytes.size());\n}\n\nTYPED_TEST_P(SpanTest, reverse_iterator) {\n  using span = typename TypeParam::template apply<int, size_t(-1)>;\n\n  int data[2] = {1, 2};\n  auto const obj = span{data};\n  auto it = obj.rbegin();\n  EXPECT_EQ(2, *it++);\n  EXPECT_EQ(1, *it++);\n  EXPECT_EQ(it, obj.rend());\n}\n\nnamespace fallback_span_ctad {\n\nnamespace fallback = folly::detail::fallback_span;\n\ntemplate <typename... Ts>\nusing deduced_for = decltype(fallback::span(std::declval<Ts>()...));\n\nstatic_assert( //\n    std::is_same_v<\n        fallback::span<const int>,\n        deduced_for<const int*, std::size_t>>);\n\nstatic_assert( //\n    std::is_same_v<\n        fallback::span<const int>,\n        deduced_for<const std::vector<int>&>>);\n\nstatic_assert( //\n    std::is_same_v<fallback::span<int>, deduced_for<std::vector<int>&>>);\n\nstatic_assert(\n    std::is_same_v<fallback::span<int, 3>, deduced_for<std::array<int, 3>&>>);\n\nstatic_assert( //\n    std::is_same_v<\n        fallback::span<const int, 3>,\n        deduced_for<const std::array<int, 3>&>>);\n\nint arr1[3];\nstatic_assert( //\n    std::is_same_v<fallback::span<int, 3>, decltype(fallback::span(arr1))>);\n\nconstexpr int arr2[3]{0, 1, 2};\nstatic_assert( //\n    std::is_same_v<\n        fallback::span<const int, 3>,\n        decltype(fallback::span(arr2))>);\n\n} // namespace fallback_span_ctad\n\n// clang-format off\nREGISTER_TYPED_TEST_SUITE_P(\n    SpanTest\n    , dyn_traits\n    , dyn_ctor_default\n    , dyn_ctor_pointer_size\n    , dyn_ctor_pointer_pointer\n    , dyn_ctor_c_array\n    , dyn_ctor_std_array\n    , dyn_ctor_std_vector\n    , dyn_access\n    , dyn_static_subspan\n    , dyn_dynamic_subspan\n    , dyn_as_bytes\n    , nul_traits\n    , nul_ctor_default\n    , fix_traits\n    , fix_ctor_pointer_size\n    , fix_ctor_pointer_pointer\n    , fix_ctor_c_array\n    , fix_ctor_std_array\n    , fix_ctor_std_vector\n    , fix_access\n    , fix_static_subspan\n    , fix_dynamic_subspan\n    , fix_as_bytes\n    , reverse_iterator\n    );\n// clang-format on\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    folly_fallback_span, SpanTest, quote<folly::detail::fallback_span::span>);\nINSTANTIATE_TYPED_TEST_SUITE_P(folly_span, SpanTest, quote<folly::span>);\n#if __cpp_lib_span >= 202002L\nINSTANTIATE_TYPED_TEST_SUITE_P(std_span, SpanTest, quote<std::span>);\n#endif\n\n#if __cpp_lib_span\n\ntemplate <typename To, typename From>\nusing reinterpret_span_cast_result_type =\n    decltype(folly::reinterpret_span_cast<To>(std::declval<From>()));\n\ntemplate <typename To, typename From>\nusing static_span_cast_result_type =\n    decltype(folly::const_span_cast<To>(std::declval<From>()));\n\ntemplate <typename To, typename From>\nusing const_span_cast_result_type =\n    decltype(folly::const_span_cast<To>(std::declval<From>()));\n\nstatic_assert( //\n    std::is_same_v<\n        std::span<char>,\n        reinterpret_span_cast_result_type<char, std::span<int>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        std::span<const char>,\n        reinterpret_span_cast_result_type<const char, std::span<const int>>>);\nstatic_assert( //\n    std::is_same_v<\n        std::span<const char, 12>,\n        reinterpret_span_cast_result_type<\n            const char,\n            std::span<const int, 3>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        std::span<const char, 12>,\n        const_span_cast_result_type<const char, std::span<char, 12>>>);\nstatic_assert( //\n    std::is_same_v<\n        std::span<char, 12>,\n        const_span_cast_result_type<char, std::span<const char, 12>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        std::span<const char>,\n        const_span_cast_result_type<const char, std::span<char>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        std::span<char>,\n        const_span_cast_result_type<char, std::span<const char>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        std::span<const char, 12>,\n        static_span_cast_result_type<const char, std::span<char, 12>>>);\n\nstatic_assert( //\n    std::is_same_v<\n        std::span<const char>,\n        static_span_cast_result_type<const char, std::span<char>>>);\n\nstruct SpanCastTest : testing::Test {\n  template <typename To, typename From>\n  static auto test(To to, From from) {\n    EXPECT_EQ(\n        static_cast<const void*>(from.data()),\n        static_cast<const void*>(to.data()));\n\n    EXPECT_EQ(\n        static_cast<const void*>(from.data() + from.size()),\n        static_cast<const void*>(to.data() + to.size()));\n  }\n};\n\nTEST_F(SpanCastTest, array) {\n  std::array<int, 4> a;\n  test(folly::reinterpret_span_cast<const char>(std::span(a)), std::span(a));\n  test(folly::reinterpret_span_cast<double>(std::span(a)), std::span(a));\n}\n\nTEST_F(SpanCastTest, vector) {\n  std::vector<int> a(4u, 1);\n  test(folly::reinterpret_span_cast<const char>(std::span(a)), std::span(a));\n  test(folly::reinterpret_span_cast<double>(std::span(a)), std::span(a));\n}\n\nTEST_F(SpanCastTest, const_cast) {\n  const std::vector<int> a(4u, 1);\n  test(folly::const_span_cast<int>(std::span(a)), std::span(a));\n}\n\nTEST_F(SpanCastTest, all_casts) {\n  std::vector<int> b(4u, 1);\n  test(folly::static_span_cast<const int>(std::span(b)), std::span(b));\n  test(folly::const_span_cast<const int>(std::span(b)), std::span(b));\n  test(folly::reinterpret_span_cast<const int>(std::span(b)), std::span(b));\n}\n\nTEST_F(SpanCastTest, static_cast_constexpr) {\n  constexpr bool validation = std::invoke([] {\n    std::array<int, 4> a{0, 1, 2, 3};\n    std::span<int, 4> mutableAFixed(a);\n    std::span<int> mutableADynamic(a);\n    auto resFixed = folly::static_span_cast<const int>(mutableAFixed);\n    if (resFixed.data() != mutableAFixed.data() ||\n        resFixed.size() != mutableAFixed.size()) {\n      return false;\n    }\n    auto resDynamic = folly::static_span_cast<const int>(mutableADynamic);\n    if (resDynamic.data() != mutableAFixed.data() ||\n        resDynamic.size() != mutableAFixed.size()) {\n      return false;\n    }\n\n    return true;\n  });\n  EXPECT_TRUE(validation);\n}\n\n#endif\n"
  },
  {
    "path": "folly/container/test/tape_bench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/tape.h>\n\n#include <random>\n#include <string>\n#include <vector>\n#include <folly/Benchmark.h>\n#include <folly/init/Init.h>\n\nnamespace {\n\nusing vec_str = std::vector<std::string>;\nusing st_tape = folly::string_tape;\nusing vv_int = std::vector<std::vector<int>>;\nusing tv_int = folly::tape<std::vector<int>>;\n\ntemplate <typename Cont>\nstruct ContGenerator {\n  ContGenerator(std::size_t minLen, std::size_t maxLen)\n      : g(minLen + maxLen), len(minLen, maxLen) {}\n\n  const Cont& operator()() {\n    buf.resize(len(g));\n    return buf; // we don't care for contents\n  }\n\n  std::mt19937 g;\n  std::uniform_int_distribution<std::size_t> len;\n\n  Cont buf;\n};\n\ntemplate <typename Cont, std::size_t n, std::size_t minLen, std::size_t maxLen>\nconst std::vector<Cont>& generateContainers() {\n  static const std::vector<Cont> res = [] {\n    ContGenerator<Cont> gen{minLen, maxLen};\n    std::vector<Cont> r;\n    r.reserve(n);\n    for (std::size_t i = 0; i != n; ++i) {\n      r.push_back(gen());\n    }\n    return r;\n  }();\n\n  return res;\n}\n\nconstexpr std::size_t kContainerCopies = 100'000;\n\ntemplate <\n    typename ContainerType,\n    typename RecordType,\n    std::size_t n,\n    std::size_t minLen,\n    std::size_t maxLen>\nconst std::vector<ContainerType>& generateContainerCopies() {\n  static const std::vector<ContainerType> res = [] {\n    const auto& input = generateContainers<RecordType, n, minLen, maxLen>();\n    const ContainerType sample(input.begin(), input.end());\n\n    std::vector<ContainerType> copies(kContainerCopies, sample);\n    std::shuffle(copies.begin(), copies.end(), std::mt19937{minLen + maxLen});\n\n    return copies;\n  }();\n  return res;\n}\n\ntemplate <\n    typename StringContainer,\n    std::size_t n,\n    std::size_t minLen,\n    std::size_t maxLen>\nvoid constructorStrings(std::size_t iters) {\n  const auto& strings = generateContainers<std::string, n, minLen, maxLen>();\n\n  while (iters--) {\n    StringContainer cont{strings.begin(), strings.end()};\n    folly::doNotOptimizeAway(cont);\n  }\n}\n\ntemplate <typename T>\nvoid pushBackByOne(\n    std::vector<std::vector<T>>& cont, const std::vector<std::vector<T>>& in) {\n  for (const auto& v : in) {\n    cont.emplace_back();\n    for (const auto& x : v) {\n      cont.back().push_back(x);\n    }\n  }\n}\n\ntemplate <typename T>\nvoid pushBackByOne(\n    folly::tape<std::vector<T>>& cont, const std::vector<std::vector<T>>& in) {\n  auto builder = cont.new_record_builder();\n  for (const auto& v : in) {\n    for (const auto& x : v) {\n      builder.push_back(x);\n    }\n    builder.commit();\n  }\n}\n\ntemplate <\n    typename Container,\n    std::size_t n,\n    std::size_t minLen,\n    std::size_t maxLen>\nvoid pushBackInts(std::size_t iters) {\n  const auto& vecs = generateContainers<std::vector<int>, n, minLen, maxLen>();\n\n  while (iters--) {\n    Container r;\n    pushBackByOne(r, vecs);\n    folly::doNotOptimizeAway(r);\n  }\n}\n\ntemplate <typename Container>\nvoid iterateBenchImpl(const std::vector<Container>& copies, std::size_t iters) {\n  std::size_t i = 0;\n\n  while (iters--) {\n    for (const auto& v : copies[i]) {\n      for (const auto& x : v) {\n        auto xCopy = x;\n        folly::doNotOptimizeAway(x);\n        folly::doNotOptimizeAway(xCopy);\n      }\n    }\n    ++i;\n    i %= copies.size();\n  }\n}\n\ntemplate <\n    typename Container,\n    std::size_t n,\n    std::size_t minLen,\n    std::size_t maxLen>\nvoid iterateInts(std::size_t iters) {\n  const auto& copies =\n      generateContainerCopies<Container, std::vector<int>, n, minLen, maxLen>();\n  iterateBenchImpl(copies, iters);\n}\n\ntemplate <\n    typename Container,\n    std::size_t n,\n    std::size_t minLen,\n    std::size_t maxLen>\nvoid iterateStrings(std::size_t iters) {\n  const auto& copies =\n      generateContainerCopies<Container, std::string, n, minLen, maxLen>();\n  iterateBenchImpl(copies, iters);\n}\n\n// Disabling clang format for table formatting\n// clang-format off\nBENCHMARK(IterateVecIntsCacheMiss_20_0_15,     n) { iterateInts   <vv_int,  20, 0, 15>(n); }\nBENCHMARK(IterateTapeIntsCacheMiss_20_0_15,    n) { iterateInts   <tv_int,  20, 0, 15>(n); }\nBENCHMARK(IterateVecStringsCacheMiss_20_0_15,  n) { iterateStrings<vec_str, 20, 0, 15>(n); }\nBENCHMARK(IterateTapeStringsCacheMiss_20_0_15, n) { iterateStrings<st_tape, 20, 0, 15>(n); }\nBENCHMARK_DRAW_LINE();\nBENCHMARK(PushBackVecInts_20_0_15,   n) { pushBackInts<vv_int, 20, 0, 15>(n); }\nBENCHMARK(PushBackTapeInts_20_0_15,  n) { pushBackInts<tv_int, 20, 0, 15>(n); }\nBENCHMARK_DRAW_LINE();\nBENCHMARK(ConstructVecSmallStrings_20_0_15,   n) { constructorStrings<vec_str, 20, 0, 15>(n); }\nBENCHMARK(ConstructTapeSmallStrings_20_0_15,  n) { constructorStrings<st_tape, 20, 0, 15>(n); }\nBENCHMARK(ConstructVecLargeStrings_20_16_32,  n) { constructorStrings<vec_str, 20, 16, 32>(n); }\nBENCHMARK(ConstructTapeLargeStrings_20_16_32, n) { constructorStrings<st_tape, 20, 16, 32>(n); }\nBENCHMARK_DRAW_LINE();\nBENCHMARK(ConstructVec2SmallStrings,    n) { constructorStrings<vec_str, 2, 0, 15>(n); }\nBENCHMARK(ConstructTape2SmallStrings,   n) { constructorStrings<st_tape, 2, 0, 15>(n); }\n// clang-format on\n\n} // namespace\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/container/test/tape_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/tape.h>\n\n#include <forward_list>\n#include <iterator>\n#include <vector>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/small_vector.h>\n\nnamespace {\n\ntemplate <typename I>\nstruct InputIter {\n  I wrapped;\n\n  using value_type = typename std::iterator_traits<I>::value_type;\n  using reference = typename std::iterator_traits<I>::reference;\n  using difference_type = typename std::iterator_traits<I>::difference_type;\n  using pointer = typename std::iterator_traits<I>::pointer;\n  using iterator_category = std::input_iterator_tag;\n\n  InputIter& operator++() {\n    ++wrapped;\n    return *this;\n  }\n\n  reference operator*() const { return *wrapped; }\n\n  auto operator++(int) { operator++(); }\n\n  friend bool operator==(const InputIter& x, const InputIter& y) {\n    return x.wrapped == y.wrapped;\n  }\n  friend bool operator!=(const InputIter& x, const InputIter& y) {\n    return !(x == y);\n  }\n};\n\ntemplate <typename T>\nstruct InputRange {\n  std::vector<T> c_;\n  using iterator = InputIter<typename std::vector<T>::const_iterator>;\n\n  iterator begin() const { return iterator{c_.begin()}; }\n  iterator end() const { return iterator{c_.end()}; }\n};\n\n#if defined(__cpp_lib_ranges)\nstatic_assert(!std::forward_iterator<InputIter<int*>>);\nstatic_assert(std::input_iterator<InputIter<int*>>);\n#endif\n\ntemplate <typename T>\nauto asRange(const std::vector<T>& c, std::input_iterator_tag) {\n  return InputRange<T>{c};\n}\n\ntemplate <typename T>\nauto asRange(const std::vector<T>& c, std::forward_iterator_tag) {\n  return std::forward_list<T>{c.begin(), c.end()};\n}\n\ntemplate <typename T>\nauto asRange(const std::vector<T>& c, std::random_access_iterator_tag) {\n  return c;\n}\n\n} // namespace\n\n// tape types\n\nstatic_assert(\n    std::is_same_v<folly::StringPiece, folly::string_tape::reference>);\nstatic_assert(\n    std::is_same_v<folly::StringPiece, folly::string_tape::value_type>);\nstatic_assert( //\n    std::is_same_v<\n        folly::Range<const int*>,\n        folly::tape<std::vector<int>>::reference>);\nstatic_assert( //\n    std::is_same_v<\n        folly::Range<const int*>,\n        folly::tape<std::vector<int>>::value_type>);\nstatic_assert( //\n    std::is_same_v<\n        folly::Range<const int*>,\n        folly::tape<folly::small_vector<int, 4>>::reference>);\n\n#if defined(__cpp_lib_ranges)\nstatic_assert(std::ranges::random_access_range<folly::string_tape>);\n#endif\n\nTEST(Tape, SmokeTest) {\n  folly::string_tape st;\n\n  st.push_back(\"abc\");\n  st.push_back(\"def\");\n\n  ASSERT_EQ(\"abc\", st[0]);\n  ASSERT_EQ(\"def\", st[1]);\n  ASSERT_EQ(\"abc\", st.front());\n  ASSERT_EQ(\"def\", st.back());\n}\n\nTEST(Tape, IntTape) {\n  folly::tape<std::vector<int>> t;\n  t.push_back({1, 2});\n  t.emplace_back(std::vector{3});\n\n  ASSERT_EQ(1, t[0][0]);\n  ASSERT_EQ(2, t[0][1]);\n  ASSERT_EQ(3, t[1][0]);\n}\n\nTEST(Tape, Count) {\n  folly::string_tape st{\"abc\", \"def\", \"bla\", \"abc\"};\n\n  ASSERT_EQ(2U, (std::count(st.begin(), st.end(), \"abc\")));\n  ASSERT_EQ(2U, (std::count(st.cbegin(), st.cend(), \"abc\")));\n  ASSERT_EQ(2U, (std::count(st.rbegin(), st.rend(), \"abc\")));\n  ASSERT_EQ(2U, (std::count(st.crbegin(), st.crend(), \"abc\")));\n}\n\nTEST(Tape, At) {\n  folly::string_tape st{\"ab\", \"cde\", \"f\"};\n  ASSERT_EQ(\"ab\", st.at(0));\n  ASSERT_EQ(\"cde\", st.at(1));\n  ASSERT_EQ(\"f\", st.at(2));\n  ASSERT_THROW((void)st.at(3), std::out_of_range);\n}\n\nTEST(Tape, Move) {\n  folly::string_tape st0{\"ab\", \"cde\", \"f\"};\n  folly::string_tape st1{st0};\n\n  folly::string_tape move_constuctor{std::move(st0)};\n  folly::string_tape move_assign;\n  move_assign = std::move(st1);\n\n  ASSERT_EQ(move_constuctor, move_assign);\n  ASSERT_THAT(move_constuctor, testing::ElementsAre(\"ab\", \"cde\", \"f\"));\n\n  // moved out should be valid but unspecified.\n  // we had a bug where 0s marker was missing.\n  // The test is over specified.\n  ASSERT_EQ(0U, st0.size());\n  ASSERT_EQ(0U, st1.size());\n\n  ASSERT_TRUE(st0.begin() == st0.end());\n  ASSERT_TRUE(st1.begin() == st1.end());\n\n  st0.push_back(\"ab\");\n  st1.push_back(\"ab\");\n\n  ASSERT_THAT(st0, testing::ElementsAre(\"ab\"));\n  ASSERT_THAT(st1, testing::ElementsAre(\"ab\"));\n\n  // self-move should be valid but unspecified.\n  // not many generic choices, though: either no-op or clear or reset\n  // std::vector<int> self-move is:\n  //   reset under libstdc++ and libc++\n  //   no-op under microsoft-stl\n  // the current implementation is reset\n  move_assign = std::move(std::move(move_assign)); // suppress self-move warning\n  ASSERT_EQ(0U, move_assign.size());\n}\n\nTEST(Tape, TwoItersConstructor) {\n  auto tst = [&](auto tag) {\n    {\n      const std::vector<std::vector<int>> a{{1, 2}, {3, 4, 5}};\n\n      auto input = asRange(a, tag);\n      folly::tape<std::vector<int>> t{input.begin(), input.end()};\n      ASSERT_EQ(2, t.size());\n      ASSERT_EQ(folly::crange(a[0]), t[0]);\n      ASSERT_EQ(folly::crange(a[1]), t[1]);\n    }\n    {\n      const std::vector<std::vector<int>> empty;\n      auto input = asRange(empty, tag);\n      folly::tape<std::vector<int>> t{input.begin(), input.end()};\n      ASSERT_EQ(0, t.size());\n      ASSERT_TRUE(t.empty());\n    }\n    {\n      const std::vector<std::vector<int>> emptyRow{{1, 2}, {}, {3, 4, 5}};\n      auto input = asRange(emptyRow, tag);\n      folly::tape<std::vector<int>> t{input.begin(), input.end()};\n      ASSERT_EQ(3, t.size());\n      ASSERT_EQ(folly::crange(emptyRow[0]), t[0]);\n      ASSERT_EQ(folly::crange(emptyRow[1]), t[1]);\n      ASSERT_EQ(folly::crange(emptyRow[2]), t[2]);\n    }\n  };\n\n  tst(std::input_iterator_tag{});\n  tst(std::forward_iterator_tag{});\n  tst(std::random_access_iterator_tag{});\n}\n\nTEST(Tape, InitListNotStrings) {\n  std::vector<int> a[] = {{1, 2}, {3, 4, 5}, {6, 7}};\n\n  folly::tape<std::vector<int>> t{a[0], a[1], a[2]};\n  ASSERT_EQ(3, t.size());\n  ASSERT_EQ(folly::crange(a[0]), t[0]);\n  ASSERT_EQ(folly::crange(a[1]), t[1]);\n  ASSERT_EQ(folly::crange(a[2]), t[2]);\n}\n\nTEST(Tape, Ordering) {\n  const folly::string_tape st1{\"abc\", \"def\"};\n  const folly::string_tape st2{\"abcd\", \"ef\"};\n\n  ASSERT_EQ(st1, st1);\n  ASSERT_NE(st1, st2);\n\n  ASSERT_LT(st1, st2);\n  ASSERT_LE(st1, st2);\n  ASSERT_LE(st1, st1);\n\n  ASSERT_GT(st2, st1);\n  ASSERT_GE(st2, st1);\n  ASSERT_GE(st1, st1);\n}\n\nTEST(Tape, RecordBuilder) {\n  folly::string_tape st;\n\n  {\n    auto builder = st.new_record_builder();\n    ASSERT_TRUE(builder.empty());\n    ASSERT_EQ(0U, builder.size());\n    builder.push_back('a');\n    builder.push_back('b');\n    ASSERT_FALSE(builder.empty());\n    ASSERT_EQ(2U, builder.size());\n\n    ASSERT_EQ('b', builder.back());\n    ASSERT_EQ('b', std::as_const(builder).back());\n\n    ASSERT_EQ(std::string_view(builder.begin(), builder.end()), \"ab\");\n    ASSERT_EQ(std::string_view(builder.cbegin(), builder.cend()), \"ab\");\n    *builder.begin() = 'c';\n    builder.commit();\n  }\n  {\n    auto builder = st.new_record_builder();\n    ASSERT_EQ(builder.emplace_back('d'), 'd');\n    builder[0] = 'e';\n    ASSERT_EQ(std::as_const(builder)[0], 'e');\n    builder.commit();\n  }\n  {\n    auto builder = st.new_record_builder();\n    builder.emplace_back(); // test emplace with default constructor\n    builder.at(0) = 'b';\n    ASSERT_EQ(std::as_const(builder).at(0), 'b');\n    builder[0] = 'a';\n    ASSERT_EQ(std::as_const(builder)[0], 'a');\n    builder.commit();\n  }\n  {\n    auto builder = st.new_record_builder();\n    constexpr std::string_view s = \"abc\";\n    std::copy(s.begin(), s.end(), builder.back_inserter());\n    builder.commit();\n  }\n  {\n    auto builder = st.new_record_builder();\n    builder.push_back('0');\n    builder.push_back('1');\n    // Default abort - not committed\n  }\n  {\n    // calling abort\n    auto builder = st.new_record_builder();\n    ASSERT_EQ(0U, builder.size());\n    builder.push_back('a');\n    builder.push_back('b');\n    ASSERT_EQ(2U, builder.size());\n    builder.abort();\n    ASSERT_EQ(0U, builder.size());\n    builder.push_back('a');\n    builder.push_back('b');\n    builder.commit();\n  }\n  {\n    // amending record\n    ASSERT_EQ(st.back(), \"ab\");\n    auto builder = st.last_record_builder();\n    builder.push_back('c');\n    builder[0] = '0';\n    builder.commit();\n    ASSERT_EQ(st.back(), \"0bc\");\n  }\n  {\n    // not committing last record\n    ASSERT_EQ(st.size(), 5);\n\n    {\n      auto builder = st.new_record_builder();\n      builder.push_back('a');\n      builder.push_back('b');\n      builder.commit();\n    }\n\n    ASSERT_EQ(st.size(), 6);\n    (void)st.last_record_builder(); // destructor will auto abort.\n\n    ASSERT_EQ(st.size(), 5);\n  }\n\n  ASSERT_EQ(\"cb\", st[0]);\n  ASSERT_EQ(\"e\", st[1]);\n  ASSERT_EQ(\"a\", st[2]);\n  ASSERT_EQ(\"abc\", st[3]);\n  ASSERT_EQ(\"0bc\", st[4]);\n  ASSERT_EQ(10U, st.size_flat());\n  ASSERT_EQ(5U, st.size());\n\n  // checking that pop back is still ok.\n\n  st.pop_back();\n  ASSERT_EQ(\"cb\", st[0]);\n  ASSERT_EQ(\"e\", st[1]);\n  ASSERT_EQ(\"a\", st[2]);\n  ASSERT_EQ(\"abc\", st[3]);\n  ASSERT_EQ(7U, st.size_flat());\n  ASSERT_EQ(4U, st.size());\n\n  st.pop_back();\n  ASSERT_EQ(\"cb\", st[0]);\n  ASSERT_EQ(\"e\", st[1]);\n  ASSERT_EQ(\"a\", st[2]);\n  ASSERT_EQ(4U, st.size_flat());\n  ASSERT_EQ(3U, st.size());\n\n  st.pop_back();\n  ASSERT_EQ(\"cb\", st[0]);\n  ASSERT_EQ(\"e\", st[1]);\n  ASSERT_EQ(2U, st.size());\n  ASSERT_EQ(3U, st.size_flat());\n\n  st.pop_back();\n  ASSERT_EQ(\"cb\", st[0]);\n  ASSERT_EQ(1U, st.size());\n  ASSERT_EQ(2U, st.size_flat());\n\n  st.pop_back();\n  ASSERT_EQ(0U, st.size());\n  ASSERT_EQ(0U, st.size_flat());\n}\n\nTEST(Tape, PushBack) {\n  folly::tape<std::vector<int>> t;\n\n  auto tst = [&](auto tag) mutable {\n    t.clear();\n    ASSERT_EQ(0, t.size());\n\n    std::vector<int> a{1, 2, 3}, b{4, 5}, c;\n    t.push_back(asRange(a, tag));\n    ASSERT_EQ(1, t.size());\n    ASSERT_EQ(folly::crange(a), t[0]);\n\n    t.push_back(asRange(b, tag));\n    ASSERT_EQ(2, t.size());\n    ASSERT_EQ(folly::crange(a), t[0]);\n    ASSERT_EQ(folly::crange(b), t[1]);\n\n    t.push_back(c);\n    ASSERT_EQ(3, t.size());\n    ASSERT_EQ(folly::crange(a), t[0]);\n    ASSERT_EQ(folly::crange(b), t[1]);\n    ASSERT_EQ(folly::crange(c), t[2]);\n  };\n\n  tst(std::input_iterator_tag{});\n  tst(std::forward_iterator_tag{});\n  tst(std::random_access_iterator_tag{});\n}\n\nTEST(Tape, PushBackStr) {\n  folly::string_tape st;\n\n  st.push_back(\"abc\");\n  st.push_back({'d', 'e'});\n\n  constexpr std::string_view c = \"fgh\";\n  st.push_back(c);\n  constexpr std::string_view d = \"ijklm\";\n  st.push_back(d.begin(), d.end());\n  ASSERT_THAT(st, testing::ElementsAre(\"abc\", \"de\", \"fgh\", \"ijklm\"));\n}\n\nTEST(Tape, EmptyRecord) {\n  folly::tape<std::vector<int>> t;\n\n  t.push_back({});\n  t.push_back({});\n\n  ASSERT_EQ(2U, t.size());\n  ASSERT_TRUE(t[0].empty());\n  ASSERT_TRUE(t[1].empty());\n}\n\nTEST(Tape, Resize) {\n  folly::string_tape st{\"ab\", \"abc\", \"abcd\", \"de\"};\n\n  st.resize(6U);\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"abc\", \"abcd\", \"de\", \"\", \"\"));\n\n  st.resize(2U);\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"abc\"));\n\n  st.resize(2U);\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"abc\"));\n\n  st.push_back(\"abcd\");\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"abc\", \"abcd\"));\n\n  st.resize(0U);\n  ASSERT_TRUE(st.empty());\n\n  st.push_back(\"ab\");\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\"));\n}\n\nTEST(Tape, EmptyStrings) {\n  folly::string_tape st{\"a\"};\n  st.push_back(\"\");\n  ASSERT_THAT(st, testing::ElementsAre(\"a\", \"\"));\n\n  st.emplace_back();\n\n  ASSERT_THAT(st, testing::ElementsAre(\"a\", \"\", \"\"));\n\n  st.erase(st.begin() + 1, st.end());\n\n  ASSERT_THAT(st, testing::ElementsAre(\"a\"));\n  st.emplace_back();\n  st.emplace_back(\"bd\");\n  ASSERT_THAT(st, testing::ElementsAre(\"a\", \"\", \"bd\"));\n}\n\nTEST(Tape, InsertOne) {\n  auto tst = [](auto tag) {\n    folly::tape<std::vector<int>> t;\n\n    std::vector<int> a{1, 2, 3}, b{4, 5}, c{6, 7, 8}, d{};\n\n    {\n      auto pos = t.insert(t.end(), asRange(a, tag));\n      ASSERT_EQ(pos - t.begin(), 0);\n      ASSERT_THAT(t, testing::ElementsAre(a));\n    }\n    {\n      auto pos = t.insert(t.end(), asRange(c, tag));\n\n      ASSERT_EQ(pos - t.begin(), 1);\n      ASSERT_THAT(t, testing::ElementsAre(a, c));\n    }\n    {\n      auto pos = t.insert(t.begin() + 1, asRange(b, tag));\n\n      ASSERT_EQ(pos - t.begin(), 1);\n      ASSERT_THAT(t, testing::ElementsAre(a, b, c));\n    }\n    {\n      auto pos = t.insert(t.begin() + 2, asRange(d, tag));\n\n      ASSERT_EQ(pos - t.begin(), 2);\n      ASSERT_THAT(t, testing::ElementsAre(a, b, d, c));\n    }\n  };\n\n  tst(std::input_iterator_tag{});\n  tst(std::forward_iterator_tag{});\n  tst(std::random_access_iterator_tag{});\n}\n\nTEST(Tape, InsertOneStr) {\n  folly::string_tape st;\n  auto pos = st.insert(st.end(), \"ab\");\n  ASSERT_EQ(pos - st.begin(), 0);\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\"));\n\n  {\n    std::string s(\"cde\");\n    pos = st.insert(st.end(), s.begin(), s.end());\n    ASSERT_EQ(pos - st.begin(), 1);\n    ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"cde\"));\n  }\n\n  {\n    pos = st.insert(st.begin() + 1, {'0', '1'});\n    ASSERT_EQ(pos - st.begin(), 1);\n    ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"01\", \"cde\"));\n  }\n\n  {\n    std::forward_list<char> in{'1', '2', '3'};\n    pos = st.insert(st.begin(), in);\n    ASSERT_EQ(pos - st.begin(), 0);\n    ASSERT_THAT(st, testing::ElementsAre(\"123\", \"ab\", \"01\", \"cde\"));\n  }\n}\n\nTEST(Tape, EraseOne) {\n  folly::string_tape st{\"ab\", \"abc\", \"abcd\", \"de\"};\n  folly::string_tape::iterator pos = st.erase(st.cbegin() + 1);\n\n  ASSERT_EQ(1, pos - st.begin());\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"abcd\", \"de\"));\n\n  // tape is still functional\n  st.push_back(\"ef\");\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"abcd\", \"de\", \"ef\"));\n\n  ASSERT_EQ(st.erase(st.cbegin()) - st.cbegin(), 0);\n  ASSERT_THAT(st, testing::ElementsAre(\"abcd\", \"de\", \"ef\"));\n\n  ASSERT_EQ(st.erase(st.begin() + 1) - st.cbegin(), 1);\n  ASSERT_THAT(st, testing::ElementsAre(\"abcd\", \"ef\"));\n\n  ASSERT_EQ(st.erase(st.cend() - 1) - st.cbegin(), 1);\n  ASSERT_THAT(st, testing::ElementsAre(\"abcd\"));\n\n  ASSERT_EQ(st.erase(st.cbegin()) - st.cbegin(), 0);\n  ASSERT_TRUE(st.empty());\n}\n\nTEST(Tape, EraseRange) {\n  folly::string_tape st{\"ab\", \"abc\", \"abcd\", \"de\"};\n\n  folly::string_tape::iterator pos = st.erase(st.cbegin() + 1, st.cend() - 1);\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"de\"));\n  ASSERT_EQ(pos - st.begin(), 1);\n\n  pos = st.erase(st.end(), st.end());\n  ASSERT_THAT(st, testing::ElementsAre(\"ab\", \"de\"));\n  ASSERT_EQ(pos - st.begin(), 2);\n}\n\nTEST(Tape, Iteration) {\n  folly::string_tape st{\"0\", \"10\", \"100\", \"1000\"};\n  ASSERT_THAT(st, testing::ElementsAre(\"0\", \"10\", \"100\", \"1000\"));\n\n  folly::string_tape st2(st.rbegin(), st.rend());\n  ASSERT_THAT(st2, testing::ElementsAre(\"1000\", \"100\", \"10\", \"0\"));\n}\n\nTEST(Tape, ReserveShrink) {\n  // There is a limit to what we can test since reserve/shrink\n  // have very little guarantees.\n\n  folly::string_tape st;\n  st.reserve(2, 7);\n\n  st.push_back(\"abc\");\n  const char* beforePushSecond = st[0].data();\n\n  st.push_back(\"defg\");\n  const char* afterPushSecond = st[0].data();\n  ASSERT_EQ(beforePushSecond, afterPushSecond);\n\n  st.shrink_to_fit();\n  const char* afterShrink = st[0].data();\n  ASSERT_EQ(beforePushSecond, afterShrink);\n}\n\nTEST(Tape, TapeOfTapes) {\n  folly::tape<folly::string_tape> strings;\n\n  auto builder = strings.new_record_builder();\n  builder.push_back(\"ab\");\n  builder.push_back(\"abc\");\n  builder.commit();\n\n  builder.push_back(\"bc\");\n  builder.push_back(\"bcd\");\n  builder.push_back(\"bcde\");\n  builder.commit();\n\n  ASSERT_EQ(strings.size(), 2);\n  ASSERT_THAT(strings[0], testing::ElementsAre(\"ab\", \"abc\"));\n  ASSERT_THAT(strings[1], testing::ElementsAre(\"bc\", \"bcd\", \"bcde\"));\n}\n"
  },
  {
    "path": "folly/container/test/vector_bool_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/vector_bool.h>\n\n#include <folly/Memory.h>\n#include <folly/container/detail/BoolWrapper.h>\n#include <folly/memory/MemoryResource.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nTEST(VectorBoolTest, Example) {\n  folly::vector_bool<> vec = {false, true};\n  EXPECT_FALSE(vec[0]);\n  EXPECT_TRUE(vec[1]);\n}\n\nTEST(VectorBoolTest, CustomAllocator) {\n  folly::vector_bool<SysAllocator> vec = {false, true};\n  EXPECT_FALSE(vec[0]);\n  EXPECT_TRUE(vec[1]);\n}\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nTEST(VectorBoolTest, PmrAllocator) {\n  folly::pmr::vector_bool vec = {false, true};\n  EXPECT_FALSE(vec[0]);\n  EXPECT_TRUE(vec[1]);\n}\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/container/vector_bool.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <vector>\n\n#include <folly/container/detail/BoolWrapper.h>\n#include <folly/memory/MemoryResource.h>\n\nnamespace folly {\n\n/// Convenience alias to use instead of `std::vector<bool>` to avoid infamous\n/// `std::vector<bool>` specialization.\n///\n/// Usage example:\n///\n///     folly::vector_bool<> vec = {false, true};\n///     assert(vec[0] == false);\n///     assert(vec[1] == true);\ntemplate <template <class> typename Allocator = std::allocator>\nusing vector_bool = std::vector<\n    folly::detail::BoolWrapper, //\n    Allocator<folly::detail::BoolWrapper>>;\n\n#if FOLLY_HAS_MEMORY_RESOURCE\nnamespace pmr {\n\nusing vector_bool = vector_bool<std::pmr::polymorphic_allocator>;\n\n} // namespace pmr\n#endif // FOLLY_HAS_MEMORY_RESOURCE\n\n} // namespace folly\n"
  },
  {
    "path": "folly/coro/Accumulate-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\ntemplate <typename Reference, typename Value, typename Output>\nTask<Output> accumulate(\n    AsyncGenerator<Reference, Value> generator, Output init) {\n  return accumulate(std::move(generator), std::move(init), std::plus{});\n}\n\ntemplate <\n    typename Reference,\n    typename Value,\n    typename Output,\n    typename BinaryOp>\nTask<Output> accumulate(\n    AsyncGenerator<Reference, Value> generator, Output init, BinaryOp op) {\n  while (auto next = co_await generator.next()) {\n    init = op(std::move(init), std::move(next).value());\n  }\n  co_return init;\n}\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Accumulate.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// Accumulate the values from an input stream into a single value given\n// an optional binary accumulation operation, similar to std::accumulate.\n//\n// The input is a stream of values.\n//\n// The output is a Task containing the result of the accumulation\n//\n// Example:\n//   AsyncGenerator<int> stream();\n//\n//   Task<void> consumer() {\n//     auto sum = co_await accumulate(stream(), 0, std::plus{});\n//   }\ntemplate <typename Reference, typename Value, typename Output>\nTask<Output> accumulate(\n    AsyncGenerator<Reference, Value> generator, Output init);\n\ntemplate <\n    typename Reference,\n    typename Value,\n    typename Output,\n    typename BinaryOp>\nTask<Output> accumulate(\n    AsyncGenerator<Reference, Value> generator, Output init, BinaryOp op);\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Accumulate-inl.h>\n"
  },
  {
    "path": "folly/coro/AsyncGenerator.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_asyncgenerator\n//\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/ExceptionWrapper.h>\n#include <folly/Traits.h>\n#include <folly/Try.h>\n#include <folly/coro/AutoCleanup-fwd.h>\n#include <folly/coro/BasePromise.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Result.h>\n#include <folly/coro/ScopeExit.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/coro/detail/Malloc.h>\n#include <folly/coro/detail/ManualLifetime.h>\n#include <folly/lang/SafeAlias-fwd.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <glog/logging.h>\n\n#include <iterator>\n#include <type_traits>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\ntemplate <typename Reference, typename Value, bool RequiresCleanup>\nclass AsyncGeneratorPromise;\n\n} // namespace detail\n\n/**\n * The AsyncGenerator class represents a sequence of asynchronously produced\n * values where the values are produced by a coroutine.\n *\n * Values are produced by using the 'co_yield' keyword and the coroutine can\n * also consume other asynchronous operations using the 'co_await' keyword.\n * The end of the sequence is indicated by executing 'co_return;' either\n * explicitly or by letting execution run off the end of the coroutine.\n *\n * Reference Type\n * --------------\n * The first template parameter controls the 'reference' type.\n * i.e. the type returned when you dereference the iterator using operator*().\n * This type is typically specified as an actual reference type.\n * eg. 'const T&' (non-mutable), 'T&' (mutable)  or 'T&&' (movable) depending\n * what access you want your consumers to have to the yielded values.\n *\n * It's also possible to specify the 'Reference' template parameter as a value\n * type. In this case the generator takes a copy of the yielded value (either\n * copied or move-constructed) and you get a copy of this value every time\n * you dereference the iterator with '*iter'.\n * This can be expensive for types that are expensive to copy, but can provide\n * a small performance win for types that are cheap to copy (like built-in\n * integer types).\n *\n * Value Type\n * ----------\n * The second template parameter is optional, but if specified can be used as\n * the value-type that should be used to take a copy of the value returned by\n * the Reference type.\n * By default this type is the same as 'Reference' type stripped of qualifiers\n * and references. However, in some cases it can be a different type.\n * For example, if the 'Reference' type was a non-reference proxy type.\n *\n * Example:\n *\n *     AsyncGenerator<std::tuple<const K&, V&>, std::tuple<K, V>> getItems() {\n *         auto firstMap = co_await getFirstMap();\n *         for (auto&& [k, v] : firstMap) {\n *           co_yield {k, v};\n *         }\n *         auto secondMap = co_await getSecondMap();\n *         for (auto&& [k, v] : secondMap) {\n *           co_yield {k, v};\n *         }\n *      }\n *\n * This is mostly useful for generic algorithms that need to take copies of\n * elements of the sequence.\n *\n * Executor Affinity\n * -----------------\n * An AsyncGenerator coroutine has similar executor-affinity to that of the\n * folly::coro::Task coroutine type. Every time a consumer requests a new value\n * from the generator using 'co_await ++it' the generator inherits the caller's\n * current executor. The coroutine will ensure that it always resumes on the\n * associated executor when resuming from `co_await' expression until it hits\n * the next 'co_yield' or 'co_return' statement.\n * Note that the executor can potentially change at a 'co_yield' statement if\n * the next element of the sequence is requested from a consumer coroutine that\n * is associated with a different executor.\n *\n * Example: Writing an async generator.\n *\n *       folly::coro::AsyncGenerator<Record&&> getRecordsAsync() {\n *         auto resultSet = executeQuery(someQuery);\n *         for (;;) {\n *           auto resultSetPage = co_await resultSet.nextPage();\n *           if (resultSetPage.empty()) break;\n *           for (auto& row : resultSetPage) {\n *             co_yield Record{row.get(\"name\"), row.get(\"email\")};\n *           }\n *         }\n *  }\n *\n * Example: Consuming items from an async generator\n *\n *       folly::coro::Task<void> consumer() {\n *         auto records = getRecordsAsync();\n *         while (auto item = co_await records.next()) {\n *           auto&& record = *item;\n *           process(record);\n *         }\n *       }\n *\n * Async Cleanup\n * -------------\n * When the template parameter RequiresCleanup is true, the owner of an\n * AsyncGenerator is responsible for awaiting cleanup() before the generator\n * object's destructor is called. That allows to use folly::coro::co_scope_exit\n * awaitables inside AsyncGenerator, which are asynchronously executed when\n * cleanup() is awaited. Note that the AsyncGenerator coroutine frame is\n * destroyed before co_scope_exit awaitables are executed.\n *\n * There is an alias CleanableAsyncGenerator for AsyncGenerator with\n * RequiresCleanup set to true.\n *\n * Drain safety\n * ------------\n * One significant difference between AsyncGenerator and folly::coro::Task is\n * that AsyncGenerator may be destroyed between next() calls - i.e. destroyed\n * without being fully drained.\n *\n * For example:\n *\n *   AsyncGenerator<int> gen() {\n *     SCOPE_EXIT {\n *       LOG(INFO) << \"Step 4\";\n *     };\n *     LOG(INFO) << \"Step 1\";\n *     co_yield 41;\n *     SCOPE_EXIT {\n *       LOG(INFO) << \"Step 3\";\n *     };\n *     LOG(INFO) << \"Step 2\";\n *     co_yield 42;\n *     SCOPE_EXIT {\n *       LOG(INFO) << \"Never reached\";\n *     };\n *     LOG(INFO) << \"Never reached\";\n *     co_yield 43;\n *   }\n *\n *   {\n *     AsyncGenerator<int> g = gen();\n *     while (auto next = co_await g.next()) {\n *       LOG(INFO) << *next;\n *       if (*next == 42) {\n *         break;\n *         // ^^^ this may trigger generator destruction before it is drained.\n *       }\n *     }\n *   }\n *\n * This means that when writing an AsyncGenerator, you should always document\n * whether such AsyncGenerator requires draining before destruction (drain\n * unsafe). When possible you should always aim to make AsyncGenerator not\n * require draining before destruction (drain safe).\n *\n * If an AsyncGenerator is drain unsafe, always mention this in the\n * documentation and ideally include some assertions that help detect cases\n * where such AsyncGenerator is destroyed without being fully drained.\n *\n * Example:\n *\n *   AsyncGenerator<int> gen() {\n *     auto drainGuard = makeGuard([] { LOG(FATAL) << \"I shall be drained!\"; });\n *     co_yield 41;\n *     co_yield 42;\n *     co_yield 43;\n *     drainGuard.dismiss();\n *   }\n */\ntemplate <\n    typename Reference,\n    typename Value = remove_cvref_t<Reference>,\n    bool RequiresCleanup = false>\nclass [[nodiscard]] AsyncGenerator {\n  static_assert(\n      std::is_constructible<Value, Reference>::value,\n      \"AsyncGenerator 'value_type' must be constructible from a 'reference'.\");\n\n public:\n  using promise_type =\n      detail::AsyncGeneratorPromise<Reference, Value, RequiresCleanup>;\n  // Standard `AsyncGenerator` coros can easily capture references & other\n  // unsafe aliasing.\n  //\n  // Future: Implement a `coro/safe` generator wrapper, like\n  // `async_closure_gen`.\n  template <safe_alias>\n  using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;\n\n private:\n  using handle_t = coroutine_handle<promise_type>;\n\n public:\n  using value_type = Value;\n  using reference = Reference;\n  using pointer = std::add_pointer_t<Reference>;\n\n public:\n  AsyncGenerator() noexcept : coro_() {}\n\n  AsyncGenerator(AsyncGenerator&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  ~AsyncGenerator() {\n    if (coro_) {\n      if constexpr (RequiresCleanup) {\n        LOG(FATAL) << \"cleanup() hasn't been called!\";\n      }\n\n      coro_.destroy();\n    }\n  }\n\n  class CleanupSemiAwaitable;\n\n  class [[nodiscard]] CleanupAwaitable {\n   public:\n    struct Awaiter {\n      bool await_ready() noexcept { return !scopeExit_; }\n\n      template <typename Promise>\n      FOLLY_NOINLINE auto await_suspend(\n          coroutine_handle<Promise> continuation) noexcept {\n        asyncFrame_.setReturnAddress();\n        scopeExit_.promise().setContext(\n            continuation, &asyncFrame_, executor_.get_alias());\n        if constexpr (detail::promiseHasAsyncFrame_v<Promise>) {\n          folly::pushAsyncStackFrameCallerCallee(\n              continuation.promise().getAsyncFrame(), asyncFrame_);\n          return scopeExit_;\n        } else {\n          folly::resumeCoroutineWithNewAsyncStackRoot(scopeExit_);\n        }\n      }\n\n      void await_resume() noexcept {}\n\n      coroutine_handle<detail::ScopeExitTaskPromiseBase> scopeExit_;\n      folly::Executor::KeepAlive<> executor_;\n      folly::AsyncStackFrame asyncFrame_;\n    };\n\n    Awaiter operator co_await() && noexcept {\n      return Awaiter{scopeExit_, std::move(executor_), {}};\n    }\n\n   private:\n    friend CleanupSemiAwaitable;\n\n    CleanupAwaitable(\n        coroutine_handle<detail::ScopeExitTaskPromiseBase> scopeExit,\n        folly::Executor::KeepAlive<> executor) noexcept\n        : scopeExit_{scopeExit}, executor_{std::move(executor)} {}\n\n    friend CleanupAwaitable tag_invoke(\n        cpo_t<co_withAsyncStack>, CleanupAwaitable awaitable) noexcept {\n      return std::move(awaitable);\n    }\n\n    coroutine_handle<detail::ScopeExitTaskPromiseBase> scopeExit_;\n    folly::Executor::KeepAlive<> executor_;\n  };\n\n  class [[nodiscard]] CleanupSemiAwaitable {\n   public:\n    CleanupAwaitable viaIfAsync(Executor::KeepAlive<> executor) noexcept {\n      return CleanupAwaitable{scopeExit_, std::move(executor)};\n    }\n\n    template <safe_alias>\n    using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;\n\n   private:\n    friend AsyncGenerator;\n\n    explicit CleanupSemiAwaitable(\n        coroutine_handle<detail::ScopeExitTaskPromiseBase> scopeExit) noexcept\n        : scopeExit_{scopeExit} {}\n\n    coroutine_handle<detail::ScopeExitTaskPromiseBase> scopeExit_;\n  };\n\n  CleanupSemiAwaitable cleanup() && {\n    static_assert(RequiresCleanup);\n    if (coro_) {\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return CleanupSemiAwaitable{coro_.promise().scopeExit_};\n    } else {\n      return CleanupSemiAwaitable{{}};\n    }\n  }\n\n  AsyncGenerator& operator=(AsyncGenerator&& other) noexcept {\n    auto oldCoro = std::exchange(coro_, std::exchange(other.coro_, {}));\n    if (oldCoro) {\n      CHECK(!RequiresCleanup) << \"cleanup() hasn't been called!\";\n      oldCoro.destroy();\n    }\n    return *this;\n  }\n\n  void swap(AsyncGenerator& other) noexcept { std::swap(coro_, other.coro_); }\n\n  class NextAwaitable;\n  class NextSemiAwaitable;\n\n  class NextResult {\n   public:\n    NextResult() noexcept : hasValue_(false) {}\n\n    NextResult(NextResult&& other) noexcept : hasValue_(other.hasValue_) {\n      if (hasValue_) {\n        value_.construct(std::move(other.value_).get());\n      }\n    }\n\n    ~NextResult() {\n      if (hasValue_) {\n        value_.destruct();\n      }\n    }\n\n    NextResult& operator=(NextResult&& other) {\n      if (&other != this) {\n        if (has_value()) {\n          hasValue_ = false;\n          value_.destruct();\n        }\n\n        if (other.has_value()) {\n          value_.construct(std::move(other.value_).get());\n          hasValue_ = true;\n        }\n      }\n      return *this;\n    }\n\n    bool has_value() const noexcept { return hasValue_; }\n\n    explicit operator bool() const noexcept { return has_value(); }\n\n    decltype(auto) value() & {\n      DCHECK(has_value());\n      return value_.get();\n    }\n\n    decltype(auto) value() && {\n      DCHECK(has_value());\n      return std::move(value_).get();\n    }\n\n    decltype(auto) value() const& {\n      DCHECK(has_value());\n      return value_.get();\n    }\n\n    decltype(auto) value() const&& {\n      DCHECK(has_value());\n      return std::move(value_).get();\n    }\n\n    decltype(auto) operator*() & { return value(); }\n\n    decltype(auto) operator*() && { return std::move(*this).value(); }\n\n    decltype(auto) operator*() const& { return value(); }\n\n    decltype(auto) operator*() const&& { return std::move(*this).value(); }\n\n    decltype(auto) operator->() {\n      DCHECK(has_value());\n      auto&& x = value_.get();\n      return std::addressof(x);\n    }\n\n    decltype(auto) operator->() const {\n      DCHECK(has_value());\n      auto&& x = value_.get();\n      return std::addressof(x);\n    }\n\n   private:\n    friend NextAwaitable;\n    explicit NextResult(handle_t coro) noexcept : hasValue_(true) {\n      value_.construct(coro.promise().getRvalue());\n    }\n\n    detail::ManualLifetime<Reference> value_;\n    bool hasValue_ = false;\n  };\n\n  class NextAwaitable {\n   public:\n    bool await_ready() noexcept { return !coro_; }\n\n    template <typename Promise>\n    FOLLY_NOINLINE auto await_suspend(\n        coroutine_handle<Promise> continuation) noexcept {\n      auto& promise = coro_.promise();\n\n      promise.setContinuation(continuation);\n      promise.clearValue();\n\n      auto& asyncFrame = promise.getAsyncFrame();\n      asyncFrame.setReturnAddress();\n\n      if constexpr (detail::promiseHasAsyncFrame_v<Promise>) {\n        folly::pushAsyncStackFrameCallerCallee(\n            continuation.promise().getAsyncFrame(), asyncFrame);\n        return coro_;\n      } else {\n        folly::resumeCoroutineWithNewAsyncStackRoot(coro_);\n      }\n    }\n\n    NextResult await_resume() {\n      if (!coro_) {\n        return NextResult{};\n      } else if (!coro_.promise().hasValue()) {\n        coro_.promise().throwIfException();\n        return NextResult{};\n      } else {\n        return NextResult{coro_};\n      }\n    }\n\n    folly::Try<NextResult> await_resume_try() noexcept {\n      if (coro_) {\n        if (coro_.promise().hasValue()) {\n          return folly::Try<NextResult>(NextResult{coro_});\n        } else if (coro_.promise().hasException()) {\n          return folly::Try<NextResult>(\n              std::move(coro_.promise().getException()));\n        }\n      }\n      return folly::Try<NextResult>(NextResult{});\n    }\n\n   private:\n    friend NextSemiAwaitable;\n    explicit NextAwaitable(handle_t coro) noexcept : coro_(coro) {}\n\n    friend NextAwaitable tag_invoke(\n        cpo_t<co_withAsyncStack>, NextAwaitable awaitable) noexcept {\n      return NextAwaitable{awaitable.coro_};\n    }\n\n    handle_t coro_;\n  };\n\n  class NextSemiAwaitable {\n   public:\n    NextAwaitable viaIfAsync(Executor::KeepAlive<> executor) noexcept {\n      if (coro_) {\n        coro_.promise().setExecutor(std::move(executor));\n      }\n      return NextAwaitable{coro_};\n    }\n\n    friend NextSemiAwaitable co_withCancellation(\n        CancellationToken cancelToken, NextSemiAwaitable&& awaitable) {\n      if (awaitable.coro_) {\n        awaitable.coro_.promise().setCancellationToken(std::move(cancelToken));\n      }\n      return NextSemiAwaitable{std::exchange(awaitable.coro_, {})};\n    }\n\n    template <safe_alias>\n    using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;\n\n   private:\n    friend AsyncGenerator;\n\n    explicit NextSemiAwaitable(handle_t coro) noexcept : coro_(coro) {}\n\n    handle_t coro_;\n  };\n\n  NextSemiAwaitable next() noexcept {\n    DCHECK(!coro_ || !coro_.done());\n    return NextSemiAwaitable{coro_};\n  }\n\n  template <typename F, typename... A, typename F_, typename... A_>\n  friend AsyncGenerator tag_invoke(\n      tag_t<co_invoke_fn>, tag_t<AsyncGenerator, F, A...>, F_ f, A_... a) {\n    if constexpr (RequiresCleanup) {\n      auto&& [fScoped, r] = co_await co_scope_exit(\n          [](auto&&, auto&& gen) { return std::move(gen).cleanup(); },\n          static_cast<F&&>(f),\n          AsyncGenerator{});\n      r = invoke(static_cast<F&&>(fScoped), static_cast<A&&>(a)...);\n      while (true) {\n        co_yield co_result(co_await co_awaitTry(r.next()));\n      }\n    } else {\n      auto r = invoke(static_cast<F&&>(f), static_cast<A&&>(a)...);\n      while (true) {\n        co_yield co_result(co_await co_awaitTry(r.next()));\n      }\n    }\n  }\n\n private:\n  friend promise_type;\n\n  explicit AsyncGenerator(coroutine_handle<promise_type> coro) noexcept\n      : coro_(coro) {}\n\n  coroutine_handle<promise_type> coro_;\n};\n\ntemplate <typename Reference, typename Value = remove_cvref_t<Reference>>\nusing CleanableAsyncGenerator =\n    AsyncGenerator<Reference, Value, true /* RequiresCleanup */>;\n\nnamespace detail {\n\ntemplate <bool RequiresCleanup>\nstruct BaseAsyncGeneratorPromise {};\n\ntemplate <>\nstruct BaseAsyncGeneratorPromise<true> {\n  coroutine_handle<ScopeExitTaskPromiseBase> scopeExit_;\n};\n\nenum class AsyncGeneratorPromiseState : std::uint8_t {\n  INVALID,\n  VALUE,\n  EXCEPTION_WRAPPER,\n  DONE,\n};\n\ntemplate <\n    typename Reference,\n    typename Value,\n    bool RequiresCleanup /* = false, in BasePromise.h */>\nclass AsyncGeneratorPromise final\n    : public ExtendedCoroutinePromiseCrtp<\n          AsyncGeneratorPromise<Reference, Value, RequiresCleanup>>,\n      BaseAsyncGeneratorPromise<RequiresCleanup>,\n      public BasePromise<AsyncGeneratorPromiseState> {\n  class YieldAwaiter {\n   public:\n    bool await_ready() noexcept { return false; }\n    coroutine_handle<> await_suspend_promise(\n        AsyncGeneratorPromise& promise) noexcept {\n      // Pop AsyncStackFrame first as clearContext() clears the frame state.\n      folly::popAsyncStackFrameCallee(promise.getAsyncFrame());\n      promise.clearContext();\n      if (promise.hasException()) {\n        auto [handle, frame] =\n            promise.continuation_.getErrorHandle(promise.getException());\n        return handle.getHandle();\n      }\n      return promise.continuation_.getHandle();\n    }\n    coroutine_handle<> await_suspend(\n        coroutine_handle<AsyncGeneratorPromise> h) noexcept {\n      return await_suspend_promise(h.promise());\n    }\n    void await_resume() noexcept {}\n  };\n\n  using State = AsyncGeneratorPromiseState;\n  State state() const { return tailStorage_; }\n  void setState(State s) { tailStorage_ = s; }\n\n public:\n  template <typename... Args>\n  AsyncGeneratorPromise(Args&... args) {\n    setState(State::INVALID);\n    if constexpr (RequiresCleanup) {\n      scheduleAutoCleanupIfNeeded(\n          coroutine_handle<AsyncGeneratorPromise>::from_promise(*this),\n          args...);\n    }\n  }\n\n  ~AsyncGeneratorPromise() {\n    switch (state()) {\n      case State::VALUE:\n        folly::coro::detail::deactivate(value_);\n        break;\n      case State::EXCEPTION_WRAPPER:\n        folly::coro::detail::deactivate(exceptionWrapper_);\n        break;\n      case State::DONE:\n      case State::INVALID:\n        break;\n    }\n  }\n\n  static void* operator new(std::size_t size) {\n    return ::folly_coro_async_malloc(size);\n  }\n\n  static void operator delete(void* ptr, std::size_t size) {\n    ::folly_coro_async_free(ptr, size);\n  }\n\n  AsyncGenerator<Reference, Value, RequiresCleanup>\n  get_return_object() noexcept {\n    return AsyncGenerator<Reference, Value, RequiresCleanup>{\n        coroutine_handle<AsyncGeneratorPromise>::from_promise(*this)};\n  }\n\n  suspend_always initial_suspend() noexcept { return {}; }\n\n  YieldAwaiter final_suspend() noexcept {\n    DCHECK(!hasValue());\n    return {};\n  }\n\n  YieldAwaiter yield_value(Reference&& value) noexcept(\n      std::is_nothrow_move_constructible<Reference>::value) {\n    DCHECK(state() == State::INVALID);\n    folly::coro::detail::activate(value_, static_cast<Reference&&>(value));\n    setState(State::VALUE);\n    return YieldAwaiter{};\n  }\n\n  /// In the case where 'Reference' is not actually a reference-type we\n  /// allow implicit conversion from the co_yield argument to Reference.\n  /// However, we don't want to allow this for cases where 'Reference' _is_\n  /// a reference because this could result in the reference binding to a\n  /// temporary that results from an implicit conversion.\n  template <\n      typename U,\n      std::enable_if_t<\n          !std::is_reference_v<Reference> &&\n              std::is_convertible_v<U&&, Reference>,\n          int> = 0>\n  YieldAwaiter yield_value(U&& value) noexcept(\n      std::is_nothrow_constructible_v<Reference, U>) {\n    DCHECK(state() == State::INVALID);\n    folly::coro::detail::activate(value_, static_cast<U&&>(value));\n    setState(State::VALUE);\n    return {};\n  }\n\n  YieldAwaiter yield_value(co_error&& error) noexcept {\n    DCHECK(state() == State::INVALID);\n    folly::coro::detail::activate(\n        exceptionWrapper_, std::move(error.exception()));\n    setState(State::EXCEPTION_WRAPPER);\n    return {};\n  }\n\n  YieldAwaiter yield_value(co_result<Value>&& res) noexcept {\n    if (res.result().hasValue()) {\n      return yield_value(std::move(res.result().value()));\n    } else if (res.result().hasException()) {\n      return yield_value(co_error(res.result().exception()));\n    } else {\n      return_void();\n      return {};\n    }\n  }\n\n  YieldAwaiter yield_value(\n      co_result<typename AsyncGenerator<Reference, Value, RequiresCleanup>::\n                    NextResult>&& res) noexcept {\n    DCHECK(\n        res.result().hasValue() ||\n        (res.result().hasException() && res.result().exception()));\n    if (res.result().hasException()) {\n      return yield_value(co_error(res.result().exception()));\n    } else if (res.result().hasValue()) {\n      if (res.result()->has_value()) {\n        return yield_value(std::move(res.result()->value()));\n      } else {\n        return_void();\n        return {};\n      }\n    }\n    return yield_value(co_error(UsingUninitializedTry{}));\n  }\n\n  using BasePromise<AsyncGeneratorPromiseState>::await_transform;\n\n  variant_awaitable<YieldAwaiter, ready_awaitable<>> await_transform(\n      co_safe_point_t) noexcept {\n    return do_safe_point<YieldAwaiter>(*this);\n  }\n\n  void unhandled_exception() noexcept {\n    DCHECK(state() == State::INVALID);\n    folly::coro::detail::activate(exceptionWrapper_, current_exception());\n    setState(State::EXCEPTION_WRAPPER);\n  }\n\n  void return_void() noexcept {\n    DCHECK(state() == State::INVALID);\n    setState(State::DONE);\n  }\n\n  void setExecutor(folly::Executor::KeepAlive<> executor) noexcept {\n    DCHECK(executor);\n    executor_ = std::move(executor);\n  }\n\n  void setContinuation(ExtendedCoroutineHandle continuation) noexcept {\n    continuation_ = continuation;\n  }\n\n  bool hasException() const noexcept {\n    return state() == State::EXCEPTION_WRAPPER;\n  }\n\n  folly::exception_wrapper& getException() noexcept {\n    DCHECK(hasException());\n    return exceptionWrapper_.get();\n  }\n\n  void throwIfException() {\n    if (state() == State::EXCEPTION_WRAPPER) {\n      exceptionWrapper_.get().throw_exception();\n    }\n  }\n\n  decltype(auto) getRvalue() noexcept {\n    DCHECK(hasValue());\n    return std::move(value_).get();\n  }\n\n  void clearValue() noexcept {\n    if (hasValue()) {\n      setState(State::INVALID);\n      folly::coro::detail::deactivate(value_);\n    } else {\n      CHECK(state() != State::DONE)\n          << \"Using generator after receiving completion.\";\n      CHECK(state() != State::EXCEPTION_WRAPPER)\n          << \"Using generator after receiving exception.\";\n    }\n  }\n\n  bool hasValue() const noexcept { return state() == State::VALUE; }\n\n  // Unlike `getErrorHandleUncheckedImpl`, checks the type of `me`.\n  static std::optional<ExtendedCoroutineHandle::ErrorHandle> getErrorHandleImpl(\n      AsyncGeneratorPromise& me, exception_wrapper& ex) {\n    return getErrorHandleUncheckedImpl(me, ex);\n  }\n\n private:\n  friend AsyncGenerator<Reference, Value, RequiresCleanup>;\n\n  void clearContext() noexcept {\n    executor_ = {};\n    cancelToken_ = {};\n    hasCancelTokenOverride_ = false;\n    asyncFrame_.clear();\n  }\n\n  friend coroutine_handle<ScopeExitTaskPromiseBase> tag_invoke(\n      cpo_t<co_attachScopeExit>,\n      AsyncGeneratorPromise& p,\n      coroutine_handle<ScopeExitTaskPromiseBase> scopeExit) noexcept {\n    static_assert(\n        RequiresCleanup,\n        \"Only CleanableAsyncGenerator (AsyncGenerator with RequiresCleanup\"\n        \" template parameter set to true) supports attaching co_scope_exit\");\n    return std::exchange(p.scopeExit_, scopeExit);\n  }\n\n  // From the base: continuation_, asyncFrame_, executor_, cancelToken_,\n  // hasCancelTokenOverride_, tailStorage_ == state() / setState()\n  union {\n    ManualLifetime<folly::exception_wrapper> exceptionWrapper_;\n    ManualLifetime<Reference> value_;\n  };\n};\n\n} // namespace detail\n\ntemplate <typename Reference, typename Value>\nauto tag_invoke(\n    cpo_t<co_cleanup>, CleanableAsyncGenerator<Reference, Value>&& gen) {\n  return std::move(gen).cleanup();\n}\n\n} // namespace coro\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/coro/AsyncPipe.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Try.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/SmallUnboundedQueue.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/fibers/Semaphore.h>\n\n#include <memory>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// An AsyncGenerator with a write end\n//\n// Usage:\n//   auto pipe = AsyncPipe<T>::create();\n//   pipe.second.write(std::move(val1));\n//   auto val2 = co_await pipe.first.next();\n//\n//  write() returns false if the read end has been destroyed (unless\n//  SingleProducer is disabled, in which case this behavior is undefined).\n//  The generator is completed when the write end is destroyed or on close()\n//  close() can also be passed an exception, which is thrown when read.\n//\n//  An optional onClosed callback can be passed to create(). This callback will\n//  be called either when the generator is destroyed by the consumer, or when\n//  the pipe is closed by the publisher (whichever comes first). The onClosed\n//  callback may destroy the AsyncPipe object inline, and must not call close()\n//  on the AsyncPipe object inline. If an onClosed callback is specified and the\n//  publisher would like to destroy the pipe outside of the callback, it must\n//  first close the pipe.\n//\n//  If SingleProducer is disabled, AsyncPipe's write() method (but not its\n//  close() method) becomes thread-safe. close() must be sequenced after all\n//  write()s in this mode.\n\ntemplate <\n    typename T,\n    bool SingleProducer = true,\n    template <typename, bool, bool> typename QueueType = SmallUnboundedQueue>\nclass AsyncPipe {\n public:\n  ~AsyncPipe() {\n    CHECK(!onClosed_ || onClosed_->wasInvokeRequested())\n        << \"If an onClosed callback is specified and the generator still \"\n        << \"exists, the publisher must explicitly close the pipe prior to \"\n        << \"destruction.\";\n    std::move(*this).close();\n  }\n\n  AsyncPipe(AsyncPipe&& pipe) noexcept {\n    queue_ = std::move(pipe.queue_);\n    onClosed_ = std::move(pipe.onClosed_);\n  }\n\n  AsyncPipe& operator=(AsyncPipe&& pipe) {\n    if (this != &pipe) {\n      CHECK(!onClosed_ || onClosed_->wasInvokeRequested())\n          << \"If an onClosed callback is specified and the generator still \"\n          << \"exists, the publisher must explicitly close the pipe prior to \"\n          << \"destruction.\";\n      std::move(*this).close();\n      queue_ = std::move(pipe.queue_);\n      onClosed_ = std::move(pipe.onClosed_);\n    }\n    return *this;\n  }\n\n  static std::pair<folly::coro::AsyncGenerator<T&&>, AsyncPipe> create(\n      folly::Function<void()> onClosed = nullptr) {\n    auto queue = std::make_shared<Queue>();\n    auto cancellationSource = std::optional<folly::CancellationSource>();\n    auto onClosedCallback = std::unique_ptr<OnClosedCallback>();\n    if (onClosed != nullptr) {\n      cancellationSource.emplace();\n      onClosedCallback = std::make_unique<OnClosedCallback>(\n          *cancellationSource, std::move(onClosed));\n    }\n    auto guard =\n        folly::makeGuard([cancellationSource = std::move(cancellationSource)] {\n          if (cancellationSource) {\n            cancellationSource->requestCancellation();\n          }\n        });\n    return {\n        folly::coro::co_invoke(\n            [queue,\n             guard = std::move(guard)]() -> folly::coro::AsyncGenerator<T&&> {\n              while (true) {\n                co_yield co_result(co_await co_nothrow(queue->dequeue()));\n              }\n            }),\n        AsyncPipe(queue, std::move(onClosedCallback))};\n  }\n\n  template <typename U = T>\n  bool write(U&& val) {\n    if (auto queue = queue_.lock()) {\n      queue->enqueue(folly::Try<T>(std::forward<U>(val)));\n      return true;\n    }\n    return false;\n  }\n\n  void close(folly::exception_wrapper ew) && {\n    if (auto queue = queue_.lock()) {\n      queue->enqueue(folly::Try<T>(std::move(ew)));\n      queue_.reset();\n    }\n    if (onClosed_ != nullptr) {\n      onClosed_->requestInvoke();\n      onClosed_.reset();\n    }\n  }\n\n  void close() && {\n    if (auto queue = queue_.lock()) {\n      queue->enqueue(folly::Try<T>());\n      queue_.reset();\n    }\n    if (onClosed_ != nullptr) {\n      onClosed_->requestInvoke();\n      onClosed_.reset();\n    }\n  }\n\n  bool isClosed() const { return queue_.expired(); }\n\n private:\n  using Queue = QueueType<folly::Try<T>, SingleProducer, true>;\n\n  class OnClosedCallback {\n   public:\n    OnClosedCallback(\n        folly::CancellationSource cancellationSource,\n        folly::Function<void()> onClosedFunc)\n        : cancellationSource_(std::move(cancellationSource)),\n          cancellationCallback_(\n              cancellationSource_.getToken(), std::move(onClosedFunc)) {}\n\n    void requestInvoke() { cancellationSource_.requestCancellation(); }\n\n    bool wasInvokeRequested() {\n      return cancellationSource_.isCancellationRequested();\n    }\n\n   private:\n    folly::CancellationSource cancellationSource_;\n    folly::CancellationCallback cancellationCallback_;\n  };\n\n  explicit AsyncPipe(\n      std::weak_ptr<Queue> queue, std::unique_ptr<OnClosedCallback> onClosed)\n      : queue_(std::move(queue)), onClosed_(std::move(onClosed)) {}\n\n  std::weak_ptr<Queue> queue_;\n  std::unique_ptr<OnClosedCallback> onClosed_;\n};\n\n// Bounded variant of AsyncPipe which buffers a fixed number of writes\n// before blocking new attempts to write until the buffer is drained.\n//\n// Usage:\n//   auto [generator, pipe] = BoundedAsyncPipe<T>::create(/* tokens */ 10);\n//   co_await pipe.write(std::move(entry));\n//   auto entry = co_await generator.next().value();\n//\n// write() is a coroutine which only blocks when\n// no capacity is remaining. write() returns false if the read-end has been\n// destroyed or was destroyed while blocking, only throwing OperationCanceled\n// if the parent coroutine was canceled while blocking.\n//\n// try_write() is offered which will never block, but will return false\n// and not write if no capacity is remaining or the read end is already\n// destroyed.\n//\n// close() functions the same as AsyncPipe, and must be invoked before\n// destruction if an onClose callback is attached.\ntemplate <\n    typename T,\n    bool SingleProducer = true,\n    template <typename, bool, bool> typename QueueType = SmallUnboundedQueue>\nclass BoundedAsyncPipe {\n public:\n  using Pipe = AsyncPipe<T, SingleProducer, QueueType>;\n\n  static std::pair<AsyncGenerator<T&&>, BoundedAsyncPipe> create(\n      size_t tokens, folly::Function<void()> onClosed = nullptr) {\n    auto [generator, pipe] = Pipe::create(std::move(onClosed));\n\n    auto semaphore = std::make_shared<folly::fibers::Semaphore>(tokens);\n\n    folly::CancellationSource cancellationSource;\n    auto cancellationToken = cancellationSource.getToken();\n    auto guard = folly::makeGuard(\n        [cancellationSource = std::move(cancellationSource)]() {\n          cancellationSource.requestCancellation();\n        });\n\n    auto signalingGenerator = co_invoke(\n        [generator_2 = std::move(generator),\n         guard = std::move(guard),\n         semaphore]() mutable -> folly::coro::AsyncGenerator<T&&> {\n          while (true) {\n            auto itemTry = co_await co_awaitTry(generator_2.next());\n            semaphore->signal();\n            co_yield co_result(std::move(itemTry));\n          }\n        });\n    return std::pair<AsyncGenerator<T&&>, BoundedAsyncPipe>(\n        std::move(signalingGenerator),\n        BoundedAsyncPipe(\n            std::move(pipe),\n            std::move(semaphore),\n            std::move(cancellationToken)));\n  }\n\n  template <typename U = T>\n  folly::coro::Task<bool> write(U&& u) {\n    auto parentToken = co_await co_current_cancellation_token;\n\n    auto waitResult = co_await co_awaitTry(co_withCancellation(\n        folly::cancellation_token_merge(\n            std::move(parentToken), cancellationToken_),\n        semaphore_->co_wait()));\n    if (cancellationToken_.isCancellationRequested()) {\n      // eagerly return false if the read-end was destroyed instead of throwing\n      // OperationCanceled, to have uniform behavior when the generator is\n      // destroyed\n      co_return false;\n    } else if (waitResult.hasException()) {\n      co_yield co_error(std::move(waitResult).exception());\n    }\n\n    co_return pipe_.write(std::forward<U>(u));\n  }\n\n  template <typename U = T>\n  bool try_write(U&& u) {\n    bool available = semaphore_->try_wait();\n    if (!available) {\n      return false;\n    }\n    return pipe_.write(std::forward<U>(u));\n  }\n\n  size_t getAvailableSpace() { return semaphore_->getAvailableTokens(); }\n\n  size_t getOccupiedSpace() {\n    return semaphore_->getCapacity() - getAvailableSpace();\n  }\n\n  void close(exception_wrapper&& w) && { std::move(pipe_).close(std::move(w)); }\n  void close() && { std::move(pipe_).close(); }\n\n  bool isClosed() const { return pipe_.isClosed(); }\n\n private:\n  BoundedAsyncPipe(\n      Pipe&& pipe,\n      std::shared_ptr<folly::fibers::Semaphore> semaphore,\n      folly::CancellationToken cancellationToken)\n      : pipe_(std::move(pipe)),\n        semaphore_(std::move(semaphore)),\n        cancellationToken_(std::move(cancellationToken)) {}\n\n  Pipe pipe_;\n  std::shared_ptr<folly::fibers::Semaphore> semaphore_;\n  folly::CancellationToken cancellationToken_;\n};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/AsyncScope.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_asyncscope\n//\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/ExceptionWrapper.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/detail/Barrier.h>\n#include <folly/coro/detail/BarrierTask.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/SourceLocation.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\n#include <glog/logging.h>\n\n#include <atomic>\n#include <cassert>\n#include <optional>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n/**\n * The AsyncScope class is used to allow you to start a dynamic, unbounded\n * number of tasks, which can all run concurrently, and then later wait for\n * completion of all of the tasks.\n *\n * Tasks added to an AsyncScope must have a void or folly::Unit result-type\n * and must handle any errors prior to completing.\n *\n * @refcode folly/docs/examples/folly/coro/AsyncScope.cpp\n * @class folly::coro::AsyncScope\n */\n//\n// Example:\n//    folly::coro::Task<void> process(Event event) {\n//      try {\n//        co_await do_processing(event.data);\n//      } catch (...) {\n//        LOG(ERROR) << \"Processing event failed\";\n//      }\n//    }\n//\n//    folly::coro::AsyncScope scope;\n//    scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), process(ev1)));\n//    scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), process(ev2)));\n//    scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), process(ev3)));\n//    co_await scope.joinAsync();\n//\nclass AsyncScope {\n public:\n  AsyncScope() noexcept;\n\n  // @param throwOnJoin If true, will throw last unhandled exception (if any)\n  //                    on joinAsync. Default behavior is to ignore the\n  //                    exception in opt builds and FATAL in dev builds\n  explicit AsyncScope(bool throwOnJoin) noexcept;\n\n  // Destroy the AsyncScope object.\n  //\n  // NOTE: If you have called add() on this scope then you _must_\n  // call either cleanup() or joinAsync() and wait until the that operation\n  // completes before calling the destructor.\n  ~AsyncScope();\n\n  /**\n   * Query the number of tasks added to the scope that have not yet completed.\n   */\n  std::size_t remaining() const noexcept;\n\n  /**\n   * Start the specified task/awaitable by co_awaiting it.\n   *\n   * Exceptions\n   * ----------\n   * IMPORTANT: Tasks submitted to the AsyncScope by calling .add() must\n   * ensure they do not complete with an exception. Exceptions propagating\n   * from the 'co_await awaitable' expression are logged using DFATAL.\n   *\n   * To avoid this occurring you should make sure to catch and handle any\n   * exceptions within the task being started here.\n   *\n   * Interaction with cleanup/joinAsync\n   * ----------------------------------\n   * It is invalid to call add() once the joinAsync() or cleanup()\n   * operations have completed.\n   *\n   * This generally means that it is unsafe to call .add() once cleanup()\n   * has started as it may be racing with completion of cleanup().\n   *\n   * The exception to this rule is for cases where you know you are running\n   * within a task that has been started with .add() and thus you know that\n   * cleanup() will not yet have completed.\n   *\n   * Passing folly::coro::Task\n   * -------------------------\n   * NOTE: You cannot pass a folly::coro::Task to this method.\n   * You must first call co_withExecutor() to specify which executor the task\n   * should run on.\n   */\n  // returnAddress customize entry point to async stack (useful if this is\n  // called from async code already). If not set will default to\n  // FOLLY_ASYNC_STACK_RETURN_ADDRESS()\n  template <typename Awaitable>\n  void add(Awaitable&& awaitable, void* returnAddress = nullptr);\n\n  template <typename Awaitable>\n  void addWithSourceLoc(\n      Awaitable&& awaitable,\n      void* returnAddress = nullptr,\n      source_location sourceLocation = source_location::current());\n\n  /**\n   * Asynchronously wait for all started tasks to complete.\n   *\n   * Either call this method _or_ cleanup() to join the work.\n   * It is invalid to call both of them.\n   */\n  Task<void> joinAsync() noexcept;\n\n  /**\n   * Asynchronously cleanup all started tasks.\n   *\n   * If you have previously called add() then you must call cleanup()\n   * and wait for the returned future to complete before the AsyncScope\n   * object destructs.\n   */\n  SemiFuture<Unit> cleanup() noexcept;\n\n private:\n  template <typename Awaitable>\n  static detail::DetachedBarrierTask addImpl(\n      Awaitable awaitable,\n      bool throwOnJoin,\n      folly::exception_wrapper& maybeException,\n      std::optional<source_location> source,\n      std::atomic<bool>& exceptionRaised) {\n    static_assert(\n        std::is_void_v<await_result_t<Awaitable>> ||\n            std::is_same_v<await_result_t<Awaitable>, folly::Unit>,\n        \"Result of the task would be discarded. Make sure task result is either void or folly::Unit.\");\n\n    exception_wrapper exn;\n    try {\n      if constexpr (detail::is_awaitable_try<Awaitable>) {\n        auto ret = co_await co_awaitTry(std::move(awaitable));\n        if (ret.hasException()) {\n          exn = std::move(ret.exception());\n        }\n      } else {\n        co_await std::move(awaitable);\n      }\n    } catch (...) {\n      // not-awaitable-try awaitables and not-noexcept-copy-constructible values\n      exn = exception_wrapper(std::current_exception());\n    }\n    if (exn && !exn.get_exception<OperationCancelled>()) {\n      if (throwOnJoin) {\n        LOG(ERROR)\n            << (source.has_value() ? sourceLocationToString(source.value())\n                                   : \"\")\n            << \"Unhandled exception thrown from task added to AsyncScope: \"\n            << exn;\n        if (!exceptionRaised.exchange(true)) {\n          maybeException = std::move(exn);\n        }\n      } else {\n        LOG(DFATAL)\n            << (source.has_value() ? sourceLocationToString(source.value())\n                                   : \"\")\n            << \"Unhandled exception thrown from task added to AsyncScope: \"\n            << exn;\n      }\n    }\n  }\n\n  detail::Barrier barrier_{1};\n  relaxed_atomic<bool> anyTasksStarted_{false};\n  relaxed_atomic<bool> joinStarted_{false};\n  relaxed_atomic<bool> joined_{false};\n  bool throwOnJoin_{false};\n  folly::exception_wrapper maybeException_;\n  std::atomic<bool> exceptionRaised_{false};\n};\n\ninline AsyncScope::AsyncScope() noexcept : throwOnJoin_(false) {}\n\ninline AsyncScope::AsyncScope(bool throwOnJoin) noexcept\n    : throwOnJoin_(throwOnJoin) {}\n\ninline AsyncScope::~AsyncScope() {\n  CHECK(!anyTasksStarted_ || joined_)\n      << \"AsyncScope::cleanup() not yet complete\";\n}\n\ninline std::size_t AsyncScope::remaining() const noexcept {\n  const std::size_t count = barrier_.remaining();\n  return joinStarted_ ? count : (count > 1 ? count - 1 : 0);\n}\n\ntemplate <typename Awaitable>\nFOLLY_NOINLINE inline void AsyncScope::add(\n    Awaitable&& awaitable, void* returnAddress) {\n  CHECK(!joined_)\n      << \"It is invalid to add() more work after work has been joined\";\n  anyTasksStarted_ = true;\n  addImpl(\n      static_cast<Awaitable&&>(awaitable),\n      throwOnJoin_,\n      maybeException_,\n      std::nullopt,\n      exceptionRaised_)\n      .start(\n          &barrier_,\n          returnAddress ? returnAddress : FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n}\n\ntemplate <typename Awaitable>\nFOLLY_NOINLINE inline void AsyncScope::addWithSourceLoc(\n    Awaitable&& awaitable,\n    void* returnAddress,\n    source_location sourceLocation) {\n  CHECK(!joined_)\n      << \"It is invalid to add() more work after work has been joined\";\n  anyTasksStarted_ = true;\n  addImpl(\n      static_cast<Awaitable&&>(awaitable),\n      throwOnJoin_,\n      maybeException_,\n      sourceLocation,\n      exceptionRaised_)\n      .start(\n          &barrier_,\n          returnAddress ? returnAddress : FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n}\n\ninline Task<void> AsyncScope::joinAsync() noexcept {\n  assert(!joinStarted_ && \"It is invalid to join a scope multiple times\");\n  joinStarted_ = true;\n  co_await barrier_.arriveAndWait();\n  joined_ = true;\n  if (maybeException_) {\n    co_yield co_error{std::move(maybeException_)};\n  }\n}\n\ninline folly::SemiFuture<folly::Unit> AsyncScope::cleanup() noexcept {\n  return joinAsync().semi();\n}\n\n/**\n * A cancellable version of AsyncScope. Work added to this scope will be\n * provided a cancellation token for cancelling during join.\n *\n * See add() and cancelAndJoinAsync() for more information.\n *\n * Note: Task and AsyncGenerator will ignore the internal cancellation\n * signal if they already have a cancellation token (i.e. if someone has already\n * called co_withCancellation on them.)\n * If you need an external cancellation signal as well, pass that token to this\n * constructor or to add() instead of attaching it to the Awaitable.\n *\n * @refcode\n * folly/docs/examples/folly/coro/CancellableAsyncScope.cpp\n * @class folly::coro::CancellableAsyncScope\n */\nclass CancellableAsyncScope {\n public:\n  CancellableAsyncScope() noexcept\n      : cancellationToken_(cancellationSource_.getToken()) {}\n  explicit CancellableAsyncScope(bool throwOnJoin) noexcept\n      : cancellationToken_(cancellationSource_.getToken()),\n        scope_(throwOnJoin) {}\n  explicit CancellableAsyncScope(CancellationToken&& token)\n      : cancellationToken_(cancellation_token_merge(\n            cancellationSource_.getToken(), std::move(token))) {}\n  CancellableAsyncScope(CancellationToken&& token, bool throwOnJoin)\n      : cancellationToken_(cancellation_token_merge(\n            cancellationSource_.getToken(), std::move(token))),\n        scope_(throwOnJoin) {}\n\n  /**\n   * Query the number of tasks added to the scope that have not yet completed.\n   */\n  std::size_t remaining() const noexcept { return scope_.remaining(); }\n\n  /**\n   * Start the specified task/awaitable by co_awaiting it. The awaitable will be\n   * provided a cancellation token to respond to cancelAndJoinAsync() in the\n   * future.\n   *\n   * An additional cancellation token may be passed in to apply to the\n   * awaitable; it will be merged with the internal token.\n   *\n   * Note that cancellation is cooperative, your task must handle cancellation\n   * in order to have any effect.\n   *\n   * See the documentation on AsyncScope::add.\n   */\n  template <typename Awaitable>\n  FOLLY_NOINLINE void add(\n      Awaitable&& awaitable,\n      std::optional<CancellationToken> token = std::nullopt,\n      void* returnAddress = nullptr) {\n    scope_.add(\n        co_withCancellation(\n            token ? cancellation_token_merge(*token, cancellationToken_)\n                  : cancellationToken_,\n            static_cast<Awaitable&&>(awaitable)),\n        returnAddress ? returnAddress : FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  }\n\n  template <typename Awaitable>\n  FOLLY_NOINLINE void addWithSourceLoc(\n      Awaitable&& awaitable,\n      std::optional<CancellationToken> token,\n      void* returnAddress = nullptr,\n      source_location sourceLocation = source_location::current()) {\n    scope_.addWithSourceLoc(\n        co_withCancellation(\n            token ? cancellation_token_merge(*token, cancellationToken_)\n                  : cancellationToken_,\n            static_cast<Awaitable&&>(awaitable)),\n        returnAddress ? returnAddress : FOLLY_ASYNC_STACK_RETURN_ADDRESS(),\n        sourceLocation);\n  }\n\n  template <typename Awaitable>\n  void addWithSourceLoc(\n      Awaitable&& awaitable,\n      source_location sourceLocation = source_location::current()) {\n    addWithSourceLoc(\n        std::forward<Awaitable>(awaitable),\n        std::nullopt,\n        nullptr,\n        std::move(sourceLocation));\n  }\n\n  /**\n   * Schedules the given task on the current executor and adds it to the\n   * AsyncScope. The task will be provided a cancellation token to respond to\n   * cancelAndJoinAsync() in the future.\n   *\n   * An additional cancellation token may be passed in to apply to the\n   * awaitable; it will be merged with the internal token.\n   *\n   * Note that cancellation is cooperative, your task must handle cancellation\n   * in order to have any effect.\n   */\n  template <class T>\n  folly::coro::Task<void> co_schedule(\n      folly::coro::Task<T>&& task,\n      std::optional<CancellationToken> token = std::nullopt) {\n    add(co_withExecutor(co_await co_current_executor, std::move(task)),\n        std::move(token));\n  }\n\n  /**\n   * Request cancellation for all started tasks that accepted a\n   * CancellationToken in add().\n   */\n  void requestCancellation() const noexcept {\n    cancellationSource_.requestCancellation();\n  }\n\n  /**\n   * Query if cancellation was requested on the tasks added to this AsyncScope.\n   *\n   * This will return true if either cancellation was requested using\n   * `requestCancellation()` method of this scope OR if cancellation is\n   * requested on the token passed into the constructor.\n   */\n  bool isScopeCancellationRequested() const noexcept {\n    return cancellationToken_.isCancellationRequested();\n  }\n\n  /**\n   * Request cancellation and asynchronously wait for all started tasks to\n   * complete.\n   *\n   * Either call this method, _or_ joinAsync() to join the work. It is invalid\n   * to call both of them.\n   */\n  Task<void> cancelAndJoinAsync() noexcept {\n    requestCancellation();\n    co_await joinAsync();\n  }\n\n  /**\n   * Asynchronously wait for all started tasks to complete without requesting\n   * cancellation.\n   *\n   * Either call this method _or_ cancelAndJoinAsync() to join the\n   * work. It is invalid to call both of them.\n   */\n  Task<void> joinAsync() noexcept { co_await scope_.joinAsync(); }\n\n private:\n  folly::CancellationSource cancellationSource_;\n  CancellationToken cancellationToken_;\n  AsyncScope scope_;\n};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/AsyncStack.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Executor.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <utility>\n#include <vector>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\nclass AsyncStackTraceAwaitable {\n  class Awaiter {\n   public:\n    bool await_ready() const noexcept { return false; }\n\n    template <typename Promise>\n    bool await_suspend(coroutine_handle<Promise> h) noexcept {\n      initialFrame_ = &h.promise().getAsyncFrame();\n      return false;\n    }\n\n    FOLLY_NOINLINE std::vector<std::uintptr_t> await_resume() {\n      static constexpr size_t maxFrames = 100;\n      std::array<std::uintptr_t, maxFrames> result;\n\n      result[0] =\n          reinterpret_cast<std::uintptr_t>(FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n      auto numFrames = getAsyncStackTraceFromInitialFrame(\n          initialFrame_, result.data() + 1, maxFrames - 1);\n\n      return std::vector<std::uintptr_t>(\n          std::make_move_iterator(result.begin()),\n          std::make_move_iterator(result.begin()) + numFrames + 1);\n    }\n\n   private:\n    folly::AsyncStackFrame* initialFrame_;\n  };\n\n public:\n  AsyncStackTraceAwaitable viaIfAsync(\n      const folly::Executor::KeepAlive<>&) const noexcept {\n    return {};\n  }\n\n  Awaiter operator co_await() const noexcept { return {}; }\n\n  friend AsyncStackTraceAwaitable tag_invoke(\n      cpo_t<co_withAsyncStack>, AsyncStackTraceAwaitable awaitable) noexcept {\n    return awaitable;\n  }\n};\n\ninline constexpr AsyncStackTraceAwaitable co_current_async_stack_trace = {};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/AutoCleanup-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Cleanup.h>\n#include <folly/coro/Coroutine.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\ntemplate <typename T, typename CleanupFn = co_cleanup_fn>\nclass AutoCleanup;\n\ntemplate <typename T>\nstruct is_auto_cleanup : std::false_type {};\n\ntemplate <typename T>\nconstexpr bool is_auto_cleanup_v = is_auto_cleanup<T>::value;\n\ntemplate <typename T, typename CleanupFn>\nstruct is_auto_cleanup<AutoCleanup<T, CleanupFn>> : std::true_type {};\n\nnamespace detail {\ntemplate <typename Promise, typename... Args>\nvoid scheduleAutoCleanup(coroutine_handle<Promise> coro, Args&... args);\n} // namespace detail\n\ntemplate <typename Promise, typename... Args>\nvoid scheduleAutoCleanupIfNeeded(\n    coroutine_handle<Promise> coro, Args&... args) {\n  if constexpr ((is_auto_cleanup_v<Args> || ...)) {\n    detail::scheduleAutoCleanup(coro, args...);\n  }\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/AutoCleanup.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/AutoCleanup-fwd.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/Task.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nnamespace detail {\ntemplate <typename T>\nstruct ScopeExitArg {\n  explicit ScopeExitArg(T&) {}\n\n  folly::coro::Task<> cleanup() && { co_return; }\n\n  void update(T&) {}\n};\n} // namespace detail\n\n/// The user can use AutoCleanup to wrap arguments passed to a\n/// CleanableAsyncGenerator. When the coroutine promise of\n/// CleanableAsyncGenerator is created it will automatically attach\n/// co_scope_exit task that performs async cleanup for all the arguments wrapped\n/// in AutoCleanup. This allows to ensure cleanup of the arguments even when\n/// next() of the CleanableAsyncGenerator is never co_awaited.\n///\n/// Example usage:\n///  folly::coro::CleanableAsyncGenerator<std::pair<int, int>> zip(\n///      folly::coro::AutoCleanup<folly::coro::CleanableAsyncGenerator<int>> a,\n///      folly::coro::AutoCleanup<folly::coro::CleanableAsyncGenerator<int>> b\n///  ) {\n///    while (true) {\n///      auto x = co_await a->next();\n///      if (!x) {\n///        break;\n///      }\n///      auto y = co_await b->next();\n///      if (!y) {\n///        break;\n///      }\n///      co_yield std::make_pair(*x, *y);\n///    }\n///  }\n///\n/// In the example above, a and b will be cleaned up automatically when\n/// cleanup() of the zip generator is co_awaited, even if next() of the zip\n/// generator have been never co_awaited.\n\ntemplate <typename T, typename CleanupFn>\nclass AutoCleanup : MoveOnly {\n public:\n  using type = T;\n  using cleanup_fn = CleanupFn;\n\n  explicit AutoCleanup(T&& object, CleanupFn cleanupFn = CleanupFn{}) noexcept\n      : ptr_{std::addressof(object)}, cleanupFn_{std::move(cleanupFn)} {}\n\n  AutoCleanup(const AutoCleanup&) = delete;\n\n  AutoCleanup(AutoCleanup&& other) noexcept\n      : ptr_{std::exchange(other.ptr_, nullptr)},\n        cleanupFn_{std::move(other.cleanupFn_)} {}\n\n  ~AutoCleanup() { DCHECK(!kIsDebug || ptr_ == nullptr || scheduled_); }\n\n  AutoCleanup& operator=(const AutoCleanup&) = delete;\n\n  AutoCleanup& operator=(AutoCleanup&&) = delete;\n\n  std::add_lvalue_reference_t<T> operator*() const\n      noexcept(noexcept(*std::declval<T*>())) {\n    return *get();\n  }\n\n  T* operator->() const noexcept { return get(); }\n\n  T* get() const noexcept {\n    DCHECK(!kIsDebug || scheduled_);\n    return ptr_;\n  }\n\n private:\n  using BoolIfDebug = conditional_t<kIsDebug, bool, std::false_type>;\n\n  T* ptr_;\n  CleanupFn cleanupFn_;\n  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] BoolIfDebug scheduled_{};\n\n  friend struct detail::ScopeExitArg<AutoCleanup<T, CleanupFn>>;\n};\n\nnamespace detail {\n\ntemplate <typename T, typename CleanupFn>\nstruct ScopeExitArg<AutoCleanup<T, CleanupFn>> {\n  T object;\n  CleanupFn cleanupFn;\n\n  explicit ScopeExitArg(AutoCleanup<T, CleanupFn>& autoCleanup)\n      : object{std::move(*autoCleanup.ptr_)},\n        cleanupFn{autoCleanup.cleanupFn_} {}\n\n  auto cleanup() && { return cleanupFn(std::move(object)); }\n\n  void update(AutoCleanup<T, CleanupFn>& autoCleanup) {\n    autoCleanup.ptr_ = std::addressof(object);\n    if constexpr (kIsDebug) {\n      DCHECK(!autoCleanup.scheduled_);\n      autoCleanup.scheduled_ = true;\n    }\n  }\n};\n\ntemplate <typename Promise, typename Action, typename... Args>\nauto attachScopeExit(\n    coroutine_handle<Promise> coro, Action&& action, Args&&... args) {\n  auto scopeExitAwaiter = co_viaIfAsync(\n      nullptr,\n      co_scope_exit(\n          static_cast<Action&&>(action), static_cast<Args&&>(args)...));\n  auto ready = scopeExitAwaiter.await_ready();\n  DCHECK(!ready);\n  auto suspend = scopeExitAwaiter.await_suspend(coro);\n  DCHECK(!suspend);\n  return scopeExitAwaiter.await_resume();\n}\n\ntemplate <typename Promise, typename... Args>\nvoid scheduleAutoCleanup(coroutine_handle<Promise> coro, Args&... args) {\n  auto result = attachScopeExit(\n      coro,\n      [](auto&&... scopeExitArgs) -> Task<> {\n        co_await collectAll(std::move(scopeExitArgs).cleanup()...);\n      },\n      detail::ScopeExitArg<Args>(args)...);\n  std::apply([&](auto&... objs) { ((objs.update(args)), ...); }, result);\n}\n\n} // namespace detail\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"accumulate\",\n    headers = [\n        \"Accumulate.h\",\n        \"Accumulate-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:task\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"async_generator\",\n    srcs = [],\n    headers = [\"AsyncGenerator.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:cancellation_token\",\n        \"//folly:exception_wrapper\",\n        \"//folly:traits\",\n        \"//folly:try\",\n        \"//folly/coro:auto_cleanup_fwd\",\n        \"//folly/coro:base_promise\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_malloc\",\n        \"//folly/coro:detail_manual_lifetime\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:result\",\n        \"//folly/coro:scope_exit\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/coro:with_async_stack\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/lang:safe_alias_fwd\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"async_pipe\",\n    srcs = [],\n    headers = [\"AsyncPipe.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:try\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:small_unbounded_queue\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/fibers:semaphore\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"async_scope\",\n    srcs = [],\n    headers = [\"AsyncScope.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":coroutine\",\n        \":current_executor\",\n        \":detail_barrier\",\n        \":detail_barrier_task\",\n        \":task\",\n        \"//folly:cancellation_token\",\n        \"//folly:exception_wrapper\",\n        \"//folly/futures:core\",\n        \"//folly/portability:source_location\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"async_stack\",\n    headers = [\"AsyncStack.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:executor\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:with_async_stack\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"auto_cleanup\",\n    headers = [\n        \"AutoCleanup.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:auto_cleanup_fwd\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:task\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"auto_cleanup_fwd\",\n    headers = [\n        \"AutoCleanup-fwd.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:cleanup\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"base_promise\",\n    headers = [\"BasePromise.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":current_executor\",\n        \":error\",\n        \":nothrow\",\n        \":value_or_error\",\n        \"//folly:portability\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"baton\",\n    srcs = [\"Baton.cpp\"],\n    headers = [\"Baton.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly/synchronization:atomic_util\",\n    ],\n    exported_deps = [\n        \"//folly:try\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"blocking_wait\",\n    headers = [\"BlockingWait.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:try\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_malloc\",\n        \"//folly/coro:detail_traits\",\n        \"//folly/coro:task\",\n        \"//folly/coro:traits\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/coro:with_async_stack\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/fibers:core\",\n        \"//folly/lang:must_use_immediately\",\n        \"//folly/synchronization:baton\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"cleanup\",\n    headers = [\n        \"Cleanup.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/functional:invoke\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"collect\",\n    headers = [\n        \"Collect.h\",\n        \"Collect-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:exception_wrapper\",\n        \"//folly:try\",\n        \"//folly:unit\",\n        \"//folly/container:access\",\n        \"//folly/container:iterator\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:async_pipe\",\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_barrier\",\n        \"//folly/coro:detail_barrier_task\",\n        \"//folly/coro:detail_current_async_frame\",\n        \"//folly/coro:detail_helpers\",\n        \"//folly/coro:detail_traits\",\n        \"//folly/coro:mutex\",\n        \"//folly/coro:task\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/coro/detail:pick_task_wrapper\",\n        \"//folly/coro/safe:now_task\",\n        \"//folly/coro/safe:safe_task\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"concat\",\n    headers = [\n        \"Concat.h\",\n        \"Concat-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"coroutine\",\n    headers = [\"Coroutine.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly:utility\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"current_executor\",\n    headers = [\"CurrentExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:executor\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/coro:with_async_stack\",\n        \"//folly/io/async:request_context\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detach_on_cancel\",\n    headers = [\"DetachOnCancel.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:baton\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_helpers\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:task\",\n        \"//folly/coro:traits\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detail_barrier\",\n    headers = [\"detail/Barrier.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":coroutine\",\n        \":traits\",\n        \":with_async_stack\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detail_barrier_task\",\n    headers = [\"detail/BarrierTask.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":coroutine\",\n        \":detail_barrier\",\n        \":detail_malloc\",\n        \":with_async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detail_current_async_frame\",\n    headers = [\"detail/CurrentAsyncFrame.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":coroutine\",\n        \":with_async_stack\",\n        \"//folly:executor\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detail_helpers\",\n    headers = [\"detail/Helpers.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":coroutine\",\n        \"//folly:executor\",\n        \"//folly:singleton_thread_local\",\n        \"//folly/io/async:request_context\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detail_malloc\",\n    srcs = [\"detail/Malloc.cpp\"],\n    headers = [\"detail/Malloc.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly/lang:hint\",\n        \"//folly/lang:new\",\n    ],\n    exported_deps = [\n        \"//folly:c_portability\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detail_manual_lifetime\",\n    headers = [\"detail/ManualLifetime.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:scope_guard\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"detail_traits\",\n    headers = [\"detail/Traits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:traits\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"error\",\n    headers = [\"Error.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:exception_wrapper\",\n        \"//folly:operation_cancelled\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"filter\",\n    headers = [\n        \"Filter.h\",\n        \"Filter-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"future_util\",\n    headers = [\"FutureUtil.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:current_executor\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:task\",\n        \"//folly/coro:traits\",\n        \"//folly/futures:core\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"generator\",\n    headers = [\"Generator.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:invoke\",\n        \"//folly/lang:exception\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"gmock_helpers\",\n    headers = [\"GmockHelpers.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":blocking_wait\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:result\",\n        \"//folly/coro:task\",\n        \"//folly/portability:gmock\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"gtest_helpers\",\n    headers = [\"GtestHelpers.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:task\",\n        \"//folly/debugging/exception_tracer:smart_exception_stack_trace_hooks\",  # @manual\n        \"//folly/debugging/exception_tracer:smart_exception_tracer\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"inline_task\",\n    headers = [\"detail/InlineTask.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":coroutine\",\n        \":detail_malloc\",\n        \":with_async_stack\",\n        \"//folly:scope_guard\",\n        \"//folly:try\",\n        \"//folly/lang:assume\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"invoke\",\n    headers = [\"Invoke.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/functional:invoke\",\n        \"//folly/lang:customization_point\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"merge\",\n    headers = [\n        \"Merge.h\",\n        \"Merge-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:executor\",\n        \"//folly:scope_guard\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_barrier\",\n        \"//folly/coro:detail_barrier_task\",\n        \"//folly/coro:detail_current_async_frame\",\n        \"//folly/coro:detail_helpers\",\n        \"//folly/coro:mutex\",\n        \"//folly/coro:task\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/coro:with_cancellation\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"mutex\",\n    srcs = [\"Mutex.cpp\"],\n    headers = [\"Mutex.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:executor\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:via_if_async\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"value_or_fatal\",\n    headers = [\"ValueOrFatal.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":task_wrapper\",\n        \":via_if_async\",\n        \"//folly:unit\",\n        \"//folly/lang:assume\",\n        \"//folly/result:result\",\n        \"//folly/result:try\",\n        \"//folly/result:value_only_result\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"nothrow\",\n    headers = [\"Nothrow.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":via_if_async\",\n        \"//folly:exception_wrapper\",\n        \"//folly:portability\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"promise\",\n    headers = [\"Promise.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:try\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:coroutine\",\n        \"//folly/futures:core\",\n        \"//folly/lang:safe_alias_fwd\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"result\",\n    headers = [\"Result.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":error\",\n        \"//folly:try\",\n        \"//folly/result:try\",\n        \"//folly/result:value_only_result\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"retry\",\n    headers = [\"Retry.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:constexpr_math\",\n        \"//folly:exception_wrapper\",\n        \"//folly:random\",\n        \"//folly:try\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:result\",\n        \"//folly/coro:sleep\",\n        \"//folly/coro:task\",\n        \"//folly/coro:traits\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"rust_adaptors\",\n    headers = [\"RustAdaptors.h\"],\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:executor\",\n        \"//folly:optional\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:task\",\n        \"//folly/futures:core\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"scope_exit\",\n    headers = [\"ScopeExit.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:exception_wrapper\",\n        \"//folly:executor\",\n        \"//folly:scope_guard\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:traits\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:customization_point\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"serial_queue_runner\",\n    srcs = [\"SerialQueueRunner.cpp\"],\n    headers = [\"SerialQueueRunner.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":baton\",\n        \":task\",\n        \"//folly:exception_wrapper\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"shared_lock\",\n    headers = [\"SharedLock.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:portability\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:task\",\n        \"//folly/synchronization:lock\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"shared_mutex\",\n    srcs = [\"SharedMutex.cpp\"],\n    headers = [\"SharedMutex.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:executor\",\n        \"//folly:spin_lock\",\n        \"//folly:synchronized\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:shared_lock\",\n        \"//folly/coro:via_if_async\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"shared_promise\",\n    headers = [\n        \"SharedPromise.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":promise\",\n        \"//folly:likely\",\n        \"//folly:small_vector\",\n        \"//folly:synchronized\",\n        \"//folly:utility\",\n        \"//folly/futures:core\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"sleep\",\n    headers = [\n        \"Sleep.h\",\n        \"Sleep-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:future_util\",\n        \"//folly/coro:task\",\n        \"//folly/futures:core\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"small_unbounded_queue\",\n    srcs = [],\n    headers = [\"SmallUnboundedQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:baton\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:mutex\",\n        \"//folly/coro:task\",\n        \"//folly/experimental/channels/detail:atomic_queue\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"synchronized\",\n    headers = [\"Synchronized.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":shared_lock\",\n        \":shared_mutex\",\n        \":task\",\n        \":traits\",\n        \"//folly:utility\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"task\",\n    headers = [\"Task.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:cancellation_token\",\n        \"//folly:default_keep_alive_executor\",\n        \"//folly:executor\",\n        \"//folly:glog\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:traits\",\n        \"//folly:try\",\n        \"//folly/coro:base_promise\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_malloc\",\n        \"//folly/coro:detail_traits\",\n        \"//folly/coro:inline_task\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:result\",\n        \"//folly/coro:scope_exit\",\n        \"//folly/coro:traits\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/coro:with_async_stack\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:request_context\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:must_use_immediately\",\n        \"//folly/lang:safe_alias_fwd\",\n        \"//folly/result:result\",\n        \"//folly/result:try\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"task_wrapper\",\n    headers = [\"TaskWrapper.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":task\",\n        \"//folly/lang:must_use_immediately\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"timed_wait\",\n    headers = [\"TimedWait.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:optional\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_helpers\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:task\",\n        \"//folly/coro:traits\",\n        \"//folly/futures:core\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"timeout\",\n    headers = [\n        \"Timeout.h\",\n        \"Timeout-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:task\",\n        \"//folly/coro:traits\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/coro/detail:pick_task_wrapper\",\n        \"//folly/coro/safe:now_task\",\n        \"//folly/coro/safe:safe_task\",\n        \"//folly/futures:core\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"traits\",\n    headers = [\"Traits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:traits\",\n        \"//folly/coro:coroutine\",\n    ],\n    exported_external_deps = [],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"transform\",\n    headers = [\n        \"Transform.h\",\n        \"Transform-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:traits\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"bounded_queue\",\n    srcs = [],\n    headers = [\"BoundedQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:mpmc_queue\",\n        \"//folly:producer_consumer_queue\",\n        \"//folly/coro:task\",\n        \"//folly/fibers:semaphore\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"unbounded_queue\",\n    srcs = [],\n    headers = [\"UnboundedQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:task\",\n        \"//folly/fibers:semaphore\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"value_or_error\",\n    headers = [\"ValueOrError.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":via_if_async\",\n        \"//folly/result:try\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"via_if_async\",\n    headers = [\"ViaIfAsync.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:executor\",\n        \"//folly:traits\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:detail_malloc\",\n        \"//folly/coro:traits\",\n        \"//folly/coro:with_async_stack\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/io/async:request_context\",\n        \"//folly/lang:customization_point\",\n        \"//folly/lang:must_use_immediately\",\n        \"//folly/lang:safe_alias_fwd\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"with_async_stack\",\n    headers = [\"WithAsyncStack.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:traits\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:customization_point\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"with_cancellation\",\n    headers = [\"WithCancellation.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/coro:coroutine\",\n        \"//folly/lang:customization_point\",\n        \"//folly/lang:must_use_immediately\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/BasePromise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h> // FOLLY_HAS_COROUTINES\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Error.h>\n#include <folly/coro/Nothrow.h>\n#include <folly/coro/ValueOrError.h>\n\n/// Coroutine promise implementation shared between tasks and async generators.\n/// Only the `await_transform` signatures here are of interest to end-users.\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro::detail {\n\nclass TaskPromiseBase;\n\ntemplate <typename Reference, typename Value, bool RequiresCleanup = false>\nclass AsyncGeneratorPromise;\n\ntemplate <typename TailStorage = Unit>\nclass BasePromise {\n protected:\n  // The destructor is protected to alert future authors -- this is a TIGHTLY\n  // COUPLED DETAIL of task & async generator, not an easily reusable\n  // component.  For example, it relies on correctly specified\n  // `await_transform` behavior, a correct definition of the `getErrorHandle`\n  // protocol, and its correct usage in `await_suspend`.\n  friend class TaskPromiseBase;\n  template <typename, typename, bool>\n  friend class AsyncGeneratorPromise;\n  ~BasePromise() = default;\n\n  ExtendedCoroutineHandle continuation_;\n  folly::AsyncStackFrame asyncFrame_;\n  folly::Executor::KeepAlive<> executor_;\n  folly::CancellationToken cancelToken_;\n  bool hasCancelTokenOverride_ = false;\n  BypassExceptionThrowing bypassThrowing_;\n  // Let derived classes pack data in one word with the prior 2 members\n  // This could perhaps be avoided by using bitfields...\n  TailStorage tailStorage_;\n\n  // Implementation of `await_transform(co_safe_point_t)`\n  template <typename Awaiter, typename Promise>\n  variant_awaitable<Awaiter, ready_awaitable<>> do_safe_point(\n      Promise& promise) noexcept {\n    if (cancelToken_.isCancellationRequested()) {\n      return promise.yield_value(co_stopped_may_throw);\n    }\n    return ready_awaitable<>{};\n  }\n\n  // Async generator & task specialize this to check the type of Promise\n  template <typename Promise>\n  static std::optional<ExtendedCoroutineHandle::ErrorHandle>\n  getErrorHandleUncheckedImpl(Promise& me, exception_wrapper& ex) {\n    if (me.bypassThrowing_.shouldBypassFor(ex)) {\n      auto finalAwaiter = me.yield_value(co_error(std::move(ex)));\n      DCHECK(!finalAwaiter.await_ready());\n      return ExtendedCoroutineHandle::ErrorHandle{\n          finalAwaiter.await_suspend_promise(me),\n          // finalAwaiter.await_suspend pops a frame\n          me.getAsyncFrame().getParentFrame()};\n    }\n    return std::nullopt;\n  }\n\n public:\n  template <\n      typename Awaitable,\n      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto await_transform(Awaitable&& awaitable) {\n    bypassThrowing_.maybeActivate<Awaitable>();\n    return folly::coro::co_withAsyncStack(\n        folly::coro::co_viaIfAsync(\n            executor_.get_alias(),\n            folly::coro::co_withCancellation(\n                cancelToken_, static_cast<Awaitable&&>(awaitable))));\n  }\n  template <\n      typename Awaitable,\n      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto await_transform(Awaitable awaitable) {\n    bypassThrowing_.maybeActivate<Awaitable>();\n    return folly::coro::co_withAsyncStack(\n        folly::coro::co_viaIfAsync(\n            executor_.get_alias(),\n            folly::coro::co_withCancellation(\n                cancelToken_,\n                folly::ext::must_use_immediately_unsafe_mover(\n                    std::move(awaitable))())));\n  }\n\n  template <typename Awaitable>\n  auto await_transform(NothrowAwaitable<Awaitable> awaitable) {\n    bypassThrowing_.requestDueToNothrow<Awaitable>();\n    return await_transform(\n        folly::ext::must_use_immediately_unsafe_mover(awaitable.unwrap())());\n  }\n\n  template <typename Awaitable>\n  auto await_transform(ValueOrError<Awaitable> awaitable) {\n    bypassThrowing_.template requestDueToValueOrError<Awaitable>();\n    return await_transform(std::move(awaitable).toValueOrErrorImpl());\n  }\n\n  auto await_transform(co_current_executor_t) noexcept {\n    return ready_awaitable<folly::Executor*>{executor_.get()};\n  }\n\n  auto await_transform(co_current_cancellation_token_t) noexcept {\n    return ready_awaitable<const folly::CancellationToken&>{cancelToken_};\n  }\n\n  // End-users can do this via `co_withCancellation`.\n  void setCancellationToken(folly::CancellationToken cancelToken) noexcept {\n    // Only keep the first cancellation token.  This is usually the inner-most\n    // cancellation scope of the consumer's calling context.\n    if (!hasCancelTokenOverride_) {\n      cancelToken_ = std::move(cancelToken);\n      hasCancelTokenOverride_ = true;\n    }\n  }\n\n  folly::AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }\n};\n\n} // namespace folly::coro::detail\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Baton.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Baton.h>\n\n#include <folly/coro/Coroutine.h>\n#include <folly/synchronization/AtomicUtil.h>\n\n#include <cassert>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nBaton::~Baton() {\n  // Should not be any waiting coroutines when the baton is destructed.\n  // Caller should ensure the baton is posted before destructing.\n  assert(\n      state_.load(std::memory_order_relaxed) == static_cast<void*>(this) ||\n      state_.load(std::memory_order_relaxed) == nullptr);\n}\n\nvoid Baton::post() noexcept {\n  void* const signalledState = static_cast<void*>(this);\n  void* oldValue = state_.exchange(signalledState, std::memory_order_acq_rel);\n  if (oldValue != signalledState) {\n    // We are the first thread to set the state to signalled and there is\n    // a waiting coroutine. We are responsible for resuming it.\n    WaitOperation* awaiter = static_cast<WaitOperation*>(oldValue);\n    while (awaiter != nullptr) {\n      std::exchange(awaiter, awaiter->next_)->awaitingCoroutine_.resume();\n    }\n  }\n}\n\nbool Baton::waitImpl(WaitOperation* awaiter) const noexcept {\n  // Try to push the awaiter onto the front of the queue of waiters.\n  const auto signalledState = static_cast<const void*>(this);\n  void* oldValue = state_.load(std::memory_order_acquire);\n  do {\n    if (oldValue == signalledState) {\n      // Already in the signalled state, don't enqueue it.\n      return false;\n    }\n    awaiter->next_ = static_cast<WaitOperation*>(oldValue);\n  } while (!folly::atomic_compare_exchange_weak_explicit(\n      &state_,\n      &oldValue,\n      awaiter,\n      std::memory_order_release,\n      std::memory_order_acquire));\n  return true;\n}\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Baton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/Try.h>\n#include <folly/coro/Coroutine.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n/// A baton is a synchronisation primitive for coroutines that allows a\n/// coroutine to co_await the baton and suspend until the baton is posted by\n/// some thread via a call to .post().\n///\n/// This primitive is typically used in the construction of larger library types\n/// rather than directly in user code.\n///\n/// As a primitive, this is not cancellation-aware.\n///\n/// The Baton supports being awaited by multiple coroutines at a time. If the\n/// baton is not ready at the time it is awaited then an awaiting coroutine\n/// suspends. All suspended coroutines waiting for the baton to be posted will\n/// be resumed when some thread next calls .post().\n///\n/// Example usage:\n///\n///   folly::coro::Baton baton;\n///   std::string sharedValue;\n///\n///   folly::coro::Task<void> consumer()\n///   {\n///     // Wait until the baton is posted.\n///     co_await baton;\n///\n///     // Now safe to read shared state.\n///     std::cout << sharedValue << std::cout;\n///   }\n///\n///   void producer()\n///   {\n///     // Write to shared state\n///     sharedValue = \"some result\";\n///\n///     // Publish the value by 'posting' the baton.\n///     // This will resume the consumer if it was currently suspended.\n///     baton.post();\n///   }\nclass Baton {\n public:\n  class WaitOperation;\n\n  /// Initialise the Baton to either the signalled or non-signalled state.\n  explicit Baton(bool initiallySignalled = false) noexcept;\n\n  ~Baton();\n\n  /// Query whether the Baton is currently in the signalled state.\n  bool ready() const noexcept;\n\n  /// Asynchronously wait for the Baton to enter the signalled state.\n  ///\n  /// The returned object must be co_awaited from a coroutine. If the Baton\n  /// is already signalled then the awaiting coroutine will continue without\n  /// suspending. Otherwise, if the Baton is not yet signalled then the\n  /// awaiting coroutine will suspend execution and will be resumed when some\n  /// thread later calls post().\n  [[nodiscard]] WaitOperation operator co_await() const noexcept;\n\n  /// Set the Baton to the signalled state if it is not already signalled.\n  ///\n  /// This will resume any coroutines that are currently suspended waiting\n  /// for the Baton inside 'co_await baton'.\n  void post() noexcept;\n\n  /// Atomically reset the baton back to the non-signalled state.\n  ///\n  /// This is a no-op if the baton was already in the non-signalled state.\n  void reset() noexcept;\n\n  class WaitOperation {\n   public:\n    explicit WaitOperation(const Baton& baton) noexcept : baton_(baton) {}\n\n    bool await_ready() const noexcept { return baton_.ready(); }\n\n    bool await_suspend(coroutine_handle<> awaitingCoroutine) noexcept {\n      awaitingCoroutine_ = awaitingCoroutine;\n      return baton_.waitImpl(this);\n    }\n\n    void await_resume() noexcept {}\n\n    // Awaiting a baton doesn't throw, so supporting `co_awaitTry` here only\n    // serves to simplify generic code.\n    folly::Try<void> await_resume_try() noexcept { return {}; }\n\n   protected:\n    friend class Baton;\n\n    const Baton& baton_;\n    coroutine_handle<> awaitingCoroutine_;\n    WaitOperation* next_;\n  };\n\n private:\n  // Try to register the awaiter as\n  bool waitImpl(WaitOperation* awaiter) const noexcept;\n\n  // this  - Baton is in the signalled/posted state.\n  // other - Baton is not signalled/posted and this is a pointer to the head\n  //         of a potentially empty linked-list of Awaiter nodes that were\n  //         waiting for the baton to become signalled.\n  mutable std::atomic<void*> state_;\n};\n\ninline Baton::Baton(bool initiallySignalled) noexcept\n    : state_(initiallySignalled ? static_cast<void*>(this) : nullptr) {}\n\ninline bool Baton::ready() const noexcept {\n  return state_.load(std::memory_order_acquire) ==\n      static_cast<const void*>(this);\n}\n\ninline Baton::WaitOperation Baton::operator co_await() const noexcept {\n  return Baton::WaitOperation{*this};\n}\n\ninline void Baton::reset() noexcept {\n  // Transition from 'signalled' (ie. 'this') to not-signalled (ie. nullptr).\n  void* oldState = this;\n  (void)state_.compare_exchange_strong(\n      oldState, nullptr, std::memory_order_acq_rel, std::memory_order_relaxed);\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/BlockingWait.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Try.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/coro/detail/Malloc.h>\n#include <folly/coro/detail/Traits.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/fibers/Baton.h>\n#include <folly/lang/MustUseImmediately.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <cassert>\n#include <deque>\n#include <exception>\n#include <type_traits>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\nnamespace detail {\n\ntemplate <typename T>\nclass BlockingWaitTask;\n\nclass BlockingWaitPromiseBase {\n  struct FinalAwaiter {\n    bool await_ready() noexcept { return false; }\n    template <typename Promise>\n    void await_suspend(coroutine_handle<Promise> coro) noexcept {\n      BlockingWaitPromiseBase& promise = coro.promise();\n      folly::deactivateAsyncStackFrame(promise.getAsyncFrame());\n      promise.baton_.post();\n    }\n    void await_resume() noexcept {}\n  };\n\n public:\n  BlockingWaitPromiseBase() noexcept = default;\n\n  static void* operator new(std::size_t size) {\n    return ::folly_coro_async_malloc(size);\n  }\n\n  static void operator delete(void* ptr, std::size_t size) {\n    ::folly_coro_async_free(ptr, size);\n  }\n\n  suspend_always initial_suspend() { return {}; }\n\n  FinalAwaiter final_suspend() noexcept { return {}; }\n\n  template <typename Awaitable>\n  decltype(auto) await_transform(Awaitable&& awaitable) {\n    return folly::coro::co_withAsyncStack(static_cast<Awaitable&&>(awaitable));\n  }\n\n  bool done() const noexcept { return baton_.ready(); }\n\n  void wait() noexcept { baton_.wait(); }\n\n  folly::AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }\n\n private:\n  folly::fibers::Baton baton_;\n  folly::AsyncStackFrame asyncFrame_;\n};\n\ntemplate <typename T>\nclass BlockingWaitPromise final : public BlockingWaitPromiseBase {\n public:\n  BlockingWaitPromise() noexcept = default;\n\n  ~BlockingWaitPromise() = default;\n\n  BlockingWaitTask<T> get_return_object() noexcept;\n\n  void unhandled_exception() noexcept {\n    result_->emplaceException(folly::exception_wrapper{current_exception()});\n  }\n\n  template <\n      typename U = T,\n      std::enable_if_t<std::is_convertible<U, T>::value, int> = 0>\n  void return_value(U&& value) noexcept(\n      std::is_nothrow_constructible<T, U&&>::value) {\n    result_->emplace(static_cast<U&&>(value));\n  }\n\n  void setTry(folly::Try<T>* result) noexcept { result_ = &result; }\n\n private:\n  folly::Try<T>* result_;\n};\n\ntemplate <typename T>\nclass BlockingWaitPromise<T&> final : public BlockingWaitPromiseBase {\n public:\n  BlockingWaitPromise() noexcept = default;\n\n  ~BlockingWaitPromise() = default;\n\n  BlockingWaitTask<T&> get_return_object() noexcept;\n\n  void unhandled_exception() noexcept {\n    result_->emplaceException(folly::exception_wrapper{current_exception()});\n  }\n\n  auto yield_value(T&& value) noexcept {\n    result_->emplace(std::ref(value));\n    return final_suspend();\n  }\n\n  auto yield_value(T& value) noexcept {\n    result_->emplace(std::ref(value));\n    return final_suspend();\n  }\n\n  void return_void() {\n    // This should never be reachable.\n    // The coroutine should either have suspended at co_yield or should have\n    // thrown an exception and skipped over the implicit co_return and\n    // gone straight to unhandled_exception().\n    std::abort();\n  }\n\n  void setTry(folly::Try<std::reference_wrapper<T>>* result) noexcept {\n    result_ = result;\n  }\n\n private:\n  folly::Try<std::reference_wrapper<T>>* result_;\n};\n\ntemplate <>\nclass BlockingWaitPromise<void> final : public BlockingWaitPromiseBase {\n public:\n  BlockingWaitPromise() = default;\n\n  BlockingWaitTask<void> get_return_object() noexcept;\n\n  void return_void() noexcept {}\n\n  void unhandled_exception() noexcept {\n    result_->emplaceException(exception_wrapper{current_exception()});\n  }\n\n  void setTry(folly::Try<void>* result) noexcept { result_ = result; }\n\n private:\n  folly::Try<void>* result_;\n};\n\ntemplate <typename T>\nclass BlockingWaitTask {\n public:\n  using promise_type = BlockingWaitPromise<T>;\n  using handle_t = coroutine_handle<promise_type>;\n\n  explicit BlockingWaitTask(handle_t coro) noexcept : coro_(coro) {}\n\n  BlockingWaitTask(BlockingWaitTask&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  BlockingWaitTask& operator=(BlockingWaitTask&& other) noexcept = delete;\n\n  ~BlockingWaitTask() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  FOLLY_NOINLINE T get(folly::AsyncStackFrame& parentFrame) && {\n    folly::Try<detail::lift_lvalue_reference_t<T>> result;\n    auto& promise = coro_.promise();\n    promise.setTry(&result);\n\n    auto& asyncFrame = promise.getAsyncFrame();\n    asyncFrame.setParentFrame(parentFrame);\n    asyncFrame.setReturnAddress();\n    {\n      RequestContextScopeGuard guard{RequestContext::saveContext()};\n      folly::resumeCoroutineWithNewAsyncStackRoot(coro_);\n    }\n    promise.wait();\n    return std::move(result).value();\n  }\n\n  FOLLY_NOINLINE T getVia(\n      folly::DrivableExecutor* executor,\n      folly::AsyncStackFrame& parentFrame) && {\n    folly::Try<detail::lift_lvalue_reference_t<T>> result;\n    auto& promise = coro_.promise();\n    promise.setTry(&result);\n\n    auto& asyncFrame = promise.getAsyncFrame();\n    asyncFrame.setReturnAddress();\n    asyncFrame.setParentFrame(parentFrame);\n\n    executor->add(\n        [coro = coro_, rctx = RequestContext::saveContext()]() mutable {\n          RequestContextScopeGuard guard{std::move(rctx)};\n          folly::resumeCoroutineWithNewAsyncStackRoot(coro);\n        });\n    while (!promise.done()) {\n      executor->drive();\n    }\n    return std::move(result).value();\n  }\n\n private:\n  handle_t coro_;\n};\n\ntemplate <typename T>\ninline BlockingWaitTask<T>\nBlockingWaitPromise<T>::get_return_object() noexcept {\n  return BlockingWaitTask<T>{\n      coroutine_handle<BlockingWaitPromise<T>>::from_promise(*this)};\n}\n\ntemplate <typename T>\ninline BlockingWaitTask<T&>\nBlockingWaitPromise<T&>::get_return_object() noexcept {\n  return BlockingWaitTask<T&>{\n      coroutine_handle<BlockingWaitPromise<T&>>::from_promise(*this)};\n}\n\ninline BlockingWaitTask<void>\nBlockingWaitPromise<void>::get_return_object() noexcept {\n  return BlockingWaitTask<void>{\n      coroutine_handle<BlockingWaitPromise<void>>::from_promise(*this)};\n}\n\ntemplate <\n    typename Awaitable,\n    typename Result = await_result_t<Awaitable>,\n    std::enable_if_t<std::is_void<Result>::value, int> = 0>\nBlockingWaitTask<void> makeRefBlockingWaitTask(Awaitable&& awaitable) {\n  co_await static_cast<Awaitable&&>(awaitable);\n}\n\ntemplate <\n    typename Awaitable,\n    typename Result = await_result_t<Awaitable>,\n    std::enable_if_t<!std::is_void<Result>::value, int> = 0>\nauto makeRefBlockingWaitTask(Awaitable&& awaitable)\n    -> BlockingWaitTask<std::add_lvalue_reference_t<Result>> {\n  co_yield co_await static_cast<Awaitable&&>(awaitable);\n}\n\nclass BlockingWaitExecutor final\n    : public folly::DrivableExecutor,\n      public SequencedExecutor {\n public:\n  ~BlockingWaitExecutor() override {\n    while (keepAliveCount_.load() > 0) {\n      drive();\n    }\n  }\n\n  void add(Func func) override {\n    bool empty;\n    {\n      auto wQueue = queue_.wlock();\n      empty = wQueue->empty();\n      wQueue->emplace_back(\n          std::move(func), folly::RequestContext::saveContext());\n    }\n    if (empty) {\n      baton_.post();\n    }\n  }\n\n  void drive() override {\n    baton_.wait();\n    baton_.reset();\n\n    folly::fibers::runInMainContext([&]() {\n      std::deque<BlockingWaitTaskInfo> infos;\n      queue_.swap(infos);\n      RequestContextSaverScopeGuard guard;\n      for (auto& info : infos) {\n        folly::RequestContext::setContext(std::move(info.rctx));\n        std::exchange(info.func, nullptr)();\n      }\n    });\n  }\n\n private:\n  bool keepAliveAcquire() noexcept override {\n    auto keepAliveCount =\n        keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    DCHECK(keepAliveCount >= 0);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    auto keepAliveCount = keepAliveCount_.load(std::memory_order_relaxed);\n    do {\n      DCHECK(keepAliveCount > 0);\n      if (keepAliveCount == 1) {\n        add([this] {\n          // the final count *must* be released from this executor or else if we\n          // are mid-destructor we have a data race\n          keepAliveCount_.fetch_sub(1, std::memory_order_relaxed);\n        });\n        return;\n      }\n    } while (!keepAliveCount_.compare_exchange_weak(\n        keepAliveCount,\n        keepAliveCount - 1,\n        std::memory_order_release,\n        std::memory_order_relaxed));\n  }\n\n  struct BlockingWaitTaskInfo {\n    Func func;\n    std::shared_ptr<folly::RequestContext> rctx;\n    BlockingWaitTaskInfo(Func f, std::shared_ptr<folly::RequestContext> r)\n        : func(std::move(f)), rctx(std::move(r)) {}\n  };\n\n  folly::Synchronized<std::deque<BlockingWaitTaskInfo>> queue_;\n  fibers::Baton baton_;\n\n  std::atomic<ssize_t> keepAliveCount_{0};\n};\n\n} // namespace detail\n\n/// blocking_wait_fn\n///\n/// Awaits co_awaits the passed awaitable and blocks the current thread until\n/// the await operation completes.\n///\n/// Useful for launching an asynchronous operation from the top-level main()\n/// function or from unit-tests.\n///\n/// WARNING:\n/// Avoid using this function within any code that might run on the thread\n/// of an executor as this can potentially lead to deadlock if the operation\n/// you are waiting on needs to do some work on that executor in order to\n/// complete.\nstruct blocking_wait_fn {\n  template <typename Awaitable>\n  FOLLY_NOINLINE auto operator()(Awaitable&& awaitable) const\n      -> detail::decay_rvalue_reference_t<await_result_t<Awaitable>> {\n    folly::AsyncStackFrame frame;\n    frame.setReturnAddress();\n\n    folly::AsyncStackRoot stackRoot;\n    stackRoot.setNextRoot(folly::tryGetCurrentAsyncStackRoot());\n    stackRoot.setStackFrameContext();\n    stackRoot.setTopFrame(frame);\n\n    return static_cast<std::add_rvalue_reference_t<await_result_t<Awaitable>>>(\n        detail::makeRefBlockingWaitTask(static_cast<Awaitable&&>(awaitable))\n            .get(frame));\n  }\n\n  template <\n      typename SemiAwaitable,\n      std::enable_if_t<\n          !folly::ext::must_use_immediately_v<SemiAwaitable>,\n          int> = 0>\n  FOLLY_NOINLINE auto operator()(\n      SemiAwaitable&& awaitable, folly::DrivableExecutor* executor) const\n      -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {\n    folly::AsyncStackFrame frame;\n    frame.setReturnAddress();\n\n    folly::AsyncStackRoot stackRoot;\n    stackRoot.setNextRoot(folly::tryGetCurrentAsyncStackRoot());\n    stackRoot.setStackFrameContext();\n    stackRoot.setTopFrame(frame);\n\n    return static_cast<\n        std::add_rvalue_reference_t<semi_await_result_t<SemiAwaitable>>>(\n        detail::makeRefBlockingWaitTask(\n            folly::coro::co_viaIfAsync(\n                folly::getKeepAliveToken(executor),\n                static_cast<SemiAwaitable&&>(awaitable)))\n            .getVia(executor, frame));\n  }\n  template <\n      typename SemiAwaitable,\n      std::enable_if_t<folly::ext::must_use_immediately_v<SemiAwaitable>, int> =\n          0>\n  FOLLY_NOINLINE auto operator()(\n      SemiAwaitable awaitable, folly::DrivableExecutor* executor) const\n      -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {\n    folly::AsyncStackFrame frame;\n    frame.setReturnAddress();\n\n    folly::AsyncStackRoot stackRoot;\n    stackRoot.setNextRoot(folly::tryGetCurrentAsyncStackRoot());\n    stackRoot.setStackFrameContext();\n    stackRoot.setTopFrame(frame);\n\n    return static_cast<\n        std::add_rvalue_reference_t<semi_await_result_t<SemiAwaitable>>>(\n        detail::makeRefBlockingWaitTask(\n            folly::coro::co_viaIfAsync(\n                folly::getKeepAliveToken(executor),\n                folly::ext::must_use_immediately_unsafe_mover(\n                    std::move(awaitable))()))\n            .getVia(executor, frame));\n  }\n\n  template <\n      typename SemiAwaitable,\n      std::enable_if_t<!is_awaitable_v<SemiAwaitable>, int> = 0,\n      std::enable_if_t<\n          !folly::ext::must_use_immediately_v<SemiAwaitable>,\n          int> = 0>\n  auto operator()(SemiAwaitable&& awaitable) const\n      -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {\n    std::exception_ptr eptr;\n    {\n      detail::BlockingWaitExecutor executor;\n      try {\n        return operator()(static_cast<SemiAwaitable&&>(awaitable), &executor);\n      } catch (...) {\n        eptr = current_exception();\n      }\n    }\n    std::rethrow_exception(eptr);\n  }\n  template <\n      typename SemiAwaitable,\n      std::enable_if_t<!is_awaitable_v<SemiAwaitable>, int> = 0,\n      std::enable_if_t<folly::ext::must_use_immediately_v<SemiAwaitable>, int> =\n          0>\n  auto operator()(SemiAwaitable awaitable) const\n      -> detail::decay_rvalue_reference_t<semi_await_result_t<SemiAwaitable>> {\n    std::exception_ptr eptr;\n    {\n      detail::BlockingWaitExecutor executor;\n      try {\n        return operator()(\n            folly::ext::must_use_immediately_unsafe_mover(\n                std::move(awaitable))(),\n            &executor);\n      } catch (...) {\n        eptr = current_exception();\n      }\n    }\n    std::rethrow_exception(eptr);\n  }\n};\ninline constexpr blocking_wait_fn blocking_wait{};\nstatic constexpr blocking_wait_fn const& blockingWait =\n    blocking_wait; // backcompat\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/BoundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/MPMCQueue.h>\n#include <folly/ProducerConsumerQueue.h>\n#include <folly/coro/Task.h>\n#include <folly/fibers/Semaphore.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// A coroutine version of bounded queue with given capacity. Both enqueue and\n// dequeue are async awaitable.\ntemplate <typename T, bool SingleProducer = false, bool SingleConsumer = false>\nclass BoundedQueue {\n  static constexpr bool kSPSC = SingleProducer && SingleConsumer;\n\n public:\n  explicit BoundedQueue(uint32_t capacity)\n      : queue_(\n            kSPSC ? capacity + 1 // One more extra space because usable space of\n                                 // ProducerConsumerQueue used below is (size-1)\n                  : capacity),\n        enqueueSemaphore_{capacity},\n        dequeueSemaphore_{0} {}\n\n  BoundedQueue(const BoundedQueue&) = delete;\n  BoundedQueue& operator=(const BoundedQueue&) = delete;\n\n  template <typename U = T>\n  folly::coro::Task<void> enqueue(U&& item) {\n    co_await folly::coro::co_nothrow(enqueueSemaphore_.co_wait());\n    enqueueReady(std::forward<U>(item));\n    dequeueSemaphore_.signal();\n  }\n\n  template <typename U = T>\n  bool try_enqueue(U&& item) {\n    auto waitSuccess = enqueueSemaphore_.try_wait();\n    if (!waitSuccess) {\n      return false;\n    }\n    enqueueReady(std::forward<U>(item));\n    dequeueSemaphore_.signal();\n    return true;\n  }\n\n  // Dequeue a value from the queue.\n  // Note that this operation can be safely cancelled by requesting cancellation\n  // on the awaiting coroutine's associated CancellationToken.\n  // If the operation is successfully cancelled then it will complete with\n  // an error of type folly::OperationCancelled.\n  // WARNING: It is not safe to wrap this with folly::coro::timeout(). Wrap with\n  // folly::coro::timeoutNoDiscard(), or use co_try_dequeue_for() instead.\n  folly::coro::Task<T> dequeue() {\n    co_await folly::coro::co_nothrow(dequeueSemaphore_.co_wait());\n    T item;\n    dequeueReady(item);\n    enqueueSemaphore_.signal();\n    co_return item;\n  }\n\n  // Try to dequeue a value from the queue with a timeout. The operation will\n  // either successfully dequeue an item from the queue, or else be cancelled\n  // and complete with an error of type folly::OperationCancelled.\n  template <typename Duration>\n  folly::coro::Task<T> co_try_dequeue_for(Duration timeout) {\n    co_await folly::coro::co_nothrow(\n        dequeueSemaphore_.co_try_wait_for(timeout));\n    T item;\n    dequeueReady(item);\n    enqueueSemaphore_.signal();\n    co_return item;\n  }\n\n  folly::coro::Task<void> dequeue(T& item) {\n    co_await folly::coro::co_nothrow(dequeueSemaphore_.co_wait());\n    dequeueReady(item);\n    enqueueSemaphore_.signal();\n  }\n\n  std::optional<T> try_dequeue() {\n    T item;\n    if (try_dequeue(item)) {\n      return item;\n    }\n    return std::nullopt;\n  }\n\n  bool try_dequeue(T& item) {\n    auto waitSuccess = dequeueSemaphore_.try_wait();\n    if (!waitSuccess) {\n      return false;\n    }\n    dequeueReady(item);\n    enqueueSemaphore_.signal();\n    return true;\n  }\n\n  bool empty() const { return queue_.isEmpty(); }\n\n  size_t size() const {\n    if constexpr (kSPSC) {\n      return queue_.sizeGuess();\n    } else {\n      return queue_.size();\n    }\n  }\n\n private:\n  template <typename U = T>\n  void enqueueReady(U&& item) {\n    if constexpr (kSPSC) {\n      CHECK(queue_.write(std::forward<U>(item)));\n    } else {\n      // Cannot use write() because the thread that acquired the next ticket may\n      // not have completed the read yet.\n      CHECK(queue_.writeIfNotFull(std::forward<U>(item)));\n    }\n  }\n\n  void dequeueReady(T& item) {\n    if constexpr (kSPSC) {\n      CHECK(queue_.read(item));\n    } else {\n      // Cannot use read() because the thread that acquired the next ticket may\n      // not have completed the write yet.\n      CHECK(queue_.readIfNotEmpty(item));\n    }\n  }\n\n  std::conditional_t<kSPSC, ProducerConsumerQueue<T>, MPMCQueue<T>> queue_;\n  folly::fibers::Semaphore enqueueSemaphore_;\n  folly::fibers::Semaphore dequeueSemaphore_;\n};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME accumulate\n  HEADERS\n    Accumulate-inl.h\n    Accumulate.h\n  EXPORTED_DEPS\n    folly_coro_async_generator\n    folly_coro_coroutine\n    folly_coro_task\n)\n\nfolly_add_library(\n  NAME async_generator\n  HEADERS\n    AsyncGenerator.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_auto_cleanup_fwd\n    folly_coro_base_promise\n    folly_coro_coroutine\n    folly_coro_detail_malloc\n    folly_coro_detail_manual_lifetime\n    folly_coro_invoke\n    folly_coro_result\n    folly_coro_scope_exit\n    folly_coro_via_if_async\n    folly_coro_with_async_stack\n    folly_coro_with_cancellation\n    folly_exception_wrapper\n    folly_lang_safe_alias_fwd\n    folly_tracing_async_stack\n    folly_traits\n    folly_try\n)\n\nfolly_add_library(\n  NAME async_pipe\n  HEADERS\n    AsyncPipe.h\n  EXPORTED_DEPS\n    folly_coro_async_generator\n    folly_coro_coroutine\n    folly_coro_invoke\n    folly_coro_small_unbounded_queue\n    folly_coro_via_if_async\n    folly_fibers_semaphore\n    folly_try\n)\n\nfolly_add_library(\n  NAME async_scope\n  HEADERS\n    AsyncScope.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_coroutine\n    folly_coro_current_executor\n    folly_coro_detail_barrier\n    folly_coro_detail_barrier_task\n    folly_coro_task\n    folly_exception_wrapper\n    folly_futures_core\n    folly_portability_source_location\n    folly_synchronization_relaxed_atomic\n)\n\nfolly_add_library(\n  NAME async_stack\n  HEADERS\n    AsyncStack.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_with_async_stack\n    folly_executor\n    folly_tracing_async_stack\n)\n\nfolly_add_library(\n  NAME auto_cleanup\n  HEADERS\n    AutoCleanup.h\n  EXPORTED_DEPS\n    folly_coro_auto_cleanup_fwd\n    folly_coro_collect\n    folly_coro_task\n)\n\nfolly_add_library(\n  NAME auto_cleanup_fwd\n  HEADERS\n    AutoCleanup-fwd.h\n  EXPORTED_DEPS\n    folly_coro_cleanup\n    folly_coro_coroutine\n)\n\nfolly_add_library(\n  NAME base_promise\n  HEADERS\n    BasePromise.h\n  EXPORTED_DEPS\n    folly_coro_current_executor\n    folly_coro_error\n    folly_coro_nothrow\n    folly_coro_value_or_error\n    folly_portability\n)\n\nfolly_add_library(\n  NAME baton\n  SRCS\n    Baton.cpp\n  HEADERS\n    Baton.h\n  DEPS\n    folly_synchronization_atomic_util\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_try\n)\n\nfolly_add_library(\n  NAME blocking_wait\n  HEADERS\n    BlockingWait.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_detail_malloc\n    folly_coro_detail_traits\n    folly_coro_task\n    folly_coro_traits\n    folly_coro_via_if_async\n    folly_coro_with_async_stack\n    folly_executors_manual_executor\n    folly_executors_sequenced_executor\n    folly_fibers_core\n    folly_lang_must_use_immediately\n    folly_synchronization_baton\n    folly_tracing_async_stack\n    folly_try\n)\n\nfolly_add_library(\n  NAME bounded_queue\n  HEADERS\n    BoundedQueue.h\n  EXPORTED_DEPS\n    folly_coro_task\n    folly_fibers_semaphore\n    folly_mpmc_queue\n    folly_producer_consumer_queue\n)\n\nfolly_add_library(\n  NAME cleanup\n  HEADERS\n    Cleanup.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n)\n\nfolly_add_library(\n  NAME collect\n  HEADERS\n    Collect-inl.h\n    Collect.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_container_access\n    folly_container_iterator\n    folly_coro_async_generator\n    folly_coro_async_pipe\n    folly_coro_async_scope\n    folly_coro_coroutine\n    folly_coro_detail_barrier\n    folly_coro_detail_barrier_task\n    folly_coro_detail_current_async_frame\n    folly_coro_detail_helpers\n    folly_coro_detail_pick_task_wrapper\n    folly_coro_detail_traits\n    folly_coro_mutex\n    folly_coro_safe_now_task\n    folly_coro_safe_safe_task\n    folly_coro_task\n    folly_coro_via_if_async\n    folly_exception_wrapper\n    folly_try\n    folly_unit\n)\n\nfolly_add_library(\n  NAME concat\n  HEADERS\n    Concat-inl.h\n    Concat.h\n  EXPORTED_DEPS\n    folly_coro_async_generator\n    folly_coro_coroutine\n)\n\nfolly_add_library(\n  NAME coroutine\n  HEADERS\n    Coroutine.h\n  EXPORTED_DEPS\n    folly_portability\n    folly_utility\n)\n\nfolly_add_library(\n  NAME current_executor\n  HEADERS\n    CurrentExecutor.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_via_if_async\n    folly_coro_with_async_stack\n    folly_executor\n    folly_io_async_request_context\n)\n\nfolly_add_library(\n  NAME detach_on_cancel\n  HEADERS\n    DetachOnCancel.h\n  EXPORTED_DEPS\n    folly_coro_baton\n    folly_coro_coroutine\n    folly_coro_detail_helpers\n    folly_coro_invoke\n    folly_coro_task\n    folly_coro_traits\n)\n\nfolly_add_library(\n  NAME detail_barrier\n  HEADERS\n    detail/Barrier.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_traits\n    folly_coro_with_async_stack\n    folly_tracing_async_stack\n)\n\nfolly_add_library(\n  NAME detail_barrier_task\n  HEADERS\n    detail/BarrierTask.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_detail_barrier\n    folly_coro_detail_malloc\n    folly_coro_with_async_stack\n)\n\nfolly_add_library(\n  NAME detail_current_async_frame\n  HEADERS\n    detail/CurrentAsyncFrame.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_with_async_stack\n    folly_executor\n    folly_tracing_async_stack\n)\n\nfolly_add_library(\n  NAME detail_helpers\n  HEADERS\n    detail/Helpers.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_executor\n    folly_io_async_request_context\n    folly_singleton_thread_local\n    folly_tracing_async_stack\n)\n\nfolly_add_library(\n  NAME detail_malloc\n  SRCS\n    detail/Malloc.cpp\n  HEADERS\n    detail/Malloc.h\n  DEPS\n    folly_lang_hint\n    folly_lang_new\n  EXPORTED_DEPS\n    folly_c_portability\n)\n\nfolly_add_library(\n  NAME detail_manual_lifetime\n  HEADERS\n    detail/ManualLifetime.h\n  EXPORTED_DEPS\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME detail_traits\n  HEADERS\n    detail/Traits.h\n  EXPORTED_DEPS\n    folly_traits\n)\n\nfolly_add_library(\n  NAME error\n  HEADERS\n    Error.h\n  EXPORTED_DEPS\n    folly_exception_wrapper\n    folly_operation_cancelled\n)\n\nfolly_add_library(\n  NAME filter\n  HEADERS\n    Filter-inl.h\n    Filter.h\n  EXPORTED_DEPS\n    folly_coro_async_generator\n    folly_coro_coroutine\n)\n\nfolly_add_library(\n  NAME future_util\n  HEADERS\n    FutureUtil.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_baton\n    folly_coro_coroutine\n    folly_coro_current_executor\n    folly_coro_invoke\n    folly_coro_task\n    folly_coro_traits\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME generator\n  HEADERS\n    Generator.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_invoke\n    folly_lang_exception\n)\n\nfolly_add_library(\n  NAME gmock_helpers\n  HEADERS\n    GmockHelpers.h\n  EXPORTED_DEPS\n    folly_coro_blocking_wait\n    folly_coro_coroutine\n    folly_coro_gtest_helpers\n    folly_coro_result\n    folly_coro_task\n    folly_portability_gmock\n)\n\nfolly_add_library(\n  NAME gtest_helpers\n  HEADERS\n    GtestHelpers.h\n  EXPORTED_DEPS\n    folly_coro_blocking_wait\n    folly_coro_coroutine\n    folly_coro_task\n    folly_debugging_exception_tracer_smart_exception_stack_trace_hooks\n    folly_debugging_exception_tracer_smart_exception_tracer\n    folly_portability_gtest\n)\n\nfolly_add_library(\n  NAME inline_task\n  HEADERS\n    detail/InlineTask.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_detail_malloc\n    folly_coro_with_async_stack\n    folly_lang_assume\n    folly_scope_guard\n    folly_tracing_async_stack\n    folly_try\n)\n\nfolly_add_library(\n  NAME invoke\n  HEADERS\n    Invoke.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_lang_customization_point\n)\n\nfolly_add_library(\n  NAME merge\n  HEADERS\n    Merge-inl.h\n    Merge.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_async_generator\n    folly_coro_baton\n    folly_coro_coroutine\n    folly_coro_detail_barrier\n    folly_coro_detail_barrier_task\n    folly_coro_detail_current_async_frame\n    folly_coro_detail_helpers\n    folly_coro_mutex\n    folly_coro_task\n    folly_coro_via_if_async\n    folly_coro_with_cancellation\n    folly_executor\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME mutex\n  SRCS\n    Mutex.cpp\n  HEADERS\n    Mutex.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_via_if_async\n    folly_executor\n)\n\nfolly_add_library(\n  NAME nothrow\n  HEADERS\n    Nothrow.h\n  EXPORTED_DEPS\n    folly_coro_via_if_async\n    folly_exception_wrapper\n    folly_portability\n)\n\nfolly_add_library(\n  NAME promise\n  HEADERS\n    Promise.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_baton\n    folly_coro_coroutine\n    folly_futures_core\n    folly_lang_safe_alias_fwd\n    folly_synchronization_relaxed_atomic\n    folly_try\n)\n\nfolly_add_library(\n  NAME result\n  HEADERS\n    Result.h\n  EXPORTED_DEPS\n    folly_coro_error\n    folly_result_try\n    folly_result_value_only_result\n    folly_try\n)\n\nfolly_add_library(\n  NAME retry\n  HEADERS\n    Retry.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_constexpr_math\n    folly_coro_coroutine\n    folly_coro_result\n    folly_coro_sleep\n    folly_coro_task\n    folly_coro_traits\n    folly_exception_wrapper\n    folly_random\n    folly_try\n)\n\nfolly_add_library(\n  NAME rust_adaptors\n  HEADERS\n    RustAdaptors.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_async_generator\n    folly_coro_task\n    folly_executor\n    folly_futures_core\n    folly_optional\n    folly_synchronization_baton\n)\n\nfolly_add_library(\n  NAME scope_exit\n  HEADERS\n    ScopeExit.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_traits\n    folly_coro_via_if_async\n    folly_exception_wrapper\n    folly_executor\n    folly_functional_invoke\n    folly_lang_assume\n    folly_lang_customization_point\n    folly_scope_guard\n    folly_tracing_async_stack\n)\n\nfolly_add_library(\n  NAME serial_queue_runner\n  SRCS\n    SerialQueueRunner.cpp\n  HEADERS\n    SerialQueueRunner.h\n  EXPORTED_DEPS\n    folly_coro_baton\n    folly_coro_task\n    folly_exception_wrapper\n)\n\nfolly_add_library(\n  NAME shared_lock\n  HEADERS\n    SharedLock.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_task\n    folly_portability\n    folly_synchronization_lock\n)\n\nfolly_add_library(\n  NAME shared_mutex\n  SRCS\n    SharedMutex.cpp\n  HEADERS\n    SharedMutex.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_shared_lock\n    folly_coro_via_if_async\n    folly_executor\n    folly_spin_lock\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME shared_promise\n  HEADERS\n    SharedPromise.h\n  EXPORTED_DEPS\n    folly_coro_promise\n    folly_futures_core\n    folly_likely\n    folly_small_vector\n    folly_synchronized\n    folly_utility\n)\n\nfolly_add_library(\n  NAME sleep\n  HEADERS\n    Sleep-inl.h\n    Sleep.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_future_util\n    folly_coro_task\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME small_unbounded_queue\n  HEADERS\n    SmallUnboundedQueue.h\n  EXPORTED_DEPS\n    folly_coro_baton\n    folly_coro_coroutine\n    folly_coro_mutex\n    folly_coro_task\n    folly_experimental_channels_detail_atomic_queue\n)\n\nfolly_add_library(\n  NAME synchronized\n  HEADERS\n    Synchronized.h\n  EXPORTED_DEPS\n    folly_coro_shared_lock\n    folly_coro_shared_mutex\n    folly_coro_task\n    folly_coro_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME task\n  HEADERS\n    Task.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_base_promise\n    folly_coro_coroutine\n    folly_coro_detail_malloc\n    folly_coro_detail_traits\n    folly_coro_inline_task\n    folly_coro_invoke\n    folly_coro_result\n    folly_coro_scope_exit\n    folly_coro_traits\n    folly_coro_via_if_async\n    folly_coro_with_async_stack\n    folly_coro_with_cancellation\n    folly_default_keep_alive_executor\n    folly_executor\n    folly_futures_core\n    folly_glog\n    folly_io_async_request_context\n    folly_lang_assume\n    folly_lang_must_use_immediately\n    folly_lang_safe_alias_fwd\n    folly_portability\n    folly_result_result\n    folly_result_try\n    folly_scope_guard\n    folly_tracing_async_stack\n    folly_traits\n    folly_try\n)\n\nfolly_add_library(\n  NAME task_wrapper\n  HEADERS\n    TaskWrapper.h\n  EXPORTED_DEPS\n    folly_coro_task\n    folly_lang_must_use_immediately\n)\n\nfolly_add_library(\n  NAME timed_wait\n  HEADERS\n    TimedWait.h\n  EXPORTED_DEPS\n    folly_coro_baton\n    folly_coro_coroutine\n    folly_coro_detail_helpers\n    folly_coro_invoke\n    folly_coro_task\n    folly_coro_traits\n    folly_futures_core\n    folly_optional\n)\n\nfolly_add_library(\n  NAME timeout\n  HEADERS\n    Timeout-inl.h\n    Timeout.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_baton\n    folly_coro_coroutine\n    folly_coro_detail_pick_task_wrapper\n    folly_coro_safe_now_task\n    folly_coro_safe_safe_task\n    folly_coro_task\n    folly_coro_traits\n    folly_coro_with_cancellation\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME traits\n  HEADERS\n    Traits.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_traits\n)\n\nfolly_add_library(\n  NAME transform\n  HEADERS\n    Transform-inl.h\n    Transform.h\n  EXPORTED_DEPS\n    folly_coro_async_generator\n    folly_coro_coroutine\n    folly_traits\n)\n\nfolly_add_library(\n  NAME unbounded_queue\n  HEADERS\n    UnboundedQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_coro_coroutine\n    folly_coro_task\n    folly_fibers_semaphore\n)\n\nfolly_add_library(\n  NAME value_or_error\n  HEADERS\n    ValueOrError.h\n  EXPORTED_DEPS\n    folly_coro_via_if_async\n    folly_result_try\n)\n\nfolly_add_library(\n  NAME value_or_fatal\n  HEADERS\n    ValueOrFatal.h\n  EXPORTED_DEPS\n    folly_coro_task_wrapper\n    folly_coro_via_if_async\n    folly_lang_assume\n    folly_result_result\n    folly_result_try\n    folly_result_value_only_result\n    folly_unit\n)\n\nfolly_add_library(\n  NAME via_if_async\n  HEADERS\n    ViaIfAsync.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_detail_malloc\n    folly_coro_traits\n    folly_coro_with_async_stack\n    folly_coro_with_cancellation\n    folly_executor\n    folly_io_async_request_context\n    folly_lang_customization_point\n    folly_lang_must_use_immediately\n    folly_lang_safe_alias_fwd\n    folly_tracing_async_stack\n    folly_traits\n)\n\nfolly_add_library(\n  NAME with_async_stack\n  HEADERS\n    WithAsyncStack.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n    folly_coro_traits\n    folly_functional_invoke\n    folly_lang_assume\n    folly_lang_customization_point\n    folly_tracing_async_stack\n)\n\nfolly_add_library(\n  NAME with_cancellation\n  HEADERS\n    WithCancellation.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_coro_coroutine\n    folly_lang_customization_point\n    folly_lang_must_use_immediately\n)\n\nadd_subdirectory(detail)\nadd_subdirectory(safe)\n"
  },
  {
    "path": "folly/coro/Cleanup.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/functional/Invoke.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\n/// A customization point that allows to provide an async cleanup function for a\n/// type. folly::coro::AutoCleanup uses co_cleanup_fn as the default cleanup\n/// function, so it is enough to define co_cleanup for a type to be able to use\n/// it with AutoCleanup.\nstruct co_cleanup_fn {\n  template <\n      typename T,\n      std::enable_if_t<\n          folly::is_tag_invocable_v<co_cleanup_fn, T&&> &&\n              !std::is_lvalue_reference_v<T>,\n          int> = 0>\n  auto operator()(T&& object) const\n      noexcept(folly::is_nothrow_tag_invocable_v<co_cleanup_fn, T&&>)\n          -> folly::tag_invoke_result_t<co_cleanup_fn, T&&> {\n    return folly::tag_invoke(co_cleanup_fn{}, std::forward<T>(object));\n  }\n\n  template <typename T>\n  void operator()(T& object) = delete;\n};\n\nFOLLY_DEFINE_CPO(co_cleanup_fn, co_cleanup)\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/Collect-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n#include <utility>\n\n#include <folly/CancellationToken.h>\n#include <folly/ExceptionWrapper.h>\n#include <folly/coro/AsyncPipe.h>\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/detail/Barrier.h>\n#include <folly/coro/detail/BarrierTask.h>\n#include <folly/coro/detail/CurrentAsyncFrame.h>\n#include <folly/coro/detail/Helpers.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\ntemplate <typename T>\nT&& getValueOrUnit(Try<T>&& value) {\n  assert(value.hasValue());\n  return std::move(value).value();\n}\n\ninline Unit getValueOrUnit([[maybe_unused]] Try<void>&& value) {\n  assert(value.hasValue());\n  return Unit{};\n}\n\ntemplate <\n    typename InputRange,\n    typename Make,\n    typename Iter = invoke_result_t<access::begin_fn, InputRange&>,\n    typename Elem = remove_cvref_t<decltype(*std::declval<Iter&>())>,\n    typename RTask = invoke_result_t<Make&, Elem, std::size_t>>\nstd::vector<RTask> collectMakeInnerTaskVec(InputRange& awaitables, Make& make) {\n  std::vector<RTask> tasks;\n\n  auto abegin = access::begin(awaitables);\n  auto aend = access::end(awaitables);\n\n  if constexpr (is_invocable_v<folly::access::size_fn, InputRange&>) {\n    tasks.reserve(static_cast<std::size_t>(folly::access::size(awaitables)));\n  } else if constexpr (range_has_known_distance_v<InputRange&>) {\n    tasks.reserve(static_cast<std::size_t>(std::distance(abegin, aend)));\n  }\n\n  std::size_t index = 0;\n  for (auto aiter = abegin; aiter != aend; ++aiter) {\n    tasks.push_back(make(std::move(*aiter), index++));\n  }\n\n  return tasks;\n}\n\nnamespace collect_detail {\n// Detection trait for whether co_awaitTry can be applied to a type\ntemplate <typename T>\nusing detect_co_awaitTry_t = decltype(co_awaitTry(std::declval<T>()));\n\ntemplate <typename T>\ninline constexpr bool has_co_awaitTry_v =\n    folly::is_detected_v<detect_co_awaitTry_t, T>;\n} // namespace collect_detail\n\ntemplate <typename SemiAwaitableMover, typename Result>\nBarrierTask makeCollectAllTryTask(\n    Executor::KeepAlive<> executor,\n    const CancellationToken& cancelToken,\n    SemiAwaitableMover&& mover,\n    Try<Result>& result) {\n  try {\n    if constexpr (std::is_void_v<Result>) {\n      co_await co_viaIfAsync(\n          std::move(executor),\n          co_withCancellation(\n              cancelToken, static_cast<SemiAwaitableMover&&>(mover)()));\n      result.emplace();\n    } else {\n      result.emplace(\n          co_await co_viaIfAsync(\n              std::move(executor),\n              co_withCancellation(\n                  cancelToken, static_cast<SemiAwaitableMover&&>(mover)())));\n    }\n  } catch (...) {\n    result.emplaceException(current_exception());\n  }\n}\n\ntemplate <\n    typename Ret,\n    typename... SemiAwaitables,\n    size_t... Indices,\n    typename... SemiAwaitablesMovers>\nRet collectAllTryImpl(\n    tag_t<Ret, SemiAwaitables...>,\n    std::index_sequence<Indices...>,\n    SemiAwaitablesMovers... movers) {\n  static_assert(sizeof...(Indices) == sizeof...(SemiAwaitables));\n  static_assert(sizeof...(Indices) == sizeof...(SemiAwaitablesMovers));\n  if constexpr (sizeof...(SemiAwaitables) == 0) {\n    co_return std::tuple<>{};\n  } else {\n    const Executor::KeepAlive<> executor = co_await co_current_executor;\n    const CancellationToken& cancelToken =\n        co_await co_current_cancellation_token;\n\n    std::tuple<collect_all_try_component_t<SemiAwaitables>...> results;\n\n    folly::coro::detail::BarrierTask tasks[sizeof...(SemiAwaitables)] = {\n        makeCollectAllTryTask(\n            executor.get_alias(),\n            cancelToken,\n            static_cast<SemiAwaitablesMovers&&>(movers),\n            std::get<Indices>(results))...,\n    };\n\n    folly::coro::detail::Barrier barrier{sizeof...(SemiAwaitables) + 1};\n\n    auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n    // Use std::initializer_list to ensure that the sub-tasks are launched\n    // in the order they appear in the parameter pack.\n\n    // Save the initial context and restore it after starting each task\n    // as the task may have modified the context before suspending and we\n    // want to make sure the next task is started with the same initial\n    // context.\n    const auto context = RequestContext::saveContext();\n    (void)std::initializer_list<int>{(\n        tasks[Indices].start(&barrier, asyncFrame),\n        RequestContext::setContext(context),\n        0)...};\n\n    // Wait for all of the sub-tasks to finish execution.\n    // Should be safe to avoid an executor transition here even if the\n    // operation completes asynchronously since all of the child tasks\n    // should already have transitioned to the correct executor due to\n    // the use of co_viaIfAsync() within makeCollectAllTryTask().\n    co_await UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n\n    co_return results;\n  }\n}\n\ntemplate <\n    typename Ret,\n    typename... SemiAwaitables,\n    size_t... Indices,\n    typename... SemiFns>\nRet collectAllImpl(\n    tag_t<Ret, SemiAwaitables...>,\n    std::index_sequence<Indices...>,\n    // `semiFns()` is the immovable, must-use-immediately `SemiAwaitable`\n    SemiFns... semiFns) {\n  if constexpr (sizeof...(SemiAwaitables) == 0) {\n    co_return std::tuple<>{};\n  } else {\n    const Executor::KeepAlive<> executor = co_await co_current_executor;\n    const CancellationToken& parentCancelToken =\n        co_await co_current_cancellation_token;\n\n    const CancellationSource cancelSource;\n    const CancellationToken cancelToken =\n        cancellation_token_merge(parentCancelToken, cancelSource.getToken());\n\n    exception_wrapper firstException;\n\n    auto makeTask = [&](auto&& fn, auto& result) -> BarrierTask {\n      using await_result =\n          semi_await_result_t<decltype(static_cast<decltype(fn)>(fn)())>;\n      try {\n        if constexpr (std::is_void_v<await_result>) {\n          co_await co_viaIfAsync(\n              executor.get_alias(),\n              co_withCancellation(\n                  cancelToken, static_cast<decltype(fn)>(fn)()));\n          result.emplace();\n        } else {\n          result.emplace(\n              co_await co_viaIfAsync(\n                  executor.get_alias(),\n                  co_withCancellation(\n                      cancelToken, static_cast<decltype(fn)>(fn)())));\n        }\n      } catch (...) {\n        if (!cancelSource.requestCancellation()) {\n          // This was the first failure, remember its error.\n          firstException = exception_wrapper{current_exception()};\n        }\n      }\n    };\n\n    std::tuple<collect_all_try_component_t<SemiAwaitables>...> results;\n\n    folly::coro::detail::BarrierTask tasks[sizeof...(SemiAwaitables)] = {\n        makeTask(\n            static_cast<SemiFns&&>(semiFns), std::get<Indices>(results))...,\n    };\n\n    folly::coro::detail::Barrier barrier{sizeof...(SemiAwaitables) + 1};\n\n    // Save the initial context and restore it after starting each task\n    // as the task may have modified the context before suspending and we\n    // want to make sure the next task is started with the same initial\n    // context.\n    const auto context = RequestContext::saveContext();\n\n    auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n    // Use std::initializer_list to ensure that the sub-tasks are launched\n    // in the order they appear in the parameter pack.\n    (void)std::initializer_list<int>{(\n        tasks[Indices].start(&barrier, asyncFrame),\n        RequestContext::setContext(context),\n        0)...};\n\n    // Wait for all of the sub-tasks to finish execution.\n    // Should be safe to avoid an executor transition here even if the\n    // operation completes asynchronously since all of the child tasks\n    // should already have transitioned to the correct executor due to\n    // the use of co_viaIfAsync() within makeBarrierTask().\n    co_await UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n\n    if (firstException) {\n      co_yield co_error(std::move(firstException));\n    }\n\n    co_return std::tuple<collect_all_component_t<SemiAwaitables>...>{\n        getValueOrUnit(std::get<Indices>(std::move(results)))...};\n  }\n}\n\ntemplate <typename InputRange, typename IsTry, typename AsyncScope>\nauto makeUnorderedAsyncGeneratorImpl(\n    AsyncScope& scope, InputRange awaitables, IsTry) {\n  using Item =\n      async_generator_from_awaitable_range_item_t<InputRange, IsTry::value>;\n  return [](AsyncScope& scopeParam,\n            InputRange awaitablesParam) -> AsyncGenerator<Item&&> {\n    auto [results, pipe] = AsyncPipe<Item, false>::create();\n    struct SharedState {\n      explicit SharedState(AsyncPipe<Item, false>&& p) : pipe(std::move(p)) {}\n\n      AsyncPipe<Item, false> pipe;\n      const CancellationSource cancelSource;\n    };\n    auto sharedState = std::make_shared<SharedState>(std::move(pipe));\n    auto cancelToken = sharedState->cancelSource.getToken();\n\n    auto guard = folly::makeGuard([&] {\n      sharedState->cancelSource.requestCancellation();\n    });\n    auto ex = co_await co_current_executor;\n    size_t expected = 0;\n    // Save the initial context and restore it after starting each task\n    // as the task may have modified the context before suspending and we\n    // want to make sure the next task is started with the same initial\n    // context.\n    const auto context = RequestContext::saveContext();\n\n    for (auto&& semiAwaitable : static_cast<InputRange&&>(awaitablesParam)) {\n      auto task = [](auto semiAwaitableParam, auto state) -> Task<void> {\n        auto result = co_await co_awaitTry(std::move(semiAwaitableParam));\n        if (!result.hasValue() && !IsTry::value) {\n          state->cancelSource.requestCancellation();\n        }\n        state->pipe.write(std::move(result));\n      }(static_cast<decltype(semiAwaitable)&&>(semiAwaitable), sharedState);\n      if constexpr (std::is_same_v<AsyncScope, folly::coro::AsyncScope>) {\n        scopeParam.add(co_withExecutor(\n            ex, co_withCancellation(cancelToken, std::move(task))));\n      } else {\n        static_assert(std::is_same_v<AsyncScope, CancellableAsyncScope>);\n        scopeParam.add(co_withExecutor(ex, std::move(task)), cancelToken);\n      }\n      ++expected;\n      RequestContext::setContext(context);\n    }\n\n    while (expected > 0) {\n      CancellationCallback cancelCallback(\n          co_await co_current_cancellation_token,\n          [&]() noexcept { sharedState->cancelSource.requestCancellation(); });\n\n      if constexpr (!IsTry::value) {\n        auto result = co_await co_awaitTry(results.next());\n        if (result.hasValue() && result->has_value()) {\n          co_yield std::move(**result);\n          if (--expected) {\n            continue;\n          }\n          result.emplace(); // completion result\n        }\n        guard.dismiss();\n        co_yield co_result(std::move(result));\n      } else {\n        // Prevent AsyncPipe from receiving cancellation so we get the right\n        // number of OperationCancelled.\n        auto result = co_await co_withCancellation({}, results.next());\n        co_yield std::move(*result);\n        if (--expected == 0) {\n          guard.dismiss();\n          co_return;\n        }\n      }\n    }\n  }(scope, std::move(awaitables));\n}\n\ntemplate <typename... SemiAwaitables, size_t... Indices>\nauto collectAnyImpl(\n    std::index_sequence<Indices...>, SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::pair<\n        std::size_t,\n        folly::Try<collect_any_component_t<SemiAwaitables...>>>> {\n  const CancellationToken& parentCancelToken =\n      co_await co_current_cancellation_token;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken =\n      cancellation_token_merge(parentCancelToken, cancelSource.getToken());\n\n  std::pair<std::size_t, folly::Try<collect_any_component_t<SemiAwaitables...>>>\n      firstCompletion;\n  firstCompletion.first = size_t(-1);\n  co_await folly::coro::collectAll(\n      folly::coro::co_withCancellation(\n          cancelToken,\n          folly::coro::co_invoke(\n              [&, aw = static_cast<SemiAwaitables&&>(awaitables)]() mutable\n                  -> folly::coro::Task<void> {\n                auto result = co_await folly::coro::co_awaitTry(\n                    static_cast<SemiAwaitables&&>(aw));\n                if (!cancelSource.requestCancellation()) {\n                  // This is first entity to request cancellation.\n                  firstCompletion.first = Indices;\n                  firstCompletion.second = std::move(result);\n                }\n              }))...);\n\n  co_return firstCompletion;\n}\n\ntemplate <typename... SemiAwaitables, size_t... Indices>\nauto collectAnyWithoutExceptionImpl(\n    std::index_sequence<Indices...>, SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::pair<\n        std::size_t,\n        folly::Try<detail::collect_any_component_t<SemiAwaitables...>>>> {\n  const CancellationToken& parentCancelToken =\n      co_await co_current_cancellation_token;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken =\n      cancellation_token_merge(parentCancelToken, cancelSource.getToken());\n\n  constexpr std::size_t nAwaitables = sizeof...(SemiAwaitables);\n  std::atomic<std::size_t> nAwaited = 1;\n  std::pair<std::size_t, folly::Try<collect_any_component_t<SemiAwaitables...>>>\n      firstValueOrLastException;\n  firstValueOrLastException.first = std::numeric_limits<size_t>::max();\n  co_await folly::coro::collectAll(\n      folly::coro::co_withCancellation(\n          cancelToken, [&]() -> folly::coro::Task<void> {\n            auto result = co_await folly::coro::co_awaitTry(\n                std::forward<SemiAwaitables>(awaitables));\n            if ((result.hasValue() ||\n                 nAwaited.fetch_add(1, std::memory_order_relaxed) ==\n                     nAwaitables) &&\n                !cancelSource.requestCancellation()) {\n              firstValueOrLastException.first = Indices;\n              firstValueOrLastException.second = std::move(result);\n            }\n          }())...);\n\n  co_return firstValueOrLastException;\n}\n\ntemplate <typename... SemiAwaitables, size_t... Indices>\nauto collectAnyNoDiscardImpl(\n    std::index_sequence<Indices...>, SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<\n        std::tuple<collect_all_try_component_t<SemiAwaitables>...>> {\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken = cancellation_token_merge(\n      co_await co_current_cancellation_token, cancelSource.getToken());\n\n  std::tuple<collect_all_try_component_t<SemiAwaitables>...> results;\n  co_await folly::coro::collectAll(\n      folly::coro::co_withCancellation(\n          cancelToken, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n            auto result = co_await folly::coro::co_awaitTry(\n                std::forward<SemiAwaitables>(awaitables));\n            cancelSource.requestCancellation();\n            std::get<Indices>(results) = std::move(result);\n          }))...);\n\n  co_return results;\n}\n\n} // namespace detail\n\ntemplate <typename... SemiAwaitables>\nauto collectAll(SemiAwaitables... awaitables)\n    -> detail::CollectAllTask<SemiAwaitables...> {\n  return detail::collectAllImpl(\n      tag<detail::CollectAllTask<SemiAwaitables...>, SemiAwaitables...>,\n      std::make_index_sequence<sizeof...(SemiAwaitables)>{},\n      folly::ext::must_use_immediately_unsafe_mover(\n          static_cast<SemiAwaitables&&>(awaitables))...);\n}\n\ntemplate <typename... SemiAwaitables>\nauto collectAllTry(SemiAwaitables... awaitables)\n    -> detail::CollectAllTryTask<SemiAwaitables...> {\n  return detail::collectAllTryImpl(\n      tag<detail::CollectAllTryTask<SemiAwaitables...>, SemiAwaitables...>,\n      std::make_index_sequence<sizeof...(SemiAwaitables)>{},\n      folly::ext::must_use_immediately_unsafe_mover(\n          static_cast<SemiAwaitables&&>(awaitables))...);\n}\n\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        !std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int>>\nauto collectAllRange(InputRange awaitables)\n    -> folly::coro::Task<std::vector<detail::collect_all_range_component_t<\n        detail::range_reference_t<InputRange>>>> {\n  const folly::Executor::KeepAlive<> executor = co_await co_current_executor;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken = cancellation_token_merge(\n      co_await co_current_cancellation_token, cancelSource.getToken());\n\n  std::vector<detail::collect_all_try_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      tryResults;\n\n  exception_wrapper firstException;\n\n  using awaitable_type = remove_cvref_t<detail::range_reference_t<InputRange>>;\n  auto makeTask = [&](awaitable_type semiAwaitable, std::size_t index)\n      -> detail::BarrierTask {\n    assert(index < tryResults.size());\n\n    try {\n      tryResults[index].emplace(\n          co_await co_viaIfAsync(\n              executor.get_alias(),\n              co_withCancellation(cancelToken, std::move(semiAwaitable))));\n    } catch (...) {\n      if (!cancelSource.requestCancellation()) {\n        firstException = exception_wrapper{current_exception()};\n      }\n    }\n  };\n\n  auto tasks = detail::collectMakeInnerTaskVec(awaitables, makeTask);\n\n  tryResults.resize(tasks.size());\n\n  // Save the initial context and restore it after starting each task\n  // as the task may have modified the context before suspending and we\n  // want to make sure the next task is started with the same initial\n  // context.\n  const auto context = RequestContext::saveContext();\n\n  auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n  // Launch the tasks and wait for them all to finish.\n  {\n    detail::Barrier barrier{tasks.size() + 1};\n    for (auto&& task : tasks) {\n      task.start(&barrier, asyncFrame);\n      RequestContext::setContext(context);\n    }\n    co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n  }\n\n  // Check if there were any exceptions and rethrow the first one.\n  if (firstException) {\n    co_yield co_error(std::move(firstException));\n  }\n\n  std::vector<detail::collect_all_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      results;\n  results.reserve(tryResults.size());\n  for (auto& result : tryResults) {\n    results.emplace_back(std::move(result).value());\n  }\n\n  co_return results;\n}\n\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int>>\nauto collectAllRange(InputRange awaitables) -> folly::coro::Task<void> {\n  const folly::Executor::KeepAlive<> executor = co_await co_current_executor;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken = cancellation_token_merge(\n      co_await co_current_cancellation_token, cancelSource.getToken());\n\n  exception_wrapper firstException;\n\n  using awaitable_type = remove_cvref_t<detail::range_reference_t<InputRange>>;\n  auto makeTask =\n      [&](awaitable_type semiAwaitable, std::size_t) -> detail::BarrierTask {\n    try {\n      co_await co_viaIfAsync(\n          executor.get_alias(),\n          co_withCancellation(cancelToken, std::move(semiAwaitable)));\n    } catch (...) {\n      if (!cancelSource.requestCancellation()) {\n        firstException = exception_wrapper{current_exception()};\n      }\n    }\n  };\n\n  auto tasks = detail::collectMakeInnerTaskVec(awaitables, makeTask);\n\n  // Save the initial context and restore it after starting each task\n  // as the task may have modified the context before suspending and we\n  // want to make sure the next task is started with the same initial\n  // context.\n  const auto context = RequestContext::saveContext();\n\n  auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n  // Launch the tasks and wait for them all to finish.\n  {\n    detail::Barrier barrier{tasks.size() + 1};\n    for (auto&& task : tasks) {\n      task.start(&barrier, asyncFrame);\n      RequestContext::setContext(context);\n    }\n    co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n  }\n\n  // Check if there were any exceptions and rethrow the first one.\n  if (firstException) {\n    co_yield co_error(std::move(firstException));\n  }\n}\n\ntemplate <typename InputRange>\nauto collectAllTryRange(InputRange awaitables)\n    -> folly::coro::Task<std::vector<detail::collect_all_try_range_component_t<\n        detail::range_reference_t<InputRange>>>> {\n  std::vector<detail::collect_all_try_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      results;\n\n  const folly::Executor::KeepAlive<> executor =\n      folly::getKeepAliveToken(co_await co_current_executor);\n\n  const CancellationToken& cancelToken = co_await co_current_cancellation_token;\n\n  using awaitable_type = remove_cvref_t<detail::range_reference_t<InputRange>>;\n  auto makeTask = [&](awaitable_type semiAwaitable, std::size_t index)\n      -> detail::BarrierTask {\n    assert(index < results.size());\n    auto& result = results[index];\n    if constexpr (detail::collect_detail::has_co_awaitTry_v<awaitable_type>) {\n      // Fast path: use co_awaitTry to avoid exception rethrow overhead\n      result = co_await co_awaitTry(co_viaIfAsync(\n          executor.get_alias(),\n          co_withCancellation(cancelToken, std::move(semiAwaitable))));\n    } else {\n      // Fallback: use try-catch for awaitables without co_awaitTry support\n      try {\n        using await_result = semi_await_result_t<awaitable_type>;\n        if constexpr (std::is_void_v<await_result>) {\n          co_await co_viaIfAsync(\n              executor.get_alias(),\n              co_withCancellation(cancelToken, std::move(semiAwaitable)));\n          result.emplace();\n        } else {\n          result.emplace(\n              co_await co_viaIfAsync(\n                  executor.get_alias(),\n                  co_withCancellation(cancelToken, std::move(semiAwaitable))));\n        }\n      } catch (...) {\n        result.emplaceException(current_exception());\n      }\n    }\n  };\n\n  auto tasks = detail::collectMakeInnerTaskVec(awaitables, makeTask);\n\n  // Now that we know how many tasks there are, allocate that\n  // many Try objects to store the results before we start\n  // executing the tasks.\n  results.resize(tasks.size());\n\n  // Save the initial context and restore it after starting each task\n  // as the task may have modified the context before suspending and we\n  // want to make sure the next task is started with the same initial\n  // context.\n  const auto context = RequestContext::saveContext();\n\n  auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n  // Launch the tasks and wait for them all to finish.\n  {\n    detail::Barrier barrier{tasks.size() + 1};\n    for (auto&& task : tasks) {\n      task.start(&barrier, asyncFrame);\n      RequestContext::setContext(context);\n    }\n    co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n  }\n\n  co_return results;\n}\n\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int>>\nauto collectAllWindowed(InputRange awaitables, std::size_t maxConcurrency)\n    -> folly::coro::Task<void> {\n  assert(maxConcurrency > 0);\n\n  const folly::Executor::KeepAlive<> executor = co_await co_current_executor;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken = cancellation_token_merge(\n      co_await co_current_cancellation_token, cancelSource.getToken());\n\n  exception_wrapper firstException;\n\n  const auto trySetFirstException = [&](exception_wrapper&& e) noexcept {\n    if (!cancelSource.requestCancellation()) {\n      // This is first entity to request cancellation.\n      firstException = std::move(e);\n    }\n  };\n\n  auto iter = access::begin(awaitables);\n  const auto iterEnd = access::end(awaitables);\n\n  using iterator_t = decltype(iter);\n  using awaitable_t = typename std::iterator_traits<iterator_t>::value_type;\n\n  folly::coro::Mutex mutex;\n\n  exception_wrapper iterationException;\n\n  auto makeWorker = [&]() -> detail::BarrierTask {\n    auto lock =\n        co_await co_viaIfAsync(executor.get_alias(), mutex.co_scoped_lock());\n\n    while (!iterationException && iter != iterEnd) {\n      std::optional<awaitable_t> awaitable;\n      try {\n        awaitable.emplace(*iter);\n        ++iter;\n      } catch (...) {\n        iterationException = exception_wrapper{current_exception()};\n        cancelSource.requestCancellation();\n      }\n\n      if (!awaitable) {\n        co_return;\n      }\n\n      lock.unlock();\n\n      try {\n        co_await co_viaIfAsync(\n            executor.get_alias(),\n            co_withCancellation(cancelToken, std::move(*awaitable)));\n      } catch (...) {\n        trySetFirstException(exception_wrapper{current_exception()});\n      }\n\n      lock =\n          co_await co_viaIfAsync(executor.get_alias(), mutex.co_scoped_lock());\n    }\n  };\n\n  std::vector<detail::BarrierTask> workerTasks;\n\n  detail::Barrier barrier{1};\n\n  // Save the initial context and restore it after starting each task\n  // as the task may have modified the context before suspending and we\n  // want to make sure the next task is started with the same initial\n  // context.\n  const auto context = RequestContext::saveContext();\n\n  auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n  try {\n    auto lock = co_await mutex.co_scoped_lock();\n\n    while (!iterationException && iter != iterEnd &&\n           workerTasks.size() < maxConcurrency) {\n      // Unlock the mutex before starting the worker so that\n      // it can consume as many results synchronously as it can before\n      // returning here and letting us spawn another task.\n      // This can avoid spawning more worker coroutines than is necessary\n      // to consume all of the awaitables.\n      lock.unlock();\n\n      workerTasks.push_back(makeWorker());\n      barrier.add(1);\n      workerTasks.back().start(&barrier, asyncFrame);\n\n      RequestContext::setContext(context);\n\n      lock = co_await mutex.co_scoped_lock();\n    }\n  } catch (...) {\n    if (workerTasks.empty()) {\n      iterationException = exception_wrapper{current_exception()};\n    }\n  }\n\n  co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n\n  if (auto& ex = iterationException ? iterationException : firstException) {\n    co_yield co_error(std::move(ex));\n  }\n}\n\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        !std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int>>\nauto collectAllWindowed(InputRange awaitables, std::size_t maxConcurrency)\n    -> folly::coro::Task<std::vector<detail::collect_all_range_component_t<\n        detail::range_reference_t<InputRange>>>> {\n  assert(maxConcurrency > 0);\n\n  const folly::Executor::KeepAlive<> executor = co_await co_current_executor;\n\n  const CancellationToken& parentCancelToken =\n      co_await co_current_cancellation_token;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken =\n      cancellation_token_merge(parentCancelToken, cancelSource.getToken());\n\n  exception_wrapper firstException;\n\n  auto trySetFirstException = [&](exception_wrapper&& e) noexcept {\n    if (!cancelSource.requestCancellation()) {\n      // This is first entity to request cancellation.\n      firstException = std::move(e);\n    }\n  };\n\n  auto iter = access::begin(awaitables);\n  const auto iterEnd = access::end(awaitables);\n\n  using iterator_t = decltype(iter);\n  using awaitable_t = typename std::iterator_traits<iterator_t>::value_type;\n\n  folly::coro::Mutex mutex;\n\n  std::vector<detail::collect_all_try_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      tryResults;\n\n  if constexpr (is_invocable_v<folly::access::size_fn, InputRange&>) {\n    tryResults.reserve(\n        static_cast<std::size_t>(folly::access::size(awaitables)));\n  } else if constexpr (range_has_known_distance_v<InputRange&>) {\n    tryResults.reserve(static_cast<std::size_t>(std::distance(iter, iterEnd)));\n  }\n\n  exception_wrapper iterationException;\n\n  auto makeWorker = [&]() -> detail::BarrierTask {\n    auto lock =\n        co_await co_viaIfAsync(executor.get_alias(), mutex.co_scoped_lock());\n\n    while (!iterationException && iter != iterEnd) {\n      const std::size_t thisIndex = tryResults.size();\n      std::optional<awaitable_t> awaitable;\n      try {\n        tryResults.emplace_back();\n        awaitable.emplace(*iter);\n        ++iter;\n      } catch (...) {\n        iterationException = exception_wrapper{current_exception()};\n        cancelSource.requestCancellation();\n      }\n\n      if (!awaitable) {\n        co_return;\n      }\n\n      lock.unlock();\n\n      detail::collect_all_try_range_component_t<\n          detail::range_reference_t<InputRange>>\n          tryResult;\n\n      try {\n        tryResult.emplace(\n            co_await co_viaIfAsync(\n                executor.get_alias(),\n                co_withCancellation(\n                    cancelToken, static_cast<awaitable_t&&>(*awaitable))));\n      } catch (...) {\n        trySetFirstException(exception_wrapper{current_exception()});\n      }\n\n      lock =\n          co_await co_viaIfAsync(executor.get_alias(), mutex.co_scoped_lock());\n\n      try {\n        tryResults[thisIndex] = std::move(tryResult);\n      } catch (...) {\n        trySetFirstException(exception_wrapper{current_exception()});\n      }\n    }\n  };\n\n  std::vector<detail::BarrierTask> workerTasks;\n\n  detail::Barrier barrier{1};\n\n  exception_wrapper workerCreationException;\n\n  // Save the initial context and restore it after starting each task\n  // as the task may have modified the context before suspending and we\n  // want to make sure the next task is started with the same initial\n  // context.\n  const auto context = RequestContext::saveContext();\n\n  auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n  try {\n    auto lock = co_await mutex.co_scoped_lock();\n\n    while (!iterationException && iter != iterEnd &&\n           workerTasks.size() < maxConcurrency) {\n      // Unlock the mutex before starting the worker so that\n      // it can consume as many results synchronously as it can before\n      // returning here and letting us spawn another task.\n      // This can avoid spawning more worker coroutines than is necessary\n      // to consume all of the awaitables.\n      lock.unlock();\n\n      workerTasks.push_back(makeWorker());\n      barrier.add(1);\n      workerTasks.back().start(&barrier, asyncFrame);\n\n      RequestContext::setContext(context);\n\n      lock = co_await mutex.co_scoped_lock();\n    }\n  } catch (...) {\n    // Only a fatal error if we failed to create any worker tasks.\n    if (workerTasks.empty()) {\n      // No need to synchronise here. There are no concurrent tasks running.\n      iterationException = exception_wrapper{current_exception()};\n    }\n  }\n\n  co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n\n  if (auto& ex = iterationException ? iterationException : firstException) {\n    co_yield co_error(std::move(ex));\n  }\n\n  std::vector<detail::collect_all_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      results;\n  results.reserve(tryResults.size());\n\n  for (auto&& tryResult : tryResults) {\n    assert(tryResult.hasValue());\n    results.emplace_back(std::move(tryResult).value());\n  }\n\n  co_return results;\n}\n\ntemplate <typename InputRange>\nauto collectAllTryWindowed(InputRange awaitables, std::size_t maxConcurrency)\n    -> folly::coro::Task<std::vector<detail::collect_all_try_range_component_t<\n        detail::range_reference_t<InputRange>>>> {\n  assert(maxConcurrency > 0);\n\n  std::vector<detail::collect_all_try_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      results;\n\n  exception_wrapper iterationException;\n\n  folly::coro::Mutex mutex;\n\n  const Executor::KeepAlive<> executor = co_await co_current_executor;\n  const CancellationToken& cancelToken = co_await co_current_cancellation_token;\n\n  auto iter = access::begin(awaitables);\n  const auto iterEnd = access::end(awaitables);\n\n  using iterator_t = decltype(iter);\n  using awaitable_t = typename std::iterator_traits<iterator_t>::value_type;\n  using result_t = semi_await_result_t<awaitable_t>;\n\n  auto makeWorker = [&]() -> detail::BarrierTask {\n    auto lock =\n        co_await co_viaIfAsync(executor.get_alias(), mutex.co_scoped_lock());\n\n    while (!iterationException && iter != iterEnd) {\n      const std::size_t thisIndex = results.size();\n      std::optional<awaitable_t> awaitable;\n\n      try {\n        results.emplace_back();\n        awaitable.emplace(*iter);\n        ++iter;\n      } catch (...) {\n        iterationException = exception_wrapper{current_exception()};\n      }\n\n      if (!awaitable) {\n        co_return;\n      }\n\n      lock.unlock();\n\n      detail::collect_all_try_range_component_t<\n          detail::range_reference_t<InputRange>>\n          result;\n\n      try {\n        if constexpr (std::is_void_v<result_t>) {\n          co_await co_viaIfAsync(\n              executor.get_alias(),\n              co_withCancellation(cancelToken, std::move(*awaitable)));\n          result.emplace();\n        } else {\n          result.emplace(\n              co_await co_viaIfAsync(\n                  executor.get_alias(),\n                  co_withCancellation(cancelToken, std::move(*awaitable))));\n        }\n      } catch (...) {\n        result.emplaceException(current_exception());\n      }\n\n      lock =\n          co_await co_viaIfAsync(executor.get_alias(), mutex.co_scoped_lock());\n\n      try {\n        results[thisIndex] = std::move(result);\n      } catch (...) {\n        results[thisIndex].emplaceException(current_exception());\n      }\n    }\n  };\n\n  std::vector<detail::BarrierTask> workerTasks;\n\n  detail::Barrier barrier{1};\n\n  // Save the initial context and restore it after starting each task\n  // as the task may have modified the context before suspending and we\n  // want to make sure the next task is started with the same initial\n  // context.\n  const auto context = RequestContext::saveContext();\n\n  auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n  try {\n    auto lock = co_await mutex.co_scoped_lock();\n    while (!iterationException && iter != iterEnd &&\n           workerTasks.size() < maxConcurrency) {\n      // Unlock the mutex before starting the child operation so that\n      // it can consume as many results synchronously as it can before\n      // returning here and letting us potentially spawn another task.\n      // This can avoid spawning more worker coroutines than is necessary\n      // to consume all of the awaitables.\n      lock.unlock();\n\n      workerTasks.push_back(makeWorker());\n      barrier.add(1);\n      workerTasks.back().start(&barrier, asyncFrame);\n\n      RequestContext::setContext(context);\n\n      lock = co_await mutex.co_scoped_lock();\n    }\n  } catch (...) {\n    // Failure to create a worker is an error if we failed\n    // to create _any_ workers. As long as we created one then\n    // the algorithm should still be able to make forward progress.\n    if (workerTasks.empty()) {\n      iterationException = exception_wrapper{current_exception()};\n    }\n  }\n\n  co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n\n  if (iterationException) {\n    co_yield co_error(std::move(iterationException));\n  }\n\n  co_return results;\n}\n\ntemplate <typename InputRange>\nauto makeUnorderedAsyncGenerator(AsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        false>&&> {\n  return detail::makeUnorderedAsyncGeneratorImpl(\n      scope, std::move(awaitables), std::bool_constant<false>{});\n}\n\ntemplate <typename InputRange>\nauto makeUnorderedTryAsyncGenerator(AsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        true>&&> {\n  return detail::makeUnorderedAsyncGeneratorImpl(\n      scope, std::move(awaitables), std::bool_constant<true>{});\n}\n\ntemplate <typename InputRange>\nauto makeUnorderedAsyncGenerator(\n    CancellableAsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        false>&&> {\n  return detail::makeUnorderedAsyncGeneratorImpl(\n      scope, std::move(awaitables), std::bool_constant<false>{});\n}\n\ntemplate <typename InputRange>\nauto makeUnorderedTryAsyncGenerator(\n    CancellableAsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        true>&&> {\n  return detail::makeUnorderedAsyncGeneratorImpl(\n      scope, std::move(awaitables), std::bool_constant<true>{});\n}\n\ntemplate <typename SemiAwaitable, typename... SemiAwaitables>\nauto collectAny(SemiAwaitable&& awaitable, SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::pair<\n        std::size_t,\n        folly::Try<detail::collect_any_component_t<\n            SemiAwaitable,\n            SemiAwaitables...>>>> {\n  return detail::collectAnyImpl(\n      std::make_index_sequence<sizeof...(SemiAwaitables) + 1>{},\n      static_cast<SemiAwaitable&&>(awaitable),\n      static_cast<SemiAwaitables&&>(awaitables)...);\n}\n\ntemplate <typename... SemiAwaitables>\nauto collectAnyWithoutException(SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::pair<\n        std::size_t,\n        folly::Try<detail::collect_any_component_t<SemiAwaitables...>>>> {\n  return detail::collectAnyWithoutExceptionImpl(\n      std::make_index_sequence<sizeof...(SemiAwaitables)>{},\n      static_cast<SemiAwaitables&&>(awaitables)...);\n}\n\ntemplate <typename... SemiAwaitables>\nauto collectAnyNoDiscard(SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::tuple<detail::collect_all_try_component_t<\n        remove_cvref_t<SemiAwaitables>>...>> {\n  return detail::collectAnyNoDiscardImpl(\n      std::make_index_sequence<sizeof...(SemiAwaitables)>{},\n      static_cast<SemiAwaitables&&>(awaitables)...);\n}\n\ntemplate <typename InputRange>\nauto collectAnyRange(InputRange awaitables) -> folly::coro::Task<std::pair<\n    size_t,\n    folly::Try<detail::collect_all_range_component_t<\n        detail::range_reference_t<InputRange>>>>> {\n  const CancellationToken& parentCancelToken =\n      co_await co_current_cancellation_token;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken =\n      cancellation_token_merge(parentCancelToken, cancelSource.getToken());\n\n  std::pair<\n      size_t,\n      folly::Try<detail::collect_all_range_component_t<\n          detail::range_reference_t<InputRange>>>>\n      firstCompletion;\n  firstCompletion.first = size_t(-1);\n\n  using awaitable_type = remove_cvref_t<detail::range_reference_t<InputRange>>;\n  auto makeTask = [&](awaitable_type semiAwaitable, size_t index)\n      -> folly::coro::Task<void> {\n    auto result = co_await folly::coro::co_awaitTry(std::move(semiAwaitable));\n    if (!cancelSource.requestCancellation()) {\n      // This is first entity to request cancellation.\n      firstCompletion.first = index;\n      firstCompletion.second = std::move(result);\n    }\n  };\n\n  auto tasks = detail::collectMakeInnerTaskVec(awaitables, makeTask);\n\n  co_await folly::coro::co_withCancellation(\n      cancelToken, folly::coro::collectAllRange(detail::MoveRange(tasks)));\n\n  co_return firstCompletion;\n}\n\ntemplate <typename InputRange>\nauto collectAnyWithoutExceptionRange(InputRange awaitables)\n    -> folly::coro::Task<std::pair<\n        size_t,\n        folly::Try<detail::collect_all_range_component_t<\n            detail::range_reference_t<InputRange>>>>> {\n  const CancellationToken& parentCancelToken =\n      co_await co_current_cancellation_token;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken =\n      cancellation_token_merge(parentCancelToken, cancelSource.getToken());\n\n  size_t nAwaitables;\n  std::atomic<std::size_t> nAwaited = 1;\n  std::pair<\n      size_t,\n      folly::Try<detail::collect_all_range_component_t<\n          detail::range_reference_t<InputRange>>>>\n      firstValueOrLastException;\n  firstValueOrLastException.first = std::numeric_limits<size_t>::max();\n\n  using awaitable_type = remove_cvref_t<detail::range_reference_t<InputRange>>;\n  auto makeTask = [&](awaitable_type semiAwaitable, size_t index)\n      -> folly::coro::Task<void> {\n    auto result = co_await folly::coro::co_awaitTry(std::move(semiAwaitable));\n    if ((result.hasValue() ||\n         nAwaited.fetch_add(1, std::memory_order_relaxed) == nAwaitables) &&\n        !cancelSource.requestCancellation()) {\n      firstValueOrLastException.first = index;\n      firstValueOrLastException.second = std::move(result);\n    }\n  };\n\n  auto tasks = detail::collectMakeInnerTaskVec(awaitables, makeTask);\n  nAwaitables = tasks.size();\n  co_await folly::coro::co_withCancellation(\n      cancelToken, folly::coro::collectAllRange(detail::MoveRange(tasks)));\n\n  co_return firstValueOrLastException;\n}\n\ntemplate <typename InputRange>\nauto collectAnyNoDiscardRange(InputRange awaitables)\n    -> folly::coro::Task<std::vector<detail::collect_all_try_range_component_t<\n        detail::range_reference_t<InputRange>>>> {\n  const CancellationToken& parentCancelToken =\n      co_await co_current_cancellation_token;\n  const CancellationSource cancelSource;\n  const CancellationToken cancelToken =\n      cancellation_token_merge(parentCancelToken, cancelSource.getToken());\n\n  std::vector<detail::collect_all_try_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      results;\n\n  using awaitable_type = remove_cvref_t<detail::range_reference_t<InputRange>>;\n  auto makeTask = [&](awaitable_type semiAwaitable, size_t index)\n      -> folly::coro::Task<void> {\n    auto result = co_await folly::coro::co_awaitTry(std::move(semiAwaitable));\n    cancelSource.requestCancellation();\n    results[index] = std::move(result);\n  };\n\n  auto tasks = detail::collectMakeInnerTaskVec(awaitables, makeTask);\n\n  results.resize(tasks.size());\n  co_await folly::coro::co_withCancellation(\n      cancelToken, folly::coro::collectAllRange(detail::MoveRange(tasks)));\n\n  co_return results;\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Collect.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Try.h>\n#include <folly/Unit.h>\n#include <folly/container/Access.h>\n#include <folly/container/Iterator.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/detail/PickTaskWrapper.h>\n#include <folly/coro/detail/Traits.h>\n// `collectAll(coroFutureInt())` makes a `safe_task`\n#include <folly/coro/safe/SafeTask.h>\n// `collectAll(memberTask())` makes a `now_task`\n#include <folly/coro/safe/NowTask.h>\n\n#include <iterator>\n#include <tuple>\n#include <type_traits>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\ntemplate <typename SemiAwaitable>\nusing collect_all_try_component_t = folly::Try<decay_rvalue_reference_t<\n    lift_lvalue_reference_t<semi_await_result_t<SemiAwaitable>>>>;\n\ntemplate <typename SemiAwaitable>\nusing collect_all_component_t =\n    decay_rvalue_reference_t<lift_unit_t<semi_await_result_t<SemiAwaitable>>>;\n\ntemplate <typename SemiAwaitable>\nusing collect_all_range_component_t = decay_rvalue_reference_t<\n    lift_lvalue_reference_t<lift_unit_t<semi_await_result_t<SemiAwaitable>>>>;\n\ntemplate <typename SemiAwaitable>\nusing collect_all_try_range_component_t =\n    collect_all_try_component_t<SemiAwaitable>;\n\ntemplate <typename... SemiAwaitables>\nusing collect_any_component_t = std::common_type_t<\n    decay_rvalue_reference_t<semi_await_result_t<SemiAwaitables>>...>;\n\ntemplate <typename Range>\nusing range_iterator_t = decltype(access::begin(std::declval<Range&>()));\n\ntemplate <typename Iterator>\nusing iterator_reference_t = typename std::iterator_traits<Iterator>::reference;\n\ntemplate <typename Range>\nusing range_reference_t = iterator_reference_t<range_iterator_t<Range>>;\n\n// A bare-bones std::range implementation that is similar to ranges::views::move\ntemplate <typename Container>\nclass MoveRange {\n public:\n  explicit MoveRange(Container& container) : container_(container) {}\n\n  auto begin() { return std::make_move_iterator(container_.begin()); }\n  auto end() { return std::make_move_iterator(container_.end()); }\n\n private:\n  Container& container_;\n};\n\n// Future: Apply `value_or_fatal` to the task if the entire collection process\n// is value-only awaitable.  This would require reworking the implementation a\n// bit, since e.g. `cancellation_token_merge` can throw `bad_alloc`.\ntemplate <typename... SemiAwaitables>\nusing CollectAllTask = pick_task_wrapper<\n    std::tuple<collect_all_component_t<remove_cvref_t<SemiAwaitables>>...>,\n    std::min(\n        {safe_alias::maybe_value, lenient_safe_alias_of_v<SemiAwaitables>...}),\n    (folly::ext::must_use_immediately_v<SemiAwaitables> || ...)>;\n\ntemplate <typename... SemiAwaitables>\nusing CollectAllTryTask = pick_task_wrapper<\n    std::tuple<collect_all_try_component_t<remove_cvref_t<SemiAwaitables>>...>,\n    std::min(\n        {safe_alias::maybe_value, lenient_safe_alias_of_v<SemiAwaitables>...}),\n    (folly::ext::must_use_immediately_v<SemiAwaitables> || ...)>;\n\n} // namespace detail\n\n///////////////////////////////////////////////////////////////////////////\n// collectAll(SemiAwaitable<Ts>...) -> SemiAwaitable<std::tuple<Ts...>>\n//\n// The collectAll() function can be used to concurrently co_await on multiple\n// SemiAwaitable objects and continue once they are all complete.\n//\n// collectAll() accepts an arbitrary number of SemiAwaitable objects and returns\n// a SemiAwaitable object that will complete with a std::tuple of the results.\n//\n// When the returned SemiAwaitable object is co_awaited it will launch\n// a new coroutine for awaiting each input awaitable in-turn.\n//\n// Note that coroutines for awaiting the input awaitables of later arguments\n// will not be launched until the prior coroutine reaches its first suspend\n// point. This means that awaiting multiple sub-tasks that all complete\n// synchronously will still execute them sequentially on the current thread.\n//\n// If any of the input operations complete with an exception then it will\n// request cancellation of any outstanding tasks and the whole collectAll()\n// operation will complete with an exception once all of the operations\n// have completed.  Any partial results will be discarded. If multiple\n// operations fail with an exception then the exception from the first task\n// to fail will be rethrown and subsequent errors are discarded.\n//\n// If you need to know which operation failed or you want to handle partial\n// failures then you can use the folly::coro::collectAllTry() instead which\n// returns a tuple of Try<T> objects instead of a tuple of values.\n//\n// Example: Serially awaiting multiple operations (slower)\n//   folly::coro::Task<Foo> doSomething();\n//   folly::coro::Task<Bar> doSomethingElse();\n//\n//   Foo result1 = co_await doSomething();\n//   Bar result2 = co_await doSomethingElse();\n//\n// Example: Concurrently awaiting multiple operations (faster) C++17-only.\n//   auto [result1, result2] =\n//       co_await folly::coro::collectAll(doSomething(), doSomethingElse());\n//\ntemplate <typename... SemiAwaitables>\n// Do NOT take awaitables by-reference, that would break `now_task` safety.\nauto collectAll(SemiAwaitables... awaitables)\n    -> detail::CollectAllTask<SemiAwaitables...>;\n\n///////////////////////////////////////////////////////////////////////////\n// collectAllTry(SemiAwaitable<Ts>...)\n//    -> SemiAwaitable<std::tuple<Try<Ts>...>>\n//\n// Like the collectAll() function, the collectAllTry() function can be used to\n// concurrently await multiple input SemiAwaitable objects.\n//\n// The collectAllTry() function differs from collectAll() in that it produces a\n// tuple of Try<T> objects rather than a tuple of the values.\n// This allows the caller to inspect the success/failure of individual\n// operations and handle partial failures but has a less-convenient interface\n// than collectAll().\n//\n// It also differs in that failure of one subtask does _not_ request\n// cancellation of the other subtasks.\n//\n// Example: Handling partial failure with collectAllTry()\n//    folly::coro::Task<Foo> doSomething();\n//    folly::coro::Task<Bar> doSomethingElse();\n//\n//    auto [result1, result2] = co_await folly::coro::collectAllTry(\n//        doSomething(), doSomethingElse());\n//\n//    if (result1.hasValue()) {\n//      Foo& foo = result1.value();\n//      process(foo);\n//    } else {\n//      logError(\"doSomething() failed\", result1.exception());\n//    }\n//\n//    if (result2.hasValue()) {\n//      Bar& bar = result2.value();\n//      process(bar);\n//    } else {\n//      logError(\"doSomethingElse() failed\", result2.exception());\n//    }\n//\ntemplate <typename... SemiAwaitables>\nauto collectAllTry(SemiAwaitables... awaitables)\n    -> detail::CollectAllTryTask<SemiAwaitables...>;\n\n////////////////////////////////////////////////////////////////////////\n// collectAllRange(RangeOf<SemiAwaitable<T>>&&)\n//   -> SemiAwaitable<std::vector<T>>\n//\n// The collectAllRange() function can be used to concurrently await a collection\n// of SemiAwaitable objects, returning a std::vector of the individual results\n// in the same order as the input once all operations have completed.\n//\n// If any of the operations fail with an exception then requests cancellation of\n// any outstanding operations and the entire operation fails with an exception,\n// discarding any partial results. If more than one operation fails with an\n// exception then the exception from task that failed first (in time) is\n// rethrown. Other results and exceptions are discarded.\n//\n// If you need to be able to distinguish which operation failed or handle\n// partial failures then use collectAllTryRange() instead.\n//\n// Note that the expression `*it` must be SemiAwaitable.\n// This typically means that containers of Task<T> must be adapted to produce\n// moved-elements by applying the ranges::views::move transform.\n// e.g.\n//\n//   std::vector<Task<T>> tasks = ...;\n//   std::vector<T> vals = co_await collectAllRange(tasks |\n//   ranges::views::move);\n//\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        !std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int> = 0>\nauto collectAllRange(InputRange awaitables)\n    -> folly::coro::Task<std::vector<detail::collect_all_range_component_t<\n        detail::range_reference_t<InputRange>>>>;\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int> = 0>\nauto collectAllRange(InputRange awaitables) -> folly::coro::Task<void>;\n\n////////////////////////////////////////////////////////////////////////////\n// collectAllTryRange(RangeOf<SemiAwaitable<T>>&&)\n//    -> SemiAwaitable<std::vector<folly::Try<T>>>\n//\n// The collectAllTryRange() function can be used to concurrently await a\n// collection of SemiAwaitable objects and produces a std::vector of\n// Try<T> objects in the same order as the input once all of the input\n// operations have completed.\n//\n// The success/failure of individual results can be inspected by calling\n// .hasValue() or .hasException() on the elements of the returned vector.\ntemplate <typename InputRange>\nauto collectAllTryRange(InputRange awaitables)\n    -> folly::coro::Task<std::vector<detail::collect_all_try_range_component_t<\n        detail::range_reference_t<InputRange>>>>;\n\n// collectAllRange()/collectAllTryRange() overloads that simplifies the\n// common-case where an rvalue std::vector<SemiAwaitable> is passed.\n//\n// This avoids the caller needing to pipe the input through ranges::views::move\n// transform to force the elements to be rvalue-references since the\n// std::vector<T>::reference type is T& rather than T&& and some awaitables,\n// such as Task<U>, are not lvalue awaitable.\ntemplate <typename SemiAwaitable>\nauto collectAllRange(std::vector<SemiAwaitable> awaitables)\n    -> decltype(collectAllRange(detail::MoveRange(awaitables))) {\n  co_return co_await collectAllRange(detail::MoveRange(awaitables));\n}\n\ntemplate <typename SemiAwaitable>\nauto collectAllTryRange(std::vector<SemiAwaitable> awaitables)\n    -> decltype(collectAllTryRange(detail::MoveRange(awaitables))) {\n  co_return co_await collectAllTryRange(detail::MoveRange(awaitables));\n}\n\nnamespace detail {\ntemplate <typename InputRange, bool IsTry>\nusing async_generator_from_awaitable_range_item_t = conditional_t<\n    IsTry,\n    collect_all_try_range_component_t<range_reference_t<InputRange>>,\n    collect_all_range_component_t<range_reference_t<InputRange>>>;\n}\n\n////////////////////////////////////////////////////////////////////////////\n// makeUnorderedAsyncGenerator(AsyncScope&,\n// RangeOf<SemiAwaitable<T>>&&) -> AsyncGenerator<T&&>\n// makeUnorderedTryAsyncGenerator(AsyncScope&,\n// RangeOf<SemiAwaitable<T>>&&) -> AsyncGenerator<Try<T>&&>\n\n// Returns an AsyncGenerator that yields results of passed-in awaitables in\n// order of completion.\n// Destroying or cancelling the AsyncGenerator cancels the remaining awaitables.\n//\n// makeUnorderedAsyncGenerator cancels all remaining\n// awaitables when any of them fail with an exception. Any results obtained\n// before the failure are still returned via the generator, then the first\n// exception in time. makeUnorderedTryAsyncGenerator does not\n// cancel awaitables when one fails, and yields all results even when cancelled.\n//\n// Awaitables are attached to the passed-in AsyncScope.\n\ntemplate <typename InputRange>\nauto makeUnorderedAsyncGenerator(AsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        false>&&>;\ntemplate <typename InputRange>\nauto makeUnorderedTryAsyncGenerator(AsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        true>&&>;\n\ntemplate <typename SemiAwaitable>\nauto makeUnorderedAsyncGenerator(\n    AsyncScope& scope, std::vector<SemiAwaitable> awaitables)\n    -> decltype(makeUnorderedAsyncGenerator(\n        scope, detail::MoveRange(awaitables))) {\n  auto gen = makeUnorderedAsyncGenerator(scope, detail::MoveRange(awaitables));\n  while (true) {\n    co_yield co_result(co_await co_awaitTry(gen.next()));\n  }\n}\ntemplate <typename SemiAwaitable>\nauto makeUnorderedTryAsyncGenerator(\n    AsyncScope& scope, std::vector<SemiAwaitable> awaitables)\n    -> decltype(makeUnorderedTryAsyncGenerator(\n        scope, detail::MoveRange(awaitables))) {\n  auto gen =\n      makeUnorderedTryAsyncGenerator(scope, detail::MoveRange(awaitables));\n  while (true) {\n    co_yield co_result(co_await co_awaitTry(gen.next()));\n  }\n}\n\n// Can also be used with CancellableAsyncScope\n\ntemplate <typename InputRange>\nauto makeUnorderedAsyncGenerator(\n    CancellableAsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        false>&&>;\ntemplate <typename InputRange>\nauto makeUnorderedTryAsyncGenerator(\n    CancellableAsyncScope& scope, InputRange awaitables)\n    -> AsyncGenerator<detail::async_generator_from_awaitable_range_item_t<\n        InputRange,\n        true>&&>;\n\ntemplate <typename SemiAwaitable>\nauto makeUnorderedAsyncGenerator(\n    CancellableAsyncScope& scope, std::vector<SemiAwaitable> awaitables)\n    -> decltype(makeUnorderedAsyncGenerator(\n        scope, detail::MoveRange(awaitables))) {\n  auto gen = makeUnorderedAsyncGenerator(scope, detail::MoveRange(awaitables));\n  while (true) {\n    co_yield co_result(co_await co_awaitTry(gen.next()));\n  }\n}\ntemplate <typename SemiAwaitable>\nauto makeUnorderedTryAsyncGenerator(\n    CancellableAsyncScope& scope, std::vector<SemiAwaitable> awaitables)\n    -> decltype(makeUnorderedTryAsyncGenerator(\n        scope, detail::MoveRange(awaitables))) {\n  auto gen =\n      makeUnorderedTryAsyncGenerator(scope, detail::MoveRange(awaitables));\n  while (true) {\n    co_yield co_result(co_await co_awaitTry(gen.next()));\n  }\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// collectAllWindowed(RangeOf<SemiAwaitable<T>>&&, size_t maxConcurrency)\n//   -> SemiAwaitable<std::vector<T>>\n//\n// collectAllWindowed(RangeOf<SemiAwaitable<void>>&&, size_t maxConcurrency)\n//   -> SemiAwaitable<void>\n//\n// Await each of the input awaitables in the range, allowing at most\n// 'maxConcurrency' of these input awaitables to be concurrently awaited\n// at any one point in time.\n//\n// If any of the input awaitables fail with an exception then requests\n// cancellation of any incomplete operations and fails the whole\n// operation with an exception. If multiple input awaitables fail with\n// an exception then the exception from the first task to fail (in time)\n// will be rethrown and the rest of the results will be discarded.\n//\n// If there is an exception thrown while iterating over the input-range then\n// it will still guarantee that any prior awaitables in the input-range will\n// run to completion before completing the collectAllWindowed() operation with\n// the exception thrown during iteration.\n//\n// The resulting std::vector will contain the results in the corresponding\n// order of their respective awaitables in the input range.\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int> = 0>\nauto collectAllWindowed(InputRange awaitables, std::size_t maxConcurrency)\n    -> folly::coro::Task<void>;\ntemplate <\n    typename InputRange,\n    std::enable_if_t<\n        !std::is_void_v<\n            semi_await_result_t<detail::range_reference_t<InputRange>>>,\n        int> = 0>\nauto collectAllWindowed(InputRange awaitables, std::size_t maxConcurrency)\n    -> folly::coro::Task<std::vector<detail::collect_all_range_component_t<\n        detail::range_reference_t<InputRange>>>>;\n\n///////////////////////////////////////////////////////////////////////////////\n// collectAllTryWindowed(RangeOf<SemiAwaitable<T>>&, size_t maxConcurrency)\n//   -> SemiAwaitable<std::vector<folly::Try<T>>>\n//\n// Concurrently awaits a collection of awaitable with bounded concurrency,\n// producing a vector of Try values containing each of the results.\n//\n// The resulting std::vector will contain the results in the corresponding\n// order of their respective awaitables in the input range.\n//\n// Note that the whole operation may still complete with an exception if\n// iterating over the awaitables fails with an exception (eg. if you pass\n// a Generator<Task<T>&&> and the generator throws an exception).\ntemplate <typename InputRange>\nauto collectAllTryWindowed(InputRange awaitables, std::size_t maxConcurrency)\n    -> folly::coro::Task<std::vector<detail::collect_all_try_range_component_t<\n        detail::range_reference_t<InputRange>>>>;\n\n// collectAllWindowed()/collectAllTryWindowed() overloads that simplify the\n// use of these functions with std::vector<SemiAwaitable>.\ntemplate <typename SemiAwaitable>\nauto collectAllWindowed(\n    std::vector<SemiAwaitable> awaitables, std::size_t maxConcurrency)\n    -> decltype(collectAllWindowed(\n        detail::MoveRange(awaitables), maxConcurrency)) {\n  co_return co_await collectAllWindowed(\n      detail::MoveRange(awaitables), maxConcurrency);\n}\n\ntemplate <typename SemiAwaitable>\nauto collectAllTryWindowed(\n    std::vector<SemiAwaitable> awaitables, std::size_t maxConcurrency)\n    -> decltype(collectAllTryWindowed(\n        detail::MoveRange(awaitables), maxConcurrency)) {\n  co_return co_await collectAllTryWindowed(\n      detail::MoveRange(awaitables), maxConcurrency);\n}\n\n///////////////////////////////////////////////////////////////////////////\n// collectAny(SemiAwaitable<Ts>...) -> SemiAwaitable<\n//   std::pair<std::size_t, folly::Try<std::common_type<Ts...>>>>\n//\n// The collectAny() function can be used to concurrently co_await on multiple\n// SemiAwaitable objects, get the result and index of the first one completing,\n// cancel the remaining ones and continue once they are completed.\n//\n// collectAny() accepts a positive number of SemiAwaitable objects and\n// returns a SemiAwaitable object that will complete with a pair containing the\n// result of the first one to complete and its index.\n//\n// collectAny() is built on top of collectAll(), be aware of the coroutine\n// starting behavior described in collectAll() documentation.\n//\n// The result of the first SemiAwaitable is going to be returned, whether it\n// is a value or an exception. Any result of the remaining SemiAwaitables will\n// be discarded, independently of whether it's a value or an exception.\n//\n// Example:\n//   folly::coro::Task<Foo> getDataOneWay();\n//   folly::coro::Task<Foo> getDataAnotherWay();\n//\n//   std::pair<std::size_t, Try<Foo>> result = co_await folly::coro::collectAny(\n//       getDataOneWay(), getDataAnotherWay());\n//\ntemplate <typename SemiAwaitable, typename... SemiAwaitables>\nauto collectAny(SemiAwaitable&& awaitable, SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::pair<\n        std::size_t,\n        folly::Try<detail::collect_any_component_t<\n            SemiAwaitable,\n            SemiAwaitables...>>>>;\n\n///////////////////////////////////////////////////////////////////////////\n// collectAnyWithoutException(SemiAwaitable<Ts>...)\n//    -> SemiAwaitable<std::pair<std::size_t, folly::Try<T>>>\n//\n// The collectAnyWithoutException() function is similar to collectAny() in that\n// it co_awaits multiple SemiAwaitables and cancels any outstanding operations\n// when complete. Unlike collectAny(), it returns the first success, or the last\n// exception if all of the SemiAwaitables fail.\n//\n// collectAnyWithoutException() is built on top of collectAll(), be aware of the\n// coroutine starting behavior described in collectAll() documentation.\n//\n// The result of the first successful SemiAwaitable, or the exception from\n// the last SemiAwaitable is returned if none are successful. Any result of the\n// remaining SemiAwaitables will be discarded, independently of whether it's a\n// value or an exception.\n//\n// Example:\n//   folly::coro::Task<Foo> getDataOneWay();\n//   folly::coro::Task<Foo> getDataAnotherWay();\n//\n//   std::pair<std::size_t, Try<Foo>> result =\n//       co_await folly::coro::collectAnyWithoutException(\n//           getDataOneWay(), getDataAnotherWay());\n//\ntemplate <typename... SemiAwaitables>\nauto collectAnyWithoutException(SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::pair<\n        std::size_t,\n        folly::Try<detail::collect_any_component_t<SemiAwaitables...>>>>;\n\n///////////////////////////////////////////////////////////////////////////\n// collectAnyNoDiscard(SemiAwaitable<Ts>...) ->\n//   SemiAwaitable<std::tuple<folly::Try<Ts>...>>\n//\n// The collectAnyNoDiscard() function is similar to collectAny() in that it\n// co_awaits multiple SemiAwaitables and cancels any outstanding operations once\n// at least one has finished. Unlike collectAny(), it returns results from *all*\n// SemiAwaitables, including folly::OperationCancelled for operations that were\n// cancelled.\n//\n// collectAnyNoDiscard() is built on top of collectAll(), be aware of the\n// coroutine starting behavior described in collectAll() documentation.\n//\n// The returned tuple contains the results of all the SemiAwaitables.\n//\n// Example:\n//   folly::coro::Task<Foo> getDataOneWay();\n//   folly::coro::Task<Bar> getDataAnotherWay();\n//\n//   std::tuple<folly::Try<Foo>, folly::Try<Bar>> result = co_await\n//      folly::coro::collectAnyNoDiscard(getDataOneWay(), getDataAnotherWay());\n//\ntemplate <typename... SemiAwaitables>\nauto collectAnyNoDiscard(SemiAwaitables&&... awaitables)\n    -> folly::coro::Task<std::tuple<detail::collect_all_try_component_t<\n        remove_cvref_t<SemiAwaitables>>...>>;\n\n///////////////////////////////////////////////////////////////////////////\n// collectAnyRange(RangeOf<SemiAwaitable<T>>&&)\n//   -> SemiAwaitable<std::pair<std::size_t, folly::Try<T>>>\n//\n// The collectAnyRange() function can be used to concurrently co_await on\n// multiple SemiAwaitable objects, get the result and index of the first one\n// completing, cancel the remaining ones and continue once they are completed.\n//\n// collectAnyRange() accepts zero or more SemiAwaitable objects and\n// returns a SemiAwaitable object that will complete with a pair containing the\n// result of the first one to complete and its index.\n//\n// collectAnyRange() is built on top of collectAllRange(), be aware of the\n// coroutine starting behavior described in collectAll() documentation.\n//\n// The result of the first SemiAwaitable is going to be returned, whether it\n// is a value or an exception. Any result of the remaining SemiAwaitables will\n// be discarded, independently of whether it's a value or an exception.\n//\n// e.g.\n//\n//   std::vector<Task<T>> tasks = ...;\n//   std::pair<size_t, Try<T>> result = co_await collectAnyRange(tasks |\n//       ranges::views::move);\n//\ntemplate <typename InputRange>\nauto collectAnyRange(InputRange awaitables) -> folly::coro::Task<std::pair<\n    size_t,\n    folly::Try<detail::collect_all_range_component_t<\n        detail::range_reference_t<InputRange>>>>>;\n\n///////////////////////////////////////////////////////////////////////////\n// collectAnyWithoutExceptionRange(RangeOf<SemiAwaitable<T>>&&)\n//   -> SemiAwaitable<std::pair<std::size_t, folly::Try<T>>>\n//\n// The collectAnyWithoutExceptionRange() function is similar to\n// collectAnyRange() in that it co_awaits multiple SemiAwaitables and cancels\n// any outstanding operations when complete. Unlike collectAnyRange(), it\n// returns the first success, or the last exception if all of the SemiAwaitables\n// fail.\n//\n// collectAnyWithoutExceptionRange() is built on top of collectAllRange(), be\n// aware of the coroutine starting behavior described in collectAll()\n// documentation.\n//\n// The result of the first successful SemiAwaitable, or the the exception from\n// the last SemiAwaitable is returned if none are successful. Any result of the\n// remaining SemiAwaitables will be discarded, independently of whether it's a\n// value or an exception.\n//\n// Example:\n//   std::vector<Task<T>> tasks = ...;\n//   std::pair<size_t, Try<T>> result =\n//       co_await collectAnyWithoutExceptionRange(tasks | ranges::views::move);\n//\ntemplate <typename InputRange>\nauto collectAnyWithoutExceptionRange(InputRange awaitables)\n    -> folly::coro::Task<std::pair<\n        size_t,\n        folly::Try<detail::collect_all_range_component_t<\n            detail::range_reference_t<InputRange>>>>>;\n\n///////////////////////////////////////////////////////////////////////////\n// collectAnyNoDiscardRange(RangeOf<SemiAwaitable<T>>&&)\n//    -> SemiAwaitable<std::vector<folly::Try<T>>>\n//\n// The collectAnyNoDiscardRange() function is similar to collectAnyRange() in\n// that it co_awaits multiple SemiAwaitables and cancels any outstanding\n// operations once at least one has finished. Unlike collectAnyRange(), it\n// returns results from *all* SemiAwaitables, including\n// folly::OperationCancelled for operations that were cancelled.\n//\n// collectAnyNoDiscardRange() is built on top of collectAllRange(), be aware of\n// the coroutine starting behavior described in collectAll() documentation.\n//\n// The success/failure of individual results can be inspected by calling\n// .hasValue() or .hasException() on the elements of the returned vector.\n//\n// Example:\n//   folly::coro::Task<Foo> getDataOneWay();\n//   folly::coro::Task<Foo> getDataAnotherWay();\n//\n//   std::vector<folly::Try<Foo>> result = co_await\n//      folly::coro::collectAnyNoDiscard(getDataOneWay(), getDataAnotherWay());\n//\ntemplate <typename InputRange>\nauto collectAnyNoDiscardRange(InputRange awaitables)\n    -> folly::coro::Task<std::vector<detail::collect_all_try_range_component_t<\n        detail::range_reference_t<InputRange>>>>;\n\n// collectAnyRange()/collectAnyWithoutExceptionRange()/collectAnyNoDiscardRange()\n// overloads that simplifies the common-case where an rvalue\n// std::vector<SemiAwaitable> is passed.\n//\n// This avoids the caller needing to pipe the input through ranges::views::move\n// transform to force the elements to be rvalue-references since the\n// std::vector<T>::reference type is T& rather than T&& and some awaitables,\n// such as Task<U>, are not lvalue awaitable.\ntemplate <typename SemiAwaitable>\nauto collectAnyRange(std::vector<SemiAwaitable> awaitables)\n    -> decltype(collectAnyRange(detail::MoveRange(awaitables))) {\n  co_return co_await collectAnyRange(detail::MoveRange(awaitables));\n}\ntemplate <typename SemiAwaitable>\nauto collectAnyWithoutExceptionRange(std::vector<SemiAwaitable> awaitables)\n    -> decltype(collectAnyWithoutExceptionRange(\n        detail::MoveRange(awaitables))) {\n  co_return co_await collectAnyWithoutExceptionRange(\n      detail::MoveRange(awaitables));\n}\ntemplate <typename SemiAwaitable>\nauto collectAnyNoDiscardRange(std::vector<SemiAwaitable> awaitables)\n    -> decltype(collectAnyNoDiscardRange(detail::MoveRange(awaitables))) {\n  co_return co_await collectAnyNoDiscardRange(detail::MoveRange(awaitables));\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Collect-inl.h>\n"
  },
  {
    "path": "folly/coro/Concat-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Coroutine.h>\n\nnamespace folly {\nnamespace coro {\n\ntemplate <\n    typename HReference,\n    typename... TReference,\n    typename HValue,\n    typename... TValue>\nAsyncGenerator<HReference, HValue> concat(\n    AsyncGenerator<HReference, HValue> head,\n    AsyncGenerator<TReference, TValue>... tail) {\n  static_assert((std::is_same_v<decltype(head), decltype(tail)> && ...));\n  using list = AsyncGenerator<HReference, HValue>[];\n  for (auto& gen : list{std::move(head), std::move(tail)...}) {\n    while (auto val = co_await gen.next()) {\n      co_yield std::move(val).value();\n    }\n  }\n}\n\n} // namespace coro\n} // namespace folly\n"
  },
  {
    "path": "folly/coro/Concat.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Coroutine.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// Concatenate the values from multiple streams into a single stream such\n// that each stream is exhausted before the next one begins.\n//\n// The input is a variadic list of AsyncGenerators, where each input has the\n// same Reference and Value types.\n//\n// The output is a single AsyncGenerator over all of the input generators.\n//\n// Example:\n//  AsyncGenerator<int> stream();\n//\n//  Task<int> consumer() {\n//    auto values = concat(stream(), stream(), stream());\n//\n//    int result = 0;\n//    while (auto item = co_await values.next()) {\n//      result += *item;\n//    }\n//\n//    return result;\n//  }\ntemplate <\n    typename HReference,\n    typename... TReference,\n    typename HValue,\n    typename... TValue>\nAsyncGenerator<HReference, HValue> concat(\n    AsyncGenerator<HReference, HValue> head,\n    AsyncGenerator<TReference, TValue>... tail);\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Concat-inl.h>\n"
  },
  {
    "path": "folly/coro/Coroutine.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <optional>\n#include <type_traits>\n#include <variant>\n\n#include <folly/Portability.h>\n#include <folly/Utility.h>\n\n#if FOLLY_HAS_COROUTINES\n\n// libc++'s <coroutine> header only provides its declarations for C++20 and\n// above, so we need to fall back to <experimental/coroutine> when building with\n// C++17.\n#if (__has_include(<coroutine>) && !defined(LLVM_COROUTINES)) || defined(__cpp_impl_coroutine)\n#define FOLLY_USE_STD_COROUTINE 1\n#else\n#define FOLLY_USE_STD_COROUTINE 0\n#endif\n\n#if FOLLY_USE_STD_COROUTINE\n#include <coroutine>\n#else\n#include <experimental/coroutine>\n#endif\n\n#endif // FOLLY_HAS_COROUTINES\n\n//  A place for foundational vocabulary types.\n//\n//  This header reexports the foundational vocabulary coroutine-helper types\n//  from the standard, and exports several new foundational vocabulary types\n//  as well.\n//\n//  Types which are non-foundational and non-vocabulary should go elsewhere.\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nclass exception_wrapper;\nstruct AsyncStackFrame;\n} // namespace folly\n\nnamespace folly::coro {\n\n#if FOLLY_USE_STD_COROUTINE\nnamespace impl = std;\n#else\nnamespace impl = std::experimental;\n#endif\n\nusing impl::coroutine_handle;\nusing impl::coroutine_traits;\nusing impl::noop_coroutine;\nusing impl::noop_coroutine_handle;\nusing impl::noop_coroutine_promise;\nusing impl::suspend_always;\nusing impl::suspend_never;\n\n//  ready_awaitable\n//\n//  An awaitable which is immediately ready with a value. Suspension is no-op.\n//  Resumption returns the value.\n//\n//  The value type is permitted to be a reference.\ntemplate <typename T = void>\nclass ready_awaitable {\n  static_assert(!std::is_void<T>::value, \"base template unsuitable for void\");\n\n public:\n  explicit ready_awaitable(T value) //\n      noexcept(noexcept(T(FOLLY_DECLVAL(T&&))))\n      : value_(static_cast<T&&>(value)) {}\n\n  bool await_ready() noexcept { return true; }\n  void await_suspend(coroutine_handle<>) noexcept {}\n  T await_resume() noexcept(noexcept(T(FOLLY_DECLVAL(T&&)))) {\n    return static_cast<T&&>(value_);\n  }\n\n private:\n  T value_;\n};\n\n//  ready_awaitable\n//\n//  An awaitable type which is immediately ready. Suspension is a no-op.\ntemplate <>\nclass ready_awaitable<void> {\n public:\n  ready_awaitable() noexcept = default;\n\n  bool await_ready() noexcept { return true; }\n  void await_suspend(coroutine_handle<>) noexcept {}\n  void await_resume() noexcept {}\n};\n\nnamespace detail {\n\n//  await_suspend_return_coroutine_fn\n//  await_suspend_return_coroutine\n//\n//  The special member await_suspend has three forms, differing in their return\n//  types. It may return void, bool, or coroutine_handle<>. This invokes member\n//  await_suspend on the argument, conspiring always to return coroutine_handle\n//  no matter the underlying form of member await_suspend on the argument.\nstruct await_suspend_return_coroutine_fn {\n  template <typename A, typename P>\n  coroutine_handle<> operator()(A& a, coroutine_handle<P> coro) const\n      noexcept(noexcept(a.await_suspend(coro))) {\n    using result = decltype(a.await_suspend(coro));\n    if constexpr (std::is_same<void, result>::value) {\n      a.await_suspend(coro);\n      return noop_coroutine();\n    } else if constexpr (std::is_same<bool, result>::value) {\n      return a.await_suspend(coro) ? noop_coroutine() : coro;\n    } else {\n      return a.await_suspend(coro);\n    }\n  }\n};\ninline constexpr await_suspend_return_coroutine_fn\n    await_suspend_return_coroutine{};\n\n} // namespace detail\n\n#if __has_include(<variant>)\n\n//  variant_awaitable\n//\n//  An awaitable type which is backed by one of several possible underlying\n//  awaitables.\ntemplate <typename... A>\nclass variant_awaitable : private std::variant<A...> {\n private:\n  using base = std::variant<A...>;\n\n  template <typename Visitor>\n  auto visit(Visitor v) {\n    return std::visit(v, static_cast<base&>(*this));\n  }\n\n public:\n  // imports the base-class constructors wholesale for implementation simplicity\n  using base::base; // assume there are no valueless-by-exception instances\n\n  auto await_ready() noexcept(\n      (noexcept(FOLLY_DECLVAL(A&).await_ready()) && ...)) {\n    return visit([&](auto& a) { return a.await_ready(); });\n  }\n  template <typename P>\n  auto await_suspend(coroutine_handle<P> coro) noexcept(\n      (noexcept(FOLLY_DECLVAL(A&).await_suspend(coro)) && ...)) {\n    auto impl = detail::await_suspend_return_coroutine;\n    return visit([&](auto& a) { return impl(a, coro); });\n  }\n  auto await_resume() noexcept(\n      (noexcept(FOLLY_DECLVAL(A&).await_resume()) && ...)) {\n    return visit([&](auto& a) { return a.await_resume(); });\n  }\n};\n\n#endif // __has_include(<variant>)\n\n//  ----\n\nnamespace detail {\n\nstruct detect_promise_return_object_eager_conversion_ {\n  struct promise_type {\n    struct return_object {\n      /* implicit */ return_object(promise_type& p) noexcept : promise{&p} {\n        promise->object = this;\n      }\n      ~return_object() {\n        if (promise) {\n          promise->object = nullptr;\n        }\n      }\n\n      promise_type* promise;\n    };\n\n    ~promise_type() {\n      if (object) {\n        object->promise = nullptr;\n      }\n    }\n\n    suspend_never initial_suspend() const noexcept { return {}; }\n    suspend_never final_suspend() const noexcept { return {}; }\n    void unhandled_exception() {}\n\n    return_object get_return_object() noexcept { return {*this}; }\n    void return_void() {}\n\n    return_object* object = nullptr;\n  };\n\n  /* implicit */ detect_promise_return_object_eager_conversion_(\n      promise_type::return_object const& o) noexcept\n      : eager{!!o.promise} {}\n  //  letting the coroutine type be trivially-copyable makes the coroutine crash\n  //  under clang; to work around, provide an empty but not trivial destructor\n  ~detect_promise_return_object_eager_conversion_() {}\n\n  bool eager = false;\n\n  static detect_promise_return_object_eager_conversion_ go() noexcept {\n    // FIXME: when building against Apple SDKs using c++17, we hit this all over\n    // the place on complex testing infrastructure for iOS. Since it's not clear\n    // how to fix the issue properly right now, force ignore this warnings and\n    // unblock expected/optional coroutines. This should be removed once the\n    // build config is changed to use -Wno-deprecated-experimental-coroutine.\n    FOLLY_PUSH_WARNING\n#if defined(__clang__) && \\\n    (13 < __clang_major__ && __clang_major__ < 17 - defined(__APPLE__))\n    FOLLY_CLANG_DISABLE_WARNING(\"-Wdeprecated-experimental-coroutine\")\n#endif\n    co_return;\n    FOLLY_POP_WARNING\n  }\n};\n\n} // namespace detail\n\n//  detect_promise_return_object_eager_conversion\n//\n//  Returns true if the compiler implements coroutine promise return-object\n//  conversion eagerly and returns false if the compiler defers conversion.\n//\n//  It is expected that the caller holds the promise return-object until the\n//  promise is fulfilled, even when it is not the same type as the coroutine.\n//\n//    auto ret = promise.get_return_object();\n//    initial-suspend, etc...\n//    return ret;\n//\n//  But this expected behavior was, mistakenly, never specified.\n//\n//  Some compilers misbehave, where the caller holds precisely the coroutine\n//  type by converting the promise return-object eagerly when it is of some\n//  type different from the coroutine type.\n//\n//    coro-type ret = promise.get_return_object();\n//    initial-suspend, etc...\n//    return ret;\n//\n//  Known behaviors are as follows:\n//  * For msvc, conversion is eager for vs < 2019 update 16.5 (msc ver 1925) and\n//    is deferred for vs >= 2019 update 16.5 (msc ver 1925).\n//    References:\n//      https://developercommunity.visualstudio.com/t/c-coroutine-get-return-object-converted-too-early/222420\n//  * For g++, conversion is deferred.\n//  * For clang++, conversion is eager for 15 <= llvm < 17 and is deferred for\n//    llvm < 15 or llvm >= 17.\n//    References:\n//      https://reviews.llvm.org/D117087\n//      https://github.com/llvm/llvm-project/issues/56532\n//      https://reviews.llvm.org/D145639\n//\n//  Meta sometimes uses llvm patched to have deferred conversion where the\n//  corresponding upstream implements eager conversion. So version numbers do\n//  not tell the whole story.\n//\n//  This function detects which behavior the compiler implements at a mix of\n//  compile time and run time, depending on the compiler. It is only necessary\n//  to do the runtime detection for llvm but, conveniently, llvm is able to do\n//  full heap-allocation elision (\"HALO\") and optimize the detection down to a\n//  constant.\n//\n//  TODO: Remove this detection once the behavior is specified.\ninline bool detect_promise_return_object_eager_conversion() {\n  using coro = detail::detect_promise_return_object_eager_conversion_;\n  constexpr auto t = kMscVer && kMscVer < 1925;\n  constexpr auto f = (kGnuc && !kIsClang) || (kMscVer >= 1925);\n  return t ? true : f ? false : coro::go().eager;\n}\n\ntemplate <typename>\nclass ExtendedCoroutinePromiseCrtp;\n\nnamespace detail {\ntemplate <typename, typename, typename>\nclass TaskPromiseWrapperBase;\n}\n\n// Extended version of coroutine_handle<void>\n// Assumes (and enforces) assumption that coroutine_handle is a pointer\nclass ExtendedCoroutineHandle {\n protected:\n  template <typename>\n  friend class ExtendedCoroutinePromiseCrtp;\n  template <typename, typename, typename>\n  friend class detail::TaskPromiseWrapperBase;\n  // This passkey aims to stop end users from calling `getPromiseBase`, which\n  // is an unsafe implementation detail, and to prevent overload ambiguity.\n  //\n  // It also doubles as the sigil for `use_extended_handle_concept`, another\n  // private detail.\n  class PrivateTag {\n   private:\n    friend ExtendedCoroutineHandle;\n    PrivateTag() = default;\n  };\n\n private:\n  // SFINAE detection for the `use_extended_handle_concept` member type alias\n  // that classes implementing `getErrorHandle` must expose.  We don't want to\n  // use any kind of common base on `TaskWrapperPromise`, be it non-empty\n  // `PromiseBase`, or a dedicated empty tag, since either one would break\n  // empty-base optimization.\n\n  template <typename T>\n  using use_extended_handle_of_ = typename T::use_extended_handle_concept;\n\n  template <typename T, typename Void = void>\n  struct use_extended_handle {\n    static_assert(\n        require_sizeof<T>, \"`use_extended_handle` on incomplete type\");\n    static constexpr bool value = false;\n  };\n\n  template <typename T>\n  struct use_extended_handle<T, void_t<use_extended_handle_of_<T>>> {\n    static constexpr bool value =\n        std::is_same_v<use_extended_handle_of_<T>, PrivateTag>;\n  };\n\n public:\n  using ErrorHandle = std::pair<ExtendedCoroutineHandle, AsyncStackFrame*>;\n\n  class PromiseBase {\n   private:\n    friend class ExtendedCoroutineHandle;\n    template <typename>\n    friend class ExtendedCoroutinePromiseCrtp;\n\n    using Fn = std::optional<ErrorHandle>(PromiseBase*, exception_wrapper& ex);\n\n    explicit PromiseBase(Fn* fn) : getErrorHandlePtr_(fn) {}\n    ~PromiseBase() = default;\n\n    // A manual vtable with 1 function. Benefits over virtual inheritance:\n    //   - `TaskWrapperPromise` can implement `getErrorHandle` without bloating\n    //     itself with with a vtable it does not need.\n    //   - A tiny binary size win.\n    //   - Derived classes like `TaskPromise` don't have to be `final` in order\n    //     for the compiler to treat them as non-polymorphic.\n    Fn* getErrorHandlePtr_;\n  };\n\n  template <typename Promise>\n  /*implicit*/ ExtendedCoroutineHandle(\n      coroutine_handle<Promise> handle) noexcept\n      : basic_(handle), extended_(fromBasic(handle)) {}\n\n  /*implicit*/ ExtendedCoroutineHandle(coroutine_handle<> handle) noexcept\n      : basic_(handle) {}\n\n  template <\n      typename Promise,\n      std::enable_if_t<use_extended_handle<Promise>::value, int> = 0>\n  /*implicit*/ ExtendedCoroutineHandle(Promise* promise) noexcept\n      : basic_(coroutine_handle<Promise>::from_promise(*promise)),\n        extended_(Promise::getPromiseBase(PrivateTag{}, promise)) {}\n\n  ExtendedCoroutineHandle() noexcept = default;\n\n  void resume() { basic_.resume(); }\n\n  void destroy() { basic_.destroy(); }\n\n  coroutine_handle<> getHandle() const noexcept { return basic_; }\n\n  ErrorHandle getErrorHandle(exception_wrapper& ex) {\n    if (extended_) {\n      if (auto res = extended_->getErrorHandlePtr_(extended_, ex)) {\n        return *res;\n      }\n    }\n    return {basic_, nullptr};\n  }\n\n  explicit operator bool() const noexcept { return !!basic_; }\n\n private:\n  template <typename Promise>\n  static auto fromBasic(coroutine_handle<Promise> handle) noexcept {\n    if constexpr (use_extended_handle<Promise>::value) {\n      return Promise::getPromiseBase(PrivateTag{}, &handle.promise());\n    } else {\n      return nullptr;\n    }\n  }\n\n  coroutine_handle<> basic_;\n  PromiseBase* extended_{nullptr};\n};\n\n// folly::coro types are expected to implement this extended promise interface.\n//\n// It allows types to provide a more efficient resumption path when they know\n// they will be receiving an error result from the awaitee.\n//\n// First, publicly inherit from `ExtendedCoroutinePromiseCrtp<YourPromise>`,\n// Second, implement this static method on `YourPromise`:\n//\n//   static std::optional<ExtendedCoroutineHandle::ErrorHandle>\n//   getErrorHandleImpl(YourPromise&, exception_wrapper&);\n//\n// Return `std::nullopt` to avoid changing the resumption path.  Otherwise,\n// return the `ExtendedCoroutineHandle` to resume & the active stack frame.\n//\n// DANGER: `YourPromise& promise` is a promise instance, but it might NOT\n// directly correspond to a coro frame.  For example, if your coro is wrapped,\n// that promise is a **member** inside a larger wrapper promise for the coro.\n// Therefore, you must NOT call `coroutine_handle<...>::from_promise(promise)`.\n// In the future, the true handle could be supplied, but none of the current\n// coros required it.\ntemplate <typename Promise>\nclass ExtendedCoroutinePromiseCrtp\n    : public ExtendedCoroutineHandle::PromiseBase {\n public:\n  using use_extended_handle_concept = ExtendedCoroutineHandle::PrivateTag;\n\n  static ExtendedCoroutineHandle::PromiseBase* getPromiseBase(\n      ExtendedCoroutineHandle::PrivateTag, ExtendedCoroutinePromiseCrtp* me) {\n    return me;\n  }\n\n protected:\n  using PromiseBase = typename ExtendedCoroutineHandle::PromiseBase;\n  ExtendedCoroutinePromiseCrtp()\n      : PromiseBase(+[](PromiseBase* p, exception_wrapper& ex) {\n          return Promise::getErrorHandleImpl(*static_cast<Promise*>(p), ex);\n        }) {}\n  ~ExtendedCoroutinePromiseCrtp() = default;\n};\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/CurrentExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <utility>\n\n#include <folly/Executor.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/io/async/Request.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\nnamespace detail {\nstruct co_current_executor_ {\n  enum class secret_ { token_ };\n  explicit constexpr co_current_executor_(secret_) {}\n};\n} // namespace detail\n\nusing co_current_executor_t = detail::co_current_executor_;\n\n// Special placeholder object that can be 'co_await'ed from within a Task<T>\n// or an AsyncGenerator<T> to obtain the current folly::Executor associated\n// with the current coroutine.\n//\n// Note that for a folly::Task the executor will remain the same throughout\n// the lifetime of the coroutine. For a folly::AsyncGenerator<T> the current\n// executor may change when resuming from a co_yield suspend-point.\n//\n// Example:\n//   folly::coro::Task<void> example() {\n//     Executor* e = co_await folly::coro::co_current_executor;\n//     e->add([] { do_something(); });\n//   }\ninline constexpr co_current_executor_t co_current_executor{\n    co_current_executor_t::secret_::token_};\n\nnamespace detail {\n\nclass co_reschedule_on_current_executor_ {\n  class AwaiterBase {\n   public:\n    explicit AwaiterBase(folly::Executor::KeepAlive<> executor) noexcept\n        : executor_(std::move(executor)) {}\n\n    bool await_ready() noexcept { return false; }\n\n    void await_resume() noexcept {}\n\n   protected:\n    folly::Executor::KeepAlive<> executor_;\n  };\n\n public:\n  class StackAwareAwaiter : public AwaiterBase {\n   public:\n    using AwaiterBase::AwaiterBase;\n\n    template <typename Promise>\n    void await_suspend(coroutine_handle<Promise> coro) noexcept {\n      await_suspend_impl(coro, coro.promise().getAsyncFrame());\n    }\n\n   private:\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend_impl(\n        coroutine_handle<> coro, AsyncStackFrame& frame) {\n      auto& stackRoot = *frame.getStackRoot();\n      folly::deactivateAsyncStackFrame(frame);\n      try {\n        executor_->add(\n            [coro, &frame, ctx = RequestContext::saveContext()]() mutable {\n              RequestContextScopeGuard contextScope{std::move(ctx)};\n              folly::resumeCoroutineWithNewAsyncStackRoot(coro, frame);\n            });\n      } catch (...) {\n        folly::activateAsyncStackFrame(stackRoot, frame);\n        throw;\n      }\n    }\n  };\n\n  class Awaiter : public AwaiterBase {\n   public:\n    using AwaiterBase::AwaiterBase;\n\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend(\n        coroutine_handle<> coro) {\n      executor_->add([coro, ctx = RequestContext::saveContext()]() mutable {\n        RequestContextScopeGuard contextScope{std::move(ctx)};\n        coro.resume();\n      });\n    }\n\n    friend StackAwareAwaiter tag_invoke(\n        cpo_t<co_withAsyncStack>, Awaiter awaiter) {\n      return StackAwareAwaiter{std::move(awaiter.executor_)};\n    }\n  };\n\n  friend Awaiter co_viaIfAsync(\n      folly::Executor::KeepAlive<> executor,\n      co_reschedule_on_current_executor_) {\n    return Awaiter{std::move(executor)};\n  }\n};\n\n} // namespace detail\n\nusing co_reschedule_on_current_executor_t =\n    detail::co_reschedule_on_current_executor_;\n\n// A SemiAwaitable object that allows you to reschedule the current coroutine\n// onto the currently associated executor.\n//\n// This can be used as a form of cooperative multi-tasking for coroutines that\n// wish to provide fair access to the execution resources. eg. to periodically\n// give up their current execution slot to allow other tasks to run.\n//\n// Example:\n//   folly::coro::Task<void> doCpuIntensiveWorkFairly() {\n//     for (int i = 0; i < 1'000'000; ++i) {\n//       // Periodically reschedule to the executor.\n//       if ((i % 1024) == 1023) {\n//         co_await folly::coro::co_reschedule_on_current_executor;\n//       }\n//       doSomeWork(i);\n//     }\n//   }\ninline constexpr co_reschedule_on_current_executor_t\n    co_reschedule_on_current_executor;\n\nnamespace detail {\nstruct co_current_cancellation_token_ {\n  enum class secret_ { token_ };\n  explicit constexpr co_current_cancellation_token_(secret_) {}\n};\n} // namespace detail\n\nusing co_current_cancellation_token_t = detail::co_current_cancellation_token_;\n\ninline constexpr co_current_cancellation_token_t co_current_cancellation_token{\n    co_current_cancellation_token_t::secret_::token_};\n\n//  co_safe_point_t\n//  co_safe_point\n//\n//  A semi-awaitable type and value which, when awaited in an async coroutine\n//  supporting safe-points, causes a safe-point to be reached.\n//\n//  Example:\n//\n//    co_await co_safe_point; // a safe-point is reached\n//\n//  At this safe-point:\n//  - If cancellation has been requested then the coroutine is terminated with\n//    cancellation.\n//  - To aid overall system concurrency, the coroutine may be rescheduled onto\n//    the current executor.\n//  - Otherwise, the coroutine is resumed.\n//\n//  Recommended for use wherever cancellation is checked and handled via early\n//  termination.\n//\n//  Technical note: behavior is typically implemented in some overload\n//  of await_transform in the coroutine's promise type, or in the awaitable\n//  or awaiter it returns. Example:\n//\n//      struct /* some coroutine type */ {\n//        struct promise_type {\n//          /* some awaiter */ await_transform(co_safe_point_t) noexcept;\n//        };\n//      };\nclass co_safe_point_t final {};\ninline constexpr co_safe_point_t co_safe_point{};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/DetachOnCancel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/detail/Helpers.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n/**\n * detachOnCancel is used to handle operations that are hard to be cancelled. A\n * typical use case is: The caller starts a task with timeout (in this case, 1\n * sec timeout). The task itself launches a long running job and the job doesn't\n * handle cancellation (sleep_for in this example). The caller has timeout and\n * the cancellation is propagated to the task. The detachOnCancel detects the\n * cancellation and return immediately. However, the background task still runs\n * until the thread join.\n *\n * \\refcode folly/docs/examples/folly/coro/DetachOnCancel.cpp\n *\n * It is important to manage the scope of each variable. If the long running\n * task references any variable that is created in the scope of detachOnCancel,\n * then the result may be freed and the long running task may trigger\n * use-after-free error.\n */\ntemplate <typename Awaitable>\nTask<semi_await_result_t<Awaitable>> detachOnCancel(Awaitable awaitable) {\n  auto posted = std::make_unique<std::atomic<bool>>(false);\n  Baton baton;\n  Try<detail::lift_lvalue_reference_t<semi_await_result_t<Awaitable>>> result;\n\n  {\n    auto t = co_invoke(\n        [awaitable_2 = std::move(\n             awaitable)]() mutable -> Task<semi_await_result_t<Awaitable>> {\n          co_return co_await std::move(awaitable_2);\n        });\n    co_withExecutor(co_await co_current_executor, std::move(t))\n        .startInlineUnsafe(\n            [postedPtr = posted.get(), &baton, &result](auto&& r) {\n              std::unique_ptr<std::atomic<bool>> p(postedPtr);\n              if (!p->exchange(true, std::memory_order_acq_rel)) {\n                p.release();\n                tryAssign(result, std::move(r));\n                baton.post();\n              }\n            },\n            co_await co_current_cancellation_token);\n  }\n\n  {\n    CancellationCallback cancelCallback(\n        co_await co_current_cancellation_token, [&posted, &baton, &result] {\n          if (!posted->exchange(true, std::memory_order_acq_rel)) {\n            posted.release();\n            result.emplaceException(folly::OperationCancelled{});\n            baton.post();\n          }\n        });\n    co_await baton;\n  }\n\n  if (result.hasException()) {\n    co_yield folly::coro::co_error(result.exception());\n  }\n\n  co_return std::move(result).value();\n}\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Error.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// KEEP THIS HEADER MINIMAL -- it is included by all `result` coro users.\n\n#include <cassert>\n#include <type_traits>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/OperationCancelled.h>\n\nnamespace folly::coro {\n\nclass co_error final {\n public:\n  template <\n      typename... A,\n      std::enable_if_t<\n          sizeof...(A) && std::is_constructible<exception_wrapper, A...>::value,\n          int> = 0>\n  explicit co_error(A&&... a) noexcept(\n      std::is_nothrow_constructible<exception_wrapper, A...>::value)\n      : ex_(static_cast<A&&>(a)...) {\n    static_assert(\n        sizeof...(A) != 1 ||\n            !(std::is_same_v<std::decay_t<A>, OperationCancelled> && ...),\n        \"Use co_stopped_may_throw instead of co_error(OperationCancelled{})\");\n    assert(ex_);\n  }\n\n  const exception_wrapper& exception() const { return ex_; }\n\n  exception_wrapper& exception() { return ex_; }\n\n private:\n  exception_wrapper ex_;\n};\n\nclass co_stopped_may_throw_t final {\n public:\n  /* implicit */ operator co_error() const {\n    return co_error(make_exception_wrapper<OperationCancelled>());\n  }\n};\n\ninline constexpr co_stopped_may_throw_t co_stopped_may_throw{};\n\n[[deprecated(\"Use co_stopped_may_throw\")]]\ninline constexpr co_stopped_may_throw_t co_cancelled{};\n\n} // namespace folly::coro\n"
  },
  {
    "path": "folly/coro/Filter-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\ntemplate <typename FilterFn, typename Reference, typename Value>\nAsyncGenerator<Reference, Value> filter(\n    AsyncGenerator<Reference, Value> source, FilterFn filterFn) {\n  while (auto item = co_await source.next()) {\n    if (invoke(filterFn, item.value())) {\n      co_yield std::move(item).value();\n    }\n  }\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Filter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Coroutine.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// Filter the Values from an input stream using an unary predicate.\n//\n// The input is a stream of Values.\n//\n// The output is a stream of Values that satisfy the predicate.\n//\n// Example:\n//   AsyncGenerator<int> getAllNumbers();\n//\n//   AsyncGenerator<int> getEvenNumbers(AsyncGenerator<int> allNumbers) {\n//     return filter(getAllNumbers(), [](int i){ return i % 2 == 0; });\n//   }\ntemplate <typename FilterFn, typename Reference, typename Value>\nAsyncGenerator<Reference, Value> filter(\n    AsyncGenerator<Reference, Value> source, FilterFn filterFn);\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Filter-inl.h>\n"
  },
  {
    "path": "folly/coro/FutureUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Traits.h>\n#include <folly/futures/Future.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// Converts the given SemiAwaitable to a Task (without starting it)\nstruct ToTaskFn {\n  template <typename SemiAwaitable>\n  Task<semi_await_result_t<SemiAwaitable>> operator()(SemiAwaitable a) const {\n    co_return co_await std::move(a);\n  }\n  template <typename SemiAwaitable>\n  Task<semi_await_result_t<SemiAwaitable>> operator()(\n      std::reference_wrapper<SemiAwaitable> a) const {\n    co_return co_await a.get();\n  }\n  Task<void> operator()(folly::Future<Unit> a) const {\n    co_yield co_result(co_await co_awaitTry(std::move(a)));\n  }\n  Task<void> operator()(folly::SemiFuture<Unit> a) const {\n    co_yield co_result(co_await co_awaitTry(std::move(a)));\n  }\n};\ninline constexpr ToTaskFn toTask{};\n\ntemplate <typename V>\nTask<drop_unit_t<V>> toTaskInterruptOnCancel(folly::Future<V> f) {\n  bool cancelled{false};\n  Baton baton;\n  Try<V> result;\n  f.setCallback_(\n      [&result, &baton](Executor::KeepAlive<>&&, Try<V>&& t) {\n        result = std::move(t);\n        baton.post();\n      },\n      // No user logic runs in the callback, we can avoid the cost of switching\n      // the context.\n      /* context */ nullptr);\n\n  {\n    CancellationCallback cancelCallback(\n        co_await co_current_cancellation_token, [&]() noexcept {\n          cancelled = true;\n          f.cancel();\n        });\n    co_await baton;\n  }\n  if (cancelled) {\n    co_yield co_stopped_may_throw;\n  }\n  co_yield co_result(std::move(result));\n}\n\ntemplate <typename V>\nTask<drop_unit_t<V>> toTaskInterruptOnCancel(folly::SemiFuture<V> f) {\n  auto ex = co_await co_current_executor;\n  co_await co_nothrow(toTaskInterruptOnCancel(std::move(f).via(ex)));\n}\n\n// Converts the given SemiAwaitable to a SemiFuture (without starting it)\ntemplate <typename SemiAwaitable>\nfolly::SemiFuture<\n    lift_unit_t<semi_await_result_t<remove_reference_wrapper_t<SemiAwaitable>>>>\ntoSemiFuture(SemiAwaitable&& a) {\n  return toTask(std::forward<SemiAwaitable>(a)).semi();\n}\n\n// Converts the given SemiAwaitable to a Future, starting it on the Executor\ntemplate <typename SemiAwaitable>\nfolly::Future<\n    lift_unit_t<semi_await_result_t<remove_reference_wrapper_t<SemiAwaitable>>>>\ntoFuture(SemiAwaitable&& a, Executor::KeepAlive<> ex) {\n  auto excopy = ex;\n  return co_withExecutor(\n             std::move(excopy), toTask(std::forward<SemiAwaitable>(a)))\n      .start()\n      .via(std::move(ex));\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Generator.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <exception>\n#include <type_traits>\n#include <utility>\n\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Invoke.h>\n#include <folly/lang/Exception.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\ntemplate <typename T>\nclass Generator {\n public:\n  class promise_type final {\n   public:\n    promise_type() noexcept\n        : m_value(nullptr),\n          m_exception(nullptr),\n          m_root(this),\n          m_parentOrLeaf(this) {}\n\n    promise_type(const promise_type&) = delete;\n    promise_type(promise_type&&) = delete;\n\n    auto get_return_object() noexcept { return Generator<T>{*this}; }\n\n    suspend_always initial_suspend() noexcept { return {}; }\n\n    suspend_always final_suspend() noexcept { return {}; }\n\n    void unhandled_exception() noexcept { m_exception = current_exception(); }\n\n    void return_void() noexcept {}\n\n    suspend_always yield_value(T& value) noexcept {\n      m_value = std::addressof(value);\n      return {};\n    }\n\n    suspend_always yield_value(T&& value) noexcept {\n      m_value = std::addressof(value);\n      return {};\n    }\n\n    auto yield_value(Generator&& generator) noexcept {\n      return yield_value(generator);\n    }\n\n    auto yield_value(Generator& generator) noexcept {\n      struct awaitable {\n        awaitable(promise_type* childPromise) : m_childPromise(childPromise) {}\n\n        bool await_ready() noexcept { return this->m_childPromise == nullptr; }\n\n        void await_suspend(coroutine_handle<promise_type>) noexcept {}\n\n        void await_resume() {\n          if (this->m_childPromise != nullptr) {\n            this->m_childPromise->throw_if_exception();\n          }\n        }\n\n       private:\n        promise_type* m_childPromise;\n      };\n\n      if (generator.m_promise != nullptr) {\n        m_root->m_parentOrLeaf = generator.m_promise;\n        generator.m_promise->m_root = m_root;\n        generator.m_promise->m_parentOrLeaf = this;\n        generator.m_promise->resume();\n\n        // NB: This branch looks like a (premature?) optimization for empty\n        // generators, and until proven otherwise in benchmarks, it may be\n        // advantageous to simply return `awaitable{generator.m_promise}`.\n        if (!generator.m_promise->is_complete() ||\n            generator.m_promise->m_exception != nullptr) {\n          return awaitable{generator.m_promise};\n        }\n\n        m_root->m_parentOrLeaf = this;\n      }\n\n      return awaitable{nullptr};\n    }\n\n    // Don't allow any use of 'co_await' inside the Generator\n    // coroutine.\n    template <typename U>\n    void await_transform(U&& value) = delete;\n\n    void destroy() noexcept {\n      coroutine_handle<promise_type>::from_promise(*this).destroy();\n    }\n\n    void throw_if_exception() {\n      if (m_exception != nullptr) {\n        std::rethrow_exception(std::move(m_exception));\n      }\n    }\n\n    bool is_complete() noexcept {\n      return coroutine_handle<promise_type>::from_promise(*this).done();\n    }\n\n    T& value() noexcept {\n      assert(this == m_root);\n      assert(!is_complete());\n      return *(m_parentOrLeaf->m_value);\n    }\n\n    void pull() noexcept {\n      assert(this == m_root);\n      assert(!m_parentOrLeaf->is_complete());\n\n      m_parentOrLeaf->resume();\n\n      while (m_parentOrLeaf != this && m_parentOrLeaf->is_complete()) {\n        m_parentOrLeaf = m_parentOrLeaf->m_parentOrLeaf;\n        m_parentOrLeaf->resume();\n      }\n    }\n\n   private:\n    void resume() noexcept {\n      coroutine_handle<promise_type>::from_promise(*this).resume();\n    }\n\n    std::add_pointer_t<T> m_value;\n    std::exception_ptr m_exception;\n\n    promise_type* m_root;\n\n    // If this is the promise of the root generator then this field\n    // is a pointer to the leaf promise.\n    // For non-root generators this is a pointer to the parent promise.\n    promise_type* m_parentOrLeaf;\n  };\n\n  Generator() noexcept : m_promise(nullptr) {}\n\n  Generator(promise_type& promise) noexcept : m_promise(&promise) {}\n\n  Generator(Generator&& other) noexcept : m_promise(other.m_promise) {\n    other.m_promise = nullptr;\n  }\n\n  Generator(const Generator& other) = delete;\n  Generator& operator=(const Generator& other) = delete;\n\n  ~Generator() {\n    if (m_promise != nullptr) {\n      m_promise->destroy();\n    }\n  }\n\n  Generator& operator=(Generator&& other) noexcept {\n    if (this != &other) {\n      if (m_promise != nullptr) {\n        m_promise->destroy();\n      }\n\n      m_promise = other.m_promise;\n      other.m_promise = nullptr;\n    }\n\n    return *this;\n  }\n\n  class iterator {\n   public:\n    using iterator_category = std::input_iterator_tag;\n    // What type should we use for counting elements of a potentially infinite\n    // sequence?\n    using difference_type = std::ptrdiff_t;\n    using value_type = std::remove_reference_t<T>;\n    using reference = std::conditional_t<std::is_reference_v<T>, T, T&>;\n    using pointer = std::add_pointer_t<T>;\n\n    iterator() noexcept : m_promise(nullptr) {}\n\n    explicit iterator(promise_type* promise) noexcept : m_promise(promise) {}\n\n    bool operator==(const iterator& other) const noexcept {\n      return m_promise == other.m_promise;\n    }\n\n    bool operator!=(const iterator& other) const noexcept {\n      return m_promise != other.m_promise;\n    }\n\n    iterator& operator++() {\n      assert(m_promise != nullptr);\n      assert(!m_promise->is_complete());\n\n      m_promise->pull();\n      if (m_promise->is_complete()) {\n        auto* temp = m_promise;\n        m_promise = nullptr;\n        temp->throw_if_exception();\n      }\n\n      return *this;\n    }\n\n    void operator++(int) { (void)operator++(); }\n\n    reference operator*() const noexcept {\n      assert(m_promise != nullptr);\n      return static_cast<reference>(m_promise->value());\n    }\n\n    pointer operator->() const noexcept { return std::addressof(operator*()); }\n\n   private:\n    promise_type* m_promise;\n  };\n\n  iterator begin() {\n    if (m_promise != nullptr) {\n      m_promise->pull();\n      if (!m_promise->is_complete()) {\n        return iterator(m_promise);\n      }\n\n      m_promise->throw_if_exception();\n    }\n\n    return iterator(nullptr);\n  }\n\n  iterator end() noexcept { return iterator(nullptr); }\n\n  void swap(Generator& other) noexcept {\n    std::swap(m_promise, other.m_promise);\n  }\n\n  template <typename F, typename... A, typename F_, typename... A_>\n  friend Generator tag_invoke(\n      tag_t<co_invoke_fn>, tag_t<Generator, F, A...>, F_ f, A_... a) {\n    auto&& r = invoke(static_cast<F&&>(f), static_cast<A&&>(a)...);\n    for (auto&& v : r) {\n      co_yield std::move(v);\n    }\n  }\n\n private:\n  friend class promise_type;\n\n  promise_type* m_promise;\n};\n\ntemplate <typename T>\nvoid swap(Generator<T>& a, Generator<T>& b) noexcept {\n  a.swap(b);\n}\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/GmockHelpers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <type_traits>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Result.h>\n#include <folly/coro/Task.h>\n#include <folly/portability/GMock.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace gmock_helpers {\n\n// This helper function is intended for use in GMock implementations where the\n// implementation of the method is a coroutine lambda.\n//\n// The GMock framework internally always takes a copy of an action/lambda\n// before invoking it to prevent cases where invoking the method might end\n// up destroying itself.\n//\n// However, this is problematic for coroutine-lambdas-with-captures as the\n// return-value from invoking a coroutine lambda will typically capture a\n// reference to the copy of the lambda which will immediately become a dangling\n// reference as soon as the mocking framework returns that value to the caller.\n//\n// Use this action-factory instead of Invoke() when passing coroutine-lambdas\n// to mock definitions to ensure that a copy of the lambda is kept alive until\n// the coroutine completes. It does this by invoking the lambda using the\n// folly::coro::co_invoke() helper instead of directly invoking the lambda.\n//\n//\n// Example:\n//   using namespace ::testing\n//   using namespace folly::coro::gmock_helpers;\n//\n//   MockFoo mock;\n//   int fooCallCount = 0;\n//\n//   EXPECT_CALL(mock, foo(_))\n//     .WillRepeatedly(CoInvoke(\n//         [&](int x) -> folly::coro::Task<int> {\n//           ++fooCallCount;\n//           co_return x + 1;\n//         }));\n//\ntemplate <typename F>\nauto CoInvoke(F&& f) {\n  return ::testing::Invoke([f = static_cast<F&&>(f)](auto&&... a) {\n    return co_invoke(f, static_cast<decltype(a)>(a)...);\n  });\n}\n\n// Member function overload\ntemplate <class Class, typename MethodPtr>\nauto CoInvoke(Class* obj_ptr, MethodPtr method_ptr) {\n  return ::testing::Invoke([=](auto&&... a) {\n    return co_invoke(method_ptr, obj_ptr, static_cast<decltype(a)>(a)...);\n  });\n}\n\n// CoInvoke variant that does not pass arguments to callback function.\n//\n// Example:\n//   using namespace ::testing\n//   using namespace folly::coro::gmock_helpers;\n//\n//   MockFoo mock;\n//   int fooCallCount = 0;\n//\n//   EXPECT_CALL(mock, foo(_))\n//     .WillRepeatedly(CoInvokeWithoutArgs(\n//         [&]() -> folly::coro::Task<int> {\n//           ++fooCallCount;\n//           co_return 42;\n//         }));\ntemplate <typename F>\nauto CoInvokeWithoutArgs(F&& f) {\n  return ::testing::InvokeWithoutArgs([f = static_cast<F&&>(f)]() {\n    return co_invoke(f);\n  });\n}\n\n// Member function overload\ntemplate <class Class, typename MethodPtr>\nauto CoInvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) {\n  return ::testing::InvokeWithoutArgs([=]() {\n    return co_invoke(method_ptr, obj_ptr);\n  });\n}\n\nnamespace detail {\n\n// Matcher implementation that wraps a Task and delegates to an inner matcher\n// (typically ::testing::Throws or ::testing::ThrowsMessage).\n// This allows CoThrows/CoThrowsMessage to reuse GMock's exception matchers.\ntemplate <typename InnerMatcher>\nclass CoroExceptionMatcherImpl {\n public:\n  explicit CoroExceptionMatcherImpl(InnerMatcher inner_matcher)\n      : inner_matcher_(std::move(inner_matcher)),\n        // Convert the inner matcher to a concrete Matcher type so we can\n        // use its DescribeTo/DescribeNegationTo methods.\n        callable_matcher_(inner_matcher_) {}\n\n  void DescribeTo(std::ostream* os) const { callable_matcher_.DescribeTo(os); }\n\n  void DescribeNegationTo(std::ostream* os) const {\n    callable_matcher_.DescribeNegationTo(os);\n  }\n\n  template <typename T>\n  bool MatchAndExplain(T&& x, ::testing::MatchResultListener* listener) const {\n    // Task is move-only. GoogleTest passes values as const T&, but we need\n    // to move the Task into blockingWait. We use const_cast to enable the\n    // move. This is safe because each matcher instance is used only once.\n    using MutableT = std::remove_const_t<std::remove_reference_t<T>>;\n    auto& mutableX = const_cast<MutableT&>(x);\n\n    // Wrap the task in a shared_ptr so the callable can be const-invoked\n    // (GMock's Throws matcher requires the callable to be const-invocable).\n    auto taskHolder =\n        std::make_shared<std::optional<MutableT>>(std::move(mutableX));\n\n    std::function<void()> callable = [taskHolder]() {\n      if (!taskHolder->has_value()) {\n        throw std::logic_error(\"Task already consumed\");\n      }\n      auto task = std::move(taskHolder->value());\n      taskHolder->reset();\n      blockingWait(std::move(task));\n    };\n\n    // Delegate to the inner matcher (Throws/ThrowsMessage).\n    // GMock's Throws matchers expect a callable, which they invoke to check\n    // for exceptions.\n    return callable_matcher_.MatchAndExplain(callable, listener);\n  }\n\n private:\n  InnerMatcher inner_matcher_;\n  ::testing::Matcher<std::function<void()>> callable_matcher_;\n};\n\ntemplate <typename Fn>\nauto makeCoAction(Fn&& fn) {\n  static_assert(\n      std::is_copy_constructible_v<remove_cvref_t<Fn>>,\n      \"Fn should be copyable to allow calling mocked call multiple times.\");\n\n  using Ret = invoke_result_t<remove_cvref_t<Fn>&&>;\n  return ::testing::InvokeWithoutArgs(\n      [fn = std::forward<Fn>(fn)]() mutable -> Ret { return co_invoke(fn); });\n}\n\n// Helper class to capture a ByMove return value for mocked coroutine function.\n// Adds a test failure if it is moved twice like:\n//    .WillRepeatedly(CoReturnByMove...)\ntemplate <typename R>\nstruct OnceForwarder {\n  static_assert(std::is_reference_v<R>);\n  using V = remove_cvref_t<R>;\n\n  explicit OnceForwarder(R r) noexcept(std::is_nothrow_constructible_v<V>)\n      : val_(static_cast<R>(r)) {}\n\n  R operator()() noexcept {\n    auto performedPreviously =\n        performed_.exchange(true, std::memory_order_relaxed);\n    if (performedPreviously) {\n      terminate_with<std::runtime_error>(\n          \"a CoReturnByMove action must be performed only once\");\n    }\n    return static_cast<R>(val_);\n  }\n\n private:\n  V val_;\n  std::atomic<bool> performed_ = false;\n};\n\n// Allow to return a value by providing a convertible value.\n// This works similarly to Return(x):\n// MOCK_METHOD1(Method, T(U));\n// EXPECT_CALL(mock, Method(_)).WillOnce(Return(F()));\n// should work as long as F is convertible to T.\ntemplate <typename T>\nclass CoReturnImpl {\n public:\n  explicit CoReturnImpl(T&& value) : value_(std::move(value)) {}\n\n  template <typename Result, typename ArgumentTuple>\n  Result Perform(const ArgumentTuple& /* unused */) const {\n    return [](T value) -> Result { co_return value; }(T(value_));\n  }\n\n private:\n  T value_;\n};\n\ntemplate <typename T>\nclass CoReturnByMoveImpl {\n public:\n  explicit CoReturnByMoveImpl(std::shared_ptr<OnceForwarder<T&&>> forwarder)\n      : forwarder_(std::move(forwarder)) {}\n\n  template <typename Result, typename ArgumentTuple>\n  Result Perform(const ArgumentTuple& /* unused */) const {\n    return [](std::shared_ptr<OnceForwarder<T&&>> forwarder) -> Result {\n      co_return (*forwarder)();\n    }(forwarder_);\n  }\n\n private:\n  std::shared_ptr<OnceForwarder<T&&>> forwarder_;\n};\n\n} // namespace detail\n\n// Helper functions to adapt CoRoutines enabled functions to be mocked using\n// gMock. CoReturn and CoThrows are gMock Action types that mirror the Return\n// and Throws Action types used in EXPECT_CALL|ON_CALL invocations.\n//\n// Example:\n//   using namespace ::testing\n//   using namespace folly::coro::gmock_helpers;\n//\n//   MockFoo mock;\n//   std::string result = \"abc\";\n//\n//   EXPECT_CALL(mock, co_foo(_))\n//     .WillRepeatedly(CoReturn(result));\n//\n//   // For Task<void> return types.\n//   EXPECT_CALL(mock, co_bar(_))\n//     .WillRepeatedly(CoReturn());\n//\n//   // For returning by move.\n//   EXPECT_CALL(mock, co_bar(_))\n//     .WillRepeatedly(CoReturnByMove(std::move(result)));\n//\n//   // For returning by move.\n//   EXPECT_CALL(mock, co_bar(_))\n//     .WillRepeatedly(CoReturnByMove(std::make_unique(result)));\n//\n//\n//  EXPECT_CALL(mock, co_foo(_))\n//     .WillRepeatedly(CoThrow<std::string>(std::runtime_error(\"error\")));\ntemplate <typename T>\nauto CoReturn(T ret) {\n  return ::testing::MakePolymorphicAction(\n      detail::CoReturnImpl<T>(std::move(ret)));\n}\n\ninline auto CoReturn() {\n  return ::testing::InvokeWithoutArgs([]() -> Task<> { co_return; });\n}\n\ntemplate <typename T>\nauto CoReturnByMove(T&& ret) {\n  static_assert(\n      !std::is_lvalue_reference_v<decltype(ret)>,\n      \"the argument must be passed as non-const rvalue-ref\");\n  static_assert(\n      !std::is_const_v<T>,\n      \"the argument must be passed as non-const rvalue-ref\");\n\n  auto ptr = std::make_shared<detail::OnceForwarder<T&&>>(std::move(ret));\n\n  return ::testing::MakePolymorphicAction(\n      detail::CoReturnByMoveImpl<T>(std::move(ptr)));\n}\n\ntemplate <typename T, typename Ex>\nauto CoThrow(Ex&& e) {\n  return detail::makeCoAction([ex = std::forward<Ex>(e)]() -> Task<T> {\n    co_yield co_error(ex);\n  });\n}\n\ntemplate <typename T>\nauto CoStoppedMayThrow() {\n  return detail::makeCoAction([]() -> Task<T> {\n    co_yield co_stopped_may_throw;\n  });\n}\n\n// CoThrows()\n// CoThrows(exceptionMatcher)\n// CoThrowsMessage(messageMatcher)\n//\n// These matchers accept a folly::coro::Task and verify that when awaited,\n// it throws an exception with the given type and properties.\n//\n// Examples:\n//\n//   EXPECT_THAT(\n//       []() -> folly::coro::Task<void> {\n//         throw std::runtime_error(\"message\");\n//         co_return;\n//       }(),\n//       CoThrows<std::runtime_error>());\n//\n//   EXPECT_THAT(\n//       []() -> folly::coro::Task<void> {\n//         throw std::runtime_error(\"message\");\n//         co_return;\n//       }(),\n//       CoThrowsMessage<std::runtime_error>(HasSubstr(\"message\")));\n//\n//   EXPECT_THAT(\n//       []() -> folly::coro::Task<void> {\n//         throw std::runtime_error(\"message\");\n//         co_return;\n//       }(),\n//       CoThrows<std::runtime_error>(\n//           Property(&std::runtime_error::what, HasSubstr(\"message\"))));\n\ntemplate <typename Err>\nauto CoThrows() {\n  return ::testing::MakePolymorphicMatcher(\n      detail::CoroExceptionMatcherImpl<decltype(::testing::Throws<Err>())>(\n          ::testing::Throws<Err>()));\n}\n\ntemplate <typename Err, typename ExceptionMatcher>\nauto CoThrows(const ExceptionMatcher& exception_matcher) {\n  return ::testing::MakePolymorphicMatcher(\n      detail::CoroExceptionMatcherImpl<decltype(::testing::Throws<Err>(\n          exception_matcher))>(::testing::Throws<Err>(exception_matcher)));\n}\n\ntemplate <typename Err, typename MessageMatcher>\nauto CoThrowsMessage(MessageMatcher&& message_matcher) {\n  static_assert(\n      std::is_base_of_v<std::exception, Err>,\n      \"expected an std::exception-derived type\");\n  return ::testing::MakePolymorphicMatcher(\n      detail::CoroExceptionMatcherImpl<decltype(::testing::ThrowsMessage<Err>(\n          std::forward<MessageMatcher>(message_matcher)))>(\n          ::testing::ThrowsMessage<Err>(\n              std::forward<MessageMatcher>(message_matcher))));\n}\n\n} // namespace gmock_helpers\n} // namespace coro\n} // namespace folly\n\n#define CO_ASSERT_THAT(value, matcher) \\\n  CO_ASSERT_PRED_FORMAT1(              \\\n      ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/GtestHelpers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n#include <folly/debugging/exception_tracer/SmartExceptionTracer.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename Out>\ninline auto gtestLogCurrentException(Out&& out) {\n  auto ew = exception_wrapper(std::current_exception());\n#ifdef FOLLY_HAVE_SMART_EXCEPTION_TRACER\n  auto trace = folly::exception_tracer::getAsyncTrace(ew);\n  out << ew << \", async stack trace: \" << trace;\n#else\n  out << ew;\n#endif\n}\n\n} // namespace detail\n} // namespace folly\n\n/**\n * This is based on the GTEST_TEST_ macro from gtest-internal.h. It seems that\n * gtest doesn't yet support coro tests, so this macro adds a way to define a\n * test case written as a coroutine using folly::coro::Task. It will be called\n * using folly::coro::blockingWait().\n *\n * Note that you cannot use ASSERT macros in coro tests. See below for\n * CO_ASSERT_*.\n */\n#define CO_TEST_(                                                              \\\n    test_suite_name,                                                           \\\n    test_name,                                                                 \\\n    parent_class,                                                              \\\n    parent_id,                                                                 \\\n    body_coro_t,                                                               \\\n    unwrap_body)                                                               \\\n  static_assert(                                                               \\\n      sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1,                           \\\n      \"test_suite_name must not be empty\");                                    \\\n  static_assert(                                                               \\\n      sizeof(GTEST_STRINGIFY_(test_name)) > 1, \"test_name must not be empty\"); \\\n  class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                     \\\n      : public parent_class {                                                  \\\n   public:                                                                     \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default;            \\\n    ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default;  \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \\\n    (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete;     \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \\\n        const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) =          \\\n        delete; /* NOLINT */                                                   \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \\\n    (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \\\n        GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept =      \\\n        delete; /* NOLINT */                                                   \\\n                                                                               \\\n   private:                                                                    \\\n    void TestBody() override;                                                  \\\n    body_coro_t co_TestBody();                                                 \\\n    static ::testing::TestInfo* const test_info_ [[maybe_unused]];             \\\n  };                                                                           \\\n                                                                               \\\n  ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(                           \\\n      test_suite_name, test_name)::test_info_ =                                \\\n      ::testing::internal::MakeAndRegisterTestInfo(                            \\\n          #test_suite_name,                                                    \\\n          #test_name,                                                          \\\n          nullptr,                                                             \\\n          nullptr,                                                             \\\n          ::testing::internal::CodeLocation(__FILE__, __LINE__),               \\\n          (parent_id),                                                         \\\n          ::testing::internal::SuiteApiResolver<                               \\\n              parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__),          \\\n          ::testing::internal::SuiteApiResolver<                               \\\n              parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__),       \\\n          new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_(     \\\n              test_suite_name, test_name)>);                                   \\\n  void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() {        \\\n    unwrap_body(co_TestBody);                                                  \\\n  }                                                                            \\\n  body_coro_t GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::co_TestBody()\n\n#define CO_UNWRAP_BODY(body)                                    \\\n  try {                                                         \\\n    folly::coro::blockingWait(body());                          \\\n  } catch (...) {                                               \\\n    folly::detail::gtestLogCurrentException(GTEST_LOG_(ERROR)); \\\n    throw;                                                      \\\n  }\n\n/**                                    \\\n * TEST() for coro tests.              \\\n */\n#define CO_TEST(test_case_name, test_name)  \\\n  CO_TEST_(                                 \\\n      test_case_name,                       \\\n      test_name,                            \\\n      ::testing::Test,                      \\\n      ::testing::internal::GetTestTypeId(), \\\n      folly::coro::Task<void>,              \\\n      CO_UNWRAP_BODY)\n\n/**\n * TEST_F() for coro tests.\n */\n#define CO_TEST_F(test_fixture, test_name)            \\\n  CO_TEST_(                                           \\\n      test_fixture,                                   \\\n      test_name,                                      \\\n      test_fixture,                                   \\\n      ::testing::internal::GetTypeId<test_fixture>(), \\\n      folly::coro::Task<void>,                        \\\n      CO_UNWRAP_BODY)\n\n#define CO_TEST_P(test_suite_name, test_name)                                  \\\n  class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                     \\\n      : public test_suite_name {                                               \\\n   public:                                                                     \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {}                    \\\n    ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default;  \\\n    void TestBody() override;                                                  \\\n    folly::coro::Task<void> co_TestBody();                                     \\\n                                                                               \\\n   private:                                                                    \\\n    static int AddToRegistry() {                                               \\\n      ::testing::UnitTest::GetInstance()                                       \\\n          ->parameterized_test_registry()                                      \\\n          .GetTestSuitePatternHolder<test_suite_name>(                         \\\n              GTEST_STRINGIFY_(test_suite_name),                               \\\n              ::testing::internal::CodeLocation(__FILE__, __LINE__))           \\\n          ->AddTestPattern(                                                    \\\n              GTEST_STRINGIFY_(test_suite_name),                               \\\n              GTEST_STRINGIFY_(test_name),                                     \\\n              new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \\\n                  test_suite_name, test_name)>(),                              \\\n              ::testing::internal::CodeLocation(__FILE__, __LINE__));          \\\n      return 0;                                                                \\\n    }                                                                          \\\n    static int gtest_registering_dummy_ [[maybe_unused]];                      \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \\\n    (const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete;     \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                         \\\n    (GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) = delete;          \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \\\n        const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) =          \\\n        delete; /* NOLINT */                                                   \\\n    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=(            \\\n        GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) =               \\\n        delete; /* NOLINT */                                                   \\\n  };                                                                           \\\n  int GTEST_TEST_CLASS_NAME_(                                                  \\\n      test_suite_name, test_name)::gtest_registering_dummy_ =                  \\\n      GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry();     \\\n  void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() {        \\\n    try {                                                                      \\\n      folly::coro::blockingWait(co_TestBody());                                \\\n    } catch (...) {                                                            \\\n      folly::detail::gtestLogCurrentException(GTEST_LOG_(ERROR));              \\\n      throw;                                                                   \\\n    }                                                                          \\\n  }                                                                            \\\n  folly::coro::Task<void> GTEST_TEST_CLASS_NAME_(                              \\\n      test_suite_name, test_name)::co_TestBody()\n\n#define CO_TYPED_TEST(CaseName, TestName)                                     \\\n  static_assert(                                                              \\\n      sizeof(GTEST_STRINGIFY_(TestName)) > 1, \"test-name must not be empty\"); \\\n  template <typename gtest_TypeParam_>                                        \\\n  class GTEST_TEST_CLASS_NAME_(CaseName, TestName)                            \\\n      : public CaseName<gtest_TypeParam_> {                                   \\\n   private:                                                                   \\\n    typedef CaseName<gtest_TypeParam_> TestFixture;                           \\\n    typedef gtest_TypeParam_ TypeParam;                                       \\\n    void TestBody() override;                                                 \\\n    folly::coro::Task<void> co_TestBody();                                    \\\n  };                                                                          \\\n  static const bool gtest_##CaseName##_##TestName##_registered_               \\\n      [[maybe_unused]] = ::testing::internal::TypeParameterizedTest<          \\\n          CaseName,                                                           \\\n          ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(            \\\n              CaseName, TestName)>,                                           \\\n          GTEST_TYPE_PARAMS_(CaseName)>::                                     \\\n          Register(                                                           \\\n              \"\",                                                             \\\n              ::testing::internal::CodeLocation(__FILE__, __LINE__),          \\\n              GTEST_STRINGIFY_(CaseName),                                     \\\n              GTEST_STRINGIFY_(TestName),                                     \\\n              0,                                                              \\\n              ::testing::internal::GenerateNames<                             \\\n                  GTEST_NAME_GENERATOR_(CaseName),                            \\\n                  GTEST_TYPE_PARAMS_(CaseName)>());                           \\\n  template <typename gtest_TypeParam_>                                        \\\n  void GTEST_TEST_CLASS_NAME_(                                                \\\n      CaseName, TestName)<gtest_TypeParam_>::TestBody() {                     \\\n    try {                                                                     \\\n      folly::coro::blockingWait(co_TestBody());                               \\\n    } catch (...) {                                                           \\\n      folly::detail::gtestLogCurrentException(GTEST_LOG_(ERROR));             \\\n      throw;                                                                  \\\n    }                                                                         \\\n  }                                                                           \\\n  template <typename gtest_TypeParam_>                                        \\\n  folly::coro::Task<void> GTEST_TEST_CLASS_NAME_(                             \\\n      CaseName, TestName)<gtest_TypeParam_>::co_TestBody()\n\n#define CO_TYPED_TEST_P(SuiteName, TestName)                      \\\n  namespace GTEST_SUITE_NAMESPACE_(SuiteName) {                   \\\n  template <typename gtest_TypeParam_>                            \\\n  class TestName : public SuiteName<gtest_TypeParam_> {           \\\n   private:                                                       \\\n    typedef SuiteName<gtest_TypeParam_> TestFixture;              \\\n    typedef gtest_TypeParam_ TypeParam;                           \\\n    void TestBody() override;                                     \\\n    folly::coro::Task<> co_TestBody();                            \\\n  };                                                              \\\n  [[maybe_unused]] static bool gtest_##TestName##_defined_ =      \\\n      GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName(     \\\n          __FILE__,                                               \\\n          __LINE__,                                               \\\n          GTEST_STRINGIFY_(SuiteName),                            \\\n          GTEST_STRINGIFY_(TestName));                            \\\n  }                                                               \\\n  template <typename gtest_TypeParam_>                            \\\n  void GTEST_SUITE_NAMESPACE_(                                    \\\n      SuiteName)::TestName<gtest_TypeParam_>::TestBody() {        \\\n    try {                                                         \\\n      folly::coro::blockingWait(co_TestBody());                   \\\n    } catch (...) {                                               \\\n      folly::detail::gtestLogCurrentException(GTEST_LOG_(ERROR)); \\\n      throw;                                                      \\\n    }                                                             \\\n  }                                                               \\\n  template <typename gtest_TypeParam_>                            \\\n  folly::coro::Task<void> GTEST_SUITE_NAMESPACE_(                 \\\n      SuiteName)::TestName<gtest_TypeParam_>::co_TestBody()\n\n/**\n * Coroutine versions of GTests's Assertion predicate macros. Use these in place\n * of ASSERT_* in CO_TEST or coroutine functions.\n */\n#define CO_GTEST_FATAL_FAILURE_(message) \\\n  co_return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)\n\n#define CO_ASSERT_PRED_FORMAT1(pred_format, v1) \\\n  GTEST_PRED_FORMAT1_(pred_format, v1, CO_GTEST_FATAL_FAILURE_)\n#define CO_ASSERT_PRED_FORMAT2(pred_format, v1, v2) \\\n  GTEST_PRED_FORMAT2_(pred_format, v1, v2, CO_GTEST_FATAL_FAILURE_)\n\n#define CO_ASSERT_TRUE(condition) \\\n  GTEST_TEST_BOOLEAN_(            \\\n      (condition), #condition, false, true, CO_GTEST_FATAL_FAILURE_)\n#define CO_ASSERT_FALSE(condition) \\\n  GTEST_TEST_BOOLEAN_(             \\\n      !(condition), #condition, true, false, CO_GTEST_FATAL_FAILURE_)\n\n#if defined(GTEST_IS_NULL_LITERAL_)\n#define CO_ASSERT_EQ(val1, val2)                                            \\\n  CO_ASSERT_PRED_FORMAT2(                                                   \\\n      ::testing::internal::EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \\\n      val1,                                                                 \\\n      val2)\n#else\n#define CO_ASSERT_EQ(val1, val2) \\\n  CO_ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)\n#endif\n\n#define CO_ASSERT_NE(val1, val2) \\\n  CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)\n#define CO_ASSERT_LE(val1, val2) \\\n  CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)\n#define CO_ASSERT_LT(val1, val2) \\\n  CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)\n#define CO_ASSERT_GE(val1, val2) \\\n  CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)\n#define CO_ASSERT_GT(val1, val2) \\\n  CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)\n\n#define CO_ASSERT_THROW(statement, expected_exception) \\\n  GTEST_TEST_THROW_(statement, expected_exception, CO_GTEST_FATAL_FAILURE_)\n#define CO_ASSERT_NO_THROW(statement) \\\n  GTEST_TEST_NO_THROW_(statement, CO_GTEST_FATAL_FAILURE_)\n#define CO_ASSERT_ANY_THROW(statement) \\\n  GTEST_TEST_ANY_THROW_(statement, CO_GTEST_FATAL_FAILURE_)\n\n/**\n * coroutine version of FAIL() which is defined as GTEST_FAIL()\n * GTEST_FATAL_FAILURE_(\"Failed\")\n */\n#define CO_FAIL() CO_GTEST_FATAL_FAILURE_(\"Failed\")\n\n/**\n * Coroutine version of SKIP() which is defined as GTEST_SKIP()\n */\n#define CO_SKIP(message) \\\n  co_return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip)\n\n/**\n * Coroutine version of SKIP_IF()\n */\n#define CO_SKIP_IF(expr, message) \\\n  GTEST_AMBIGUOUS_ELSE_BLOCKER_   \\\n  if (!(expr)) {                  \\\n  } else                          \\\n    CO_SKIP(message)\n\n/**\n * Coroutine version of SUCCEED() which is defined as GTEST_SUCCEED()\n */\n#define CO_SUCCEED(message) \\\n  co_return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)\n\n/**\n * Coroutine version\n */\n#define CO_SUCCEED_IF(expr, message) \\\n  GTEST_AMBIGUOUS_ELSE_BLOCKER_      \\\n  if (!(expr)) {                     \\\n  } else                             \\\n    CO_SUCCEED(message)\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Invoke.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/functional/Invoke.h>\n#include <folly/lang/CustomizationPoint.h>\n\nnamespace folly {\nnamespace coro {\n\n//  co_invoke\n//\n//  This utility callable is a safe way to instantiate a coroutine using a\n//  coroutine callable. It guarantees that the callable and the arguments\n//  outlive the coroutine which invocation returns. Otherwise, the callable\n//  and the arguments are not safe to be used within the coroutine body.\n//\n//  For example, if the callable is a lambda with captures, the captures would\n//  not otherwise be safe to use in the coroutine body without using co_invoke.\n//\n//  Models invoke for any callable which returns a coroutine type which declares\n//  support for co_invoke, including:\n//    * AsyncGenerator<...>\n//    * Task<...>\n//\n//  Like invoke in that the callable is invoked with the cvref-qual with which\n//  it is passed to co_invoke and the arguments are passed with the cvref-quals\n//  with which they were passed to co_invoke.\n//\n//  Different from invoke in that the callable and all arguments are decay-\n//  copied and it is the copies that are held for the lifetime of the coroutine\n//  and used in the invocation, whereas invoke merely forwards them directly in\n//  the invocation without first constructing any values copied from them.\n//\n//  Example:\n//\n//      auto gen = co_invoke([range]() -> AsyncGenerator<T> {\n//        for (auto value : range) {\n//          co_yield co_await make<T>(value);\n//        }\n//      });\n//\n//  Example:\n//\n//      auto task = co_invoke([name, dob]() -> Task<T> {\n//        co_return co_await make<T>(name, dob);\n//      });\n//\n//  A word of caution. The callable and each argument is decay-copied by the\n//  customizations. No effort is made to coalesce copies when copies would have\n//  been made with direct invocation.\n//\n//      string name = \"foobar\"; // will be copied twice\n//      auto task = co_invoke([](string n) -> Task<T> {\n//        co_return co_await make<T>(n);\n//      }, name); // passed as &\n//\n//      string name = \"foobar\"; // will be moved twice and copied zero times\n//      auto task = co_invoke([](string n) -> Task<T> {\n//        co_return co_await make<T>(n);\n//      }, std::move(name)); // passed as &&\nstruct co_invoke_fn {\n  template <typename F, typename... A>\n  FOLLY_ERASE constexpr auto\n  operator()(F&& f, A&&... a) const noexcept(noexcept(tag_invoke(\n      tag<co_invoke_fn>,\n      tag<invoke_result_t<F, A...>, F, A...>,\n      static_cast<F&&>(f),\n      static_cast<A&&>(a)...)))\n      -> decltype(tag_invoke(\n          tag<co_invoke_fn>,\n          tag<invoke_result_t<F, A...>, F, A...>,\n          static_cast<F&&>(f),\n          static_cast<A&&>(a)...)) {\n    return tag_invoke(\n        tag<co_invoke_fn>,\n        tag<invoke_result_t<F, A...>, F, A...>,\n        static_cast<F&&>(f),\n        static_cast<A&&>(a)...);\n  }\n};\nFOLLY_DEFINE_CPO(co_invoke_fn, co_invoke)\n\n} // namespace coro\n} // namespace folly\n"
  },
  {
    "path": "folly/coro/Merge-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <exception>\n#include <memory>\n\n#include <folly/CancellationToken.h>\n#include <folly/Executor.h>\n#include <folly/ScopeGuard.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/coro/detail/Barrier.h>\n#include <folly/coro/detail/BarrierTask.h>\n#include <folly/coro/detail/CurrentAsyncFrame.h>\n#include <folly/coro/detail/Helpers.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\nenum class CallbackRecordSelector { Invalid, Value, None, Error };\n\nconstexpr inline std::in_place_index_t<0> const callback_record_value{};\nconstexpr inline std::in_place_index_t<1> const callback_record_none{};\nconstexpr inline std::in_place_index_t<2> const callback_record_error{};\n\n//\n// CallbackRecord records the result of a single invocation of a callback.\n//\n// This is very related to Try and expected, but this also records None in\n// addition to Value and Error results.\n//\n// When the callback supports multiple overloads of Value then T would be\n// something like a variant<tuple<..>, ..>\n//\n// When the callback supports multiple overloads of Error then all the errors\n// are coerced to folly::exception_wrapper\n//\ntemplate <class T>\nclass CallbackRecord {\n  static void clear(CallbackRecord* that) {\n    auto selector =\n        std::exchange(that->selector_, CallbackRecordSelector::Invalid);\n    if (selector == CallbackRecordSelector::Value) {\n      detail::deactivate(that->value_);\n    } else if (selector == CallbackRecordSelector::Error) {\n      detail::deactivate(that->error_);\n    }\n  }\n  template <class OtherReference>\n  static void convert_variant(\n      CallbackRecord* that, const CallbackRecord<OtherReference>& other) {\n    if (other.hasValue()) {\n      detail::activate(that->value_, other.value_.get());\n    } else if (other.hasError()) {\n      detail::activate(that->error_, other.error_.get());\n    }\n    that->selector_ = other.selector_;\n  }\n  template <class OtherReference>\n  static void convert_variant(\n      CallbackRecord* that, CallbackRecord<OtherReference>&& other) {\n    if (other.hasValue()) {\n      detail::activate(that->value_, std::move(other.value_).get());\n    } else if (other.hasError()) {\n      detail::activate(that->error_, std::move(other.error_).get());\n    }\n    that->selector_ = other.selector_;\n  }\n\n public:\n  ~CallbackRecord() { clear(this); }\n\n  CallbackRecord() noexcept : selector_(CallbackRecordSelector::Invalid) {}\n\n  template <class V>\n  CallbackRecord(const std::in_place_index_t<0>&, V&& v) noexcept(\n      std::is_nothrow_constructible_v<T, V>)\n      : CallbackRecord() {\n    detail::activate(value_, std::forward<V>(v));\n    selector_ = CallbackRecordSelector::Value;\n  }\n  explicit CallbackRecord(const std::in_place_index_t<1>&) noexcept\n      : selector_(CallbackRecordSelector::None) {}\n  CallbackRecord(\n      const std::in_place_index_t<2>&, folly::exception_wrapper e) noexcept\n      : CallbackRecord() {\n    detail::activate(error_, std::move(e));\n    selector_ = CallbackRecordSelector::Error;\n  }\n\n  CallbackRecord(CallbackRecord&& other) noexcept(\n      std::is_nothrow_move_constructible_v<T>)\n      : CallbackRecord() {\n    convert_variant(this, std::move(other));\n  }\n\n  CallbackRecord& operator=(CallbackRecord&& other) noexcept(\n      std::is_nothrow_move_constructible_v<T>) {\n    if (&other != this) {\n      clear(this);\n      convert_variant(this, std::move(other));\n    }\n    return *this;\n  }\n\n  template <class U>\n  CallbackRecord(CallbackRecord<U>&& other) noexcept(\n      std::is_nothrow_constructible_v<T, U>)\n      : CallbackRecord() {\n    convert_variant(this, std::move(other));\n  }\n\n  bool hasNone() const noexcept {\n    return selector_ == CallbackRecordSelector::None;\n  }\n\n  bool hasError() const noexcept {\n    return selector_ == CallbackRecordSelector::Error;\n  }\n\n  decltype(auto) error() & {\n    DCHECK(hasError());\n    return error_.get();\n  }\n\n  decltype(auto) error() && {\n    DCHECK(hasError());\n    return std::move(error_).get();\n  }\n\n  decltype(auto) error() const& {\n    DCHECK(hasError());\n    return error_.get();\n  }\n\n  decltype(auto) error() const&& {\n    DCHECK(hasError());\n    return std::move(error_).get();\n  }\n\n  bool hasValue() const noexcept {\n    return selector_ == CallbackRecordSelector::Value;\n  }\n\n  decltype(auto) value() & {\n    DCHECK(hasValue());\n    return value_.get();\n  }\n\n  decltype(auto) value() && {\n    DCHECK(hasValue());\n    return std::move(value_).get();\n  }\n\n  decltype(auto) value() const& {\n    DCHECK(hasValue());\n    return value_.get();\n  }\n\n  decltype(auto) value() const&& {\n    DCHECK(hasValue());\n    return std::move(value_).get();\n  }\n\n  explicit operator bool() const noexcept {\n    return selector_ != CallbackRecordSelector::Invalid;\n  }\n\n private:\n  union {\n    detail::ManualLifetime<T> value_;\n    detail::ManualLifetime<folly::exception_wrapper> error_;\n  };\n  CallbackRecordSelector selector_;\n};\n\ntemplate <typename Reference, typename Value, typename GeneratorType>\nAsyncGenerator<Reference, Value> mergeImpl(\n    folly::Executor::KeepAlive<> executor, GeneratorType sources) {\n  struct SharedState {\n    explicit SharedState(folly::Executor::KeepAlive<> executor_)\n        : executor(std::move(executor_)) {}\n\n    const folly::Executor::KeepAlive<> executor;\n    const folly::CancellationSource cancelSource;\n    coro::Mutex mutex;\n    coro::Baton recordPublished;\n    coro::Baton recordConsumed;\n    coro::Baton allTasksCompleted;\n    detail::CallbackRecord<Reference> record;\n  };\n\n  auto makeConsumerTask =\n      [](std::shared_ptr<SharedState> state,\n         GeneratorType sources_) -> Task<void> {\n    auto makeWorkerTask =\n        [](std::shared_ptr<SharedState> state_,\n           AsyncGenerator<Reference, Value> generator)\n        -> detail::DetachedBarrierTask {\n      exception_wrapper ex;\n      auto cancelToken = state_->cancelSource.getToken();\n      try {\n        while (auto item = co_await co_viaIfAsync(\n                   state_->executor.get_alias(),\n                   co_withCancellation(cancelToken, generator.next()))) {\n          // We have a new value to emit in the merged stream.\n          {\n            auto lock = co_await co_viaIfAsync(\n                state_->executor.get_alias(), state_->mutex.co_scoped_lock());\n\n            if (cancelToken.isCancellationRequested()) {\n              // Consumer has detached and doesn't want any more values.\n              // Discard this value.\n              break;\n            }\n\n            // Publish the value.\n            state_->record = detail::CallbackRecord<Reference>{\n                detail::callback_record_value, *std::move(item)};\n            state_->recordPublished.post();\n\n            // Wait until the consumer is finished with it.\n            co_await co_viaIfAsync(\n                state_->executor.get_alias(), state_->recordConsumed);\n            state_->recordConsumed.reset();\n\n            // Clear the result before releasing the lock.\n            state_->record = {};\n          }\n\n          if (cancelToken.isCancellationRequested()) {\n            break;\n          }\n        }\n      } catch (...) {\n        ex = exception_wrapper{current_exception()};\n      }\n\n      if (ex) {\n        state_->cancelSource.requestCancellation();\n\n        auto lock = co_await co_viaIfAsync(\n            state_->executor.get_alias(), state_->mutex.co_scoped_lock());\n        if (!state_->record.hasError()) {\n          state_->record = detail::CallbackRecord<Reference>{\n              detail::callback_record_error, std::move(ex)};\n          state_->recordPublished.post();\n        }\n      }\n    };\n\n    detail::Barrier barrier{1};\n\n    auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n    // Save the initial context and restore it after starting each task\n    // as the task may have modified the context before suspending and we\n    // want to make sure the next task is started with the same initial\n    // context.\n    const auto context = RequestContext::saveContext();\n\n    exception_wrapper ex;\n    try {\n      while (auto item = co_await sources_.next()) {\n        if (state->cancelSource.isCancellationRequested()) {\n          break;\n        }\n        makeWorkerTask(state, *std::move(item)).start(&barrier, asyncFrame);\n        RequestContext::setContext(context);\n      }\n    } catch (...) {\n      ex = exception_wrapper{current_exception()};\n    }\n\n    if (ex) {\n      state->cancelSource.requestCancellation();\n\n      auto lock = co_await co_viaIfAsync(\n          state->executor.get_alias(), state->mutex.co_scoped_lock());\n      if (!state->record.hasError()) {\n        state->record = detail::CallbackRecord<Reference>{\n            detail::callback_record_error, std::move(ex)};\n        state->recordPublished.post();\n      }\n    }\n\n    // Wait for all worker tasks to finish consuming the entirety of their\n    // input streams.\n    co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n\n    // Guaranteed there are no more concurrent producers trying to acquire\n    // the mutex here.\n    if (!state->record.hasError()) {\n      // Stream not yet been terminated with an error.\n      // Terminate the stream with the 'end()' signal.\n      assert(!state->record.hasValue());\n      state->record =\n          detail::CallbackRecord<Reference>{detail::callback_record_none};\n      state->recordPublished.post();\n    }\n  };\n\n  auto state = std::make_shared<SharedState>(executor);\n\n  SCOPE_EXIT {\n    state->cancelSource.requestCancellation();\n    // Make sure we resume the worker thread so that it has a chance to notice\n    // that cancellation has been requested.\n    state->recordConsumed.post();\n  };\n\n  // Start a task that consumes the stream of input streams.\n  co_withExecutor(executor, makeConsumerTask(state, std::move(sources)))\n      .start(\n          [state](auto&&) { state->allTasksCompleted.post(); },\n          state->cancelSource.getToken());\n\n  // Consume values produced by the input streams.\n  while (true) {\n    if (!state->recordPublished.ready()) {\n      folly::CancellationCallback cb{\n          co_await co_current_cancellation_token,\n          [&] { state->cancelSource.requestCancellation(); }};\n      co_await state->recordPublished;\n    }\n    state->recordPublished.reset();\n\n    if (state->record.hasValue()) {\n      // next value\n      co_yield std::move(state->record).value();\n      state->recordConsumed.post();\n    } else {\n      // We're closing the output stream. In the spirit of structured\n      // concurrency, let's make sure to not leave any background tasks behind.\n      co_await state->allTasksCompleted;\n\n      if (state->record.hasError()) {\n        std::move(state->record).error().throw_exception();\n      } else {\n        // none\n        assert(state->record.hasNone());\n        break;\n      }\n    }\n  }\n}\n} // namespace detail\n\ntemplate <typename Reference, typename Value>\nAsyncGenerator<Reference, Value> merge(\n    folly::Executor::KeepAlive<> executor,\n    AsyncGenerator<AsyncGenerator<Reference, Value>&&> sources) {\n  return detail::mergeImpl<\n      Reference,\n      Value,\n      AsyncGenerator<AsyncGenerator<Reference, Value>&&>>(\n      std::move(executor), std::move(sources));\n}\n\ntemplate <typename Reference, typename Value>\nAsyncGenerator<Reference, Value> merge(\n    folly::Executor::KeepAlive<> executor,\n    AsyncGenerator<AsyncGenerator<Reference, Value>> sources) {\n  return detail::mergeImpl<\n      Reference,\n      Value,\n      AsyncGenerator<AsyncGenerator<Reference, Value>>>(\n      std::move(executor), std::move(sources));\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Merge.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Coroutine.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// Merge the results of a number of input streams.\n//\n// The 'executor' parameter specifies the execution context to\n// be used for awaiting each value from the sources.\n// The 'sources' parameter represents an async-stream of async-streams.\n// The resulting generator merges the results from each of the streams\n// produced by 'sources', interleaving them in the order that the values\n// are produced.\n//\n// The resulting stream will terminate when the end of the 'sources' stream has\n// been reached and the ends of all of the input streams it produced have been\n// reached.\n//\n// On exception or cancellation, cancels remaining input streams and 'sources',\n// discards any remaining values, and produces an exception (if an input stream\n// produced an exception) or end-of-stream (if next() call was cancelled).\n//\n// Structured concurrency: if the output stream produced an empty value\n// (end-of-stream) or an exception, it's guaranteed that 'sources' and all input\n// generators have been destroyed.\n// If the output stream is destroyed early (before reaching end-of-stream or\n// exception), the remaining input generators are cancelled and detached; beware\n// of use-after-free.\n//\n// Normally cancelling output stream's next() call cancels the stream, discards\n// any remaining values, and returns an end-of-stream. But there are caveats:\n//  * If there's an item ready to be delivered, next() call returns it without\n//    checking for cancellation. So if input streams are fast, and next() is\n//    called infrequently, cancellation may go unprocessed indefinitely unless\n//    you also check for cancellation on your side (which you should probably do\n//    anyway unless you're calling next() in a tight loop).\n//  * It's possible that the cancelled next() registers the cancellation but\n//    returns a value anyway (if it was produced at just the right moment). Then\n//    a later next() call would return end-of-stream even if it was called with\n//    a different, non-cancelled cancellation token.\ntemplate <typename Reference, typename Value>\nAsyncGenerator<Reference, Value> merge(\n    folly::Executor::KeepAlive<> executor,\n    AsyncGenerator<AsyncGenerator<Reference, Value>&&> sources);\n\ntemplate <typename Reference, typename Value>\nAsyncGenerator<Reference, Value> merge(\n    folly::Executor::KeepAlive<> executor,\n    AsyncGenerator<AsyncGenerator<Reference, Value>> sources);\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Merge-inl.h>\n"
  },
  {
    "path": "folly/coro/Mutex.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Mutex.h>\n\n#include <cassert>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nMutex::~Mutex() {\n  // Check there are no waiters waiting to acquire the lock.\n  assert(\n      state_.load(std::memory_order_relaxed) == unlockedState() ||\n      state_.load(std::memory_order_relaxed) == nullptr);\n  assert(waiters_ == nullptr);\n}\n\nvoid Mutex::unlock() noexcept {\n  assert(state_.load(std::memory_order_relaxed) != unlockedState());\n\n  auto* waitersHead = waiters_;\n  if (waitersHead == nullptr) {\n    void* currentState = state_.load(std::memory_order_relaxed);\n    if (currentState == nullptr) {\n      // Looks like there are no waiters waiting to acquire the lock.\n      // Try to unlock it - use a compare-exchange to decide the race between\n      // unlocking the mutex and another thread enqueueing another waiter.\n      const bool releasedLock = state_.compare_exchange_strong(\n          currentState,\n          unlockedState(),\n          std::memory_order_release,\n          std::memory_order_relaxed);\n      if (releasedLock) {\n        return;\n      }\n    }\n\n    // There are some awaiters that have been newly queued.\n    // Dequeue them and reverse their order from LIFO to FIFO.\n    currentState = state_.exchange(nullptr, std::memory_order_acquire);\n\n    assert(currentState != unlockedState());\n    assert(currentState != nullptr);\n\n    auto* waiter = static_cast<LockAwaiter*>(currentState);\n    do {\n      auto* temp = waiter->next_;\n      waiter->next_ = waitersHead;\n      waitersHead = waiter;\n      waiter = temp;\n    } while (waiter != nullptr);\n  }\n\n  assert(waitersHead != nullptr);\n\n  waiters_ = waitersHead->next_;\n\n  waitersHead->awaitingCoroutine_.resume();\n}\n\nbool Mutex::lockAsyncImpl(LockAwaiter* awaiter) {\n  void* oldValue = state_.load(std::memory_order_relaxed);\n  while (true) {\n    if (oldValue == unlockedState()) {\n      // It looks like the mutex is currently unlocked.\n      // Try to acquire it synchronously.\n      void* newValue = nullptr;\n      if (state_.compare_exchange_weak(\n              oldValue,\n              newValue,\n              std::memory_order_acquire,\n              std::memory_order_relaxed)) {\n        // Acquired synchronously, don't suspend.\n        return false;\n      }\n    } else {\n      // It looks like the mutex is currently locked.\n      // Try to queue this waiter to the list of waiters.\n      void* newValue = awaiter;\n      awaiter->next_ = static_cast<LockAwaiter*>(oldValue);\n      if (state_.compare_exchange_weak(\n              oldValue,\n              newValue,\n              std::memory_order_release,\n              std::memory_order_relaxed)) {\n        // Queued waiter successfully. Awaiting coroutine should suspend.\n        return true;\n      }\n    }\n  }\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/Mutex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/ViaIfAsync.h>\n\n#include <atomic>\n#include <mutex>\n#include <type_traits>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n/// A mutex that can be locked asynchronously using 'co_await'.\n///\n/// Ownership of the mutex is not tied to any particular thread.\n/// This allows the coroutine owning the lock to transition from one thread\n/// to another while holding the lock and then perform the unlock() operation\n/// on another thread.\n///\n/// This mutex guarantees a FIFO scheduling algorithm - coroutines acquire the\n/// lock in the order that they execute the 'co_await mutex.co_lock()'\n/// operation.\n///\n/// Note that you cannot use std::scoped_lock/std::lock_guard to acquire the\n/// lock as the lock must be acquired with use of 'co_await' which cannot be\n/// used in a constructor.\n///\n/// You can still use the std::scoped_lock/std::lock_guard in conjunction with\n/// std::adopt_lock to automatically unlock the mutex when the current scope\n/// exits after having locked the mutex using either 'co_await m.co_lock()'\n/// or 'm.try_lock()'.\n///\n/// You can also attempt to acquire the lock using std::unique_lock in\n/// conjunction with std::try_to_lock.\n///\n/// For example:\n///   folly::coro::Mutex m;\n///   folly::Executor& executor;\n///\n///   folly::coro::Task<> asyncScopedLockExample()\n///   {\n///     std::unique_lock<folly::coro::Mutex> lock = co_await m.co_scoped_lock();\n///     ...\n///   }\n///\n///   folly::coro::Task<> asyncManualLockAndUnlock()\n///   {\n///     co_await m.co_lock(executor);\n///     ...\n///     m.unlock();\n///   }\n///\n///   void nonAsyncTryLock()\n///   {\n///     if (m.try_lock())\n///     {\n///       // Once the lock is acquired you can pass ownership of the lock to\n///       // a std::lock_guard object.\n///       std::lock_guard<folly::coro::Mutex> lock{m, std::adopt_lock};\n///       ...\n///     }\n///   }\n///\n///   void nonAsyncScopedTryLock()\n///   {\n///     std::unique_lock<folly::coro::Mutex> lock{m, std::try_to_lock};\n///     if (lock)\n///     {\n///       ...\n///     }\n///   }\nclass Mutex {\n  class ScopedLockAwaiter;\n  class LockAwaiter;\n  template <typename Awaiter>\n  class LockOperation;\n\n public:\n  /// Construct a new async mutex that is initially unlocked.\n  Mutex() noexcept : state_(unlockedState()), waiters_(nullptr) {}\n\n  Mutex(const Mutex&) = delete;\n  Mutex(Mutex&&) = delete;\n  Mutex& operator=(const Mutex&) = delete;\n  Mutex& operator=(Mutex&&) = delete;\n\n  ~Mutex();\n\n  /// Try to lock the mutex synchronously.\n  ///\n  /// Returns true if the lock was able to be acquired synchronously, false\n  /// if the lock could not be acquired because it was already locked.\n  ///\n  /// If this method returns true then the caller is responsible for ensuring\n  /// that unlock() is called to release the lock.\n  bool try_lock() noexcept {\n    void* oldValue = unlockedState();\n    return state_.compare_exchange_strong(\n        oldValue,\n        nullptr,\n        std::memory_order_acquire,\n        std::memory_order_relaxed);\n  }\n\n  /// Lock the mutex asynchronously, returning an RAII object that will release\n  /// the lock at the end of the scope.\n  ///\n  /// You must co_await the return value to wait until the lock is acquired.\n  ///\n  /// Chain a call to .viaIfAsync() to specify the executor to resume on when\n  /// the lock is eventually acquired in the case that the lock could not be\n  /// acquired synchronously. Note that the executor will be passed implicitly\n  /// if awaiting from a Task or AsyncGenerator coroutine. The awaiting\n  /// coroutine will continue without suspending if the lock could be acquired\n  /// synchronously.\n  [[nodiscard]] LockOperation<ScopedLockAwaiter> co_scoped_lock() noexcept;\n\n  /// Lock the mutex asynchronously.\n  ///\n  /// You must co_await the return value to wait until the lock is acquired.\n  ///\n  /// Chain a call to .viaIfAsync() to specify the executor to resume on when\n  /// the lock is eventually acquired in the case that the lock could not be\n  /// acquired synchronously. The awaiting coroutine will continue without\n  /// suspending if the lock could be acquired synchronously.\n  ///\n  /// Once the 'co_await m.co_lock()' operation completes, the awaiting\n  /// coroutine is responsible for ensuring that .unlock() is called to release\n  /// the lock.\n  ///\n  /// Consider using co_scoped_lock() instead to obtain a std::scoped_lock\n  /// that handles releasing the lock at the end of the scope.\n  [[nodiscard]] LockOperation<LockAwaiter> co_lock() noexcept;\n\n  /// Unlock the mutex.\n  ///\n  /// If there are other coroutines waiting to lock the mutex then this will\n  /// schedule the resumption of the next coroutine in the queue.\n  void unlock() noexcept;\n\n private:\n  using folly_coro_aware_mutex = std::true_type;\n\n  class LockAwaiter {\n   public:\n    explicit LockAwaiter(Mutex& mutex) noexcept : mutex_(mutex) {}\n\n    bool await_ready() noexcept { return mutex_.try_lock(); }\n\n    bool await_suspend(coroutine_handle<> awaitingCoroutine) noexcept {\n      awaitingCoroutine_ = awaitingCoroutine;\n      return mutex_.lockAsyncImpl(this);\n    }\n\n    void await_resume() noexcept {}\n\n   protected:\n    Mutex& mutex_;\n\n   private:\n    friend Mutex;\n\n    coroutine_handle<> awaitingCoroutine_;\n    LockAwaiter* next_;\n  };\n\n  class ScopedLockAwaiter : public LockAwaiter {\n   public:\n    using LockAwaiter::LockAwaiter;\n\n    std::unique_lock<Mutex> await_resume() noexcept {\n      return std::unique_lock<Mutex>{mutex_, std::adopt_lock};\n    }\n  };\n\n  template <typename Awaiter>\n  class LockOperation {\n   public:\n    explicit LockOperation(Mutex& mutex) noexcept : mutex_(mutex) {}\n\n    auto viaIfAsync(folly::Executor::KeepAlive<> executor) const {\n      return folly::coro::co_viaIfAsync(std::move(executor), Awaiter{mutex_});\n    }\n\n   private:\n    Mutex& mutex_;\n  };\n\n  // Special value for state_ that indicates the mutex is not locked.\n  void* unlockedState() noexcept { return this; }\n\n  // Try to lock the mutex.\n  //\n  // Returns true if the lock could not be acquired synchronously and awaiting\n  // coroutine should suspend. In this case the coroutine will be resumed later\n  // once it acquires the mutex. Returns false if the lock was acquired\n  // synchronously and the awaiting coroutine should continue without\n  // suspending.\n  bool lockAsyncImpl(LockAwaiter* awaiter);\n\n  // This contains either:\n  // - this    => Not locked\n  // - nullptr => Locked, no newly queued waiters (ie. empty list of waiters)\n  // - other   => Pointer to first LockAwaiter* in a linked-list of newly\n  //              queued awaiters in LIFO order.\n  std::atomic<void*> state_;\n\n  // Linked-list of waiters in FIFO order.\n  // Only the current lock holder is allowed to access this member.\n  LockAwaiter* waiters_;\n};\n\ninline Mutex::LockOperation<Mutex::ScopedLockAwaiter>\nMutex::co_scoped_lock() noexcept {\n  return LockOperation<ScopedLockAwaiter>{*this};\n}\n\ninline Mutex::LockOperation<Mutex::LockAwaiter> Mutex::co_lock() noexcept {\n  return LockOperation<LockAwaiter>{*this};\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Nothrow.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/Portability.h> // FOLLY_HAS_COROUTINES\n#include <folly/coro/ViaIfAsync.h>\n\n// For DCHECK, `ViaIfAsync.h` depends on it already.\n#include <glog/logging.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nnamespace detail {\n\ntemplate <typename T>\nclass [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] NothrowAwaitable;\n\n// The `!value_only_awaitable_v` constraint stops `co_nothrow()` from wrapping\n// `value_or_error_or_stopped`, `co_awaitTry`, `AsNoexcept`, etc.\n//\n// Rationale: Instead, we could do:\n//   - (not very useful) Nothing -- modeling the behavior that the exception\n//     was already captured by the inner type.\n//   - (highly unexpected / buggy) Prevent the inner type from capturing the\n//     exception, and force its propagation up-stack.\n// This was banned because real-world users didn't uniformly expect one of the\n// behaviors over the other, and nobody really **needs** this to work.\ntemplate <typename T>\nclass [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]\nNothrowAwaitable : public CommutativeWrapperAwaitable<NothrowAwaitable, T> {\n public:\n  using CommutativeWrapperAwaitable<NothrowAwaitable, T>::\n      CommutativeWrapperAwaitable;\n\n  template <\n      typename T2 = T,\n      std::enable_if_t<!value_only_awaitable_v<T>, int> = 0>\n  T2&& unwrap() {\n    return std::move(this->inner_);\n  }\n};\n\ntemplate <typename>\nclass ValueOrError;\ntemplate <typename>\nclass ValueOrErrorImpl;\n\n// Mixin supporting `co_nothrow()` and `value_or_error()` for tasks & generators\nclass BypassExceptionThrowing {\n private:\n  enum class BypassMode : uint8_t {\n    // Default state for `co_await task()`.  The corresponding\n    // `await_transform` calls must call `maybeActivate()`, which will either\n    // promote `REQUESTED` to `ACTIVE`, or reset any `ACTIVE` state from a\n    // prior suspension point back to `INACTIVE`.\n    INACTIVE,\n    // State after `await_transform` with a `co_nothrow` awaitable.\n    ACTIVE,\n    // State before `await_transform` with a `co_nothrow` awaitable.\n    REQUESTED,\n    // State for `value_or_error()` only allowing for nothrow-propagation of\n    // `OperationCancelled` to the parent.\n    ONLY_WHEN_OPERATION_CANCELLED,\n  } bypassMode_{BypassMode::INACTIVE};\n\n protected:\n  // This write interface is protected to alert future authors -- this is a\n  // TIGHTLY COUPLED DETAIL.  See the analogous note in `BasePromise`.\n  template <typename>\n  friend class BasePromise;\n\n  template <typename Awaitable>\n  void maybeActivate() {\n    if (is_instantiation_of_v<ValueOrErrorImpl, Awaitable>) {\n      DCHECK(\n          // normal `value_or_error`: bypass `OperationCancelled` handling.\n          bypassMode_ == BypassMode::ONLY_WHEN_OPERATION_CANCELLED ||\n          // inner awaitable is value-only, no bypass needed.\n          bypassMode_ == BypassMode::INACTIVE);\n    } else {\n      // Awaitable should've been unwrapped before getting here.\n      static_assert(\n          !is_instantiation_of_v<NothrowAwaitable, Awaitable> &&\n          !is_instantiation_of_v<ValueOrError, Awaitable>);\n      bypassMode_ = bypassMode_ == BypassMode::REQUESTED\n          ? BypassMode::ACTIVE\n          : BypassMode::INACTIVE;\n    }\n  }\n\n  // Implements `co_nothrow` -- this gets called only from the matching\n  // `await_transform(NothrowAwaitable<Awaitable>)`.  The subsequent\n  // `await_transform(Awaitable)` maps `REQUESTED` to `ACTIVE.\n  template <typename Awaitable>\n  void requestDueToNothrow() {\n    // `co_nothrow` is incompatible with noexcept-awaitables, doc above.\n    static_assert(!value_only_awaitable_v<Awaitable>);\n    bypassMode_ = BypassMode::REQUESTED;\n  }\n\n  template <typename Awaitable>\n  void requestDueToValueOrError() {\n    // If the inner awaitable is value-only (e.g., `value_or_fatal`), it\n    // already guarantees no exceptions escape.  Do NOT activate bypass mode,\n    // as that would intercept exceptions before the inner awaitable's\n    // `await_resume_result()` can apply its policy.\n    if constexpr (!value_only_awaitable_v<Awaitable>) {\n      bypassMode_ = BypassMode::ONLY_WHEN_OPERATION_CANCELLED;\n    }\n  }\n\n public: // Otherwise we'd also need to friend `AsyncGenerator`, etc\n  bool shouldBypassFor(exception_wrapper& ex) {\n    return (bypassMode_ == BypassMode::ACTIVE) ||\n        (bypassMode_ == BypassMode::ONLY_WHEN_OPERATION_CANCELLED &&\n         // Today, this incurs RTTI cost, but once we migrate coros to use\n         // `rich_exception_ptr`, this will often be RTTI-free.\n         ex.get_exception<folly::OperationCancelled>());\n  }\n};\n\n} // namespace detail\n\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<\n        !value_only_awaitable_v<Awaitable> && // Comment on `NothrowAwaitable`\n            !folly::ext::must_use_immediately_v<Awaitable>,\n        int> = 0>\ndetail::NothrowAwaitable<remove_cvref_t<Awaitable>> co_nothrow(\n    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable&& awaitable) {\n  return detail::NothrowAwaitable<remove_cvref_t<Awaitable>>{\n      static_cast<Awaitable&&>(awaitable)};\n}\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<\n        !value_only_awaitable_v<Awaitable> && // Comment on `NothrowAwaitable`\n            folly::ext::must_use_immediately_v<Awaitable>,\n        int> = 0>\ndetail::NothrowAwaitable<remove_cvref_t<Awaitable>> co_nothrow(\n    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {\n  return detail::NothrowAwaitable<remove_cvref_t<Awaitable>>{\n      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};\n}\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Promise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <utility>\n\n#include <folly/CancellationToken.h>\n#include <folly/Try.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/futures/Promise.h>\n#include <folly/lang/SafeAlias-fwd.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\ntemplate <typename T>\nclass Promise;\ntemplate <typename T>\nclass Future;\n\n// Creates promise and associated unfulfilled future\ntemplate <typename T>\nstd::pair<Promise<T>, Future<T>> makePromiseContract();\n\n// Creates fulfilled future\ntemplate <typename T>\nFuture<remove_cvref_t<T>> makeFuture(T&&);\ntemplate <typename T>\nFuture<T> makeFuture(exception_wrapper&&);\nFuture<void> makeFuture();\n\nnamespace detail {\ntemplate <typename T>\nstruct PromiseState {\n  PromiseState() = default;\n\n  Try<T> result;\n  // Must be exchanged to true before setting result\n  folly::relaxed_atomic<bool> fulfilled{false};\n  // Must be posted after setting result\n  coro::Baton ready;\n};\n} // namespace detail\n\ntemplate <typename T>\nclass Promise {\n public:\n  /**\n   * Construct an empty Promise.\n   *\n   * This object is not valid use until you initialize it with move assignment.\n   */\n  Promise() = default;\n\n  Promise(Promise&& other) noexcept\n      : ct_(std::move(other.ct_)),\n        state_(std::exchange(other.state_, nullptr)) {}\n  Promise& operator=(Promise&& other) noexcept {\n    if (this != &other && state_ && !state_->fulfilled) {\n      setException(BrokenPromise{tag<T>});\n    }\n    ct_ = std::move(other.ct_);\n    state_ = std::exchange(other.state_, nullptr);\n    return *this;\n  }\n  Promise(const Promise&) = delete;\n  Promise& operator=(const Promise&) = delete;\n\n  ~Promise() {\n    if (state_ && !state_->fulfilled) {\n      setException(BrokenPromise{tag<T>});\n    }\n  }\n\n  bool valid() const noexcept { return state_; }\n\n  bool isFulfilled() const noexcept { return state_ && state_->fulfilled; }\n\n  template <typename... Args>\n  void setValue(Args&&... args) {\n    trySetValue(std::forward<Args>(args)...);\n  }\n\n  template <typename... Args>\n  void setException(Args&&... args) {\n    trySetException(std::forward<Args>(args)...);\n  }\n\n  void setResult(Try<T>&& result) { trySetResult(std::move(result)); }\n\n  /**\n   * Fulfills the promise with a value if not already fulfilled.\n   * @returns Whether the fulfillment took place.\n   */\n  template <typename... Args>\n  bool trySetValue(Args&&... args) {\n    DCHECK(state_);\n    if (state_->fulfilled.exchange(true)) {\n      return false;\n    }\n    if constexpr (std::is_void_v<T>) {\n      static_assert(sizeof...(Args) == 0);\n    } else {\n      state_->result.emplace(std::forward<Args>(args)...);\n    }\n    state_->ready.post();\n    return true;\n  }\n\n  /**\n   * Fulfills the promise with an exception if not already fulfilled.\n   * @returns Whether the fulfillment took place.\n   */\n  template <typename... Args>\n  bool trySetException(Args&&... args) {\n    DCHECK(state_);\n    if (state_->fulfilled.exchange(true)) {\n      return false;\n    }\n    state_->result.emplaceException(std::forward<Args>(args)...);\n    state_->ready.post();\n    return true;\n  }\n\n  /**\n   * Fulfills the promise with a Try if not already fulfilled.\n   * @returns Whether the fulfillment took place.\n   */\n  bool trySetResult(Try<T>&& result) {\n    DCHECK(state_);\n    if (state_->fulfilled.exchange(true)) {\n      return false;\n    }\n    state_->result = std::move(result);\n    state_->ready.post();\n    return true;\n  }\n\n  /**\n   * Fulfills the promise with a value/Try returned from calling func if not\n   * already fulfilled.\n   *\n   * If either the call to func or the result's constructor completes with an\n   * exception then the exception is caught and stored as the result.\n   *\n   * @returns Whether the fulfillment took place.\n   */\n  template <typename Func>\n  bool trySetWith(Func&& func) {\n    DCHECK(state_);\n    if (state_->fulfilled.exchange(true)) {\n      return false;\n    }\n    try {\n      state_->result = Try<T>(std::forward<Func>(func)());\n    } catch (...) {\n      state_->result.emplaceException(current_exception());\n    }\n    state_->ready.post();\n    return true;\n  }\n\n  /**\n   * Fulfills the promise with an exception returned from calling func if not\n   * already fulfilled.\n   *\n   * If either the call to func or the result's constructor completes with an\n   * exception then the exception is caught and stored as the result.\n   *\n   * @returns Whether the fulfillment took place.\n   */\n  template <typename Func>\n  bool trySetExceptionWith(Func&& func) {\n    DCHECK(state_);\n    if (state_->fulfilled.exchange(true)) {\n      return false;\n    }\n    try {\n      state_->result.emplaceException(std::forward<Func>(func)());\n    } catch (...) {\n      state_->result.emplaceException(current_exception());\n    }\n    state_->ready.post();\n    return true;\n  }\n\n  const CancellationToken& getCancellationToken() const { return ct_; }\n\n private:\n  Promise(CancellationToken ct, detail::PromiseState<T>& state)\n      : ct_(std::move(ct)), state_(&state) {}\n\n  CancellationToken ct_;\n  detail::PromiseState<T>* state_{nullptr};\n\n  friend std::pair<Promise<T>, Future<T>> makePromiseContract<T>();\n};\n\ntemplate <typename T>\nclass Future {\n public:\n  /**\n   * Construct an empty Future.\n   *\n   * This object is not valid use until you initialize it with move assignment.\n   */\n  Future() = default;\n\n  Future(Future&& other) noexcept\n      : cs_(std::move(other.cs_)),\n        state_(std::exchange(other.state_, nullptr)),\n        ct_(std::move(other.ct_)),\n        hasCancelTokenOverride_(\n            std::exchange(other.hasCancelTokenOverride_, false)) {}\n\n  Future& operator=(Future&& other) noexcept {\n    if (this != &other) {\n      cs_ = std::move(other.cs_);\n      state_ = std::exchange(other.state_, nullptr);\n      ct_ = std::move(other.ct_);\n      hasCancelTokenOverride_ =\n          std::exchange(other.hasCancelTokenOverride_, false);\n    }\n    return *this;\n  }\n\n  Future(const Future&) = delete;\n  Future& operator=(const Future&) = delete;\n\n  class WaitOperation : private Baton::WaitOperation {\n   public:\n    explicit WaitOperation(Future& future) noexcept\n        : Baton::WaitOperation(future.state_->ready),\n          future_(future),\n          cb_(std::move(future.ct_), [&] { future_.cancel(); }) {}\n\n    using Baton::WaitOperation::await_ready;\n    using Baton::WaitOperation::await_suspend;\n\n    T await_resume() {\n      if constexpr (!std::is_void_v<T>) {\n        return std::move(future_.state_->result.value());\n      } else {\n        future_.state_->result.throwIfFailed();\n      }\n    }\n\n    folly::Try<T> await_resume_try() {\n      return std::move(future_.state_->result);\n    }\n\n   private:\n    Future& future_;\n    CancellationCallback cb_;\n  };\n\n  [[nodiscard]] WaitOperation operator co_await() && noexcept {\n    return WaitOperation{*this};\n  }\n\n  bool valid() const noexcept { return state_ != nullptr; }\n\n  explicit operator bool() const noexcept { return valid(); }\n\n  bool isReady() const noexcept { return state_->ready.ready(); }\n\n  friend Future co_withCancellation(\n      folly::CancellationToken ct, Future&& future) noexcept {\n    if (!std::exchange(future.hasCancelTokenOverride_, true)) {\n      future.ct_ = std::move(ct);\n    }\n    return std::move(future);\n  }\n\n  template <safe_alias Default>\n  using folly_private_safe_alias_t = safe_alias_of<T, Default>;\n\n private:\n  Future(CancellationSource cs, detail::PromiseState<T>& state)\n      : cs_(std::move(cs)), state_(&state) {}\n\n  void cancel() {\n    if (!state_->fulfilled.exchange(true)) {\n      cs_.requestCancellation();\n      state_->result.emplaceException(OperationCancelled{});\n      state_->ready.post();\n    }\n  }\n\n  CancellationSource cs_;\n  detail::PromiseState<T>* state_{nullptr};\n  // The token inherited when the future is awaited\n  CancellationToken ct_;\n  bool hasCancelTokenOverride_{false};\n\n  friend std::pair<Promise<T>, Future<T>> makePromiseContract<T>();\n};\n\n/**\n * makePromiseContract can help you migrating your non-coroutine code base to\n * coroutine. If your code already uses Future/SemiFuture, you don't need this\n * tool. A common use case is with async callback functions. In the example, we\n * can pass a callback function into the legacy code sleepAndNotify and\n * sleepAndNotify sets the promise on completion. Consider to use detachOnCancel\n * with this makePromiseContract to handle long running (longer than your\n * timeout) tasks that don't handle cancellation properly.\n *\n * \\refcode folly/docs/examples/folly/coro/Promise.cpp\n */\ntemplate <typename T>\nstd::pair<Promise<T>, Future<T>> makePromiseContract() {\n  auto [cs, data] = CancellationSource::create(\n      folly::detail::WithDataTag<detail::PromiseState<T>>{});\n  return {\n      Promise<T>{cs.getToken(), std::get<0>(*data)},\n      Future<T>{std::move(cs), std::get<0>(*data)}};\n}\n\ntemplate <typename T>\nFuture<remove_cvref_t<T>> makeFuture(T&& t) {\n  auto [promise, future] = makePromiseContract<remove_cvref_t<T>>();\n  promise.setValue(std::forward<T>(t));\n  return std::move(future);\n}\ntemplate <typename T>\nFuture<T> makeFuture(exception_wrapper&& ex) {\n  auto [promise, future] = makePromiseContract<T>();\n  promise.setException(std::move(ex));\n  return std::move(future);\n}\ninline Future<void> makeFuture() {\n  auto [promise, future] = makePromiseContract<void>();\n  promise.setValue();\n  return std::move(future);\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/README.md",
    "content": "# @title Coro\n\n# Introduction\n\n[folly::coro](https://github.com/facebook/folly/blob/master/folly/experimental/coro/) is a developer-friendly asynchronous C++ framework based on [Coroutines TS](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4775.pdf). It is available for any fbcode project that is built with Сlang and uses platform007.\n\n## Basic example\n\n```c++\nfolly::coro::Task<int> task42() {\n  co_return 42;\n}\n\nfolly::coro::Task<int> taskSlow43() {\n  co_await folly::futures::sleep(std::chrono::seconds{1});\n  co_return co_await task42() + 1;\n}\n\nint main() {\n  ...\n  CHECK_EQ(43, folly::coro::blockingWait(co_withExecutor(\n      folly::getGlobalCPUExecutor(), taskSlow43(), taskSlow43())));\n  ...\n}\n```\nThe same logic implemented with folly::SemiFuture:\n\n```c++\nfolly::SemiFuture<int> task42() {\n  return folly::makeSemiFuture().deferValue([](auto) {\n    return 42;\n  });\n}\n\nfolly::SemiFuture<int> taskSlow43() {\n  return folly::futures::sleep(std::chrono::seconds{1})\n      .semi()\n      .deferValue([](auto) { return task42(); })\n      .deferValue([](auto value) { return value + 1; });\n}\n\nint main() {\n  ...\n  CHECK_EQ(\n      43,\n      taskSlow43().via(folly::getCPUExecutor().get()).get());\n  ...\n}\n```\n## Features\n\n* Better performance comparing to `folly::Future`\n* Full-compatibility with `folly::Future` and `folly::SemiFuture`\n* Asynchronous synchronization primitives (e.g. `coro::Baton`, `coro::Mutex`, `coro::SharedMutex`)\n* Compatible with any other library based on Coroutines TS\n\n# Overview\n\n## Writing a coroutine\n\nAny function that returns a `folly::coro::Task` and has at least one use of `co_await` or `co_return` is a coroutine.\nNOTE: You have to always use `co_return` instead of `return` in coroutines.\n\n```c++\nfolly::coro::Task<int> task42() {\n  co_return 42;\n}\n\nfolly::coro::Task<int> task43() {\n  auto value = co_await task42();\n  co_return value + 1;\n}\n```\n\n## Starting a coroutine\nCalling a `folly::coro::Task`-coroutine function captures the arguments, but doesn't start executing the coroutine immediately. Instead the coroutine is lazily started when you `co_await` the task.\n\nAlternatively, you can start executing a coroutine from a normal function by attaching a `folly::Executor` via `co_withExecutor()` and either calling `start()` or using `folly::coro::blockingWait()`.\n```c++\nfolly::coro::Task<void> checkArg(int arg42) {\n  CHECK_EQ(42, arg42);\n  co_return;\n}\n\nvoid runCoroutine1() {\n  int arg42 = 42;\n  // coroutine arguments are captured here, not when we start the coroutine\n  auto task = checkArg(arg42);\n  arg42 = 43;\n  folly::coro::blockingWait(co_withExecutor(\n      folly::getCPUExecutor(), std::move(task)));\n}\n\nvoid runCoroutine2() {\n  folly::SemiFuture<folly::Unit> f = co_withExecutor(\n      folly::getCPUExecutor(), checkArg(42)).start();\n}\n```\n\n## Executor-stickiness\n\nEvery `folly::coro::Task` will always be running on the `Executor` on which it was launched, even if it `co_await`ed something that completed on a different `Executor`.\n\nYou can extract the Executor which the `folly::coro::Task` is running on by using `folly::coro::co_current_executor`.\n```c++\nfolly::coro::Task<int> task42Slow() {\n  // This doesn't suspend the coroutine, just extracts the Executor*\n  folly::Executor* startExecutor = co_await folly::coro::co_current_executor;\n  co_await folly::futures::sleep(std::chrono::seconds{1});\n  folly::Executor* resumeExecutor = co_await folly::coro::co_current_executor;\n  CHECK_EQ(startExecutor, resumeExecutor);\n}\n```\n\nBy default, when a `folly::coro::Task` is awaited within the context of another `Task` it inherits the executor from the awaiting coroutine. If you want to run a child coroutine on a different executor then you can call `co_withExecutor()` to explicitly specify an alternative executor.\n```c++\nfolly::coro::Task<void> foo() {\n  co_await folly::futures::sleep(std::chrono::seconds{1});\n  std::cout << \"Current executor is \" << (co_await folly::coro::co_current_executor) << std::endl;\n}\n\nfolly::coro::Task<void> bar(folly::CPUThreadPoolExecutor* otherExecutor) {\n  // Executes foo() on whatever execution context bar() was launched on.\n  co_await foo();\n\n  // Launches foo() on 'otherExecutor' and when it's done resumes this\n  // coroutine on whatever executor bar() was launched on.\n  co_await co_withExecutor(otherExecutor, foo());\n}\n```\n\n## Awaitables\nYou can `co_await` anything that implements the `Awaitable` concept (see Coroutines TS for more details).\nIt can be `folly::coro::Task`, `folly::Future`, `folly::SemiFuture` etc.\n\nKeep in mind that an `Awaitable` may result in an exception, so you'll have to use try-catch blocks to handle errors.\n```c++\nfolly::coro::Task<void> throwCoro() {\n  throw std::logic_error(\"Expected\");\n  co_return;\n}\n\nfolly::coro::Task<void> coro() {\n  auto future42 = folly::makeSemiFuture(42);\n  EXPECT_EQ(42, co_await std::move(future42));\n\n  try {\n    co_await throwCoro();\n    LOG(FATAL) << \"Unreachable\";\n  } catch (const std::logic_error&) {\n  } catch (...) {\n    LOG(FATAL) << \"Unreachable\";\n  }\n}\n```\n\n## Concurrently awaiting multiple Tasks\n\nWhen you invoke a coroutine that returns a `folly::coro::Task`, the coroutine\ndoesn't begin execution immediately. It only starts when you apply `co_await`\nto the returned task. Applying `co_await` also suspends the coroutine that is\nawaiting, until the awaited operation is complete.\n\nThis means that you cannot perform two operations concurrently by simply calling\nthe two coroutines and later awaiting them both.\n\n**SLOWER: The following will execute the two operations sequentially**\n```c++\nfolly::coro::Task<int> task1();\nfolly::coro::Task<int> task2();\n\nfolly::coro::Task<int> example() {\n  auto t1 = task1();\n  auto t2 = task2();\n  int result1 = co_await std::move(t1);\n  int result2 = co_await std::move(t2);\n  co_return result1 + result2;\n}\n```\n\nIf, instead, you want to perform these operations concurrently and wait until\nboth of the operations complete you can use `folly::coro::collectAll()`.\n\n**FASTER: The following _may_ execute the two operations concurrently**\n```c++\nfolly::coro::Task<int> task1();\nfolly::coro::Task<int> task2();\n\nfolly::coro::Task<int> example() {\n  auto [result1, result2] =\n      co_await folly::coro::collectAll(task1(), task2());\n  co_return result1 + result2;\n}\n```\n\nNote that in the above example, when the `co_await` expression is evaluated\nit first launches the `task1()` coroutine and it will execute in the current\nthread until it reaches its first suspend-point, at which point it will then\nlaunch `task2()`. Once both sub-tasks are complete then the `example()`\ncoroutine is resumed with a tuple of the individual results.\n\nNote that if both `task1()` and `task2()` complete synchronously then they\nwill still be executed sequentially.\n\n## Handling partial failure\n\nWhen executing multiple sub-tasks concurrently it's possible that some of those\ntasks will fail with an exception and some will succeed.\n\nIf you use the `folly::coro::collectAll()` function to concurrently wait for\nmultiple tasks to complete then any partial results are discarded if any of\nthe tasks complete with an exception. If multiple sub-tasks complete with an\nexception then one of the exceptions is rethrown as the result and the others\nare discarded.\n\nIf you need to be able determine which sub-operation failed or if you need\nto be able to retrieve partial results then you can use `folly::coro::collectAllTry()`\ninstead. Instead of producing a tuple of the results it produces a tuple of\n`folly::Try<T>` values, one for each input task.\n\n```c++\nfolly::coro::Task<int> task1();\nfolly::coro::Task<int> task2();\n\nfolly::coro::Task<int> example() {\n  auto [try1, try2] = co_await folly::coro::collectAllTry(task1(), task2());\n  int result = 0;\n\n  if (try1.hasValue()) {\n    result += try1.value();\n  } else {\n    LOG(ERROR) << \"Error in task1(): \" << try1.exception().what();\n  }\n\n  if (try2.hasValue()) {\n    result += try2.value();\n  } else {\n    LOG(ERROR) << \"Error in task2(): \" << try2.exception().what();\n  }\n\n  co_return result;\n}\n```\n\n## folly::SemiFuture\nAny `folly::coro::Task` can be converted to a `folly::SemiFuture` by calling the `.semi()` method.\nNOTE: this allows using any existing `folly::Future` primitives (e.g. `collectAll()`, `collectAny()`) in coroutine code.\n```c++\nfolly::coro::Task<int> task1();\nfolly::coro::Task<int> task2();\n\nfolly::coro::Task<int> sumTask() {\n  auto f1 = task1().semi();\n  auto f2 = task2().semi();\n\n  folly::Try<int> r1, r2;\n  std::tie(r1, r2) = co_await folly::collectAllSemiFuture(std::move(f1), std::move(f2));\n\n  co_return *r1 + *r2;\n}\n\n```\n\n## Lambdas\nYou can implement a lambda coroutine however you need to explicitly specify a return type - the compiler is not yet able to deduce the return type of a coroutine from the body.\n\nIMPORTANT: You need to be very careful about the lifetimes of temporary lambda objects. Invoking a lambda coroutine returns a `folly::coro::Task` that captures a reference to the lambda and so if the returned Task is not immediately `co_await`ed then the task will be left with a dangling reference when the temporary lambda goes out of scope. \\\n\\\nUse the `folly::coro::co_invoke()` helper when immediately invoking a lambda coroutine to keep the lambda alive as long as the `Task`.\n\n**BAD:** The following code has undefined behaviour\n```c++\nfolly::coro::Task<Reply> coro_send(const Request&);\n\nfolly::SemiFuture<Reply> semifuture_send(const Request& request) {\n  auto task = [request]() -> folly::coro::Task<Reply> {\n    auto reply = co_await coro_send(request);\n    if (reply.isError()) {\n      LOG(reply.error().message());\n    }\n    co_return reply;\n  }(); // <-- Whoops, lambda is destroyed at semicolon\n\n  return std::move(task).semi();\n}\n```\n\n**GOOD:** Use `co_invoke` to invoke the lambda to prevent the lambda from being destroyed.\n```c++\nfolly::SemiFuture<Reply> semifuture_send(const Request& request) {\n  auto task = folly::coro::co_invoke([request]() -> folly::coro::Task<Reply> {\n    auto reply = co_await coro_send(request);\n    if (reply.isError()) {\n      LOG(reply.error().message());\n    }\n    co_return reply;\n  });\n\n  return std::move(task).semi();\n}\n```\n\n## Writing loops with coroutines\n\n### Sequential retry loop\n\n```c++\nfolly::coro::Task<void> pingServer();\n\nfolly::coro::Task<void> pingServerWithRetry(int retryCount) {\n  for (int retry = 0; retry <= retryCount; ++retry) {\n    try {\n      co_await pingServer();\n      co_return;\n    } catch (...) {\n      LOG(WARNING) << \"Ping attempt \" << retry << \" failed\";\n      if (retry == retryCount) throw;\n    }\n    // Wait before trying again.\n    co_await folly::futures::sleep(10ms);\n  }\n}\n```\n\n### Concurrently execute many operations\n\nOperations with side-effects:\n```c++\nfolly::coro::Task<void> doWork(int i);\n\nfolly::coro::Task<void> example(int count) {\n  std::vector<folly::coro::SemiFuture<Unit>> tasks;\n  for (int i = 0; i < count; ++i) {\n    tasks.push_back(doWork(i).semi());\n  }\n  co_await folly::collectAllSemiFuture(tasks.begin(), tasks.end());\n}\n```\n\nOperations that return values:\n```c++\nfolly::coro::Task<std::string> getString(int i);\n\nfolly::coro::Task<void> example(int count) {\n  std::vector<folly::coro::SemiFuture<Unit>> tasks;\n  for (int i = 0; i < count; ++i) {\n    tasks.push_back(getString(i).semi());\n  }\n\n  // Concurrently wait for all of these tasks.\n  std::vector<std::string> strings =\n      co_await folly::collectAllSemiFuture(tasks.begin(), tasks.end());\n\n  // ... use 'strings'\n}\n```\n"
  },
  {
    "path": "folly/coro/Result.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <type_traits>\n\n#include <folly/Try.h>\n#include <folly/coro/Error.h> // compat: used to be the same header\n#include <folly/result/try.h>\n#include <folly/result/value_only_result.h>\n\nnamespace folly::coro {\n\ntemplate <typename T>\nclass co_result final {\n public:\n  explicit co_result(Try<T>&& result) noexcept(\n      std::is_nothrow_move_constructible<T>::value)\n      : result_(std::move(result)) {\n    assert(!result_.hasException() || result_.exception());\n  }\n\n#if FOLLY_HAS_RESULT\n  // Covered in `ValueOrErrorTest.cpp`, unlike the rest of this file, which is\n  // covered in `TaskTest.cpp`.\n  template <std::same_as<folly::result<T>> U> // no implicit ctors for `result`\n  explicit co_result(U result) noexcept(\n      std::is_nothrow_move_constructible<T>::value)\n      : co_result(result_to_try(std::move(result))) {}\n\n  // value_only_result always has a value, so conversion to Try is simple.\n  template <std::same_as<folly::value_only_result<T>> U>\n  explicit co_result(U valueOnlyResult) noexcept(\n      std::is_nothrow_move_constructible<T>::value)\n      : result_(std::move(valueOnlyResult).value_or_throw()) {}\n#endif\n\n  const Try<T>& result() const { return result_; }\n\n  Try<T>& result() { return result_; }\n\n private:\n  Try<T> result_;\n};\n\n#if FOLLY_HAS_RESULT\ntemplate <typename T>\nco_result(result<T>) -> co_result<T>;\ntemplate <typename T>\nco_result(value_only_result<T>) -> co_result<T>;\n#endif\n\n} // namespace folly::coro\n"
  },
  {
    "path": "folly/coro/Retry.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_coro_retry\n//\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/ConstexprMath.h>\n#include <folly/ExceptionWrapper.h>\n#include <folly/Random.h>\n#include <folly/Try.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Result.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Traits.h>\n\n#include <cstdint>\n#include <random>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\n/**\n * \\file coro/Retry.h\n *\n * Coroutine implementation of futures/Retrying.h\n *\n * This file provides utility functions (with building blocks) to build retry\n * logic. There are three function families:\n * - retryWhen: try to a func (which produces an awaitable); if fail (with an\n * non folly::OperationCancelled exception), then wait on a delay func (which\n * produces an awaitable too). This retry logic can run **forever**, so it is\n * not recommended to use it directly. This the auxiliary function to help build\n * retryN and retryWithExponentialBackoff.\n * - retryN: try the func with limited N times\n * - retryWithExponentialBackoff: the retries will be restarted with\n * exponiential backoff.\n *\n * \\refcode folly/docs/examples/folly/coro/Retry.cpp\n */\n\nnamespace folly::coro {\n\n/// Execute a given asynchronous operation returned by func(),\n/// retrying it on failure, if desired, after awaiting\n/// retryDelay(error).\n///\n/// If 'func()' operation succeeds or completes with OperationCancelled\n/// then completes immediately with that result.\n///\n/// Otherwise, if it fails with an error then the function\n/// 'retryDelay()' is invoked with the exception_wrapper for\n/// the error and must return another Task<void>.\n///\n/// If this task completes successfully or completes with then it will retry\n/// the func() operation, otherwise if it completes with an\n/// error then the whole operation will complete with that error.\n///\n/// This allows you to do some asynchronous work between retries (such as\n/// sleeping for a given duration, but could be some reparatory work in\n/// response to particular errors) and the retry will be scheduled once\n/// the retryDelay() operation completes successfully.\ntemplate <typename Func, typename RetryDelayFunc>\nauto retryWhen(Func func, RetryDelayFunc retryDelay)\n    -> Task<semi_await_result_t<invoke_result_t<Func&>>> {\n  while (true) {\n    exception_wrapper error;\n    try {\n      auto result = co_await folly::coro::co_awaitTry(func());\n      if (result.hasValue()) {\n        co_return std::move(result).value();\n      } else {\n        assert(result.hasException());\n        error = std::move(result.exception());\n      }\n    } catch (...) {\n      error = exception_wrapper(current_exception());\n    }\n\n    if (error.is_compatible_with<folly::OperationCancelled>()) {\n      co_yield folly::coro::co_error(std::move(error));\n    }\n\n    Try<void> retryResult =\n        co_await folly::coro::co_awaitTry(retryDelay(std::move(error)));\n    if (retryResult.hasException()) {\n      /// Failure (or cancellation) of retryDelay() indicates we should stop\n      /// retrying.\n      co_yield folly::coro::co_error(std::move(retryResult.exception()));\n    }\n\n    /// Otherwise we go around the loop again.\n  }\n}\n\nnamespace detail {\n\ntemplate <typename Decider>\nclass RetryImmediatelyWithLimit {\n public:\n  template <typename Decider2>\n  explicit RetryImmediatelyWithLimit(\n      uint32_t maxRetries, Decider2&& decider) noexcept\n      : retriesRemaining_(maxRetries),\n        decider_(static_cast<Decider2&&>(decider)) {}\n\n  Task<void> operator()(exception_wrapper&& ew) & {\n    if (retriesRemaining_ == 0 || !decider_(ew)) {\n      co_yield folly::coro::co_error(std::move(ew));\n    }\n\n    const auto& cancelToken = co_await co_current_cancellation_token;\n    if (cancelToken.isCancellationRequested()) {\n      co_yield folly::coro::co_stopped_may_throw;\n    }\n\n    --retriesRemaining_;\n  }\n\n private:\n  uint32_t retriesRemaining_;\n  Decider decider_;\n};\n\nstruct AlwaysRetry {\n  bool operator()(const folly::exception_wrapper&) const noexcept {\n    return true;\n  }\n};\n\n} // namespace detail\n\n/// Executes the operation returned by func(), retrying it up to\n/// 'maxRetries' times on failure with no delay between retries.\ntemplate <typename Func, typename Decider>\nauto retryN(uint32_t maxRetries, Func&& func, Decider&& decider) {\n  return folly::coro::retryWhen(\n      static_cast<Func&&>(func),\n      detail::RetryImmediatelyWithLimit<remove_cvref_t<Decider>>{\n          maxRetries, static_cast<Decider&&>(decider)});\n}\n\ntemplate <typename Func>\nauto retryN(uint32_t maxRetries, Func&& func) {\n  return folly::coro::retryN(\n      maxRetries, static_cast<Func&&>(func), detail::AlwaysRetry{});\n}\n\nnamespace detail {\n\ntemplate <typename URNG, typename Decider>\nclass ExponentialBackoffWithJitter {\n public:\n  template <typename URNG2, typename Decider2>\n  explicit ExponentialBackoffWithJitter(\n      Timekeeper* tk,\n      uint32_t maxRetries,\n      Duration minBackoff,\n      Duration maxBackoff,\n      double relativeJitterStdDev,\n      URNG2&& rng,\n      Decider2&& decider) noexcept\n      : timeKeeper_(tk),\n        maxRetries_(maxRetries),\n        retryCount_(0),\n        minBackoff_(minBackoff),\n        maxBackoff_(maxBackoff),\n        relativeJitterStdDev_(relativeJitterStdDev),\n        randomGen_(static_cast<URNG2&&>(rng)),\n        decider_(static_cast<Decider2&&>(decider)) {}\n\n  Task<void> operator()(exception_wrapper&& ew) & {\n    using dist = std::normal_distribution<double>;\n\n    if (retryCount_ == maxRetries_ || !decider_(ew)) {\n      co_yield folly::coro::co_error(std::move(ew));\n    }\n\n    ++retryCount_;\n\n    /// The jitter will be a value between [e^-stdev]\n    const auto jitter = relativeJitterStdDev_ > 0\n        ? std::exp(dist{0., relativeJitterStdDev_}(randomGen_))\n        : 1.;\n    // TODO T186551522 Calculate backoff in microseconds.\n    const auto backoffNominal = Duration(\n        folly::constexpr_clamp_cast<Duration::rep>(\n            jitter * minBackoff_.count() * std::pow(2, retryCount_ - 1u)));\n\n    const Duration backoff = std::clamp(\n        backoffNominal, minBackoff_, std::max(minBackoff_, maxBackoff_));\n\n    co_await folly::coro::sleep(backoff, timeKeeper_);\n\n    /// Check to see if we were cancelled during the sleep.\n    const auto& cancelToken = co_await co_current_cancellation_token;\n    if (cancelToken.isCancellationRequested()) {\n      co_yield folly::coro::co_stopped_may_throw;\n    }\n  }\n\n private:\n  Timekeeper* timeKeeper_;\n  const uint32_t maxRetries_;\n  uint32_t retryCount_;\n  const Duration minBackoff_;\n  const Duration maxBackoff_;\n  const double relativeJitterStdDev_;\n  URNG randomGen_;\n  Decider decider_;\n};\n\n} // namespace detail\n\n/// Executes the operation returned from 'func()', retrying it on failure\n/// up to 'maxRetries' times, with an exponential backoff, doubling the backoff\n/// on average for each retry, applying some random jitter, up to the specified\n/// maximum backoff, passing each error to decider to decide whether to retry or\n/// not.\ntemplate <typename Func, typename URNG, typename Decider>\nauto retryWithExponentialBackoff(\n    uint32_t maxRetries,\n    Duration minBackoff,\n    Duration maxBackoff,\n    double relativeJitterStdDev,\n    Timekeeper* timeKeeper,\n    URNG&& rng,\n    Func&& func,\n    Decider&& decider) {\n  return folly::coro::retryWhen(\n      static_cast<Func&&>(func),\n      detail::ExponentialBackoffWithJitter<\n          remove_cvref_t<URNG>,\n          remove_cvref_t<Decider>>{\n          timeKeeper,\n          maxRetries,\n          minBackoff,\n          maxBackoff,\n          relativeJitterStdDev,\n          static_cast<URNG&&>(rng),\n          static_cast<Decider&&>(decider)});\n}\n\ntemplate <typename Func, typename URNG>\nauto retryWithExponentialBackoff(\n    uint32_t maxRetries,\n    Duration minBackoff,\n    Duration maxBackoff,\n    double relativeJitterStdDev,\n    Timekeeper* timeKeeper,\n    URNG&& rng,\n    Func&& func) {\n  return folly::coro::retryWithExponentialBackoff(\n      maxRetries,\n      minBackoff,\n      maxBackoff,\n      relativeJitterStdDev,\n      timeKeeper,\n      static_cast<URNG&&>(rng),\n      static_cast<Func&&>(func),\n      detail::AlwaysRetry{});\n}\n\ntemplate <typename Func>\nauto retryWithExponentialBackoff(\n    uint32_t maxRetries,\n    Duration minBackoff,\n    Duration maxBackoff,\n    double relativeJitterStdDev,\n    Timekeeper* timeKeeper,\n    Func&& func) {\n  return folly::coro::retryWithExponentialBackoff(\n      maxRetries,\n      minBackoff,\n      maxBackoff,\n      relativeJitterStdDev,\n      timeKeeper,\n      ThreadLocalPRNG(),\n      static_cast<Func&&>(func));\n}\n\ntemplate <typename Func>\nauto retryWithExponentialBackoff(\n    uint32_t maxRetries,\n    Duration minBackoff,\n    Duration maxBackoff,\n    double relativeJitterStdDev,\n    Func&& func) {\n  return folly::coro::retryWithExponentialBackoff(\n      maxRetries,\n      minBackoff,\n      maxBackoff,\n      relativeJitterStdDev,\n      static_cast<Timekeeper*>(nullptr),\n      static_cast<Func&&>(func));\n}\n\ntemplate <typename Func, typename Decider>\nauto retryWithExponentialBackoff(\n    uint32_t maxRetries,\n    Duration minBackoff,\n    Duration maxBackoff,\n    double relativeJitterStdDev,\n    Func&& func,\n    Decider&& decider) {\n  return folly::coro::retryWithExponentialBackoff(\n      maxRetries,\n      minBackoff,\n      maxBackoff,\n      relativeJitterStdDev,\n      static_cast<Timekeeper*>(nullptr),\n      ThreadLocalPRNG(),\n      static_cast<Func&&>(func),\n      static_cast<Decider&&>(decider));\n}\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/RustAdaptors.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/Executor.h>\n#include <folly/Optional.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Task.h>\n#include <folly/futures/Future.h>\n#include <folly/synchronization/Baton.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\ntemplate <typename T>\nclass PollFuture final : private Executor {\n public:\n  using Poll = Optional<lift_unit_t<T>>;\n  using Waker = Function<void()>;\n\n  explicit PollFuture(Task<T> task) {\n    Executor* self = this;\n    co_withExecutor(makeKeepAlive(self), std::move(task))\n        .start(\n            [&](Try<T>&& result) noexcept {\n              // Rust doesn't support exceptions\n              DCHECK(!result.hasException());\n              if constexpr (!std::is_same_v<T, void>) {\n                result_ = std::move(result).value();\n              } else {\n                result_ = unit;\n              }\n            },\n            cancellationSource_.getToken());\n  }\n\n  explicit PollFuture(SemiFuture<lift_unit_t<T>> future) {\n    Executor* self = this;\n    std::move(future)\n        .via(makeKeepAlive(self))\n        .setCallback_([&](Executor::KeepAlive<>&&, Try<T>&& result) mutable {\n          result_ = std::move(result).value();\n        });\n  }\n\n  ~PollFuture() override {\n    cancellationSource_.requestCancellation();\n    if (keepAliveCount_.load(std::memory_order_relaxed) > 0) {\n      folly::Baton<> b;\n      while (!poll([&] { b.post(); })) {\n        b.wait();\n        b.reset();\n      }\n    }\n  }\n\n  PollFuture(const PollFuture&) = delete;\n  PollFuture& operator=(const PollFuture&) = delete;\n  PollFuture(PollFuture&&) = delete;\n  PollFuture& operator=(PollFuture&&) = delete;\n\n  Poll poll(Waker waker) {\n    while (true) {\n      std::queue<Func> funcs;\n      {\n        auto wQueueAndWaker = queueAndWaker_.wlock();\n        if (wQueueAndWaker->funcs.empty()) {\n          wQueueAndWaker->waker = std::move(waker);\n          break;\n        }\n\n        std::swap(funcs, wQueueAndWaker->funcs);\n      }\n\n      while (!funcs.empty()) {\n        funcs.front()();\n        funcs.pop();\n      }\n    }\n\n    if (keepAliveCount_.load(std::memory_order_relaxed) == 0) {\n      return std::move(result_);\n    }\n    return none;\n  }\n\n private:\n  void add(Func func) override {\n    auto waker = [&] {\n      auto wQueueAndWaker = queueAndWaker_.wlock();\n      wQueueAndWaker->funcs.push(std::move(func));\n      return std::exchange(wQueueAndWaker->waker, {});\n    }();\n    if (waker) {\n      waker();\n    }\n  }\n\n  bool keepAliveAcquire() noexcept override {\n    auto keepAliveCount =\n        keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    DCHECK(keepAliveCount > 0);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    auto keepAliveCount = keepAliveCount_.load(std::memory_order_relaxed);\n    do {\n      DCHECK(keepAliveCount > 0);\n      if (keepAliveCount == 1) {\n        add([this] {\n          // the final count *must* be released from this executor so that we\n          // don't race with poll.\n          keepAliveCount_.fetch_sub(1, std::memory_order_relaxed);\n        });\n        return;\n      }\n    } while (!keepAliveCount_.compare_exchange_weak(\n        keepAliveCount,\n        keepAliveCount - 1,\n        std::memory_order_release,\n        std::memory_order_relaxed));\n  }\n\n  struct QueueAndWaker {\n    std::queue<Func> funcs;\n    Waker waker;\n  };\n  Synchronized<QueueAndWaker> queueAndWaker_;\n  std::atomic<ssize_t> keepAliveCount_{1};\n  Optional<lift_unit_t<T>> result_;\n  CancellationSource cancellationSource_;\n};\n\ntemplate <typename T>\nclass PollStream {\n public:\n  using Poll = Optional<Optional<T>>;\n  using Waker = Function<void()>;\n\n  explicit PollStream(AsyncGenerator<T> asyncGenerator)\n      : asyncGenerator_(std::move(asyncGenerator)) {}\n\n  Poll poll(Waker waker) {\n    if (!nextFuture_) {\n      nextFuture_.emplace(getNext());\n    }\n\n    auto nextPoll = nextFuture_->poll(std::move(waker));\n    if (!nextPoll) {\n      return none;\n    }\n\n    nextFuture_.reset();\n    return nextPoll;\n  }\n\n private:\n  Task<Optional<T>> getNext() {\n    auto next = co_await asyncGenerator_.next();\n    if (next) {\n      co_return std::move(next).value();\n    }\n    co_return none;\n  }\n\n  AsyncGenerator<T> asyncGenerator_;\n  Optional<PollFuture<Optional<T>>> nextFuture_;\n};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/ScopeExit.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_coro_scopeexit\n//\n\n#pragma once\n\n#include <folly/tracing/AsyncStack.h>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/Executor.h>\n#include <folly/ScopeGuard.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/CustomizationPoint.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\nstruct AttachScopeExitFn {\n  /// Dispatches to a custom implementation using tag_invoke()\n  template <\n      typename ParentPromise,\n      typename ChildPromise,\n      std::enable_if_t<\n          folly::is_tag_invocable_v<\n              AttachScopeExitFn,\n              ParentPromise&,\n              coroutine_handle<ChildPromise>>,\n          int> = 0>\n  auto operator()(\n      ParentPromise& parent, coroutine_handle<ChildPromise> action) const\n      noexcept(folly::is_nothrow_tag_invocable_v<\n               AttachScopeExitFn,\n               ParentPromise&,\n               coroutine_handle<ChildPromise>>)\n          -> folly::tag_invoke_result_t<\n              AttachScopeExitFn,\n              ParentPromise&,\n              coroutine_handle<ChildPromise>> {\n    return folly::tag_invoke(AttachScopeExitFn{}, parent, action);\n  }\n};\n\n/// co_attachScopeExit extension point opts the parent coroutine type into\n/// handling ScopeExitTasks and executing them at the end of the parent\n/// coroutine's scope.\n///\n/// There are two important steps the parent coroutine must take:\n/// 1. It must store the provided ScopeExitTask coroutine handle and return the\n/// latest previously attached ScopeExitTask handle (or an empty handle if this\n/// one is the first).\n/// 2. On destruction of the parent coroutine, the context of the latest stored\n/// ScopeExitTask coroutine must be set by calling setContext(...) on its\n/// promise object, then the ScopeExitTask coroutine must be executed.\n/// The continuation passed to the setContext(...) call will be resumed after\n/// the executing the last (the first attached) coroutine in the ScopeExitTask\n/// chain. NOTE: The user must not pop the async frame if it is passed to the\n/// setContext(...) call, it will be popped by the last ScopeExitTask coroutine\n/// in the chain instead.\nFOLLY_DEFINE_CPO(AttachScopeExitFn, co_attachScopeExit)\n\ntemplate <typename... Args>\nclass ScopeExitTask;\n\nclass ScopeExitTaskPromiseBase {\n public:\n  class FinalAwaiter {\n   public:\n    bool await_ready() noexcept { return false; }\n\n    template <typename Promise>\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES coroutine_handle<>\n    await_suspend(coroutine_handle<Promise> coro) noexcept {\n      SCOPE_EXIT {\n        coro.destroy();\n      };\n\n      ScopeExitTaskPromiseBase& promise = coro.promise();\n      DCHECK(promise.continuation_);\n      DCHECK(promise.parentAsyncFrame_);\n      DCHECK(promise.executor_);\n      if (promise.next_) {\n        promise.next_.promise().setContext(\n            promise.continuation_,\n            promise.parentAsyncFrame_,\n            promise.executor_.get_alias(),\n            std::move(promise.error_));\n        return promise.next_;\n      }\n\n      /// If we reached this point, then this ScopeExitTask is the final one to\n      /// be executed on the parent task, and we can now pop the parent's async\n      /// frame before calling the original parent's continuation.\n      folly::popAsyncStackFrameCallee(*promise.parentAsyncFrame_);\n      if (promise.error_) {\n        auto [handle, frame] =\n            promise.continuation_.getErrorHandle(promise.error_);\n        return handle.getHandle();\n      }\n      return promise.continuation_.getHandle();\n    }\n\n    [[noreturn]] void await_resume() noexcept { folly::assume_unreachable(); }\n  };\n\n  void setContext(\n      ExtendedCoroutineHandle continuation,\n      folly::AsyncStackFrame* asyncFrame,\n      folly::Executor::KeepAlive<> executor,\n      folly::exception_wrapper error = {}) {\n    continuation_ = continuation;\n    parentAsyncFrame_ = asyncFrame;\n    executor_ = std::move(executor);\n    error_ = std::move(error);\n  }\n\n  suspend_always initial_suspend() noexcept { return {}; }\n\n  FinalAwaiter final_suspend() noexcept { return {}; }\n\n  template <typename Awaitable>\n  auto await_transform(Awaitable&& awaitable) {\n    return folly::coro::co_withAsyncStack(\n        folly::coro::co_viaIfAsync(\n            executor_.get_alias(), static_cast<Awaitable&&>(awaitable)));\n  }\n\n  folly::AsyncStackFrame& getAsyncFrame() noexcept {\n    return *parentAsyncFrame_;\n  }\n\n  [[noreturn]] void unhandled_exception() noexcept {\n    /// Since ScopeExitTasks execute after the parent coroutine has completed,\n    /// we are unable to propagate exceptions back to the caller. Similar to\n    /// throwing another exception while unwinding an exception, we opt to\n    /// terminate here by throwing within a noexcept frame.\n    rethrow_current_exception();\n  }\n\n  void return_void() noexcept {}\n\n protected:\n  template <typename... Args>\n  friend class ScopeExitTask;\n\n  ExtendedCoroutineHandle continuation_;\n  folly::AsyncStackFrame* parentAsyncFrame_;\n  folly::Executor::KeepAlive<> executor_;\n  folly::exception_wrapper error_;\n  coroutine_handle<ScopeExitTaskPromiseBase> next_;\n};\n\ntemplate <typename... Args>\nclass ScopeExitTaskPromise : public ScopeExitTaskPromiseBase {\n public:\n  template <typename Action>\n  explicit ScopeExitTaskPromise(Action&&, Args&... args) noexcept\n      : args_(args...) {}\n\n  ScopeExitTask<Args...> get_return_object() noexcept;\n\n private:\n  friend class ScopeExitTask<Args...>;\n\n  std::tuple<Args&...> args_;\n};\n\ntemplate <typename... Args>\nclass [[nodiscard]] ScopeExitTask {\n public:\n  using promise_type = ScopeExitTaskPromise<Args...>;\n\n private:\n  class Awaiter;\n  using handle_t = coroutine_handle<promise_type>;\n\n public:\n  explicit ScopeExitTask(handle_t coro) noexcept : coro_(coro) {}\n\n  ~ScopeExitTask() {\n    /// Failing to await this Task is likely a bug\n    DCHECK(!coro_);\n  }\n\n  ScopeExitTask(ScopeExitTask&& t) noexcept\n      : coro_(std::exchange(t.coro_, {})) {}\n\n  friend auto co_viaIfAsync(Executor::KeepAlive<>, ScopeExitTask&& t) noexcept {\n    DCHECK(t.coro_);\n    return Awaiter{std::exchange(t.coro_, {})};\n  }\n\n  /// We explicitly do not handle co_withCancellation, as these tasks are\n  /// designed to always run at the end of their parent coroutine.\n\n private:\n  class Awaiter {\n   public:\n    explicit Awaiter(handle_t coro) noexcept : coro_(coro) {}\n\n    Awaiter(Awaiter&& other) noexcept : coro_(std::exchange(other.coro_, {})) {}\n\n    Awaiter(const Awaiter&) = delete;\n\n    ~Awaiter() {\n      /// The coro will destroy itself in the FinalAwaiter, before continuing\n      /// the next continuation\n      DCHECK(!coro_);\n    }\n\n    bool await_ready() const noexcept { return false; }\n\n    template <typename Promise>\n    bool await_suspend(coroutine_handle<Promise> parent) noexcept {\n      auto& promise = coro_.promise();\n      auto& parentPromise = parent.promise();\n\n      /// Calling co_attachScopeExit here inserts the ScopeExit coroutine handle\n      /// as the parent's continuation, and sets the ScopeExit's continuation as\n      /// the parents.\n      ///\n      /// Before:\n      /// Parent FinalAwaiter -> Parent's continuation\n      ///\n      /// After one scope exit:\n      /// Parent FinalAwaiter -> ScopeExit1 -> Parent's Continuation\n      /// After two scope exits:\n      /// Parent FinalAwaiter -> ScopeExit2 -> ScopeExit1 -> Parent's\n      /// continuation\n      ///\n      /// This ensures that the scope exit coroutines are executed in reverse\n      /// order to when they were attached in the parent.\n      ///\n      /// Since each ScopeExitTask runs as a continuation at the end of the\n      /// parent coroutine's scope without popping the async stack to the\n      /// caller, we must run within the parent's async frame. In order to\n      /// guarantee correctness, the parent must defer responsibility of popping\n      /// the async stack frame to the final scope exit continuation.\n      promise.next_ = co_attachScopeExit(\n          parentPromise,\n          coroutine_handle<ScopeExitTaskPromiseBase>::from_promise(\n              coro_.promise()));\n\n      return false;\n    }\n\n    std::tuple<Args&...> await_resume() noexcept {\n      /// The coro will destroy itself in the FinalAwaiter\n      handle_t coro = std::exchange(coro_, {});\n      return std::move(coro.promise().args_);\n    }\n\n   private:\n    friend Awaiter tag_invoke(cpo_t<co_withAsyncStack>, Awaiter&& t) noexcept {\n      return std::move(t);\n    }\n\n    handle_t coro_;\n  };\n\n  handle_t coro_;\n};\n\ntemplate <typename... Args>\ninline ScopeExitTask<Args...>\nScopeExitTaskPromise<Args...>::get_return_object() noexcept {\n  return ScopeExitTask<Args...>{\n      coroutine_handle<ScopeExitTaskPromise>::from_promise(*this)};\n}\n\n} // namespace detail\n\nclass co_scope_exit_fn {\n  /// Use a static helper as we do not wish to pass the implicit `this` pointer\n  /// to the promise constructor\n  ///\n  /// TODO: It's not mandatory to elide copy/move of args into the coroutine\n  /// frame today, which makes using some types, like AsyncScope, annoying. For\n  /// non-copyable, non-moveable types, you must wrap the type in a\n  /// std::unique_ptr.\n  ///\n  /// We might be able to work around this by storing the arguments in the\n  /// promise type, rather than on the coroutine frame.\n  template <typename Action, typename... Args>\n  static detail::ScopeExitTask<Args...> coScopeExitImpl(\n      Action action, Args... args) {\n    co_await std::move(action)(std::move(args)...);\n  }\n\n public:\n  template <typename Action, typename... Args>\n  detail::ScopeExitTask<std::decay_t<Args>...> operator()(\n      Action&& action, Args&&... args) const {\n    return coScopeExitImpl(\n        static_cast<Action&&>(action), static_cast<Args&&>(args)...);\n  }\n};\n\n/// co_scope_exit is a utility function that allows you to associate\n/// continuations which execute at the end of the coroutine, just before\n/// resuming the caller.\n///\n/// The first argument is a Task-returning callable. The subsequent arguments\n/// are optional state that can be used within the exit coroutine. The cleanup\n/// action will assume ownership of the provided state by copying the state\n/// inside the exit coroutine.\n///\n/// If you need access to the state in both the parent coroutine *and* in the\n/// exit coroutine, you can receive l-values to the captured state as return\n/// values. See the example below.\n///\n/// If you attach multiple co_scope_exit coroutines, they will be executed in\n/// reverse order to the order in which they were registered.\n///\n/// CAUTION: The body of the co_scope_exit coroutine runs *after* the parent\n/// coroutine has already been destroyed. This means that any local variables in\n/// the coroutine body will no longer be accessible. Do not capture references\n/// to any locals in the exit coroutine, or else you will hit undefined\n/// behavior. Any state you wish to pass to the scope exit coroutine should be\n/// passed as an argument to co_scope_exit.\n///\n/// Example:\n/// folly::coro::Task<> doSomethingComplicated(std::vector<int> inputs) {\n///   auto&& [scope] = co_await folly::coro::co_scope_exit(\n///       [](auto scope) -> folly::coro::Task<> {\n///         co_await scope.joinAsync();\n///       }, std::make_unique<AsyncScope>());\n///\n///   // Do some complicated, potentially throwing work using the AsyncScope\n///   auto ex = co_await co_current_executor;\n///   asyncScope->add(co_withExecutor(ex, someTask(std::move(inputs))));\n/// }\n///\n/// The body of the coroutine passed to co_scope_exit will be executed when the\n/// parent task completes, either when the parent completes with a result, or\n/// due to an unhandled exception.\ninline constexpr co_scope_exit_fn co_scope_exit{};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/SerialQueueRunner.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/SerialQueueRunner.h>\n\n#include <functional>\n#include <stdexcept>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nvoid SerialQueueRunner::add(Work task) {\n  std::unique_lock lock{mut_};\n  if (done_) {\n    throw std::runtime_error(\"add after done\");\n  }\n  tasks_.push_back(std::move(task));\n  if (baton_) {\n    baton_->post();\n  }\n}\n\nvoid SerialQueueRunner::done() {\n  std::unique_lock lock{mut_};\n  if (done_) {\n    throw std::runtime_error(\"add after done\");\n  }\n  done_ = true;\n  if (baton_) {\n    baton_->post();\n  }\n}\n\nTask<> SerialQueueRunner::run() {\n  if (running_.exchange(true, std::memory_order_relaxed)) {\n    co_yield co_error{\n        make_exception_wrapper<std::runtime_error>(\"multiple calls to run\")};\n  }\n  while (true) {\n    auto [done, tasks] = co_await pull();\n    for (auto& task : tasks) {\n      auto res = co_await co_awaitTry(std::move(task));\n      exception_wrapper exn =\n          res.hasException() ? std::move(res.exception()) : exception_wrapper();\n      if (!exn_ && exn && !exn.get_exception<OperationCancelled>()) {\n        exn_ = std::move(exn);\n      }\n    }\n    if (done) {\n      break;\n    }\n  }\n  if (exn_) {\n    co_yield co_error{std::move(exn_)};\n  }\n}\n\nvoid SerialQueueRunner::cancel() {\n  std::unique_lock lock{mut_};\n  if (!done_) {\n    done();\n  }\n}\n\nTask<> SerialQueueRunner::await() {\n  CancellationCallback cb{\n      co_await co_current_cancellation_token,\n      std::bind(&SerialQueueRunner::cancel, this)};\n  co_await *baton_;\n}\n\nTask<SerialQueueRunner::PullResult> SerialQueueRunner::pull() {\n  std::unique_lock lock{mut_};\n  if (!done_ && tasks_.empty()) {\n    folly::coro::Baton baton;\n    baton_ = &baton;\n    lock.unlock();\n    co_await await();\n    lock.lock();\n    baton_ = nullptr;\n  }\n  co_return std::pair{done_, std::move(tasks_)};\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/SerialQueueRunner.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <mutex>\n#include <queue>\n#include <tuple>\n#include <vector>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/Task.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\n/// SerialQueueRunner\n///\n/// Runs coroutine work items submitted via add() in sequence, i.e. with no\n/// overlapping.\n///\n/// Different from scheduling via SequencedExecutor, which runs the *parts* of\n/// the work items between resume- and suspend-points in sequence, but which\n/// overlaps work items.\nclass SerialQueueRunner {\n private:\n  using Work = Task<>;\n\n public:\n  void add(Work task); // task must not throw when awaited!\n  void done();\n\n  Task<> run();\n\n private:\n  using PullResult = std::pair<bool, std::vector<Work>>;\n\n  struct Mutex : std::mutex {\n    // to suppress lint advice about holding lock objects alive across co_await\n    using folly_coro_aware_mutex = void;\n  };\n\n  void cancel();\n  Task<> await();\n  Task<PullResult> pull();\n\n  Mutex mut_{};\n  Baton* baton_{};\n  std::vector<Work> tasks_{};\n  bool done_{};\n  std::atomic<bool> running_{};\n  exception_wrapper exn_{};\n};\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/SharedLock.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <mutex>\n#include <type_traits>\n#include <utility>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n#include <folly/synchronization/Lock.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\nnamespace detail {\n\ntemplate <typename Mutex, typename Policy>\nclass [[nodiscard]] LockBase {\n public:\n  static_assert(std::is_same_v<\n                bool,\n                invoke_result_t<typename Policy::try_lock_fn, Mutex&>>);\n\n  LockBase() noexcept : mutex_(nullptr), locked_(false) {}\n\n  explicit LockBase(Mutex& mutex, std::defer_lock_t) noexcept\n      : mutex_(std::addressof(mutex)), locked_(false) {}\n\n  explicit LockBase(Mutex& mutex, std::adopt_lock_t) noexcept\n      : mutex_(std::addressof(mutex)), locked_(true) {}\n\n  explicit LockBase(Mutex& mutex, std::try_to_lock_t) noexcept(\n      noexcept(typename Policy::try_lock_fn{}(mutex)))\n      : mutex_(std::addressof(mutex)),\n        locked_(typename Policy::try_lock_fn{}(mutex)) {}\n\n  LockBase(LockBase&& other) noexcept\n      : mutex_(std::exchange(other.mutex_, nullptr)),\n        locked_(std::exchange(other.locked_, false)) {}\n\n  LockBase(const LockBase&) = delete;\n  LockBase& operator=(const LockBase&) = delete;\n\n  ~LockBase() {\n    if (locked_) {\n      typename Policy::unlock_fn{}(*mutex_);\n    }\n  }\n\n  LockBase& operator=(LockBase&& other) noexcept {\n    LockBase temp(std::move(other));\n    swap(temp);\n    return *this;\n  }\n\n  Mutex* mutex() const noexcept { return mutex_; }\n\n  Mutex* release() noexcept {\n    locked_ = false;\n    return std::exchange(mutex_, nullptr);\n  }\n\n  bool owns_lock() const noexcept { return locked_; }\n\n  explicit operator bool() const noexcept { return owns_lock(); }\n\n  bool try_lock() noexcept(noexcept(typename Policy::try_lock_fn{}(*mutex_))) {\n    DCHECK(!locked_);\n    DCHECK(mutex_ != nullptr);\n    locked_ = typename Policy::try_lock{}(*mutex_);\n    return locked_;\n  }\n\n  void unlock() noexcept(noexcept(typename Policy::unlock_fn{}(*mutex_))) {\n    DCHECK(locked_);\n    locked_ = false;\n    typename Policy::unlock_fn{}(*mutex_);\n  }\n\n  void swap(LockBase& other) noexcept {\n    std::swap(mutex_, other.mutex_);\n    std::swap(locked_, other.locked_);\n  }\n\n protected:\n  Mutex* mutex_;\n  bool locked_;\n};\n\nstruct lock_policy_shared {\n  using try_lock_fn = access::try_lock_shared_fn;\n  using unlock_fn = access::unlock_shared_fn;\n};\nstruct lock_policy_upgrade {\n  using try_lock_fn = access::try_lock_upgrade_fn;\n  using unlock_fn = access::unlock_upgrade_fn;\n};\n} // namespace detail\n\n/// This type mirrors the interface of std::shared_lock as much as possible.\n///\n/// The main difference between this type and std::shared_lock is that this\n/// type is designed to be used with asynchronous shared-mutex types where\n/// the lock acquisition is an asynchronous operation.\n///\n/// TODO: Actually implement the .co_lock() method on this class.\n///\n/// Workaround for now is to use:\n///   SharedLock<SharedMutex> lock{mutex, std::defer_lock};\n///   ...\n///   lock = co_await lock.mutex()->co_scoped_lock_shared();\ntemplate <typename Mutex>\nclass SharedLock : public detail::LockBase<Mutex, detail::lock_policy_shared> {\n public:\n  using detail::LockBase<Mutex, detail::lock_policy_shared>::LockBase;\n};\n\ntemplate <typename Mutex, typename... A>\nexplicit SharedLock(Mutex&, A const&...) -> SharedLock<Mutex>;\n\ntemplate <typename Mutex>\nclass UpgradeLock\n    : public detail::LockBase<Mutex, detail::lock_policy_upgrade> {\n public:\n  using detail::LockBase<Mutex, detail::lock_policy_upgrade>::LockBase;\n};\n\ntemplate <typename Mutex, typename... A>\nexplicit UpgradeLock(Mutex&, A const&...) -> UpgradeLock<Mutex>;\n\n/// Async version of the folly::transition_lock\n/// TODO: add more transition policies beyond just from upgrade to exclusive\ntemplate <typename Mutex>\nfolly::coro::Task<std::unique_lock<Mutex>> co_transition_lock(\n    UpgradeLock<Mutex>& lock) {\n  if (lock.owns_lock()) {\n    co_return co_await lock.release()->co_scoped_unlock_upgrade_and_lock();\n  } else {\n    co_return std::unique_lock<Mutex>{*lock.release(), std::defer_lock};\n  }\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/SharedMutex.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/SharedMutex.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nSharedMutexFair::~SharedMutexFair() {\n  assert(state_.lock()->lockedFlagAndReaderCount_ == kUnlocked);\n  assert(state_.lock()->waitersHead_ == nullptr);\n}\n\nbool SharedMutexFair::try_lock() noexcept {\n  auto lock = state_.lock();\n  if (lock->lockedFlagAndReaderCount_ == kUnlocked) {\n    lock->lockedFlagAndReaderCount_ = kExclusiveLockFlag;\n    return true;\n  }\n  return false;\n}\n\nbool SharedMutexFair::try_lock_shared() noexcept {\n  auto lock = state_.lock();\n  if (canLockShared(*lock)) {\n    lock->lockedFlagAndReaderCount_ += kSharedLockCountIncrement;\n    // check for potential overflow\n    assert(lock->lockedFlagAndReaderCount_ >= kSharedLockCountIncrement);\n    return true;\n  }\n  return false;\n}\n\nbool SharedMutexFair::try_lock_upgrade() noexcept {\n  auto lock = state_.lock();\n  if (canLockUpgrade(*lock)) {\n    lock->lockedFlagAndReaderCount_ |= kUpgradeLockFlag;\n    return true;\n  }\n  return false;\n}\n\nbool SharedMutexFair::try_unlock_upgrade_and_lock() noexcept {\n  auto lock = state_.lock();\n  assert(lock->lockedFlagAndReaderCount_ & kUpgradeLockFlag);\n  // skip the line and perform the upgrade as long as there is\n  // no outstanding shared locks\n  if (lock->lockedFlagAndReaderCount_ == kUpgradeLockFlag) {\n    lock->lockedFlagAndReaderCount_ = kExclusiveLockFlag;\n    return true;\n  }\n  return false;\n}\n\nvoid SharedMutexFair::unlock() noexcept {\n  LockAwaiterBase* awaitersToResume = nullptr;\n  {\n    auto lockedState = state_.lock();\n    assert(lockedState->lockedFlagAndReaderCount_ == kExclusiveLockFlag);\n    lockedState->lockedFlagAndReaderCount_ = kUnlocked;\n    awaitersToResume = getWaitersToResume(*lockedState, LockType::EXCLUSIVE);\n  }\n\n  resumeWaiters(awaitersToResume);\n}\n\nvoid SharedMutexFair::unlock_shared() noexcept {\n  LockAwaiterBase* awaitersToResume = nullptr;\n  {\n    auto lockedState = state_.lock();\n    assert(lockedState->lockedFlagAndReaderCount_ >= kSharedLockCountIncrement);\n    lockedState->lockedFlagAndReaderCount_ -= kSharedLockCountIncrement;\n    awaitersToResume = getWaitersToResume(*lockedState, LockType::SHARED);\n  }\n\n  resumeWaiters(awaitersToResume);\n}\n\nvoid SharedMutexFair::unlock_upgrade() noexcept {\n  LockAwaiterBase* awaitersToResume = nullptr;\n  {\n    auto lockedState = state_.lock();\n    assert(lockedState->lockedFlagAndReaderCount_ & kUpgradeLockFlag);\n    lockedState->lockedFlagAndReaderCount_ &= ~kUpgradeLockFlag;\n    awaitersToResume = getWaitersToResume(*lockedState, LockType::UPGRADE);\n  }\n\n  resumeWaiters(awaitersToResume);\n}\n\n// `getWaitersToResume` can sometimes perform long reader scan to\n// find readers to resume. But the amortized cost of it is still O(1),\n// as we never scan the same waiter more than once (except the head).\nSharedMutexFair::LockAwaiterBase* SharedMutexFair::getWaitersToResume(\n    SharedMutexFair::State& state, LockType prevLockType) noexcept {\n  // to keep the state transition code concise we only modify\n  // the mutex's waitersHead_ pointer, and depend on the SCOPE_EXIT\n  // to ensure the consistency of the state\n  SCOPE_EXIT {\n    if (state.waitersHead_ == nullptr) {\n      state.waitersTailNext_ = &state.waitersHead_;\n    }\n  };\n\n  // the state transition is a function of the lock type of the\n  // previous lock (what just got unlocked), the waiter(s) and the current mutex\n  // state (outstanding locks)\n  if (state.upgrader_ != nullptr) {\n    // there is an active upgrade lock holder waiting to upgrade to exclusive\n    // prioritize the lock transfer over other lock acquisition waiters\n    assert(state.lockedFlagAndReaderCount_ & kUpgradeLockFlag);\n    if (state.lockedFlagAndReaderCount_ == kUpgradeLockFlag) {\n      auto* waiter = std::exchange(state.upgrader_, nullptr);\n      // there can only be an active upgrade lock holder waiting to transfer to\n      // exclusive\n      assert(waiter->nextAwaiter_ == nullptr);\n      state.lockedFlagAndReaderCount_ = kExclusiveLockFlag;\n      return waiter;\n    } else {\n      // drain the readers and do not grant any locks to other waiters\n      return nullptr;\n    }\n  }\n\n  // there is no active upgrader; process the pending lock acquisition requests\n  // in order\n  auto* head = state.waitersHead_;\n  if (head == nullptr) {\n    // there is no waiters to resume\n    return nullptr;\n  }\n\n  // There is no pending lock transfers. The mutex can only be unlock_* into one\n  // of the following states:\n  // - unlocked (from unlock)\n  // - shared locked (from unlock_shared or unlock_upgrade)\n  // - upgrade and shared locked (from unlock_shared)\n  assert(state.lockedFlagAndReaderCount_ != kExclusiveLockFlag);\n  if (head->lockType_ == LockType::EXCLUSIVE) {\n    if (state.lockedFlagAndReaderCount_ == kUnlocked) {\n      // transition to exclusively locked state\n      state.waitersHead_ = std::exchange(head->nextAwaiter_, nullptr);\n      state.lockedFlagAndReaderCount_ = kExclusiveLockFlag;\n      --state.waitingWriterCount_;\n      return head;\n    }\n  } else if (\n      head->lockType_ != LockType::UPGRADE ||\n      (state.lockedFlagAndReaderCount_ & kUpgradeLockFlag) == 0) {\n    // Now the next waiter is either a reader or an upgrader, and the mutex\n    // state ensures that the next waiter can always be resumed.\n    // The only case that we can't resume the next head (and skip scanning)\n    // is when the head is an upgrader and the mutex is in an upgrade locked\n    // state. There is nothing to resume in that case (no readers will be queued\n    // to begin with).\n    state.waitersHead_ = scanReadersAndUpgrader(head, state, prevLockType);\n    return head;\n  }\n  return nullptr;\n}\n\nvoid SharedMutexFair::resumeWaiters(LockAwaiterBase* awaiters) noexcept {\n  while (awaiters != nullptr) {\n    std::exchange(awaiters, awaiters->nextAwaiter_)->resume();\n  }\n}\n\n// Scan for a run of SHARED and UPGRADE lock types and return the next\n// waiter\nSharedMutexFair::LockAwaiterBase* SharedMutexFair::scanReadersAndUpgrader(\n    SharedMutexFair::LockAwaiterBase* head,\n    SharedMutexFair::State& lockedState,\n    LockType prevLockType) noexcept {\n  SharedMutexFair::LockAwaiterBase* last =\n      nullptr; // tail of the waiters to be resumed\n  size_t& state = lockedState.lockedFlagAndReaderCount_;\n  // Scan for a continuous run of SHARED and UPGRADE lock types\n  while (head != nullptr) {\n    if (head->lockType_ == LockType::SHARED) {\n      state += kSharedLockCountIncrement;\n      // check for potential overflow\n      assert(state >= kSharedLockCountIncrement);\n    } else if (\n        head->lockType_ == LockType::UPGRADE &&\n        (state & kUpgradeLockFlag) == 0) {\n      state |= kUpgradeLockFlag;\n    } else {\n      break;\n    }\n    last = head;\n    head = head->nextAwaiter_;\n  }\n  assert(last != nullptr);\n\n  auto* newWaiterHead = head;\n  auto* prev = last;\n  if (prevLockType == LockType::EXCLUSIVE) {\n    // Do a long reader scan up until the next exclusive lock waiter\n    // iff someone just unlocked an exclusive lock.\n    // e.g. when the waiter list looks like\n    // U1 U2 U3 U4 S1 W S2\n    // , and someone just unlocked an exclusive lock\n    // we unlock U1 _and_ S1.\n    // However, doing long reader scan every time someone unlocks any lock\n    // is wasteful. Readers can only be blocked when the mutex has an active\n    // exclusive lock or there are writers waiting ahead of it. In other words,\n    // a reader can only be blocked when there is one or more writers ahead of\n    // it. The writer can be holding an active lock or simply waiting ahead of\n    // the reader.\n    //\n    // So we only need to do the long reader scan when an exclusive lock is\n    // released, and we do a long reader scan up until the next exclusive\n    // waiter. If any other lock (shared or upgrade) is released, all the\n    // readers are blocked right now should remain blocked (as they are blocked\n    // one or more writers ahead of them).\n    //\n    // We never want to scan past an exclusive waiter, as it would lead to\n    // writer starvation and makes this mutex \"unfair\" or reader-prioritized. In\n    // this way, we can keep the amortized cost of `getWaitersToResume` be O(1).\n    //\n    // Notice that previous lock type being EXCLUSIVE is different from mutex\n    // state being UNLOCKED, as the first condition is more restrictive. E.g. if\n    // the mutex enters UNLOCKED from unlock_upgrade(), and the waiter list\n    // looks like \"U U W S W U ... \" there is no point to scan the waiter list\n    // for readers, as either there is no blocked readers to begin with, or\n    // they are blocked by another writer ahead of them, and should remain\n    // blocked. The waiter list would never look like \"U U S\" at the time of\n    // unlock_upgrade() because the \"S\" should never be blocked to begin with.\n    //\n    // This behavior should not lead to starvation\n    // - it does not starve writers because we do not scan pass \"W\"\n    // - it does not starve upgraders because these upgraders are not blocked\n    //   by the readers anyway\n    // - it does not block lock transition (upgrade -> exclusive) because\n    //   lock transition is handled with highest priority, and no readers\n    //   are granted when a lock transition is in progress, when it is trying\n    //   to drain all the readers\n    // E.g. the waiter list might look like U1 U2 S1 U3 S2 W1 S3\n    // After calling this function, the `head` should point to U1 S1 S2\n    // and the return value should point to U2 U3 W1 S3\n    while (head != nullptr) {\n      if (head->lockType_ == LockType::SHARED) {\n        assert(head != newWaiterHead);\n        assert(prev != last);\n        state += kSharedLockCountIncrement;\n        // check for potential overflow\n        assert(state >= kSharedLockCountIncrement);\n        prev->nextAwaiter_ = head->nextAwaiter_;\n        last->nextAwaiter_ = head;\n        last = head;\n        head = head->nextAwaiter_;\n        if (head == nullptr) {\n          // if we skipped ahead and resumed the last waiter\n          // we need to update the waiter tail pointer\n          lockedState.waitersTailNext_ = &prev->nextAwaiter_;\n        }\n      } else if (head->lockType_ == LockType::UPGRADE) {\n        // skip the upgrade waiter\n        prev = head;\n        head = head->nextAwaiter_;\n      } else {\n        break;\n      }\n    }\n  }\n\n  last->nextAwaiter_ = nullptr;\n  return newWaiterHead;\n}\n#endif\n"
  },
  {
    "path": "folly/coro/SharedMutex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <limits>\n#include <mutex>\n#include <utility>\n\n#include <folly/Executor.h>\n#include <folly/SpinLock.h>\n#include <folly/Synchronized.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/SharedLock.h>\n#include <folly/coro/ViaIfAsync.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n/// The folly::coro::SharedMutexFair class provides a thread synchronisation\n/// primitive that allows a coroutine to asynchronously acquire a lock on the\n/// mutex.\n///\n/// The mutex supports three kinds of locks:\n/// - exclusive-lock - Also known as a write-lock.\n///                    While an exclusive lock is held, no other thread will be\n///                    able to acquire either an exclusive lock or a shared\n///                    lock until the exclusive lock is released.\n/// - shared-lock    - Also known as a read-lock.\n///                    The mutex permits multiple shared locks to be held\n///                    concurrently but does not permit shared locks to be held\n///                    concurrently with exclusive locks.\n/// - upgrade-lock   - When an upgrade lock is held, others can still acquire\n///                    shared locks but no exclusive lock, or upgrade lock.\n///                    An upgrade lock can be later upgraded to an exclusive\n///                    lock atomically after all the outstanding shared locks\n///                    are released.\n///\n/// This mutex employs a fair lock acquisition strategy that attempts to process\n/// locks in a mostly FIFO order in which they arrive at the mutex.\n/// This means that if the mutex is currently read-locked and some coroutine\n/// tries to acquire a write-lock, that subsequent read-lock attempts will\n/// be queued up behind the write-lock, allowing the write-lock to be acquired\n/// in a bounded amount of time.\n///\n/// One implication of this strategy is that it is not safe to unconditionally\n/// acquire a new read-lock while already holding a read-lock, since it's\n/// possible that this could lead to deadlock if there was another coroutine\n/// that was currently waiting on a write-lock.\n///\n/// Notably, lock transition (e.g. upgrade an upgrade lock to an exclusive lock)\n/// does not respect the FIFO order and is eager. This means a pending lock\n/// transition will be processed as soon as possible. This is to avoid deadlock\n/// in following scenario\n/// 1. coroutine A has the upgrade lock\n/// 2. coroutine B is waiting for an exclusive lock\n/// 3. coroutine A tries to upgrade the lock to exclusive\n/// Coroutine A and B would deadlock if we process the lock transition\n/// (operation #3) in FIFO order. The readers will not be starved because they\n/// are not blocked by the upgrade state to begin with. The writers/upgraders\n/// will not be starved because they cannot acquire the lock anyway.\n///\n/// The locks acquired by this mutex do not have thread affinity. A coroutine\n/// can acquire the lock on one thread and release the lock on another thread.\n///\n/// Example usage:\n///\n///  class AsyncStringSet {\n///    mutable folly::coro::SharedMutexFair mutex_;\n///    std::unordered_set<std::string> values_;\n///\n///    AsyncStringSet() = default;\n///\n///    folly::coro::Task<bool> insert(std::string value) {\n///      auto lock = co_await mutex_.co_scoped_lock();\n///      co_return values_.insert(value).second;\n///    }\n///\n///    folly::coro::Task<bool> remove(std::string value) {\n///      auto lock = co_await mutex_.co_scoped_lock();\n///      co_return values_.erase(value) > 0;\n///    }\n///\n///    folly::coro::Task<bool> contains(std::string value) const {\n///      auto lock = co_await mutex_.co_scoped_lock_shared();\n///      co_return values_.count(value) > 0;\n///    }\n///  };\nclass SharedMutexFair : private folly::NonCopyableNonMovable {\n  template <typename Awaiter>\n  class LockOperation;\n  class LockAwaiter;\n  class ScopedLockAwaiter;\n  class LockSharedAwaiter;\n  class ScopedLockSharedAwaiter;\n  class LockUpgradeAwaiter;\n  class ScopedLockUpgradeAwaiter;\n  class UnlockUpgradeAndLockAwaiter;\n  class ScopedUnlockUpgradeAndLockAwaiter;\n\n public:\n  SharedMutexFair() noexcept = default;\n\n  ~SharedMutexFair();\n\n  /// Try to acquire an exclusive lock on the mutex synchronously.\n  ///\n  /// If this returns true then the exclusive lock was acquired synchronously\n  /// and the caller is responsible for calling .unlock() later to release\n  /// the exclusive lock. If this returns false then the lock was not acquired.\n  ///\n  /// Consider using a std::unique_lock to ensure the lock is released at the\n  /// end of a scope.\n  bool try_lock() noexcept;\n\n  /// Try to acquire a shared lock on the mutex synchronously.\n  ///\n  /// If this returns true then the shared lock was acquired synchronously\n  /// and the caller is responsible for calling .unlock_shared() later to\n  /// release the shared lock.\n  bool try_lock_shared() noexcept;\n\n  /// Try to acquire an upgrade lock on the mutex synchronously.\n  ///\n  /// If this returns true then the upgrade lock was acquired synchronously\n  /// and the caller is responsible for calling .unlock_upgrade() later to\n  /// release the upgrade lock.\n  bool try_lock_upgrade() noexcept;\n\n  /// Asynchronously acquire an exclusive lock on the mutex.\n  ///\n  /// Returns a SemiAwaitable<void> type that requires the caller to inject\n  /// an executor by calling .viaIfAsync(executor) and then co_awaiting the\n  /// result to wait for the lock to be acquired. Note that if the caller is\n  /// awaiting the lock operation within a folly::coro::Task then the current\n  /// executor will be injected implicitly without needing to call\n  /// .viaIfAsync().\n  ///\n  /// If the lock was acquired synchronously then the awaiting coroutine\n  /// continues on the current thread without suspending.\n  /// If the lock could not be acquired synchronously then the awaiting\n  /// coroutine is suspended and later resumed on the specified executor when\n  /// the lock becomes available.\n  ///\n  /// After this operation completes, the caller is responsible for calling\n  /// .unlock() to release the lock.\n  [[nodiscard]] LockOperation<LockAwaiter> co_lock() noexcept;\n\n  /// Asynchronously acquire an exclusive lock on the mutex and return an object\n  /// that will release the lock when it goes out of scope.\n  ///\n  /// Returns a SemiAwaitable<std::unique_lock<SharedMutexFair>> that, once\n  /// associated with an executor using .viaIfAsync(), must be co_awaited to\n  /// wait for the lock to be acquired.\n  ///\n  /// If the lock could be acquired immediately then the coroutine continues\n  /// execution without suspending. Otherwise, the coroutine is suspended and\n  /// will later be resumed on the specified executor once the lock has been\n  /// acquired.\n  [[nodiscard]] LockOperation<ScopedLockAwaiter> co_scoped_lock() noexcept;\n\n  /// Asynchronously acquire a shared lock on the mutex.\n  ///\n  /// Returns a SemiAwaitable<void> type that requires the caller to inject\n  /// an executor by calling .viaIfAsync(executor) and then co_awaiting the\n  /// result to wait for the lock to be acquired. Note that if the caller is\n  /// awaiting the lock operation within a folly::coro::Task then the current\n  /// executor will be injected implicitly without needing to call\n  /// .viaIfAsync().\n  ///\n  /// If the lock was acquired synchronously then the awaiting coroutine\n  /// continues on the current thread without suspending.\n  /// If the lock could not be acquired synchronously then the awaiting\n  /// coroutine is suspended and later resumed on the specified executor when\n  /// the lock becomes available.\n  ///\n  /// After this operation completes, the caller is responsible for calling\n  /// .unlock_shared() to release the lock.\n  [[nodiscard]] LockOperation<LockSharedAwaiter> co_lock_shared() noexcept;\n\n  /// Asynchronously acquire a shared lock on the mutex and return an object\n  /// that will release the lock when it goes out of scope.\n  ///\n  /// Returns a SemiAwaitable<std::shared_lock<SharedMutexFair>> that, once\n  /// associated with an executor using .viaIfAsync(), must be co_awaited to\n  /// wait for the lock to be acquired.\n  ///\n  /// If the lock could be acquired immediately then the coroutine continues\n  /// execution without suspending. Otherwise, the coroutine is suspended and\n  /// will later be resumed on the specified executor once the lock has been\n  /// acquired.\n  [[nodiscard]] LockOperation<ScopedLockSharedAwaiter>\n  co_scoped_lock_shared() noexcept;\n\n  /// Asynchronously acquire an upgrade lock on the mutex.\n  ///\n  /// Returns a SemiAwaitable<void> type that requires the caller to inject\n  /// an executor by calling .viaIfAsync(executor) and then co_awaiting the\n  /// result to wait for the lock to be acquired. Note that if the caller is\n  /// awaiting the lock operation within a folly::coro::Task then the current\n  /// executor will be injected implicitly without needing to call\n  /// .viaIfAsync().\n  ///\n  /// If the lock was acquired synchronously then the awaiting coroutine\n  /// continues on the current thread without suspending.\n  /// If the lock could not be acquired synchronously then the awaiting\n  /// coroutine is suspended and later resumed on the specified executor when\n  /// the lock becomes available.\n  ///\n  /// After this operation completes, the caller is responsible for calling\n  /// .unlock_upgrade() to release the lock.\n  [[nodiscard]] LockOperation<LockUpgradeAwaiter> co_lock_upgrade() noexcept;\n\n  /// Asynchronously acquire an upgrade lock on the mutex and return an object\n  /// that will release the lock when it goes out of scope.\n  ///\n  /// Returns a SemiAwaitable<UpgradeLock<SharedMutexFair>> that, once\n  /// associated with an executor using .viaIfAsync(), must be co_awaited to\n  /// wait for the lock to be acquired.\n  ///\n  /// If the lock could be acquired immediately then the coroutine continues\n  /// execution without suspending. Otherwise, the coroutine is suspended and\n  /// will later be resumed on the specified executor once the lock has been\n  /// acquired.\n  [[nodiscard]] LockOperation<ScopedLockUpgradeAwaiter>\n  co_scoped_lock_upgrade() noexcept;\n\n  /// Asynchronously transition the currently held upgrade lock to exclusive.\n  ///\n  /// Returns a SemiAwaitable<void> type that requires the caller to inject\n  /// an executor by calling .viaIfAsync(executor) and then co_awaiting the\n  /// result to wait for the lock to be acquired. Note that if the caller is\n  /// awaiting the lock operation within a folly::coro::Task then the current\n  /// executor will be injected implicitly without needing to call\n  /// .viaIfAsync().\n  ///\n  /// If the lock was transitioned synchronously then the awaiting coroutine\n  /// continues on the current thread without suspending.\n  /// If the lock could not be transitioned synchronously then the awaiting\n  /// coroutine is suspended and later resumed on the specified executor when\n  /// the lock becomes available.\n  ///\n  /// After this operation completes, the caller is responsible for calling\n  /// .unlock() to release the lock.\n  [[nodiscard]] LockOperation<UnlockUpgradeAndLockAwaiter>\n  co_unlock_upgrade_and_lock() noexcept;\n\n  /// Asynchronously transfer the currently held upgrade lock to exclusive\n  /// and return an object that will release the exclusive lock when it\n  /// goes out of scope.\n  ///\n  /// Notice that if the upgrade lock is acquired using\n  /// `co_scoped_lock_upgrade()`, one should transfer the lock via\n  /// `co_transition_lock(coro::UpgradeLock<coro::SharedMutex>&)` to avoid\n  /// double unlock. This method is mostly useful if the original upgrade\n  /// lock is acquired manually via `co_await mutex.co_lock_upgrade();`.\n  ///\n  /// Returns a SemiAwaitable<std::unique_lock<SharedMutexFair>> that, once\n  /// associated with an executor using .viaIfAsync(), must be co_awaited to\n  /// wait for the lock to be acquired.\n  ///\n  /// If the lock could be acquired immediately then the coroutine continues\n  /// execution without suspending. Otherwise, the coroutine is suspended and\n  /// will later be resumed on the specified executor once the lock has been\n  /// acquired.\n  [[nodiscard]] LockOperation<ScopedUnlockUpgradeAndLockAwaiter>\n  co_scoped_unlock_upgrade_and_lock() noexcept;\n\n  /// Release the exclusive lock.\n  ///\n  /// This will resume the next coroutine(s) waiting to acquire the lock, if\n  /// any.\n  void unlock() noexcept;\n\n  /// Release a shared lock.\n  ///\n  /// If this is the last shared lock then this will resume the next\n  /// coroutine(s) waiting to acquire the lock, if any.\n  void unlock_shared() noexcept;\n\n  /// Release an upgrade lock.\n  ///\n  /// This will resume the next coroutine(s) waiting to acquire an exclusive\n  /// lock or an upgrade lock, if any.\n  void unlock_upgrade() noexcept;\n\n  /// Try to atomically transition an upgrade lock to an exclusive lock\n  /// synchronously.\n  ///\n  /// If this returns true then the lock was acquired synchronously\n  /// and the caller is responsible for calling .unlock() later to\n  /// release the lock. Otherwise, the caller remains responsible for calling\n  /// .unlock_upgrade() later to release the upgrade lock.\n  bool try_unlock_upgrade_and_lock() noexcept;\n\n private:\n  using folly_coro_aware_mutex = std::true_type;\n\n  enum class LockType : std::uint8_t { EXCLUSIVE, UPGRADE, SHARED };\n\n  class LockAwaiterBase {\n   protected:\n    friend class SharedMutexFair;\n\n    explicit LockAwaiterBase(SharedMutexFair& mutex, LockType lockType) noexcept\n        : mutex_(&mutex), nextAwaiter_(nullptr), lockType_(lockType) {}\n\n    void resume() noexcept { continuation_.resume(); }\n\n    SharedMutexFair* mutex_;\n    LockAwaiterBase* nextAwaiter_;\n    coroutine_handle<> continuation_;\n    LockType lockType_;\n  };\n\n  class LockAwaiter : public LockAwaiterBase {\n   public:\n    explicit LockAwaiter(SharedMutexFair& mutex) noexcept\n        : LockAwaiterBase(mutex, LockType::EXCLUSIVE) {}\n\n    bool await_ready() noexcept { return mutex_->try_lock(); }\n\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES bool await_suspend(\n        coroutine_handle<> continuation) noexcept {\n      auto lock = mutex_->state_.lock();\n\n      // Exclusive lock can only be acquired if it's currently unlocked.\n      if (lock->lockedFlagAndReaderCount_ == kUnlocked) {\n        lock->lockedFlagAndReaderCount_ = kExclusiveLockFlag;\n        return false;\n      }\n\n      // Append to the end of the waiters queue.\n      continuation_ = continuation;\n      ++lock->waitingWriterCount_;\n      *lock->waitersTailNext_ = this;\n      lock->waitersTailNext_ = &nextAwaiter_;\n      return true;\n    }\n\n    void await_resume() noexcept {}\n  };\n\n  class LockSharedAwaiter : public LockAwaiterBase {\n   public:\n    explicit LockSharedAwaiter(SharedMutexFair& mutex) noexcept\n        : LockAwaiterBase(mutex, LockType::SHARED) {}\n\n    bool await_ready() noexcept { return mutex_->try_lock_shared(); }\n\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES bool await_suspend(\n        coroutine_handle<> continuation) noexcept {\n      auto lock = mutex_->state_.lock();\n\n      if (canLockShared(*lock)) {\n        lock->lockedFlagAndReaderCount_ += kSharedLockCountIncrement;\n        // check for potential overflow\n        assert(lock->lockedFlagAndReaderCount_ >= kSharedLockCountIncrement);\n        return false;\n      }\n\n      // Lock not available immediately.\n      // Queue up for later resumption.\n      continuation_ = continuation;\n      *lock->waitersTailNext_ = this;\n      lock->waitersTailNext_ = &nextAwaiter_;\n      return true;\n    }\n\n    void await_resume() noexcept {}\n  };\n\n  class LockUpgradeAwaiter : public LockAwaiterBase {\n   public:\n    explicit LockUpgradeAwaiter(SharedMutexFair& mutex) noexcept\n        : LockAwaiterBase(mutex, LockType::UPGRADE) {}\n\n    bool await_ready() noexcept { return mutex_->try_lock_upgrade(); }\n\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES bool await_suspend(\n        coroutine_handle<> continuation) noexcept {\n      auto lock = mutex_->state_.lock();\n\n      if (canLockUpgrade(*lock)) {\n        lock->lockedFlagAndReaderCount_ |= kUpgradeLockFlag;\n        return false;\n      }\n\n      continuation_ = continuation;\n      *lock->waitersTailNext_ = this;\n      lock->waitersTailNext_ = &nextAwaiter_;\n      return true;\n    }\n\n    void await_resume() noexcept {}\n  };\n\n  class UnlockUpgradeAndLockAwaiter : public LockAwaiterBase {\n   public:\n    explicit UnlockUpgradeAndLockAwaiter(SharedMutexFair& mutex) noexcept\n        : LockAwaiterBase(mutex, LockType::EXCLUSIVE) {}\n\n    bool await_ready() noexcept {\n      return mutex_->try_unlock_upgrade_and_lock();\n    }\n\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES bool await_suspend(\n        coroutine_handle<> continuation) noexcept {\n      auto lock = mutex_->state_.lock();\n\n      assert(lock->lockedFlagAndReaderCount_ & kUpgradeLockFlag);\n      if (lock->lockedFlagAndReaderCount_ == kUpgradeLockFlag) {\n        lock->lockedFlagAndReaderCount_ = kExclusiveLockFlag;\n        return false;\n      }\n\n      continuation_ = continuation;\n      assert(lock->upgrader_ == nullptr);\n      lock->upgrader_ = this;\n      return true;\n    }\n\n    void await_resume() noexcept {}\n  };\n\n  class ScopedLockAwaiter : public LockAwaiter {\n   public:\n    using LockAwaiter::LockAwaiter;\n\n    [[nodiscard]] std::unique_lock<SharedMutexFair> await_resume() noexcept {\n      LockAwaiter::await_resume();\n      return std::unique_lock<SharedMutexFair>{*mutex_, std::adopt_lock};\n    }\n  };\n\n  class ScopedLockSharedAwaiter : public LockSharedAwaiter {\n   public:\n    using LockSharedAwaiter::LockSharedAwaiter;\n\n    [[nodiscard]] SharedLock<SharedMutexFair> await_resume() noexcept {\n      LockSharedAwaiter::await_resume();\n      return SharedLock<SharedMutexFair>{*mutex_, std::adopt_lock};\n    }\n  };\n\n  class ScopedLockUpgradeAwaiter : public LockUpgradeAwaiter {\n   public:\n    using LockUpgradeAwaiter::LockUpgradeAwaiter;\n\n    [[nodiscard]] UpgradeLock<SharedMutexFair> await_resume() noexcept {\n      LockUpgradeAwaiter::await_resume();\n      return UpgradeLock<SharedMutexFair>{*mutex_, std::adopt_lock};\n    }\n  };\n\n  class ScopedUnlockUpgradeAndLockAwaiter : public UnlockUpgradeAndLockAwaiter {\n   public:\n    using UnlockUpgradeAndLockAwaiter::UnlockUpgradeAndLockAwaiter;\n\n    [[nodiscard]] std::unique_lock<SharedMutexFair> await_resume() noexcept {\n      UnlockUpgradeAndLockAwaiter::await_resume();\n      return std::unique_lock<SharedMutexFair>{*mutex_, std::adopt_lock};\n    }\n  };\n\n  friend class LockAwaiter;\n\n  template <typename Awaiter>\n  class LockOperation {\n   public:\n    explicit LockOperation(SharedMutexFair& mutex) noexcept : mutex_(mutex) {}\n\n    auto viaIfAsync(folly::Executor::KeepAlive<> executor) const {\n      return folly::coro::co_viaIfAsync(std::move(executor), Awaiter{mutex_});\n    }\n\n   private:\n    SharedMutexFair& mutex_;\n  };\n\n  // There is an invariant that if the mutex state is unlocked, there must be no\n  // waiters; the converse is obviously not always true. This is guaranteed by\n  // the `getWaitersToResume` function. If there are waiters after an unlock_*\n  // operation, the mutex state will transition to a non-unlocked state.\n  // This helps avoid a redundant check on the waiters list when the mutex is\n  // unlocked.\n  struct State {\n    State() noexcept\n        : lockedFlagAndReaderCount_(kUnlocked),\n          waitingWriterCount_(0),\n          waitersHead_(nullptr),\n          upgrader_(nullptr),\n          waitersTailNext_(&waitersHead_) {}\n\n    // bit 0 - exclusive lock is held\n    // bit 1 - upgrade lock is held\n    // bits 2-[31/63] - count of held shared locks\n    std::size_t lockedFlagAndReaderCount_;\n    std::size_t waitingWriterCount_;\n    LockAwaiterBase* waitersHead_;\n    // active upgrade lock holder who's waiting to upgrade to exclusive\n    // at most one waiter can be in such state\n    LockAwaiterBase* upgrader_;\n    LockAwaiterBase** waitersTailNext_;\n  };\n\n  static LockAwaiterBase* getWaitersToResume(\n      State& state, LockType prevLockType) noexcept;\n  static LockAwaiterBase* scanReadersAndUpgrader(\n      LockAwaiterBase* head,\n      State& lockedState,\n      LockType prevLockType) noexcept;\n\n  static void resumeWaiters(LockAwaiterBase* awaiters) noexcept;\n  static bool canLockShared(const State& state) noexcept {\n    // a shared lock can be acquired if there are no exclusive locks held,\n    // exclusive lock pending or lock transition pending\n    // an exclusive lock is pending if there are queued waiters for\n    // it; there is a pending lock transition if there is active upgrade lock\n    // waiting to be upgraded to exclusive\n    return state.lockedFlagAndReaderCount_ == kUnlocked ||\n        (state.lockedFlagAndReaderCount_ != kExclusiveLockFlag &&\n         state.waitingWriterCount_ == 0 && state.upgrader_ == nullptr);\n  }\n  static bool canLockUpgrade(const State& state) noexcept {\n    return state.lockedFlagAndReaderCount_ == kUnlocked ||\n        ((state.lockedFlagAndReaderCount_ &\n          (kExclusiveLockFlag | kUpgradeLockFlag)) == 0 &&\n         state.waitingWriterCount_ == 0);\n  }\n\n  static constexpr std::size_t kUnlocked = 0;\n  static constexpr std::size_t kExclusiveLockFlag = 1;\n  static constexpr std::size_t kUpgradeLockFlag = 2;\n  static constexpr std::size_t kSharedLockCountIncrement = 4;\n\n  struct ExclusiveMutex {\n    std::unique_ptr<std::mutex> impl_{std::make_unique<std::mutex>()};\n    void lock() { return impl_->lock(); }\n    void unlock() { return impl_->unlock(); }\n  };\n  using Mutex = std::conditional_t<kIsSanitizeThread, ExclusiveMutex, SpinLock>;\n  folly::Synchronized<State, Mutex> state_;\n};\n\ninline SharedMutexFair::LockOperation<SharedMutexFair::LockAwaiter>\nSharedMutexFair::co_lock() noexcept {\n  return LockOperation<LockAwaiter>{*this};\n}\n\ninline SharedMutexFair::LockOperation<SharedMutexFair::LockSharedAwaiter>\nSharedMutexFair::co_lock_shared() noexcept {\n  return LockOperation<LockSharedAwaiter>{*this};\n}\n\ninline SharedMutexFair::LockOperation<SharedMutexFair::ScopedLockAwaiter>\nSharedMutexFair::co_scoped_lock() noexcept {\n  return LockOperation<ScopedLockAwaiter>{*this};\n}\n\ninline SharedMutexFair::LockOperation<SharedMutexFair::ScopedLockSharedAwaiter>\nSharedMutexFair::co_scoped_lock_shared() noexcept {\n  return LockOperation<ScopedLockSharedAwaiter>{*this};\n}\n\ninline SharedMutexFair::LockOperation<SharedMutexFair::LockUpgradeAwaiter>\nSharedMutexFair::co_lock_upgrade() noexcept {\n  return LockOperation<LockUpgradeAwaiter>{*this};\n}\n\ninline SharedMutexFair::LockOperation<SharedMutexFair::ScopedLockUpgradeAwaiter>\nSharedMutexFair::co_scoped_lock_upgrade() noexcept {\n  return LockOperation<ScopedLockUpgradeAwaiter>{*this};\n}\n\ninline SharedMutexFair::LockOperation<\n    SharedMutexFair::UnlockUpgradeAndLockAwaiter>\nSharedMutexFair::co_unlock_upgrade_and_lock() noexcept {\n  return LockOperation<UnlockUpgradeAndLockAwaiter>{*this};\n}\n\ninline SharedMutexFair::LockOperation<\n    SharedMutexFair::ScopedUnlockUpgradeAndLockAwaiter>\nSharedMutexFair::co_scoped_unlock_upgrade_and_lock() noexcept {\n  return LockOperation<ScopedUnlockUpgradeAndLockAwaiter>{*this};\n}\n\n// The default SharedMutex is SharedMutexFair.\nusing SharedMutex = SharedMutexFair;\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/SharedPromise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Likely.h>\n#include <folly/Synchronized.h>\n#include <folly/Utility.h>\n#include <folly/coro/Promise.h>\n#include <folly/futures/Promise.h>\n#include <folly/small_vector.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\n/**\n * SharedPromise is a simple wrapper around folly::coro::Promise and\n * folly::coro::Future that allows for fetching cancellable and awaitable\n * futures from a single promise.\n *\n * It has the same behavior as folly::SharedPromise<>.  This includes the\n * difference in behavior of folly::SharedPromise and folly::Promise with\n * regards to invalid promise exceptions -- when SharedPromise<> is\n * moved from, calling setValue(), setTry(), or setException() don't result in\n * a PromiseInvalid exception.\n */\ntemplate <typename T>\nclass SharedPromise {\n  using TryType = Try<lift_unit_t<T>>;\n\n public:\n  /**\n   * Constructors have behavior identical to folly::SharedPromise.\n   */\n  SharedPromise() = default;\n  SharedPromise(SharedPromise&&) noexcept;\n  SharedPromise& operator=(SharedPromise&&) noexcept;\n  SharedPromise(const SharedPromise&) = delete;\n  SharedPromise& operator=(const SharedPromise&) = delete;\n\n  /**\n   * Returns a future that is fulfilled when the user sets a value on the\n   * promise.  Because this is a coro::Future, it supports cancellation.\n   */\n  folly::coro::Future<T> getFuture() const;\n\n  /**\n   * Returns the number of futures associated with the SharedPromise.\n   */\n  std::size_t size() const;\n\n  /**\n   * Returns true if the promise has either a value or an exception set.\n   */\n  bool isFulfilled() const;\n\n  /*\n   * Returns either an optional holding the result or an empty optional\n   * depending on whether or not (respectively) the promise has been\n   * fulfilled (i.e., `isFulfilled() == true`).\n   */\n  std::optional<TryType> poll() const;\n\n  /**\n   * Sets an exception in the promise.\n   */\n  void setException(folly::exception_wrapper&&);\n\n  /**\n   * Sets a value in the promise.\n   */\n  template <typename U = T>\n  void setValue(U&&);\n  template <typename U = T, typename = std::enable_if_t<std::is_void_v<U>>>\n  void setValue();\n\n  /**\n   * Sets a folly::Try object in the promise.\n   */\n  void setTry(TryType&&);\n\n private:\n  struct State {\n    TryType result;\n    folly::small_vector<folly::coro::Promise<T>> promises;\n  };\n\n  static bool isFulfilled(const State&);\n  static void setTry(State&, TryType&&);\n\n  mutable folly::Synchronized<State> state_;\n};\n\ntemplate <typename T>\nSharedPromise<T>::SharedPromise(SharedPromise&& other) noexcept\n    : state_{std::exchange(*other.state_.wlock(), {})} {}\n\ntemplate <typename T>\nSharedPromise<T>& SharedPromise<T>::operator=(SharedPromise&& other) noexcept {\n  if (FOLLY_LIKELY(this != &other)) {\n    synchronized(\n        [](auto self, auto other) { *self = std::exchange(*other, {}); },\n        wlock(state_),\n        wlock(other.state_));\n  }\n  return *this;\n}\n\ntemplate <typename T>\nfolly::coro::Future<T> SharedPromise<T>::getFuture() const {\n  return state_.withWLock([&](auto& state) {\n    // if the promise already has a value, then we just return a ready future\n    if (isFulfilled(state)) {\n      if constexpr (std::is_void_v<T>) {\n        return state.result.hasValue()\n            ? folly::coro::makeFuture()\n            : folly::coro::makeFuture<void>(\n                  folly::copy(state.result.exception()));\n      } else {\n        return state.result.hasValue()\n            ? folly::coro::makeFuture<T>(folly::copy(state.result.value()))\n            : folly::coro::makeFuture<T>(folly::copy(state.result.exception()));\n      }\n    }\n\n    auto [promise, future] = folly::coro::makePromiseContract<T>();\n    state.promises.push_back(std::move(promise));\n    return std::move(future);\n  });\n}\n\ntemplate <typename T>\nstd::size_t SharedPromise<T>::size() const {\n  return state_.withRLock([](auto& state) { return state.promises.size(); });\n}\n\ntemplate <typename T>\nbool SharedPromise<T>::isFulfilled() const {\n  return state_.withRLock([](auto& state) { return isFulfilled(state); });\n}\n\ntemplate <typename T>\nauto SharedPromise<T>::poll() const -> std::optional<TryType> {\n  return state_.withRLock([](const auto& state) {\n    return isFulfilled(state) ? std::make_optional(state.result) : std::nullopt;\n  });\n}\n\ntemplate <typename T>\nvoid SharedPromise<T>::setException(folly::exception_wrapper&& exception) {\n  state_.withWLock([&](auto& state) {\n    setTry(state, TryType{std::move(exception)});\n  });\n}\n\ntemplate <typename T>\ntemplate <typename U>\nvoid SharedPromise<T>::setValue(U&& input) {\n  state_.withWLock([&](auto& state) {\n    setTry(state, TryType{std::in_place, std::forward<U>(input)});\n  });\n}\n\ntemplate <typename T>\ntemplate <typename U, typename>\nvoid SharedPromise<T>::setValue() {\n  setTry(TryType{unit});\n}\n\ntemplate <typename T>\nvoid SharedPromise<T>::setTry(TryType&& result) {\n  state_.withWLock([&](auto& state) { setTry(state, std::move(result)); });\n}\n\ntemplate <typename T>\nbool SharedPromise<T>::isFulfilled(const SharedPromise<T>::State& state) {\n  return state.result.hasException() || state.result.hasValue();\n}\n\ntemplate <typename T>\nvoid SharedPromise<T>::setTry(\n    SharedPromise<T>::State& state, TryType&& result) {\n  if (isFulfilled(state)) {\n    throw_exception<PromiseAlreadySatisfied>();\n  }\n\n  auto promises = std::exchange(state.promises, {});\n  for (auto& promise : promises) {\n    promise.setResult(folly::copy(result));\n  }\n\n  state.result = std::move(result);\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/Sleep-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/FutureUtil.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\ninline Task<void> sleep(HighResDuration d, Timekeeper* tk) {\n  //  using via with the current executor is observed to deadlock in some cases,\n  //  so convert to future without via and thereby bypass conversion using via\n  //  in the overload of toTaskInterruptOnCancel taking semi-future; woroks only\n  //  since sleep() returns a semi-future without any deferred work attached\n  auto f = folly::futures::sleep(d, tk).toUnsafeFuture();\n  co_await co_nothrow(toTaskInterruptOnCancel(std::move(f)));\n}\n\ninline Task<void> sleepReturnEarlyOnCancel(HighResDuration d, Timekeeper* tk) {\n  auto result = co_await co_awaitTry(sleep(d, tk));\n  if (result.hasException<OperationCancelled>()) {\n    co_return;\n  }\n  co_yield co_result(std::move(result));\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Sleep.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n#include <folly/futures/Future.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n/// Return a task that, when awaited, will sleep for the specified duration.\n///\n/// Throws folly::OperationCancelled if cancellation is requested on the\n/// awaiting coroutine's associated CancellationToken.\nTask<void> sleep(HighResDuration d, Timekeeper* tk = nullptr);\n\n/// Return a task that, when awaited, will sleep for the specified duration.\n///\n/// May complete sooner that the specified duration if cancellation is requested\n/// on the awaiting coroutine's associated CancellationToken.\nTask<void> sleepReturnEarlyOnCancel(\n    HighResDuration d, Timekeeper* tk = nullptr);\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Sleep-inl.h>\n"
  },
  {
    "path": "folly/coro/SmallUnboundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/Task.h>\n#include <folly/experimental/channels/detail/AtomicQueue.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\ntemplate <bool UseMutex>\nstruct SmallUnboundedQueueBase {\n  auto co_scoped_lock() { return ready_awaitable(true); }\n};\ntemplate <>\nstruct SmallUnboundedQueueBase<true> {\n  auto co_scoped_lock() { return mutex_.co_scoped_lock(); }\n  folly::coro::Mutex mutex_;\n};\n} // namespace detail\n\n// Alternative to coro::UnboundedQueue with much smaller memory size when empty\n// but lower throughput.\n// Substantially worse in multi-consumer case.\n// Only supports enqueue(T) and dequeue().\n\ntemplate <typename T, bool SingleProducer = false, bool SingleConsumer = false>\nclass SmallUnboundedQueue : detail::SmallUnboundedQueueBase<!SingleConsumer> {\n  struct Consumer {\n    void consume() { baton.post(); }\n    void canceled() { std::terminate(); }\n    folly::coro::Baton baton;\n  };\n\n public:\n  ~SmallUnboundedQueue() { queue_.close(); }\n\n  template <typename U = T>\n  void enqueue(U&& val) {\n    queue_.push(T(std::forward<U>(val)));\n  }\n\n  folly::coro::Task<T> dequeue() {\n    [[maybe_unused]] auto maybeLock = co_await this->co_scoped_lock();\n    if (buffer_.empty()) {\n      Consumer c;\n      if (queue_.wait(&c)) {\n        bool cancelled = false;\n        CancellationCallback cb(co_await co_current_cancellation_token, [&] {\n          if (queue_.cancelCallback()) {\n            cancelled = true;\n            c.baton.post();\n          }\n        });\n        co_await c.baton;\n        if (cancelled) {\n          co_yield co_stopped_may_throw;\n        }\n      }\n      buffer_ = queue_.getMessages();\n      DCHECK(!buffer_.empty());\n    }\n    SCOPE_EXIT {\n      buffer_.pop();\n    };\n    co_return std::move(buffer_.front());\n  }\n\n private:\n  folly::channels::detail::AtomicQueue<Consumer, T> queue_;\n  folly::channels::detail::Queue<T> buffer_;\n};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Synchronized.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <mutex>\n#include <utility>\n\n#include <folly/Utility.h>\n#include <folly/coro/SharedLock.h>\n#include <folly/coro/SharedMutex.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Traits.h>\n\nnamespace folly::coro {\n\nnamespace detail {\n\ntemplate <typename CoroMutexType>\nstruct SynchronizedMutexTraits;\n\ntemplate <>\nstruct SynchronizedMutexTraits<SharedMutexFair> {\n  using CoroMutex = SharedMutexFair;\n  using ReadLock = SharedLock<CoroMutex>;\n  using WriteLock = std::unique_lock<CoroMutex>;\n\n  static inline auto co_readLock(CoroMutex& mutex) {\n    return mutex.co_scoped_lock_shared();\n  }\n\n  static inline ReadLock tryReadLock(CoroMutex& mutex) noexcept(\n      noexcept(ReadLock(mutex, std::try_to_lock))) {\n    return ReadLock(mutex, std::try_to_lock);\n  }\n\n  static inline auto co_writeLock(CoroMutex& mutex) {\n    return mutex.co_scoped_lock();\n  }\n\n  static inline auto tryWriteLock(CoroMutex& mutex) noexcept(\n      noexcept(WriteLock(mutex, std::try_to_lock))) {\n    return WriteLock(mutex, std::try_to_lock);\n  }\n\n  static inline void unlock(ReadLock& lock) noexcept(noexcept(lock.unlock())) {\n    lock.unlock();\n  }\n\n  static inline void unlock(WriteLock& lock) noexcept(noexcept(lock.unlock())) {\n    lock.unlock();\n  }\n\n  static inline auto ownsLock(const ReadLock& lock) noexcept(\n      noexcept(lock.owns_lock())) {\n    return lock.owns_lock();\n  }\n\n  static inline auto ownsLock(const WriteLock& lock) noexcept(\n      noexcept(lock.owns_lock())) {\n    return lock.owns_lock();\n  }\n};\n\n} // namespace detail\n\n/**\n * This class is an adaptation of the folly::Synchronized class but is designed\n * to work with coro-compatible mutexes like coro::SharedMutexFair instead.\n *\n * In practice what this means is\n * that we can co_await gaining the read/write lock rather than blocking whilst\n * acquiring it.\n *\n * The API is not a complete clone of everything that folly::Synchronized\n * supports but is instead the minimum of what we need. Ultimately this classes\n * main job is to abstract away gaining the locks.\n */\ntemplate <\n    typename Inner,\n    typename CoroMutexType = SharedMutexFair,\n    typename CoroMutexTraits = detail::SynchronizedMutexTraits<CoroMutexType>>\nclass Synchronized : public NonCopyableNonMovable {\n public:\n  using Traits = CoroMutexTraits;\n  using CoroMutex = typename Traits::CoroMutex;\n  using ReadLock = typename Traits::ReadLock;\n  using WriteLock = typename Traits::WriteLock;\n\n  Synchronized() noexcept(noexcept(CoroMutex{}) && noexcept(Inner{})) = default;\n\n  explicit Synchronized(const Inner& rhs) noexcept(noexcept(Inner(rhs)))\n      : inner_(rhs) {}\n\n  explicit Synchronized(Inner&& rhs) noexcept(noexcept(Inner(std::move(rhs))))\n      : inner_(std::move(rhs)) {}\n\n  template <typename... Args>\n  explicit Synchronized(std::in_place_t, Args&&... args)\n      : inner_(std::forward<Args>(args)...) {}\n\n  /**\n   * A RAII wrapper around a pointer to the underlying object together with\n   * a lock on the underlying mutex.\n   *\n   * If acquired with a try-lock style method, you must check the boolean\n   * value of the locked pointer before dereferencing it.\n   */\n  template <typename ValueType, typename LockType>\n  class GenericLockedPtr : public MoveOnly {\n   public:\n    GenericLockedPtr(GenericLockedPtr&& other) noexcept(\n        noexcept(LockType(std::move(other.lock_))))\n        : lock_(std::move(other.lock_)),\n          ptr_(std::exchange(other.ptr_, nullptr)) {}\n\n    GenericLockedPtr& operator=(GenericLockedPtr&& other) noexcept(\n        noexcept(lock_ = std::move(other.lock_))) {\n      if (this != &other) {\n        lock_ = std::move(other.lock_);\n        ptr_ = std::exchange(other.ptr_, nullptr);\n      }\n      return *this;\n    }\n\n    ValueType* operator->() const noexcept {\n      DCHECK_NE(ptr_, nullptr);\n      return ptr_;\n    }\n\n    ValueType& operator*() const noexcept {\n      DCHECK_NE(ptr_, nullptr);\n      return *ptr_;\n    }\n\n    void unlock() {\n      DCHECK_NE(ptr_, nullptr);\n      ptr_ = nullptr;\n      Traits::unlock(lock_);\n    }\n\n    explicit operator bool() const noexcept { return Traits::ownsLock(lock_); }\n\n   private:\n    friend class Synchronized;\n    explicit GenericLockedPtr(LockType&& lock, ValueType* ptr)\n        : lock_(std::move(lock)), ptr_(ptr) {}\n\n    LockType lock_;\n    ValueType* ptr_ = nullptr;\n  };\n\n  using ReadLockedPtr = GenericLockedPtr<const Inner, ReadLock>;\n  using WriteLockedPtr = GenericLockedPtr<Inner, WriteLock>;\n\n  Task<WriteLockedPtr> wLock() {\n    auto lock = co_await Traits::co_writeLock(mutex_);\n    co_return WriteLockedPtr{std::move(lock), &inner_};\n  }\n\n  Task<ReadLockedPtr> rLock() const {\n    auto lock = co_await Traits::co_readLock(mutex_);\n    co_return ReadLockedPtr{std::move(lock), &inner_};\n  }\n\n  ReadLockedPtr tryRLock() const {\n    auto lock = Traits::tryReadLock(mutex_);\n    auto* ptr = Traits::ownsLock(lock) ? &inner_ : nullptr;\n    return ReadLockedPtr{std::move(lock), ptr};\n  }\n\n  WriteLockedPtr tryWLock() {\n    auto lock = WriteLock{mutex_, std::try_to_lock};\n    auto* ptr = Traits::ownsLock(lock) ? &inner_ : nullptr;\n    return WriteLockedPtr{std::move(lock), ptr};\n  }\n\n  template <typename FuncT>\n  using rlock_result_t = invoke_result_t<FuncT, ReadLockedPtr>;\n\n  template <typename FuncT>\n  using wlock_result_t = invoke_result_t<FuncT, WriteLockedPtr>;\n\n  template <typename FuncT, typename ReturnT = rlock_result_t<FuncT>>\n  typename std::enable_if<!is_semi_awaitable_v<ReturnT>, Task<ReturnT>>::type\n  withRLock(FuncT func) const {\n    auto lock = co_await Traits::co_readLock(mutex_);\n    co_return func(ReadLockedPtr{std::move(lock), &inner_});\n  }\n\n  template <typename FuncT, typename ReturnT = rlock_result_t<FuncT>>\n  typename std::enable_if<\n      is_semi_awaitable_v<ReturnT>,\n      Task<semi_await_result_t<ReturnT>>>::type\n  withRLock(FuncT func) const {\n    auto lock = co_await Traits::co_readLock(mutex_);\n    co_return co_await func(ReadLockedPtr{std::move(lock), &inner_});\n  }\n\n  template <typename FuncT, typename ReturnT = wlock_result_t<FuncT>>\n  typename std::enable_if<!is_semi_awaitable_v<ReturnT>, Task<ReturnT>>::type\n  withWLock(FuncT func) {\n    auto lock = co_await Traits::co_writeLock(mutex_);\n    co_return func(WriteLockedPtr{std::move(lock), &inner_});\n  }\n\n  template <typename FuncT, typename ReturnT = wlock_result_t<FuncT>>\n  typename std::enable_if<\n      is_semi_awaitable_v<ReturnT>,\n      Task<semi_await_result_t<ReturnT>>>::type\n  withWLock(FuncT func) {\n    auto lock = co_await Traits::co_writeLock(mutex_);\n    co_return co_await func(WriteLockedPtr{std::move(lock), &inner_});\n  }\n\n  /**\n   * Temporarlily locks both objects and swaps their underlying data.\n   *\n   * Mimics the behaviour of folly::Synchronized in that we return early if you\n   * try to swap with itself and gains locks in ascending memory order to\n   * prevent deadlocks.\n   */\n  Task<void> swap(Synchronized& rhs) {\n    if (this == &rhs) {\n      co_return;\n    }\n\n    // Can't compare pointers for inequality with operator> because it's\n    // unspecified behavior unless they share provenance, see:\n    // - https://en.wikipedia.org/wiki/Unspecified_behavior,\n    // - https://en.cppreference.com/w/cpp/language/operator_comparison.\n    if (std::greater<>()(this, &rhs)) {\n      co_return co_await rhs.swap(*this);\n    }\n\n    auto guard1 = co_await wLock();\n    auto guard2 = co_await rhs.wLock();\n\n    using std::swap;\n    swap(inner_, rhs.inner_);\n\n    co_return;\n  }\n\n  Task<Inner> copy() const {\n    auto lock = co_await Traits::co_readLock(mutex_);\n    Inner res = folly::copy(inner_);\n    co_return res;\n  }\n\n  Task<void> swap(Inner& newInner) {\n    auto lock = co_await Traits::co_writeLock(mutex_);\n\n    using std::swap;\n    swap(inner_, newInner);\n  }\n\n private:\n  mutable CoroMutex mutex_;\n  Inner inner_;\n};\n\n} // namespace folly::coro\n"
  },
  {
    "path": "folly/coro/Task.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_coro_task\n//\n\n#pragma once\n\n#include <exception>\n#include <type_traits>\n\n#include <glog/logging.h>\n\n#include <folly/CancellationToken.h>\n#include <folly/DefaultKeepAliveExecutor.h>\n#include <folly/Executor.h>\n#include <folly/GLog.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/Try.h>\n#include <folly/coro/BasePromise.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Result.h>\n#include <folly/coro/ScopeExit.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/coro/detail/InlineTask.h>\n#include <folly/coro/detail/Malloc.h>\n#include <folly/coro/detail/Traits.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/Request.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/MustUseImmediately.h>\n#include <folly/lang/SafeAlias-fwd.h>\n#include <folly/result/result.h>\n#include <folly/result/try.h>\n#include <folly/tracing/AsyncStack.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\ntemplate <typename T = void>\nclass Task;\n\ntemplate <typename T = void>\nclass TaskWithExecutor;\n\nnamespace detail {\n\nclass TaskPromiseBase;\n\nclass TaskPromisePrivate {\n private:\n  friend TaskPromiseBase;\n  TaskPromisePrivate() = default;\n};\n\nclass TaskPromiseBase : public BasePromise<> {\n  static TaskPromisePrivate privateTag() { return TaskPromisePrivate{}; }\n\n protected:\n  class FinalAwaiter {\n   public:\n    bool await_ready() noexcept { return false; }\n\n    template <typename Promise>\n    coroutine_handle<> await_suspend_promise(Promise& promise) noexcept {\n      // If ScopeExitTask has been attached, then we expect that the\n      // ScopeExitTask will handle the lifetime of the async stack. See\n      // ScopeExitTaskPromise's FinalAwaiter for more details.\n      //\n      // This is a bit untidy, and hopefully something we can replace with\n      // a virtual wrapper over coroutine_handle that handles the pop for us.\n      if (promise.scopeExitRef(privateTag())) {\n        promise.scopeExitRef(privateTag())\n            .promise()\n            .setContext(\n                promise.continuationRef(privateTag()),\n                &promise.getAsyncFrame(),\n                promise.executorRef(privateTag()).get_alias(),\n                promise.result().hasException()\n                    ? promise.result().exception()\n                    : exception_wrapper{});\n        return promise.scopeExitRef(privateTag());\n      }\n\n      folly::popAsyncStackFrameCallee(promise.getAsyncFrame());\n      if (promise.result().hasException()) {\n        auto [handle, frame] =\n            promise.continuationRef(privateTag())\n                .getErrorHandle(promise.result().exception());\n        return handle.getHandle();\n      }\n      return promise.continuationRef(privateTag()).getHandle();\n    }\n\n    template <typename Promise>\n    FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES coroutine_handle<>\n    await_suspend(coroutine_handle<Promise> coro) noexcept {\n      return await_suspend_promise(coro.promise());\n    }\n\n    [[noreturn]] void await_resume() noexcept { folly::assume_unreachable(); }\n  };\n\n  TaskPromiseBase() noexcept = default;\n  ~TaskPromiseBase() = default;\n\n public:\n  static void* operator new(std::size_t size) {\n    return ::folly_coro_async_malloc(size);\n  }\n\n  static void operator delete(void* ptr, std::size_t size) {\n    ::folly_coro_async_free(ptr, size);\n  }\n\n  suspend_always initial_suspend() noexcept { return {}; }\n\n  FinalAwaiter final_suspend() noexcept { return {}; }\n\n  folly::Executor::KeepAlive<> getExecutor() const noexcept {\n    return executor_;\n  }\n\n  // These getters exist so that `FinalAwaiter` can interact with wrapped\n  // `TaskPromise`s, and not just `TaskPromiseBase` descendants.  We use a\n  // private tag to let `TaskWrapper` call them without becoming a `friend`.\n  auto& scopeExitRef(TaskPromisePrivate /*unused*/) { return scopeExit_; }\n  // FIXME: `result/coro.h` checks if this overload is callable to decide when\n  // something is a task-promise.  A second use-case would merit a concept.\n  auto& continuationRef(TaskPromisePrivate /*unused*/) { return continuation_; }\n  // Unlike `getExecutor()`, does not copy an atomic.\n  auto& executorRef(TaskPromisePrivate /*unused*/) { return executor_; }\n\n private:\n  template <typename>\n  friend class folly::coro::TaskWithExecutor;\n\n  template <typename>\n  friend class folly::coro::Task;\n\n  friend coroutine_handle<ScopeExitTaskPromiseBase> tag_invoke(\n      cpo_t<co_attachScopeExit> /*unused*/,\n      TaskPromiseBase& p,\n      coroutine_handle<ScopeExitTaskPromiseBase> scopeExit) noexcept {\n    return std::exchange(p.scopeExit_, scopeExit);\n  }\n\n  // From the base: continuation_, asyncFrame_, executor_, cancelToken_,\n  // hasCancelTokenOverride_\n  coroutine_handle<ScopeExitTaskPromiseBase> scopeExit_;\n};\n\n// Separate from `TaskPromiseBase` so the compiler has less to specialize.\ntemplate <typename Promise, typename T>\nclass TaskPromiseCrtpBase\n    : public TaskPromiseBase,\n      public ExtendedCoroutinePromiseCrtp<Promise> {\n public:\n  using StorageType = detail::lift_lvalue_reference_t<T>;\n\n  Task<T> get_return_object() noexcept;\n\n  void unhandled_exception() noexcept {\n    result_.emplaceException(exception_wrapper{current_exception()});\n  }\n\n  Try<StorageType>& result() { return result_; }\n\n  auto yield_value(co_error ex) {\n    result_.emplaceException(std::move(ex.exception()));\n    return final_suspend();\n  }\n\n  auto yield_value(co_result<StorageType>&& result) {\n    result_ = std::move(result.result());\n    return final_suspend();\n  }\n\n  using BasePromise<>::await_transform;\n\n  auto await_transform(co_safe_point_t /*unused*/) noexcept {\n    return do_safe_point<FinalAwaiter>(*this);\n  }\n\n  // Unlike `getErrorHandleUncheckedImpl`, checks the type of `me`.\n  static std::optional<ExtendedCoroutineHandle::ErrorHandle> getErrorHandleImpl(\n      Promise& me, exception_wrapper& ex) {\n    return getErrorHandleUncheckedImpl(me, ex);\n  }\n\n protected:\n  TaskPromiseCrtpBase() noexcept = default;\n  ~TaskPromiseCrtpBase() = default;\n\n  Try<StorageType> result_;\n};\n\ntemplate <typename T>\nclass TaskPromise final : public TaskPromiseCrtpBase<TaskPromise<T>, T> {\n public:\n  static_assert(\n      !std::is_rvalue_reference_v<T>,\n      \"Task<T&&> is not supported. \"\n      \"Consider using Task<T> or Task<std::unique_ptr<T>> instead.\");\n  friend class TaskPromiseBase;\n\n  using StorageType =\n      typename TaskPromiseCrtpBase<TaskPromise<T>, T>::StorageType;\n\n  TaskPromise() noexcept = default;\n\n  template <typename U = T>\n  void return_value(U&& value) {\n    if constexpr (std::is_same_v<remove_cvref_t<U>, Try<StorageType>>) {\n      DCHECK(value.hasValue() || (value.hasException() && value.exception()));\n      this->result_ = static_cast<U&&>(value);\n    } else if constexpr (\n        std::is_same_v<remove_cvref_t<U>, Try<void>> &&\n        std::is_same_v<remove_cvref_t<T>, Unit>) {\n      // special-case to make task -> semifuture -> task preserve void type\n      DCHECK(value.hasValue() || (value.hasException() && value.exception()));\n      this->result_ = static_cast<Try<Unit>>(static_cast<U&&>(value));\n    } else {\n      static_assert(\n          std::is_convertible<U&&, StorageType>::value,\n          \"cannot convert return value to type T\");\n      this->result_.emplace(static_cast<U&&>(value));\n    }\n  }\n};\n\ntemplate <>\nclass TaskPromise<void> final\n    : public TaskPromiseCrtpBase<TaskPromise<void>, void> {\n public:\n  friend class TaskPromiseBase;\n\n  using StorageType = void;\n\n  TaskPromise() noexcept = default;\n\n  void return_void() noexcept { this->result_.emplace(); }\n\n  using TaskPromiseCrtpBase<TaskPromise<void>, void>::yield_value;\n\n  auto yield_value(co_result<Unit>&& result) {\n    this->result_ = std::move(result.result());\n    return final_suspend();\n  }\n};\n\nnamespace adl {\n// ADL should prefer your `friend co_withExecutor` over this dummy overload.\nvoid co_withExecutor();\n// This CPO deliberately does NOT use `tag_invoke`, but rather reuses the\n// `co_withExecutor` name as the ADL implementation, just like `co_viaIfAsync`.\n// The reason is that `tag_invoke()` would plumb through `Awaitable&&` instead\n// of `Awaitable`, but `folly::ext::must_use_immediately_v` types require\n// by-value.\nstruct WithExecutorFunction {\n  template <typename Awaitable>\n  // Pass `awaitable` by-value, since `&&` would break immediate types\n  auto operator()(Executor::KeepAlive<> executor, Awaitable awaitable) const\n      FOLLY_DETAIL_FORWARD_BODY(co_withExecutor(\n          std::move(executor),\n          // NOLINTNEXTLINE(facebook-folly-coro-temporary-by-ref)\n          folly::ext::must_use_immediately_unsafe_mover(\n              std::move(awaitable))()))\n};\n} // namespace adl\n\n} // namespace detail\n\n// Semi-awaitables like `Task` should use this CPO to attach executors:\n//   auto taskWithExec = co_withExecutor(std::move(exec), std::move(task));\nFOLLY_DEFINE_CPO(detail::adl::WithExecutorFunction, co_withExecutor)\n\n/// Represents an allocated but not yet started coroutine that has already\n/// been bound to an executor.\n///\n/// This task, when co_awaited, will launch the task on the bound executor\n/// and will resume the awaiting coroutine on the bound executor when it\n/// completes.\n///\n/// More information on how to use this is available at folly::coro::Task.\ntemplate <typename T>\nclass [[nodiscard]] TaskWithExecutor {\n  using handle_t = coroutine_handle<detail::TaskPromise<T>>;\n  using StorageType = typename detail::TaskPromise<T>::StorageType;\n\n public:\n  /// @private\n  ~TaskWithExecutor() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  TaskWithExecutor(TaskWithExecutor&& t) noexcept\n      : coro_(std::exchange(t.coro_, {})) {}\n\n  TaskWithExecutor& operator=(TaskWithExecutor t) noexcept {\n    swap(t);\n    return *this;\n  }\n  /// Returns the executor that the task is bound to\n  folly::Executor* executor() const noexcept {\n    return coro_.promise().executor_.get();\n  }\n\n  void swap(TaskWithExecutor& t) noexcept { std::swap(coro_, t.coro_); }\n\n  /// Start eager execution of this task.\n  ///\n  /// This starts execution of the Task on the bound executor.\n  /// @returns folly::SemiFuture<T> that will complete with the result.\n  FOLLY_NOINLINE SemiFuture<lift_unit_t<StorageType>> start() && {\n    folly::Promise<lift_unit_t<StorageType>> p;\n\n    auto sf = p.getSemiFuture();\n\n    std::move(*this).startImpl(\n        [promise = std::move(p)](Try<StorageType>&& result) mutable {\n          promise.setTry(std::move(result));\n        },\n        folly::CancellationToken{},\n        FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n\n    return sf;\n  }\n\n  /// Start eager execution of the task and call the passed callback on\n  /// completion\n  ///\n  /// This starts execution of the Task on the bound executor, and call the\n  /// passed callback upon completion. The callback takes a Try<T> which\n  /// represents either th value returned by the Task on success or an\n  /// exception thrown by the Task\n  /// @param tryCallback a function that takes in a Try<T>\n  /// @param cancelToken a CancelationToken object\n  template <typename F>\n  FOLLY_NOINLINE void start(\n      F&& tryCallback, folly::CancellationToken cancelToken = {}) && {\n    std::move(*this).startImpl(\n        static_cast<F&&>(tryCallback),\n        std::move(cancelToken),\n        FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  }\n\n  /// Start eager execution of this task on this thread.\n  ///\n  /// Assumes the current thread is already on the executor associated with the\n  /// Task. Refer to TaskWithExecuter::start(F&& tryCallback,\n  /// folly::CancellationToken cancelToken = {}) for more information.\n  template <typename F>\n  FOLLY_NOINLINE void startInlineUnsafe(\n      F&& tryCallback, folly::CancellationToken cancelToken = {}) && {\n    std::move(*this).startInlineImpl(\n        static_cast<F&&>(tryCallback),\n        std::move(cancelToken),\n        FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  }\n\n  /// Start eager execution of this task on this thread.\n  ///\n  /// Assumes the current thread is already on the executor associated with the\n  /// Task. Refer to TaskWithExecuter::start() for more information.\n  FOLLY_NOINLINE SemiFuture<lift_unit_t<StorageType>> startInlineUnsafe() && {\n    folly::Promise<lift_unit_t<StorageType>> p;\n\n    auto sf = p.getSemiFuture();\n\n    std::move(*this).startInlineImpl(\n        [promise = std::move(p)](Try<StorageType>&& result) mutable {\n          promise.setTry(std::move(result));\n        },\n        folly::CancellationToken{},\n        FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n\n    return sf;\n  }\n\n private:\n  template <typename F>\n  void startImpl(\n      F&& tryCallback,\n      folly::CancellationToken cancelToken,\n      void* returnAddress) && {\n    coro_.promise().setCancellationToken(std::move(cancelToken));\n    startImpl(std::move(*this), static_cast<F&&>(tryCallback))\n        .start(returnAddress);\n  }\n\n  template <typename F>\n  void startInlineImpl(\n      F&& tryCallback,\n      folly::CancellationToken cancelToken,\n      void* returnAddress) && {\n    coro_.promise().setCancellationToken(std::move(cancelToken));\n    // If the task replaces the request context and reaches a suspension point,\n    // it will not have a chance to restore the previous context before we\n    // return, so we need to ensure it is restored. This simulates starting the\n    // coroutine in an actual executor, which would wrap the task with a guard.\n    RequestContextSaverScopeGuard contextScope;\n    startInlineImpl(std::move(*this), static_cast<F&&>(tryCallback))\n        .start(returnAddress);\n  }\n\n  template <typename F>\n  detail::InlineTaskDetached startImpl(TaskWithExecutor task, F cb) {\n    try {\n      cb(co_await folly::coro::co_awaitTry(std::move(task)));\n    } catch (...) {\n      cb(Try<StorageType>(exception_wrapper(current_exception())));\n    }\n  }\n\n  template <typename F>\n  detail::InlineTaskDetached startInlineImpl(TaskWithExecutor task, F cb) {\n    try {\n      cb(co_await InlineTryAwaitable{std::exchange(task.coro_, {})});\n    } catch (...) {\n      cb(Try<StorageType>(exception_wrapper(current_exception())));\n    }\n  }\n\n public:\n  class Awaiter {\n   public:\n    explicit Awaiter(handle_t coro) noexcept : coro_(coro) {}\n\n    Awaiter(Awaiter&& other) noexcept : coro_(std::exchange(other.coro_, {})) {}\n\n    ~Awaiter() {\n      if (coro_) {\n        coro_.destroy();\n      }\n    }\n\n    bool await_ready() const noexcept { return false; }\n\n    template <typename Promise>\n    FOLLY_NOINLINE void await_suspend(\n        coroutine_handle<Promise> continuation) noexcept {\n      DCHECK(coro_);\n      auto& promise = coro_.promise();\n      DCHECK(!promise.continuation_);\n      DCHECK(promise.executor_);\n      DCHECK(!dynamic_cast<folly::InlineExecutor*>(promise.executor_.get()))\n          << \"InlineExecutor is not safe and is not supported for coro::Task. \"\n          << \"If you need to run a task inline in a unit-test, you should use \"\n          << \"coro::blockingWait instead.\";\n      DCHECK(!dynamic_cast<folly::QueuedImmediateExecutor*>(\n          promise.executor_.get()))\n          << \"QueuedImmediateExecutor is not safe and is not supported for coro::Task. \"\n          << \"If you need to run a task inline in a unit-test, you should use \"\n          << \"coro::blockingWait instead.\";\n      if constexpr (kIsDebug) {\n        if (dynamic_cast<InlineLikeExecutor*>(promise.executor_.get())) {\n          FB_LOG_ONCE(ERROR)\n              << \"InlineLikeExecutor is not safe and is not supported for coro::Task. \"\n              << \"If you need to run a task inline in a unit-test, you should use \"\n              << \"coro::blockingWait or write your test using the CO_TEST* macros instead.\"\n              << \"If you are using folly::getCPUExecutor, switch to getGlobalCPUExecutor \"\n              << \"or be sure to call setCPUExecutor first.\";\n        }\n        if (dynamic_cast<folly::DefaultKeepAliveExecutor::WeakRefExecutor*>(\n                promise.executor_.get())) {\n          FB_LOG_ONCE(ERROR)\n              << \"You are scheduling a coro::Task on a weak executor. \"\n              << \"It is not supported, and can lead to memory leaks. \"\n              << \"Consider using CancellationToken instead.\";\n        }\n      }\n\n      auto& calleeFrame = promise.getAsyncFrame();\n      calleeFrame.setReturnAddress();\n\n      if constexpr (detail::promiseHasAsyncFrame_v<Promise>) {\n        auto& callerFrame = continuation.promise().getAsyncFrame();\n        calleeFrame.setParentFrame(callerFrame);\n        folly::deactivateAsyncStackFrame(callerFrame);\n      }\n\n      promise.continuation_ = continuation;\n      promise.executor_->add(\n          [coro = coro_, ctx = RequestContext::saveContext()]() mutable {\n            RequestContextScopeGuard contextScope{std::move(ctx)};\n            folly::resumeCoroutineWithNewAsyncStackRoot(coro);\n          });\n    }\n\n    T await_resume() {\n      DCHECK(coro_);\n      // Eagerly destroy the coroutine-frame once we have retrieved the result.\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return std::move(coro_.promise().result()).value();\n    }\n\n    folly::Try<StorageType> await_resume_try() noexcept(\n        std::is_nothrow_move_constructible_v<StorageType>) {\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return std::move(coro_.promise().result());\n    }\n\n#if FOLLY_HAS_RESULT\n    result<T> await_resume_result() noexcept(\n        std::is_nothrow_move_constructible_v<StorageType>) {\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return try_to_result(std::move(coro_.promise().result()));\n    }\n#endif\n\n   private:\n    handle_t coro_;\n  };\n\n  class InlineTryAwaitable {\n   public:\n    InlineTryAwaitable(handle_t coro) noexcept : coro_(coro) {}\n\n    InlineTryAwaitable(InlineTryAwaitable&& other) noexcept\n        : coro_(std::exchange(other.coro_, {})) {}\n\n    ~InlineTryAwaitable() {\n      if (coro_) {\n        coro_.destroy();\n      }\n    }\n\n    bool await_ready() noexcept { return false; }\n\n    template <typename Promise>\n    FOLLY_NOINLINE coroutine_handle<> await_suspend(\n        coroutine_handle<Promise> continuation) noexcept {\n      DCHECK(coro_);\n      auto& promise = coro_.promise();\n      DCHECK(!promise.continuation_);\n      DCHECK(promise.executor_);\n\n      promise.continuation_ = continuation;\n\n      auto& calleeFrame = promise.getAsyncFrame();\n      calleeFrame.setReturnAddress();\n\n      // This awaitable is only ever awaited from a DetachedInlineTask\n      // which is an async-stack-aware coroutine.\n      //\n      // Assume it has a .getAsyncFrame() and that this frame is currently\n      // active.\n      auto& callerFrame = continuation.promise().getAsyncFrame();\n      folly::pushAsyncStackFrameCallerCallee(callerFrame, calleeFrame);\n      return coro_;\n    }\n\n    folly::Try<StorageType> await_resume() {\n      DCHECK(coro_);\n      // Eagerly destroy the coroutine-frame once we have retrieved the result.\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return std::move(coro_.promise().result());\n    }\n\n   private:\n    friend InlineTryAwaitable tag_invoke(\n        cpo_t<co_withAsyncStack> /*unused*/,\n        InlineTryAwaitable&& awaitable) noexcept {\n      return std::move(awaitable);\n    }\n\n    handle_t coro_;\n  };\n\n public:\n  Awaiter operator co_await() && noexcept {\n    DCHECK(coro_);\n    return Awaiter{std::exchange(coro_, {})};\n  }\n\n  std::pair<Task<T>, Executor::KeepAlive<>> unwrap() && {\n    auto executor = std::move(coro_.promise().executor_);\n    Task<T> task{std::exchange(coro_, {})};\n    return {std::move(task), std::move(executor)};\n  }\n\n  friend ViaIfAsyncAwaitable<TaskWithExecutor> co_viaIfAsync(\n      Executor::KeepAlive<> executor,\n      TaskWithExecutor&& taskWithExecutor) noexcept {\n    auto [task, taskExecutor] = std::move(taskWithExecutor).unwrap();\n    return ViaIfAsyncAwaitable<TaskWithExecutor>(\n        std::move(executor),\n        co_withExecutor(std::move(taskExecutor), [](Task<T> t) -> Task<T> {\n          co_yield co_result(co_await co_awaitTry(std::move(t)));\n        }(std::move(task))));\n  }\n\n  friend TaskWithExecutor co_withCancellation(\n      folly::CancellationToken cancelToken, TaskWithExecutor&& task) noexcept {\n    DCHECK(task.coro_);\n    task.coro_.promise().setCancellationToken(std::move(cancelToken));\n    return std::move(task);\n  }\n\n  friend TaskWithExecutor tag_invoke(\n      cpo_t<co_withAsyncStack> /*unused*/, TaskWithExecutor&& task) noexcept {\n    return std::move(task);\n  }\n\n  using folly_private_task_without_executor_t = Task<T>;\n  // See comment in `Task`, or use `safe_task_with_executor` instead.\n  template <safe_alias>\n  using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;\n\n private:\n  friend class Task<T>;\n\n  explicit TaskWithExecutor(handle_t coro) noexcept : coro_(coro) {}\n\n  handle_t coro_;\n};\n\n// This macro makes it easier for `TaskWrapper.h` users to apply the correct\n// attributes for the wrapped `Task`s.\n#define FOLLY_CORO_TASK_ATTRS \\\n  [[nodiscard]] [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]\n\n/// Represents an allocated, but not-started coroutine, which is not yet\n/// been bound to an executor.\n///\n/// You can only co_await a Task from within another Task, in which case it\n/// is implicitly bound to the same executor as the parent Task.\n///\n/// Alternatively, you can explicitly provide an executor by calling\n/// `co_withExecutor(executor, task())`, which will return a not-yet-started\n/// `TaskWithExecutor` that can be `co_await`ed anywhere and that will\n/// automatically schedule the coroutine to start executing on the bound\n/// executor when it is `co_await`ed.\n///\n/// Within the body of a Task's coroutine, executor binding to the parent\n/// executor is maintained by implicitly transforming all 'co_await expr'\n/// expressions into `co_await co_viaIfAsync(parentExecutor, expr)' to ensure\n/// that the coroutine always resumes on the parent's executor.\n///\n/// The Task coroutine is RequestContext-aware\n/// and will capture the current RequestContext at the time the coroutine\n/// function is either awaited or explicitly started and will save/restore the\n/// current RequestContext whenever the coroutine suspends and resumes at a\n/// co_await expression.\n///\n/// More documentation on how to use coroutines is available at\n/// https://github.com/facebook/folly/blob/main/folly/coro/README.md\n///\n/// @refcode folly/docs/examples/folly/coro/Task.cpp\ntemplate <typename T>\nclass FOLLY_CORO_TASK_ATTRS Task {\n public:\n  using promise_type = detail::TaskPromise<T>;\n  using StorageType = typename promise_type::StorageType;\n\n private:\n  class Awaiter;\n  using handle_t = coroutine_handle<promise_type>;\n\n  void setExecutor(folly::Executor::KeepAlive<>&& e) noexcept {\n    DCHECK(coro_);\n    DCHECK(e);\n    coro_.promise().executor_ = std::move(e);\n  }\n\n  // `co_withExecutor` implementation detail -- this works around the fact that\n  // not all compilers consider the hidden friend `co_withExecutor` to be a\n  // friend of `TaskWithExecutor`, and I found no uniform way to add the\n  // friendship without making it non-hidden.  Try folding back into\n  // `co_withExecutor` in 2027 or so, to see if the old compiler issue is gone.\n  TaskWithExecutor<T> asTaskWithExecutor() && {\n    return TaskWithExecutor<T>{std::exchange(coro_, {})};\n  }\n\n public:\n  Task(const Task& t) = delete;\n\n  /// Create a Task, invalidating the original Task in the process.\n  Task(Task&& t) noexcept : coro_(std::exchange(t.coro_, {})) {}\n\n  /// @private\n  ~Task() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  Task& operator=(Task t) noexcept {\n    swap(t);\n    return *this;\n  }\n\n  void swap(Task& t) noexcept { std::swap(coro_, t.coro_); }\n\n  /// Specify the executor that this task should execute on:\n  ///   co_withExecutor(executor, std::move(task))\n  //\n  /// @param executor An Executor::KeepAlive object, which can be implicitly\n  /// constructed from Executor*\n  /// @returns a new TaskWithExecutor object, which represents the existing Task\n  /// bound to an executor\n  friend TaskWithExecutor<T> co_withExecutor(\n      Executor::KeepAlive<> executor, Task task) noexcept {\n    task.setExecutor(std::move(executor));\n    DCHECK(task.coro_);\n    return std::move(task).asTaskWithExecutor();\n  }\n  [[deprecated(\"Legacy form, prefer `co_withExecutor(exec, yourTask())`.\")]]\n  TaskWithExecutor<T> scheduleOn(Executor::KeepAlive<> executor) && noexcept {\n    return co_withExecutor(std::move(executor), std::move(*this));\n  }\n\n  /// Converts a Task into a SemiFuture object.\n  ///\n  /// The SemiFuture object is implicitly of type Semifuture<Try<T>>, where the\n  /// Try represents whether the execution of the converted Task succeeded and T\n  /// is the original task's result type.\n  /// @returns a SemiFuture object\n  FOLLY_NOINLINE\n  SemiFuture<folly::lift_unit_t<StorageType>> semi() && {\n    return makeSemiFuture().deferExTry(\n        [task = std::move(*this),\n         returnAddress = FOLLY_ASYNC_STACK_RETURN_ADDRESS()](\n            const Executor::KeepAlive<>& executor, Try<Unit>&&) mutable {\n          folly::Promise<lift_unit_t<StorageType>> p;\n\n          auto sf = p.getSemiFuture();\n\n          co_withExecutor(executor, std::move(task))\n              .startInlineImpl(\n                  [promise = std::move(p)](Try<StorageType>&& result) mutable {\n                    promise.setTry(std::move(result));\n                  },\n                  folly::CancellationToken{},\n                  returnAddress);\n\n          return sf;\n        });\n  }\n\n  friend auto co_viaIfAsync(\n      Executor::KeepAlive<> executor, Task<T>&& t) noexcept {\n    DCHECK(t.coro_);\n    // Child task inherits the awaiting task's executor\n    t.setExecutor(std::move(executor));\n    return Awaiter{std::exchange(t.coro_, {})};\n  }\n\n  friend Task co_withCancellation(\n      folly::CancellationToken cancelToken, Task&& task) noexcept {\n    DCHECK(task.coro_);\n    task.coro_.promise().setCancellationToken(std::move(cancelToken));\n    return std::move(task);\n  }\n\n  template <typename F, typename... A, typename F_, typename... A_>\n  friend Task tag_invoke(\n      tag_t<co_invoke_fn> /*unused*/,\n      tag_t<Task, F, A...> /*unused*/,\n      F_ f,\n      A_... a) {\n    co_yield co_result(\n        co_await co_awaitTry(\n            invoke(static_cast<F&&>(f), static_cast<A&&>(a)...)));\n  }\n\n  using PrivateAwaiterTypeForTests = Awaiter;\n  // Use `safe_task` instead of `Task` to move tasks into other safe coro APIs.\n  //\n  // User-facing stuff from `Task.h` can trivially include unsafe aliasing, the\n  // `folly::coro` docs include hundreds of words of pitfalls.  The intent here\n  // is to catch people accidentally passing `Task`s into safer primitives, and\n  // breaking their memory-safety guarantees.\n  template <safe_alias>\n  using folly_private_safe_alias_t = safe_alias_constant<safe_alias::unsafe>;\n\n private:\n  friend class detail::TaskPromiseBase;\n  friend class detail::TaskPromiseCrtpBase<detail::TaskPromise<T>, T>;\n  friend class TaskWithExecutor<T>;\n\n  class Awaiter {\n   public:\n    explicit Awaiter(handle_t coro) noexcept : coro_(coro) {}\n\n    Awaiter(Awaiter&& other) noexcept : coro_(std::exchange(other.coro_, {})) {}\n\n    Awaiter(const Awaiter&) = delete;\n\n    ~Awaiter() {\n      if (coro_) {\n        coro_.destroy();\n      }\n    }\n\n    bool await_ready() noexcept { return false; }\n\n    template <typename Promise>\n    FOLLY_NOINLINE auto await_suspend(\n        coroutine_handle<Promise> continuation) noexcept {\n      DCHECK(coro_);\n      auto& promise = coro_.promise();\n\n      promise.continuation_ = continuation;\n\n      auto& calleeFrame = promise.getAsyncFrame();\n      calleeFrame.setReturnAddress();\n\n      if constexpr (detail::promiseHasAsyncFrame_v<Promise>) {\n        auto& callerFrame = continuation.promise().getAsyncFrame();\n        folly::pushAsyncStackFrameCallerCallee(callerFrame, calleeFrame);\n        return coro_;\n      } else {\n        folly::resumeCoroutineWithNewAsyncStackRoot(coro_);\n        return;\n      }\n    }\n\n    T await_resume() {\n      DCHECK(coro_);\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return std::move(coro_.promise().result()).value();\n    }\n\n    folly::Try<StorageType> await_resume_try() noexcept(\n        std::is_nothrow_move_constructible_v<StorageType>) {\n      DCHECK(coro_);\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return std::move(coro_.promise().result());\n    }\n\n#if FOLLY_HAS_RESULT\n    result<T> await_resume_result() noexcept(\n        std::is_nothrow_move_constructible_v<StorageType>) {\n      DCHECK(coro_);\n      SCOPE_EXIT {\n        std::exchange(coro_, {}).destroy();\n      };\n      return try_to_result(std::move(coro_.promise().result()));\n    }\n#endif\n\n   private:\n    // This overload needed as Awaiter is returned from co_viaIfAsync() which is\n    // then passed into co_withAsyncStack().\n    friend Awaiter tag_invoke(\n        cpo_t<co_withAsyncStack> /*unused*/, Awaiter&& awaiter) noexcept {\n      return std::move(awaiter);\n    }\n\n    handle_t coro_;\n  };\n\n  Task(handle_t coro) noexcept : coro_(coro) {}\n\n  handle_t coro_;\n};\n\n/// Make a task that trivially returns a value.\n/// @param t value to be returned by the Task\ntemplate <class T>\nTask<T> makeTask(T t) {\n  co_return t;\n}\n\n/// Make a Task that trivially returns with no return value.\ninline Task<void> makeTask() {\n  co_return;\n}\n/// Same as makeTask(). See Unit\ninline Task<void> makeTask(Unit /*unused*/) {\n  co_return;\n}\n\n/// Make a Task that will trivially yield an Exception.\n/// @param ew an exception_wrapper object\ntemplate <class T>\nTask<T> makeErrorTask(exception_wrapper ew) {\n  co_yield co_error(std::move(ew));\n}\n\n/// Make a Task out of a Try.\n/// @tparam T the type of the value wrapped by the Try\n/// @param t the Try to convert into a Task\n/// @returns a Task that will yield the Try's value or exception.\ntemplate <class T>\nTask<drop_unit_t<T>> makeResultTask(Try<T> t) {\n  co_yield co_result(std::move(t));\n}\n\ntemplate <typename Promise, typename T>\ninline Task<T>\ndetail::TaskPromiseCrtpBase<Promise, T>::get_return_object() noexcept {\n  // Watch out: When used with `TaskWrapper`, this relies on \"practically safe\"\n  // UB wherein this handle is only valid because `TaskPromise` and the true\n  // \"wrapper promise\" of the wrapper coro coincide in layout exactly.\n  // Documented in `TaskPromiseWrapperBase::is_promise_type_punning_safe`.\n  return Task<T>{\n      coroutine_handle<Promise>::from_promise(*static_cast<Promise*>(this))};\n}\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/TaskWrapper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Task.h>\n#include <folly/lang/MustUseImmediately.h>\n\n/// `TaskWrapper.h` provides base classes for wrapping `folly::coro::Task` with\n/// custom functionality.  These work by composition, which avoids the pitfalls\n/// of inheritance -- your custom wrapper will not be \"is-a-Task\", and will not\n/// implicitly \"object slice\" to a `Task`.\n///\n/// The point of this header is to uniformly forward the large API surface of\n/// `Task`, `TaskWithExecutor`, and `TaskPromise`, leaving just the \"new logic\"\n/// in each wrapper's implementation.\n///\n///   - `TaskWrapperCrtp` makes your type (1)  a coroutine (`promise_type`)\n///     that can `co_await` other `folly::coro` objects.  (2) semi-awaitable by\n///     other `folly::coro` coroutines.  It has the following features:\n///        * `co_await`ability (using `co_viaIfAsync`)\n///        * Interoperates with `folly::coro` awaitable wrappers like\n///          `co_awaitTry` and `co_nothrow`.\n///        * `co_withCancellation` to add a cancellation token\n///        * `co_withExecutor` to add a cancellation token\n///        * Basic reflection via `folly/coro/Traits.h`\n///        * Empty base optimization for zero runtime overhead\n///\n///   - `TaskWithExecutorWrapperCrtp` is awaitable, but not a coroutine.  It\n///     has the same features, except for `co_withExecutor`.\n///\n/// ### WARNING: Do not blindly forward more APIs in `TaskWrapper.h`!\n///\n/// Several existing wrappers are immediately-awaitable (see\n/// `MustUseImmediately.h`).  For those tasks (e.g. `now_task`), API\n/// forwarding is risky:\n///   - Do NOT forward `semi()`, `start*()`, `unwrap()`, or other methods, or\n///     CPOs that take the awaitable by-reference.  All of those make it\n///     trivial to accidentally break the immediately-awaitable invariant, and\n///     cause lifetime bugs.\n///   - When forwarding an API, use either a static method or CPO.  Then,\n///     either ONLY take the awaitable by-value, or bifurcate the API on\n///     `folly::ext::must_use_immediately_v<Awaitable>`, grep for examples.\n///\n/// If you **have** to forward an unsafe API, here are some suggestions:\n///   - Only add them in your wrapper.\n///   - Add them via `UnsafeTaskWrapperCrtp` deriving from `TaskWrapperCrtp`.\n///   - Add boolean flags to the configuration struct, and gate the methods via\n///     `enable_if`.  NB: You probably cannot gate these on `Derived` **not**\n///     being must-use-immediately, since CRTP bases see an incomplete type.\n///\n/// ### WARNING: Beware of object slicing in \"unwrapping\" APIs\n///\n/// Start by reading \"A note on object slicing\" in `MustUseImmediately.h`.\n///\n/// If your wrapper is adding new members, or customizing object lifecycle\n/// (dtor / copy / move / assignment), then you must:\n///   - Write a custom `unsafe_mover()` as per `lang/MustUseImmediately.h`.\n///   - Overload the protected `unsafeTask()` and `unsafeTaskWithExecutor()` to\n///     reduce slicing risk.\n///   - Take care not to slice down to the `Crtp` bases.\n///\n/// ### How to implement a wrapper\n///\n/// First, read the WARNING sections above. Then, follow one of the \"Tiny\"\n/// examples in `TaskWrapperTest.cpp`. The important things are:\n///   - Actually read the \"object slicing\" warning above!\n///   - In most cases, you'll need to both implement a task, and customize its\n///     `TaskWithExecutorT`.  If you leave that as `coro::TaskWithExecutor`,\n///     some users will accidentally avoid your wrapper's effects.\n///   - Tag `YourTaskWithExecutor` with `[[nodiscard]]`.\n///   - Tag `YourTask` with the `FOLLY_CORO_TASK_ATTRS` attribute.  Caveat:\n///     This assumes that the coro's caller will outlive it.  That is true for\n///     `Task`, and almost certainly true of all sensible wrapper types.\n///   - Mark your wrappers `final` to discourage inheritance and object-slicing\n///     bugs.  They can still be wrapped recursively.\n///\n/// Future: Once this has a benchmark, see if `FOLLY_ALWAYS_INLINE` makes\n/// any difference on the wrapped functions (it shouldn't).\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro {\n\nnamespace detail {\n\ntemplate <typename Wrapper>\nusing task_wrapper_inner_semiawaitable_t =\n    typename Wrapper::folly_private_task_wrapper_inner_t;\n\ntemplate <typename SemiAwaitable, typename T>\ninline constexpr bool is_task_or_wrapper_v =\n    (!std::is_same_v<nonesuch, SemiAwaitable> && // Does not wrap Task\n     (std::is_same_v<SemiAwaitable, Task<T>> || // Wraps Task\n      is_task_or_wrapper_v<\n          detected_t<task_wrapper_inner_semiawaitable_t, SemiAwaitable>,\n          T>));\n\ntemplate <typename Wrapper>\nusing task_wrapper_inner_promise_t = typename Wrapper::TaskWrapperInnerPromise;\n\ntemplate <typename Promise, typename T>\ninline constexpr bool is_task_promise_or_wrapper_v =\n    (!std::is_same_v<nonesuch, Promise> && // Does not wrap TaskPromise\n     (std::is_same_v<Promise, TaskPromise<T>> || // Wraps TaskPromise\n      is_task_promise_or_wrapper_v<\n          detected_t<task_wrapper_inner_promise_t, Promise>,\n          T>));\n\ntemplate <typename T, typename WrapperTask, typename Promise>\nclass TaskPromiseWrapperBase {\n private:\n  // Detail of `is_promise_type_punning_safe`. Implementation notes:\n  //  - This needs a body because GCC doesn't want `d` referenced in a `->` type\n  //    signature.\n  //  - To use this with the cheaper-to-compile `FOLLY_DECLVAL`, which is\n  //    `nullptr`, this must be in an unevaluated context, since patently-null\n  //    static casts are special in that they discard offsets.  So, the below\n  //    equality would always be true during constant evaluation.\n  template <typename Me>\n  static consteval auto promise_at_offset0(Me me) {\n    return std::bool_constant<\n        static_cast<const void*>(&me) ==\n        static_cast<const void*>(&me.promise_)>{};\n  }\n\n  // CRITICAL SAFETY CHECK: `&promise_` must be the same as `this`, and both\n  // objects must have the same size.  This is required since some promise\n  // operations (notably `get_return_object()` need to obtain a\n  // `coroutine_handle<Promise>` to allow the wrapped coro not to know about\n  // the wrapper.  At the same time, the actual promise is\n  // `TaskPromiseWrapperBase`.  Punning promises & handles in this way is\n  // technically UB, but it's practically safe so long as the layouts of\n  // `Promise` and `TaskPromiseWrapperBase` are identical, which is what we\n  // verify here.\n  static consteval bool is_promise_type_punning_safe() {\n    return require_sizeof<Promise> == require_sizeof<TaskPromiseWrapperBase> &&\n        decltype(promise_at_offset0(\n            FOLLY_DECLVAL(TaskPromiseWrapperBase)))::value;\n  }\n\n protected:\n  static_assert(\n      is_task_or_wrapper_v<WrapperTask, T>,\n      \"SemiAwaitable must be a sequence of wrappers ending in Task<T>\");\n  static_assert(\n      is_task_promise_or_wrapper_v<Promise, T>,\n      \"Promise must be a sequence of wrappers ending in TaskPromise<T>\");\n\n  Promise promise_;\n\n  TaskPromiseWrapperBase() noexcept = default;\n  ~TaskPromiseWrapperBase() = default;\n\n public:\n  using TaskWrapperInnerPromise = Promise;\n\n  WrapperTask get_return_object() noexcept {\n    // CRITICAL: This assert justifies why it is practically safe to rely on\n    // the `from_promise` UB in `TaskPromiseCrtpBase::get_return_object`.\n    //\n    // PS The assert isn't at class scope, since the type would be incomplete.\n    static_assert(is_promise_type_punning_safe());\n    return WrapperTask{promise_.get_return_object()};\n  }\n\n  static void* operator new(std::size_t size) {\n    return ::folly_coro_async_malloc(size);\n  }\n  static void operator delete(void* ptr, std::size_t size) {\n    ::folly_coro_async_free(ptr, size);\n  }\n\n  auto initial_suspend() noexcept { return promise_.initial_suspend(); }\n  auto final_suspend() noexcept { return promise_.final_suspend(); }\n\n  template <\n      typename Awaitable,\n      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto await_transform(Awaitable&& what) {\n    return promise_.await_transform(std::forward<Awaitable>(what));\n  }\n  template <\n      typename Awaitable,\n      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto await_transform(Awaitable what) {\n    return promise_.await_transform(\n        folly::ext::must_use_immediately_unsafe_mover(std::move(what))());\n  }\n\n  auto yield_value(auto&& v)\n    requires requires { promise_.yield_value(std::forward<decltype(v)>(v)); }\n  {\n    return promise_.yield_value(std::forward<decltype(v)>(v));\n  }\n\n  void unhandled_exception() noexcept { promise_.unhandled_exception(); }\n\n  // These getters are all interposed for `TaskPromiseBase::FinalAwaiter`\n  decltype(auto) result() { return promise_.result(); }\n  decltype(auto) getAsyncFrame() { return promise_.getAsyncFrame(); }\n  auto& scopeExitRef(TaskPromisePrivate tag) {\n    return promise_.scopeExitRef(tag);\n  }\n  auto& continuationRef(TaskPromisePrivate tag) {\n    return promise_.continuationRef(tag);\n  }\n  auto& executorRef(TaskPromisePrivate tag) {\n    return promise_.executorRef(tag);\n  }\n\n  // These next two definitions forward `getErrorHandle` behavior to the\n  // innermost promise object.  See `ExtendedCoroutineHandle` for docs.\n\n  using use_extended_handle_concept = ExtendedCoroutineHandle::PrivateTag;\n\n  static ExtendedCoroutineHandle::PromiseBase* getPromiseBase(\n      ExtendedCoroutineHandle::PrivateTag priv, TaskPromiseWrapperBase* me) {\n    return Promise::getPromiseBase(priv, &me->promise_);\n  }\n};\n\ntemplate <typename T, typename WrapperTask, typename Promise>\nclass TaskPromiseWrapper\n    : public TaskPromiseWrapperBase<T, WrapperTask, Promise> {\n protected:\n  TaskPromiseWrapper() noexcept = default;\n  ~TaskPromiseWrapper() = default;\n\n public:\n  template <typename U = T> // see \"`co_return` with implicit ctor\" test\n  auto return_value(U&& value) {\n    static_assert( // See `is_promise_type_punning_safe` for rationale\n        require_sizeof<TaskPromiseWrapper> ==\n        require_sizeof<TaskPromiseWrapperBase<T, WrapperTask, Promise>>);\n    return this->promise_.return_value(std::forward<U>(value));\n  }\n};\n\ntemplate <typename WrapperTask, typename Promise>\nclass TaskPromiseWrapper<void, WrapperTask, Promise>\n    : public TaskPromiseWrapperBase<void, WrapperTask, Promise> {\n protected:\n  TaskPromiseWrapper() noexcept = default;\n  ~TaskPromiseWrapper() = default;\n\n public:\n  void return_void() noexcept {\n    static_assert( // See `is_promise_type_punning_safe` for rationale\n        require_sizeof<TaskPromiseWrapper> ==\n        require_sizeof<TaskPromiseWrapperBase<void, WrapperTask, Promise>>);\n    this->promise_.return_void();\n  }\n};\n\n// Mixin for TaskWrapper.h configs for `Task` & `TaskWithExecutor` types\nstruct DoesNotWrapAwaitable {\n  template <typename Awaitable>\n  static inline constexpr Awaitable&& wrapAwaitable(Awaitable&& awaitable) {\n    return static_cast<Awaitable&&>(awaitable);\n  }\n};\n\n} // namespace detail\n\n// IMPORTANT: Read \"Do not blindly forward more APIs\" in the file docblock.  In\n// a nutshell, adding methods, or by-ref CPOs, can compromise the safety of\n// immediately-awaitable wrappers, so DON'T DO THAT.\ntemplate <typename Derived, typename Cfg>\nclass TaskWrapperCrtp {\n public:\n  using promise_type = typename Cfg::PromiseT;\n\n  // Pass `tw` by-value, since `&&` would break immediately-awaitable types\n  friend typename Cfg::TaskWithExecutorT co_withExecutor(\n      Executor::KeepAlive<> executor, Derived tw) noexcept {\n    return typename Cfg::TaskWithExecutorT{\n        co_withExecutor(std::move(executor), std::move(tw).unwrapTask())};\n  }\n\n  // Pass `tw` by-value, since `&&` would break immediately-awaitable types\n  friend Derived co_withCancellation(\n      CancellationToken cancelToken, Derived tw) noexcept {\n    return Derived{co_withCancellation(\n        std::move(cancelToken), std::move(tw).unwrapTask())};\n  }\n\n  // Pass `tw` by-value, since `&&` would break immediately-awaitable types\n  // Has copy-pasta below in `TaskWithExecutorWrapperCrtp`.\n  friend auto co_viaIfAsync(\n      Executor::KeepAlive<> executor, Derived tw) noexcept {\n    return Cfg::wrapAwaitable(co_viaIfAsync(\n        std::move(executor),\n        folly::ext::must_use_immediately_unsafe_mover(\n            std::move(tw).unwrapTask())()));\n  }\n\n  // No `cpo_t<co_withAsyncStack>` since a \"Task\" is not an awaitable.\n\n  using folly_private_task_wrapper_inner_t = typename Cfg::InnerTaskT;\n  using folly_private_task_wrapper_crtp_base = TaskWrapperCrtp;\n\n  // Wrappers can override these as-needed\n  using folly_must_use_immediately_t =\n      ext::must_use_immediately_t<typename Cfg::InnerTaskT>;\n  using folly_private_value_only_awaitable_t =\n      value_only_awaitable_t<typename Cfg::InnerTaskT>;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      safe_alias_of<folly_private_task_wrapper_inner_t, Default>;\n\n private:\n  using Inner = folly_private_task_wrapper_inner_t;\n  static_assert(\n      detail::is_task_or_wrapper_v<Inner, typename Cfg::ValueT>,\n      \"*TaskWrapper must wrap a sequence of wrappers ending in Task<T>\");\n\n  Inner inner_;\n\n protected:\n  template <typename, typename, typename> // can construct\n  friend class ::folly::coro::detail::TaskPromiseWrapperBase;\n\n  explicit TaskWrapperCrtp(Inner t) noexcept\n      // NOLINTNEXTLINE(facebook-folly-coro-temporary-by-ref)\n      : inner_(folly::ext::must_use_immediately_unsafe_mover(std::move(t))()) {\n    static_assert(\n        folly::ext::must_use_immediately_v<Derived> ||\n            !folly::ext::must_use_immediately_v<\n                typename Cfg::TaskWithExecutorT>,\n        \"`TaskWithExecutorT` must `ext::wrap_must_use_immediately_t` because the inner \"\n        \"task did\");\n  }\n\n  // See \"A note on object slicing\" in `MustUseImmediately.h`\n  Inner unwrapTask() && noexcept {\n    static_assert(sizeof(Inner) == sizeof(Derived));\n    return folly::ext::must_use_immediately_unsafe_mover(std::move(inner_))();\n  }\n\n private:\n  template <typename T>\n  using my_curried_mover = folly::ext::curried_unsafe_mover_t<\n      T,\n      decltype(folly::ext::must_use_immediately_unsafe_mover(\n          FOLLY_DECLVAL(Inner)))>;\n\n public:\n  template <\n      typename Me, // not a forwarding ref, see SFINAE\n      // This check guards against misuse (+ fails on lvalue refs)\n      // See `wrap_must_use_immediately_t::unsafe_mover` for more context\n      std::enable_if_t<std::is_base_of_v<TaskWrapperCrtp, Me>, int> = 0>\n  static my_curried_mover<Me> unsafe_mover(\n      folly::ext::must_use_immediately_private_t /*unused*/, Me&& me) noexcept {\n    return folly::ext::curried_unsafe_mover_from_bases_and_members<\n        TaskWrapperCrtp>(\n        folly::tag</*no bases*/>,\n        folly::vtag<&TaskWrapperCrtp::inner_>,\n        static_cast<Me&&>(me));\n  }\n  template <\n      typename DerivedFromMe,\n      // Matches the SFINAE logic in our `unsafe_mover`\n      std::enable_if_t<std::is_base_of_v<TaskWrapperCrtp, DerivedFromMe>, int> =\n          0>\n  explicit TaskWrapperCrtp(\n      folly::ext::curried_unsafe_mover_private_t /*unused*/,\n      my_curried_mover<DerivedFromMe>&& mover)\n      // `must_use_immediately_unsafe_mover` has more `noexcept` assertions\n      noexcept(noexcept(Inner{std::move(mover.template get<0>())()}))\n      : inner_{std::move(mover.template get<0>())()} {}\n};\n\n// IMPORTANT: Read \"Do not blindly forward more APIs\" in the file docblock.  In\n// a nutshell, adding methods, or by-ref CPOs, can compromise the safety of\n// immediately-awaitable wrappers, so DON'T DO THAT.\ntemplate <typename Derived, typename Cfg>\nclass TaskWithExecutorWrapperCrtp {\n private:\n  using Inner = typename Cfg::InnerTaskWithExecutorT;\n  Inner inner_;\n\n protected:\n  // See \"A note on object slicing\" in `MustUseImmediately.h`\n  Inner unwrapTaskWithExecutor() && noexcept {\n    static_assert(sizeof(Inner) == sizeof(Derived));\n    return folly::ext::must_use_immediately_unsafe_mover(std::move(inner_))();\n  }\n\n  // Our task can construct us, and that logic lives in the CRTP base\n  friend typename Cfg::WrapperTaskT::folly_private_task_wrapper_crtp_base;\n\n  explicit TaskWithExecutorWrapperCrtp(Inner t) noexcept(noexcept(Inner{\n      FOLLY_DECLVAL(Inner)}))\n      : inner_(folly::ext::must_use_immediately_unsafe_mover(std::move(t))()) {}\n\n public:\n  // This is a **deliberately undefined** declaration. It is provided so that\n  // `await_result_t` can work, e.g. `AsyncScope` checks that for all tasks.\n  //\n  // We do NOT want a definition here, for two reasons:\n  //   - As a destructive member function, this can easily violate the\n  //     \"immediately awaitable\" invariant -- all you have to do is\n  //     `twe.operator co_await()`.\n  //   - A definition would have to handle `Cfg::wrapAwaitable`, but also avoid\n  //     double-wrapping the awaitable (*if* that can occur?).  No definition\n  //     means I don't have to think through this :)\n  //\n  // If, in the future, something requires `get_awaiter()` to handle a wrapped\n  // task-with-executor in an **evaluated** context, we can then provide the\n  // definition, being mindful of the above concerns.\n  //\n  // NB: Adding a definition should not let this naively wrong code compile --\n  // that goes through `await_transform()`.  `NowTaskTest.cpp` checks this.\n  //   auto t = co_withExecutor(ex, someNowTask());\n  //   co_await std::move(t);\n  auto operator co_await() && noexcept\n      -> decltype(Cfg::wrapAwaitable(std::move(inner_)).operator co_await());\n\n  // Pass `twe` by-value, since `&&` would break immediately-awaitable types\n  friend Derived co_withCancellation(\n      CancellationToken cancelToken, Derived twe) noexcept {\n    return Derived{co_withCancellation(\n        std::move(cancelToken),\n        folly::ext::must_use_immediately_unsafe_mover(\n            std::move(twe.inner_))())};\n  }\n\n  // Pass `twe` by-value, since `&&` would break immediately-awaitable types\n  // Has copy-pasta above in `TaskWrapperCrtp`.\n  friend auto co_viaIfAsync(\n      Executor::KeepAlive<> executor, Derived twe) noexcept {\n    return Cfg::wrapAwaitable(co_viaIfAsync(\n        std::move(executor),\n        folly::ext::must_use_immediately_unsafe_mover(\n            std::move(twe.inner_))()));\n  }\n\n  // `AsyncScope` requires an awaitable with an executor already attached, and\n  // thus directly calls `co_withAsyncStack` instead of `co_viaIfAsync`.  But,\n  // we still need to wrap the awaitable on that code path.\n  //\n  // NB: Passing by-&& here looks like it could compromise the safety of\n  // immediately-awaitable coros (`now_task`, `now_task_with_executor`).  With\n  // by-value, `BlockingWaitTest.AwaitNowTaskWithExecutor` would not build.\n  //\n  // Supporting pass-by-value would require fixing a LOT of plumbing.\n  //   - `WithAsyncStack.h` calls `is_tag_invocable_v`, which would fail on\n  //     `now_task_with_executor` if this is by-value, since the implementation\n  //     of `is_tag_invocable_v` presents all args by-&&.\n  //   - `CommutativeWrapperAwaitable` and `StackAwareViaIfAsyncAwaiter`,\n  //     among others, also assume that `co_withAsyncStack` takes by-ref.\n  //\n  // Fortunately, I'm not aware of any practical reduction in\n  // immediately-awaitable safety from this issue.  `co_withAsyncStack` should\n  // never be called in user code.  Internal usage in `folly/coro` looks\n  // overall immediately-awaitable-safe -- and the best safeguard for any\n  // particular scenario is to test, see e.g. `NowTaskTest.blockingWait`.\n  friend auto\n  tag_invoke(cpo_t<co_withAsyncStack> /*unused*/, Derived&& twe) noexcept(\n      noexcept(co_withAsyncStack(FOLLY_DECLVAL(Inner)))) {\n    return Cfg::wrapAwaitable(co_withAsyncStack(std::move(twe.inner_)));\n  }\n\n  using folly_must_use_immediately_t = ext::must_use_immediately_t<Inner>;\n  using folly_private_task_without_executor_t = typename Cfg::WrapperTaskT;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t = safe_alias_of<Inner, Default>;\n\n private:\n  template <typename T>\n  using my_curried_mover = folly::ext::curried_unsafe_mover_t<\n      T,\n      decltype(folly::ext::must_use_immediately_unsafe_mover(\n          FOLLY_DECLVAL(Inner)))>;\n\n public:\n  template <\n      typename Me, // not a forwarding ref, see SFINAE\n      // This check guards against misuse (+ fails on lvalue refs)\n      // See `wrap_must_use_immediately_t::unsafe_mover` for more context\n      std::enable_if_t<\n          std::is_base_of_v<TaskWithExecutorWrapperCrtp, Me>,\n          int> = 0>\n  static my_curried_mover<Me> unsafe_mover(\n      folly::ext::must_use_immediately_private_t /*unused*/, Me&& me) noexcept {\n    return folly::ext::curried_unsafe_mover_from_bases_and_members<\n        TaskWithExecutorWrapperCrtp>(\n        folly::tag</*no bases*/>,\n        folly::vtag<&TaskWithExecutorWrapperCrtp::inner_>,\n        static_cast<Me&&>(me));\n  }\n  template <\n      typename DerivedFromMe,\n      // Matches the SFINAE logic in our `unsafe_mover`\n      std::enable_if_t<\n          std::is_base_of_v<TaskWithExecutorWrapperCrtp, DerivedFromMe>,\n          int> = 0>\n  explicit TaskWithExecutorWrapperCrtp(\n      folly::ext::curried_unsafe_mover_private_t /*unused*/,\n      my_curried_mover<DerivedFromMe>&& mover)\n      // `must_use_immediately_unsafe_mover` has more `noexcept` assertions\n      noexcept(noexcept(Inner{std::move(mover.template get<0>())()}))\n      : inner_{std::move(mover.template get<0>())()} {}\n};\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/TimedWait.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/Optional.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/detail/Helpers.h>\n#include <folly/futures/Future.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\ntemplate <typename Awaitable>\nTask<Optional<lift_unit_t<detail::decay_rvalue_reference_t<\n    detail::lift_lvalue_reference_t<semi_await_result_t<Awaitable>>>>>>\ntimed_wait(Awaitable awaitable, Duration duration) {\n  Baton baton;\n  Try<lift_unit_t<detail::decay_rvalue_reference_t<\n      detail::lift_lvalue_reference_t<semi_await_result_t<Awaitable>>>>>\n      result;\n\n  Executor* executor = co_await co_current_executor;\n  auto sleepFuture = futures::sleep(duration).toUnsafeFuture();\n  auto posted = new std::atomic<bool>(false);\n  sleepFuture.setCallback_(\n      [posted, &baton, executor = Executor::KeepAlive<>{executor}](\n          auto&&, auto&&) {\n        if (!posted->exchange(true, std::memory_order_acq_rel)) {\n          executor->add([&baton] { baton.post(); });\n        } else {\n          delete posted;\n        }\n      },\n      // No user logic runs in the callback, we can avoid the cost of switching\n      // the context.\n      /* context */ nullptr);\n\n  {\n    auto t = co_invoke(\n        [awaitable = std::move(\n             awaitable)]() mutable -> Task<semi_await_result_t<Awaitable>> {\n          co_return co_await std::move(awaitable);\n        });\n    co_withExecutor(executor, std::move(t))\n        .start([posted, &baton, &result, sleepFuture = std::move(sleepFuture)](\n                   auto&& r) mutable {\n          if (!posted->exchange(true, std::memory_order_acq_rel)) {\n            result = std::move(r);\n            baton.post();\n            sleepFuture.cancel();\n          } else {\n            delete posted;\n          }\n        });\n  }\n\n  co_await detail::UnsafeResumeInlineSemiAwaitable{get_awaiter(baton)};\n\n  if (!result.hasValue() && !result.hasException()) {\n    co_return folly::none;\n  }\n  co_return std::move(*result);\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Timeout-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/WithCancellation.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nnamespace detail {\n\ntemplate <bool>\nstruct DiscardImpl {\n  folly::coro::Baton baton;\n  exception_wrapper timeoutResult;\n  bool parentCancelled = false;\n  bool checkedTimeout = false;\n};\n\ntemplate <>\nstruct DiscardImpl<false> {};\n\ntemplate <\n    typename SemiAwaitable,\n    typename Duration,\n    bool discard,\n    typename Fn,\n    typename TimekeeperPtr>\ntypename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeoutImpl(\n    Fn semiFn, Duration timeoutDuration, TimekeeperPtr tk) {\n  CancellationSource cancelSource;\n  DiscardImpl<discard> impl;\n  auto sleepFuture =\n      folly::futures::sleep(timeoutDuration, tk).toUnsafeFuture();\n  sleepFuture.setCallback_(\n      [&, cancelSource](Executor::KeepAlive<>&&, Try<Unit>&& result) noexcept {\n        if constexpr (discard) {\n          if (result.hasException()) {\n            impl.timeoutResult = std::move(result.exception());\n          } else {\n            impl.timeoutResult = folly::make_exception_wrapper<FutureTimeout>();\n          }\n          impl.baton.post();\n        }\n        cancelSource.requestCancellation();\n      });\n\n  bool isSleepCancelled = false;\n  auto tryCancelSleep = [&]() noexcept {\n    if (!isSleepCancelled) {\n      isSleepCancelled = true;\n      sleepFuture.cancel();\n    }\n  };\n\n  std::optional<CancellationCallback> cancelCallback{\n      std::in_place, co_await co_current_cancellation_token, [&]() {\n        cancelSource.requestCancellation();\n        tryCancelSleep();\n        if constexpr (discard) {\n          impl.parentCancelled = true;\n        }\n      }};\n\n  exception_wrapper error;\n  try {\n    auto resultTry = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(\n            cancelSource.getToken(), std::move(semiFn)()));\n\n    cancelCallback.reset();\n\n    if constexpr (discard) {\n      if (!impl.parentCancelled && impl.baton.ready()) {\n        // Timer already fired\n        co_yield folly::coro::co_error(std::move(impl.timeoutResult));\n      }\n      impl.checkedTimeout = true;\n    }\n\n    tryCancelSleep();\n    if constexpr (discard) {\n      co_await impl.baton;\n    }\n\n    if (resultTry.hasException()) {\n      co_yield folly::coro::co_error(std::move(resultTry).exception());\n    }\n\n    co_return std::move(resultTry).value();\n  } catch (...) {\n    error = exception_wrapper{current_exception()};\n  }\n\n  assert(error);\n\n  cancelCallback.reset();\n\n  if constexpr (discard) {\n    if (!impl.checkedTimeout && !impl.parentCancelled && impl.baton.ready()) {\n      // Timer already fired\n      co_yield folly::coro::co_error(std::move(impl.timeoutResult));\n    }\n  }\n\n  tryCancelSleep();\n  if constexpr (discard) {\n    co_await impl.baton;\n  }\n\n  co_yield folly::coro::co_error(std::move(error));\n}\n\n} // namespace detail\n\ntemplate <typename SemiAwaitable, typename Duration, typename TimekeeperPtr>\ntypename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeout(\n    SemiAwaitable semiAwaitable, Duration timeoutDuration, TimekeeperPtr tk) {\n  return detail::timeoutImpl<SemiAwaitable, Duration, /*discard=*/true>(\n      folly::ext::must_use_immediately_unsafe_mover(std::move(semiAwaitable)),\n      timeoutDuration,\n      std::move(tk));\n}\n\ntemplate <typename SemiAwaitable, typename Duration, typename TimekeeperPtr>\ntypename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeoutNoDiscard(\n    SemiAwaitable semiAwaitable, Duration timeoutDuration, TimekeeperPtr tk) {\n  return detail::timeoutImpl<SemiAwaitable, Duration, /*discard=*/false>(\n      folly::ext::must_use_immediately_unsafe_mover(std::move(semiAwaitable)),\n      timeoutDuration,\n      std::move(tk));\n}\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Timeout.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/detail/PickTaskWrapper.h>\n#include <folly/futures/Future.h>\n// `timeout(coroFutureInt())` makes a `safe_task`\n#include <folly/coro/safe/SafeTask.h>\n// `timeout(memberTask())` makes a `now_task`\n#include <folly/coro/safe/NowTask.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nnamespace detail {\n// This doesn't try to apply `value_or_fatal` to the output, since `timeout` is\n// expected to throw, and `timeoutNoDiscard()` may either complete with a\n// stopped state, or with an error.\ntemplate <typename SemiAwaitable, typename TimekeeperPtr>\nusing TimeoutTask = pick_task_wrapper<\n    typename semi_await_try_result_t<SemiAwaitable>::element_type,\n    std::min(\n        lenient_safe_alias_of_v<TimekeeperPtr>,\n        lenient_safe_alias_of_v<SemiAwaitable>),\n    folly::ext::must_use_immediately_v<SemiAwaitable>>;\n} // namespace detail\n\n/// Returns a Task that, when started, starts a timer of duration\n/// 'timeoutDuration' and awaits the passed SemiAwaitable.\n///\n/// If the timeoutDuration elapses before the 'co_await semiAwaitable'\n/// operation completes then requests cancellation of the child operation\n/// and completes with an error of type folly::FutureTimeout.\n/// Otherwise, if the 'co_await semiAwaitable' operation completes before\n/// the timeoutDuration elapses then cancels the timer and completes with\n/// the result of the semiAwaitable.\n///\n/// IMPORTANT: The operation passed as the first argument must be able\n/// to respond to a request for cancellation on the CancellationToken\n/// injected to it via folly::coro::co_withCancellation in a timely manner for\n/// the timeout to work as expected.\n///\n/// If a timekeeper is provided then uses that timekeeper to start the timer,\n/// otherwise uses the process' default TimeKeeper if 'tk' is null.\n///\n/// \\throws folly::FutureTimeout\n/// \\refcode folly/docs/examples/folly/coro/DetachOnCancel.cpp\ntemplate <\n    typename SemiAwaitable,\n    typename Duration,\n    // Templated so we can take safe pointers like `capture<Timekeeper&>` from\n    // `folly/coro/safe`, and return a `safe_task`.\n    typename TimekeeperPtr = std::nullptr_t>\ntypename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeout(\n    SemiAwaitable semiAwaitable,\n    Duration timeoutDuration,\n    TimekeeperPtr tk = nullptr);\n\n/// Returns a Task that, when started, starts a timer of duration\n/// 'timeoutDuration' and awaits the passed SemiAwaitable (operation).\n///\n/// The returned result is *always* that of the operation. In other words the\n/// result is never discarded, in contrast with `timeout`.\n///\n/// If the timeout duration elapses before the operation completes, the result\n/// should and typically will reflect cancellation (e.g. `OperationCancelled`)\n/// but this depends on how the operation responds (as cancellation is\n/// cooperative).\n///\n/// To disambiguate between cancellation and timeout, callers can inspect their\n/// own cancellation token.\n///\n/// IMPORTANT: This function has no effect if the passed operation does not\n/// respond to cancellation. The operation passed as the first argument must be\n/// able to respond to a request for cancellation on the CancellationToken\n/// injected to it via folly::coro::co_withCancellation in a timely manner for\n/// the timeout to work as expected.\n///\n/// If a timekeeper is provided then uses that timekeeper to start the timer,\n/// otherwise uses the process' default TimeKeeper if 'tk' is null.\ntemplate <\n    typename SemiAwaitable,\n    typename Duration,\n    typename TimekeeperPtr = std::nullptr_t> // templated for reason above\ntypename detail::TimeoutTask<SemiAwaitable, TimekeeperPtr> timeoutNoDiscard(\n    SemiAwaitable semiAwaitable,\n    Duration timeoutDuration,\n    TimekeeperPtr tk = nullptr);\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Timeout-inl.h>\n"
  },
  {
    "path": "folly/coro/Traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Traits.h>\n#include <folly/coro/Coroutine.h>\n\n#include <type_traits>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n/**\n * A type trait to unwrap a std::reference_wrapper<T> to a type T\n */\ntemplate <typename T>\nstruct remove_reference_wrapper {\n  using type = T;\n};\ntemplate <typename T>\nstruct remove_reference_wrapper<std::reference_wrapper<T>> {\n  using type = T;\n};\ntemplate <typename T>\nusing remove_reference_wrapper_t = typename remove_reference_wrapper<T>::type;\n\nnamespace detail {\n\ntemplate <typename T>\ninline constexpr bool is_coroutine_handle_v = folly::is_instantiation_of_v< //\n    coroutine_handle,\n    T>;\n\n} // namespace detail\n\n/// is_awaiter<T>::value\n/// is_awaiter_v<T>\n///\n/// Template metafunction for querying whether the specified type implements\n/// the 'Awaiter' concept.\n///\n/// An 'Awaiter' must have the following three methods.\n/// - awaiter.await_ready() -> bool\n/// - awaiter.await_suspend(coroutine_handle<void>()) ->\n///     void OR\n///     bool OR\n///     coroutine_handle<T> for some T\n/// - awaiter.await_resume()\n///\n/// Note that we don't check for a valid await_suspend() method here since\n/// we don't yet know the promise type to use and some await_suspend()\n/// implementations have particular requirements on the promise (eg. the\n/// stack-aware awaiters may require the .getAsyncFrame() method)\ntemplate <typename T, typename = void>\nstruct is_awaiter : std::bool_constant<!require_sizeof<T>> {};\n\ntemplate <typename T>\nstruct is_awaiter<T, std::enable_if_t<std::is_void_v<T>>> : std::false_type {};\n\ntemplate <typename T>\nstruct is_awaiter<\n    T,\n    folly::void_t<\n        decltype(std::declval<T&>().await_ready()),\n        decltype(std::declval<T&>().await_resume())>>\n    : std::is_same<bool, decltype(std::declval<T&>().await_ready())> {};\n\ntemplate <typename T>\nconstexpr bool is_awaiter_v = is_awaiter<T>::value;\n\nnamespace detail {\n\ntemplate <typename Awaitable, typename = void>\nstruct _has_member_operator_co_await\n    : std::bool_constant<!require_sizeof<Awaitable>> {};\n\ntemplate <typename T>\nstruct _has_member_operator_co_await<T, std::enable_if_t<std::is_void_v<T>>>\n    : std::false_type {};\n\ntemplate <typename Awaitable>\nstruct _has_member_operator_co_await<\n    Awaitable,\n    folly::void_t<decltype(std::declval<Awaitable>().operator co_await())>>\n    : is_awaiter<decltype(std::declval<Awaitable>().operator co_await())> {};\n\ntemplate <typename Awaitable, typename = void>\nstruct _has_free_operator_co_await\n    : std::bool_constant<!require_sizeof<Awaitable>> {};\n\ntemplate <typename T>\nstruct _has_free_operator_co_await<T, std::enable_if_t<std::is_void_v<T>>>\n    : std::false_type {};\n\ntemplate <typename Awaitable>\nstruct _has_free_operator_co_await<\n    Awaitable,\n    folly::void_t<decltype(operator co_await(std::declval<Awaitable>()))>>\n    : is_awaiter<decltype(operator co_await(std::declval<Awaitable>()))> {};\n\n} // namespace detail\n\n/// is_awaitable<T>::value\n/// is_awaitable_v<T>\n///\n/// Query if a type, T, is awaitable within the context of any coroutine whose\n/// promise_type does not have an await_transform() that modifies what is\n/// normally awaitable.\n///\n/// A type, T, is awaitable if it is an Awaiter, or if it has either a\n/// member operator co_await() or a free-function operator co_await() that\n/// returns an Awaiter.\ntemplate <typename T>\nstruct is_awaitable\n    : folly::Disjunction<\n          detail::_has_member_operator_co_await<T>,\n          detail::_has_free_operator_co_await<T>,\n          is_awaiter<T>> {};\n\ntemplate <typename T>\nconstexpr bool is_awaitable_v = is_awaitable<T>::value;\n\n/// get_awaiter(Awaitable&&) -> awaiter_type_t<Awaitable>\n///\n/// The get_awaiter() function takes an Awaitable type and returns a value\n/// that contains the await_ready(), await_suspend() and await_resume() methods\n/// for that type.\n///\n/// This encapsulates calling 'operator co_await()' if it exists.\nstruct get_awaiter_fn {\n  template <\n      typename Awaitable,\n      std::enable_if_t<\n          folly::Conjunction<\n              is_awaiter<Awaitable>,\n              folly::Negation<detail::_has_free_operator_co_await<Awaitable>>,\n              folly::Negation<\n                  detail::_has_member_operator_co_await<Awaitable>>>::value,\n          int> = 0>\n  Awaitable& operator()(Awaitable&& awaitable) const {\n    return static_cast<Awaitable&>(awaitable);\n  }\n\n  template <\n      typename Awaitable,\n      std::enable_if_t<\n          detail::_has_member_operator_co_await<Awaitable>::value,\n          int> = 0>\n  decltype(auto) operator()(Awaitable&& awaitable) const {\n    return static_cast<Awaitable&&>(awaitable).operator co_await();\n  }\n\n  template <\n      typename Awaitable,\n      std::enable_if_t<\n          folly::Conjunction<\n              detail::_has_free_operator_co_await<Awaitable>,\n              folly::Negation<\n                  detail::_has_member_operator_co_await<Awaitable>>>::value,\n          int> = 0>\n  decltype(auto) operator()(Awaitable&& awaitable) const {\n    return operator co_await(static_cast<Awaitable&&>(awaitable));\n  }\n};\nconstexpr inline get_awaiter_fn get_awaiter{};\n\n/// awaiter_type<Awaitable>\n///\n/// A template-metafunction that lets you query the type that will be used\n/// as the Awaiter object when you co_await a value of type Awaitable.\n/// This is the return-type of get_awaiter() when passed a value of type\n/// Awaitable.\ntemplate <typename Awaitable, typename = void>\nstruct awaiter_type {};\n\ntemplate <typename Awaitable>\nstruct awaiter_type<Awaitable, std::enable_if_t<is_awaitable_v<Awaitable>>> {\n  using type = decltype(get_awaiter(std::declval<Awaitable>()));\n};\n\n/// await_result<Awaitable>\n///\n/// A template metafunction that allows you to query the type that will result\n/// from co_awaiting a value of that type in the context of a coroutine that\n/// does not modify the normal behaviour with promise_type::await_transform().\ntemplate <typename Awaitable>\nusing awaiter_type_t = typename awaiter_type<Awaitable>::type;\n\ntemplate <typename Awaitable, typename = void>\nstruct await_result {};\n\ntemplate <typename Awaitable>\nstruct await_result<Awaitable, std::enable_if_t<is_awaitable_v<Awaitable>>> {\n  using type = decltype(get_awaiter(std::declval<Awaitable>()).await_resume());\n};\n\ntemplate <typename Awaitable>\nusing await_result_t = typename await_result<Awaitable>::type;\n\nnamespace detail {\n\ntemplate <typename Promise, typename = void>\nconstexpr bool promiseHasAsyncFrame_v = !require_sizeof<Promise>;\n\ntemplate <typename Promise>\nconstexpr bool promiseHasAsyncFrame_v<\n    Promise,\n    void_t<decltype(std::declval<Promise&>().getAsyncFrame())>> = true;\n\n} // namespace detail\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Transform-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Traits.h>\n#include <folly/coro/Transform.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\ntemplate <\n    typename ReturnType,\n    typename TransformFn,\n    typename Reference,\n    typename Value,\n    typename ReturnReference>\nAsyncGenerator<ReturnReference> transform(\n    AsyncGenerator<Reference, Value> source, TransformFn transformFn) {\n  while (auto item = co_await source.next()) {\n    using InvokeResult = decltype(invoke(transformFn, std::move(item).value()));\n    if constexpr (std::is_constructible_v<ReturnReference&&, InvokeResult>) {\n      co_yield invoke(transformFn, std::move(item).value());\n    } else {\n      remove_cvref_t<ReturnReference> result =\n          invoke(transformFn, std::move(item).value());\n      co_yield std::forward<ReturnReference>(result);\n    }\n  }\n}\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/Transform.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Coroutine.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\nnamespace detail {\nstruct computed_from_input;\n}\n\n// Transform the Values from an input stream into a stream of the\n// Trandformed Values.\n//\n// The input is a stream of Values.\n//\n// The output is a stream of Transformed Value.\n//\n// Example:\n//   AsyncGenerator<int> stream();\n//\n//   Task<void> consumer() {\n//     auto to_float = [](int i){ return i * 1.0f; };\n//     AsyncGenerator<float&> events = transform(stream(), to_float);\n//     try {\n//       while (auto item = co_await events.next()) {\n//         // Value\n//         float& value = *item;\n//         std::cout << \"value \" << value << \"\\n\";\n//       }\n//       // End Of Stream\n//       std::cout << \"end\\n\";\n//     } catch (const std::exception& error) {\n//       // Exception\n//       std::cout << \"error \" << error.what() << \"\\n\";\n//     }\n//   }\n//\n// By default the AsyncGenerator returns a reference to the computed value.\n// Specify the first template argument to override the return type of the\n// generator.\n//\n// Example:\n//   AsyncGenerator<double> events = transform<double>(stream(), to_float);\ntemplate <\n    typename ReturnType = detail::computed_from_input,\n    typename TransformFn,\n    typename Reference,\n    typename Value,\n    typename ReturnReference = std::conditional_t<\n        std::is_same_v<ReturnType, detail::computed_from_input>,\n        invoke_result_t<TransformFn&, Reference&&>&&,\n        ReturnType>>\nAsyncGenerator<ReturnReference> transform(\n    AsyncGenerator<Reference, Value> source, TransformFn transformFn);\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n\n#include <folly/coro/Transform-inl.h>\n"
  },
  {
    "path": "folly/coro/UnboundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Task.h>\n#include <folly/fibers/Semaphore.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\n// Wrapper around folly::UnboundedQueue with async wait\n\ntemplate <\n    typename T,\n    bool SingleProducer = false,\n    bool SingleConsumer = false,\n    bool MayBlock = false,\n    size_t LgSegmentSize = 8>\nclass UnboundedQueue {\n public:\n  template <typename U = T>\n  void enqueue(U&& val) {\n    queue_.enqueue(std::forward<U>(val));\n    sem_.signal();\n  }\n\n  // Dequeue a value from the queue.\n  // Note that this operation can be safely cancelled by requesting cancellation\n  // on the awaiting coroutine's associated CancellationToken.\n  // If the operation is successfully cancelled then it will complete with\n  // an error of type folly::OperationCancelled.\n  // WARNING: It is not safe to wrap this with folly::coro::timeout(). Wrap with\n  // folly::coro::timeoutNoDiscard(), or use co_try_dequeue_for() instead.\n  folly::coro::Task<T> dequeue() {\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(sem_.co_wait());\n    if (result.hasException()) {\n      co_yield co_error(std::move(result).exception());\n    }\n\n    co_return queue_.dequeue();\n  }\n\n  // Try to dequeue a value from the queue with a timeout. The operation will\n  // either successfully dequeue an item from the queue, or else be cancelled\n  // and complete with an error of type folly::OperationCancelled.\n  template <typename Duration>\n  folly::coro::Task<T> co_try_dequeue_for(Duration timeout) {\n    folly::Try<void> result =\n        co_await folly::coro::co_awaitTry(sem_.co_try_wait_for(timeout));\n    if (result.hasException()) {\n      co_yield co_error(std::move(result).exception());\n    }\n\n    co_return queue_.dequeue();\n  }\n\n  folly::coro::Task<void> dequeue(T& out) {\n    co_await sem_.co_wait();\n    queue_.dequeue(out);\n  }\n\n  folly::Optional<T> try_dequeue() {\n    return sem_.try_wait() ? queue_.try_dequeue() : folly::none;\n  }\n\n  bool try_dequeue(T& out) {\n    return sem_.try_wait() ? queue_.try_dequeue(out) : false;\n  }\n\n  bool empty() const { return queue_.empty(); }\n\n  const T* try_peek() noexcept { return queue_.try_peek(); }\n\n  size_t size() const { return queue_.size(); }\n\n private:\n  folly::UnboundedQueue< //\n      T,\n      SingleProducer,\n      SingleConsumer,\n      MayBlock,\n      LgSegmentSize>\n      queue_;\n  folly::fibers::Semaphore sem_{0};\n};\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/ValueOrError.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/result/try.h>\n\n#if FOLLY_HAS_RESULT\n\nnamespace folly::coro {\n\n/// `value_or_error_or_stopped` is the `result<T>` analog of the older\n/// `co_awaitTry`.\n///\n/// In a `folly::coro` async coroutine, use `value_or_error_or_stopped` like so:\n///\n///   result<int> res = co_await value_or_error_or_stopped(taskReturningInt());\n///   if (auto ex = get_exception<MyError>(res)) {\n///     /* handle ex */\n///   } else {\n///     sum += co_await co_ready(res); // efficiently propagate unhandled error\n///   }\n///\n/// Contrast that with related async coro vocabulary:\n///  - `co_yield co_result(r)` from `Result.h` -- propagate `result<T>` or\n///    `Try<T>` to the awaiter of the current coro.\n///  - `auto& v = co_await co_ready(r)` from `Ready.h` -- given a `result<T>`,\n///    unpack the value, or propagate any error to our awaiter.\n///\n/// The purpose of `value_or_error_or_stopped` is to handle errors from a child\n/// task via `result<T>`, rather than through `try {} catch {}`.  Some reasons\n/// to do so:\n///   - Your error-handling APIs (logging, retry, etc) use `result<T>`.\n///   - You wish to avoid the ~microsecond cost of thrown exceptions,\n///     applicable only when your error path is hot, and the child uses\n///     `co_yield` instead of `throw` to propagate exceptions.\n\nnamespace detail {\n\ntemplate <typename Awaiter>\nusing detect_await_resume_result =\n    decltype(FOLLY_DECLVAL(Awaiter).await_resume_result());\n\ntemplate <typename Awaiter>\nconstexpr bool is_awaiter_result =\n    is_detected_v<detect_await_resume_result, Awaiter>;\n\ntemplate <typename Awaitable>\nconstexpr bool is_awaitable_result =\n    is_awaiter_result<awaiter_type_t<Awaitable>>;\n\n// On the happy path, this uses the dedicated `await_resume_result()` protocol,\n// if it's supported by the awaiter.\n//\n// As fallback, this reuses the `await_resume_try()` machinery in the hope that\n// the compiler will be able to optimize away the `Try` -> `result` conversion.\n//\n// The reasons to support the dedicated protocol are (1) better semantics, and\n// (2) a data flow that's easier for the compiler to optimize.  Specifically:\n//\n//   - `await_resume_result()` cleanly handles `Task<V&>`, whereas `Try`\n//     doesn't support storing references, and the caller of `co_awaitTry` has\n//     to deal with `Try<std::reference_wrapper<V>>`.  See the test in\n//     `TaskOfLvalueReferenceAsTry`.\n//\n//   - `await_resume_result()` implementations can explicitly avoid the \"empty\n//     `Try`\" pitfall, which is something that gets converted to a\n//     `UsingUninitializedTry` error by the `try_to_result()` fallback.\n//\n//   - Falling back to `await_resume_try()` can incur an extra move-copy, which\n//     may not always optimize away.\ntemplate <typename Awaitable>\nclass ResultAwaiter : public ValueOnlyAwaiterBase<Awaitable> {\n private:\n  static_assert(is_awaitable_try<Awaitable> || is_awaitable_result<Awaitable>);\n\n  using Base = ValueOnlyAwaiterBase<Awaitable>;\n\n public:\n  explicit ResultAwaiter(Awaitable&& awaitable)\n      : Base(static_cast<Awaitable&&>(awaitable)) {}\n\n  // Preferred `await_resume()`: use `await_resume_result()` when available\n  template <\n      typename Awaiter2 = typename Base::Awaiter,\n      typename Result =\n          decltype(FOLLY_DECLVAL(Awaiter2&).await_resume_result())>\n  Result await_resume() noexcept(\n      noexcept(this->awaiter_.await_resume_result())) {\n    return this->awaiter_.await_resume_result();\n  }\n\n  // Fallback `await_resume()`: use `await_resume_try()` otherwise\n  template <\n      typename Awaiter2 = typename Base::Awaiter,\n      typename Result =\n          decltype(try_to_result(FOLLY_DECLVAL(Awaiter2&).await_resume_try()))>\n  Result await_resume() noexcept(\n      noexcept(try_to_result(this->awaiter_.await_resume_try())))\n    requires(!is_awaitable_result<Awaitable>) // Fallback logic\n  {\n    return try_to_result(this->awaiter_.await_resume_try());\n  }\n};\n\ntemplate <typename T>\nclass [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] ValueOrErrorOrStopped\n    : public CommutativeWrapperAwaitable<ValueOrErrorOrStopped, T> {\n public:\n  using CommutativeWrapperAwaitable<ValueOrErrorOrStopped, T>::\n      CommutativeWrapperAwaitable;\n\n  template <\n      typename Self,\n      std::enable_if_t<\n          std::is_same_v<remove_cvref_t<Self>, ValueOrErrorOrStopped>,\n          int> = 0,\n      typename T2 = like_t<Self, T>,\n      std::enable_if_t<is_awaitable_v<T2>, int> = 0>\n  friend ResultAwaiter<T2> operator co_await(Self&& self) {\n    return ResultAwaiter<T2>{static_cast<Self&&>(self).inner_};\n  }\n\n  using folly_private_value_only_awaitable_t = std::true_type;\n};\n\n// The awaitable backing `ValueOrError`, see that doc for why it's separate.\n//\n// NB: Besides `folly_private_value_only_awaitable_t`, this is identical to\n// `ValueOrErrorOrStopped`, but the way the `CommutativeWrapperAwaitable`\n// template is set up, it's a lot of code to parameterize it.\n//\n// IMPORTANT: this will never return \"stopped\" since `BasePromise` sets up\n// `OperationCancelled` to unwind directly to the parent.\ntemplate <typename T>\nclass [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]\nValueOrErrorImpl : public CommutativeWrapperAwaitable<ValueOrErrorImpl, T> {\n public:\n  using CommutativeWrapperAwaitable<ValueOrErrorImpl, T>::\n      CommutativeWrapperAwaitable;\n\n  template <\n      typename Self,\n      std::enable_if_t<\n          std::is_same_v<remove_cvref_t<Self>, ValueOrErrorImpl>,\n          int> = 0,\n      typename T2 = like_t<Self, T>,\n      std::enable_if_t<is_awaitable_v<T2>, int> = 0>\n  friend ResultAwaiter<T2> operator co_await(Self&& self) {\n    return ResultAwaiter<T2>{static_cast<Self&&>(self).inner_};\n  }\n\n  // Future: Cannot mark `folly_private_value_only_awaitable_t` since it\n  // completes with \"stopped\" which may throw in contexts like `blocking_wait`.\n  // Once the `awaitable_completions_v` refactor is complete, this should\n  // remove the \"error\" completion from the underlying awaitable.\n};\n\n// Similarly to NothrowAwaitable, this deliberately omits `co_await` and relies\n// on `BasePromise::await_transform`.  The goal is to prevent its use in\n// contexts that cannot provide nothrow-propagation semantics for stopped\n// coros.  Gating on `BasePromise` provides a tightly controlled allowlist of\n// where this can be awaited, and how.\n//\n// For a specific example, consider `blocking_wait(value_or_error(...))` --\n// this should either not compile, or throw on `OperationCancelled`.  The\n// latter \"sometimes throwing\" semantics would be a surprising footgun, so we\n// instead require the obvious contract of `value_or_error_or_stopped()` here.\ntemplate <typename T>\nclass [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]\nValueOrError : public CommutativeWrapperAwaitable<ValueOrError, T> {\n public:\n  using CommutativeWrapperAwaitable<ValueOrError, T>::\n      CommutativeWrapperAwaitable;\n\n protected:\n  template <typename>\n  friend class BasePromise;\n  ValueOrErrorImpl<T> toValueOrErrorImpl() && {\n    return ValueOrErrorImpl<T>{folly::ext::must_use_immediately_unsafe_mover(\n        std::move(this->inner_))()};\n  }\n\n  // Future: See `ValueOrErrorImpl` regarding\n  // `folly_private_value_only_awaitable_t` and `awaitable_completions_v`.\n};\n\n} // namespace detail\n\n// BEFORE CHANGING: If you need an `Awaitable&&` overload, you must bifurcate\n// these APIs on `must_use_immediately_v`, see `co_awaitTry` for an example.\n\n/// When awaited, returns a `result<T>` in a value or an error state, but never\n/// `has_stopped()` (aka `OperationCancelled`).  If `awaitable` reports as\n/// \"stopped\", the awaiting coroutine will be promptly torn down, **without\n/// throwing**, and its parent will in turn receive a \"stopped\" completion.\n///\n/// BE CAREFUL -- This is `co_nothrow` semantics, but **only** for\n/// cancellation.  -- if your coro requires async cleanup, such as awaiting an\n/// async scope or other background work, then you **must** use a safe async\n/// RAII pattern, such `async_closure` if applicable (or `co_scope_exit`, but\n/// beware of its lifetime risks).\ntemplate <typename Awaitable>\ndetail::ValueOrError<Awaitable> value_or_error(\n    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {\n  return detail::ValueOrError<Awaitable>{\n      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};\n}\n\n/// When awaited, returns `result<T>` just like `value_or_error()`, but also\n/// captures \"stopped\" completions, so you **must** test for `has_stopped()`.\ntemplate <typename Awaitable>\ndetail::ValueOrErrorOrStopped<Awaitable> value_or_error_or_stopped(\n    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {\n  return detail::ValueOrErrorOrStopped<Awaitable>{\n      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/ValueOrFatal.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Unit.h>\n#include <folly/coro/TaskWrapper.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/lang/Assume.h>\n#include <folly/result/result.h>\n#include <folly/result/try.h>\n#include <folly/result/value_only_result.h>\n\n#if FOLLY_HAS_COROUTINES\n\n/// `value_or_fatal<Task<T>, Policy>` wraps a task to guarantee value-only\n/// completion.  Use this only with APIs that require it -- like `co_cleanup()`\n/// async RAII, or async scopes.  If your code compiles without it, skip it!\n///\n/// You must specify a policy at least for \"stopped\" -- from C++26 onward,\n/// cancellation is not an error: https://wg21.link/p1677\n///\n///   Policy                            | Stopped | Error\n///   ----------------------------------|---------|------\n///   on_stopped_void                   | void    | fatal\n///   on_stopped<V>                     | V       | fatal\n///   on_stopped_and_error<V>           | V       | V\n///   on_stopped_and_error<V, W>        | V       | W\n///   on_stopped_and_error<will_fatal>  | fatal   | fatal\n///\n/// ## Why not just `noexcept`?\n///\n/// A `noexcept` coroutine only fatals on errors during frame construction --\n/// awaiting it can still throw.  `value_or_fatal<>` makes the await not throw.\n///\n/// ## This is probably not for you\n///\n///   - Not related to `co_nothrow`, which efficiently propagates exceptions to\n///     the parent, without letting the awaiting task handle them.\n///   - Not a general \"avoid exceptions\" tool -- the wrapper adds build cost,\n///     frame allocation can still throw, and `std::terminate` offramps can\n///     pessimize vs normal exception propagation.\n///\n/// ## Custom policies\n///\n///   struct my_policy_t {\n///     static int value_only_default(non_value_result&& nv) noexcept {\n///       LOG(ERROR) << (nv.has_stopped() ? \"stopped\" : \"error\");\n///       return 0;\n///     }\n///   };\n///   inline constexpr my_policy_t my_policy{};\n///   value_or_fatal<Task<int>, my_policy> foo() { ... }\n\nnamespace folly::coro {\n\n// Tag for \"terminate on this completion\"\nstruct will_fatal_t {};\ninline constexpr will_fatal_t will_fatal{};\n\n/// Primary policy struct: specify behavior for stopped (cancellation) and\n/// error. Use via the variable templates below, not directly.\ntemplate <auto Stopped, auto Error>\nclass on_stopped_and_error_t {\n private:\n  template <auto V, typename RetT>\n  static constexpr RetT value_or_terminate(non_value_result&& nv) noexcept {\n    if constexpr (std::is_same_v<decltype(V), will_fatal_t>) {\n      nv.throw_exception(); // Better error than `std::terminate()`\n      folly::assume_unreachable();\n    } else {\n      return V;\n    }\n  }\n\n public:\n  template <\n      typename RetT = conditional_t<\n          std::is_same_v<decltype(Stopped), will_fatal_t>,\n          decltype(Error),\n          decltype(Stopped)>>\n  static constexpr RetT value_only_default(non_value_result&& nv) noexcept {\n    return nv.has_stopped()\n        ? value_or_terminate<Stopped, RetT>(std::move(nv))\n        : value_or_terminate<Error, RetT>(std::move(nv));\n  }\n};\n\ntemplate <auto Stopped, auto Error = Stopped>\ninline constexpr auto on_stopped_and_error =\n    on_stopped_and_error_t<Stopped, Error>{};\n\n/// Convenience: stopped returns V, error terminates (fatal).\ntemplate <auto V>\ninline constexpr auto on_stopped = on_stopped_and_error<V, will_fatal>;\n\n/// Convenience for void tasks: stopped returns unit, error terminates.\ninline constexpr auto on_stopped_void = on_stopped<unit>;\n\nnamespace detail {\n\n// Detect if a type is a TaskWithExecutor-like type (has an attached executor).\ntemplate <typename T>\ninline constexpr bool is_task_with_executor_v = requires {\n  typename T::folly_private_task_without_executor_t;\n};\n\ntemplate <typename Awaitable, auto Policy>\nclass ValueOrFatalAwaiter : public ValueOnlyAwaiterBase<Awaitable> {\n private:\n  using Base = ValueOnlyAwaiterBase<Awaitable>;\n  using RetT = decltype(FOLLY_DECLVAL(typename Base::Awaiter).await_resume());\n\n public:\n  explicit ValueOrFatalAwaiter(Awaitable&& awaitable)\n      : Base(static_cast<Awaitable&&>(awaitable)) {}\n\n  // Returns `value_only_result<T>` for composition with `await_resume_result()`\n  // protocol. Implicitly converts to `result<T>` for callers expecting that.\n  value_only_result<RetT> await_resume_result() noexcept {\n    // Get result from awaiter using the best available protocol\n    auto res = [&]() -> result<RetT> {\n      if constexpr (is_awaiter_result<typename Base::Awaiter>) {\n        // Catch missed optimization: `awaiter_` returning `value_only_result`\n        // would needlessly convert `value_only_result` to `result`, then back.\n        static_assert(\n            !is_instantiation_of_v<\n                value_only_result,\n                std::remove_cvref_t<\n                    decltype(this->awaiter_.await_resume_result())>>);\n        return this->awaiter_.await_resume_result();\n      } else if constexpr (is_awaiter_try<typename Base::Awaiter>) {\n        return try_to_result(this->awaiter_.await_resume_try());\n      } else {\n        try {\n          if constexpr (std::is_void_v<RetT>) {\n            this->awaiter_.await_resume();\n            return {};\n          } else {\n            return this->awaiter_.await_resume();\n          }\n        } catch (...) { // Policy may handle both \"stopped\" and \"error\" below\n          return non_value_result::from_current_exception();\n        }\n      }\n    }();\n\n    // Handle result uniformly\n    if (res.has_value()) {\n      if constexpr (std::is_void_v<RetT>) {\n        return {};\n      } else {\n        return std::move(res).value_or_throw();\n      }\n    } else { // Apply policy to non-value result\n      auto nv = std::move(res).non_value();\n      using PolicyRetT = decltype(Policy.value_only_default(std::move(nv)));\n      if constexpr (std::is_same_v<PolicyRetT, will_fatal_t>) {\n        // Policy terminates (will_fatal), won't return\n        Policy.value_only_default(std::move(nv));\n        folly::assume_unreachable();\n      } else if constexpr (std::is_void_v<RetT>) {\n        Policy.value_only_default(std::move(nv));\n        return {};\n      } else {\n        return Policy.value_only_default(std::move(nv));\n      }\n    }\n  }\n\n  RetT await_resume() noexcept { return await_resume_result().value_only(); }\n\n  // IMPORTANT: There is deliberately NO `await_resume_try()` here. This causes\n  // `co_awaitTry(value_or_fatal(...))` to INTENTIONALLY fail at compile time:\n  //   - `value_or_fatal` guarantees value-only completion\n  //   - `Try`'s error state would be useless (never populated)\n  //   - We want new code to adopt `result`, not `Try`\n  void await_resume_try(auto&&...) = delete;\n};\n\ntemplate <typename, auto>\nclass ValueOrFatalAwaitable;\n\ntemplate <auto Policy>\nstruct ValueOrFatalAwaitableWithPolicy {\n  template <typename T>\n  using apply = ValueOrFatalAwaitable<T, Policy>;\n};\n\n/// `value_or_fatal<>` / `ValueOrFatalAwaitable<>` compose properly with other\n/// coro- and awaitable-wrappers.  But, not all combinations make sense -- see\n/// the test, and/or extend it if needed.  For example, the outer wrapper is\n/// useless in `ValueOrFatalAwaitable<...>(co_awaitTry(...))`, since exceptions\n/// would already have been routed into a `Try`.\ntemplate <typename T, auto Policy>\nclass [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]] ValueOrFatalAwaitable\n    : public CommutativeWrapperAwaitable<\n          ValueOrFatalAwaitableWithPolicy<Policy>::template apply,\n          T> {\n public:\n  using CommutativeWrapperAwaitable<\n      ValueOrFatalAwaitableWithPolicy<Policy>::template apply,\n      T>::CommutativeWrapperAwaitable;\n\n  template <typename T2 = T, std::enable_if_t<is_awaitable_v<T2>, int> = 0>\n  ValueOrFatalAwaiter<T, Policy> operator co_await() && {\n    return ValueOrFatalAwaiter<T, Policy>{std::move(this->inner_)};\n  }\n\n  using folly_private_value_only_awaitable_t = std::true_type;\n};\n\n} // namespace detail\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\ntemplate <typename Inner, auto Policy>\nclass value_or_fatal;\n\nnamespace detail {\n\n// Configuration for wrapping TaskWithExecutor-like types\ntemplate <typename Inner, auto Policy>\nstruct value_or_fatal_with_executor_cfg {\n  using InnerTaskWithExecutorT = Inner;\n  using WrapperTaskT = value_or_fatal<\n      typename Inner::folly_private_task_without_executor_t,\n      Policy>;\n  template <typename Awaitable>\n  static inline auto wrapAwaitable(Awaitable&& awaitable) noexcept {\n    static_assert(!value_only_awaitable_v<Awaitable>); // Don't double-wrap\n    return detail::ValueOrFatalAwaitable<Awaitable, Policy>{\n        folly::ext::must_use_immediately_unsafe_mover(\n            static_cast<Awaitable&&>(awaitable))()};\n  }\n};\n\ntemplate <typename Inner, auto Policy>\nusing value_or_fatal_with_executor_base = TaskWithExecutorWrapperCrtp<\n    value_or_fatal<Inner, Policy>,\n    value_or_fatal_with_executor_cfg<Inner, Policy>>;\n\ntemplate <typename... BaseArgs>\nclass value_or_fatal_task_promise_wrapper final\n    : public TaskPromiseWrapper<BaseArgs...> {};\n\n// Configuration for wrapping Task-like types\ntemplate <typename Inner, auto Policy>\nstruct value_or_fatal_cfg {\n  using ValueT = semi_await_result_t<Inner>;\n  using InnerTaskT = Inner;\n  using TaskWithExecutorT = value_or_fatal<\n      decltype(co_withExecutor(\n          FOLLY_DECLVAL(Executor::KeepAlive<>), FOLLY_DECLVAL(Inner))),\n      Policy>;\n  using PromiseT = value_or_fatal_task_promise_wrapper<\n      ValueT,\n      value_or_fatal<Inner, Policy>,\n      typename folly::coro::coroutine_traits<Inner>::promise_type>;\n  template <typename Awaitable>\n  static inline auto wrapAwaitable(Awaitable&& awaitable) noexcept {\n    static_assert(!value_only_awaitable_v<Awaitable>); // Don't double-wrap\n    return detail::ValueOrFatalAwaitable<Awaitable, Policy>{\n        static_cast<Awaitable&&>(awaitable)};\n  }\n};\n\ntemplate <typename Inner, auto Policy>\nusing value_or_fatal_base = TaskWrapperCrtp<\n    value_or_fatal<Inner, Policy>,\n    value_or_fatal_cfg<Inner, Policy>>;\n\n// Selects base class based on whether Inner has an attached executor\ntemplate <typename Inner, auto Policy>\nusing value_or_fatal_auto_base = conditional_t<\n    is_task_with_executor_v<Inner>,\n    value_or_fatal_with_executor_base<Inner, Policy>,\n    value_or_fatal_base<Inner, Policy>>;\n\n// CAUTION: `value_or_fatal_rewrapper` gives you the power to wrap and unwrap\n// `value_or_fatal`, so you must be extremely careful to preserve behavior:\n//   - The unwrapped task must be rewrapped before awaiting.\n//   - You must not wrap any other task.\n\ntemplate <typename>\nstruct value_or_fatal_rewrapper {\n  static inline constexpr bool value_or_fatal_wrapped = false;\n  static auto wrap_with(auto fn) { return fn(); }\n};\n\ntemplate <typename Inner, auto Policy>\nstruct value_or_fatal_rewrapper<value_or_fatal<Inner, Policy>> {\n  static inline constexpr bool value_or_fatal_wrapped = true;\n  static Inner unwrapTask(value_or_fatal<Inner, Policy>&& t) {\n    return std::move(t).unwrapTask();\n  }\n  static auto wrap_with(auto fn) {\n    return value_or_fatal<decltype(fn()), Policy>{fn()};\n  }\n};\n\n} // namespace detail\n\n/// `value_or_fatal<Task<T>, Policy>` wraps a task to guarantee value-only\n/// completion.\ntemplate <typename Inner, auto Policy>\nclass FOLLY_CORO_TASK_ATTRS value_or_fatal final\n    : public detail::value_or_fatal_auto_base<Inner, Policy> {\n protected:\n  using detail::value_or_fatal_auto_base<Inner, Policy>::\n      value_or_fatal_auto_base;\n\n  template <typename>\n  friend struct detail::value_or_fatal_rewrapper;\n\n public:\n  using folly_private_value_only_awaitable_t = std::true_type;\n};\n\n#endif // FOLLY_HAS_IMMOVABLE_COROUTINES\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/ViaIfAsync.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/Executor.h>\n#include <folly/Traits.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/coro/detail/Malloc.h>\n#include <folly/io/async/Request.h>\n#include <folly/lang/CustomizationPoint.h>\n#include <folly/lang/MustUseImmediately.h>\n#include <folly/lang/SafeAlias-fwd.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <glog/logging.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nnamespace detail {\n\nclass ViaCoroutinePromiseBase {\n public:\n  static void* operator new(std::size_t size) {\n    return ::folly_coro_async_malloc(size);\n  }\n\n  static void operator delete(void* ptr, std::size_t size) {\n    ::folly_coro_async_free(ptr, size);\n  }\n\n  suspend_always initial_suspend() noexcept { return {}; }\n\n  void return_void() noexcept {}\n\n  [[noreturn]] void unhandled_exception() noexcept {\n    folly::assume_unreachable();\n  }\n\n  void setExecutor(folly::Executor::KeepAlive<> executor) noexcept {\n    executor_ = std::move(executor);\n  }\n\n  void setContinuation(ExtendedCoroutineHandle continuation) noexcept {\n    continuation_ = continuation;\n  }\n\n  void setParentFrame(folly::AsyncStackFrame& parentFrame) noexcept {\n    leafFrame_.setParentFrame(parentFrame);\n  }\n\n  void setReturnAddress(void* returnAddress) noexcept {\n    leafFrame_.setReturnAddress(returnAddress);\n  }\n\n  folly::AsyncStackFrame& getLeafFrame() noexcept { return leafFrame_; }\n\n  void setRequestContext(\n      std::shared_ptr<folly::RequestContext> context) noexcept {\n    context_ = std::move(context);\n  }\n\n protected:\n  void scheduleContinuation() noexcept {\n    executor_->add([this]() noexcept { this->executeContinuation(); });\n  }\n\n private:\n  void executeContinuation() noexcept {\n    RequestContextScopeGuard contextScope{std::move(context_)};\n    if (folly::isSuspendedLeafActive(leafFrame_)) {\n      folly::deactivateSuspendedLeaf(leafFrame_);\n    }\n    if (leafFrame_.getParentFrame()) {\n      folly::resumeCoroutineWithNewAsyncStackRoot(\n          continuation_.getHandle(), *leafFrame_.getParentFrame());\n    } else {\n      continuation_.resume();\n    }\n  }\n\n protected:\n  virtual ~ViaCoroutinePromiseBase() = default;\n\n  folly::Executor::KeepAlive<> executor_;\n  ExtendedCoroutineHandle continuation_;\n  folly::AsyncStackFrame leafFrame_;\n  std::shared_ptr<RequestContext> context_;\n};\n\ntemplate <bool IsStackAware>\nclass ViaCoroutine {\n public:\n  class promise_type final\n      : public ViaCoroutinePromiseBase,\n        public ExtendedCoroutinePromiseCrtp<promise_type> {\n    struct FinalAwaiter {\n      bool await_ready() noexcept { return false; }\n\n      // This code runs immediately after the inner awaitable resumes its fake\n      // continuation, and it schedules the real continuation on the awaiter's\n      // executor\n      FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend(\n          coroutine_handle<promise_type> h) noexcept {\n        auto& promise = h.promise();\n        if (!promise.context_) {\n          promise.setRequestContext(RequestContext::saveContext());\n        }\n\n        if constexpr (IsStackAware) {\n          folly::deactivateAsyncStackFrame(promise.getAsyncFrame());\n        }\n\n        promise.scheduleContinuation();\n      }\n\n      [[noreturn]] void await_resume() noexcept { folly::assume_unreachable(); }\n    };\n\n   public:\n    ViaCoroutine get_return_object() noexcept {\n      return ViaCoroutine{coroutine_handle<promise_type>::from_promise(*this)};\n    }\n\n    FinalAwaiter final_suspend() noexcept { return {}; }\n\n    template <\n        bool IsStackAware2 = IsStackAware,\n        std::enable_if_t<IsStackAware2, int> = 0>\n    folly::AsyncStackFrame& getAsyncFrame() noexcept {\n      DCHECK(this->leafFrame_.getParentFrame() != nullptr);\n      return *this->leafFrame_.getParentFrame();\n    }\n\n    folly::AsyncStackFrame& getLeafFrame() noexcept { return leafFrame_; }\n\n    static std::optional<ExtendedCoroutineHandle::ErrorHandle>\n    getErrorHandleImpl(promise_type& me, exception_wrapper& ex) {\n      auto [handle, frame] = me.continuation_.getErrorHandle(ex);\n      me.setContinuation(handle);\n      if (frame && IsStackAware) {\n        me.leafFrame_.setParentFrame(*frame);\n      }\n      return std::nullopt;\n    }\n  };\n\n  ViaCoroutine(ViaCoroutine&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  ~ViaCoroutine() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  static ViaCoroutine create(folly::Executor::KeepAlive<> executor) {\n    ViaCoroutine coroutine = createImpl();\n    coroutine.setExecutor(std::move(executor));\n    return coroutine;\n  }\n\n  void setExecutor(folly::Executor::KeepAlive<> executor) noexcept {\n    coro_.promise().setExecutor(std::move(executor));\n  }\n\n  void setContinuation(ExtendedCoroutineHandle continuation) noexcept {\n    coro_.promise().setContinuation(continuation);\n  }\n\n  void setParentFrame(folly::AsyncStackFrame& frame) noexcept {\n    coro_.promise().setParentFrame(frame);\n  }\n\n  void setReturnAddress(void* returnAddress) noexcept {\n    coro_.promise().setReturnAddress(returnAddress);\n  }\n\n  folly::AsyncStackFrame& getLeafFrame() noexcept {\n    return coro_.promise().getLeafFrame();\n  }\n\n  void destroy() noexcept {\n    if (coro_) {\n      std::exchange(coro_, {}).destroy();\n    }\n  }\n\n  void saveContext() noexcept {\n    coro_.promise().setRequestContext(folly::RequestContext::saveContext());\n  }\n\n  coroutine_handle<promise_type> getHandle() noexcept { return coro_; }\n\n private:\n  explicit ViaCoroutine(coroutine_handle<promise_type> coro) noexcept\n      : coro_(coro) {}\n\n  static ViaCoroutine createImpl() { co_return; }\n\n  coroutine_handle<promise_type> coro_;\n};\n\n} // namespace detail\n\ntemplate <typename Awaitable>\nclass StackAwareViaIfAsyncAwaiter {\n  using WithAsyncStackAwaitable =\n      decltype(folly::coro::co_withAsyncStack(std::declval<Awaitable>()));\n  using Awaiter = folly::coro::awaiter_type_t<WithAsyncStackAwaitable>;\n  using CoroutineType = detail::ViaCoroutine<true>;\n  using CoroutinePromise = typename CoroutineType::promise_type;\n  using WrapperHandle = coroutine_handle<CoroutinePromise>;\n\n  using await_suspend_result_t =\n      decltype(std::declval<Awaiter&>().await_suspend(\n          std::declval<WrapperHandle>()));\n\n public:\n  explicit StackAwareViaIfAsyncAwaiter(\n      folly::Executor::KeepAlive<> executor, Awaitable&& awaitable)\n      : viaCoroutine_(CoroutineType::create(std::move(executor))),\n        awaitable_(\n            folly::coro::co_withAsyncStack(\n                static_cast<Awaitable&&>(awaitable))),\n        awaiter_(\n            get_awaiter(static_cast<WithAsyncStackAwaitable&&>(awaitable_))) {}\n\n  decltype(auto) await_ready() noexcept(noexcept(awaiter_.await_ready())) {\n    return awaiter_.await_ready();\n  }\n\n  template <typename Promise>\n  auto await_suspend(coroutine_handle<Promise> h) noexcept(noexcept(\n      std::declval<Awaiter&>().await_suspend(std::declval<WrapperHandle>())))\n      -> await_suspend_result_t {\n    auto& promise = h.promise();\n    auto& asyncFrame = promise.getAsyncFrame();\n\n    viaCoroutine_.setContinuation(h);\n    viaCoroutine_.setParentFrame(asyncFrame);\n\n    if constexpr (!detail::is_coroutine_handle_v<await_suspend_result_t>) {\n      viaCoroutine_.saveContext();\n    }\n\n    return awaiter_.await_suspend(viaCoroutine_.getHandle());\n  }\n\n  decltype(auto) await_resume() noexcept(noexcept(awaiter_.await_resume())) {\n    viaCoroutine_.destroy();\n    return awaiter_.await_resume();\n  }\n\n  template <\n      typename Awaiter2 = Awaiter,\n      typename Result = decltype(std::declval<Awaiter2&>().await_resume_try())>\n  Result await_resume_try() noexcept(\n      noexcept(std::declval<Awaiter&>().await_resume_try())) {\n    viaCoroutine_.destroy();\n    return awaiter_.await_resume_try();\n  }\n\n#if FOLLY_HAS_RESULT\n  template <\n      typename Awaiter2 = Awaiter,\n      typename Result =\n          decltype(FOLLY_DECLVAL(Awaiter2&).await_resume_result())>\n  Result await_resume_result() noexcept(\n      noexcept(FOLLY_DECLVAL(Awaiter2&).await_resume_result())) {\n    viaCoroutine_.destroy();\n    return awaiter_.await_resume_result();\n  }\n#endif\n\n private:\n  CoroutineType viaCoroutine_;\n  WithAsyncStackAwaitable awaitable_;\n  Awaiter awaiter_;\n};\n\ntemplate <bool IsCallerAsyncStackAware, typename Awaitable>\nclass ViaIfAsyncAwaiter {\n  using Awaiter = folly::coro::awaiter_type_t<Awaitable>;\n  using CoroutineType = detail::ViaCoroutine<false>;\n  using CoroutinePromise = typename CoroutineType::promise_type;\n  using WrapperHandle = coroutine_handle<CoroutinePromise>;\n\n  using await_suspend_result_t =\n      decltype(std::declval<Awaiter&>().await_suspend(\n          std::declval<WrapperHandle>()));\n\n public:\n  explicit ViaIfAsyncAwaiter(\n      folly::Executor::KeepAlive<> executor, Awaitable&& awaitable)\n      : viaCoroutine_(CoroutineType::create(std::move(executor))),\n        awaiter_(get_awaiter(static_cast<Awaitable&&>(awaitable))) {}\n\n  decltype(auto) await_ready() noexcept(noexcept(awaiter_.await_ready())) {\n    return awaiter_.await_ready();\n  }\n\n  // NOTE: We are using a heuristic here to determine when is the correct\n  // time to capture the RequestContext. We want to capture the context just\n  // before the coroutine suspends and execution is returned to the executor.\n  //\n  // In cases where we are awaiting another coroutine and symmetrically\n  // transferring execution to another coroutine we are not yet returning\n  // execution to the executor so we want to defer capturing the context until\n  // the ViaCoroutine is resumed and suspends in final_suspend() before\n  // scheduling the resumption on the executor.\n  //\n  // In cases where the awaitable may suspend without transferring execution\n  // to another coroutine and will therefore return back to the executor we\n  // want to capture the execution context before calling into the wrapped\n  // awaitable's await_suspend() method (since it's await_suspend() method\n  // might schedule resumption on another thread and could resume and destroy\n  // the ViaCoroutine before the await_suspend() method returns).\n  //\n  // The heuristic is that if await_suspend() returns a coroutine_handle\n  // then we assume it's the first case. Otherwise if await_suspend() returns\n  // void/bool then we assume it's the second case.\n  //\n  // This heuristic isn't perfect since a coroutine_handle-returning\n  // await_suspend() method could return noop_coroutine() in which case we\n  // could fail to capture the current context. Awaitable types that do this\n  // would need to provide a custom implementation of co_viaIfAsync() that\n  // correctly captures the RequestContext to get correct behaviour in this\n  // case.\n\n  // NO_INLINE is required here because we capture the return address of the\n  // calling coroutine\n  template <typename Promise>\n  FOLLY_NOINLINE auto\n  await_suspend(coroutine_handle<Promise> continuation) noexcept(noexcept(\n      std::declval<Awaiter&>().await_suspend(std::declval<WrapperHandle>())))\n      -> await_suspend_result_t {\n    viaCoroutine_.setContinuation(continuation);\n\n    if constexpr (!detail::is_coroutine_handle_v<await_suspend_result_t>) {\n      viaCoroutine_.saveContext();\n    }\n\n    if constexpr (IsCallerAsyncStackAware) {\n      auto& asyncFrame = continuation.promise().getAsyncFrame();\n      auto& stackRoot = *asyncFrame.getStackRoot();\n\n      viaCoroutine_.setParentFrame(asyncFrame);\n      viaCoroutine_.setReturnAddress(FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n\n      folly::deactivateAsyncStackFrame(asyncFrame);\n      folly::activateSuspendedLeaf(viaCoroutine_.getLeafFrame());\n\n      // Reactivate the stack-frame before we resume.\n      auto rollback = makeGuard([&] {\n        folly::activateAsyncStackFrame(stackRoot, asyncFrame);\n        folly::deactivateSuspendedLeaf(viaCoroutine_.getLeafFrame());\n      });\n      if constexpr (std::is_same_v<await_suspend_result_t, bool>) {\n        if (!awaiter_.await_suspend(viaCoroutine_.getHandle())) {\n          return false;\n        }\n        rollback.dismiss();\n        return true;\n      } else if constexpr (std::is_same_v<await_suspend_result_t, void>) {\n        awaiter_.await_suspend(viaCoroutine_.getHandle());\n        rollback.dismiss();\n        return;\n      } else {\n        auto ret = awaiter_.await_suspend(viaCoroutine_.getHandle());\n        rollback.dismiss();\n        return ret;\n      }\n    } else {\n      return awaiter_.await_suspend(viaCoroutine_.getHandle());\n    }\n  }\n\n  auto await_resume() noexcept(\n      noexcept(std::declval<Awaiter&>().await_resume()))\n      -> decltype(std::declval<Awaiter&>().await_resume()) {\n    viaCoroutine_.destroy();\n    return awaiter_.await_resume();\n  }\n\n  template <\n      typename Awaiter2 = Awaiter,\n      typename Result = decltype(std::declval<Awaiter2&>().await_resume_try())>\n  Result await_resume_try() noexcept(\n      noexcept(std::declval<Awaiter&>().await_resume_try())) {\n    viaCoroutine_.destroy();\n    return awaiter_.await_resume_try();\n  }\n\n#if FOLLY_HAS_RESULT\n  template <\n      typename Awaiter2 = Awaiter,\n      typename Result =\n          decltype(FOLLY_DECLVAL(Awaiter2&).await_resume_result())>\n  Result await_resume_result() noexcept(\n      noexcept(FOLLY_DECLVAL(Awaiter2&).await_resume_result())) {\n    viaCoroutine_.destroy();\n    return awaiter_.await_resume_result();\n  }\n#endif\n\n private:\n  CoroutineType viaCoroutine_;\n  Awaiter awaiter_;\n};\n\ntemplate <typename Awaitable>\nclass StackAwareViaIfAsyncAwaitable {\n public:\n  explicit StackAwareViaIfAsyncAwaitable(\n      folly::Executor::KeepAlive<> executor,\n      Awaitable&&\n          awaitable) noexcept(std::is_nothrow_move_constructible<Awaitable>::\n                                  value)\n      : executor_(std::move(executor)),\n        awaitable_(static_cast<Awaitable&&>(awaitable)) {}\n\n  auto operator co_await() && {\n    if constexpr (is_awaitable_async_stack_aware_v<Awaitable>) {\n      return StackAwareViaIfAsyncAwaiter<Awaitable>{\n          std::move(executor_), static_cast<Awaitable&&>(awaitable_)};\n    } else {\n      return ViaIfAsyncAwaiter<true, Awaitable>{\n          std::move(executor_), static_cast<Awaitable&&>(awaitable_)};\n    }\n  }\n\n private:\n  folly::Executor::KeepAlive<> executor_;\n  Awaitable awaitable_;\n};\n\ntemplate <typename Awaitable>\nclass ViaIfAsyncAwaitable {\n public:\n  explicit ViaIfAsyncAwaitable(\n      folly::Executor::KeepAlive<> executor,\n      Awaitable&&\n          awaitable) noexcept(std::is_nothrow_move_constructible<Awaitable>::\n                                  value)\n      : executor_(std::move(executor)),\n        awaitable_(static_cast<Awaitable&&>(awaitable)) {}\n\n  ViaIfAsyncAwaiter<false, Awaitable> operator co_await() && {\n    return ViaIfAsyncAwaiter<false, Awaitable>{\n        std::move(executor_), static_cast<Awaitable&&>(awaitable_)};\n  }\n\n  friend StackAwareViaIfAsyncAwaitable<Awaitable> tag_invoke(\n      cpo_t<co_withAsyncStack>, ViaIfAsyncAwaitable&& self) {\n    return StackAwareViaIfAsyncAwaitable<Awaitable>{\n        std::move(self.executor_), static_cast<Awaitable&&>(self.awaitable_)};\n  }\n\n private:\n  folly::Executor::KeepAlive<> executor_;\n  Awaitable awaitable_;\n};\n\nnamespace detail {\n\ntemplate <typename SemiAwaitable, typename = void>\nstruct HasViaIfAsyncMethod\n    : std::bool_constant<!require_sizeof<SemiAwaitable>> {};\n\ntemplate <typename SemiAwaitable>\nstruct HasViaIfAsyncMethod<\n    SemiAwaitable,\n    std::enable_if_t<std::is_void_v<SemiAwaitable>>> : std::false_type {};\n\ntemplate <typename SemiAwaitable>\nstruct HasViaIfAsyncMethod<\n    SemiAwaitable,\n    void_t<decltype(std::declval<SemiAwaitable>().viaIfAsync(\n        std::declval<folly::Executor::KeepAlive<>>()))>> : std::true_type {};\n\nnamespace adl {\n\ntemplate <typename SemiAwaitable>\nauto co_viaIfAsync(\n    folly::Executor::KeepAlive<> executor,\n    SemiAwaitable&&\n        awaitable) noexcept(noexcept(static_cast<SemiAwaitable&&>(awaitable)\n                                         .viaIfAsync(std::move(executor))))\n    -> decltype(static_cast<SemiAwaitable&&>(awaitable).viaIfAsync(\n        std::move(executor))) {\n  return static_cast<SemiAwaitable&&>(awaitable).viaIfAsync(\n      std::move(executor));\n}\n\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<\n        is_awaitable_v<Awaitable> && !HasViaIfAsyncMethod<Awaitable>::value,\n        int> = 0,\n    std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\nauto co_viaIfAsync(folly::Executor::KeepAlive<> executor, Awaitable&& awaitable)\n    -> ViaIfAsyncAwaitable<Awaitable> {\n  return ViaIfAsyncAwaitable<Awaitable>{\n      std::move(executor), static_cast<Awaitable&&>(awaitable)};\n}\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<\n        is_awaitable_v<Awaitable> && !HasViaIfAsyncMethod<Awaitable>::value,\n        int> = 0,\n    std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\nauto co_viaIfAsync(folly::Executor::KeepAlive<> executor, Awaitable awaitable)\n    -> ViaIfAsyncAwaitable<Awaitable> {\n  return ViaIfAsyncAwaitable<Awaitable>{\n      std::move(executor), std::move(awaitable)};\n}\n\nstruct ViaIfAsyncFunction {\n  template <\n      typename Awaitable,\n      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto operator()(folly::Executor::KeepAlive<> executor, Awaitable&& awaitable)\n      const noexcept(noexcept(co_viaIfAsync(\n          std::move(executor), static_cast<Awaitable&&>(awaitable))))\n          -> decltype(co_viaIfAsync(\n              std::move(executor), static_cast<Awaitable&&>(awaitable))) {\n    return co_viaIfAsync(\n        std::move(executor), static_cast<Awaitable&&>(awaitable));\n  }\n  template <\n      typename Awaitable,\n      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto operator()(folly::Executor::KeepAlive<> executor, Awaitable awaitable)\n      const noexcept(noexcept(co_viaIfAsync(\n          std::move(executor),\n          folly::ext::must_use_immediately_unsafe_mover(\n              std::move(awaitable))())))\n          -> decltype(co_viaIfAsync(\n              std::move(executor),\n              folly::ext::must_use_immediately_unsafe_mover(\n                  std::move(awaitable))())) {\n    return co_viaIfAsync(\n        std::move(executor),\n        folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))());\n  }\n};\n\n} // namespace adl\n} // namespace detail\n\n/// Returns a new awaitable that will resume execution of the awaiting coroutine\n/// on a specified executor in the case that the operation does not complete\n/// synchronously.\n///\n/// If the operation completes synchronously then the awaiting coroutine\n/// will continue execution on the current thread without transitioning\n/// execution to the specified executor.\nFOLLY_DEFINE_CPO(detail::adl::ViaIfAsyncFunction, co_viaIfAsync)\n\ntemplate <typename T>\nusing semi_await_awaitable_t = decltype(folly::coro::co_viaIfAsync(\n    FOLLY_DECLVAL(folly::Executor::KeepAlive<>), FOLLY_DECLVAL(T)));\n\ntemplate <typename T, typename = void>\nstruct is_semi_awaitable : std::bool_constant<!require_sizeof<T>> {};\n\ntemplate <typename T>\nstruct is_semi_awaitable<T, std::enable_if_t<std::is_void_v<T>>>\n    : std::false_type {};\n\ntemplate <typename T>\nstruct is_semi_awaitable<T, void_t<semi_await_awaitable_t<T>>>\n    : std::true_type {};\n\ntemplate <typename T>\nconstexpr bool is_semi_awaitable_v = is_semi_awaitable<T>::value;\n\ntemplate <typename T>\nusing semi_await_result_t = await_result_t<semi_await_awaitable_t<T>>;\n\nnamespace detail {\n\n/// Mixin for awaiters that forward to an inner awaiter with value-only\n/// semantics. Provides `await_ready()` and `await_suspend()` with static\n/// asserts that both are noexcept. Derived classes implement `await_resume()`.\n///\n/// GUIDANCE FOR NEW AWAITABLES: Design `await_ready` and `await_suspend` to\n/// be noexcept.  If they must check for invariant violations:\n///   - If feasible, push invariant failures into `await_resume()`.\n///   - Otherwise, debug-fatal on violations, and throw an error with a clearly\n///     \"do not catch me\" naming in opt.\ntemplate <typename Awaitable>\nclass ValueOnlyAwaiterBase {\n public:\n  using Awaiter = awaiter_type_t<Awaitable>;\n\n protected:\n  Awaiter awaiter_;\n\n  explicit ValueOnlyAwaiterBase(Awaitable&& awaitable)\n      : awaiter_(get_awaiter(static_cast<Awaitable&&>(awaitable))) {}\n\n public:\n  auto await_ready() noexcept -> decltype(awaiter_.await_ready()) {\n    static_assert(\n        noexcept(FOLLY_DECLVAL(Awaiter&).await_ready()),\n        \"value-only await requires noexcept await_ready(); see class doc\");\n    return awaiter_.await_ready();\n  }\n\n  template <typename Promise>\n  auto await_suspend(coroutine_handle<Promise> coro) noexcept\n      -> decltype(awaiter_.await_suspend(coro)) {\n    static_assert(\n        noexcept(awaiter_.await_suspend(coro)),\n        \"value-only await requires noexcept await_suspend(); see class doc\");\n    return awaiter_.await_suspend(coro);\n  }\n};\n\ntemplate <typename T>\nusing value_only_awaitable_of_ =\n    typename T::folly_private_value_only_awaitable_t;\n\ntemplate <typename Void, typename T>\nstruct value_only_awaitable_ {\n  static_assert(\n      require_sizeof<T>, \"`value_only_awaitable_t` on incomplete type\");\n  using type = std::false_type;\n};\n\ntemplate <>\nstruct value_only_awaitable_<void, void> {\n  using type = std::false_type;\n};\n\ntemplate <typename T>\nstruct value_only_awaitable_<void_t<value_only_awaitable_of_<T>>, T> {\n  using type = value_only_awaitable_of_<T>;\n};\n\n} // namespace detail\n\n/// True for awaitables that only complete with value (not stopped or error)\ntemplate <typename T>\nusing value_only_awaitable_t =\n    typename detail::value_only_awaitable_<void, T>::type;\ntemplate <typename T>\ninline constexpr bool value_only_awaitable_v = value_only_awaitable_t<T>::value;\n\nnamespace detail {\n\ntemplate <typename Awaiter>\nusing detect_await_resume_try =\n    decltype(FOLLY_DECLVAL(Awaiter).await_resume_try());\n\ntemplate <typename Awaiter>\nconstexpr bool is_awaiter_try = is_detected_v<detect_await_resume_try, Awaiter>;\n\ntemplate <typename Awaitable>\nconstexpr bool is_awaitable_try = is_awaiter_try<awaiter_type_t<Awaitable>>;\n\ntemplate <typename Awaitable>\nclass TryAwaiter : public ValueOnlyAwaiterBase<Awaitable> {\n public:\n  static_assert(is_awaitable_try<Awaitable&&>);\n\n  explicit TryAwaiter(Awaitable&& awaitable)\n      : ValueOnlyAwaiterBase<Awaitable>(static_cast<Awaitable&&>(awaitable)) {}\n\n  auto await_resume()\n      FOLLY_DETAIL_FORWARD_BODY(this->awaiter_.await_resume_try())\n};\n\n/**\n * Common machinery for building wrappers like co_awaitTry\n * Allows the wrapper to commute with the universal wrappers like\n * co_withCancellation while keeping the corresponding awaitable on the outside\n */\ntemplate <template <typename T> typename Derived, typename T>\nclass CommutativeWrapperAwaitable {\n public:\n  template <\n      typename T2,\n      std::enable_if_t<!folly::ext::must_use_immediately_v<T2>, int> = 0>\n  explicit CommutativeWrapperAwaitable(T2&& awaitable) noexcept(\n      std::is_nothrow_constructible_v<T, T2>)\n      : inner_(static_cast<T2&&>(awaitable)) {}\n  template <\n      typename T2,\n      std::enable_if_t<folly::ext::must_use_immediately_v<T2>, int> = 0>\n  explicit CommutativeWrapperAwaitable(T2 awaitable) noexcept(noexcept(T{\n      FOLLY_DECLVAL(T2)}))\n      : inner_(\n            folly::ext::must_use_immediately_unsafe_mover(\n                std::move(awaitable))()) {}\n\n  template <typename Factory>\n  explicit CommutativeWrapperAwaitable(std::in_place_t, Factory&& factory)\n      : inner_(factory()) {}\n\n  // Two overloads for the CancellationToken to avoid unnecessary copies\n  // (atomic refcount costs).\n  //\n  // NB: If we merged the overloads into a single template, overload resolution\n  // rules would consider it ambiguous wrt the default implementation in\n  // `WithCancellation.h`.\n  template <\n      typename T2 = T,\n      // \"WART:\" in `WithCancellation.h` explains the remove-reference\n      typename Result =\n          std::remove_reference_t<decltype(folly::coro::co_withCancellation(\n              FOLLY_DECLVAL(const folly::CancellationToken&),\n              FOLLY_DECLVAL(T2)))>>\n  friend Derived<Result> co_withCancellation(\n      const folly::CancellationToken& cancelToken, Derived<T> awaitable) {\n    return Derived<Result>{\n        std::in_place, [&]() {\n          return folly::coro::co_withCancellation(\n              cancelToken,\n              folly::ext::must_use_immediately_unsafe_mover(\n                  std::move(awaitable.inner_))());\n        }};\n  }\n  template <\n      typename T2 = T,\n      // \"WART:\" in `WithCancellation.h` explains the remove-reference\n      typename Result =\n          std::remove_reference_t<decltype(folly::coro::co_withCancellation(\n              FOLLY_DECLVAL(folly::CancellationToken&&), FOLLY_DECLVAL(T2)))>>\n  friend Derived<Result> co_withCancellation(\n      folly::CancellationToken&& cancelToken, Derived<T> awaitable) {\n    return Derived<Result>{\n        std::in_place, [&]() {\n          return folly::coro::co_withCancellation(\n              std::move(cancelToken),\n              folly::ext::must_use_immediately_unsafe_mover(\n                  std::move(awaitable.inner_))());\n        }};\n  }\n\n  template <\n      typename T2 = T,\n      typename Result =\n          decltype(folly::coro::co_withAsyncStack(std::declval<T2>()))>\n  friend Derived<Result>\n  tag_invoke(cpo_t<co_withAsyncStack>, Derived<T>&& awaitable) noexcept(\n      noexcept(folly::coro::co_withAsyncStack(std::declval<T2>()))) {\n    return Derived<Result>{\n        std::in_place, [&]() -> decltype(auto) {\n          return folly::coro::co_withAsyncStack(\n              static_cast<T&&>(awaitable.inner_));\n        }};\n  }\n\n  template <typename T2 = T, typename Result = semi_await_awaitable_t<T2>>\n  friend Derived<Result> co_viaIfAsync(\n      folly::Executor::KeepAlive<> executor,\n      Derived<T> awaitable) //\n      noexcept(noexcept(folly::coro::co_viaIfAsync(\n          FOLLY_DECLVAL(folly::Executor::KeepAlive<>), FOLLY_DECLVAL(T2)))) {\n    return Derived<Result>{\n        std::in_place, [&]() {\n          return folly::coro::co_viaIfAsync(\n              std::move(executor),\n              folly::ext::must_use_immediately_unsafe_mover(\n                  std::move(awaitable.inner_))());\n        }};\n  }\n\n  // IMPORTANT: If a commutative wrapper changes safety, immediate- or\n  // noexcept-awaitability, it must remember to override these:\n  using folly_must_use_immediately_t = ext::must_use_immediately_t<T>;\n  using folly_private_value_only_awaitable_t = value_only_awaitable_t<T>;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t = safe_alias_of<T, Default>;\n\n protected:\n  T inner_;\n\n private:\n  template <typename U>\n  using my_curried_mover = folly::ext::curried_unsafe_mover_t<\n      U,\n      decltype(folly::ext::must_use_immediately_unsafe_mover(\n          FOLLY_DECLVAL(T)))>;\n\n public:\n  template <\n      typename Me, // not a forwarding ref, see SFINAE\n      typename T2 = T,\n      std::enable_if_t<\n          // This check guards against misuse (+ fails on lvalue refs)\n          // See `wrap_must_use_immediately_t::unsafe_mover` for more context\n          std::is_base_of_v<CommutativeWrapperAwaitable, Me> &&\n              // Without this check we might instantiate this for things like\n              // `TryAwaitable<coro::Future<...>&&>`, erroring with:\n              //   \"cannot form a pointer-to-member to member of reference type\"\n              folly::ext::must_use_immediately_v<T2>,\n          int> = 0>\n  static my_curried_mover<Me> unsafe_mover(\n      folly::ext::must_use_immediately_private_t, Me&& me) noexcept {\n    return folly::ext::curried_unsafe_mover_from_bases_and_members<\n        CommutativeWrapperAwaitable>(\n        folly::tag</*no bases*/>,\n        folly::vtag<&CommutativeWrapperAwaitable::inner_>,\n        static_cast<Me&&>(me));\n  }\n  template <\n      typename DerivedFromMe,\n      // Matches the SFINAE logic in our `unsafe_mover`\n      std::enable_if_t<\n          std::is_base_of_v<CommutativeWrapperAwaitable, DerivedFromMe>,\n          int> = 0>\n  explicit CommutativeWrapperAwaitable(\n      folly::ext::curried_unsafe_mover_private_t,\n      my_curried_mover<DerivedFromMe>&& mover)\n      // `must_use_immediately_unsafe_mover` has more `noexcept` assertions\n      noexcept(noexcept(T{std::move(mover.template get<0>())()}))\n      : inner_{std::move(mover.template get<0>())()} {}\n};\n\ntemplate <typename T>\nclass [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE]]\nTryAwaitable : public CommutativeWrapperAwaitable<TryAwaitable, T> {\n public:\n  using CommutativeWrapperAwaitable<TryAwaitable, T>::\n      CommutativeWrapperAwaitable;\n\n  template <\n      typename Self,\n      std::enable_if_t<\n          std::is_same_v<remove_cvref_t<Self>, TryAwaitable>,\n          int> = 0,\n      typename T2 = like_t<Self, T>,\n      std::enable_if_t<is_awaitable_v<T2>, int> = 0>\n  friend TryAwaiter<T2> operator co_await(Self&& self) {\n    return TryAwaiter<T2>{static_cast<Self&&>(self).inner_};\n  }\n\n  using folly_private_value_only_awaitable_t = std::true_type;\n};\n\n} // namespace detail\n\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\ndetail::TryAwaitable<remove_cvref_t<Awaitable>> co_awaitTry(\n    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable&& awaitable) {\n  return detail::TryAwaitable<remove_cvref_t<Awaitable>>{\n      static_cast<Awaitable&&>(awaitable)};\n}\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\ndetail::TryAwaitable<Awaitable> co_awaitTry(\n    [[FOLLY_ATTR_CLANG_CORO_AWAIT_ELIDABLE_ARGUMENT]] Awaitable awaitable) {\n  return detail::TryAwaitable<Awaitable>{\n      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))()};\n}\n\ntemplate <typename T>\nusing semi_await_try_result_t = await_result_t<semi_await_awaitable_t<\n    decltype(folly::coro::co_awaitTry(FOLLY_DECLVAL(T)))>>;\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/WithAsyncStack.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/CustomizationPoint.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <cassert>\n#include <type_traits>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nnamespace detail {\n\nclass WithAsyncStackCoroutine {\n public:\n  class promise_type {\n   public:\n    WithAsyncStackCoroutine get_return_object() noexcept {\n      return WithAsyncStackCoroutine{\n          coroutine_handle<promise_type>::from_promise(*this)};\n    }\n\n    suspend_always initial_suspend() noexcept { return {}; }\n\n    struct FinalAwaiter {\n      bool await_ready() noexcept { return false; }\n      void await_suspend(coroutine_handle<promise_type> h) noexcept {\n        auto& promise = h.promise();\n        folly::deactivateSuspendedLeaf(promise.getLeafFrame());\n        folly::resumeCoroutineWithNewAsyncStackRoot(\n            promise.continuation_, *promise.getLeafFrame().getParentFrame());\n      }\n\n      [[noreturn]] void await_resume() noexcept { folly::assume_unreachable(); }\n    };\n\n    FinalAwaiter final_suspend() noexcept { return {}; }\n\n    void return_void() noexcept {}\n\n    [[noreturn]] void unhandled_exception() noexcept {\n      folly::assume_unreachable();\n    }\n\n    folly::AsyncStackFrame& getLeafFrame() noexcept { return leafFrame; }\n\n   private:\n    friend WithAsyncStackCoroutine;\n\n    coroutine_handle<> continuation_;\n    folly::AsyncStackFrame leafFrame;\n  };\n\n  WithAsyncStackCoroutine() noexcept : coro_() {}\n\n  WithAsyncStackCoroutine(WithAsyncStackCoroutine&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  ~WithAsyncStackCoroutine() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  WithAsyncStackCoroutine& operator=(WithAsyncStackCoroutine other) noexcept {\n    std::swap(coro_, other.coro_);\n    return *this;\n  }\n\n  static WithAsyncStackCoroutine create() { co_return; }\n\n  template <typename Promise>\n  coroutine_handle<promise_type> getWrapperHandleFor(\n      coroutine_handle<Promise> h, void* returnAddress) noexcept {\n    auto& promise = coro_.promise();\n    promise.continuation_ = h;\n    promise.getLeafFrame().setParentFrame(h.promise().getAsyncFrame());\n    promise.getLeafFrame().setReturnAddress(returnAddress);\n    return coro_;\n  }\n\n  folly::AsyncStackFrame& getLeafFrame() noexcept {\n    return coro_.promise().getLeafFrame();\n  }\n\n private:\n  explicit WithAsyncStackCoroutine(coroutine_handle<promise_type> h) noexcept\n      : coro_(h) {}\n\n  coroutine_handle<promise_type> coro_;\n};\n\ntemplate <typename Awaitable>\nclass WithAsyncStackAwaiter {\n  using Awaiter = awaiter_type_t<Awaitable>;\n\n  static constexpr bool kSuspendIsNoexcept =\n      noexcept(FOLLY_DECLVAL(Awaiter&).await_suspend(FOLLY_DECLVAL(\n          coroutine_handle<WithAsyncStackCoroutine::promise_type>)));\n\n public:\n  explicit WithAsyncStackAwaiter(Awaitable&& awaitable)\n      : awaiter_(get_awaiter(static_cast<Awaitable&&>(awaitable))),\n        coroWrapper_(WithAsyncStackCoroutine::create()) {}\n\n  auto await_ready() noexcept(noexcept(std::declval<Awaiter&>().await_ready()))\n      -> decltype(std::declval<Awaiter&>().await_ready()) {\n    return awaiter_.await_ready();\n  }\n\n  // needs to be no-inline as return address is being captured for async stack\n  // tracing\n  template <typename Promise>\n  FOLLY_NOINLINE auto await_suspend(coroutine_handle<Promise> h) noexcept(\n      kSuspendIsNoexcept) {\n    AsyncStackFrame& callerFrame = h.promise().getAsyncFrame();\n    AsyncStackRoot* stackRoot = callerFrame.getStackRoot();\n    assert(stackRoot != nullptr);\n\n    auto wrapperHandle =\n        coroWrapper_.getWrapperHandleFor(h, FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n\n    folly::deactivateAsyncStackFrame(callerFrame);\n    folly::activateSuspendedLeaf(coroWrapper_.getLeafFrame());\n\n    using await_suspend_result_t =\n        decltype(awaiter_.await_suspend(wrapperHandle));\n\n    auto doSuspend = [&]() {\n      if constexpr (std::is_same_v<await_suspend_result_t, bool>) {\n        if (!awaiter_.await_suspend(wrapperHandle)) {\n          folly::activateAsyncStackFrame(*stackRoot, callerFrame);\n          folly::deactivateSuspendedLeaf(coroWrapper_.getLeafFrame());\n          return false;\n        }\n        return true;\n      } else {\n        return awaiter_.await_suspend(wrapperHandle);\n      }\n    };\n\n    if constexpr (kSuspendIsNoexcept) {\n      return doSuspend();\n    } else {\n      // Restore async stack state on exception, then rethrow.\n      try {\n        return doSuspend();\n      } catch (...) {\n        folly::activateAsyncStackFrame(*stackRoot, callerFrame);\n        folly::deactivateSuspendedLeaf(coroWrapper_.getLeafFrame());\n        throw;\n      }\n    }\n  }\n\n  auto await_resume() noexcept(\n      noexcept(std::declval<Awaiter&>().await_resume()))\n      -> decltype(std::declval<Awaiter&>().await_resume()) {\n    coroWrapper_ = WithAsyncStackCoroutine();\n    return awaiter_.await_resume();\n  }\n\n  template <typename Awaiter2 = Awaiter>\n  auto await_resume_try() noexcept(\n      noexcept(std::declval<Awaiter2&>().await_resume_try()))\n      -> decltype(std::declval<Awaiter2&>().await_resume_try()) {\n    coroWrapper_ = WithAsyncStackCoroutine();\n    return awaiter_.await_resume_try();\n  }\n\n#if FOLLY_HAS_RESULT\n  template <typename Awaiter2 = Awaiter>\n  auto await_resume_result() noexcept(\n      noexcept(FOLLY_DECLVAL(Awaiter2&).await_resume_result()))\n      -> decltype(FOLLY_DECLVAL(Awaiter2&).await_resume_result()) {\n    coroWrapper_ = WithAsyncStackCoroutine();\n    return awaiter_.await_resume_result();\n  }\n#endif\n\n private:\n  awaiter_type_t<Awaitable> awaiter_;\n  WithAsyncStackCoroutine coroWrapper_;\n};\n\ntemplate <typename Awaitable>\nclass WithAsyncStackAwaitable {\n public:\n  explicit WithAsyncStackAwaitable(Awaitable&& awaitable)\n      : awaitable_(static_cast<Awaitable&&>(awaitable)) {}\n\n  WithAsyncStackAwaiter<Awaitable&> operator co_await() & {\n    return WithAsyncStackAwaiter<Awaitable&>{awaitable_};\n  }\n\n  WithAsyncStackAwaiter<Awaitable> operator co_await() && {\n    return WithAsyncStackAwaiter<Awaitable>{\n        static_cast<Awaitable&&>(awaitable_)};\n  }\n\n private:\n  Awaitable awaitable_;\n};\n\nstruct WithAsyncStackFunction {\n  // Dispatches to a custom implementation using tag_invoke()\n  template <\n      typename Awaitable,\n      std::enable_if_t<\n          folly::is_tag_invocable_v<WithAsyncStackFunction, Awaitable>,\n          int> = 0>\n  auto operator()(Awaitable&& awaitable) const noexcept(\n      folly::is_nothrow_tag_invocable_v<WithAsyncStackFunction, Awaitable>)\n      -> folly::tag_invoke_result_t<WithAsyncStackFunction, Awaitable> {\n    return folly::tag_invoke(\n        WithAsyncStackFunction{}, static_cast<Awaitable&&>(awaitable));\n  }\n\n  // Fallback implementation. Wraps the awaitable in the\n  // WithAsyncStackAwaitable which just saves/restores the\n  // awaiting coroutine's AsyncStackFrame.\n  template <\n      typename Awaitable,\n      std::enable_if_t<\n          !folly::is_tag_invocable_v<WithAsyncStackFunction, Awaitable>,\n          int> = 0,\n      std::enable_if_t<folly::coro::is_awaitable_v<Awaitable>, int> = 0>\n  WithAsyncStackAwaitable<Awaitable> operator()(Awaitable&& awaitable) const\n      noexcept(std::is_nothrow_move_constructible_v<Awaitable>) {\n    return WithAsyncStackAwaitable<Awaitable>{\n        static_cast<Awaitable&&>(awaitable)};\n  }\n};\n\n} // namespace detail\n\ntemplate <typename Awaitable>\ninline constexpr bool is_awaitable_async_stack_aware_v =\n    folly::is_tag_invocable_v<detail::WithAsyncStackFunction, Awaitable>;\n\n// Coroutines that support the AsyncStack protocol will apply the\n// co_withAsyncStack() customisation-point to an awaitable inside its\n// await_transform() to ensure that the current coroutine's AsyncStackFrame\n// is saved and later restored when the coroutine resumes.\n//\n// The default implementation is used for awaitables that don't know\n// about the AsyncStackFrame and just wraps the awaitable to ensure\n// that the stack-frame is saved/restored if the coroutine suspends.\n//\n// Awaitables that know about the AsyncStackFrame protocol can customise\n// this CPO by defining an overload of tag_invoke() for this CPO\n// for their type.\n//\n// For example:\n//   class MyAwaitable {\n//     friend MyAwaitable&& tag_invoke(\n//         cpo_t<folly::coro::co_withAsyncStack>, MyAwaitable&& awaitable) {\n//       return std::move(awaitable);\n//     }\n//\n//     ...\n//   };\n//\n// If you customise this CPO then it is your responsibility to ensure that\n// if the awaiting coroutine suspends then before the coroutine is resumed\n// that its original AsyncStackFrame is activated on the current thread.\n// e.g. using folly::activateAsyncStackFrame()\n//\n// The awaiting coroutine's AsyncStackFrame can be obtained from its\n// promise, which is assumed to have a 'AsyncStackFrame& getAsyncFrame()'\n// method that returns a reference to the parent coroutine's async frame.\n\nFOLLY_DEFINE_CPO(detail::WithAsyncStackFunction, co_withAsyncStack)\n\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/WithCancellation.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/lang/CustomizationPoint.h>\n#include <folly/lang/MustUseImmediately.h>\n\n#if FOLLY_HAS_COROUTINES\n\n/**\n * \\file coro/WithCancellation.h\n * co_withCancellation allows caller to pass in a cancellation token to a\n * awaitable\n *\n * \\refcode folly/docs/examples/folly/coro/WithCancellation.cpp\n */\n\nnamespace folly {\nnamespace coro {\n\nnamespace detail {\nnamespace adl {\n\n/// Default implementation that does not hook the cancellation token.\n/// Types must opt-in to hooking cancellation by customising this function.\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n// WART: The `Awaitable&&` overload must return `Awaitable&&` to support\n// `Baton`s, which is immovable and awaitable only by lvalue.  We could remove\n// this, and simplify other code, by migrating `Baton`s to emit an explicit\n// must-use-immediately awaitable instead of awaiting them by lvalue.\nAwaitable&& co_withCancellation(\n    const folly::CancellationToken&, Awaitable&& awaitable) noexcept {\n  return static_cast<Awaitable&&>(awaitable);\n}\ntemplate <\n    typename Awaitable,\n    std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\nAwaitable co_withCancellation(\n    const folly::CancellationToken&, Awaitable awaitable) noexcept {\n  return folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))();\n}\n\nstruct WithCancellationFunction {\n  template <\n      typename Awaitable,\n      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto operator()(\n      const folly::CancellationToken& cancelToken, Awaitable&& awaitable) const\n      noexcept(noexcept(co_withCancellation(\n          cancelToken, static_cast<Awaitable&&>(awaitable))))\n          -> decltype(co_withCancellation(\n              cancelToken, static_cast<Awaitable&&>(awaitable))) {\n    return co_withCancellation(\n        cancelToken, static_cast<Awaitable&&>(awaitable));\n  }\n  template <\n      typename Awaitable,\n      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto operator()(\n      const folly::CancellationToken& cancelToken, Awaitable awaitable) const\n      noexcept(noexcept(co_withCancellation(\n          cancelToken,\n          folly::ext::must_use_immediately_unsafe_mover(\n              std::move(awaitable))())))\n          -> decltype(co_withCancellation(\n              cancelToken,\n              folly::ext::must_use_immediately_unsafe_mover(\n                  std::move(awaitable))())) {\n    return co_withCancellation(\n        cancelToken,\n        folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))());\n  }\n  template <\n      typename Awaitable,\n      std::enable_if_t<!folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto operator()(folly::CancellationToken&& cancelToken, Awaitable&& awaitable)\n      const noexcept(noexcept(co_withCancellation(\n          std::move(cancelToken), static_cast<Awaitable&&>(awaitable))))\n          -> decltype(co_withCancellation(\n              std::move(cancelToken), static_cast<Awaitable&&>(awaitable))) {\n    return co_withCancellation(\n        std::move(cancelToken), static_cast<Awaitable&&>(awaitable));\n  }\n  template <\n      typename Awaitable,\n      std::enable_if_t<folly::ext::must_use_immediately_v<Awaitable>, int> = 0>\n  auto operator()(folly::CancellationToken&& cancelToken, Awaitable awaitable)\n      const noexcept(noexcept(co_withCancellation(\n          std::move(cancelToken),\n          folly::ext::must_use_immediately_unsafe_mover(\n              std::move(awaitable))())))\n          -> decltype(co_withCancellation(\n              std::move(cancelToken),\n              folly::ext::must_use_immediately_unsafe_mover(\n                  std::move(awaitable))())) {\n    return co_withCancellation(\n        std::move(cancelToken),\n        folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))());\n  }\n};\n} // namespace adl\n} // namespace detail\n\nFOLLY_DEFINE_CPO(detail::adl::WithCancellationFunction, co_withCancellation)\n\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"pick_task_wrapper\",\n    headers = [\"PickTaskWrapper.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly/lang:safe_alias_fwd\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/detail/Barrier.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <atomic>\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\n// A Barrier is a synchronisation building block that can be used to\n// implement higher-level coroutine-based synchronisation primitives.\n//\n// It allows a single coroutine to wait until a counter reaches zero.\n// The counter typically represents the amount of outstanding work.\n// When a coroutine completes some work it should call arrive() which\n// will return a continuation.\nclass Barrier {\n public:\n  explicit Barrier(std::size_t initialCount = 0) noexcept\n      : count_(initialCount) {}\n\n  void add(std::size_t count = 1) noexcept {\n    [[maybe_unused]] std::size_t oldCount =\n        count_.fetch_add(count, std::memory_order_relaxed);\n    // Check we didn't overflow the count.\n    assert(SIZE_MAX - oldCount >= count);\n  }\n\n  // Query the number of remaining tasks that the barrier is waiting\n  // for. This indicates the number of arrive() calls that must be\n  // made before the Barrier will be released.\n  //\n  // Note that this should just be used as an approximate guide\n  // for the number of outstanding tasks. This value may be out\n  // of date immediately upon being returned.\n  std::size_t remaining() const noexcept {\n    return count_.load(std::memory_order_acquire);\n  }\n\n  [[nodiscard]] coroutine_handle<> arrive(\n      folly::AsyncStackFrame& currentFrame) noexcept {\n    auto& stackRoot = *currentFrame.getStackRoot();\n    folly::deactivateAsyncStackFrame(currentFrame);\n\n    const std::size_t oldCount = count_.fetch_sub(1, std::memory_order_acq_rel);\n\n    // Invalid to call arrive() if you haven't previously incremented the\n    // counter using .add().\n    assert(oldCount >= 1);\n\n    if (oldCount == 1) {\n      if (asyncFrame_ != nullptr) {\n        folly::activateAsyncStackFrame(stackRoot, *asyncFrame_);\n      }\n      return std::exchange(continuation_, {});\n    } else {\n      return coro::noop_coroutine();\n    }\n  }\n\n  [[nodiscard]] coroutine_handle<> arrive() noexcept {\n    const std::size_t oldCount = count_.fetch_sub(1, std::memory_order_acq_rel);\n\n    // Invalid to call arrive() if you haven't previously incremented the\n    // counter using .add().\n    assert(oldCount >= 1);\n\n    if (oldCount == 1) {\n      auto coro = std::exchange(continuation_, {});\n      if (asyncFrame_ != nullptr) {\n        folly::resumeCoroutineWithNewAsyncStackRoot(coro, *asyncFrame_);\n        return coro::noop_coroutine();\n      } else {\n        return coro;\n      }\n    } else {\n      return coro::noop_coroutine();\n    }\n  }\n\n private:\n  class Awaiter {\n   public:\n    explicit Awaiter(Barrier& barrier) noexcept : barrier_(barrier) {}\n\n    bool await_ready() noexcept { return false; }\n\n    template <typename Promise>\n    coroutine_handle<> await_suspend(\n        coroutine_handle<Promise> continuation) noexcept {\n      if constexpr (detail::promiseHasAsyncFrame_v<Promise>) {\n        barrier_.setContinuation(\n            continuation, &continuation.promise().getAsyncFrame());\n        return barrier_.arrive(continuation.promise().getAsyncFrame());\n      } else {\n        barrier_.setContinuation(continuation, nullptr);\n        return barrier_.arrive();\n      }\n    }\n\n    void await_resume() noexcept {}\n\n   private:\n    friend Awaiter tag_invoke(\n        cpo_t<co_withAsyncStack>, Awaiter&& awaiter) noexcept {\n      return Awaiter{awaiter.barrier_};\n    }\n\n    Barrier& barrier_;\n  };\n\n public:\n  auto arriveAndWait() noexcept { return Awaiter{*this}; }\n\n  void setContinuation(\n      coroutine_handle<> continuation,\n      folly::AsyncStackFrame* parentFrame) noexcept {\n    assert(!continuation_);\n    continuation_ = continuation;\n    asyncFrame_ = parentFrame;\n  }\n\n private:\n  std::atomic<std::size_t> count_;\n  coroutine_handle<> continuation_;\n  folly::AsyncStackFrame* asyncFrame_ = nullptr;\n};\n\n} // namespace detail\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/detail/BarrierTask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/coro/detail/Barrier.h>\n#include <folly/coro/detail/Malloc.h>\n\n#include <cassert>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\nclass BarrierTask {\n public:\n  class promise_type {\n    struct FinalAwaiter {\n      bool await_ready() noexcept { return false; }\n\n      coroutine_handle<> await_suspend(\n          coroutine_handle<promise_type> h) noexcept {\n        auto& promise = h.promise();\n        assert(promise.barrier_ != nullptr);\n        return promise.barrier_->arrive(promise.asyncFrame_);\n      }\n\n      void await_resume() noexcept {}\n    };\n\n   public:\n    static void* operator new(std::size_t size) {\n      return ::folly_coro_async_malloc(size);\n    }\n\n    static void operator delete(void* ptr, std::size_t size) {\n      ::folly_coro_async_free(ptr, size);\n    }\n\n    BarrierTask get_return_object() noexcept {\n      return BarrierTask{coroutine_handle<promise_type>::from_promise(*this)};\n    }\n\n    suspend_always initial_suspend() noexcept { return {}; }\n\n    FinalAwaiter final_suspend() noexcept { return {}; }\n\n    template <typename Awaitable>\n    auto await_transform(Awaitable&& awaitable) {\n      return folly::coro::co_withAsyncStack(\n          static_cast<Awaitable&&>(awaitable));\n    }\n\n    void return_void() noexcept {}\n\n    [[noreturn]] void unhandled_exception() noexcept { std::terminate(); }\n\n    void setBarrier(Barrier* barrier) noexcept {\n      assert(barrier_ == nullptr);\n      barrier_ = barrier;\n    }\n\n    folly::AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }\n\n   private:\n    folly::AsyncStackFrame asyncFrame_;\n    Barrier* barrier_ = nullptr;\n  };\n\n private:\n  using handle_t = coroutine_handle<promise_type>;\n\n  explicit BarrierTask(handle_t coro) noexcept : coro_(coro) {}\n\n public:\n  BarrierTask(BarrierTask&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  ~BarrierTask() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  BarrierTask& operator=(BarrierTask other) noexcept {\n    swap(other);\n    return *this;\n  }\n\n  void swap(BarrierTask& b) noexcept { std::swap(coro_, b.coro_); }\n\n  FOLLY_NOINLINE void start(Barrier* barrier) noexcept {\n    start(barrier, folly::getDetachedRootAsyncStackFrame());\n  }\n\n  FOLLY_NOINLINE void start(\n      Barrier* barrier, folly::AsyncStackFrame& parentFrame) noexcept {\n    assert(coro_);\n    auto& calleeFrame = coro_.promise().getAsyncFrame();\n    calleeFrame.setParentFrame(parentFrame);\n    calleeFrame.setReturnAddress();\n    coro_.promise().setBarrier(barrier);\n\n    folly::resumeCoroutineWithNewAsyncStackRoot(coro_);\n  }\n\n private:\n  handle_t coro_;\n};\n\nclass DetachedBarrierTask {\n public:\n  class promise_type {\n   public:\n    promise_type() noexcept {\n      asyncFrame_.setParentFrame(folly::getDetachedRootAsyncStackFrame());\n    }\n\n    DetachedBarrierTask get_return_object() noexcept {\n      return DetachedBarrierTask{\n          coroutine_handle<promise_type>::from_promise(*this)};\n    }\n\n    suspend_always initial_suspend() noexcept { return {}; }\n\n    auto final_suspend() noexcept {\n      struct awaiter {\n        bool await_ready() noexcept { return false; }\n        auto await_suspend(coroutine_handle<promise_type> h) noexcept {\n          assert(h.promise().barrier_ != nullptr);\n          auto continuation =\n              h.promise().barrier_->arrive(h.promise().getAsyncFrame());\n\n          // Due to a bug in MSVC versions up to and including 19.39, we observe\n          // an extra call to the destructor of the task with an explicit call\n          // to coroutine_handle::destroy. Furthermore, with versions\n          // above 19.30, this causes a crash when named return value\n          // optimization is enabled.\n#if !(!defined(__clang__) && defined(_MSC_VER) && _MSC_VER <= 1939)\n          h.destroy();\n#endif\n\n          return continuation;\n        }\n        void await_resume() noexcept {}\n      };\n      return awaiter{};\n    }\n\n    [[noreturn]] void unhandled_exception() noexcept { std::terminate(); }\n\n    void return_void() noexcept {}\n\n    template <typename Awaitable>\n    auto await_transform(Awaitable&& awaitable) {\n      return folly::coro::co_withAsyncStack(\n          static_cast<Awaitable&&>(awaitable));\n    }\n\n    void setBarrier(Barrier* barrier) noexcept { barrier_ = barrier; }\n\n    AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }\n\n   private:\n    AsyncStackFrame asyncFrame_;\n    Barrier* barrier_;\n  };\n\n private:\n  using handle_t = coroutine_handle<promise_type>;\n\n  explicit DetachedBarrierTask(handle_t coro) : coro_(coro) {}\n\n public:\n  DetachedBarrierTask(DetachedBarrierTask&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  ~DetachedBarrierTask() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  FOLLY_NOINLINE void start(Barrier* barrier) && noexcept {\n    std::move(*this).start(barrier, FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  }\n\n  FOLLY_NOINLINE void start(\n      Barrier* barrier, folly::AsyncStackFrame& parentFrame) && noexcept {\n    assert(coro_);\n    coro_.promise().getAsyncFrame().setParentFrame(parentFrame);\n    std::move(*this).start(barrier, FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  }\n\n  void start(Barrier* barrier, void* returnAddress) && noexcept {\n    assert(coro_);\n    assert(barrier != nullptr);\n    barrier->add(1);\n    auto coro = std::exchange(coro_, {});\n    coro.promise().setBarrier(barrier);\n    coro.promise().getAsyncFrame().setReturnAddress(returnAddress);\n    folly::resumeCoroutineWithNewAsyncStackRoot(coro);\n  }\n\n private:\n  handle_t coro_;\n};\n\n} // namespace detail\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME pick_task_wrapper\n  HEADERS\n    PickTaskWrapper.h\n  EXPORTED_DEPS\n    folly_lang_safe_alias_fwd\n    folly_portability\n)\n"
  },
  {
    "path": "folly/coro/detail/CurrentAsyncFrame.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * This macro enables FbSystrace usage in production for fb4a. When\n * FOLLY_SCOPED_TRACE_SECTION_HEADER is defined then a trace section is started\n * and later automatically terminated at the close of the scope it is called in.\n * In all other cases no action is taken.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/tracing/AsyncStack.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\n// Helper struct for getting access to the current coroutine's AsyncStackFrame\nclass CurrentAsyncStackFrameAwaitable {\n  class Awaiter {\n   public:\n    bool await_ready() noexcept { return false; }\n\n    template <typename Promise>\n    bool await_suspend(coroutine_handle<Promise> h) noexcept {\n      asyncFrame_ = &h.promise().getAsyncFrame();\n      return false;\n    }\n\n    folly::AsyncStackFrame& await_resume() noexcept { return *asyncFrame_; }\n\n   private:\n    folly::AsyncStackFrame* asyncFrame_ = nullptr;\n  };\n\n public:\n  CurrentAsyncStackFrameAwaitable viaIfAsync(\n      const folly::Executor::KeepAlive<>&) const noexcept {\n    return {};\n  }\n\n  friend Awaiter tag_invoke(\n      cpo_t<co_withAsyncStack>, CurrentAsyncStackFrameAwaitable) noexcept {\n    return Awaiter{};\n  }\n};\n\n// Await this object within a coroutine to obtain a reference to the current\n// coroutine's AsyncStackFrame. This will only work within a coroutine whose\n// promise_type implements the getAsyncFrame() method.\ninline constexpr CurrentAsyncStackFrameAwaitable co_current_async_stack_frame{};\n\n} // namespace detail\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/detail/Helpers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n#include <folly/SingletonThreadLocal.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/io/async/Request.h>\n#include <folly/tracing/AsyncStack.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n// Helper class that can be used to annotate Awaitable objects that will\n// guarantee that they will be resumed on the correct executor so that\n// when the object is awaited within a Task<T> it doesn't automatically\n// wrap the Awaitable in something that forces a reschedule onto the\n// executor.\ntemplate <typename Awaitable>\nclass UnsafeResumeInlineSemiAwaitable {\n public:\n  explicit UnsafeResumeInlineSemiAwaitable(Awaitable&& awaitable) noexcept\n      : awaitable_(awaitable) {}\n\n  Awaitable&& viaIfAsync(folly::Executor::KeepAlive<>) && noexcept {\n    return static_cast<Awaitable&&>(awaitable_);\n  }\n\n private:\n  Awaitable awaitable_;\n};\n\n} // namespace detail\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/detail/InlineTask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ScopeGuard.h>\n#include <folly/Try.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/coro/detail/Malloc.h>\n#include <folly/lang/Assume.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <cassert>\n#include <utility>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\n/// InlineTask<T> is a coroutine-return type where the coroutine is launched\n/// inline in the current execution context when it is co_awaited and the\n/// task's continuation is launched inline in the execution context that the\n/// task completed on.\n///\n/// This task type is primarily intended as a building block for certain\n/// coroutine operators. It is not intended for general use in application\n/// code or in library interfaces exposed to library code as it can easily be\n/// abused to accidentally run logic on the wrong execution context.\n///\n/// For this reason, the InlineTask<T> type has been placed inside the\n/// folly::coro::detail namespace to discourage general usage.\ntemplate <typename T>\nclass InlineTask;\n\nclass InlineTaskPromiseBase {\n  struct FinalAwaiter {\n    bool await_ready() noexcept { return false; }\n\n    template <typename Promise>\n    coroutine_handle<> await_suspend(coroutine_handle<Promise> h) noexcept {\n      InlineTaskPromiseBase& promise = h.promise();\n      return promise.continuation_;\n    }\n\n    void await_resume() noexcept {}\n  };\n\n protected:\n  InlineTaskPromiseBase() noexcept = default;\n\n  InlineTaskPromiseBase(const InlineTaskPromiseBase&) = delete;\n  InlineTaskPromiseBase(InlineTaskPromiseBase&&) = delete;\n  InlineTaskPromiseBase& operator=(const InlineTaskPromiseBase&) = delete;\n  InlineTaskPromiseBase& operator=(InlineTaskPromiseBase&&) = delete;\n\n public:\n  static void* operator new(std::size_t size) {\n    return ::folly_coro_async_malloc(size);\n  }\n\n  static void operator delete(void* ptr, std::size_t size) {\n    ::folly_coro_async_free(ptr, size);\n  }\n\n  suspend_always initial_suspend() noexcept { return {}; }\n\n  auto final_suspend() noexcept { return FinalAwaiter{}; }\n\n  void set_continuation(coroutine_handle<> continuation) noexcept {\n    assert(!continuation_);\n    continuation_ = continuation;\n  }\n\n private:\n  coroutine_handle<> continuation_;\n};\n\ntemplate <typename T>\nclass InlineTaskPromise : public InlineTaskPromiseBase {\n public:\n  static_assert(\n      std::is_move_constructible<T>::value,\n      \"InlineTask<T> only supports types that are move-constructible.\");\n  static_assert(\n      !std::is_rvalue_reference<T>::value, \"InlineTask<T&&> is not supported\");\n\n  InlineTaskPromise() noexcept = default;\n\n  ~InlineTaskPromise() = default;\n\n  InlineTask<T> get_return_object() noexcept;\n\n  template <\n      typename Value = T,\n      std::enable_if_t<std::is_convertible<Value&&, T>::value, int> = 0>\n  void return_value(Value&& value) noexcept(\n      std::is_nothrow_constructible<T, Value&&>::value) {\n    result_.emplace(static_cast<Value&&>(value));\n  }\n\n  void unhandled_exception() noexcept {\n    result_.emplaceException(folly::exception_wrapper{current_exception()});\n  }\n\n  T result() { return std::move(result_).value(); }\n\n private:\n  // folly::Try<T> doesn't support storing reference types so we store a\n  // std::reference_wrapper instead.\n  using StorageType = std::conditional_t<\n      std::is_lvalue_reference<T>::value,\n      std::reference_wrapper<std::remove_reference_t<T>>,\n      T>;\n\n  folly::Try<StorageType> result_;\n};\n\ntemplate <>\nclass InlineTaskPromise<void> : public InlineTaskPromiseBase {\n public:\n  InlineTaskPromise() noexcept = default;\n\n  InlineTask<void> get_return_object() noexcept;\n\n  void return_void() noexcept {}\n\n  void unhandled_exception() noexcept {\n    result_.emplaceException(folly::exception_wrapper{current_exception()});\n  }\n\n  void result() { return result_.value(); }\n\n private:\n  folly::Try<void> result_;\n};\n\ntemplate <typename T>\nclass InlineTask {\n public:\n  using promise_type = detail::InlineTaskPromise<T>;\n\n private:\n  using handle_t = coroutine_handle<promise_type>;\n\n public:\n  InlineTask(InlineTask&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  ~InlineTask() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  class Awaiter {\n   public:\n    ~Awaiter() {\n      if (coro_) {\n        coro_.destroy();\n      }\n    }\n\n    bool await_ready() noexcept { return false; }\n\n    handle_t await_suspend(coroutine_handle<> awaitingCoroutine) noexcept {\n      assert(coro_ && !coro_.done());\n      coro_.promise().set_continuation(awaitingCoroutine);\n      return coro_;\n    }\n\n    T await_resume() {\n      auto destroyOnExit = folly::makeGuard([this] {\n        std::exchange(coro_, {}).destroy();\n      });\n      return coro_.promise().result();\n    }\n\n   private:\n    friend class InlineTask<T>;\n    explicit Awaiter(handle_t coro) noexcept : coro_(coro) {}\n    handle_t coro_;\n  };\n\n  Awaiter operator co_await() && {\n    assert(coro_ && !coro_.done());\n    return Awaiter{std::exchange(coro_, {})};\n  }\n\n private:\n  friend class InlineTaskPromise<T>;\n  explicit InlineTask(handle_t coro) noexcept : coro_(coro) {}\n  handle_t coro_;\n};\n\ntemplate <typename T>\ninline InlineTask<T> InlineTaskPromise<T>::get_return_object() noexcept {\n  return InlineTask<T>{\n      coroutine_handle<InlineTaskPromise<T>>::from_promise(*this)};\n}\n\ninline InlineTask<void> InlineTaskPromise<void>::get_return_object() noexcept {\n  return InlineTask<void>{\n      coroutine_handle<InlineTaskPromise<void>>::from_promise(*this)};\n}\n\n/// InlineTaskDetached is a coroutine-return type where the coroutine is\n/// launched in the current execution context when it is created and the\n/// task's continuation is launched inline in the execution context that the\n/// task completed on.\n///\n/// This task type is primarily intended as a building block for certain\n/// coroutine operators. It is not intended for general use in application\n/// code or in library interfaces exposed to library code as it can easily be\n/// abused to accidentally run logic on the wrong execution context.\n///\n/// For this reason, the InlineTaskDetached type has been placed inside the\n/// folly::coro::detail namespace to discourage general usage.\nstruct InlineTaskDetached {\n  class promise_type {\n    struct FinalAwaiter {\n      bool await_ready() noexcept { return false; }\n      void await_suspend(coroutine_handle<promise_type> h) noexcept {\n        folly::deactivateAsyncStackFrame(h.promise().getAsyncFrame());\n        h.destroy();\n      }\n      [[noreturn]] void await_resume() noexcept { folly::assume_unreachable(); }\n    };\n\n   public:\n    static void* operator new(std::size_t size) {\n      return ::folly_coro_async_malloc(size);\n    }\n\n    static void operator delete(void* ptr, std::size_t size) {\n      ::folly_coro_async_free(ptr, size);\n    }\n\n    promise_type() noexcept {\n      asyncFrame_.setParentFrame(folly::getDetachedRootAsyncStackFrame());\n    }\n\n    InlineTaskDetached get_return_object() noexcept {\n      return InlineTaskDetached{\n          coroutine_handle<promise_type>::from_promise(*this)};\n    }\n\n    suspend_always initial_suspend() noexcept { return {}; }\n\n    FinalAwaiter final_suspend() noexcept { return {}; }\n\n    void return_void() noexcept {}\n\n    [[noreturn]] void unhandled_exception() noexcept { std::terminate(); }\n\n    template <typename Awaitable>\n    decltype(auto) await_transform(Awaitable&& awaitable) {\n      return folly::coro::co_withAsyncStack(\n          static_cast<Awaitable&&>(awaitable));\n    }\n\n    folly::AsyncStackFrame& getAsyncFrame() noexcept { return asyncFrame_; }\n\n   private:\n    folly::AsyncStackFrame asyncFrame_;\n  };\n\n  InlineTaskDetached(InlineTaskDetached&& other) noexcept\n      : coro_(std::exchange(other.coro_, {})) {}\n\n  ~InlineTaskDetached() {\n    if (coro_) {\n      coro_.destroy();\n    }\n  }\n\n  FOLLY_NOINLINE void start() noexcept {\n    start(FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  }\n\n  void start(void* returnAddress) noexcept {\n    coro_.promise().getAsyncFrame().setReturnAddress(returnAddress);\n    folly::resumeCoroutineWithNewAsyncStackRoot(std::exchange(coro_, {}));\n  }\n\n private:\n  explicit InlineTaskDetached(coroutine_handle<promise_type> h) noexcept\n      : coro_(h) {}\n\n  coroutine_handle<promise_type> coro_;\n};\n\n} // namespace detail\n} // namespace coro\n} // namespace folly\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/detail/Malloc.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/Malloc.h>\n\n#include <folly/lang/Hint.h>\n#include <folly/lang/New.h>\n\nextern \"C\" {\n\nFOLLY_NOINLINE\nvoid* folly_coro_async_malloc(std::size_t size) {\n  auto p = folly::operator_new(size);\n\n  // Add this after the call to prevent the compiler from\n  // turning the call to operator new() into a tailcall.\n  folly::compiler_must_not_elide(p);\n\n  return p;\n}\n\nFOLLY_NOINLINE\nvoid folly_coro_async_free(void* ptr, std::size_t size) {\n  folly::operator_delete(ptr, size);\n\n  // Add this after the call to prevent the compiler from\n  // turning the call to operator delete() into a tailcall.\n  folly::compiler_must_not_elide(size);\n}\n} // extern \"C\"\n"
  },
  {
    "path": "folly/coro/detail/Malloc.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CPortability.h>\n\n#include <cstddef>\n\nextern \"C\" {\n\n// Heap allocations for coroutine-frames for all async coroutines\n// (Task, AsyncGenerator, etc.) should be funneled through these\n// functions to allow better tracing/profiling of coroutine allocations.\nFOLLY_NOINLINE\nvoid* folly_coro_async_malloc(std::size_t size);\n\nFOLLY_NOINLINE\nvoid folly_coro_async_free(void* ptr, std::size_t size);\n} // extern \"C\"\n"
  },
  {
    "path": "folly/coro/detail/ManualLifetime.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <new>\n#include <type_traits>\n\n#include <folly/ScopeGuard.h>\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\n// Helper class for a variable with manually-controlled lifetime.\n//\n// You must explicitly call .construct() to construct/initialise the value.\n//\n// If it has been initialised then you must explicitly call .destruct() before\n// the ManualLifetime object is destroyed to ensure the destructor is run.\ntemplate <typename T>\nclass ManualLifetime {\n public:\n  ManualLifetime() noexcept {}\n  ~ManualLifetime() {}\n\n  template <\n      typename... Args,\n      std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>\n  void construct(Args&&... args) noexcept(\n      noexcept(std::is_nothrow_constructible<T, Args...>::value)) {\n    ::new (static_cast<void*>(std::addressof(value_)))\n        T(static_cast<Args&&>(args)...);\n  }\n\n  void destruct() noexcept { value_.~T(); }\n\n  const T& get() const& { return value_; }\n  T& get() & { return value_; }\n  const T&& get() const&& { return static_cast<const T&&>(value_); }\n  T&& get() && { return static_cast<T&&>(value_); }\n\n private:\n  union {\n    std::remove_const_t<T> value_;\n  };\n};\n\ntemplate <typename T>\nclass ManualLifetime<T&> {\n public:\n  ManualLifetime() noexcept : ptr_(nullptr) {}\n  ~ManualLifetime() {}\n\n  void construct(T& value) noexcept { ptr_ = std::addressof(value); }\n\n  void destruct() noexcept { ptr_ = nullptr; }\n\n  T& get() const noexcept { return *ptr_; }\n\n private:\n  T* ptr_;\n};\n\ntemplate <typename T>\nclass ManualLifetime<T&&> {\n public:\n  ManualLifetime() noexcept : ptr_(nullptr) {}\n  ~ManualLifetime() {}\n\n  void construct(T&& value) noexcept { ptr_ = std::addressof(value); }\n\n  void destruct() noexcept { ptr_ = nullptr; }\n\n  T&& get() const noexcept { return static_cast<T&&>(*ptr_); }\n\n private:\n  T* ptr_;\n};\n\ntemplate <>\nclass ManualLifetime<void> {\n public:\n  void construct() noexcept {}\n\n  void destruct() noexcept {}\n\n  void get() const noexcept {}\n};\n\n// For use when the ManualLifetime is a member of a union. First,\n// it in-place constructs the ManualLifetime, making it the active\n// member of the union. Then it calls 'construct' on it to construct\n// the value inside it.\ntemplate <\n    typename T,\n    typename... Args,\n    std::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>\nvoid activate(ManualLifetime<T>& box, Args&&... args) noexcept(\n    std::is_nothrow_constructible<T, Args...>::value) {\n  auto* p = ::new (&box) ManualLifetime<T>{};\n  // Use ScopeGuard to destruct the ManualLifetime if the 'construct' throws.\n  auto guard = makeGuard([p]() noexcept { p->~ManualLifetime(); });\n  p->construct(static_cast<Args&&>(args)...);\n  guard.dismiss();\n}\n\n// For use when the ManualLifetime is a member of a union. First,\n// it calls 'destruct' on the ManualLifetime to destroy the value\n// inside it. Then it calls the destructor of the ManualLifetime\n// object itself.\ntemplate <typename T>\nvoid deactivate(ManualLifetime<T>& box) noexcept {\n  box.destruct();\n  box.~ManualLifetime();\n}\n\n} // namespace detail\n} // namespace coro\n} // namespace folly\n"
  },
  {
    "path": "folly/coro/detail/PickTaskWrapper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/lang/SafeAlias-fwd.h>\n\n/// For functions-of-coros, like `timeout()` or `collectAll()`, we want the\n/// outer coro to be able to pass through these attributes of the inner coro:\n///  - `folly::ext::must_use_immediately_v`\n///  - `value_only_awaitable_v`\n///  - `strict_safe_alias_of_v` / `lenient_safe_alias_of_v`\n///\n/// Variation along these dimensions is currently implemented as a zoo of coro\n/// templates and wrappers -- `Task` aka `UnsafeMovableTask`, `now_task`,\n/// `safe_task`, `value_or_fatal<InnerTask>`.  The type function\n/// `pick_task_wrapper` provides common logic for picking a task type with the\n/// given attributes.\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\ntemplate <typename T>\nclass Task;\ntemplate <typename T>\nclass TaskWithExecutor;\n\ntemplate <safe_alias, typename>\nclass safe_task;\ntemplate <safe_alias, typename>\nclass safe_task_with_executor;\n\ntemplate <typename T>\nclass now_task;\ntemplate <typename T>\nclass now_task_with_executor;\n\ntemplate <typename, auto>\nclass value_or_fatal;\n\nnamespace detail {\n\nstruct identity_metafunction {\n  template <typename T>\n  using apply = T;\n};\n\ntemplate <safe_alias, bool /*must use immediately (now)*/>\nstruct pick_task_wrapper_impl;\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\ntemplate <>\nstruct pick_task_wrapper_impl<safe_alias::unsafe, /*await now*/ false> {\n  template <typename T>\n  using Task = Task<T>;\n  template <typename T>\n  using TaskWithExecutor = TaskWithExecutor<T>;\n};\n\ntemplate <>\nstruct pick_task_wrapper_impl<safe_alias::unsafe, /*await now*/ true> {\n  template <typename T>\n  using Task = now_task<T>;\n  template <typename T>\n  using TaskWithExecutor = now_task_with_executor<T>;\n};\n\n// These `safe_task` types are immovable, so \"await now\" doesn't matter.\ntemplate <safe_alias Safety, bool AwaitNow>\n  requires(Safety < safe_alias::closure_min_arg_safety)\nstruct pick_task_wrapper_impl<Safety, AwaitNow> {\n  template <typename T>\n  using Task = safe_task<Safety, T>;\n  template <typename T>\n  using TaskWithExecutor = safe_task_with_executor<Safety, T>;\n};\n\ntemplate <safe_alias Safety>\n  requires(Safety >= safe_alias::closure_min_arg_safety)\n// Future: There is no principled reason we can't have must-use-immediately\n// `safe_task`s with these higher safety levels, but supporting that cleanly\n// would require reorganizing the `folly/coro` task-wrapper implementations. Two\n// possible approaches are:\n//  - `now_task<T> = await_now<Task<T>>`\n//  - Roll up `now_task` and `safe_task` into `basic_task<T, Cfg>` or similar,\n//    where `Cfg` captures both safety & immediate-awaitability.\nstruct pick_task_wrapper_impl<Safety, /*await now*/ false> {\n  template <typename T>\n  using Task = safe_task<Safety, T>;\n  template <typename T>\n  using TaskWithExecutor = safe_task_with_executor<Safety, T>;\n};\n\n#else // no FOLLY_HAS_IMMOVABLE_COROUTINES\n\n// This fallback is required because `coro::Future<SafeType>` is safe and is\n// available on earlier build systems.  We have no choice but to emit `Task`.\ntemplate <safe_alias Safety>\nstruct pick_task_wrapper_impl<Safety, /*await now*/ false> {\n  template <typename T>\n  using Task = Task<T>;\n  template <typename T>\n  using TaskWithExecutor = TaskWithExecutor<T>;\n};\n\n#endif // FOLLY_HAS_IMMOVABLE_COROUTINES\n\n// Pass this as `AddWrapperMetaFn` to `pick_task_wrapper` to add\n// `value_or_fatal`.\ntemplate <auto CancelCfg>\nstruct value_or_fatal_with_cancel_cfg {\n  template <typename T>\n  using apply = value_or_fatal<T, CancelCfg>;\n};\n\ntemplate <\n    typename T,\n    safe_alias Safety,\n    bool MustUseImmediately,\n    typename AddWrapperMetaFn = identity_metafunction>\nusing pick_task_wrapper = typename AddWrapperMetaFn::template apply<\n    typename pick_task_wrapper_impl<Safety, MustUseImmediately>::template Task<\n        T>>;\n\ntemplate <\n    typename T,\n    safe_alias Safety,\n    bool MustUseImmediately,\n    typename AddWrapperMetaFn = identity_metafunction>\nusing pick_task_with_executor_wrapper =\n    typename AddWrapperMetaFn::template apply<typename pick_task_wrapper_impl<\n        Safety,\n        MustUseImmediately>::template TaskWithExecutor<T>>;\n\n} // namespace detail\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/detail/Traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Traits.h>\n\nnamespace folly {\nnamespace coro {\nnamespace detail {\n\n/**\n * A type trait that lifts lvalue references into std::reference_wrapper<T>\n * eg. so the value can be stored in std::optional or folly::Try.\n */\ntemplate <typename T>\nstruct lift_lvalue_reference {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct lift_lvalue_reference<T&> {\n  using type = std::reference_wrapper<T>;\n};\n\ntemplate <typename T>\nusing lift_lvalue_reference_t = typename lift_lvalue_reference<T>::type;\n\n/**\n * A type trait to decay rvalue-reference types to a prvalue.\n */\ntemplate <typename T>\nstruct decay_rvalue_reference {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct decay_rvalue_reference<T&&> : remove_cvref<T> {};\n\ntemplate <typename T>\nusing decay_rvalue_reference_t = typename decay_rvalue_reference<T>::type;\n\n} // namespace detail\n} // namespace coro\n} // namespace folly\n"
  },
  {
    "path": "folly/coro/detail/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"pick_task_wrapper_test\",\n    srcs = [\n        \"PickTaskWrapperTest.cpp\",\n    ],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/coro:value_or_fatal\",\n        \"//folly/coro/detail:pick_task_wrapper\",\n        \"//folly/coro/safe:now_task\",\n        \"//folly/coro/safe:safe_task\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/detail/test/PickTaskWrapperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/ValueOrFatal.h>\n#include <folly/coro/detail/PickTaskWrapper.h>\n#include <folly/coro/safe/NowTask.h>\n#include <folly/coro/safe/SafeTask.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nTEST(PickTaskWrapperTest, AllTestsAreStatic) {}\n\n// (unsafe, await now) -> now_task\nstatic_assert(\n    std::is_same_v<\n        now_task<int>,\n        detail::pick_task_wrapper<\n            int,\n            safe_alias::unsafe,\n            /*await now*/ true>>);\nstatic_assert(\n    std::is_same_v<\n        now_task_with_executor<int>,\n        detail::pick_task_with_executor_wrapper<\n            int,\n            safe_alias::unsafe,\n            /*await now*/ true>>);\n\n// (maybe_value, movable) -> value_task\nstatic_assert(\n    std::is_same_v<\n        value_task<int>,\n        detail::pick_task_wrapper<\n            int,\n            safe_alias::maybe_value,\n            /*await now*/ false>>);\nstatic_assert(\n    std::is_same_v<\n        safe_task_with_executor<safe_alias::maybe_value, int>,\n        detail::pick_task_with_executor_wrapper<\n            int,\n            safe_alias::maybe_value,\n            /*await now*/ false>>);\n\n// (co_cleanup_safe_ref, movable, wrapper) ->\n// value_or_fatal<co_cleanup_safe_task<>>\nstatic_assert(\n    std::is_same_v<\n        value_or_fatal<\n            co_cleanup_safe_task<int>,\n            on_stopped_and_error<will_fatal>>,\n        detail::pick_task_wrapper<\n            int,\n            safe_alias::co_cleanup_safe_ref,\n            /*await now*/ false,\n            detail::value_or_fatal_with_cancel_cfg<\n                on_stopped_and_error<will_fatal>>>>);\nstatic_assert(\n    std::is_same_v<\n        value_or_fatal<\n            safe_task_with_executor<safe_alias::co_cleanup_safe_ref, int>,\n            on_stopped_and_error<will_fatal>>,\n        detail::pick_task_with_executor_wrapper<\n            int,\n            safe_alias::co_cleanup_safe_ref,\n            /*await now*/ false,\n            detail::value_or_fatal_with_cancel_cfg<\n                on_stopped_and_error<will_fatal>>>>);\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/AsyncClosure-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly::coro {\n\nclass AsyncObjectTag;\n\nnamespace detail {\ntemplate <auto>\nauto bind_captures_to_closure(auto&&, auto);\n} // namespace detail\n\n// Tag type used by `async_closure` to trigger cleanup of `capture`s that\n// have an `co_cleanup(async_closure_private_t)` overload.\nclass async_closure_private_t {\n protected:\n  friend class AsyncObjectTag;\n  template <auto>\n  friend auto detail::bind_captures_to_closure(auto&&, auto);\n\n  async_closure_private_t() = default;\n};\n\n} // namespace folly::coro\n"
  },
  {
    "path": "folly/coro/safe/AsyncClosure.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/safe/detail/AsyncClosure.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro {\n\n/// Learn more about `coro/safe` tools by browsing `docs/`.  Start with\n/// `README.md` and `AsyncClosure.md`.  Here's a tl;dr for `AsyncClosure.h`.\n///\n/// Use `async_closure()` / `async_now_closure()` only when `now_task` is not\n/// enough.  For your effort, you get (1) guaranteed, exception-safe async\n/// RAII, and (2) the resulting coro is an automatically-measured movable\n/// `safe_task`, which improves lifetime safety (`LifetimeSafetyBenefits.md`).\n///\n/// Control flow matches a regular `now_task` lazy-start coro:\n///   - All \"argument-binding\" and \"coro creation\" work is eager.\n///   - Your inner task & subsequent cleanup can only run when awaited.\n///\n/// `async_closure(bind::args{...}, taskFn)` wraps an outer task around yours,\n/// unless elided via an automatic optimization.  The outer task owns special\n/// \"capture\" args passed to the closure, ensuring they outlive the inner task.\n///\n/// Lifecycle contract:\n///   - `bind::args{}` evaluate left-to-right (L2R) due to `{}`.\n///   - Construction of `bind::capture_in_place` / `bind::in_place` args is also\n///   L2R.\n///   - When args are passed to the inner coro, copy/move order is unspecified.\n///   - Upon awaiting the inner coro, `setParentCancelToken()` is called on the\n///     capture args in L2R order.\n///   - After the inner coro exits, arg `co_cleanup()` is executed in R2L order.\n///   - When the outer coro exits, captures are destroyed in R2L order.\n///\n/// The `co_cleanup()` and `setParentCancelToken()` protocols support capture\n/// types like `safe_async_scope` and `BackgroundTask`, which give the user\n/// guaranteed, exception-safe async cleanup. Before building custom async\n/// RAII, carefully read `CoCleanupAsyncRAII.md`.\n///\n/// The `async_closure_make_outer_coro` machinery is reused by `AsyncObject`,\n/// which implements a similar \"async RAII\" contract for object scopes.\n///\n/// The difference between `async_closure()` and `async_now_closure()` is that\n/// the former measures argument & inner coro safety, and makes a `safe_task`,\n/// while the latter has no safety checks, and makes a `now_task`.  Both make it\n/// easy to write lifetime-safe code.\n\nstruct async_closure_config {\n  /// POWER USERS ONLY: For efficiency, `async_closure` will elide the outer\n  /// coro if there are no `co_cleanup` captures.  In particular,\n  /// `setParentCancelToken` isn't currently part of this detection, since we\n  /// don't expect it to be used without `co_cleanup`.\n  ///\n  /// This optimization has some observable effects on the types seen by the\n  /// closure, e.g.  `capture<int&>` becomes `capture<int>` since the inner\n  /// coro now owns the `int` instead of just holding a reference.  However, to\n  /// the extent possible, the before/after types \"quack\" the same.\n  ///\n  /// There are some edge-case scenarios where you may want to disable this\n  /// optimization. An incomplete list:\n  ///   - If you're passing many in-place, non-movable captures, the current\n  ///     implementation will allocate each one on the heap, separately.\n  ///     Setting `.force_outer_coro = true` will consolidate them into one\n  ///     `unique_ptr<tuple<>>` owned by the outer coro.  If this scenario\n  ///     proves perf-sensitive, we may add an automatic heuristic.\n  ///   - This can be required to make a member function coro own its object.\n  ///\n  /// NB: Currently, if you set `.force_outer_coro = true`, but there are no\n  /// captures to store, the outer coro will still be elided.\n  bool force_outer_coro = false;\n};\n\n// Implementation note: None of the below functions can take `make_inner_coro`\n// by-value, because stateful callables (lambdas with captures) are allowed\n// here -- even in `async_closure()` if it's a coroutine wrapper.  A callable\n// passed by-value would be destroyed before it can be awaited, causing a\n// stack-use-after-return error.\n\nnamespace detail {\ntemplate <bool ForceOuterCoro, bool EmitNowTask>\n// OK to take `bind::args` by-ref since the porcelain functions take it by-value\nauto async_closure_impl(auto&& bargs, auto&& make_inner_coro) {\n  constexpr detail::async_closure_bindings_cfg Cfg{\n      .force_outer_coro = ForceOuterCoro,\n      .emit_now_task = EmitNowTask,\n      .is_invoke_member = is_instantiation_of_v<\n          invoke_member_wrapper_fn,\n          std::remove_reference_t<decltype(make_inner_coro)>>};\n  return detail::bind_captures_to_closure<Cfg>(\n      static_cast<decltype(make_inner_coro)>(make_inner_coro),\n      detail::async_closure_safeties_and_bindings<Cfg>(\n          static_cast<decltype(bargs)>(bargs)));\n}\n} // namespace detail\n\n// Makes a `safe_task` whose safety is determined by the supplied arguments.\n// `safe_task` requires that (1) the inner coroutine must not take arguments\n// by-reference, and (2) must have a `maybe_value`-safe return type.\n//\n// Caveat: When `make_inner_coro` is a coroutine wrapper, that part is\n// evaluated synchronously, and is not subject to either (1) or (2).\n//\n// Coro creation, argument storage, and in-place construction are also\n// synchronous, as is the movement of the args into the task coroutine.\n//\n// The first argument should be `bind::args{...}`.  For single-argument\n// closures, you can omit the `bind::args` if you're passing `bind::capture()`,\n// `bind::capture_in_place<>()`, or another `bind::ext::like_args` item.\n//\n// Async RAII: Awaiting the task ensures `co_cleanup(async_closure_private_t)`\n// is awaited for each of the `capture` arguments that defines it.\n//\n// Awaiting the task also forwards its ambient cancellation token to the\n// captures that have a `setParentCancelToken()` member.  WARNING: If you want\n// a type to define that, WITHOUT implementing `co_cleanup()`, then read the\n// `force_outer_coro` doc above -- you'll have to add a bit of logic to\n// `capture_needs_outer_coro()`.\ntemplate <async_closure_config Cfg = async_closure_config{}>\nauto async_closure(auto bargs, auto&& make_inner_coro) {\n  return folly::coro::detail::\n      async_closure_impl<Cfg.force_outer_coro, /*EmitNowTask*/ false>(\n             std::move(bargs),\n             static_cast<decltype(make_inner_coro)>(make_inner_coro))\n          .release_outer_coro();\n}\n\n// Like `async_closure` -- same argument binding semantics, same `co_cleanup`\n// async RAII, and cancellation support, but returns a non-movable `now_task`\n// without the lifetime safety enforcement:\n//   - `make_inner_coro` may return a `now_task`, plain `Task`, or `safe_task`.\n//   - It can take arguments by ref, you can pass raw pointers, etc.\n//   - There are no checks on the `co_return` type.\n//\n// Requiring the task to be immediately awaited prevents a lot of common\n// lifetime bugs.  If you cannot immediately await the task, then you should\n// review `LifetimSafetyBenefits.md` and use the `safe_task`-enabled\n// `async_closure()`, which is movable and schedulable on `safe_async_scope`.\n//\n// BEWARE: Returning `now_task` doesn't prevent egregious bugs like returning\n// a pointer to a local.  Instead, make sure to configure your compiler to\n// error on simple, non-async lifetime bugs (e.g. `-Wdangling -Werror`).\ntemplate <async_closure_config Cfg = async_closure_config{}>\nauto async_now_closure(auto bargs, auto&& make_inner_coro) {\n  return folly::coro::detail::\n      async_closure_impl<Cfg.force_outer_coro, /*EmitNowTask*/ true>(\n          std::move(bargs),\n          static_cast<decltype(make_inner_coro)>(make_inner_coro));\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"async_closure_fwd\",\n    headers = [\"AsyncClosure-fwd.h\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"bind_captures\",\n    headers = [\"BindCaptures.h\"],\n    exported_deps = [\"//folly/lang/bind:bind\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"captures\",\n    headers = [\"Captures.h\"],\n    exported_deps = [\n        \":async_closure_fwd\",\n        \"//folly:portability\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/coro/safe/detail:define_movable_deep_const_lref_copyable\",\n        \"//folly/detail:tuple\",\n        \"//folly/lang:safe_alias_fwd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_closure\",\n    headers = [\"AsyncClosure.h\"],\n    exported_deps = [\n        \"//folly/coro/safe/detail:async_closure\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"now_task\",\n    headers = [\"NowTask.h\"],\n    exported_deps = [\n        \"//folly/coro:task_wrapper\",\n        \"//folly/lang:safe_alias_fwd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"safe_task\",\n    headers = [\"SafeTask.h\"],\n    exported_deps = [\n        \":now_task\",\n        \"//folly/coro:task_wrapper\",\n        \"//folly/lang:safe_alias\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/safe/BindCaptures.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/lang/bind/Bind.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\n///\n/// Please read the user- and developer-facing docs in `Capture.md`.\n///\n/// Callers typically only include `BindCaptures.h`, while callees need to\n/// include `Captures.h`.  Both are provided by `AsyncClosure.h`.\n///\n/// NOTE:\n///   - The binding helpers in this file are all in `folly::bind::` for\n///     uniformity with the other related verbs -- `bind::args`,\n///     `bind::in_place` `bind::constant`, etc.\n///   - The `bind_to_storage_policy` specialization for this is only used\n///     in one file, so it's inlined in `BindAsyncClosure.h`.\n///\n\nnamespace folly::bind {\n\nnamespace detail {\n\n// Any binding with this key is meant to be owned by the async closure\nenum class capture_kind {\n  plain = 0,\n  // Syntax sugar: Passing `bind::capture_indirect()` with a pointer-like (e.g.\n  // `unique_ptr<T>`), this emits a `capture_indirect<>`, giving access to\n  // the underlying `T` with just one dereference `*` / `->`, instead of 2.\n  indirect,\n};\n\nstruct capture_bind_info_t : folly::bind::ext::bind_info_t {\n  capture_kind captureKind_;\n\n  constexpr explicit capture_bind_info_t(\n      // Using a constraint prevents object slicing\n      std::same_as<folly::bind::ext::bind_info_t> auto bi,\n      capture_kind ap)\n      : folly::bind::ext::bind_info_t(std::move(bi)), captureKind_(ap) {}\n};\n\ntemplate <capture_kind Kind, typename UpdateBI = std::identity>\nstruct capture_bind_info {\n  // Using `auto` prevents object slicing\n  constexpr auto operator()(auto bi) {\n    return capture_bind_info_t{UpdateBI{}(std::move(bi)), Kind};\n  }\n};\n\n} // namespace detail\n\n///\n/// `bind::capture()` and `bind::capture_indirect()` work much like other\n/// `folly::bind` modifiers.  However, since they're primarily intended\n/// for `async_closure` arguments, you will practically only use them:\n///   - alone, for non-`co_cleanup` arguments;\n///   - with `bind::in_place()` or `bind::in_place_with()`, for `co_cleanup`\n///     arguments;\n///   - with `constant()`, for either.\n///\n/// `capture_in_place<T>()` is short for `bind::capture(bind::in_place<T>())`.\n///\n/// See `Captures.md` and `folly/lang/Bindings.md`.\n///\n\ntemplate <typename... Ts>\nstruct capture\n    : ::folly::bind::ext::merge_update_args<\n          detail::capture_bind_info<detail::capture_kind::plain>,\n          Ts...> {\n  using ::folly::bind::ext::merge_update_args<\n      detail::capture_bind_info<detail::capture_kind::plain>,\n      Ts...>::merge_update_args;\n};\ntemplate <typename... Ts>\ncapture(Ts&&...) -> capture<folly::bind::ext::deduce_args_t<Ts>...>;\n\ntemplate <typename... Ts>\nstruct capture_indirect\n    : ::folly::bind::ext::merge_update_args<\n          detail::capture_bind_info<detail::capture_kind::indirect>,\n          Ts...> {\n  using ::folly::bind::ext::merge_update_args<\n      detail::capture_bind_info<detail::capture_kind::indirect>,\n      Ts...>::merge_update_args;\n};\ntemplate <typename... Ts>\ncapture_indirect(Ts&&...)\n    -> capture_indirect<folly::bind::ext::deduce_args_t<Ts>...>;\n\n// Sugar for `capture{const_ref{...}}`\ntemplate <typename... Ts>\nstruct capture_const_ref\n    : ::folly::bind::ext::merge_update_args<\n          detail::capture_bind_info<\n              detail::capture_kind::plain,\n              ::folly::bind::detail::const_ref_bind_info>,\n          Ts...> {\n  using ::folly::bind::ext::merge_update_args<\n      detail::capture_bind_info<\n          detail::capture_kind::plain,\n          ::folly::bind::detail::const_ref_bind_info>,\n      Ts...>::merge_update_args;\n};\ntemplate <typename... Ts>\ncapture_const_ref(Ts&&...)\n    -> capture_const_ref<folly::bind::ext::deduce_args_t<Ts>...>;\n// Sugar for `capture{mut_ref{...}}`\ntemplate <typename... Ts>\nstruct capture_mut_ref\n    : ::folly::bind::ext::merge_update_args<\n          detail::capture_bind_info<\n              detail::capture_kind::plain,\n              ::folly::bind::detail::mut_ref_bind_info>,\n          Ts...> {\n  using ::folly::bind::ext::merge_update_args<\n      detail::capture_bind_info<\n          detail::capture_kind::plain,\n          ::folly::bind::detail::mut_ref_bind_info>,\n      Ts...>::merge_update_args;\n};\ntemplate <typename... Ts>\ncapture_mut_ref(Ts&&...)\n    -> capture_mut_ref<folly::bind::ext::deduce_args_t<Ts>...>;\n\n// Sugar for `capture{in_place<T>(...)}`\ntemplate <typename T>\nauto capture_in_place(auto&&... as [[clang::lifetimebound]]) {\n  return capture(bind::in_place<T>(static_cast<decltype(as)>(as)...));\n}\n// Sugar for `capture{in_place_with(fn, ...)}`\nauto capture_in_place_with(\n    auto make_fn, auto&&... as [[clang::lifetimebound]]) {\n  return capture(\n      bind::in_place_with(\n          std::move(make_fn), static_cast<decltype(as)>(as)...));\n}\n\n} // namespace folly::bind\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async_closure\n  HEADERS\n    AsyncClosure.h\n  EXPORTED_DEPS\n    folly_coro_safe_detail_async_closure\n)\n\nfolly_add_library(\n  NAME async_closure_fwd\n  HEADERS\n    AsyncClosure-fwd.h\n)\n\nfolly_add_library(\n  NAME bind_captures\n  HEADERS\n    BindCaptures.h\n  EXPORTED_DEPS\n    folly_lang_bind_bind\n)\n\nfolly_add_library(\n  NAME captures\n  HEADERS\n    Captures.h\n  EXPORTED_DEPS\n    folly_coro_safe_async_closure_fwd\n    folly_coro_safe_detail_define_movable_deep_const_lref_copyable\n    folly_detail_tuple\n    folly_lang_safe_alias_fwd\n    folly_portability\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME now_task\n  HEADERS\n    NowTask.h\n  EXPORTED_DEPS\n    folly_coro_task_wrapper\n    folly_lang_safe_alias_fwd\n)\n\nfolly_add_library(\n  NAME safe_task\n  HEADERS\n    SafeTask.h\n  EXPORTED_DEPS\n    folly_coro_safe_now_task\n    folly_coro_task_wrapper\n    folly_lang_safe_alias\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/coro/safe/Captures.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/coro/safe/AsyncClosure-fwd.h>\n#include <folly/lang/SafeAlias-fwd.h>\n// `#undef`ed at end-of-file not to leak this macro.\n#include <folly/coro/safe/detail/DefineMovableDeepConstLrefCopyable.h>\n#include <folly/detail/tuple.h>\n\n///\n/// Please read the user- and developer-facing docs in `Capture.md`.\n///\n/// Callers typically only include `BindCaptures.h`, while callees need to\n/// include `Captures.h`.  Both are provided by `AsyncClosure.h`.\n///\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly {\nclass CancellationToken;\nclass exception_wrapper;\n} // namespace folly\n\nnamespace folly::coro {\n\nclass AsyncObjectTag;\n\ntemplate <safe_alias, typename>\nclass safe_task;\n\nnamespace detail {\n\nnamespace lite_tuple {\nusing namespace ::folly::detail::lite_tuple;\n}\n\ntemplate <typename>\nstruct AsyncObjectRefForSlot;\n\ntemplate <typename T>\nconcept has_async_closure_co_cleanup_error_oblivious =\n    requires(T t, async_closure_private_t p) { std::move(t).co_cleanup(p); };\ntemplate <typename T>\nconcept has_async_closure_co_cleanup_with_error = requires(\n    T t, async_closure_private_t p, const exception_wrapper* e) {\n  std::move(t).co_cleanup(p, e);\n};\ntemplate <typename T> // DO NOT USE: for AsyncObject only\nconcept has_async_object_private_hack_co_cleanup = requires(\n    T t, async_closure_private_t p, const exception_wrapper* e) {\n  t.privateHack_co_cleanup(std::move(t), p, e);\n};\ntemplate <typename T>\nconcept has_async_closure_co_cleanup =\n    has_async_closure_co_cleanup_error_oblivious<T> ||\n    has_async_closure_co_cleanup_with_error<T> ||\n    has_async_object_private_hack_co_cleanup<T>;\n// `T` must be immovable to go in `co_cleanup_capture<T>` and similar places.\n// The aim here is to prevent bugs. A safe \"move-like\" operation for `T` must:\n//   - Ensure that the destination of the move is \"managed\", i.e. is another\n//     `co_cleanup_capture<>` or a similar object from `folly/coro/safe`\n//     privileged to access `capture_private_t`. Otherwise, cleanup is no longer\n//     guaranteed.\n//   - Leave the moved-out object in a state where its `co_cleanup()`, which\n//     will still be awaited, is a safe no-op.\n//\n// A regular move ctor cannot adequately vet the destination. That is because\n// per `CoCleanupAsyncRAII.md`, a just-constructed object must NEVER require\n// cleanup (required since closure setup is fallible, e.g. due to `bad_alloc`).\n//\n// So, where necessary (I don't have such a use-case yet) -- types should\n// provide a specialized move operation instead.\ntemplate <typename T>\nconcept immovable_async_closure_co_cleanup =\n    has_async_closure_co_cleanup<T> && !std::is_copy_constructible_v<T> &&\n    !std::is_copy_assignable_v<T> && !std::is_move_constructible_v<T> &&\n    !std::is_move_assignable_v<T> && !std::swappable<T>;\n\ntemplate <typename, template <typename> class, typename>\nclass capture_crtp_base;\n\n} // namespace detail\n\ntemplate <typename T>\n  requires(!detail::has_async_closure_co_cleanup<T>)\nclass capture;\ntemplate <typename T>\n  requires(!detail::has_async_closure_co_cleanup<T>)\nclass after_cleanup_capture;\ntemplate <typename T>\nclass capture_indirect;\ntemplate <typename T>\nclass after_cleanup_capture_indirect;\n\n// Given a cvref-qualified `capture` type, what `capture` reference type is it\n// convertible to?  The input value category affects the output reference type\n// exactly as you'd expect for types NOT wrapped by `capture`.  But,\n// additionally, this knows to pick the correct wrapper:\n//   - `co_cleanup_capture` inputs become `co_cleanup_capture<SomeRef>`\n//   - `after_cleanup_ref_*` inputs become `after_cleanup_capture<SomeRef>`\n//   - everything else becomes just `capture<SomeRef>`\ntemplate <typename Captures>\nusing capture_ref_conversion_t =\n    std::remove_cvref_t<Captures>::template ref_like_t<Captures>;\n\n// This namespace has tools for library authors who're building new\n// `co_cleanup` types. See the guide in `Captures.md`.\nnamespace ext {\n\n// Used with `capture_proxy(capture_proxy_tag<KIND>, ...)`.  We don't\n// need to track `const` state here, since prvalue semantics do apply any\n// `const` qualifier on the return type of `capture_proxy()`.\nenum class capture_proxy_kind {\n  lval_ref,\n  lval_ptr,\n  rval_ref,\n  rval_ptr,\n};\n\n// Passkey used with `capture_proxy` methods.\ntemplate <capture_proxy_kind Kind>\nclass capture_proxy_tag {\n private:\n  template <typename, template <typename> class, typename>\n  friend class ::folly::coro::detail::capture_crtp_base;\n  explicit capture_proxy_tag() = default;\n};\n\n// When implementing the `capture_proxy()` ADL customization point, it is\n// important for the second argument to match both `T&` and `const T&`:\n//   template <capture_proxy_kind Kind, const_or_not<YourType> Me>\n//   friend auto capture_proxy(capture_proxy_tag<Kind>, Me&);\ntemplate <typename T, typename U>\nconcept const_or_not = (std::same_as<T, U> || std::same_as<T, const U>);\n\n} // namespace ext\n\nnamespace detail {\n\n// DANGER: Using this passkey makes it easy to break the lifetime-safety\n// guarantees of `folly/coro/safe`, so before adding a new callsite, get\n// familiar with the lifetime-safety docs, and get a careful review.  This used\n// to have a private-with-friends constructor, but the friend list grew\n// unmanageably large as the number of lifetime-safe utilities increased.\nstruct capture_private_t {\n  explicit capture_private_t() = default;\n};\n\nstruct capture_restricted_tag {}; // detail of `restricted_co_cleanup_capture`\n\ntemplate <typename T>\nstruct bind_wrapper_t {\n  T t_;\n  constexpr decltype(auto) what_to_bind() && { return static_cast<T&&>(t_); }\n};\n\n// Makes a `bind_wrapper_t` with a forwarding ref of the argument.\nconstexpr auto forward_bind_wrapper(auto&& v [[clang::lifetimebound]]) {\n  static_assert(std::is_reference_v<decltype(v)>);\n  return bind_wrapper_t<decltype(v)>{static_cast<decltype(v)>(v)};\n}\n\nconstexpr auto unsafe_tuple_to_bind_wrapper(auto tup) {\n  static_assert(1 == std::tuple_size_v<decltype(tup)>);\n  return bind_wrapper_t<std::tuple_element_t<0, decltype(tup)>>{\n      .t_ = lite_tuple::get<0>(std::move(tup))};\n}\n\ntemplate <typename Derived, template <typename> class RefArgT, typename T>\nclass capture_crtp_base {\n private:\n  static constexpr decltype(auto) assert_result_is_non_copyable_non_movable(\n      auto&& fn) {\n    using U = decltype(fn());\n    // Tests `U&` instead of `is_copy_*` to catch non-regular classes that\n    // declare a U(U&) ctor.  This implementation is for class types only.\n    static_assert(\n        // E.g. `AsyncObjectPtr::capture_proxy()` just returns a reference\n        // or pointer, in effect emulating `capture_indirect`.\n        std::is_reference_v<U> || std::is_pointer_v<U> ||\n            !(std::is_constructible_v<U, U&> ||\n              std::is_constructible_v<U, U&&> || std::is_assignable_v<U&, U&> ||\n              std::is_assignable_v<U&, U&&>),\n        \"When a class provides custom dereferencing via `capture_proxy`, \"\n        \"it must be `NonCopyableNonMovable` to ensure that it can only passed \"\n        \"via `capture<Ref>`, not via your temporary proxy object. The goals \"\n        \"are (1) ensure correct `safe_alias_of` markings, (2) keep the \"\n        \"forwarding object as a hidden implementation detail.\");\n    return fn();\n  }\n\n  // Object intended for use with `capture`  (like `safe_async_scope`) may\n  // provide overloads of the helper function `capture_proxy` to provide\n  // proxy types for `capture` operators `*` and `->`.\n  //\n  // IMPORTANT: Be sure to cover the options in `capture_proxy_kind`.  Also,\n  // if you provide a `const`-qualified `capture_proxy` it should model\n  // `const` access.\n  //\n  // The reason for this indirection is as follows:\n  //   - \"Restricted\" references to scopes must enforce stricter\n  //     `safe_alias_of` constraints on their awaitables.\n  //     `restricted_co_cleanup_capture` explains the usage.\n  //   - A `restricted_co_cleanup_capture<Ref>` may be obtained from an\n  //     `co_cleanup_capture<...AsyncScope...>` that was originally NOT\n  //     restricted -- so, \"restricted\" is a property of the reference, not\n  //     of the underlying scope object.\n  //   - Therefore, the public API of `safe_async_scope` must sit in a\n  //     \"reference\" object that knows if it's restricted, not in the storage\n  //     object (which does not).\n  //   - It would break encapsulation to put `AsyncScope`-specific logic like\n  //     `add` / `schedule` / `schedule*Closure` into `Captures.h`.\n  //\n  // A type will not be accessible via `restricted_co_cleanup_capture`\n  // unless it provides overloads for `capture_restricted_proxy`.  There's\n  // no default behavior for restricted refs, because the underlying class\n  // needs to implement strong enough safety constraints that the ref can be\n  // `after_cleanup_ref`.\n  template <ext::capture_proxy_kind Kind>\n  static constexpr decltype(auto) get_proxy(\n      ext::capture_proxy_tag<Kind> proxy_tag, auto& self) {\n    auto& lref = self.get_lref();\n    if constexpr (std::is_base_of_v<capture_restricted_tag, Derived>) {\n      return assert_result_is_non_copyable_non_movable([&]() -> decltype(auto) {\n        return capture_restricted_proxy(proxy_tag, lref);\n      });\n    } else if constexpr ( // Custom dereference\n        requires { capture_proxy(proxy_tag, lref); }) {\n      return assert_result_is_non_copyable_non_movable([&]() -> decltype(auto) {\n        return capture_proxy(proxy_tag, lref);\n      });\n    } else if constexpr (Kind == ext::capture_proxy_kind::lval_ref) {\n      return lref; // Unproxied l-value reference\n    } else if constexpr (Kind == ext::capture_proxy_kind::rval_ref) {\n      // Implement regular forwarding-ref semantics:\n      //   (V&)&& -> V&, (V)&& -> V&&, (V&&)&& -> V&&\n      if constexpr (std::is_lvalue_reference_v<T>) {\n        return lref;\n      } else {\n        return std::move(lref);\n      }\n    } else if constexpr (\n        Kind == ext::capture_proxy_kind::lval_ptr ||\n        Kind == ext::capture_proxy_kind::rval_ptr) {\n      return &lref; // Unproxied pointer\n    } else {\n      static_assert(false, \"Unhandled capture_proxy_kind\");\n    }\n  }\n\n  // Invokes a callable, ensuring its return value is of type `Expected`,\n  // while retaining prvalue semantics.\n  template <typename Expected>\n  static constexpr auto assert_return_type(auto fn) {\n    static_assert(std::is_same_v<decltype(fn()), Expected>);\n    return fn();\n  }\n\n public:\n  using capture_type = T;\n\n  // Implement operators `*` and `->` for lvalue `capture` types.\n  //\n  // This rvalue specialization has an intentional & important deviation in\n  // semantics:\n  //   - All the getters require a `&&`-qualified object, i.e.  their intended\n  //     use is destructive -- you can `*std::move(arg_ref)` once.  Thereafter,\n  //     use-after-move linters will complain if you reuse the `capture<V&&>`.\n  //   - Correspondingly, `operator*` returns `V&&` instead of `V&`.\n  [[nodiscard]] constexpr decltype(auto) operator*() & noexcept {\n    static_assert(\n        !std::is_rvalue_reference_v<T>,\n        \"With `capture<T&&> a`, use `*std::move(a)`\");\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::lval_ref>{},\n        *static_cast<Derived*>(this));\n  }\n  [[nodiscard]] constexpr decltype(auto) operator*() && noexcept {\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::rval_ref>{},\n        *static_cast<Derived*>(this));\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() & noexcept {\n    static_assert(\n        !std::is_rvalue_reference_v<T>,\n        \"With `capture<T&&> a`, use `std::move(a)->`\");\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::lval_ptr>{},\n        *static_cast<Derived*>(this));\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() && noexcept {\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::rval_ptr>{},\n        *static_cast<Derived*>(this));\n  }\n  [[nodiscard]] constexpr decltype(auto) operator*() const& noexcept {\n    static_assert(\n        !std::is_rvalue_reference_v<T>,\n        \"With `capture<T&&> a`, use `*std::move(a)`\");\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::lval_ref>{},\n        *static_cast<const Derived*>(this));\n  }\n  [[nodiscard]] constexpr decltype(auto) operator*() const&& noexcept {\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::rval_ref>{},\n        *static_cast<const Derived*>(this));\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() const& noexcept {\n    static_assert(\n        !std::is_rvalue_reference_v<T>,\n        \"With `capture<T&&> a`, use `std::move(a)->`\");\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::lval_ptr>{},\n        *static_cast<const Derived*>(this));\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() const&& noexcept {\n    return get_proxy(\n        ext::capture_proxy_tag<ext::capture_proxy_kind::rval_ptr>{},\n        *static_cast<const Derived*>(this));\n  }\n\n  // Private implementation detail -- public users should instead use the below\n  // conversions.  This is how `async_closure` (and similar) create a matching\n  // `capture<Ref>` from a `Derived` instance.  The resulting type is\n  // `RefArgT`, except for the narrow case when a non-`shared_cleanup` closure\n  // is converting a `after_cleanup_ref_` input.\n  //   - `Derived::capture_type` may be a value or a reference\n  //   - `T` may be a value or reference\n  // The main reason `to_capture_ref` is locked down is that when\n  // `SharedCleanupClosure == false`, we upgrade `after_cleanup_ref_` refs.\n  // This is unsafe to do unless we know that the ref is going into\n  // an independent, nested async scope.\n  template <bool SharedCleanupClosure>\n  auto to_capture_ref(capture_private_t) & {\n    return to_capture_ref_impl<SharedCleanupClosure>(\n        static_cast<Derived&>(*this).get_lref());\n  }\n  template <bool SharedCleanupClosure>\n  auto to_capture_ref(capture_private_t) const& {\n    return to_capture_ref_impl<SharedCleanupClosure>(\n        static_cast<const Derived&>(*this).get_lref());\n  }\n  template <bool SharedCleanupClosure>\n  auto to_capture_ref(capture_private_t) && {\n    return to_capture_ref_impl<SharedCleanupClosure>(\n        std::move(static_cast<Derived&>(*this).get_lref()));\n  }\n  template <bool SharedCleanupClosure>\n  auto to_capture_ref(capture_private_t) const&& {\n    return to_capture_ref_impl<SharedCleanupClosure>(\n        std::move(static_cast<const Derived&>(*this).get_lref()));\n  }\n\n  // Prefer `capture_ref_conversion_t`, which is easier to use.  Given an\n  // instance of this `capture` of cvref category `LikeMe`, which\n  // `capture<Ref>` can it be converted to?\n  template <typename LikeMe>\n  using ref_like_t = RefArgT<like_t<LikeMe&&, T>>;\n\n  // Convert a capture instance to a capture reference of a matching cvref\n  // category.\n  //\n  // Two implicit conversions are provided because we want capture-wrapped\n  // types to act much like the underlying unwrapped types.  You can think of\n  // this conversion as allowing cvref qualifiers on the wrapper to be moved\n  // **inside** the wrapper.  The test shows full coverage, but in essence,\n  // the outer reference category replaces the inner one, any `const` moves\n  // inside the wrapper, and we never remove a `const` qualifier already\n  // present in the wrapper.  Examples:\n  //   capture<int>& -> capture<int&>\n  //   const capture<int>& -> capture<const int&>\n  //\n  // The rvalue qualified analog is explicit, to avoid some bad side effects:\n  //   capture<const int&>&& -> capture<const int&&> (explicit!)\n  //\n  // For those 3 conversions, find the destination `capture` type via the\n  // function `capture_ref_conversion_t`.\n  //\n  // We also support an explicit rref to lref conversion:\n  //   capture<int&&>&& -> capture<int&>\n  // The idea here is that you're passing `capture<V&&>` down into a child\n  // of your closure.  That deliberately has stricter single-use semantics\n  // than `V&&` in vanilla C++ -- for example, without single-use, an rref\n  // could be used to move out a value that is still referenced in\n  // safe_async_scope task.  Having the explicit && -> & conversion permits\n  // the child change its mind about moving out the value.\n  //\n  // Future ideas & implementation notes:\n  //   - We may want to support implicitly adding `const`. Today's solution\n  //     is to take `const capture`, which should be fine for most usage?\n  //   - This (and `to_capture_ref` should technically have a `const&&`\n  //     overload, but that's \"impact for another day\", whenever someone\n  //     actually needs it.\n  //   - All 3 of these conversions can be `operator auto`, but I suspect\n  //     this would hurt compile-time.  Benchmark before changing.\n  /*implicit*/ operator ref_like_t<int&>() & {\n    return assert_return_type<ref_like_t<int&>>([&] {\n      return static_cast<Derived&>(*this)\n          .template to_capture_ref</*SharedCleanup*/ true>(capture_private_t{});\n    });\n  }\n  /*implicit*/ operator ref_like_t<const int&>() const& {\n    return assert_return_type<ref_like_t<const int&>>([&] {\n      return static_cast<const Derived&>(*this)\n          .template to_capture_ref</*SharedCleanup*/ true>(capture_private_t{});\n    });\n  }\n  // This is explicit, because if it were implicit, then prvalues of type\n  // `capture<int&>` would bind to arguments of type `capture<int&&>` which is\n  // an unexpected / unsafe behavior.\n  explicit operator auto() && { // Actually, `operator ref_like_t<int&&>`\n    // This has to be `operator auto`, with a \"stub\" branch for cleanup\n    // args, because an `co_cleanup_capture` constraint bans r-value\n    // references, preventing us from unconditionally instantiating\n    // `ref_like_t<int&&>` for all `capture` types.  It would be possible to\n    // delay the \"no rvalue reference\" test by making it a `static_assert`\n    // in a constructor (or another guaranteed-to-be-instantiated) function,\n    // but this wouldn't be shorter, and it would be more fragile.\n    if constexpr (has_async_closure_co_cleanup<std::remove_cvref_t<T>>) {\n      return;\n    } else {\n      return assert_return_type<ref_like_t<int&&>>([&] {\n        return static_cast<Derived&&>(*this)\n            .template to_capture_ref</*SharedCleanup*/ true>(\n                capture_private_t{});\n      });\n    }\n  }\n  // Allow explicitly moving `capture<V&&>` into `capture<V&>`. Example:\n  //   auto lcap = capture<int&>{std::move(rcap)};\n  explicit operator ref_like_t<int&>() &&\n    requires(std::is_rvalue_reference_v<T>)\n  {\n    return assert_return_type<ref_like_t<int&>>([&] {\n      return to_capture_ref_impl</*SharedCleanup*/ true>(\n          static_cast<Derived&&>(*this).get_lref());\n    });\n  }\n\n private:\n  template <bool SharedCleanupClosure, typename V>\n  static auto to_capture_ref_impl(V&& v) {\n    // If the receiving closure takes no `shared_cleanup` args, then it\n    // cannot* pass any of its `capture` refs to an external, longer-lived\n    // cleanup callback.  That implies we can safely upgrade any incoming\n    // `after_cleanup_ref_` refs to regular post-cleanup `capture` refs --\n    // anything received from the parent is `co_cleanup_safe_ref` from the point\n    // of view of **this** closure's cleanup args, and it cannot access others.\n    //\n    // * As always, subject to the `SafeAlias.h` caveats.\n    if constexpr (has_async_closure_co_cleanup<V>) {\n      // Identical to the default `else` branch.  Required, since we cannot\n      // instantiate `after_cleanup_capture<V>` when `V` has `co_cleanup`.\n      return RefArgT<V&&>{\n          capture_private_t{}, forward_bind_wrapper(static_cast<V&&>(v))};\n    } else if constexpr (\n        !SharedCleanupClosure &&\n        std::is_same_v<RefArgT<V>, after_cleanup_capture<V>>) {\n      return capture<V&&>{\n          capture_private_t{}, forward_bind_wrapper(static_cast<V&&>(v))};\n    } else if constexpr (\n        !SharedCleanupClosure &&\n        std::is_same_v<RefArgT<V>, after_cleanup_capture_indirect<V>>) {\n      return capture_indirect<V&&>{\n          capture_private_t{}, forward_bind_wrapper(static_cast<V&&>(v))};\n    } else {\n      return RefArgT<V&&>{\n          capture_private_t{}, forward_bind_wrapper(static_cast<V&&>(v))};\n    }\n  }\n};\n\n// The primary template is for values, with a specialization for references.\n// Value and lval refs should quack the same, exposing a pointer-like API,\n// which (unlike regular pointers or ref wrappers) is deep-const.\n//\n// The rvalue reference specialization has a nonstandard semantics.  For\n// `capture`s, rvalue refs are **single-use**.  Users should only create\n// `capture<V&&>` if they intend to move the value, or perform another\n// destructive operation.\n//\n// Why specialize for references instead of storing `T t_;` in a single\n// class, and dispatch via SFINAE?  The main reason is that `T t_` wouldn't\n// support assignment, since `T = V&` or `T = V&&` could not be rebound.\ntemplate <typename Derived, template <typename> class RefArgT, typename V>\nclass capture_storage : public capture_crtp_base<Derived, RefArgT, V> {\n  static_assert(!std::is_reference_v<V>); // Specialized for refs below\n public:\n  constexpr capture_storage(capture_private_t, auto bind_wrapper)\n      : v_(std::move(bind_wrapper).what_to_bind()) {}\n\n protected:\n  template <typename, template <typename> class, typename>\n  friend class capture_crtp_base;\n  friend void async_closure_set_cancel_token(\n      async_closure_private_t, auto&&, const CancellationToken&);\n  friend auto async_closure_make_cleanup_tuple(\n      async_closure_private_t, auto&&, const exception_wrapper*);\n  template <typename> // For the `capture` specializations only!\n  friend struct AsyncObjectRefForSlot;\n  template <typename ArgMap, size_t ArgI, typename Arg>\n  friend decltype(auto) async_closure_resolve_backref(\n      capture_private_t, auto&, Arg&);\n\n  constexpr auto& get_lref() noexcept { return v_; }\n  constexpr const auto& get_lref() const noexcept { return v_; }\n\n  V v_;\n};\n// Future: When `R` is an rvalue reference, it might be good to support a\n// runtime check against reuse, in the style of `RValueReferenceWrapper`.\n// Unlike that class, I would make it DFATAL to avoid opt-build cost.\ntemplate <typename Derived, template <typename> class RefArgT, typename R>\n  requires std::is_reference_v<R>\nclass capture_storage<Derived, RefArgT, R>\n    : public capture_crtp_base<Derived, RefArgT, R> {\n public:\n  // This double-cast is an ugly workaround to go from `V&&` to `V*`.  We\n  // need the outer `const_cast` because C++ doesn’t allow address-of-rvalue\n  // refs, and only allows them to be cast to `const` lvalue refs.  It is\n  // safe, since the final destination type has the same const-qualification\n  // as the original `what_to_bind()` result.\n  constexpr capture_storage(capture_private_t, auto bind_wrapper)\n      : p_(&const_cast<std::remove_reference_t<R>&>(\n            static_cast<std::add_const_t<std::remove_reference_t<R>>&>(\n                std::move(bind_wrapper).what_to_bind()))) {}\n\n protected:\n  template <typename, template <typename> class, typename>\n  friend class capture_crtp_base;\n  constexpr auto& get_lref() noexcept { return *p_; }\n  constexpr const auto& get_lref() const noexcept { return *p_; }\n\n  std::remove_reference_t<R>* p_;\n};\n\n// There are no \"heap reference\" variants since a reference doesn't need to\n// know how it's stored, and \"heap\" vs \"plain\" is meant to be a low-visibility\n// implementation detail.\ntemplate <typename Derived, template <typename> class RefArgT, typename T>\n  requires(!std::is_reference_v<T> && !has_async_closure_co_cleanup<T>)\n// Since `capture_heap` is owned directly by the inner task, it has to be\n// movable to be passed to the coroutine.  But, to stay API-compatible per\n// above, it'd be preferable if users did NOT move it.  To help prevent such\n// moves, a linter is proposed in `FutureLinters.md`.\n//\n// We deliberately do NOT support moving out the underlying `unique_ptr`\n// because heap storage is meant to be an implementation detail, and is not\n// intended to be nullable.  A user needing nullability should pass a\n// `unique_ptr` either as `capture_indirect` (1 dereference) or `capture` (2).\nclass capture_heap_storage : public capture_crtp_base<Derived, RefArgT, T> {\n public:\n  capture_heap_storage(capture_private_t, auto bind_wrapper)\n      : p_(std::make_unique<T>(std::move(bind_wrapper).what_to_bind())) {}\n\n protected:\n  template <typename, template <typename> class, typename>\n  friend class capture_crtp_base;\n  constexpr auto& get_lref() noexcept { return *p_; }\n  constexpr const auto& get_lref() const noexcept { return *p_; }\n\n  std::unique_ptr<T> p_;\n};\n\n// This is a direct counterpart to `capture_storage` that collapses two\n// dereference operations into one for better UX.  There is no need for a\n// `capture_heap_indirect_storage`, since this \"indirect\" syntax sugar only\n// applies to pointer types, which are always cheaply movable, and thus\n// don't benefit from `bind::in_place`.\n//\n// Similarly, no support for `co_cleanup()` captures since those generally\n// aren't pointer-like, and won't suffer from double-dereferences.\ntemplate <typename Derived, template <typename> class RefArgT, typename T>\n  requires(!has_async_closure_co_cleanup<T>)\nclass capture_indirect_storage : public capture_storage<Derived, RefArgT, T> {\n public:\n  using capture_storage<Derived, RefArgT, T>::capture_storage;\n\n  // These are all intended to be equivalent to dereferencing the\n  // corresponding `capture<T>` twice.\n  [[nodiscard]] constexpr decltype(auto) operator*() & noexcept {\n    return *(capture_storage<Derived, RefArgT, T>::operator*());\n  }\n  [[nodiscard]] constexpr decltype(auto) operator*() const& noexcept {\n    return *(capture_storage<Derived, RefArgT, T>::operator*());\n  }\n  [[nodiscard]] constexpr decltype(auto) operator*() && noexcept {\n    return *(\n        std::move(*this).capture_storage<Derived, RefArgT, T>::operator*());\n  }\n  [[nodiscard]] constexpr decltype(auto) operator*() const&& noexcept {\n    return *(\n        std::move(*this).capture_storage<Derived, RefArgT, T>::operator*());\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() & noexcept {\n    return (capture_storage<Derived, RefArgT, T>::operator->()) -> operator->();\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() const& noexcept {\n    return (capture_storage<Derived, RefArgT, T>::operator->()) -> operator->();\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() && noexcept {\n    return (std::move(*this).capture_storage<Derived, RefArgT, T>::operator->())\n        -> operator->();\n  }\n  [[nodiscard]] constexpr decltype(auto) operator->() const&& noexcept {\n    return (std::move(*this).capture_storage<Derived, RefArgT, T>::operator->())\n        -> operator->();\n  }\n\n  // Unlike other captures, `capture_indirect` is nullable since the\n  // underlying pointer type is, too.\n  explicit constexpr operator bool() const\n      noexcept(noexcept(this->get_lref().operator bool())) {\n    return this->get_lref().operator bool();\n  }\n\n  // Use these to access the underlying `T`, instead of dereferencing twice.\n  //\n  // RISKS: Clearing or reallocating a pointer (e.g. `reset()`) in async\n  // code can cause faults for other code that holds a `capture` reference.\n  // Ideally, you should only use this if you can prove that there are no\n  // other outstanding references, or that they all expect the change.\n  decltype(auto) get_underlying_unsafe() & {\n    return capture_storage<Derived, RefArgT, T>::operator*();\n  }\n  decltype(auto) get_underlying_unsafe() const& {\n    return capture_storage<Derived, RefArgT, T>::operator*();\n  }\n  decltype(auto) get_underlying_unsafe() && {\n    return std::move(capture_storage<Derived, RefArgT, T>::operator*());\n  }\n  decltype(auto) get_underlying_unsafe() const&& {\n    return std::move(capture_storage<Derived, RefArgT, T>::operator*());\n  }\n};\n\n// `capture_safety_impl_v` is separate for `AsyncObject.h` to specialize\ntemplate <typename T, safe_alias Default>\ninline constexpr auto capture_safety_impl_v = safe_alias_of<T, Default>::value;\n\n// ALL \"capture\" types must have `folly_private_safe_alias_t` markings.\n//\n// `capture` refs are only valid as long as their on-closure storage.  They can\n// be copied/moved, so their `safe_alias` marking is the only thing preventing\n// the use of invalid references.  The docs in `enum class safe_alias` discuss\n// how safety levels are assigned for closure `capture`s.  `async_closure`\n// invokes `to_capture_ref()` to emit refs with the appropriate safety.\n//\n// If the underlying type is `<= shared_cleanup`, that leaks through to\n// all `capture`s containing it.  See e.g. `AsyncObjectPtr`.\n//   * Note: A `shared_cleanup` type `T` gives a closure a way of passing refs\n//     onto parent `safe_async_scope`s (generically: cleanup phases), so\n//     `capture<T>` must never be safer than `T` (unless we're dealing with a\n//     restricted capture ref),\n//\n// Otherwise, the safety measurement of `T` is \"outer\" to the current\n// closure, and is one of `after_cleanup_ref`, `co_cleanup_safe_ref`, or\n// `maybe_value`.  Those should all behave the same inside the closure,\n// so `MaxRefSafety` is all that matters.\n//   * Note: `capture<V>` is convertible to `capture<V&>` etc, so the ref\n//     version should never be safer.\ntemplate <typename T, safe_alias MaxRefSafety, safe_alias Default>\nstruct capture_safety\n    : safe_alias_constant<\n          (capture_safety_impl_v<std::remove_reference_t<T>, Default> <=\n           safe_alias::shared_cleanup)\n              ? std::min(\n                    MaxRefSafety,\n                    capture_safety_impl_v<std::remove_reference_t<T>, Default>)\n              : MaxRefSafety> {};\n\n} // namespace detail\n\n// Please read the file docblock.\n//\n// Rationale for the move/copy policy of `A = capture<T>`:\n//   - When `T` is a ref, `A` must be passed-by-value into coroutines, and\n//     so must be at least movable.\n//   - Ideally, for value `T`, the args would be permanently attached to the\n//     originating closure, but we have to let them be movable so that\n//     `async_closure`s without the outer task can own them.  To help\n//     prevent this, a linter is proposed in `FutureLinters.md`.\n//   - Forbid copying for rvalue ref `T` to make use-after-move linters useful.\n//     We don't follow `folly::rvalue_reference_wrapper` in adding a runtime\n//     `nullptr` check for moved-out refs, but this could be done later.\n//   - Allowing copies of lvalue refs is optional, but helpful.  For example,\n//     it lets users naturally pass arg refs into bare sub-tasks.  This seems\n//     like a reasonable & low-risk thing to do -- our operators already expose\n//     refs to the underlying data, so we can't prevent the user from passing\n//     `T&` to non-`safe_alias` callables, anyhow.\ntemplate <typename T> // may be a value or reference\n  requires(!detail::has_async_closure_co_cleanup<T>)\nclass capture : public detail::capture_storage<capture<T>, capture, T> {\n public:\n  FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(capture, T);\n  using detail::capture_storage<capture<T>, capture, T>::capture_storage;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::co_cleanup_safe_ref, Default>;\n};\ntemplate <typename T> // may be a value or reference\n  requires(!detail::has_async_closure_co_cleanup<T>)\nclass after_cleanup_capture\n    : public detail::\n          capture_storage<after_cleanup_capture<T>, after_cleanup_capture, T> {\n public:\n  FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(after_cleanup_capture, T);\n  using detail::capture_storage<\n      after_cleanup_capture<T>,\n      after_cleanup_capture,\n      T>::capture_storage;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;\n};\n\n// The use-case for `capture_heap` is to allow a closure without cleanup\n// args to avoid an inner/outer task split, while still taking\n// `bind::in_place` arguments.  This is meant to be an implementation detail\n// that's almost fully API-compatible with `capture`.  At a future\n// point we *could* remove this:\n//  - Then, any use of `bind::in_place` would auto-create an outer task.\n//  - Any user code that explicitly specifies `capture_heap` in signatures\n//    would need to be updated to `capture`.\n//  - Any places that rely on moving `capture_heap<V>` would need to migrate\n//    to `capture_indirect<std::unique_ptr<V>>{}` (which, in contrast, is\n//    nullable).  This should be rare, since we mark all value `capture`s as\n//    `unsafe` to encourage leaving the value `capture` wrappers in-closure.\ntemplate <typename T>\nclass capture_heap\n    : public detail::capture_heap_storage<capture_heap<T>, capture, T> {\n public:\n  using detail::capture_heap_storage<capture_heap<T>, capture, T>::\n      capture_heap_storage;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::co_cleanup_safe_ref, Default>;\n};\ntemplate <typename T>\nclass after_cleanup_capture_heap\n    : public detail::capture_heap_storage<\n          after_cleanup_capture_heap<T>,\n          after_cleanup_capture,\n          T> {\n public:\n  using detail::capture_heap_storage<\n      after_cleanup_capture_heap<T>,\n      after_cleanup_capture,\n      T>::capture_heap_storage;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;\n};\n\n// `capture_indirect<SomePtr<T>>` is like `capture<SomePtr<T>>` with syntax\n// sugar to avoid dereferencing twice.  Use `get_underlying_unsafe()` instead\n// of `*` / `->` to access the pointer object itself (see its doc for RISKS).\ntemplate <typename T>\nclass capture_indirect\n    : public detail::\n          capture_indirect_storage<capture_indirect<T>, capture_indirect, T> {\n public:\n  using detail::capture_indirect_storage<\n      capture_indirect<T>,\n      capture_indirect,\n      T>::capture_indirect_storage;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::co_cleanup_safe_ref, Default>;\n};\ntemplate <typename T>\nclass after_cleanup_capture_indirect\n    : public detail::capture_indirect_storage<\n          after_cleanup_capture_indirect<T>,\n          after_cleanup_capture_indirect,\n          T> {\n public:\n  using detail::capture_indirect_storage<\n      after_cleanup_capture_indirect<T>,\n      after_cleanup_capture_indirect,\n      T>::capture_indirect_storage;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;\n};\n\n// A closure that takes a cleanup arg is required to mark its directly-owned\n// `capture`s with the `after_cleanup_` prefix, to prevent refs to these\n// short-lived args from being passed into longer-lived callbacks. Similarly,\n// it may not upgrade incoming `after_cleanup_capture`s to just `capture`s.\n//\n// Don't allow r-value refs to cleanup args, since moving those out of the\n// owning closure is unexpected, and probably wrong.\ntemplate <typename T> // may be a value or lvalue reference\n  requires(!std::is_rvalue_reference_v<T> &&\n           detail::immovable_async_closure_co_cleanup<std::remove_cvref_t<T>>)\nclass co_cleanup_capture\n    : public detail::\n          capture_storage<co_cleanup_capture<T>, co_cleanup_capture, T>,\n      std::conditional_t<\n          !std::is_reference_v<T>,\n          folly::NonCopyableNonMovable,\n          tag_t<>> {\n public:\n  FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(co_cleanup_capture, T);\n  using detail::capture_storage<co_cleanup_capture<T>, co_cleanup_capture, T>::\n      capture_storage;\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::shared_cleanup, Default>;\n};\n\n// What this accomplishes, in brief -- details in `Captures.md`:\n//   - A closure that takes a `co_cleanup_capture<X&> x` from a parent will\n//     see some of its arguments downgraded to `after_cleanup_capture`.\n//   - To avoid the safety downgrade, the closure can instead take the ref\n//     as `restricted_co_cleanup_capture<X&> xr`, whose APIs will mirror\n//     those of `x`, but will be restricted to ONLY accept args with\n//     `maybe_value` safety.\n//\n// This only takes `T = V&`, because \"restricted\" is always a view on\n// an underlying `co_cleanup_capture`.\n//\n// `V` needs to ADL-customize `capture_restricted_proxy()`.\ntemplate <typename T>\n  requires(std::is_lvalue_reference_v<T> &&\n           detail::immovable_async_closure_co_cleanup<std::remove_cvref_t<T>>)\nclass restricted_co_cleanup_capture\n    : public detail::capture_storage<\n          restricted_co_cleanup_capture<T>,\n          restricted_co_cleanup_capture,\n          T>,\n      private detail::capture_restricted_tag {\n public:\n  FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(restricted_co_cleanup_capture, T);\n  using detail::capture_storage<\n      restricted_co_cleanup_capture<T>,\n      restricted_co_cleanup_capture,\n      T>::capture_storage;\n  // FIXME: `capture_safety` will still measure this as `shared_cleanup` due\n  // to `T` being that safety.  So, when implementing restricted refs, we'll\n  // have to add a new case to `capture_safety` to handle this.\n  template <safe_alias Default>\n  using folly_private_safe_alias_t =\n      detail::capture_safety<T, safe_alias::after_cleanup_ref, Default>;\n};\n\nnamespace detail {\ntemplate <typename T>\nconcept is_any_co_cleanup_capture =\n    (is_instantiation_of_v<co_cleanup_capture, T> ||\n     is_instantiation_of_v<restricted_co_cleanup_capture, T>);\ntemplate <typename T>\nconcept is_any_capture =\n    (is_instantiation_of_v<capture, T> ||\n     is_instantiation_of_v<capture_heap, T> ||\n     is_instantiation_of_v<capture_indirect, T> ||\n     is_instantiation_of_v<after_cleanup_capture, T> ||\n     is_instantiation_of_v<after_cleanup_capture_heap, T> ||\n     is_instantiation_of_v<after_cleanup_capture_indirect, T> ||\n     is_instantiation_of_v<co_cleanup_capture, T> ||\n     is_instantiation_of_v<restricted_co_cleanup_capture, T>);\ntemplate <typename T>\nconcept is_any_capture_ref =\n    is_any_capture<T> && std::is_reference_v<typename T::capture_type>;\ntemplate <typename T>\nconcept is_any_capture_val =\n    is_any_capture<T> && !std::is_reference_v<typename T::capture_type>;\n\n} // namespace detail\n\n} // namespace folly::coro\n\n// Customize `safe_closure` to store `capture<V>` as `capture<V&>`.\nnamespace folly::detail {\ntemplate <typename ST>\nstruct safe_closure_arg_storage_helper;\ntemplate <coro::detail::is_any_capture_val ST>\nstruct safe_closure_arg_storage_helper<ST> {\n  // We should never move `capture<Val>`s, so `safe_closure` will fail to\n  // implicitly convert from `capture<Val>&&` to `capture<V&>` since the\n  // `&&`-qualified conversions are `explicit` above.\n  using type = coro::capture_ref_conversion_t<ST&>;\n};\n} // namespace folly::detail\n\n#endif\n\n#undef FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE\n"
  },
  {
    "path": "folly/coro/safe/NowTask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/TaskWrapper.h>\n#include <folly/lang/SafeAlias-fwd.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\n/// `now_task<T>` quacks like `Task<T>` but is immovable, and must be\n/// `co_await`ed in the same expression that created it.\n///\n/// Using `now_task` by default brings considerable safety benefits.  With\n/// `Task`, the following would be anti-patterns that cause dangling reference\n/// bugs, but with `now_task`, C++ lifetime extension rules ensure that they\n/// simply work.\n///   - Pass-by-reference into coroutines.\n///   - Ephemeral coro lambdas with captures.\n///   - Coro lambdas with capture-by-reference.\n///\n/// Q: Is it `now_task` or `NowTask`?\n///\n/// A: In order to ease adoption, both forms will compile, but `now_task` is\n///    primary.  Both are intended to eventually be renamed to simply\n///    `folly::coro::task`, while the current movable, delayed-awaitable task\n///    will be renamed to `unsafe_task`, and largely superseded by\n///    `async_closure` and various `safe_task` flavors.\n///\n/// Notes:\n///   - (subject to change) Unlike `safe_task`, `now_task` does NOT check\n///     `safe_alias_of` for the return type `T`.  `now_task` is essentially an\n///     immediate async function -- it satisfies the structured concurrency\n///     maxim of \"lexical scope drives both control flow & lifetime\".  That\n///     lowers the odds that returned pointers/references are unexpectedly\n///     invalid.  The one failure mode I can think of is that the\n///     pointed-to-data gets invalidated by a concurrent thread of execution,\n///     but in that case the program almost certainly has a data race --\n///     regardless of the lifetime bug -- and that requires runtime\n///     instrumentation (like TSAN) to detect in present-day C++.\n\nnamespace folly::coro {\n\ntemplate <safe_alias, typename>\nclass BackgroundTask;\n\ntemplate <typename T = void>\nclass now_task;\n\ntemplate <typename T = void>\nclass now_task_with_executor;\n\n// Backwards-compatibility shims\ntemplate <typename T = void>\nusing NowTask = now_task<T>;\ntemplate <typename T = void>\nusing NowTaskWithExecutor = now_task_with_executor<T>;\n\nnamespace detail {\ntemplate <typename T>\nstruct now_task_with_executor_cfg : DoesNotWrapAwaitable {\n  using InnerTaskWithExecutorT = TaskWithExecutor<T>;\n  using WrapperTaskT = now_task<T>;\n};\ntemplate <typename T>\nusing now_task_with_executor_base =\n    ext::wrap_must_use_immediately_t<TaskWithExecutorWrapperCrtp<\n        now_task_with_executor<T>,\n        detail::now_task_with_executor_cfg<T>>>;\n} // namespace detail\n\ntemplate <typename T>\nclass [[nodiscard]]\nnow_task_with_executor final : public detail::now_task_with_executor_base<T> {\n protected:\n  using detail::now_task_with_executor_base<T>::now_task_with_executor_base;\n\n  template <safe_alias, typename>\n  friend class BackgroundTask; // for `unwrapTaskWithExecutor`, remove later\n public:\n  [[deprecated(\n      \"`as_unsafe()` is provided as an escape hatch for interoperating with \"\n      \"older futures-based code, or other places not yet compatible with \"\n      \"true structured concurrency patterns. Beware, the full `Task` API \"\n      \"abounds with footguns like `start()` and `semi()` -- including UB, \"\n      \"leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for \"\n      \" safe migration patterns.\")]]\n  TaskWithExecutor<T> as_unsafe() && {\n    return std::move(*this).unwrapTaskWithExecutor();\n  }\n};\n\nnamespace detail {\ntemplate <typename T>\nclass now_task_promise final\n    : public TaskPromiseWrapper<T, now_task<T>, TaskPromise<T>> {};\ntemplate <typename T>\nstruct now_task_cfg : DoesNotWrapAwaitable {\n  using ValueT = T;\n  using InnerTaskT = Task<T>;\n  using TaskWithExecutorT = now_task_with_executor<T>;\n  using PromiseT = now_task_promise<T>;\n};\ntemplate <typename T>\nusing now_task_base = ext::wrap_must_use_immediately_t<\n    TaskWrapperCrtp<now_task<T>, detail::now_task_cfg<T>>>;\n} // namespace detail\n\ntemplate <safe_alias, typename>\nclass safe_task;\n\ntemplate <safe_alias S, typename U>\nauto to_now_task(safe_task<S, U> t);\n\ntemplate <typename T>\nclass FOLLY_CORO_TASK_ATTRS now_task final : public detail::now_task_base<T> {\n protected:\n  using detail::now_task_base<T>::now_task_base;\n\n  template <typename U> // can construct\n  friend auto to_now_task(Task<U>);\n  template <safe_alias S, typename U> // can construct\n  friend auto to_now_task(safe_task<S, U>);\n  template <typename U> // can construct & `unwrapTask`\n  friend auto to_now_task(now_task<U>);\n\n public:\n  [[deprecated(\n      \"`as_unsafe()` is provided as an escape hatch for interoperating with \"\n      \"older futures-based code, or other places not yet compatible with \"\n      \"true structured concurrency patterns. Beware, the full `Task` API \"\n      \"abounds with footguns like `start()` and `semi()` -- including UB, \"\n      \"leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for \"\n      \" safe migration patterns.\")]]\n  Task<T> as_unsafe() && {\n    return std::move(*this).unwrapTask();\n  }\n};\n\n// NB: `to_now_task(safe_task)` is in `SafeTask.h` to avoid circular deps.\ntemplate <typename T>\nauto to_now_task(Task<T> t) {\n  return now_task<T>{std::move(t)};\n}\ntemplate <typename T>\nauto to_now_task(now_task<T> t) {\n  return now_task<T>{std::move(t).unwrapTask()};\n}\n\n// Apparently, Clang 15 has a bug in prvalue semantics support, so it cannot\n// return immovable coroutines.\n#if !defined(__clang__) || __clang_major__ > 15\n\n/// Make a `now_task` that trivially returns a value.\ntemplate <class T>\nnow_task<T> make_now_task(T t) {\n  co_return t;\n}\n\n/// Make a `now_task` that trivially returns no value\ninline now_task<> make_now_task() {\n  co_return;\n}\n/// Same as make_now_task(). See Unit\ninline now_task<> make_now_task(Unit /*unused*/) {\n  co_return;\n}\n\n/// Make a `now_task` that will trivially yield an exception.\ntemplate <class T>\nnow_task<T> make_error_now_task(exception_wrapper ew) {\n  co_yield co_error(std::move(ew));\n}\n\n#endif // no `make_now_task` on old/buggy clang\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/SafeTask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/TaskWrapper.h>\n#include <folly/coro/safe/NowTask.h>\n#include <folly/lang/SafeAlias.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro {\n\n/// Why is `SafeTask.h` useful?\n///\n/// `now_task` from `NowTask.h` should be your default.  This immovable task\n/// should always be awaited in the full-expression that created it,\n/// eliminating most classes of lifetime-safety bugs.\n///\n/// If you need a MOVABLE task with compile-time safety checks, read on.\n///\n/// Typically, you will not use `safe_task` directly.  Instead, choose one of\n/// the type-aliases below, following `APIBestPractices.md` guidance.  Briefly:\n///\n///   `value_task`:\n///   Use if your coro only takes value-semantic args.\n///\n///   `member_task`:\n///   Use for non-static member functions.  Can be awaited immediately (like\n///   `now_task`), or wrapped in an `async_closure` to support less-structured\n///   concurrency -- including scheduling on a background scope belonging to\n///   the object.  IMPORTANT: adding a `member_task` to a class requires it to\n///   have an **explicit** `folly/lang/SafeAlias.h` annotation.\n///\n///   `closure_task`:\n///   Use if your coro is called via `async_closure`.  Outside of closures,\n///   this behaves like `now_task`.\n///\n///   `co_cleanup_safe_task`:\n///   Use for tasks that can be directly scheduled on a `safe_async_scope`.\n///\n///   `auto_safe_task`:\n///   Generic coros where you want the argument & return types to automatically\n///   choose between a `now_task` and a `safe_task`.\n///\n/// `safe_task` is a thin wrapper around `folly::coro::Task` that uses\n/// `safe_alias_of` to enforce some compile-time guarantees:\n///   - The `safe_task` has `safe_alias_of` memory safety at least as high as\n///     the coro's arguments.  In particular, no args are taken by reference.\n///   - Regardless of the task's declared safety, the coro's return must\n///     have safety `maybe_value` (explained in `safe_task_ret_and_args`).\n///   - The coroutine is NOT a stateful callable -- this prohibits lambda\n///     captures, since those are a very common cause of coro memory bugs.\ntemplate <safe_alias, typename = void>\nclass safe_task;\ntemplate <safe_alias, typename = void>\nclass safe_task_with_executor;\n\n// A `safe_task` whose args and return type follow value semantics.\ntemplate <typename T = void>\nusing value_task = safe_task<safe_alias::maybe_value, T>;\n\n// A `safe_task` that can be added to `safe_async_scope`, and may run during\n// closure cleanup.  Its content must therefore be `co_cleanup_safe_ref`-safe.\ntemplate <typename T = void>\nusing co_cleanup_safe_task = safe_task<safe_alias::co_cleanup_safe_ref, T>;\n\n// Use `closure_task` as the inner coro type for tasks meant to be wrapped in\n// an `async_closure` or `async_now_closure`.  The closure code will measure\n// the safety of its args, and return a correct `safe_task`.\n//\n// Without a surrounding closure call, a `closure_task` is immovable, and works\n// like `now_task`, but with some restrictions on its arg & return types.\n//\n// Immovability rationale: `closure_task` is implemented as a `safe_task` for\n// reasons explained in the next paragraph.  But, its safety contract is weaker\n// than that of the usual closure (it can take `capture<Val>`, which should\n// never be moved) -- immovability is meant to reduce the odds of misuse.\n// Making it truly opaque / not semi-awaitable would be a stronger safeguard,\n// but that requires extra complexity even just so that\n// `value_or_fatal<closure_task<>> foo()` would compile.\n//\n// \"closure_task is a safe_task\" rationale: `async_closure` cannot emit a\n// `safe_task` without the inner coro being a `safe_task` -- otherwise it could\n// not guarantee that none of the args are taken by reference.  Conveniently,\n// `safe_task` also checks the return type is safe, and the coro's callable is\n// stateless, so `async_closure` can rely on those checks.\n//\n// This `closure_task` implementation uses a `safe_alias` level safer than\n// `unsafe` to get all of the above `SafeAlias` checks.  The level also has\n// to be less safe than `shared_cleanup` so we can treat these differently:\n//  - `capture<Value>` (safety `unsafe_closure_internal`) should stay in the\n//    original closure.  Users can move the content, but shouldn't move the\n//    wrapper, since that messes with the safety system.\n//  - `co_cleanup_capture<Value&>` refs (safety `shared_cleanup`) can safely\n//    be moved or copied into other closures.\n//  - By the way, `co_cleanup_capture<Value>` should never be moved from the\n//    owning closure that's responsible for its cleanup.\ntemplate <typename T = void>\nusing closure_task = safe_task<safe_alias::unsafe_closure_internal, T>;\n\n// A `member_task` is nearly identical to `closure_task`, except that it's\n// usable with non-static member coroutines on **stateful** classes.\n//   - In `async_closure`, it behaves like `closure_task`, with one\n//     extra constraint -- its implicit object parameter must refer to a class\n//     with an explicitly annotated `safe_alias_of` (see `SafeAlias.h`).\n//   - Without `async_closure`, it is immediately-awaitable, like `now_task`.\n//   - Since it is also a `safe_task`, it forbids `safe_alias::unsafe`\n//     arguments, and unsafe return types.\n//\n// To construct `async_closure`s with `member_task`s (for background or scope\n// tasks), use this special calling convention:\n//\n//   async_closure(bind::args{obj, args...}, FOLLY_INVOKE_MEMBER(memberFnName))\n//\n// This closure will safety-check the now-explicit object param, and produce a\n// movable `safe_task` of the safety level determined by the arguments.  This\n// integration lets us safely schedule member coros on `safe_async_scope`, pass\n// `co_cleanup` args into such coros, etc.\n//\n// Design note: Fundamentally, the reason that we have `member_task`, and\n// cannot just use `closure_task` for class members, is that C++20 does not let\n// a coroutine function distinguish (i) an \"implicit object param\" from (ii) a\n// regular lvalue reference.  For (i), `member_task` requires `async_closure`\n// to invoke it with `FOLLY_INVOKE_MEMBER`, so in this case we just need the\n// first arg to be a reference-to-a-safe-object.  In case (ii), such as\n// `closure_task`, a reference-to-a-safe-object is definitely unsafe, whereas a\n// reference to a stateless/empty object is safe enough.\ntemplate <typename T>\nusing member_task = safe_task<safe_alias::unsafe_member_internal, T>;\n\n// NB: There are some `async_closure`-specific values of `safe_alias` that\n// do not yet have a `safe_task` alias.  That's because they haven't come up\n// in user-facing type signatures.\n\nnamespace detail {\ntemplate <typename T, safe_alias Safety>\nusing auto_safe_task_impl = std::conditional_t<\n    // This checks both args & the return value because we want to avoid this\n    // resolving to a `safe_task` that won't actually compile.\n    (Safety >= safe_alias::closure_min_arg_safety &&\n     lenient_safe_alias_of_v<T> >= safe_alias::maybe_value),\n    safe_task<Safety, T>,\n    now_task<T>>;\n}\n\n/// Coros declared as `safe_task<Safety, T>` will satisfy the strong\n/// constraints above, or fail with a compile error.\n///\n/// The safety of a coroutine template may vary depending on the args or\n/// return type, meaning that the user can't actually pick a fixed\n/// safety level for their generic coro.\n///\n/// Instead, the generic coro can return `auto_safe_task<ReturnT,\n/// SafetyArgs...>`, where `SafetyArgs` is (typically) the subset of the\n/// coroutine's argument types that may affect safety.\n///\n/// `auto_safe_task` has a Significant Caveat -- you can't use it with\n/// non-`static` member functions -- the implicit object parameter is unsafe\n/// (as it should be).  And if you do use it, you will get a compile-time\n/// error instead of a `now_task`, simply because this type-function has no\n/// access to the callable.  See `APIBestPractices.md` for workarounds.\ntemplate <typename T, typename... SafetyArgs>\nusing auto_safe_task = detail::auto_safe_task_impl<\n    T,\n    // Same logic as `lenient_safe_alias_of_v`\n    safe_alias_of_pack<safe_alias::maybe_value, SafetyArgs...>::value>;\n\nnamespace detail {\n\nstruct SafeTaskTest;\n\ntemplate <safe_alias ArgSafety, typename RetT, typename... Args>\nconcept safe_task_ret_and_args =\n    ((lenient_safe_alias_of_v<Args> >= ArgSafety) && ...) &&\n    // In the event that you need a child scope to return a reference to\n    // something owned by a still-valid ancestor scope, we don't have a good\n    // way to detect this automatically.  To work around, use a `manual_safe_*`\n    // wrapper in `SafeAlias.h`, and comment why it is safe.\n    (lenient_safe_alias_of_v<RetT> >= safe_alias::maybe_value);\n\ntemplate <typename T>\nconcept is_stateless_class_or_func =\n    // `require_sizeof` avoids `is_empty` UB on incomplete types\n    (require_sizeof<T> >= 0 && std::is_empty_v<T>) ||\n    (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>);\n\ntemplate <safe_alias, typename...>\ninline constexpr bool is_safe_task_valid = false;\n// Coros taking 0 args can't be methods (no implicit object parameter),\n// so their safety is determined by the return type.\ntemplate <safe_alias ArgSafety, typename RetT>\ninline constexpr bool is_safe_task_valid<ArgSafety, RetT> =\n    safe_task_ret_and_args<ArgSafety, RetT>;\n// Inspect the first argument, which can be an implicit object parameter, to\n// allow stateless callables (like lambdas), but to prohibit stateful callables\n// (these can contain unsafe aliasing in their state, which we can't inspect\n// automatically).  If you need to make `safe_task`s from a stateful object,\n// ideas include:\n//   - Pass a `capture<YourClass&>` to a static member `YourClass::func`\n//   - Explicitly mark `safe_alias_of<YourClass>` and use `member_task`.\n//   - Future: Check out `AsyncObject.h`.\n//\n// How this works: With >= 1 args in the pack, the `First` argument\n// **could** be an implicit object parameter.  We don't know if it is, but\n// we do know that any such parameter has type lvalue reference, which means\n// that it would fail `safe_task_ret_and_args<RetT, First, Args...>`.\n//\n// This test accepts any `First` that is an lref to a stateless class\n// or function -- that is, it returns `true` if the first arg is either:\n//   - not an lref, and has no unsafe aliasing (not an implicit object param)\n//   - an lref to something stateless (MAY be an implicit object param)\n// As a side effect, this allows coros without an implicit object param to\n// pass a stateless class by reference, if it's the first param.  This\n// should be harmless in practice.\n//\n// For `member_task`, `First` is assumed to be the implicit object parameter.\n// We cannot deduce its safety, so we use `strict_safe_alias_of_v` to force the\n// user to explicitly mark this class -- incidentally, this precludes\n// `member_task` lambdas.  It is safe for `member_task` to treat\n// a ref-to-a-safe-object as safe, since it is usable only:\n//   - As a free coro, where it acts like a restricted `now_task`\n//   - Via `async_closure`, which enforces a `FOLLY_INVOKE_MEMBER` calling\n//     convention that either takes a safe capture-ref, or makes the closure\n//     take ownership of the closure.\n// We MUST NOT apply the same treatment to `closure_task`, since it cannot\n// ensure that the ref itself is safe (even if the referenced type is).\ntemplate <safe_alias ArgSafety, typename RetT, typename First, typename... Args>\ninline constexpr bool is_safe_task_valid<ArgSafety, RetT, First, Args...> =\n    (std::is_lvalue_reference_v<First> &&\n     ((ArgSafety == safe_alias::unsafe_member_internal &&\n       strict_safe_alias_of_v<std::remove_reference_t<First>> >=\n           // This `std::max` requires the implicit object parameter of\n           // `member_task`s to have safety `> shared_cleanup`.  In effect,\n           // this prohibits `member_task`s from being added to classes that\n           // store `co_cleanup_capture` refs, or (equivalently) to\n           // `async_object`s.  This tradeoff is required so that\n           // `async_now_closure()` of `member_task`s is not ALWAYS forced to\n           // apply shared-cleanup downgrades to its arguments.  The details\n           // are in `async_closure_safeties_and_bindings`.  If you find\n           // yourself blocked by this restriction, my best idea is:\n           // (1) Add another `co_cleanup_member_task` (better name TBD), with\n           //     a new safety level `< unsafe_closure_internal`.\n           // (2) Scan all mentions of `unsafe_closure_internal` to make sure\n           //     nothing assumes it is the lowest \"safe\" level. Importantly,\n           //     the `bind_captures_to_closure` logic related to\n           //     shared-cleanup in `async_now_closure` would continue to\n           //     test for `unsafe_closure_internal`, NOT the new level.\n           // (3) Special-case `co_cleanup_member_task` in this check to\n           //     let its implicit object param be `<= shared_cleanup`.\n           std::max(\n               ArgSafety,\n               safe_alias{\n                   to_underlying(safe_alias::closure_min_arg_safety) + 1})) ||\n      is_stateless_class_or_func<std::remove_reference_t<First>>))\n    ? safe_task_ret_and_args<ArgSafety, RetT, Args...>\n    : safe_task_ret_and_args<ArgSafety, RetT, First, Args...>;\n\ntemplate <safe_alias ArgSafety, typename T, typename... Args>\nclass safe_task_promise final\n    : public TaskPromiseWrapper<\n          T,\n          safe_task<ArgSafety, T>,\n          detail::TaskPromise<T>> {\n  // \"Unsafe\" is not a \"safe\" task any more.  In the future, we could have\n  // `safe_task<unsafe, T>` act as `now_task<T>`, but there's no present use\n  // for this uniformity, but there are benefits to explicitness.\n  static_assert(\n      ArgSafety > safe_alias::unsafe,\n      \"Instead of making an unsafe `safe_task`, use a `now_task`, or \"\n      \"`async_now_closure()`\");\n\n public:\n  // IMPORTANT: If you alter this arrangement, do the \"Manual test\" inside\n  // `returnsVoid` in `SafeTaskTest.cpp`.\n  //\n  // This is a no-op wrapper.  It needs to exist because `is_safe_task_valid`\n  // requires the coroutine function to be a complete type before checking if\n  // it's a stateless callable in `is_safe_task_valid`.  This is a good place\n  // to do this, since this function is guaranteed to be instantiated.\n  safe_task<ArgSafety, T> get_return_object() noexcept {\n    // If your build failed here, your `safe_task<>` coro declaration is\n    // invalid.  Specific causes for this failure:\n    //   - One of the arguments, or the return value, contains \"unsafe\n    //     aliasing\" -- see `SafeAlias.h` for the details.  Typical\n    //     causes include raw pointers, references, reference wrappers, etc.\n    //   - A stateful callable: lambda with captures, class with members, etc.\n    static_assert(\n        detail::is_safe_task_valid<ArgSafety, T, Args...>,\n        \"Bad safe_task: check for unsafe aliasing in arguments or return \"\n        \"type; also ensure your callable is stateless (or, in the case of \"\n        \"`member_task` -- explicitly specifies `safe_alias_of`).\");\n    return TaskPromiseWrapper<\n        T,\n        safe_task<ArgSafety, T>,\n        detail::TaskPromise<T>>::get_return_object();\n  }\n};\n\ntemplate <auto>\nauto bind_captures_to_closure(auto&&, auto);\n\ntemplate <safe_alias ArgSafety, typename T>\nstruct safe_task_with_executor_cfg : DoesNotWrapAwaitable {\n  using InnerTaskWithExecutorT = TaskWithExecutor<T>;\n  using WrapperTaskT = safe_task<ArgSafety, T>;\n};\n\ntemplate <safe_alias, typename>\nstruct safe_task_with_executor_base_traits;\n\ntemplate <safe_alias ArgSafety, typename T>\n  requires(ArgSafety >= safe_alias::closure_min_arg_safety)\nstruct safe_task_with_executor_base_traits<ArgSafety, T> {\n  using type = TaskWithExecutorWrapperCrtp<\n      safe_task_with_executor<ArgSafety, T>,\n      safe_task_with_executor_cfg<ArgSafety, T>>;\n};\n\n// `member_task` and `closure_task` are immovable.\ntemplate <safe_alias ArgSafety, typename T>\n  requires(ArgSafety < safe_alias::closure_min_arg_safety)\nstruct safe_task_with_executor_base_traits<ArgSafety, T> {\n  using type = ext::wrap_must_use_immediately_t<TaskWithExecutorWrapperCrtp<\n      safe_task_with_executor<ArgSafety, T>,\n      safe_task_with_executor_cfg<ArgSafety, T>>>;\n};\n\ntemplate <safe_alias ArgSafety, typename T>\nstruct safe_task_cfg : DoesNotWrapAwaitable {\n  using ValueT = T;\n  using InnerTaskT = Task<T>;\n  using TaskWithExecutorT = safe_task_with_executor<ArgSafety, T>;\n  // There is no `promise_type` here because it's added by `coroutine_traits`\n  // below.  This is the mechanism that enables `safe_task_promise` to inspect\n  // the specific arguments of the coroutine (including the implicit object\n  // parameter), and fail the compilation if anything looks unsafe.\n  using PromiseT = void;\n};\n\ntemplate <safe_alias ArgSafety, typename T>\nstruct safe_task_base_traits {\n  using type =\n      TaskWrapperCrtp<safe_task<ArgSafety, T>, safe_task_cfg<ArgSafety, T>>;\n};\n\n// `member_task` and `closure_task` are immovable.\ntemplate <safe_alias ArgSafety, typename T>\n  requires(ArgSafety < safe_alias::closure_min_arg_safety)\nstruct safe_task_base_traits<ArgSafety, T> {\n  using type = ext::wrap_must_use_immediately_t<\n      TaskWrapperCrtp<safe_task<ArgSafety, T>, safe_task_cfg<ArgSafety, T>>>;\n};\n\n} // namespace detail\n\ntemplate <safe_alias, typename>\nclass BackgroundTask;\n\n// IMPORTANT: This omits `start()` because backgrounded tasks can easily\n// outlive the references they took, defeating the purpose of `safe_task`.\n// See `BackgroundTask` instead.\ntemplate <safe_alias ArgSafety, typename T>\nclass [[nodiscard]] safe_task_with_executor final\n    : public detail::safe_task_with_executor_base_traits<ArgSafety, T>::type {\n protected:\n  using detail::safe_task_with_executor_base_traits<ArgSafety, T>::type::type;\n\n  template <safe_alias, typename>\n  friend class BackgroundTask; // for `unwrapTaskWithExecutor()`, remove later\n\n public:\n  template <safe_alias>\n  using folly_private_safe_alias_t = safe_alias_constant<ArgSafety>;\n\n  [[deprecated(\n      \"`as_unsafe()` is provided as an escape hatch for interoperating with \"\n      \"older futures-based code, or other places not yet compatible with \"\n      \"true structured concurrency patterns. Beware, the full `Task` API \"\n      \"abounds with footguns like `start()` and `semi()` -- including UB, \"\n      \"leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for \"\n      \" safe migration patterns.\")]]\n  TaskWithExecutor<T> as_unsafe() && {\n    return std::move(*this).unwrapTaskWithExecutor();\n  }\n};\n\ntemplate <safe_alias ArgSafety, typename T>\nclass FOLLY_CORO_TASK_ATTRS safe_task final\n    : public detail::safe_task_base_traits<ArgSafety, T>::type {\n protected:\n  friend struct folly::coro::detail::SafeTaskTest; // to test `withNewSafety`\n  template <safe_alias, typename>\n  friend class safe_task; // `withNewSafety` makes a different `safe_task`\n  template <auto> // uses `withNewSafety`\n  friend auto detail::bind_captures_to_closure(auto&&, auto);\n  template <safe_alias Safety, typename U>\n  friend auto to_now_task(safe_task<Safety, U>);\n\n  // The `async_closure` implementation is allowed to override the\n  // argument-deduced `lenient_safe_alias_of_v` for a `safe_task` because\n  // `capture_safety` marks some coro-stored `*capture*`s as `unsafe` even\n  // though they're safe -- to discourage users from moving them.\n  template <safe_alias NewSafety>\n  safe_task<NewSafety, T> withNewSafety() && {\n    return safe_task<NewSafety, T>{std::move(*this).unwrapTask()};\n  }\n\n public:\n  using detail::safe_task_base_traits<ArgSafety, T>::type::type;\n  template <safe_alias>\n  using folly_private_safe_alias_t = safe_alias_constant<ArgSafety>;\n\n  [[deprecated(\n      \"`as_unsafe()` is provided as an escape hatch for interoperating with \"\n      \"older futures-based code, or other places not yet compatible with \"\n      \"true structured concurrency patterns. Beware, the full `Task` API \"\n      \"abounds with footguns like `start()` and `semi()` -- including UB, \"\n      \"leaks, and lost errors. See `folly/coro/safe/docs/AsUnsafe.md` for \"\n      \" safe migration patterns.\")]]\n  Task<T> as_unsafe() && {\n    return std::move(*this).unwrapTask();\n  }\n};\n\ntemplate <safe_alias Safety, typename T>\nauto to_now_task(safe_task<Safety, T> t) {\n  return now_task<T>{std::move(t).unwrapTask()};\n}\n\nnamespace detail {\n\ntemplate <typename>\nstruct safe_task_traits;\n\ntemplate <typename T>\nstruct safe_task_traits<Task<T>> {\n  static constexpr safe_alias arg_safety = safe_alias::unsafe;\n  using return_type = T;\n};\ntemplate <typename T>\nstruct safe_task_traits<TaskWithExecutor<T>> : safe_task_traits<Task<T>> {};\ntemplate <typename T>\nstruct safe_task_traits<now_task<T>> : safe_task_traits<Task<T>> {};\ntemplate <typename T>\nstruct safe_task_traits<now_task_with_executor<T>> : safe_task_traits<Task<T>> {\n};\n\ntemplate <safe_alias ArgSafety, typename T>\nstruct safe_task_traits<safe_task<ArgSafety, T>> {\n  static constexpr safe_alias arg_safety = ArgSafety;\n  using return_type = T;\n};\ntemplate <safe_alias ArgSafety, typename T>\nstruct safe_task_traits<safe_task_with_executor<ArgSafety, T>>\n    : safe_task_traits<safe_task<ArgSafety, T>> {};\n\n} // namespace detail\n\n} // namespace folly::coro\n\ntemplate <folly::safe_alias ArgSafety, typename T, typename... Args>\nstruct folly::coro::\n    coroutine_traits<folly::coro::safe_task<ArgSafety, T>, Args...> {\n  // UGH: Pass `Args...` into `safe_task_promise` because at this point, the\n  // coroutine function is still an incomplete type, and can't be validated.\n  using promise_type =\n      folly::coro::detail::safe_task_promise<ArgSafety, T, Args...>;\n};\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/detail/AsyncClosure.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/ValueOrFatal.h>\n#include <folly/coro/safe/SafeTask.h>\n#include <folly/coro/safe/detail/BindAsyncClosure.h>\n#include <folly/detail/tuple.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\nFOLLY_PUSH_WARNING\nFOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS\n\n// DANGER: Do NOT touch this implementation without understanding the contract,\n// at least at the level of the tl;dr in `safe/AsyncClosure.h`, and in full\n// depth if you're changing `safe_alias` measurements.\n\nnamespace folly::coro::detail {\n\nvoid async_closure_set_cancel_token(\n    async_closure_private_t priv, auto&& arg, const CancellationToken& ctok) {\n  if constexpr ( // DO NOT USE: for AsyncObject only\n      requires { arg.privateHackSetParentCancelToken(arg, priv, ctok); }) {\n    arg.privateHackSetParentCancelToken(arg, priv, ctok);\n  } else if constexpr ( //\n      requires {\n        {\n          arg.get_lref().setParentCancelToken(priv, ctok)\n        } -> std::same_as<void>;\n      }) {\n    arg.get_lref().setParentCancelToken(priv, ctok);\n  }\n}\n\nauto async_closure_make_cleanup_tuple(\n    async_closure_private_t priv, auto&& arg, const exception_wrapper* err) {\n  auto to_lite_tuple = []<typename T>(T task) {\n    static_assert(\n        value_only_awaitable_v<T> && std::is_void_v<semi_await_result_t<T>>,\n        \"`co_cleanup()` must return a value-only awaitable `void` coro. \"\n        \"Change your return type to `value_or_fatal<Task<>>` and don't throw.\");\n    return lite_tuple::tuple{std::move(task)};\n  };\n  if constexpr (has_async_object_private_hack_co_cleanup<decltype(arg)>) {\n    return arg.privateHack_co_cleanup(std::move(arg), priv, err);\n  } else {\n    using ArgT = typename std::remove_reference_t<decltype(arg)>::capture_type;\n    if constexpr (has_async_closure_co_cleanup_with_error<ArgT>) {\n      return to_lite_tuple(std::move(arg.get_lref()).co_cleanup(priv, err));\n    } else if constexpr (has_async_closure_co_cleanup_error_oblivious<ArgT>) {\n      return to_lite_tuple(std::move(arg.get_lref()).co_cleanup(priv));\n    } else {\n      return lite_tuple::tuple{};\n    }\n  }\n}\n\ntemplate <typename T>\nconcept has_result_after_cleanup = requires(\n    lift_unit_t<T> t, async_closure_private_t priv) {\n  std::move(t).result_after_cleanup(priv);\n};\n\ntemplate <bool AssertNoexcept, typename T>\n  requires(!std::is_reference_v<T>)\nauto async_closure_outer_coro_result(async_closure_private_t priv, T r) {\n  if constexpr (has_result_after_cleanup<T>) {\n    static_assert(\n        !AssertNoexcept || noexcept(std::move(r).result_after_cleanup(priv)));\n    return std::move(r).result_after_cleanup(priv);\n  } else {\n    static_assert(!AssertNoexcept || std::is_nothrow_constructible_v<T, T&&>);\n    (void)priv;\n    return r;\n  }\n}\n\ntemplate <\n    bool SetCancelTok,\n    typename ResultT,\n    safe_alias OuterSafety,\n    bool AssertNoexcept>\nauto async_closure_make_outer_coro(\n    async_closure_private_t priv, auto inner_mover, auto storage_ptr) {\n  return lite_tuple::apply(\n      [&](auto... reversed_noexcept_cleanups) {\n        return async_closure_outer_coro<\n            SetCancelTok,\n            ResultT,\n            OuterSafety,\n            AssertNoexcept>(\n            priv,\n            // Doesn't downgrade safety, since movers are library-internal\n            // \"unsafe\" types that don't expose the inner type's `safe_alias`.\n            std::move(inner_mover),\n            std::move(storage_ptr),\n            // We don't require a `safe_task` for `co_cleanup` because the coro\n            // cannot outlive the object (or `exception_ptr*`) it references.\n            manual_safe_val(std::move(reversed_noexcept_cleanups))...);\n      },\n      // Contract: `co_cleanup()`s are awaited sequentially right-to-left, in\n      // the reverse of the construction order.  All cleanups finish before any\n      // of the destructors; those also run right-to-left.\n      //\n      // Implementation notes:\n      //   - `bad_alloc` safety: make the tasks before awaiting the inner coro.\n      //   - This \"apply\" is outside of `async_closure_outer_coro` because\n      //     that saves us a coro frame allocation.\n      lite_tuple::reverse_apply( // Merge `co_cleanup` tuples from all the args\n          [&](auto&... args) {\n            return lite_tuple::tuple_cat(async_closure_make_cleanup_tuple(\n                priv, args, storage_ptr->inner_err_ptr())...);\n          },\n          storage_ptr->storage_tuple_like()));\n}\n\n// IMPORTANT: This must not allow unhandled exceptions to escape, since for\n// noexcept-awaitable inner coros, the outer one is marked noexcept-awaitable.\ntemplate <\n    bool SetCancelTok,\n    typename ResultT,\n    safe_alias OuterSafety,\n    // This coro is value-only awaitable iff `async_closure_outer_coro_result`\n    // is `noexcept`. But we don't want to restrict it for coros that are not\n    // marked `value_or_fatal` -- this boolean toggles its \"is noexcept\"\n    // asserts.\n    bool AssertNoexcept,\n    typename OuterResT =\n        drop_unit_t<decltype(async_closure_outer_coro_result<AssertNoexcept>(\n            std::declval<async_closure_private_t>(),\n            std::declval<lift_unit_t<ResultT>&&>()))>>\nstd::conditional_t<\n    OuterSafety >= safe_alias::closure_min_arg_safety,\n    safe_task<OuterSafety, OuterResT>,\n    now_task<OuterResT>>\nasync_closure_outer_coro(\n    async_closure_private_t priv,\n    auto inner_mover,\n    auto storage_ptr,\n    auto... reversed_noexcept_cleanups) {\n  auto& inner_err = *storage_ptr->inner_err_ptr();\n  if constexpr (kIsDebug) {\n    inner_err.reset(); // Clear `BUG_co_cleanup_must_not_copy_error`\n  }\n\n  // Pass our cancellation token to args that want it for cleanup.  The user\n  // code can throw -- e.g. `cancellation_token_merge()` may allocate.\n  if constexpr (SetCancelTok) {\n    const auto& ctok = co_await co_current_cancellation_token;\n    inner_err = try_and_catch([&]() {\n      lite_tuple::apply(\n          [&](auto&&... args) {\n            (async_closure_set_cancel_token(priv, args, ctok), ...);\n          },\n          storage_ptr->storage_tuple_like());\n    });\n  }\n\n  // Await the inner task (unless some `setParentCancelToken` failed)\n  Try<ResultT> res;\n  if (!inner_err) {\n    // NOTE: Here and below, assume that the semi-awaitable `co_viaIfAsync`\n    // machinery for `Task` (or other `inner` type) is non-throwing.\n    // I would love a `static_assert(noexcept(...))` to prove this, but that\n    // requires plumbing `noexcept(noexcept(...))` annotations through more\n    // of `ViaIfAsync.h`.\n    res = co_await co_awaitTry(std::move(inner_mover)());\n    if (res.hasException()) {\n      inner_err = std::move(res.exception());\n    }\n  }\n\n  // We took the cleanup tasks as a pack to let us await them without making an\n  // extra coro frame.\n  (co_await std::move(reversed_noexcept_cleanups.get()), ...);\n\n  if (FOLLY_LIKELY(res.hasValue())) {\n    if constexpr (std::is_void_v<ResultT>) {\n      co_return;\n    } else {\n      co_return async_closure_outer_coro_result<AssertNoexcept>(\n          priv, std::move(res).value());\n    }\n  } else if (FOLLY_LIKELY(res.hasException())) {\n    co_yield co_error(std::move(inner_err));\n  } else { // should never happen\n    co_yield co_error(UsingUninitializedTry{});\n  }\n  (void)storage_ptr; // This param keeps the stored args alive\n}\n\n// E.g. maps <0, 2, 1, 0, 2> to <0, 2, 3, 3> -- see Test.cpp\ntemplate <auto Sum, auto...>\ninline constexpr auto cumsum_except_last = vtag<>;\ntemplate <auto Sum, auto Head, auto... Tail>\ninline constexpr auto cumsum_except_last<Sum, Head, Tail...> =\n    []<auto... Vs>(vtag_t<Vs...>) {\n      return vtag<Sum, Vs...>;\n    }(cumsum_except_last<Sum + Head, Tail...>);\n\n// When returned from `bind_captures_to_closure`, this wraps a coroutine\n// instance.  This reconciles two goals:\n//  - Let tests cover the `is_safe()` logic.\n//  - `static_assert()` the closure's safety before releasing it.\n//\n// Closure safety checks follow the model of `SafeTask.h` -- and actually\n// reuse most of that implementation by requiring the inner coro to be a\n// `safe_task`.\n//\n// Note that we don't check whether the callable passed into `async_closure`\n// is stateless, and we don't need to -- it is executed eagerly, and may be\n// a coroutine wrapper.  The coro callable underlying the inner `safe_task`\n// will have been verified to be stateless.\n//\n// Future: An `AsyncGenerator` closure flavor is possible, just think about\n// safety assertions on the yielded type, and review\n// https://fburl.com/asyncgenerator_delegation\ntemplate < // inner coro safety is measured BEFORE re-wrapping it!\n    safe_alias OuterSafety,\n    safe_alias InnerSafety,\n    typename NoexceptWrap,\n    typename OuterMover>\nclass async_closure_wrap_coro {\n private:\n  OuterMover outer_mover_;\n\n protected:\n  template <auto>\n  friend auto bind_captures_to_closure(auto&&, auto);\n  explicit async_closure_wrap_coro(OuterMover outer_mover)\n      : outer_mover_(std::move(outer_mover)) {}\n\n public:\n  // Don't allow closures with `unsafe*` args.\n  static constexpr bool has_safe_args =\n      (OuterSafety >= safe_alias::closure_min_arg_safety);\n\n  // The reason we need `safe_task` here is that it have already detected any\n  // by-reference arguments (impossible to detect otherwise), stateful\n  // coros, and unsafe return types.\n  static constexpr bool is_inner_coro_safe =\n      (InnerSafety >= safe_alias::unsafe_closure_internal);\n\n  // KEEP IN SYNC with `release_outer_coro`. Separate for testing.\n  static consteval bool is_safe() {\n    return has_safe_args && is_inner_coro_safe;\n  }\n\n  // Delay the `static_assert`s so we can test `bind_captures_to_closure`\n  // on unsafe inputs.\n  auto release_outer_coro() && {\n    // KEEP IN SYNC with `is_safe`.\n    static_assert(\n        has_safe_args,\n        \"Args passed into `async_closure()` must have `safe_alias_of` of at \"\n        \"least `shared_cleanup`. `now_task` and `async_now_closure()` do not \"\n        \"have this constraint. To force a movable closure, use `manual_safe_*`,\"\n        \" and comment with a proof of why your usage is memory-safe.\");\n    static_assert(\n        is_inner_coro_safe,\n        \"`async_closure` currently only supports `safe_task` as the inner coro.\");\n    return NoexceptWrap::wrap_with([&]() { return std::move(outer_mover_)(); });\n  }\n};\n\n// The compiler cannot deduce that `async_closure_outer_stored_arg` cannot\n// occur when `storage_ptr` is `nullopt_t`.  This helper function just\n// delays instantiation of `storage_ptr->`.\ntemplate <size_t Idx>\ndecltype(auto) get_from_storage_ptr(auto& p) {\n  return lite_tuple::get<Idx>(p->storage_tuple_like());\n}\n\ntemplate <bool Debug = kIsDebug> // ODR safeguard\ninline auto async_closure_default_inner_err() {\n  if constexpr (Debug) {\n    // If you see this diagnostic, check that your `co_cleanup` does not\n    // inadvertently copy the `exception_wrapper` parameter before creating the\n    // coro frame.  Store the provided pointer instead.\n    struct BUG_co_cleanup_must_not_copy_error : std::exception {};\n    return make_exception_wrapper<BUG_co_cleanup_must_not_copy_error>();\n  } else {\n    return exception_wrapper{};\n  }\n}\n\ntemplate <auto Tag, size_t ArgI, size_t StoredI>\nstruct async_closure_backref_entry {\n  static inline constexpr auto tag = Tag;\n  static inline constexpr size_t arg_idx = ArgI;\n  static inline constexpr size_t stored_idx = StoredI;\n};\n\ntemplate <typename... Entries>\nstruct async_closure_backref_map : Entries... {};\n\ntemplate <auto Tag, size_t ArgI, size_t StoredI>\nasync_closure_backref_entry<Tag, ArgI, StoredI> async_closure_backref_get(\n    async_closure_backref_entry<Tag, ArgI, StoredI>);\n\ntemplate <typename, size_t, typename T>\n  requires(!std::is_lvalue_reference_v<T>)\nstruct async_closure_backref_populator {\n  T&& operator()(capture_private_t, auto&, T&& t) const {\n    return static_cast<T&&>(t);\n  }\n};\n\ntemplate <typename ArgMap, size_t ArgI, typename Arg>\ndecltype(auto) async_closure_resolve_backref(\n    capture_private_t priv, auto& tup, Arg&) {\n  constexpr auto Tag = Arg::folly_bindings_identifier_tag;\n  // `BindAsyncClosure.h` populates tags via `named_bind_info_tag_v`, which\n  // uses `no_tag_t` to mean \"no tag was set\" -- so you can't look it up.\n  static_assert(!std::is_same_v<folly::bind::ext::no_tag_t, decltype(Tag)>);\n  // This will fail on missing, or ambiguous tags.\n  using Entry = decltype(async_closure_backref_get<Tag>(FOLLY_DECLVAL(ArgMap)));\n  static_assert(\n      Entry::arg_idx < ArgI,\n      \"Can only take backrefs to capture storage to the left of the current \"\n      \"capture, since in-place captures are constructed left-to-right.\");\n  auto& target = lite_tuple::get<Entry::stored_idx>(tup);\n  using SourceCapture = std::remove_reference_t<decltype(target)>;\n  static_assert(is_any_capture<SourceCapture>);\n  using Source = typename SourceCapture::capture_type;\n  // At present, it's not even possible to add an `\"x\"_id` tag to a non-stored\n  // argument.  We would also never want to allow backrefs to rvalue reference\n  // captures, since those are meant to be single-use.\n  static_assert(!std::is_reference_v<Source>);\n  return capture<Source&>(priv, forward_bind_wrapper(target.get_lref()));\n}\n\n// Replace `\"x\"_id` backreferences in the args of `bind::capture_in_place` and\n// `bind::capture_in_place_with` with `capture<T&>` references to the\n// corresponding capture storage.\n//\n// Backrefs may ONLY point to capture storage -- any args moved into the inner\n// coro are subject to unspecified destruction order, and so could not safely\n// reference each other.  In principle, we could allow backrefs to\n// `capture<T&>` refs being passed from the parent, but that adds complexity,\n// and isn't very useful.\n//\n// We don't need an explicit \"closure has outer coro\" test, since the\n// backref-population logic ONLY runs against stored args.\ntemplate <typename ArgMap, size_t ArgI, typename T, typename... Args>\n  requires(requires(Args a) { a.folly_bindings_identifier_tag; } || ...)\nstruct async_closure_backref_populator<\n    ArgMap,\n    ArgI,\n    bind_wrapper_t<folly::bind::detail::in_place_args_maker<T, Args...>>> {\n  using BindWrap =\n      bind_wrapper_t<folly::bind::detail::in_place_args_maker<T, Args...>>;\n  auto operator()(capture_private_t priv, auto& tup, BindWrap&& bw) const {\n    return lite_tuple::apply(\n        [&](Args&&... args) {\n          return unsafe_tuple_to_bind_wrapper(\n              bind::in_place_with([&]() {\n                return T{[&]() -> decltype(auto) {\n                  if constexpr (requires(Args a) {\n                                  a.folly_bindings_identifier_tag;\n                                }) {\n                    // Pass (and take) `args` by lvalue ref because moving\n                    // backref tokens doesn't make sense.\n                    return async_closure_resolve_backref<ArgMap, ArgI>(\n                        priv, tup, args);\n                  } else {\n                    return static_cast<Args&&>(args);\n                  }\n                }()...};\n              }).unsafe_tuple_to_bind());\n        },\n        static_cast<BindWrap&&>(bw).what_to_bind().release_arg_tuple());\n  }\n};\n\ntemplate <typename ArgMap, typename... Ts>\nstruct async_closure_storage {\n  template <typename... StoredArgs> // no forwarding refs\n  explicit async_closure_storage(capture_private_t priv, StoredArgs&&... sas)\n      : inner_err_(async_closure_default_inner_err()),\n        // Curly braces guarantee that in-place construction is left-to-right\n        storage_tuple_{Ts{\n            priv,\n            async_closure_backref_populator<\n                ArgMap,\n                StoredArgs::arg_idx,\n                decltype(sas.bindWrapper_)>{}(\n                priv,\n                // Here, we access `storage_tuple_` before it is constructed,\n                // which emits an \"uninitialized access\" warning.  However,\n                // this one is safe because:\n                //   - lite_tuple constructs elements left-to-right\n                //   - we check above that backrefs only point right-to-left\n                //\n                // clang-format off\n                FOLLY_PUSH_WARNING\n                FOLLY_GNU_DISABLE_WARNING(\"-Wuninitialized\")\n                storage_tuple_,\n                FOLLY_POP_WARNING\n                // clang-format on\n                static_cast<StoredArgs&&>(sas)\n                    .bindWrapper_)}...} {}\n\n  // We go through getters so that `AsyncObject` can reuse closure machinery.\n  // Note that we only need lvalue refs to the storage tuple, meaning that\n  // returning a ref-to-a-tuple is as good as a tuple-of-refs here.\n  // We return an rvalue ref for compatibility with the latter scenario.\n  auto&& storage_tuple_like() { return storage_tuple_; }\n  auto* inner_err_ptr() { return &inner_err_; }\n\n  // For `bad_alloc` safety, we must create the cleanup coros before awaiting\n  // the inner coro.  This preallocated exception (which is passed to the\n  // cleanup coros by-reference) further enables us to create the cleanup coros\n  // before we even create the outer coro.  That avoids an extra coro frame\n  // that would otherwise be need to await a cleanup tuple.\n  exception_wrapper inner_err_;\n  lite_tuple::tuple<Ts...> storage_tuple_;\n};\n\ntemplate <size_t StorageI, typename Bs, bool DerefResult = false>\ndecltype(auto) async_closure_bind_inner_coro_arg(\n    capture_private_t priv, Bs& bs, auto& storage_ptr) {\n  auto fn = [&]() -> decltype(auto) {\n    if constexpr (is_async_closure_outer_stored_arg<Bs>) {\n      // \"own\": arg was already moved into `storage_ptr`.\n      auto& storage_ref = get_from_storage_ptr<StorageI>(storage_ptr);\n      static_assert(\n          std::is_same_v<\n              typename Bs::storage_type,\n              std::remove_reference_t<decltype(storage_ref)>>);\n      // `SharedCleanupClosure=true` preserves the `after_cleanup_ref_` prefix\n      // of the storage type.\n      return storage_ref.template to_capture_ref</*shared*/ true>(priv);\n    } else if constexpr (\n        // \"own\": Move stored `bind::capture()` into inner coro.\n        is_instantiation_of_v<async_closure_inner_stored_arg, Bs> ||\n        // `scheduleSelfClosure` / `scheduleScopeClosure` self-references.\n        is_instantiation_of_v<async_closure_scope_self_ref_hack, Bs>) {\n      return typename Bs::storage_type{priv, std::move(bs.bindWrapper_)};\n    } else if constexpr (is_any_capture<Bs>) {\n      // \"pass\": Move `capture<Ref>` into the inner coro.\n      static_assert(std::is_reference_v<typename Bs::capture_type>);\n      return std::move(bs);\n    } else { // \"regular\": Non-`capture` binding.\n      static_assert(is_instantiation_of_v<async_closure_regular_arg, Bs>);\n      // We don't inspect `storage_type` here -- `detail/BindAsyncClosure.h`\n      // should have ensured that `bind_info_t` was in a default, no-op state.\n      return std::move(bs).bindWrapper_.what_to_bind();\n    }\n  };\n  if constexpr (DerefResult) {\n    return *fn();\n  } else {\n    return fn();\n  }\n}\n\ntemplate <typename, typename T>\nstruct with_tag {\n  T value;\n};\n\ntemplate <bool OnlyGetInnerCoroType, auto Cfg, typename BTup>\nauto async_closure_inner_coro_and_storage(auto&& make_inner_coro, BTup& b_tup) {\n  // Ensure the compiler doesn't waste cycles on the \"coro type detection\"\n  // code-path for safe `async_closure` -- only `async_now_closure` needs it.\n  static_assert(Cfg.emit_now_task || !OnlyGetInnerCoroType);\n  using BTupIs = std::make_index_sequence<std::tuple_size_v<BTup>>;\n  // For stored arg  @ `i`, `VtagStorageIs[i]` is a `*storage_ptr` index.\n  using VtagStorageIs = decltype(lite_tuple::apply(\n      [&]<typename... Bs>(Bs&...) {\n        return cumsum_except_last<\n            (size_t)0,\n            is_async_closure_outer_stored_arg<Bs>...>;\n      },\n      b_tup));\n\n  // If some arguments require outer-coro storage, construct them in-place\n  // on a `unique_ptr<tuple<>>`.  Without an outer coro, this stores `nullopt`.\n  //\n  // Rationale: Storing on-heap allows the outer coro own the arguments,\n  // while simultaneously providing stable pointers to be passed into the\n  // inner coro.\n  //\n  // Future: With a custom coro class, it should be possible to store the\n  // argument tuple ON the coro frame, saving one allocation.\n  auto storage_ptr = lite_tuple::apply(\n      []<typename... Entries, typename... SAs>(with_tag<Entries, SAs>... as) {\n        if constexpr (sizeof...(SAs) == 0) {\n          return std::nullopt; // Signals \"no outer closure\" to the caller\n        } else {\n          // (2) Construct all the storage args in-place in one tuple.\n          return std::make_unique<async_closure_storage<\n              async_closure_backref_map<Entries...>,\n              typename SAs::storage_type...>>(\n              capture_private_t{}, std::move(as).value...);\n        }\n      },\n      // (1) Collect the args that need storage on the outer coro.\n      []<size_t... ArgIs, size_t... StorageIs>(\n          auto& tup, std::index_sequence<ArgIs...>, vtag_t<StorageIs...>) {\n        // Future: Could support using the `self_id` backref to get a capture\n        // ref to the `async_closure_scope_self_ref_hack` arg.\n        return lite_tuple::tuple_cat([]<typename B>(B& b) {\n          if constexpr (is_async_closure_outer_stored_arg<B>) {\n            static_assert(ArgIs == B::arg_idx);\n            return lite_tuple::tuple{with_tag<\n                async_closure_backref_entry<B::tag, ArgIs, StorageIs>,\n                B>{std::move(b)}};\n          } else {\n            return lite_tuple::tuple{};\n          }\n        }(lite_tuple::get<ArgIs>(tup))...);\n      }(b_tup, BTupIs{}, VtagStorageIs{}));\n\n  using StoragePtr = decltype(storage_ptr);\n  // The `apply` + nested lambdas jointly iterate over several packs:\n  //   - Binding tuple elements: `Bs... bs` from `b_tup`, indexed by `ArgIs...`\n  //   - `StorageIs...` point into `storage_ptr` for owned captures.\n  //\n  // (1) The return type depends on `OnlyGetInnerCoroType`.\n  //\n  // `true`: Only used inside a `decltype()`, which drives the logic from\n  // `async_closure_safeties_and_bindings` labeled `task_forces_shared_cleanup`.\n  // The return type is `tuple<type_identity<InnerCoro>, unused storage_ptr>`.\n  //\n  // `false`: This is the evaluated path that makes the actual inner coro.\n  //\n  // (2) If `DerefResult` is `true` below, this means the callable is a\n  // `FOLLY_INVOKE_MEMBER`.  It accesses the member function via `.`, but this\n  // arg is expected to be `co_cleanup_capture<>` or `AsyncObjectPtr<>`, so we\n  // \"magically\" dereference it here.\n  //\n  // On member-invocation safety: `bind_captures_to_closure` will assert that\n  // we made a `member_task<T>`, which `inner_rewrapped` will implicitly unwrap\n  // & mark with a higher safety level.  By itself, `member_task` provides only\n  // a minimal safety attestation, namely:\n  //   - For the implicit object param `Arg1`,\n  //       strict_safe_alias_of_v<Arg1> > shared_cleanup\n  //   - None of the other args are taken by-reference.\n  // This is fine, since for `OuterSafety`, we will have accounted for all the\n  // args' safety levels.\n  return lite_tuple::tuple{\n      lite_tuple::apply(\n          [&]<typename... Bs>(Bs&... bs) {\n            return [&]<size_t... ArgIs, size_t... StorageIs>(\n                       std::index_sequence<ArgIs...>, vtag_t<StorageIs...>) {\n              if constexpr (OnlyGetInnerCoroType) { // Non-evaluated, see (1)\n                return type_identity<detected_or_t<\n                    void*,\n                    invoke_result_t,\n                    decltype(make_inner_coro),\n                    decltype(async_closure_bind_inner_coro_arg<\n                            StorageIs,\n                            Bs,\n                            /*DerefResult*/ Cfg.is_invoke_member && ArgIs == 0>(\n                            capture_private_t{},\n                            FOLLY_DECLVAL(Bs&),\n                            FOLLY_DECLVAL(StoragePtr&)))...>>{};\n              } else { // Actually create a coro\n                return make_inner_coro(\n                    async_closure_bind_inner_coro_arg<\n                        StorageIs,\n                        Bs,\n                        /*DerefResult*/ (Cfg.is_invoke_member && ArgIs == 0)>(\n                        capture_private_t{}, bs, storage_ptr)...);\n              }\n            }(BTupIs{}, VtagStorageIs{});\n          },\n          b_tup),\n      std::move(storage_ptr)};\n}\n\n// Eagerly construct -- but do not await -- an `async_closure`:\n//   - Resolve bindings.  For `emit_now_closure` this can  involve a tricky\n//     dance with trying two different versions of bindings, as described\n//     inside `async_closure_safeties_and_bindings`.\n//   - Construct & store args for the user-supplied inner coro.\n//   - For ensuring cleanup in the face of `bad_alloc`, pre-allocate the\n//     outer task & `co_cleanup` tasks, if needed.\n//   - Create the inner coro, passing it `capture` references, or -- if\n//     there are no `co_cleanup` args and no outer coro -- quack-alike\n//     owning wrappers.\n//   - Marks the final user-facing task with the `safe_alias` that\n//     describes the memory-safety of the closure's arguments.\n//   - Returns the task inside a wrapper that statically checks the memory\n//     safety of the return & `make_inner_coro` types when\n//     `release_outer_coro()` is called.\n//\n// NB: Due to the \"omit outer coro\" optimization, `release_outer_coro()`\n// will in some cases return a no-overhead wrapper around the coro returned\n// by `make_inner_coro()`.\n//\n// Rationale: \"Eager\" is the only option matching user expectations, since\n// regular coroutine args are bound eagerly too.  Implementation-wise, all\n// `lang/bind/Bind.h` logic has to be resolved within the current statement,\n// since the auxiliary reference-bearing objects aren't valid beyond that.\ntemplate <auto Cfg>\nauto bind_captures_to_closure(auto&& make_inner_coro, auto safeties_and_binds) {\n  // The comment in `async_closure_safeties_and_bindings` covers WHY there are\n  // two `b_tup` flavors, and why the overall algorithm is correct. In brief:\n  //   - `b_tup` is the happy path.\n  //   - `b_tup_for_unsafe_task` is only used when it turns out that the inner\n  //     coro for an `async_now_closure` is an unsafe task.  It is `nullptr` on\n  //     code paths where it definitely won't get used.\n  auto& [arg_min_safety, b_tup, b_tup_for_unsafe_task] = safeties_and_binds;\n  auto pick_safest_b_tup = [&]() -> auto& {\n    // Avoid the compile-time cost of `unsafe_inner_coro_forces_shared_cleanup`\n    if constexpr (\n        !Cfg.emit_now_task ||\n        decltype(arg_min_safety)::args_force_shared_cleanup) {\n      return b_tup;\n    } else {\n      // Per the discussion in `async_closure_safeties_and_bindings`, if\n      // `async_now_closure` produces a `safe_task`, then we don't have to\n      // force shared-cleanup behavior, and may use the `b_tup` bindings.\n      // Using `b_tup_for_unsafe_task` will cause the inner coro to see\n      // capture-refs of lower safety (e.g.  `after_cleanup_ref` instead of\n      // `co_cleanup_safe_ref`).\n      constexpr bool unsafe_inner_coro_forces_shared_cleanup =\n          Cfg.emit_now_task &&\n          (strict_safe_alias_of_v<typename std::tuple_element_t<\n               0,\n               decltype(async_closure_inner_coro_and_storage<\n                        /*get type*/ true,\n                        Cfg>(\n                   static_cast<decltype(make_inner_coro)>(make_inner_coro),\n                   b_tup))>::type> < safe_alias::unsafe_closure_internal);\n      if constexpr (unsafe_inner_coro_forces_shared_cleanup) {\n        return b_tup_for_unsafe_task;\n      } else { // Stateless inner coro\n        return b_tup;\n      }\n    }\n  };\n  auto [raw_inner_coro, storage_ptr] =\n      async_closure_inner_coro_and_storage</*get type*/ false, Cfg>(\n          static_cast<decltype(make_inner_coro)>(make_inner_coro),\n          pick_safest_b_tup());\n\n  // First, unwrap `value_or_fatal` so that `safe_task_traits` below can work.\n  // We only allow `value_or_fatal` as the outer wrapper.\n  using NoexceptWrap = value_or_fatal_rewrapper<decltype(raw_inner_coro)>;\n  auto unwrapped_inner = []<typename T>(T&& t) {\n    if constexpr (NoexceptWrap::value_or_fatal_wrapped) {\n      return NoexceptWrap::unwrapTask(std::move(t));\n    } else {\n      return ::folly::ext::must_use_immediately_unsafe_mover(std::move(t))();\n    }\n  }(std::move(raw_inner_coro));\n\n  // Compute the safety of the arguments being passed by the caller.\n  constexpr safe_alias OuterSafety = Cfg.emit_now_task\n      ? safe_alias::unsafe\n      : decltype(arg_min_safety)::parent_view;\n  // Also check that the coroutine function's signature looks safe.\n  constexpr safe_alias InnerSafety =\n      safe_task_traits<decltype(unwrapped_inner)>::arg_safety;\n\n  // This converts `raw_inner_task` into a \"task mover\" that can be plumbed\n  // down to, and used by, `async_closure_outer_coro()`.  We do 3 tricks here:\n  //   - Wrap all tasks into a \"mover\" to handle immovables like `now_task`.\n  //   - For `closure_task`, we'll internally LIE about its safety to let it be\n  //     `co_await`ed. Per below, that's OK thanks to `async_closure_wrap_coro`.\n  //   - For `safe_task` closures with the \"no outer coro\" optimization, we set\n  //     the inner coro's safety to `OuterSafety`, for reasons explained below.\n  auto inner_mover = [&]() {\n    // The first branch is always taken for safe/movable `async_closure()`\n    // invocations.  For `async_now_closure()`, this branch is taken iff the\n    // inner coro is a `closure_task` or other `safe_task`.\n    if constexpr (InnerSafety >= safe_alias::unsafe_closure_internal) {\n      // In the presence of stored `capture`s, `InnerSafety` (as measured by\n      // `safe_alias_of` on the inner coro) is not what we want.  That's\n      // because `Captures.h` marks owned captures as `unsafe_closure_internal`\n      // to discourage them being moved out of the closure.  Instead, we set\n      // safety based on `vtag_safety_of_async_closure_args` (`OuterSafety`).\n      //\n      // `closure_task` cannot be `co_await`ed, so clip to `>= min_arg_safety`.\n      // This is OK since `async_closure_wrap_coro` will later enforce:\n      //   OuterSafety >= closure_min_arg_safety\n      constexpr auto newSafety =\n          std::max(OuterSafety, safe_alias::closure_min_arg_safety);\n      return ::folly::ext::must_use_immediately_unsafe_mover(\n          std::move(unwrapped_inner).template withNewSafety<newSafety>());\n    } else { // The \"new safety\" rewrite doesn't apply to unsafe tasks!\n      return ::folly::ext::must_use_immediately_unsafe_mover(\n          std::move(unwrapped_inner));\n    }\n  }();\n\n  using ResultT = semi_await_result_t<decltype(std::move(inner_mover)())>;\n\n  // We require this calling convention because the `is_invoke_member`\n  // branch above dereferences the 1st arg.  That is only sensible if\n  // we KNOW that the arg is the implicit object parameter, which\n  // would not be true e.g.  if the user passed something like this:\n  //   [](int num, auto me) { return me->addNumber(num); }\n  static_assert(\n      std::is_same_v<member_task<ResultT>, decltype(unwrapped_inner)> ==\n          Cfg.is_invoke_member,\n      \"To use `member_task<>` coros with `async_closure`, you must pass \"\n      \"the callable as `FOLLY_INVOKE_MEMBER(memberName)`, and pass the \"\n      \"instance's `capture`/`AsyncObjectPtr`/... as the first argument.\");\n\n  auto outer_mover = [&] {\n    if constexpr (std::is_same_v<decltype(storage_ptr), std::nullopt_t>) {\n      // No outer coro is needed, so we can return the inner one.\n      static_assert(\n          !has_result_after_cleanup<ResultT>,\n          \"Cannot `co_return *after_cleanup()` without a cleanup arg\");\n      return std::move(inner_mover);\n    } else {\n      return ::folly::ext::must_use_immediately_unsafe_mover(\n          async_closure_make_outer_coro<\n              /*cancelTok*/ true,\n              ResultT,\n              OuterSafety,\n              NoexceptWrap::value_or_fatal_wrapped>(\n              async_closure_private_t{},\n              std::move(inner_mover),\n              std::move(storage_ptr)));\n    }\n  }();\n\n  if constexpr (Cfg.emit_now_task) {\n    return NoexceptWrap::wrap_with([&]() {\n      return to_now_task(std::move(outer_mover)());\n    });\n  } else {\n    return async_closure_wrap_coro<\n        OuterSafety,\n        InnerSafety,\n        NoexceptWrap,\n        decltype(outer_mover)>{std::move(outer_mover)};\n  }\n}\n\n} // namespace folly::coro::detail\n\nFOLLY_POP_WARNING\n#endif\n"
  },
  {
    "path": "folly/coro/safe/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"async_closure\",\n    headers = [\"AsyncClosure.h\"],\n    exported_deps = [\n        \":bind_async_closure\",\n        \"//folly/coro:value_or_fatal\",\n        \"//folly/coro/safe:safe_task\",\n        \"//folly/detail:tuple\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"bind_async_closure\",\n    headers = [\"BindAsyncClosure.h\"],\n    exported_deps = [\n        \"//folly/coro/safe:bind_captures\",\n        \"//folly/coro/safe:captures\",\n        \"//folly/lang:safe_alias\",\n        \"//folly/lang/bind:named\",\n        \"//folly/lang/bind:named_to_storage\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"define_movable_deep_const_lref_copyable\",\n    headers = [\"DefineMovableDeepConstLrefCopyable.h\"],\n)\n"
  },
  {
    "path": "folly/coro/safe/detail/BindAsyncClosure.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <compare>\n\n#include <folly/coro/safe/BindCaptures.h>\n#include <folly/coro/safe/Captures.h>\n#include <folly/lang/SafeAlias.h>\n#include <folly/lang/bind/Named.h> // See test `AsyncClosure.captureBackref`\n#include <folly/lang/bind/NamedToStorage.h>\n\n/// This header's `async_closure_safeties_and_bindings` implements the\n/// argument-binding logic for `async_closure`.\n///\n/// Before reading further, make sure to get familiar with:\n///   - `folly/lang/Bindings.md` for `bind::args` & friends.\n///   - `docs/Captures.md` to understand the `capture` type wrappers, and the\n///     safety upgrade/downgrade rules for passing them into closures.\n/// In particular, know this distinction:\n///   * \"owned captures\" look like `capture<V>`.  These are wrappers tied\n///     to the closure whose `bind::capture()` created it. Note that a closure\n///     with an outer coro will pass these as `capture<V&>` to the inner task.\n///   * \"capture references\" are `capture<V&>` or `<V&&>, implicitly created\n///     for the closure from any caller-provided `capture` (value or ref).\n///\n/// `async_closure` takes user-specified closure arguments as `bind::args`, an\n/// immovable object with 1-expression lifetime.  This header plumbs them\n/// through some transformations.  In opt builds, it is intended to be elided\n/// by compiler's alias analysis (NB: this needs benchmarks & perhaps tweaks).\n///\n/// Every variant of `async_closure` invokes `..._safeties_and_bindings()`,\n/// which does several jobs.  Here's a summary of the data flow:\n///   * Figure out if the closure needs an outer coro, or if it can be elided.\n///   * Measure the safety of the closure, as seen by its caller.  This uses\n///     the safeties of the arguments **before** any transformations.\n///   * Transform the arg tuple:\n///       - Convert each entry of the `bind::args` into a `capture` ref (when\n///         the caller gave us a `capture`), or one of 4 tag types\n///         (`async_closure*_arg` or `async_closure*_self_ref_hack`).  The tag\n///         types tells the `async_closure` implementation whether to store the\n///         arg, and how to bind it to the inner closure.\n///       - Figure out the storage type for each `bind::capture` binding using\n///         `bind::bind_to_storage_policy`, to support `bind::in_place*`.\n///       - Transform non-owned `capture`s via `to_capture_ref`.  Parents'\n///         owned captures are implicitly passed by-ref; `after_cleanup_` refs\n///         are \"upgraded\" if possible.  Docs in `Captures.md`.\n///       - Apply special handling to the first argument when the closure runs\n///         `FOLLY_INVOKE_MEMBER`.\n///       - Other args are perfectly forwarded.\n///   * Validate the user inputs and try to issue readable error messages.\n///     Also check internal invariants.\n///\n/// After the validation & transformation, `detail/AsyncClosure.h` is\n/// responsible for actually storing the args, and creating the coroutine.\n///\n/// ## Implementation glossary\n///\n/// This header classifies the bound arguments into a few categories:\n///   * \"owned capture\" or just \"own\": Make a new `capture` whose storage (and\n///     cleanup) belongs to this closure.\n///       - These correspond to `async_closure_{inner,outer}_stored_arg`.\n///       - If the closure is shared-cleanup, the safety of the new capture\n///         is downgraded to `after_cleanup_ref`.\n///       - The inner task sees a `capture` ref for closures with an outer\n///         coro, and a `capture` value otherwise.\n///   * \"pass capture ref\" or just \"pass\": A `capture` from the caller.\n///       - These are passed as `capture<Ref>`, even if the input is a value.\n///       - The inner coro may see upgraded safety relative to the caller.\n///   * \"regular arg\": The easy / normal case -- simply bind a forwarding\n///     reference from the caller to the inner coro.  The reference is dressed\n///     in `async_closure_regular_arg`.\n///   * \"self-reference hack\": See `async_closure_scope_self_ref_hack`.\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\nFOLLY_PUSH_WARNING\nFOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS\n\n// Future: If this is used anywhere else, move it to `BindCapturesToStorage.h`.\n//\n// We extended `bind::ext::bind_info_t` with `capture_kind`, so we must\n// explicitly specialize `bind_to_storage_policy`.  We reuse the standard\n// rules.  Custom `capture` binding logic lives in `BindAsyncClosure.h`.\nnamespace folly::bind::ext {\ntemplate <auto BI, typename BindingType>\n  requires std::same_as< // Written as a constraint to prevent object slicing\n      decltype(BI),\n      ::folly::bind::detail::capture_bind_info_t>\nclass bind_to_storage_policy<binding_t<BI, BindingType>> {\n private:\n  using standard =\n      bind_to_storage_policy<binding_t<bind_info_t{BI}, BindingType>>;\n\n public:\n  using storage_type = typename standard::storage_type;\n  using signature_type = typename standard::signature_type;\n};\n} // namespace folly::bind::ext\n\nnamespace folly::coro {\nclass AsyncObject;\nclass AsyncScopeSlotObject;\ntemplate <typename, size_t>\nclass safe_async_scope_context_proxy;\ntemplate <typename>\nclass AsyncObjectNonSlotPtr;\n} // namespace folly::coro\n\nnamespace folly::coro::detail {\n\ntemplate <safe_alias... Vs>\nconstexpr safe_alias vtag_least_safe_alias(vtag_t<Vs...>) {\n  return std::min({safe_alias::maybe_value, Vs...});\n}\n\n//\n// There are 4 tag types here, which all quack the same interface:\n//   * `storage_type`: For storing \"owned captures\", but also for measuring\n//     \"caller's point-of-view\" safety of regular args.\n//   * `bindWrapper_`: A `bind_wrapper_t<T>`, which is literally just `T` but\n//     preserving the value category even for references.  It has either the\n//     forwarding reference or the value being bound.  Note that `T` is not\n//     necessarily related to `storage_type` -- for in-place construction, it\n//     is a \"maker\", which is implicitly-convertible to the `storage_type`.\n//\n// There's also 5th case without a tag type -- when passing capture refs,\n// `async_closure_safeties_and_bindings()` simply emits an unwrapped `capture`,\n// and `AsyncClosure.h` detects it via `is_any_capture<Bs>`.\n//\n// The goal is that none of the 5 cases instantiate any storage, or call\n// copy/move constructors until the final moment, when we either:\n//   - pass the arg to the inner coro, or\n//   - store it in the outer coro's `unique_ptr<tuple<>>` of owned captures.\n//\n\n// This is just a fancy forwarding reference, never a value.\ntemplate <typename Storage, typename BindWrapper>\nstruct async_closure_regular_arg {\n  using storage_type = Storage;\n  BindWrapper bindWrapper_;\n};\n\n// Use `is_base_of` since `instantiation_of` cannot handle no-type templates\nclass async_closure_outer_stored_arg_base {};\ntemplate <typename T>\nconcept is_async_closure_outer_stored_arg =\n    std::is_base_of_v<async_closure_outer_stored_arg_base, T>;\n\n// For a given closure, all `_stored_arg` tags are going to be of one flavor.\n// With an outer coro, we capture info required to resolve backrefs.  Also, the\n// \"has outer coro\" decision isn't exported in any way besides the \"inner\" vs\n// \"outer\" stored arg type.  If useful, we could easily refactor this to be one\n// `_owned_capture` tag type, and branch on `has_outer_coro` in\n// `AsyncClosure.h`.  Reworking things this way would make it possible to have\n// an outer coro without storage, which might be needed if someone has a\n// legitimate use-case for captures that define `setParentCancelToken()` or\n// `setParentExecutor()` without defining `co_cleanup()` (dubious!).\ntemplate <is_any_capture Storage, typename BindWrapper, size_t ArgI, auto Tag>\nstruct async_closure_outer_stored_arg : async_closure_outer_stored_arg_base {\n  using storage_type = Storage;\n  constexpr static inline size_t arg_idx = ArgI;\n  constexpr static inline auto tag = Tag;\n  BindWrapper bindWrapper_;\n};\ntemplate <is_any_capture Storage, typename BindWrapper>\nstruct async_closure_inner_stored_arg {\n  using storage_type = Storage;\n  BindWrapper bindWrapper_;\n};\n\n// ## Why does this `self_ref_hack` type even exist?\n//\n// To avoid synchronization costs, `folly::coro` async scopes disallow `add()`\n// after `joinAsync()` has completed (violating this is UB).  However, it is\n// always safe to call `add()` on an async scope from a task running on that\n// **same** scope, even if `joinAsync()` has already started. This is because\n// any active scope awaitable prevents its `joinAsync()` from completing.\n//\n// Unfortunately, though it would be safe, you cannot directly pass an\n// `async_arg_cleanup<Scope&>` ref (`shared_cleanup` safety) into a closure\n// being scheduled on the SAME async scope (needs `>= co_cleanup_safe_ref`).\n// That's because at compile-time, we cannot tell if the \"scope being scheduled\n// on\" is the same as \"the scope being referenced\".\n//\n// Moreover, \"pass a ref to the current scope\" is the ONLY case that is always\n// safe [1].  Otherwise, the closure with the scope ref could run `add()` when\n// the (other) scope that it references had already been cleaned up.\n//\n// [1] Future: Thanks to `co_cleanup` ordering, it may later be feasible to\n// allow scopes that are passed later (cleaned up earlier) to reference those\n// that are passed earlier (cleaned up later).\n//\n// ## How does `self_ref_hack` help allow safe self-references for scopes?\n//\n// The solution is to introduce `scheduleScopeClosure()`, which acts just like\n// `schedule(async_closure())`, but prepends an \"implicit scope parameter\" to\n// the user-supplied `bind::args{}`.\n//\n// This implicit param is `self_ref_hack`, which is handled specially inside\n// `detail/AsyncClosure*`.  As a result, the user's inner task gets a\n// `co_cleanup_capture<Scope&>` as its first argument.\n//\n// Analogously, `AsyncObject::scheduleSelfClosure()` uses this mechanism to\n// allow sub-closures to safely reference the `Slot` of the object, which\n// contains the \"current\" async scope.\n//\n// ## Why are all aspects of `self_ref_hack` protected?\n//\n// This is NOT safe to use in other settings, because:\n// (1) We instantiates a new `capture` ref from a bare reference, bypassing the\n//     usual lifetime safety checks.\n// (2) This `ref_hack` object is deliberately excluded from the final\n//     `async_closure`'s safety accounting -- it only affects the\n//     `shard_cleanup` downgrade.\n// Both of these are ONLY okay because the sub-closure's own scope outlives it.\ntemplate <typename Storage, typename BindWrapper>\nstruct async_closure_scope_self_ref_hack {\n  using storage_type = Storage;\n\n protected:\n  template <size_t, typename Bs, bool>\n  friend decltype(auto) async_closure_bind_inner_coro_arg(\n      capture_private_t, Bs&, auto&);\n  template <typename, size_t>\n  friend class folly::coro::safe_async_scope_context_proxy;\n  friend class folly::coro::AsyncScopeSlotObject;\n  // The innards are `protected`, since `transform_binding` lets this be\n  // supplied from outside the closure machinery.  Any new client being\n  // added here must think THOROUGHLY about the risks in the class docblock!\n  explicit async_closure_scope_self_ref_hack(BindWrapper b)\n      : bindWrapper_(std::move(b)) {}\n  BindWrapper bindWrapper_;\n};\n\nstruct binding_helper_cfg {\n  bool is_shared_cleanup_closure;\n  bool has_outer_coro;\n  bool in_safety_measurement_pass;\n  constexpr auto operator<=>(const binding_helper_cfg&) const = default;\n};\n\ntemplate <typename Binding, auto Cfg, size_t ArgI>\nclass capture_binding_helper;\n\nstruct capture_ref_measurement_stub {};\n\n// This helper class only has static members.  It exists only so that the\n// various functions can share some type aliases.\ntemplate <\n    std::derived_from<bind::ext::bind_info_t> auto BI,\n    typename BindingType,\n    auto Cfg,\n    size_t ArgI>\nclass capture_binding_helper<bind::ext::binding_t<BI, BindingType>, Cfg, ArgI> {\n private:\n  // A constraint on the template would make forward-declarations messy.\n  static_assert(std::is_same_v<decltype(Cfg), binding_helper_cfg>);\n\n  using category_t = bind::ext::category_t;\n  using ST = typename bind::ext::bind_to_storage_policy<\n      bind::ext::binding_t<BI, BindingType>>::storage_type;\n  using UncvrefST = std::remove_cvref_t<ST>;\n\n  // \"Pass capture ref\" validation.  Here, `ST` is either a value or a\n  // reference `capture`.  `RetST` is the `capture<Ref>` for the inner task.\n  //\n  // There is no \"business logic\" here.  This only documents the possible data\n  // flows, `static_assert`s a few common user errors for better compiler\n  // messages, and refers to the relevant unit tests.\n  template <typename RetST>\n  static constexpr void static_assert_passing_capture() {\n    using ArgT = typename UncvrefST::capture_type;\n    static_assert(std::is_reference_v<ST> == (BI.category == category_t::ref));\n    if constexpr (std::is_reference_v<ArgT>) { // Is `capture<Ref>`?\n      // Design note: Why do we automatically pass all `capture`s by-reference?\n      //\n      // As an alternative, recall we have `bind::const_ref` / `bind::mut_ref`.\n      // These are now used for `bind::capture_const_ref` and\n      // `bind::capture_mut_ref` with different semantics (below).  Why didn't\n      // we instead use these as mandatory markings for `captures` that get\n      // passed by-reference.  That might seem more explicit, but also more\n      // confusing and harder to use:\n      //   - `const_ref` / `mut_ref CANNOT be used for \"regular\" args -- they're\n      //     by-reference iff the caller writes `T&` in the signature.\n      //   - `capture`s are intended to belong to the parent closure, it rarely\n      //     makes sense to copy or move them.\n      //   - Syntactically, `capture`s behave like pointers.\n      //   - If we needed an explicit `const_ref` / `mut_ref` only to pass\n      //     `capture<Val>` as a `capture<Ref>`, then migrating a closure from\n      //     \"has outer coro\" to \"lacks outer coro\" would require adding such a\n      //     modifier at every callsite.\n      //   - You can still move out the contents of a capture into a child by\n      //     passing an rvalue ref as `std::move(cap)` to the child, or by\n      //     passing the actual value via `*std::move(cap)`.  Similarly, `*cap`\n      //     would copy the value.\n      //\n      // N.B.  We DO use `bind::capture{const_ref{}}` etc in order to convert\n      // plain references from a parent coro into `capture` refs in a child\n      // closure, see \"capture-by-reference\" in `Captures.md`.\n      static_assert(\n          !std::is_reference_v<ST>,\n          \"Pass `capture<Ref>` by value, do not use `const_ref` / `mut_ref`\");\n      // Passing the caller's `capture<Ref>` makes a new `capture` ref object.\n      if constexpr (std::is_lvalue_reference_v<BindingType>) {\n        // Improve errors over just \"deleted copy ctor\".\n        static_assert(\n            !std::is_rvalue_reference_v<ArgT>,\n            \"capture<V&&> is move-only. Try std::move(yourRef).\");\n\n        // `check_capture_lref_to_lref` tests this branch -- passing a\n        // `capture<Ref>` that the caller bound as an lvalue.  We'll copy it,\n        // potentially upgrading `after_cleanup_capture` -> `capture.\n      } else {\n        // Cleanup args don't support rval refs.  No user-facing message, since\n        // `co_cleanup_capture` would first have a constraint failure.\n        static_assert(!is_any_co_cleanup_capture<UncvrefST>);\n\n        // Tests: `check_capture_lref_to_rref` & `check_capture_rref_to_rref`.\n        // An input `capture<Ref>` bound as an rvalue gives the child a\n        // `capture<Val&&>`.\n        //\n        // It is in some sense optional to support this, since users can\n        // pass around lval refs and `std::move(*argRef)` at the last\n        // minute.  However, I wanted to encourage the best practice of\n        // `std::move(argRef)` at the outermost callsite that knows about\n        // the move.  Reasons:\n        //   - The initial `std::move(arg)` enables use-after-move linting\n        //     in the outermost scope.\n        //   - `capture<T&&>` is move-only, meaning subsequent scopes also\n        //     get use-after-move linting.\n        //   - Future: we could build a debug-only use-after-move runtime\n        //     checker by adding some state on `capture`s.\n      }\n    } else { // Is `capture<Val>`?\n      // Cleanup args require an outer coro, so it should never be the case\n      // that `*co_cleanup_capture<Val>` is being passed to a child..\n      static_assert(!is_any_co_cleanup_capture<UncvrefST>);\n\n      // Tested in `check_capture_val_to_ref`: `capture<Val>` is implicitly\n      // passed as `capture<Ref>`.\n    }\n  }\n\n  template <typename T>\n  static constexpr auto store_as(auto bind_wrapper) {\n    if constexpr (Cfg.has_outer_coro) {\n      return async_closure_outer_stored_arg<\n          T,\n          decltype(bind_wrapper),\n          ArgI,\n          bind::ext::named_bind_info_tag_v<decltype(BI)>>{\n          .bindWrapper_ = std::move(bind_wrapper)};\n    } else {\n      return async_closure_inner_stored_arg<T, decltype(bind_wrapper)>{\n          .bindWrapper_ = std::move(bind_wrapper)};\n    }\n  }\n\n  // \"owned capture\": The closure creates storage for `bind::capture()` bindings\n  static constexpr auto store_capture_binding(auto bind_wrapper) {\n    static_assert(\n        !is_any_capture<ST>,\n        \"Given a capture `c`, do not write `bind::capture(c)` to pass it to a \"\n        \"closure. Just write `c` as the argument, and it'll automatically \"\n        \"be passed as a capture reference.\");\n    if constexpr (has_async_closure_co_cleanup<ST>) {\n      static_assert(Cfg.has_outer_coro);\n      // Future: Add a toggle to emit `restricted_co_cleanup_capture`\n      return store_as<co_cleanup_capture<ST>>(std::move(bind_wrapper));\n    } else if constexpr (\n        BI.captureKind_ == bind::detail::capture_kind::indirect) {\n      if constexpr (Cfg.is_shared_cleanup_closure) {\n        return store_as<after_cleanup_capture_indirect<ST>>(\n            std::move(bind_wrapper));\n      } else {\n        return store_as<capture_indirect<ST>>(std::move(bind_wrapper));\n      }\n    } else if constexpr (\n        !Cfg.has_outer_coro &&\n        // `bind::in_place*` is often used for immovable types, so without an\n        // outer coro, they must be on-heap to pass ownership to the inner coro.\n        bind::ext::is_binding_t_type_in_place<BindingType> &&\n        // Heuristic: Moving a type is usually cheaper than putting it on\n        // the heap.  If not, people can always use `capture_indirect` with\n        // `unique_ptr`...  Or, we could later add new capture kinds, like\n        // `plain_auto_storage = 0`, `plain_heap`, and `plain_non_heap`.\n        !std::is_move_constructible_v<BindingType>) {\n      if constexpr (Cfg.is_shared_cleanup_closure) {\n        return store_as<after_cleanup_capture_heap<ST>>(\n            std::move(bind_wrapper));\n      } else {\n        return store_as<capture_heap<ST>>(std::move(bind_wrapper));\n      }\n    } else {\n      if constexpr (Cfg.is_shared_cleanup_closure) {\n        return store_as<after_cleanup_capture<ST>>(std::move(bind_wrapper));\n      } else {\n        return store_as<capture<ST>>(std::move(bind_wrapper));\n      }\n    }\n  }\n\n  template <typename>\n  static inline constexpr bool is_supported_capture_bind_info_v = false;\n\n  template <>\n  inline constexpr bool\n      is_supported_capture_bind_info_v<bind::detail::capture_bind_info_t> =\n          true;\n\n  // Future: Right now, we only check that `\"x\"_id = ` tags are unique at time\n  // of use, and this only applies for stored captures.  But, from a pure \"code\n  // quality\" point of view, it would be reasonable to demand that all tags are\n  // unique, and that they are all used.  This could be done either as a linter\n  // or in this file, at some compile-time cost.\n  template <auto Tag>\n  static inline constexpr bool is_supported_capture_bind_info_v<\n      bind::ext::named_bind_info_t<Tag, bind::detail::capture_bind_info_t>> =\n      true;\n\n public:\n  // Transforms the binding as per the file docblock, returns a new binding.\n  // (either one of the 4 tag types above, or `capture<Ref>`)\n  static constexpr auto transform_binding(auto bind_wrapper) {\n    if constexpr (is_supported_capture_bind_info_v<decltype(BI)>) {\n      // Implement \"capture-by-reference\", docs in `Captures.md`\n      if constexpr (BI.category == category_t::ref) {\n        // Test in `check_parent_capture_ref`\n        static_assert(std::is_reference_v<ST>);\n        static_assert(\n            !is_any_capture<UncvrefST>,\n            \"Do not use `const_ref` / `mut_ref` verbs to pass a `capture` to \"\n            \"a child closure -- just pass it directly.\");\n        // It should be hard to get a ref to a co_cleanup type\n        static_assert(!has_async_closure_co_cleanup<UncvrefST>);\n        if constexpr (Cfg.in_safety_measurement_pass) {\n          return capture_ref_measurement_stub{};\n        } else if constexpr (Cfg.is_shared_cleanup_closure) {\n          return after_cleanup_capture<ST>{\n              capture_private_t{}, std::move(bind_wrapper)};\n        } else {\n          return capture<ST>{capture_private_t{}, std::move(bind_wrapper)};\n        }\n      } else { // Tests in `check_stored_*`.\n        static_assert(!std::is_reference_v<ST>);\n        return store_capture_binding(std::move(bind_wrapper));\n      }\n    } else { // Bindings for arguments the closure does NOT store.\n      static_assert(\n          std::is_same_v<vtag_t<BI>, vtag_t<bind::ext::bind_info_t{}>>,\n          \"`folly::bind::` modifiers like `constant` (or `\\\"x\\\"_id = `) \"\n          \"only make sense with `bind::capture()` bindings -- for example, to \"\n          \"move a mutable value into `const` capture storage. For regular \"\n          \"args, use `const` in the signature of your inner coro, and/or \"\n          \"`std::as_const` when passing the arg.\");\n      // If we allowed `bind::in_place` without `bind::capture`, the argument\n      // would require a copy or a move to be passed to the inner task (which\n      // the type may not support).  If `bind::capture` isn't appropriate, the\n      // user can also work around that via `std::make_unique<TheirType>` and/or\n      // `bind::capture_indirect`.\n      static_assert(\n          !bind::ext::is_binding_t_type_in_place<BindingType>,\n          \"Did you mean `bind::capture_in_place<T>(...)`?\");\n      if constexpr (is_any_capture<UncvrefST>) { // Tests in `check_capture_*`\n        // Pass preexisting `capture`s (NOT owned by this closure).\n        // Future: Add a toggle to make `restricted_co_cleanup_capture` refs.\n        auto arg_ref =\n            std::move(bind_wrapper)\n                .what_to_bind()\n                .template to_capture_ref<Cfg.is_shared_cleanup_closure>(\n                    capture_private_t{});\n        static_assert_passing_capture<decltype(arg_ref)>();\n        return std::move(arg_ref);\n      } else if constexpr (\n          is_instantiation_of_v<async_closure_scope_self_ref_hack, UncvrefST>) {\n        // This `ref_hack` type quacks like the `stored_arg` types, but we need\n        // to unwrap it for it to be handled correctly downstream.\n        return std::move(bind_wrapper).what_to_bind();\n      } else { // Test in `check_regular_args`\n        // \"regular\" args -- neither an owned capture (`bind::capture()` et al),\n        // nor a parent's `capture`. Passed via forwarding reference.\n\n        // This may be redundant, since `co_cleanup_capture` enforces that\n        // cleanup types are immovable.  If we did allow passing bare\n        // `co_cleanup` types, it could violate memory safety protections for\n        // `async_closure`s.  For `async_now_closure`, there is also no obvious\n        // use-case for passing `*captureVar` by-reference into the child.\n        static_assert(\n            !has_async_closure_co_cleanup<UncvrefST>,\n            \"This argument implements `async_closure` cleanup, so you should \"\n            \"almost certainly pass it `bind::capture()` -- or, if you already \"\n            \"have as a reference `capture`, by-value.\");\n\n        return async_closure_regular_arg<ST, decltype(bind_wrapper)>{\n            .bindWrapper_ = std::move(bind_wrapper)};\n      }\n    }\n  }\n};\n\n// See `vtag_safety_of_async_closure_args` for the docs.\n// NB: As a nested lambda, this breaks on clang-17 due to compiler bugs.\ntemplate <bool ParentViewOfSafety, typename T>\nauto vtag_safety_of_async_closure_arg() {\n  // \"owned capture\": `store_as` outputs `async_closure_*_stored_arg`.\n  if constexpr (\n      is_async_closure_outer_stored_arg<T> ||\n      is_instantiation_of_v<async_closure_inner_stored_arg, T>) {\n    using CT = typename T::storage_type::capture_type;\n    static_assert(!std::is_reference_v<CT>);\n    // Stored captures are as safe as the type being stored.  For example, when\n    // a closure stores a `BackgroundTask<Safety, T>`, it cannot be safer than\n    // `Safety`.\n    //\n    // While this replicates `lenient_safe_alias_of_v` logic, we don't directly\n    // use it here, since `AsyncObject.h` specializes `capture_safety_impl_v`.\n    return vtag<capture_safety_impl_v<CT, safe_alias::maybe_value>>;\n  } else if constexpr ( //\n      is_instantiation_of_v<async_closure_scope_self_ref_hack, T>) {\n    // This is a closure made by `spawn_self_closure()` et al. It must:\n    //  - Avoid marking the closure's outer task `shared_cleanup`, so it can\n    //    still be added to the scope that made it (`if` branch).\n    //  - Downgrade [*] the safety of its own captures (`else` branch).\n    //\n    // [*] It would be memory-unsafe to reference such captures from\n    // recursively scheduled closures on the same scope!\n    if constexpr (ParentViewOfSafety) {\n      return vtag<>;\n    } else {\n      constexpr auto storage_safety =\n          lenient_safe_alias_of_v<typename T::storage_type>;\n      // In current usage, ref_hack can only contain `co_cleanup_capture<V&>`.\n      static_assert(storage_safety == safe_alias::shared_cleanup);\n      return vtag<storage_safety>;\n    }\n  } else if constexpr (is_any_capture<T>) {\n    // \"pass capture ref\": Output of the `to_capture_ref` branch.\n    static_assert(std::is_reference_v<typename T::capture_type>);\n    return vtag<lenient_safe_alias_of_v<T>>;\n  } else if constexpr (std::is_same_v<capture_ref_measurement_stub, T>) {\n    if constexpr (ParentViewOfSafety) {\n      // Only allow capture-by-reference in `async_now_closure`s\n      return vtag<safe_alias::unsafe>;\n    } else {\n      // But, don't do closure-internal downgrades, since `transform_bindings`\n      // tries to prevent it from taking in refs to co_cleanup types this way.\n      return vtag<>;\n    }\n  } else {\n    // \"regular arg\": A non-`capture` passed via forwarding reference.\n    static_assert(is_instantiation_of_v<async_closure_regular_arg, T>);\n    return vtag<lenient_safe_alias_of_v<typename T::storage_type>>;\n  }\n}\n\n// Returns a vtag of `safe_alias_v` for the storage type of the args that\n// did not come from `store_capture_binding`.\n//\n// We have to special-case the stored ones because `Captures.h` marks the\n// `capture` wrappers for on-closure stored values `unsafe` to discourage users\n// from moving them from the original closure.  And, the wrappers themselves\n// check the safety of the underlying type (via `capture_safety`).\n//\n// The doc in `scheduleScopeClosure()` justifies why our first call to this\n// function includes `ref_hack` args in the measurement (we want the\n// closure's own args downgraded to `after_cleanup_ref` safety), but not in\n// the second (we don't want the emitted `safe_task` to be knocked down to\n// `shared_cleanup` safety, since that would make it unschedulable).\ntemplate <bool ParentViewOfSafety, typename TransformedBindingList>\nconstexpr auto vtag_safety_of_async_closure_args() {\n  return []<typename... T>(tag_t<T...>) {\n    return value_list_concat_t<\n        vtag_t,\n        decltype(vtag_safety_of_async_closure_arg<\n                 ParentViewOfSafety,\n                 T>())...>{};\n  }(TransformedBindingList{});\n}\n\ntemplate <typename BindingT>\nconstexpr bool capture_needs_outer_coro() {\n  using BP = bind::ext::bind_to_storage_policy<BindingT>;\n  using ST = typename BP::storage_type;\n  return has_async_closure_co_cleanup<ST>;\n}\n\nstruct async_closure_bindings_cfg {\n  bool force_outer_coro;\n  bool emit_now_task;\n  bool is_invoke_member;\n};\n\n// For `is_invoke_member` closures, we must run an additional lifetime-safety\n// check.  For convenience, we also implicitly wrap the first argument with\n// `bind::capture` when that's the obviously right choice.\ntemplate <async_closure_bindings_cfg Cfg>\nstruct async_closure_invoke_member_bindings {\n  constexpr auto operator()(tag_t<>) { return tag<>; }\n  template <auto BI0, typename BT0, auto... BI, typename... BT>\n  constexpr auto operator()(\n      tag_t<bind::ext::binding_t<BI0, BT0>, bind::ext::binding_t<BI, BT>...>) {\n    using T = std::remove_cvref_t<BT0>;\n    constexpr bool arg0_is_non_owning_ptr =\n        // `transform_binding()` passes captures as non-owning refs\n        is_any_capture<T> ||\n        // Raw pointers are allowed in `async_now_closure()`\n        std::is_pointer_v<T> ||\n        is_instantiation_of_v<folly::coro::AsyncObjectNonSlotPtr, T> ||\n        // `scheduleScopeClosure` & `scheduleSelfClosure` give non-owning\n        // pointers.  NB: This covers `SlotLimitedObjectPtr`.\n        is_instantiation_of_v<async_closure_scope_self_ref_hack, T>;\n    // Invoking a `member_task` requires `force_outer_coro` iff the first arg\n    // is an owning capture.\n    //\n    // NB: Both implicit & explicit `bind::capture()`s are assumed to be owning,\n    // and thus also `force_outer_coro`.\n    //\n    // The reason that `force_outer_coro` is NOT done automatically is that\n    // it adds perf overhead, which would be easily avoided if the user made\n    // their member function `static` instead.\n    static_assert(\n        Cfg.force_outer_coro || !Cfg.is_invoke_member || arg0_is_non_owning_ptr,\n        \"It looks like you want the `member_task` closure to own the object \"\n        \"instance. Use `async_now_closure(bind::args{&obj}, fn)` if that \"\n        \"applies. The next best approach is to make your task `static`, \"\n        \"with its first arg `auto self`. If that's not viable, then use \"\n        \"`async_closure_config{.force_outer_coro = true}` to allocate a \"\n        \"coro frame to own your object.\");\n    // Syntax sugar: `bind::capture()` may be left as implicit for the arg0\n    // \"object parameter\" of `FOLLY_INVOKE_MEMBER`.\n    if constexpr (\n        Cfg.is_invoke_member &&\n        // If arg0 is `bind::capture()` or similar, don't double-wrap it.\n        !std::derived_from<decltype(BI0), bind::detail::capture_bind_info_t> &&\n        // Non-owning pointer-like things don't need to be captured.\n        !arg0_is_non_owning_ptr) {\n      static_assert(\n          // BT0 is a value for `bind::in_place`, rval ref otherwise.\n          !std::is_lvalue_reference_v<BT0>,\n          \"If you call `async_closure` with `FOLLY_INVOKE_MEMBER` and \"\n          \"a non-`capture` argument, then it has to be an r-value, so \"\n          \"that the closure can take ownership of the object instance. \"\n          \"Consider `folly::copy()` or `std::move()`.\");\n      return tag<\n          bind::ext::binding_t<\n              bind::detail::capture_bind_info<\n                  bind::detail::capture_kind::plain>{}(BI0),\n              BT0>,\n          bind::ext::binding_t<BI, BT>...>;\n    } else {\n      return tag<\n          bind::ext::binding_t<BI0, BT0>,\n          bind::ext::binding_t<BI, BT>...>;\n    }\n  }\n};\n\ntemplate <safe_alias ParentView, bool ArgsForceSharedCleanup>\nstruct async_closure_arg_safety {\n  static inline constexpr auto parent_view = ParentView;\n  static inline constexpr auto args_force_shared_cleanup =\n      ArgsForceSharedCleanup;\n};\n\n// Converts forwarded arguments to bindings, figures out the storage policy\n// (outer coro?, shared cleanup?), and applies `transform_bindings` to compute\n// the final storage & binding outcome for each argument.  The caller should\n// create an outer coro iff the resulting `tuple` contains at least one\n// `async_closure_outer_stored_arg`.\n//\n// Returns a 3-tuple:\n// [0] `async_closure_arg_safety`, see `vtag_safety_of_async_closure_arg()`\n// [1] A tuple `transform_bindings()` outputs, containing:\n//       binding OR async_closure_{inner,outer}_stored_arg, ...\n//     This tuple is always used by `bind_captures_to_closure`, at least for\n//     type computations, if not for creating the actual inner coro.  It is\n//     computed as if the closure's shared-cleanup behavior were fully\n//     determined by the bound args we see here.\n// [2] Either `nullptr`, OR a tuple of `transform_bindings()` outputs identical\n//     to [1], except that shared-cleanup is force-enabled.  This tuple is only\n//     computed for the cases when `bind_captures_to_closure` would use it.\n//\n// NB: It's fine for this implementation detail to take `BoundArgs` by-ref\n// because `async_closure` & friends took them by value.\ntemplate <async_closure_bindings_cfg Cfg, typename BoundArgs>\nconstexpr auto async_closure_safeties_and_bindings(BoundArgs&& bargs) {\n  using Bindings = decltype(async_closure_invoke_member_bindings<Cfg>{}(\n      typename BoundArgs::binding_list_t{}));\n\n  auto tup = static_cast<BoundArgs&&>(bargs).unsafe_tuple_to_bind();\n  auto make_bindings = [&]<binding_helper_cfg HelperCfg>(vtag_t<HelperCfg>) {\n    return [&]<size_t... Is>(std::index_sequence<Is...>) {\n      return lite_tuple::tuple{[&]() {\n        using Binding = type_list_element_t<Is, Bindings>;\n        using T = std::tuple_element_t<Is, decltype(tup)>;\n        return capture_binding_helper<Binding, HelperCfg, Is>::\n            transform_binding(\n                bind_wrapper_t<T>{\n                    .t_ = static_cast<T&&>(lite_tuple::get<Is>(tup))});\n      }()...};\n    }(std::make_index_sequence<type_list_size_v<Bindings>>{});\n  };\n\n  // Future: If there are many `bind::in_place` arguments (which require\n  // `capture_heap`), it may be more efficient to auto-select an outer coro,\n  // for just 2 heap allocations.  Beware: this changes user-facing types\n  // (`capture_heap` to `capture`), but most users shouldn't depend on that.\n  constexpr bool has_outer_coro =\n      Cfg.force_outer_coro || []<typename... Bs>(tag_t<Bs...>) {\n        return (capture_needs_outer_coro<Bs>() || ...);\n      }(Bindings{});\n  // Figure out `IsSharedCleanupClosure` for `binding_cfg` for the real\n  // `transform_binding` call.\n  //\n  // Our choice of `is_shared_cleanup_closure = true` is important since we\n  // reuse this type list for the returned\n  // `vtag_safety_of_async_closure_args`.  That vtag is used by\n  // `async_closure` to compute the safety level for the resulting\n  // `safe_task`.  This safety must NOT be increased by reference upgrades --\n  // a reference's safety is only upgraded inside the child closure, but the\n  // original safety applies in the parent closure, which is where the\n  // returned `vtag` is consumed.\n  //\n  // Choosing `true` here does not affect the `is_shared_cleanup` choice below\n  // It merely toggles between `after_cleanup_ref_capture` and `capture`, with\n  // either `after_cleanup_ref` or `co_cleanup_safe_ref` safety.\n  using shared_cleanup_transformed_binding_types = type_list_concat_t<\n      tag_t,\n      decltype(make_bindings(\n          vtag<binding_helper_cfg{\n              .is_shared_cleanup_closure = true,\n              .has_outer_coro = has_outer_coro,\n              .in_safety_measurement_pass = true}>))>;\n  // The template args store two views of the safety of the closure's args:\n  //   - As seen by the parent, where `ParentViewOfSafety == true`,\n  //   - As used for internal safety computations.\n  //\n  // This toggle supports two usage scenarios:\n  //\n  // (1) Capture-by-reference behaviors, like `bind::capture_const_ref()` /\n  // `bind::capture(const_ref())` et al.\n  //    - `unsafe` for parent --  Since these are raw references from the\n  //      parent's scope, ensure they're only allowed in `async_now_closure`s.\n  //    - Ignored by child -- Simultaneously, we don't want the internal coro\n  //      to be subject to shared-cleanup downgrades.  Doing that would,\n  //      e.g., break the useful pattern of an on-closure scope collecting\n  //      results on a parent collector passed via capture-by-reference.\n  //\n  // (2) Closures created by `spawn_self_closure()` et al.  Also see\n  // `async_closure_scope_self_ref_hack`.\n  //\n  //   - In the returned `vtag` that measures the parent's view of the safety\n  //     of the closure, `ParentViewOfSafety == true` will exclude the\n  //     closure's first arg (the scope or object ref) from the vtag -- it\n  //     would otherwise be `shared_cleanup`.  That is, of course, the entire\n  //     point of `spawn_self_closure()` -- we happen to know that the scope\n  //     ref is safe because of the circumstances of the closure's creation.\n  //\n  //   - Using `ParentViewOfSafety = false` here makes `spawn_self_closure`s\n  //     **internally** consider themselves to be `shared_cleanup` closures.\n  //     I.e. `after_cleanup_` inputs are not upgraded, and owned captures are\n  //     downgraded to `after_cleanup_`.\n  //\n  //     To see that these downgrades are the correct behavior, imagine a chain\n  //     of closures, each calling `spawn_self_closure()` to make the next.\n  //     `safe_async_scope` awaits these concurrently, so they must not take\n  //     dependencies on each other's owned captures.\n  async_closure_arg_safety<\n      // parent_view=\n      vtag_least_safe_alias(\n          vtag_safety_of_async_closure_args<\n              /*ParentViewOfSafety*/ true,\n              shared_cleanup_transformed_binding_types>()),\n      // args_force_shared_cleanup=\n      (safe_alias::shared_cleanup >=\n       vtag_least_safe_alias(\n           vtag_safety_of_async_closure_args<\n               /*ParentViewOfSafety*/ false,\n               shared_cleanup_transformed_binding_types>()))>\n      arg_min_safety;\n\n  // How this async-closure will store and/or bind its arguments depends on\n  // whether we have to force shared-cleanup downgrades for its args.\n  //\n  // This can happen in two ways:\n  //\n  // (1) `async_closure` takes a `safe_task` and emits a `safe_task`.  Just\n  //     above, we computed` `args_force_shared_cleanup`, which says that the\n  //     closure needs shared-cleanup iff a parent passes a\n  //     `co_cleanup_capture` ref.\n  static_assert(\n      safe_alias::closure_min_arg_safety == safe_alias::shared_cleanup);\n\n  // (2) `async_now_closure` always emits a `now_task`, which is meant\n  //     to be usable even with unsafe coros & arguments.\n  //\n  //     However, we only need shared-cleanup if the inner-coro COULD access a\n  //     ref of safety <= `shared_cleanup`.  Above, we tested the explicit\n  //     inputs via `args_force_shared_cleanup`, but that misses the \"implicit\n  //     object parameter\" (aka `this`) of the coro callable.\n  //\n  //     For example, `async_now_closure(..., [&]() -> now_task<> { ...  })`\n  //     compiles, and can access potentially-unsafe refs via the lambda's\n  //     `this` pointer, which gets access to raw refs into the outer scope.\n  //\n  //     Since `make_inner_coro` is evaluated synchronously, it is allowed to\n  //     be a \"coro wrapper\", i.e. a plain lambda with unsafe captures that\n  //     calls an actual coroutine function.  We only care about the safety of\n  //     the resulting coro.  Luckily, `is_safe_task_valid` from `SafeTask.h`\n  //     constrains the safety of the implicit object parameter.  Specifically,\n  //     we know that a `safe_task` with `ArgSafety >= unsafe_internal_closure`\n  //     requires[*] the implicit object parameter to have safety `>\n  //     shared_cleanup` (yes, greater than!), which is exactly our goal here.\n  //\n  //     To sum up: Above, we saw that if `async_now_closure`'s inner coro has\n  //     safety `>= unsafe_closure_internal`, then it should not have access to\n  //     any refs of safety `<= shared_cleanup`.\n  //\n  //     HOWEVER, here, in `async_closure_safeties_and_bindings`, we do not yet\n  //     know the inner coro result type, and cannot test its safety.  In fact,\n  //     we need to synthesize the argument types for `make_inner_coro` just to\n  //     resolve the right overload.  As a workaround, the returned 3-tuple\n  //     provides bindings for BOTH options -- by tuple index:\n  //\n  //     [1] `task_forces_shared_cleanup == false`.  Use these arg bindings,\n  //         made with `.is_shared_cleanup == args_force_shared_cleanup`, to\n  //         compute the inner coro type.  Use them to actually make the inner\n  //         coro ONLY IF it has safety `>= unsafe_closure_internal`.\n  //\n  //     [2] `task_forces_shared_cleanup == true`.  These arg bindings are\n  //         computed optionally (condition below), by setting\n  //         `.is_shared_cleanup == true`.  They will be used to make a\n  //         fallback inner coro if the plan from [1] didn't work.  We force\n  //         shared cleanup because, per above, the unsafe inner coro may have\n  //         access to unsafe refs.\n  auto maybe_make_bindings_for =\n      [&]<bool task_forces_shared_cleanup>(vtag_t<task_forces_shared_cleanup>) {\n        if constexpr (\n            // Always run `make_bindings` for index 1 of the returned `tuple`.\n            !task_forces_shared_cleanup ||\n            // Only run `make_bindings` for index 2 IF it may get used.\n            (Cfg.emit_now_task && !arg_min_safety.args_force_shared_cleanup)) {\n          return make_bindings(\n              vtag<binding_helper_cfg{\n                  .is_shared_cleanup_closure = task_forces_shared_cleanup ||\n                      arg_min_safety.args_force_shared_cleanup,\n                  .has_outer_coro = has_outer_coro,\n                  .in_safety_measurement_pass = false}>);\n        } else {\n          return nullptr; // result[2] will not be used\n        }\n      };\n\n  return lite_tuple::tuple{\n      arg_min_safety,\n      // The doc above `maybe_make_bindings_for` explains how these are used.\n      maybe_make_bindings_for(vtag</*task_forces_shared_cleanup*/ false>),\n      maybe_make_bindings_for(vtag</*task_forces_shared_cleanup*/ true>)};\n}\n\n} // namespace folly::coro::detail\n\nFOLLY_POP_WARNING\n#endif\n"
  },
  {
    "path": "folly/coro/safe/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async_closure\n  HEADERS\n    AsyncClosure.h\n  EXPORTED_DEPS\n    folly_coro_safe_detail_bind_async_closure\n    folly_coro_safe_safe_task\n    folly_coro_value_or_fatal\n    folly_detail_tuple\n)\n\nfolly_add_library(\n  NAME bind_async_closure\n  HEADERS\n    BindAsyncClosure.h\n  EXPORTED_DEPS\n    folly_coro_safe_bind_captures\n    folly_coro_safe_captures\n    folly_lang_bind_named\n    folly_lang_bind_named_to_storage\n    folly_lang_safe_alias\n)\n\nfolly_add_library(\n  NAME define_movable_deep_const_lref_copyable\n  HEADERS\n    DefineMovableDeepConstLrefCopyable.h\n)\n"
  },
  {
    "path": "folly/coro/safe/detail/DefineMovableDeepConstLrefCopyable.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// DELIBERATELY omits `#pragma once`\n\n#include <type_traits>\n\n#ifdef FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE\n#error \"FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE already defined\"\n#endif\n\n// IMPORTANT:\n//   - `#include` just before use.\n//   - `#undef` after using, don't leak the macro from your header!\n//\n// ## Purpose\n//\n// This macro helps implement `capture<T>`, so `inner_type` refers to `T`.\n//\n// \"Deep const\" means that `const capture<V&>` prohibits write access to the\n// underlying V.  This macro customizes constructors to close the following\n// hole -- the default copy ctor discards the outer const qualifier:\n//\n//   const capture<V&> constRef = someCapture;\n//   capture<V&> nonConstRef = constRef; // SHOULD NOT COMPILE!\n//\n// This is like `folly::MoveOnly`, plus some copyability if `T` is an lval ref:\n//   - `YourClass<const V&>` is fully copyable\n//   - For non-const `V`, you can copy from `YourClass<V&>&` but NOT from\n//     `const YourClass<V&>&`.\n//\n// In other words, `YourClass<const int&>` is fully copyable, but\n// `YourClass<int&>` is only copyable from a non-const ref.\n//\n// ## Usage\n//\n// In the below, `YourClass` should be a leaf of the inheritance hierarchy,\n// in that none of its child classes should define special constructors.\n//\n//   #include \"DefineMovableDeepConstLrefCopyable.h\"\n//   template <typename T> // either ref or value\n//   class YourClass {\n//    public:\n//     FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(YourClass, T);\n//   };\n//   // Don't leak the macro from your header!\n//   #undef FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE\n//\n// ## Why is this a macro?\n//\n// I wish this could be a private base like `MoveOnly`, but it HAS to be a\n// macro, applied on the leaf class in your inheritance hierarchy.  This\n// requirement comes about because if you derive from a class with this\n// macro, the C++ default-constructor machinery treats the base class as\n// non-copyable, instead of the more nuanced behavior we need.  To\n// understand this in detail, check out this Compiler Explorer demo (also\n// see the commit message): https://godbolt.org/z/9bh4Wcso7\n//\n// ## Other design notes\n//\n//   - This lacks restrictions on a defaulted move from `const classname&&`\n//     because this would only apply when copyable, and our copy constructor\n//     already enforces the \"deep const\" behavior.\n//   - The destructor is provided just to shut up an over-simple linter.\n#define FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(class_name, inner_type) \\\n  class_name(class_name&&) = default;                                      \\\n  class_name& operator=(class_name&&) = default;                           \\\n  class_name(class_name&)                                                  \\\n    requires std::is_lvalue_reference_v<inner_type>                        \\\n  = default;                                                               \\\n  class_name& operator=(class_name&)                                       \\\n    requires std::is_lvalue_reference_v<inner_type>                        \\\n  = default;                                                               \\\n  class_name(const class_name&)                                            \\\n    requires(std::is_lvalue_reference_v<inner_type> &&                     \\\n             std::is_const_v<std::remove_reference_t<inner_type>>)         \\\n  = default;                                                               \\\n  class_name& operator=(const class_name&)                                 \\\n    requires(std::is_lvalue_reference_v<inner_type> &&                     \\\n             std::is_const_v<std::remove_reference_t<inner_type>>)         \\\n  = default;                                                               \\\n  ~class_name() = default\n"
  },
  {
    "path": "folly/coro/safe/detail/test/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_unittest(\n    name = \"bind_async_closure_test\",\n    srcs = [\"BindAsyncClosureTest.cpp\"],\n    deps = [\n        \"//folly/coro:task\",\n        \"//folly/coro:value_or_fatal\",\n        \"//folly/coro/safe/detail:bind_async_closure\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"define_movable_deep_const_lref_copyable_test\",\n    srcs = [\"DefineMovableDeepConstLrefCopyableTest.cpp\"],\n    deps = [\n        \"//folly/coro/safe/detail:define_movable_deep_const_lref_copyable\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/safe/detail/test/BindAsyncClosureTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Task.h>\n#include <folly/coro/ValueOrFatal.h>\n#include <folly/coro/safe/detail/BindAsyncClosure.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro::detail {\n\nstruct HasCleanup : NonCopyableNonMovable {\n  value_or_fatal<Task<>, on_stopped_void> co_cleanup(async_closure_private_t) {\n    co_return;\n  }\n};\n\nconstexpr capture_private_t coro_safe_detail_bindings_test_private() {\n  return capture_private_t{};\n}\ninline constexpr auto priv = coro_safe_detail_bindings_test_private();\n\n// This is here so that test \"runs\" show up in CI history\nTEST(BindingsTest, all_tests_run_at_build_time) {}\n\nstruct MoveMe : folly::MoveOnly {\n  int x;\n};\n\ntemplate <\n    typename ExpectedT,\n    safe_alias ExpectedSafety = safe_alias::maybe_value,\n    bool ArgsForceSharedCleanup = false>\nconstexpr void check_one_no_shared_cleanup(auto arg_fn) {\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(bind::args{arg_fn()})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<ExpectedSafety, ArgsForceSharedCleanup>,\n              lite_tuple::tuple<ExpectedT>,\n              std::nullptr_t>>);\n}\n\ntemplate <\n    typename ExpectedT,\n    safe_alias ExpectedSafety = safe_alias::maybe_value>\nconstexpr void check_one_shared_cleanup(auto arg_fn) {\n  constexpr auto ExpectedMinSafety =\n      std::min({ExpectedSafety, safe_alias::shared_cleanup});\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(bind::args{\n              arg_fn(),\n              // Triggers \"shared cleanup\" downgrade logic.\n              // NB: Without the second `&`, this would look like\n              // `std::move(c)`, which is not allowed for cleanup captures.\n              std::declval<co_cleanup_capture<HasCleanup&>&>()})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  ExpectedMinSafety,\n                  /*args_force_shared_cleanup*/ true>,\n              lite_tuple::tuple<ExpectedT, co_cleanup_capture<HasCleanup&>>,\n              std::nullptr_t>>);\n}\n\ntemplate <\n    typename ExpectedT,\n    safe_alias ExpectedSafety = safe_alias::maybe_value,\n    bool ArgsForceSharedCleanup = false>\nconstexpr void check_one(auto arg_fn) {\n  check_one_no_shared_cleanup<\n      ExpectedT,\n      ExpectedSafety,\n      ArgsForceSharedCleanup>(arg_fn);\n  check_one_shared_cleanup<ExpectedT, ExpectedSafety>(arg_fn);\n}\n\nconstexpr bool check_empty() {\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(bind::args{})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  safe_alias::maybe_value,\n                  /*args_force_shared_cleanup*/ false>,\n              lite_tuple::tuple<>,\n              std::nullptr_t>>);\n  return true;\n}\n\nstatic_assert(check_empty());\n\n// Somewhat redundant with `lang/bind/Bind.h` tests, but we should show that\n// these work \"as expected\".  No `bind::in_place` coverage here since we don't\n// allow those on the \"regular\" path.\nconstexpr bool check_regular_args() {\n  int x = 7;\n  check_one<async_closure_regular_arg<int, bind_wrapper_t<int&>>>(\n      [&]() -> auto& { return x; });\n\n  // Unsafe arg binding is relevant for `async_now_closure`\n  check_one<\n      async_closure_regular_arg<int*, bind_wrapper_t<int*&&>>,\n      safe_alias::unsafe,\n      /*args_force_shared_cleanup*/ true>([&]() -> auto* { return &x; });\n\n  MoveMe moo{.x = 7};\n  check_one<async_closure_regular_arg<MoveMe, bind_wrapper_t<MoveMe&&>>>(\n      [&]() -> auto&& { return std::move(moo); });\n\n  // The `MoveMe` test makes an rvalue ref from an lvalue, but this here is\n  // actually passing a prvalue.  The reason we test both scenarios is that\n  // it's completely fine to plumb prvalues through `async_closure`, since the\n  // resulting task will either take it by-value, or it'll be a `now_task`.\n  // Since `bind::args{}` objects are immovable, it's quite hard for a user to\n  // accidentally grab a dangling ref to a prvalue this way.\n  check_one<async_closure_regular_arg<int, bind_wrapper_t<int&&>>>([]() {\n    return 5;\n  });\n\n  return true;\n}\n\nstatic_assert(check_regular_args());\n\n// `check_capture_*_to_ref()` should closely parallel the corresponding\n// `Captures.h` tests, but we still need to check the plumbing end-to-end.\n\nconstexpr bool check_capture_val_to_ref() {\n  { // makeRef1: implicitly convert `capture<Val>` to `capture<Ref>`\n    capture<int> av{priv, forward_bind_wrapper(5)};\n    check_one<capture<int&>, safe_alias::co_cleanup_safe_ref>([&]() -> auto& {\n      return av;\n    });\n    check_one<capture<int&&>, safe_alias::co_cleanup_safe_ref>([&]() -> auto&& {\n      return std::move(av);\n    });\n  }\n\n  { // makeRef1: auto-upgrade `after_cleanup_capture<Val>` to `capture<Ref>`\n    after_cleanup_capture<int> av{priv, forward_bind_wrapper(5)};\n    // IMPORTANT: Here and below, the closure's safety is `after_cleanup_ref`\n    // **even though** the argument was upgraded.  That is, we must measure\n    // the safety of the closure according to its pre-transformation\n    // arguments.  For example, this buggy code should not compile due to the\n    // `schedule()` closure measuring as `after_cleanup_ref`.\n    //\n    //   co_await async_closure(\n    //       safeAsyncScope<CancelViaParent>(),\n    //       [](auto scope) -> closure_task<void> {\n    //         co_await async_closure(\n    //             bind::args{scope, bind::capture(123)},\n    //             [](auto outerScope, auto n) -> closure_task<void> {\n    //               outerScope.with(co_await co_current_executor).schedule(\n    //                   async_closure(\n    //                       bind::args{n},\n    //                       [](auto n2) -> closure_task<void> {\n    //                         ++n2;\n    //                         co_return;\n    //                       }));\n    //         \t   });\n    //         // BAD: The scheduled task may be running, but `n` is destroyed\n    //       });\n    check_one_no_shared_cleanup<capture<int&>, safe_alias::after_cleanup_ref>(\n        [&]() -> auto& { return av; });\n    check_one_no_shared_cleanup<capture<int&&>, safe_alias::after_cleanup_ref>(\n        [&]() -> auto&& { return std::move(av); });\n  }\n\n  { // makeRef1: no automatic upgrade due to `shared_cleanup` arg\n    after_cleanup_capture<int> av{priv, forward_bind_wrapper(5)};\n    check_one_shared_cleanup<\n        after_cleanup_capture<int&>,\n        safe_alias::after_cleanup_ref>([&]() -> auto& { return av; });\n    check_one_shared_cleanup<\n        after_cleanup_capture<int&&>,\n        safe_alias::after_cleanup_ref>([&]() -> auto&& {\n      return std::move(av);\n    });\n  }\n\n  { // makeRef2: `as_const(capture<Val>)` becomes `capture<const Ref>`\n    capture<int> av{priv, forward_bind_wrapper(5)};\n    check_one<capture<const int&>, safe_alias::co_cleanup_safe_ref>(\n        [&]() -> auto& { return std::as_const(av); });\n    check_one<capture<const int&&>, safe_alias::co_cleanup_safe_ref>(\n        // NOLINTNEXTLINE(facebook-hte-ConstMove,performance-move-const-arg)\n        [&]() -> auto&& { return std::move(std::as_const(av)); });\n  }\n\n  // Don't repeat the upgrade-related `makeRef1` tests for `makeRef2` since\n  // the same code path implements both.\n\n  return true;\n}\n\nstatic_assert(check_capture_val_to_ref());\n\nconstexpr bool check_capture_lref_to_lref() {\n  int x = 5;\n  // forwardRef: Copy `capture<V&>` bound as lref\n  {\n    capture<int&> ar{priv, forward_bind_wrapper(x)};\n    check_one<capture<int&>, safe_alias::co_cleanup_safe_ref>([&]() -> auto& {\n      return ar;\n    });\n    check_one<capture<const int&>, safe_alias::co_cleanup_safe_ref>(\n        [&]() -> auto& { return std::as_const(ar); });\n  }\n\n  // forwardRef: Upgrade `after_cleanup_capture<V&>` bound as lref\n  {\n    auto lbind_x = forward_bind_wrapper(x);\n    // NOLINTNEXTLINE(performance-move-const-arg)\n    after_cleanup_capture<int&> ar{priv, std::move(lbind_x)};\n    check_one_no_shared_cleanup<capture<int&>, safe_alias::after_cleanup_ref>(\n        [&]() -> auto& { return ar; });\n    check_one_no_shared_cleanup<\n        capture<const int&>,\n        safe_alias::after_cleanup_ref>([&]() -> auto& {\n      return std::as_const(ar);\n    });\n  }\n\n  // forwardRef: Do NOT upgrade `after_cleanup_capture<V&>`\n  {\n    after_cleanup_capture<int&> ar{priv, forward_bind_wrapper(x)};\n    check_one_shared_cleanup<\n        after_cleanup_capture<int&>,\n        safe_alias::after_cleanup_ref>([&]() -> auto& { return ar; });\n    check_one_shared_cleanup<\n        after_cleanup_capture<const int&>,\n        safe_alias::after_cleanup_ref>([&]() -> auto& {\n      return std::as_const(ar);\n    });\n  }\n\n  // forwardRef: Copy `co_cleanup_capture<V&>` bound as lref\n  {\n    HasCleanup cleanup;\n    co_cleanup_capture<HasCleanup&> ar{priv, forward_bind_wrapper(cleanup)};\n    check_one<\n        co_cleanup_capture<HasCleanup&>,\n        safe_alias::shared_cleanup,\n        /*args_force_shared_cleanup*/ true>([&]() -> auto& { return ar; });\n    check_one<\n        co_cleanup_capture<const HasCleanup&>,\n        safe_alias::shared_cleanup,\n        /*args_force_shared_cleanup*/ true>([&]() -> auto& {\n      return std::as_const(ar);\n    });\n  }\n\n  // Manual test: binding `capture<V&&>` as an lvalue won't compile\n  {\n    // NOLINTNEXTLINE(performance-move-const-arg)\n    capture<int&&> ar{priv, forward_bind_wrapper(std::move(x))};\n#if 0\n    check_one<capture<int&&>, safe_alias::co_cleanup_safe_ref>(\n                    [&]() -> auto& { return ar; });\n#endif\n  }\n\n  return true;\n}\n\nstatic_assert(check_capture_lref_to_lref());\n\nconstexpr bool check_capture_lref_to_rref() {\n  int x = 5;\n\n  // forwardRef: `capture<V&>` bound as rref -> `capture<V&&>\n  check_one<capture<int&&>, safe_alias::co_cleanup_safe_ref>([&]() {\n    return capture<int&>{priv, forward_bind_wrapper(x)};\n  });\n\n  // forwardRef: Upgrade `after_cleanup_capture<V&>` while moving\n  check_one_no_shared_cleanup<capture<int&&>, safe_alias::after_cleanup_ref>(\n      [&]() {\n        return after_cleanup_capture<int&>{priv, forward_bind_wrapper(x)};\n      });\n\n  // forwardRef: Do NOT upgrade `after_cleanup_capture<V&>` while moving\n  check_one_shared_cleanup<\n      after_cleanup_capture<int&&>,\n      safe_alias::after_cleanup_ref>([&]() {\n    return after_cleanup_capture<int&>{priv, forward_bind_wrapper(x)};\n  });\n\n  // Manual test (set to 1 for compile error): Cannot move cleanup arg refs\n#if 0\n  HasCleanup cleanup;\n  async_closure_safeties_and_bindings<async_closure_bindings_cfg{\n      .force_outer_coro = false,\n      .emit_now_task = false,\n      .is_invoke_member = false}>(bind::args{\n      co_cleanup_capture<HasCleanup&>{priv, forward_bind_wrapper(cleanup)}});\n#endif\n\n  return true;\n}\nstatic_assert(check_capture_lref_to_rref());\n\nconstexpr bool check_capture_rref_to_rref() {\n  int x = 5;\n\n  // forwardRef: `capture<V&>` bound as rref -> `capture<V&&>\n  check_one<capture<int&&>, safe_alias::co_cleanup_safe_ref>([&]() {\n    // NOLINTNEXTLINE(performance-move-const-arg)\n    return capture<int&&>{priv, forward_bind_wrapper(std::move(x))};\n  });\n\n  // forwardRef: Upgrade `after_cleanup_capture<V&>` while moving\n  check_one_no_shared_cleanup<capture<int&&>, safe_alias::after_cleanup_ref>(\n      [&]() {\n        return after_cleanup_capture<int&&>{\n            priv,\n            // NOLINTNEXTLINE(performance-move-const-arg)\n            forward_bind_wrapper(std::move(x))};\n      });\n\n  // forwardRef: Do NOT upgrade `after_cleanup_capture<V&>` while moving\n  check_one_shared_cleanup<\n      after_cleanup_capture<int&&>,\n      safe_alias::after_cleanup_ref>([&]() {\n    return after_cleanup_capture<int&&>{\n        priv,\n        // NOLINTNEXTLINE(performance-move-const-arg)\n        forward_bind_wrapper(std::move(x))};\n  });\n\n  return true;\n}\nstatic_assert(check_capture_rref_to_rref());\n\nconstexpr bool check_owned_capture_int() {\n  check_one_no_shared_cleanup<\n      async_closure_inner_stored_arg<capture<int>, bind_wrapper_t<int&&>>>(\n      []() { return bind::capture(5); });\n  // In this test, a `co_cleanup_capture` ref is passed as an argument, but\n  // importantly, that doesn't force the closure to have an outer coro.\n  check_one_shared_cleanup<async_closure_inner_stored_arg<\n      after_cleanup_capture<int>,\n      bind_wrapper_t<int&&>>>([]() { return bind::capture(5); });\n  return true;\n}\n\nstatic_assert(check_owned_capture_int());\n\nconstexpr bool check_parent_capture_ref() {\n  int x = 5;\n  check_one_no_shared_cleanup<capture<const int&>, safe_alias::unsafe>([&]() {\n    return bind::capture_const_ref{x};\n  });\n  check_one_no_shared_cleanup<capture<int&>, safe_alias::unsafe>([&]() {\n    return bind::capture_mut_ref{x};\n  });\n  check_one_shared_cleanup<after_cleanup_capture<int&&>, safe_alias::unsafe>(\n      [&]() { return bind::capture_mut_ref{std::move(x)}; });\n\n  // Check multiple args together, including a stored argument eligible for\n  // `after_cleanup` downgrade, and a ref eligible for an upgrade.  Ensures\n  // that passing a \"capture-by-ref\" arg doesn't make a coro \"shared cleanup\".\n  // This choice should be safe since `transform_binding` doesn't allow taking\n  // references to `capture` types or `co_cleanup` types.  Contrariwise,\n  // capture-by-ref wouldn't be very useful if it did force shared cleanup.\n  constexpr async_closure_bindings_cfg Cfg{\n      .force_outer_coro = false,\n      .emit_now_task = false,\n      .is_invoke_member = false};\n  after_cleanup_capture<int> av{priv, forward_bind_wrapper(5)};\n  using ActualTup = decltype(async_closure_safeties_and_bindings<Cfg>(\n      bind::args{bind::capture{bind::const_ref{5}, 5}, av}));\n  using ExpectedTup = lite_tuple::tuple<\n      async_closure_arg_safety<\n          safe_alias::unsafe,\n          /*args_force_shared_cleanup*/ false>,\n      lite_tuple::tuple<\n          capture<const int&&>,\n          async_closure_inner_stored_arg<capture<int>, bind_wrapper_t<int&&>>,\n          capture<int&>>,\n      std::nullptr_t>;\n  static_assert(std::is_same_v<ActualTup, ExpectedTup>);\n\n  return true;\n}\n\nstatic_assert(check_parent_capture_ref());\n\nconstexpr bool check_owned_cleanup_capture() {\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(\n              bind::args{bind::capture_in_place<HasCleanup>()})),\n          lite_tuple::tuple<\n              // This is the safety from the point of view of the closure's\n              // parent.  It does not matter that inside the closure, we have\n              // a `shared_cleanup`-safety `co_cleanup_capture<HasCleanup&>`.\n              async_closure_arg_safety<\n                  safe_alias::maybe_value,\n                  /*args_force_shared_cleanup*/ false>,\n              lite_tuple::tuple<async_closure_outer_stored_arg<\n                  co_cleanup_capture<HasCleanup>,\n                  bind_wrapper_t<folly::bind::detail::in_place_args_maker<\n                      detail::HasCleanup>>,\n                  /*ArgI = */ 0,\n                  /*Tag = */ folly::bind::ext::no_tag_t{}>>,\n              std::nullptr_t>>);\n  return true;\n}\n\nstatic_assert(check_owned_cleanup_capture());\n\nconstexpr bool check_emit_now_task_blocks_ref_upgrade() {\n  int x = 7;\n  after_cleanup_capture<int&> cr{priv, forward_bind_wrapper(x)};\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(bind::args{cr})),\n          lite_tuple::tuple<\n              // ref upgrades don't affect the parent's measurement...\n              async_closure_arg_safety<\n                  safe_alias::after_cleanup_ref,\n                  /*args_force_shared_cleanup*/ false>,\n              // ...but the child sees a more-safe `capture<int&>`\n              lite_tuple::tuple<capture<int&>>,\n              std::nullptr_t>>);\n\n  // Compared to the above, we only change `.emit_now_task = true`...\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = true, // NOTE: `false` ABOVE\n                       .is_invoke_member = false}>(bind::args{cr})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  safe_alias::after_cleanup_ref,\n                  /*args_force_shared_cleanup*/ false>,\n              // ... we upgrade refs here (safe inner coro),\n              lite_tuple::tuple<capture<int&>>,\n              // ... but not here (unsafe inner coro).\n              lite_tuple::tuple<after_cleanup_capture<int&>>>>);\n\n  // Still `.emit_now_task = true`, but now with the addition of\n  // a new arg that forces shared-cleanup behavior...\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = true,\n                       .is_invoke_member = false}>(bind::args{\n              cr,\n              // NOTE: NEW ARGUMENT\n              std::declval<co_cleanup_capture<HasCleanup&>&>()})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  safe_alias::shared_cleanup,\n                  /*args_force_shared_cleanup*/ true>,\n              lite_tuple::tuple<\n                  // ... thanks to the arg, we can never upgrade refs,\n                  after_cleanup_capture<int&>,\n                  co_cleanup_capture<folly::coro::detail::HasCleanup&>>,\n              // ... so the third entry is unused.\n              std::nullptr_t>>);\n\n  return true;\n}\n\nstatic_assert(check_emit_now_task_blocks_ref_upgrade());\n\nconstexpr bool check_force_outer_coro() {\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = false,\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(\n              bind::args{bind::capture(5)})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  safe_alias::maybe_value,\n                  /*args_force_shared_cleanup*/ false>,\n              // inner <=> no outer coro\n              lite_tuple::tuple<async_closure_inner_stored_arg<\n                  capture<int>,\n                  bind_wrapper_t<int&&>>>,\n              std::nullptr_t>>);\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = true, // this changed...\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(\n              bind::args{bind::capture(5)})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  safe_alias::maybe_value,\n                  /*args_force_shared_cleanup*/ false>,\n              // ... outer <=> no outer coro\n              lite_tuple::tuple<async_closure_outer_stored_arg<\n                  capture<int>,\n                  bind_wrapper_t<int&&>,\n                  /*ArgI = */ 0,\n                  /*Tag = */ folly::bind::ext::no_tag_t{}>>,\n              std::nullptr_t>>);\n  return true;\n}\n\nstatic_assert(check_force_outer_coro());\n\nconstexpr bool check_is_invoke_member_implicit_capture() {\n  // `is_invoke_member = true` implicitly captures the 1st arg rvalue\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       // Without the outer coro, a static assert would fire.\n                       // Rationale: we need a stable address for the implicit\n                       // object parameter, and moving it into the inner coro\n                       // cannot provide that.\n                       .force_outer_coro = true,\n                       .emit_now_task = false,\n                       .is_invoke_member = true}>(\n              bind::args{MoveMe{}, MoveMe{}})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  safe_alias::maybe_value,\n                  /*args_force_shared_cleanup*/ false>,\n              lite_tuple::tuple<\n                  async_closure_outer_stored_arg<\n                      capture<MoveMe>,\n                      bind_wrapper_t<MoveMe&&>,\n                      /*ArgI = */ 0,\n                      /*Tag = */ folly::bind::ext::no_tag_t{}>,\n                  // The second arg is NOT implicitly captured\n                  async_closure_regular_arg<MoveMe, bind_wrapper_t<MoveMe&&>>>,\n              std::nullptr_t>>);\n  // Same as above, but with `is_invoke_member = false`\n  static_assert(\n      std::is_same_v<\n          decltype(async_closure_safeties_and_bindings<\n                   async_closure_bindings_cfg{\n                       .force_outer_coro = true,\n                       .emit_now_task = false,\n                       .is_invoke_member = false}>(\n              bind::args{MoveMe{}, MoveMe{}})),\n          lite_tuple::tuple<\n              async_closure_arg_safety<\n                  safe_alias::maybe_value,\n                  /*args_force_shared_cleanup*/ false>,\n              lite_tuple::tuple<\n                  // Neither arg is implicitly captured\n                  async_closure_regular_arg<MoveMe, bind_wrapper_t<MoveMe&&>>,\n                  async_closure_regular_arg<MoveMe, bind_wrapper_t<MoveMe&&>>>,\n              std::nullptr_t>>);\n  return true;\n}\n\nstatic_assert(check_is_invoke_member_implicit_capture());\n\n} // namespace folly::coro::detail\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/detail/test/DefineMovableDeepConstLrefCopyableTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/portability/GTest.h>\n\n#include <folly/coro/safe/detail/DefineMovableDeepConstLrefCopyable.h>\n\ntemplate <typename T>\nstruct Foo {\n  FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE(Foo, T);\n};\n// Don't leak this macro from your header!\n#undef FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE\n\n// This is here so that test \"runs\" show up in CI history\nTEST(DefineMovableDeepConstLrefCopyableTest, all_tests_run_at_build_time) {}\n\n// Fully movable if `T` is a value\nstatic_assert(std::is_move_constructible_v<Foo<int>>);\nstatic_assert(std::is_constructible_v<Foo<int>, Foo<int>&&>);\nstatic_assert(std::is_move_assignable_v<Foo<int>>);\nstatic_assert(std::is_assignable_v<Foo<int>, Foo<int>&&>);\n\n// Fully movable if `T` is an rval ref\nstatic_assert(std::is_move_constructible_v<Foo<int&&>>);\nstatic_assert(std::is_constructible_v<Foo<int&&>, Foo<int&&>&&>);\nstatic_assert(std::is_move_assignable_v<Foo<int&&>>);\nstatic_assert(std::is_assignable_v<Foo<int&&>, Foo<int&&>&&>);\n\n// Fully movable if `T` is an lval ref\nstatic_assert(std::is_move_constructible_v<Foo<int&>>);\nstatic_assert(std::is_constructible_v<Foo<int&>, Foo<int&>&&>);\nstatic_assert(std::is_move_assignable_v<Foo<int&>>);\nstatic_assert(std::is_assignable_v<Foo<int&>, Foo<int&>&&>);\n\n// Not copyable if `T` is a value\nstatic_assert(!std::is_copy_constructible_v<Foo<int>>);\nstatic_assert(!std::is_constructible_v<Foo<int>, Foo<int>&>);\nstatic_assert(!std::is_constructible_v<Foo<int>, const Foo<int>&>);\nstatic_assert(!std::is_constructible_v<Foo<int>, const Foo<int>&&>);\nstatic_assert(!std::is_copy_assignable_v<Foo<int>>);\nstatic_assert(!std::is_assignable_v<Foo<int>, Foo<int>&>);\nstatic_assert(!std::is_assignable_v<Foo<int>, const Foo<int>&>);\nstatic_assert(!std::is_assignable_v<Foo<int>, const Foo<int>&&>);\n\n// Not copyable if `T` is an rval ref\nstatic_assert(!std::is_copy_constructible_v<Foo<int&&>>);\nstatic_assert(!std::is_constructible_v<Foo<int&&>, Foo<int&&>&>);\nstatic_assert(!std::is_constructible_v<Foo<int&&>, const Foo<int&&>&>);\nstatic_assert(!std::is_constructible_v<Foo<int&&>, const Foo<int&&>&&>);\nstatic_assert(!std::is_copy_assignable_v<Foo<int&&>>);\nstatic_assert(!std::is_assignable_v<Foo<int&&>, Foo<int&&>&>);\nstatic_assert(!std::is_assignable_v<Foo<int&&>, const Foo<int&&>&>);\nstatic_assert(!std::is_assignable_v<Foo<int&&>, const Foo<int&&>&&>);\n\n// Folly copyable if `T` is an l-val ref to `const`\nstatic_assert(std::is_copy_constructible_v<Foo<const int&>>);\nstatic_assert(std::is_constructible_v<Foo<const int&>, Foo<const int&>&>);\nstatic_assert(std::is_constructible_v<Foo<const int&>, const Foo<const int&>&>);\nstatic_assert(\n    std::is_constructible_v<Foo<const int&>, const Foo<const int&>&&>);\nstatic_assert(std::is_copy_assignable_v<Foo<const int&>>);\nstatic_assert(std::is_assignable_v<Foo<const int&>, Foo<const int&>&>);\nstatic_assert(std::is_assignable_v<Foo<const int&>, const Foo<const int&>&>);\nstatic_assert(std::is_assignable_v<Foo<const int&>, const Foo<const int&>&&>);\n\n// Only copyable from mutable refs if `T` is an lval ref to non-`const`\nstatic_assert(std::is_copy_constructible_v<Foo<const int&>>); // Has caveat:\nstatic_assert(std::is_constructible_v<Foo<int&>, Foo<int&>&>);\nstatic_assert(!std::is_constructible_v<Foo<int&>, const Foo<int&>&>);\nstatic_assert(!std::is_constructible_v<Foo<int&>, const Foo<int&>&&>);\nstatic_assert(std::is_copy_assignable_v<Foo<const int&>>); // Has caveat:\nstatic_assert(std::is_assignable_v<Foo<int&>, Foo<int&>&>);\nstatic_assert(!std::is_assignable_v<Foo<int&>, const Foo<int&>&>);\nstatic_assert(!std::is_assignable_v<Foo<int&>, const Foo<int&>&&>);\n"
  },
  {
    "path": "folly/coro/safe/docs/AsUnsafe.md",
    "content": "## Guide to `as_unsafe()` and future->coro Migration\n\nThe `as_unsafe()` method on `now_task`, `now_task_with_executor`, and `safe_task`\nis an escape hatch for interoperating with older futures-based code or other\nplaces not yet compatible with true structured concurrency patterns. `as_unsafe()`\nshould only be used temporarily during migrations, particularly when you need to\nintegrate futures-based automated testing with in-development coroutine implementations,\nbut should not be used in production code.\n\n**WARNING**: The `folly::coro::Task` is not safe to pass references to.\n`now_task.as_unsafe()` returns a `folly::coro::Task`. You must be careful with\nlifetime management when passing references to `as_unsafe()`.\n\n### Why `now_task` is safe by default\n\nWhen you use `now_task` normally (without `as_unsafe()`), the compiler forces\nyou to await it in the same full-expression that created it. This means C++\nreference lifetime extension protects you from bugs:\n\n```cpp\n// Safe: awaited immediately in the same expression\nco_await someNowTask(localRef);\n```\n\nOnce you call `as_unsafe()`, you exit this protected zone and must manually\nreason through reference lifetimes.\n\n### The stack-use-after-return or use-after-free: A Common and Pernicious Bug Pattern\n\nThis bug pattern often manifests as a heisenbug that only shows up in edge\ncases in production. It is **very hard to track down** if you don't run your\nservice under ASAN.\n\n**Bug recipe:**\n\n1. Create a task containing references with lifetime equal to scope X.\n2. Call `.semi()` or `.start()` (with `now_task`, these are gated behind\n   `as_unsafe()`). This often starts the task \"in the background\", on another\n   thread.\n3. Forget or fail to await the completion of the background task -- this often\n   happens due to an uncaught exception.\n4. Execution exits scope X, but your task is still running and is now touching\n   invalid memory.\n\n**Example of the bug:**\n\n```cpp\nnow_task<int> processData(int& x);\n\nfolly::SemiFuture<int> BAD_processDataParent() {\n  // BUG: `x` is a reference that will go out of scope!\n  int x = 123;\n  return processData(x).as_unsafe().semi();\n  // The scope exits before the future completes,\n  // `processData` will be reading invalid memory.\n}\n```\n\n### Recommendation\n\n**Do not use `as_unsafe()` in production code** unless you are in a very narrow\nscenario where it is feasible to manually prove the resulting movable task is\nused in a lifetime-safe way.\n\n### Safe Migration Patterns\n\nIn order to safely pass a reference to an unsafe `Task`, this condition must hold:\n\n**The underlying memory is guaranteed to outlive the coroutine execution.**\n\nThis condition can be achieved using `co_invoke` or `deferValue`. **`co_invoke`\nis strongly preferred** — use `deferValue` only as an absolute last resort when you\ncannot afford the extra memory cost of `co_invoke`.\n\nThe `co_invoke` coroutine frame is typically only a few dozen bytes larger. This difference is\nunlikely to matter for the vast majority of use cases. Only consider `deferValue` in extremely\nhigh-fanout scenarios where this memory overhead accumulates significantly.\n\n**WARNING**: `deferValue` introduces additional safety risks. With `deferValue`, the task has\nalready started — the work runs in the background on some other executor. This makes it\nvery easy to fail to await completion, due to an unhandled exception or other subtle\ncontrol-flow bug. Failing to await completion is dangerous because:\n\n1. **You lose success-or-error information.** Your background work might be\n   failing 100% of the time, but there will be no recourse — errors are silently\n   dropped.\n\n2. **You may violate implicit sequencing invariants.** Something that expects to\n   run \"after\" the completion may run concurrently instead. While this might be\n   fine if you packaged up all task state in `deferValue`, in practice code\n   often relies on global state. Your background work might still be running\n   after the global state no longer expects it to (is destroyed, invalid, or\n   must not be mutated).\n\nIn the rare case where you must make a `now_task` take reference arguments\nself-contained, use one of these patterns.\n\n#### Pattern 1: `co_invoke` (Strongly Preferred)\n\nUse `co_invoke` to make decay-copies of arguments into an additional coro frame (often on the heap),\nguaranteeing their lifetime for the duration of the coroutine:\n\n```cpp\nnow_task<int> processData(int& x);\n\nfolly::SemiFuture<int> processDataSafe(int& x) {\n  // co_invoke makes decay-copies of arguments into a new coroutine frame\n  return folly::coro::co_invoke(\n             [](auto&&... args) {\n               return processData(std::forward<decltype(args)>(args)...)\n                   .as_unsafe();\n             },\n             x)\n      .semi();\n}\n```\n\n#### Pattern 2: `deferValue` with Explicit Heap Allocation (Last Resort)\n\nIf `co_invoke` is not suitable, you can use `deferValue` to extend the\nlifetime of heap-allocated data:\n\n```cpp\nnow_task<int> processData(int& x);\n\nfolly::SemiFuture<int> processDataSafe(int& x) {\n  // Allocate on heap\n  auto dataPtr = std::make_unique<int>(x);\n\n  // Use deferValue to ensure dataPtr lives until the future completes\n  return processData(*dataPtr).as_unsafe().semi().deferValue(\n      [dataPtr = std::move(dataPtr)](int result) { return result; });\n}\n```\n\n**Note**: The `deferValue` callback captures `dataPtr`, ensuring it stays alive\nuntil the future is resolved.\n"
  },
  {
    "path": "folly/coro/safe/docs/Captures.md",
    "content": "## Simple guide to `bind::capture()`, `capture<T>` and friends\n\nIf you are just reading some code with `bind::capture()` arguments -- think of\nthese as zero-cost (compile-time) smart pointers, each owned by the\n`async_closure` taking the arg.\n\nFor example, `n` below is a `capture<int>`.  It is an **owned capture**, a\nwrapper around `int` whose lifetime is tightly bound to the closure.\n\n```cpp\nnamespace bind = folly::bind; // Can add to `.cpp` files and project namespaces\n\nassert(15 == co_await async_closure(\n    // NB: Can omit the outer `args` -- its sole arg is `bind::ext::like_args`\n    bind::args{bind::capture(5)},\n    [](auto n) -> closure_task<int> {\n      co_await async_closure(\n        bind::args{n},\n        [](auto nRef) -> closure_task<void> {\n          *nRef += 10;\n          co_return;\n        });\n      co_return *n;\n    }));\n```\n\nOn the other hand, `nRef` is `capture<int&>`.  It is a **capture reference**\nthat was implicitly made from `n`.  Per `LifetimeSafetyDesign.md`, there are\nvarious compile-time checks that make it harder to construct invalid capture\nreferences.\n\n### When & how to use `bind::capture()`\n\n 1. If type `T` requires async RAII (`co_cleanup`), you will need\n    `bind::capture_in_place<T>()`.  For a working example, see `BackgroundTask.h` or\n    `SafeAsyncScope.h`.\n\n 1. Suppose you passed a `co_cleanup` type `T` into an async closure (example:\n    `safeAsyncScope<>()`).  Then, the closure will internally own\n    `co_cleanup_capture<T>`, and the closure's coroutine will get a capture\n    reference `c`.\n\n    Now imagine your closure wants to pass a **reference** to a variable `v`\n    into the cleanup object, something like `c.someMethod(v)`.  Any correctly\n    implemented `co_cleanup` type should require that its inputs are valid\n    beyond the point where its cleanup is awaited.  For example, cleanup runs\n    after your closure's coroutine exits, so any references to your coro's\n    stack are unsafe.  The `capture` type system causes such safety bugs not to\n    compile.  See `LifetimeSafetyBenefits.md` for more.\n\n    `capture`s are our mechanism for making lifetime-safe references.  In order\n    to make `c.someMethod(v)` work, you will need to make `v` itself a capture,\n    by having your closure take `auto v`, and make it either:\n      - `bind::capture()` for an owned capture, **OR**\n      - `parentA` to make a capture reference from a parent's capture.\n\n### Accessing `capture<T>`s\n\nIf your function takes a capture, here is all you need to know:\n  - All `capture<T>` class templates act like pointers.  Use `->` and `*` to\n    access your `T`.\n  - Don't worry about the difference between the `capture` templates -- either\n    pass by `auto`, or use the type from the compiler error.\n  - Never move the capture wrapper, always move the `T` inside it.\n    In other words -- good: `*std::move(cap)`, bad: `std::move(*cap)`.\n  - Captures should be transparent to value-category modifiers. That is:\n      * `const capture<T>` acts like & converts to `capture<const T>`.\n      * `capture<T>&` converts to `capture<T&>`.\n      * `capture<T>&&` converts to `capture<T&&>`.\n  - If you pass `capture<Value> c` into an `async_closure`, it is always passed\n    by-reference.  That is, the child closure automatically gets\n    `capture<Value&>`, or `capture<Value&&>` from `std::move(c)`.\n  - `capture<T>` behaves much like `T`, besides the above caveats (must\n    dereference; pass-by-reference in async closures):\n      * For value type `V`, `capture<V>` represents ownership. The capture\n        wrapper belongs to whatever constructed it, and **should not be moved**\n        -- but, if `V` is movable, you can of course move the inner type:\n        ```cpp\n        V dst = *std::move(srcCap);\n        ```\n      * `capture<V&>` is copyable & movable.\n      * `capture<V&&>` is move-only, can *explicitly* convert to `capture<V&>`\n\n        *Caveat*: To reduce use-after-move errors, dereferencing requires rvalues.\n        That is, `*rcap` won't work -- you must `*std::move(rcap)`.\n  - *Power users:* It is a limitation of C++ that `operator->` loses value\n    category by returning a pointer.  In special cases where this is critical\n    for a good UX, it is technically possible to address that returning a\n    pointer to a specially crafted rvalue proxy for `rval_ptr` queries.  In\n    regular metaprogramming, you should use `(*cap).member` for deref.\n\n### `safe_alias` warning: the \"composition hole\" & lambda captures\n\nIt bears repeating the \"composition hole\" warning from `SafeAlias.h`.\n  - If a type stores any kind of reference (like `capture<Ref>`) or pointer, or\n    anything else that's not a straight-up value type, then it **must**\n    correctly specialize `safe_alias_of`.\n  - When your child closure gets a lambda (or another object) from a parent,\n    it is **particularly risky** to pass `capture<Ref>`s into its `operator()`\n    (or other member function).  If the parent stored any reference in that\n    object, (as easy as `[&](...) { ...  }`, then the child can incorrectly\n    plumb through its own short-lived references into the parent's scope.\n\n### Syntax sugar: `capture_indirect<SOME_PTR<T>>`\n\nTo access `capture<shared_ptr<int>> capSharedN`, you need to dereference twice:\n```cpp\n**capSharedN += 10;\n```\n\nWriting `bind::capture_indirect()` gives you `capture_indirect<shared_ptr<int>>`,\nwhich needs just one dereference, and can still access the `shared_ptr` via\n`get_underlying_unsafe()` -- but see its docblock for **RISKS**.\n\n**Watch out:** Be sure to null-check `capture_indirect` via its `operator bool`.\nThis is important, since, the underlying type is typically nullable!\n\n### Escape hatch: Capture-by-reference\n\nUse via `bind::capture_const_ref{}`, `bind::capture{bind::const_ref{}}`,\n`bind::capture_mut_ref{}`, etc in your closure's `bind::args{}` list.\n\nThis mechanism solves problems similar to `AfterCleanup.h`, but after-cleanup\nis strictly safer, so you should prefer it when applicable.\n\nCapture-by-ref is a way of turning a reference from a parent scope into a\n`capture<T&>` or `<T&&>` inside a child `async_now_closure`.  While the\n`now_task` restriction aids lifetime safety, the user must still be careful to\navoid giving the child the ability to store short-lived child refs in the\nparent's scope.  To fix a concrete instance of this problem, the\n`BindAsyncClosure.h` implementation blocks the capture-by-ref mechanism\nfrom passing `co_cleanup` refs & `captures`.\n\n#### Design notes for capture-by-reference\n\nThis section discusses potential relaxations of the capture-by-ref safety\nrules, which would make it more broadly applicable.  However, the relaxations\nwould come with both new footguns, and new complexity, so I'm currently\nthinking of them as \"rejected designs\" rather than future work.\n\n**Note 1:** It would be within the spirit of regular RAII to defer awaiting the\nclosure to a later point in the current scope.  That is, the `safe_task` taking\nthese `capture_ref()` args would be marked down to `<= lexical_scope_ref`\nsafety.  This could be a new safety level with:\n\n```cpp\nafter_cleanup_ref >= lexical_scope_ref >= shared_cleanup\n```\n\nThe valid lifetime for this body-only `safe_task` is clearly shorter than\n`after_cleanup_ref` -- it's invalid whenever the captured refs are destroyed,\nwhich (under typical RAII) is a bit longer than the lexical lifetime of the\ntask.  In the `lexical_scope_ref` scenario, the user can, of course, invalidate\nthe reference before awaiting the task, but it takes a bit of effort, and might\nbe covered if the [P1179R1 lifetime safety profile](https://wg21.link/P1179R1)\nis standardized.  For example:\n\n```cpp\nstd::optional<safe_task<safe_alias::lexical_scope_ref, void>> t;\n{\n  int i = 5;\n  t = async_closure([](auto i) -> closure_task<void> {\n    std::cout << *i << std::endl;\n    co_return;\n  }, capture_ref(i));\n  ++i;\n}\n// BAD: The reference to `i` is now invalid!\nco_await std::move(*t);\n```\n\n**Note 2:** The way that `async_closure(...  bind::capture_const_ref(...) ...)`\nbehaves, it seems like we could just universally allow creating\n`lexical_scope_capture<T&>` from `T&` -- even outside `async_closure`\ninvocations.  `Captures.h` would need to support auto-upgrade of\n`lexical_scope_capture` to `capture` or `after_cleanup_capture`, depending on\n`shared_cleanup` status.  This \"universal\" implementation would be more\ncomplex, but without `async_closure()`'s capture-upgrade semantics, there's not\na lot of value in obtaining a `lexical_scope_capture<Ref>` -- for example, you\ncan't use it to schedule work on a nested `safe_async_scope`.\n\n### Debugging lifetime safety compile errors\n\nIf you're working with captures, and get a compile error about `safe_task`,\n`safe_alias_of`, or similar, there is a good chance that you triggered a\nlifetime safety check. Read `LifetimeSafetyDebugging.md` for what to do next --\nit also covers the lifetime safety design of `Captures.h`.\n\n### Implementation gaps\n\n  - While `Captures.h` mentions `restricted_co_cleanup_capture`, the\n    implementation is not finished. See `FutureWork.md` for more details.\n  - `FutureLinters.md` describes several linters that help achieve maximum\n    lifetime safety when using captures.\n\n---\n---\n---\n\n## Notes for advanced users\n\n### Why does `capture` even exist?\n\n`async_closure` is a lexical scope with guaranteed async RAII. When implementing\nsuch a thing, you end up needing to store two kinds of values that live strictly\nlonger than the coroutine function scope itself. Specifically:\n  - Values with `co_cleanup` (details in `CoCleanupAsyncRAII.md`). The\n    archetypal type is `safe_async_scope`, which is immovable to allow an\n    efficient implementation -- so the storage mechanism also needs to support\n    in-place construction.\n  - Values that outlive the cleanup, so they can be safely referenced by the\n    `co_cleanup` types.\n  - Both kinds of values are passed by-reference into the inner coro.  And,\n    since `async_closure` tasks often need to be movable (e.g.  to run on\n    scopes), the value storage **must provide stable pointers**.\n  - In addition to the above two special types of data, we want to be able to\n    pass regular arguments into async closures, without ambiguity.\n\nAt its core, `capture<T>` addresses those \"must have\" needs.\n\nHowever, it also provides some important \"bonus\" features:\n  - As discussed in \"your own `co_cleanup` type\" below, types supporting\n    `co_cleanup` should not be usable outside of a \"managed\" context that always\n    awaits cleanup.\n\n    Without captures, passing a \"passkey\" type into the in-place constructor for\n    `co_cleanup` type could address this need (with some static assertions).\n    However, with captures, `capture_proxy` gives us a cleaner solution.\n  - `capture`s also help us enforce `safe_alias` lifetime safety heuristics\n    using the type system. Doing something equivalent with static analysis on\n    \"plain\" arguments would be prohibitive, e.g. because it would require\n    chasing references across compilation units. In contrast, `capture` types\n    automatically embed a lifetime safety contract.\n\n### `shared_cleanup` closures downgrade `capture` safety to `after_cleanup_`\n\n*tl;dr* If you see `after_cleanup_SOME_capture`, know that it quacks just like\n`SOME_capture`, but with a lower `safe_alias` level.  If this behavior is\nblocking you, you may benefit from finishing `restricted_co_cleanup_capture`.\n\n#### Problem solved\n\nWhen a child closure takes a reference to a parent's async scope, it can easily\ngive a short-lived reference to a longer-lived task on that scope:\n\n```cpp\nco_await async_closure(\n    safeAsyncScope<CancelViaParent>(),\n    [](auto scope) -> closure_task<void> {\n      co_await async_closure(\n          bind::args{scope, bind::capture(5)},\n          [](auto outerScope, auto n1) -> closure_task<void> {\n              outerScope->with(co_await co_current_executor).schedule(\n                  [](capture<int&> n2) -> co_cleanup_safe_task<void> {\n                    assert(*n2 == 5); // Invalid memory access!\n                    co_return;\n                  }(n1));\n          });\n    });\n```\n\nNote that the lifetime of `n1` aka `n2` is shorter than that of `scope`.  That\nis, by the time `*n2` happens, the closure owning `n1` may have been destroyed.\n\nFortunately, this code doesn't compile thanks to the `after_cleanup_` downgrade\ndescribed below:\n```\nno known conversion from 'after_cleanup_capture<int>' to 'capture<int &>'\n```\nChanging the inner lambda to `after_cleanup_capture` still won't compile:\n```\nBad safe_task: check for unsafe aliasing in arguments or return type\n```\nRelaxing the inner task to `safe_task<safe_alias::after_cleanup_ref, void>` also\nwon't let the bug through, since `schedule()` won't take a less-safe task.\n```\nconstraints not satisfied ... schedule( ...\nis_void_safe_task<\n    safe_task<safe_alias::after_cleanup_ref, void>,\n    safe_alias::co_cleanup_safe_ref>' evaluated to false\n```\n\nTo understand the solution, let's reformulate this bug more abstractly:\n\n  - Any closure taking `co_cleanup_capture<T&>` is vulnerable to the problem,\n    **unless** the API of `T` specifically ensures that it only takes inputs of\n    safety `maybe_value`.  In this section, we focus on `co_cleanup` types that\n    must be able to take references, like `safe_async_scope`.\n\n    NB: Types with value-only APIs should expose `capture_restricted_proxy()`.\n\n  - Actually, closures sometimes reference `co_cleanup` types in ways besides\n    `co_cleanup_capture<T&>` -- for example, `capture<AsyncObjectPtr<T>>`.  For\n    this reason, we define a brand-new level `safe_alias::shared_cleanup`,\n    which must be used for any type that may give a child closure access to the\n    parent's longer-lived lexical scope.\n\n    **Note:** The name `shared_cleanup` aims to evoke that a child taking such\n    an argument must take the parent's perspective on safety measurements.\n\n  - By definition, APIs of `co_cleanup` types must guard against inputs less\n    safe than `co_cleanup_safe_ref`, so the problem can only occur if a child\n    is able to obtain a plain `capture<>` (or equivalently `capture_heap<>` /\n    `capture_indirect<>).\n\n  - Plain `capture<>`s come about in two ways:\n      * Getting a `capture<>` reference from a parent. This scenario is fine --\n        **if** the parent could safely hold the `capture<>` together with the\n        `co_cleanup_capture<T&>` that creates the risk, then it's no less safe\n        for the child to handle that `capture` ref.\n      * Making an owned `capture`. By default, owned captures are plain, but\n        in the above example, `after_cleanup_capture<>` makes an appearance.\n        That is, in fact, the fix!\n\n        **Anytime a closure takes a `shared_cleanup` input, it loses the\n        ability to instantiate plain captures.** Its owned captures get the\n        `after_cleanup_` prefix (the \"downgrade\"), and it can no longer\n        \"upgrade\" `after_cleanup_capture` references that it gets from a parent\n        -- more on both below.\n\nNow that you saw the problem, and the solution, let's review the formalism.\n\n#### Upgrade/downgrade rules\n\nThe reference downgrade rules rely on the fact that `co_cleanup_capture` APIs\n(per \"your own `co_cleanup` type\" below) are required to check that the lifetime\nsafety of each input is `>= co_cleanup_safe_ref`.\n\nAn `async_closure` is considered `shared_cleanup` if any of its external\n(non-owned) arguments have `shared_cleanup` safety. Such closures deviate from\nnormal `capture`-passing rules in two ways:\n  - **Own `capture`s are downgraded:** In the example, you will note that `n1` is\n    of type `after_cleanup_capture<int>`. Whereas, in the absence of `outerScope`,\n    the type would be `capture<int>`.\n  - **Parent `capture`s are not upgraded:** Suppose the example scheduled a\n    closure: `.schedule(async_closure(bind::args{n1}, ...))`.  Then, **inside\n    that closure** `n1` would be visible as `capture<int&>` because it can't be\n    exfiltrated to `outerScope`.  That's the upgrade behavior[†].  But, when\n    passing `bind::args{outerScope, n1}`, the `shared_cleanup` argument blocks\n    the upgrade, and the closure would still see `after_cleanup_capture<int&>`.\n\n    > [†] Note that when a closure upgrades its refs, e.g. from\n    > `after_cleanup_capture` to `capture`, the safety of the closure's task is\n    > **not** affected. That is, reference upgrades are an internal matter.\n\n#### **Why is the downgraded name `after_cleanup_`?**\n\nIn short, because such `capture`s can safely be used in `co_return\nmove_after_cleanup()` and similar constructs.\n\nWe need a new `after_cleanup_ref` level because:\n  - `after_cleanup_capture<int&>` is safer than a `shared_cleanup` ref, which is\n    *not* allowed in `move_after_cleanup` et al.\n  - `after_cleanup_ref` is less safe than `co_cleanup_safe_ref`, since we don't\n    want the downgraded references to be scheduled on scopes.\n\n### Avoid safety downgrades via `restricted_co_cleanup_capture`\n\n**NB:** This feature isn't fully implemented yet (see \"Implementation gaps\"\nin this doc, and `FutureWork.md`), but what remains is quite simple, just\nsearch the code for \"restricted\".\n\nIn some scenarios -- e.g. passing around a fire-and-forget logger -- it is\nimportant to avoid the safety downgrade.  For example, a closure taking a\n`co_cleanup_capture<Logger&>` would be unable to pass any of its own captures to\na `co_cleanup_capture<safe_async_scope&>` that it owns.\n\nTo avoid downgrades, pass `restricted_co_cleanup_capture<Logger&>` to the child\nclosure.  This `capture` uses ADL customization point\n`capture_restricted_proxy()` to dereference, returning a proxy object that\n**only** accepts inputs with `maybe_value` safety. Obviously, such a proxy\ncannot accidentally pass short-lived refs from a child closure to a parent\n`co_cleanup_capture`, and thus it needs no downgrades.\n\n**IMPORTANT:** The implementation must pick between one of:\n  - \"once restricted, always restricted\"\n  - \"restricted->unrestricted also downgrades `capture`s from parents\"\n\nWhat we cannot do is \"let a closure take a formerly `restricted` ref as\nunrestricted **and** take non-downgraded refs from parents.\" If both were\npossible at once, then the following lifetime safety violation could occur:\n  - depth 0: creates a `co_cleanup_capture<S&> x0`\n  - depth 1: takes restricted `x1` and owns `capture<int> y1`\n  - depth 2: unrestricts `x1` as `x2`, takes ref to `y1` as `y2`\nThe problem is that short-lived `y2` can now be passed to `x0`. Either\nsolution above eliminates the safety gap.\n\n### The no-cleanup closure optimization & `_heap` captures\n\nAlthough `async_closure` was built to support async RAII, it should also see\nusage *just* because of `LifetimeSafetyBenefits.md`.\n\nClosures are implemented in such a way that users don't have to choose between\nsafety and performance. Specifically, an async closure that takes no\n`co_cleanup` captures should perform exactly the same as the bare inner\ncoroutine.\n\nThis zero-cost behavior is called the \"no-cleanup closure optimization\". When\nimplementing async RAII, it is hard (or perhaps impossible) to avoid allocating\na second coro frame, the one that awaits cleanup. But, when `async_closure` sees\nthat it owns no `co_cleanup_capture`s, it will:\n  - Omit the outer coro frame (which would now be no-op)\n  - Move in its own captures into the inner coro.\n  - For owned captures that are `bind::in_place`, automatically use the\n    `capture_heap` variation.\n\nFrom most perspectives, a no-cleanup closure quacks just like its\nouter-coro-awaits-inner-coro cousin. However, its own capture args' signatures\nwill differ:\n  - `capture<Value>` is passed instead of `capture<Value&>`.\n  - `bind::in_place` captures use the `capture_heap` template.\n\nBy design, reference and value, plain and `_heap` captures have identical\ninterfaces, letting `async_closure` freely pick the storage for those typical\ninner coros that take all captures by `auto`.\n\nIn the unlikely event of a no-cleanup closure taking lots of `bind::in_place`\ncaptures, you can try `async_closure::force_outer_coro` to coalesce allocations.\n\n### Integrating your own `co_cleanup` type\n\nThis section assumes you're familiar with `CoCleanupAsyncRAII.md`.\n\nA properly implemented `co_cleanup` type `T` should:\n\n  - Construct `T` in a state that does not yet require cleanup.\n\n  - Be immovable, e.g. derive from `private folly::NonCopyableNonMovable`.\n\n    This prevents lifetime issues, since all our safety checks assume that\n    the `T` is cleaned up by its original owner.\n\n  - Restrict public APIs that affect `T`'s need for cleanup to **only** be\n    accessible by dereferencing `co_cleanup_capture<T&>`. This guarantees that\n    if cleanup is needed, it will be called.\n\n    When `capture` types evaluate `operator*` and `operator->`, they look\n    for an ADL customization point. Declare it like so:\n    ```cpp\n    template <capture_proxy_kind Kind, const_or_not<YourType> T>\n    friend auto capture_proxy(capture_proxy_tag<Kind>, T&);\n    ```\n    This should return a proxy type implementing your public API appropriate\n    for both `Kind`, and the `const`-qualification of `T` -- `forward_like`\n    may be useful when accessing members of `T`.  The proxy type should be\n    `NonCopyableNonMovable` with a constructor restricted to your class.\n\n  - For public APIs, require `lenient_safe_alias_of_v` of at least\n    `co_cleanup_safe_ref` for any input that may be stored until cleanup time.\n\n  - If `restricted_co_cleanup_capture<T&>` support is desired, ADL-customize\n    `capture_restricted_proxy()` as above, and enforce that all API inputs have\n    `lenient_safe_alias_of_v` of `maybe_value`.\n\n### How many templates are in this type zoo? Can't you type-erase?\n\nLook at `is_any_capture`. There are 8 as of this writing. Only the 3\n`after_cleanup_` ones are specific to lifetime-safety tracking.\n\nWe deliberately chose distinct templates instead of template parameters, since\nthis should result in more readable compiler errors.\n\nType-erasure isn't a very practical idea for simplifying the type signatures.\nThere are two reasons:\n  - `folly/coro/safe` should be zero-cost so teams can adopt it confidently.\n  - We shouldn't type-erase the lifetime safety level, and at present 4\n    different ones need to be distinguished.\n\nShrinking the capture type zoo from 8 to 4 doesn't seem worth the runtime &\ncomplexity cost of type erasure.\n\n### Why do `capture`s quack like pointers?\n\nMorally, they are either values or references, and the \"pointer-like\" UX hurts\nergonomics.\n\nLet's consider the \"no wrapper\" alternative.  It would be possible for\n`bind::capture()` args to `async_closure` to just pass a reference to the\nunderlying type into the inner coro.  This comes with many downsides:\n  - Bug farm: The inner coro has to remember to write `auto&` / `ActualType&` --\n    **except** when you have the no-cleanup closure optimization. Pass-by-value\n    for non-cleanup captures **will** compile, making a copy, but the runtime\n    behavior is wrong. This is fragile enough that I wold abandon the no-cleanup\n    optimization in this scenario.\n  - Clarity: The inner coro signature doesn't distinguish between captures and\n    plain arguments, even though they have important lifetime differences (only\n    capture refs can be passed into async scope tasks, e.g.).\n  - Raw reference: no lifetime safety checks are possible.\n  - To prevent `co_cleanup` types from being used in unmanaged contexts, we'd\n    have to use the \"passkey constructor\" pattern, which is less flexible than\n    the `capture_proxy` design.\n\nWhile [C++20 lacks a good story for generic reference-like wrapper\ntypes](https://medium.com/@snowp/transparent-phantom-types-in-c-de6ac5bed1d1),\nper above, the uglier dereferenceable wrapper style provides benefits that far\noutweigh the syntactic boilerplate.\n"
  },
  {
    "path": "folly/coro/safe/test/AsyncClosureTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Timeout.h>\n#include <folly/coro/ValueOrFatal.h>\n#include <folly/coro/safe/AsyncClosure.h>\n#include <folly/fibers/Semaphore.h>\n#include <folly/init/Init.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro {\n\nusing namespace folly::bind::literals;\nusing namespace std::literals::chrono_literals;\n\nCO_TEST(AsyncClosure, invalid_co_cleanup) {\n  auto checkCleanup = []<typename T>(tag_t<T>) {\n    return async_closure(\n        bind::capture_in_place<T>(),\n        [](auto) -> closure_task<void> { co_return; });\n  };\n\n  struct ValidCleanup : NonCopyableNonMovable {\n    value_or_fatal<Task<>, on_stopped_void> co_cleanup(\n        async_closure_private_t) {\n      co_return;\n    }\n  };\n  co_await checkCleanup(tag<ValidCleanup>);\n\n  struct InvalidCleanupNonVoid : NonCopyableNonMovable {\n    [[maybe_unused]] value_or_fatal<Task<int>, on_stopped<0>> co_cleanup(\n        async_closure_private_t) {\n      co_return 1;\n    }\n  };\n#if 0 // Manual test -- this uses `static_assert` for better UX.\n  co_await checkCleanup(tag<InvalidCleanupNonVoid>);\n#endif\n\n  struct InvalidCleanupLacksNoexcept : NonCopyableNonMovable {\n    [[maybe_unused]] Task<void> co_cleanup(async_closure_private_t) {\n      co_return;\n    }\n  };\n#if 0 // Manual test -- this uses `static_assert` for better UX.\n  co_await checkCleanup(tag<InvalidCleanupLacksNoexcept>);\n#endif\n\n  struct InvalidCleanupIsMovable {\n    [[maybe_unused]] value_or_fatal<Task<>, on_stopped_void> co_cleanup(\n        async_closure_private_t) {\n      co_return;\n    }\n  };\n#if 0 // Manual test -- this failure escapes `is_detected_v`.\n  co_await checkCleanup(tag<InvalidCleanupIsMovable>);\n#endif\n}\n\nstatic_assert(std::is_same_v<\n              decltype(folly::coro::detail::cumsum_except_last<0, 2, 1, 3>),\n              const vtag_t<0, 2, 3>>);\n\nclosure_task<int> intTask(int x) {\n  co_return x;\n}\nstruct StatelessIntCallable {\n  closure_task<int> operator()(int x) { co_return x; }\n};\nstruct StatelessGenericCallable {\n  closure_task<int> operator()(auto x) { co_return x; }\n};\n\n// We can't directly test `async_closure*` for unsafe inputs, since that\n// would trigger `static_assert`s in `release_outer_coro()`.  Instead, test\n// `is_safe()` which verifies the same conditions.\ntemplate <bool ForceOuter>\nvoid checkSafety() {\n  constexpr int x = 42;\n\n  auto safeWrap = [](auto fn, auto&& bargs) {\n    return folly::coro::detail::\n        async_closure_impl<ForceOuter, /*EmitNowTask*/ false>(\n            std::move(bargs), std::move(fn));\n  };\n\n  // Check safe usage, with various levels of arg safety.\n  // Covers: fn ptrs, plain & generic lambdas, callable & generic callables.\n  safe_alias_constant<safe_alias::maybe_value> kValue;\n  auto checkIsSafe = [&](auto arg_safety, auto fn, auto bargs) {\n    auto s = safeWrap(std::move(fn), std::move(bargs));\n    static_assert(s.is_safe());\n    static_assert(\n        folly::coro::detail::safe_task_traits<\n            decltype(std::move(s).release_outer_coro())>::arg_safety ==\n        arg_safety.value);\n  };\n\n  checkIsSafe(kValue, intTask, bind::args{5});\n  checkIsSafe(kValue, StatelessIntCallable{}, bind::args{5});\n  checkIsSafe(kValue, StatelessGenericCallable{}, bind::args{5});\n  checkIsSafe(kValue, []() -> closure_task<int> { co_return 5; }, bind::args{});\n  checkIsSafe(kValue, []() -> closure_task<void> { co_return; }, bind::args{});\n  checkIsSafe(\n      kValue, [](int x) -> closure_task<int> { co_return x; }, bind::args{5});\n  checkIsSafe(\n      kValue, [](auto) -> closure_task<void> { co_return; }, bind::args{5});\n  checkIsSafe(\n      safe_alias_constant<safe_alias::co_cleanup_safe_ref>{},\n      [](auto) -> closure_task<void> { co_return; },\n      bind::args{manual_safe_ref<safe_alias::co_cleanup_safe_ref>(x)});\n  checkIsSafe(\n      safe_alias_constant<safe_alias::after_cleanup_ref>{},\n      [](auto) -> closure_task<void> { co_return; },\n      bind::args{manual_safe_ref<safe_alias::after_cleanup_ref>(x)});\n\n  auto checkIsUnsafe = [&](auto fn, auto bargs) {\n    auto s = safeWrap(std::move(fn), std::move(bargs));\n    static_assert(!s.is_safe());\n  };\n  // Only `safe_task` is allowed as the inner coro.\n  checkIsUnsafe([]() -> Task<int> { co_return 5; }, bind::args{});\n  checkIsUnsafe([]() -> Task<void> { co_return; }, bind::args{});\n  checkIsUnsafe([](int x) -> Task<int> { co_return x; }, bind::args{5});\n  checkIsUnsafe([](auto) -> Task<void> { co_return; }, bind::args{5});\n  // Don't allow passing in `unsafe*` args externally.\n  checkIsUnsafe(\n      [](auto) -> closure_task<void> { co_return; },\n      bind::args{manual_safe_ref<safe_alias::unsafe_closure_internal>(x)});\n}\n\nTEST(AsyncClosure, safetyNoOuter) {\n  checkSafety</*force outer*/ false>();\n}\nTEST(AsyncClosure, safety) {\n  checkSafety</*force outer*/ true>();\n}\n\ninline constexpr async_closure_config ForceOuter{.force_outer_coro = true};\ninline constexpr async_closure_config NoForceOuter{.force_outer_coro = false};\n\n// Checks that `async_closure` returns the `safe_task` we expect.\ntemplate <typename ExpectedT, async_closure_config Cfg = NoForceOuter>\nconstexpr auto asyncClosureCheckType(auto fn, auto bargs) {\n  auto t = async_closure<Cfg>(\n      // Actually, safe because `bargs` is by-value\n      bind::ext::unsafe_move_args::from(std::move(bargs)),\n      std::move(fn));\n  static_assert(std::is_same_v<decltype(t), ExpectedT>);\n  return std::move(t);\n}\n\ntemplate <async_closure_config Cfg>\nTask<void> checkNoArgs() {\n  auto res = co_await asyncClosureCheckType<value_task<int>, Cfg>(\n      []() -> closure_task<int> { co_return 7; }, bind::args{});\n  EXPECT_EQ(7, res);\n}\n\nCO_TEST(AsyncClosure, noArgsNoOuter) {\n  co_await checkNoArgs<NoForceOuter>();\n}\nCO_TEST(AsyncClosure, noArgs) {\n  co_await checkNoArgs<ForceOuter>();\n}\n\nnamespace {\nstatic bool ran_returnsVoid;\n}\n\ntemplate <async_closure_config Cfg>\nTask<void> checkReturnsVoid() {\n  ran_returnsVoid = false;\n  co_await asyncClosureCheckType<value_task<void>, Cfg>(\n      []() -> closure_task<void> {\n        ran_returnsVoid = true;\n        co_return;\n      },\n      bind::args{});\n  EXPECT_TRUE(ran_returnsVoid);\n}\n\nCO_TEST(AsyncClosure, returnsVoidNoOuter) {\n  co_await checkReturnsVoid<NoForceOuter>();\n}\nCO_TEST(AsyncClosure, returnsVoid) {\n  co_await checkReturnsVoid<ForceOuter>();\n}\n\ntemplate <async_closure_config Cfg>\nTask<void> checkPlainArgs() {\n  int thirtySix = 36; // test passing l-values\n  auto res = co_await asyncClosureCheckType<value_task<int>, Cfg>(\n      [](int x, auto yPtr, const auto z) -> closure_task<int> {\n        ++x;\n        int r = x + *yPtr + z;\n        yPtr.reset();\n        // Plain args have plain types\n        static_assert(std::is_same_v<std::unique_ptr<int>, decltype(yPtr)>);\n        co_return r;\n      },\n      bind::args{thirtySix, std::make_unique<int>(1200), 100});\n  EXPECT_EQ(1337, res);\n}\n\nCO_TEST(AsyncClosure, plainArgsNoOuter) {\n  co_await checkPlainArgs<NoForceOuter>();\n}\nCO_TEST(AsyncClosure, plainArgsOuter) {\n  co_await checkPlainArgs<ForceOuter>();\n}\n\nclosure_task<std::string> funcTemplate(auto hi) {\n  *hi += \"de-and-seek\";\n  co_return std::move(*hi);\n}\n\nCO_TEST(AsyncClosure, callFuncTemplate) {\n  auto res = co_await asyncClosureCheckType<value_task<std::string>>(\n      // As of 2024, C++ lacks an \"overload set\" type, and thus can't\n      // directly deduce `funcTemplate` (see P3360R0 pr P3312R0).\n      FOLLY_INVOKE_QUAL(funcTemplate),\n      bind::args{bind::capture_in_place<std::string>(\"hi\")});\n  EXPECT_EQ(\"hide-and-seek\", res);\n}\n\n// With `bind::capture()`, immovable objects get auto-promoted to\n// `capture_heap<>` iff the closure's outer coro is elided.\nstruct ImmovableString : private NonCopyableNonMovable {\n  explicit ImmovableString(std::string s) : s_(std::move(s)) {}\n  std::string s_;\n};\n\n// When needed, closure callbacks can have explicit & readable type signatures.\n// Unfortunately, the signature depends on whether the closure has an outer\n// coro wrapping the inner one.\nclosure_task<std::string> funcNoOuter(capture_heap<ImmovableString> hi) {\n  hi->s_ += \"de-and-seek\";\n  co_return std::move(hi->s_);\n}\nclosure_task<std::string> funcWithOuter(capture<ImmovableString&> hi) {\n  hi->s_ += \"de-and-seek\";\n  co_return std::move(hi->s_);\n}\n\nCO_TEST(AsyncClosure, callFunctionNoOuter) {\n  auto res = co_await asyncClosureCheckType<value_task<std::string>>(\n      funcNoOuter, bind::args{bind::capture_in_place<ImmovableString>(\"hi\")});\n  EXPECT_EQ(\"hide-and-seek\", res);\n}\n\nCO_TEST(AsyncClosure, callFunctionWithOuter) {\n  auto res =\n      co_await asyncClosureCheckType<value_task<std::string>, ForceOuter>(\n          funcWithOuter,\n          bind::args{bind::capture_in_place<ImmovableString>(\"hi\")});\n  EXPECT_EQ(\"hide-and-seek\", res);\n}\n\nstruct TakesBackref {\n  capture<std::string&> prefix_;\n  std::string suffix_;\n};\n\nCO_TEST(AsyncClosure, captureBackref) {\n  auto concat_prefix_suffix =\n      [](auto, auto /*hello*/, auto world) -> closure_task<std::string> {\n    co_return *world->prefix_ + world->suffix_;\n  };\n\n  auto r1 = co_await asyncClosureCheckType<value_task<std::string>, ForceOuter>(\n      concat_prefix_suffix,\n      bind::args{\n          \"s1\"_id = bind::capture(std::string{\"goodbye\"}),\n          \"s2\"_id = bind::capture(std::string{\"hello\"}),\n          bind::capture_in_place<TakesBackref>(\"s2\"_id, \" world!\")});\n  EXPECT_EQ(\"hello world!\", r1);\n\n  auto r2 = co_await asyncClosureCheckType<value_task<std::string>, ForceOuter>(\n      concat_prefix_suffix,\n      bind::args{\n          \"s1\"_id = bind::capture(std::string{\"goodbye\"}),\n          \"s2\"_id = bind::capture(std::string{\"hello\"}),\n          bind::capture_in_place<TakesBackref>(\"s1\"_id, \" world!\")});\n  EXPECT_EQ(\"goodbye world!\", r2);\n\n#if 0 // manual test for \"backrefs must point only to the left\" assert\n  (void)asyncClosureCheckType<value_task<std::string>, ForceOuter>(\n      concat_prefix_suffix,\n      bind::args{\n          \"s1\"_id = bind::capture(std::string{\"goodbye\"}),\n          bind::capture_in_place<TakesBackref>(\"s2\"_id, \" world!\"),\n          \"s2\"_id = bind::capture(std::string{\"hello\"})});\n#endif\n\n#if 0 // manual test for \"ambiguous backref\" scenario\n  // Future: Make this error message clearer than the current:\n  //   error: no matching function for call to 'async_closure_backref_get'\n  (void)asyncClosureCheckType<value_task<std::string>, ForceOuter>(\n      concat_prefix_suffix,\n      bind::args{\n          \"s\"_id = bind::capture(std::string{\"goodbye\"}),\n          \"s\"_id = bind::capture(std::string{\"hello\"}),\n          bind::capture_in_place<TakesBackref>(\"s\"_id, \" world!\")});\n#endif\n\n#if 0 // manual test for \"backref not found\" scenario\n  // Future: Make this error message clearer than the current:\n  //   error: no matching function for call to 'async_closure_backref_get'\n  (void)asyncClosureCheckType<value_task<std::string>, ForceOuter>(\n      concat_prefix_suffix,\n      bind::args{\n          \"x1\"_id = bind::capture(std::string{\"goodbye\"}),\n          \"x2\"_id = bind::capture(std::string{\"hello\"}),\n          bind::capture_in_place<TakesBackref>(\"s\"_id, \" world!\")});\n#endif\n}\n\nCO_TEST(AsyncClosure, simpleCancellation) {\n  EXPECT_THROW(\n      co_await timeout(\n          async_closure(\n              bind::args{},\n              []() -> closure_task<void> {\n                folly::fibers::Semaphore stuck{0}; // a cancellable baton\n                co_await stuck.co_wait();\n              }),\n          200ms),\n      folly::FutureTimeout);\n}\n\nstruct InPlaceOnly : folly::NonCopyableNonMovable {\n  explicit InPlaceOnly(bool* made, int n) : n_(n) {\n    if (made) {\n      *made = true;\n    }\n  }\n  int n_;\n};\n\nvoid assertArgConst(auto& arg) {\n  static_assert(std::is_const_v<std::remove_reference_t<decltype(*arg)>>);\n  static_assert(\n      std::is_const_v<std::remove_pointer_t<decltype(arg.operator->())>>);\n}\n\ntemplate <async_closure_config Cfg>\nTask<void> checkInPlaceArgs() {\n  bool made = false;\n  auto res = co_await asyncClosureCheckType<value_task<int>, Cfg>(\n      [](int a, auto b, auto c, auto d) -> closure_task<int> {\n        static_assert(\n            std::is_same_v<\n                decltype(b),\n                std::conditional_t<\n                    Cfg.force_outer_coro,\n                    capture<int&>,\n                    capture<int>>>);\n        *b += 100;\n        static_assert(\n            std::is_same_v<\n                decltype(c),\n                std::conditional_t<\n                    Cfg.force_outer_coro,\n                    capture<const InPlaceOnly&>,\n                    capture_heap<const InPlaceOnly>>>);\n        assertArgConst(c); // `const` underlying type\n        assertArgConst(d); // marked `constant`\n        co_return a + *b + c->n_ + *d;\n      },\n      bind::args{\n          30, // a\n          // Test both const and non-const `AsyncOuterClosurePtr`s.\n          // Check that \"x\"_id tagging for capture backrefs is transparent.\n          \"b\"_id = bind::capture(1000),\n          \"c\"_id = bind::capture_in_place<const InPlaceOnly>(&made, 7),\n          bind::capture(bind::constant(200))}); // d\n  EXPECT_EQ(1337, res);\n  EXPECT_TRUE(made);\n}\n\nCO_TEST(AsyncClosure, inPlaceArgsNoOuter) {\n  co_await checkInPlaceArgs<NoForceOuter>();\n}\nCO_TEST(AsyncClosure, inPlaceArgs) {\n  co_await checkInPlaceArgs<ForceOuter>();\n}\n\n// Tests that, with an outer coro, the user can specify `const auto`\n// args on the inner task, and they work as expected.\n//\n// IIUC this can't work generically for the \"no outer coro\" scenario, since\n// args need to be copied or moved into the inner coro, and non-copyable,\n// `const` classes are not movable.  In `checkInPlaceArgs()`, you can see\n// the workaround of passing a `const` (or equivalenly `constant()`) arg.\nCO_TEST(AsyncClosureTest, constAutoArgWithOuterCoro) {\n  bool made = false;\n  auto res = co_await asyncClosureCheckType<value_task<int>, ForceOuter>(\n      [](const auto a) -> closure_task<int> {\n        static_assert(\n            std::is_same_v<decltype(a), const capture<const InPlaceOnly&>>);\n        assertArgConst(a);\n        co_return a->n_;\n      },\n      bind::args{bind::capture(\n          bind::in_place<\n// Manual test: When set to 0, this should fail to compile because the `const\n// auto` above requires (via `FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE`) the\n// inner type to be `const`.\n#if 1\n              const\n#endif\n              InPlaceOnly>(&made, 7))});\n  EXPECT_EQ(7, res);\n  EXPECT_TRUE(made);\n}\n\n// A simple test pair showing the \"move-in\" vs \"by-ref\" behavior of the \"no\n// outer coro\" optimization. The `nestedRefs*` tests elaborate on this.\nCO_TEST(AsyncClosure, noOuterCoroGetsCaptureValue) {\n  co_await async_closure(bind::capture(1337), [](auto n) -> closure_task<void> {\n    static_assert(std::is_same_v<decltype(n), capture<int>>);\n    co_return;\n  });\n}\nCO_TEST(AsyncClosure, outerCoroGetsCaptureRef) {\n  co_await async_closure<ForceOuter>(\n      bind::capture(1337), [](auto n) -> closure_task<void> {\n        static_assert(std::is_same_v<decltype(n), capture<int&>>);\n        co_return;\n      });\n}\n\nCO_TEST(AsyncClosure, nestedRefsWithOuterCoro) {\n  auto res = co_await asyncClosureCheckType<value_task<int>, ForceOuter>(\n      [](auto x, const auto y, const auto z) -> closure_task<int> {\n        static_assert(std::is_same_v<decltype(x), capture<int&>>);\n        static_assert(\n            std::is_same_v<\n                decltype(y),\n                const capture<const std::unique_ptr<int>&>>);\n        assertArgConst(y);\n        static_assert(\n            std::is_same_v<\n                decltype(z),\n                const capture_indirect<const std::unique_ptr<int>&>>);\n        *x += 100;\n        co_await asyncClosureCheckType<co_cleanup_safe_task<void>>(\n            [](auto x2, auto y2, auto z2) -> closure_task<void> {\n              static_assert(std::is_same_v<decltype(x2), capture<int&>>);\n              static_assert(\n                  std::is_same_v<\n                      decltype(y2),\n                      capture<const std::unique_ptr<int>&>>);\n              assertArgConst(y2);\n              static_assert(\n                  std::is_same_v<\n                      decltype(z2),\n                      capture_indirect<const std::unique_ptr<int>&>>);\n              *x2 += 100; // ref remains non-const -- C++ arg semantics\n              co_return;\n            },\n            bind::args{x, y, z});\n        // Can also pass `capture<Ref>`s into a bare safe_task.\n        co_await [](auto x3, auto y3, auto z3) -> co_cleanup_safe_task<void> {\n          static_assert(std::is_same_v<decltype(x3), capture<int&>>);\n          static_assert(\n              std::is_same_v<\n                  decltype(y3),\n                  capture<const std::unique_ptr<int>&>>);\n          assertArgConst(y3);\n          static_assert(\n              std::is_same_v<\n                  decltype(z3),\n                  capture_indirect<const std::unique_ptr<int>&>>);\n          *x3 += 100; // ref remains non-const -- C++ arg semantics\n          co_return;\n        }(x, y, z);\n        co_return *x + **y + *z;\n      },\n      bind::args{\n          bind::capture(\n              bind::in_place<int>(1000),\n              bind::constant(std::make_unique<int>(23))),\n          bind::capture_indirect(bind::constant(std::make_unique<int>(14)))});\n  EXPECT_EQ(1337, res);\n}\n\n// Like `ImmovableString`, this helps us detect when the outer coro was elided\nstruct ImmovableInt : private NonCopyableNonMovable {\n  explicit ImmovableInt(int n) : n_(std::move(n)) {}\n  int n_;\n};\n\n// We want this to be as similar as possible to `nestedRefsWithOuterCoro` --\n// after all, \"no outer coro\" is supposed to be a \"mostly transparent\"\n// optimization. Therefore, the main differences are:\n//   - Split `x` into `w` and `x` to cover both heap and non-heap behaviors.\n//   - `capture`s move into the inner coro, and therefore cannot:\n//     * Write `const auto y` or `const auto z`, which would need a copy ctor\n//     * Use `constant()` around `std::make_unique()` (prevents move).\n//   - Correspondingly, we have to drop the `const`ness asserts.\n//   - To pass `capture<Val>` into a bare `safe_task`, we now have to\n//     explicitly declare the its argument types, to use the implicit\n//     conversion from `capture<Val>` to `capture<Val&>`.\nCO_TEST(AsyncClosure, nestedRefsWithoutOuterCoro) {\n  auto res = co_await asyncClosureCheckType<value_task<int>, NoForceOuter>(\n      [](auto w, auto x, auto y, auto z) -> closure_task<int> {\n        // Only the immovable type gets promoted to `capture_heap`.\n        static_assert(std::is_same_v<decltype(w), capture<int>>);\n        static_assert(std::is_same_v<decltype(x), capture_heap<ImmovableInt>>);\n        static_assert(\n            std::is_same_v<\n                decltype(z),\n                capture_indirect<std::unique_ptr<const int>>>);\n        x->n_ += 100;\n        co_await asyncClosureCheckType<co_cleanup_safe_task<void>>(\n            [](auto w2, auto y2, auto z2) -> closure_task<void> {\n              static_assert(std::is_same_v<decltype(w2), capture<int&>>);\n              static_assert(\n                  std::is_same_v<decltype(y2), capture<std::unique_ptr<int>&>>);\n              static_assert(\n                  std::is_same_v<\n                      decltype(z2),\n                      capture_indirect<std::unique_ptr<const int>&>>);\n              *w2 += 100; // ref remains non-const -- C++ arg semantics\n              co_return;\n            },\n            bind::args{w, y, z});\n        // Can pass implicitly converted `capture<Ref>`s into a safe_task\n        co_await\n            [](capture<ImmovableInt&> x3,\n               capture<std::unique_ptr<int>&> y3,\n               capture_indirect<std::unique_ptr<const int>&>)\n                -> co_cleanup_safe_task<void> {\n              x3->n_ += 50;\n              *(*y3) += 50;\n              co_return;\n            }(x, y, z);\n        co_return *w + x->n_ + **y + *z;\n      },\n      bind::args{\n          bind::capture(\n              bind::in_place<int>(700),\n              bind::in_place<ImmovableInt>(300),\n              std::make_unique<int>(23)),\n          // Can't use `constant()` here because we can't move a `const\n          // unique_ptr`.\n          bind::capture_indirect(std::make_unique<const int>(14))});\n  EXPECT_EQ(1337, res);\n}\n\nstruct ErrorObliviousHasCleanup : NonCopyableNonMovable {\n  explicit ErrorObliviousHasCleanup(int* p) : cleanBits_(p) {}\n  int* cleanBits_;\n  value_or_fatal<Task<>, on_stopped_void> co_cleanup(async_closure_private_t) {\n    *cleanBits_ += 3;\n    co_return;\n  }\n};\n\nCO_TEST(AsyncClosure, errorObliviousCleanup) {\n  int cleanBits = 0;\n  co_await async_closure(\n      bind::capture_in_place<ErrorObliviousHasCleanup>(&cleanBits),\n      [](auto) -> closure_task<void> { co_return; });\n  EXPECT_EQ(3, cleanBits);\n}\n\nstruct HasCleanup : NonCopyableNonMovable {\n  explicit HasCleanup(auto* p) : optCleanupErrPtr_(p) {}\n  std::optional<exception_wrapper>* optCleanupErrPtr_;\n  // If the closure (not other cleanups!) exited with an exception, each\n  // `co_cleanup` gets to see it.\n  value_or_fatal<Task<>, on_stopped_void> co_cleanup(\n      async_closure_private_t, const exception_wrapper* ew) {\n    *optCleanupErrPtr_ = *ew;\n    co_return;\n  }\n};\n\nCO_TEST(AsyncClosure, cleanupAfterSuccess) {\n  std::optional<exception_wrapper> optCleanErr;\n  co_await async_closure(\n      bind::capture_in_place<HasCleanup>(&optCleanErr),\n      [](auto) -> closure_task<void> { co_return; });\n  EXPECT_FALSE(optCleanErr->has_exception_ptr());\n}\n\nCO_TEST(AsyncClosure, cleanupAfterError) {\n  struct MagicError : std::exception {\n    explicit MagicError(int m) : magic_(m) {}\n    int magic_;\n  };\n\n  std::optional<exception_wrapper> optCleanErr;\n  auto res = co_await co_awaitTry(async_closure(\n      bind::capture_in_place<HasCleanup>(&optCleanErr),\n      [](auto) -> closure_task<void> {\n        co_yield folly::coro::co_error{MagicError{111}};\n      }));\n  EXPECT_EQ(111, optCleanErr->get_exception<MagicError>()->magic_);\n  EXPECT_EQ(111, res.tryGetExceptionObject<MagicError>()->magic_);\n}\n\nstruct CustomDerefCleanupProxy : NonCopyableNonMovable {\n  explicit CustomDerefCleanupProxy(int y) : y_(y) {}\n  auto operator->() { return static_cast<CustomDerefCleanupProxy*>(this); }\n  int y_;\n};\n\nstruct CustomDerefCleanup : HasCleanup {\n  explicit CustomDerefCleanup(auto* p) : HasCleanup(p) {}\n  using KindT = folly::coro::ext::capture_proxy_kind;\n  template <KindT Kind, folly::coro::ext::const_or_not<CustomDerefCleanup> T>\n  friend auto capture_proxy(folly::coro::ext::capture_proxy_tag<Kind>, T&) {\n    if constexpr (Kind == KindT::lval_ref) {\n      return CustomDerefCleanupProxy{101 + 1000 * std::is_const_v<T>};\n    } else if constexpr (Kind == KindT::lval_ptr) {\n      return CustomDerefCleanupProxy{202 + 1000 * std::is_const_v<T>};\n    } else if constexpr (Kind == KindT::rval_ref) {\n      return CustomDerefCleanupProxy{303 + 1000 * std::is_const_v<T>};\n    } else if constexpr (Kind == KindT::rval_ptr) {\n      return CustomDerefCleanupProxy{404 + 1000 * std::is_const_v<T>};\n    } else {\n      static_assert(false);\n    }\n  }\n};\n\ntemplate <typename CleanupT>\nTask<void> check_pass_cleanup_arg_to_subclosure(auto validate_ref) {\n  std::optional<exception_wrapper> optCleanErr;\n  co_await async_closure(\n      bind::args{bind::capture_in_place<CleanupT>(&optCleanErr), validate_ref},\n      [](auto c, auto validate_ref2) -> closure_task<void> {\n        validate_ref2(c);\n        static_assert(\n            std::is_same_v<decltype(c), co_cleanup_capture<CleanupT&>>);\n        co_await async_closure(\n            bind::args{c, validate_ref2},\n            [](auto c2, auto validate_ref3) -> closure_task<void> {\n              validate_ref3(c2);\n              static_assert(\n                  std::is_same_v<decltype(c2), co_cleanup_capture<CleanupT&>>);\n              co_return;\n            });\n      });\n  EXPECT_FALSE(optCleanErr->has_exception_ptr());\n}\n\nCO_TEST(AsyncClosure, passCleanupArgToSubclosure) {\n  co_await check_pass_cleanup_arg_to_subclosure<HasCleanup>([](auto&) {});\n}\n// Check that the \"custom dereferencing\" code doesn't break the automatic\n// passing of `capture` refs to child closures.\nCO_TEST(AsyncClosure, passCustomDerefCleanupArgToSubclosure) {\n  co_await check_pass_cleanup_arg_to_subclosure<CustomDerefCleanup>(\n      [](auto& c) {\n        EXPECT_EQ(101, (*c).y_);\n        EXPECT_EQ(202, c->y_);\n        EXPECT_EQ(404, std::move(c)->y_);\n\n        EXPECT_EQ(1101, (*std::as_const(c)).y_);\n        EXPECT_EQ(1202, std::as_const(c)->y_);\n        EXPECT_EQ(1404, std::move(std::as_const(c))->y_);\n      });\n}\n\nTEST(AsyncClosure, nonSafeTaskIsNotAwaited) {\n  bool awaited = false;\n  auto lambda = [&]() -> Task<void> {\n    awaited = true;\n    co_return;\n  };\n  // We can't `release_outer_coro()` on either since they have a\n  // `static_assert` -- but `checkIsUnsafe` above checks the logic.\n  folly::coro::detail::async_closure_impl<\n      /*ForceOuter*/ false,\n      /*EmitNowTask*/ false>(bind::args{}, lambda);\n  folly::coro::detail::async_closure_impl<\n      /*ForceOuter*/ true,\n      /*EmitNowTask*/ false>(bind::args{}, lambda);\n  EXPECT_FALSE(awaited);\n}\n\nstruct HasMemberTask {\n  int z = 1300; // Goal: ASAN failures if the class is destroyed\n  member_task<int> task(auto x, auto y) { co_return x + *y + z; }\n  // An explicit safety annotation is required to use `member_task`\n  template <safe_alias>\n  using folly_private_safe_alias_t =\n      safe_alias_constant<safe_alias::maybe_value>;\n};\n\nCO_TEST(AsyncClosure, memberTask) {\n  // First, examples of a \"bound\" member closure that actually owns the object:\n  EXPECT_EQ(\n      1337,\n      co_await async_closure<ForceOuter>(\n          bind::args{bind::capture(HasMemberTask{}), 30, bind::capture(7)},\n          FOLLY_INVOKE_MEMBER(task)));\n  EXPECT_EQ(\n      1337, // Syntax sugar: implicit `bind::capture` for member's object\n            // parameter\n      co_await async_closure<ForceOuter>(\n          bind::args{HasMemberTask{}, 30, bind::capture(7)},\n          FOLLY_INVOKE_MEMBER(task)));\n  EXPECT_EQ(\n      1337, // Same, but showing that `bind::in_place` still works\n      co_await async_closure<ForceOuter>(\n          bind::args{bind::in_place<HasMemberTask>(), 30, bind::capture(7)},\n          FOLLY_INVOKE_MEMBER(task)));\n  HasMemberTask hmt;\n  EXPECT_EQ(\n      1337, // Wouldn't compile without either `std::move` or `folly::copy`.\n      co_await async_closure<ForceOuter>(\n          bind::args{std::move(hmt), 30, bind::capture(7)},\n          FOLLY_INVOKE_MEMBER(task)));\n\n  // Second, call a member coro on an existing `capture<HasMemberTask>`.\n  EXPECT_EQ(\n      1337,\n      co_await async_closure<ForceOuter>(\n          bind::capture(HasMemberTask{}), [](auto mt) -> closure_task<int> {\n            co_return co_await async_closure(\n                bind::args{mt, 30, bind::capture(7)},\n                FOLLY_INVOKE_MEMBER(task));\n          }));\n}\n\n// Check that `async_now_closure` returns `now_task<int>` & return the task.\nnow_task<int> intAsyncNowClosure(auto&& bargs, auto&& fn) {\n  return async_now_closure(\n      bind::ext::unsafe_move_args::from(std::move(bargs)), std::move(fn));\n}\n\ntemplate <typename T>\nnow_task<void> check_now_closure_no_outer_coro_unsafe_task() {\n  int b1 = 300, c = 30, d = 7;\n  // The coro take raw references & use lambda captures\n  int res1 = co_await intAsyncNowClosure(\n      bind::args{bind::capture(1000), b1}, [&c, d](auto a, int& b2) -> T {\n        // No ref upgrade, since `T` is unsafe (`Task` or `now_task`)\n        static_assert(std::is_same_v<after_cleanup_capture<int>, decltype(a)>);\n        co_return *a + b2 + c + d;\n      });\n  EXPECT_EQ(1337, res1);\n\n  // Same \"no ref upgrade\" test, but with a concrete arg type.\n  int res2 = co_await intAsyncNowClosure(\n      bind::capture(42),\n      [](after_cleanup_capture<int> a) -> T { co_return *a; });\n  EXPECT_EQ(42, res2);\n\n  // Same \"no ref upgrade\" test, but with a parent ref forcing shared-cleanup\n  std::optional<exception_wrapper> optCleanErr;\n  int res3 = co_await async_now_closure(\n      bind::capture_in_place<HasCleanup>(&optCleanErr),\n      [](auto cleanup) -> closure_task<int> {\n        co_return co_await intAsyncNowClosure(\n            bind::args{cleanup, bind::capture(5)},\n            [](auto, auto a) -> closure_task<int> {\n              // No ref upgrade despite safe task, due to `cleanup` arg.\n              static_assert(\n                  std::is_same_v<after_cleanup_capture<int>, decltype(a)>);\n              co_return *a;\n            });\n      });\n  EXPECT_EQ(5, res3);\n}\n\n// The plumbing for an outer-coro closure is different, so test it too.\ntemplate <typename T>\nnow_task<void> check_now_closure_with_outer_coro() {\n  int cleanBits = 128;\n  int res = co_await intAsyncNowClosure(\n      bind::capture_in_place<ErrorObliviousHasCleanup>(&cleanBits),\n      [](auto c) -> T { co_return *c->cleanBits_; });\n  EXPECT_EQ(128, res);\n}\n\nCO_TEST(AsyncClosure, nowClosure) {\n  co_await check_now_closure_no_outer_coro_unsafe_task<Task<int>>();\n  co_await check_now_closure_no_outer_coro_unsafe_task<now_task<int>>();\n\n  co_await check_now_closure_with_outer_coro<Task<int>>();\n  co_await check_now_closure_with_outer_coro<now_task<int>>();\n\n  // Going from `closure_task` / `member_task` to `now_task` is rare, but it\n  // does work.  Of course, passing raw refs is not possible in this case.\n\n  co_await check_now_closure_with_outer_coro<closure_task<int>>();\n\n  auto makeNowClosure =\n      []<typename TaskT, typename CaptureRef>(tag_t<TaskT, CaptureRef>) {\n        return intAsyncNowClosure(bind::capture(7), [](auto n) -> TaskT {\n          static_assert(std::is_same_v<CaptureRef, decltype(n)>);\n          co_return *n;\n        });\n      };\n  // Safe `closure_task` -- safe to do a ref upgrade, absent `co_cleanup` args\n  // from the parent.\n  EXPECT_EQ(7, co_await makeNowClosure(tag<closure_task<int>, capture<int>>));\n  EXPECT_EQ( // Unsafe `Task` -- no ref upgrade\n      7,\n      co_await makeNowClosure(tag<Task<int>, after_cleanup_capture<int>>));\n\n  HasMemberTask hmt;\n  auto memberRes = co_await intAsyncNowClosure(\n      bind::args{&hmt, 7, bind::capture(30)}, FOLLY_INVOKE_MEMBER(task));\n  EXPECT_EQ(1337, memberRes);\n}\n\nCO_TEST(AsyncClosure, captureByReference) {\n  // This demo uses an atomic because e.g. with async scopes, the inner tasks\n  // might be concurrent -- and you can't move atomics, so you either need to\n  // use `AfterCleanup.h` (preferred, safer!) or capture-by-reference.\n  std::atomic_int n = 0;\n  co_await async_now_closure(\n      bind::capture_mut_ref{n}, [](auto n) -> closure_task<void> {\n        static_assert(std::is_same_v<capture<std::atomic_int&>, decltype(n)>);\n        n->fetch_add(42);\n        co_return;\n      });\n  EXPECT_EQ(42, n.load());\n  // Same, but with shared-cleanup downgrade due to an unsafe inner task type\n  co_await async_now_closure(\n      bind::capture_mut_ref{n}, [](auto n) -> Task<void> {\n        static_assert(\n            std::is_same_v<\n                after_cleanup_capture<std::atomic_int&>,\n                decltype(n)>);\n        n->fetch_add(-5);\n        co_return;\n      });\n  EXPECT_EQ(37, n.load());\n}\n\ntemplate <typename TaskT, typename CaptureIntRef>\nnow_task<> checkNowClosureCoCleanup() {\n  std::optional<exception_wrapper> optCleanErr;\n  int res = co_await async_now_closure(\n      bind::args{\n          bind::capture_in_place<HasCleanup>(&optCleanErr),\n          bind::capture(1300)},\n      [](auto cleanup, auto n) -> TaskT {\n        static_assert(\n            std::is_same_v<co_cleanup_capture<HasCleanup&>, decltype(cleanup)>);\n        static_assert(std::is_same_v<CaptureIntRef, decltype(n)>);\n        co_return *n + 37;\n      });\n  EXPECT_EQ(1337, res);\n  EXPECT_TRUE(optCleanErr.has_value());\n}\n\nCO_TEST(AsyncClosure, nowClosureCoCleanup) {\n  co_await checkNowClosureCoCleanup<closure_task<int>, capture<int&>>();\n  // Downgraded ref safety, since `Task` is unsafe\n  co_await checkNowClosureCoCleanup<Task<int>, after_cleanup_capture<int&>>();\n}\n\nconstexpr bool check_value_or_fatal_closures() {\n  static_assert( // safe_task, without outer coro\n      std::is_same_v<\n          value_or_fatal<value_task<>, on_stopped_void>,\n          decltype(async_closure(\n              bind::args{},\n              []() -> value_or_fatal<closure_task<>, on_stopped_void> {\n                co_return;\n              }))>);\n\n  static_assert( // safe_task, with outer coro\n      std::is_same_v<\n          value_or_fatal<value_task<>, on_stopped_void>,\n          decltype(async_closure<ForceOuter>(\n              bind::args{},\n              []() -> value_or_fatal<closure_task<>, on_stopped_void> {\n                co_return;\n              }))>);\n\n  static_assert( // now_task, without outer coro\n      std::is_same_v<\n          value_or_fatal<now_task<>, on_stopped_void>,\n          decltype(async_now_closure(\n              bind::args{}, []() -> value_or_fatal<Task<>, on_stopped_void> {\n                co_return;\n              }))>);\n  static_assert( // now_task, with outer coro\n      std::is_same_v<\n          value_or_fatal<now_task<>, on_stopped_void>,\n          decltype(async_now_closure<ForceOuter>(\n              bind::args{}, []() -> value_or_fatal<Task<>, on_stopped_void> {\n                co_return;\n              }))>);\n\n  return true;\n}\n\nstatic_assert(check_value_or_fatal_closures());\n\nTEST(AsyncClosure, throwingMoveCtor) {\n  struct MyErr : std::exception {};\n\n  struct ThrowOnMove {\n    ThrowOnMove() {}\n    ~ThrowOnMove() = default;\n    [[noreturn]] ThrowOnMove(ThrowOnMove&&) { throw MyErr{}; }\n    ThrowOnMove(const ThrowOnMove&) = delete;\n    void operator=(ThrowOnMove&&) = delete;\n    void operator=(const ThrowOnMove&) = delete;\n  };\n\n  // This used to also test `value_or_fatal<closure_task<ThrowingMove>...>`,\n  // but this no longer compiles since value-only awaitables don't make any\n  // sense with throwing move constructors.\n\n  auto throwNoOuter = async_closure(\n      bind::args{}, []() -> closure_task<ThrowOnMove> { co_return {}; });\n  EXPECT_THROW(blockingWait(std::move(throwNoOuter)), MyErr);\n\n  auto throwOuter = async_closure<ForceOuter>(\n      bind::args{}, []() -> closure_task<ThrowOnMove> { co_return {}; });\n  EXPECT_THROW(blockingWait(std::move(throwOuter)), MyErr);\n}\n\n// Records construction order, asserts that (1) cleanup & destruction happen in\n// the opposite order, and (2) all cleanups complete before any dtors.\nstruct OrderTracker : NonCopyableNonMovable {\n  int myN_;\n  int& nRef_;\n  int myCleanupN_;\n  int& cleanupNRef_;\n\n  explicit OrderTracker(int& n, int& cleanupN)\n      : myN_(++n), nRef_(n), myCleanupN_(++cleanupN), cleanupNRef_(cleanupN) {}\n\n  value_or_fatal<Task<>, on_stopped_void> co_cleanup(async_closure_private_t) {\n    EXPECT_EQ(myCleanupN_, cleanupNRef_--);\n    co_return;\n  }\n  ~OrderTracker() {\n    // Our contract is that all cleanups complete before any capture is\n    // destroyed.  This is required for `AfterCleanup.h` to be useful.\n    EXPECT_EQ(1000, cleanupNRef_);\n    EXPECT_EQ(myN_, nRef_--);\n  }\n};\n\nCO_TEST(AsyncClosure, ctorCleanupDtorOrdering) {\n  int n = 0, cleanupN = 1000;\n  co_await async_closure(\n      bind::args{\n          bind::capture_in_place<OrderTracker>(n, cleanupN),\n          bind::capture_in_place<OrderTracker>(n, cleanupN),\n          bind::capture_in_place<OrderTracker>(n, cleanupN),\n          bind::capture_in_place<OrderTracker>(n, cleanupN)},\n      [](auto c1, auto c2, auto c3, auto c4) -> closure_task<void> {\n        EXPECT_EQ(4, c1->nRef_);\n        EXPECT_EQ(1, c1->myN_);\n        EXPECT_EQ(2, c2->myN_);\n        EXPECT_EQ(3, c3->myN_);\n        EXPECT_EQ(4, c4->myN_);\n\n        EXPECT_EQ(1004, c1->cleanupNRef_);\n        EXPECT_EQ(1001, c1->myCleanupN_);\n        EXPECT_EQ(1002, c2->myCleanupN_);\n        EXPECT_EQ(1003, c3->myCleanupN_);\n        EXPECT_EQ(1004, c4->myCleanupN_);\n\n        co_return;\n      });\n}\n\n} // namespace folly::coro\n\n#endif\n\nint main(int argc, char** argv) {\n  ::testing::InitGoogleTest(&argc, argv);\n  folly::Init init(&argc, &argv); // `timeout` uses a `Timekeeper` singleton\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "folly/coro/safe/test/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_unittest(\n    name = \"captures_test\",\n    srcs = [\"CapturesTest.cpp\"],\n    deps = [\n        \"//folly/coro/safe:captures\",\n        \"//folly/lang/bind:bind\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"async_closure_test\",\n    srcs = [\"AsyncClosureTest.cpp\"],\n    deps = [\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:timeout\",\n        \"//folly/coro:value_or_fatal\",\n        \"//folly/coro/safe:async_closure\",\n        \"//folly/fibers:semaphore\",\n        \"//folly/init:init\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"now_task_test\",\n    srcs = [\"NowTaskTest.cpp\"],\n    deps = [\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro/safe:now_task\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"safe_task_test\",\n    srcs = [\"SafeTaskTest.cpp\"],\n    deps = [\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:timeout\",\n        \"//folly/coro/safe:safe_task\",\n        \"//folly/fibers:semaphore\",\n        \"//folly/init:init\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/safe/test/CapturesTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/safe/Captures.h>\n#include <folly/lang/bind/Bind.h>\n#include <folly/portability/GTest.h>\n\n#ifdef FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE\nstatic_assert(false, \"Leaked FOLLY_MOVABLE_AND_DEEP_CONST_LREF_COPYABLE\");\n#endif\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro::detail {\n\nstruct SimpleCleanup : NonCopyableNonMovable {\n  void co_cleanup(async_closure_private_t) {}\n  int x() & { return 1001; }\n  int x() const& { return 2002; }\n  int x() && { return 3003; }\n  void canMutate() {}\n};\n\ndecltype(auto) get_address(const auto& v) {\n  return &v;\n}\n\ntemplate <typename TA, typename TB>\nvoid check_capture_same_address(TA&& a, TB&& b) {\n  EXPECT_EQ(\n      get_address(*std::forward<TA>(a)), get_address(*std::forward<TB>(b)));\n}\n\nstruct CapturesTest : testing::Test {\n  // Required to access friend-only ctor of `capture`s.\n  static constexpr capture_private_t arg_priv{};\n\n  template <typename ArgT>\n  static constexpr auto make(auto&& arg) {\n    return ArgT{\n        arg_priv, forward_bind_wrapper(std::forward<decltype(arg)>(arg))};\n  }\n\n  template <typename ArgT>\n  auto make_co_cleanup(auto&&... args) {\n    return co_cleanup_capture<ArgT>{\n        arg_priv,\n        unsafe_tuple_to_bind_wrapper(\n            bind::in_place<ArgT>(std::forward<decltype(args)>(args)...)\n                .unsafe_tuple_to_bind())};\n  }\n\n  template <typename Arg>\n  auto shared_cleanup_ref(Arg&& arg) {\n    return std::forward<Arg>(arg).template to_capture_ref<true>(arg_priv);\n  }\n  template <typename Arg>\n  auto independent_cleanup_ref(Arg&& arg) {\n    return std::forward<Arg>(arg).template to_capture_ref<false>(arg_priv);\n  }\n\n  template <template <typename> class RefArg = capture, typename T = int>\n  void check_ref_from_mutable(int expected, auto a, auto make_ref) {\n    {\n      auto ar = make_ref(a);\n      static_assert(std::is_same_v<decltype(ar), RefArg<T&>>);\n      EXPECT_EQ(expected, *ar);\n      *a += 10;\n      expected += 10;\n\n      auto ar2 = make_ref(ar);\n      static_assert(std::is_same_v<decltype(ar2), RefArg<T&>>);\n      EXPECT_EQ(expected, *ar2);\n\n      // Future: How to concisely test that just `T&` won't compile?\n      auto ar3 = make_ref(std::as_const(ar2));\n      static_assert(std::is_same_v<decltype(ar3), RefArg<const T&>>);\n      EXPECT_EQ(expected, *ar3);\n\n      // Future: How to test here (and below) that `std::move` is required?\n      auto ar4 = make_ref(std::move(ar2));\n      static_assert(std::is_same_v<decltype(ar4), RefArg<T&&>>);\n      EXPECT_EQ(expected, *std::move(ar4));\n    }\n    {\n      auto ar = make_ref(std::move(a));\n      static_assert(std::is_same_v<decltype(ar), RefArg<T&&>>);\n      EXPECT_EQ(expected, *std::move(ar));\n      // NOLINTNEXTLINE(bugprone-use-after-move)\n      *a -= 10;\n      expected -= 10;\n\n      // NOLINTNEXTLINE(bugprone-use-after-move)\n      auto ar2 = make_ref(std::move(ar));\n      static_assert(std::is_same_v<decltype(ar2), RefArg<T&&>>);\n      EXPECT_EQ(expected, *std::move(ar2));\n    }\n  }\n\n  template <template <typename> class RefArg = capture, typename T = int>\n  void check_ref_from_const(int expected, auto a, auto make_ref) {\n    {\n      auto ar = make_ref(a);\n      static_assert(std::is_same_v<decltype(ar), RefArg<const T&>>);\n      check_capture_same_address(a, ar);\n\n      auto ar2 = make_ref(ar);\n      static_assert(std::is_same_v<decltype(ar2), RefArg<const T&>>);\n      check_capture_same_address(a, ar2);\n\n      auto ar3 = make_ref(std::move(ar2));\n      static_assert(std::is_same_v<decltype(ar3), RefArg<const T&&>>);\n      EXPECT_EQ(expected, *std::move(ar3));\n    }\n    {\n      auto ar = make_ref(std::move(a));\n      static_assert(std::is_same_v<decltype(ar), RefArg<const T&&>>);\n\n      auto ar2 = make_ref(std::move(ar));\n      static_assert(std::is_same_v<decltype(ar2), RefArg<const T&&>>);\n      EXPECT_EQ(expected, *std::move(ar2));\n    }\n  }\n\n  template <\n      typename T = SimpleCleanup,\n      template <typename> class RefArg = co_cleanup_capture>\n  void check_ref_from_cleanup(auto a, auto make_ref) {\n    auto ar = make_ref(a);\n    static_assert(std::is_same_v<decltype(ar), RefArg<T&>>);\n    check_capture_same_address(a, ar);\n    if constexpr (!std::is_const_v<T>) {\n      a->canMutate();\n    }\n\n    auto ar2 = make_ref(ar);\n    static_assert(std::is_same_v<decltype(ar2), RefArg<T&>>);\n    check_capture_same_address(a, ar2);\n\n    // Future: How to concisely test that just `T&` won't compile?\n    auto ar3 = make_ref(std::as_const(ar2));\n    static_assert(std::is_same_v<decltype(ar3), RefArg<const T&>>);\n    check_capture_same_address(a, ar3);\n  }\n\n  template <\n      template <typename> class RefFromAfterCleanup,\n      template <typename> class IndirectRefFromAfterCleanup>\n  void check_to_capture_ref(auto make_ref_fn) {\n    check_ref_from_mutable(5, make<capture<int>>(5), make_ref_fn);\n    check_ref_from_mutable(5, make<capture_heap<int>>(5), make_ref_fn);\n    check_ref_from_mutable<capture_indirect, std::unique_ptr<int>>(\n        5,\n        make<capture_indirect<std::unique_ptr<int>>>(std::make_unique<int>(5)),\n        make_ref_fn);\n    check_ref_from_cleanup(make_co_cleanup<SimpleCleanup>(), make_ref_fn);\n\n    // Same, but with a `const` type -- no mutability assertions.\n    check_ref_from_const(7, make<capture<const int>>(7), make_ref_fn);\n    check_ref_from_const(7, make<capture_heap<const int>>(7), make_ref_fn);\n    check_ref_from_const<capture_indirect, std::unique_ptr<int>>(\n        7,\n        make<capture_indirect<const std::unique_ptr<int>>>(\n            std::make_unique<int>(7)),\n        make_ref_fn);\n    check_ref_from_cleanup<const SimpleCleanup>(\n        make_co_cleanup<const SimpleCleanup>(), make_ref_fn);\n\n    // Repeat the above blocks, checking whether we shed `after_cleanup_ref_`\n    // from the ref.  There is no `after_cleanup_ref_co_cleanup_capture`, of\n    // course.\n\n    check_ref_from_mutable<RefFromAfterCleanup>(\n        5, make<after_cleanup_capture<int>>(5), make_ref_fn);\n    check_ref_from_mutable<RefFromAfterCleanup>(\n        5, make<after_cleanup_capture_heap<int>>(5), make_ref_fn);\n    check_ref_from_mutable<IndirectRefFromAfterCleanup, std::unique_ptr<int>>(\n        5,\n        make<after_cleanup_capture_indirect<std::unique_ptr<int>>>(\n            std::make_unique<int>(5)),\n        make_ref_fn);\n\n    check_ref_from_const<RefFromAfterCleanup>(\n        7, make<after_cleanup_capture<const int>>(7), make_ref_fn);\n    check_ref_from_const<RefFromAfterCleanup>(\n        7, make<after_cleanup_capture_heap<const int>>(7), make_ref_fn);\n    check_ref_from_const<IndirectRefFromAfterCleanup, std::unique_ptr<int>>(\n        7,\n        make<after_cleanup_capture_indirect<const std::unique_ptr<int>>>(\n            std::make_unique<int>(7)),\n        make_ref_fn);\n  }\n\n  struct TestStruct {\n    int member_;\n  };\n\n  // Check that member access through capture has expected value category.\n  // Crucially, we use `(*x).` instead of `x->` since the latter returns a\n  // pointer, discarding value category.\n  template <\n      typename InnerType,\n      typename ExpectedRvalMemberType,\n      typename ExpectedLvalMemberType = void>\n  static constexpr void check_member_access() {\n    using Capture = capture<InnerType>;\n\n    TestStruct obj{};\n    auto cap = make<Capture>(obj);\n    static_assert(std::is_same_v<Capture, decltype(cap)>);\n\n    static_assert(\n        std::is_same_v<\n            decltype(((*static_cast<Capture&&>(cap)).member_)),\n            ExpectedRvalMemberType>);\n\n    // C++ language limitation: `operator->` returns a pointer, so it always\n    // gives lvalue access regardless of value category\n    static_assert(\n        std::is_same_v<\n            decltype((static_cast<Capture&&>(cap)->member_)),\n            std::remove_reference_t<ExpectedRvalMemberType>&>);\n\n    // Test lvalue access for `capture<V>` and `capture<V&>`.  `capture<V&&>`\n    // may only be accessed by-rvalue.\n    if constexpr (!std::is_rvalue_reference_v<InnerType>) {\n      static_assert(\n          std::is_same_v<decltype(((*cap).member_)), ExpectedLvalMemberType>);\n      static_assert(\n          std::is_same_v<\n              decltype(((*static_cast<Capture&>(cap)).member_)),\n              ExpectedLvalMemberType>);\n      static_assert(\n          std::is_same_v<\n              decltype((static_cast<Capture&>(cap)->member_)),\n              ExpectedLvalMemberType&>);\n    }\n  }\n\n  // Test the value category behavior of member access through capture-refs.\n  static constexpr bool test_member_value_category() {\n    // capture<V> (owned value) gives lvalue member for lvalue access, and\n    // rvalue members for rvalue access.\n    check_member_access<TestStruct, int&&, int&>();\n    check_member_access<const TestStruct, const int&&, const int&>();\n\n    // capture<V&> always gives lval members, even when accessed by rvalue.\n    check_member_access<TestStruct&, int&, int&>();\n    check_member_access<const TestStruct&, const int&, const int&>();\n\n    // capture<V&&> is only accessed by rvalue.\n    check_member_access<TestStruct&&, int&&>();\n    check_member_access<const TestStruct&&, const int&&>();\n\n    return true;\n  }\n};\n\nstatic_assert(CapturesTest::test_member_value_category());\n\nTEST_F(CapturesTest, indirect_getUnderlyingUnsafe) {\n  auto ci = make<capture_indirect<std::unique_ptr<short>>>(\n      std::make_unique<short>(37));\n  auto x = std::move(ci).get_underlying_unsafe();\n  static_assert(std::is_same_v<decltype(x), std::unique_ptr<short>>);\n  EXPECT_EQ(37, *x);\n}\n\nTEST_F(CapturesTest, to_capture_ref_sharedCleanup) {\n  // We set `SharedCleanup == true`, so an input arg type with a\n  // `after_cleanup_ref_` prefix will retain that prefix on the ref.\n  check_to_capture_ref<after_cleanup_capture, after_cleanup_capture_indirect>(\n      [&](auto&& arg) {\n        return shared_cleanup_ref(std::forward<decltype(arg)>(arg));\n      });\n}\n\nTEST_F(CapturesTest, to_capture_ref_independentCleanup) {\n  // \"Upgrade\" behavior: We set `SharedCleanup == false`, so all arg types\n  // emit `capture` refs, even when the input was `after_cleanup_ref_`.\n  check_to_capture_ref<capture, capture_indirect>([&](auto&& arg) {\n    return independent_cleanup_ref(std::forward<decltype(arg)>(arg));\n  });\n}\n\nTEST_F(CapturesTest, capture_implicitRefConversion) {\n  // Implicit conversion to capture refs parallels\n  // `to_capture_ref_sharedCleanup` -- we must not upgrade\n  // `after_cleanup_ref_async_arc*` to non-`after_cleanup_ref`, since we're not\n  // creating an independent scope for the new capture ref.\n  check_to_capture_ref<after_cleanup_capture, after_cleanup_capture_indirect>(\n      [&](auto&& arg) {\n        return capture_ref_conversion_t<decltype(arg)>{\n            std::forward<decltype(arg)>(arg)};\n      });\n\n  // Sample no-abstraction checks -- `check_to_capture_ref` has more coverage\n  {\n    auto a = make<capture<int>>(5);\n    capture<const int&> aclr = std::as_const(a);\n    check_capture_same_address(a, aclr);\n    auto arr = capture<int&&>{std::move(a)};\n    check_capture_same_address(a, std::move(arr));\n    // Explicitly converting an rref into an lref is NOT covered by\n    // `check_to_capture_ref` because that logic is not part of\n    // async_closure binding conversions.\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    auto alr = capture<int&>{std::move(arr)};\n    check_capture_same_address(a, std::move(alr));\n  }\n  {\n    auto a = make<capture<const int>>(5);\n    capture<const int&> aclr = std::as_const(a);\n    check_capture_same_address(a, std::move(aclr));\n    auto acrr = capture<const int&&>{capture<const int&>{std::as_const(a)}};\n    check_capture_same_address(a, std::move(acrr));\n  }\n  {\n    auto a = make_co_cleanup<SimpleCleanup>();\n    co_cleanup_capture<SimpleCleanup&> ar = a;\n    check_capture_same_address(a, std::move(ar));\n  }\n\n  // Check `capture_ref_conversion_t` which is intended for finding the\n  // reference type, to which an capture instance can be implicitly\n  // converted.\n\n  // Simple mutable refs\n  static_assert(\n      std::is_same_v<capture_ref_conversion_t<capture<int>&>, capture<int&>>);\n  static_assert(\n      std::is_same_v<capture_ref_conversion_t<capture<int>>, capture<int&&>>);\n  static_assert(\n      std::is_same_v<capture_ref_conversion_t<capture<int>&&>, capture<int&&>>);\n\n  // Same, with a `const` wrapped type, ensuring we don't strip `const`\n  static_assert(\n      std::is_same_v<\n          capture_ref_conversion_t<capture<const int>&>,\n          capture<const int&>>);\n  static_assert(\n      std::is_same_v<\n          capture_ref_conversion_t<capture<const int>>,\n          capture<const int&&>>);\n  static_assert(\n      std::is_same_v<\n          capture_ref_conversion_t<capture<const int>&&>,\n          capture<const int&&>>);\n\n  // `const` from the wrapper moves to the wrapped type\n  static_assert(\n      std::is_same_v<\n          capture_ref_conversion_t<const capture<int>&>,\n          capture<const int&>>);\n  static_assert(\n      std::is_same_v<\n          capture_ref_conversion_t<const capture<int>>,\n          capture<const int&&>>);\n\n  // The outer reference type clobbers the inner one.  We DON'T want the\n  // `lref&&`-remains-an-lref reference collapsing rule here, since it seems\n  // desirable for this to work:\n  //   capture<T&&> myRref = std::move(myLref);\n  static_assert(\n      std::is_same_v<capture_ref_conversion_t<capture<int&&>&>, capture<int&>>);\n  static_assert(\n      std::is_same_v<capture_ref_conversion_t<capture<int&>&>, capture<int&>>);\n  static_assert(\n      std::\n          is_same_v<capture_ref_conversion_t<capture<int&>&&>, capture<int&&>>);\n  static_assert(\n      std::is_same_v<\n          capture_ref_conversion_t<capture<int&&>&&>,\n          capture<int&&>>);\n}\n\nTEST_F(CapturesTest, make_in_place) {\n  // A little redundant with `make_co_cleanup`, but isolated\n  capture<std::string> cap{\n      arg_priv,\n      unsafe_tuple_to_bind_wrapper(\n          bind::in_place<std::string>(\"hi\").unsafe_tuple_to_bind())};\n}\n\nTEST_F(CapturesTest, noCustomDereference) {\n  static_assert(has_async_closure_co_cleanup<SimpleCleanup>);\n\n  auto sc = make_co_cleanup<SimpleCleanup>();\n  EXPECT_EQ(1001, sc->x());\n  EXPECT_EQ(2002, std::as_const(sc)->x());\n  {\n    auto sc2 = make_co_cleanup<SimpleCleanup>();\n    EXPECT_EQ(3003, (*std::move(sc2)).x());\n  }\n  {\n    auto sc2 = make_co_cleanup<SimpleCleanup>();\n    // Note: Unfortunately, since `->` returns a plain pointer in the\n    // absence of `capture_proxy`, `std::move(sc)->` acts like `sc->`.\n    EXPECT_EQ(1001, std::move(sc2)->x());\n  }\n\n  auto rsc = restricted_co_cleanup_capture<SimpleCleanup&>{\n      arg_priv, forward_bind_wrapper(*sc)};\n#if 0 // Manual test: won't compile due to missing\n      // `capture_restricted_proxy`\n  static_assert(!requires { rsc->x(); });\n#endif\n}\n\nstruct CustomDerefCleanupRef : NonCopyableNonMovable {\n  explicit CustomDerefCleanupRef(int y) : y_(y) {}\n  auto operator->() { return static_cast<CustomDerefCleanupRef*>(this); }\n  int y_;\n};\n\nstruct CustomDerefCleanup : NonCopyableNonMovable {\n  void co_cleanup(async_closure_private_t) {}\n  template <\n      ext::capture_proxy_kind Kind,\n      ext::const_or_not<CustomDerefCleanup> T>\n  friend auto capture_proxy(ext::capture_proxy_tag<Kind>, T&) {\n    if constexpr (Kind == ext::capture_proxy_kind::lval_ref) {\n      return CustomDerefCleanupRef{101 + 1000 * std::is_const_v<T>};\n    } else if constexpr (Kind == ext::capture_proxy_kind::lval_ptr) {\n      return CustomDerefCleanupRef{202 + 1000 * std::is_const_v<T>};\n    } else if constexpr (Kind == ext::capture_proxy_kind::rval_ref) {\n      return CustomDerefCleanupRef{303 + 1000 * std::is_const_v<T>};\n    } else if constexpr (Kind == ext::capture_proxy_kind::rval_ptr) {\n      return CustomDerefCleanupRef{404 + 1000 * std::is_const_v<T>};\n    } else {\n      static_assert(false);\n    }\n  }\n  template <\n      ext::capture_proxy_kind Kind,\n      ext::const_or_not<CustomDerefCleanup> T>\n  friend auto capture_restricted_proxy(ext::capture_proxy_tag<Kind>, T&) {\n    if constexpr (Kind == ext::capture_proxy_kind::lval_ref) {\n      return CustomDerefCleanupRef{11 + 100 * std::is_const_v<T>};\n    } else if constexpr (Kind == ext::capture_proxy_kind::lval_ptr) {\n      return CustomDerefCleanupRef{22 + 100 * std::is_const_v<T>};\n    } else if constexpr (Kind == ext::capture_proxy_kind::rval_ref) {\n      return CustomDerefCleanupRef{33 + 100 * std::is_const_v<T>};\n    } else if constexpr (Kind == ext::capture_proxy_kind::rval_ptr) {\n      return CustomDerefCleanupRef{44 + 100 * std::is_const_v<T>};\n    } else {\n      static_assert(false);\n    }\n  }\n};\n\nTEST_F(CapturesTest, customDereference) {\n  static_assert(has_async_closure_co_cleanup<CustomDerefCleanup>);\n\n  auto c = make_co_cleanup<CustomDerefCleanup>();\n\n  EXPECT_EQ(101, (*c).y_);\n  EXPECT_EQ(202, c->y_);\n  EXPECT_EQ(404, shared_cleanup_ref(c)->y_);\n\n  EXPECT_EQ(1101, (*std::as_const(c)).y_);\n  EXPECT_EQ(1202, std::as_const(c)->y_);\n  EXPECT_EQ(1404, shared_cleanup_ref(std::as_const(c))->y_);\n\n  CustomDerefCleanup c2;\n  auto rc = restricted_co_cleanup_capture<CustomDerefCleanup&>{\n      arg_priv, forward_bind_wrapper(c2)};\n\n  EXPECT_EQ(11, (*rc).y_);\n  EXPECT_EQ(22, rc->y_);\n  EXPECT_EQ(33, (*std::move(rc)).y_);\n\n  EXPECT_EQ(111, (*std::as_const(rc)).y_);\n  EXPECT_EQ(122, std::as_const(rc)->y_);\n  EXPECT_EQ(133, (*std::move(std::as_const(rc))).y_);\n}\n\nTEST_F(CapturesTest, copyLValueRef) {\n  int n = 5;\n  // Copy non-const to non-const\n  {\n    auto src = make<capture<int&>>(n);\n    capture<int&> dst{src}; // copy ctor\n    dst = src; // copy assignment\n  }\n\n  // Copy to const from non-const OR from const\n  {\n    auto src = make<capture<int&>>(n);\n    capture<const int&> dst{src};\n    dst = src;\n  }\n  {\n    auto src = make<capture<int&>>(n);\n    capture<const int&> dst{std::as_const(src)};\n    dst = std::as_const(src);\n  }\n\n  // CANNOT copy from `const capture<int&>` to `capture<int&>` because we\n  // want this `capture` to have \"deep const\" semantics -- a `const` wrapper\n  // should protect the underlying data from changes.\n#if 0 // Manual test -- should fail with a \"no copy ctor\" error.\n  auto src = make<capture<int&>>(n);\n  capture<int&> dst{std::as_const(src)};\n#endif\n  // The asserts approximate the `#if 0` manual test that won't compile.\n  // Check both \"true\" and \"false\" cases to ensure the test itself is correct.\n  static_assert(\n      std::is_constructible_v<capture<const int&>, const capture<int>&>);\n  static_assert(!std::is_constructible_v<capture<int&>, const capture<int>&>);\n#if 0 // Manual test -- should fail with a \"no copy assignment\" error.\n  auto src = make<capture<int&>>(n);\n  capture<int&> dst{src}; // same as \"copy non-const to non-const\" above\n  dst = std::as_const(src); // fails here\n#endif\n  static_assert(std::is_assignable_v<capture<const int&>, const capture<int>&>);\n  static_assert(!std::is_assignable_v<capture<int&>, const capture<int>&>);\n}\n\nTEST_F(CapturesTest, moveLValueRef) {\n  // Move non-const& to non-const&\n  {\n    int n = 5;\n    auto src = make<capture<int&>>(n);\n    capture<int&> dst{std::move(src)}; // move ctor\n    (void)dst;\n  }\n  {\n    int n = 5;\n    auto src = make<capture<int&>>(n);\n    capture<int&> dst{make<capture<int&>>(n)};\n    dst = std::move(src); // move assignment\n  }\n\n  // Move non-const& to non-const&&\n  {\n    int n = 5;\n    auto src = make<capture<int&>>(n);\n    capture<int&&> dst{std::move(src)}; // move ctor\n    (void)dst;\n  }\n  {\n    int n = 5;\n    auto src = make<capture<int&>>(n);\n    capture<int&&> dst{make<capture<int&&>>(std::move(n))};\n    dst = capture<int&&>{std::move(src)}; // move assignment\n  }\n\n  // Move non-const& to const&\n  {\n    int n = 5;\n    auto src = make<capture<int&>>(n);\n    capture<const int&> dst{std::move(src)}; // move ctor\n    (void)dst;\n  }\n  {\n    int n = 5;\n    auto src = make<capture<int&>>(n);\n    capture<const int&> dst{make<capture<const int&>>(n)};\n    dst = std::move(src); // move assignment\n  }\n\n  // Like the CANNOT test in `copyLValueRef`, check we don't shed `const`\n  static_assert(\n      std::is_constructible_v<capture<const int&>, const capture<int>&&>);\n  static_assert(!std::is_constructible_v<capture<int&>, const capture<int>&&>);\n\n  // Maybe future: support \"move non-const& to const&&\".  Currently not done\n  // because of its low utility.  Also add a CANNOT shed const test.\n}\n\nTEST_F(CapturesTest, onlyMoveRValueRef) {\n  // Move non-const to non-const\n  {\n    int n = 5;\n    auto src = make<capture<int&&>>(std::move(n));\n    capture<int&&> dst{std::move(src)}; // move ctor\n    (void)dst;\n  }\n  {\n    int n1 = 5, n2 = 6;\n    auto src = make<capture<int&&>>(std::move(n1));\n    capture<int&&> dst{make<capture<int&&>>(std::move(n2))};\n    dst = std::move(src); // move assignment\n  }\n\n  // Future: Maybe add a move to `const &&` from non-const.  Right now I'm\n  // missing an overload for this due to its low utility.  Also add a CANNOT\n  // shed const test.\n}\n\n} // namespace folly::coro::detail\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/test/NowTaskTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/safe/NowTask.h>\n\n// Throughout this file, we have blocks of static asserts that follow the same\n// pattern:\n//  - Do some trait tests for movable `Task`, making sure they come out as\n//    expected.  This serves mainly to validate the trait actually checks what\n//    we want it to test.\n//  - Then, flip to `now_task` and confirm that the \"move\" action fails.\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro {\n\nstatic_assert(lenient_safe_alias_of_v<Task<int>> == safe_alias::unsafe);\nstatic_assert(lenient_safe_alias_of_v<now_task<int>> == safe_alias::unsafe);\n\nstatic_assert(std::is_void_v<await_result_t<now_task_with_executor<void>>>);\nstatic_assert(std::is_same_v<int, await_result_t<now_task_with_executor<int>>>);\n\nTask<int> demoTask(int x) {\n  co_return 1300 + x;\n}\nnow_task<int> demoNowTask(int x) {\n  co_return 1300 + x;\n}\n\ntemplate <typename T, typename Res = int>\ninline constexpr bool test_semi_await_result_v =\n    std::is_same_v<detected_t<semi_await_result_t, T>, Res>;\n\nstatic_assert(test_semi_await_result_v<Task<int>>);\nstatic_assert(!test_semi_await_result_v<Task<int>&>);\nstatic_assert(test_semi_await_result_v<Task<int>&&>);\nstatic_assert(test_semi_await_result_v<now_task<int>>);\nstatic_assert(!test_semi_await_result_v<now_task<int>&>);\nstatic_assert(!test_semi_await_result_v<now_task<int>&&>);\n\nusing DemoTryTask = decltype(co_awaitTry(demoTask(37)));\nusing DemoTryNowTask = decltype(co_awaitTry(demoNowTask(37)));\nstatic_assert(test_semi_await_result_v<DemoTryTask, Try<int>>);\nstatic_assert(!test_semi_await_result_v<DemoTryTask&, Try<int>>);\nstatic_assert(test_semi_await_result_v<DemoTryTask&&, Try<int>>);\nstatic_assert(test_semi_await_result_v<DemoTryNowTask, Try<int>>);\nstatic_assert(!test_semi_await_result_v<DemoTryNowTask&, Try<int>>);\nstatic_assert(!test_semi_await_result_v<DemoTryNowTask&&, Try<int>>);\n\n// Note: This `test_` predicate, and similar ones below, may look somewhat\n// redundant with `test_semi_await_result_v` above.  Both aim to test this:\n//   co_await demoNowTask(37); // works\n//   auto t = demoNowTask(37);\n//   co_await std::move(t); // does not compile\n// We check against test bugs by ensuring that BOTH forms work for `Task`.\n//\n// The rationale for the supposed redundancy is that here, we spell out the\n// expected call-path-to-awaiter.  This is pretty robust, so long as I check\n// both `Task` (good) and `now_task` (bad), whereas with the fancy\n// metaprogramming of `semi_await_result_t`, there's more risk that\n// `co_await std::move(t)` compiles for `now_task`, even when the type\n// function fails to substitute.\ntemplate <typename T>\nusing await_transform_result_t =\n    decltype(std::declval<detail::TaskPromise<void>>().await_transform(\n        FOLLY_DECLVAL(T)));\ntemplate <typename T>\ninline constexpr bool test_transform_moved_v = std::is_same_v<\n    detected_t<await_transform_result_t, T>,\n    typename Task<int>::PrivateAwaiterTypeForTests>;\n\nCO_TEST(NowTaskTest, simple) {\n  EXPECT_EQ(1337, co_await demoNowTask(37));\n\n  static_assert(test_transform_moved_v<Task<int>>);\n  // Won't compile: static_assert(!test_transform_moved_v<Task<int>&>);\n  static_assert(test_transform_moved_v<Task<int>&&>);\n  static_assert(test_transform_moved_v<now_task<int>>);\n  static_assert(!test_transform_moved_v<now_task<int>&>);\n  static_assert(!test_transform_moved_v<now_task<int>&&>);\n#if 0 // The above asserts approximate this manual test\n  auto t = demoNowTask(37);\n  co_await std::move(t);\n#endif\n}\n\ntemplate <typename T>\nusing co_withExecutor_result_t = decltype(co_withExecutor(\n    FOLLY_DECLVAL(Executor::KeepAlive<>), FOLLY_DECLVAL(T)));\n// Check type of `co_withExecutor(...)` for task prvalue or ref inputs.\ntemplate <typename T, typename TWithExec>\ninline constexpr bool test_move_into_co_withExecutor_v =\n    std::is_same_v<detected_t<co_withExecutor_result_t, T>, TWithExec>;\n\n// Check type of `co_await @`, where `@` is a task-with-executor prvalue or ref\ntemplate <typename T>\ninline constexpr bool test_transform_moved_with_executor_v = std::is_same_v<\n    detected_t<await_transform_result_t, T>,\n    StackAwareViaIfAsyncAwaitable<TaskWithExecutor<int>>>;\n\n// Check that `AsyncGenerator::await_transform` behaves like that of `Task`.\nCO_TEST(NowTaskTest, awaitFromGenerator) {\n  auto now30 = []() -> now_task<int> { co_return 30; };\n  auto now7 = []() -> now_task<int> { co_return 7; };\n  auto genFn = [&]() -> AsyncGenerator<int> {\n    co_yield co_await now30();\n    co_yield co_await co_nothrow(now7());\n  };\n  auto gen = genFn();\n  EXPECT_EQ(30, *(co_await gen.next()));\n  EXPECT_EQ(7, *(co_await gen.next()));\n}\n\nCO_TEST(NowTaskTest, withExecutor) {\n  auto exec = co_await co_current_executor;\n  EXPECT_EQ(1337, co_await co_withExecutor(exec, demoNowTask(37)));\n\n  // Check passing a task/ref into `co_withExecutor()`.\n  static_assert(\n      test_move_into_co_withExecutor_v<Task<int>, TaskWithExecutor<int>>);\n  static_assert(\n      !test_move_into_co_withExecutor_v<Task<int>&, TaskWithExecutor<int>>);\n  static_assert(\n      test_move_into_co_withExecutor_v<Task<int>&&, TaskWithExecutor<int>>);\n  static_assert(\n      test_move_into_co_withExecutor_v<\n          now_task<int>,\n          now_task_with_executor<int>>);\n  static_assert(\n      !test_move_into_co_withExecutor_v<\n          now_task<int>&,\n          now_task_with_executor<int>>);\n  static_assert(\n      !test_move_into_co_withExecutor_v<\n          now_task<int>&&,\n          now_task_with_executor<int>>);\n#if 0 // The above asserts approximate this manual test\n  auto t = demoNowTask(37);\n  co_await co_withExecutor(exec, std::move(t));\n#endif\n\n  // Check awaiting a task-with-executor or ref.\n  static_assert(test_transform_moved_with_executor_v<TaskWithExecutor<int>>);\n  // Won't compile:\n  // static_assert(!test_transform_moved_with_executor_v<TaskWithExecutor<int>&>);\n  static_assert(test_transform_moved_with_executor_v<TaskWithExecutor<int>&&>);\n  static_assert(\n      test_transform_moved_with_executor_v<now_task_with_executor<int>>);\n  static_assert(\n      !test_transform_moved_with_executor_v<now_task_with_executor<int>&>);\n  static_assert(\n      !test_transform_moved_with_executor_v<now_task_with_executor<int>&&>);\n#if 0 // The above asserts approximate this manual test\n  auto twe = co_withExecutor(exec, demoNowTask(37));\n  co_await std::move(twe);\n#endif\n}\n\n// `co_nothrow` isn't a function object, so we can't wrap it & pass prvalues\ntemplate <typename T>\nusing co_nothrow_result_t = decltype(co_nothrow(FOLLY_DECLVAL(T)));\ntemplate <typename T>\ninline constexpr bool test_make_co_nothrow_v = std::is_same_v<\n    detected_t<co_nothrow_result_t, T>,\n    detail::NothrowAwaitable<std::remove_reference_t<T>>>;\n\nCO_TEST(NowTaskTest, nothrow) {\n  EXPECT_EQ(1337, co_await co_nothrow(demoNowTask(37)));\n\n  static_assert(test_make_co_nothrow_v<Task<int>>);\n  // False -- there's no substitution failure, but instantiating the awaitable\n  // is an error.  This doesn't invalidate the test.\n  // static_assert(!test_make_co_nothrow_v<Task<int>&>);\n  static_assert(test_make_co_nothrow_v<Task<int>&&>);\n  static_assert(test_make_co_nothrow_v<now_task<int>>);\n  static_assert(!test_make_co_nothrow_v<now_task<int>&>);\n  static_assert(!test_make_co_nothrow_v<now_task<int>&&>);\n#if 0 // The above asserts approximate this manual test\n  auto t = demoNowTask(37);\n  co_nothrow(std::move(t));\n#endif\n\n  using DemoNothrowTask = decltype(co_nothrow(demoTask(37)));\n  using DemoNothrowNowTask = decltype(co_nothrow(demoNowTask(37)));\n  static_assert(test_transform_moved_v<DemoNothrowTask>);\n  // Won't compile: static_assert(!test_transform_moved_v<DemoNothrowTask&>);\n  static_assert(test_transform_moved_v<DemoNothrowTask&&>);\n  static_assert(test_transform_moved_v<DemoNothrowNowTask>);\n  static_assert(!test_transform_moved_v<DemoNothrowNowTask&>);\n  static_assert(!test_transform_moved_v<DemoNothrowNowTask&&>);\n#if 0 // The above asserts approximate this manual test\n  auto t = co_nothrow(demoNowTask(37));\n  co_await std::move(t);\n#endif\n\n  // Check that the \"nothrow\" functionality still works\n  struct MyErr : std::exception {};\n  auto errTask = []() -> now_task<> { co_yield co_error{MyErr{}}; };\n  auto outerTask = [&]() -> now_task<> {\n    try {\n      co_await co_nothrow(errTask());\n    } catch (...) {\n      ADD_FAILURE() << \"Not reached\";\n    }\n  };\n  EXPECT_THROW(co_await outerTask(), MyErr);\n}\n\n// `TryAwaitable` has a custom `operator co_await`, unlike `simple` and\n// `nothrow` that just return an awaiter from `await_transform`.\ntemplate <typename T>\nusing co_await_and_transform_result_t = decltype(operator co_await(\n    std::declval<detail::TaskPromise<void>>().await_transform(\n        FOLLY_DECLVAL(T))));\ntemplate <typename T>\ninline constexpr bool test_transform_and_await_moved_v = std::is_same_v<\n    detected_t<co_await_and_transform_result_t, T>,\n    detail::TryAwaiter<typename Task<int>::PrivateAwaiterTypeForTests>>;\n\n// `co_awaitTry` isn't a function object, so we can't wrap it & pass prvalues\ntemplate <typename T>\nusing co_awaitTry_result_t = decltype(co_awaitTry(FOLLY_DECLVAL(T)));\ntemplate <typename T>\ninline constexpr bool test_make_co_awaitTry_v = std::is_same_v<\n    detected_t<co_awaitTry_result_t, T>,\n    detail::TryAwaitable<std::remove_reference_t<T>>>;\n\nCO_TEST(NowTaskTest, awaitTry) {\n  EXPECT_EQ(1337, *(co_await co_awaitTry(demoNowTask(37))));\n\n  static_assert(test_make_co_awaitTry_v<Task<int>>);\n  // False -- there's no substitution failure, but instantiating the awaitable\n  // is an error.  This doesn't invalidate the test.\n  // static_assert(!test_make_co_awaitTry_v<Task<int>&>);\n  static_assert(test_make_co_awaitTry_v<Task<int>&&>);\n  static_assert(test_make_co_awaitTry_v<now_task<int>>);\n  static_assert(!test_make_co_awaitTry_v<now_task<int>&>);\n  static_assert(!test_make_co_awaitTry_v<now_task<int>&&>);\n#if 0 // The above asserts approximate this manual test\n  auto t = demoNowTask(37);\n  co_awaitTry(std::move(t));\n#endif\n\n  static_assert(test_transform_and_await_moved_v<DemoTryTask>);\n  // Won't compile:\n  // static_assert(!test_transform_and_await_moved_v<DemoTryTask&>);\n  static_assert(test_transform_and_await_moved_v<DemoTryTask&&>);\n  static_assert(test_transform_and_await_moved_v<DemoTryNowTask>);\n  static_assert(!test_transform_and_await_moved_v<DemoTryNowTask&>);\n  static_assert(!test_transform_and_await_moved_v<DemoTryNowTask&&>);\n#if 0 // The above asserts approximate this manual test\n  auto t = co_awaitTry(demoNowTask(37));\n  co_await std::move(t);\n#endif\n}\n\n// `invoke_result_t` cannot pass prvalues -- it invokes a move ctor.\ntemplate <typename T>\nusing blockingWait_result_t = decltype(blockingWait(FOLLY_DECLVAL(T)));\ntemplate <typename T, typename Res>\ninline constexpr bool test_blocking_wait_moved_v =\n    std::is_same_v<detected_t<blockingWait_result_t, T>, Res>;\n\nTEST(NowTaskTest, blockingWait) {\n  EXPECT_EQ(1337, blockingWait(demoNowTask(37)));\n\n  static_assert(test_blocking_wait_moved_v<Task<int>, int>);\n  static_assert(!test_blocking_wait_moved_v<Task<int>&, int>);\n  static_assert(test_blocking_wait_moved_v<Task<int>&&, int>);\n  static_assert(test_blocking_wait_moved_v<now_task<int>, int>);\n  static_assert(!test_blocking_wait_moved_v<now_task<int>&, int>);\n  static_assert(!test_blocking_wait_moved_v<now_task<int>&&, int>);\n#if 0 // The above asserts approximate this manual test\n  auto t = demoNowTask(37);\n  blockingWait(std::move(t));\n#endif\n}\n\nTEST(NowTaskTest, blockingWaitTry) {\n  EXPECT_EQ(1337, *blockingWait(co_awaitTry(demoNowTask(37))));\n\n  static_assert(test_blocking_wait_moved_v<DemoTryTask, Try<int>>);\n  static_assert(!test_blocking_wait_moved_v<DemoTryTask&, Try<int>>);\n  static_assert(test_blocking_wait_moved_v<DemoTryTask&&, Try<int>>);\n  static_assert(test_blocking_wait_moved_v<DemoTryNowTask, Try<int>>);\n  static_assert(!test_blocking_wait_moved_v<DemoTryNowTask&, Try<int>>);\n  static_assert(!test_blocking_wait_moved_v<DemoTryNowTask&&, Try<int>>);\n#if 0 // The above asserts approximate this manual test\n  auto t = co_awaitTry(demoNowTask(37));\n  blockingWait(std::move(t));\n#endif\n}\n\n// Both of these are antipatterns with `Task` because if you awaited either\n// of these coros outside of the statement that created them, it would have\n// dangling refs.\n//\n// Since `now_task` tries to ensure it can ONLY be awaited in the statement\n// that created it, C++ lifetime extension should save our bacon.\nCO_TEST(NowTaskTest, passByRef) {\n  auto res = co_await [](int&& x) -> now_task<int> { co_return 1300 + x; }(37);\n  EXPECT_EQ(1337, res);\n}\nCO_TEST(NowTaskTest, lambdaWithCaptures) {\n  int a = 1300, b = 37;\n  auto res = co_await [&a, b]() -> now_task<int> { co_return a + b; }();\n  EXPECT_EQ(1337, res);\n}\n\nCO_TEST(NowTaskTest, to_now_task) {\n  static_assert(\n      std::is_same_v<now_task<int>, decltype(to_now_task(demoNowTask(5)))>);\n  auto t = []() -> Task<int> { co_return 5; }();\n  static_assert(\n      std::is_same_v<now_task<int>, decltype(to_now_task(std::move(t)))>);\n  EXPECT_EQ(5, co_await to_now_task(std::move(t)));\n}\n\n// Test `as_unsafe()` escape hatch for `now_task` and `now_task_with_executor`.\n// These methods are marked deprecated, intended use is only for future->coro\n// migrations.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\")\n\nCO_TEST(NowTaskTest, asUnsafe) {\n  static_assert(\n      std::is_same_v<\n          Task<int>,\n          decltype(std::declval<now_task<int>>().as_unsafe())>);\n\n  // Passing by value is safe here\n  // @lint-ignore CLANGTIDY facebook-hte-Deprecated\n  EXPECT_EQ(1337, co_await demoNowTask(37).as_unsafe());\n}\n\nCO_TEST(NowTaskTest, asUnsafeWithExecutor) {\n  auto exec = co_await co_current_executor;\n  static_assert(\n      std::is_same_v<\n          TaskWithExecutor<int>,\n          decltype(std::declval<now_task_with_executor<int>>().as_unsafe())>);\n\n  // Passing by value is safe here\n  // @lint-ignore CLANGTIDY facebook-hte-Deprecated\n  EXPECT_EQ(1337, co_await co_withExecutor(exec, demoNowTask(37)).as_unsafe());\n}\n\n// Recommended migration patterns when using `now_task` with `SemiFuture`.\n// See `folly/coro/safe/docs/AsUnsafe.md` for full documentation.\nnow_task<int> processData(const int& x) {\n  // Force suspension, trigger potential lifetime issues with caller's stack\n  co_await folly::coro::co_reschedule_on_current_executor;\n  co_return 1300 + x;\n}\n\n// Pattern 1 (Preferred): Use `co_invoke` to allocate argument copies.\nfolly::SemiFuture<int> processDataCoInvoke(const int& x) {\n  // co_invoke makes decay-copies of arguments into a new coro frame,\n  // guaranteeing lifetime for the duration of `processData`\n\n  // @lint-ignore CLANGTIDY facebook-folly-coro-return-captures-local-var\n  return folly::coro::co_invoke(\n             [](auto&&... args) {\n               // @lint-ignore CLANGTIDY facebook-hte-Deprecated\n               return processData(std::forward<decltype(args)>(args)...)\n                   .as_unsafe();\n             },\n             x)\n      .semi();\n}\n\n// Pattern 2: Use `deferValue` to extend heap-allocated data lifetime.\nfolly::SemiFuture<int> processDataDefer(const int& x) {\n  // `make_unique` makes a copy onto the heap, but this would normally be\n  // destroyed at the return of `processDataDefer`.\n  // In order to guarantee lifetime for the duration of `processData` we must\n  // move the underlying data into the `deferValue` lambda\n  auto dataPtr = std::make_unique<int>(x);\n\n  // workaround for `facebook-hte-MoveEvaluationOrder`\n  auto rawPtr = dataPtr.get();\n\n  // @lint-ignore CLANGTIDY facebook-hte-Deprecated\n  // @lint-ignore CLANGTIDY facebook-folly-coro-temporary-by-ref\n  return processData(*rawPtr).as_unsafe().semi().deferValue(\n      [dataPtr = std::move(dataPtr)](int result) { return result; });\n}\n\nTEST(NowTaskTest, asUnsafeSemiFutureWithCoInvokeGuard) {\n  // Good hygiene is to `.get()` futures derived from `now_task` on the\n  // same line that creates them. Here, we intentionally use multi-line to\n  // demonstrate that the future became self-contained despite\n  // the by-ref args in `processData`.\n\n  auto future = processDataCoInvoke(37);\n  auto result = std::move(future).get();\n  EXPECT_EQ(result, 1337);\n}\n\nTEST(NowTaskTest, asUnsafeSemiFutureWithDeferGuard) {\n  // Bad code hygiene, refer to `asUnsafeSemiFutureWithCoInvokeGuard`\n  auto future = processDataDefer(37);\n  auto result = std::move(future).get();\n  EXPECT_EQ(result, 1337);\n}\n\nFOLLY_POP_WARNING\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/safe/test/SafeTaskTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Timeout.h>\n#include <folly/coro/safe/SafeTask.h>\n#include <folly/fibers/Semaphore.h>\n#include <folly/init/Init.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nusing namespace folly;\nusing namespace folly::coro;\nusing namespace std::literals::chrono_literals;\n\nstruct StatelessClass {\n  value_task<void> validSafeTask() { co_return; }\n};\n\nstruct StatefulClass {\n  int i;\n};\n\nstruct SafeStatefulClass {\n  int i;\n  template <safe_alias>\n  using folly_private_safe_alias_t =\n      // The least-safe marking that `member_task` will accept\n      safe_alias_constant<safe_alias::after_cleanup_ref>;\n};\n\nstruct UnsafeStatefulClass {\n  int i;\n  template <safe_alias>\n  using folly_private_safe_alias_t =\n      // The most-safe marking that `member_task` won't accept\n      safe_alias_constant<safe_alias::shared_cleanup>;\n};\n\nTEST(SafeTask, isSafeTaskValid) {\n  using folly::coro::detail::is_safe_task_valid;\n  constexpr auto kVal = safe_alias::maybe_value;\n  constexpr auto kPost = safe_alias::co_cleanup_safe_ref;\n  constexpr auto kPre = safe_alias::after_cleanup_ref;\n  constexpr auto kMember = safe_alias::unsafe_member_internal;\n  constexpr auto kClosure = safe_alias::unsafe_closure_internal;\n\n  // Without an implicit object parameter\n  static_assert(is_safe_task_valid<kVal, int, int>);\n  static_assert(!is_safe_task_valid<kVal, int, int*>);\n  static_assert(!is_safe_task_valid<kVal, int*, int>);\n  static_assert(is_safe_task_valid<kVal, void, int>);\n  static_assert(!is_safe_task_valid<kVal, void, int*>);\n\n  // With an implicit \"class\" object parameter\n  static_assert(is_safe_task_valid<kVal, int, StatelessClass&, int>);\n  static_assert(is_safe_task_valid<kVal, int, const StatelessClass&, int>);\n  static_assert(!is_safe_task_valid<kVal, int, StatefulClass&, int>);\n  static_assert(!is_safe_task_valid<kVal, int, const StatefulClass&, int>);\n\n  // Unlike `closure_task`, `member_task` tolerates stateful classes iff their\n  // safety is explicitly marked.\n  // (1) Stateless\n  static_assert(is_safe_task_valid<kClosure, int, StatelessClass&, int>);\n  static_assert(is_safe_task_valid<kClosure, int, const StatelessClass&, int>);\n  static_assert(is_safe_task_valid<kMember, int, StatelessClass&, int>);\n  static_assert(is_safe_task_valid<kMember, int, const StatelessClass&, int>);\n  // (2) Stateful, unmarked\n  static_assert(!is_safe_task_valid<kClosure, int, StatefulClass&, int>);\n  static_assert(!is_safe_task_valid<kClosure, int, const StatefulClass&, int>);\n  static_assert(!is_safe_task_valid<kMember, int, StatefulClass&, int>);\n  static_assert(!is_safe_task_valid<kMember, int, const StatefulClass&, int>);\n  // (3) Stateful, marked safe enough\n  static_assert(!is_safe_task_valid<kClosure, int, SafeStatefulClass&, int>);\n  static_assert(\n      !is_safe_task_valid<kClosure, int, const SafeStatefulClass&, int>);\n  static_assert(is_safe_task_valid<kMember, int, SafeStatefulClass&, int>);\n  static_assert(\n      is_safe_task_valid<kMember, int, const SafeStatefulClass&, int>);\n  // (4) Stateful, marked not safe enough\n  static_assert(!is_safe_task_valid<kClosure, int, UnsafeStatefulClass&, int>);\n  static_assert(\n      !is_safe_task_valid<kClosure, int, const UnsafeStatefulClass&, int>);\n  static_assert(!is_safe_task_valid<kMember, int, UnsafeStatefulClass&, int>);\n  static_assert(\n      !is_safe_task_valid<kMember, int, const UnsafeStatefulClass&, int>);\n\n  // With an implicit \"lambda\" object parameter\n  auto okFn = [](int x) -> value_task<int> { co_return x; };\n  static_assert(is_safe_task_valid<kVal, int, decltype(okFn)&, int>);\n  static_assert(is_safe_task_valid<kVal, int, const decltype(okFn)&, int>);\n  // Can declare this with captures because it's not a coro\n  auto badFn = [okFn](int x) -> value_task<int> { return okFn(x); };\n  static_assert(!is_safe_task_valid<kVal, int, decltype(badFn)&, int>);\n  static_assert(!is_safe_task_valid<kVal, int, const decltype(badFn)&, int>);\n\n  // With a templated implicit object parameter\n  auto okTmpl = [](auto x) -> value_task<int> { co_return x; };\n  static_assert(is_safe_task_valid<kVal, int, decltype(okTmpl)&, int>);\n  static_assert(is_safe_task_valid<kVal, int, const decltype(okTmpl)&, int>);\n  // Can declare this with captures because it's not a coro\n  auto badTmpl = [okTmpl](auto x) -> value_task<int> { return okTmpl(x); };\n  static_assert(!is_safe_task_valid<kVal, int, decltype(badTmpl)&, int>);\n  static_assert(!is_safe_task_valid<kVal, int, const decltype(badTmpl)&, int>);\n\n  // safe_alias::after_cleanup_ref relaxes constraint on args, but not return\n  // val\n  static_assert(is_safe_task_valid<kPre, int, manual_safe_ref_t<kPre, int>>);\n  static_assert(is_safe_task_valid<kPre, int, manual_safe_ref_t<kPost, int>>);\n  static_assert(!is_safe_task_valid<kPre, int, int*>);\n  static_assert(!is_safe_task_valid<kPre, int*, int>);\n  static_assert(!is_safe_task_valid<kPre, manual_safe_ref_t<kPre, int>, int>);\n  static_assert(!is_safe_task_valid<kPre, manual_safe_ref_t<kPost, int>, int>);\n  static_assert(is_safe_task_valid<kPre, void, int>);\n  static_assert(!is_safe_task_valid<kPre, void, int*>);\n\n  // Ditto for safe_alias::co_cleanup_safe_ref\n  static_assert(!is_safe_task_valid<kPost, int, manual_safe_ref_t<kPre, int>>);\n  static_assert(is_safe_task_valid<kPost, int, manual_safe_ref_t<kPost, int>>);\n  static_assert(!is_safe_task_valid<kPost, int, int*>);\n  static_assert(!is_safe_task_valid<kPost, int*, int>);\n  static_assert(!is_safe_task_valid<kPost, manual_safe_ref_t<kPre, int>, int>);\n  static_assert(!is_safe_task_valid<kPost, manual_safe_ref_t<kPost, int>, int>);\n  static_assert(is_safe_task_valid<kPost, void, int>);\n  static_assert(!is_safe_task_valid<kPost, void, int*>);\n}\n\nTEST(SafeTask, safe_alias_of_v) {\n  static_assert(\n      strict_safe_alias_of_v<value_task<int>> == safe_alias::maybe_value);\n  static_assert(\n      lenient_safe_alias_of_v<value_task<int>> == safe_alias::maybe_value);\n  static_assert(\n      lenient_safe_alias_of_v<safe_task<safe_alias::after_cleanup_ref, int>> ==\n      safe_alias::after_cleanup_ref);\n}\n\nCO_TEST(SafeTask, trivial) {\n  EXPECT_EQ(\n      1337, co_await [](int x) -> value_task<int> { co_return 1300 + x; }(37));\n}\n\nCO_TEST(CoCleanupSafeTask, trivial) {\n  int x = 37;\n  auto t = [](auto x) -> co_cleanup_safe_task<int> { co_return 1300 + x; };\n  EXPECT_EQ(\n      1337, co_await t(manual_safe_ref<safe_alias::co_cleanup_safe_ref>(x)));\n  EXPECT_EQ(1337, co_await t(manual_safe_ref(x)));\n}\n\nCO_TEST(PreCleanupTask, trivial) {\n  int x = 37;\n  auto t = [](auto x) -> safe_task<safe_alias::after_cleanup_ref, int> {\n    co_return 1300 + x;\n  };\n  EXPECT_EQ(\n      1337, co_await t(manual_safe_ref<safe_alias::after_cleanup_ref>(x)));\n  EXPECT_EQ(\n      1337, co_await t(manual_safe_ref<safe_alias::co_cleanup_safe_ref>(x)));\n  EXPECT_EQ(1337, co_await t(manual_safe_ref(x)));\n}\n\nnamespace {\nvalue_task<int> intFunc(auto x) {\n  co_return *x;\n}\n} // namespace\n\nCO_TEST(SafeTask, returnsNonVoid) {\n  auto x = std::make_unique<int>(17);\n  auto lambdaTmpl = [](auto x) -> value_task<int> { co_return x; };\n  EXPECT_EQ(\n      20,\n      // Would fail to compile with a raw pointer (i.e. `.get()`)\n      co_await intFunc(std::move(x)) + co_await lambdaTmpl(3));\n}\n\nnamespace {\nvalue_task<void> voidFunc(auto x) {\n  EXPECT_EQ(17, *x);\n  co_return;\n}\n} // namespace\n\nCO_TEST(SafeTask, returnsVoid) {\n  auto lambdaTmpl = [](auto x) -> value_task<void> {\n    EXPECT_EQ(3, x);\n    co_return;\n  };\n  co_await lambdaTmpl(3);\n  auto x = std::make_unique<int>(17);\n#if 1\n  co_await voidFunc(std::move(x));\n#else // Manual test: passing `int*` breaks the build with \"Bad safe_task\"\n  co_await voidFunc(x.get());\n#endif\n}\n\nCO_TEST(SafeTask, awaitsTask) {\n  EXPECT_EQ(\n      1337, co_await []() -> value_task<int> {\n        co_return 1300 + co_await ([]() -> Task<int> { co_return 37; }());\n      }());\n}\n\nCO_TEST(SafeTask, cancellation) {\n  EXPECT_THROW(\n      co_await timeout(\n          []() -> value_task<void> {\n            folly::fibers::Semaphore stuck{0}; // a cancellable baton\n            co_await stuck.co_wait();\n          }(),\n          200ms),\n      folly::FutureTimeout);\n}\n\nnamespace {\nstruct MyError : std::exception {};\n} // namespace\n\nCO_TEST(SafeTask, throws) {\n  EXPECT_THROW(\n      co_await []() -> value_task<void> { co_yield co_error(MyError{}); }(),\n      MyError);\n}\n\nCO_TEST(SafeTask, co_awaitTry) {\n  auto res = co_await co_awaitTry([]() -> value_task<void> {\n    co_yield co_error(MyError{});\n  }());\n  EXPECT_TRUE(res.hasException<MyError>());\n}\n\nnamespace folly::coro::detail {\n\nstruct SafeTaskTest : testing::Test {\n  template <safe_alias NewSafety>\n  auto withNewSafety(auto t) {\n    return std::move(t).template withNewSafety<NewSafety>();\n  }\n};\n\n// DO NOT COPY THIS!  `withNewSafety` is a hacks meant EXCLUSIVELY for the\n// `async_closure` implementation.\nCO_TEST_F(SafeTaskTest, withNewSafety) {\n  int x = 7;\n  auto t = withNewSafety<safe_alias::maybe_value>(\n      [](auto x) -> safe_task<safe_alias::shared_cleanup, int> {\n        co_return 30 + x;\n      }(manual_safe_ref<safe_alias::shared_cleanup>(x)));\n  static_assert(std::is_same_v<decltype(t), value_task<int>>);\n  EXPECT_EQ(37, co_await std::move(t));\n}\n\nCO_TEST_F(SafeTaskTest, ClosureTask) {\n  int x = 37;\n  auto t = [](auto x) -> closure_task<int> { co_return 1300 + x; };\n  // These must be unwrapped to be awaited. The \"new safety\" is incidental.\n  EXPECT_EQ(\n      1337,\n      co_await withNewSafety<safe_alias::maybe_value>(\n          t(manual_safe_ref<safe_alias::shared_cleanup>(x))));\n  EXPECT_EQ(\n      1337,\n      co_await withNewSafety<safe_alias::maybe_value>(t(manual_safe_ref(x))));\n}\n\nstruct HasMemberTask {\n  member_task<int> task(auto x) { co_return 1300 + x; }\n};\n\nstatic_assert(!std::is_move_constructible_v<member_task<int>>);\nstatic_assert(!std::is_move_assignable_v<member_task<int>>);\n\nCO_TEST_F(SafeTaskTest, MemberTask) {\n  HasMemberTask mt;\n  int x = 37;\n  EXPECT_EQ(1337, co_await mt.task(x));\n  EXPECT_EQ(\n      1337, co_await mt.task(manual_safe_ref<safe_alias::shared_cleanup>(x)));\n  EXPECT_EQ(1337, co_await mt.task(manual_safe_ref(x)));\n}\n\nstatic_assert(std::is_void_v<await_result_t<\n                  safe_task_with_executor<safe_alias::maybe_value, void>>>);\nstatic_assert(\n    std::is_same_v<\n        int,\n        await_result_t<safe_task_with_executor<safe_alias::maybe_value, int>>>);\n\n} // namespace folly::coro::detail\n\n#endif\n\nint main(int argc, char** argv) {\n  ::testing::InitGoogleTest(&argc, argv);\n  folly::Init init(&argc, &argv); // `timeout` uses a `Timekeeper` singleton\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "folly/coro/scripts/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:python_library.bzl\", \"python_library\")\nload(\"@fbsource//tools/build_defs:fb_python_library.bzl\", \"fb_python_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = fb_python_library,\n    name = \"co_bt\",\n    srcs = [\"co_bt.py\"],\n    visibility = [\"PUBLIC\"],\n)\n\n# !!!! fbcode/folly/coro/scripts/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = python_library,\n    # @autodeps-skip\n    name = \"co_bt\",\n    srcs = [\"co_bt.py\"],\n)\n"
  },
  {
    "path": "folly/coro/scripts/__init__.py",
    "content": ""
  },
  {
    "path": "folly/coro/scripts/co_bt.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# pyre-unsafe\n\nimport abc\nimport enum\nimport re\nimport sys\nimport traceback\nfrom dataclasses import dataclass\nfrom typing import ClassVar\n\n\nclass DebuggerValue(abc.ABC):\n    \"\"\"\n    Represents a value from the debugger. This could represent the value\n    of a variable, register, or expression.\n    \"\"\"\n\n    @staticmethod\n    @abc.abstractmethod\n    def nullptr() -> \"DebuggerValue\":\n        \"\"\"\n        Returns a nullptr value.\n        \"\"\"\n        pass\n\n    @staticmethod\n    @abc.abstractmethod\n    def parse_and_eval(expr: str) -> \"DebuggerValue\":\n        \"\"\"\n        Executes `expr` in the debugger and returns the value.\n        \"\"\"\n        pass\n\n    @staticmethod\n    @abc.abstractmethod\n    def execute(expr: str) -> str:\n        \"\"\"\n        Executes `expr` and returns the debugger output.\n        \"\"\"\n        pass\n\n    @staticmethod\n    @abc.abstractmethod\n    def get_current_pthread_addr() -> \"DebuggerValue\":\n        \"\"\"\n        Returns a pointer to the current pthread. Returns nullptr if not found\n        \"\"\"\n        pass\n\n    @staticmethod\n    @abc.abstractmethod\n    def get_register(register: str) -> \"DebuggerValue\":\n        \"\"\"\n        Returns the value in the provided register.\n        \"\"\"\n        pass\n\n    @abc.abstractmethod\n    def get_field(self, n: int) -> \"DebuggerValue\":\n        \"\"\"\n        This assumes the value is a pointer to a struct that consists entirely\n        of pointers. Returns the n-th pointer in the struct.\n        \"\"\"\n        pass\n\n    @abc.abstractmethod\n    def is_nullptr(self) -> bool:\n        \"\"\"\n        Returns True if the value is nullptr or 0.\n        \"\"\"\n        pass\n\n    @abc.abstractmethod\n    def int_value(self) -> int:\n        \"\"\"\n        Returns the int value of the debugger value\n        \"\"\"\n        pass\n\n    @abc.abstractmethod\n    def to_hex(self) -> str:\n        \"\"\"\n        Returns the value in hex padded with leading zeroes.\n        \"\"\"\n        pass\n\n    @abc.abstractmethod\n    def get_file_name_and_line(self) -> tuple[str, int] | None:\n        \"\"\"\n        Returns the file name and line number of the value.\n        Assumes the value is a pointer to an instruction.\n        Returns None if the name could not be found.\n        \"\"\"\n        pass\n\n    @abc.abstractmethod\n    def get_func_name(self) -> str | None:\n        \"\"\"\n        Returns the function name of the value. Returns None if the name could\n        not be found\n        \"\"\"\n        pass\n\n\n\"\"\"\nThese are the memory representations of the types folly uses for tracking\nasync stacks. See folly/tracing/AsyncStack.h\n\n// Pointed to by thread local storage\nstruct AsyncStackRootHolder {\n  AsyncStackRoot* value;\n};\n\nstruct AsyncStackRoot {\n  AsyncStackFrame* topFrame;\n  AsyncStackRoot* nextRoot;\n  void* stackFramePtr;\n  void* returnAddress;\n};\n\nstruct AsyncStackFrame {\n  AsyncStackFrame* parentFrame;\n  void* instructionPointer;\n  AsyncStackRoot* stackRoot;\n};\n\n// Memory representation of how the compiler generates stack frames\nstruct StackFrame {\n  StackFrame* stackFrame;\n  void* returnAddress;\n};\n\"\"\"\n\n# Key used in pthread thread local storage to hold a pointer to\n# AsyncStackRootHolder\nASYNC_STACK_ROOT_TLS_KEY = \"folly_async_stack_root_tls_key\"\n\n\n@dataclass\nclass AsyncStackRootHolder:\n    value: DebuggerValue\n\n    @staticmethod\n    def from_addr(addr: DebuggerValue) -> \"AsyncStackRootHolder\":\n        return AsyncStackRootHolder(\n            value=addr.get_field(0),\n        )\n\n\n@dataclass\nclass AsyncStackRoot:\n    top_frame: DebuggerValue\n    next_root: DebuggerValue\n    stack_frame_ptr: DebuggerValue\n    stack_root: DebuggerValue\n\n    @staticmethod\n    def from_addr(addr: DebuggerValue) -> \"AsyncStackRoot\":\n        return AsyncStackRoot(\n            top_frame=addr.get_field(0),\n            next_root=addr.get_field(1),\n            stack_frame_ptr=addr.get_field(2),\n            stack_root=addr.get_field(3),\n        )\n\n\n@dataclass\nclass AsyncStackFrame:\n    parent_frame: DebuggerValue\n    instruction_pointer: DebuggerValue\n    stack_root: DebuggerValue\n\n    @staticmethod\n    def from_addr(addr: DebuggerValue) -> \"AsyncStackFrame\":\n        return AsyncStackFrame(\n            parent_frame=addr.get_field(0),\n            instruction_pointer=addr.get_field(1),\n            stack_root=addr.get_field(2),\n        )\n\n\n@dataclass\nclass StackFrame:\n    stack_frame: DebuggerValue\n    return_address: DebuggerValue\n\n    @staticmethod\n    def from_addr(addr: DebuggerValue) -> \"StackFrame\":\n        return StackFrame(\n            stack_frame=addr.get_field(0),\n            return_address=addr.get_field(1),\n        )\n\n\ndef get_async_stack_root_addr(\n    debugger_value_class: type[DebuggerValue],\n) -> DebuggerValue:\n    \"\"\"\n    Returns a pointer to the top-most async stack root, or a nullptr if none\n    exists.\n    \"\"\"\n    pthread_addr = debugger_value_class.get_current_pthread_addr()\n    if pthread_addr.is_nullptr():\n        return debugger_value_class.nullptr()\n\n    # Check if the tls key is initialized\n    tls_key = debugger_value_class.parse_and_eval(\n        f\"(uint64_t){ASYNC_STACK_ROOT_TLS_KEY}\"\n    )\n    if (tls_key.int_value() % (2**32)) == ((2**32) - 1):\n        return debugger_value_class.nullptr()\n\n    # get the stack root pointer from thread-local storage\n    try:\n        # Note: \"struct pthread\" is the implementation type for \"pthread_t\".\n        # Its symbol information may not be available, depending if pthread\n        # debug symbols are available.\n        async_stack_root_holder_addr = debugger_value_class.parse_and_eval(\n            f\"((struct pthread*){pthread_addr.to_hex()})->specific\"\n            f\"[(int){tls_key.to_hex()}/32]\"\n            f\"[(int){tls_key.to_hex()}%32]\"\n            \".data\"\n        )\n        if async_stack_root_holder_addr.is_nullptr():\n            raise Exception(\"struct pthread info not found\")\n    except Exception:\n        # If \"struct pthread\" isn't defined, use the precalculated offset.\n        # Note: The offset is specific to linux x86_64.\n        specific_offset = 1296\n        specific_addr = debugger_value_class.parse_and_eval(\n            f\"{pthread_addr.to_hex()}+{specific_offset}\"\n        )\n        specific_second_level_addr = specific_addr.get_field(tls_key.int_value() // 32)\n        # pthread_key_data is equivalent to uintptr_t[2]\n        # We want the N-th pthread_key_data, and the second pointer inside\n        # pthread_key_data. So we want the uintptr_t at the 2 * N + 1 position\n        async_stack_root_holder_addr = specific_second_level_addr.get_field(\n            (2 * (tls_key.int_value() % 32)) + 1\n        )\n\n    if async_stack_root_holder_addr.is_nullptr():\n        return debugger_value_class.nullptr()\n    async_stack_root_holder = AsyncStackRootHolder.from_addr(\n        async_stack_root_holder_addr\n    )\n    return async_stack_root_holder.value\n\n\ndef print_async_stack_addrs(addrs: list[DebuggerValue]) -> None:\n    if len(addrs) == 0:\n        print(\"No async operation detected\")\n        return\n    num_digits = len(str(len(addrs)))\n    for i, addr in enumerate(addrs):\n        func_name = addr.get_func_name()\n        if func_name is None:\n            func_name = \"???\"\n        file_name_line_pair = addr.get_file_name_and_line()\n        if file_name_line_pair is None:\n            file_name = \"???\"\n            line = 0\n        else:\n            file_name, line = file_name_line_pair\n        print(\n            f\"#{str(i).ljust(num_digits, ' ')}\"\n            f\" {addr.to_hex()} in {func_name} () at {file_name}:{line}\"\n        )\n\n\ndef get_async_stack_addrs_from_initial_frame(\n    async_stack_frame_addr: DebuggerValue,\n) -> list[DebuggerValue]:\n    \"\"\"\n    Gets the list of async stack frames rooted at the current frame\n    \"\"\"\n    addrs: list[DebuggerValue] = []\n    while not async_stack_frame_addr.is_nullptr():\n        async_stack_frame = AsyncStackFrame.from_addr(async_stack_frame_addr)\n        addrs.append(async_stack_frame.instruction_pointer)\n        async_stack_frame_addr = async_stack_frame.parent_frame\n    return addrs\n\n\ndef walk_normal_stack(\n    normal_stack_frame_addr: DebuggerValue,\n    normal_stack_frame_stop_addr: DebuggerValue,\n) -> list[DebuggerValue]:\n    \"\"\"\n    Returns the list of return addresses in the normal stack.\n    Does not include stop_addr\n    \"\"\"\n    addrs: list[DebuggerValue] = []\n    while not normal_stack_frame_addr.is_nullptr():\n        normal_stack_frame = StackFrame.from_addr(normal_stack_frame_addr)\n        if (\n            not normal_stack_frame_stop_addr.is_nullptr()\n            and normal_stack_frame.stack_frame == normal_stack_frame_stop_addr\n        ):\n            # Reached end of normal stack, transition to the async stack\n            # Do not include the return address in the stack trace that points\n            # to the frame that registered the AsyncStackRoot.\n            break\n        addrs.append(normal_stack_frame.return_address)\n        normal_stack_frame_addr = normal_stack_frame.stack_frame\n    return addrs\n\n\n@dataclass\nclass WalkAsyncStackResult:\n    addrs: list[DebuggerValue]\n    # Normal stack frame to start the next normal stack walk\n    normal_stack_frame_addr: DebuggerValue\n    normal_stack_frame_stop_addr: DebuggerValue\n    # Async stack frame to start the next async stack walk after the next\n    # normal stack walk\n    async_stack_frame_addr: DebuggerValue\n\n\ndef walk_async_stack(\n    debugger_value_class: type[DebuggerValue],\n    async_stack_frame_addr: DebuggerValue,\n) -> WalkAsyncStackResult:\n    \"\"\"\n    Walks the async stack and returns the next normal stack and async stack\n    addresses to walk.\n    \"\"\"\n    addrs: list[DebuggerValue] = []\n    normal_stack_frame_addr = debugger_value_class.nullptr()\n    normal_stack_frame_stop_addr = debugger_value_class.nullptr()\n    async_stack_frame_next_addr = debugger_value_class.nullptr()\n    while not async_stack_frame_addr.is_nullptr():\n        async_stack_frame = AsyncStackFrame.from_addr(async_stack_frame_addr)\n        addrs.append(async_stack_frame.instruction_pointer)\n\n        if async_stack_frame.parent_frame.is_nullptr():\n            # Reached end of async stack\n            # Check if there is an AsyncStackRoot and if so, whether there\n            # is an associated stack frame that indicates the normal stack\n            # frame we should continue walking at.\n            async_stack_root_addr = async_stack_frame.stack_root\n            if async_stack_root_addr.is_nullptr():\n                # This is a detached async stack. We are done\n                break\n            async_stack_root = AsyncStackRoot.from_addr(async_stack_root_addr)\n            normal_stack_frame_addr = async_stack_root.stack_frame_ptr\n            if normal_stack_frame_addr.is_nullptr():\n                # No associated normal stack frame for this async stack root.\n                # This means we should treat this as a top-level/detached\n                # stack and not try to walk any further.\n                break\n            # Skip to the parent stack frame pointer\n            normal_stack_frame = StackFrame.from_addr(normal_stack_frame_addr)\n            normal_stack_frame_addr = normal_stack_frame.stack_frame\n\n            # Check if there is a higher-level AsyncStackRoot that defines\n            # the stop point we should stop walking normal stack frames at.\n            # If there is no higher stack root then we will walk to the\n            # top of the normal stack (normalStackFrameStop == nullptr).\n            # Otherwise we record the frame pointer that we should stop\n            # at and walk normal stack frames until we hit that frame.\n            # Also get the async stack frame where the next async stack walk\n            # should begin after the next normal stack walk finishes.\n            async_stack_root_addr = async_stack_root.next_root\n            if not async_stack_root_addr.is_nullptr():\n                async_stack_root = AsyncStackRoot.from_addr(async_stack_root_addr)\n                normal_stack_frame_stop_addr = async_stack_root.stack_frame_ptr\n                async_stack_frame_next_addr = async_stack_root.top_frame\n\n        async_stack_frame_addr = async_stack_frame.parent_frame\n\n    return WalkAsyncStackResult(\n        addrs=addrs,\n        normal_stack_frame_addr=normal_stack_frame_addr,\n        normal_stack_frame_stop_addr=normal_stack_frame_stop_addr,\n        async_stack_frame_addr=async_stack_frame_next_addr,\n    )\n\n\ndef get_async_stack_addrs(\n    debugger_value_class: type[DebuggerValue],\n) -> list[DebuggerValue]:\n    \"\"\"\n    Gets the async stack trace, including normal stack frames with async\n    stack frames.\n\n    See C++ implementation in `getAsyncStackTraceSafe` in\n    folly/debugging/symbolizer/StackTrace.cpp\n    \"\"\"\n    async_stack_root_addr = get_async_stack_root_addr(debugger_value_class)\n\n    # If we have no async stack root, this should return no frames.\n    # If we do have a stack root, also include the current return address.\n    if async_stack_root_addr.is_nullptr():\n        return []\n\n    # Start the stack trace from the top\n    debugger_value_class.execute(\"f 0\")\n\n    # Start by walking the normal stack until we get to the frame right before\n    # the frame that holds the async root.\n    async_stack_root = AsyncStackRoot.from_addr(async_stack_root_addr)\n    normal_stack_frame_addr = debugger_value_class.get_register(\"rbp\")\n    normal_stack_frame_stop_addr = async_stack_root.stack_frame_ptr\n    addrs: list[DebuggerValue] = []\n    addrs.append(debugger_value_class.get_register(\"pc\"))\n    async_stack_frame_addr = async_stack_root.top_frame\n\n    while (\n        not normal_stack_frame_addr.is_nullptr()\n        or not async_stack_frame_addr.is_nullptr()\n    ):\n        addrs += walk_normal_stack(\n            normal_stack_frame_addr, normal_stack_frame_stop_addr\n        )\n        walk_async_stack_result = walk_async_stack(\n            debugger_value_class, async_stack_frame_addr\n        )\n        addrs += walk_async_stack_result.addrs\n        normal_stack_frame_addr = walk_async_stack_result.normal_stack_frame_addr\n        normal_stack_frame_stop_addr = (\n            walk_async_stack_result.normal_stack_frame_stop_addr\n        )\n        async_stack_frame_addr = walk_async_stack_result.async_stack_frame_addr\n    return addrs\n\n\ndef print_async_stack_root_addrs(addrs: list[DebuggerValue]) -> None:\n    if len(addrs) == 0:\n        print(\"No async stack roots detected\")\n        return\n    num_digits = len(str(len(addrs)))\n    for i, addr in enumerate(addrs):\n        async_stack_root = AsyncStackRoot.from_addr(addr)\n        if not async_stack_root.stack_frame_ptr.is_nullptr():\n            stack_frame = StackFrame.from_addr(async_stack_root.stack_frame_ptr)\n            func_name = stack_frame.return_address.get_func_name()\n            if func_name is None:\n                func_name = \"???\"\n            file_name_line_pair = stack_frame.return_address.get_file_name_and_line()\n            if file_name_line_pair is not None:\n                file_name, line = file_name_line_pair\n            else:\n                file_name = \"???\"\n                line = 0\n        else:\n            func_name = \"???\"\n            file_name = \"???\"\n            line = 0\n        print(\n            f\"#{str(i).ljust(num_digits, ' ')}\"\n            f\" async stack root {addr.to_hex()}\"\n            f\" located in normal stack frame {async_stack_root.stack_frame_ptr.to_hex()}\"\n            f\" in {func_name} () at {file_name}:{line}\"\n        )\n\n\ndef get_async_stack_root_addrs(\n    debugger_value_class: type[DebuggerValue],\n) -> list[DebuggerValue]:\n    \"\"\"\n    Gets all the async stack roots that exist for the current thread.\n    \"\"\"\n    addrs: list[DebuggerValue] = []\n    async_stack_root_addr = get_async_stack_root_addr(debugger_value_class)\n    while not async_stack_root_addr.is_nullptr():\n        addrs.append(async_stack_root_addr)\n        async_stack_root = AsyncStackRoot.from_addr(async_stack_root_addr)\n        async_stack_root_addr = async_stack_root.next_root\n    return addrs\n\n\ndef backtrace_command(\n    debugger_value_class: type[DebuggerValue],\n    stack_root: str | None,\n) -> None:\n    try:\n        addrs: list[DebuggerValue] = []\n        if stack_root:\n            async_stack_root_addr = debugger_value_class.parse_and_eval(stack_root)\n            if not async_stack_root_addr.is_nullptr():\n                async_stack_root = AsyncStackRoot.from_addr(async_stack_root_addr)\n                addrs = get_async_stack_addrs_from_initial_frame(\n                    async_stack_root.top_frame\n                )\n        else:\n            addrs = get_async_stack_addrs(debugger_value_class)\n        print_async_stack_addrs(addrs)\n    except Exception:\n        print(\"Error collecting async stack trace:\")\n        # pyre-fixme[6]: For 1st argument expected `BaseException` but got\n        #  `Union[None, Type[BaseException], BaseException, TracebackType]`.\n        traceback.print_exception(*sys.exc_info())\n\n\ndef async_stack_roots_command(debugger_value_class: type[DebuggerValue]) -> None:\n    addrs = get_async_stack_root_addrs(debugger_value_class)\n    print_async_stack_root_addrs(addrs)\n\n\ndef co_bt_info() -> str:\n    return \"\"\"Command: co_bt [async_stack_root_addr]\n\nPrints async stack trace for the current thread.\nIf an async stack root address is provided,\nprints the async stack starting from this root.\n\"\"\"\n\n\ndef co_async_stack_root_info() -> str:\n    return \"\"\"Command: co_async_stack_roots\n\nPrints all async stack roots.\n\"\"\"\n\n\nclass DebuggerType(enum.Enum):\n    GDB = 0\n    LLDB = 1\n\n\ndebugger_type: DebuggerType | None = None\nif debugger_type is None:  # noqa: C901\n    try:\n        # pyre-fixme[21]: Could not find module `gdb`.\n        import gdb\n\n        class GdbValue(DebuggerValue):\n            \"\"\"\n            GDB implementation of a debugger value\n            \"\"\"\n\n            # pyre-fixme[11]: Annotation `Value` is not defined as a type.\n            value: gdb.Value\n\n            def __init__(self, value: gdb.Value) -> None:\n                self.value = value\n\n            @staticmethod\n            def nullptr() -> DebuggerValue:\n                return GdbValue.parse_and_eval(\"0x0\")\n\n            @staticmethod\n            def parse_and_eval(expr: str) -> DebuggerValue:\n                return GdbValue(gdb.parse_and_eval(expr))\n\n            @staticmethod\n            def execute(expr: str) -> str:\n                return gdb.execute(expr, from_tty=False, to_string=True)\n\n            @staticmethod\n            def get_current_pthread_addr() -> DebuggerValue:\n                try:\n                    # On Linux x86_64, the pthread struct pointer is stored\n                    # in the fs_base virtual register. Try to read it from\n                    # this register first.\n                    fs_base = GdbValue.get_register(\"fs_base\")\n                    if not fs_base.is_nullptr():\n                        return fs_base\n                except Exception:\n                    pass\n                regex = re.compile(r\"\\[Current thread is.*\\(Thread (.*) \\(LWP .*\\)\\)\")\n                output = GdbValue.execute(\"thread\").split(\"\\n\")[0]\n                groups = regex.match(output)\n                return (\n                    GdbValue.parse_and_eval(groups.group(1))\n                    if groups\n                    else GdbValue.nullptr()\n                )\n\n            @staticmethod\n            def get_register(register: str) -> DebuggerValue:\n                return GdbValue.parse_and_eval(f\"${register}\")\n\n            def get_field(self, n: int) -> DebuggerValue:\n                return GdbValue.parse_and_eval(f\"((uintptr_t*){self.value})[{n}]\")\n\n            def is_nullptr(self) -> bool:\n                return int(self.value) == 0\n\n            def int_value(self) -> int:\n                return int(self.value)\n\n            def to_hex(self) -> str:\n                return f\"{int(self.value):#0{18}x}\"\n\n            def get_file_name_and_line(self) -> tuple[str, int] | None:\n                regex = re.compile(r\"Line (\\d+) of (.*) starts at.*\")\n                output = GdbValue.execute(\n                    f\"info line *{self.to_hex()}\",\n                ).split(\"\\n\")[0]\n                groups = regex.match(output)\n                return (\n                    (groups.group(2).strip('\"'), int(groups.group(1)))\n                    if groups\n                    else None\n                )\n\n            def get_func_name(self) -> str | None:\n                regex = re.compile(r\"(.*) \\+ \\d+ in section.* of .*\")\n                output = GdbValue.execute(\n                    f\"info symbol {self.to_hex()}\",\n                ).split(\"\\n\")[0]\n                groups = regex.match(output)\n                return groups.group(1) if groups else None\n\n            def __eq__(self, other) -> bool:\n                return self.int_value() == other.int_value()\n\n        # pyre-fixme[11]: Annotation `Command` is not defined as a type.\n        class GdbCoroBacktraceCommand(gdb.Command):\n            def __init__(self):\n                print(co_bt_info())\n                super().__init__(\"co_bt\", gdb.COMMAND_USER)\n\n            def invoke(self, arg: str, from_tty: bool):\n                backtrace_command(GdbValue, arg)\n\n        class GdbCoroAsyncStackRootsCommand(gdb.Command):\n            def __init__(self):\n                print(co_async_stack_root_info())\n                super().__init__(\"co_async_stack_roots\", gdb.COMMAND_USER)\n\n            def invoke(self, arg: str, from_tty: bool):\n                async_stack_roots_command(GdbValue)\n\n        debugger_type = DebuggerType.GDB\n    except Exception:\n        pass\n\nif debugger_type is None:  # noqa: C901\n    try:\n        # pyre-fixme[21]: Could not find module `lldb`.\n        import lldb\n\n        class LldbValue(DebuggerValue):\n            \"\"\"\n            LLDB implementation of a debugger value\n            \"\"\"\n\n            # pyre-fixme[11]: Annotation `SBExecutionContext` is not defined as a type.\n            exe_ctx: ClassVar[lldb.SBExecutionContext | None] = None\n            next_name_num: ClassVar[int] = 0\n            # pyre-fixme[11]: Annotation `SBValue` is not defined as a type.\n            value: lldb.SBValue\n\n            def __init__(self, value: lldb.SBValue) -> None:\n                self.value = value\n\n            @staticmethod\n            def nullptr() -> DebuggerValue:\n                return LldbValue.parse_and_eval(\"nullptr\")\n\n            @staticmethod\n            def parse_and_eval(expr: str) -> DebuggerValue:\n                value = LldbValue(\n                    LldbValue.exe_ctx.GetTarget().CreateValueFromExpression(\n                        f\"{LldbValue.next_name_num}\", expr\n                    )\n                )\n                LldbValue.next_name_num += 1\n                return value\n\n            @staticmethod\n            def execute(expr: str) -> str:\n                return_obj = lldb.SBCommandReturnObject()\n                LldbValue.exe_ctx.GetTarget().GetDebugger().GetCommandInterpreter().HandleCommand(\n                    expr, LldbValue.exe_ctx, return_obj, False\n                )\n                if return_obj.Succeeded():\n                    return return_obj.GetOutput()\n                return return_obj.GetError()\n\n            @staticmethod\n            def get_current_pthread_addr() -> DebuggerValue:\n                try:\n                    # On Linux x86_64, the pthread struct pointer is stored\n                    # in the fs_base virtual register.\n                    # LLDB upstream currently does not provide support for\n                    # the lldb fs_base virtual register, see:\n                    # https://discourse.llvm.org/t/how-to-get-pthread-pointer-from-lldb/70542\n                    # Internally, we have a patch to add support for fs_base on\n                    # Linux x86_64 that we will upstream soon.\n                    # In this case, try to use the fs_base register if it\n                    # exists, otherwise fall back to other ways:\n                    fs_base = LldbValue.get_register(\"fs_base\")\n                    if not fs_base.is_nullptr():\n                        return fs_base\n                except Exception:\n                    pass\n\n                # If we are a live process, try to call this helper to get\n                # the pthread struct pointer. Note that this may not work\n                # on core dumps or optimized builds.\n                result = LldbValue.exe_ctx.GetFrame().EvaluateExpression(\n                    \"(struct pthread*)pthread_self()\"\n                )\n                if result.GetError().Success():\n                    return LldbValue(result)\n                return LldbValue.nullptr()\n\n            @staticmethod\n            def get_register(register: str) -> DebuggerValue:\n                return LldbValue(LldbValue.exe_ctx.GetFrame().FindRegister(register))\n\n            def get_field(self, n: int) -> DebuggerValue:\n                # Linux x86_64 size of a pointer\n                ptr_size = 8\n                ptr_type = (\n                    LldbValue.exe_ctx.GetTarget()\n                    .FindFirstType(\"uintptr_t\")\n                    .GetPointerType()\n                )\n                address = lldb.SBAddress(\n                    self.int_value() + ptr_size * n,\n                    LldbValue.exe_ctx.GetTarget(),\n                )\n                value = LldbValue(\n                    LldbValue.exe_ctx.GetTarget().CreateValueFromAddress(\n                        f\"{LldbValue.next_name_num}\", address, ptr_type\n                    )\n                )\n                LldbValue.next_name_num += 1\n                return value\n\n            def is_nullptr(self) -> bool:\n                return int(self.value.value, 0) == 0\n\n            def int_value(self) -> int:\n                return int(self.value.value, 0)\n\n            def to_hex(self) -> str:\n                return f\"{int(self.value.value, 0):#0{18}x}\"\n\n            # Type must be in quotes because it breaks parsing\n            # with conditional imports\n            # pyre-fixme[11]: Annotation `SBSymbolContext` is not defined as a type.\n            def _get_symbol_context(self) -> \"lldb.SBSymbolContext\":\n                address = lldb.SBAddress(\n                    self.int_value(), LldbValue.exe_ctx.GetTarget()\n                )\n                return LldbValue.exe_ctx.GetTarget().ResolveSymbolContextForAddress(\n                    address, lldb.eSymbolContextEverything\n                )\n\n            def get_file_name_and_line(self) -> tuple[str, int] | None:\n                symbol_context = self._get_symbol_context()\n                line_entry = symbol_context.GetLineEntry()\n                path = line_entry.GetFileSpec().fullpath\n                if path:\n                    return (path, line_entry.GetLine())\n                return None\n\n            def get_func_name(self) -> str | None:\n                symbol_context = self._get_symbol_context()\n                if symbol_context.GetFunction().IsValid():\n                    return symbol_context.GetFunction().GetDisplayName()\n                return symbol_context.GetSymbol().GetDisplayName()\n\n            def __eq__(self, other) -> bool:\n                return self.int_value() == other.int_value()\n\n        class LldbCoroBacktraceCommand:\n            program: ClassVar[str] = \"co_bt\"\n\n            def __init__(self, debugger, internal_dict):\n                pass\n\n            @classmethod\n            def register_lldb_command(cls, debugger, module_name):\n                command = (\n                    f\"command script add -c {module_name}.{cls.__name__} {cls.program}\"\n                )\n                debugger.HandleCommand(command)\n\n            def get_short_help(self):\n                return co_bt_info()\n\n            def get_long_help(self):\n                return co_bt_info()\n\n            def __call__(self, debugger, command, exe_ctx, result):\n                LldbValue.exe_ctx = exe_ctx\n                backtrace_command(LldbValue, command)\n\n        class LldbCoroAsyncStackRootsCommand:\n            program = \"co_async_stack_roots\"\n\n            def __init__(self, debugger, internal_dict):\n                pass\n\n            @classmethod\n            def register_lldb_command(cls, debugger, module_name):\n                command = (\n                    f\"command script add -c {module_name}.{cls.__name__} {cls.program}\"\n                )\n                debugger.HandleCommand(command)\n\n            def get_short_help(self):\n                return co_async_stack_root_info()\n\n            def get_long_help(self):\n                return co_async_stack_root_info()\n\n            def __call__(self, debugger, command, exe_ctx, result):\n                LldbValue.exe_ctx = exe_ctx\n                async_stack_roots_command(LldbValue)\n\n        debugger_type = DebuggerType.LLDB\n    except Exception:\n        pass\n\n\ndef info():\n    return f\"\"\"Pretty printers for folly::coro. Available commands:\n{co_bt_info()}\n\n{co_async_stack_root_info()}\n\"\"\"\n\n\ndef load(debugger=None) -> None:\n    \"\"\"\n    This debugger script is meant to work with both lldb and gdb. Use\n    conditional imports, as one will not be defined when we use the other.\n    \"\"\"\n    if debugger_type == DebuggerType.GDB:\n        GdbCoroBacktraceCommand()\n        GdbCoroAsyncStackRootsCommand()\n    elif debugger_type == DebuggerType.LLDB:\n        LldbCoroBacktraceCommand.register_lldb_command(debugger, __name__)\n        LldbCoroAsyncStackRootsCommand.register_lldb_command(debugger, __name__)\n    else:\n        pass\n\n\ndef __lldb_init_module(debugger, internal_dict) -> None:\n    \"\"\"\n    This function will be invoked automatically by lldb when we run:\n\n    command script import <path>\n\n    debugger will be of type lldb.SBDebugger\n    \"\"\"\n    load(debugger)\n\n\nif __name__ == \"__main__\":\n    load()\n"
  },
  {
    "path": "folly/coro/scripts/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:python_unittest.bzl\", \"python_unittest\")\nload(\"@fbsource//tools/build_defs:fb_python_test.bzl\", \"fb_python_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = fb_python_test,\n    name = \"co_bt\",\n    srcs = [\"co_bt.py\"],\n    deps = [\n        \"//xplat/folly/coro/scripts:co_bt\",\n    ],\n)\n\n# !!!! fbcode/folly/coro/scripts/test/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = python_unittest,\n    name = \"co_bt\",\n    srcs = [\"co_bt.py\"],\n    deps = [\n        \"//folly/coro/scripts:co_bt\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/scripts/test/co_bt.py",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# pyre-unsafe\n\n\nimport sys\nimport unittest\n\n\n# mock lldb module\nclass Lldb:\n    class Command:\n        pass\n\n    class SBExecutionContext:\n        pass\n\n    class SBValue:\n        value: str\n\n        def __init__(self, value: str) -> None:\n            self.value = value\n\n    def parse_and_eval(self, b):\n        return self.SBValue(b)\n\n\nclass CoBt(unittest.TestCase):\n    def setUp(self) -> None:\n        # pyre-fixme[6]: For 2nd argument expected `ModuleType` but got `Lldb`.\n        sys.modules[\"lldb\"] = Lldb()\n\n    def test_null_eq(self) -> None:\n        from .. import co_bt\n\n        null1 = co_bt.LldbValue(Lldb.SBValue(\"0x0\"))\n        null2 = co_bt.LldbValue(Lldb.SBValue(\"0x0\"))\n        self.assertEqual(null1, null2)\n"
  },
  {
    "path": "folly/coro/test/AccumulateTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/Accumulate.h>\n#include <folly/coro/BlockingWait.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nnamespace {\n\nAsyncGenerator<int> generateInts(int begin, int end) {\n  for (int i = begin; i < end; i++) {\n    co_await co_reschedule_on_current_executor;\n    co_yield i;\n  }\n}\n\n} // namespace\n\nclass AccumulateTest : public testing::Test {};\n\nTEST_F(AccumulateTest, NoOperationProvided) {\n  auto result = blockingWait(accumulate(generateInts(0, 5), 0));\n  auto expected = 0 + 1 + 2 + 3 + 4;\n\n  EXPECT_EQ(result, expected);\n}\n\nTEST_F(AccumulateTest, OperationProvided) {\n  auto result =\n      blockingWait(accumulate(generateInts(1, 5), 1, std::multiplies{}));\n  auto expected = 1 * 2 * 3 * 4;\n\n  EXPECT_EQ(result, expected);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/AsyncGeneratorBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Generator.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/ViaIfAsync.h>\n\n#include <folly/ExceptionWrapper.h>\n\n#include <exception>\n\n#if FOLLY_HAS_COROUTINES\n\nstruct SomeError : std::exception {};\n\nBENCHMARK(asyncGeneratorThrowError, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      auto gen = []() -> folly::coro::AsyncGenerator<int> {\n        co_yield 42;\n        throw SomeError{};\n      }();\n\n      auto item1 = co_await gen.next();\n      try {\n        auto item2 = co_await gen.next();\n        std::terminate();\n      } catch (const SomeError&) {\n      }\n    }\n  }());\n}\n\nBENCHMARK(asyncGeneratorThrowErrorAwaitTry, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      auto gen = []() -> folly::coro::AsyncGenerator<int> {\n        co_yield 42;\n        throw SomeError{};\n      }();\n\n      auto try1 = co_await folly::coro::co_awaitTry(gen.next());\n      auto try2 = co_await folly::coro::co_awaitTry(gen.next());\n      if (!try2.hasException() ||\n          !try2.exception().is_compatible_with<SomeError>()) {\n        std::terminate();\n      }\n    }\n  }());\n}\n\nBENCHMARK(asyncGeneratorYieldError, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      auto gen = []() -> folly::coro::AsyncGenerator<int> {\n        co_yield 42;\n        co_yield folly::coro::co_error(SomeError{});\n      }();\n\n      auto item1 = co_await gen.next();\n      try {\n        auto item2 = co_await gen.next();\n        std::terminate();\n      } catch (const SomeError&) {\n      }\n    }\n  }());\n}\n\nBENCHMARK(asyncGeneratorYieldErrorAwaitTry, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      auto gen = []() -> folly::coro::AsyncGenerator<int> {\n        co_yield 42;\n        co_yield folly::coro::co_error(SomeError{});\n      }();\n\n      auto try1 = co_await folly::coro::co_awaitTry(gen.next());\n      auto try2 = co_await folly::coro::co_awaitTry(gen.next());\n      if (!try2.hasException() ||\n          !try2.exception().is_compatible_with<SomeError>()) {\n        std::terminate();\n      }\n    }\n  }());\n}\n\n/*\nComparing just the \"hot paths\" of the two generator coroutines:\n\n11/08/23 16:25$ buck2 run @mode/opt \\\n  //folly/coro/test:async_generator_bench -- \\\n    -bm_regex '.*YieldValue.*'\n============================================================================\n[...]coro/test/AsyncGeneratorBenchmark.cpp     relative  time/iter   iters/s\n============================================================================\nasyncGeneratorYieldValues                                  12.00ns    83.32M\ncompareToSynchronousGeneratorYieldValues                    4.43ns   225.72M\n*/\n\nBENCHMARK(asyncGeneratorYieldValues, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = [](size_t iters) -> folly::coro::AsyncGenerator<size_t> {\n      for (size_t iter = 0; iter < iters; ++iter) {\n        co_yield iter;\n      }\n    }(iters);\n    size_t i = 0;\n    while (auto it = co_await gen.next()) {\n      CHECK_EQ(i++, *it);\n    }\n  }());\n}\n\nBENCHMARK(compareToSynchronousGeneratorYieldValues, iters) {\n  auto gen = [](size_t iters) -> folly::coro::Generator<size_t> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      co_yield iter;\n    }\n  }(iters);\n  size_t i = 0;\n  for (size_t iter : gen) {\n    CHECK_EQ(i++, iter);\n  }\n}\n\n#endif\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/AsyncGeneratorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/ScopeGuard.h>\n#include <folly/Traits.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/ValueOrError.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/coro/safe/NowTask.h>\n#include <folly/futures/Future.h>\n\n#include <folly/portability/GTest.h>\n#include <folly/portability/PThread.h>\n\n#include <chrono>\n#include <map>\n#include <string>\n#include <tuple>\n\n#if FOLLY_HAS_COROUTINES\n\nconstexpr bool check_for_size_regressions() {\n  using namespace folly::coro;\n  namespace detail = folly::coro::detail;\n\n  static_assert(sizeof(AsyncGenerator<int&>) == sizeof(void*));\n\n  // Prevent size regressions due to member or base ordering\n  constexpr size_t promiseSize =\n      // From AsyncGeneratorPromise:\n      sizeof(ExtendedCoroutineHandle) + sizeof(folly::AsyncStackFrame) +\n      sizeof(folly::Executor::KeepAlive<>) + sizeof(folly::CancellationToken) +\n      // The value/error union\n      sizeof(folly::exception_wrapper) +\n      // state_, hasCancelTokenOverride_ and bypassExceptionThrowing_ together:\n      sizeof(void*) +\n      // From ExtendedCoroutinePromiseCrtp:\n      sizeof(ExtendedCoroutineHandle::PromiseBase);\n  static_assert(\n      sizeof(detail::AsyncGeneratorPromise<int&, int>) == promiseSize);\n\n  return true;\n}\nstatic_assert(check_for_size_regressions());\n\nclass AsyncGeneratorTest : public testing::Test {};\n\nTEST_F(AsyncGeneratorTest, DefaultConstructedGeneratorIsEmpty) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncGenerator<int> g;\n    auto result = co_await g.next();\n    CHECK(!result);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, GeneratorDestroyedBeforeCallingBegin) {\n  bool started = false;\n  auto makeGenerator = [&]() -> folly::coro::AsyncGenerator<int> {\n    started = true;\n    co_return;\n  };\n\n  {\n    auto gen = makeGenerator();\n    (void)gen;\n  }\n\n  CHECK(!started);\n}\n\nTEST_F(AsyncGeneratorTest, PartiallyConsumingSequenceDestroysObjectsInScope) {\n  bool started = false;\n  bool destroyed = false;\n  auto makeGenerator = [&]() -> folly::coro::AsyncGenerator<int> {\n    SCOPE_EXIT {\n      destroyed = true;\n    };\n    started = true;\n    co_yield 1;\n    co_yield 2;\n    co_return;\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    {\n      auto gen = makeGenerator();\n      CHECK(!started);\n      CHECK(!destroyed);\n      auto result = co_await gen.next();\n      CHECK(started);\n      CHECK(!destroyed);\n      CHECK(result);\n      CHECK_EQ(1, *result);\n    }\n    CHECK(destroyed);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, FullyConsumeSequence) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<int> {\n    for (int i = 0; i < 4; ++i) {\n      co_yield i;\n    }\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n    CHECK(result);\n    CHECK_EQ(0, *result);\n    result = co_await gen.next();\n    CHECK(result);\n    CHECK_EQ(1, *result);\n    result = co_await gen.next();\n    CHECK(result);\n    CHECK_EQ(2, *result);\n    result = co_await gen.next();\n    CHECK(result);\n    CHECK_EQ(3, *result);\n    result = co_await gen.next();\n    CHECK(!result);\n  }());\n}\n\nnamespace {\nstruct SomeError : std::exception {};\n} // namespace\n\nTEST_F(AsyncGeneratorTest, ThrowExceptionBeforeFirstYield) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<int> {\n    if (true) {\n      throw SomeError{};\n    }\n    co_return;\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    bool caughtException = false;\n    try {\n      (void)co_await gen.next();\n      CHECK(false);\n    } catch (const SomeError&) {\n      caughtException = true;\n    }\n    CHECK(caughtException);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, ThrowExceptionAfterFirstYield) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<int> {\n    co_yield 42;\n    throw SomeError{};\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n    CHECK(result);\n    CHECK_EQ(42, *result);\n    bool caughtException = false;\n    try {\n      (void)co_await gen.next();\n      CHECK(false);\n    } catch (const SomeError&) {\n      caughtException = true;\n    }\n    CHECK(caughtException);\n  }());\n}\n\nTEST_F(\n    AsyncGeneratorTest, ConsumingManySynchronousElementsDoesNotOverflowStack) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<std::uint64_t> {\n    for (std::uint64_t i = 0; i < 1'000'000; ++i) {\n      co_yield i;\n    }\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    std::uint64_t sum = 0;\n    while (auto result = co_await gen.next()) {\n      sum += *result;\n    }\n    CHECK_EQ(499999500000u, sum);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, ProduceResultsAsynchronously) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    folly::Executor* executor = co_await folly::coro::co_current_executor;\n    auto makeGenerator = [&]() -> folly::coro::AsyncGenerator<int> {\n      using namespace std::literals::chrono_literals;\n      CHECK_EQ(executor, co_await folly::coro::co_current_executor);\n      co_await folly::coro::sleep(1ms);\n      CHECK_EQ(executor, co_await folly::coro::co_current_executor);\n      co_yield 1;\n      CHECK_EQ(executor, co_await folly::coro::co_current_executor);\n      co_await folly::coro::sleep(1ms);\n      CHECK_EQ(executor, co_await folly::coro::co_current_executor);\n      co_yield 2;\n      CHECK_EQ(executor, co_await folly::coro::co_current_executor);\n      co_await folly::coro::sleep(1ms);\n      CHECK_EQ(executor, co_await folly::coro::co_current_executor);\n    };\n\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n    CHECK_EQ(1, *result);\n    result = co_await gen.next();\n    CHECK_EQ(2, *result);\n    result = co_await gen.next();\n    CHECK(!result);\n  }());\n}\n\nstruct ConvertibleToIntReference {\n  int value;\n  operator int&() { return value; }\n};\n\nTEST_F(AsyncGeneratorTest, GeneratorOfLValueReference) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<int&> {\n    int value = 10;\n    co_yield value;\n    // Consumer gets a mutable reference to the value and can modify it.\n    CHECK_EQ(20, value);\n\n    // NOTE: Not allowed to yield an rvalue from an AsyncGenerator<T&>?\n    // co_yield 30;  // Compile-error\n\n    co_yield ConvertibleToIntReference{30};\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n    CHECK_EQ(10, result.value());\n    *result = 20;\n    result = co_await gen.next();\n    CHECK_EQ(30, result.value());\n    result = co_await gen.next();\n    CHECK(!result.has_value());\n  }());\n}\n\nstruct ConvertibleToInt {\n  operator int() const { return 99; }\n};\n\nTEST_F(AsyncGeneratorTest, GeneratorOfConstLValueReference) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<const int&> {\n    int value = 10;\n    co_yield value;\n    // Consumer gets a const reference to the value.\n\n    // Allowed to yield an rvalue from an AsyncGenerator<const T&>.\n    co_yield 30;\n\n    co_yield ConvertibleToInt{};\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n    CHECK_EQ(10, *result);\n    result = co_await gen.next();\n    CHECK_EQ(30, *result);\n    result = co_await gen.next();\n    CHECK_EQ(99, *result);\n    result = co_await gen.next();\n    CHECK(!result);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, GeneratorOfRValueReference) {\n  auto makeGenerator =\n      []() -> folly::coro::AsyncGenerator<std::unique_ptr<int>&&> {\n    co_yield std::make_unique<int>(10);\n\n    auto ptr = std::make_unique<int>(20);\n    co_yield std::move(ptr);\n    CHECK(ptr == nullptr);\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n\n    auto result = co_await gen.next();\n    CHECK_EQ(10, **result);\n    // Don't move it to a local var.\n\n    result = co_await gen.next();\n    CHECK_EQ(20, **result);\n    auto ptr = *result; // Move it to a local var.\n\n    result = co_await gen.next();\n    CHECK(!result);\n  }());\n}\n\nstruct MoveOnly {\n  explicit MoveOnly(int value) : value_(value) {}\n  MoveOnly(MoveOnly&& other) noexcept\n      : value_(std::exchange(other.value_, -1)) {}\n  ~MoveOnly() {}\n  MoveOnly& operator=(MoveOnly&&) = delete;\n  int value() const { return value_; }\n\n private:\n  int value_;\n};\n\nTEST_F(AsyncGeneratorTest, GeneratorOfMoveOnlyType) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<MoveOnly> {\n    MoveOnly rvalue(1);\n    co_yield std::move(rvalue);\n    CHECK_EQ(-1, rvalue.value());\n\n    co_yield MoveOnly(2);\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n\n    // NOTE: It's an error to dereference using '*it' as this returns a copy\n    // of the iterator's reference type, which in this case is 'MoveOnly'.\n    CHECK_EQ(1, result->value());\n\n    result = co_await gen.next();\n    CHECK_EQ(2, result->value());\n\n    result = co_await gen.next();\n    CHECK(!result);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, GeneratorOfConstValue) {\n  auto makeGenerator = []() -> folly::coro::AsyncGenerator<const int> {\n    // OK to yield prvalue\n    co_yield 42;\n\n    // OK to yield lvalue\n    int x = 123;\n    co_yield x;\n\n    co_yield ConvertibleToInt{};\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n    CHECK_EQ(42, *result);\n    static_assert(std::is_same_v<decltype(*result), const int&>);\n    result = co_await gen.next();\n    CHECK_EQ(123, *result);\n    result = co_await gen.next();\n    CHECK_EQ(99, *result);\n    result = co_await gen.next();\n    CHECK(!result);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, ExplicitValueType) {\n  std::map<std::string, std::string> items;\n  items[\"foo\"] = \"hello\";\n  items[\"bar\"] = \"goodbye\";\n\n  auto makeGenerator = [&]()\n      -> folly::coro::AsyncGenerator<\n          std::tuple<const std::string&, std::string&>,\n          std::tuple<std::string, std::string>> {\n    for (auto& [k, v] : items) {\n      co_yield {k, v};\n    }\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = makeGenerator();\n    auto result = co_await gen.next();\n    {\n      auto [kRef, vRef] = *result;\n      CHECK_EQ(\"bar\", kRef);\n      CHECK_EQ(\"goodbye\", vRef);\n      decltype(gen)::value_type copy = *result;\n      vRef = \"au revoir\";\n      CHECK_EQ(\"goodbye\", std::get<1>(copy));\n      CHECK_EQ(\"au revoir\", std::get<1>(*result));\n    }\n  }());\n\n  CHECK_EQ(\"au revoir\", items[\"bar\"]);\n}\n\nTEST_F(AsyncGeneratorTest, InvokeLambda) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto ptr = std::make_unique<int>(123);\n    auto gen = folly::coro::co_invoke(\n        [p = std::move(ptr), str = std::string(\"test\")]() mutable\n            -> folly::coro::AsyncGenerator<std::unique_ptr<int>&&> {\n          SCOPE_EXIT {\n            CHECK_EQ(str, \"test\");\n          };\n          co_yield std::move(p);\n        });\n\n    auto result = co_await gen.next();\n    CHECK(result);\n    ptr = *result;\n    CHECK(ptr);\n    CHECK(*ptr == 123);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, InvokeLambdaRequiresCleanup) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto ptr = std::make_unique<int>(123);\n    auto gen = folly::coro::co_invoke(\n        [p = std::move(ptr), str = std::string(\"test\")]() mutable\n            -> folly::coro::CleanableAsyncGenerator<std::unique_ptr<int>&&> {\n          SCOPE_EXIT {\n            CHECK_EQ(str, \"test\");\n          };\n          co_yield std::move(p);\n        });\n\n    auto result = co_await gen.next();\n    CHECK(result);\n    ptr = *result;\n    CHECK(ptr);\n    CHECK(*ptr == 123);\n    co_await std::move(gen).cleanup();\n  }());\n}\n\ntemplate <typename Ref, typename Value = folly::remove_cvref_t<Ref>>\nfolly::coro::AsyncGenerator<Ref, Value> neverStream() {\n  folly::coro::Baton baton;\n  folly::CancellationCallback cb{\n      co_await folly::coro::co_current_cancellation_token,\n      [&] { baton.post(); }};\n  co_await baton;\n}\n\nTEST_F(AsyncGeneratorTest, CancellationTokenPropagatesFromConsumer) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    bool suspended = false;\n    bool done = false;\n    co_await folly::coro::collectAll(\n        folly::coro::co_withCancellation(\n            cancelSource.getToken(),\n            [&]() -> folly::coro::Task<void> {\n              auto stream = neverStream<int>();\n              suspended = true;\n              auto result = co_await stream.next();\n              CHECK(!result.has_value());\n              done = true;\n            }()),\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          CHECK(suspended);\n          CHECK(!done);\n          cancelSource.requestCancellation();\n        }());\n    CHECK(done);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, BlockingWaitOnFinalNextDoesNotDeadlock) {\n  auto gen = []() -> folly::coro::AsyncGenerator<int> { co_yield 42; };\n\n  auto g = gen();\n  auto val1 = folly::coro::blockingWait(g.next());\n  CHECK_EQ(42, val1.value());\n  auto val2 = folly::coro::blockingWait(g.next());\n  CHECK(!val2.has_value());\n}\n\nTEST_F(AsyncGeneratorTest, BlockingWaitOnThrowingFinalNextDoesNotDeadlock) {\n  auto gen = []() -> folly::coro::AsyncGenerator<int> {\n    co_yield 42;\n    throw SomeError{};\n  };\n\n  auto g = gen();\n  auto val1 = folly::coro::blockingWait(g.next());\n  CHECK_EQ(42, val1.value());\n  try {\n    folly::coro::blockingWait(g.next());\n    CHECK(false);\n  } catch (const SomeError&) {\n  }\n}\n\nfolly::coro::AsyncGenerator<size_t> sizeRange(size_t from, size_t to) {\n  for (size_t i = from; i < to; ++i) {\n    co_yield i;\n  }\n}\n\nTEST_F(AsyncGeneratorTest, SymmetricTransfer) {\n  // 512KiB is larger than we need, technically.  It comes from MacOS's\n  // default stack size for secondary threads.  The superstition behind\n  // using a realistic size is that even if someone stubs out `setstacksize`\n  // to be a no-op for a platform, we still have a chance of catching bugs.\n  constexpr size_t kStackSize = 1 << 19;\n  auto testFn = [](void* numItersPtr) -> void* {\n    size_t numIters = *reinterpret_cast<size_t*>(numItersPtr);\n    size_t sum = 0;\n    folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n      auto g = sizeRange(1, numIters + 1);\n      int* prevAddress = nullptr;\n      while (auto result = co_await g.next()) {\n        [[maybe_unused]] int stackVariable = 0;\n        if (!prevAddress) {\n          prevAddress = &stackVariable;\n        } else {\n          // If symmetric transfer is broken, this should fire before we\n          // overflow the stack. `EXPECT_EQ` hangs on Mac / Rosetta.\n          CHECK_EQ(prevAddress, &stackVariable);\n        }\n        sum += *result;\n      }\n      CHECK_EQ((numIters + 1) * numIters / 2, sum);\n    }());\n    static_assert(sizeof(size_t) <= sizeof(void*));\n    return reinterpret_cast<void*>(sum);\n  };\n  pthread_attr_t attr;\n  ASSERT_EQ(0, pthread_attr_init(&attr));\n  ASSERT_EQ(0, pthread_attr_setstacksize(&attr, kStackSize));\n  pthread_t thread;\n  // The goal is to overflow the stack with even 1 byte per item\n  size_t numIters = kStackSize + 1;\n  ASSERT_EQ(0, pthread_create(&thread, &attr, testFn, &numIters));\n  void* sumAsPtr;\n  ASSERT_EQ(0, pthread_join(thread, &sumAsPtr));\n  // Redundant with above, but ensures the thread actually ran!\n  EXPECT_EQ((numIters + 1) * numIters / 2, reinterpret_cast<size_t>(sumAsPtr));\n}\n\nTEST(AsyncGenerator, YieldCoError) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto gen = []() -> folly::coro::AsyncGenerator<std::string> {\n      co_yield \"foo\";\n      co_yield \"bar\";\n      co_yield folly::coro::co_error(SomeError{});\n      // not enforced in opt mode yet so this code is reached in the\n      // EXPECT_DEBUG_DEATH line\n      if (folly::kIsDebug) {\n        ADD_FAILURE();\n      }\n    }();\n\n    auto item1 = co_await gen.next();\n    CHECK(item1.value() == \"foo\");\n    auto item2 = co_await gen.next();\n    CHECK(item2.value() == \"bar\");\n\n    try {\n      (void)co_await gen.next();\n      ADD_FAILURE();\n    } catch (const SomeError&) {\n    }\n\n    EXPECT_DEATH(\n        co_await gen.next(), \"Using generator after receiving exception.\");\n  }());\n}\n\nTEST(AsyncGenerator, YieldCoResult) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto gen = []() -> folly::coro::AsyncGenerator<std::string> {\n      co_yield folly::coro::co_result(folly::Try<std::string>(\"foo\"));\n      co_yield folly::coro::co_result(folly::Try<std::string>(\"bar\"));\n      co_yield folly::coro::co_result(folly::Try<std::string>(SomeError{}));\n      ADD_FAILURE();\n    }();\n\n    auto item1 = co_await gen.next();\n    CHECK(item1.value() == \"foo\");\n    auto item2 = co_await gen.next();\n    CHECK(item2.value() == \"bar\");\n\n    EXPECT_THROW(co_await gen.next(), SomeError);\n\n    gen = []() -> folly::coro::AsyncGenerator<std::string> {\n      co_yield folly::coro::co_result(folly::Try<std::string>(\"foo\"));\n      co_yield folly::coro::co_result(folly::Try<std::string>(\"bar\"));\n      co_yield folly::coro::co_result(folly::Try<std::string>());\n      ADD_FAILURE();\n    }();\n\n    item1 = co_await gen.next();\n    CHECK(item1.value() == \"foo\");\n    item2 = co_await gen.next();\n    CHECK(item2.value() == \"bar\");\n    EXPECT_FALSE(co_await gen.next());\n  }());\n}\n\nTEST(AsyncGenerator, CoResultProxyExample) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto makeProxy = [](folly::coro::AsyncGenerator<int> gen)\n        -> folly::coro::AsyncGenerator<int> {\n      while (true) {\n        auto t = co_await folly::coro::co_awaitTry(gen.next());\n        co_yield folly::coro::co_result(std::move(t));\n      }\n    };\n\n    auto gen = []() -> folly::coro::AsyncGenerator<int> {\n      for (int i = 0; i < 5; ++i) {\n        co_yield i;\n      }\n    }();\n\n    auto proxy = makeProxy(std::move(gen));\n\n    for (int i = 0; i < 5; ++i) {\n      auto val = co_await proxy.next();\n      EXPECT_EQ(*val, i);\n    }\n    EXPECT_FALSE(co_await proxy.next());\n\n    gen = []() -> folly::coro::AsyncGenerator<int> {\n      for (int i = 0; i < 5; ++i) {\n        co_yield i;\n      }\n      throw SomeError();\n    }();\n\n    proxy = makeProxy(std::move(gen));\n\n    for (int i = 0; i < 5; ++i) {\n      auto val = co_await proxy.next();\n      EXPECT_EQ(*val, i);\n    }\n    EXPECT_THROW(co_await proxy.next(), SomeError);\n  }());\n}\n\nTEST(AsyncGenerator, CoAwaitTry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto gen = []() -> folly::coro::AsyncGenerator<std::string> {\n      co_yield \"foo\";\n      co_yield \"bar\";\n      co_yield folly::coro::co_error(SomeError{});\n      CHECK(false);\n    }();\n\n    auto item1 = co_await folly::coro::co_awaitTry(gen.next());\n    CHECK(item1.hasValue());\n    CHECK(*item1.value() == \"foo\");\n    auto item2 = co_await folly::coro::co_awaitTry(gen.next());\n    CHECK(item2.hasValue());\n    CHECK(*item2.value() == \"bar\");\n    auto item3 = co_await folly::coro::co_awaitTry(gen.next());\n    CHECK(item3.hasException());\n    CHECK(item3.exception().is_compatible_with<SomeError>());\n  }());\n}\n\nTEST(AsyncGenerator, CoAwaitValueOrError) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto gen = []() -> folly::coro::AsyncGenerator<std::string> {\n      co_yield \"foo\";\n      co_yield \"bar\";\n      co_yield folly::coro::co_error(SomeError{});\n      CHECK(false);\n    }();\n\n    auto item1 = co_await folly::coro::value_or_error_or_stopped(gen.next());\n    CHECK(item1.has_value());\n    CHECK(*item1.value_or_throw() == \"foo\");\n    auto item2 = co_await folly::coro::value_or_error_or_stopped(gen.next());\n    CHECK(item2.has_value());\n    CHECK(*item2.value_or_throw() == \"bar\");\n    auto item3 = co_await folly::coro::value_or_error_or_stopped(gen.next());\n    CHECK(!item3.has_value() && !item3.has_stopped());\n    CHECK(folly::get_exception<SomeError>(item3));\n  }());\n}\n\n#if FOLLY_HAS_RESULT\n// Test that value_or_error() auto-propagates OperationCancelled (stops the\n// consuming coroutine) when an AsyncGenerator yields co_stopped_may_throw.\nCO_TEST(AsyncGenerator, ValueOrErrorPropagatesStopped) {\n  using namespace folly::coro;\n\n  auto stoppedGen = []() -> AsyncGenerator<int> {\n    co_yield 1;\n    co_yield co_stopped_may_throw;\n    ADD_FAILURE() << \"Generator should not continue after co_stopped_may_throw\";\n  };\n\n  int firstItem = 0;\n  auto consumer = [&]() -> now_task<> {\n    auto gen = stoppedGen();\n    auto r1 = co_await value_or_error(gen.next());\n    firstItem = *r1.value_or_throw();\n    // This should propagate OperationCancelled, stopping this task\n    (void)co_await value_or_error(gen.next());\n    ADD_FAILURE();\n  };\n\n  auto res = co_await value_or_error_or_stopped(consumer());\n  EXPECT_EQ(1, firstItem);\n  EXPECT_TRUE(res.has_stopped());\n}\n#endif\n\nTEST(AsyncGenerator, SafePoint) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    enum class step_type {\n      init,\n      before_continue_sp,\n      after_continue_sp,\n      before_cancel_sp,\n      after_cancel_sp,\n    };\n    step_type step = step_type::init;\n\n    folly::CancellationSource cancelSrc;\n    auto gen =\n        folly::coro::co_invoke([&]() -> folly::coro::AsyncGenerator<int> {\n          step = step_type::before_continue_sp;\n          co_await folly::coro::co_safe_point;\n          step = step_type::after_continue_sp;\n\n          cancelSrc.requestCancellation();\n\n          step = step_type::before_cancel_sp;\n          co_await folly::coro::co_safe_point;\n          step = step_type::after_cancel_sp;\n        });\n\n    auto result = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(cancelSrc.getToken(), gen.next()));\n    EXPECT_THROW(result.value(), folly::OperationCancelled);\n    EXPECT_EQ(step_type::before_cancel_sp, step);\n  }());\n}\n\nTEST_F(AsyncGeneratorTest, NextAfterCancel) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSrc;\n    auto gen =\n        folly::coro::co_invoke([&]() -> folly::coro::AsyncGenerator<int> {\n          co_yield 1;\n          co_yield 2;\n          co_await folly::coro::co_safe_point;\n          co_yield 3;\n        });\n\n    auto result = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(cancelSrc.getToken(), gen.next()));\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(1, *result.value());\n\n    cancelSrc.requestCancellation();\n    result = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(cancelSrc.getToken(), gen.next()));\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(2, *result.value());\n\n    result = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(cancelSrc.getToken(), gen.next()));\n    EXPECT_TRUE(result.hasException());\n    EXPECT_THROW(result.value(), folly::OperationCancelled);\n  }());\n}\n\nTEST(AsyncGenerator, CoAwaitNothrow) {\n  auto res =\n      folly::coro::blockingWait(co_awaitTry([]() -> folly::coro::Task<void> {\n        auto gen = []() -> folly::coro::AsyncGenerator<int> {\n          auto inner = []() -> folly::coro::AsyncGenerator<int> {\n            co_yield 42;\n            throw std::runtime_error(\"\");\n          }();\n          co_yield *co_await co_nothrow(inner.next());\n          try {\n            co_yield *co_await co_nothrow(inner.next());\n          } catch (...) {\n            ADD_FAILURE();\n          }\n          ADD_FAILURE();\n        }();\n\n        co_await co_nothrow(gen.next());\n        try {\n          co_await co_nothrow(gen.next());\n        } catch (...) {\n          ADD_FAILURE();\n        }\n        ADD_FAILURE();\n      }()));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nTEST(AsyncGenerator, CoAwaitNothrowMultiTask) {\n  auto res =\n      folly::coro::blockingWait(co_awaitTry([]() -> folly::coro::Task<void> {\n        auto gen = co_await\n            []() -> folly::coro::Task<folly::coro::AsyncGenerator<int>> {\n          auto gen = []() -> folly::coro::AsyncGenerator<int> {\n            co_yield 42;\n            throw std::runtime_error(\"\");\n          }();\n\n          co_await co_nothrow(gen.next());\n          co_return gen;\n        }();\n\n        try {\n          co_await co_nothrow(gen.next());\n        } catch (...) {\n          ADD_FAILURE();\n        }\n        ADD_FAILURE();\n      }()));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/AsyncPipeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/AsyncPipe.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n\n#include <folly/portability/GTest.h>\n\n#include <string>\n\n#if FOLLY_HAS_COROUTINES\n\nTEST(AsyncPipeTest, PublishConsume) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  for (int i = 0; i < 5; ++i) {\n    EXPECT_TRUE(pipe.second.write(i));\n  }\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int i = 0; i < 5; ++i) {\n      auto val = co_await pipe.first.next();\n      EXPECT_TRUE(val);\n      EXPECT_EQ(*val, i);\n    }\n  }());\n}\n\nTEST(AsyncPipeTest, PublishLRValue) {\n  auto pipe = folly::coro::AsyncPipe<std::string>::create();\n  constexpr auto val = \"a string\";\n  std::string val1 = val;\n  EXPECT_TRUE(pipe.second.write(val1));\n  EXPECT_TRUE(pipe.second.write(std::move(val1)));\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto gen = std::move(pipe.first);\n    for (int i = 0; i < 2; ++i) {\n      auto val2 = co_await gen.next();\n      EXPECT_TRUE(val2);\n      EXPECT_EQ(*val2, val);\n    }\n  }());\n}\n\nTEST(AsyncPipeTest, PublishConsumeClose) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  for (int i = 0; i < 5; ++i) {\n    EXPECT_TRUE(pipe.second.write(i));\n  }\n  std::move(pipe.second).close();\n  EXPECT_FALSE(pipe.second.write(0));\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int i = 0; i < 5; ++i) {\n      auto val = co_await pipe.first.next();\n      EXPECT_TRUE(val);\n      EXPECT_EQ(*val, i);\n    }\n    auto val = co_await pipe.first.next();\n    EXPECT_FALSE(val);\n  }());\n}\n\nTEST(AsyncPipeTest, PublishConsumeError) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  for (int i = 0; i < 5; ++i) {\n    EXPECT_TRUE(pipe.second.write(i));\n  }\n  std::move(pipe.second).close(std::runtime_error(\"\"));\n  EXPECT_FALSE(pipe.second.write(0));\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int i = 0; i < 5; ++i) {\n      auto val = co_await pipe.first.next();\n      EXPECT_TRUE(val);\n      EXPECT_EQ(*val, i);\n    }\n    EXPECT_THROW({ co_await pipe.first.next(); }, std::runtime_error);\n  }());\n}\n\nTEST(AsyncPipeTest, PublishConsumeDestroy) {\n  folly::coro::AsyncGenerator<int&&> gen;\n  {\n    auto pipe = folly::coro::AsyncPipe<int>::create();\n    for (int i = 0; i < 5; ++i) {\n      EXPECT_TRUE(pipe.second.write(i));\n    }\n    gen = std::move(pipe.first);\n  }\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int i = 0; i < 5; ++i) {\n      auto val = co_await gen.next();\n      EXPECT_TRUE(val);\n      EXPECT_EQ(*val, i);\n    }\n    auto val = co_await gen.next();\n    EXPECT_FALSE(val);\n  }());\n}\n\nTEST(AsyncPipeTest, PublishConsumeWithMoves) {\n  auto [generator, pipe1] = folly::coro::AsyncPipe<int>::create();\n  for (int i = 0; i < 2; ++i) {\n    EXPECT_TRUE(pipe1.write(i));\n  }\n  // Move constructor\n  auto pipe2 = std::move(pipe1);\n  for (int i = 2; i < 4; ++i) {\n    EXPECT_TRUE(pipe2.write(i));\n  }\n  // Move assignment (optional forces the assignment)\n  std::optional<folly::coro::AsyncPipe<int>> pipe3;\n  pipe3 = std::move(pipe2);\n  for (int i = 4; i < 6; ++i) {\n    EXPECT_TRUE(pipe3->write(i));\n  }\n  // Should still read all values\n  folly::coro::blockingWait(\n      [generator_2 =\n           std::move(generator)]() mutable -> folly::coro::Task<void> {\n        for (int i = 0; i < 6; ++i) {\n          auto val = co_await generator_2.next();\n          EXPECT_TRUE(val);\n          EXPECT_EQ(*val, i);\n        }\n      }());\n}\n\nTEST(AsyncPipeTest, BrokenPipe) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  EXPECT_TRUE(pipe.second.write(0));\n  {\n    auto gen = std::move(pipe.first);\n  }\n  EXPECT_FALSE(pipe.second.write(0));\n  std::move(pipe.second).close();\n}\n\nTEST(AsyncPipeTest, IsClosed) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  EXPECT_FALSE(pipe.second.isClosed());\n  {\n    auto gen = std::move(pipe.first);\n  }\n  EXPECT_TRUE(pipe.second.isClosed());\n  std::move(pipe.second).close();\n}\n\nTEST(AsyncPipeTest, WriteWhileBlocking) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  folly::ManualExecutor ex;\n\n  auto fut =\n      co_withExecutor(\n          &ex,\n          folly::coro::co_invoke(\n              [&]() -> folly::coro::Task<\n                        folly::coro::AsyncGenerator<int&&>::NextResult> {\n                co_return co_await pipe.first.next();\n              }))\n          .start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n\n  EXPECT_TRUE(pipe.second.write(0));\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_EQ(*std::move(fut).get(), 0);\n}\n\nTEST(AsyncPipeTest, CloseWhileBlocking) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  folly::ManualExecutor ex;\n\n  auto fut =\n      co_withExecutor(\n          &ex,\n          folly::coro::co_invoke(\n              [&]() -> folly::coro::Task<\n                        folly::coro::AsyncGenerator<int&&>::NextResult> {\n                co_return co_await pipe.first.next();\n              }))\n          .start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n\n  std::move(pipe.second).close();\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_FALSE(std::move(fut).get());\n}\n\nTEST(AsyncPipeTest, DestroyWhileBlocking) {\n  auto pipe = folly::coro::AsyncPipe<int>::create();\n  folly::ManualExecutor ex;\n\n  auto fut =\n      co_withExecutor(\n          &ex,\n          folly::coro::co_invoke(\n              [&]() -> folly::coro::Task<\n                        folly::coro::AsyncGenerator<int&&>::NextResult> {\n                co_return co_await pipe.first.next();\n              }))\n          .start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n\n  {\n    auto pipe_ = std::move(pipe.second);\n  }\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_FALSE(std::move(fut).get());\n}\n\nTEST(AsyncPipeTest, OnClosedCallbackCalledWhenGeneratorDestroyed) {\n  auto onCloseCallbackBaton = folly::Baton<>();\n  auto pipe = folly::coro::AsyncPipe<int>::create([&]() {\n    onCloseCallbackBaton.post();\n  } /* onClosed */);\n\n  auto ex = folly::ManualExecutor();\n  auto cancellationSource = folly::CancellationSource();\n  auto fut =\n      co_withExecutor(\n          &ex,\n          folly::coro::co_withCancellation(\n              cancellationSource.getToken(),\n              folly::coro::co_invoke(\n                  [gen = std::move(pipe.first)]() mutable\n                      -> folly::coro::Task<\n                          folly::coro::AsyncGenerator<int&&>::NextResult> {\n                    co_return co_await gen.next();\n                  })))\n          .start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n  EXPECT_FALSE(onCloseCallbackBaton.ready());\n\n  cancellationSource.requestCancellation();\n\n  ex.drain();\n  EXPECT_TRUE(onCloseCallbackBaton.ready());\n}\n\nTEST(AsyncPipeTest, OnClosedCallbackCalledWhenPublisherClosesPipe) {\n  auto onCloseCallbackBaton = folly::Baton<>();\n  auto pipe = folly::coro::AsyncPipe<int>::create([&]() {\n    onCloseCallbackBaton.post();\n  } /* onClosed */);\n\n  EXPECT_FALSE(onCloseCallbackBaton.ready());\n\n  std::move(pipe.second).close();\n\n  EXPECT_TRUE(onCloseCallbackBaton.ready());\n}\n\nTEST(AsyncPipeTest, OnClosedCallbackCalledWhenPublisherClosesPipeWithError) {\n  auto onCloseCallbackExecuted = folly::Promise<folly::Unit>();\n  auto pipe = folly::coro::AsyncPipe<int>::create([&]() {\n    onCloseCallbackExecuted.setValue();\n  } /* onClosed */);\n\n  EXPECT_FALSE(onCloseCallbackExecuted.isFulfilled());\n\n  std::move(pipe.second).close(std::runtime_error(\"error\"));\n\n  EXPECT_TRUE(onCloseCallbackExecuted.isFulfilled());\n}\n\nTEST(\n    AsyncPipeTest,\n    OnClosedCallbackJoinedWhenPublisherClosesPipeWhileGeneratorDestructing) {\n  auto onCloseCallbackStartedBaton = folly::Baton<>();\n  auto onCloseCallbackCompletionBaton = folly::Baton<>();\n  auto pipe = folly::coro::AsyncPipe<int>::create([&]() {\n    onCloseCallbackStartedBaton.post();\n    onCloseCallbackCompletionBaton.wait();\n  } /* onClosed */);\n\n  auto ex = folly::ManualExecutor();\n  auto cancellationSource = folly::CancellationSource();\n  auto fut =\n      co_withExecutor(\n          &ex,\n          folly::coro::co_withCancellation(\n              cancellationSource.getToken(),\n              folly::coro::co_invoke(\n                  [gen = std::move(pipe.first)]() mutable\n                      -> folly::coro::Task<\n                          folly::coro::AsyncGenerator<int&&>::NextResult> {\n                    co_return co_await gen.next();\n                  })))\n          .start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n  EXPECT_FALSE(onCloseCallbackStartedBaton.ready());\n\n  auto cancelThread = std::thread([&]() {\n    cancellationSource.requestCancellation();\n    ex.drain();\n  });\n  onCloseCallbackStartedBaton.wait();\n\n  auto pipeClosedBaton = folly::Baton<>();\n  auto closePipeThread = std::thread([&]() {\n    std::move(pipe.second).close();\n    pipeClosedBaton.post();\n  });\n  /* sleep override */\n  std::this_thread::sleep_for(std::chrono::milliseconds(100));\n\n  EXPECT_FALSE(pipeClosedBaton.ready());\n\n  onCloseCallbackCompletionBaton.post();\n  pipeClosedBaton.wait();\n  cancelThread.join();\n  closePipeThread.join();\n}\n\nTEST(AsyncPipeTest, PublisherMustCloseIfCallbackSetAndGeneratorAlive) {\n  EXPECT_DEATH(\n      ([&]() {\n        auto pipe = folly::coro::AsyncPipe<int>::create([]() {} /* onClosed */);\n      })(),\n      \"If an onClosed callback is specified and the generator still exists, \"\n      \"the publisher must explicitly close the pipe prior to destruction.\");\n\n  EXPECT_DEATH(\n      ([&]() {\n        auto pipe1 = folly::coro::AsyncPipe<int>::create([]() {\n        } /* onClosed */);\n        auto pipe2 = folly::coro::AsyncPipe<int>::create();\n        pipe1.second = std::move(pipe2.second);\n      })(),\n      \"If an onClosed callback is specified and the generator still exists, \"\n      \"the publisher must explicitly close the pipe prior to destruction.\");\n}\n\nTEST(BoundedAsyncPipeTest, PublishConsume) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 10);\n    for (int i = 0; i < 15; ++i) {\n      EXPECT_TRUE(co_await pipe.write(i));\n\n      auto item = co_await generator.next();\n      EXPECT_TRUE(item.has_value());\n      EXPECT_EQ(item.value(), i);\n    }\n    std::move(pipe).close();\n    EXPECT_FALSE(co_await generator.next());\n  }());\n}\n\nTEST(BoundedAsyncPipeTest, PipeCapacity) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 10);\n    EXPECT_EQ(pipe.getAvailableSpace(), 10);\n    EXPECT_EQ(pipe.getOccupiedSpace(), 0);\n    for (int i = 0; i < 7; ++i) {\n      EXPECT_TRUE(co_await pipe.write(i));\n    }\n    EXPECT_EQ(pipe.getAvailableSpace(), 3);\n    EXPECT_EQ(pipe.getOccupiedSpace(), 7);\n    for (int i = 0; i < 7; ++i) {\n      auto item = co_await generator.next();\n    }\n    EXPECT_EQ(pipe.getAvailableSpace(), 10);\n    EXPECT_EQ(pipe.getOccupiedSpace(), 0);\n    std::move(pipe).close();\n  }());\n}\n\nTEST(BoundedAsyncPipeTest, PublisherBlocks) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::ManualExecutor executor;\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 10);\n\n    for (size_t i = 0; i < 10; ++i) {\n      co_await pipe.write(i);\n    }\n\n    // wrap in co_invoke() here, since write() accepts arguments by reference,\n    // and temporaries may go out of scope\n    auto writeFuture =\n        co_withExecutor(\n            &executor,\n            folly::coro::co_invoke(\n                [&pipe_2 = pipe]() -> folly::coro::Task<bool> {\n                  co_return co_await pipe_2.write(20);\n                }))\n            .start();\n    executor.drain();\n    EXPECT_FALSE(writeFuture.isReady());\n\n    auto item = co_await generator.next();\n    EXPECT_TRUE(item.has_value());\n\n    executor.drain();\n    EXPECT_TRUE(writeFuture.isReady());\n  }());\n}\n\nTEST(BoundedAsyncPipeTest, IsClosed) {\n  auto [generator, pipe] =\n      folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 2);\n\n  EXPECT_FALSE(pipe.isClosed());\n  {\n    // destroy the read end\n    auto _ = std::move(generator);\n  }\n  EXPECT_TRUE(pipe.isClosed());\n}\n\nTEST(BoundedAsyncPipeTest, BlockingPublisherCanceledOnDestroy) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::ManualExecutor executor;\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 2);\n\n    for (size_t i = 0; i < 2; ++i) {\n      co_await pipe.write(i);\n    }\n\n    std::vector<folly::SemiFuture<bool>> futures;\n    for (size_t i = 0; i < 5; ++i) {\n      auto writeFuture =\n          co_withExecutor(\n              &executor,\n              folly::coro::co_invoke(\n                  [&pipe_2 = pipe]() -> folly::coro::Task<bool> {\n                    co_return co_await pipe_2.write(20);\n                  }))\n              .start();\n      executor.drain();\n      EXPECT_FALSE(writeFuture.isReady());\n      futures.emplace_back(std::move(writeFuture));\n    }\n\n    {\n      // destroy the read end\n      auto _ = std::move(generator);\n    }\n\n    executor.drain();\n    for (auto& future : futures) {\n      EXPECT_TRUE(future.isReady());\n      EXPECT_FALSE(std::move(future).get());\n    }\n  }());\n}\n\nTEST(BoundedAsyncPipeTest, PublisherFailsAfterDestroyWithRemainingTokens) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 2);\n    {\n      // destroy the read end\n      auto _ = std::move(generator);\n    }\n\n    EXPECT_FALSE(co_await pipe.write(1));\n    EXPECT_FALSE(co_await pipe.write(1));\n\n    // No tokens left, blocking path also returns false\n    EXPECT_FALSE(co_await pipe.write(1));\n    EXPECT_FALSE(co_await pipe.write(1));\n  }());\n}\n\nTEST(BoundedAsyncPipeTest, BlockingPublisherCancelsWithParent) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::ManualExecutor executor;\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 2);\n\n    for (size_t i = 0; i < 2; ++i) {\n      co_await pipe.write(i);\n    }\n\n    folly::CancellationSource cs;\n    auto future =\n        co_withExecutor(\n            &executor,\n            folly::coro::co_withCancellation(\n                cs.getToken(),\n                folly::coro::co_invoke(\n                    [&pipe_2 = pipe]() -> folly::coro::Task<bool> {\n                      co_return co_await pipe_2.write(100);\n                    })))\n            .start();\n    executor.drain();\n    EXPECT_FALSE(future.isReady());\n\n    cs.requestCancellation();\n    executor.drain();\n    EXPECT_TRUE(future.isReady());\n    EXPECT_TRUE(std::move(future).getTry().hasException());\n  }());\n}\n\nTEST(BoundedAsyncPipeTest, ClosingPublisherEndsConsumer) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 2);\n\n    for (size_t i = 0; i < 2; ++i) {\n      co_await pipe.write(i);\n    }\n    std::move(pipe).close();\n\n    EXPECT_TRUE(co_await generator.next());\n    EXPECT_TRUE(co_await generator.next());\n    EXPECT_FALSE(co_await generator.next());\n  }());\n}\n\nTEST(BoundedAsyncPipeTest, ClosingPublisherWithException) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto [generator, pipe] =\n        folly::coro::BoundedAsyncPipe<int>::create(/* tokens */ 2);\n\n    for (size_t i = 0; i < 2; ++i) {\n      co_await pipe.write(i);\n    }\n    std::move(pipe).close(std::runtime_error(\"error!\"));\n\n    EXPECT_TRUE(co_await generator.next());\n    EXPECT_TRUE(co_await generator.next());\n\n    auto itemTry = co_await folly::coro::co_awaitTry(generator.next());\n    EXPECT_TRUE(itemTry.hasException());\n  }());\n}\n#endif\n"
  },
  {
    "path": "folly/coro/test/AsyncScopeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncScope.h>\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/GlobalExecutor.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nstruct AsyncScopeTest : public testing::Test {};\n\nTEST_F(AsyncScopeTest, ConstructDestruct) {\n  // Safe to construct/destruct an AsyncScope without calling any methods.\n  folly::coro::AsyncScope scope;\n}\n\nCO_TEST_F(AsyncScopeTest, AddAndJoin) {\n  std::atomic<int> count = 0;\n  auto makeTask = [&]() -> folly::coro::Task<> {\n    ++count;\n    co_return;\n  };\n\n  folly::coro::AsyncScope scope;\n  for (int i = 0; i < 100; ++i) {\n    scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n  }\n\n  co_await scope.joinAsync();\n\n  EXPECT_EQ(count, 100);\n}\n\nCO_TEST_F(AsyncScopeTest, StartChildTasksAfterCleanupStarted) {\n  folly::coro::AsyncScope scope;\n  folly::coro::Baton baton;\n  bool childFinished = false;\n  auto executor = co_await folly::coro::co_current_executor;\n\n  auto childTask = [&]() -> folly::coro::Task<> {\n    co_await folly::coro::co_reschedule_on_current_executor;\n    childFinished = true;\n  };\n\n  auto parentTask = [&]() -> folly::coro::Task<> {\n    co_await baton;\n    scope.add(co_withExecutor(executor, childTask()));\n  };\n\n  scope.add(co_withExecutor(executor, parentTask()));\n\n  co_await folly::coro::collectAll(\n      scope.joinAsync(), [&]() -> folly::coro::Task<> {\n        baton.post();\n        co_return;\n      }());\n\n  EXPECT_TRUE(childFinished);\n}\n\nCO_TEST_F(AsyncScopeTest, QueryRemainingCount) {\n  folly::coro::Baton baton;\n\n  auto makeTask = [&]() -> folly::coro::Task<> { co_await baton; };\n  auto executor = co_await folly::coro::co_current_executor;\n\n  folly::coro::AsyncScope scope;\n\n  CO_ASSERT_EQ(0, scope.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope.add(co_withExecutor(executor, makeTask()));\n  }\n  CO_ASSERT_EQ(10, scope.remaining());\n\n  baton.post();\n\n  co_await scope.joinAsync();\n  CO_ASSERT_EQ(0, scope.remaining());\n}\n\nCO_TEST_F(AsyncScopeTest, QueryRemainingCountAfterJoined) {\n  folly::coro::AsyncScope scope;\n  folly::coro::Baton baton;\n\n  auto makeTask = [&]() -> folly::coro::Task<> { co_await baton; };\n  auto executor = co_await folly::coro::co_current_executor;\n  scope.add(co_withExecutor(executor, makeTask()));\n\n  EXPECT_EQ(scope.remaining(), 1);\n\n  folly::coro::Baton validateBaton;\n  auto validateTask = [&]() -> folly::coro::Task<> {\n    EXPECT_EQ(scope.remaining(), 1);\n    validateBaton.post();\n    // sleep for scope.joinAsync() to get called.\n    co_await folly::coro::sleep(std::chrono::milliseconds(10));\n    EXPECT_EQ(scope.remaining(), 1);\n    baton.post();\n  };\n  auto validateFut = co_withExecutor(executor, validateTask()).start();\n  co_await validateBaton;\n  co_await scope.joinAsync();\n  co_await std::move(validateFut);\n}\n\nnamespace {\nfolly::coro::Task<> crash() {\n  folly::coro::AsyncScope scope{false};\n  auto makeTask = [&]() -> folly::coro::Task<> {\n    // sleep to force yielding\n    co_await folly::coro::sleep(std::chrono::milliseconds(100));\n    throw std::runtime_error(\"Computer says no\");\n  };\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n  co_return;\n}\n} // namespace\n\nCO_TEST_F(AsyncScopeTest, DontThrowOnJoin) {\n  EXPECT_DEATH(folly::coro::blockingWait(crash()), \"not yet complete\");\n  co_return;\n}\n\nCO_TEST_F(AsyncScopeTest, ThrowOnJoin) {\n  folly::coro::AsyncScope scope{true};\n  auto makeTask = [&]() -> folly::coro::Task<> {\n    // sleep to force yielding\n    co_await folly::coro::sleep(std::chrono::milliseconds(100));\n    throw std::runtime_error(\"Computer says no\");\n  };\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n\n  EXPECT_THROW(co_await scope.joinAsync(), std::runtime_error);\n}\n\nstruct CancellableAsyncScopeTest : public testing::Test {};\n\nTEST_F(CancellableAsyncScopeTest, ConstructDestruct) {\n  // Safe to construct/destruct an AsyncScope without calling any methods.\n  folly::coro::CancellableAsyncScope scope;\n}\n\nCO_TEST_F(CancellableAsyncScopeTest, AddAndJoin) {\n  std::atomic<int> count = 0;\n  auto makeTask = [&]() -> folly::coro::Task<> {\n    ++count;\n    co_return;\n  };\n\n  folly::coro::CancellableAsyncScope scope;\n  for (int i = 0; i < 99; ++i) {\n    scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n  }\n  scope.addWithSourceLoc(\n      co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n\n  co_await scope.joinAsync();\n\n  EXPECT_EQ(count, 100);\n}\n\nCO_TEST_F(CancellableAsyncScopeTest, StartChildTasksAfterCleanupStarted) {\n  folly::coro::CancellableAsyncScope scope;\n  folly::coro::Baton baton;\n  bool childFinished = false;\n  auto executor = co_await folly::coro::co_current_executor;\n\n  auto childTask = [&]() -> folly::coro::Task<> {\n    co_await folly::coro::co_reschedule_on_current_executor;\n    childFinished = true;\n  };\n\n  auto parentTask = [&]() -> folly::coro::Task<> {\n    co_await baton;\n    scope.add(co_withExecutor(executor, childTask()));\n  };\n\n  scope.add(co_withExecutor(executor, parentTask()));\n\n  co_await folly::coro::collectAll(\n      scope.joinAsync(), [&]() -> folly::coro::Task<> {\n        baton.post();\n        co_return;\n      }());\n\n  EXPECT_TRUE(childFinished);\n}\n\nCO_TEST_F(CancellableAsyncScopeTest, QueryRemainingCount) {\n  folly::coro::Baton baton;\n\n  auto makeTask = [&]() -> folly::coro::Task<> { co_await baton; };\n  auto executor = co_await folly::coro::co_current_executor;\n\n  folly::coro::CancellableAsyncScope scope;\n\n  CO_ASSERT_EQ(0, scope.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope.add(co_withExecutor(executor, makeTask()));\n  }\n  CO_ASSERT_EQ(10, scope.remaining());\n\n  baton.post();\n\n  co_await scope.joinAsync();\n  CO_ASSERT_EQ(0, scope.remaining());\n}\n\nCO_TEST_F(CancellableAsyncScopeTest, QueryIsCancellationRequested) {\n  using namespace std::chrono_literals;\n\n  auto makeTask = [&]() -> folly::coro::Task<> {\n    while (true) {\n      co_await folly::coro::sleep(500s);\n    }\n  };\n  auto executor = co_await folly::coro::co_current_executor;\n\n  // default constructed scope\n  folly::coro::CancellableAsyncScope scope;\n  CO_ASSERT_EQ(false, scope.isScopeCancellationRequested());\n  for (int i = 0; i < 10; ++i) {\n    scope.add(co_withExecutor(executor, makeTask()));\n  }\n  CO_ASSERT_EQ(10, scope.remaining());\n\n  co_await scope.cancelAndJoinAsync();\n  CO_ASSERT_EQ(true, scope.isScopeCancellationRequested());\n  CO_ASSERT_EQ(0, scope.remaining());\n\n  // construct scope using external CancellationSource and cancel using the\n  // external cancellationSource\n  folly::CancellationSource source;\n  folly::coro::CancellableAsyncScope scope2(source.getToken());\n\n  CO_ASSERT_EQ(0, scope2.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope2.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n  }\n  CO_ASSERT_EQ(10, scope2.remaining());\n  CO_ASSERT_EQ(false, scope2.isScopeCancellationRequested());\n\n  source.requestCancellation();\n  CO_ASSERT_EQ(true, scope2.isScopeCancellationRequested());\n  co_await scope2.joinAsync();\n  CO_ASSERT_EQ(0, scope2.remaining());\n\n  source = {};\n  // construct scope using external CancellationSource and cancel using the\n  // class's cancellation source\n  folly::coro::CancellableAsyncScope scope3(source.getToken());\n\n  CO_ASSERT_EQ(0, scope3.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope3.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n  }\n  CO_ASSERT_EQ(10, scope3.remaining());\n  CO_ASSERT_EQ(false, scope3.isScopeCancellationRequested());\n  co_await scope3.cancelAndJoinAsync();\n  CO_ASSERT_EQ(true, scope3.isScopeCancellationRequested());\n  CO_ASSERT_EQ(0, scope3.remaining());\n\n  source = {};\n  // default scope construction; each task is added with custom cancellation\n  // token\n  folly::coro::CancellableAsyncScope scope4;\n  CO_ASSERT_EQ(0, scope4.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope4.add(\n        co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()),\n        source.getToken());\n  }\n  CO_ASSERT_EQ(10, scope4.remaining());\n  source.requestCancellation();\n  CO_ASSERT_EQ(source.isCancellationRequested(), true);\n  CO_ASSERT_EQ(false, scope4.isScopeCancellationRequested());\n  co_await scope4.joinAsync();\n  // this is false since we the token that is used is not part of the AsyncScope\n  // state\n  CO_ASSERT_EQ(false, scope4.isScopeCancellationRequested());\n  CO_ASSERT_EQ(0, scope4.remaining());\n}\n\nCO_TEST_F(CancellableAsyncScopeTest, CancelSuspendedWork) {\n  using namespace std::chrono_literals;\n\n  auto makeTask = [&]() -> folly::coro::Task<> {\n    co_await folly::coro::sleep(300s);\n  };\n\n  folly::coro::CancellableAsyncScope scope;\n\n  CO_ASSERT_EQ(0, scope.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n  }\n  CO_ASSERT_EQ(10, scope.remaining());\n\n  // Although we are suspended while sleeping, cancelAndJoinAsync will handle\n  // this correctly.\n  co_await scope.cancelAndJoinAsync();\n  CO_ASSERT_EQ(0, scope.remaining());\n\n  folly::CancellationSource source;\n  folly::coro::CancellableAsyncScope scope2(source.getToken());\n\n  CO_ASSERT_EQ(0, scope2.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope2.add(co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()));\n  }\n  CO_ASSERT_EQ(10, scope2.remaining());\n\n  source.requestCancellation();\n  co_await scope2.joinAsync();\n  CO_ASSERT_EQ(0, scope2.remaining());\n\n  source = {};\n  folly::coro::CancellableAsyncScope scope3;\n\n  CO_ASSERT_EQ(0, scope3.remaining());\n  for (int i = 0; i < 10; ++i) {\n    scope3.add(\n        co_withExecutor(folly::getGlobalCPUExecutor(), makeTask()),\n        source.getToken());\n  }\n  CO_ASSERT_EQ(10, scope3.remaining());\n\n  source.requestCancellation();\n  co_await scope3.joinAsync();\n  CO_ASSERT_EQ(0, scope3.remaining());\n}\n\nCO_TEST_F(CancellableAsyncScopeTest, CancelSuspendedWorkCoSchedule) {\n  using namespace std::chrono_literals;\n\n  auto makeTask = [&]() -> folly::coro::Task<> {\n    co_await folly::coro::sleep(300s);\n  };\n\n  folly::coro::CancellableAsyncScope scope;\n\n  CO_ASSERT_EQ(0, scope.remaining());\n  for (int i = 0; i < 10; ++i) {\n    co_await scope.co_schedule(makeTask());\n  }\n  CO_ASSERT_EQ(10, scope.remaining());\n\n  co_await scope.cancelAndJoinAsync();\n  CO_ASSERT_EQ(0, scope.remaining());\n\n  // default scope construction; each task is added with custom cancellation\n  // token; cancellation handled by the scope\n  folly::coro::CancellableAsyncScope scope2;\n  folly::CancellationSource source;\n\n  CO_ASSERT_EQ(0, scope2.remaining());\n  for (int i = 0; i < 10; ++i) {\n    co_await scope2.co_schedule(makeTask(), source.getToken());\n  }\n  CO_ASSERT_EQ(10, scope2.remaining());\n\n  source.requestCancellation();\n  co_await scope2.cancelAndJoinAsync();\n  CO_ASSERT_EQ(0, scope2.remaining());\n\n  // default scope construction; each task is added with custom cancellation\n  // token; cancellation handled by the custom token\n  folly::coro::CancellableAsyncScope scope3;\n  source = {};\n\n  CO_ASSERT_EQ(0, scope3.remaining());\n  for (int i = 0; i < 10; ++i) {\n    co_await scope3.co_schedule(makeTask(), source.getToken());\n  }\n  CO_ASSERT_EQ(10, scope3.remaining());\n\n  source.requestCancellation();\n  co_await scope3.joinAsync();\n  CO_ASSERT_EQ(0, scope3.remaining());\n}\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/test/AsyncStackTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncStack.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/Task.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <cstdint>\n#include <cstdio>\n#include <vector>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nclass AsyncStackTest : public testing::Test {};\n\nTEST_F(AsyncStackTest, SimpleStackTrace) {\n  blockingWait([&]() -> Task<void> {\n    auto trace = co_await co_current_async_stack_trace;\n    // [0] - current coroutine IP\n    // [1] - BlockingWaitTask IP\n    // [2] - blockingWait()\n    // [3] - SimpleStackTrace_Test::TestBody()\n    CHECK_EQ(4, trace.size());\n    CHECK(trace[0] != 0);\n    CHECK(trace[1] != 0);\n    CHECK(trace[2] != 0);\n    CHECK(trace[3] != 0);\n  }());\n}\n\nTEST_F(AsyncStackTest, NestedStackTrace) {\n  blockingWait([]() -> Task<void> { // Coroutine 1\n    co_await []() -> Task<void> { // Coroutine 2\n      co_await []() -> Task<void> { // Coroutine 3\n        auto trace = co_await co_current_async_stack_trace;\n        // [0] - Coroutine 3 IP\n        // [1] - Coroutine 2 IP\n        // [2] - Coroutine 1 IP\n        // [3] - BlockingWaitTask\n        // [4] - blockingWait()\n        // [5] - NestedStackTrace_Test::TestBody()\n        CHECK_EQ(6, trace.size());\n        CHECK(trace[0] != 0);\n        CHECK(trace[1] != 0);\n        CHECK(trace[2] != 0);\n        CHECK(trace[3] != 0);\n        CHECK(trace[4] != 0);\n        CHECK(trace[5] != 0);\n      }();\n    }();\n  }());\n}\n\nTEST_F(AsyncStackTest, CollectAll) {\n  blockingWait([]() -> Task<void> { // Coroutine 1\n    folly::coro::Baton b;\n    auto makeTask = [&]() -> Task<std::vector<std::uintptr_t>> {\n      co_return co_await co_current_async_stack_trace;\n    };\n\n    auto [stack1, stack2] = co_await collectAll(makeTask(), makeTask());\n\n    // Instruction pointers should be the same.\n    CHECK(stack1 != stack2);\n\n    // [0] - lambda coroutine\n    // [1] - BarrierTask\n    // [2] - collectAll\n    // [3] - this coroutine\n    // [4] - BlockingWaitTask\n    // [5] - blockingWait()\n    // [6] - CollectAll_Test::TestBody()\n    CHECK_EQ(7, stack1.size());\n    CHECK_EQ(7, stack2.size());\n\n    CHECK_EQ(stack1[0], stack2[0]);\n    CHECK_EQ(stack1[1], stack2[1]);\n    CHECK_NE(stack1[2], stack2[2]); // Should be started from different\n                                    // addresses in collectAll().\n    CHECK_EQ(stack1[3], stack2[3]);\n    CHECK_EQ(stack1[4], stack2[4]);\n    CHECK_EQ(stack1[5], stack2[5]);\n    CHECK_EQ(stack1[6], stack2[6]);\n\n    auto afterStack = co_await co_current_async_stack_trace;\n    CHECK_EQ(4, afterStack.size());\n    CHECK_NE(afterStack[0], stack1[3]);\n    CHECK_EQ(afterStack[1], stack1[4]);\n    CHECK_EQ(afterStack[2], stack1[5]);\n    CHECK_EQ(afterStack[3], stack1[6]);\n  }());\n}\n\n#if defined(__linux__) && FOLLY_X64\n\nstruct stack_frame {\n  stack_frame* nextFrame;\n  void (*returnAddress)();\n};\n\nFOLLY_NOINLINE std::vector<std::uintptr_t> walk_stack() {\n  auto& root = folly::getCurrentAsyncStackRoot();\n\n  void* asyncRootReturnAddress = root.getReturnAddress();\n\n  stack_frame* stackFrame = (stack_frame*)FOLLY_ASYNC_STACK_FRAME_POINTER();\n  CHECK(stackFrame != nullptr);\n\n  std::vector<std::uintptr_t> stack;\n\n  while (stackFrame->nextFrame->returnAddress != asyncRootReturnAddress) {\n    stack.push_back(\n        reinterpret_cast<std::uintptr_t>(stackFrame->returnAddress));\n    stackFrame = stackFrame->nextFrame;\n  }\n\n  CHECK(root.getTopFrame() != nullptr);\n  for (auto* asyncFrame = root.getTopFrame(); asyncFrame != nullptr;\n       asyncFrame = asyncFrame->getParentFrame()) {\n    stack.push_back(\n        reinterpret_cast<std::uintptr_t>(asyncFrame->getReturnAddress()));\n  }\n\n  return stack;\n}\n\nFOLLY_NOINLINE void normalFunction() {\n  auto stack1 = walk_stack();\n  // Stack should be:\n  //  1) normalFunction()\n  //  2) coro1()\n  //  3) coro2()\n  //  4) makeRefBlockingWaitTask()\n  //  5) blockingWait()\n  //  6) MixedStackWalk_Test::TestBody()\n  // Note that some extra frames could be in-between these depending on inlining\n  // of std library primitives (e.g. coroutine_handle).\n\n  CHECK_LE(6, stack1.size());\n\n  auto stack2 = walk_stack();\n\n  CHECK_LE(6, stack2.size());\n\n  // All except the topmost stack-frame should be the same.\n  CHECK(\n      std::equal(\n          stack1.begin() + 1, stack1.end(), stack2.begin() + 1, stack2.end()));\n}\n\nfolly::coro::Task<void> coro1() {\n  normalFunction();\n  co_return;\n}\n\nfolly::coro::Task<void> coro2() {\n  co_await coro1();\n}\n\nTEST_F(AsyncStackTest, MixedStackWalk) {\n  folly::coro::blockingWait(coro2());\n}\n\n#endif // defined(__linux__) && FOLLY_X64\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"async_generator_bench\",\n    srcs = [\"AsyncGeneratorBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:exception_wrapper\",\n        \"//folly:portability\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:generator\",\n        \"//folly/coro:task\",\n        \"//folly/coro:via_if_async\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_stack_test\",\n    srcs = [\n        \"AsyncStackTest.cpp\",\n    ],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/coro:async_stack\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:task\",\n        \"//folly/portability:gtest\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"value_or_error_test\",\n    srcs = [\"ValueOrErrorTest.cpp\"],\n    deps = [\n        \"//folly:try\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:result\",\n        \"//folly/coro:value_or_error\",\n        \"//folly/coro:value_or_fatal\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/coro/safe:now_task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"blocking_wait_bench\",\n    srcs = [\"BlockingWaitBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:portability\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"collect_all_benchmark\",\n    srcs = [\"CollectAllBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:generator\",\n        \"//folly/coro:task\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"collect_all_try_benchmark\",\n    srcs = [\"CollectAllTryBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:cancellation_token\",\n        \"//folly:exception_wrapper\",\n        \"//folly:try\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:detail_current_async_frame\",\n        \"//folly/coro:task\",\n        \"//folly/coro:via_if_async\",\n        \"//folly/io/async:request_context\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"coro_benchmark_allocator\",\n    srcs = [\"CoroBenchmarkAllocator.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"coro_benchmark_nrvo\",\n    srcs = [\"CoroBenchmarkNRVO.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/coro:coroutine\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_generator_test\",\n    srcs = [\"AsyncGeneratorTest.cpp\"],\n    deps = [\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:traits\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:sleep\",\n        \"//folly/coro:task\",\n        \"//folly/coro:value_or_error\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/coro/safe:now_task\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:pthread\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"coro_test\",\n    srcs = [\n        \"AccumulateTest.cpp\",\n        \"AsyncPipeTest.cpp\",\n        \"AsyncScopeTest.cpp\",\n        \"BatonTest.cpp\",\n        \"BlockingWaitTest.cpp\",\n        \"BoundedQueueTest.cpp\",\n        \"CollectTest.cpp\",\n        \"ConcatTest.cpp\",\n        \"CoroTest.cpp\",\n        \"CurrentExecutorTest.cpp\",\n        \"FilterTest.cpp\",\n        \"FutureUtilTest.cpp\",\n        \"InlineTaskTest.cpp\",\n        \"MergeTest.cpp\",\n        \"MutexTest.cpp\",\n        \"ScopeExitTest.cpp\",\n        \"SharedMutexTest.cpp\",\n        \"SmallUnboundedQueueTest.cpp\",\n        \"TaskTest.cpp\",\n        \"TimeoutTest.cpp\",\n        \"TraitsTest.cpp\",\n        \"TransformTest.cpp\",\n        \"UnboundedQueueTest.cpp\",\n    ],\n    labels = [\"case-isolation-failure\"],\n    deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:chrono\",\n        \"//folly:conv\",\n        \"//folly:optional\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly/coro:accumulate\",\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:async_pipe\",\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:auto_cleanup\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:bounded_queue\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:concat\",\n        \"//folly/coro:coroutine\",\n        \"//folly/coro:current_executor\",\n        \"//folly/coro:detach_on_cancel\",\n        \"//folly/coro:filter\",\n        \"//folly/coro:future_util\",\n        \"//folly/coro:generator\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:inline_task\",\n        \"//folly/coro:invoke\",\n        \"//folly/coro:merge\",\n        \"//folly/coro:mutex\",\n        \"//folly/coro:promise\",\n        \"//folly/coro:shared_mutex\",\n        \"//folly/coro:sleep\",\n        \"//folly/coro:small_unbounded_queue\",\n        \"//folly/coro:task\",\n        \"//folly/coro:timed_wait\",\n        \"//folly/coro:timeout\",\n        \"//folly/coro:traits\",\n        \"//folly/coro:transform\",\n        \"//folly/coro:unbounded_queue\",\n        \"//folly/coro:value_or_error\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/coro/safe:captures\",\n        \"//folly/coro/safe:now_task\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/fibers:core_manager\",\n        \"//folly/fibers:fiber_manager_map\",\n        \"//folly/fibers:semaphore\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:request_context\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/lang:assume\",\n        \"//folly/portability:gtest\",\n        \"//folly/result:coro\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"coroutine_test\",\n    srcs = [\"CoroutineTest.cpp\"],\n    deps = [\n        \"//folly/coro:coroutine\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"error_test\",\n    srcs = [\n        \"ErrorTest.cpp\",\n    ],\n    deps = [\n        \"//folly:exception_wrapper\",\n        \"//folly:portability\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:result\",\n        \"//folly/coro:value_or_error\",\n        \"//folly/coro/safe:now_task\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"generator_test\",\n    srcs = [\"GeneratorTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly/coro:generator\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"gmock_helpers_test\",\n    srcs = [\n        \"GmockHelpersTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//third-party/googletest:gtest\",\n        \"//folly:portability\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:gmock_helpers\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:task\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"gtest_helpers_test\",\n    srcs = [\n        \"GtestHelpersTest.cpp\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"value_or_fatal_test\",\n    srcs = [\"ValueOrFatalTest.cpp\"],\n    deps = [\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:value_or_error\",\n        \"//folly/coro:value_or_fatal\",\n        \"//folly/coro/safe:now_task\",\n        \"//folly/coro/safe:safe_task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"with_async_stack_test\",\n    srcs = [\"WithAsyncStackTest.cpp\"],\n    deps = [\n        \"//folly/coro:detail_current_async_frame\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:traits\",\n        \"//folly/coro:with_async_stack\",\n        \"//folly/coro/safe:now_task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"promise_benchmark\",\n    srcs = [\"PromiseBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:portability\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:future_util\",\n        \"//folly/coro:promise\",\n        \"//folly/coro:task\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"shared_mutex_benchmark\",\n    srcs = [\"SharedMutexBenchmark.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:shared_mutex\",\n        \"//folly/coro:task\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"request_context_test\",\n    srcs = [\"RequestContextTest.cpp\"],\n    deps = [\n        \"//folly/coro:async_generator\",\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:mutex\",\n        \"//folly/coro:task\",\n        \"//folly/coro:unbounded_queue\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"retry_test\",\n    srcs = [\"RetryTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:retry\",\n        \"//folly/coro:sleep\",\n        \"//folly/coro:task\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"serial_queue_runner_test\",\n    srcs = [\"SerialQueueRunnerTest.cpp\"],\n    deps = [\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:serial_queue_runner\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"shared_promise_test\",\n    srcs = [\n        \"SharedPromiseTest.cpp\",\n    ],\n    deps = [\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:detach_on_cancel\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:shared_promise\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"sleep_test\",\n    srcs = [\n        \"SleepTest.cpp\",\n    ],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:sleep\",\n        \"//folly/coro:task\",\n        \"//folly/futures:manual_timekeeper\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"suspended_stack_test\",\n    srcs = [\n        \"SuspendedStackTest.cpp\",\n    ],\n    deps = [\n        \"//folly/coro:async_stack\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:mutex\",\n        \"//folly/coro:task\",\n        \"//folly/debugging/symbolizer:symbolizer\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"synchronized_test\",\n    srcs = [\"SynchronizedTest.cpp\"],\n    deps = [\n        \"//folly/coro:baton\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:synchronized\",\n        \"//folly/coro:task\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"task_bench\",\n    srcs = [\"TaskBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:portability\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:current_executor\",\n        \"//folly/coro:task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"task_wrapper_test\",\n    srcs = [\"TaskWrapperTest.cpp\"],\n    deps = [\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:task_wrapper\",\n        \"//folly/coro:timeout\",\n        \"//folly/fibers:semaphore\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"RustAdaptorsTest\",\n    srcs = [\n        \"RustAdaptorsTest.cpp\",\n    ],\n    deps = [\n        \"//folly:cancellation_token\",\n        \"//folly:portability\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:rust_adaptors\",\n        \"//folly/coro:sleep\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"PromiseTest\",\n    srcs = [\n        \"PromiseTest.cpp\",\n    ],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:collect\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:promise\",\n        \"//folly/coro:task\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/coro/test/BatonTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\n#include <stdio.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly;\n\nclass BatonTest : public testing::Test {};\n\nTEST_F(BatonTest, Ready) {\n  coro::Baton b;\n  CHECK(!b.ready());\n  b.post();\n  CHECK(b.ready());\n  b.reset();\n  CHECK(!b.ready());\n}\n\nTEST_F(BatonTest, InitiallyReady) {\n  coro::Baton b{true};\n  CHECK(b.ready());\n  b.reset();\n  CHECK(!b.ready());\n}\n\nTEST_F(BatonTest, AwaitBaton) {\n  coro::Baton baton;\n  bool reachedBeforeAwait = false;\n  bool reachedAfterAwait = false;\n\n  auto makeTask = [&]() -> coro::Task<void> {\n    reachedBeforeAwait = true;\n    co_await baton;\n    reachedAfterAwait = true;\n  };\n\n  coro::Task<void> t = makeTask();\n\n  CHECK(!reachedBeforeAwait);\n  CHECK(!reachedAfterAwait);\n\n  ManualExecutor executor;\n  auto f = co_withExecutor(&executor, std::move(t)).start();\n  executor.drain();\n\n  CHECK(reachedBeforeAwait);\n  CHECK(!reachedAfterAwait);\n\n  baton.post();\n  executor.drain();\n\n  CHECK(reachedAfterAwait);\n}\n\nTEST_F(BatonTest, MultiAwaitBaton) {\n  coro::Baton baton;\n\n  bool reachedBeforeAwait1 = false;\n  bool reachedBeforeAwait2 = false;\n  bool reachedAfterAwait1 = false;\n  bool reachedAfterAwait2 = false;\n\n  auto makeTask1 = [&]() -> coro::Task<void> {\n    reachedBeforeAwait1 = true;\n    co_await baton;\n    reachedAfterAwait1 = true;\n  };\n\n  auto makeTask2 = [&]() -> coro::Task<void> {\n    reachedBeforeAwait2 = true;\n    // Equivalent to `co_await baton`, we just want it to compile.\n    co_await co_awaitTry(baton.operator co_await());\n    reachedAfterAwait2 = true;\n  };\n\n  coro::Task<void> t1 = makeTask1();\n  coro::Task<void> t2 = makeTask2();\n\n  ManualExecutor executor;\n  auto f1 = co_withExecutor(&executor, std::move(t1)).start();\n  auto f2 = co_withExecutor(&executor, std::move(t2)).start();\n  executor.drain();\n\n  CHECK(reachedBeforeAwait1);\n  CHECK(reachedBeforeAwait2);\n  CHECK(!reachedAfterAwait1);\n  CHECK(!reachedAfterAwait2);\n\n  baton.post();\n  executor.drain();\n\n  CHECK(f1.isReady());\n  CHECK(f2.isReady());\n\n  CHECK(reachedAfterAwait1);\n  CHECK(reachedAfterAwait2);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/BlockingWaitBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/Portability.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Coroutine.h>\n\n#include <string>\n\n#if FOLLY_HAS_COROUTINES\n\nBENCHMARK(blockingWaitRVOInt, iters) {\n  for (size_t iter = 0; iter < iters; ++iter) {\n    auto result =\n        folly::coro::blockingWait(folly::coro::ready_awaitable<int>(42));\n    if (result != 42) {\n      std::abort();\n    }\n  }\n}\n\nconstexpr folly::StringPiece longString =\n    \"hello coroutines! this is a longer string that \"\n    \"should hopefully inhibit short string optimisations.\";\n\nBENCHMARK(blockingWaitRVOStrings, iters) {\n  for (size_t iter = 0; iter < iters; ++iter) {\n    auto result = folly::coro::blockingWait(\n        folly::coro::ready_awaitable<std::string>(longString.str()));\n    if (result.size() != longString.size()) {\n      std::abort();\n    }\n  }\n}\n\nstruct IdentityMatrix {};\n\nstruct Matrix {\n  /* implicit */ Matrix(IdentityMatrix) noexcept {\n    for (int i = 0; i < 4; ++i) {\n      for (int j = 0; j < 4; ++j) {\n        values_[i][j] = (i == j) ? 1 : 0;\n      }\n    }\n  }\n\n  Matrix(const Matrix&) noexcept = default;\n  Matrix& operator=(const Matrix&) noexcept = default;\n\n  std::uint64_t values_[4][4];\n};\n\nBENCHMARK(blockingWaitRVO, iters) {\n  folly::coro::ready_awaitable<Matrix> identityAwaitable{IdentityMatrix{}};\n  for (size_t iter = 0; iter < iters; ++iter) {\n    auto result = folly::coro::blockingWait(identityAwaitable);\n    if (result.values_[3][3] != 1) {\n      std::abort();\n    }\n  }\n}\n\n#endif\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/BlockingWaitTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/Optional.h>\n#include <folly/ScopeGuard.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/safe/NowTask.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/FiberManagerMap.h>\n#include <folly/portability/GTest.h>\n\n#include <memory>\n#include <type_traits>\n\n#if FOLLY_HAS_COROUTINES\n\nstatic_assert( //\n    std::is_same<\n        decltype(folly::coro::blockingWait(\n            std::declval<folly::coro::ready_awaitable<>>())),\n        void>::value);\nstatic_assert( //\n    std::is_same<\n        decltype(folly::coro::blockingWait(\n            std::declval<folly::coro::ready_awaitable<int>>())),\n        int>::value);\nstatic_assert( //\n    std::is_same<\n        decltype(folly::coro::blockingWait(\n            std::declval<folly::coro::ready_awaitable<int&>>())),\n        int&>::value);\n//  blockingWait() should convert rvalue-reference-returning awaitables\n//  into a returned prvalue to avoid potential lifetime issues since\n//  its possible the rvalue reference could have been to some temporary\n//  object stored inside the Awaiter which would have been destructed\n//  by the time blockingWait returns.\nstatic_assert( //\n    std::is_same<\n        decltype(folly::coro::blockingWait(\n            std::declval<folly::coro::ready_awaitable<int&&>>())),\n        int>::value);\n\nclass BlockingWaitTest : public testing::Test {};\n\nTEST_F(BlockingWaitTest, AwaitNowTask) {\n  bool ran = false;\n  folly::coro::blockingWait([&]() -> folly::coro::now_task<> {\n    ran = true;\n    co_return;\n  }());\n  EXPECT_TRUE(ran);\n}\n\nTEST_F(BlockingWaitTest, AwaitNowTaskWithExecutor) {\n  bool ran = false;\n  folly::coro::blockingWait(co_withExecutor(\n      folly::getGlobalCPUExecutor(), [&]() -> folly::coro::now_task<> {\n        ran = true;\n        co_return;\n      }()));\n  EXPECT_TRUE(ran);\n}\n\nTEST_F(BlockingWaitTest, SynchronousCompletionVoidResult) {\n  folly::coro::blockingWait(folly::coro::ready_awaitable<>{});\n}\n\nTEST_F(BlockingWaitTest, SynchronousCompletionPRValueResult) {\n  EXPECT_EQ(\n      123, folly::coro::blockingWait(folly::coro::ready_awaitable<int>{123}));\n  EXPECT_EQ(\n      \"hello\",\n      folly::coro::blockingWait(\n          folly::coro::ready_awaitable<std::string>(\"hello\")));\n}\n\nTEST_F(BlockingWaitTest, SynchronousCompletionLValueResult) {\n  int value = 123;\n  int& result =\n      folly::coro::blockingWait(folly::coro::ready_awaitable<int&>{value});\n  EXPECT_EQ(&value, &result);\n  EXPECT_EQ(123, result);\n}\n\nTEST_F(BlockingWaitTest, SynchronousCompletionRValueResult) {\n  auto p = std::make_unique<int>(123);\n  auto* ptr = p.get();\n\n  // Should return a prvalue which will lifetime-extend when assigned to an\n  // auto&& local variable.\n  auto&& result = folly::coro::blockingWait(\n      folly::coro::ready_awaitable<std::unique_ptr<int>&&>{std::move(p)});\n\n  EXPECT_EQ(ptr, result.get());\n  EXPECT_FALSE(p);\n}\n\nstruct TrickyAwaitable {\n  struct Awaiter {\n    std::unique_ptr<int> value_;\n\n    bool await_ready() const noexcept { return false; }\n\n    bool await_suspend(folly::coro::coroutine_handle<>) noexcept {\n      value_ = std::make_unique<int>(42);\n      return false;\n    }\n\n    std::unique_ptr<int>&& await_resume() { return std::move(value_); }\n  };\n\n  Awaiter operator co_await() { return {}; }\n};\n\nTEST_F(BlockingWaitTest, ReturnRvalueReferenceFromAwaiter) {\n  // This awaitable stores the result in the temporary Awaiter object that\n  // is placed on the coroutine frame as part of the co_await expression.\n  // It then returns an rvalue-reference to the value inside this temporary\n  // Awaiter object. This test is making sure that we copy/move the result\n  // before destructing the Awaiter object.\n  auto result = folly::coro::blockingWait(TrickyAwaitable{});\n  CHECK(result);\n  CHECK_EQ(42, *result);\n}\n\nTEST_F(BlockingWaitTest, AsynchronousCompletionOnAnotherThread) {\n  folly::coro::Baton baton;\n  std::thread t{[&] { baton.post(); }};\n  SCOPE_EXIT {\n    t.join();\n  };\n  folly::coro::blockingWait(baton);\n}\n\ntemplate <typename T>\nclass SimplePromise {\n public:\n  class WaitOperation {\n   public:\n    explicit WaitOperation(\n        folly::coro::Baton& baton, folly::Optional<T>& value) noexcept\n        : awaiter_(baton), value_(value) {}\n\n    bool await_ready() noexcept { return awaiter_.await_ready(); }\n\n    template <typename Promise>\n    auto await_suspend(folly::coro::coroutine_handle<Promise> h) noexcept {\n      return awaiter_.await_suspend(h);\n    }\n\n    T&& await_resume() {\n      awaiter_.await_resume();\n      return std::move(*value_);\n    }\n\n   private:\n    folly::coro::Baton::WaitOperation awaiter_;\n    folly::Optional<T>& value_;\n  };\n\n  SimplePromise() = default;\n\n  WaitOperation operator co_await() { return WaitOperation{baton_, value_}; }\n\n  template <typename... Args>\n  void emplace(Args&&... args) {\n    value_.emplace(static_cast<Args&&>(args)...);\n    baton_.post();\n  }\n\n private:\n  folly::coro::Baton baton_;\n  folly::Optional<T> value_;\n};\n\nTEST_F(BlockingWaitTest, WaitOnSimpleAsyncPromise) {\n  SimplePromise<std::string> p;\n  std::thread t{[&] { p.emplace(\"hello coroutines!\"); }};\n  SCOPE_EXIT {\n    t.join();\n  };\n  auto result = folly::coro::blockingWait(p);\n  EXPECT_EQ(\"hello coroutines!\", result);\n}\n\nstruct MoveCounting {\n  int count_;\n  MoveCounting() noexcept : count_(0) {}\n  MoveCounting(MoveCounting&& other) noexcept : count_(other.count_ + 1) {}\n  MoveCounting& operator=(MoveCounting&& other) = delete;\n};\n\nTEST_F(BlockingWaitTest, WaitOnMoveOnlyAsyncPromise) {\n  SimplePromise<MoveCounting> p;\n  std::thread t{[&] { p.emplace(); }};\n  SCOPE_EXIT {\n    t.join();\n  };\n  auto result = folly::coro::blockingWait(p);\n\n  // Number of move-constructions:\n  // 0. Value is in-place constructed in Optional<T>\n  // 0. await_resume() returns rvalue reference to Optional<T> value.\n  // 1. return_value() moves value into Try<T>\n  // 2. Value is moved from Try<T> to blockingWait() return value.\n  EXPECT_GE(2, result.count_);\n}\n\nTEST_F(BlockingWaitTest, moveCountingAwaitableReady) {\n  folly::coro::ready_awaitable<MoveCounting> awaitable{MoveCounting{}};\n  auto result = folly::coro::blockingWait(awaitable);\n\n  // Moves:\n  // 1. Move value into ready_awaitable\n  // 2. Move value to await_resume() return-value\n  // 3. Move value to Try<T>\n  // 4. Move value to blockingWait() return-value\n  EXPECT_GE(4, result.count_);\n}\n\nTEST_F(BlockingWaitTest, WaitInFiber) {\n  SimplePromise<int> promise;\n  folly::EventBase evb;\n  auto& fm = folly::fibers::getFiberManager(evb);\n\n  auto future = fm.addTaskFuture([&] {\n    return folly::coro::blockingWait(promise);\n  });\n\n  evb.loopOnce();\n  EXPECT_FALSE(future.isReady());\n\n  promise.emplace(42);\n\n  evb.loopOnce();\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(42, std::move(future).get());\n}\n\nTEST_F(BlockingWaitTest, WaitTaskInFiber) {\n  SimplePromise<int> promise;\n  folly::EventBase evb;\n  auto& fm = folly::fibers::getFiberManager(evb);\n\n  auto future = fm.addTaskFuture([&] {\n    return folly::coro::blockingWait(\n        folly::coro::co_invoke([&]() -> folly::coro::Task<int> {\n          EXPECT_FALSE(folly::fibers::onFiber());\n          auto ret = co_await promise;\n          EXPECT_FALSE(folly::fibers::onFiber());\n          co_return ret;\n        }));\n  });\n\n  evb.loopOnce();\n  EXPECT_FALSE(future.isReady());\n\n  promise.emplace(42);\n\n  evb.loopOnce();\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(42, std::move(future).get());\n}\n\nstruct ExpectedException {};\n\nTEST_F(BlockingWaitTest, WaitTaskInFiberException) {\n  folly::EventBase evb;\n  auto& fm = folly::fibers::getFiberManager(evb);\n  EXPECT_TRUE(\n      fm.addTaskFuture([&] {\n          try {\n            folly::coro::blockingWait(\n                folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                  folly::via(co_await folly::coro::co_current_executor, []() {\n                  });\n                  throw ExpectedException();\n                }));\n            return false;\n          } catch (const ExpectedException&) {\n            return true;\n          }\n        }).getVia(&evb));\n}\n\nTEST_F(BlockingWaitTest, WaitOnSemiFuture) {\n  int result = folly::coro::blockingWait(folly::makeSemiFuture(123));\n  CHECK_EQ(result, 123);\n}\n\nTEST_F(BlockingWaitTest, RequestContext) {\n  folly::RequestContextScopeGuard outerGuard;\n  std::shared_ptr<folly::RequestContext> ctx1, ctx2;\n  ctx1 = folly::RequestContext::saveContext();\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    EXPECT_EQ(ctx1.get(), folly::RequestContext::get());\n    folly::RequestContextScopeGuard guard;\n    ctx2 = folly::RequestContext::saveContext();\n    EXPECT_NE(ctx1, ctx2);\n    co_await folly::coro::co_reschedule_on_current_executor;\n    EXPECT_EQ(ctx2.get(), folly::RequestContext::get());\n\n    // The blockingWait executor should also propagate the request context.\n    auto taskExecutor = co_await folly::coro::co_current_executor;\n    folly::coro::Baton done;\n    taskExecutor->add([ctx = folly::RequestContext::try_get(), &done, &ctx1] {\n      EXPECT_EQ(ctx, folly::RequestContext::try_get())\n          << \"Outer context: \" << ctx1.get();\n      done.post();\n    });\n    co_await done;\n\n    co_return;\n  }());\n  EXPECT_EQ(ctx1.get(), folly::RequestContext::get());\n}\n\nTEST_F(BlockingWaitTest, DrivableExecutor) {\n  folly::ManualExecutor executor;\n  folly::coro::blockingWait(\n      [&]() -> folly::coro::Task<void> {\n        folly::Executor* taskExecutor =\n            co_await folly::coro::co_current_executor;\n        EXPECT_EQ(&executor, taskExecutor);\n      }(),\n      &executor);\n}\n\nTEST_F(BlockingWaitTest, ReleaseExecutorFromAnotherThread) {\n  auto fn = []() {\n    auto [p1, f1] = folly::makePromiseContract<folly::Executor::KeepAlive<>>();\n    auto [p2, f2] = folly::makePromiseContract<folly::Unit>();\n    std::thread t{[&, &p2_ = p2, &f1_ = f1] {\n      auto e = std::move(f1_).get();\n      p2_.setValue(folly::Unit{});\n      std::this_thread::sleep_for(std::chrono::microseconds(1));\n      e = {};\n    }};\n    folly::coro::blockingWait(\n        [&, &p1_ = p1, &f2_ = f2]() -> folly::coro::Task<void> {\n          folly::Executor::KeepAlive<> taskExecutor =\n              co_await folly::coro::co_current_executor;\n          p1_.setValue(std::move(taskExecutor));\n          co_await std::move(f2_);\n        }());\n    t.join();\n  };\n  std::vector<std::thread> threads;\n  for (int i = 0; i < 100; ++i) {\n    threads.emplace_back(fn);\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\nTEST_F(BlockingWaitTest, ReleaseManualExecutorFromAnotherThread) {\n  auto fn = []() {\n    auto [p1, f1] = folly::makePromiseContract<folly::Executor::KeepAlive<>>();\n    auto [p2, f2] = folly::makePromiseContract<folly::Unit>();\n    std::thread t{[&, &p2_ = p2, &f1_ = f1] {\n      auto e = std::move(f1_).get();\n      p2_.setValue(folly::Unit{});\n      std::this_thread::sleep_for(std::chrono::microseconds(1));\n      e = {};\n    }};\n    {\n      folly::ManualExecutor executor;\n      folly::coro::blockingWait(\n          [&, &p1_ = p1, &f2_ = f2]() -> folly::coro::Task<void> {\n            folly::Executor::KeepAlive<> taskExecutor =\n                co_await folly::coro::co_current_executor;\n            p1_.setValue(std::move(taskExecutor));\n            co_await std::move(f2_);\n          }(),\n          &executor);\n    }\n    t.join();\n  };\n  std::vector<std::thread> threads;\n  for (int i = 0; i < 100; ++i) {\n    threads.emplace_back(fn);\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/BoundedQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/BoundedQueue.h>\n\n#include <chrono>\n#include <optional>\n#include <string>\n#include <thread>\n\n#include <folly/CancellationToken.h>\n#include <folly/Portability.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Sleep.h>\n#include <folly/portability/GTest.h>\n#if FOLLY_HAS_COROUTINES\n\nusing namespace std::chrono_literals;\n\nnamespace {\nstruct SlowMover {\n  explicit SlowMover(bool slow = false) : slow(slow) {}\n  SlowMover(SlowMover&& other) noexcept { *this = std::move(other); }\n  SlowMover& operator=(SlowMover&& other) noexcept {\n    slow = other.slow;\n    if (slow) {\n      /* sleep override */ std::this_thread::sleep_for(\n          std::chrono::milliseconds(50));\n    }\n    return *this;\n  }\n\n  bool slow;\n};\n} // namespace\n\nCO_TEST(BoundedQueueTest, EnqueueDeque) {\n  folly::coro::BoundedQueue<std::string, true, true> queue(100);\n  constexpr auto val = \"a string\";\n  std::string val1 = val;\n  EXPECT_TRUE(queue.empty());\n  EXPECT_EQ(queue.size(), 0);\n\n  co_await queue.enqueue(val1);\n  EXPECT_FALSE(queue.empty());\n  co_await queue.enqueue(std::move(val1));\n  EXPECT_EQ(queue.size(), 2);\n\n  for (int i = 0; i < 2; ++i) {\n    auto val2 = co_await queue.dequeue();\n    EXPECT_EQ(val2, val);\n  }\n  EXPECT_TRUE(queue.empty());\n}\n\nCO_TEST(BoundedQueueTest, DequeueWhileBlocking) {\n  folly::coro::BoundedQueue<int> queue(5);\n  folly::ManualExecutor ex;\n\n  auto fut = co_withExecutor(&ex, queue.dequeue()).start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n  co_await queue.enqueue(0);\n\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_EQ(std::move(fut).get(), 0);\n}\n\nCO_TEST(BoundedQueueTest, EnqueueDequeMultiProducer) {\n  folly::coro::BoundedQueue<int, false, true> queue(5);\n  std::atomic<int> i = 0;\n\n  std::vector<std::thread> enqueuers;\n  for (int n = 0; n < 5; ++n) {\n    enqueuers.emplace_back([&] {\n      while (true) {\n        int next = i++;\n        if (next >= 100) {\n          break;\n        }\n        folly::coro::blockingWait(\n            [&, next]() mutable -> folly::coro::Task<void> {\n              co_await queue.enqueue(std::move(next));\n            }());\n      }\n    });\n  }\n  for (int n = 0; n < 100; ++n) {\n    co_await queue.dequeue();\n  }\n\n  EXPECT_TRUE(queue.empty());\n\n  for (int n = 0; n < 5; ++n) {\n    enqueuers[n].join();\n  }\n}\n\nCO_TEST(BoundedQueueTest, EnqueueDequeMultiConsumer) {\n  folly::coro::BoundedQueue<int, true, false> queue(10);\n  std::atomic<int> seen = 0;\n\n  std::vector<std::thread> dequeuers;\n  for (int n = 0; n < 5; ++n) {\n    dequeuers.emplace_back([&] {\n      folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n        while (++seen <= 100) {\n          co_await queue.dequeue();\n        }\n      }());\n    });\n  }\n\n  for (int n = 0; n < 100; ++n) {\n    co_await queue.enqueue(std::move(n));\n  }\n  for (int n = 0; n < 5; ++n) {\n    dequeuers[n].join();\n  }\n  EXPECT_TRUE(queue.empty());\n}\n\nCO_TEST(BoundedQueueTest, EnqueueDequeMPMCWithSingleSlot) {\n  folly::coro::BoundedQueue<int, false, false> queue(1);\n  std::atomic<int> seen = 0, i = 0;\n\n  std::vector<std::thread> enqueuers;\n  for (int n = 0; n < 5; ++n) {\n    enqueuers.emplace_back([&] {\n      folly::coro::blockingWait([&]() mutable -> folly::coro::Task<void> {\n        while (true) {\n          int next = i++;\n          if (next >= 100) {\n            break;\n          }\n          co_await queue.enqueue(std::move(next));\n        }\n      }());\n    });\n  }\n\n  std::vector<std::thread> dequeuers;\n  for (int n = 0; n < 5; ++n) {\n    dequeuers.emplace_back([&] {\n      folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n        while (++seen <= 100) {\n          co_await queue.dequeue();\n        }\n      }());\n    });\n  }\n\n  for (int n = 0; n < 5; ++n) {\n    enqueuers[n].join();\n  }\n  for (int n = 0; n < 5; ++n) {\n    dequeuers[n].join();\n  }\n  EXPECT_TRUE(queue.empty());\n\n  co_return;\n}\n\nCO_TEST(\n    BoundedQueueTest, CancelledDequeueCompletesNormallyIfAnItemIsAvailable) {\n  folly::coro::BoundedQueue<int> queue(10);\n  folly::CancellationSource cancelSource;\n  cancelSource.requestCancellation();\n\n  co_await queue.enqueue(123);\n\n  int result = co_await folly::coro::co_withCancellation(\n      cancelSource.getToken(), queue.dequeue());\n  EXPECT_EQ(123, result);\n}\n\nCO_TEST(BoundedQueueTest, EnqueueWait) {\n  folly::coro::BoundedQueue<int> queue(2);\n  co_await folly::coro::collectAll(\n      [&]() -> folly::coro::Task<void> {\n        for (int i = 0; i < 100; i++) {\n          auto val = i;\n          co_await queue.enqueue(std::move(val));\n        }\n      }(),\n      [&]() -> folly::coro::Task<void> {\n        for (int i = 0; i < 100; i++) {\n          int val = co_await queue.dequeue();\n          EXPECT_EQ(val, i);\n        }\n      }());\n}\n\nCO_TEST(BoundedQueueTest, DequeueWait) {\n  folly::coro::BoundedQueue<int> queue(2);\n  co_await folly::coro::collectAll(\n      [&]() -> folly::coro::Task<void> {\n        for (int i = 0; i < 100; i++) {\n          auto val = i;\n          co_await queue.enqueue(std::move(val));\n        }\n      }(),\n      [&]() -> folly::coro::Task<void> {\n        for (int i = 0; i < 100; i++) {\n          int val = co_await queue.dequeue();\n          EXPECT_EQ(val, i);\n        }\n      }());\n}\n\nCO_TEST(BoundedQueueTest, TryEnqueue) {\n  folly::coro::BoundedQueue<int> queue(2);\n\n  EXPECT_TRUE(queue.try_enqueue(1));\n  EXPECT_TRUE(queue.try_enqueue(1));\n  EXPECT_FALSE(queue.try_enqueue(1));\n  co_await queue.dequeue();\n  EXPECT_TRUE(queue.try_enqueue(1));\n}\n\nCO_TEST(BoundedQueueTest, TryDequeue) {\n  folly::coro::BoundedQueue<int> queue(2);\n\n  EXPECT_FALSE(queue.try_dequeue().has_value());\n  co_await queue.enqueue(1);\n  EXPECT_TRUE(queue.try_dequeue().has_value());\n}\n\nTEST(BoundedQueueTest, UnorderedEnqueueCompletion) {\n  // Use optional to verify we're not accidentally dequeueing\n  // default-constructed values.\n  folly::coro::BoundedQueue<std::optional<SlowMover>> queue(1024);\n  std::atomic<int> turn = 0;\n\n  std::thread consumer([&] {\n    ++turn;\n    for (size_t i = 0; i < 2; ++i) {\n      ASSERT_TRUE(folly::coro::blockingWait(queue.dequeue()).has_value());\n    }\n  });\n\n  // producer2 will frequently initiate the enqueue after producer1 (thus\n  // acquiring a larger ticket) but complete the move after it. The consumer\n  // thus needs to block until the head-of-line item is available.\n  std::thread producer1([&] {\n    ++turn;\n    while (turn < 3) {\n    }\n    ++turn;\n    ASSERT_TRUE(queue.try_enqueue(std::optional(SlowMover(true))));\n  });\n  std::thread producer2([&] {\n    ++turn;\n    while (turn < 4) {\n    }\n    /* sleep override */ std::this_thread::sleep_for(\n        std::chrono::milliseconds(1));\n    ASSERT_TRUE(queue.try_enqueue(std::optional(SlowMover(false))));\n  });\n\n  producer1.join();\n  producer2.join();\n  consumer.join();\n}\n\nTEST(BoundedQueueTest, UnorderedDequeueCompletion) {\n  // Use optional to verify we're not accidentally dequeueing\n  // default-constructed values.\n  folly::coro::BoundedQueue<std::optional<SlowMover>> queue(2);\n\n  ASSERT_TRUE(queue.try_enqueue(std::optional(SlowMover(true))));\n  ASSERT_TRUE(queue.try_enqueue(std::optional(SlowMover(false))));\n\n  std::vector<std::thread> consumers;\n  for (size_t i = 0; i < 3; ++i) {\n    consumers.emplace_back([&]() {\n      ASSERT_TRUE(folly::coro::blockingWait(queue.dequeue()).has_value());\n    });\n  }\n\n  // The producer will get the ticket for the slow moving slot which will still\n  // be in the process of dequeuing, so the producer needs to block until it\n  // finishes and the slot becomes available.\n  std::thread producer([&] {\n    folly::coro::blockingWait(queue.enqueue(std::optional(SlowMover(false))));\n  });\n\n  producer.join();\n  for (auto& consumer : consumers) {\n    consumer.join();\n  }\n}\n\nTEST(BoundedQueueTest, TryDequeueFor) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::BoundedQueue<int> queue(2);\n\n    EXPECT_THROW(\n        (co_await queue.co_try_dequeue_for(1ms)), folly::OperationCancelled);\n\n    co_await queue.enqueue(42);\n    auto val = co_await queue.co_try_dequeue_for(1ms);\n    EXPECT_EQ(val, 42);\n\n    co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await queue.enqueue(43);\n        }(),\n        [&]() -> folly::coro::Task<void> {\n          EXPECT_TRUE(queue.empty());\n          val = co_await queue.co_try_dequeue_for(1h);\n          EXPECT_EQ(val, 43);\n        }());\n\n    folly::CancellationSource cancelSource;\n    co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          cancelSource.requestCancellation();\n        }(),\n        [&]() -> folly::coro::Task<void> {\n          EXPECT_THROW(\n              (co_await folly::coro::co_withCancellation(\n                  cancelSource.getToken(), queue.co_try_dequeue_for(1h))),\n              folly::OperationCancelled);\n        }());\n  }());\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/CollectAllBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/Generator.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/synchronization/Baton.h>\n\nvoid doWork() {}\n\nfolly::CPUThreadPoolExecutor executor(4);\n\nvoid collectAllFuture(size_t batchSize) {\n  std::vector<folly::Future<folly::Unit>> futures;\n  for (size_t i = 0; i < batchSize; ++i) {\n    futures.emplace_back(folly::via(&executor, [] { doWork(); }));\n  }\n  folly::collectAll(std::move(futures)).get();\n}\n\nvoid collectAllFutureInline(size_t batchSize) {\n  std::vector<folly::Future<folly::Unit>> futures;\n  for (size_t i = 0; i < batchSize; ++i) {\n    futures.emplace_back(\n        folly::via(&executor, [] {\n          doWork();\n        }).via(&folly::InlineExecutor::instance()));\n  }\n  folly::collectAll(std::move(futures)).get();\n}\n\nfolly::coro::Task<void> co_doWork() {\n  co_await folly::coro::co_reschedule_on_current_executor;\n  doWork();\n}\n\nvoid collectAllCoro(size_t batchSize) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    co_await co_withExecutor(\n        &executor,\n        folly::coro::collectAllRange(\n            [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n              for (size_t i = 0; i < batchSize; ++i) {\n                co_yield co_doWork();\n              }\n            }()));\n  }());\n}\n\nvoid collectAllBaton(size_t batchSize) {\n  folly::Baton<> baton;\n  std::shared_ptr<folly::Baton<>> batonGuard(&baton, [](folly::Baton<>* baton) {\n    baton->post();\n  });\n  for (size_t i = 0; i < batchSize; ++i) {\n    executor.add([batonGuard]() { doWork(); });\n  }\n  batonGuard.reset();\n  baton.wait();\n}\n\nBENCHMARK(collectAllFuture10000, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllFuture(10000);\n  }\n}\n\nBENCHMARK(collectAllFutureInline10000, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllFutureInline(10000);\n  }\n}\n\nBENCHMARK(collectAllCoro10000, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllCoro(10000);\n  }\n}\n\nBENCHMARK(collectAllBaton10000, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllBaton(10000);\n  }\n}\n\nBENCHMARK(collectAllFuture100, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllFuture(100);\n  }\n}\n\nBENCHMARK(collectAllFutureInline100, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllFutureInline(100);\n  }\n}\n\nBENCHMARK(collectAllCoro100, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllCoro(100);\n  }\n}\n\nBENCHMARK(collectAllBaton100, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    collectAllBaton(100);\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/CollectAllTryBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Benchmark comparing co_awaitTry vs try/catch for collecting results from\n// multiple coroutines into folly::Try values. The try/catch baseline uses the\n// same barrier/scheduling machinery as the real collectAllTryRange, matching\n// the original implementation before it was converted to co_awaitTry.\n\n#include <folly/Benchmark.h>\n#include <folly/Try.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/detail/Barrier.h>\n#include <folly/coro/detail/BarrierTask.h>\n#include <folly/coro/detail/CurrentAsyncFrame.h>\n#include <folly/coro/detail/Helpers.h>\n#include <folly/io/async/Request.h>\n\n#include <vector>\n\n#if FOLLY_HAS_COROUTINES\n\nstruct BenchmarkError : std::exception {};\n\nfolly::coro::Task<int> succeedingTask(int value) {\n  co_return value;\n}\n\nfolly::coro::Task<int> failingTask() {\n  co_yield folly::coro::co_error(BenchmarkError{});\n}\n\n// Old collectAllTryRange implementation using try/catch (before the\n// co_awaitTry conversion). Uses the same barrier machinery as the real\n// implementation for an apples-to-apples comparison.\nnamespace folly::coro {\n\nnamespace benchmark_detail {\ntemplate <typename InputRange>\nauto collectAllTryRangeWithTryCatchImpl(InputRange awaitables)\n    -> Task<std::vector<detail::collect_all_try_range_component_t<\n        detail::range_reference_t<InputRange>>>> {\n  std::vector<detail::collect_all_try_range_component_t<\n      detail::range_reference_t<InputRange>>>\n      results;\n\n  const folly::Executor::KeepAlive<> executor =\n      folly::getKeepAliveToken(co_await co_current_executor);\n\n  const CancellationToken& cancelToken = co_await co_current_cancellation_token;\n\n  using awaitable_type = remove_cvref_t<detail::range_reference_t<InputRange>>;\n  auto makeTask = [&](awaitable_type semiAwaitable, std::size_t index)\n      -> detail::BarrierTask {\n    assert(index < results.size());\n    auto& result = results[index];\n    // Original try/catch pattern\n    try {\n      using await_result = semi_await_result_t<awaitable_type>;\n      if constexpr (std::is_void_v<await_result>) {\n        co_await co_viaIfAsync(\n            executor.get_alias(),\n            co_withCancellation(cancelToken, std::move(semiAwaitable)));\n        result.emplace();\n      } else {\n        result.emplace(\n            co_await co_viaIfAsync(\n                executor.get_alias(),\n                co_withCancellation(cancelToken, std::move(semiAwaitable))));\n      }\n    } catch (...) {\n      result.emplaceException(current_exception());\n    }\n  };\n\n  auto tasks = detail::collectMakeInnerTaskVec(awaitables, makeTask);\n\n  results.resize(tasks.size());\n\n  const auto context = RequestContext::saveContext();\n  auto& asyncFrame = co_await detail::co_current_async_stack_frame;\n\n  {\n    detail::Barrier barrier{tasks.size() + 1};\n    for (auto&& task : tasks) {\n      task.start(&barrier, asyncFrame);\n      RequestContext::setContext(context);\n    }\n    co_await detail::UnsafeResumeInlineSemiAwaitable{barrier.arriveAndWait()};\n  }\n\n  co_return results;\n}\n} // namespace benchmark_detail\n\n// std::vector overload that wraps with MoveRange (mirrors the real API)\ntemplate <typename SemiAwaitable>\nauto collectAllTryRangeWithTryCatch(std::vector<SemiAwaitable> awaitables)\n    -> decltype(benchmark_detail::collectAllTryRangeWithTryCatchImpl(\n        detail::MoveRange(awaitables))) {\n  co_return co_await benchmark_detail::collectAllTryRangeWithTryCatchImpl(\n      detail::MoveRange(awaitables));\n}\n\n} // namespace folly::coro\n\n// ---------------------------------------------------------------------------\n// All tasks succeed (100 tasks)\n// ---------------------------------------------------------------------------\nBENCHMARK(coAwaitTry_AllSucceed_100, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      std::vector<folly::coro::Task<int>> tasks;\n      tasks.reserve(100);\n      for (int i = 0; i < 100; ++i) {\n        tasks.push_back(succeedingTask(i));\n      }\n      auto results = co_await folly::coro::collectAllTryRange(std::move(tasks));\n      folly::doNotOptimizeAway(results);\n    }\n  }());\n}\n\nBENCHMARK_RELATIVE(tryCatch_AllSucceed_100, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      std::vector<folly::coro::Task<int>> tasks;\n      tasks.reserve(100);\n      for (int i = 0; i < 100; ++i) {\n        tasks.push_back(succeedingTask(i));\n      }\n      auto results = co_await folly::coro::collectAllTryRangeWithTryCatch(\n          std::move(tasks));\n      folly::doNotOptimizeAway(results);\n    }\n  }());\n}\n\nBENCHMARK_DRAW_LINE();\n\n// ---------------------------------------------------------------------------\n// All tasks fail (100 tasks)\n// ---------------------------------------------------------------------------\nBENCHMARK(coAwaitTry_AllFail_100, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      std::vector<folly::coro::Task<int>> tasks;\n      tasks.reserve(100);\n      for (int i = 0; i < 100; ++i) {\n        tasks.push_back(failingTask());\n      }\n      auto results = co_await folly::coro::collectAllTryRange(std::move(tasks));\n      folly::doNotOptimizeAway(results);\n    }\n  }());\n}\n\nBENCHMARK_RELATIVE(tryCatch_AllFail_100, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      std::vector<folly::coro::Task<int>> tasks;\n      tasks.reserve(100);\n      for (int i = 0; i < 100; ++i) {\n        tasks.push_back(failingTask());\n      }\n      auto results = co_await folly::coro::collectAllTryRangeWithTryCatch(\n          std::move(tasks));\n      folly::doNotOptimizeAway(results);\n    }\n  }());\n}\n\nBENCHMARK_DRAW_LINE();\n\n// ---------------------------------------------------------------------------\n// 50% tasks fail (100 tasks)\n// ---------------------------------------------------------------------------\nBENCHMARK(coAwaitTry_HalfFail_100, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      std::vector<folly::coro::Task<int>> tasks;\n      tasks.reserve(100);\n      for (int i = 0; i < 100; ++i) {\n        if (i % 2 == 0) {\n          tasks.push_back(succeedingTask(i));\n        } else {\n          tasks.push_back(failingTask());\n        }\n      }\n      auto results = co_await folly::coro::collectAllTryRange(std::move(tasks));\n      folly::doNotOptimizeAway(results);\n    }\n  }());\n}\n\nBENCHMARK_RELATIVE(tryCatch_HalfFail_100, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      std::vector<folly::coro::Task<int>> tasks;\n      tasks.reserve(100);\n      for (int i = 0; i < 100; ++i) {\n        if (i % 2 == 0) {\n          tasks.push_back(succeedingTask(i));\n        } else {\n          tasks.push_back(failingTask());\n        }\n      }\n      auto results = co_await folly::coro::collectAllTryRangeWithTryCatch(\n          std::move(tasks));\n      folly::doNotOptimizeAway(results);\n    }\n  }());\n}\n\nBENCHMARK_DRAW_LINE();\n\n// ---------------------------------------------------------------------------\n// Single task (isolate per-task overhead)\n// ---------------------------------------------------------------------------\nBENCHMARK(coAwaitTry_SingleSuccess, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      auto result = co_await folly::coro::co_awaitTry(succeedingTask(42));\n      folly::doNotOptimizeAway(result);\n    }\n  }());\n}\n\nBENCHMARK_RELATIVE(tryCatch_SingleSuccess, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      folly::Try<int> result;\n      try {\n        result.emplace(co_await succeedingTask(42));\n      } catch (...) {\n        result.emplaceException(folly::current_exception());\n      }\n      folly::doNotOptimizeAway(result);\n    }\n  }());\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(coAwaitTry_SingleFailure, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      auto result = co_await folly::coro::co_awaitTry(failingTask());\n      folly::doNotOptimizeAway(result);\n    }\n  }());\n}\n\nBENCHMARK_RELATIVE(tryCatch_SingleFailure, iters) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (size_t iter = 0; iter < iters; ++iter) {\n      folly::Try<int> result;\n      try {\n        result.emplace(co_await failingTask());\n      } catch (...) {\n        result.emplaceException(folly::current_exception());\n      }\n      folly::doNotOptimizeAway(result);\n    }\n  }());\n}\n\n#endif\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/CollectTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Generator.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/Promise.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/safe/NowTask.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/io/async/Request.h>\n#include <folly/portability/GTest.h>\n\n#include <numeric>\n#include <string>\n#include <vector>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\nstruct CollectAll {\n  template <typename... Args>\n  auto operator()(Args...) const\n      -> decltype(collectAll(FOLLY_DECLVAL(Args)...));\n};\n\nstruct CollectAllTry {\n  template <typename... Args>\n  auto operator()(Args...) const\n      -> decltype(collectAllTry(FOLLY_DECLVAL(Args)...));\n};\n\ntemplate <typename Fn, typename In, typename Out>\nstruct SafeTaskTypeAssertions {\n  static_assert(\n      std::is_same_v<\n          decltype(Fn()(FOLLY_DECLVAL(now_task<In>), FOLLY_DECLVAL(Task<In>))),\n          now_task<std::tuple<Out, Out>>>);\n  static_assert(\n      std::is_same_v<\n          decltype(Fn()(FOLLY_DECLVAL(Task<In>), FOLLY_DECLVAL(now_task<In>))),\n          now_task<std::tuple<Out, Out>>>);\n  static_assert(std::is_same_v<\n                decltype(Fn()(\n                    FOLLY_DECLVAL(now_task<In>), FOLLY_DECLVAL(now_task<In>))),\n                now_task<std::tuple<Out, Out>>>);\n\n  // collectAll(now_task) -> now_task\n  static_assert(std::is_same_v<\n                now_task<std::tuple<Out>>,\n                decltype(Fn()(FOLLY_DECLVAL(now_task<In>)))>);\n  static_assert(std::is_same_v<\n                now_task<std::tuple<Out>>,\n                decltype(Fn()(FOLLY_DECLVAL(now_task_with_executor<In>)))>);\n\n  // collectAll(value_task or coro::Future) -> value_task\n  static_assert(std::is_same_v<\n                value_task<std::tuple<Out>>,\n                decltype(Fn()(FOLLY_DECLVAL(value_task<In>)))>);\n  static_assert(std::is_same_v<\n                value_task<std::tuple<Out>>,\n                decltype(Fn()(FOLLY_DECLVAL(\n                    safe_task_with_executor<safe_alias::maybe_value, In>)))>);\n  static_assert(std::is_same_v<\n                value_task<std::tuple<Out>>,\n                decltype(Fn()(FOLLY_DECLVAL(coro::Future<In>)))>);\n\n  // collectAll: returns the \"least safe\" wrapper\n  static_assert(\n      std::is_same_v<\n          now_task<std::tuple<Out, Out>>,\n          decltype(Fn()(\n              FOLLY_DECLVAL(now_task<In>), FOLLY_DECLVAL(value_task<In>)))>);\n  static_assert(std::is_same_v<\n                Task<std::tuple<Out, Out>>,\n                decltype(Fn()(\n                    FOLLY_DECLVAL(Task<In>), FOLLY_DECLVAL(value_task<In>)))>);\n  static_assert(\n      std::is_same_v<\n          now_task<std::tuple<Out, Out, Out>>,\n          decltype(Fn()(\n              FOLLY_DECLVAL(Task<In>),\n              FOLLY_DECLVAL(now_task<In>),\n              FOLLY_DECLVAL(value_task<In>)))>);\n};\nnamespace {\n[[maybe_unused]] struct SafeTaskTypeAssertions<CollectAll, int, int>\n    checkCollectAll;\n\n[[maybe_unused]] struct SafeTaskTypeAssertions<CollectAllTry, int, Try<int>>\n    checkCollectAllTry;\n} // namespace\n\n} // namespace folly::coro\n\nfolly::coro::Task<void> sleepThatShouldBeCancelled(\n    std::chrono::milliseconds dur) {\n  EXPECT_THROW(co_await folly::coro::sleep(dur), folly::OperationCancelled);\n}\n\nclass CollectAllTest : public testing::Test {};\n\nCO_TEST_F(CollectAllTest, NowTask) {\n  auto [a, b] = co_await folly::coro::collectAll(\n      []() -> folly::coro::now_task<int> { co_return 3; }(),\n      []() -> folly::coro::now_task<int> { co_return 7; }());\n  EXPECT_EQ(3, a);\n  EXPECT_EQ(7, b);\n}\n\nTEST_F(CollectAllTest, WithNoArgs) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    std::tuple<> result = co_await folly::coro::collectAll();\n    completed = true;\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAllTest, OneTaskWithValue) {\n  folly::coro::Baton baton;\n  auto f = [&]() -> folly::coro::Task<std::string> {\n    co_await baton;\n    co_return \"hello\";\n  };\n\n  bool completed = false;\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [result] = co_await folly::coro::collectAll(f());\n    EXPECT_EQ(\"hello\", result);\n    completed = true;\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  executor.drain();\n\n  EXPECT_FALSE(completed);\n\n  baton.post();\n\n  // Posting the baton should have just scheduled the 'f()' coroutine\n  // for resumption on the executor but should not have executed\n  // until we drain the executor again.\n  EXPECT_FALSE(completed);\n\n  executor.drain();\n\n  EXPECT_TRUE(completed);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAllTest, OneVoidTask) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that 'void' results are\n    // promoted to folly::Unit when placed in a tuple.\n    std::tuple<folly::Unit> result =\n        co_await folly::coro::collectAll([&]() -> folly::coro::Task<void> {\n          completed = true;\n          co_return;\n        }());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAllTest, CollectAllDoesntCompleteUntilAllTasksComplete) {\n  folly::coro::Baton baton1;\n  folly::coro::Baton baton2;\n  bool task1Started = false;\n  bool task2Started = false;\n  bool complete = false;\n\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [first, second] = co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<void> {\n          task1Started = true;\n          co_await baton1;\n        }(),\n        [&]() -> folly::coro::Task<void> {\n          task2Started = true;\n          co_await baton2;\n        }());\n    complete = true;\n    (void)first;\n    (void)second;\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  EXPECT_FALSE(task1Started);\n  EXPECT_FALSE(task2Started);\n\n  executor.drain();\n\n  EXPECT_TRUE(task1Started);\n  EXPECT_TRUE(task2Started);\n  EXPECT_FALSE(complete);\n  baton1.post();\n  executor.drain();\n  EXPECT_FALSE(complete);\n  baton2.post();\n  executor.drain();\n  EXPECT_TRUE(complete);\n  EXPECT_TRUE(future.isReady());\n}\n\nstruct ErrorA : std::exception {};\nstruct ErrorB : std::exception {};\nstruct ErrorC : std::exception {};\n\nTEST_F(CollectAllTest, ThrowsFirstError) {\n  bool caughtException = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    try {\n      bool throwError = true;\n      // Child tasks are started in-order.\n      // The first task will reschedule itself onto the executor.\n      // The second task will fail immediately and will be the first\n      // task to fail.\n      // Then the third and first tasks will fail.\n      // As the second task failed first we should see its exception\n      // propagate out of collectAll().\n      auto [x, y, z] = co_await folly::coro::collectAll(\n          [&]() -> folly::coro::Task<int> {\n            co_await folly::coro::co_reschedule_on_current_executor;\n            if (throwError) {\n              throw ErrorA{};\n            }\n            co_return 1;\n          }(),\n          [&]() -> folly::coro::Task<int> {\n            if (throwError) {\n              throw ErrorB{};\n            }\n            co_return 2;\n          }(),\n          [&]() -> folly::coro::Task<int> {\n            if (throwError) {\n              throw ErrorC{};\n            }\n            co_return 3;\n          }());\n      (void)x;\n      (void)y;\n      (void)z;\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorB&) {\n      caughtException = true;\n    }\n  }());\n  EXPECT_TRUE(caughtException);\n}\n\nTEST_F(CollectAllTest, SynchronousCompletionInLoopDoesntCauseStackOverflow) {\n  // This test checks that collectAll() is using symmetric transfer to\n  // resume the awaiting coroutine without consume stack-space.\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int i = 0; i < 1'000'000; ++i) {\n      auto [n, s] = co_await folly::coro::collectAll(\n          []() -> folly::coro::Task<int> { co_return 123; }(),\n          []() -> folly::coro::Task<std::string> { co_return \"abc\"; }());\n      EXPECT_EQ(n, 123);\n      EXPECT_EQ(s, \"abc\");\n    }\n  }());\n}\n\nstruct OperationCancelled : std::exception {};\n\ntemplate <\n    typename Iter,\n    typename Sentinel,\n    typename BinaryOp,\n    typename InitialValue = typename std::iterator_traits<Iter>::value_type>\nfolly::coro::Task<InitialValue> parallelAccumulate(\n    Iter begin, Sentinel end, BinaryOp op, InitialValue initialValue = {}) {\n  auto distance = std::distance(begin, end);\n  if (distance < 512) {\n    co_return std::accumulate(\n        begin, end, std::move(initialValue), std::move(op));\n  } else {\n    auto mid = begin + (distance / 2);\n    auto [first, second] = co_await folly::coro::collectAll(\n        parallelAccumulate(begin, mid, op, std::move(initialValue)),\n        parallelAccumulate(mid + 1, end, op, *mid));\n    co_return op(std::move(first), std::move(second));\n  }\n}\n\nTEST_F(CollectAllTest, ParallelAccumulate) {\n  folly::CPUThreadPoolExecutor threadPool{\n      4, std::make_shared<folly::NamedThreadFactory>(\"TestThreadPool\")};\n\n  folly::coro::blockingWait(\n      co_withExecutor(&threadPool, []() -> folly::coro::Task<void> {\n        std::vector<int> values(100'000);\n        for (int i = 0; i < 100'000; ++i) {\n          values[i] = (1337 * i) % 1'000'000;\n        }\n\n        auto result = co_await parallelAccumulate(\n            values.begin(), values.end(), [](int a, int b) {\n              return std::max(a, b);\n            });\n\n        EXPECT_EQ(999'989, result);\n      }()));\n}\n\nTEST_F(CollectAllTest, CollectAllCancelsSubtasksWhenASubtaskFails) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    try {\n      auto [a, b, c] = co_await folly::coro::collectAll(\n          []() -> folly::coro::Task<int> {\n            co_await folly::coro::sleep(10s);\n            co_return 42;\n          }(),\n          []() -> folly::coro::Task<float> {\n            co_await folly::coro::sleep(5s);\n            co_return 3.14f;\n          }(),\n          []() -> folly::coro::Task<void> {\n            co_await folly::coro::co_reschedule_on_current_executor;\n            throw ErrorA{};\n          }());\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n      (void)a;\n      (void)b;\n      (void)c;\n    } catch (const ErrorA&) {\n    }\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAllTest, CollectAllCancelsSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n    auto [a, b, c] = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAll(\n            [&]() -> folly::coro::Task<int> {\n              co_await sleepThatShouldBeCancelled(10s);\n              co_return 42;\n            }(),\n            [&]() -> folly::coro::Task<float> {\n              co_await sleepThatShouldBeCancelled(5s);\n              co_return 3.14f;\n            }(),\n            [&]() -> folly::coro::Task<void> {\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              cancelSource.requestCancellation();\n            }()));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n    EXPECT_EQ(42, a);\n    EXPECT_EQ(3.14f, b);\n    (void)c;\n  }());\n}\n\nTEST_F(CollectAllTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    });\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), folly::coro::collectAll(std::move(task)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nnamespace {\n\nclass TestRequestData : public folly::RequestData {\n public:\n  explicit TestRequestData() noexcept {}\n\n  bool hasCallback() override { return false; }\n};\n\n} // namespace\n\nTEST_F(CollectAllTest, CollectAllKeepsRequestContextOfChildTasksIndependent) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::RequestContextScopeGuard requestScope;\n\n    auto getContextData = []() {\n      return folly::RequestContext::get()->getContextData(\"test\");\n    };\n\n    auto setContextData = []() {\n      folly::RequestContext::get()->setContextData(\n          \"test\", std::make_unique<TestRequestData>());\n    };\n\n    setContextData();\n    auto initialContextData = getContextData();\n\n    auto makeChildTask = [&]() -> folly::coro::Task<void> {\n      EXPECT_TRUE(getContextData() == initialContextData);\n      folly::RequestContextScopeGuard childScope;\n      EXPECT_TRUE(getContextData() == nullptr);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == nullptr);\n      setContextData();\n      auto newContextData = getContextData();\n      EXPECT_TRUE(newContextData != nullptr);\n      EXPECT_TRUE(newContextData != initialContextData);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == newContextData);\n    };\n\n    co_await folly::coro::collectAll(makeChildTask(), makeChildTask());\n\n    EXPECT_TRUE(getContextData() == initialContextData);\n  }());\n}\n\nTEST_F(CollectAllTest, TaskWithExecutorUsage) {\n  folly::CPUThreadPoolExecutor threadPool{\n      4, std::make_shared<folly::NamedThreadFactory>(\"TestThreadPool\")};\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto [a, b] = co_await folly::coro::collectAll(\n        co_withExecutor(\n            &threadPool, []() -> folly::coro::Task<int> { co_return 42; }()),\n        co_withExecutor(&threadPool, []() -> folly::coro::Task<std::string> {\n          co_return \"coroutine\";\n        }()));\n    EXPECT_TRUE(a == 42);\n    EXPECT_TRUE(b == \"coroutine\");\n  }());\n}\n\nclass CollectAllTryTest : public testing::Test {};\n\nCO_TEST_F(CollectAllTryTest, NowTask) {\n  auto [a, b] = co_await folly::coro::collectAllTry(\n      []() -> folly::coro::now_task<void> { co_return; }(),\n      []() -> folly::coro::now_task<int> {\n        if (false) {\n          co_return 42;\n        }\n        throw ErrorA{};\n      }());\n  EXPECT_TRUE(a.hasValue());\n  EXPECT_TRUE(b.hasException());\n}\n\nTEST_F(CollectAllTryTest, WithNoArgs) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    std::tuple<> result = co_await folly::coro::collectAllTry();\n    completed = true;\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAllTryTest, OneTaskWithValue) {\n  folly::coro::Baton baton;\n  auto f = [&]() -> folly::coro::Task<std::string> {\n    co_await baton;\n    co_return \"hello\";\n  };\n\n  bool completed = false;\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [result] = co_await folly::coro::collectAllTry(f());\n    EXPECT_EQ(\"hello\", result.value());\n    completed = true;\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  executor.drain();\n\n  EXPECT_FALSE(completed);\n\n  baton.post();\n\n  // Posting the baton should have just scheduled the 'f()' coroutine\n  // for resumption on the executor but should not have executed\n  // until we drain the executor again.\n  EXPECT_FALSE(completed);\n\n  executor.drain();\n\n  EXPECT_TRUE(completed);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAllTryTest, OneTaskWithError) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto [result] =\n        co_await folly::coro::collectAllTry([&]() -> folly::coro::Task<void> {\n          if (false) {\n            co_return;\n          }\n          throw ErrorA{};\n        }());\n    EXPECT_FALSE(result.hasValue());\n    EXPECT_TRUE(result.hasException());\n    EXPECT_TRUE(result.exception().get_exception<ErrorA>() != nullptr);\n  }());\n}\n\nTEST_F(CollectAllTryTest, PartialFailure) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto [aRes, bRes, cRes, dRes] = co_await folly::coro::collectAllTry(\n        []() -> folly::coro::Task<int> { co_return 123; }(),\n        []() -> folly::coro::Task<std::string> {\n          if (true) {\n            throw ErrorA{};\n          }\n          co_return \"hello\";\n        }(),\n        []() -> folly::coro::Task<void> {\n          if (true) {\n            throw ErrorB{};\n          }\n          co_return;\n        }(),\n        []() -> folly::coro::Task<double> { co_return 3.1415; }());\n    EXPECT_TRUE(cRes.hasException());\n    EXPECT_TRUE(cRes.exception().get_exception<ErrorB>() != nullptr);\n    EXPECT_TRUE(dRes.hasValue());\n    EXPECT_EQ(3.1415, dRes.value());\n  }());\n}\n\nTEST_F(CollectAllTryTest, CollectAllTryDoesNotCancelSubtasksWhenASubtaskFails) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto [a, b, c] = co_await folly::coro::collectAllTry(\n        []() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          EXPECT_FALSE((co_await folly::coro::co_current_cancellation_token)\n                           .isCancellationRequested());\n          co_return 42;\n        }(),\n        []() -> folly::coro::Task<float> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          EXPECT_FALSE((co_await folly::coro::co_current_cancellation_token)\n                           .isCancellationRequested());\n          co_return 3.14f;\n        }(),\n        []() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          throw ErrorA{};\n        }());\n\n    EXPECT_TRUE(a.hasValue());\n    EXPECT_EQ(42, a.value());\n    EXPECT_TRUE(b.hasValue());\n    EXPECT_EQ(3.14f, b.value());\n    EXPECT_TRUE(c.hasException());\n  }());\n}\n\nTEST_F(CollectAllTryTest, CollectAllCancelsSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n    auto [a, b, c] = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAllTry(\n            [&]() -> folly::coro::Task<int> {\n              co_await sleepThatShouldBeCancelled(10s);\n              co_return 42;\n            }(),\n            [&]() -> folly::coro::Task<float> {\n              co_await sleepThatShouldBeCancelled(5s);\n              co_return 3.14f;\n            }(),\n            [&]() -> folly::coro::Task<void> {\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              cancelSource.requestCancellation();\n            }()));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n    EXPECT_EQ(42, a.value());\n    EXPECT_EQ(3.14f, b.value());\n    EXPECT_TRUE(c.hasValue());\n  }());\n}\n\nTEST_F(CollectAllTryTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    });\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), folly::coro::collectAllTry(std::move(task)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllTryTest, KeepsRequestContextOfChildTasksIndependent) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::RequestContextScopeGuard requestScope;\n\n    auto getContextData = []() {\n      return folly::RequestContext::get()->getContextData(\"test\");\n    };\n\n    auto setContextData = []() {\n      folly::RequestContext::get()->setContextData(\n          \"test\", std::make_unique<TestRequestData>());\n    };\n\n    setContextData();\n    auto initialContextData = getContextData();\n\n    auto makeChildTask = [&]() -> folly::coro::Task<void> {\n      EXPECT_TRUE(getContextData() == initialContextData);\n      folly::RequestContextScopeGuard childScope;\n      EXPECT_TRUE(getContextData() == nullptr);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == nullptr);\n      setContextData();\n      auto newContextData = getContextData();\n      EXPECT_TRUE(newContextData != nullptr);\n      EXPECT_TRUE(newContextData != initialContextData);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == newContextData);\n    };\n\n    co_await folly::coro::collectAllTry(makeChildTask(), makeChildTask());\n\n    EXPECT_TRUE(getContextData() == initialContextData);\n  }());\n}\n\nclass CollectAllRangeTest : public testing::Test {};\n\nTEST_F(CollectAllRangeTest, EmptyRangeOfVoidTask) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    std::vector<folly::coro::Task<void>> tasks;\n    auto collectTask = folly::coro::collectAllRange(std::move(tasks));\n    static_assert(\n        std::is_void<\n            folly::coro::semi_await_result_t<decltype(collectTask)>>::value,\n        \"Result of awaiting collectAllRange() of Task<void> should be void\");\n    co_await std::move(collectTask);\n  }());\n}\n\nTEST_F(CollectAllRangeTest, RangeOfVoidAllSucceeding) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int count = 0;\n    auto makeTask = [&]() -> folly::coro::Task<void> {\n      ++count;\n      co_return;\n    };\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n\n    co_await folly::coro::collectAllRange(std::move(tasks));\n\n    EXPECT_EQ(3, count);\n  }());\n}\n\nTEST_F(CollectAllRangeTest, RangeOfVoidSomeFailing) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int count = 0;\n    auto makeTask = [&]() -> folly::coro::Task<void> {\n      if ((++count % 3) == 0) {\n        throw ErrorA{};\n      }\n      co_return;\n    };\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n\n    try {\n      co_await folly::coro::collectAllRange(std::move(tasks));\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorA&) {\n    }\n\n    EXPECT_EQ(5, count);\n  }());\n}\n\nTEST_F(CollectAllRangeTest, RangeOfNonVoid) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int count = 0;\n    auto makeTask = [&]() -> folly::coro::Task<int> {\n      using namespace std::literals::chrono_literals;\n      int x = count++;\n      if ((x % 20) == 0) {\n        co_await folly::coro::co_reschedule_on_current_executor;\n      }\n      co_return x;\n    };\n\n    constexpr int taskCount = 50;\n\n    std::vector<folly::coro::Task<int>> tasks;\n    for (int i = 0; i < taskCount; ++i) {\n      tasks.push_back(makeTask());\n    }\n\n    EXPECT_EQ(0, count);\n\n    std::vector<int> results =\n        co_await folly::coro::collectAllRange(std::move(tasks));\n\n    EXPECT_EQ(taskCount, results.size());\n    EXPECT_EQ(taskCount, count);\n\n    for (int i = 0; i < taskCount; ++i) {\n      EXPECT_EQ(i, results[i]);\n    }\n  }());\n}\n\nTEST_F(CollectAllRangeTest, SubtasksCancelledWhenASubtaskFails) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    bool consumedAllTasks = false;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      for (int i = 0; i < 10; ++i) {\n        co_yield folly::coro::sleep(10s);\n      }\n\n      co_yield []() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        throw ErrorA{};\n      }();\n\n      for (int i = 0; i < 10; ++i) {\n        co_yield folly::coro::sleep(10s);\n      }\n\n      consumedAllTasks = true;\n    };\n\n    auto start = std::chrono::steady_clock::now();\n    try {\n      co_await folly::coro::collectAllRange(generateTasks());\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorA&) {\n    }\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n    EXPECT_TRUE(consumedAllTasks);\n  }());\n}\n\nTEST_F(CollectAllRangeTest, FailsWithErrorOfFirstTaskToFailWhenMultipleErrors) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    try {\n      co_await folly::coro::collectAllRange(\n          []() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n            co_yield folly::coro::sleep(1s);\n            co_yield []() -> folly::coro::Task<> {\n              co_await folly::coro::sleep(1s);\n              throw ErrorA{};\n            }();\n            co_yield []() -> folly::coro::Task<> {\n              co_await folly::coro::co_reschedule_on_current_executor;\n              throw ErrorB{};\n            }();\n            co_yield folly::coro::sleep(2s);\n          }());\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorB&) {\n    }\n  }());\n}\n\nTEST_F(CollectAllRangeTest, SubtasksCancelledWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n\n    bool consumedAllTasks = false;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      for (int i = 0; i < 10; ++i) {\n        co_yield folly::coro::sleepReturnEarlyOnCancel(10s);\n      }\n\n      co_yield [&]() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        cancelSource.requestCancellation();\n\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        EXPECT_TRUE(token.isCancellationRequested());\n      }();\n\n      consumedAllTasks = true;\n    };\n\n    auto start = std::chrono::steady_clock::now();\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), folly::coro::collectAllRange(generateTasks()));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n    EXPECT_TRUE(consumedAllTasks);\n  }());\n}\n\nTEST_F(CollectAllRangeTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        auto ex = co_await folly::coro::co_current_executor;\n        scope.add(co_withExecutor(\n            ex,\n            folly::coro::co_withCancellation(\n                token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                  auto innerToken =\n                      co_await folly::coro::co_current_cancellation_token;\n                  co_await baton;\n                  EXPECT_TRUE(innerToken.isCancellationRequested());\n                }))));\n      });\n    };\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), folly::coro::collectAllRange(generateTasks()));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllRangeTest, KeepsRequestContextOfChildTasksIndependent) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::RequestContextScopeGuard requestScope;\n\n    auto getContextData = []() {\n      return folly::RequestContext::get()->getContextData(\"test\");\n    };\n\n    auto setContextData = []() {\n      folly::RequestContext::get()->setContextData(\n          \"test\", std::make_unique<TestRequestData>());\n    };\n\n    setContextData();\n    auto initialContextData = getContextData();\n\n    auto makeChildTask = [&]() -> folly::coro::Task<void> {\n      EXPECT_TRUE(getContextData() == initialContextData);\n      folly::RequestContextScopeGuard childScope;\n      EXPECT_TRUE(getContextData() == nullptr);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == nullptr);\n      setContextData();\n      auto newContextData = getContextData();\n      EXPECT_TRUE(newContextData != nullptr);\n      EXPECT_TRUE(newContextData != initialContextData);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == newContextData);\n    };\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.emplace_back(makeChildTask());\n    tasks.emplace_back(makeChildTask());\n    tasks.emplace_back(makeChildTask());\n\n    co_await folly::coro::collectAllRange(std::move(tasks));\n\n    EXPECT_TRUE(getContextData() == initialContextData);\n  }());\n}\n\nTEST_F(CollectAllRangeTest, VectorOfTaskWithExecutorUsage) {\n  folly::CPUThreadPoolExecutor threadPool{\n      4, std::make_shared<folly::NamedThreadFactory>(\"TestThreadPool\")};\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    std::vector<folly::coro::TaskWithExecutor<int>> tasks;\n    for (int i = 0; i < 4; ++i) {\n      tasks.push_back(co_withExecutor(\n          &threadPool,\n          [](int idx) -> folly::coro::Task<int> { co_return idx + 1; }(i)));\n    }\n\n    auto results = co_await folly::coro::collectAllRange(std::move(tasks));\n    EXPECT_TRUE(results.size() == 4);\n    EXPECT_TRUE(results[0] == 1);\n    EXPECT_TRUE(results[1] == 2);\n    EXPECT_TRUE(results[2] == 3);\n    EXPECT_TRUE(results[3] == 4);\n  }());\n}\n\nTEST_F(CollectAllRangeTest, GeneratorFromRange) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::CancellableAsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> {\n      co_await folly::coro::sleep(std::chrono::milliseconds(100 * i));\n      co_return i;\n    };\n    std::vector<folly::coro::Task<int>> tasks;\n    for (int i = 5; i > 0; --i) {\n      tasks.push_back(makeTask(i));\n    }\n\n    auto results =\n        folly::coro::makeUnorderedAsyncGenerator(scope, std::move(tasks));\n    // co_await doesn't work inside EXPECT_EQ\n    EXPECT_TRUE(*(co_await results.next()) == 1);\n    EXPECT_TRUE(*(co_await results.next()) == 2);\n    EXPECT_TRUE(*(co_await results.next()) == 3);\n    EXPECT_TRUE(*(co_await results.next()) == 4);\n    EXPECT_TRUE(*(co_await results.next()) == 5);\n    EXPECT_FALSE(co_await results.next());\n    co_await scope.joinAsync();\n  }());\n}\n\nCO_TEST_F(CollectAllRangeTest, GeneratorFromVoidRange) {\n  folly::coro::CancellableAsyncScope scope;\n  auto makeTask = [](int i) -> folly::coro::Task<void> {\n    co_await folly::coro::sleep(std::chrono::milliseconds(100 * i));\n    if (i == 4) {\n      throw std::runtime_error(\"fail on 4\");\n    }\n    co_return;\n  };\n  std::vector<folly::coro::Task<void>> tasks;\n  for (int i = 5; i > 0; --i) {\n    tasks.push_back(makeTask(i));\n  }\n\n  auto results =\n      folly::coro::makeUnorderedAsyncGenerator(scope, std::move(tasks));\n  // The first 3 results should be produced normally\n  co_await results.next();\n  co_await results.next();\n  co_await results.next();\n  // The next should generate an exception\n  try {\n    co_await results.next();\n    ADD_FAILURE() << \"expected an exception\";\n  } catch (const std::runtime_error& ex) {\n    EXPECT_STREQ(ex.what(), \"fail on 4\");\n  }\n  co_await scope.joinAsync();\n}\n\nTEST_F(CollectAllRangeTest, GeneratorFromRangePartialConsume) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> { co_return i; };\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      for (int i = 5; i > 0; --i) {\n        co_yield makeTask(i);\n      }\n    };\n\n    auto results =\n        folly::coro::makeUnorderedAsyncGenerator(scope, generateTasks());\n    for (int i = 0; i < 3; ++i) {\n      co_await results.next();\n    }\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllRangeTest, GeneratorFromRangeFailed) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> {\n      co_await folly::coro::sleep(std::chrono::milliseconds(100 * i));\n      co_return i;\n    };\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield []() -> folly::coro::Task<int> {\n        co_await folly::coro::sleep(std::chrono::milliseconds(350));\n        co_yield folly::coro::co_error(std::runtime_error(\"foo\"));\n      }();\n      for (int i = 5; i > 0; --i) {\n        co_yield makeTask(i);\n      }\n    };\n\n    auto results =\n        folly::coro::makeUnorderedAsyncGenerator(scope, generateTasks());\n    // co_await doesn't work inside EXPECT_EQ\n    EXPECT_TRUE(*(co_await results.next()) == 1);\n    EXPECT_TRUE(*(co_await results.next()) == 2);\n    EXPECT_TRUE(*(co_await results.next()) == 3);\n    EXPECT_TRUE((co_await co_awaitTry(results.next()))\n                    .hasException<std::runtime_error>());\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllRangeTest, GeneratorFromRangeCancelled) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> {\n      co_await folly::coro::sleep(std::chrono::milliseconds(1000 * i));\n      co_return i;\n    };\n    folly::CancellationSource cancelSource;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      for (int i = 0; i < 10; ++i) {\n        co_yield makeTask(i);\n        if (i == 4) {\n          cancelSource.requestCancellation();\n        }\n      }\n    };\n    auto start = std::chrono::steady_clock::now();\n    auto results =\n        folly::coro::makeUnorderedAsyncGenerator(scope, generateTasks());\n    auto result = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), co_awaitTry(results.next()));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, std::chrono::milliseconds(1000));\n    EXPECT_TRUE(result.hasException<folly::OperationCancelled>());\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllRangeTest, GeneratorFromRangeCancelledFromScope) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::CancellableAsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> {\n      co_await folly::coro::sleep(std::chrono::milliseconds(1000 * i));\n      co_return i;\n    };\n    folly::CancellationSource cancelSource;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      for (int i = 0; i < 10; ++i) {\n        co_yield makeTask(i);\n        if (i == 4) {\n          scope.requestCancellation();\n        }\n      }\n    };\n    auto start = std::chrono::steady_clock::now();\n    auto results =\n        folly::coro::makeUnorderedAsyncGenerator(scope, generateTasks());\n    auto result = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), co_awaitTry(results.next()));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, std::chrono::milliseconds(1000));\n    EXPECT_TRUE(result.hasException<folly::OperationCancelled>());\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllRangeTest, GeneratorFromEmptyRange) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncScope scope;\n    std::vector<folly::coro::Task<void>> tasks;\n    auto results =\n        folly::coro::makeUnorderedAsyncGenerator(scope, std::move(tasks));\n    while (auto next = co_await results.next()) {\n      EXPECT_FALSE(true) << \"Unexpected result\";\n    }\n    co_await scope.joinAsync();\n  }());\n}\n\nclass CollectAllTryRangeTest : public testing::Test {};\n\nTEST_F(CollectAllTryRangeTest, RangeOfVoidSomeFailing) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int count = 0;\n    auto makeTask = [&]() -> folly::coro::Task<void> {\n      if ((++count % 3) == 0) {\n        throw ErrorA{};\n      }\n      co_return;\n    };\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n\n    auto results = co_await folly::coro::collectAllTryRange(std::move(tasks));\n\n    EXPECT_EQ(5, results.size());\n    EXPECT_TRUE(results[0].hasValue());\n    EXPECT_TRUE(results[1].hasValue());\n    EXPECT_TRUE(results[2].hasException());\n    EXPECT_TRUE(results[3].hasValue());\n    EXPECT_TRUE(results[4].hasValue());\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, RangeOfValueSomeFailing) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int count = 0;\n    auto makeTask = [&]() -> folly::coro::Task<std::string> {\n      if ((++count % 3) == 0) {\n        throw ErrorA{};\n      }\n      co_return \"testing\";\n    };\n\n    std::vector<folly::coro::Task<std::string>> tasks;\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n    tasks.push_back(makeTask());\n\n    auto results = co_await folly::coro::collectAllTryRange(std::move(tasks));\n\n    EXPECT_EQ(6, results.size());\n    EXPECT_TRUE(results[0].hasValue());\n    EXPECT_EQ(\"testing\", results[0].value());\n    EXPECT_TRUE(results[1].hasValue());\n    EXPECT_EQ(\"testing\", results[1].value());\n    EXPECT_TRUE(results[2].hasException());\n    EXPECT_TRUE(results[3].hasValue());\n    EXPECT_EQ(\"testing\", results[3].value());\n    EXPECT_TRUE(results[4].hasValue());\n    EXPECT_EQ(\"testing\", results[4].value());\n    EXPECT_TRUE(results[5].hasException());\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, NotCancelledWhenSubtaskFails) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      auto makeValidationTask = []() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        EXPECT_FALSE(token.isCancellationRequested());\n      };\n\n      co_yield makeValidationTask();\n      co_yield makeValidationTask();\n\n      co_yield []() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        throw ErrorA{};\n      }();\n\n      co_yield makeValidationTask();\n      co_yield makeValidationTask();\n    };\n\n    auto results = co_await folly::coro::collectAllTryRange(generateTasks());\n    EXPECT_EQ(5, results.size());\n    EXPECT_TRUE(results[0].hasValue());\n    EXPECT_TRUE(results[1].hasValue());\n    EXPECT_TRUE(results[2].hasException());\n    EXPECT_TRUE(results[3].hasValue());\n    EXPECT_TRUE(results[4].hasValue());\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, SubtasksCancelledWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n\n    bool consumedAllTasks = false;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      for (int i = 0; i < 10; ++i) {\n        co_yield sleepThatShouldBeCancelled(10s);\n      }\n\n      co_yield [&]() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        cancelSource.requestCancellation();\n\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        EXPECT_TRUE(token.isCancellationRequested());\n      }();\n\n      consumedAllTasks = true;\n    };\n\n    auto start = std::chrono::steady_clock::now();\n    auto results = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAllTryRange(generateTasks()));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_EQ(11, results.size());\n    for (auto& result : results) {\n      EXPECT_TRUE(result.hasValue());\n    }\n    EXPECT_LT(end - start, 1s);\n    EXPECT_TRUE(consumedAllTasks);\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        auto ex = co_await folly::coro::co_current_executor;\n        scope.add(co_withExecutor(\n            ex,\n            folly::coro::co_withCancellation(\n                token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                  auto innerToken =\n                      co_await folly::coro::co_current_cancellation_token;\n                  co_await baton;\n                  EXPECT_TRUE(innerToken.isCancellationRequested());\n                }))));\n      });\n    };\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAllTryRange(generateTasks()));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, KeepsRequestContextOfChildTasksIndependent) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::RequestContextScopeGuard requestScope;\n\n    auto getContextData = []() {\n      return folly::RequestContext::get()->getContextData(\"test\");\n    };\n\n    auto setContextData = []() {\n      folly::RequestContext::get()->setContextData(\n          \"test\", std::make_unique<TestRequestData>());\n    };\n\n    setContextData();\n    auto initialContextData = getContextData();\n\n    auto makeChildTask = [&]() -> folly::coro::Task<void> {\n      EXPECT_TRUE(getContextData() == initialContextData);\n      folly::RequestContextScopeGuard childScope;\n      EXPECT_TRUE(getContextData() == nullptr);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == nullptr);\n      setContextData();\n      auto newContextData = getContextData();\n      EXPECT_TRUE(newContextData != nullptr);\n      EXPECT_TRUE(newContextData != initialContextData);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == newContextData);\n    };\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.emplace_back(makeChildTask());\n    tasks.emplace_back(makeChildTask());\n    tasks.emplace_back(makeChildTask());\n\n    co_await folly::coro::collectAllTryRange(std::move(tasks));\n\n    EXPECT_TRUE(getContextData() == initialContextData);\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, GeneratorFromRange) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::CancellableAsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> {\n      co_await folly::coro::sleep(std::chrono::milliseconds(100 * i));\n      co_return i;\n    };\n    std::vector<folly::coro::Task<int>> tasks;\n    for (int i = 5; i > 0; --i) {\n      tasks.push_back(makeTask(i));\n    }\n\n    auto results =\n        folly::coro::makeUnorderedTryAsyncGenerator(scope, std::move(tasks));\n    // co_await doesn't work inside EXPECT_EQ\n    EXPECT_TRUE(**(co_await results.next()) == 1);\n    EXPECT_TRUE(**(co_await results.next()) == 2);\n    EXPECT_TRUE(**(co_await results.next()) == 3);\n    EXPECT_TRUE(**(co_await results.next()) == 4);\n    EXPECT_TRUE(**(co_await results.next()) == 5);\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, GeneratorFromRangeFailed) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> {\n      co_await folly::coro::sleep(std::chrono::milliseconds(200 * i));\n      co_return i;\n    };\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield []() -> folly::coro::Task<int> {\n        co_await folly::coro::sleep(std::chrono::milliseconds(700));\n        co_yield folly::coro::co_error(std::runtime_error(\"foo\"));\n      }();\n      for (int i = 5; i > 0; --i) {\n        co_yield makeTask(i);\n      }\n    };\n\n    auto results =\n        folly::coro::makeUnorderedTryAsyncGenerator(scope, generateTasks());\n    // co_await doesn't work inside EXPECT_EQ\n    EXPECT_TRUE(**(co_await results.next()) == 1);\n    EXPECT_TRUE(**(co_await results.next()) == 2);\n    EXPECT_TRUE(**(co_await results.next()) == 3);\n    EXPECT_TRUE((co_await results.next())->hasException<std::runtime_error>());\n    EXPECT_TRUE(**(co_await results.next()) == 4);\n    EXPECT_TRUE(**(co_await results.next()) == 5);\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllTryRangeTest, GeneratorFromRangeCancelled) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncScope scope;\n    auto makeTask = [](int i) -> folly::coro::Task<int> {\n      co_await folly::coro::sleep(std::chrono::milliseconds(1000 * i));\n      co_return i;\n    };\n    folly::CancellationSource cancelSource;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      for (int i = 1; i < 10; ++i) {\n        co_yield makeTask(i);\n        if (i == 4) {\n          cancelSource.requestCancellation();\n        }\n      }\n    };\n    auto start = std::chrono::steady_clock::now();\n    auto results =\n        folly::coro::makeUnorderedTryAsyncGenerator(scope, generateTasks());\n    auto result = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), results.next());\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, std::chrono::milliseconds(1000));\n    EXPECT_TRUE(result->hasException<folly::OperationCancelled>());\n    co_await scope.joinAsync();\n  }());\n}\n\nclass CollectAllWindowedTest : public testing::Test {};\n\nTEST_F(CollectAllWindowedTest, ConcurrentTasks) {\n  folly::CPUThreadPoolExecutor threadPool{\n      4, std::make_shared<folly::NamedThreadFactory>(\"TestThreadPool\")};\n\n  using namespace folly::coro;\n\n  auto results = blockingWait(collectAllWindowed(\n      [&]() -> Generator<Task<std::string>&&> {\n        for (int i = 0; i < 10'000; ++i) {\n          co_yield [](int idx) -> Task<std::string> {\n            co_await folly::coro::co_reschedule_on_current_executor;\n            co_return folly::to<std::string>(idx);\n          }(i);\n        }\n      }(),\n      10));\n\n  EXPECT_EQ(10'000, results.size());\n  for (int i = 0; i < 10'000; ++i) {\n    EXPECT_EQ(folly::to<std::string>(i), results[i]);\n  }\n}\n\nTEST_F(CollectAllWindowedTest, WithGeneratorOfTaskOfValue) {\n  using namespace std::literals::chrono_literals;\n\n  const std::size_t maxConcurrency = 10;\n  std::atomic<int> activeCount{0};\n  std::atomic<int> completedCount{0};\n  auto makeTask = [&](int index) -> folly::coro::Task<int> {\n    auto count = ++activeCount;\n    CHECK_LE(count, maxConcurrency);\n\n    // Reschedule a variable number of times so that tasks may complete out\n    // of order.\n    for (int i = 0; i < index % 5; ++i) {\n      co_await folly::coro::co_reschedule_on_current_executor;\n    }\n\n    --activeCount;\n    ++completedCount;\n\n    co_return index;\n  };\n\n  auto makeTaskGenerator =\n      [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n    for (int i = 0; i < 100; ++i) {\n      co_yield makeTask(i);\n    }\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto results = co_await folly::coro::collectAllWindowed(\n        makeTaskGenerator(), maxConcurrency);\n    EXPECT_EQ(100, results.size());\n    for (int i = 0; i < 100; ++i) {\n      EXPECT_EQ(i, results[i]);\n    }\n  }());\n\n  EXPECT_EQ(0, activeCount.load());\n  EXPECT_EQ(100, completedCount);\n}\n\nTEST_F(CollectAllWindowedTest, WithGeneratorOfTaskOfVoid) {\n  using namespace std::literals::chrono_literals;\n\n  const std::size_t maxConcurrency = 10;\n  std::atomic<int> activeCount{0};\n  std::atomic<int> completedCount{0};\n  auto makeTask = [&]() -> folly::coro::Task<void> {\n    auto count = ++activeCount;\n    CHECK_LE(count, maxConcurrency);\n    co_await folly::coro::co_reschedule_on_current_executor;\n    --activeCount;\n    ++completedCount;\n  };\n\n  auto makeTaskGenerator =\n      [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n    for (int i = 0; i < 100; ++i) {\n      co_yield makeTask();\n    }\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    co_await folly::coro::collectAllWindowed(\n        makeTaskGenerator(), maxConcurrency);\n  }());\n\n  EXPECT_EQ(0, activeCount.load());\n  EXPECT_EQ(100, completedCount);\n}\n\nTEST_F(CollectAllWindowedTest, VectorOfVoidTask) {\n  using namespace std::literals::chrono_literals;\n\n  int count = 0;\n  auto makeTask = [&]() -> folly::coro::Task<void> {\n    co_await folly::coro::co_reschedule_on_current_executor;\n    ++count;\n  };\n\n  std::vector<folly::coro::Task<void>> tasks;\n  for (int i = 0; i < 10; ++i) {\n    tasks.push_back(makeTask());\n  }\n\n  folly::coro::blockingWait(\n      folly::coro::collectAllWindowed(std::move(tasks), 5));\n\n  EXPECT_EQ(10, count);\n}\n\nTEST_F(CollectAllWindowedTest, VectorOfValueTask) {\n  using namespace std::literals::chrono_literals;\n\n  int count = 0;\n  auto makeTask = [&](int i) -> folly::coro::Task<std::unique_ptr<int>> {\n    co_await folly::coro::co_reschedule_on_current_executor;\n    ++count;\n    co_return std::make_unique<int>(i);\n  };\n\n  std::vector<folly::coro::Task<std::unique_ptr<int>>> tasks;\n  for (int i = 0; i < 10; ++i) {\n    tasks.push_back(makeTask(i));\n  }\n\n  auto results = folly::coro::blockingWait(\n      folly::coro::collectAllWindowed(std::move(tasks), 5));\n\n  EXPECT_EQ(10, count);\n  EXPECT_EQ(10, results.size());\n  for (int i = 0; i < 10; ++i) {\n    EXPECT_EQ(i, *results[i]);\n  }\n}\n\nTEST_F(CollectAllWindowedTest, MultipleFailuresPropagatesFirstError) {\n  try {\n    [[maybe_unused]] auto results = folly::coro::blockingWait(\n        folly::coro::collectAllWindowed(\n            []() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n              for (int i = 0; i < 10; ++i) {\n                co_yield [](int idx) -> folly::coro::Task<int> {\n                  using namespace std::literals::chrono_literals;\n                  if (idx == 3) {\n                    co_await folly::coro::co_reschedule_on_current_executor;\n                    co_await folly::coro::co_reschedule_on_current_executor;\n                    throw ErrorA{};\n                  } else if (idx == 7) {\n                    co_await folly::coro::co_reschedule_on_current_executor;\n                    throw ErrorB{};\n                  }\n                  co_return idx;\n                }(i);\n              }\n            }(),\n            5));\n    ADD_FAILURE() << \"Hit unexpected codepath\"; // Should have thrown.\n  } catch (const ErrorB&) {\n    // Expected.\n  }\n}\n\nTEST_F(CollectAllWindowedTest, SubtasksCancelledWhenASubtaskFails) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    bool consumedAllTasks = false;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield []() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        throw ErrorA{};\n      }();\n\n      for (int i = 0; i < 10; ++i) {\n        co_yield folly::coro::sleep(10s);\n      }\n\n      consumedAllTasks = true;\n    };\n\n    auto start = std::chrono::steady_clock::now();\n    try {\n      co_await folly::coro::collectAllWindowed(generateTasks(), 2);\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorA&) {\n    }\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n    EXPECT_TRUE(consumedAllTasks);\n  }());\n}\n\nTEST_F(CollectAllWindowedTest, SubtasksCancelledWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n\n    bool consumedAllTasks = false;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield sleepThatShouldBeCancelled(10s);\n      co_yield sleepThatShouldBeCancelled(10s);\n\n      co_yield [&]() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        cancelSource.requestCancellation();\n\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        EXPECT_TRUE(token.isCancellationRequested());\n      }();\n\n      co_yield sleepThatShouldBeCancelled(10s);\n      co_yield sleepThatShouldBeCancelled(10s);\n\n      consumedAllTasks = true;\n    };\n\n    auto start = std::chrono::steady_clock::now();\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAllWindowed(generateTasks(), 4));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n    EXPECT_TRUE(consumedAllTasks);\n  }());\n}\n\nTEST_F(CollectAllWindowedTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        auto ex = co_await folly::coro::co_current_executor;\n        scope.add(co_withExecutor(\n            ex,\n            folly::coro::co_withCancellation(\n                token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                  auto innerToken =\n                      co_await folly::coro::co_current_cancellation_token;\n                  co_await baton;\n                  EXPECT_TRUE(innerToken.isCancellationRequested());\n                }))));\n      });\n    };\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAllWindowed(generateTasks(), 1));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST_F(CollectAllWindowedTest, KeepsRequestContextOfChildTasksIndependent) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::RequestContextScopeGuard requestScope;\n\n    auto getContextData = []() {\n      return folly::RequestContext::get()->getContextData(\"test\");\n    };\n\n    auto setContextData = []() {\n      folly::RequestContext::get()->setContextData(\n          \"test\", std::make_unique<TestRequestData>());\n    };\n\n    setContextData();\n    auto initialContextData = getContextData();\n\n    auto makeChildTask = [&]() -> folly::coro::Task<void> {\n      EXPECT_TRUE(getContextData() == initialContextData);\n      folly::RequestContextScopeGuard childScope;\n      EXPECT_TRUE(getContextData() == nullptr);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == nullptr);\n      setContextData();\n      auto newContextData = getContextData();\n      EXPECT_TRUE(newContextData != nullptr);\n      EXPECT_TRUE(newContextData != initialContextData);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_TRUE(getContextData() == newContextData);\n    };\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.emplace_back(makeChildTask());\n    tasks.emplace_back(makeChildTask());\n    tasks.emplace_back(makeChildTask());\n\n    co_await folly::coro::collectAllWindowed(std::move(tasks), 2);\n\n    EXPECT_TRUE(getContextData() == initialContextData);\n  }());\n}\n\nTEST_F(CollectAllWindowedTest, VectorOfTaskWithExecutorUsage) {\n  folly::CPUThreadPoolExecutor threadPool{\n      4, std::make_shared<folly::NamedThreadFactory>(\"TestThreadPool\")};\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    std::vector<folly::coro::TaskWithExecutor<int>> tasks;\n    for (int i = 0; i < 4; ++i) {\n      tasks.push_back(co_withExecutor(\n          &threadPool,\n          [](int idx) -> folly::coro::Task<int> { co_return idx + 1; }(i)));\n    }\n\n    auto results =\n        co_await folly::coro::collectAllWindowed(std::move(tasks), 2);\n    EXPECT_TRUE(results.size() == 4);\n    EXPECT_TRUE(results[0] == 1);\n    EXPECT_TRUE(results[1] == 2);\n    EXPECT_TRUE(results[2] == 3);\n    EXPECT_TRUE(results[3] == 4);\n  }());\n}\n\nclass CollectAllTryWindowedTest : public testing::Test {};\n\nTEST_F(CollectAllTryWindowedTest, PartialFailure) {\n  auto results = folly::coro::blockingWait(\n      folly::coro::collectAllTryWindowed(\n          []() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n            for (int i = 0; i < 10; ++i) {\n              co_yield [](int idx) -> folly::coro::Task<int> {\n                using namespace std::literals::chrono_literals;\n                if (idx == 3) {\n                  co_await folly::coro::co_reschedule_on_current_executor;\n                  co_await folly::coro::co_reschedule_on_current_executor;\n                  throw ErrorA{};\n                } else if (idx == 7) {\n                  co_await folly::coro::co_reschedule_on_current_executor;\n                  throw ErrorB{};\n                }\n                co_return idx;\n              }(i);\n            }\n          }(),\n          5));\n  EXPECT_EQ(10, results.size());\n\n  for (int i = 0; i < 10; ++i) {\n    if (i == 3) {\n      EXPECT_TRUE(results[i].hasException());\n      EXPECT_TRUE(results[i].exception().is_compatible_with<ErrorA>());\n    } else if (i == 7) {\n      EXPECT_TRUE(results[i].hasException());\n      EXPECT_TRUE(results[i].exception().is_compatible_with<ErrorB>());\n    } else {\n      EXPECT_TRUE(results[i].hasValue());\n      EXPECT_EQ(i, results[i].value());\n    }\n  }\n}\n\nTEST_F(CollectAllTryWindowedTest, GeneratorFailure) {\n  int active = 0;\n  int started = 0;\n  auto makeTask = [&](int i) -> folly::coro::Task<void> {\n    ++active;\n    ++started;\n    for (int j = 0; j < (i % 3); ++j) {\n      co_await folly::coro::co_reschedule_on_current_executor;\n    }\n    --active;\n  };\n\n  auto generateTasks =\n      [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n    for (int i = 0; i < 10; ++i) {\n      co_yield makeTask(i);\n    }\n    throw ErrorA{};\n  };\n\n  try {\n    [[maybe_unused]] auto results = folly::coro::blockingWait(\n        folly::coro::collectAllTryWindowed(generateTasks(), 5));\n    ADD_FAILURE() << \"Hit unexpected codepath\";\n  } catch (const ErrorA&) {\n  }\n\n  // Even if the generator throws an exception we should still have launched\n  // and waited for completion all of the prior tasks in the sequence.\n  EXPECT_EQ(10, started);\n  EXPECT_EQ(0, active);\n}\n\nTEST_F(CollectAllTryWindowedTest, NotCancelledWhenSubtaskFails) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield []() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        throw ErrorA{};\n      }();\n\n      auto makeValidationTask = []() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        EXPECT_FALSE(token.isCancellationRequested());\n      };\n\n      co_yield makeValidationTask();\n      co_yield makeValidationTask();\n    };\n\n    auto results =\n        co_await folly::coro::collectAllTryWindowed(generateTasks(), 2);\n    EXPECT_EQ(3, results.size());\n    EXPECT_TRUE(results[0].hasException());\n    EXPECT_TRUE(results[1].hasValue());\n    EXPECT_TRUE(results[2].hasValue());\n  }());\n}\n\nTEST_F(CollectAllTryWindowedTest, SubtasksCancelledWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n\n    bool consumedAllTasks = false;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield sleepThatShouldBeCancelled(10s);\n      co_yield sleepThatShouldBeCancelled(10s);\n\n      co_yield [&]() -> folly::coro::Task<void> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        cancelSource.requestCancellation();\n\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        EXPECT_TRUE(token.isCancellationRequested());\n      }();\n\n      co_yield sleepThatShouldBeCancelled(10s);\n      co_yield sleepThatShouldBeCancelled(10s);\n\n      consumedAllTasks = true;\n    };\n\n    auto start = std::chrono::steady_clock::now();\n    auto results = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAllTryWindowed(generateTasks(), 4));\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_EQ(5, results.size());\n    for (auto& result : results) {\n      EXPECT_TRUE(result.hasValue());\n    }\n    EXPECT_LT(end - start, 1s);\n    EXPECT_TRUE(consumedAllTasks);\n  }());\n}\n\nTEST_F(CollectAllTryWindowedTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n        auto token = co_await folly::coro::co_current_cancellation_token;\n        auto ex = co_await folly::coro::co_current_executor;\n        scope.add(co_withExecutor(\n            ex,\n            folly::coro::co_withCancellation(\n                token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                  auto innerToken =\n                      co_await folly::coro::co_current_cancellation_token;\n                  co_await baton;\n                  EXPECT_TRUE(innerToken.isCancellationRequested());\n                }))));\n      });\n    };\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAllTryWindowed(generateTasks(), 1));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nclass CollectAnyTest : public testing::Test {};\n\nTEST_F(CollectAnyTest, OneTaskWithValue) {\n  folly::coro::Baton baton;\n  auto f = [&]() -> folly::coro::Task<std::string> {\n    co_await baton;\n    co_return \"hello\";\n  };\n\n  bool completed = false;\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [index, result] = co_await folly::coro::collectAny(f());\n    EXPECT_EQ(\"hello\", result.value());\n    EXPECT_EQ(0, index);\n    completed = true;\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  executor.drain();\n\n  EXPECT_FALSE(completed);\n\n  baton.post();\n\n  // Posting the baton should have just scheduled the 'f()' coroutine\n  // for resumption on the executor but should not have executed\n  // until we drain the executor again.\n  EXPECT_FALSE(completed);\n\n  executor.drain();\n\n  EXPECT_TRUE(completed);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyTest, OneVoidTask) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that 'void' results are\n    // promoted to folly::Unit when placed in a tuple.\n    std::pair<std::size_t, folly::Try<void>> result =\n        co_await folly::coro::collectAny([&]() -> folly::coro::Task<void> {\n          completed = true;\n          co_return;\n        }());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyTest, MoveOnlyType) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that move only results\n    // can be correctly returned\n    std::pair<std::size_t, folly::Try<std::unique_ptr<int>>> result =\n        co_await folly::coro::collectAny(\n            [&]() -> folly::coro::Task<std::unique_ptr<int>> {\n              completed = true;\n              co_return std::make_unique<int>(1);\n            }());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyTest, CollectAnyDoesntCompleteUntilAllTasksComplete) {\n  folly::coro::Baton baton1;\n  folly::coro::Baton baton2;\n  bool task1Started = false;\n  bool task2Started = false;\n  bool complete = false;\n  constexpr std::size_t taskTerminatingExpectedIndex = 1;\n\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [index, result] = co_await folly::coro::collectAny(\n        [&]() -> folly::coro::Task<int> {\n          task1Started = true;\n          co_await baton1;\n          co_return 42;\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          task2Started = true;\n          co_await baton2;\n          co_return 314;\n        }());\n    complete = true;\n    EXPECT_EQ(taskTerminatingExpectedIndex, index);\n    EXPECT_EQ(result.value(), 314);\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  EXPECT_FALSE(task1Started);\n  EXPECT_FALSE(task2Started);\n\n  executor.drain();\n\n  EXPECT_TRUE(task1Started);\n  EXPECT_TRUE(task2Started);\n  EXPECT_FALSE(complete);\n  baton2.post();\n  executor.drain();\n  EXPECT_FALSE(complete);\n  baton1.post();\n  executor.drain();\n  EXPECT_TRUE(complete);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyTest, ThrowsFirstError) {\n  bool caughtException = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    bool throwError = true;\n    // Child tasks are started in-order.\n    // The first task will reschedule itself onto the executor.\n    // The second task will fail immediately and will be the first\n    // task to fail.\n    // Then the third and first tasks will fail.\n    // As the second task failed first we should see its exception\n    // propagate out of collectAny().\n    auto [index, result] = co_await folly::coro::collectAny(\n        [&]() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          if (throwError) {\n            throw ErrorA{};\n          }\n          co_return 1;\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          if (throwError) {\n            throw ErrorB{};\n          }\n          co_return 2;\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          if (throwError) {\n            throw ErrorC{};\n          }\n          co_return 3;\n        }());\n    EXPECT_EQ(1, index);\n    try {\n      result.value();\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorB&) {\n      caughtException = true;\n    }\n  }());\n  EXPECT_TRUE(caughtException);\n}\n\nTEST_F(CollectAnyTest, CollectAnyCancelsSubtasksWhenASubtaskCompletes) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n\n    auto [index, result] = co_await folly::coro::collectAny(\n        []() -> folly::coro::Task<int> {\n          co_await sleepThatShouldBeCancelled(10s);\n          co_return 42;\n        }(),\n        []() -> folly::coro::Task<int> {\n          co_await sleepThatShouldBeCancelled(5s);\n          co_return 314;\n        }(),\n        []() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          throw ErrorA{};\n        }());\n    EXPECT_EQ(2, index);\n    try {\n      result.value();\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorA&) {\n    }\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAnyTest, CollectAnyCancelsSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n    auto [index, result] = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAny(\n            [&]() -> folly::coro::Task<int> {\n              co_await sleepThatShouldBeCancelled(10s);\n              co_return 42;\n            }(),\n            [&]() -> folly::coro::Task<int> {\n              co_await sleepThatShouldBeCancelled(5s);\n              co_return 314;\n            }(),\n            [&]() -> folly::coro::Task<int> {\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              cancelSource.requestCancellation();\n              co_await sleepThatShouldBeCancelled(15s);\n              co_return 123;\n            }()));\n    auto end = std::chrono::steady_clock::now();\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAnyTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    });\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), folly::coro::collectAny(std::move(task)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nclass CollectAnyWithoutExceptionTest : public testing::Test {};\n\nTEST_F(CollectAnyWithoutExceptionTest, OneTaskWithValue) {\n  folly::coro::Baton baton;\n  auto f = [&]() -> folly::coro::Task<std::string> {\n    co_await baton;\n    co_return \"hello\";\n  };\n\n  bool completed = false;\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [index, result] =\n        co_await folly::coro::collectAnyWithoutException(f());\n    EXPECT_EQ(\"hello\", result.value());\n    EXPECT_EQ(0, index);\n    completed = true;\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  executor.drain();\n\n  EXPECT_FALSE(completed);\n\n  baton.post();\n\n  // Posting the baton should have just scheduled the 'f()' coroutine\n  // for resumption on the executor but should not have executed\n  // until we drain the executor again.\n  EXPECT_FALSE(completed);\n\n  executor.drain();\n\n  EXPECT_TRUE(completed);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyWithoutExceptionTest, OneVoidTask) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that 'void' results are\n    // promoted to folly::Unit when placed in a tuple.\n    std::pair<std::size_t, folly::Try<void>> result =\n        co_await folly::coro::collectAnyWithoutException(\n            [&]() -> folly::coro::Task<void> {\n              completed = true;\n              co_return;\n            }());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyWithoutExceptionTest, MoveOnlyType) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that move only results\n    // can be correctly returned\n    std::pair<std::size_t, folly::Try<std::unique_ptr<int>>> result =\n        co_await folly::coro::collectAnyWithoutException(\n            [&]() -> folly::coro::Task<std::unique_ptr<int>> {\n              completed = true;\n              co_return std::make_unique<int>(1);\n            }());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyWithoutExceptionTest, DoesntCompleteUntilAllTasksComplete) {\n  folly::coro::Baton baton1;\n  folly::coro::Baton baton2;\n  bool task1Started = false;\n  bool task2Started = false;\n  bool complete = false;\n  constexpr std::size_t taskTerminatingExpectedIndex = 1;\n\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [index, result] = co_await folly::coro::collectAnyWithoutException(\n        [&]() -> folly::coro::Task<int> {\n          task1Started = true;\n          co_await baton1;\n          co_return 42;\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          task2Started = true;\n          co_await baton2;\n          co_return 314;\n        }());\n    complete = true;\n    EXPECT_EQ(taskTerminatingExpectedIndex, index);\n    EXPECT_EQ(result.value(), 314);\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  EXPECT_FALSE(task1Started);\n  EXPECT_FALSE(task2Started);\n\n  executor.drain();\n\n  EXPECT_TRUE(task1Started);\n  EXPECT_TRUE(task2Started);\n  EXPECT_FALSE(complete);\n  baton2.post();\n  executor.drain();\n  EXPECT_FALSE(complete);\n  baton1.post();\n  executor.drain();\n  EXPECT_TRUE(complete);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyWithoutExceptionTest, ThrowsLastError) {\n  bool caughtException = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    bool throwError = true;\n    // Child tasks are started in-order.\n    // Since the third task fails last we should see its exception\n    // propagate out of collectAnyWithoutException().\n    auto [index, result] = co_await folly::coro::collectAnyWithoutException(\n        [&]() -> folly::coro::Task<int> {\n          if (throwError) {\n            throw ErrorA{};\n          }\n          co_return 1;\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          if (throwError) {\n            throw ErrorB{};\n          }\n          co_return 2;\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          if (throwError) {\n            throw ErrorC{};\n          }\n          co_return 3;\n        }());\n    EXPECT_EQ(2, index);\n    try {\n      result.value();\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorC&) {\n      caughtException = true;\n    }\n  }());\n  EXPECT_TRUE(caughtException);\n}\n\nTEST_F(CollectAnyWithoutExceptionTest, ReturnsFirstValueWithoutException) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Child tasks are started in-order.\n    // Since the third task returns a value first ignore the exceptions and\n    // the last value and propagate the third value out of\n    // collectAnyWithoutException().\n    auto [index, result] = co_await folly::coro::collectAnyWithoutException(\n        []() -> folly::coro::Task<int> {\n          throw ErrorA{};\n          co_return 1;\n        }(),\n        []() -> folly::coro::Task<int> {\n          throw ErrorB{};\n          co_return 2;\n        }(),\n        []() -> folly::coro::Task<int> { co_return 3; }(),\n        []() -> folly::coro::Task<int> { co_return 4; }());\n    EXPECT_EQ(2, index);\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(3, result.value());\n  }());\n}\n\nTEST_F(CollectAnyWithoutExceptionTest, CancelsSubtasksWhenASubtaskCompletes) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n\n    auto [index, result] = co_await folly::coro::collectAnyWithoutException(\n        []() -> folly::coro::Task<int> {\n          co_await sleepThatShouldBeCancelled(10s);\n          co_return 42;\n        }(),\n        []() -> folly::coro::Task<int> {\n          co_await sleepThatShouldBeCancelled(5s);\n          co_return 314;\n        }(),\n        []() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_return 1;\n        }());\n    EXPECT_EQ(2, index);\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(1, result.value());\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAnyWithoutExceptionTest, CancelsSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n    auto [index, result] = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyWithoutException(\n            [&]() -> folly::coro::Task<int> {\n              co_await sleepThatShouldBeCancelled(10s);\n              co_return 42;\n            }(),\n            [&]() -> folly::coro::Task<int> {\n              co_await sleepThatShouldBeCancelled(5s);\n              co_return 314;\n            }(),\n            [&]() -> folly::coro::Task<int> {\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              cancelSource.requestCancellation();\n              co_await sleepThatShouldBeCancelled(15s);\n              co_return 123;\n            }()));\n    auto end = std::chrono::steady_clock::now();\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(\n    CollectAnyWithoutExceptionTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    });\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyWithoutException(std::move(task)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nclass CollectAnyNoDiscardTest : public testing::Test {};\n\nTEST_F(CollectAnyNoDiscardTest, OneTask) {\n  auto value = [&]() -> folly::coro::Task<std::string> { co_return \"hello\"; };\n  auto throws = [&]() -> folly::coro::Task<std::string> {\n    co_yield folly::coro::co_error(ErrorA{});\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto [result] = co_await folly::coro::collectAnyNoDiscard(value());\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(\"hello\", result.value());\n    std::tie(result) = co_await folly::coro::collectAnyNoDiscard(throws());\n    EXPECT_TRUE(result.hasException<ErrorA>());\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardTest, MultipleTasksWithValues) {\n  std::atomic_size_t count{0};\n\n  // Busy wait until all threads have started before returning\n  auto busyWait =\n      [](std::atomic_size_t& count, size_t num) -> folly::coro::Task<void> {\n    count.fetch_add(1);\n    while (count.load() < num) {\n      // Need to yield because collectAnyNoDiscard() won't start the second\n      // and third coroutines until the first one gets to a suspend point\n      co_await folly::coro::co_reschedule_on_current_executor;\n    }\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto [first, second, third] = co_await folly::coro::collectAnyNoDiscard(\n        busyWait(count, 3), busyWait(count, 3), busyWait(count, 3));\n    EXPECT_TRUE(first.hasValue());\n    EXPECT_TRUE(second.hasValue());\n    EXPECT_TRUE(third.hasValue());\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardTest, OneVoidTask) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that 'void' results are\n    // promoted to folly::Unit when placed in a tuple.\n    std::tuple<folly::Try<void>> result =\n        co_await folly::coro::collectAnyNoDiscard(\n            [&]() -> folly::coro::Task<void> {\n              completed = true;\n              co_return;\n            }());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyNoDiscardTest, DoesntCompleteUntilAllTasksComplete) {\n  folly::coro::Baton baton1;\n  folly::coro::Baton baton2;\n  bool task1Started = false;\n  bool task2Started = false;\n  bool complete = false;\n\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [first, second] = co_await folly::coro::collectAnyNoDiscard(\n        [&]() -> folly::coro::Task<int> {\n          task1Started = true;\n          co_await baton1;\n          co_return 42;\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          task2Started = true;\n          co_await baton2;\n          co_return 314;\n        }());\n    complete = true;\n    EXPECT_TRUE(first.hasValue());\n    EXPECT_EQ(first.value(), 42);\n    EXPECT_TRUE(second.hasValue());\n    EXPECT_EQ(second.value(), 314);\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  EXPECT_FALSE(task1Started);\n  EXPECT_FALSE(task2Started);\n\n  executor.drain();\n\n  EXPECT_TRUE(task1Started);\n  EXPECT_TRUE(task2Started);\n  EXPECT_FALSE(complete);\n  baton2.post();\n  executor.drain();\n  EXPECT_FALSE(complete);\n  baton1.post();\n  executor.drain();\n  EXPECT_TRUE(complete);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyNoDiscardTest, ThrowsAllErrors) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Child tasks are started in-order.\n    // The first task will reschedule itself onto the executor.\n    // The second task will fail immediately and will be the first task to\n    // fail. Then the third and first tasks will fail. We should see all\n    // exceptions propagate out of collectAnyNoDiscard().\n    auto [first, second, third] = co_await folly::coro::collectAnyNoDiscard(\n        [&]() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_yield folly::coro::co_error(ErrorA{});\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          co_yield folly::coro::co_error(ErrorB{});\n        }(),\n        [&]() -> folly::coro::Task<int> {\n          co_yield folly::coro::co_error(ErrorC{});\n        }());\n    EXPECT_TRUE(first.hasException<ErrorA>());\n    EXPECT_TRUE(second.hasException<ErrorB>());\n    EXPECT_TRUE(third.hasException<ErrorC>());\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardTest, CancelSubtasksWhenASubtaskCompletes) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n\n    auto [first, second, third] = co_await folly::coro::collectAnyNoDiscard(\n        []() -> folly::coro::Task<int> {\n          co_await folly::coro::sleep(10s);\n          co_return 42;\n        }(),\n        []() -> folly::coro::Task<int> {\n          co_await folly::coro::sleep(5s);\n          co_return 314;\n        }(),\n        []() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_yield folly::coro::co_error(ErrorA{});\n        }());\n    EXPECT_TRUE(first.hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(second.hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(third.hasException<ErrorA>());\n\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardTest, CancelSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n    auto [first, second, third] = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyNoDiscard(\n            [&]() -> folly::coro::Task<int> {\n              co_await folly::coro::sleep(10s);\n              co_return 42;\n            }(),\n            [&]() -> folly::coro::Task<int> {\n              co_await folly::coro::sleep(5s);\n              co_return 314;\n            }(),\n            [&]() -> folly::coro::Task<int> {\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              co_await folly::coro::co_reschedule_on_current_executor;\n              cancelSource.requestCancellation();\n              co_await folly::coro::sleep(15s);\n              co_return 123;\n            }()));\n    auto end = std::chrono::steady_clock::now();\n    EXPECT_LT(end - start, 1s);\n    EXPECT_TRUE(first.hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(second.hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(third.hasException<folly::OperationCancelled>());\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    });\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyNoDiscard(std::move(task)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nclass CollectAnyRangeTest : public testing::Test {};\n\nTEST_F(CollectAnyRangeTest, OneTaskWithValue) {\n  folly::coro::Baton baton;\n  auto f = [&]() -> folly::coro::Task<std::string> {\n    co_await baton;\n    co_return \"hello\";\n  };\n\n  bool completed = false;\n  auto run = [&]() -> folly::coro::Task<void> {\n    std::vector<folly::coro::Task<std::string>> tasks;\n    tasks.push_back(f());\n    auto [index, result] =\n        co_await folly::coro::collectAnyRange(std::move(tasks));\n    EXPECT_EQ(\"hello\", result.value());\n    EXPECT_EQ(0, index);\n    completed = true;\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  executor.drain();\n\n  EXPECT_FALSE(completed);\n\n  baton.post();\n\n  // Posting the baton should have just scheduled the 'f()' coroutine\n  // for resumption on the executor but should not have executed\n  // until we drain the executor again.\n  EXPECT_FALSE(completed);\n\n  executor.drain();\n\n  EXPECT_TRUE(completed);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyRangeTest, OneVoidTask) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that 'void' results are\n    // promoted to folly::Unit when placed in a tuple.\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield [&]() -> folly::coro::Task<void> {\n        completed = true;\n        co_return;\n      }();\n    };\n    std::pair<std::size_t, folly::Try<void>> result =\n        co_await folly::coro::collectAnyRange(generateTasks());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyRangeTest, MoveOnlyType) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that move only results\n    // can be correctly returned\n    auto generateTasks = [&]()\n        -> folly::coro::Generator<folly::coro::Task<std::unique_ptr<int>>&&> {\n      co_yield [&]() -> folly::coro::Task<std::unique_ptr<int>> {\n        completed = true;\n        co_return std::make_unique<int>(1);\n      }();\n    };\n    std::pair<std::size_t, folly::Try<std::unique_ptr<int>>> result =\n        co_await folly::coro::collectAnyRange(generateTasks());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyRangeTest, CollectAnyDoesntCompleteUntilAllTasksComplete) {\n  folly::coro::Baton baton1;\n  folly::coro::Baton baton2;\n  bool task1Started = false;\n  bool task2Started = false;\n  bool complete = false;\n  constexpr std::size_t taskTerminatingExpectedIndex = 1;\n\n  auto generateTasks =\n      [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n    auto t1 = [&]() -> folly::coro::Task<int> {\n      task1Started = true;\n      co_await baton1;\n      co_return 42;\n    };\n    co_yield t1();\n    auto t2 = [&]() -> folly::coro::Task<int> {\n      task2Started = true;\n      co_await baton2;\n      co_return 314;\n    };\n    co_yield t2();\n  };\n\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [index, result] =\n        co_await folly::coro::collectAnyRange(generateTasks());\n    complete = true;\n    EXPECT_EQ(taskTerminatingExpectedIndex, index);\n    EXPECT_EQ(result.value(), 314);\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  EXPECT_FALSE(task1Started);\n  EXPECT_FALSE(task2Started);\n\n  executor.drain();\n\n  EXPECT_TRUE(task1Started);\n  EXPECT_TRUE(task2Started);\n  EXPECT_FALSE(complete);\n  baton2.post();\n  executor.drain();\n  EXPECT_FALSE(complete);\n  baton1.post();\n  executor.drain();\n  EXPECT_TRUE(complete);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyRangeTest, ThrowsFirstError) {\n  bool caughtException = false;\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    bool throwError = true;\n\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        if (throwError) {\n          throw ErrorA{};\n        }\n        co_return 1;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        if (throwError) {\n          throw ErrorB{};\n        }\n        co_return 2;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        if (throwError) {\n          throw ErrorC{};\n        }\n        co_return 3;\n      }();\n    };\n    // Child tasks are started in-order.\n    // The first task will reschedule itself onto the executor.\n    // The second task will fail immediately and will be the first\n    // task to fail.\n    // Then the third and first tasks will fail.\n    // As the second task failed first we should see its exception\n    // propagate out of collectAny().\n    auto [index, result] =\n        co_await folly::coro::collectAnyRange(generateTasks());\n    EXPECT_EQ(1, index);\n    try {\n      result.value();\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorB&) {\n      caughtException = true;\n    }\n  }());\n  EXPECT_TRUE(caughtException);\n}\n\nTEST_F(CollectAnyRangeTest, CollectAnyCancelsSubtasksWhenASubtaskCompletes) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    std::vector<folly::coro::Task<int>> tasks;\n    tasks.push_back([]() -> folly::coro::Task<int> {\n      co_await sleepThatShouldBeCancelled(10s);\n      co_return 42;\n    }());\n    tasks.push_back([]() -> folly::coro::Task<int> {\n      co_await sleepThatShouldBeCancelled(5s);\n      co_return 314;\n    }());\n    tasks.push_back([]() -> folly::coro::Task<int> {\n      co_await folly::coro::co_reschedule_on_current_executor;\n      throw ErrorA{};\n    }());\n\n    auto [index, result] =\n        co_await folly::coro::collectAnyRange(std::move(tasks));\n    EXPECT_EQ(2, index);\n    try {\n      result.value();\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorA&) {\n    }\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAnyRangeTest, CollectAnyCancelsSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await sleepThatShouldBeCancelled(10s);\n        co_return 42;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await sleepThatShouldBeCancelled(5s);\n        co_return 314;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        cancelSource.requestCancellation();\n        co_await sleepThatShouldBeCancelled(15s);\n        co_return 123;\n      }();\n    };\n    auto [index, result] = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), folly::coro::collectAnyRange(generateTasks()));\n    auto end = std::chrono::steady_clock::now();\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAnyRangeTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    });\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(std::move(task));\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyRange(std::move(tasks)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nclass CollectAnyWithoutExceptionRangeTest : public testing::Test {};\n\nTEST_F(CollectAnyWithoutExceptionRangeTest, OneTaskWithValue) {\n  folly::coro::Baton baton;\n  auto f = [&]() -> folly::coro::Task<std::string> {\n    co_await baton;\n    co_return \"hello\";\n  };\n\n  bool completed = false;\n  auto run = [&]() -> folly::coro::Task<void> {\n    std::vector<folly::coro::Task<std::string>> tasks;\n    tasks.push_back(f());\n    auto [index, result] =\n        co_await folly::coro::collectAnyWithoutExceptionRange(std::move(tasks));\n    EXPECT_EQ(\"hello\", result.value());\n    EXPECT_EQ(0, index);\n    completed = true;\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  executor.drain();\n\n  EXPECT_FALSE(completed);\n\n  baton.post();\n\n  // Posting the baton should have just scheduled the 'f()' coroutine\n  // for resumption on the executor but should not have executed\n  // until we drain the executor again.\n  EXPECT_FALSE(completed);\n\n  executor.drain();\n\n  EXPECT_TRUE(completed);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyWithoutExceptionRangeTest, OneVoidTask) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that 'void' results are\n    // promoted to folly::Unit when placed in a tuple.\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<void>&&> {\n      co_yield [&]() -> folly::coro::Task<void> {\n        completed = true;\n        co_return;\n      }();\n    };\n    std::pair<std::size_t, folly::Try<void>> result =\n        co_await folly::coro::collectAnyWithoutExceptionRange(generateTasks());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyWithoutExceptionRangeTest, MoveOnlyType) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that move only results\n    // can be correctly returned\n    auto generateTasks = [&]()\n        -> folly::coro::Generator<folly::coro::Task<std::unique_ptr<int>>&&> {\n      co_yield [&]() -> folly::coro::Task<std::unique_ptr<int>> {\n        completed = true;\n        co_return std::make_unique<int>(1);\n      }();\n    };\n    std::pair<std::size_t, folly::Try<std::unique_ptr<int>>> result =\n        co_await folly::coro::collectAnyWithoutExceptionRange(generateTasks());\n    (void)result;\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(\n    CollectAnyWithoutExceptionRangeTest, DoesntCompleteUntilAllTasksComplete) {\n  folly::coro::Baton baton1;\n  folly::coro::Baton baton2;\n  bool task1Started = false;\n  bool task2Started = false;\n  bool complete = false;\n  constexpr std::size_t taskTerminatingExpectedIndex = 1;\n\n  auto generateTasks =\n      [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n    auto t1 = [&]() -> folly::coro::Task<int> {\n      task1Started = true;\n      co_await baton1;\n      co_return 42;\n    };\n    co_yield t1();\n    auto t2 = [&]() -> folly::coro::Task<int> {\n      task2Started = true;\n      co_await baton2;\n      co_return 314;\n    };\n    co_yield t2();\n  };\n\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto [index, result] =\n        co_await folly::coro::collectAnyWithoutExceptionRange(generateTasks());\n    complete = true;\n    EXPECT_EQ(taskTerminatingExpectedIndex, index);\n    EXPECT_EQ(result.value(), 314);\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  EXPECT_FALSE(task1Started);\n  EXPECT_FALSE(task2Started);\n\n  executor.drain();\n\n  EXPECT_TRUE(task1Started);\n  EXPECT_TRUE(task2Started);\n  EXPECT_FALSE(complete);\n  baton2.post();\n  executor.drain();\n  EXPECT_FALSE(complete);\n  baton1.post();\n  executor.drain();\n  EXPECT_TRUE(complete);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyWithoutExceptionRangeTest, ThrowsLastError) {\n  bool caughtException = false;\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    bool throwError = true;\n\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield [&]() -> folly::coro::Task<int> {\n        if (throwError) {\n          throw ErrorA{};\n        }\n        co_return 1;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        if (throwError) {\n          throw ErrorB{};\n        }\n        co_return 2;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        if (throwError) {\n          throw ErrorC{};\n        }\n        co_return 3;\n      }();\n    };\n    // Child tasks are started in-order.\n    // Since the third task fails last we should see its exception\n    // propagate out of collectAnyWithoutException().\n    auto [index, result] =\n        co_await folly::coro::collectAnyWithoutExceptionRange(generateTasks());\n    EXPECT_EQ(2, index);\n    try {\n      result.value();\n      ADD_FAILURE() << \"Hit unexpected codepath\";\n    } catch (const ErrorC&) {\n      caughtException = true;\n    }\n  }());\n  EXPECT_TRUE(caughtException);\n}\n\nTEST_F(CollectAnyWithoutExceptionRangeTest, ReturnsFirstValueWithoutException) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield []() -> folly::coro::Task<int> {\n        throw ErrorA{};\n        co_return 1;\n      }();\n      co_yield []() -> folly::coro::Task<int> {\n        throw ErrorB{};\n        co_return 2;\n      }();\n      co_yield []() -> folly::coro::Task<int> { co_return 3; }();\n      co_yield []() -> folly::coro::Task<int> { co_return 4; }();\n    };\n    // Child tasks are started in-order.\n    // Since the third task returns a value first ignore the exceptions and\n    // the last value and propagate the third value out of\n    // collectAnyWithoutException().\n    auto [index, result] =\n        co_await folly::coro::collectAnyWithoutExceptionRange(generateTasks());\n    EXPECT_EQ(2, index);\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(3, result.value());\n  }());\n}\n\nTEST_F(\n    CollectAnyWithoutExceptionRangeTest, CancelsSubtasksWhenASubtaskCompletes) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    std::vector<folly::coro::Task<int>> tasks;\n    tasks.push_back([]() -> folly::coro::Task<int> {\n      co_await sleepThatShouldBeCancelled(10s);\n      co_return 42;\n    }());\n    tasks.push_back([]() -> folly::coro::Task<int> {\n      co_await sleepThatShouldBeCancelled(5s);\n      co_return 314;\n    }());\n    tasks.push_back([]() -> folly::coro::Task<int> {\n      co_await folly::coro::co_reschedule_on_current_executor;\n      co_return 1;\n    }());\n\n    auto [index, result] =\n        co_await folly::coro::collectAnyWithoutExceptionRange(std::move(tasks));\n    EXPECT_EQ(2, index);\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(1, result.value());\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(\n    CollectAnyWithoutExceptionRangeTest,\n    CancelsSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await sleepThatShouldBeCancelled(10s);\n        co_return 42;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await sleepThatShouldBeCancelled(5s);\n        co_return 314;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        cancelSource.requestCancellation();\n        co_await sleepThatShouldBeCancelled(15s);\n        co_return 123;\n      }();\n    };\n    auto [index, result] = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyWithoutExceptionRange(generateTasks()));\n    auto end = std::chrono::steady_clock::now();\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(\n    CollectAnyWithoutExceptionRangeTest,\n    CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    });\n\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(std::move(task));\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyWithoutExceptionRange(std::move(tasks)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\n#endif\n\nclass CollectAnyNoDiscardRangeTest : public testing::Test {};\n\nTEST_F(CollectAnyNoDiscardRangeTest, OneTask) {\n  auto value = [&]() -> folly::coro::Task<std::string> { co_return \"hello\"; };\n  auto throws = [&]() -> folly::coro::Task<std::string> {\n    co_yield folly::coro::co_error(ErrorA{});\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    {\n      std::vector<folly::coro::Task<std::string>> tasks;\n      tasks.push_back(value());\n      auto result =\n          co_await folly::coro::collectAnyNoDiscardRange(std::move(tasks));\n      EXPECT_EQ(result.size(), 1);\n      EXPECT_TRUE(result[0].hasValue());\n      EXPECT_EQ(\"hello\", result[0].value());\n    }\n    {\n      std::vector<folly::coro::Task<std::string>> tasks;\n      tasks.push_back(throws());\n      auto result =\n          co_await folly::coro::collectAnyNoDiscardRange(std::move(tasks));\n      EXPECT_EQ(result.size(), 1);\n      EXPECT_TRUE(result[0].hasException<ErrorA>());\n    }\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardRangeTest, MultipleTasksWithValues) {\n  std::atomic_size_t count{0};\n\n  // Busy wait until all threads have started before returning\n  auto busyWait =\n      [](std::atomic_size_t& count, size_t num) -> folly::coro::Task<void> {\n    count.fetch_add(1);\n    while (count.load() < num) {\n      // Need to yield because collectAnyNoDiscard() won't start the second\n      // and third coroutines until the first one gets to a suspend point\n      co_await folly::coro::co_reschedule_on_current_executor;\n    }\n  };\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(busyWait(count, 3));\n    tasks.push_back(busyWait(count, 3));\n    tasks.push_back(busyWait(count, 3));\n    auto result =\n        co_await folly::coro::collectAnyNoDiscardRange(std::move(tasks));\n    EXPECT_EQ(result.size(), 3);\n    EXPECT_TRUE(result[0].hasValue());\n    EXPECT_TRUE(result[1].hasValue());\n    EXPECT_TRUE(result[2].hasValue());\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardRangeTest, OneVoidTask) {\n  bool completed = false;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Checks that the task actually runs and that 'void' results are\n    // promoted to folly::Unit when placed in a tuple.\n    auto makeTask = [&]() -> folly::coro::Task<void> {\n      completed = true;\n      co_return;\n    };\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(makeTask());\n    auto result =\n        co_await folly::coro::collectAnyNoDiscardRange(std::move(tasks));\n    EXPECT_EQ(result.size(), 1);\n  }());\n  EXPECT_TRUE(completed);\n}\n\nTEST_F(CollectAnyNoDiscardRangeTest, DoesntCompleteUntilAllTasksComplete) {\n  folly::coro::Baton baton1;\n  folly::coro::Baton baton2;\n  bool task1Started = false;\n  bool task2Started = false;\n  bool complete = false;\n\n  auto run = [&]() -> folly::coro::Task<void> {\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      auto t1 = [&]() -> folly::coro::Task<int> {\n        task1Started = true;\n        co_await baton1;\n        co_return 42;\n      };\n      co_yield t1();\n      auto t2 = [&]() -> folly::coro::Task<int> {\n        task2Started = true;\n        co_await baton2;\n        co_return 314;\n      };\n      co_yield t2();\n    };\n\n    auto result =\n        co_await folly::coro::collectAnyNoDiscardRange(generateTasks());\n    complete = true;\n    EXPECT_EQ(result.size(), 2);\n    EXPECT_TRUE(result[0].hasValue());\n    EXPECT_EQ(result[0].value(), 42);\n    EXPECT_TRUE(result[1].hasValue());\n    EXPECT_EQ(result[1].value(), 314);\n  };\n\n  folly::ManualExecutor executor;\n\n  auto future = co_withExecutor(&executor, run()).start();\n\n  EXPECT_FALSE(task1Started);\n  EXPECT_FALSE(task2Started);\n\n  executor.drain();\n\n  EXPECT_TRUE(task1Started);\n  EXPECT_TRUE(task2Started);\n  EXPECT_FALSE(complete);\n  baton2.post();\n  executor.drain();\n  EXPECT_FALSE(complete);\n  baton1.post();\n  executor.drain();\n  EXPECT_TRUE(complete);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CollectAnyNoDiscardRangeTest, ThrowsAllErrors) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    // Child tasks are started in-order.\n    // The first task will reschedule itself onto the executor.\n    // The second task will fail immediately and will be the first task to\n    // fail. Then the third and first tasks will fail. We should see all\n    // exceptions propagate out of collectAnyNoDiscard().\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_yield folly::coro::co_error(ErrorA{});\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_yield folly::coro::co_error(ErrorB{});\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_yield folly::coro::co_error(ErrorC{});\n      }();\n    };\n    auto result =\n        co_await folly::coro::collectAnyNoDiscardRange(generateTasks());\n    EXPECT_EQ(result.size(), 3);\n    EXPECT_TRUE(result[0].hasException<ErrorA>());\n    EXPECT_TRUE(result[1].hasException<ErrorB>());\n    EXPECT_TRUE(result[2].hasException<ErrorC>());\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardRangeTest, CancelSubtasksWhenASubtaskCompletes) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield []() -> folly::coro::Task<int> {\n        co_await folly::coro::sleep(10s);\n        co_return 42;\n      }();\n      co_yield []() -> folly::coro::Task<int> {\n        co_await folly::coro::sleep(5s);\n        co_return 314;\n      }();\n      co_yield []() -> folly::coro::Task<int> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_yield folly::coro::co_error(ErrorA{});\n      }();\n    };\n    auto result =\n        co_await folly::coro::collectAnyNoDiscardRange(generateTasks());\n    EXPECT_EQ(result.size(), 3);\n    EXPECT_TRUE(result[0].hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(result[1].hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(result[2].hasException<ErrorA>());\n\n    auto end = std::chrono::steady_clock::now();\n\n    EXPECT_LT(end - start, 1s);\n  }());\n}\n\nTEST_F(CollectAnyNoDiscardRangeTest, CancelSubtasksWhenParentTaskCancelled) {\n  using namespace std::chrono_literals;\n\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto start = std::chrono::steady_clock::now();\n    folly::CancellationSource cancelSource;\n\n    auto generateTasks =\n        [&]() -> folly::coro::Generator<folly::coro::Task<int>&&> {\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await folly::coro::sleep(10s);\n        co_return 42;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await folly::coro::sleep(5s);\n        co_return 314;\n      }();\n      co_yield [&]() -> folly::coro::Task<int> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        co_await folly::coro::co_reschedule_on_current_executor;\n        cancelSource.requestCancellation();\n        co_await folly::coro::sleep(15s);\n        co_return 123;\n      }();\n    };\n    auto result = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyNoDiscardRange(generateTasks()));\n    auto end = std::chrono::steady_clock::now();\n    EXPECT_LT(end - start, 1s);\n    EXPECT_EQ(result.size(), 3);\n    EXPECT_TRUE(result[0].hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(result[1].hasException<folly::OperationCancelled>());\n    EXPECT_TRUE(result[2].hasException<folly::OperationCancelled>());\n  }());\n}\n\nTEST_F(\n    CollectAnyNoDiscardRangeTest, CancellationTokenRemainsActiveAfterReturn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    folly::coro::AsyncScope scope;\n    folly::coro::Baton baton;\n    std::vector<folly::coro::Task<void>> tasks;\n    tasks.push_back(folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n      auto token = co_await folly::coro::co_current_cancellation_token;\n      auto ex = co_await folly::coro::co_current_executor;\n      scope.add(co_withExecutor(\n          ex,\n          folly::coro::co_withCancellation(\n              token, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n                auto innerToken =\n                    co_await folly::coro::co_current_cancellation_token;\n                co_await baton;\n                EXPECT_TRUE(innerToken.isCancellationRequested());\n              }))));\n    }));\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(),\n        folly::coro::collectAnyNoDiscardRange(std::move(tasks)));\n    cancelSource.requestCancellation();\n    baton.post();\n    co_await scope.joinAsync();\n  }());\n}\n\nTEST(MakeUnorderedAsyncGeneratorTest, GeneratorEarlyDestroy) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::AsyncScope scope;\n    folly::CPUThreadPoolExecutor executor(2);\n\n    std::vector<folly::coro::TaskWithExecutor<int>> tasks;\n\n    tasks.push_back(co_withExecutor(\n        &executor, folly::coro::co_invoke([]() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          std::this_thread::sleep_for(std::chrono::seconds{2});\n          co_return 42;\n        })));\n    tasks.push_back(co_withExecutor(\n        &executor, folly::coro::co_invoke([]() -> folly::coro::Task<int> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          std::this_thread::sleep_for(std::chrono::seconds{1});\n          co_return 43;\n        })));\n\n    {\n      auto gen =\n          folly::coro::makeUnorderedAsyncGenerator(scope, std::move(tasks));\n      EXPECT_EQ(43, *(co_await gen.next()));\n    }\n\n    co_await scope.joinAsync();\n  }());\n}\n"
  },
  {
    "path": "folly/coro/test/ConcatTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Concat.h>\n#include <folly/coro/Task.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nnamespace {\n\nAsyncGenerator<int> generateInts(int begin, int end) {\n  for (int i = begin; i < end; i++) {\n    co_await co_reschedule_on_current_executor;\n    co_yield i;\n  }\n}\n\nTask<std::vector<int>> toVector(AsyncGenerator<int> generator) {\n  std::vector<int> result;\n  while (auto x = co_await generator.next()) {\n    result.push_back(*x);\n  }\n  co_return result;\n}\n\n} // namespace\n\nclass ConcatTest : public testing::Test {};\n\nTEST_F(ConcatTest, ConcatSingle) {\n  auto gen = concat(generateInts(0, 5));\n  auto result = blockingWait(toVector(std::move(gen)));\n  std::vector<int> expected{0, 1, 2, 3, 4};\n\n  EXPECT_EQ(result, expected);\n}\n\nTEST_F(ConcatTest, ConcatMultiple) {\n  auto gen =\n      concat(generateInts(0, 5), generateInts(7, 10), generateInts(12, 15));\n  auto result = blockingWait(toVector(std::move(gen)));\n  std::vector<int> expected{0, 1, 2, 3, 4, 7, 8, 9, 12, 13, 14};\n\n  EXPECT_EQ(result, expected);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/CoroBenchmarkAllocator.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n\n#include <future>\n\n#include <folly/coro/Coroutine.h>\n\n#if FOLLY_HAS_COROUTINES\n\nclass Wait {\n public:\n  class promise_type {\n   public:\n    Wait get_return_object() { return Wait(promise_.get_future()); }\n\n    folly::coro::suspend_never initial_suspend() noexcept { return {}; }\n\n    folly::coro::suspend_never final_suspend() noexcept { return {}; }\n\n    void return_void() { promise_.set_value(); }\n\n    void unhandled_exception() {\n      promise_.set_exception(std::current_exception());\n    }\n\n   private:\n    std::promise<void> promise_;\n  };\n\n  explicit Wait(std::future<void> future) : future_(std::move(future)) {}\n\n  Wait(Wait&&) = default;\n\n  void detach() { future_ = {}; }\n\n  ~Wait() {\n    if (future_.valid()) {\n      future_.get();\n    }\n  }\n\n private:\n  std::future<void> future_;\n};\n\ntemplate <typename T>\nclass InlineTask {\n public:\n  InlineTask(const InlineTask&) = delete;\n  InlineTask(InlineTask&& other)\n      : promise_(std::exchange(other.promise_, nullptr)) {}\n\n  ~InlineTask() { DCHECK(!promise_); }\n\n  bool await_ready() const noexcept { return false; }\n\n  folly::coro::coroutine_handle<> await_suspend(\n      folly::coro::coroutine_handle<> awaiter) {\n    promise_->valuePtr_ = &value_;\n    promise_->awaiter_ = std::move(awaiter);\n    return folly::coro::coroutine_handle<promise_type>::from_promise(*promise_);\n  }\n\n  T await_resume() {\n    folly::coro::coroutine_handle<promise_type>::from_promise(\n        *std::exchange(promise_, nullptr))\n        .destroy();\n    T value = std::move(value_);\n    return value;\n  }\n\n  class promise_type {\n   public:\n    InlineTask get_return_object() { return InlineTask(this); }\n\n    template <typename U = T>\n    void return_value(U&& value) {\n      *valuePtr_ = std::forward<U>(value);\n    }\n\n    void unhandled_exception() { std::terminate(); }\n\n    folly::coro::suspend_always initial_suspend() { return {}; }\n\n    class FinalSuspender {\n     public:\n      bool await_ready() noexcept { return false; }\n\n      auto await_suspend(\n          folly::coro::coroutine_handle<promise_type> h) noexcept {\n        return h.promise().awaiter_;\n      }\n\n      void await_resume() noexcept {}\n    };\n\n    FinalSuspender final_suspend() noexcept { return FinalSuspender{}; }\n\n   private:\n    friend class InlineTask;\n\n    T* valuePtr_;\n    folly::coro::coroutine_handle<> awaiter_;\n  };\n\n private:\n  friend class promise_type;\n\n  explicit InlineTask(promise_type* promise) : promise_(promise) {}\n\n  T value_;\n  promise_type* promise_;\n};\n\nclass StackAllocator {\n public:\n  explicit StackAllocator(size_t bytes) : buffer_(new char[bytes]) {}\n  ~StackAllocator() { delete[] buffer_; }\n  StackAllocator(const StackAllocator&) = delete;\n\n  void* allocate(size_t bytes) {\n    auto ptr = buffer_;\n    buffer_ += bytes;\n    return ptr;\n  }\n\n  void deallocate(void*, size_t bytes) { buffer_ -= bytes; }\n\n private:\n  char* buffer_;\n};\n\n// We only need this because clang doesn't correctly pass arguments to operator\n// new.\nStackAllocator defaultAllocator(1000 * 512);\n\ntemplate <typename T>\nclass InlineTaskAllocator {\n public:\n  InlineTaskAllocator(const InlineTaskAllocator&) = delete;\n  InlineTaskAllocator(InlineTaskAllocator&& other)\n      : promise_(std::exchange(other.promise_, nullptr)) {}\n\n  ~InlineTaskAllocator() { DCHECK(!promise_); }\n\n  bool await_ready() const noexcept { return false; }\n\n  folly::coro::coroutine_handle<> await_suspend(\n      folly::coro::coroutine_handle<> awaiter) {\n    promise_->valuePtr_ = &value_;\n    promise_->awaiter_ = std::move(awaiter);\n    return folly::coro::coroutine_handle<promise_type>::from_promise(*promise_);\n  }\n\n  T await_resume() {\n    folly::coro::coroutine_handle<promise_type>::from_promise(\n        *std::exchange(promise_, nullptr))\n        .destroy();\n    T value = std::move(value_);\n    return value;\n  }\n\n  class promise_type {\n   public:\n    static void* operator new(size_t size) {\n      size += sizeof(StackAllocator*);\n      StackAllocator** buffer =\n          static_cast<StackAllocator**>(defaultAllocator.allocate(size));\n      buffer[0] = &defaultAllocator;\n      return buffer + 1;\n    }\n\n    static void operator delete(void* ptr, size_t size) {\n      size += sizeof(StackAllocator*);\n      StackAllocator** buffer = static_cast<StackAllocator**>(ptr) - 1;\n      auto allocator = buffer[0];\n      allocator->deallocate(ptr, size);\n    }\n\n    InlineTaskAllocator get_return_object() {\n      return InlineTaskAllocator(this);\n    }\n\n    template <typename U = T>\n    void return_value(U&& value) {\n      *valuePtr_ = std::forward<U>(value);\n    }\n\n    [[noreturn]] void unhandled_exception() noexcept { std::terminate(); }\n\n    folly::coro::suspend_always initial_suspend() noexcept { return {}; }\n\n    class FinalSuspender {\n     public:\n      bool await_ready() noexcept { return false; }\n\n      auto await_suspend(\n          folly::coro::coroutine_handle<promise_type> h) noexcept {\n        return h.promise().awaiter_;\n      }\n\n      void await_resume() noexcept {}\n    };\n\n    FinalSuspender final_suspend() noexcept { return FinalSuspender{}; }\n\n   private:\n    friend class InlineTaskAllocator;\n\n    T* valuePtr_;\n    folly::coro::coroutine_handle<> awaiter_;\n  };\n\n private:\n  friend class promise_type;\n\n  explicit InlineTaskAllocator(promise_type* promise) : promise_(promise) {}\n\n  T value_;\n  promise_type* promise_;\n};\n\nclass Recursion {\n public:\n  static std::unique_ptr<Recursion> create(size_t depth) {\n    if (depth == 0) {\n      return std::unique_ptr<Recursion>(new Recursion(nullptr));\n    }\n    return std::unique_ptr<Recursion>(new Recursion(create(depth - 1)));\n  }\n\n  int operator()() {\n    if (child_) {\n      return (*child_)() + 1;\n    }\n    return 0;\n  }\n\n  InlineTask<int> operator co_await() {\n    if (child_) {\n      co_return co_await *child_ + 1;\n    }\n    co_return 0;\n  }\n\n  InlineTaskAllocator<int> co_allocator() {\n    if (child_) {\n      co_return co_await child_->co_allocator() + 1;\n    }\n    co_return 0;\n  }\n\n private:\n  explicit Recursion(std::unique_ptr<Recursion> child)\n      : child_(std::move(child)) {}\n\n  std::unique_ptr<Recursion> child_;\n};\n\nvoid coroRecursion(size_t times, size_t iters) {\n  auto recursion = Recursion::create(times);\n  for (size_t iter = 0; iter < iters; ++iter) {\n    [](Recursion& recursion, size_t times) -> Wait {\n      CHECK_EQ(times, co_await recursion);\n      co_return;\n    }(*recursion, times);\n  }\n}\n\nBENCHMARK(coroRecursionDepth10, iters) {\n  coroRecursion(10, iters);\n}\n\nBENCHMARK(coroRecursionDepth1000, iters) {\n  coroRecursion(1000, iters);\n}\n\nvoid coroRecursionAllocator(size_t times, size_t iters) {\n  auto recursion = Recursion::create(times);\n  for (size_t iter = 0; iter < iters; ++iter) {\n    [](Recursion& recursion, size_t times) -> Wait {\n      CHECK_EQ(times, co_await recursion.co_allocator());\n      co_return;\n    }(*recursion, times);\n  }\n}\n\nBENCHMARK(coroRecursionAllocatorDepth10, iters) {\n  coroRecursionAllocator(10, iters);\n}\n\nBENCHMARK(coroRecursionAllocatorDepth1000, iters) {\n  coroRecursionAllocator(1000, iters);\n}\n\nvoid recursion(size_t times, size_t iters) {\n  auto recursion = Recursion::create(times);\n  for (size_t iter = 0; iter < iters; ++iter) {\n    CHECK_EQ(times, (*recursion)());\n  }\n}\n\nBENCHMARK(recursionDepth10, iters) {\n  recursion(10, iters);\n}\n\nBENCHMARK(recursionDepth1000, iters) {\n  recursion(1000, iters);\n}\n#endif\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/CoroBenchmarkNRVO.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n\n#include <folly/coro/Coroutine.h>\n\n#include <future>\n#include <thread>\n\nstruct ExpensiveCopy {\n  ExpensiveCopy() {}\n\n  ExpensiveCopy(const ExpensiveCopy&) {\n    std::this_thread::sleep_for(std::chrono::milliseconds{1});\n  }\n\n  ExpensiveCopy& operator=(const ExpensiveCopy&) = default;\n};\n\n#if FOLLY_HAS_COROUTINES\n\nclass Wait {\n public:\n  class promise_type {\n   public:\n    Wait get_return_object() { return Wait(promise_.get_future()); }\n\n    folly::coro::suspend_never initial_suspend() noexcept { return {}; }\n\n    folly::coro::suspend_never final_suspend() noexcept { return {}; }\n\n    void return_void() { promise_.set_value(); }\n\n    void unhandled_exception() {\n      promise_.set_exception(std::current_exception());\n    }\n\n   private:\n    std::promise<void> promise_;\n  };\n\n  explicit Wait(std::future<void> future) : future_(std::move(future)) {}\n\n  Wait(Wait&&) = default;\n\n  void detach() { future_ = {}; }\n\n  ~Wait() {\n    if (future_.valid()) {\n      future_.get();\n    }\n  }\n\n private:\n  std::future<void> future_;\n};\n\ntemplate <typename T>\nclass InlineTask {\n public:\n  InlineTask(const InlineTask&) = delete;\n  InlineTask(InlineTask&& other)\n      : promise_(std::exchange(other.promise_, nullptr)) {}\n\n  ~InlineTask() { DCHECK(!promise_); }\n\n  bool await_ready() const noexcept { return false; }\n\n  folly::coro::coroutine_handle<> await_suspend(\n      folly::coro::coroutine_handle<> awaiter) {\n    promise_->valuePtr_ = &value_;\n    promise_->awaiter_ = std::move(awaiter);\n    return folly::coro::coroutine_handle<promise_type>::from_promise(*promise_);\n  }\n\n  T await_resume() {\n    folly::coro::coroutine_handle<promise_type>::from_promise(\n        *std::exchange(promise_, nullptr))\n        .destroy();\n    T value = std::move(value_);\n    return value;\n  }\n\n  class promise_type {\n   public:\n    InlineTask get_return_object() { return InlineTask(this); }\n\n    template <typename U = T>\n    void return_value(U&& value) {\n      *valuePtr_ = std::forward<U>(value);\n    }\n\n    void unhandled_exception() { std::terminate(); }\n\n    folly::coro::suspend_always initial_suspend() { return {}; }\n\n    class FinalSuspender {\n     public:\n      explicit FinalSuspender(folly::coro::coroutine_handle<> awaiter) noexcept\n          : awaiter_(std::move(awaiter)) {}\n\n      bool await_ready() noexcept { return false; }\n\n      auto await_suspend(folly::coro::coroutine_handle<>) noexcept {\n        return awaiter_;\n      }\n\n      void await_resume() noexcept {}\n\n     private:\n      folly::coro::coroutine_handle<> awaiter_;\n    };\n\n    FinalSuspender final_suspend() noexcept {\n      return FinalSuspender(std::move(awaiter_));\n    }\n\n   private:\n    friend class InlineTask;\n\n    T* valuePtr_;\n    folly::coro::coroutine_handle<> awaiter_;\n  };\n\n private:\n  friend class promise_type;\n\n  explicit InlineTask(promise_type* promise) : promise_(promise) {}\n\n  T value_;\n  promise_type* promise_;\n};\n\nInlineTask<ExpensiveCopy> co_nestedCalls(size_t times) {\n  ExpensiveCopy ret;\n  if (times > 0) {\n    ret = co_await co_nestedCalls(times - 1);\n  }\n  co_return ret;\n}\n\nvoid coroNRVO(size_t times, size_t iters) {\n  for (size_t iter = 0; iter < iters; ++iter) {\n    [](size_t times) -> Wait {\n      (void)co_await co_nestedCalls(times);\n      co_return;\n    }(times);\n  }\n}\n\nBENCHMARK(coroNRVOOneAwait, iters) {\n  coroNRVO(1, iters);\n}\n\nBENCHMARK(coroNRVOTenAwaits, iters) {\n  coroNRVO(10, iters);\n}\n#endif\n\nExpensiveCopy nestedCalls(size_t times) {\n  ExpensiveCopy ret;\n  if (times > 0) {\n    ret = nestedCalls(times - 1);\n  }\n\n  return ret;\n}\n\nvoid NRVO(size_t times, size_t iters) {\n  for (size_t iter = 0; iter < iters; ++iter) {\n    nestedCalls(times);\n  }\n}\n\nBENCHMARK(NRVOOneAwait, iters) {\n  NRVO(1, iters);\n}\n\nBENCHMARK(NRVOTenAwaits, iters) {\n  NRVO(10, iters);\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/CoroTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/CancellationToken.h>\n#include <folly/Chrono.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/DetachOnCancel.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/TimedWait.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/fibers/Semaphore.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/lang/Assume.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly;\n\nclass CoroTest : public testing::Test {};\n\nTEST_F(CoroTest, Basic) {\n  ManualExecutor executor;\n  auto task42 = []() -> coro::Task<int> { co_return 42; };\n  auto future = co_withExecutor(&executor, task42()).start();\n\n  EXPECT_FALSE(future.isReady());\n\n  executor.drain();\n\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(42, std::move(future).get());\n}\n\nTEST_F(CoroTest, BasicSemiFuture) {\n  ManualExecutor executor;\n  auto task42 = []() -> coro::Task<int> { co_return 42; };\n  auto future = task42().semi().via(&executor);\n\n  EXPECT_FALSE(future.isReady());\n\n  executor.drain();\n\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(42, std::move(future).get());\n}\n\nTEST_F(CoroTest, BasicFuture) {\n  ManualExecutor executor;\n\n  auto task42 = []() -> coro::Task<int> { co_return 42; };\n  auto future = co_withExecutor(&executor, task42()).start();\n\n  EXPECT_FALSE(future.isReady());\n\n  EXPECT_EQ(42, std::move(future).via(&executor).getVia(&executor));\n}\n\ncoro::Task<void> taskVoid() {\n  auto task42 = []() -> coro::Task<int> { co_return 42; };\n  (void)co_await task42();\n  co_return;\n}\n\nTEST_F(CoroTest, Basic2) {\n  ManualExecutor executor;\n  auto future = co_withExecutor(&executor, taskVoid()).start();\n\n  EXPECT_FALSE(future.isReady());\n\n  executor.drain();\n\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CoroTest, TaskOfMoveOnly) {\n  auto f = []() -> coro::Task<std::unique_ptr<int>> {\n    co_return std::make_unique<int>(123);\n  };\n\n  auto p = coro::blockingWait(f());\n  EXPECT_TRUE(p);\n  EXPECT_EQ(123, *p);\n}\n\ncoro::Task<void> taskSleep() {\n  (void)co_await coro::sleep(std::chrono::seconds{1});\n  co_return;\n}\n\nTEST_F(CoroTest, Sleep) {\n  ScopedEventBaseThread evbThread;\n\n  auto startTime = std::chrono::steady_clock::now();\n  auto task = co_withExecutor(evbThread.getEventBase(), taskSleep());\n\n  coro::blockingWait(std::move(task));\n\n  // The total time should be roughly 1 second. Some builds, especially\n  // optimized ones, may result in slightly less than 1 second, so we perform\n  // rounding here.\n  auto totalTime = std::chrono::steady_clock::now() - startTime;\n  EXPECT_GE(\n      chrono::round<std::chrono::seconds>(totalTime), std::chrono::seconds{1});\n}\n\nTEST_F(CoroTest, ExecutorKeepAlive) {\n  auto future = [] {\n    ScopedEventBaseThread evbThread;\n\n    return co_withExecutor(evbThread.getEventBase(), taskSleep()).start();\n  }();\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(CoroTest, ExecutorKeepAliveDummy) {\n  struct CountingExecutor : public ManualExecutor {\n    bool keepAliveAcquire() noexcept override {\n      ++keepAliveCounter;\n      return true;\n    }\n    void keepAliveRelease() noexcept override { --keepAliveCounter; }\n\n    size_t keepAliveCounter{0};\n  };\n\n  struct ExecutorRec {\n    static coro::Task<void> go(int depth) {\n      if (depth == 0) {\n        co_return;\n      }\n\n      auto executor =\n          dynamic_cast<CountingExecutor*>(co_await coro::co_current_executor);\n      DCHECK(executor);\n\n      // Note, extra keepalives are being kept by the Futures.\n      EXPECT_EQ(3, executor->keepAliveCounter);\n\n      co_await go(depth - 1);\n    }\n  };\n\n  CountingExecutor executor;\n  co_withExecutor(&executor, ExecutorRec::go(42))\n      .start()\n      .via(&executor)\n      .getVia(&executor);\n}\n\nTEST_F(CoroTest, FutureThrow) {\n  auto taskException = []() -> coro::Task<int> {\n    throw std::runtime_error(\"Test exception\");\n    co_return 42;\n  };\n\n  ManualExecutor executor;\n  auto future = co_withExecutor(&executor, taskException()).start();\n\n  EXPECT_FALSE(future.isReady());\n\n  executor.drain();\n\n  EXPECT_TRUE(future.isReady());\n  EXPECT_THROW(std::move(future).get(), std::runtime_error);\n}\n\ncoro::Task<int> taskRecursion(int depth) {\n  if (depth > 0) {\n    EXPECT_EQ(depth - 1, co_await taskRecursion(depth - 1));\n  } else {\n    (void)co_await coro::sleep(std::chrono::seconds{1});\n  }\n\n  co_return depth;\n}\n\nTEST_F(CoroTest, LargeStack) {\n  ScopedEventBaseThread evbThread;\n  auto task = co_withExecutor(evbThread.getEventBase(), taskRecursion(50000));\n\n  EXPECT_EQ(50000, coro::blockingWait(std::move(task)));\n}\n\nTEST_F(CoroTest, NestedThreads) {\n  auto taskThreadNested = [](std::thread::id threadId) -> coro::Task<void> {\n    EXPECT_EQ(threadId, std::this_thread::get_id());\n    (void)co_await coro::sleep(std::chrono::seconds{1});\n    EXPECT_EQ(threadId, std::this_thread::get_id());\n    co_return;\n  };\n\n  auto taskThread = [&]() -> coro::Task<int> {\n    auto threadId = std::this_thread::get_id();\n\n    // BUG: Under @mode/clang-opt builds this object is placed on the coroutine\n    // frame and the code for the constructor assumes that it is allocated on\n    // a 16-byte boundary. However, when placed in the coroutine frame it can\n    // end up at a location that is not 16-byte aligned. This causes a SIGSEGV\n    // when performing a store to members that uses SSE instructions.\n    folly::ScopedEventBaseThread evbThread;\n\n    co_await co_withExecutor(\n        evbThread.getEventBase(), taskThreadNested(evbThread.getThreadId()));\n\n    EXPECT_EQ(threadId, std::this_thread::get_id());\n\n    co_return 42;\n  };\n\n  ScopedEventBaseThread evbThread;\n  auto task = co_withExecutor(evbThread.getEventBase(), taskThread());\n  EXPECT_EQ(42, coro::blockingWait(std::move(task)));\n}\n\nTEST_F(CoroTest, CurrentExecutor) {\n  auto taskGetCurrentExecutor = [](Executor* executor) -> coro::Task<int> {\n    auto current = co_await coro::co_current_executor;\n    EXPECT_EQ(executor, current);\n    auto task42 = []() -> coro::Task<int> { co_return 42; };\n    co_return co_await co_withExecutor(current, task42());\n  };\n\n  ScopedEventBaseThread evbThread;\n  auto task = co_withExecutor(\n      evbThread.getEventBase(),\n      taskGetCurrentExecutor(evbThread.getEventBase()));\n  EXPECT_EQ(42, coro::blockingWait(std::move(task)));\n}\n\nTEST_F(CoroTest, TimedWaitFuture) {\n  auto taskTimedWaitFuture = []() -> coro::Task<void> {\n    auto ex = co_await coro::co_current_executor;\n    auto fastFuture =\n        futures::sleep(std::chrono::milliseconds{50})\n            .via(ex)\n            .thenValue([](Unit) { return 42; });\n    auto fastResult = co_await coro::timed_wait(\n        std::move(fastFuture), std::chrono::milliseconds{100});\n    EXPECT_TRUE(fastResult);\n    EXPECT_EQ(42, *fastResult);\n\n    struct ExpectedException : public std::runtime_error {\n      ExpectedException() : std::runtime_error(\"ExpectedException\") {}\n    };\n\n    auto throwingFuture =\n        futures::sleep(std::chrono::milliseconds{50})\n            .via(ex)\n            .thenValue([](Unit) { throw ExpectedException(); });\n    EXPECT_THROW(\n        (void)co_await coro::timed_wait(\n            std::move(throwingFuture), std::chrono::milliseconds{100}),\n        ExpectedException);\n\n    auto [lifetimePromise, lifetimeFuture] =\n        folly::makePromiseContract<folly::Unit>(ex);\n    auto slowFuture =\n        futures::sleep(std::chrono::milliseconds{200})\n            .via(ex)\n            .thenValue(\n                [lifetimePromise_ = std::move(lifetimePromise)](Unit) mutable {\n                  lifetimePromise_.setValue();\n                  return 42;\n                });\n    auto slowResult = co_await coro::timed_wait(\n        std::move(slowFuture), std::chrono::milliseconds{100});\n    EXPECT_FALSE(slowResult);\n\n    // Ensure that task completes for safe executor lifetimes\n    (void)co_await std::move(lifetimeFuture);\n\n    co_return;\n  };\n\n  coro::blockingWait(taskTimedWaitFuture());\n}\n\nTEST_F(CoroTest, TimedWaitTask) {\n  auto taskTimedWaitTask = []() -> coro::Task<void> {\n    auto fastTask = []() -> coro::Task<int> {\n      co_await coro::sleep(std::chrono::milliseconds{50});\n      co_return 42;\n    }();\n    auto fastResult = co_await coro::timed_wait(\n        std::move(fastTask), std::chrono::milliseconds{100});\n    EXPECT_TRUE(fastResult);\n    EXPECT_EQ(42, *fastResult);\n\n    struct ExpectedException : public std::runtime_error {\n      ExpectedException() : std::runtime_error(\"ExpectedException\") {}\n    };\n\n    auto throwingTask = []() -> coro::Task<void> {\n      co_await coro::sleep(std::chrono::milliseconds{50});\n      throw ExpectedException();\n    }();\n    EXPECT_THROW(\n        (void)co_await coro::timed_wait(\n            std::move(throwingTask), std::chrono::milliseconds{100}),\n        ExpectedException);\n\n    auto slowTask = []() -> coro::Task<int> {\n      co_await futures::sleep(std::chrono::milliseconds{200});\n      co_return 42;\n    }();\n    auto slowResult = co_await coro::timed_wait(\n        std::move(slowTask), std::chrono::milliseconds{100});\n    EXPECT_FALSE(slowResult);\n\n    co_return;\n  };\n\n  coro::blockingWait(taskTimedWaitTask());\n}\n\nTEST_F(CoroTest, TimedWaitKeepAlive) {\n  auto start = std::chrono::steady_clock::now();\n  coro::blockingWait([]() -> coro::Task<void> {\n    co_await coro::timed_wait(\n        coro::sleep(std::chrono::milliseconds{100}), std::chrono::seconds{60});\n    co_return;\n  }());\n  auto duration = std::chrono::steady_clock::now() - start;\n  EXPECT_LE(duration, std::chrono::seconds{30});\n}\n\nTEST_F(CoroTest, TimedWaitNonCopyable) {\n  auto task = []() -> coro::Task<std::unique_ptr<int>> {\n    co_return std::make_unique<int>(42);\n  }();\n  EXPECT_EQ(\n      42,\n      **coro::blockingWait(\n          [&]() -> coro::Task<folly::Optional<std::unique_ptr<int>>> {\n            co_return co_await coro::timed_wait(\n                std::move(task), std::chrono::seconds{60});\n          }()));\n}\n\nnamespace {\n\ntemplate <int value>\nstruct AwaitableInt {\n  bool await_ready() const noexcept { return true; }\n\n  bool await_suspend(coro::coroutine_handle<>) { assume_unreachable(); }\n\n  int await_resume() { return value; }\n};\n\nstruct AwaitableWithOperator {};\n\nAwaitableInt<42> operator co_await(const AwaitableWithOperator&) {\n  return {};\n}\n\nstruct AwaitableWithMemberOperator {\n  AwaitableInt<42> operator co_await() { return {}; }\n};\n\n[[maybe_unused]] AwaitableInt<24> operator co_await(\n    const AwaitableWithMemberOperator&) {\n  return {};\n}\n\n} // namespace\n\nTEST_F(CoroTest, AwaitableWithOperator) {\n  auto taskAwaitableWithOperator = []() -> coro::Task<int> {\n    co_return co_await AwaitableWithOperator();\n  };\n\n  EXPECT_EQ(42, coro::blockingWait(taskAwaitableWithOperator()));\n}\n\nTEST_F(CoroTest, AwaitableWithMemberOperator) {\n  auto taskAwaitableWithMemberOperator = []() -> coro::Task<int> {\n    co_return co_await AwaitableWithMemberOperator();\n  };\n\n  EXPECT_EQ(42, coro::blockingWait(taskAwaitableWithMemberOperator()));\n}\n\nTEST_F(CoroTest, Baton) {\n  auto taskBaton = [](fibers::Baton& baton) -> coro::Task<int> {\n    co_await baton;\n    co_return 42;\n  };\n\n  ManualExecutor executor;\n  fibers::Baton baton;\n  auto future = co_withExecutor(&executor, taskBaton(baton)).start();\n\n  EXPECT_FALSE(future.isReady());\n\n  executor.drain();\n\n  EXPECT_FALSE(future.isReady());\n\n  baton.post();\n  executor.drain();\n\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(42, std::move(future).get());\n}\n\nTEST_F(CoroTest, FulfilledFuture) {\n  auto taskFuture = [](auto value) -> coro::Task<decltype(value)> {\n    co_return co_await folly::makeFuture(std::move(value));\n  };\n\n  EXPECT_EQ(42, coro::blockingWait(taskFuture(42)));\n}\n\nTEST_F(CoroTest, MoveOnlyReturn) {\n  auto taskFuture = [](auto value) -> coro::Task<decltype(value)> {\n    co_return co_await folly::makeFuture(std::move(value));\n  };\n\n  EXPECT_EQ(42, *coro::blockingWait(taskFuture(std::make_unique<int>(42))));\n}\n\nTEST_F(CoroTest, co_invoke) {\n  ManualExecutor executor;\n  Promise<folly::Unit> p;\n  auto coroFuture =\n      co_withExecutor(\n          &executor,\n          coro::co_invoke(\n              [f = p.getSemiFuture()]() mutable -> coro::Task<void> {\n                (void)co_await std::move(f);\n                co_return;\n              }))\n          .start();\n  executor.drain();\n  EXPECT_FALSE(coroFuture.isReady());\n\n  p.setValue(folly::unit);\n\n  executor.drain();\n  EXPECT_TRUE(coroFuture.isReady());\n}\n\nTEST_F(CoroTest, Semaphore) {\n  static constexpr size_t kTasks = 10;\n  static constexpr size_t kIterations = 10000;\n  static constexpr size_t kNumTokens = 10;\n  static constexpr size_t kNumThreads = 16;\n\n  fibers::Semaphore sem(kNumTokens);\n\n  struct Worker {\n    explicit Worker(fibers::Semaphore& s) : sem(s), t([&] { run(); }) {}\n\n    void run() {\n      folly::EventBase evb;\n\n      {\n        std::shared_ptr<folly::EventBase> completionCounter(\n            &evb, [](folly::EventBase* evb_) { evb_->terminateLoopSoon(); });\n\n        for (size_t i = 0; i < kTasks; ++i) {\n          co_withExecutor(\n              &evb,\n              coro::co_invoke([&, completionCounter]() -> coro::Task<void> {\n                for (size_t j = 0; j < kIterations; ++j) {\n                  co_await sem.co_wait();\n                  ++counter;\n                  sem.signal();\n                  --counter;\n\n                  EXPECT_LT(counter, kNumTokens);\n                  EXPECT_GE(counter, 0);\n                }\n              }))\n              .start();\n        }\n      }\n      evb.loopForever();\n    }\n\n    fibers::Semaphore& sem;\n    int counter{0};\n    std::thread t;\n  };\n\n  std::vector<Worker> workers;\n  workers.reserve(kNumThreads);\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    workers.emplace_back(sem);\n  }\n\n  for (auto& worker : workers) {\n    worker.t.join();\n  }\n\n  for (auto& worker : workers) {\n    EXPECT_EQ(0, worker.counter);\n  }\n}\n\nTEST_F(CoroTest, SemaphoreWaitWhenCancellationAlreadyRequested) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    folly::CancellationSource cancelSource;\n    cancelSource.requestCancellation();\n\n    // Run some logic while in an already-cancelled state.\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), []() -> folly::coro::Task<> {\n          folly::fibers::Semaphore sem{1};\n\n          // If in a signalled state then should complete normally\n          co_await sem.co_wait();\n\n          // And the semaphore should no longer be in the signalled state.\n\n          // But if not signalled then should complete with cancellation\n          // immediately.\n          EXPECT_THROW(co_await sem.co_wait(), folly::OperationCancelled);\n        }());\n  }());\n}\n\nTEST_F(CoroTest, CancelOutstandingSemaphoreWait) {\n  struct ExpectedError : std::exception {};\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    folly::fibers::Semaphore sem{0};\n    try {\n      co_await folly::coro::collectAll(\n          [&]() -> folly::coro::Task<> {\n            EXPECT_THROW(co_await sem.co_wait(), OperationCancelled);\n          }(),\n          []() -> folly::coro::Task<> {\n            co_await folly::coro::co_reschedule_on_current_executor;\n            // Completing the second task with an error will cause\n            // collectAll() to request cancellation of the other task\n            // which should request cancellation of sem.co_wait().\n            co_yield folly::coro::co_error(ExpectedError{});\n          }());\n    } catch (const ExpectedError&) {\n    }\n  }());\n}\n\nTEST_F(CoroTest, CancelOneSemaphoreWaitDoesNotAffectOthers) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::fibers::Semaphore sem{0};\n\n    folly::CancellationSource cancelSource;\n\n    co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<> {\n          EXPECT_THROW(\n              (co_await folly::coro::co_withCancellation(\n                  cancelSource.getToken(), sem.co_wait())),\n              OperationCancelled);\n        }(),\n        [&]() -> folly::coro::Task<> { co_await sem.co_wait(); }(),\n        [&]() -> folly::coro::Task<> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          cancelSource.requestCancellation();\n          sem.signal();\n        }());\n  }());\n}\n\nTEST_F(CoroTest, FutureTry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto task42 = []() -> coro::Task<int> { co_return 42; };\n    auto taskException = []() -> coro::Task<int> {\n      throw std::runtime_error(\"Test exception\");\n      co_return 42;\n    };\n\n    {\n      auto result = co_await folly::coro::co_awaitTry(task42().semi());\n      EXPECT_TRUE(result.hasValue());\n      EXPECT_EQ(42, result.value());\n    }\n\n    {\n      auto result = co_await folly::coro::co_awaitTry(taskException().semi());\n      EXPECT_TRUE(result.hasException());\n    }\n\n    {\n      auto result = co_await folly::coro::co_awaitTry(\n          task42().semi().via(co_await folly::coro::co_current_executor));\n      EXPECT_TRUE(result.hasValue());\n      EXPECT_EQ(42, result.value());\n    }\n\n    {\n      auto result = co_await folly::coro::co_awaitTry(\n          taskException().semi().via(\n              co_await folly::coro::co_current_executor));\n      EXPECT_TRUE(result.hasException());\n    }\n  }());\n}\n\nTEST_F(CoroTest, CancellableSleep) {\n  using namespace std::chrono;\n  using namespace std::chrono_literals;\n\n  CancellationSource cancelSrc;\n\n  auto start = steady_clock::now();\n  EXPECT_THROW(\n      coro::blockingWait([&]() -> coro::Task<void> {\n        co_await coro::collectAll(\n            [&]() -> coro::Task<void> {\n              co_await coro::co_withCancellation(\n                  cancelSrc.getToken(), coro::sleep(10s));\n            }(),\n            [&]() -> coro::Task<void> {\n              co_await coro::co_reschedule_on_current_executor;\n              co_await coro::co_reschedule_on_current_executor;\n              co_await coro::co_reschedule_on_current_executor;\n              cancelSrc.requestCancellation();\n            }());\n      }()),\n      OperationCancelled);\n  auto end = steady_clock::now();\n  CHECK((end - start) < 1s);\n}\n\nTEST_F(CoroTest, DefaultConstructible) {\n  coro::blockingWait([]() -> coro::Task<void> {\n    struct S {\n      int x = 42;\n    };\n\n    auto taskS = []() -> coro::Task<S> { co_return {}; };\n\n    auto s = co_await taskS();\n    EXPECT_EQ(42, s.x);\n  }());\n}\n\nTEST(Coro, CoReturnTry) {\n  EXPECT_EQ(42, folly::coro::blockingWait([]() -> folly::coro::Task<int> {\n              co_return folly::Try<int>(42);\n            }()));\n\n  struct ExpectedException : public std::runtime_error {\n    ExpectedException() : std::runtime_error(\"ExpectedException\") {}\n  };\n  EXPECT_THROW(\n      folly::coro::blockingWait([]() -> folly::coro::Task<int> {\n        co_return folly::Try<int>(ExpectedException());\n      }()),\n      ExpectedException);\n\n  EXPECT_EQ(42, folly::coro::blockingWait([]() -> folly::coro::Task<int> {\n              folly::Try<int> t(42);\n              co_return t;\n            }()));\n\n  EXPECT_EQ(42, folly::coro::blockingWait([]() -> folly::coro::Task<int> {\n              const folly::Try<int> tConst(42);\n              co_return tConst;\n            }()));\n}\n\nTEST(Coro, CoThrow) {\n  struct ExpectedException : public std::runtime_error {\n    ExpectedException() : std::runtime_error(\"ExpectedException\") {}\n  };\n  EXPECT_THROW(\n      folly::coro::blockingWait([]() -> folly::coro::Task<int> {\n        co_yield folly::coro::co_error(ExpectedException());\n        ADD_FAILURE() << \"unreachable\";\n        // Intentional lack of co_return statement to check\n        // that compiler treats code after co_yield co_error()\n        // as unreachable.\n      }()),\n      ExpectedException);\n\n  EXPECT_THROW(\n      folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n        co_yield folly::coro::co_error(ExpectedException());\n        ADD_FAILURE() << \"unreachable\";\n      }()),\n      ExpectedException);\n}\n\nTEST_F(CoroTest, DetachOnCancel) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    folly::CancellationSource cancelSource;\n    cancelSource.requestCancellation();\n\n    // Run some logic while in an already-cancelled state.\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), []() -> folly::coro::Task<> {\n          EXPECT_THROW(\n              co_await folly::coro::detachOnCancel(\n                  folly::futures::sleep(std::chrono::seconds{1})\n                      .deferValue([](auto) { return 42; })),\n              folly::OperationCancelled);\n        }());\n  }());\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    folly::CancellationSource cancelSource;\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), []() -> folly::coro::Task<> {\n          EXPECT_EQ(\n              42,\n              co_await folly::coro::detachOnCancel(\n                  folly::futures::sleep(std::chrono::milliseconds{10})\n                      .deferValue([](auto) { return 42; })));\n        }());\n  }());\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    folly::CancellationSource cancelSource;\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), []() -> folly::coro::Task<> {\n          co_await folly::coro::detachOnCancel([]() -> folly::coro::Task<void> {\n            co_await folly::futures::sleep(std::chrono::milliseconds{10});\n          }());\n        }());\n  }());\n}\n\nstruct CountingManualExecutor : public folly::ManualExecutor {\n  void add(Func func) override {\n    ++addCount_;\n    ManualExecutor::add(std::move(func));\n  }\n\n  ssize_t getAddCount() const { return addCount_.load(); }\n\n private:\n  std::atomic<ssize_t> addCount_{0};\n};\n\nssize_t runAndCountExecutorAdd(folly::coro::Task<void> task) {\n  CountingManualExecutor executor;\n  auto future = co_withExecutor(&executor, std::move(task)).start();\n\n  executor.drain();\n\n  EXPECT_TRUE(future.isReady());\n  return executor.getAddCount();\n}\n\nTEST_F(CoroTest, SemiNoReschedule) {\n  auto task42 = []() -> coro::Task<int> { co_return 42; };\n  auto semiFuture42 = []() {\n    return makeSemiFuture().deferValue([](auto) { return 42; });\n  };\n  EXPECT_EQ(\n      2, // One extra for keepAlive release logic of ManualExecutor\n      runAndCountExecutorAdd(folly::coro::co_invoke([&]() -> coro::Task<void> {\n        EXPECT_EQ(42, co_await task42());\n      })));\n  EXPECT_EQ(\n      2, // One extra for keepAlive release logic of ManualExecutor\n      runAndCountExecutorAdd(folly::coro::co_invoke([&]() -> coro::Task<void> {\n        EXPECT_EQ(42, co_await task42().semi());\n      })));\n  EXPECT_EQ(\n      2, // One extra for keepAlive release logic of ManualExecutor\n      runAndCountExecutorAdd(folly::coro::co_invoke([&]() -> coro::Task<void> {\n        EXPECT_EQ(42, co_await semiFuture42());\n      })));\n}\n\nCO_TEST(FutureAwaiter, InvalidFutureTerminates) {\n  auto fut = makeFuture(42);\n  [[maybe_unused]] auto fut2 = std::move(fut);\n  // NOLINTNEXTLINE(bugprone-use-after-move)\n  EXPECT_DEATH(co_await std::move(fut), \"Future invalid\");\n}\n\nCO_TEST(FutureAwaiter, AlreadyContinuedReturnsError) {\n  Promise<int> promise;\n  auto fut = promise.getFuture();\n  [[maybe_unused]] auto fut2 = fut.via(&InlineExecutor::instance());\n  if constexpr (kIsDebug) {\n    EXPECT_DEATH(co_await std::move(fut), \"state_ != State::OnlyCallback\");\n  } else {\n    auto result = co_await coro::co_awaitTry(std::move(fut));\n    EXPECT_TRUE(result.hasException());\n    EXPECT_TRUE(\n        result.exception().is_compatible_with<FutureAlreadyContinued>());\n  }\n}\n#endif\n"
  },
  {
    "path": "folly/coro/test/CoroutineTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Coroutine.h>\n\n#include <folly/lang/Keep.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nextern \"C\" FOLLY_KEEP bool\ncheck_folly_coro_detect_promise_return_object_eager_conversion() {\n  return folly::coro::detect_promise_return_object_eager_conversion();\n}\n\nstruct CoroutineTest : testing::Test {};\n\nTEST_F(CoroutineTest, detect_promise_return_object_eager_conversion) {\n  auto const eager =\n      folly::coro::detect_promise_return_object_eager_conversion();\n  if (folly::kMscVer) {\n    EXPECT_EQ(folly::kMscVer < 1925, eager);\n  }\n  if (folly::kGnuc && !folly::kIsClang) {\n    EXPECT_FALSE(eager);\n  }\n  if (folly::kIsClang) {\n    constexpr auto ver = folly::kClangVerMajor;\n    if (ver <= 14 || ver >= 17) {\n      EXPECT_FALSE(eager);\n    } else {\n      SUCCEED(); // we sometimes use patched llvm-15/llvm-16\n    }\n  }\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/CurrentExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Task.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nclass CoRescheduleOnCurrentExecutorTest : public testing::Test {};\n\nTEST_F(CoRescheduleOnCurrentExecutorTest, example) {\n  std::vector<int> results;\n  folly::coro::blockingWait(\n      folly::coro::collectAll(\n          folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n            for (int i = 0; i <= 10; i += 2) {\n              if (i == 6) {\n                co_await folly::coro::co_reschedule_on_current_executor;\n              }\n              results.push_back(i);\n            }\n          }),\n          folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n            for (int i = 1; i < 10; i += 2) {\n              if (i == 7) {\n                co_await folly::coro::co_reschedule_on_current_executor;\n              }\n              results.push_back(i);\n            }\n          })));\n\n  CHECK_EQ(11, results.size());\n  const int expected[11] = {0, 2, 4, 1, 3, 5, 6, 8, 10, 7, 9};\n  for (int i = 0; i < 11; ++i) {\n    CHECK_EQ(expected[i], results[i]);\n  }\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/ErrorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Result.h>\n#include <folly/coro/ValueOrError.h>\n#include <folly/coro/safe/NowTask.h>\n\n#include <type_traits>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly;\nusing namespace folly::coro;\n\nTEST(CoErrorTest, constructible) {\n  EXPECT_TRUE((std::is_constructible_v<co_error, exception_wrapper>));\n  EXPECT_TRUE((std::is_constructible_v<co_error, std::runtime_error>));\n  EXPECT_TRUE(\n      (std::is_constructible_v<\n          co_error,\n          std::in_place_type_t<std::runtime_error>,\n          std::string>));\n  EXPECT_FALSE((std::is_constructible_v<co_error, int>));\n}\n\n// NB: Cancellation is not an error (https://wg21.link/p1677), but in current\n// coro, the handling is so intertwined that we test it here.\n\nCO_TEST(CoCancellationTest, propagateOperationCancelled) {\n  auto cancelledTask = []() -> now_task<> {\n    co_yield co_stopped_may_throw;\n    ADD_FAILURE() << \"Not reached\";\n  };\n\n  // `value_or_error_or_stopped` & `co_awaitTry` interrupt cancellation\n  EXPECT_TRUE(\n      (co_await value_or_error_or_stopped(cancelledTask())).has_stopped());\n  EXPECT_TRUE(\n      // Prefer `has_stopped()` in coro code.  Outside of coro code, catching\n      // `OperationCancelled` is OK.\n      (co_await co_awaitTry(cancelledTask()))\n          .hasException<OperationCancelled>());\n\n  // Throws if awaited directly in coro or non-coro code\n  EXPECT_THROW((co_await cancelledTask()), OperationCancelled);\n  EXPECT_THROW(blocking_wait(cancelledTask()), OperationCancelled);\n\n  // `value_or_error` does not interrupt cancellation\n  auto outerTask = [&]() -> now_task<> {\n    [[maybe_unused]] result<> r = co_await value_or_error(cancelledTask());\n    ADD_FAILURE() << \"Not reached\";\n  };\n  EXPECT_TRUE((co_await value_or_error_or_stopped(outerTask())).has_stopped());\n  EXPECT_THROW((co_await outerTask()), OperationCancelled);\n  EXPECT_THROW(blocking_wait(outerTask()), OperationCancelled);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/FilterTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Filter.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nclass FilterTest : public testing::Test {};\n\nTEST_F(FilterTest, SimpleStream) {\n  const auto allNumbers = []() -> AsyncGenerator<int> {\n    for (int i = 0; i < 10; ++i) {\n      co_yield i;\n    }\n  };\n\n  auto evenNumbers = filter(allNumbers(), [](int i) { return i % 2 == 0; });\n  EXPECT_EQ(0, blockingWait(evenNumbers.next()).value());\n  EXPECT_EQ(2, blockingWait(evenNumbers.next()).value());\n  EXPECT_EQ(4, blockingWait(evenNumbers.next()).value());\n  EXPECT_EQ(6, blockingWait(evenNumbers.next()).value());\n  EXPECT_EQ(8, blockingWait(evenNumbers.next()).value());\n  EXPECT_FALSE(blockingWait(evenNumbers.next()));\n}\n\nTEST_F(FilterTest, EmptyInputStream) {\n  const auto emptyStream = []() -> AsyncGenerator<int> { co_return; };\n\n  auto emptyStreamFiltered = filter(emptyStream(), [](int) { return true; });\n  EXPECT_FALSE(blockingWait(emptyStreamFiltered.next()));\n}\n\nTEST_F(FilterTest, EmptyOutputStream) {\n  const auto nonEmptyStream = []() -> AsyncGenerator<int> {\n    co_yield 0;\n    co_yield 1;\n    co_yield 2;\n  };\n\n  auto emptyOutputStream = filter(nonEmptyStream(), [](int) { return false; });\n  EXPECT_FALSE(blockingWait(emptyOutputStream.next()));\n}\n\nTEST_F(FilterTest, ThrowingStream) {\n  struct Exception : std::exception {};\n\n  const auto throwingStream = []() -> AsyncGenerator<int> {\n    co_yield 0;\n    co_yield 1;\n    throw Exception{};\n  };\n\n  auto throwingStreamFiltered = filter(throwingStream(), [](int) {\n    return true;\n  });\n  EXPECT_EQ(0, blockingWait(throwingStreamFiltered.next()).value());\n  EXPECT_EQ(1, blockingWait(throwingStreamFiltered.next()).value());\n  EXPECT_THROW(blockingWait(throwingStreamFiltered.next()), Exception);\n}\n\nTEST_F(FilterTest, ThrowingPredicate) {\n  struct Exception : std::exception {};\n\n  auto nonThrowingStream = []() -> AsyncGenerator<int> {\n    co_yield 0;\n    co_yield 1;\n    co_yield 2;\n  };\n\n  auto evenNumbers = filter(nonThrowingStream(), [](int i) {\n    if (i == 1) {\n      throw Exception{};\n    };\n    return true;\n  });\n  EXPECT_EQ(0, blockingWait(evenNumbers.next()).value());\n  EXPECT_THROW(blockingWait(evenNumbers.next()), Exception);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/FutureUtilTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/FutureUtil.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nstatic folly::coro::Task<int> makeTask() {\n  co_return 42;\n}\nstatic folly::coro::AsyncGenerator<int> makeGen() {\n  co_yield 42;\n}\nstatic folly::coro::Task<void> makeVoidTask() {\n  co_return;\n}\n\nTEST(FutureUtilTest, ToTask) {\n  EXPECT_EQ(folly::coro::blockingWait(folly::coro::toTask(makeTask())), 42);\n\n  auto gen = makeGen();\n  EXPECT_EQ(*folly::coro::blockingWait(folly::coro::toTask(gen.next())), 42);\n\n  folly::coro::Baton baton;\n  auto task = folly::coro::toTask(std::ref(baton));\n  baton.post();\n  folly::coro::blockingWait(std::move(task));\n}\n\nTEST(FutureUtilTest, ToSemiFuture) {\n  folly::ManualExecutor ex;\n\n  auto semi = folly::coro::toSemiFuture(makeTask());\n  EXPECT_FALSE(semi.isReady());\n  semi = std::move(semi).via(&ex);\n  EXPECT_FALSE(semi.isReady());\n  ex.drain();\n  EXPECT_TRUE(semi.isReady());\n  EXPECT_EQ(std::move(semi).get(), 42);\n\n  auto gen = makeGen();\n  auto semi2 = folly::coro::toSemiFuture(gen.next());\n  EXPECT_FALSE(semi2.isReady());\n  semi2 = std::move(semi2).via(&ex);\n  EXPECT_FALSE(semi2.isReady());\n  ex.drain();\n  EXPECT_TRUE(semi2.isReady());\n  EXPECT_EQ(*std::move(semi2).get(), 42);\n\n  folly::coro::Baton baton;\n  auto semi3 = folly::coro::toSemiFuture(std::ref(baton));\n  EXPECT_FALSE(semi3.isReady());\n  semi3 = std::move(semi3).via(&ex);\n  EXPECT_FALSE(semi3.isReady());\n  ex.drain();\n  EXPECT_FALSE(semi3.isReady());\n  baton.post();\n  ex.drain();\n  EXPECT_TRUE(semi3.isReady());\n}\n\nTEST(FutureUtilTest, ToFuture) {\n  folly::ManualExecutor ex;\n\n  auto fut = folly::coro::toFuture(makeTask(), &ex);\n  EXPECT_FALSE(fut.isReady());\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_EQ(std::move(fut).get(), 42);\n\n  auto gen = makeGen();\n  auto fut2 = folly::coro::toFuture(gen.next(), &ex);\n  EXPECT_FALSE(fut2.isReady());\n  ex.drain();\n  EXPECT_TRUE(fut2.isReady());\n  EXPECT_EQ(*std::move(fut2).get(), 42);\n\n  folly::coro::Baton baton;\n  auto fut3 = folly::coro::toFuture(std::ref(baton), &ex);\n  EXPECT_FALSE(fut3.isReady());\n  ex.drain();\n  EXPECT_FALSE(fut3.isReady());\n  baton.post();\n  ex.drain();\n  EXPECT_TRUE(fut3.isReady());\n}\n\nTEST(FutureUtilTest, VoidRoundtrip) {\n  folly::coro::Task<void> task = makeVoidTask();\n  folly::SemiFuture<folly::Unit> semi =\n      folly::coro::toSemiFuture(std::move(task));\n  task = folly::coro::toTask(std::move(semi));\n  folly::coro::blockingWait(std::move(task));\n}\n\nCO_TEST(FutureUtilTest, ToTaskInterruptOnCancelFutureWithCancellation) {\n  auto [p, f] = folly::makePromiseContract<folly::Unit>();\n\n  // to verify that cancellation propagates into the future interrupt-handler\n  folly::exception_wrapper interrupt;\n  p.setInterruptHandler([&, p_ = &p](auto&& ew) {\n    interrupt = ew;\n    p_->setException(std::move(ew));\n  });\n\n  // to verify that deferred work runs\n  folly::Try<folly::Unit> touched;\n  auto f1 = std::move(f).defer([&](folly::Try<folly::Unit> t) { touched = t; });\n  CO_ASSERT_FALSE(touched.hasException()); // sanity check\n\n  // run the scenario\n  folly::CancellationSource csource;\n  auto result = std::get<0>(co_await folly::coro::collectAllTry(\n\n      // a task that will be cancelled, wrapping a future to be interrupted\n      folly::coro::co_withCancellation(\n          csource.getToken(),\n          std::invoke([&, f_ = &f1]() -> folly::coro::Task<> {\n            CO_ASSERT_FALSE(touched.hasException()); // sanity check\n            co_await folly::coro::toTaskInterruptOnCancel(std::move(*f_));\n          })),\n\n      // a task that will do the cancelling, after waiting a bit\n      std::invoke([&]() -> folly::coro::Task<> {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        csource.requestCancellation();\n        CO_ASSERT_FALSE(touched.hasException()); // sanity check\n      })));\n\n  // verify that the future was interrupted\n  EXPECT_TRUE(touched.hasException<folly::FutureCancellation>());\n  EXPECT_TRUE(result.hasException<folly::OperationCancelled>());\n  EXPECT_TRUE(interrupt.get_exception<folly::FutureCancellation>());\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/GeneratorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <algorithm>\n\n#include <folly/ScopeGuard.h>\n#include <folly/coro/Generator.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace coro {\n\nclass GeneratorTest : public testing::Test {};\n\nTEST_F(GeneratorTest, DefaultConstructed_EmptySequence) {\n  Generator<std::uint32_t> ints;\n  EXPECT_EQ(ints.begin(), ints.end());\n}\n\nTEST_F(GeneratorTest, NonRecursiveUse) {\n  auto f = []() -> Generator<float> {\n    co_yield 1.0f;\n    co_yield 2.0f;\n  };\n\n  auto gen = f();\n  auto iter = gen.begin();\n  EXPECT_EQ(*iter, 1.0f);\n  ++iter;\n  EXPECT_EQ(*iter, 2.0f);\n  ++iter;\n  EXPECT_EQ(iter, gen.end());\n}\n\nTEST_F(GeneratorTest, ThrowsBeforeYieldingFirstElement_RethrowsFromBegin) {\n  class MyException : public std::exception {};\n\n  auto f = []() -> Generator<std::uint32_t> {\n    throw MyException{};\n    co_return;\n  };\n\n  auto gen = f();\n  EXPECT_THROW(gen.begin(), MyException);\n}\n\nTEST_F(GeneratorTest, ThrowsAfterYieldingFirstElement_RethrowsFromIncrement) {\n  class MyException : public std::exception {};\n\n  auto f = []() -> Generator<std::uint32_t> {\n    co_yield 1;\n    throw MyException{};\n  };\n\n  auto gen = f();\n  auto iter = gen.begin();\n  EXPECT_EQ(*iter, 1u);\n  EXPECT_THROW(++iter, MyException);\n}\n\nTEST_F(GeneratorTest, NotStartedUntilCalled) {\n  bool reachedA = false;\n  bool reachedB = false;\n  bool reachedC = false;\n  auto f = [&]() -> Generator<std::uint32_t> {\n    reachedA = true;\n    co_yield 1;\n    reachedB = true;\n    co_yield 2;\n    reachedC = true;\n  };\n\n  auto gen = f();\n  EXPECT_FALSE(reachedA);\n  auto iter = gen.begin();\n  EXPECT_TRUE(reachedA);\n  EXPECT_FALSE(reachedB);\n  EXPECT_EQ(*iter, 1u);\n  ++iter;\n  EXPECT_TRUE(reachedB);\n  EXPECT_FALSE(reachedC);\n  EXPECT_EQ(*iter, 2u);\n  ++iter;\n  EXPECT_TRUE(reachedC);\n  EXPECT_EQ(iter, gen.end());\n}\n\nTEST_F(GeneratorTest, DestroyedBeforeCompletion_DestructsObjectsOnStack) {\n  bool destructed = false;\n  bool completed = false;\n  auto f = [&]() -> Generator<std::uint32_t> {\n    SCOPE_EXIT {\n      destructed = true;\n    };\n\n    co_yield 1;\n    co_yield 2;\n    completed = true;\n  };\n\n  {\n    auto g = f();\n    auto it = g.begin();\n    auto itEnd = g.end();\n    EXPECT_NE(it, itEnd);\n    EXPECT_EQ(*it, 1u);\n    EXPECT_FALSE(destructed);\n  }\n\n  EXPECT_FALSE(completed);\n  EXPECT_TRUE(destructed);\n}\n\nTEST_F(GeneratorTest, SimpleRecursiveYield) {\n  auto f = [](int n, auto& f_) -> Generator<const std::uint32_t> {\n    co_yield n;\n    if (n > 0) {\n      co_yield f_(n - 1, f_);\n      co_yield n;\n    }\n  };\n\n  auto f2 = [&f](int n) { return f(n, f); };\n\n  {\n    auto gen = f2(1);\n    auto iter = gen.begin();\n    EXPECT_EQ(*iter, 1u);\n    ++iter;\n    EXPECT_EQ(*iter, 0u);\n    ++iter;\n    EXPECT_EQ(*iter, 1u);\n    ++iter;\n    EXPECT_EQ(iter, gen.end());\n  }\n\n  {\n    auto gen = f2(2);\n    auto iter = gen.begin();\n    EXPECT_EQ(*iter, 2u);\n    ++iter;\n    EXPECT_EQ(*iter, 1u);\n    ++iter;\n    EXPECT_EQ(*iter, 0u);\n    ++iter;\n    EXPECT_EQ(*iter, 1u);\n    ++iter;\n    EXPECT_EQ(*iter, 2u);\n    ++iter;\n    EXPECT_EQ(iter, gen.end());\n  }\n}\n\nTEST_F(GeneratorTest, NestedEmptyYield) {\n  auto f = []() -> Generator<std::uint32_t> { co_return; };\n\n  auto g = [&f]() -> Generator<std::uint32_t> {\n    co_yield 1;\n    co_yield f();\n    co_yield 2;\n  };\n\n  auto gen = g();\n  auto iter = gen.begin();\n  EXPECT_EQ(*iter, 1u);\n  ++iter;\n  EXPECT_EQ(*iter, 2u);\n  ++iter;\n  EXPECT_EQ(iter, gen.end());\n}\n\nTEST_F(GeneratorTest, ExceptionThrownFromRecursiveCall_CanBeCaughtByCaller) {\n  class SomeException : public std::exception {};\n  bool caught = false;\n\n  auto f = [&](std::uint32_t depth, auto&& f_) -> Generator<std::uint32_t> {\n    if (depth == 1u) {\n      throw SomeException{};\n    }\n\n    co_yield 1;\n\n    try {\n      co_yield f_(1, f_);\n    } catch (const SomeException&) {\n      caught = true;\n    }\n\n    co_yield 2;\n  };\n\n  auto gen = f(0, f);\n  auto iter = gen.begin();\n  EXPECT_EQ(*iter, 1u);\n  EXPECT_FALSE(caught);\n  ++iter;\n  EXPECT_TRUE(caught);\n  EXPECT_EQ(*iter, 2u);\n  ++iter;\n  EXPECT_EQ(iter, gen.end());\n}\n\nTEST_F(GeneratorTest, ExceptionThrownFromNestedCall_CanBeCaughtByCaller) {\n  class SomeException : public std::exception {};\n\n  auto f = [](std::uint32_t depth, auto&& f_) -> Generator<std::uint32_t> {\n    if (depth == 4u) {\n      throw SomeException{};\n    } else if (depth == 3u) {\n      co_yield 3;\n\n      bool caught = false;\n      try {\n        co_yield f_(4, f_);\n      } catch (const SomeException&) {\n        caught = true;\n      }\n\n      co_yield caught ? 33 : 1337;\n\n      throw SomeException{};\n    } else if (depth == 2u) {\n      bool caught = false;\n      try {\n        co_yield f_(3, f_);\n      } catch (const SomeException&) {\n        caught = true;\n      }\n\n      if (caught) {\n        co_yield 2;\n      }\n    } else {\n      co_yield 1;\n      co_yield f_(2, f_);\n      co_yield f_(3, f_);\n    }\n  };\n\n  auto gen = f(1, f);\n  auto iter = gen.begin();\n  EXPECT_EQ(*iter, 1u);\n  ++iter;\n  EXPECT_EQ(*iter, 3u);\n  ++iter;\n  EXPECT_EQ(*iter, 33u);\n  ++iter;\n  EXPECT_EQ(*iter, 2u);\n  ++iter;\n  EXPECT_EQ(*iter, 3u);\n  ++iter;\n  EXPECT_EQ(*iter, 33u);\n  EXPECT_THROW(++iter, SomeException);\n\n  EXPECT_EQ(iter, gen.end());\n}\n\nnamespace {\nGenerator<std::uint32_t> iterate_range(std::uint32_t begin, std::uint32_t end) {\n  if ((end - begin) <= 10u) {\n    for (std::uint32_t i = begin; i < end; ++i) {\n      co_yield i;\n    }\n  } else {\n    std::uint32_t mid = begin + (end - begin) / 2;\n    co_yield iterate_range(begin, mid);\n    co_yield iterate_range(mid, end);\n  }\n}\n} // namespace\n\nTEST_F(GeneratorTest, UsageInStandardAlgorithms) {\n  {\n    auto a = iterate_range(5, 30);\n    auto b = iterate_range(5, 30);\n    EXPECT_TRUE(std::equal(a.begin(), a.end(), b.begin(), b.end()));\n  }\n\n  {\n    auto a = iterate_range(5, 30);\n    auto b = iterate_range(5, 300);\n    EXPECT_FALSE(std::equal(a.begin(), a.end(), b.begin(), b.end()));\n  }\n}\n\nTEST_F(GeneratorTest, InvokeLambda) {\n  auto ptr = std::make_unique<int>(123);\n  auto gen = folly::coro::co_invoke(\n      [p = std::move(\n           ptr)]() mutable -> folly::coro::Generator<std::unique_ptr<int>&&> {\n        co_yield std::move(p);\n      });\n\n  auto it = gen.begin();\n  auto result = std::move(*it);\n  EXPECT_NE(result, nullptr);\n  EXPECT_EQ(*result, 123);\n}\n} // namespace coro\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/GmockHelpersTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdexcept>\n#include <string>\n#include <vector>\n\n#include <folly/Portability.h>\n\n#include <gtest/gtest-death-test.h>\n\n#include <folly/coro/GmockHelpers.h>\n#include <folly/coro/GtestHelpers.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace ::testing;\nusing namespace folly::coro::gmock_helpers;\n\nnamespace {\nclass Foo {\n public:\n  virtual ~Foo() = default;\n  virtual folly::coro::Task<std::vector<std::string>> getValues() = 0;\n\n  virtual folly::coro::Task<std::string> getString() = 0;\n  virtual folly::coro::Task<std::string> getStringArg(std::string) = 0;\n\n  virtual folly::coro::Task<void> getVoid() = 0;\n};\n\nclass MockFoo : Foo {\n public:\n  MOCK_METHOD(folly::coro::Task<std::vector<std::string>>, getValues, ());\n\n  MOCK_METHOD(folly::coro::Task<std::string>, getString, ());\n  MOCK_METHOD(folly::coro::Task<std::string>, getStringArg, (std::string));\n  MOCK_METHOD(folly::coro::Task<void>, getVoid, ());\n};\n\nclass ExplicitlyCopyableString : public std::string {\n public:\n  explicit ExplicitlyCopyableString(const char* cstr) : std::string(cstr) {}\n  explicit ExplicitlyCopyableString(const ExplicitlyCopyableString&) = default;\n  ExplicitlyCopyableString(ExplicitlyCopyableString&&) = default;\n  ExplicitlyCopyableString& operator=(const ExplicitlyCopyableString&) = delete;\n  [[maybe_unused]] ExplicitlyCopyableString& operator=(\n      ExplicitlyCopyableString&&) = default;\n};\n} // namespace\n\nTEST(CoroGTestHelpers, CoInvokeAvoidsDanglingReferences) {\n  MockFoo mock;\n\n  const std::vector<std::string> values{\"1\", \"2\", \"3\"};\n  EXPECT_CALL(mock, getValues())\n      .WillRepeatedly(\n          CoInvoke([&values]() -> folly::coro::Task<std::vector<std::string>> {\n            co_return values;\n          }));\n\n  auto ret = folly::coro::blockingWait(mock.getValues());\n  EXPECT_EQ(ret, values);\n\n  auto ret2 = folly::coro::blockingWait(mock.getValues());\n  EXPECT_EQ(ret2, values);\n}\n\nTEST(CoroGTestHelpers, CoInvokeWithoutArgsTest) {\n  MockFoo mock;\n  int numCalls = 0;\n\n  EXPECT_CALL(mock, getString())\n      .WillRepeatedly(\n          CoInvokeWithoutArgs([&numCalls]() -> folly::coro::Task<std::string> {\n            if (numCalls++ == 0) {\n              co_return \"123\";\n            } else {\n              co_return \"abc\";\n            }\n          }));\n\n  auto ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"123\");\n  EXPECT_EQ(numCalls, 1);\n\n  ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n  EXPECT_EQ(numCalls, 2);\n}\n\nTEST(CoroGTestHelpers, CoReturnTest) {\n  MockFoo mock;\n\n  EXPECT_CALL(mock, getString()).WillRepeatedly(CoReturn(std::string(\"abc\")));\n\n  auto ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n\n  ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n}\n\nTEST(CoroGTestHelpers, CoReturnExplicitCopyTest) {\n  MockFoo mock;\n\n  EXPECT_CALL(mock, getString())\n      .WillRepeatedly(CoReturn(ExplicitlyCopyableString(\"abc\")));\n\n  auto ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n\n  ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n}\n\nTEST(CoroGTestHelpers, CoReturnWithImplicitConversionTest) {\n  MockFoo mock;\n\n  EXPECT_CALL(mock, getString()).WillRepeatedly(CoReturn(\"abc\"));\n\n  auto ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n\n  ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n}\n\nTEST(CoroGTestHelpers, CoReturnByMoveTest) {\n  MockFoo mock;\n\n  EXPECT_CALL(mock, getString())\n      .WillRepeatedly(CoReturnByMove(std::string(\"abc\")));\n\n  auto ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n}\n\nTEST(CoroGTestHelpers, CoReturnByMoveWithImplicitConversionTest) {\n  MockFoo mock;\n\n  struct ImplicitToStringMoveOnly {\n    constexpr ImplicitToStringMoveOnly() noexcept = default;\n    ImplicitToStringMoveOnly(const ImplicitToStringMoveOnly&) = delete;\n    ImplicitToStringMoveOnly& operator=(const ImplicitToStringMoveOnly&) =\n        delete;\n\n    ImplicitToStringMoveOnly(ImplicitToStringMoveOnly&&) = default;\n    [[maybe_unused]] ImplicitToStringMoveOnly& operator=(\n        ImplicitToStringMoveOnly&&) = default;\n\n    operator std::string() { return \"abc\"; }\n  };\n\n  EXPECT_CALL(mock, getString())\n      .WillRepeatedly(CoReturnByMove(ImplicitToStringMoveOnly{}));\n\n  auto ret = folly::coro::blockingWait(mock.getString());\n  EXPECT_EQ(ret, \"abc\");\n}\n\nTEST(CoroGTestHelpers, CoVoidReturnTypeTest) {\n  MockFoo mock;\n\n  EXPECT_CALL(mock, getVoid()).WillRepeatedly(CoReturn());\n\n  EXPECT_NO_THROW(folly::coro::blockingWait(mock.getVoid()));\n}\n\nTEST(CoroLambdaGtest, CoThrowTest) {\n  MockFoo mock;\n\n  std::runtime_error ex(\"error\");\n  EXPECT_CALL(mock, getVoid())\n      .WillOnce(CoThrow<void>(ex))\n      .WillOnce(CoThrow<void>(std::out_of_range(\"range error\")));\n\n  EXPECT_THROW(folly::coro::blockingWait(mock.getVoid()), std::runtime_error);\n  EXPECT_THROW(folly::coro::blockingWait(mock.getVoid()), std::out_of_range);\n\n  EXPECT_CALL(mock, getString()).WillOnce(CoThrow<std::string>(ex));\n  EXPECT_THROW(folly::coro::blockingWait(mock.getString()), std::runtime_error);\n}\n\nCO_TEST(CoAssertThat, CoAssertThat) {\n  CO_ASSERT_THAT(1, Gt(0));\n}\n\nCO_TEST(CoroGTestHelpers, CoInvokeMemberFunction) {\n  struct S {\n    folly::coro::Task<std::string> arg(std::string x) { co_return x + \" bar\"; }\n    folly::coro::Task<std::string> noArg() { co_return \"foo\"; }\n  } s;\n\n  MockFoo mock;\n  EXPECT_CALL(mock, getStringArg(_))\n      .WillOnce(CoInvoke(&s, &S::arg))\n      .WillOnce(CoInvokeWithoutArgs(&s, &S::noArg));\n\n  EXPECT_EQ(co_await mock.getStringArg(\"baz\"), \"baz bar\");\n  EXPECT_EQ(co_await mock.getStringArg(\"\"), \"foo\");\n}\n\nTEST(CoroGTestHelpers, CoThrowsMatcherBasic) {\n  EXPECT_THAT(\n      []() -> folly::coro::Task<void> {\n        throw std::runtime_error(\"test error message\");\n        co_return;\n      }(),\n      CoThrows<std::runtime_error>());\n}\n\nTEST(CoroGTestHelpers, CoThrowsMatcherWithExceptionMatcher) {\n  EXPECT_THAT(\n      []() -> folly::coro::Task<int> {\n        throw std::runtime_error(\"specific error\");\n        co_return 42;\n      }(),\n      CoThrows<std::runtime_error>(\n          Property(&std::runtime_error::what, HasSubstr(\"specific\"))));\n}\n\nTEST(CoroGTestHelpers, CoThrowsMessageMatcher) {\n  EXPECT_THAT(\n      []() -> folly::coro::Task<void> {\n        throw std::runtime_error(\"database connection failed\");\n        co_return;\n      }(),\n      CoThrowsMessage<std::runtime_error>(HasSubstr(\"connection failed\")));\n}\n\nTEST(CoroGTestHelpers, CoThrowsMessageMatcherExactMatch) {\n  EXPECT_THAT(\n      []() -> folly::coro::Task<std::string> {\n        throw std::invalid_argument(\"invalid input\");\n        co_return \"never reached\";\n      }(),\n      CoThrowsMessage<std::invalid_argument>(Eq(\"invalid input\")));\n}\n\nTEST(CoroGTestHelpers, CoThrowsNoExceptionFails) {\n  EXPECT_THAT(\n      []() -> folly::coro::Task<void> { co_return; }(),\n      Not(CoThrows<std::runtime_error>()));\n}\n\nTEST(CoroGTestHelpers, CoThrowsWrongExceptionTypeFails) {\n  EXPECT_THAT(\n      []() -> folly::coro::Task<void> {\n        throw std::logic_error(\"wrong type\");\n        co_return;\n      }(),\n      Not(CoThrows<std::runtime_error>()));\n}\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/test/GtestHelpersTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/portability/GTest.h>\n\n#include <folly/coro/GtestHelpers.h>\n\nusing namespace ::testing;\n\nnamespace {\nfolly::coro::Task<int> co_getInt(int x) {\n  co_return x;\n}\n\nstruct GtestHelpersMultiplicationTestParam {\n  int x;\n  int y;\n  int expectedProduct;\n};\n} // namespace\n\nclass GtestHelpersMultiplicationTest\n    : public TestWithParam<GtestHelpersMultiplicationTestParam> {};\n\nCO_TEST_P(GtestHelpersMultiplicationTest, BasicTest) {\n  const auto& param = GetParam();\n  int product = (co_await co_getInt(param.x)) * (co_await co_getInt(param.y));\n\n  EXPECT_EQ(product, param.expectedProduct);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    GtestHelpersMultiplicationTest,\n    GtestHelpersMultiplicationTest,\n    ValuesIn(\n        std::vector<GtestHelpersMultiplicationTestParam>{\n            {1, 1, 1},\n            {1, 2, 2},\n            {2, 2, 4},\n            {-1, -6, 6},\n        }));\n\nnamespace {\n\n// allow same test case to be used over different types.\ntemplate <typename T>\nstruct GtestHelpersTypedTest : public ::testing::Test {\n  using type = T;\n\n  folly::coro::Task<std::string> type_str() const {\n    co_return std::string(folly::demangle(typeid(type).name()));\n  }\n};\n\nstruct Foo {};\n\n// also allow same test case to be used with different fixture classes.\ntemplate <>\nstruct GtestHelpersTypedTest<Foo> : public ::testing::Test {\n  using type = Foo;\n\n  folly::coro::Task<std::string> type_str() const { co_return \"Foo_x\"; }\n};\n\nusing CoroTypedTests = ::testing::Types<int, char, Foo>;\n\nTYPED_TEST_SUITE(GtestHelpersTypedTest, CoroTypedTests);\n\nCO_TYPED_TEST(GtestHelpersTypedTest, Test_type_str) {\n  using type = typename std::remove_reference_t<decltype(*this)>::type;\n\n  if constexpr (std::is_same_v<Foo, type>) {\n    EXPECT_EQ(co_await this->type_str(), \"Foo_x\");\n  } else if constexpr (std::is_same_v<int, type>) {\n    EXPECT_EQ(co_await this->type_str(), \"int\");\n  } else if constexpr (std::is_same_v<char, type>) {\n    EXPECT_EQ(co_await this->type_str(), \"char\");\n  }\n}\n\ntemplate <class T>\nclass GtestHelpersTypedTest2 : public GtestHelpersTypedTest<T> {};\n\nTYPED_TEST_SUITE_P(GtestHelpersTypedTest2);\n\nCO_TYPED_TEST_P(GtestHelpersTypedTest2, Test_type_str) {\n  using type = typename std::remove_reference_t<decltype(*this)>::type;\n\n  if constexpr (std::is_same_v<Foo, type>) {\n    EXPECT_EQ(co_await this->type_str(), \"Foo_x\");\n  } else if constexpr (std::is_same_v<int, type>) {\n    EXPECT_EQ(co_await this->type_str(), \"int\");\n  } else if constexpr (std::is_same_v<char, type>) {\n    EXPECT_EQ(co_await this->type_str(), \"char\");\n  }\n}\n\nREGISTER_TYPED_TEST_SUITE_P(GtestHelpersTypedTest2, Test_type_str);\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    GtestHelpersTypedTest2, GtestHelpersTypedTest2, CoroTypedTests);\n\nCO_TEST(GtestHelpersTest, testCoAssertNoThrow) {\n  CO_ASSERT_NO_THROW(co_await co_getInt(0));\n}\n\nCO_TEST(GtestHelpersTest, testCoAssertThrow) {\n  constexpr auto co_throwInvalidArgument = []() -> folly::coro::Task<> {\n    throw std::invalid_argument{\"\"};\n  };\n  CO_ASSERT_THROW(co_await co_throwInvalidArgument(), std::invalid_argument);\n  CO_ASSERT_ANY_THROW(co_await co_throwInvalidArgument());\n}\n\n} // namespace\n"
  },
  {
    "path": "folly/coro/test/InlineTaskTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/detail/InlineTask.h>\n#include <folly/portability/GTest.h>\n\n#include <tuple>\n\n#if FOLLY_HAS_COROUTINES\n\ntemplate <typename T>\nusing InlineTask = folly::coro::detail::InlineTask<T>;\n\nclass InlineTaskTest : public testing::Test {};\n\nTEST_F(InlineTaskTest, CallVoidTaskWithoutAwaitingNeverRuns) {\n  bool hasStarted = false;\n  auto f = [&]() -> InlineTask<void> {\n    hasStarted = true;\n    co_return;\n  };\n  {\n    auto task = f();\n    EXPECT_FALSE(hasStarted);\n  }\n  EXPECT_FALSE(hasStarted);\n}\n\nTEST_F(InlineTaskTest, CallValueTaskWithoutAwaitingNeverRuns) {\n  bool hasStarted = false;\n  auto f = [&]() -> InlineTask<int> {\n    hasStarted = true;\n    co_return 123;\n  };\n  {\n    auto task = f();\n    EXPECT_FALSE(hasStarted);\n  }\n  EXPECT_FALSE(hasStarted);\n}\n\nTEST_F(InlineTaskTest, CallRefTaskWithoutAwaitingNeverRuns) {\n  bool hasStarted = false;\n  int value;\n  auto f = [&]() -> InlineTask<int&> {\n    hasStarted = true;\n    co_return value;\n  };\n  {\n    auto task = f();\n    EXPECT_FALSE(hasStarted);\n  }\n  EXPECT_FALSE(hasStarted);\n}\n\nTEST_F(InlineTaskTest, SimpleVoidTask) {\n  bool hasRun = false;\n  auto f = [&]() -> InlineTask<void> {\n    hasRun = true;\n    co_return;\n  };\n  auto t = f();\n  EXPECT_FALSE(hasRun);\n  folly::coro::blockingWait(std::move(t));\n  EXPECT_TRUE(hasRun);\n}\n\nTEST_F(InlineTaskTest, SimpleValueTask) {\n  bool hasRun = false;\n  auto f = [&]() -> InlineTask<int> {\n    hasRun = true;\n    co_return 42;\n  };\n  auto t = f();\n  EXPECT_FALSE(hasRun);\n  EXPECT_EQ(42, folly::coro::blockingWait(std::move(t)));\n  EXPECT_TRUE(hasRun);\n}\n\nTEST_F(InlineTaskTest, SimpleRefTask) {\n  bool hasRun = false;\n  auto f = [&]() -> InlineTask<bool&> {\n    hasRun = true;\n    co_return hasRun;\n  };\n\n  auto t = f();\n  EXPECT_FALSE(hasRun);\n  auto& result = folly::coro::blockingWait(std::move(t));\n  EXPECT_TRUE(hasRun);\n  EXPECT_EQ(&hasRun, &result);\n}\n\nTEST_F(InlineTaskTest, ReturnValueWithInitializerListSyntax) {\n  struct TypeWithImplicitSingleValueConstructor {\n    float value_;\n    /* implicit */ TypeWithImplicitSingleValueConstructor(float x)\n        : value_(x) {}\n  };\n\n  auto f = []() -> InlineTask<TypeWithImplicitSingleValueConstructor> {\n    co_return {1.23f};\n  };\n\n  auto result = folly::coro::blockingWait(f());\n  EXPECT_EQ(1.23f, result.value_);\n}\n\nTEST_F(InlineTaskTest, ReturnValueWithInitializerListSyntax2) {\n  struct TypeWithImplicitMultiValueConstructor {\n    std::string s_;\n    float x_;\n    /* implicit */ TypeWithImplicitMultiValueConstructor(\n        std::string s, float x) noexcept\n        : s_(s), x_(x) {}\n  };\n\n  auto f = []() -> InlineTask<TypeWithImplicitMultiValueConstructor> {\n    co_return {\"hello\", 3.1415f};\n  };\n\n  auto result = folly::coro::blockingWait(f());\n  EXPECT_EQ(\"hello\", result.s_);\n  EXPECT_EQ(3.1415f, result.x_);\n}\n\nnamespace {\n\nstruct MoveOnlyType {\n  int value_;\n\n  explicit MoveOnlyType(int value) noexcept : value_(value) {}\n\n  MoveOnlyType(MoveOnlyType&& other) noexcept\n      : value_(std::exchange(other.value_, -1)) {}\n\n  [[maybe_unused]] MoveOnlyType& operator=(MoveOnlyType&& other) noexcept {\n    value_ = std::exchange(other.value_, -1);\n    return *this;\n  }\n\n  ~MoveOnlyType() { value_ = -2; }\n};\n\n} // namespace\n\nTEST_F(InlineTaskTest, TaskOfMoveOnlyType) {\n  auto f = []() -> InlineTask<MoveOnlyType> { co_return MoveOnlyType{42}; };\n\n  auto x = folly::coro::blockingWait(f());\n  EXPECT_EQ(42, x.value_);\n\n  bool executed = false;\n  auto g = [&]() -> InlineTask<void> {\n    auto result = co_await f();\n    EXPECT_EQ(42, result.value_);\n    executed = true;\n  };\n\n  folly::coro::blockingWait(g());\n\n  EXPECT_TRUE(executed);\n}\n\nTEST_F(InlineTaskTest, MoveOnlyTypeNRVO) {\n  auto f = []() -> InlineTask<MoveOnlyType> {\n    MoveOnlyType x{10};\n    co_return x;\n  };\n\n  auto x = folly::coro::blockingWait(f());\n  EXPECT_EQ(10, x.value_);\n}\n\nTEST_F(InlineTaskTest, ReturnLvalueReference) {\n  int value = 0;\n  auto f = [&]() -> InlineTask<int&> { co_return value; };\n\n  auto& x = folly::coro::blockingWait(f());\n  EXPECT_EQ(&value, &x);\n}\n\nTEST_F(InlineTaskTest, ExceptionsPropagateFromVoidTask) {\n  struct MyException : std::exception {};\n\n  auto f = []() -> InlineTask<void> {\n    co_await folly::coro::suspend_never{};\n    throw MyException{};\n  };\n  EXPECT_THROW(folly::coro::blockingWait(f()), MyException);\n}\n\nTEST_F(InlineTaskTest, ExceptionsPropagateFromValueTask) {\n  struct MyException : std::exception {};\n\n  auto f = []() -> InlineTask<int> {\n    co_await folly::coro::suspend_never{};\n    throw MyException{};\n  };\n  EXPECT_THROW(folly::coro::blockingWait(f()), MyException);\n}\n\nTEST_F(InlineTaskTest, ExceptionsPropagateFromRefTask) {\n  struct MyException : std::exception {};\n\n  auto f = []() -> InlineTask<int&> {\n    co_await folly::coro::suspend_never{};\n    throw MyException{};\n  };\n  EXPECT_THROW(folly::coro::blockingWait(f()), MyException);\n}\n\nTEST_F(InlineTaskTest, ExceptionsPropagateFromReturnValueConstructor) {\n  struct MyException : std::exception {};\n\n  struct ThrowingCopyConstructor {\n    [[maybe_unused]] ThrowingCopyConstructor() noexcept = default;\n\n    [[noreturn]] ThrowingCopyConstructor(\n        const ThrowingCopyConstructor&) noexcept(false) {\n      throw MyException{};\n    }\n\n    ThrowingCopyConstructor& operator=(const ThrowingCopyConstructor&) = delete;\n  };\n\n  auto f = []() -> InlineTask<ThrowingCopyConstructor> { co_return {}; };\n  EXPECT_THROW(folly::coro::blockingWait(f()), MyException);\n}\n\nTEST_F(InlineTaskTest, DeepRecursionDoesntStackOverflow) {\n  struct RecursiveTask {\n    InlineTask<void> operator()(int depth) {\n      if (depth > 0) {\n        co_await operator()(depth - 1);\n      }\n    }\n  };\n\n  folly::coro::blockingWait(RecursiveTask{}(500000));\n}\n\nTEST_F(InlineTaskTest, DeepRecursionOfValueTaskDoesntStackOverflow) {\n  struct RecursiveValueTask {\n    InlineTask<int> operator()(int depth) {\n      if (depth > 0) {\n        co_return co_await operator()(depth - 1) + 1;\n      }\n      co_return 0;\n    }\n  };\n\n  EXPECT_EQ(500000, folly::coro::blockingWait(RecursiveValueTask{}(500000)));\n}\n\nTEST_F(InlineTaskTest, DeepRecursionOfExceptions) {\n  struct MyException : std::exception {};\n\n  struct RecursiveThrowingTask {\n    static InlineTask<void> go(int depth) {\n      if (depth > 0) {\n        co_await go(depth - 1);\n      }\n\n      throw MyException{};\n    }\n  };\n\n  EXPECT_THROW(\n      folly::coro::blockingWait(RecursiveThrowingTask::go(50000)), MyException);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/MergeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/CancellationToken.h>\n#include <folly/ScopeGuard.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Merge.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\nusing namespace std::chrono_literals;\n\nclass MergeTest : public testing::Test {};\n\nTEST_F(MergeTest, SimpleMerge) {\n  blockingWait([]() -> Task<void> {\n    auto generator = merge(\n        co_await co_current_executor,\n        []() -> AsyncGenerator<AsyncGenerator<int>> {\n          auto makeGenerator = [](int start, int count) -> AsyncGenerator<int> {\n            for (int i = start; i < start + count; ++i) {\n              co_yield i;\n              co_await co_reschedule_on_current_executor;\n            }\n          };\n\n          co_yield makeGenerator(0, 3);\n          co_yield makeGenerator(3, 2);\n        }());\n\n    const std::array<int, 5> expectedValues = {{0, 3, 1, 4, 2}};\n\n    auto item = co_await generator.next();\n    for (int expectedValue : expectedValues) {\n      CHECK(!!item);\n      CHECK_EQ(expectedValue, *item);\n      item = co_await generator.next();\n    }\n    CHECK(!item);\n  }());\n}\n\nTEST_F(MergeTest, TruncateStream) {\n  blockingWait([]() -> Task<void> {\n    int started = 0;\n    int completed = 0;\n    {\n      auto generator = merge(\n          co_await co_current_executor,\n          co_invoke([&]() -> AsyncGenerator<AsyncGenerator<int>> {\n            auto makeGenerator = [&]() -> AsyncGenerator<int> {\n              ++started;\n              SCOPE_EXIT {\n                ++completed;\n              };\n              co_yield 1;\n              co_await co_reschedule_on_current_executor;\n              co_yield 2;\n            };\n\n            co_yield co_invoke(makeGenerator);\n            co_yield co_invoke(makeGenerator);\n            co_yield co_invoke(makeGenerator);\n          }));\n\n      auto item = co_await generator.next();\n      CHECK_EQ(1, *item);\n      item = co_await generator.next();\n      CHECK_EQ(1, *item);\n      CHECK_EQ(3, started);\n      // Truncate the stream after consuming only 2 of the 6 values it\n      // would have produced.\n    }\n\n    // Spin the executor until the generators finish responding to cancellation.\n    for (int i = 0; completed != started && i < 10; ++i) {\n      co_await co_reschedule_on_current_executor;\n    }\n\n    CHECK_EQ(3, completed);\n  }());\n}\n\nTEST_F(MergeTest, TruncateStreamMultiThreaded) {\n  blockingWait([]() -> Task<void> {\n    std::atomic<int> completed = 0;\n    folly::Baton allCompleted;\n    {\n      auto generator = merge(\n          folly::getGlobalCPUExecutor(),\n          co_invoke([&]() -> AsyncGenerator<AsyncGenerator<int>> {\n            auto makeGenerator = [&]() -> AsyncGenerator<int> {\n              SCOPE_EXIT {\n                if (++completed == 3) {\n                  allCompleted.post();\n                }\n              };\n              co_yield 1;\n              co_yield 2;\n            };\n\n            co_yield co_invoke(makeGenerator);\n            co_yield co_invoke(makeGenerator);\n            co_yield co_invoke(makeGenerator);\n          }));\n\n      auto item = co_await generator.next();\n      CHECK_EQ(1, *item);\n      co_await generator.next();\n      // Truncate the stream after consuming only 2 of the 6 values it\n      // would have produced.\n    }\n\n    CHECK(allCompleted.try_wait_for(1s));\n  }());\n}\n\nTEST_F(MergeTest, SequencesOfRValueReferences) {\n  blockingWait([]() -> Task<void> {\n    auto makeStreamOfStreams =\n        []() -> AsyncGenerator<AsyncGenerator<std::vector<int>&&>> {\n      auto makeStreamOfVectors = []() -> AsyncGenerator<std::vector<int>&&> {\n        co_yield std::vector{1, 2, 3};\n        co_await co_reschedule_on_current_executor;\n        co_yield std::vector{2, 4, 6};\n      };\n\n      co_yield makeStreamOfVectors();\n      co_yield makeStreamOfVectors();\n    };\n\n    auto gen = merge(co_await co_current_executor, makeStreamOfStreams());\n    int resultCount = 0;\n    while (auto item = co_await gen.next()) {\n      ++resultCount;\n      std::vector<int>&& v = *item;\n      CHECK_EQ(3, v.size());\n    }\n    CHECK_EQ(4, resultCount);\n  }());\n}\n\nTEST_F(MergeTest, SequencesOfLValueReferences) {\n  blockingWait([]() -> Task<void> {\n    auto makeStreamOfStreams =\n        []() -> AsyncGenerator<AsyncGenerator<std::vector<int>&>> {\n      auto makeStreamOfVectors = []() -> AsyncGenerator<std::vector<int>&> {\n        std::vector<int> v{1, 2, 3};\n        co_yield v;\n        CHECK_EQ(4, v.size());\n        co_await co_reschedule_on_current_executor;\n        v.push_back(v.back());\n        co_yield v;\n      };\n\n      co_yield makeStreamOfVectors();\n      co_yield makeStreamOfVectors();\n    };\n\n    auto gen = merge(co_await co_current_executor, makeStreamOfStreams());\n    int resultCount = 0;\n    while (auto item = co_await gen.next()) {\n      ++resultCount;\n      std::vector<int>& v = *item;\n      if (v.size() == 3) {\n        CHECK_EQ(1, v[0]);\n        CHECK_EQ(2, v[1]);\n        CHECK_EQ(3, v[2]);\n        v.push_back(7);\n      } else {\n        CHECK_EQ(5, v.size());\n        CHECK_EQ(1, v[0]);\n        CHECK_EQ(2, v[1]);\n        CHECK_EQ(3, v[2]);\n        CHECK_EQ(7, v[3]);\n        CHECK_EQ(7, v[4]);\n      }\n    }\n    CHECK_EQ(4, resultCount);\n  }());\n}\n\ntemplate <typename Ref, typename Value = folly::remove_cvref_t<Ref>>\nfolly::coro::AsyncGenerator<Ref, Value> neverStream() {\n  folly::coro::Baton baton;\n  folly::CancellationCallback cb{\n      co_await folly::coro::co_current_cancellation_token,\n      [&] { baton.post(); }};\n  co_await baton;\n}\n\nTEST_F(MergeTest, CancellationTokenPropagatesToOuterFromConsumer) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    bool suspended = false;\n    bool done = false;\n    co_await folly::coro::collectAll(\n        folly::coro::co_withCancellation(\n            cancelSource.getToken(),\n            [&]() -> folly::coro::Task<void> {\n              auto stream = merge(\n                  co_await co_current_executor,\n                  neverStream<AsyncGenerator<int>>());\n              suspended = true;\n              auto result = co_await stream.next();\n              CHECK(!result.has_value());\n              done = true;\n            }()),\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          CHECK(suspended);\n          CHECK(!done);\n          cancelSource.requestCancellation();\n        }());\n    CHECK(done);\n  }());\n}\n\nTEST_F(MergeTest, CancellationTokenPropagatesToInnerFromConsumer) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    bool suspended = false;\n    bool done = false;\n    auto makeStreamOfStreams = []() -> AsyncGenerator<AsyncGenerator<int>> {\n      co_yield neverStream<int>();\n      co_yield neverStream<int>();\n    };\n\n    co_await folly::coro::collectAll(\n        folly::coro::co_withCancellation(\n            cancelSource.getToken(),\n            [&]() -> folly::coro::Task<void> {\n              auto stream =\n                  merge(co_await co_current_executor, makeStreamOfStreams());\n              suspended = true;\n              auto result = co_await stream.next();\n              CHECK(!result.has_value());\n              done = true;\n            }()),\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          CHECK(suspended);\n          CHECK(!done);\n          cancelSource.requestCancellation();\n        }());\n    CHECK(done);\n  }());\n}\n\n// Check that by the time merged generator's next() returns an empty value\n// (end of stream) or throws an exception all source generators are destroyed.\nTEST_F(MergeTest, SourcesAreDestroyedBeforeEof) {\n  std::atomic<int> runningSourceGenerators = 0;\n  std::atomic<int> runningListGenerators = 0;\n\n  auto sourceGenerator =\n      [&](bool shouldThrow) -> folly::coro::AsyncGenerator<int> {\n    ++runningSourceGenerators;\n    SCOPE_EXIT {\n      --runningSourceGenerators;\n    };\n    co_await folly::coro::co_reschedule_on_current_executor;\n    co_yield 42;\n    co_await folly::coro::co_reschedule_on_current_executor;\n    if (shouldThrow) {\n      throw std::runtime_error(\"test exception\");\n    }\n  };\n\n  auto listGenerator = [&](bool shouldThrow)\n      -> folly::coro::AsyncGenerator<folly::coro::AsyncGenerator<int>> {\n    CHECK(runningListGenerators == 0);\n    ++runningListGenerators;\n    SCOPE_EXIT {\n      /* sleep override */\n      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n      --runningListGenerators;\n    };\n    for (int i = 0;; ++i) {\n      co_await folly::coro::co_reschedule_on_current_executor;\n      co_yield sourceGenerator(shouldThrow && (i % 2 == 1));\n    }\n  };\n\n  folly::CPUThreadPoolExecutor exec(4);\n\n  // Stream interrupted by cancellation.\n  auto future =\n      co_withExecutor(\n          &exec, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n            auto gen = folly::coro::merge(\n                &exec, listGenerator(/* shouldThrow */ false));\n            folly::CancellationSource cancelSource;\n            auto r = co_await folly::coro::co_withCancellation(\n                cancelSource.getToken(), gen.next());\n            CHECK(r.has_value());\n            CHECK_EQ(*r, 42);\n            CHECK_GT(\n                runningSourceGenerators.load() + runningListGenerators.load(),\n                0);\n            cancelSource.requestCancellation();\n            // Currently the merged generator discards items produced\n            // after cancellation. But this behavior is not important, and\n            // it would probably be equally fine to return them (but stop\n            // calling source generators for more), so this test accepts\n            // either behavior.\n            while (true) {\n              r = co_await folly::coro::co_withCancellation(\n                  cancelSource.getToken(), gen.next());\n              if (!r.has_value()) {\n                break;\n              }\n              CHECK_EQ(*r, 42);\n            }\n            CHECK_EQ(runningSourceGenerators.load(), 0);\n            CHECK_EQ(runningListGenerators.load(), 0);\n          }))\n          .start();\n  std::move(future).get();\n\n  // Stream interrupted by exception.\n  future =\n      co_withExecutor(\n          &exec, folly::coro::co_invoke([&]() -> folly::coro::Task<void> {\n            auto gen = folly::coro::merge(\n                &exec, listGenerator(/* shouldThrow */ true));\n            auto r = co_await gen.next();\n            CHECK(r.has_value());\n            CHECK_EQ(*r, 42);\n            CHECK_GT(\n                runningSourceGenerators.load() + runningListGenerators.load(),\n                0);\n            while (true) {\n              auto r2 = co_await folly::coro::co_awaitTry(gen.next());\n              if (!r2.hasValue()) {\n                CHECK(\n                    r2.exception().what().find(\"test exception\") !=\n                    std::string::npos);\n                break;\n              }\n              CHECK(r2->has_value());\n              CHECK_EQ(r2->value(), 42);\n            }\n            CHECK_EQ(runningSourceGenerators.load(), 0);\n            CHECK_EQ(runningListGenerators.load(), 0);\n          }))\n          .start();\n  std::move(future).get();\n}\n\nTEST_F(MergeTest, DontLeakRequestContext) {\n  class TestData : public folly::RequestData {\n   public:\n    explicit TestData() noexcept {}\n    bool hasCallback() override { return false; }\n\n    static void set() {\n      folly::RequestContext::get()->setContextData(\n          \"test\", std::make_unique<TestData>());\n    }\n    static auto get() {\n      return folly::RequestContext::get()->getContextData(\"test\");\n    }\n  };\n  blockingWait([]() -> Task<void> {\n    folly::RequestContextScopeGuard requestScope;\n\n    TestData::set();\n    auto initialContextData = TestData::get();\n    CHECK(initialContextData != nullptr);\n\n    auto generator = merge(\n        co_await co_current_executor,\n        co_invoke([&]() -> AsyncGenerator<AsyncGenerator<int>> {\n          auto makeGenerator = [&]() -> AsyncGenerator<int> {\n            for (int i = 0; i < 10; ++i) {\n              CHECK(TestData::get() == initialContextData);\n              folly::RequestContextScopeGuard childScope;\n              CHECK(TestData::get() == nullptr);\n              co_await co_reschedule_on_current_executor;\n              CHECK(TestData::get() == nullptr);\n              TestData::set();\n              auto newContextData = TestData::get();\n              CHECK(newContextData != nullptr);\n              CHECK(newContextData != initialContextData);\n              co_await co_reschedule_on_current_executor;\n              CHECK(TestData::get() == newContextData);\n            }\n          };\n          for (int i = 0; i < 5; ++i) {\n            co_yield makeGenerator();\n          }\n        }));\n\n    while (auto val = co_await generator.next()) {\n    }\n\n    CHECK(TestData::get() == initialContextData);\n  }());\n}\n\nTEST_F(MergeTest, SimpleMergeWithRValueGeneratorType) {\n  blockingWait([]() -> Task<void> {\n    auto generator = merge(\n        co_await co_current_executor,\n        []() -> AsyncGenerator<AsyncGenerator<int>&&> {\n          auto makeGenerator = [](int start, int count) -> AsyncGenerator<int> {\n            for (int i = start; i < start + count; ++i) {\n              co_yield i;\n              co_await co_reschedule_on_current_executor;\n            }\n          };\n\n          co_yield makeGenerator(0, 3);\n          co_yield makeGenerator(3, 2);\n        }());\n\n    const std::array<int, 5> expectedValues = {{0, 3, 1, 4, 2}};\n\n    auto item = co_await generator.next();\n    for (int expectedValue : expectedValues) {\n      CHECK(!!item);\n      CHECK_EQ(expectedValue, *item);\n      item = co_await generator.next();\n    }\n    CHECK(!item);\n  }());\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/MutexTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/detail/InlineTask.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\n#include <mutex>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly;\n\nclass MutexTest : public testing::Test {};\n\nTEST_F(MutexTest, TryLock) {\n  coro::Mutex m;\n  CHECK(m.try_lock());\n  CHECK(!m.try_lock());\n  m.unlock();\n  CHECK(m.try_lock());\n}\n\nTEST_F(MutexTest, ScopedLock) {\n  coro::Mutex m;\n  {\n    std::unique_lock lock{m, std::try_to_lock};\n    CHECK(lock.owns_lock());\n\n    {\n      std::unique_lock lock2{m, std::try_to_lock};\n      CHECK(!lock2.owns_lock());\n    }\n  }\n\n  CHECK(m.try_lock());\n  m.unlock();\n}\n\nTEST_F(MutexTest, LockAsync) {\n  coro::Mutex m;\n  coro::Baton b1;\n  coro::Baton b2;\n\n  int value = 0;\n\n  auto makeTask = [&](coro::Baton& b) -> coro::Task<void> {\n    co_await m.co_lock();\n    ++value;\n    co_await b;\n    ++value;\n    m.unlock();\n  };\n\n  ManualExecutor executor;\n\n  auto f1 = co_withExecutor(&executor, makeTask(b1)).start();\n  executor.drain();\n  CHECK_EQ(1, value);\n  CHECK(!m.try_lock());\n\n  auto f2 = co_withExecutor(&executor, makeTask(b2)).start();\n  executor.drain();\n  CHECK_EQ(1, value);\n\n  // This will resume f1 coroutine and let it release the\n  // lock. This will in turn resume f2 which was suspended\n  // at co_await m.lockAsync() which will then increment the value\n  // before becoming blocked on\n  b1.post();\n  executor.drain();\n\n  CHECK_EQ(3, value);\n  CHECK(!m.try_lock());\n\n  b2.post();\n  executor.drain();\n  CHECK_EQ(4, value);\n  CHECK(m.try_lock());\n}\n\nTEST_F(MutexTest, ScopedLockAsync) {\n  coro::Mutex m;\n  coro::Baton b1;\n  coro::Baton b2;\n\n  int value = 0;\n\n  auto makeTask = [&](coro::Baton& b) -> coro::Task<void> {\n    auto lock = co_await m.co_scoped_lock();\n    ++value;\n    co_await b;\n    ++value;\n  };\n\n  ManualExecutor executor;\n\n  auto f1 = co_withExecutor(&executor, makeTask(b1)).start();\n  executor.drain();\n  CHECK_EQ(1, value);\n  CHECK(!m.try_lock());\n\n  auto f2 = co_withExecutor(&executor, makeTask(b2)).start();\n  executor.drain();\n  CHECK_EQ(1, value);\n\n  // This will resume f1 coroutine and let it release the\n  // lock. This will in turn resume f2 which was suspended\n  // at co_await m.lockAsync() which will then increment the value\n  // before becoming blocked on b2.\n  b1.post();\n  executor.drain();\n\n  CHECK_EQ(3, value);\n  CHECK(!m.try_lock());\n\n  b2.post();\n  executor.drain();\n  CHECK_EQ(4, value);\n  CHECK(m.try_lock());\n}\n\nTEST_F(MutexTest, ThreadSafety) {\n  CPUThreadPoolExecutor threadPool{\n      2, std::make_shared<NamedThreadFactory>(\"CPUThreadPool\")};\n\n  int value = 0;\n  coro::Mutex mutex;\n\n  auto makeTask = [&]() -> coro::Task<void> {\n    for (int i = 0; i < 10'000; ++i) {\n      auto lock = co_await mutex.co_scoped_lock();\n      ++value;\n    }\n  };\n\n  auto f1 = co_withExecutor(&threadPool, makeTask()).start();\n  auto f2 = co_withExecutor(&threadPool, makeTask()).start();\n  auto f3 = co_withExecutor(&threadPool, makeTask()).start();\n\n  std::move(f1).get();\n  std::move(f2).get();\n  std::move(f3).get();\n\n  CHECK_EQ(30'000, value);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/PromiseBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/Portability.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/FutureUtil.h>\n#include <folly/coro/Promise.h>\n#include <folly/coro/Task.h>\n#include <folly/futures/Future.h>\n\n#if FOLLY_HAS_COROUTINES\n\nvoid resetMallocStats() {\n  BENCHMARK_SUSPEND {\n    static uint64_t epoch = 0;\n    ++epoch;\n    size_t sz = sizeof(epoch);\n    mallctl(\"epoch\", &epoch, &sz, &epoch, sz);\n  };\n}\nvoid setMallocStats(folly::UserCounters& counters) {\n  BENCHMARK_SUSPEND {\n    size_t allocated = 0;\n    size_t sz = sizeof(allocated);\n    mallctl(\"stats.allocated\", &allocated, &sz, nullptr, 0);\n    counters[\"allocated\"] = allocated;\n  };\n}\n\nBENCHMARK_COUNTERS(CoroFutureImmediateUnwrapped, counters, iters) {\n  resetMallocStats();\n\n  for (std::size_t i = 0; i < iters; ++i) {\n    auto [promise, future] = folly::coro::makePromiseContract<int>();\n    promise.setValue(42);\n    folly::coro::blockingWait(std::move(future));\n  }\n\n  setMallocStats(counters);\n}\n\n// You can't directly blockingWait a SemiFuture (it deadlocks) so there's no\n// comparison for this one\n\nBENCHMARK_COUNTERS(CoroFutureImmediate, counters, iters) {\n  resetMallocStats();\n\n  for (std::size_t i = 0; i < iters; ++i) {\n    auto [promise, future] = folly::coro::makePromiseContract<int>();\n    promise.setValue(42);\n    folly::coro::blockingWait(folly::coro::toTask(std::move(future)));\n  }\n\n  setMallocStats(counters);\n}\n\nBENCHMARK_COUNTERS(FuturesFutureImmediate, counters, iters) {\n  resetMallocStats();\n\n  for (std::size_t i = 0; i < iters; ++i) {\n    auto [promise, future] = folly::makePromiseContract<int>();\n    promise.setValue(42);\n    folly::coro::blockingWait(folly::coro::toTask(std::move(future)));\n  }\n\n  setMallocStats(counters);\n}\n\nBENCHMARK_COUNTERS(CoroFutureSuspend, counters, iters) {\n  resetMallocStats();\n\n  for (std::size_t i = 0; i < iters; ++i) {\n    auto [this_promise, this_future] = folly::coro::makePromiseContract<int>();\n    auto waiter = [](auto future) -> folly::coro::Task<int> {\n      co_return co_await std::move(future);\n    }(std::move(this_future));\n    auto fulfiller = [](auto promise) -> folly::coro::Task<> {\n      promise.setValue(42);\n      co_return;\n    }(std::move(this_promise));\n\n    folly::coro::blockingWait(\n        folly::coro::collectAll(\n            co_awaitTry(std::move(waiter)), std::move(fulfiller)));\n  }\n\n  setMallocStats(counters);\n}\n\nBENCHMARK_COUNTERS(FuturesFutureSuspend, counters, iters) {\n  resetMallocStats();\n\n  for (std::size_t i = 0; i < iters; ++i) {\n    auto [this_promise, this_future] = folly::makePromiseContract<int>();\n    auto waiter = [](auto future) -> folly::coro::Task<int> {\n      co_return co_await std::move(future);\n    }(std::move(this_future));\n    auto fulfiller = [](auto promise) -> folly::coro::Task<> {\n      promise.setValue(42);\n      co_return;\n    }(std::move(this_promise));\n\n    folly::coro::blockingWait(\n        folly::coro::collectAll(\n            co_awaitTry(std::move(waiter)), std::move(fulfiller)));\n  }\n\n  setMallocStats(counters);\n}\n\n#endif\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/PromiseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Promise.h>\n\n#include <tuple>\n\n#include <folly/Portability.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly;\nusing namespace ::testing;\n\nstatic_assert(\n    std::is_move_assignable<folly::coro::Promise<void>>::value,\n    \"promise should be move assignable\");\nstatic_assert(\n    std::is_move_assignable<folly::coro::Future<void>>::value,\n    \"future should be move assignable\");\n\nCO_TEST(PromiseTest, ImmediateValue) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.valid());\n  EXPECT_FALSE(promise.isFulfilled());\n  EXPECT_TRUE(future.valid());\n  EXPECT_TRUE(static_cast<bool>(future));\n  EXPECT_TRUE(promise.trySetValue(42));\n  EXPECT_TRUE(promise.valid());\n  EXPECT_TRUE(promise.isFulfilled());\n  EXPECT_EQ(co_await std::move(future), 42);\n}\n\nCO_TEST(PromiseTest, CollectImmediateValue) {\n  {\n    auto [promise, future] = coro::makePromiseContract<int>();\n    EXPECT_TRUE(promise.trySetWith([]() { return 42; }));\n    auto [res] = co_await collectAll(std::move(future));\n    EXPECT_EQ(res, 42);\n  }\n  {\n    auto [promise, future] = coro::makePromiseContract<int>();\n    EXPECT_TRUE(promise.trySetWith([]() { return 42; }));\n    auto [res] = co_await collectAll(co_awaitTry(std::move(future)));\n    EXPECT_EQ(res.value(), 42);\n  }\n}\n\nCO_TEST(PromiseTest, ImmediateWithValue) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetWith([]() { return 42; }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_EQ(res.value(), 42);\n}\n\nCO_TEST(PromiseTest, ImmediateWithValueThrows) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetWith([]() -> int {\n    throw std::runtime_error(\"\");\n  }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nCO_TEST(PromiseTest, ImmediateWithValueImplicit) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetWith([]() { return '*'; }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_EQ(res.value(), 42);\n}\n\nCO_TEST(PromiseTest, ImmediateValueMultiple) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetValue(42));\n  EXPECT_FALSE(promise.trySetValue(43));\n  EXPECT_FALSE(promise.trySetValue(44));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_EQ(res.value(), 42);\n}\n\nCO_TEST(PromiseTest, ImmediateTry) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetResult(Try(42)));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_EQ(res.value(), 42);\n}\n\nCO_TEST(PromiseTest, ImmediateWithTry) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetWith([]() { return Try(42); }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_EQ(res.value(), 42);\n}\n\nCO_TEST(PromiseTest, ImmediateWithTryThrows) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetWith([]() -> Try<int> {\n    throw std::runtime_error(\"\");\n  }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nCO_TEST(PromiseTest, ImmediateException) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetException(std::runtime_error(\"\")));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nCO_TEST(PromiseTest, ImmediateWithException) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetExceptionWith([]() {\n    return exception_wrapper(std::runtime_error(\"\"));\n  }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nCO_TEST(PromiseTest, ImmediateWithExceptionImplicit) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetExceptionWith([]() {\n    return std::runtime_error(\"\");\n  }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nCO_TEST(PromiseTest, ImmediateWithExceptionThrows) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetExceptionWith([]() -> exception_wrapper {\n    throw std::runtime_error(\"\");\n  }));\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nCO_TEST(PromiseTest, ImmediateExceptionVoid) {\n  auto [promise, future] = coro::makePromiseContract<void>();\n  EXPECT_TRUE(promise.trySetException(std::runtime_error(\"\")));\n  EXPECT_THROW(co_await std::move(future), std::runtime_error);\n}\n\nCO_TEST(PromiseTest, ImmediateWithExceptionVoid) {\n  auto [promise, future] = coro::makePromiseContract<void>();\n  EXPECT_TRUE(promise.trySetExceptionWith([]() {\n    return std::runtime_error(\"\");\n  }));\n  EXPECT_THROW(co_await std::move(future), std::runtime_error);\n}\n\nCO_TEST(PromiseTest, SuspendValue) {\n  auto [this_promise, this_future] = coro::makePromiseContract<int>();\n  auto waiter = [](auto future) -> coro::Task<int> {\n    co_return co_await std::move(future);\n  }(std::move(this_future));\n  auto fulfiller = [](auto promise) -> coro::Task<> {\n    EXPECT_TRUE(promise.trySetValue(42));\n    co_return;\n  }(std::move(this_promise));\n\n  auto [res, _] = co_await coro::collectAll(\n      co_awaitTry(std::move(waiter)), std::move(fulfiller));\n\n  EXPECT_EQ(res.value(), 42);\n}\n\nCO_TEST(PromiseTest, SuspendException) {\n  auto [this_promise, this_future] = coro::makePromiseContract<int>();\n  auto waiter = [](auto future) -> coro::Task<int> {\n    co_return co_await std::move(future);\n  }(std::move(this_future));\n  auto fulfiller = [](auto promise) -> coro::Task<> {\n    EXPECT_TRUE(promise.trySetException(std::logic_error(\"\")));\n    co_return;\n  }(std::move(this_promise));\n\n  auto [res, _] = co_await coro::collectAll(\n      co_awaitTry(std::move(waiter)), std::move(fulfiller));\n\n  EXPECT_TRUE(res.hasException<std::logic_error>());\n}\n\nCO_TEST(PromiseTest, ImmediateCancel) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  CancellationSource cs;\n  cs.requestCancellation();\n  bool cancelled = false;\n  CancellationCallback cb{\n      promise.getCancellationToken(), [&] { cancelled = true; }};\n  EXPECT_FALSE(cancelled);\n  auto res = co_await co_awaitTry(\n      co_withCancellation(cs.getToken(), std::move(future)));\n  EXPECT_TRUE(cancelled);\n  EXPECT_TRUE(res.hasException<OperationCancelled>());\n  EXPECT_FALSE(promise.trySetValue(42));\n}\n\nCO_TEST(PromiseTest, CancelFulfilled) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_TRUE(promise.trySetValue(42));\n  CancellationSource cs;\n  cs.requestCancellation();\n  bool cancelled = false;\n  CancellationCallback cb{\n      promise.getCancellationToken(), [&] { cancelled = true; }};\n  auto res = co_await co_awaitTry(\n      co_withCancellation(cs.getToken(), std::move(future)));\n  EXPECT_FALSE(cancelled); // not signalled if already fulfilled\n  EXPECT_EQ(res.value(), 42);\n}\n\nCO_TEST(PromiseTest, SuspendCancel) {\n  auto [promise, this_future] = coro::makePromiseContract<int>();\n  CancellationSource cs;\n  bool cancelled = false;\n  CancellationCallback cb{\n      promise.getCancellationToken(), [&] { cancelled = true; }};\n  auto waiter = [](auto future) -> coro::Task<int> {\n    co_return co_await std::move(future);\n  }(co_withCancellation(cs.getToken(), std::move(this_future)));\n  auto fulfiller = [](auto cs) -> coro::Task<> {\n    cs.requestCancellation();\n    co_return;\n  }(cs);\n\n  auto [res, _] = co_await coro::collectAll(\n      co_awaitTry(std::move(waiter)), std::move(fulfiller));\n\n  EXPECT_TRUE(cancelled);\n  EXPECT_TRUE(res.hasException<OperationCancelled>());\n}\n\nCO_TEST(PromiseTest, ImmediateBreakPromise) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  {\n    auto p2 = std::move(promise);\n  }\n  // @lint-ignore CLANGTIDY\n  EXPECT_FALSE(promise.valid());\n  auto res = co_await co_awaitTry(std::move(future));\n  EXPECT_TRUE(res.hasException<BrokenPromise>());\n}\n\nCO_TEST(PromiseTest, SuspendBreakPromise) {\n  auto [this_promise, this_future] = coro::makePromiseContract<int>();\n  auto waiter = [](auto future) -> coro::Task<int> {\n    co_return co_await std::move(future);\n  }(std::move(this_future));\n  auto fulfiller = [](auto promise) -> coro::Task<> {\n    (void)promise;\n    co_return;\n  }(std::move(this_promise));\n\n  auto [res, _] = co_await coro::collectAll(\n      co_awaitTry(std::move(waiter)), std::move(fulfiller));\n\n  EXPECT_TRUE(res.hasException<BrokenPromise>());\n}\n\nCO_TEST(PromiseTest, Lifetime) {\n  struct Guard {\n    int& destroyed;\n    explicit Guard(int& d) : destroyed(d) {}\n    Guard(Guard&&) = default;\n    ~Guard() { destroyed++; }\n  };\n\n  int destroyed = 0;\n  {\n    auto [promise, future] = coro::makePromiseContract<Guard>();\n    EXPECT_TRUE(promise.trySetValue(Guard(destroyed)));\n    EXPECT_EQ(destroyed, 1); // the temporary\n    co_await std::move(future);\n    EXPECT_EQ(destroyed, 2); // the return value\n  }\n  EXPECT_EQ(destroyed, 3); // the slot in shared state\n}\n\nTEST(PromiseTest, DropFuture) {\n  struct Guard {\n    int& destroyed;\n    explicit Guard(int& d) : destroyed(d) {}\n    Guard(Guard&&) = default;\n    ~Guard() { destroyed++; }\n  };\n\n  int destroyed = 0;\n  {\n    auto [promise, future] = coro::makePromiseContract<Guard>();\n    EXPECT_TRUE(promise.trySetValue(Guard(destroyed)));\n    EXPECT_EQ(destroyed, 1); // the temporary\n  }\n  EXPECT_EQ(destroyed, 2); // the slot in shared state\n}\n\nCO_TEST(PromiseTest, MoveOnly) {\n  auto [promise, future] = coro::makePromiseContract<std::unique_ptr<int>>();\n  EXPECT_TRUE(promise.trySetValue(std::make_unique<int>(42)));\n  auto val = co_await std::move(future);\n  EXPECT_EQ(*val, 42);\n}\n\nCO_TEST(PromiseTest, Void) {\n  auto [promise, future] = coro::makePromiseContract<void>();\n  EXPECT_TRUE(promise.trySetValue());\n  co_await std::move(future);\n}\n\nTEST(PromiseTest, IsReady) {\n  auto [promise, future] = coro::makePromiseContract<int>();\n  EXPECT_FALSE(future.isReady());\n  EXPECT_TRUE(promise.trySetValue(42));\n  EXPECT_TRUE(future.isReady());\n}\n\nCO_TEST(PromiseTest, MakeFuture) {\n  auto future = coro::makeFuture(42);\n  EXPECT_TRUE(future.valid());\n  EXPECT_TRUE(static_cast<bool>(future));\n  EXPECT_TRUE(future.isReady());\n  auto val = co_await std::move(future);\n  EXPECT_EQ(val, 42);\n\n  auto future2 = coro::makeFuture<int>(std::runtime_error(\"\"));\n  EXPECT_TRUE(future2.valid());\n  EXPECT_TRUE(static_cast<bool>(future2));\n  EXPECT_TRUE(future2.isReady());\n  auto res = co_await co_awaitTry(std::move(future2));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n\n  auto future3 = coro::makeFuture();\n  EXPECT_TRUE(future3.valid());\n  EXPECT_TRUE(static_cast<bool>(future3));\n  EXPECT_TRUE(future3.isReady());\n  auto res3 = co_await co_awaitTry(std::move(future3));\n  EXPECT_TRUE(res3.hasValue());\n}\n\nCO_TEST(PromiseTest, MoveAssign) {\n  coro::Promise<void> promise;\n  coro::Future<void> future;\n  // Test default-constructed future is invalid\n  EXPECT_FALSE(promise.valid());\n  EXPECT_FALSE(future.valid());\n  EXPECT_FALSE(static_cast<bool>(future));\n\n  std::tie(promise, future) = coro::makePromiseContract<void>();\n  EXPECT_TRUE(promise.valid());\n  EXPECT_TRUE(future.valid());\n  EXPECT_TRUE(static_cast<bool>(future));\n\n  // Test move invalidates source\n  auto movedFuture = std::move(future);\n  EXPECT_FALSE(future.valid());\n  EXPECT_FALSE(static_cast<bool>(future));\n  EXPECT_TRUE(movedFuture.valid());\n  EXPECT_TRUE(static_cast<bool>(movedFuture));\n\n  EXPECT_TRUE(promise.trySetValue());\n  co_await std::move(movedFuture);\n\n  // After awaiting and moving, the future should be invalid\n  EXPECT_FALSE(movedFuture.valid());\n  EXPECT_FALSE(static_cast<bool>(movedFuture));\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/RequestContextTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/UnboundedQueue.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\n// Test RequestContext propagation behavior in various scenarios involving\n// coroutines.\n\nstatic const folly::RequestToken token{\"RequestContextTest\"};\n\nclass TagData : public folly::RequestData {\n public:\n  int tag = 0;\n\n  bool hasCallback() override { return false; }\n\n  TagData() = default;\n  explicit TagData(int t) : tag(t) {}\n};\n\n// -1 if there's no current RequestContext or no TagData on it.\nstatic int getTag() {\n  folly::RequestContext* rc = folly::RequestContext::try_get();\n  if (rc == nullptr) {\n    return -1;\n  }\n  auto* t = rc->getContextData(token);\n  return t ? dynamic_cast<TagData*>(t)->tag : -1;\n}\n\nstatic void setTag(int t) {\n  folly::RequestContext::get()->setContextData(\n      token, std::make_unique<TagData>(t));\n}\n\nstatic void clearTag() {\n  folly::RequestContext::get()->clearContextData(token);\n}\n\nTEST(RequestContextTest, Main) {\n  folly::ManualExecutor exec;\n\n  // Various things on which we'll co_await and check that request context is\n  // preserved.\n  folly::coro::Baton baton;\n  folly::coro::UnboundedQueue<int> queue;\n  folly::coro::Mutex mutex;\n  bool locked = mutex.try_lock();\n  EXPECT_TRUE(locked);\n\n  // A generator on which we'll also co_await to see what happens.\n  auto generator = [&]() -> folly::coro::AsyncGenerator<int&&> {\n    // Request context propagated from the caller.\n    EXPECT_EQ(getTag(), 4);\n\n    co_await folly::coro::co_reschedule_on_current_executor;\n    EXPECT_EQ(getTag(), 4);\n\n    // Change request context before yielding. This change will propagate to the\n    // caller.\n    folly::RequestContext::create();\n    setTag(5);\n    co_yield 10;\n    // The caller changes request context before calling next(). This change is\n    // propagated to us.\n    EXPECT_EQ(getTag(), 6);\n\n    co_await folly::coro::co_reschedule_on_current_executor;\n    EXPECT_EQ(getTag(), 6);\n\n    co_yield 20;\n    EXPECT_EQ(getTag(), 6);\n  };\n\n  // Main coroutine of the test. Awaits on various things and checks that\n  // request context was preserved/unpreserved when expected.\n  auto task = [&]() -> folly::coro::Task<void> {\n    EXPECT_EQ(getTag(), 1);\n    clearTag();\n    setTag(2);\n    EXPECT_EQ(getTag(), 2);\n\n    // co_reschedule_on_current_executor preserves request context\n    // (see CurrentExecutor.h).\n    co_await folly::coro::co_reschedule_on_current_executor;\n\n    // Baton, UnboundedQueue, Mutex, and other awaitables that don't customize\n    // viaIfAsync preserve request context (see ViaIfAsync.h).\n\n    // Baton.\n    co_await baton;\n    EXPECT_EQ(getTag(), 2);\n\n    // UnboundedQueue.\n    int v = co_await queue.dequeue();\n    EXPECT_EQ(v, 42);\n    EXPECT_EQ(getTag(), 2);\n\n    // Mutex.\n    co_await mutex.co_scoped_lock();\n    EXPECT_EQ(getTag(), 2);\n\n    // blockingWait.\n    folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n      co_await folly::coro::co_reschedule_on_current_executor;\n      co_return;\n    }());\n    EXPECT_EQ(getTag(), 2);\n\n    // Now on to the things that do leak request context.\n    // This is probably intended. I guess the convention is that a\n    // function/coroutine should to restore the original request context before\n    // returning.\n\n    // Task that doesn't suspend. If it changes request context before\n    // returning, the change is propagated to us.\n    v = co_await []() -> folly::coro::Task<int> {\n      EXPECT_EQ(getTag(), 2);\n      folly::RequestContext::create();\n      setTag(3);\n      EXPECT_EQ(getTag(), 3);\n      co_return 30;\n    }();\n    EXPECT_EQ(v, 30);\n    EXPECT_EQ(getTag(), 3);\n\n    // Task that suspends. Same as above, request context gets out.\n    v = co_await [&]() -> folly::coro::Task<int> {\n      EXPECT_EQ(getTag(), 3);\n      folly::RequestContext::create();\n      EXPECT_EQ(getTag(), -1);\n      setTag(4);\n      co_await folly::coro::co_reschedule_on_current_executor;\n      EXPECT_EQ(getTag(), 4);\n      co_return 40;\n    }();\n    EXPECT_EQ(v, 40);\n    EXPECT_EQ(getTag(), 4);\n\n    // AsyncGenerator.\n\n    auto gen = generator();\n    EXPECT_EQ(getTag(), 4);\n\n    // Request context propagates out of the generator.\n    auto x = co_await gen.next();\n    EXPECT_EQ(x.value(), 10);\n    EXPECT_EQ(getTag(), 5);\n\n    co_await folly::coro::co_reschedule_on_current_executor;\n    EXPECT_EQ(getTag(), 5);\n\n    // Request context propagates into the generator.\n    folly::RequestContext::create();\n    setTag(6);\n    x = co_await gen.next();\n    EXPECT_EQ(x.value(), 20);\n    EXPECT_EQ(getTag(), 6);\n\n    co_await folly::coro::co_reschedule_on_current_executor;\n    EXPECT_EQ(getTag(), 6);\n  };\n\n  // Start the main coroutine.\n  folly::SemiFuture<folly::Unit> f;\n  {\n    folly::RequestContextScopeGuard rg;\n    setTag(1);\n    f = co_withExecutor(&exec, task()).start();\n  }\n  exec.drain();\n  EXPECT_FALSE(f.isReady());\n  EXPECT_EQ(getTag(), -1);\n\n  // Send the various wakeup signals the main coroutine expects.\n\n  // Baton.\n  {\n    folly::RequestContextScopeGuard rg;\n    setTag(100);\n    baton.post();\n    EXPECT_EQ(getTag(), 100);\n  }\n  exec.drain();\n  EXPECT_FALSE(f.isReady());\n  EXPECT_EQ(getTag(), -1);\n\n  // UnboundedQueue.\n  {\n    folly::RequestContextScopeGuard rg;\n    setTag(200);\n    queue.enqueue(42);\n    EXPECT_EQ(getTag(), 200);\n  }\n  exec.drain();\n  EXPECT_FALSE(f.isReady());\n  EXPECT_EQ(getTag(), -1);\n\n  // Mutex.\n  {\n    folly::RequestContextScopeGuard rg;\n    setTag(300);\n    mutex.unlock();\n    EXPECT_EQ(getTag(), 300);\n  }\n  exec.drain();\n\n  // Main coroutine should be done now.\n  EXPECT_TRUE(f.isReady());\n  std::move(f).get();\n\n  EXPECT_EQ(getTag(), -1);\n}\n"
  },
  {
    "path": "folly/coro/test/RetryTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Retry.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/portability/GTest.h>\n\n#include <chrono>\n#include <exception>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace std::chrono_literals;\n\nnamespace {\n\nstruct SomeError : std::exception {\n  explicit SomeError(int v) : value(v) {}\n  int value;\n};\n\n} // namespace\n\nTEST(RetryN, Success) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    co_await folly::coro::retryN(3, [&]() -> folly::coro::Task<void> {\n      ++runCount;\n      co_return;\n    });\n    EXPECT_EQ(1, runCount);\n  }());\n}\n\nTEST(RetryN, Failure) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryN(3, [&]() -> folly::coro::Task<void> {\n          ++runCount;\n          co_yield folly::coro::co_error(SomeError{runCount});\n        }));\n    EXPECT_EQ(4, runCount);\n    EXPECT_TRUE(result.hasException<SomeError>());\n    EXPECT_EQ(4, result.tryGetExceptionObject<SomeError>()->value);\n  }());\n}\n\nTEST(RetryN, EventualSuccess) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryN(3, [&]() -> folly::coro::Task<void> {\n          ++runCount;\n          if (runCount <= 2) {\n            co_yield folly::coro::co_error(SomeError{runCount});\n          }\n        }));\n    EXPECT_EQ(3, runCount);\n    EXPECT_TRUE(result.hasValue());\n  }());\n}\n\nTEST(RetryN, NeverRetry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryN(\n            3,\n            [&]() -> folly::coro::Task<void> {\n              ++runCount;\n              if (runCount <= 2) {\n                co_yield folly::coro::co_error(SomeError{runCount});\n              }\n            },\n            [](const folly::exception_wrapper&) { return false; }));\n    EXPECT_EQ(1, runCount);\n  }());\n}\n\nTEST(RetryWithJitter, Success) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    auto start = std::chrono::steady_clock::now();\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryWithExponentialBackoff(\n            10, 10ms, 500ms, 0.25, [&]() -> folly::coro::Task<void> {\n              ++runCount;\n              co_return;\n            }));\n    auto end = std::chrono::steady_clock::now();\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(1, runCount);\n    EXPECT_TRUE((end - start) < 10ms);\n  }());\n}\n\nTEST(RetryWithJitter, Failure) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    auto prev = std::chrono::steady_clock::now();\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryWithExponentialBackoff(\n            5, 10ms, 100ms, 0.25, [&]() -> folly::coro::Task<void> {\n              ++runCount;\n              auto now = std::chrono::steady_clock::now();\n              auto elapsedMs =\n                  std::chrono::duration_cast<std::chrono::milliseconds>(\n                      now - prev)\n                      .count();\n              LOG(INFO) << \"Attempt \" << runCount << \" after \" << elapsedMs\n                        << \"ms\";\n              prev = now;\n              co_yield folly::coro::co_error(SomeError{runCount});\n            }));\n    EXPECT_TRUE(result.hasException<SomeError>());\n    EXPECT_EQ(6, runCount);\n  }());\n}\n\nTEST(RetryWithJitter, EventualSuccess) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    auto start = std::chrono::steady_clock::now();\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryWithExponentialBackoff(\n            10, 10ms, 500ms, 0.25, [&]() -> folly::coro::Task<void> {\n              ++runCount;\n              auto now = std::chrono::steady_clock::now();\n              if (runCount == 1) {\n                EXPECT_TRUE((now - start) < 5ms);\n                start = now;\n                co_yield folly::coro::co_error(SomeError{1});\n              } else if (runCount == 2) {\n                // Really should be at least 10ms but allowing for some\n                // potential measurement error between the timer and\n                // steady_clock.\n                EXPECT_TRUE((now - start) >= 8ms);\n                start = now;\n                co_yield folly::coro::co_error(SomeError{2});\n              }\n              co_return;\n            }));\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(3, runCount);\n  }());\n}\n\nTEST(RetryWithDecider, AlwaysRetry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryWithExponentialBackoff(\n            5, 0ms, 1ms, 0.0, [&]() -> folly::coro::Task<void> {\n              ++runCount;\n              co_yield folly::coro::co_error(SomeError(1));\n            }));\n    EXPECT_TRUE(result.hasException());\n    EXPECT_EQ(6, runCount);\n  }());\n}\n\nTEST(RetryWithDecider, NeverRetry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryWithExponentialBackoff(\n            5,\n            0ms,\n            1ms,\n            0.0,\n            [&]() -> folly::coro::Task<void> {\n              ++runCount;\n              co_yield folly::coro::co_error(SomeError(1));\n            },\n            [](const folly::exception_wrapper&) { return false; }));\n    EXPECT_TRUE(result.hasException());\n    EXPECT_EQ(1, runCount);\n  }());\n}\n\nTEST(RetryWithDecider, SometimesRetry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    int runCount = 0;\n    folly::Try<void> result = co_await folly::coro::co_awaitTry(\n        folly::coro::retryWithExponentialBackoff(\n            5,\n            0ms,\n            1ms,\n            0.0,\n            [&]() -> folly::coro::Task<void> {\n              ++runCount;\n              co_yield folly::coro::co_error(SomeError(runCount));\n            },\n            [](const folly::exception_wrapper& ew) {\n              try {\n                ew.throw_exception();\n              } catch (const SomeError& e) {\n                return e.value < 3;\n              }\n            }));\n    EXPECT_TRUE(result.hasException());\n    EXPECT_EQ(3, runCount);\n  }());\n}\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/test/RustAdaptorsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/RustAdaptors.h>\n#include <folly/coro/Sleep.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\n#include <chrono>\n\n#if FOLLY_HAS_COROUTINES\n\ntemplate <typename T>\nT getPollFuture(folly::coro::PollFuture<T> future) {\n  while (true) {\n    folly::Baton<> b;\n    auto poll = future.poll([&] { b.post(); });\n    if (poll) {\n      return std::move(poll).value();\n    }\n    b.wait();\n  }\n}\n\ntemplate <typename T>\nfolly::Optional<T> getNextPollStream(folly::coro::PollStream<T>& stream) {\n  while (true) {\n    folly::Baton<> b;\n    auto poll = stream.poll([&] { b.post(); });\n    if (poll) {\n      return std::move(poll).value();\n    }\n    b.wait();\n  }\n}\n\nfolly::coro::Task<int> task42() {\n  co_await folly::coro::sleep(std::chrono::milliseconds{10});\n  co_await folly::coro::sleep(std::chrono::milliseconds{10});\n  co_return 42;\n}\n\nTEST(RustAdaptorsTest, PollFuture) {\n  EXPECT_EQ(42, getPollFuture(folly::coro::PollFuture<int>(task42())));\n}\n\nTEST(RustAdaptorsTest, PollFutureSemiFuture) {\n  EXPECT_EQ(42, getPollFuture(folly::coro::PollFuture<int>(task42().semi())));\n}\n\nfolly::coro::AsyncGenerator<int> stream123() {\n  co_await folly::coro::sleep(std::chrono::milliseconds{10});\n  co_await folly::coro::sleep(std::chrono::milliseconds{10});\n  co_yield 1;\n  co_await folly::coro::sleep(std::chrono::milliseconds{10});\n  co_yield 2;\n  co_await folly::coro::sleep(std::chrono::milliseconds{10});\n  co_await folly::coro::sleep(std::chrono::milliseconds{10});\n  co_yield 3;\n}\n\nTEST(RustAdaptorsTest, PollStream) {\n  auto stream = folly::coro::PollStream<int>(stream123());\n  EXPECT_EQ(1, getNextPollStream(stream).value());\n  EXPECT_EQ(2, getNextPollStream(stream).value());\n  EXPECT_EQ(3, getNextPollStream(stream).value());\n  EXPECT_FALSE(getNextPollStream(stream).hasValue());\n}\n\nfolly::coro::Task<void> cancellationTask(bool& done) {\n  folly::coro::Baton b;\n  folly::CancellationCallback cb(\n      co_await folly::coro::co_current_cancellation_token, [&] { b.post(); });\n  co_await b;\n  done = true;\n}\n\nTEST(RustAdaptorsTest, PollFutureCancellation) {\n  bool done{false};\n  {\n    auto future = folly::coro::PollFuture<void>(cancellationTask(done));\n    EXPECT_EQ(folly::none, future.poll([] {}));\n    EXPECT_FALSE(done);\n  }\n  EXPECT_TRUE(done);\n}\n\nfolly::coro::AsyncGenerator<int> cancellationStream(bool& done) {\n  co_yield 1;\n  co_yield 2;\n  co_await cancellationTask(done);\n}\n\nTEST(RustAdaptorsTest, PollStreamCancellation) {\n  bool done{false};\n  {\n    auto stream = folly::coro::PollStream<int>(cancellationStream(done));\n    EXPECT_EQ(1, getNextPollStream(stream).value());\n    EXPECT_EQ(2, getNextPollStream(stream).value());\n    EXPECT_EQ(folly::none, stream.poll([] {}));\n    EXPECT_FALSE(done);\n  }\n  EXPECT_TRUE(done);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/ScopeExitTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/AutoCleanup.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n#include <folly/portability/GTest.h>\n\n#include <stdexcept>\n#include <type_traits>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nnamespace {\nclass ScopeExitTest : public testing::Test {\n protected:\n  int count = 0;\n};\n\nclass AsyncGeneratorScopeExitTest : public ScopeExitTest {};\n\nclass AutoCleanupScopeExitTest : public ScopeExitTest {};\n} // namespace\n\nTEST_F(ScopeExitTest, OneExitAction) {\n  folly::coro::blockingWait([this]() -> Task<> {\n    ++count;\n    co_await co_scope_exit([this]() -> Task<> {\n      count *= 2;\n      co_return;\n    });\n    ++count;\n  }());\n  EXPECT_EQ(count, 4);\n}\n\nTEST_F(ScopeExitTest, TwoExitActions) {\n  folly::coro::blockingWait([this]() -> Task<> {\n    ++count;\n    co_await co_scope_exit([this]() -> Task<> {\n      count *= count;\n      co_return;\n    });\n    co_await co_scope_exit([this]() -> Task<> {\n      count *= 2;\n      co_return;\n    });\n    ++count;\n  }());\n  EXPECT_EQ(count, 16);\n}\n\nTEST_F(ScopeExitTest, OneExitActionWithException) {\n  EXPECT_THROW(\n      folly::coro::blockingWait([this]() -> Task<> {\n        ++count;\n        co_await co_scope_exit([this]() -> Task<> {\n          count *= 2;\n          co_return;\n        });\n        throw std::runtime_error(\"Something bad happened!\");\n      }()),\n      std::runtime_error);\n  EXPECT_EQ(count, 2);\n}\n\nTEST_F(ScopeExitTest, ExceptionInExitActionCausesTermination) {\n  ASSERT_DEATH(\n      folly::coro::blockingWait([]() -> Task<> {\n        co_await co_scope_exit([]() -> Task<> {\n          throw std::runtime_error(\"Something bad happened!\");\n        });\n      }()),\n      \"\");\n}\n\nTEST_F(ScopeExitTest, ExceptionInExitActionDuringExceptionCausesTermination) {\n  ASSERT_DEATH(\n      folly::coro::blockingWait([]() -> Task<> {\n        co_await co_scope_exit([]() -> Task<> {\n          throw std::runtime_error(\"Something bad happened!\");\n        });\n        throw std::runtime_error(\"Throwing from parent\");\n      }()),\n      \"Something bad happened!\");\n}\n\nTEST_F(ScopeExitTest, StatefulExitAction) {\n  folly::coro::blockingWait([this]() -> Task<> {\n    auto&& [i] = co_await co_scope_exit(\n        [this](int&& ii) -> Task<void> {\n          count += ii;\n          co_return;\n        },\n        3);\n    ++count;\n    i *= i;\n  }());\n  EXPECT_EQ(count, 10);\n}\n\nTEST_F(ScopeExitTest, NonMoveableState) {\n  folly::coro::blockingWait([this]() -> Task<> {\n    auto&& [asyncScope] = co_await co_scope_exit(\n        [this](auto&& scope) -> Task<void> {\n          co_await scope->joinAsync();\n          count *= 2;\n        },\n        std::make_unique<AsyncScope>());\n\n    auto ex = co_await co_current_executor;\n    asyncScope->add(co_withExecutor(\n        ex, co_invoke([this]() -> Task<> {\n          ++count;\n          co_return;\n        })));\n  }());\n  EXPECT_EQ(count, 2);\n}\n\nTEST_F(ScopeExitTest, OneExitActionThroughNoThrow) {\n  EXPECT_THROW(\n      folly::coro::blockingWait([this]() -> Task<> {\n        co_await co_scope_exit([this]() -> Task<> {\n          ++count;\n          co_return;\n        });\n        co_await co_nothrow([]() -> Task<> {\n          throw std::runtime_error(\"Something bad happened!\");\n          co_return;\n        }());\n      }()),\n      std::runtime_error);\n  EXPECT_EQ(count, 1);\n}\n\nTEST_F(ScopeExitTest, ExitActionInsideNoThrow) {\n  EXPECT_THROW(\n      folly::coro::blockingWait([this]() -> Task<> {\n        try {\n          co_await co_nothrow([this]() -> Task<> {\n            co_await co_scope_exit([this]() -> Task<> {\n              ++count;\n              co_return;\n            });\n            throw std::runtime_error(\"Something bad happened!\");\n            co_return;\n          }());\n        } catch (...) {\n          ADD_FAILURE();\n        }\n      }()),\n      std::runtime_error);\n  EXPECT_EQ(count, 1);\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, PartiallyConsumed) {\n  auto makeGenerator = [&]() -> folly::coro::CleanableAsyncGenerator<int> {\n    co_await co_scope_exit([&]() -> folly::coro::Task<> {\n      ++count;\n      co_return;\n    });\n    co_yield 1;\n    co_yield 2;\n  };\n\n  auto gen = makeGenerator();\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    auto result = co_await gen.next();\n    EXPECT_TRUE(result);\n    EXPECT_EQ(count, 0);\n    co_await std::move(gen).cleanup();\n  }());\n\n  EXPECT_EQ(count, 1);\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, FullyConsumed) {\n  auto makeGenerator = [&]() -> folly::coro::CleanableAsyncGenerator<int> {\n    co_await co_scope_exit([&]() -> folly::coro::Task<> {\n      ++count;\n      co_return;\n    });\n    co_yield 1;\n  };\n\n  auto gen = makeGenerator();\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    auto result = co_await gen.next();\n    EXPECT_TRUE(result);\n    result = co_await gen.next();\n    EXPECT_FALSE(result);\n    EXPECT_EQ(count, 0);\n    co_await std::move(gen).cleanup();\n  }());\n\n  EXPECT_EQ(count, 1);\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, TwoExitActions) {\n  auto makeGenerator = [&]() -> folly::coro::CleanableAsyncGenerator<int> {\n    co_await co_scope_exit([&]() -> folly::coro::Task<> {\n      EXPECT_EQ(count, 1);\n      ++count;\n      co_return;\n    });\n    co_await co_scope_exit([&]() -> folly::coro::Task<> {\n      EXPECT_EQ(count, 0);\n      ++count;\n      co_return;\n    });\n    co_return;\n  };\n\n  auto gen = makeGenerator();\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    auto result = co_await gen.next();\n    EXPECT_FALSE(result);\n    EXPECT_EQ(count, 0);\n    co_await std::move(gen).cleanup();\n  }());\n\n  EXPECT_EQ(count, 2);\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, StatefulExitAction) {\n  auto makeGenerator = [&]() -> folly::coro::CleanableAsyncGenerator<int> {\n    auto&& [i] = co_await co_scope_exit(\n        [&](int&& ii) -> Task<void> {\n          count += ii;\n          co_return;\n        },\n        3);\n    ++count;\n    i *= i;\n    co_return;\n  };\n\n  auto gen = makeGenerator();\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    EXPECT_EQ(count, 0);\n    auto result = co_await gen.next();\n    EXPECT_FALSE(result);\n    EXPECT_EQ(count, 1);\n    co_await std::move(gen).cleanup();\n  }());\n\n  EXPECT_EQ(count, 10);\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, OneExitActionThroughNoThrow) {\n  struct Error : std::exception {};\n\n  auto makeGenerator = [&]() -> folly::coro::CleanableAsyncGenerator<int> {\n    co_await co_scope_exit([&]() -> folly::coro::Task<> {\n      ++count;\n      co_return;\n    });\n    co_await co_nothrow([]() -> Task<> {\n      throw Error{};\n      co_return;\n    }());\n  };\n\n  auto gen = makeGenerator();\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<> {\n    EXPECT_THROW(co_await gen.next(), Error);\n    co_await std::move(gen).cleanup();\n  }());\n\n  EXPECT_EQ(count, 1);\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, NextAfterCancel) {\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSrc;\n    auto gen = folly::coro::co_invoke(\n        [&]() -> folly::coro::CleanableAsyncGenerator<int> {\n          co_await co_scope_exit([&]() -> folly::coro::Task<> {\n            ++count;\n            co_return;\n          });\n          co_yield 1;\n          co_yield 2;\n          co_await folly::coro::co_safe_point;\n          co_await co_scope_exit([&]() -> folly::coro::Task<> {\n            ++count;\n            co_return;\n          });\n          co_yield 3;\n        });\n\n    auto result = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(cancelSrc.getToken(), gen.next()));\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(1, *result.value());\n\n    cancelSrc.requestCancellation();\n    result = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(cancelSrc.getToken(), gen.next()));\n    EXPECT_TRUE(result.hasValue());\n    EXPECT_EQ(2, *result.value());\n\n    result = co_await folly::coro::co_awaitTry(\n        folly::coro::co_withCancellation(cancelSrc.getToken(), gen.next()));\n    EXPECT_TRUE(result.hasException());\n    EXPECT_THROW(result.value(), folly::OperationCancelled);\n\n    EXPECT_EQ(count, 0);\n    co_await std::move(gen).cleanup();\n    EXPECT_EQ(count, 1);\n  }());\n}\n\nTEST_F(AutoCleanupScopeExitTest, AsyncGeneratorAutoCleanup) {\n  blockingWait([&]() -> Task<> {\n    auto gen = co_invoke([&]() -> CleanableAsyncGenerator<int> {\n      co_await co_scope_exit([&]() -> Task<> {\n        ++count;\n        co_return;\n      });\n      co_yield 1;\n    });\n    co_await gen.next();\n    gen = co_invoke(\n        [](auto&&, auto) -> CleanableAsyncGenerator<int> { co_return; },\n        AutoCleanup{std::move(gen)},\n        42);\n    co_await std::move(gen).cleanup();\n    EXPECT_EQ(count, 1);\n  }());\n}\n\nTEST_F(AutoCleanupScopeExitTest, AsyncGeneratorAutoCleanupMove) {\n  blockingWait([&]() -> Task<> {\n    auto gen = co_invoke([&]() -> CleanableAsyncGenerator<int> {\n      co_await co_scope_exit([&]() -> Task<> {\n        ++count;\n        co_return;\n      });\n      co_yield 1;\n    });\n    gen = co_invoke(\n        [](auto autoCleanupGen, auto) -> CleanableAsyncGenerator<int> {\n          co_await autoCleanupGen->next();\n        },\n        AutoCleanup{std::move(gen)},\n        42);\n    co_await gen.next();\n    co_await std::move(gen).cleanup();\n    EXPECT_EQ(count, 1);\n  }());\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, AsyncGeneratorAutoCleanupFn) {\n  std::function cleanupFn{[&](int&&) -> folly::coro::Task<> {\n    ++count;\n    co_return;\n  }};\n  blockingWait([&]() -> Task<> {\n    auto gen = co_invoke(\n        [](auto) -> CleanableAsyncGenerator<int> { co_return; },\n        AutoCleanup{1, cleanupFn});\n    co_await std::move(gen).cleanup();\n    EXPECT_EQ(count, 1);\n  }());\n}\n\nTEST_F(AsyncGeneratorScopeExitTest, AsyncGeneratorAutoCleanupFnMove) {\n  std::function cleanupFn{[&](int&&) -> folly::coro::Task<> {\n    ++count;\n    co_return;\n  }};\n  blockingWait([&]() -> Task<> {\n    auto gen = co_invoke(\n        [](auto) -> CleanableAsyncGenerator<int> { co_return; },\n        AutoCleanup{1, cleanupFn});\n    co_await gen.next();\n    co_await std::move(gen).cleanup();\n    EXPECT_EQ(count, 2);\n  }());\n}\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/test/SerialQueueRunnerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/SerialQueueRunner.h>\n\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nstruct QueueRunnerTest : testing::Test {};\n\nCO_TEST_F(QueueRunnerTest, example) {\n  folly::coro::AsyncScope scope;\n  folly::coro::SerialQueueRunner queue;\n\n  // fork the runner\n  scope.add(\n      co_withExecutor(co_await folly::coro::co_current_executor, queue.run()));\n\n  // launch work items in the runner's queue\n  std::vector<size_t> nums;\n  for (size_t i = 0; i < 10; ++i) {\n    co_await folly::coro::co_reschedule_on_current_executor;\n    queue.add(folly::coro::co_invoke([&nums, i]() -> folly::coro::Task<> {\n      for (size_t j = 0; j < 10; ++j) {\n        co_await folly::coro::co_reschedule_on_current_executor;\n        nums.push_back(i);\n      }\n    }));\n  }\n  co_await folly::coro::co_reschedule_on_current_executor;\n  queue.done();\n\n  // join the runner\n  co_await scope.joinAsync();\n\n  // quick expectations\n  EXPECT_TRUE(std::is_sorted(nums.begin(), nums.end()));\n  EXPECT_EQ(100, nums.size());\n\n  // full expectation\n  std::vector<size_t> expected;\n  for (size_t i = 0; i < 10; ++i) {\n    for (size_t j = 0; j < 10; ++j) {\n      expected.push_back(i);\n    }\n  }\n  EXPECT_EQ(expected, nums);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/SharedMutexBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n#include <folly/Benchmark.h>\n#include <folly/Random.h>\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/SharedMutex.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/init/Init.h>\n\nusing namespace folly;\n\nvoid runBenchmark(double writePercent, size_t numThreads, size_t numTasks) {\n  auto suspender = folly::BenchmarkSuspender();\n  folly::CPUThreadPoolExecutor executor(numThreads);\n  coro::SharedMutex mutex;\n  int valueProtected{42};\n  std::atomic<bool> go(false);\n  auto task = [&]() -> coro::Task<void> {\n    // decide if this will be a writer or reader before start benchmarking\n    bool isWrite = (folly::Random::randDouble01() < writePercent);\n    while (!go.load()) {\n      std::this_thread::yield();\n    }\n    if (isWrite) {\n      auto wLock = co_await mutex.co_scoped_lock();\n      auto copy = valueProtected;\n      folly::doNotOptimizeAway(copy);\n    } else {\n      auto rLock = co_await mutex.co_scoped_lock_shared();\n      auto copy = valueProtected;\n      folly::doNotOptimizeAway(copy);\n    }\n  };\n  coro::AsyncScope scope;\n  for (size_t i = 0; i < numTasks; ++i) {\n    scope.add(coro::co_withExecutor(&executor, task()));\n  }\n  suspender.dismiss();\n\n  // start benchmarking\n  go.store(true);\n  // block the current thread until all tasks are complete\n  scope.cleanup().wait();\n}\n\nBENCHMARK(SharedMutex_all_read_1thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.0, 1, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_read_4thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.0, 4, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_read_8thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.0, 8, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_read_16thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.0, 16, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_read_32thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.0, 32, 10000);\n  }\n}\nBENCHMARK_DRAW_LINE();\nBENCHMARK(SharedMutex_1pct_write_1thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.01, 1, 10000);\n  }\n}\nBENCHMARK(SharedMutex_1pct_write_4thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.01, 4, 10000);\n  }\n}\nBENCHMARK(SharedMutex_1pct_write_8thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.01, 8, 10000);\n  }\n}\nBENCHMARK(SharedMutex_1pct_write_16thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.01, 16, 10000);\n  }\n}\nBENCHMARK(SharedMutex_1pct_write_32thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.01, 32, 10000);\n  }\n}\nBENCHMARK_DRAW_LINE();\nBENCHMARK(SharedMutex_10pct_write_1thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.1, 1, 10000);\n  }\n}\nBENCHMARK(SharedMutex_10pct_write_4thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.1, 4, 10000);\n  }\n}\nBENCHMARK(SharedMutex_10pct_write_8thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.1, 8, 10000);\n  }\n}\nBENCHMARK(SharedMutex_10pct_write_16thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.1, 16, 10000);\n  }\n}\nBENCHMARK(SharedMutex_10pct_write_32thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(0.1, 32, 10000);\n  }\n}\nBENCHMARK_DRAW_LINE();\nBENCHMARK(SharedMutex_all_write_1thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(1.0, 1, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_write_4thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(1.0, 4, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_write_8thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(1.0, 8, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_write_16thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(1.0, 16, 10000);\n  }\n}\nBENCHMARK(SharedMutex_all_write_32thread_10000task, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    runBenchmark(1.0, 32, 10000);\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv, true);\n\n  folly::runBenchmarks();\n  return 0;\n}\n\n#if 0\n\nOn Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz\n$ buck run @mode/opt folly/coro/test:shared_mutex_benchmark -- --bm_slice_usec 50000\n============================================================================\n[...]ly/coro/test/SharedMutexBenchmark.cpp     relative  time/iter   iters/s\n============================================================================\nSharedMutex_all_read_1thread_10000task                      3.56ms    280.74\nSharedMutex_all_read_4thread_10000task                      5.16ms    193.83\nSharedMutex_all_read_8thread_10000task                      5.59ms    179.02\nSharedMutex_all_read_16thread_10000task                     8.58ms    116.49\nSharedMutex_all_read_32thread_10000task                    11.45ms     87.33\n----------------------------------------------------------------------------\nSharedMutex_1pct_write_1thread_10000task                    3.59ms    278.48\nSharedMutex_1pct_write_4thread_10000task                   10.97ms     91.12\nSharedMutex_1pct_write_8thread_10000task                   13.18ms     75.88\nSharedMutex_1pct_write_16thread_10000task                  14.85ms     67.34\nSharedMutex_1pct_write_32thread_10000task                  17.24ms     58.00\n----------------------------------------------------------------------------\nSharedMutex_10pct_write_1thread_10000task                   3.70ms    269.94\nSharedMutex_10pct_write_4thread_10000task                  15.34ms     65.17\nSharedMutex_10pct_write_8thread_10000task                  16.81ms     59.50\nSharedMutex_10pct_write_16thread_10000task                 19.14ms     52.25\nSharedMutex_10pct_write_32thread_10000task                 22.95ms     43.57\n----------------------------------------------------------------------------\nSharedMutex_all_write_1thread_10000task                     3.70ms    270.50\nSharedMutex_all_write_4thread_10000task                    20.68ms     48.36\nSharedMutex_all_write_8thread_10000task                    22.51ms     44.43\nSharedMutex_all_write_16thread_10000task                   25.31ms     39.51\nSharedMutex_all_write_32thread_10000task                   27.40ms     36.50\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/SharedMutexTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/SharedMutex.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\n#include <mutex>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly;\n\nclass SharedMutexTest : public testing::Test {};\n\nTEST_F(SharedMutexTest, TryLock) {\n  // the mutex can only be one of the following five states\n  struct MutexStateBase : private folly::NonCopyableNonMovable {\n    virtual ~MutexStateBase() {}\n    coro::SharedMutex m{};\n    bool upgraded{false};\n  };\n  struct Unlocked : public MutexStateBase {};\n  struct ExclusivelyLocked : public MutexStateBase {\n    ExclusivelyLocked() { CHECK(m.try_lock()); }\n    ~ExclusivelyLocked() override { m.unlock(); }\n  };\n  struct SharedLockedOnly : public MutexStateBase {\n    SharedLockedOnly() { CHECK(m.try_lock_shared()); }\n    ~SharedLockedOnly() override { m.unlock_shared(); }\n  };\n  struct UpgradeLockedOnly : public MutexStateBase {\n    UpgradeLockedOnly() { CHECK(m.try_lock_upgrade()); }\n    ~UpgradeLockedOnly() override {\n      if (upgraded) {\n        m.unlock();\n      } else {\n        m.unlock_upgrade();\n      }\n    }\n  };\n  struct UpgradeAndSharedLocked : public MutexStateBase {\n    UpgradeAndSharedLocked() {\n      CHECK(m.try_lock_upgrade());\n      CHECK(m.try_lock_shared());\n    }\n    ~UpgradeAndSharedLocked() override {\n      if (upgraded) {\n        m.unlock();\n      } else {\n        m.unlock_upgrade();\n      }\n      m.unlock_shared();\n    }\n  };\n  // all possible actions\n  auto lock_success = [](MutexStateBase& state) {\n    CHECK(state.m.try_lock());\n    state.m.unlock();\n  };\n  auto lock_fail = [](MutexStateBase& state) { CHECK(!state.m.try_lock()); };\n  auto lock_upgrade_success = [](MutexStateBase& state) {\n    CHECK(state.m.try_lock_upgrade());\n    state.m.unlock_upgrade();\n  };\n  auto lock_upgrade_fail = [](MutexStateBase& state) {\n    CHECK(!state.m.try_lock_upgrade());\n  };\n  auto lock_shared_success = [](MutexStateBase& state) {\n    CHECK(state.m.try_lock_shared());\n    state.m.unlock_shared();\n  };\n  auto lock_shared_fail = [](MutexStateBase& state) {\n    CHECK(!state.m.try_lock_shared());\n  };\n  auto unlock_upgrade_and_lock_success = [](MutexStateBase& state) {\n    CHECK(state.m.try_unlock_upgrade_and_lock());\n    state.upgraded = true;\n  };\n  auto unlock_upgrade_and_lock_fail = [](MutexStateBase& state) {\n    CHECK(!state.m.try_unlock_upgrade_and_lock());\n  };\n\n  std::vector<std::tuple<\n      std::unique_ptr<MutexStateBase> /* initial state */,\n      std::function<void(MutexStateBase&)> /* action */\n      >>\n      cases;\n\n  // all state transitions and expected outcome\n  cases.emplace_back(std::make_unique<Unlocked>(), lock_success);\n  cases.emplace_back(std::make_unique<Unlocked>(), lock_upgrade_success);\n  cases.emplace_back(std::make_unique<Unlocked>(), lock_shared_success);\n\n  cases.emplace_back(std::make_unique<ExclusivelyLocked>(), lock_fail);\n  cases.emplace_back(std::make_unique<ExclusivelyLocked>(), lock_upgrade_fail);\n  cases.emplace_back(std::make_unique<ExclusivelyLocked>(), lock_shared_fail);\n\n  cases.emplace_back(std::make_unique<SharedLockedOnly>(), lock_fail);\n  cases.emplace_back(\n      std::make_unique<SharedLockedOnly>(), lock_upgrade_success);\n  cases.emplace_back(std::make_unique<SharedLockedOnly>(), lock_shared_success);\n\n  cases.emplace_back(std::make_unique<UpgradeLockedOnly>(), lock_fail);\n  cases.emplace_back(\n      std::make_unique<UpgradeLockedOnly>(), lock_shared_success);\n  cases.emplace_back(std::make_unique<UpgradeLockedOnly>(), lock_upgrade_fail);\n  cases.emplace_back(\n      std::make_unique<UpgradeLockedOnly>(), unlock_upgrade_and_lock_success);\n\n  cases.emplace_back(std::make_unique<UpgradeAndSharedLocked>(), lock_fail);\n  cases.emplace_back(\n      std::make_unique<UpgradeAndSharedLocked>(), lock_shared_success);\n  cases.emplace_back(\n      std::make_unique<UpgradeAndSharedLocked>(), lock_upgrade_fail);\n  cases.emplace_back(\n      std::make_unique<UpgradeAndSharedLocked>(), unlock_upgrade_and_lock_fail);\n\n  for (auto& [state, action] : cases) {\n    action(*state);\n  }\n}\n\nTEST_F(SharedMutexTest, ManualLockAsync) {\n  coro::SharedMutex mutex;\n  int value = 0;\n\n  auto makeReaderTask = [&](coro::Baton& b) -> coro::Task<int> {\n    co_await mutex.co_lock_shared();\n    int valueCopy = value;\n    co_await b;\n    mutex.unlock_shared();\n    co_return valueCopy;\n  };\n\n  auto makeWriterTask = [&](coro::Baton& b) -> coro::Task<void> {\n    co_await mutex.co_lock();\n    co_await b;\n    value += 1;\n    mutex.unlock();\n  };\n\n  ManualExecutor executor;\n\n  {\n    coro::Baton b1;\n    coro::Baton b2;\n    coro::Baton b3;\n    coro::Baton b4;\n    coro::Baton b5;\n\n    auto r1 = co_withExecutor(&executor, makeReaderTask(b1)).start();\n    auto r2 = co_withExecutor(&executor, makeReaderTask(b2)).start();\n    auto w1 = co_withExecutor(&executor, makeWriterTask(b3)).start();\n    auto w2 = co_withExecutor(&executor, makeWriterTask(b4)).start();\n    auto r3 = co_withExecutor(&executor, makeReaderTask(b5)).start();\n    executor.drain();\n\n    b1.post();\n    executor.drain();\n    CHECK_EQ(0, std::move(r1).get());\n\n    b2.post();\n    executor.drain();\n    CHECK_EQ(0, std::move(r2).get());\n\n    b3.post();\n    executor.drain();\n    CHECK_EQ(1, value);\n\n    b4.post();\n    executor.drain();\n    CHECK_EQ(2, value);\n\n    // This reader should have had to wait for the prior two write locks\n    // to complete before it acquired the read-lock.\n    b5.post();\n    executor.drain();\n    CHECK_EQ(2, std::move(r3).get());\n  }\n}\n\nvoid testAllStateTransitions(\n    std::function<folly::coro::Task<void>(coro::SharedMutex&)> lock,\n    std::function<folly::coro::Task<void>(coro::SharedMutex&)> lock_upgrade,\n    std::function<folly::coro::Task<void>(coro::SharedMutex&)> lock_shared,\n    std::function<folly::coro::Task<void>(coro::SharedMutex&)>\n        unlock_upgrade_and_lock) {\n  // all possible initial state\n  auto unlocked = [](coro::SharedMutex&) {};\n  auto exclusively_locked = [](coro::SharedMutex& m) { CHECK(m.try_lock()); };\n  auto shared_locked_only = [](coro::SharedMutex& m) {\n    CHECK(m.try_lock_shared());\n  };\n  auto upgrade_locked_only = [](coro::SharedMutex& m) {\n    CHECK(m.try_lock_upgrade());\n  };\n  auto upgrade_and_shared_locked = [](coro::SharedMutex& m) {\n    CHECK(m.try_lock_shared());\n    CHECK(m.try_lock_upgrade());\n  };\n\n  // cleanup helpers\n  auto unlock = [](coro::SharedMutex& m) { m.unlock(); };\n  auto unlock_shared = [](coro::SharedMutex& m) { m.unlock_shared(); };\n  auto unlock_upgrade = [](coro::SharedMutex& m) { m.unlock_upgrade(); };\n  // unlock in different order changes the waiter list and the mutex state\n  auto unlock_upgrade_and_unlock_shared = [](coro::SharedMutex& m) {\n    m.unlock_upgrade();\n    m.unlock_shared();\n  };\n  auto unlock_shared_and_unlock_upgrade = [](coro::SharedMutex& m) {\n    m.unlock_shared();\n    m.unlock_upgrade();\n  };\n\n  std::vector<std::tuple<\n      std::string /* test name */,\n      std::function<void(coro::SharedMutex&)> /* set up initial state*/,\n      std::optional<std::function<coro::Task<void>(\n          coro::SharedMutex&)>> /* optionally arrange a waiter */,\n      std::function<coro::Task<void>(coro::SharedMutex&)> /* action */,\n      bool /* expect the task to complete or not */,\n      std::optional<std::function<void(coro::SharedMutex&)>> /* optional\n                                                                  cleanup */\n      >>\n      cases;\n\n  // unlocked & no one waiting\n  cases.emplace_back(\n      \"unlocked-no-waiter-lock\",\n      unlocked,\n      std::nullopt,\n      lock,\n      true,\n      std::nullopt);\n  cases.emplace_back(\n      \"unlocked-no-waiter-lock_upgrade\",\n      unlocked,\n      std::nullopt,\n      lock_upgrade,\n      true,\n      std::nullopt);\n  cases.emplace_back(\n      \"unlocked-no-waiter-lock_shared\",\n      unlocked,\n      std::nullopt,\n      lock_shared,\n      true,\n      std::nullopt);\n\n  // locked with no waiters\n  cases.emplace_back(\n      \"locked-no-waiter-lock\",\n      exclusively_locked,\n      std::nullopt,\n      lock,\n      false,\n      unlock);\n  cases.emplace_back(\n      \"locked-no-waiter-lock_upgrade\",\n      exclusively_locked,\n      std::nullopt,\n      lock_upgrade,\n      false,\n      unlock);\n  cases.emplace_back(\n      \"locked-no-waiter-lock_shared\",\n      exclusively_locked,\n      std::nullopt,\n      lock_shared,\n      false,\n      unlock);\n\n  // locked with waiters\n  cases.emplace_back(\n      \"locked-lock-waiter-lock\", exclusively_locked, lock, lock, false, unlock);\n  cases.emplace_back(\n      \"locked-lock-waiter-lock_upgrade\",\n      exclusively_locked,\n      lock,\n      lock_upgrade,\n      false,\n      unlock);\n  cases.emplace_back(\n      \"locked-lock-waiter-lock_shared\",\n      exclusively_locked,\n      lock,\n      lock_shared,\n      false,\n      unlock);\n\n  cases.emplace_back(\n      \"locked-lock_shared-waiter-lock\",\n      exclusively_locked,\n      lock_shared,\n      lock,\n      false,\n      unlock);\n  cases.emplace_back(\n      \"locked-lock_shared-waiter-lock_upgrade\",\n      exclusively_locked,\n      lock_shared,\n      lock_upgrade,\n      false,\n      unlock);\n  cases.emplace_back(\n      \"locked-lock_shared-waiter-lock_shared\",\n      exclusively_locked,\n      lock_shared,\n      lock_shared,\n      false,\n      unlock);\n\n  cases.emplace_back(\n      \"locked-lock_upgrade-waiter-lock\",\n      exclusively_locked,\n      lock_upgrade,\n      lock,\n      false,\n      unlock);\n  cases.emplace_back(\n      \"locked-lock_upgrade-waiter-lock_upgrade\",\n      exclusively_locked,\n      lock_upgrade,\n      lock_upgrade,\n      false,\n      unlock);\n  cases.emplace_back(\n      \"locked-lock_upgrade-waiter-lock_shared\",\n      exclusively_locked,\n      lock_upgrade,\n      lock_shared,\n      false,\n      unlock);\n\n  // shared locked and no one waiting\n  cases.emplace_back(\n      \"shared_locked-no-waiter-lock\",\n      shared_locked_only,\n      std::nullopt,\n      lock,\n      false,\n      unlock_shared);\n  cases.emplace_back(\n      \"shared_locked-no-waiter-lock_upgrade\",\n      shared_locked_only,\n      std::nullopt,\n      lock_upgrade,\n      true,\n      unlock_shared);\n  cases.emplace_back(\n      \"shared_locked-no-waiter-lock_shared\",\n      shared_locked_only,\n      std::nullopt,\n      lock_shared,\n      true,\n      unlock_shared);\n\n  // shared locked with waiters\n  cases.emplace_back(\n      \"shared_locked-lock-waiter-lock\",\n      shared_locked_only,\n      lock,\n      lock,\n      false,\n      unlock_shared);\n  // this is the case where lock_upgrade wait for waiting writers to\n  // avoid writer starvation\n  cases.emplace_back(\n      \"shared_locked-lock-waiter-lock_upgrade\",\n      shared_locked_only,\n      lock,\n      lock_upgrade,\n      false,\n      unlock_shared);\n  // this is the reader-block-writer-block-reader case, since the mutex\n  // prioritizes the writer/upgrader\n  cases.emplace_back(\n      \"shared_locked-lock-waiter-lock_shared\",\n      shared_locked_only,\n      lock,\n      lock_shared,\n      false,\n      unlock_shared);\n\n  // upgrade locked and no one waiting\n  cases.emplace_back(\n      \"upgrade_locked-no-waiter-lock\",\n      upgrade_locked_only,\n      std::nullopt,\n      lock,\n      false,\n      unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_locked-no-waiter-lock_upgrade\",\n      upgrade_locked_only,\n      std::nullopt,\n      lock_upgrade,\n      false,\n      unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_locked-no-waiter-lock_shared\",\n      upgrade_locked_only,\n      std::nullopt,\n      lock_shared,\n      true,\n      unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_locked-no-waiter-unlock_upgrade_and_lock\",\n      upgrade_locked_only,\n      std::nullopt,\n      unlock_upgrade_and_lock,\n      true,\n      std::nullopt);\n\n  // upgrade locked with waiters\n  cases.emplace_back(\n      \"upgrade_locked-lock-waiter-lock\",\n      upgrade_locked_only,\n      lock,\n      lock,\n      false,\n      unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_locked-lock-waiter-lock_upgrade\",\n      upgrade_locked_only,\n      lock,\n      lock_upgrade,\n      false,\n      unlock_upgrade);\n  // since the mutex prioritizes the writer, lock_shared() would be blocked by\n  // the lock() even when the mutex is only upgrade locked\n  cases.emplace_back(\n      \"upgrade_locked-lock-waiter-lock_shared\",\n      upgrade_locked_only,\n      lock,\n      lock_shared,\n      false,\n      unlock_upgrade);\n  // this is the case where lock transfer skips the waiter line to avoid\n  // deadlock\n  cases.emplace_back(\n      \"upgrade_locked-lock-waiter-unlock_upgrade_and_lock\",\n      upgrade_locked_only,\n      lock,\n      unlock_upgrade_and_lock,\n      true,\n      std::nullopt);\n\n  cases.emplace_back(\n      \"upgrade_locked-lock_upgrade-waiter-lock\",\n      upgrade_locked_only,\n      lock_upgrade,\n      lock,\n      false,\n      unlock_upgrade);\n  // the mutex prioritizes writers but read locks can still be granted as long\n  // as there is no writers waiting\n  // granting the read lock here will not starve the waiting upgrader, as they\n  // are not contending anyway\n  cases.emplace_back(\n      \"upgrade_locked-lock_upgrade-waiter-lock_shared\",\n      upgrade_locked_only,\n      lock_upgrade,\n      lock_shared,\n      true,\n      unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_locked-lock_upgrade-waiter-lock_upgrade\",\n      upgrade_locked_only,\n      lock_upgrade,\n      lock_upgrade,\n      false,\n      unlock_upgrade);\n  // this is the case where lock transfer skips the waiter line to avoid\n  // deadlock\n  cases.emplace_back(\n      \"upgrade_locked-lock_upgrade-waiter-unlock_upgrade_and_lock\",\n      upgrade_locked_only,\n      lock_upgrade,\n      unlock_upgrade_and_lock,\n      true,\n      std::nullopt);\n\n  // upgrade and shared locked with no one waiting\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-no-waiter-lock-0\",\n      upgrade_and_shared_locked,\n      std::nullopt,\n      lock,\n      false,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-no-waiter-lock-1\",\n      upgrade_and_shared_locked,\n      std::nullopt,\n      lock,\n      false,\n      unlock_shared_and_unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-no-waiter-lock_upgrade-0\",\n      upgrade_and_shared_locked,\n      std::nullopt,\n      lock_upgrade,\n      false,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-no-waiter-lock_upgrade-1\",\n      upgrade_and_shared_locked,\n      std::nullopt,\n      lock_upgrade,\n      false,\n      unlock_shared_and_unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-no-waiter-lock_shared-0\",\n      upgrade_and_shared_locked,\n      std::nullopt,\n      lock_shared,\n      true,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-no-waiter-lock_shared-1\",\n      upgrade_and_shared_locked,\n      std::nullopt,\n      lock_shared,\n      true,\n      unlock_shared_and_unlock_upgrade);\n\n  // the lock transfer needs to wait for the readers to drain\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-no-waiter-unlock_upgrade_and_lock\",\n      upgrade_and_shared_locked,\n      std::nullopt,\n      unlock_upgrade_and_lock,\n      false,\n      unlock_shared);\n\n  // upgrade and shared locked with waiters\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock-waiter-lock-0\",\n      upgrade_and_shared_locked,\n      lock,\n      lock,\n      false,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock-waiter-lock-1\",\n      upgrade_and_shared_locked,\n      lock,\n      lock,\n      false,\n      unlock_shared_and_unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock-waiter-lock_upgrade-0\",\n      upgrade_and_shared_locked,\n      lock,\n      lock_upgrade,\n      false,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock-waiter-lock_upgrade-1\",\n      upgrade_and_shared_locked,\n      lock,\n      lock_upgrade,\n      false,\n      unlock_shared_and_unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock-waiter-lock_shared-0\",\n      upgrade_and_shared_locked,\n      lock,\n      lock_shared,\n      false,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock-waiter-lock_shared-1\",\n      upgrade_and_shared_locked,\n      lock,\n      lock_shared,\n      false,\n      unlock_shared_and_unlock_upgrade);\n  // this is the case where lock transfer skips the waiter line to avoid\n  // deadlock once the reader is drained, the lock transfer will succeed first\n  // before the lock()\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock-waiter-unlock_upgrade_and_lock\",\n      upgrade_and_shared_locked,\n      lock,\n      unlock_upgrade_and_lock,\n      false,\n      unlock_shared);\n\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock_upgrade-waiter-lock-0\",\n      upgrade_and_shared_locked,\n      lock_upgrade,\n      lock,\n      false,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock_upgrade-waiter-lock-1\",\n      upgrade_and_shared_locked,\n      lock_upgrade,\n      lock,\n      false,\n      unlock_shared_and_unlock_upgrade);\n  // the mutex prioritizes writers but read locks can still be granted as long\n  // as there is no writers waiting\n  // granting the read lock here will not starve the waiting upgrader, as they\n  // are not contending anyway\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock_upgrade-waiter-lock_shared-0\",\n      upgrade_and_shared_locked,\n      lock_upgrade,\n      lock_shared,\n      true,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock_upgrade-waiter-lock_shared-1\",\n      upgrade_and_shared_locked,\n      lock_upgrade,\n      lock_shared,\n      true,\n      unlock_shared_and_unlock_upgrade);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock_upgrade-waiter-lock_upgrade-0\",\n      upgrade_and_shared_locked,\n      lock_upgrade,\n      lock_upgrade,\n      false,\n      unlock_upgrade_and_unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock_upgrade-waiter-lock_upgrade-1\",\n      upgrade_and_shared_locked,\n      lock_upgrade,\n      lock_upgrade,\n      false,\n      unlock_shared_and_unlock_upgrade);\n  // this is the case where lock transfer skips the waiter line to avoid\n  // deadlock once the reader is drained, the lock transfer will succeed\n  // first before the lock_upgrade()\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-lock_upgrade-waiter-unlock_upgrade_and_lock\",\n      upgrade_and_shared_locked,\n      lock_upgrade,\n      unlock_upgrade_and_lock,\n      false,\n      unlock_shared);\n  // tests when there is a lock transition waiter\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-unlock_upgrade_and_lock-waiter-lock\",\n      upgrade_and_shared_locked,\n      unlock_upgrade_and_lock,\n      lock,\n      false,\n      unlock_shared);\n  // this is the case where the mutex prioritizes draining the readers when\n  // there is pending lock transfer and not granting new reader locks even when\n  // it could\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-unlock_upgrade_and_lock-waiter-lock_shared\",\n      upgrade_and_shared_locked,\n      unlock_upgrade_and_lock,\n      lock_shared,\n      false,\n      unlock_shared);\n  cases.emplace_back(\n      \"upgrade_and_shared_locked-unlock_upgrade_and_lock-waiter-lock_upgrade\",\n      upgrade_and_shared_locked,\n      unlock_upgrade_and_lock,\n      lock_upgrade,\n      false,\n      unlock_shared);\n\n  for (auto& [testName, init, waiterSetup, action, expectDone, cleanup] :\n       cases) {\n    SCOPED_TRACE(testName);\n    coro::SharedMutex m;\n    ManualExecutor executor;\n    init(m);\n    std::optional<folly::SemiFuture<folly::Unit>> waiterSemi;\n    if (waiterSetup) {\n      waiterSemi = co_withExecutor(&executor, (*waiterSetup)(m)).start();\n    }\n    executor.drain();\n    if (waiterSemi) {\n      ASSERT_FALSE(waiterSemi->isReady()); // the waiter is supposed to wait\n    }\n    auto semi = co_withExecutor(&executor, action(m)).start();\n    executor.drain();\n    ASSERT_EQ(semi.isReady(), expectDone);\n    if (expectDone) {\n      ASSERT_TRUE(semi.hasValue());\n    }\n    if (cleanup) {\n      (*cleanup)(m);\n    }\n    executor.drain();\n    ASSERT_TRUE(semi.isReady());\n    ASSERT_TRUE(semi.hasValue());\n    if (waiterSemi) {\n      ASSERT_TRUE(waiterSemi->isReady());\n      ASSERT_TRUE(waiterSemi->hasValue());\n    }\n  }\n}\n\nTEST_F(SharedMutexTest, ManualLockAllStateTransitions) {\n  // these are all the async operations that can happen to the mutex\n  auto lock = [](coro::SharedMutex& m) -> coro::Task<void> {\n    co_await m.co_lock();\n    m.unlock();\n  };\n  auto lock_upgrade = [](coro::SharedMutex& m) -> coro::Task<void> {\n    co_await m.co_lock_upgrade();\n    m.unlock_upgrade();\n  };\n  auto lock_shared = [](coro::SharedMutex& m) -> coro::Task<void> {\n    co_await m.co_lock_shared();\n    m.unlock_shared();\n  };\n  auto unlock_upgrade_and_lock = [](coro::SharedMutex& m) -> coro::Task<void> {\n    co_await m.co_unlock_upgrade_and_lock();\n    m.unlock();\n  };\n\n  testAllStateTransitions(\n      lock, lock_upgrade, lock_shared, unlock_upgrade_and_lock);\n}\n\nTEST_F(SharedMutexTest, ScopedLockAsync) {\n  coro::SharedMutex mutex;\n  int value = 0;\n\n  auto makeReaderTask = [&](coro::Baton& b) -> coro::Task<int> {\n    auto lock = co_await mutex.co_scoped_lock_shared();\n    co_await b;\n    co_return value;\n  };\n\n  auto makeWriterTask = [&](coro::Baton& b) -> coro::Task<void> {\n    auto lock = co_await mutex.co_scoped_lock();\n    co_await b;\n    value += 1;\n  };\n\n  ManualExecutor executor;\n\n  {\n    coro::Baton b1;\n    coro::Baton b2;\n    coro::Baton b3;\n    coro::Baton b4;\n    coro::Baton b5;\n\n    auto r1 = co_withExecutor(&executor, makeReaderTask(b1)).start();\n    auto r2 = co_withExecutor(&executor, makeReaderTask(b2)).start();\n    auto w1 = co_withExecutor(&executor, makeWriterTask(b3)).start();\n    auto w2 = co_withExecutor(&executor, makeWriterTask(b4)).start();\n    auto r3 = co_withExecutor(&executor, makeReaderTask(b5)).start();\n\n    b1.post();\n    executor.drain();\n    CHECK_EQ(0, std::move(r1).get());\n\n    b2.post();\n    executor.drain();\n    CHECK_EQ(0, std::move(r2).get());\n\n    b3.post();\n    executor.drain();\n    CHECK_EQ(1, value);\n\n    b4.post();\n    executor.drain();\n    CHECK_EQ(2, value);\n\n    // This reader should have had to wait for the prior two write locks\n    // to complete before it acquired the read-lock.\n    b5.post();\n    executor.drain();\n    CHECK_EQ(2, std::move(r3).get());\n  }\n}\n\nTEST_F(SharedMutexTest, MultipleWaiters) {\n  coro::SharedMutex mutex;\n  auto writer = [&]() -> coro::Task<void> { co_await mutex.co_lock(); };\n  auto upgrader = [&]() -> coro::Task<void> {\n    co_await mutex.co_lock_upgrade();\n  };\n  auto reader = [&]() -> coro::Task<void> { co_await mutex.co_lock_shared(); };\n\n  {\n    // test that the long reader scan does not go pass a waiting writer\n    ManualExecutor executor;\n    ASSERT_TRUE(mutex.try_lock());\n    // U1 S1 U2 S2 W S3\n    // expect U1 S1 S2 to be unblocked on unlock\n    auto u1 = co_withExecutor(&executor, upgrader()).start();\n    auto s1 = co_withExecutor(&executor, reader()).start();\n    auto u2 = co_withExecutor(&executor, upgrader()).start();\n    auto s2 = co_withExecutor(&executor, reader()).start();\n    auto w = co_withExecutor(&executor, writer()).start();\n    auto s3 = co_withExecutor(&executor, reader()).start();\n    executor.drain();\n    EXPECT_FALSE(u1.isReady());\n    EXPECT_FALSE(s1.isReady());\n    EXPECT_FALSE(s2.isReady());\n    mutex.unlock();\n    executor.drain();\n    EXPECT_TRUE(u1.hasValue());\n    EXPECT_TRUE(s1.hasValue());\n    EXPECT_TRUE(s2.hasValue());\n\n    EXPECT_FALSE(u2.isReady());\n    EXPECT_FALSE(w.isReady());\n    EXPECT_FALSE(s3.isReady());\n\n    mutex.unlock_upgrade();\n    mutex.unlock_shared();\n    mutex.unlock_shared();\n    executor.drain();\n    EXPECT_TRUE(u2.hasValue());\n    EXPECT_FALSE(w.isReady());\n    EXPECT_FALSE(s3.isReady());\n\n    mutex.unlock_upgrade();\n    executor.drain();\n    EXPECT_TRUE(w.hasValue());\n    EXPECT_FALSE(s3.isReady());\n\n    mutex.unlock();\n    executor.drain();\n    EXPECT_TRUE(s3.hasValue());\n    mutex.unlock_shared();\n  }\n\n  {\n    // test that resuming a tail reader via long scan is handled correct\n    // this is to ensure the waiter tail pointer is updated correctly\n    ManualExecutor executor;\n    ASSERT_TRUE(mutex.try_lock());\n    // U1 U2 S\n    auto u1 = co_withExecutor(&executor, upgrader()).start();\n    auto u2 = co_withExecutor(&executor, upgrader()).start();\n    auto s = co_withExecutor(&executor, reader()).start();\n    executor.drain();\n    EXPECT_FALSE(u1.isReady());\n    EXPECT_FALSE(u2.isReady());\n    EXPECT_FALSE(s.isReady());\n    mutex.unlock();\n    executor.drain();\n    EXPECT_TRUE(u1.hasValue());\n    EXPECT_TRUE(s.hasValue());\n    EXPECT_FALSE(u2.isReady());\n\n    // u3 should be enqueued behind u2 which is the new tail\n    auto u3 = co_withExecutor(&executor, upgrader()).start();\n    executor.drain();\n    EXPECT_FALSE(u3.isReady());\n\n    mutex.unlock_upgrade();\n    executor.drain();\n    EXPECT_TRUE(u2.hasValue());\n    EXPECT_FALSE(u3.isReady());\n\n    mutex.unlock_upgrade();\n    executor.drain();\n    EXPECT_TRUE(u3.hasValue());\n\n    mutex.unlock_upgrade();\n    mutex.unlock_shared();\n  }\n}\n\nTEST_F(SharedMutexTest, ScopedLockAllStateTransitions) {\n  // these are all the async operations that can happen to the mutex\n  auto lock = [](coro::SharedMutex& m) -> coro::Task<void> {\n    auto l = co_await m.co_scoped_lock();\n  };\n  auto lock_upgrade = [](coro::SharedMutex& m) -> coro::Task<void> {\n    auto l = co_await m.co_scoped_lock_upgrade();\n  };\n  auto lock_shared = [](coro::SharedMutex& m) -> coro::Task<void> {\n    auto l = co_await m.co_scoped_lock_shared();\n  };\n  auto unlock_upgrade_and_lock = [](coro::SharedMutex& m) -> coro::Task<void> {\n    auto l = co_await m.co_scoped_unlock_upgrade_and_lock();\n  };\n\n  testAllStateTransitions(\n      lock, lock_upgrade, lock_shared, unlock_upgrade_and_lock);\n}\n\nTEST_F(SharedMutexTest, AsyncLockTransition) {\n  coro::SharedMutex mutex;\n  int value = 0;\n  auto conditionalWrite = [&]() -> coro::Task<void> {\n    auto uLock = co_await mutex.co_scoped_lock_upgrade();\n    if (value == 0) {\n      auto wLock = co_await folly::coro::co_transition_lock(uLock);\n      value += 1;\n    }\n  };\n\n  auto read = [&]() -> coro::Task<int> {\n    auto rLock = co_await mutex.co_scoped_lock_shared();\n    int copyValue = value;\n    co_return copyValue;\n  };\n\n  {\n    ManualExecutor executor;\n    value = 0;\n    auto w = co_withExecutor(&executor, conditionalWrite()).start();\n    auto r = co_withExecutor(&executor, read()).start();\n    executor.drain();\n    EXPECT_EQ(std::move(r).get(), 1);\n  }\n\n  {\n    ManualExecutor executor;\n    value = 0;\n    auto r1 = co_withExecutor(&executor, read()).start();\n    auto w = co_withExecutor(&executor, conditionalWrite()).start();\n    auto r2 = co_withExecutor(&executor, read()).start();\n    executor.drain();\n    EXPECT_EQ(std::move(r1).get(), 0);\n    EXPECT_EQ(std::move(r2).get(), 1);\n  }\n\n  {\n    ManualExecutor executor;\n    value = 0;\n    auto r1 = co_withExecutor(&executor, read()).start();\n    auto w1 = co_withExecutor(&executor, conditionalWrite()).start();\n    auto w2 = co_withExecutor(&executor, conditionalWrite()).start();\n    auto r2 = co_withExecutor(&executor, read()).start();\n    executor.drain();\n    EXPECT_EQ(std::move(r1).get(), 0);\n    EXPECT_EQ(std::move(r2).get(), 1);\n  }\n}\n\nTEST_F(SharedMutexTest, ThreadSafety) {\n  // Spin up a thread-pool with 3 threads and 6 coroutines\n  // (2 writers, 4 readers) that are constantly spinning in a loop reading\n  // and modifying some shared state.\n\n  CPUThreadPoolExecutor threadPool{\n      3, std::make_shared<NamedThreadFactory>(\"TestThreadPool\")};\n\n  static constexpr int iterationCount = 100'000;\n\n  coro::SharedMutex mutex;\n  int value1 = 0;\n  int value2 = 0;\n\n  auto makeWriterTask = [&]() -> coro::Task<void> {\n    for (int i = 0; i < iterationCount; ++i) {\n      auto lock = co_await mutex.co_scoped_lock();\n      ++value1;\n      ++value2;\n    }\n  };\n\n  auto makeReaderTask = [&]() -> coro::Task<void> {\n    for (int i = 0; i < iterationCount; ++i) {\n      auto lock = co_await mutex.co_scoped_lock_shared();\n      CHECK_EQ(value1, value2);\n    }\n  };\n\n  auto w1 = co_withExecutor(&threadPool, makeWriterTask()).start();\n  auto w2 = co_withExecutor(&threadPool, makeWriterTask()).start();\n  auto r1 = co_withExecutor(&threadPool, makeReaderTask()).start();\n  auto r2 = co_withExecutor(&threadPool, makeReaderTask()).start();\n  auto r3 = co_withExecutor(&threadPool, makeReaderTask()).start();\n  auto r4 = co_withExecutor(&threadPool, makeReaderTask()).start();\n\n  std::move(w1).get();\n  std::move(w2).get();\n  std::move(r1).get();\n  std::move(r2).get();\n  std::move(r3).get();\n  std::move(r4).get();\n\n  CHECK_EQ(value1, 2 * iterationCount);\n  CHECK_EQ(value2, 2 * iterationCount);\n}\n\nTEST_F(SharedMutexTest, StressTest) {\n  // create a large number of coroutines running on an executor with\n  // 10 threads, constantly trying to read and write shared state\n\n  coro::SharedMutex mutex;\n  int value1 = 0;\n  int value2 = 0;\n  folly::relaxed_atomic<bool> reachedTarget{false};\n  folly::relaxed_atomic<size_t> earlyExists{0};\n  constexpr int target = 100'000;\n\n  auto incrementIfEven = [&]() -> coro::Task<void> {\n    {\n      auto rLock = co_await mutex.co_scoped_lock_shared();\n      if (value1 % 2 != 0) {\n        ++earlyExists;\n        co_return;\n      }\n    }\n    auto uLock = co_await mutex.co_scoped_lock_upgrade();\n    if (value1 % 2 != 0) {\n      ++earlyExists;\n      co_return;\n    }\n    auto wLock = co_await folly::coro::co_transition_lock(uLock);\n    ++value1;\n    ++value2;\n  };\n  auto incrementIfOdd = [&]() -> coro::Task<void> {\n    {\n      auto rLock = co_await mutex.co_scoped_lock_shared();\n      if (value1 % 2 == 0) {\n        ++earlyExists;\n        co_return;\n      }\n    }\n    auto uLock = co_await mutex.co_scoped_lock_upgrade();\n    if (value1 % 2 == 0) {\n      ++earlyExists;\n      co_return;\n    }\n    auto wLock = co_await folly::coro::co_transition_lock(uLock);\n    ++value1;\n    ++value2;\n  };\n  auto read = [&]() -> coro::Task<int> {\n    auto rLock = co_await mutex.co_scoped_lock_shared();\n    EXPECT_EQ(value1, value2);\n    co_return value1;\n  };\n  auto check = [&]() -> coro::Task<void> {\n    auto rLock = co_await mutex.co_scoped_lock_shared();\n    if (value1 >= target) {\n      reachedTarget = true;\n    }\n    EXPECT_EQ(value1, value2);\n  };\n\n  CPUThreadPoolExecutor executor{\n      10, std::make_shared<NamedThreadFactory>(\"TestThreadPool\")};\n\n  size_t writeTaskCnt = 0;\n  folly::coro::AsyncScope scope;\n  while (!reachedTarget) {\n    writeTaskCnt += 2;\n    scope.add(co_withExecutor(&executor, check()));\n    scope.add(co_withExecutor(&executor, incrementIfOdd()));\n    scope.add(co_withExecutor(&executor, check()));\n    scope.add(co_withExecutor(&executor, incrementIfEven()));\n    scope.add(co_withExecutor(&executor, check()));\n  }\n  folly::coro::blockingWait(co_withExecutor(&executor, scope.joinAsync()));\n\n  // final read\n  int finalValue =\n      folly::coro::blockingWait(co_withExecutor(&executor, read()));\n\n  EXPECT_GE(finalValue, target);\n  EXPECT_EQ(writeTaskCnt, finalValue + earlyExists);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/SharedPromiseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utility>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/DetachOnCancel.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/SharedPromise.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nclass BlockingWaitWaitInterface {\n public:\n  std::string waitAndGetValue(folly::coro::Future<std::string> future) {\n    return folly::coro::blocking_wait(std::move(future));\n  }\n};\n\nclass CPUThreadPoolWaitInterface {\n public:\n  std::string waitAndGetValue(folly::coro::Future<std::string> future) {\n    return co_withExecutor(\n               cpuThreadPoolExecutor_.get(), coGet(std::move(future)))\n        .start()\n        .get();\n  }\n\n private:\n  folly::coro::Task<std::string> coGet(\n      folly::coro::Future<std::string> future) {\n    co_return co_await std::move(future);\n  }\n\n  std::unique_ptr<folly::CPUThreadPoolExecutor> cpuThreadPoolExecutor_{\n      std::make_unique<folly::CPUThreadPoolExecutor>(1)};\n};\n\ntemplate <typename WaitInterface>\nclass ValueInterface : public WaitInterface {\n public:\n  void set(std::string value, SharedPromise<std::string>& promise) {\n    promise.setValue(std::move(value));\n  }\n\n  std::string get(folly::coro::Future<std::string> future) {\n    return this->waitAndGetValue(std::move(future));\n  }\n};\n\ntemplate <typename WaitInterface>\nclass ExceptionInterface : public WaitInterface {\n  class StringException : public std::exception {\n   public:\n    explicit StringException(std::string string) : string_{string} {}\n    std::string get() { return string_; }\n\n   private:\n    std::string string_;\n  };\n\n public:\n  void set(std::string value, SharedPromise<std::string>& promise) {\n    promise.setException(StringException{value});\n  }\n\n  std::string get(folly::coro::Future<std::string> future) {\n    try {\n      this->waitAndGetValue(std::move(future));\n    } catch (StringException& exception) {\n      return exception.get();\n    }\n\n    CHECK(false) << \"Expected value in exception, \"\n                 << \" but didn't get any exception\";\n  }\n};\n\ntemplate <typename TestInterface>\nclass SharedPromiseTest : public ::testing::Test, public TestInterface {};\n\nusing TestTypes = ::testing::Types<\n    ValueInterface<BlockingWaitWaitInterface>,\n    ValueInterface<CPUThreadPoolWaitInterface>,\n    ExceptionInterface<BlockingWaitWaitInterface>,\n    ExceptionInterface<CPUThreadPoolWaitInterface>>;\n\nTYPED_TEST_SUITE(SharedPromiseTest, TestTypes);\n\nTYPED_TEST(SharedPromiseTest, Basic) {\n  auto promise = SharedPromise<std::string>{};\n  auto future = promise.getFuture();\n\n  this->set(\"ynwa\", promise);\n  EXPECT_EQ(\"ynwa\", this->get(std::move(future)));\n}\n\nTYPED_TEST(SharedPromiseTest, MultipleFutures) {\n  auto promise = SharedPromise<std::string>{};\n  auto futures = std::vector<folly::coro::Future<std::string>>{};\n  for (auto i = 0; i < 10; ++i) {\n    futures.push_back(promise.getFuture());\n  }\n\n  this->set(\"liverpool_best_club\", promise);\n  for (auto& future : futures) {\n    EXPECT_EQ(\"liverpool_best_club\", this->get(std::move(future)));\n  }\n}\n\nTYPED_TEST(SharedPromiseTest, BrokenPromise) {\n  auto testFutures = []() {\n    auto promise = SharedPromise<std::string>{};\n\n    auto futures = std::vector<folly::coro::Future<std::string>>{};\n    for (auto i = 0; i < 10; ++i) {\n      futures.push_back(promise.getFuture());\n    }\n\n    return futures;\n  }();\n\n  for (auto& future : testFutures) {\n    EXPECT_THROW(this->get(std::move(future)), folly::BrokenPromise);\n  }\n}\n\nTYPED_TEST(SharedPromiseTest, PromiseAlreadySatisfied) {\n  {\n    auto promise = SharedPromise<std::string>{};\n    this->set(\"liverpool_epl_champions\", promise);\n\n    EXPECT_THROW(this->set(\"ynwa\", promise), folly::PromiseAlreadySatisfied);\n  }\n\n  {\n    auto promise = SharedPromise<std::string>{};\n    auto futures = std::vector<folly::coro::Future<std::string>>{};\n    for (auto i = 0; i < 10; ++i) {\n      futures.push_back(promise.getFuture());\n    }\n\n    this->set(\"ynwa_1\", promise);\n    EXPECT_THROW(this->set(\"ynwa_2\", promise), folly::PromiseAlreadySatisfied);\n\n    for (auto& future : futures) {\n      EXPECT_EQ(\"ynwa_1\", this->get(std::move(future)));\n    }\n  }\n}\n\nTYPED_TEST(SharedPromiseTest, FutureAfterFulfilled) {\n  auto promise = SharedPromise<std::string>{};\n  auto futures = std::vector<folly::coro::Future<std::string>>{};\n  for (auto i = 0; i < 10; ++i) {\n    futures.push_back(promise.getFuture());\n  }\n\n  this->set(\"ynwa\", promise);\n  for (auto& future : futures) {\n    EXPECT_EQ(this->get(std::move(future)), \"ynwa\");\n  }\n\n  EXPECT_EQ(this->get(promise.getFuture()), \"ynwa\");\n}\n\nTYPED_TEST(SharedPromiseTest, PostMoveConstruction) {\n  auto promise = SharedPromise<std::string>{};\n  auto future = promise.getFuture();\n\n  this->set(\"ynwa_1\", promise);\n  auto anotherPromise = std::move(promise);\n\n  EXPECT_EQ(this->get(std::move(future)), \"ynwa_1\");\n  EXPECT_EQ(this->get(anotherPromise.getFuture()), \"ynwa_1\");\n\n  // @lint-ignore CLANGTIDY\n  auto anotherFuture = promise.getFuture();\n  this->set(\"ynwa_2\", promise);\n  EXPECT_EQ(this->get(std::move(anotherFuture)), \"ynwa_2\");\n  EXPECT_EQ(this->get(promise.getFuture()), \"ynwa_2\");\n}\n\nTYPED_TEST(SharedPromiseTest, PostMoveAssignment) {\n  auto promise = SharedPromise<std::string>{};\n  auto future = promise.getFuture();\n\n  this->set(\"ynwa_1\", promise);\n  auto anotherPromise = SharedPromise<std::string>{};\n  anotherPromise = std::move(promise);\n\n  EXPECT_EQ(this->get(std::move(future)), \"ynwa_1\");\n  EXPECT_EQ(this->get(anotherPromise.getFuture()), \"ynwa_1\");\n\n  // @lint-ignore CLANGTIDY\n  auto anotherFuture = promise.getFuture();\n  this->set(\"ynwa_2\", promise);\n  EXPECT_EQ(this->get(std::move(anotherFuture)), \"ynwa_2\");\n  EXPECT_EQ(this->get(promise.getFuture()), \"ynwa_2\");\n}\n\nnamespace {\nclass FallibleExecutor : public folly::Executor {\n public:\n  explicit FallibleExecutor(std::unique_ptr<folly::Executor> executor)\n      : executor_{std::move(executor)} {}\n\n  void add(folly::Function<void()> function) override {\n    if (!failed_.load()) {\n      executor_->add(std::move(function));\n      return;\n    }\n\n    CHECK(false) << \"Cannot add to FallibleExecutor after it has entered the \"\n                 << \"fail state.\";\n  }\n\n  void fail() { failed_.store(true); }\n\n private:\n  std::unique_ptr<folly::Executor> executor_;\n  std::atomic<bool> failed_{false};\n};\n} // namespace\n\nTYPED_TEST(SharedPromiseTest, CleanlyCancellableWait) {\n  // This test makes sure that there are no detached tasks lying around on the\n  // executor _after_ the cancellation exception has propagated to the user.  In\n  // particular, this bug was found in the falcon codebase and fixed in\n  // D39738899.\n  //\n  // folly::coro::Promise and folly::coro::Future should natively support\n  // cancellable waits.  We test for this assertion here.\n  //\n  // If the promise and future types were to be switched to folly::Promise and\n  // folly::Future, this test would never return because cancellation would\n  // never get triggered.  Adding detachOnCancel() leaves around a detached\n  // coroutine on the executor, which we put in a \"failed\" state, similar to\n  // folly::Future's internal WaitExecutor.  So using detachOnCancel() will\n  // cause the test to fail as well\n  auto promise = SharedPromise<std::string>{};\n  auto future = promise.getFuture();\n  auto baton = folly::Baton<>{};\n\n  auto task = folly::coro::co_invoke([&]() -> folly::coro::Task<std::string> {\n    co_return co_await std::move(future);\n  });\n\n  auto cpu = std::make_unique<folly::CPUThreadPoolExecutor>(1);\n  auto fallibleExecutor = std::make_unique<FallibleExecutor>(std::move(cpu));\n  auto cancellationSource = folly::CancellationSource{};\n  auto cancellationToken = cancellationSource.getToken();\n\n  auto started =\n      co_withExecutor(\n          fallibleExecutor.get(),\n          folly::coro::co_withCancellation(cancellationToken, std::move(task)))\n          .start();\n\n  cancellationSource.requestCancellation();\n  EXPECT_THROW(std::move(started).get(), folly::OperationCancelled);\n\n  // make the executor go in a failed state and set the promise, so if the\n  // future was detached, we would get the detached coroutine to run on the\n  // executor\n  fallibleExecutor->fail();\n  promise.setValue(\"ynwa\");\n}\n\nTYPED_TEST(SharedPromiseTest, NoHeapAllocation) {\n  char allocBuffer[1024];\n  auto promise = new (allocBuffer) SharedPromise<std::string>{};\n  promise->setValue(\"foo\");\n  // If SharedPromise has heap allocation, it would trigger ASAN failures\n}\n\nTEST(SharedPromiseTest, BasicVoid) {\n  {\n    auto promise = SharedPromise<void>{};\n    auto future = promise.getFuture();\n\n    promise.setValue();\n    blocking_wait(std::move(future));\n  }\n\n  {\n    auto promise = SharedPromise<void>{};\n    promise.setValue();\n    blocking_wait(promise.getFuture());\n  }\n}\n\nCO_TEST(SharedPromiseTest, Swap) {\n  SharedPromise<int> p1, p2;\n  p1.setValue(42);\n  p2.setValue(43);\n  using std::swap;\n  swap(p1, p2);\n  EXPECT_EQ(co_await p1.getFuture(), 43);\n  EXPECT_EQ(co_await p2.getFuture(), 42);\n}\n\nCO_TEST(SharedPromiseTest, MoveFrom) {\n  SharedPromise<int> p1;\n  p1.setValue(42);\n  SharedPromise<int> p2;\n  p2.setValue(43);\n  p2 = std::move(p1);\n  EXPECT_EQ(co_await p2.getFuture(), 42);\n  EXPECT_FALSE(p1.isFulfilled());\n}\n\nCO_TEST(SharedPromiseTest, SelfMove) {\n  SharedPromise<int> p;\n  p.setValue(1);\n  auto& alias = p; // defeat -Wself-move\n  p = std::move(alias);\n  CO_ASSERT_TRUE(p.isFulfilled());\n  EXPECT_EQ(co_await p.getFuture(), 1);\n}\n\nTEST(SharedPromiseTest, Exchange) {\n  SharedPromise<void> p1;\n  using std::exchange;\n  SharedPromise<void> p2 = exchange(p1, {});\n}\n\nTEST(SharedPromiseTest, Poll) {\n  SharedPromise<std::string> promise;\n  EXPECT_FALSE(promise.poll().has_value());\n  const std::string value = \"ynwa\";\n  promise.setValue(value);\n  const auto result = promise.poll();\n  ASSERT_TRUE(result.has_value() && result->hasValue());\n  EXPECT_EQ(value, result->value());\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/SleepTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/futures/ManualTimekeeper.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nstruct SleepTest : testing::Test {};\n\nCO_TEST_F(SleepTest, Basic) {\n  constexpr auto kSleepDuration{std::chrono::seconds(1)};\n\n  folly::coro::CancellableAsyncScope asyncScope;\n  folly::ManualTimekeeper manualTimekeeper;\n\n  size_t sleepCompletedCount = 0;\n\n  auto func = [&]() -> folly::coro::Task<> {\n    while (true) {\n      co_await folly::coro::sleep(kSleepDuration, &manualTimekeeper);\n      ++sleepCompletedCount;\n    }\n  };\n\n  asyncScope.add(\n      co_withExecutor(co_await folly::coro::co_current_executor, func()));\n  co_await folly::coro::co_reschedule_on_current_executor;\n  EXPECT_EQ(sleepCompletedCount, 0);\n\n  manualTimekeeper.advance(2 * kSleepDuration);\n  co_await folly::coro::co_reschedule_on_current_executor;\n  EXPECT_EQ(sleepCompletedCount, 1);\n\n  manualTimekeeper.advance(2 * kSleepDuration);\n  co_await folly::coro::co_reschedule_on_current_executor;\n  EXPECT_EQ(sleepCompletedCount, 2);\n\n  manualTimekeeper.advance(2 * kSleepDuration);\n  co_await asyncScope.cancelAndJoinAsync();\n  EXPECT_EQ(sleepCompletedCount, 2);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/SmallUnboundedQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/SmallUnboundedQueue.h>\n\n#include <folly/portability/GTest.h>\n\n#include <string>\n#include <thread>\n\n#if FOLLY_HAS_COROUTINES\n\nTEST(SmallUnboundedQueueTest, EnqueueDeque) {\n  folly::coro::SmallUnboundedQueue<std::string, true, true> queue;\n  constexpr auto val = \"a string\";\n  std::string val1 = val;\n  queue.enqueue(val1);\n  queue.enqueue(std::move(val1));\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int i = 0; i < 2; ++i) {\n      auto val2 = co_await queue.dequeue();\n      EXPECT_EQ(val2, val);\n    }\n  }());\n}\n\nTEST(SmallUnboundedQueueTest, DequeueWhileBlocking) {\n  folly::coro::SmallUnboundedQueue<int> queue;\n  folly::ManualExecutor ex;\n\n  auto fut = co_withExecutor(&ex, queue.dequeue()).start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n\n  queue.enqueue(0);\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_EQ(std::move(fut).get(), 0);\n}\n\nTEST(SmallUnboundedQueueTest, EnqueueDequeMultiProducer) {\n  folly::coro::SmallUnboundedQueue<int, false, true> queue;\n  std::atomic<int> i = 0;\n\n  std::vector<std::thread> enqueuers;\n  for (int n = 0; n < 5; ++n) {\n    enqueuers.emplace_back([&] {\n      while (true) {\n        int next = i++;\n        if (next >= 100) {\n          break;\n        }\n        queue.enqueue(next);\n      }\n    });\n  }\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int n = 0; n < 100; ++n) {\n      co_await queue.dequeue();\n    }\n  }());\n\n  for (int n = 0; n < 5; ++n) {\n    enqueuers[n].join();\n  }\n}\n\nTEST(SmallUnboundedQueueTest, EnqueueDequeMultiConsumer) {\n  folly::coro::SmallUnboundedQueue<int, true, false> queue;\n  std::atomic<int> seen = 0;\n\n  std::vector<std::thread> dequeuers;\n  for (int n = 0; n < 5; ++n) {\n    dequeuers.emplace_back([&] {\n      folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n        while (++seen <= 100) {\n          co_await queue.dequeue();\n        }\n      }());\n    });\n  }\n\n  for (int n = 0; n < 100; ++n) {\n    queue.enqueue(n);\n  }\n  for (int n = 0; n < 5; ++n) {\n    dequeuers[n].join();\n  }\n}\n\nTEST(SmallUnboundedQueueTest, EnqueueDequeMPMC) {\n  folly::coro::SmallUnboundedQueue<int, false, false> queue;\n  std::atomic<int> seen = 0, i = 0;\n\n  std::vector<std::thread> enqueuers;\n  for (int n = 0; n < 5; ++n) {\n    enqueuers.emplace_back([&] {\n      while (true) {\n        int next = i++;\n        if (next >= 100) {\n          break;\n        }\n        queue.enqueue(next);\n      }\n    });\n  }\n\n  std::vector<std::thread> dequeuers;\n  for (int n = 0; n < 5; ++n) {\n    dequeuers.emplace_back([&] {\n      folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n        while (++seen <= 100) {\n          co_await queue.dequeue();\n        }\n      }());\n    });\n  }\n\n  for (int n = 0; n < 5; ++n) {\n    enqueuers[n].join();\n  }\n  for (int n = 0; n < 5; ++n) {\n    dequeuers[n].join();\n  }\n}\n\nTEST(SmallUnboundedQueueTest, CancelledDequeueThrowsOperationCancelled) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::SmallUnboundedQueue<int> queue;\n    folly::CancellationSource cancelSource;\n\n    co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<void> {\n          EXPECT_THROW(\n              (co_await folly::coro::co_withCancellation(\n                  cancelSource.getToken(), queue.dequeue())),\n              folly::OperationCancelled);\n        }(),\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          cancelSource.requestCancellation();\n        }());\n  }());\n}\n\nTEST(\n    SmallUnboundedQueueTest,\n    CancelledDequeueCompletesNormallyIfAnItemIsAvailable) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::SmallUnboundedQueue<int> queue;\n    folly::CancellationSource cancelSource;\n    cancelSource.requestCancellation();\n\n    queue.enqueue(123);\n\n    int result = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), queue.dequeue());\n    EXPECT_EQ(123, result);\n  }());\n}\n#endif\n"
  },
  {
    "path": "folly/coro/test/SuspendedStackTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <unordered_set>\n#include <folly/coro/AsyncStack.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/Task.h>\n#include <folly/debugging/symbolizer/Symbolizer.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GTest.h>\n\nconstexpr size_t coroDepth = 5;\n\n#if FOLLY_HAS_COROUTINES\n\ntemplate <size_t Depth, typename Awaitable>\nFOLLY_NOINLINE folly::coro::Task<void> waitOn(Awaitable&& aw) {\n  if constexpr (Depth == 0) {\n    co_await std::forward<Awaitable&&>(aw);\n  } else {\n    co_await waitOn<Depth - 1>(std::forward<Awaitable&&>(aw));\n  }\n  co_return;\n}\n\nstatic void expectSuspendedFrames(size_t numFrames, size_t stackDepth = 0) {\n  struct StackAggregator {\n    std::vector<std::vector<std::uintptr_t>> stacks;\n\n    void operator()(folly::AsyncStackFrame* frame) {\n      stacks.push_back(walkStack(frame));\n    }\n\n    std::vector<std::uintptr_t> walkStack(folly::AsyncStackFrame* topFrame) {\n      std::array<std::uintptr_t, 128> frames;\n      auto numFrames = folly::getAsyncStackTraceFromInitialFrame(\n          topFrame, frames.data(), 128);\n      return std::vector<std::uintptr_t>(\n          frames.data(), frames.data() + numFrames);\n    }\n\n  } agg;\n\n  folly::sweepSuspendedLeafFrames(agg);\n  if constexpr (!folly::kIsDebug) {\n    // Stack tracing doesn't work in non-debug builds\n    CHECK_EQ(0, agg.stacks.size());\n  } else {\n    CHECK_EQ(numFrames, agg.stacks.size());\n\n    for (const auto& stack : agg.stacks) {\n      CHECK_EQ(stack.size(), stackDepth);\n    }\n\n    if (folly::symbolizer::Symbolizer::isAvailable()) {\n      auto stacks = folly::symbolizer::getSuspendedStackTraces();\n      CHECK_EQ(numFrames, stacks.size());\n    }\n  }\n}\n\nCO_TEST(SuspendedStacksTest, testBaton) {\n  auto currentDepth =\n      (co_await folly::coro::co_current_async_stack_trace).size();\n  auto* ex = co_await folly::coro::co_current_executor;\n\n  expectSuspendedFrames(0);\n\n  folly::coro::Baton b;\n  co_withExecutor(ex, waitOn<coroDepth>(b)).start();\n  co_await folly::coro::co_reschedule_on_current_executor;\n  expectSuspendedFrames(1, currentDepth + coroDepth);\n\n  co_withExecutor(ex, waitOn<coroDepth>(b)).start();\n  co_await folly::coro::co_reschedule_on_current_executor;\n  expectSuspendedFrames(2, currentDepth + coroDepth);\n\n  b.post();\n  co_await folly::coro::co_reschedule_on_current_executor;\n\n  expectSuspendedFrames(0);\n}\n\nCO_TEST(SuspendedStacksTest, testFibersBaton) {\n  auto currentDepth =\n      (co_await folly::coro::co_current_async_stack_trace).size();\n  auto* ex = co_await folly::coro::co_current_executor;\n\n  expectSuspendedFrames(0);\n\n  folly::fibers::Baton b;\n  co_withExecutor(ex, waitOn<coroDepth>(b)).start();\n  co_await folly::coro::co_reschedule_on_current_executor;\n  expectSuspendedFrames(1, currentDepth + coroDepth);\n\n  // Only one fiber/coro can wait on a fibers::Baton\n\n  b.post();\n  co_await folly::coro::co_reschedule_on_current_executor;\n\n  expectSuspendedFrames(0);\n}\n\nCO_TEST(SuspendedStacksTest, testLock) {\n  auto currentDepth =\n      (co_await folly::coro::co_current_async_stack_trace).size();\n\n  folly::coro::Mutex m;\n  co_await m.co_lock();\n\n  expectSuspendedFrames(0);\n\n  auto aw = m.co_lock();\n  co_withExecutor(\n      co_await folly::coro::co_current_executor,\n      waitOn<coroDepth>(std::move(aw)))\n      .start();\n  co_await folly::coro::co_reschedule_on_current_executor;\n  expectSuspendedFrames(1, currentDepth + coroDepth);\n\n  m.unlock();\n  co_await folly::coro::co_reschedule_on_current_executor;\n  expectSuspendedFrames(0);\n}\n\nCO_TEST(SuspendedStacksTest, testFuture) {\n  auto currentDepth =\n      (co_await folly::coro::co_current_async_stack_trace).size();\n  auto [promise, future] = folly::makePromiseContract<int>();\n\n  co_withExecutor(\n      co_await folly::coro::co_current_executor,\n      waitOn<coroDepth>(std::move(future)))\n      .start();\n  co_await folly::coro::co_reschedule_on_current_executor;\n  expectSuspendedFrames(1, currentDepth + coroDepth);\n\n  promise.setValue(1);\n  co_await folly::coro::co_reschedule_on_current_executor;\n  expectSuspendedFrames(0);\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/SynchronizedTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Synchronized.h>\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\nusing folly::coro::Baton;\nusing folly::coro::Synchronized;\nusing folly::coro::Task;\n\nnamespace {\n\n// Silences clang-tidy's warning about use-after-move\ntemplate <typename T>\nT& assumeInitialized(T& value) {\n  return value;\n}\n\n} // namespace\n\nclass SynchronizedTest : public testing::Test {};\n\nTEST_F(SynchronizedTest, MoveLockingPtr) {\n  Synchronized<int64_t> counter{5};\n\n  auto readPtr = counter.tryRLock();\n  ASSERT_TRUE(readPtr);\n  EXPECT_EQ(*readPtr, 5);\n\n  auto readPtr2 = std::move(readPtr);\n  assumeInitialized(readPtr);\n  EXPECT_FALSE(readPtr);\n  if (folly::kIsDebug) {\n    EXPECT_DEATH({ (void)*readPtr; }, \"\");\n  }\n  ASSERT_TRUE(readPtr2);\n  EXPECT_EQ(*readPtr2, 5);\n\n  auto readPtr3 = counter.tryRLock();\n  EXPECT_TRUE(readPtr3);\n  readPtr3 = std::move(readPtr2);\n  assumeInitialized(readPtr2);\n  EXPECT_FALSE(readPtr2);\n  if (folly::kIsDebug) {\n    EXPECT_DEATH({ (void)*readPtr2; }, \"\");\n  }\n  ASSERT_TRUE(readPtr3);\n  EXPECT_EQ(*readPtr3, 5);\n}\n\nTEST_F(SynchronizedTest, ConcurrentReads) {\n  Synchronized<int64_t> counter;\n\n  folly::ManualExecutor executor;\n  Baton b;\n\n  static auto kNumReaders = 10;\n  int64_t activeReaders = 0;\n  for (auto i = 0; i < kNumReaders; i++) {\n    auto readTask = counter.withRLock(\n        [&b, &activeReaders](auto /* unused */) mutable -> Task<folly::Unit> {\n          activeReaders++;\n          co_await b;\n          activeReaders--;\n          co_return folly::unit;\n        });\n    co_withExecutor(&executor, std::move(readTask)).start();\n  }\n  executor.drain();\n  EXPECT_EQ(activeReaders, kNumReaders);\n\n  bool writeComplete = false;\n  auto writeTask = counter.withWLock(\n      [&writeComplete,\n       &activeReaders](auto /* unused */) mutable -> Task<folly::Unit> {\n        EXPECT_EQ(activeReaders, 0);\n        writeComplete = true;\n        co_return folly::unit;\n      });\n  co_withExecutor(&executor, std::move(writeTask)).start();\n\n  // Readers have the lock. Writing doesn't run.\n  executor.drain();\n  EXPECT_EQ(activeReaders, kNumReaders);\n\n  // Post the baton. Writing will succeed.\n  b.post();\n  executor.drain();\n  EXPECT_TRUE(writeComplete);\n}\n\nTEST_F(SynchronizedTest, ConcurrentReadsWithUnlock) {\n  Synchronized<int64_t> counter;\n\n  folly::ManualExecutor executor;\n  Baton b1, b2;\n\n  static auto kNumReaders = 10;\n  int64_t activeReaders = 0;\n  for (auto i = 0; i < kNumReaders; i++) {\n    auto readTask = counter.withRLock(\n        [&b1, &b2, &activeReaders](\n            auto counterReadPtr) mutable -> folly::coro::Task<folly::Unit> {\n          activeReaders++;\n          co_await b1;\n\n          counterReadPtr.unlock();\n          co_await b2;\n\n          activeReaders--;\n          co_return folly::unit;\n        });\n    co_withExecutor(&executor, std::move(readTask)).start();\n  }\n  executor.drain();\n  EXPECT_EQ(activeReaders, kNumReaders);\n\n  bool writeComplete = false;\n  auto writeTask = counter.withWLock(\n      [&writeComplete](\n          auto /* unused */) mutable -> folly::coro::Task<folly::Unit> {\n        writeComplete = true;\n        co_return folly::unit;\n      });\n  co_withExecutor(&executor, std::move(writeTask)).start();\n\n  // Readers have the lock. Writing doesn't run.\n  executor.drain();\n  EXPECT_EQ(activeReaders, kNumReaders);\n\n  // Post the first baton. Lock released, writing will succeed,\n  // activeReaders won't change.\n  b1.post();\n  executor.drain();\n  EXPECT_TRUE(writeComplete);\n  EXPECT_EQ(activeReaders, kNumReaders);\n\n  // Post the second baton. Lock already released, activeReaders will change.\n  b2.post();\n  executor.drain();\n  EXPECT_EQ(activeReaders, 0);\n}\n\nTEST_F(SynchronizedTest, ThreadSafety) {\n  folly::CPUThreadPoolExecutor threadPool{\n      2, std::make_shared<folly::NamedThreadFactory>(\"CPUThreadPool\")};\n\n  static auto kBumpCount = 10000;\n  Synchronized<int64_t> counter = {};\n\n  auto makeTask = [&]() -> Task<void> {\n    for (int i = 0; i < kBumpCount; ++i) {\n      co_await counter.withWLock([](auto cnt) -> void { (*cnt)++; });\n    }\n  };\n\n  auto f1 = co_withExecutor(&threadPool, makeTask()).start();\n  auto f2 = co_withExecutor(&threadPool, makeTask()).start();\n  auto f3 = co_withExecutor(&threadPool, makeTask()).start();\n\n  std::move(f1).get();\n  std::move(f2).get();\n  std::move(f3).get();\n\n  auto finalValue = folly::coro::blockingWait(counter.copy());\n  EXPECT_EQ(3 * kBumpCount, finalValue);\n}\n\nCO_TEST_F(SynchronizedTest, SwapAndCopy) {\n  Synchronized<std::vector<int64_t>> protectedList({1});\n  std::vector<int64_t> newValues = {2};\n\n  co_await protectedList.swap(newValues);\n  auto updatedValues = co_await protectedList.copy();\n\n  EXPECT_EQ(newValues[0], 1);\n  EXPECT_EQ(updatedValues[0], 2);\n}\n\nTEST_F(SynchronizedTest, TryLock) {\n  Synchronized<int64_t> sync(1);\n\n  {\n    const Synchronized<int64_t>& constSynch = sync;\n    auto lock = constSynch.tryRLock();\n    EXPECT_TRUE(lock);\n  }\n\n  {\n    auto lock = sync.tryRLock();\n    EXPECT_TRUE(lock);\n\n    // Multiple read locks can be acquired\n    EXPECT_TRUE(sync.tryRLock());\n\n    // Exclusive lock cannot be acquired\n    auto writeLock = sync.tryWLock();\n    EXPECT_FALSE(writeLock);\n    if (folly::kIsDebug) {\n      EXPECT_DEATH({ (void)*writeLock; }, \"\");\n    }\n\n    lock.unlock();\n\n    // After unlock another lock can be acquired\n    EXPECT_TRUE(sync.tryRLock());\n  }\n\n  {\n    auto lock = sync.tryWLock();\n    EXPECT_TRUE(lock);\n\n    // Another exclusive lock cannot be acquired\n    auto writeLock = sync.tryWLock();\n    EXPECT_FALSE(writeLock);\n    if (folly::kIsDebug) {\n      EXPECT_DEATH({ (void)*writeLock; }, \"\");\n    }\n\n    // A read lock cannot be acquired\n    auto readLock = sync.tryRLock();\n    EXPECT_FALSE(readLock);\n    if (folly::kIsDebug) {\n      EXPECT_DEATH({ (void)*readLock; }, \"\");\n    }\n\n    lock.unlock();\n\n    // After unlock another lock can be acquired\n    EXPECT_TRUE(sync.tryRLock());\n  }\n}\n"
  },
  {
    "path": "folly/coro/test/TaskBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/Portability.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Task.h>\n\n#include <memory>\n\n#if FOLLY_HAS_COROUTINES\n\nBENCHMARK(SingleVoidSynchronousTaskInLoop, iters) {\n  folly::coro::blockingWait([iters]() -> folly::coro::Task<void> {\n    auto completeSynchronously = []() -> folly::coro::Task<void> { co_return; };\n    for (std::size_t i = 0; i < iters; ++i) {\n      co_await completeSynchronously();\n    }\n  }());\n}\n\nBENCHMARK(AsyncTaskInLoop, iters) {\n  folly::coro::blockingWait([iters]() -> folly::coro::Task<void> {\n    auto asyncReschedule = []() -> folly::coro::Task<void> {\n      co_await folly::coro::co_reschedule_on_current_executor;\n    };\n    for (std::size_t i = 0; i < iters; ++i) {\n      co_await asyncReschedule();\n    }\n  }());\n}\n\nBENCHMARK(AsyncRescheduleNoTask, iters) {\n  folly::coro::blockingWait([iters]() -> folly::coro::Task<void> {\n    for (std::size_t i = 0; i < iters; ++i) {\n      co_await folly::coro::co_reschedule_on_current_executor;\n    }\n  }());\n}\n\nclass Base {\n public:\n  virtual ~Base() {}\n\n  virtual folly::coro::Task<int> virtualMethod() = 0;\n};\n\ntemplate <int Tag>\nclass Derived : public Base {\n public:\n  FOLLY_NOINLINE\n  folly::coro::Task<int> virtualMethod() override { co_return Tag; }\n};\n\nFOLLY_NOINLINE\nstatic std::unique_ptr<Base> makeBase(size_t iters) {\n  switch (iters % 3) {\n    case 0:\n      return std::make_unique<Derived<1>>();\n    case 1:\n      return std::make_unique<Derived<2>>();\n    default:\n      return std::make_unique<Derived<3>>();\n  }\n}\n\nBENCHMARK(VirtualTaskMethod, iters) {\n  folly::coro::blockingWait([iters]() -> folly::coro::Task<void> {\n    auto base = makeBase(iters);\n    size_t count = 0;\n    for (std::size_t i = 0; i < iters; ++i) {\n      count += co_await base->virtualMethod();\n    }\n    if (count != iters * ((iters % 3) + 1)) {\n      std::terminate();\n    }\n  }());\n}\n\ntemplate <size_t N>\nstatic folly::coro::Task<void> staticNestedCalls() {\n  if constexpr (N > 0) {\n    co_await staticNestedCalls<N - 1>();\n  }\n  co_return;\n}\n\ntemplate <size_t N>\nstatic void benchStaticNestedCalls(size_t iters) {\n  folly::coro::blockingWait([iters]() -> folly::coro::Task<void> {\n    for (size_t i = 0; i < iters; ++i) {\n      co_await staticNestedCalls<N>();\n    }\n  }());\n}\n\nBENCHMARK(StaticNestedCalls3, iters) {\n  benchStaticNestedCalls<3>(iters / 3);\n}\n\nBENCHMARK(StaticNestedCalls10, iters) {\n  benchStaticNestedCalls<10>(iters / 10);\n}\n\nstatic folly::coro::Task<void> nestedCalls(size_t depth) {\n  if (depth > 0) {\n    co_await nestedCalls(depth - 1);\n  }\n  co_return;\n}\n\nstatic void benchNestedCalls(size_t depth, size_t iters) {\n  folly::coro::blockingWait([depth, iters]() -> folly::coro::Task<void> {\n    for (size_t i = 0; i < iters; ++i) {\n      co_await nestedCalls(depth);\n    }\n  }());\n}\n\nBENCHMARK(NestedCalls3, iters) {\n  benchNestedCalls(3, iters / 3);\n}\n\nBENCHMARK(NestedCalls10, iters) {\n  benchNestedCalls(10, iters / 10);\n}\n\nstatic void benchNestedCallsWithCancellation(size_t depth, size_t iters) {\n  folly::CancellationSource cancelSource;\n  folly::coro::blockingWait(\n      folly::coro::co_withCancellation(\n          cancelSource.getToken(), [depth, iters]() -> folly::coro::Task<void> {\n            for (size_t i = 0; i < iters; ++i) {\n              co_await nestedCalls(depth);\n            }\n          }()));\n}\n\nBENCHMARK(NestedCallsWithCancellation3, iters) {\n  benchNestedCallsWithCancellation(3, iters / 3);\n}\n\nBENCHMARK(NestedCallsWithCancellation10, iters) {\n  benchNestedCallsWithCancellation(10, iters / 10);\n}\n\n#endif // FOLLY_HAS_COROUTINES\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/coro/test/TaskTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Conv.h>\n#include <folly/Portability.h>\n\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Invoke.h>\n#include <folly/coro/Mutex.h>\n#include <folly/coro/SharedMutex.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/ValueOrError.h>\n#include <folly/coro/detail/InlineTask.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/portability/GTest.h>\n#include <folly/result/coro.h>\n\n#include <stdexcept>\n#include <type_traits>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly;\n\nconstexpr bool check_for_size_regressions() {\n  namespace detail = folly::coro::detail;\n\n  static_assert(sizeof(coro::Task<>) == sizeof(void*));\n  static_assert(sizeof(coro::Task<int>) == sizeof(void*));\n\n  // Prevent size regressions due to member or base ordering\n  constexpr size_t promiseSize =\n      // From TaskPromiseBase:\n      sizeof(coro::ExtendedCoroutineHandle) + sizeof(folly::AsyncStackFrame) +\n      sizeof(folly::Executor::KeepAlive<>) + sizeof(folly::CancellationToken) +\n      sizeof(coro::coroutine_handle<detail::ScopeExitTaskPromiseBase>) +\n      // hasCancelTokenOverride_ and bypassExceptionThrowing_ should pack into\n      sizeof(void*) +\n      // From TaskPromiseCrtpBase:\n      sizeof(Try<int>) +\n      // From ExtendedCoroutinePromiseCrtp:\n      sizeof(coro::ExtendedCoroutineHandle::PromiseBase);\n  static_assert(sizeof(detail::TaskPromise<void>) == promiseSize);\n  static_assert(sizeof(detail::TaskPromise<int>) == promiseSize);\n\n  return true;\n}\nstatic_assert(check_for_size_regressions());\n\nstatic_assert( //\n    std::is_same<\n        folly::coro::semi_await_result_t<folly::coro::Task<void>>,\n        void>::value);\nstatic_assert( //\n    std::is_same<\n        folly::coro::semi_await_result_t<folly::coro::Task<int>>,\n        int>::value);\n\nstatic_assert(\n    std::is_same<\n        folly::coro::semi_await_result_t<folly::coro::detail::InlineTask<void>>,\n        void>::value);\nstatic_assert(\n    std::is_same<\n        folly::coro::semi_await_result_t<folly::coro::detail::InlineTask<int>>,\n        int>::value);\n\nstatic_assert(\n    std::is_same<folly::coro::semi_await_result_t<folly::coro::Baton&>, void>::\n        value);\nstatic_assert(\n    std::is_same<\n        folly::coro::semi_await_result_t<\n            decltype(std::declval<folly::coro::SharedMutex&>()\n                         .co_scoped_lock_shared())>,\n        folly::coro::SharedLock<folly::coro::SharedMutex>>::value);\n\nnamespace {\n\nconst RequestToken testToken1(\"corotest1\");\nconst RequestToken testToken2(\"corotest2\");\n\nclass TestRequestData : public RequestData {\n public:\n  explicit TestRequestData(std::string key) noexcept : key_(std::move(key)) {}\n\n  bool hasCallback() override { return false; }\n\n  const std::string& key() const noexcept { return key_; }\n\n private:\n  std::string key_;\n};\n\n} // namespace\n\nstatic coro::Task<void> childRequest(coro::Mutex& m, coro::Baton& b) {\n  ShallowCopyRequestContextScopeGuard requestScope;\n\n  auto* parentContext = dynamic_cast<TestRequestData*>(\n      RequestContext::get()->getContextData(testToken1));\n\n  EXPECT_TRUE(parentContext != nullptr);\n\n  auto childKey = parentContext->key() + \".child\";\n\n  RequestContext::get()->setContextData(\n      testToken2, std::make_unique<TestRequestData>(childKey));\n\n  auto* childContext = dynamic_cast<TestRequestData*>(\n      RequestContext::get()->getContextData(testToken2));\n  CHECK(childContext != nullptr);\n\n  {\n    auto lock = co_await m.co_scoped_lock();\n\n    CHECK_EQ(\n        parentContext,\n        dynamic_cast<TestRequestData*>(\n            RequestContext::get()->getContextData(testToken1)));\n    CHECK_EQ(\n        childContext,\n        dynamic_cast<TestRequestData*>(\n            RequestContext::get()->getContextData(testToken2)));\n\n    co_await b;\n\n    CHECK_EQ(\n        parentContext,\n        dynamic_cast<TestRequestData*>(\n            RequestContext::get()->getContextData(testToken1)));\n    CHECK_EQ(\n        childContext,\n        dynamic_cast<TestRequestData*>(\n            RequestContext::get()->getContextData(testToken2)));\n  }\n\n  CHECK_EQ(\n      parentContext,\n      dynamic_cast<TestRequestData*>(\n          RequestContext::get()->getContextData(testToken1)));\n  CHECK_EQ(\n      childContext,\n      dynamic_cast<TestRequestData*>(\n          RequestContext::get()->getContextData(testToken2)));\n}\n\nstatic coro::Task<void> parentRequest(int id) {\n  ShallowCopyRequestContextScopeGuard requestScope;\n\n  // Should have captured the value at the time the coroutine was co_awaited\n  // rather than at the time the coroutine was called.\n  auto* globalData = dynamic_cast<TestRequestData*>(\n      RequestContext::get()->getContextData(\"global\"));\n  CHECK(globalData != nullptr);\n  CHECK_EQ(\"other value\", globalData->key());\n\n  std::string key = folly::to<std::string>(\"request\", id);\n\n  RequestContext::get()->setContextData(\n      testToken1, std::make_unique<TestRequestData>(key));\n\n  auto* contextData = RequestContext::get()->getContextData(testToken1);\n  CHECK(contextData != nullptr);\n\n  coro::Mutex mutex;\n  coro::Baton baton1;\n  coro::Baton baton2;\n\n  auto fut1 =\n      co_withExecutor(\n          co_await coro::co_current_executor, childRequest(mutex, baton1))\n          .start();\n  auto fut2 =\n      co_withExecutor(\n          co_await coro::co_current_executor, childRequest(mutex, baton1))\n          .start();\n\n  CHECK_EQ(contextData, RequestContext::get()->getContextData(testToken1));\n\n  baton1.post();\n  baton2.post();\n\n  (void)co_await std::move(fut1);\n\n  CHECK_EQ(contextData, RequestContext::get()->getContextData(testToken1));\n\n  // Check that context from child operation doesn't leak into this coroutine.\n  CHECK(RequestContext::get()->getContextData(testToken2) == nullptr);\n\n  (void)co_await std::move(fut2);\n\n  // Check that context from child operation doesn't leak into this coroutine.\n  CHECK(RequestContext::get()->getContextData(testToken2) == nullptr);\n}\n\nclass TaskTest : public testing::Test {};\n\nTEST_F(TaskTest, RequestContextIsPreservedAcrossSuspendResume) {\n  ManualExecutor executor;\n\n  RequestContextScopeGuard requestScope;\n\n  RequestContext::get()->setContextData(\n      \"global\", std::make_unique<TestRequestData>(\"global value\"));\n\n  // Context should be captured at coroutine co_await time and not at\n  // call time.\n  auto task1 = co_withExecutor(&executor, parentRequest(1));\n  auto task2 = co_withExecutor(&executor, parentRequest(2));\n\n  {\n    RequestContextScopeGuard nestedRequestScope;\n\n    RequestContext::get()->setContextData(\n        \"global\", std::make_unique<TestRequestData>(\"other value\"));\n\n    // Start execution of the tasks.\n    auto fut1 = std::move(task1).start();\n    auto fut2 = std::move(task2).start();\n\n    // Check that the contexts set by starting the tasks don't bleed out\n    // to the caller.\n    CHECK(RequestContext::get()->getContextData(testToken1) == nullptr);\n    CHECK(RequestContext::get()->getContextData(testToken2) == nullptr);\n    CHECK_EQ(\n        \"other value\",\n        dynamic_cast<TestRequestData*>(\n            RequestContext::get()->getContextData(\"global\"))\n            ->key());\n\n    executor.drain();\n\n    CHECK(fut1.isReady());\n    CHECK(fut2.isReady());\n\n    // Check that the contexts set by the coroutines executing on the executor\n    // do not leak out to the caller.\n    CHECK(RequestContext::get()->getContextData(testToken1) == nullptr);\n    CHECK(RequestContext::get()->getContextData(testToken2) == nullptr);\n    CHECK_EQ(\n        \"other value\",\n        dynamic_cast<TestRequestData*>(\n            RequestContext::get()->getContextData(\"global\"))\n            ->key());\n  }\n}\n\nTEST_F(TaskTest, ContextPreservedAcrossMutexLock) {\n  folly::coro::Mutex mutex;\n\n  auto handleRequest =\n      [&](folly::coro::Baton& event) -> folly::coro::Task<void> {\n    RequestContextScopeGuard requestScope;\n\n    RequestData* contextDataPtr = nullptr;\n    {\n      auto contextData = std::make_unique<TestRequestData>(\"some value\");\n      contextDataPtr = contextData.get();\n      RequestContext::get()->setContextData(\n          \"mutex_test\", std::move(contextData));\n    }\n\n    auto lock = co_await mutex.co_scoped_lock();\n\n    // Check that the request context was preserved across mutex lock\n    // acquisition.\n    CHECK_EQ(\n        RequestContext::get()->getContextData(\"mutex_test\"), contextDataPtr);\n\n    co_await event;\n\n    // Check that request context was preserved across baton wait.\n    CHECK_EQ(\n        RequestContext::get()->getContextData(\"mutex_test\"), contextDataPtr);\n  };\n\n  folly::ManualExecutor manualExecutor;\n\n  folly::coro::Baton event1;\n  folly::coro::Baton event2;\n  auto t1 = co_withExecutor(&manualExecutor, handleRequest(event1)).start();\n  auto t2 = co_withExecutor(&manualExecutor, handleRequest(event2)).start();\n\n  manualExecutor.drain();\n\n  event1.post();\n\n  manualExecutor.drain();\n\n  event2.post();\n\n  manualExecutor.drain();\n\n  EXPECT_TRUE(t1.isReady());\n  EXPECT_TRUE(t2.isReady());\n\n  EXPECT_FALSE(t1.hasException());\n  EXPECT_FALSE(t2.hasException());\n}\n\nTEST_F(TaskTest, RequestContextSideEffectsArePreserved) {\n  auto f =\n      [&](folly::coro::Baton& baton) -> folly::coro::detail::InlineTask<void> {\n    RequestContext::create();\n\n    RequestContext::get()->setContextData(\n        testToken1, std::make_unique<TestRequestData>(\"test\"));\n\n    EXPECT_NE(RequestContext::get()->getContextData(testToken1), nullptr);\n\n    // HACK: Need to use co_viaIfAsync() to ensure request context is preserved\n    // across suspend-point.\n    co_await folly::coro::co_viaIfAsync(\n        &folly::InlineExecutor::instance(), baton);\n\n    EXPECT_NE(RequestContext::get()->getContextData(testToken1), nullptr);\n\n    co_return;\n  };\n\n  auto g = [&](folly::coro::Baton& baton) -> folly::coro::Task<void> {\n    EXPECT_EQ(RequestContext::get()->getContextData(testToken1), nullptr);\n    co_await f(baton);\n    EXPECT_NE(RequestContext::get()->getContextData(testToken1), nullptr);\n    EXPECT_EQ(\n        dynamic_cast<TestRequestData*>(\n            RequestContext::get()->getContextData(testToken1))\n            ->key(),\n        \"test\");\n  };\n\n  folly::ManualExecutor executor;\n  folly::coro::Baton baton;\n\n  auto t = co_withExecutor(&executor, g(baton)).start();\n\n  executor.drain();\n\n  baton.post();\n\n  executor.drain();\n\n  EXPECT_TRUE(t.isReady());\n  EXPECT_FALSE(t.hasException());\n}\n\nTEST_F(TaskTest, FutureTailCall) {\n  EXPECT_EQ(\n      42,\n      folly::coro::blockingWait(\n          folly::coro::co_invoke([&]() -> folly::coro::Task<int> {\n            co_return co_await folly::makeSemiFuture().deferValue([](auto) {\n              return folly::makeSemiFuture(42);\n            });\n          })));\n}\n\nTEST_F(TaskTest, FutureRoundtrip) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    co_yield folly::coro::co_result(\n        co_await folly::coro::co_awaitTry(\n            []() -> folly::coro::Task<void> { co_return; }().semi()));\n  }());\n\n  EXPECT_THROW(\n      folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n        co_yield folly::coro::co_result(\n            co_await folly::coro::co_awaitTry(\n                []() -> folly::coro::Task<void> {\n                  co_yield folly::coro::co_error(std::runtime_error(\"\"));\n                }()\n                            .semi()));\n      }()),\n      std::runtime_error);\n}\n\nnamespace {\n\n// We just want to make sure this compiles without errors or warnings.\n[[maybe_unused]] folly::coro::Task<void>\ncheckAwaitingFutureOfUnitDoesntWarnAboutDiscardedResult() {\n  co_await folly::makeSemiFuture();\n\n  using namespace std::literals::chrono_literals;\n  co_await folly::futures::sleep(1ms);\n}\n\n} // namespace\n\nTEST_F(TaskTest, TaskOfLvalueReference) {\n  auto returnIntRef = [](int& value) -> folly::coro::Task<int&> {\n    co_return value;\n  };\n\n  int value = 123;\n  auto&& result = folly::coro::blockingWait(returnIntRef(value));\n  static_assert(std::is_same_v<decltype(result), int&>);\n  CHECK_EQ(&value, &result);\n}\n\nTEST_F(TaskTest, TaskOfLvalueReferenceAsTry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto returnIntRef = [](int& value) -> folly::coro::Task<int&> {\n      co_return value;\n    };\n\n    int value = 123;\n    auto&& result = co_await co_awaitTry(returnIntRef(value));\n    CHECK(result.hasValue());\n    CHECK_EQ(&value, &result.value().get());\n\n    int& valueRef = co_await returnIntRef(value);\n    CHECK_EQ(&value, &valueRef);\n  }());\n}\n\nTEST_F(TaskTest, TaskOfLvalueReferenceAsResult) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto returnIntRef = [](int& value) -> folly::coro::Task<int&> {\n      co_return value;\n    };\n\n    int value = 123;\n    auto&& res = co_await value_or_error_or_stopped(returnIntRef(value));\n    CHECK(res.has_value());\n    CHECK_EQ(&value, &res.value_or_throw());\n    CHECK_EQ(&value, &(co_await folly::or_unwind(std::move(res))));\n\n    int& valueRef = co_await returnIntRef(value);\n    CHECK_EQ(&value, &valueRef);\n  }());\n}\n\nTEST_F(TaskTest, CancellationPropagation) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto token = co_await folly::coro::co_current_cancellation_token;\n    CHECK(!token.canBeCancelled());\n\n    folly::CancellationSource cancelSource;\n\n    co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), [&]() -> folly::coro::Task<void> {\n          auto token2 = co_await folly::coro::co_current_cancellation_token;\n          CHECK(token2.canBeCancelled());\n          CHECK(!token2.isCancellationRequested());\n\n          // The cancellation token should implicitly propagate into the\n          //\n          co_await [&]() -> folly::coro::Task<void> {\n            auto token3 = co_await folly::coro::co_current_cancellation_token;\n            CHECK(token3 == token2);\n            cancelSource.requestCancellation();\n            CHECK(token3.isCancellationRequested());\n          }();\n          CHECK(token2.isCancellationRequested());\n        }());\n  }());\n}\n\nTEST_F(TaskTest, CancellationPropagatesThroughCoAwaitTry) {\n  folly::CancellationSource source;\n  folly::Try<int> result = folly::coro::blockingWait(\n      folly::coro::co_withCancellation(\n          source.getToken(),\n          folly::coro::co_awaitTry([&]() -> folly::coro::Task<int> {\n            auto cancelToken =\n                co_await folly::coro::co_current_cancellation_token;\n            EXPECT_TRUE(cancelToken == source.getToken());\n            co_return 42;\n          }())));\n  EXPECT_EQ(42, result.value());\n}\n\nTEST_F(TaskTest, StartInlineUnsafe) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto executor = co_await folly::coro::co_current_executor;\n    bool hasStarted = false;\n    bool hasFinished = false;\n    auto makeTask = [&]() -> folly::coro::Task<void> {\n      hasStarted = true;\n      co_await folly::coro::co_reschedule_on_current_executor;\n      hasFinished = true;\n    };\n    auto sf = co_withExecutor(executor, makeTask()).startInlineUnsafe();\n\n    // Check that the task started inline on the current thread.\n    // It should not yet have completed, however, since the rest\n    // of the coroutine needs this coroutine to suspend so the\n    // executor can schedule the rest of it.\n    CHECK(hasStarted);\n    CHECK(!hasFinished);\n\n    co_await std::move(sf);\n\n    CHECK(hasFinished);\n  }());\n}\n\nTEST_F(TaskTest, StartInlineUnsafePreservesRequestContext) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    RequestContextScopeGuard parentGuard;\n    auto* parentCtx = RequestContext::try_get();\n    auto executor = co_await folly::coro::co_current_executor;\n\n    bool hasStarted = false;\n    coro::Baton baton;\n\n    auto makeTask = [&]() -> folly::coro::Task<void> {\n      RequestContextScopeGuard childGuard;\n      auto* childCtx = RequestContext::try_get();\n      hasStarted = true;\n      co_await baton;\n      EXPECT_EQ(childCtx, RequestContext::try_get());\n    };\n    auto sf = co_withExecutor(executor, makeTask()).startInlineUnsafe();\n\n    EXPECT_TRUE(hasStarted);\n    EXPECT_EQ(parentCtx, RequestContext::try_get());\n\n    baton.post();\n    co_await std::move(sf);\n  }());\n}\n\nTEST_F(TaskTest, YieldTry) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto innerTaskVoid = []() -> folly::coro::Task<void> {\n      co_yield folly::coro::co_error(std::runtime_error(\"\"));\n    }();\n    auto retVoid = co_await co_awaitTry([&]() -> folly::coro::Task<void> {\n      co_yield folly::coro::co_result(\n          co_await co_awaitTry(std::move(innerTaskVoid)));\n    }());\n    EXPECT_TRUE(retVoid.hasException());\n\n    innerTaskVoid = []() -> folly::coro::Task<void> { co_return; }();\n    retVoid = co_await co_awaitTry([&]() -> folly::coro::Task<void> {\n      co_yield folly::coro::co_result(\n          co_await co_awaitTry(std::move(innerTaskVoid)));\n    }());\n    EXPECT_FALSE(retVoid.hasException());\n\n    auto innerTaskInt = []() -> folly::coro::Task<int> {\n      co_yield folly::coro::co_error(std::runtime_error(\"\"));\n    }();\n    auto retInt = co_await co_awaitTry([&]() -> folly::coro::Task<int> {\n      co_yield folly::coro::co_result(\n          co_await co_awaitTry(std::move(innerTaskInt)));\n    }());\n    EXPECT_TRUE(retInt.hasException());\n\n    innerTaskInt = []() -> folly::coro::Task<int> { co_return 0; }();\n    retInt = co_await co_awaitTry([&]() -> folly::coro::Task<int> {\n      co_yield folly::coro::co_result(\n          co_await co_awaitTry(std::move(innerTaskInt)));\n    }());\n    EXPECT_TRUE(retInt.hasValue());\n\n    EXPECT_THROW(\n        co_await [&]() -> folly::coro::Task<int> {\n          co_yield folly::coro::co_result(folly::Try<int>());\n        }(),\n        UsingUninitializedTry);\n  }());\n}\n\nTEST_F(TaskTest, MakeTask) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto ret = co_await folly::coro::makeTask(42);\n    EXPECT_EQ(ret, 42);\n\n    co_await folly::coro::makeTask();\n    co_await folly::coro::makeTask(folly::unit);\n\n    auto err = co_await co_awaitTry(\n        folly::coro::makeErrorTask<int>(\n            folly::make_exception_wrapper<std::runtime_error>(\"\")));\n    EXPECT_TRUE(err.hasException());\n\n    err = co_await co_awaitTry(\n        folly::coro::makeResultTask(\n            folly::Try<int>(\n                folly::make_exception_wrapper<std::runtime_error>(\"\"))));\n    EXPECT_TRUE(err.hasException());\n\n    auto try1 = co_await co_awaitTry(\n        folly::coro::makeResultTask(\n            folly::Try<folly::Unit>(\n                folly::make_exception_wrapper<std::runtime_error>(\"\"))));\n    EXPECT_TRUE(try1.hasException());\n    try1 = co_await co_awaitTry(\n        folly::coro::makeResultTask(folly::Try<folly::Unit>(folly::unit)));\n    EXPECT_TRUE(try1.hasValue());\n\n    // test move happens immediately (i.e. no dangling reference)\n    struct {\n      int i{0};\n    } s;\n    auto t = folly::coro::makeTask(std::move(s));\n    s.i = 1;\n    auto s2 = co_await std::move(t);\n    EXPECT_EQ(s2.i, 0);\n  }());\n}\n\nTEST_F(TaskTest, ScheduleOnRestoresExecutor) {\n  folly::ScopedEventBaseThread ebt;\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    co_await co_withExecutor(&ebt, [&]() -> folly::coro::Task<void> {\n      EXPECT_TRUE(ebt.getEventBase()->inRunningEventBaseThread());\n      co_return;\n    }());\n    EXPECT_FALSE(ebt.getEventBase()->inRunningEventBaseThread());\n    try {\n      co_await co_withExecutor(&ebt, [&]() -> folly::coro::Task<void> {\n        EXPECT_TRUE(ebt.getEventBase()->inRunningEventBaseThread());\n        throw std::runtime_error(\"\");\n        co_return;\n      }());\n    } catch (...) {\n    }\n    EXPECT_FALSE(ebt.getEventBase()->inRunningEventBaseThread());\n  }());\n}\n\nTEST_F(TaskTest, CoAwaitTryWithScheduleOn) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    auto t = []() -> folly::coro::Task<int> { co_return 42; }();\n\n    folly::Try<int> result = co_await folly::coro::co_awaitTry(\n        co_withExecutor(folly::getGlobalCPUExecutor(), std::move(t)));\n    EXPECT_EQ(42, result.value());\n  }());\n}\n\nTEST_F(TaskTest, CoAwaitTryWithScheduleOnAndCancellation) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSrc;\n\n    auto makeTask = [&]() -> folly::coro::Task<int> {\n      auto ct = co_await folly::coro::co_current_cancellation_token;\n      EXPECT_FALSE(ct.isCancellationRequested());\n      cancelSrc.requestCancellation();\n      EXPECT_TRUE(ct.isCancellationRequested());\n      co_return 42;\n    };\n\n    {\n      folly::Try<int> result = co_await folly::coro::co_withCancellation(\n          cancelSrc.getToken(),\n          folly::coro::co_awaitTry(\n              co_withExecutor(folly::getGlobalCPUExecutor(), makeTask())));\n      EXPECT_EQ(42, result.value());\n    }\n\n    cancelSrc = {};\n\n    {\n      folly::Try<int> result = co_await folly::coro::co_awaitTry(\n          folly::coro::co_withCancellation(\n              cancelSrc.getToken(),\n              co_withExecutor(folly::getGlobalCPUExecutor(), makeTask())));\n      EXPECT_EQ(42, result.value());\n    }\n  }());\n}\n\nTEST_F(TaskTest, Moved) {\n  if (folly::kIsDebug) {\n    ASSERT_DEATH_IF_SUPPORTED(\n        folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n          folly::coro::Task<int> task = folly::coro::makeTask(1);\n          co_await std::move(task);\n          co_await std::move(task);\n        }()),\n        \"task\\\\.coro_\");\n  }\n}\n\nTEST_F(TaskTest, SafePoint) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    enum class step_type {\n      init,\n      before_continue_sp,\n      after_continue_sp,\n      before_cancel_sp,\n      after_cancel_sp,\n    };\n    step_type step = step_type::init;\n\n    folly::CancellationSource cancelSrc;\n    auto makeTask = [&]() -> folly::coro::Task<void> {\n      step = step_type::before_continue_sp;\n      co_await folly::coro::co_safe_point;\n      step = step_type::after_continue_sp;\n\n      cancelSrc.requestCancellation();\n\n      step = step_type::before_cancel_sp;\n      co_await folly::coro::co_safe_point;\n      step = step_type::after_cancel_sp;\n    };\n\n    auto result = co_await folly::coro::co_awaitTry( //\n        folly::coro::co_withCancellation(cancelSrc.getToken(), makeTask()));\n    EXPECT_THROW(result.value(), folly::OperationCancelled);\n    EXPECT_EQ(step_type::before_cancel_sp, step);\n  }());\n}\n\nTEST_F(TaskTest, CoAwaitNothrow) {\n  auto res =\n      folly::coro::blockingWait(co_awaitTry([]() -> folly::coro::Task<void> {\n        auto t = []() -> folly::coro::Task<int> { co_return 42; }();\n\n        int result = co_await folly::coro::co_nothrow(std::move(t));\n        EXPECT_EQ(42, result);\n\n        t = []() -> folly::coro::Task<int> {\n          co_yield folly::coro::co_error(std::runtime_error(\"\"));\n        }();\n\n        try {\n          result = co_await folly::coro::co_nothrow(std::move(t));\n        } catch (...) {\n          ADD_FAILURE();\n        }\n        ADD_FAILURE();\n      }()));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nTEST_F(TaskTest, CoAwaitNothrowWithScheduleOn) {\n  auto res =\n      folly::coro::blockingWait(co_awaitTry([]() -> folly::coro::Task<void> {\n        auto t = []() -> folly::coro::Task<int> { co_return 42; }();\n\n        int result = co_await folly::coro::co_nothrow(\n            co_withExecutor(folly::getGlobalCPUExecutor(), std::move(t)));\n        EXPECT_EQ(42, result);\n\n        t = []() -> folly::coro::Task<int> {\n          co_yield folly::coro::co_error(std::runtime_error(\"\"));\n        }();\n\n        try {\n          result = co_await folly::coro::co_nothrow(\n              co_withExecutor(folly::getGlobalCPUExecutor(), std::move(t)));\n        } catch (...) {\n          ADD_FAILURE();\n        }\n        ADD_FAILURE();\n      }()));\n  EXPECT_TRUE(res.hasException<std::runtime_error>());\n}\n\nTEST_F(TaskTest, CoAwaitThrowAfterNothrow) {\n  auto res =\n      folly::coro::blockingWait(co_awaitTry([]() -> folly::coro::Task<void> {\n        auto t = []() -> folly::coro::Task<int> { co_return 42; }();\n\n        int result = co_await folly::coro::co_nothrow(std::move(t));\n        EXPECT_EQ(42, result);\n\n        t = []() -> folly::coro::Task<int> {\n          co_yield folly::coro::co_error(std::runtime_error(\"\"));\n        }();\n\n        try {\n          result = co_await std::move(t);\n          ADD_FAILURE();\n        } catch (...) {\n          throw std::logic_error(\"translated\");\n        }\n      }()));\n  EXPECT_TRUE(res.hasException<std::logic_error>());\n}\n\nTEST_F(TaskTest, CoAwaitNothrowDestructorOrdering) {\n  int i = 0;\n  folly::coro::blockingWait(co_awaitTry([&]() -> folly::coro::Task<> {\n    co_await folly::coro::co_nothrow([&]() -> folly::coro::Task<> {\n      SCOPE_EXIT {\n        i *= i;\n      };\n      co_await folly::coro::co_nothrow([&]() -> folly::coro::Task<> {\n        SCOPE_EXIT {\n          i *= 3;\n        };\n        co_await folly::coro::co_nothrow([&]() -> folly::coro::Task<> {\n          SCOPE_EXIT {\n            i += 1;\n          };\n          co_return;\n        }());\n      }());\n    }());\n  }()));\n  EXPECT_EQ(i, 9);\n}\n\nstruct ExpectedException : public std::runtime_error {\n  ExpectedException() : std::runtime_error(\"expected\") {}\n};\n\nTEST_F(TaskTest, CoYieldCoErrorSameExecutor) {\n  folly::ScopedEventBaseThread ebThread;\n\n  auto scopeAndThrow = [&]() -> folly::coro::Task<void> {\n    auto eb = dynamic_cast<folly::EventBase*>(\n        co_await folly::coro::co_current_executor);\n    CHECK(eb);\n    SCOPE_EXIT {\n      CHECK(eb->inRunningEventBaseThread());\n    };\n    co_yield folly::coro::co_error(ExpectedException());\n  };\n  auto scopeAndThrowWrapper = [&]() -> folly::coro::Task<void> {\n    co_await co_withExecutor(ebThread.getEventBase(), scopeAndThrow());\n  };\n\n  EXPECT_THROW(\n      folly::coro::blockingWait(scopeAndThrowWrapper()), ExpectedException);\n}\n#endif\n"
  },
  {
    "path": "folly/coro/test/TaskWrapperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/TaskWrapper.h>\n#include <folly/coro/Timeout.h>\n#include <folly/fibers/Semaphore.h>\n\nusing namespace std::literals::chrono_literals;\n\nnamespace folly::coro {\n\ntemplate <typename>\nclass tiny_now_task;\ntemplate <typename>\nclass tiny_now_task_with_executor;\n\nnamespace detail {\ntemplate <typename T>\nstruct tiny_now_task_with_executor_cfg : DoesNotWrapAwaitable {\n  using InnerTaskWithExecutorT = TaskWithExecutor<T>;\n  using WrapperTaskT = tiny_now_task<T>;\n};\ntemplate <typename T>\nusing tiny_now_task_with_executor_base =\n    ext::wrap_must_use_immediately_t<TaskWithExecutorWrapperCrtp<\n        tiny_now_task_with_executor<T>,\n        detail::tiny_now_task_with_executor_cfg<T>>>;\n} // namespace detail\n\ntemplate <typename T>\nclass [[nodiscard]] tiny_now_task_with_executor final\n    : public detail::tiny_now_task_with_executor_base<T> {\n protected:\n  using detail::tiny_now_task_with_executor_base<\n      T>::tiny_now_task_with_executor_base;\n};\n\nnamespace detail {\ntemplate <typename T>\nclass tiny_now_task_promise final\n    : public TaskPromiseWrapper<T, tiny_now_task<T>, TaskPromise<T>> {};\ntemplate <typename T>\nstruct tiny_now_task_cfg : DoesNotWrapAwaitable {\n  using ValueT = T;\n  using InnerTaskT = Task<T>;\n  using TaskWithExecutorT = tiny_now_task_with_executor<T>;\n  using PromiseT = tiny_now_task_promise<T>;\n};\ntemplate <typename T>\nusing tiny_now_task_base = ext::wrap_must_use_immediately_t<\n    TaskWrapperCrtp<tiny_now_task<T>, detail::tiny_now_task_cfg<T>>>;\n} // namespace detail\n\ntemplate <typename T>\nclass FOLLY_CORO_TASK_ATTRS tiny_now_task final\n    : public detail::tiny_now_task_base<T> {\n protected:\n  using detail::tiny_now_task_base<T>::tiny_now_task_base;\n};\n\nstatic_assert(is_semi_awaitable_v<tiny_now_task<int>>);\n\n///////////////////\n\ntemplate <typename>\nclass TinyMovableTask;\ntemplate <typename>\nclass TinyMovableTaskWithExecutor;\n\nnamespace detail {\ntemplate <typename T>\nstruct TinyMovableTaskWithExecutorCfg : DoesNotWrapAwaitable {\n  using InnerTaskWithExecutorT = TaskWithExecutor<T>;\n  using WrapperTaskT = TinyMovableTask<T>;\n};\ntemplate <typename T>\nusing TinyMovableTaskWithExecutorBase = TaskWithExecutorWrapperCrtp<\n    TinyMovableTaskWithExecutor<T>,\n    detail::TinyMovableTaskWithExecutorCfg<T>>;\n} // namespace detail\n\ntemplate <typename T>\nclass [[nodiscard]] TinyMovableTaskWithExecutor final\n    : public detail::TinyMovableTaskWithExecutorBase<T> {\n protected:\n  using detail::TinyMovableTaskWithExecutorBase<\n      T>::TinyMovableTaskWithExecutorBase;\n};\n\nnamespace detail {\ntemplate <typename T>\nclass TinyMovableTaskPromise final\n    : public TaskPromiseWrapper<T, TinyMovableTask<T>, TaskPromise<T>> {};\ntemplate <typename T>\nstruct TinyMovableTaskCfg : DoesNotWrapAwaitable {\n  using ValueT = T;\n  using InnerTaskT = Task<T>;\n  using TaskWithExecutorT = TinyMovableTaskWithExecutor<T>;\n  using PromiseT = TinyMovableTaskPromise<T>;\n};\ntemplate <typename T>\nusing TinyMovableTaskBase =\n    TaskWrapperCrtp<TinyMovableTask<T>, detail::TinyMovableTaskCfg<T>>;\n} // namespace detail\n\ntemplate <typename T>\nclass FOLLY_CORO_TASK_ATTRS TinyMovableTask final\n    : public detail::TinyMovableTaskBase<T> {\n protected:\n  using detail::TinyMovableTaskBase<T>::TinyMovableTaskBase;\n};\n\nstatic_assert(is_semi_awaitable_v<TinyMovableTask<int>>);\n\n///////////////////\n\ntemplate <\n    template <typename> class TaskT,\n    template <typename> class TaskWithExecutorT>\nstruct TaskWrapperTest : testing::Test {\n  static TaskT<int> intFunc(int x) { co_return x; }\n  static TaskT<int> intPtrFunc(auto x) { co_return *x; }\n  static TaskT<void> voidFunc(auto x, int* ran) {\n    EXPECT_EQ(17, *x);\n    ++*ran;\n    co_return;\n  }\n\n  Task<void> checkBasics() {\n    // Non-void ephemeral lambda\n    EXPECT_EQ(\n        1337, co_await [](int x) -> TaskT<int> { co_return 1300 + x; }(37));\n    // Non-void fn & named lambda\n    {\n      auto x = std::make_unique<int>(17);\n      auto lambdaTmpl = [](auto x) -> TaskT<int> { co_return x; };\n      EXPECT_EQ(20, co_await intPtrFunc(std::move(x)) + co_await lambdaTmpl(3));\n    }\n    // void lambda\n    {\n      int ran = 0;\n      auto lambdaTmpl = [&](auto x) -> TaskT<void> {\n        EXPECT_EQ(3, x);\n        ++ran;\n        co_return;\n      };\n      co_await lambdaTmpl(3);\n      EXPECT_EQ(1, ran);\n    }\n    // void fn\n    {\n      int ran = 0;\n      auto x = std::make_unique<int>(17);\n      co_await voidFunc(std::move(x), &ran);\n      EXPECT_EQ(1, ran);\n    }\n    // can await a `Task`\n    EXPECT_EQ(\n        1337, co_await []() -> TaskT<int> {\n          co_return 1300 + co_await ([]() -> Task<int> { co_return 37; }());\n        }());\n    // `co_return` works with an implicit constructor\n    {\n      auto t = []() -> TaskT<std::pair<int, int>> { co_return {3, 4}; };\n      EXPECT_EQ(std::pair(3, 4), co_await t());\n    }\n  }\n\n  Task<void> checkCancellation() {\n    bool ran = false;\n    EXPECT_THROW(\n        co_await timeout(\n            [&]() -> TaskT<void> {\n              ran = true;\n              folly::fibers::Semaphore stuck{0}; // a cancellable baton\n              co_await stuck.co_wait();\n            }(),\n            200ms),\n        folly::FutureTimeout);\n    EXPECT_TRUE(ran);\n  }\n\n  now_task<> checkNestedCancellation() {\n    bool ran = false;\n    EXPECT_THROW(\n        co_await timeout(\n            [&]() -> TaskT<void> {\n              // Regression test: `CommutativeWrapperAwaitable` used to fail to\n              // propagate cancellation to immovable tasks.\n              co_await co_awaitTry([&]() -> TaskT<void> {\n                ran = true;\n                folly::fibers::Semaphore stuck{0}; // a cancellable baton\n                co_await stuck.co_wait();\n              }());\n            }(),\n            200ms),\n        folly::FutureTimeout);\n    EXPECT_TRUE(ran);\n  }\n\n  Task<void> checkWithExecutor() {\n    auto ex = co_await co_current_executor;\n    static_assert(\n        std::is_same_v<\n            decltype(co_withExecutor(ex, intFunc(5))),\n            TaskWithExecutorT<int>>);\n    EXPECT_EQ(5, co_await co_withExecutor(ex, intFunc(5)));\n  }\n\n  struct MyError : std::exception {};\n\n  Task<void> checkException() {\n    EXPECT_THROW(\n        co_await []() -> TaskT<void> { co_yield co_error(MyError{}); }(),\n        MyError);\n    auto res = co_await co_awaitTry([]() -> TaskT<void> {\n      co_yield co_error(MyError{});\n    }());\n    EXPECT_TRUE(res.template hasException<MyError>());\n  }\n};\n\nusing NowTaskWrapperTest =\n    TaskWrapperTest<tiny_now_task, tiny_now_task_with_executor>;\nusing MovableTaskWrapperTest =\n    TaskWrapperTest<TinyMovableTask, TinyMovableTaskWithExecutor>;\n\nCO_TEST_F(NowTaskWrapperTest, basics) {\n  co_await checkBasics();\n}\nCO_TEST_F(MovableTaskWrapperTest, basics) {\n  co_await checkBasics();\n}\n\nCO_TEST_F(NowTaskWrapperTest, withExecutor) {\n  co_await checkWithExecutor();\n}\nCO_TEST_F(MovableTaskWrapperTest, withExecutor) {\n  co_await checkWithExecutor();\n}\n\nCO_TEST_F(NowTaskWrapperTest, cancellation) {\n  co_await checkCancellation();\n}\nCO_TEST_F(MovableTaskWrapperTest, cancellation) {\n  co_await checkCancellation();\n}\n\nCO_TEST_F(NowTaskWrapperTest, nestedCancellation) {\n  co_await checkNestedCancellation();\n}\nCO_TEST_F(MovableTaskWrapperTest, nestedCancellation) {\n  co_await checkNestedCancellation();\n}\n\nCO_TEST_F(NowTaskWrapperTest, exceptions) {\n  co_await checkException();\n}\nCO_TEST_F(MovableTaskWrapperTest, exceptions) {\n  co_await checkException();\n}\n\ntemplate <typename>\nstruct RecursiveWrapTask;\n\nnamespace detail {\n\ntemplate <typename... BaseArgs>\nclass RecursiveTaskPromiseWrapper final\n    : public TaskPromiseWrapper<BaseArgs...> {};\n\ntemplate <typename T, typename InnerTask>\nstruct RecursiveTaskWrapperConfig : DoesNotWrapAwaitable {\n  using ValueT = T;\n  using InnerTaskT = InnerTask;\n  // IMPORTANT: In a real implementation, this should, of course, wrap an\n  // `InnerTaskWithExecutor`.\n  using TaskWithExecutorT = TaskWithExecutor<T>;\n  using PromiseT = RecursiveTaskPromiseWrapper<\n      T,\n      RecursiveWrapTask<RecursiveTaskWrapperConfig>,\n      typename InnerTask::promise_type>;\n};\n\ntemplate <typename Cfg>\nusing RecursiveWrapTaskBase = TaskWrapperCrtp<RecursiveWrapTask<Cfg>, Cfg>;\n\ntemplate <typename T>\nusing TwoWrapTaskConfig = RecursiveTaskWrapperConfig<T, TinyMovableTask<T>>;\n\n} // namespace detail\n\ntemplate <typename Cfg>\nstruct FOLLY_CORO_TASK_ATTRS RecursiveWrapTask final\n    : public detail::RecursiveWrapTaskBase<Cfg> {\n  using detail::RecursiveWrapTaskBase<Cfg>::unwrapTask;\n\n protected:\n  using detail::RecursiveWrapTaskBase<Cfg>::RecursiveWrapTaskBase;\n};\n\ntemplate <typename T>\nusing TwoWrapTask = RecursiveWrapTask<detail::TwoWrapTaskConfig<T>>;\n\nnamespace detail {\ntemplate <typename T>\nusing ThreeWrapTaskConfig = RecursiveTaskWrapperConfig<T, TwoWrapTask<T>>;\n} // namespace detail\n\ntemplate <typename T>\nusing ThreeWrapTask = RecursiveWrapTask<detail::ThreeWrapTaskConfig<T>>;\n\nCO_TEST(TaskWrapper, recursiveUnwrap) {\n  auto t = []() -> ThreeWrapTask<int> { co_return 3; };\n  EXPECT_EQ(3, co_await t());\n  static_assert(std::is_same_v<decltype(t().unwrapTask()), TwoWrapTask<int>>);\n  EXPECT_EQ(3, co_await t().unwrapTask());\n  static_assert(\n      std::is_same_v<\n          decltype(t().unwrapTask().unwrapTask()),\n          TinyMovableTask<int>>);\n  EXPECT_EQ(3, co_await t().unwrapTask().unwrapTask());\n}\n\n} // namespace folly::coro\n"
  },
  {
    "path": "folly/coro/test/TimeoutTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/Promise.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Timeout.h>\n#include <folly/coro/safe/Captures.h>\n#include <folly/coro/safe/NowTask.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/Request.h>\n#include <folly/portability/GTest.h>\n\n#include <chrono>\n#include <stdexcept>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace std::chrono_literals;\nusing namespace folly;\n\n#if FOLLY_HAS_IMMOVABLE_COROUTINES\n\nnamespace folly::coro {\n\n// timeout(now_task) -> now_task\nstatic_assert(std::is_same_v<\n              now_task<int>,\n              decltype(timeout(FOLLY_DECLVAL(now_task<int>), 1s))>);\nstatic_assert(\n    std::is_same_v<\n        now_task<int>,\n        decltype(timeout(FOLLY_DECLVAL(now_task_with_executor<int>), 1s))>);\n\n// timeout(value_task or coro::Future) -> value_task\nstatic_assert(std::is_same_v<\n              value_task<int>,\n              decltype(timeout(FOLLY_DECLVAL(value_task<int>), 1s))>);\nstatic_assert(\n    std::is_same_v<\n        value_task<int>,\n        decltype(timeout(\n            FOLLY_DECLVAL(\n                safe_task_with_executor<safe_alias::maybe_value, int>),\n            1s))>);\nstatic_assert(std::is_same_v<\n              value_task<int>,\n              decltype(timeout(FOLLY_DECLVAL(coro::Future<int>), 1s))>);\n\n// Passing a `Timekeeper` pointer changes the safety\nstatic_assert(\n    std::is_same_v<\n        Task<int>,\n        decltype(timeout(\n            FOLLY_DECLVAL(value_task<int>), 1s, FOLLY_DECLVAL(Timekeeper*)))>);\nstatic_assert(\n    std::is_same_v<\n        co_cleanup_safe_task<int>,\n        decltype(timeout(\n            FOLLY_DECLVAL(value_task<int>),\n            1s,\n            FOLLY_DECLVAL(capture<Timekeeper&>)))>);\n\n} // namespace folly::coro\n\n#endif\n\nstruct Timeout {\n  template <typename... Arg>\n  auto operator()(Arg&&... args) {\n    return coro::timeout(std::forward<Arg>(args)...);\n  }\n  using ExType = FutureTimeout;\n};\n\nstruct TimeoutNoDiscard {\n  template <typename... Arg>\n  auto operator()(Arg&&... args) {\n    return coro::timeoutNoDiscard(std::forward<Arg>(args)...);\n  }\n  using ExType = OperationCancelled;\n};\n\ntemplate <typename T>\nstruct TimeoutFixture : public ::testing::Test {\n  T fn;\n};\n\nusing TimeoutTestTypes = ::testing::Types<Timeout, TimeoutNoDiscard>;\nTYPED_TEST_SUITE(TimeoutFixture, TimeoutTestTypes);\n\nTYPED_TEST(TimeoutFixture, CompletesSynchronously) {\n  coro::blockingWait([&fn = this->fn]() -> coro::Task<> {\n    // Completing synchronously with void\n    co_await fn([]() -> coro::Task<void> { co_return; }(), 1s);\n\n    // Completing synchronously with a value.\n\n    auto result = co_await fn([]() -> coro::Task<int> { co_return 42; }(), 1s);\n    EXPECT_EQ(42, result);\n\n    // Test that it handles failing synchronously\n    auto tryResult = co_await coro::co_awaitTry(fn(\n        [&]() -> coro::Task<int> {\n          if (true) {\n            throw std::runtime_error{\"bad value\"};\n          }\n          co_return result;\n        }(),\n        1s));\n    EXPECT_TRUE(tryResult.template hasException<std::runtime_error>());\n  }());\n}\n\nTYPED_TEST(TimeoutFixture, CompletesWithinTimeout) {\n  coro::blockingWait([&fn = this->fn]() -> coro::Task<> {\n    // Completing synchronously with void\n    co_await fn(\n        []() -> coro::Task<void> {\n          co_await coro::sleep(1ms);\n          co_return;\n        }(),\n        1s);\n\n    // Completing synchronously with a value.\n    auto result = co_await fn(\n        []() -> coro::Task<int> {\n          co_await coro::sleep(1ms);\n          co_return 42;\n        }(),\n        1s);\n    EXPECT_EQ(42, result);\n\n    // Test that it handles failing synchronously\n    auto tryResult = co_await coro::co_awaitTry(fn(\n        [&]() -> coro::Task<int> {\n          co_await coro::sleep(1ms);\n          if (true) {\n            throw std::runtime_error{\"bad value\"};\n          }\n          co_return result;\n        }(),\n        1s));\n    EXPECT_TRUE(tryResult.template hasException<std::runtime_error>());\n  }());\n}\n\nTEST(TimeoutNoDiscard, ResultOnTimeout) {\n  coro::blockingWait([]() -> coro::Task<> {\n    co_await coro::timeoutNoDiscard(\n        []() -> coro::Task<void> {\n          co_await coro::sleepReturnEarlyOnCancel(10s);\n          EXPECT_TRUE((co_await coro::co_current_cancellation_token)\n                          .isCancellationRequested());\n          co_return;\n        }(),\n        1ms);\n\n    auto result = co_await coro::timeoutNoDiscard(\n        []() -> coro::Task<int> {\n          co_await coro::sleepReturnEarlyOnCancel(10s);\n          EXPECT_TRUE((co_await coro::co_current_cancellation_token)\n                          .isCancellationRequested());\n          co_return 42;\n        }(),\n        1ms);\n    EXPECT_EQ(42, result);\n\n    struct sentinel : public std::exception {};\n    auto tryResult = co_await coro::co_awaitTry(\n        coro::timeoutNoDiscard(\n            [&]() -> coro::Task<int> {\n              co_await coro::sleepReturnEarlyOnCancel(10s);\n              EXPECT_TRUE((co_await coro::co_current_cancellation_token)\n                              .isCancellationRequested());\n              throw sentinel{};\n            }(),\n            1ms));\n    EXPECT_TRUE(tryResult.template hasException<sentinel>());\n  }());\n}\n\nTYPED_TEST(TimeoutFixture, TimeoutElapsed) {\n  using ExType = typename decltype(this->fn)::ExType;\n  coro::blockingWait([&fn = this->fn]() -> coro::Task<> {\n    // Completing synchronously with void\n    auto start = std::chrono::steady_clock::now();\n    folly::Try<void> voidResult = co_await coro::co_awaitTry(fn(\n        []() -> coro::Task<void> {\n          co_await coro::sleep(1s);\n          EXPECT_TRUE((co_await coro::co_current_cancellation_token)\n                          .isCancellationRequested());\n          co_return;\n        }(),\n        5ms));\n    auto elapsed = std::chrono::steady_clock::now() - start;\n    EXPECT_LT(elapsed, 100ms);\n    EXPECT_TRUE(voidResult.hasException<ExType>());\n\n    // Completing synchronously with a value.\n    start = std::chrono::steady_clock::now();\n    auto result = co_await coro::co_awaitTry(fn(\n        []() -> coro::Task<int> {\n          co_await coro::sleep(1s);\n          EXPECT_TRUE((co_await coro::co_current_cancellation_token)\n                          .isCancellationRequested());\n          co_return 42;\n        }(),\n        5ms));\n    elapsed = std::chrono::steady_clock::now() - start;\n    EXPECT_LT(elapsed, 100ms);\n    EXPECT_TRUE(result.template hasException<ExType>());\n\n    // Test that it handles failing synchronously\n    start = std::chrono::steady_clock::now();\n    auto failResult = co_await coro::co_awaitTry(fn(\n        [&]() -> coro::Task<int> {\n          co_await coro::sleep(1s);\n          EXPECT_TRUE((co_await coro::co_current_cancellation_token)\n                          .isCancellationRequested());\n          if (true) {\n            throw std::runtime_error{\"bad value\"};\n          }\n          co_return result;\n        }(),\n        5ms));\n    elapsed = std::chrono::steady_clock::now() - start;\n    EXPECT_LT(elapsed, 100ms);\n    EXPECT_TRUE(result.template hasException<ExType>());\n  }());\n}\n\nTYPED_TEST(TimeoutFixture, CancelParent) {\n  coro::blockingWait([&fn = this->fn]() -> coro::Task<> {\n    CancellationSource cancelSource;\n\n    auto start = std::chrono::steady_clock::now();\n\n    auto [cancelled, _] = co_await coro::collectAll(\n        coro::co_withCancellation(\n            cancelSource.getToken(),\n            fn(\n                []() -> coro::Task<bool> {\n                  auto result = co_await coro::co_awaitTry(coro::sleep(5s));\n                  co_return result.template hasException<OperationCancelled>();\n                }(),\n                10s)),\n        [&]() -> coro::Task<void> {\n          cancelSource.requestCancellation();\n          co_return;\n        }());\n\n    auto elapsed = std::chrono::steady_clock::now() - start;\n    EXPECT_LT(elapsed, 1s);\n\n    EXPECT_TRUE(cancelled);\n  }());\n}\n\nTYPED_TEST(TimeoutFixture, AsyncGenerator) {\n  coro::blockingWait([&fn = this->fn]() -> coro::Task<> {\n    // Completing synchronously with a value.\n    auto result = co_await fn(\n        []() -> coro::AsyncGenerator<int> { co_yield 42; }().next(), 1s);\n    EXPECT_EQ(42, *result);\n\n    // Test that it handles failing synchronously\n    auto tryResult = co_await coro::co_awaitTry(fn(\n        []() -> coro::AsyncGenerator<int> {\n          co_yield coro::co_error(std::runtime_error{\"bad value\"});\n        }()\n                    .next(),\n        1s));\n    EXPECT_TRUE(tryResult.template hasException<std::runtime_error>());\n\n    // Generator completing normally.\n    result = co_await fn(\n        []() -> coro::AsyncGenerator<int> { co_return; }().next(), 1s);\n    EXPECT_FALSE(result);\n  }());\n}\n\nTYPED_TEST(TimeoutFixture, RequestContextInCancellationCallback) {\n  RequestContextScopeGuard guard;\n  auto* ctx = folly::RequestContext::try_get();\n  ASSERT_TRUE(ctx);\n\n  bool cancelled = false;\n  coro::blockingWait([&, &fn = this->fn]() -> coro::Task<> {\n    co_await coro::co_awaitTry(fn(\n        [&]() -> coro::Task<void> {\n          CancellationCallback cb{\n              co_await coro::co_current_cancellation_token, [&] {\n                EXPECT_EQ(folly::RequestContext::try_get(), ctx);\n                cancelled = true;\n              }};\n          co_await coro::sleep(1s);\n        }(),\n        5ms));\n  }());\n  ASSERT_TRUE(cancelled);\n}\n\nTYPED_TEST(TimeoutFixture, TimeoutTaskType) {\n  coro::blockingWait([&fn = this->fn]() -> coro::Task<> {\n    // timeout(Task) -> Task\n    auto five = []() -> coro::Task<int> { co_return 5; };\n    static_assert(std::is_same_v<decltype(fn(five(), 1s)), coro::Task<int>>);\n    EXPECT_EQ(5, co_await fn(five(), 1s));\n\n    // timeout(now_task) -> now_task\n    auto now_two = []() -> coro::now_task<int> { co_return 2; };\n    auto timeout_now_two = [&]() {\n      // Can't use `fn` here because it's set up with perfect forwarding\n      // instead of pass-by-value.  Not worth refactoring for 1 test.\n      if constexpr (std::is_same_v<TypeParam, TimeoutNoDiscard>) {\n        return coro::timeoutNoDiscard(now_two(), 1s);\n      } else {\n        static_assert(std::is_same_v<TypeParam, Timeout>);\n        return coro::timeout(now_two(), 1s);\n      }\n    };\n    static_assert(\n        std::is_same_v<decltype(timeout_now_two()), coro::now_task<int>>);\n    EXPECT_EQ(2, co_await timeout_now_two());\n  }());\n}\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/coro/test/TraitsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <type_traits>\n\n#include <folly/coro/Coroutine.h>\n#include <folly/coro/Traits.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\ntemplate <typename T>\nstruct SomeAwaiter1 {\n  bool await_ready();\n  void await_suspend(coroutine_handle<>);\n  T await_resume();\n};\n\ntemplate <typename T>\nstruct SomeAwaiter2 {\n  bool await_ready();\n  bool await_suspend(coroutine_handle<>);\n  T await_resume();\n};\n\ntemplate <typename T>\nstruct SomeAwaiter3 {\n  bool await_ready();\n  coroutine_handle<> await_suspend(coroutine_handle<>);\n  T await_resume();\n};\n\nstruct MissingAwaitReady {\n  void await_suspend(coroutine_handle<>);\n  int await_resume();\n};\n\nstruct WrongAwaitReadyReturnType {\n  void* await_ready();\n  void await_suspend(coroutine_handle<>);\n  int await_resume();\n};\n\nstruct MissingAwaitResume {\n  bool await_ready();\n  void await_suspend(coroutine_handle<void>);\n};\n\nstruct MemberOperatorCoAwait {\n  SomeAwaiter1<void> operator co_await() &;\n  SomeAwaiter2<int> operator co_await() &&;\n  SomeAwaiter3<float> operator co_await() const&;\n};\n\nstruct FreeOperatorCoAwait {};\nSomeAwaiter1<void> operator co_await(FreeOperatorCoAwait);\n\nstruct MoveOnlyFreeOperatorCoAwait {};\nSomeAwaiter1<int> operator co_await(MoveOnlyFreeOperatorCoAwait&&);\n\nstruct MemberOperatorCoAwaitWithInvalidAwaiter {\n  int operator co_await();\n};\n\nstatic_assert(is_awaiter_v<SomeAwaiter1<void>>);\nstatic_assert(is_awaiter_v<SomeAwaiter2<int>>);\nstatic_assert(is_awaiter_v<SomeAwaiter3<float>>);\nstatic_assert(!is_awaiter_v<void>);\nstatic_assert(!is_awaiter_v<int>);\nstatic_assert(!is_awaiter_v<MissingAwaitReady>);\nstatic_assert(!is_awaiter_v<WrongAwaitReadyReturnType>);\nstatic_assert(!is_awaiter_v<MissingAwaitResume>);\nstatic_assert(!is_awaiter_v<MemberOperatorCoAwait>);\n\nstatic_assert(is_awaitable_v<SomeAwaiter1<void>>);\nstatic_assert(is_awaitable_v<SomeAwaiter2<int>>);\nstatic_assert(is_awaitable_v<SomeAwaiter3<void*>>);\nstatic_assert(is_awaitable_v<MemberOperatorCoAwait>);\nstatic_assert(is_awaitable_v<MemberOperatorCoAwait&>);\nstatic_assert(is_awaitable_v<MemberOperatorCoAwait&&>);\nstatic_assert(is_awaitable_v<const MemberOperatorCoAwait&>);\nstatic_assert(is_awaitable_v<FreeOperatorCoAwait>);\nstatic_assert(is_awaitable_v<FreeOperatorCoAwait&&>);\nstatic_assert(is_awaitable_v<const FreeOperatorCoAwait&>);\nstatic_assert(is_awaitable_v<MoveOnlyFreeOperatorCoAwait&&>);\nstatic_assert(!is_awaitable_v<MoveOnlyFreeOperatorCoAwait&>);\nstatic_assert(!is_awaitable_v<const MoveOnlyFreeOperatorCoAwait&>);\nstatic_assert(!is_awaitable_v<void>);\nstatic_assert(!is_awaitable_v<MemberOperatorCoAwaitWithInvalidAwaiter>);\n\nstatic_assert(\n    std::is_same<awaiter_type_t<SomeAwaiter1<void>>, SomeAwaiter1<void>&>::\n        value);\nstatic_assert(\n    std::is_same<awaiter_type_t<MemberOperatorCoAwait>, SomeAwaiter2<int>>::\n        value);\nstatic_assert(\n    std::is_same<awaiter_type_t<MemberOperatorCoAwait&>, SomeAwaiter1<void>>::\n        value);\nstatic_assert(\n    std::is_same<awaiter_type_t<FreeOperatorCoAwait>, SomeAwaiter1<void>>::\n        value);\n\nstatic_assert(std::is_same<await_result_t<SomeAwaiter1<void>>, void>::value);\nstatic_assert(std::is_same<await_result_t<MemberOperatorCoAwait>, int>::value);\nstatic_assert(\n    std::is_same<await_result_t<MemberOperatorCoAwait&>, void>::value);\nstatic_assert(\n    std::is_same<await_result_t<const MemberOperatorCoAwait&>, float>::value);\nstatic_assert(\n    std::is_same<await_result_t<MoveOnlyFreeOperatorCoAwait>, int>::value);\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/TransformTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utility>\n#include <folly/Portability.h>\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/AsyncGenerator.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/CurrentExecutor.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Transform.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace folly::coro;\n\nclass TransformTest : public testing::Test {};\n\nAsyncGenerator<int> ints(int n) {\n  for (int i = 0; i <= n; ++i) {\n    co_await co_reschedule_on_current_executor;\n    co_yield i;\n  }\n}\n\nTEST_F(TransformTest, SimpleStream) {\n  struct MyError : std::exception {};\n\n  const float seenEndOfStream = 100.0f;\n  const float seenError = 200.0f;\n\n  std::vector<float> lastSeen(3, -1);\n  std::vector<float> expectedSeen = {\n      {seenEndOfStream, seenEndOfStream, seenError}};\n\n  int totalEventCount = 0;\n\n  auto selectStream = [](int index) -> AsyncGenerator<int> {\n    auto failing = []() -> AsyncGenerator<int> {\n      co_yield 0;\n      co_yield 1;\n      throw MyError{};\n    };\n\n    if (index == 0) {\n      return ints(4);\n    } else if (index == 1) {\n      return ints(3);\n    }\n    return failing();\n  };\n\n  auto test = [&](int index) -> Task<void> {\n    auto generator = transform(selectStream(index), [](int i) {\n      return i * 1.0f;\n    });\n    try {\n      while (auto item = co_await generator.next()) {\n        ++totalEventCount;\n\n        auto value = *item;\n        CHECK(index >= 0 && index <= 2);\n        CHECK_EQ(lastSeen[index] + 1.0f, value);\n        lastSeen[index] = value;\n      }\n\n      // EndOfStream\n      if (index == 0) {\n        CHECK_EQ(4, lastSeen[index]);\n      } else if (index == 1) {\n        CHECK_EQ(3, lastSeen[index]);\n      } else {\n        // Stream 2 should have completed with an error not EndOfStream.\n        CHECK(false);\n      }\n      lastSeen[index] = seenEndOfStream;\n\n    } catch (const MyError&) {\n      // Error\n      CHECK_EQ(2, index);\n      CHECK_EQ(1, lastSeen[index]);\n      lastSeen[index] = seenError;\n    }\n\n    CHECK_EQ(expectedSeen[index], lastSeen[index]);\n  };\n  blockingWait(test(0));\n  blockingWait(test(1));\n  blockingWait(test(2));\n}\n\ntemplate <typename Ref, typename Value = folly::remove_cvref_t<Ref>>\nfolly::coro::AsyncGenerator<Ref, Value> neverStream() {\n  folly::coro::Baton baton;\n  folly::CancellationCallback cb{\n      co_await folly::coro::co_current_cancellation_token,\n      [&] { baton.post(); }};\n  co_await baton;\n}\n\nTEST_F(TransformTest, CancellationTokenPropagatesFromConsumer) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::CancellationSource cancelSource;\n    bool suspended = false;\n    bool done = false;\n    co_await folly::coro::collectAll(\n        folly::coro::co_withCancellation(\n            cancelSource.getToken(),\n            [&]() -> folly::coro::Task<void> {\n              auto stream = transform(neverStream<float>(), [](float) {\n                return 42;\n              });\n              suspended = true;\n              auto result = co_await stream.next();\n              CHECK(!result.has_value());\n              done = true;\n            }()),\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          CHECK(suspended);\n          CHECK(!done);\n          cancelSource.requestCancellation();\n        }());\n    CHECK(done);\n  }());\n}\n\nAsyncGenerator<float&> floatRefs(int n) {\n  for (int i = 0; i <= n; ++i) {\n    co_await co_reschedule_on_current_executor;\n    float f = i;\n    co_yield f;\n  }\n}\n\ntemplate <typename T>\nauto useStream(AsyncGenerator<T> ag) {\n  std::vector<typename decltype(ag)::value_type> vals;\n  blockingWait([&](auto ag) -> Task<void> {\n    while (auto item = co_await ag.next()) {\n      vals.emplace_back(std::move(item).value());\n    }\n  }(std::move(ag)));\n  return vals;\n}\n\nTEST_F(TransformTest, TransformDeducesTypesCorrectlySyncFun) {\n  auto makeStream = []() -> AsyncGenerator<int> { return ints(2); };\n  using fv = std::vector<float>;\n  using dv = std::vector<double>;\n\n  {\n    // Function returning value\n    std::function<float(int)> fun = [](int) { return 1.f; };\n\n    // Default deduced as r-value reference\n    AsyncGenerator<float&&> s0 = transform(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s0)), (fv{1.f, 1.f, 1.f}));\n    AsyncGenerator<float&&> s1 = transform<float&&>(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s1)), (fv{1.f, 1.f, 1.f}));\n    AsyncGenerator<float&> s2 = transform<float&>(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s2)), (fv{1.f, 1.f, 1.f}));\n    AsyncGenerator<float> s3 = transform<float>(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s3)), (fv{1.f, 1.f, 1.f}));\n  }\n\n  {\n    // Function returning reference\n\n    std::function<float&(float&)> fun = [&](float& f) -> float& { return f; };\n\n    // Default deduced as l-value reference\n    AsyncGenerator<float&> s0 = transform(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s0)), (fv{0.f, 1.f, 2.f}));\n    AsyncGenerator<float&&> s1 = transform<float&&>(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s1)), (fv{0.f, 1.f, 2.f}));\n    AsyncGenerator<float&> s2 = transform<float&>(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s2)), (fv{0.f, 1.f, 2.f}));\n    AsyncGenerator<float> s3 = transform<float>(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s3)), (fv{0.f, 1.f, 2.f}));\n  }\n\n  // Conversion\n\n  {\n    // Function returning value\n    std::function<float(int)> fun = [](int) { return 1.f; };\n\n    AsyncGenerator<double&&> s1 = transform<double&&>(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s1)), (dv{1., 1., 1.}));\n    AsyncGenerator<double&> s2 = transform<double&>(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s2)), (dv{1., 1., 1.}));\n    AsyncGenerator<const double&> s3 =\n        transform<const double&>(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s3)), (dv{1., 1., 1.}));\n    AsyncGenerator<double> s4 = transform<double>(makeStream(), fun);\n    EXPECT_EQ(useStream(std::move(s4)), (dv{1., 1., 1.}));\n  }\n\n  {\n    // Function returning reference\n    std::function<float&(float&)> fun = [&](float& f) -> float& { return f; };\n\n    AsyncGenerator<double&&> s1 = transform<double&&>(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s1)), (dv{0., 1., 2.}));\n    AsyncGenerator<double&> s2 = transform<double&>(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s2)), (dv{0., 1., 2.}));\n    AsyncGenerator<const double&> s3 =\n        transform<const double&>(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s3)), (dv{0., 1., 2.}));\n    AsyncGenerator<double&&> s4 = transform<double&&>(floatRefs(2), fun);\n    EXPECT_EQ(useStream(std::move(s4)), (dv{0., 1., 2.}));\n  }\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/UnboundedQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Collect.h>\n#include <folly/coro/UnboundedQueue.h>\n\n#include <folly/portability/GTest.h>\n\n#include <chrono>\n#include <string>\n#include <thread>\n\n#if FOLLY_HAS_COROUTINES\n\nusing namespace std::chrono_literals;\n\nTEST(UnboundedQueueTest, EnqueueDeque) {\n  folly::coro::UnboundedQueue<std::string, true, true> queue;\n\n  constexpr auto val = \"a string\";\n  std::string val1 = val;\n\n  EXPECT_TRUE(queue.empty());\n  EXPECT_EQ(queue.size(), 0);\n\n  queue.enqueue(val1);\n  EXPECT_FALSE(queue.empty());\n\n  queue.enqueue(std::move(val1));\n  EXPECT_EQ(queue.size(), 2);\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int i = 0; i < 2; ++i) {\n      auto val2 = co_await queue.dequeue();\n      EXPECT_EQ(val2, val);\n    }\n    EXPECT_TRUE(queue.empty());\n  }());\n}\n\nTEST(UnboundedQueueTest, DequeueWhileBlocking) {\n  folly::coro::UnboundedQueue<int> queue;\n  folly::ManualExecutor ex;\n\n  auto fut = co_withExecutor(&ex, queue.dequeue()).start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n\n  queue.enqueue(0);\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_EQ(std::move(fut).get(), 0);\n}\n\nTEST(UnboundedQueueTest, EnqueueDequeMultiProducer) {\n  folly::coro::UnboundedQueue<int, false, true> queue;\n  std::atomic<int> i = 0;\n\n  std::vector<std::thread> enqueuers;\n  for (int n = 0; n < 5; ++n) {\n    enqueuers.emplace_back([&] {\n      while (true) {\n        int next = i++;\n        if (next >= 100) {\n          break;\n        }\n        queue.enqueue(next);\n      }\n    });\n  }\n\n  folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n    for (int n = 0; n < 100; ++n) {\n      co_await queue.dequeue();\n    }\n  }());\n  EXPECT_TRUE(queue.empty());\n\n  for (int n = 0; n < 5; ++n) {\n    enqueuers[n].join();\n  }\n}\n\nTEST(UnboundedQueueTest, EnqueueDequeMultiConsumer) {\n  folly::coro::UnboundedQueue<int, true, false> queue;\n  std::atomic<int> seen = 0;\n\n  std::vector<std::thread> dequeuers;\n  for (int n = 0; n < 5; ++n) {\n    dequeuers.emplace_back([&] {\n      folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n        while (++seen <= 100) {\n          co_await queue.dequeue();\n        }\n      }());\n    });\n  }\n\n  for (int n = 0; n < 100; ++n) {\n    queue.enqueue(n);\n  }\n  for (int n = 0; n < 5; ++n) {\n    dequeuers[n].join();\n  }\n  EXPECT_TRUE(queue.empty());\n}\n\nTEST(UnboundedQueueTest, EnqueueDequeMPMC) {\n  folly::coro::UnboundedQueue<int, false, false> queue;\n  std::atomic<int> seen = 0, i = 0;\n\n  std::vector<std::thread> enqueuers;\n  for (int n = 0; n < 5; ++n) {\n    enqueuers.emplace_back([&] {\n      while (true) {\n        int next = i++;\n        if (next >= 100) {\n          break;\n        }\n        queue.enqueue(next);\n      }\n    });\n  }\n\n  std::vector<std::thread> dequeuers;\n  for (int n = 0; n < 5; ++n) {\n    dequeuers.emplace_back([&] {\n      folly::coro::blockingWait([&]() -> folly::coro::Task<void> {\n        while (++seen <= 100) {\n          co_await queue.dequeue();\n        }\n      }());\n    });\n  }\n\n  for (int n = 0; n < 5; ++n) {\n    enqueuers[n].join();\n  }\n  for (int n = 0; n < 5; ++n) {\n    dequeuers[n].join();\n  }\n\n  EXPECT_TRUE(queue.empty());\n}\n\nTEST(UnboundedQueueTest, CancelledDequeueThrowsOperationCancelled) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::UnboundedQueue<int> queue;\n    folly::CancellationSource cancelSource;\n\n    co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<void> {\n          EXPECT_THROW(\n              (co_await folly::coro::co_withCancellation(\n                  cancelSource.getToken(), queue.dequeue())),\n              folly::OperationCancelled);\n        }(),\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          co_await folly::coro::co_reschedule_on_current_executor;\n          cancelSource.requestCancellation();\n        }());\n  }());\n}\n\nTEST(UnboundedQueueTest, CancelledDequeueCompletesNormallyIfAnItemIsAvailable) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::UnboundedQueue<int> queue;\n    folly::CancellationSource cancelSource;\n    cancelSource.requestCancellation();\n\n    queue.enqueue(123);\n\n    int result = co_await folly::coro::co_withCancellation(\n        cancelSource.getToken(), queue.dequeue());\n    EXPECT_EQ(123, result);\n  }());\n}\n\nTEST(UnboundedQueueTest, TryDequeue) {\n  folly::coro::UnboundedQueue<int> queue;\n\n  queue.enqueue(42);\n  EXPECT_EQ(42, queue.try_dequeue());\n\n  folly::ManualExecutor ex;\n\n  auto fut = co_withExecutor(&ex, queue.dequeue()).start();\n  ex.drain();\n  EXPECT_FALSE(fut.isReady());\n\n  queue.enqueue(13);\n  ex.drain();\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_EQ(std::move(fut).get(), 13);\n}\n\nTEST(UnboundedQueueTest, TryPeekSingleConsumer) {\n  folly::coro::UnboundedQueue<int, false, true> queue;\n  EXPECT_EQ(nullptr, queue.try_peek());\n\n  queue.enqueue(42);\n  EXPECT_EQ(42, *queue.try_peek());\n\n  queue.enqueue(13);\n  EXPECT_EQ(42, *queue.try_peek());\n\n  queue.enqueue(63);\n  EXPECT_EQ(42, *queue.try_peek());\n\n  EXPECT_EQ(42, queue.try_dequeue());\n  EXPECT_EQ(13, *queue.try_peek());\n\n  EXPECT_EQ(13, queue.try_dequeue());\n  EXPECT_EQ(63, *queue.try_peek());\n\n  EXPECT_EQ(63, queue.try_dequeue());\n  EXPECT_EQ(nullptr, queue.try_peek());\n}\n\nTEST(UnboundedQueueTest, TryDequeueFor) {\n  folly::coro::blockingWait([]() -> folly::coro::Task<void> {\n    folly::coro::UnboundedQueue<int> queue;\n\n    EXPECT_THROW(\n        (co_await queue.co_try_dequeue_for(1ms)), folly::OperationCancelled);\n\n    queue.enqueue(42);\n    auto val = co_await queue.co_try_dequeue_for(1ms);\n    EXPECT_EQ(val, 42);\n\n    co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          queue.enqueue(43);\n        }(),\n        [&]() -> folly::coro::Task<void> {\n          EXPECT_TRUE(queue.empty());\n          val = co_await queue.co_try_dequeue_for(1h);\n          EXPECT_EQ(val, 43);\n        }());\n\n    folly::CancellationSource cancelSource;\n    co_await folly::coro::collectAll(\n        [&]() -> folly::coro::Task<void> {\n          co_await folly::coro::co_reschedule_on_current_executor;\n          cancelSource.requestCancellation();\n        }(),\n        [&]() -> folly::coro::Task<void> {\n          EXPECT_THROW(\n              (co_await folly::coro::co_withCancellation(\n                  cancelSource.getToken(), queue.co_try_dequeue_for(1h))),\n              folly::OperationCancelled);\n        }());\n  }());\n}\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/ValueOrErrorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Try.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Result.h>\n#include <folly/coro/ValueOrError.h>\n#include <folly/coro/ValueOrFatal.h>\n#include <folly/coro/ViaIfAsync.h>\n#include <folly/coro/safe/NowTask.h>\n\n/// Besides `value_or_error_or_stopped`, this test also covers the\n/// `folly::result<T>` integration with `coro::co_result` from `coro/Result.h`.\n\n#if FOLLY_HAS_RESULT\n\nnamespace folly::coro {\n\nCO_TEST(ValueOrErrorTest, valueOrErrorOrStoppedOfError) {\n  auto voidErrorTask = []() -> now_task<void> {\n    co_yield co_error(std::runtime_error(\"foo\"));\n  };\n  { // Capture the error\n    auto res = co_await value_or_error_or_stopped(voidErrorTask());\n    EXPECT_STREQ(\"foo\", get_exception<std::runtime_error>(res)->what());\n  }\n  { // Also test `coro::co_result` integration\n    auto res = co_await value_or_error_or_stopped([&]() -> now_task<void> {\n      co_yield co_result(co_await value_or_error_or_stopped(voidErrorTask()));\n    }());\n    EXPECT_STREQ(\"foo\", get_exception<std::runtime_error>(res)->what());\n  }\n}\n\nCO_TEST(ValueOrErrorTest, valueOrErrorOrStoppedOfValue) {\n  // Return a move-only thing to make sure we don't copy\n  auto valueTask = []() -> now_task<std::unique_ptr<int>> {\n    co_return std::make_unique<int>(1337);\n  };\n  { // Capture the value\n    auto res = co_await value_or_error_or_stopped(valueTask());\n    // Real code should use `co_await co_ready(res)` to unpack!\n    EXPECT_EQ(1337, *res.value_or_throw());\n  }\n  { // Also test `coro::co_result` integration\n    auto res = co_await value_or_error_or_stopped(\n        [&]() -> now_task<std::unique_ptr<int>> {\n          co_yield co_result(co_await value_or_error_or_stopped(valueTask()));\n        }());\n    EXPECT_EQ(1337, *res.value_or_throw());\n  }\n  { // Also test `co_result` with `value_only_result`\n    auto res = co_await value_or_error_or_stopped([&]() -> now_task<int> {\n      co_yield co_result(value_only_result<int>{42});\n    }());\n    EXPECT_EQ(42, res.value_or_throw());\n  }\n}\n\nCO_TEST(ValueOrErrorTest, valueOrErrorOrStoppedOfVoid) {\n  int numAwaited = 0;\n  auto voidTask = [&]() -> now_task<void> {\n    ++numAwaited;\n    co_return;\n  };\n  { // Capturing a \"value\" completion\n    auto res = co_await value_or_error_or_stopped(voidTask());\n    static_assert(std::is_same_v<decltype(res), result<>>);\n    EXPECT_TRUE(res.has_value());\n    EXPECT_EQ(1, numAwaited);\n  }\n  { // Also test `coro::co_result` integration\n    auto res = co_await value_or_error_or_stopped([&]() -> now_task<void> {\n      co_yield co_result(co_await value_or_error_or_stopped(voidTask()));\n    }());\n    static_assert(std::is_same_v<decltype(res), result<>>);\n    EXPECT_TRUE(res.has_value());\n    EXPECT_EQ(2, numAwaited);\n  }\n}\n\nCO_TEST(ValueOrErrorTest, valueOrErrorOrStoppedStopped) {\n  auto stoppedTask = [&]() -> now_task<void> {\n    co_yield co_stopped_may_throw;\n    LOG(FATAL) << \"not reached\";\n  };\n  { // Capturing a \"stopped\" completion\n    auto res = co_await value_or_error_or_stopped(stoppedTask());\n    EXPECT_TRUE(res.has_stopped());\n  }\n  { // Also test `coro::co_result` integration\n    auto res = co_await value_or_error_or_stopped([&]() -> now_task<void> {\n      co_yield co_result(co_await value_or_error_or_stopped(stoppedTask()));\n      LOG(FATAL) << \"not reached\";\n    }());\n    EXPECT_TRUE(res.has_stopped());\n  }\n}\n\nTEST(ValueOrErrorTest, coNothrowValueOrErrorOrStopped) {\n  bool ran = []<typename T>(T) { // `requires` does SFINAE inside templates\n    auto errorTask = [&]() -> now_task<> {\n      co_yield co_error(std::runtime_error(\"foo\"));\n    };\n    (void)co_nothrow(errorTask()); // compile\n    (void)value_or_error_or_stopped(errorTask()); // compiles\n    static_assert(!requires {\n      co_nothrow(value_or_error_or_stopped(errorTask()));\n    });\n#if 0 // manual test equivalent of above `static_assert`\n    (void)co_nothrow(value_or_error_or_stopped(errorTask())); // constraint failure\n#endif\n    return true;\n  }(5);\n  EXPECT_TRUE(ran); // it's easy to forget to call the lambda\n}\n\nstruct ThrowingMove {\n  ThrowingMove(const ThrowingMove&) = default;\n  ThrowingMove& operator=(const ThrowingMove&) = default;\n  // NOLINTNEXTLINE(performance-noexcept-move-constructor)\n  ThrowingMove(ThrowingMove&&) {}\n  // NOLINTNEXTLINE(performance-noexcept-move-constructor)\n  ThrowingMove& operator=(ThrowingMove&&) { return *this; }\n};\nstruct NothrowMove {};\n\ntemplate <typename T>\nusing TestAwaiter = detail::ResultAwaiter<TaskWithExecutor<T>>;\n\n// While it's unclear if anything cares about `await_resume()` `noexcept`\n// discipline, it's easy enough to check.\nstatic_assert(noexcept(FOLLY_DECLVAL(TestAwaiter<int>).await_resume()));\nstatic_assert(noexcept(FOLLY_DECLVAL(TestAwaiter<NothrowMove>).await_resume()));\nstatic_assert(\n    !noexcept(FOLLY_DECLVAL(TestAwaiter<ThrowingMove>).await_resume()));\n\n// Awaiter lacking noexcept on `await_suspend`, for manual test below.\nstruct ThrowingAwaitSuspendAwaitable {\n  bool await_ready() noexcept { return false; }\n  void await_suspend(coro::coroutine_handle<>) {} // not noexcept\n  int await_resume() noexcept { return 0; }\n  Try<int> await_resume_try() noexcept { return Try<int>{0}; }\n  friend ThrowingAwaitSuspendAwaitable&& co_viaIfAsync(\n      const folly::Executor::KeepAlive<>&,\n      ThrowingAwaitSuspendAwaitable&& a) noexcept {\n    return std::move(a);\n  }\n};\n\nCO_TEST(ValueOrErrorTest, requiresNoexceptAwait) {\n#if 0 // Manual test: \"value-only await requires noexcept await_suspend()\"\n  (void)co_await value_or_error(ThrowingAwaitSuspendAwaitable{});\n#endif\n  co_return;\n}\n\nCO_TEST(ValueOrErrorTest, coAwaitTryRequiresNoexceptAwait) {\n#if 0 // Manual test: \"value-only await requires noexcept await_suspend()\"\n  (void)co_await co_awaitTry(ThrowingAwaitSuspendAwaitable{});\n#endif\n  co_return;\n}\n\n// Deliberately redundant with `ValueOrFatalTest.ValueOrErrorComposition`.\n// Do NOT remove.\n//\n// `value_or_error` must not activate the \"bypass\" mechanism when wrapping a\n// value-only awaitable like `value_or_fatal`. Otherwise, `OperationCancelled`\n// would be intercepted at the promise level, before the inner awaitable's\n// `on_stopped` policy could substitute the default value.\nCO_TEST(ValueOrErrorTest, valueOrErrorAroundValueOnlyAwaitable) {\n  auto coStopped = []() -> value_or_fatal<Task<int>, on_stopped<99>> {\n    co_yield co_stopped_may_throw;\n    co_return -1;\n  };\n  EXPECT_EQ(99, (co_await value_or_error(coStopped())).value_only());\n  EXPECT_EQ(\n      99, (co_await value_or_error_or_stopped(coStopped())).value_or_throw());\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/ValueOrFatalTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncScope.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/ValueOrError.h>\n#include <folly/coro/ValueOrFatal.h>\n#include <folly/coro/safe/NowTask.h>\n#include <folly/coro/safe/SafeTask.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\n\n// Helper for fatal-on-any-exception wrapping of awaitables\ntemplate <typename Awaitable>\nauto fatal_on_non_value(Awaitable awaitable) {\n  return detail::ValueOrFatalAwaitable<\n      Awaitable,\n      on_stopped_and_error<will_fatal>>(\n      folly::ext::must_use_immediately_unsafe_mover(std::move(awaitable))());\n}\n\n// Check `await_result_t` for `ValueOrFatalAwaitable`-wrapped awaitables.\nstatic_assert(std::is_same_v<\n              int,\n              await_result_t<decltype(fatal_on_non_value(\n                  FOLLY_DECLVAL(TaskWithExecutor<int>)))>>);\nstatic_assert(std::is_same_v<\n              int,\n              await_result_t<decltype(fatal_on_non_value(\n                  FOLLY_DECLVAL(now_task_with_executor<int>)))>>);\n\n// Check whether `semi_await_result_t` is available for various value\n// categories.  This is part of verifying that `ValueOrFatalAwaitable` correctly\n// preserves the immediately-awaitable property.\ntemplate <typename T, typename Res = int>\ninline constexpr bool test_semi_await_result_v =\n    std::is_same_v<detected_t<semi_await_result_t, T>, Res>;\n\nstatic_assert(test_semi_await_result_v<Task<int>>);\nstatic_assert(!test_semi_await_result_v<Task<int>&>);\nstatic_assert(test_semi_await_result_v<Task<int>&&>);\n\nusing fatal_on_non_value_of_Task =\n    decltype(fatal_on_non_value(FOLLY_DECLVAL(Task<int>)));\nusing fatal_on_non_value_of_now_task =\n    decltype(fatal_on_non_value(FOLLY_DECLVAL(now_task<int>)));\n\nstatic_assert(test_semi_await_result_v<fatal_on_non_value_of_Task, int>);\nstatic_assert(!test_semi_await_result_v<fatal_on_non_value_of_Task&, int>);\nstatic_assert(test_semi_await_result_v<fatal_on_non_value_of_Task&&, int>);\nstatic_assert(test_semi_await_result_v<fatal_on_non_value_of_now_task, int>);\nstatic_assert(!test_semi_await_result_v<fatal_on_non_value_of_now_task&, int>);\nstatic_assert(!test_semi_await_result_v<fatal_on_non_value_of_now_task&&, int>);\n\n// Check `value_only_awaitable_v` is set correctly, and that `await_resume_try`\n// is NOT available (we want compile errors for\n// `co_awaitTry(value_or_fatal(...))`).\nstatic_assert(!value_only_awaitable_v<Task<int>>);\nstatic_assert(value_only_awaitable_v<detail::TryAwaitable<Task<int>>>);\nstatic_assert(value_only_awaitable_v<fatal_on_non_value_of_Task>);\nstatic_assert(!value_only_awaitable_v<detail::NothrowAwaitable<Task<int>>>);\n\n// Verify `await_resume_result()` is available on `ValueOrFatalAwaiter` and\n// returns `value_only_result<T>`. This enables composition with\n// `value_or_error`.\nstatic_assert(\n    detail::is_awaiter_result<\n        awaiter_type_t<value_or_fatal<TaskWithExecutor<int>, on_stopped<0>>>>);\nstatic_assert(\n    std::is_same_v<\n        value_only_result<int>,\n        decltype(FOLLY_DECLVAL(\n                     awaiter_type_t<\n                         value_or_fatal<TaskWithExecutor<int>, on_stopped<0>>>&)\n                     .await_resume_result())>);\nstatic_assert(\n    std::is_same_v<\n        value_only_result<void>,\n        decltype(FOLLY_DECLVAL(\n                     awaiter_type_t<\n                         value_or_fatal<TaskWithExecutor<>, on_stopped_void>>&)\n                     .await_resume_result())>);\n\n// Verify `await_resume_try()` is NOT available (see comment in ValueOrFatal.h)\nstatic_assert(\n    !detail::is_awaiter_try<\n        awaiter_type_t<value_or_fatal<TaskWithExecutor<int>, on_stopped<0>>>>);\n\nstruct MyErr : std::exception {};\n\n#if 0 // Manual test: value_or_fatal<Task<T>> requires T to be noexcept-movable\nstruct ThrowOnMove {\n  ThrowOnMove() = default;\n  [[noreturn]] ThrowOnMove(ThrowOnMove&&) { throw MyErr{}; }\n};\nvalue_or_fatal<Task<ThrowOnMove>, on_stopped_and_error<will_fatal>>\nthrowOnMoveTask() {\n  co_return ThrowOnMove{};\n}\n[[maybe_unused]] void instantiateThrowOnMoveTask() {\n  blockingWait(throwOnMoveTask());\n}\n#endif\n\ntemplate <typename TaskT>\nnow_task<void> checkFatalOnNonValue() {\n  auto coThrow = []() -> TaskT {\n    throw MyErr{};\n    co_return;\n  };\n  EXPECT_THROW(co_await coThrow(), MyErr);\n  EXPECT_THROW(blockingWait(coThrow()), MyErr);\n  EXPECT_DEATH({ blockingWait(fatal_on_non_value(coThrow())); }, \"MyErr\");\n\n  // Composition with `co_awaitTry()`.\n  //\n  // (1) The order `co_awaitTry(fatal_on_non_value())` makes no sense, the\n  // exception would fatal before getting to the `co_awaitTry`, and it shouldn't\n  // compile since `ValueOrFatalAwaiter` lacks `await_resume_try`.\n  //\n  // NB: If your metaprogramming task requires this for uniformity, the good\n  // path forward would be to ignore legacy `Try` and to instead add\n  // `await_resume_result` returning `value_only_result`.  This way, you get\n  // uniform UX without paying for the error path.  If implementing this, might\n  // as well add an `EXPECT_DEATH` test too.\n  static_assert(detail::is_awaitable_try<semi_await_awaitable_t<TaskT>>);\n  static_assert(\n      !detail::is_awaitable_try<\n          semi_await_awaitable_t<decltype(fatal_on_non_value(coThrow()))>>);\n  // (2) The opposite order \"just works\", no exception is thrown.\n  auto ew = (co_await fatal_on_non_value(co_awaitTry(coThrow()))).exception();\n  EXPECT_NE(nullptr, ew.template get_exception<MyErr>());\n\n  // Composition with `co_nothrow()`.\n  //\n  // (1) Putting `fatal_on_non_value` around `co_nothrow` doesn't compile\n  // because `NothrowAwaitable` isn't an actual awaitable, and is special-cased\n  // in some places.  This is fine, since it's unclear if any of the possible\n  // behaviors for this combination are \"expected\" to the user.\n  using ValueOrFatalOfNothrow =\n      decltype(fatal_on_non_value(co_nothrow(coThrow())));\n  static_assert(!test_semi_await_result_v<ValueOrFatalOfNothrow, void>);\n  static_assert(!is_awaitable_v<ValueOrFatalOfNothrow>);\n  static_assert(\n      std::is_same_v<\n          ValueOrFatalOfNothrow,\n          detail::ValueOrFatalAwaitable<\n              detail::NothrowAwaitable<TaskT>,\n              on_stopped_and_error<will_fatal>>>);\n  // ... but yes, this works\n  static_assert(\n      test_semi_await_result_v<decltype(fatal_on_non_value(coThrow())), void>);\n  // ... and yes, the problem is with `co_nothrow`\n  static_assert(\n      !test_semi_await_result_v<decltype(co_nothrow(coThrow())), void>);\n  static_assert(!is_awaitable_v<decltype(co_nothrow(coThrow()))>);\n  // (2) The reverse order is banned for reasons `NothrowAwaitable` describes\n  bool ran = [&]<typename T>(T) { // `requires` does SFINAE inside templates\n    (void)co_nothrow(coThrow()); // compiles\n    (void)fatal_on_non_value(coThrow()); // compiles\n    static_assert(requires { co_nothrow(coThrow()); }); // same as prior line\n    static_assert(!requires { co_nothrow(fatal_on_non_value(coThrow())); });\n#if 0 // manual test equivalent of above `static_assert`\n    (void)co_nothrow(fatal_on_non_value(coThrow())); // constraint failure\n#endif\n    return true;\n  }(5);\n  EXPECT_TRUE(ran); // it's easy to forget to call the lambda\n}\n\nCO_TEST(ValueOrFatalTest, task) {\n  co_await checkFatalOnNonValue<Task<void>>();\n}\n\nCO_TEST(ValueOrFatalTest, nowTask) {\n  co_await checkFatalOnNonValue<now_task<void>>();\n}\n\n// Test that `now_task` remains immovable when wrapped in `fatal_on_non_value`.\ntemplate <typename T>\nusing fatal_on_non_value_result_t =\n    decltype(fatal_on_non_value(FOLLY_DECLVAL(T)));\n// SFINAE check for whether `T` can be `fatal_on_non_value`-wrapped.\n// For `now_task` we expect: prvalue -- yes, ref -- no.\ntemplate <typename T>\ninline constexpr bool test_make_fatal_on_non_value_v = std::is_same_v<\n    detected_t<fatal_on_non_value_result_t, T>,\n    detail::ValueOrFatalAwaitable<\n        std::remove_reference_t<T>,\n        on_stopped_and_error<will_fatal>>>;\n\nCO_TEST(ValueOrFatalTest, nowTaskIsImmediate) {\n  auto myNowTask = []() -> now_task<int> { co_return 5; };\n  EXPECT_EQ(5, co_await fatal_on_non_value(myNowTask()));\n\n  static_assert(test_make_fatal_on_non_value_v<Task<int>>);\n  static_assert(!test_make_fatal_on_non_value_v<Task<int>&>);\n  static_assert(test_make_fatal_on_non_value_v<Task<int>&&>);\n  static_assert(test_make_fatal_on_non_value_v<now_task<int>>);\n  static_assert(!test_make_fatal_on_non_value_v<now_task<int>&>);\n  static_assert(!test_make_fatal_on_non_value_v<now_task<int>&&>);\n#if 0 // The above asserts approximate this manual test\n  auto t = myNowTask();\n  fatal_on_non_value(std::move(t));\n#endif\n\n  using MyValueOrFatalT =\n      decltype(fatal_on_non_value(FOLLY_DECLVAL(Task<int>)));\n  static_assert(std::is_same_v<int, semi_await_result_t<MyValueOrFatalT>>);\n  static_assert(!is_detected_v<semi_await_result_t, MyValueOrFatalT&>);\n  static_assert(std::is_same_v<int, semi_await_result_t<MyValueOrFatalT&&>>);\n  using MyValueOrFatalNowT = decltype(fatal_on_non_value(myNowTask()));\n  static_assert(std::is_same_v<int, semi_await_result_t<MyValueOrFatalNowT>>);\n  static_assert(!is_detected_v<semi_await_result_t, MyValueOrFatalNowT&>);\n  static_assert(!is_detected_v<semi_await_result_t, MyValueOrFatalNowT&&>);\n#if 0 // The above asserts approximate this manual test\n  auto t = fatal_on_non_value(myNowTask());\n  co_await std::move(t);\n#endif\n}\n\n// Check `awaiter_type_t` and `await_result_t` for `value_or_fatal`.\ntemplate <typename Inner, typename Res = void>\nconsteval bool check_value_or_fatal_awaiter() {\n  static_assert(\n      std::is_same_v<\n          detail::ValueOrFatalAwaiter<Inner, on_stopped_and_error<will_fatal>>,\n          awaiter_type_t<\n              value_or_fatal<Inner, on_stopped_and_error<will_fatal>>>>);\n  static_assert(\n      std::is_same_v<\n          Res,\n          await_result_t<\n              value_or_fatal<Inner, on_stopped_and_error<will_fatal>>>>);\n  return true;\n}\nstatic_assert(check_value_or_fatal_awaiter<TaskWithExecutor<void>>());\nstatic_assert(check_value_or_fatal_awaiter<now_task_with_executor<void>>());\nstatic_assert(check_value_or_fatal_awaiter<TaskWithExecutor<float>, float>());\nstatic_assert(\n    check_value_or_fatal_awaiter<now_task_with_executor<float>, float>());\n\n// Check whether `semi_await_result_t` is available for various value\n// categories.  This is part of verifying that wrapping with `value_or_fatal<>`\n// correctly preserves the immediately-awaitable property.\nstatic_assert(test_semi_await_result_v<\n              value_or_fatal<Task<int>, on_stopped_and_error<will_fatal>>,\n              int>);\nstatic_assert(!test_semi_await_result_v<\n              value_or_fatal<Task<int>, on_stopped_and_error<will_fatal>>&,\n              int>);\nstatic_assert(test_semi_await_result_v<\n              value_or_fatal<Task<int>, on_stopped_and_error<will_fatal>>&&,\n              int>);\nstatic_assert(test_semi_await_result_v<\n              value_or_fatal<now_task<int>, on_stopped_and_error<will_fatal>>,\n              int>);\nstatic_assert(!test_semi_await_result_v<\n              value_or_fatal<now_task<int>, on_stopped_and_error<will_fatal>>&,\n              int>);\nstatic_assert(!test_semi_await_result_v<\n              value_or_fatal<now_task<int>, on_stopped_and_error<will_fatal>>&&,\n              int>);\n\n// Check the `value_only_awaitable_v` trait is applied correctly by\n// `value_or_fatal`\nstatic_assert(!value_only_awaitable_v<Task<int>>);\nstatic_assert(value_only_awaitable_v<\n              value_or_fatal<Task<int>, on_stopped_and_error<will_fatal>>>);\nstatic_assert(!value_only_awaitable_v<now_task<int>>);\nstatic_assert(value_only_awaitable_v<\n              value_or_fatal<now_task<int>, on_stopped_and_error<will_fatal>>>);\nstatic_assert(!value_only_awaitable_v<TaskWithExecutor<int>>);\nstatic_assert(\n    value_only_awaitable_v<value_or_fatal<\n        TaskWithExecutor<int>,\n        on_stopped_and_error<will_fatal>>>);\nstatic_assert(!value_only_awaitable_v<now_task_with_executor<int>>);\nstatic_assert(\n    value_only_awaitable_v<value_or_fatal<\n        now_task_with_executor<int>,\n        on_stopped_and_error<will_fatal>>>);\n\n// Test on_stopped_and_error<will_fatal>: both stopped and error terminate\ntemplate <typename TaskT>\nnow_task<void> checkValueOrFatalAllFatal() {\n  // Error path terminates\n  auto coFatalThrow =\n      []() -> value_or_fatal<TaskT, on_stopped_and_error<will_fatal>> {\n    throw MyErr{};\n    co_return;\n  };\n  EXPECT_DEATH({ co_await coFatalThrow(); }, \"MyErr\");\n  EXPECT_DEATH(\n      {\n        co_await co_withExecutor(co_await co_current_executor, coFatalThrow());\n      },\n      \"MyErr\");\n\n  // Stopped path also terminates (tests the other branch in value_only_default)\n  auto coFatalStopped =\n      []() -> value_or_fatal<TaskT, on_stopped_and_error<will_fatal>> {\n    co_yield co_stopped_may_throw;\n  };\n  EXPECT_DEATH({ co_await coFatalStopped(); }, \"OperationCancelled\");\n}\n\nCO_TEST(ValueOrFatalTest, allFatalTask) {\n  co_await checkValueOrFatalAllFatal<Task<void>>();\n\n  // We want to check `value_or_fatal` for an `AsyncScope` task because\n  // this uses a different code path to prepare the awaitable, specifically:\n  //   co_withAsyncStack(yourTaskWithExecutor)\n  auto coThrowFromScopeTask = []() -> now_task<> {\n    AsyncScope scope{/*throwOnJoin*/ true};\n    scope.add(co_withExecutor(\n        co_await co_current_executor,\n        []() -> value_or_fatal<Task<>, on_stopped_and_error<will_fatal>> {\n          throw MyErr{};\n          co_return;\n        }()));\n    co_await scope.joinAsync();\n  };\n  EXPECT_DEATH(co_await coThrowFromScopeTask(), \"MyErr\");\n}\n\nCO_TEST(ValueOrFatalTest, allFatalNowTask) {\n  co_await checkValueOrFatalAllFatal<now_task<>>();\n}\n\n// Normal completion (no exception) works correctly\nCO_TEST(ValueOrFatalTest, successfulCompletion) {\n  auto coSuccessInt = []() -> value_or_fatal<Task<int>, on_stopped<0>> {\n    co_return 42;\n  };\n  EXPECT_EQ(42, co_await coSuccessInt());\n\n  bool ran = false;\n  auto coSuccessVoid = [&]() -> value_or_fatal<Task<>, on_stopped_void> {\n    ran = true;\n    co_return;\n  };\n  co_await coSuccessVoid();\n  EXPECT_TRUE(ran);\n}\n\ntemplate <typename ExceptionType, auto Policy>\nvalue_or_fatal<now_task<int>, Policy> intTaskThrows() {\n  throw ExceptionType{};\n  co_return -1;\n}\n\ntemplate <typename ExceptionType, auto Policy>\nvalue_or_fatal<now_task<>, Policy> voidTaskThrows() {\n  throw ExceptionType{};\n  co_return;\n}\n\n// Policy substitutes value instead of fataling\nCO_TEST(ValueOrFatalTest, policySubstitutesValue) {\n  // stopped -> void\n  co_await voidTaskThrows<OperationCancelled, on_stopped_void>();\n  co_await voidTaskThrows<OperationCancelled, on_stopped<unit>>();\n\n  // stopped & error -> void\n  co_await voidTaskThrows<OperationCancelled, on_stopped_and_error<unit>>();\n  co_await voidTaskThrows<MyErr, on_stopped_and_error<unit>>();\n\n  // stopped -> int, error -> fatal\n  EXPECT_EQ(42, (co_await intTaskThrows<OperationCancelled, on_stopped<42>>()));\n  EXPECT_DEATH(\n      { blockingWait(intTaskThrows<MyErr, on_stopped<42>>()); }, \"MyErr\");\n\n  // stopped & error -> same int\n  EXPECT_EQ(\n      99,\n      (co_await intTaskThrows<OperationCancelled, on_stopped_and_error<99>>()));\n  EXPECT_EQ(99, (co_await intTaskThrows<MyErr, on_stopped_and_error<99>>()));\n\n  // on_stopped_and_error<V1, V2> lets stopped and error use distinct values\n  EXPECT_EQ( // stopped -> V1\n      1,\n      (co_await intTaskThrows<\n          OperationCancelled,\n          on_stopped_and_error<1, 2>>()));\n  EXPECT_EQ(2, (co_await intTaskThrows<MyErr, on_stopped_and_error<1, 2>>()));\n}\n\n// co_withExecutor(as_noexcept<Task>) makes as_noexcept<TaskWithExecutor>\nstatic_assert(\n    std::is_same_v<\n        value_or_fatal<TaskWithExecutor<int>, on_stopped_and_error<will_fatal>>,\n        decltype(co_withExecutor(\n            FOLLY_DECLVAL(Executor::KeepAlive<>),\n            FOLLY_DECLVAL(\n                value_or_fatal<\n                    Task<int>,\n                    on_stopped_and_error<will_fatal>>)))>);\n\n// Spot-check the relevant `safe_alias_of` specializations\nstatic_assert(\n    safe_alias::unsafe_closure_internal ==\n    lenient_safe_alias_of_v<detail::ValueOrFatalAwaitable<\n        safe_task<safe_alias::unsafe_closure_internal>,\n        on_stopped<unit>>>);\nstatic_assert(\n    safe_alias::maybe_value ==\n    strict_safe_alias_of_v<detail::ValueOrFatalAwaitable<\n        safe_task<safe_alias::maybe_value>,\n        on_stopped<unit>>>);\nstatic_assert(\n    safe_alias::maybe_value ==\n    lenient_safe_alias_of_v<detail::ValueOrFatalAwaitable<\n        safe_task<safe_alias::maybe_value>,\n        on_stopped<unit>>>);\nstatic_assert(\n    safe_alias::unsafe_member_internal ==\n    lenient_safe_alias_of_v<value_or_fatal<\n        safe_task<safe_alias::unsafe_member_internal>,\n        on_stopped_void>>);\nstatic_assert(\n    safe_alias::unsafe_member_internal ==\n    lenient_safe_alias_of_v<value_or_fatal<\n        safe_task_with_executor<safe_alias::unsafe_member_internal>,\n        on_stopped_void>>);\n\n// Test `value_or_error(value_or_fatal(...))` composition - verifies the\n// `await_resume_result()` protocol is used correctly.\nCO_TEST(ValueOrFatalTest, valueOrErrorComposition) {\n  { // value_or_error(value_or_fatal(...)) returns value_only_result<T>\n    auto coSuccess = []() -> value_or_fatal<Task<int>, on_stopped<0>> {\n      co_return 42;\n    };\n    auto res = co_await value_or_error(coSuccess());\n    static_assert(std::is_same_v<decltype(res), value_only_result<int>>);\n    EXPECT_EQ(42, res.value_only());\n  }\n\n  { // Also works with void tasks\n    bool ran = false;\n    auto coVoid = [&]() -> value_or_fatal<Task<>, on_stopped_void> {\n      ran = true;\n      co_return;\n    };\n    auto voidRes = co_await value_or_error(coVoid());\n    static_assert(std::is_same_v<decltype(voidRes), value_only_result<void>>);\n    EXPECT_TRUE(ran);\n  }\n\n  { // Stopped policy -- intentionally cloned in ValueOrErrorTest.cpp\n    auto coStopped = []() -> value_or_fatal<Task<int>, on_stopped<99>> {\n      co_yield co_stopped_may_throw;\n      co_return -1;\n    };\n    EXPECT_EQ(99, (co_await value_or_error(coStopped())).value_only());\n  }\n\n  { // Error policy substitution\n    auto coError = []() -> value_or_fatal<Task<int>, on_stopped_and_error<42>> {\n      co_yield co_error{MyErr{}};\n      co_return -1;\n    };\n    EXPECT_EQ(42, (co_await value_or_error(coError())).value_only());\n  }\n}\n\n} // namespace folly::coro\n\n#endif\n"
  },
  {
    "path": "folly/coro/test/WithAsyncStackTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Traits.h>\n#include <folly/coro/WithAsyncStack.h>\n#include <folly/coro/detail/CurrentAsyncFrame.h>\n#include <folly/coro/safe/NowTask.h>\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly::coro {\nnamespace {\n\nstruct Err {};\n\n// The parameters are `WithAsyncStackAwaiter::await_suspend`'s test matrix\ntemplate <bool IsNoexcept, bool ReturnsBool = false>\nstruct TestAwaiter {\n  bool await_ready() noexcept { return false; }\n\n  template <typename Promise>\n  auto await_suspend(coroutine_handle<Promise>) noexcept(IsNoexcept) {\n    if constexpr (!IsNoexcept) {\n      throw Err{};\n    }\n    if constexpr (ReturnsBool) {\n      return false; // Don't actually suspend\n    }\n  }\n\n  void await_resume() noexcept {}\n\n  // Verify wrapped awaiter noexcept matches inner awaiter noexcept.\n  static constexpr void check_wrapped() {\n    using Wrapped = decltype(get_awaiter(co_withAsyncStack(TestAwaiter{})));\n    static_assert(\n        IsNoexcept ==\n        noexcept(std::declval<Wrapped&>().await_suspend(\n            coroutine_handle<now_task<int>::promise_type>{})));\n    static_assert(\n        std::is_same_v<Wrapped, detail::WithAsyncStackAwaiter<TestAwaiter>>);\n  }\n};\n\nCO_TEST(WithAsyncStackTest, AwaitSuspendNoexceptPropagates) {\n  // Wrapped awaiter is noexcept iff inner awaiter is noexcept.\n  TestAwaiter</*IsNoexcept=*/true>::check_wrapped();\n  TestAwaiter</*IsNoexcept=*/false>::check_wrapped();\n  TestAwaiter</*IsNoexcept=*/true, /*ReturnsBool=*/true>::check_wrapped();\n  TestAwaiter</*IsNoexcept=*/false, /*ReturnsBool=*/true>::check_wrapped();\n\n  auto& frameBefore = co_await detail::co_current_async_stack_frame;\n\n  // Exception from inner await_suspend propagates, and async stack is restored.\n  EXPECT_THROW(co_await TestAwaiter</*IsNoexcept=*/false>{}, Err);\n\n  // Async stack frame should be restored after the exception.\n  auto& frameAfter = co_await detail::co_current_async_stack_frame;\n  EXPECT_EQ(&frameBefore, &frameAfter);\n}\n\n} // namespace\n} // namespace folly::coro\n\n#endif // FOLLY_HAS_COROUTINES\n"
  },
  {
    "path": "folly/crypto/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"lt_hash\",\n    srcs = [\n        \"LtHash.cpp\",\n    ],\n    headers = [\n        \"LtHash.h\",\n        \"LtHash-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:cpu_id\",\n        \"//folly:memory\",\n    ],\n    exported_deps = [\n        \":blake2xb\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly/crypto/detail:lt_hash_internal\",\n        \"//folly/crypto/detail:math_operation_avx2_disable\",  # @manual\n        \"//folly/crypto/detail:math_operation_simple\",  # @manual\n        \"//folly/crypto/detail:math_operation_sse2_disable\",  # @manual\n        \"//folly/io:iobuf\",\n        \"//folly/lang:bits\",\n    ],\n    exported_external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lt_hash_avx2\",\n    srcs = [\n        \"LtHash.cpp\",\n    ],\n    headers = [\n        \"LtHash.h\",\n        \"LtHash-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:cpu_id\",\n        \"//folly:memory\",\n    ],\n    exported_deps = [\n        \":blake2xb\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly/crypto/detail:lt_hash_internal\",\n        \"//folly/crypto/detail:math_operation_avx2\",  # @manual\n        \"//folly/crypto/detail:math_operation_simple\",  # @manual\n        \"//folly/crypto/detail:math_operation_sse2\",  # @manual\n        \"//folly/io:iobuf\",\n        \"//folly/lang:bits\",\n    ],\n    exported_external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"blake2xb\",\n    srcs = [\n        \"Blake2xb.cpp\",\n    ],\n    headers = [\n        \"Blake2xb.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/lang:bits\",\n    ],\n    exported_deps = [\n        \"//folly:range\",\n    ],\n    exported_external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lt_hash_sse2\",\n    srcs = [\n        \"LtHash.cpp\",\n    ],\n    headers = [\n        \"LtHash.h\",\n        \"LtHash-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:cpu_id\",\n        \"//folly:memory\",\n    ],\n    exported_deps = [\n        \":blake2xb\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly/crypto/detail:lt_hash_internal\",\n        \"//folly/crypto/detail:math_operation_avx2_disable\",  # @manual\n        \"//folly/crypto/detail:math_operation_simple\",  # @manual\n        \"//folly/crypto/detail:math_operation_sse2\",  # @manual\n        \"//folly/io:iobuf\",\n        \"//folly/lang:bits\",\n    ],\n    exported_external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n"
  },
  {
    "path": "folly/crypto/Blake2xb.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n\n#include <folly/crypto/Blake2xb.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\nnamespace crypto {\n\nnamespace {\n\n// In libsodium 1.0.17, the crypto_generichash_blake2b_state struct was made\n// opaque. We have to copy the internal definition of the real struct here\n// so we can properly initialize it.\n#if SODIUM_LIBRARY_VERSION_MAJOR > 10 || \\\n    (SODIUM_LIBRARY_VERSION_MAJOR == 10 && SODIUM_LIBRARY_VERSION_MINOR >= 2)\nstruct _blake2b_state {\n  uint64_t h[8];\n  uint64_t t[2];\n  uint64_t f[2];\n  uint8_t buf[256];\n  size_t buflen;\n  uint8_t last_node;\n};\n#define __LIBSODIUM_BLAKE2B_OPAQUE__ 1\n#endif\n\nconstexpr std::array<uint64_t, 8> kBlake2bIV = {{\n    0x6a09e667f3bcc908ULL,\n    0xbb67ae8584caa73bULL,\n    0x3c6ef372fe94f82bULL,\n    0xa54ff53a5f1d36f1ULL,\n    0x510e527fade682d1ULL,\n    0x9b05688c2b3e6c1fULL,\n    0x1f83d9abfb41bd6bULL,\n    0x5be0cd19137e2179ULL,\n}};\n\nvoid initStateFromParams(\n    crypto_generichash_blake2b_state* _state,\n    const detail::Blake2xbParam& param,\n    ByteRange key) {\n#ifdef __LIBSODIUM_BLAKE2B_OPAQUE__\n  auto state = reinterpret_cast<_blake2b_state*>(_state);\n#else\n  crypto_generichash_blake2b_state* state = _state;\n#endif\n  auto p = reinterpret_cast<const uint64_t*>(&param);\n  for (int i = 0; i < 8; ++i) {\n    state->h[i] = kBlake2bIV.data()[i] ^ Endian::little(p[i]);\n  }\n  std::memset(\n      reinterpret_cast<uint8_t*>(state) + sizeof(state->h),\n      0,\n      sizeof(*state) - sizeof(state->h));\n  if (!key.empty()) {\n    if (key.size() < crypto_generichash_blake2b_KEYBYTES_MIN ||\n        key.size() > crypto_generichash_blake2b_KEYBYTES_MAX) {\n      throw std::runtime_error(\"invalid key size\");\n    }\n    std::array<uint8_t, 128> block;\n    memcpy(block.data(), key.data(), key.size());\n    memset(block.data() + key.size(), 0, block.size() - key.size());\n    crypto_generichash_blake2b_update(\n#ifdef __LIBSODIUM_BLAKE2B_OPAQUE__\n        reinterpret_cast<decltype(_state)>(state),\n#else\n        state,\n#endif\n        block.data(),\n        block.size());\n    sodium_memzero(block.data(), block.size()); // erase key from stack\n  }\n}\n} // namespace\n\nBlake2xb::Blake2xb()\n    : param_{},\n      state_{},\n      outputLengthKnown_{false},\n      initialized_{false},\n      finished_{false} {\n  static const int sodiumInitResult = sodium_init();\n  if (sodiumInitResult == -1) {\n    throw std::runtime_error(\"sodium_init() failed\");\n  }\n}\n\nBlake2xb::~Blake2xb() = default;\n\nvoid Blake2xb::init(\n    size_t outputLength,\n    ByteRange key /* = {} */,\n    ByteRange salt /* = {} */,\n    ByteRange personalization /* = {}*/) {\n  if (outputLength == kUnknownOutputLength) {\n    outputLengthKnown_ = false;\n    outputLength = kUnknownOutputLengthMagic;\n  } else if (outputLength > kMaxOutputLength) {\n    throw std::runtime_error(\"Output length too large\");\n  } else {\n    outputLengthKnown_ = true;\n  }\n  std::memset(&param_, 0, sizeof(param_));\n  param_.digestLength = crypto_generichash_blake2b_BYTES_MAX;\n  param_.keyLength = static_cast<uint8_t>(key.size());\n  param_.fanout = 1;\n  param_.depth = 1;\n  param_.xofLength = Endian::little(static_cast<uint32_t>(outputLength));\n  if (!salt.empty()) {\n    if (salt.size() != crypto_generichash_blake2b_SALTBYTES) {\n      throw std::runtime_error(\"Invalid salt length, must be 16 bytes\");\n    }\n    std::memcpy(param_.salt, salt.data(), sizeof(param_.salt));\n  }\n  if (!personalization.empty()) {\n    if (personalization.size() != crypto_generichash_blake2b_PERSONALBYTES) {\n      throw std::runtime_error(\n          \"Invalid personalization length, must be 16 bytes\");\n    }\n    std::memcpy(\n        param_.personal, personalization.data(), sizeof(param_.personal));\n  }\n  initStateFromParams(&state_, param_, key);\n  initialized_ = true;\n  finished_ = false;\n}\n\nvoid Blake2xb::update(ByteRange data) {\n  if (!initialized_) {\n    throw std::runtime_error(\"Must call init() before calling update()\");\n  } else if (finished_) {\n    throw std::runtime_error(\"Can't call update() after finish()\");\n  }\n  int res =\n      crypto_generichash_blake2b_update(&state_, data.data(), data.size());\n  if (res != 0) {\n    throw std::runtime_error(\"crypto_generichash_blake2b_update() failed\");\n  }\n}\n\nvoid Blake2xb::finish(MutableByteRange out) {\n  if (!initialized_) {\n    throw std::runtime_error(\"Must call init() before calling finish()\");\n  } else if (finished_) {\n    throw std::runtime_error(\"finish() already called\");\n  }\n\n  if (outputLengthKnown_) {\n    auto outLength = static_cast<uint32_t>(out.size());\n    if (outLength != Endian::little(param_.xofLength)) {\n      throw std::runtime_error(\"out.size() must equal output length\");\n    }\n  }\n\n  std::array<uint8_t, crypto_generichash_blake2b_BYTES_MAX> h0;\n  int res = crypto_generichash_blake2b_final(&state_, h0.data(), h0.size());\n  if (res != 0) {\n    throw std::runtime_error(\"crypto_generichash_blake2b_final() failed\");\n  }\n\n  param_.keyLength = 0;\n  param_.fanout = 0;\n  param_.depth = 0;\n  param_.leafLength = Endian::little(\n      static_cast<uint32_t>(crypto_generichash_blake2b_BYTES_MAX));\n  param_.innerLength = crypto_generichash_blake2b_BYTES_MAX;\n  size_t pos = 0;\n  size_t remaining = out.size();\n  while (remaining > 0) {\n    param_.nodeOffset = Endian::little(\n        static_cast<uint32_t>(pos / crypto_generichash_blake2b_BYTES_MAX));\n    size_t len = std::min(\n        static_cast<size_t>(crypto_generichash_blake2b_BYTES_MAX), remaining);\n    param_.digestLength = static_cast<uint8_t>(len);\n    initStateFromParams(&state_, param_, {} /* key */);\n    res = crypto_generichash_blake2b_update(&state_, h0.data(), h0.size());\n    if (res != 0) {\n      throw std::runtime_error(\"crypto_generichash_blake2b_update() failed\");\n    }\n    res = crypto_generichash_blake2b_final(&state_, out.data() + pos, len);\n    if (res != 0) {\n      throw std::runtime_error(\"crypto_generichash_blake2b_final() failed\");\n    }\n    pos += len;\n    remaining -= len;\n  }\n  finished_ = true;\n}\n\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/Blake2xb.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sodium.h>\n\n#include <folly/Range.h>\n\nnamespace folly {\nnamespace crypto {\n\nnamespace detail {\n\nstruct Blake2xbParam {\n  uint8_t digestLength; /*  1 */\n  uint8_t keyLength; /*  2 */\n  uint8_t fanout; /*  3 */\n  uint8_t depth; /*  4 */\n  uint32_t leafLength; /*  8 */\n  uint32_t nodeOffset; /* 12 */\n  uint32_t xofLength; /* 16 */\n  uint8_t nodeDepth; /* 17 */\n  uint8_t innerLength; /* 18 */\n  uint8_t reserved[14]; /* 32 */\n  uint8_t salt[16]; /* 48 */\n  uint8_t personal[16]; /* 64 */\n};\n\nstatic_assert(sizeof(Blake2xbParam) == 64, \"wrong sizeof(Blake2xbParam)\");\n\n} // namespace detail\n\n/**\n * An implementation of the BLAKE2x XOF (extendable output function)\n * hash function using BLAKE2b as the underlying hash. This hash function\n * can produce cryptographic hashes of arbitrary length (between 1 and 2^32 - 2\n * bytes) from inputs of arbitrary size. Like BLAKE2b, it can be keyed, and can\n * accept optional salt and personlization parameters.\n *\n * Note that if you need to compute hashes between 16 and 64 bytes in length,\n * you should use Blake2b instead - it's more efficient and you will have an\n * easier time interoperating with other languages, since implementations of\n * Blake2b are more common than implementations of Blake2xb. You can generate\n * a blake2b hash using the following functions from libsodium:\n * - crypto_generichash_blake2b()\n * - crypto_generichash_blake2b_salt_personal()\n */\nclass Blake2xb {\n public:\n  /**\n   * Minimum output hash size, if it is known in advance.\n   */\n  static constexpr size_t kMinOutputLength = 1;\n  /**\n   * Maximum output hash size, if it is known in advance.\n   */\n  static constexpr size_t kMaxOutputLength = 0xfffffffeULL;\n  /**\n   * If the amount of output data desired is not known in advance, use this\n   * constant as the outputLength parameter to init().\n   */\n  static constexpr size_t kUnknownOutputLength = 0;\n\n  /**\n   * Creates a new uninitialized Blake2xb instance. The init() method must\n   * be called before it can be used.\n   */\n  Blake2xb();\n\n  /**\n   * Shorthand for calling the no-argument constructor followed by\n   * newInstance.init(outputLength, key, salt, personlization).\n   */\n  explicit Blake2xb(\n      size_t outputLength,\n      ByteRange key = {},\n      ByteRange salt = {},\n      ByteRange personalization = {})\n      : Blake2xb() {\n    init(outputLength, key, salt, personalization);\n  }\n\n  ~Blake2xb();\n\n  /**\n   * Initializes the digest object. This must be called after a new instance\n   * is constructed and before update() is called. It can also be called on\n   * a previously-used instance to reset its internal state and reuse it for\n   * a new hash computation.\n   */\n  void init(\n      size_t outputLength,\n      ByteRange key = {},\n      ByteRange salt = {},\n      ByteRange personalization = {});\n\n  /**\n   * Hashes some more input data.\n   */\n  void update(ByteRange data);\n\n  /**\n   * Computes the final hash and stores it in the given output. The value of\n   * out.size() MUST equal the outputLength parameter that was given to the\n   * last init() call, except when the outputLength parameter was\n   * kUnknownOutputLength.\n   *\n   * WARNING: never compare the results of two Blake2xb.finish() calls\n   * using non-constant time comparison. The recommended way to compare\n   * cryptographic hashes is with sodium_memcmp() (or some other constant-time\n   * memory comparison function).\n   */\n  void finish(MutableByteRange out);\n\n  /**\n   * Convenience function, use this if you are hashing a single input buffer,\n   * the output length is known in advance, and the output data is allocated\n   * and ready to accept the hash value.\n   */\n  static void hash(\n      MutableByteRange out,\n      ByteRange data,\n      ByteRange key = {},\n      ByteRange salt = {},\n      ByteRange personalization = {}) {\n    Blake2xb d;\n    d.init(out.size(), key, salt, personalization);\n    d.update(data);\n    d.finish(out);\n  }\n\n private:\n  static constexpr size_t kUnknownOutputLengthMagic = 0xffffffffULL;\n\n  detail::Blake2xbParam param_;\n  crypto_generichash_blake2b_state state_;\n  bool outputLengthKnown_;\n  bool initialized_;\n  bool finished_;\n};\n\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\n# crypto targets require libsodium\nif (LIBSODIUM_FOUND)\n\nfolly_add_library(\n  NAME blake2xb\n  SRCS\n    Blake2xb.cpp\n  HEADERS\n    Blake2xb.h\n  DEPS\n    folly_lang_bits\n  EXPORTED_DEPS\n    folly_range\n  EXTERNAL_DEPS\n    ${LIBSODIUM_LIBRARIES}\n  EXTERNAL_INCLUDE_DIRS\n    ${LIBSODIUM_INCLUDE_DIRS}\n)\n\nfolly_add_library(\n  NAME lt_hash\n  SRCS\n    LtHash.cpp\n  HEADERS\n    LtHash-inl.h\n    LtHash.h\n  DEPS\n    folly_cpu_id\n    folly_memory\n  EXPORTED_DEPS\n    folly_crypto_blake2xb\n    folly_crypto_detail_lt_hash_internal\n    folly_crypto_detail_math_operation_avx2_disable\n    folly_crypto_detail_math_operation_simple\n    folly_crypto_detail_math_operation_sse2_disable\n    folly_io_iobuf\n    folly_lang_bits\n    folly_optional\n    folly_range\n  EXTERNAL_DEPS\n    ${LIBSODIUM_LIBRARIES}\n  EXTERNAL_INCLUDE_DIRS\n    ${LIBSODIUM_INCLUDE_DIRS}\n)\n\nfolly_add_library(\n  NAME lt_hash_avx2\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    LtHash.cpp\n  HEADERS\n    LtHash-inl.h\n    LtHash.h\n  DEPS\n    folly_cpu_id\n    folly_memory\n  EXPORTED_DEPS\n    folly_crypto_blake2xb\n    folly_crypto_detail_lt_hash_internal\n    folly_crypto_detail_math_operation_avx2\n    folly_crypto_detail_math_operation_simple\n    folly_crypto_detail_math_operation_sse2\n    folly_io_iobuf\n    folly_lang_bits\n    folly_optional\n    folly_range\n  EXTERNAL_DEPS\n    ${LIBSODIUM_LIBRARIES}\n  EXTERNAL_INCLUDE_DIRS\n    ${LIBSODIUM_INCLUDE_DIRS}\n)\n\nfolly_add_library(\n  NAME lt_hash_sse2\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    LtHash.cpp\n  HEADERS\n    LtHash-inl.h\n    LtHash.h\n  DEPS\n    folly_cpu_id\n    folly_memory\n  EXPORTED_DEPS\n    folly_crypto_blake2xb\n    folly_crypto_detail_lt_hash_internal\n    folly_crypto_detail_math_operation_avx2_disable\n    folly_crypto_detail_math_operation_simple\n    folly_crypto_detail_math_operation_sse2\n    folly_io_iobuf\n    folly_lang_bits\n    folly_optional\n    folly_range\n  EXTERNAL_DEPS\n    ${LIBSODIUM_LIBRARIES}\n  EXTERNAL_INCLUDE_DIRS\n    ${LIBSODIUM_INCLUDE_DIRS}\n)\n\nadd_subdirectory(detail)\n\nendif() # LIBSODIUM_FOUND\n"
  },
  {
    "path": "folly/crypto/LtHash-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstring>\n#include <stdexcept>\n\n#include <sodium.h>\n\n#include <folly/crypto/detail/LtHashInternal.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\nnamespace crypto {\n\nnamespace detail {\n\n/**\n * Implements bit twiddling operations for elements of size B bits.\n * Currently there are specializations for B = 16, B = 20, and B = 32.\n * All operations are performed on groups of elements packed into uint64_t\n * operands.\n *\n * When B == 16, each uint64_t contains 4 elements without any padding bits.\n * Both SSE2 and AVX2 have native support for adding vectors of 16-bit ints\n * so we can use those directly. When not using SSE2 or AVX2, there is some\n * minor inefficiency because the odd and even elements of each 64-bit block\n * need to be added separately, then XORed together.\n * The packed int looks like:\n *  <16 bits of data> <16 bits of data> <16 bits of data> <16 bits of data>.\n *\n * When B == 20, each uint64_t contains 3 elements with 0 padding bits at\n *   0-based positions 63, 62, 41, and 20. The packed int looks like:\n *   00 <20 bits of data> 0 <20 bits of data> 0 <20 bits of data>.\n *\n * When B == 32, each uint64_t contains 2 elements without any padding bits.\n * Both SSE2 and AVX2 have native support for adding vectors of 32-bit ints\n * so we can use those directly. When not using SSE2 or AVX2, there is some\n * minor inefficiency because the high and low elements of each 64-bit block\n * need to be added separately, then XORed together.\n * The packed int looks like:\n *  <32 bits of data> <32 bits of data>.\n */\ntemplate <std::size_t B>\nstruct Bits {\n  static inline constexpr uint64_t kDataMask();\n  static inline constexpr bool needsPadding();\n};\n\n////// Template specialization for B = 16\n\n// static\ntemplate <>\ninline constexpr uint64_t Bits<16>::kDataMask() {\n  return 0xffffffffffffffffULL;\n}\n\n// static\ntemplate <>\ninline constexpr bool Bits<16>::needsPadding() {\n  return false;\n}\n\n////// Template specialization for B = 20\n\n// static\ntemplate <>\ninline constexpr uint64_t Bits<20>::kDataMask() {\n  // In binary this mask looks like:\n  // 00 <1 repeated 20 times> 0 <1 repeated 20 times> 0 <1 repeated 20 times>\n  return ~0xC000020000100000ULL;\n}\n\n// static\ntemplate <>\ninline constexpr bool Bits<20>::needsPadding() {\n  return true;\n}\n\n////// Template specialization for B = 32\n\n// static\ntemplate <>\ninline constexpr uint64_t Bits<32>::kDataMask() {\n  return 0xffffffffffffffffULL;\n}\n\n// static\ntemplate <>\ninline constexpr bool Bits<32>::needsPadding() {\n  return false;\n}\n\n/* static */\ntemplate <std::size_t B>\nconstexpr size_t getElementsPerUint64() {\n  // how many elements fit into a 64-bit int? If padding is needed, assumes that\n  // there is 1 padding bit between elements and any partial space is not used.\n  // If padding is not needed, the computation is a trivial division.\n  return detail::Bits<B>::needsPadding()\n      ? ((sizeof(uint64_t) * 8) / (B + 1))\n      : ((sizeof(uint64_t) * 8) / B);\n}\n\n// Compile-time computation of the checksum size for a hash with given B and N.\ntemplate <std::size_t B, std::size_t N>\nconstexpr size_t getChecksumSizeBytes() {\n  constexpr size_t elemsPerUint64 = getElementsPerUint64<B>();\n  static_assert(\n      N % elemsPerUint64 == 0,\n      \"Invalid parameters: N %% elemsPerUint64 must be 0\");\n  return (N / elemsPerUint64) * sizeof(uint64_t);\n}\n\n} // namespace detail\n\ntemplate <std::size_t B, std::size_t N>\nLtHash<B, N>::LtHash(const folly::IOBuf& initialChecksum)\n    : checksum_{}, key_{folly::none} {\n  static_assert(N > 999, \"element count must be at least 1000\");\n  static_assert(\n      B == 16 || B == 20 || B == 32,\n      \"invalid element size in bits, must be one of: [ 16, 20, 32 ]\");\n\n  // Make sure libsodium is initialized, but only do it once.\n  static const int sodiumInitResult = []() { return sodium_init(); }();\n\n  if (sodiumInitResult == -1) {\n    throw std::runtime_error(\"sodium_init() failed\");\n  }\n\n  if (initialChecksum.length() == 0) {\n    checksum_ = detail::allocateCacheAlignedIOBuf(getChecksumSizeBytes());\n    checksum_.append(getChecksumSizeBytes());\n    reset();\n  } else {\n    setChecksum(initialChecksum);\n  }\n}\n\ntemplate <std::size_t B, std::size_t N>\nLtHash<B, N>::LtHash(std::unique_ptr<folly::IOBuf> initialChecksum)\n    : checksum_{}, key_{folly::none} {\n  // Make sure libsodium is initialized, but only do it once.\n  static const int sodiumInitResult = []() { return sodium_init(); }();\n\n  if (sodiumInitResult == -1) {\n    throw std::runtime_error(\"sodium_init() failed\");\n  }\n\n  setChecksum(std::move(initialChecksum));\n}\n\ntemplate <std::size_t B, std::size_t N>\nLtHash<B, N>::LtHash(const LtHash<B, N>& that)\n    : checksum_{}, key_{folly::none} {\n  // Note: we don't need to initialize libsodium in the copy constructor, since\n  // before a copy constructor is called, at least one object of this type must\n  // be constructed without using a copy constructor, so we know that libsodium\n  // must have been initialized already.\n  setChecksum(that.checksum_);\n  key_ = that.key_;\n}\n\ntemplate <std::size_t B, std::size_t N>\nLtHash<B, N>& LtHash<B, N>::operator=(const LtHash<B, N>& that) {\n  if (checksum_.length() == that.checksum_.length()) {\n    std::memcpy(\n        checksum_.writableData(), that.checksum_.data(), checksum_.length());\n  } else {\n    // this probably means that this object was moved away from and\n    // checksum_.length() is 0, so we need to allocate a new checksum_ and\n    // copy the contents.\n    setChecksum(that.checksum_);\n  }\n  key_ = that.key_;\n  return *this;\n}\n\ntemplate <std::size_t B, std::size_t N>\nLtHash<B, N>::~LtHash() {\n  clearKey(); // securely erase the old key if there is one\n}\n\ntemplate <std::size_t B, std::size_t N>\nvoid LtHash<B, N>::setKey(folly::ByteRange key) {\n  if (key.size() < crypto_generichash_blake2b_KEYBYTES_MIN ||\n      key.size() > crypto_generichash_blake2b_KEYBYTES_MAX) {\n    throw std::runtime_error(\"invalid key size\");\n  }\n  clearKey(); // securely erase the old key if there is one\n  key_ = std::vector<uint8_t>{key.begin(), key.end()};\n}\n\ntemplate <std::size_t B, std::size_t N>\nvoid LtHash<B, N>::clearKey() {\n  if (key_.has_value()) {\n    sodium_memzero(key_->data(), key_->size());\n    key_ = folly::none;\n  }\n}\n\ntemplate <std::size_t B, std::size_t N>\nLtHash<B, N>& LtHash<B, N>::operator+=(const LtHash<B, N>& rhs) {\n  if (!keysEqual(*this, rhs)) {\n    throw std::runtime_error(\"Cannot add 2 LtHashes with different keys\");\n  }\n  detail::MathOperation<detail::MathEngine::AUTO>::add(\n      detail::Bits<B>::kDataMask(),\n      B,\n      {checksum_.data(), checksum_.length()},\n      {rhs.checksum_.data(), rhs.checksum_.length()},\n      {checksum_.writableData(), checksum_.length()});\n  return *this;\n}\n\ntemplate <std::size_t B, std::size_t N>\nLtHash<B, N>& LtHash<B, N>::operator-=(const LtHash<B, N>& rhs) {\n  if (!keysEqual(*this, rhs)) {\n    throw std::runtime_error(\"Cannot subtract 2 LtHashes with different keys\");\n  }\n  detail::MathOperation<detail::MathEngine::AUTO>::sub(\n      detail::Bits<B>::kDataMask(),\n      B,\n      {checksum_.data(), checksum_.length()},\n      {rhs.checksum_.data(), rhs.checksum_.length()},\n      {checksum_.writableData(), checksum_.length()});\n  return *this;\n}\n\ntemplate <std::size_t B, std::size_t N>\nbool LtHash<B, N>::operator==(const LtHash<B, N>& that) const {\n  if (this == &that) { // same memory location means it's the same object\n    return true;\n  } else if (this->checksum_.length() != that.checksum_.length()) {\n    return false;\n  } else if (this->checksum_.length() == 0) {\n    // both objects must have been moved away from\n    return true;\n  } else {\n    int cmp = sodium_memcmp(\n        this->checksum_.data(),\n        that.checksum_.data(),\n        this->checksum_.length());\n    return cmp == 0;\n  }\n}\n\ntemplate <std::size_t B, std::size_t N>\nbool LtHash<B, N>::checksumEquals(folly::ByteRange otherChecksum) const {\n  if (otherChecksum.size() != getChecksumSizeBytes()) {\n    throw std::runtime_error(\"Invalid checksum size\");\n  } else if (this->checksum_.length() != otherChecksum.size()) {\n    return false;\n  } else {\n    int cmp = sodium_memcmp(\n        this->checksum_.data(), otherChecksum.data(), this->checksum_.length());\n    return cmp == 0;\n  }\n}\n\ntemplate <std::size_t B, std::size_t N>\nbool LtHash<B, N>::operator!=(const LtHash<B, N>& that) const {\n  return !(*this == that);\n}\n\ntemplate <std::size_t B, std::size_t N>\nvoid LtHash<B, N>::reset() {\n  std::memset(checksum_.writableData(), 0, checksum_.length());\n}\n\ntemplate <std::size_t B, std::size_t N>\nvoid LtHash<B, N>::setChecksum(const folly::IOBuf& checksum) {\n  if (checksum.computeChainDataLength() != getChecksumSizeBytes()) {\n    throw std::runtime_error(\"Invalid checksum size\");\n  }\n  folly::IOBuf checksumCopy =\n      detail::allocateCacheAlignedIOBuf(getChecksumSizeBytes());\n  for (auto range : checksum) {\n    std::memcpy(checksumCopy.writableTail(), range.data(), range.size());\n    checksumCopy.append(range.size());\n  }\n  if constexpr (detail::Bits<B>::needsPadding()) {\n    bool isPaddedCorrectly =\n        detail::MathOperation<detail::MathEngine::AUTO>::checkPaddingBits(\n            detail::Bits<B>::kDataMask(),\n            {checksumCopy.data(), checksumCopy.length()});\n    if (!isPaddedCorrectly) {\n      throw std::runtime_error(\"Invalid checksum has non-0 padding bits\");\n    }\n  }\n  checksum_ = std::move(checksumCopy);\n}\n\ntemplate <std::size_t B, std::size_t N>\nvoid LtHash<B, N>::setChecksum(std::unique_ptr<folly::IOBuf> checksum) {\n  if (checksum == nullptr) {\n    throw std::runtime_error(\"null checksum\");\n  }\n  // If the checksum is not eligible for move, call the copy version\n  if (checksum->isChained() || checksum->isShared() ||\n      !detail::isCacheAlignedAddress(checksum->data())) {\n    setChecksum(*checksum);\n    return;\n  }\n\n  if (checksum->computeChainDataLength() != getChecksumSizeBytes()) {\n    throw std::runtime_error(\"Invalid checksum size\");\n  }\n\n  // If we get here, we know that the input is not null, shared, or chained,\n  // is the proper size, and is aligned on a cache line boundary.\n  // Just need to check the padding bits before taking ownership of the buffer.\n  if constexpr (detail::Bits<B>::needsPadding()) {\n    bool isPaddedCorrectly =\n        detail::MathOperation<detail::MathEngine::AUTO>::checkPaddingBits(\n            detail::Bits<B>::kDataMask(),\n            {checksum->data(), checksum->length()});\n    if (!isPaddedCorrectly) {\n      throw std::runtime_error(\"Invalid checksum has non-0 padding bits\");\n    }\n  }\n  checksum_ = std::move(*checksum);\n}\n\ntemplate <std::size_t B, std::size_t N>\ntemplate <typename... Args>\nvoid LtHash<B, N>::hashObject(\n    folly::MutableByteRange out,\n    folly::ByteRange firstRange,\n    Args&&... moreRanges) {\n  CHECK_EQ(getChecksumSizeBytes(), out.size());\n  Blake2xb digest;\n  if (key_.has_value()) {\n    digest.init(out.size(), folly::range(*key_));\n  } else {\n    digest.init(out.size());\n  }\n  updateDigest(digest, firstRange, std::forward<Args>(moreRanges)...);\n  digest.finish(out);\n  if constexpr (detail::Bits<B>::needsPadding()) {\n    detail::MathOperation<detail::MathEngine::AUTO>::clearPaddingBits(\n        detail::Bits<B>::kDataMask(), out);\n  }\n}\n\ntemplate <std::size_t B, std::size_t N>\ntemplate <typename... Args>\nvoid LtHash<B, N>::updateDigest(\n    Blake2xb& digest, folly::ByteRange firstRange, Args&&... moreRanges) {\n  digest.update(firstRange);\n  updateDigest(digest, std::forward<Args>(moreRanges)...);\n}\n\ntemplate <std::size_t B, std::size_t N>\nvoid LtHash<B, N>::updateDigest(Blake2xb& /* digest */) {}\n\ntemplate <std::size_t B, std::size_t N>\ntemplate <typename... Args>\nLtHash<B, N>& LtHash<B, N>::addObject(\n    folly::ByteRange firstRange, Args&&... moreRanges) {\n  // hash obj and add to elements of checksum\n  using H = std::array<unsigned char, getChecksumSizeBytes()>;\n  alignas(detail::kCacheLineSize) H h;\n  hashObject(\n      {h.data(), h.size()}, firstRange, std::forward<Args>(moreRanges)...);\n  detail::MathOperation<detail::MathEngine::AUTO>::add(\n      detail::Bits<B>::kDataMask(),\n      B,\n      {checksum_.data(), checksum_.length()},\n      {h.data(), h.size()},\n      {checksum_.writableData(), checksum_.length()});\n  return *this;\n}\n\ntemplate <std::size_t B, std::size_t N>\ntemplate <typename... Args>\nLtHash<B, N>& LtHash<B, N>::removeObject(\n    folly::ByteRange firstRange, Args&&... moreRanges) {\n  // hash obj and subtract from elements of checksum\n  using H = std::array<unsigned char, getChecksumSizeBytes()>;\n  alignas(detail::kCacheLineSize) H h;\n  hashObject(\n      {h.data(), h.size()}, firstRange, std::forward<Args>(moreRanges)...);\n  detail::MathOperation<detail::MathEngine::AUTO>::sub(\n      detail::Bits<B>::kDataMask(),\n      B,\n      {checksum_.data(), checksum_.length()},\n      {h.data(), h.size()},\n      {checksum_.writableData(), checksum_.length()});\n  return *this;\n}\n\n/* static */\ntemplate <std::size_t B, std::size_t N>\nconstexpr size_t LtHash<B, N>::getChecksumSizeBytes() {\n  return detail::getChecksumSizeBytes<B, N>();\n}\n\n/* static */\ntemplate <std::size_t B, std::size_t N>\nconstexpr size_t LtHash<B, N>::getElementSizeInBits() {\n  return B;\n}\n\n/* static */\ntemplate <std::size_t B, std::size_t N>\nconstexpr size_t LtHash<B, N>::getElementsPerUint64() {\n  return detail::getElementsPerUint64<B>();\n}\n\n/* static */\ntemplate <std::size_t B, std::size_t N>\nconstexpr size_t LtHash<B, N>::getElementCount() {\n  return N;\n}\n\n/* static */\ntemplate <std::size_t B, std::size_t N>\nconstexpr bool LtHash<B, N>::hasPaddingBits() {\n  return detail::Bits<B>::needsPadding();\n}\n\ntemplate <std::size_t B, std::size_t N>\nstd::unique_ptr<folly::IOBuf> LtHash<B, N>::getChecksum() const {\n  auto result = std::make_unique<folly::IOBuf>(\n      detail::allocateCacheAlignedIOBuf(checksum_.length()));\n  result->append(checksum_.length());\n  std::memcpy(result->writableData(), checksum_.data(), checksum_.length());\n  return result;\n}\n\n// static\ntemplate <std::size_t B, std::size_t N>\nbool LtHash<B, N>::keysEqual(const LtHash<B, N>& h1, const LtHash<B, N>& h2) {\n  if (h1.key_.has_value() != h2.key_.has_value()) {\n    return false;\n  }\n  if (!h1.key_.has_value()) {\n    return true; // both LtHashes have empty keys\n  }\n  if (h1.key_->size() != h2.key_->size()) {\n    return false;\n  }\n  return sodium_memcmp(h1.key_->data(), h2.key_->data(), h1.key_->size()) == 0;\n}\n\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/LtHash.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/crypto/LtHash.h>\n\n#include <folly/CpuId.h>\n\n#include <folly/Memory.h>\n\nnamespace folly {\nnamespace crypto {\nnamespace detail {\n\nfolly::IOBuf allocateCacheAlignedIOBuf(size_t size) {\n  void* ptr = folly::aligned_malloc(size, kCacheLineSize);\n  if (ptr == nullptr) {\n    throw std::bad_alloc();\n  }\n  return folly::IOBuf(\n      folly::IOBuf::TAKE_OWNERSHIP,\n      ptr,\n      static_cast<uint64_t>(size), // capacity\n      0ULL, // initial size\n      [](void* addr, void* /* userData*/) { folly::aligned_free(addr); });\n}\n\nstd::unique_ptr<folly::IOBuf> allocateCacheAlignedIOBufUnique(size_t size) {\n  return std::make_unique<folly::IOBuf>(allocateCacheAlignedIOBuf(size));\n}\n\nbool isCacheAlignedAddress(const void* addr) {\n  auto addrValue = reinterpret_cast<size_t>(addr);\n  return (addrValue & (kCacheLineSize - 1)) == 0;\n}\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::SIMPLE>::isAvailable() {\n  return true;\n}\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::SSE2>::isAvailable() {\n  static const bool kIsAvailable =\n      CpuId().sse2() && MathOperation<MathEngine::SSE2>::isImplemented();\n  return kIsAvailable;\n}\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::AVX2>::isAvailable() {\n  static const bool kIsAvailable =\n      CpuId().avx2() && MathOperation<MathEngine::AVX2>::isImplemented();\n  return kIsAvailable;\n}\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::AUTO>::isAvailable() {\n  return true;\n}\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::AUTO>::isImplemented() {\n  return true;\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::AUTO>::add(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    folly::ByteRange b1,\n    folly::ByteRange b2,\n    folly::MutableByteRange out) {\n  // Note: implementation is a function pointer that is initialized to point\n  // at the fastest available implementation the first time this function is\n  // called.\n  static auto implementation = []() {\n    if (MathOperation<MathEngine::AVX2>::isAvailable()) {\n      LOG(INFO) << \"Selected AVX2 MathEngine for add() operation\";\n      return MathOperation<MathEngine::AVX2>::add;\n    } else if (MathOperation<MathEngine::SSE2>::isAvailable()) {\n      LOG(INFO) << \"Selected SSE2 MathEngine for add() operation\";\n      return MathOperation<MathEngine::SSE2>::add;\n    } else {\n      LOG(INFO) << \"Selected SIMPLE MathEngine for add() operation\";\n      return MathOperation<MathEngine::SIMPLE>::add;\n    }\n  }();\n  implementation(dataMask, bitsPerElement, b1, b2, out);\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::AUTO>::sub(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    folly::ByteRange b1,\n    folly::ByteRange b2,\n    folly::MutableByteRange out) {\n  // Note: implementation is a function pointer that is initialized to point\n  // at the fastest available implementation the first time this function is\n  // called.\n  static auto implementation = []() {\n    if (MathOperation<MathEngine::AVX2>::isAvailable()) {\n      LOG(INFO) << \"Selected AVX2 MathEngine for sub() operation\";\n      return MathOperation<MathEngine::AVX2>::sub;\n    } else if (MathOperation<MathEngine::SSE2>::isAvailable()) {\n      LOG(INFO) << \"Selected SSE2 MathEngine for sub() operation\";\n      return MathOperation<MathEngine::SSE2>::sub;\n    } else {\n      LOG(INFO) << \"Selected SIMPLE MathEngine for sub() operation\";\n      return MathOperation<MathEngine::SIMPLE>::sub;\n    }\n  }();\n  implementation(dataMask, bitsPerElement, b1, b2, out);\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::AUTO>::clearPaddingBits(\n    uint64_t dataMask, folly::MutableByteRange buf) {\n  // Note: implementation is a function pointer that is initialized to point\n  // at the fastest available implementation the first time this function is\n  // called.\n  static auto implementation = []() {\n    if (MathOperation<MathEngine::AVX2>::isAvailable()) {\n      LOG(INFO) << \"Selected AVX2 MathEngine for clearPaddingBits() operation\";\n      return MathOperation<MathEngine::AVX2>::clearPaddingBits;\n    } else if (MathOperation<MathEngine::SSE2>::isAvailable()) {\n      LOG(INFO) << \"Selected SSE2 MathEngine for clearPaddingBits() operation\";\n      return MathOperation<MathEngine::SSE2>::clearPaddingBits;\n    } else {\n      LOG(INFO)\n          << \"Selected SIMPLE MathEngine for clearPaddingBits() operation\";\n      return MathOperation<MathEngine::SIMPLE>::clearPaddingBits;\n    }\n  }();\n  implementation(dataMask, buf);\n}\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::AUTO>::checkPaddingBits(\n    uint64_t dataMask, folly::ByteRange buf) {\n  // Note: implementation is a function pointer that is initialized to point\n  // at the fastest available implementation the first time this function is\n  // called.\n  static auto implementation = []() {\n    if (MathOperation<MathEngine::AVX2>::isAvailable()) {\n      LOG(INFO) << \"Selected AVX2 MathEngine for checkPaddingBits() operation\";\n      return MathOperation<MathEngine::AVX2>::checkPaddingBits;\n    } else if (MathOperation<MathEngine::SSE2>::isAvailable()) {\n      LOG(INFO) << \"Selected SSE2 MathEngine for checkPaddingBits() operation\";\n      return MathOperation<MathEngine::SSE2>::checkPaddingBits;\n    } else {\n      LOG(INFO)\n          << \"Selected SIMPLE MathEngine for checkPaddingBits() operation\";\n      return MathOperation<MathEngine::SIMPLE>::checkPaddingBits;\n    }\n  }();\n  return implementation(dataMask, buf);\n}\n\ntemplate struct MathOperation<MathEngine::AUTO>;\n\n} // namespace detail\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/LtHash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/crypto/Blake2xb.h>\n#include <folly/io/IOBuf.h>\n\nnamespace folly {\nnamespace crypto {\n\nnamespace detail {\n/**\n * Allocates an IOBuf of the given size, aligned on a cache line boundary.\n * Similar to folly::IOBuf::create(), the returned IOBuf has an initial\n * capacity == size and an initial length == 0.\n */\nfolly::IOBuf allocateCacheAlignedIOBuf(size_t size);\n\n/**\n * Similar to allocateCacheAlignedIOBuf(), but returns a unique_ptr to an IOBuf\n * instead of an IOBuf.\n */\nstd::unique_ptr<folly::IOBuf> allocateCacheAlignedIOBufUnique(size_t size);\n\n/**\n * Returns true if the given memory address is aligned on a cache line boundary\n * and false if it isn't.\n */\nbool isCacheAlignedAddress(const void* addr);\n\n} // namespace detail\n\n/**\n * Templated homomorphic hash, using LtHash (lattice-based crypto).\n * Template parameters: B = element size in bits, N = number of elements.\n *\n * Current constraints (checked at compile time with static asserts):\n * (1) B must be 16, 20 or 32.\n * (2) N must be > 999.\n * (3) when B is 16, N must be divisible by 32.\n * (4) when B is 20, N must be divisible by 24.\n * (5) when B is 32, N must be divisible by 16.\n */\ntemplate <std::size_t B, std::size_t N>\nclass LtHash {\n public:\n  explicit LtHash(const folly::IOBuf& initialChecksum = {});\n\n  /**\n   * Like the above constructor but takes ownership of the checksum buffer,\n   * avoiding a copy if these conditions about the input buffer are met:\n   * - initialChecksum->isChained() is false\n   * - initialChecksum->isShared() is false\n   * - detail::isCacheAlignedAddress(initialChecksum.data()) is true\n   *\n   * If you want to take advantage of this and need to make sure your IOBuf\n   * address is aligned on a cache line boundary, you can use the\n   * function detail::allocateCacheAlignedIOBufUnique() to do it.\n   */\n  explicit LtHash(std::unique_ptr<folly::IOBuf> initialChecksum);\n\n  // Note: we explicitly implement copy constructor and copy assignment\n  // operator to make sure the checksum_ IOBuf is deep-copied.\n  LtHash(const LtHash<B, N>& that);\n  LtHash<B, N>& operator=(const LtHash<B, N>& that);\n\n  LtHash(LtHash<B, N>&& that) noexcept = default;\n  LtHash<B, N>& operator=(LtHash<B, N>&& that) noexcept = default;\n  ~LtHash();\n\n  /**\n   * Sets the secret Blake2xb key. The key will be used to hash every element\n   * added with addObject() / removed with removeObject(). This can be used\n   * to compute a keyed LtHash value for a set of elements, if desired.\n   *\n   * The key must be between 16 and 64 bytes long (inclusive) and should be\n   * a cryptographic key (e.g. a random value generated by a CPRNG).\n   *\n   * Note that if the LtHash value is transmitted from one user to another, the\n   * two users will have to securely share the secret key before the receiver\n   * can verify the integrity of the LtHash value they got from the sender.\n   */\n  void setKey(folly::ByteRange key);\n\n  /**\n   * Unsets the secret Blake2xb key and erases the key contents from memory.\n   */\n  void clearKey();\n\n  /**\n   * Resets the checksum in this LtHash. This puts the hash into the same\n   * state as if it was just constructed with the zero-argument constructor.\n   */\n  void reset();\n\n  /**\n   * IMPORTANT: Unlike regular hash, the incremental hash functions operate on\n   * individual objects, not a stream of data. For example, the following\n   * example codes will lead to different checksum values.\n   * (1) addObject(\"Hello\"); addObject(\" World\");\n   * (2) addObject(\"Hello World\");\n   * because addObject() calculates hashes for the two words separately, and\n   * aggregate them to update checksum.\n   *\n   * addObject() is commutative. LtHash generates the same checksum over a\n   * given set of objects regardless of the order they were added.\n   * Example: H(a + b + c) = H(b + c + a)\n   *\n   * addObject() can be called with multiple ByteRange parameters, in which\n   * case it will behave as if it was called with a single ByteRange which\n   * contained the concatenation of all the input ByteRanges. This allows\n   * adding an object whose hash is computed from several non-contiguous\n   * ranges of data, without having to copy the data to a contiguous\n   * piece of memory.\n   *\n   * Example: addObject(r1, r2, r3) is equivalent to\n   * addObject(r4) where r4 contains the concatenation of r1 + r2 + r3.\n   */\n  template <typename... Args>\n  LtHash<B, N>& addObject(folly::ByteRange firstRange, Args&&... moreRanges);\n\n  /**\n   * removeObject() is the inverse function of addObject(). Note that it does\n   * NOT check whether the object has been actually added to LtHash. The caller\n   * should ensure that the object is valid.\n   *\n   * Example: H(a - a + b - b + c - c) = H(a + b + c - a - b - c) = H()\n   *\n   * Similar to addObject(), removeObject() can be called with more than one\n   * ByteRange parameter.\n   */\n  template <typename... Args>\n  LtHash<B, N>& removeObject(folly::ByteRange firstRange, Args&&... moreRanges);\n\n  /**\n   * Because the addObject() operation in LtHash is commutative and transitive,\n   * it's possible to break down a large LtHash computation (i.e. adding 100k\n   * objects) into several parallel steps each of which computes a LtHash of a\n   * subset of the objects, and then add the LtHash objects together.\n   * Pseudocode:\n   *\n   *   std::vector<std::string> objects = ...;\n   *   Future<LtHash<20, 1008>> h1 = computeInBackgroundThread(\n   *       &objects[0], &objects[10000]);\n   *   Future<LtHash<20, 1008>> h2 = computeInBackgroundThread(\n   *       &objects[10001], &objects[20000]);\n   *   LtHash<20, 1008> result = h1.get() + h2.get();\n   */\n  LtHash<B, N>& operator+=(const LtHash<B, N>& rhs);\n  friend LtHash<B, N> operator+(\n      const LtHash<B, N>& lhs, const LtHash<B, N>& rhs) {\n    LtHash<B, N> result = lhs;\n    result += rhs;\n    return result;\n  }\n  friend LtHash<B, N> operator+(LtHash<B, N>&& lhs, const LtHash<B, N>& rhs) {\n    LtHash<B, N> result = std::move(lhs);\n    result += rhs;\n    return result;\n  }\n  friend LtHash<B, N> operator+(const LtHash<B, N>& lhs, LtHash<B, N>&& rhs) {\n    // addition is commutative so we can just swap the two arguments\n    return std::move(rhs) + lhs;\n  }\n  friend LtHash<B, N> operator+(LtHash<B, N>&& lhs, LtHash<B, N>&& rhs) {\n    LtHash<B, N> result = std::move(lhs);\n    result += rhs;\n    return result;\n  }\n\n  /**\n   * The subtraction operator is provided for symmetry, but I'm not sure if\n   * anyone will ever actually use it outside of tests.\n   */\n  LtHash<B, N>& operator-=(const LtHash<B, N>& rhs);\n  friend LtHash<B, N> operator-(\n      const LtHash<B, N>& lhs, const LtHash<B, N>& rhs) {\n    LtHash<B, N> result = lhs;\n    result -= rhs;\n    return result;\n  }\n  friend LtHash<B, N> operator-(LtHash<B, N>&& lhs, const LtHash<B, N>& rhs) {\n    LtHash<B, N> result = std::move(lhs);\n    result -= rhs;\n    return result;\n  }\n\n  /**\n   * Equality comparison operator, implemented in a data-independent way to\n   * guard against timing attacks. Always use this to check if two LtHash\n   * values are equal instead of manually comparing checksum buffers.\n   */\n  bool operator==(const LtHash<B, N>& that) const;\n\n  /**\n   * Equality comparison operator for checksum in ByteRange, implemented in a\n   * data-independent way to guard against timing attacks.\n   */\n  bool checksumEquals(folly::ByteRange otherChecksum) const;\n\n  /**\n   * Inequality comparison operator.\n   */\n  bool operator!=(const LtHash<B, N>& that) const;\n\n  /**\n   * Sets the initial checksum value to use for processing objects in the\n   * xxxObject() calls.\n   */\n  void setChecksum(const folly::IOBuf& checksum);\n\n  /**\n   * Like the above method but takes ownership of the checksum buffer,\n   * avoiding a copy if these conditions about the input buffer are met:\n   * - checksum->isChained() is false\n   * - checksum->isShared() is false\n   * - detail::isCacheAlignedAddress(checksum.data()) is true\n   *\n   * If you want to take advantage of this and need to make sure your IOBuf\n   * address is aligned on a cache line boundary, you can use the\n   * function detail::allocateCacheAlignedIOBufUnique() to do it.\n   */\n  void setChecksum(std::unique_ptr<folly::IOBuf> checksum);\n\n  /**\n   * Returns the total length of the checksum (element_count * element_length)\n   */\n  static constexpr size_t getChecksumSizeBytes();\n\n  /**\n   * Returns the template parameter B.\n   */\n  static constexpr size_t getElementSizeInBits();\n\n  /**\n   * Returns the number of elements that get packed into a single uint64_t.\n   */\n  static constexpr size_t getElementsPerUint64();\n\n  /**\n   * Returns the template parameter N.\n   */\n  static constexpr size_t getElementCount();\n\n  /**\n   * Retruns true if the internal checksum uses padding bits between elements.\n   */\n  static constexpr bool hasPaddingBits();\n\n  /**\n   * Returns a copy of the current checksum value\n   */\n  std::unique_ptr<folly::IOBuf> getChecksum() const;\n\n private:\n  template <typename... Args>\n  void hashObject(\n      folly::MutableByteRange out,\n      folly::ByteRange firstRange,\n      Args&&... moreRanges);\n\n  template <typename... Args>\n  void updateDigest(\n      Blake2xb& digest, folly::ByteRange range, Args&&... moreRanges);\n\n  void updateDigest(Blake2xb& digest);\n\n  static bool keysEqual(const LtHash<B, N>& h1, const LtHash<B, N>& h2);\n\n  // current checksum\n  folly::IOBuf checksum_;\n  folly::Optional<std::vector<uint8_t>> key_;\n};\n\n} // namespace crypto\n} // namespace folly\n\n#include <folly/crypto/LtHash-inl.h>\n\nnamespace folly {\nnamespace crypto {\n\n// This is the fastest and smallest specialization and should be\n// preferred in most cases. It provides over 200 bits of security\n// which should be good enough for most cases.\nusing LtHash16_1024 = LtHash<16, 1024>;\n\n// These specializations are available to users who want a higher\n// level of cryptographic security. They are slower and larger than\n// the one above.\nusing LtHash20_1008 = LtHash<20, 1008>;\nusing LtHash32_1024 = LtHash<32, 1024>;\n\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"math_operation_simple\",\n    srcs = [\n        \"MathOperation_Simple.cpp\",\n    ],\n    deps = [\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/crypto/detail:lt_hash_internal\",\n        \"//xplat/folly/lang:bits\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"math_operation_avx2_disable\",\n    srcs = [\n        \"MathOperation_AVX2.cpp\",\n    ],\n    deps = [\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/crypto/detail:lt_hash_internal\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/third-party/sodium:sodium\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lt_hash_internal\",\n    headers = [\n        \"LtHashInternal.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:range\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"math_operation_avx2\",\n    srcs = [\n        \"MathOperation_AVX2.cpp\",\n    ],\n    deps = [\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/crypto/detail:lt_hash_internal\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/third-party/sodium:sodium\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"math_operation_sse2_disable\",\n    srcs = [\n        \"MathOperation_SSE2.cpp\",\n    ],\n    deps = [\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/crypto/detail:lt_hash_internal\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/third-party/sodium:sodium\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"math_operation_sse2\",\n    srcs = [\n        \"MathOperation_SSE2.cpp\",\n    ],\n    deps = [\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/crypto/detail:lt_hash_internal\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/third-party/sodium:sodium\",\n    ],\n)\n\n# !!!! fbcode/folly/crypto/detail/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"math_operation_simple\",\n    srcs = [\n        \"MathOperation_Simple.cpp\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mno-avx\",\n            \"-mno-avx2\",\n            \"-mno-sse2\",\n        ],\n    }),\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":lt_hash_internal\",\n        \"//folly:memory\",\n        \"//folly/lang:bits\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"math_operation_sse2\",\n    srcs = [\n        \"MathOperation_SSE2.cpp\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mno-avx\",\n            \"-mno-avx2\",\n            \"-msse2\",\n        ],\n    }),\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":lt_hash_internal\",\n        \"//folly:memory\",\n        \"//folly/lang:bits\",\n    ],\n    external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"math_operation_avx2\",\n    srcs = [\n        \"MathOperation_AVX2.cpp\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mavx\",\n            \"-mavx2\",\n            \"-msse2\",\n        ],\n    }),\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":lt_hash_internal\",\n        \"//folly:memory\",\n        \"//folly/lang:bits\",\n    ],\n    external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"math_operation_sse2_disable\",\n    srcs = [\n        \"MathOperation_SSE2.cpp\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mno-avx\",\n            \"-mno-avx2\",\n            \"-mno-sse2\",\n        ],\n    }),\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":lt_hash_internal\",\n        \"//folly:memory\",\n        \"//folly/lang:bits\",\n    ],\n    external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"math_operation_avx2_disable\",\n    srcs = [\n        \"MathOperation_AVX2.cpp\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mno-avx\",\n            \"-mno-avx2\",\n            \"-mno-sse2\",\n        ],\n    }),\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":lt_hash_internal\",\n        \"//folly:memory\",\n        \"//folly/lang:bits\",\n    ],\n    external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n"
  },
  {
    "path": "folly/crypto/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# NOTE: This file is manually maintained, not auto-generated.\n# Update this file when the corresponding BUCK file changes.\n\nfolly_add_library(\n  NAME lt_hash_internal\n  HEADERS\n    LtHashInternal.h\n  EXPORTED_DEPS\n    folly_range\n)\n\nfolly_add_library(\n  NAME math_operation_simple\n  SRCS\n    MathOperation_Simple.cpp\n  DEPS\n    folly_crypto_detail_lt_hash_internal\n    folly_lang_bits\n    folly_memory\n)\n\n# Apply SSE2/AVX2 compile flags on x86\nif (IS_X86_64_ARCH)\n  target_compile_options(folly_crypto_detail_math_operation_simple_obj\n    PRIVATE -mno-avx -mno-avx2 -mno-sse2)\nendif()\n\nfolly_add_library(\n  NAME math_operation_sse2\n  SRCS\n    MathOperation_SSE2.cpp\n  DEPS\n    folly_crypto_detail_lt_hash_internal\n    folly_lang_bits\n    folly_memory\n)\n\nif (IS_X86_64_ARCH)\n  target_compile_options(folly_crypto_detail_math_operation_sse2_obj\n    PRIVATE -mno-avx -mno-avx2 -msse2)\nendif()\n\nfolly_add_library(\n  NAME math_operation_avx2\n  SRCS\n    MathOperation_AVX2.cpp\n  DEPS\n    folly_crypto_detail_lt_hash_internal\n    folly_lang_bits\n    folly_memory\n)\n\nif (IS_X86_64_ARCH)\n  target_compile_options(folly_crypto_detail_math_operation_avx2_obj\n    PRIVATE -mavx -mavx2 -msse2)\nendif()\n\nfolly_add_library(\n  NAME math_operation_sse2_disable\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    MathOperation_SSE2.cpp\n  DEPS\n    folly_crypto_detail_lt_hash_internal\n    folly_lang_bits\n    folly_memory\n)\n\nfolly_add_library(\n  NAME math_operation_avx2_disable\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    MathOperation_AVX2.cpp\n  DEPS\n    folly_crypto_detail_lt_hash_internal\n    folly_lang_bits\n    folly_memory\n)\n"
  },
  {
    "path": "folly/crypto/detail/LtHashInternal.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Range.h>\n\nnamespace folly {\nnamespace crypto {\nnamespace detail {\n\n// As of 2019, most (or all?) modern Intel CPUs have 64-byte L1 cache lines,\n// and aligning data buffers on cache line boundaries on such CPUs\n// noticeably benefits performance (up to 10% difference).\n//\n// If you change this, code that depends on it in MathOperation_*.cpp may\n// break and could need fixing.\nconstexpr size_t kCacheLineSize = 64;\n\n// Invariants about kCacheLineSize that other logic depends on: it must be\n// a power of 2 and cannot be zero.\nstatic_assert(kCacheLineSize > 0, \"kCacheLineSize cannot be 0\");\nstatic_assert(\n    (kCacheLineSize & (kCacheLineSize - 1)) == 0,\n    \"kCacheLineSize must be a power of 2\");\n\n/**\n * Defines available math engines that we can use to perform element-wise\n * modular addition or subtraction of element vectors.\n * - AUTO: pick the best available, from best to worst: AVX2, SSE2, SIMPLE\n * - SIMPLE: perform addition/subtraction using uint64_t values\n * - SSE2: perform addition/subtraction using 128-bit __m128i values.\n *   Intel only, requires SSE2 instruction support.\n * - AVX2: perform addition/subtraction using 256-bit __m256i values.\n *   Intel only, requires AVX2 instruction support.\n */\nenum class MathEngine { AUTO, SIMPLE, SSE2, AVX2 };\n\n/**\n * This actually implements the bulk addition/subtraction operations.\n */\ntemplate <MathEngine E>\nstruct MathOperation {\n  /**\n   * Returns true if the math engine E is supported by the CPU and OS and is\n   * implemented.\n   */\n  static bool isAvailable();\n\n  /**\n   * Returns true if the math engine E is implemented.\n   */\n  static bool isImplemented();\n\n  /**\n   * Performs element-wise modular addition of 2 vectors of elements packed\n   * into the buffers b1 and b2. Writes the output into the buffer out. The\n   * output buffer may be the same as one of the input buffers. The dataMask\n   * parameter should be Bits<B>::kDataMask() where B is the element size\n   * in bits.\n   */\n  static void add(\n      uint64_t dataMask,\n      size_t bitsPerElement,\n      ByteRange b1,\n      ByteRange b2,\n      MutableByteRange out);\n\n  /**\n   * Performs element-wise modular subtraction of 2 groups of elements packed\n   * into the buffers b1 and b2. Note that (a - b) % M == (a + (M - b)) % M,\n   *  which is how we actually implement it to avoid underflow issues. The\n   * dataMask parameter should be Bits<B>::kDataMask() where B is the element\n   * size in bits.\n   */\n  static void sub(\n      uint64_t dataMask,\n      size_t bitsPerElement,\n      ByteRange b1,\n      ByteRange b2,\n      MutableByteRange out);\n\n  /**\n   * Clears the padding bits of the given buffer according to the given\n   * data mask: for each uint64_t in the input buffer, all 0 bits in the\n   * data mask are cleared, and all 1 bits in the data mask are preserved.\n   */\n  static void clearPaddingBits(uint64_t dataMask, MutableByteRange buf);\n\n  /**\n   * Returns true if the given checksum buffer contains 0 bits at the padding\n   * bit positions, according to the given data mask.\n   */\n  static bool checkPaddingBits(uint64_t dataMask, ByteRange buf);\n};\n\n// These forward declarations of explicit template instantiations seem to be\n// required to get things to compile. I tried to get things to work without it,\n// but the compiler complained when I had any AVX2 types in this header, so I\n// think they need to be hidden in the .cpp file for some reason.\n#define FORWARD_DECLARE_EXTERN_TEMPLATE(E)                                   \\\n  template <>                                                                \\\n  bool MathOperation<E>::isAvailable();                                      \\\n  template <>                                                                \\\n  bool MathOperation<E>::isImplemented();                                    \\\n  template <>                                                                \\\n  void MathOperation<E>::add(                                                \\\n      uint64_t dataMask,                                                     \\\n      size_t bitsPerElement,                                                 \\\n      ByteRange b1,                                                          \\\n      ByteRange b2,                                                          \\\n      MutableByteRange out);                                                 \\\n  template <>                                                                \\\n  void MathOperation<E>::sub(                                                \\\n      uint64_t dataMask,                                                     \\\n      size_t bitsPerElement,                                                 \\\n      ByteRange b1,                                                          \\\n      ByteRange b2,                                                          \\\n      MutableByteRange out);                                                 \\\n  template <>                                                                \\\n  void MathOperation<E>::clearPaddingBits(                                   \\\n      uint64_t dataMask, MutableByteRange buf);                              \\\n  template <>                                                                \\\n  bool MathOperation<E>::checkPaddingBits(uint64_t dataMask, ByteRange buf); \\\n  extern template struct MathOperation<E>\n\nFORWARD_DECLARE_EXTERN_TEMPLATE(MathEngine::AUTO);\nFORWARD_DECLARE_EXTERN_TEMPLATE(MathEngine::SIMPLE);\nFORWARD_DECLARE_EXTERN_TEMPLATE(MathEngine::SSE2);\nFORWARD_DECLARE_EXTERN_TEMPLATE(MathEngine::AVX2);\n\n#undef FORWARD_DECLARE_EXTERN_TEMPLATE\n\n} // namespace detail\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/detail/MathOperation_AVX2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Implementation of the MathOperation<MathEngine::AVX2> template\n// specializations.\n#include <folly/crypto/detail/LtHashInternal.h>\n\n#include <glog/logging.h>\n\n#ifdef __AVX2__\n#include <immintrin.h>\n#include <sodium.h>\n\n#include <folly/lang/Bits.h>\n#endif // __AVX2__\n\n#include <folly/Memory.h>\n\nnamespace folly {\nnamespace crypto {\nnamespace detail {\n\n#ifdef __AVX2__\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::AVX2>::isImplemented() {\n  return true;\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::AVX2>::add(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    ByteRange b1,\n    ByteRange b2,\n    MutableByteRange out) {\n  DCHECK_EQ(b1.size(), b2.size());\n  DCHECK_EQ(b1.size(), out.size());\n  DCHECK_EQ(0, b1.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(__m256i) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(__m256i)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(__m256i);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(__m256i)\");\n\n  // gcc issues 'ignoring attributes on template argument' warning if\n  // __m256i is used below, so have to type explicitly\n  alignas(kCacheLineSize) std::array<\n      long long __attribute__((__vector_size__(sizeof(__m256i)))),\n      kValsPerCacheLine>\n      results;\n\n  // Note: AVX2 is Intel x86_64 only which is little-endian, so we don't need\n  // the Endian::little() conversions when loading or storing data.\n  if (bitsPerElement == 16 || bitsPerElement == 32) {\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      const __m256i* v1p = reinterpret_cast<const __m256i*>(b1.data() + pos);\n      const __m256i* v2p = reinterpret_cast<const __m256i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m256i v1 = _mm256_load_si256(v1p + i);\n        __m256i v2 = _mm256_load_si256(v2p + i);\n        if (bitsPerElement == 16) {\n          results[i] = _mm256_add_epi16(v1, v2);\n        } else { // bitsPerElement == 32\n          results[i] = _mm256_add_epi32(v1, v2);\n        }\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  } else {\n    __m256i mask = _mm256_set1_epi64x(dataMask);\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      const __m256i* v1p = reinterpret_cast<const __m256i*>(b1.data() + pos);\n      const __m256i* v2p = reinterpret_cast<const __m256i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m256i v1 = _mm256_load_si256(v1p + i);\n        __m256i v2 = _mm256_load_si256(v2p + i);\n        results[i] = _mm256_and_si256(_mm256_add_epi64(v1, v2), mask);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  }\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::AVX2>::sub(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    ByteRange b1,\n    ByteRange b2,\n    MutableByteRange out) {\n  DCHECK_EQ(b1.size(), b2.size());\n  DCHECK_EQ(b1.size(), out.size());\n  DCHECK_EQ(0, b1.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(__m256i) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(__m256i)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(__m256i);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(__m256i)\");\n\n  // gcc issues 'ignoring attributes on template argument' warning if\n  // __m256i is used below, so have to type explicitly\n  alignas(kCacheLineSize) std::array<\n      long long __attribute__((__vector_size__(sizeof(__m256i)))),\n      kValsPerCacheLine>\n      results;\n\n  // Note: AVX2 is Intel x86_64 only which is little-endian, so we don't need\n  // the Endian::little() conversions when loading or storing data.\n  if (bitsPerElement == 16 || bitsPerElement == 32) {\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      const __m256i* v1p = reinterpret_cast<const __m256i*>(b1.data() + pos);\n      const __m256i* v2p = reinterpret_cast<const __m256i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m256i v1 = _mm256_load_si256(v1p + i);\n        __m256i v2 = _mm256_load_si256(v2p + i);\n        if (bitsPerElement == 16) {\n          results[i] = _mm256_sub_epi16(v1, v2);\n        } else { // bitsPerElement == 32\n          results[i] = _mm256_sub_epi32(v1, v2);\n        }\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  } else {\n    __m256i mask = _mm256_set1_epi64x(dataMask);\n    __m256i paddingMask = _mm256_set1_epi64x(~dataMask);\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      const __m256i* v1p = reinterpret_cast<const __m256i*>(b1.data() + pos);\n      const __m256i* v2p = reinterpret_cast<const __m256i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m256i v1 = _mm256_load_si256(v1p + i);\n        __m256i v2 = _mm256_load_si256(v2p + i);\n        __m256i negV2 =\n            _mm256_and_si256(_mm256_sub_epi64(paddingMask, v2), mask);\n        results[i] = _mm256_and_si256(_mm256_add_epi64(v1, negV2), mask);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  }\n}\n\ntemplate <>\nvoid MathOperation<MathEngine::AVX2>::clearPaddingBits(\n    uint64_t dataMask, MutableByteRange buf) {\n  if (dataMask == 0xffffffffffffffffULL) {\n    return;\n  }\n  DCHECK_EQ(0, buf.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(__m256i) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(__m256i)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(__m256i);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(__m256i)\");\n  // gcc issues 'ignoring attributes on template argument' warning if\n  // __m256i is used below, so have to type explicitly\n  alignas(kCacheLineSize) std::array<\n      long long __attribute__((__vector_size__(sizeof(__m256i)))),\n      kValsPerCacheLine>\n      results;\n  __m256i mask = _mm256_set1_epi64x(dataMask);\n  for (size_t pos = 0; pos < buf.size(); pos += kCacheLineSize) {\n    const __m256i* p = reinterpret_cast<const __m256i*>(buf.data() + pos);\n    for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n      results[i] = _mm256_and_si256(_mm256_load_si256(p + i), mask);\n    }\n    std::memcpy(buf.data() + pos, results.data(), sizeof(results));\n  }\n}\n\ntemplate <>\nbool MathOperation<MathEngine::AVX2>::checkPaddingBits(\n    uint64_t dataMask, ByteRange buf) {\n  if (dataMask == 0xffffffffffffffffULL) {\n    return true;\n  }\n  DCHECK_EQ(0, buf.size() % sizeof(__m256i));\n  __m256i paddingMask = _mm256_set1_epi64x(~dataMask);\n  static const __m256i kZero = _mm256_setzero_si256();\n  for (size_t pos = 0; pos < buf.size(); pos += sizeof(__m256i)) {\n    __m256i val =\n        _mm256_load_si256(reinterpret_cast<const __m256i*>(buf.data() + pos));\n    __m256i paddingBits = _mm256_and_si256(val, paddingMask);\n    if (sodium_memcmp(&paddingBits, &kZero, sizeof(kZero)) != 0) {\n      return false;\n    }\n  }\n  return true;\n}\n\n#else // !__AVX2__\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::AVX2>::isImplemented() {\n  return false;\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::AVX2>::add(\n    uint64_t /* dataMask */,\n    size_t bitsPerElement,\n    ByteRange /* b1 */,\n    ByteRange /* b2 */,\n    MutableByteRange /* out */) {\n  if (bitsPerElement != 0) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::AVX2>::\"\n               << \"add() called\";\n  }\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::AVX2>::sub(\n    uint64_t /* dataMask */,\n    size_t bitsPerElement,\n    ByteRange /* b1 */,\n    ByteRange /* b2 */,\n    MutableByteRange /* out */) {\n  if (bitsPerElement != 0) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::AVX2>::\"\n               << \"sub() called\";\n  }\n}\n\ntemplate <>\nvoid MathOperation<MathEngine::AVX2>::clearPaddingBits(\n    uint64_t /* dataMask */, MutableByteRange buf) {\n  if (buf.data() != nullptr) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::AVX2>::\"\n               << \"clearPaddingBits() called\";\n  }\n}\n\ntemplate <>\nbool MathOperation<MathEngine::AVX2>::checkPaddingBits(\n    uint64_t /* dataMask */, ByteRange buf) {\n  if (buf.data() != nullptr) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::AVX2>::\"\n               << \"checkPaddingBits() called\";\n  }\n  return false;\n}\n\n#endif // __AVX2__\n\ntemplate struct MathOperation<MathEngine::AVX2>;\n\n} // namespace detail\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/detail/MathOperation_SSE2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Implementation of the MathOperation<MathEngine::SSE2> template\n// specializations.\n#include <folly/crypto/detail/LtHashInternal.h>\n\n#include <glog/logging.h>\n\n#ifdef __SSE2__\n#include <emmintrin.h>\n#include <sodium.h>\n\n#include <folly/lang/Bits.h>\n#endif // __SSE2__\n\n#include <folly/Memory.h>\n\nnamespace folly {\nnamespace crypto {\nnamespace detail {\n\n#ifdef __SSE2__\n// static\ntemplate <>\nbool MathOperation<MathEngine::SSE2>::isImplemented() {\n  return true;\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::SSE2>::add(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    ByteRange b1,\n    ByteRange b2,\n    MutableByteRange out) {\n  DCHECK_EQ(b1.size(), b2.size());\n  DCHECK_EQ(b1.size(), out.size());\n  DCHECK_EQ(0, b1.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(__m128i) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(__m128i)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(__m128i);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(__m128i)\");\n\n  // gcc issues 'ignoring attributes on template argument' warning if\n  // __m128i is used below, so have to type explicitly\n  alignas(kCacheLineSize) std::array<\n      long long __attribute__((__vector_size__(sizeof(__m128i)))),\n      kValsPerCacheLine>\n      results;\n\n  // Note: SSE2 is Intel x86(_64) only which is little-endian, so we don't need\n  // the Endian::little() conversions when loading or storing data.\n  if (bitsPerElement == 16 || bitsPerElement == 32) {\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const __m128i*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const __m128i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m128i v1 = _mm_load_si128(v1p + i);\n        __m128i v2 = _mm_load_si128(v2p + i);\n        if (bitsPerElement == 16) {\n          results[i] = _mm_add_epi16(v1, v2);\n        } else { // bitsPerElement == 32\n          results[i] = _mm_add_epi32(v1, v2);\n        }\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  } else {\n    __m128i mask = _mm_set_epi64x(dataMask, dataMask);\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const __m128i*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const __m128i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m128i v1 = _mm_load_si128(v1p + i);\n        __m128i v2 = _mm_load_si128(v2p + i);\n        results[i] = _mm_and_si128(_mm_add_epi64(v1, v2), mask);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  }\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::SSE2>::sub(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    ByteRange b1,\n    ByteRange b2,\n    MutableByteRange out) {\n  DCHECK_EQ(b1.size(), b2.size());\n  DCHECK_EQ(b1.size(), out.size());\n  DCHECK_EQ(0, b1.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(__m128i) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(__m128i)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(__m128i);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(__m128i)\");\n  // gcc issues 'ignoring attributes on template argument' warning if\n  // __m128i is used below, so have to type explicitly\n  alignas(kCacheLineSize) std::array<\n      long long __attribute__((__vector_size__(sizeof(__m128i)))),\n      kValsPerCacheLine>\n      results;\n\n  // Note: SSE2 is Intel x86(_64) only which is little-endian, so we don't need\n  // the Endian::little() conversions when loading or storing data.\n  if (bitsPerElement == 16 || bitsPerElement == 32) {\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const __m128i*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const __m128i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m128i v1 = _mm_load_si128(v1p + i);\n        __m128i v2 = _mm_load_si128(v2p + i);\n        if (bitsPerElement == 16) {\n          results[i] = _mm_sub_epi16(v1, v2);\n        } else { // bitsPerElement == 32\n          results[i] = _mm_sub_epi32(v1, v2);\n        }\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  } else {\n    __m128i mask = _mm_set_epi64x(dataMask, dataMask);\n    __m128i paddingMask = _mm_set_epi64x(~dataMask, ~dataMask);\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const __m128i*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const __m128i*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        __m128i v1 = _mm_load_si128(v1p + i);\n        __m128i v2 = _mm_load_si128(v2p + i);\n        __m128i negV2 = _mm_and_si128(_mm_sub_epi64(paddingMask, v2), mask);\n        results[i] = _mm_and_si128(_mm_add_epi64(v1, negV2), mask);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  }\n}\n\ntemplate <>\nvoid MathOperation<MathEngine::SSE2>::clearPaddingBits(\n    uint64_t dataMask, MutableByteRange buf) {\n  if (dataMask == 0xffffffffffffffffULL) {\n    return;\n  }\n  DCHECK_EQ(0, buf.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(__m128i) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(__m128i)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(__m128i);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(__m128i)\");\n\n  // gcc issues 'ignoring attributes on template argument' warning if\n  // __m128i is used below, so have to type explicitly\n  alignas(kCacheLineSize) std::array<\n      long long __attribute__((__vector_size__(sizeof(__m128i)))),\n      kValsPerCacheLine>\n      results;\n\n  __m128i mask = _mm_set_epi64x(dataMask, dataMask);\n  for (size_t pos = 0; pos < buf.size(); pos += kCacheLineSize) {\n    auto p = reinterpret_cast<const __m128i*>(buf.data() + pos);\n    for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n      results[i] = _mm_and_si128(_mm_load_si128(p + i), mask);\n    }\n    std::memcpy(buf.data() + pos, results.data(), sizeof(results));\n  }\n}\n\ntemplate <>\nbool MathOperation<MathEngine::SSE2>::checkPaddingBits(\n    uint64_t dataMask, ByteRange buf) {\n  if (dataMask == 0xffffffffffffffffULL) {\n    return true;\n  }\n  DCHECK_EQ(0, buf.size() % sizeof(__m128i));\n  __m128i paddingMask = _mm_set_epi64x(~dataMask, ~dataMask);\n  static const __m128i kZero = _mm_setzero_si128();\n  for (size_t pos = 0; pos < buf.size(); pos += sizeof(__m128i)) {\n    __m128i val =\n        _mm_load_si128(reinterpret_cast<const __m128i*>(buf.data() + pos));\n    __m128i paddingBits = _mm_and_si128(val, paddingMask);\n    if (sodium_memcmp(&paddingBits, &kZero, sizeof(kZero)) != 0) {\n      return false;\n    }\n  }\n  return true;\n}\n\n#else // !__SSE2__\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::SSE2>::isImplemented() {\n  return false;\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::SSE2>::add(\n    uint64_t /* dataMask */,\n    size_t bitsPerElement,\n    ByteRange /* b1 */,\n    ByteRange /* b2 */,\n    MutableByteRange /* out */) {\n  if (bitsPerElement != 0) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::SSE2>::\"\n               << \"add() called\";\n  }\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::SSE2>::sub(\n    uint64_t /* dataMask */,\n    size_t bitsPerElement,\n    ByteRange /* b1 */,\n    ByteRange /* b2 */,\n    MutableByteRange /* out */) {\n  if (bitsPerElement != 0) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::SSE2>::\"\n               << \"sub() called\";\n  }\n}\n\ntemplate <>\nvoid MathOperation<MathEngine::SSE2>::clearPaddingBits(\n    uint64_t /* dataMask */, MutableByteRange buf) {\n  if (buf.data() != nullptr) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::SSE2>::\"\n               << \"clearPaddingBits() called\";\n  }\n  return; // not reached\n}\n\ntemplate <>\nbool MathOperation<MathEngine::SSE2>::checkPaddingBits(\n    uint64_t /* dataMask */, ByteRange buf) {\n  if (buf.data() != nullptr) { // hack to defeat [[noreturn]] compiler warning\n    LOG(FATAL) << \"Unimplemented function MathOperation<MathEngine::SSE2>::\"\n               << \"checkPaddingBits() called\";\n  }\n  return false;\n}\n\n#endif // __SSE2__\n\ntemplate struct MathOperation<MathEngine::SSE2>;\n\n} // namespace detail\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/detail/MathOperation_Simple.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Implementation of the MathOperation<MathEngine::SIMPLE> template\n// specializations.\n#include <folly/crypto/detail/LtHashInternal.h>\n\n#include <glog/logging.h>\n\n#include <folly/Memory.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\nnamespace crypto {\nnamespace detail {\n\n// static\ntemplate <>\nbool MathOperation<MathEngine::SIMPLE>::isImplemented() {\n  return true;\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::SIMPLE>::add(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    ByteRange b1,\n    ByteRange b2,\n    MutableByteRange out) {\n  DCHECK_EQ(b1.size(), b2.size());\n  DCHECK_EQ(b1.size(), out.size());\n  DCHECK_EQ(0, b1.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(uint64_t) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(uint64_t)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(uint64_t);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(uint64_t)\");\n  alignas(kCacheLineSize) std::array<uint64_t, kValsPerCacheLine> results;\n\n  if (bitsPerElement == 16 || bitsPerElement == 32) {\n    // When bitsPerElement is 16:\n    // There are no padding bits, 4x 16-bit values fit exactly into a uint64_t:\n    // uint64_t U = [ uint16_t W, uint16_t X, uint16_t Y, uint16_t Z ].\n    // We break them up into A and B groups, with each group containing\n    // alternating elements, such that A | B = the original number:\n    // uint64_t A = [ uint16_t W,          0, uint16_t Y,          0 ]\n    // uint64_t B = [          0, uint16_t X,          0, uint16_t Z ]\n    // Then we add the A group and B group independently, and bitwise-OR\n    // the results.\n    // When bitsPerElement is 32:\n    // There are no padding bits, 2x 32-bit values fit exactly into a uint64_t.\n    // We independently add the high and low halves and then XOR them together.\n    const uint64_t kMaskA =\n        bitsPerElement == 16 ? 0xffff0000ffff0000ULL : 0xffffffff00000000ULL;\n    const uint64_t kMaskB = ~kMaskA;\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const uint64_t*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const uint64_t*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        uint64_t v1 = Endian::little(*(v1p + i));\n        uint64_t v2 = Endian::little(*(v2p + i));\n        uint64_t v1a = v1 & kMaskA;\n        uint64_t v1b = v1 & kMaskB;\n        uint64_t v2a = v2 & kMaskA;\n        uint64_t v2b = v2 & kMaskB;\n        uint64_t v3a = (v1a + v2a) & kMaskA;\n        uint64_t v3b = (v1b + v2b) & kMaskB;\n        results[i] = Endian::little(v3a | v3b);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  } else {\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const uint64_t*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const uint64_t*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        uint64_t v1 = Endian::little(*(v1p + i));\n        uint64_t v2 = Endian::little(*(v2p + i));\n        results[i] = Endian::little((v1 + v2) & dataMask);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  }\n}\n\n// static\ntemplate <>\nvoid MathOperation<MathEngine::SIMPLE>::sub(\n    uint64_t dataMask,\n    size_t bitsPerElement,\n    ByteRange b1,\n    ByteRange b2,\n    MutableByteRange out) {\n  DCHECK_EQ(b1.size(), b2.size());\n  DCHECK_EQ(b1.size(), out.size());\n  DCHECK_EQ(0, b1.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(uint64_t) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(uint64_t)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(uint64_t);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(uint64_t)\");\n  alignas(kCacheLineSize) std::array<uint64_t, kValsPerCacheLine> results;\n\n  if (bitsPerElement == 16 || bitsPerElement == 32) {\n    // When bitsPerElement is 16:\n    // There are no padding bits, 4x 16-bit values fit exactly into a uint64_t:\n    // uint64_t U = [ uint16_t W, uint16_t X, uint16_t Y, uint16_t Z ].\n    // We break them up into A and B groups, with each group containing\n    // alternating elements, such that A | B = the original number:\n    // uint64_t A = [ uint16_t W,          0, uint16_t Y,          0 ]\n    // uint64_t B = [          0, uint16_t X,          0, uint16_t Z ]\n    // Then we add the A group and B group independently, and bitwise-OR\n    // the results.\n    // When bitsPerElement is 32:\n    // There are no padding bits, 2x 32-bit values fit exactly into a uint64_t.\n    // We independently add the high and low halves and then XOR them together.\n    const uint64_t kMaskA =\n        bitsPerElement == 16 ? 0xffff0000ffff0000ULL : 0xffffffff00000000ULL;\n    const uint64_t kMaskB = ~kMaskA;\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const uint64_t*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const uint64_t*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        uint64_t v1 = Endian::little(*(v1p + i));\n        uint64_t v2 = Endian::little(*(v2p + i));\n        uint64_t v1a = v1 & kMaskA;\n        uint64_t v1b = v1 & kMaskB;\n        uint64_t v2a = v2 & kMaskA;\n        uint64_t v2b = v2 & kMaskB;\n        uint64_t v3a = (v1a + (kMaskB - v2a)) & kMaskA;\n        uint64_t v3b = (v1b + (kMaskA - v2b)) & kMaskB;\n        results[i] = Endian::little(v3a | v3b);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  } else {\n    for (size_t pos = 0; pos < b1.size(); pos += kCacheLineSize) {\n      auto v1p = reinterpret_cast<const uint64_t*>(b1.data() + pos);\n      auto v2p = reinterpret_cast<const uint64_t*>(b2.data() + pos);\n      for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n        uint64_t v1 = Endian::little(*(v1p + i));\n        uint64_t v2 = Endian::little(*(v2p + i));\n        results[i] =\n            Endian::little((v1 + ((~dataMask - v2) & dataMask)) & dataMask);\n      }\n      std::memcpy(out.data() + pos, results.data(), sizeof(results));\n    }\n  }\n}\n\ntemplate <>\nvoid MathOperation<MathEngine::SIMPLE>::clearPaddingBits(\n    uint64_t dataMask, MutableByteRange buf) {\n  if (dataMask == 0xffffffffffffffffULL) {\n    return;\n  }\n\n  DCHECK_EQ(0, buf.size() % kCacheLineSize);\n  static_assert(\n      kCacheLineSize % sizeof(uint64_t) == 0,\n      \"kCacheLineSize must be a multiple of sizeof(uint64_t)\");\n  static constexpr size_t kValsPerCacheLine = kCacheLineSize / sizeof(uint64_t);\n  static_assert(\n      kValsPerCacheLine > 0, \"kCacheLineSize must be >= sizeof(uint64_t)\");\n  alignas(kCacheLineSize) std::array<uint64_t, kValsPerCacheLine> results;\n  for (size_t pos = 0; pos < buf.size(); pos += kCacheLineSize) {\n    auto p = reinterpret_cast<const uint64_t*>(buf.data() + pos);\n    for (size_t i = 0; i < kValsPerCacheLine; ++i) {\n      results[i] = Endian::little(Endian::little(*(p + i)) & dataMask);\n    }\n    std::memcpy(buf.data() + pos, results.data(), sizeof(results));\n  }\n}\n\ntemplate <>\nbool MathOperation<MathEngine::SIMPLE>::checkPaddingBits(\n    uint64_t dataMask, ByteRange buf) {\n  if (dataMask == 0xffffffffffffffffULL) {\n    return true;\n  }\n\n  DCHECK_EQ(0, buf.size() % sizeof(uint64_t));\n  for (size_t pos = 0; pos < buf.size(); pos += sizeof(uint64_t)) {\n    uint64_t val =\n        Endian::little(*reinterpret_cast<const uint64_t*>(buf.data() + pos));\n    if ((val & ~dataMask) != 0ULL) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate struct MathOperation<MathEngine::SIMPLE>;\n\n} // namespace detail\n} // namespace crypto\n} // namespace folly\n"
  },
  {
    "path": "folly/crypto/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"blake2xb_benchmark\",\n    srcs = [\"Blake2xbBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/crypto:blake2xb\",\n        \"//folly/init:init\",\n        \"//folly/io:iobuf\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"blake2xb_test\",\n    srcs = [\"Blake2xbTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:string\",\n        \"//folly/crypto:blake2xb\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"lt_hash_benchmark\",\n    srcs = [\n        \"LtHashBenchmark.cpp\",\n    ],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/crypto:lt_hash\",\n        \"//folly/crypto:lt_hash_sse2\",\n        \"//folly/init:init\",\n        \"//folly/io:iobuf\",\n    ],\n    external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"lt_hash_benchmark_sse2\",\n    srcs = [\n        \"LtHashBenchmark.cpp\",\n    ],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/crypto:lt_hash_sse2\",  # @manual\n        \"//folly/init:init\",\n        \"//folly/io:iobuf\",\n    ],\n    external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"lt_hash_benchmark_avx2\",\n    srcs = [\n        \"LtHashBenchmark.cpp\",\n    ],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:random\",\n        \"//folly/crypto:lt_hash_avx2\",  # @manual\n        \"//folly/crypto:lt_hash_sse2\",\n        \"//folly/init:init\",\n        \"//folly/io:iobuf\",\n    ],\n    external_deps = [\n        (\"libsodium\", None, \"sodium\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"lt_hash_test\",\n    srcs = [\n        \"LtHashTest.cpp\",\n    ],\n    headers = [],\n    deps = [\n        \"//folly:random\",\n        \"//folly:string\",\n        \"//folly/crypto:lt_hash\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"lt_hash_test_sse2\",\n    srcs = [\n        \"LtHashTest.cpp\",\n    ],\n    headers = [],\n    deps = [\n        \"//folly:random\",\n        \"//folly:string\",\n        \"//folly/crypto:lt_hash\",\n        \"//folly/crypto:lt_hash_sse2\",  # @manual\n        \"//folly/io:iobuf\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"lt_hash_test_avx2\",\n    srcs = [\n        \"LtHashTest.cpp\",\n    ],\n    headers = [],\n    deps = [\n        \"//folly:random\",\n        \"//folly:string\",\n        \"//folly/crypto:lt_hash\",\n        \"//folly/crypto:lt_hash_avx2\",  # @manual\n        \"//folly/io:iobuf\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/crypto/test/Blake2xbBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/crypto/Blake2xb.h>\n\n#include <vector>\n\n#include <folly/Benchmark.h>\n#include <folly/Random.h>\n#include <folly/init/Init.h>\n#include <folly/io/IOBuf.h>\n\nusing namespace ::folly::crypto;\n\nvoid benchmarkBlake2b(size_t inputSize, size_t n) {\n  std::array<uint8_t, crypto_generichash_blake2b_BYTES_MAX> result;\n  std::vector<uint8_t> input;\n  BENCHMARK_SUSPEND {\n    input.resize(inputSize);\n  };\n  for (size_t i = 0; i < static_cast<size_t>(n); ++i) {\n    int res = crypto_generichash_blake2b(\n        result.data(), sizeof(result), input.data(), input.size(), nullptr, 0);\n    if (res != 0) {\n      throw std::runtime_error(\"blake2b hash failed\");\n    }\n  }\n}\n\nvoid benchmarkBlake2bMultiple(size_t inputSize, size_t m, size_t n) {\n  std::vector<uint8_t> input;\n  std::vector<uint8_t> output;\n  std::vector<uint8_t> personalization;\n  std::array<uint8_t, crypto_generichash_blake2b_BYTES_MAX> h0;\n  BENCHMARK_SUSPEND {\n    output.resize(crypto_generichash_blake2b_BYTES_MAX * m);\n    input.resize(inputSize);\n    personalization.resize(crypto_generichash_blake2b_PERSONALBYTES);\n  };\n  for (size_t i = 0; i < static_cast<size_t>(n); ++i) {\n    int res = crypto_generichash_blake2b(\n        h0.data(), sizeof(h0), input.data(), input.size(), nullptr, 0);\n    if (res != 0) {\n      throw std::runtime_error(\"blake2b hash failed\");\n    }\n\n    for (size_t j = 0; j < m; j++) {\n      res = crypto_generichash_blake2b_salt_personal(\n          output.data() + (crypto_generichash_blake2b_BYTES_MAX * j),\n          crypto_generichash_blake2b_BYTES_MAX,\n          h0.data(),\n          h0.size(),\n          nullptr /* key */,\n          0 /* keylen */,\n          nullptr /* salt */,\n          personalization.data());\n      if (res != 0) {\n        throw std::runtime_error(\"blake2b hash failed\");\n      }\n      sodium_increment(\n          personalization.data(), crypto_generichash_blake2b_PERSONALBYTES);\n    }\n  }\n}\n\nvoid benchmarkBlake2xb(size_t inputSize, size_t outputSize, size_t n) {\n  std::vector<uint8_t> input;\n  std::vector<uint8_t> output;\n  BENCHMARK_SUSPEND {\n    input.resize(inputSize);\n    output.resize(outputSize);\n  };\n  for (size_t i = 0; i < static_cast<size_t>(n); ++i) {\n    Blake2xb::hash({output.data(), output.size()}, folly::range(input));\n  }\n}\n\nBENCHMARK(blake2b_100b_in_64b_out, n) {\n  benchmarkBlake2b(100, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_100b_in_64b_out, n) {\n  benchmarkBlake2xb(100, 64, n);\n}\n\nBENCHMARK(blake2b_100b_in_128b_out, n) {\n  benchmarkBlake2bMultiple(100, 128 / 64, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_100b_in_128b_out, n) {\n  benchmarkBlake2xb(100, 128, n);\n}\n\nBENCHMARK(blake2b_100b_in_1024b_out, n) {\n  benchmarkBlake2bMultiple(100, 1024 / 64, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_100b_in_1024b_out, n) {\n  benchmarkBlake2xb(100, 1024, n);\n}\n\nBENCHMARK(blake2b_100b_in_4096b_out, n) {\n  benchmarkBlake2bMultiple(100, 4096 / 64, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_100b_in_4096b_out, n) {\n  benchmarkBlake2xb(100, 4096, n);\n}\n\nBENCHMARK(blake2b_1000b_in_64b_out, n) {\n  benchmarkBlake2b(1000, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_1000b_in_64b_out, n) {\n  benchmarkBlake2xb(1000, 64, n);\n}\n\nBENCHMARK(blake2b_1000b_in_128b_out, n) {\n  benchmarkBlake2bMultiple(1000, 128 / 64, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_1000b_in_128b_out, n) {\n  benchmarkBlake2xb(1000, 128, n);\n}\n\nBENCHMARK(blake2b_1000b_in_1024b_out, n) {\n  benchmarkBlake2bMultiple(1000, 1024 / 64, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_1000b_in_1024b_out, n) {\n  benchmarkBlake2xb(1000, 1024, n);\n}\n\nBENCHMARK(blake2b_1000b_in_4096b_out, n) {\n  benchmarkBlake2bMultiple(1000, 4096 / 64, n);\n}\n\nBENCHMARK_RELATIVE(blake2xb_1000b_in_4096b_out, n) {\n  benchmarkBlake2xb(1000, 4096, n);\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/crypto/test/Blake2xbTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/crypto/Blake2xb.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <folly/String.h>\n#include <folly/io/IOBuf.h>\n#include <folly/portability/GTest.h>\n\n// Note: the test vectors in this file were generated from\n// https://github.com/BLAKE2/BLAKE2/blob/master/testvectors/blake2-kat.h\n// using a script.\n\nusing namespace ::folly::crypto;\n\nclass Blake2xbTestBase : public ::testing::Test {\n protected:\n  static folly::ByteRange getHashInput() {\n    static const std::vector<unsigned char> bytes = []() {\n      std::vector<unsigned char> b;\n      b.resize(256);\n      for (size_t i = 0; i < 256; ++i) {\n        b[i] = static_cast<uint8_t>(i);\n      }\n      return b;\n    }();\n    return folly::range(bytes);\n  }\n\n  virtual folly::ByteRange getHashKey() const = 0;\n\n  void checkTestVector(const std::vector<std::string>& testVector) {\n    folly::ByteRange input = getHashInput();\n    folly::ByteRange key = getHashKey();\n    for (size_t i = 0; i < testVector.size(); ++i) {\n      const auto& expectedHex = testVector[i];\n      auto actualBuf = folly::IOBuf::create(expectedHex.length() / 2);\n      actualBuf->append(expectedHex.length() / 2);\n      Blake2xb::hash(\n          {actualBuf->writableData(), actualBuf->length()}, input, key);\n      std::string actualHex = folly::hexlify(actualBuf->moveToFbString());\n      EXPECT_EQ(expectedHex, actualHex)\n          << (key.size() == 0 ? \"\" : \"Keyed \") << \" test \" << i << \" failed\";\n    }\n  }\n};\n\nclass Blake2xbTest : public Blake2xbTestBase {\n protected:\n  folly::ByteRange getHashKey() const override { return {}; }\n\n  static const std::vector<std::string>& getTestVector() {\n    static const std::vector<std::string> testVector = []() {\n      std::vector<std::string> v;\n      v.emplace_back(\"\");\n      v.emplace_back(\"f0\");\n      v.emplace_back(\"b5aa\");\n      v.emplace_back(\"bc38f1\");\n      v.emplace_back(\"57624fb2\");\n      v.emplace_back(\"ea9d54f5f2\");\n      v.emplace_back(\"2bcb84c09d35\");\n      v.emplace_back(\"2df3b0c53f2967\");\n      v.emplace_back(\"26de76fed412b6f1\");\n      v.emplace_back(\"b91f740750ffdb2aa9\");\n      v.emplace_back(\"e161ee158218cfd98f91\");\n      v.emplace_back(\"6bdaf88b0922b637274001\");\n      v.emplace_back(\"eff9f92cbd769c81a64c20f2\");\n      v.emplace_back(\"313f0d863f80e115d342afc286\");\n      v.emplace_back(\"9ac7c942487e8f48b1bec271562d\");\n      v.emplace_back(\"221d56ed5ac8b0936111c773f9a744\");\n      v.emplace_back(\"d0a446adfd1fe0cc61e42e70772584c2\");\n      v.emplace_back(\"a7248218b83af8ca2728d9aca773438100\");\n      v.emplace_back(\"7ada5a3cd09be192f152e36eb77a49228bd5\");\n      v.emplace_back(\"8969cf3e34b108498786f3c9807a54da29c5df\");\n      v.emplace_back(\"d6981306dc3865bebe4ee085ae81a11d43601bba\");\n      v.emplace_back(\"e1f7cd444fc960831929a1ba4c81316537e315dab1\");\n      v.emplace_back(\"dd675ed39403b1500a2a619889c2d9d91791fe15d177\");\n      v.emplace_back(\"d503e3a0261176767e318f079e9c4941cee791d3db3e03\");\n      v.emplace_back(\"4bcb663054528ad4cc568d12ee1ac0e33790e0635189e53e\");\n      v.emplace_back(\"ee8ad528ce57395cfc101654a2e205deb62429640755a6067c\");\n      v.emplace_back(\"8435fb101e96b9dbca75a444212a99211ccb35173e9f1c2b01c9\");\n      v.emplace_back(\"bb849abcb58a9e8a3a2becdf0550774cff0093d5ee5dab5e9db38a\");\n      v.emplace_back(\n          \"0a74d45327c814c45abc41713680021f487be9a133455a7550fad2e2\");\n      v.emplace_back(\n          \"f662e426340a253766ad9a1a13b7f60db4bdf953a04456789b5a261543\");\n      v.emplace_back(\n          \"1a03271192bdc3981f166cb43945d4d78878cd2a2ab620f56337f4cf1206\");\n      v.emplace_back(\n          \"e8cdc5a0f15392219c8ad35abf0c1a976fb430debe801887ac8000a7968fd5\");\n      v.emplace_back(\n          \"b5d259e2e3a86c77cbf6d53f9dc78daddc2afd84dbb4ba7e9891227fec079d5a\");\n      v.emplace_back(\n          \"d3818948294fdecac8411f860f7cad50469df5d1485524e059d4dd8cfb69c32b\"\n          \"bd\");\n      v.emplace_back(\n          \"8e3ee191e4d30346f19ab6904b6e810d416a87a1da3c7f78445db72fe49f6157\"\n          \"05a6\");\n      v.emplace_back(\n          \"c2622f680f4350aadb5dc35300e08197b1e968a1df8b3091cadc3abfe261a269\"\n          \"605319\");\n      v.emplace_back(\n          \"7030a487b0c270d224a0f2eba284b876ac44576a119546af47627417ddce0f46\"\n          \"50bb8b56\");\n      v.emplace_back(\n          \"1c09ce4bedf8ee67d19430f8a4708d73f0be22e19c55fd397471e7705ff99586\"\n          \"03911f6a38\");\n      v.emplace_back(\n          \"84f7f7502bd140a07b57e0f69863874a635403c111836b3fdfd27a030582e6d8\"\n          \"e46162b62cf7\");\n      v.emplace_back(\n          \"63971b351ba119eff6342fdfda6bd558edada5c56e65ec0648ba3455fca1a3d5\"\n          \"1b603c028ead8f\");\n      v.emplace_back(\n          \"64e5a1f06e4d5cb5859ff01f3af1dcecdb89729d97aad3d4c8cd96eed4bd1048\"\n          \"7f918ea0ec6c96c0\");\n      v.emplace_back(\n          \"d3cbcee509e8efa84f4c54f6eb097f17ba98c23024097f8ae5498d364d45afec\"\n          \"b6ec1654e2e3c073c1\");\n      v.emplace_back(\n          \"c35631a7735d09551b02c64eb3edfcfdd515c12646bb51695ac51681b3384119\"\n          \"7e92f6c3a0a2691cfbd9\");\n      v.emplace_back(\n          \"4835394320ff955c272fce2a6eefc8279aaf63492610912ccb525a9f3c788703\"\n          \"97c6dd35119900e88385d8\");\n      v.emplace_back(\n          \"66c4227b1a29889d5cc7e2025fa4649c365e56d153eb4c4e1790b3f0a26a3553\"\n          \"c51f198b04851401af1c0acb\");\n      v.emplace_back(\n          \"a1550d81d0e465b8e1bd228e0d3b71c29a23f8cf58d9c43361ecac7eff8698cd\"\n          \"68bca923c25d08be3c92bb9926\");\n      v.emplace_back(\n          \"3ba25c21d16bb7c93ce16fc914b1bdb5b7ec249ba7dda6be1533c76e8dc20b70\"\n          \"4913cbe53201086b0e14e5901042\");\n      v.emplace_back(\n          \"0c4c0363a32aa180de2d3aebad786dfa1612141ae77f2eefedda3fc6366f34ef\"\n          \"8d64e496a30e972ead3760f13553f5\");\n      v.emplace_back(\n          \"60fea990c5efb33b64005694d1ec92c90ea86f434ffc603cc26393a6bcdaaf99\"\n          \"c8993f6f2fc5a3080a5cca5532697a08\");\n      v.emplace_back(\n          \"67c9623f20ada31853c15e3d973b8a9c69643c3e328908bd138fa7d74789b4e5\"\n          \"408da66ca04f0a286cda823c738958665c\");\n      v.emplace_back(\n          \"dcda6413fdbd760d59bcdf5d28000c099818c9237511acdc6e0ad40819d47c40\"\n          \"f0f883bb0b98d3caefb7fbd281db805d3aa8\");\n      v.emplace_back(\n          \"120c123171c486c9a11af8d7a1aeef60f78d071c8edb55fd97959261e4c708ac\"\n          \"06eacee87e657b84a5072a7989b101c98b0415\");\n      v.emplace_back(\n          \"434cb92246e474ce066de67de1fce06ab17416438598d3cff730faf1deb45748\"\n          \"12e877f6a3f2dbc30a3e48a2cf4e441da32c4ee9\");\n      v.emplace_back(\n          \"dd4cde9f87ecd959a41c6454869e55342ce0e5d2ff306b3d4dd2263365e192ee\"\n          \"6781fe463175280d4682b397b8d6020699ed3a9611\");\n      v.emplace_back(\n          \"df8471721ee7aae06d5aab20ac9ab5624797cd0311ee38116eec76748a42aa5a\"\n          \"d23d1e3dcadbfba6c296aceaa05512cf1f2a2b415c14\");\n      v.emplace_back(\n          \"d226e59df92c995bd5fac8b9cdbd4bce4e11d6b2a2cc382b253a5188e7f41640\"\n          \"63d1daff2254b4cfc7ecca462b7c1e11080c1ae51dd908\");\n      v.emplace_back(\n          \"0316b9cf375915d70a2c0a0f6560a609b3fd43bc8b26b8489caaece3c8cab25b\"\n          \"eecc3bb86d3860d6f2ca9297625fa2f2d5fcca5f6f0a32f6\");\n      v.emplace_back(\n          \"828bdf033346520f262ac1383a5e7091640fb9df39c51f82de52bc61b7284f4d\"\n          \"7cfb1e90fa19d0ffe3f38dfd60fd05136d66c190cc47639634\");\n      v.emplace_back(\n          \"9388577eb0bacab40bfc38e2333f82fe72b575392db914da2706bf2a30787d63\"\n          \"8b6b31343e8245e4dbb2c962cf3f940d8ed0945d2db04b902b0c\");\n      v.emplace_back(\n          \"5a79d4de91c298c87bfba338f3f85efe00275f6e5463419af83c34129586f30a\"\n          \"3d36f57bdf68c9b5e16373c9f9921866c302bc75722c314fc57cf4\");\n      v.emplace_back(\n          \"73068c5b623b14802a9eb899f6285ebb7e601c4d916b0255762b7cccc2161417\"\n          \"695818a605fed681fd4016e4cb1ad3a42cd88fe1a3f73367ae0aaff6\");\n      v.emplace_back(\n          \"668d87bfd70619d877ee8d8f86b5d5ecc4df2aacbad00f2dcf7c80741be0b890\"\n          \"bf404bf62c1b4c8d1b0201ffb92d85fdc45149fa58f557a9c6a0190e7d\");\n      v.emplace_back(\n          \"cbaab54fe4bfd9ed5d9b7fc97d8a4105af147e13f009514ddb083402ee941ecc\"\n          \"7dc3c286308d9555bee67cd73505142758db79fd819ed399e490b801623c\");\n      v.emplace_back(\n          \"d9942e996573688a348aa0fd1a2951b11d7732103acc23f31f27b222d5103879\"\n          \"b9d3837f2571a7aebffd170ad03cfd89281f48fa70edb7c9f4103b5b8bb791\");\n      v.emplace_back(\n          \"571be91037c15145e2ab4894a7bb8d8a3cab75e6e64ef296e760c15cf8f3f3ac\"\n          \"fa5c894ee56cb6ac2db9b32c39a1cc39f96c50dd333f1059230482f3ed2d9246\");\n      v.emplace_back(\n          \"c6f0b1b66f22726cef3e4fca2325d2bb4e922b39f9df5ef548d321419c07391f\"\n          \"c311904407f98db7d7462db1e8576138baeac2a76400b2a2f72b4497c19e2394\"\n          \"30\");\n      v.emplace_back(\n          \"43cb5507bcb6d7162f7b1d6b81a958b1e21ed2db9ae907a663819ae0d613ebb4\"\n          \"3c4b39359ff859ce90a9f65c369c6d30b83aa56b0107a5193a6fadced2d6c0ec\"\n          \"5be9\");\n      v.emplace_back(\n          \"d412ca3d1b8fa9adcda589c5c422c295c3fe953ffd896e7a98f9262689bd1067\"\n          \"0047d9224b685c449b6daa5ff5d660c3ecbe5b3865652932d35cf15176de70e4\"\n          \"356ed9\");\n      v.emplace_back(\n          \"54955d39b0806ec95897e117064c4b173f20fb248596ac8b00ce57de2d88c01a\"\n          \"12f42d3a6f0de1d1af8c41c0b2f8dcd613532314f773bb3ad0f470500466f0d8\"\n          \"ae599164\");\n      v.emplace_back(\n          \"713b87f7f4f2edb42d279e9ca3bef61e8ceaad72e6ea90cfcae4f9638798e00e\"\n          \"8d4b464bf49ff26231dfca5a4dc8b3d47afb36708f494ea2c20cb9bd3d344901\"\n          \"a42a95ff82\");\n      v.emplace_back(\n          \"c9421a7a80df6882010c6c4aff7ddf920924fc77246d1870da90f183c14dc326\"\n          \"4faeace4c76426020d8383efaca2abfbb0957f1cc8249a212699019d36afae81\"\n          \"1253e8bb3b26\");\n      v.emplace_back(\n          \"2f28d45cdb35951f9e043335c0df22d53e238a7b2df3bfd74d5656bb7e65f24d\"\n          \"12c35fe0254669622edb9f76fe2672a7978dff201aecfd2605b2b326a73a43fd\"\n          \"470dff9d8d98bb\");\n      v.emplace_back(\n          \"93694e4b1a7b15ac94963b9111f86a29266f4beabf1fce740ee44fd264ff44ea\"\n          \"dd8d0df5aafba8b8b65f48513a5920bcccd2e4d9c3a90b71fe51e11e2857df2e\"\n          \"0379debecb4ade7f\");\n      v.emplace_back(\n          \"48887c63b6a5b7351632689a03b53cfba034c653cb65ba6756e0f816eb630663\"\n          \"b263ea897025b65703cac600e1a450d71c945f7063d1606f0950da744f47ce00\"\n          \"21d7a180e943ee9aef\");\n      v.emplace_back(\n          \"44c7a8ed8751f54d0e5428d1ae063f1ec081d93acca64542d28d8c11aa0011ca\"\n          \"ec398a2897b3c3a15ed382610b23620e833ab295d9a0eb61afd2948b4093d9e5\"\n          \"df08d01d03dd6834742b\");\n      v.emplace_back(\n          \"d625de2d8ee2ad2ef5a207af5092eb7965c4df09ac6b55ebe2bdaae799162a32\"\n          \"e576925129f32f02c00e42bb2ce5afd73e0c64b9fa8298fe1495f0c8f201ad5c\"\n          \"6780b83d58787cb2b4d8f2\");\n      v.emplace_back(\n          \"36370178c82981799e8622265c63ecf2329875efec250e995a8de5064aa5f1dd\"\n          \"80d2854adb1d806f6bf2360c567c34e802d58fdd0ea15008b20492e09a6e11ff\"\n          \"340de57dd8b03aa319d61c41\");\n      v.emplace_back(\n          \"554918ff5b98e3df9c43ab9559a75e8eeb8f2bcd5c4bc87b8a9c2329df8fc35c\"\n          \"241bbf9ad354fba00c318a2057e9eea6184260fb11072f57c6a587add3043c9f\"\n          \"2bbc162abc6e50d06c7c673c9f\");\n      v.emplace_back(\n          \"72715c4ad8a633712074b9f133fea34cad5f7c44aa12ae7f1027b03500a9a132\"\n          \"a7ecc477c0d18cf4a1a794e064eeca6743534ce07dba5ca211251c903a6d2729\"\n          \"d02728161fb8812ff511e7e49c16\");\n      v.emplace_back(\n          \"98a842b0fbce066ec6778cc378ca4b90c69ef7570247cf789dd5c9a502f5f7dc\"\n          \"b3ab02c32f06375c2e153237babe51d0cf3fee0cd75bf1e34095a98ba712a2e1\"\n          \"1c1017500abc9238dc1494c527c4d5\");\n      v.emplace_back(\n          \"2e548c4d06fe63198b95a9629e5c68372f8c51b0f2689a2a0b7994a204dee4a5\"\n          \"669525786c5709e68ff35faa3d29be6f8902ff1bd742c4f1534fe3d6b5cf0b4f\"\n          \"6f1c0f9415bd9801ca0c33c8e11940d2\");\n      v.emplace_back(\n          \"9ea0b2e651b1d1755a7f67e78d2333b658bd4dd49157795657a7ad8bbce158dc\"\n          \"0d4a002e5e737f52ab1e55abd0cfadac928a977949b264946af48045920b46da\"\n          \"19f63649dc116217f6ac67355edb94c46f\");\n      v.emplace_back(\n          \"740fe0d1f4db6f6337d300089af7a166ce44da3c6e71e80ad6e92d604f80ac06\"\n          \"7eb077f2ac2ad76bc0604a8089ddbd22ed3373438e5ee0f25dc34ed466ff4b42\"\n          \"0d77db7e1e9d88a4ac919fc421116b62e673\");\n      v.emplace_back(\n          \"c435ab1e8d4697596abec90384c47634689ec73e87fb2d360f30bc5b4b47268b\"\n          \"f3a1f31b271b800132f8a45dc85f82ee7620d50db5bdd400d36a36e1c87cb2f6\"\n          \"37c30afbd07ae34417e77ef2dd24e017deb7b9\");\n      v.emplace_back(\n          \"7f32551c0b81ba6233553c8ce988da296fa2e345262950188cf6372aca5bcb8c\"\n          \"edbbec424978310adddec426551681f93a9b4cf6e15a06ac70650dd211386498\"\n          \"d45dbd6b70d66b843f73f07fdec611bbc5ee0440\");\n      v.emplace_back(\n          \"d8e9839dd767f514a33125051022d7e50c05d6521d852fbaa635fed502e59554\"\n          \"bc9b8a1a31753f4fe90d2f270b27e73d65edcdecc18055d53fe1859744ca3d5f\"\n          \"39bfa6b23a4cbdb9c326d7b3be831ebf7c0abbe676\");\n      v.emplace_back(\n          \"a1d587c939232585840b8f9ff27503efcbc1f59bca47f2dbae3fcf8ce26743de\"\n          \"bf6d67936f3d45bf2cf7474eb8f69b0765f362867be29a7ccfe41710e2c3c9fb\"\n          \"5ab8a0a860612461e7f4b52ef28c73a087ef0852116e\");\n      v.emplace_back(\n          \"63dad275e477674191cf03436e979fbf16b1220e81ea8965fe53828e46f06a1d\"\n          \"bd1a6bb03cabfbca70261f63d5cf491e54e31c024e87394a3cbaa9ea1cab3a2f\"\n          \"3a6f5a015888c01150286460dabadc8d0af900bced9a64\");\n      v.emplace_back(\n          \"bbe011c112d53f842c0cffe98d96855b8d775c8c1572d29ffcf3feb0bae18de3\"\n          \"17e1db03f847fcba90ed941095ce0b2b96c8b1c7d9dc2afda7f08d16ca6c0f32\"\n          \"d3a5cafca2fac92487c5a177af200c9adc866112629beb26\");\n      v.emplace_back(\n          \"ddafad4d5b5fcbc07f2222f9765f750f7a526e5894a165bbc9ddd6de71e23775\"\n          \"246601393f488b61ca9f26aba2d3847de759f4082999e472e40c829e6923282b\"\n          \"4f6c1d3702071457c7fb2dbd0db54c5c6072159d1fdcdf90b0\");\n      v.emplace_back(\n          \"275cd52846616d516fe77a6c7a4069ce46f120b66fe8043be79dd70906abb006\"\n          \"ce5ced75dee096277b8c26899323cb8567a8c389578fa0ddb0b0988ded7d96ca\"\n          \"69d8b78abd52663fd20e66a0ef4660e1f38460db06304479e421\");\n      v.emplace_back(\n          \"39e75539fdefb2bd552b0009e7b2fa583afc8a2b011b505de62aba44c545e13b\"\n          \"86590c731fc2fd848a219a510c3f1184ba0149283668ba93dd5a056cf5b75220\"\n          \"0659991351a7db19f04fcc7f96b3d25de5b4a726c7dad1b7ec7768\");\n      v.emplace_back(\n          \"cdb61379ab20f7975a61b52dfc3f218d5f803f08e8286881aecc94b92f469239\"\n          \"73f227b7a1637c1269cc87b9634bce578858f4e9b04fa60ee0516899d71573c8\"\n          \"e7560886dfea6d08b744010c0a9c236f3caabf523cf3a3d7a075e23b\");\n      v.emplace_back(\n          \"a1b3588cc3742b70cf826f7af7c45ed5b4cba2559541e34cdd3562a216afdaa2\"\n          \"a4e39436183ece09c222bc77ed5cf7b806b7f67c703f5c273a7d5879a6300292\"\n          \"24140f9b33bf2f4243372e8f781851f7db7d3dd8c795d161605257aaf4\");\n      v.emplace_back(\n          \"3443227e56b16df3f7e15deeb5c8c0713300348703503a82474f964612ae13a0\"\n          \"47925c3f5b6b364af3f5f89f3b8fbfd1814a42856ed777b90e702256d241938a\"\n          \"60b16d00a65143762ca29f577405301979aef51ac5c666247dc2f932ffe0\");\n      v.emplace_back(\n          \"e0cde88552c1f5e0f9d3e7a97ccb49996aedc8d38093edd3930094002306f729\"\n          \"b8f55d1fdd54db364173ce2abfdb65a35e698f78ffa02686119217597b26216a\"\n          \"f81ce7c7701e9ecc74dda65feba3d63e3e7dfc1f0a2c7ca13c552fe16c2830\");\n      v.emplace_back(\n          \"610a5ed42281e3b93a210848ebb8203b8eacafc2a19a502d8baaa2e604a573ba\"\n          \"c3acd16f265ea4befde07c5de8c0c5cd019877d90de6c3e93df1afb6930ae311\"\n          \"bd52b7c6e7c677aac72df9edd2657264d145755dc936193e1ecc44edb1246dfe\");\n      v.emplace_back(\n          \"99f96987a955b931534b13d38fb49f383078112195ac492fd6cc44a6efb2e161\"\n          \"ab87caa50a594fc7f86328f374d4b21c7802a84f99fa498c22d62c461c294534\"\n          \"7e6abf1749afe2e22c0aae2f053b6a6bc7854f56503f3ea6d70193287bf9b82f\"\n          \"23\");\n      v.emplace_back(\n          \"3ddc943c2a5e5d0786d8671083dd8893906b02a610db62bfe64c7e6ab0866464\"\n          \"74483193062f08f903866d6050b50ef55213e3935aaeccbc385f90ebd040f7e7\"\n          \"efca8be101824770d1e5e7ef92fd65d148e63fa627469c3f2b5fd5e5e4476159\"\n          \"665c\");\n      v.emplace_back(\n          \"3df18ae1f63e037219787a95ad960e967671a8389c1ef07c17be3632d5bfb30d\"\n          \"dd86ccd7b53f191baa81dba189665407df6d3b1931c7a94c2ad62bb6ed9b7da1\"\n          \"dc9b2a5b98cc069abb2c7e58648ed4436d359eb60fe5425c16103d20c793ca66\"\n          \"bf847d\");\n      v.emplace_back(\n          \"726ead16f67729ba596654a551eb126e99457962286fc54bb6baf50d93c28340\"\n          \"9694db0142264b697e6d9be81bd7f63e4965c784ef0af12529294ef7795e9d64\"\n          \"c371b15c1a5701c48dae9e2a3d908602c4a82bbeddb9a20eca30b591140f76fb\"\n          \"c11a3df2\");\n      v.emplace_back(\n          \"bb100a9b0c720ffb4e57422af4017c7eee0396f9c1b1174e0248298d521ad171\"\n          \"ffac53c622b68f45c9482b3e520170f44b9ac4855f25874674afec56ba9f608c\"\n          \"6c7a6e8bc9b77dc7f5f48a148052e649ff31004a47dc1b3f15bc668b06086268\"\n          \"4bae6cc402\");\n      v.emplace_back(\n          \"cde6c07be9135468c33b09e648fa0551578d8b7317fffa60add7e430838633f0\"\n          \"fc1efe2783468c96c74e2208ac947d726b139b5f5b682bf615910ea9a911195f\"\n          \"71ed8ed899f7b8ed126d6452cbcdbf6aee558662de0f689d766e69ab2244f5ca\"\n          \"70e6bbe9cc4d\");\n      v.emplace_back(\n          \"591fbc984e4b79a372b2dc951afbc269dc51afcca0f131840335fd93275b40b3\"\n          \"2be13db09b36a8bbbe7644470093210e04ed4832e6aea478fb5028320ca8ef51\"\n          \"3f27d0b3d1c018fd7d2fe1bb1b8da6fab196dbebc33043b13469a114153267a3\"\n          \"d4668d062d109f\");\n      v.emplace_back(\n          \"b14803314977b0e29e3ac469b17206b527b6e95e3a47f537bd7e5e18ce69e0e7\"\n          \"83737f8b1d993c48b0dc2078b0bacf2f752ade8a0a709f8b27bb5efac90177d6\"\n          \"bad0d946223992cab2bedd83f8e874f13839578bfbb1e283423616b9cc9c5a77\"\n          \"93ae921664338d4e\");\n      v.emplace_back(\n          \"5b3f9b47a210b68bb40b96396fdfdc4b130cf0c4f7708277a6bce760837dd850\"\n          \"8f4e321e09cd36cde6fb6125ede04599064fd9a7675c0508b240b8352e711686\"\n          \"68681eb6a0aac08dd9145439ae2ceb9c7b0e575230c51e1f89a08fdd4590c3c1\"\n          \"bc2c397b64d098302c\");\n      v.emplace_back(\n          \"dd31b6a184c00932da12262e6030c8045d45433e15b975eaad70144143c8b9ed\"\n          \"e3c71f4bc0324c04617ffd377362caed64ee57e40cbd952b559b54b59fc86832\"\n          \"b687d08931403f854f26297251e606c75f41717228a3a6eff683fad7528f2529\"\n          \"60a286e15edac01a5a59\");\n      v.emplace_back(\n          \"872cba270f1ac791d7444b1222ba36706735ee0a2794cbb33eb0e7f1e091f061\"\n          \"24f61c1a1332e78f71290c8e9bd3f8f7b73d7619958a2d9a8ec9e7345e43c2b4\"\n          \"9868264ce15785577f4b7542b4dacbea045850d38c006e40f61a710b660ffb1b\"\n          \"e2a9697d0c50802c19fe2f\");\n      v.emplace_back(\n          \"190acd9ec74d1e20da6d30b7c8ba4a8477d87cc700569017e74dbd1fde1e66fb\"\n          \"746d43cd115e7d4e4e960cf23a762325c2fe0a36fe5f9b1f5b3d100cd0427c97\"\n          \"47b4ed2fb8e4c1f8e86da805884c55333f5d8b29db7317699919f927b235aed2\"\n          \"6014b4bb0ccf02b6b3ee4ea6\");\n      v.emplace_back(\n          \"d7b9dc5c89dc7a2f6caa59d66faf48c7d3cd85b40241ed5f839f7693a637e2f9\"\n          \"95300cdbb942dda736929ec84cbb4113a982666b9f49f7758b1dd8cfe1edf204\"\n          \"9f8f822afeb9d7b469839325e1a854a0a48fed747ccdecd01f1dec302899578a\"\n          \"27947eac0ecf4f07742b311053\");\n      v.emplace_back(\n          \"1c8f939474416a28984e8e5edd261c73757210a84a070b8feff99a3395e5f61f\"\n          \"4eb5fc97b4a10015d5adc35fdc79be330112cb1133c9ec8362872029cff48c1a\"\n          \"ed3a4734d343208a809ecb1280e442ff80cdd793ffe3a0feb207de7ffdf685f5\"\n          \"95633b758f80f0e932464935e79d\");\n      v.emplace_back(\n          \"bf442201603db1da2d8e8dbebad06c0856aa36008825fff03295f3e81219f070\"\n          \"8983414a8e584c2e40bc897a777a03923b3be75dd66b764863c67b7dcb18fa78\"\n          \"1e2543f8c1a901e9e7a50c125f7ed0202f5fe5ffa4e2ebb3242e36e2ffa25ac5\"\n          \"fa6d86ee556310a7cecc84a023b16a\");\n      v.emplace_back(\n          \"33166d74ead94fd2ea667981edbe87ff5a7418098953bd4a293ceb01954e8399\"\n          \"1ac116ad990bd176a885ebec291a3b2385d78e7b2c1034849b413a66bfea9891\"\n          \"0e5aaf3c3a83b726ec63c94b8f36832235f5986eefa495e7e9e1320ad00ff57b\"\n          \"7898284a0f1550986cfb5ad938bc8e35\");\n      v.emplace_back(\n          \"eca675d47a8c160371cfde83919fb31b65653792432281718d113780c0d0c1d8\"\n          \"eed4f5e0238606d66fa0a3b515716ba58535c7d36a2a3835f7599ed6601a7e14\"\n          \"67adc1720514d78946a1658139482d3ec38cf5d6aeb58f79ec51780b780a58df\"\n          \"316a05784764d791e3a8f37368137e8ce3\");\n      v.emplace_back(\n          \"923bbbb27c5a11d4d5305a35646543efc0ff2c38b8602f306024b2b5954d9400\"\n          \"39720677ec0e873c8e0e83f9581a045867e9b2c02edb359249d9e006dcc6c79f\"\n          \"75c9cc5dee9c5f04ae43268d5a4a1da37122904b2750aa8aa43800b7ff90e070\"\n          \"41b9752cc7001928d9fca5e73874e4fd78eb\");\n      v.emplace_back(\n          \"16353325821cbee3c476b1f872fc6822a902426f812affdffb50b5cb7c8b5c55\"\n          \"0133c9135e0ac6068c3f8f0709f1a720717da2833c3a83dd9e6faddc45502950\"\n          \"c33ac14d35dd05a96cc7a41158fbfbada5e5775668c6d0724a454446655f25e7\"\n          \"a212e6d6b6335df1c86d0db17332fb4d12698f\");\n      v.emplace_back(\n          \"04a979bac8684f95894f1c4db8009a33bcc0c054858c7e8ec40f0820d9c98e09\"\n          \"758be2b492426333cdbb0606fa7981033aca5afe0d13c89bb51b8c3f5b655931\"\n          \"83c91eb23165a141b4ed8b9064469c71301541b8f7d087d5bdbf192b99c8f5cc\"\n          \"440f01c3e29631c5d10c88f9f3ef9236fb42a1e5\");\n      v.emplace_back(\n          \"19352454e048610cb26842f57414f8f62285eac8944f6c448a09b2706c8b8532\"\n          \"67ef45d93a5056a89b2f28dece8475b4232f6206ca0c9090ce731b0dffa51303\"\n          \"83eaed7a81f06c457a4684e5ee1783d4792e0d47681b7262757ed3446f037e6a\"\n          \"9808972585cb2ef0074c07994d30caceecf9a9d66a\");\n      v.emplace_back(\n          \"965ac58510f8f8f446dc09b91d3eda85b3a2de1350a4ede9aa95391bd116898f\"\n          \"d4e70c0311df9353e602b1d8f1060d69b8dd8672fcd6d1dd7249c804c5b4031d\"\n          \"22896019809434483beb3eceeca78f11415a71e101df3fe5eef09afa97a1d1c6\"\n          \"6ee3f3efc08cc35a5268e06521b1f0742a45d0fb4053\");\n      v.emplace_back(\n          \"5f2215f843ba3949f5c68cdf5cac13ecc64a589ba0d752ef877a4928dc462d91\"\n          \"8395c83bd1c1f5dc3036621e75a51038f9467a8023800d6545b970abda4029ce\"\n          \"1fabf0887ae3721f3494dc15a6eaab704969a5b4670c9339f181ea91eb7085be\"\n          \"064154f6a359f12b6715e6a1190fe9fa2aac0b1a082f91\");\n      v.emplace_back(\n          \"8e6a13e3d41197e3f8b897761594dd9ba97ea1166a281fa01e2804e92597dd59\"\n          \"6d25726df67d9d7fcef9607a6fd248ae1502ee743f6d0eb3a1a8efc621f86bca\"\n          \"cde2c53e091f6778eed63bcf5092ea732ed2ef7f71090f4c41d0b6567a4d7fa6\"\n          \"2c40ce14d7321f5fc18261a7c86fc06c764e94eb6b72f63f\");\n      v.emplace_back(\n          \"13fe6282f3c1379432cba49fa10fa325d165fe17e7923620c4172759bc898939\"\n          \"5d16715cffd3bc719d72558a19cda78fca79477ae6342da459aec809692976ef\"\n          \"227fea180e4df795766883c4260320acfa8e8128c6bd616ded9714e9d5badb3a\"\n          \"22e93ee69fdf5496d9ca6c5c3a93dc524bc519861d80dea323\");\n      v.emplace_back(\n          \"6812a934dde83d1b997082e980f7c4b01f12e354cd064131de1380d10627549f\"\n          \"cbd13db3405bef9fdc9bea482e72a29e727a233b0a5df6bafbf5512e30d58cee\"\n          \"5cb21cf351199251f5dd8d45bce9c868d562f6eb6898952a82082eb5d334c69f\"\n          \"c85543491d04c5bdfbf8b50337bff27a503563d8d7baedde9207\");\n      v.emplace_back(\n          \"f8c6b86a31408a2278b8b6ceb60eab1254987587eadf7655ffe26389e3119319\"\n          \"ead76d4c1086ba5ca8c42aad07e607de1205594483184401ebd3fa5ac8bfdc32\"\n          \"76c84f78b9a2c3d52580c6e7ee439168c30720fde06738753140c64206902bb5\"\n          \"97a794bc3e359053716c7cf1ccfaf3916f79902358501b13f81498\");\n      v.emplace_back(\n          \"81672d4663cb2ffb96f8cf0646a522b58dd0ceb087da631e24b57446345f7a2e\"\n          \"d9b684e1f22bc6f20a51004b58fc4cd3f575af5ac846aa777f9be473362fccdb\"\n          \"8155d24ea889cbfb418f774b96c8ad1c6e5b5da2af8722f74661691b56662ad5\"\n          \"fdba5022385717151d33e2d1f4d373c8260778881fcaf9efdf676a12\");\n      v.emplace_back(\n          \"f7abea1397adedb382ef9efe62949b6b3a4358470937ec54c5e7df6d30ae6db3\"\n          \"8082bb2d56f56eceef44bc13e4372a2d6af84a671fb7fe007513e9d5f1161774\"\n          \"ebda4fd832184118cb7ac265c043be9c65f63c418ccd27a4c9da085b12e6c653\"\n          \"3db311755bd1e678d3934581af794c0587c8203822dbbe865653b2aef8\");\n      v.emplace_back(\n          \"9c9b67c43ccff3b84ee5b83d17c2d8ae44dd079821967b2176336a1667c72490\"\n          \"99ece48abc047351fa6bf730c55c10823442350e164116fa4e0b290ea378bcde\"\n          \"454ac8ec4d6962462d63917321a5d509fd2bcdccd47ddf5302c5696815fb1cea\"\n          \"ca869dfa07285b1b43f19874c53793583f689bc3952f34272bb7da273c24\");\n      v.emplace_back(\n          \"95edd838e7fe5a3916372ba59f6b58222f66552b6321066ac66159efa14cd7e0\"\n          \"6365c3430d325e9a8bc8945e595a0569de98ed571d340fa63f8ee506d9aa8070\"\n          \"f9b70757a8d31fa5d677cedb5909fdaf12cac56b4e138d1e072ffdb126dbc850\"\n          \"159bc581c98f3c26e27b8c79ca50d77dd622eeffe10a95882ab2d93d0c9a19\");\n      v.emplace_back(\n          \"926f571626650610f95622628f738040814e59315fe7af85a8e346d18c28cfc6\"\n          \"f3cab985db9947917d0fc128b138af2ecb02fd840ed91c363f8d52608ea405e3\"\n          \"7e2a522d0f1bf185cf2c3199fd9f1957f7216f6f2e6ea661c6a3196e77608402\"\n          \"373dc9c36e35b2eff1fe17ae8f269e5241956088130f8e7b94cf042391482329\");\n      v.emplace_back(\n          \"afc3dc4a953e845bc367f2930acf37a902e0b2fc61563119f41260c5d50bfed6\"\n          \"4951b127611789bab0e9679325a24c4642e0e80ff392c42c340e2bbb6d208c7e\"\n          \"28e833a0d8adee30f907afca672835acb7b41063d804cef1e8df7e2688d9803d\"\n          \"4d34b31200a4e2ef25280bace4e11266a1250653e89b2e9b350616dcc09bda92\"\n          \"41\");\n      v.emplace_back(\n          \"0323b5622248d8ef0fc718e54c0296c99176043504f4f8739caf6078d17ddfb8\"\n          \"f738e35e8a2469e62c57fde5b3678b66dc3ffa8291251ed099340a6bb07987bb\"\n          \"47bb2bca76f58346d3ac254442ff6ed32712a80ad20b622c1e2a7e010b2a3091\"\n          \"5fcce91ad88c3eb6137c347cb2943970b2eb72b463209703c034c82bd22a302c\"\n          \"5527\");\n      v.emplace_back(\n          \"6db9fb1727b40f3736250d908386e19f2329afd69389826073c2ad5eef09eb57\"\n          \"f2e3b7bc746b4b7d346dbbadfa4c3e368300f6c21535eb3f3b5cf400fdef2084\"\n          \"d38d1a042e3093cac8074a915ab7c8593f171ce6eaab28abb1b83786f0095be1\"\n          \"757c7a71a38fac667d16f9f7c4ed2629f1465fafe635f624ee946f8d08e0587b\"\n          \"62349b\");\n      v.emplace_back(\n          \"0733913340ec863e87e9c0c29882a73aa820fd764130cbbeeca52c70b20b65a4\"\n          \"437af34cfcd220b22ffc1d7f7dd6c143653177035cd29dcf5a68834de1a6d1e5\"\n          \"17b381ad173a9dd31aa93c7bd57ebc58214c8106910df2b3879377686ca7aaa9\"\n          \"e39e8ee7fe65dc1c87749b475a24edb68b423135aa47c7f423034b4be5fa3eb0\"\n          \"6b1f67ec\");\n      v.emplace_back(\n          \"d9e86e137b90bdfe911a9fe8181f733d6a1a1bf1bef0a6e8e21ecbc2b52cbdb3\"\n          \"3b00097d3a2329eea102266fc9a5828f20d8f79b0b38e6e46f832c4dd09f2022\"\n          \"eeb4de8a063cee2777b18f57e9184bcea014511c793f6ec65b2cb5b829cf02e3\"\n          \"2089663a7807f7f5f292fe2bf07a2a2efddcdbf0998e7511e0fb92ca96d2851d\"\n          \"e61ac1d92f\");\n      v.emplace_back(\n          \"a802b116e08094afd366f0884b21917f20cffa2bbbc962f0338b75d0374ab095\"\n          \"7c42c4abef8fa2a0bd9f208b54ccd39b0dfbadd13a4f9a2e6b699ab8938112e3\"\n          \"fdb907de7dd3105388b137f998ceb943132aa97fc5b616d2a2f038e3eb8ca8b8\"\n          \"5abf0d74b70a5c64d8d39c5d01d6f653431f73e5ee74dbd12b770f87ede864d6\"\n          \"7a30942efbfe\");\n      v.emplace_back(\n          \"f69081e0dcace4ce289806fb4fdeb0b48599dffb1ddf7f5f558e101fd1a0528d\"\n          \"534a5286db0f1e18cae824849ddd440a735801a24c84fff16ab92c4a09e091c3\"\n          \"316d72677c3dcec71a9bb412b8763858dd649f28150642b850e642a17923632b\"\n          \"e4bde995d01d43225f72d3ac91d7fb55d8bed4e8deb4a8e88ed71811933e6e4a\"\n          \"126e1a1e275633\");\n      v.emplace_back(\n          \"eefbad10e1a20fb3a4747860dd0a5d1ae60a5dc9da7919f21b3aff8cf40f4652\"\n          \"21f1ded0584e73d02f1f3d598ccc1259b1a39f173ad03c4e3573528bb1e4aa41\"\n          \"0e5ac0702f16c53f71b041e06a631195066ddfb5c97ca6c6955ebfd9aa24f5ba\"\n          \"650f2a9fbb574e30a0b19ae4bb485b422e3a47fde01fd22fd72633c11e397bff\"\n          \"e55af45bef687673\");\n      v.emplace_back(\n          \"97e9262370583e5abe378e45febbfb8738691395eab70550021a94c31a82069c\"\n          \"22beff6edbb9c65341c6eee246cef57f25ff864ad0eff66cc3b9a41fd3d82287\"\n          \"528dfaa12452f9bea39997c00552d45fea39a460ecc2f23d7a58673f93acc4bc\"\n          \"48513c0d01298af2195a2d0b692d5ce0b4ccc85c82b45a9a43f70a6e91800dac\"\n          \"fc022b27d535cbc147\");\n      v.emplace_back(\n          \"ea995f5f197d938e1958a041708710e6632bf48f92a1ef5b1ba1fed9b0566e9c\"\n          \"d6b1fefbb77b2009e98fdf14d0f0e6d14dd33fd7ae1bc4d4de7ab1c614ecdbf5\"\n          \"651707f1386a6120651cfd2a561a31019f80b50b330d0e5d052a434d053b7659\"\n          \"4f93bba3ad7b2e048d2dba4fa7c3498fe8f310c0ceaae5c12e26d74aff0a6717\"\n          \"aa16850aa2b07115fc9f\");\n      v.emplace_back(\n          \"d1cc5d7ec1035fcc4160d5cd7ff1a3c89194697ccb0a00cc3ed4b3d48ee71eb5\"\n          \"fcf228746e20d4b3e93dabc12427c15bbc3147f00b124d812437d19eb6f9ea52\"\n          \"36f87052a5fbb379e27091ad829199365115275061c79f20521053a88fca71cd\"\n          \"7b0afc377fe4fe34d9d56d21816d88a374f7df5de258123f35ee1ebbf9cb20f1\"\n          \"ae94705581f67f24f626f6\");\n      v.emplace_back(\n          \"2fe0f366c5051ece560570f2c2783604c1bac4c84c2156c916fa5ef7839ba296\"\n          \"343eb2e26c9dc446441897c62a9fa56fcce2fb92af4db0ca6d16999514a1b63b\"\n          \"ee0f0b949cba08fa3e5aad137df5cf5656e7fc3b09ed8c698738618719110913\"\n          \"03f855d79e678f674fb74830b263e22be7ac7b89434fed87e0df401ade983a67\"\n          \"2ad919565cef1ed9403a41b7\");\n      v.emplace_back(\n          \"e234ec499d037ff0ad5e3698ecdb7dae1e10dad50e4d5507545395913fb51831\"\n          \"e4f767e578a7e17bbbb77f57d1abf76bc1d419e6f38383b26fb639a5ae6e14a9\"\n          \"10a2b22ed2a41aa18437862ce6c2fe8d1206f21900d50f26b1f24024c8ff36e9\"\n          \"b662b3c4c0687364921d2fa6f6d0cba9e76d4b2b4b2a74f14dd8c2e1a752e99b\"\n          \"f1e5154ef64b095197b0e7da71\");\n      v.emplace_back(\n          \"c5b8214c2baa3953871edac53f0513cfae89f14c99eb9119075430c8882f71f2\"\n          \"efdde2aee59b8395dd84db4dbd0c0dc0d2f248159d9e994799491aa75b02093c\"\n          \"ff37fc9a4e06a09ae5b6d2bb80bb46c21eebcc2a03ad0bfb1cdf86197af8b5cb\"\n          \"a960ba137fb9c3ff8656c4b38dba954944f05a921f98e19a19d89aad62db2ae7\"\n          \"c12804e0947970cdf30fbdc056bc\");\n      v.emplace_back(\n          \"7de7a86206aed65ca62e28e8021707156d74cdf87e0de02acfdfb0fdb46de5a7\"\n          \"0a06b4907e3d90ce9aa016723adcab4186fba4dd054c10f715ddb95991b10a18\"\n          \"afedeb83746d17d3d3287645c00b4b9cdec703fdb4a802bf919514c605957865\"\n          \"b27c1b601d2a1a0010f9e5de3839a325b99e6b8bf6691b4c298221297250488f\"\n          \"c406878fecd9c6cd7319cc1bc8f869\");\n      v.emplace_back(\n          \"6c441cb15bb438db10c972797d08b719aba3987c056800016fb542a3daa944c4\"\n          \"226b9b3c41260c8013721158b36f6aa3f3118524bc91b68b35def994a010d05e\"\n          \"35cb29a3c784968eb8ce322edd3c3d5f1fbe89970a1817d7d5b7359342c964e2\"\n          \"d4adc992cc27ac5322ba43c352ceebd88e08aeadc090a7a62983fdffa66002a8\"\n          \"62d24be79f20a408fdf051d302972e81\");\n      v.emplace_back(\n          \"5e6e5eb22f30350dd71c5f3253d403d85471d2130967e049ed4294f7e137743b\"\n          \"b60c0b11b0f5818c0224bdb4ae1295458a98857b6a32ffbfd1f7d2863acce5c8\"\n          \"44e044bb314e34df2222721614d0d51e5bb2c04548228a1693d90783dd985818\"\n          \"d25bcc6c61ff875dc4b6fc0eaf6af89e58d981904b522a589ddb0178d6b3a1d1\"\n          \"c395922584b62c67e965e840589f658c63\");\n      v.emplace_back(\n          \"e0cc757725180643bf8d08d9256aa7acea53a56dad9b49ff86e73792e721c96a\"\n          \"a5c496d2922665ce3ab27fcaaf596d2aeac7ccba1e5f56c1bb3aae070dd01a70\"\n          \"2dce11ba34fbe71b102c35df3420928e90e84671640279ede57748346a3bb864\"\n          \"3a37cffc092490760406146e7922e45680f6520b694f8e599b857074981be25e\"\n          \"89bbdf82f9b1af169936a2ac1b2eb1fb7513\");\n      v.emplace_back(\n          \"f992da612169ab7b8184e28fc2fbbcb5006b3e92f084052dfbd89a74cc65dd0c\"\n          \"361a0e0c764a315f58ee5123ef8d48cd6c5d8421e8bcecf0fa1bc2933671d856\"\n          \"fe30dbd9e9492c4c3970804297df06f08336b05e5f5227b568b7d99570d9b7ee\"\n          \"54aef3a8bb236a736605403fe0945fd85cccb0ba083f20034d6c625bf5a75e09\"\n          \"0f42af954f444aad730ba13489e972bfcf0a15\");\n      v.emplace_back(\n          \"762a04d1740f3a31150b0763b5b3b91d3e1203b9939e3d45a6bf21e96ba6c822\"\n          \"14f1b7481137084c234445406aebf30d7b2148afedb78c19e308ef49debdef5d\"\n          \"ca50926bf123d9be9f0a39d0f59e2de55f512075c2ff4d5b426168f31284e1aa\"\n          \"5385127dcd054ab144c26c351f5a70d9ffb7735c43b10a83e790df8da1a8311c\"\n          \"7175dc8e2a79f4bc7b47cba13a1d8af0440ef70b\");\n      v.emplace_back(\n          \"f5b7e6dfe2febe4e8280667743680cb85ffa1c520ca8651046dadeb10d38e6a0\"\n          \"cbdd2abc9dbdf4e5c7f0d81497acdf291fa41848c30a6bee17330ec49bc440ba\"\n          \"92b4b5bf3515cb02e5675f7f09856041560fa38e4f26c6309f2c4be814138839\"\n          \"ed8ef64be1cc13d322bc9eac111090a24e0a7ac29fb7c9b9bc8f864f2dc96f86\"\n          \"2598026352530ab7d3120dffdcaca1560b7b52bbe8\");\n      v.emplace_back(\n          \"ac6436d8b5bc121875027945b6ec42ac48bc7d37c81ab624851121e6f8938a67\"\n          \"f49efc5223205478e25ce51c6a802773be807b2e61a448a7656b7c9f22622e8e\"\n          \"9101486c8c6ea443ad17402f2f373123236137925cfbc5d8a154a55b9e7295f0\"\n          \"b0dc3e58c91dfef8eff278e770c9007d5247f481dbce8ec0c129e49a95fe4ae2\"\n          \"ebe9ec6a75dbe7c9c44d29218e1a69389da9783933ae\");\n      v.emplace_back(\n          \"47a8cca3b77e63f270d2448200d9f3606374f7e708d3e60669896f159e2e8019\"\n          \"2c141210cbce44c06369f339d93f97c1107affef1722cd2238546dd69505bf7a\"\n          \"2f894bae87f13209d03fcf372413aedef8fef4583270c6bd787a452647e3534c\"\n          \"e8cfde89d03e3a4bf8100e4b57c04d6844492af0eaac44e1482814e038039d37\"\n          \"d41d7df47d7098254ae1fac3bc3b2af97b46eb2af9b8ca\");\n      v.emplace_back(\n          \"a7f309a4215057b16b9084a95cae92e7b91526786b63acd8f8c5c13d7ed0ed69\"\n          \"6994f07b96d9cd2c416909529ac914a128634ccd9979edfe256205998569b395\"\n          \"a06095de53699bd1e9ffef2638432a4cbd4d02b53b600fd34e04d2032555d7cc\"\n          \"ee0a217e6d96c67c76467b62bd4cf4099210b8155f8ec0ebcf4336047c45d925\"\n          \"622e328be20b4966aa8706bc36fd222def584579decd3f59\");\n      v.emplace_back(\n          \"39e0a9b4109ac94bc86adcc6f3b13e5b6bd12980f6b6203a6de641804791164f\"\n          \"ddcfc888db5cd5d26d9e7bdb8e2d1467f5870031a93b55b4b8e872adb1886c98\"\n          \"e698dbc19d6eac9c767ab2b562d3e4a726f2c8782db54b27b0ace7836dbf86ea\"\n          \"5dddc3ca95447c17b90f97a6d925c913b0df825135b93f32e7c845a0c40ec7ab\"\n          \"b07970c928b6e2153de1f5f927a872624a1a6329e3d675cdbc\");\n      v.emplace_back(\n          \"34c075d9d6d050229c6f9575cd477ef976a83026b7979776c1a255382de75189\"\n          \"4a47e9905c16a596a6fdbb557825cfe194cce09d520009ec70b4d3e591c96130\"\n          \"c882a282334b9def2b0ec09714380a3437e8f0f568a00b91e5ec6617eb64db9a\"\n          \"0e5a631e089ba4cc3030b918def43d5e2d745362ec7caf4302dea3741686f423\"\n          \"df8904a03732968a16528a36b26acd4c6c677a724cc19181f040\");\n      v.emplace_back(\n          \"b43e4a514c52415dfaa0e9d69e7a329520093e5760a1d79116d756c177518245\"\n          \"757f603d3f859a48ad27f7ef25c210eb6660a37fd27f8dd4dc29f16b9717507f\"\n          \"3cef8ee8c49b0cb44ca0cbe2cb2762d91ea3f49db133271212d7dcfdd6afddab\"\n          \"fa34c5bd3f6c5f57e12b6d4d13e1eabd96baa27da286b139e2fad4896ffb7701\"\n          \"d6bf57df16d2779b6b46aebf4d498d991d6387e5ed9cd23fd1c847\");\n      v.emplace_back(\n          \"f132c18f218b14ab6add0c359f2c81638f9df0d11a951236818e81fd7d436b97\"\n          \"e18c45abd3307ccbc3bc93e0b17c1c66bd65d089d16e78236f557cefb1e62195\"\n          \"86d223c284144199e3fbd715c6d5adb5f5dffed926c8cb9fc825602b3f206b91\"\n          \"d4aaab5b868b6610bbabbfcb8b3c96400c4045e47951ccdaacd2d72a3c8f8bc2\"\n          \"65db7553eca4f53a7e816628ca70f1ed5943d33fefc7c4462dbe4c5a\");\n      v.emplace_back(\n          \"5dff03f0b320ab343c4b63733b193bc2ac369c015ed55ed7217207b0cc865827\"\n          \"58cc59620e02abafd241c892f237130178186f97e50a90154a3f020a2bec33e4\"\n          \"9d5d06b17e13bc3ddcbbcfb6503c9eb14e64a10a9b1bde1aca7fa6f1af33c182\"\n          \"795c00c283d033b5f7420265ac8194e79327aa4817ef04d4e9991035e7fb5efb\"\n          \"bfe7426098392c3d5a33908ab6cdf7bca5354880e138d341853e5401ec\");\n      v.emplace_back(\n          \"89e2b44833586822f237d1cf49e47479aea3a909bd703f2623faa889544032e5\"\n          \"2e7084670456375a58297f04e73cb6bb05a2af8e7d6f295972192f143001caee\"\n          \"5dcb15d93bf02133cb5056b94dfe3f64283f2f1c59ef9f8cf7732563d088a674\"\n          \"47fb92d13159b0950de9c4efee5cd4da5847830f62144b553803601e695960ad\"\n          \"04e3d37232056dd1cb8a90ff304b172dfb035226d29cbd0b59e9d5b21c3e\");\n      v.emplace_back(\n          \"7bef5d050056bf05c4c735ca53f59a5db8ba571a09a16012c469552c5a2231a0\"\n          \"d8b52452051ccb79b11153f2afd3c9e5f2a491bc8d49a81f320b1dda084b3010\"\n          \"f86eaa22dc5cab4b179b2189556f09467013258e0f1afba6264af97bbcbc844d\"\n          \"7136103f403f56db79cdb997b7d0a20240852025648f7507101b81a6883ebfa4\"\n          \"9255ed6cc0318082fa48a3927a91ee6d73160bc7665faa67a5716005e4931b\");\n      v.emplace_back(\n          \"750212ce39be39e573bf4a4787e969103a41dd9e2d0e9025026c5ff30c6a66e3\"\n          \"f42378e1ebfbcb193cc8b695ef90d94b1dd6b785fbc3010d95e9f4a91108d3fc\"\n          \"f97ab46ed7059839adec545599369703756a3939c23979e254671a1b3840953f\"\n          \"7a7b089cc33089e3da314a8bb1899d70efa47e9320b81ffaa3364c7e403351e2\"\n          \"6ab49d9a7e6f288cca67ed5c1120fb9c8f1d58557036dbecab75b0f40a9d9647\");\n      v.emplace_back(\n          \"7d927ba14c4d09e95ced48ab6aa295b68262ec0ad054025de80da8cd73e4a38c\"\n          \"ede35ab2abfbc29bda89dc6e5185b313d9de1f21cd8020c1b45bfefca1372596\"\n          \"6603d3b0a19d906e76a1599eb6612edbcd98abec8278d1147f1cff473a626636\"\n          \"f75e0c2f691146ace47b4bea98e78b34c3aa0f2ea3df7f57a10d4cae3aba3f23\"\n          \"23fc44c0eb8db6c1b3fe0562328461eed1c3da8c2543150e0b535faa87273973\"\n          \"95\");\n      v.emplace_back(\n          \"bf24edc1bbb0ba5f27a8bcb2c6c10fe342e7e3f05b47990dc118aa4afb459842\"\n          \"c91faca491e57c32a73b09ef42fbd00e1cab092a616523ce8392a8d65537c4db\"\n          \"ca23928d7c85df694d7cd7353adea0ba1f5b944d5396660003f394f9db0b75e7\"\n          \"f4188dfd1e4ed6bc0d6e651d3e0b51a576913c7bcd6b2e585f80f9b2c23f76d3\"\n          \"a756f2d905bcbc52290e73d29a1453b7555419cff091679d0accb3a0d687ad11\"\n          \"5020\");\n      v.emplace_back(\n          \"f633b297ac617d6e4885ece567e1d25979f305be0a2f8d8f35cd48def39b9684\"\n          \"8d26419832cd6871126d862c7b00870116e23aac91d3ac7d428b61521f7dfd67\"\n          \"6459261e47b47b2e389960cf2925050266bfd09de6df95097c2978334d857790\"\n          \"36b82c4a934e29646bb076a9f9762d56fa18cb59f37c026267461e8ebf18bedb\"\n          \"565520f7b1f2dda53c026539f31b63e5b09166595cddf7f1a0812f23fdffffc6\"\n          \"3c169c\");\n      v.emplace_back(\n          \"1abb663429f560454807260b09a5b7291f483127d168259872e964f0de5f885a\"\n          \"2280cd3f75ecbb7afe1fa4bf5edf058a3f591a37315fa132d3d18ca52c5ded50\"\n          \"48370f9717cd64e42a964a5d708a492f2bf7fed270e570fa493152d3b794ae44\"\n          \"0259fa0dfb56dafe068f40785272854b06d4bc022ef1815846f5389ffc3a48b1\"\n          \"5e40e69875586824f6efbc44669f0457afd3e69ab8437c0e594206430a8ca8f8\"\n          \"1d787ac0\");\n      v.emplace_back(\n          \"5f81f7efaf96c3d6f2586b7ea870c287b8b4d9e3f785867ae56a8a93307c1369\"\n          \"5dd1300b423b5004f0a03b0ff3a84b012e47086da6a7700b1ace111c753de888\"\n          \"44af71217bbe4d0b8d905cca16a163999baa30e514d402e22b265ee33032e6e8\"\n          \"e69b7aa871130f779d40bd8d89f47c72623421f54c0de9138817a436ce2b3d86\"\n          \"45994427524dd26348b6caba28768e924b3faa468c4abf68b8a39da2b39aa843\"\n          \"1af99997d8\");\n      v.emplace_back(\n          \"9f7fe6ca671658925daa4ab04f5cd68b9ab5e41b504f4f85a504affd2e3b8caa\"\n          \"d9d7a735640b348dd657a30fac592708803e31fc675e0dec7e344f4c55ffd707\"\n          \"b67f1c5f80af611ba923b9c2abd71294c2a29f75f3d686948abb2b5aba5c324a\"\n          \"f2ca5711342f7eee49be3e19e97fc59cf4a5edf82f7bc01a49ea90c94f3d549a\"\n          \"45ce01ab785f2174a0ba35e2bbb3738ed4bf4b8b708d94163e74faa108034fb8\"\n          \"defd5c506c62\");\n      v.emplace_back(\n          \"e07157420910a5ffe21df9ee78671ce38984c83a89a3219a6e8873c569f378a2\"\n          \"afe4132e9b768a6a5391a6733897e642aac6f9b7020b2750ee9abe3d13ffd24b\"\n          \"e62b62f943420c503a68ab8cb6830762a59e42039f723b06667b6cc483dda771\"\n          \"05b65ee205de8b9452e8fb7c5009be1107d255b79a5bc5f2ed9bf8e6e92aa0f7\"\n          \"b5e70d676dd66fd445bd2583f225b5cec24e8c8d725b27b1ec218abb485490a6\"\n          \"96318ca6da50f6\");\n      v.emplace_back(\n          \"50fbfaef285279077944e04de5c0ceb42d7fa25b9e40f4efb2730c605e9b868e\"\n          \"5fa3de3e5dc39a838eecc31bdfccc0e67587fbf9b2bfa8bb96f77a9ec3a0baf8\"\n          \"4014dbbedc288c4307c8648a97051b39bf30825766fab4974ebe3396dc4b9209\"\n          \"d6de68640cea6548d2e660d5cf375cdc2519ddc396769ee5aadf4cba872610fd\"\n          \"1f4322b3adf0b02f9437b28ed007beab1212e15fc5a854f9bb7d8b78d7f760f8\"\n          \"9f854675ee0e8b70\");\n      v.emplace_back(\n          \"f650845cc5512c33490a9eddf7d940dabd432789a736a105e44737b1ec445970\"\n          \"6cedfddb4a1e6774a5c113d4195cc5073bf2b9e4e403bbbe349b687c5d9b9385\"\n          \"02568231b294a445c6e0cec07f4010ef5e88d700ad796b5488c9f26735e82fc5\"\n          \"56cf196759a6346130b6a103ecd89134c2b9a8763b5afaeac942c69cbd5e0f0b\"\n          \"05caf8460ac7adbb0af868e943874320888d2687299b0ece196e93fdad44f6b3\"\n          \"55264c6cbe233c4709\");\n      v.emplace_back(\n          \"faeb1f08b867088b601a8d773405fba4fa28ada560c2e9e46a34eccde500b708\"\n          \"0a35bbbe108bfcdb0f28cfb0a6fa0ac50b80fa0917b65868439129707bb26eb1\"\n          \"3290fd2ad8c60061c20b3b75668d0d2ed539f1dd99076e58513b302004977f92\"\n          \"069c077c4e6332ee044c14d0cbb71d480a344080cd12f7f31e17245a55165cbc\"\n          \"6727053443a1264361f41a7784f6043d93cd8bf0fc0f2141ad1cdbf366f612e1\"\n          \"6d07f49ee8398142f1b9\");\n      v.emplace_back(\n          \"0b703c4014d626e29fa067f138861ec42f3b71fce5cd19110f0dfcef1f50b3d0\"\n          \"7880cd07d2db8f6c2f4975bd674dd6b62c0b98bf97ea54eb541ffc66f73d6b2c\"\n          \"16d1fb08e208163289be8a8f05c423e05e68523f75baac9d61fa3b0f6e1bfb4a\"\n          \"7faad3007197c37ec3b0e34ceebcec9592501faabf812fff49ca8c2c5373bc7f\"\n          \"4d75de7b1a2e5e6bd32cb77c6c2d6fd58cd56326f33cd61ba0940b4c1086e606\"\n          \"de79ddb50f7cb182cc5742\");\n      v.emplace_back(\n          \"a992d3932a5d000119aeb6933b815ede519b52c7176c6d62ae98a39b70bd52c6\"\n          \"02075ed884fd82bd0b2380df2f8f244bf759fbfc5cf99954ac5cb9adb70317b4\"\n          \"d52e1f982293e0d5a377753740f8f744ae4aa025fc66ed382002ba7f405a2f0c\"\n          \"bce92ae70b20660da3b3ac10abf4f179e02553c2520b8b7c997ed51233fce909\"\n          \"18547a6004a4f729711df06c8d2b29f65f24024459ea040a6bad1cc27fb1c0d8\"\n          \"ff3d756a6c9bc74dc0a9703f\");\n      v.emplace_back(\n          \"152718090ecce8f70756546834cb6591fe853759c6eff8771c36d81e08c4458b\"\n          \"080041d2f3d3a2f5fdc5efade8144dca0176d68c61909ef985060b522cec9f8e\"\n          \"c6d54ee2453f1d670a75ebe7ab12c7de5a30d65c28fdf561599dc19c72c8f75e\"\n          \"54eaa2de391909a948aa47c9a76358ef46554791bc18c289f8535bb9d30101bb\"\n          \"c6d840347903c2b4f61cad5c2f6f04227df38108236a7a2f2bfef15ddcbc5972\"\n          \"57b48e8a5718c668d61872641f\");\n      v.emplace_back(\n          \"63d02fc7a14e2881eb47db6c79104f866a15d26f9f84d2c55adaf26b3c010a69\"\n          \"ba973d586de5b12ad51e89a899c9b4743a60017dbf356b7c5a485da33047c028\"\n          \"d580bcfe8d1408a1dbce0194af2a84011a6ea16dd5efddd7073e8a0c024c5f5d\"\n          \"c4e71f36dea8229976962de385162896d0eedebb22ba35d7275b9ba8a5aede12\"\n          \"c78843ad540a28838728bc1d4ad24e53c91f9d025371cbc230032a836212ca45\"\n          \"aec4b611ebc14b5d353c54e06e6c\");\n      v.emplace_back(\n          \"cad989474092fba2670873c9cebc67dec86eb823dde7b0b99f1178d298be0582\"\n          \"8e4aa3eb1dc369fe7c6058b8372184156600adb5624da2ad769c689a7cbcb5e5\"\n          \"c38e259a45d4b83ff0e93011e3fff285601fd209db19134883c1fd97e5979f97\"\n          \"f7da4df2f3ac489290494dfc6748008f96b98e92637d4eaca6953c2cae677dd6\"\n          \"395d2884ad59e632592c15df904cd7c9c8e481228e23667860dc3f5d2e6c4ea1\"\n          \"ce0c0a73076e6a747aee3cf3c3647e\");\n      v.emplace_back(\n          \"36b5ba6d0fcb50ee37aaf66544d34b4106ff8f865c24b9c8ea769d6b16894ca0\"\n          \"592dd3d709124f18997a98aa2c88e0a45af0a5fcbc4cbfa7baf15b246c74a26e\"\n          \"c0e672bef688a9b619b081b63e7a30e09c0c8442de9fb071e73909f5d50b6c1d\"\n          \"0692004242d3750d793f8a767d28fbb8b4bd40b6fe7fefdace8ee530aca73f75\"\n          \"f5b0e000e242e1c6d31e3a3adde861668721439bf952edcdaab40560e30795c9\"\n          \"578436f0373f6316a66dee75f2a13fd7\");\n      v.emplace_back(\n          \"b9d5f19e82dcd525aeca9808a2d76174a04574e48265396a5aef082e66c867a0\"\n          \"551bc30f9d1f044009db3c2d0d698678a9734ddeaed08d96df5e6efffb40c758\"\n          \"c81ea7e5924f5530d60efe3a983351f54388683b21fb08cbbcd95aab9306454d\"\n          \"d9104cd7d0b6b1cea85d7630d38b818082badf854af8104fdaa76e4c186b77fe\"\n          \"0047f3e3566cea7db732d893b3453ff52cef9d0e7cbc58a5417c547454a353cd\"\n          \"90dbcef06dda6a2643ee50f00dcdb9019a\");\n      v.emplace_back(\n          \"742d6d0d638dada3fa15074f7e6ca29f861131e7784a46720687b3d4534db709\"\n          \"0d1312d1a215cbc5adad439e741f938e3cc31d2b92561e9302bc54ab4588ba4e\"\n          \"89d0d538437e11960a83a11a1e52a30dce185cf3bc3ea671b0e24d54f6561e50\"\n          \"2f6d987b6de7a49e057b38123acc7125fd68ebf3e8fda86b64baf026fa8ad53c\"\n          \"2ec32f0af41cd37c56d624f83611e0f10861b11f78b9999680f4aab8ec352988\"\n          \"97c206522e554cc032c8a1847d4112b40dee\");\n      v.emplace_back(\n          \"aea024c695d12c1e8e5b8d181ea49771fa6941188fbe128216b65f20849e11d6\"\n          \"1855ed132c2458524ff7f7bf4bfbdb31a09a5e3f1257243553e35c8f78b64803\"\n          \"ac2c10db6dba6662caac0049aafdc627d65d040bdef334fcf5bdcb4e4aa25629\"\n          \"cfe86faec497d1bb7bb9c9c581fb89fb91f7898ff9f2f3ac3db4c8b58fcfc1fe\"\n          \"741a5ac6fd34c49cd058b48f39432345da0699bec367b04f4b5591a30097a451\"\n          \"a593d0df658e9a9e15e1f5481e23d137104f1d\");\n      v.emplace_back(\n          \"d28db81040d09a6b5303b588f280411511fc5f8c32a8fd6739f38f5b633d0175\"\n          \"391c12e7f429cd387eec7b2bda428e56b93877da802354f5622a67ae458c37ac\"\n          \"9676d7ad065e2764adfcdf8082001f9a2b86f0f46162f4a5cb8007122fec5d38\"\n          \"38806a9758a6440433e808c8392f55e27c295f517ede674126739f7d32d923c6\"\n          \"c09003cdb701ddb53e2cb48545cf184a142f6916694c9d823366ea900b49bb20\"\n          \"fde261d55790160a41ff42f2b0a199c6272a6bd9\");\n      v.emplace_back(\n          \"ace60c7ac074b101965aee3c36049cdf4b8a409f81e713771d519294819bcdd1\"\n          \"b36e2bc76c4b7559830ebd7dc838696def0e1aae649e0ceb583eeddd0b94a239\"\n          \"dfbf18b5dba8800898187b1c4c7eac811f43b8d8e6d9d250a35810c7171ecc79\"\n          \"b4967bcf73c016cbfe8aa7cbebbaff236abfde7135bc6e29fa9e2af007eb5e52\"\n          \"4c15a1008d5535309ef3209276f14f27d05955d92e0d7d3eb05e4bbd43016259\"\n          \"64129893abadb60f6f8b7d6c3c015b8570cbb4522c\");\n      v.emplace_back(\n          \"5b5c19a7fb153284634acd3a98da6a66e31f66ffb581d71befe94f958105d814\"\n          \"aa2b370c245134b25f3547abd0101abde238110b7d7f25206cb8aa57a4e1415b\"\n          \"205c6cf3b46af23981d1cf48b6d6159040b279ba60ab78a14d08f6a3377b2889\"\n          \"2b5bb3d0e44f980290cceec226f90d5f4457a5bebe8d1a39e2e98c3b4e2010ef\"\n          \"9eb24438a23ae73d0386bc5c9f56b581ca358b164ac7c051933e2ca54648456a\"\n          \"f3bfde933fd090aa0a3d57c5cbc3b3df57ea4a31b5a8\");\n      v.emplace_back(\n          \"dd7b1c4ad1d97d73a7f5b00c0f45c1cd33be706a31aa44b36ddf6704796da1eb\"\n          \"23d2195dd92740221b97bfc11a11fd0c5a1f8717ffd84bb5401e965a3e987e4a\"\n          \"6c91a5163a0d2860e3c96f0acdc30ae389048f5eead04606f8f2d313b7862396\"\n          \"2d55f5c81aafa9f4e6c754f9525b1fef34403ca08d2c0e20d0cd61f6957b2b09\"\n          \"6471130e4d1d714e4e270e4fc29d45c536c035642afad9bf17e893c4e37c1393\"\n          \"5d9055a926a9ff0d5460eb3f809646e18222fef84d28ae\");\n      v.emplace_back(\n          \"195e390bc6f727cbc247c31f58dba36117921596afae5be4fa0f33d1a8d454ff\"\n          \"417bc95f03fdae775325ff64dc6918354adbb586844d66490814ee513700fe89\"\n          \"3d7640e81e24ab461ee79221308b245d5e54d99d1f7472a4262ee2ba759963a5\"\n          \"970c46153add4bc04328fda5983ebfe903e2b47e076b48d517f7f0a6cff9ada7\"\n          \"d9bb07d787c0acc11a2ebe22fc352f3517640e9dc5395b92ae769d00251dbae8\"\n          \"8a809d0673f08525494ee3ff7fb9956a23a6ab37dfe2b13b\");\n      v.emplace_back(\n          \"2b18ec0134bd03907c3a81f39186adc4b025043d58deb0c327673d73a4d79b17\"\n          \"20d843fb4f7bab22fb4126f4378a801b9fdde70051a48c59a4dbfd094cfc8bd6\"\n          \"58855ce16af0e563750c5f7909c273a78815a55b30e019a5ee26752a0a25db50\"\n          \"32d1735f0df1c03c078a43ad190944cf2b6c89933466b49abc32f2e5242077e1\"\n          \"3b48c92d22e232e53a52c4bacee3b1e03d61c7fb8578cb8c58605fab06f86c01\"\n          \"0f5722f7dec13cba3931143f979269a4d7031068771cd7131a\");\n      v.emplace_back(\n          \"2f9268871cd9a46480466df658d8ab1513de8ae18aea3175b00ebc92af48e363\"\n          \"384b24723c780371e1c6a45444dcf17182c0a66c315c73de24f430a49aa8f2f8\"\n          \"947ebc7bb8cc10fbf85fc8fbe134d2f6e9e11eea9dc79d0de6bc227ed831567d\"\n          \"55a939f388cd4b2ec2c057e5ed8eb583b4addc14f0f2a5842e974556426e6d45\"\n          \"10b56fb2bb0cb8518ce3a4e14dcdd3377329280364b0b1f602e72ba15e27e099\"\n          \"1255801983211917f26c196bef06c3cdd90291def8c677a257e5\");\n      v.emplace_back(\n          \"d567627598873f4dec3f6236240abe5b6943ac8dfbf2774dd7f40efacebc50b0\"\n          \"7fe52e6b89595553ecd93bf9065db058163ae63552d2fe4f3d19a614715981b7\"\n          \"a503c6052c3e9e2747f6018b5275ffe078216c46f3820d964a2d11e85eb031f6\"\n          \"f314602f462dd3c3aecc8a4f77d4e73bc44505201fd3e8e580d2b04c3f4c885f\"\n          \"0d13e52b505067f3f605e9b637b5ad81d3d2cffb07f88f12ace18da209c10d0f\"\n          \"4d9aa38c5a17995c92c8fa28d55fc731ecdcafa65d956dd65ada03\");\n      v.emplace_back(\n          \"e6f01a669f9e61cec57e3256c7a7c23b840b749fcc849b9e46d66f5903f770c7\"\n          \"bddde56e969a46228dd2d69a8e5bceb5bb06a0555375178e15cb9c5957b2f525\"\n          \"68a41778659a0841fa62cce468ba409bbc30e1a70facb45e0c748f08ad36ce11\"\n          \"3612f1217281f822546e29ac37466e32fbbf9fc878a12a75c5849c7efb6ccd2c\"\n          \"3163bd2fd9ca8349dfbbd234c15da524256ce20d150e54086cdb6a83d3ae83a0\"\n          \"b9c4a49cb5cd67ad91719dabc6179df90012b5193c120179c0b69987\");\n      v.emplace_back(\n          \"f3d1c40217c3ed135e5e6afb91770819b1596034a0a183ceb9ba5a1050f4cdac\"\n          \"ea0c8ce35111abecda4a09615fcc0ca476531b24d67e94f11b30b15fdae2c31d\"\n          \"09995a2ee9f8db40667656dc197dc35dff1416d968a572424c7fea2de1f4c23b\"\n          \"f6ead4345c881cbcf22c4a98ba1d3d3c6100e4e4a21e9197d3d54634a5d3c18d\"\n          \"afcb9a8270f4550cfdd17cf77e06e1e72a6181d9342dbdbd1b656eaf735a07af\"\n          \"c9ca4e883ca545e041f6aabadff6b1ece06870a534aebd638db701ceb9\");\n      v.emplace_back(\n          \"4e80407aad5316ba80492fde6cd6caa97b1eb853111cdf4909bb0ef9ca3828cf\"\n          \"94d059349f363e1c5afa16aa1f18c95e9b0b44b2ff348bcca79877e294beb740\"\n          \"5c88b05dec34b775947d0fae8ec1da26c02bd5035788d27305707181fa60327c\"\n          \"5825e2fc50e175e2922753307b994d27f902f0cc72b5f2e3b78ac3ea66973400\"\n          \"b8faff4e346e48405eb2bedf96f70fbbda6ab905dad86e766dc3db774a358f16\"\n          \"a1d416cdc0bc8a0d99a90fe23780c2da3ea7774aa976025cf784e46eda77\");\n      v.emplace_back(\n          \"a01a2a35365e7f0b3349529bdafc41cf031feac97e6254182bbc6f78ccc97b91\"\n          \"8dd51ba1279c24f0ee5a257b8dfb3e838567da4fde3fa4b2b49d108b5e843f8e\"\n          \"a2453e2a5ba4cee6bfcb9e224d172369d7d8fa3e8fdac85aa257498b28b0af88\"\n          \"559213cb147b6116ec0f7fc872dd6a84f246ca1f41b10ca43fc19c8f20ea5d63\"\n          \"c4c39bc2c257ca5aaf7a89f2e50aba5eb6b069c200f733d7f68f2f11f4c430b9\"\n          \"32d40e7e62e84c22b75952cfd941dc505085f12869bc520dc645b00d0cdaa0\");\n      v.emplace_back(\n          \"03c4b34be5d2a1891b10a0a74e4cccd5a0be17ae1f2388a972ad699db8c247c4\"\n          \"ec013ac22fe6c6c1a75751834101a17c930c90dd3805963235aa8909edd60211\"\n          \"cd97f2896332f606164a3ddb1aa9465fa8c994aab818768166828e3d7a81b9ae\"\n          \"b5dedf93555fc351782663167e2e36b618fb16abcb6d64de99971082ca76ed6e\"\n          \"c17d5d0cd8b45e0336ff3061a5e06c54793b8eb10a1b772c8cfe390e5d32ccf6\"\n          \"1c05a618f5130af24b33068ce35dde6e3a9acf7550797078294e69a9b6c10be1\");\n      v.emplace_back(\n          \"52f245b0d61ee4f1b173511bd008d3970a25b5022250ec2b9f9a28b68b3b0c8d\"\n          \"274ead30fb9fc1f9b3b5f2c3e7125c4fad241dd3f5f4d0c186f64ebe09d87992\"\n          \"2a682f638c73c0419e7a729329809a7325a76851b1df2eb4cdb4eca2204779b8\"\n          \"acc052c62551e274b9137b1c50d822cca8d4cd0b8eb7554ba448b7ac6409eaa3\"\n          \"8093281c5017260ce2bba9bce09b3467178cba5bfa899101ea3d073cf778944a\"\n          \"fe12651ab713743218c28092e6d37b41721f191e006f29b5ac33f973d671e943\"\n          \"d9\");\n      v.emplace_back(\n          \"24b7bb806303fb0581f5baaa960cbea9b2eaf6ad927d073237e4d77cc52c306a\"\n          \"407a4b1094c668061ecb445eb3de6f1880bd72db303bc05af8a5b72ab54014a0\"\n          \"32c28af1d71a62fed15f95b468557a28fbf06eb22caad469b20702b3e067e96e\"\n          \"be06ec31a61ffc2cd4edcb19c11abaeb5e303860869ec7ce19061bef3522a6c3\"\n          \"b0c64e11c7226bab5547ccf4042bf59b1bc0c2c41dd1a7db42418e835e7871bf\"\n          \"121bc9b1aa037c3796214e31b682f8393a1531d1734e2bf0237be24002f8c2a8\"\n          \"a7ca\");\n      v.emplace_back(\n          \"21535b47e5d30e131aaa9572e94390d6466ea90f4daaa27b2211a9725ef1715b\"\n          \"e8805ca5dd95e01a649d23984d5e1dbd461ca6c6d9c9c4d62779bcd3c286103e\"\n          \"6d3a86d289a86c58cf84941e74d022cc75942d41af9da94602361e1839a4d823\"\n          \"2c3d0ad09f8db42d13e66f79bc22bf52950abad83a84fe6c071aabd718c243ce\"\n          \"9f11d84a266b172c08f0b17bb07d0032cc27d60fe21f29479474f52563b9eb42\"\n          \"e40a7c2188404019e02ecda1c588a3b9684191b19dd33bbde2fb3e9d5ecd1317\"\n          \"594127\");\n      v.emplace_back(\n          \"f64e8a480d548be1e8dfbfc1a6c494b81e9c630d05c9e1c843d35c62109496e0\"\n          \"3c954da403b57249e6c3863f3f7289c47bd97bbfc927de8edd896c2dc4dd0297\"\n          \"1bec98624cfaa7244543c4bdc02c0ba6edcbe543cfe80a34245d5fc4abbb5a60\"\n          \"588df8a1783d655c65606d4fb3a3568b1b44c1ab7397ad8117c5d6d9033890e2\"\n          \"558ac2e2b9c8e262191cb35b2c7f77d4ab0c459473beea90eb8129a4cb4008fe\"\n          \"bac2bf51997ec1074acdb75b8c446803b8f0d4cdd24d411c7cdd58f21e587a98\"\n          \"a79a8562\");\n      v.emplace_back(\n          \"ea65942ff43fa6092e4056100586228f2d44cd8f7020d7c9a0927af28fc4cfda\"\n          \"7d7f8202b1dec3ac153d186b97729508f8875bc46c5213bb3254717facf81fb1\"\n          \"b750f56b0e25923d428aee8f06ffa9f55bb9d06b7144c98926f9dc82cb7de678\"\n          \"d0d217816d73821b34e60ec41a64e4b9cbabfa8a88ba9559ded2ad1c2e5c3b54\"\n          \"654af840715d7de483c1844ed17e8d515d13016ad5dbb83e09d1eab459b68720\"\n          \"672ffe1d8ac982fb5ffebaf08b7b94fcdd9481ce3bc07df4d4aacdf06b4f1458\"\n          \"71133b8296\");\n      v.emplace_back(\n          \"2cd24ad3e4a9f3b145eb0c899f4e9622724c3ee8afe865f8f1aa10003c584cc6\"\n          \"eaf3639154ba7ae2ceb4c4daad3b2e9712bdd50fcb8bb844a080ae9ae2565a56\"\n          \"2333b098ae9f56fcad5219cf37bd7a093191eee913cd46231ca9290ca858e8c0\"\n          \"57a4862700c701178a908795932a16d95d17e4000d71911ac1048d82cfaf6c80\"\n          \"07f3c50ba8b1eb87d07d66d62a19ed638079d4a5e813de2863362b2237b9c694\"\n          \"0708373ebf162fe5365cae6f43a535a73e6f49d6ca51e8ef3811bd395cb84fcb\"\n          \"7387db81d7fd\");\n      v.emplace_back(\n          \"35d3281fcd49033ff7255c49ee4b084e90a34cabbba2984fb4ce4f66a62b5149\"\n          \"77b328050f0af3b9ec9b2907abca5413de2ca1aa05edeadd440d5a261c861cb3\"\n          \"e726488913917cc07e2c4763024aaad13d37158f1606bcda253d1332811f0fde\"\n          \"69d411bf8296d00b45830d300567dbaefa79ae5f152a7a6212f0c481838a9319\"\n          \"d042404dd3e64892b592fefd3b1127c300cb541388867dae011b749672008958\"\n          \"764dad93c13898a4b612e6a137bdfa4ccf0da58aa0c25c096ba79cfa49ec9af6\"\n          \"89e761855fd712\");\n      v.emplace_back(\n          \"d055196d7bf4fbe53b8fac09d12e55f2401fe2dfdb423fc25c6e787a10ba2c19\"\n          \"2885c2ee5fedaa4d2cd1c880833bc32e2095246311d47f464629ad53c82cd0ec\"\n          \"a24de0801cc5d5f72c5f0d37733ca62b9dd47dfbbfb1f66ecbb1b710e342afbe\"\n          \"e3ba971c1fc735c9441e910ea7fd9669dd78d1fd4053dd06856744a122be93e5\"\n          \"f73ecf04606af47d49403e3e658849c3a76d38833d96271ed76b0ad924b5aea8\"\n          \"ee680b1da889991d52da6a4b7ea12c848e134fdbb1305e27c2fbce7233280c3b\"\n          \"3bea6a1219fcc3bc\");\n      v.emplace_back(\n          \"5d3e88955c388dcf6177185f894fa7901bc5874a9e73d9596da159dd88b77fcc\"\n          \"cb3ad5fed768ee6d69c05d6e38df5a679eb433e0161b3464b4b8157cffec2c45\"\n          \"0a28eab12c11b18ccbb68f3ae14c71a233e114c4868ccbd1e9eca1a2b6ca4a63\"\n          \"779508099080d3de3396649344423a8b445d34e5902725627608e9b5ec920a82\"\n          \"02d82a5eefbb3b3360d5eacbec5d9817a64d111052e5f030622ffca610e1af69\"\n          \"beb2296825f2409a1042e4012daab54d649f5ae284ccfa665e6fe80fd910f39c\"\n          \"fe3860f3adee2912c6\");\n      v.emplace_back(\n          \"392bfcad38f2938b8d7880f70e33c14070fe54843ce8622ebbd9c5fd9d7cca22\"\n          \"156dc73c1dd616449826ce33e4bfeb4d536c8b3a72aa23cdd512bd16a7c7ed5f\"\n          \"ebe356c8869c5db31d67b4fa34ceec7015a19391c4b5d8ff95dcf414aeac3e80\"\n          \"d261689275be9b70e336cb13d9255d05084c367f42d92c18939e89018e0b5e3a\"\n          \"b9a51bd9eaef72829e964e65c8d70e47ee0668af16d27a0307da66a9c4297da6\"\n          \"7963ac1bff76083e3a87ff12aa25aa5d042a744bc01157102cebe6521d7b2e59\"\n          \"32e81fe2a23341534823\");\n      v.emplace_back(\n          \"97d63a07164ce86d6b413acfe23156e919e1c40e320ee6419b9aea50271506d7\"\n          \"39aafaa4829862b611786a772e7aeced9007e09bd7524309095f1643ac8d18af\"\n          \"3d3a95f9864b18d2e89df43a3a4597b0801f2ce05811ccfaa88c8e94373378bf\"\n          \"325fa7fb6f05cdd0c8ec6cbe8db438ae131f5097353eba012e18f5d1499e735f\"\n          \"f4bc951986390530998726e7a90b0ed71d16e8986074dde9d3770005a748fdcf\"\n          \"411ddf0b03615896d2e0cabeddb07c57d74ef262e1778016c8246625c237be90\"\n          \"1bb8a6c05cdb1ec2f3f4b7\");\n      v.emplace_back(\n          \"5d14d28542ed0c9c21aa82de98c45157b83675341370700d01a9cdf62c3254ec\"\n          \"8e44bb1346f503b561ddcda6f1176816449993f99f870d774bf6610af93cf00c\"\n          \"5d36e08a6e006c4dc78c6605345c8abad4a8405f575cf40744b1c789f987cba4\"\n          \"4c31a022d98d20e79d214659653dc1d9812c7b7f82ed38b469e8c718a8f4a281\"\n          \"f71911929ed1b5d4e618c4250dcd6980bdc64cb34f57d0d4778511c38456c403\"\n          \"00ee6b0b2f50f64542a44a8c9b3b41d4c14bc06b4e166200c1a22bf0f11d51f0\"\n          \"7dd130ed482f6a5804c6ea11\");\n      v.emplace_back(\n          \"b606c4c803672e40423f7b2017825cc6d87f7db31cb155458427d40824f4d8ef\"\n          \"0e77b8f2aa152a3938e1acdc8db298728ded23dd2eab091f91273c284b8f6443\"\n          \"28d16d7568c112f4f0d1209a857a6fcd9ed00fda2d8bf2409a01fe2cb771006f\"\n          \"ae826ae58d7f5d4af94415569395bddf575a116d6daebbca841469f06ca234ed\"\n          \"d6348e078506d5f3699e8fa74fbeb65e6e182e40af3b129bbfab140a287d95bc\"\n          \"ed6a4ddf4bc942eeccbb875c60aff88987642b499d6d50f2d37beb1b54d9a27d\"\n          \"c25350b324e13b4dbad157d18d\");\n      v.emplace_back(\n          \"29606ee5ab59bb463bdb766a319af2085a36d5d5d92b83e60092c0f568ebc8bd\"\n          \"2c7139cc0042f7e248c0c8a89936a39f4655a78b66e668451562fc7c7f9127a7\"\n          \"254f4fa39fdb21528f21aacc04d86ca7d985056db91d70cf46ddd89a54a78cb2\"\n          \"f133ae1310ab830813637fddaad4d70118b68f50919476e81bae14010d8b5dbf\"\n          \"c88b2f92b048476139e7d47f6501ef8b0e8b52e1626924d6f90fa32a7ca62e1f\"\n          \"ceebd50dab14baae9e21a68a64af88962b6d8c55e0e7e6cc22233405e7a1d293\"\n          \"60058bfff24051db40ebed223271\");\n      v.emplace_back(\n          \"63b44af02cda636a386591f0f2698336e2110a2552e43783ad6da06ded94e314\"\n          \"fd50adf6661f1caef42c42f60d9c4e50261a5eb45267fbb457deb03ad0317c4c\"\n          \"9ece21c6595d17c7c854a3589aa6e75e04a9865f821d3b8552acba9bac49c959\"\n          \"188de5fdf7f81a26e4f634ecfcf46ab5acac7233b697ef91b79a04dca30fc295\"\n          \"9bae72c0a9806c74a59c53f6eb322e00301b8c4858f6d554a43a4e2f24863067\"\n          \"04ae96b0b815802caaa96f4078b27e5bb7968da16b5a6c5b0168be405c95647b\"\n          \"d21b3055e6c849d65f0510d458ee25\");\n      v.emplace_back(\n          \"d33b0bb08e56a3ba99a7bc02d236c884110bd637c293804ba2dc254473461ebc\"\n          \"307a311658232ebdd608177e1be7f9fb66d912a433ae6bd1500822d7becbe453\"\n          \"f01e1df8b7b3903d2b8dcffe1ac23e42b33d8d5b446e22f5dd220ab3f22217d1\"\n          \"c992b30d656cb56bd3c1199e8672328a8598599c6099bfe3d452c942a285f35f\"\n          \"96a85584e11e9e4586f726b721098294fd27e3b4ca3ecd127989e1202eeb218f\"\n          \"a5d74aa66fd5533a22b25b213eafc8dffbabef6e17362b9c1888e82b00108cbf\"\n          \"8ce096348bab79d7d53ce997a1b182e1\");\n      v.emplace_back(\n          \"623b1417140ab573ca90ded7bd86f7fe01292df33d285d2a2ded9fc6ad130607\"\n          \"69d18cf5aff2e276231a172a9ff46800434ef60f8feed67e10058a6d32dbb111\"\n          \"aa286db0a8f0980a5e55c6498f4e380bf31b1a4af1332dbea6cc0add86f563f1\"\n          \"ba70df596b29eb9fc694201590a63e817cf455bdaf49ca1e5a4ee4250643e8f3\"\n          \"0389eca76e03251b41ef211ff1d17250ff7bf7a72993687f6cbd1e73015d4248\"\n          \"5ca36c995352e77b966c2f77a201ef57d5d3d8272bb87931077df73ea3937195\"\n          \"b4bc6c95cc7d975053c150c6f354a5cb6c\");\n      v.emplace_back(\n          \"debdf7e34d1927d34002aeda057f7c56a5d2fc56ee130c91007432860e1da1a9\"\n          \"40a71293f371b2da670ecc5a7e3fbfe8779c1546cf4939a6f36dca6aec540187\"\n          \"70ec3c9945cba91a83edb3fd32ca6182c01d0e1b74c1d80a4e5f5537a17c2200\"\n          \"fbe0659dedbd4b3200ead90ed34a8549759eb3a21eaf6f8f9bb1b9525f11bb4e\"\n          \"10ea55b04174dec2a7fb6b5ba2dc212d4f4e45e6b948ab3d6600f51767ade133\"\n          \"9c26277cdf0b3627df43e227aff9a38800fc496f6c4b3cda3dcb5bb1c3dd03ff\"\n          \"916266d5f6f4bf1df0ed4024afe84ad1edc5\");\n      v.emplace_back(\n          \"dcdc862adacdbdbb9b1d43ba399136029cd9901fd16f443311ce1009a17b2bbd\"\n          \"118a92db41f60bd9640be21488c671c8267b7ef10d94f001d94bc43cc783351e\"\n          \"b05a419c183a6abec9af39d91edfca281f0c53db8bba509140924327739f394a\"\n          \"f61b77352543530b1364fee4dec9a04bfcc3aa51373692087b4d3115a7295e54\"\n          \"9736abebaeb87c64066d3e1d5752988395bfe67c9b5fe9598e313a39766486fc\"\n          \"a2bc053c4ed09b5dee30b182cabda9395ab140809fae76ccd5851ca625c8ef0d\"\n          \"c8eed9308248aba77a06fe6d581aa103b43e00\");\n      v.emplace_back(\n          \"2a94dc0ec9592004cb301aa3adc90da3d326794934c8c05e17915d31d3912b13\"\n          \"3b0508d16d47c77c03cc7097d68f1879a39139260d39a10ec407db9680048e8e\"\n          \"d06f3cb4ee9e53afd01ae78da4f18d0e7e298bdc882239b22c4e0790863cd88f\"\n          \"d3485b633adf21d279811c4eaee6f61a3b0b6146be2075c08a9c97883062b4ca\"\n          \"2a16c4f309406a3782fdb3646e51b8d52d25a15c7af7edbba28693906dc8497b\"\n          \"9240518b2457003a9c55c7a41601329ba7eb46e82af1db23d1ddbe1a67dd358a\"\n          \"9cfddd3497bd91cf0d4e41edaae8d2232e3acbf5\");\n      v.emplace_back(\n          \"b390805562b9503677a1a5adefa7eb55ebb22acc2c7fd7032326c3f87900a6d4\"\n          \"29eda3663b77eed2a5df929e1b43763325bde8ed901092e4099aa96f89b42c26\"\n          \"20a1d8a2f20f772187c4b30d62dc49f12fa929396249c41936e2bc379474c8d8\"\n          \"ae0d71fef5307644893eaa14b46ebeb581bb139956e1ff4064301d03862cd358\"\n          \"eb156c9dfce5712b35b149e42b53be3097e813305b8a689d4a211259d73ed116\"\n          \"fed8fd6ed457f58665289c73799137aa57925037d2a4898e19609a576609a539\"\n          \"d640a6a0898d76e7d1170de06e98a87c0aecce67e7\");\n      v.emplace_back(\n          \"d527df309ff5112d906def8c461e300942e10324f9ea54fc47ac4e81bc7f0e83\"\n          \"f9eb3c7f347ec1877340c78346f4618f52681eec7828b38583719def723ef4b4\"\n          \"05553373e280668c33d846ad90ce5e074f31cf6ea49b08e86cfe2ba6039a7259\"\n          \"ef9d73297d310c6db2e17491f524811edeff14383280dcd3a6ac23cf170bcae8\"\n          \"54b7bfd476195b3ff0762f1ef4bd0d5c1727968fb79c3dd15b256d6cd53ddd0d\"\n          \"df4e29eadf3f75013d9099a351c53e9c4e604516f050dc6b2883d07a28e69179\"\n          \"8aab696cabf607bdcb6f59fc32e1079d20424995d13c\");\n      v.emplace_back(\n          \"58ced7f7d6ecfaeddf35b67823815da814b008028d25af3b79364d93ac4aa8c1\"\n          \"20a27745598f742a52a4dadc2298df0d7d0fbc23c250cd097a0076b89017c870\"\n          \"7963e0b90f06161dbb4df4822bfcd2656870aceb9a5adae5cae7de37c3df6aba\"\n          \"f2ec751cd163f03613e60409ddf579dd9b732ba3c429271f3200251c560b4010\"\n          \"e9310233426904f8e2418798373ece661646e8e511a75b0df17eadaedcc64259\"\n          \"cf8c4fea77d754eb09f378edc79259325ba9414865385e6347efd0f41de3c52c\"\n          \"6f27d6c8b92d97a29c1e06d37874e0c58c3d940f0f996c\");\n      v.emplace_back(\n          \"6a1d428d191bb36d060f1263573118da568af27ed52b96c71dcfd8e4a61274c6\"\n          \"4bd3627ccc59825ac8f2325b2a7cd46be2fcd5c22f3ea1b7a8920ee8d150542f\"\n          \"08e3595b225404a125a96ba66f9ce1fd36d57f12bef1c66fbea22144d1353d65\"\n          \"a072d506d0187e2e8aaaa25d1c7c8695e3293f01fbddfd44307f687f6389c34a\"\n          \"2969ccdbdfc6237b382063f6f6a9aaca24e370e88ccec8e74972fcb6934c08dc\"\n          \"ded213830f6430b37a82b05f408c8209f95ea2bce17b712e73ec83acbf3bc51a\"\n          \"2b6881e3f3bdf02684b6b752e7abe723679191e26abe2cc7\");\n      v.emplace_back(\n          \"acd7222469ae8767f7c949610852bb7f120a51bc6561fbf66cc7396b38dfbdf3\"\n          \"3049302b4f26caa93b2844c6c4d46b6ec0f5384c9767358751b7c148830d957e\"\n          \"68c08e11ef9a0fd7f381aaca2238c773f4d2f885fafa151d17a12746c7c28a57\"\n          \"b2ec7c575d88b9d98652ff9140c1a4c50f31ee4491e53572bf16a10b29efa94a\"\n          \"2c079046604c0715ff4fa1c4ea8fda3cf30fa8ce37e53740274e83f6dcc4a63d\"\n          \"24d34b3ed9393b671d3b9915dde6fdeda18ca5d670277c434d793090bed30966\"\n          \"dbaab252966afba1d426ae2d19b5c74b16d3bd36528cb42b4d\");\n      v.emplace_back(\n          \"97ef05ca9a81c3ccb8e993d10943be011b8ca3e8307ff65b1ca259d70718f22b\"\n          \"ed4fe50de5e46d6abdfb3da2bf9669c6ade7746d44a40ae0655e9e8b4dec1f21\"\n          \"c41a9e907fb0b4beafe49ede427c7da456d9c9139530875ddcd9e6e1602480e6\"\n          \"3ab8426fcafa6eaa3f4a68e3e04d53b64312e25e3339d0084a987b53c11dae4c\"\n          \"ab7091141018f9f1780753e87aee6317b9e249135ca32d26289783ca2af99a2d\"\n          \"29ef35b92d4f6541e5e337b85716266441867d89f0af4b855ce0db3fcd0b7b71\"\n          \"d8491d43023ef06586070e167d2dcd90ce9aee71f9043913f459\");\n      v.emplace_back(\n          \"faab0206e2bd10ec36f111531e9114f4ee7fa43deb60b268254c0e3d29a4cdf9\"\n          \"28497a097791816a8ee8220d8bcd6e5ae6d403ce64c7bac17104dfed8f56870f\"\n          \"067bbb210aad4b042654fdc7d5e7c1598eef1f307fe871d00e6d69d68067dd98\"\n          \"a7d91abb8040393455f606da8349beb2faa52bccec14c4f1f4d9609b3b23dc24\"\n          \"b031c65e7eb67ed4faf8e096511403c871a9f64e4b8dc3e557e9bb5d6716d158\"\n          \"924bc4e5b53d81138b2643c253fe9276110956e553790e0ea89a79366934198c\"\n          \"21f9532b43e3675552dad56b447f4bab67ce04d53101b7734a50b7\");\n      v.emplace_back(\n          \"48d6d638ea797322909ec196c41ecd64717c3ef45a9c7cc24820524b1e20e430\"\n          \"c59a9fe3efd044c692a2b7fa0d476d546b35cb09e8144877c62ade19bfeaf598\"\n          \"d8087a97acb68f8add97862c1db10f0fc032a74ba3c8fe4fbd07a28bb9a3c107\"\n          \"ad0a2a0e0da23eb74ab55f8a2b7b511e1bdae340b1d8803b46edbcef3f537c8a\"\n          \"6ec2806b89dac13989b89186587792f42e5cc2f8d08f9bb989f00b770e4c4a29\"\n          \"e1c0689809b950c04dd34e7e7f74823b1bfcc4f855bc955ec7fa53d9a6d582a5\"\n          \"186ca1c282f030869fe5d7caee534b98ca7748c37476c6c69a277051\");\n      v.emplace_back(\n          \"2894313e0e99da4a8d543ab6dd9803eeb369cd4b3a7c590e2e56b9f99487c16b\"\n          \"ef7eb190ff51fd2aa6b93723e712717cf721106115f10b2951141deb33b18ef7\"\n          \"ef1e7145ed9b5eff22fa30780f05948adc7195118e8411f853b3a26caf220e81\"\n          \"d241121dd431716a58994b7d97bf76b4acec5424818e545c4c334586efb63907\"\n          \"dd436e92bd04aee200bd7dcb7cc1ca5f39e67e355b9e1fce7ddf882e324bcf95\"\n          \"7500212461df00303eba46f538c6de2a1681d08432e3e28ed69767b1538b09ee\"\n          \"f75637da24d100ca8acbe418760edfa21a1125a8dcdb30762544405405\");\n      v.emplace_back(\n          \"0a760b5a7b059f5f97b19552306d18463598a21ce5d01c9d228bdf61bc5eb4a6\"\n          \"820cefc9e3d59018f775e945e20518f9520ac91a7469b612d738a900c94e0ac4\"\n          \"2431aeae194040c02b6d628f1815e5270edd3bf616221b0238e779cfca37c303\"\n          \"4a0a747a0c0b25a60d9fc67abd1fbee5498355cde9821814edc8785b2f965d29\"\n          \"eccb4aa1b6c5c417150afe9e2537bad0b696228e073d73b0e6753fd165831b47\"\n          \"9c95adeeb2dea1466ab405ec85bf72a436a0764bda5581369fab7dc094cb0e85\"\n          \"56e3336bf1c6380c1f35cec4f38cb2e2ab03969ae62c7fa81b3a43869cdd\");\n      v.emplace_back(\n          \"6f268254d6fcea73605bd2ce4d85df1c319e2ec84dcb204d46037e25d3acc810\"\n          \"51f9a32be04f687b642a6a18d506b26b0c6c8f2c00a6bf1726c628113069beed\"\n          \"e1020cfc255391be45cdf3ebda30042bb300c3053608716ecf5f8c435bb10d4f\"\n          \"5da66a8695788b034c28956d2fc6fe5dcf4b3285fab8fb650d3c4c6ee0ecaffa\"\n          \"47f8177eab9ebec5f8adc5a8cfaa9c3adbc94b91629413e6da4781a86525a3b2\"\n          \"7597c78b0642cce5f12e5bcb844d2439bf901c3934d66e17f2414b1b8a62b534\"\n          \"47203cdbb9299f928799a0701c58cd816afc73f0001f58b4097cad8e1412e5\");\n      v.emplace_back(\n          \"bbdf17fb1bb249561642899623e7c7f5cd41a403171b675bbe59e5ede54a76eb\"\n          \"e2cddfe9eb77a4a66494a09748f25e1fc3f0bd744bc685ea2199196e0859d6a4\"\n          \"b6733f866b7b2df0ed69eb5c5ff6223a520c9ea99840c9c5ff0795d9ba45118d\"\n          \"491d4fd6ed8413dc22e0f1ecd64e64a01c7b93ef9a9ee7dba83bae239d116637\"\n          \"ccef80f25cca04acfa82eed665c46c98a9bc04121f70d781c73ab892f7982d0e\"\n          \"772ab37dfdc3b84d2f357efbd60542ade377ba416d9d5a595c96d17ed8dd5c8a\"\n          \"32f114ec99512dc2001227013eba20356120f0f712291c8da6df5681e2197ef4\");\n      v.emplace_back(\n          \"439943e702aed09907d07e36c81f1fba89772351f4b60fd69e3058e88330e242\"\n          \"470c0bba6e42a3c16e1b89115eeb4226c2d9d2e49ffba7038b3bca20e0802894\"\n          \"7b166957ff2bd91d21bcc6377f105b3d49a91cae8eb6b9b701de96a423445dde\"\n          \"2472ba3eb001261c17ca50a955c0daf9832c7fe86f9434f88d2411d7a030389e\"\n          \"7d93f14b6568b300aab8f54865343ae1863852827c9f72e7102e92a1f6d67c55\"\n          \"ddc6a2b216241893d010bbe104d2229acb0282263979d5b0b86e2768ad7a59ed\"\n          \"51935d29bdb7989bc3b9900c6e7e2ca65d27b9673d2c8def797c3fa554a032b8\"\n          \"c9\");\n      v.emplace_back(\n          \"4f66b96ecfb7dd7f1fe069e77f9a40ea372bac1f13c0c8b29e03a4384a928ddc\"\n          \"f6d0c7b29e429991d43a1d835878f4d597b59da447b448209788dc3cae8f7b3f\"\n          \"110490e1bd0e7d096d1d4b433b2acc70031b74daafee42f3ea8cfb12aa2a72bf\"\n          \"12217457e3ccd4660a9ce8c6b1adc002dd5e50faa748546920b61e27f1e6ae0f\"\n          \"cb4eda0336381d81833321eb8edef96ed046bb88416c95cfed95d30321ba5395\"\n          \"2c9b738ea3a6c8650ae31bcd1342016ec070e4527ac9509b4542d9983ad63ca2\"\n          \"26528448d46ffd6417f70c78dbc5160f546d92a4ab0854aa6abe37481824ea95\"\n          \"6792\");\n      v.emplace_back(\n          \"e71efa0eae7d17b57212f0a1b9e9ce30e9442bcbe26312fe8dc1dc0e2b0b1ef0\"\n          \"28e0e98ac816aca2af4a725a0abce96c0907cca5c07c612707dc785eff79e759\"\n          \"393258f90b981d7f4d89833629d32507aeab8348d628484e67b4783c0bce6d81\"\n          \"0ccbffdc77ee2796553c9182f5ef9ef6d84774518c05374ea6cac33f720767d7\"\n          \"a8ed29c3c422a3667a692e0bb8cf9439d879ef90659636442bbe07438dcc1bba\"\n          \"764c6497433fc000a09b7eb5518b2c179364e829f7a1128c7504935503ebc7d1\"\n          \"d59166a843ce018f721e4d554fd27b731570ddda8482e67f03e6669ed4ef2511\"\n          \"aa7bd9\");\n      v.emplace_back(\n          \"fc646e856c320f2b9caa33bd90bf08231db8740d7fd3ced036411aa80b7650b5\"\n          \"8ae100bc07195e88d8ccc460aa58557482a794f15204a51ee45adb7986bff620\"\n          \"03a32083e5bab62d66ac406dd74bfaa09cbfd21f2467457a51c3cd4988d40628\"\n          \"d65b6363e186f7be7195d110d772f3ae0a8c24be2b0d28ffbbe00b133cce4ecb\"\n          \"51651f0d8f6ed63ef5ed012c93bf58c221ee7837c6c7ea0c09302570cbf2316e\"\n          \"76474cf264633c5b28e71988ebf9bdc055f127e19b49a46d892291b76f70ac29\"\n          \"0f87c8534292d76c4c7bac67a2dc498a81c108e52b8c0db290628121882a067c\"\n          \"ffe235a2\");\n      v.emplace_back(\n          \"c6f6b3eca36be502cfe65b1d4803854336969b65febede26d9513e83c6d55a38\"\n          \"948a85c54997c99f206fbef972f473a8aee5ab44d32eb75f38f03ecaa31223cb\"\n          \"c4bff215772061afb48a80705e1511d0cdd4ddf00a365a09d7e1e8daf0f32629\"\n          \"bde8576e2055e5fee04053f661224f96e28c3c3b56c8bcc6bfe14c7a224242dc\"\n          \"f0e3e7f002192655846037017acaf069c63a44b72a343a14cfed90ced833822d\"\n          \"e6118a5b5b257bbce56d24ae81bc731e0b4a318e45a84310bbcb569833dde17b\"\n          \"396f76b4b0f72f4e59239ab3738d028319765e3e79dc752f2aecf2a3ab5c5192\"\n          \"3d8d6bc58d\");\n      v.emplace_back(\n          \"d11ae03c75a7b0bc1723d301b4bd2775085801d01ae5cccb9dec444e46e44f0f\"\n          \"413ab0ac34a005a4b7877cfcbc6d7db3b46071c0e73b90a430f4cd3a2a457676\"\n          \"3926df0894dfdaf47ecf18d2d4a9844e818ade7c11a993d11349e04a6b3da209\"\n          \"0889e0ac67fbb0b86817215505a728bacd2e3dd7be9f80ec92c591037d16fd1b\"\n          \"8f706c95c097b18f01aa4577437bb2a38c569a64fe262192fe00921df4a9d95f\"\n          \"3e481fcf422d7d35fccdfab474f633e17dc041285d6fd59831056846166cf8f9\"\n          \"5e56a6204239794125b1502376f1934ff62b35a2dcf1f51b53720a96f191d720\"\n          \"32138035cff2\");\n      v.emplace_back(\n          \"6d243cfc8bc00b2def28def7543a0ca2b0d531c4be9cd1cef41d53bb2b84da4f\"\n          \"3e1f58c2fe89a49658dc0ff614beaec3949dbc673a45fce18e7bfaf7953e16b8\"\n          \"298c406e5013949e268aeed343a2abb4ffc1e740937f40fc5c99313209688929\"\n          \"a6fab1223ce62e924ec290c21702acc2627a1862098cf3eaed6ab08004eca710\"\n          \"8b1b02fd6188e04353012a5eac7bf17547ffa761cb7430fec5d21d576bafa3ae\"\n          \"e71be6787d6d210a72cda07bf8fefbbd49c3326826836698ba003f3482005907\"\n          \"d5fd7f4fc8d31ed92802b6ad28df0c174cdb525238dfe82cc324b628f3359ccb\"\n          \"57f4024c06c17e\");\n      v.emplace_back(\n          \"579efb8aa51c50b13766d79a95712358ad522c2a1baa33b10df4b6817f8909e3\"\n          \"d855b037f9f382a18aed61fa776ceb53dcb9bd2adfddb7b69e417e3ec6740b36\"\n          \"3852625ad0182e686274b3556c1fd71b3cb5df25c64ed23ce194f247022ac398\"\n          \"408e804de1fe525046f6455c41122a3818f24b312c5db11714537f75d0f96d3c\"\n          \"6ce02e379046a7878514157398153f9187dc5ef160e9f3572dd7abe016fc710e\"\n          \"f0ab7670610305ec612f084026771e93274bed74cadeb5a6522076af6db38fd1\"\n          \"84c07c3721f281754119221cb49e1c35ad07838565f10f234e05bae1d88f66d8\"\n          \"e9ab5e51d838a151\");\n      v.emplace_back(\n          \"a857f1dae5f4e7fc5b8035e82a3df1735dc0eb001c70da569d93e85efcb3ee64\"\n          \"bb58c553770ae07c9fd6bbc5cbc8b6743b7587f1a75d277eed7599f946d94944\"\n          \"aa668568dcb42fec6a3a7144f52c89731996207664ec0bd7aa0aae2dec262bbb\"\n          \"3a3f4edc902619e5e5e24656f98d5dec3b9ac6937b3a27a913e43782dddfa351\"\n          \"dc863b9b72465f653f59e1cc2cf32e04ead53cd231ec6f00603517b191bdc343\"\n          \"4b989ff9d8e83f4ecd0bd1a145593e245b8fff15bdbfdcbbd7e1696d28df5ac6\"\n          \"d285bff0eac38bb5342dd7ceb630e4f238019ca1235e13b8cef8f03b0945a3b1\"\n          \"f777cef905b15a1087\");\n      v.emplace_back(\n          \"e0bdc65893480aab82ac4665e5e732a634619d7cb68fe5cfc25a4726c15ca1fd\"\n          \"604d45aff79387153e8466f724c902c2a1ada5c53d61daca9320722c47342fef\"\n          \"394645b5b8631dbf45046afd67b682ffca139ccf97f1f94dc0ee90155c4eed46\"\n          \"dc42e658b105d592d0a70eb43a68a0dd9f3b8eb6609355c8169cfa483956afa4\"\n          \"6ff9ea55eaf0e66a7c36ca0d19d6986175c034d4105976580ff9d9d4959d0002\"\n          \"5b5978ae7c76fde710f7d8c9161befb62f40179be1d072f43610709af18f4727\"\n          \"98e96586a11dea0b1e37ecb4254d9b0b376916ec412f5668e93f332f8a1ef883\"\n          \"f57f2fec44ada795286a\");\n      v.emplace_back(\n          \"bc9fb80a5685dde8244f6f552a32794a8fe86ac9a3822fcc753484726513c7e1\"\n          \"29c5794b1055e1202f1cd91ebc5ee789d131c532c9efd2248beeea52cbe0eb96\"\n          \"287a6e3a4a8b763afb37f3176e11e2c4fd9c593c3246f51bb5092f94e7d6d63b\"\n          \"5ba5942dda975c01c3b4990a11a8571ce3494809584605d4b9d06b45d1a96046\"\n          \"16b10477caa617542c6a89f1e8a155a1ba4b0e31c63497a8fd48ed62b47ea098\"\n          \"f4850b9d386a2a0de0a1d793d20e720c4e1d63ab2e19133bcb2a379ca830bea3\"\n          \"2ac8103eb9105207eb10c812c0fe3dee657a357ecb13e405cb23bfbad572bee5\"\n          \"ca80fb5bc4b315c3821b28\");\n      v.emplace_back(\n          \"f9ae35ffbbb49c533eb324cd02252de0aedaa3748c4c8884c389ca6abae2e953\"\n          \"e405212dd687237efc676f7a000235fb13d604e0481617839493bd10a2ccac9c\"\n          \"7d8d11186dd33134a41da716ee7a4a7e5085e48fea22b9b753709b9d86d264a5\"\n          \"21978955b2e4836573859f7124d6c9d89107f55914f33cd009fef23fd8f28c85\"\n          \"fc53d6a7ff331ab2df6899ea0565ae4fe2f0168830ff1c20f39f994f37a857d5\"\n          \"02002b1239f7809b117856bfb92eaff2e4d8c05c718fde83825431003c5c11e6\"\n          \"61ae40b516289e3e347957669a7f20ddc665dc3bcab5bd42f2e03bca3511d835\"\n          \"19f4a6cdb8c67e0f33b12dfd\");\n      v.emplace_back(\n          \"664950c1caf4e5737671bb02158b45a938ba5aca3f7c153b64ef531c1d7e9e79\"\n          \"bf78678abc9480046286cbf03bcea3db6de2cc5663193198e8dfa9907f771289\"\n          \"2fc522ba644d46bd956bd8a8ce8a2d35266ef237e6c1a9fd0ec6e5c5ceccd7f7\"\n          \"26e4dad639eaa21cb475e9765671cf041f45b88840d60b22c1537112c72471f4\"\n          \"d2430b6ace85ae80eaf4da52fb2ae1ad15ba2c5e7754da94de5b00f6aab061c1\"\n          \"d96a7a524ffbc1ea526d3d1744d4682985e8a1427c72f90aee27505c73ae7e37\"\n          \"8e371c6000c4602007c2fc3be33936b15483a60e0838aea4834cf8d38325ad34\"\n          \"30a1614d56ddb0370e8c8ef748\");\n      v.emplace_back(\n          \"3deef64b2abd079784b7dbaabe360aa3f6cb20763520eff7069ec1704c9789be\"\n          \"ea0fe3390ba5af842abde876b371d43a94771b513a081099e136d32f4f8a88f4\"\n          \"c9630db04f05ae6019b814489a5ecb7ace0c25476ae1decd59c6dda06de38e3e\"\n          \"06347cd2294aeaaf941f0e030a895c2f2b2bc88e2ca698dcf6b6f18f24479e38\"\n          \"3a36caa47224719e581a20002bf2a21d8650f031f7dd1870c3153693b6246080\"\n          \"69f30a0ba6cf5a9a1eb712d92bb97ad3a3327a41069e23a7445c02d6de1e46b3\"\n          \"5b4a8a44134ee19886afbef0a4834f7a7fda53c1f784aee2ffaeecd86e7df02b\"\n          \"e15b62ea204aa3a082637c4ea34a\");\n      v.emplace_back(\n          \"fa80ca58dee32b10b4282f576ac3f88ea89530aa712ca01a708761cfbe2a14de\"\n          \"2fb4d5ffcc486ffab600ef97e79e4d734337b637947d04f1aa87e60020be8a26\"\n          \"937d0e701b39c2ef09b54cc1fc784931bcc5d6b58b01bf8f636c6d40545ef5a7\"\n          \"a5aff122f21d72e40fa1b3bea67c5a6c27127c55ccf61b601f4d59438a453c6e\"\n          \"8ef9f1904e5c209556c085393c4ea7152412090961dc0f406dd7c008d00c8bac\"\n          \"435b6f77ce8f26240d3ca3653d86a542240b34209ae9ab87086a539a10f9fa55\"\n          \"51b9d13ed9501877faa3708219a2b0b2678ec57bb1ad31a8d0462ee7b2cc38f2\"\n          \"644969b742c0da8aefe33c5185e088\");\n      v.emplace_back(\n          \"d87dc4024a0266375b6e4ba966765b1f98a02c0b14ae969d3a00bcbcc2c1b741\"\n          \"dc96035fddc310d2b2f801e019252489084363589be8242dd4c5454cfec5cd68\"\n          \"858d519d9f1a2660d522a399638a3dce554fbb3b9c5956f8046f7e2c488739f6\"\n          \"fc399c208c8abb94bef1e057a4e64b8a2b4a1e71903a4ebb5540934919828696\"\n          \"f09fe0a2bf4560d9206f7bf7d5e78ac1ccf8e4650d05cffb71b20725249f82b6\"\n          \"2f94730e854c2e50cdab1bda0888ca1137b4bc32a7b469191ea7ee33a329fc5c\"\n          \"ef8c096934ccd6142f109163b4efb93f12e85307da35eb6562ef110d4eedd0ba\"\n          \"a1ed720aa77c2dafccb1a33c6f5d8a23\");\n      v.emplace_back(\n          \"c0ce6af7494dff967497d120cf99bc0fdabdd04831ba57bd6fa5d7f5d1378b1f\"\n          \"ad4aa0c5638b3aeb34730aa782515e9a720ba112933adeeed13f5407959bad97\"\n          \"15057001402327698a8512af562b75bb70e0f9883df3726407edf3a6cfdd4107\"\n          \"18ed739969ddafa6b4e186b3ee77dbf47cad4ed5e7a458bb927b8efccdd63a5b\"\n          \"2399e49926e68c6d4dafaa639354e0ba349187a0cf4f3e92774a33bf95878ac5\"\n          \"85fd72b5544ae54295a3a0d8fd0d063b0e6e77feb7deb3e617e263de65531d60\"\n          \"d138eb2e54de5d50b12c47c23ba4bc91bc477556ac56b0706629a2a89657253c\"\n          \"cd36746918be8d0b57b9e97c6146466554\");\n      v.emplace_back(\n          \"6df5132e38e7c63b5e09d42239bf16f6a53187733ba07287b51f2362196dcd93\"\n          \"47cf74d9f6be301ed993b63ceff5192e6f68966dee3277587bf4845bb345af79\"\n          \"07217cfd0f3c99c34a0d8b723f8c70c5648b998e22ad0c4612b778235f757755\"\n          \"b5fdc4f00684d5aed5c135fcf487f06cedf9f11934715b66589d6af2188a3b4e\"\n          \"885d28e6f223f60d98817415a2d47607ed5d5b43a7559cb2bde1021f168a9d4a\"\n          \"89d1cda0801e2c876e03208a841ed48ce86965b822039e99d56fa82d62bcd9f5\"\n          \"0deb810420e456e80f535be7baa5c1d3087f5145690a4dcf284a106ba6f5903f\"\n          \"c0f1ecb57a7b81485710c82edf7090cf382a\");\n      v.emplace_back(\n          \"925437b13c121ef97e09a3ab9a90ccba96896302f81c52697109dfc0987eeef2\"\n          \"8b9f1062981c61076b7b2d028bb6547f50401c1268d192570bcd05d5003c9bcf\"\n          \"3845995f195339696e010981afa1adbc79857df2f72757eb4c72dd61944b68cf\"\n          \"d1805bc248ccef6a20874add8029a8b9768d632e74fd03698d959b71b3e9e801\"\n          \"280c022ba6d1b193cad60227a22fada2a0ff5f00b673e866127cc2da1c355cf5\"\n          \"8093fcc65580d2f1795c2ecae21ac5f0bb5737d748dbe3f83d26bc5194b00a50\"\n          \"250367fc687d813acb857acdd580aeef2637fa78c2a7ee2dd7543d4a40d37e49\"\n          \"673aa073932dfb75e9d79c087fe757db4414d6\");\n      v.emplace_back(\n          \"a840502c7c8a93e3a9722b1baa51b553df591b2091a842678e4c68e34c92afcb\"\n          \"1099b3d3334e247aa2acc24e03a32b438dec4ffa644f114ff50e3683d6955627\"\n          \"54134c73ffe785f1a2c87591a50239402d6302c30c8365dd8f50dfeb5c2479f7\"\n          \"60eb119f31686e29ae973b46ce646463e1e56c0f8a6252b85d83bfd17fa22ce9\"\n          \"8a9dc2880db8fe277d6f92cd4cf7cf73cd930c9e33cf61395a36548b31ca1f8b\"\n          \"27dd43100fe9df2884a7b384f14bf7ccb69e8a8b21884012058c11e3e1078727\"\n          \"e452dbbc49c26db558c3d00032dffb21ae2841a186fc66d5bc5243ddad577727\"\n          \"fbee6bf8c2d0af778773a1b5250e875483bca9c0\");\n      v.emplace_back(\n          \"8e474a9a84ca66665afbe283ea1dcc50e9a4e962a8c4a57aae5531047a062852\"\n          \"db6b2a0622fb46cd62be1be9136a41834ce55ea5676142415b7c3ad60901a365\"\n          \"df3197a375d9b2d78eaab078eaa1df2e0bce6e5f6c983a73f15d8275ebc31867\"\n          \"a1b85abad097742e6213841ea0f2c96ca9860d73a4908b8544de88c82e12a32f\"\n          \"38c8af1434c720a82dac08152ec7b3acae5482664a68ef92a5eb8e7c27a45f27\"\n          \"50c0b4e7f057d6fb3bc36b07ea16735e12c14d0c1ba4dc5f6788428f036b4e5e\"\n          \"4fe59766e80f864d11962f02805c0ddb7f9705faa0e2eae4d3c7f1b44af42bae\"\n          \"be8b079bc063bfe14638a126926c9984210a2b932d\");\n      v.emplace_back(\n          \"c39669d1c430c3e2c1724f007eabc83ab5965414fbcb96c5496285529885cdc7\"\n          \"fe1e499d7a10f697b7d6b1d96720481ef33758100b1237aab8d204cdfecfc332\"\n          \"4ec5232c18c95e427a16ccfdd2755850f142e67f61b5cde4a4b17b2427ce216d\"\n          \"d0021edb094c78321a6e73a120da59e11188064db3432b30942b5caf3d8692d4\"\n          \"762fb64b0a725c097d747366cba193de4651e92de640911838c351a43e85a391\"\n          \"d85638b38a85c7083ee02e41bded091399a77851ddd026ac2d8cf11f8b07883d\"\n          \"238f7e1e19acb2ef215e1d4a033cca51d7d7ff132bb89cfcde2693b3a41efd51\"\n          \"23a0f17a64d7e0a6d2e5b77283e99ab1c69fcc6d20e0\");\n      v.emplace_back(\n          \"7dbeac7fa7003c93db93ad5c10e1c5a7d1f3d25df52edc39192ad115a9aa1429\"\n          \"86803a35912edf5568ff4d35a8a68a2db44d5c2ae93c7239198c642dc0732e28\"\n          \"f703dbdf4b586a3ad2db363fe17c27c77e08344cd8fa36db95665ca974b5a061\"\n          \"3f3eb584eb6b371ef1432d39edbed3ef88104d0664a006b2b08ade648f90da57\"\n          \"661b267ec637c147bdfec665bb05e01e4d607070b8eeddbfce52ab461b4a54d4\"\n          \"c3c3eb33c6213eeb5581c7d752669d70ba1542c9f83a3e8e5445afc468306180\"\n          \"268083aa7c0c471929dc70150d3886e2fdd8ffa1821f956b3eb1cb5d8870c369\"\n          \"10bed17f32872a8c36e6df6a77d2b8ba67d0e367b71137\");\n      v.emplace_back(\n          \"9e75cfd15638c15d60ea531b2806b51d3ae590e64d6aac611812992e870fba84\"\n          \"f76c367f78c8b26de7033f87896468edc89d88a5ee582429548b620ec67388ee\"\n          \"80888be6f513009777d9243dc6d71f3b3251418ff9d2aef57287d7e9d1a62437\"\n          \"f54d39dec07aa36bb28ee45d3c7f050b8f9a3e37e233e3aa91711287510dd511\"\n          \"1616c0fe19ce08390f6033408dfcc5ad37bb6af02e8ccf794e5609d5e16e971a\"\n          \"a36e21304dfcdb4368131db4acf38f7c911368e4df2b42fb02068509e3a15b9d\"\n          \"59b87292d684966e7492a1f46e2923a9a40324b0bec5f7d1751b41feb97def10\"\n          \"447a278a062150bba4129e6ba7206bad86d4b6d7d98b06b7\");\n      v.emplace_back(\n          \"e6cd6a12f97317f3c1fc588b7a4f1afa8abde43821301514f2970b224af94a90\"\n          \"32efa0c6f97a8434dd37bba19471faeaff3b8a8f9cb5a9acc71f00917563a8c8\"\n          \"35bb97e4fda77aa709a4e88937b852e957b01f0f2385b82db6327185a131efec\"\n          \"a048f2a853ffe5b1cfc5310513efaef893f95360447acffc38cc409f7ab04857\"\n          \"22aab359a37918c52019c86689dbb9a4f0f38c9917d76b22910ed656ffee07ac\"\n          \"efb88ec7f0809e7f0203fd3cd4a1e7527cce0c029b7c80852b86455cb9d87a6f\"\n          \"0878f08b0d001afba2768f33334d81be572503b1cc3a0af7807ec41a4dfaad3a\"\n          \"50fc96476af744cd7c49d919454c187d156799e583a8c74d03\");\n      v.emplace_back(\n          \"02608e76c626822f416e6deb6056eba09a6898fd696174e39620d960e47b78de\"\n          \"1fc006d8130521843c8f3e610a6295fe15950c8974b2f7b18f3850a257eae172\"\n          \"69a0268ce18b321f480d96e2750e923bd32d6c05fafb4ed3eb49d45c02f2c535\"\n          \"8baa0411743c96285bee23da543dd8e21ac15326f9d9eabd3c3feb98d91cc99c\"\n          \"0322d52622321946a688e28180c1212e75461d205eaa0080ca2667c670747f8b\"\n          \"b5b18ceaadcb4fbcf5ad8be2878030b510c6fcd564c848bb08b5b877da740e68\"\n          \"4d9d52654324067c8a32f90c8ef40a9ad0067b183d1c18f93d5437f08bf03e4a\"\n          \"04cebdbf8a075e88ce8b95669b71dff7e40d384d20d1c06a31af\");\n      v.emplace_back(\n          \"2f4119c21c013098b20b8dbd84b47fd5011c72df62b939746b7c8d496ab4173c\"\n          \"dd2d8ff9952619fbcc86d1ef2777f17638de90c1644b17e27d7ed97da0074a2f\"\n          \"530b2441eb6d0eb56eb46ce882105bf2ae3d956c4d7e5be803c5dab0ce7c5554\"\n          \"8306cb9105ab5d098288d8aeedc03cca581721ef1cab2e04e315cd7f8bddae7c\"\n          \"9ac4afa865a15bbf558b8f4205a6fdf405d021b67a0326efda528149b1729c26\"\n          \"b3b4d3869425f324b5f0865a6be0ed9dd04893f1fa2da06b0e5665fc317e89b4\"\n          \"7cb71fe6e673878ae4839fbbdb26fae94cf37583985b642186afafa3c896c55e\"\n          \"9284ee2b7e5fde9596c42d5136a5024ce6f0c6ba5fed11928ef0ad\");\n      v.emplace_back(\n          \"a270cd89ec091f4862974d10dca5a283b8c332f7b4a99527ad63fb86e1d4b64e\"\n          \"dc1281e5278d000c9c6f1bb5fca1d687f689ec64ab32d61b3f47a23c98ef7071\"\n          \"8cc1510b4785c2b56e3b619b3e5c184628e0c96255257b345a6c42a589fa245e\"\n          \"2fdbd7819b8f0460fc371d683c37a468a5eb61bfd5338fdedb66d70ac110949a\"\n          \"19b9e417b60d6fdf511eb41737c35ae15975f5a98125198f53214375ae8361f2\"\n          \"d1a4d9df67c21067a676301a040e2ff99b7f9f4b7f27a5a2db82c56f8fdb366a\"\n          \"eb3deaeff45d163c859ee2d60f11a16193a3b81f51ab9c268d53883c166fbf2a\"\n          \"f91f34735b170278a8d594c4489ef6fc530e2faa10e78c90274084b5\");\n      v.emplace_back(\n          \"3e4cc5a816a4eb2e2c4a7fa626ed70a7dd08bc3d8b3fe70ed007c76db3fc62be\"\n          \"345d00107519a2f16c31479b9ab74553169b8a6a54c3e1bf5c142a946cba2d1c\"\n          \"ff48bfb4c4896209514a349c6367df2ade1d6b5848a4aad085db2e48ca933f92\"\n          \"17a11ffd55f1addc12f20abfbd71382836df2283e739bd003031acafb7331fbe\"\n          \"4baef9a166f45f504f6aae650e29733a3b8f15cf39c99506cfd1bf2bd7a70ad6\"\n          \"00fd27bf34a18a8b94be6e7ccd0d92fc004de9d3f06268878ff7af6c796d3503\"\n          \"88d28760e9930a8de562d4a99f5c7446520a186337389f3763305209212571f5\"\n          \"73d0cb26ab0cbddb0b09eec2112feffcde44dcc641d2396dbd1a31d965\");\n      v.emplace_back(\n          \"f34675a5f4c344c1616dfffbe5ed963b6308a5409b7d0106a2a733117f9ad889\"\n          \"23d33478d4d0f52058f03bf7c2da3f26221cc0495fe9edb16bd32682965f992d\"\n          \"7e9a14daae5cd44f29d4dd92d0b4f1a893394c659c2a755231ab20e59a31aec6\"\n          \"451d3b301d6e7a41027fb8a2520177094b7422575803e72e647de294a04c4f34\"\n          \"f8e487036de84679f3c5f915608cfd15d565e24b8ae27acefcbf54b033a83882\"\n          \"745f6418a217ccd0f8ae4e10ff04e67f57b36d94dcd5b442f6e36e452ffbf6ec\"\n          \"7ba6490e079419252d54ab64c5afdde196d0b5c352ad70ce39b16791cccbb33d\"\n          \"498d5a7ffd2ae2174b34b23f78e8972a5fa04f7ebe66203d681bb163aa18\");\n      v.emplace_back(\n          \"65caea398636380c6955c7549491c91157776fa1a6514355837e51fc6bbc35b7\"\n          \"bb8b44fe019c1be93ce474e810305e36e5cd445b417001cb2b8bba78af6fdc1c\"\n          \"12b83e326a5d323752930c5fe879629d5f5772f872b3db4ddb1cbf43ef3115e3\"\n          \"44327b3dcba6a7d8c82511c74a70b12b405481e66dbd1b8a7a9cdab1d52bdcde\"\n          \"972aba064915ceee02e7901e757d1470fabc32f9ab873508c6e243b956cac2d6\"\n          \"3aeb32b179f2cfab3cb4c2345dfb6a18c05b97f9e659c0020de22f85b5ceef47\"\n          \"0a5ad6e8597c8570a85be25d48d60151577f9a4fbe2c09862dd57ff734e156f6\"\n          \"6fd7107ccfe0e46193d2272ce6d6c0dfc0a81cef52cbd61d2964aea53922bb\");\n      return v;\n    }();\n    return testVector;\n  }\n};\n\nTEST_F(Blake2xbTest, checkTestVectors) {\n  checkTestVector(Blake2xbTest::getTestVector());\n}\n\nclass Blake2xbKeyedTest : public Blake2xbTestBase {\n protected:\n  static const std::vector<std::string>& getTestVector() {\n    static const std::vector<std::string> testVector = []() {\n      std::vector<std::string> v;\n      v.emplace_back(\"64\");\n      v.emplace_back(\"f457\");\n      v.emplace_back(\"e8c045\");\n      v.emplace_back(\"a74c6d0d\");\n      v.emplace_back(\"eb02ae482a\");\n      v.emplace_back(\"be65b981275e\");\n      v.emplace_back(\"8540ccd083a455\");\n      v.emplace_back(\"074a02fa58d7c7c0\");\n      v.emplace_back(\"da6da05e10db3022b6\");\n      v.emplace_back(\"542a5aae2f28f2c3b68c\");\n      v.emplace_back(\"ca3af2afc4afe891da78b1\");\n      v.emplace_back(\"e0f66b8dcebf4edc85f12c85\");\n      v.emplace_back(\"744224d383733b3fa2c53bfcf5\");\n      v.emplace_back(\"b09b653e85b72ef5cdf8fcfa95f3\");\n      v.emplace_back(\"dd51877f31f1cf7b9f68bbb09064a3\");\n      v.emplace_back(\"f5ebf68e7ebed6ad445ffc0c47e82650\");\n      v.emplace_back(\"ebdcfe03bcb7e21a9091202c5938c0a1bb\");\n      v.emplace_back(\"860fa5a72ff92efafc48a89df1632a4e2809\");\n      v.emplace_back(\"0d6d49daa26ae2818041108df3ce0a4db48c8d\");\n      v.emplace_back(\"e5d7e1bc5715f5ae991e4043e39533af5d53e47f\");\n      v.emplace_back(\"5232028a43b9d4dfa7f37439b49495926481ab8a29\");\n      v.emplace_back(\"c118803c922f9ae2397fb676a2ab7603dd9c29c21fe4\");\n      v.emplace_back(\"2af924f48b9bd7076bfd68794bba6402e2a7ae048de3ea\");\n      v.emplace_back(\"61255ac38231087c79ea1a0fa14538c26be1c851b6f318c0\");\n      v.emplace_back(\"f9712b8e42f0532162822f142cb946c40369f2f0e77b6b186e\");\n      v.emplace_back(\"76da0b89558df66f9b1e66a61d1e795b178ce77a359087793ff2\");\n      v.emplace_back(\"9036fd1eb32061bdecebc4a32aa524b343b8098a16768ee774d93c\");\n      v.emplace_back(\n          \"f4ce5a05934e125d159678bea521f585574bcf9572629f155f63efcc\");\n      v.emplace_back(\n          \"5e1c0d9fae56393445d3024d6b82692d1339f7b5936f68b062c691d3bf\");\n      v.emplace_back(\n          \"538e35f3e11111d7c4bab69f83b30ade4f67addf1f45cdd2ac74bf299509\");\n      v.emplace_back(\n          \"17572c4dcbb17faf8785f3bba9f6903895394352eae79b01ebd758377694cc\");\n      v.emplace_back(\n          \"29f6bb55de7f8868e053176c878c9fe6c2055c4c5413b51ab0386c277fdbac75\");\n      v.emplace_back(\n          \"bad026c8b2bd3d294907f2280a7145253ec2117d76e3800357be6d431b16366e\"\n          \"41\");\n      v.emplace_back(\n          \"386b7cb6e0fd4b27783125cbe80065af8eb9981fafc3ed18d8120863d972fa74\"\n          \"27d9\");\n      v.emplace_back(\n          \"06e8e6e26e756fff0b83b226dce974c21f970e44fb5b3e5bbada6e4b12f81cca\"\n          \"666f48\");\n      v.emplace_back(\n          \"2f9bd300244f5bc093ba6dcdb4a89fa29da22b1de9d2c9762af919b5fedf6998\"\n          \"fbda305b\");\n      v.emplace_back(\n          \"cf6bdcc46d788074511f9e8f0a4b86704365b2d3f98340b8db53920c385b959a\"\n          \"38c8869ae7\");\n      v.emplace_back(\n          \"1171e603e5cdeb4cda8fd7890222dd8390ede87b6f3284cac0f0d832d8250c92\"\n          \"00715af7913d\");\n      v.emplace_back(\n          \"bda7b2ad5d02bd35ffb009bdd72b7d7bc9c28b3a32f32b0ba31d6cbd3ee87c60\"\n          \"b7b98c03404621\");\n      v.emplace_back(\n          \"2001455324e748503aa08eff2fb2e52ae0170e81a6e9368ada054a36ca340fb7\"\n          \"79393fb045ac72b3\");\n      v.emplace_back(\n          \"45f0761aefafbf87a68f9f1f801148d9bba52616ad5ee8e8ac9207e9846a782f\"\n          \"487d5cca8b20355a18\");\n      v.emplace_back(\n          \"3a7e05708be62f087f17b41ac9f20e4ef8115c5ab6d08e84d46af8c273fb46d3\"\n          \"ce1aabebae5eea14e018\");\n      v.emplace_back(\n          \"ea318da9d042ca337ccdfb2bee3e96ecb8f907876c8d143e8e44569178353c2e\"\n          \"593e4a82c265931ba1dd79\");\n      v.emplace_back(\n          \"e0f7c08f5bd712f87094b04528fadb283d83c9ceb82a3e39ec31c19a42a1a1c3\"\n          \"bee5613b5640abe069b0d690\");\n      v.emplace_back(\n          \"d35e63fb1f3f52ab8f7c6cd7c8247e9799042e53922fbaea808ab979fa0c0965\"\n          \"88cfea3009181d2f93002dfc11\");\n      v.emplace_back(\n          \"b8b0ab69e3ae55a8699eb481dd665b6a2424c89bc6b7cca02d15fdf1b9854139\"\n          \"cab49d34de498b50b2c7e8b910cf\");\n      v.emplace_back(\n          \"fb65e3222a2950eae1701d4cdd4736266f65bf2c0d2e77968996eadb60ef74fb\"\n          \"786f6234973a2524bdfe32d100aa0e\");\n      v.emplace_back(\n          \"f28b4bb3a2e2c4d5c01a23ff134558559a2d3d704b75402983ee4e0f71d273ae\"\n          \"056842c4153b18ee5c47e2bfa54313d4\");\n      v.emplace_back(\n          \"7bb78794e58a53c3e4b1aeb161e756af051583d14e0a5a3205e094b7c9a8cf62\"\n          \"d098fa9ea1db12f330a51ab9852c17f983\");\n      v.emplace_back(\n          \"a879a8ebae4d0987789bcc58ec3448e35ba1fa1ee58c668d8295aba4eaeaf276\"\n          \"2b053a677e25404f635a53037996974d418a\");\n      v.emplace_back(\n          \"695865b353ec701ecc1cb38f3154489eed0d39829fc192bb68db286d20fa0a64\"\n          \"235cde5639137819f7e99f86bd89afcef84a0f\");\n      v.emplace_back(\n          \"a6ec25f369f71176952fb9b33305dc768589a6070463ee4c35996e1ced4964a8\"\n          \"65a5c3dc8f0d809eab71366450de702318e4834d\");\n      v.emplace_back(\n          \"604749f7bfadb069a036409ffac5ba291fa05be8cba2f141554132f56d9bcb88\"\n          \"d1ce12f2004cd3ade1aa66a26e6ef64e327514096d\");\n      v.emplace_back(\n          \"daf9fa7dc2464a899533594e7916fc9bc585bd29dd60c930f3bfa78bc47f6c84\"\n          \"39448043a45119fc9228c15bce5fd24f46baf9de736b\");\n      v.emplace_back(\n          \"943ea5647a8666763084da6a6f15dcf0e8dc24f27fd0d9194805d25180fe3a6d\"\n          \"98f4b2b5e0d6a04e9b41869817030f16ae975dd41fc35c\");\n      v.emplace_back(\n          \"af4f73cbfc093760dfeb52d57ef45207bbd1a515f5523404e5d95a73c237d97a\"\n          \"e65bd195b472de6d514c2c448b12fafc282166da132258e9\");\n      v.emplace_back(\n          \"605f4ed72ed7f5046a342fe4cf6808100d4632e610d59f7ebb016e367d0ff0a9\"\n          \"5cf45b02c727ba71f147e95212f52046804d376c918cadd260\");\n      v.emplace_back(\n          \"3750d8ab0a6b13f78e51d321dfd1aa801680e958de45b7b977d05732ee39f856\"\n          \"b27cb2bcce8fbf3db6666d35e21244c2881fdcc27fbfea6b1672\");\n      v.emplace_back(\n          \"8f1b929e80ab752b58abe9731b7b34eb61369536995abef1c0980d93903c1880\"\n          \"da3637d367456895f0cb4769d6de3a979e38ed6f5f6ac4d48e9b32\");\n      v.emplace_back(\n          \"d8469b7aa538b36cdc711a591d60dafecca22bd421973a70e2deef72f69d8014\"\n          \"a6f0064eabfbebf5383cbb90f452c6e113d2110e4b1092c54a38b857\");\n      v.emplace_back(\n          \"7d1f1ad2029f4880e1898af8289c23bc933a40863cc4ab697fead79c58b6b8e2\"\n          \"5b68cf5324579b0fe879fe7a12e6d03907f0140dfe7b29d33d6109ecf1\");\n      v.emplace_back(\n          \"87a77aca6d551642288a0dff66078225ae39d288801607429d6725ca949eed7a\"\n          \"6f199dd8a65523b4ee7cfa4187400e96597bfffc3e38ade0ae0ab88536a9\");\n      v.emplace_back(\n          \"e101f43179d8e8546e5ce6a96d7556b7e6b9d4a7d00e7aade5579d085d527ce3\"\n          \"4a9329551ebcaf6ba946949bbe38e30a62ae344c1950b4bde55306b3bac432\");\n      v.emplace_back(\n          \"4324561d76c370ef35ac36a4adf8f3773a50d86504bd284f71f7ce9e2bc4c1f1\"\n          \"d34a7fb2d67561d101955d448b67577eb30dfee96a95c7f921ef53e20be8bc44\");\n      v.emplace_back(\n          \"78f0ed6e220b3da3cc9381563b2f72c8dc830cb0f39a48c6ae479a6a78dcfa94\"\n          \"002631dec467e9e9b47cc8f0887eb680e340aec3ec009d4a33d241533c76c8ca\"\n          \"8c\");\n      v.emplace_back(\n          \"9f6589c31a472e0a736f4eb22b6c70a9d332cc15304ccb66a6b97cd051b6ed82\"\n          \"f8990e1d9bee2e4bb1c3c45e550ae0e7b96e93ae23f2fb8f63b309131e72b36c\"\n          \"ba6a\");\n      v.emplace_back(\n          \"c138077ee4ed3d7ffa85ba851dfdf6e9843fc1dc00889d117237bfaad9aa7571\"\n          \"92f73556b959f98e6d24886ce48869f2a01a48c371785f12b6484eb2078f08c2\"\n          \"2066e1\");\n      v.emplace_back(\n          \"f83e7c9e0954a500576ea1fc90a3db2cbd7994eaef647dab5b34e88ab9dc0b47\"\n          \"addbc807b21c8e6dd3d0bd357f008471d4f3e0abb18450e1d4919e03a34545b9\"\n          \"643f870e\");\n      v.emplace_back(\n          \"3277a11f2628544fc66f50428f1ad56bcba6ee36ba2ca6ecdf7e255effc0c302\"\n          \"35c039d13e01f04cf1efe95b5c2033ab72adda30994b62f2851d17c9920eadca\"\n          \"9a251752dc\");\n      v.emplace_back(\n          \"c2a834281a06fe7b730d3a03f90761daf02714c066e33fc07e1f59ac801ec2f4\"\n          \"433486b5a2da8faa51a0cf3c34e29b2960cd0013378938dbd47c3a3d12d70db0\"\n          \"1d7d06c3e91e\");\n      v.emplace_back(\n          \"47680182924a51cabe142a6175c9253e8ba7ea579ece8d9bcb78b1e9ca00db84\"\n          \"4fa08abcf41702bd758ee2c608d9612fed50e85854469cb4ef3038acf1e35b6b\"\n          \"a4390561d8ae82\");\n      v.emplace_back(\n          \"cec45830cd71869e83b109a99a3cd7d935f83a95de7c582f3adbd34e4938fa2f\"\n          \"3f922f52f14f169c38cc6618d3f306a8a4d607b345b8a9c48017136fbf825aec\"\n          \"f7b620e85f837fae\");\n      v.emplace_back(\n          \"46fb53c70ab105079d5d78dc60eaa30d938f26e4d0b9df122e21ec85deda9474\"\n          \"4c1daf8038b8a6652d1ff3e7e15376f5abd30e564784a999f665078340d66b0e\"\n          \"939e0c2ef03f9c08bb\");\n      v.emplace_back(\n          \"7b0dcb52791a170cc52f2e8b95d8956f325c3751d3ef3b2b83b41d82d4496b46\"\n          \"228a750d02b71a96012e56b0720949ca77dc68be9b1ef1ad6d6a5ceb86bf565c\"\n          \"b972279039e209dddcdc\");\n      v.emplace_back(\n          \"7153fd43e6b05f5e1a4401e0fef954a737ed142ec2f60bc4daeef9ce73ea1b40\"\n          \"a0fcaf1a1e03a3513f930dd5335723632f59f7297fe3a98b68e125eadf478eb0\"\n          \"45ed9fc4ee566d13f537f5\");\n      v.emplace_back(\n          \"c7f569c79c801dab50e9d9ca6542f25774b3841e49c83efe0b89109f569509ce\"\n          \"7887bc0d2b57b50320eb81fab9017f16c4c870e59edb6c26620d93748500231d\"\n          \"70a36f48a7c60747ca2d5986\");\n      v.emplace_back(\n          \"0a81e0c547648595adca65623ce783411aac7f7d30c3ad269efafab288e7186f\"\n          \"6895261972f5137877669c550f34f5128850ebb50e1884814ea1055ee29a866a\"\n          \"fd04b2087abed02d9592573428\");\n      v.emplace_back(\n          \"6a7b6769e1f1c95314b0c7fe77013567891bd23416374f23e4f43e27bc4c55cf\"\n          \"ada13b53b1581948e07fb96a50676baa2756db0988077b0f27d36ac088e0ff0f\"\n          \"e72eda1e8eb4b8facff3218d9af0\");\n      v.emplace_back(\n          \"a399474595cb1ccab6107f18e80f03b1707745c7bf769fc9f260094dc9f8bc6f\"\n          \"e09271cb0b131ebb2acd073de4a6521c8368e664278be86be216d1622393f234\"\n          \"35fae4fbc6a2e7c961282a777c2d75\");\n      v.emplace_back(\n          \"4f0fc590b2755a515ae6b46e9628092369d9c8e589e3239320639aa8f7aa44f8\"\n          \"111c7c4b3fdbe6e55e036fbf5ebc9c0aa87a4e66851c11e86f6cbf0bd9eb1c98\"\n          \"a378c7a7d3af900f55ee108b59bc9e5c\");\n      v.emplace_back(\n          \"ed96a046f08dd675107331d267379c6fce3c352a9f8d7b243008a74cb4e94108\"\n          \"36afaabe871dab6038ca94ce5f6d41fa922ce08aba58169f94cfc86d9f688f39\"\n          \"6abd24c11a6a9b0830572105a477c33e92\");\n      v.emplace_back(\n          \"379955f539abf0eb2972ee99ed9546c4bbee363403991833005dc27904c271ef\"\n          \"22a799bc32cb39f08d2e4ba6717d55153feb692d7c5efae70890bf29d96df023\"\n          \"33c7b05ccc314e4835b018fec9141a82c745\");\n      v.emplace_back(\n          \"e16cc8d41b96547ede0d0cf4d908c5fa393399daa4a9696e76a4c1f6a2a9fef7\"\n          \"0f17fb53551a8145ed88f18db8fe780a079d94732437023f7c1d1849ef69ad53\"\n          \"6a76204239e8ba5d97e507c36c7d042f87fe0e\");\n      v.emplace_back(\n          \"a81de50750ece3f84536728f227208bf01ec5b7721579d007de72c88ee206633\"\n          \"18332efe5bc7c09ad1fa8342be51f0609046ccf760a7957a7d8dc88941adb936\"\n          \"66a4521ebe76618e5ddc2dd3261493d400b50073\");\n      v.emplace_back(\n          \"b72c5fb7c7f60d243928fa41a2d711157b96aef290185c64b4de3dcfa3d644da\"\n          \"67a8f37c2ac55caad79ec695a473e8b481f658c497edb8a191526592b11a4122\"\n          \"82d2a4010c90ef4647bd6ce745ebc9244a71d4876b\");\n      v.emplace_back(\n          \"9550703877079c90e200e830f277b605624954c549e729c359ee01ee2b07741e\"\n          \"cc4255cb37f96682dafcdbaade1063e2c5ccbd1918fb669926a67744101fb6de\"\n          \"3ac016be4c74165a1e5a696b704ba2ebf4a953d44b95\");\n      v.emplace_back(\n          \"a17eb44d4de502dc04a80d5a5e9507d17f27c96467f24c79b06bc98a4c410741\"\n          \"d4ac2db98ec02c2a976d788531f1a4451b6c6204cef6dae1b6ebbcd0bde23e6f\"\n          \"ffb02754043c8fd3c783d90a670b16879ce68b5554fe1c\");\n      v.emplace_back(\n          \"41d3ea1eaba5be4a206732dbb5b70b79b66a6e5908795ad4fb7cf9e67efb13f0\"\n          \"6fef8f90acb080ce082aadec6a1b543af759ab63fa6f1d3941186482b0c2b312\"\n          \"f1151ea8386253a13ed3708093279b8eb04185636488b226\");\n      v.emplace_back(\n          \"5e7cdd8373dc42a243c96013cd29df9283b5f28bb50453a903c85e2ce57f3586\"\n          \"1bf93f03029072b70dac0804e7d51fd0c578c8d9fa619f1e9ce3d8044f65d556\"\n          \"34dba611280c1d5cfb59c836a595c803124f696b07ddfac718\");\n      v.emplace_back(\n          \"26a14c4aa168907cb5de0d12a82e1373a128fb21f2ed11feba108b1bebce934a\"\n          \"d63ed89f4ed7ea5e0bc8846e4fc10142f82de0bebd39d68f7874f615c3a9c896\"\n          \"bab34190e85df05aaa316e14820b5e478d838fa89dfc94a7fc1e\");\n      v.emplace_back(\n          \"0211dfc3c35881adc170e4ba6daab1b702dff88933db9a6829a76b8f4a7c2a6d\"\n          \"658117132a974f0a0b3a38ceea1efc2488da21905345909e1d859921dc2b5054\"\n          \"f09bce8eeb91fa2fc6d048ce00b9cd655e6aafbdaa3a2f19270a16\");\n      v.emplace_back(\n          \"ddf015b01b68c4f5f72c3145d54049867d99ee6bef24282abf0eecdb506e295b\"\n          \"acf8f23ffa65a4cd891f76a046b9dd82cae43a8d01e18a8dff3b50aeb92672be\"\n          \"69d7c087ec1fa2d3b2a39196ea5b49b7baede37a586fea71aded587f\");\n      v.emplace_back(\n          \"6ee721f71ca4dd5c9ce7873c5c04c6ce76a2c824b984251c15535afc96adc9a4\"\n          \"d48ca314bfeb6b8ee65092f14cf2a7ca9614e1dcf24c2a7f0f0c11207d3d8aed\"\n          \"4af92873b56e8b9ba2fbd659c3f4ca90fa24f113f74a37181bf0fdf758\");\n      v.emplace_back(\n          \"689bd150e65ac123612524f720f54def78c095eaab8a87b8bcc72b443408e322\"\n          \"7f5c8e2bd5af9bcac684d497bc3e41b7a022c28fb5458b95e8dfa2e8caccde04\"\n          \"92936ff1902476bb7b4ef2125b19aca2cd3384d922d9f36dddbcd96ae0d6\");\n      v.emplace_back(\n          \"3a3c0ef066fa4390ec76ad6be1dc9c31ddf45fef43fbfa1f49b439caa2eb9f30\"\n          \"42253a9853e96a9cf86b4f873785a5d2c5d3b05f6501bc876e09031188e05f48\"\n          \"937bf3c9b667d14800db62437590b84ce96aa70bb5141ee2ea41b55a6fd944\");\n      v.emplace_back(\n          \"741ce384e5e0edaebb136701ce38b3d33215415197758ae81235307a4115777d\"\n          \"4dab23891db530c6d28f63a957428391421f742789a0e04c99c828373d9903b6\"\n          \"4dd57f26b3a38b67df829ae243feef731ead0abfca049924667fdec49d40f665\");\n      v.emplace_back(\n          \"a513f450d66cd5a48a115aee862c65b26e836f35a5eb6894a80519e2cd96cc4c\"\n          \"ad8ed7eb922b4fc9bbc55c973089d627b1da9c3a95f6c019ef1d47143cc545b1\"\n          \"5e4244424be28199c51a5efc7234dcd94e72d229897c392af85f523c26334278\"\n          \"25\");\n      v.emplace_back(\n          \"71f1554d2d49bb7bd9e62e71fa049fb54a2c097032f61ebda669b3e1d4593962\"\n          \"e47fc62a0ab5d85706aebd6a2f9a192c88aa1ee2f6a46710cf4af6d3c25b7e68\"\n          \"ad5c3db23ac009c8f13625ff85dc8e50a9a1b2682d3329330b973ec8cbb7bb73\"\n          \"b2bd\");\n      v.emplace_back(\n          \"167cc1067bc08a8d2c1a0c10041ebe1fc327b37043f6bd8f1c63569e9d36ded5\"\n          \"8519e66b162f34b6d8f1107ef1e3de199d97b36b44141a1fc4f49b883f40507f\"\n          \"f11f909a017869dc8a2357fc7336ae68703d25f75710b0ff5f9765321c0fa53a\"\n          \"51675c\");\n      v.emplace_back(\n          \"cb859b35dc70e264efaad2a809fea1e71cd4a3f924be3b5a13f8687a1166b538\"\n          \"c40b2ad51d5c3e47b0de482497382673140f547068ff0b3b0fb7501209e1bf36\"\n          \"082509ae85f60bb98fd02ac50d883a1a8daa704952d83c1f6da60c9624bc7c99\"\n          \"912930bf\");\n      v.emplace_back(\n          \"afb1f0c6b7125b04fa2578dd40f60cb411b35ebc7026c702e25b3f0ae3d4695d\"\n          \"44cfdf37cb755691dd9c365edadf21ee44245620e6a24d4c2497135b37cd7ac6\"\n          \"7e3bd0aaee9f63f107746f9b88859ea902bc7d6895406aa2161f480cad56327d\"\n          \"0a5bba2836\");\n      v.emplace_back(\n          \"13e9c0522587460d90c7cb354604de8f1bf850e75b4b176bda92862d35ec8108\"\n          \"61f7d5e7ff6ba9302f2c2c8642ff8b7776a2f53665790f570fcef3cac069a90d\"\n          \"50db42227331c4affb33d6c040d75b9aeafc9086eb83ced38bb02c759e95ba08\"\n          \"c92b17031288\");\n      v.emplace_back(\n          \"0549812d62d3ed497307673a4806a21060987a4dbbf43d352b9b170a29240954\"\n          \"cf04bc3e1e250476e6800b79e843a8bd8253b7d743de01ab336e978d4bea384e\"\n          \"aff700ce020691647411b10a60acacb6f8837fb08ad666b8dcc9eaa87ccb42ae\"\n          \"f6914a3f3bc30a\");\n      v.emplace_back(\n          \"3a263efbe1f2d463f20526e1d0fd735035fd3f808925f058b32c4d8788aeeab9\"\n          \"b8ce233b3c34894731cd73361f465bd350395aebcabd2fb63010298ca025d849\"\n          \"c1fa3cd573309b74d7f824bbfe383f09db24bcc565f636b877333206a6ad7081\"\n          \"5c3bef5574c5fc1c\");\n      v.emplace_back(\n          \"3c6a7d8a84ef7e3eaa812fc1eb8e85105467230d2c9e4562edbfd808f4d1ac15\"\n          \"d16b786cc6a02959c2bc17149c2ce74c6f85ee5ef22a8a96b9be1f197cffd214\"\n          \"c1ab02a06a9227f37cd432579f8c28ff2b5ac91cca8ffe6240932739d56788c3\"\n          \"54e92c591e1dd76499\");\n      v.emplace_back(\n          \"b571859294b02af17541a0b5e899a5f67d6f5e36d38255bc417486e69240db56\"\n          \"b09cf2607fbf4f95d085a779358a8a8b41f36503438c1860c8f361ce0f2783a0\"\n          \"8b21bd7232b50ca6d35428335272a5c05b436b2631d8d5c84d60e8040083768c\"\n          \"e56a250727fb0579dd5c\");\n      v.emplace_back(\n          \"98ee1b7269d2a0dd490ca38d447279870ea55326571a1b430adbb2cf65c49213\"\n          \"1136f504145df3ab113a13abfb72c33663266b8bc9c458db4bf5d7ef03e1d3b8\"\n          \"a99d5de0c024be8fabc8dc4f5dac82a0342d8ed65c329e7018d6997e69e29a01\"\n          \"350516c86beaf153da65ac\");\n      v.emplace_back(\n          \"41c5c95f088df320d35269e5bf86d10248f17aec6776f0fe653f1c356aae4097\"\n          \"88c938befeb67c86d1c8870e8099ca0ce61a80fbb5a6654c44529368f70fc9b9\"\n          \"c2f912f5092047d0ffc339577d24142300e34948e086f62e23ecaca410d24f8a\"\n          \"36b5c8c5a80e0926bc8aa16a\");\n      v.emplace_back(\n          \"9f93c41f533b2a82a4df893c78faaaa793c1506974ba2a604cd33101713ca4ad\"\n          \"fd30819ffd8403402b8d40aff78106f3357f3e2c24312c0d3603a17184d7b999\"\n          \"fc9908d14d50192aebabd90d05073da7af4be37dd3d81c90acc80e8333df546f\"\n          \"17ab6874f1ec204392d1c0571e\");\n      v.emplace_back(\n          \"3da5207245ac270a915fc91cdb314e5a2577c4f8e269c4e701f0d7493ba716de\"\n          \"79935918b917a2bd5db98050dbd1eb3894b65fac5abf13e075abebc011e651c0\"\n          \"3cafb6127147771a5c8418223e1548137a89206635c26ca9c235ccc108dc25cf\"\n          \"846e4732444bd0c2782b197b262b\");\n      v.emplace_back(\n          \"96011af3965bb941dc8f749932ea484eccb9ba94e34b39f24c1e80410f96ce1d\"\n          \"4f6e0aa5be606def4f54301e930493d4b55d484d93ab9dd4dc2c9cfb79345363\"\n          \"af31ad42f4bd1aa6c77b8afc9f0d551bef7570b13b927afe3e7ac4de7603a087\"\n          \"6d5edb1ad9be05e9ee8b53941e8f59\");\n      v.emplace_back(\n          \"51dbbf2a7ca224e524e3454fe82ddc901fafd2120fa8603bc343f129484e9600\"\n          \"f688586e040566de0351d1693829045232d04ff31aa6b80125c763faab2a9b23\"\n          \"3313d931903dcfaba490538b06e4688a35886dc24cdd32a13875e6acf45454a8\"\n          \"eb8a315ab95e608ad8b6a49aef0e299a\");\n      v.emplace_back(\n          \"5a6a422529e22104681e8b18d64bc0463a45df19ae2633751c7aae412c250f8f\"\n          \"b2cd5e1270d3d0cf009c8aa69688ccd4e2b6536f5747a5bc479b20c135bf4e89\"\n          \"d33a26118705a614c6be7ecfe766932471ad4ba01c4f045b1abb5070f90ec784\"\n          \"39a27a1788db9327d1c32f939e5fb1d5ba\");\n      v.emplace_back(\n          \"5d26c983642093cb12ff0afabd87b7c56e211d01844ad6da3f623b9f20a0c968\"\n          \"034299f2a65e6673530c5980a532beb831c7d0697d12760445986681076dfb6f\"\n          \"ae5f3a4d8f17a0db5008ce8619f566d2cfe4cf2a6d6f9c3664e3a48564a351c0\"\n          \"b3c945c5ee24587521e4112c57e318be1b6a\");\n      v.emplace_back(\n          \"52641dbc6e36be4d905d8d60311e303e8e859cc47901ce30d6f67f152343e3c4\"\n          \"030e3a33463793c19effd81fb7c4d631a9479a7505a983a052b1e948ce093b30\"\n          \"efa595fab3a00f4cef9a2f664ceeb07ec61719212d58966bca9f00a7d7a8cb40\"\n          \"24cf6476bab7fbccee5fd4e7c3f5e2b2975aa2\");\n      v.emplace_back(\n          \"a34ce135b37bf3db1c4aaa4878b4499bd2ee17b85578fcaf605d41e1826b45fd\"\n          \"aa1b083d8235dc642787f11469a5493e36806504fe2a2063905e821475e2d5ee\"\n          \"217057950370492f5024995e77b82aa51b4f5bd8ea24dc71e0a8a640b0592c0d\"\n          \"80c24a726169cf0a10b40944747113d03b52708c\");\n      v.emplace_back(\n          \"46b3cdf4946e15a5334fc3244d6680f5fc132afa67bf43bfade23d0c9e0ec64e\"\n          \"7dab76faaeca1870c05f96b7d019411d8b0873d9fed04fa5057c039d5949a4d5\"\n          \"92827f619471359d6171691cfa8a5d7cb07ef2804f6ccad4821c56d4988bea77\"\n          \"65f660f09ef87405f0a80bcf8559efa111f2a0b419\");\n      v.emplace_back(\n          \"8b9fc21691477f11252fca050b121c5334eb4280aa11659e267297de1fec2b22\"\n          \"94c7ccee9b59a149b9930b08bd320d3943130930a7d931b71d2f10234f4480c6\"\n          \"7f1de883d9894ada5ed5071660e221d78ae402f1f05af47761e13fec979f2671\"\n          \"e3c63fb0ae7aa1327cf9b8313adab90794a52686bbc4\");\n      v.emplace_back(\n          \"cd6598924ce847de7ff45b20ac940aa6292a8a99b56a74eddc24f2cfb4579718\"\n          \"8614a21d4e8867e23ff75afd7cd324248d58fcf1ddc73fbd115dfa8c09e62022\"\n          \"fab540a59f87c989c12a86ded05130939f00cd2f3b512963dfe0289f0e54acad\"\n          \"881c1027d2a0292138fdee902d67d9669c0ca1034a9456\");\n      v.emplace_back(\n          \"594e1cd7337248704e691854af0fdb021067ddf7832b049ba7b684438c32b029\"\n          \"eded2df2c89a6ff5f2f2c311522ae2dc6db5a815afc60637b15ec24ef9541f15\"\n          \"50409db2a006da3affffe548a1eaee7bd114e9b805d0756c8e90c4dc33cb0522\"\n          \"6bc2b393b18d953f8730d4c7ae693159cdba758ad28964e2\");\n      v.emplace_back(\n          \"1f0d292453f04406ada8be4c161b82e3cdd69099a8637659e0ee40b8f6da4600\"\n          \"5cfc6085db9804852decfbe9f7b4dda019a7112612895a144ed430a960c8b2f5\"\n          \"458d3d56b7f427cee6358915aee7146278aed2a0296cdd929e4d21ef95a3adf8\"\n          \"b7a6beba673cdccdbdcfb2474711732d972ad054b2dc64f38d\");\n      v.emplace_back(\n          \"b65a72d4e1f9f9f75911cc46ad0806b9b18c87d105332a3fe183f45f063a746c\"\n          \"892dc6c4b9181b1485b3e3a2cc3b453eba2d4c39d6905a774ed3fb755468beb1\"\n          \"90925ecd8e57ecb0d985125741650c6b6a1b2a3a50e93e3892c21d47ed5884ee\"\n          \"d83aa94e1602288f2f49fe286624de9d01fcb54433a0dc4ad70b\");\n      v.emplace_back(\n          \"705ce0ffa469250782aff725248fc88fe98eb76659e8407edc1c4842c9867d61\"\n          \"fe64fb86f74e980598b92bc213d06f337bd5654fc28643c7ba769a4c31563427\"\n          \"543c00808b627a19c90d86c322f33566ce020121cc322229c3337943d46f68ef\"\n          \"939d613dcef0077269f88151d6398b6b009abb763410b154ad76a3\");\n      v.emplace_back(\n          \"7fa881ce87498440ab6af13854f0d851a7e0404de33896999a9b3292a5d2f5b3\"\n          \"ad033530c558168fe5d2fdb9b89a2354c46cf32a0e612afc6c6485d789511bfe\"\n          \"f26800c74bf1a4cfbe30bda310d5f6029c3dccdedb6149e4971274e276dccfab\"\n          \"d63bc4b9955e8303feb57f8a688db55ecb4b33d1f9fe1b3a8ba7ac32\");\n      v.emplace_back(\n          \"23a98f71c01c0408ae16843dc03be7db0aeaf055f951709d4e0dfdf64fffbffa\"\n          \"f900ee592ee10929648e56f6c1e9f5be5793f7df66453eb56502c7c56c0f0c88\"\n          \"da77abc8fa371e434104627ef7c663c49f40998dbad63fa6c7aa4fac17ae138d\"\n          \"8bbe081f9bd168cd33c1fbc92fa35ed687679f48a64b87db1fe5bae675\");\n      v.emplace_back(\n          \"7b8970b6a33237e5a7bcb39272703edb92285c55842b30b9a48834b1b507cc02\"\n          \"a6764739f2f7ee6ae02a7b715a1c455e59e8c77a1ae98abb10161853f1234d20\"\n          \"da99016588cd8602d6b7ec7e177d4011edfa61e6b3766a3c6f8d6e9eac893c56\"\n          \"8903eb6e6aba9c4725774f6b4343b7acaa6c031593a36eef6c72806ff309\");\n      v.emplace_back(\n          \"f7f4d328ba108b7b1de4443e889a985ed52f485f3ca4e0c246aa5526590cbed3\"\n          \"44e9f4fe53e4eea0e761c82324649206ca8c2b45152157d4115e68c818644b03\"\n          \"b65bb47ad79f94d37cb03c1d953b74c2b8adfa0e1c418bda9c518ddcd7050e0f\"\n          \"149044740a2b16479413b63fc13c36144f80c73687513dca761ba8642a8ae0\");\n      v.emplace_back(\n          \"2d7dc80c19a1d12d5fe3963569547a5d1d3e821e6f06c5d5e2c09401f946c9f7\"\n          \"e13cd019f2f9a878b62dd850453b6294b99ccaa068e542993524b0f63832d48e\"\n          \"865be31e8ec1ee103c718340c904b32efb69170b67f038d50a3252794b1b4076\"\n          \"c0620621ab3d91215d55ffea99f23d54e161a90d8d4902fda5931d9f6a27146a\");\n      v.emplace_back(\n          \"77dff4c7ad30c954338c4b23639dae4b275086cbe654d401a2343528065e4c9f\"\n          \"1f2eca22aa025d49ca823e76fdbb35df78b1e5075ff2c82b680bca385c6d57f7\"\n          \"ea7d1030bb392527b25dd73e9eeff97bea397cf3b9dda0c817a9c870ed12c006\"\n          \"cc054968c64000e0da874e9b7d7d621b0679866912243ea096c7b38a1344e98f\"\n          \"74\");\n      v.emplace_back(\n          \"83bed0d556798f2b419f7056e6d3ffada06e939b95a688d0ec8c6ac5ea45ab73\"\n          \"a4cf01043e0a170766e21395f27ab4b78c435f5f0dfe6e93ab80df38610e4115\"\n          \"8429ddf20296f53a06a017723359fe22dc08b5da33f0800a4fe50118e8d7eab2\"\n          \"f83a85cd764bf8a166903bd0e9dcfeeceba44ff4ca4439846458d31ea2bb5646\"\n          \"45d1\");\n      v.emplace_back(\n          \"ea12cf5a113543e39504123036f15a5bafa9c555562469f99cd29996a4dfaaab\"\n          \"2a34b00557ccf15f37fc0cc1b3be427e725f2cd952e50af7970dda9200cd5ce2\"\n          \"52b1f29c40067fea3027ed686190803b59d834179d1b8f5b55abe55ad174b2a1\"\n          \"188f7753ec0ae2fc01316e7d498b68ee3598a0e9baaaa664a60f7fb4f90edbed\"\n          \"494ad7\");\n      v.emplace_back(\n          \"55266358332d8d9e68bd13432088beadf95833aab67a0eb3b10650414255f299\"\n          \"e2670c3e1a5b2976159a46c72a7ce57d59b7be14c15798e09ed50fa312a431b0\"\n          \"264d7a1396aa6168bde897e208ece53d2cfc83786113b1e6eac5e9bb98984abb\"\n          \"6c8d64eebb991903254abc650c999bb9958a5d7937434b869bc940e21b9dc1cc\"\n          \"8982f2ba\");\n      v.emplace_back(\n          \"4d6104ded730aefe02873f4c741232c8234a6d66d85393aff57fbf56ba634766\"\n          \"6988dfc4d58f3cc895a0da598822edeee4533d24ec0ee292fd5e1ad04898ffbc\"\n          \"1ff4bef14dec220babcb0f28fffe32a6e2c28aaaac16442bf4feb02917d18bb3\"\n          \"a415d84fa9358d5a9852688d846c92271911f934181c30f82434d915f93f155a\"\n          \"1ffbf0b125\");\n      v.emplace_back(\n          \"eb5f579a4c476af554aac11e5719d378549497e613b35a929d6f36bb8831d7a4\"\n          \"66aa76de9be24ebb55543f1c13924f64cfd648a5b3fa90387315c16174dbf1e9\"\n          \"a183c196d9bb8f84af65f1f8212429aadc11ef2426d07d4716062b85c8d5d2df\"\n          \"f8e21b9e62b7fa7dbd57d72633054b464fb28583a56ca13ccc5ddc74dae94249\"\n          \"2f31731e7046\");\n      v.emplace_back(\n          \"ebddec3dcaf18063e45a76ebeac39af85a1adc2818881ccce48c106288f59883\"\n          \"65cca2b4b1d7f037322da46840f42bebdcbc7193838d426e101087d8cea03aaf\"\n          \"f743d573eb4f4e9a71a2c884390769a6503874125d194bee8d46a3a0d5e4fcf2\"\n          \"8ff8465887d8e9df771d70157e75df3642b331d2778ceb32ceba868640171ab7\"\n          \"a5d22eede1ee44\");\n      v.emplace_back(\n          \"26d87ec70b57691e3bb359633d3ddba17f029d62cdfe977f5fd42274d79b444a\"\n          \"32494d1c01e9f72d03cce78c806df96e93ea78da3a054209924ed765edc4d570\"\n          \"f66168dc25ee3114e4017e387440349c8f0a94804761c3055f88e4fda2a49b86\"\n          \"0b1486a9609095f6250f268b6a4d1aecc03a505632ebf0b9dc22d0755a736faf\"\n          \"7ad7000858b5864b\");\n      v.emplace_back(\n          \"3880f5cc2d08fa70ef44b1f263fcf534d062a298c1bd5ee2eee8c3265806c4ce\"\n          \"50b004f3a1fc1fa5b024aaac7f528c023c8181f67c6e1c357425dc4d573bd46b\"\n          \"93a542afa3a19bdb140a2ce666e1a01f5c4d2dcd681fa9f5839b797813c39473\"\n          \"8d5ee4971386c12c7c117d17c7bec324b760aa30cda9ab2aa850284ba6fa9794\"\n          \"6f710f02449d1883c6\");\n      v.emplace_back(\n          \"3317d2f452105dd3f4a96f9257af8285a80be58066b50f6f54bd633749b49f6a\"\n          \"b9d57d45652d2ae852a2f6940cd5ec3159dd7f333358b12f502325df38843508\"\n          \"faf7e246352d201280babd90b14fbf7722641c3601d0e458474439973c611bb5\"\n          \"502fd0eb3078f87124ca7e1a016fcb6cfeff65f6a565985aca7122cfa8c5a11d\"\n          \"a0cb47797c5132333179\");\n      v.emplace_back(\n          \"f2c5c955d0224e784a46b9125f8fef8a5e1271e145eb08bbbd07ca8e1cfc848c\"\n          \"ef14fa3b36221ac62006403dbb7f7d77958ccc54a8566c837858b809f3e310ac\"\n          \"e8ca682515bc655d2a397cab238a663b464d511f02dc5d033dad4cb5e0e519e9\"\n          \"4a54b62a3896e460ec70e5716b5921bf8396aa86a60123e6287e34570bb01bdc\"\n          \"602e113670bf498af2ff10\");\n      v.emplace_back(\n          \"180e275205691a83630cf4b0c7b80e6df8fad6ef1c23ba8013d2f09aef7abade\"\n          \"1827f23af230de90676240b4b3b0673f8afdea0327330055041741f65560d903\"\n          \"48de696d34ca80dfe8afae582fe4879d4594b80e9408fb53e800e01ca58552b9\"\n          \"05c365e7f1416e51c080f517d6bbd30e64ae1535d59decdc76c6624d737868f4\"\n          \"9f2f719da39ba1344d59eab9\");\n      v.emplace_back(\n          \"c517a84e4631a7f65ace170d1e5c2fdb259841535d88da323e68c0883e6af7b0\"\n          \"41cfe05908815a5a9d1b14fa712c2c16fadcf1ca54d3aa954d411240df331b2a\"\n          \"ebdfb65aced84d0b8aace56ec0aa7c13ec7d75ca883b6bcf6db74c9e98463c48\"\n          \"4a8262684f29910373430651f90ecffe18b072170e61ee58de20e2a6ff67b3ab\"\n          \"00fccbb80af943f20b56b98107\");\n      v.emplace_back(\n          \"d1a56a5ee990e02b84b5862fde62f69ec07567be2d7ccb769a461c4989d11fdd\"\n          \"a6c945d942fb8b2da795ed97e43a5b7dbdde7f8fd2ff7154544336d5c50fb738\"\n          \"0341e660d4898c7fbc39b2b782f28defac6873523c7c1de8e52c65e4395c686b\"\n          \"a483c35a220b0416d46357a063fa4c33fa9c52d5c207a1304ae141c791e62ba6\"\n          \"a7374ed922b8dd94079b72b69302\");\n      v.emplace_back(\n          \"4720b88d6bfb1ab43958e26827730d852d9ec30173ebd0fe0d273edcece2e788\"\n          \"558984cd9306fe5978086a5cb6d37975755d2a3daeb16f99a8a11544b8247a8b\"\n          \"7ed5587afc5bea1daf85dcea5703c5905cf56ae7cc76408ccabb8fcc25cacc5f\"\n          \"f456db3f62fa559c45b9c71505eb5073df1f10fc4c9060843f0cd68bbb4e8edf\"\n          \"b48d0fd81d9c21e53b28a2aae4f7ba\");\n      v.emplace_back(\n          \"f4639b511db9e092823d47d2947efacbaae0e5b912dec3b284d2350b9262f3a5\"\n          \"1796a0cd9f8bc5a65879d6578ec24a060e293100c2e12ad82d5b2a0e9d229658\"\n          \"58030e7cdf2ab3562bfa8ac084c6e8237aa22f54b94c4e92d69f22169ced6c85\"\n          \"a293f5e16bfc326153bf629cdd6393675c6627cd949cd367eef02e0f54779f4d\"\n          \"5210197698e4754a5fe490a3a7521c1c\");\n      v.emplace_back(\n          \"3d9e7a860a718565e3670c29079ce80e381969fea91017cfd5952e0d8a4a79bb\"\n          \"08e2cd1e26161f30ee03a24891d1bfa8c212861b51618d07429fb48000ff87ef\"\n          \"09c6fca526567777e9c076d58a642d5c521b1caa5fb0fb3a4b8982dc14a44473\"\n          \"2b72b239b8f01fc8ba8ee86b3013b5d3e98a92b2aeaecd4879fca5d5e9e0bd88\"\n          \"0dbfffa6f96f94f3998812aac6a714f331\");\n      v.emplace_back(\n          \"4d9bf551d7fd531e7482e2ec875c0651b0bcc6caa738f7497befd11e67ae0e03\"\n          \"6c9d7ae4301cc3c7906f0d0e1ed4738753f414f9b3cd9b8a71176e325c4c74ce\"\n          \"020680ecbfb146889597f5b40487e93f974cd866817fb9fb24c7c7c16177e6e1\"\n          \"20bfe349e83aa82ba40e59e917565788658a2b254f25cf99bc65070b3794cea2\"\n          \"259eb10e42bb54852cba3110baa773dcd70c\");\n      v.emplace_back(\n          \"b91f65ab5bc059bfa5b43b6ebae243b1c46826f3da061338b5af02b2da76bb5e\"\n          \"bad2b426de3c3134a633499c7c36a120369727cb48a0c6cbab0acecdda137057\"\n          \"159aa117a5d687c4286868f561a272e0c18966b2fec3e55d75abea818ce2d339\"\n          \"e26adc005c2658493fe06271ad0cc33fcb25065e6a2a286af45a518aee5e2532\"\n          \"f81ec9256f93ff2d0d41c9b9a2efdb1a2af899\");\n      v.emplace_back(\n          \"736f6e387acb9acbee026a6080f8a9eb8dbb5d7c54ac7053ce75dd184b2cb7b9\"\n          \"42e22a3497419ddb3a04cf9e4eb9340a1a6f9474c06ee1dcfc8513979fee1fc4\"\n          \"768087617fd424f4d65f54782c787a1d2de6efc81534343e855f20b3f3589027\"\n          \"a5436201eee747d45b9b8375e4294d72ab6a52e04dfbb2914db92ee58f134b02\"\n          \"6527ed52d4f794459e02a43a17b0d51ea69bd7f3\");\n      v.emplace_back(\n          \"9242d3eb31d26d923b99d66954cfade94f25a18912e6356810b63b971ae74bb5\"\n          \"3bc58b3c01424208ea1e0b1499936daea27e63d904f9ed65fdf69de40780a302\"\n          \"7b2e89d94bdf214f585472613ce328f628f4f0d56217dfb53db5f7a07f54c8d7\"\n          \"1db16e27de7cdb8d23988837b49b65c12f1771d979e8b192c9f4a16b8d9fba91\"\n          \"7bcf74ce5a82aac2075608ba6c2d485fa59864b9de\");\n      v.emplace_back(\n          \"5da68704f4b592d41f08aca08f62d85e2e2466e5f3be010315d11d113db674c4\"\n          \"b98764a509a2f5aacc7ae72c9deff2bcc42810b47f64d429b35745b9efff0b18\"\n          \"c58653461e968aaa3c2c7fc455bc5771a8f10cd184be831040df767201ab8d32\"\n          \"cb9a58c89afbebecb524502c9b940c1b838f8361bbcde90d272715017f67609e\"\n          \"a39b20fac985332d82daaa023999e3f8bfa5f3758bb8\");\n      v.emplace_back(\n          \"71ea2af9c8ac2e5ae44a176662882e01027ca3cdb41ec2c6785606a07d7231cd\"\n          \"4a2bded7155c2feef3d44d8fd42afa73265cef826f6e03aa761c5c51d5b1f129\"\n          \"ddc27503ff50d9c2d748322df4b13dd5cdc7d46381528ab22b79b0049011e4d2\"\n          \"e57fe2735e0d58d8d56e92c75dbeac8c76c4239d7f3f24fb56697593b3e4afa6\"\n          \"671d5bbc96c079a1c154fe20212ade67b05d49ceaa7a84\");\n      v.emplace_back(\n          \"1d133170582fa4bff59a21953ebbc01bc202d43cd79c083d1f5c02fa15a43a0f\"\n          \"519e36acb710bdabac880f04bc003800641c2487930de9c03c0e0deb347fa815\"\n          \"efca0a38c6c5de694db698743bc955581f6a945deec4ae988ef7cdf40498b777\"\n          \"96ddea3fae0ea844891ab751c7ee20917c5a4af53cd4ebd82170078f41ada279\"\n          \"5e6eea17593fa90cbf5290a1095e299fc7f507f360f187cd\");\n      v.emplace_back(\n          \"5ec4ac45d48fc15c72471d795066bdf8e99a483d5fdd599511b9cdc408de7c06\"\n          \"16491b73924d0266da34a495331a935c4b8884f57d7ad8cce4cbe586875aa524\"\n          \"82215ed39d7626cce55d50349c7767981c8bd6890f132a196184247343566fc9\"\n          \"72b86fe3c5369d6a6519e9f07942f0522b77ad01c751dcf7defe31e471a0ec00\"\n          \"963765dd8518144a3b8c3c978ad108056516a25dbe3092e73c\");\n      v.emplace_back(\n          \"0d5e74b78290c689f2b3cfea45fc9b6a84c822639cd438a7f05c07c374adced4\"\n          \"2cdc12d2a9233a4ffe80307efc1ac13cb04300e165f8d90dd01c0ea955e76573\"\n          \"32c6e86ad6b43e78ba4c13c675aed83192d8427866fb6484e6a3071b2369a46f\"\n          \"ba9005f31232da7ffec7952f831aaaddf63e225263531c2cf387f8cc14fa856c\"\n          \"8795137142c3a52ffa69b8e30ebc88ce3bbc227597bcc8dddd89\");\n      v.emplace_back(\n          \"a0fe36f983259921dc2fa7d89002b3066241d63bfc2448caf7e10522a35562be\"\n          \"0bfedc3dce49cfce2e614a04d4c64cfc0ab898873a7fc26928dc1927c009d12f\"\n          \"6f9b7a278205d3d0057604f4ac746f8b9287c3bc6b929832bf253b6586192ac4\"\n          \"3fdd29ba585dbd9059aab9c6ff6000a7867c67fec1457b733f6b620881166b8f\"\n          \"ed92bc8d84f0426002e7be7fcd6ee0abf3755e2babfe5636ca0b37\");\n      v.emplace_back(\n          \"1d29b6d8eca793bb801becf90b7d7de215b17618ec32340da4bac707cdbb58b9\"\n          \"51d5036ec02e105d83b5960e2a72002d19b7fa8e1128cc7c5049ed1f76b82a59\"\n          \"eac6ed09e56eb73d9ade38a6739f0e07155afa6ec0d9f5cf13c4b30f5f9a465b\"\n          \"162a9c3ba04b5a0b3363c2a63f13f2a3b57c590ec6aa7f64f4dcf7f1582d0ca1\"\n          \"57eb3b3e53b20e306b1f24e9bda87397d413f01b453ceffeca1fb1e7\");\n      v.emplace_back(\n          \"6a2860c110cd0fc5a19bcaafcd30762ee10242d34739638e716bd89fd537ea4d\"\n          \"c630e6f85d1bd88a25ad3892ca554c232c9830bd56980c9f08d378d28f7fa6fa\"\n          \"7df4fcbf6ad98b1adfff3ec1f63310e50f920c99a5200b8e64c2c2ca249399a1\"\n          \"49942261f737d5d72da949e914c024d57c4b639cb89990fed2b38a37e5bcd24d\"\n          \"17ca12dfcd36ce04691fd03c32f6ed5de2a2191ed7c826375ba81f78d0\");\n      v.emplace_back(\n          \"7132aa291ddc9210c60dbe7eb3c19f9053f2dd74742cf57fdc5df98312adbf47\"\n          \"10a73245de4a0c3b24e21ab8b466a77ae29d15500d5142555ef3088cbccbe685\"\n          \"ed9119a10755148f0b9f0dbcf02b2b9bcadc8517c88346ea4e78285e9cbab122\"\n          \"f824cc18faf53b742a87c008bb6aa47eed8e1c8709b8c2b9adb4cc4f07fb423e\"\n          \"5830a8e503ab4f7945a2a02ab0a019b65d4fd71dc364d07bdc6e637990e3\");\n      v.emplace_back(\n          \"3e664da330f2c6007bff0d5101d88288aaacd3c07913c09e871cce16e55a39fd\"\n          \"e1ce4db6b8379977c46cce08983ca686778afe0a77a41baf447854b9aa286c39\"\n          \"8c2b83c95a127b053101b6799c1638e5efd67273b2618df6ec0b96d8d040e8c1\"\n          \"ee01a99b9b5c8fe63fea2f749e6c90d31f6fae4e1469ac09884c4fe1a8539acb\"\n          \"313f42c941224a0e79c059e18affc2bcb6724975c436f7bf949ebdd8aef51c\");\n      v.emplace_back(\n          \"7a6ea63a271eb49470f5ce77519ed61ae9b2f1be07a96855726bc3df1d0723af\"\n          \"3a703fdfc2e739c9d31d25814daf661a23558b50982e66ee37ad880f5c8f11c8\"\n          \"130fac8a5d0250583700d5a324894fae6d61993f6bf9327214f8674649f355b2\"\n          \"3fd634940b2c467973a839e659169c773119919f5b81ee171edb2e5f6940d755\"\n          \"1f9e5a70625d9ea88711ad0ed8ab2da720ad358bef954456cb2d5636425717c2\");\n      v.emplace_back(\n          \"c5106bbda114168c449172e49590c7eeb827fa4e1a2a7a87a3c1f721a9047d0c\"\n          \"0a50fbf244731be1b7eb1a2ef30f5ae846a9f38f0df44f32af61b68dbdcd0226\"\n          \"e741dfb6ef81a2503691af5e4b3171f48c59ba4ef91eba344b5b697f261df7bb\"\n          \"bb734ca6e6daebaa4a179feb17002823281b8534d55a6531c59305f6e3fd3fa6\"\n          \"3b747bcf0deb654c392a02fe687a269effb1238f38bcaea6b208b221c45fe7fb\"\n          \"e7\");\n      v.emplace_back(\n          \"597716a5ebeebc4bf524c15518816f0b5dcda39cc833c3d66b6368ce39f3fd02\"\n          \"ceba8d12072bfe6137c68d3acd50c849873150928b320b4fbc31c1456679ea1d\"\n          \"0acaeeabf666d1f1bad3e6b9312c5cbdecf9b799d3e30b0316bed5f41245107b\"\n          \"693366accc8b2bcef2a6be54209ffabc0bb6f93377abdcd57d1b25a89e046f16\"\n          \"d8fd00f99d1c0cd247aafa72234386ae484510c084ee609f08aad32a005a0a57\"\n          \"10cb\");\n      v.emplace_back(\n          \"0771ffe789f4135704b6970b617bae41666bc9a6939d47bd04282e140d5a861c\"\n          \"44cf05e0aa57190f5b02e298f1431265a365d29e3127d6fccd86ec0df600e26b\"\n          \"cdda2d8f487d2e4b38fbb20f1667591f9b5730930788f2691b9ee1564829d1ad\"\n          \"a15fffc53e785e0c5e5dd11705a5a71e390ca66f4a592785be188fefe89b4bd0\"\n          \"85b2024b22a210cb7f4a71c2ad215f082ec63746c7367c22aedb5601f513d9f1\"\n          \"ffc1f3\");\n      v.emplace_back(\n          \"be6556c94313739c115895a7bad2b620c0708e24f0390daa55521c31d2c6782a\"\n          \"cf41156271238885c367a57c72b4fe999c160e804ad58d8e565edbce14a2dd90\"\n          \"e443eb80626b3eab9d7ab75d6f8a062d7ca89b7af8eb292c98eaf87ad1dfd0db\"\n          \"103d1bb6188bd7e7a63502153cf3ce23d43b60c5782602bac8ad92fb2324f5a7\"\n          \"9453898c5de18415639ecc5c7974d3077f76fc1df5b956723bb19a624d7ea3ec\"\n          \"13ba3d86\");\n      v.emplace_back(\n          \"4bc33729f14cd2f1dc2ff459abee8f6860dda1062845e4adab78b53c835d106b\"\n          \"dfa35dd9e77219eaef403d4e80488ca6bd1c93dd76ef9d543fbb7c8904dccc5f\"\n          \"71509a6214f73d0f4e467c3e038ea639b29e7fc442ee29f57117740576188ada\"\n          \"15a739827c647a46b0271817ab235c023c30c90f2115e5c90cd8501e7b286962\"\n          \"fc66ffc3fe7e8978746168314908a41998bd83a1eeffda9d714b864f4d490fde\"\n          \"b9c7a6edfa\");\n      v.emplace_back(\n          \"ab12faea205b3d3a803cf6cb32b9698c32301a1e7f7c6c23a20174c95e98b7c3\"\n          \"cfe93fffb3c970face8f5751312a261741141b948d777b8a2ea286fe69fc8ac8\"\n          \"4d34116a4674bb09a1a0b6af90a748e511749de4697908f4acb22be08e96ebc5\"\n          \"8ab1690acf73914286c198a2b57f1dd70ea8a52325d3045b8bdfe9a097925215\"\n          \"26b7564a2a5fcd01e291f1f8894017ce7d3e8a5dba15332fb410fcfc8d62195a\"\n          \"48a9e7c86fc4\");\n      v.emplace_back(\n          \"7d421e59a567af70594757a49809a9c22e07fe14061090b9a041875bb77933de\"\n          \"ae36c823a9b47044fa0599187c75426b6b5ed94982ab1af7882d9e952eca399e\"\n          \"e80a8903c4bc8ebe7a0fb035b6b26a2a013536e57fa9c94b16f8c2753c9dd79f\"\n          \"b568f638966b06da81ce87cd77ac0793b7a36c45b8687c995bf4414d28289dbe\"\n          \"e977e77bf05d931b4feaa359a397ca41be529910077c8d498e0e8fb06e8e660c\"\n          \"c6ebf07b77a02f\");\n      v.emplace_back(\n          \"0c18ab727725d62fd3a2714b7185c09faca130438eff1675b38beca7f93a6962\"\n          \"d7b98cb300ea33067a2035cdd694348784aa2eda2f16c731eca119a050d3b3ce\"\n          \"7d5c0fd6c234354a1da98c0642451922f670984d035f8c6f35031d6188bbeb31\"\n          \"a95e99e21b26f6eb5e2af3c7f8eea426357b3b5f83e0029f4c4732bca366c9aa\"\n          \"625748297f039327c276cd8d9c9bf692a47af098aa50ca97b99961bef8bc2a7a\"\n          \"802e0b8cfdb84319\");\n      v.emplace_back(\n          \"92d5909d18a8b2b9971cd1627b461e98a74ba377186a6a9df5bd133635250b30\"\n          \"0abccb2254cacb775df6d99f7c7d0952653c28e6909b9f9a45adce691f7adc1a\"\n          \"fffcd9b06e49f775364cc2c62825b9c1a86089080e26b57e732aac98d80d009b\"\n          \"fe50df01b95205aa07ed8ec5c873da3b92d00d53af825aa64b3c634c5ece40bf\"\n          \"f152c331222d3453fd92e0ca17cef19ecb96a6eed4961b627aca48b12fecd091\"\n          \"754f770d52ba861546\");\n      v.emplace_back(\n          \"802f22e4a388e874927fef24c797408254e03910bab5bf372320207f8067f2b1\"\n          \"ea543917d4a27df89f5bf936ba12e04302bde23119533d0976beca9e20cc16b4\"\n          \"dbf17a2ddc44b66aba76c61ad59d5e90de02a88327ead0a8b75463a1a68e307a\"\n          \"6e2e53ecc1986274b9ee80bc9f3140671d5285bc5fb57b281042a8978a117590\"\n          \"0c6073fd7bd740122956602c1aa773dd2896674d0a6beab24454b107f7c847ac\"\n          \"b31a0d332b4dfc5e3f2f\");\n      v.emplace_back(\n          \"3844fe65db11c92fb90bf15e2e0cd216b5b5be91604baf3b84a0ca480e41ecfa\"\n          \"ca3709b32f8c6e8761406a635b88eec91e075c48799a16ca08f295d9766d7447\"\n          \"5c47f3f2a274eae8a6ee1d191a7f37ee413a4bf42cad52acd5564a651715ae42\"\n          \"ac2cddd52f819c692ecdef52ecb763270322cdca7bd5aef71428fa73e844568b\"\n          \"96b43c89bf1ed42a0abf209ffad0eeec286c6f141e8af073ba4adfbbdeda2537\"\n          \"52ae36c9957dfc905b4c49\");\n      v.emplace_back(\n          \"329377f7bf3c8d74991a7d61b0cf39baff5d485d79751b0d5ad017d23bec570f\"\n          \"b19810105bab79ab5acb102ab972165224d4ec888ec7de5148077fa9c1bb6820\"\n          \"e0d91ae4e2591a21fec2f820606ce4bafc1e377f8dc3a5bd1a9e2772a57abccd\"\n          \"0b757164d768872c91d02789545ab5b203f688d71dd08522a3fd2f5bcd7df507\"\n          \"aebf1ca27ddff0a82afb7aa9c180008f49d1325adf97d047e77238fc75f56356\"\n          \"de4e87d8c961575c9f6362c9\");\n      v.emplace_back(\n          \"f7f269929b0d71ea8eef7120e55ccba691c582dd534692abef35c0fe9dec7dae\"\n          \"973cd9702e5ad420d278fe0e653fdcb22fdcb63148109ec7e94f2d0750b28157\"\n          \"dd1764376ae10fdb0a4aef3b304bd82793e0595f941226a2d72abbc929f53134\"\n          \"dc495b0d65ced409914f94c2523f3dfbbdeeac84ae247ab5d1b9ea33dce1a808\"\n          \"885a55be1f3683b46f4be73d9b62eec2585f690056858dfc427aabf591cd2767\"\n          \"24885bcd4c00b93bb51fb7484d\");\n      v.emplace_back(\n          \"ac022309aa2c4d7fb628255b8b7fb4c3e3ae64b1cb65e0de711a6def1653d95d\"\n          \"8088871cb8905fe8ae76423604988a8f77589f3f776dc1e4b30dbe9dd262b218\"\n          \"7db02518a132d219bd1a06ebac13132b5164b6c420b37dd2ccee7d69b3b7fa12\"\n          \"e54f0a53b853d490a68379ea1fa2d79762830ffb71bf86aab506b51f85c4b6a4\"\n          \"1b69325c7d0c7aa85b93b7144489d213e8f33dbb879fce22849865337b620b15\"\n          \"5cb2d2d36a68832889e30194d36d\");\n      v.emplace_back(\n          \"d009c2b78a8f02e5e5dbb586ef71fc324b375092e15913ca1a5bfd22d516baad\"\n          \"b96867bee3562e77c4a4852344a1a76c30728be5e22400b4cc41711f66754c24\"\n          \"6a520498d8c24f0205b9c873748dbeb67fe1ad099ad04cf89f4b517f0aa48113\"\n          \"6d9f6de2d727df01c6aa4099da59d4382b51e25fd47c33d9842c32b62331e507\"\n          \"94bfe8b61b3ba9de1b8b704779c6d65edff3af00f121ab4a7ea384edabe47c6d\"\n          \"0098a48991f387ca4444135ec59d46\");\n      v.emplace_back(\n          \"c00bab36cce69899817d1425016d222d7303197ed3e3fdcac744705e7f178a1a\"\n          \"c745968900f69299163e19b3161f3e0a4cc55aa2e4e71e0ee6ac427d1f4d14e0\"\n          \"63f68d303ddfbb18118335cfa7a6a90d99c38319ee76f7a884846a9e0b68030b\"\n          \"f28e78bfbd56359b9368842814da42b04cb0e307d5d846dc22f049147bae31b9\"\n          \"a956d17676a8cc348dafa3cabc2007a30e730e3894dddf9999fb8819086311f0\"\n          \"703e141613ed6dcd7af8510e2dc435b0\");\n      v.emplace_back(\n          \"c9789152a9fc29698d49ed95f09bd11b75f18a8c5615a73dbe54ae5e550027fd\"\n          \"0ae6a8b60667040c1b12de3d1ee3f6bf061c78c951a3210effc912e19f482dd4\"\n          \"de152063c588c44903bc11761706fd935afa040df085b08144d83d0dde32b46a\"\n          \"b52f4fae98ac116c7ff11d7f553450c2e37b9c5f0b1dd9e0b8640a24cba6f2a5\"\n          \"246c41f197f46e3dc8a29131c79bef3351c6e277a0a34442274d546ccd058891\"\n          \"277473d668420f121750d19cd684267405\");\n      v.emplace_back(\n          \"06a15a0731ce52557e368bcbaa11ef3399299e36fb9f2eda6e5726907c1d29c5\"\n          \"c6fc581405ba48c7e2e522206a8f128d7c1c939d1132a00bd7d6366aa82724e9\"\n          \"68964eb2e373563f607dfa649590dcf5589114df69da5547fef8d1604cc4c6de\"\n          \"1ed5783c8746918a4dd31168d6bc8784cd0c769206bd803d6ca8557b66748770\"\n          \"402b075ef44b38157d4c0da7c6281725a2065d087b1f7b23455fa673bdeeba45\"\n          \"b983311c44eabe9ef4b7bde3420ae9881863\");\n      v.emplace_back(\n          \"d08aacef2d7a41aec09473bd8a44f628e15addb7b9e5b77a1e09c8ab4942f379\"\n          \"a0bfcb324d580b774666f18ae78dd36710824ff12393f059068fe4b559c53662\"\n          \"c2b0e6c69e23785c8f32554e837ec1714bee902e60737b639dd933af4f68cb9d\"\n          \"7de77e1f3b28e5b122891afce62b79acd5b1ab4ba411662cc77d806449e69c5a\"\n          \"45a143b742d98ac84a0826d68433b9b700ace6cd472ba2d58a90847f42ce9c43\"\n          \"f38ffc017db4bf40450b2eee1f4594dc740c0f\");\n      v.emplace_back(\n          \"6a6058b0a498b7ea76a93c646eb9b8629f0cba4a0c726420c5f67ba9b0412cad\"\n          \"e356abdf0a4fb94384bad32ce0d5dd9e23dcaae1d6f28ff8683616b30f139289\"\n          \"0c67b3a2c04b360893b801f127e527e4da82e239f4c878da13f4a4f1c76db071\"\n          \"90e77ec123995168102fb274434a2d1e12913b9b5cbab4aacaad2bd89d88b3ca\"\n          \"2b8e60dacf7c22c9379097ff60880f552e320ca3b571994f52534470feee2b39\"\n          \"e0dadb5cd88257a3e459a4cc6f12f17b8d54e1bb\");\n      v.emplace_back(\n          \"adeced01fc5671531cbb45679f5ddd42b3a95151677b6125aaf6f5e8f82fbaba\"\n          \"a5ecf7c3552c2458587224f0042870f178f5fca5465250e75d71352e652eeed2\"\n          \"3cdb7f915f5ebb44099b6db116ca1be45530ac8ed32b7f161d60ed4397ad3d7d\"\n          \"649ae6bf75ca5bec891d8e595605be9764f3a03965e1fe0eaffbf212e3df4f0f\"\n          \"a35e08ff9d0091e6d4ac4748edfe43b611085a6ffec163014655fdd839fd9e81\"\n          \"b63b1fa8cae4ec335ec343289758e389a79ceedfae\");\n      v.emplace_back(\n          \"d014592f3a83ba40af366f137c674724916c3cdd3f6cf9d4c5c7c8d6d51ebf26\"\n          \"e315e2c12b3546be56fb52382904046ecbd2f5b883aa4ff473de6f0c26ab862c\"\n          \"3fa34bf3d880cc1911ce39a4088c6617c179dc5faf68a2c488bbde12d67b50f7\"\n          \"3abcfab0e3b062e68c95363e11f5f1de8ec36ed01ea21442518089045df67d34\"\n          \"6135283ad5b3fff80cf57f20876849f6db9fa139728358415a90610f69ec720f\"\n          \"c92d8234e3e122551e9df2c644c4a2c4e3734d07de8e\");\n      v.emplace_back(\n          \"c0d0c37838873ba8757d6e41b409605043bc1635edcd731219587676d94217e9\"\n          \"f0ab44b71de25000661ce7303b7015f45e6eaa7b7ebef92b8f4a34c902c908d2\"\n          \"172185505fa33aca5a41be83079316cdfdd430fc2c45f505f85d867e6d516f7e\"\n          \"1bf19c001d9f43018968aab65ec031b3801399231c83ec9e622dab5629922a6b\"\n          \"424cab938c135ff7310501c2c02971bfd2f577e25904d1a618baf0859f77f4e8\"\n          \"b1d0cde9544e95ec52ff710c0672fdb3d891feeea2b017\");\n      v.emplace_back(\n          \"7022e7f00902219ba97baa0e940e8ac7727f58955aa068c29680fac4a16bcd81\"\n          \"2c03eeb5adbcfe867a7f7c6b5d89f4641adb9173b76a1a8438866f9b4f640ce2\"\n          \"aedf5f1080c890bcf515b4be4e3e512352f1e5323c62ec46cb73f3d71be8235f\"\n          \"ee55a154763f7c3f9aeb61ffd28f4cd93d3310f608e2133586bf1ab3f102de96\"\n          \"f64c68a4668de8acb2a76a7ce0cddddc8fa3df5e9d230823da16ed9ebb402d36\"\n          \"e38e6e018795e5a71517ecab5f9ca472b9ced8ff69d2d195\");\n      v.emplace_back(\n          \"acaf4baf3681ab865ab9abfae41697141ead9d5e98523c2e0e1eeb6373dd1540\"\n          \"5242a3393611e19b693cabaa4e45ac866cc66663a6e898dc73095a4132d43fb7\"\n          \"8ff7166724f06562fc6c546c78f2d5087467fcfb780478ec871ac38d9516c2f6\"\n          \"2bdb66c00218747e959b24f1f1795fafe39ee4109a1f84e3f82e96436a3f8e2c\"\n          \"74ef1a665b0daaa459c7a80757b52c905e2fb4e30c4a3f882e87bce35d70e292\"\n          \"5a1671205c28c89886a49e045e31434abaab4a7aed077ff22c\");\n      v.emplace_back(\n          \"84cb6ec8a2da4f6c3b15edf77f9af9e44e13d67acc17b24bd4c7a33980f37050\"\n          \"c0301ba3aa15ad92efe842cd3ebd3636cf945bb1f199fe0682037b9dacf86f16\"\n          \"2dadabfa625239c37f8b8db9901df0e618ff56fa62a57499f7ba83baebc085ea\"\n          \"f3dda850835520344a67e09419368d81012168e5de5ea45158397af9a5c6a165\"\n          \"7b26f319b66f816cd2c28996547d697e8df2bb163ccb9dda4d6691dffd102a13\"\n          \"667ab9cde60ffbfb872187d9c425a7f67c1d9fffff9276ed0aeb\");\n      v.emplace_back(\n          \"6a52c9bbbba454c14540b2be58230d78ecbeb391646a0c6fcce2f789086a7836\"\n          \"4b81ae85d5396d7cfa8b46bda41e3083ec5cf7b4c47dc601c8a697df52f557de\"\n          \"fca248506dbebab25657f5a561d09625b7f4b2f0119a12beeac087efc9d350a7\"\n          \"35c35d2431c1da7dda99befb17f41a3dc4da0f00bb95366be128538ce27763d8\"\n          \"1f832fe3c1d4efc07b5b08ad8dc9e65fb5e48546664e18cb2d3bb3fe1f56fa7a\"\n          \"ae718c5e3bbdeaf70e15023f6a25b72a2d177fcfd04211d40664fe\");\n      v.emplace_back(\n          \"c3c4d3b31f1f5f9538923df3478c84fffaef411520a542da9a220ee4132eabb9\"\n          \"d718b5076fb2f985485e8ba058330aed27ddfd3afa3db34aa60301088caec3d0\"\n          \"053828c0c2bc87e2e61db5ea5a29f62fdad9c8b5fc5063ec4ee865e5b2e35fac\"\n          \"0c7a835d5f57a1b1079833c25fc38fcb14311c54f8a3bd251bca19342d69e578\"\n          \"5f9c2e43cf189d421c76c8e8db925d70fa0fae5ee3a28c4047c23a2b8a167ce5\"\n          \"3f35ced33bec822b88b06f41558c47d4fed1bfa3e21eb060df4d8ba1\");\n      v.emplace_back(\n          \"8d55e92136992ba23856c1aea109766fc44772477efc932b3194af2265e433ed\"\n          \"77d63b44d2a1cff2e8680eff120a430fe012f0f09c6201d546e13ad46fc4ce91\"\n          \"0eab27bb1569879abed2d9c37fae9f1267c2216ec5debcb20d4de58461a621e6\"\n          \"ce8946899de81c0add44d35e27b7982a97f2a5e6314901caebe41dbba35f48bc\"\n          \"9244ca6dca2bdde7306435892f287036df088633a070c2e385815ab3e2bfc1a4\"\n          \"7c05a5b9fe0e80dd6e38e4713a70c8f82bd32475eea8400c7bc67f59cf\");\n      v.emplace_back(\n          \"5016284e20362610fa05ca9d789cad25f6d43263787e7e085476764ce4a8908c\"\n          \"e99b262b375e9d106170b1bec1f473d5e777e0c1896533040e39c8c1465e0790\"\n          \"7ef5860e14e4d8310013e35f12090e0bfc687474b1f15f3dd2033a0edac52461\"\n          \"02da4deec7e188c3517d84d9c2a0a4497a4c5f82a30f1ba009e45ee6eb3ab436\"\n          \"8c720ea6feee428ffd2c4cc52debb8d634a64176572c72368f94a66689f23f8a\"\n          \"01218f532117af5a8060d140e7ca435a92882fcb5630ebe14a4805f1dc83\");\n      v.emplace_back(\n          \"05456ec59b8d41bbd736727976b96b38c43827f9e16169be673ff37870c2ecd5\"\n          \"f0d1ea1a136be4cc7b047a02a4421d484fd2a12ece418e42ee391a13a0b1df5a\"\n          \"0162b29ab70d3fe3e04ba6ab26b37d62b7cf05a5e2f033611bf970b8e1f30e19\"\n          \"8e483e740fa9618c1e8677e07b61296b94a9787a68fba622d7653b5568f4a862\"\n          \"8025939b0f74389ea8fced6098c065bf2a869fd8e07d705eadb53006be2abb71\"\n          \"6a3114ceb0236d7e916f037cb954cf977720855d12be76d900ca124a2a66bb\");\n      v.emplace_back(\n          \"eb6f60b83fcee77060ff346aaf6ec34d82a8af469947d3b5074cde8eb26566eb\"\n          \"1fa039bcc707738df1e95869bd827c246e88436f0614d9834ead5392ef376105\"\n          \"c4a9f370071cdeaaff6ca0f18b74c3a48d19a717253c49bd9009ccbfdd5728a0\"\n          \"8b7d112a2ed8dbafbbb46d7a75dc9a05e09bfde1a0a92d74a51887f9d123d789\"\n          \"6e9f9d0057b660ed7d55454c069d3c5260411db4cdc67e7b74f680d7ac4b9dcc\"\n          \"2f8baf72e15e6b3cafebcdf449a6436ed2c398b675f79c644747c57553bf7ea2\");\n      v.emplace_back(\n          \"187a88e88514f6c4157c1ba40b442baae1ae563a6c989277443b12a219aa484c\"\n          \"b9fa8adbb9a29d429f50155321b15664926317477079c7060dfdaa84c1d74bba\"\n          \"78892c34e6f21ad35208d2ae622012401696bff5cd57b6485944b3db7b9071fa\"\n          \"5f57fbfb1085d91bb9cff5808d662cdc6c8157249478262c44b7fbc397ed42a4\"\n          \"977b202e817717bfccc9f0467294062313f7705251ed09573f16d23429361fad\"\n          \"a259dfb300369c4198f07341b38e84d02cdb74af5de6aab1fc2026208ea7c418\"\n          \"c0\");\n      v.emplace_back(\n          \"be31bc96606d0fab007e5caeded2f1c9f747c759777e9b6eef962bed49e45a1d\"\n          \"4fc993e279d024915e600865ecb087b960584be18c41114d3c43f92169b9e0e1\"\n          \"f85a0ebcd4e196376ccdc920e66103cd3b1c58407d0aafd0e003c4e341a1dadd\"\n          \"b9f4faba974362a32f35db83384b05ae8e3322d728893861afd8b1c940de5a17\"\n          \"f691e763ce4969b6d94f67fb4a0235d100225bd8602f291388f0ca4a568748ad\"\n          \"0d6040f1262eac2aede6cd27419bb78a394c1ffad72c262be8c3f9d9619d633e\"\n          \"51d0\");\n      v.emplace_back(\n          \"4d83d85ca838b4518588f2a90228a4dd18f14dd5b4c012d26298a97d848abbd8\"\n          \"25d221d02cceb6e8c701b4ad00e1dee4889b5c533e4bb60f1f41a4a61ee5478b\"\n          \"e2c1b1016c30345afd7a5253668260515e70751f22c8b4022d7fe4877d7bbce9\"\n          \"0b46531507dd3e89549e7fd58ea28f4cb23d33662bd003c1345ba94cc4b06867\"\n          \"f778957901a8c441bee0f3b12e16463a51f7e50690356971dd73a686a49fda1e\"\n          \"ae46c9d54fba262811d698025d0ee053f1c58591c3bb3cbde69de0b31549ef5b\"\n          \"69cf10\");\n      v.emplace_back(\n          \"cdeb07d36dc5f9a1cd717a9e9cca37a2ce93caa298eee63571f7d6c5fde2a11c\"\n          \"666cf53cf2dcb41ca2ea2319e7230ca68e38c647905928713a13982bf47fe33d\"\n          \"7095ebd50b2df976208920a43eb2e29b942f32467403c45cea18bf44e0f6aeb1\"\n          \"55b48a8e5c471fec972a9d62f7ae093d2758f0aaec7ca50cb4725bfa219f1a3a\"\n          \"46ad6bde7361f445f86b94d66b8ece080e56c510250693a5d0ea0ae87b442186\"\n          \"0b853bcf0381eae4f1bf7c5c0472a93ad18407bc88475ab8560d344a921d3e86\"\n          \"a02da397\");\n      v.emplace_back(\n          \"a598fad52852c5d51ae3b10528fc1f722e21d44fbd42ae5acdf20e85a28532e6\"\n          \"46a223d27fd907bfd38eb8bb75175636892f8242877aab89e8c0824d368f3339\"\n          \"ce7a82aa4e5af6db1f3b588a4d667a00f67bee37cfd2724dde06d2909fb9e58d\"\n          \"892f4cfd2c4ca85acdf8256f5458b030a6bda151154ff2e6d7a8da90b54a2884\"\n          \"c8a99fab5a4ac211ff23dc0975f4f592fd1b6b9dc7783bdcd2d4ca4e68d2902f\"\n          \"2013e122cb62e2bff6b0a98ec55ba25837e21f1cfe67739b568d43e6413dab2b\"\n          \"d1dc471e5a\");\n      v.emplace_back(\n          \"17b68c74c9fe4926e8102070916a4e381b9fe25f5973c9bd4b04ce25749fc189\"\n          \"31f37a65a356d3f5e5a1ef125d546f4f0ea797c15fb2efea6fbfcc5739c56469\"\n          \"3d47adeb12dcb3d98a2830719b13247792cb2491dca159a28138c6cff925aca4\"\n          \"2f4fdb02e73fbd508ec49b25c60703a7595a3e8f44b155b371d525e48e7e5dc8\"\n          \"4ac7b17c52bf5e526a67e7187234a2f19f57c548c70fc0b27183df73ffa53fa5\"\n          \"8b658034c896fa791ae9a7fd2620f5e46ce84c842a6e60e9324ae4db224ffc87\"\n          \"d9617cb85ca2\");\n      v.emplace_back(\n          \"b9e4267ea39e1de1fed0579f93bb351007c9f8fcdd811053fae33f09e2753d74\"\n          \"28f04e1a9efcd45ea701a5d87a35b3afb2e6b65365dee6ead0bbb611b7797b21\"\n          \"2ac688653f542e604a39df277f12514ddfee3b4e27b98395c2cd97a203f1f115\"\n          \"3c50327965770802ec2c9783edc428271762b275471e7ac65ac36523df28b0d7\"\n          \"e6e6ccc7674268a132a63411fc82c0738dbb68af003b769a0bf9e6587b36476c\"\n          \"b465350fee13f88ea355d47ffac7b0f964f4139db11b7642cb8d75fe1bc74d85\"\n          \"9b6d9e884f75ac\");\n      v.emplace_back(\n          \"8ca704fe7208fe5f9c23110c0b3b4eee0ef632cae82bda68d8db2436ad409aa0\"\n          \"5cf159223586e1e6d8bdae9f316ea786809fbe7fe81ec61c61552d3a83cd6bea\"\n          \"f652d1263862664df6aae321d0323440430f400f291c3efbe5d5c690b0cc6b0b\"\n          \"f871b3933befb40bc870e2ee1ebb68025a2dcc11b68daadef6be29b5f21e4403\"\n          \"74301bde1e80dcfade4c9d681480e65ec494a6af48df232c3d51447b9d06be71\"\n          \"4949249c44c43cf73ed13ef0d533e770284e51369d94ae241a5fb2f163893071\"\n          \"b2b4c118aeaf9eae\");\n      v.emplace_back(\n          \"4fd8dd01012bb4df82bf42e0683f998e6f52dd9c5617bae33f867d6c0b69798c\"\n          \"ead8179346d70acc941abbbdd26e3229d5651361d2252c72ff22db2938d06ff6\"\n          \"fc29a42fdf800ae967d06479bc7bbb8e71f40b1190a4b7189ffc9a7096cdb76d\"\n          \"40aec424e1388e1eb7ef4ac3b34f3f089da8fda7d1927f5d775c0b2801d22dd1\"\n          \"265c973158f640cec93edfed06dc80b20ef8c496b98289d54d46ccd205951cbb\"\n          \"0f4e7daeb866b60bacb483411e4382b6f04d472843186bd0e31fbaa93e5c901e\"\n          \"c028efafeb45fc551a\");\n      v.emplace_back(\n          \"e9ee1b22b04b321a5fdd8301627011f583887d77560fb0f35552e207561f81e3\"\n          \"8ac58a0d0aeaf832d1ee72d913720d01f75574e9a321864fe95f4d0d8f0b8db9\"\n          \"7649a53e71e940aede5c40b4b9105daa42a6fb2811b61209247534cbaf830b07\"\n          \"abe338d75d2f5f4eb1c3cf151e9edabe2c8d5f6fff08fac1495ef48160b100d3\"\n          \"0dcb0676700bcceb28723a29980ab0766a93abb8cb3d1963007db8458ed99b68\"\n          \"9d2a7c28c788743c80e8c1239b20982c81dadd0eed6740c65fbc4ef15c7b5569\"\n          \"cb9fc997c6550a34b3b2\");\n      v.emplace_back(\n          \"ec01e3a60964360f7f23ab0b22e021815765ad706f242265ebc19a2bb9e4eac9\"\n          \"4393952dcf61aae47682671a10f9165f0b20adf83a6706bfbdcf04c6faba6114\"\n          \"653a35584267267873291c6fe7ff5f7695243143421509502c8875aafa9e9afe\"\n          \"5be5ef2c851c7f35d69be5d3896000ccdbbfab5c238bb34d607cfe2d55d74888\"\n          \"0545b4aa7ca61137992925189025c62654b1f20d49c3ccd75aa73ce99cd7258d\"\n          \"abedd6480a9f5185531fc0118beb68cc0a9cd182f6973287cf9252e12be5b619\"\n          \"f15c25b65c71b7a316ebfd\");\n      v.emplace_back(\n          \"db51a2f84704b78414093aa93708ec5e78573595c6e3a16c9e15744fa0f98ec7\"\n          \"8a1b3ed1e16f9717c01f6cab1bff0d56367ffc516c2e33261074935e0735ccf0\"\n          \"d018744b4d28450f9a4db0dcf7ff504d3183aa967f76a507357948da9018fc38\"\n          \"f150db53e2df6cea14466f03792f8bc11bdb5266dd6d508cde9e12ff04305c02\"\n          \"95de29de19d491ad86e766774bb517e7e65befb1c5e2c267f013e235d8483e17\"\n          \"7214f89978b4cdc81aa7eff8b39f2825ad3a1b6ac1424e30edd49b067d770f16\"\n          \"e74dd7a9c3af2ad74289a676\");\n      v.emplace_back(\n          \"00e40f30ae3746edad0f5dd03d0e640933cf3d1694804c1e1ed6399ac36611d4\"\n          \"05196ee48f129344a8512feda16a354517871322bd5d9c6a1b592933eab53192\"\n          \"3efb393ffb23d9109cbe1075cebfa5fb917b40df028a621460ff6783c798792c\"\n          \"b1d9635b5a6f84ec13918fa302924649b5c7fcb1f7007f0d2f06e9cfd7c27491\"\n          \"e565a96c68a0c3644f92cd8f38857258c33801c5d537a83dfe583cba59d7eec7\"\n          \"e394199c0a2660a62fabe3ed2099d57f315a6cd8de1a4ade29d977f15d65759c\"\n          \"ff433e5ac0c182aef3761163e1\");\n      v.emplace_back(\n          \"3c5ea24d0d9b618294a263f062b2414a722be4eb10dfc346a6ec3b821d7396eb\"\n          \"a61cd6ef33618b04cd087a811f299d4606820227f16000d7c839062b96d3e3f5\"\n          \"9cd1a082448d13fc8f56b3fa7fb5f66d0350aa3b72dd7c165d590282f7da2e12\"\n          \"cfe9e60e1796122bb8c2d40fdc2997af634b9c6b127a893dfb3467909378300d\"\n          \"b3da911be1d7b616bb8e0572433e65527e15d936500a2c60e9f9909dcf22ab5e\"\n          \"4b6700f0238c205b4a813626fac3d945bab2637fb08203044a73d20c9a3fcf7c\"\n          \"3fc4eb7807c3276dd5f73ce89597\");\n      v.emplace_back(\n          \"9271aeeebfac46f4de85df78f1bfd36136aa8905e15835c9e1941176f71e3aa5\"\n          \"b1b131843d40479735e23e182a2bd71f66f6149dccb7ed8c16469079dc8590bb\"\n          \"f165374951785f4531f7e7361de62f936cfb23a2b5bdf186632e7042a0dd451f\"\n          \"dc9b7208f923f3a5f250ae590ec348c63a16c3aacaf7379f53b5dd4152dcd40d\"\n          \"23e683e2156e64c592ffc07e2cd6bbeebef4dd590b2f6b2bcbf08fcd111c079f\"\n          \"5c4033adb6c17574f8756ecd87be27eff1d7c8e8d0324438d59ae171d5a17128\"\n          \"fbcb5533d921bd044a2038a5046b33\");\n      v.emplace_back(\n          \"4e3e533d5bcb15793d1b9d0468aaee801f32fdb486b11027183553a09ddbee82\"\n          \"13924296f2815dc61577297459e834bf1c7a53f87d43782209e589b8295219ba\"\n          \"7073a8fff18ad647fdb474fa39e1faa69911bf83438d5f64fe52f38ce6a991f2\"\n          \"5812c8f548de7bf2fdea7e9b4782beb4011d3567184c817521a2ba0ebad75b89\"\n          \"2f7f8e35d68b099827a1b08a84ec5e8125651d6f260295684d0ab1011a9209d2\"\n          \"bdeb75128bf5364774d7df91e0746b7b08bda9185035f4f226e7d0a1946fcaa9\"\n          \"c607a66b185d8546aac2800e85b74e67\");\n      v.emplace_back(\n          \"b5d89fa2d94531093365d1259cc6fe8827fea48e6374c8b9a8c4d2209c280fa5\"\n          \"c44958a1847222a692a59e6aa2696e6cdc8a543dd89b0ce03bc293b4e78d6ef4\"\n          \"8e1839694ccd5c65661143095c705b07e3ced84a0f5959114dd89deb956ab3fa\"\n          \"c8130eb4a878278205b801ae41a29e34146192308c4e759b374757b0c3b00319\"\n          \"bce92a1b95a4d2ee179fd6714ff96155d26f693a5bc973f84ac8b3b91e392627\"\n          \"6297532d98b46992a3f104c08100bf1671c43134bac280c617da711e90a01001\"\n          \"37525375ebb12802a428885ae7fce6514a\");\n      v.emplace_back(\n          \"40e3d8048fc10650cb8a7fc2e7113e26dec34f9ca2d5129cd10a8e8e44d113d6\"\n          \"1ee48c7d003e19fd307fc6debd70feb30243f298c510ccc4418355ce143066f0\"\n          \"67ad7c6de7288c3080e7ad46a23c8d34deb55a43e652fe90444ad3c57d3ec1e1\"\n          \"c489d63ef915a24bc74a7925a0a7b1e1523f21ca8fee78df24e3d0a68d001342\"\n          \"3db97c280799a0618229c0f2c167289a891e5c8d6661ab21285951c31710e3b5\"\n          \"fe55f6347fe16d9b40507948a59252efeb616df83e5c098b07d0a7247cd371da\"\n          \"ff0e50491c582503fd89f79ba94d6af9ed76\");\n      v.emplace_back(\n          \"1fa444de01dd3901e2b4684e3d7a799ffa02d85afd35fb30fe4c9d672837bee6\"\n          \"dd8a3b8608b4bb5e589220ad5a854f46b46e41c6d57ad124a46beab4169ff69f\"\n          \"ee7e3838a6165e19dad8eb5d7bf53d4edd3cd2769daf219510a02fdd2afe0c0e\"\n          \"1da3cd30fcd1aa88b68965586f07a25a1720fbd90a096ea30fc8e945e3637d78\"\n          \"57c8a9c0ab4154ffb2000e57b5f9adfa4e4eaf8065bc3c2b2e75f49596332558\"\n          \"8785a6ce417dcddffd299873b15dcccca128d63cd4eeeadb64cda28099a9ad7c\"\n          \"80d34844901f26b88b00b9aafeb2f90286d29d\");\n      v.emplace_back(\n          \"fde0a0d9d813983bd1f55cf778a003a2023b34a555322ab280584537bc6bdd84\"\n          \"4d22a7d6066c18da83ec09f3d8d5a1aab4be0d5ce19b436052f6e259a4b49017\"\n          \"a1f47f1fe2bf115d5bc8599fb216351c60dd6b1bedb2e6f4dcadf424b833501b\"\n          \"6f099cbfad9e2290680fb69c25032b42a6274f7cb9b5c5950401354838a45f7c\"\n          \"b77b95bf54718e2f3d3d9fb91eb2311903980277396398d9736d8e92fd838594\"\n          \"ac8a537c6c529db5a8a4f89290e6ba6f20ac0e5ed6fef40901d0e0e8e3e50299\"\n          \"0811f9acaae555dd54eb1bcd96b513e2fe751bec\");\n      v.emplace_back(\n          \"9f8e0caec87858599f5ab29bff86da78a841a918a023a111098687ecdf274761\"\n          \"2d3f3809d9ca400b878bd4f92c43a1004f1c17c7f19a3cd1ce449bd2b23aff55\"\n          \"1623c37dd8c0be56bf3fd857b500c2b9f9ccea62481944090a3cf3b6ee81d9af\"\n          \"8eeb60f65ef150f9fa4d3ed6ce4762d3d4f174ee8ccd460c25cafac0ea5ec8a6\"\n          \"a4b2f9e8c0520cb7061155e532cb65f188b01e4b9086db951f504b060c296b32\"\n          \"6b3fc1c590498ecce594f828f4a10ea416675720ae505295d38a791bd0e93f42\"\n          \"8448a8f4c1fc0af53604a9e8255384d29ae5c334e2\");\n      v.emplace_back(\n          \"33d1e683a4c97ee6bbaa5f9df1a88cb53b7f3c157b6045d70a56fda0ccbd3a1f\"\n          \"a1f049cd564da072b53f415bf5fb843771c1d2551fd075d33377362b2f7c0645\"\n          \"f9723123d11975991db8a2b518f02e2c7c30342a044754290bae2c77496d755e\"\n          \"5981f12e6b0a0174280b958bf11ed628a9062775993ced04bf752ea8d165e3ac\"\n          \"2177d7cd1b9371c44efa98f0b3e68602a839d384eec007979f46429dafb138cb\"\n          \"c231ad928a9f65f7d66fac77416395e8f1debaaf76ec2e4e03e8674102cd26f6\"\n          \"14739f3ec9f949033df1fb97e87c2326d65aef94ed5f\");\n      v.emplace_back(\n          \"180048f09d0b480887af7fd548a85abf605440c1ddde6afe4c30c30670233f7b\"\n          \"f928f43b4681f59279ebbda5e8f8f2a1abefdee129e18ac60f9224e90b38b0aa\"\n          \"bd01308e0a27f41b6fb2ee07ee176ec9048c5fe33c3f7c791469c81f30e28170\"\n          \"585b9f3e7e3c8c2e9d74370cb4518f13bf2dee048cbd98ffa32d85e43bcc64a6\"\n          \"26b40efb51ce712925fdd6fee006dc68b88004a81549d2121986dd1966084cd6\"\n          \"54a7c6686b3bae32afbd9625e09344e85cf9611ea08dfce835a2e5b3726e69ae\"\n          \"8a76a97db60fcc539944ba4b1e8449e4d9802ae99fae86\");\n      v.emplace_back(\n          \"13c0bc2f5eb887cd90eae426143764cf82b3545998c386007cca871890912217\"\n          \"aa143ac4ed4ddb5a7495b704aa4de18419b8664b15bc26cfc6596a4d2ae408f9\"\n          \"8b47a566476d5802d594ba84c2f538def9d016661f6404bb2337a3932a24f6e3\"\n          \"0073a6c9c274b940c62c727242e24466084a3ea336365d71ea8fa6499c0ea8d5\"\n          \"9eea505f1126b99c795023c4963aa0d99323d0391e8701110edf551b2d3799e1\"\n          \"063ca443f1add162156e445502ca1a052fe70c289838593b58839fc63de128a0\"\n          \"3e2bbf389e22ae0cf957fd03315ee407b096cc1cfd92dee6\");\n      v.emplace_back(\n          \"6f1eb607d679efef065df08987a1174aab41bdac8aece7726dfa65805d6fff5b\"\n          \"3d17a672d96b770dc32165f144f0f7324822a5c87563b7cd9e37a742ae83ef24\"\n          \"5d09006d91576f435a03476f509ea2936636232f66aa7f6cdf1ac187bbd1fcb8\"\n          \"e20f8791866e60ed96c73374c12ac16795e999b891c64507d2dbd97e5fc29fac\"\n          \"750ad27f2937cbcd29fdafccf27ab22453834d475f6186eaf975a36fad5c8bd6\"\n          \"1c21da554e1ded46c4c39765dcf5c8f5ccfb49b6a4dc562c919d0c7d8940ec53\"\n          \"6ab2448ec3c9a9c8b0e8fd4870cad9de2577c7b0c38563f355\");\n      v.emplace_back(\n          \"dcdd993c94d3acbc555f464871a32c5da6f13b3d5bbc3e34429705e8ad2e7639\"\n          \"3fdd96a69a94acb652f5dc3c120d41187e9aa919669f727c4868013b0cb6acc1\"\n          \"65c1b7706c52248e15c3bf81eb6c147619467945c7c48fa14a73e7c3d5bec917\"\n          \"06c567145342a026c9d97eff97ec672c5debb9df1a998083b0b0081d65c517b3\"\n          \"e5634c95e347e781aa30ca1c8af815e2e494d844e847fdcb41622894a518dc36\"\n          \"571123a40bfdbe8c4f4cff44d83c61dd9dcd24c464c53b395edb31efee9f3aa0\"\n          \"80e87cdc3d22d613ae84a53c9249c32c96f9a3bc4629bb126a70\");\n      v.emplace_back(\n          \"49971f9823e63c3a72574d977953329e813b22a8387cd13f56d8ea77a5d1a8a2\"\n          \"0012632d1d8732bbcb9f756b9675aab5db927beacab7ca263e5718b8dfa7b2ee\"\n          \"d9a91bf5ed163b16139d45f7b8cc7e3f7bdda6202106f67dfb23b7c315ee3e17\"\n          \"a09d466b1e6b13e7c7428184a979f5358667b4fa8bd40bcc8ea46058db44587a\"\n          \"85377ac46bf155136c09ac58cb6c27f28e17028c91e7e8f74d5b500e56293b31\"\n          \"6974f02b9d9ea205d9b6ac4cfb74eb8eb0c944577fd2f41316368307beab3e32\"\n          \"7bf7dbaa0a4428836ec4e895dea635234abeaf113ceeadac33c7a3\");\n      v.emplace_back(\n          \"c57a9cc958cee983599b04fe694f15fb470fcbc53e4bfcc00a27351b12d5d243\"\n          \"4444253ad4184e87b81b738922ffd7ff1dc1e54f39c5518b49fb8fe50d63e393\"\n          \"5f99e4bd125e8dc0ba8a17fd62de709339a43fabe15cf86d96a54010112170c3\"\n          \"40cfac4132182eed7301402bc7c8276089dec38488af145cb6222525894658f0\"\n          \"3501204b7a66aba0be1b557b28a2f652d66f7313ed825ecc4d8596c1be7420d4\"\n          \"425b86a1a90a5b7f30d0f24e0d1aae0eb619ca457a71699e44be612a4011c597\"\n          \"ee80b94d5507e429d7fc6af22579cd6ad642723b05ef169fade526fb\");\n      v.emplace_back(\n          \"0568a672cd1ecbaa947045b712e2ac27995392fbef8f9488f79803cbee561c21\"\n          \"2287f080eca95adb5ba42739d78e3ba667f06045d87850d3a0499358649caa25\"\n          \"7ad29f1a9c511e7054db20554d15cbb55ff854afa45cae475c729cea72ede953\"\n          \"522031865bc02b95589ed4d9841c552a8cc94904a93ed09ed77222f6c1781950\"\n          \"56be59bc4e96a815adf534e6b466fb47e262ff79c803c157a21b6e2269c2e0ab\"\n          \"eb494113cd868d8466e82d4b2f6a28b73645853d96bc9242515d803e33294848\"\n          \"d3fe42fdff68da53c03491636beede47ff1399dd3d54a5e914d55d7adf\");\n      v.emplace_back(\n          \"3f19f61a4cd085796731ac9f85a75a8bce77031932c31762d87d8b8d07b8bd19\"\n          \"ff78d6b7d1bd1e87f3a4f41aad03b6c4d17a6cbc86be55f7c8b88ada047bb04f\"\n          \"8d49f1c34bcf81cc0f3389ad01a758fc7eeb0072aa9ad1481992bfdde82e438e\"\n          \"75590a4423832dfbe3756e2229ea873bc3606e6d72174cb2163bf40b5d49c810\"\n          \"09dab85ecc03e311351bbf96e32c030a2b276a7698cb25bc2c967acb3213161a\"\n          \"1fdde7d912cd6a804490f8056c47da1333f6e35c41e749c2c23919cb9af5eec5\"\n          \"652e6e072b034fb1682e9aaa194a9c0bd456ea0b008d14dbce37967a7a8e\");\n      v.emplace_back(\n          \"705f98f632d99d3651793825c38dc4deda56c59eac539da6a0159c83131cf8ab\"\n          \"6f2ee0c3b74111fde351f7aa1a8c500a0cecab17c212d2c58ca09eae608c8eef\"\n          \"c922b9902ef8d6832f799ba48c3c28aa702b3242107edeba01daafe424406a38\"\n          \"22965056cfe8783455a671e93b1e2eae2321364f1871471c82124df33bc09e1b\"\n          \"52882bd7e1c4c7d0b2f3dd4a28c2a002a43246768af0700f9659de99d62167be\"\n          \"93177aabf19d678e79e9c726ac510d94e74873eda99620a3961930cd91937c88\"\n          \"a06d8153d64fd60da7ca38cf26d1d4f04a0df273f52127c53fdc593f0f8df9\");\n      v.emplace_back(\n          \"ea6f8e977c954657b45f25480ff42c36c7a10c77caa26eb1c907062e24fbca5a\"\n          \"ebc65cacca0de10abea8c78322f08672e13d8ac16996eca1aa17402eaea4c1cc\"\n          \"6c800b22dc18cb8d620192d74bac02c07b5cfa61e513c7f28b7e29b9700e0e44\"\n          \"2720bf4c669d4995da19d19f841d9eb68cc74153592591e3bf059ef616b95305\"\n          \"aa453b32fe99a91afb35bd482cf2b7aa42702837a53be3c38883d2963020e347\"\n          \"556f841254ec6b85854485fe8c520b05f2ea67a9bf3981555c20991e2bacd4db\"\n          \"5b418228b6002d8d41c025cb472bf5443aaa885974a408ea7f2e3f932c600deb\");\n      v.emplace_back(\n          \"408190134ed06556811b1af808ab2d986aff152a28de2c41a2207c0ccc18125a\"\n          \"c20f48384de89ea7c80cda1da14e60cc1599943646b4c0082bbcda2d9fa55a13\"\n          \"e9df2934edf15eb4fd41f25fa3dd706ab6de522ed351b106321e494e7a27d5f7\"\n          \"caf44ec6fadf1122d227eefc0f57aefc140d2c63d07dcbfd65790b1099745ed0\"\n          \"42cfd1548242076b98e616b76ff0d53db5179df8dd62c06a36a8b9e95a671e2a\"\n          \"9b9dd3fb187a31ae5828d218ec5851913e0b52e2532bd4bf9e7b349f32de2b6d\"\n          \"5d3cdf9f372d49617b6220c93c05962327e99a0480488443349f0fd54c1860f7\"\n          \"c8\");\n      v.emplace_back(\n          \"5f9e5c6f38573a85010a9d84d33f29c057003b2645e3ea6f72cbc7af95d197ce\"\n          \"6a06b13fea81722853e6991791b8b15091cd066f5ed913592ed3d3af5370d39b\"\n          \"a22beeb2a582a414b16824b77e194a094c2afdcc09aa73ce36f4943cca5ae32c\"\n          \"5017dc398801dd92a47382d9327c9f6cffd38ca4167cd836f7855fc5ff048d8e\"\n          \"fba378cdde224905a0425e6b1de061fc951c5e624a5153b008ad41160a710b3f\"\n          \"f2081748d5e02deb9f841f4fc6cf4a15153dd4fe874fd447482696283e79ee0e\"\n          \"6bc8c1c0409baa5ab02c5209c319e3169b2476149c0c6e541c6197ca46e004ee\"\n          \"f533\");\n      v.emplace_back(\n          \"218c6b3508aec69574f2b5039b30b942b72a8349d05f48ff945bbbe5c8957d5a\"\n          \"6199492a6bf54bab821c9377e2edfa4c908384664d2c80112d5e805d66e0a551\"\n          \"b941021be17dd20bd825bea9a3b6afb1b8c605805b3bda58750f03ea5c953a69\"\n          \"8494b425d8980c69f34d1c3f6b5866e8717031152a127215c256e08873c21b0f\"\n          \"5cc85875d0f7c94601659150c04cd5fe5d381ba29983a2d94fcd3a65a94c53c7\"\n          \"279cd000dddd4253d8cff8d7f6ace10247fe3bc30d63ba4bb54f557b3d22a392\"\n          \"4369430d71ab37b701e9500bda70b5a643704858beed4726a889b6c9c9158419\"\n          \"4c68f1\");\n      v.emplace_back(\n          \"dac26aa7273fc25d6e044c79fc2bfa46e59892a42bbca59a86826c91e76ab03e\"\n          \"4bd9f7c0b5f08d1931d88b36ea77d94f7ba67cd4f1d3086e529427201119096a\"\n          \"e066ae6f170940830ed7900de7bb9d66e09788287403a4ecc93c6da975d2fb08\"\n          \"e918840a236c15f5d3a8f7375c2eeebbf6f01a6e7f29ca2b8d42df158414c320\"\n          \"777433663c59fdcd1f39ca68e3473db721be7ce8c6dba5fddc024f94fedb286b\"\n          \"0477581d451313ca8c737484daf60d67f9b2d56d4bcc271f7e9ae958c7f258ef\"\n          \"bc74d25753e0516f28282461941bf2dcc7dd8c7df6173b89760cefcac0719024\"\n          \"3ff863fb\");\n      v.emplace_back(\n          \"c46e6512e6797cc7a54254a1b26b2de29aa83d6c4b1ea5a2786fbcec38827062\"\n          \"5b12635eae39e1fba013f8a65219421bca8b52a8ddfd431cda60299bdf160734\"\n          \"d5a7450ec79620058522702174ae451b9bfa7c4a455fbbee3e1d048c7d4bac51\"\n          \"31018228f137c8e130440c7059b4f15eaa34ce872a851a16ce86f982df78a00b\"\n          \"e4d564da2003a450ddee9ab43ea876b8b4b65c84f0b39265fd5456417afb5bc5\"\n          \"4997c986e66fc222f2123ba5e719c4d6b9a177b188277df384f1125821cf19d5\"\n          \"248cef0be183ccdc84ac194506f740ed2188b2689ea4c9236a9e9e3a2fff85b6\"\n          \"af4e9b49a3\");\n      v.emplace_back(\n          \"1ccd4d278d67b65cf2564ecd4de1b55fe07adc80e1f735fe2f08ea53fd397732\"\n          \"3689122c29c798957abaff6aba09bdcbf661d77f4dc8913ab1fe2bef38846166\"\n          \"e3834785e7105d746484eff8c656af5d8c7854abc1c62b7fadb65521dc6f793d\"\n          \"978bda9838eb3800417d32e8a24d8c8cb1d18a5de6ca79d9e1b0ff9aa25e6218\"\n          \"fe944cf18666fecc1e31334b390260dbe0997539e1b02f6366b2aea4f4a21efe\"\n          \"04f4b97568fcb39e59919d5ebac6543d5d0f48fc66b923c34aac377dc95c2032\"\n          \"9b837b6ed5e8d9a3d2089cd0d8f025658006ff41cbdaccca618822ca590ab155\"\n          \"253f8bc1c7f5\");\n      v.emplace_back(\n          \"9875209588395ee3c9fdd793fd48717cc84c8c3ea622b2ccc4a1be4448e6034b\"\n          \"7810569855255031f10be5ffd714b05f9ce01972d712d40abf03d4d0ce175813\"\n          \"a7a668f761324996093fc2aa5912f7fc2abdadd8775d2b4d9ad4922162933814\"\n          \"60ed8f6db3d641d1525f4242c348bbfe504c704f215dc461de51b5c75c1aae96\"\n          \"7936963848f16c673eca5e78dfd47eb19001d52d1bcf96c98956dad5ddf594a5\"\n          \"da757e7ca35f2f69803b784e66ac5a58b75c228b8266ec592505e5d1ca87d812\"\n          \"25738855f15bc0914677e81593fd409e77d159f8a908f67788de9eb06c556154\"\n          \"7aada96c47c535\");\n      v.emplace_back(\n          \"40c90e375e366f3756d89091eb3eed9fe0fbfc5638700af4617d358812bac531\"\n          \"24a2205dd6756456787d49cd6a35e302479a0992288f47532e4ea7ab62fc5ad5\"\n          \"adc690a5d9a446f7e035ad4641bd8dae83946aee3338ec984ccb5cc633e1409f\"\n          \"2531eeffe05532a8b0062ba99454c9aeabf8ecb94db195af7032bfebc22912f4\"\n          \"9d39330add47ff8fa5720612d697f0b602738930e060a1bb214efc5e292224cf\"\n          \"34e29deaea6b1b1ff847e94ecc997325ac38df61db45d82bf0e74a664d2fe085\"\n          \"c20b04c39e90d6a170b68d2f1d373f00c731c524456ada73d659aaac9df3191a\"\n          \"7a3865083343fc13\");\n      v.emplace_back(\n          \"e8800d82e072210ca6d7fa2472028974780b76aad4bcb9ad362422dd05ae3232\"\n          \"668251d164daa375a43b26a38cce28dbeb3dee1a4a579f70d0fe7febb29b5ece\"\n          \"8aa836e050fb3d188c63aa9c3c0da6c717d86458a6096b5effceb964efdec703\"\n          \"5960c09ccd10dea3c5f1c7f9f478d5887ebbe2e15c5ff85dbacbc444bb951c4e\"\n          \"ec7abecb89ed80187e409e2972ffe1a5f01562af109f2cf09471cf72cf83a3bb\"\n          \"8f4e2ef38ed0e326b698296394e5b2718a5000c01425708e8ad0461e62462d88\"\n          \"19c2377f13ab1be2c7c9f33dc06fe23cad27b87569f2ce2e56e4b2c60c7b1b3d\"\n          \"370841d89ebdc1f192\");\n      v.emplace_back(\n          \"796d6d1447d5b7e8c55cd8b2f8b7010db39f27565f907e3fc0e464ea2d4bb52b\"\n          \"37f10e7c6dcfc59231b9cdee12c32aeb4adbc42b86e86eb6defb5b69e6ca75e1\"\n          \"f4d0dae3e124e5a1b8b6697f7e10b0403f1f0a5ff848eef3752837a9ba17780f\"\n          \"16a9a709188a8d5b89a2fa74adb2e651163b1c2b3d261e225c9158dcd9eb7ac3\"\n          \"d6704cee290cdff6bcb3cb90cee030aa0d19d4693655c3c30ac6fc06d2ae3778\"\n          \"7c47126d57ed9a6bef5f8a6c56859aefc08755739a95aac57a4dd916a92ba9f3\"\n          \"afbf969df8085949615033365c751a9a3e1a18cee98a69d22e64009bebf83071\"\n          \"69b6c61de0617ecfafdf\");\n      v.emplace_back(\n          \"4f9057183566153cf337b07c3f5556006de54c56b2a1e5326c07aaeabd1886ec\"\n          \"6f1641358925db232b2f0dbf75229c796a7395b2f934c1f99090bec1123f3c84\"\n          \"1b1cb3c5b1ec42ed5408f2940f0c48a9470b852c46d6557853d459cecd2c32bb\"\n          \"cd8ee21fa11e385eef0857cba4d8545a61b52a484cdd779db4739fbc7aa9860d\"\n          \"cabe0488b98fa0b60c3f7d6153db279000a52ffb573dab37d2ab1896a90e5deb\"\n          \"7ac6bbe56239085c325d83a917dc6e8a448425b718c2356b9f3066163555ec44\"\n          \"4f372e184e02c8c4c69b1c1c2ae2b51e45b98f73d933d18750968945ca85d6bb\"\n          \"b22014b4c4015262e3c40d\");\n      v.emplace_back(\n          \"79dcca7d8b81a61359e4aece21f3df7b99518ce70bd2f57a18bab5e7114af2ad\"\n          \"d0a0cea7f319d69f231f060e0a539d9a23fb3e95451ce8c6340cfb09edf931df\"\n          \"84203a39226dd9eb278f11b691ef612585b973daab373e65d11325898badf673\"\n          \"2100371fd759960fa8fec373268421d28bffdb9b12a430b92fe4b07566ca0c89\"\n          \"e616e49f8fc75ccd9cdc66db820d7c02e109aa5ed86b89770262918a518f90a2\"\n          \"292f6b68d68ae03992e4259a17a23c84ec2a417f082b5abf3a26e44d2278ecb8\"\n          \"ba9456965303a75f25394d1aaf5544590e74b14d8a4cc4050be2b0ebcfe4d2db\"\n          \"6b12a02c68a3bcdda70301f3\");\n      v.emplace_back(\n          \"848755dc31e25e9a42f9ec12d847d19f292c14c162c9aba49e972cb123b58b8e\"\n          \"57bb263a923929833373858594ff52dbc298dbbc078599194e4c07b0e5fc1e10\"\n          \"808bbacdb6e93c72b333685cf961f28eb0d5a395c63266b01f130d25db384b35\"\n          \"6e5da6d01042fc2359581b89c63b3bb2d1ce897fbc9e83fe85d9666cb60e6a8c\"\n          \"657f70caad5387b8a045bf91095606802c8424ea8ac52ef29386dc46183378a5\"\n          \"fcb2cb927428b8c070f1c42aafd3bc70ca25437807696a46873cfeb7b80ba2eb\"\n          \"c3c4272443d445e46343a1465253a9eebd532a0d1d2c18264b91ff45159f2454\"\n          \"04ae9335f2af55c802772426b4\");\n      v.emplace_back(\n          \"ecaa6e999ef355a0768730edb835db411829a3764f79d764bb5682af6d00f51b\"\n          \"313e017b83fffe2e332cd4a3de0a81d6a52084d5748346a1f81eb9b183ff6d93\"\n          \"d05edc00e938d001c90872dfe234e8dd085f639af168af4a07e18f1c56ca6c7c\"\n          \"1addffc4a70eb4660666dda0321636c3f83479ad3b64e23d749620413a2ecdcc\"\n          \"52ad4e6e63f2b817ce99c15b5d2da3792721d7158297cce65e0c04fe810d7e24\"\n          \"34b969e4c7892b3840623e153576356e9a696fd9e7a801c25de621a7849da3f9\"\n          \"9158d3d09bf039f43c510c8ffb00fa3e9a3c12d2c8062dd25b8dabe53d8581e3\"\n          \"0427e81c3dfc2d455352487e1255\");\n      v.emplace_back(\n          \"23a3fe80e3636313fdf922a1359514d9f31775e1adf24285e8001c04dbce866d\"\n          \"f055edf25b506e18953492a173ba5aa0c1ec758123406a97025ba9b6b7a97eb1\"\n          \"4734424d1a7841ec0eaeba0051d6e9734263bea1af9895a3b8c83d8c854da2ae\"\n          \"7832bdd7c285b73f8113c3821cced38b3656b4e6369a9f8327cd368f04128f1d\"\n          \"78b6b4260f55995277feffa15e34532cd0306c1f47354667c17018ee012a791a\"\n          \"f2dbbc7afc92c388008c601740cccbbe66f1eb06ea657e9d478066c2bd2093ab\"\n          \"62cd94abadc002722f50968e8acf361658fc64f50685a5b1b004888b3b4f64a4\"\n          \"ddb67bec7e4ac64c9ee8deeda896b9\");\n      v.emplace_back(\n          \"758f3567cd992228386a1c01930f7c52a9dcce28fdc1aaa54b0fed97d9a54f1d\"\n          \"f805f31bac12d559e90a2063cd7df8311a148f6904f78c5440f75e49877c0c08\"\n          \"55d59c7f7ee52837e6ef3e54a568a7b38a0d5b896e298c8e46a56d24d8cabda8\"\n          \"aeff85a622a3e7c87483ba921f34156defd185f608e2241224286e38121a162c\"\n          \"2ba7604f68484717196f6628861a948180e8f06c6cc1ec66d032cf8d16da039c\"\n          \"d74277cde31e535bc1692a44046e16881c954af3cd91dc49b443a3680e4bc42a\"\n          \"954a46ebd1368b1398edd7580f935514b15c7fbfa9b40048a35122283af731f5\"\n          \"e460aa85b66e65f49a9d158699bd2870\");\n      v.emplace_back(\n          \"fe511e86971cea2b6af91b2afa898d9b067fa71780790bb409189f5debe719f4\"\n          \"05e16acf7c4306a6e6ac5cd535290efe088943b9e6c5d25bfc508023c1b105d2\"\n          \"0d57252fee8cdbddb4d34a6ec2f72e8d55be55afcafd2e922ab8c31888bec4e8\"\n          \"16d04f0b2cd23df6e04720969c5152b3563c6da37e4608554cc7b8715bc10aba\"\n          \"6a2e3b6fbcd35408df0dd73a9076bfad32b741fcdb0edfb563b3f753508b9b26\"\n          \"f0a91673255f9bcda2b9a120f6bfa0632b6551ca517d846a747b66ebda1b2170\"\n          \"891ece94c19ce8bf682cc94afdf0053fba4e4f0530935c07cdd6f879c999a8c4\"\n          \"328ef6d3e0a37974a230ada83910604337\");\n      v.emplace_back(\n          \"a6024f5b959698c0de45f4f29e1803f99dc8112989c536e5a1337e281bc856ff\"\n          \"721e986de183d7b0ea9eb61166830ae5d6d6bc857dc833ff189b52889b8e2bd3\"\n          \"f35b4937624d9b36dc5f19db44f0772508029784c7dac9568d28609058bc437e\"\n          \"2f79f95b12307d8a8fb042d7fd6ee910a9e8df609ede3283f958ba918a9925a0\"\n          \"b1d0f9f9f232062315f28a52cbd60e71c09d83e0f6600f508f0ae8ad7642c080\"\n          \"ffc618fcd2314e26f67f1529342569f6df37017f7e3b2dac32ad88d56d175ab2\"\n          \"2205ee7e3ee94720d76933a21132e110fefbb0689a3adbaa4c685f43652136d0\"\n          \"9b3a359b5c671e38f11915cb5612db2ae294\");\n      v.emplace_back(\n          \"af6de0e227bd78494acb559ddf34d8a7d55a03912384831be21c38376f39cda8\"\n          \"a864aff7a48aed758f6bdf777779a669068a75ce82a06f6b3325c855ed83daf5\"\n          \"513a078a61f7dc6c1622a633367e5f3a33e765c8ec5d8d54f48494006fdbf892\"\n          \"2063e5340013e312871b7f8f8e5ea439c0d4cb78e2f19dd11f010729b692c65d\"\n          \"d0d347f0ce53de9d849224666ea2f6487f1c6f953e8f9dbfd3d6de291c3e9d04\"\n          \"5e633cfd83c89d2f2327d0b2f31f72ac1604a3db1febc5f22cad081532780472\"\n          \"10cc2894582c251a014c652e3951593e70e52a5d7451be8924b64f85c8247dab\"\n          \"6268d24710b39fc1c07b4ac829fbda34ed79b5\");\n      v.emplace_back(\n          \"d7314e8b1ff82100b8f5870da62b61c31ab37ace9e6a7b6f7d294571523783c1\"\n          \"fdedcbc00dd487dd6f848c34aab493507d07071b5eb59d1a2346068c7f356755\"\n          \"fbde3d2cab67514f8c3a12d6ff9f96a977a9ac9263491bd33122a904da5386b9\"\n          \"43d35a6ba383932df07f259b6b45f69e9b27b4ca124fb3ae143d709853eed866\"\n          \"90bc2754d5f8865c355a44b5279d8eb31cdc00f7407fb5f5b34edc57fc7ace94\"\n          \"3565da2222dc80632ccf42f2f125ceb19714ea964c2e50603c9f8960c3f27c2e\"\n          \"d0e18a559931c4352bd7422109a28c5e145003f55c9b7c664fdc985168868950\"\n          \"396eaf6fefc7b73d815c1aca721d7c67da632925\");\n      v.emplace_back(\n          \"2928b55c0e4d0f5cb4b60af59e9a702e3d616a8cf427c8bb03981fb8c29026d8\"\n          \"f7d89161f36c11654f9a5e8ccb703595a58d671ecdc22c6a784abe363158682b\"\n          \"e4643002a7da5c9d268a30ea9a8d4cc24f562ab59f55c2b43af7dbcecc7e5ebe\"\n          \"7494e82d74145a1e7d442125eb0431c5ea0939b27afa47f8ca97849f341f7076\"\n          \"60c7fbe49b7a0712fbcb6f7562ae2961425f27c7779c7534ecdeb8047ff3cb89\"\n          \"a25159f3e1cefe42f9ef16426241f2c4d62c11d7ac43c4500dfcd184436bb4ef\"\n          \"33260366f875230f26d81613c334dbda4736ba9d1d2966502914ec01bbe72d88\"\n          \"5606ec11da7a2cb01b29d35eebedbb0ecc73ed6c35\");\n      v.emplace_back(\n          \"fd993f50e8a68c7b2c7f87511ce65b93c0aa94dcbdf2c9cca93816f0f3b2ab34\"\n          \"c62c586fc507b4900a34cf9d0517e0fe10a89d154c5419c1f5e38de00e8834fe\"\n          \"3dc1032abdeb10729a81655a69a12856a78ca6e12110580de879b086fd660872\"\n          \"6541cfa9616326bdd36064bc0d1e5f9c93b41278bff6a13b2494b81e238c0c45\"\n          \"aea1b07d855e8f3fe1478e373bd9d3957cf8a5e5b9003386793d994c7c575cff\"\n          \"2322e2428cbbaa4f47560316ae3354a7478842ff7cc5dcbacb6e871e72b36f06\"\n          \"d63a9aaeb9044cfb7974afdc238a5816f537dcf33ee40b4e1a5eb3cff2402b46\"\n          \"d548264e133008d284f11b7e4e450bc3c5ff9f79b9c4\");\n      v.emplace_back(\n          \"8df21892f5fc303b0de4adef1970186db6fe71bb3ea3094922e13afcfabf1d0b\"\n          \"e009f36d6f6310c5f9fda51f1a946507a055b645c296370440e5e83d8e906a2f\"\n          \"b51f2b42de8856a81a4f28a73a8825c68ea08e5e366730bce8047011cb7d6d9b\"\n          \"e8c6f4211308fad21856284d5bc47d199988e0abf5badf8693ceeed0a2d98e8a\"\n          \"e94b7775a42925edb1f697ffbd8e806af23145054a85e071819cca4cd4887529\"\n          \"0ca65e5ee72a9a54ff9f19c10ef4adaf8d04c9a9afcc73853fc128bbebc61f78\"\n          \"702787c966ca6e1b1a0e4dab646acdfcd3c6bf3e5cfbec5ebe3e06c8abaa1de5\"\n          \"6e48421d87c46b5c78030afcafd91f27e7d7c85eb4872b\");\n      v.emplace_back(\n          \"48ec6ec520f8e593d7b3f653eb15553de246723b81a6d0c3221aaa42a37420fb\"\n          \"a98a23796338dff5f845dce6d5a449be5ecc1887356619270461087e08d05fb6\"\n          \"0433a83d7bd00c002b09ea210b428965124b9b27d9105a71c826c1a2491cfd60\"\n          \"e4cfa86c2da0c7100a8dc1c3f2f94b280d54e01e043acf0e966200d9fa8a41da\"\n          \"f3b9382820786c75cadbb8841a1b2be5b6cbeb64878e4a231ae063a99b4e2308\"\n          \"960ef0c8e2a16bb3545cc43bdf171493fb89a84f47e7973dc60cf75aeeca71e0\"\n          \"a7ebe17d161d4fb9fe009941cc438f16a5bae6c99fcad08cac486eb2a48060b0\"\n          \"23d8730bf1d82fe60a2f036e6f52a5bff95f43bbe088933f\");\n      v.emplace_back(\n          \"f4d84ed3e564c102600a795eaa9b1eaf4ad12f1a4deca1d042a0a2750ddf6201\"\n          \"db03073d8bf553cb9dde48a1b0083827a609f7242b86584cc180964ae794b12c\"\n          \"e55661e00e36a6ba4dbc389e6a5a85f1b45df9af7ead1b0a54db56e68639b9d4\"\n          \"38a91504e82c35d40c7bc7e048a53ac0b04accd0dadf4ac9884b0ca0e3cb5ba4\"\n          \"336e3581be4c4760a553823ffa283a1120d4e145af56a59f2533903650f0b9e9\"\n          \"ad9fe2e8a3c3c3dd03a1fcb709032c8835324839c735b0c051d0cbd8b5d86761\"\n          \"7c11023432e4bd275d3d0eb98a0b6cf58071a5b712922f2bc751ac7c2588c447\"\n          \"444cde2f37a8ea5ec126425bf517e0d17c9e2999f52fee14b3\");\n      v.emplace_back(\n          \"2ccea21bac9c2b70d3923309cbf2d7cb7abd1fcc8b8b002688870a80029c6239\"\n          \"7350c3c898194e5deea360bb963d26d485cb7963f8167586976ec0556950b2e8\"\n          \"6135f4a2800991ce8473bfd44a3c5e937a48b5e355ba5141bccf2131a83988d9\"\n          \"d2a9e8e7635a956105b3512c05ef708139ced51d7a4e204c12d8a49a21e8dc6d\"\n          \"e2629a2fd092326885d9f218745fe09f6d91fb6afce250a30a63689534b6be1f\"\n          \"26899ffa3767d835cf586aa47776700f94241bc999b1e3deefe188f37ff734f5\"\n          \"f16ee6a00914323dc7b8a143c9137cdcc5cd08ae9566f04bb2941532674c97df\"\n          \"f6ffa5ce3405ef8e5d27ec403114253dd6394c0167d72a0044c5\");\n      v.emplace_back(\n          \"2b681c6398aee63bf862770341648bbcd31d7de7903c5903fe3d9469311320bb\"\n          \"24d914f2af0cdca199c97214c7c679dc32a2800ba484a03c010ea6be3bb9f2c8\"\n          \"7e30a98b606050b8a3f297f12b8f92caaeceb3e844652115934874e0a1ab093a\"\n          \"73d759b53f6a6c3096940dd22c2bb96ce6820a7b9c6d71a208de9892aa6a7209\"\n          \"b0fff56a0cafea52b952cdd6f5752cff3309d448800b4e4c878aa595595b56b1\"\n          \"2b83fcd6ca89520c7da664e449d7b4438fc455888aad5de0fad9a06eed14afd3\"\n          \"513b5ebbffe01775549b701181bd26370764f56eba52fdb24286ad1ac0f5418a\"\n          \"7c429f7dfc7f3168437fa8eed7a2ed7c723a485e4c3ed14dea2e07\");\n      v.emplace_back(\n          \"aadfd505a89f4aade2c3018258a7e039401b1fc6a7f3d87910dddbb880d372ec\"\n          \"8a13c70d92245de5b8e5f9a285c33b99dc82fa2b22decee72b93a72211656ad7\"\n          \"a52696c8e570f78be28c0e427a371dafde856e8d5ed24f83b0660b51e7fac05d\"\n          \"93a8666dfde6def59af863f80f3e5f6801182c87422203df390dcb736b8f8300\"\n          \"52a8832eeeb0b4e27e732aaf793d166b5a3ec7745aeef3766937c2b75a276bdd\"\n          \"d145f6010c29d035e343e267cb2d828436876ec3a7ebe3b6347d4172f7a99d68\"\n          \"21ce152e039e53deb33340b324c7f068ffb94b3cde35a8eaa12d15c3806a7ad0\"\n          \"acec3e8c7078c1d32a28fd3eec9f32cb86e4c22166ff69e83785e851\");\n      v.emplace_back(\n          \"1605b8cce529a9d6262fd4390d9e4ae5e14e0adc0ec89b028ef68dd0f373ea25\"\n          \"9aaa96f2967091dd0874c0105385e9e6da9ca68297c31afa44ef834535fb302c\"\n          \"e5b4e49edacbbdf359fe1228a8172495b3e57014c27edd58b685110980056c50\"\n          \"c398a64f4923f2d720b4df16d75cb36b4233660694182099c35028a972519c24\"\n          \"764fc94e18e582b24deb3491535fc06b83837c7958522800e822201d694af0bd\"\n          \"0aa3834e17d4b1ba36f470905ae5f8bbeeb6c4c8604d8af02baa347b07086d69\"\n          \"89867ddd5e8e8ed7740c3469bfa2810519c55c6add1332c4c54ee9097961d674\"\n          \"1cb12a09713a0d07645f784f42f5ad94b48b836b34263130b0483f15e3\");\n      v.emplace_back(\n          \"ff9c6125b2f60bfd6c2427b279df070e430075096647599bdc68c531152c58e1\"\n          \"3858b82385d78c856092d6c74106e87ccf51ac7e673936332d9b223444eaa0e7\"\n          \"62ee258d8a733d3a515ec68ed73285e5ca183ae3278b4820b0ab2797feb1e7d8\"\n          \"cc864df585dfb5ebe02a993325a9ad5e2d7d49d3132cf66013898351d044e0fe\"\n          \"908ccdfeeebf651983601e3673a1f92d36510c0cc19b2e75856db8e4a41f92a5\"\n          \"1efa66d6cc22e414944c2c34a5a89ccde0be76f51410824e330d8e7c61319433\"\n          \"8c93732e8aea651fca18bcf1ac1824340c5553aff1e58d4ab8d7c8842b471202\"\n          \"1e517cd6c140f6743c69c7bee05b10a8f24050a8caa4f96d1664909c5a06\");\n      v.emplace_back(\n          \"6e85c2f8e1fdc3aaeb969da1258cb504bbf0070cd03d23b3fb5ee08feea5ee2e\"\n          \"0ee1c71a5d0f4f701b351f4e4b4d74cb1e2ae6184814f77b62d2f08134b7236e\"\n          \"bf6b67d8a6c9f01b4248b30667c555f5d8646dbfe291151b23c9c9857e33a4d5\"\n          \"c847be29a5ee7b402e03bac02d1a4319acc0dd8f25e9c7a266f5e5c896cc11b5\"\n          \"b238df96a0963ae806cb277abc515c298a3e61a3036b177acf87a56ca4478c4c\"\n          \"6d0d468913de602ec891318bbaf52c97a77c35c5b7d164816cf24e4c4b0b5f45\"\n          \"853882f716d61eb947a45ce2efa78f1c70a918512af1ad536cbe6148083385b3\"\n          \"4e207f5f690d7a954021e4b5f4258a385fd8a87809a481f34202af4caccb82\");\n      return v;\n    }();\n    return testVector;\n  }\n\n  folly::ByteRange getHashKey() const override {\n    static const std::vector<unsigned char> bytes = []() {\n      std::vector<unsigned char> b;\n      b.resize(64);\n      for (size_t i = 0; i < b.size(); ++i) {\n        b[i] = static_cast<uint8_t>(i);\n      }\n      return b;\n    }();\n    return folly::range(bytes);\n  }\n};\n\nTEST_F(Blake2xbKeyedTest, checkTestVector) {\n  checkTestVector(Blake2xbKeyedTest::getTestVector());\n}\n"
  },
  {
    "path": "folly/crypto/test/LtHashBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sodium.h>\n\n#include <folly/Benchmark.h>\n#include <folly/Random.h>\n#include <folly/crypto/LtHash.h>\n#include <folly/init/Init.h>\n#include <folly/io/IOBuf.h>\n\n#include <glog/logging.h>\n\nusing namespace ::folly::crypto;\n\nnamespace {\nconstexpr size_t kObjectCount = 1000;\nconstexpr size_t kObjectSize = 150;\nstd::vector<std::unique_ptr<const folly::IOBuf>> kObjects;\n} // namespace\n\nstd::unique_ptr<folly::IOBuf> makeRandomData(size_t length) {\n  auto data = std::make_unique<folly::IOBuf>(\n      folly::crypto::detail::allocateCacheAlignedIOBuf(length));\n  data->append(length);\n  randombytes_buf(data->writableData(), data->length());\n  return data;\n}\n\ntemplate <std::size_t B, std::size_t N>\nvoid runBenchmark(size_t n) {\n  LtHash<B, N> ltHash;\n  for (size_t i = 0; i < static_cast<size_t>(n); ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    ltHash.addObject({obj.data(), obj.length()});\n  }\n}\n\nBENCHMARK(single_blake2b, n) {\n  std::array<unsigned char, crypto_generichash_blake2b_BYTES_MAX> result;\n  for (size_t i = 0; i < static_cast<size_t>(n); ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    int res = crypto_generichash_blake2b(\n        result.data(), sizeof(result), obj.data(), obj.length(), nullptr, 0);\n    if (res != 0) {\n      throw std::runtime_error(\"blake2b hash failed\");\n    }\n  }\n}\n\nBENCHMARK_RELATIVE(LtHash_element_count_1024_length_16, n) {\n  runBenchmark<16, 1024>(static_cast<size_t>(n));\n}\n\nBENCHMARK_RELATIVE(LtHash_element_count_1008_length_20, n) {\n  runBenchmark<20, 1008>(static_cast<size_t>(n));\n}\n\nBENCHMARK_RELATIVE(LtHash_element_count_1024_length_32, n) {\n  runBenchmark<32, 1024>(static_cast<size_t>(n));\n}\n\nBENCHMARK_RELATIVE(LtHash_element_count_2048_length_32, n) {\n  runBenchmark<32, 2048>(static_cast<size_t>(n));\n}\n\nBENCHMARK(calculateChecksumFor100KObjects_B20_N1008) {\n  LtHash<20, 1008> ltHash;\n  for (auto i = 0; i < 100000; ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    ltHash.addObject({obj.data(), obj.length()});\n  }\n}\n\nBENCHMARK_RELATIVE(calculateChecksumFor100KObjects_B16_N1024) {\n  LtHash<16, 1024> ltHash;\n  for (auto i = 0; i < 100000; ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    ltHash.addObject({obj.data(), obj.length()});\n  }\n}\n\nBENCHMARK_RELATIVE(calculateChecksumFor100KObjects_B32_N1024) {\n  LtHash<32, 1024> ltHash;\n  for (auto i = 0; i < 100000; ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    ltHash.addObject({obj.data(), obj.length()});\n  }\n}\n\nBENCHMARK(subtractChecksumFor100KObjects_B20_N1008) {\n  LtHash<20, 1008> ltHash;\n  for (auto i = 0; i < 100000; ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    ltHash.removeObject({obj.data(), obj.length()});\n  }\n}\n\nBENCHMARK_RELATIVE(subtractChecksumFor100KObjects_B16_N1024) {\n  LtHash<16, 1024> ltHash;\n  for (auto i = 0; i < 100000; ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    ltHash.removeObject({obj.data(), obj.length()});\n  }\n}\n\nBENCHMARK_RELATIVE(subtractChecksumFor100KObjects_B32_N1024) {\n  LtHash<32, 1024> ltHash;\n  for (auto i = 0; i < 100000; ++i) {\n    const folly::IOBuf& obj = *(kObjects[i % kObjects.size()]);\n    ltHash.removeObject({obj.data(), obj.length()});\n  }\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n\n  if (sodium_init() < 0) {\n    throw std::runtime_error(\"Failed to initialize libsodium\");\n  }\n\n  // pre-generate objects with random length to hash\n  for (size_t i = 0; i < kObjectCount; i++) {\n    kObjects.push_back(makeRandomData(kObjectSize));\n  }\n\n  // Trigger the implementation selection of AUTO math operations before\n  // starting the benchmark, so log messages don't pollute the output table.\n  LtHash<20, 1008> ltHash;\n  ltHash.addObject(folly::range(\"hello world\"));\n  ltHash.removeObject(folly::range(\"hello world\"));\n\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/crypto/test/LtHashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/crypto/LtHash.h>\n\n#include <algorithm>\n#include <memory>\n#include <random>\n#include <string>\n#include <vector>\n\n#include <folly/Random.h>\n#include <folly/String.h>\n#include <folly/io/IOBuf.h>\n#include <folly/portability/GTest.h>\n\nusing namespace ::testing;\n\nusing namespace folly::crypto;\n\nnamespace {\nstd::string toHex(const folly::IOBuf& buf) {\n  return folly::hexlify({buf.data(), buf.length()});\n}\n\nstd::unique_ptr<folly::IOBuf> makeRandomData(size_t length) {\n  auto data = folly::IOBuf::create(static_cast<uint64_t>(length));\n  data->append(length);\n  randombytes_buf(data->writableData(), data->length());\n  return data;\n}\n\ntemplate <typename T>\nstruct IsLtHash {\n  static inline constexpr bool value() { return false; }\n};\n\ntemplate <std::size_t B, std::size_t N>\nstruct IsLtHash<LtHash<B, N>> {\n  static inline constexpr bool value() { return true; }\n};\n\n} // namespace\n\nnamespace folly::crypto {\n// Needed so an EXPECT_EQ() or EXPECT_NE() failure prints the LtHash checksum.\ntemplate <std::size_t B, std::size_t N>\nstatic std::ostream& operator<<(std::ostream& os, const LtHash<B, N>& h) {\n  os << toHex(*h.getChecksum());\n  return os;\n}\n} // namespace folly::crypto\n\n// Note: the template parameter H must be an instance of LtHash<B, N> for some\n// valid B and N.\ntemplate <typename H>\nclass LtHashTest : public ::testing::Test {\n protected:\n  static_assert(\n      IsLtHash<H>::value(),\n      \"template parameter H is not an instance of LtHash\");\n\n  std::array<unsigned char, 1> obj1_{{'a'}};\n  std::array<unsigned char, 1> obj2_{{'b'}};\n\n  static size_t kChecksumLength() {\n    return H::getElementCount() / H::getElementsPerUint64() * sizeof(uint64_t);\n  }\n\n  static const H& kEmptyHash() {\n    static const H emptyHash;\n    return emptyHash;\n  }\n};\n\n// Note: to instantiate template-parameterized test cases, use TYPED_TEST()\n// instead of TEST_F().\n\n// Some googletest macro magic to make TYPED_TEST work\nusing LtHashTestTypes =\n    ::testing::Types<LtHash<16, 1024>, LtHash<20, 1008>, LtHash<32, 1024>>;\nTYPED_TEST_SUITE(LtHashTest, LtHashTestTypes);\n\n// Note: in all test cases below, `TypeParam` refers to the H template param.\n// Static methods must be prefixed with `TestFixture::`, while class variables\n// and instance methods must be accessed through `this->`.\n//\n// See the \"Typed Tests\" section of the Advanced Guide at\n// https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md\n// for details.\n\nTYPED_TEST(LtHashTest, empty) {\n  TypeParam h;\n  size_t checksumLength = TestFixture::kChecksumLength();\n  EXPECT_EQ(checksumLength, h.getChecksumSizeBytes());\n  auto checksum = h.getChecksum();\n  EXPECT_EQ(checksumLength, checksum->length());\n  EXPECT_EQ(std::string(checksumLength * 2, '0'), toHex(*checksum));\n}\n\nTYPED_TEST(LtHashTest, reset) {\n  TypeParam h;\n  h.addObject(folly::range(this->obj1_));\n  EXPECT_NE(TestFixture::kEmptyHash(), h);\n  h.reset();\n  EXPECT_EQ(TestFixture::kEmptyHash(), h);\n}\n\nTYPED_TEST(LtHashTest, copyAssignment) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  TypeParam h2{h1};\n  EXPECT_EQ(h1, h2);\n  TypeParam h3 = TestFixture::kEmptyHash();\n  h3 = h1;\n  EXPECT_EQ(h1, h3);\n}\n\nTYPED_TEST(LtHashTest, checksumEquals) {\n  TypeParam h0;\n  auto length = h0.getChecksumSizeBytes();\n  // should throw exception for invalid length checksum\n  EXPECT_THROW(\n      h0.checksumEquals(folly::range(std::string(length - 1, '\\0'))),\n      std::runtime_error);\n  // initial checksum is filled with '0' bytes\n  EXPECT_TRUE(h0.checksumEquals(folly::range(std::string(length, '\\0'))));\n  auto checksum = h0.getChecksum();\n  EXPECT_TRUE(h0.checksumEquals({checksum->data(), checksum->length()}));\n  // add an object and compare checksums\n  h0.addObject(folly::range(this->obj1_));\n  checksum = h0.getChecksum();\n  EXPECT_TRUE(h0.checksumEquals({checksum->data(), checksum->length()}));\n  // compare against moved-out LtHash, should return false\n  TypeParam h1 = std::move(h0);\n  // NOLINTNEXTLINE(bugprone-use-after-move)\n  EXPECT_FALSE(h0.checksumEquals({checksum->data(), checksum->length()}));\n}\n\nTYPED_TEST(LtHashTest, moveAssignment) {\n  TypeParam h0;\n  h0.addObject(folly::range(this->obj1_));\n  TypeParam h1 = h0;\n  TypeParam h2{std::move(h1)};\n  EXPECT_EQ(h0, h2);\n  TypeParam h3;\n  h3 = std::move(h2);\n  EXPECT_EQ(h0, h3);\n  // Make sure copying to a moved-from object works\n  h2 = h3;\n  EXPECT_EQ(h0, h2);\n  // Move from h3 to make sure calling destructor of a moved-from object\n  // does not crash.\n  TypeParam h4 = std::move(h3);\n  // This line is here to make sure the previous line is not optimized out.\n  EXPECT_EQ(h0, h4);\n}\n\nTYPED_TEST(LtHashTest, addObjectCommutative) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  h1.addObject(folly::range(this->obj2_));\n\n  TypeParam h2;\n  h2.addObject(folly::range(this->obj2_));\n  h2.addObject(folly::range(this->obj1_));\n\n  // add('a'); add('b') == add('b'); add('a')\n  EXPECT_EQ(h1, h2);\n}\n\nTYPED_TEST(LtHashTest, addTwoLtHashes) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n\n  TypeParam h2;\n  h2.addObject(folly::range(this->obj2_));\n\n  TypeParam h3 = h1 + h2;\n  TypeParam h4;\n  h4.addObject(folly::range(this->obj1_));\n  h4.addObject(folly::range(this->obj2_));\n\n  EXPECT_EQ(h4, h3);\n  // Make sure the move-semantics version works\n  h3 = std::move(h1) + std::move(h2);\n  EXPECT_EQ(h4, h3);\n}\n\nTYPED_TEST(LtHashTest, subtractTwoLtHashes) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  h1.addObject(folly::range(this->obj2_));\n\n  TypeParam h2;\n  h2.addObject(folly::range(this->obj2_));\n\n  TypeParam h3 = h1 - h2;\n  TypeParam h4;\n  h4.addObject(folly::range(this->obj1_));\n\n  EXPECT_EQ(h4, h3);\n  // Make sure the move-semantics version works\n  h3 = std::move(h1) - std::move(h2);\n  EXPECT_EQ(h4, h3);\n}\n\nTYPED_TEST(LtHashTest, addObjectChaining) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  h1.addObject(folly::range(this->obj2_));\n  TypeParam h2;\n  h2.addObject(folly::range(this->obj1_)).addObject(folly::range(this->obj2_));\n\n  // add('a'); add('b') == add('a').add('b')\n  EXPECT_EQ(h1, h2);\n}\n\nTYPED_TEST(LtHashTest, addDifferentObjects) {\n  TypeParam h1;\n  TypeParam h2;\n  h1.addObject(folly::range(this->obj1_));\n  h2.addObject(folly::range(this->obj2_));\n\n  // add('a') != add('b')\n  EXPECT_NE(h1, h2);\n}\n\nTYPED_TEST(LtHashTest, addAndremoveObjectSame) {\n  TypeParam h;\n  h.addObject(folly::range(this->obj1_));\n  h.removeObject(folly::range(this->obj1_));\n\n  // add('a'); delete('a') == 0\n  EXPECT_EQ(TestFixture::kEmptyHash(), h);\n}\n\nTYPED_TEST(LtHashTest, addObjectWithInitialChecksum) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  h1.addObject(folly::range(this->obj2_));\n\n  TypeParam h2;\n  h2.addObject(folly::range(this->obj1_));\n  TypeParam h3;\n  h3.setChecksum(*h2.getChecksum());\n  h3.addObject(folly::range(this->obj2_));\n\n  // add('a'); LtHash(checksum) and add('b') == add('a'); add('b');\n  EXPECT_EQ(h1, h3);\n}\n\nTYPED_TEST(LtHashTest, addObjectVariadic) {\n  std::vector<unsigned char> combinedObj;\n  for (size_t i = 0; i < this->obj1_.size(); ++i) {\n    combinedObj.push_back(this->obj1_[i]);\n  }\n  for (size_t i = 0; i < this->obj2_.size(); ++i) {\n    combinedObj.push_back(this->obj2_[i]);\n  }\n\n  TypeParam h1, h2, h3;\n  h1.addObject(folly::range(combinedObj));\n  h2.addObject(folly::range(this->obj1_), folly::range(this->obj2_));\n  EXPECT_EQ(h1, h2);\n  h3.addObject(folly::range(this->obj2_), folly::range(this->obj1_));\n  EXPECT_NE(h1, h3);\n}\n\nTYPED_TEST(LtHashTest, removeObjectVariadic) {\n  std::vector<unsigned char> combinedObj;\n  for (size_t i = 0; i < this->obj1_.size(); ++i) {\n    combinedObj.push_back(this->obj1_[i]);\n  }\n  for (size_t i = 0; i < this->obj2_.size(); ++i) {\n    combinedObj.push_back(this->obj2_[i]);\n  }\n\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  h1.addObject(folly::range(combinedObj));\n\n  TypeParam h2 = h1;\n  TypeParam h3 = h1;\n\n  h1.removeObject(folly::range(combinedObj));\n  h2.removeObject(folly::range(this->obj1_), folly::range(this->obj2_));\n  EXPECT_EQ(h1, h2);\n  h3.removeObject(folly::range(this->obj2_), folly::range(this->obj1_));\n  EXPECT_NE(h1, h3);\n}\n\nTYPED_TEST(LtHashTest, getChecksumDeepCopy) {\n  // verifies that getChecksum() returns a deep copy of the IOBuf\n  // the returned checksum should not change when a new object is added to\n  // LtHash later\n  TypeParam h;\n  h.addObject(folly::range(this->obj1_));\n  auto checksum = h.getChecksum();\n  auto temp = folly::IOBuf::create(checksum->length());\n  std::memcpy(temp->writableData(), checksum->data(), checksum->length());\n  EXPECT_EQ(0, std::memcmp(checksum->data(), temp->data(), checksum->length()));\n\n  h.addObject(folly::range(this->obj1_));\n  auto c2 = h.getChecksum();\n  EXPECT_EQ(0, std::memcmp(checksum->data(), temp->data(), checksum->length()));\n  EXPECT_NE(0, std::memcmp(checksum->data(), c2->data(), checksum->length()));\n}\n\nTYPED_TEST(LtHashTest, addObjectRandomShuffle) {\n  // 1) generates random objects\n  // 2) add them to LtHash\n  // 3) shuffle the object list\n  // 2) add them to a new LtHash\n  // 3) then verifies that they reach the same checksum\n  int objectCount = 1000;\n  std::vector<std::unique_ptr<folly::IOBuf>> objects;\n  for (int i = 0; i < objectCount; i++) {\n    // object size is between 1 byte and 1 KB\n    size_t objectSize = (folly::Random::rand32() % 1024) + 1;\n    objects.push_back(makeRandomData(objectSize));\n  }\n  TypeParam h1;\n  for (size_t i = 0; i < objects.size(); i++) {\n    h1.addObject({objects[i]->data(), objects[i]->length()});\n  }\n  auto rng = std::default_random_engine{};\n  std::shuffle(std::begin(objects), std::end(objects), rng);\n  TypeParam h2;\n  for (size_t i = 0; i < objects.size(); i++) {\n    h2.addObject({objects[i]->data(), objects[i]->length()});\n  }\n  // H(o1 + o2 + ...) == H(o_i + o_j + ...)\n  EXPECT_EQ(h1, h2);\n}\n\nTYPED_TEST(LtHashTest, addAndremoveObjectRandom) {\n  // 1) generate random objects\n  // 2) adds them to LtHash while storing the checksum after each add\n  // 3) removes objects in reverse order, verifies that checksum is reversed\n  TypeParam h;\n  size_t objectCount = 1000;\n  std::vector<std::unique_ptr<folly::IOBuf>> objects;\n  for (size_t i = 0; i < objectCount; i++) {\n    // object size is between 1 byte and 1 KB\n    size_t objectSize = (folly::Random::rand32() % 1024) + 1;\n    objects.push_back(makeRandomData(objectSize));\n  }\n  std::vector<std::unique_ptr<folly::IOBuf>> checksums;\n  checksums.push_back(h.getChecksum());\n  for (size_t i = 0; i < objects.size(); i++) {\n    h.addObject({objects[i]->data(), objects[i]->length()});\n    checksums.push_back(h.getChecksum());\n  }\n  EXPECT_TRUE(folly::IOBufEqualTo()(*h.getChecksum(), *checksums.back()));\n  for (int i = static_cast<int>(objects.size() - 1); i >= 0; i--) {\n    h.removeObject({objects[i]->data(), objects[i]->length()});\n    EXPECT_TRUE(folly::IOBufEqualTo()(*h.getChecksum(), *checksums[i]));\n  }\n  EXPECT_EQ(TestFixture::kEmptyHash(), h);\n}\n\nTYPED_TEST(LtHashTest, setChecksum) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  auto checksum = h1.getChecksum();\n  TypeParam h2(*checksum); // copy version\n  EXPECT_EQ(h1, h2);\n  TypeParam h3(h1.getChecksum()); // move version\n  EXPECT_EQ(h1, h3);\n  TypeParam h4;\n  h4.setChecksum(*checksum); // copy version\n  EXPECT_EQ(h1, h4);\n  TypeParam h5;\n  h5.setChecksum(std::move(checksum)); // move version\n  EXPECT_EQ(h1, h5);\n}\n\nTYPED_TEST(LtHashTest, setChecksumWithChainedIOBuf) {\n  TypeParam h;\n  h.addObject(folly::range(this->obj1_));\n  auto c1 = h.getChecksum();\n  auto c2 = folly::IOBuf::create(c1->length() / 2);\n  std::memcpy(c2->writableTail(), c1->data(), c1->length() / 2);\n  c2->append(c1->length() / 2);\n  auto c3 = folly::IOBuf::create(c1->length() - c2->length());\n  std::memcpy(\n      c3->writableTail(),\n      c1->data() + c2->length(),\n      c1->length() - c2->length());\n  c3->append(c1->length() - c2->length());\n  c2->prependChain(std::move(c3));\n  TypeParam h2;\n  h2.setChecksum(*c2);\n  EXPECT_EQ(h, h2);\n}\n\nTYPED_TEST(LtHashTest, setChecksumFailure) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n  auto checksum = h1.getChecksum();\n  auto partialChecksum =\n      folly::IOBuf::copyBuffer(checksum->data(), checksum->length() - 1);\n  TypeParam h2;\n  EXPECT_THROW(h2.setChecksum(*partialChecksum), std::runtime_error);\n  EXPECT_THROW(h2.setChecksum(std::move(partialChecksum)), std::runtime_error);\n  // Trying to set a null checksum should fail\n  EXPECT_THROW(\n      h2.setChecksum(std::unique_ptr<folly::IOBuf>{}), std::runtime_error);\n  // If padding bits are not properly zeroed out, the checksum is invalid.\n  if (TypeParam::hasPaddingBits()) {\n    uint64_t* ptr = reinterpret_cast<uint64_t*>(checksum->writableData());\n    *ptr = 0xFFFFFFFFFFFFFFFFULL;\n    EXPECT_THROW(h2.setChecksum(*checksum), std::runtime_error);\n    EXPECT_THROW(h2.setChecksum(std::move(checksum)), std::runtime_error);\n  }\n}\n\nTYPED_TEST(LtHashTest, addObjectWithKnownValue) {\n  std::array<unsigned char, 5> obj3{{'h', 'e', 'l', 'l', 'o'}};\n  TypeParam h;\n  h.addObject(folly::range(this->obj1_));\n  h.addObject(folly::range(this->obj2_));\n  h.addObject(folly::range(obj3));\n  h.removeObject(folly::range(this->obj2_));\n  if (h.getElementSizeInBits() == 16 && h.getElementCount() == 1024) {\n    std::string expectedChecksum =\n        \"353cae6169e519eb9cf80edd2c5b33810276227e77a09030e3ac3b00299c9716c6b592\"\n        \"b262b2b05ad82db539f23fc03baa1ffacc9704fe078219307c02c0f501c810895c19a7\"\n        \"71934855d091e30db8eba564596f071400fcca93b69115055c55e0b333b5583ec0068a\"\n        \"219289b557be5b24cfa679ae8e20b9084c77eadab966e4f94239d5f671371aa17c41f0\"\n        \"510aaaeb6e28fed0eb37b57c5ff8f6c64a0395ddb32d2948abee9ae84930ee0d43d015\"\n        \"b2f577cadb558eef33e715f349114c1937817ff26b606f1f33a1f3b4a72eaa3b24573a\"\n        \"78d06b315857a8295675ec2bfc9897b644f60d401c4315bea8a6ad410f77e3969aaa03\"\n        \"2d31526df0c271665647c98f1e4d3946b659e47f45480c3eac9b0e0b742501595b24d5\"\n        \"362d3f6f4ba8a4fcda7d87951ade9ec184a45c2fd5bff5282835c29071551e96d940c3\"\n        \"ed19bb3124c3b37080dc3c80bc22f61b431195b9489bed3244e0e522bf8f8c752145b0\"\n        \"1ee47701085ffa1238f3a1d5e778052b393330fff8b586d9399cced75d4d15697f9015\"\n        \"174d3302d97b1cc55ae20cdb573d4061d2940b213a35808122e7d55bd53e2c9ba1779c\"\n        \"8a19532ff1e65a440e871f96e086dce6693efba86e033f7e3b04069f9eeccc0f5c5947\"\n        \"af0b04f5528be1b57bba0912eeb52fdd11f0cac0e40ae641bbc40207188adbfe13463c\"\n        \"880e84016476facae56f7f6de26e7f508a277a409988aabec7f9bb552000e3f7a44f51\"\n        \"ec5c7c98979a227403464797a06fae0d7aa951bb429cb9df4ed65a430a98e0c88f7d4e\"\n        \"47e1256f17c4b126f05b885154507b3b80a2a1b6e1f43eea48b4b93cab0622bd002a25\"\n        \"dd5d1b69fe05c11619837eba6edfac493d663409f5ce82762584205fe49e8f718fbc5a\"\n        \"92823cd9a17c1c9a07ce9f2535c918c6ee0f0729b67eb0be8b5e0edc990260679fdf5a\"\n        \"9991a6d62ec1d72f5e5a478dbf0e5cbd1703daf5f170411d0d7aca4921cb644ec1d86e\"\n        \"02711d09359b0f2b45a5b9fe57e122add8b5ae27aaeb44aa77a9fe187a67ea7447b27d\"\n        \"02b4bf41fc5024350dc8838fb8f977535ba4481569a74d90306e0c9979a9d149be9502\"\n        \"23d1ca5d425b9ec281ee3884d8e8a1ad0d00504f0f57ff35e0ee33d184f35fd28dfa34\"\n        \"8686fb926da95597fb947acc509a5cb7cfe1eeb33dfdf9b4b384346c862cfb198f6948\"\n        \"a6f6d53a74848043c8b647076b0a90151bd40c58d32434ebf549aa92f4a5b7581b7ec6\"\n        \"821ca3485cf8e2a6ce0f5e204ed5a92c84618c2828e5c6f222ec3c48e37dcada7ce28b\"\n        \"ba5c09740170d32aa004b43cde46d45f9912528a3a7a7f30fb6019548dd174b4d7b0ba\"\n        \"fb232920b972362db4d863a5e0a9e30a041ecb874a7acbd378ccb11ffbffcd086ad797\"\n        \"be5b4de07859d0b1fb3e4835a84ea224940482a3849cf392528dfcf8920d4b4bfc4060\"\n        \"6e852d85b7bfd1f2723214969dab6adfb8c26dc5f51b1b043b8a25df1eadd90d1a2324\"\n        \"0b735943841ae4e13564ddb6f0f7dcac1db82a34ab9ca042f8c4690727c7a0fac98c10\"\n        \"dac065a57dff8010e9d49ba3b801622e8b786bb44079ceecf61ff7cc07be8672c647b5\"\n        \"25ffea7c3fab95d40d9d36e220bb3a5292880faf05a8dd94e60a4ff0ccfc124d2dca03\"\n        \"a85d0864bfa28cddb7bdcc83ff717239dae979596691b6e3062068e6ea442ebd354bc6\"\n        \"53b0e5b750bcbfaec275c77ab82bd3452e4776734df686d6bae946855a4659dd3566f4\"\n        \"8d0879a00c06a7ce81c0f234e0203ce68ffc9434f3f10281d76110887a4b460514f761\"\n        \"b517f1d151d88724160fbeff7f69a5a23eae2bf48916ad55c084b908d955519a67096b\"\n        \"94638fa10d8d153a60d0c44f2d9148ad549fb1e64ac423aac1fdc754bc44a69573578c\"\n        \"6b881bca177698e68d6ffdc2d7d89469f2e1039e8e3b955581a56c15519590b65bd9bc\"\n        \"c3b3b1a95d1d484c2585ebdfd8a15c737b436456934d9b8439d92d1212bf8799028780\"\n        \"d9f35d208c093ba6506aff74979faa10fa807398e8fb769be070318caee6b4f5091d8d\"\n        \"9254656d0a1e838ba73ed0f0c8e8d4a0d19f9e91340578baa7ce5aec9f73f8e26db927\"\n        \"3c544f11d6b8e5e142f4a8ad70a9e21c3dc2c7b4403073c4722e0af775f98c37ae0645\"\n        \"e1829dec574de3108f62965a5354aaa7695c1e4feab1fc8ccf9a5e2a7ed0758e411ea9\"\n        \"ea25f4f659a36cc5aa0bed2a9ce4518cd1aa1b4ed94c2d62596059d20cd948a058b78e\"\n        \"f9ad3c9e7c7c9fd433c42701aad7aff74fb14ea39812c3e68b6ca8585432ecd53a7dfe\"\n        \"ece8e6a73b0ecddabc8c9da37b140adee6308c540bedbcf77d49762e7efaeededf5196\"\n        \"6503315fb287b69a08854ec58fd41c2f214c3273cc48bc71718b801c27936c7fd339b7\"\n        \"f78c2eda6835e7d532ef6496cbbc7b018cc48ed49e33c16e16d2bdc98f47f376208770\"\n        \"b5d5b6e789b30ca55ad4e8cd09b6bd90b66e8d4abfd0fbc3e98fc28f3913e476161d0f\"\n        \"0f7477d3ca066adce7567d1af90dc3415970199ada286e22ea90892da107c34d3745c5\"\n        \"d5d3f290fbd2d0b64942117955e94b343517d76959f0764216ce27bc33e772fefe4a48\"\n        \"20315d67b43302f73e8002cc6144c3f3ad66c307eb2f9e0192a00da9ddf262b600dd0a\"\n        \"49721da25ca71997d17c3441cd9588c5469f6927966d06676f89243387f01ebd2094be\"\n        \"ac0716a2e486d85524c51ba2898abb8b8df3a169f93fe6333f4f6a868738969905ba55\"\n        \"176f1b8055d19749c0c5122ab1eaf34b0eb458fc10a656811fe4a7bb588eac3450b6d6\"\n        \"1c7f634588996ef2d903478cde206f58c1a93069c7df80a28394d05e9a8ed99b5312e9\"\n        \"cbec0a2dba2e3e3e24e854798e9dc8c09922fadb987e945a765e2f614993f2b5605548\"\n        \"1e3702371d4eb86c872ca65269125be61d86\";\n    EXPECT_EQ(expectedChecksum, toHex(*h.getChecksum()));\n  } else if (h.getElementSizeInBits() == 20 && h.getElementCount() == 1008) {\n    std::string expectedChecksum =\n        \"aec6e81142ad553e9eae2f5ea2c9d20dec91881229418b20024b05a235b5f50adb98ed\"\n        \"276aa9443a7be0047ed20d3f31c8f80ba3d5048719d5174076ac1db9298ec1e62d6b74\"\n        \"0e3dd825427e4d1dcf1270eb656e8cddfc16f549cf769e8dc30fec35039986e41e0230\"\n        \"a063e4ca145c162766404257f89c2d43f4eb45beddfc036d552dcf94b5fe3780514217\"\n        \"52797a3d67d909529399c31ffdb2ce05f725f03fb0892d45ba84602818af60832ea1f1\"\n        \"0b7489ee8ce0c8eb01478ca884b4602f197858a0a95d71e83f4796ee61a2a130297691\"\n        \"203342e49820f3ab2e78cdedc0190268a81102d08026851eeeeed5600333b6b6cff6df\"\n        \"2c7511c1cfc0cb7b85f7283933abffe308d42779698c3f10b8c42058ec4a60ec78270b\"\n        \"68104d6dc7fd042124a32bd2c0b4422d3d9ea3c0d878b71ca12d6778a068db04f49aed\"\n        \"a11b31160283abef5143e84111c2502012e490f33488ee6098c809aa2870bd63e1dcc9\"\n        \"081925fb4622af39ad02322d24de7311fd1dfaa3eb543ec0a5086f6a4f849281322124\"\n        \"d7650430409c3cfff6a2d7d2259937ee21eef320657c13cc1fe1689a35a81ee51802a7\"\n        \"54ad1b1a8242ed98f18d033550922eb6b191123f803c68a249d9ef1818290085a00870\"\n        \"3db27fad9985f42d36469be3edfa450a290194230f2ad9350304ee8cb3e97cb40ca773\"\n        \"6e2953c106256f08c4d7972c611cc134049e431c032d91ff0d5cbf549c006f9042cb79\"\n        \"3ca7128ca90faf42212f1e1e02877d91f5ec14884440b3df70352ff73ea75c4768541c\"\n        \"de0f03f85ef9a824d1a64834ddd9b0303aa543e959813a07950e235a8b1d9e11c7e26e\"\n        \"6e42a8c80a89c58f590561be0dd70a6e2f820c9c2c0af3478eecb47513bcef8ff1ccdd\"\n        \"9c1830a92b25c518513033856391c009ec37919fa6d5de6ddc32c4cca1a267257b0184\"\n        \"17adb3d1e0d9341f6de3143c98d61d625f8aa39fd0723f9773cb84a6c4c2140c0fa0d7\"\n        \"ca5d7d15058c0acdd0cd703b19920b2c42c9c30e71392cb6d39c7b1d5794c44af309c6\"\n        \"1db784415a80940a129135abd79bac822f067b4631db656d07a53a8f3a212d510f3724\"\n        \"c031cab0a51632792ef2477c061fbef3855725a9f90fbe1608498918392ba532c7927a\"\n        \"90a3140b0d4309bd31131381270ef120b51720936184f4c4494e05d10082efe281f936\"\n        \"10184ef84c198c3ccb77a2e76fd10f31b9afc33a0e51ff2f707842f590719a1368e589\"\n        \"69bbc9db0e208a20c957fd663079f2e347184d081abd5382965ca59817d7f40df44e98\"\n        \"f42c668169a79fbc0324a36a86e78c2d4507d2672889760577009261e494cc9d3638b9\"\n        \"81697bbc71373ed31149804e3497184bcd2e5f2b11310a360b812431792a1a509560e1\"\n        \"eb597c3eb6ec09ea32e1823a3400ec0e8af96f38a35c0f623271cb3fee83461da71823\"\n        \"288e1a47de8a79f4284ac24daa8bb9493d65d3051198740600774a63e7cbe4b6125aba\"\n        \"678c7d94c920e93cab8fc239be312fb2aa214b91fb3dca17a0f4acd1e732838e09c255\"\n        \"615c2d9a2d066a705ce334c0cacc3625fcd413b0c5276da3d935393d0a896861a0e415\"\n        \"86da248985ed4626fdde66dacdbc97384cb724b11c51d42947dd686376b19004bdc26b\"\n        \"b677543d309b4feb60bd88e922fb5227aed9489130d7954b1b65e152337fb545ad83a8\"\n        \"6b2ca548e439c268741dbec80181190d232c4fc8c67399e5900b269463069f6c4506f1\"\n        \"7e4f507f40ee294e12eb2973b48022d2b7293a6b9501100bf9aa58ce01c82813bfc79e\"\n        \"99e80514ce5b6d92f899800edbf34b87f52c121d8de5cc16429072167eee4c7cfcd050\"\n        \"3fa42cace2ad2c641f6187a5d42cc8f60cf70d81b2019182274dc64540cf85c62ba229\"\n        \"09d82284fe1899fdc9f13761d408c7072563c5400224a3b9ad73127582311691e03ca7\"\n        \"9cf71ff31f226fe6cc8a0807494b976854bf0db4dfe02975a0ee2ee66d414aaaada809\"\n        \"855fa4bdea98880a7adfa4e81a099a1ad718cee89fa88b0640fbadfbb3e47d00b075a2\"\n        \"f5b0e50f34d09c8ef98fa5462559b50f17b9a44e36e9e949c2c4ccac0e2467a7431adc\"\n        \"812dc3c2087119f1521bc8080760db2dfa1f4928c97a2b88ce01011921250cf8ca3ba8\"\n        \"39461b7868030ec410e2463659ea29ea540bdcde606304504180a147ed052a56150a75\"\n        \"2f045320d34ec0fd469c621d29408af4b861e20dda2262b1fdbd4d02739cadd16f64e2\"\n        \"339f62c14ef2a5371e1b4de553e5309528e51d262744896132dda9c48983a801021ff3\"\n        \"634ec1bd070f5dd5c8ade63de51b348d0c0fb00d003d17720d4673cd5d05e18f69c90f\"\n        \"e82c0b1c6661fe60f41a16a285accbf0f47539d8986cf1d78961321a9564ac9b11221e\"\n        \"e3be45d9e1dd1a00a35301d968ccf521230ac5aa85a97f35bafea0d27a918d258c1e0d\"\n        \"cca7041b2a598a86e291493c140c3e0cbee51c120bdcf1e8ff4ac1ad07f35663f1f1ac\"\n        \"be3c8ae58baccb598a127293232631a878351764046d09494914b6e7a7e11b98d427bd\"\n        \"d4aa0b0da9f1271ed78d6ea20dbb1a77974b9b16e9b013cc0f85bf6ac5ed23ec048bb5\"\n        \"17f4133a98a1a3279375cd0faecc86ba317dfe184177294e430c3d0fb81a8904a3c9a6\"\n        \"2d790566069dbcee14b79882268eb88b37becb23791f218b3743b66c281ca0912c380f\"\n        \"8d40acedb72885174640d0f824157af0ef2968c93e25679ae4ade784322f31034c5d4f\"\n        \"9c2538ea4a800900a55027d2b1239a5bf09209e91a83a402654f06fc0f43800dd46029\"\n        \"61a4c58180a0e2252de900de3eb1873ad74707bb962cbf0dd1c7e0672ae9da0a14bbef\"\n        \"125408343478dee0549119431b01d9aa8ae850ff133f8a89bde69d8914a316850ee409\"\n        \"970a5383a8d491c806307d9082dfa1b9f03f9cbb40889ff509121c9fa1dfda80f52803\"\n        \"0de018a1a8c517797b08065d90bd30c8dc67d1dee50d17885a8e0b6ef57a23e290634d\"\n        \"1589d0158f9c8a6a7b14471172cbcab20ae5e024918faf959ab8a80c729141f294b8ff\"\n        \"07b6af09666aad7c3ebc6ca88e95c1da3057efcd124d7d123bc2d0474d36c4783c1619\"\n        \"45181d856007df8dc040d331d2207262a01cd63d7514279def9d4d98243d132e6554c8\"\n        \"611b3adfa683d9a3a8a61cca40cb8af3058e3683fc0bdfc1d9bd0f62cd2265db18542b\"\n        \"17d9a08671bc6d381d6ee1aa27c5400b4112a0c15d087306581d67006ac1343861ae4d\"\n        \"01d938732deae4e9efabf44f1102aa6fbbce2d111f3edb4ac0bc2c0533b186e6632e2c\"\n        \"631edde5e4dc9ebce8158f4ee0fa10cd5e046a78c2d03b91bf1a608cad8c248c5e1cb0\"\n        \"62847e09850e13fa2187be526d883cdcc168555b1c081ffe8b4d3e1764971bb7b9255c\"\n        \"07a4361a857a88d4e059b7300d3e0c769e3ce304ce7048163f7d7b3f16738987faa4ed\"\n        \"21d1cb853997341d193947484317584d39e3882691bd29773937184ca49265e93e3fca\"\n        \"2cedd84cd0353eac027e456d1c26050661a5444d7b36be4fcd38720c3637e5326e759e\"\n        \"9d3838729e0fcc49b4540989ed2e7534a956209b6da9b7f470ff033f23a7f1a6e9101d\"\n        \"00d00e9cdcb8482a5b06c18cc6993f29f8d2a7ddcb91e804b82a417c404d761edc6ae6\"\n        \"0b81d884268e41c161fcf18f3dec7061e86b046e3831afe93ca869fe06404f4e174d75\"\n        \"420686108a011129790b1d4046eae7714e11d980c271eae5f13858d1427caaec9c023e\"\n        \"73afdcd76d593922d30a6f04d41f2d8d8a820e0e48171f92ee4ee455700b381fac06e5\"\n        \"70c58b0a81bc499a835826117f1146877c90b011ea0ca4db40a17d29\";\n    EXPECT_EQ(expectedChecksum, toHex(*h.getChecksum()));\n  } else if (h.getElementSizeInBits() == 32 && h.getElementCount() == 1024) {\n    std::string expectedChecksum =\n        \"68c13c13c663d66f6f834f309cc293115b5c2026dab510ce1c6dae6b13763954ad4771\"\n        \"4f93a997a80ca52922770c4e64e3bbf10f67cc87a51d183ec0c66dc9ff751fdc5723cc\"\n        \"f8bb6636e0d34e1c7b3a38adcaa2b22e1e9f68bc1524ae89c62a601b1fd1ae544ede9b\"\n        \"a826e2153ef1c406d5e6807018e635e677a676031d94a798281d6801792a2aeb22d5ab\"\n        \"3082fe6b90b0bb5ffcc71727eb662fb0996e374b4a50074c47f8d68725986e142c98e1\"\n        \"38d4c934e17c17ce8ac9b554ed3d0e5016aa8d34976104ccdf63f5309f9a38de29a45f\"\n        \"14cd08041f7dd27882891307346495eb98348f06999fae96eba531e7837fcf868ae438\"\n        \"2200051f4f5c6ada89584a4a301faa5943ca28c3c0d850fe4754818674fc5a41efdd5b\"\n        \"6860c23ad851a5f35ed54dfc70603023b02ae43a8d211dcf7857764559b50aa6cbaa01\"\n        \"9fea1485aa0b597efc968e969f1e88add067022a695836fb5da6baacbf4f2278544acd\"\n        \"a0b22182788b872a2a75f70fbd2b36ae7dd8c5a291e2e2d2657f583b3852e5183d4bf1\"\n        \"5a2b6084cd3f5ef66f2457673aeb13a366ae31ec2cb8c36bf265ffe34af45e8e4e6782\"\n        \"0730952b527a955af1fa2dd5a3c4ba3bf94847627e3d049cce0c44c976be8f197f41da\"\n        \"a61fa9cf4f95fdc130585d30d897c0364e7a1b162841f7af267bf76888b376dfe7790e\"\n        \"14b3d1786e3369c0270bc3d0130c3acfd6a4638b72bd39a9e68e50b16d00a6ba74cf12\"\n        \"ccf3ab028e4d728d25c3f99323ffe5c158054b9581cd2a897d24468bb0001337b68b14\"\n        \"94fc4ba2f407d2df0d49a593daa31993b4ac16d880efbb94feca0728bd284e0acea435\"\n        \"4e386327a086e2e1b991ec31690e0b3eb4a8dffe5c982cf368597980553e2b12e87ccf\"\n        \"2e6994c2ea74546a3029a62a9851c5a23b89c2053519ae6304704a19284d8930fa7cca\"\n        \"2ab6231e5d620d5743594f11e1248b6b362ba8b0015ca9c7d6bc36d0ae9104221e75c9\"\n        \"519e34b0d0888a672cadaca3198d79c214cc275ffee909e600d949e3353cb24e033e19\"\n        \"596b4a6d7c299de11337f4d46704f6ad5292fa3eb72535bfd6a27c106a457eca485dd5\"\n        \"fdea051666affc73d0e051db5aaa894735c403e2456d0594c42cb0bd1ec325d30ac2dc\"\n        \"6aa698a2c1e499a809f9c25f88101d1307b339c7251fc4f455d5d52d1cc08d17ec626a\"\n        \"be8ff10ae10b61c0d96b786357dc1385b84f76e1a0fc2fd38ed67f3021ebff3c1655fe\"\n        \"20056434a7e68f2569c262f5c1bca2ecc0f9c8160e6b9bfed5fb6d8cf0e74f9df39453\"\n        \"9e5a9f04d4a0f2da01023d92ea2e8d2c36ce0b87d07fe6c996bba8795dc81c3c9b7c07\"\n        \"26879351817464e4626694aa5ce33ae6ee988f81412c74a8216901fbe46a4adaa75b78\"\n        \"39ab01d933bd77b43d8bd4736adbb0507318801f024a044a6a3308cccf4438b6865ecd\"\n        \"59521505806a935871c26cfcd4bf3955b2b573eabfbecaf53eb52e92f6fd3e397d4c2a\"\n        \"63090ef8b9e2434290e4184aff69bc64dc1246a5e4acf3f02b663c723404911ae4863b\"\n        \"fa3222af924647fb47e6ca45dc51bc5e20f771865e73652607006df18ff0d62f688a4d\"\n        \"3ed986fa8e79d7f400253d28028f79770d84daa2b14d3aee4a2d4153882f56d693ec7d\"\n        \"d9dbca9e1496464e0772abe54c0610dc969952189649a52ceb910cb52a61f989bb993a\"\n        \"e948899edd5a08dc4b10eceb47cf68cc3747304dcdde918c4fe60d336af06b0e84afbe\"\n        \"e81f39cd3bdfc054f421a12a0d0c8c89b451b3dc385e1ea94aece03cd692ad01237425\"\n        \"98fc638ac688c15d1738ef89c07bd19384ea0acbf8e97342c33c71b10993df7c28a36e\"\n        \"14278d352a02599478a30a942069dfbb10c7dfc6b921d75eb47f5e6296e17c3a79ef39\"\n        \"3d764f167b8c1c6598482497ffe7dd3103ea0c682d15565fe2d75357500482b6dc2848\"\n        \"f5a0ef2b7ab697fff5878ec016aa674cf21666cd83b88720ccb6fcfaf9ca9d85f42409\"\n        \"471718ec28c85717369f1d9c130b00e18ae7bfab24994bcb582c8e5e9bae67aabf5306\"\n        \"8f8d3ae2860c24250f1388b6cda75f2218b674084ee131823b8a1bf2bf70d1028cb99f\"\n        \"db3cbb3758e0f03efd6d928900d8ea73f3c9600a593d146ee6d10d437021699ba3fede\"\n        \"c2e7942200bfd968a4744b477078b2479f223264e4c845e994558424ebfebfea0cc4af\"\n        \"9975b0e59792a28b972d81e2391ad73970842fc7ceab6e217fb40338d107fb5522b0ba\"\n        \"c9ae4346600a7444c97f488db697a4a3d63c2b762b1a48eb036a2464bcf5def2c20919\"\n        \"bb2bd8ad4f5e00e5a8fc3f77ba8489069b415ed84ff767c6b3e80130688459770bb1bd\"\n        \"870d1861f043270ea20a31545a1734134ff406a00d47c65d7e09fb06f714f7a70f20ce\"\n        \"cc39bf5e4d4fca8e083fcfc61d8c2212561bc0633dc5c32ff0d922f583ef2b1aa21d83\"\n        \"4767708c257d65123d7280f4508bad4c36e162554dfb3a913454b94a4fd78573dc2499\"\n        \"f11092ba5b02549f0abf5c325e57d82e749d1e78809cd6484edf8e9b71e1a9b1e58c86\"\n        \"b6d82b11ffd1c36a19bb31a6fb38857c4811bd184ae1658b6a3a86175c8d0567ff08cc\"\n        \"e74c281b47557a48a1c92a3eee6a903f730ee62e0ef37882dbeb1526a6181f3681ed48\"\n        \"529a8af5efdf4c898e84546a18e084223b8077cea0ba9d2fd3d43a2a8560945167e8ef\"\n        \"d293a839d086c9b324c3da7a082576fc5e7bbe80b3b2a4057571714eeaf28e5e927fa1\"\n        \"f046759dcb5c7bd0b33f1b109ea40b5afaded8c72cfa555f558a2cb581aa4d41216587\"\n        \"e9bc03ddfda78471b26aae2b12a265f6f0d1b5a5a61a3e0af9efb96aa606b4d8c6bdaf\"\n        \"bab903fb553da5088e7dcebd2666b9c02fc287ec89bfd49a2107f5eac4f60a105c590c\"\n        \"a6fba3c9bdce8d385ea8f4b2ee0117f8bf29fe855f27e3ae6f205c43854410a8a5f6b5\"\n        \"3c9474d2eeb14bc050df8b8c9b697a510b77f93fe5c41ac0ccf2f13512768e6db2e43f\"\n        \"e78e5f8bf61129a91cad0bff6a25d96b568696ea5920f2a4bcc3902df8f929a1d7af3f\"\n        \"1e7e37aacf22488dd63a5f33c290d3d57a30a17740210e7f949cc244042fa43f75f25f\"\n        \"1da2210cb28e255d57111a647b7b1e2a209545a288fca4f19a1d628c0f5a03e680560f\"\n        \"80e7561478cfe222a10844cd5a9257b0ec8bc388d18d6bfbf2cf020448e20c4efaeaea\"\n        \"4441c8b82685533ad6c2e657e18d22038fc1fac7e2500b29e86080a54d85661c326646\"\n        \"7af9fd33fdc84aa7ce4b5297c9477eec2b45ebaf3c324629f2bfd9fd38eb62c11da2d1\"\n        \"abf6291005da15d12910e27cbf02dbe2c02093923668d0dac192c402dd874f7f3692c5\"\n        \"fca4b2d9850a6c28dbf0b1d13690cb077174fbaa14c9638d773f5e6ad43a891f0783ac\"\n        \"b90da25c894d5b33075ef153a477f76934380dbf2a6b39f3039f43a403387b33137348\"\n        \"8cbbcc5534df67c3b1c4323d20d98b4a68e266ce8eff85cc49053449d254e1f8eace18\"\n        \"9597731c9b8a5780437c53755706b72a959ce57a2baa10e559e577527c9dacb89fc6bc\"\n        \"cbbe250cf6ad229fa68186e7c0687992990d1fac6dfb0af7e6f10f600717d8ece368b9\"\n        \"ffb7460f50f8ba1ea58849781ea7f4c369df359c9d08f1e401fe31aafd0ba9a7af0550\"\n        \"599c33a1452a7d35dd7ebd241f93d725cb53864c6b0ecae5383d36ed1a1baee0d9a4fb\"\n        \"8191091eb68113210a7d2fb7441127987300677eddeb0b3d4104d4e70f40ae0f67468e\"\n        \"cf1f7c56e38737ab32dc4e3adbf8bff02cd9c730eab344bb48d4a87625cfbf8b01090a\"\n        \"c935e5b63835202945904281659fb1091b3c9ecb48bd2a1192552006f6f6d32d779b86\"\n        \"fe3aafb4927972b99be92cc229ef6b0008ac2f8ac782e499442accf4e27eeb19b8e53b\"\n        \"46669ee9a53fa78d7e5e03bb40195fec07673f7dcf21c4b71ff1eae78a8728ba7c6658\"\n        \"be16cbcfb8bebd400367ffac62b3e5a64a25becea53c62f0dfe3a62273d83dd97bdf82\"\n        \"8f4ad6b8c240d4043cf5f8a472e0d5a6caebd3915f68cfe82f30eafd499c6aec35199a\"\n        \"bc76fddf895320d5cebd24600532588aa35adc3c117ab71b528b80064b55bf6c7f107a\"\n        \"e5465a41a24cc0d59bc3608c101d93c52fd614d37b9a80e912285859d470e4d9815d49\"\n        \"9250cf4bd0a85a0d8fd825f37128e62ef37b64a2c0648badd3ccbd448b5ab57e92baee\"\n        \"949d2ec36d3c1512316a972f570bb1e44fc8e4d07ab417af6771de2fa66677323b8cbe\"\n        \"587f978e5a463f56b0b76e9ce5871d24c80f2a0ef3c57c28338de45871a3632ae1cf7b\"\n        \"4437bb1f148ef8bba02911147aa536d140d09e456f41db3151d23c1037a812e4c37a64\"\n        \"d95297170729ad8e7b3ed13794e78f16f40c11c50ea3af8723b142fb53e22a9ed9ad47\"\n        \"a4a9a919614cf72311f6566cb3b3dfe046530524787e773181e734e86728dbfae34a04\"\n        \"5041c83958c509ac68664870a1c7334614506da4692a29e9cf4cb9ac99ac94b3e63dbe\"\n        \"f58f80d55863a4a13b3543efff58295f8c690e5fe71534860f67b19c73013c0e6ff52f\"\n        \"e0d4c6830023dc56d0473a9797417df439d63d83779097b9f6899a42d6b8a63312aef6\"\n        \"f6e75b66efccfcbb63fe4e14002aeb043ec467b68b6bb7fb3ed2e0e850d1c5da0e0015\"\n        \"7d8ad7a12c3ec9b6fff04ae03ade0f00ad6d6d76d8bf50f7f9309bcff6184aa3161eee\"\n        \"7ffe48b24145389b08a892d2f71efcc9b8be2db9309efd419073519c39993fff210de3\"\n        \"ced7eb2e62c6356fc936db012cb2e29b0724e3da7ec9a74042daaf476e150eb0227334\"\n        \"0994673cf019525eb3a341e7bb92424ac66c0af6c00a4c94a54bbd65c7de052a6c4d5c\"\n        \"cb6e905e609d9c9d93cd28a3993b0df86a7097411e91c46db8471ce2e4645ed7901116\"\n        \"fcfd72a117158752b02dfae2e4b2b7be13b8eb01b49066a359fbcb013607d9623fb275\"\n        \"711576e948acbacba72fba1c385799b908d14d7ecf57b9d83a59b628c092b686fcf6a9\"\n        \"4a5330d95aa8f204a34c4502350564e3657219c914029139fb0fc95a536183e40c1d96\"\n        \"d6e1f75073b833c51170cca1a32ca1937fc7e46af88e81346f10aaf88a8e2473444cf6\"\n        \"dcef0a178d6f88ec82e8cc79eb6a162d0f177ff4e6ab3b738210f57995a57800dbbe73\"\n        \"89349484e6d71813a93c074c55279ad87a64cc0c9e33203a2a275ed59cdc0d8a03aa74\"\n        \"6793a93e83868b42fd781d091181455aa28d1d93850766a0b58cdd4fb6d4fd30966da7\"\n        \"ab99c42f6c337f8654869803dc372839e3b0e21ec959e59ec07a50e8d11642c4a82b73\"\n        \"d07064d265a33f030d397133306d0d69caa3e93238fdc7f78432eea40851a05f683924\"\n        \"d7f959c353a66fa11da2c27090505529474ec9f8220794725ab25575e4562f6f94dafc\"\n        \"107d5c9fd808471dacf6ec06faa727d7960fb81648551dc248deea3b13bbb941d3d485\"\n        \"dec2b60d81c9d8378f25f88dba0d3611137fa0181e8d2b73dddb24aa2f904b97e207cf\"\n        \"df4fba7faf36dcb6e7bb857b20751dbaf85608181bcc04d6f23e661ca8595cef86c837\"\n        \"75a67825dc7274924e81ef31a01b9a9067ed16cb8c0f2ccd92123b2dc11f847e1467a8\"\n        \"9e09259cb63c25b35fb3cf8f20e53667ca44a95f5d7bbe00c50f92cdd970e6c60ae2d6\"\n        \"232bc7720c128ef4a4a5e3913be0ae1c5e1c64d10b1b8f24e493c9f543a7cc2ca5eaa9\"\n        \"ea208de5ce933340c4f7df1151694c8f24edf4abd8ca09db389b14f4afa2a3be7be8ea\"\n        \"0f384384a324c75569278ab5a9a29d48714db5525e63c7bdde225a59d36b9786fc6e09\"\n        \"686a79a29884ae47e0cb69a020f9e796cf13fcc53ef08ba265d121a9f7bb35021eb098\"\n        \"09\";\n    EXPECT_EQ(expectedChecksum, toHex(*h.getChecksum()));\n  } else {\n    FAIL() << \"Unexpected element size and/or count: B=\"\n           << h.getElementSizeInBits() << \", N=\" << h.getElementCount()\n           << \", checksum=\" << toHex(*h.getChecksum());\n  }\n}\n\nTYPED_TEST(LtHashTest, setKeyTooShort) {\n  TypeParam h1;\n  std::string shortKey = \"0123456789abcde\";\n  EXPECT_THROW(h1.setKey(folly::range(shortKey)), std::runtime_error);\n}\n\nTYPED_TEST(LtHashTest, setKeyTooLong) {\n  TypeParam h1;\n  std::string longKey =\n      \"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0\";\n  EXPECT_THROW(h1.setKey(folly::range(longKey)), std::runtime_error);\n}\n\nTYPED_TEST(LtHashTest, subtractWithDifferentKeysFail) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n\n  TypeParam h2;\n  h2.setKey(folly::range(\"0123456789abcdef\"));\n  h2.addObject(folly::range(this->obj1_));\n\n  EXPECT_THROW(h1 - h2, std::runtime_error);\n}\n\nTYPED_TEST(LtHashTest, addWithDifferentKeysFail) {\n  TypeParam h1;\n  h1.addObject(folly::range(this->obj1_));\n\n  TypeParam h2;\n  h2.setKey(folly::range(\"0123456789abcdef\"));\n  h2.addObject(folly::range(this->obj1_));\n\n  EXPECT_THROW(h1 + h2, std::runtime_error);\n}\n\nTYPED_TEST(LtHashTest, keyedLtHashesEqual) {\n  std::string key = \"0123456789abcdef\";\n  TypeParam h1;\n  h1.setKey(folly::range(key));\n  h1.addObject(folly::range(this->obj1_));\n  h1.addObject(folly::range(this->obj2_));\n\n  TypeParam h2;\n  h2.setKey(folly::range(key));\n  h2.addObject(folly::range(this->obj1_));\n  h2.addObject(folly::range(this->obj2_));\n\n  EXPECT_EQ(h1, h2);\n}\n\nTYPED_TEST(LtHashTest, keyedLtHashesDifferentKeysNotEqual) {\n  std::string key1 = \"0123456789abcdef\";\n  TypeParam h1;\n  h1.setKey(folly::range(key1));\n  h1.addObject(folly::range(this->obj1_));\n  h1.addObject(folly::range(this->obj2_));\n\n  std::string key2 = \"1123456789abcdef\";\n  TypeParam h2;\n  h1.setKey(folly::range(key2));\n  h2.addObject(folly::range(this->obj1_));\n  h2.addObject(folly::range(this->obj2_));\n\n  EXPECT_NE(h1, h2);\n\n  // Compare to unkeyed LtHash\n  TypeParam h3;\n  h3.addObject(folly::range(this->obj1_));\n  h3.addObject(folly::range(this->obj2_));\n\n  EXPECT_NE(h1, h3);\n}\n"
  },
  {
    "path": "folly/debugging/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nadd_subdirectory(exception_tracer)\nadd_subdirectory(symbolizer)\n"
  },
  {
    "path": "folly/debugging/exception_tracer/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\n    \"@fbsource//tools/build_defs:platform_defs.bzl\",\n    \"IOS\",\n    \"MACOSX\",\n)\nload(\"@fbsource//tools/build_defs:selects.bzl\", \"selects\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"compatibility\",\n    srcs = [\n    ],\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"Compatibility.h\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_tracer_base\",\n    srcs = [\n        \"ExceptionTracer.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"ExceptionTracer.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/experimental/symbolizer:symbolizer\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:cpp_attributes\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly/debugging/exception_tracer:exception_abi\",\n        \"//xplat/folly/debugging/exception_tracer:stacktrace\",\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"exception_abi\",\n    headers = [\"ExceptionAbi.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":compatibility\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"smart_exception_tracer\",\n    srcs = [\n        \"SmartExceptionTracer.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    raw_headers = [\n        \"SmartExceptionTracer.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/experimental/symbolizer:symbolizer\",\n        \"//xplat/folly:exception_wrapper\",\n        \"//xplat/folly:map_util\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_base\",\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_callbacks\",\n        \"//xplat/folly/debugging/exception_tracer:smart_exception_tracer_singleton\",\n        \"//xplat/folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"smart_exception_tracer_singleton\",\n    srcs = [\n        \"SmartExceptionTracerSingleton.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"SmartExceptionTracerSingleton.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:synchronized\",\n        \"//xplat/folly/container:f14_hash\",\n        \"//xplat/folly/debugging/exception_tracer:stacktrace\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"smart_exception_stack_trace_hooks\",\n    srcs = [\n        \"SmartExceptionStackTraceHooks.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    deps = [\n        \"fbsource//xplat/folly/experimental/symbolizer:symbolizer\",\n        \":exception_tracer_callbacks\",\n        \":smart_exception_tracer_singleton\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_counter\",\n    srcs = [\n        \"ExceptionCounterLib.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    raw_headers = [\n        \"ExceptionCounterLib.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/experimental/symbolizer:symbolizer\",\n        \"fbsource//xplat/folly/hash:spooky_hash_v2\",\n        \"fbsource//xplat/folly/synchronization:rw_spin_lock\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:singleton_thread_local\",\n        \"//xplat/folly:synchronized\",\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_base\",\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_callbacks\",\n        \"//xplat/folly/debugging/exception_tracer:stacktrace\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_tracer\",\n    srcs = [\n        \"ExceptionStackTraceLib.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    deps = [\n        \"fbsource//xplat/folly/experimental/symbolizer:symbolizer\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/debugging/exception_tracer:exception_abi\",\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_base\",\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_callbacks\",\n        \"//xplat/folly/debugging/exception_tracer:stacktrace\",\n        \"//xplat/folly/lang:exception\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"stacktrace\",\n    srcs = [\n        \"StackTrace.cpp\",\n    ],\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"StackTrace.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/experimental/symbolizer:stack_trace\",\n        \"//xplat/folly:portability\",\n    ],\n)\n\nFBCODE_STATIC_LIBSTDCXX_SELECTOR = selects.and_(\n    selects.cond(\"ovr_config//runtime/constraints:fbcode\"),\n    selects.cond(\"ovr_config//cpp:libstdc++\"),\n    selects.or_(\n        selects.cond(\"fbsource//arvr/third-party/toolchains/platform010/build/libgcc:libgcc-static\"),\n        selects.cond(\"ovr_config//third-party/libgcc:11.x\"),\n    ),\n)\n\nFOLLY_SHOULD_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS = selects.or_(\n    selects.cond(\"fbsource//xplat/toolchains/android/ndk:cxx-static-runtime-type\"),\n    FBCODE_STATIC_LIBSTDCXX_SELECTOR,\n)\n\nFOLLY_SHOULD_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER = selects.or_(\n    selects.cond(\"fbsource//xplat/toolchains/android/ndk:cxx-static-runtime-type\"),\n    FBCODE_STATIC_LIBSTDCXX_SELECTOR,\n)\n\nEXCEPTION_TRACER_LIB_COMPILER_FLAGS = selects.if_(\n    FOLLY_SHOULD_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS,\n    [\"-DFOLLY_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS=1\"],\n    [],\n) + selects.if_(\n    FOLLY_SHOULD_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER,\n    [\"-DFOLLY_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER=1\"],\n    [],\n)\n\nEXCEPTION_TRACER_LIB_EXPORTED_LINKER_FLAGS = selects.if_(\n    FOLLY_SHOULD_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS,\n    [\n        # @fb-only[end= ]: \"-Wl,--wrap=__cxa_throw\",\n        # @fb-only[end= ]: \"-Wl,--wrap=__cxa_rethrow\",\n        # @fb-only[end= ]: \"-Wl,--wrap=__cxa_begin_catch\",\n        # @fb-only[end= ]: \"-Wl,--wrap=__cxa_end_catch\",\n    ],\n    [],\n) + selects.if_(\n    FOLLY_SHOULD_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER,\n    select({\n        \"DEFAULT\": [\"-Wl,--wrap=_ZSt17rethrow_exceptionSt13exception_ptr\"],\n        \"ovr_config//cpp:libstdc++\": [\"-Wl,--wrap=_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE\"],\n    }),\n    [],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_tracer_callbacks\",\n    srcs = [\n        \"ExceptionTracerLib.cpp\",\n    ],\n    compiler_flags = EXCEPTION_TRACER_LIB_COMPILER_FLAGS,\n    exported_linker_flags = EXCEPTION_TRACER_LIB_EXPORTED_LINKER_FLAGS,\n    link_whole = True,\n    raw_headers = [\n        \"ExceptionTracerLib.h\",\n    ],\n    deps = [\n        \"//xplat/folly:indestructible\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:shared_mutex\",\n        \"//xplat/folly:synchronized\",\n        \"//xplat/folly/debugging/exception_tracer:compatibility\",\n        \"//xplat/third-party/linker_lib:dl\",\n    ],\n)\n\n# !!!! fbcode/folly/debugging/exception_tracer/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"compatibility\",\n    srcs = [],\n    headers = [\"Compatibility.h\"],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_counter\",\n    srcs = [\"ExceptionCounterLib.cpp\"],\n    headers = [\"ExceptionCounterLib.h\"],\n    deps = [\n        \":exception_tracer_callbacks\",\n        \":stacktrace\",\n        \"//folly:range\",\n        \"//folly:singleton_thread_local\",\n        \"//folly:synchronized\",\n        \"//folly/experimental/symbolizer:symbolizer\",\n        \"//folly/hash:spooky_hash_v2\",\n        \"//folly/synchronization:rw_spin_lock\",\n    ],\n    exported_deps = [\n        \":compatibility\",\n        \":exception_tracer_base\",\n        \":exception_tracer_callbacks\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_counter_static_registration\",\n    srcs = [\"ExceptionCounterLibStaticRegistration.cpp\"],\n    link_whole = True,\n    deps = [\n        \":exception_counter\",\n        \":exception_tracer_callbacks\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer\",\n    srcs = [\"ExceptionStackTraceLib.cpp\"],\n    headers = [],\n    link_whole = True,\n    deps = [\n        \":exception_abi\",\n        \":exception_tracer_base\",\n        \":exception_tracer_callbacks\",\n        \":stacktrace\",\n        \"//folly:utility\",\n        \"//folly/lang:exception\",\n    ],\n    exported_deps = [\n        \":exception_tracer_callbacks\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer_base\",\n    srcs = [\"ExceptionTracer.cpp\"],\n    headers = [\"ExceptionTracer.h\"],\n    # Usage of dlsym(RTLD_NEXT, \"symbol\") does not always work with link groups\n    # due to relying on link order. Excluding all libs with `RTLD_NEXT` usage from any link group.\n    labels = [\"EXCLUDED_FROM_LINK_GROUPS\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":exception_abi\",\n        \":stacktrace\",\n        \"//folly:portability\",\n        \"//folly:string\",\n        \"//folly/experimental/symbolizer:symbolizer\",\n    ],\n    exported_deps = [\n        \"//folly/portability:config\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n    ],\n)\n\nLIBSTDCXX_BUCKIFIED_SELECTOR = selects.and_(\n    selects.cond(\"ovr_config//cpp:libstdc++\"),\n    selects.cond(\"ovr_config//third-party/libgcc:11.x\"),\n)\n\nLIBCXX_BUCKIFIED_SELECTOR = selects.and_(\n    selects.cond(\"ovr_config//cpp:libc++\"),\n    selects.or_(\n        selects.cond(\"ovr_config//third-party/libcxx:17\"),\n        selects.cond(\"ovr_config//third-party/libcxx:19\"),\n        selects.cond(\"ovr_config//third-party/libcxx:21\"),\n    ),\n)\n\nSTATIC_CXX_STDLIB_SELECTOR = selects.or_(\n    LIBSTDCXX_BUCKIFIED_SELECTOR,\n    LIBCXX_BUCKIFIED_SELECTOR,\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer_callbacks\",\n    srcs = [\"ExceptionTracerLib.cpp\"],\n    headers = [\"ExceptionTracerLib.h\"],\n    compiler_flags = selects.if_(\n        STATIC_CXX_STDLIB_SELECTOR,\n        select({\n            \"DEFAULT\": [],\n            \"ovr_config//cpp:libc++\": [\n                \"-DFOLLY_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS=1\",\n                \"-DFOLLY_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER=1\",\n            ],\n            \"ovr_config//cpp:libstdc++\": [\n                \"-DFOLLY_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS=1\",\n                \"-DFOLLY_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER=1\",\n            ],\n        }),\n        [],\n    ),\n    exported_linker_flags = selects.if_(\n        STATIC_CXX_STDLIB_SELECTOR,\n        select({\n            \"DEFAULT\": [],\n            \"ovr_config//cpp:libc++\": [\n                \"-Wl,--wrap=__cxa_throw\",\n                \"-Wl,--wrap=__cxa_rethrow\",\n                \"-Wl,--wrap=__cxa_begin_catch\",\n                \"-Wl,--wrap=__cxa_end_catch\",\n                \"-Wl,--wrap=_ZSt17rethrow_exceptionSt13exception_ptr\",\n            ],\n            \"ovr_config//cpp:libstdc++\": [\n                \"-Wl,--wrap=__cxa_throw\",\n                \"-Wl,--wrap=__cxa_rethrow\",\n                \"-Wl,--wrap=__cxa_begin_catch\",\n                \"-Wl,--wrap=__cxa_end_catch\",\n                \"-Wl,--wrap=_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE\",\n            ],\n        }),\n        [],\n    ),\n    labels = [\"EXCLUDED_FROM_LINK_GROUPS\"],\n    link_whole = True,\n    supports_python_dlopen = True,\n    deps = [\n        \"//folly:indestructible\",\n        \"//folly:portability\",\n        \"//folly:shared_mutex\",\n        \"//folly:synchronized\",\n    ],\n    exported_deps = [\n        \":compatibility\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"stacktrace\",\n    srcs = [\"StackTrace.cpp\"],\n    headers = [\"StackTrace.h\"],\n    deps = [\n        \"//folly/experimental/symbolizer:stack_trace\",\n    ],\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"smart_exception_tracer_singleton\",\n    srcs = [\n        \"SmartExceptionTracerSingleton.cpp\",\n    ],\n    headers = [\n        \"SmartExceptionTracerSingleton.h\",\n    ],\n    exported_deps = [\n        \":stacktrace\",\n        \"//folly:synchronized\",\n        \"//folly/container:f14_hash\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"smart_exception_stack_trace_hooks\",\n    srcs = [\n        \"SmartExceptionStackTraceHooks.cpp\",\n    ],\n    link_whole = True,\n    deps = [\n        \":exception_tracer_callbacks\",\n        \":smart_exception_tracer_singleton\",\n        \"//folly/experimental/symbolizer:symbolizer\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"smart_exception_tracer\",\n    srcs = [\"SmartExceptionTracer.cpp\"],\n    headers = [\"SmartExceptionTracer.h\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":exception_tracer_callbacks\",\n        \":smart_exception_tracer_singleton\",\n        \":stacktrace\",\n        \"//folly:map_util\",\n        \"//folly:scope_guard\",\n        \"//folly:synchronized\",\n        \"//folly/container:f14_hash\",\n        \"//folly/lang:exception\",\n    ],\n    exported_deps = [\n        \":compatibility\",\n        \":exception_tracer_base\",\n        \":exception_tracer_callbacks\",\n        \"//folly:exception_wrapper\",\n    ],\n)\n"
  },
  {
    "path": "folly/debugging/exception_tracer/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\n# Exception tracer is excluded when FOLLY_NO_EXCEPTION_TRACER is set\nif (NOT FOLLY_NO_EXCEPTION_TRACER)\n\nfolly_add_library(\n  NAME compatibility\n  HEADERS\n    Compatibility.h\n)\n\nfolly_add_library(\n  NAME exception_abi\n  HEADERS\n    ExceptionAbi.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_compatibility\n)\n\nfolly_add_library(\n  NAME exception_counter\n  SRCS\n    ExceptionCounterLib.cpp\n  HEADERS\n    ExceptionCounterLib.h\n  DEPS\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n    folly_debugging_exception_tracer_stacktrace\n    folly_experimental_symbolizer_symbolizer\n    folly_hash_spooky_hash_v2\n    folly_range\n    folly_singleton_thread_local\n    folly_synchronization_rw_spin_lock\n    folly_synchronized\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_compatibility\n    folly_debugging_exception_tracer_exception_tracer_base\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n)\n\nfolly_add_library(\n  NAME exception_counter_static_registration\n  SRCS\n    ExceptionCounterLibStaticRegistration.cpp\n  DEPS\n    folly_debugging_exception_tracer_exception_counter\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n)\n\nfolly_add_library(\n  NAME exception_tracer\n  SRCS\n    ExceptionStackTraceLib.cpp\n  DEPS\n    folly_debugging_exception_tracer_exception_abi\n    folly_debugging_exception_tracer_exception_tracer_base\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n    folly_debugging_exception_tracer_stacktrace\n    folly_lang_exception\n    folly_utility\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n)\n\nfolly_add_library(\n  NAME exception_tracer_base\n  SRCS\n    ExceptionTracer.cpp\n  HEADERS\n    ExceptionTracer.h\n  DEPS\n    folly_debugging_exception_tracer_exception_abi\n    folly_debugging_exception_tracer_stacktrace\n    folly_experimental_symbolizer_symbolizer\n    folly_portability\n    folly_string\n  EXPORTED_DEPS\n    folly_portability_config\n)\n\nfolly_add_library(\n  NAME exception_tracer_callbacks\n  SRCS\n    ExceptionTracerLib.cpp\n  HEADERS\n    ExceptionTracerLib.h\n  DEPS\n    folly_indestructible\n    folly_portability\n    folly_shared_mutex\n    folly_synchronized\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_compatibility\n)\n\nfolly_add_library(\n  NAME smart_exception_stack_trace_hooks\n  SRCS\n    SmartExceptionStackTraceHooks.cpp\n  DEPS\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n    folly_debugging_exception_tracer_smart_exception_tracer_singleton\n    folly_experimental_symbolizer_symbolizer\n)\n\nfolly_add_library(\n  NAME smart_exception_tracer\n  SRCS\n    SmartExceptionTracer.cpp\n  HEADERS\n    SmartExceptionTracer.h\n  DEPS\n    folly_container_f14_hash\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n    folly_debugging_exception_tracer_smart_exception_tracer_singleton\n    folly_debugging_exception_tracer_stacktrace\n    folly_lang_exception\n    folly_map_util\n    folly_scope_guard\n    folly_synchronized\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_compatibility\n    folly_debugging_exception_tracer_exception_tracer_base\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n    folly_exception_wrapper\n)\n\nfolly_add_library(\n  NAME smart_exception_tracer_singleton\n  SRCS\n    SmartExceptionTracerSingleton.cpp\n  HEADERS\n    SmartExceptionTracerSingleton.h\n  EXPORTED_DEPS\n    folly_container_f14_hash\n    folly_debugging_exception_tracer_stacktrace\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME stacktrace\n  SRCS\n    StackTrace.cpp\n  HEADERS\n    StackTrace.h\n  DEPS\n    folly_experimental_symbolizer_stack_trace\n  EXPORTED_DEPS\n    folly_portability\n)\n\nendif() # NOT FOLLY_NO_EXCEPTION_TRACER\n"
  },
  {
    "path": "folly/debugging/exception_tracer/Compatibility.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <version>\n\n#if (defined(__GLIBCXX__) || defined(_LIBCPP_VERSION))\n#define FOLLY_DETAIL_EXN_TRACER_CXX_STDLIB_COMPATIBLE 1\n#else\n#define FOLLY_DETAIL_EXN_TRACER_CXX_STDLIB_COMPATIBLE 0\n#endif // (defined(__GLIBCXX__) || defined(_LIBCPP_VERSION))\n\n#if (!defined(__FreeBSD__) && !_WIN32)\n#define FOLLY_DETAIL_EXN_TRACER_OS_CXX_ABI_COMPATIBLE 1\n#else\n#define FOLLY_DETAIL_EXN_TRACER_OS_CXX_ABI_COMPATIBLE 0\n#endif // (!defined(__FreeBSD__) && ! _WIN32)\n\n#if FOLLY_DETAIL_EXN_TRACER_CXX_STDLIB_COMPATIBLE && \\\n    FOLLY_DETAIL_EXN_TRACER_OS_CXX_ABI_COMPATIBLE\n#define FOLLY_HAS_EXCEPTION_TRACER 1\n#else\n#define FOLLY_HAS_EXCEPTION_TRACER 0\n#endif // CXX_STDLIB_COMPATIBLE && OS_CXX_ABI_COMPATIBLE\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionAbi.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// A clone of the relevant parts of unwind-cxx.h from libstdc++\n// The layout of these structures is defined by the ABI.\n\n#include <exception>\n#include <typeinfo>\n\n#include <folly/debugging/exception_tracer/Compatibility.h>\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\n#include <unwind.h>\n\nnamespace __cxxabiv1 {\n\nstruct __cxa_exception {\n// Unlike other implementations, GCC's libsupc++ doesn't have this code.\n// See gcc/libstdc++-v3/libsupc++/unwind-cxx.h\n#if !defined(__GLIBCXX__)\n#if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI)\n  // Now _Unwind_Exception is marked with __attribute__((aligned)),\n  // which implies __cxa_exception is also aligned. Insert padding\n  // in the beginning of the struct, rather than before unwindHeader.\n  void* reserve;\n\n  // This is a new field to support C++11 exception_ptr.\n  // For binary compatibility it is at the start of this\n  // struct which is prepended to the object thrown in\n  // __cxa_allocate_exception.\n  size_t referenceCount;\n#endif // defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI)\n#endif // !defined(__GLIBCXX__)\n\n  std::type_info* exceptionType;\n  void (*exceptionDestructor)(void*);\n  void (*unexpectedHandler)(); // std::unexpected_handler has been removed from\n                               // C++17.\n  std::terminate_handler terminateHandler;\n  __cxa_exception* nextException;\n\n  int handlerCount;\n  int handlerSwitchValue;\n  const char* actionRecord;\n  const char* languageSpecificData;\n  void* catchTemp;\n  void* adjustedPtr;\n\n  _Unwind_Exception unwindHeader;\n};\n\nstruct __cxa_eh_globals {\n  __cxa_exception* caughtExceptions;\n  unsigned int uncaughtExceptions;\n};\n\nextern \"C\" {\n__cxa_eh_globals* __cxa_get_globals(void) noexcept;\n__cxa_eh_globals* __cxa_get_globals_fast(void) noexcept;\n}\n\n} // namespace __cxxabiv1\n\n#endif // FOLLY_HAS_EXCEPTION_TRACER\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionCounterLib.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionCounterLib.h>\n\n#include <iosfwd>\n#include <unordered_map>\n\n#include <folly/Range.h>\n#include <folly/SingletonThreadLocal.h>\n#include <folly/Synchronized.h>\n#include <folly/hash/SpookyHashV2.h>\n#include <folly/synchronization/RWSpinLock.h>\n\n#include <folly/debugging/exception_tracer/Compatibility.h>\n#include <folly/debugging/exception_tracer/StackTrace.h>\n#include <folly/experimental/symbolizer/Symbolizer.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nusing namespace folly::exception_tracer;\n\nnamespace {\n\n// We use the hash of stack trace and exception type to uniquely\n// identify the exception.\nusing ExceptionId = uint64_t;\n\nusing ExceptionStatsHolderType =\n    std::unordered_map<ExceptionId, ExceptionStats>;\n\nstruct ExceptionStatsStorage {\n  void appendTo(ExceptionStatsHolderType& data) {\n    ExceptionStatsHolderType tempHolder;\n    statsHolder.wlock()->swap(tempHolder);\n\n    for (const auto& myData : tempHolder) {\n      auto inserted = data.insert(myData);\n      if (!inserted.second) {\n        inserted.first->second.count += myData.second.count;\n      }\n    }\n  }\n\n  folly::Synchronized<ExceptionStatsHolderType, folly::RWSpinLock> statsHolder;\n};\n\nclass Tag {};\n\nusing ExceptionStatsTL =\n    folly::SingletonThreadLocal<ExceptionStatsStorage, Tag>;\n\n} // namespace\n\nnamespace folly {\nnamespace exception_tracer {\n\nstd::vector<ExceptionStats> getExceptionStatistics() {\n  ExceptionStatsHolderType accumulator;\n  for (auto& threadStats : ExceptionStatsTL::accessAllThreads()) {\n    threadStats.appendTo(accumulator);\n  }\n\n  std::vector<ExceptionStats> result;\n  result.reserve(accumulator.size());\n  for (auto& item : accumulator) {\n    result.push_back(std::move(item.second));\n  }\n\n  std::sort(\n      result.begin(),\n      result.end(),\n      [](const ExceptionStats& lhs, const ExceptionStats& rhs) {\n        return lhs.count > rhs.count;\n      });\n\n  return result;\n}\n\nstd::ostream& operator<<(std::ostream& out, const ExceptionStats& stats) {\n  out << \"Exception report: \\n\"\n      << \"Exception count: \" << stats.count << \"\\n\"\n      << stats.info;\n\n  return out;\n}\n\n/*\n * This handler gathers statistics on all exceptions thrown by the program\n * Information is being stored in thread local storage.\n */\nvoid exceptionStatsThrowHandler(\n    void*, std::type_info* exType, void (**)(void*)) noexcept {\n  // This array contains the exception type and the stack frame\n  // pointers so they get all hashed together.\n  uintptr_t frames[kMaxFrames + 1];\n  frames[0] = reinterpret_cast<uintptr_t>(exType);\n  auto n = folly::symbolizer::getStackTrace(frames + 1, kMaxFrames);\n\n  if (n == -1) {\n    // If we fail to collect the stack trace for this exception we\n    // just log it under empty stack trace.\n    n = 0;\n  }\n\n  auto exceptionId =\n      folly::hash::SpookyHashV2::Hash64(frames, (n + 1) * sizeof(frames[0]), 0);\n\n  ExceptionStatsTL::get().statsHolder.withWLock([&](auto& holder) {\n    auto it = holder.find(exceptionId);\n    if (it != holder.end()) {\n      ++it->second.count;\n    } else {\n      ExceptionInfo info;\n      info.type = exType;\n      info.frames.assign(frames + 1, frames + 1 + n);\n      holder.emplace(exceptionId, ExceptionStats{1, std::move(info)});\n    }\n  });\n}\n\n} // namespace exception_tracer\n} // namespace folly\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionCounterLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <ostream>\n#include <vector>\n\n#include <folly/debugging/exception_tracer/Compatibility.h>\n#include <folly/debugging/exception_tracer/ExceptionTracer.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nnamespace folly {\nnamespace exception_tracer {\n\nstruct ExceptionStats {\n  uint64_t count;\n  ExceptionInfo info;\n};\n\n/**\n * Throw handler that intercepts exception throwing and accumulates stats.\n * Use with registerCxaThrowCallback/unregisterCxaThrowCallback.\n */\nvoid exceptionStatsThrowHandler(\n    void*, std::type_info* exType, void (**)(void*)) noexcept;\n\n/**\n * This function accumulates exception throwing statistics across all threads.\n * Please note, that during call to this function, other threads might block\n * on exception throws, so it should be called seldomly.\n * All pef-thread statistics is being reset by the call.\n */\nstd::vector<ExceptionStats> getExceptionStatistics();\n\nstd::ostream& operator<<(std::ostream& out, const ExceptionStats& stats);\n\n} // namespace exception_tracer\n} // namespace folly\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionCounterLibStaticRegistration.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionCounterLib.h>\n#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF && FOLLY_HAS_EXCEPTION_TRACER\n\nnamespace {\n\nstruct Initializer {\n  Initializer() {\n    folly::exception_tracer::registerCxaThrowCallback(\n        folly::exception_tracer::exceptionStatsThrowHandler);\n  }\n};\n\nInitializer initializer;\n\n} // namespace\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF && FOLLY_HAS_EXCEPTION_TRACER\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionStackTraceLib.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <exception>\n#include <type_traits>\n\n#include <folly/Utility.h>\n#include <folly/debugging/exception_tracer/ExceptionAbi.h>\n#include <folly/debugging/exception_tracer/ExceptionTracer.h>\n#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>\n#include <folly/debugging/exception_tracer/StackTrace.h>\n#include <folly/lang/Exception.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nusing namespace folly::exception_tracer;\n\nnamespace {\n\n// If we somehow ended up in an invalid state, we don't want to print any stack\n// trace at all because in could be bogus\nthread_local bool invalid;\n\nthread_local StackTraceStack uncaughtExceptions;\nthread_local StackTraceStack caughtExceptions;\n\n// StackTraceStack should be usable as thread_local for the entire lifetime of\n// a thread, and not just up until thread_local variables are destroyed\nstatic_assert(std::is_trivially_destructible_v<StackTraceStack>);\nstatic_assert(folly::is_constexpr_default_constructible_v<StackTraceStack>);\n\n} // namespace\n\n// These functions are exported and may be found via dlsym(RTLD_NEXT, ...)\nextern \"C\" const StackTraceStack*\nfolly_exception_tracer_get_uncaught_exceptions_stack_trace_stack() {\n  return invalid ? nullptr : &uncaughtExceptions;\n}\nextern \"C\" const StackTraceStack*\nfolly_exception_tracer_get_caught_exceptions_stack_trace_stack() {\n  return invalid ? nullptr : &caughtExceptions;\n}\n\nnamespace folly {\n\nnamespace detail {\n/// access_cxxabi\n///\n/// When building folly with thread sanitizer but using a prebuilt libstdc++ or\n/// libc++, folly's accesses here are instrumented while the standard library's\n/// accesses are not. This can cause false positive data-race reports. This\n/// namespace is here as an easy way to set up thread-sanitizer suppressions.\n///\n/// One scenario is when the __cxa_end_catch folly hook does an access, then the\n/// last std::exception_ptr to the same exception is released. The exception_ptr\n/// tor does an uninstrumented access and then calls free, which like malloc is\n/// instrumented. The thread-sanitizer runtime observes only an access in folly\n/// in one thread followed by a call to free in another thread, but does not\n/// observe the synchronizing refcount-decrement in between.\n///\n/// Aside from suppressions, there are two other solutions:\n/// * Never access cxxabi data directly.\n/// * Use an instrumented build of the standard library when building folly or\n///   the application with instrumentation.\nnamespace access_cxxabi {\n\nstatic auto get_caught_exceptions_handler_count() {\n  return __cxxabiv1::__cxa_get_globals_fast()->caughtExceptions->handlerCount;\n}\n\n} // namespace access_cxxabi\n\n} // namespace detail\n\nnamespace {\n\nsize_t stackDepth(const StackTraceStack& stack) {\n  size_t depth = 0;\n  auto* p = stack.top();\n  while (p) {\n    ++depth;\n    p = stack.next(p);\n  }\n  return depth;\n}\n\nvoid addActiveException() {\n  // Capture stack trace\n  if (!invalid) {\n    // When __cxa_begin_catch is bypassed (e.g. ASAN stubs or libc++ internal\n    // calls that don't go through --wrap), traces can accumulate on the\n    // uncaught stack without being moved to the caught stack or freed. Drain\n    // any orphaned entries by comparing our stack depth with the actual\n    // uncaught count. Our callback fires before __real___cxa_throw increments\n    // the count, so the expected depth equals std::uncaught_exceptions().\n    auto expected = static_cast<size_t>(folly::uncaught_exceptions());\n    auto depth = stackDepth(uncaughtExceptions);\n    while (depth > expected) {\n      uncaughtExceptions.pop();\n      --depth;\n    }\n\n    if (!uncaughtExceptions.pushCurrent()) {\n      uncaughtExceptions.clear();\n      caughtExceptions.clear();\n      invalid = true;\n    }\n  }\n}\n\nvoid moveTopException(StackTraceStack& from, StackTraceStack& to) {\n  if (invalid) {\n    return;\n  }\n  if (!to.moveTopFrom(from)) {\n    from.clear();\n    to.clear();\n    invalid = true;\n  }\n}\n\nstruct Initializer {\n  Initializer() {\n    registerCxaThrowCallback(\n        *+[](void*, std::type_info*, void (**)(void*)) noexcept {\n          addActiveException();\n        });\n\n    registerCxaBeginCatchCallback(*+[](void*) noexcept {\n      moveTopException(uncaughtExceptions, caughtExceptions);\n    });\n\n    registerCxaRethrowCallback(*+[]() noexcept {\n      moveTopException(caughtExceptions, uncaughtExceptions);\n    });\n\n    registerCxaEndCatchCallback(*+[]() noexcept {\n      if (invalid) {\n        return;\n      }\n\n      auto topHandlerCount =\n          detail::access_cxxabi::get_caught_exceptions_handler_count();\n      // This is gcc specific and not specified in the ABI:\n      // abs(handlerCount) is the number of active handlers, it's negative\n      // for rethrown exceptions and positive (always 1) for regular\n      // exceptions.\n      // In the rethrow case, we've already popped the exception off the\n      // caught stack, so we don't do anything here.\n      // For Lua interop, we see the handlerCount = 0\n      if ((topHandlerCount == 1) || (topHandlerCount == 0)) {\n        if (!caughtExceptions.pop()) {\n          uncaughtExceptions.clear();\n          invalid = true;\n        }\n      }\n    });\n\n    registerRethrowExceptionCallback(*+[](std::exception_ptr) noexcept {\n      addActiveException();\n    });\n\n    try {\n      ::folly::exception_tracer::installHandlers();\n    } catch (...) {\n    }\n  }\n};\n\nInitializer initializer;\n\n} // namespace\n\n} // namespace folly\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionTracer.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionTracer.h>\n\n#include <cstdlib>\n#include <exception>\n#include <iostream>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/String.h>\n#include <folly/debugging/exception_tracer/ExceptionAbi.h>\n#include <folly/debugging/exception_tracer/StackTrace.h>\n#include <folly/experimental/symbolizer/Symbolizer.h>\n\n#if __has_include(<dlfcn.h>)\n#include <dlfcn.h>\n#endif\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nnamespace {\n\nusing namespace ::folly::exception_tracer;\nusing namespace ::folly::symbolizer;\nusing namespace __cxxabiv1;\n\nextern \"C\" {\nconst StackTraceStack*\nfolly_exception_tracer_get_caught_exceptions_stack_trace_stack(void)\n    __attribute__((__weak__));\nconst StackTraceStack*\nfolly_exception_tracer_get_uncaught_exceptions_stack_trace_stack(void)\n    __attribute__((__weak__));\ntypedef const StackTraceStack* (*GetExceptionStackTraceStackType)();\nGetExceptionStackTraceStackType getCaughtExceptionStackTraceStackFn;\nGetExceptionStackTraceStackType getUncaughtExceptionStackTraceStackFn;\n}\n\n} // namespace\n\nnamespace folly {\nnamespace exception_tracer {\n\nstd::ostream& operator<<(std::ostream& out, const ExceptionInfo& info) {\n  printExceptionInfo(out, info, SymbolizePrinter::COLOR_IF_TTY);\n  return out;\n}\n\nvoid printExceptionInfo(\n    std::ostream& out, const ExceptionInfo& info, int options) {\n  out << \"Exception type: \";\n  if (info.type) {\n    out << folly::demangle(*info.type);\n  } else {\n    out << \"(unknown type)\";\n  }\n  static constexpr size_t kInternalFramesNumber = 3;\n\n  // Skip our own internal frames.\n  size_t frameCount = info.frames.size();\n  if (frameCount <= kInternalFramesNumber) {\n    out << \"\\n\";\n    return;\n  }\n  auto addresses = info.frames.data() + kInternalFramesNumber;\n  frameCount -= kInternalFramesNumber;\n\n  out << \" (\" << frameCount << (frameCount == 1 ? \" frame\" : \" frames\")\n      << \")\\n\";\n  try {\n    std::vector<SymbolizedFrame> frames;\n    frames.resize(frameCount);\n\n    Symbolizer symbolizer(\n        (options & SymbolizePrinter::NO_FILE_AND_LINE)\n            ? LocationInfoMode::DISABLED\n            : Symbolizer::kDefaultLocationInfoMode);\n    symbolizer.symbolize(addresses, frames.data(), frameCount);\n\n    OStreamSymbolizePrinter osp(out, options);\n    osp.println(frames.data(), frameCount);\n  } catch (const std::exception& e) {\n    out << \"\\n !! caught \" << folly::exceptionStr(e) << \"\\n\";\n  } catch (...) {\n    out << \"\\n !!! caught unexpected exception\\n\";\n  }\n}\n\nnamespace {\n\n/**\n * Is this a standard C++ ABI exception?\n *\n * Dependent exceptions (thrown via std::rethrow_exception) aren't --\n * exc doesn't actually point to a __cxa_exception structure, but\n * the offset of unwindHeader is correct, so exc->unwindHeader actually\n * returns a _Unwind_Exception object.  Yeah, it's ugly like that.\n *\n * Type of exception_class depends on ABI: on some it is defined as a\n * native endian uint64_t, on others a big endian char[8].\n */\nstruct ArmAbiTag {};\nstruct AnyAbiTag {};\n\n[[maybe_unused]] bool isAbiCppException(ArmAbiTag, const char (&klazz)[8]) {\n  return klazz[4] == 'C' && klazz[5] == '+' && klazz[6] == '+' &&\n      klazz[7] == '\\0';\n}\n\n[[maybe_unused]] bool isAbiCppException(AnyAbiTag, const uint64_t& klazz) {\n  // The least significant four bytes must be \"C++\\0\"\n  static const uint64_t cppClass =\n      ((uint64_t)'C' << 24) | ((uint64_t)'+' << 16) | ((uint64_t)'+' << 8);\n  return (klazz & 0xffffffff) == cppClass;\n}\n\nbool isAbiCppException(const __cxa_exception* exc) {\n  using tag = std::conditional_t<kIsArchArm, ArmAbiTag, AnyAbiTag>;\n  return isAbiCppException(tag{}, exc->unwindHeader.exception_class);\n}\n\n} // namespace\n\nstd::vector<ExceptionInfo> getCurrentExceptions() {\n  struct Once {\n    Once() {\n      // See if linked in with us\n      // (folly_exception_tracer_get_caught_exceptions_stack_trace_stack is\n      // weak)\n      getCaughtExceptionStackTraceStackFn =\n          folly_exception_tracer_get_caught_exceptions_stack_trace_stack;\n\n      if (!getCaughtExceptionStackTraceStackFn) {\n        // Nope, see if it's in a shared library\n        getCaughtExceptionStackTraceStackFn =\n            (GetExceptionStackTraceStackType)dlsym(\n                RTLD_NEXT,\n                \"folly_exception_tracer_get_caught_exceptions_stack_trace_stack\");\n      }\n\n      getUncaughtExceptionStackTraceStackFn =\n          folly_exception_tracer_get_uncaught_exceptions_stack_trace_stack;\n\n      if (!getUncaughtExceptionStackTraceStackFn) {\n        getUncaughtExceptionStackTraceStackFn =\n            (GetExceptionStackTraceStackType)dlsym(\n                RTLD_NEXT,\n                \"folly_exception_tracer_get_uncaught_exceptions_stack_trace_stack\");\n      }\n    }\n  };\n  static Once once;\n\n  std::vector<ExceptionInfo> exceptions;\n  auto currentException = __cxa_get_globals()->caughtExceptions;\n  if (!currentException) {\n    return exceptions;\n  }\n\n  const StackTraceStack* traceStack = nullptr;\n  if (!getCaughtExceptionStackTraceStackFn) {\n    static bool logged = false;\n    if (!logged) {\n      LOG(WARNING)\n          << \"Exception tracer library not linked, stack traces not available\";\n      logged = true;\n    }\n  } else if ((traceStack = getCaughtExceptionStackTraceStackFn()) == nullptr) {\n    static bool logged = false;\n    if (!logged) {\n      LOG(WARNING)\n          << \"Exception stack trace invalid, stack traces not available\";\n      logged = true;\n    }\n  }\n\n  const StackTrace* trace = traceStack ? traceStack->top() : nullptr;\n  // Fallback: if the caught trace stack is empty but there are caught\n  // exceptions, the C++ runtime's internal __cxa_begin_catch call may have\n  // bypassed our hook (e.g., libc++abi calls __cxa_begin_catch from within\n  // __cxa_throw for uncaught exceptions, and that intra-DSO call is not\n  // intercepted by symbol interposition). Try the uncaught trace stack.\n  if (traceStack && !trace && currentException &&\n      getUncaughtExceptionStackTraceStackFn) {\n    if (auto uncaughtTraceStack = getUncaughtExceptionStackTraceStackFn()) {\n      if (uncaughtTraceStack->top()) {\n        traceStack = uncaughtTraceStack;\n        trace = traceStack->top();\n      }\n    }\n  }\n  while (currentException) {\n    ExceptionInfo info;\n    // Dependent exceptions (thrown via std::rethrow_exception) aren't\n    // standard ABI __cxa_exception objects, and are correctly labeled as\n    // such in the exception_class field.  We could try to extract the\n    // primary exception type in horribly hacky ways, but, for now, nullptr.\n    info.type = isAbiCppException(currentException)\n        ? currentException->exceptionType\n        : nullptr;\n\n    if (traceStack) {\n      LOG_IF(WARNING, !trace)\n          << \"Invalid trace stack for exception of type: \"\n          << (info.type ? folly::demangle(*info.type) : \"null\");\n\n      if (!trace) {\n        return {};\n      }\n\n      info.frames.assign(\n          trace->addresses, trace->addresses + trace->frameCount);\n      trace = traceStack->next(trace);\n    }\n    currentException = currentException->nextException;\n    exceptions.push_back(std::move(info));\n  }\n\n  LOG_IF(WARNING, trace) << \"Invalid trace stack!\";\n\n  return exceptions;\n}\n\nnamespace {\n\nstd::terminate_handler origTerminate = abort;\n\nvoid dumpExceptionStack(const char* prefix) {\n  auto exceptions = getCurrentExceptions();\n  if (exceptions.empty()) {\n    return;\n  }\n  LOG(ERROR) << prefix << \", exception stack follows\";\n  for (auto& exc : exceptions) {\n    LOG(ERROR) << exc << \"\\n\";\n  }\n  LOG(ERROR) << \"exception stack complete\";\n}\n\nvoid terminateHandler() {\n  dumpExceptionStack(\"terminate() called\");\n  origTerminate();\n}\n\n} // namespace\n\nvoid installHandlers() {\n  struct Once {\n    Once() { origTerminate = std::set_terminate(terminateHandler); }\n  };\n  static Once once;\n}\n\n} // namespace exception_tracer\n} // namespace folly\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionTracer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Exception tracer library.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n#include <typeinfo>\n#include <vector>\n\n#include <folly/portability/Config.h>\n\nnamespace folly {\nnamespace exception_tracer {\n\nstruct ExceptionInfo {\n  const std::type_info* type{nullptr};\n  // The values in frames are IP (instruction pointer) addresses.\n  // They are only filled if the low-level exception tracer library is\n  // linked in or LD_PRELOADed.\n  std::vector<uintptr_t> frames; // front() is top of stack\n};\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nvoid printExceptionInfo(\n    std::ostream& out, const ExceptionInfo& info, int options);\nstd::ostream& operator<<(std::ostream& out, const ExceptionInfo& info);\n\n/**\n * Get current exceptions being handled.  front() is the most recent exception.\n * There should be at most one unless rethrowing.\n */\nstd::vector<ExceptionInfo> getCurrentExceptions();\n\n/**\n * Install the terminate / unexpected handlers to dump exceptions.\n */\nvoid installHandlers();\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n} // namespace exception_tracer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionTracerLib.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>\n\n#include <vector>\n\n#include <folly/Indestructible.h>\n#include <folly/Portability.h>\n#include <folly/SharedMutex.h>\n#include <folly/Synchronized.h>\n\n#if __has_include(<dlfcn.h>)\n#include <dlfcn.h>\n#endif\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nnamespace __cxxabiv1 {\n\nextern \"C\" {\n#ifdef FOLLY_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS\n[[noreturn]] void __real___cxa_throw(\n    void* thrownException, std::type_info* type, void (*destructor)(void*));\nvoid* __real___cxa_begin_catch(void* excObj) noexcept;\n[[noreturn]] void __real___cxa_rethrow(void);\nvoid __real___cxa_end_catch(void);\n#else\n__attribute__((__noreturn__)) void __cxa_throw(\n    void* thrownException, std::type_info* type, void (*destructor)(void*));\nvoid* __cxa_begin_catch(void* excObj) noexcept;\n__attribute__((__noreturn__)) void __cxa_rethrow(void);\nvoid __cxa_end_catch(void);\n#endif\n}\n\n} // namespace __cxxabiv1\n\n#ifdef FOLLY_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER\nextern \"C\" {\n#if __GLIBCXX__\n[[noreturn]] void\n__real__ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE(\n    std::exception_ptr ep);\n#endif\n\n#if _LIBCPP_VERSION\n[[noreturn]] void __real__ZSt17rethrow_exceptionSt13exception_ptr(\n    std::exception_ptr ep);\n#endif\n} // extern \"C\"\n\n#endif\n\nusing namespace folly::exception_tracer;\n\nnamespace {\n\ntemplate <typename Sig>\nclass CallbackHolder {\n public:\n  void registerCallback(Sig& f) { callbacks_.wlock()->push_back(f); }\n  void unregisterCallback(Sig& f) {\n    auto callbacks = callbacks_.wlock();\n    std::erase(*callbacks, f);\n  }\n\n  // always inline to enforce kInternalFramesNumber\n  template <typename... Args>\n  FOLLY_ALWAYS_INLINE void invoke(Args... args) {\n    auto callbacksLock = callbacks_.rlock();\n    for (auto& cb : *callbacksLock) {\n      cb(args...);\n    }\n  }\n\n private:\n  folly::Synchronized<std::vector<Sig*>> callbacks_;\n};\n\n} // namespace\n\nnamespace folly {\nnamespace exception_tracer {\n\n#define FOLLY_EXNTRACE_DECLARE_CALLBACK(NAME)                   \\\n  CallbackHolder<NAME##Sig>& get##NAME##Callbacks() {           \\\n    static Indestructible<CallbackHolder<NAME##Sig>> Callbacks; \\\n    return *Callbacks;                                          \\\n  }                                                             \\\n  void register##NAME##Callback(NAME##Sig& callback) {          \\\n    get##NAME##Callbacks().registerCallback(callback);          \\\n  }                                                             \\\n  void unregister##NAME##Callback(NAME##Sig& callback) {        \\\n    get##NAME##Callbacks().unregisterCallback(callback);        \\\n  }\n\nFOLLY_EXNTRACE_DECLARE_CALLBACK(CxaThrow)\nFOLLY_EXNTRACE_DECLARE_CALLBACK(CxaBeginCatch)\nFOLLY_EXNTRACE_DECLARE_CALLBACK(CxaRethrow)\nFOLLY_EXNTRACE_DECLARE_CALLBACK(CxaEndCatch)\nFOLLY_EXNTRACE_DECLARE_CALLBACK(RethrowException)\n\n#undef FOLLY_EXNTRACE_DECLARE_CALLBACK\n\n} // namespace exception_tracer\n} // namespace folly\n\nnamespace __cxxabiv1 {\n\n#ifdef FOLLY_INSTALL_STATIC_CXA_FUNCTIONS_WRAPPERS\nextern \"C\" {\n\n[[noreturn]] void __wrap___cxa_throw(\n    void* thrownException, std::type_info* type, void (*destructor)(void*)) {\n  getCxaThrowCallbacks().invoke(thrownException, type, &destructor);\n  __real___cxa_throw(thrownException, type, destructor);\n  __builtin_unreachable(); // orig_cxa_throw never returns\n}\n\n[[noreturn]] void __wrap___cxa_rethrow() {\n  // __cxa_rethrow leaves the current exception on the caught stack,\n  // and __cxa_begin_catch recognizes that case.  We could do the same, but\n  // we'll implement something simpler (and slower): we pop the exception from\n  // the caught stack, and push it back onto the active stack; this way, our\n  // implementation of __cxa_begin_catch doesn't have to do anything special.\n  getCxaRethrowCallbacks().invoke();\n  __real___cxa_rethrow();\n  __builtin_unreachable(); // orig_cxa_rethrow never returns\n}\n\nvoid* __wrap___cxa_begin_catch(void* excObj) noexcept {\n  // excObj is a pointer to the unwindHeader in __cxa_exception\n  getCxaBeginCatchCallbacks().invoke(excObj);\n  return __real___cxa_begin_catch(excObj);\n}\n\nvoid __wrap___cxa_end_catch() {\n  getCxaEndCatchCallbacks().invoke();\n  __real___cxa_end_catch();\n}\n}\n\n#else\n\n__attribute__((__noreturn__)) void __cxa_throw(\n    void* thrownException, std::type_info* type, void (*destructor)(void*)) {\n  static auto orig_cxa_throw =\n      reinterpret_cast<decltype(&__cxa_throw)>(dlsym(RTLD_NEXT, \"__cxa_throw\"));\n  getCxaThrowCallbacks().invoke(thrownException, type, &destructor);\n  orig_cxa_throw(thrownException, type, destructor);\n  __builtin_unreachable(); // orig_cxa_throw never returns\n}\n\n__attribute__((__noreturn__)) void __cxa_rethrow() {\n  // __cxa_rethrow leaves the current exception on the caught stack,\n  // and __cxa_begin_catch recognizes that case.  We could do the same, but\n  // we'll implement something simpler (and slower): we pop the exception from\n  // the caught stack, and push it back onto the active stack; this way, our\n  // implementation of __cxa_begin_catch doesn't have to do anything special.\n  static auto orig_cxa_rethrow = reinterpret_cast<decltype(&__cxa_rethrow)>(\n      dlsym(RTLD_NEXT, \"__cxa_rethrow\"));\n  getCxaRethrowCallbacks().invoke();\n  orig_cxa_rethrow();\n  __builtin_unreachable(); // orig_cxa_rethrow never returns\n}\n\nvoid* __cxa_begin_catch(void* excObj) noexcept {\n  // excObj is a pointer to the unwindHeader in __cxa_exception\n  static auto orig_cxa_begin_catch =\n      reinterpret_cast<decltype(&__cxa_begin_catch)>(\n          dlsym(RTLD_NEXT, \"__cxa_begin_catch\"));\n  getCxaBeginCatchCallbacks().invoke(excObj);\n  return orig_cxa_begin_catch(excObj);\n}\n\nvoid __cxa_end_catch() {\n  static auto orig_cxa_end_catch = reinterpret_cast<decltype(&__cxa_end_catch)>(\n      dlsym(RTLD_NEXT, \"__cxa_end_catch\"));\n  getCxaEndCatchCallbacks().invoke();\n  orig_cxa_end_catch();\n}\n#endif\n\n} // namespace __cxxabiv1\n\n#ifdef FOLLY_INSTALL_STD_RETHROW_EXCEPTION_WRAPPER\n// Mangled name for std::rethrow_exception\n// TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr\n// is typedef'ed to a type in namespace __exception_ptr\nextern \"C\" {\n\n#ifdef __GLIBCXX__\n[[noreturn]] void\n__wrap__ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE(\n    std::exception_ptr ep) {\n  getRethrowExceptionCallbacks().invoke(ep);\n  __real__ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE(ep);\n  __builtin_unreachable(); // orig_rethrow_exception never returns\n}\n#endif\n\n#ifdef _LIBCPP_VERSION\n[[noreturn]] void __wrap__ZSt17rethrow_exceptionSt13exception_ptr(\n    std::exception_ptr ep) {\n  getRethrowExceptionCallbacks().invoke(ep);\n  __real__ZSt17rethrow_exceptionSt13exception_ptr(ep);\n  __builtin_unreachable(); // orig_rethrow_exception never returns\n}\n#endif\n}\n\n#else\n\nnamespace folly {\nconstexpr const char* kRethrowExceptionMangledName = kIsGlibcxx\n    ? \"_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE\"\n    : \"_ZSt17rethrow_exceptionSt13exception_ptr\";\n}\n\nnamespace std {\n\n__attribute__((__noreturn__)) void rethrow_exception(std::exception_ptr ep) {\n  // Mangled name for std::rethrow_exception\n  // TODO(tudorb): Dicey, as it relies on the fact that std::exception_ptr\n  // is typedef'ed to a type in namespace __exception_ptr\n  static auto orig_rethrow_exception =\n      reinterpret_cast<decltype(&rethrow_exception)>(\n          dlsym(RTLD_NEXT, folly::kRethrowExceptionMangledName));\n  getRethrowExceptionCallbacks().invoke(ep);\n  orig_rethrow_exception(std::move(ep));\n  __builtin_unreachable(); // orig_rethrow_exception never returns\n}\n\n} // namespace std\n#endif\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n"
  },
  {
    "path": "folly/debugging/exception_tracer/ExceptionTracerLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <exception>\n#include <typeinfo>\n\n#include <folly/debugging/exception_tracer/Compatibility.h>\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nnamespace folly {\nnamespace exception_tracer {\n\nusing CxaThrowSig = void(void*, std::type_info*, void (**)(void*)) noexcept;\nusing CxaBeginCatchSig = void(void*) noexcept;\nusing CxaRethrowSig = void() noexcept;\nusing CxaEndCatchSig = void() noexcept;\nusing RethrowExceptionSig = void(std::exception_ptr) noexcept;\n\nvoid registerCxaThrowCallback(CxaThrowSig& callback);\nvoid registerCxaBeginCatchCallback(CxaBeginCatchSig& callback);\nvoid registerCxaRethrowCallback(CxaRethrowSig& callback);\nvoid registerCxaEndCatchCallback(CxaEndCatchSig& callback);\nvoid registerRethrowExceptionCallback(RethrowExceptionSig& callback);\nvoid unregisterCxaThrowCallback(CxaThrowSig& callback);\nvoid unregisterCxaBeginCatchCallback(CxaBeginCatchSig& callback);\nvoid unregisterCxaRethrowCallback(CxaRethrowSig& callback);\nvoid unregisterCxaEndCatchCallback(CxaEndCatchSig& callback);\nvoid unregisterRethrowExceptionCallback(RethrowExceptionSig& callback);\n\n} // namespace exception_tracer\n} // namespace folly\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n"
  },
  {
    "path": "folly/debugging/exception_tracer/README.md",
    "content": "Exception tracer library\n\nThis library allows you to inspect the exception stack at runtime.\nThe library can be used in three ways:\n\n1. Link in the exception_tracer_base library.  You get access to the functions\nin ExceptionTracer.h, but no stack traces.  This has no runtime overhead,\nand is compliant with the C++ ABI.\n\n2. Link in the (full) exception_tracer library.  You get access to the\nfunctions in ExceptionTracer.h, the std::terminate and std::unexpected\nhandlers are installed by default, and you get full stack traces with\nall exceptions.  This has some runtime overhead (the stack trace must be\ncaptured and stored whenever an exception is thrown) added to throw\nand catch, but no runtime overhead otherwise.  This is less portable\n(depends on internal details of GNU's libstdc++).\n\n3. LD_PRELOAD libexceptiontracer.so.  This is equivalent to #2 above, but\nrequires no link-time changes.  On the other hand, you need to ensure that\nlibexceptiontracer.so is compiled with the same compiler and flags as\nyour binary, and the usual caveats about LD_PRELOAD apply (it propagates\nto child processes, etc).\n"
  },
  {
    "path": "folly/debugging/exception_tracer/SmartExceptionStackTraceHooks.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>\n#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h>\n#include <folly/experimental/symbolizer/Symbolizer.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nnamespace folly::exception_tracer {\n\nnamespace {\n\nvoid metaDeleter(void* ex) noexcept {\n  auto meta = detail::getMetaMap().withWLock([ex](auto& locked) noexcept {\n    auto iter = locked.find(ex);\n    CHECK(iter != locked.end());\n    auto ret = std::move(iter->second);\n    locked.erase(iter);\n    return ret;\n  });\n\n  // If the thrown object was allocated statically it may not have a deleter.\n  CHECK(meta);\n  if (meta->deleter) {\n    meta->deleter(ex);\n  }\n}\n\n// This callback runs when an exception is thrown so we can grab the stack\n// trace. To manage the lifetime of the stack trace we override the deleter with\n// our own wrapper.\nvoid throwCallback(\n    void* ex, std::type_info*, void (**deleter)(void*)) noexcept {\n  // Make this code reentrant safe in case we throw an exception while\n  // handling an exception. Thread local variables are zero initialized.\n  static thread_local bool handlingThrow;\n  if (handlingThrow) {\n    return;\n  }\n  SCOPE_EXIT {\n    handlingThrow = false;\n  };\n  handlingThrow = true;\n\n  try {\n    // This can allocate memory potentially causing problems in an OOM\n    // situation so we catch and short circuit.\n    auto meta = std::make_shared<detail::ExceptionMeta>();\n\n    // Override the deleter with our custom one and capture the old one.\n    meta->deleter = std::exchange(*deleter, metaDeleter);\n\n    ssize_t n =\n        folly::symbolizer::getStackTrace(meta->trace.addresses, kMaxFrames);\n    if (n != -1) {\n      meta->trace.frameCount = n;\n    }\n    ssize_t nAsync = folly::symbolizer::getAsyncStackTraceSafe(\n        meta->traceAsync.addresses, kMaxFrames);\n    if (nAsync != -1) {\n      meta->traceAsync.frameCount = nAsync;\n    }\n\n    detail::getMetaMap().withWLock([&](auto& wlock) {\n      CHECK(wlock.try_emplace(ex, std::move(meta)).second);\n    });\n  } catch (const std::bad_alloc&) {\n  }\n}\n\nstruct Initialize {\n  Initialize() {\n    registerCxaThrowCallback(throwCallback);\n    detail::setSmartExceptionTracerHookEnabled(true);\n  }\n};\n\nInitialize initialize;\n\n} // namespace\n} // namespace folly::exception_tracer\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/SmartExceptionTracer.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/SmartExceptionTracer.h>\n\n#include <glog/logging.h>\n#include <folly/MapUtil.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Synchronized.h>\n#include <folly/container/F14Map.h>\n#include <folly/debugging/exception_tracer/ExceptionTracerLib.h>\n#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h>\n#include <folly/debugging/exception_tracer/StackTrace.h>\n#include <folly/lang/Exception.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\nnamespace folly {\nnamespace exception_tracer {\nnamespace {\n\nstd::atomic_bool loggedMessage{false};\n\n// ExceptionMetaFunc takes a `const ExceptionMeta&` and\n// returns a pair of iterators to represent a range of addresses for\n// the stack frames to use.\ntemplate <typename ExceptionMetaFunc>\nExceptionInfo getTraceWithFunc(\n    void* ex, const std::type_info* typeInfo, ExceptionMetaFunc func) {\n  if (!detail::isSmartExceptionTracerHookEnabled() &&\n      !loggedMessage.load(std::memory_order_relaxed)) {\n    LOG(WARNING)\n        << \"Smart exception tracer library not linked, stack traces not available\";\n    loggedMessage = true;\n  }\n\n  ExceptionInfo info;\n  info.type = typeInfo;\n\n  if (auto meta = get_default(*detail::getMetaMap().rlock(), ex)) {\n    auto [traceBeginIt, traceEndIt] = func(*meta);\n    info.frames.assign(traceBeginIt, traceEndIt);\n  }\n\n  return info;\n}\n\ntemplate <typename ExceptionMetaFunc>\nExceptionInfo getTraceWithFunc(\n    const std::exception_ptr& ptr, ExceptionMetaFunc func) {\n  if (auto ex = folly::exception_ptr_get_object(ptr, nullptr)) {\n    return getTraceWithFunc(\n        ex, folly::exception_ptr_get_type(ptr), std::move(func));\n  }\n  return ExceptionInfo();\n}\n\ntemplate <typename ExceptionMetaFunc>\nExceptionInfo getTraceWithFunc(\n    const std::exception& e, ExceptionMetaFunc func) {\n  return getTraceWithFunc(\n      const_cast<void*>(dynamic_cast<const void*>(&e)),\n      &typeid(e),\n      std::move(func));\n}\n\ntemplate <typename ExceptionMetaFunc>\nExceptionInfo getTraceWithFunc(\n    const exception_wrapper& ew, ExceptionMetaFunc func) {\n  return getTraceWithFunc(ew.exception_ptr(), std::move(func));\n}\n\nauto getAsyncStackTraceItPair(const detail::ExceptionMeta& meta) {\n  auto addr = meta.traceAsync.addresses;\n  return std::pair(addr, addr + meta.traceAsync.frameCount);\n}\n\nauto getNormalStackTraceItPair(const detail::ExceptionMeta& meta) {\n  auto addr = meta.trace.addresses;\n  return std::pair(addr, addr + meta.trace.frameCount);\n}\n\n} // namespace\n\nExceptionInfo getTrace(const std::exception_ptr& ptr) {\n  return getTraceWithFunc(ptr, getNormalStackTraceItPair);\n}\n\nExceptionInfo getTrace(const exception_wrapper& ew) {\n  return getTraceWithFunc(ew, getNormalStackTraceItPair);\n}\n\nExceptionInfo getTrace(const std::exception& ex) {\n  return getTraceWithFunc(ex, getNormalStackTraceItPair);\n}\n\nExceptionInfo getAsyncTrace(const std::exception_ptr& ptr) {\n  return getTraceWithFunc(ptr, getAsyncStackTraceItPair);\n}\n\nExceptionInfo getAsyncTrace(const exception_wrapper& ew) {\n  return getTraceWithFunc(ew, getAsyncStackTraceItPair);\n}\n\nExceptionInfo getAsyncTrace(const std::exception& ex) {\n  return getTraceWithFunc(ex, getAsyncStackTraceItPair);\n}\n\n} // namespace exception_tracer\n} // namespace folly\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/SmartExceptionTracer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/debugging/exception_tracer/Compatibility.h>\n#include <folly/debugging/exception_tracer/ExceptionTracer.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAS_EXCEPTION_TRACER\n\n#define FOLLY_HAVE_SMART_EXCEPTION_TRACER 1\n\n// These functions report stack traces if available.\n// To enable collecting stack traces your binary must also include the\n// smart_exception_stack_trace_hooks target.\n\nnamespace folly::exception_tracer {\n\nExceptionInfo getTrace(const std::exception_ptr& ex);\n\nExceptionInfo getTrace(const std::exception& ex);\n\nExceptionInfo getTrace(const exception_wrapper& ew);\n\nExceptionInfo getAsyncTrace(const std::exception_ptr& ex);\n\nExceptionInfo getAsyncTrace(const std::exception& ex);\n\nExceptionInfo getAsyncTrace(const exception_wrapper& ew);\n\n} // namespace folly::exception_tracer\n\n#endif //  FOLLY_HAS_EXCEPTION_TRACER\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/SmartExceptionTracerSingleton.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h>\n\nextern \"C\" {\n\nfolly::exception_tracer::detail::ExceptionMetaMap*\n    __folly_smart_exception_store{nullptr};\n}\n\nnamespace folly::exception_tracer::detail {\n\nSynchronized<ExceptionMetaMap>& getMetaMap() {\n  static Indestructible<std::unique_ptr<Synchronized<ExceptionMetaMap>>> meta(\n      folly::factory_constructor, []() {\n        auto ret = std::make_unique<folly::Synchronized<ExceptionMetaMap>>();\n        __folly_smart_exception_store = &ret->unsafeGetUnlocked();\n        return ret;\n      });\n  return **meta;\n}\n\nstatic std::atomic_bool hookEnabled{false};\n\nbool isSmartExceptionTracerHookEnabled() {\n  return hookEnabled.load(std::memory_order_relaxed);\n}\nvoid setSmartExceptionTracerHookEnabled(bool enabled) {\n  hookEnabled = enabled;\n}\n\n} // namespace folly::exception_tracer::detail\n"
  },
  {
    "path": "folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Synchronized.h>\n#include <folly/container/F14Map.h>\n#include <folly/debugging/exception_tracer/StackTrace.h>\n\nnamespace folly::exception_tracer::detail {\n\nstruct ExceptionMeta {\n  void (*deleter)(void*);\n  // normal stack trace\n  StackTrace trace;\n  // async stack trace\n  StackTrace traceAsync;\n};\n\n// using a vector-map so that all the values are laid out contiguously in an\n// array, specifically to make debugging easier - no need to learn the structure\n// of the f14-chunk-array when debugging the exception hooks\nusing ExceptionMetaMap =\n    folly::F14VectorMap<void const*, std::shared_ptr<ExceptionMeta const>>;\n\nSynchronized<ExceptionMetaMap>& getMetaMap();\n\nbool isSmartExceptionTracerHookEnabled();\nvoid setSmartExceptionTracerHookEnabled(bool enabled);\n\n} // namespace folly::exception_tracer::detail\n"
  },
  {
    "path": "folly/debugging/exception_tracer/StackTrace.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/StackTrace.h>\n\n#include <cassert>\n#include <cstdlib>\n#include <new>\n\n#include <folly/experimental/symbolizer/StackTrace.h>\n\nnamespace folly {\nnamespace exception_tracer {\n\nclass StackTraceStack::Node : public StackTrace {\n public:\n  static Node* allocate();\n  void deallocate();\n\n  Node* next;\n\n private:\n  Node() : next(nullptr) {}\n  ~Node() = default;\n};\n\nauto StackTraceStack::Node::allocate() -> Node* {\n  // Null pointer on error, please.\n  return new (std::nothrow) Node();\n}\n\nvoid StackTraceStack::Node::deallocate() {\n  delete this;\n}\n\nbool StackTraceStack::pushCurrent() {\n  auto node = Node::allocate();\n  if (!node) {\n    // cannot allocate memory\n    return false;\n  }\n\n  ssize_t n = folly::symbolizer::getStackTrace(node->addresses, kMaxFrames);\n  if (n == -1) {\n    node->deallocate();\n    return false;\n  }\n  node->frameCount = n;\n\n  node->next = state_;\n  state_ = node;\n  return true;\n}\n\nbool StackTraceStack::pop() {\n  if (!state_) {\n    return false;\n  }\n\n  auto node = state_;\n  state_ = node->next;\n  node->deallocate();\n  return true;\n}\n\nbool StackTraceStack::moveTopFrom(StackTraceStack& other) {\n  if (!other.state_) {\n    return false;\n  }\n\n  auto node = other.state_;\n  other.state_ = node->next;\n  node->next = state_;\n  state_ = node;\n  return true;\n}\n\nvoid StackTraceStack::clear() {\n  while (state_) {\n    pop();\n  }\n}\n\nStackTrace* StackTraceStack::top() {\n  return state_;\n}\n\nconst StackTrace* StackTraceStack::top() const {\n  return state_;\n}\n\nStackTrace* StackTraceStack::next(StackTrace* p) {\n  assert(p);\n  return static_cast<Node*>(p)->next;\n}\n\nconst StackTrace* StackTraceStack::next(const StackTrace* p) const {\n  assert(p);\n  return static_cast<const Node*>(p)->next;\n}\n} // namespace exception_tracer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/exception_tracer/StackTrace.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n\n#include <folly/Portability.h>\n\nnamespace folly {\nnamespace exception_tracer {\n\nconstexpr size_t kMaxFrames = 500;\n\nstruct StackTrace {\n  StackTrace() : frameCount(0) {}\n\n  size_t frameCount;\n  uintptr_t addresses[kMaxFrames];\n};\n\nclass StackTraceStack {\n  class Node;\n\n public:\n  constexpr StackTraceStack() = default;\n\n  StackTraceStack(const StackTraceStack&) = delete;\n  void operator=(const StackTraceStack&) = delete;\n\n  /**\n   * Push the current stack trace onto the stack.\n   * Returns false on failure (not enough memory, getting stack trace failed),\n   * true on success.\n   */\n  bool pushCurrent();\n\n  /**\n   * Pop the top stack trace from the stack.\n   * Returns true on success, false on failure (stack was empty).\n   */\n  bool pop();\n\n  /**\n   * Move the top stack trace from other onto this.\n   * Returns true on success, false on failure (other was empty).\n   */\n  bool moveTopFrom(StackTraceStack& other);\n\n  /**\n   * Clear the stack.\n   */\n\n  void clear();\n\n  /**\n   * Is the stack empty?\n   */\n  bool empty() const { return !state_; }\n\n  /**\n   * Return the top stack trace, or nullptr if the stack is empty.\n   */\n  StackTrace* top();\n  const StackTrace* top() const;\n\n  /**\n   * Return the stack trace following p, or nullptr if p is the bottom of\n   * the stack.\n   */\n  StackTrace* next(StackTrace* p);\n  const StackTrace* next(const StackTrace* p) const;\n\n private:\n  Node* state_ = nullptr;\n};\n} // namespace exception_tracer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbcode_macros//build_defs:python_unittest.bzl\", \"python_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"exception_counter_test\",\n    srcs = [\"ExceptionCounterTest.cpp\"],\n    deps = [\n        \"//folly/debugging/exception_tracer:exception_counter\",\n        \"//folly/debugging/exception_tracer:exception_counter_static_registration\",  # @manual\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer_benchmark_main\",\n    srcs = [\"ExceptionTracerBenchmark.cpp\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly/debugging/exception_tracer:exception_tracer_base\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"exception_tracer_base_benchmark\",\n    deps = [\n        \":exception_tracer_benchmark_main\",  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"exception_tracer_benchmark\",\n    deps = [\n        \":exception_tracer_benchmark_main\",  # @manual\n        \"//folly/debugging/exception_tracer:exception_tracer\",  # @manual  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer_test_main\",\n    srcs = [\"ExceptionTracerTest.cpp\"],\n    deps = [\n        \"//folly/debugging/exception_tracer:exception_tracer_base\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"exception_tracer_base_test\",\n    deps = [\n        \":exception_tracer_test_main\",  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"exception_tracer_test\",\n    deps = [\n        \":exception_tracer_test_main\",  # @manual\n        \"//folly/debugging/exception_tracer:exception_tracer\",  # @manual  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"smart_exception_tracer_test\",\n    srcs = [\"SmartExceptionTracerTest.cpp\"],\n    deps = [\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:task\",\n        \"//folly/debugging/exception_tracer:smart_exception_stack_trace_hooks\",  # @manual\n        \"//folly/debugging/exception_tracer:smart_exception_tracer\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"smart_exception_tracer_benchmark\",\n    srcs = [\"SmartExceptionTracerBenchmark.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:exception_wrapper\",\n        \"//folly/debugging/exception_tracer:smart_exception_stack_trace_hooks\",  # @manual\n        \"//folly/debugging/exception_tracer:smart_exception_tracer\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"exception_tracer_uncaught_test_bin\",\n    srcs = [\"ExceptionTracerUncaughtTest.cpp\"],\n    deps = [\n        \"//folly/debugging/exception_tracer:exception_tracer\",  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = python_unittest,\n    name = \"exception_tracer_uncaught_test\",\n    srcs = [\"exception_tracer_uncaught_test.py\"],\n    env = {\n        \"EXCEPTION_TRACER_TEST_BIN\": \"$(location :exception_tracer_uncaught_test_bin)\",\n    },\n)\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/ExceptionCounterTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <condition_variable>\n#include <mutex>\n#include <sstream>\n#include <stdexcept>\n#include <thread>\n\n#include <folly/debugging/exception_tracer/ExceptionCounterLib.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nstruct MyException {};\n\n// clang-format off\n[[noreturn]] void bar() {\n  throw std::runtime_error(\"hello\");\n}\n\n[[noreturn]] void foo() {\n  throw MyException();\n}\n\n[[noreturn]] void baz() {\n  foo();\n}\n// clang-format on\n\nusing namespace folly::exception_tracer;\n\ntemplate <typename F>\nvoid throwAndCatch(F f) {\n  try {\n    f();\n  } catch (...) {\n    // ignore\n  }\n}\n\nTEST(ExceptionCounter, oneThread) {\n  throwAndCatch(foo);\n\n  // Use volatile to prevent loop unrolling (it screws up stack frame grouping).\n  for (volatile int i = 0; i < 10; ++i) {\n    throwAndCatch(bar);\n  }\n\n  auto stats = getExceptionStatistics();\n  EXPECT_EQ(stats.size(), 2);\n  EXPECT_EQ(stats[0].count, 10);\n  EXPECT_EQ(stats[1].count, 1);\n  EXPECT_EQ(*(stats[0].info.type), typeid(std::runtime_error));\n  EXPECT_EQ(*(stats[1].info.type), typeid(MyException));\n}\n\nTEST(ExceptionCounter, testClearExceptionStatistics) {\n  throwAndCatch(foo);\n  auto stats = getExceptionStatistics();\n  EXPECT_EQ(stats.size(), 1);\n  stats = getExceptionStatistics();\n  EXPECT_EQ(stats.size(), 0);\n}\n\nTEST(ExceptionCounter, testDifferentStacks) {\n  throwAndCatch(foo);\n  throwAndCatch(baz);\n  auto stats = getExceptionStatistics();\n  EXPECT_EQ(stats.size(), 2);\n}\n\nTEST(ExceptionCounter, multyThreads) {\n  constexpr size_t kNumIterations = 10000;\n  constexpr size_t kNumThreads = 10;\n  std::vector<std::thread> threads;\n  threads.resize(kNumThreads);\n\n  std::mutex preparedMutex;\n  std::mutex finishedMutex;\n  std::condition_variable preparedBarrier;\n  std::condition_variable finishedBarrier;\n  int preparedThreads = 0;\n  bool finished = false;\n\n  for (auto& t : threads) {\n    t = std::thread([&]() {\n      for (size_t i = 0; i < kNumIterations; ++i) {\n        throwAndCatch(foo);\n      }\n\n      {\n        std::unique_lock lock(preparedMutex);\n        ++preparedThreads;\n        preparedBarrier.notify_one();\n      }\n\n      std::unique_lock lock(finishedMutex);\n      finishedBarrier.wait(lock, [&]() { return finished; });\n    });\n  }\n\n  {\n    std::unique_lock lock(preparedMutex);\n    preparedBarrier.wait(lock, [&]() {\n      return preparedThreads == kNumThreads;\n    });\n  }\n\n  auto stats = getExceptionStatistics();\n  EXPECT_EQ(stats.size(), 1);\n  EXPECT_EQ(stats[0].count, kNumIterations * kNumThreads);\n\n  {\n    std::unique_lock lock(finishedMutex);\n    finished = true;\n    finishedBarrier.notify_all();\n  }\n\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/ExceptionTracerBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdexcept>\n#include <thread>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/debugging/exception_tracer/ExceptionTracer.h>\n#include <folly/portability/GFlags.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nvoid recurse(int level) {\n  if (level == 0) {\n    throw std::runtime_error(\"\");\n  }\n  recurse(level - 1);\n  folly::doNotOptimizeAway(0); // prevent tail recursion\n}\n\nvoid loop(int iters) {\n  for (int i = 0; i < iters * 100; ++i) {\n    try {\n      recurse(100);\n    } catch (const std::exception&) {\n      folly::exception_tracer::getCurrentExceptions();\n    }\n  }\n}\n\nBENCHMARK(ExceptionTracer, iters) {\n  std::vector<std::thread> threads;\n  constexpr size_t kNumThreads = 10;\n  threads.resize(kNumThreads);\n  for (auto& t : threads) {\n    t = std::thread([iters]() { loop(iters); });\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  google::InitGoogleLogging(argv[0]);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/ExceptionTracerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <stdexcept>\n\n#include <folly/debugging/exception_tracer/ExceptionTracer.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n// clang-format off\n[[noreturn]] void bar() {\n  throw std::runtime_error(\"hello\");\n}\n// clang-format on\n\nvoid dumpExceptions(const char* prefix) {\n  std::cerr << \"--- \" << prefix << \"\\n\";\n  auto exceptions = ::folly::exception_tracer::getCurrentExceptions();\n  for (auto& exc : exceptions) {\n    std::cerr << exc << \"\\n\";\n  }\n}\n\nvoid foo() {\n  try {\n    try {\n      bar();\n    } catch (const std::exception&) {\n      dumpExceptions(\"foo: simple catch\");\n      bar();\n    }\n  } catch (const std::exception&) {\n    dumpExceptions(\"foo: catch, exception thrown from previous catch block\");\n  }\n}\n\n[[noreturn]] void baz() {\n  try {\n    try {\n      bar();\n    } catch (...) {\n      dumpExceptions(\"baz: simple catch\");\n      throw;\n    }\n  } catch (const std::exception&) {\n    dumpExceptions(\"baz: catch rethrown exception\");\n    throw \"hello\";\n  }\n}\n\nvoid testExceptionPtr1() {\n  std::exception_ptr exc;\n  try {\n    bar();\n  } catch (...) {\n    exc = std::current_exception();\n  }\n\n  try {\n    std::rethrow_exception(exc);\n  } catch (...) {\n    dumpExceptions(\"std::rethrow_exception 1\");\n  }\n}\n\nvoid testExceptionPtr2() {\n  std::exception_ptr exc;\n  try {\n    throw std::out_of_range(\"x\");\n  } catch (...) {\n    exc = std::current_exception();\n  }\n\n  try {\n    std::rethrow_exception(exc);\n  } catch (...) {\n    dumpExceptions(\"std::rethrow_exception 2\");\n  }\n}\n\nint main(int /* argc */, char* /* argv */[]) {\n  foo();\n  testExceptionPtr1();\n  testExceptionPtr2();\n  baz();\n  // no return because baz() is [[noreturn]]\n}\n\n#else // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nint main(int /* argc */, char* /* argv */[]) {}\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/ExceptionTracerUncaughtTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdexcept>\n\n__attribute__((noinline)) [[noreturn]] void foo() {\n  throw std::runtime_error(\"error!\");\n}\n\nint main() {\n  foo();\n}\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/SmartExceptionTracerBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdexcept>\n#include <vector>\n\n#include <folly/Benchmark.h>\n#include <folly/ExceptionWrapper.h>\n#include <folly/debugging/exception_tracer/SmartExceptionTracer.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAVE_SMART_EXCEPTION_TRACER\n\nusing namespace folly::exception_tracer;\n\nnamespace {\n\n[[noreturn]] FOLLY_NOINLINE void throwRuntimeError() {\n  throw std::runtime_error(\"benchmark exception\");\n}\n\nFOLLY_NOINLINE ExceptionInfo recurse(int n) {\n  try {\n    if (n == 0) {\n      throwRuntimeError();\n    }\n    return recurse(n - 1);\n  } catch (const std::bad_exception&) {\n    // unreachable\n    throw;\n  }\n}\n\nExceptionInfo makeExceptionWithStackDepthMeasureThrow(int n) {\n  try {\n    return recurse(n);\n  } catch (...) {\n    folly::BenchmarkSuspender s;\n    return getTrace(std::current_exception());\n  }\n}\n\nExceptionInfo makeExceptionRefWithStackDepthMeasureRetrieve(int n) {\n  folly::BenchmarkSuspender s;\n  try {\n    return recurse(n);\n  } catch (const std::exception& e) {\n    return s.dismissing([&] { return getTrace(e); });\n  }\n}\n\nExceptionInfo makeExceptionPtrWithStackDepthMeasureRetrieve(int n) {\n  folly::BenchmarkSuspender s;\n  try {\n    return recurse(n);\n  } catch (...) {\n    const auto ep = std::current_exception();\n    return s.dismissing([&] { return getTrace(ep); });\n  }\n}\n\nExceptionInfo makeExceptionWrapperWithStackDepthMeasureRetrieve(int n) {\n  folly::BenchmarkSuspender s;\n  try {\n    return recurse(n);\n  } catch (...) {\n    const auto ew = folly::exception_wrapper(std::current_exception());\n    return s.dismissing([&] { return getTrace(ew); });\n  }\n}\n\n} // namespace\n\nvoid captureException(int iterations, int stackDepth) {\n  for (int i = 0; i < iterations; i++) {\n    auto info = makeExceptionWithStackDepthMeasureThrow(stackDepth);\n    folly::compiler_must_not_elide(info);\n  }\n}\n\nvoid retrieveStackTracePtr(int iterations, int stackDepth) {\n  for (int i = 0; i < iterations; i++) {\n    auto info = makeExceptionPtrWithStackDepthMeasureRetrieve(stackDepth);\n    folly::compiler_must_not_elide(info);\n  }\n}\n\nvoid retrieveStackTraceRef(int iterations, int stackDepth) {\n  for (int i = 0; i < iterations; i++) {\n    auto info = makeExceptionRefWithStackDepthMeasureRetrieve(stackDepth);\n    folly::compiler_must_not_elide(info);\n  }\n}\n\nvoid retrieveStackTraceWrapper(int iterations, int stackDepth) {\n  for (int i = 0; i < iterations; i++) {\n    auto info = makeExceptionWrapperWithStackDepthMeasureRetrieve(stackDepth);\n    folly::compiler_must_not_elide(info);\n  }\n}\n\nBENCHMARK_PARAM(captureException, 0)\nBENCHMARK_PARAM(captureException, 20)\nBENCHMARK_PARAM(captureException, 100)\nBENCHMARK_PARAM(captureException, 1000)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(retrieveStackTracePtr, 0)\nBENCHMARK_PARAM(retrieveStackTracePtr, 20)\nBENCHMARK_PARAM(retrieveStackTracePtr, 100)\nBENCHMARK_PARAM(retrieveStackTracePtr, 1000)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(retrieveStackTraceRef, 0)\nBENCHMARK_PARAM(retrieveStackTraceRef, 20)\nBENCHMARK_PARAM(retrieveStackTraceRef, 100)\nBENCHMARK_PARAM(retrieveStackTraceRef, 1000)\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_PARAM(retrieveStackTraceWrapper, 0)\nBENCHMARK_PARAM(retrieveStackTraceWrapper, 20)\nBENCHMARK_PARAM(retrieveStackTraceWrapper, 100)\nBENCHMARK_PARAM(retrieveStackTraceWrapper, 1000)\n\nint main(int, char**) {\n  folly::runBenchmarks();\n}\n\n#endif\n#endif\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/SmartExceptionTracerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n#include <folly/debugging/exception_tracer/SmartExceptionTracer.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nusing namespace folly::exception_tracer;\n\n[[noreturn]] FOLLY_NOINLINE void testThrowException() {\n  throw std::runtime_error(\"test exception\");\n}\n\nTEST(SmartExceptionTracer, ExceptionPtr) {\n  auto ew = folly::try_and_catch(testThrowException);\n  auto info = getTrace(ew.to_exception_ptr());\n\n  std::ostringstream ss;\n  ss << info;\n  ASSERT_TRUE(ss.str().find(\"testThrowException\") != std::string::npos);\n}\n\nTEST(SmartExceptionTracer, ExceptionWrapper) {\n  auto ew = folly::try_and_catch(testThrowException);\n  auto info = getTrace(ew);\n\n  std::ostringstream ss;\n  ss << info;\n  ASSERT_TRUE(ss.str().find(\"testThrowException\") != std::string::npos);\n}\n\nTEST(SmartExceptionTracer, StdException) {\n  try {\n    testThrowException();\n  } catch (std::exception& ex) {\n    auto info = getTrace(ex);\n\n    std::ostringstream ss;\n    ss << info;\n    ASSERT_TRUE(ss.str().find(\"testThrowException\") != std::string::npos);\n  }\n}\n\nTEST(SmartExceptionTracer, EmptyExceptionWrapper) {\n  auto ew = folly::exception_wrapper();\n  auto info = getTrace(ew);\n\n  std::ostringstream ss;\n  ss << info;\n  ASSERT_TRUE(\n      ss.str().find(\"Exception type: (unknown type)\") != std::string::npos);\n}\n\nTEST(SmartExceptionTracer, NonStdException) {\n  try {\n    throw 10;\n  } catch (...) {\n    auto info = getTrace(std::current_exception());\n\n    std::ostringstream ss;\n    ss << info;\n    ASSERT_THAT(ss.str(), testing::HasSubstr(\"Exception type: int\"));\n  }\n}\n\nTEST(SmartExceptionTracer, UnthrownException) {\n  std::runtime_error ex(\"unthrown\");\n  auto info = getTrace(ex);\n\n  std::ostringstream ss;\n  ss << info;\n  ASSERT_TRUE(\n      ss.str().find(\"Exception type: std::runtime_error\") != std::string::npos);\n}\n\nnamespace {\n\n[[noreturn]] void funcD() {\n  throw std::runtime_error(\"test ex\");\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcC() {\n  funcD();\n  co_return;\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcB() {\n  co_await co_funcC();\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcA() {\n  co_await co_funcB();\n}\n\n} // namespace\n\nTEST(SmartExceptionTracer, AsyncStackTrace) {\n  try {\n    folly::coro::blockingWait(co_funcA());\n    FAIL() << \"exception should be thrown\";\n  } catch (const std::exception& ex) {\n    ASSERT_EQ(std::string(\"test ex\"), ex.what());\n    auto info = folly::exception_tracer::getAsyncTrace(ex);\n    LOG(INFO) << \"async stack trace: \" << info;\n\n    std::ostringstream ss;\n    ss << info;\n\n    // Symbols should appear in this relative order\n    std::vector<std::string> symbols{\n        \"funcD\",\n        \"co_funcC\",\n        \"co_funcB\",\n        \"co_funcA\",\n    };\n    std::vector<size_t> positions;\n    for (const auto& symbol : symbols) {\n      SCOPED_TRACE(symbol);\n      auto pos = ss.str().find(symbol);\n      ASSERT_NE(std::string::npos, pos);\n      positions.push_back(pos);\n    }\n    for (size_t i = 0; i < positions.size() - 1; ++i) {\n      ASSERT_LT(positions[i], positions[i + 1]);\n    }\n  }\n}\n\nclass ExceptionE : virtual public std::runtime_error {\n public:\n  ExceptionE() : std::runtime_error(\"ExceptionA\") {}\n};\n\nclass ExceptionF : virtual public std::runtime_error {\n public:\n  ExceptionF() : std::runtime_error(\"ExceptionB\") {}\n};\n\nclass ExceptionG : virtual public ExceptionE, virtual public ExceptionF {\n public:\n  ExceptionG() : std::runtime_error(\"ExceptionC\"), ExceptionE(), ExceptionF() {}\n};\n\n[[noreturn]] FOLLY_NOINLINE void throwDiamond() {\n  throw ExceptionG();\n}\n\nTEST(SmartExceptionTracer, diamondExceptionPointer) {\n  try {\n    throwDiamond();\n  } catch (...) {\n    ASSERT_NE(\n        folly::exception_ptr_get_object<std::exception>(\n            std::current_exception()),\n        nullptr);\n    auto info = getTrace(std::current_exception());\n    auto infoStr = std::stringstream{} << info;\n\n    EXPECT_THAT(info.frames, testing::SizeIs(testing::Gt(0)));\n    EXPECT_THAT(infoStr.str(), testing::HasSubstr(\"throwDiamond\"));\n  }\n}\n\nTEST(SmartExceptionTracer, diamondStdException) {\n  try {\n    throwDiamond();\n  } catch (const std::exception& e) {\n    auto info = getTrace(e);\n    auto infoStr = std::stringstream{} << info;\n\n    EXPECT_THAT(info.frames, testing::SizeIs(testing::Gt(0)));\n    EXPECT_THAT(infoStr.str(), testing::HasSubstr(\"throwDiamond\"));\n  }\n}\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/exception_tracer/test/exception_tracer_uncaught_test.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# pyre-strict\n\nimport os\nimport signal\nimport subprocess\nimport unittest\n\n\nclass ExceptionTracerUncaughtTest(unittest.TestCase):\n    def setUp(self) -> None:\n        self.helper = os.environ.get(\"EXCEPTION_TRACER_TEST_BIN\")\n        self.assertIsNotNone(\n            self.helper, \"EXCEPTION_TRACER_TEST_BIN env var must be set\"\n        )\n\n    def test_uncaught_exception(self) -> None:\n        assert self.helper is not None\n        p = subprocess.Popen(\n            [self.helper], stdout=subprocess.PIPE, stderr=subprocess.PIPE\n        )\n        out, err = p.communicate()\n        err_str = err.decode(\"utf-8\", errors=\"replace\")\n\n        # Process should be killed by SIGABRT (terminate called for uncaught exception)\n        self.assertEqual(\n            p.returncode,\n            -signal.SIGABRT,\n            f\"Expected SIGABRT (-{signal.SIGABRT}), got {p.returncode}.\\nstderr:\\n{err_str}\",\n        )\n\n        # Verify exception tracer output\n        self.assertIn(\"Exception type: std::runtime_error\", err_str)\n        self.assertIn(\"foo()\", err_str)\n        self.assertIn(\"main\", err_str)\n"
  },
  {
    "path": "folly/debugging/symbolizer/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"dwarf\",\n    srcs = [\n        \"Dwarf.cpp\",\n        \"DwarfImpl.cpp\",\n        \"DwarfLineNumberVM.cpp\",\n        \"DwarfSection.cpp\",\n        \"DwarfUtil.cpp\",\n    ],\n    headers = [\n        \"Dwarf.h\",\n        \"DwarfImpl.h\",\n        \"DwarfLineNumberVM.h\",\n        \"DwarfSection.h\",\n        \"DwarfUtil.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:optional\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/portability:config\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \":elf\",\n        \":elf_cache\",\n        \":symbolized_frame\",\n        \"//folly:function\",\n        \"//folly:range\",\n        \"//folly/portability/provide:libdwarf\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"elf\",\n    srcs = [\n        \"Elf.cpp\",\n    ],\n    headers = [\n        \"Elf.h\",\n        \"Elf-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:exception\",\n        \"//folly:scope_guard\",\n        \"//folly/lang:c_string\",\n        \"//folly/portability:sys_mman\",\n    ],\n    exported_deps = [\n        \"//folly:conv\",\n        \"//folly:likely\",\n        \"//folly:range\",\n        \"//folly/container:span\",\n        \"//folly/lang:cstring_view\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/portability:config\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"elf_cache\",\n    srcs = [\n        \"ElfCache.cpp\",\n    ],\n    headers = [\n        \"ElfCache.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:scope_guard\",\n        \"//folly/portability:sys_mman\",\n    ],\n    exported_deps = [\n        \":elf\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly/hash:hash\",\n        \"//folly/memory:reentrant_allocator\",\n        \"//folly/portability:config\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"line_reader\",\n    srcs = [\"LineReader.cpp\"],\n    headers = [\"LineReader.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:file_util\",\n    ],\n    exported_deps = [\n        \"//folly:range\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"stack_trace\",\n    srcs = [\"StackTrace.cpp\"],\n    headers = [\"StackTrace.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:portability\",\n        \"//folly/portability:config\",\n        \"//folly/portability:libunwind\",\n        \"//folly/tracing:async_stack\",\n    ],\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly/portability:sys_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"symbolized_frame\",\n    srcs = [\"SymbolizedFrame.cpp\"],\n    headers = [\"SymbolizedFrame.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:range\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"symbolize_printer\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":symbolizer\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"symbolizer\",\n    srcs = [\n        \"SignalHandler.cpp\",\n        \"SymbolizePrinter.cpp\",\n        \"Symbolizer.cpp\",\n    ],\n    headers = [\n        \"SignalHandler.h\",\n        \"SymbolizePrinter.h\",\n        \"Symbolizer.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":elf\",\n        \"//folly:demangle\",\n        \"//folly:file_util\",\n        \"//folly:memory\",\n        \"//folly:scope_guard\",\n        \"//folly:synchronized\",\n        \"//folly/container:evicting_cache_map\",\n        \"//folly/debugging/symbolizer/detail:debug\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/lang:to_ascii\",\n        \"//folly/memory:sanitize_address\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:sys_syscall\",\n        \"//folly/tracing:async_stack\",\n    ],\n    exported_deps = [\n        \":dwarf\",\n        \":elf_cache\",\n        \":stack_trace\",\n        \":symbolized_frame\",\n        \"//folly:fbstring\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly:string\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:config\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"signal_handler\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":symbolizer\"],\n)\n"
  },
  {
    "path": "folly/debugging/symbolizer/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME dwarf\n  SRCS\n    Dwarf.cpp\n    DwarfImpl.cpp\n    DwarfLineNumberVM.cpp\n    DwarfSection.cpp\n    DwarfUtil.cpp\n  HEADERS\n    Dwarf.h\n    DwarfImpl.h\n    DwarfLineNumberVM.h\n    DwarfSection.h\n    DwarfUtil.h\n  DEPS\n    folly_lang_safe_assert\n    folly_optional\n    folly_portability_config\n    folly_portability_unistd\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_elf\n    folly_debugging_symbolizer_elf_cache\n    folly_debugging_symbolizer_symbolized_frame\n    folly_function\n    folly_portability_provide_libdwarf\n    folly_range\n)\n\nfolly_add_library(\n  NAME elf\n  SRCS\n    Elf.cpp\n  HEADERS\n    Elf-inl.h\n    Elf.h\n  DEPS\n    folly_exception\n    folly_lang_c_string\n    folly_portability_sys_mman\n    folly_scope_guard\n  EXPORTED_DEPS\n    folly_container_span\n    folly_conv\n    folly_lang_cstring_view\n    folly_lang_safe_assert\n    folly_likely\n    folly_portability_config\n    folly_portability_unistd\n    folly_range\n)\n\nfolly_add_library(\n  NAME elf_cache\n  SRCS\n    ElfCache.cpp\n  HEADERS\n    ElfCache.h\n  DEPS\n    folly_portability_sys_mman\n    folly_scope_guard\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_elf\n    folly_hash_hash\n    folly_memory_reentrant_allocator\n    folly_optional\n    folly_portability_config\n    folly_range\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME line_reader\n  SRCS\n    LineReader.cpp\n  HEADERS\n    LineReader.h\n  DEPS\n    folly_file_util\n  EXPORTED_DEPS\n    folly_range\n)\n\nfolly_add_library(\n  NAME signal_handler\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_symbolizer\n)\n\nfolly_add_library(\n  NAME stack_trace\n  SRCS\n    StackTrace.cpp\n  HEADERS\n    StackTrace.h\n  DEPS\n    folly_portability\n    folly_portability_config\n    folly_portability_libunwind\n    folly_tracing_async_stack\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_portability_sys_types\n)\n\nfolly_add_library(\n  NAME symbolize_printer\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_symbolizer\n)\n\nfolly_add_library(\n  NAME symbolized_frame\n  SRCS\n    SymbolizedFrame.cpp\n  HEADERS\n    SymbolizedFrame.h\n  EXPORTED_DEPS\n    folly_range\n)\n\nfolly_add_library(\n  NAME symbolizer\n  SRCS\n    SignalHandler.cpp\n    SymbolizePrinter.cpp\n    Symbolizer.cpp\n  HEADERS\n    SignalHandler.h\n    SymbolizePrinter.h\n    Symbolizer.h\n  DEPS\n    folly_container_evicting_cache_map\n    folly_debugging_symbolizer_detail_debug\n    folly_debugging_symbolizer_elf\n    folly_demangle\n    folly_file_util\n    folly_lang_safe_assert\n    folly_lang_to_ascii\n    folly_memory\n    folly_memory_sanitize_address\n    folly_portability_sys_mman\n    folly_portability_sys_syscall\n    folly_scope_guard\n    folly_synchronized\n    folly_tracing_async_stack\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_dwarf\n    folly_debugging_symbolizer_elf_cache\n    folly_debugging_symbolizer_stack_trace\n    folly_debugging_symbolizer_symbolized_frame\n    folly_fbstring\n    folly_io_iobuf\n    folly_optional\n    folly_portability_config\n    folly_portability_unistd\n    folly_range\n    folly_string\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/debugging/symbolizer/Dwarf.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Dwarf.h>\n\n#include <array>\n#include <type_traits>\n\n#include <folly/Optional.h>\n#include <folly/debugging/symbolizer/DwarfImpl.h>\n#include <folly/debugging/symbolizer/DwarfSection.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/portability/Config.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\n#include <dwarf.h> // @manual\n\nnamespace folly {\nnamespace symbolizer {\n\nDwarf::Dwarf(ElfCacheBase* elfCache, const ElfFile* elf)\n    : elfCache_(elfCache),\n      defaultDebugSections_{\n          .elf = elf,\n          .debugCuIndex = getElfSection(elf, \".debug_cu_index\"),\n          .debugAbbrev = getElfSection(elf, \".debug_abbrev\"),\n          .debugAddr = getElfSection(elf, \".debug_addr\"),\n          .debugAranges = getElfSection(elf, \".debug_aranges\"),\n          .debugInfo = getElfSection(elf, \".debug_info\"),\n          .debugLine = getElfSection(elf, \".debug_line\"),\n          .debugLineStr = getElfSection(elf, \".debug_line_str\"),\n          .debugLoclists = getElfSection(elf, \".debug_loclists\"),\n          .debugRanges = getElfSection(elf, \".debug_ranges\"),\n          .debugRnglists = getElfSection(elf, \".debug_rnglists\"),\n          .debugStr = getElfSection(elf, \".debug_str\"),\n          .debugStrOffsets = getElfSection(elf, \".debug_str_offsets\")} {\n  // Optional sections:\n  //  - defaultDebugSections_.debugAranges: for fast address range lookup.\n  //     If missing .debug_info can be used - but it's much slower (linear\n  //     scan).\n  //  - debugRanges_ (DWARF 4) / debugRnglists_ (DWARF 5): non-contiguous\n  //    address ranges of debugging information entries.\n  //    Used for inline function address lookup.\n  if (defaultDebugSections_.debugInfo.empty() ||\n      defaultDebugSections_.debugAbbrev.empty() ||\n      defaultDebugSections_.debugLine.empty() ||\n      defaultDebugSections_.debugStr.empty()) {\n    defaultDebugSections_.elf = nullptr;\n  }\n}\n\nnamespace {\n\n/**\n * Find @address in .debug_aranges and return the offset in\n * .debug_info for compilation unit to which this address belongs.\n */\nbool findDebugInfoOffset(\n    uintptr_t address, StringPiece aranges, uint64_t& offset) {\n  DwarfSection section(aranges);\n  folly::StringPiece chunk;\n  while (section.next(chunk)) {\n    auto version = read<uint16_t>(chunk);\n    if (version != 2) {\n      FOLLY_SAFE_DFATAL(\"invalid aranges version: \", version);\n      return false;\n    }\n\n    offset = readOffset(chunk, section.is64Bit());\n    auto addressSize = read<uint8_t>(chunk);\n    if (addressSize != sizeof(uintptr_t)) {\n      FOLLY_SAFE_DFATAL(\"invalid address size: \", addressSize);\n      return false;\n    }\n    auto segmentSize = read<uint8_t>(chunk);\n    if (segmentSize != 0) {\n      FOLLY_SAFE_DFATAL(\"segmented architecture not supported: \", segmentSize);\n      return false;\n    }\n\n    // Padded to a multiple of 2 addresses.\n    // Strangely enough, this is the only place in the DWARF spec that requires\n    // padding.\n    {\n      size_t alignment = 2 * sizeof(uintptr_t);\n      size_t remainder = (chunk.data() - aranges.data()) % alignment;\n      if (remainder != 0) {\n        if (alignment - remainder > chunk.size()) {\n          FOLLY_SAFE_DFATAL(\n              \"invalid padding: alignment: \",\n              alignment,\n              \" remainder: \",\n              remainder,\n              \" chunk.size(): \",\n              chunk.size());\n          return false;\n        }\n        chunk.advance(alignment - remainder);\n      }\n    }\n\n    for (;;) {\n      auto start = read<uintptr_t>(chunk);\n      auto length = read<uintptr_t>(chunk);\n\n      if (start == 0 && length == 0) {\n        break;\n      }\n\n      // Is our address in this range?\n      if (address >= start && address < start + length) {\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\n} // namespace\n\nbool Dwarf::findAddress(\n    uintptr_t address,\n    LocationInfoMode mode,\n    SymbolizedFrame& frame,\n    folly::Range<SymbolizedFrame*> inlineFrames,\n    folly::FunctionRef<void(const folly::StringPiece name)> eachParameterName)\n    const {\n  if (mode == LocationInfoMode::DISABLED) {\n    return false;\n  }\n\n  if (!defaultDebugSections_.elf) { // No file.\n    return false;\n  }\n\n  if (!defaultDebugSections_.debugAranges.empty()) {\n    // Fast path: find the right .debug_info entry by looking up the\n    // address in .debug_aranges.\n    uint64_t offset = 0;\n    if (findDebugInfoOffset(\n            address, defaultDebugSections_.debugAranges, offset)) {\n      // Read compilation unit header from .debug_info\n      auto unit = getCompilationUnits(\n          elfCache_,\n          defaultDebugSections_,\n          offset,\n          mode == LocationInfoMode::FULL_WITH_INLINE);\n      if (unit.mainCompilationUnit.unitType != DW_UT_compile &&\n          unit.mainCompilationUnit.unitType != DW_UT_skeleton) {\n        return false;\n      }\n      DwarfImpl impl(elfCache_, unit, mode);\n      return impl.findLocation(\n          address,\n          frame,\n          inlineFrames,\n          eachParameterName,\n          false /*checkAddress*/);\n    } else if (mode == LocationInfoMode::FAST) {\n      // NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries\n      // in .debug_aranges for some functions, but always generates\n      // .debug_info entries.  Scanning .debug_info is slow, so fall back to\n      // it only if such behavior is requested via LocationInfoMode.\n      return false;\n    } else {\n      FOLLY_SAFE_DCHECK(\n          mode == LocationInfoMode::FULL ||\n              mode == LocationInfoMode::FULL_WITH_INLINE,\n          \"unexpected mode\");\n      // Fall back to the linear scan.\n    }\n  }\n\n  // Slow path (linear scan): Iterate over all .debug_info entries\n  // and look for the address in each compilation unit.\n  uint64_t offset = 0;\n  while (offset < defaultDebugSections_.debugInfo.size()) {\n    auto unit = getCompilationUnits(\n        elfCache_,\n        defaultDebugSections_,\n        offset,\n        mode == LocationInfoMode::FULL_WITH_INLINE);\n    offset += unit.mainCompilationUnit.size;\n    if (unit.mainCompilationUnit.unitType != DW_UT_compile &&\n        unit.mainCompilationUnit.unitType != DW_UT_skeleton) {\n      continue;\n    }\n    DwarfImpl impl(elfCache_, unit, mode);\n    if (impl.findLocation(\n            address,\n            frame,\n            inlineFrames,\n            eachParameterName,\n            true /*checkAddress*/)) {\n      return true;\n    }\n  }\n  return false;\n}\n\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/symbolizer/Dwarf.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// DWARF record parser\n\n#pragma once\n\n#include <folly/Function.h>\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/DwarfUtil.h>\n#include <folly/debugging/symbolizer/Elf.h>\n#include <folly/debugging/symbolizer/ElfCache.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\n/**\n * DWARF record parser.\n *\n * We only implement enough DWARF functionality to convert from PC address\n * to file and line number information.\n *\n * This means (although they're not part of the public API of this class), we\n * can parse Debug Information Entries (DIEs), abbreviations, attributes (of\n * all forms), and we can interpret bytecode for the line number VM.\n *\n * We can interpret DWARF records of version 2, 3, or 4, although we don't\n * actually support many of the version 4 features (such as VLIW, multiple\n * operations per instruction)\n *\n * Note that the DWARF record parser does not allocate heap memory at all.\n * This is on purpose: you can use the parser from\n * memory-constrained situations (such as an exception handler for\n * std::out_of_memory)  If it weren't for this requirement, some things would\n * be much simpler: the Path class would be unnecessary and would be replaced\n * with a std::string; the list of file names in the line number VM would be\n * kept as a vector of strings instead of re-executing the program to look for\n * DW_LNE_define_file instructions, etc.\n */\nclass Dwarf {\n  /**\n   * Note that Dwarf uses (and returns) StringPiece a lot.\n   * The StringPieces point within sections in the ELF file, and so will\n   * be live for as long as the passed-in ElfFile is live.\n   */\n public:\n  /** Create a DWARF parser around an ELF file. */\n  Dwarf(ElfCacheBase* elfCache, const ElfFile* elf);\n\n  /**\n   * Find the file and line number information corresponding to address.\n   * If `eachParameterName` is provided, the callback will be invoked once\n   * for each parameter of the function.\n   */\n  bool findAddress(\n      uintptr_t address,\n      LocationInfoMode mode,\n      SymbolizedFrame& frame,\n      folly::Range<SymbolizedFrame*> inlineFrames = {},\n      folly::FunctionRef<void(const folly::StringPiece name)>\n          eachParameterName = {}) const;\n\n private:\n  ElfCacheBase* elfCache_;\n  DebugSections defaultDebugSections_;\n};\n\n#endif\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfImpl.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/DwarfImpl.h>\n\n#include <array>\n#include <type_traits>\n\n#include <folly/Optional.h>\n#include <folly/debugging/symbolizer/DwarfUtil.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/portability/Config.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\n#include <dwarf.h> // @manual\n\n// We need a single dwarf5 tag, but may not be building against\n// a new enough libdwarf, so just define it ourselves.\n#ifndef DW_TAG_skeleton_unit\n#define DW_TAG_skeleton_unit 0x4a\n#endif\n\nnamespace folly {\nnamespace symbolizer {\n\n// Indicates inline function `name` is called  at `line@file`.\nstruct CallLocation {\n  Path file = {};\n  uint64_t line = 0;\n  folly::StringPiece name;\n};\n\nDwarfImpl::DwarfImpl(\n    ElfCacheBase* elfCache, CompilationUnits& cu, LocationInfoMode mode)\n    : elfCache_(elfCache), cu_(cu), mode_(mode) {}\n\n/**\n * Find the @locationInfo for @address in the compilation unit @cu_.\n *\n * Best effort:\n * - fills @inlineFrames if mode_ == FULL_WITH_INLINE,\n * - calls @eachParameterName on the function parameters.\n *\n * if @checkAddress is true, we verify that the address is mapped to\n * a range in this CU before running the line number VM\n */\nbool DwarfImpl::findLocation(\n    uintptr_t address,\n    SymbolizedFrame& frame,\n    folly::Range<SymbolizedFrame*> inlineFrames,\n    folly::FunctionRef<void(folly::StringPiece)> eachParameterName,\n    bool checkAddress) const {\n  auto mainCu = cu_.mainCompilationUnit;\n  Die die = getDieAtOffset(mainCu, mainCu.firstDie);\n  // Partial compilation unit (DW_TAG_partial_unit) is not supported.\n  if (die.abbr.tag != DW_TAG_compile_unit &&\n      die.abbr.tag != DW_TAG_skeleton_unit) {\n    FOLLY_SAFE_DFATAL(\"Unsupported die.abbr.tag: \", die.abbr.tag);\n    return false;\n  }\n\n  // Offset in .debug_line for the line number VM program for this CU\n  folly::Optional<uint64_t> lineOffset;\n  folly::StringPiece compilationDirectory;\n  folly::Optional<folly::StringPiece> mainFileName;\n  folly::Optional<uint64_t> baseAddrCU;\n  folly::Optional<uint64_t> rangesOffset;\n  bool seenLowPC = false;\n  bool seenHighPC = false;\n  enum : unsigned {\n    kStmtList = 1U << 0,\n    kCompDir = 1U << 1,\n    kName = 1U << 2,\n    kLowPC = 1U << 3,\n    kHighPCOrRanges = 1U << 4,\n  };\n  unsigned expectedAttributes = kStmtList | kCompDir | kName | kLowPC;\n  bool foundAddress = !checkAddress;\n  if (!foundAddress) {\n    expectedAttributes |= kHighPCOrRanges;\n  }\n\n  forEachAttribute(mainCu, die, [&](const Attribute& attr) {\n    switch (attr.spec.name) {\n      case DW_AT_stmt_list:\n        expectedAttributes &= ~kStmtList;\n        // Offset in .debug_line for the line number VM program for this\n        // compilation unit\n        lineOffset = std::get<uint64_t>(attr.attrValue);\n        break;\n      case DW_AT_comp_dir:\n        expectedAttributes &= ~kCompDir;\n        // Compilation directory\n        compilationDirectory = std::get<folly::StringPiece>(attr.attrValue);\n        break;\n      case DW_AT_name:\n        expectedAttributes &= ~kName;\n        // File name of main file being compiled\n        mainFileName = std::get<folly::StringPiece>(attr.attrValue);\n        break;\n      case DW_AT_low_pc:\n        expectedAttributes &= ~kLowPC;\n        baseAddrCU = std::get<uint64_t>(attr.attrValue);\n        if (!foundAddress) {\n          if (address < *baseAddrCU) {\n            return false;\n          }\n          seenLowPC = true;\n          if (seenHighPC) {\n            foundAddress = true;\n          } else if (rangesOffset) {\n            if (!isAddrInRangeList(\n                    mainCu,\n                    address,\n                    baseAddrCU,\n                    *rangesOffset,\n                    mainCu.addrSize)) {\n              return false;\n            }\n            foundAddress = true;\n          }\n        }\n        break;\n      case DW_AT_high_pc:\n        expectedAttributes &= ~kHighPCOrRanges;\n        if (!foundAddress) {\n          if (address >= std::get<uint64_t>(attr.attrValue)) {\n            return false;\n          }\n          seenHighPC = true;\n          foundAddress = seenLowPC;\n        }\n        break;\n      case DW_AT_ranges:\n        // 3.1.1: CU entries have:\n        // - either DW_AT_low_pc and DW_AT_high_pc\n        // OR\n        // - DW_AT_ranges and optional DW_AT_low_pc\n        expectedAttributes &= ~kHighPCOrRanges;\n        if (!foundAddress) {\n          rangesOffset = std::get<uint64_t>(attr.attrValue);\n          if (seenLowPC) {\n            if (!isAddrInRangeList(\n                    mainCu,\n                    address,\n                    baseAddrCU,\n                    *rangesOffset,\n                    mainCu.addrSize)) {\n              return false;\n            }\n            foundAddress = true;\n          }\n        }\n        break;\n    }\n    return (expectedAttributes != 0); // continue forEachAttribute\n  });\n\n  if (!foundAddress || !lineOffset) {\n    return false;\n  }\n\n  if (mainFileName) {\n    frame.location.hasMainFile = true;\n    frame.location.mainFile = Path(compilationDirectory, \"\", *mainFileName);\n  }\n\n  folly::StringPiece lineSection(mainCu.debugSections.debugLine);\n  lineSection.advance(*lineOffset);\n  DwarfLineNumberVM lineVM(\n      lineSection, compilationDirectory, mainCu.debugSections);\n\n  // Execute line number VM program to find file and line\n  frame.location.hasFileAndLine =\n      lineVM.findAddress(address, frame.location.file, frame.location.line);\n  if (!frame.location.hasFileAndLine) {\n    return false;\n  }\n\n  // NOTE: locationInfo was found, so findLocation returns success bellow.\n  // Missing inline function / parameter name is not a failure (best effort).\n  bool checkInline =\n      (mode_ == LocationInfoMode::FULL_WITH_INLINE && !inlineFrames.empty());\n  if (!checkInline && !eachParameterName) {\n    return true;\n  }\n\n  auto& cu = cu_.defaultCompilationUnit();\n  if (cu_.splitCU.hasValue()) {\n    die = getDieAtOffset(*cu_.splitCU, cu_.splitCU->firstDie);\n  }\n\n  // Cache abbreviation.\n  std::array<DIEAbbreviation, kMaxAbbreviationEntries> abbrs;\n  cu.abbrCache = folly::range(abbrs);\n  folly::StringPiece abbrev = cu.debugSections.debugAbbrev;\n  abbrev.advance(cu.abbrevOffset.value_or(0));\n  DIEAbbreviation abbr;\n  while (readAbbreviation(abbrev, abbr)) {\n    // Abbreviation code 0 is reserved for null debugging information entries.\n    if (abbr.code != 0 && abbr.code <= kMaxAbbreviationEntries) {\n      cu.abbrCache.data()[abbr.code - 1] = abbr;\n    }\n  }\n\n  // Find the subprogram that matches the given address.\n  Die subprogram;\n  if (!findSubProgramDieForAddress(cu, die, address, baseAddrCU, subprogram)) {\n    // Even though @cu contains @address, it's possible\n    // that the corresponding DW_TAG_subprogram DIE is missing.\n    return true;\n  }\n\n  if (auto name = getFunctionNameFromDie(cu, subprogram); !name.empty()) {\n    // frame.name may already be filled from an earlier call to:\n    //   frame.name = elf->getSymbolName(elf->getDefinitionByAddress(address))\n    //\n    // It's possible to have multiple symbols point to the same addresses.\n    // - to disambiguate use the DW_AT_linkage_name/DW_AT_name as found in the\n    //   subprogram DIE.\n    //\n    // It's possible that DW_AT_linkage_name is a prefix of the symbol name\n    // - e.g. coroutines may add .resume/.destroy/.cleanup suffixes\n    //   to the symbol name, but not to DW_AT_linkage_name.\n    // - If names share a common prefix, prefer the more specific name.\n    if (!folly::StringPiece(frame.name).startsWith(name)) {\n      frame.name = name.data();\n    }\n  }\n\n  if (eachParameterName) {\n    forEachChild(cu, subprogram, [&](const Die& child) {\n      if (child.abbr.tag == DW_TAG_formal_parameter) {\n        if (auto name =\n                getAttribute<folly::StringPiece>(cu, child, DW_AT_name)) {\n          eachParameterName(*name);\n        }\n      }\n      return true; // continue forEachChild\n    });\n  }\n\n  if (!checkInline || !subprogram.abbr.hasChildren) {\n    return true;\n  }\n\n  // NOTE: @subprogram is the DIE of caller function.\n  // Use an extra location and get its call file and call line, so that\n  // they can be used for the second last location when we don't have\n  // enough inline frames for all inline functions call stack.\n  size_t size =\n      std::min<size_t>(kMaxInlineLocationInfoPerFrame, inlineFrames.size()) + 1;\n  CallLocation callLocations[kMaxInlineLocationInfoPerFrame + 1];\n  size_t numFound = 0;\n  findInlinedSubroutineDieForAddress(\n      cu,\n      subprogram,\n      lineVM,\n      address,\n      baseAddrCU,\n      folly::Range<CallLocation*>(callLocations, size),\n      numFound);\n  folly::Range<CallLocation*> inlineLocations(callLocations, numFound);\n  fillInlineFrames(address, frame, inlineLocations, inlineFrames);\n  return true;\n}\n\nvoid DwarfImpl::fillInlineFrames(\n    uintptr_t address,\n    SymbolizedFrame& frame,\n    folly::Range<CallLocation*> inlineLocations,\n    folly::Range<SymbolizedFrame*> inlineFrames) const {\n  if (inlineLocations.empty()) {\n    return;\n  }\n  size_t numFound = inlineLocations.size();\n  const auto innerMostFile = frame.location.file;\n  const auto innerMostLine = frame.location.line;\n\n  // Earlier we filled in locationInfo:\n  // - mainFile: the path to the CU -- the file where the non-inlined\n  //   call is made from.\n  // - file + line: the location of the inner-most inlined call.\n  // Here we already find inlined info so mainFile would be redundant.\n  frame.location.hasMainFile = false;\n  frame.location.mainFile = Path{};\n  // @findInlinedSubroutineDieForAddress fills inlineLocations[0] with the\n  // file+line of the non-inlined outer function making the call.\n  // frame.location.name is already set by the caller by looking up the\n  // non-inlined function @address belongs to.\n  frame.location.hasFileAndLine = true;\n  frame.location.file = inlineLocations[0].file;\n  frame.location.line = inlineLocations[0].line;\n\n  // The next inlined subroutine's call file and call line is the current\n  // caller's location.\n  for (size_t i = 0; i < numFound - 1; i++) {\n    inlineLocations[i].file = inlineLocations[i + 1].file;\n    inlineLocations[i].line = inlineLocations[i + 1].line;\n  }\n  // CallLocation for the inner-most inlined function:\n  // - will be computed if enough space was available in the passed\n  //   buffer.\n  // - will have a .name, but no !.file && !.line\n  // - its corresponding file+line is the one returned by LineVM based\n  //   on @address.\n  // Use the inner-most inlined file+line info we got from the LineVM.\n  inlineLocations[numFound - 1].file = innerMostFile;\n  inlineLocations[numFound - 1].line = innerMostLine;\n\n  // Skip the extra location when actual inline function calls are more\n  // than provided frames.\n  inlineLocations =\n      inlineLocations.subpiece(0, std::min(numFound, inlineFrames.size()));\n\n  // Fill in inline frames in reverse order (as\n  // expected by the caller).\n  std::reverse(inlineLocations.begin(), inlineLocations.end());\n  for (size_t i = 0; i < inlineLocations.size(); i++) {\n    inlineFrames[i].found = true;\n    inlineFrames[i].addr = address;\n    inlineFrames[i].name = inlineLocations[i].name.data();\n    inlineFrames[i].location.hasFileAndLine = true;\n    inlineFrames[i].location.file = inlineLocations[i].file;\n    inlineFrames[i].location.line = inlineLocations[i].line;\n  }\n}\n\n// Finds the Compilation Unit starting at offset.\nCompilationUnit DwarfImpl::findCompilationUnit(\n    const CompilationUnit& cu, uint64_t targetOffset) const {\n  FOLLY_SAFE_DCHECK(\n      targetOffset < cu.debugSections.debugInfo.size(),\n      \"unexpected target address\");\n  uint64_t offset = 0;\n  while (offset < cu.debugSections.debugInfo.size()) {\n    folly::StringPiece chunk(cu.debugSections.debugInfo);\n    chunk.advance(offset);\n\n    auto initialLength = read<uint32_t>(chunk);\n    auto is64Bit = (initialLength == (uint32_t)-1);\n    auto size = is64Bit ? read<uint64_t>(chunk) : initialLength;\n    if (size > chunk.size()) {\n      FOLLY_SAFE_DFATAL(\n          \"invalid chunk size: \", size, \" chunk.size(): \", chunk.size());\n      break;\n    }\n    size += is64Bit ? 12 : 4;\n    if (offset + size > targetOffset) {\n      break;\n    }\n    offset += size;\n  }\n  return getCompilationUnits(\n             elfCache_, cu.debugSections, offset, /* requireSplitDwarf */ false)\n      .mainCompilationUnit;\n}\n\nsize_t DwarfImpl::forEachChild(\n    const CompilationUnit& cu,\n    const Die& die,\n    folly::FunctionRef<bool(const Die& die)> f) const {\n  size_t nextDieOffset = forEachAttribute(cu, die, [&](const Attribute&) {\n    return true;\n  });\n  if (!die.abbr.hasChildren) {\n    return nextDieOffset;\n  }\n\n  auto childDie = getDieAtOffset(cu, nextDieOffset);\n  while (childDie.code != 0) {\n    if (!f(childDie)) {\n      return childDie.offset;\n    }\n\n    // NOTE: Don't run `f` over grandchildren, just skip over them.\n    size_t siblingOffset = forEachChild(cu, childDie, [](const Die&) {\n      return true;\n    });\n    childDie = getDieAtOffset(cu, siblingOffset);\n  }\n\n  // childDie is now a dummy die whose offset is to the code 0 marking the\n  // end of the children. Need to add one to get the offset of the next die.\n  return childDie.offset + 1;\n}\n\nbool DwarfImpl::isAddrInRangeList(\n    const CompilationUnit& cu,\n    uint64_t address,\n    folly::Optional<uint64_t> baseAddr,\n    size_t offset,\n    uint8_t addrSize) const {\n  if (addrSize != 4 && addrSize != 8) {\n    FOLLY_SAFE_DFATAL(\"wrong address size: \", int(addrSize));\n    return false;\n  }\n  if (cu.version <= 4 && !cu.debugSections.debugRanges.empty()) {\n    const bool is64BitAddr = addrSize == 8;\n    folly::StringPiece sp = cu.debugSections.debugRanges;\n    if (offset > sp.size()) {\n      return false;\n    }\n    sp.advance(offset);\n    const uint64_t maxAddr = is64BitAddr\n        ? std::numeric_limits<uint64_t>::max()\n        : std::numeric_limits<uint32_t>::max();\n    while (sp.size() >= 2 * addrSize) {\n      uint64_t begin = readOffset(sp, is64BitAddr);\n      uint64_t end = readOffset(sp, is64BitAddr);\n      // The range list entry is a base address selection entry.\n      if (begin == maxAddr) {\n        baseAddr = end;\n        continue;\n      }\n      // The range list entry is an end of list entry.\n      if (begin == 0 && end == 0) {\n        break;\n      }\n\n      // Check if the given address falls in the range list entry.\n      // 2.17.3 Non-Contiguous Address Ranges\n      // The applicable base address of a range list entry is determined by\n      // the closest preceding base address selection entry (see below) in the\n      // same range list. If there is no such selection entry, then the\n      // applicable base address defaults to the base address of the\n      // compilation unit.\n      if (baseAddr && address >= begin + *baseAddr &&\n          address < end + *baseAddr) {\n        return true;\n      }\n    }\n  }\n\n  if (cu.version == 5 && !cu.debugSections.debugRnglists.empty() &&\n      cu.addrBase.has_value()) {\n    auto debugRnglists = cu.debugSections.debugRnglists;\n    debugRnglists.advance(offset);\n\n    while (!debugRnglists.empty()) {\n      auto kind = read<uint8_t>(debugRnglists);\n      switch (kind) {\n        case DW_RLE_end_of_list:\n          return false;\n        case DW_RLE_base_addressx: {\n          auto index = readULEB(debugRnglists);\n          auto sp = cu.debugSections.debugAddr.subpiece(\n              *cu.addrBase + index * sizeof(uint64_t));\n          baseAddr = read<uint64_t>(sp);\n        } break;\n\n        case DW_RLE_startx_endx: {\n          auto indexStart = readULEB(debugRnglists);\n          auto indexEnd = readULEB(debugRnglists);\n          auto spStart = cu.debugSections.debugAddr.subpiece(\n              *cu.addrBase + indexStart * sizeof(uint64_t));\n          auto start = read<uint64_t>(spStart);\n\n          auto spEnd = cu.debugSections.debugAddr.subpiece(\n              *cu.addrBase + indexEnd * sizeof(uint64_t));\n          auto end = read<uint64_t>(spEnd);\n          if (address >= start && address < end) {\n            return true;\n          }\n        } break;\n\n        case DW_RLE_startx_length: {\n          auto indexStart = readULEB(debugRnglists);\n          auto length = readULEB(debugRnglists);\n          auto spStart = cu.debugSections.debugAddr.subpiece(\n              *cu.addrBase + indexStart * sizeof(uint64_t));\n          auto start = read<uint64_t>(spStart);\n          auto end = start + length;\n          if (start != end && address >= start && address < end) {\n            return true;\n          }\n        } break;\n\n        case DW_RLE_offset_pair: {\n          auto offsetStart = readULEB(debugRnglists);\n          auto offsetEnd = readULEB(debugRnglists);\n          if (baseAddr && address >= (*baseAddr + offsetStart) &&\n              address < (*baseAddr + offsetEnd)) {\n            return true;\n          }\n        } break;\n\n        case DW_RLE_base_address:\n          baseAddr = read<uint64_t>(debugRnglists);\n          break;\n\n        case DW_RLE_start_end: {\n          uint64_t start = read<uint64_t>(debugRnglists);\n          uint64_t end = read<uint64_t>(debugRnglists);\n          if (address >= start && address < end) {\n            return true;\n          }\n        } break;\n\n        case DW_RLE_start_length: {\n          uint64_t start = read<uint64_t>(debugRnglists);\n          uint64_t end = start + readULEB(debugRnglists);\n          if (address >= start && address < end) {\n            return true;\n          }\n        } break;\n\n        default:\n          FOLLY_SAFE_DFATAL(\n              \"Unexpected debug_rnglists entry kind: \", static_cast<int>(kind));\n          return false;\n      }\n    }\n  }\n  return false;\n}\n\nbool DwarfImpl::findSubProgramDieForAddress(\n    const CompilationUnit& cu,\n    const Die& die,\n    uint64_t address,\n    folly::Optional<uint64_t> baseAddrCU,\n    Die& subprogram) const {\n  forEachChild(cu, die, [&](const Die& childDie) {\n    if (childDie.abbr.tag == DW_TAG_subprogram) {\n      folly::Optional<uint64_t> lowPc;\n      folly::Optional<uint64_t> highPc;\n      folly::Optional<bool> isHighPcAddr;\n      folly::Optional<uint64_t> rangeOffset;\n      forEachAttribute(cu, childDie, [&](const Attribute& attr) {\n        switch (attr.spec.name) {\n          case DW_AT_ranges:\n            rangeOffset =\n                std::get<uint64_t>(attr.attrValue) + cu.rangesBase.value_or(0);\n            break;\n          case DW_AT_low_pc:\n            lowPc = std::get<uint64_t>(attr.attrValue);\n            break;\n          case DW_AT_high_pc:\n            // The value of the DW_AT_high_pc attribute can be\n            // an address (DW_FORM_addr*) or an offset (DW_FORM_data*).\n            isHighPcAddr = attr.spec.form == DW_FORM_addr || //\n                attr.spec.form == DW_FORM_addrx || //\n                attr.spec.form == DW_FORM_addrx1 || //\n                attr.spec.form == DW_FORM_addrx2 || //\n                attr.spec.form == DW_FORM_addrx3 || //\n                attr.spec.form == DW_FORM_addrx4;\n            highPc = std::get<uint64_t>(attr.attrValue);\n            break;\n        }\n        return true; // continue forEachAttribute\n      });\n\n      bool pcMatch = lowPc && highPc && isHighPcAddr && address >= *lowPc &&\n          (address < (*isHighPcAddr ? *highPc : *lowPc + *highPc));\n      if (pcMatch) {\n        subprogram = childDie;\n        return false; // stop forEachChild\n      }\n\n      bool rangeMatch = rangeOffset &&\n          isAddrInRangeList(cu, address, baseAddrCU, *rangeOffset, cu.addrSize);\n      if (rangeMatch) {\n        subprogram = childDie;\n        return false; // stop forEachChild\n      }\n    }\n\n    // Continue forEachChild to next sibling DIE only if not already found.\n    return !findSubProgramDieForAddress(\n        cu, childDie, address, baseAddrCU, subprogram);\n  });\n  return subprogram.abbr.tag == DW_TAG_subprogram;\n}\n\n/**\n * Find DW_TAG_inlined_subroutine child DIEs that contain @address and\n * then extract:\n * - Where was it called from (DW_AT_call_file & DW_AT_call_line):\n *   the statement or expression that caused the inline expansion.\n * - The inlined function's name. As a function may be inlined multiple\n *   times, common attributes like DW_AT_linkage_name or DW_AT_name\n *   are only stored in its \"concrete out-of-line instance\" (a\n *   DW_TAG_subprogram) which we find using DW_AT_abstract_origin.\n */\nvoid DwarfImpl::findInlinedSubroutineDieForAddress(\n    const CompilationUnit& cu,\n    const Die& die,\n    const DwarfLineNumberVM& lineVM,\n    uint64_t address,\n    folly::Optional<uint64_t> baseAddrCU,\n    folly::Range<CallLocation*> locations,\n    size_t& numFound) const {\n  if (numFound >= locations.size()) {\n    return;\n  }\n\n  forEachChild(cu, die, [&](const Die& childDie) {\n    // Between a DW_TAG_subprogram and DW_TAG_inlined_subroutine we might\n    // have arbitrary intermediary \"nodes\", including DW_TAG_common_block,\n    // DW_TAG_lexical_block, DW_TAG_try_block, DW_TAG_catch_block and\n    // DW_TAG_with_stmt, etc.\n    // We can't filter with location here since its range may be not specified.\n    // See section 2.6.2: A location list containing only an end of list entry\n    // describes an object that exists in the source code but not in the\n    // executable program.\n    if (childDie.abbr.tag == DW_TAG_try_block ||\n        childDie.abbr.tag == DW_TAG_catch_block ||\n        childDie.abbr.tag == DW_TAG_entry_point ||\n        childDie.abbr.tag == DW_TAG_common_block ||\n        childDie.abbr.tag == DW_TAG_lexical_block) {\n      findInlinedSubroutineDieForAddress(\n          cu, childDie, lineVM, address, baseAddrCU, locations, numFound);\n      return true;\n    }\n\n    if (childDie.abbr.tag != DW_TAG_subprogram &&\n        childDie.abbr.tag != DW_TAG_inlined_subroutine) {\n      return true;\n    }\n\n    folly::Optional<uint64_t> lowPc;\n    folly::Optional<uint64_t> highPc;\n    folly::Optional<bool> isHighPcAddr;\n    folly::Optional<uint64_t> abstractOrigin;\n    folly::Optional<uint64_t> abstractOriginRefType;\n    folly::Optional<uint64_t> callFile;\n    folly::Optional<uint64_t> callLine;\n    folly::Optional<uint64_t> rangeOffset;\n    forEachAttribute(cu, childDie, [&](const Attribute& attr) {\n      switch (attr.spec.name) {\n        case DW_AT_ranges:\n          rangeOffset =\n              std::get<uint64_t>(attr.attrValue) + cu.rangesBase.value_or(0);\n          break;\n        case DW_AT_low_pc:\n          lowPc = std::get<uint64_t>(attr.attrValue);\n          break;\n        case DW_AT_high_pc:\n          // The value of the DW_AT_high_pc attribute can be\n          // an address (DW_FORM_addr*) or an offset (DW_FORM_data*).\n          isHighPcAddr = attr.spec.form == DW_FORM_addr || //\n              attr.spec.form == DW_FORM_addrx || //\n              attr.spec.form == DW_FORM_addrx1 || //\n              attr.spec.form == DW_FORM_addrx2 || //\n              attr.spec.form == DW_FORM_addrx3 || //\n              attr.spec.form == DW_FORM_addrx4;\n          highPc = std::get<uint64_t>(attr.attrValue);\n          break;\n        case DW_AT_abstract_origin:\n          abstractOriginRefType = attr.spec.form;\n          abstractOrigin = std::get<uint64_t>(attr.attrValue);\n          break;\n        case DW_AT_call_line:\n          callLine = std::get<uint64_t>(attr.attrValue);\n          break;\n        case DW_AT_call_file:\n          callFile = std::get<uint64_t>(attr.attrValue);\n          break;\n      }\n      return true; // continue forEachAttribute\n    });\n\n    // 2.17 Code Addresses and Ranges\n    // Any debugging information entry describing an entity that has a\n    // machine code address or range of machine code addresses,\n    // which includes compilation units, module initialization, subroutines,\n    // ordinary blocks, try/catch blocks, labels and the like, may have\n    //  - A DW_AT_low_pc attribute for a single address,\n    //  - A DW_AT_low_pc and DW_AT_high_pc pair of attributes for a\n    //    single contiguous range of addresses, or\n    //  - A DW_AT_ranges attribute for a non-contiguous range of addresses.\n    // TODO: Support DW_TAG_entry_point and DW_TAG_common_block that don't\n    // have DW_AT_low_pc/DW_AT_high_pc pairs and DW_AT_ranges.\n    // TODO: Support relocated address which requires lookup in relocation\n    // map.\n    bool pcMatch = lowPc && highPc && isHighPcAddr && address >= *lowPc &&\n        (address < (*isHighPcAddr ? *highPc : (*lowPc + *highPc)));\n    bool rangeMatch = rangeOffset &&\n        isAddrInRangeList(cu, address, baseAddrCU, *rangeOffset, cu.addrSize);\n    if (!pcMatch && !rangeMatch) {\n      // Address doesn't match. Keep searching other children.\n      return true;\n    }\n\n    if (!abstractOrigin || !abstractOriginRefType || !callLine || !callFile) {\n      // We expect a single sibling DIE to match on addr, but it's missing\n      // required fields. Stop searching for other DIEs.\n      return false;\n    }\n\n    locations[numFound].file = lineVM.getFullFileName(*callFile);\n    locations[numFound].line = *callLine;\n\n    // DW_AT_abstract_origin is a reference. There a 3 types of references:\n    // - the reference can identify any debugging information entry within the\n    //   compilation unit (DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4,\n    //   DW_FORM_ref8, DW_FORM_ref_udata). This type of reference is an offset\n    //   from the first byte of the compilation header for the compilation\n    //   unit containing the reference.\n    // - the reference can identify any debugging information entry within a\n    //   .debug_info section; in particular, it may refer to an entry in a\n    //   different compilation unit (DW_FORM_ref_addr)\n    // - the reference can identify any debugging information type entry that\n    //   has been placed in its own type unit.\n    //   Not applicable for DW_AT_abstract_origin.\n    locations[numFound].name = (*abstractOriginRefType != DW_FORM_ref_addr)\n        ? getFunctionName(cu, cu.offset + *abstractOrigin)\n        : getFunctionName(\n              findCompilationUnit(cu, *abstractOrigin), *abstractOrigin);\n    findInlinedSubroutineDieForAddress(\n        cu, childDie, lineVM, address, baseAddrCU, locations, ++numFound);\n\n    return false;\n  });\n}\n\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfImpl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// DWARF record parser\n\n#pragma once\n\n#include <variant>\n\n#include <folly/Function.h>\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/DwarfLineNumberVM.h>\n#include <folly/debugging/symbolizer/DwarfSection.h>\n#include <folly/debugging/symbolizer/DwarfUtil.h>\n#include <folly/debugging/symbolizer/Elf.h>\n#include <folly/debugging/symbolizer/ElfCache.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\nstruct CallLocation;\n\nclass DwarfImpl {\n public:\n  explicit DwarfImpl(\n      ElfCacheBase* elfCache, CompilationUnits& cu, LocationInfoMode mode);\n\n  /**\n   * Find the @locationInfo for @address in the compilation unit @cu.\n   *\n   * Best effort:\n   * - fills @inlineFrames if mode == FULL_WITH_INLINE,\n   * - calls @eachParameterName on the function parameters.\n   *\n   * if @checkAddress is true, we verify that the address is mapped to\n   * a range in this CU before running the line number VM\n   */\n  bool findLocation(\n      uintptr_t address,\n      SymbolizedFrame& frame,\n      folly::Range<SymbolizedFrame*> inlineFrames,\n      folly::FunctionRef<void(folly::StringPiece)> eachParameterName,\n      bool checkAddress = true) const;\n\n private:\n  using AttributeValue = std::variant<uint64_t, folly::StringPiece>;\n\n  /**\n   * Finds a subprogram debugging info entry that contains a given address among\n   * children of given die. Depth first search.\n   */\n  bool findSubProgramDieForAddress(\n      const CompilationUnit& cu,\n      const Die& die,\n      uint64_t address,\n      folly::Optional<uint64_t> baseAddrCU,\n      Die& subprogram) const;\n\n  /**\n   * Finds inlined subroutine DIEs and their caller lines that contains a given\n   * address among children of given die. Depth first search.\n   */\n  void findInlinedSubroutineDieForAddress(\n      const CompilationUnit& cu,\n      const Die& die,\n      const DwarfLineNumberVM& lineVM,\n      uint64_t address,\n      folly::Optional<uint64_t> baseAddrCU,\n      folly::Range<CallLocation*> locations,\n      size_t& numFound) const;\n\n  CompilationUnit findCompilationUnit(\n      const CompilationUnit& cu, uint64_t targetOffset) const;\n\n  /**\n   * Find the actual definition DIE instead of declaration for the given die.\n   */\n  Die findDefinitionDie(const CompilationUnit& cu, const Die& die) const;\n\n  /**\n   * Iterates over all children of a debugging info entry, calling the given\n   * callable for each. Iteration is stopped early if any of the calls return\n   * false. Returns the offset of next DIE after iterations.\n   */\n  size_t forEachChild(\n      const CompilationUnit& cu,\n      const Die& die,\n      folly::FunctionRef<bool(const Die& die)> f) const;\n\n  /**\n   * Check if the given address is in the range list at the given offset in\n   * .debug_ranges.\n   */\n  bool isAddrInRangeList(\n      const CompilationUnit& cu,\n      uint64_t address,\n      folly::Optional<uint64_t> baseAddr,\n      size_t offset,\n      uint8_t addrSize) const;\n\n  void fillInlineFrames(\n      uintptr_t address,\n      SymbolizedFrame& frame,\n      folly::Range<CallLocation*> inlineLocations,\n      folly::Range<SymbolizedFrame*> inlineFrames) const;\n\n  ElfCacheBase* elfCache_;\n  CompilationUnits& cu_;\n  const LocationInfoMode mode_;\n};\n\n#endif\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfLineNumberVM.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/DwarfLineNumberVM.h>\n\n#include <folly/Optional.h>\n#include <folly/debugging/symbolizer/DwarfSection.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\nnamespace folly {\nnamespace symbolizer {\n\nDwarfLineNumberVM::DwarfLineNumberVM(\n    folly::StringPiece data,\n    folly::StringPiece compilationDirectory,\n    const DebugSections& debugSections)\n    : compilationDirectory_(compilationDirectory),\n      debugSections_(debugSections) {\n  DwarfSection section(data);\n  if (!section.next(data_)) {\n    FOLLY_SAFE_DFATAL(\"invalid line number VM\");\n    reset();\n    return;\n  }\n  is64Bit_ = section.is64Bit();\n  initializationSuccess_ = init();\n}\n\nvoid DwarfLineNumberVM::reset() {\n  address_ = 0;\n  file_ = 1;\n  line_ = 1;\n  column_ = 0;\n  isStmt_ = defaultIsStmt_;\n  basicBlock_ = false;\n  endSequence_ = false;\n  prologueEnd_ = false;\n  epilogueBegin_ = false;\n  isa_ = 0;\n  discriminator_ = 0;\n}\n\nnamespace {\n\nstruct LineNumberAttribute {\n  uint64_t contentTypeCode;\n  uint64_t formCode;\n  std::variant<uint64_t, folly::StringPiece> attrValue;\n};\n\nfolly::Optional<LineNumberAttribute> readLineNumberAttribute(\n    bool is64Bit,\n    folly::StringPiece& format,\n    folly::StringPiece& entries,\n    folly::StringPiece debugStr,\n    folly::StringPiece debugLineStr) {\n  uint64_t contentTypeCode = readULEB(format);\n  uint64_t formCode = readULEB(format);\n  std::variant<uint64_t, folly::StringPiece> attrValue;\n\n  switch (contentTypeCode) {\n    case DW_LNCT_path: {\n      switch (formCode) {\n        case DW_FORM_string:\n          attrValue = readNullTerminated(entries);\n          break;\n        case DW_FORM_line_strp: {\n          auto off = readOffset(entries, is64Bit);\n          attrValue = getStringFromStringSection(debugLineStr, off);\n        } break;\n        case DW_FORM_strp:\n          attrValue = getStringFromStringSection(\n              debugStr, readOffset(entries, is64Bit));\n          break;\n        case DW_FORM_strp_sup:\n          FOLLY_SAFE_DFATAL(\"Unexpected DW_FORM_strp_sup\");\n          return folly::none;\n        default:\n          FOLLY_SAFE_DFATAL(\"Unexpected form for DW_LNCT_path: \", formCode);\n          return folly::none;\n      }\n    } break;\n\n    case DW_LNCT_directory_index: {\n      switch (formCode) {\n        case DW_FORM_data1:\n          attrValue = read<uint8_t>(entries);\n          break;\n        case DW_FORM_data2:\n          attrValue = read<uint16_t>(entries);\n          break;\n        case DW_FORM_udata:\n          attrValue = readULEB(entries);\n          break;\n        default:\n          FOLLY_SAFE_DFATAL(\n              \"Unexpected form for DW_LNCT_directory_index: \", formCode);\n          return folly::none;\n      }\n    } break;\n\n    case DW_LNCT_timestamp: {\n      switch (formCode) {\n        case DW_FORM_udata:\n          attrValue = readULEB(entries);\n          break;\n        case DW_FORM_data4:\n          attrValue = read<uint32_t>(entries);\n          break;\n        case DW_FORM_data8:\n          attrValue = read<uint64_t>(entries);\n          break;\n        case DW_FORM_block:\n          attrValue = readBytes(entries, readULEB(entries));\n          break;\n        default:\n          FOLLY_SAFE_DFATAL(\n              \"Unexpected form for DW_LNCT_timestamp: \", formCode);\n          return folly::none;\n      }\n    } break;\n\n    case DW_LNCT_size: {\n      switch (formCode) {\n        case DW_FORM_udata:\n          attrValue = readULEB(entries);\n          break;\n        case DW_FORM_data1:\n          attrValue = read<uint8_t>(entries);\n          break;\n        case DW_FORM_data2:\n          attrValue = read<uint16_t>(entries);\n          break;\n        case DW_FORM_data4:\n          attrValue = read<uint32_t>(entries);\n          break;\n        case DW_FORM_data8:\n          attrValue = read<uint64_t>(entries);\n          break;\n        default:\n          FOLLY_SAFE_DFATAL(\"Unexpected form for DW_LNCT_size: \", formCode);\n          return folly::none;\n      }\n    } break;\n\n    case DW_LNCT_MD5: {\n      switch (formCode) {\n        case DW_FORM_data16:\n          attrValue = readBytes(entries, 16);\n          break;\n        default:\n          FOLLY_SAFE_DFATAL(\"Unexpected form for DW_LNCT_MD5: \", formCode);\n          return folly::none;\n      }\n    } break;\n\n    default:\n      // TODO: skip over vendor data as specified by the form instead.\n      FOLLY_SAFE_DFATAL(\n          \"Unexpected vendor content type code: \", contentTypeCode);\n      return folly::none;\n  }\n  return LineNumberAttribute{\n      .contentTypeCode = contentTypeCode,\n      .formCode = formCode,\n      .attrValue = attrValue,\n  };\n}\n\n} // namespace\n\nbool DwarfLineNumberVM::init() {\n  version_ = read<uint16_t>(data_);\n  if (version_ < 2 || version_ > 5) {\n    FOLLY_SAFE_DFATAL(\"invalid version in line number VM: \", version_);\n    return false;\n  }\n  if (version_ == 5) {\n    auto addressSize = read<uint8_t>(data_);\n    if (addressSize != sizeof(uintptr_t)) {\n      FOLLY_SAFE_DFATAL(\n          \"Unexpected Line Number Table address_size: \", addressSize);\n      return false;\n    }\n    auto segment_selector_size = read<uint8_t>(data_);\n    if (segment_selector_size != 0) {\n      FOLLY_SAFE_DFATAL(\"Segments not supported\");\n      return false;\n    }\n  }\n  uint64_t headerLength = readOffset(data_, is64Bit_);\n  if (headerLength > data_.size()) {\n    FOLLY_SAFE_DFATAL(\n        \"invalid line number VM header length: headerLength: \",\n        headerLength,\n        \" data_.size(): \",\n        data_.size());\n    return false;\n  }\n\n  folly::StringPiece header(data_.data(), headerLength);\n  data_.assign(header.end(), data_.end());\n\n  minLength_ = read<uint8_t>(header);\n  if (version_ >= 4) { // Version 2 and 3 records don't have this\n    uint8_t maxOpsPerInstruction = read<uint8_t>(header);\n    if (maxOpsPerInstruction != 1) {\n      FOLLY_SAFE_DFATAL(\"VLIW not supported\");\n      return false;\n    }\n  }\n  defaultIsStmt_ = read<uint8_t>(header);\n  lineBase_ = read<int8_t>(header); // yes, signed\n  lineRange_ = read<uint8_t>(header);\n  opcodeBase_ = read<uint8_t>(header);\n  if (opcodeBase_ == 0) {\n    FOLLY_SAFE_DFATAL(\"invalid opcode base\");\n    return false;\n  }\n  standardOpcodeLengths_ = reinterpret_cast<const uint8_t*>(header.data());\n  header.advance(opcodeBase_ - 1);\n\n  if (version_ <= 4) {\n    // We don't want to use heap, so we don't keep an unbounded amount of state.\n    // We'll just skip over include directories and file names here, and\n    // we'll loop again when we actually need to retrieve one.\n    folly::StringPiece sp;\n    const char* tmp = header.data();\n    v4_.includeDirectoryCount = 0;\n    while (!(sp = readNullTerminated(header)).empty()) {\n      ++v4_.includeDirectoryCount;\n    }\n    v4_.includeDirectories.assign(tmp, header.data());\n\n    tmp = header.data();\n    FileName fn;\n    v4_.fileNameCount = 0;\n    while (readFileName(header, fn)) {\n      ++v4_.fileNameCount;\n    }\n    v4_.fileNames.assign(tmp, header.data());\n  } else if (version_ == 5) {\n    v5_.directoryEntryFormatCount = read<uint8_t>(header);\n    const char* tmp = header.data();\n    for (uint8_t i = 0; i < v5_.directoryEntryFormatCount; i++) {\n      // A sequence of directory entry format descriptions. Each description\n      // consists of a pair of ULEB128 values:\n      readULEB(header); // A content type code\n      readULEB(header); // A form code using the attribute form codes\n    }\n    v5_.directoryEntryFormat.assign(tmp, header.data());\n    v5_.directoriesCount = readULEB(header);\n    tmp = header.data();\n    for (uint64_t i = 0; i < v5_.directoriesCount; i++) {\n      folly::StringPiece format = v5_.directoryEntryFormat;\n      for (uint8_t f = 0; f < v5_.directoryEntryFormatCount; f++) {\n        if (!readLineNumberAttribute(\n                is64Bit_,\n                format,\n                header,\n                debugSections_.debugStr,\n                debugSections_.debugLineStr)) {\n          return false;\n        }\n      }\n    }\n    v5_.directories.assign(tmp, header.data());\n\n    v5_.fileNameEntryFormatCount = read<uint8_t>(header);\n    tmp = header.data();\n    for (uint8_t i = 0; i < v5_.fileNameEntryFormatCount; i++) {\n      // A sequence of file entry format descriptions. Each description\n      // consists of a pair of ULEB128 values:\n      readULEB(header); // A content type code\n      readULEB(header); // A form code using the attribute form codes\n    }\n    v5_.fileNameEntryFormat.assign(tmp, header.data());\n    v5_.fileNamesCount = readULEB(header);\n    tmp = header.data();\n    for (uint64_t i = 0; i < v5_.fileNamesCount; i++) {\n      folly::StringPiece format = v5_.fileNameEntryFormat;\n      for (uint8_t f = 0; f < v5_.fileNameEntryFormatCount; f++) {\n        if (!readLineNumberAttribute(\n                is64Bit_,\n                format,\n                header,\n                debugSections_.debugStr,\n                debugSections_.debugLineStr)) {\n          return false;\n        }\n      }\n    }\n    v5_.fileNames.assign(tmp, header.data());\n  }\n  return true;\n}\n\nbool DwarfLineNumberVM::next(folly::StringPiece& program) {\n  DwarfLineNumberVM::StepResult ret;\n  do {\n    ret = step(program);\n  } while (ret == CONTINUE);\n\n  return (ret == COMMIT);\n}\n\nDwarfLineNumberVM::FileName DwarfLineNumberVM::getFileName(\n    uint64_t index) const {\n  FileName fn;\n  if (!initializationSuccess_) {\n    return fn;\n  }\n  if (version_ <= 4) {\n    if (index == 0) {\n      FOLLY_SAFE_DFATAL(\"invalid file index 0\");\n      return fn;\n    }\n    if (index <= v4_.fileNameCount) {\n      folly::StringPiece fileNames = v4_.fileNames;\n      for (; index; --index) {\n        if (!readFileName(fileNames, fn)) {\n          abort();\n        }\n      }\n      return fn;\n    }\n\n    index -= v4_.fileNameCount;\n\n    folly::StringPiece program = data_;\n    for (; index; --index) {\n      if (!nextDefineFile(program, fn)) {\n        FOLLY_SAFE_DFATAL(\"invalid file index: \", index);\n        return fn;\n      }\n    }\n\n    return fn;\n  } else {\n    if (index >= v5_.fileNamesCount) {\n      FOLLY_SAFE_DFATAL(\n          \"invalid file index: \",\n          index,\n          \" v5_.fileNamesCount: \",\n          v5_.fileNamesCount);\n      return fn;\n    }\n\n    folly::StringPiece fileNames = v5_.fileNames;\n    for (uint64_t i = 0; i < v5_.fileNamesCount; i++) {\n      folly::StringPiece format = v5_.fileNameEntryFormat;\n      for (uint8_t f = 0; f < v5_.fileNameEntryFormatCount; f++) {\n        auto attr = readLineNumberAttribute(\n            is64Bit_,\n            format,\n            fileNames,\n            debugSections_.debugStr,\n            debugSections_.debugLineStr);\n        if (!attr) {\n          return fn;\n        }\n        if (i == index) {\n          switch (attr->contentTypeCode) {\n            case DW_LNCT_path:\n              fn.relativeName = std::get<folly::StringPiece>(attr->attrValue);\n              break;\n            case DW_LNCT_directory_index:\n              fn.directoryIndex = std::get<uint64_t>(attr->attrValue);\n              break;\n          }\n        }\n      }\n    }\n    return fn;\n  }\n}\n\nfolly::StringPiece DwarfLineNumberVM::getIncludeDirectory(\n    uint64_t index) const {\n  if (version_ <= 4) {\n    if (index == 0) {\n      // In DWARF <= 4 the current directory is not represented in the\n      // directories field and a directory index of 0 implicitly referred to\n      // that directory as found in the DW_AT_comp_dir attribute of the\n      // compilation unit debugging information entry.\n      return {};\n    }\n\n    if (index > v4_.includeDirectoryCount) {\n      FOLLY_SAFE_DFATAL(\n          \"invalid include directory: index: \",\n          index,\n          \" v4_.includeDirectoryCount: \",\n          v4_.includeDirectoryCount);\n      return {};\n    }\n\n    folly::StringPiece includeDirectories = v4_.includeDirectories;\n    folly::StringPiece dir;\n    for (; index; --index) {\n      dir = readNullTerminated(includeDirectories);\n      if (dir.empty()) {\n        FOLLY_SAFE_DFATAL(\n            \"Unexpected empty null-terminated directory name: index: \", index);\n        return {};\n      }\n    }\n\n    return dir;\n  } else {\n    if (index >= v5_.directoriesCount) {\n      FOLLY_SAFE_DFATAL(\n          \"invalid file index: index: \",\n          index,\n          \" v5_.directoriesCount: \",\n          v5_.directoriesCount);\n      return {};\n    }\n    folly::StringPiece directories = v5_.directories;\n    for (uint64_t i = 0; i < v5_.directoriesCount; i++) {\n      folly::StringPiece format = v5_.directoryEntryFormat;\n      for (uint8_t f = 0; f < v5_.directoryEntryFormatCount; f++) {\n        auto attr = readLineNumberAttribute(\n            is64Bit_,\n            format,\n            directories,\n            debugSections_.debugStr,\n            debugSections_.debugLineStr);\n        if (!attr) {\n          return {};\n        }\n        if (i == index && attr->contentTypeCode == DW_LNCT_path) {\n          return std::get<folly::StringPiece>(attr->attrValue);\n        }\n      }\n    }\n    // This could only happen if DWARF5's directory_entry_format doesn't contain\n    // a DW_LNCT_path. Highly unlikely, but we shouldn't crash.\n    return folly::StringPiece(\"<directory not found>\");\n  }\n}\n\nbool DwarfLineNumberVM::readFileName(\n    folly::StringPiece& program, FileName& fn) {\n  fn.relativeName = readNullTerminated(program);\n  if (fn.relativeName.empty()) {\n    return false;\n  }\n  fn.directoryIndex = readULEB(program);\n  // Skip over file size and last modified time\n  readULEB(program);\n  readULEB(program);\n  return true;\n}\n\nbool DwarfLineNumberVM::nextDefineFile(\n    folly::StringPiece& program, FileName& fn) const {\n  while (!program.empty()) {\n    auto opcode = read<uint8_t>(program);\n\n    if (opcode >= opcodeBase_) { // special opcode\n      continue;\n    }\n\n    if (opcode != 0) { // standard opcode\n      // Skip, slurp the appropriate number of LEB arguments\n      uint8_t argCount = standardOpcodeLengths_[opcode - 1];\n      while (argCount--) {\n        readULEB(program);\n      }\n      continue;\n    }\n\n    // Extended opcode\n    auto length = readULEB(program);\n    // the opcode itself should be included in the length, so length >= 1\n    if (length == 0) {\n      FOLLY_SAFE_DFATAL(\"unexpected extended opcode length = 0\");\n      return false;\n    }\n    read<uint8_t>(program); // extended opcode\n    --length;\n\n    if (opcode == DW_LNE_define_file) {\n      if (version_ == 5) {\n        FOLLY_SAFE_DFATAL(\"DW_LNE_define_file deprecated in DWARF5\");\n        return false;\n      }\n      if (!readFileName(program, fn)) {\n        FOLLY_SAFE_DFATAL(\"invalid empty file in DW_LNE_define_file\");\n        return false;\n      }\n      return true;\n    }\n\n    program.advance(length);\n    continue;\n  }\n\n  return false;\n}\n\nDwarfLineNumberVM::StepResult DwarfLineNumberVM::step(\n    folly::StringPiece& program) {\n  auto opcode = read<uint8_t>(program);\n\n  if (opcode >= opcodeBase_) { // special opcode\n    uint8_t adjustedOpcode = opcode - opcodeBase_;\n    uint8_t opAdvance = adjustedOpcode / lineRange_;\n\n    address_ += minLength_ * opAdvance;\n    line_ += lineBase_ + adjustedOpcode % lineRange_;\n\n    basicBlock_ = false;\n    prologueEnd_ = false;\n    epilogueBegin_ = false;\n    discriminator_ = 0;\n    return COMMIT;\n  }\n\n  if (opcode != 0) { // standard opcode\n    // Only interpret opcodes that are recognized by the version we're parsing;\n    // the others are vendor extensions and we should ignore them.\n    switch (opcode) {\n      case DW_LNS_copy:\n        basicBlock_ = false;\n        prologueEnd_ = false;\n        epilogueBegin_ = false;\n        discriminator_ = 0;\n        return COMMIT;\n      case DW_LNS_advance_pc:\n        address_ += minLength_ * readULEB(program);\n        return CONTINUE;\n      case DW_LNS_advance_line:\n        line_ += readSLEB(program);\n        return CONTINUE;\n      case DW_LNS_set_file:\n        file_ = readULEB(program);\n        return CONTINUE;\n      case DW_LNS_set_column:\n        column_ = readULEB(program);\n        return CONTINUE;\n      case DW_LNS_negate_stmt:\n        isStmt_ = !isStmt_;\n        return CONTINUE;\n      case DW_LNS_set_basic_block:\n        basicBlock_ = true;\n        return CONTINUE;\n      case DW_LNS_const_add_pc:\n        address_ += minLength_ * ((255 - opcodeBase_) / lineRange_);\n        return CONTINUE;\n      case DW_LNS_fixed_advance_pc:\n        address_ += read<uint16_t>(program);\n        return CONTINUE;\n      case DW_LNS_set_prologue_end:\n        if (version_ == 2) {\n          break; // not supported in version 2\n        }\n        prologueEnd_ = true;\n        return CONTINUE;\n      case DW_LNS_set_epilogue_begin:\n        if (version_ == 2) {\n          break; // not supported in version 2\n        }\n        epilogueBegin_ = true;\n        return CONTINUE;\n      case DW_LNS_set_isa:\n        if (version_ == 2) {\n          break; // not supported in version 2\n        }\n        isa_ = readULEB(program);\n        return CONTINUE;\n    }\n\n    // Unrecognized standard opcode, slurp the appropriate number of LEB\n    // arguments.\n    uint8_t argCount = standardOpcodeLengths_[opcode - 1];\n    while (argCount--) {\n      readULEB(program);\n    }\n    return CONTINUE;\n  }\n\n  // Extended opcode\n  auto length = readULEB(program);\n  // the opcode itself should be included in the length, so length >= 1\n  if (length == 0) {\n    FOLLY_SAFE_DFATAL(\"unexpected extended opcode length = 0\");\n    return END;\n  }\n  auto extendedOpcode = read<uint8_t>(program);\n  --length;\n\n  switch (extendedOpcode) {\n    case DW_LNE_end_sequence:\n      return END;\n    case DW_LNE_set_address:\n      address_ = read<uintptr_t>(program);\n      return CONTINUE;\n    case DW_LNE_define_file:\n      if (version_ == 5) {\n        FOLLY_SAFE_DFATAL(\"DW_LNE_define_file deprecated in DWARF5\");\n        return END;\n      }\n      // We can't process DW_LNE_define_file here, as it would require us to\n      // use unbounded amounts of state (ie. use the heap).  We'll do a second\n      // pass (using nextDefineFile()) if necessary.\n      break;\n#if !defined(__FreeBSD__)\n    case DW_LNE_set_discriminator:\n      discriminator_ = readULEB(program);\n      return CONTINUE;\n#endif\n  }\n\n  // Unrecognized extended opcode\n  program.advance(length);\n  return CONTINUE;\n}\n\nPath DwarfLineNumberVM::getFullFileName(uint64_t index) const {\n  auto fn = getFileName(index);\n  return Path(\n      // DWARF <= 4: the current dir is not represented in the CU's Line Number\n      // Program Header and relies on the CU's DW_AT_comp_dir.\n      // DWARF 5: the current directory is explicitly present.\n      version_ == 5 ? \"\" : compilationDirectory_,\n      getIncludeDirectory(fn.directoryIndex),\n      fn.relativeName);\n}\n\nbool DwarfLineNumberVM::findAddress(\n    uintptr_t target, Path& file, uint64_t& line) {\n  if (!initializationSuccess_) {\n    return false;\n  }\n  folly::StringPiece program = data_;\n\n  // Within each sequence of instructions, the address may only increase.\n  // Unfortunately, within the same compilation unit, sequences may appear\n  // in any order.  So any sequence is a candidate if it starts at an address\n  // <= the target address, and we know we've found the target address if\n  // a candidate crosses the target address.\n  enum State {\n    START,\n    LOW_SEQ, // candidate\n    HIGH_SEQ\n  };\n  State state = START;\n  reset();\n\n  uint64_t prevFile = 0;\n  uint64_t prevLine = 0;\n  while (!program.empty()) {\n    bool seqEnd = !next(program);\n\n    if (state == START) {\n      if (!seqEnd) {\n        state = address_ <= target ? LOW_SEQ : HIGH_SEQ;\n      }\n    }\n\n    if (state == LOW_SEQ) {\n      if (address_ > target) {\n        // Found it!  Note that \">\" is indeed correct (not \">=\"), as each\n        // sequence is guaranteed to have one entry past-the-end (emitted by\n        // DW_LNE_end_sequence)\n\n        // NOTE: In DWARF <= 4 the file register is non-zero.\n        //   See DWARF 4: 6.2.4 The Line Number Program Header\n        //   \"The line number program assigns numbers to each of the file\n        //   entries in order, beginning with 1, and uses those numbers instead\n        //   of file names in the file register.\"\n        // DWARF 5 has a different include directory/file header and 0 is valid.\n        if (version_ <= 4 && prevFile == 0) {\n          return false;\n        }\n        file = getFullFileName(prevFile);\n        line = prevLine;\n        return true;\n      }\n      prevFile = file_;\n      prevLine = line_;\n    }\n\n    if (seqEnd) {\n      state = START;\n      reset();\n    }\n  }\n\n  return false;\n}\n\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfLineNumberVM.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/DwarfUtil.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\nclass DwarfLineNumberVM {\n public:\n  DwarfLineNumberVM(\n      folly::StringPiece data,\n      folly::StringPiece compilationDirectory,\n      const DebugSections& debugSections);\n\n  bool findAddress(uintptr_t target, Path& file, uint64_t& line);\n\n  /** Gets full file name at given index including directory. */\n  Path getFullFileName(uint64_t index) const;\n\n private:\n  bool init();\n  void reset();\n\n  /** Execute until we commit one new row to the line number matrix */\n  bool next(folly::StringPiece& program);\n  enum StepResult {\n    CONTINUE, // Continue feeding opcodes\n    COMMIT, // Commit new <address, file, line> tuple\n    END, // End of sequence\n  };\n  /** Execute one opcode */\n  StepResult step(folly::StringPiece& program);\n\n  struct FileName {\n    folly::StringPiece relativeName;\n    // 0 = current compilation directory\n    // otherwise, 1-based index in the list of include directories\n    uint64_t directoryIndex;\n  };\n\n  /** Read one FileName object, advance sp */\n  static bool readFileName(folly::StringPiece& program, FileName& fn);\n\n  /**\n   * Get file name at given index; may be in the initial table\n   * (fileNames_) or defined using DW_LNE_define_file (and we reexecute\n   * enough of the program to find it, if so)\n   */\n  FileName getFileName(uint64_t index) const;\n\n  /** Get include directory at given index */\n  folly::StringPiece getIncludeDirectory(uint64_t index) const;\n\n  /**\n   * Execute opcodes until finding a DW_LNE_define_file and return true;\n   * return file at the end.\n   */\n  bool nextDefineFile(folly::StringPiece& program, FileName& fn) const;\n\n  // Initialization\n  bool initializationSuccess_ = false;\n  bool is64Bit_;\n  folly::StringPiece data_;\n  folly::StringPiece compilationDirectory_;\n  const DebugSections& debugSections_;\n\n  // Header\n  uint16_t version_;\n  uint8_t minLength_;\n  bool defaultIsStmt_;\n  int8_t lineBase_;\n  uint8_t lineRange_;\n  uint8_t opcodeBase_;\n  const uint8_t* standardOpcodeLengths_;\n\n  // 6.2.4 The Line Number Program Header.\n  struct {\n    size_t includeDirectoryCount;\n    folly::StringPiece includeDirectories;\n    size_t fileNameCount;\n    folly::StringPiece fileNames;\n  } v4_;\n\n  struct {\n    uint8_t directoryEntryFormatCount;\n    folly::StringPiece directoryEntryFormat;\n    uint64_t directoriesCount;\n    folly::StringPiece directories;\n\n    uint8_t fileNameEntryFormatCount;\n    folly::StringPiece fileNameEntryFormat;\n    uint64_t fileNamesCount;\n    folly::StringPiece fileNames;\n  } v5_;\n\n  // State machine registers\n  uint64_t address_;\n  uint64_t file_;\n  uint64_t line_;\n  uint64_t column_;\n  bool isStmt_;\n  bool basicBlock_;\n  bool endSequence_;\n  bool prologueEnd_;\n  bool epilogueBegin_;\n  uint64_t isa_;\n  uint64_t discriminator_;\n};\n\n#endif\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfSection.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/DwarfSection.h>\n\n#include <folly/debugging/symbolizer/DwarfUtil.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\nnamespace folly {\nnamespace symbolizer {\n\nDwarfSection::DwarfSection(folly::StringPiece d) : is64Bit_(false), data_(d) {}\n\n// Next chunk in section\nbool DwarfSection::next(folly::StringPiece& chunk) {\n  chunk = data_;\n  if (chunk.empty()) {\n    return false;\n  }\n\n  // Initial length is a uint32_t value for a 32-bit section, and\n  // a 96-bit value (0xffffffff followed by the 64-bit length) for a 64-bit\n  // section.\n  auto initialLength = read<uint32_t>(chunk);\n  is64Bit_ = (initialLength == uint32_t(-1));\n  auto length = is64Bit_ ? read<uint64_t>(chunk) : initialLength;\n  if (length > chunk.size()) {\n    FOLLY_SAFE_DFATAL(\n        \"invalid DWARF section, length: \",\n        length,\n        \" chunk.size(): \",\n        chunk.size());\n    return false;\n  }\n  chunk.reset(chunk.data(), length);\n  data_.assign(chunk.end(), data_.end());\n  return true;\n}\n\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfSection.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Range.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\nnamespace folly {\nnamespace symbolizer {\n\n/**\n * DWARF section made up of chunks, each prefixed with a length header. The\n * length indicates whether the chunk is DWARF-32 or DWARF-64, which guides\n * interpretation of \"section offset\" records. (yes, DWARF-32 and DWARF-64\n * sections may coexist in the same file).\n */\nclass DwarfSection {\n public:\n  DwarfSection() : is64Bit_(false) {}\n\n  explicit DwarfSection(folly::StringPiece d);\n\n  /**\n   * Return next chunk, if any; the 4- or 12-byte length was already\n   * parsed and isn't part of the chunk.\n   */\n  bool next(folly::StringPiece& chunk);\n\n  /** Is the current chunk 64 bit? */\n  bool is64Bit() const { return is64Bit_; }\n\n private:\n  // Yes, 32- and 64- bit sections may coexist.  Yikes!\n  bool is64Bit_;\n  folly::StringPiece data_;\n};\n\n} // namespace symbolizer\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfUtil.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/DwarfUtil.h>\n\n#include <array>\n#include <type_traits>\n\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/Elf.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/Unistd.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\n#include <dwarf.h> // @manual\n\n// We need a single dwarf5 tag, but may not be building against\n// a new enough libdwarf, so just define it ourselves.\n#ifndef DW_TAG_skeleton_unit\n#define DW_TAG_skeleton_unit 0x4a\n#endif\n\nnamespace folly {\nnamespace symbolizer {\n\nfolly::StringPiece getElfSection(const ElfFile* elf, const char* name) {\n  const ElfShdr* elfSection = elf->getSectionByName(name);\n  if (!elfSection) {\n    return {};\n  }\n#ifdef SHF_COMPRESSED\n  if (elfSection->sh_flags & SHF_COMPRESSED) {\n    return {};\n  }\n#endif\n  return elf->getSectionBody(*elfSection);\n}\n\n// Read (bitwise) an unsigned number of N bytes (N in 1, 2, 3, 4).\ntemplate <size_t N>\nuint64_t readU64(folly::StringPiece& sp) {\n  FOLLY_SAFE_CHECK(sp.size() >= N, \"underflow\");\n  uint64_t x = 0;\n  memcpy(&x, sp.data(), N);\n  sp.advance(N);\n  return x;\n}\n\n// Read ULEB (unsigned) varint value; algorithm from the DWARF spec\nuint64_t readULEB(folly::StringPiece& sp, uint8_t& shift, uint8_t& val) {\n  uint64_t r = 0;\n  shift = 0;\n  do {\n    val = read<uint8_t>(sp);\n    r |= ((uint64_t)(val & 0x7f) << shift);\n    shift += 7;\n  } while (val & 0x80);\n  return r;\n}\n\nuint64_t readULEB(folly::StringPiece& sp) {\n  uint8_t shift;\n  uint8_t val;\n  return readULEB(sp, shift, val);\n}\n\n// Read SLEB (signed) varint value; algorithm from the DWARF spec\nint64_t readSLEB(folly::StringPiece& sp) {\n  uint8_t shift;\n  uint8_t val;\n  uint64_t r = readULEB(sp, shift, val);\n\n  if (shift < 64 && (val & 0x40)) {\n    r |= -(1ULL << shift); // sign extend\n  }\n\n  return r;\n}\n\n// Read a value of \"section offset\" type, which may be 4 or 8 bytes\nuint64_t readOffset(folly::StringPiece& sp, bool is64Bit) {\n  return is64Bit ? read<uint64_t>(sp) : read<uint32_t>(sp);\n}\n\n// Read \"len\" bytes\nfolly::StringPiece readBytes(folly::StringPiece& sp, uint64_t len) {\n  FOLLY_SAFE_CHECK(len <= sp.size(), \"invalid string length\");\n  folly::StringPiece ret(sp.data(), len);\n  sp.advance(len);\n  return ret;\n}\n\n// Read a null-terminated string\nfolly::StringPiece readNullTerminated(folly::StringPiece& sp) {\n  const char* p = static_cast<const char*>(memchr(sp.data(), 0, sp.size()));\n  FOLLY_SAFE_CHECK(p, \"invalid null-terminated string\");\n  folly::StringPiece ret(sp.data(), p);\n  sp.assign(p + 1, sp.end());\n  return ret;\n}\n\nfolly::StringPiece getStringFromStringSection(\n    folly::StringPiece str, uint64_t offset) {\n  FOLLY_SAFE_CHECK(offset < str.size(), \"invalid string offset\");\n  str.advance(offset);\n  return readNullTerminated(str);\n}\n\nAttributeSpec readAttributeSpec(folly::StringPiece& sp) {\n  AttributeSpec spec;\n  spec.name = readULEB(sp);\n  spec.form = readULEB(sp);\n  if (spec.form == DW_FORM_implicit_const) {\n    spec.implicitConst = readSLEB(sp);\n  }\n  return spec;\n}\n\n// Reads an abbreviation from a StringPiece, return true if at end; advance sp\nbool readAbbreviation(folly::StringPiece& section, DIEAbbreviation& abbr) {\n  // Abbreviation code\n  abbr.code = readULEB(section);\n  if (abbr.code == 0) {\n    return false;\n  }\n\n  // Abbreviation tag\n  abbr.tag = readULEB(section);\n\n  // does this entry have children?\n  abbr.hasChildren = (read<uint8_t>(section) != DW_CHILDREN_no);\n\n  // attributes\n  const char* attributeBegin = section.data();\n  for (;;) {\n    if (section.empty()) {\n      FOLLY_SAFE_DFATAL(\"invalid attribute section\");\n      return false;\n    }\n    auto spec = readAttributeSpec(section);\n    if (!spec) {\n      break;\n    }\n  }\n  abbr.attributes.assign(attributeBegin, section.data());\n  return true;\n}\n\nnamespace {\n\nDIEAbbreviation getAbbreviation(\n    folly::StringPiece debugAbbrev, uint64_t code, uint64_t offset) {\n  // Linear search in the .debug_abbrev section, starting at offset\n  debugAbbrev.advance(offset);\n\n  DIEAbbreviation abbr;\n  while (readAbbreviation(debugAbbrev, abbr)) {\n    if (abbr.code == code) {\n      return abbr;\n    }\n  }\n\n  FOLLY_SAFE_DFATAL(\"could not find abbreviation code\");\n  return {};\n}\n\n// Parse compilation unit info, abbrev and str_offsets offset from\n// .debug_cu_index section.\nbool findCompiliationOffset(\n    folly::StringPiece debugCuIndex, uint64_t dwoId, CompilationUnit& cu) {\n  if (debugCuIndex.empty()) {\n    return false;\n  }\n\n  // v4: GCC Debug Fission defines version as an unsigned 4B field with value\n  // of 2.\n  // https://gcc.gnu.org/wiki/DebugFissionDWP#Format_of_the_CU_and_TU_Index_Sections\n  //\n  // v5: DWARF5 7.3.5.3 Format of the CU and TU Index Sections\n  // The first 2 header entries are version (2B, value=5) and padding (2B,\n  // value=0)\n  //\n  // Both were read into version (4B) and on little endian will have value=5.\n  auto version = read<uint32_t>(debugCuIndex);\n  if (version != 2 && version != (5 << (kIsLittleEndian ? 0 : 16))) {\n    return false;\n  }\n  auto numColumns = read<uint32_t>(debugCuIndex);\n  read<uint32_t>(debugCuIndex);\n  auto numBuckets = read<uint32_t>(debugCuIndex);\n\n  // Get index of the CU matches the given dwo id.\n  folly::StringPiece signatureSection = debugCuIndex;\n  folly::StringPiece indexesSection = debugCuIndex;\n  indexesSection.advance(numBuckets * sizeof(uint64_t));\n  ssize_t idx = -1;\n  for (unsigned i = 0; i < numBuckets; i++) {\n    uint64_t hash = read<uint64_t>(signatureSection);\n    uint32_t index = read<uint32_t>(indexesSection);\n    if (hash == dwoId) {\n      idx = index;\n      break;\n    }\n  }\n  if (idx <= 0) {\n    return false;\n  }\n\n  // Skip signature and parallel table of indexes\n  debugCuIndex.advance(numBuckets * sizeof(uint64_t));\n  debugCuIndex.advance(numBuckets * sizeof(uint32_t));\n\n  // column headers\n  ssize_t infoSectionIndex = -1;\n  ssize_t abbrevSectionIndex = -1;\n  ssize_t strOffsetsSectionIndex = -1;\n  ssize_t rnglistsSectionIndex = -1;\n  for (unsigned i = 0; i != numColumns; ++i) {\n    auto index = read<uint32_t>(debugCuIndex);\n    if (index == DW_SECT_INFO) {\n      infoSectionIndex = i;\n    } else if (index == DW_SECT_ABBREV) {\n      abbrevSectionIndex = i;\n    } else if (index == DW_SECT_STR_OFFSETS) {\n      strOffsetsSectionIndex = i;\n    } else if (index == DW_SECT_RNGLISTS) {\n      rnglistsSectionIndex = i;\n    }\n  }\n\n  // DW_SECT_RNGLISTS/rnglistsSectionIndex only appears in DWARF5 .dwp, not in\n  // DWARF4 .dwp\n  if (infoSectionIndex == -1 || abbrevSectionIndex == -1 ||\n      strOffsetsSectionIndex == -1) {\n    return false;\n  }\n\n  // debugCuIndex offsets\n  debugCuIndex.advance((idx - 1) * numColumns * sizeof(uint32_t));\n  for (unsigned i = 0; i != numColumns; ++i) {\n    auto offset = read<uint32_t>(debugCuIndex);\n    if (i == infoSectionIndex) {\n      cu.offset = offset;\n    }\n    if (i == abbrevSectionIndex) {\n      cu.abbrevOffset = offset;\n    }\n    if (i == strOffsetsSectionIndex) {\n      cu.strOffsetsBase = offset;\n    }\n    if (i == rnglistsSectionIndex) {\n      cu.rnglistsBase = offset;\n    }\n  }\n  return true;\n}\n\nbool parseCompilationUnitMetadata(CompilationUnit& cu, size_t offset) {\n  auto debugInfo = cu.debugSections.debugInfo;\n  folly::StringPiece chunk(debugInfo);\n  cu.offset = offset;\n  chunk.advance(offset);\n  // 1) unit_length\n  auto initialLength = read<uint32_t>(chunk);\n  cu.is64Bit = (initialLength == uint32_t(-1));\n  cu.size = cu.is64Bit ? read<uint64_t>(chunk) : initialLength;\n  if (cu.size > chunk.size()) {\n    FOLLY_SAFE_DFATAL(\n        \"invalid size: \", cu.size, \" chunk.size(): \", chunk.size());\n    return false;\n  }\n  cu.size += cu.is64Bit ? 12 : 4;\n\n  // 2) version\n  cu.version = read<uint16_t>(chunk);\n  if (cu.version < 2 || cu.version > 5) {\n    FOLLY_SAFE_DFATAL(\"invalid info version: \", cu.version);\n    return false;\n  }\n\n  if (cu.version == 5) {\n    // DWARF5: 7.5.1.1 Full and Partial Compilation Unit Headers\n    // 3) unit_type (new DWARF 5)\n    cu.unitType = read<uint8_t>(chunk);\n    if (cu.unitType != DW_UT_compile && cu.unitType != DW_UT_skeleton &&\n        cu.unitType != DW_UT_split_compile) {\n      return false;\n    }\n    // 4) address_size\n    cu.addrSize = read<uint8_t>(chunk);\n    if (cu.addrSize != sizeof(uintptr_t)) {\n      FOLLY_SAFE_DFATAL(\"invalid address size: \", cu.addrSize);\n      return false;\n    }\n\n    // 5) debug_abbrev_offset\n    // This can be already set in from .debug_cu_index if dwp file is used.\n    uint64_t abbrevOffset = readOffset(chunk, cu.is64Bit);\n    if (!cu.abbrevOffset.hasValue()) {\n      cu.abbrevOffset = abbrevOffset;\n    }\n\n    if (cu.unitType == DW_UT_skeleton || cu.unitType == DW_UT_split_compile) {\n      // 6) dwo_id\n      cu.dwoId = read<uint64_t>(chunk);\n    }\n  } else {\n    // DWARF4 has a single type of unit in .debug_info\n    cu.unitType = DW_UT_compile;\n    // 3) debug_abbrev_offset\n    // This can be already set in from .debug_cu_index if dwp file is used.\n    uint64_t abbrevOffset = readOffset(chunk, cu.is64Bit);\n    if (!cu.abbrevOffset.hasValue()) {\n      cu.abbrevOffset = abbrevOffset;\n    }\n    // 4) address_size\n    cu.addrSize = read<uint8_t>(chunk);\n    if (cu.addrSize != sizeof(uintptr_t)) {\n      FOLLY_SAFE_DFATAL(\"invalid address size: \", cu.addrSize);\n      return false;\n    }\n  }\n  cu.firstDie = chunk.data() - debugInfo.data();\n  Die die = getDieAtOffset(cu, cu.firstDie);\n  if (die.abbr.tag != DW_TAG_compile_unit &&\n      die.abbr.tag != DW_TAG_skeleton_unit) {\n    return false;\n  }\n\n  // Read the DW_AT_*_base attributes.\n  // Attributes which use FORMs relative to these base attrs\n  // will not have valid values during this first pass!\n  forEachAttribute(cu, die, [&](const Attribute& attr) {\n    switch (attr.spec.name) {\n      case DW_AT_comp_dir:\n        cu.compDir = std::get<folly::StringPiece>(attr.attrValue);\n        break;\n      case DW_AT_addr_base:\n      case DW_AT_GNU_addr_base:\n        cu.addrBase = std::get<uint64_t>(attr.attrValue);\n        break;\n      case DW_AT_GNU_ranges_base:\n        cu.rangesBase = std::get<uint64_t>(attr.attrValue);\n        break;\n      case DW_AT_loclists_base:\n        cu.loclistsBase = std::get<uint64_t>(attr.attrValue);\n        break;\n      case DW_AT_rnglists_base:\n        cu.rnglistsBase = std::get<uint64_t>(attr.attrValue);\n        break;\n      case DW_AT_str_offsets_base:\n        cu.strOffsetsBase = std::get<uint64_t>(attr.attrValue);\n        break;\n      case DW_AT_GNU_dwo_name:\n      case DW_AT_dwo_name: // dwo id is set above in dwarf5.\n        cu.dwoName = std::get<folly::StringPiece>(attr.attrValue);\n        break;\n      case DW_AT_GNU_dwo_id:\n        cu.dwoId = std::get<uint64_t>(attr.attrValue);\n        break;\n    }\n    return true; // continue forEachAttribute\n  });\n\n  return true;\n}\n\n} // namespace\n\nCompilationUnits getCompilationUnits(\n    ElfCacheBase* elfCache,\n    const DebugSections& debugSections,\n    uint64_t offset,\n    bool requireSplitDwarf) {\n  const folly::StringPiece debugInfo = debugSections.debugInfo;\n  FOLLY_SAFE_DCHECK(offset < debugInfo.size(), \"unexpected offset\");\n  CompilationUnits cu;\n  cu.mainCompilationUnit.debugSections = debugSections;\n\n  if (!parseCompilationUnitMetadata(cu.mainCompilationUnit, offset)) {\n    return cu;\n  }\n\n  if (!requireSplitDwarf || !cu.mainCompilationUnit.dwoId.hasValue() ||\n      !cu.mainCompilationUnit.dwoName.hasValue()) {\n    cu.mainCompilationUnit.rangesBase.reset();\n    return cu;\n  }\n\n  CompilationUnit splitCU;\n  // https://gcc.gnu.org/wiki/DebugFission\n  // In order for the debugger to find the contribution to the .debug_addr\n  // section corresponding to a particular compilation unit, the skeleton\n  // DW_TAG_compile_unit entry in the .o file's .debug_info section will also\n  // contain a DW_AT_addr_base attribute whose value points to the base of that\n  // compilation unit's .debug_addr contribution.\n  splitCU.addrBase = cu.mainCompilationUnit.addrBase;\n  // The skeleton DW_TAG_compile_unit entry in the .o file’s .debug_info section\n  // will contain a DW_AT_ranges_base attribute whose (relocated) value points\n  // to the base of that compilation unit’s .debug_ranges contribution. All\n  // values using DW_FORM_sec_offset for a DW_AT_ranges or DW_AT_start_scope\n  // attribute (i.e., those attributes whose values are of class rangelistptr)\n  // must be interpreted as offsets relative to the base address given by the\n  // DW_AT_ranges_base attribute in the skeleton compilation unit DIE.\n  splitCU.rangesBase = cu.mainCompilationUnit.rangesBase;\n\n  // NOTE: For backwards compatibility reasons, DW_AT_GNU_ranges_base\n  // applies only to DIE within dwo, but not within skeleton CU.\n  if (cu.mainCompilationUnit.version <= 4) {\n    cu.mainCompilationUnit.rangesBase.reset();\n  }\n\n  // Read from dwo file.\n  static const size_t kPathLimit = 4 << 10; // 4KB\n  ElfFile* elf = nullptr;\n  {\n    char path[kPathLimit];\n    if (cu.mainCompilationUnit.compDir.size() + strlen(\"/\") +\n            cu.mainCompilationUnit.dwoName->size() + 1 >\n        kPathLimit) {\n      return cu;\n    }\n    path[0] = '\\0';\n    strncat(\n        path,\n        cu.mainCompilationUnit.compDir.data(),\n        cu.mainCompilationUnit.compDir.size());\n    strcat(path, \"/\");\n    strncat(\n        path,\n        cu.mainCompilationUnit.dwoName->data(),\n        cu.mainCompilationUnit.dwoName->size());\n    elf = elfCache->getFile(path).get();\n  }\n\n  // Read from dwp file if dwo file doesn't exist.\n  bool useDWP = false;\n  if (elf == nullptr) {\n    if (strlen(cu.mainCompilationUnit.debugSections.elf->filepath()) + 5 >\n        kPathLimit) {\n      return cu;\n    }\n    char dwpPath[kPathLimit];\n    strcpy(dwpPath, cu.mainCompilationUnit.debugSections.elf->filepath());\n    strcat(dwpPath, \".dwp\");\n    elf = elfCache->getFile(dwpPath).get();\n    if (elf == nullptr) {\n      return cu;\n    }\n    useDWP = true;\n  }\n\n  splitCU.debugSections = {\n      .elf = elf,\n      .debugCuIndex = getElfSection(elf, \".debug_cu_index\"),\n      .debugAbbrev = getElfSection(elf, \".debug_abbrev.dwo\"),\n      .debugAddr = cu.mainCompilationUnit.debugSections.debugAddr,\n      .debugAranges = cu.mainCompilationUnit.debugSections.debugAranges,\n      .debugInfo = getElfSection(elf, \".debug_info.dwo\"),\n      .debugLine = getElfSection(elf, \".debug_line.dwo\"),\n      .debugLineStr = cu.mainCompilationUnit.debugSections.debugLineStr,\n      .debugLoclists = getElfSection(elf, \".debug_loclists.dwo\"),\n      .debugRanges = cu.mainCompilationUnit.debugSections.debugRanges,\n      .debugRnglists = getElfSection(elf, \".debug_rnglists.dwo\"),\n      .debugStr = getElfSection(elf, \".debug_str.dwo\"),\n      .debugStrOffsets = getElfSection(elf, \".debug_str_offsets.dwo\")};\n  if (splitCU.debugSections.debugInfo.empty() ||\n      splitCU.debugSections.debugAbbrev.empty() ||\n      splitCU.debugSections.debugLine.empty() ||\n      splitCU.debugSections.debugStr.empty()) {\n    return cu;\n  }\n\n  if (useDWP) {\n    if (!findCompiliationOffset(\n            splitCU.debugSections.debugCuIndex,\n            *cu.mainCompilationUnit.dwoId,\n            splitCU) ||\n        !parseCompilationUnitMetadata(splitCU, splitCU.offset)) {\n      return cu;\n    }\n    // 3.1.3 Split Full Compilation Unit Entries\n    // The following attributes are not part of a split full compilation unit\n    // entry but instead are inherited (if present) from the corresponding\n    // skeleton compilation unit: DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges,\n    // DW_AT_stmt_list, DW_AT_comp_dir, DW_AT_str_offsets_base, DW_AT_addr_base\n    // and DW_AT_rnglists_base.\n    if (cu.mainCompilationUnit.version == 5) {\n      // 7.26 String Offsets Table\n      // Each set of entries in the string offsets table contained in the\n      // .debug_str_offsets\n      //  or .debug_str_offsets.dwo section begins with a header containing:\n      //  1. unit_length (initial length)\n      //     A 4-byte or 12-byte length containing the length of the set of\n      //     entries for this compilation unit, not including the length field\n      //     itself.\n      //  2. version (uhalf)\n      //     A 2-byte version identifier containing the value 5.\n      //  3. padding (uhalf)\n      //     Reserved to DWARF (must be zero).\n      splitCU.strOffsetsBase =\n          splitCU.strOffsetsBase.value_or(0) + (splitCU.is64Bit ? 16 : 8);\n      // 7.28 Range List Table\n      // 1. unit_length (initial length)\n      //    A 4-byte or 12-byte length containing the length of the set of\n      //    entries for this compilation unit, not including the length field\n      //    itself.\n      // 2. version (uhalf)\n      //    A 2-byte version identifier containing the value 5.\n      // 3. address_size (ubyte)\n      //    A 1-byte unsigned integer containing the size in bytes of an\n      //    address.\n      // 4. segment_selector_size (ubyte)\n      //    A 1-byte unsigned integer containing the size in bytes of a segment\n      //    selector on the target system.\n      // 5. offset_entry_count (uword)\n      //    A 4-byte count of the number of offsets that follow the header.\n      splitCU.rnglistsBase =\n          splitCU.rnglistsBase.value_or(0) + (splitCU.is64Bit ? 20 : 12);\n    }\n  } else {\n    // Skip CUs like DW_TAG_type_unit\n    for (size_t dwoOffset = 0;\n         !parseCompilationUnitMetadata(splitCU, dwoOffset) &&\n         dwoOffset < splitCU.debugSections.debugInfo.size();) {\n      dwoOffset = dwoOffset + splitCU.size;\n    }\n    if (!splitCU.dwoId || *splitCU.dwoId != *cu.mainCompilationUnit.dwoId) {\n      return cu;\n    }\n    if (cu.mainCompilationUnit.version == 5) {\n      splitCU.strOffsetsBase = splitCU.is64Bit ? 16 : 8;\n      splitCU.rnglistsBase = splitCU.is64Bit ? 20 : 12;\n    }\n  }\n\n  cu.splitCU.emplace(std::move(splitCU));\n  return cu;\n}\n\nDie getDieAtOffset(const CompilationUnit& cu, uint64_t offset) {\n  const folly::StringPiece debugInfo = cu.debugSections.debugInfo;\n  FOLLY_SAFE_DCHECK(offset < debugInfo.size(), \"unexpected offset\");\n  Die die;\n  folly::StringPiece sp = folly::StringPiece{\n      debugInfo.data() + offset, debugInfo.data() + cu.offset + cu.size};\n  die.offset = offset;\n  die.is64Bit = cu.is64Bit;\n  auto code = readULEB(sp);\n  die.code = code;\n  if (code == 0) {\n    return die;\n  }\n  die.attrOffset = sp.data() - debugInfo.data() - offset;\n  die.abbr = !cu.abbrCache.empty() && die.code < kMaxAbbreviationEntries\n      ? cu.abbrCache[die.code - 1]\n      : getAbbreviation(\n            cu.debugSections.debugAbbrev,\n            die.code,\n            cu.abbrevOffset.value_or(0));\n\n  return die;\n}\n\nAttribute readAttribute(\n    const CompilationUnit& cu,\n    const Die& die,\n    AttributeSpec spec,\n    folly::StringPiece& info) {\n  // DWARF 5 introduces new FORMs whose values are relative to some base\n  // attrs: DW_AT_str_offsets_base, DW_AT_rnglists_base, DW_AT_addr_base.\n  // Debug Fission DWARF 4 uses GNU DW_AT_GNU_ranges_base &\n  // DW_AT_GNU_addr_base.\n  //\n  // The order in which attributes appear in a CU is not defined.\n  // The DW_AT_*_base attrs may appear after attributes that need them.\n  // The DW_AT_*_base attrs are CU specific; so we read them just after\n  // reading the CU header. During this first pass return empty values\n  // when encountering a FORM that depends on DW_AT_*_base.\n  auto getStringUsingOffsetTable = [&](uint64_t index) {\n    if (!cu.strOffsetsBase.has_value() &&\n        !(cu.version < 5 && cu.dwoId.has_value())) {\n      return folly::StringPiece();\n    }\n    // DWARF 5: 7.26 String Offsets Table\n    // The DW_AT_str_offsets_base attribute points to the first entry\n    // following the header. The entries are indexed sequentially from\n    // this base entry, starting from 0. For DWARF 4 version DWO files,\n    // the value of DW_AT_str_offsets_base is implicitly zero.\n    auto strOffsetsOffset = cu.strOffsetsBase.value_or(0) +\n        index * (cu.is64Bit ? sizeof(uint64_t) : sizeof(uint32_t));\n    auto sp = cu.debugSections.debugStrOffsets.subpiece(strOffsetsOffset);\n    uint64_t strOffset = readOffset(sp, cu.is64Bit);\n    return getStringFromStringSection(cu.debugSections.debugStr, strOffset);\n  };\n\n  auto readDebugAddr = [&](uint64_t index) {\n    if (!cu.addrBase.has_value()) {\n      return uint64_t(0);\n    }\n    // DWARF 5: 7.27 Address Table\n    // The DW_AT_addr_base attribute points to the first entry following\n    // the header. The entries are indexed sequentially from this base\n    // entry, starting from 0.\n    auto sp = cu.debugSections.debugAddr.subpiece(\n        *cu.addrBase + index * sizeof(uint64_t));\n    return read<uint64_t>(sp);\n  };\n\n  switch (spec.form) {\n    case DW_FORM_addr:\n      return {spec, die, read<uintptr_t>(info)};\n    case DW_FORM_block1:\n      return {spec, die, readBytes(info, read<uint8_t>(info))};\n    case DW_FORM_block2:\n      return {spec, die, readBytes(info, read<uint16_t>(info))};\n    case DW_FORM_block4:\n      return {spec, die, readBytes(info, read<uint32_t>(info))};\n    case DW_FORM_block:\n      [[fallthrough]];\n    case DW_FORM_exprloc:\n      return {spec, die, readBytes(info, readULEB(info))};\n    case DW_FORM_data1:\n      [[fallthrough]];\n    case DW_FORM_ref1:\n      return {spec, die, read<uint8_t>(info)};\n    case DW_FORM_data2:\n      [[fallthrough]];\n    case DW_FORM_ref2:\n      return {spec, die, read<uint16_t>(info)};\n    case DW_FORM_data4:\n      [[fallthrough]];\n    case DW_FORM_ref4:\n      return {spec, die, read<uint32_t>(info)};\n    case DW_FORM_data8:\n      [[fallthrough]];\n    case DW_FORM_ref8:\n      [[fallthrough]];\n    case DW_FORM_ref_sig8:\n      return {spec, die, read<uint64_t>(info)};\n    case DW_FORM_sdata:\n      return {spec, die, to_unsigned(readSLEB(info))};\n    case DW_FORM_udata:\n      [[fallthrough]];\n    case DW_FORM_ref_udata:\n      return {spec, die, readULEB(info)};\n    case DW_FORM_flag:\n      return {spec, die, read<uint8_t>(info)};\n    case DW_FORM_flag_present:\n      return {spec, die, 1u};\n    case DW_FORM_sec_offset:\n      [[fallthrough]];\n    case DW_FORM_ref_addr:\n      return {spec, die, readOffset(info, die.is64Bit)};\n    case DW_FORM_string:\n      return {spec, die, readNullTerminated(info)};\n    case DW_FORM_strp:\n      return {\n          spec,\n          die,\n          getStringFromStringSection(\n              cu.debugSections.debugStr, readOffset(info, die.is64Bit))};\n    case DW_FORM_indirect: // form is explicitly specified\n      // Update spec with the actual FORM.\n      spec.form = readULEB(info);\n      return readAttribute(cu, die, spec, info);\n\n    // DWARF 5:\n    case DW_FORM_implicit_const: // form is explicitly specified\n      // For attributes with this form, the attribute specification\n      // contains a third part, which is a signed LEB128 number. The value\n      // of this number is used as the value of the attribute, and no\n      // value is stored in the .debug_info section.\n      return {spec, die, to_unsigned(spec.implicitConst)};\n\n    case DW_FORM_addrx:\n    case DW_FORM_GNU_addr_index:\n      return {spec, die, readDebugAddr(readULEB(info))};\n    case DW_FORM_addrx1:\n      return {spec, die, readDebugAddr(readU64<1>(info))};\n    case DW_FORM_addrx2:\n      return {spec, die, readDebugAddr(readU64<2>(info))};\n    case DW_FORM_addrx3:\n      return {spec, die, readDebugAddr(readU64<3>(info))};\n    case DW_FORM_addrx4:\n      return {spec, die, readDebugAddr(readU64<4>(info))};\n\n    case DW_FORM_line_strp:\n      return {\n          spec,\n          die,\n          getStringFromStringSection(\n              cu.debugSections.debugLineStr, readOffset(info, die.is64Bit))};\n\n    case DW_FORM_strx:\n      return {spec, die, getStringUsingOffsetTable(readULEB(info))};\n    case DW_FORM_strx1:\n      return {spec, die, getStringUsingOffsetTable(readU64<1>(info))};\n    case DW_FORM_strx2:\n      return {spec, die, getStringUsingOffsetTable(readU64<2>(info))};\n    case DW_FORM_strx3:\n      return {spec, die, getStringUsingOffsetTable(readU64<3>(info))};\n    case DW_FORM_strx4:\n      return {spec, die, getStringUsingOffsetTable(readU64<4>(info))};\n\n    case DW_FORM_GNU_str_index:\n      return {spec, die, getStringUsingOffsetTable(readULEB(info))};\n\n    case DW_FORM_rnglistx: {\n      auto index = readULEB(info);\n      if (!cu.rnglistsBase.has_value()) {\n        return {spec, die, 0u};\n      }\n      const uint64_t offsetSize =\n          cu.is64Bit ? sizeof(uint64_t) : sizeof(uint32_t);\n      auto sp = cu.debugSections.debugRnglists.subpiece(\n          *cu.rnglistsBase + index * offsetSize);\n      auto offset = readOffset(sp, cu.is64Bit);\n      return {spec, die, *cu.rnglistsBase + offset};\n    }\n\n    case DW_FORM_loclistx: {\n      auto index = readULEB(info);\n      if (!cu.loclistsBase.has_value()) {\n        return {spec, die, 0u};\n      }\n      const uint64_t offsetSize =\n          cu.is64Bit ? sizeof(uint64_t) : sizeof(uint32_t);\n      auto sp = cu.debugSections.debugLoclists.subpiece(\n          *cu.loclistsBase + index * offsetSize);\n      auto offset = readOffset(sp, cu.is64Bit);\n      return {spec, die, *cu.loclistsBase + offset};\n    }\n\n    case DW_FORM_data16:\n      return {spec, die, readBytes(info, 16)};\n\n    case DW_FORM_ref_sup4:\n    case DW_FORM_ref_sup8:\n    case DW_FORM_strp_sup:\n      FOLLY_SAFE_DFATAL(\n          \"Unexpected DWARF5 supplimentary object files: \", spec.form);\n      return {spec, die, 0u};\n\n    default:\n      FOLLY_SAFE_DFATAL(\"invalid attribute form: \", spec.form);\n      return {spec, die, 0u};\n  }\n}\n\n/*\n * Iterate over all attributes of the given DIE, calling the given\n * callable for each. Iteration is stopped early if any of the calls\n * return false.\n */\nsize_t forEachAttribute(\n    const CompilationUnit& cu,\n    const Die& die,\n    folly::FunctionRef<bool(const Attribute&)> f) {\n  auto attrs = die.abbr.attributes;\n  auto values = folly::StringPiece{\n      cu.debugSections.debugInfo.data() + die.offset + die.attrOffset,\n      cu.debugSections.debugInfo.data() + cu.offset + cu.size};\n  while (auto spec = readAttributeSpec(attrs)) {\n    auto attr = readAttribute(cu, die, spec, values);\n    if (!f(attr)) {\n      return static_cast<size_t>(-1);\n    }\n  }\n  return values.data() - cu.debugSections.debugInfo.data();\n}\n\nfolly::StringPiece getFunctionNameFromDie(\n    const CompilationUnit& srcu, const Die& die) {\n  folly::StringPiece name;\n  forEachAttribute(srcu, die, [&](const Attribute& attr) {\n    switch (attr.spec.name) {\n      case DW_AT_linkage_name:\n        name = std::get<folly::StringPiece>(attr.attrValue);\n        break;\n      case DW_AT_name:\n        // NOTE: when DW_AT_linkage_name and DW_AT_name match, dwarf\n        // emitters omit DW_AT_linkage_name (to save space). If present\n        // DW_AT_linkage_name should always be preferred (mangled C++ name\n        // vs just the function name).\n        if (name.empty()) {\n          name = std::get<folly::StringPiece>(attr.attrValue);\n        }\n        break;\n    }\n    return true; // continue forEachAttribute\n  });\n  return name;\n}\n\nfolly::StringPiece getFunctionName(\n    const CompilationUnit& srcu, uint64_t dieOffset) {\n  auto declDie = getDieAtOffset(srcu, dieOffset);\n  auto name = getFunctionNameFromDie(srcu, declDie);\n  return name.empty()\n      ? getFunctionNameFromDie(srcu, findDefinitionDie(srcu, declDie))\n      : name;\n}\n\nDie findDefinitionDie(const CompilationUnit& cu, const Die& die) {\n  // Find the real definition instead of declaration.\n  // DW_AT_specification: Incomplete, non-defining, or separate declaration\n  // corresponding to a declaration\n  auto offset = getAttribute<uint64_t>(cu, die, DW_AT_specification);\n  if (!offset) {\n    return die;\n  }\n  return getDieAtOffset(cu, cu.offset + offset.value());\n}\n\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/symbolizer/DwarfUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// DWARF record parser\n\n#pragma once\n\n#include <variant>\n\n#include <folly/Function.h>\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/Elf.h>\n#include <folly/debugging/symbolizer/ElfCache.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n#include <dwarf.h> // @manual\n\nnamespace folly {\nnamespace symbolizer {\n\n/**\n * More than one location info may exist if current frame is an inline\n * function call.\n */\nconst uint32_t kMaxInlineLocationInfoPerFrame = 20;\n\n// Maximum number of DIEAbbreviation to cache in a compilation unit. Used to\n// speed up inline function lookup.\nconst uint32_t kMaxAbbreviationEntries = 1000;\n\n// A struct contains an Elf object and different debug sections.\nstruct DebugSections {\n  const ElfFile* elf;\n  folly::StringPiece debugCuIndex; // .debug_cu_index\n  folly::StringPiece debugAbbrev; // .debug_abbrev\n  folly::StringPiece debugAddr; // .debug_addr (DWARF 5)\n  folly::StringPiece debugAranges; // .debug_aranges\n  folly::StringPiece debugInfo; // .debug_info\n  folly::StringPiece debugLine; // .debug_line\n  folly::StringPiece debugLineStr; // .debug_line_str (DWARF 5)\n  folly::StringPiece debugLoclists; // .debug_loclists (DWARF 5)\n  folly::StringPiece debugRanges; // .debug_ranges\n  folly::StringPiece debugRnglists; // .debug_rnglists (DWARF 5)\n  folly::StringPiece debugStr; // .debug_str\n  folly::StringPiece debugStrOffsets; // .debug_str_offsets (DWARF 5)\n};\n\n// Abbreviation for a Debugging Information Entry.\nstruct DIEAbbreviation {\n  uint64_t code = 0;\n  uint64_t tag = 0;\n  bool hasChildren = false;\n  folly::StringPiece attributes;\n};\n\n// A struct that contains the metadata of a compilation unit.\nstruct CompilationUnit {\n  DebugSections debugSections;\n\n  bool is64Bit = false;\n  uint8_t version = 0;\n  uint8_t unitType = DW_UT_compile; // DW_UT_compile or DW_UT_skeleton\n  uint8_t addrSize = 0;\n  // Offset in .debug_info of this compilation unit.\n  uint32_t offset = 0;\n  uint32_t size = 0;\n  // Offset in .debug_info for the first DIE in this compilation unit.\n  uint32_t firstDie = 0;\n  folly::Optional<uint64_t> abbrevOffset;\n\n  // Compilation directory.\n  folly::StringPiece compDir = \".\";\n  // The beginning of the CU's contribution to .debug_addr\n  // DW_AT_addr_base (DWARF 5, DebugFission)\n  folly::Optional<uint64_t> addrBase;\n  // The beginning of the CU's contribution to .debug_ranges\n  // DW_AT_ranges_base (DebugFission)\n  folly::Optional<uint64_t> rangesBase;\n  // The beginning of the offsets table (immediately following the\n  // header) of the CU's contribution to .debug_loclists\n  folly::Optional<uint64_t> loclistsBase; // DW_AT_loclists_base (DWARF 5)\n  // The beginning of the offsets table (immediately following the\n  // header) of the CU's contribution to .debug_rnglists\n  folly::Optional<uint64_t> rnglistsBase; // DW_AT_rnglists_base (DWARF 5)\n  // Points to the first string offset of the compilation unit’s\n  // contribution to the .debug_str_offsets (or .debug_str_offsets.dwo) section.\n  folly::Optional<uint64_t> strOffsetsBase; // DW_AT_str_offsets_base (DWARF 5)\n\n  // The actual dwo file and id contains the debug sections for this\n  // compilation unit.\n  folly::Optional<folly::StringPiece> dwoName;\n  folly::Optional<uint64_t> dwoId;\n\n  // Only the CompilationUnit that contains the caller functions needs this.\n  // Indexed by (abbr.code - 1) if (abbr.code - 1) < abbrCache.size();\n  folly::Range<DIEAbbreviation*> abbrCache;\n};\n\n// Contains the main compilation unit in the binary file and an optional\n// compilation unit in dwo/dwp file if the main one is a skeleton.\nstruct CompilationUnits {\n  CompilationUnit mainCompilationUnit;\n  folly::Optional<CompilationUnit> splitCU;\n\n  CompilationUnit& defaultCompilationUnit() {\n    if (splitCU.hasValue()) {\n      return *splitCU;\n    }\n    return mainCompilationUnit;\n  }\n};\n\n// Debugging information entry to define a low-level representation of a\n// source program. Each debugging information entry consists of an identifying\n// tag and a series of attributes. An entry, or group of entries together,\n// provide a description of a corresponding entity in the source program.\nstruct Die {\n  bool is64Bit = false;\n  // Offset from start to first attribute\n  uint8_t attrOffset = 0;\n  // Offset within debug info.\n  uint32_t offset = 0;\n  uint64_t code = 0;\n  DIEAbbreviation abbr;\n};\n\nstruct AttributeSpec {\n  uint64_t name = 0;\n  uint64_t form = 0;\n  int64_t implicitConst = 0; // only set when form=DW_FORM_implicit_const\n\n  explicit operator bool() const { return name != 0 || form != 0; }\n};\n\nstruct Attribute {\n  AttributeSpec spec;\n  const Die& die;\n  std::variant<uint64_t, folly::StringPiece> attrValue;\n};\n\n// Get an ELF section by name.\nfolly::StringPiece getElfSection(const ElfFile* elf, const char* name);\n\n// All following read* functions read from a StringPiece, advancing the\n// StringPiece, and aborting if there's not enough room.\n\n// Read (bitwise) one object of type T\ntemplate <class T>\ntypename std::enable_if<\n    std::is_standard_layout<T>::value && std::is_trivial<T>::value,\n    T>::type\nread(folly::StringPiece& sp) {\n  FOLLY_SAFE_CHECK(sp.size() >= sizeof(T), \"underflow\");\n  T x;\n  memcpy(&x, sp.data(), sizeof(T));\n  sp.advance(sizeof(T));\n  return x;\n}\n\n// Read (bitwise) an unsigned number of N bytes (N in 1, 2, 3, 4).\ntemplate <size_t N>\nuint64_t readU64(folly::StringPiece& sp);\n\n// Read ULEB (unsigned) varint value; algorithm from the DWARF spec\nuint64_t readULEB(folly::StringPiece& sp, uint8_t& shift, uint8_t& val);\nuint64_t readULEB(folly::StringPiece& sp);\n\n// Read SLEB (signed) varint value; algorithm from the DWARF spec\nint64_t readSLEB(folly::StringPiece& sp);\n\n// Read a value of \"section offset\" type, which may be 4 or 8 bytes\nuint64_t readOffset(folly::StringPiece& sp, bool is64Bit);\n\n// Read \"len\" bytes\nfolly::StringPiece readBytes(folly::StringPiece& sp, uint64_t len);\n\nAttributeSpec readAttributeSpec(folly::StringPiece& sp);\n\n// Reads an abbreviation from a StringPiece, return true if at end; advance sp\nbool readAbbreviation(folly::StringPiece& section, DIEAbbreviation& abbr);\n\n// Read a null-terminated string\nfolly::StringPiece readNullTerminated(folly::StringPiece& sp);\n\nfolly::StringPiece getStringFromStringSection(\n    folly::StringPiece str, uint64_t offset);\n\nCompilationUnits getCompilationUnits(\n    ElfCacheBase* elfCache,\n    const DebugSections& debugSections,\n    uint64_t offset,\n    bool requireSplitDwarf = false);\n\n/** cu must exist during the life cycle of created Die. */\nDie getDieAtOffset(const CompilationUnit& cu, uint64_t offset);\n\n// Read attribute value.\nAttribute readAttribute(\n    const CompilationUnit& cu,\n    const Die& die,\n    AttributeSpec spec,\n    folly::StringPiece& info);\n\n/*\n * Iterate over all attributes of the given DIE, calling the given callable\n * for each. Iteration is stopped early if any of the calls return false.\n */\nsize_t forEachAttribute(\n    const CompilationUnit& cu,\n    const Die& die,\n    folly::FunctionRef<bool(const Attribute&)> f);\n\ntemplate <class T>\nfolly::Optional<T> getAttribute(\n    const CompilationUnit& cu, const Die& die, uint64_t attrName) {\n  folly::Optional<T> result;\n  forEachAttribute(cu, die, [&](const Attribute& attr) {\n    if (attr.spec.name == attrName) {\n      result = std::get<T>(attr.attrValue);\n      return false;\n    }\n    return true;\n  });\n  return result;\n}\n\nfolly::StringPiece getFunctionNameFromDie(\n    const CompilationUnit& srcu, const Die& die);\n\nfolly::StringPiece getFunctionName(\n    const CompilationUnit& srcu, uint64_t dieOffset);\n\nDie findDefinitionDie(const CompilationUnit& cu, const Die& die);\n\n} // namespace symbolizer\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/debugging/symbolizer/Elf-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nnamespace folly {\nnamespace symbolizer {\n\ntemplate <class Fn>\nconst ElfPhdr* ElfFile::iterateProgramHeaders(Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, ElfPhdr const&>) {\n  // Validate that all program headers fit within the mapped file bounds.\n  // There exist malformed ELF binaries with invalid header counts/offsets,\n  // which can cause SIGBUS when iterating past the valid mapped memory\n  // region. This code must handle malformed binaries without crashing.\n  size_t headersSize;\n  size_t headersEnd;\n  if (!folly::checked_mul(\n          &headersSize,\n          static_cast<size_t>(elfHeader().e_phnum),\n          sizeof(ElfPhdr)) ||\n      !folly::checked_add(\n          &headersEnd, static_cast<size_t>(elfHeader().e_phoff), headersSize) ||\n      headersEnd > length_) {\n    return nullptr;\n  }\n\n  const ElfPhdr* ptr = &at<ElfPhdr>(elfHeader().e_phoff);\n  for (size_t i = 0; i < elfHeader().e_phnum; i++, ptr++) {\n    if (fn(*ptr)) {\n      return ptr;\n    }\n  }\n  return nullptr;\n}\n\ntemplate <class Fn>\nconst ElfShdr* ElfFile::iterateSections(Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>) {\n  // Validate that all section headers fit within the mapped file bounds.\n  // There exist malformed ELF binaries with invalid header counts/offsets,\n  // which can cause SIGBUS when iterating past the valid mapped memory\n  // region. This code must handle malformed binaries without crashing.\n  size_t sectionsSize;\n  size_t sectionsEnd;\n  if (!folly::checked_mul(\n          &sectionsSize,\n          static_cast<size_t>(elfHeader().e_shnum),\n          sizeof(ElfShdr)) ||\n      !folly::checked_add(\n          &sectionsEnd,\n          static_cast<size_t>(elfHeader().e_shoff),\n          sectionsSize) ||\n      sectionsEnd > length_) {\n    return nullptr;\n  }\n\n  const ElfShdr* ptr = &at<ElfShdr>(elfHeader().e_shoff);\n  for (size_t i = 0; i < elfHeader().e_shnum; i++, ptr++) {\n    if (fn(*ptr)) {\n      return ptr;\n    }\n  }\n  return nullptr;\n}\n\ntemplate <class Fn>\nconst ElfShdr* ElfFile::iterateSectionsWithType(uint32_t type, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>) {\n  return iterateSections([&](const ElfShdr& sh) {\n    return sh.sh_type == type && fn(sh);\n  });\n}\n\ntemplate <class Fn>\nconst ElfShdr* ElfFile::iterateSectionsWithTypes(\n    std::initializer_list<uint32_t> types, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>) {\n  return iterateSections([&](const ElfShdr& sh) {\n    auto const it = std::find(types.begin(), types.end(), sh.sh_type);\n    return it != types.end() && fn(sh);\n  });\n}\n\ntemplate <class Fn>\nconst char* ElfFile::iterateStrings(const ElfShdr& stringTable, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, const char*>) {\n  validateStringTable(stringTable);\n\n  const char* start = file_ + stringTable.sh_offset;\n  const char* end = start + stringTable.sh_size;\n\n  const char* ptr = start;\n  while (ptr != end && !fn(ptr)) {\n    ptr += strlen(ptr) + 1;\n  }\n\n  return ptr != end ? ptr : nullptr;\n}\n\ntemplate <typename E, class Fn>\nconst E* ElfFile::iterateSectionEntries(const ElfShdr& section, Fn&& fn) const\n\n    noexcept(is_nothrow_invocable_v<E const&>) {\n  FOLLY_SAFE_CHECK(\n      section.sh_entsize == sizeof(E), \"invalid entry size in table\");\n\n  // Validate that the entire section data is within the mapped file bounds.\n  // There exist malformed ELF binaries with invalid section offsets/sizes,\n  // which can cause SIGBUS when iterating past the valid mapped memory\n  // region. This code must handle malformed binaries without crashing.\n  size_t sectionEnd;\n  if (!folly::checked_add(\n          &sectionEnd,\n          static_cast<size_t>(section.sh_offset),\n          static_cast<size_t>(section.sh_size)) ||\n      sectionEnd > length_) {\n    return nullptr;\n  }\n\n  const E* ent = &at<E>(section.sh_offset);\n  const E* end = ent + (section.sh_size / section.sh_entsize);\n\n  while (ent < end) {\n    if (fn(*ent)) {\n      return ent;\n    }\n\n    ++ent;\n  }\n\n  return nullptr;\n}\n\ntemplate <class Fn>\nconst ElfSym* ElfFile::iterateSymbols(const ElfShdr& section, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>) {\n  return iterateSectionEntries<ElfSym>(section, fn);\n}\n\ntemplate <class Fn>\nconst ElfSym* ElfFile::iterateSymbolsWithType(\n    const ElfShdr& section, uint32_t type, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>) {\n  // N.B. st_info has the same representation on 32- and 64-bit platforms\n  return iterateSymbols(section, [&](const ElfSym& sym) -> bool {\n    return ELF32_ST_TYPE(sym.st_info) == type && fn(sym);\n  });\n}\n\ntemplate <class Fn>\nconst ElfSym* ElfFile::iterateSymbolsWithTypes(\n    const ElfShdr& section, std::initializer_list<uint32_t> types, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>) {\n  // N.B. st_info has the same representation on 32- and 64-bit platforms\n  return iterateSymbols(section, [&](const ElfSym& sym) -> bool {\n    auto const elfType = ELF32_ST_TYPE(sym.st_info);\n    auto const it = std::find(types.begin(), types.end(), elfType);\n    return it != types.end() && fn(sym);\n  });\n}\n\ntemplate <class Fn>\nfolly::Expected<ElfFile::Note, ElfFile::FindNoteError>\nElfFile::iterateNotesInBodyHelper(folly::StringPiece body, Fn& fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, const Note&>) {\n  static_assert(alignof(ElfNhdr) >= 4);\n  if (uintptr_t(body.data()) % alignof(ElfNhdr) != 0) {\n    return Unexpected(FindNoteError(FindNoteFailureCode::NoteUnaligned));\n  }\n\n  while (body.size() > 0) {\n    folly::span<const uint8_t> noteBody =\n        span(reinterpret_cast<const uint8_t*>(body.data()), body.size());\n    auto noteMaybe = Note::parse(noteBody);\n    if (!noteMaybe) {\n      return noteMaybe;\n    }\n\n    auto note = *noteMaybe;\n\n    if (fn(note)) {\n      return note;\n    }\n\n    body.advance(note.alignedSize());\n  }\n\n  return Unexpected(FindNoteError(FindNoteFailureCode::NoteNotFound));\n}\n\ntemplate <class Fn>\nfolly::Expected<ElfFile::Note, ElfFile::FindNoteError>\nElfFile::iterateNotesInSections(const ElfShdr* section, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, const Note&>) {\n  if (section != nullptr) {\n    return iterateNotesInBodyHelper(getSectionBody(*section), fn);\n  }\n\n  folly::Expected<Note, FindNoteError> noteMaybe =\n      Unexpected(FindNoteError(FindNoteFailureCode::NoteNotFound));\n  iterateSectionsWithType(SHT_NOTE, [&](const ElfShdr& sh) {\n    noteMaybe = iterateNotesInBodyHelper(getSectionBody(sh), fn);\n    // Check if we got a good result, if so return it\n    if (noteMaybe) {\n      return STOP;\n    }\n\n    // Check if we got a data corruption error, stop and return that.\n    if (noteMaybe.error().isDataCorruptionError()) {\n      return STOP;\n    }\n    return CONTINUE;\n  });\n\n  return noteMaybe;\n}\n\ntemplate <class Fn>\nfolly::Expected<ElfFile::Note, ElfFile::FindNoteError>\nElfFile::iterateNotesInSegments(const ElfPhdr* segment, Fn fn) const\n    noexcept(is_nothrow_invocable_v<Fn&, const Note&>) {\n  if (segment != nullptr) {\n    return iterateNotesInBodyHelper(getSegmentBody(*segment), fn);\n  }\n\n  folly::Expected<Note, FindNoteError> noteMaybe =\n      Unexpected(FindNoteError(FindNoteFailureCode::NoteNotFound));\n  iterateProgramHeaders([&](const ElfPhdr& ph) {\n    if (ph.p_type != PT_NOTE) {\n      return CONTINUE;\n    }\n\n    noteMaybe = iterateNotesInBodyHelper(getSegmentBody(ph), fn);\n    // Check if we got a good result, if so return it\n    if (noteMaybe) {\n      return STOP;\n    }\n\n    // Check if we got a data corruption error, stop and return that.\n    if (noteMaybe.error().isDataCorruptionError()) {\n      return STOP;\n    }\n    return CONTINUE;\n  });\n\n  return noteMaybe;\n}\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/Elf.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Elf.h>\n\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <cstring>\n\n#include <glog/logging.h>\n\n#include <folly/Conv.h>\n#include <folly/Exception.h>\n#include <folly/ScopeGuard.h>\n#include <folly/lang/CString.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/Unistd.h>\n\n#if FOLLY_HAVE_ELF\n\n#ifndef STT_GNU_IFUNC\n#define STT_GNU_IFUNC 10\n#endif\n\n#if defined(__ELF_NATIVE_CLASS)\n#define FOLLY_ELF_NATIVE_CLASS __ELF_NATIVE_CLASS\n#elif defined(__FreeBSD__)\n#if defined(__LP64__)\n#define FOLLY_ELF_NATIVE_CLASS 64\n#else\n#define FOLLY_ELF_NATIVE_CLASS 32\n#endif\n#elif defined(__ANDROID__)\n#define FOLLY_ELF_NATIVE_CLASS __WORDSIZE\n#endif // __ELF_NATIVE_CLASS\n\nnamespace folly {\nnamespace symbolizer {\n\nElfFile::ElfFile() noexcept\n    : fd_(-1),\n      file_(static_cast<char*>(MAP_FAILED)),\n      length_(0),\n      fileId_(),\n      baseAddress_(0) {}\n\nElfFile::ElfFile(const char* name, Options const& options)\n    : fd_(-1),\n      file_(static_cast<char*>(MAP_FAILED)),\n      length_(0),\n      fileId_(),\n      baseAddress_(0) {\n  open(name, options);\n}\n\nvoid ElfFile::open(const char* name, Options const& options) {\n  auto r = openNoThrow(name, options);\n  if (r == kSystemError) {\n    throwSystemError(r.msg);\n  } else {\n    CHECK_EQ(r, kSuccess) << r.msg;\n  }\n}\n\nElfFile::OpenResult ElfFile::openNoThrow(\n    const char* name, Options const& options) noexcept {\n  FOLLY_SAFE_CHECK(fd_ == -1, \"File already open\");\n  // Always close fd and unmap in case of failure along the way to avoid\n  // check failure above if we leave fd != -1 and the object is recycled\n  auto guard = makeGuard([&] { reset(); });\n  strlcpy(filepath_, name, kFilepathMaxLen - 1);\n  fd_ = ::open(name, options.writable() ? O_RDWR : O_RDONLY);\n  if (fd_ == -1) {\n    return {kSystemError, \"open\"};\n  }\n  struct stat st;\n  int r = fstat(fd_, &st);\n  if (r == -1) {\n    return {kSystemError, \"fstat\"};\n  }\n\n  uint64_t mtime_ns = st.st_mtim.tv_sec * 1000'000'000LL + st.st_mtim.tv_nsec;\n  fileId_ = ElfFileId{\n      to_narrow(st.st_dev),\n      to_narrow(st.st_ino),\n      to_narrow(st.st_size),\n      mtime_ns};\n\n  length_ = st.st_size;\n  int prot = PROT_READ;\n  if (options.writable()) {\n    prot |= PROT_WRITE;\n  }\n  file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));\n  if (file_ == MAP_FAILED) {\n    return {kSystemError, \"mmap\"};\n  }\n  auto const initOpenResult = init();\n  if (initOpenResult != kSuccess) {\n    reset();\n    errno = EINVAL;\n    return initOpenResult;\n  }\n  guard.dismiss();\n  return {kSuccess, nullptr};\n}\n\nElfFile::OpenResult ElfFile::openAndFollow(\n    const char* name, Options const& options) noexcept {\n  auto result = openNoThrow(name, options);\n  if (options.writable() || result != kSuccess) {\n    return result;\n  }\n\n  /* NOTE .gnu_debuglink specifies only the name of the debugging info file\n   * (with no directory components). GDB checks 3 different directories, but\n   * ElfFile only supports the first version:\n   *     - dirname(name)\n   *     - dirname(name) + /.debug/\n   *     - X/dirname(name)/ - where X is set in gdb's `debug-file-directory`.\n   */\n  auto dirend = strrchr(name, '/');\n  // include ending '/' if any.\n  auto dirlen = dirend != nullptr ? dirend + 1 - name : 0;\n\n  auto debuginfo = getSectionByName(\".gnu_debuglink\");\n  if (!debuginfo) {\n    return result;\n  }\n\n  // The section starts with the filename, with any leading directory\n  // components removed, followed by a zero byte.\n  auto debugFileName = getSectionBody(*debuginfo);\n  auto debugFileLen = strlen(debugFileName.begin());\n  if (dirlen + debugFileLen >= PATH_MAX) {\n    return result;\n  }\n\n  char linkname[PATH_MAX];\n  memcpy(linkname, name, dirlen);\n  memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1);\n  reset();\n  result = openNoThrow(linkname, options);\n  if (result == kSuccess) {\n    return result;\n  }\n  return openNoThrow(name, options);\n}\n\nElfFile::~ElfFile() {\n  reset();\n}\n\nElfFile::ElfFile(ElfFile&& other) noexcept\n    : fd_(other.fd_),\n      file_(other.file_),\n      length_(other.length_),\n      fileId_(other.fileId_),\n      baseAddress_(other.baseAddress_) {\n  // copy other.filepath_, leaving filepath_ zero-terminated, always.\n  strlcpy(filepath_, other.filepath_, kFilepathMaxLen - 1);\n  other.filepath_[0] = 0;\n  other.fd_ = -1;\n  other.file_ = static_cast<char*>(MAP_FAILED);\n  other.length_ = 0;\n  other.fileId_ = {};\n  other.baseAddress_ = 0;\n}\n\nElfFile& ElfFile::operator=(ElfFile&& other) noexcept {\n  assert(this != &other);\n  reset();\n\n  // copy other.filepath_, leaving filepath_ zero-terminated, always.\n  strlcpy(filepath_, other.filepath_, kFilepathMaxLen - 1);\n  fd_ = other.fd_;\n  file_ = other.file_;\n  length_ = other.length_;\n  fileId_ = other.fileId_;\n  baseAddress_ = other.baseAddress_;\n\n  other.filepath_[0] = 0;\n  other.fd_ = -1;\n  other.file_ = static_cast<char*>(MAP_FAILED);\n  other.length_ = 0;\n  other.fileId_ = {};\n  other.baseAddress_ = 0;\n\n  return *this;\n}\n\nvoid ElfFile::reset() noexcept {\n  filepath_[0] = 0;\n\n  if (file_ != MAP_FAILED) {\n    munmap(file_, length_);\n    file_ = static_cast<char*>(MAP_FAILED);\n  }\n\n  if (fd_ != -1) {\n    fileops::close(fd_);\n    fd_ = -1;\n  }\n\n  fileId_ = {};\n}\n\nElfFile::OpenResult ElfFile::init() noexcept {\n  if (length_ < 4) {\n    return {kInvalidElfFile, \"not an ELF file (too short)\"};\n  }\n\n  std::array<char, SELFMAG> elfMagBuf = {{0, 0, 0, 0}};\n  if (::lseek(fd_, 0, SEEK_SET) != 0 ||\n      fileops::read(fd_, elfMagBuf.data(), SELFMAG) != SELFMAG) {\n    return {kInvalidElfFile, \"unable to read ELF file for magic number\"};\n  }\n  if (std::strncmp(elfMagBuf.data(), ELFMAG, SELFMAG) != 0) {\n    return {kInvalidElfFile, \"invalid ELF magic\"};\n  }\n  char c;\n  if (::pread(fd_, &c, 1, length_ - 1) != 1) {\n    auto msg =\n        \"The last bit of the mmaped memory is no longer valid. This may be \"\n        \"caused by the original file being resized, \"\n        \"deleted or otherwise modified.\";\n    return {kInvalidElfFile, msg};\n  }\n\n  if (::lseek(fd_, 0, SEEK_SET) != 0) {\n    return {\n        kInvalidElfFile,\n        \"unable to reset file descriptor after reading ELF magic number\"};\n  }\n\n  auto& elfHeader = this->elfHeader();\n\n#define EXPECTED_CLASS P1(ELFCLASS, FOLLY_ELF_NATIVE_CLASS)\n#define P1(a, b) P2(a, b)\n#define P2(a, b) a##b\n  // Validate ELF class (32/64 bits)\n  if (elfHeader.e_ident[EI_CLASS] != EXPECTED_CLASS) {\n    return {kInvalidElfFile, \"invalid ELF class\"};\n  }\n#undef P1\n#undef P2\n#undef EXPECTED_CLASS\n\n  // Validate ELF data encoding (LSB/MSB)\n  static constexpr auto kExpectedEncoding =\n      kIsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB;\n  if (elfHeader.e_ident[EI_DATA] != kExpectedEncoding) {\n    return {kInvalidElfFile, \"invalid ELF encoding\"};\n  }\n\n  // Validate ELF version (1)\n  if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT ||\n      elfHeader.e_version != EV_CURRENT) {\n    return {kInvalidElfFile, \"invalid ELF version\"};\n  }\n\n  // We only support executable and shared object files\n  if (elfHeader.e_type != ET_REL && elfHeader.e_type != ET_EXEC &&\n      elfHeader.e_type != ET_DYN && elfHeader.e_type != ET_CORE) {\n    return {kInvalidElfFile, \"invalid ELF file type\"};\n  }\n\n  // We support executable and shared object files and extracting debug info\n  // from relocatable objects (.dwo sections in .o/.dwo files). The e_phnum and\n  // e_phentsize header fields are not required for relocatable files.\n  // https://docs.oracle.com/cd/E19620-01/805-4693/6j4emccrq/index.html\n  if (elfHeader.e_type != ET_REL) {\n    if (elfHeader.e_phnum == 0) {\n      return {kInvalidElfFile, \"no program header!\"};\n    }\n\n    if (elfHeader.e_phentsize != sizeof(ElfPhdr)) {\n      return {kInvalidElfFile, \"invalid program header entry size\"};\n    }\n  }\n\n  if (elfHeader.e_shentsize != sizeof(ElfShdr)) {\n    if (elfHeader.e_shentsize != 0 || elfHeader.e_type != ET_CORE) {\n      return {kInvalidElfFile, \"invalid section header entry size\"};\n    }\n  }\n\n  // Program headers are sorted by load address, so the first PT_LOAD\n  // header gives us the base address.\n  if (elfHeader.e_type != ET_REL) {\n    const ElfPhdr* programHeader = iterateProgramHeaders([](auto& h) {\n      return h.p_type == PT_LOAD;\n    });\n\n    if (!programHeader) {\n      return {kInvalidElfFile, \"could not find base address\"};\n    }\n    baseAddress_ = programHeader->p_vaddr;\n  }\n\n  return {kSuccess, nullptr};\n}\n\nconst ElfShdr* ElfFile::getSectionByIndex(size_t idx) const noexcept {\n  FOLLY_SAFE_CHECK(idx < elfHeader().e_shnum, \"invalid section index\");\n  if (elfHeader().e_shoff + (idx + 1) * sizeof(ElfShdr) > length_) {\n    // Handle ELFs with invalid internal offsets to program/section headers.\n    return nullptr;\n  }\n  return &at<ElfShdr>(elfHeader().e_shoff + idx * sizeof(ElfShdr));\n}\n\nfolly::StringPiece ElfFile::getSectionBody(\n    const ElfShdr& section) const noexcept {\n  return folly::StringPiece(file_ + section.sh_offset, section.sh_size);\n}\n\nfolly::StringPiece ElfFile::getSegmentBody(\n    const ElfPhdr& segment) const noexcept {\n  return folly::StringPiece(file_ + segment.p_offset, segment.p_filesz);\n}\n\nvoid ElfFile::validateStringTable(const ElfShdr& stringTable) const noexcept {\n  FOLLY_SAFE_CHECK(\n      stringTable.sh_type == SHT_STRTAB, \"invalid type for string table\");\n\n  const char* start = file_ + stringTable.sh_offset;\n  // First and last bytes must be 0\n  FOLLY_SAFE_CHECK(\n      stringTable.sh_size == 0 ||\n          (start[0] == '\\0' && start[stringTable.sh_size - 1] == '\\0'),\n      \"invalid string table\");\n}\n\nconst char* ElfFile::getString(\n    const ElfShdr& stringTable, size_t offset) const noexcept {\n  validateStringTable(stringTable);\n  FOLLY_SAFE_CHECK(\n      offset < stringTable.sh_size, \"invalid offset in string table\");\n\n  return file_ + stringTable.sh_offset + offset;\n}\n\nconst char* ElfFile::getSectionName(const ElfShdr& section) const noexcept {\n  if (elfHeader().e_shstrndx == SHN_UNDEF) {\n    return nullptr; // no section name string table\n  }\n\n  auto stringSection = getSectionByIndex(elfHeader().e_shstrndx);\n  if (!stringSection) {\n    return nullptr;\n  }\n  return getString(*stringSection, section.sh_name);\n}\n\nconst ElfShdr* ElfFile::getSectionByName(const char* name) const noexcept {\n  if (elfHeader().e_shstrndx == SHN_UNDEF) {\n    return nullptr; // no section name string table\n  }\n\n  auto stringSection = getSectionByIndex(elfHeader().e_shstrndx);\n  if (!stringSection) {\n    return nullptr;\n  }\n  const ElfShdr& sectionNames = *stringSection;\n  const char* start = file_ + sectionNames.sh_offset;\n\n  // Find section with the appropriate sh_name offset\n  const ElfShdr* foundSection = iterateSections([&](const ElfShdr& sh) {\n    if (sh.sh_name >= sectionNames.sh_size) {\n      return false;\n    }\n    return !strcmp(start + sh.sh_name, name);\n  });\n  return foundSection;\n}\n\nElfFile::Symbol ElfFile::getDefinitionByAddress(\n    uintptr_t address) const noexcept {\n  Symbol foundSymbol{nullptr, nullptr};\n\n  auto findSection = [&, address](const ElfShdr& section) {\n    auto findSymbols = [&, address](const ElfSym& sym) {\n      if (sym.st_shndx == SHN_UNDEF) {\n        return false; // not a definition\n      }\n      if (address >= sym.st_value && address < sym.st_value + sym.st_size) {\n        foundSymbol.first = &section;\n        foundSymbol.second = &sym;\n        return true;\n      }\n\n      return false;\n    };\n\n    return iterateSymbolsWithTypes(\n        section, {STT_OBJECT, STT_FUNC, STT_GNU_IFUNC}, findSymbols);\n  };\n\n  // Try the .dynsym section first if it exists, it's smaller.\n  (iterateSectionsWithType(SHT_DYNSYM, findSection) ||\n   iterateSectionsWithType(SHT_SYMTAB, findSection));\n\n  return foundSymbol;\n}\n\nElfFile::Symbol ElfFile::getSymbolByName(\n    const char* name, std::initializer_list<uint32_t> types) const noexcept {\n  Symbol foundSymbol{nullptr, nullptr};\n\n  auto findSection = [&](const ElfShdr& section) -> bool {\n    // This section has no string table associated w/ its symbols; hence we\n    // can't get names for them\n    if (section.sh_link == SHN_UNDEF) {\n      return false;\n    }\n\n    auto findSymbols = [&](const ElfSym& sym) -> bool {\n      if (sym.st_shndx == SHN_UNDEF) {\n        return false; // not a definition\n      }\n      if (sym.st_name == 0) {\n        return false; // no name for this symbol\n      }\n      auto linkSection = getSectionByIndex(section.sh_link);\n      if (!linkSection) {\n        return false;\n      }\n      const char* sym_name = getString(*linkSection, sym.st_name);\n      if (strcmp(sym_name, name) == 0) {\n        foundSymbol.first = &section;\n        foundSymbol.second = &sym;\n        return true;\n      }\n\n      return false;\n    };\n\n    return iterateSymbolsWithTypes(section, types, findSymbols);\n  };\n\n  // Try the .dynsym section first if it exists, it's smaller.\n  iterateSectionsWithType(SHT_DYNSYM, findSection) ||\n      iterateSectionsWithType(SHT_SYMTAB, findSection);\n\n  return foundSymbol;\n}\n\nconst ElfShdr* ElfFile::getSectionContainingAddress(\n    ElfAddr addr) const noexcept {\n  return iterateSections([&](const ElfShdr& sh) -> bool {\n    return (addr >= sh.sh_addr) && (addr < (sh.sh_addr + sh.sh_size));\n  });\n}\n\nconst char* ElfFile::getSymbolName(const Symbol& symbol) const noexcept {\n  if (!symbol.first || !symbol.second) {\n    return nullptr;\n  }\n\n  if (symbol.second->st_name == 0) {\n    return nullptr; // symbol has no name\n  }\n\n  if (symbol.first->sh_link == SHN_UNDEF) {\n    return nullptr; // symbol table has no strings\n  }\n\n  auto linkSection = getSectionByIndex(symbol.first->sh_link);\n  if (!linkSection) {\n    return nullptr;\n  }\n  return getString(*linkSection, symbol.second->st_name);\n}\n\nfolly::Expected<span<const uint8_t>, ElfFile::FindNoteError>\nElfFile::getNoteGnuBuildId() const noexcept {\n  auto filter = [](const Note& note) {\n    return note.getName() == \"GNU\" && note.getType() == NT_GNU_BUILD_ID;\n  };\n  auto desc = [](auto note) { return note.getDesc(); };\n  auto section = getSectionByName(\".note.gnu.build-id\");\n  return iterateNotesInSections(section, filter).then(desc);\n}\n\nfolly::Expected<ElfFile::Note, ElfFile::FindNoteError> ElfFile::findNoteByName(\n    std::string_view name) const noexcept {\n  auto filter = [name](const Note& note) { return note.getName() == name; };\n\n  // If we get a success, or a data corruption error, return it.\n  // otherwise iterate segments.\n  auto foundMaybe = iterateNotesInSections(nullptr, filter);\n  if (foundMaybe || foundMaybe.error().isDataCorruptionError()) {\n    return foundMaybe;\n  }\n\n  foundMaybe = iterateNotesInSegments(nullptr, filter);\n  return foundMaybe;\n}\n\nfolly::Expected<ElfFile::Note, ElfFile::FindNoteError> ElfFile::findNoteByType(\n    size_t type) const noexcept {\n  auto filter = [type](const Note& note) {\n    return note.header()->n_type == type;\n  };\n\n  // If we get a success, or a data corruption error, return it.\n  // otherwise iterate segments.\n  auto foundMaybe = iterateNotesInSections(nullptr, filter);\n  if (foundMaybe || foundMaybe.error().isDataCorruptionError()) {\n    return foundMaybe;\n  }\n\n  foundMaybe = iterateNotesInSegments(nullptr, filter);\n  return foundMaybe;\n}\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_ELF\n"
  },
  {
    "path": "folly/debugging/symbolizer/Elf.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// ELF file parser\n\n#pragma once\n\n#include <fcntl.h>\n#include <sys/types.h>\n#include <cstdio>\n#include <initializer_list>\n#include <stdexcept>\n#include <system_error>\n#include <unordered_map>\n\n#include <folly/Conv.h>\n#include <folly/Likely.h>\n#include <folly/Range.h>\n#include <folly/container/span.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/lang/cstring_view.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/Unistd.h>\n\n#if FOLLY_HAVE_ELF\n\n#include <elf.h>\n#include <link.h> // For ElfW()\n\nnamespace folly {\nnamespace symbolizer {\n\n#if defined(ElfW)\n#define FOLLY_ELF_ELFW(name) ElfW(name)\n#elif defined(__FreeBSD__)\n#define FOLLY_ELF_ELFW(name) Elf_##name\n#endif\n\nusing ElfAddr = FOLLY_ELF_ELFW(Addr);\nusing ElfEhdr = FOLLY_ELF_ELFW(Ehdr);\nusing ElfOff = FOLLY_ELF_ELFW(Off);\nusing ElfWord = FOLLY_ELF_ELFW(Word);\nusing ElfPhdr = FOLLY_ELF_ELFW(Phdr);\nusing ElfShdr = FOLLY_ELF_ELFW(Shdr);\nusing ElfSym = FOLLY_ELF_ELFW(Sym);\nusing ElfRel = FOLLY_ELF_ELFW(Rel);\nusing ElfRela = FOLLY_ELF_ELFW(Rela);\nusing ElfDyn = FOLLY_ELF_ELFW(Dyn);\nusing ElfNhdr = FOLLY_ELF_ELFW(Nhdr);\n\n// ElfFileId is supposed to uniquely identify any instance of an ELF binary.\n// It does that by using the file's inode, dev ID, size and modification time\n// (ns): <dev, inode, size in bytes, mod time> Just using dev, inode is not\n// unique enough, because the file can be overwritten with new contents, but\n// will keep same dev and inode, so we take into account modification time and\n// file size to minimize risk.\nstruct ElfFileId {\n  dev_t dev;\n  ino_t inode;\n  off_t size;\n  uint64_t mtime;\n};\n\ninline bool operator==(const ElfFileId& lhs, const ElfFileId& rhs) {\n  return lhs.dev == rhs.dev && lhs.inode == rhs.inode && lhs.size == rhs.size &&\n      lhs.mtime == rhs.mtime;\n}\n\n/**\n * ELF file parser.\n *\n * We handle native files only (32-bit files on a 32-bit platform, 64-bit files\n * on a 64-bit platform), and only executables (ET_EXEC) shared objects\n * (ET_DYN), core files (ET_CORE) and relocatable file (ET_REL).\n */\nclass ElfFile {\n public:\n  class Options {\n   public:\n    constexpr Options() noexcept {}\n\n    constexpr bool writable() const noexcept { return writable_; }\n\n    constexpr Options& writable(bool const value) noexcept {\n      writable_ = value;\n      return *this;\n    }\n\n   private:\n    bool writable_ = false;\n  };\n\n  ElfFile() noexcept;\n\n  // Note: may throw, call openNoThrow() explicitly if you don't want to throw\n  explicit ElfFile(const char* name, Options const& options = Options());\n\n  // Open the ELF file.\n  // Returns 0 on success, kSystemError (guaranteed to be -1) (and sets errno)\n  // on IO error, kInvalidElfFile (and sets errno to EINVAL) for an invalid\n  // Elf file. On error, if msg is not nullptr, sets *msg to a static string\n  // indicating what failed.\n  enum OpenResultCode : int {\n    kSuccess = 0,\n    kSystemError = -1,\n    kInvalidElfFile = -2,\n  };\n  struct OpenResult {\n    OpenResultCode code{};\n    char const* msg{};\n\n    /* implicit */ constexpr operator OpenResultCode() const noexcept {\n      return code;\n    }\n  };\n  // Open the ELF file. Does not throw on error.\n  OpenResult openNoThrow(\n      const char* name, Options const& options = Options()) noexcept;\n\n  // Like openNoThrow, but follow .gnu_debuglink if present\n  OpenResult openAndFollow(\n      const char* name, Options const& options = Options()) noexcept;\n\n  // Open the ELF file. Throws on error.\n  void open(const char* name, Options const& options = Options());\n\n  ~ElfFile();\n\n  ElfFile(ElfFile&& other) noexcept;\n  ElfFile& operator=(ElfFile&& other) noexcept;\n\n  /** Retrieve the ELF header */\n  const ElfEhdr& elfHeader() const noexcept { return at<ElfEhdr>(0); }\n\n  /**\n   * Get the base address, the address where the file should be loaded if\n   * no relocations happened.\n   */\n  uintptr_t getBaseAddress() const noexcept { return baseAddress_; }\n\n  /** Find a section given its name */\n  const ElfShdr* getSectionByName(const char* name) const noexcept;\n\n  /** Find a section given its index in the section header table */\n  const ElfShdr* getSectionByIndex(size_t idx) const noexcept;\n\n  /** Retrieve the name of a section */\n  const char* getSectionName(const ElfShdr& section) const noexcept;\n\n  /** Get the actual section body */\n  folly::StringPiece getSectionBody(const ElfShdr& section) const noexcept;\n\n  folly::StringPiece getSegmentBody(const ElfPhdr& segment) const noexcept;\n\n  /** Retrieve a string from a string table section */\n  const char* getString(\n      const ElfShdr& stringTable, size_t offset) const noexcept;\n\n  /**\n   * Iterate over all strings in a string table section for as long as\n   * fn(str) returns false.\n   * Returns the current (\"found\") string when fn returned true, or nullptr\n   * if fn returned false for all strings in the table.\n   */\n  template <class Fn>\n  const char* iterateStrings(const ElfShdr& stringTable, Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, const char*>);\n\n  /**\n   * Iterate over program headers as long as fn(section) returns false.\n   * Returns a pointer to the current (\"found\") section when fn returned\n   * true, or nullptr if fn returned false for all sections.\n   */\n  template <class Fn>\n  const ElfPhdr* iterateProgramHeaders(Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, ElfPhdr const&>);\n\n  /**\n   * Iterate over all sections for as long as fn(section) returns false.\n   * Returns a pointer to the current (\"found\") section when fn returned\n   * true, or nullptr if fn returned false for all sections.\n   */\n  template <class Fn>\n  const ElfShdr* iterateSections(Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>);\n\n  /**\n   * Iterate over all sections with a given type. Similar to\n   * iterateSections(), but filtered only for sections with the given type.\n   */\n  template <class Fn>\n  const ElfShdr* iterateSectionsWithType(uint32_t type, Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>);\n\n  /**\n   * Iterate over all sections with a given types. Similar to\n   * iterateSectionWithTypes(), but filtered on multiple types.\n   */\n  template <class Fn>\n  const ElfShdr* iterateSectionsWithTypes(\n      std::initializer_list<uint32_t> types, Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, ElfShdr const&>);\n\n  /**\n   * Iterate over all symbols within a given section.\n   *\n   * Returns a pointer to the current (\"found\") symbol when fn returned true,\n   * or nullptr if fn returned false for all symbols.\n   */\n  template <class Fn>\n  const ElfSym* iterateSymbols(const ElfShdr& section, Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>);\n  template <class Fn>\n  const ElfSym* iterateSymbolsWithType(\n      const ElfShdr& section, uint32_t type, Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>);\n  template <class Fn>\n  const ElfSym* iterateSymbolsWithTypes(\n      const ElfShdr& section,\n      std::initializer_list<uint32_t> types,\n      Fn fn) const noexcept(is_nothrow_invocable_v<Fn&, ElfSym const&>);\n\n  /**\n   * Iterate over entries within a given section.\n   *\n   * Returns a pointer to the current (\"found\") entry when fn returned\n   * true, or nullptr if fn returned false for all entries.\n   */\n  template <typename E, class Fn>\n  const E* iterateSectionEntries(const ElfShdr& section, Fn&& fn) const\n\n      noexcept(is_nothrow_invocable_v<E const&>);\n\n  /**\n   * Find symbol definition by address.\n   * Note that this is the file virtual address, so you need to undo\n   * any relocation that might have happened.\n   *\n   * Returns {nullptr, nullptr} if not found.\n   */\n  using Symbol = std::pair<const ElfShdr*, const ElfSym*>;\n  Symbol getDefinitionByAddress(uintptr_t address) const noexcept;\n\n  /**\n   * Find symbol definition by name. Optionally specify the symbol types to\n   * consider.\n   *\n   * If a symbol with this name cannot be found, a <nullptr, nullptr> Symbol\n   * will be returned. This is O(N) in the number of symbols in the file.\n   *\n   * Returns {nullptr, nullptr} if not found.\n   */\n  Symbol getSymbolByName(\n      const char* name,\n      std::initializer_list<uint32_t> types = {\n          STT_OBJECT, STT_FUNC, STT_GNU_IFUNC}) const noexcept;\n\n  /**\n   * Find multiple symbol definitions by name. Because searching for a symbol is\n   * O(N) this method enables searching for multiple symbols in a single pass.\n   *\n   * Returns a map containing a key for each unique symbol name in the provided\n   * names container. The corresponding value is either Symbol or <nullptr,\n   * nullptr> if the symbol was not found.\n   */\n  template <typename C, typename T = typename C::value_type>\n  std::unordered_map<std::string, Symbol> getSymbolsByName(\n      const C& names,\n      std::initializer_list<uint32_t> types = {\n          STT_OBJECT, STT_FUNC, STT_GNU_IFUNC}) const noexcept {\n    std::unordered_map<std::string, Symbol> result(names.size());\n    if (names.empty()) {\n      return result;\n    }\n\n    for (const std::string& name : names) {\n      result[name] = {nullptr, nullptr};\n    }\n    size_t seenCount = 0;\n\n    auto findSymbol =\n        [&](const folly::symbolizer::ElfShdr& section,\n            const folly::symbolizer::ElfSym& sym) -> bool {\n      auto symbol = folly::symbolizer::ElfFile::Symbol(&section, &sym);\n      auto name = getSymbolName(symbol);\n      if (name == nullptr) {\n        return false;\n      }\n      auto itr = result.find(name);\n      if (itr != result.end() && itr->second.first == nullptr &&\n          itr->second.second == nullptr) {\n        itr->second = symbol;\n        ++seenCount;\n      }\n      return seenCount == result.size();\n    };\n\n    auto iterSection = [&](const folly::symbolizer::ElfShdr& section) -> bool {\n      iterateSymbolsWithTypes(section, types, [&](const auto& sym) -> bool {\n        return findSymbol(section, sym);\n      });\n      return false;\n    };\n    // Try the .dynsym section first if it exists, it's smaller.\n    iterateSectionsWithType(SHT_DYNSYM, iterSection) ||\n        iterateSectionsWithType(SHT_SYMTAB, iterSection);\n\n    return result;\n  }\n\n  /**\n   * Get the value of a symbol.\n   */\n  template <class T>\n  const T* getSymbolValue(const ElfSym* symbol) const noexcept {\n    const ElfShdr* section = getSectionByIndex(symbol->st_shndx);\n    if (section == nullptr) {\n      return nullptr;\n    }\n\n    return valueAt<T>(*section, symbol->st_value);\n  }\n\n  /**\n   * Get the value of the object stored at the given address.\n   *\n   * This is the function that you want to use in conjunction with\n   * getSymbolValue() to follow pointers. For example, to get the value of\n   * a char* symbol, you'd do something like this:\n   *\n   *  auto sym = getSymbolByName(\"someGlobalValue\");\n   *  auto addrPtr = getSymbolValue<ElfAddr>(sym.second);\n   *  const char* str = getAddressValue<const char>(*addrPtr);\n   */\n  template <class T>\n  const T* getAddressValue(const ElfAddr addr) const noexcept {\n    const ElfShdr* section = getSectionContainingAddress(addr);\n    if (section == nullptr) {\n      return nullptr;\n    }\n\n    return valueAt<T>(*section, addr);\n  }\n\n  /**\n   * Retrieve symbol name.\n   */\n  const char* getSymbolName(const Symbol& symbol) const noexcept;\n\n  /** Find the section containing the given address */\n  const ElfShdr* getSectionContainingAddress(ElfAddr addr) const noexcept;\n\n  const char* filepath() const { return filepath_; }\n\n  const ElfFileId& getFileId() const { return fileId_; }\n\n  enum class FindNoteFailureCode {\n    NoteNotFound = 1,\n    NoteUndersized,\n    NoteUnaligned,\n  };\n\n  /** Describes the reason searching for a note has failed, including an error\n   * code and a flag denoting if that error code is due to corruption in the ELF\n   * file itself.\n   */\n  struct FindNoteError {\n    FindNoteFailureCode failureCode;\n\n    static inline constexpr std::string_view GetNoteErrorString[] = {\n        \"Note not found\",\n        \"Note body undersized (smaller than specified in the note header)\",\n        \"Note underaligned (aligned less than the note header)\",\n    };\n\n    static constexpr std::string_view getErrorMessage(\n        FindNoteError& err) noexcept {\n      auto index = to_underlying(err.failureCode);\n      if (index < 1 || to_unsigned(index) > std::size(GetNoteErrorString)) {\n        return std::string_view{};\n      }\n\n      return GetNoteErrorString[index - 1];\n    }\n\n    bool isDataCorruptionError() {\n      return failureCode == FindNoteFailureCode::NoteUndersized ||\n          failureCode == FindNoteFailureCode::NoteUnaligned;\n    }\n\n    explicit FindNoteError(FindNoteFailureCode code) : failureCode(code) {}\n  };\n\n  /** Structure containing a note header and it's body */\n  struct Note {\n    folly::span<const uint8_t> note;\n    explicit Note(folly::span<const uint8_t> note_) : note(note_) {}\n\n    const ElfNhdr* header() const {\n      return reinterpret_cast<const ElfNhdr*>(note.data());\n    }\n\n    folly::span<const uint8_t> body() const {\n      return note.subspan(sizeof(*header()));\n    }\n\n    ElfWord getType() const {\n      if (!header()) {\n        return {};\n      }\n      return header()->n_type;\n    }\n\n    cstring_view getName() const {\n      if (!header()) {\n        return {};\n      }\n\n      // Subtract 1 to remove the trailing null character.\n      return cstring_view(\n          reinterpret_cast<const char*>(body().data()), header()->n_namesz - 1);\n    }\n\n    folly::span<const uint8_t> getDesc() const {\n      if (!header()) {\n        return span<const uint8_t>{};\n      }\n\n      size_t paddedNameSize = folly::align_ceil(header()->n_namesz, 4);\n      return body().subspan(paddedNameSize);\n    }\n\n    static size_t alignedBodySize(const ElfNhdr& nhdr) {\n      return folly::align_ceil(nhdr.n_namesz, 4) +\n          folly::align_ceil(nhdr.n_descsz, 4);\n    }\n\n    static size_t alignedSize(const ElfNhdr& nhdr) {\n      return sizeof(ElfNhdr) + alignedBodySize(nhdr);\n    }\n\n    /**\n     * Get the aligned size of this note. If the header is null, 0 will be\n     * returned.\n     */\n    size_t alignedSize() const { return header() ? alignedSize(*header()) : 0; }\n\n    /**\n     * Parse a note from a given set of bytes. If the supplied bytes is less\n     * than the note header, or the aligned size of the body an unexpected will\n     * be returned.\n     */\n    static folly::Expected<Note, FindNoteError> parse(\n        folly::span<const uint8_t> noteBody) {\n      if (noteBody.size() < sizeof(ElfNhdr)) {\n        return Unexpected(FindNoteError(FindNoteFailureCode::NoteUndersized));\n      }\n\n      const ElfNhdr* nhdr = reinterpret_cast<const ElfNhdr*>(noteBody.data());\n      if (alignedSize(*nhdr) > noteBody.size()) {\n        return Unexpected(FindNoteError(FindNoteFailureCode::NoteUndersized));\n      }\n\n      return Note(noteBody.subspan(0, alignedSize(*nhdr)));\n    }\n  };\n\n  /**\n   * Iterate over notes in a given section, or all sections if section is null.\n   * In the case no note is found, a pair will be returned with a nullptr for\n   * the note header and an empty span for the note body.\n   *\n   * Note that notes are not unique, and notes in sections may also\n   * be present when iterating over segments.\n   */\n  template <class Fn>\n  folly::Expected<Note, FindNoteError> iterateNotesInSections(\n      const ElfShdr* section, Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, const Note&>);\n\n  /**\n   * Iterate over notes in a given segment, or all segments if segment is null.\n   * In the case no note is found, a pair will be returned with a nullptr for\n   * the note header and an empty span for the note body.\n   *\n   * Note that notes are not unique, and notes in segments may also\n   * be present when iterating over sections.\n   */\n  template <class Fn>\n  folly::Expected<Note, FindNoteError> iterateNotesInSegments(\n      const ElfPhdr* segment, Fn fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, const Note&>);\n\n  /**\n   * Retrieve the content of .note.gnu.build-id, if available.\n   */\n  folly::Expected<span<const uint8_t>, FindNoteError> getNoteGnuBuildId()\n      const noexcept;\n\n  /**\n   * Find a note by name in either a section or segment. If multiple\n   * notes share the same name, the first match will be returned. An error will\n   * be returned if the note is not found, or if there is underlying file\n   * corruption.\n   *\n   * Empty is a valid input, and will return when notes with an empty name are\n   * found. This can happen in ELF Cores, or other notes when the namespace\n   * unknown. Read more at https://man7.org/linux/man-pages/man5/elf.5.html\n   *\n   * Note that notes are not unique, and notes in sections may also be in\n   * segments. For this reason this method does not differentiate between\n   * segments and sections.\n   */\n  folly::Expected<Note, FindNoteError> findNoteByName(\n      std::string_view name) const noexcept;\n\n  /**\n   * Find a note by type in either a section or segment. If multiple\n   * notes share the same type, the first match will be returned. An error will\n   * be returned if the note is not found, or if there is underlying file\n   * corruption.\n   *\n   * Note that notes are not unique, and notes in sections may also be in\n   * segments. For this reason this method does not differentiate between\n   * segments and sections.\n   */\n  folly::Expected<Note, FindNoteError> findNoteByType(\n      size_t type) const noexcept;\n\n private:\n  OpenResult init() noexcept;\n  void reset() noexcept;\n  ElfFile(const ElfFile&) = delete;\n  ElfFile& operator=(const ElfFile&) = delete;\n\n  enum { CONTINUE = 0, STOP = 1 };\n\n  void validateStringTable(const ElfShdr& stringTable) const noexcept;\n\n  template <class T>\n  const T& at(ElfOff offset) const noexcept {\n    static_assert(\n        std::is_standard_layout<T>::value && std::is_trivial<T>::value,\n        \"non-pod\");\n    FOLLY_SAFE_CHECK(\n        offset + sizeof(T) <= length_,\n        \"Offset (\",\n        static_cast<size_t>(offset),\n        \" + \",\n        sizeof(T),\n        \") is not contained within our mapped file (\",\n        filepath_,\n        \") of length \",\n        length_);\n    return *reinterpret_cast<T*>(file_ + offset);\n  }\n\n  template <class T>\n  const T* valueAt(const ElfShdr& section, const ElfAddr addr) const noexcept {\n    // For exectuables and shared objects, st_value holds a virtual address\n    // that refers to the memory owned by sections. Since we didn't map the\n    // sections into the addresses that they're expecting (sh_addr), but\n    // instead just mmapped the entire file directly, we need to translate\n    // between addresses and offsets into the file.\n    //\n    // TODO: For other file types, st_value holds a file offset directly. Since\n    //       I don't have a use-case for that right now, just assert that\n    //       nobody wants this. We can always add it later.\n    if (!(elfHeader().e_type == ET_EXEC || elfHeader().e_type == ET_DYN ||\n          elfHeader().e_type == ET_CORE)) {\n      return nullptr;\n    }\n    if (!(addr >= section.sh_addr &&\n          (addr + sizeof(T)) <= (section.sh_addr + section.sh_size))) {\n      return nullptr;\n    }\n\n    // SHT_NOBITS: a section that occupies no space in the file but otherwise\n    // resembles SHT_PROGBITS. Although this section contains no bytes, the\n    // sh_offset member contains the conceptual file offset. Typically used\n    // for zero-initialized data sections like .bss.\n    if (section.sh_type == SHT_NOBITS) {\n      static T t = {};\n      return &t;\n    }\n\n    ElfOff offset = section.sh_offset + (addr - section.sh_addr);\n\n    return (offset + sizeof(T) <= length_) ? &at<T>(offset) : nullptr;\n  }\n\n  // Helper to iterate notes in a section or segment.\n  // Takes in a StringPiece of the body of the section or segment, and iterates\n  // over the notes in that body.\n  template <class Fn>\n  folly::Expected<Note, FindNoteError> iterateNotesInBodyHelper(\n      folly::StringPiece body, Fn& fn) const\n      noexcept(is_nothrow_invocable_v<Fn&, const Note&>);\n\n  static constexpr size_t kFilepathMaxLen = 512;\n  char filepath_[kFilepathMaxLen] = {};\n  int fd_;\n  char* file_; // mmap() location\n  size_t length_; // mmap() length\n  ElfFileId fileId_;\n\n  uintptr_t baseAddress_;\n};\n\n} // namespace symbolizer\n} // namespace folly\n\nnamespace std {\ntemplate <>\nstruct hash<folly::symbolizer::ElfFileId> {\n  size_t operator()(const folly::symbolizer::ElfFileId fileId) const {\n    return folly::hash::hash_combine(\n        fileId.dev, fileId.inode, fileId.size, fileId.mtime);\n  }\n};\n} // namespace std\n\n#include <folly/debugging/symbolizer/Elf-inl.h>\n\n#endif // FOLLY_HAVE_ELF\n"
  },
  {
    "path": "folly/debugging/symbolizer/ElfCache.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/ElfCache.h>\n\n#include <signal.h>\n\n#include <folly/ScopeGuard.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/SysMman.h>\n\n#if FOLLY_HAVE_ELF\n\nnamespace folly {\nnamespace symbolizer {\n\nSignalSafeElfCache::Path::Path(\n    char const* const data,\n    std::size_t const size,\n    reentrant_allocator<char> const& alloc) noexcept\n    : data_{alloc} {\n  data_.reserve(size + 1);\n  data_.insert(data_.end(), data, data + size);\n  data_.insert(data_.end(), '\\0');\n}\n\nstd::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {\n  struct cmp {\n    bool operator()(Entry const& a, StringPiece b) const noexcept {\n      return a.path < b;\n    }\n    bool operator()(StringPiece a, Entry const& b) const noexcept {\n      return a < b.path;\n    }\n  };\n\n  sigset_t newsigs;\n  sigfillset(&newsigs);\n  sigset_t oldsigs;\n  sigemptyset(&oldsigs);\n  sigprocmask(SIG_SETMASK, &newsigs, &oldsigs);\n  SCOPE_EXIT {\n    sigprocmask(SIG_SETMASK, &oldsigs, nullptr);\n  };\n\n  if (!state_) {\n    state_.emplace();\n  }\n\n  auto pos = state_->map.find(p, cmp{});\n  if (pos == state_->map.end()) {\n    state_->list.emplace_front(p, state_->alloc);\n    pos = state_->map.insert(state_->list.front()).first;\n  }\n\n  if (!pos->init) {\n    int r = pos->file->openAndFollow(pos->path.c_str());\n    pos->init = r == ElfFile::kSuccess;\n  }\n  if (!pos->init) {\n    return nullptr;\n  }\n\n  return pos->file;\n}\n\nstd::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {\n  std::lock_guard lock(mutex_);\n\n  auto pos = files_.find(p);\n  if (pos != files_.end()) {\n    // Found\n    auto& entry = pos->second;\n    return filePtr(entry);\n  }\n\n  auto entry = std::make_shared<Entry>();\n  entry->path = p.str();\n  auto& path = entry->path;\n\n  // No negative caching\n  int r = entry->file.openAndFollow(path.c_str());\n  if (r != ElfFile::kSuccess) {\n    return nullptr;\n  }\n\n  pos = files_.emplace(path, std::move(entry)).first;\n\n  return filePtr(pos->second);\n}\n\nstd::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) {\n  // share ownership\n  return std::shared_ptr<ElfFile>(e, &e->file);\n}\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_ELF\n"
  },
  {
    "path": "folly/debugging/symbolizer/ElfCache.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <forward_list>\n#include <memory>\n#include <mutex>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include <boost/intrusive/avl_set.hpp>\n\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/Elf.h>\n#include <folly/hash/Hash.h>\n#include <folly/memory/ReentrantAllocator.h>\n#include <folly/portability/Config.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n#if FOLLY_HAVE_ELF\n\nclass ElfCacheBase {\n public:\n  virtual std::shared_ptr<ElfFile> getFile(StringPiece path) = 0;\n  virtual ~ElfCacheBase() = default;\n  ElfCacheBase() = default;\n  ElfCacheBase(const ElfCacheBase&) = delete;\n  ElfCacheBase& operator=(const ElfCacheBase&) = delete;\n  ElfCacheBase(ElfCacheBase&&) = delete;\n  ElfCacheBase& operator=(ElfCacheBase&&) = delete;\n};\n\n/**\n * Cache ELF files. Async-signal-safe: does memory allocation via mmap.\n *\n * Not MT-safe. May not be used concurrently from multiple threads.\n */\nclass SignalSafeElfCache : public ElfCacheBase {\n public:\n  std::shared_ptr<ElfFile> getFile(StringPiece path) override;\n\n  //  Path\n  //\n  //  A minimal implementation of the subset of std::string used below, as if:\n  //\n  //    using Path = std::basic_string<\n  //        char, std::char_traits<char>, reentrant_allocator<char>>;\n  //\n  //  Since some library implementations of std::basic_string, as on CentOS 7,\n  //  do not build when instantiated with a non-default-constructible allocator,\n  //  and since other library replacements, such as folly::basic_fbstring, just\n  //  ignore the allocator parameter.\n  class Path {\n   public:\n    Path(\n        char const* data,\n        std::size_t size,\n        reentrant_allocator<char> const& alloc) noexcept;\n    ~Path() = default;\n    Path() = delete;\n    Path(Path const&) = delete;\n    void operator=(Path const&) = delete;\n    Path(Path&&) = delete;\n    Path& operator=(Path&&) = delete;\n\n    /* implicit */ operator StringPiece() const noexcept { return data_; }\n\n    char const* c_str() const noexcept { return data_.data(); }\n\n    friend bool operator<(Path const& a, Path const& b) noexcept {\n      return a.data_ < b.data_;\n    }\n\n   private:\n    std::vector<char, reentrant_allocator<char>> data_;\n  };\n\n  struct Entry : boost::intrusive::avl_set_base_hook<> {\n    Path path;\n    std::shared_ptr<ElfFile> file;\n    bool init = false;\n\n    explicit Entry(StringPiece p, reentrant_allocator<char> alloc) noexcept\n        : path{p.data(), p.size(), alloc},\n          file{std::allocate_shared<ElfFile>(alloc)} {}\n    ~Entry() = default;\n    Entry(Entry const&) = delete;\n    Entry& operator=(Entry const& that) = delete;\n    Entry(Entry&&) = delete;\n    Entry& operator=(Entry&&) = delete;\n\n    friend bool operator<(Entry const& a, Entry const& b) noexcept {\n      return a.path < b.path;\n    }\n  };\n\n  struct State {\n    reentrant_allocator<void> alloc{\n        reentrant_allocator_options().block_size_lg(16).large_size_lg(12)};\n    std::forward_list<Entry, reentrant_allocator<Entry>> list{alloc};\n    // note: map entry dtors check that they have already been unlinked\n    boost::intrusive::avl_set<Entry> map; // must follow list\n  };\n  Optional<State> state_;\n};\n\n/**\n * General-purpose ELF file cache.\n *\n * LRU of given capacity. MT-safe (uses locking). Not async-signal-safe.\n */\nclass ElfCache : public ElfCacheBase {\n public:\n  std::shared_ptr<ElfFile> getFile(StringPiece path) override;\n\n private:\n  std::mutex mutex_;\n\n  struct Entry {\n    std::string path;\n    ElfFile file;\n  };\n\n  static std::shared_ptr<ElfFile> filePtr(const std::shared_ptr<Entry>& e);\n\n  std::unordered_map<StringPiece, std::shared_ptr<Entry>, Hash> files_;\n};\n\n#endif // FOLLY_HAVE_ELF\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/LineReader.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/LineReader.h>\n\n#include <cstring>\n\n#include <folly/FileUtil.h>\n\nnamespace folly {\nnamespace symbolizer {\n\nLineReader::LineReader(int fd, char* buf, size_t bufSize)\n    : fd_(fd),\n      buf_(buf),\n      bufEnd_(buf_ + bufSize),\n      bol_(buf),\n      eol_(buf),\n      end_(buf),\n      state_(kReading) {}\n\nLineReader::State LineReader::readLine(StringPiece& line) {\n  bol_ = eol_; // Start past what we already returned\n  for (;;) {\n    // Search for newline\n    char* newline = static_cast<char*>(memchr(eol_, '\\n', end_ - eol_));\n    if (newline) {\n      eol_ = newline + 1;\n      break;\n    } else if (state_ != kReading || (bol_ == buf_ && end_ == bufEnd_)) {\n      // If the buffer is full with one line (line too long), or we're\n      // at the end of the file, return what we have.\n      eol_ = end_;\n      break;\n    }\n\n    // We don't have a full line in the buffer, but we have room to read.\n    // Move to the beginning of the buffer.\n    memmove(buf_, eol_, end_ - eol_);\n    end_ -= (eol_ - buf_);\n    bol_ = buf_;\n    eol_ = end_;\n\n    // Refill\n    ssize_t available = bufEnd_ - end_;\n    ssize_t n = readFull(fd_, end_, available);\n    if (n < 0) {\n      state_ = kError;\n      n = 0;\n    } else if (n < available) {\n      state_ = kEof;\n    }\n    end_ += n;\n  }\n\n  line.assign(bol_, eol_);\n  return eol_ != bol_ ? kReading : state_;\n}\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/LineReader.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n\n#include <folly/Range.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n/**\n * Async-signal-safe line reader.\n */\nclass LineReader {\n public:\n  /**\n   * Create a line reader that reads into a user-provided buffer (of size\n   * bufSize).\n   */\n  LineReader(int fd, char* buf, size_t bufSize);\n\n  LineReader(const LineReader&) = delete;\n  LineReader& operator=(const LineReader&) = delete;\n\n  enum State {\n    kReading,\n    kEof,\n    kError,\n  };\n  /**\n   * Read the next line from the file.\n   *\n   * If the line is at most bufSize characters long, including the trailing\n   * newline, it will be returned (including the trailing newline).\n   *\n   * If the line is longer than bufSize, we return the first bufSize bytes\n   * (which won't include a trailing newline) and then continue from that\n   * point onwards.\n   *\n   * The lines returned are not null-terminated.\n   *\n   * Returns kReading with a valid line, kEof if at end of file, or kError\n   * if a read error was encountered.\n   *\n   * Example:\n   *   bufSize = 10\n   *   input has \"hello world\\n\"\n   *   The first call returns \"hello worl\"\n   *   The second call returns \"d\\n\"\n   */\n  State readLine(StringPiece& line);\n\n private:\n  int const fd_;\n  char* const buf_;\n  char* const bufEnd_;\n\n  // buf_ <= bol_ <= eol_ <= end_ <= bufEnd_\n  //\n  // [buf_, end_): current buffer contents (read from file)\n  //\n  // [buf_, bol_): free (already processed, can be discarded)\n  // [bol_, eol_): current line, including \\n if it exists, eol_ points\n  //               1 character past the \\n\n  // [eol_, end_): read, unprocessed\n  // [end_, bufEnd_): free\n\n  char* bol_;\n  char* eol_;\n  char* end_;\n  State state_;\n};\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/SignalHandler.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is heavily inspired by the signal handler from google-glog\n\n#include <folly/debugging/symbolizer/SignalHandler.h>\n\n#include <signal.h>\n\n#include <atomic>\n#include <cerrno>\n#include <ctime>\n#include <mutex>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/ScopeGuard.h>\n#include <folly/debugging/symbolizer/Symbolizer.h>\n#include <folly/lang/ToAscii.h>\n#include <folly/portability/SysSyscall.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n#ifdef _WIN32\n\nconst unsigned long kAllFatalSignals = 0;\n\n#else\n\nconst unsigned long kAllFatalSignals = (1UL << SIGSEGV) | (1UL << SIGILL) |\n    (1UL << SIGFPE) | (1UL << SIGABRT) | (1UL << SIGBUS) | (1UL << SIGTERM) |\n    (1UL << SIGQUIT);\n\n#endif\n\n#ifndef SI_KERNEL\n#define SI_KERNEL 0x80\n#endif\n#ifndef SI_TKILL\n#define SI_TKILL (-6)\n#endif\n\nnamespace {\n\n/**\n * Fatal signal handler registry.\n */\nclass FatalSignalCallbackRegistry {\n public:\n  FatalSignalCallbackRegistry();\n\n  void add(SignalCallback func);\n  void markInstalled();\n  void run();\n\n private:\n  std::atomic<bool> installed_;\n  std::mutex mutex_;\n  std::vector<SignalCallback> handlers_;\n};\n\nFatalSignalCallbackRegistry::FatalSignalCallbackRegistry()\n    : installed_(false) {}\n\nvoid FatalSignalCallbackRegistry::add(SignalCallback func) {\n  std::lock_guard lock(mutex_);\n  CHECK(!installed_) << \"FatalSignalCallbackRegistry::add may not be used \"\n                        \"after installing the signal handlers.\";\n  handlers_.push_back(func);\n}\n\nvoid FatalSignalCallbackRegistry::markInstalled() {\n  std::lock_guard lock(mutex_);\n  CHECK(!installed_.exchange(true))\n      << \"FatalSignalCallbackRegistry::markInstalled must be called \"\n      << \"at most once\";\n}\n\nvoid FatalSignalCallbackRegistry::run() {\n  if (!installed_) {\n    return;\n  }\n\n  for (auto& fn : handlers_) {\n    fn();\n  }\n}\n\nstd::atomic<FatalSignalCallbackRegistry*> gFatalSignalCallbackRegistry{};\n\nFatalSignalCallbackRegistry* getFatalSignalCallbackRegistry() {\n  // Leak it so we don't have to worry about destruction order\n  static FatalSignalCallbackRegistry* fatalSignalCallbackRegistry =\n      new FatalSignalCallbackRegistry();\n\n  return fatalSignalCallbackRegistry;\n}\n\n} // namespace\n\nvoid addFatalSignalCallback(SignalCallback cb) {\n  getFatalSignalCallbackRegistry()->add(cb);\n}\n\nvoid installFatalSignalCallbacks() {\n  getFatalSignalCallbackRegistry()->markInstalled();\n}\n\n#ifndef _WIN32\n\nnamespace {\n\nstruct FatalSignalInfo {\n  int number;\n  const char* name;\n  struct sigaction oldAction;\n};\n\nFatalSignalInfo kFatalSignals[] = {\n    {SIGSEGV, \"SIGSEGV\", {}},\n    {SIGILL, \"SIGILL\", {}},\n    {SIGFPE, \"SIGFPE\", {}},\n    {SIGABRT, \"SIGABRT\", {}},\n    {SIGBUS, \"SIGBUS\", {}},\n    {SIGTERM, \"SIGTERM\", {}},\n    {SIGQUIT, \"SIGQUIT\", {}},\n    {0, nullptr, {}},\n};\n\ntemplate <typename...>\nbool try_async_reraise(int signum, siginfo_t* info) {\n  using folly::detail::linux_syscall;\n#if defined(__linux__) && defined(SYS_pidfd_send_signal) && \\\n    defined(SYS_pidfd_open)\n  constexpr long nr_pidfd_send_signal = SYS_pidfd_send_signal;\n  constexpr long nr_pidfd_open = SYS_pidfd_open;\n#else\n  constexpr long nr_pidfd_send_signal = -1;\n  constexpr long nr_pidfd_open = -1;\n#endif\n  if constexpr (kIsLinux && nr_pidfd_send_signal >= 0 && nr_pidfd_open >= 0) {\n    constexpr auto kPIdfdSelf = -10000;\n    // PIDFD_SELF handling introduced in linux-6.15 (released 2025-05-25)\n    if (0 == linux_syscall(nr_pidfd_send_signal, kPIdfdSelf, signum, info, 0)) {\n      return true;\n    }\n    // fallback using a real pidfd\n    // TODO: remove fallback once minimum is linux-6.15\n    if (errno != EBADF) { // EBADF here means PIDFD_SELF is not yet supported\n      return false;\n    }\n    auto const tid = linux_syscall(FOLLY_SYS_gettid);\n    // pidfd_open introduced in linux-5.3 (released 2019-09-15)\n    int const fd = to_narrow(linux_syscall(nr_pidfd_open, tid, 0));\n    if (-1 == fd) {\n      return false;\n    }\n    // pidfd_send_signal introduced in linux-5.1 (released 2019-05-05)\n    // no need to close(fd) after this - the process is about to terminate\n    return 0 == linux_syscall(nr_pidfd_send_signal, fd, signum, info, 0);\n  }\n  return false;\n}\n\nvoid signalHandler(int signum, siginfo_t* info, void* uctx);\n\n[[maybe_unused]] void callPreviousSignalHandler(int signum, siginfo_t* info) {\n  // Restore disposition to old disposition, then kill ourselves with the same\n  // signal. The signal will remain blocked until the current call to the signal\n  // handler returns.\n  //\n  // On Linux, use pidfd_send_signal to re-raise the original signal with the\n  // original siginfo structure. Otherwise, just re-raise the original signal.\n  // Re-raising the original signal with the original siginfo enables the Linux\n  // kernel to record the true cause of the signal into the coredump. Otherwise,\n  // the kernel would see the explicit call to raise() as the cause and would\n  // record that in the coredump.\n  //\n  // For a signal arising from a faulting instruction, there is an alternative\n  // technique. After restoring disposition, the signal handler would simply\n  // return. Then the faulting instruction would execute again and be expected\n  // to trigger the same signal again. The second time, there would be no signal\n  // handler to handle the signal, so the kernel would see this second execution\n  // of the instruction as the true cause of the signal and would record that in\n  // the coredump. However, this technique is subject to the race where another\n  // thread might resolve the fault, causing the instruction's second execution\n  // to resume without fault. This can be a problem since we do want the process\n  // to terminate immediately with a coredump, and since the process resumes but\n  // without the corresponding signal handler anymore so the next time the same\n  // fault type happens there is no signal handler to report it.\n  for (auto p = kFatalSignals; p->name; ++p) {\n    if (p->number == signum) {\n      sigaction(signum, &p->oldAction, nullptr);\n      if (try_async_reraise(signum, info)) {\n        return;\n      }\n      // Unblock the signal before raising it. Since our handler doesn't use\n      // SA_NODEFER, the signal is currently blocked.\n      sigset_t mask;\n      sigemptyset(&mask);\n      sigaddset(&mask, signum);\n      pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);\n      raise(signum);\n    }\n  }\n\n  // Not one of the signals we know about. Oh well. Reset to default.\n  struct sigaction sa;\n  memset(&sa, 0, sizeof(sa));\n  sa.sa_handler = SIG_DFL;\n  sigaction(signum, &sa, nullptr);\n  if (try_async_reraise(signum, info)) {\n    return;\n  }\n  raise(signum);\n}\n\n#if FOLLY_USE_SYMBOLIZER\n\n// Note: not thread-safe, but that's okay, as we only let one thread\n// in our signal handler at a time.\n//\n// Leak it so we don't have to worry about destruction order\n//\n// Initialized by installFatalSignalHandler\nSafeStackTracePrinter* gStackTracePrinter;\n\nvoid print(StringPiece sp) {\n  gStackTracePrinter->print(sp);\n}\n\nvoid flush() {\n  gStackTracePrinter->flush();\n}\n\nvoid printDec(uint64_t val) {\n  char buf[to_ascii_size_max_decimal<uint64_t>];\n  size_t n = to_ascii_decimal(buf, val);\n  gStackTracePrinter->print(StringPiece(buf, n));\n}\n\nvoid printHex(uint64_t val) {\n  char buf[2 + to_ascii_size_max<16, uint64_t>];\n  auto out = buf + 0;\n  *out++ = '0';\n  *out++ = 'x';\n  out += to_ascii_lower<16>(out, buf + sizeof(buf), val);\n  gStackTracePrinter->print(StringPiece(buf, out - buf));\n}\n\nvoid dumpTimeInfo() {\n  SCOPE_EXIT {\n    flush();\n  };\n  time_t now = time(nullptr);\n  print(\"*** Aborted at \");\n  printDec(now);\n  print(\" (Unix time, try 'date -d @\");\n  printDec(now);\n  print(\"') ***\\n\");\n}\n\n// Any signal can have a generic SI_CODE attached to it.\nconst char* generic_sicode_reason(int si_code) {\n  switch (si_code) {\n    case SI_USER:\n      return \"sent by kill, sigsend or raise\";\n    case SI_QUEUE:\n      return \"sent by sigqueue\";\n    case SI_TIMER:\n      return \"sent by alarm/timer expiry\";\n    case SI_ASYNCIO:\n      return \"sent by AIO completion\";\n    case SI_KERNEL:\n      return \"sent by kernel (SI_KERNEL)\";\n    case SI_TKILL:\n      return \"sent by tkill or tgkill\";\n    default:\n      return nullptr;\n  }\n}\n\nconst char* sigill_reason(int si_code) {\n  switch (si_code) {\n    case ILL_ILLOPC:\n      return \"illegal opcode\";\n    case ILL_ILLOPN:\n      return \"illegal operand\";\n    case ILL_ILLADR:\n      return \"illegal addressing mode\";\n    case ILL_ILLTRP:\n      return \"illegal trap\";\n    case ILL_PRVOPC:\n      return \"privileged opcode\";\n    case ILL_PRVREG:\n      return \"privileged register\";\n    case ILL_COPROC:\n      return \"coprocessor error\";\n    case ILL_BADSTK:\n      return \"internal stack error\";\n\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nconst char* sigfpe_reason(int si_code) {\n  switch (si_code) {\n    case FPE_INTDIV:\n      return \"integer divide by zero\";\n    case FPE_INTOVF:\n      return \"integer overflow\";\n    case FPE_FLTDIV:\n      return \"floating-point divide by zero\";\n    case FPE_FLTOVF:\n      return \"floating-point overflow\";\n    case FPE_FLTUND:\n      return \"floating-point underflow\";\n    case FPE_FLTRES:\n      return \"floating-point inexact result\";\n    case FPE_FLTINV:\n      return \"floating-point invalid operation\";\n    case FPE_FLTSUB:\n      return \"subscript out of range\";\n\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nconst char* sigsegv_reason(int si_code) {\n  switch (si_code) {\n    case SEGV_MAPERR:\n      return \"address not mapped to object\";\n    case SEGV_ACCERR:\n      return \"invalid permissions for mapped object\";\n\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nconst char* sigbus_reason(int si_code) {\n  switch (si_code) {\n    case BUS_ADRALN:\n      return \"invalid address alignment\";\n    case BUS_ADRERR:\n      return \"nonexistent physical address\";\n    case BUS_OBJERR:\n      return \"object-specific hardware error\";\n\n      // MCEERR_AR and MCEERR_AO: in sigaction(2) but not in headers.\n\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nconst char* sigtrap_reason(int si_code) {\n  switch (si_code) {\n    case TRAP_BRKPT:\n      return \"process breakpoint\";\n    case TRAP_TRACE:\n      return \"process trace trap\";\n\n      // TRAP_BRANCH and TRAP_HWBKPT: in sigaction(2) but not in headers.\n\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nconst char* sigchld_reason(int si_code) {\n  switch (si_code) {\n    case CLD_EXITED:\n      return \"child has exited\";\n    case CLD_KILLED:\n      return \"child was killed\";\n    case CLD_DUMPED:\n      return \"child terminated abnormally\";\n    case CLD_TRAPPED:\n      return \"traced child has trapped\";\n    case CLD_STOPPED:\n      return \"child has stopped\";\n    case CLD_CONTINUED:\n      return \"stopped child has continued\";\n\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nconst char* sigio_reason(int si_code) {\n  switch (si_code) {\n    case POLL_IN:\n      return \"data input available\";\n    case POLL_OUT:\n      return \"output buffers available\";\n    case POLL_MSG:\n      return \"input message available\";\n    case POLL_ERR:\n      return \"I/O error\";\n    case POLL_PRI:\n      return \"high priority input available\";\n    case POLL_HUP:\n      return \"device disconnected\";\n\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nconst char* signal_reason(int signum, int si_code) {\n  switch (signum) {\n    case SIGILL:\n      return sigill_reason(si_code);\n    case SIGFPE:\n      return sigfpe_reason(si_code);\n    case SIGSEGV:\n      return sigsegv_reason(si_code);\n    case SIGBUS:\n      return sigbus_reason(si_code);\n    case SIGTRAP:\n      return sigtrap_reason(si_code);\n    case SIGCHLD:\n      return sigchld_reason(si_code);\n    case SIGIO:\n      return sigio_reason(si_code); // aka SIGPOLL\n    default:\n      return generic_sicode_reason(si_code);\n  }\n}\n\nvoid dumpSignalInfo(int signum, siginfo_t* siginfo) {\n  SCOPE_EXIT {\n    flush();\n  };\n  // Get the signal name, if possible.\n  const char* name = nullptr;\n  for (auto p = kFatalSignals; p->name; ++p) {\n    if (p->number == signum) {\n      name = p->name;\n      break;\n    }\n  }\n\n  print(\"*** Signal \");\n  printDec(signum);\n  if (name) {\n    print(\" (\");\n    print(name);\n    print(\")\");\n  }\n\n  print(\" (\");\n  printHex(reinterpret_cast<uint64_t>(siginfo->si_addr));\n  print(\") received by PID \");\n  printDec(getpid());\n  print(\" (pthread TID \");\n  printHex((uint64_t)pthread_self());\n#if defined(__linux__)\n  print(\") (linux TID \");\n  printDec(syscall(__NR_gettid));\n#elif defined(__FreeBSD__)\n  long tid = 0;\n  syscall(432, &tid);\n  print(\") (freebsd TID \");\n  printDec(tid);\n#endif\n\n  // Kernel-sourced signals don't give us useful info for pid/uid.\n  if (siginfo->si_code <= 0) {\n    print(\") (maybe from PID \");\n    printDec(siginfo->si_pid);\n    print(\", UID \");\n    printDec(siginfo->si_uid);\n  }\n\n  auto reason = signal_reason(signum, siginfo->si_code);\n\n  print(\") (code: \");\n  // If we can't find a reason code make a best effort to print the (int) code.\n  if (reason != nullptr) {\n    print(reason);\n  } else {\n    if (siginfo->si_code < 0) {\n      print(\"-\");\n      printDec(-siginfo->si_code);\n    } else {\n      printDec(siginfo->si_code);\n    }\n  }\n\n  print(\"), stack trace: ***\\n\");\n}\n\n// On Linux, pthread_t is a pointer, so 0 is an invalid value, which we\n// take to indicate \"no thread in the signal handler\".\n//\n// POSIX defines PTHREAD_NULL for this purpose, but that's not available.\nconstexpr pthread_t kInvalidThreadId = 0;\n\nstd::atomic<pthread_t> gSignalThread(kInvalidThreadId);\nstd::atomic<bool> gInRecursiveSignalHandler(false);\n\n// Here be dragons.\nvoid innerSignalHandler(int signum, siginfo_t* info, void* /* uctx */) {\n  // First, let's only let one thread in here at a time.\n  pthread_t myId = pthread_self();\n\n  pthread_t prevSignalThread = kInvalidThreadId;\n  while (!gSignalThread.compare_exchange_strong(prevSignalThread, myId)) {\n    if (pthread_equal(prevSignalThread, myId)) {\n      // First time here. Try to dump the stack trace without symbolization.\n      // If we still fail, well, we're mightily screwed, so we do nothing the\n      // next time around.\n      if (!gInRecursiveSignalHandler.exchange(true)) {\n        print(\"Entered fatal signal handler recursively. We're in trouble.\\n\");\n        gStackTracePrinter->printStackTrace(false); // no symbolization\n      }\n      return;\n    }\n\n    // Wait a while, try again.\n    timespec ts;\n    ts.tv_sec = 0;\n    ts.tv_nsec = 100L * 1000 * 1000; // 100ms\n    nanosleep(&ts, nullptr);\n\n    prevSignalThread = kInvalidThreadId;\n  }\n\n  dumpTimeInfo();\n  dumpSignalInfo(signum, info);\n  gStackTracePrinter->printStackTrace(true); // with symbolization\n\n  // Run user callbacks\n  auto callbacks = gFatalSignalCallbackRegistry.load(std::memory_order_acquire);\n  if (callbacks) {\n    callbacks->run();\n  }\n}\n\nnamespace {\nstd::atomic<bool> gFatalSignalReceived{false};\n} // namespace\n\nvoid signalHandler(int signum, siginfo_t* info, void* uctx) {\n  gFatalSignalReceived.store(true, std::memory_order_relaxed);\n\n  int savedErrno = errno;\n  SCOPE_EXIT {\n    flush();\n    errno = savedErrno;\n  };\n  innerSignalHandler(signum, info, uctx);\n\n  gSignalThread = kInvalidThreadId;\n  // Kill ourselves with the previous handler.\n  callPreviousSignalHandler(signum, info);\n}\n\n#endif // FOLLY_USE_SYMBOLIZER\n\n// Small sigaltstack size threshold.\n// 51392 is known to cause the signal handler to stack overflow during\n// symbolization of trivial async stacks (e.g [] { CHECK(false); co_return; }).\n// 54016 is known to cause the signal handler to stack overflow during\n// symbolization of less trivial async stacks. Setting 64KB to have a bit larger\n// and \"less\" magical threshold.\nconstexpr size_t kSmallSigAltStackSize = 65536;\n\n[[maybe_unused]] bool isSmallSigAltStackEnabled() {\n  stack_t ss;\n  if (sigaltstack(nullptr, &ss) != 0) {\n    return false;\n  }\n  if ((ss.ss_flags & SS_DISABLE) != 0) {\n    return false;\n  }\n  return ss.ss_size <= kSmallSigAltStackSize;\n}\n\n} // namespace\n\n#endif // _WIN32\n\nnamespace {\nstd::atomic<bool> gAlreadyInstalled;\n}\n\nvoid installFatalSignalHandler(std::bitset<64> signals) {\n  if (gAlreadyInstalled.exchange(true)) {\n    // Already done.\n    return;\n  }\n\n  // make sure gFatalSignalCallbackRegistry is created before we\n  // install the fatal signal handler\n  gFatalSignalCallbackRegistry.store(\n      getFatalSignalCallbackRegistry(), std::memory_order_release);\n\n#if FOLLY_USE_SYMBOLIZER\n  // If a small sigaltstack is enabled (ex. Rust stdlib might use sigaltstack\n  // to set a small stack), the default SafeStackTracePrinter would likely\n  // stack overflow. Replace it with the unsafe self-allocate printer.\n  bool useUnsafePrinter = kIsLinux && isSmallSigAltStackEnabled();\n  if (useUnsafePrinter) {\n#if FOLLY_HAVE_SWAPCONTEXT\n    gStackTracePrinter = new UnsafeSelfAllocateStackTracePrinter();\n#else\n    // This environment does not support swapcontext, so always use\n    // SafeStackTracePrinter.\n    gStackTracePrinter = new SafeStackTracePrinter();\n#endif // FOLLY_HAVE_SWAPCONTEXT\n  } else {\n    gStackTracePrinter = new SafeStackTracePrinter();\n  }\n\n  struct sigaction sa;\n  memset(&sa, 0, sizeof(sa));\n  if (useUnsafePrinter) {\n    // The signal handler is not async-signal-safe. Block all signals to\n    // make it safer. But it's still unsafe.\n    sigfillset(&sa.sa_mask);\n  } else {\n    sigemptyset(&sa.sa_mask);\n  }\n  // By default signal handlers are run on the signaled thread's stack.\n  // In case of stack overflow running the SIGSEGV signal handler on\n  // the same stack leads to another SIGSEGV and crashes the program.\n  // Use SA_ONSTACK, so alternate stack is used (only if configured via\n  // sigaltstack).\n  // Golang also requires SA_ONSTACK. See:\n  // https://golang.org/pkg/os/signal/#hdr-Go_programs_that_use_cgo_or_SWIG\n  sa.sa_flags |= SA_SIGINFO | SA_ONSTACK;\n  sa.sa_sigaction = &signalHandler;\n\n  for (auto p = kFatalSignals; p->name; ++p) {\n    if ((p->number < static_cast<int>(signals.size())) &&\n        signals.test(p->number)) {\n      CHECK_ERR(sigaction(p->number, &sa, &p->oldAction));\n    }\n  }\n#endif // FOLLY_USE_SYMBOLIZER\n}\n\nbool fatalSignalReceived() {\n#ifdef FOLLY_USE_SYMBOLIZER\n  return gFatalSignalReceived.load(std::memory_order_relaxed);\n#else\n  return false;\n#endif\n}\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/SignalHandler.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <bitset>\n\nnamespace folly {\nnamespace symbolizer {\n\nextern const unsigned long kAllFatalSignals;\n\n/**\n * Install handler for fatal signals. The list of signals being handled is in\n * SignalHandler.cpp.\n *\n * The handler will dump signal and time information followed by a stack trace\n * to stderr, and then call the callbacks registered below.\n *\n * The signals parameter can be used to specify only specific fatal signals for\n * which the handler should be installed.  Only signals from kAllFatalSignals\n * are honored in this list, other signals are ignored.\n */\nvoid installFatalSignalHandler(\n    std::bitset<64> signals = std::bitset<64>(kAllFatalSignals));\n\n/**\n * Add a callback to be run when receiving a fatal signal. They will also\n * be called by LOG(FATAL) and abort() (as those raise SIGABRT internally).\n *\n * These callbacks must be async-signal-safe, so don't even think of using\n * LOG(...) or printf or malloc / new or doing anything even remotely fun.\n *\n * All these fatal callback must be added before calling\n * installFatalSignalCallbacks(), below.\n */\nusing SignalCallback = void (*)();\nvoid addFatalSignalCallback(SignalCallback cb);\n\n/**\n * Install the fatal signal callbacks; fatal signals will call these\n * callbacks in the order in which they were added.\n */\nvoid installFatalSignalCallbacks();\n\n/**\n * True if a fatal signal was received (i.e. the process is crashing).\n */\nbool fatalSignalReceived();\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/StackTrace.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/StackTrace.h>\n#include <folly/tracing/AsyncStack.h>\n\n#include <memory>\n#include <optional>\n\n#include <folly/Portability.h>\n#include <folly/portability/Config.h>\n\n#if FOLLY_HAVE_LIBUNWIND\n// Must be first to ensure that UNW_LOCAL_ONLY is defined\n#define UNW_LOCAL_ONLY 1\n#include <folly/portability/Libunwind.h>\n#endif\n\n#if FOLLY_HAVE_BACKTRACE\n#include <execinfo.h> // @donotremove\n#endif\n\nnamespace folly {\nnamespace symbolizer {\n\nnamespace {\n// force a getStackTrace to work around a race condition in the\n// libunwind tdep_init\nstatic uintptr_t sAddr = 0;\nstatic ssize_t sInit = getStackTrace(&sAddr, 0);\n} // namespace\n\nssize_t getStackTrace(\n    [[maybe_unused]] uintptr_t* addresses,\n    [[maybe_unused]] size_t maxAddresses) {\n  static_assert(\n      sizeof(uintptr_t) == sizeof(void*), \"uintptr_t / pointer size mismatch\");\n  std::ignore = sInit;\n  // The libunwind documentation says that unw_backtrace is\n  // async-signal-safe but, as of libunwind 1.0.1, it isn't\n  // (tdep_trace allocates memory on x86_64)\n  //\n  // There are two major variants of libunwind. libunwind on Linux\n  // (https://www.nongnu.org/libunwind/) provides unw_backtrace, and\n  // Apache/LLVM libunwind (notably used on Apple platforms)\n  // doesn't. They can be distinguished with the UNW_VERSION #define.\n  //\n  // When unw_backtrace is not available, fall back on the standard\n  // `backtrace` function from execinfo.h.\n#if FOLLY_HAVE_LIBUNWIND && defined(UNW_VERSION)\n  int r = unw_backtrace(reinterpret_cast<void**>(addresses), maxAddresses);\n  return r < 0 ? -1 : r;\n#elif FOLLY_HAVE_BACKTRACE\n  int r = backtrace(reinterpret_cast<void**>(addresses), maxAddresses);\n  return r < 0 ? -1 : r;\n#elif FOLLY_HAVE_LIBUNWIND\n  return getStackTraceSafe(addresses, maxAddresses);\n#else\n  return -1;\n#endif\n}\n\nnamespace {\n\n// Heuristic for guessing the maximum stack frame size. This is needed to ensure\n// we do not have stack corruption while walking the stack.\nconstexpr size_t kMaxExpectedStackFrameSizeLg2 = sizeof(size_t) == 8\n    ? 36 // 64GB\n    : 28 // 256MB\n    ;\nconstexpr size_t kMaxExpectedStackFrameSize //\n    = size_t(1) << kMaxExpectedStackFrameSizeLg2;\n\n#if FOLLY_HAVE_LIBUNWIND\n\nstruct FrameInfo {\n  /// The instruction pointer (ip) of the frame.\n  uintptr_t uip;\n  bool isSignalFrame;\n\n  inline uintptr_t getAdjustedInstructionPointer(\n      std::optional<FrameInfo> frameInfoPrev) const {\n    bool adjustment;\n\n    if constexpr (folly::kIsArchAArch64) {\n      // This ip adjustment logic matches `unw_backtrace`.\n      // https://github.com/libunwind/libunwind/blob/v1.8.2/src/aarch64/Gtrace.c#L554-L555\n      // The ip is adjusted if there wasn't a previous frame,\n      // or if the previous frame was NOT a signal frame.\n      adjustment = !frameInfoPrev || !frameInfoPrev->isSignalFrame;\n    } else {\n      // Use previous instruction in normal (call) frames (because the\n      // return address might not be in the same function for noreturn\n      // functions) but not in signal frames.\n      adjustment = !this->isSignalFrame;\n    }\n\n    return this->uip - adjustment;\n  }\n};\n\n/// Gets the `FrameInfo` from libunwind and stores it in the out ref parameter.\n///\n/// @return true on success and false on error.\ninline bool getFrameInfo(unw_cursor_t* cursor, FrameInfo& frameInfo) {\n  unw_word_t uip;\n  if (unw_get_reg(cursor, UNW_REG_IP, &uip) < 0) {\n    return false;\n  }\n  int r = unw_is_signal_frame(cursor);\n  if (r < 0) {\n    return false;\n  }\n  bool isSignalFrame = r > 0;\n\n  frameInfo = FrameInfo{uip, isSignalFrame};\n\n  return true;\n}\n\n// on ppc64le, fails with\n// function can never be inlined because it uses setjmp\n#if FOLLY_PPC64 == 0\nFOLLY_ALWAYS_INLINE\n#endif\nssize_t getStackTraceInPlace(\n    unw_context_t& context,\n    unw_cursor_t& cursor,\n    uintptr_t* addresses,\n    size_t maxAddresses) {\n  if (maxAddresses == 0) {\n    return 0;\n  }\n  if (unw_getcontext(&context) < 0) {\n    return -1;\n  }\n  if (unw_init_local(&cursor, &context) < 0) {\n    return -1;\n  }\n  FrameInfo frameInfo;\n  if (!getFrameInfo(&cursor, frameInfo)) {\n    return -1;\n  }\n\n  std::optional<FrameInfo> frameInfoPrev; // no previous frame, yet.\n  *addresses = frameInfo.getAdjustedInstructionPointer(frameInfoPrev);\n  ++addresses;\n  size_t count = 1;\n  for (; count != maxAddresses; ++count, ++addresses) {\n    int r = unw_step(&cursor);\n    if (r < 0) {\n      return -1;\n    }\n    if (r == 0) {\n      break;\n    }\n    frameInfoPrev = frameInfo;\n    if (!getFrameInfo(&cursor, frameInfo)) {\n      return -1;\n    }\n    *addresses = frameInfo.getAdjustedInstructionPointer(frameInfoPrev);\n  }\n  return count;\n}\n\n#endif // FOLLY_HAVE_LIBUNWIND\n} // namespace\n\nssize_t getStackTraceSafe(\n    [[maybe_unused]] uintptr_t* addresses,\n    [[maybe_unused]] size_t maxAddresses) {\n  std::ignore = sInit;\n#if defined(__APPLE__)\n  // While Apple platforms support libunwind, the unw_init_local,\n  // unw_step step loop does not cross the boundary from async signal\n  // handlers to the aborting code, while `backtrace` from execinfo.h\n  // does. `backtrace` is not explicitly documented on either macOS or\n  // Linux to be async-signal-safe, but the implementation in\n  // https://opensource.apple.com/source/Libc/Libc-1353.60.8/, and it is\n  // widely used in signal handlers in practice.\n  return backtrace(reinterpret_cast<void**>(addresses), maxAddresses);\n#elif FOLLY_HAVE_LIBUNWIND\n  unw_context_t context;\n  unw_cursor_t cursor;\n  return getStackTraceInPlace(context, cursor, addresses, maxAddresses);\n#else\n  return -1;\n#endif\n}\n\nssize_t getStackTraceHeap(\n    [[maybe_unused]] uintptr_t* addresses,\n    [[maybe_unused]] size_t maxAddresses) {\n  std::ignore = sInit;\n#if FOLLY_HAVE_LIBUNWIND\n  struct Ctx {\n    unw_context_t context;\n    unw_cursor_t cursor;\n  };\n  auto ctx_ptr = std::make_unique<Ctx>();\n  if (!ctx_ptr) {\n    return -1;\n  }\n  return getStackTraceInPlace(\n      ctx_ptr->context, ctx_ptr->cursor, addresses, maxAddresses);\n#else\n  return -1;\n#endif\n}\n\nnamespace {\n// Helper struct for manually walking the stack using stack frame pointers\nstruct StackFrame {\n  StackFrame* parentFrame;\n  void* returnAddress;\n};\n\nFOLLY_DISABLE_THREAD_SANITIZER size_t walkNormalStack(\n    uintptr_t* addresses,\n    size_t maxAddresses,\n    StackFrame* normalStackFrame,\n    StackFrame* normalStackFrameStop) {\n  size_t numFrames = 0;\n  while (numFrames < maxAddresses && normalStackFrame != nullptr) {\n    auto* normalStackFrameNext = normalStackFrame->parentFrame;\n    if (!(normalStackFrameNext > normalStackFrame &&\n          normalStackFrameNext <\n              normalStackFrame + kMaxExpectedStackFrameSize)) {\n      // Stack frame addresses should increase as we traverse the stack.\n      // If it doesn't, it means we have stack corruption, or an unusual calling\n      // convention. Ensure that each subsequent frame's address is within a\n      // valid range. If it does not, stop walking the stack early to avoid\n      // incorrect stack walking.\n      break;\n    }\n    if (normalStackFrameStop != nullptr &&\n        normalStackFrameNext == normalStackFrameStop) {\n      // Reached end of normal stack, need to transition to the async stack.\n      // Do not include the return address in the stack trace that points\n      // to the frame that registered the AsyncStackRoot.\n      // Use the return address from the AsyncStackFrame as the current frame's\n      // return address rather than the return address from the normal\n      // stack frame, which would be the address of the executor function\n      // that invoked the callback.\n      break;\n    }\n    addresses[numFrames++] =\n        reinterpret_cast<std::uintptr_t>(normalStackFrame->returnAddress);\n    normalStackFrame = normalStackFrameNext;\n  }\n  return numFrames;\n}\n\nstruct WalkAsyncStackResult {\n  // Number of frames added in this walk\n  size_t numFrames{0};\n  // Normal stack frame to start the next normal stack walk\n  StackFrame* normalStackFrame{nullptr};\n  StackFrame* normalStackFrameStop{nullptr};\n  // Async stack frame to start the next async stack walk after the next\n  // normal stack walk\n  AsyncStackFrame* asyncStackFrame{nullptr};\n};\n\nWalkAsyncStackResult walkAsyncStack(\n    uintptr_t* addresses,\n    size_t maxAddresses,\n    AsyncStackFrame* asyncStackFrame) {\n  WalkAsyncStackResult result;\n  while (result.numFrames < maxAddresses && asyncStackFrame != nullptr) {\n    addresses[result.numFrames++] =\n        reinterpret_cast<std::uintptr_t>(asyncStackFrame->getReturnAddress());\n\n    auto* asyncStackFrameNext = asyncStackFrame->getParentFrame();\n    if (asyncStackFrameNext == nullptr) {\n      // Reached end of async-stack.\n      // Check if there is an AsyncStackRoot and if so, whether there\n      // is an associated stack frame that indicates the normal stack\n      // frame we should continue walking at.\n      const auto* asyncStackRoot = asyncStackFrame->getStackRoot();\n      if (asyncStackRoot == nullptr) {\n        // This is a detached async stack. We are done\n        break;\n      }\n\n      // Get the normal stack frame holding this async root.\n      result.normalStackFrame =\n          reinterpret_cast<StackFrame*>(asyncStackRoot->getStackFramePointer());\n      if (result.normalStackFrame == nullptr) {\n        // No associated normal stack frame for this async stack root.\n        // This means we should treat this as a top-level/detached\n        // stack and not try to walk any further.\n        break;\n      }\n      // Skip to the parent stack-frame pointer\n      result.normalStackFrame = result.normalStackFrame->parentFrame;\n\n      // Check if there is a higher-level AsyncStackRoot that defines\n      // the stop point we should stop walking normal stack frames at.\n      // If there is no higher stack root then we will walk to the\n      // top of the normal stack (normalStackFrameStop == nullptr).\n      // Otherwise we record the frame pointer that we should stop\n      // at and walk normal stack frames until we hit that frame.\n      // Also get the async stack frame where the next async stack walk\n      // should begin after the next normal stack walk finishes.\n      asyncStackRoot = asyncStackRoot->getNextRoot();\n      if (asyncStackRoot != nullptr) {\n        result.normalStackFrameStop = reinterpret_cast<StackFrame*>(\n            asyncStackRoot->getStackFramePointer());\n        result.asyncStackFrame = asyncStackRoot->getTopFrame();\n      }\n    }\n    asyncStackFrame = asyncStackFrameNext;\n  }\n  return result;\n}\n} // namespace\n\nFOLLY_NOINLINE ssize_t\ngetAsyncStackTraceSafe(uintptr_t* addresses, size_t maxAddresses) {\n  size_t numFrames = 0;\n  const auto* asyncStackRoot = tryGetCurrentAsyncStackRoot();\n  if (asyncStackRoot == nullptr) {\n    // No async operation in progress. Return empty stack\n    return numFrames;\n  }\n\n  // Start by walking the normal stack until we get to the frame right before\n  // the frame that holds the async root.\n  auto* normalStackFrame =\n      reinterpret_cast<StackFrame*>(FOLLY_ASYNC_STACK_FRAME_POINTER());\n  auto* normalStackFrameStop =\n      reinterpret_cast<StackFrame*>(asyncStackRoot->getStackFramePointer());\n  if (numFrames < maxAddresses) {\n    addresses[numFrames++] =\n        reinterpret_cast<std::uintptr_t>(FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  }\n  auto* asyncStackFrame = asyncStackRoot->getTopFrame();\n\n  while (numFrames < maxAddresses &&\n         (normalStackFrame != nullptr || asyncStackFrame != nullptr)) {\n    numFrames += walkNormalStack(\n        addresses + numFrames,\n        maxAddresses - numFrames,\n        normalStackFrame,\n        normalStackFrameStop);\n\n    auto walkAsyncStackResult = walkAsyncStack(\n        addresses + numFrames, maxAddresses - numFrames, asyncStackFrame);\n    numFrames += walkAsyncStackResult.numFrames;\n    normalStackFrame = walkAsyncStackResult.normalStackFrame;\n    normalStackFrameStop = walkAsyncStackResult.normalStackFrameStop;\n    asyncStackFrame = walkAsyncStackResult.asyncStackFrame;\n  }\n  return numFrames;\n}\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/StackTrace.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n#include <cstdint>\n#include <cstdlib>\n\n#include <folly/CPortability.h>\n#include <folly/portability/SysTypes.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n/**\n * Get the current stack trace into addresses, which has room for at least\n * maxAddresses frames.\n *\n * Returns the number of frames written in the array.\n * Returns -1 on failure.\n *\n * NOT async-signal-safe, but fast.\n */\nssize_t getStackTrace(uintptr_t* addresses, size_t maxAddresses);\n\n/**\n * Get the current stack trace into addresses, which has room for at least\n * maxAddresses frames.\n *\n * Returns the number of frames written in the array.\n * Returns -1 on failure.\n *\n * Async-signal-safe, but likely slower.\n */\nssize_t getStackTraceSafe(uintptr_t* addresses, size_t maxAddresses);\n\n/**\n * Get the current stack trace into addresses, which has room for at least\n * maxAddresses frames.\n *\n * Returns the number of frames written in the array.\n * Returns -1 on failure.\n *\n * Heap allocates its context. Likely slower than getStackTrace but\n * avoids large stack allocations.\n */\nssize_t getStackTraceHeap(uintptr_t* addresses, size_t maxAddresses);\n\n/**\n * Get the current async stack trace into addresses, which has room for at least\n * maxAddresses frames. If no async operation is progress, then this will\n * write 0 frames.\n *\n * This will include both async and non-async frames. For example, the stack\n * trace could look something like this:\n *\n * funcD     <--  non-async, current top of stack\n * funcC     <--  non-async\n * co_funcB  <--  async\n * co_funcA  <--  async\n * main      <--  non-async, root of async stack\n *\n * Returns the number of frames written in the array.\n * Returns -1 on failure.\n *\n * Async-signal-safe, but likely slower.\n */\nFOLLY_NOINLINE ssize_t\ngetAsyncStackTraceSafe(uintptr_t* addresses, size_t maxAddresses);\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/SymbolizePrinter.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/SymbolizePrinter.h>\n\n#include <folly/Demangle.h>\n#include <folly/FileUtil.h>\n#include <folly/ScopeGuard.h>\n#include <folly/io/IOBuf.h>\n#include <folly/lang/ToAscii.h>\n\n#ifdef __GLIBCXX__\n#include <ext/stdio_filebuf.h>\n#include <ext/stdio_sync_filebuf.h>\n#endif\n\nnamespace folly {\nnamespace symbolizer {\n\nnamespace {\nconstexpr char kHexChars[] = \"0123456789abcdef\";\nconstexpr auto kAddressColor = SymbolizePrinter::Color::Blue;\nconstexpr auto kFunctionColor = SymbolizePrinter::Color::Purple;\nconstexpr auto kFileColor = SymbolizePrinter::Color::Default;\n\n#ifdef _WIN32\nconstexpr size_t kPathMax = 4096;\n#else\nconstexpr size_t kPathMax = PATH_MAX;\n#endif\n} // namespace\n\nAddressFormatter::AddressFormatter() {\n  memcpy(buf_, bufTemplate, sizeof(buf_));\n}\n\nfolly::StringPiece AddressFormatter::format(uintptr_t address) {\n  // Can't use sprintf, not async-signal-safe\n  static_assert(sizeof(uintptr_t) <= 8, \"huge uintptr_t?\");\n  char* end = buf_ + sizeof(buf_) - 1 - (16 - 2 * sizeof(uintptr_t));\n  char* p = end;\n  *p-- = '\\0';\n  while (address != 0) {\n    *p-- = kHexChars[address & 0xf];\n    address >>= 4;\n  }\n\n  return folly::StringPiece(buf_, end);\n}\n\nvoid SymbolizePrinter::print(const SymbolizedFrame& frame) {\n  if (options_ & TERSE) {\n    printTerse(frame);\n    return;\n  }\n\n  SCOPE_EXIT {\n    color(Color::Default);\n  };\n\n  if (!(options_ & NO_FRAME_ADDRESS) && !(options_ & TERSE_FILE_AND_LINE)) {\n    color(kAddressColor);\n\n    AddressFormatter formatter;\n    doPrint(formatter.format(frame.addr));\n  }\n\n  const char padBuf[] = \"                       \";\n  folly::StringPiece pad(\n      padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));\n\n  color(kFunctionColor);\n  if (!frame.found) {\n    doPrint(\" (not found)\");\n    return;\n  }\n\n  if (!(options_ & TERSE_FILE_AND_LINE)) {\n    if (!frame.name || frame.name[0] == '\\0') {\n      doPrint(\" (unknown)\");\n    } else {\n      char demangledBuf[2048];\n      folly::demangle(frame.name, demangledBuf, sizeof(demangledBuf));\n      doPrint(\" \");\n      doPrint(demangledBuf[0] == '\\0' ? frame.name : demangledBuf);\n    }\n  }\n\n  if (!(options_ & NO_FILE_AND_LINE)) {\n    color(kFileColor);\n    char fileBuf[kPathMax];\n    fileBuf[0] = '\\0';\n    if (frame.location.hasFileAndLine) {\n      frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));\n      if (!(options_ & TERSE_FILE_AND_LINE)) {\n        doPrint(\"\\n\");\n        doPrint(pad);\n      }\n      doPrint(fileBuf);\n\n      char buf[to_ascii_size_max_decimal<decltype(frame.location.line)>];\n      const auto n = to_ascii_decimal(buf, frame.location.line);\n      doPrint(\":\");\n      doPrint(StringPiece(buf, n));\n    } else {\n      if ((options_ & TERSE_FILE_AND_LINE)) {\n        doPrint(\"(unknown)\");\n      }\n    }\n\n    if (frame.location.hasMainFile && !(options_ & TERSE_FILE_AND_LINE)) {\n      char mainFileBuf[kPathMax];\n      mainFileBuf[0] = '\\0';\n      frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));\n      if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {\n        doPrint(\"\\n\");\n        doPrint(pad);\n        doPrint(\"-> \");\n        doPrint(mainFileBuf);\n      }\n    }\n  }\n}\n\nvoid SymbolizePrinter::color(SymbolizePrinter::Color color) {\n  if ((options_ & COLOR) == 0 && ((options_ & COLOR_IF_TTY) == 0 || !isTty_)) {\n    return;\n  }\n  if (static_cast<size_t>(color) >= kColorMap.size()) { // catches underflow too\n    return;\n  }\n  doPrint(kColorMap[color]);\n}\n\nvoid SymbolizePrinter::println(const SymbolizedFrame& frame) {\n  print(frame);\n  doPrint(\"\\n\");\n}\n\nvoid SymbolizePrinter::printTerse(const SymbolizedFrame& frame) {\n  if (frame.found && frame.name && frame.name[0] != '\\0') {\n    char demangledBuf[2048] = {0};\n    demangle(frame.name, demangledBuf, sizeof(demangledBuf));\n    doPrint(demangledBuf[0] == '\\0' ? frame.name : demangledBuf);\n  } else {\n    // Can't use sprintf, not async-signal-safe\n    static_assert(sizeof(uintptr_t) <= 8, \"huge uintptr_t?\");\n    char buf[] = \"0x0000000000000000\";\n    char* end = buf + sizeof(buf) - 1 - (16 - 2 * sizeof(uintptr_t));\n    char* p = end;\n    *p-- = '\\0';\n    auto address = frame.addr;\n    while (address != 0) {\n      *p-- = kHexChars[address & 0xf];\n      address >>= 4;\n    }\n    doPrint(StringPiece(buf, end));\n  }\n}\n\nvoid SymbolizePrinter::println(\n    const SymbolizedFrame* frames, size_t frameCount) {\n  for (size_t i = 0; i < frameCount; ++i) {\n    println(frames[i]);\n  }\n}\n\nnamespace {\n\nint getFD(const std::ios& stream) {\n#if defined(__GLIBCXX__) && FOLLY_HAS_RTTI\n  std::streambuf* buf = stream.rdbuf();\n  using namespace __gnu_cxx;\n\n  {\n    auto sbuf = dynamic_cast<stdio_sync_filebuf<char>*>(buf);\n    if (sbuf) {\n      return fileno(sbuf->file());\n    }\n  }\n  {\n    auto sbuf = dynamic_cast<stdio_filebuf<char>*>(buf);\n    if (sbuf) {\n      return sbuf->fd();\n    }\n  }\n#else\n  (void)stream;\n#endif\n  return -1;\n}\n\nbool isColorfulTty(int options, int fd) {\n  if ((options & SymbolizePrinter::TERSE) != 0 ||\n      (options & SymbolizePrinter::COLOR_IF_TTY) == 0 || fd < 0 ||\n      !::isatty(fd)) {\n    return false;\n  }\n  auto term = ::getenv(\"TERM\");\n  return !(term == nullptr || term[0] == '\\0' || strcmp(term, \"dumb\") == 0);\n}\n\n} // namespace\n\nOStreamSymbolizePrinter::OStreamSymbolizePrinter(std::ostream& out, int options)\n    : SymbolizePrinter(options, isColorfulTty(options, getFD(out))),\n      out_(out) {}\n\nvoid OStreamSymbolizePrinter::doPrint(StringPiece sp) {\n  out_ << sp;\n}\n\nFDSymbolizePrinter::FDSymbolizePrinter(int fd, int options, size_t bufferSize)\n    : SymbolizePrinter(options, isColorfulTty(options, fd)),\n      fd_(fd),\n      buffer_(bufferSize ? IOBuf::create(bufferSize) : nullptr) {}\n\nFDSymbolizePrinter::~FDSymbolizePrinter() {\n  flush();\n}\n\nvoid FDSymbolizePrinter::doPrint(StringPiece sp) {\n  if (buffer_) {\n    if (sp.size() > buffer_->tailroom()) {\n      flush();\n      writeFull(fd_, sp.data(), sp.size());\n    } else {\n      memcpy(buffer_->writableTail(), sp.data(), sp.size());\n      buffer_->append(sp.size());\n    }\n  } else {\n    writeFull(fd_, sp.data(), sp.size());\n  }\n}\n\nvoid FDSymbolizePrinter::flush() {\n  if (buffer_ && !buffer_->empty()) {\n    writeFull(fd_, buffer_->data(), buffer_->length());\n    buffer_->clear();\n  }\n}\n\nFILESymbolizePrinter::FILESymbolizePrinter(FILE* file, int options)\n    : SymbolizePrinter(options, isColorfulTty(options, fileno(file))),\n      file_(file) {}\n\nvoid FILESymbolizePrinter::doPrint(StringPiece sp) {\n  fwrite(sp.data(), 1, sp.size(), file_);\n}\n\nvoid StringSymbolizePrinter::doPrint(StringPiece sp) {\n  buf_.append(sp.data(), sp.size());\n}\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/SymbolizePrinter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n\n#include <folly/FBString.h>\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n\nnamespace folly {\nclass IOBuf;\n\nnamespace symbolizer {\n\n/**\n * Format one address in the way it's usually printed by SymbolizePrinter.\n * Async-signal-safe.\n */\nclass AddressFormatter {\n public:\n  AddressFormatter();\n\n  /**\n   * Format the address. Returns an internal buffer.\n   */\n  StringPiece format(uintptr_t address);\n\n private:\n  static constexpr char bufTemplate[] = \"    @ 0000000000000000\";\n  char buf_[sizeof(bufTemplate)];\n};\n\n/**\n * Print a list of symbolized addresses. Base class.\n */\nclass SymbolizePrinter {\n public:\n  /**\n   * Print one frame, no ending newline.\n   */\n  void print(const SymbolizedFrame& frame);\n\n  /**\n   * Print one frame with ending newline.\n   */\n  void println(const SymbolizedFrame& frame);\n\n  /**\n   * Print multiple frames on separate lines.\n   */\n  void println(const SymbolizedFrame* frames, size_t frameCount);\n\n  /**\n   * Print a string, no endling newline.\n   */\n  void print(StringPiece sp) { doPrint(sp); }\n\n  /**\n   * Print multiple frames on separate lines, skipping the first\n   * skip addresses.\n   */\n  template <size_t N>\n  void println(const FrameArray<N>& fa, size_t skip = 0) {\n    if (skip < fa.frameCount) {\n      println(fa.frames + skip, fa.frameCount - skip);\n    }\n  }\n\n  /**\n   * If output buffered inside this class, send it to the output stream, so that\n   * any output done in other ways appears after this.\n   */\n  virtual void flush() {}\n\n  virtual ~SymbolizePrinter() {}\n\n  enum Options {\n    // Skip file and line information\n    NO_FILE_AND_LINE = 1 << 0,\n\n    // As terse as it gets: function name if found, address otherwise\n    TERSE = 1 << 1,\n\n    // Always colorize output (ANSI escape code)\n    COLOR = 1 << 2,\n\n    // Colorize output only if output is printed to a TTY (ANSI escape code)\n    COLOR_IF_TTY = 1 << 3,\n\n    // Skip frame address information\n    NO_FRAME_ADDRESS = 1 << 4,\n\n    // Simple file and line output\n    TERSE_FILE_AND_LINE = 1 << 5,\n  };\n\n  // NOTE: enum values used as indexes in kColorMap.\n  enum Color { Default, Red, Green, Yellow, Blue, Cyan, White, Purple, Num };\n  void color(Color c);\n\n protected:\n  explicit SymbolizePrinter(int options, bool isTty = false)\n      : options_(options), isTty_(isTty) {}\n\n  const int options_;\n  const bool isTty_;\n\n private:\n  void printTerse(const SymbolizedFrame& frame);\n  virtual void doPrint(StringPiece sp) = 0;\n\n  static constexpr std::array<const char*, Color::Num> kColorMap = {{\n      \"\\x1B[0m\",\n      \"\\x1B[31m\",\n      \"\\x1B[32m\",\n      \"\\x1B[33m\",\n      \"\\x1B[34m\",\n      \"\\x1B[36m\",\n      \"\\x1B[37m\",\n      \"\\x1B[35m\",\n  }};\n};\n\n/**\n * Print a list of symbolized addresses to a stream.\n * Not reentrant. Do not use from signal handling code.\n */\nclass OStreamSymbolizePrinter : public SymbolizePrinter {\n public:\n  explicit OStreamSymbolizePrinter(std::ostream& out, int options = 0);\n\n private:\n  void doPrint(StringPiece sp) override;\n  std::ostream& out_;\n};\n\n/**\n * Print a list of symbolized addresses to a file descriptor.\n * Ignores errors. Async-signal-safe.\n */\nclass FDSymbolizePrinter : public SymbolizePrinter {\n public:\n  explicit FDSymbolizePrinter(int fd, int options = 0, size_t bufferSize = 0);\n  ~FDSymbolizePrinter() override;\n  virtual void flush() override;\n\n private:\n  void doPrint(StringPiece sp) override;\n\n  const int fd_;\n  std::unique_ptr<IOBuf> buffer_;\n};\n\n/**\n * Print a list of symbolized addresses to a FILE*.\n * Ignores errors. Not reentrant. Do not use from signal handling code.\n */\nclass FILESymbolizePrinter : public SymbolizePrinter {\n public:\n  explicit FILESymbolizePrinter(FILE* file, int options = 0);\n\n private:\n  void doPrint(StringPiece sp) override;\n  FILE* const file_ = nullptr;\n};\n\n/**\n * Print a list of symbolized addresses to a std::string.\n * Not reentrant. Do not use from signal handling code.\n */\nclass StringSymbolizePrinter : public SymbolizePrinter {\n public:\n  explicit StringSymbolizePrinter(int options = 0)\n      : SymbolizePrinter(options) {}\n\n  std::string str() const { return buf_.toStdString(); }\n  const fbstring& fbstr() const { return buf_; }\n  fbstring moveFbString() { return std::move(buf_); }\n\n private:\n  void doPrint(StringPiece sp) override;\n  fbstring buf_;\n};\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/SymbolizedFrame.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n\nnamespace folly {\nnamespace symbolizer {\n\nPath::Path(StringPiece baseDir, StringPiece subDir, StringPiece file)\n    : baseDir_(baseDir), subDir_(subDir), file_(file) {\n  using std::swap;\n\n  // Normalize\n  if (file_.empty()) {\n    baseDir_.clear();\n    subDir_.clear();\n    return;\n  }\n\n  if (file_[0] == '/') {\n    // file_ is absolute\n    baseDir_.clear();\n    subDir_.clear();\n  }\n\n  if (!subDir_.empty() && subDir_[0] == '/') {\n    baseDir_.clear(); // subDir_ is absolute\n  }\n\n  // Make sure it's never the case that baseDir_ is empty, but subDir_ isn't.\n  if (baseDir_.empty()) {\n    swap(baseDir_, subDir_);\n  }\n}\n\nsize_t Path::size() const {\n  size_t size = 0;\n  bool needsSlash = false;\n\n  if (!baseDir_.empty()) {\n    size += baseDir_.size();\n    needsSlash = !baseDir_.endsWith('/');\n  }\n\n  if (!subDir_.empty()) {\n    size += needsSlash;\n    size += subDir_.size();\n    needsSlash = !subDir_.endsWith('/');\n  }\n\n  if (!file_.empty()) {\n    size += needsSlash;\n    size += file_.size();\n  }\n\n  return size;\n}\n\nsize_t Path::toBuffer(char* buf, size_t bufSize) const {\n  size_t totalSize = 0;\n  bool needsSlash = false;\n\n  auto append = [&](StringPiece sp) {\n    if (bufSize >= 2) {\n      size_t toCopy = std::min(sp.size(), bufSize - 1);\n      memcpy(buf, sp.data(), toCopy);\n      buf += toCopy;\n      bufSize -= toCopy;\n    }\n    totalSize += sp.size();\n  };\n\n  if (!baseDir_.empty()) {\n    append(baseDir_);\n    needsSlash = !baseDir_.endsWith('/');\n  }\n  if (!subDir_.empty()) {\n    if (needsSlash) {\n      append(\"/\");\n    }\n    append(subDir_);\n    needsSlash = !subDir_.endsWith('/');\n  }\n  if (!file_.empty()) {\n    if (needsSlash) {\n      append(\"/\");\n    }\n    append(file_);\n  }\n  if (bufSize) {\n    *buf = '\\0';\n  }\n  assert(totalSize == size());\n  return totalSize;\n}\n\nvoid Path::toString(std::string& dest) const {\n  size_t initialSize = dest.size();\n  dest.reserve(initialSize + size());\n  if (!baseDir_.empty()) {\n    dest.append(baseDir_.begin(), baseDir_.end());\n  }\n  if (!subDir_.empty()) {\n    if (!dest.empty() && dest.back() != '/') {\n      dest.push_back('/');\n    }\n    dest.append(subDir_.begin(), subDir_.end());\n  }\n  if (!file_.empty()) {\n    if (!dest.empty() && dest.back() != '/') {\n      dest.push_back('/');\n    }\n    dest.append(file_.begin(), file_.end());\n  }\n  assert(dest.size() == initialSize + size());\n}\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/SymbolizedFrame.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <memory>\n#include <ostream>\n#include <string>\n\n#include <folly/Range.h>\n\nnamespace folly {\nnamespace symbolizer {\n\nclass ElfFile;\n\n/**\n * Represent a file path as a collection of three parts (base directory,\n * subdirectory, and file).\n */\nclass Path {\n public:\n  Path() = default;\n\n  Path(\n      folly::StringPiece baseDir,\n      folly::StringPiece subDir,\n      folly::StringPiece file);\n\n  folly::StringPiece baseDir() const { return baseDir_; }\n  folly::StringPiece subDir() const { return subDir_; }\n  folly::StringPiece file() const { return file_; }\n\n  size_t size() const;\n\n  /**\n   * Copy the Path to a buffer of size bufSize.\n   *\n   * toBuffer behaves like snprintf: It will always null-terminate the\n   * buffer (so it will copy at most bufSize-1 bytes), and it will return\n   * the number of bytes that would have been written if there had been\n   * enough room, so, if toBuffer returns a value >= bufSize, the output\n   * was truncated.\n   */\n  size_t toBuffer(char* buf, size_t bufSize) const;\n\n  void toString(std::string& dest) const;\n  std::string toString() const {\n    std::string s;\n    toString(s);\n    return s;\n  }\n\n private:\n  folly::StringPiece baseDir_;\n  folly::StringPiece subDir_;\n  folly::StringPiece file_;\n};\n\ninline std::ostream& operator<<(std::ostream& out, const Path& path) {\n  return out << path.toString();\n}\n\nenum class LocationInfoMode {\n  // Don't resolve location info.\n  DISABLED,\n  // Perform CU lookup using .debug_aranges (might be incomplete).\n  FAST,\n  // Scan all CU in .debug_info (slow!) on .debug_aranges lookup failure.\n  FULL,\n  // Scan .debug_info (super slower, use with caution) for inline functions in\n  // addition to FULL.\n  FULL_WITH_INLINE,\n};\n\n/**\n * Contains location info like file name, line number, etc.\n */\nstruct LocationInfo {\n  bool hasFileAndLine = false;\n  bool hasMainFile = false;\n  Path mainFile;\n  Path file;\n  uint64_t line = 0;\n};\n\n/**\n * Frame information: symbol name and location.\n */\nstruct SymbolizedFrame {\n  bool found = false;\n  uintptr_t addr = 0;\n  // Mangled symbol name. Use `folly::demangle()` to demangle it.\n  const char* name = nullptr;\n  LocationInfo location;\n  std::shared_ptr<ElfFile> file;\n\n  void clear() { *this = SymbolizedFrame(); }\n};\n\ntemplate <size_t N>\nstruct FrameArray {\n  FrameArray() = default;\n\n  size_t frameCount = 0;\n  uintptr_t addresses[N];\n  SymbolizedFrame frames[N];\n};\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/Symbolizer.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Symbolizer.h>\n\n#include <cstdlib>\n\n#include <folly/FileUtil.h>\n#include <folly/Memory.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Synchronized.h>\n#include <folly/container/EvictingCacheMap.h>\n#include <folly/debugging/symbolizer/Dwarf.h>\n#include <folly/debugging/symbolizer/Elf.h>\n#include <folly/debugging/symbolizer/ElfCache.h>\n#include <folly/debugging/symbolizer/detail/Debug.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/lang/ToAscii.h>\n#include <folly/memory/SanitizeAddress.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/Unistd.h>\n#include <folly/tracing/AsyncStack.h>\n\n#if FOLLY_HAVE_SWAPCONTEXT\n// folly/portability/Config.h (thus features.h) must be included\n// first, and _XOPEN_SOURCE must be defined to unable the context\n// functions on macOS.\n#ifndef _XOPEN_SOURCE\n#define _XOPEN_SOURCE\n#endif\n#include <ucontext.h>\n#endif\n\n#if FOLLY_HAVE_BACKTRACE\n#include <execinfo.h>\n#endif\n\n#ifdef __roar__\nextern \"C\" char* _roar_upcall_symbolizeAddress(\n    void* Address, unsigned* NumFrames, bool WithSrcLine, bool WithInline);\n#endif\n\nnamespace folly {\nnamespace symbolizer {\n\nnamespace {\ntemplate <typename PrintFunc>\nvoid printAsyncStackInfo(PrintFunc print) {\n  char buf[to_ascii_size_max<16, uint64_t>];\n  auto printHex = [&print, &buf](uint64_t val) {\n    print(\"0x\");\n    print(StringPiece(buf, to_ascii_lower<16>(buf, val)));\n  };\n\n  // Print async stack trace, if available\n  const auto* asyncStackRoot = tryGetCurrentAsyncStackRoot();\n  const auto* asyncStackFrame =\n      asyncStackRoot ? asyncStackRoot->getTopFrame() : nullptr;\n\n  print(\"\\n\");\n  print(\"*** Check failure async stack trace: ***\\n\");\n  print(\"*** First async stack root: \");\n  printHex((uint64_t)asyncStackRoot);\n  print(\", normal stack frame pointer holding async stack root: \");\n  printHex(\n      asyncStackRoot ? (uint64_t)asyncStackRoot->getStackFramePointer() : 0);\n  print(\", return address: \");\n  printHex(asyncStackRoot ? (uint64_t)asyncStackRoot->getReturnAddress() : 0);\n  print(\" ***\\n\");\n  print(\"*** First async stack frame pointer: \");\n  printHex((uint64_t)asyncStackFrame);\n  print(\", return address: \");\n  printHex(asyncStackFrame ? (uint64_t)asyncStackFrame->getReturnAddress() : 0);\n  print(\", async stack trace: ***\\n\");\n}\n\n} // namespace\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nnamespace {\n\ntemplate <size_t N>\nvoid symbolizeAndPrint(\n    const std::unique_ptr<SymbolizePrinter>& printer,\n    Symbolizer& symbolizer,\n    FrameArray<N>& addresses,\n    bool symbolize) {\n  if (symbolize) {\n    symbolizer.symbolize(addresses);\n\n    // Skip the top 2 frames:\n    printer->println(addresses, 2);\n  } else {\n    printer->print(\"(safe mode, symbolizer not available)\\n\");\n    AddressFormatter formatter;\n    for (size_t i = 0; i < addresses.frameCount; ++i) {\n      printer->print(formatter.format(addresses.addresses[i]));\n      printer->print(\"\\n\");\n    }\n  }\n}\n\nElfCache* defaultElfCache() {\n  static auto cache = new ElfCache();\n  return cache;\n}\n\n#ifdef __roar__\nbool setROARSymbolizedFrame(\n    SymbolizedFrame& frame,\n    uintptr_t address,\n    LocationInfoMode mode,\n    folly::Range<SymbolizedFrame*> extraInlineFrames = {}) {\n  const bool withSrcLine = mode != LocationInfoMode::DISABLED;\n  const bool withInline = mode == LocationInfoMode::FULL_WITH_INLINE;\n  unsigned numFrames = 0;\n  char* jitNames = _roar_upcall_symbolizeAddress(\n      reinterpret_cast<void*>(address), &numFrames, withSrcLine, withInline);\n  if (numFrames == 0)\n    return false;\n  unsigned firstFrame =\n      numFrames - std::min<unsigned>(numFrames, extraInlineFrames.size() + 1);\n  unsigned i = 0;\n  for (unsigned curFrame = 0; curFrame < numFrames; ++curFrame) {\n    std::string_view name = std::string_view(jitNames);\n    jitNames += name.size() + 1;\n    std::string_view fileName = std::string_view(jitNames);\n    jitNames += fileName.size() + 1;\n    std::string_view lineNo = std::string_view(jitNames);\n    jitNames += lineNo.size() + 1;\n    if (curFrame < firstFrame) {\n      continue;\n    }\n    if (curFrame == numFrames - 1) {\n      frame.name = name.data();\n      frame.location.hasFileAndLine = withSrcLine && !fileName.empty();\n      frame.location.file = Path({}, {}, fileName);\n      frame.location.line = atoi(lineNo.data());\n      break;\n    }\n    extraInlineFrames[i].found = true;\n    extraInlineFrames[i].addr = address;\n    extraInlineFrames[i].name = name.data();\n    extraInlineFrames[i].location.hasFileAndLine =\n        withSrcLine && !fileName.empty();\n    extraInlineFrames[i].location.file = Path({}, {}, fileName);\n    extraInlineFrames[i].location.line = atoi(lineNo.data());\n    ++i;\n  }\n  return true;\n}\n\n#endif\n\nvoid setSymbolizedFrame(\n    ElfCacheBase* const elfCache,\n    SymbolizedFrame& frame,\n    const std::shared_ptr<ElfFile>& file,\n    uintptr_t address,\n    LocationInfoMode mode,\n    folly::Range<SymbolizedFrame*> extraInlineFrames = {}) {\n  frame.clear();\n  frame.found = true;\n  frame.addr = address;\n  frame.file = file;\n#ifdef __roar__\n  if (setROARSymbolizedFrame(frame, address, mode, extraInlineFrames)) {\n    return;\n  }\n#endif\n  frame.name = file->getSymbolName(file->getDefinitionByAddress(address));\n\n  Dwarf(elfCache, file.get())\n      .findAddress(address, mode, frame, extraInlineFrames);\n}\n\n// SymbolCache contains mapping between an address and its frames. The first\n// frame is the normal function call, and the following are stacked inline\n// function calls if any.\nusing CachedSymbolizedFrames =\n    std::array<SymbolizedFrame, 1 + kMaxInlineLocationInfoPerFrame>;\n\nusing UnsyncSymbolCache = EvictingCacheMap<uintptr_t, CachedSymbolizedFrames>;\n\n/**\n * @param instructionAddr The address of an instruction after it has been\n * adjusted by the linker's `l_addr`.\n * @return true if the given address is contained in an executable segment of\n * `elfFile`.\n */\nbool containedInExecutableSegment(\n    const ElfFile& elfFile, ElfAddr instructionAddr) {\n  return elfFile.iterateProgramHeaders([&](const ElfPhdr& sh) {\n    bool executable = sh.p_flags & PF_X;\n    bool loadable = sh.p_type == PT_LOAD;\n    if (!(executable && loadable)) {\n      return false;\n    }\n    return sh.p_vaddr <= instructionAddr &&\n        instructionAddr < (sh.p_vaddr + sh.p_memsz);\n  });\n}\n\n} // namespace\n\nstruct Symbolizer::SymbolCache : public Synchronized<UnsyncSymbolCache> {\n  using Super = Synchronized<UnsyncSymbolCache>;\n  using Super::Super;\n};\n\nbool Symbolizer::isAvailable() {\n  return detail::get_r_debug();\n}\n\nSymbolizer::Symbolizer(\n    ElfCacheBase* cache,\n    LocationInfoMode mode,\n    size_t symbolCacheSize,\n    std::string exePath)\n    : cache_(cache ? cache : defaultElfCache()),\n      mode_(mode),\n      exePath_(std::move(exePath)) {\n  if (symbolCacheSize > 0) {\n    symbolCache_ =\n        std::make_unique<SymbolCache>(UnsyncSymbolCache{symbolCacheSize});\n  }\n}\n\n// Needs complete type for SymbolCache\nSymbolizer::~Symbolizer() {}\n\nsize_t Symbolizer::symbolize(\n    folly::Range<const uintptr_t*> addrs,\n    folly::Range<SymbolizedFrame*> frames) {\n  size_t addrCount = addrs.size();\n  size_t frameCount = frames.size();\n  if (addrCount > frameCount) {\n    FOLLY_SAFE_DFATAL(\n        \"Not enough frames: addrCount: \",\n        addrCount,\n        \" frameCount: \",\n        frameCount);\n    return 0;\n  }\n  size_t remaining = addrCount;\n\n  auto const dbg = detail::get_r_debug();\n  if (dbg == nullptr) {\n    return 0;\n  }\n  if (dbg->r_version != 1) {\n    return 0;\n  }\n\n  char selfPath[PATH_MAX + 8];\n  ssize_t selfSize;\n  if ((selfSize = readlink(exePath_.c_str(), selfPath, PATH_MAX + 1)) == -1) {\n    // Something has gone terribly wrong.\n    return 0;\n  }\n  selfPath[selfSize] = '\\0';\n\n  // If we call symbolize on the same range of frames twice, results are\n  // slightly different. This is happening because we copy over the addresses\n  // again but in the second pass we don't hit the code for adjusting the\n  // address anymore. Therefore skipping copying over the addresses again if\n  // frames are already filled.\n  for (size_t i = 0; i < addrCount; i++) {\n    if (frames[i].found) {\n      continue;\n    }\n    frames[i].addr = addrs[i];\n  }\n\n  // Find out how many frames were filled in.\n  auto countFrames = [](folly::Range<SymbolizedFrame*> framesRange) {\n    return std::distance(\n        framesRange.begin(),\n        std::find_if(framesRange.begin(), framesRange.end(), [&](auto frame) {\n          return !frame.found;\n        }));\n  };\n\n  for (auto lmap = dbg->r_map; lmap != nullptr && remaining != 0;\n       lmap = lmap->l_next) {\n    // The empty string is used in place of the filename for the link_map\n    // corresponding to the running executable.  Additionally, the `l_addr' is\n    // 0 and the link_map appears to be first in the list---but none of this\n    // behavior appears to be documented, so checking for the empty string is\n    // as good as anything.\n    auto const objPath = lmap->l_name[0] != '\\0' ? lmap->l_name : selfPath;\n    auto const elfFile = cache_->getFile(objPath);\n    if (!elfFile) {\n      continue;\n    }\n\n    for (size_t i = 0; i < addrCount && remaining != 0; ++i) {\n      auto& frame = frames[i];\n      if (frame.found) {\n        continue;\n      }\n\n      auto const addr = frame.addr;\n      if (symbolCache_) {\n        // Need a write lock, because EvictingCacheMap brings found item to\n        // front of eviction list.\n        auto lockedSymbolCache = symbolCache_->wlock();\n\n        auto const iter = lockedSymbolCache->find(addr);\n        if (iter != lockedSymbolCache->end()) {\n          size_t numCachedFrames = countFrames(folly::range(iter->second));\n          // 1 entry in cache is the non-inlined function call and that one\n          // already has space reserved at `frames[i]`\n          auto numInlineFrames = numCachedFrames - 1;\n          if (numInlineFrames <= frameCount - addrCount) {\n            // Move the rest of the frames to make space for inlined frames.\n            std::move_backward(\n                frames.begin() + i + 1,\n                frames.begin() + addrCount,\n                frames.begin() + addrCount + numInlineFrames);\n            // Overwrite frames[i] too (the non-inlined function call entry).\n            std::copy(\n                iter->second.begin(),\n                iter->second.begin() + numInlineFrames + 1,\n                frames.begin() + i);\n            i += numInlineFrames;\n            addrCount += numInlineFrames;\n          }\n          continue;\n        }\n      }\n\n      // Get the unrelocated, ELF-relative address by normalizing via the\n      // address at which the object is loaded.\n      auto const eaddr = static_cast<ElfAddr>(addr);\n      auto const maddr = lmap->l_addr;\n      auto const adjusted = eaddr < maddr ? ~ElfAddr(0) : eaddr - maddr;\n      size_t numInlined = 0;\n      if (containedInExecutableSegment(*elfFile, adjusted)) {\n        if (mode_ == LocationInfoMode::FULL_WITH_INLINE &&\n            frameCount > addrCount) {\n          size_t maxInline = std::min<size_t>(\n              kMaxInlineLocationInfoPerFrame, frameCount - addrCount);\n          // First use the trailing empty frames (index starting from addrCount)\n          // to get the inline call stack, then rotate these inline functions\n          // before the caller at `frame[i]`.\n          folly::Range<SymbolizedFrame*> inlineFrameRange(\n              frames.begin() + addrCount,\n              frames.begin() + addrCount + maxInline);\n          setSymbolizedFrame(\n              cache_, frame, elfFile, adjusted, mode_, inlineFrameRange);\n\n          numInlined = countFrames(inlineFrameRange);\n          // Rotate inline frames right before its caller frame.\n          std::rotate(\n              frames.begin() + i,\n              frames.begin() + addrCount,\n              frames.begin() + addrCount + numInlined);\n          addrCount += numInlined;\n        } else {\n          setSymbolizedFrame(cache_, frame, elfFile, adjusted, mode_);\n        }\n        --remaining;\n        if (symbolCache_) {\n          // frame may already have been set here.  That's ok, we'll just\n          // overwrite, which doesn't cause a correctness problem.\n          CachedSymbolizedFrames cacheFrames;\n          std::copy(\n              frames.begin() + i,\n              frames.begin() + i + std::min(numInlined + 1, cacheFrames.size()),\n              cacheFrames.begin());\n          symbolCache_->wlock()->set(addr, cacheFrames);\n        }\n        // Skip over the newly added inlined items.\n        i += numInlined;\n      }\n    }\n  }\n\n  return addrCount;\n}\n\nFastStackTracePrinter::FastStackTracePrinter(\n    std::unique_ptr<SymbolizePrinter> printer, size_t symbolCacheSize)\n    : printer_(std::move(printer)),\n      symbolizer_(defaultElfCache(), LocationInfoMode::FULL, symbolCacheSize) {}\n\nFastStackTracePrinter::~FastStackTracePrinter() = default;\n\nvoid FastStackTracePrinter::printStackTrace(bool symbolize) {\n  SCOPE_EXIT {\n    printer_->flush();\n  };\n\n  FrameArray<kMaxStackTraceDepth> addresses;\n\n  if (!getStackTraceSafe(addresses)) {\n    printer_->print(\"(error retrieving stack trace)\\n\");\n  } else {\n    symbolizeAndPrint(printer_, symbolizer_, addresses, symbolize);\n  }\n\n  addresses.frameCount = 0;\n  if (!getAsyncStackTraceSafe(addresses) || addresses.frameCount == 0) {\n    return;\n  }\n  printAsyncStackInfo([this](auto sp) { printer_->print(sp); });\n  symbolizeAndPrint(printer_, symbolizer_, addresses, symbolize);\n}\n\nvoid FastStackTracePrinter::flush() {\n  printer_->flush();\n}\n\nTwoStepFastStackTracePrinter::TwoStepFastStackTracePrinter(\n    std::unique_ptr<SymbolizePrinter> printer, size_t symbolCacheSize)\n    : printer_(std::move(printer)),\n      symbolizer_(defaultElfCache(), LocationInfoMode::FULL, symbolCacheSize) {\n  getStackTraceSafe(syncAddresses_);\n  getAsyncStackTraceSafe(asyncAddresses_);\n}\n\nvoid TwoStepFastStackTracePrinter::printStackTrace(bool symbolize) {\n  std::lock_guard lock(mutex_);\n  SCOPE_EXIT {\n    printer_->flush();\n  };\n\n  if (syncAddresses_.frameCount == 0) {\n    printer_->print(\"(error retrieving stack trace)\\n\");\n  } else {\n    symbolizeAndPrint(printer_, symbolizer_, syncAddresses_, symbolize);\n  }\n\n  if (asyncAddresses_.frameCount == 0) {\n    return;\n  }\n  printAsyncStackInfo([this](auto sp) { printer_->print(sp); });\n  symbolizeAndPrint(printer_, symbolizer_, asyncAddresses_, symbolize);\n}\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nSafeStackTracePrinter::SafeStackTracePrinter(int fd)\n    : fd_(fd),\n      printer_(\n          fd,\n          SymbolizePrinter::COLOR_IF_TTY,\n          size_t(64) << 10), // 64KiB\n      addresses_(std::make_unique<FrameArray<kMaxStackTraceDepth>>()) {}\n\nvoid SafeStackTracePrinter::flush() {\n  printer_.flush();\n  fsyncNoInt(fd_);\n}\n\nvoid SafeStackTracePrinter::printSymbolizedStackTrace() {\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n  // This function might run on an alternative stack allocated by\n  // UnsafeSelfAllocateStackTracePrinter. Capturing a stack from\n  // here is probably wrong.\n\n  // Do our best to populate location info, process is going to terminate,\n  // so performance isn't critical.\n  SignalSafeElfCache elfCache_;\n  Symbolizer symbolizer(&elfCache_, LocationInfoMode::FULL);\n  symbolizer.symbolize(*addresses_);\n\n  // Skip the top 2 frames captured by printStackTrace:\n  // getStackTraceSafe\n  // SafeStackTracePrinter::printStackTrace (captured stack)\n  //\n  // Leaving signalHandler on the stack for clarity, I think.\n  printer_.println(*addresses_, 2);\n#else\n  printUnsymbolizedStackTrace();\n#endif\n}\n\nvoid SafeStackTracePrinter::printUnsymbolizedStackTrace() {\n  print(\"(safe mode, symbolizer not available)\\n\");\n#if FOLLY_HAVE_BACKTRACE\n  // `backtrace_symbols_fd` from execinfo.h is not explicitly\n  // documented on either macOS or Linux to be async-signal-safe, but\n  // the implementation in\n  // https://opensource.apple.com/source/Libc/Libc-1353.60.8/ appears\n  // safe.\n  ::backtrace_symbols_fd(\n      reinterpret_cast<void**>(addresses_->addresses),\n      addresses_->frameCount,\n      fd_);\n#else\n  AddressFormatter formatter;\n  for (size_t i = 0; i < addresses_->frameCount; ++i) {\n    print(formatter.format(addresses_->addresses[i]));\n    print(\"\\n\");\n  }\n#endif\n}\n\nvoid SafeStackTracePrinter::printStackTrace(bool symbolize) {\n  SCOPE_EXIT {\n    flush();\n  };\n\n  // Skip the getStackTrace frame\n  if (!getStackTraceSafe(*addresses_)) {\n    print(\"(error retrieving stack trace)\\n\");\n  } else if (symbolize) {\n    printSymbolizedStackTrace();\n  } else {\n    printUnsymbolizedStackTrace();\n  }\n\n  addresses_->frameCount = 0;\n  if (!getAsyncStackTraceSafe(*addresses_) || addresses_->frameCount == 0) {\n    return;\n  }\n  printAsyncStackInfo([this](auto sp) { print(sp); });\n  if (symbolize) {\n    printSymbolizedStackTrace();\n  } else {\n    printUnsymbolizedStackTrace();\n  }\n}\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\nnamespace {\nconstexpr size_t kMaxStackTraceDepth = 100;\n\ntemplate <size_t N, typename StackTraceFunc>\nstd::string getStackTraceStrImpl(\n    StackTraceFunc func, bool showFullInfo = false, size_t skip = 0) {\n  FrameArray<N> addresses;\n\n  if (!func(addresses)) {\n    return \"\";\n  } else {\n    return detail::getStackTraceStr(\n        addresses.addresses,\n        addresses.frames,\n        addresses.frameCount,\n        showFullInfo,\n        skip);\n  }\n}\n} // namespace\n\nnamespace detail {\nstd::string getStackTraceStr(\n    const uintptr_t* addresses,\n    SymbolizedFrame* frames,\n    size_t frameCount,\n    bool showFullInfo,\n    size_t skip) {\n  if (frameCount == 0 || skip >= frameCount) {\n    return {};\n  }\n\n  ElfCache elfCache;\n  LocationInfoMode mode =\n      showFullInfo ? LocationInfoMode::FULL : LocationInfoMode::FAST;\n  Symbolizer symbolizer(&elfCache, mode);\n  symbolizer.symbolize(addresses, frames, frameCount);\n\n  StringSymbolizePrinter printer;\n  printer.println(frames + skip, frameCount - skip);\n  return printer.str();\n}\n} // namespace detail\n\nstd::string getStackTraceStr(bool showFullInfo, size_t skip) {\n  return getStackTraceStrImpl<kMaxStackTraceDepth>(\n      getStackTrace<kMaxStackTraceDepth>, showFullInfo, skip);\n}\n\nstd::string getAsyncStackTraceStr(size_t skip) {\n  return getStackTraceStrImpl<kMaxStackTraceDepth>(\n      getAsyncStackTraceSafe<kMaxStackTraceDepth>, skip);\n}\n\nstd::vector<std::string> getSuspendedStackTraces() {\n  std::vector<std::string> stacks;\n  sweepSuspendedLeafFrames([&](AsyncStackFrame* topFrame) {\n    stacks.emplace_back(\n        getStackTraceStrImpl<kMaxStackTraceDepth>([topFrame](auto& frameArray) {\n          return detail::fixFrameArray(\n              frameArray,\n              getAsyncStackTraceFromInitialFrame(\n                  topFrame, frameArray.addresses, kMaxStackTraceDepth));\n        }));\n  });\n  return stacks;\n}\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n#if FOLLY_HAVE_SWAPCONTEXT\n\n// Stack utilities used by UnsafeSelfAllocateStackTracePrinter\nnamespace {\n// Size of mmap-allocated stack. Not to confuse with sigaltstack.\nconst size_t kMmapStackSize = 1 * 1024 * 1024;\n\nusing MmapPtr = std::unique_ptr<char, void (*)(char*)>;\n\nMmapPtr getNull() {\n  return MmapPtr(nullptr, [](char*) {});\n}\n\n// Assign a mmap-allocated stack to oucp.\n// Return a non-empty smart pointer on success.\nMmapPtr allocateStack(ucontext_t* oucp, size_t pageSize) {\n  MmapPtr p(\n      (char*)mmap(\n          nullptr,\n          kMmapStackSize,\n          PROT_WRITE | PROT_READ,\n          MAP_ANONYMOUS | MAP_PRIVATE,\n          /* fd */ -1,\n          /* offset */ 0),\n      [](char* addr) {\n        // Usually runs inside a fatal signal handler.\n        // Error handling is skipped.\n        munmap(addr, kMmapStackSize);\n      });\n\n  if (!p) {\n    return getNull();\n  }\n\n  // Prepare read-only guard pages on both ends\n  if (pageSize * 2 >= kMmapStackSize) {\n    return getNull();\n  }\n  size_t upperBound = ((kMmapStackSize - 1) / pageSize) * pageSize;\n  if (mprotect(p.get(), pageSize, PROT_NONE) != 0) {\n    return getNull();\n  }\n  if (mprotect(p.get() + upperBound, kMmapStackSize - upperBound, PROT_NONE) !=\n      0) {\n    return getNull();\n  }\n\n  oucp->uc_stack.ss_sp = p.get() + pageSize;\n  oucp->uc_stack.ss_size = upperBound - pageSize;\n  oucp->uc_stack.ss_flags = 0;\n\n  return p;\n}\n\n} // namespace\n\nFOLLY_PUSH_WARNING\n\n// On Apple platforms, some ucontext methods that are used here are deprecated.\n#ifdef __APPLE__\nFOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\")\n#endif\n\nvoid UnsafeSelfAllocateStackTracePrinter::printSymbolizedStackTrace() {\n  if (pageSizeUnchecked_ <= 0) {\n    return;\n  }\n\n  ucontext_t cur;\n  memset(&cur, 0, sizeof(cur));\n  ucontext_t alt;\n  memset(&alt, 0, sizeof(alt));\n\n  if (getcontext(&alt) != 0) {\n    return;\n  }\n  alt.uc_link = &cur;\n\n  MmapPtr p = allocateStack(&alt, (size_t)pageSizeUnchecked_);\n  if (!p) {\n    return;\n  }\n\n  auto contextStart = [](UnsafeSelfAllocateStackTracePrinter* that) {\n    void const* fromStack;\n    size_t fromStackSize;\n    sanitizer_finish_switch_fiber(nullptr, &fromStack, &fromStackSize);\n    if (that) {\n      that->SafeStackTracePrinter::printSymbolizedStackTrace();\n    }\n    sanitizer_start_switch_fiber(nullptr, fromStack, fromStackSize);\n  };\n\n  makecontext(\n      &alt,\n      (void (*)())(void (*)(UnsafeSelfAllocateStackTracePrinter*))(\n          contextStart),\n      /* argc */ 1,\n      /* arg */ this);\n  void* currentFakestack;\n  sanitizer_start_switch_fiber(\n      &currentFakestack, alt.uc_stack.ss_sp, alt.uc_stack.ss_size);\n  // NOTE: swapcontext is not async-signal-safe\n  if (swapcontext(&cur, &alt) != 0) {\n    contextStart(nullptr);\n  }\n  sanitizer_finish_switch_fiber(currentFakestack, nullptr, nullptr);\n}\n\nFOLLY_POP_WARNING\n\n#endif // FOLLY_HAVE_SWAPCONTEXT\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/Symbolizer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <memory>\n#include <string>\n\n#include <folly/FBString.h>\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/String.h>\n#include <folly/debugging/symbolizer/Dwarf.h>\n#include <folly/debugging/symbolizer/ElfCache.h>\n#include <folly/debugging/symbolizer/StackTrace.h>\n#include <folly/debugging/symbolizer/SymbolizePrinter.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n#include <folly/io/IOBuf.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\nnamespace symbolizer {\n\n/**\n * Get stack trace into a given FrameArray, return true on success (and\n * set frameCount to the actual frame count, which may be > N) and false\n * on failure.\n */\nnamespace detail {\ntemplate <size_t N>\nbool fixFrameArray(FrameArray<N>& fa, ssize_t n) {\n  if (n != -1) {\n    fa.frameCount = n;\n    for (size_t i = 0; i < fa.frameCount; ++i) {\n      fa.frames[i].found = false;\n    }\n    return true;\n  } else {\n    fa.frameCount = 0;\n    return false;\n  }\n}\n\nstd::string getStackTraceStr(\n    const uintptr_t* addresses,\n    SymbolizedFrame* frames,\n    size_t frameCount,\n    bool showFullInfo,\n    size_t skip = 0);\n} // namespace detail\n\n// Always inline these functions; they don't do much, and unittests rely\n// on them never showing up in a stack trace.\ntemplate <size_t N>\nFOLLY_ALWAYS_INLINE bool getStackTrace(FrameArray<N>& fa);\n\ntemplate <size_t N>\ninline bool getStackTrace(FrameArray<N>& fa) {\n  return detail::fixFrameArray(fa, getStackTrace(fa.addresses, N));\n}\ntemplate <size_t N>\nFOLLY_ALWAYS_INLINE bool getStackTraceSafe(FrameArray<N>& fa);\n\ntemplate <size_t N>\ninline bool getStackTraceSafe(FrameArray<N>& fa) {\n  return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));\n}\n\ntemplate <size_t N>\nFOLLY_ALWAYS_INLINE bool getStackTraceHeap(FrameArray<N>& fa);\n\ntemplate <size_t N>\ninline bool getStackTraceHeap(FrameArray<N>& fa) {\n  return detail::fixFrameArray(fa, getStackTraceHeap(fa.addresses, N));\n}\n\ntemplate <size_t N>\nFOLLY_ALWAYS_INLINE bool getAsyncStackTraceSafe(FrameArray<N>& fa);\n\ntemplate <size_t N>\ninline bool getAsyncStackTraceSafe(FrameArray<N>& fa) {\n  return detail::fixFrameArray(fa, getAsyncStackTraceSafe(fa.addresses, N));\n}\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nclass Symbolizer {\n public:\n  static constexpr auto kDefaultLocationInfoMode = LocationInfoMode::FAST;\n\n  static bool isAvailable();\n\n  explicit Symbolizer(LocationInfoMode mode = kDefaultLocationInfoMode)\n      : Symbolizer(nullptr, mode) {}\n\n  explicit Symbolizer(\n      ElfCacheBase* cache,\n      LocationInfoMode mode = kDefaultLocationInfoMode,\n      size_t symbolCacheSize = 0,\n      std::string exePath = \"/proc/self/exe\");\n\n  ~Symbolizer();\n\n  /**\n   *  Symbolize given addresses and return the number of @frames filled:\n   *\n   * - all entries in @addrs will be symbolized (if possible, e.g. if they're\n   *   valid code addresses and if frames.size() >= addrs.size())\n   *\n   * - if `mode_ == FULL_WITH_INLINE` and `frames.size() > addrs.size()` then at\n   *   most `frames.size() - addrs.size()` additional inlined functions will\n   *   also be symbolized (at most `kMaxInlineLocationInfoPerFrame` per @addr\n   *   entry).\n   */\n  size_t symbolize(\n      folly::Range<const uintptr_t*> addrs,\n      folly::Range<SymbolizedFrame*> frames);\n\n  size_t symbolize(\n      const uintptr_t* addresses, SymbolizedFrame* frames, size_t frameCount) {\n    return symbolize(\n        folly::Range<const uintptr_t*>(addresses, frameCount),\n        folly::Range<SymbolizedFrame*>(frames, frameCount));\n  }\n\n  template <size_t N>\n  size_t symbolize(FrameArray<N>& fa) {\n    return symbolize(\n        folly::Range<const uintptr_t*>(fa.addresses, fa.frameCount),\n        folly::Range<SymbolizedFrame*>(fa.frames, N));\n  }\n\n  /**\n   * Shortcut to symbolize one address.\n   */\n  bool symbolize(uintptr_t address, SymbolizedFrame& frame) {\n    symbolize(\n        folly::Range<const uintptr_t*>(&address, 1),\n        folly::Range<SymbolizedFrame*>(&frame, 1));\n    return frame.found;\n  }\n\n private:\n  ElfCacheBase* const cache_;\n  const LocationInfoMode mode_;\n  const std::string exePath_;\n\n  // Details in cpp file to minimize header dependencies\n  struct SymbolCache;\n  std::unique_ptr<SymbolCache> symbolCache_;\n};\n\n/**\n * Use this class to print a stack trace from normal code.  It will malloc and\n * won't flush or sync.\n *\n * These methods are thread safe, through locking.  However, they are not\n * signal safe.\n */\nclass FastStackTracePrinter {\n public:\n  static constexpr size_t kDefaultSymbolCacheSize = 10000;\n\n  explicit FastStackTracePrinter(\n      std::unique_ptr<SymbolizePrinter> printer,\n      size_t symbolCacheSize = kDefaultSymbolCacheSize);\n\n  ~FastStackTracePrinter();\n\n  /**\n   * This is NOINLINE to make sure it shows up in the stack we grab, which\n   * makes it easy to skip printing it.\n   */\n  FOLLY_NOINLINE void printStackTrace(bool symbolize);\n\n  void flush();\n\n private:\n  static constexpr size_t kMaxStackTraceDepth = 100;\n\n  const std::unique_ptr<SymbolizePrinter> printer_;\n  Symbolizer symbolizer_;\n};\n\n/**\n * This is a copy of FastStackTracePrinter, but it uses a two-step approach to\n * symbolize the stack trace. This is useful for cases where symbolization is\n * slow and we want to avoid blocking the main thread.\n */\nclass TwoStepFastStackTracePrinter {\n public:\n  static constexpr size_t kDefaultSymbolCacheSize = 10000;\n\n  explicit TwoStepFastStackTracePrinter(\n      std::unique_ptr<SymbolizePrinter> printer,\n      size_t symbolCacheSize = kDefaultSymbolCacheSize);\n\n  FOLLY_NOINLINE void printStackTrace(bool symbolize);\n\n private:\n  static constexpr size_t kMaxStackTraceDepth = 100;\n\n  const std::unique_ptr<SymbolizePrinter> printer_;\n  Symbolizer symbolizer_;\n  FrameArray<kMaxStackTraceDepth> syncAddresses_;\n  FrameArray<kMaxStackTraceDepth> asyncAddresses_;\n  std::mutex mutex_;\n};\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n/**\n * Use this class to print a stack trace from a signal handler, or other place\n * where you shouldn't allocate memory on the heap, and fsync()ing your file\n * descriptor is more important than performance.\n *\n * Make sure to create one of these on startup, not in the signal handler, as\n * the constructor allocates on the heap, whereas the other methods don't.\n * Best practice is to just leak this object, rather than worry about\n * destruction order.\n *\n * These methods aren't thread safe, so if you could have signals on multiple\n * threads at the same time, you need to do your own locking to ensure you\n * don't call these methods from multiple threads.  They are signal safe,\n * however.\n */\nclass SafeStackTracePrinter {\n public:\n  explicit SafeStackTracePrinter(int fd = STDERR_FILENO);\n\n  virtual ~SafeStackTracePrinter() {}\n\n  /**\n   * Only allocates on the stack and is signal-safe but not thread-safe. Don't\n   * call printStackTrace() on the same StackTracePrinter object from multiple\n   * threads at the same time.\n   *\n   * This is NOINLINE to make sure it shows up in the stack we grab, which\n   * makes it easy to skip printing it.\n   */\n  FOLLY_NOINLINE void printStackTrace(bool symbolize);\n\n  void print(StringPiece sp) { printer_.print(sp); }\n\n  // Flush printer_, also fsync, in case we're about to crash again...\n  void flush();\n\n protected:\n  virtual void printSymbolizedStackTrace();\n  void printUnsymbolizedStackTrace();\n\n private:\n  static constexpr size_t kMaxStackTraceDepth = 100;\n\n  int fd_;\n  FDSymbolizePrinter printer_;\n  std::unique_ptr<FrameArray<kMaxStackTraceDepth>> addresses_;\n};\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n/**\n * Gets the stack trace for the current thread, skipping the top `skip` frames,\n * and returns a string representation. Convenience function meant for\n * debugging and logging. Empty string indicates stack trace functionality\n * is not available.\n *\n * NOT async-signal-safe.\n * @param showFullInfo If true, include file names and line numbers (when\n * available).\n * @param skip Skip the top `skip` frames.\n */\nstd::string getStackTraceStr(bool showFullInfo = false, size_t skip = 0);\n\n/**\n * Gets the async stack trace for the current thread, skipping the top `skip`\n * frames, and returns a string representation. Convenience function meant for\n * debugging and logging. Empty string indicates stack trace functionality\n * is not available.\n *\n * NOT async-signal-safe.\n */\nstd::string getAsyncStackTraceStr(size_t skip = 0);\n\n/**\n * Get the async stack traces (string representation) for suspended\n * coroutines. Convenience function meant for debugging and logging, works\n * only in some DEBUG builds\n *\n * Note: The returned traces will only have async frames (no normal frames).\n */\nstd::vector<std::string> getSuspendedStackTraces();\n\n#else\n// Define these in the header, as headers are always available, but not all\n// platforms can link against the symbolizer library cpp sources.\n\ninline std::string getStackTraceStr(\n    bool /*showFullInfo*/ = false, size_t /*skip*/ = 0) {\n  return \"\";\n}\n\ninline std::string getAsyncStackTraceStr(size_t /*skip*/ = 0) {\n  return \"\";\n}\n\ninline std::vector<std::string> getSuspendedStackTraces() {\n  return {};\n}\n\nnamespace detail {\ninline std::string getStackTraceStr(\n    const uintptr_t* /*addresses*/,\n    SymbolizedFrame* /*frames*/,\n    size_t /*frameCount*/,\n    bool /*showFullInfo*/,\n    size_t /*skip*/) {\n  return \"\";\n}\n} // namespace detail\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n/**\n * Convert FrameArray to string representation.\n * Convenience function for debugging and logging.\n */\ntemplate <size_t N>\nFOLLY_ALWAYS_INLINE std::string getStackTraceStr(\n    FrameArray<N>& fa, bool showFullInfo = false, size_t skip = 0);\n\ntemplate <size_t N>\ninline std::string getStackTraceStr(\n    FrameArray<N>& fa, bool showFullInfo, size_t skip) {\n  return detail::getStackTraceStr(\n      fa.addresses, fa.frames, fa.frameCount, showFullInfo, skip);\n}\n\n#if FOLLY_HAVE_SWAPCONTEXT\n\n/**\n * Use this class in rare situations where signal handlers are running in a\n * tiny stack specified by sigaltstack.\n *\n * This is neither thread-safe nor signal-safe. However, it can usually print\n * something useful while SafeStackTracePrinter would stack overflow.\n *\n * Signal handlers would need to block other signals to make this safer.\n * Note it's still unsafe even with that.\n */\nclass UnsafeSelfAllocateStackTracePrinter : public SafeStackTracePrinter {\n protected:\n  void printSymbolizedStackTrace() override;\n  const long pageSizeUnchecked_ = sysconf(_SC_PAGESIZE);\n};\n\n#endif // FOLLY_HAVE_SWAPCONTEXT\n\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"debug\",\n    srcs = [\"Debug.cpp\"],\n    headers = [\"Debug.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:portability\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n    ],\n)\n"
  },
  {
    "path": "folly/debugging/symbolizer/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME debug\n  SRCS\n    Debug.cpp\n  HEADERS\n    Debug.h\n  DEPS\n    folly_portability\n)\n"
  },
  {
    "path": "folly/debugging/symbolizer/detail/Debug.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/detail/Debug.h>\n\n#include <folly/Portability.h>\n\n#ifndef _WIN32\n#include <dlfcn.h>\n#endif\n\n#if defined(__APPLE__) && !TARGET_OS_OSX\n#define FOLLY_DETAIL_HAS_R_DEBUG 0\n#elif !defined(__linux__) || !FOLLY_HAVE_ELF || !FOLLY_HAVE_DWARF\n#define FOLLY_DETAIL_HAS_R_DEBUG 0\n#else\n#define FOLLY_DETAIL_HAS_R_DEBUG 1\n#endif\n\nnamespace folly {\nnamespace symbolizer {\nnamespace detail {\n\n#if FOLLY_DETAIL_HAS_R_DEBUG\n\n/// There is a strong requirement of finding the true _r_debug in ld.so.\n///\n/// The previous code used the declaration of the extern variable as found in\n///\n///   #include <link.h>\n///\n/// The intention of using the extern variable is to get the toolchain to emit a\n/// GOT access, e.g. like:\n///\n///   mov rax, qword ptr [rip + _r_debug@GOTPCREL]\n///\n/// This works in typical conditions. However, in the case of compiling with:\n///\n///   -mcmodel=small -fno-pic\n///\n/// ie with non-pic and small-code-model, there is a different outcome. Here\n/// instead, the toolchain emits a COPY relocation to copy _r_debug into the\n/// executable and emits a direct non-GOT access to the copy like:\n///\n///   mov eax, offset _r_debug\n///\n/// This causes all accesses in ld.so to be redirected to the copy in the main\n/// executable, which is a valid approach.\n///\n/// However, LLDB specifically looks for _r_debug in ld.so. When the debugger\n/// inspects a process, the _r_debug in the executable has all of the current\n/// information about the link and load state, but the debugger looks only at\n/// the _r_debug in ld.so which is empty! This breaks debugging.\nstatic r_debug* r_debug_cache_;\n[[gnu::constructor(101)]] void r_debug_cache_init_() {\n  r_debug_cache_ = static_cast<r_debug*>(dlsym(RTLD_DEFAULT, \"_r_debug\"));\n}\n\n#endif\n\nstruct r_debug* get_r_debug() {\n#if FOLLY_DETAIL_HAS_R_DEBUG\n  return r_debug_cache_;\n#else\n  return nullptr;\n#endif\n}\n\n} // namespace detail\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/detail/Debug.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nstruct r_debug;\n\nnamespace folly {\nnamespace symbolizer {\nnamespace detail {\n\nstruct r_debug* get_r_debug();\n\n} // namespace detail\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbcode_macros//build_defs:custom_unittest.bzl\", \"custom_unittest\")\nload(\"@fbcode_macros//build_defs:native_rules.bzl\", \"buck_sh_binary\")\nload(\":validate_folly_symbolizer.bzl\", \"SPLIT_DWARF_FLAGS\", \"customized_unittest\", \"validate_folly_symbolizer\", \"validate_symbolizer_dwp\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"crash\",\n    srcs = [\"Crash.cpp\"],\n    compiler_specific_flags = {\n        \"clang\": [\n            \"-gdwarf-aranges\",\n        ],\n    },\n    deps = [\n        \"//folly/debugging/symbolizer:signal_handler\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"dwarf_benchmark\",\n    srcs = [\"DwarfBenchmark.cpp\"],\n    compiler_specific_flags = {\n        \"clang\": [\n            \"-g\",\n        ],\n    },\n    target_compatible_with = [\"fbcode//opensource/macros:broken-in-oss\"],\n    deps = [\n        \":symbolizer_test_utils_dwarf5_dwarf64_single_inlining_aaranges\",\n        \"//folly:benchmark\",\n        \"//folly:range\",\n        \"//folly/debugging/symbolizer:dwarf\",\n        \"//folly/debugging/symbolizer:symbolized_frame\",\n        \"//folly/debugging/symbolizer:symbolizer\",\n        \"//folly/lang:cast\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"small_sigaltstack_crash\",\n    srcs = [\"SmallSigAltStackCrash.cpp\"],\n    compiler_specific_flags = {\n        \"clang\": [\n            \"-gdwarf-aranges\",\n        ],\n    },\n    deps = [\n        \"//folly/debugging/symbolizer:signal_handler\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"symbolized_frame_test\",\n    srcs = [\"SymbolizedFrameTest.cpp\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/debugging/symbolizer:symbolized_frame\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"simple_elf\",\n    srcs = [\"SimpleElf.cpp\"],\n    linker_flags = [\n        \"--build-id=0xdeadbeef\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"elf_test\",\n    srcs = [\"ElfTest.cpp\"],\n    # Do NOT move segments to a seperate helper .so lib because\n    # this test expect to find its symbols in /proc/self/exe.\n    extract_helper_lib = False,\n    resources = [\n        \":simple_elf\",\n    ],\n    deps = [\n        \"fbsource//third-party/googletest:gmock\",\n        \"//folly:cpp_attributes\",\n        \"//folly:file_util\",\n        \"//folly:string\",\n        \"//folly/debugging/symbolizer:elf\",\n        \"//folly/debugging/symbolizer/detail:debug\",\n        \"//folly/portability:gtest\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = buck_sh_binary,\n    name = \"gnu_debuglink_test.sh\",\n    main = \"gnu_debuglink_test.sh\",\n)\n\nfbcode_target(\n    _kind = custom_unittest,\n    name = \"gnu_debuglink_test\",\n    command = [\n        \"$(exe :gnu_debuglink_test.sh)\",\n        \"$(location :crash)\",\n    ],\n    type = \"json\",\n    deps = [\n        \":crash\",\n    ],\n)\n\n# This test consistently fails.\n# See https://www.internalfb.com/diff/D9846902?dst_version_fbid=724209254597440&transaction_fbid=918539549011564\n# custom_unittest(\n#     name = \"gnu_debuglink_test_small_sigaltstack\",\n#     command = [\n#         \"folly/debugging/symbolizer/test/gnu_debuglink_test.sh\",\n#         \"$(location :small_sigaltstack_crash)\",\n#     ],\n#     type = \"json\",\n#     deps = [\n#         \":small_sigaltstack_crash\",\n#     ],\n# )\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"line_reader_test\",\n    srcs = [\"LineReaderTest.cpp\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:file_util\",\n        \"//folly/debugging/symbolizer:line_reader\",\n        \"//folly/portability:gtest\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"signal_handler_test\",\n    srcs = [\"SignalHandlerTest.cpp\"],\n    headers = [\"SignalHandlerTest.h\"],\n    labels = [\"broken-test\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:c_portability\",\n        \"//folly:file_util\",\n        \"//folly:range\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:task\",\n        \"//folly/debugging/symbolizer:signal_handler\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"stack_trace_benchmark\",\n    srcs = [\"StackTraceBenchmark.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/debugging/symbolizer:stack_trace\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"stack_trace_size_limit_test\",\n    srcs = [\"StackTraceSizeLimitTest.cpp\"],\n    labels = [\"oss-broken\"],\n    deps = [\n        \"//folly/debugging/symbolizer:stack_trace\",\n        \"//folly/debugging/symbolizer:symbolizer\",\n        \"//folly/fibers:core_manager\",\n        \"//folly/fibers:simple_loop_controller\",\n        \"//folly/init:init\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"stack_trace_test\",\n    srcs = [\"StackTraceTest.cpp\"],\n    labels = [\"oss-broken\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:task\",\n        \"//folly/debugging/symbolizer:stack_trace\",\n        \"//folly/debugging/symbolizer:symbolizer\",\n        \"//folly/lang:hint\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n        \"//folly/testing:test_util\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_regex\"),\n    ],\n)\n\nfbcode_target(\n    _kind = customized_unittest,\n    available_dwarf_sizes = [\n        32,\n        64,\n    ],\n    available_dwarf_versions = [\n        \"dwarf4\",\n        \"dwarf5\",\n    ],\n    avilable_split_dwarf_keys = SPLIT_DWARF_FLAGS.keys(),\n    custom_suffix = \"\",\n    extra_compiler_flags = [],\n)\n\nfbcode_target(\n    _kind = customized_unittest,\n    available_dwarf_sizes = [\n        32,\n    ],\n    available_dwarf_versions = [\n        \"dwarf5\",\n    ],\n    avilable_split_dwarf_keys = [\"none\"],\n    custom_suffix = \"_split-dwarf4-by-default\",\n    extra_compiler_flags = [\"-gdwarf-5\"],\n)\n\nfbcode_target(\n    _kind = buck_sh_binary,\n    name = \"compare-addr2line.sh\",\n    main = \"compare-addr2line.sh\",\n)\n\nfbcode_target(\n    _kind = validate_folly_symbolizer,\n    name = \"compare-addr2line-symbolizer_test_dwarf4\",\n    binary = \":symbolizer_test_dwarf4_dwarf32_aaranges\",\n)\n\nfbcode_target(\n    _kind = buck_sh_binary,\n    name = \"symbolizer_dwp_compability.sh\",\n    main = \"symbolizer_dwp_compability.sh\",\n)\n\nfbcode_target(\n    _kind = validate_symbolizer_dwp,\n    name = \"validate_symbolizer_dwp_dwarf4_dwarf32_single_inlining_aaranges\",\n    binary = \":symbolizer_test_dwarf4_dwarf32_single_inlining_aaranges\",\n)\n\nfbcode_target(\n    _kind = validate_symbolizer_dwp,\n    name = \"validate_symbolizer_dwp_dwarf5_dwarf32_single_inlining_aaranges\",\n    # we cannot mix split-dwarf-4 with split-dwarf-5. fbcode enables split-dwarf4 by default\n    # unless -c fbcode.split-dwarf5=True is enabled, so let's disable split-dwarf in the test\n    # CU that is built with dwarf5\n    binary = \":symbolizer_test_dwarf5_dwarf32_aaranges_split-dwarf4-by-default\",\n)\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/Crash.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstdlib>\n#include <folly/debugging/symbolizer/SignalHandler.h>\n\nint main() {\n  folly::symbolizer::installFatalSignalHandler();\n  std::abort();\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/DwarfBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/Dwarf.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n#include <folly/debugging/symbolizer/Symbolizer.h>\n#include <folly/debugging/symbolizer/test/SymbolizerTestUtils.h>\n#include <folly/lang/Cast.h>\n#include <folly/portability/GFlags.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nnamespace {\n\nusing namespace folly::symbolizer;\nusing namespace folly::symbolizer::test;\n\nFOLLY_NOINLINE void lexicalBlockBar() try {\n  [[maybe_unused]] size_t unused = 0;\n  unused++;\n  inlineB_inlineA_lfind();\n} catch (...) {\n  folly::assume_unreachable();\n}\n\nvoid run(LocationInfoMode mode, size_t n) {\n  folly::BenchmarkSuspender suspender;\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      folly::reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  lexicalBlockBar();\n  symbolizer.symbolize(frames);\n  // The address of the line where lexicalBlockBar calls inlineB_inlineA_lfind.\n  uintptr_t address = frames.frames[7].addr;\n\n  ElfCache cache;\n  ElfFile elf(\"/proc/self/exe\");\n  Dwarf dwarf(&cache, &elf);\n  auto inlineFrames = std::array<SymbolizedFrame, 10>();\n  suspender.dismiss();\n\n  for (size_t i = 0; i < n; i++) {\n    folly::symbolizer::SymbolizedFrame frame;\n    dwarf.findAddress(address, mode, frame, folly::range(inlineFrames));\n  }\n\n  suspender.rehire();\n}\n\n} // namespace\n\nBENCHMARK(DwarfFindAddressFast, n) {\n  run(folly::symbolizer::LocationInfoMode::FAST, n);\n}\n\nBENCHMARK(DwarfFindAddressFull, n) {\n  run(folly::symbolizer::LocationInfoMode::FULL, n);\n}\n\nBENCHMARK(DwarfFindAddressFullWithInline, n) {\n  run(folly::symbolizer::LocationInfoMode::FULL_WITH_INLINE, n);\n}\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  google::InitGoogleLogging(argv[0]);\n  folly::runBenchmarksOnFlag();\n  return 0;\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/ElfTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Elf.h>\n\n#include <gmock/gmock.h>\n#include <folly/CppAttributes.h>\n#include <folly/FileUtil.h>\n#include <folly/String.h>\n#include <folly/debugging/symbolizer/detail/Debug.h>\n#include <folly/portability/GTest.h>\n#include <folly/testing/TestUtil.h>\n\n#if FOLLY_HAVE_ELF\n\nusing folly::symbolizer::ElfFile;\nusing folly::symbolizer::ElfNhdr;\n\n// Add some symbols for testing. Note that we have to be careful with type\n// signatures here to prevent name mangling\nextern \"C\" {\n[[gnu::used, FOLLY_ATTR_GNU_RETAIN]] uint64_t kIntegerValue = 1234567890ULL;\n[[gnu::used, FOLLY_ATTR_GNU_RETAIN]] const char* kStringValue = \"coconuts\";\n[[gnu::noinline, gnu::used, FOLLY_ATTR_GNU_RETAIN]] int sum_func(\n    int lhs, int rhs) {\n  return lhs + rhs;\n}\n[[gnu::noinline, gnu::used, FOLLY_ATTR_GNU_RETAIN]] int sub_func(\n    int lhs, int rhs) {\n  return lhs - rhs;\n}\n}\n\nconst char* const kDefaultElf = \"/proc/self/exe\";\nclass ElfTest : public ::testing::Test {\n protected:\n  ElfFile elfFile_{kDefaultElf};\n};\n\nTEST_F(ElfTest, IntegerValue) {\n  auto sym = elfFile_.getSymbolByName(\"kIntegerValue\");\n  EXPECT_NE(nullptr, sym.first) << \"Failed to look up symbol kIntegerValue\";\n  EXPECT_EQ(kIntegerValue, *elfFile_.getSymbolValue<uint64_t>(sym.second));\n}\n\nTEST_F(ElfTest, PointerValue) {\n  auto sym = elfFile_.getSymbolByName(\"kStringValue\");\n  EXPECT_NE(nullptr, sym.first) << \"Failed to look up symbol kStringValue\";\n  ElfW(Addr) addr = *elfFile_.getSymbolValue<ElfW(Addr)>(sym.second);\n  // Let's check the address for the symbol against our own copy of\n  // kStringValue.\n  // For PIE binaries we need to adjust the address due to relocation.\n  auto binaryOffset = folly::symbolizer::detail::get_r_debug()->r_map->l_addr;\n  EXPECT_EQ(\n      static_cast<const void*>(&kStringValue),\n      reinterpret_cast<const void*>(binaryOffset + sym.second->st_value));\n  if (binaryOffset == 0) { // non-PIE\n    // Only do this check if we have a non-PIE. For the PIE case, the compiler\n    // could put a 0 in the .data section for kStringValue, and then rely on\n    // the dynamic linker to fill in the actual pointer to the .rodata section\n    // via relocation; the actual offset of the string is encoded in the\n    // .rela.dyn section, which isn't parsed in the current implementation of\n    // ElfFile.\n    const char* str = elfFile_.getAddressValue<const char>(addr);\n    EXPECT_STREQ(kStringValue, str);\n  }\n}\n\nTEST_F(ElfTest, SymbolByName) {\n  auto sym = elfFile_.getSymbolByName(\"sum_func\");\n  EXPECT_NE(nullptr, sym.first) << \"Failed to look up symbol sum_func\";\n\n  sym = elfFile_.getSymbolByName(\"sum_func\", {STT_OBJECT});\n  EXPECT_EQ(nullptr, sym.first) << \"Unexpectedly look up of non-object symbol.\";\n\n  sym = elfFile_.getSymbolByName(\"sum_func\", {STT_FUNC});\n  EXPECT_NE(nullptr, sym.first)\n      << \"Failed to look up symbol sum_func when specifying type\";\n\n  sym = elfFile_.getSymbolByName(\"kIntegerValue\", {STT_FUNC});\n  EXPECT_EQ(nullptr, sym.first) << \"Unexpected look up of non-func symbol.\";\n\n  sym = elfFile_.getSymbolByName(\"kIntegerValue\", {STT_OBJECT});\n  EXPECT_NE(nullptr, sym.first)\n      << \"Failed to look up symbol kIntegerValue when specifying type\";\n}\n\nTEST_F(ElfTest, SymbolsByNameSuccess) {\n  std::vector<std::string> names = {\"sum_func\", \"sub_func\"};\n  auto result = elfFile_.getSymbolsByName(names, {STT_FUNC});\n\n  EXPECT_EQ(names.size(), result.size());\n  EXPECT_TRUE(result.find(\"sum_func\") != result.end());\n  EXPECT_TRUE(result.find(\"sub_func\") != result.end());\n\n  const auto& sumFuncSymbol = result.at(\"sum_func\");\n  EXPECT_NE(nullptr, sumFuncSymbol.first);\n  EXPECT_NE(nullptr, sumFuncSymbol.second);\n\n  const auto& subFuncSymbol = result.at(\"sub_func\");\n  EXPECT_NE(nullptr, subFuncSymbol.first);\n  EXPECT_NE(nullptr, subFuncSymbol.second);\n}\n\nTEST_F(ElfTest, SymbolsByNamePartial) {\n  std::vector<std::string> names = {\"sum_func\", \"sub_func\", \"foo_func\"};\n  auto result = elfFile_.getSymbolsByName(names, {STT_FUNC});\n\n  EXPECT_EQ(names.size(), result.size());\n  EXPECT_TRUE(result.find(\"sum_func\") != result.end());\n  EXPECT_TRUE(result.find(\"sub_func\") != result.end());\n  EXPECT_TRUE(result.find(\"foo_func\") != result.end());\n\n  const auto& fooFuncSymbol = result.at(\"foo_func\");\n  EXPECT_EQ(nullptr, fooFuncSymbol.first);\n  EXPECT_EQ(nullptr, fooFuncSymbol.second);\n}\n\nTEST_F(ElfTest, SymbolsByNameEmpty) {\n  std::vector<std::string> names;\n  auto result = elfFile_.getSymbolsByName(names, {STT_FUNC});\n\n  EXPECT_EQ(0, result.size());\n  EXPECT_EQ(names.size(), result.size());\n}\n\nTEST_F(ElfTest, iterateProgramHeaders) {\n  auto phdr = elfFile_.iterateProgramHeaders([](auto& h) {\n    return h.p_type == PT_LOAD;\n  });\n  EXPECT_NE(nullptr, phdr);\n  EXPECT_GE(phdr->p_filesz, 0);\n  auto body = elfFile_.getSegmentBody(*phdr);\n  EXPECT_EQ(body.size(), phdr->p_filesz);\n}\n\nTEST_F(ElfTest, TinyNonElfFile) {\n  folly::test::TemporaryFile tmpFile;\n  const static folly::StringPiece contents = \"!\";\n  folly::writeFull(tmpFile.fd(), contents.data(), contents.size());\n\n  ElfFile elfFile;\n  auto res = elfFile.openNoThrow(tmpFile.path().c_str());\n  EXPECT_EQ(ElfFile::kInvalidElfFile, res.code);\n  EXPECT_STREQ(\"not an ELF file (too short)\", res.msg);\n}\n\nTEST_F(ElfTest, NonElfScript) {\n  folly::test::TemporaryFile tmpFile;\n  const static folly::StringPiece contents =\n      \"#!/bin/sh\\necho I'm small non-ELF executable\\n\";\n  folly::writeFull(tmpFile.fd(), contents.data(), contents.size());\n\n  ElfFile elfFile;\n  auto res = elfFile.openNoThrow(tmpFile.path().c_str());\n  EXPECT_EQ(ElfFile::kInvalidElfFile, res.code);\n  EXPECT_STREQ(\"invalid ELF magic\", res.msg);\n}\n\nTEST_F(ElfTest, FailToOpenLargeFilename) {\n  // ElfFile used to segfault if it failed to open large filenames\n  folly::test::TemporaryDirectory tmpDir;\n  auto elfFile = std::make_unique<ElfFile>(); // on the heap so asan can see it\n  auto const largeNonExistingName = (tmpDir.path() / std::string(1000, 'x'));\n  ASSERT_EQ(\n      ElfFile::kSystemError,\n      elfFile->openNoThrow(largeNonExistingName.c_str()));\n  ASSERT_EQ(\n      ElfFile::kSystemError,\n      elfFile->openNoThrow(largeNonExistingName.c_str()));\n  EXPECT_EQ(ElfFile::kSuccess, elfFile->openNoThrow(kDefaultElf));\n}\n\nTEST(TestGetNoteGnuBuildId, SimpleElf) {\n  auto const file =\n      folly::test::find_resource(\"folly/debugging/symbolizer/test/simple_elf\");\n  ASSERT_TRUE(std::filesystem::exists(file.c_str())) << file.c_str();\n  ElfFile elfFile = ElfFile(file.c_str());\n  auto noteMaybe = elfFile.getNoteGnuBuildId();\n  ASSERT_TRUE(([&]() {\n    return noteMaybe.hasError()\n        ? testing::AssertionFailure()\n            << ElfFile::FindNoteError::getErrorMessage(noteMaybe.error())\n        : testing::AssertionSuccess();\n  })());\n\n  auto note = noteMaybe.value();\n  EXPECT_EQ(4, note.size());\n  EXPECT_THAT(note, ::testing::ElementsAreArray({0xDE, 0xAD, 0xBE, 0xEF}));\n}\n\nTEST(TestNoteSectionIteration, SimpleElf) {\n  auto const file =\n      folly::test::find_resource(\"folly/debugging/symbolizer/test/simple_elf\");\n  EXPECT_TRUE(std::filesystem::exists(file.c_str())) << file.c_str();\n  ElfFile elfFile = ElfFile(file.c_str());\n  const auto* shdr = elfFile.getSectionByName(\".note.gnu.build-id\");\n  EXPECT_NE(nullptr, shdr);\n\n  std::vector<folly::Expected<ElfFile::Note, ElfFile::FindNoteError>>\n      noteMaybes;\n  noteMaybes.emplace_back(\n      elfFile.iterateNotesInSections(shdr, [](const ElfFile::Note& note) {\n        if (note.header()->n_type != NT_GNU_BUILD_ID) {\n          return false;\n        }\n\n        auto expectedSize = folly::align_ceil(note.header()->n_namesz, 4) +\n            folly::align_ceil(note.header()->n_descsz, 4);\n        return note.body().size() == expectedSize;\n      }));\n\n  noteMaybes.emplace_back(\n      elfFile.iterateNotesInSections(nullptr, [](const ElfFile::Note& note) {\n        if (note.header()->n_type != NT_GNU_BUILD_ID) {\n          return false;\n        }\n\n        auto expectedSize = folly::align_ceil(note.header()->n_namesz, 4) +\n            folly::align_ceil(note.header()->n_descsz, 4);\n        return note.body().size() == expectedSize;\n      }));\n\n  for (auto& noteMaybe : noteMaybes) {\n    ASSERT_TRUE(([&]() {\n      return noteMaybe.hasError()\n          ? testing::AssertionFailure()\n              << ElfFile::FindNoteError::getErrorMessage(noteMaybe.error())\n          : testing::AssertionSuccess();\n    })());\n\n    ElfFile::Note note = *noteMaybe;\n    ASSERT_NE(nullptr, note.header());\n    EXPECT_EQ(note.getName(), \"GNU\");\n    EXPECT_THAT(\n        note.getDesc(), ::testing::ElementsAreArray({0xDE, 0xAD, 0xBE, 0xEF}));\n  }\n}\n\nTEST(TestNoteSegmentIteration, SimpleElf) {\n  auto const file =\n      folly::test::find_resource(\"folly/debugging/symbolizer/test/simple_elf\");\n  EXPECT_TRUE(std::filesystem::exists(file.c_str())) << file.c_str();\n  ElfFile elfFile = ElfFile(file.c_str());\n\n  // PRStatus should always be in the segments\n  auto noteMaybe =\n      elfFile.iterateNotesInSegments(nullptr, [](const ElfFile::Note& note) {\n        return note.header()->n_type == NT_PRSTATUS;\n      });\n\n  ASSERT_TRUE(([&]() {\n    return noteMaybe.hasError()\n        ? testing::AssertionFailure()\n            << ElfFile::FindNoteError::getErrorMessage(noteMaybe.error())\n        : testing::AssertionSuccess();\n  })());\n\n  auto note = *noteMaybe;\n  ASSERT_NE(nullptr, note.header());\n  EXPECT_EQ(note.getName(), \"GNU\");\n  EXPECT_NE(0, note.getDesc().size());\n}\n\nTEST(TestNoteMultipleNoteSections, SimpleElf) {\n  auto const file =\n      folly::test::find_resource(\"folly/debugging/symbolizer/test/simple_elf\");\n  EXPECT_TRUE(std::filesystem::exists(file.c_str())) << file.c_str();\n  ElfFile elfFile = ElfFile(file.c_str());\n  std::vector<ElfFile::Note> notes;\n  elfFile.iterateNotesInSections(nullptr, [&](const ElfFile::Note& note) {\n    notes.push_back(note);\n    return false;\n  });\n\n  EXPECT_NE(0, notes.size());\n  for (const auto& noteEntry : notes) {\n    EXPECT_NE(nullptr, noteEntry.header());\n    EXPECT_NE(0, noteEntry.body().size());\n    EXPECT_NE(\"\", noteEntry.getName());\n    EXPECT_NE(0, noteEntry.getDesc().size());\n  }\n}\n\nTEST(TestNoteParsing, SimpleElf) {\n  uint8_t headerOnly[sizeof(ElfNhdr)];\n  ElfNhdr header;\n  header.n_namesz = 8;\n  header.n_descsz = 4;\n  memcpy(&headerOnly, &header, sizeof(ElfNhdr));\n  auto noteMaybe = ElfFile::Note::parse(\n      folly::span<const uint8_t>(\n          reinterpret_cast<const uint8_t*>(&headerOnly), sizeof(ElfNhdr)));\n  EXPECT_TRUE(([&]() {\n    return noteMaybe.hasError()\n        ? testing::AssertionSuccess()\n        : testing::AssertionFailure();\n  })());\n  auto& err = noteMaybe.error();\n  EXPECT_TRUE(err.isDataCorruptionError());\n  EXPECT_EQ(err.failureCode, ElfFile::FindNoteFailureCode::NoteUndersized);\n\n  uint8_t smallerThanHeader[6];\n  noteMaybe = ElfFile::Note::parse(\n      folly::span<const uint8_t>(\n          reinterpret_cast<const uint8_t*>(&smallerThanHeader), 6));\n  EXPECT_TRUE(([&]() {\n    return noteMaybe.hasError()\n        ? testing::AssertionSuccess()\n        : testing::AssertionFailure();\n  })());\n  err = noteMaybe.error();\n  EXPECT_TRUE(err.isDataCorruptionError());\n  EXPECT_EQ(err.failureCode, ElfFile::FindNoteFailureCode::NoteUndersized);\n}\n\nTEST(TestFindNote, SimpleElf) {\n  auto const file =\n      folly::test::find_resource(\"folly/debugging/symbolizer/test/simple_elf\");\n\n  EXPECT_TRUE(std::filesystem::exists(file.c_str())) << file.c_str();\n  ElfFile elfFile = ElfFile(file.c_str());\n  const auto* shdr = elfFile.getSectionByName(\".note.gnu.build-id\");\n  EXPECT_NE(nullptr, shdr);\n\n  // There are multiple notes whose names are \"GNU\"\n  // to avoid flakiness we just check the name. But below when we iterate\n  // by type, we check the type and the name.\n  std::string noteName = \"GNU\";\n  {\n    auto noteMaybe = elfFile.findNoteByName(noteName);\n    ASSERT_TRUE(([&]() {\n      return noteMaybe.hasError()\n          ? testing::AssertionFailure()\n              << ElfFile::FindNoteError::getErrorMessage(noteMaybe.error())\n          : testing::AssertionSuccess();\n    })());\n\n    EXPECT_EQ(noteName, noteMaybe->getName());\n  }\n  {\n    auto noteMaybe = elfFile.findNoteByType(NT_GNU_BUILD_ID);\n    ASSERT_TRUE(([&]() {\n      return noteMaybe.hasError()\n          ? testing::AssertionFailure()\n              << ElfFile::FindNoteError::getErrorMessage(noteMaybe.error())\n          : testing::AssertionSuccess();\n    })());\n\n    EXPECT_EQ(noteName, noteMaybe->getName());\n    EXPECT_EQ(NT_GNU_BUILD_ID, noteMaybe->header()->n_type);\n  }\n  {\n    auto noteMaybe = elfFile.findNoteByType(NT_PRSTATUS);\n    ASSERT_TRUE(([&]() {\n      return noteMaybe.hasError()\n          ? testing::AssertionFailure()\n              << ElfFile::FindNoteError::getErrorMessage(noteMaybe.error())\n          : testing::AssertionSuccess();\n    })());\n\n    EXPECT_EQ(noteName, noteMaybe->getName());\n    EXPECT_EQ(NT_PRSTATUS, noteMaybe->header()->n_type);\n  }\n  {\n    auto noteMaybe =\n        elfFile.findNoteByType(std::numeric_limits<uint32_t>::max());\n    EXPECT_TRUE(noteMaybe.hasError());\n    EXPECT_FALSE(noteMaybe.error().isDataCorruptionError());\n  }\n  {\n    auto noteMaybe = elfFile.findNoteByName(\"NOT_A_NOTE\");\n    EXPECT_TRUE(noteMaybe.hasError());\n    EXPECT_FALSE(noteMaybe.error().isDataCorruptionError());\n  }\n}\n\n#endif\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/LineReaderTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/LineReader.h>\n\n#include <glog/logging.h>\n\n#include <folly/FileUtil.h>\n#include <folly/portability/GTest.h>\n#include <folly/testing/TestUtil.h>\n\nnamespace folly {\nnamespace symbolizer {\nnamespace test {\n\nusing folly::test::TemporaryFile;\n\nvoid writeAll(int fd, const char* str) {\n  ssize_t n = strlen(str);\n  CHECK_EQ(n, writeFull(fd, str, n));\n}\n\nvoid expect(LineReader& lr, const char* expected) {\n  StringPiece line;\n  size_t expectedLen = strlen(expected);\n  EXPECT_EQ(\n      expectedLen != 0 ? LineReader::kReading : LineReader::kEof,\n      lr.readLine(line));\n  EXPECT_EQ(expectedLen, line.size());\n  EXPECT_EQ(std::string(expected, expectedLen), line.str());\n}\n\nTEST(LineReader, Simple) {\n  TemporaryFile file;\n  int fd = file.fd();\n  writeAll(\n      fd,\n      \"Meow\\n\"\n      \"Hello world\\n\"\n      \"This is a long line. It is longer than the other lines.\\n\"\n      \"\\n\"\n      \"Incomplete last line\");\n\n  {\n    CHECK_ERR(lseek(fd, 0, SEEK_SET));\n    char buf[10];\n    LineReader lr(fd, buf, sizeof(buf));\n    expect(lr, \"Meow\\n\");\n    expect(lr, \"Hello worl\");\n    expect(lr, \"d\\n\");\n    expect(lr, \"This is a \");\n    expect(lr, \"long line.\");\n    expect(lr, \" It is lon\");\n    expect(lr, \"ger than t\");\n    expect(lr, \"he other l\");\n    expect(lr, \"ines.\\n\");\n    expect(lr, \"\\n\");\n    expect(lr, \"Incomplete\");\n    expect(lr, \" last line\");\n    expect(lr, \"\");\n  }\n\n  {\n    CHECK_ERR(lseek(fd, 0, SEEK_SET));\n    char buf[80];\n    LineReader lr(fd, buf, sizeof(buf));\n    expect(lr, \"Meow\\n\");\n    expect(lr, \"Hello world\\n\");\n    expect(lr, \"This is a long line. It is longer than the other lines.\\n\");\n    expect(lr, \"\\n\");\n    expect(lr, \"Incomplete last line\");\n    expect(lr, \"\");\n  }\n}\n} // namespace test\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SignalHandlerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/test/SignalHandlerTest.h>\n\n#include <folly/debugging/symbolizer/SignalHandler.h>\n\n#include <folly/CPortability.h>\n#include <folly/FileUtil.h>\n#include <folly/Range.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/portability/GTest.h>\n\n#include <glog/logging.h>\n\n#include <memory>\n\nnamespace folly {\nnamespace symbolizer {\nnamespace test {\n\nnamespace {\n\nvoid print(StringPiece sp) {\n  writeFull(STDERR_FILENO, sp.data(), sp.size());\n}\n\nvoid callback1() {\n  print(\"Callback1\\n\");\n}\n\nvoid callback2() {\n  if (fatalSignalReceived()) {\n    print(\"Callback2\\n\");\n  }\n}\n\n[[noreturn]] FOLLY_NOINLINE void funcC() {\n  LOG(FATAL) << \"Die\";\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcB() {\n  funcC();\n  co_return;\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcA() {\n  co_await co_funcB();\n}\n\n} // namespace\n\nTEST(SignalHandler, Simple) {\n  addFatalSignalCallback(callback1);\n  addFatalSignalCallback(callback2);\n  installFatalSignalHandler();\n  installFatalSignalCallbacks();\n\n  EXPECT_FALSE(fatalSignalReceived());\n\n  EXPECT_DEATH(\n      failHard(),\n      \"^\\\\*\\\\*\\\\* Aborted at [0-9]+ \\\\(Unix time, try 'date -d @[0-9]+'\\\\) \"\n      \"\\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* Signal 11 \\\\(SIGSEGV\\\\) \\\\(0x2a\\\\) received by PID [0-9]+ \"\n      \"\\\\(pthread TID 0x[0-9a-f]+\\\\) \\\\(linux TID [0-9]+\\\\) \"\n      \"\\\\(code: address not mapped to object\\\\), \"\n      \"stack trace: \\\\*\\\\*\\\\*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* folly::symbolizer::test::SignalHandler_Simple_Test\"\n      \"::TestBody\\\\(\\\\).*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* main.*\\n\"\n      \".*\\n\"\n      \"Callback1\\n\"\n      \"Callback2\\n\"\n      \".*\");\n}\n\nTEST(SignalHandler, GenericSICode) {\n  addFatalSignalCallback(callback1);\n  addFatalSignalCallback(callback2);\n  installFatalSignalHandler();\n  installFatalSignalCallbacks();\n\n  EXPECT_FALSE(fatalSignalReceived());\n\n  EXPECT_DEATH(\n      raise(SIGSEGV),\n      \"^\\\\*\\\\*\\\\* Aborted at [0-9]+ \\\\(Unix time, try 'date -d @[0-9]+'\\\\) \"\n      \"\\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* Signal 11 \\\\(SIGSEGV\\\\) \\\\(0x[a-f0-9]+\\\\) received by PID [0-9]+ \"\n      \"\\\\(pthread TID 0x[0-9a-f]+\\\\) \\\\(linux TID [0-9]+\\\\) \\\\(maybe from PID [0-9]+, UID [0-9]+\\\\) \"\n      \"\\\\(code: sent by tkill or tgkill\\\\), \"\n      \"stack trace: \\\\*\\\\*\\\\*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* folly::symbolizer::test::SignalHandler_GenericSICode_Test\"\n      \"::TestBody\\\\(\\\\).*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* main.*\\n\"\n      \".*\\n\"\n      \"Callback1\\n\"\n      \"Callback2\\n\"\n      \".*\");\n}\n\nTEST(SignalHandler, AsyncStackTraceSimple) {\n  addFatalSignalCallback(callback1);\n  addFatalSignalCallback(callback2);\n  installFatalSignalHandler();\n  installFatalSignalCallbacks();\n\n  EXPECT_DEATH(\n      folly::coro::blockingWait(co_funcA()),\n      \"\\\\*\\\\*\\\\* Aborted at [0-9]+ \\\\(Unix time, try 'date -d @[0-9]+'\\\\) \"\n      \"\\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* Signal 6 \\\\(SIGABRT\\\\) \\\\(0x[0-9a-f]+\\\\) received by PID [0-9]+ \"\n      \"\\\\(pthread TID 0x[0-9a-f]+\\\\) \\\\(linux TID [0-9]+\\\\) .*, \"\n      \"stack trace: \\\\*\\\\*\\\\*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* folly::symbolizer::test::SignalHandler\"\n      \"_AsyncStackTraceSimple_Test::TestBody\\\\(\\\\).*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* main.*\\n\"\n      \".*\\n\"\n      \"\\\\*\\\\*\\\\* Check failure async stack trace: \\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* First async stack root.* \\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* First async stack frame pointer.* \\\\*\\\\*\\\\*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* folly::symbolizer::test::\\\\(anonymous namespace\\\\)\"\n      \"::co_funcA.*\\n\"\n      \".*\\n\"\n      \"Callback1\\n\"\n      \"Callback2\\n\"\n      \".*\");\n}\n\nTEST(SignalHandler, AsyncStackTraceSimple2) {\n  addFatalSignalCallback(callback1);\n  addFatalSignalCallback(callback2);\n  installFatalSignalHandler();\n  installFatalSignalCallbacks();\n  EXPECT_DEATH(\n      [] {\n        auto ex = std::make_unique<IOThreadPoolExecutor>(/* nThreads */ 1);\n        auto fut = co_withExecutor(ex.get(), co_funcA()).start();\n        fut.wait();\n      }(),\n      \"\\\\*\\\\*\\\\* Aborted at [0-9]+ \\\\(Unix time, try 'date -d @[0-9]+'\\\\) \"\n      \"\\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* Signal 6 \\\\(SIGABRT\\\\) \\\\(0x[0-9a-f]+\\\\) received by PID [0-9]+ \"\n      \"\\\\(pthread TID 0x[0-9a-f]+\\\\) \\\\(linux TID [0-9]+\\\\) .*, \"\n      \"stack trace: \\\\*\\\\*\\\\*\\n\"\n      \"(.*WARNING.*\\n)?\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* folly::symbolizer::test::\\\\(anonymous namespace\\\\)\"\n      \"::funcC\\\\(\\\\)\\n\"\n      \".*\\n\"\n      \"\\\\*\\\\*\\\\* Check failure async stack trace: \\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* First async stack root.* \\\\*\\\\*\\\\*\\n\"\n      \"\\\\*\\\\*\\\\* First async stack frame pointer.* \\\\*\\\\*\\\\*\\n\"\n      \".*\\n\"\n      \".*    @ [0-9a-f]+.* folly::symbolizer::test::\\\\(anonymous namespace\\\\)\"\n      \"::co_funcA.*\\n\"\n      \".*\\n\"\n      \"Callback1\\n\"\n      \"Callback2\\n\"\n      \".*\");\n}\n\n} // namespace test\n} // namespace symbolizer\n} // namespace folly\n\n// Can't use initFacebookLight since that would install its own signal handlers\n// Can't use initFacebookNoSignals since we cannot depend on common\nint main(int argc, char** argv) {\n  ::testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SignalHandlerTest.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly {\nnamespace symbolizer {\nnamespace test {\n\ninline void failHard() {\n  *(/* nolint */ volatile char*)42; // SIGSEGV\n}\n} // namespace test\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SimpleElf.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nint main() {\n  int x = 42;\n  return x;\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SmallSigAltStackCrash.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <signal.h>\n#include <array>\n\n#include <folly/debugging/symbolizer/SignalHandler.h>\n\nnamespace {\nstd::array<char, 8192> stack;\n}\n\nint main() {\n  stack_t ss;\n  ss.ss_sp = stack.data();\n  ss.ss_size = stack.size();\n  ss.ss_flags = 0;\n  sigaltstack(&ss, nullptr);\n\n  folly::symbolizer::installFatalSignalHandler();\n  __builtin_trap();\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/StackTraceBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/debugging/symbolizer/StackTrace.h>\n#include <folly/init/Init.h>\n\nusing namespace folly;\nusing namespace folly::symbolizer;\n\nunsigned int basic(size_t nFrames) {\n  unsigned int iters = 1000000;\n  constexpr size_t kMaxAddresses = 100;\n  uintptr_t addresses[kMaxAddresses];\n\n  CHECK_LE(nFrames, kMaxAddresses);\n\n  for (unsigned int i = 0; i < iters; ++i) {\n    ssize_t n = getStackTrace(addresses, nFrames);\n    CHECK_NE(n, -1);\n  }\n  // Reduce iters by nFrames as the recursive call will add it up.\n  return iters - nFrames;\n}\n\nunsigned int Recurse(size_t nFrames, size_t remaining) {\n  if (remaining == 0) {\n    return basic(nFrames);\n  } else {\n    // +1 to prevent tail recursion optimization from kicking in.\n    return Recurse(nFrames, remaining - 1) + 1;\n  }\n}\n\nunsigned int SetupStackAndTest(int /* iters */, size_t nFrames) {\n  return Recurse(nFrames, nFrames);\n}\n\nBENCHMARK_NAMED_PARAM_MULTI(SetupStackAndTest, 2_frames, 2)\nBENCHMARK_NAMED_PARAM_MULTI(SetupStackAndTest, 4_frames, 4)\nBENCHMARK_NAMED_PARAM_MULTI(SetupStackAndTest, 8_frames, 8)\nBENCHMARK_NAMED_PARAM_MULTI(SetupStackAndTest, 16_frames, 16)\nBENCHMARK_NAMED_PARAM_MULTI(SetupStackAndTest, 32_frames, 32)\nBENCHMARK_NAMED_PARAM_MULTI(SetupStackAndTest, 64_frames, 64)\n\n/**\n============================================================================\n                                                          time/iter  iters/s\n============================================================================\nSetupStackAndTest(2_frames)                                 51.68ns   19.35M\nSetupStackAndTest(4_frames)                                 72.59ns   13.78M\nSetupStackAndTest(8_frames)                                117.03ns    8.54M\nSetupStackAndTest(16_frames)                               195.54ns    5.11M\nSetupStackAndTest(32_frames)                               384.70ns    2.60M\nSetupStackAndTest(64_frames)                               728.65ns    1.37M\n============================================================================\n*/\n\nint main(int argc, char* argv[]) {\n  folly::Init init(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/StackTraceSizeLimitTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/StackTrace.h>\n#include <folly/debugging/symbolizer/Symbolizer.h>\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/SimpleLoopController.h>\n#include <folly/init/Init.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly::fibers;\nusing namespace folly::symbolizer;\n\nconstexpr size_t kMaxAddresses = 1000;\n\nvoid fBaseline(FrameArray<kMaxAddresses>& /* unused */) {\n  auto ffa = std::make_unique<FrameArray<kMaxAddresses>>();\n  (void)ffa;\n}\n\nvoid fStack(FrameArray<kMaxAddresses>& fa) {\n  auto ffa = std::make_unique<FrameArray<kMaxAddresses>>();\n  (void)ffa;\n  getStackTrace(fa);\n}\n\nvoid fHeap(FrameArray<kMaxAddresses>& fa) {\n  auto ffa = std::make_unique<FrameArray<kMaxAddresses>>();\n  (void)ffa;\n  getStackTraceHeap(fa);\n}\n\n// Check that the requires stacks for capturing a stack trace do not\n// exceed reasonable levels surprisingly.\nTEST(StackTraceSizeLimitTest, FiberLimit) {\n  FiberManager::Options opts;\n  opts.recordStackEvery = 1;\n\n  auto t = [&](folly::Function<void(FrameArray<kMaxAddresses>&)> f,\n               size_t highWaterMark) {\n    FiberManager manager(std::make_unique<SimpleLoopController>(), opts);\n    auto& loopController =\n        dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n    bool checkRan = false;\n    FrameArray<kMaxAddresses> fa;\n    manager.addTask([&] {\n      checkRan = true;\n      f(fa);\n    });\n\n    EXPECT_EQ(manager.stackHighWatermark(), 0);\n\n    loopController.loop([&]() { loopController.stop(); });\n\n    EXPECT_LE(manager.stackHighWatermark(), highWaterMark);\n\n    EXPECT_TRUE(checkRan);\n  };\n  // Initial run\n  t(fBaseline, 10000);\n\n  // The amount of stack needed varies based on compilation modes.\n  if (folly::kIsDebug) {\n    if (folly::kIsSanitizeAddress) {\n      t(fBaseline, 0);\n      t(fStack, 0);\n      t(fHeap, 0);\n    } else if (folly::kIsSanitizeThread) {\n      t(fBaseline, 3700);\n      t(fStack, 10000);\n      // \"Typical memory overhead introduced by ThreadSanitizer is about\n      // 5x-10x.\" - LLVM ThreadSanitizer Documentation\n      t(fHeap, 5'632);\n    } else {\n      t(fBaseline, 3700);\n      t(fStack, 10000);\n      t(fHeap, 3'840);\n    }\n  } else {\n    if (folly::kIsSanitizeThread) {\n      t(fBaseline, 2500);\n      t(fStack, 9000);\n      t(fHeap, 3500);\n    } else {\n      t(fBaseline, 1600);\n      t(fStack, 9000);\n      t(fHeap, 2200);\n    }\n  }\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/StackTraceTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstring>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n#include <folly/debugging/symbolizer/StackTrace.h>\n#include <folly/debugging/symbolizer/Symbolizer.h>\n#include <folly/lang/Hint.h>\n#include <folly/test/TestUtils.h>\n#include <folly/testing/TestUtil.h>\n\n#include <boost/regex.hpp>\n\n#include <glog/logging.h>\n\n#include <folly/portability/GTest.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nusing namespace folly;\nusing namespace folly::symbolizer;\n\nFOLLY_NOINLINE void foo1();\nFOLLY_NOINLINE void foo2();\n\nvoid verifyStackTraces() {\n  constexpr size_t kMaxAddresses = 100;\n  FrameArray<kMaxAddresses> fa;\n  CHECK(getStackTrace(fa));\n\n  FrameArray<kMaxAddresses> faSafe;\n  CHECK(getStackTraceSafe(faSafe));\n\n  FrameArray<kMaxAddresses> faHeap;\n  CHECK(getStackTraceHeap(faHeap));\n\n  CHECK_EQ(fa.frameCount, faSafe.frameCount);\n  CHECK_EQ(fa.frameCount, faHeap.frameCount);\n\n  if (VLOG_IS_ON(1)) {\n    Symbolizer symbolizer;\n    OStreamSymbolizePrinter printer(std::cerr, SymbolizePrinter::COLOR_IF_TTY);\n\n    symbolizer.symbolize(fa);\n    VLOG(1) << \"getStackTrace\\n\";\n    printer.println(fa);\n\n    symbolizer.symbolize(faSafe);\n    VLOG(1) << \"getStackTraceSafe\\n\";\n    printer.println(faSafe);\n\n    symbolizer.symbolize(faHeap);\n    VLOG(1) << \"getStackTraceHeap\\n\";\n    printer.println(faHeap);\n  }\n\n  // Other than the top 2 frames (this one and getStackTrace /\n  // getStackTraceSafe), the stack traces should be identical\n  for (size_t i = 2; i < fa.frameCount; ++i) {\n    LOG(INFO) << \"i=\" << i << \" \" << std::hex << \"0x\" << fa.addresses[i]\n              << \" 0x\" << faSafe.addresses[i] << \" 0x\" << faHeap.addresses[i];\n    EXPECT_EQ(fa.addresses[i], faSafe.addresses[i]);\n    EXPECT_EQ(fa.addresses[i], faHeap.addresses[i]);\n  }\n}\n\nvoid foo1() {\n  foo2();\n}\n\nvoid foo2() {\n  verifyStackTraces();\n}\n\nvolatile bool handled = false;\nvoid handler(int /* num */, siginfo_t* /* info */, void* /* ctx */) {\n  // Yes, getStackTrace and VLOG aren't async-signal-safe, but signals\n  // raised with raise() aren't \"async\" signals.\n  foo1();\n  handled = true;\n}\n\nTEST(StackTraceTest, Simple) {\n  foo1();\n}\n\nTEST(StackTraceTest, Signal) {\n  if (folly::kIsSanitizeThread) {\n    // TSAN doesn't like signal-unsafe functions in a signal handler regardless\n    // of how the signal is raised. So skip the test in that case.\n    SKIP() << \"Not supported for TSAN\";\n  }\n  struct sigaction sa;\n  memset(&sa, 0, sizeof(sa));\n  sa.sa_sigaction = handler;\n  sa.sa_flags = SA_RESETHAND | SA_SIGINFO;\n  CHECK_ERR(sigaction(SIGUSR1, &sa, nullptr));\n  raise(SIGUSR1);\n  EXPECT_TRUE(handled);\n}\n\nssize_t read_all(int fd, uint8_t* buffer, size_t size) {\n  uint8_t* pos = buffer;\n  ssize_t bytes_read;\n  do {\n    bytes_read = fileops::read(fd, pos, size);\n    if (bytes_read < 0) {\n      if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {\n        continue;\n      }\n      return bytes_read;\n    }\n\n    pos += bytes_read;\n    size -= bytes_read;\n  } while (bytes_read > 0 && size > 0);\n\n  return pos - buffer;\n}\n\n// Returns the position in the file after done reading.\noff_t get_stack_trace(int fd, size_t file_pos, uint8_t* buffer, size_t count) {\n  off_t rv = lseek(fd, file_pos, SEEK_SET);\n  CHECK_EQ(rv, (off_t)file_pos);\n\n  // Subtract 1 from size of buffer to hold nullptr.\n  ssize_t bytes_read = read_all(fd, buffer, count - 1);\n  CHECK_GT(bytes_read, 0);\n  buffer[bytes_read] = '\\0';\n  return lseek(fd, 0, SEEK_CUR);\n}\n\ntemplate <class StackTracePrinter>\nvoid testStackTracePrinter(StackTracePrinter& printer, int fd) {\n  ASSERT_GT(fd, 0);\n\n  printer.printStackTrace(true);\n\n  std::array<uint8_t, 4000> first;\n  off_t pos = get_stack_trace(fd, 0, first.data(), first.size());\n  ASSERT_GT(pos, 0);\n\n  printer.printStackTrace(true);\n\n  std::array<uint8_t, 4000> second;\n  get_stack_trace(fd, pos, second.data(), second.size());\n\n  // The first two lines refer to this stack frame, which is different in the\n  // two cases, so strip those off.  The rest should be equal.\n  ASSERT_STREQ(\n      strchr(strchr((const char*)first.data(), '\\n') + 1, '\\n') + 1,\n      strchr(strchr((const char*)second.data(), '\\n') + 1, '\\n') + 1);\n}\n\nTEST(StackTraceTest, SafeStackTracePrinter) {\n  test::TemporaryFile file;\n\n  SafeStackTracePrinter printer{file.fd()};\n\n  testStackTracePrinter<SafeStackTracePrinter>(printer, file.fd());\n}\n\nTEST(StackTraceTest, FastStackTracePrinter) {\n  test::TemporaryFile file;\n\n  FastStackTracePrinter printer{\n      std::make_unique<FDSymbolizePrinter>(file.fd())};\n\n  testStackTracePrinter<FastStackTracePrinter>(printer, file.fd());\n}\n\nTEST(StackTraceTest, TerseStackTracePrinter) {\n  test::TemporaryFile file;\n\n  FastStackTracePrinter printer{\n      std::make_unique<FDSymbolizePrinter>(file.fd(), SymbolizePrinter::TERSE)};\n\n  testStackTracePrinter<FastStackTracePrinter>(printer, file.fd());\n}\n\nTEST(StackTraceTest, TerseFileAndLineStackTracePrinter) {\n  test::TemporaryFile file;\n\n  FastStackTracePrinter printer{std::make_unique<FDSymbolizePrinter>(\n      file.fd(), SymbolizePrinter::TERSE_FILE_AND_LINE)};\n\n  testStackTracePrinter<FastStackTracePrinter>(printer, file.fd());\n}\n\nTEST(StackTraceTest, TwoStepFastStackTracePrinter) {\n  test::TemporaryFile file;\n\n  TwoStepFastStackTracePrinter printer{\n      std::make_unique<FDSymbolizePrinter>(file.fd())};\n\n  testStackTracePrinter<TwoStepFastStackTracePrinter>(printer, file.fd());\n}\n\nTEST(StackTraceTest, TwoStepTerseStackTracePrinter) {\n  test::TemporaryFile file;\n\n  TwoStepFastStackTracePrinter printer{\n      std::make_unique<FDSymbolizePrinter>(file.fd(), SymbolizePrinter::TERSE)};\n\n  testStackTracePrinter<TwoStepFastStackTracePrinter>(printer, file.fd());\n}\n\nTEST(StackTraceTest, TwoStepTerseFileAndLineStackTracePrinter) {\n  test::TemporaryFile file;\n\n  TwoStepFastStackTracePrinter printer{std::make_unique<FDSymbolizePrinter>(\n      file.fd(), SymbolizePrinter::TERSE_FILE_AND_LINE)};\n\n  testStackTracePrinter<TwoStepFastStackTracePrinter>(printer, file.fd());\n}\n\nnamespace {\nconstexpr int frames = 5;\nFOLLY_NOINLINE void foo(FrameArray<frames>& addresses) {\n  getStackTraceSafe(addresses);\n}\n\nFOLLY_NOINLINE void bar(FrameArray<frames>& addresses) {\n  foo(addresses);\n}\n\nFOLLY_NOINLINE void baz(FrameArray<frames>& addresses) {\n  bar(addresses);\n}\n} // namespace\n\nTEST(StackTraceTest, TerseFileAndLineStackTracePrinterOutput) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(LocationInfoMode::FULL);\n  FrameArray<frames> addresses;\n  StringSymbolizePrinter printer(SymbolizePrinter::TERSE_FILE_AND_LINE);\n  baz(addresses);\n  symbolizer.symbolize(addresses);\n  printer.println(addresses, 0);\n\n  // Match a sequence of file+line results that should appear as:\n  // ./folly/debugging/symbolizer/test/StackTraceTest.cpp:202\n  // or:\n  // (unknown)\n  boost::regex regex(\"((([^:]*:[0-9]*)|(\\\\(unknown\\\\)))\\n)+\");\n  auto match = boost::regex_match(\n      printer.str(), regex, boost::regex_constants::match_not_dot_newline);\n\n  ASSERT_TRUE(match);\n}\n\nnamespace {\nFOLLY_ALWAYS_INLINE void verifyAsyncStackTraces() {\n  constexpr size_t kMaxAddresses = 100;\n  FrameArray<kMaxAddresses> fa;\n  CHECK(getAsyncStackTraceSafe(fa));\n\n  CHECK_GT(fa.frameCount, 0);\n\n  Symbolizer symbolizer;\n  symbolizer.symbolize(fa);\n  symbolizer::StringSymbolizePrinter printer;\n  printer.println(fa);\n  auto stackTraceStr = printer.str();\n\n  if (VLOG_IS_ON(1)) {\n    VLOG(1) << \"getAsyncStackTrace\\n\" << stackTraceStr;\n  }\n\n  // These functions should appear in this relative order\n  std::vector<std::string> funcNames{\n      \"funcF\",\n      \"funcE\",\n      \"co_funcD\",\n      \"co_funcC\",\n      \"funcB2_blocking\",\n      \"funcB1\",\n      \"co_funcB0\",\n      \"co_funcA2\",\n      \"co_funcA1\",\n      \"co_funcA0\",\n  };\n  std::vector<size_t> positions;\n  for (const auto& funcName : funcNames) {\n    SCOPED_TRACE(funcName);\n    auto pos = stackTraceStr.find(funcName);\n    ASSERT_NE(pos, std::string::npos);\n    positions.push_back(pos);\n  }\n  for (size_t i = 0; i < positions.size() - 1; ++i) {\n    ASSERT_LT(positions[i], positions[i + 1]);\n  }\n}\n\nFOLLY_NOINLINE void funcF() {\n  verifyAsyncStackTraces();\n}\n\nFOLLY_NOINLINE void funcE() {\n  funcF();\n  compiler_must_not_elide(0); // prevent tail-call above\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcD() {\n  funcE();\n  co_return;\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcC() {\n  co_await co_funcD();\n}\n\nFOLLY_NOINLINE void funcB2_blocking() {\n  // This should trigger a new AsyncStackRoot\n  folly::coro::blockingWait(co_funcC());\n}\n\nFOLLY_NOINLINE void funcB1() {\n  funcB2_blocking();\n  compiler_must_not_elide(0); // prevent tail-call above\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcB0() {\n  funcB1();\n  co_return;\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcA2() {\n  co_await co_funcB0();\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcA1() {\n  co_await co_funcA2();\n}\n\nFOLLY_NOINLINE folly::coro::Task<void> co_funcA0() {\n  co_await co_funcA1();\n}\n} // namespace\n\nTEST(StackTraceTest, AsyncStackTraceSimple) {\n  folly::coro::blockingWait(co_funcA0());\n}\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SymbolizedFrameTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n\n#include <glog/logging.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly::symbolizer;\n\nvoid checkPath(\n    std::string expectedPath,\n    std::string expectedBaseDir,\n    std::string expectedSubDir,\n    std::string expectedFile,\n    std::string rawBaseDir,\n    std::string rawSubDir,\n    std::string rawFile) {\n  Path path(rawBaseDir, rawSubDir, rawFile);\n\n  EXPECT_EQ(expectedBaseDir, path.baseDir())\n      << \"Path(\" << rawBaseDir << \", \" << rawSubDir << \", \" << rawFile << \")\";\n  EXPECT_EQ(expectedSubDir, path.subDir())\n      << \"Path(\" << rawBaseDir << \", \" << rawSubDir << \", \" << rawFile << \")\";\n  EXPECT_EQ(expectedFile, path.file())\n      << \"Path(\" << rawBaseDir << \", \" << rawSubDir << \", \" << rawFile << \")\";\n\n  EXPECT_EQ(expectedPath, path.toString());\n\n  // Check the `toBuffer` function.\n  char buf[1024];\n  size_t len;\n  len = path.toBuffer(buf, 1024);\n  EXPECT_EQ(expectedPath, std::string(buf, len));\n}\n\nTEST(SymbolizedFrame, Path) {\n  checkPath(\"hello.cpp\", \"\", \"\", \"hello.cpp\", \"\", \"\", \"hello.cpp\");\n  checkPath(\"foo/hello.cpp\", \"foo\", \"\", \"hello.cpp\", \"foo\", \"\", \"hello.cpp\");\n  checkPath(\"foo/hello.cpp\", \"foo\", \"\", \"hello.cpp\", \"\", \"foo\", \"hello.cpp\");\n  checkPath(\n      \"./////./////hello.cpp\",\n      \"./////\",\n      \"./////\",\n      \"hello.cpp\",\n      \"./////\",\n      \"./////\",\n      \"hello.cpp\");\n  checkPath(\n      \"/////./////hello.cpp\",\n      \"/////\",\n      \"./////\",\n      \"hello.cpp\",\n      \"/////\",\n      \"./////\",\n      \"hello.cpp\");\n  checkPath(\n      \"/./././././././hello.cpp\",\n      \"/./././././././\",\n      \"\",\n      \"hello.cpp\",\n      \"/./././././././\",\n      \"\",\n      \"hello.cpp\");\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SymbolizerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Symbolizer.h>\n\n#include <signal.h>\n#include <array>\n#include <cstdlib>\n\n#include <glog/logging.h>\n#include <folly/Demangle.h>\n#include <folly/Range.h>\n#include <folly/ScopeGuard.h>\n#include <folly/debugging/symbolizer/ElfCache.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n#include <folly/debugging/symbolizer/test/SymbolizerTestUtils.h>\n#include <folly/lang/Cast.h>\n#include <folly/portability/Filesystem.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/test/TestUtils.h>\n\n#if FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\nnamespace folly {\nnamespace symbolizer {\nnamespace test {\n\n#define SCOPED_TRACE_FRAMES(frames) \\\n  SCOPED_TRACE([&] {                \\\n    StringSymbolizePrinter printer; \\\n    printer.println(frames);        \\\n    return printer.str();           \\\n  }())\n\nvoid foo() {}\n\nFOLLY_NOINLINE void bar() {\n  std::array<int, 2> a = {{1, 2}};\n  // Use lfind, which is in a different library\n  int key = 1;\n  unsigned long nmemb = 2;\n  lfind(&key, a.data(), &nmemb, sizeof(int), testComparator);\n}\n\nTEST(Symbolizer, Single) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  // It looks like we could only use .debug_aranges with \"-g2\", with\n  // \"-g1 -gdwarf-aranges\", the code has to fallback to line-tables to\n  // get the file name.\n  Symbolizer symbolizer(LocationInfoMode::FULL);\n  SymbolizedFrame a;\n  ASSERT_TRUE(symbolizer.symbolize(reinterpret_cast<uintptr_t>(foo), a));\n  EXPECT_EQ(\"folly::symbolizer::test::foo()\", folly::demangle(a.name));\n\n  auto path = a.location.file.toString();\n  folly::StringPiece basename(path);\n  auto pos = basename.rfind('/');\n  if (pos != folly::StringPiece::npos) {\n    basename.advance(pos + 1);\n  }\n  EXPECT_EQ(\"SymbolizerTest.cpp\", basename.str());\n}\n\nTEST(Symbolizer, SingleCustomExePath) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  auto pid = ::fork();\n  if (pid == -1) {\n    SKIP(\"fork failed\");\n  } else if (pid == 0) {\n    // child process waits forever, parent will kill\n    folly::Baton<> baton;\n    baton.wait();\n  } else {\n    // parent process\n\n    // kill the child process on cleanup\n    auto guard = folly::makeGuard([pid] {\n      auto error = ::kill(pid, SIGKILL);\n      ASSERT_EQ(0, error);\n    });\n\n    // path to executable for child binary\n    auto exePath = folly::to<std::string>(\"/proc/\", pid, \"/exe\");\n\n    // It looks like we could only use .debug_aranges with \"-g2\", with\n    // \"-g1 -gdwarf-aranges\", the code has to fallback to line-tables to\n    // get the file name.\n    Symbolizer symbolizer(\n        nullptr, LocationInfoMode::FULL, 0, std::move(exePath));\n    SymbolizedFrame a;\n    ASSERT_TRUE(symbolizer.symbolize(reinterpret_cast<uintptr_t>(foo), a));\n    EXPECT_EQ(\"folly::symbolizer::test::foo()\", folly::demangle(a.name));\n\n    auto path = a.location.file.toString();\n    folly::StringPiece basename(path);\n    auto pos = basename.rfind('/');\n    if (pos != folly::StringPiece::npos) {\n      basename.advance(pos + 1);\n    }\n    EXPECT_EQ(\"SymbolizerTest.cpp\", basename.str());\n  }\n}\n\n// Test stack frames...\n\nclass ElfCacheTest : public testing::Test {\n protected:\n  void SetUp() override;\n};\n\n// Capture \"golden\" stack trace with default-configured Symbolizer\nFrameArray<100> goldenFrames;\n\nvoid ElfCacheTest::SetUp() {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  gComparatorGetStackTraceArg = &goldenFrames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  bar();\n\n  Symbolizer symbolizer;\n  symbolizer.symbolize(goldenFrames);\n  // At least 3 stack frames from us + getStackTrace()\n  ASSERT_LE(4, goldenFrames.frameCount);\n}\n\nvoid runElfCacheTest(Symbolizer& symbolizer) {\n  FrameArray<100> frames = goldenFrames;\n  for (size_t i = 0; i < frames.frameCount; ++i) {\n    frames.frames[i].clear();\n  }\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  ASSERT_LE(4, frames.frameCount);\n  for (size_t i = 1; i < 4; ++i) {\n    EXPECT_STREQ(goldenFrames.frames[i].name, frames.frames[i].name);\n  }\n}\n\nTEST_F(ElfCacheTest, ElfCache) {\n  ElfCache cache;\n  Symbolizer symbolizer(&cache);\n  for (size_t i = 0; i < 2; ++i) {\n    runElfCacheTest(symbolizer);\n  }\n}\n\nTEST_F(ElfCacheTest, SignalSafeElfCache) {\n  SignalSafeElfCache cache;\n  Symbolizer symbolizer(&cache);\n  for (size_t i = 0; i < 2; ++i) {\n    runElfCacheTest(symbolizer);\n  }\n}\n\nTEST(SymbolizerTest, SymbolCache) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL, 100);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  bar();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  FrameArray<100> frames2;\n  gComparatorGetStackTraceArg = &frames2;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  bar();\n  symbolizer.symbolize(frames2);\n  SCOPED_TRACE_FRAMES(frames2);\n\n  for (size_t i = 0; i < frames.frameCount; i++) {\n    EXPECT_STREQ(frames.frames[i].name, frames2.frames[i].name);\n  }\n}\n\nnamespace {\n\nvoid expectFrameEq(\n    SymbolizedFrame frame,\n    const std::string& shortName,\n    const std::string& fullName,\n    const std::string& file,\n    size_t lineno) {\n  auto normalizePath = [](std::string path) {\n    path = fs::lexically_normal(path).native();\n    return (path.find(\"./\", 0) != std::string::npos) ? path.substr(2) : path;\n  };\n  auto demangled = folly::demangle(frame.name);\n  EXPECT_TRUE(demangled == shortName || demangled == fullName)\n      << \"Found: demangled=\" << demangled << \" expecting shortName=\"\n      << shortName << \" or fullName=\" << fullName << \" address: \" << frame.addr\n      << \" hex(address): \" << std::hex << frame.addr;\n  // Use endsWith in case the build system adds extra paths in front.\n  EXPECT_TRUE(\n      folly::StringPiece(normalizePath(frame.location.file.toString()))\n          .endsWith(normalizePath(file)))\n      << ' ' << fullName << \" address: \" << frame.addr\n      << \" hex(address): \" << std::hex << frame.addr\n      << \" frame.location.file.toString(): \" << frame.location.file.toString()\n      << \" normalizePath(frame.location.file.toString()): \"\n      << normalizePath(frame.location.file.toString()) //\n      << \" file: \" << file //\n      << \" normalizePath(file): \" << normalizePath(file);\n  EXPECT_EQ(frame.location.line, lineno) << ' ' << fullName;\n}\n\ntemplate <size_t kNumFrames = 100>\nvoid expectFramesEq(\n    const FrameArray<kNumFrames>& frames1,\n    const FrameArray<kNumFrames>& frames2) {\n  EXPECT_EQ(frames1.frameCount, frames2.frameCount);\n  for (size_t i = 0; i < frames1.frameCount; i++) {\n    EXPECT_STREQ(frames1.frames[i].name, frames2.frames[i].name);\n  }\n}\n\ntemplate <size_t kNumFrames = 100>\nvoid printFrames(const FrameArray<kNumFrames>& frames) {\n  for (size_t i = 0; i < frames.frameCount; i++) {\n    auto& frame = frames.frames[i];\n    LOG(INFO) << std::hex << frame.addr << \": \" << frame.name << \" in \"\n              << frame.location.file.toString() << \":\" << std::dec\n              << frame.location.line;\n  }\n}\n\n} // namespace\n\nTEST(SymbolizerTest, InlineFunctionBasic) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_inlineB_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  printFrames(frames);\n\n  expectFrameEq(\n      frames.frames[4],\n      \"inlineA_lfind\",\n      \"folly::symbolizer::test::inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_lfind);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"inlineB_inlineA_lfind\",\n      \"folly::symbolizer::test::inlineB_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_inlineA_lfind);\n\n  FrameArray<100> frames2;\n  gComparatorGetStackTraceArg = &frames2;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_inlineB_inlineA_lfind();\n  symbolizer.symbolize(frames2);\n\n  expectFramesEq(frames, frames2);\n}\n\nFOLLY_NOINLINE void call_B_A_lfind() {\n  kLineno_inlineB_inlineA_lfind = __LINE__ + 1;\n  inlineB_inlineA_lfind();\n}\n\nTEST(SymbolizerTest, InlineFunctionWithoutEnoughFrames) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_B_A_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  std::array<SymbolizedFrame, 2> limitedFrames;\n\n  // The address of the line where call_B_A_lfind calls inlineB_inlineA_lfind.\n  symbolizer.symbolize(\n      folly::Range<const uintptr_t*>(&frames.addresses[4], 1),\n      folly::range(limitedFrames));\n\n  expectFrameEq(\n      limitedFrames[0],\n      \"inlineB_inlineA_lfind\",\n      \"folly::symbolizer::test::inlineB_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_inlineA_lfind);\n  expectFrameEq(\n      limitedFrames[1],\n      \"call_B_A_lfind\",\n      \"folly::symbolizer::test::call_B_A_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTest.cpp\",\n      kLineno_inlineB_inlineA_lfind);\n}\n\nTEST(SymbolizerTest, InlineFunctionInLexicalBlock) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_lexicalBlock_inlineB_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  expectFrameEq(\n      frames.frames[4],\n      \"inlineA_lfind\",\n      \"folly::symbolizer::test::inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_lfind);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"inlineB_inlineA_lfind\",\n      \"folly::symbolizer::test::inlineB_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_inlineA_lfind);\n\n  expectFrameEq(\n      frames.frames[6],\n      \"lexicalBlock_inlineB_inlineA_lfind\",\n      \"folly::symbolizer::test::lexicalBlock_inlineB_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils.cpp\",\n      kLineno_inlineB_inlineA_lfind);\n\n  printFrames(frames);\n}\n\nTEST(SymbolizerTest, InlineFunctionInDifferentCompilationUnit) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  // NOTE: inlineLTO_inlineA_lfind is only inlined with LTO/ThinLTO.\n  call_inlineLTO_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"inlineLTO_inlineA_lfind\",\n      \"folly::symbolizer::test::inlineLTO_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils.cpp\",\n      kLineno_inlineA_lfind);\n}\n\nTEST(SymbolizerTest, InlineClassMemberFunctionSameFile) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_same_file_memberInline_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  expectFrameEq(\n      frames.frames[4],\n      \"inlineA_lfind\",\n      \"folly::symbolizer::test::inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_lfind);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"memberInline_inlineA_lfind\",\n      \"folly::symbolizer::test::ClassSameFile::memberInline_inlineA_lfind() const\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils.cpp\",\n      kLineno_inlineA_lfind);\n}\n\nTEST(SymbolizerTest, StaticInlineClassMemberFunctionSameFile) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_same_file_staticMemberInline_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  expectFrameEq(\n      frames.frames[4],\n      \"inlineA_lfind\",\n      \"folly::symbolizer::test::inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_lfind);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"staticMemberInline_inlineA_lfind\",\n      \"folly::symbolizer::test::ClassSameFile::staticMemberInline_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils.cpp\",\n      kLineno_inlineA_lfind);\n}\n\nTEST(SymbolizerTest, InlineClassMemberFunctionInDifferentFile) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_different_file_memberInline_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  expectFrameEq(\n      frames.frames[4],\n      \"inlineA_lfind\",\n      \"folly::symbolizer::test::inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_lfind);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"memberInline_inlineA_lfind\",\n      \"folly::symbolizer::test::ClassDifferentFile::memberInline_inlineA_lfind() const\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_inlineA_lfind);\n}\n\nTEST(SymbolizerTest, StaticInlineClassMemberFunctionInDifferentFile) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_different_file_staticMemberInline_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  expectFrameEq(\n      frames.frames[4],\n      \"inlineA_lfind\",\n      \"folly::symbolizer::test::inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_lfind);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"staticMemberInline_inlineA_lfind\",\n      \"folly::symbolizer::test::ClassDifferentFile::staticMemberInline_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_inlineA_lfind);\n}\n\n// No inline frames should be filled because of no extra frames.\nTEST(SymbolizerTest, InlineFunctionNoExtraFrames) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100);\n  FrameArray<9> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<9>);\n  call_inlineB_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  Symbolizer symbolizer2(nullptr, LocationInfoMode::FULL, 100);\n  FrameArray<9> frames2;\n  gComparatorGetStackTraceArg = &frames2;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<9>);\n  call_inlineB_inlineA_lfind();\n  symbolizer2.symbolize(frames2);\n  SCOPED_TRACE_FRAMES(frames2);\n\n  expectFramesEq<9>(frames, frames2);\n}\n\nTEST(SymbolizerTest, InlineFunctionWithCache) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100);\n\n  FrameArray<100> frames;\n  gComparatorGetStackTraceArg = &frames;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_inlineB_inlineA_lfind();\n  symbolizer.symbolize(frames);\n  SCOPED_TRACE_FRAMES(frames);\n\n  expectFrameEq(\n      frames.frames[4],\n      \"inlineA_lfind\",\n      \"folly::symbolizer::test::inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_lfind);\n\n  expectFrameEq(\n      frames.frames[5],\n      \"inlineB_inlineA_lfind\",\n      \"folly::symbolizer::test::inlineB_inlineA_lfind()\",\n      \"folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h\",\n      kLineno_inlineA_lfind);\n\n  FrameArray<100> frames2;\n  gComparatorGetStackTraceArg = &frames2;\n  gComparatorGetStackTrace =\n      reinterpret_function_cast<bool(void*)>(getStackTrace<100>);\n  call_inlineB_inlineA_lfind();\n  symbolizer.symbolize(frames2);\n  expectFramesEq(frames, frames2);\n}\n\nint64_t functionWithTwoParameters(size_t a, int32_t b) {\n  return a + b;\n}\n\nTEST(Dwarf, FindParameterNames) {\n  SKIP_IF(!Symbolizer::isAvailable());\n\n  ElfCache elfCache;\n\n  auto address = reinterpret_cast<uintptr_t>(functionWithTwoParameters);\n  Symbolizer symbolizer;\n  SymbolizedFrame frame;\n  ASSERT_TRUE(symbolizer.symbolize(address, frame));\n\n  std::vector<folly::StringPiece> names;\n  Dwarf dwarf(&elfCache, frame.file.get());\n\n  folly::Range<SymbolizedFrame*> extraInlineFrames = {};\n  dwarf.findAddress(\n      frame.addr,\n      LocationInfoMode::FAST,\n      frame,\n      extraInlineFrames,\n      [&](const folly::StringPiece name) { names.push_back(name); });\n\n  if (names.empty()) {\n    // When using -fsplit-dwarf-inlining info about parameters will not be\n    // emitted for the inlined debug info.\n    return;\n  }\n\n  ASSERT_EQ(2, names.size());\n  ASSERT_EQ(\"a\", names[0]);\n  ASSERT_EQ(\"b\", names[1]);\n}\n\n#undef SCOPED_TRACE_FRAMES\n\n} // namespace test\n} // namespace symbolizer\n} // namespace folly\n\n#endif // FOLLY_HAVE_ELF && FOLLY_HAVE_DWARF\n\n// Can't use initFacebookLight since that would install its own signal handlers\n// Can't use initFacebookNoSignals since we cannot depend on common\nint main(int argc, char** argv) {\n  ::testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// NOTE: To simplify generated DWARF keep #includes to a minimum.\n\n#pragma once\n\n#include <folly/debugging/symbolizer/test/SymbolizerTestUtils.h>\n\nextern \"C\" {\n// Fwd declare instead of #include <stdlib.h> to minimize generated DWARF.\nvoid* lfind(\n    const void* key,\n    const void* base,\n    unsigned long* nmemb,\n    unsigned long size,\n    int (*compar)(const void*, const void*));\n\n} // \"C\"\n\nnamespace folly {\nnamespace symbolizer {\nnamespace test {\n\n/*\n * Put the inline functions definition in a separate -inl.h file to cover test\n * cases that define and declare inline functions in different files.\n */\n\n__attribute__((__always_inline__)) inline void inlineA_lfind() {\n  int a[2] = {1, 2};\n  // Use lfind, which is in a different library\n  int key = 1;\n  unsigned long nmemb = 2;\n  kLineno_lfind = __LINE__ + 1;\n  lfind(&key, a, &nmemb, sizeof(int), testComparator);\n}\n\n__attribute__((__always_inline__)) inline void inlineB_inlineA_lfind() {\n  kLineno_inlineA_lfind = __LINE__ + 1;\n  inlineA_lfind();\n}\n\n__attribute__((__always_inline__)) inline void\nClassDifferentFile::memberInline_inlineA_lfind() const {\n  kLineno_inlineA_lfind = __LINE__ + 1;\n  inlineA_lfind();\n}\n\n/* static */ __attribute__((__always_inline__)) inline void\nClassDifferentFile::staticMemberInline_inlineA_lfind() {\n  kLineno_inlineA_lfind = __LINE__ + 1;\n  inlineA_lfind();\n}\n\n} // namespace test\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SymbolizerTestUtils.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/test/SymbolizerTestUtils.h>\n\n// NOTE: To simplify generated DWARF keep #includes to a minimum.\n\nnamespace folly {\nnamespace symbolizer {\nnamespace test {\n\nbool (*gComparatorGetStackTrace)(void* arg) = nullptr;\nvoid* gComparatorGetStackTraceArg = nullptr;\n\nint testComparator(const void* ap, const void* bp) {\n  // This comparator is called for the side effect of capturing the stack trace.\n  // The function that calls it: lfind usually lives in a separate library -\n  // which exercises cross-ELF file support.\n  (*gComparatorGetStackTrace)(gComparatorGetStackTraceArg);\n\n  int a = *static_cast<const int*>(ap);\n  int b = *static_cast<const int*>(bp);\n  // which returns zero if the key object matches the array member, and nonzero\n  // otherwise.\n  return (a == b) ? 0 : 1;\n  // return a < b ? -1 : a > b ? 1 : 0;\n}\n\nint kLineno_lfind = 0;\nint kLineno_inlineA_lfind = 0;\nint kLineno_inlineB_inlineA_lfind = 0;\n\n// NOTE: inlineLTO_inlineA_lfind is only inlined with LTO/ThinLTO.\nvoid inlineLTO_inlineA_lfind() {\n  kLineno_inlineA_lfind = __LINE__ + 1;\n  inlineA_lfind();\n}\n\nvoid call_inlineA_lfind() {\n  inlineA_lfind();\n}\n\nvoid call_inlineB_inlineA_lfind() {\n  inlineB_inlineA_lfind();\n}\n\nvoid call_inlineLTO_inlineA_lfind() {\n  inlineLTO_inlineA_lfind();\n}\n\nclass ClassSameFile {\n public:\n  __attribute__((__always_inline__)) void memberInline_inlineA_lfind() const {\n    kLineno_inlineA_lfind = __LINE__ + 1;\n    inlineA_lfind();\n  }\n\n  __attribute__((__always_inline__)) static void\n  staticMemberInline_inlineA_lfind() {\n    kLineno_inlineA_lfind = __LINE__ + 1;\n    inlineA_lfind();\n  }\n\n  int dummy() const { return dummy_; }\n  int dummy_ = 0;\n};\n\nvoid call_same_file_memberInline_inlineA_lfind() {\n  ClassSameFile obj;\n  obj.memberInline_inlineA_lfind();\n}\n\nvoid call_same_file_staticMemberInline_inlineA_lfind() {\n  ClassSameFile::staticMemberInline_inlineA_lfind();\n}\n\nvoid call_different_file_memberInline_inlineA_lfind() {\n  ClassDifferentFile obj;\n  obj.memberInline_inlineA_lfind();\n}\n\nvoid call_different_file_staticMemberInline_inlineA_lfind() {\n  ClassDifferentFile::staticMemberInline_inlineA_lfind();\n}\n\nvoid lexicalBlock_inlineB_inlineA_lfind() try {\n  kLineno_inlineB_inlineA_lfind = __LINE__ + 1;\n  inlineB_inlineA_lfind();\n} catch (...) {\n}\n\nvoid call_lexicalBlock_inlineB_inlineA_lfind() {\n  lexicalBlock_inlineB_inlineA_lfind();\n}\n\n} // namespace test\n} // namespace symbolizer\n} // namespace folly\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/SymbolizerTestUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// NOTE: To simplify generated DWARF keep #includes to a minimum.\n\nnamespace folly {\nnamespace symbolizer {\nnamespace test {\n\n// Tests capture a stack trace from @testComparator by calling\n// gComparatorGetStackTrace(gComparatorGetStackTraceArg).\nextern bool (*gComparatorGetStackTrace)(void* arg);\nextern void* gComparatorGetStackTraceArg;\nint testComparator(const void* ap, const void* bp);\n\n// Set to the line number in the caller function when calling\n// lfind/inlineA_lfind/inlineB_inlineA_lfind.\nextern int kLineno_lfind;\nextern int kLineno_inlineA_lfind;\nextern int kLineno_inlineB_inlineA_lfind;\n\n// Debug Info for inlined functions is emitted in the functions where they're\n// inlined in. The SymbolizeTest.o object file has many dependencies and will\n// have a huge amount of debug info. To simplify debugging call inlined\n// functions through these trampolines so that all debug info worth inspecting\n// is emitted in the tiny SymbolizerTestUtils.o\nvoid call_inlineA_lfind();\nvoid call_inlineB_inlineA_lfind();\nvoid call_inlineLTO_inlineA_lfind();\nvoid call_same_file_memberInline_inlineA_lfind();\nvoid call_same_file_staticMemberInline_inlineA_lfind();\nvoid call_different_file_memberInline_inlineA_lfind();\nvoid call_different_file_staticMemberInline_inlineA_lfind();\nvoid call_lexicalBlock_inlineB_inlineA_lfind();\n\n// NOTE: inlineLTO_inlineA_lfind is only inlined with LTO/ThinLTO.\nvoid inlineLTO_inlineA_lfind();\n\nclass ClassDifferentFile {\n public:\n  __attribute__((__always_inline__)) inline void memberInline_inlineA_lfind()\n      const;\n  __attribute__((__always_inline__)) inline static void\n  staticMemberInline_inlineA_lfind();\n\n  int dummy() const { return dummy_; }\n  int dummy_ = 0;\n};\n\n} // namespace test\n} // namespace symbolizer\n} // namespace folly\n\n#include <folly/debugging/symbolizer/test/SymbolizerTestUtils-inl.h>\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/compare-addr2line.sh",
    "content": "#!/bin/bash\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nFOLLY_ADDR2LINE=\"$1\"\nLLVM_ADDR2LINE=\"$2\"\nBINARY_TO_SYMBOLIZE=\"$3\"\n\nN=100\nADDRS=(\"$@\")\nADDRS=(\"${ADDRS[@]:3}\")\n\nif [[ ${#ADDRS[@]} == 0 ]]; then\n    SYMBOLS=$(readelf -s \"$BINARY_TO_SYMBOLIZE\")\n    # An address may be part of multiple functions.\n    # folly-addr2line and llvm-addr2line may choose either version leading to a comparison failure.\n    # Only compare symbolization output if a single function matches the address.\n    FUNCTION_ADDR_RANGES=$(echo \"$SYMBOLS\" | grep -E 'FUNC +GLOBAL +DEFAULT +[0-9]+' | awk '{print $2 \" \" $3}' | sort)\n    UNIQUE_ADDR_RANGES=$(echo \"$SYMBOLS\" | awk '{print $2 \" \" $3}' | sort | uniq -u)\n    FUNCTION_UNIQUE_ADDR_RANGES=$(comm -12 <(echo \"$FUNCTION_ADDR_RANGES\") <(echo \"$UNIQUE_ADDR_RANGES\") | shuf -n \"$N\")\n    if [[ -z \"$FUNCTION_UNIQUE_ADDR_RANGES\" ]]; then\n        exit 0\n    fi\n    readarray -t ADDRS < <(\n        echo \"$FUNCTION_UNIQUE_ADDR_RANGES\" | shuf -n \"$N\" | while read -r start_hex size; do\n            start=\"$((16#$start_hex))\"\n            end=\"$((start + size))\"\n            printf '0x%x\\n' \"$(seq \"$start\" \"$end\" | shuf -n 1)\"\n        done\n    )\nfi\n\necho ADDRS: \"${ADDRS[@]}\"\n\nset -x\nset -o pipefail\n\nif command -v wdiff &> /dev/null\nthen\n    DIFF=wdiff\nelse\n    DIFF=\"diff -U 10\"\nfi\n\nif command -v colordiff &> /dev/null\nthen\n    COLORDIFF=\"colordiff\"\nelse\n    COLORDIFF=\"cat\"\nfi\n\n$DIFF <(\"$FOLLY_ADDR2LINE\" -e \"$BINARY_TO_SYMBOLIZE\" -i -f -a \"${ADDRS[@]}\") <(\"$LLVM_ADDR2LINE\" -e \"$BINARY_TO_SYMBOLIZE\" -i -f -a \"${ADDRS[@]}\" | sed -E 's| \\(discriminator [0-9]+\\)||g') | \"$COLORDIFF\"\n\n# Test that reading from stdin and args yields the same result.\n$DIFF \\\n    <(\"$FOLLY_ADDR2LINE\" -e \"$BINARY_TO_SYMBOLIZE\" -i -f -a \"${ADDRS[@]}\") \\\n    <(printf '%s\\n' \"${ADDRS[@]}\" | \"$FOLLY_ADDR2LINE\" -e \"$BINARY_TO_SYMBOLIZE\" -i -f -a)\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/gnu_debuglink_test.sh",
    "content": "#!/bin/bash\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ncrash=\"$1\"\n\nuuid=$(cat /proc/sys/kernel/random/uuid)\nfor sig in 1 2 3 13 15; do eval \"trap 'exit $((sig + 128))' $sig\"; done\ntrap 'rm -f \"$crash.debuginfo.$uuid\" \"$crash.strip.$uuid\"' 0\n\nobjcopy --only-keep-debug \"$crash\" \"$crash.debuginfo.$uuid\"\nobjcopy --strip-debug --add-gnu-debuglink=\"$crash.debuginfo.$uuid\" \"$crash\" \"$crash.strip.$uuid\"\n\necho '{\"op\":\"start\",\"test\":\"gnu_debuglink_test\"}';\nstart=$(date +%s)\nif \"$crash.strip.$uuid\" 2>&1 | grep -q 'Crash.cpp:[0-9]*$'; then\n    result='\"status\":\"passed\"';\nelse\n    result='\"status\":\"failed\"';\nfi\nend=$(date +%s)\necho '{\"op\":\"test_done\",\"test\":\"gnu_debuglink_test\",'\"$result\"'}'\necho '{\"op\":\"all_done\",\"results\":[{\"name\":\"gnu_debuglink_test\",'\"$result\"',\"start_time\":'\"$start\"',\"end_time\":'\"$end\"',\"details\":\"nothing\"}]}'\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/symbolizer_dwp_compability.sh",
    "content": "#!/bin/bash\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nBINARY=\"$1\"\nDWP=\"$2\"\n\nNEW_BINARY=$(dirname \"$DWP\")\n# Keep the binary file with the dwp file in the same directory.\ncp \"$BINARY\" \"$NEW_BINARY\"\nNEW_BINARY+=\"/$(basename \"$BINARY\")\"\n\necho \"$NEW_BINARY\" \"$DWP\"\n\nOUTPUT=$(tr -d '[:space:]' <<<$($NEW_BINARY))\necho \"Unit test output is \" \"$OUTPUT\"\n[[ $OUTPUT =~ \"[PASSED]16tests\" ]] && exit 0 || exit 5\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/tool/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"segv\",\n    srcs = [\"segv.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/debugging/symbolizer:signal_handler\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:unistd\",\n    ],\n)\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/tool/segv.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstdlib>\n#include <cstring>\n\n#ifndef _WIN32\n#include <signal.h>\n#endif\n\n#include <folly/debugging/symbolizer/SignalHandler.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/Unistd.h>\n\nstatic bool has_opt(\n    int const argc, char const* const* const argv, char const* const name) {\n  for (int i = 1; i < argc; ++i) {\n    auto const arg = argv[i];\n    if (arg[0] == '-' && arg[1] == '-' && strcmp(arg + 2, name) == 0) {\n      return true;\n    }\n  }\n  return false;\n}\n\n[[maybe_unused]] static constexpr size_t kPageSize = 4096;\n\nint main(int const argc, char const* const* const argv) {\n  fprintf(stderr, \"%d\\n\", int(getpid()));\n  fflush(stderr);\n\n#ifndef _WIN32\n  if (has_opt(argc, argv, \"altstack\")) {\n    auto const perms = PROT_READ | PROT_WRITE;\n    auto const flags = MAP_ANONYMOUS | MAP_PRIVATE;\n    auto const alloc = mmap(nullptr, kPageSize * 4, perms, flags, -1, 0);\n    stack_t ss;\n    ss.ss_sp = alloc;\n    ss.ss_size = kPageSize * 4;\n    ss.ss_flags = 0;\n    sigaltstack(&ss, nullptr);\n  }\n#endif\n\n  if (has_opt(argc, argv, \"handle\")) {\n    folly::symbolizer::installFatalSignalHandler();\n  }\n\n  auto const perms = PROT_NONE;\n  auto const flags = MAP_ANONYMOUS | MAP_PRIVATE;\n  auto const alloc = mmap(nullptr, kPageSize, perms, flags, -1, 0);\n  return *static_cast<int const*>(alloc); // read the first int: segv\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/test/validate_folly_symbolizer.bzl",
    "content": "load(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbcode_macros//build_defs:custom_unittest.bzl\", \"custom_unittest\")\n\nSPLIT_DWARF_FLAGS = {\n    \"none\": [\"-gno-split-dwarf\"],\n    \"single_inlining\": [\n        \"-gsplit-dwarf=single\",\n        \"-fsplit-dwarf-inlining\",\n    ],\n    \"single_no_inlining\": [\n        \"-gsplit-dwarf=single\",\n        \"-fno-split-dwarf-inlining\",\n    ],\n    \"split_no_inlining\": [\n        \"-gsplit-dwarf=split\",\n        \"-fno-split-dwarf-inlining\",\n    ],\n}\n\ndef _dwarf_size_flag(size):\n    if size == 32:\n        return []\n    else:\n        return [\"-gdwarf{}\".format(size)]\n\ndef customized_unittest(\n        available_dwarf_versions = [],\n        avilable_split_dwarf_keys = [],\n        available_dwarf_sizes = [],\n        extra_compiler_flags = [],\n        custom_suffix = \"\"):\n    # Testing different combinations of the following options:\n    # 1. Dwarf4 or Dwarf5.\n    # 2. Dwarf32 or Dwarf 64.\n    # 3. Split dwarf options.\n    # 4. Use aaranges or not.\n    for dwarf_version in available_dwarf_versions:\n        for dwarf_size in available_dwarf_sizes:\n            for split_dwarf_option in avilable_split_dwarf_keys:\n                # buck2 doesn't support split version yet.\n                if split_dwarf_option == \"none\" or split_dwarf_option == \"single_inlining\":\n                    for use_aaranges in [False, True]:\n                        cpp_library(\n                            name = \"symbolizer_test_utils_\" + dwarf_version +\n                                   \"_dwarf{}\".format(dwarf_size) +\n                                   (\"\" if split_dwarf_option == \"none\" else \"_\" + split_dwarf_option) +\n                                   (\"_aaranges\" if use_aaranges else \"_noaaranges\") + custom_suffix,\n                            srcs = [\"SymbolizerTestUtils.cpp\"],\n                            headers = [\n                                \"SymbolizerTestUtils.h\",\n                                \"SymbolizerTestUtils-inl.h\",\n                            ],\n                            # Tests rely on this library having full debug info, so use `-g` to override\n                            # the platform default, and use `--emit-relocs` to prevent `--strip-debug-*`\n                            # flags from dropping debug info.\n                            compiler_flags = [\"-g\"] +\n                                             ([\"-gdwarf-5\"] if dwarf_version == \"dwarf5\" else [\"-gdwarf-4\"]) +\n                                             _dwarf_size_flag(dwarf_size) +\n                                             SPLIT_DWARF_FLAGS[split_dwarf_option] +\n                                             ([\"-gdwarf-aranges\"] if use_aaranges else []) +\n                                             extra_compiler_flags,\n                            private_linker_flags = [\n                                \"--emit-relocs\",  # makes linker ignore `--strip-debug-*` flags\n                            ],\n                            target_compatible_with = [\"fbcode//opensource/macros:broken-in-oss\"],\n                        )\n                        cpp_unittest(\n                            name = \"symbolizer_test_\" + dwarf_version +\n                                   \"_dwarf{}\".format(dwarf_size) +\n                                   (\"\" if split_dwarf_option == \"none\" else \"_\" + split_dwarf_option) +\n                                   (\"_aaranges\" if use_aaranges else \"_noaaranges\") + custom_suffix,\n                            srcs = [\"SymbolizerTest.cpp\"],\n                            # This tests requires full debug info, so use `-g` to override the platform\n                            # default, and use `--emit-relocs` to prevent `--strip-debug-*` flags from\n                            # dropping debug info.\n                            compiler_flags = [\"-g\"] +\n                                             ([\"-gdwarf-5\"] if dwarf_version == \"dwarf5\" else [\"-gdwarf-4\"]) +\n                                             _dwarf_size_flag(dwarf_size) +\n                                             SPLIT_DWARF_FLAGS[split_dwarf_option] +\n                                             ([\"-gdwarf-aranges\"] if use_aaranges else []) + extra_compiler_flags,\n                            labels = [\"dwp\"] if split_dwarf_option == \"single_inlining\" and use_aaranges else [],\n                            linker_flags = [\n                                \"--emit-relocs\",  # makes linker ignore `--strip-debug-*` flags\n                            ],\n                            supports_static_listing = True,\n                            target_compatible_with = [\"fbcode//opensource/macros:broken-in-oss\"],\n                            deps = [\n                                \":symbolizer_test_utils_\" + dwarf_version +\n                                \"_dwarf{}\".format(dwarf_size) +\n                                (\"\" if split_dwarf_option == \"none\" else \"_\" + split_dwarf_option) +\n                                (\"_aaranges\" if use_aaranges else \"_noaaranges\"),  # @manual\n                                \"//folly:demangle\",\n                                \"//folly:range\",\n                                \"//folly:scope_guard\",\n                                \"//folly:string\",\n                                \"//folly/debugging/symbolizer:elf_cache\",\n                                \"//folly/debugging/symbolizer:symbolized_frame\",\n                                \"//folly/debugging/symbolizer:symbolizer\",\n                                \"//folly/debugging/symbolizer/detail:debug\",\n                                \"//folly/lang:cast\",\n                                \"//folly/portability:filesystem\",\n                                \"//folly/portability:gtest\",\n                                \"//folly/portability:unistd\",\n                                \"//folly/synchronization:baton\",\n                                \"//folly/test:test_utils\",\n                                \"fbsource//third-party/glog:glog\",\n                            ],\n                        )\n\ndef validate_folly_symbolizer(name, binary):\n    custom_unittest(\n        name = name,\n        command = [\n            \"$(exe //folly/debugging/symbolizer/test:compare-addr2line.sh)\",\n            \"$(location //folly/debugging/symbolizer/tool:folly-addr2line)\",\n            \"$(location //third-party-buck/platform010/build/llvm-fb/15:bin/llvm-addr2line)\",\n            \"$(location {})\".format(binary),\n        ],\n        type = \"simple\",\n    )\n\ndef validate_symbolizer_dwp(name, binary):\n    custom_unittest(\n        name = name,\n        command = [\n            \"$(exe //folly/debugging/symbolizer/test:symbolizer_dwp_compability.sh)\",\n            \"$(location {})\".format(binary),\n            \"$(location {}[dwp])\".format(binary),\n        ],\n        type = \"simple\",\n        # Only test in opt mode.\n        # In dev mode, the test still depends on the shared libraries except\n        # binary + dwp file.\n        target_compatible_with = [\"ovr_config//build_mode/constraints:opt\"],\n    )\n"
  },
  {
    "path": "folly/debugging/symbolizer/tool/Addr2Line.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Minimal addr2line using folly::symbolizer. Used for tests.\n *\n *    folly-addr2line -e objectfile --demangle <addr1> [<addr2> ...]\n */\n\n#include <algorithm>\n#include <array>\n#include <cstdlib>\n#include <iostream>\n\n#include <folly/Range.h>\n#include <folly/debugging/symbolizer/Dwarf.h>\n#include <folly/debugging/symbolizer/Elf.h>\n#include <folly/debugging/symbolizer/SymbolizedFrame.h>\n#include <folly/debugging/symbolizer/Symbolizer.h>\n#include <folly/init/Init.h>\n#include <folly/portability/GFlags.h>\n\n#if FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\nDEFINE_string(e, \"\", \"Path to ELF object file (.so, binary)\");\nDEFINE_bool(demangle, false, \"Degmangle symbols\");\nDEFINE_bool(C, false, \"Degmangle symbols\");\nDEFINE_bool(a, false, \"Show addresses\");\nDEFINE_bool(i, false, \"Unwind inlined functions\");\nDEFINE_bool(f, false, \"Show function names\");\n\nusing namespace folly::symbolizer;\n\nbool shouldDemangle() {\n  return FLAGS_demangle || FLAGS_C;\n}\n\nvoid addr2line(std::shared_ptr<ElfFile> elfFile, uintptr_t address) {\n  if (elfFile->getSectionContainingAddress(address) == nullptr) {\n    if (FLAGS_a) {\n      std::cout << \"0x\" << std::hex << address << std::dec << \"\\n\";\n    }\n    if (FLAGS_f) {\n      std::cout << \"??\\n\";\n    }\n    std::cout << \"??:0\\n\";\n    return;\n  }\n\n  std::array<SymbolizedFrame, 100> frames;\n  frames[0].found = true;\n  frames[0].addr = address;\n  frames[0].file = elfFile;\n  frames[0].name =\n      elfFile->getSymbolName(elfFile->getDefinitionByAddress(address));\n\n  ElfCache cache;\n  Dwarf(&cache, elfFile.get())\n      .findAddress(\n          address,\n          FLAGS_i ? LocationInfoMode::FULL_WITH_INLINE : LocationInfoMode::FULL,\n          frames[0],\n          /* extraInlineFrames */ folly::range(frames).subpiece(1));\n\n  size_t n = 0;\n  while (frames[n].found) {\n    n++;\n  }\n  if (!FLAGS_i) {\n    CHECK_LE(n, 1);\n  }\n\n  // Inlined frames [1,n) are filled in deepest call first order,\n  // but frames[0] is used for the non-inlined caller.\n  // Move frames[0] to the end so the entire array is ordered.\n  std::rotate(&frames[0], &frames[1], &frames[0] + n);\n\n  CHECK_NE(n, 0);\n  if (FLAGS_a) {\n    std::cout << \"0x\" << std::hex << address << std::dec << '\\n';\n  }\n  for (size_t i = 0; i < n; i++) {\n    const auto& f = frames[i];\n    if (FLAGS_f) {\n      std::cout\n          << (f.name ? (shouldDemangle() ? folly::demangle(f.name) : f.name)\n                     : \"??\")\n          << '\\n';\n    }\n    auto path = f.location.file.toString();\n    path = path.empty() ? \"??\" : path;\n    std::cout << path << \":\" << f.location.line << '\\n';\n  }\n}\n\nint main(int argc, char* argv[]) {\n  folly::Init init(&argc, &argv);\n  auto elfFile = std::make_shared<ElfFile>(FLAGS_e.c_str());\n\n  if (argc == 1) {\n    for (std::string line; std::getline(std::cin, line);) {\n      addr2line(elfFile, strtoll(line.c_str(), nullptr, 0));\n    }\n  } else {\n    for (int i = 1; i < argc; i++) {\n      addr2line(elfFile, strtoll(argv[i], nullptr, 0));\n    }\n  }\n\n  return 0;\n}\n\n#else // FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n\nint main(int, char*[]) {\n  return 1;\n}\n\n#endif // FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF\n"
  },
  {
    "path": "folly/debugging/symbolizer/tool/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:export_files.bzl\", \"export_file\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"folly-addr2line\",\n    srcs = [\n        \"Addr2Line.cpp\",\n    ],\n    deps = [\n        \"//folly:range\",\n        \"//folly/debugging/symbolizer:dwarf\",\n        \"//folly/debugging/symbolizer:elf\",\n        \"//folly/debugging/symbolizer:symbolized_frame\",\n        \"//folly/debugging/symbolizer:symbolizer\",\n        \"//folly/init:init\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = export_file,\n    name = \"libFollySegFault.so.v\",\n)\n\n# Build a standlone DSO for use w/ `LD_PRELOAD`.\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"libFollySegFault.so\",\n    srcs = [\n        \"LibSegFault.cpp\",\n    ],\n    compiler_flags = [\n        # Allow using the elvis operator.\n        \"-Wno-gnu-conditional-omitted-operand\",\n    ],\n    dlopen_enabled = True,\n    linker_flags = [\n\n        # Trim runtime `DT_NEEDED` deps.\n        \"--as-needed\",\n\n        # Use a version script to prevent exporting any symbols.\n        \"--version-script=$(location :libFollySegFault.so.v)\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//runtime:fbcode\": [\n            # This how we can statically link `libstdc++.so` w/ the fbcode\n            # toolchain.\n            \"--push-state\",\n            \"-Bstatic\",\n            \"-lstdc++_pic\",\n            \"--pop-state\",\n        ],\n    }),\n    # Statically link libstdc++ to avoid an external dep which could make\n    # this less flexible.\n    os_linker_flags = [\n        (\n            \"linux\",\n            select({\n                \"DEFAULT\": [\n                    \"-static-libstdc++\",\n                ],\n                \"ovr_config//runtime:fbcode\": [],\n            }),\n        ),\n    ],\n    target_compatible_with = [\"fbcode//opensource/macros:broken-in-oss\"],\n    deps = [\n        \"//folly:range\",\n        \"//folly/debugging/symbolizer:signal_handler\",\n    ],\n)\n"
  },
  {
    "path": "folly/debugging/symbolizer/tool/LibSegFault.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A standalone shared library that can be `LD_PRELOAD`d to print symbolized\n * native stack traces when the process dies due to a signal. By default, this\n * is enabled for SIGSEGV, SIGILL, SIGBUS, SIGSTKFLT, SIGABRT, and SIGFPE.\n *\n * Based on glibc's `libSegFault.so`:\n * https://github.com/lattera/glibc/blob/master/debug/segfault.c\n *\n * Usage:\n *\n *   $ LD_PRELOAD=libFollySegFault.so <my_prog> ...\n *\n */\n\n#include <folly/debugging/symbolizer/SignalHandler.h>\n\n#include <bitset>\n#include <csignal>\n#include <cstdlib>\n#include <string_view>\n#include <unordered_map>\n\n#include <folly/Range.h>\n\nstatic void __attribute__((constructor)) install_handler(void) {\n  static const std::unordered_map<std::string_view, int> signalMap = {\n      {\"segv\", SIGSEGV},\n      {\"ill\", SIGILL},\n#ifdef SIGBUS\n      {\"bus\", SIGBUS},\n#endif\n#ifdef SIGSTKFLT\n      {\"stkflt\", SIGSTKFLT},\n#endif\n      {\"abrt\", SIGABRT},\n      {\"fpe\", SIGFPE},\n  };\n\n  std::bitset<64> sigMask;\n\n  // The string description of signals to install the handler for.\n  folly::StringPiece sigEnv = std::getenv(\"FOLLY_SEGFAULT_SIGNALS\") ?: \"all\";\n  while (!sigEnv.empty()) {\n    std::string_view sigName = sigEnv.split_step(\" \");\n\n    // If signame of all was given, add in all signals.\n    if (sigName == \"all\") {\n      for (const auto& ent : signalMap) {\n        sigMask.set(ent.second);\n      }\n    } else {\n      auto sig = signalMap.find(sigName);\n      if (sig == signalMap.end()) {\n        fprintf(\n            stderr,\n            \"unknown signal: \\\"%.*s\\\"\\n\",\n            static_cast<int>(sigName.length()),\n            sigName.data());\n        std::abort();\n      }\n      sigMask.set(sig->second);\n    }\n  }\n\n  folly::symbolizer::installFatalSignalHandler(sigMask);\n}\n"
  },
  {
    "path": "folly/debugging/symbolizer/tool/libFollySegFault.so.v",
    "content": "{\n  /* We don't export anything, since our constructor does all the work. */\n  local: *;\n};\n"
  },
  {
    "path": "folly/defs.bzl",
    "content": "\"\"\"Provides helper functions for the folly library\n[folly]\n    have_libgflags_override = {True|[False]}\n\"\"\"\n\nload(\"@fbsource//tools/build_defs:buckconfig.bzl\", \"read_bool\")\nload(\n    \"@fbsource//tools/build_defs:default_platform_defs.bzl\",\n    \"ANDROID\",\n    \"APPLE\",\n    \"APPLETVOS\",\n    \"CXX\",\n    \"FBCODE\",\n    \"IOS\",\n    \"MACOSX\",\n    \"WINDOWS\",\n)\nload(\"@fbsource//tools/build_defs:fb_xplat_cxx_binary.bzl\", \"fb_xplat_cxx_binary\")\nload(\"@fbsource//tools/build_defs:fb_xplat_cxx_library.bzl\", \"fb_xplat_cxx_library\")\nload(\"@fbsource//tools/build_defs:fb_xplat_cxx_test.bzl\", \"fb_xplat_cxx_test\")\nload(\"@fbsource//tools/build_defs/dirsync:dirsync_redirect.bzl\", \"dirsync_redirect\")\n\ndef should_enable_gflags():\n    return read_bool(\"folly\", \"have_libgflags_override\", False)\n\ndef is_folly_mobile_flag():\n    return native.read_config(\"cpp_flags\", \"preprocessing\", \"\") == \"DFOLLY_MOBILE\"\n\ndef cpp_flags():\n    flags = [\n        \"-DFOLLY_HAVE_LIBJEMALLOC=0\",\n        \"-DFOLLY_HAVE_PREADV=0\",\n        \"-DFOLLY_HAVE_PWRITEV=0\",\n        \"-DFOLLY_HAVE_TFO=0\",\n    ]\n\n    if is_folly_mobile_flag():\n        flags += select({\n            \"DEFAULT\": [],\n            \"ovr_config//os:linux\": [\"-DFOLLY_MOBILE=1\"],\n        })\n\n    else:\n        flags += select({\n            \"DEFAULT\": select({\n                \"DEFAULT\": [\"-DFOLLY_MOBILE=1\"],\n                \"ovr_config//os:windows\": [],\n            }),\n            \"ovr_config//build_mode:arvr_mode\": select({\n                \"DEFAULT\": [\"-DFOLLY_MOBILE=1\"],\n                \"ovr_config//os:linux\": [],\n                \"ovr_config//os:macos\": [],\n            }),\n        })\n\n    return flags\n\nFBANDROID_CPPFLAGS = [\n    \"-DFOLLY_HAVE_IFUNC=0\",\n    \"-DFOLLY_HAVE_INT128_T=0\",\n    \"-DSSLCONTEXT_NO_REFCOUNT\",\n    \"-D__STDC_FORMAT_MACROS\",\n    \"-D__STDC_LIMIT_MACROS\",\n    \"-D__STDC_CONSTANT_MACROS\",\n]\n\nCLANG_CXX_FLAGS = [\n    \"-frtti\",\n    \"-fexceptions\",\n    \"-Wall\",\n    \"-Werror\",\n    \"-Wno-unused-local-typedefs\",\n    \"-Wno-unused-variable\",\n    \"-Wno-sign-compare\",\n    \"-Wno-comment\",\n    \"-Wno-return-type\",\n    \"-Wno-global-constructors\",\n    \"-Wno-missing-prototypes\",\n    \"-Wno-nullability-completeness\",\n    \"-Wno-c++17-extensions\",\n    \"-Wno-undef\",\n    \"-Wno-unreachable-code\",\n    \"-Wno-deprecated-declarations\",\n]\nCXXFLAGS = select({\n    \"DEFAULT\": [],\n    \"ovr_config//compiler:clang\": CLANG_CXX_FLAGS,\n})\n\nFBOBJC_CXXFLAGS = [\"-Os\"]\n\nFBANDROID_CXXFLAGS = [\n    \"-ffunction-sections\",\n    \"-Wno-uninitialized\",\n]\n\nWINDOWS_MSVC_CXXFLAGS = [\n    \"/EHs\",\n]\n\nWINDOWS_CLANG_CXX_FLAGS = [\n    \"-Wno-deprecated-declarations\",\n    \"-Wno-microsoft-cast\",\n    \"-Wno-missing-braces\",\n    \"-Wno-unused-function\",\n    \"-Wno-undef\",\n    \"-DBOOST_HAS_THREADS\",\n]\n\nDEFAULT_APPLE_SDKS = (IOS, APPLETVOS, MACOSX)\nDEFAULT_PLATFORMS = (CXX, ANDROID, APPLE, FBCODE, WINDOWS)\n\ndef _compute_include_directories():\n    base_path = native.package_name()\n    if base_path == \"xplat/folly\":\n        return [\"..\"]\n    folly_path = base_path[6:]\n    return [\"/\".join(len(folly_path.split(\"/\")) * [\"..\"])]\n\ndef folly_xplat_library(\n        name,\n        srcs = (),\n        header_namespace = \"\",\n        exported_headers = (),\n        raw_headers = (),\n        raw_headers_as_headers_mode = \"enabled\",\n        deps = (),\n        exported_deps = (),\n        force_static = True,\n        apple_sdks = None,\n        platforms = None,\n        enable_static_variant = False,\n        labels = (),\n        redirect = True,\n        **kwargs):\n    \"\"\"Translate a simpler declaration into the more complete library target.\n\n    When a redirect is configured via PACKAGE (using dirsync_redirect.write()),\n    this macro creates:\n    1. The actual implementation target with name \"_{name}\"\n    2. An alias target with the original name that uses select() to choose\n       between the local impl and the redirected target based on platform\n       constraints.\n    \"\"\"\n\n    # Check for redirect configuration\n    redirect_config = dirsync_redirect.read()\n    use_redirect = redirect and redirect_config != None and not name.startswith(\"_\")\n    impl_name = \"_{}\".format(name) if use_redirect else name\n\n    # Set default platform settings. `()` means empty, whereas None\n    # means default\n    if apple_sdks == None:\n        apple_sdks = DEFAULT_APPLE_SDKS\n    if platforms == None:\n        platforms = DEFAULT_PLATFORMS\n\n    # We use gflags on fbcode platforms, which don't mix well when mixing static\n    # and dynamic linking.\n    force_static = select({\n        \"DEFAULT\": select({\n            \"DEFAULT\": force_static,\n            \"ovr_config//runtime:fbcode\": False,\n        }),\n        \"ovr_config//build_mode:arvr_mode\": force_static,\n    })\n\n    # Preserve lib_name when redirecting to maintain SONAME\n    if use_redirect:\n        kwargs.setdefault(\"soname\", dirsync_redirect.soname(name))\n\n    fb_xplat_cxx_library(\n        name = impl_name,\n        srcs = srcs,\n        header_namespace = header_namespace,\n        exported_headers = exported_headers,\n        raw_headers = raw_headers,\n        raw_headers_as_headers_mode = raw_headers_as_headers_mode,\n        public_include_directories = _compute_include_directories(),\n        mangled_keys = [\n            \"name\",\n            \"deps\",\n            \"exported_deps\",\n            \"provided_deps\",\n            \"tests\",\n            \"soname\",\n            \"precompiled_header\",\n        ],\n        deps = deps,\n        exported_deps = exported_deps,\n        force_static = force_static,\n        apple_sdks = apple_sdks,\n        platforms = platforms,\n        enable_static_variant = enable_static_variant,\n        labels = list(labels),\n        compiler_flags = CXXFLAGS + kwargs.pop(\"compiler_flags\", []) + select({\n            \"DEFAULT\": [],\n            \"ovr_config//os:android\": FBANDROID_CXXFLAGS,\n            # TODO: Why appletvos, iphoneos, and macos are not marked as clang compilers?\n            \"ovr_config//os:appletvos\": CLANG_CXX_FLAGS,\n            \"ovr_config//os:iphoneos\": CLANG_CXX_FLAGS,\n            \"ovr_config//os:macos\": CLANG_CXX_FLAGS + [\"-fvisibility=default\"],\n        }) + select({\n            \"DEFAULT\": [],\n            \"ovr_config//os:windows-cl\": WINDOWS_MSVC_CXXFLAGS,\n            \"ovr_config//os:windows-gcc-or-clang\": WINDOWS_CLANG_CXX_FLAGS,\n        }) + [\n            \"-fexceptions\",\n            \"-frtti\",\n        ],\n        fbobjc_compiler_flags = kwargs.pop(\"fbobjc_compiler_flags\", []) +\n                                FBOBJC_CXXFLAGS,\n        windows_preferred_linkage = \"static\",\n        visibility = kwargs.pop(\"visibility\", [\"PUBLIC\"]),\n        **kwargs\n    )\n\n    # Create redirect alias if configured\n    if use_redirect:\n        dirsync_redirect.create_alias(\n            name,\n            redirect_config,\n            platforms = platforms,\n            apple_sdks = apple_sdks,\n            enable_static_variant = enable_static_variant,\n        )\n\ndef folly_xplat_cxx_library(name, **kwargs):\n    folly_xplat_library(\n        name = name,\n        **kwargs\n    )\n\ndef folly_xplat_cxx_test(\n        name,\n        srcs,\n        raw_headers = [],\n        deps = [],\n        contacts = [],\n        **kwargs):\n    # resources is cherry picked because some of the other kwargs\n    # have issues that need to be investigated.\n    # e.g., Some args are duplicated. Some args cause TSAN errors.\n    # TODO(T188948036): Fix xplat/folly:folly-futures-test and folly_xplat_cxx_test\n    resources = kwargs.get(\"resources\", [])\n\n    fb_xplat_cxx_test(\n        name = name,\n        srcs = srcs,\n        raw_headers = raw_headers,\n        resources = resources,\n        include_directories = _compute_include_directories(),\n        deps = deps + [\n            \"//xplat/folly/test/common:test_main\",\n        ],\n        contacts = contacts,\n        platforms = (CXX,),\n    )\n\ndef folly_xplat_cxx_binary(\n        name,\n        srcs,\n        raw_headers = [],\n        deps = [],\n        contacts = [],\n        **kwargs):\n    fb_xplat_cxx_binary(\n        name = name,\n        srcs = srcs,\n        raw_headers = raw_headers,\n        include_directories = _compute_include_directories(),\n        deps = deps,\n        contacts = contacts,\n        platforms = (CXX,),\n    )\n"
  },
  {
    "path": "folly/detail/AsyncTrace.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/AsyncTrace.h>\n\nnamespace folly {\nnamespace async_tracing {\nFOLLY_ATTR_WEAK void logSetGlobalCPUExecutor(Executor*) noexcept {}\nFOLLY_ATTR_WEAK void logSetGlobalCPUExecutorToImmutable() noexcept {}\nFOLLY_ATTR_WEAK void logGetGlobalCPUExecutor(Executor*) noexcept {}\nFOLLY_ATTR_WEAK void logGetImmutableCPUExecutor(Executor*) noexcept {}\nFOLLY_ATTR_WEAK void logSetGlobalIOExecutor(IOExecutor*) noexcept {}\nFOLLY_ATTR_WEAK void logGetGlobalIOExecutor(IOExecutor*) noexcept {}\nFOLLY_ATTR_WEAK void logGetImmutableIOExecutor(IOExecutor*) noexcept {}\nFOLLY_ATTR_WEAK void logSemiFutureVia(Executor*, Executor*) noexcept {}\nFOLLY_ATTR_WEAK void logFutureVia(Executor*, Executor*) noexcept {}\nFOLLY_ATTR_WEAK void logBlockingOperation(std::chrono::milliseconds) noexcept {}\nFOLLY_ATTR_WEAK void logSemiFutureDiscard(\n    DiscardHasDeferred /* hasDeferredExecutor */) noexcept {}\n} // namespace async_tracing\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/AsyncTrace.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n\n#include <folly/Optional.h>\n\nnamespace folly {\nclass Executor;\nclass IOExecutor;\nnamespace async_tracing {\nenum class DiscardHasDeferred {\n  NO_EXECUTOR,\n  DEFERRED_EXECUTOR,\n};\nvoid logSetGlobalCPUExecutor(Executor*) noexcept;\nvoid logSetGlobalCPUExecutorToImmutable() noexcept;\nvoid logGetGlobalCPUExecutor(Executor*) noexcept;\nvoid logGetImmutableCPUExecutor(Executor*) noexcept;\nvoid logSetGlobalIOExecutor(IOExecutor*) noexcept;\nvoid logGetGlobalIOExecutor(IOExecutor*) noexcept;\nvoid logGetImmutableIOExecutor(IOExecutor*) noexcept;\nvoid logSemiFutureVia(Executor*, Executor*) noexcept;\nvoid logFutureVia(Executor*, Executor*) noexcept;\nvoid logBlockingOperation(std::chrono::milliseconds) noexcept;\nvoid logSemiFutureDiscard(\n    DiscardHasDeferred /* hasDeferredExecutor */) noexcept;\n} // namespace async_tracing\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/AtomicHashUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <thread>\n\n#include <folly/portability/Asm.h>\n\n// Some utilities used by AtomicHashArray and AtomicHashMap\n//\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename Cond>\nvoid atomic_hash_spin_wait(Cond condition) {\n  constexpr size_t kPauseLimit = 10000;\n  for (size_t i = 0; condition(); ++i) {\n    if (i < kPauseLimit) {\n      folly::asm_volatile_pause();\n    } else {\n      std::this_thread::yield();\n    }\n  }\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/AtomicUnorderedMapUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <cstdlib>\n\n#include <folly/lang/Exception.h>\n#include <folly/memory/Malloc.h>\n\nnamespace folly {\nnamespace detail {\n\nclass MallocAlloc {\n public:\n  void* allocate(size_t size) {\n    void* p = std::malloc(size);\n    if (p == nullptr) {\n      throw_exception<std::bad_alloc>();\n    }\n    return p;\n  }\n\n  void deallocate(void* p, size_t size) { sizedFree(p, size); }\n};\n\ntemplate <typename Allocator>\nstruct GivesZeroFilledMemory : public std::false_type {};\n\n/*\n * Example of how to specialize GivesZeroFilledMemory for custom allocator.\n *\n * template <>\n * struct GivesZeroFilledMemory<Alloc> : public std::true_type {};\n */\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Avx2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/Avx2.h>\n\n#include <folly/CppAttributes.h>\n\nnamespace folly {\nnamespace detail {\n\n#if defined(__AVX2__)\n\n[[FOLLY_ATTR_GNU_FLATTEN]] FOLLY_DISABLE_SANITIZERS __m256i\n_mm256_loadu_si256_nosan(__m256i const* const p) {\n  return _mm256_loadu_si256(p);\n}\n\n[[FOLLY_ATTR_GNU_FLATTEN]] FOLLY_DISABLE_SANITIZERS __m256i\n_mm256_load_si256_nosan(__m256i const* const p) {\n  return _mm256_load_si256(p);\n}\n\n#endif\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Avx2.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n\n#if defined(__AVX2__)\n\n#include <immintrin.h>\n\n#endif\n\nnamespace folly {\nnamespace detail {\n\n#if defined(__AVX2__)\n\n__m256i _mm256_loadu_si256_nosan(__m256i const* const p);\nFOLLY_ERASE __m256i _mm256_loadu_si256_unchecked(__m256i const* const p) {\n  return kIsSanitize ? _mm256_loadu_si256_nosan(p) : _mm256_loadu_si256(p);\n}\n\n__m256i _mm256_load_si256_nosan(__m256i const* const p);\nFOLLY_ERASE __m256i _mm256_load_si256_unchecked(__m256i const* const p) {\n  return kIsSanitize ? _mm256_load_si256_nosan(p) : _mm256_load_si256(p);\n}\n\n#endif\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../defs.bzl\",\n    \"CXXFLAGS\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"async_trace\",\n    srcs = [\"AsyncTrace.cpp\"],\n    headers = [\"AsyncTrace.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:optional\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"atomic_hash_utils\",\n    headers = [\"AtomicHashUtils.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/portability:asm\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"atomic_unordered_map_utils\",\n    headers = [\"AtomicUnorderedMapUtils.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:exception\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"avx2\",\n    srcs = [\"Avx2.cpp\"],\n    headers = [\"Avx2.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:cpp_attributes\",\n        \"//folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"discriminated_ptr_detail\",\n    headers = [\"DiscriminatedPtrDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/functional:invoke\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"file_util_detail\",\n    srcs = [\"FileUtilDetail.cpp\"],\n    headers = [\"FileUtilDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/portability:config\",\n    ],\n    exported_deps = [\n        \"//folly/portability:sys_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"file_util_vector_detail\",\n    headers = [\"FileUtilVectorDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":file_util_detail\",\n        \"//folly/portability:sys_uio\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fingerprint_polynomial\",\n    headers = [\"FingerprintPolynomial.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"group_varint_detail\",\n    headers = [\"GroupVarintDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"ip_address\",\n    srcs = [\"IPAddress.cpp\"],\n    headers = [\"IPAddress.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/portability:fmt_compile\",\n    ],\n    exported_deps = [\n        \"//folly/portability:sockets\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"ip_address_source\",\n    headers = [\"IPAddressSource.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \":ip_address\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"memory_idler\",\n    srcs = [\"MemoryIdler.cpp\"],\n    headers = [\"MemoryIdler.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:glog\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/memory:mallctl_helper\",\n        \"//folly/memory:malloc\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:pthread\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:unistd\",\n        \"//folly/system:pid\",\n    ],\n    exported_deps = [\n        \":futex\",\n        \"//folly/hash:hash\",\n        \"//folly/synchronization:atomic_struct\",\n        \"//folly/system:thread_id\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"mpmc_pipeline_detail\",\n    headers = [\"MPMCPipelineDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:mpmc_queue\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"perf_scoped\",\n    srcs = [\"PerfScoped.cpp\"],\n    headers = [\"PerfScoped.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    # folly subprocess is not supported on windows\n    deps = [\n        \"//folly:conv\",\n        \"//folly/system:pid\",\n        \"//folly/testing:test_util\",\n    ] + select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux\": [\"//folly:subprocess\"],\n    }),\n    external_deps = [\n        (\"boost\", None, \"boost_regex\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"poly_detail\",\n    headers = [\"PolyDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":typelist\",\n        \"//folly:poly_exception\",\n        \"//folly:portability\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:static_const\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"simple_simd_string_utils\",\n    srcs = [\"SimpleSimdStringUtils.cpp\"],\n    headers = [\n        \"SimpleSimdStringUtils.h\",\n        \"SimpleSimdStringUtilsImpl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:range\",\n        \"//folly/algorithm/simd/detail:simd_any_of\",\n        \"//folly/algorithm/simd/detail:simd_platform\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"singleton\",\n    headers = [\"Singleton.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:traits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"slow_fingerprint\",\n    headers = [\"SlowFingerprint.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":fingerprint_polynomial\",\n        \"//folly:fingerprint\",\n        \"//folly:range\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"socket_fast_open\",\n    srcs = [\"SocketFastOpen.cpp\"],\n    headers = [\"SocketFastOpen.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/net:network_socket\",\n        \"//folly/portability:sockets\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"split_string_simd\",\n    headers = [\n        \"SplitStringSimd.h\",\n        \"SplitStringSimdImpl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/algorithm/simd:ignore\",\n        \"//folly/algorithm/simd:movemask\",\n        \"//folly/algorithm/simd/detail:simd_for_each\",\n        \"//folly/algorithm/simd/detail:simd_platform\",\n        \"//folly/lang:bits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"sse\",\n    srcs = [\"Sse.cpp\"],\n    headers = [\"Sse.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:cpp_attributes\",\n        \"//folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"thread_local_detail\",\n    srcs = [\"ThreadLocalDetail.cpp\"],\n    headers = [\"ThreadLocalDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":thread_local_globals\",\n        \"//folly:constexpr_math\",\n        \"//folly:utility\",\n        \"//folly/hash:murmur_hash\",\n        \"//folly/lang:hint\",\n        \"//folly/memory:sanitize_leak\",\n        \"//folly/random:hash\",\n        \"//folly/synchronization:call_once\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":static_singleton_manager\",\n        \":unique_instance\",\n        \"//folly:exception\",\n        \"//folly:function\",\n        \"//folly:map_util\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:shared_mutex\",\n        \"//folly:synchronized\",\n        \"//folly/concurrency/container:atomic_grow_array\",\n        \"//folly/container:foreach\",\n        \"//folly/lang:exception\",\n        \"//folly/memory:malloc\",\n        \"//folly/portability:pthread\",\n        \"//folly/synchronization:micro_spin_lock\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/system:at_fork\",\n        \"//folly/system:thread_id\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"thread_local_globals\",\n    srcs = [\"thread_local_globals.cpp\"],\n    headers = [\"thread_local_globals.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":static_singleton_manager\",\n        \"//folly/lang:exception\",\n        \"//folly/portability:pthread\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"turn_sequencer\",\n    headers = [\"TurnSequencer.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":futex\",\n        \"//folly:portability\",\n        \"//folly/chrono:hardware\",\n        \"//folly/portability:asm\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"tuple\",\n    headers = [\"tuple.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:cpp_attributes\",\n        \"//folly:traits\",\n        \"//folly/lang:safe_alias_fwd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"typelist\",\n    headers = [\"TypeList.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:traits\",\n        \"//folly:utility\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"unique_instance\",\n    srcs = [\"UniqueInstance.cpp\"],\n    headers = [\"UniqueInstance.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:demangle\",\n        \"//folly/lang:exception\",\n    ],\n    exported_deps = [\n        \":static_singleton_manager\",\n        \"//folly:cpp_attributes\",\n    ],\n)\n\n# Targets below are kept as dual fbcode_target/non_fbcode_target patterns\n# because they have complex attributes that fb_dirsync_cpp_library doesn't handle:\n# - futex, iterators: compiler_flags on xplat side\n# - range_common, range_simd, range_sse42: undefined_symbols on fbcode side\n# - static_singleton_manager: force_static = select() on xplat side\n# - traponavx512: arch_preprocessor_flags (fbcode) vs compiler_flags + CXXFLAGS (xplat)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"futex\",\n    srcs = [\n        \"Futex.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"Futex.h\",\n        \"Futex-inl.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/hash:hash\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:unistd\",\n        \"fbsource//xplat/folly/synchronization:parking_lot\",\n        \"//xplat/folly:scope_guard\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"futex\",\n    srcs = [\"Futex.cpp\"],\n    headers = [\n        \"Futex.h\",\n        \"Futex-inl.h\",\n    ],\n    deps = [\n        \"//folly:scope_guard\",\n        \"//folly/hash:hash\",\n        \"//folly/portability:sys_syscall\",\n    ],\n    exported_deps = [\n        \"//folly/synchronization:parking_lot\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"iterators\",\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"Iterators.h\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:sys_types\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"iterators\",\n    headers = [\"Iterators.h\"],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"range_common\",\n    srcs = [\n        \"RangeCommon.cpp\",\n    ],\n    raw_headers = [\n        \"RangeCommon.h\",\n    ],\n    deps = [\n        \"//xplat/folly:likely\",\n        \"//xplat/folly/container:sparse_byte_set\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"range_common\",\n    srcs = [\"RangeCommon.cpp\"],\n    headers = [\"RangeCommon.h\"],\n    undefined_symbols = True,  # TODO(T23121628): fix deps and remove\n    deps = [\n        \"//folly/container:sparse_byte_set\",\n    ],\n    exported_deps = [\n        \"//folly:likely\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"range_simd\",\n    srcs = [\n        \"RangeSimd.cpp\",\n    ],\n    raw_headers = [\n        \"RangeSimd.h\",\n    ],\n    deps = [\n        \":range_sse42\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly/detail:range_common\",\n        \"//xplat/folly/external/nvidia/detail:range_sve2\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"range_simd\",\n    srcs = [\"RangeSimd.cpp\"],\n    headers = [\"RangeSimd.h\"],\n    undefined_symbols = True,\n    deps = [\n        \":range_sse42\",\n        \"//folly:portability\",\n        \"//folly/external/nvidia/detail:range_sve2\",\n    ],\n    exported_deps = [\n        \":range_common\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"range_sse42\",\n    srcs = [\n        \"RangeSse42.cpp\",\n    ],\n    raw_headers = [\n        \"RangeSse42.h\",\n    ],\n    deps = [\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly/detail:range_common\",\n        \"//xplat/folly/detail:sse\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"range_sse42\",\n    srcs = [\"RangeSse42.cpp\"],\n    headers = [\"RangeSse42.h\"],\n    undefined_symbols = True,  # TODO(T23121628): fix deps and remove\n    deps = [\n        \":sse\",\n        \"//folly:likely\",\n        \"//folly:portability\",\n    ],\n    exported_deps = [\n        \":range_common\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"static_singleton_manager\",\n    srcs = [\n        \"StaticSingletonManager.cpp\",\n    ],\n    force_static = bool(select({\n        \"DEFAULT\": False,\n        \"ovr_config//os:macos\": True,\n    })),\n    raw_headers = [\n        \"StaticSingletonManager.h\",\n    ],\n    deps = [\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:indestructible\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/detail:singleton\",\n        \"//xplat/folly/lang:thunk\",\n        \"//xplat/folly/lang:type_info\",\n        \"//xplat/folly/memory:reentrant_allocator\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"static_singleton_manager\",\n    srcs = [\"StaticSingletonManager.cpp\"],\n    headers = [\"StaticSingletonManager.h\"],\n    deps = [\n        \"//folly/memory:reentrant_allocator\",\n    ],\n    exported_deps = [\n        \":singleton\",\n        \"//folly:c_portability\",\n        \"//folly:indestructible\",\n        \"//folly:likely\",\n        \"//folly:utility\",\n        \"//folly/lang:thunk\",\n        \"//folly/lang:type_info\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"traponavx512\",\n    srcs = [\"TrapOnAvx512.cpp\"],\n    compiler_flags = CXXFLAGS + select({\n        \"DEFAULT\": [],\n        \"ovr_config//toolchain/fb:fbcode-x86_64\": [\n            \"-msse4.2\",\n            \"-mavx512f\",\n            \"-mavx512vl\",\n        ],\n    }),\n    raw_headers = [\"TrapOnAvx512.h\"],\n    deps = [\n        \"//xplat/folly:portability\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"traponavx512\",\n    srcs = [\"TrapOnAvx512.cpp\"],\n    headers = [\"TrapOnAvx512.h\"],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-msse4.2\",\n            \"-mavx512f\",\n            \"-mavx512vl\",\n        ],\n    }),\n    deps = [\n        \"//folly:portability\",\n    ],\n)\n"
  },
  {
    "path": "folly/detail/BenchmarkAdaptive.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/BenchmarkAdaptive.h>\n\n#include <algorithm>\n#include <chrono>\n#include <iomanip>\n#include <sstream>\n\n#include <folly/Random.h>\n#include <folly/String.h>\n\nnamespace folly {\nnamespace detail {\n\nusing namespace std::chrono;\n\n// Compute split-half stability: are the first-half and second-half estimates\n// within each other's CIs (with epsilon tolerance)?  Needs at least 4 samples.\nStabilityStats computeStabilityStats(\n    const std::vector<double>& samples,\n    double percentile,\n    double targetPrecisionPct) {\n  size_t n = samples.size();\n  CHECK_GE(n, 4) << \"computeStabilityStats requires at least 4 samples\";\n\n  std::vector<double> firstHalf(samples.begin(), samples.begin() + n / 2);\n  std::vector<double> secondHalf(samples.begin() + n / 2, samples.end());\n\n  auto ci1 = SortedSamples(std::move(firstHalf)).percentileCI(percentile);\n  auto ci2 = SortedSamples(std::move(secondHalf)).percentileCI(percentile);\n\n  double epsilonNs = std::max(\n      // 1 picosecond is below typical CPU clock resolution, so any\n      // \"instability\" of that amount is spurious.\n      0.001,\n      // Inter-half drift below 1/2 of the target precision can't push the\n      // final estimate outside the precision budget.\n      targetPrecisionPct / 100.0 * 0.5 * std::min(ci1.estimate, ci2.estimate));\n\n  // Check if each estimate is within the other's CI, with epsilon tolerance\n  return {\n      .firstHalf = ci1,\n      .secondHalf = ci2,\n      .isStable = (ci1.estimate >= ci2.lo - epsilonNs) &&\n          (ci1.estimate <= ci2.hi + epsilonNs) &&\n          (ci2.estimate >= ci1.lo - epsilonNs) &&\n          (ci2.estimate <= ci1.hi + epsilonNs),\n  };\n}\n\nsize_t recalibrateIterCount(\n    size_t iterCount, nanoseconds target, nanoseconds dur) {\n  if (dur.count() > 10) { // don't divide by small numbers\n    size_t candidate = std::max<size_t>(\n        1,\n        static_cast<size_t>(std::ceil(\n            iterCount * static_cast<double>(target.count()) / dur.count())));\n    return std::max(iterCount, candidate);\n  }\n  return iterCount * 2;\n}\n\nnamespace {\n\n// IMPORTANT: Many functions here are marked FOLLY_NOINLINE to try to reduce\n// interference between the benchmark harness and the code under test. The hope\n// is that by keeping logging, statistics, and convergence-checking code out of\n// line, we:\n//   (1) Keep the hot measurement loop compact and cache-friendly\n//   (2) Reduce the chance of harness code polluting icache during measurement\n//   (3) Prevent the compiler from intermingling harness with benchmarks\n// This is somewhat speculative - we can't prove it helps - but the cost is low\n// and the potential benefit is cleaner measurements. The most important uses\n// are on SamplingLoop::run() and checkAllDone() which bound the hot path.\n\n// Format sample statistics concisely.\nFOLLY_NOINLINE std::string formatSampleStats(\n    const char* name,\n    const std::vector<double>& samples,\n    const AdaptiveOptions& opts,\n    nanoseconds elapsed) {\n  std::ostringstream oss;\n  oss << std::fixed << std::setprecision(2);\n  oss << kANSIBold << name << kANSIReset << \": \";\n\n  if (samples.size() < 2) {\n    oss << samples.size() << \" samples\";\n    return oss.str();\n  }\n\n  SortedSamples sorted(samples);\n  auto ci = sorted.percentileCI(opts.targetPercentile);\n  oss << \"p\" << opts.targetPercentile << \"=\"\n      << readableTime(ci.estimate / 1e9, 1) << \" to \" << ci.relWidth() << \"%\"\n      << \", \" << samples.size() << \" samples\"\n      << \", \" << duration_cast<seconds>(elapsed).count() << \"s\";\n\n  if (samples.size() >= 4) {\n    auto ss = computeStabilityStats(\n        samples, opts.targetPercentile, opts.targetPrecisionPct);\n    if (!ss.isStable) {\n      oss << \"\\n    \" << kANSIBoldYellow << \"[unstable]\" << kANSIReset\n          << \" 1st=\" << ss.firstHalf.estimate << \" [\" << ss.firstHalf.lo << \", \"\n          << ss.firstHalf.hi << \"]\"\n          << \" 2nd=\" << ss.secondHalf.estimate << \" [\" << ss.secondHalf.lo\n          << \", \" << ss.secondHalf.hi << \"]\";\n    }\n  }\n  return oss.str();\n}\n\n// State for tracking samples, used for both benchmarks and baselines.\n// For baselines: reg=nullptr, counters will be empty, done unused.\nstruct BenchState {\n  std::string name; // Benchmark or baseline name\n  const BenchmarkRegistration* reg; // nullptr for baselines\n  const AdaptiveOptions* opts;\n  size_t iterCount = 1;\n  std::vector<std::pair<double, UserCounters>> samples{};\n  nanoseconds elapsed{0};\n  bool done = false; // benchmarks only\n\n  // Extract just the timing values for statistical analysis.\n  std::vector<double> timings() const {\n    std::vector<double> result;\n    result.reserve(samples.size());\n    for (const auto& s : samples) {\n      result.push_back(s.first);\n    }\n    return result;\n  }\n\n  void doRecalibrate(nanoseconds dur) {\n    auto target = duration_cast<nanoseconds>(microseconds(opts->sliceUsec));\n    size_t prev = iterCount;\n    iterCount = folly::detail::recalibrateIterCount(iterCount, target, dur);\n    if (!opts->quiet && dur > 2 * target && prev > 1) {\n      LOG(WARNING) << kANSIBoldYellow << \"Unstable calibration: \" << kANSIReset\n                   << name << \" took \" << dur.count() << \"ns (target \"\n                   << target.count() << \"ns)\";\n    }\n  }\n\n  // For baselines: run function and store raw sample (no correction).\n  double runAndAddSampleRaw(\n      const BenchmarkFun& fun, nanoseconds& totalElapsed) {\n    TimeIterData res = fun(static_cast<unsigned>(iterCount));\n    auto dur = duration_cast<nanoseconds>(res.duration);\n    double nsPerIter = static_cast<double>(dur.count()) / res.niter;\n    samples.emplace_back(nsPerIter, UserCounters{});\n    elapsed += dur;\n    totalElapsed += dur;\n    doRecalibrate(dur);\n    return nsPerIter;\n  }\n\n  // For benchmarks: run and store baseline-adjusted sample.\n  void runAndAddSample(\n      double baselineNsPerIter,\n      double suspenderOverheadNsPerSuspension,\n      nanoseconds& totalElapsed) {\n    TimeIterData res = reg->func(static_cast<unsigned>(iterCount));\n    auto dur = duration_cast<nanoseconds>(res.duration);\n    double adjusted =\n        static_cast<double>(dur.count()) / res.niter - baselineNsPerIter;\n    if (res.suspensionCount > 0) {\n      adjusted -= static_cast<double>(res.suspensionCount) / res.niter *\n          suspenderOverheadNsPerSuspension;\n    }\n    adjusted = std::max(0.0, adjusted);\n    samples.emplace_back(adjusted, std::move(res.userCounters));\n    elapsed += dur;\n    totalElapsed += dur;\n    doRecalibrate(dur);\n  }\n\n  bool isStable() const {\n    return samples.size() >= 4 &&\n        computeStabilityStats(\n            timings(), opts->targetPercentile, opts->targetPrecisionPct)\n            .isStable;\n  }\n\n  bool checkDone() {\n    if (elapsed >= seconds(opts->maxSecs)) {\n      done = true;\n      return true;\n    }\n    if (samples.size() < opts->minSamples ||\n        elapsed < duration<double>(opts->minSecs)) {\n      return false;\n    }\n    if (!isStable()) {\n      return false;\n    }\n    done =\n        SortedSamples(timings()).percentileCIRelWidth(opts->targetPercentile) <=\n        opts->targetPrecisionPct;\n    return done;\n  }\n\n  UserCounters countersForEstimate(double estimate) const {\n    if (samples.empty()) {\n      return {};\n    }\n    auto best = std::min_element(\n        samples.begin(),\n        samples.end(),\n        [estimate](const auto& a, const auto& b) {\n          return std::abs(a.first - estimate) < std::abs(b.first - estimate);\n        });\n    return best->second;\n  }\n\n  std::string formatStats() const {\n    return formatSampleStats(name.c_str(), timings(), *opts, elapsed);\n  }\n};\n\nFOLLY_NOINLINE void verboseLogInitial(\n    const AdaptiveOptions& opts, const std::vector<BenchState>& states) {\n  std::ostringstream oss;\n  oss << \"\\n  \" << opts.toString();\n  for (const auto& s : states) {\n    oss << \"\\n    \" << s.name;\n  }\n  LOG(INFO) << oss.str();\n}\n\nFOLLY_NOINLINE void verboseLogFinal(\n    size_t round,\n    const BenchState& baselineState,\n    const BenchState& suspenderBaselineState,\n    nanoseconds wallClock,\n    const std::vector<BenchState>& states) {\n  // Compute total benchmark time\n  nanoseconds totalBenchTime{0};\n  for (const auto& s : states) {\n    totalBenchTime += s.elapsed;\n  }\n  auto accountedTime =\n      baselineState.elapsed + suspenderBaselineState.elapsed + totalBenchTime;\n  auto overhead = wallClock - accountedTime;\n\n  std::ostringstream oss;\n  oss << \"\\nFinal results (\" << round << \" rounds)\";\n\n  // Time breakdown using prettyPrint for concise output\n  auto fmtTime = [](nanoseconds ns) {\n    return trimWhitespace(\n               prettyPrint(\n                   duration_cast<duration<double>>(ns).count(),\n                   PRETTY_TIME_HMS,\n                   false))\n        .str();\n  };\n  oss << \"\\n  Time: \" << fmtTime(wallClock) << \" – baseline \"\n      << fmtTime(baselineState.elapsed) << \", suspender \"\n      << fmtTime(suspenderBaselineState.elapsed) << \", benchmarks \"\n      << fmtTime(totalBenchTime) << \", overhead \" << fmtTime(overhead);\n\n  // Baseline stats\n  oss << \"\\n  \" << baselineState.formatStats();\n  oss << \"\\n  \" << suspenderBaselineState.formatStats();\n\n  // Benchmark stats\n  for (const auto& s : states) {\n    oss << \"\\n  \" << s.formatStats();\n  }\n  LOG(INFO) << oss.str();\n}\n\n// Build the table + detailed stats for benchmarks that haven't converged.\n// Returns empty string if all benchmarks have converged and baselines are\n// stable. Used by the periodic intermediate warning.\nFOLLY_NOINLINE std::string formatIntermediateReport(\n    const std::vector<BenchState>& states,\n    const AdaptiveOptions& opts,\n    const BenchState& baselineState,\n    const BenchState& suspenderBaselineState) {\n  std::vector<BenchmarkResult> tableResults;\n  std::vector<std::string> annotations;\n  std::string stats;\n  bool anyNonConverged = false;\n\n  for (const auto& s : states) {\n    if (s.samples.empty()) {\n      continue;\n    }\n    SortedSamples sorted(s.timings());\n    double pctile = sorted.percentile(opts.targetPercentile);\n    tableResults.push_back(\n        {s.reg->file, s.name, pctile, s.countersForEstimate(pctile)});\n\n    bool converged = s.samples.size() >= opts.minSamples && s.isStable() &&\n        sorted.percentileCIRelWidth(opts.targetPercentile) <=\n            opts.targetPrecisionPct;\n    if (converged) {\n      annotations.emplace_back();\n    } else {\n      anyNonConverged = true;\n      annotations.emplace_back(s.isStable() ? \"[imprecise]\" : \"[unstable]\");\n      if (s.samples.size() >= opts.minSamples) {\n        stats += \"\\n  \" + s.formatStats();\n      }\n    }\n  }\n\n  if (!anyNonConverged) {\n    return \"\";\n  }\n\n  // Add unstable baseline stats\n  for (const auto* bs : {&baselineState, &suspenderBaselineState}) {\n    if (!bs->isStable() && bs->samples.size() >= opts.minSamples) {\n      stats += \"\\n  \" + bs->formatStats();\n    }\n  }\n\n  return (stats.empty() ? \"\\n\" : stats + \"\\n\\n\") +\n      benchmarkResultsToString(tableResults, \"  \", annotations) + \"\\n\";\n}\n\n// Encapsulates the sampling loop state and logic.\n// Methods are noinline to keep the hot loop compact and isolated.\nstruct SamplingLoop {\n  const BenchmarkFun& baselineFun;\n  const BenchmarkFun& suspenderBaselineFun;\n  BenchState& baselineState;\n  BenchState& suspenderBaselineState;\n  std::vector<BenchState>& states;\n  const AdaptiveOptions& opts;\n  nanoseconds totalElapsed{0};\n  size_t round = 0;\n\n  // Baseline sampling intervals to reduce overhead.\n  // Baseline is very cheap (~0.3ns), suspender is more expensive (~35ns).\n  static constexpr size_t kBaselineSampleInterval = 8;\n  static constexpr size_t kSuspenderSampleInterval = 2;\n\n  // Cached baseline values (reused between samples)\n  double cachedBaselineNsPerIter = 0;\n  double cachedSuspenderOverheadNsPerSuspension = 0;\n\n  // Convergence check state\n  nanoseconds lastCheck{0};\n  nanoseconds lastIntermediateResult{0};\n\n  static constexpr auto kCheckInterval = milliseconds(150);\n  static constexpr auto kIntermediateResultInterval = seconds(5);\n\n  // Run sampling loop until all benchmarks are done.\n  FOLLY_NOINLINE void run() {\n    std::vector<size_t> order(states.size());\n    for (size_t i = 0; i < order.size(); ++i) {\n      order[i] = i;\n    }\n    ThreadLocalPRNG rng;\n\n    while (true) {\n      // Sample baseline periodically (cheap, every 8 rounds)\n      if (round % kBaselineSampleInterval == 0) {\n        cachedBaselineNsPerIter =\n            baselineState.runAndAddSampleRaw(baselineFun, totalElapsed);\n      }\n\n      // Sample suspender baseline more frequently (expensive, every 2 rounds)\n      if (round % kSuspenderSampleInterval == 0) {\n        cachedSuspenderOverheadNsPerSuspension =\n            suspenderBaselineState.runAndAddSampleRaw(\n                suspenderBaselineFun, totalElapsed);\n      }\n\n      // Run a slice of each unconverged benchmark, in random order\n      std::shuffle(order.begin(), order.end(), rng);\n      for (size_t i : order) {\n        auto& s = states[i];\n        if (s.done) {\n          continue;\n        }\n        s.runAndAddSample(\n            cachedBaselineNsPerIter,\n            cachedSuspenderOverheadNsPerSuspension,\n            totalElapsed);\n      }\n      ++round;\n      if (checkAllDone()) {\n        break;\n      }\n    }\n  }\n\n  // Returns true if all benchmarks are done and we should exit the loop.\n  FOLLY_NOINLINE bool checkAllDone() {\n    if (totalElapsed - lastCheck < kCheckInterval) {\n      return false;\n    }\n    lastCheck = totalElapsed;\n\n    // Warn periodically about non-converged benchmarks\n    if (!opts.quiet &&\n        totalElapsed - lastIntermediateResult >= kIntermediateResultInterval) {\n      auto report = formatIntermediateReport(\n          states, opts, baselineState, suspenderBaselineState);\n      if (!report.empty()) {\n        LOG(WARNING) << \"After \" << duration_cast<seconds>(totalElapsed).count()\n                     << \"s of trials:\" << report;\n        lastIntermediateResult = totalElapsed;\n      }\n    }\n\n    // Check done and collect newly-done benchmarks by category\n    std::string converged, timedOutUnstable, timedOutImprecise;\n    bool allDone = true;\n    for (auto& s : states) {\n      bool wasDone = s.done;\n      if (!s.checkDone()) {\n        allDone = false;\n      } else if (!wasDone && opts.verbose && !opts.quiet) {\n        bool exceeded = s.elapsed >= seconds(opts.maxSecs);\n        if (!exceeded) {\n          converged += \"\\n  \" + s.formatStats();\n        } else if (!s.isStable()) {\n          timedOutUnstable += \"\\n  \" + s.formatStats();\n        } else {\n          timedOutImprecise += \"\\n  \" + s.formatStats();\n        }\n      }\n    }\n    std::string msg;\n    if (!converged.empty()) {\n      msg += fmt::format(\n          \"\\n{}→ Converged:{}{}\", kANSIBoldGreen, kANSIReset, converged);\n    }\n    if (!timedOutUnstable.empty()) {\n      msg += fmt::format(\n          \"\\n{}→ Exceeded max_secs (unstable):{}{}\",\n          kANSIBoldYellow,\n          kANSIReset,\n          timedOutUnstable);\n    }\n    if (!timedOutImprecise.empty()) {\n      msg += fmt::format(\n          \"\\n{}→ Exceeded max_secs (imprecise):{}{}\",\n          kANSIBoldYellow,\n          kANSIReset,\n          timedOutImprecise);\n    }\n    if (!msg.empty()) {\n      LOG(INFO) << msg;\n    }\n    return allDone;\n  }\n};\n\n} // namespace\n\nAdaptiveResult runBenchmarksAdaptive(\n    const std::vector<const BenchmarkRegistration*>& benchmarks,\n    const BenchmarkFun& baselineFun,\n    const BenchmarkFun& suspenderBaselineFun,\n    const AdaptiveOptions& opts) {\n  AdaptiveResult result;\n  if (benchmarks.empty()) {\n    return result;\n  }\n\n  auto wallClockStart = steady_clock::now();\n\n  // No upfront calibration — each BenchState starts at `iterCount=1` and\n  // self-adjusts via `recalibrateIterCount()` after every sample.  This avoids\n  // the cold-start problem where the first call is slow (page faults, TLB\n  // misses, lazy linking) causing the doubling search to short-circuit and\n  // return iterCount=1 for all subsequent rounds.\n\n  std::vector<BenchState> states;\n  states.reserve(benchmarks.size());\n  for (const auto* b : benchmarks) {\n    states.emplace_back(\n        BenchState{\n            .name = b->name,\n            .reg = b,\n            .opts = &opts,\n        });\n  }\n\n  if (opts.verbose) {\n    verboseLogInitial(opts, states);\n  }\n\n  BenchState baselineState{\n      .name = \"baseline\",\n      .reg = nullptr,\n      .opts = &opts,\n  };\n  BenchState suspenderBaselineState{\n      .name = \"suspenderBaseline\",\n      .reg = nullptr,\n      .opts = &opts,\n  };\n  SamplingLoop loop{\n      .baselineFun = baselineFun,\n      .suspenderBaselineFun = suspenderBaselineFun,\n      .baselineState = baselineState,\n      .suspenderBaselineState = suspenderBaselineState,\n      .states = states,\n      .opts = opts};\n\n  // Interleaved Sampling with time-based convergence checks\n  loop.run();\n\n  // Final results\n  if (opts.verbose) {\n    verboseLogFinal(\n        loop.round,\n        baselineState,\n        suspenderBaselineState,\n        duration_cast<nanoseconds>(steady_clock::now() - wallClockStart),\n        states);\n  }\n\n  // Log errors for benchmarks that didn't converge (stability or CI failures)\n  {\n    std::string msg;\n    for (const auto& s : states) {\n      if (!s.isStable() ||\n          SortedSamples(s.timings())\n                  .percentileCIRelWidth(opts.targetPercentile) >\n              opts.targetPrecisionPct) {\n        msg += \"\\n  \" + s.formatStats();\n      }\n    }\n    if (!msg.empty()) {\n      LOG(ERROR) << kANSIBoldRed << \"Did not converge:\" << kANSIReset << msg;\n    }\n  }\n\n  result.results.reserve(states.size());\n  for (const auto& s : states) {\n    SortedSamples sorted(s.timings());\n    double pctile = sorted.percentile(opts.targetPercentile);\n    result.results.emplace_back(\n        BenchmarkResult{\n            s.reg->file,\n            s.reg->name,\n            pctile, // Already baseline-adjusted\n            s.countersForEstimate(pctile)});\n  }\n\n  result.totalRounds = loop.round;\n  return result;\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/BenchmarkAdaptive.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Benchmark.h>\n\n#include <glog/logging.h>\n\n#include <algorithm>\n#include <cmath>\n#include <sstream>\n#include <vector>\n\nnamespace folly {\nnamespace detail {\n\ninline constexpr const char* kANSIBold = \"\\033[1m\";\ninline constexpr const char* kANSIBoldGreen = \"\\033[1;32m\";\ninline constexpr const char* kANSIBoldYellow = \"\\033[1;33m\";\ninline constexpr const char* kANSIBoldRed = \"\\033[1;31m\";\ninline constexpr const char* kANSIReset = \"\\033[0m\";\n\n// Confidence interval for a percentile estimate.\nstruct PercentileCI {\n  double lo;\n  double hi;\n  double estimate;\n\n  double relWidth() const {\n    if (estimate == 0) {\n      LOG(WARNING) << \"relWidth called with zero estimate\";\n      return 100.0;\n    }\n    return (hi - lo) / estimate * 100.0;\n  }\n};\n\n// Sample container with statistical methods. Sorts on construction.\nclass SortedSamples {\n public:\n  explicit SortedSamples(std::vector<double> samples)\n      : samples_(std::move(samples)) {\n    std::sort(samples_.begin(), samples_.end());\n  }\n\n  size_t size() const { return samples_.size(); }\n\n  // Returns the value at the given percentile (0-100).\n  // Uses linear interpolation between floor and ceil indices.\n  double percentile(double pct) const {\n    CHECK_GE(samples_.size(), 1) << \"percentile requires at least 1 sample\";\n    double pos = (samples_.size() - 1) * pct / 100.0;\n    size_t lo = static_cast<size_t>(std::floor(pos));\n    size_t hi = static_cast<size_t>(std::ceil(pos));\n    double frac = pos - lo;\n    return samples_[lo] * (1.0 - frac) + samples_[hi] * frac;\n  }\n\n  // Returns CI bounds and estimate for the given percentile.\n  //\n  // Normal approximation to binomial distribution of order statistics.  The\n  // expected position of the p-quantile is (n-1)*p with SE = sqrt(n*p*(1-p)).\n  // Conservative for small n, but adequate for n >= 20 that's typical here.\n  PercentileCI percentileCI(double pct, double zScore = 1.96) const {\n    CHECK_GE(samples_.size(), 2) << \"percentileCI requires at least 2 samples\";\n    size_t n = samples_.size();\n    double q = pct / 100.0;\n\n    // SE of order statistic position under binomial distribution\n    double positionSE = std::sqrt(n * q * (1 - q));\n    double pos = (n - 1) * q;\n\n    // Convert position bounds back to percentiles\n    double loPct = std::max(0.0, (pos - zScore * positionSE) / (n - 1) * 100.0);\n    double hiPct =\n        std::min(100.0, (pos + zScore * positionSE) / (n - 1) * 100.0);\n\n    return {percentile(loPct), percentile(hiPct), percentile(pct)};\n  }\n\n  // Convenience method: returns CI relative width, or 100.0 if < 2 samples.\n  double percentileCIRelWidth(double pct, double zScore = 1.96) const {\n    return samples_.size() >= 2 ? percentileCI(pct, zScore).relWidth() : 100.0;\n  }\n\n private:\n  std::vector<double> samples_;\n};\n\n// Split-half stability stats for detecting oscillation.\nstruct StabilityStats {\n  PercentileCI firstHalf;\n  PercentileCI secondHalf;\n  bool isStable;\n};\n\n// Compute split-half stability: are the first-half and second-half estimates\n// within each other's CIs (with epsilon tolerance)?  Needs at least 4 samples.\nStabilityStats computeStabilityStats(\n    const std::vector<double>& samples,\n    double percentile,\n    // Determines epsilon -- inter-half drift below 1/2 of the target precision\n    // does not meaningfully affect the final answer.\n    double targetPrecisionPct);\n\nstruct AdaptiveOptions {\n  int64_t sliceUsec;\n  double targetPercentile;\n  double targetPrecisionPct;\n  size_t minSamples;\n  double minSecs;\n  int32_t maxSecs;\n  bool verbose;\n  bool quiet;\n\n  std::string toString() const {\n    std::ostringstream oss;\n    oss << \"sliceUsec=\" << sliceUsec << \" targetPercentile=\" << targetPercentile\n        << \" targetPrecisionPct=\" << targetPrecisionPct << \" minSamples=\"\n        << minSamples << \" minSecs=\" << minSecs << \" maxSecs=\" << maxSecs;\n    return oss.str();\n  }\n};\n\nstruct AdaptiveResult {\n  std::vector<BenchmarkResult> results;\n  size_t totalRounds = 0;\n};\n\nAdaptiveResult runBenchmarksAdaptive(\n    const std::vector<const BenchmarkRegistration*>&,\n    const BenchmarkFun& baseline,\n    const BenchmarkFun& suspenderBaseline,\n    const AdaptiveOptions&);\n\n// Given current `iterCount` and the duration of the last sample, compute a new\n// `iterCount` that would make the next sample closer to `target` duration.\n// Monotonically increasing: returns `max(iterCount, candidate)`.\nsize_t recalibrateIterCount(\n    size_t iterCount,\n    std::chrono::nanoseconds target,\n    std::chrono::nanoseconds dur);\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async_trace\n  SRCS\n    AsyncTrace.cpp\n  HEADERS\n    AsyncTrace.h\n  EXPORTED_DEPS\n    folly_optional\n)\n\nfolly_add_library(\n  NAME atomic_hash_utils\n  HEADERS\n    AtomicHashUtils.h\n  EXPORTED_DEPS\n    folly_portability_asm\n)\n\nfolly_add_library(\n  NAME atomic_unordered_map_utils\n  HEADERS\n    AtomicUnorderedMapUtils.h\n  EXPORTED_DEPS\n    folly_exception\n    folly_portability_sys_mman\n    folly_portability_unistd\n)\n\nfolly_add_library(\n  NAME avx2\n  SRCS\n    Avx2.cpp\n  HEADERS\n    Avx2.h\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_portability\n)\n\nfolly_add_library(\n  NAME discriminated_ptr_detail\n  HEADERS\n    DiscriminatedPtrDetail.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n)\n\nfolly_add_library(\n  NAME file_util_detail\n  SRCS\n    FileUtilDetail.cpp\n  HEADERS\n    FileUtilDetail.h\n  DEPS\n    folly_portability_config\n  EXPORTED_DEPS\n    folly_portability_sys_types\n)\n\nfolly_add_library(\n  NAME file_util_vector_detail\n  HEADERS\n    FileUtilVectorDetail.h\n  EXPORTED_DEPS\n    folly_detail_file_util_detail\n    folly_portability_sys_uio\n    folly_portability_unistd\n)\n\nfolly_add_library(\n  NAME fingerprint_polynomial\n  HEADERS\n    FingerprintPolynomial.h\n)\n\nfolly_add_library(\n  NAME futex\n  SRCS\n    Futex.cpp\n  HEADERS\n    Futex-inl.h\n    Futex.h\n  DEPS\n    folly_hash_hash\n    folly_portability_sys_syscall\n    folly_scope_guard\n  EXPORTED_DEPS\n    folly_synchronization_parking_lot\n)\n\nfolly_add_library(\n  NAME group_varint_detail\n  HEADERS\n    GroupVarintDetail.h\n)\n\nfolly_add_library(\n  NAME ip_address\n  SRCS\n    IPAddress.cpp\n  HEADERS\n    IPAddress.h\n  DEPS\n    folly_portability_fmt_compile\n  EXPORTED_DEPS\n    folly_portability_sockets\n)\n\nfolly_add_library(\n  NAME ip_address_source\n  HEADERS\n    IPAddressSource.h\n  EXPORTED_DEPS\n    folly_detail_ip_address\n)\n\nfolly_add_library(\n  NAME iterators\n  HEADERS\n    Iterators.h\n)\n\nfolly_add_library(\n  NAME memory_idler\n  SRCS\n    MemoryIdler.cpp\n  HEADERS\n    MemoryIdler.h\n  DEPS\n    folly_concurrency_cache_locality\n    folly_glog\n    folly_memory_mallctl_helper\n    folly_memory_malloc\n    folly_portability\n    folly_portability_gflags\n    folly_portability_pthread\n    folly_portability_sys_mman\n    folly_portability_unistd\n    folly_scope_guard\n    folly_system_pid\n  EXPORTED_DEPS\n    folly_detail_futex\n    folly_hash_hash\n    folly_synchronization_atomic_struct\n    folly_system_thread_id\n)\n\nfolly_add_library(\n  NAME mpmc_pipeline_detail\n  HEADERS\n    MPMCPipelineDetail.h\n  EXPORTED_DEPS\n    folly_mpmc_queue\n)\n\nset(FOLLY_DETAIL_PERF_SCOPED_SELECT_DEPS)\nif(CMAKE_SYSTEM_NAME STREQUAL \"Linux\")\n  list(APPEND FOLLY_DETAIL_PERF_SCOPED_SELECT_DEPS folly_subprocess)\nendif()\n\nfolly_add_library(\n  NAME perf_scoped\n  SRCS\n    PerfScoped.cpp\n  HEADERS\n    PerfScoped.h\n  DEPS\n    folly_conv\n    folly_system_pid\n    folly_testing_test_util\n    ${FOLLY_DETAIL_PERF_SCOPED_SELECT_DEPS}\n  EXTERNAL_DEPS\n    Boost::regex\n)\n\nfolly_add_library(\n  NAME poly_detail\n  HEADERS\n    PolyDetail.h\n  EXPORTED_DEPS\n    folly_detail_typelist\n    folly_functional_invoke\n    folly_lang_exception\n    folly_lang_static_const\n    folly_poly_exception\n    folly_portability\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME range_common\n  SRCS\n    RangeCommon.cpp\n  HEADERS\n    RangeCommon.h\n  DEPS\n    folly_container_sparse_byte_set\n  EXPORTED_DEPS\n    folly_likely\n)\n\nfolly_add_library(\n  NAME range_simd\n  SRCS\n    RangeSimd.cpp\n  HEADERS\n    RangeSimd.h\n  DEPS\n    folly_detail_range_sse42\n    folly_external_nvidia_detail_range_sve2\n    folly_portability\n  EXPORTED_DEPS\n    folly_detail_range_common\n)\n\nfolly_add_library(\n  NAME range_sse42\n  SRCS\n    RangeSse42.cpp\n  HEADERS\n    RangeSse42.h\n  DEPS\n    folly_detail_sse\n    folly_likely\n    folly_portability\n  EXPORTED_DEPS\n    folly_detail_range_common\n)\n\nfolly_add_library(\n  NAME simple_simd_string_utils\n  SRCS\n    SimpleSimdStringUtils.cpp\n  HEADERS\n    SimpleSimdStringUtils.h\n    SimpleSimdStringUtilsImpl.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_detail_simd_any_of\n    folly_algorithm_simd_detail_simd_platform\n    folly_range\n)\n\nfolly_add_library(\n  NAME singleton\n  HEADERS\n    Singleton.h\n  EXPORTED_DEPS\n    folly_traits\n)\n\nfolly_add_library(\n  NAME slow_fingerprint\n  HEADERS\n    SlowFingerprint.h\n  EXPORTED_DEPS\n    folly_detail_fingerprint_polynomial\n    folly_fingerprint\n    folly_range\n)\n\nfolly_add_library(\n  NAME socket_fast_open\n  SRCS\n    SocketFastOpen.cpp\n  HEADERS\n    SocketFastOpen.h\n  EXPORTED_DEPS\n    folly_net_network_socket\n    folly_portability_sockets\n)\n\nfolly_add_library(\n  NAME split_string_simd\n  HEADERS\n    SplitStringSimd.h\n    SplitStringSimdImpl.h\n  EXPORTED_DEPS\n    folly_algorithm_simd_detail_simd_for_each\n    folly_algorithm_simd_detail_simd_platform\n    folly_algorithm_simd_ignore\n    folly_algorithm_simd_movemask\n    folly_lang_bits\n    folly_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME sse\n  SRCS\n    Sse.cpp\n  HEADERS\n    Sse.h\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_portability\n)\n\nfolly_add_library(\n  NAME static_singleton_manager\n  SRCS\n    StaticSingletonManager.cpp\n  HEADERS\n    StaticSingletonManager.h\n  DEPS\n    folly_memory_reentrant_allocator\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_detail_singleton\n    folly_indestructible\n    folly_lang_thunk\n    folly_lang_type_info\n    folly_likely\n    folly_utility\n)\n\nfolly_add_library(\n  NAME thread_local_detail\n  SRCS\n    ThreadLocalDetail.cpp\n  HEADERS\n    ThreadLocalDetail.h\n  DEPS\n    folly_constexpr_math\n    folly_detail_thread_local_globals\n    folly_hash_murmur_hash\n    folly_lang_hint\n    folly_memory_sanitize_leak\n    folly_random_hash\n    folly_synchronization_call_once\n    folly_utility\n  EXPORTED_DEPS\n    folly_concurrency_container_atomic_grow_array\n    folly_container_foreach\n    folly_detail_static_singleton_manager\n    folly_detail_unique_instance\n    folly_exception\n    folly_function\n    folly_lang_exception\n    folly_map_util\n    folly_memory_malloc\n    folly_portability\n    folly_portability_pthread\n    folly_scope_guard\n    folly_shared_mutex\n    folly_synchronization_micro_spin_lock\n    folly_synchronization_relaxed_atomic\n    folly_synchronized\n    folly_system_at_fork\n    folly_system_thread_id\n)\n\nfolly_add_library(\n  NAME thread_local_globals\n  SRCS\n    thread_local_globals.cpp\n  HEADERS\n    thread_local_globals.h\n  DEPS\n    folly_detail_static_singleton_manager\n    folly_lang_exception\n    folly_portability_pthread\n)\n\nfolly_add_library(\n  NAME traponavx512\n  SRCS\n    TrapOnAvx512.cpp\n  HEADERS\n    TrapOnAvx512.h\n  DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME tuple\n  HEADERS\n    tuple.h\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_lang_safe_alias_fwd\n    folly_traits\n)\n\nfolly_add_library(\n  NAME turn_sequencer\n  HEADERS\n    TurnSequencer.h\n  EXPORTED_DEPS\n    folly_chrono_hardware\n    folly_detail_futex\n    folly_portability\n    folly_portability_asm\n    folly_portability_unistd\n)\n\nfolly_add_library(\n  NAME typelist\n  HEADERS\n    TypeList.h\n  EXPORTED_DEPS\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME unique_instance\n  SRCS\n    UniqueInstance.cpp\n  HEADERS\n    UniqueInstance.h\n  DEPS\n    folly_demangle\n    folly_lang_exception\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_detail_static_singleton_manager\n)\n\nadd_subdirectory(base64_detail)\n"
  },
  {
    "path": "folly/detail/DiscriminatedPtrDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\nnamespace dptr_detail {\n\n// Determine the result type of applying a visitor of type V on pointers of\n// all types in Types..., asserting that the type is the same for all types\n// in Types...\ntemplate <typename V, typename... Types>\nstruct VisitorResult {\n  template <typename T>\n  using res = invoke_result_t<V, T*>;\n  using type = std::common_type_t<res<Types>...>;\n  static_assert((std::is_same_v<type, res<Types>> && ...));\n};\n\n// Determine the result type of applying a visitor of type V on const pointers\n// of all types in Types..., asserting that the type is the same for all types\n// in Types...\ntemplate <typename V, typename... Types>\nstruct ConstVisitorResult {\n  template <typename T>\n  using res = invoke_result_t<V, const T*>;\n  using type = std::common_type_t<res<Types>...>;\n  static_assert((std::is_same_v<type, res<Types>> && ...));\n};\n\ntemplate <typename... Types>\nstruct ApplyVisitor {\n  template <typename V, typename T, typename R>\n  static R one(V& visitor, void* ptr) {\n    return visitor(static_cast<T*>(ptr));\n  }\n\n  template <typename V, typename R = _t<VisitorResult<V&, Types...>>>\n  R operator()(size_t runtimeIndex, V& visitor, void* ptr) const {\n    using F = R(V&, void*);\n    constexpr F* f[] = {nullptr, &one<V, Types, R>...};\n    return f[runtimeIndex](visitor, ptr);\n  }\n};\n\ntemplate <typename... Types>\nstruct ApplyConstVisitor {\n  template <typename V, typename T, typename R>\n  static R one(V& visitor, void* ptr) {\n    return visitor(static_cast<const T*>(ptr));\n  }\n\n  template <typename V, typename R = _t<ConstVisitorResult<V&, Types...>>>\n  R operator()(size_t runtimeIndex, V& visitor, void* ptr) const {\n    using F = R(V&, void*);\n    constexpr F* f[] = {nullptr, &one<V, Types, R>...};\n    return f[runtimeIndex](visitor, ptr);\n  }\n};\n\n} // namespace dptr_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/FileUtilDetail.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cassert>\n\n#include <folly/detail/FileUtilDetail.h>\n#include <folly/portability/Config.h>\n\nnamespace folly {\nnamespace fileutil_detail {\nnamespace {\nstd::string getTemporaryFilePathStringWithoutTempDirectory(\n    const std::string& filePath);\n\nstd::string getTemporaryFilePathStringWithTemporaryDirectory(\n    const std::string& temporaryDirectory);\n} // namespace\n\nstd::string getTemporaryFilePathString(\n    const std::string& filePath, const std::string& temporaryDirectory) {\n  return temporaryDirectory.empty()\n      ? getTemporaryFilePathStringWithoutTempDirectory(filePath)\n      : getTemporaryFilePathStringWithTemporaryDirectory(temporaryDirectory);\n}\n\nnamespace {\nstd::string getTemporaryFilePathStringWithoutTempDirectory(\n    const std::string& filePath) {\n  return filePath + std::string{\".XXXXXX\"};\n}\n\nstd::string getTemporaryFilePathStringWithTemporaryDirectory(\n    const std::string& temporaryDirectory) {\n#if !defined(_WIN32) && !FOLLY_MOBILE\n  return (temporaryDirectory.back() == '/')\n      ? (temporaryDirectory + std::string{\"tempForAtomicWrite.XXXXXX\"})\n      : (temporaryDirectory + std::string{\"/tempForAtomicWrite.XXXXXX\"});\n#else\n  // The implementation currently does not support win32 or mobile\n  // for temporary directory based atomic file writes.\n  static_cast<void>(temporaryDirectory);\n  assert(false);\n\n  // return needed to silence -Werror=return-type errors on some builds\n  return std::string{};\n#endif\n}\n} // namespace\n} // namespace fileutil_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/FileUtilDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cerrno>\n#include <cstddef>\n#include <functional>\n#include <string>\n#include <type_traits>\n\n#include <folly/portability/SysTypes.h>\n\n//  This header is intended to be extremely lightweight. In particular, the\n//  parallel private functions for wrapping vector file-io are in a separate\n//  header.\n\nnamespace folly {\nnamespace fileutil_detail {\n\n//  The following wrapX() funcions are private functions for wrapping file-io\n//  against interrupt and partial op completions.\n\n// Wrap call to f(args) in loop to retry on EINTR\ntemplate <class F, class... Args, class R = std::invoke_result_t<F&, Args&...>>\nR wrapNoInt(F f, Args... args) {\n  R r;\n  do {\n    r = f(args...);\n  } while (r == -1 && errno == EINTR);\n  return r;\n}\n\ninline void incr(ssize_t) {}\ntemplate <typename Offset>\ninline void incr(ssize_t n, Offset& offset) {\n  offset += static_cast<Offset>(n);\n}\n\n// Wrap call to read/pread/write/pwrite(fd, buf, count, offset?) to retry on\n// incomplete reads / writes.  The variadic argument magic is there to support\n// an additional argument (offset) for pread / pwrite; see the incr() functions\n// above which do nothing if the offset is not present and increment it if it\n// is.\ntemplate <class F, class... Offset>\nssize_t wrapFull(F f, int fd, void* buf, size_t count, Offset... offset) {\n  char* b = static_cast<char*>(buf);\n  ssize_t totalBytes = 0;\n  ssize_t r;\n  do {\n    r = f(fd, b, count, offset...);\n    if (r == -1) {\n      if (errno == EINTR) {\n        continue;\n      }\n      return r;\n    }\n\n    totalBytes += r;\n    b += r;\n    count -= r;\n    incr(r, offset...);\n  } while (r != 0 && count); // 0 means EOF\n\n  return totalBytes;\n}\n\n//  Returns a string compatible for mkstemp()\nstd::string getTemporaryFilePathString(\n    const std::string& filePath, const std::string& temporaryDirectory);\n\n} // namespace fileutil_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/FileUtilVectorDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cerrno>\n\n#include <folly/detail/FileUtilDetail.h>\n#include <folly/portability/SysUio.h>\n#include <folly/portability/Unistd.h>\n\n/**\n * Helper functions and templates for FileUtil.cpp.  Declared here so\n * they can be unittested.\n */\nnamespace folly {\nnamespace fileutil_detail {\n\n// Retry reading/writing iovec objects.\n// On POSIX platforms this wraps readv/preadv/writev/pwritev to\n// retry on incomplete reads / writes.  On Windows this wraps\n// read/pread/write/pwrite.  Note that pread and pwrite are not native calls on\n// Windows and are provided by folly/portability/Unistd.cpp\ntemplate <class F, class... Offset>\nssize_t wrapvFull(F f, int fd, iovec* iov, int count, Offset... offset) {\n  ssize_t totalBytes = 0;\n  ssize_t r;\n  do {\n#ifndef _WIN32\n    r = f(fd, iov, std::min<int>(count, kIovMax), offset...);\n#else // _WIN32\n    // On Windows the caller will pass in just the simple\n    // read/write/pread/pwrite function, since the OS does not provide *v()\n    // versions.\n    r = f(fd, iov->iov_base, iov->iov_len, offset...);\n#endif // _WIN32\n    if (r == -1) {\n      if (errno == EINTR) {\n        continue;\n      }\n      return r;\n    }\n\n    if (r == 0) {\n      break; // EOF\n    }\n\n    totalBytes += r;\n    incr(r, offset...);\n    while (r != 0 && count != 0) {\n      if (r >= ssize_t(iov->iov_len)) {\n        r -= ssize_t(iov->iov_len);\n        ++iov;\n        --count;\n      } else {\n        iov->iov_base = static_cast<char*>(iov->iov_base) + r;\n        iov->iov_len -= r;\n        r = 0;\n      }\n    }\n  } while (count);\n\n  return totalBytes;\n}\n\n} // namespace fileutil_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/FingerprintPolynomial.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stddef.h>\n\n#include <cstdint>\n\nnamespace folly {\nnamespace detail {\n\n/**\n * Representation of a polynomial of degree DEG over GF(2) (that is,\n * with binary coefficients).\n *\n * Probably of no use outside of Fingerprint code; used by\n * GenerateFingerprintTables and the unittest.\n */\ntemplate <int DEG>\nclass FingerprintPolynomial {\n public:\n  static constexpr int size() { return 1 + DEG / 64; }\n\n  constexpr FingerprintPolynomial() {}\n\n  constexpr explicit FingerprintPolynomial(const uint64_t (&vals)[size()]) {\n    for (int i = 0; i < size(); i++) {\n      val_[i] = vals[i];\n    }\n  }\n\n  constexpr uint64_t get(size_t i) const { return val_[i]; }\n\n  constexpr void add(const FingerprintPolynomial<DEG>& other) {\n    for (int i = 0; i < size(); i++) {\n      val_[i] ^= other.val_[i];\n    }\n  }\n\n  // Multiply by X.  The actual degree must be < DEG.\n  constexpr void mulX() {\n    uint64_t b = 0;\n    for (int i = size() - 1; i >= 0; i--) {\n      uint64_t nb = val_[i] >> 63;\n      val_[i] = (val_[i] << 1) | b;\n      b = nb;\n    }\n  }\n\n  // Compute (this * X) mod P(X), where P(X) is a monic polynomial of degree\n  // DEG+1 (represented as a FingerprintPolynomial<DEG> object, with the\n  // implicit coefficient of X^(DEG+1)==1)\n  //\n  // This is a bit tricky. If k=DEG+1:\n  // Let P(X) = X^k + p_(k-1) * X^(k-1) + ... + p_1 * X + p_0\n  // Let this = A(X) = a_(k-1) * X^(k-1) + ... + a_1 * X + a_0\n  // Then:\n  //   A(X) * X\n  // = a_(k-1) * X^k + (a_(k-2) * X^(k-1) + ... + a_1 * X^2 + a_0 * X)\n  // = a_(k-1) * X^k + (the binary representation of A, left shift by 1)\n  //\n  // if a_(k-1) = 0, we can ignore the first term.\n  // if a_(k-1) = 1, then:\n  //   X^k mod P(X)\n  // = X^k - P(X)\n  // = P(X) - X^k\n  // = p_(k-1) * X^(k-1) + ... + p_1 * X + p_0\n  // = exactly the binary representation passed in as an argument to this\n  //   function!\n  //\n  // So A(X) * X mod P(X) is:\n  //   the binary representation of A, left shift by 1,\n  //   XOR p if a_(k-1) == 1\n  constexpr void mulXmod(const FingerprintPolynomial<DEG>& p) {\n    bool needXOR = (val_[0] & (1ULL << 63));\n    val_[0] &= ~(1ULL << 63);\n    mulX();\n    if (needXOR) {\n      add(p);\n    }\n  }\n\n  // Compute (this * X^k) mod P(X) by repeatedly multiplying by X (see above)\n  constexpr void mulXkmod(int k, const FingerprintPolynomial<DEG>& p) {\n    for (int i = 0; i < k; i++) {\n      mulXmod(p);\n    }\n  }\n\n  // add X^k, where k <= DEG\n  constexpr void addXk(int k) {\n    int word_offset = (DEG - k) / 64;\n    int bit_offset = 63 - (DEG - k) % 64;\n    val_[word_offset] ^= (1ULL << bit_offset);\n  }\n\n  // Set the highest 8 bits to val.\n  // If val is interpreted as polynomial of degree 7, then this sets *this\n  // to val * X^(DEG-7)\n  constexpr void setHigh8Bits(uint8_t val) {\n    val_[0] = ((uint64_t)val) << (64 - 8);\n    for (int i = 1; i < size(); i++) {\n      val_[i] = 0;\n    }\n  }\n\n private:\n  // Internal representation: big endian\n  // val_[0] contains the highest order coefficients, with bit 63 as the\n  // highest order coefficient\n  //\n  // If DEG+1 is not a multiple of 64,  val_[size()-1] only uses the highest\n  // order (DEG+1)%64 bits (the others are always 0)\n  uint64_t val_[size()] = {};\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Futex-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/synchronization/ParkingLot.h>\n\nnamespace folly {\nnamespace detail {\n\n/** Optimal when TargetClock is the same type as Clock.\n *\n *  Otherwise, both Clock::now() and TargetClock::now() must be invoked. */\ntemplate <typename TargetClock, typename Clock, typename Duration>\ntypename TargetClock::time_point time_point_conv(\n    std::chrono::time_point<Clock, Duration> const& time) {\n  using std::chrono::duration_cast;\n  using TimePoint = std::chrono::time_point<Clock, Duration>;\n  using TargetDuration = typename TargetClock::duration;\n  using TargetTimePoint = typename TargetClock::time_point;\n  if (time == TimePoint::max()) {\n    return TargetTimePoint::max();\n  } else if (std::is_same<Clock, TargetClock>::value) {\n    // in place of time_point_cast, which cannot compile without if-constexpr\n    auto const delta = time.time_since_epoch();\n    return TargetTimePoint(duration_cast<TargetDuration>(delta));\n  } else {\n    // different clocks with different epochs, so non-optimal case\n    auto const delta = time - Clock::now();\n    return TargetClock::now() + duration_cast<TargetDuration>(delta);\n  }\n}\n\n/**\n * Available overloads, with definitions elsewhere\n *\n * These functions are treated as ADL-extension points, the templates above\n * call these functions without them having being pre-declared.  This works\n * because ADL lookup finds the definitions of these functions when you pass\n * the relevant arguments\n */\nint futexWakeImpl(\n    const Futex<std::atomic>* futex, int count, uint32_t wakeMask);\nFutexResult futexWaitImpl(\n    const Futex<std::atomic>* futex,\n    uint32_t expected,\n    std::chrono::system_clock::time_point const* absSystemTime,\n    std::chrono::steady_clock::time_point const* absSteadyTime,\n    uint32_t waitMask);\n\nint futexWakeImpl(\n    const Futex<EmulatedFutexAtomic>* futex, int count, uint32_t wakeMask);\nFutexResult futexWaitImpl(\n    const Futex<EmulatedFutexAtomic>* futex,\n    uint32_t expected,\n    std::chrono::system_clock::time_point const* absSystemTime,\n    std::chrono::steady_clock::time_point const* absSteadyTime,\n    uint32_t waitMask);\n\ntemplate <typename Futex, typename Deadline>\ntypename std::enable_if<Deadline::clock::is_steady, FutexResult>::type\nfutexWaitImpl(\n    Futex* futex,\n    uint32_t expected,\n    Deadline const& deadline,\n    uint32_t waitMask) {\n  return futexWaitImpl(futex, expected, nullptr, &deadline, waitMask);\n}\n\ntemplate <typename Futex, typename Deadline>\ntypename std::enable_if<!Deadline::clock::is_steady, FutexResult>::type\nfutexWaitImpl(\n    Futex* futex,\n    uint32_t expected,\n    Deadline const& deadline,\n    uint32_t waitMask) {\n  return futexWaitImpl(futex, expected, &deadline, nullptr, waitMask);\n}\n\ntemplate <typename Futex>\nFutexResult futexWait(\n    const Futex* futex, uint32_t expected, uint32_t waitMask) {\n  auto rv = futexWaitImpl(futex, expected, nullptr, nullptr, waitMask);\n  assert(rv != FutexResult::TIMEDOUT);\n  return rv;\n}\n\ntemplate <typename Futex>\nint futexWake(const Futex* futex, int count, uint32_t wakeMask) {\n  return futexWakeImpl(futex, count, wakeMask);\n}\n\ntemplate <typename Futex, class Clock, class Duration>\nFutexResult futexWaitUntil(\n    const Futex* futex,\n    uint32_t expected,\n    std::chrono::time_point<Clock, Duration> const& deadline,\n    uint32_t waitMask) {\n  using Target = typename std::conditional<\n      Clock::is_steady,\n      std::chrono::steady_clock,\n      std::chrono::system_clock>::type;\n  auto const converted = time_point_conv<Target>(deadline);\n  return converted == Target::time_point::max()\n      ? futexWaitImpl(futex, expected, nullptr, nullptr, waitMask)\n      : futexWaitImpl(futex, expected, converted, waitMask);\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Futex.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/Futex.h>\n\n#include <cerrno>\n#include <cstdint>\n\n#include <folly/ScopeGuard.h>\n#include <folly/hash/Hash.h>\n#include <folly/portability/SysSyscall.h>\n#include <folly/synchronization/ParkingLot.h>\n\n#ifdef __linux__\n#include <linux/futex.h>\n#endif\n\nusing namespace std::chrono;\n\nnamespace folly {\nnamespace detail {\n\nnamespace {\n\n////////////////////////////////////////////////////\n// native implementation using the futex() syscall\n\n// The native implementation of futex wake must be async-signal-safe.\n\n#ifdef __linux__\n\n/// Certain toolchains (like Android's) don't include the full futex API in\n/// their headers even though they support it. Make sure we have our constants\n/// even if the headers don't have them.\n#ifndef FUTEX_WAIT_BITSET\n#define FUTEX_WAIT_BITSET 9\n#endif\n#ifndef FUTEX_WAKE_BITSET\n#define FUTEX_WAKE_BITSET 10\n#endif\n#ifndef FUTEX_PRIVATE_FLAG\n#define FUTEX_PRIVATE_FLAG 128\n#endif\n#ifndef FUTEX_CLOCK_REALTIME\n#define FUTEX_CLOCK_REALTIME 256\n#endif\n\nint nativeFutexWake(const void* addr, int count, uint32_t wakeMask) {\n  const auto rv = syscall(\n      __NR_futex,\n      addr, /* addr1 */\n      FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, /* op */\n      count, /* val */\n      nullptr, /* timeout */\n      nullptr, /* addr2 */\n      wakeMask); /* val3 */\n\n  /* NOTE: we ignore errors on wake for the case of a futex\n     guarding its own destruction, similar to this\n     glibc bug with sem_post/sem_wait:\n     https://sourceware.org/bugzilla/show_bug.cgi?id=12674 */\n  if (rv < 0) {\n    return 0;\n  }\n  return rv;\n}\n\ntemplate <class Clock>\nstruct timespec timeSpecFromTimePoint(time_point<Clock> absTime) {\n  auto epoch = absTime.time_since_epoch();\n  if (epoch.count() < 0) {\n    // kernel timespec_valid requires non-negative seconds and nanos in [0,1G)\n    epoch = Clock::duration::zero();\n  }\n\n  // timespec-safe seconds and nanoseconds;\n  // chrono::{nano,}seconds are `long long int`\n  // whereas timespec uses smaller types\n  using time_t_seconds = duration<std::time_t, seconds::period>;\n  using long_nanos = duration<long int, nanoseconds::period>;\n\n  auto secs = duration_cast<time_t_seconds>(epoch);\n  auto nanos = duration_cast<long_nanos>(epoch - secs);\n  struct timespec result = {secs.count(), nanos.count()};\n  return result;\n}\n\nFutexResult nativeFutexWaitImpl(\n    const void* addr,\n    uint32_t expected,\n    system_clock::time_point const* absSystemTime,\n    steady_clock::time_point const* absSteadyTime,\n    uint32_t waitMask) {\n  assert(absSystemTime == nullptr || absSteadyTime == nullptr);\n\n  int op = FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG;\n  struct timespec ts;\n  struct timespec* timeout = nullptr;\n\n  if (absSystemTime != nullptr) {\n    op |= FUTEX_CLOCK_REALTIME;\n    ts = timeSpecFromTimePoint(*absSystemTime);\n    timeout = &ts;\n  } else if (absSteadyTime != nullptr) {\n    ts = timeSpecFromTimePoint(*absSteadyTime);\n    timeout = &ts;\n  }\n\n  // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET requires an absolute timeout\n  // value - http://locklessinc.com/articles/futex_cheat_sheet/\n  const auto rv = syscall(\n      __NR_futex,\n      addr, /* addr1 */\n      op, /* op */\n      expected, /* val */\n      timeout, /* timeout */\n      nullptr, /* addr2 */\n      waitMask); /* val3 */\n\n  if (rv == 0) {\n    return FutexResult::AWOKEN;\n  } else {\n    switch (errno) {\n      case ETIMEDOUT:\n        assert(timeout != nullptr);\n        return FutexResult::TIMEDOUT;\n      case EINTR:\n        return FutexResult::INTERRUPTED;\n      case EWOULDBLOCK:\n        return FutexResult::VALUE_CHANGED;\n      default:\n        assert(false);\n        // EINVAL, EACCESS, or EFAULT.  EINVAL means there was an invalid\n        // op (should be impossible) or an invalid timeout (should have\n        // been sanitized by timeSpecFromTimePoint).  EACCESS or EFAULT\n        // means *addr points to invalid memory, which is unlikely because\n        // the caller should have segfaulted already.  We can either\n        // crash, or return a value that lets the process continue for\n        // a bit. We choose the latter. VALUE_CHANGED probably turns the\n        // caller into a spin lock.\n        return FutexResult::VALUE_CHANGED;\n    }\n  }\n}\n\n#endif // __linux__\n\n///////////////////////////////////////////////////////\n// compatibility implementation using standard C++ API\n\n// This implementation may be non-async-signal-safe.\n\nusing Lot = ParkingLot<uint32_t>;\nLot parkingLot;\n\nint emulatedFutexWake(const void* addr, int count, uint32_t waitMask) {\n  int woken = 0;\n  parkingLot.unpark(addr, [&](const uint32_t& mask) {\n    if ((mask & waitMask) == 0) {\n      return UnparkControl::RetainContinue;\n    }\n    assert(count > 0);\n    count--;\n    woken++;\n    return count > 0\n        ? UnparkControl::RemoveContinue\n        : UnparkControl::RemoveBreak;\n  });\n  return woken;\n}\n\ntemplate <typename F>\nFutexResult emulatedFutexWaitImpl(\n    F* futex,\n    uint32_t expected,\n    system_clock::time_point const* absSystemTime,\n    steady_clock::time_point const* absSteadyTime,\n    uint32_t waitMask) {\n  static_assert(\n      std::is_same<F, const Futex<std::atomic>>::value ||\n          std::is_same<F, const Futex<EmulatedFutexAtomic>>::value,\n      \"Type F must be either Futex<std::atomic> or Futex<EmulatedFutexAtomic>\");\n  ParkResult res;\n  if (absSystemTime) {\n    res = parkingLot.park_until(\n        futex,\n        waitMask,\n        [&] { return *futex == expected; },\n        [] {},\n        *absSystemTime);\n  } else if (absSteadyTime) {\n    res = parkingLot.park_until(\n        futex,\n        waitMask,\n        [&] { return *futex == expected; },\n        [] {},\n        *absSteadyTime);\n  } else {\n    res = parkingLot.park(\n        futex, waitMask, [&] { return *futex == expected; }, [] {});\n  }\n  switch (res) {\n    case ParkResult::Skip:\n      return FutexResult::VALUE_CHANGED;\n    case ParkResult::Unpark:\n      return FutexResult::AWOKEN;\n    case ParkResult::Timeout:\n      return FutexResult::TIMEDOUT;\n  }\n\n  return FutexResult::INTERRUPTED;\n}\n\n} // namespace\n\n/////////////////////////////////\n// Futex<> overloads\n\nint futexWakeImpl(\n    const Futex<std::atomic>* futex, int count, uint32_t wakeMask) {\n#ifdef __linux__\n  return nativeFutexWake(futex, count, wakeMask);\n#else\n  return emulatedFutexWake(futex, count, wakeMask);\n#endif\n}\n\nint futexWakeImpl(\n    const Futex<EmulatedFutexAtomic>* futex, int count, uint32_t wakeMask) {\n  return emulatedFutexWake(futex, count, wakeMask);\n}\n\nFutexResult futexWaitImpl(\n    const Futex<std::atomic>* futex,\n    uint32_t expected,\n    system_clock::time_point const* absSystemTime,\n    steady_clock::time_point const* absSteadyTime,\n    uint32_t waitMask) {\n#ifdef __linux__\n  return nativeFutexWaitImpl(\n      futex, expected, absSystemTime, absSteadyTime, waitMask);\n#else\n  return emulatedFutexWaitImpl(\n      futex, expected, absSystemTime, absSteadyTime, waitMask);\n#endif\n}\n\nFutexResult futexWaitImpl(\n    const Futex<EmulatedFutexAtomic>* futex,\n    uint32_t expected,\n    system_clock::time_point const* absSystemTime,\n    steady_clock::time_point const* absSteadyTime,\n    uint32_t waitMask) {\n  return emulatedFutexWaitImpl(\n      futex, expected, absSystemTime, absSteadyTime, waitMask);\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Futex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <chrono>\n#include <cstdint>\n#include <limits>\n#include <type_traits>\n\nnamespace folly {\nnamespace detail {\n\nenum class FutexResult {\n  VALUE_CHANGED, /* futex value didn't match expected */\n  AWOKEN, /* wakeup by matching futex wake, or spurious wakeup */\n  INTERRUPTED, /* wakeup by interrupting signal */\n  TIMEDOUT, /* wakeup by expiring deadline */\n};\n\n/**\n * Futex is an atomic 32 bit unsigned integer that provides access to the\n * futex() syscall on that value.  It is templated in such a way that it\n * can interact properly with DeterministicSchedule testing.\n *\n * If you don't know how to use futex(), you probably shouldn't be using\n * this class.  Even if you do know how, you should have a good reason\n * (and benchmarks to back you up).\n *\n * Because of the semantics of the futex syscall, the futex family of\n * functions are available as free functions rather than member functions\n */\ntemplate <template <typename> class Atom = std::atomic>\nusing Futex = Atom<std::uint32_t>;\n\n/**\n * Puts the thread to sleep if this->load() == expected.  Returns true when\n * it is returning because it has consumed a wake() event, false for any\n * other return (signal, this->load() != expected, or spurious wakeup).\n */\ntemplate <typename Futex>\nFutexResult futexWait(\n    const Futex* futex, uint32_t expected, uint32_t waitMask = -1);\n\n/**\n * Similar to futexWait but also accepts a deadline until when the wait call\n * may block.\n *\n * Optimal clock types: std::chrono::system_clock, std::chrono::steady_clock.\n * NOTE: On some systems steady_clock is just an alias for system_clock,\n * and is not actually steady.\n *\n * For any other clock type, now() will be invoked twice.\n */\ntemplate <typename Futex, class Clock, class Duration>\nFutexResult futexWaitUntil(\n    const Futex* futex,\n    uint32_t expected,\n    std::chrono::time_point<Clock, Duration> const& deadline,\n    uint32_t waitMask = -1);\n\n/**\n * Wakes up to count waiters where (waitMask & wakeMask) != 0, returning the\n * number of awoken threads, or -1 if an error occurred.  Note that when\n * constructing a concurrency primitive that can guard its own destruction, it\n * is likely that you will want to ignore EINVAL here (as well as making sure\n * that you never touch the object after performing the memory store that is\n * the linearization point for unlock or control handoff).  See\n * https://sourceware.org/bugzilla/show_bug.cgi?id=13690\n */\ntemplate <typename Futex>\nint futexWake(\n    const Futex* futex,\n    int count = std::numeric_limits<int>::max(),\n    uint32_t wakeMask = -1);\n\n/** A std::atomic subclass that can be used to force Futex to emulate\n *  the underlying futex() syscall.  This is primarily useful to test or\n *  benchmark the emulated implementation on systems that don't need it. */\ntemplate <typename T>\nstruct EmulatedFutexAtomic : public std::atomic<T> {\n  EmulatedFutexAtomic() noexcept = default;\n  constexpr /* implicit */ EmulatedFutexAtomic(T init) noexcept\n      : std::atomic<T>(init) {}\n  // It doesn't copy or move\n  EmulatedFutexAtomic(EmulatedFutexAtomic&& rhs) = delete;\n};\n\n} // namespace detail\n} // namespace folly\n\n#include <folly/detail/Futex-inl.h>\n"
  },
  {
    "path": "folly/detail/GroupVarintDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n\nnamespace folly {\n\ntemplate <typename T>\nclass GroupVarint;\n\nnamespace detail {\n\ntemplate <typename T>\nstruct GroupVarintTraits;\n\ntemplate <>\nstruct GroupVarintTraits<uint32_t> {\n  enum : uint32_t {\n    kGroupSize = 4,\n    kHeaderSize = 1,\n  };\n};\n\ntemplate <>\nstruct GroupVarintTraits<uint64_t> {\n  enum : uint32_t {\n    kGroupSize = 5,\n    kHeaderSize = 2,\n  };\n};\n\ntemplate <typename T>\nclass GroupVarintBase {\n protected:\n  using Traits = GroupVarintTraits<T>;\n  enum : uint32_t { kHeaderSize = Traits::kHeaderSize };\n\n public:\n  using type = T;\n\n  /**\n   * Number of integers encoded / decoded in one pass.\n   */\n  enum : uint32_t { kGroupSize = Traits::kGroupSize };\n\n  /**\n   * Maximum encoded size.\n   */\n  enum : uint32_t { kMaxSize = kHeaderSize + sizeof(type) * kGroupSize };\n\n  /**\n   * Maximum size for n values.\n   */\n  static size_t maxSize(size_t n) {\n    // Full groups\n    size_t total = (n / kGroupSize) * kFullGroupSize;\n    // Incomplete last group, if any\n    n %= kGroupSize;\n    if (n) {\n      total += kHeaderSize + n * sizeof(type);\n    }\n    return total;\n  }\n\n  /**\n   * Size of n values starting at p.\n   */\n  static size_t totalSize(const T* p, size_t n) {\n    size_t size = 0;\n    for (; n >= kGroupSize; n -= kGroupSize, p += kGroupSize) {\n      size += Derived::size(p);\n    }\n    if (n) {\n      size += Derived::partialSize(p, n);\n    }\n    return size;\n  }\n\n private:\n  using Derived = GroupVarint<T>;\n  enum { kFullGroupSize = kHeaderSize + kGroupSize * sizeof(type) };\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/IPAddress.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/IPAddress.h>\n\n#include <stdexcept>\n\n#include <folly/portability/FmtCompile.h>\n\nnamespace folly {\nnamespace detail {\n\nstd::string familyNameStrDefault(sa_family_t family) {\n  return fmt::format(FOLLY_FMT_COMPILE(\"sa_family_t({})\"), family);\n}\n\n[[noreturn]] void getNthMSBitImplThrow(size_t bitCount, sa_family_t family) {\n  throw std::invalid_argument(\n      fmt::format(\n          FOLLY_FMT_COMPILE(\"Bit index must be < {} for addresses of type: {}\"),\n          bitCount,\n          familyNameStr(family)));\n}\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/IPAddress.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <string>\n\n#include <folly/portability/Sockets.h>\n\nnamespace folly {\nnamespace detail {\n\nstd::string familyNameStrDefault(sa_family_t family);\n\ninline std::string familyNameStr(sa_family_t family) {\n  switch (family) {\n    case AF_INET:\n      return \"AF_INET\";\n    case AF_INET6:\n      return \"AF_INET6\";\n    case AF_UNSPEC:\n      return \"AF_UNSPEC\";\n    case AF_UNIX:\n      return \"AF_UNIX\";\n    default:\n      return familyNameStrDefault(family);\n  }\n}\n\n[[noreturn]] void getNthMSBitImplThrow(size_t bitCount, sa_family_t family);\n\ntemplate <typename IPAddrType>\ninline bool getNthMSBitImpl(\n    const IPAddrType& ip, size_t bitIndex, sa_family_t family) {\n  if (bitIndex >= ip.bitCount()) {\n    getNthMSBitImplThrow(ip.bitCount(), family);\n  }\n  // Underlying bytes are in n/w byte order\n  return (ip.getNthMSByte(bitIndex / 8) & (0x80 >> (bitIndex % 8))) != 0;\n}\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/IPAddressSource.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <algorithm>\n#include <array>\n#include <cstring>\n#include <string>\n#include <type_traits>\n\n#include <glog/logging.h>\n\n#include <fmt/core.h>\n#include <folly/detail/IPAddress.h>\n\n// BSDish platforms don't provide standard access to s6_addr16\n#ifndef s6_addr16\n#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \\\n    defined(__OpenBSD__)\n#define s6_addr16 __u6_addr.__u6_addr16\n#endif\n#endif\n\nnamespace folly {\nnamespace detail {\n\n/**\n * Helper for working with unsigned char* or uint8_t* ByteArray values\n */\nstruct Bytes {\n  // mask the values from two byte arrays, returning a new byte array\n  template <std::size_t N>\n  static std::array<uint8_t, N> mask(\n      const std::array<uint8_t, N>& a, const std::array<uint8_t, N>& b) {\n    static_assert(N > 0, \"Can't mask an empty ByteArray\");\n    std::size_t asize = a.size();\n    std::array<uint8_t, N> ba{{0}};\n    for (std::size_t i = 0; i < asize; i++) {\n      ba[i] = uint8_t(a[i] & b[i]);\n    }\n    return ba;\n  }\n\n  template <std::size_t N>\n  static std::pair<std::array<uint8_t, N>, uint8_t> longestCommonPrefix(\n      const std::array<uint8_t, N>& one,\n      uint8_t oneMask,\n      const std::array<uint8_t, N>& two,\n      uint8_t twoMask) {\n    static constexpr auto kBitCount = N * 8;\n    static constexpr std::array<uint8_t, 8> kMasks{{\n        0x80, // /1\n        0xc0, // /2\n        0xe0, // /3\n        0xf0, // /4\n        0xf8, // /5\n        0xfc, // /6\n        0xfe, // /7\n        0xff // /8\n    }};\n    if (oneMask > kBitCount || twoMask > kBitCount) {\n      throw std::invalid_argument(\n          fmt::format(\n              \"Invalid mask length: {}. Mask length must be <= {}\",\n              std::max(oneMask, twoMask),\n              kBitCount));\n    }\n\n    auto mask = std::min(oneMask, twoMask);\n    uint8_t byteIndex = 0;\n    std::array<uint8_t, N> ba{{0}};\n    // Compare a byte at a time. Note - I measured compared this with\n    // going multiple bytes at a time (8, 4, 2 and 1). It turns out\n    // to be 20 - 25% slower for 4 and 16 byte arrays.\n    while (byteIndex * 8 < mask && one[byteIndex] == two[byteIndex]) {\n      ba[byteIndex] = one[byteIndex];\n      ++byteIndex;\n    }\n    auto bitIndex = std::min(mask, uint8_t(byteIndex * 8));\n    uint8_t bI = uint8_t(bitIndex / 8);\n    uint8_t bM = uint8_t(bitIndex % 8);\n    // Compute the bit up to which the two byte arrays match in the\n    // unmatched byte.\n    // Here the check is bitIndex < mask since the 0th mask entry in\n    // kMasks array holds the mask for masking the MSb in this byte.\n    // We could instead make it hold so that no 0th entry masks no\n    // bits but thats a useless iteration.\n    while (\n        bitIndex < mask && ((one[bI] & kMasks[bM]) == (two[bI] & kMasks[bM]))) {\n      ba[bI] = uint8_t(one[bI] & kMasks[bM]);\n      ++bitIndex;\n      bI = uint8_t(bitIndex / 8);\n      bM = uint8_t(bitIndex % 8);\n    }\n    return {ba, bitIndex};\n  }\n\n  // create an in_addr from an uint8_t*\n  static inline in_addr mkAddress4(const uint8_t* src) {\n    union {\n      in_addr addr;\n      uint8_t bytes[4];\n    } addr;\n    std::memset(&addr, 0, 4);\n    std::memcpy(addr.bytes, src, 4);\n    return addr.addr;\n  }\n\n  // create an in6_addr from an uint8_t*\n  static inline in6_addr mkAddress6(const uint8_t* src) {\n    in6_addr addr;\n    std::memset(&addr, 0, 16);\n    std::memcpy(addr.s6_addr, src, 16);\n    return addr;\n  }\n\n  // convert an uint8_t* to its hex value\n  static std::string toHex(const uint8_t* src, std::size_t len) {\n    static const char* const lut = \"0123456789abcdef\";\n    std::string out(len * 2, 0);\n    for (std::size_t i = 0; i < len; i++) {\n      const unsigned char c = src[i];\n      out[i * 2 + 0] = lut[c >> 4];\n      out[i * 2 + 1] = lut[c & 15];\n    }\n    return out;\n  }\n\n private:\n  Bytes() = delete;\n  ~Bytes() = delete;\n};\n\n//\n// Write a maximum amount of base-converted character digits, of a\n// given base, from an unsigned integral type into a byte buffer of\n// sufficient size.\n//\n// This function does not append null terminators.\n//\n// Output buffer size must be guaranteed by caller (indirectly\n// controlled by DigitCount template parameter).\n//\n// Having these parameters at compile time allows compiler to\n// precompute several of the values, use smaller instructions, and\n// better optimize surrounding code.\n//\n// IntegralType:\n//   - Something like uint8_t, uint16_t, etc\n//\n// DigitCount is the maximum number of digits to be printed\n//   - This is tied to IntegralType and Base. For example:\n//     - uint8_t in base 10 will print at most 3 digits (\"255\")\n//     - uint16_t in base 16 will print at most 4 hex digits (\"FFFF\")\n//\n// Base is the desired output base of the string\n//   - Base 10 will print [0-9], base 16 will print [0-9a-f]\n//\n// PrintAllDigits:\n//   - Whether or not leading zeros should be printed\n//\ntemplate <\n    class IntegralType,\n    IntegralType DigitCount,\n    IntegralType Base = IntegralType(10),\n    bool PrintAllDigits = false,\n    class = typename std::enable_if<\n        std::is_integral<IntegralType>::value &&\n            std::is_unsigned<IntegralType>::value,\n        bool>::type>\ninline void writeIntegerString(IntegralType val, char** buffer) {\n  char* buf = *buffer;\n\n  if (!PrintAllDigits && val == 0) {\n    *(buf++) = '0';\n    *buffer = buf;\n    return;\n  }\n\n  IntegralType powerToPrint = 1;\n  for (IntegralType i = 1; i < DigitCount; ++i) {\n    powerToPrint *= Base;\n  }\n\n  bool found = PrintAllDigits;\n  while (powerToPrint) {\n    if (found || powerToPrint <= val) {\n      IntegralType value = IntegralType(val / powerToPrint);\n      if (Base == 10 || value < 10) {\n        value += '0';\n      } else {\n        value += ('a' - 10);\n      }\n      *(buf++) = char(value);\n      val %= powerToPrint;\n      found = true;\n    }\n\n    powerToPrint /= Base;\n  }\n\n  *buffer = buf;\n}\n\ninline size_t fastIpV4ToBufferUnsafe(const in_addr& inAddr, char* str) {\n  const uint8_t* octets = reinterpret_cast<const uint8_t*>(&inAddr.s_addr);\n  char* buf = str;\n\n  writeIntegerString<uint8_t, 3>(octets[0], &buf);\n  *(buf++) = '.';\n  writeIntegerString<uint8_t, 3>(octets[1], &buf);\n  *(buf++) = '.';\n  writeIntegerString<uint8_t, 3>(octets[2], &buf);\n  *(buf++) = '.';\n  writeIntegerString<uint8_t, 3>(octets[3], &buf);\n\n  return buf - str;\n}\n\ninline std::string fastIpv4ToString(const in_addr& inAddr) {\n  char str[sizeof(\"255.255.255.255\")];\n  return std::string(str, fastIpV4ToBufferUnsafe(inAddr, str));\n}\n\ninline void fastIpv4AppendToString(const in_addr& inAddr, std::string& out) {\n  char str[sizeof(\"255.255.255.255\")];\n  out.append(str, fastIpV4ToBufferUnsafe(inAddr, str));\n}\n\ninline size_t fastIpv6ToBufferUnsafe(const in6_addr& in6Addr, char* str) {\n#ifdef _MSC_VER\n  const uint16_t* bytes = reinterpret_cast<const uint16_t*>(&in6Addr.u.Word);\n#else\n  const uint16_t* bytes = reinterpret_cast<const uint16_t*>(&in6Addr.s6_addr16);\n#endif\n  char* buf = str;\n\n  for (int i = 0; i < 8; ++i) {\n    writeIntegerString<\n        uint16_t,\n        4, // at most 4 hex digits per ushort\n        16, // base 16 (hex)\n        true>(htons(bytes[i]), &buf);\n\n    if (i != 7) {\n      *(buf++) = ':';\n    }\n  }\n\n  return buf - str;\n}\n\ninline std::string fastIpv6ToString(const in6_addr& in6Addr) {\n  char str[sizeof(\"2001:0db8:0000:0000:0000:ff00:0042:8329\")];\n  return std::string(str, fastIpv6ToBufferUnsafe(in6Addr, str));\n}\n\ninline void fastIpv6AppendToString(const in6_addr& in6Addr, std::string& out) {\n  char str[sizeof(\"2001:0db8:0000:0000:0000:ff00:0042:8329\")];\n  out.append(str, fastIpv6ToBufferUnsafe(in6Addr, str));\n}\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Iterators.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <iterator>\n#include <type_traits>\n\n/*\n * This contains stripped-down workalikes of some Boost classes:\n *\n *   iterator_adaptor\n *   iterator_facade\n *\n * Rationale: the boost headers providing those classes are surprisingly large.\n * The bloat comes from the headers themselves, but more so, their transitive\n * includes.\n *\n * These implementations are simple and minimal.  They may be missing features\n * provided by the Boost classes mentioned above.  Also at this time they only\n * support forward-iterators.  They provide just enough for the few uses within\n * Folly libs; more features will be slapped in here if and when they're needed.\n *\n * These classes may possibly add features as well.  Care is taken not to\n * change functionality where it's expected to be the same (e.g. `dereference`\n * will do the same thing).\n *\n * These are currently only intended for use within Folly, hence their living\n * under detail.  Use outside Folly is not recommended.\n *\n * To see how to use these classes, find the instances where this is used within\n * Folly libs.  Common use cases can also be found in `IteratorsTest.cpp`.\n */\n\nnamespace folly {\nnamespace detail {\n\n/**\n * Currently this only supports forward and bidirectional iteration.  The\n * derived class must must have definitions for these methods:\n *\n *   void increment();\n *   void decrement(); // optional, to be used with bidirectional\n *   reference dereference() const;\n *   bool equal([appropriate iterator type] const& rhs) const;\n *\n * These names are consistent with those used by the Boost iterator\n * facade / adaptor classes to ease migration classes in this file.\n *\n * Template parameters:\n * D: the deriving class (CRTP)\n * V: value type\n * Tag: the iterator category, one of:\n *   std::forward_iterator_tag\n *   std::bidirectional_iterator_tag\n */\ntemplate <class D, class V, class Tag>\nclass IteratorFacade {\n public:\n  using value_type = V;\n  using reference = value_type&;\n  using pointer = value_type*;\n  using difference_type = std::ptrdiff_t;\n  using iterator_category = Tag;\n\n  friend bool operator==(D const& lhs, D const& rhs) { return equal(lhs, rhs); }\n\n  friend bool operator!=(D const& lhs, D const& rhs) { return !(lhs == rhs); }\n\n  V& operator*() const { return asDerivedConst().dereference(); }\n\n  V* operator->() const { return std::addressof(operator*()); }\n\n  D& operator++() {\n    asDerived().increment();\n    return asDerived();\n  }\n\n  D operator++(int) {\n    auto ret = asDerived(); // copy\n    asDerived().increment();\n    return ret;\n  }\n\n  D& operator--() {\n    asDerived().decrement();\n    return asDerived();\n  }\n\n  D operator--(int) {\n    auto ret = asDerived(); // copy\n    asDerived().decrement();\n    return ret;\n  }\n\n private:\n  D& asDerived() { return static_cast<D&>(*this); }\n\n  D const& asDerivedConst() const { return static_cast<D const&>(*this); }\n\n  static bool equal(D const& lhs, D const& rhs) { return lhs.equal(rhs); }\n};\n\n/**\n * Wrap one iterator while providing an interator interface with e.g. a\n * different value_type.\n *\n * Template parameters:\n * D: the deriving class (CRTP)\n * I: the wrapper iterator type\n * V: value type\n */\ntemplate <class D, class I, class V, class Tag>\nclass IteratorAdaptor : public IteratorFacade<D, V, Tag> {\n public:\n  using Super = IteratorFacade<D, V, Tag>;\n  using value_type = typename Super::value_type;\n  using iterator_category = typename Super::iterator_category;\n  using reference = typename Super::reference;\n  using pointer = typename Super::pointer;\n  using difference_type = typename Super::difference_type;\n\n  IteratorAdaptor() = default;\n  explicit IteratorAdaptor(I base) : base_(std::move(base)) {}\n\n  void increment() { ++base_; }\n\n  void decrement() { --base_; }\n\n  V& dereference() const { return *base_; }\n\n  bool equal(D const& rhs) const { return base_ == rhs.base_; }\n\n  I const& base() const { return base_; }\n  I& base() { return base_; }\n\n private:\n  I base_;\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/MPMCPipelineDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/MPMCQueue.h>\n\nnamespace folly {\n\ntemplate <class T, class... Stages>\nclass MPMCPipeline;\n\ntemplate <class T, size_t Amp>\nclass MPMCPipelineStage {\n public:\n  using value_type = T;\n  static constexpr size_t kAmplification = Amp;\n};\n\nnamespace detail {\n\n/**\n * Helper template to determine value type and amplification whether or not\n * we use MPMCPipelineStage<>\n */\ntemplate <class T>\nstruct PipelineStageInfo {\n  static constexpr size_t kAmplification = 1;\n  using value_type = T;\n};\n\ntemplate <class T, size_t Amp>\nstruct PipelineStageInfo<MPMCPipelineStage<T, Amp>> {\n  static constexpr size_t kAmplification = Amp;\n  using value_type = T;\n};\n\n/**\n * Wrapper around MPMCQueue (friend) that keeps track of tickets.\n */\ntemplate <class T>\nclass MPMCPipelineStageImpl {\n public:\n  using value_type = T;\n  template <class U, class... Stages>\n  friend class MPMCPipeline;\n\n  // Implicit so that MPMCPipeline construction works\n  /* implicit */ MPMCPipelineStageImpl(size_t capacity) : queue_(capacity) {}\n  MPMCPipelineStageImpl() {}\n\n  // only use on first stage, uses queue_.pushTicket_ instead of existing\n  // ticket\n  template <class... Args>\n  void blockingWrite(Args&&... args) noexcept {\n    queue_.blockingWrite(std::forward<Args>(args)...);\n  }\n\n  template <class... Args>\n  bool write(Args&&... args) noexcept {\n    return queue_.write(std::forward<Args>(args)...);\n  }\n\n  template <class... Args>\n  void blockingWriteWithTicket(uint64_t ticket, Args&&... args) noexcept {\n    queue_.enqueueWithTicket(ticket, std::forward<Args>(args)...);\n  }\n\n  uint64_t blockingRead(T& elem) noexcept {\n    uint64_t ticket;\n    queue_.blockingReadWithTicket(ticket, elem);\n    return ticket;\n  }\n\n  bool read(T& elem) noexcept { // only use on last stage, won't track ticket\n    return queue_.read(elem);\n  }\n\n  template <class... Args>\n  bool readAndGetTicket(uint64_t& ticket, T& elem) noexcept {\n    return queue_.readAndGetTicket(ticket, elem);\n  }\n\n  // See MPMCQueue<T>::writeCount; only works for the first stage\n  uint64_t writeCount() const noexcept { return queue_.writeCount(); }\n\n  uint64_t readCount() const noexcept { return queue_.readCount(); }\n\n private:\n  MPMCQueue<T> queue_;\n};\n\n// Product of amplifications of a tuple of PipelineStageInfo<X>\ntemplate <class Tuple>\nstruct AmplificationProduct;\n\ntemplate <>\nstruct AmplificationProduct<std::tuple<>> {\n  static constexpr size_t value = 1;\n};\n\ntemplate <class T, class... Ts>\nstruct AmplificationProduct<std::tuple<T, Ts...>> {\n  static constexpr size_t value =\n      T::kAmplification * AmplificationProduct<std::tuple<Ts...>>::value;\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/MemoryIdler.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/MemoryIdler.h>\n\n#include <climits>\n#include <cstring>\n\n#include <folly/GLog.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/memory/MallctlHelper.h>\n#include <folly/memory/Malloc.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/PThread.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/Unistd.h>\n#include <folly/system/Pid.h>\n#include <folly/system/ThreadId.h>\n\nFOLLY_GFLAGS_DEFINE_bool(\n    folly_memory_idler_purge_arenas,\n    false,\n    \"if enabled, folly memory-idler purges jemalloc arenas on thread idle\");\n\nFOLLY_GFLAGS_DEFINE_bool(\n    folly_memory_idler_madvise_stacks,\n    true,\n    \"if enabled, folly memory-idler madvises dontneed stacks on thread idle\");\n\nnamespace folly {\nnamespace detail {\n\nAtomicStruct<std::chrono::steady_clock::duration>\n    MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5));\n\nbool MemoryIdler::isUnmapUnusedStackAvailable() noexcept {\n  // Linux uses an automatic stack expansion mechanism to expand the main thread\n  // stack on demand. Before the main thread stack grows to its full extent, the\n  // vma corresponding to the main thread stack is not yet fully allocated. It's\n  // possible for the kernel to allocate the not-yet-allocated main thread stack\n  // vma to random sbrk() or mmap() requests, and for the resulting regions from\n  // these requests to be used by other user code. If this happens, the madvise-\n  // dontneed here is dangerous - it can zero arbitrary heap buffers! So it must\n  // be skipped. In the case where this runs a fork() child in that thread which\n  // returned from fork(), the os-thread-id will coincide with the pid, which is\n  // a harmless false positive where the madvise-dontneed will be skipped.\n  if (kIsLinux && getOSThreadID() == static_cast<uint64_t>(get_cached_pid())) {\n    return false;\n  }\n\n  return true;\n}\n\nvoid MemoryIdler::flushLocalMallocCaches() {\n  if (!usingJEMalloc()) {\n    return;\n  }\n  if (!mallctl || !mallctlnametomib || !mallctlbymib) {\n    FB_LOG_EVERY_MS(ERROR, 10000) << \"mallctl* weak link failed\";\n    return;\n  }\n\n  // Not using mallctlCall as this will fail if tcache is disabled.\n  mallctl(\"thread.tcache.flush\", nullptr, nullptr, nullptr, 0);\n\n  if (FLAGS_folly_memory_idler_purge_arenas) {\n    try {\n      // By default jemalloc has 4 arenas per cpu, and then assigns each\n      // thread to one of those arenas.  This means that in any service\n      // that doesn't perform a lot of context switching, the chances that\n      // another thread will be using the current thread's arena (and hence\n      // doing the appropriate dirty-page purging) are low.  Some good\n      // tuned configurations (such as that used by hhvm) use fewer arenas\n      // and then pin threads to avoid contended access.  In that case,\n      // purging the arenas is counter-productive.  We use the heuristic\n      // that if narenas <= 2 * num_cpus then we shouldn't do anything here,\n      // which detects when the narenas has been reduced from the default\n      unsigned narenas;\n      unsigned arenaForCurrent;\n      size_t mib[3];\n      size_t miblen = 3;\n\n      mallctlRead(\"opt.narenas\", &narenas);\n      mallctlRead(\"thread.arena\", &arenaForCurrent);\n      if (narenas > 2 * CacheLocality::system().numCpus &&\n          mallctlnametomib(\"arena.0.purge\", mib, &miblen) == 0) {\n        mib[1] = static_cast<size_t>(arenaForCurrent);\n        mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0);\n      }\n    } catch (const std::runtime_error& ex) {\n      FB_LOG_EVERY_MS(WARNING, 10000) << ex.what();\n    }\n  }\n}\n\n// Stack madvise isn't Linux or glibc specific, but the system calls\n// and arithmetic (and bug compatibility) are not portable.  The set of\n// platforms could be increased if it was useful.\n#if defined(__GLIBC__) && defined(__linux__) && !FOLLY_MOBILE && \\\n    (!defined(FOLLY_SANITIZE_ADDRESS) || !FOLLY_SANITIZE_ADDRESS)\n\nstatic thread_local uintptr_t tls_stackLimit;\nstatic thread_local size_t tls_stackSize;\n\nstatic size_t pageSize() {\n  static const size_t s_pageSize = sysconf(_SC_PAGESIZE);\n  return s_pageSize;\n}\n\nstatic void fetchStackLimits() {\n  int err;\n  pthread_attr_t attr;\n  if ((err = pthread_getattr_np(pthread_self(), &attr))) {\n    // some restricted environments can't access /proc\n    FB_LOG_ONCE(ERROR) << \"pthread_getaddr_np failed errno=\" << err;\n    tls_stackSize = 1;\n    return;\n  }\n  SCOPE_EXIT {\n    pthread_attr_destroy(&attr);\n  };\n\n  void* addr;\n  size_t rawSize;\n  if ((err = pthread_attr_getstack(&attr, &addr, &rawSize))) {\n    // unexpected, but it is better to continue in prod than do nothing\n    FB_LOG_ONCE(ERROR) << \"pthread_attr_getstack error \" << err;\n    assert(false);\n    tls_stackSize = 1;\n    return;\n  }\n  if (rawSize >= (1ULL << 32)) {\n    // Avoid unmapping huge swaths of memory if there is an insane\n    // stack size.  The boundary of sanity is somewhat arbitrary: 4GB.\n    //\n    // If we went into /proc to find the actual contiguous mapped pages\n    // before unmapping we wouldn't care about the stack size at all,\n    // but our current strategy is to unmap the entire range that might\n    // be used for the stack even if it hasn't been fully faulted-in.\n    //\n    // Very large stack size is a bug (hence the assert), but we can\n    // carry on if we are in prod.\n    FB_LOG_ONCE(ERROR)\n        << \"pthread_attr_getstack returned insane stack size \" << rawSize;\n    assert(false);\n    tls_stackSize = 1;\n    return;\n  }\n  assert(addr != nullptr);\n  assert(\n      0 < PTHREAD_STACK_MIN &&\n      rawSize >= static_cast<size_t>(PTHREAD_STACK_MIN));\n\n  // glibc subtracts guard page from stack size, even though pthread docs\n  // seem to imply the opposite\n  size_t guardSize;\n  if (pthread_attr_getguardsize(&attr, &guardSize) != 0) {\n    guardSize = 0;\n  }\n  assert(rawSize > guardSize);\n\n  // stack goes down, so guard page adds to the base addr\n  tls_stackLimit = reinterpret_cast<uintptr_t>(addr) + guardSize;\n  tls_stackSize = rawSize - guardSize;\n\n  assert((tls_stackLimit & (pageSize() - 1)) == 0);\n}\n\nFOLLY_NOINLINE static uintptr_t getStackPtr() {\n  char marker;\n  auto rv = reinterpret_cast<uintptr_t>(&marker);\n  return rv;\n}\n\nvoid MemoryIdler::unmapUnusedStack(size_t retain) {\n  if (!FLAGS_folly_memory_idler_madvise_stacks) {\n    return;\n  }\n\n  if (!isUnmapUnusedStackAvailable()) {\n    return;\n  }\n\n  if (tls_stackSize == 0) {\n    fetchStackLimits();\n  }\n  if (tls_stackSize <= std::max(static_cast<size_t>(1), retain)) {\n    // covers both missing stack info, and impossibly large retain\n    return;\n  }\n\n  auto sp = getStackPtr();\n  assert(sp >= tls_stackLimit);\n  assert(sp - tls_stackLimit < tls_stackSize);\n\n  auto end = (sp - retain) & ~(pageSize() - 1);\n  if (end <= tls_stackLimit) {\n    // no pages are eligible for unmapping\n    return;\n  }\n\n  size_t len = end - tls_stackLimit;\n  assert((len & (pageSize() - 1)) == 0);\n  if (madvise((void*)tls_stackLimit, len, MADV_DONTNEED) != 0) {\n    // It is likely that the stack vma hasn't been fully grown.  In this\n    // case madvise will apply dontneed to the present vmas, then return\n    // errno of ENOMEM.\n    // If thread stack pages are backed by locked or huge pages, madvise will\n    // fail with EINVAL. (EINVAL may also be returned if the address or length\n    // are bad.) Warn in debug mode, since MemoryIdler may not function as\n    // expected.\n    // We can also get an EAGAIN, theoretically.\n    PLOG_IF(WARNING, kIsDebug && errno == EINVAL) << \"madvise failed\";\n    assert(errno == EAGAIN || errno == ENOMEM || errno == EINVAL);\n  }\n}\n\n#else\n\nvoid MemoryIdler::unmapUnusedStack(size_t /* retain */) {}\n\n#endif\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/MemoryIdler.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <chrono>\n\n#include <folly/detail/Futex.h>\n#include <folly/hash/Hash.h>\n#include <folly/synchronization/AtomicStruct.h>\n#include <folly/system/ThreadId.h>\n\nnamespace folly {\nnamespace detail {\n\n/// MemoryIdler provides helper routines that allow routines to return\n/// some assigned memory resources back to the system.  The intended\n/// use is that when a thread is waiting for a long time (perhaps it\n/// is in a LIFO thread pool and hasn't been needed for a long time)\n/// it should release its thread-local malloc caches (both jemalloc and\n/// tcmalloc use these for better performance) and unmap the stack pages\n/// that contain no useful data.\nstruct MemoryIdler {\n  /// Returns memory from thread-local allocation pools to the global\n  /// pool, if we know how to for the current malloc implementation.\n  /// jemalloc is supported.\n  static void flushLocalMallocCaches();\n\n  enum {\n    /// This value is a tradeoff between reclaiming memory and triggering\n    /// a page fault immediately on wakeup.  Note that the actual unit\n    /// of idling for the stack is pages, so the actual stack that\n    /// will be available on wakeup without a page fault is between\n    /// kDefaultStackToRetain and kDefaultStackToRetain + PageSize -\n    /// 1 bytes.\n    kDefaultStackToRetain = 1024,\n  };\n\n  static bool isUnmapUnusedStackAvailable() noexcept;\n\n  /// Uses madvise to discard the portion of the thread's stack that\n  /// currently doesn't hold any data, trying to ensure that no page\n  /// faults will occur during the next retain bytes of stack allocation\n  static void unmapUnusedStack(size_t retain = kDefaultStackToRetain);\n\n  /// The system-wide default for the amount of time a blocking\n  /// thread should wait before reclaiming idle memory.  Set this to\n  /// Duration::max() to never wait.  The default value is 5 seconds.\n  /// Endpoints using this idle timeout might randomly wait longer to\n  /// avoid synchronizing their flushes.\n  static AtomicStruct<std::chrono::steady_clock::duration> defaultIdleTimeout;\n\n  /// Selects a timeout pseudo-randomly chosen to be between\n  /// idleTimeout and idleTimeout * (1 + timeoutVariationFraction), to\n  /// smooth out the behavior in a bursty system\n  template <typename IdleTime = std::chrono::steady_clock::duration>\n  static IdleTime getVariationTimeout(\n      IdleTime const& idleTimeout =\n          defaultIdleTimeout.load(std::memory_order_acquire),\n      float timeoutVariationFrac = (float)0.5) {\n    if (idleTimeout <= IdleTime::zero() || timeoutVariationFrac <= 0) {\n      return idleTimeout;\n    }\n\n    // hash the pthread_t and the time to get the adjustment\n    // Standard hash func isn't very good, so bit mix the result\n    uint64_t h = folly::hash::twang_mix64(\n        folly::hash::hash_combine(\n            getCurrentThreadID(),\n            std::chrono::system_clock::now().time_since_epoch().count()));\n\n    // multiplying the duration by a floating point doesn't work, grr\n    auto extraFrac = timeoutVariationFrac /\n        static_cast<float>(std::numeric_limits<uint64_t>::max()) *\n        static_cast<float>(h);\n    auto tics =\n        uint64_t(static_cast<float>(idleTimeout.count()) * (1 + extraFrac));\n    return IdleTime(tics);\n  }\n\n  /// Equivalent to fut.futexWait(expected, waitMask), but calls\n  /// flushLocalMallocCaches() and unmapUnusedStack(stackToRetain)\n  /// after idleTimeout has passed (if it has passed). Internally uses\n  /// fut.futexWait and fut.futexWaitUntil. The actual timeout will be\n  /// pseudo-randomly chosen to be between idleTimeout and idleTimeout *\n  /// (1 + timeoutVariationFraction), to smooth out the behavior in a\n  /// system with bursty requests. The default is to wait up to 50%\n  /// extra, so on average 25% extra.\n  template <\n      typename Futex,\n      typename IdleTime = std::chrono::steady_clock::duration>\n  static FutexResult futexWait(\n      Futex& fut,\n      uint32_t expected,\n      uint32_t waitMask = -1,\n      IdleTime const& idleTimeout =\n          defaultIdleTimeout.load(std::memory_order_acquire),\n      size_t stackToRetain = kDefaultStackToRetain,\n      float timeoutVariationFrac = (float)0.5) {\n    FutexResult pre;\n    if (futexWaitPreIdle(\n            pre,\n            fut,\n            expected,\n            std::chrono::steady_clock::time_point::max(),\n            waitMask,\n            idleTimeout,\n            stackToRetain,\n            timeoutVariationFrac)) {\n      return pre;\n    }\n\n    using folly::detail::futexWait;\n    return futexWait(&fut, expected, waitMask);\n  }\n\n  /// Equivalent to fut.futexWaitUntil(expected, deadline, waitMask), but\n  /// calls flushLocalMallocCaches() and unmapUnusedStack(stackToRetain)\n  /// after idleTimeout has passed (if it has passed). Internally uses\n  /// fut.futexWaitUntil. The actual timeout will be pseudo-randomly\n  /// chosen to be between idleTimeout and idleTimeout *\n  /// (1 + timeoutVariationFraction), to smooth out the behavior in a\n  /// system with bursty requests. The default is to wait up to 50%\n  /// extra, so on average 25% extra.\n  template <\n      typename Futex,\n      typename Deadline,\n      typename IdleTime = std::chrono::steady_clock::duration>\n  static FutexResult futexWaitUntil(\n      Futex& fut,\n      uint32_t expected,\n      Deadline const& deadline,\n      uint32_t waitMask = -1,\n      IdleTime const& idleTimeout =\n          defaultIdleTimeout.load(std::memory_order_acquire),\n      size_t stackToRetain = kDefaultStackToRetain,\n      float timeoutVariationFrac = (float)0.5) {\n    FutexResult pre;\n    if (futexWaitPreIdle(\n            pre,\n            fut,\n            expected,\n            deadline,\n            waitMask,\n            idleTimeout,\n            stackToRetain,\n            timeoutVariationFrac)) {\n      return pre;\n    }\n\n    using folly::detail::futexWaitUntil;\n    return futexWaitUntil(&fut, expected, deadline, waitMask);\n  }\n\n private:\n  template <typename Futex, typename Deadline, typename IdleTime>\n  static bool futexWaitPreIdle(\n      FutexResult& _ret,\n      Futex& fut,\n      uint32_t expected,\n      Deadline const& deadline,\n      uint32_t waitMask,\n      IdleTime idleTimeout,\n      size_t stackToRetain,\n      float timeoutVariationFrac) {\n    // idleTimeout < 0 means no flush behavior\n    if (idleTimeout < IdleTime::zero()) {\n      return false;\n    }\n\n    // idleTimeout == 0 means flush immediately, without variation\n    // idleTimeout > 0 means flush after delay, with variation\n    if (idleTimeout > IdleTime::zero()) {\n      idleTimeout = std::max(\n          IdleTime::zero(),\n          getVariationTimeout(idleTimeout, timeoutVariationFrac));\n    }\n    if (idleTimeout > IdleTime::zero()) {\n      auto idleDeadline = Deadline::clock::now() + idleTimeout;\n      if (idleDeadline < deadline) {\n        using folly::detail::futexWaitUntil;\n        auto rv = futexWaitUntil(&fut, expected, idleDeadline, waitMask);\n        if (rv != FutexResult::TIMEDOUT) {\n          // finished before timeout hit, no flush\n          _ret = rv;\n          return true;\n        }\n      } else {\n        // deadline is before the idle timeout, never flush in this case\n        return false;\n      }\n    }\n\n    // flush, then wait\n    flushLocalMallocCaches();\n    unmapUnusedStack(stackToRetain);\n    return false;\n  }\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/PerfScoped.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/PerfScoped.h>\n\n#include <folly/Conv.h>\n\n#if FOLLY_PERF_IS_SUPPORTED\n#include <folly/Subprocess.h> // @manual\n#include <folly/system/Pid.h>\n#include <folly/testing/TestUtil.h>\n#endif\n\n#include <filesystem>\n#include <stdexcept>\n#include <thread>\n\n#include <boost/regex.hpp>\n\nnamespace folly {\nnamespace detail {\n\n#if FOLLY_PERF_IS_SUPPORTED\n\nnamespace {\n\nconstexpr std::chrono::milliseconds kTerminateTimeout{500};\n\nstd::vector<std::string> prependCommonArgs(\n    const std::vector<std::string>& passed, const test::TemporaryFile* output) {\n  std::vector<std::string> res{std::string(kPerfBinaryPath)};\n  res.insert(res.end(), passed.begin(), passed.end());\n\n  res.emplace_back(\"-p\");\n  res.push_back(folly::to<std::string>(get_cached_pid()));\n  if (output) {\n    res.emplace_back(\"--output\");\n    res.push_back(output->path().string());\n  }\n  return res;\n}\n\nSubprocess::Options subprocessOptions() {\n  Subprocess::Options res;\n  res.terminateChildOnDestruction(kTerminateTimeout);\n  return res;\n}\n\n} // namespace\n\nclass PerfScoped::PerfScopedImpl {\n public:\n  PerfScopedImpl(const std::vector<std::string>& args, std::string* output)\n      : proc_(\n            prependCommonArgs(args, output != nullptr ? &outputFile_ : nullptr),\n            subprocessOptions()),\n        output_(output) {}\n\n  PerfScopedImpl(const PerfScopedImpl&) = delete;\n  PerfScopedImpl(PerfScopedImpl&&) = delete;\n  PerfScopedImpl& operator=(const PerfScopedImpl&) = delete;\n  PerfScopedImpl& operator=(PerfScopedImpl&&) = delete;\n\n  ~PerfScopedImpl() noexcept {\n    waitUntilAttached();\n\n    proc_.sendSignal(SIGINT);\n    proc_.wait();\n\n    if (output_) {\n      readFile(outputFile_.fd(), *output_);\n    }\n  }\n\n private:\n  void waitUntilAttached() {\n    const boost::regex regex{R\"(anon_inode:\\[perf_event(:\\w+)?\\])\"};\n    const auto slashproc = std::filesystem::path(\"/proc\");\n    const auto fddir = slashproc / folly::to<std::string>(proc_.pid()) / \"fd\";\n    while (true) {\n      for (const auto& entry : std::filesystem::directory_iterator(fddir)) {\n        std::error_code ec;\n        const auto target = std::filesystem::read_symlink(entry.path(), ec);\n        if (boost::regex_match(target.string(), regex)) {\n          return;\n        }\n      }\n      std::this_thread::yield();\n    }\n  }\n\n  test::TemporaryFile outputFile_;\n  Subprocess proc_;\n  std::string* output_;\n};\n\nPerfScoped::PerfScoped(\n    const std::vector<std::string>& args, std::string* output)\n    : pimpl_(std::make_unique<PerfScopedImpl>(args, output)) {}\n\n#else // FOLLY_PERF_IS_SUPPORTED\n\nclass PerfScoped::PerfScopedImpl {};\n\nPerfScoped::PerfScoped(\n    const std::vector<std::string>& args, std::string* output) {\n  (void)args;\n  (void)output;\n  throw std::runtime_error(\"Perf is not supported on Windows.\");\n}\n\n#endif\n\nPerfScoped::PerfScoped() = default;\nPerfScoped::PerfScoped(PerfScoped&&) noexcept = default;\nPerfScoped& PerfScoped::operator=(PerfScoped&&) noexcept = default;\nPerfScoped::~PerfScoped() noexcept = default;\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/PerfScoped.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <vector>\n\nnamespace folly {\nnamespace detail {\n\n#if defined(__linux__) && !defined(__ANDROID__)\n#define FOLLY_PERF_IS_SUPPORTED 1\n#else\n#define FOLLY_PERF_IS_SUPPORTED 0\n#endif\n\nconstexpr std::string_view kPerfBinaryPath = \"/usr/bin/perf\";\n\n/*\n * A folly::benchmark helper for attaching `perf` profiler\n * to a given block of code.\n *\n * Only available on linux.\n */\nclass PerfScoped {\n public:\n  // Not running. Used to be able to move things/not start\n  PerfScoped();\n\n  // Actually starts perf\n  // Output is for testing, if passed, perf output will be\n  // put there.\n  explicit PerfScoped(\n      const std::vector<std::string>& args, std::string* output = nullptr);\n\n  // Sends Ctrl-C to stop recording.\n  ~PerfScoped() noexcept;\n\n  PerfScoped(const PerfScoped&) = delete;\n  PerfScoped& operator=(const PerfScoped&) = delete;\n\n  PerfScoped(PerfScoped&& x) noexcept;\n  PerfScoped& operator=(PerfScoped&& x) noexcept;\n\n private:\n  class PerfScopedImpl;\n\n  std::unique_ptr<PerfScopedImpl> pimpl_;\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/PolyDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <new>\n#include <tuple>\n#include <type_traits>\n#include <typeinfo>\n#include <utility>\n\n#include <folly/PolyException.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/detail/TypeList.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/StaticConst.h>\n\nnamespace folly {\n/// \\cond\nnamespace detail {\ntemplate <class I>\nstruct PolyRoot;\n\nusing RRef_ = MetaQuoteTrait<std::add_rvalue_reference>;\nusing LRef_ = MetaQuoteTrait<std::add_lvalue_reference>;\n\ntemplate <typename T>\nstruct XRef_ : Type<MetaQuoteTrait<Type>> {};\ntemplate <typename T>\nusing XRef = _t<XRef_<T>>;\ntemplate <typename T>\nstruct XRef_<T&&> : Type<MetaCompose<RRef_, XRef<T>>> {};\ntemplate <typename T>\nstruct XRef_<T&> : Type<MetaCompose<LRef_, XRef<T>>> {};\ntemplate <typename T>\nstruct XRef_<T const> : Type<MetaQuoteTrait<std::add_const>> {};\n\ntemplate <class A, class B>\nusing AddCvrefOf = MetaApply<XRef<B>, A>;\n} // namespace detail\n/// \\endcond\n\ntemplate <class I>\nstruct Poly;\n\ntemplate <class T, class I>\ndetail::AddCvrefOf<T, I>& poly_cast(detail::PolyRoot<I>&);\n\ntemplate <class T, class I>\ndetail::AddCvrefOf<T, I> const& poly_cast(detail::PolyRoot<I> const&);\n\ntemplate <auto...>\nstruct PolyMembers;\n\n/// \\cond\nnamespace detail {\n/* *****************************************************************************\n * IMPLEMENTATION NOTES\n *\n\nBuilding the Interface\n----------------------\n\nHere is a high-level description of how Poly works. Users write an interface\nsuch as:\n\n  struct Mine {\n    template <class Base>\n    struct Interface {\n      int Exec() const {\n        return folly::poly_call<0>(*this);\n      }\n    }\n    template <class T>\n    using Members = folly::PolyMembers<&T::Exec>;\n  };\n\nThen they instantiate Poly<Mine>, which will have an Exec member function\nof the correct signature. The Exec member function comes from\nMine::Interface<PolyNode<Mine, PolyRoot<Mine>>>, from which Poly<Mine> inherits.\nHere's what each piece means:\n\n- PolyRoot<I>: stores Data, which is a union of a void* (used when the Poly is\n  storing an object on the heap or a reference) and some aligned storage (used\n  when the Poly is storing an object in-situ). PolyRoot also stores a vtable\n  pointer for interface I, which is a pointer to a struct containing function\n  pointers. The function pointers are bound member functions (e.g.,\n  SomeType::Exec). More on the vtable pointer and how it is generated below.\n\n- PolyNode: provides the hooks used by folly::poly_call to dispatch to the\ncorrectly bound member function for this interface. In the context of an\ninterface I, folly::poly_call<K>(*this, args...) will:\n    1. Fetch the vtable pointer from PolyRoot,\n    2. Select the I portion of that vtable (which, in the case of interface\n       extension, may only be a part of the total vtable),\n    3. Fetch the K-th function pointer from that vtable part,\n    4. Call through that function pointer, passing Data (from PolyRoot) and any\n       additional arguments in the folly::poly_call<K> invocation.\n\nIn the case of interface extension -- for instance, if interface Mine extended\ninterface Yours by inheriting from PolyExtends<Yours> -- then interface Mine\nwill have a list of base interfaces in a typelist called \"Subsumptions\".\nPoly<Mine> will fold all the subsumed interfaces together, linearly inheriting\nfrom them. To take the example of an interface Mine that extends Yours,\nPoly<Mine> would inherit from this type:\n\n  Mine::Interface<\n    PolyNode<Mine,\n      Your::Interface<\n        PolyNode<Your, PolyRoot<Mine>>>>>\n\nThrough linear inheritance, Poly<Mine> ends up with the public member functions\nof both interfaces, Mine and Yours.\n\nVTables\n-------\n\nAs mentioned above, PolyRoot<I> stores a vtable pointer for interface I. The\nvtable is a struct whose members are function pointers. How are the types of\nthose function pointers computed from the interface? A dummy type is created,\nArchetype<I>, in much the same way that Poly<I>'s base type is computed. Instead\nof PolyNode and PolyRoot, there is ArchetypeNode and ArchetypeRoot. These types\nalso provide hooks for folly::poly_call, but they are dummy hooks that do\nnothing. (Actually, they call std::terminate; they will never be called.) Once\nArchetype<I> has been constructed, it is a concrete type that has all the\nmember functions of the interface and its subsumed interfaces. That type is\npassed to Mine::Members, which takes the address of Archetype<I>::Exec and\ninspects the resulting member function type. This is done for each member in the\ninterface. From a list of [member] function pointers, it is a simple matter of\nmetaprogramming to build a struct of function pointers. std::tuple is used for\nthis.\n\nAn extra field is added to the tuple for a function that handles all of the\n\"special\" operations: destruction, copying, moving, getting the type\ninformation, getting the address of the stored object, and fetching a fixed-up\nvtable pointer for reference conversions (e.g., I -> I&, I& -> I const&, etc).\n\nSubsumed interfaces are handled by having VTable<IDerived> inherit from\nBasePtr<IBase>, where BasePtr<IBase> has only one member of type\nVTable<IBase> const*.\n\nNow that the type of VTable<I> is computed, how are the fields populated?\nPoly<I> default-constructs to an empty state. Its vtable pointer points to a\nvtable whose fields are initialized with the addresses of functions that do\nnothing but throw a BadPolyAccess exception. That way, if you call a member\nfunction on an empty Poly, you get an exception. The function pointer\ncorresponding to the \"special\" operations points to a no-op function; copying,\nmoving and destroying an empty Poly does nothing.\n\nOn the other hand, when you pass an object of type T satisfying interface I to\nPoly<I>'s constructor or assignment operator, a vtable for {I,T} is reified by\npassing type T to I::Members, thereby creating a list of bindings for T's member\nfunctions. The address of this vtable gets stored in the PolyRoot<I> subobject,\nimbuing the Poly object with the behaviors of type T. The T object itself gets\nstored either on the heap or in the aligned storage within the Poly object\nitself, depending on the size of T and whether or not it has a noexcept move\nconstructor.\n*/\n\ntemplate <class T, template <class...> class U>\nstruct IsInstanceOf : std::false_type {};\n\ntemplate <class... Ts, template <class...> class U>\nstruct IsInstanceOf<U<Ts...>, U> : std::true_type {};\n\ntemplate <class Then>\ndecltype(auto) if_constexpr(std::true_type, Then then) {\n  return then(Identity{});\n}\n\ntemplate <class Then>\nvoid if_constexpr(std::false_type, Then) {}\n\ntemplate <class Then, class Else>\ndecltype(auto) if_constexpr(std::true_type, Then then, Else) {\n  return then(Identity{});\n}\n\ntemplate <class Then, class Else>\ndecltype(auto) if_constexpr(std::false_type, Then, Else else_) {\n  return else_(Identity{});\n}\n\nenum class Op : short { eNuke, eMove, eCopy, eType, eAddr, eRefr };\n\nenum class RefType : std::uintptr_t { eRvalue, eLvalue, eConstLvalue };\n\nstruct Data;\n\ntemplate <class I>\nstruct PolyVal;\n\ntemplate <class I>\nstruct PolyRef;\n\nstruct PolyAccess;\n\ntemplate <class T>\nusing IsPoly = IsInstanceOf<remove_cvref_t<T>, Poly>;\n\n// Given an interface I and a concrete type T that satisfies the interface\n// I, create a list of member function bindings from members of T to members\n// of I.\ntemplate <class I, class T>\nusing MembersOf = typename I::template Members<remove_cvref_t<T>>;\n\n// Given an interface I and a base type T, create a type that implements\n// the interface I in terms of the capabilities of T.\ntemplate <class I, class T>\nusing InterfaceOf = typename I::template Interface<T>;\n\nstruct PolyBase {};\n\ntemplate <class I, class = void>\nstruct SubsumptionsOf_ {\n  using type = TypeList<>;\n};\n\ntemplate <class I>\nusing InclusiveSubsumptionsOf = TypePushFront<_t<SubsumptionsOf_<I>>, I>;\n\ntemplate <class I>\nstruct SubsumptionsOf_<I, void_t<typename I::Subsumptions>> {\n  using type = TypeJoin<TypeTransform<\n      typename I::Subsumptions,\n      MetaQuote<InclusiveSubsumptionsOf>>>;\n};\n\ntemplate <class I>\nusing SubsumptionsOf = TypeReverseUnique<_t<SubsumptionsOf_<I>>>;\n\nstruct Bottom {\n  template <class T>\n  [[noreturn]] /* implicit */ operator T&&() const {\n    std::terminate();\n  }\n};\n\nusing ArchetypeNode = MetaQuote<InterfaceOf>;\n\ntemplate <class I>\nstruct ArchetypeRoot;\n\ntemplate <class I>\nusing Archetype =\n    TypeFold<InclusiveSubsumptionsOf<I>, ArchetypeRoot<I>, ArchetypeNode>;\n\nstruct ArchetypeBase : Bottom {\n  ArchetypeBase() = default;\n  template <class T>\n  /* implicit */ ArchetypeBase(T&&);\n  template <std::size_t, class... As>\n  [[noreturn]] Bottom _polyCall_(As&&...) const {\n    std::terminate();\n  }\n\n  friend bool operator==(ArchetypeBase const&, ArchetypeBase const&);\n  friend bool operator!=(ArchetypeBase const&, ArchetypeBase const&);\n  friend bool operator<(ArchetypeBase const&, ArchetypeBase const&);\n  friend bool operator<=(ArchetypeBase const&, ArchetypeBase const&);\n  friend bool operator>(ArchetypeBase const&, ArchetypeBase const&);\n  friend bool operator>=(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator++(ArchetypeBase const&);\n  friend Bottom operator++(ArchetypeBase const&, int);\n  friend Bottom operator--(ArchetypeBase const&);\n  friend Bottom operator--(ArchetypeBase const&, int);\n  friend Bottom operator+(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator+=(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator-(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator-=(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator*(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator*=(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator/(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator/=(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator%(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator%=(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator<<(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator<<=(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator>>(ArchetypeBase const&, ArchetypeBase const&);\n  friend Bottom operator>>=(ArchetypeBase const&, ArchetypeBase const&);\n};\n\ntemplate <class I>\nstruct ArchetypeRoot : ArchetypeBase {\n  template <class Node, class Tfx>\n  using _polySelf_ = Archetype<AddCvrefOf<MetaApply<Tfx, I>, Node>>;\n  using _polyInterface_ = I;\n};\n\nstruct Data {\n  Data() = default;\n  // Suppress compiler-generated copy ops to not copy anything:\n  Data(Data const&) {}\n  Data& operator=(Data const&) { return *this; }\n  union {\n    void* pobj_ = nullptr;\n    folly::aligned_storage_for_t<double[2]> buff_;\n  };\n};\n\ntemplate <class U, class I>\nusing Arg =\n    If<std::is_same<remove_cvref_t<U>, Archetype<I>>::value,\n       Poly<AddCvrefOf<I, U const&>>,\n       U>;\n\ntemplate <class U, class I>\nusing Ret =\n    If<std::is_same<remove_cvref_t<U>, Archetype<I>>::value,\n       AddCvrefOf<Poly<I>, U>,\n       U>;\n\ntemplate <class Member, class I>\nstruct SignatureOf_;\n\ntemplate <class R, class C, class... As, class I>\nstruct SignatureOf_<R (C::*)(As...), I> {\n  using type = Ret<R, I> (*)(Data&, Arg<As, I>...);\n};\n\ntemplate <class R, class C, class... As, class I>\nstruct SignatureOf_<R (C::*)(As...) const, I> {\n  using type = Ret<R, I> (*)(Data const&, Arg<As, I>...);\n};\n\ntemplate <class R, class C, class... As, class I>\nstruct SignatureOf_<R (C::*)(As...) noexcept, I> {\n  using type = std::add_pointer_t<Ret<R, I>(Data&, Arg<As, I>...) noexcept>;\n};\n\ntemplate <class R, class C, class... As, class I>\nstruct SignatureOf_<R (C::*)(As...) const noexcept, I> {\n  using type =\n      std::add_pointer_t<Ret<R, I>(Data const&, Arg<As, I>...) noexcept>;\n};\n\ntemplate <class R, class This, class... As, class I>\nstruct SignatureOf_<R (*)(This&, As...), I> {\n  using type = Ret<R, I> (*)(Data&, Arg<As, I>...);\n};\n\ntemplate <class R, class This, class... As, class I>\nstruct SignatureOf_<R (*)(This const&, As...), I> {\n  using type = Ret<R, I> (*)(Data const&, Arg<As, I>...);\n};\n\ntemplate <auto Arch, class I>\nusing SignatureOf = _t<SignatureOf_<decltype(Arch), I>>;\n\ntemplate <auto User, class I, class Sig = SignatureOf<User, I>>\nstruct ArgTypes_;\n\ntemplate <auto User, class I, class Ret, class Data, class... Args>\nstruct ArgTypes_<User, I, Ret (*)(Data, Args...)> {\n  using type = TypeList<Args...>;\n};\n\ntemplate <auto User, class I, class Ret, class Data, class... Args>\nstruct ArgTypes_<User, I, Ret (*)(Data, Args...) noexcept> {\n  using type = TypeList<Args...>;\n};\n\ntemplate <auto User, class I>\nusing ArgTypes = _t<ArgTypes_<User, I>>;\n\ntemplate <class R, class... Args>\nusing FnPtr = R (*)(Args...);\n\nstruct ThrowThunk {\n  template <class R, class... Args>\n  constexpr /* implicit */ operator FnPtr<R, Args...>() const noexcept {\n    struct _ {\n      static R call(Args...) { throw_exception<BadPolyAccess>(); }\n    };\n    return &_::call;\n  }\n};\n\ninline constexpr ThrowThunk throw_() noexcept {\n  return ThrowThunk{};\n}\n\ntemplate <class T>\ninline constexpr bool inSitu() noexcept {\n  return !std::is_reference<T>::value &&\n      sizeof(std::decay_t<T>) <= sizeof(Data) &&\n      std::is_nothrow_move_constructible<std::decay_t<T>>::value;\n}\n\ntemplate <class T>\nT& get(Data& d) noexcept {\n  if (inSitu<T>()) {\n    return *(std::add_pointer_t<T>)static_cast<void*>(&d.buff_);\n  } else {\n    return *static_cast<std::add_pointer_t<T>>(d.pobj_);\n  }\n}\n\ntemplate <class T>\nT const& get(Data const& d) noexcept {\n  if (inSitu<T>()) {\n    return *(std::add_pointer_t<T const>)static_cast<void const*>(&d.buff_);\n  } else {\n    return *static_cast<std::add_pointer_t<T const>>(d.pobj_);\n  }\n}\n\nenum class State : short { eEmpty, eInSitu, eOnHeap };\n\ntemplate <class T>\nstruct IsPolyRef : std::false_type {};\n\ntemplate <class T>\nstruct IsPolyRef<Poly<T&>> : std::true_type {};\n\ntemplate <class Arg, class U>\ndecltype(auto) convert(U&& u) {\n  return detail::if_constexpr(\n      StrictConjunction<\n          IsPolyRef<remove_cvref_t<U>>,\n          Negation<std::is_convertible<U, Arg>>>(),\n      [&](auto id) -> decltype(auto) {\n        return poly_cast<remove_cvref_t<Arg>>(id(u).get());\n      },\n      [&](auto id) -> U&& { return static_cast<U&&>(id(u)); });\n}\n\ntemplate <class Fun>\nstruct IsConstMember : std::false_type {};\n\ntemplate <class R, class C, class... As>\nstruct IsConstMember<R (C::*)(As...) const> : std::true_type {};\n\ntemplate <class R, class C, class... As>\nstruct IsConstMember<R (*)(C const&, As...)> : std::true_type {};\n\ntemplate <class R, class C, class... As>\nstruct IsConstMember<R (C::*)(As...) const noexcept> : std::true_type {};\n\ntemplate <class R, class C, class... As>\nstruct IsConstMember<R (*)(C const&, As...) noexcept> : std::true_type {};\n\ntemplate <\n    class T,\n    auto User,\n    class I,\n    class = ArgTypes<User, I>,\n    class = Bool<true>>\nstruct ThunkFn {\n  template <class R, class D, class... As>\n  constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept {\n    return nullptr;\n  }\n};\n\ntemplate <class T, auto User, class I, class... Args>\nstruct ThunkFn<\n    T,\n    User,\n    I,\n    TypeList<Args...>,\n    Bool<\n        !std::is_const<std::remove_reference_t<T>>::value ||\n        IsConstMember<decltype(User)>::value>> {\n  template <class R, class D, class... As>\n  constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept {\n    struct _ {\n      static R call(D& d, As... as) {\n        return folly::invoke(\n            User, get<T>(d), convert<Args>(static_cast<As&&>(as))...);\n      }\n    };\n    return &_::call;\n  }\n};\n\ntemplate <\n    class I,\n    class = MembersOf<I, Archetype<I>>,\n    class = SubsumptionsOf<I>>\nstruct VTable;\n\ntemplate <class T, auto User, class I>\ninline constexpr ThunkFn<T, User, I> thunk_() noexcept {\n  return ThunkFn<T, User, I>{};\n}\n\ntemplate <class I>\nconstexpr VTable<I> const* vtable() noexcept {\n  return &StaticConst<VTable<I>>::value;\n}\n\ntemplate <class I, class T>\nstruct VTableFor : VTable<I> {\n  constexpr VTableFor() noexcept : VTable<I>{Type<T>{}} {}\n};\n\ntemplate <class I, class T>\nconstexpr VTable<I> const* vtableFor() noexcept {\n  return &StaticConst<VTableFor<I, T>>::value;\n}\n\ntemplate <class I, class T>\nconstexpr void* vtableForRef(RefType ref) {\n  switch (ref) {\n    case RefType::eRvalue:\n      return const_cast<VTable<I>*>(vtableFor<I, T&&>());\n    case RefType::eLvalue:\n      return const_cast<VTable<I>*>(vtableFor<I, T&>());\n    case RefType::eConstLvalue:\n      return const_cast<VTable<I>*>(vtableFor<I, T const&>());\n  }\n  return nullptr;\n}\n\ntemplate <\n    class I,\n    class T,\n    std::enable_if_t<std::is_reference<T>::value, int> = 0>\nvoid* execOnHeap(Op op, Data* from, void* to) {\n  switch (op) {\n    case Op::eNuke:\n      break;\n    case Op::eMove:\n    case Op::eCopy:\n      static_cast<Data*>(to)->pobj_ = from->pobj_;\n      break;\n    case Op::eType:\n      return const_cast<void*>(static_cast<void const*>(&typeid(T)));\n    case Op::eAddr:\n      if (*static_cast<std::type_info const*>(to) == typeid(T)) {\n        return from->pobj_;\n      }\n      throw_exception<BadPolyCast>();\n    case Op::eRefr:\n      return vtableForRef<I, remove_cvref_t<T>>(\n          static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to)));\n  }\n  return nullptr;\n}\n\ntemplate <\n    class I,\n    class T,\n    std::enable_if_t<Negation<std::is_reference<T>>::value, int> = 0>\nvoid* execOnHeap(Op op, Data* from, void* to) {\n  switch (op) {\n    case Op::eNuke:\n      delete &get<T>(*from);\n      break;\n    case Op::eMove:\n      static_cast<Data*>(to)->pobj_ = std::exchange(from->pobj_, nullptr);\n      break;\n    case Op::eCopy:\n      detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) {\n        static_cast<Data*>(to)->pobj_ = new T(id(get<T>(*from)));\n      });\n      break;\n    case Op::eType:\n      return const_cast<void*>(static_cast<void const*>(&typeid(T)));\n    case Op::eAddr:\n      if (*static_cast<std::type_info const*>(to) == typeid(T)) {\n        return from->pobj_;\n      }\n      throw_exception<BadPolyCast>();\n    case Op::eRefr:\n      return vtableForRef<I, remove_cvref_t<T>>(\n          static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to)));\n  }\n  return nullptr;\n}\n\ntemplate <class I, class T>\nvoid* execInSitu(Op op, Data* from, void* to) {\n  switch (op) {\n    case Op::eNuke:\n      get<T>(*from).~T();\n      break;\n    case Op::eMove:\n      ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_))\n          T(std::move(get<T>(*from)));\n      get<T>(*from).~T();\n      break;\n    case Op::eCopy:\n      detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) {\n        ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_))\n            T(id(get<T>(*from)));\n      });\n      break;\n    case Op::eType:\n      return const_cast<void*>(static_cast<void const*>(&typeid(T)));\n    case Op::eAddr:\n      if (*static_cast<std::type_info const*>(to) == typeid(T)) {\n        return &from->buff_;\n      }\n      throw_exception<BadPolyCast>();\n    case Op::eRefr:\n      return vtableForRef<I, remove_cvref_t<T>>(\n          static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to)));\n  }\n  return nullptr;\n}\n\ninline void* noopExec(Op op, Data*, void*) {\n  if (op == Op::eAddr) {\n    throw_exception<BadPolyAccess>();\n  }\n  return const_cast<void*>(static_cast<void const*>(&typeid(void)));\n}\n\ntemplate <class I>\nstruct BasePtr {\n  VTable<I> const* vptr_;\n};\n\ntemplate <class I, class T>\nconstexpr void* (*getOpsImpl(std::true_type) noexcept)(Op, Data*, void*) {\n  return &execInSitu<I, T>;\n}\n\ntemplate <class I, class T>\nconstexpr void* (*getOpsImpl(std::false_type) noexcept)(Op, Data*, void*) {\n  return &execOnHeap<I, T>;\n}\n\ntemplate <class I, class T>\nconstexpr void* (*getOps() noexcept)(Op, Data*, void*) {\n  return getOpsImpl<I, T>(std::integral_constant<bool, inSitu<T>()>{});\n}\n\ntemplate <class I, auto... Arch, class... S>\nstruct VTable<I, PolyMembers<Arch...>, TypeList<S...>>\n    : BasePtr<S>..., std::tuple<SignatureOf<Arch, I>...> {\n private:\n  template <class T, auto... User>\n  constexpr VTable(Type<T>, PolyMembers<User...>) noexcept\n      : BasePtr<S>{vtableFor<S, T>()}...,\n        std::tuple<SignatureOf<Arch, I>...>{thunk_<T, User, I>()...},\n        state_{inSitu<T>() ? State::eInSitu : State::eOnHeap},\n        ops_{getOps<I, T>()} {}\n\n public:\n  constexpr VTable() noexcept\n      : BasePtr<S>{vtable<S>()}...,\n        std::tuple<SignatureOf<Arch, I>...>{\n            static_cast<SignatureOf<Arch, I>>(throw_())...},\n        state_{State::eEmpty},\n        ops_{&noopExec} {}\n\n  template <class T>\n  explicit constexpr VTable(Type<T>) noexcept\n      : VTable{Type<T>{}, MembersOf<I, T>{}} {}\n\n  State state_;\n  void* (*ops_)(Op, Data*, void*);\n\n  std::tuple<SignatureOf<Arch, I>...>& tuple() { return *this; }\n  const std::tuple<SignatureOf<Arch, I>...>& tuple() const { return *this; }\n};\n\ntemplate <class I>\nconstexpr VTable<I> const& select(VTable<_t<Type<I>>> const& vtbl) noexcept {\n  return vtbl;\n}\n\ntemplate <class I>\nconstexpr VTable<I> const& select(BasePtr<_t<Type<I>>> const& base) noexcept {\n  return *base.vptr_;\n}\n\nstruct PolyAccess {\n  template <std::size_t N, typename This, typename... As>\n  static auto call(This&& _this, As&&... args)\n      -> decltype(static_cast<This&&>(_this).template _polyCall_<N>(\n          static_cast<As&&>(args)...)) {\n    static_assert(\n        !IsInstanceOf<std::decay_t<This>, Poly>::value,\n        \"When passing a Poly<> object to call(), you must explicitly \"\n        \"say which Interface to dispatch to, as in \"\n        \"call<0, MyInterface>(self, args...)\");\n    return static_cast<This&&>(_this).template _polyCall_<N>(\n        static_cast<As&&>(args)...);\n  }\n\n  template <class Poly>\n  using Iface = typename remove_cvref_t<Poly>::_polyInterface_;\n\n  template <class Node, class Tfx = MetaIdentity>\n  static typename remove_cvref_t<Node>::template _polySelf_<Node, Tfx> self_();\n\n  template <class T, class Poly, class I = Iface<Poly>>\n  static decltype(auto) cast(Poly&& _this) {\n    using Ret = AddCvrefOf<AddCvrefOf<T, I>, Poly&&>;\n    return static_cast<Ret>(\n        *static_cast<std::add_pointer_t<Ret>>(_this.vptr_->ops_(\n            Op::eAddr,\n            const_cast<Data*>(static_cast<Data const*>(&_this)),\n            const_cast<void*>(static_cast<void const*>(&typeid(T))))));\n  }\n\n  template <class Poly>\n  static decltype(auto) root(Poly&& _this) noexcept {\n    return static_cast<Poly&&>(_this)._polyRoot_();\n  }\n\n  template <class I>\n  static std::type_info const& type(PolyRoot<I> const& _this) noexcept {\n    return *static_cast<std::type_info const*>(\n        _this.vptr_->ops_(Op::eType, nullptr, nullptr));\n  }\n\n  template <class I>\n  static VTable<remove_cvref_t<I>> const* vtable(\n      PolyRoot<I> const& _this) noexcept {\n    return _this.vptr_;\n  }\n\n  template <class I>\n  static Data* data(PolyRoot<I>& _this) noexcept {\n    return &_this;\n  }\n\n  template <class I>\n  static Data const* data(PolyRoot<I> const& _this) noexcept {\n    return &_this;\n  }\n\n  template <class I>\n  static Poly<I&&> move(PolyRoot<I&> const& _this) noexcept {\n    return Poly<I&&>{_this, Type<I&>{}};\n  }\n\n  template <class I>\n  static Poly<I const&> move(PolyRoot<I const&> const& _this) noexcept {\n    return Poly<I const&>{_this, Type<I const&>{}};\n  }\n};\n\ntemplate <class I, class Tail>\nstruct PolyNode : Tail {\n private:\n  friend PolyAccess;\n  using Tail::Tail;\n\n  template <std::size_t K, typename... As>\n  decltype(auto) _polyCall_(As&&... as) {\n    // Convert VTable to tuple explicitly to workaround an MSVC bug.\n    return std::get<K>(select<I>(*PolyAccess::vtable(*this)).tuple())(\n        *PolyAccess::data(*this), static_cast<As&&>(as)...);\n  }\n  template <std::size_t K, typename... As>\n  decltype(auto) _polyCall_(As&&... as) const {\n    return std::get<K>(select<I>(*PolyAccess::vtable(*this)).tuple())(\n        *PolyAccess::data(*this), static_cast<As&&>(as)...);\n  }\n};\n\nstruct MakePolyNode {\n  template <class I, class State>\n  using apply = InterfaceOf<I, PolyNode<I, State>>;\n};\n\ntemplate <class I>\nstruct PolyRoot : private PolyBase, private Data {\n  friend PolyAccess;\n  friend Poly<I>;\n  friend PolyVal<I>;\n  friend PolyRef<I>;\n  template <class Node, class Tfx>\n  using _polySelf_ = Poly<AddCvrefOf<MetaApply<Tfx, I>, Node>>;\n  using _polyInterface_ = I;\n\n private:\n  PolyRoot& _polyRoot_() noexcept { return *this; }\n  PolyRoot const& _polyRoot_() const noexcept { return *this; }\n  VTable<std::decay_t<I>> const* vptr_ = vtable<std::decay_t<I>>();\n};\n\ntemplate <class I>\nusing PolyImpl = TypeFold<\n    InclusiveSubsumptionsOf<remove_cvref_t<I>>,\n    PolyRoot<I>,\n    MakePolyNode>;\n\n// A const-qualified function type means the user is trying to disambiguate\n// a member function pointer.\ntemplate <class Fun> // Fun = R(As...) const\nstruct Sig {\n  template <class T>\n  constexpr Fun T::* operator()(Fun T::* t) const\n      /* nolint */ volatile noexcept {\n    return t;\n  }\n  template <class F, class T>\n  constexpr F T::* operator()(F T::* t) const /* nolint */ volatile noexcept {\n    return t;\n  }\n};\n\n// A function type with no arguments means the user is trying to disambiguate\n// a member function pointer.\ntemplate <class R>\nstruct Sig<R()> : Sig<R() const> {\n  using Fun = R();\n  using Sig<R() const>::operator();\n\n  template <class T>\n  constexpr Fun T::* operator()(Fun T::* t) const noexcept {\n    return t;\n  }\n};\n\ntemplate <class R, class... As>\nstruct SigImpl : Sig<R(As...) const> {\n  using Fun = R(As...);\n  using Sig<R(As...) const>::operator();\n\n  template <class T>\n  constexpr Fun T::* operator()(Fun T::* t) const noexcept {\n    return t;\n  }\n  constexpr Fun* operator()(Fun* t) const noexcept { return t; }\n  template <class F>\n  constexpr F* operator()(F* t) const noexcept {\n    return t;\n  }\n};\n\n// The user could be trying to disambiguate either a member or a free function.\ntemplate <class R, class... As>\nstruct Sig<R(As...)> : SigImpl<R, As...> {};\n\n// This case is like the one above, except we want to add an overload that\n// handles the case of a free function where the first argument is more\n// const-qualified than the user explicitly specified.\ntemplate <class R, class A, class... As>\nstruct Sig<R(A&, As...)> : SigImpl<R, A&, As...> {\n  using CCFun = R(A const&, As...);\n  using SigImpl<R, A&, As...>::operator();\n\n  constexpr CCFun* operator()(CCFun* t) const /* nolint */ volatile noexcept {\n    return t;\n  }\n};\n\ntemplate <bool>\nstruct ModelsInterfaceFalse0_;\ntemplate <>\nstruct ModelsInterfaceFalse0_<false> {\n  template <typename... T>\n  using apply = std::bool_constant<(!require_sizeof<T> || ...)>;\n};\ntemplate <>\nstruct ModelsInterfaceFalse0_<true> {\n  template <typename...>\n  using apply = std::false_type;\n};\ntemplate <typename... T>\nusing ModelsInterfaceFalse_ = typename ModelsInterfaceFalse0_<(\n    std::is_function_v<remove_cvref_t<T>> || ...)>::template apply<T...>;\n\ntemplate <class T, class I, class = void>\nstruct ModelsInterface2_ : ModelsInterfaceFalse_<T, I> {};\n\ntemplate <class T, class I>\nstruct ModelsInterface2_<\n    T,\n    I,\n    void_t<\n        std::enable_if_t<\n            std::is_constructible<AddCvrefOf<std::decay_t<T>, I>, T>::value>,\n        MembersOf<std::decay_t<I>, std::decay_t<T>>>> : std::true_type {};\n\ntemplate <class T, class I, class = void>\nstruct ModelsInterface_ : ModelsInterfaceFalse_<T, I> {};\n\ntemplate <class T, class I>\nstruct ModelsInterface_<\n    T,\n    I,\n    std::enable_if_t<\n        Negation<std::is_base_of<PolyBase, std::decay_t<T>>>::value>>\n    : ModelsInterface2_<T, I> {};\n\ntemplate <class T, class I>\nstruct ModelsInterface : ModelsInterface_<T, I> {};\n\ntemplate <class I1, class I2>\nstruct ValueCompatible : std::is_base_of<I1, I2> {};\n\n// This prevents PolyRef's converting constructors and assignment operators\n// from being considered as copy constructors and assignment operators:\ntemplate <class I1>\nstruct ValueCompatible<I1, I1> : std::false_type {};\n\ntemplate <class I1, class I2, class I2Ref>\nstruct ReferenceCompatible : std::is_constructible<I1, I2Ref> {};\n\n// This prevents PolyRef's converting constructors and assignment operators\n// from being considered as copy constructors and assignment operators:\ntemplate <class I1, class I2Ref>\nstruct ReferenceCompatible<I1, I1, I2Ref> : std::false_type {};\n\n} // namespace detail\n/// \\endcond\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/RangeCommon.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/RangeCommon.h>\n\n#include <bitset>\n\n#include <folly/container/SparseByteSet.h>\n\nnamespace folly {\n\nnamespace detail {\n\nsize_t qfind_first_byte_of_bitset(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  std::bitset<256> s;\n  for (auto needle : needles) {\n    s[(uint8_t)needle] = true;\n  }\n  for (size_t index = 0; index < haystack.size(); ++index) {\n    if (s[(uint8_t)haystack[index]]) {\n      return index;\n    }\n  }\n  return std::string::npos;\n}\n\nsize_t qfind_first_byte_of_byteset(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  SparseByteSet s;\n  for (auto needle : needles) {\n    s.add(uint8_t(needle));\n  }\n  for (size_t index = 0; index < haystack.size(); ++index) {\n    if (s.contains(uint8_t(haystack[index]))) {\n      return index;\n    }\n  }\n  return std::string::npos;\n}\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/RangeCommon.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <cassert>\n#include <string>\n\n#include <folly/Likely.h>\n\nnamespace folly {\n\nnamespace detail {\n\n/***\n *  The qfind_first_byte_of_* functions are declared here, before Range.h, so\n *  they cannot take StringPiece values. But they're there to operate on\n *  StringPiece values. Dependency cycles: fun.\n *\n *  StringPieceLite is here to break that dependency cycle.\n */\nclass StringPieceLite {\n public:\n  StringPieceLite(const char* b, const char* e) : b_(b), e_(e) {}\n  template <typename Range>\n  /* implicit */ StringPieceLite(const Range& r)\n      : StringPieceLite(r.data(), r.data() + r.size()) {}\n  const char* data() const { return b_; }\n  const char* begin() const { return b_; }\n  const char* end() const { return e_; }\n  size_t size() const { return size_t(e_ - b_); }\n  bool empty() const { return size() == 0; }\n  const char& operator[](size_t i) const {\n    assert(size() > i);\n    return b_[i];\n  }\n  template <typename Range>\n  explicit operator Range() const {\n    return Range(begin(), end());\n  }\n\n private:\n  const char* b_;\n  const char* e_;\n};\n\ninline size_t qfind_first_byte_of_std(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  auto ret = std::find_first_of(\n      haystack.begin(),\n      haystack.end(),\n      needles.begin(),\n      needles.end(),\n      [](char a, char b) { return a == b; });\n  return ret == haystack.end() ? std::string::npos : ret - haystack.begin();\n}\n\nsize_t qfind_first_byte_of_bitset(\n    const StringPieceLite haystack, const StringPieceLite needles);\n\nsize_t qfind_first_byte_of_byteset(\n    const StringPieceLite haystack, const StringPieceLite needles);\n\ninline size_t qfind_first_byte_of_nosimd(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  if (FOLLY_UNLIKELY(needles.empty() || haystack.empty())) {\n    return std::string::npos;\n  }\n  // The thresholds below were empirically determined by benchmarking.\n  // This is not an exact science since it depends on the CPU, the size of\n  // needles, and the size of haystack.\n  if ((needles.size() >= 4 && haystack.size() <= 10) ||\n      (needles.size() >= 16 && haystack.size() <= 64) || needles.size() >= 32) {\n    return qfind_first_byte_of_byteset(haystack, needles);\n  }\n  return qfind_first_byte_of_std(haystack, needles);\n}\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/RangeSimd.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/RangeSimd.h>\n\n#include <folly/Portability.h>\n\n#include <folly/detail/RangeSse42.h>\n#include <folly/external/nvidia/detail/RangeSve2.h>\n\nnamespace folly {\nnamespace detail {\n\nsize_t qfind_first_byte_of_simd(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n#if FOLLY_ARM_FEATURE_SVE2\n  return qfind_first_byte_of_sve2(haystack, needles);\n#elif FOLLY_SSE_PREREQ(4, 2)\n  return qfind_first_byte_of_sse42(haystack, needles);\n#else\n  return qfind_first_byte_of_nosimd(haystack, needles);\n#endif\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/RangeSimd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n\n#include <folly/detail/RangeCommon.h>\n\nnamespace folly {\nnamespace detail {\n\nsize_t qfind_first_byte_of_simd(\n    const StringPieceLite haystack, const StringPieceLite needles);\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/RangeSse42.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/RangeSse42.h>\n\n#include <cassert>\n\n#include <folly/Portability.h>\n\n//  Essentially, two versions of this file: one with an SSE42 implementation\n//  and one with a fallback implementation. We determine which version to use by\n//  testing for the presence of the required headers.\n//\n//  TODO: Maybe this should be done by the build system....\n#if !FOLLY_SSE_PREREQ(4, 2)\nnamespace folly {\nnamespace detail {\nsize_t qfind_first_byte_of_sse42(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  return qfind_first_byte_of_nosimd(haystack, needles);\n}\n} // namespace detail\n} // namespace folly\n#else\n#include <emmintrin.h>\n#include <nmmintrin.h>\n#include <smmintrin.h>\n\n#include <cstdint>\n#include <limits>\n#include <string>\n\n#include <folly/Likely.h>\n#include <folly/detail/Sse.h>\n\nnamespace folly {\nnamespace detail {\n\n// It's okay if pages are bigger than this (as powers of two), but they should\n// not be smaller.\nstatic constexpr size_t kMinPageSize = 4096;\nstatic_assert(\n    kMinPageSize >= 16, \"kMinPageSize must be at least SSE register size\");\n\ntemplate <typename T>\nstatic inline uintptr_t page_for(T* addr) {\n  return reinterpret_cast<uintptr_t>(addr) / kMinPageSize;\n}\n\nstatic inline size_t nextAlignedIndex(const char* arr) {\n  auto firstPossible = reinterpret_cast<uintptr_t>(arr) + 1;\n  return 1 + // add 1 because the index starts at 'arr'\n      ((firstPossible + 15) & ~0xF) // round up to next multiple of 16\n      - firstPossible;\n}\n\n// helper method for case where needles.size() <= 16\nsize_t qfind_first_byte_of_needles16(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  assert(haystack.size() > 0u);\n  assert(needles.size() > 0u);\n  assert(needles.size() <= 16u);\n  if ((needles.size() <= 2 && haystack.size() >= 256) ||\n      // must bail if we can't even SSE-load a single segment of haystack\n      (haystack.size() < 16 &&\n       page_for(haystack.end() - 1) != page_for(haystack.data() + 15)) ||\n      // can't load needles into SSE register if it could cross page boundary\n      page_for(needles.end() - 1) != page_for(needles.data() + 15)) {\n    return detail::qfind_first_byte_of_nosimd(haystack, needles);\n  }\n\n  auto arr2 = _mm_loadu_si128_unchecked(\n      reinterpret_cast<const __m128i*>(needles.data()));\n  // do an unaligned load for first block of haystack\n  auto arr1 = _mm_loadu_si128_unchecked(\n      reinterpret_cast<const __m128i*>(haystack.data()));\n  auto index =\n      _mm_cmpestri(arr2, int(needles.size()), arr1, int(haystack.size()), 0);\n  if (index < 16) {\n    return size_t(index);\n  }\n\n  // Now, we can do aligned loads hereafter...\n  size_t i = nextAlignedIndex(haystack.data());\n  for (; i < haystack.size(); i += 16) {\n    arr1 = _mm_load_si128_unchecked(\n        reinterpret_cast<const __m128i*>(haystack.data() + i));\n    index = _mm_cmpestri(\n        arr2, int(needles.size()), arr1, int(haystack.size() - i), 0);\n    if (index < 16) {\n      return i + index;\n    }\n  }\n  return std::string::npos;\n}\n\n// Scans a 16-byte block of haystack (starting at blockStartIdx) to find first\n// needle. If HAYSTACK_ALIGNED, then haystack must be 16byte aligned.\n// If !HAYSTACK_ALIGNED, then caller must ensure that it is safe to load the\n// block.\ntemplate <bool HAYSTACK_ALIGNED>\nsize_t scanHaystackBlock(\n    const StringPieceLite haystack,\n    const StringPieceLite needles,\n    uint64_t blockStartIdx) {\n  assert(needles.size() > 16u); // should handled by *needles16() method\n  assert(\n      blockStartIdx + 16 <= haystack.size() ||\n      (page_for(haystack.data() + blockStartIdx) ==\n       page_for(haystack.data() + blockStartIdx + 15)));\n\n  __m128i arr1;\n  if (HAYSTACK_ALIGNED) {\n    arr1 = _mm_load_si128_unchecked(\n        reinterpret_cast<const __m128i*>(haystack.data() + blockStartIdx));\n  } else {\n    arr1 = _mm_loadu_si128_unchecked(\n        reinterpret_cast<const __m128i*>(haystack.data() + blockStartIdx));\n  }\n\n  // This load is safe because needles.size() >= 16\n  auto arr2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(needles.data()));\n  auto b =\n      _mm_cmpestri(arr2, 16, arr1, int(haystack.size() - blockStartIdx), 0);\n\n  size_t j = nextAlignedIndex(needles.data());\n  for (; j < needles.size(); j += 16) {\n    arr2 = _mm_load_si128_unchecked(\n        reinterpret_cast<const __m128i*>(needles.data() + j));\n\n    auto index = _mm_cmpestri(\n        arr2,\n        int(needles.size() - j),\n        arr1,\n        int(haystack.size() - blockStartIdx),\n        0);\n    b = std::min(index, b);\n  }\n\n  if (b < 16) {\n    return blockStartIdx + b;\n  }\n  return std::string::npos;\n}\n\nsize_t qfind_first_byte_of_sse42(\n    const StringPieceLite haystack, const StringPieceLite needles);\n\nsize_t qfind_first_byte_of_sse42(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  if (FOLLY_UNLIKELY(needles.empty() || haystack.empty())) {\n    return std::string::npos;\n  } else if (needles.size() <= 16) {\n    // we can save some unnecessary load instructions by optimizing for\n    // the common case of needles.size() <= 16\n    return qfind_first_byte_of_needles16(haystack, needles);\n  }\n\n  if (haystack.size() < 16 &&\n      page_for(haystack.end() - 1) != page_for(haystack.data() + 16)) {\n    // We can't safely SSE-load haystack. Use a different approach.\n    if (haystack.size() <= 2) {\n      return qfind_first_byte_of_std(haystack, needles);\n    }\n    return qfind_first_byte_of_byteset(haystack, needles);\n  }\n\n  auto ret = scanHaystackBlock<false>(haystack, needles, 0);\n  if (ret != std::string::npos) {\n    return ret;\n  }\n\n  size_t i = nextAlignedIndex(haystack.data());\n  for (; i < haystack.size(); i += 16) {\n    ret = scanHaystackBlock<true>(haystack, needles, i);\n    if (ret != std::string::npos) {\n      return ret;\n    }\n  }\n\n  return std::string::npos;\n}\n} // namespace detail\n} // namespace folly\n#endif\n"
  },
  {
    "path": "folly/detail/RangeSse42.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n\n#include <folly/detail/RangeCommon.h>\n\nnamespace folly {\n\nnamespace detail {\n\nsize_t qfind_first_byte_of_sse42(\n    const StringPieceLite haystack, const StringPieceLite needles);\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SimpleSimdStringUtils.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/SimpleSimdStringUtils.h>\n\n#include <folly/algorithm/simd/detail/SimdPlatform.h>\n#include <folly/detail/SimpleSimdStringUtilsImpl.h>\n\nnamespace folly {\nnamespace detail {\n\nbool simdHasSpaceOrCntrlSymbols(folly::StringPiece s) {\n  return SimpleSimdStringUtilsImpl<\n      simd::detail::SimdPlatform<std::uint8_t>>::hasSpaceOrCntrlSymbols(s);\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SimpleSimdStringUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Range.h>\n\nnamespace folly {\nnamespace detail {\n\nbool simdHasSpaceOrCntrlSymbols(folly::StringPiece s);\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SimpleSimdStringUtilsImpl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Range.h>\n#include <folly/algorithm/simd/detail/SimdAnyOf.h>\n#include <folly/algorithm/simd/detail/SimdPlatform.h>\n\nnamespace folly {\nnamespace detail {\n\n// Implementations of SimpleSimdStringUtils\n\ntemplate <typename Platform>\nstruct SimpleSimdStringUtilsImpl {\n  using reg_t = typename Platform::reg_t;\n  using logical_t = typename Platform::logical_t;\n\n  struct HasSpaceOrCntrlSymbolsLambda {\n    FOLLY_ALWAYS_INLINE\n    logical_t operator()(reg_t reg) {\n      // This happens to be equivalent to std::isspace(c) || std::iscntrl(c)\n      return Platform::logical_or(\n          Platform::less_equal(reg, 0x20), Platform::equal(reg, 0x7F));\n    }\n  };\n\n  FOLLY_ALWAYS_INLINE\n  static bool hasSpaceOrCntrlSymbols(folly::StringPiece s) {\n    return simd::detail::simdAnyOf<Platform, /*unrolling*/ 4>(\n        reinterpret_cast<const std::uint8_t*>(s.data()),\n        reinterpret_cast<const std::uint8_t*>(s.data() + s.size()),\n        HasSpaceOrCntrlSymbolsLambda{});\n  }\n};\n\ntemplate <>\nstruct SimpleSimdStringUtilsImpl<void> {\n  FOLLY_ALWAYS_INLINE\n  static bool hasSpaceOrCntrlSymbols(folly::StringPiece s) {\n    return std::any_of(s.begin(), s.end(), [](char c) {\n      std::uint32_t uc = static_cast<std::uint8_t>(c);\n      return uc <= 0x20 || c == 0x7F;\n    });\n  }\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Singleton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/Traits.h>\n\nnamespace folly {\nnamespace detail {\n\nstruct FOLLY_EXPORT DefaultTag {};\n\ntemplate <typename T>\nstruct DefaultMake {\n  T operator()() const { return T(); }\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SlowFingerprint.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Fingerprint.h>\n#include <folly/Range.h>\n#include <folly/detail/FingerprintPolynomial.h>\n\nnamespace folly {\nnamespace detail {\n\n/**\n * Slow, one-bit-at-a-time implementation of the Rabin fingerprint.\n *\n * This is useful as a reference implementation to test the Broder optimization\n * for correctness in the unittest; it's probably too slow for any real use.\n */\ntemplate <int BITS>\nclass SlowFingerprint {\n public:\n  SlowFingerprint() : poly_(FingerprintTable<BITS>::poly) {\n    // Use the same starting value as Fingerprint, (1 << (BITS-1))\n    fp_.addXk(BITS - 1);\n  }\n\n  SlowFingerprint& update8(uint8_t v) {\n    updateLSB(v, 8);\n    return *this;\n  }\n\n  SlowFingerprint& update32(uint32_t v) {\n    updateLSB(v, 32);\n    return *this;\n  }\n\n  SlowFingerprint& update64(uint64_t v) {\n    updateLSB(v, 64);\n    return *this;\n  }\n\n  SlowFingerprint& update(const folly::StringPiece str) {\n    const char* p = str.start();\n    for (int i = str.size(); i != 0; p++, i--) {\n      update8(static_cast<uint8_t>(*p));\n    }\n    return *this;\n  }\n\n  void write(uint64_t* out) const {\n    for (int i = 0; i < fp_.size(); ++i) {\n      out[i] = fp_.get(i);\n    }\n  }\n\n private:\n  void updateBit(bool bit) {\n    fp_.mulXmod(poly_);\n    if (bit) {\n      fp_.addXk(0);\n    }\n  }\n\n  void updateLSB(uint64_t val, int bits) {\n    val <<= (64 - bits);\n    for (; bits != 0; --bits) {\n      updateBit(val & (1ULL << 63));\n      val <<= 1;\n    }\n  }\n\n  const FingerprintPolynomial<BITS - 1> poly_;\n  FingerprintPolynomial<BITS - 1> fp_;\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SocketFastOpen.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/SocketFastOpen.h>\n\n#include <cerrno>\n#include <cstdio>\n#include <fstream>\n\n#include <folly/portability/Sockets.h>\n\nnamespace folly {\nnamespace detail {\n\n#if FOLLY_ALLOW_TFO && defined(__linux__)\n\n// Sometimes these flags are not present in the headers,\n// so define them if not present.\n#if !defined(MSG_FASTOPEN)\n#define MSG_FASTOPEN 0x20000000\n#endif\n\n#if !defined(TCP_FASTOPEN)\n#define TCP_FASTOPEN 23\n#endif\n\n#if !defined(TCPI_OPT_SYN_DATA)\n#define TCPI_OPT_SYN_DATA 32\n#endif\n\nssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags) {\n  flags |= MSG_FASTOPEN;\n  return netops::sendmsg(sockfd, msg, flags);\n}\n\nint tfo_enable(NetworkSocket sockfd, size_t max_queue_size) {\n  return netops::setsockopt(\n      sockfd, SOL_TCP, TCP_FASTOPEN, &max_queue_size, sizeof(max_queue_size));\n}\n\nbool tfo_succeeded(NetworkSocket sockfd) {\n  // Call getsockopt to check if TFO was used.\n  struct tcp_info info;\n  socklen_t info_len = sizeof(info);\n  errno = 0;\n  if (netops::getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, &info_len) !=\n      0) {\n    // errno is set from getsockopt\n    return false;\n  }\n  return info.tcpi_options & TCPI_OPT_SYN_DATA;\n}\n\nPlatformTFOSettings tfo_platform_availability() {\n  static PlatformTFOSettings TFOSettings = [] {\n    size_t fastOpen = 0;\n    try {\n      std::ifstream ifs(\"/proc/sys/net/ipv4/tcp_fastopen\");\n      if (ifs.is_open()) {\n        ifs >> fastOpen;\n      }\n    } catch (std::exception&) {\n    }\n\n    PlatformTFOSettings settings{};\n    if ((fastOpen & 1) == 0) {\n      settings.client = TFOAvailability::None;\n    } else if ((fastOpen & 4) == 0) {\n      settings.client = TFOAvailability::WithCookies;\n    } else {\n      settings.client = TFOAvailability::Unconditional;\n    }\n\n    if ((fastOpen & 2) == 0) {\n      settings.server = TFOAvailability::None;\n    } else if ((fastOpen & 0x200) == 0) {\n      settings.server = TFOAvailability::WithCookies;\n    } else {\n      settings.server = TFOAvailability::Unconditional;\n    }\n\n    return settings;\n  }();\n\n  return TFOSettings;\n}\n\n#elif FOLLY_ALLOW_TFO && defined(__APPLE__)\n\nssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags) {\n  sa_endpoints_t endpoints;\n  endpoints.sae_srcif = 0;\n  endpoints.sae_srcaddr = nullptr;\n  endpoints.sae_srcaddrlen = 0;\n  endpoints.sae_dstaddr = (struct sockaddr*)msg->msg_name;\n  endpoints.sae_dstaddrlen = msg->msg_namelen;\n  int ret = connectx(\n      sockfd.toFd(),\n      &endpoints,\n      SAE_ASSOCID_ANY,\n      CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,\n      nullptr,\n      0,\n      nullptr,\n      nullptr);\n\n  if (ret != 0) {\n    return ret;\n  }\n  ret = netops::sendmsg(sockfd, msg, flags);\n  return ret;\n}\n\nint tfo_enable(NetworkSocket sockfd, size_t max_queue_size) {\n  return netops::setsockopt(\n      sockfd,\n      IPPROTO_TCP,\n      TCP_FASTOPEN,\n      &max_queue_size,\n      sizeof(max_queue_size));\n}\n\nbool tfo_succeeded(NetworkSocket /* sockfd */) {\n  errno = EOPNOTSUPP;\n  return false;\n}\n\nPlatformTFOSettings tfo_platform_availability() {\n  static PlatformTFOSettings TFOSettings{\n      TFOAvailability::None, TFOAvailability::None};\n  return TFOSettings;\n}\n\n#else\n\nssize_t tfo_sendmsg(\n    NetworkSocket /* sockfd */,\n    const struct msghdr* /* msg */,\n    int /* flags */) {\n  errno = EOPNOTSUPP;\n  return -1;\n}\n\nint tfo_enable(NetworkSocket /* sockfd */, size_t /* max_queue_size */) {\n  errno = ENOPROTOOPT;\n  return -1;\n}\n\nbool tfo_succeeded(NetworkSocket /* sockfd */) {\n  errno = EOPNOTSUPP;\n  return false;\n}\n\nPlatformTFOSettings tfo_platform_availability() {\n  static PlatformTFOSettings TFOSettings{\n      TFOAvailability::None, TFOAvailability::None};\n  return TFOSettings;\n}\n\n#endif\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SocketFastOpen.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <folly/net/NetworkSocket.h>\n#include <folly/portability/Sockets.h>\n\n#if !defined(FOLLY_ALLOW_TFO)\n#if defined(__linux__) || defined(__APPLE__)\n// only allow for linux right now\n#define FOLLY_ALLOW_TFO 1\n#endif\n#endif\n\nnamespace folly {\nnamespace detail {\n\n/**\n * TFOAvailability describes support for TCP Fast Open (TFO).\n */\nenum class TFOAvailability {\n  /**\n   * TFOAvailability::None indicates that the current environment\n   * does not support TFO (or we could not detect support via probing).\n   */\n  None,\n  /**\n   * TFOAvailability::WithCookies indicates that TFO is supported by the\n   * platform. However, in order for TFO data to be used or for TFO to\n   * be attempted, a prior connection to the same peer must have successfully\n   * taken place so that the peer could issue a cookie.\n   */\n  WithCookies,\n  /**\n   * TFOAvailability::Unconditional indicates that TFO will always be\n   * unconditionally sent or accepted, regardless of whether or not prior\n   * connections have taken place.\n   */\n  Unconditional\n};\n\n/**\n * PlatformTFOSettings describes TFO support for client connections and\n * server connections.\n */\nstruct PlatformTFOSettings {\n  TFOAvailability client{TFOAvailability::None};\n  TFOAvailability server{TFOAvailability::None};\n};\n\n/**\n * tfo_sendto has the same semantics as sendmsg, but is used to\n * send with TFO data.\n */\nssize_t tfo_sendmsg(NetworkSocket sockfd, const struct msghdr* msg, int flags);\n\n/**\n * Enable TFO on a listening socket.\n */\nint tfo_enable(NetworkSocket sockfd, size_t max_queue_size);\n\n/**\n * Check if TFO succeeded in being used.\n */\nbool tfo_succeeded(NetworkSocket sockfd);\n\n/**\n * Check if TFO is supported by the kernel.\n */\nPlatformTFOSettings tfo_platform_availability();\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SplitStringSimd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Range.h>\n#include <folly/detail/SplitStringSimdImpl.h>\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename Container>\nvoid simdSplitByChar(\n    char sep, folly::StringPiece what, Container& res, bool ignoreEmpty) {\n  using Platform = simd::detail::SimdPlatform<std::uint8_t>;\n  if (ignoreEmpty) {\n    PlatformSimdSplitByChar<\n        Platform,\n        /*ignoreEmpty*/ true>{}(sep, what, res);\n  } else {\n    PlatformSimdSplitByChar<\n        Platform,\n        /*ignoreEmpty*/ false>{}(sep, what, res);\n  }\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/SplitStringSimdImpl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/algorithm/simd/Ignore.h>\n#include <folly/algorithm/simd/Movemask.h>\n#include <folly/algorithm/simd/detail/SimdForEach.h>\n#include <folly/algorithm/simd/detail/SimdPlatform.h>\n#include <folly/lang/Bits.h>\n\n#if FOLLY_X64\n#include <immintrin.h>\n#endif\n\n#if FOLLY_AARCH64\n#include <arm_neon.h>\n#endif\n\nnamespace folly {\nnamespace detail {\n\ntemplate <bool ignoreEmpty, typename Container>\nFOLLY_NOINLINE void splitByCharScalar(\n    char sep, folly::StringPiece what, Container& res) {\n  const char* prev = what.data();\n  const char* f = prev;\n  const char* l = what.data() + what.size();\n\n  auto emplaceBack = [&](const char* sf, const char* sl) mutable {\n    if (ignoreEmpty && sf == sl) {\n      return;\n    }\n    res.emplace_back(sf, sl - sf);\n  };\n\n  while (f != l) {\n    const char* next = f + 1;\n    if (*f == sep) {\n      if (!ignoreEmpty || (prev != f)) {\n        emplaceBack(prev, f);\n      }\n      prev = next;\n    }\n    f = next;\n  }\n  emplaceBack(prev, f);\n}\n\ntemplate <typename Platform, bool ignoreEmpty>\nstruct PlatformSimdSplitByChar {\n  using reg_t = typename Platform::reg_t;\n\n  template <typename Container>\n  FOLLY_ALWAYS_INLINE static void emplaceBack(\n      Container& res, const std::uint8_t* f, const std::uint8_t* l) {\n    if (ignoreEmpty && f == l) {\n      return;\n    }\n    res.emplace_back(reinterpret_cast<const char*>(f), l - f);\n  }\n\n  template <typename Uint, typename BitsPerElement, typename Container>\n  FOLLY_ALWAYS_INLINE static void outputStringsFoMmask(\n      std::pair<Uint, BitsPerElement> mmask,\n      const std::uint8_t* pos,\n      const std::uint8_t*& prev,\n      Container& res) {\n    Uint mmaskBits = mmask.first;\n    while (mmaskBits) {\n      auto counted = folly::findFirstSet(mmaskBits) - 1;\n      mmaskBits >>= counted;\n      mmaskBits >>= BitsPerElement{};\n      auto firstSet = counted / BitsPerElement{};\n\n      const std::uint8_t* split = pos + firstSet;\n      pos = split + 1;\n      emplaceBack(res, prev, split);\n      prev = pos;\n    }\n  }\n\n  template <typename Container>\n  struct ForEachDelegate {\n    std::uint8_t sep;\n    const std::uint8_t*& prev;\n    Container& res;\n\n    template <typename Ignore, typename UnrollIndex>\n    FOLLY_ALWAYS_INLINE bool step(\n        const std::uint8_t* ptr, Ignore ignore, UnrollIndex) const {\n      reg_t loaded = Platform::loada(ptr, ignore);\n      auto mmask =\n          simd::movemask<std::uint8_t>(Platform::equal(loaded, sep), ignore);\n      outputStringsFoMmask(mmask, ptr, prev, res);\n      return false;\n    }\n  };\n\n  template <typename Container>\n  FOLLY_NOINLINE static void simdSplitByChar(\n      char sep, folly::StringPiece what, Container& res) {\n    const std::uint8_t* what_f =\n        reinterpret_cast<const std::uint8_t*>(what.data());\n    const std::uint8_t* what_l = what_f + what.size();\n\n    const std::uint8_t* prev = what_f;\n\n    ForEachDelegate<Container> delegate{\n        static_cast<std::uint8_t>(sep), prev, res};\n    simd::detail::simdForEachAligning</*unrolling*/ 1>(\n        Platform::kCardinal, what_f, what_l, delegate);\n    emplaceBack(res, prev, what_l);\n  }\n\n  template <typename Container>\n  FOLLY_ALWAYS_INLINE void operator()(\n      char sep, folly::StringPiece what, Container& res) const {\n    return simdSplitByChar(sep, what, res);\n  }\n};\n\ntemplate <bool ignoreEmpty>\nstruct PlatformSimdSplitByChar<void, ignoreEmpty> {\n  template <typename Container>\n  FOLLY_ALWAYS_INLINE void operator()(\n      char sep, folly::StringPiece what, Container& res) const {\n    return splitByCharScalar<ignoreEmpty>(sep, what, res);\n  }\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Sse.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/Sse.h>\n\n#include <folly/CppAttributes.h>\n\nnamespace folly {\nnamespace detail {\n\n#if FOLLY_SSE_PREREQ(2, 0)\n\n[[FOLLY_ATTR_GNU_FLATTEN]] FOLLY_DISABLE_SANITIZERS __m128i\n_mm_loadu_si128_nosan(__m128i const* const p) {\n  return _mm_loadu_si128(p);\n}\n\n[[FOLLY_ATTR_GNU_FLATTEN]] FOLLY_DISABLE_SANITIZERS __m128i\n_mm_load_si128_nosan(__m128i const* const p) {\n  return _mm_load_si128(p);\n}\n\n#endif\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/Sse.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n\n#if FOLLY_SSE_PREREQ(2, 0)\n\n#include <emmintrin.h>\n\n#endif\n\nnamespace folly {\nnamespace detail {\n\n#if FOLLY_SSE_PREREQ(2, 0)\n\n__m128i _mm_loadu_si128_nosan(__m128i const* const p);\nFOLLY_ERASE __m128i _mm_loadu_si128_unchecked(__m128i const* const p) {\n  return kIsSanitize ? _mm_loadu_si128_nosan(p) : _mm_loadu_si128(p);\n}\n\n__m128i _mm_load_si128_nosan(__m128i const* const p);\nFOLLY_ERASE __m128i _mm_load_si128_unchecked(__m128i const* const p) {\n  return kIsSanitize ? _mm_load_si128_nosan(p) : _mm_load_si128(p);\n}\n\n#endif\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/StaticSingletonManager.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/StaticSingletonManager.h>\n\n#include <mutex>\n#include <typeindex>\n#include <unordered_map>\n\n#include <folly/memory/ReentrantAllocator.h>\n\nnamespace folly {\nnamespace detail {\n\nnamespace {\n\nclass StaticSingletonManagerWithRttiImpl {\n public:\n  using Self = StaticSingletonManagerWithRttiImpl;\n  using Make = void*();\n\n  static Self& instance() {\n    // This Leaky Meyers Singleton must always live in the .cpp file.\n    static Indestructible<StaticSingletonManagerWithRttiImpl> instance;\n    return instance;\n  }\n\n  template <typename Arg>\n  static void* get_existing(Arg& arg) {\n    auto const* const entry = instance().get_existing_entry(*arg.key);\n    auto const ptr = entry ? entry->get_existing() : nullptr;\n    if (ptr) {\n      arg.cache.store(ptr, std::memory_order_release);\n    }\n    return ptr;\n  }\n\n  template <typename Arg>\n  static void* create(Arg& arg) {\n    auto& entry = instance().create_entry(*arg.key);\n    auto const ptr = entry.create(*arg.make, *arg.debug);\n    arg.cache.store(ptr, std::memory_order_release);\n    return ptr;\n  }\n\n private:\n  struct Entry {\n    std::atomic<void*> ptr{};\n    std::mutex mutex;\n\n    void* get_existing() const { return ptr.load(std::memory_order_acquire); }\n\n    void* create(Make& make, void*& debug) {\n      if (auto const v = ptr.load(std::memory_order_acquire)) {\n        return v;\n      }\n      std::unique_lock lock(mutex);\n      if (auto const v = ptr.load(std::memory_order_acquire)) {\n        return v;\n      }\n      auto const v = make();\n      ptr.store(v, std::memory_order_release);\n      debug = ptr;\n      return v;\n    }\n  };\n\n  Entry* get_existing_entry(std::type_info const& key) {\n    std::unique_lock lock(mutex_);\n    auto const it = map_.find(key);\n    return it == map_.end() ? nullptr : &it->second;\n  }\n\n  Entry& create_entry(std::type_info const& key) {\n    std::unique_lock lock(mutex_);\n    return map_[key];\n  }\n\n  // Using reentrant_allocator to permit new/delete hooks to use this class.\n  // std::map would be preferred over std::unordered_map to reduce number of\n  // mmap regions, since reentrant_allocator creates mmap regions to avoid\n  // malloc/free. However, std::map surfaced address sanitizer issues in\n  // std::type_info::before when defining\n  // _LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION=2 on Mac builds.\n  using map_value_t = std::pair<std::type_index const, Entry>;\n  using map_alloc_t = reentrant_allocator<map_value_t>;\n  using map_t = std::unordered_map<\n      std::type_index,\n      Entry,\n      std::hash<std::type_index>,\n      std::equal_to<std::type_index>,\n      map_alloc_t>;\n\n  map_t map_{map_alloc_t{reentrant_allocator_options{}}};\n  std::mutex mutex_;\n};\n\n} // namespace\n\nvoid* StaticSingletonManagerWithRtti::get_existing_(Arg& arg) noexcept {\n  return StaticSingletonManagerWithRttiImpl::get_existing(arg);\n}\n\ntemplate <bool Noexcept>\nvoid* StaticSingletonManagerWithRtti::create_(Arg& arg) noexcept(Noexcept) {\n  return StaticSingletonManagerWithRttiImpl::create(arg);\n}\n\ntemplate void* StaticSingletonManagerWithRtti::create_<false>(Arg& arg);\ntemplate void* StaticSingletonManagerWithRtti::create_<true>(Arg& arg) noexcept;\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/StaticSingletonManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <typeinfo>\n\n#include <folly/CPortability.h>\n#include <folly/Indestructible.h>\n#include <folly/Likely.h>\n#include <folly/Utility.h>\n#include <folly/detail/Singleton.h>\n#include <folly/lang/Thunk.h>\n#include <folly/lang/TypeInfo.h>\n\nnamespace folly {\nnamespace detail {\n\n// Does not support dynamic loading but works without rtti.\nclass StaticSingletonManagerSansRtti {\n private:\n  using Self = StaticSingletonManagerSansRtti;\n  using Cache = std::atomic<void*>;\n  using Instance = void*(bool);\n  struct Arg {\n    Cache cache{}; // should be first field\n    Instance* instance;\n\n    template <typename T, typename Tag>\n    /* implicit */ constexpr Arg(tag_t<T, Tag>) noexcept\n        : instance{instance_<T, Tag>} {}\n  };\n\n  template <typename T, typename Tag>\n  static void* debug; // visible to debugger\n\n public:\n  template <bool Noexcept>\n  struct ArgCreate : private Arg {\n    friend class StaticSingletonManagerSansRtti;\n\n    template <typename T, typename Tag>\n    /* implicit */ constexpr ArgCreate(tag_t<T, Tag> t) noexcept : Arg{t} {\n      static_assert(Noexcept == noexcept(T()), \"mismatched noexcept\");\n    }\n  };\n\n  /// get_existing_cached\n  ///\n  /// Returns a pointer to the global if it has already been created and if it\n  /// is also already cached in the global arg.\n  template <typename T, typename Tag>\n  FOLLY_ERASE static T* get_existing_cached() {\n    return get_existing_cached<T>(global<T, Tag>());\n  }\n\n  /// get_existing\n  ///\n  /// Returns a pointer to the global if it has already been created. Caches it\n  /// in the global arg.\n  template <typename T, typename Tag>\n  FOLLY_ERASE static T* get_existing() {\n    return get_existing<T>(global<T, Tag>());\n  }\n\n  /// create\n  ///\n  /// Returns a pointer to the global if it has already been created, or creates\n  /// it. Caches it in the global arg.\n  template <typename T, typename Tag>\n  FOLLY_ERASE static T& create() {\n    return create<T>(global<T, Tag>());\n  }\n\n  /// get_existing_cached\n  ///\n  /// Returns a pointer to the global if it has already been created and if it\n  /// is also already cached in the given arg.\n  template <typename T, typename..., bool Noexcept = noexcept(T())>\n  FOLLY_ERASE static T* get_existing_cached(ArgCreate<Noexcept>& arg) {\n    auto const v = arg.cache.load(std::memory_order_acquire);\n    return static_cast<T*>(v);\n  }\n\n  /// get_existing\n  ///\n  /// Returns a pointer to the global if it has already been created. Caches it\n  /// in the given arg.\n  template <typename T, typename..., bool Noexcept = noexcept(T())>\n  FOLLY_ERASE static T* get_existing(ArgCreate<Noexcept>& arg) {\n    auto const v = arg.cache.load(std::memory_order_acquire);\n    auto const p = FOLLY_LIKELY(!!v) ? v : get_existing_(arg);\n    return static_cast<T*>(p);\n  }\n\n  /// create\n  ///\n  /// Returns a pointer to the global if it has already been created, or creates\n  /// it. Caches it in the given arg.\n  template <typename T, typename..., bool Noexcept = noexcept(T())>\n  FOLLY_ERASE static T& create(ArgCreate<Noexcept>& arg) {\n    auto const v = arg.cache.load(std::memory_order_acquire);\n    auto const p = FOLLY_LIKELY(!!v) ? v : create_<noexcept(T())>(arg);\n    return *static_cast<T*>(p);\n  }\n\n private:\n  template <typename T, typename Tag, typename R = ArgCreate<noexcept(T())>>\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static FOLLY_CXX23_CONSTEXPR R& global() {\n    static ArgCreate<noexcept(T())> arg{tag<T, Tag>};\n    return arg;\n  }\n\n  template <typename T, typename Tag>\n  FOLLY_EXPORT static void* instance_(bool create) {\n    static_assert(std::atomic<void*>::is_always_lock_free);\n    // the two static variables must be in the same function in order for them\n    // to be exported together and therefore to be structurally in sync\n    static std::atomic<void*> guard{nullptr};\n    if (!create) {\n      return guard.load(std::memory_order_acquire);\n    } else {\n      struct Holder {\n        T instance;\n        Holder() {\n          auto const ptr = &reinterpret_cast<unsigned char&>(instance);\n          guard.store(ptr, std::memory_order_release);\n          debug<T, Tag> = ptr;\n        }\n      };\n      static Indestructible<Holder> holder{};\n      return &holder->instance;\n    }\n  }\n\n  FOLLY_NOINLINE static void* get_existing_(Arg& arg) noexcept {\n    auto const v = arg.instance(false);\n    if (v) {\n      arg.cache.store(v, std::memory_order_release);\n    }\n    return v;\n  }\n\n  template <bool Noexcept>\n  FOLLY_NOINLINE static void* create_(Arg& arg) noexcept(Noexcept) {\n    auto const v = arg.instance(true);\n    arg.cache.store(v, std::memory_order_release);\n    return v;\n  }\n};\n\ntemplate <typename T, typename Tag>\nvoid* StaticSingletonManagerSansRtti::debug;\n\n// This internal-use-only class is used to create all leaked Meyers singletons.\n// It guarantees that only one instance of every such singleton will ever be\n// created, even when requested from different compilation units linked\n// dynamically.\n//\n// Supports dynamic loading but requires rtti.\nclass StaticSingletonManagerWithRtti {\n private:\n  using Self = StaticSingletonManagerWithRtti;\n  using Key = std::type_info;\n  using Make = void*();\n  using Cache = std::atomic<void*>;\n  template <typename T, typename Tag>\n  struct FOLLY_EXPORT Src {};\n  struct Arg {\n    Cache cache{}; // should be first field\n    Key const* key;\n    Make* make;\n    void** debug;\n\n    // gcc and clang behave poorly if typeid is hidden behind a non-constexpr\n    // function, but typeid is not constexpr under msvc\n    template <typename T, typename Tag>\n    /* implicit */ constexpr Arg(tag_t<T, Tag>) noexcept\n        : key{FOLLY_TYPE_INFO_OF(Src<T, Tag>)},\n          make{thunk::make<T>},\n          debug{&Self::debug<T, Tag>} {}\n  };\n\n  template <typename T, typename Tag>\n  static void* debug; // visible to debugger\n\n public:\n  template <bool Noexcept>\n  struct ArgCreate : private Arg {\n    friend class StaticSingletonManagerWithRtti;\n\n    template <typename T, typename Tag>\n    /* implicit */ constexpr ArgCreate(tag_t<T, Tag> t) noexcept : Arg{t} {\n      static_assert(Noexcept == noexcept(T()), \"mismatched noexcept\");\n    }\n  };\n\n  /// get_existing_cached\n  ///\n  /// Returns a pointer to the global if it has already been created and if it\n  /// is also already cached in the global arg.\n  template <typename T, typename Tag>\n  FOLLY_ERASE static T* get_existing_cached() {\n    return get_existing_cached<T>(global<T, Tag>());\n  }\n\n  /// get_existing\n  ///\n  /// Returns a pointer to the global if it has already been created. Caches it\n  /// in the global arg.\n  template <typename T, typename Tag>\n  FOLLY_ERASE static T* get_existing() {\n    return get_existing<T>(global<T, Tag>());\n  }\n\n  /// create\n  ///\n  /// Returns a pointer to the global if it has already been created, or creates\n  /// it. Caches it in the global arg.\n  template <typename T, typename Tag>\n  FOLLY_ERASE static T& create() {\n    return create<T>(global<T, Tag>());\n  }\n\n  /// get_existing_cached\n  ///\n  /// Returns a pointer to the global if it has already been created and if it\n  /// is also already cached in the given arg.\n  template <typename T, typename..., bool Noexcept = noexcept(T())>\n  FOLLY_ERASE static T* get_existing_cached(ArgCreate<Noexcept>& arg) {\n    auto const v = arg.cache.load(std::memory_order_acquire);\n    return static_cast<T*>(v);\n  }\n\n  /// get_existing\n  ///\n  /// Returns a pointer to the global if it has already been created. Caches it\n  /// in the given arg.\n  template <typename T, typename..., bool Noexcept = noexcept(T())>\n  FOLLY_ERASE static T* get_existing(ArgCreate<Noexcept>& arg) {\n    auto const v = arg.cache.load(std::memory_order_acquire);\n    auto const p = FOLLY_LIKELY(!!v) ? v : get_existing_(arg);\n    return static_cast<T*>(p);\n  }\n\n  /// create\n  ///\n  /// Returns a pointer to the global if it has already been created, or creates\n  /// it. Caches it in the given arg.\n  template <typename T, typename..., bool Noexcept = noexcept(T())>\n  FOLLY_ERASE static T& create(ArgCreate<Noexcept>& arg) {\n    auto const v = arg.cache.load(std::memory_order_acquire);\n    auto const p = FOLLY_LIKELY(!!v) ? v : create_<noexcept(T())>(arg);\n    return *static_cast<T*>(p);\n  }\n\n private:\n  template <typename T, typename Tag, typename R = ArgCreate<noexcept(T())>>\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static FOLLY_CXX23_CONSTEXPR R& global() {\n    static ArgCreate<noexcept(T())> arg{tag<T, Tag>};\n    return arg;\n  }\n\n  FOLLY_NOINLINE static void* get_existing_(Arg& arg) noexcept;\n\n  template <bool Noexcept>\n  FOLLY_NOINLINE static void* create_(Arg& arg) noexcept(Noexcept);\n};\n\ntemplate <typename T, typename Tag>\nvoid* StaticSingletonManagerWithRtti::debug;\n\nusing StaticSingletonManager = std::conditional_t<\n    kHasRtti,\n    StaticSingletonManagerWithRtti,\n    StaticSingletonManagerSansRtti>;\n\ntemplate <typename T, typename Tag>\nFOLLY_ERASE T& createGlobal() {\n  return StaticSingletonManager::create<T, Tag>();\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/ThreadLocalDetail.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/ThreadLocalDetail.h>\n\n#include <algorithm>\n#include <mutex>\n#include <random>\n\n#include <folly/ConstexprMath.h>\n#include <folly/Utility.h>\n#include <folly/detail/thread_local_globals.h>\n#include <folly/hash/MurmurHash.h>\n#include <folly/lang/Hint.h>\n#include <folly/memory/SanitizeLeak.h>\n#include <folly/random/hash.h>\n#include <folly/synchronization/CallOnce.h>\n\nconstexpr auto kSmallGrowthFactor = 1.1;\nconstexpr auto kBigGrowthFactor = 1.7;\n\nnamespace folly {\nnamespace threadlocal_detail {\n\nnamespace {\n\nstruct murmur_hash_fn {\n  std::uint64_t operator()(std::uint64_t const val) const noexcept {\n    auto const ptr = reinterpret_cast<const char*>(&val);\n    return folly::hash::murmurHash64(ptr, sizeof(val), 0u);\n  }\n};\n\n} // namespace\n\nSharedPtrDeleter::SharedPtrDeleter(std::shared_ptr<void> const& ts) noexcept\n    : ts_{ts} {}\nSharedPtrDeleter::SharedPtrDeleter(SharedPtrDeleter const& that) noexcept\n    : ts_{that.ts_} {}\nSharedPtrDeleter::~SharedPtrDeleter() = default;\nvoid SharedPtrDeleter::operator()(\n    void* /* ptr */, folly::TLPDestructionMode) const {\n  ts_.reset();\n}\n\nuintptr_t ElementWrapper::castForgetAlign(DeleterFunType* f) noexcept {\n  auto const p = reinterpret_cast<char const*>(f);\n  auto const q = std::launder(p);\n  return reinterpret_cast<uintptr_t>(q);\n}\n\nbool ThreadEntrySet::basicSanity() const {\n  if constexpr (!kIsDebug) {\n    return true;\n  }\n  if (threadElements.empty() && entryToVectorSlot.empty()) {\n    return true;\n  }\n  if (threadElements.size() != entryToVectorSlot.size()) {\n    return false;\n  }\n  auto const size = threadElements.size();\n  // NOLINTNEXTLINE\n  static std::atomic<std::uint64_t> rng_seed_{to_unsigned(std::rand())};\n  using engine = hash_counter_engine<murmur_hash_fn, std::uint64_t>;\n  thread_local engine rng{rng_seed_.fetch_add(1, std::memory_order_relaxed)};\n  std::uniform_int_distribution<size_t> dist{0, size - 1};\n  if (!(dist(rng) < constexpr_log2(size))) {\n    return true;\n  }\n  for (auto const& kvp : entryToVectorSlot) {\n    if (!(kvp.second < threadElements.size() &&\n          threadElements[kvp.second].threadEntry == kvp.first)) {\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid ThreadEntrySet::clear() {\n  DCHECK(basicSanity());\n  entryToVectorSlot.clear();\n  threadElements.clear();\n}\n\nint64_t ThreadEntrySet::getIndexFor(ThreadEntry* entry) const {\n  auto iter = entryToVectorSlot.find(entry);\n  if (iter != entryToVectorSlot.end()) {\n    return static_cast<int64_t>(iter->second);\n  }\n  return -1;\n}\n\nvoid* ThreadEntrySet::getPtrForThread(ThreadEntry* entry) const {\n  auto index = getIndexFor(entry);\n  if (index < 0) {\n    return nullptr;\n  }\n  return threadElements[static_cast<size_t>(index)].wrapper.ptr;\n}\n\nbool ThreadEntrySet::contains(ThreadEntry* entry) const {\n  DCHECK(basicSanity());\n  return entryToVectorSlot.find(entry) != entryToVectorSlot.end();\n}\n\nbool ThreadEntrySet::insert(ThreadEntry* entry) {\n  DCHECK(basicSanity());\n  auto iter = entryToVectorSlot.find(entry);\n  if (iter != entryToVectorSlot.end()) {\n    // Entry already present. Sanity check and exit.\n    DCHECK_EQ(entry, threadElements[iter->second].threadEntry);\n    return false;\n  }\n  threadElements.emplace_back(entry);\n  auto idx = threadElements.size() - 1;\n  entryToVectorSlot[entry] = idx;\n  return true;\n}\n\nbool ThreadEntrySet::insert(const Element& element) {\n  DCHECK(basicSanity());\n  auto iter = entryToVectorSlot.find(element.threadEntry);\n  if (iter != entryToVectorSlot.end()) {\n    // Entry already present. Skip copying over element. Caller\n    // responsible for handling acceptability of this behavior.\n    DCHECK_EQ(element.threadEntry, threadElements[iter->second].threadEntry);\n    return false;\n  }\n  threadElements.push_back(element);\n  auto idx = threadElements.size() - 1;\n  entryToVectorSlot[element.threadEntry] = idx;\n  return true;\n}\n\nThreadEntrySet::Element ThreadEntrySet::erase(ThreadEntry* entry) {\n  DCHECK(basicSanity());\n  auto iter = entryToVectorSlot.find(entry);\n  if (iter == entryToVectorSlot.end()) {\n    // Entry not present.\n    return Element{nullptr};\n  }\n  auto idx = iter->second;\n  DCHECK_LT(idx, threadElements.size());\n  entryToVectorSlot.erase(iter);\n  Element last = threadElements.back();\n  Element current = threadElements[idx];\n  if (idx != threadElements.size() - 1) {\n    threadElements[idx] = last;\n    entryToVectorSlot[last.threadEntry] = idx;\n  }\n  threadElements.pop_back();\n  DCHECK(basicSanity());\n  if (compressible()) {\n    compress();\n  }\n  DCHECK(basicSanity());\n  return current;\n}\n\nbool ThreadEntrySet::compressible() const {\n  // We choose a sufficiently-large multiplier so that there is no risk of a\n  // following insert growing the vector and then a following erase shrinking\n  // the vector, since that way lies non-amortized-O(N)-complexity costs for\n  // both insert and erase ops.\n  constexpr size_t const mult = 4;\n  auto& vec = threadElements;\n  return std::max(size_t(1), vec.size()) * mult <= vec.capacity();\n}\n\nbool ThreadEntry::cachedInSetMatchesElementsArray(uint32_t id) {\n  if constexpr (!kIsDebug) {\n    return true;\n  }\n\n  if (removed_) {\n    // Pointer in entry set and elements array need not match anymore.\n    return true;\n  }\n\n  auto rlock = meta->allId2ThreadEntrySets_[id].tryRLock();\n  if (!rlock) {\n    // Try lock failed. Skip checking in this case. Avoids\n    // getting stuck in case this validation is called when\n    // already holding the entry set lock.\n    return true;\n  }\n\n  return elements[id].ptr == rlock->getPtrForThread(this);\n}\n\nvoid ThreadEntrySet::compress() {\n  assert(compressible());\n  // compress the vector\n  threadElements.shrink_to_fit();\n  // compress the index\n  EntryIndex newIndex;\n  newIndex.reserve(entryToVectorSlot.size());\n  while (!entryToVectorSlot.empty()) {\n    newIndex.insert(entryToVectorSlot.extract(entryToVectorSlot.begin()));\n  }\n  entryToVectorSlot = std::move(newIndex);\n}\n\n/**\n * We want to disable onThreadExit call at the end of shutdown, we don't care\n * about leaking memory at that point.\n *\n * Otherwise if ThreadLocal is used in a shared library, onThreadExit may be\n * called after dlclose().\n *\n * This class has one single static instance; however since it's so widely used,\n * directly or indirectly, by so many classes, we need to take care to avoid\n * problems stemming from the Static Initialization/Destruction Order Fiascos.\n * Therefore this class needs to be constexpr-constructible, so as to avoid\n * the need for this to participate in init/destruction order.\n */\nclass PthreadKeyUnregister {\n public:\n  static constexpr size_t kMaxKeys = size_t(1) << 16;\n\n  ~PthreadKeyUnregister() {\n    // If static constructor priorities are not supported then\n    // ~PthreadKeyUnregister logic is not safe.\n#if !defined(__APPLE__) && !defined(_MSC_VER)\n    MSLGuard lg(lock_);\n    while (size_) {\n      pthread_key_delete(keys_[--size_]);\n    }\n#endif\n  }\n\n  static void registerKey(pthread_key_t key) { instance_.registerKeyImpl(key); }\n\n private:\n  /**\n   * Only one global instance should exist, hence this is private.\n   * See also the important note at the top of this class about `constexpr`\n   * usage.\n   */\n  constexpr PthreadKeyUnregister() : lock_(), size_(0), keys_() {}\n\n  void registerKeyImpl(pthread_key_t key) {\n    MSLGuard lg(lock_);\n    if (size_ == kMaxKeys) {\n      throw_exception<std::logic_error>(\n          \"pthread_key limit has already been reached\");\n    }\n    keys_[size_++] = key;\n  }\n\n  MicroSpinLock lock_;\n  size_t size_;\n  pthread_key_t keys_[kMaxKeys];\n\n  static PthreadKeyUnregister instance_;\n};\n\nStaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict)\n    : nextId_(1), threadEntry_(threadEntry), strict_(strict) {\n  int ret = pthread_key_create(&pthreadKey_, &onThreadExit);\n  checkPosixError(ret, \"pthread_key_create failed\");\n  PthreadKeyUnregister::registerKey(pthreadKey_);\n}\n\nThreadEntryList* StaticMetaBase::getThreadEntryList() {\n  class PthreadKey {\n   public:\n    static void onThreadExit(void* ptr) {\n      ThreadEntryList* list = static_cast<ThreadEntryList*>(ptr);\n      StaticMetaBase::cleanupThreadEntriesAndList(list);\n    }\n\n    PthreadKey() {\n      int ret = pthread_key_create(&pthreadKey_, &onThreadExit);\n      checkPosixError(ret, \"pthread_key_create failed\");\n      PthreadKeyUnregister::registerKey(pthreadKey_);\n    }\n\n    FOLLY_ALWAYS_INLINE pthread_key_t get() const { return pthreadKey_; }\n\n   private:\n    pthread_key_t pthreadKey_;\n  };\n\n  auto& instance = detail::createGlobal<PthreadKey, void>();\n\n  ThreadEntryList* threadEntryList =\n      static_cast<ThreadEntryList*>(pthread_getspecific(instance.get()));\n\n  if (FOLLY_UNLIKELY(!threadEntryList)) {\n    auto uptr = std::make_unique<ThreadEntryList>();\n    int ret = pthread_setspecific(instance.get(), uptr.get());\n    checkPosixError(ret, \"pthread_setspecific failed\");\n    threadEntryList = uptr.release();\n    threadEntryList->count = 1; // Pin once for own onThreadExit callback.\n    lsan_ignore_object(threadEntryList);\n  }\n\n  return threadEntryList;\n}\n\nThreadEntry* StaticMetaBase::allocateNewThreadEntry() {\n  // If a thread is exiting, various cleanup handlers could\n  // end up triggering use of a ThreadLocal and (re)create a\n  // ThreadEntry. This is problematic since we don't know if the\n  // destructor specified in the pthreadKey_ has already been run\n  // for this StaticMeta or will run again. Such a newly created\n  // ThreadEntry could leak. The count incremented on threadEntryList\n  // could also cause all the list and other threadEntry objects used\n  // by this thread to leak.\n  // For now, debug assert that the thread is not exiting to keep the\n  // legacy behavior. The handling of this case needs to be improved\n  // as well.\n  DCHECK(!dying());\n  ThreadEntryList* threadEntryList = getThreadEntryList();\n  ThreadEntry* threadEntry = new ThreadEntry();\n\n  threadEntry->list = threadEntryList;\n  threadEntry->listNext = threadEntryList->head;\n  threadEntryList->head = threadEntry;\n\n  threadEntry->tid() = std::this_thread::get_id();\n  threadEntry->tid_os = folly::getOSThreadID();\n\n  // if we're adding a thread entry\n  // we need to increment the list count\n  // even if the entry is reused\n  threadEntryList->count++;\n\n  threadEntry->meta = this;\n  return threadEntry;\n}\n\nbool StaticMetaBase::dying() {\n  return folly::detail::thread_is_dying();\n}\n\n/*\n * Note on safe lifecycle management of TL objects:\n * Any instance of a TL object created has to be disposed of safely. The\n * non-trivial cases are a) when a thread exits and we have to dispose of all\n * instances that belong to the thread across different TL objects; b) when a TL\n * object is destroyed and we have to dispose of all instances of that TL object\n * across all the threads that may have instantiated it. These 2 paths can also\n * race and are resolved, using the ThreadEntrySet locks, as follows:\n * - The exiting thread goes over all ThreadEntrySets and removes itself from\n * it. After this, the thread is responsible for disposing of any elements that\n * are still set in its 'elements' array. By setting the removed_ flag any new\n * elements created when disposing elements will not be made discoverable via\n * ThreadEntrySets and hence remain the responsibility of the exiting thread to\n * dispose.\n * - The thread calling destroy goes over all ThreadEntry in the ThreadEntrySet\n * of the TL object being destoryed (in popThreadEntrySetAndClearElementPtrs).\n * The ElementWrapper for the ThreadEntry found there are copied out and cleared\n * from the 'elements' array. This is done under the ThreadEntrySet lock and the\n * 'destroy' call is responsible for disposing the elements it thus claimed. The\n * actual dispose happens after releasing the locks and since the TL object is\n * being destroyed, no new elements of it should be getting created. The destroy\n * code should not access any ThreadEntry outside the safety of\n * popThreadEntrySetAndClearElementPtrs as their owner threads could exit and\n * release them.\n */\n\nvoid StaticMetaBase::onThreadExit(void* ptr) {\n  folly::detail::thread_is_dying_mark();\n  auto threadEntry = static_cast<ThreadEntry*>(ptr);\n\n  {\n    auto& meta = *threadEntry->meta;\n\n    // Make sure this ThreadEntry is available if ThreadLocal A is accessed in\n    // ThreadLocal B destructor.\n    pthread_setspecific(meta.pthreadKey_, threadEntry);\n\n    std::shared_lock forkRlock(meta.forkHandlerLock_);\n    std::shared_lock rlock(meta.accessAllThreadsLock_, std::defer_lock);\n    if (meta.strict_) {\n      rlock.lock();\n    }\n    meta.removeThreadEntryFromAllInMap(threadEntry);\n    forkRlock.unlock();\n    {\n      std::lock_guard g(meta.lock_);\n      // mark it as removed. Once this is set, any new TL object created in this\n      // thread as part of invoking the destructor on another TL object will not\n      // record this threadEntry in that TL object's ThreadEntrySet. This thread\n      // itself will be responsible for cleaning up the new object(s) so\n      // created.\n      threadEntry->removed_ = true;\n      auto elementsCapacity = threadEntry->getElementsCapacity();\n      auto beforeCount = meta.totalElementWrappers_.fetch_sub(elementsCapacity);\n      DCHECK_GE(beforeCount, elementsCapacity);\n      // No need to hold the lock any longer; the ThreadEntry is private to this\n      // thread now that it's been removed from meta.\n    }\n    // NOTE: User-provided deleter / object dtor itself may be using ThreadLocal\n    // with the same Tag, so dispose() calls below may (re)create some of the\n    // elements or even increase elementsCapacity, thus multiple cleanup rounds\n    // may be required.\n    for (bool shouldRun = true; shouldRun;) {\n      shouldRun = false;\n      auto elementsCapacity = threadEntry->getElementsCapacity();\n      FOR_EACH_RANGE (i, 0, elementsCapacity) {\n        if (threadEntry->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) {\n          threadEntry->elements[i].cleanup();\n          shouldRun = true;\n        }\n      }\n      DCHECK(meta.isThreadEntryRemovedFromAllInMap(threadEntry, !meta.strict_));\n    }\n    pthread_setspecific(meta.pthreadKey_, nullptr);\n  }\n\n  auto threadEntryList = threadEntry->list;\n  DCHECK_GT(threadEntryList->count, 0u);\n\n  cleanupThreadEntriesAndList(threadEntryList);\n}\n\n/* static */\nvoid StaticMetaBase::cleanupThreadEntriesAndList(\n    ThreadEntryList* threadEntryList) {\n  --threadEntryList->count;\n  if (threadEntryList->count) {\n    return;\n  }\n\n  // dispose all the elements\n  for (bool shouldRunOuter = true; shouldRunOuter;) {\n    shouldRunOuter = false;\n    auto tmp = threadEntryList->head;\n    while (tmp) {\n      auto& meta = *tmp->meta;\n      pthread_setspecific(meta.pthreadKey_, tmp);\n      std::shared_lock rlock(meta.accessAllThreadsLock_, std::defer_lock);\n      if (meta.strict_) {\n        rlock.lock();\n      }\n      // ThreadEntry 'tmp' was removed from all sets in its own cleanup. Since\n      // we set removed_ flag, no subsequent element being added (during destroy\n      // of other TL elements) should have added the ThreadEntry back to the\n      // tracking set.\n      DCHECK(meta.isThreadEntryRemovedFromAllInMap(tmp, !meta.strict_));\n\n      for (bool shouldRunInner = true; shouldRunInner;) {\n        shouldRunInner = false;\n        auto elementsCapacity = tmp->getElementsCapacity();\n        FOR_EACH_RANGE (i, 0, elementsCapacity) {\n          if (tmp->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) {\n            tmp->elements[i].cleanup();\n            shouldRunInner = true;\n            shouldRunOuter = true;\n          }\n        }\n      }\n      pthread_setspecific(meta.pthreadKey_, nullptr);\n      tmp = tmp->listNext;\n    }\n  }\n\n  // free the entry list\n  auto head = threadEntryList->head;\n  threadEntryList->head = nullptr;\n  while (head) {\n    auto tmp = head;\n    head = head->listNext;\n    if (tmp->elements) {\n      free(tmp->elements);\n      tmp->elements = nullptr;\n      tmp->setElementsCapacity(0);\n    }\n\n    // Fail safe check to make sure that the ThreadEntry is not present\n    // before issuing a delete.\n    DCHECK(tmp->meta->isThreadEntryRemovedFromAllInMap(tmp, true));\n\n    delete tmp;\n  }\n\n  delete threadEntryList;\n}\n\nuint32_t StaticMetaBase::elementsCapacity() const {\n  ThreadEntry* threadEntry = (*threadEntry_)();\n\n  return FOLLY_LIKELY(!!threadEntry) ? threadEntry->getElementsCapacity() : 0;\n}\n\nuint32_t StaticMetaBase::allocate(EntryID* ent) {\n  uint32_t id;\n  auto& meta = *this;\n  std::lock_guard g(meta.lock_);\n\n  id = ent->value.load(std::memory_order_relaxed);\n\n  if (id == kEntryIDInvalid) {\n    if (!meta.freeIds_.empty()) {\n      id = meta.freeIds_.back();\n      meta.freeIds_.pop_back();\n    } else {\n      id = meta.nextId_++;\n    }\n    uint32_t old_id = ent->value.exchange(id, std::memory_order_release);\n    DCHECK_EQ(old_id, kEntryIDInvalid);\n  }\n  return id;\n}\n\nThreadEntrySet StaticMetaBase::popThreadEntrySetAndClearElementPtrs(\n    uint32_t id) {\n  // Lock the ThreadEntrySet for id so that no other thread can update\n  // its local ptr or alter the its elements array or ThreadEntry object\n  // itself, before this function is done updating them. This is called\n  // by the `destroy` function to release a TL object. There should be\n  // no racing accessAllThreads() call with it.\n  auto wlocked = allId2ThreadEntrySets_[id].wlock();\n  ThreadEntrySet tmp;\n  std::swap(*wlocked, tmp);\n  std::lock_guard g(lock_);\n  for (auto& e : tmp.threadElements) {\n    auto elementsCapacity = e.threadEntry->getElementsCapacity();\n    if (id < elementsCapacity) {\n      /*\n       * Writing another thread's ThreadEntry from here is fine;\n       * The TL object is being destroyed, so get(id), or reset()\n       * or accessAllThreads calls on it are illegal. Only other\n       * racing accesses would be from the owner thread itself\n       * either a) reallocating the elements array (guarded by\n       * lock_, so safe) or b) exiting and trying to clear the\n       * elements array or free the elements and ThreadEntry itself. The\n       * ThreadEntrySet lock synchronizes this part as the exiting thread will\n       * acquire it to remove itself from the set.\n       */\n      DCHECK_EQ(e.threadEntry->elements[id].ptr, e.wrapper.ptr);\n      e.wrapper.deleter = e.threadEntry->elements[id].deleter;\n      e.threadEntry->elements[id].ptr = nullptr;\n      e.threadEntry->elements[id].deleter = 0;\n    }\n    // Destroy should not access thread entry after this call as racing\n    // exit call can make it invalid.\n    e.threadEntry = nullptr;\n  }\n  return tmp;\n}\n\nvoid StaticMetaBase::destroy(EntryID* ent) {\n  try {\n    auto& meta = *this;\n\n    // Elements in other threads that use this id.\n    ThreadEntrySet tmpEntrySet;\n\n    {\n      std::shared_lock forkRlock(meta.forkHandlerLock_);\n      std::unique_lock wlock(meta.accessAllThreadsLock_, std::defer_lock);\n      if (meta.strict_) {\n        /*\n         * In strict mode, the logic guarantees per-thread instances are\n         * destroyed by the moment ThreadLocal<> dtor returns.\n         * In order to achieve that, we should wait until concurrent\n         * onThreadExit() calls (that might acquire ownership over per-thread\n         * instances in order to destroy them) are finished.\n         */\n        wlock.lock();\n      }\n\n      uint32_t id =\n          ent->value.exchange(kEntryIDInvalid, std::memory_order_acquire);\n      if (id == kEntryIDInvalid) {\n        return;\n      }\n      tmpEntrySet = meta.popThreadEntrySetAndClearElementPtrs(id);\n      forkRlock.unlock();\n\n      {\n        // Release the id to be reused by another TL variable. Some\n        // other TL object may acquire and re-use it before the rest\n        // of the cleanup work is done. That is ok. The destructor callbacks\n        // for the objects should not access the same TL variable again. If\n        // we want to tolerate that pattern, due to any legacy behavior, this\n        // block should be after the loop to dispose of all collected elements\n        // below.\n        std::lock_guard g(meta.lock_);\n        meta.freeIds_.push_back(id);\n      }\n    }\n    // Delete elements outside the locks.\n    for (auto& e : tmpEntrySet.threadElements) {\n      if (e.wrapper.dispose(TLPDestructionMode::ALL_THREADS)) {\n        e.wrapper.cleanup();\n      }\n    }\n  } catch (...) { // Just in case we get a lock error or something anyway...\n    LOG(WARNING) << \"Destructor discarding an exception that was thrown.\";\n  }\n}\n\nElementWrapper* StaticMetaBase::reallocate(\n    ThreadEntry* threadEntry, uint32_t idval, size_t& newCapacity) {\n  size_t prevCapacity = threadEntry->getElementsCapacity();\n\n  // Growth factor < 2, see folly/docs/FBVector.md; + 5 to prevent\n  // very slow start.\n  auto smallCapacity = static_cast<size_t>((idval + 5) * kSmallGrowthFactor);\n  auto bigCapacity = static_cast<size_t>((idval + 5) * kBigGrowthFactor);\n\n  newCapacity =\n      (threadEntry->meta && (bigCapacity <= threadEntry->meta->nextId_))\n      ? bigCapacity\n      : smallCapacity;\n\n  assert(newCapacity > prevCapacity);\n  ElementWrapper* reallocated = nullptr;\n\n  // Need to grow. Note that we can't call realloc, as elements is\n  // still linked in meta, so another thread might access invalid memory\n  // after realloc succeeds. We'll copy by hand and update our ThreadEntry\n  // under the lock.\n  if (usingJEMalloc()) {\n    bool success = false;\n    size_t newByteSize = nallocx(newCapacity * sizeof(ElementWrapper), 0);\n\n    // Try to grow in place.\n    //\n    // Note that xallocx(MALLOCX_ZERO) will only zero newly allocated memory,\n    // even if a previous allocation allocated more than we requested.\n    // This is fine; we always use MALLOCX_ZERO with jemalloc and we\n    // always expand our allocation to the real size.\n    if (prevCapacity * sizeof(ElementWrapper) >= jemallocMinInPlaceExpandable) {\n      success =\n          (xallocx(threadEntry->elements, newByteSize, 0, MALLOCX_ZERO) ==\n           newByteSize);\n    }\n\n    // In-place growth failed.\n    if (!success) {\n      success =\n          ((reallocated = static_cast<ElementWrapper*>(\n                mallocx(newByteSize, MALLOCX_ZERO))) != nullptr);\n    }\n\n    if (success) {\n      // Expand to real size\n      assert(newByteSize / sizeof(ElementWrapper) >= newCapacity);\n      newCapacity = newByteSize / sizeof(ElementWrapper);\n    } else {\n      throw_exception<std::bad_alloc>();\n    }\n  } else { // no jemalloc\n    // calloc() is simpler than malloc() followed by memset(), and\n    // potentially faster when dealing with a lot of memory, as it can get\n    // already-zeroed pages from the kernel.\n    reallocated = static_cast<ElementWrapper*>(\n        calloc(newCapacity, sizeof(ElementWrapper)));\n    if (!reallocated) {\n      throw_exception<std::bad_alloc>();\n    }\n\n    // When the main thread exits, it will call functions registered with\n    // 'atexit' and then call 'exit()'. However, It will NOT call any functions\n    // registered via the 'TLS' feature of pthread_key_create.\n    // Reference:\n    // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html\n    folly::lsan_ignore_object(reallocated);\n  }\n  return reallocated;\n}\n\n/**\n * Reserve enough space in the ThreadEntry::elements for the item\n * @id to fit in.\n */\n\nvoid StaticMetaBase::reserve(EntryID* id) {\n  auto& meta = *this;\n  ThreadEntry* threadEntry = (*threadEntry_)();\n  size_t prevCapacity = threadEntry->getElementsCapacity();\n\n  uint32_t idval = id->getOrAllocate(meta);\n  if (prevCapacity > idval) {\n    return;\n  }\n\n  size_t newCapacity;\n  ElementWrapper* reallocated = reallocate(threadEntry, idval, newCapacity);\n\n  // Success, update the entry\n  {\n    std::lock_guard g(meta.lock_);\n\n    if (reallocated) {\n      /*\n       * Note: we need to hold the meta lock when copying data out of\n       * the old vector, because some other thread might be\n       * destructing a ThreadLocal and writing to the elements vector\n       * of this thread.\n       */\n      if (prevCapacity != 0) {\n        memcpy(\n            reallocated,\n            threadEntry->elements,\n            sizeof(*reallocated) * prevCapacity);\n      }\n      std::swap(reallocated, threadEntry->elements);\n    }\n\n    threadEntry->setElementsCapacity(newCapacity);\n  }\n\n  meta.totalElementWrappers_ += (newCapacity - prevCapacity);\n  free(reallocated);\n}\n\nFOLLY_NOINLINE void StaticMetaBase::ensureThreadEntryIsInSet(\n    ThreadEntry* te,\n    SynchronizedThreadEntrySet& set,\n    SynchronizedThreadEntrySet::RLockedPtr& rlock) {\n  rlock.unlock();\n  auto wlock = set.wlock();\n  wlock->insert(te);\n  rlock = wlock.moveFromWriteToRead();\n}\n\n/*\n * release the element @id.\n */\nvoid* ThreadEntry::releaseElement(uint32_t id) {\n  auto rlocked = meta->allId2ThreadEntrySets_[id].rlock();\n  auto capacity = getElementsCapacity();\n  void* ptrToReturn = (capacity >= id) ? elements[id].release() : nullptr;\n  auto slot = rlocked->getIndexFor(this);\n  if (slot < 0) {\n    DCHECK(removed_ || ptrToReturn == nullptr);\n    return ptrToReturn;\n  }\n  auto& element = rlocked.asNonConstUnsafe().threadElements[slot];\n  DCHECK_EQ(ptrToReturn, element.wrapper.ptr);\n  element.wrapper = {};\n  return ptrToReturn;\n}\n\n/*\n * Cleanup the element. Caller is holding rlock on the ThreadEntrySet\n * corresponding to the id. Running destructors of user objects isn't ideal\n * under lock but this is the historical behavior. It should be possible to\n * restructure this if a need for it arises.\n */\nvoid ThreadEntry::cleanupElement(uint32_t id) {\n  elements[id].dispose(TLPDestructionMode::THIS_THREAD);\n  // Cleanup\n  elements[id].cleanup();\n}\n\nvoid ThreadEntry::resetElementImplAfterSet(\n    const ElementWrapper& element, uint32_t id) {\n  auto& set = meta->allId2ThreadEntrySets_[id];\n  auto rlock = set.rlock();\n  cleanupElement(id);\n  elements[id] = element;\n  if (removed_) {\n    // Elements no longer being mirrored in the ThreadEntrySet.\n    // Thread must have cleared itself from the set when it started exiting.\n    DCHECK(!rlock->contains(this));\n    return;\n  }\n  if (element.ptr != nullptr && !rlock->contains(this)) {\n    meta->ensureThreadEntryIsInSet(this, set, rlock);\n  }\n  auto slot = rlock->getIndexFor(this);\n  if (slot < 0) {\n    // Not present in ThreadEntrySet implies the value was never set to be\n    // non-null and new value in element.ptr is nullptr as well.\n    DCHECK(!element.ptr);\n    DCHECK(!elements[id].ptr);\n    return;\n  }\n  size_t uslot = static_cast<size_t>(slot);\n  rlock.asNonConstUnsafe().threadElements[uslot].wrapper = element;\n}\n\nFOLLY_STATIC_CTOR_PRIORITY_MAX\nPthreadKeyUnregister PthreadKeyUnregister::instance_;\n#if defined(__GLIBC__)\n// Invoking thread_local dtor register early to fix issue\n// https://github.com/facebook/folly/issues/1252\nstruct GlibcThreadLocalInit {\n  struct GlibcThreadLocalInitHelper {\n    FOLLY_NOINLINE ~GlibcThreadLocalInitHelper() {\n      compiler_must_not_elide(this);\n    }\n  };\n  GlibcThreadLocalInit() {\n    static thread_local GlibcThreadLocalInitHelper glibcThreadLocalInit;\n    compiler_must_not_elide(glibcThreadLocalInit);\n  }\n};\n__attribute__((\n    __init_priority__(101))) GlibcThreadLocalInit glibcThreadLocalInit;\n#endif\n} // namespace threadlocal_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/ThreadLocalDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <limits.h>\n\n#include <atomic>\n#include <functional>\n#include <memory>\n#include <mutex>\n#include <string>\n#include <thread>\n#include <unordered_map>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/Exception.h>\n#include <folly/Function.h>\n#include <folly/MapUtil.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/SharedMutex.h>\n#include <folly/Synchronized.h>\n#include <folly/concurrency/container/atomic_grow_array.h>\n#include <folly/container/Foreach.h>\n#include <folly/detail/StaticSingletonManager.h>\n#include <folly/detail/UniqueInstance.h>\n#include <folly/lang/Exception.h>\n#include <folly/memory/Malloc.h>\n#include <folly/portability/PThread.h>\n#include <folly/synchronization/MicroSpinLock.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n#include <folly/system/AtFork.h>\n#include <folly/system/ThreadId.h>\n\nnamespace folly {\n\nenum class TLPDestructionMode { THIS_THREAD, ALL_THREADS };\nstruct AccessModeStrict {};\n\nnamespace threadlocal_detail {\n\nconstexpr uint32_t kEntryIDInvalid = std::numeric_limits<uint32_t>::max();\n\n//  as a memory-usage optimization, try to make this deleter fit in-situ in\n//  the deleter function storage rather than being heap-allocated separately\n//\n//  for libstdc++, specialization below of std::__is_location_invariant\n//\n//  TODO: ensure in-situ storage for other standard-library implementations\nstruct SharedPtrDeleter {\n  mutable std::shared_ptr<void> ts_;\n  explicit SharedPtrDeleter(std::shared_ptr<void> const& ts) noexcept;\n  SharedPtrDeleter(SharedPtrDeleter const& that) noexcept;\n  void operator=(SharedPtrDeleter const& that) = delete;\n  ~SharedPtrDeleter();\n  void operator()(void* ptr, folly::TLPDestructionMode) const;\n};\n\n} // namespace threadlocal_detail\n\n} // namespace folly\n\n#if defined(__GLIBCXX__)\n\nnamespace std {\n\ntemplate <>\nstruct __is_location_invariant<::folly::threadlocal_detail::SharedPtrDeleter>\n    : std::true_type {};\n\n} // namespace std\n\n#endif\n\nnamespace folly {\n\nnamespace threadlocal_detail {\n\nstruct StaticMetaBase;\nstruct ThreadEntryList;\n\n/**\n * POD wrapper around an element (a void*) and an associated deleter.\n * This must be POD, as we memset() it to 0 and memcpy() it around.\n */\nstruct ElementWrapper {\n  using DeleterFunType = void(void*, TLPDestructionMode);\n  using DeleterObjType = std::function<DeleterFunType>;\n\n  static inline constexpr auto deleter_obj_mask = uintptr_t(0b01);\n  static inline constexpr auto deleter_all_mask = uintptr_t(0) //\n      | deleter_obj_mask //\n      ;\n\n  static_assert(alignof(DeleterObjType) > deleter_all_mask);\n\n  //  must be noinline and must launder: https://godbolt.org/z/bo6f7f6v6\n  FOLLY_NOINLINE static uintptr_t castForgetAlign(DeleterFunType*) noexcept;\n\n  bool dispose(TLPDestructionMode mode) noexcept {\n    if (ptr == nullptr) {\n      return false;\n    }\n\n    DCHECK_NE(0, deleter);\n    auto const deleter_masked = deleter & ~deleter_all_mask;\n    if (deleter & deleter_obj_mask) {\n      auto& obj = *reinterpret_cast<DeleterObjType*>(deleter_masked);\n      obj(ptr, mode);\n    } else {\n      auto& fun = *reinterpret_cast<DeleterFunType*>(deleter_masked);\n      fun(ptr, mode);\n    }\n    return true;\n  }\n\n  void* release() {\n    auto retPtr = ptr;\n\n    if (ptr != nullptr) {\n      cleanup();\n    }\n\n    return retPtr;\n  }\n\n  template <class Ptr>\n  void set(Ptr p) {\n    DCHECK_EQ(static_cast<void*>(nullptr), ptr);\n    DCHECK_EQ(0, deleter);\n\n    if (!p) {\n      return;\n    }\n    auto const fun = +[](void* pt, TLPDestructionMode) {\n      delete static_cast<Ptr>(pt);\n    };\n    auto const raw = castForgetAlign(fun);\n    if (raw & deleter_all_mask) {\n      return set(p, std::ref(*fun));\n    }\n    DCHECK_EQ(0, raw & deleter_all_mask);\n    deleter = raw;\n    ptr = p;\n  }\n\n  template <typename Ptr, typename Deleter>\n  static auto makeDeleter(const Deleter& d) {\n    return [d](void* pt, TLPDestructionMode mode) {\n      d(static_cast<Ptr>(pt), mode);\n    };\n  }\n\n  template <typename Ptr>\n  static decltype(auto) makeDeleter(const SharedPtrDeleter& d) {\n    return d;\n  }\n\n  template <class Ptr, class Deleter>\n  void set(Ptr p, const Deleter& d) {\n    DCHECK_EQ(static_cast<void*>(nullptr), ptr);\n    DCHECK_EQ(0, deleter);\n\n    if (!p) {\n      return;\n    }\n\n    auto guard = makeGuard([&] { d(p, TLPDestructionMode::THIS_THREAD); });\n    auto const obj = new DeleterObjType(makeDeleter<Ptr>(d));\n    guard.dismiss();\n    auto const raw = reinterpret_cast<uintptr_t>(obj);\n    DCHECK_EQ(0, raw & deleter_all_mask);\n    deleter = raw | deleter_obj_mask;\n    ptr = p;\n  }\n\n  void cleanup() noexcept {\n    if (deleter & deleter_obj_mask) {\n      auto const deleter_masked = deleter & ~deleter_all_mask;\n      auto const obj = reinterpret_cast<DeleterObjType*>(deleter_masked);\n      delete obj;\n    }\n    ptr = nullptr;\n    deleter = 0;\n  }\n\n  void* ptr;\n  uintptr_t deleter;\n\n  ElementWrapper() : ptr(nullptr), deleter(0) {}\n};\n\n/**\n * Per-thread entry.  Each thread using a StaticMeta object has one.\n * This is written from the owning thread only (under the lock), read\n * from the owning thread (no lock necessary), and read from other threads\n * (under the lock).\n */\nstruct ThreadEntry {\n  ElementWrapper* elements{nullptr};\n  std::atomic<size_t> elementsCapacity{0};\n  ThreadEntryList* list{nullptr};\n  ThreadEntry* listNext{nullptr};\n  StaticMetaBase* meta{nullptr};\n  bool removed_{false};\n  uint64_t tid_os{};\n  aligned_storage_for_t<std::thread::id> tid_data{};\n\n  size_t getElementsCapacity() const noexcept {\n    return elementsCapacity.load(std::memory_order_relaxed);\n  }\n\n  void setElementsCapacity(size_t capacity) noexcept {\n    elementsCapacity.store(capacity, std::memory_order_relaxed);\n  }\n\n  std::thread::id& tid() {\n    return *reinterpret_cast<std::thread::id*>(&tid_data);\n  }\n\n  /*\n   * Releases element from ThreadEntry::elements at index @id.\n   */\n  void* releaseElement(uint32_t id);\n\n  /*\n   * Clean up element from ThreadEntry::elements at index @id.\n   */\n  void cleanupElement(uint32_t id);\n\n  /*\n   * Templated methods to deal with reset with and without a deleter\n   * for the element @id\n   */\n  template <class Ptr>\n  void resetElement(Ptr p, uint32_t id);\n\n  template <class Ptr, class Deleter>\n  void resetElement(Ptr p, Deleter& d, uint32_t id);\n\n  void resetElementImplAfterSet(const ElementWrapper& element, uint32_t id);\n\n  bool cachedInSetMatchesElementsArray(uint32_t id);\n};\n\nstruct ThreadEntryList {\n  ThreadEntry* head{nullptr};\n  size_t count{0};\n};\n\n/**\n * Cache the ptr + deleter info in ThreadEntrySet too. This allows\n * accessAllThreads() to get to the per thread ptr without holding the\n * StaticMeta's lock_. Eventually, the deleter info will be\n * moved to the ThreadEntrySet alone, leaving only the ptr in the\n * ElementWrapper. For now, the ElementDisposeInfo tracked in ThreadEntrySet is\n * the same as ElementWrapper.\n */\nusing ElementDisposeInfo = ElementWrapper;\n\n// ThreadEntrySet is used to track all ThreadEntry that have a valid\n// ElementWrapper for a particular TL id. The class provides no internal locking\n// and caller must ensure safety of any access.\nstruct ThreadEntrySet {\n  struct Element {\n    ElementDisposeInfo wrapper;\n    ThreadEntry* threadEntry;\n\n    /* implicit */ Element(ThreadEntry* entry = nullptr) : threadEntry(entry) {}\n  };\n\n  // Vector of ThreadEntry for fast iteration during accessAllThreads.\n  using ElementVector = std::vector<Element>;\n  ElementVector threadElements;\n  // Map from ThreadEntry* to its slot in the threadElements vector to be able\n  // to remove an entry quickly.\n  using EntryIndex = std::unordered_map<ThreadEntry*, ElementVector::size_type>;\n  EntryIndex entryToVectorSlot;\n\n  bool basicSanity() const;\n\n  void clear();\n\n  int64_t getIndexFor(ThreadEntry* entry) const;\n\n  /**\n   * Helper function for debugging checks. Fetch ptr for a given ThreadEnrtry.\n   * Used to sanity check the value in ElementDisposeInfo wrapper matches the\n   * ElementWrapper array used for fast access from the thread itself.\n   */\n  void* getPtrForThread(ThreadEntry* entry) const;\n\n  bool contains(ThreadEntry* entry) const;\n\n  bool insert(ThreadEntry* entry);\n\n  bool insert(const Element& element);\n\n  Element erase(ThreadEntry* entry);\n\n  /// compressible\n  ///\n  /// If many elements have been removed, then size might be much less than\n  /// capacity and it becomes possible to reduce memory usage.\n  bool compressible() const;\n\n  /// compress\n  ///\n  /// Attempt to reduce the memory usage of the data structure.\n  void compress();\n};\n\nstruct StaticMetaBase {\n  // In general, emutls cleanup is not guaranteed to play nice with the way\n  // StaticMeta mixes direct pthread calls and the use of __thread. This has\n  // caused problems on multiple platforms so don't use __thread there.\n  //\n  // XXX: Ideally we would instead determine if emutls is in use at runtime as\n  // it is possible to configure glibc on Linux to use emutls regardless.\n  static constexpr bool kUseThreadLocal = !kIsMobile && !kIsApple && !kMscVer;\n\n  // Represents an ID of a thread local object. Initially set to the maximum\n  // uint. This representation allows us to avoid a branch in accessing TLS data\n  // (because if you test capacity > id if id = maxint then the test will always\n  // fail). It allows us to keep a constexpr constructor and avoid SIOF.\n  class EntryID {\n   public:\n    std::atomic<uint32_t> value;\n\n    constexpr EntryID() : value(kEntryIDInvalid) {}\n\n    EntryID(EntryID&& other) noexcept : value(other.value.load()) {\n      other.value = kEntryIDInvalid;\n    }\n\n    EntryID& operator=(EntryID&& other) noexcept {\n      assert(this != &other);\n      DCHECK(value.load() == kEntryIDInvalid);\n      value = other.value.load();\n      other.value = kEntryIDInvalid;\n      return *this;\n    }\n\n    EntryID(const EntryID& other) = delete;\n    EntryID& operator=(const EntryID& other) = delete;\n\n    uint32_t getOrInvalid() { return value.load(std::memory_order_acquire); }\n\n    uint32_t getOrAllocate(StaticMetaBase& meta) {\n      uint32_t id = getOrInvalid();\n      if (id != kEntryIDInvalid) {\n        return id;\n      }\n      // The lock inside allocate ensures that a single value is allocated\n      return meta.allocate(this);\n    }\n  };\n\n  StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict);\n\n  FOLLY_EXPORT static ThreadEntryList* getThreadEntryList();\n\n  ThreadEntry* allocateNewThreadEntry();\n\n  static bool dying();\n\n  static void onThreadExit(void* ptr);\n\n  // Helper to do final free and delete of ThreadEntry and ThreadEntryList\n  // structures.\n  static void cleanupThreadEntriesAndList(ThreadEntryList* list);\n\n  // returns the elementsCapacity for the\n  // current thread ThreadEntry struct\n  uint32_t elementsCapacity() const;\n\n  uint32_t allocate(EntryID* ent);\n\n  void destroy(EntryID* ent);\n\n  /**\n   * Reserve enough space in the ThreadEntry::elements for the item\n   * @id to fit in.\n   */\n  void reserve(EntryID* id);\n\n  ElementWrapper& getElement(EntryID* ent);\n\n  using SynchronizedThreadEntrySet = folly::Synchronized<ThreadEntrySet>;\n\n  /*\n   * Helper inline methods to add/remove/clear ThreadEntry* from\n   * allId2ThreadEntrySets_\n   */\n\n  /*\n   * Return true if given ThreadEntry is already present in the ThreadEntrySet\n   * for the given id.\n   */\n  FOLLY_ALWAYS_INLINE bool isThreadEntryInSet(ThreadEntry* te, uint32_t id) {\n    return allId2ThreadEntrySets_[id].rlock()->contains(te);\n  }\n\n  /*\n   * Ensure the given ThreadEntry* is present in the tracking set for the\n   * given id. Once added, we do not remove it until the thread exits or the\n   * whole set is reaped when the TL id itself is destroyed.\n   *\n   * Note: Call may drop and reacquire the read lock.\n   * If the provided entry is not already in the set, the given RLockedPtr will\n   * be released, entry added under a WLockedPtr, and RLockedPtr reacquired\n   * before returning.\n   */\n  FOLLY_NOINLINE void ensureThreadEntryIsInSet(\n      ThreadEntry* te,\n      SynchronizedThreadEntrySet& set,\n      SynchronizedThreadEntrySet::RLockedPtr& rlock);\n\n  /*\n   * Remove a ThreadEntry* from the map of allId2ThreadEntrySets_\n   * for all slot @id's in ThreadEntry::elements that are\n   * used. This is essentially clearing out a ThreadEntry entirely\n   * from the allId2ThreadEntrySets_.\n   */\n  FOLLY_ALWAYS_INLINE void removeThreadEntryFromAllInMap(ThreadEntry* te) {\n    for (const auto ptr : getThreadEntrySetsPtrSpan()) {\n      auto& set = *ptr;\n      set.wlock()->erase(te);\n    }\n  }\n\n  /*\n   * Pop current ThreadEntrySet and for each ThreadEntry in it, clear its\n   * ElementWrapper for the 'id' and return them in the accumulated vector. This\n   * is called when an TL object is destroyed. The ElementWrapper returned are\n   * the responsibility of the calling thread to dispose of.\n   */\n  ThreadEntrySet popThreadEntrySetAndClearElementPtrs(uint32_t id);\n\n  /*\n   * Check if ThreadEntry* is present in the map for all slots of @ids.\n   */\n  FOLLY_ALWAYS_INLINE bool isThreadEntryRemovedFromAllInMap(\n      ThreadEntry* te, bool needForkLock) {\n    std::shared_lock rlocked(forkHandlerLock_, std::defer_lock);\n    if (needForkLock) {\n      rlocked.lock();\n    }\n    for (const auto ptr : getThreadEntrySetsPtrSpan()) {\n      auto& set = *ptr;\n      if (set.rlock()->contains(te)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // static helper method to reallocate the ThreadEntry::elements\n  // returns != nullptr if the ThreadEntry::elements was reallocated\n  // nullptr if the ThreadEntry::elements was just extended\n  // and throws stdd:bad_alloc if memory cannot be allocated\n  static ElementWrapper* reallocate(\n      ThreadEntry* threadEntry, uint32_t idval, size_t& newCapacity);\n\n  span<SynchronizedThreadEntrySet* const> getThreadEntrySetsPtrSpan() {\n    return allId2ThreadEntrySets_.as_view().as_ptr_span(nextId_.load());\n  }\n\n  relaxed_atomic_uint32_t nextId_;\n  std::vector<uint32_t> freeIds_;\n  // The lock_ is used to protect the freeIds_ list as well as synchronize\n  // reallocation of a thread's private array of ElementWrappers. The freeIds_\n  // vector is manipulated on TL object id allocation and destroy. Resize of\n  // ElementWrappers array can only be done by its owner thread but other\n  // threads may try to be accessing the array at the same time if in the middle\n  // of destroying a TL object.\n  std::mutex lock_;\n  mutable SharedMutex accessAllThreadsLock_;\n  // As part of handling fork, we need to ensure no locks used by ThreadLocal\n  // implementation are held by threads other than the one forking. The total\n  // number of locks involved is large due to the per ThreadEntrySet lock. TSAN\n  // builds have to track each lock acquire and release. TSAN also has its own\n  // fork handler. Using a lot of locks in fork handler can end up deadlocking\n  // TSAN. To avoid that behavior, we the forkHandlerLock_. All code paths that\n  // acquire a lock on any ThreadEntrySet (accessAllThreads() or reset() calls)\n  // must also acquire a shared lock on forkHandlerLock_.\n  // Fork handler will acquire an exclusive lock on forkHandlerLock_,\n  // along with exclusive locks on accessAllThreadsLock_ and lock_.\n  mutable SharedMutex forkHandlerLock_;\n  pthread_key_t pthreadKey_;\n  ThreadEntry* (*threadEntry_)();\n  bool strict_;\n  // Total size of ElementWrapper arrays across all threads. This is meant\n  // to surface the overhead of thread local tracking machinery since the array\n  // can be sparse when there are lots of thread local variables under the same\n  // tag.\n  relaxed_atomic_int64_t totalElementWrappers_{0};\n  // This is a map of all thread entries mapped to index i with active\n  // elements[i];\n  folly::atomic_grow_array<SynchronizedThreadEntrySet> allId2ThreadEntrySets_;\n\n  // Note on locking rules. There are 4 locks involved in managing StaticMeta:\n  // fork handler lock (getStaticMetaGlobalForkMutex(),\n  // access all threads lock (accessAllThreadsLock_),\n  // per thread entry set lock implicit in SynchronizedThreadEntrySet and\n  // meta lock (lock_)\n  //\n  // If multiple locks need to be acquired in a call path, the above is also\n  // the order in which they should be acquired. Additionally, if per\n  // ThreadEntrySet locks are the only ones that are acquired in a path, it\n  // must also acquire shared lock on the fork handler lock.\n};\n\nstruct FakeUniqueInstance {\n  template <template <typename...> class Z, typename... Key, typename... Mapped>\n  FOLLY_ERASE constexpr explicit FakeUniqueInstance(\n      tag_t<Z<Key..., Mapped...>>, tag_t<Key...>, tag_t<Mapped...>) noexcept {}\n};\n\n/*\n * Resets element from ThreadEntry::elements at index @id.\n * call set() on the element to reset it.\n * This is a templated method for when a deleter is not provided.\n */\ntemplate <class Ptr>\nvoid ThreadEntry::resetElement(Ptr p, uint32_t id) {\n  ElementWrapper element;\n  element.set(p);\n  resetElementImplAfterSet(element, id);\n}\n\n/*\n * Resets element from ThreadEntry::elements at index @id.\n * call set() on the element to reset it.\n * This is a templated method for when a deleter is provided.\n */\ntemplate <class Ptr, class Deleter>\nvoid ThreadEntry::resetElement(Ptr p, Deleter& d, uint32_t id) {\n  ElementWrapper element;\n  element.set(p, d);\n  resetElementImplAfterSet(element, id);\n}\n\n// Held in a singleton to track our global instances.\n// We have one of these per \"Tag\", by default one for the whole system\n// (Tag=void).\n//\n// Creating and destroying ThreadLocalPtr objects, as well as thread exit\n// for threads that use ThreadLocalPtr objects collide on a lock inside\n// StaticMeta; you can specify multiple Tag types to break that lock.\ntemplate <class Tag, class AccessMode>\nstruct FOLLY_EXPORT StaticMeta final : StaticMetaBase {\n private:\n  static constexpr bool IsTagVoid = std::is_void_v<Tag>;\n  static constexpr bool IsAccessModeStrict =\n      std::is_same_v<AccessMode, AccessModeStrict>;\n  static_assert(!IsTagVoid || !IsAccessModeStrict);\n\n  using UniqueInstance =\n      conditional_t<IsTagVoid, FakeUniqueInstance, detail::UniqueInstance>;\n  static UniqueInstance unique;\n\n public:\n  StaticMeta()\n      : StaticMetaBase(&StaticMeta::getThreadEntrySlow, IsAccessModeStrict) {\n    AtFork::registerHandler(\n        this,\n        /*prepare*/ &StaticMeta::preFork,\n        /*parent*/ &StaticMeta::onForkParent,\n        /*child*/ &StaticMeta::onForkChild);\n  }\n\n  static StaticMeta<Tag, AccessMode>& instance() {\n    (void)unique; // force the object not to be thrown out as unused\n    // Leak it on exit, there's only one per process and we don't have to\n    // worry about synchronization with exiting threads.\n    return detail::createGlobal<StaticMeta<Tag, AccessMode>, void>();\n  }\n\n  struct LocalCache {\n    ThreadEntry* threadEntry;\n    size_t capacity;\n  };\n  static_assert(std::is_standard_layout_v<LocalCache>);\n  static_assert(std::is_trivial_v<LocalCache>);\n\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE static LocalCache& getLocalCache() {\n    static thread_local LocalCache instance;\n    return instance;\n  }\n\n  FOLLY_ALWAYS_INLINE static ElementWrapper& get(EntryID* ent) {\n    // Eliminate as many branches and as much extra code as possible in the\n    // cached fast path, leaving only one branch here and one indirection\n    // below.\n\n    ThreadEntry* te = getThreadEntry(ent);\n    uint32_t id = ent->getOrInvalid();\n    // Only valid index into the the elements array\n    DCHECK_NE(id, kEntryIDInvalid);\n    DCHECK(te->cachedInSetMatchesElementsArray(id));\n    return te->elements[id];\n  }\n\n  /*\n   * In order to facilitate adding/clearing ThreadEntry* to\n   * StaticMetaBase::allId2ThreadEntrySets_ during ThreadLocalPtr\n   * reset()/release() we need access to the ThreadEntry* directly. This allows\n   * for direct interaction with StaticMetaBase::allId2ThreadEntrySets_. We keep\n   * StaticMetaBase::allId2ThreadEntrySets_ updated with ThreadEntry* whenever a\n   * ThreadLocal is set/released.\n   */\n  FOLLY_ALWAYS_INLINE static ThreadEntry* getThreadEntry(EntryID* ent) {\n    if (!kUseThreadLocal) {\n      return getThreadEntrySlowReserve(ent);\n    }\n\n    // Eliminate as many branches and as much extra code as possible in the\n    // cached fast path, leaving only one branch here and one indirection below.\n    uint32_t id = ent->getOrInvalid();\n    auto& cache = getLocalCache();\n    if (FOLLY_UNLIKELY(cache.capacity <= id)) {\n      getSlowReserveAndCache(ent, cache);\n    }\n    return cache.threadEntry;\n  }\n\n  FOLLY_NOINLINE static void getSlowReserveAndCache(\n      EntryID* ent, LocalCache& cache) {\n    auto threadEntry = getThreadEntrySlowReserve(ent);\n    cache.capacity = threadEntry->getElementsCapacity();\n    cache.threadEntry = threadEntry;\n  }\n\n  FOLLY_NOINLINE static ThreadEntry* getThreadEntrySlowReserve(EntryID* ent) {\n    uint32_t id = ent->getOrInvalid();\n\n    auto& inst = instance();\n    auto threadEntry = inst.threadEntry_();\n    if (FOLLY_UNLIKELY(threadEntry->getElementsCapacity() <= id)) {\n      inst.reserve(ent);\n      id = ent->getOrInvalid();\n    }\n    assert(threadEntry->getElementsCapacity() > id);\n    return threadEntry;\n  }\n\n  FOLLY_EXPORT FOLLY_NOINLINE static ThreadEntry* getThreadEntrySlow() {\n    auto& meta = instance();\n    auto key = meta.pthreadKey_;\n    ThreadEntry* threadEntry =\n        static_cast<ThreadEntry*>(pthread_getspecific(key));\n    if (!threadEntry) {\n      threadEntry = meta.allocateNewThreadEntry();\n      int ret = pthread_setspecific(key, threadEntry);\n      checkPosixError(ret, \"pthread_setspecific failed\");\n    }\n    return threadEntry;\n  }\n\n  static bool preFork() {\n    auto& meta = instance();\n    bool gotLock = meta.forkHandlerLock_.try_lock(); // Make sure it's created\n    if (!gotLock) {\n      return false;\n    }\n    meta.accessAllThreadsLock_.lock();\n    meta.lock_.lock();\n    // Okay to not lock each set in meta.allId2ThreadEntrySets\n    // as accessAllThreadsLock_ in held by calls to reset() and\n    // accessAllThreads.\n    return true;\n  }\n\n  static void onForkParent() {\n    auto& meta = instance();\n    meta.lock_.unlock();\n    meta.accessAllThreadsLock_.unlock();\n    meta.forkHandlerLock_.unlock();\n  }\n\n  static void onForkChild() {\n    auto& meta = instance();\n    // only the current thread survives\n    meta.lock_.unlock();\n    meta.accessAllThreadsLock_.unlock();\n    auto threadEntry = meta.threadEntry_();\n    // Loop through allId2ThreadEntrySets_; Only keep ThreadEntry* in the map\n    // for ThreadEntry::elements that are still in use by the current thread.\n    // Evict all of the ThreadEntry* from other threads.\n    for (const auto ptr : meta.getThreadEntrySetsPtrSpan()) {\n      auto& set = *ptr;\n      auto wlockedSet = set.wlock();\n      auto slot = wlockedSet->getIndexFor(threadEntry);\n      if (slot >= 0) {\n        auto element = wlockedSet->threadElements[slot];\n        wlockedSet->clear();\n        wlockedSet->insert(element);\n      } else {\n        wlockedSet->clear();\n      }\n    }\n    meta.forkHandlerLock_.unlock();\n  }\n};\n\nFOLLY_PUSH_WARNING\nFOLLY_CLANG_DISABLE_WARNING(\"-Wglobal-constructors\")\ntemplate <typename Tag, typename AccessMode>\ntypename StaticMeta<Tag, AccessMode>::UniqueInstance\n    StaticMeta<Tag, AccessMode>::unique{\n        tag<StaticMeta>, tag<Tag>, tag<AccessMode>};\nFOLLY_POP_WARNING\n\n} // namespace threadlocal_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/TrapOnAvx512.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/TrapOnAvx512.h>\n\n#include <folly/Portability.h>\n\n#include <cstdint>\n#include <cstring>\n\nnamespace folly::detail {\n#if FOLLY_X64 && defined(__AVX512F__)\nnamespace {\nvoid detectTrapOnAvx512Helper() {\n  __asm__ volatile(\"vscalefpd %zmm2, %zmm17, %zmm19\");\n}\n} // namespace\n\nbool hasTrapOnAvx512() {\n  static constexpr uint8_t kUd2[] = {0x0f, 0x0b};\n  return memcmp((void*)detectTrapOnAvx512Helper, kUd2, sizeof(kUd2)) == 0;\n}\n#else\nbool hasTrapOnAvx512() {\n  return false;\n}\n#endif\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/detail/TrapOnAvx512.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly::detail {\nbool hasTrapOnAvx512();\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/detail/TurnSequencer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <limits>\n\n#include <glog/logging.h>\n\n#include <folly/Portability.h>\n#include <folly/chrono/Hardware.h>\n#include <folly/detail/Futex.h>\n#include <folly/portability/Asm.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\nnamespace detail {\n\n/// A TurnSequencer allows threads to order their execution according to\n/// a monotonically increasing (with wraparound) \"turn\" value.  The two\n/// operations provided are to wait for turn T, and to move to the next\n/// turn.  Every thread that is waiting for T must have arrived before\n/// that turn is marked completed (for MPMCQueue only one thread waits\n/// for any particular turn, so this is trivially true).\n///\n/// TurnSequencer's state_ holds 26 bits of the current turn (shifted\n/// left by 6), along with a 6 bit saturating value that records the\n/// maximum waiter minus the current turn.  Wraparound of the turn space\n/// is expected and handled.  This allows us to atomically adjust the\n/// number of outstanding waiters when we perform a FUTEX_WAKE operation.\n/// Compare this strategy to sem_t's separate num_waiters field, which\n/// isn't decremented until after the waiting thread gets scheduled,\n/// during which time more enqueues might have occurred and made pointless\n/// FUTEX_WAKE calls.\n///\n/// TurnSequencer uses futex() directly.  It is optimized for the\n/// case that the highest awaited turn is 32 or less higher than the\n/// current turn.  We use the FUTEX_WAIT_BITSET variant, which lets\n/// us embed 32 separate wakeup channels in a single futex.  See\n/// http://locklessinc.com/articles/futex_cheat_sheet for a description.\n///\n/// We only need to keep exact track of the delta between the current\n/// turn and the maximum waiter for the 32 turns that follow the current\n/// one, because waiters at turn t+32 will be awoken at turn t.  At that\n/// point they can then adjust the delta using the higher base.  Since we\n/// need to encode waiter deltas of 0 to 32 inclusive, we use 6 bits.\n/// We actually store waiter deltas up to 63, since that might reduce\n/// the number of CAS operations a tiny bit.\n///\n/// To avoid some futex() calls entirely, TurnSequencer uses an adaptive\n/// spin cutoff before waiting.  The overheads (and convergence rate)\n/// of separately tracking the spin cutoff for each TurnSequencer would\n/// be prohibitive, so the actual storage is passed in as a parameter and\n/// updated atomically.  This also lets the caller use different adaptive\n/// cutoffs for different operations (read versus write, for example).\n/// To avoid contention, the spin cutoff is only updated when requested\n/// by the caller.\n///\n/// On x86 the latency of a spin loop varies dramatically across\n/// architectures due to changes in the PAUSE instruction. Skylake\n/// increases the latency by about a factor of 15 compared to previous\n/// architectures. To work around this, on x86 we measure spins using\n/// RDTSC rather than a loop counter.\ntemplate <template <typename> class Atom>\nstruct TurnSequencer {\n  explicit TurnSequencer(const uint32_t firstTurn = 0) noexcept\n      : state_(encode(firstTurn << kTurnShift, 0)) {}\n\n  /// Returns true iff a call to waitForTurn(turn, ...) won't block\n  bool isTurn(const uint32_t turn) const noexcept {\n    auto state = state_.load(std::memory_order_acquire);\n    return decodeCurrentSturn(state) == (turn << kTurnShift);\n  }\n\n  enum class TryWaitResult { SUCCESS, PAST, TIMEDOUT };\n\n  /// See tryWaitForTurn\n  /// Requires that `turn` is not a turn in the past.\n  void waitForTurn(\n      const uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff) noexcept {\n    const auto ret = tryWaitForTurn(turn, spinCutoff, updateSpinCutoff);\n    DCHECK(ret == TryWaitResult::SUCCESS);\n  }\n\n  // Internally we always work with shifted turn values, which makes the\n  // truncation and wraparound work correctly.  This leaves us bits at\n  // the bottom to store the number of waiters.  We call shifted turns\n  // \"sturns\" inside this class.\n\n  /// Blocks the current thread until turn has arrived.\n  /// If updateSpinCutoff is true then this will spin for up to\n  /// kMaxSpinLimit before blocking and will adjust spinCutoff based\n  /// on the results, otherwise it will spin for at most spinCutoff.\n  /// Returns SUCCESS if the wait succeeded, PAST if the turn is in the\n  /// past or TIMEDOUT if the absTime time value is not nullptr and is\n  /// reached before the turn arrives\n  template <\n      class Clock = std::chrono::steady_clock,\n      class Duration = typename Clock::duration>\n  TryWaitResult tryWaitForTurn(\n      const uint32_t turn,\n      Atom<uint32_t>& spinCutoff,\n      const bool updateSpinCutoff,\n      const std::chrono::time_point<Clock, Duration>* absTime =\n          nullptr) noexcept {\n    uint32_t prevThresh = spinCutoff.load(std::memory_order_relaxed);\n    const uint32_t effectiveSpinCutoff =\n        updateSpinCutoff || prevThresh == 0 ? kMaxSpinLimit : prevThresh;\n\n    uint64_t begin = 0;\n    uint32_t tries;\n    const uint32_t sturn = turn << kTurnShift;\n    for (tries = 0;; ++tries) {\n      uint32_t state = state_.load(std::memory_order_acquire);\n      uint32_t current_sturn = decodeCurrentSturn(state);\n      if (current_sturn == sturn) {\n        break;\n      }\n\n      // wrap-safe version of (current_sturn >= sturn)\n      if (sturn - current_sturn >= std::numeric_limits<uint32_t>::max() / 2) {\n        // turn is in the past\n        return TryWaitResult::PAST;\n      }\n\n      // the first effectSpinCutoff tries are spins, after that we will\n      // record ourself as a waiter and block with futexWait\n      if (kSpinUsingHardwareClock) {\n        auto now = hardware_timestamp();\n        if (tries == 0) {\n          begin = now;\n        }\n        if (tries == 0 || now < begin + effectiveSpinCutoff) {\n          asm_volatile_pause();\n          continue;\n        }\n      } else {\n        if (tries < effectiveSpinCutoff) {\n          asm_volatile_pause();\n          continue;\n        }\n      }\n\n      uint32_t current_max_waiter_delta = decodeMaxWaitersDelta(state);\n      uint32_t our_waiter_delta = (sturn - current_sturn) >> kTurnShift;\n      uint32_t new_state;\n      if (our_waiter_delta <= current_max_waiter_delta) {\n        // state already records us as waiters, probably because this\n        // isn't our first time around this loop\n        new_state = state;\n      } else {\n        new_state = encode(current_sturn, our_waiter_delta);\n        if (state != new_state &&\n            !state_.compare_exchange_strong(state, new_state)) {\n          continue;\n        }\n      }\n      if (absTime) {\n        auto futexResult = detail::futexWaitUntil(\n            &state_, new_state, *absTime, futexChannel(turn));\n        if (futexResult == FutexResult::TIMEDOUT) {\n          return TryWaitResult::TIMEDOUT;\n        }\n      } else {\n        detail::futexWait(&state_, new_state, futexChannel(turn));\n      }\n    }\n\n    if (updateSpinCutoff || prevThresh == 0) {\n      // if we hit kMaxSpinLimit then spinning was pointless, so the right\n      // spinCutoff is kMinSpinLimit\n      uint32_t target;\n      uint64_t elapsed = !kSpinUsingHardwareClock || tries == 0\n          ? tries\n          : hardware_timestamp() - begin;\n      if (tries >= kMaxSpinLimit) {\n        target = kMinSpinLimit;\n      } else {\n        // to account for variations, we allow ourself to spin 2*N when\n        // we think that N is actually required in order to succeed\n        target = std::min(\n            uint32_t{kMaxSpinLimit},\n            std::max(\n                uint32_t{kMinSpinLimit}, static_cast<uint32_t>(elapsed) * 2));\n      }\n\n      if (prevThresh == 0) {\n        // bootstrap\n        spinCutoff.store(target);\n      } else {\n        // try once, keep moving if CAS fails.  Exponential moving average\n        // with alpha of 7/8\n        // Be careful that the quantity we add to prevThresh is signed.\n        spinCutoff.compare_exchange_weak(\n            prevThresh, prevThresh + int(target - prevThresh) / 8);\n      }\n    }\n\n    return TryWaitResult::SUCCESS;\n  }\n\n  /// Unblocks a thread running waitForTurn(turn + 1)\n  void completeTurn(const uint32_t turn) noexcept {\n    uint32_t state = state_.load(std::memory_order_acquire);\n    while (true) {\n      DCHECK(state == encode(turn << kTurnShift, decodeMaxWaitersDelta(state)));\n      uint32_t max_waiter_delta = decodeMaxWaitersDelta(state);\n      uint32_t new_state = encode(\n          (turn + 1) << kTurnShift,\n          max_waiter_delta == 0 ? 0 : max_waiter_delta - 1);\n      if (state_.compare_exchange_strong(state, new_state)) {\n        if (max_waiter_delta != 0) {\n          detail::futexWake(\n              &state_, std::numeric_limits<int>::max(), futexChannel(turn + 1));\n        }\n        break;\n      }\n      // failing compare_exchange_strong updates first arg to the value\n      // that caused the failure, so no need to reread state_\n    }\n  }\n\n  /// Returns the least-most significant byte of the current uncompleted\n  /// turn.  The full 32 bit turn cannot be recovered.\n  uint8_t uncompletedTurnLSB() const noexcept {\n    return uint8_t(state_.load(std::memory_order_acquire) >> kTurnShift);\n  }\n\n private:\n  static constexpr bool kSpinUsingHardwareClock = kIsArchAmd64;\n  static constexpr uint32_t kCyclesPerSpinLimit =\n      kSpinUsingHardwareClock ? 1 : 10;\n\n  /// kTurnShift counts the bits that are stolen to record the delta\n  /// between the current turn and the maximum waiter. It needs to be big\n  /// enough to record wait deltas of 0 to 32 inclusive.  Waiters more\n  /// than 32 in the future will be woken up 32*n turns early (since\n  /// their BITSET will hit) and will adjust the waiter count again.\n  /// We go a bit beyond and let the waiter count go up to 63, which is\n  /// free and might save us a few CAS\n  static constexpr uint32_t kTurnShift = 6;\n  static constexpr uint32_t kWaitersMask = (1 << kTurnShift) - 1;\n\n  /// The minimum spin duration that we will adaptively select. The value\n  /// here is cycles, adjusted to the way in which the limit will actually\n  /// be applied.\n  static constexpr uint32_t kMinSpinLimit = 200 / kCyclesPerSpinLimit;\n\n  /// The maximum spin duration that we will adaptively select, and the\n  /// spin duration that will be used when probing to get a new data\n  /// point for the adaptation\n  static constexpr uint32_t kMaxSpinLimit = 20000 / kCyclesPerSpinLimit;\n\n  /// This holds both the current turn, and the highest waiting turn,\n  /// stored as (current_turn << 6) | min(63, max(waited_turn - current_turn))\n  Futex<Atom> state_;\n\n  /// Returns the bitmask to pass futexWait or futexWake when communicating\n  /// about the specified turn\n  uint32_t futexChannel(uint32_t turn) const noexcept {\n    return 1u << (turn & 31);\n  }\n\n  uint32_t decodeCurrentSturn(uint32_t state) const noexcept {\n    return state & ~kWaitersMask;\n  }\n\n  uint32_t decodeMaxWaitersDelta(uint32_t state) const noexcept {\n    return state & kWaitersMask;\n  }\n\n  uint32_t encode(uint32_t currentSturn, uint32_t maxWaiterD) const noexcept {\n    return currentSturn | std::min(uint32_t{kWaitersMask}, maxWaiterD);\n  }\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/TypeList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n\n/**\n * \\file TypeList.h\n * \\author Eric Niebler\n *\n * The file contains facilities for manipulating lists of types, and for\n * defining and composing operations over types.\n *\n * The type-operations behave like compile-time functions: they accept types as\n * input and produce types as output. A simple example is a template alias, like\n * `std::add_pointer_t`. However, templates are not themselves first class\n * citizens of the language; they cannot be easily \"returned\" from a\n * metafunction, and passing them to a metafunction is awkward and often\n * requires the user to help the C++ parser by adding hints like `typename`\n * and `template` to disambiguate the syntax. That makes higher-ordered\n * metaprogramming difficult. (There is no simple way to e.g., compose two\n * template aliases and pass the result as an argument to another template.)\n *\n * Instead, we wrap template aliases in a ordinary class, which _can_ be passed\n * and returned simply from metafunctions. This is like Boost.MPL's notion of a\n * \"metafunction class\"[1], and we adopt that terminology here.\n *\n * For the Folly.TypeList library, a metafunction class is a protocol that\n * all the components of Folly.TypeList expect and agree upon. It is a class\n * type that has a nested template alias called `Apply`. So for instance,\n * `std::add_pointer_t` as a Folly metafunction class would look like this:\n *\n *     struct AddPointer {\n *       template <class T>\n *       using apply = T*;\n *     };\n *\n * Folly.TypeList gives a simple way to \"lift\" an ordinary template alias into\n * a metafunction class: `MetaQuote`. The above `AddPointer` could instead be\n * written as:\n *\n *     using AddPointer = folly::MetaQuote<std::add_pointer_t>;\n *\n * \\par Naming\n *\n * A word about naming. Components in Folly.TypeList fall into two buckets:\n * utilities for manipulating lists of types, and utilities for manipulating\n * metafunction classes. The former have names that start with `Type`, as in\n * `TypeList` and `TypeTransform`. The latter have names that start with `Meta`,\n * as in `MetaQuote` and `MetaApply`.\n *\n * [1] Boost.MPL Metafunction Class:\n *     http://www.boost.org/libs/mpl/doc/refmanual/metafunction-class.html\n */\n\nnamespace folly {\nnamespace detail {\n\n/**\n * Handy shortcuts for some standard facilities\n */\ntemplate <bool B>\nusing Bool = std::bool_constant<B>;\nusing True = std::true_type;\nusing False = std::false_type;\n\n/**\n * Given a metafunction class `Fn` and arguments `Ts...`, invoke `Fn`\n * with `Ts...`.\n */\ntemplate <class Fn, class... Ts>\nusing MetaApply = typename Fn::template apply<Ts...>;\n\n/**\n * A list of types.\n */\ntemplate <class... Ts>\nstruct TypeList {\n  /**\n   * An alias for this list of types\n   */\n  using type = TypeList;\n\n  /**\n   * \\return the number of types in this list.\n   */\n  static constexpr std::size_t size() noexcept { return sizeof...(Ts); }\n\n  /**\n   * This list of types is also a metafunction class that accepts another\n   * metafunction class and invokes it with all the types in the list.\n   */\n  template <class Fn>\n  using apply = MetaApply<Fn, Ts...>;\n};\n\n/**\n * A wrapper for a type\n */\ntemplate <class T>\nstruct Type {\n  /**\n   * An alias for the wrapped type\n   */\n  using type = T;\n\n  /**\n   * This wrapper is a metafunction class that, when applied with any number\n   * of arguments, returns the wrapped type.\n   */\n  template <class...>\n  using apply = T;\n};\n\n/**\n * An empty struct.\n */\nstruct Empty {};\n\n/// \\cond\nnamespace impl {\ntemplate <bool B>\nstruct If_ {\n  template <class T, class U>\n  using apply = T;\n};\ntemplate <>\nstruct If_<false> {\n  template <class T, class U>\n  using apply = U;\n};\n} // namespace impl\n/// \\endcond\n\n/**\n * Like std::conditional, but with fewer template instantiations\n */\ntemplate <bool If_, class Then, class Else>\nusing If = MetaApply<impl::If_<If_>, Then, Else>;\n\n/**\n * Defers the evaluation of an alias.\n *\n * Given a template `C` and arguments `Ts...`, then\n * - If `C<Ts...>` is well-formed, `MetaApply<MetaDefer<C, Ts...>>` is well-\n *   formed and is an alias for `C<Ts...>`.\n * - Otherwise, `MetaApply<MetaDefer<C, Ts...>>` is ill-formed.\n */\ntemplate <template <class...> class C, class... Ts>\nclass MetaDefer {\n  template <template <class...> class D, class = D<Ts...>>\n  static char (&try_(int))[1];\n\n  template <template <class...> class D, class = void>\n  static char (&try_(long))[2];\n  struct Result {\n    using type = C<Ts...>;\n  };\n\n public:\n  template <class... Us>\n  using apply =\n      _t<If<sizeof(try_<C>(0)) - 1 || !!sizeof...(Us), Empty, Result>>;\n};\n\n/**\n * Compose two metafunction classes into one by chaining.\n *\n * `MetaApply<MetaCompose<P, Q>, Ts...>` is equivalent to\n * `MetaApply<P, MetaApply<Q, Ts...>>`.\n */\ntemplate <class P, class Q>\nstruct MetaCompose {\n  template <class... Ts>\n  using apply = MetaApply<P, MetaApply<Q, Ts...>>;\n};\n\n/**\n * A metafunction class that always returns its argument unmodified.\n *\n * `MetaApply<MetaIdentity, int>` is equivalent to `int`.\n */\nstruct MetaIdentity {\n  template <class T>\n  using apply = T;\n};\n\n/**\n * Lifts a class template or an alias template to be a metafunction class.\n *\n * `MetaApply<MetaQuote<C>, Ts...>` is equivalent to `C<Ts...>`.\n */\ntemplate <template <class...> class C>\nstruct MetaQuote {\n  template <class... Ts>\n  using apply = MetaApply<MetaDefer<C, Ts...>>;\n};\n\n/// \\cond\n// Specialization for TypeList since it doesn't need to go through MetaDefer\ntemplate <>\nstruct MetaQuote<TypeList> {\n  template <class... Ts>\n  using apply = TypeList<Ts...>;\n};\n/// \\endcond\n\n/**\n * Lifts a trait class template to be a metafunction class.\n *\n * `MetaApply<MetaQuoteTrait<C>, Ts...>` is equivalent to\n * `typename C<Ts...>::type`.\n */\ntemplate <template <class...> class C>\nusing MetaQuoteTrait = MetaCompose<MetaQuote<_t>, MetaQuote<C>>;\n\n/**\n * Partially evaluate the metafunction class `Fn` by binding the arguments\n * `Ts...` to the front of the argument list.\n *\n * `MetaApply<MetaBindFront<Fn, Ts...>, Us...>` is equivalent to\n * `MetaApply<Fn, Ts..., Us...>`.\n */\ntemplate <class Fn, class... Ts>\nstruct MetaBindFront {\n  template <class... Us>\n  using apply = MetaApply<Fn, Ts..., Us...>;\n};\n\n/**\n * Partially evaluate the metafunction class `Fn` by binding the arguments\n * `Ts...` to the back of the argument list.\n *\n * `MetaApply<MetaBindBack<Fn, Ts...>, Us...>` is equivalent to\n * `MetaApply<Fn, Us..., Ts...>`.\n */\ntemplate <class Fn, class... Ts>\nstruct MetaBindBack {\n  template <class... Us>\n  using apply = MetaApply<Fn, Us..., Ts...>;\n};\n\n/**\n * Given a metafunction class `Fn` that expects a single `TypeList` argument,\n * turn it into a metafunction class that takes `N` arguments, wraps them in\n * a `TypeList`, and calls `Fn` with it.\n *\n * `MetaApply<MetaCurry<Fn>, Ts...>` is equivalent to\n * `MetaApply<Fn, TypeList<Ts...>>`.\n */\ntemplate <class Fn>\nusing MetaCurry = MetaCompose<Fn, MetaQuote<TypeList>>;\n\n/**\n * Given a metafunction class `Fn` that expects `N` arguments,\n * turn it into a metafunction class that takes a single `TypeList` arguments\n * and calls `Fn` with the types in the `TypeList`.\n *\n * `MetaApply<MetaUncurry<Fn>, TypeList<Ts...>>` is equivalent to\n * `MetaApply<Fn, Ts...>`.\n */\ntemplate <class Fn>\nusing MetaUncurry = MetaBindBack<MetaQuote<MetaApply>, Fn>;\n\n/**\n * Given a `TypeList` and some arguments, append those arguments to the end of\n * the `TypeList`.\n *\n * `TypePushBack<TypeList<Ts...>, Us...>` is equivalent to\n * `TypeList<Ts..., Us...>`.\n */\ntemplate <class List, class... Ts>\nusing TypePushBack = MetaApply<List, MetaBindBack<MetaQuote<TypeList>, Ts...>>;\n\n/**\n * Given a `TypeList` and some arguments, prepend those arguments to the start\n * of the `TypeList`.\n *\n * `TypePushFront<TypeList<Ts...>, Us...>` is equivalent to\n * `TypeList<Us..., Ts...>`.\n */\ntemplate <class List, class... Ts>\nusing TypePushFront =\n    MetaApply<List, MetaBindFront<MetaQuote<TypeList>, Ts...>>;\n\n/**\n * Given a metafunction class `Fn` and a `TypeList`, call `Fn` with the types\n * in the `TypeList`.\n */\ntemplate <class Fn, class List>\nusing MetaUnpack = MetaApply<List, Fn>;\n\n/// \\cond\nnamespace impl {\ntemplate <class Fn>\nstruct TypeTransform_ {\n  template <class... Ts>\n  using apply = TypeList<MetaApply<Fn, Ts>...>;\n};\n} // namespace impl\n/// \\endcond\n\n/**\n * Transform all the elements in a `TypeList` with the metafunction class `Fn`.\n *\n * `TypeTransform<TypeList<Ts..>, Fn>` is equivalent to\n * `TypeList<MetaApply<Fn, Ts>...>`.\n */\ntemplate <class List, class Fn>\nusing TypeTransform = MetaApply<List, impl::TypeTransform_<Fn>>;\n\n/**\n * Given a binary metafunction class, convert it to another binary metafunction\n * class with the argument order reversed.\n */\ntemplate <class Fn>\nstruct MetaFlip {\n  template <class A, class B>\n  using apply = MetaApply<Fn, B, A>;\n};\n\n/// \\cond\nnamespace impl {\ntemplate <class Fn>\nstruct FoldR_ {\n  template <class... Ts>\n  struct Lambda : MetaIdentity {};\n  template <class A, class... Ts>\n  struct Lambda<A, Ts...> {\n    template <class State>\n    using apply = MetaApply<Fn, A, MetaApply<Lambda<Ts...>, State>>;\n  };\n  template <class A, class B, class C, class D, class... Ts>\n  struct Lambda<A, B, C, D, Ts...> { // manually unroll 4 elements\n    template <class State>\n    using apply = MetaApply<\n        Fn,\n        A,\n        MetaApply<\n            Fn,\n            B,\n            MetaApply<\n                Fn,\n                C,\n                MetaApply<Fn, D, MetaApply<Lambda<Ts...>, State>>>>>;\n  };\n  template <class... Ts>\n  using apply = Lambda<Ts...>;\n};\n} // namespace impl\n/// \\endcond\n\n/**\n * Given a `TypeList`, an initial state, and a binary function, reduce the\n * `TypeList` by applying the function to each element and the current state,\n * producing a new state to be used with the next element. This is a \"right\"\n * fold in functional parlance.\n *\n * `TypeFold<TypeList<A, B, C>, X, Fn>` is equivalent to\n * `MetaApply<Fn, A, MetaApply<Fn, B, MetaApply<Fn, C, X>>>`.\n */\ntemplate <class List, class State, class Fn>\nusing TypeFold = MetaApply<MetaApply<List, impl::FoldR_<Fn>>, State>;\n\n/// \\cond\nnamespace impl {\ntemplate <class Fn>\nstruct FoldL_ {\n  template <class... Ts>\n  struct Lambda : MetaIdentity {};\n  template <class A, class... Ts>\n  struct Lambda<A, Ts...> {\n    template <class State>\n    using apply = MetaApply<Lambda<Ts...>, MetaApply<Fn, State, A>>;\n  };\n  template <class A, class B, class C, class D, class... Ts>\n  struct Lambda<A, B, C, D, Ts...> { // manually unroll 4 elements\n    template <class State>\n    using apply = MetaApply<\n        Lambda<Ts...>,\n        MetaApply<\n            Fn,\n            MetaApply<Fn, MetaApply<Fn, MetaApply<Fn, State, A>, B>, C>,\n            D>>;\n  };\n  template <class... Ts>\n  using apply = Lambda<Ts...>;\n};\n} // namespace impl\n/// \\endcond\n\n/**\n * Given a `TypeList`, an initial state, and a binary function, reduce the\n * `TypeList` by applying the function to each element and the current state,\n * producing a new state to be used with the next element. This is a \"left\"\n * fold, in functional parlance.\n *\n * `TypeReverseFold<TypeList<A, B, C>, X, Fn>` is equivalent to\n * `MetaApply<Fn, MetaApply<Fn, MetaApply<Fn, X, C>, B, A>`.\n */\ntemplate <class List, class State, class Fn>\nusing TypeReverseFold = MetaApply<MetaApply<List, impl::FoldL_<Fn>>, State>;\n\nnamespace impl {\ntemplate <class List>\nstruct Inherit_;\ntemplate <class... Ts>\nstruct Inherit_<TypeList<Ts...>> : Ts... {\n  using type = Inherit_;\n};\n} // namespace impl\n\n/**\n * Given a `TypeList`, create a type that inherits from all the types in the\n * list.\n *\n * Requires: all of the types in the list are non-final class types, and the\n * types are all unique.\n */\ntemplate <class List>\nusing Inherit = impl::Inherit_<List>;\n\n/// \\cond\nnamespace impl {\n// Avoid instantiating std::is_base_of when we have an intrinsic.\n#if defined(__GNUC__) || defined(_MSC_VER)\ntemplate <class T, class... Set>\nusing In_ = Bool<__is_base_of(Type<T>, Inherit<TypeList<Type<Set>...>>)>;\n#else\ntemplate <class T, class... Set>\nusing In_ = std::is_base_of<Type<T>, Inherit<TypeList<Type<Set>...>>>;\n#endif\n\ntemplate <class T>\nstruct InsertFront_ {\n  template <class... Set>\n  using apply =\n      If<In_<T, Set...>::value, TypeList<Set...>, TypeList<T, Set...>>;\n};\n\nstruct Unique_ {\n  template <class T, class List>\n  using apply = MetaApply<List, impl::InsertFront_<T>>;\n};\n} // namespace impl\n/// \\endcond\n\n/**\n * Given a `TypeList`, produce a new list of types removing duplicates, keeping\n * the first seen element.\n *\n * `TypeUnique<TypeList<int, short, int>>` is equivalent to\n * `TypeList<int, short>`.\n *\n * \\note This algorithm is O(N^2).\n */\ntemplate <class List>\nusing TypeUnique = TypeFold<List, TypeList<>, impl::Unique_>;\n\n/**\n * Given a `TypeList`, produce a new list of types removing duplicates, keeping\n * the last seen element.\n *\n * `TypeUnique<TypeList<int, short, int>>` is equivalent to\n * `TypeList<short, int>`.\n *\n * \\note This algorithm is O(N^2).\n */\ntemplate <class List>\nusing TypeReverseUnique =\n    TypeReverseFold<List, TypeList<>, MetaFlip<impl::Unique_>>;\n\n/// \\cond\nnamespace impl {\ntemplate <class T>\nstruct AsTypeList_ {};\ntemplate <template <class...> class T, class... Ts>\nstruct AsTypeList_<T<Ts...>> {\n  using type = TypeList<Ts...>;\n};\ntemplate <class T, T... Is>\nstruct AsTypeList_<std::integer_sequence<T, Is...>> {\n  using type = TypeList<std::integral_constant<T, Is>...>;\n};\n} // namespace impl\n/// \\endcond\n\n/**\n * Convert a type to a list of types. Given a type `T`:\n * - If `T` is of the form `C<Ts...>`, where `C` is a class template and\n *   `Ts...` is a list of types, the result is `TypeList<Ts...>`.\n * - Else, if `T` is of the form `std::integer_sequence<T, Is...>`, then\n *   the result is `TypeList<std::integral_constant<T, Is>...>`.\n * - Otherwise, `asTypeList<T>` is ill-formed.\n */\ntemplate <class T>\nusing AsTypeList = _t<impl::AsTypeList_<T>>;\n\n/// \\cond\nnamespace impl {\n// TODO For a list of N lists, this algorithm is O(N). It does no unrolling.\nstruct Join_ {\n  template <class Fn>\n  struct Lambda {\n    template <class... Ts>\n    using apply = MetaBindBack<Fn, Ts...>;\n  };\n  template <class List, class Fn>\n  using apply = MetaApply<List, Lambda<Fn>>;\n};\n} // namespace impl\n/// \\endcond\n\n/**\n * Given a `TypeList` of `TypeList`s, flatten the lists into a single list.\n *\n * `TypeJoin<TypeList<TypeList<As...>, TypeList<Bs...>>>` is equivalent to\n * `TypeList<As..., Bs...>`\n */\ntemplate <class List>\nusing TypeJoin = MetaApply<TypeFold<List, MetaQuote<TypeList>, impl::Join_>>;\n\n/**\n * Given several `TypeList`s, flatten the lists into a single list.\n *\n * \\note This is just the curried form of `TypeJoin`. (See `MetaCurry`.)\n *\n * `TypeConcat<TypeList<As...>, TypeList<Bs...>>` is equivalent to\n * `TypeList<As..., Bs...>`\n */\ntemplate <class... Ts>\nusing TypeConcat = TypeJoin<TypeList<Ts...>>;\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/UniqueInstance.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/UniqueInstance.h>\n\n#include <cstdlib>\n#include <cstring>\n#include <iostream>\n#include <sstream>\n#include <stdexcept>\n#include <string>\n\n#include <folly/Demangle.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\nnamespace detail {\n\nnamespace {\n\nbool equal(std::type_info const& a, std::type_info const& b) {\n  if (kIsLibcpp) {\n    auto const an = a.name();\n    auto const bn = b.name();\n    return &a == &b || an == bn || 0 == std::strcmp(an, bn);\n  }\n\n  return a == b;\n}\n\nusing Ptr = std::type_info const*;\nstruct PtrRange {\n  Ptr const* b;\n  Ptr const* e;\n};\n\ntemplate <typename Value>\nPtrRange ptr_range_key(Value value) {\n  auto const data = value.ptrs;\n  return {data, data + value.key_size};\n}\n\ntemplate <typename Value>\nPtrRange ptr_range_mapped(Value value) {\n  auto const data = value.ptrs + value.key_size;\n  return {data, data + value.mapped_size};\n}\n\nbool equal(PtrRange lhs, PtrRange rhs) {\n  auto const cmp = [](auto a, auto b) { return equal(*a, *b); };\n  return std::equal(lhs.b, lhs.e, rhs.b, rhs.e, cmp);\n}\n\nstd::string_view parse_demangled_tag_name(std::string_view str) {\n  auto off = std::string_view::npos;\n  // strip surrounding `folly::tag<{...}>`\n  off = str.find('<');\n  str = str.substr(off + 1, str.size() - off - 2);\n  // strip trailing spaces, if any\n  off = str.find_last_not_of(' ');\n  str = str.substr(0, off == std::string_view::npos ? off : off + 1);\n  // done\n  return str;\n}\n\nstd::string join(PtrRange types) {\n  std::ostringstream ret;\n  for (auto t = types.b; t != types.e; ++t) {\n    if (t != types.b) {\n      ret << \", \";\n    }\n    ret << parse_demangled_tag_name(demangle((*t)->name()));\n  }\n  return ret.str();\n}\n\ntemplate <typename Value>\nfbstring render_tmpl(Value value) {\n  return fbstring(parse_demangled_tag_name(demangle(value.tmpl->name())));\n}\n\ntemplate <typename Value>\nstd::string render(Value value) {\n  auto const tmpl_s = render_tmpl(value);\n  auto const key_s = join(ptr_range_key(value));\n  auto const mapped_s = join(ptr_range_mapped(value));\n  std::ostringstream ret;\n  ret << tmpl_s << \"<\" << key_s << \", \" << mapped_s << \">\";\n  return ret.str();\n}\n\n} // namespace\n\nvoid UniqueInstance::enforce(Arg& arg) noexcept {\n  auto const& local = arg.local;\n  auto& global = StaticSingletonManager::create<Value>(arg.global);\n\n  if (!global.tmpl) {\n    global = local;\n    return;\n  }\n  if (!equal(*global.tmpl, *local.tmpl)) {\n    throw_exception<std::logic_error>(\"mismatched unique instance\");\n  }\n  if (!equal(ptr_range_key(global), ptr_range_key(local))) {\n    throw_exception<std::logic_error>(\"mismatched unique instance\");\n  }\n  if (equal(ptr_range_mapped(global), ptr_range_mapped(local))) {\n    return;\n  }\n\n  auto const key = ptr_range_key(local);\n\n  std::ios_base::Init io_init;\n  std::cerr << \"Overloaded unique instance over <\" << join(key) << \", ...> \"\n            << \"with differing trailing arguments:\\n\"\n            << \"  \" << render(global) << \"\\n\"\n            << \"  \" << render(local) << \"\\n\";\n  std::abort();\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/UniqueInstance.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <typeinfo>\n\n#include <folly/CppAttributes.h>\n#include <folly/detail/StaticSingletonManager.h>\n\nnamespace folly {\nnamespace detail {\n\nclass UniqueInstance {\n public:\n  template <template <typename...> class Z, typename... Key, typename... Mapped>\n  FOLLY_EXPORT FOLLY_ALWAYS_INLINE explicit UniqueInstance(\n      tag_t<Z<Key..., Mapped...>>, tag_t<Key...>, tag_t<Mapped...>) noexcept {\n    static constexpr Ptr const tmpl = FOLLY_TYPE_INFO_OF(key_t<Z>);\n    static constexpr Ptr const ptrs[] = {\n        FOLLY_TYPE_INFO_OF(tag_t<Key>)...,\n        FOLLY_TYPE_INFO_OF(tag_t<Mapped>)...};\n    static constinit Arg arg{\n        {tmpl, ptrs, sizeof...(Key), sizeof...(Mapped)},\n        {tag<Value, key_t<Z, Key...>>}};\n    enforce(arg);\n  }\n\n  UniqueInstance(UniqueInstance const&) = delete;\n  UniqueInstance(UniqueInstance&&) = delete;\n  UniqueInstance& operator=(UniqueInstance const&) = delete;\n  UniqueInstance& operator=(UniqueInstance&&) = delete;\n\n private:\n  template <template <typename...> class Z, typename... Key>\n  struct key_t {};\n\n  using Ptr = std::type_info const*;\n  struct Value {\n    Ptr tmpl;\n    Ptr const* ptrs;\n    std::uint32_t key_size;\n    std::uint32_t mapped_size;\n  };\n  struct Arg {\n    Value local;\n    StaticSingletonManager::ArgCreate<true> global;\n  };\n\n  static void enforce(Arg& arg) noexcept;\n};\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/base64_detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"base64_common\",\n    headers = [\"Base64Common.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base64_constants\",\n        \"//folly:utility\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_constants\",\n    headers = [\"Base64Constants.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_hidden_constants\",\n    headers = [\"Base64HiddenConstants.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base64_constants\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_scalar\",\n    headers = [\"Base64Scalar.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base64_common\",\n        \":base64_constants\",\n        \"//folly:utility\",\n        \"//folly/memory:uninitialized_memory_hacks\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_simd\",\n    headers = [\"Base64Simd.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base64_common\",\n        \":base64_constants\",\n        \":base64_hidden_constants\",\n        \":base64_scalar\",\n        \":base64_swar\",\n        \"//folly:c_portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_sse4_2_platform\",\n    headers = [\"Base64_SSE4_2_Platform.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base64_hidden_constants\",\n        \"//folly:portability\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"base64_sse4_2\",\n    srcs = [\"Base64_SSE4_2.cpp\"],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\"-msse4.2\"],\n    }),\n    raw_headers = [\"Base64_SSE4_2.h\"],\n    deps = [\n        \"//xplat/folly:portability\",\n        \"//xplat/folly/detail/base64_detail:base64_common\",\n        \"//xplat/folly/detail/base64_detail:base64_simd\",\n        \"//xplat/folly/detail/base64_detail:base64_sse4_2_platform\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_swar\",\n    srcs = [\"Base64SWAR.cpp\"],\n    headers = [\"Base64SWAR.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":base64_constants\",\n        \":base64_hidden_constants\",\n        \":base64_scalar\",\n        \"//folly:portability\",\n    ],\n    exported_deps = [\n        \":base64_common\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_api\",\n    srcs = [\"Base64Api.cpp\"],\n    headers = [\"Base64Api.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \":base64_sse4_2\",\n        \":base64_swar\",\n        \"//folly:cpu_id\",\n    ],\n    exported_deps = [\n        \":base64_common\",\n        \":base64_scalar\",\n        \"//folly:portability\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n\n# !!!! fbcode/folly/detail/base64_detail/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"base64_sse4_2\",\n    srcs = [\"Base64_SSE4_2.cpp\"],\n    headers = [\"Base64_SSE4_2.h\"],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\"-msse4.2\"],\n    }),\n    deps = [\n        \":base64_simd\",\n        \":base64_sse4_2_platform\",\n    ],\n    exported_deps = [\n        \":base64_common\",\n        \"//folly:portability\",\n    ],\n)\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64Api.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/CpuId.h>\n#include <folly/Portability.h>\n#include <folly/detail/base64_detail/Base64Api.h>\n#include <folly/detail/base64_detail/Base64SWAR.h>\n#include <folly/detail/base64_detail/Base64_SSE4_2.h>\n\nnamespace folly::detail::base64_detail {\nBase64RuntimeImpl base64EncodeSelectImplementation() {\n#if FOLLY_SSE_PREREQ(4, 2)\n  if (folly::CpuId().sse42()) {\n    return {\n        base64Encode_SSE4_2,\n        base64URLEncode_SSE4_2,\n        base64Decode_SSE4_2,\n        base64URLDecodeSWAR};\n  }\n#endif\n  return {\n      base64EncodeScalar,\n      base64URLEncodeScalar,\n      base64DecodeSWAR,\n      base64URLDecodeSWAR};\n}\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64Api.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n#include <folly/Portability.h>\n#include <folly/detail/base64_detail/Base64Common.h>\n#include <folly/detail/base64_detail/Base64Scalar.h>\n#include <folly/portability/Constexpr.h>\n\nnamespace folly::detail::base64_detail {\n\nstruct Base64RuntimeImpl {\n  using Encode = char* (*)(const char*, const char*, char*) noexcept;\n  using Decode =\n      Base64DecodeResult (*)(const char*, const char*, char*) noexcept;\n\n  Encode encode;\n  Encode encodeURL;\n  Decode decode;\n  Decode decodeURL;\n};\n\nBase64RuntimeImpl base64EncodeSelectImplementation();\n\ninline const auto& base64RuntimeImpl() {\n  static const auto r = base64EncodeSelectImplementation();\n  return r;\n}\n\ninline char* base64EncodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  return base64RuntimeImpl().encode(f, l, o);\n}\n\ninline constexpr char* base64Encode(\n    const char* f, const char* l, char* o) noexcept {\n  if (folly::is_constant_evaluated_or(true)) {\n    return base64EncodeScalar(f, l, o);\n  } else {\n    return base64EncodeRuntime(f, l, o);\n  }\n}\n\ninline char* base64URLEncodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  return base64RuntimeImpl().encodeURL(f, l, o);\n}\n\ninline constexpr char* base64URLEncode(\n    const char* f, const char* l, char* o) noexcept {\n  if (folly::is_constant_evaluated_or(true)) {\n    return base64URLEncodeScalar(f, l, o);\n  } else {\n    return base64URLEncodeRuntime(f, l, o);\n  }\n}\n\ninline Base64DecodeResult base64DecodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  return base64RuntimeImpl().decode(f, l, o);\n}\n\ninline constexpr Base64DecodeResult base64Decode(\n    const char* f, const char* l, char* o) noexcept {\n  if (folly::is_constant_evaluated_or(true)) {\n    return base64DecodeScalar(f, l, o);\n  } else {\n    return base64DecodeRuntime(f, l, o);\n  }\n}\n\ninline Base64DecodeResult base64URLDecodeRuntime(\n    const char* f, const char* l, char* o) noexcept {\n  return base64RuntimeImpl().decodeURL(f, l, o);\n}\n\ninline constexpr Base64DecodeResult base64URLDecode(\n    const char* f, const char* l, char* o) noexcept {\n  if (folly::is_constant_evaluated_or(true)) {\n    return base64URLDecodeScalar(f, l, o);\n  } else {\n    return base64URLDecodeRuntime(f, l, o);\n  }\n}\n\ninline constexpr const char* base64PHPStrictRemoveWhitespaces(\n    const char* f, const char* l, char* o) noexcept {\n  return base64PHPStrictRemoveWhitespacesScalar(f, l, o);\n}\n\ninline constexpr Base64DecodeResult base64PHPStrictDecode(\n    const char* f, const char* l, char* o) noexcept {\n  const char* o_l = base64PHPStrictRemoveWhitespaces(f, l, o);\n\n  // Encoded string is always longer so reuse output to avoid extra allocation\n  Base64DecodeResult result = base64Decode(o, o_l, o);\n  return {result.isSuccess, result.o};\n}\n\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64Common.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <folly/Utility.h>\n#include <folly/detail/base64_detail/Base64Constants.h>\n\nnamespace folly::detail::base64_detail {\n\nconstexpr std::size_t base64EncodedSize(std::size_t inSize) {\n  return ((inSize + 2) / 3) * 4;\n}\n\nconstexpr std::size_t base64URLEncodedSize(std::size_t inSize) {\n  return (inSize / 3) * 4 + inSize % 3 + (inSize % 3 > 0);\n}\n\n// More incorrect usage of '=' padding will be detected during decoding\nconstexpr std::size_t base64PaddingToSubtract(const char* l) {\n  bool isL_1Padding = *(l - 1) == '=';\n  bool isL_2Padding = *(l - 2) == '=';\n\n  return isL_1Padding + (isL_1Padding && isL_2Padding);\n}\n\n// Does not detect errors, all of them will be detected during actual decoding.\nconstexpr std::size_t base64DecodedSize(const char* f, const char* l) {\n  std::size_t n = static_cast<std::size_t>(l - f);\n  if (n < 4) { // correctness is checked when decoding\n    return 0;\n  }\n  std::size_t res = n / 4 * 3;\n  res -= base64PaddingToSubtract(l);\n\n  return res;\n}\n\nconstexpr std::size_t base64URLDecodedSize(const char* f, const char* l) {\n  std::size_t n = static_cast<std::size_t>(l - f);\n\n  // Unfortunatly, we cannot reuse the base64DecodedSize here.\n  if (n < 2) {\n    return 0;\n  }\n\n  std::size_t res = n / 4 * 3;\n\n  if (n % 4 == 0) {\n    // The invalid padding causes a lot of assumptions break.\n    // Introduced an extra check to only subtract padding when it's\n    // in a valid position.\n    res -= base64PaddingToSubtract(l);\n  }\n\n  std::size_t extra = (n % 4) > 0 ? n % 4 - 1 : 0;\n  res += extra;\n\n  return res;\n}\n\nconstexpr std::size_t base64PHPStrictDecodeRequiredOutputSize(\n    const char* f, const char* l) {\n  std::size_t n = static_cast<std::size_t>(l - f);\n\n  // Reserve full input size as output is used for temporary storage\n  return n;\n}\n\nstruct Base64DecodeResult {\n  bool isSuccess = false;\n  char* o;\n};\n\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64Constants.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n\nnamespace folly::detail::base64_detail::constants {\n\n// Scalar --------------------------------------=\n\nconstexpr char kBase64Charset[] =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\nconstexpr char kBase64URLCharset[] =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n// Special value that we can or with any valid value and that\n// way keep track if we had encountered an error or not.\nconstexpr char kDecodeErrorMarker = char(0xff);\n\nconstexpr char base64DecodeRule(char x) {\n  if ('A' <= x && x <= 'Z') {\n    return x - 'A';\n  }\n  if ('a' <= x && x <= 'z') {\n    return x - 'a' + 26;\n  }\n  if ('0' <= x && x <= '9') {\n    return x - '0' + 26 * 2;\n  }\n  if (x == '+') {\n    return 62;\n  }\n  if (x == '/') {\n    return 63;\n  }\n  return kDecodeErrorMarker;\n}\n\nconstexpr char base64URLDecodeRule(char x) {\n  if (x == '-') {\n    return 62;\n  }\n  if (x == '_') {\n    return 63;\n  }\n  return base64DecodeRule(x);\n}\n\nconstexpr std::uint8_t base64PHPStrictDecodeSkipRule(char x) {\n  // This is different from std::isspace and std::isblank in <cctype>\n  if (x == '\\t' || x == '\\n' || x == '\\r' || x == ' ') {\n    return 0;\n  }\n\n  return 1;\n}\n\ntemplate <typename DecodeChar>\nconstexpr auto buildDecodeTable(DecodeChar decodeChar) {\n  std::array<char, 256> res = {};\n  for (std::size_t i = 0; i != res.size(); ++i) {\n    res[i] = decodeChar(static_cast<char>(i));\n  }\n  return res;\n}\n\nconstexpr auto buildBase64PHPStrictDecodeSkipTable() {\n  std::array<std::uint8_t, 256> res = {};\n  for (std::size_t i = 0; i != res.size(); ++i) {\n    res[i] = base64PHPStrictDecodeSkipRule(static_cast<char>(i));\n  }\n  return res;\n}\n\nconstexpr std::array<char, 256> kBase64DecodeTable =\n    buildDecodeTable(base64DecodeRule);\nconstexpr std::array<char, 256> kBase64URLDecodeTable =\n    buildDecodeTable(base64URLDecodeRule);\nconstexpr std::array<std::uint8_t, 256> kBase64PHPStrictDecodeSkipTable =\n    buildBase64PHPStrictDecodeSkipTable();\n\n} // namespace folly::detail::base64_detail::constants\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64HiddenConstants.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <limits>\n#include <folly/detail/base64_detail/Base64Constants.h>\n\nnamespace folly::detail::base64_detail::constants {\n\n// Some constants we have to expose for everyone in order to\n// support constexpr operations.\n// These we can internalize.\n\n// SWAR -----------------------------------------\n\nconstexpr std::uint32_t kSwarDecodeErrorMarker = 0xff'ff'ff'ff;\n\ntemplate <typename DecodeChar>\nconstexpr std::array<std::array<std::uint32_t, 256>, 4> buildSWARDecodeTable(\n    DecodeChar decodeChar) {\n  std::array<std::array<std::uint32_t, 256>, 4> res = {};\n\n  for (std::uint32_t c = std::numeric_limits<std::uint8_t>::min();\n       c != std::numeric_limits<std::uint8_t>::max() + 1;\n       ++c) {\n    char decoded = decodeChar(static_cast<char>(c));\n    if (decoded == kDecodeErrorMarker) {\n      res[0][c] = res[1][c] = res[2][c] = res[3][c] = kSwarDecodeErrorMarker;\n      continue;\n    }\n\n    // What we want? '____'cddd'bbcc'aaab (LE)\n    // clang-format off\n    const std::uint32_t d = static_cast<std::uint32_t>(decoded);\n    res[0][c] = d << 2;                                   // 0000'0000'aaa0\n    res[1][c] = d >> 4 | (d << 12 & 0xff00);              // 0000'bb00'000b\n    res[2][c] = (d << 6 & 0xff00) | (d << 22 & 0xff0000); // c000'00cc'0000\n    res[3][c] = d << 16;                                  // 0ddd'0000'0000\n    // clang-format on\n  }\n\n  return res;\n}\n\nconstexpr auto kBase64SwarDecodeTable = buildSWARDecodeTable(base64DecodeRule);\nconstexpr auto kBase64SwarURLDecodeTable =\n    buildSWARDecodeTable(base64URLDecodeRule);\n\n// Simd -----------------------------------------\n\n// clang-format off\nconstexpr std::array<std::int8_t, 16> kEncodeTable {{\n  'A' - 0,  'a' - 26,\n  '0' - 52, '1' - 53, '2' - 54, '3' - 55, '4' - 56,\n  '5' - 57, '6' - 58, '7' - 59, '8' - 60, '9' - 61,\n  '+' - 62, '/' - 63,\n  '=' - 64, '\\0' - 65\n}};\n// clang-format on\n\nconstexpr auto kEncodeURLTable = [] {\n  auto res = kEncodeTable;\n  res[12] += '-' - '+';\n  res[13] += '_' - '/';\n  return res;\n}();\n\nconstexpr auto kValidHighByLowNibble = [] {\n  auto build = [](auto... nibbles) {\n    std::uint8_t nibblesArr[] = {static_cast<std::uint8_t>(nibbles)...};\n    std::uint8_t res = 0;\n    for (std::uint8_t nibble : nibblesArr) {\n      res |= 1 << nibble;\n    }\n    return res;\n  };\n\n  std::array<std::uint8_t, 16> res{};\n\n  res[0] = build(3, 5, 7);\n\n  // 1 - 9\n  res[1] = build(3, 4, 5, 6, 7);\n  for (int i = 1; i != 0xA; ++i) {\n    res[i] = res[1];\n  }\n\n  res[0xA] = build(4, 5, 6, 7);\n  res[0xB] = build(4, 6);\n  res[0xC] = build(1, 4, 6);\n  res[0xD] = res[0xB];\n  res[0xE] = res[0xB];\n  res[0xF] = build(2, 4, 6);\n\n  return res;\n}();\n\n// clang-format off\nconstexpr std::array<std::int8_t, 16> kOffsetByHighNibbleDecodeTable {{\n  0,          // invalid\n  62 - 0x1C,  // 1: '+'\n  63 - '/',   // 2: '/'\n  52 - '0',   // 3: '0' - '9'\n  0  - 'A',   // 4: 'A' - 'O'\n  0  - 'A',   // 5: 'P' - 'Z'\n  26 - 'a',   // 6: 'a' - 'o'\n  26 - 'a',   // 7: 'p' - 'z'\n  0, 0, 0, 0, // 8, 9, 10, A: invalid\n  0, 0, 0, 0, // B, C, D, E: invalid\n}};\n// clang-format on\n\n} // namespace folly::detail::base64_detail::constants\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64SWAR.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <cstring>\n#include <folly/Portability.h>\n#include <folly/detail/base64_detail/Base64Constants.h>\n#include <folly/detail/base64_detail/Base64HiddenConstants.h>\n#include <folly/detail/base64_detail/Base64SWAR.h>\n#include <folly/detail/base64_detail/Base64Scalar.h>\n\nnamespace folly::detail::base64_detail {\nnamespace {\n\n// Practically same code that is for constexpr version, but these use SWAR\n// tables to avoid having extra constants.\nchar* base64DecodeTailSWAR(\n    const char* f, char* o, std::uint32_t& errorAccumulator) {\n  const auto& table = constants::kBase64SwarDecodeTable[0];\n\n  std::uint32_t aaa = table[atAsU8(f, 0)];\n  std::uint32_t bbb = table[atAsU8(f, 1)];\n\n  *o++ = static_cast<char>((aaa) | (bbb >> 6));\n  errorAccumulator |= aaa | bbb;\n\n  if (f[2] == '=' && f[3] == '=') {\n    if (bbb & 0x3c) {\n      errorAccumulator = constants::kSwarDecodeErrorMarker;\n    }\n    return o;\n  }\n\n  std::uint32_t ccc = table[atAsU8(f, 2)];\n  *o++ = static_cast<char>((bbb << 2) | (ccc >> 4));\n  errorAccumulator |= ccc;\n\n  if (f[3] == '=') {\n    if (ccc & 0xc) {\n      errorAccumulator = constants::kSwarDecodeErrorMarker;\n    }\n    return o;\n  }\n\n  std::uint32_t ddd = table[atAsU8(f, 3)];\n  *o++ = static_cast<char>((ccc << 4) | ddd >> 2);\n  errorAccumulator |= ddd;\n\n  return o;\n}\n\nchar* base64URLDecodeTailSWAR(\n    const char* f, const char* l, char* o, std::uint32_t& errorAccumulator) {\n  base64URLDecodeStripValidPadding(f, l);\n\n  const auto& table = constants::kBase64SwarURLDecodeTable[0];\n\n  std::uint32_t aaa = table[atAsU8(f, 0)];\n  std::uint32_t bbb = table[atAsU8(f, 1)];\n\n  *o++ = static_cast<char>((aaa) | (bbb >> 6));\n  errorAccumulator |= aaa | bbb;\n\n  f += 2;\n  if (f == l) {\n    return o;\n  }\n\n  std::uint32_t ccc = table[atAsU8(f, 0)];\n  *o++ = static_cast<char>((bbb << 2) | (ccc >> 4));\n  errorAccumulator |= ccc;\n  ++f;\n\n  if (f == l) {\n    return o;\n  }\n\n  std::uint32_t ddd = table[atAsU8(f, 0)];\n  *o++ = static_cast<char>((ccc << 4) | ddd >> 2);\n  errorAccumulator |= ddd;\n\n  return o;\n}\n\ntemplate <bool isURL>\nconstexpr auto kBase64SwarDecodeTable = isURL\n    ? constants::kBase64SwarURLDecodeTable\n    : constants::kBase64SwarDecodeTable;\n\ntemplate <bool isURL>\nstd::uint32_t base64DecodeSWARMainLoop(\n    const char*& f, const char* l, char*& o) noexcept {\n  std::uint32_t errorAccumulator = 0;\n\n  while (l - f > 4) {\n    std::uint32_t r = //\n        kBase64SwarDecodeTable<isURL>[0][atAsU8(f, 0)] |\n        kBase64SwarDecodeTable<isURL>[1][atAsU8(f, 1)] |\n        kBase64SwarDecodeTable<isURL>[2][atAsU8(f, 2)] |\n        kBase64SwarDecodeTable<isURL>[3][atAsU8(f, 3)];\n\n    errorAccumulator |= r;\n    std::memcpy(o, &r, sizeof(r));\n\n    f += 4;\n    o += 3;\n  }\n\n  return errorAccumulator;\n}\n\n} // namespace\n\nBase64DecodeResult base64DecodeSWAR(\n    const char* f, const char* l, char* o) noexcept {\n  if (f == l) {\n    return {true, o};\n  }\n  if ((l - f) % 4) {\n    return {false, o};\n  }\n\n  std::uint32_t errorAccumulator =\n      base64DecodeSWARMainLoop</*isURL*/ false>(f, l, o);\n\n  o = base64DecodeTailSWAR(f, o, errorAccumulator);\n\n  return {errorAccumulator != constants::kSwarDecodeErrorMarker, o};\n}\n\nBase64DecodeResult base64URLDecodeSWAR(\n    const char* f, const char* l, char* o) noexcept {\n  if (f == l) {\n    return {true, o};\n  }\n\n  if ((l - f) % 4 == 1) {\n    return {false, o};\n  }\n\n  std::uint32_t errorAccumulator =\n      base64DecodeSWARMainLoop</*isURL*/ true>(f, l, o);\n\n  o = base64URLDecodeTailSWAR(f, l, o, errorAccumulator);\n  return {errorAccumulator != constants::kSwarDecodeErrorMarker, o};\n}\n\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64SWAR.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/detail/base64_detail/Base64Common.h>\n\nnamespace folly::detail::base64_detail {\n\n// NOTE: maybe this makes sense to inline in SIMD implementaiton\n//       but not likely.\n\nBase64DecodeResult base64DecodeSWAR(\n    const char* f, const char* l, char* o) noexcept;\n\nBase64DecodeResult base64URLDecodeSWAR(\n    const char* f, const char* l, char* o) noexcept;\n\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64Scalar.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <folly/Utility.h>\n#include <folly/detail/base64_detail/Base64Common.h>\n#include <folly/detail/base64_detail/Base64Constants.h>\n#include <folly/memory/UninitializedMemoryHacks.h>\n#include <folly/portability/Constexpr.h>\n\nnamespace folly::detail::base64_detail {\n\nconstexpr std::uint8_t atAsU8(const char* f, int offset) {\n  return static_cast<std::uint8_t>(f[offset]);\n}\n\nconstexpr std::array<std::uint8_t, 3> base64DecodePack4To3(\n    std::uint8_t aaa, std::uint8_t bbb, std::uint8_t ccc, std::uint8_t ddd) {\n  std::uint8_t aaab = static_cast<std::uint8_t>((aaa << 2) | (bbb >> 4));\n  std::uint8_t bbcc = static_cast<std::uint8_t>((bbb << 4) | (ccc >> 2));\n  std::uint8_t cddd = static_cast<std::uint8_t>((ccc << 6) | ddd);\n\n  return {{aaab, bbcc, cddd}};\n}\n\ntemplate <bool isURL>\nstruct Base64ScalarImpl {\n  static constexpr const char* kCharset =\n      isURL ? constants::kBase64URLCharset : constants::kBase64Charset;\n\n  static constexpr const char* kDecodeTable = isURL\n      ? constants::kBase64URLDecodeTable.data()\n      : constants::kBase64DecodeTable.data();\n\n  // 0, 1 or 2 bytes\n  static constexpr char* encodeTail(const char* f, const char* l, char* o) {\n    if (f == l) {\n      return o;\n    }\n\n    std::uint8_t aaab = f[0];\n    std::uint8_t aaa = aaab >> 2;\n    *o++ = kCharset[aaa];\n\n    // duplicating some tail handling to try to do less jumps\n    if (l - f == 1) {\n      std::uint8_t b00 = aaab << 4 & 0x3f;\n      *o++ = kCharset[b00];\n      if constexpr (!isURL) {\n        *o++ = '=';\n        *o++ = '=';\n      }\n      return o;\n    }\n\n    // l - f == 2\n    std::uint8_t bbcc = f[1];\n    std::uint8_t bbb = ((aaab << 4) | (bbcc >> 4)) & 0x3f;\n    std::uint8_t cc0 = (bbcc << 2) & 0x3f;\n    *o++ = kCharset[bbb];\n    *o++ = kCharset[cc0];\n    if constexpr (!isURL) {\n      *o++ = '=';\n    }\n    return o;\n  }\n\n  static constexpr char* encode(const char* f, const char* l, char* o) {\n    while ((l - f) >= 3) {\n      std::uint8_t aaab = f[0];\n      std::uint8_t bbcc = f[1];\n      std::uint8_t cddd = f[2];\n\n      std::uint8_t aaa = aaab >> 2;\n      std::uint8_t bbb = ((aaab << 4) | (bbcc >> 4)) & 0x3f;\n      std::uint8_t ccc = ((bbcc << 2) | (cddd >> 6)) & 0x3f;\n      std::uint8_t ddd = cddd & 0x3f;\n\n      o[0] = kCharset[aaa];\n      o[1] = kCharset[bbb];\n      o[2] = kCharset[ccc];\n      o[3] = kCharset[ddd];\n\n      f += 3;\n      o += 4;\n    }\n\n    return encodeTail(f, l, o);\n  }\n\n  static constexpr std::uint8_t decodeMainLoop(\n      const char*& f, std::size_t fullSteps, char*& o) {\n    std::uint8_t errorAccumulator = 0;\n    while (fullSteps--) {\n      std::uint8_t aaa = kDecodeTable[atAsU8(f, 0)];\n      std::uint8_t bbb = kDecodeTable[atAsU8(f, 1)];\n      std::uint8_t ccc = kDecodeTable[atAsU8(f, 2)];\n      std::uint8_t ddd = kDecodeTable[atAsU8(f, 3)];\n\n      errorAccumulator |= aaa | bbb | ccc | ddd;\n\n      auto packed = base64DecodePack4To3(aaa, bbb, ccc, ddd);\n\n      o[0] = static_cast<char>(packed[0]);\n      o[1] = static_cast<char>(packed[1]);\n      o[2] = static_cast<char>(packed[2]);\n\n      f += 4;\n      o += 3;\n    }\n    return errorAccumulator;\n  }\n};\n\nconstexpr char* base64EncodeScalar(\n    const char* f, const char* l, char* o) noexcept {\n  return Base64ScalarImpl</*isURL*/ false>::encode(f, l, o);\n}\n\nconstexpr char* base64URLEncodeScalar(\n    const char* f, const char* l, char* o) noexcept {\n  return Base64ScalarImpl</*isURL*/ true>::encode(f, l, o);\n}\n\nconstexpr char* base64DecodeTailScalar(\n    const char* f, char* o, std::uint8_t& errorAccumulator) {\n  std::uint8_t aaa = constants::kBase64DecodeTable[atAsU8(f, 0)];\n  std::uint8_t bbb = constants::kBase64DecodeTable[atAsU8(f, 1)];\n\n  *o++ = static_cast<std::uint8_t>((aaa << 2) | (bbb >> 4));\n  errorAccumulator |= aaa | bbb;\n\n  if (f[2] == '=' && f[3] == '=') {\n    if (bbb & 0xf) {\n      errorAccumulator = constants::kDecodeErrorMarker;\n    }\n    return o;\n  }\n\n  std::uint8_t ccc = constants::kBase64DecodeTable[atAsU8(f, 2)];\n  *o++ = static_cast<char>((bbb << 4) | (ccc >> 2));\n  errorAccumulator |= ccc;\n\n  if (f[3] == '=') {\n    if (ccc & 0x3) {\n      errorAccumulator = constants::kDecodeErrorMarker;\n    }\n    return o;\n  }\n\n  std::uint8_t ddd = constants::kBase64DecodeTable[atAsU8(f, 3)];\n  *o++ = static_cast<char>((ccc << 6) | ddd);\n  errorAccumulator |= ddd;\n\n  return o;\n}\n\nconstexpr Base64DecodeResult base64DecodeScalar(\n    const char* f, const char* l, char* o) noexcept {\n  if (f == l) {\n    return {true, o};\n  }\n  if ((l - f) % 4) {\n    return {false, o};\n  }\n\n  std::size_t fullSteps = (l - f) / 4 - 1; // last step may contain padding\n                                           // and needs special care.\n\n  std::uint8_t errorAccumulator =\n      Base64ScalarImpl</*isURL*/ false>::decodeMainLoop(f, fullSteps, o);\n\n  o = base64DecodeTailScalar(f, o, errorAccumulator);\n\n  return {\n      errorAccumulator !=\n          static_cast<std::uint8_t>(constants::kDecodeErrorMarker),\n      o};\n}\n\nconstexpr void base64URLDecodeStripValidPadding(const char* f, const char*& l) {\n  // Valid paddings:\n  // 00==, 000=\n  // Invalid paddings:\n  // 00=, =, ==, 00=0\n  if ((l - f) != 4) {\n    return;\n  }\n  l -= *(l - 1) == '=';\n  l -= *(l - 1) == '=';\n}\n\nconstexpr char* base64URLDecodeScalarLast4Bytes(\n    const char* f, const char* l, char* o, std::uint8_t& errorAccumulator) {\n  base64URLDecodeStripValidPadding(f, l);\n\n  std::uint8_t aaa = constants::kBase64URLDecodeTable[atAsU8(f, 0)];\n  std::uint8_t bbb = constants::kBase64URLDecodeTable[atAsU8(f, 1)];\n\n  *o++ = static_cast<std::uint8_t>((aaa << 2) | (bbb >> 4));\n  errorAccumulator |= aaa | bbb; // This will detect incorrect padding as well\n\n  f += 2;\n  if (f == l) {\n    return o;\n  }\n\n  std::uint8_t ccc = constants::kBase64URLDecodeTable[atAsU8(f, 0)];\n  *o++ = static_cast<char>((bbb << 4) | (ccc >> 2));\n  errorAccumulator |= ccc;\n  ++f;\n\n  if (f == l) {\n    return o;\n  }\n\n  std::uint8_t ddd = constants::kBase64URLDecodeTable[atAsU8(f, 0)];\n  *o++ = static_cast<char>((ccc << 6) | ddd);\n  errorAccumulator |= ddd;\n\n  return o;\n}\n\nconstexpr Base64DecodeResult base64URLDecodeScalar(\n    const char* f, const char* l, char* o) noexcept {\n  if (f == l) {\n    return {true, o};\n  }\n  std::size_t rem = (l - f) % 4;\n\n  std::size_t fullSteps =\n      (l - f) / 4 - (rem == 0); // last 4 bytes may contain padding\n                                // and need special care.\n\n  std::uint8_t errorAccumulator =\n      Base64ScalarImpl</*isURL*/ true>::decodeMainLoop(f, fullSteps, o);\n\n  if (l - f < 2) {\n    return {false, o};\n  }\n  o = base64URLDecodeScalarLast4Bytes(f, l, o, errorAccumulator);\n\n  return {\n      errorAccumulator !=\n          static_cast<std::uint8_t>(constants::kDecodeErrorMarker),\n      o};\n}\n\nconstexpr inline const char* base64PHPStrictRemoveWhitespacesScalar(\n    const char* f, const char* l, char* o) noexcept {\n  constexpr auto& skips =\n      detail::base64_detail::constants::kBase64PHPStrictDecodeSkipTable;\n  std::size_t pos = 0;\n\n  while (f != l) {\n    auto v = *f++;\n    o[pos] = v;\n    pos += skips[to_unsigned(v)];\n  }\n\n  return o + pos;\n}\n\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64Simd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <cstring>\n#include <folly/CPortability.h>\n#include <folly/detail/base64_detail/Base64Common.h>\n#include <folly/detail/base64_detail/Base64Constants.h>\n#include <folly/detail/base64_detail/Base64HiddenConstants.h>\n#include <folly/detail/base64_detail/Base64SWAR.h>\n#include <folly/detail/base64_detail/Base64Scalar.h>\n\nnamespace folly::detail::base64_detail {\n\n// The funcitons are marked ALWAYS INLINE because there are platform specific\n// single instantiations actually inlined in the platform call\n\ntemplate <typename PlatformDelegate, bool isURL>\nFOLLY_ALWAYS_INLINE char* base64SimdEncodeImpl(\n    const char* f, const char* l, char* o) noexcept {\n  static constexpr std::size_t kRegisterSize = PlatformDelegate::kRegisterSize;\n  static constexpr std::size_t kInputAdvance = kRegisterSize * 3 / 4;\n  static constexpr auto* kEncodeTable = isURL\n      ? constants::kEncodeURLTable.data()\n      : constants::kEncodeTable.data();\n\n  while (static_cast<std::size_t>(l - f) >= kRegisterSize) {\n    auto loaded = PlatformDelegate::loadu(f);\n    auto idxs = PlatformDelegate::encodeToIndexes(loaded);\n    auto encoded = PlatformDelegate::lookupByIndex(idxs, kEncodeTable);\n    PlatformDelegate::storeu(o, encoded);\n    f += kInputAdvance;\n    o += kRegisterSize;\n  }\n\n  if constexpr (isURL) {\n    return base64URLEncodeScalar(f, l, o);\n  } else {\n    return base64EncodeScalar(f, l, o);\n  }\n}\n\ntemplate <typename PlatformDelegate>\nFOLLY_ALWAYS_INLINE char* base64SimdEncode(\n    const char* f, const char* l, char* o) noexcept {\n  return base64SimdEncodeImpl<PlatformDelegate, /*isURL*/ false>(f, l, o);\n}\n\ntemplate <typename PlatformDelegate>\nFOLLY_ALWAYS_INLINE char* base64URLSimdEncode(\n    const char* f, const char* l, char* o) noexcept {\n  return base64SimdEncodeImpl<PlatformDelegate, /*isURL*/ true>(f, l, o);\n}\n\ntemplate <typename PlatformDelegate>\nFOLLY_ALWAYS_INLINE Base64DecodeResult\nbase64SimdDecode(const char* f, const char* l, char* o) noexcept {\n  static constexpr std::size_t kRegisterSize = PlatformDelegate::kRegisterSize;\n  static constexpr std::size_t kOutputAdvance = kRegisterSize * 3 / 4;\n\n  static_assert(kRegisterSize >= 16);\n  static_assert(kRegisterSize % 4 == 0);\n  static_assert(kOutputAdvance * 2 > kRegisterSize);\n\n  // See proof why this is good enough in the readme.\n  static constexpr std::size_t kSimdLimit = kRegisterSize * 3 / 2;\n\n  auto errorAccumulator = PlatformDelegate::initError();\n  while (static_cast<std::size_t>(l - f) >= kSimdLimit) {\n    auto reg = PlatformDelegate::loadu(f);\n    auto idxs = PlatformDelegate::decodeToIndex(reg, errorAccumulator);\n    auto cvtd = PlatformDelegate::packIndexesToBytes(idxs);\n    PlatformDelegate::storeu(o, cvtd);\n\n    f += kRegisterSize;\n    o += kOutputAdvance;\n  }\n\n  if (PlatformDelegate::hasErrors(errorAccumulator)) {\n    return {false, o};\n  }\n\n  return base64DecodeSWAR(f, l, o);\n}\n\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64_SSE4_2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/base64_detail/Base64_SSE4_2.h>\n\n#include <folly/Portability.h>\n#include <folly/detail/base64_detail/Base64Simd.h>\n#include <folly/detail/base64_detail/Base64_SSE4_2_Platform.h>\n\n#if FOLLY_SSE_PREREQ(4, 2)\n\nnamespace folly::detail::base64_detail {\n\nchar* base64Encode_SSE4_2(const char* f, const char* l, char* o) noexcept {\n  return base64SimdEncode<Base64_SSE4_2_Platform>(f, l, o);\n}\n\nchar* base64URLEncode_SSE4_2(const char* f, const char* l, char* o) noexcept {\n  return base64URLSimdEncode<Base64_SSE4_2_Platform>(f, l, o);\n}\n\nBase64DecodeResult base64Decode_SSE4_2(\n    const char* f, const char* l, char* o) noexcept {\n  return base64SimdDecode<Base64_SSE4_2_Platform>(f, l, o);\n}\n\n} // namespace folly::detail::base64_detail\n\n#endif // defined(__SSE4_2__)\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64_SSE4_2.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <folly/Portability.h>\n#include <folly/detail/base64_detail/Base64Common.h>\n\n#if FOLLY_SSE_PREREQ(4, 2)\nnamespace folly::detail::base64_detail {\n\nchar* base64Encode_SSE4_2(const char* f, const char* l, char* o) noexcept;\nchar* base64URLEncode_SSE4_2(const char* f, const char* l, char* o) noexcept;\n\nBase64DecodeResult base64Decode_SSE4_2(\n    const char* f, const char* l, char* o) noexcept;\n\n} // namespace folly::detail::base64_detail\n#endif\n"
  },
  {
    "path": "folly/detail/base64_detail/Base64_SSE4_2_Platform.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <folly/Portability.h>\n#include <folly/detail/base64_detail/Base64HiddenConstants.h>\n\n#if FOLLY_SSE_PREREQ(4, 2)\n#include <immintrin.h>\n\nnamespace folly::detail::base64_detail {\n\n/*\n *  NOTE: PLEASE SEE README FOR A DETAILED EXPLANATIONS\n *        VIRTUALLY IMPOSSIBLE TO DECIPHER OTHERWISE.\n */\n\nstruct Base64_SSE4_2_Platform {\n  using reg_t = __m128i;\n  static constexpr std::size_t kRegisterSize = 16;\n\n  // Encode ------------------------------\n\n  static reg_t encodeToIndexesPshuvbMask() {\n    // PONM,LKJI,HGFE,DCBA => KLJK'GIGH,EFDE,BCAB\n    // clang-format off\n      return _mm_set_epi8 (\n        10, 11, 9,  10, // KLJK\n        7,   8,  6,  7, // GIGH\n        4,   5,  3,  4, // EFDE\n        1,   2,  0,  1  // BCAB\n      );\n    // clang-format on\n  }\n\n  static reg_t encodeToIndexes(reg_t in) {\n    in = _mm_shuffle_epi8(in, encodeToIndexesPshuvbMask());\n\n    const reg_t t0 = _mm_and_si128(in, _mm_set1_epi32(0x0fc0fc00));\n    const reg_t t1 = _mm_mulhi_epu16(t0, _mm_set1_epi32(0x04000040));\n    const reg_t t2 = _mm_and_si128(in, _mm_set1_epi32(0x003f03f0));\n    const reg_t t3 = _mm_mullo_epi16(t2, _mm_set1_epi32(0x01000010));\n\n    return _mm_or_si128(t1, t3);\n  }\n\n  static reg_t lookupByIndex(reg_t in, std::int8_t const* offsetTablePtr) {\n    const reg_t offsetTable =\n        _mm_loadu_si128(reinterpret_cast<const __m128i*>(offsetTablePtr));\n\n    // 0-51 become 0, 52 and bigger map to 1 and bigger\n    const reg_t reduceTooMuch = _mm_subs_epu8(in, _mm_set1_epi8(51));\n\n    // 0 when should map to A-Z, otherwise -1.\n    const reg_t biggerThan25 = _mm_cmpgt_epi8(in, _mm_set1_epi8(25));\n\n    const reg_t offsetLookup = _mm_sub_epi8(reduceTooMuch, biggerThan25);\n\n    return _mm_add_epi8(in, _mm_shuffle_epi8(offsetTable, offsetLookup));\n  }\n\n  // Decode ------------------------------------------------------------\n\n  // > 128 changes but stays > 128\n  // > '+' stays the same\n  // <= '+' becomes closer to 0 so that higher nibble in '+' is 1.\n  // Using 0x0f as offset because we'll need it in a different place too.\n  static reg_t separatePlusAndSlash(reg_t reg) {\n    const reg_t leThanPlus = _mm_cmplt_epi8(reg, _mm_set1_epi8('+' + 1));\n    const reg_t plusAndBelowOffset =\n        _mm_and_si128(leThanPlus, _mm_set1_epi8(0x0f));\n    return _mm_subs_epi8(reg, plusAndBelowOffset);\n  }\n\n  static reg_t initError() { return _mm_set1_epi8(0xff); }\n\n  static bool hasErrors(reg_t errorAccumulator) {\n    return _mm_movemask_epi8(\n        _mm_cmpeq_epi8(errorAccumulator, _mm_setzero_si128()));\n  }\n\n  static reg_t decodeErrorDetection(reg_t reg, reg_t higherNibbles) {\n    // clang-format off\n    const std::int8_t s1_7 = static_cast<std::int8_t>(1 << 7);\n    const reg_t pows2 = _mm_set_epi8(\n        0, 0, 0, 0,\n        0, 0, 0, 0,\n        s1_7,   1 << 6, 1 << 5, 1 << 4,\n        1 << 3, 1 << 2, 1 << 1, 1 << 0);\n    // clang-format on\n\n    reg_t higherNibbleBit = _mm_shuffle_epi8(pows2, higherNibbles);\n\n    // Here we should lookup by lower nibbles.\n    // However, this is either equivalent or, if the input byte is\n    // negative the result is 0, which is OK since no negative input byte\n    // is valid.\n    reg_t legalHigherNibblesBits =\n        _mm_shuffle_epi8(loadu(constants::kValidHighByLowNibble.data()), reg);\n\n    return _mm_and_si128(higherNibbleBit, legalHigherNibblesBits);\n  }\n\n  static reg_t decodeComputeIndexes(reg_t reg, reg_t higherNibbles) {\n    reg_t offset = _mm_shuffle_epi8(\n        loadu(constants::kOffsetByHighNibbleDecodeTable.data()), higherNibbles);\n    return _mm_add_epi8(offset, reg);\n  }\n\n  static reg_t decodeToIndex(reg_t reg, reg_t& errorAccumulator) {\n    reg = separatePlusAndSlash(reg);\n\n    reg_t higherNibbles =\n        _mm_and_si128(_mm_srli_epi32(reg, 4), _mm_set1_epi8(0x0f));\n\n    errorAccumulator = _mm_min_epu8(\n        decodeErrorDetection(reg, higherNibbles), errorAccumulator);\n\n    return decodeComputeIndexes(reg, higherNibbles);\n  }\n\n  static reg_t packIndexesToBytes(reg_t reg) {\n    // ccc << 6 + ddd  aaa << 6 + bbb  (<< 6 == * 0x40)\n    reg_t cccddd_aaabbb = _mm_maddubs_epi16(reg, _mm_set1_epi16(0x01'40));\n\n    // Combine the whole epi32 aaabbb << 12 + cccddd (<< 12 == 0x1000)\n    reg_t aaabbbcccddd =\n        _mm_madd_epi16(cccddd_aaabbb, _mm_set1_epi32(0x1'1000));\n\n    // clang-format off\n    return _mm_shuffle_epi8(aaabbbcccddd, _mm_set_epi8(\n      -1, -1, -1, -1, // zero out the last 4 bytes\n      12, 13, 14,\n      8,  9,  10,\n      4,   5,  6,\n      0,   1,  2\n    ));\n    // clang-format on\n  }\n\n  static reg_t loadu(const void* ptr) {\n    return _mm_loadu_si128(reinterpret_cast<const reg_t*>(ptr));\n  }\n\n  static void storeu(void* ptr, reg_t reg) {\n    _mm_storeu_si128(reinterpret_cast<reg_t*>(ptr), reg);\n  }\n};\n\n} // namespace folly::detail::base64_detail\n\n#endif // FOLLY_SSE_PREREQ(4, 2)\n"
  },
  {
    "path": "folly/detail/base64_detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# NOTE: This file is manually maintained, not auto-generated.\n# Update this file when the corresponding BUCK file changes.\n\nfolly_add_library(\n  NAME base64_api\n  SRCS\n    Base64Api.cpp\n  HEADERS\n    Base64Api.h\n  DEPS\n    folly_cpu_id\n    folly_detail_base64_detail_base64_sse4_2\n    folly_detail_base64_detail_base64_swar\n  EXPORTED_DEPS\n    folly_detail_base64_detail_base64_common\n    folly_detail_base64_detail_base64_scalar\n    folly_portability\n    folly_portability_constexpr\n)\n\nfolly_add_library(\n  NAME base64_common\n  HEADERS\n    Base64Common.h\n  EXPORTED_DEPS\n    folly_detail_base64_detail_base64_constants\n    folly_utility\n)\n\nfolly_add_library(\n  NAME base64_constants\n  HEADERS\n    Base64Constants.h\n)\n\nfolly_add_library(\n  NAME base64_hidden_constants\n  HEADERS\n    Base64HiddenConstants.h\n  EXPORTED_DEPS\n    folly_detail_base64_detail_base64_constants\n)\n\nfolly_add_library(\n  NAME base64_scalar\n  HEADERS\n    Base64Scalar.h\n  EXPORTED_DEPS\n    folly_detail_base64_detail_base64_common\n    folly_detail_base64_detail_base64_constants\n    folly_memory_uninitialized_memory_hacks\n    folly_portability_constexpr\n    folly_utility\n)\n\nfolly_add_library(\n  NAME base64_simd\n  HEADERS\n    Base64Simd.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_detail_base64_detail_base64_common\n    folly_detail_base64_detail_base64_constants\n    folly_detail_base64_detail_base64_hidden_constants\n    folly_detail_base64_detail_base64_scalar\n    folly_detail_base64_detail_base64_swar\n)\n\nfolly_add_library(\n  NAME base64_sse4_2_platform\n  HEADERS\n    Base64_SSE4_2_Platform.h\n  EXPORTED_DEPS\n    folly_detail_base64_detail_base64_hidden_constants\n    folly_portability\n)\n\nfolly_add_library(\n  NAME base64_sse4_2\n  SRCS\n    Base64_SSE4_2.cpp\n  HEADERS\n    Base64_SSE4_2.h\n  DEPS\n    folly_detail_base64_detail_base64_simd\n    folly_detail_base64_detail_base64_sse4_2_platform\n  EXPORTED_DEPS\n    folly_detail_base64_detail_base64_common\n    folly_portability\n)\n\n# Apply -msse4.2 flag on x86 (MSVC doesn't need it)\nif (IS_X86_64_ARCH AND NOT CMAKE_CXX_COMPILER_ID STREQUAL \"MSVC\")\n  target_compile_options(folly_detail_base64_detail_base64_sse4_2_obj\n    PRIVATE -msse4.2)\nendif()\n\nfolly_add_library(\n  NAME base64_swar\n  SRCS\n    Base64SWAR.cpp\n  HEADERS\n    Base64SWAR.h\n  DEPS\n    folly_detail_base64_detail_base64_constants\n    folly_detail_base64_detail_base64_hidden_constants\n    folly_detail_base64_detail_base64_scalar\n    folly_portability\n  EXPORTED_DEPS\n    folly_detail_base64_detail_base64_common\n)\n"
  },
  {
    "path": "folly/detail/base64_detail/README.md",
    "content": "*This file contains explanation of how SIMD implementation of Base64 functions*\n\nOur solution is based on 0x80 blog: [encoding](http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html), [decoding](http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html).\n\nThis is a different version of the same explanation + we have some differences.\n\n*NOTE: We are using 1 digit for two bits when writing data*\n\n# Understanding Base64 encoding.\n\n*If you find it easier, [Wikipedia](https://en.wikipedia.org/wiki/Base64).*\n\nThe goal of base64 is to encode arbitrary byte data as text that can be passed around.\nIt works as follows:\n* we select a 64 character alphabet.\n* we insert two 0 bits after each 6 bits. 2^6 is 64.\n* because we now only need to encode 64 values, our alphabet is sufficient.\n\n# Encoding\n\nSo, we have 2 steps needed to do the conversion: 1. get indexes 0-64, 2. lookup the corresponding char.\nThere is also a little bit of tail handling, see the appropriate section.\n\nIf one letter represents two bits, it looks smth like this (remember - our machines are little endian):\n\n```\n`cddd`bbcc`aaab => 0ddd`0ccc`0bbb`0aaa\n```\n\nAfter this we just have to convert the bytes into the selected alphabet.\n\nThis means we have two steps to the algorithm: encodeToIndexes and lookupByIndex.\n\n## encodeToIndexes\n\n*NOTE: assume 16 byte registers.*\n\nWe will only be converting 12 bytes to output 16 bytes.\nThe last 4 loaded bytes are ignored.\n\n1. We need to shuffle bytes in a way that each dword contains all the bits to construct output for that dword. Specifically:\n\n```\nPONM,LKJI,HGFE,DCBA => KLJK,GIGH,EFDE,BCAB\n```\n\n> STEP BY STEP:\n> * We had bytes in memory ABCD.\n> * Loaded on a LE machine they become DCBA.\n> * The first output dword will only fit ABC,\n    D goes into the second dword.\n> * We will also reshuffle CBA as BCAB because this will make\n    further operations simpler.\n\nAfter this we will mutate each dword (letter+digit indicate 2 bits):\n\n```\nshuffled b2b3c1c2'c3d1d2d3'a1a2a3b1'b2b3c1c2\nexpected 00d1d2d3'00c1c2c3'00b1b2b3'00a1a2a3\n```\n\nAt the moment of writing this we only have an SSE4.2 version. We can do it fairly straightforwardly with shifts and blends. However the 0x80 blogs suggests that using a tricky multiplication scheme is superior, and that's what we do.\n\nFor neon there are by element shift instructions which would help.\nOn avx2 there are `srlv` family of instructions that can come in handy as well.\nThe 0x80 blog also talks about a BMI implementation (pdep/pext instructions). They had issues on AMD but apparently not on the AMDs in Meta's fleet, so in the future we can consider it as a possibility as well.\n\n## lookupByIndex\n\nThe idea is to use table lookup/shuffle instructions (`pshuvb` on sse4.2). They allow you to shuffle a register by index (`0..16`).\n\nWe have values `0..64`. However, we can utilize that the output values are not random. The are split into a few contiguous groups. We don't have to lookup the complete encoding, we just need to lookup the offset to add.\n\n\n| Index | Maps to | Add to encode |\n| ----- | --------| ------------- |\n| 0-25  | 'A'-'Z' | 'A' - 0       |\n| 26-51 | 'a'-'z' | 'a' - 26      |\n| 52-61 | '0'-'9' | '0' - 52      |\n| 62    | '+'     | '+' - 62      |\n| 63    | '/'     | '/' - 63      |\n\n\n*NOTE: the URL version of Base64 encoding uses -_ instead of +/*.\n\nSince we have 16 values to map to, we are not going to bother with consolidating 52-61 to a single base and instead will them up individually.\n\nThis is our final lookup table:\n\n```\n0: 'A' - 0\n1: 'a' - 26\n2: '0' - 52,\n3: '1' - 53\n...\n11: '9' - 61\n12: '+' - 62\n13: '/' - 63\n14: -\n15: -\n```\n\nWe even have two extra indexes `14` and `15` that are unused.\n\n## Tail handling\n\nWhat if we have less than register size to load?\nWhat if we have less than 3 elements to encode?\n\nBase64 for incomplete 3 bytes works as if there were\nextra 0s at the end, except the garbage elements are replaced\nwith either a padding character or just removed.\n\nExample:\n\n|input bytes (be) | Basic  | Url  |\n|-----------------|--------|------|\n| (1)             |  AQ==  | AQ   |\n| (1, 0)          |  AQA=  | AQA  |\n| (1, 0, 0)       |  AQAA  | AQAA |\n\nAfter measuring different options, using scalar loop peeling\nproved to be the fastest.\n\n# Decoding\n\nOverall the decoding process is similar to inverse of the encoding: 1. from chars get numbers `0..63` 2. pack them\ninto bytes.\n\nHowever, unlike encoding, not all the sequences of bytes are valid.\nWe have to decide which ones to accept and how to handle errors.\n\n*NOTE: error detection is fairly expensive, if in the\nfuture there is a need for a decode that can sometimes\nswallow invalid values, it can be done faster*.\n\nDecisions:\n`base64Decode` requires `=` padding and accepts only [`+`, `/`] as 62/63 character.\nIf there are extra bits on the end, those are not allowed: iZ== is not a valid input\nbecause no input sequence will produce it.\n`base64URLDecode`: allows everything encoded with plain base64 and base64URL.\nIt will successfully decode some combinations that are not either. The extra bits on\nthe end are currently allowed: iZ== will decode successfully (this decision is not\nmotivated by anything).\n\n*NOTE:* details on `baseURLDecode` rules:\n* Padding bytes follow regular base64: if they are present they are allowed to be only in positions that\n  regular base64 allows them to be in: 4th or 3rd and 4th in the 4 byte chunk. \"aa==\", \"aaa=\" - OK, \"aa=\", \"==\" - not OK. This decision follows the previous most common implementation of base64 in the codebase.\n* Both ['+', '/'] and ['-', '_'] are allowed in any combinations. \"+-\" is legal for example.\n\n\n*NOTE: this follows the previous design and not necessarily how other places define\nbase64URLDecode. The difference comes to decoding invalid sequence of padding characters.\nExample: \"ba=\" we consider invalid while some other implementations might just strip '='. We\nsuspect it won't be an issue*.\n\nThis behaviour matches what the previous implementation did.\n\nWe only have a SIMD version of this algorithm for `base64Decode` because the URL decode has\nvery little use (so far at least).\n\nThe SIMD algorithm has 2 steps:\n- `decodeCharToIndex`: convert to numbers from `0..63`\n- `packIndexesToBytes`: packs `0..63` into the originally encoded bytes.\n\nIf we encounter an error, we are not going to stop processing data. We are just going to mark it and then check at the end.\n\n## decodeCharToIndex\n\nConversions we want\n\n| Char      | Char's Hex code | Res     | Res Hex     |\n|-----------|-----------------|---------|-------------|\n| '+'       |   0x2B          | 62      | 0x3E        |\n| '/'       |   0x2F          | 63      | 0x3F        |\n| '0' - '9' |   0x30 - 0x39   | 52 - 61 | 0x34 - 0x3D |\n| 'A' - 'Z' |   0x41 - 0x5A   | 0  - 25 | 0x00 - 0x19 |\n| 'a' - 'z' |   0x61 - 0x7A   | 26 - 51 | 0x1A - 0x33 |\n\n### Idea\n\nWe have unique higher 4 bits (nibble) per consecutive group,\nexcept for '+' and '/'.\nWe can find a unique offset with pshuvb by it.\n\n### Deal with '+' and '/' nibbles\n\nWe need to move '+' and '/' into their own nibbles.\nIt's important that all previously invalid chars stay invalid.\n\nThe best way I see to do it is to subtract 0x0f from everything less than or equal to +.\nThe reason to use 0x0f is that it's a constant we will need anyways ot get 4 higher nibbles.\n\nHowever on intel there is no unsigned comparison, so all the negative values will be considered as <= '+'. To deal with it, we can use a signed comparison but subtract with saturation.\n\n * <= '+' gives -1 where '+'\n * and 0x0f\n * subs\n\nEverything bigger than '+' will stay the same.\nEverything smaller than '+' is invalid and will become even smaller.\nIt will saturate to -127 which is still invalid, so we are OK.\n\n *NOTE: I was also thinking if this is possible to do with just one subtraction and no mask. Unfortunately we also need to keep a separate nibble for '0' and '/' and '0' == '/' + 1, so that subtraction will guarantee put '/' and '0' on the same higher nibble.*\n\n *NOTE: If we drop the requirement on invalid elements, again - this is much easier*.\n\nNew conversion table:\n\n| Char      | New Hex code  | Res     | Res Hex     |\n|-----------|---------------|---------|-------------|\n| '+'       |   0x1C        | 62      | 0x3E        |\n| '/'       |   0x2F        | 63      | 0x3F        |\n| '0' - '9' |   0x30 - 0x39 | 52 - 61 | 0x34 - 0x3D |\n| 'A' - 'Z' |   0x41 - 0x5A | 0  - 25 | 0x00 - 0x19 |\n| 'a' - 'z' |   0x61 - 0x7A | 26 - 51 | 0x1A - 0x33 |\n\n### Detect invalid values.\n\nThe idea is that we have valid higher nibbles in range from `1..7`.\nWe can convert higher nibbles into a bitmask `0000'0010 ... 1000'0000` (`1 << nibble`)\nDue to a lack of proper shift instruction on x86, we will use a table lookup (`pshuvb`).\nAll other higher nibbles can become 0, indicating invalid input.\n\nNow for all possible lower nibbles we have a set of valid higher nibbles.\n\n\n| Lower Nibble | Valid higher nibble |\n| ------------ | ------------------- |\n| 0            | 3, 5, 7             |\n| 1 - 9        | 3, 4, 5, 6, 7       |\n| A            | 4, 5, 6, 7          |\n| B            | 4, 6                |\n| C            | 1, 4, 6             |\n| D, E         | 4, 6                |\n| F            | 2, 4, 6             |\n\n\nSo: by higher nibble lookup a bit that indicates that higher nibble\n    by lower nibble lookup a set of valid bits\n    and\n\nThis will give us a non-zero when it's OK.\nAnd 0 when it's not.\n\nBetween iterations we can accumulate error with unsigned min.\n0 - is the smallest value - so we will get 0 if there was one.\n\n### Offset to correct values.\n\nSimilar idea to encode - we just lookup the proper offset and add it.\nWe'll need to lookup by higher nibble.\n\n| Char      | New Hex code  | Res     | Offset (- means minus) |\n|-----------|---------------|---------|------------------------|\n| '+'       |   0x1C        | 62      | 62 - 0x1C              |\n| '/'       |   0x2F        | 63      | 63 - '/'               |\n| '0' - '9' |   0x30 - 0x39 | 52 - 61 | 52 - '0'               |\n| 'A' - 'O' |   0x41 - 0x4F | 0  - 14 | 0  - 'A'               |\n| 'P' - 'Z' |   0x50 - 0x5A | 15 - 25 | 0  - 'A'               |\n| 'a' - 'o' |   0x61 - 0x6F | 26 - 40 | 26 - 'a'               |\n| 'p' - 'z' |   0x70 - 0x7A | 26 - 51 | 26 - 'a'               |\n\n## packIndexesToBytes\n\n*NOTE: assume 16 byte registers.*\n\nFrom a register of 16 bytes we need to get 12 bytes.\nI think the only way to do it is to convert each 4 bytes to\nappropriate 3 bytes and then put them all together.\nAfter we doing conversion 4 to 3 we'll remove the leftover byte\nwith one shuffle.\n\nGoal (*One letter - 2 bits*):\n\n- Input: `0ddd'0ccc'0bbb'0aaa`\n- Needed bytes: `'cddd'bbcc'aaab`\n\nWe can obviously shift and or to get the desired result\n(and maybe on ARM we should) but again, shifting story for x86 is poor.\n\nThe 0x80 blog suggests the following trick.\n\n### Multiply add (MADD) solution\n\nOn SSE there are `madd` instructions for adjacent bytes (`_mm_maddubs_epi16`)  and words (`_mm_madd_epi16`). Multiply is a strictly more powerful operation than shift, so this will work.\n\n*NOTE: all of our numbers are positive so the sign of the operations doesn't matter*\n\nHere is the scalar equivalent of the code we'll do.\n(*I omit indicating leading 0s and ignore casts*).\n\n```\nstd::uint16_t aaabbb = aaa << 6 + bbb;\nstd::uint16_t cccddd = ccc << 6 + ddd;\nstd::uint32_t aaabbbcccddd = aaabbb << 12 + cccddd;\n```\n\nMultiplication equivalent of `<< 6` is `* 0x40` and of `<< 12` is `* 0x1000`.\n\nNow that we have `0000'aaab'bbcc'cddd`.\nThe correct order `cddd'bccc'aaab` which we can mix\ninto the final shuffle.\n\n## looping\n\nWe can convert only in registers. There is a question: when can we write the whole\nregister to the output?\nFor example, for 16 bytes of input (no padding) we only have 12 bytes of output space.\n\nA simple way to answer if we have enough space is to compute how much output space we'll need overall.\nHowever, this operation is somewhat expensive and is likely already done for the allocation. So computing output size ideally should be avoided.\n\nWe can prove that as long as the `Register` is bigger than 16 bytes, `1.5 Register` of input space is enough to guarantee at least one register of the output space.\n\nProof.\n\nOut of `1.5 Register`, `1 Register` converts to `0.75 Register` in the output. So we are left with proving that `0.5 Register` will produce at least `0.25 Register` of the output.\n\nIf there is no padding - `0.5` converts to `3/8`, which is `> 0.25`.\nIf there is padding, the last dword of input might produce just `1` output byte.\n\nSo, we need to prove that:\n\n`convertedSize(RegisterSize / 2 - 4) >= RegisterSize / 4 - 1`\n\nFor `16` bytes it works `convertedSize(4) == 3`.\nFor `Register > 16` bytes, we can split:\n\n```\nconvertedSize(RegisterSize / 4) +\nconvertedSize(RegisterSize / 4 - 4) >=\nRegisterSize / 8 +\nRegisterSize / 8 - 1\n```\n\nSince `convertedSize(RegisterSize / 4) > RegisterSize / 8` we just reduced the problem to 16 bytes.\n\nProven.\n\n## Decoding SWAR\n\nSWAR stands for SIMD Within A Register i.e. using bit tricks to\nprocess multiple bytes in a bigger integer register.\nPrevious version of Base64Decode was using some bit tricks and, as a result,\nwas beating our performance for tails.\n\nOn top of that, if the platform lacks the necessary SIMD capabilities, SWAR is\nbeneficial, so we decided to implement it.\n\nHow does it work?\n\nThe simple version of the decoding algorithm matches from the input char to\nthe index: let's say char `a` is matched to the number `26`. So we would first match\nchars to the corresponding indexes and then blend those indexes together.\n\nLet's say (one letter still 2 bits):\n\n```\n0AAA'0BBB'0CCC'0DDD  // input chars\n0aaa'0bbb'0ccc'0ddd  // corresponding bytes\ncddd'bbcc'aaab'____  // after a bunch of bit manipulations\n                     // (last byte is w/e)\n```\n\nHowever, what if we incorporated both position and character in the lookup table?\n\n```\nchar:                 0AAA\ncorresponds to index: 0aaa\n\n[0][0AAA] = 0000'0000'aaa0'0000\n[1][0AAA] = 0000'aa00'000a'0000\n[2][0AAA] = a000'00aa'0000'0000\n[4][0AAA] = 0aaa'0000'0000'0000\n```\n\nThis way we can just `or` results for all input chars.\nSince we have one byte, which is always `0000`, we can use that byte for marking errors.\nSo for a busted input we can just store `0xffffffff`. We will `or` everything we output - and then check for `0xffffffff` in the end.\n\nThis is the formula for each position (d - decoded index for char):\n\n```\nd << 2;                                   // 0: 0000'0000'0000'aaa0\nd >> 4 | (d << 12 & 0xff00);              // 1: 0000'0000'bb00'000b\n(d << 6 & 0xff00) | (d << 22 & 0xff0000); // 2: 0000'c000'00cc'0000\nd << 16;                                  // 3: 0000'0ddd'0000'0000\n```\n\nThis is a memory/speed trade off. For a simple table lookup decoding we store 256 bytes. For this decoding we store 256 * 4 * 4 = 4kb. On big enough data this is a win of about a third over naive code. As far as SIMD clean up code is concerned, this is more questionable but for now we decided to go with it given that it shows an\nimprovement in microbenchmarks.\n"
  },
  {
    "path": "folly/detail/base64_detail/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../../defs.bzl\",\n    \"folly_xplat_cxx_test\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"base64_against_scalar_test\",\n    srcs = [\"Base64AgainstScalarTest.cpp\"],\n    supports_static_listing = True,\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly/detail/base64_detail:base64_api\",\n        \"//xplat/folly/detail/base64_detail:base64_common\",\n        \"//xplat/folly/detail/base64_detail:base64_scalar\",\n        \"//xplat/folly/detail/base64_detail:base64_sse4_2\",\n        \"//xplat/folly/detail/base64_detail:base64_swar\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_platform_test\",\n    srcs = [\"Base64PlatformTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/detail/base64_detail:base64_sse4_2_platform\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"base64_special_cases_test\",\n    srcs = [\"Base64SpecialCasesTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/detail/base64_detail:base64_api\",\n        \"//folly/detail/base64_detail:base64_scalar\",\n        \"//folly/detail/base64_detail:base64_simd\",\n        \"//folly/detail/base64_detail:base64_sse4_2\",\n        \"//folly/portability:constexpr\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\n# !!!! fbcode/folly/detail/base64_detail/tests/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"base64_against_scalar_test\",\n    srcs = [\"Base64AgainstScalarTest.cpp\"],\n    deps = [\n        \"//folly/detail/base64_detail:base64_api\",\n        \"//folly/detail/base64_detail:base64_common\",\n        \"//folly/detail/base64_detail:base64_scalar\",\n        \"//folly/detail/base64_detail:base64_sse4_2\",\n        \"//folly/detail/base64_detail:base64_swar\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/detail/base64_detail/test/Base64AgainstScalarTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <limits>\n#include <optional>\n#include <random>\n#include <string_view>\n#include <folly/detail/base64_detail/Base64Api.h>\n#include <folly/detail/base64_detail/Base64Common.h>\n#include <folly/detail/base64_detail/Base64SWAR.h>\n#include <folly/detail/base64_detail/Base64Scalar.h>\n#include <folly/detail/base64_detail/Base64_SSE4_2.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly::detail::base64_detail {\nnamespace {\n\nconstexpr std::size_t kMaxInputSize = 1000;\n\nstruct Base64RandomTest : ::testing::Test {\n  std::string generateBytes() {\n    std::string res(size_dis(engine), 0);\n    for (auto& byte : res) {\n      byte = static_cast<char>(byte_dis(engine));\n    }\n    return res;\n  }\n\n  // We don't seed the engine so that tests give consistent results\n  std::mt19937 engine;\n  std::uniform_int_distribution<unsigned int> size_dis{0, kMaxInputSize};\n  std::uniform_int_distribution<unsigned int> byte_dis{\n      0, std::numeric_limits<std::uint8_t>::max()};\n};\n\nusing Encode = char* (*)(const char*, const char*, char*);\nusing Decode = Base64DecodeResult (*)(const char*, const char*, char*);\n\nstd::string callEncode(std::string_view bytes, Encode encode) {\n  std::string buf(kMaxInputSize * 2, 0);\n  char* end = encode(bytes.data(), bytes.data() + bytes.size(), buf.data());\n  buf.resize(end - buf.data());\n  return buf;\n}\n\nauto callDecode(std::string_view encoded, Decode decode)\n    -> std::optional<std::string> {\n  std::string buf(kMaxInputSize * 2, 0);\n  Base64DecodeResult r =\n      decode(encoded.data(), encoded.data() + encoded.size(), buf.data());\n  if (!r.isSuccess) {\n    return {};\n  }\n  buf.resize(r.o - buf.data());\n  return buf;\n}\n\nconstexpr Encode kEncodes[] = {\n    base64EncodeScalar,\n#if FOLLY_SSE_PREREQ(4, 2)\n    base64Encode_SSE4_2,\n#endif\n};\n\nconstexpr Encode kEncodesURL[] = {\n    base64URLEncodeScalar,\n#if FOLLY_SSE_PREREQ(4, 2)\n    base64URLEncode_SSE4_2,\n#endif\n};\n\nconstexpr Decode kDecodes[] = {\n    base64DecodeScalar,\n    base64DecodeSWAR,\n#if FOLLY_SSE_PREREQ(4, 2)\n    base64Decode_SSE4_2,\n#endif\n};\n\nconstexpr Decode kDecodesURL[] = {\n    base64URLDecodeScalar,\n    base64URLDecodeSWAR,\n};\n\nconstexpr Decode kDecodesPHP[] = {\n    base64PHPStrictDecode,\n};\n\nTEST_F(Base64RandomTest, RandomTest) {\n  for (int i = 0; i != 10'000; ++i) {\n    auto bytes = generateBytes();\n    auto base64 = callEncode(bytes, base64EncodeScalar);\n    auto base64URL = callEncode(bytes, base64URLEncodeScalar);\n\n    for (Encode encode : kEncodes) {\n      ASSERT_EQ(base64, callEncode(bytes, encode));\n    }\n\n    for (Encode encode : kEncodesURL) {\n      ASSERT_EQ(base64URL, callEncode(bytes, encode));\n    }\n\n    for (Decode decode : kDecodes) {\n      ASSERT_EQ(bytes, callDecode(base64, decode));\n    }\n\n    for (Decode decode : kDecodesURL) {\n      ASSERT_EQ(bytes, callDecode(base64, decode));\n      ASSERT_EQ(bytes, callDecode(base64URL, decode));\n    }\n\n    for (Decode decode : kDecodesPHP) {\n      ASSERT_EQ(bytes, callDecode(base64, decode));\n    }\n  }\n}\n\nTEST_F(Base64RandomTest, RandomDecodeTest) {\n  for (int i = 0; i != 10'000; ++i) {\n    auto bytes = generateBytes();\n\n    auto scalar = callDecode(bytes, base64DecodeScalar);\n    auto scalarURL = callDecode(bytes, base64URLDecodeScalar);\n\n    for (Decode decode : kDecodes) {\n      ASSERT_EQ(scalar, callDecode(bytes, decode));\n    }\n    for (Decode decode : kDecodesURL) {\n      ASSERT_EQ(scalarURL, callDecode(bytes, decode));\n    }\n  }\n}\n\n} // namespace\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/test/Base64PlatformTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <array>\n#include <cstdint>\n#include <cstring>\n#include <numeric>\n#include <vector>\n#include <folly/portability/GTest.h>\n\n#include <folly/detail/base64_detail/Base64_SSE4_2_Platform.h>\n\nnamespace folly::detail::base64_detail {\nnamespace {\n#if FOLLY_SSE_PREREQ(4, 2)\n\nstd::array<std::uint8_t, 16> expectedEncodeToIndexes(\n    std::array<std::uint8_t, 16> in) {\n  std::array<std::uint8_t, 16> res{};\n\n  std::uint8_t const* f = in.data();\n  std::uint8_t* o = res.data();\n  std::uint8_t* const oEnd = res.data() + res.size();\n\n  while (o != oEnd) {\n    std::uint8_t aaab = f[0];\n    std::uint8_t bbcc = f[1];\n    std::uint8_t cddd = f[2];\n\n    std::uint8_t aaa = aaab >> 2;\n    std::uint8_t bbb = ((aaab << 4) | (bbcc >> 4)) & 0x3f;\n    std::uint8_t ccc = ((bbcc << 2) | (cddd >> 6)) & 0x3f;\n    std::uint8_t ddd = cddd & 0x3f;\n\n    o[0] = aaa;\n    o[1] = bbb;\n    o[2] = ccc;\n    o[3] = ddd;\n\n    f += 3;\n    o += 4;\n  }\n\n  return res;\n}\n\nstd::array<std::uint8_t, 16> expectedPackIndexesToBytes(\n    std::array<std::uint8_t, 16> in) {\n  std::array<std::uint8_t, 16> res{};\n  res.fill(0);\n\n  std::uint8_t const* f = in.data();\n  std::uint8_t const* const inEnd = in.data() + in.size();\n  std::uint8_t* o = res.data();\n\n  while (f != inEnd) {\n    std::uint8_t aaa = f[0];\n    std::uint8_t bbb = f[1];\n    std::uint8_t ccc = f[2];\n    std::uint8_t ddd = f[3];\n\n    std::uint8_t aaab = (aaa << 2) | (bbb >> 4);\n    std::uint8_t bbcc = (bbb << 4) | (ccc >> 2);\n    std::uint8_t cddd = (ccc << 6) | ddd;\n\n    o[0] = aaab;\n    o[1] = bbcc;\n    o[2] = cddd;\n\n    f += 4;\n    o += 3;\n  }\n\n  return res;\n}\n\nconstexpr char kBase64EncodeTable[] =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\n    \"=\"; // \\0 is also at the end.\n\nstd::array<std::uint8_t, 16> expectedLookupByIndex(\n    std::array<std::uint8_t, 16> in, const char* sampleTable) {\n  std::array<std::uint8_t, 16> res{};\n\n  for (std::size_t i = 0; i != in.size(); ++i) {\n    res[i] = static_cast<std::uint8_t>(sampleTable[in[i]]);\n  }\n\n  return res;\n}\n\nstd::array<std::uint8_t, 16> expectedSuccessfullDecodeToIndex(\n    std::array<std::uint8_t, 16> in) {\n  std::array<std::uint8_t, 16> r = {};\n  for (std::size_t i = 0; i != in.size(); ++i) {\n    if ('A' <= in[i] && in[i] <= 'Z') {\n      r[i] = in[i] - 'A';\n    } else if ('a' <= in[i] && in[i] <= 'z') {\n      r[i] = in[i] - 'a' + 26;\n    } else if ('0' <= in[i] && in[i] <= '9') {\n      r[i] = in[i] - '0' + 26 * 2;\n    } else if ('+' == in[i]) {\n      r[i] = 62;\n    } else if ('/' == in[i]) {\n      r[i] = 63;\n    } else {\n      return {};\n    }\n  }\n  return r;\n}\n\ntemplate <typename Platform>\nstruct Base64PlatformTest : ::testing::Test {\n  using platform = Platform;\n  using RegBytesArray = std::array<std::uint8_t, platform::kRegisterSize>;\n\n  static RegBytesArray actualEncodeToIndexes(RegBytesArray from) {\n    RegBytesArray res;\n    auto reg = platform::encodeToIndexes(platform::loadu(from.data()));\n    platform::storeu(res.data(), reg);\n    return res;\n  }\n\n  static RegBytesArray actualLookupByIndex(RegBytesArray from) {\n    RegBytesArray res;\n    auto reg = platform::lookupByIndex(\n        platform::loadu(from.data()), constants::kEncodeTable.data());\n    platform::storeu(res.data(), reg);\n    return res;\n  }\n\n  static RegBytesArray actualSuccesfullDecodeToIndex(RegBytesArray from) {\n    RegBytesArray res;\n    auto err = platform::initError();\n    auto reg = platform::decodeToIndex(platform::loadu(from.data()), err);\n    EXPECT_FALSE(platform::hasErrors(err));\n    platform::storeu(res.data(), reg);\n    return res;\n  }\n\n  static RegBytesArray actualPackIndexesToBytes(RegBytesArray from) {\n    auto reg = platform::packIndexesToBytes(platform::loadu(from.data()));\n    RegBytesArray res;\n    platform::storeu(res.data(), reg);\n    return res;\n  }\n};\n\nTYPED_TEST_SUITE(Base64PlatformTest, ::testing::Types<Base64_SSE4_2_Platform>);\n\nTYPED_TEST(Base64PlatformTest, EncodeToIndexes) {\n  using RegBytes = typename TestFixture::RegBytesArray;\n\n  for (std::uint16_t v = 0; v != 256; v += 8) {\n    RegBytes in;\n    std::iota(in.data(), in.data() + in.size(), static_cast<std::uint8_t>(v));\n\n    RegBytes expected = expectedEncodeToIndexes(in);\n    RegBytes actual = TestFixture::actualEncodeToIndexes(in);\n\n    EXPECT_EQ(expected, actual);\n  }\n}\n\nTYPED_TEST(Base64PlatformTest, IndexLookup) {\n  using RegBytes = typename TestFixture::RegBytesArray;\n\n  std::uint8_t max_index =\n      std::strlen(kBase64EncodeTable) + 1; // to include '\\0'\n\n  for (std::uint8_t i = 0; i != max_index + 1 - RegBytes{}.size(); i += 1) {\n    RegBytes in;\n    std::iota(in.data(), in.data() + in.size(), i);\n    RegBytes expected = expectedLookupByIndex(in, kBase64EncodeTable);\n    RegBytes actual = TestFixture::actualLookupByIndex(in);\n    ASSERT_EQ(expected, actual);\n  }\n}\n\nTYPED_TEST(Base64PlatformTest, errorDetection) {\n  using RegBytes = typename TestFixture::RegBytesArray;\n\n  auto anyErrors = [](const RegBytes& arr) {\n    using pl = typename TestFixture::platform;\n\n    auto errorAccum = pl::initError();\n    pl::decodeToIndex(pl::loadu(arr.data()), errorAccum);\n    return pl::hasErrors(errorAccum);\n  };\n\n  constexpr char kValidChar = 'A';\n\n  RegBytes in;\n  in.fill(kValidChar);\n  ASSERT_FALSE(anyErrors(in));\n\n  for (std::size_t sym = 0; //\n       sym != std::numeric_limits<std::uint8_t>::max() + 1;\n       ++sym) {\n    bool isValid = //\n        (sym == '+') || //\n        (sym == '/') || //\n        ('0' <= sym && sym <= '9') || //\n        ('A' <= sym && sym <= 'Z') || //\n        ('a' <= sym && sym <= 'z');\n    for (auto& inByte : in) {\n      inByte = static_cast<std::uint8_t>(sym);\n\n      ASSERT_EQ(anyErrors(in), !isValid) << std::hex << sym << std::dec;\n      inByte = kValidChar;\n    }\n  }\n}\n\nTYPED_TEST(Base64PlatformTest, decodeToIndexSuccess) {\n  using RegBytes = typename TestFixture::RegBytesArray;\n\n  // Some cases\n  for (std::uint16_t v = 0; v < 256; v += 1) {\n    RegBytes in;\n    std::iota(in.data(), in.data() + in.size(), static_cast<std::uint8_t>(v));\n\n    for (auto& x : in) {\n      x = x % 64;\n    }\n\n    RegBytes encoded = expectedLookupByIndex(in, kBase64EncodeTable);\n    RegBytes expected = expectedSuccessfullDecodeToIndex(encoded);\n    RegBytes actual = TestFixture::actualSuccesfullDecodeToIndex(encoded);\n\n    ASSERT_EQ(expected, actual) << v;\n  }\n}\n\nTYPED_TEST(Base64PlatformTest, packIndexesToBytes) {\n  using RegBytes = typename TestFixture::RegBytesArray;\n\n  for (std::uint16_t v = 0; v < 256; v += 1) {\n    RegBytes in;\n    in.fill(0);\n    std::iota(\n        in.data(), in.data() + in.size() / 4 * 3, static_cast<std::uint8_t>(v));\n\n    for (auto& x : in) {\n      x = x % 64;\n    }\n\n    RegBytes expected = expectedPackIndexesToBytes(in);\n    ASSERT_EQ(in, expectedEncodeToIndexes(expected)) << \"sanity check\";\n\n    RegBytes actual = TestFixture::actualPackIndexesToBytes(in);\n    ASSERT_EQ(expected, actual);\n  }\n}\n#endif // FOLLY_SSE_PREREQ(4, 2)\n\n} // namespace\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/base64_detail/test/Base64SpecialCasesTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstdint>\n#include <initializer_list>\n#include <sstream>\n#include <string_view>\n#include <type_traits>\n#include <folly/detail/base64_detail/Base64Api.h>\n#include <folly/detail/base64_detail/Base64Scalar.h>\n#include <folly/detail/base64_detail/Base64Simd.h>\n#include <folly/detail/base64_detail/Base64_SSE4_2.h>\n#include <folly/portability/Constexpr.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly::detail::base64_detail {\nnamespace {\n\nstruct TestCase {\n  std::string_view data;\n  std::string_view encodedStd;\n  std::string_view encodedURL;\n};\n\n// The types are weird because we need to do constexpr tests\nstruct TestCaseOnStack : TestCase {\n  constexpr TestCaseOnStack(\n      std::initializer_list<char> dataInit, // initializer list makes it easier\n                                            // to put non printable characters\n                                            // compared to strings\n      std::string_view encodedStdParam,\n      std::string_view encodedURLParam)\n      : TestCase{{}, encodedStdParam, encodedURLParam}, dataBuf{} {\n    // std::copy not constexpr\n    auto f = dataInit.begin(), l = dataInit.end();\n    auto o = dataBuf.begin();\n\n    while (f != l) {\n      *o++ = *f++;\n    }\n    data = std::string_view{\n        dataBuf.data(), static_cast<size_t>(o - dataBuf.begin())};\n  }\n\n  std::array<char, 200> dataBuf;\n} constexpr staticTestCases[] = {\n    // clang-format off\n    {\n      std::initializer_list<char>{}, \"\", \"\"\n    },\n    {\n        { 0, 0, 0 }, \"AAAA\", \"AAAA\"\n    },\n    {\n        {1}, \"AQ==\", \"AQ\"\n    },\n    {\n        {1, 0}, \"AQA=\", \"AQA\",\n    },\n    {\n        {1, 0, 0}, \"AQAA\", \"AQAA\"\n    },\n    {\n        {'a','b','c','d'},\n        \"YWJjZA==\",\n        \"YWJjZA\"\n    },\n    {\n        {'a', 'b', 'c'},\n        \"YWJj\",\n        \"YWJj\"\n    },\n    {\n      {'l','e','s','s',' ',\n       'i','s',' ',\n       'm','o','r','e',' ',\n       't','h','a','n',' ',\n       'm','o','r','e'},\n      \"bGVzcyBpcyBtb3JlIHRoYW4gbW9yZQ==\",\n      \"bGVzcyBpcyBtb3JlIHRoYW4gbW9yZQ\",\n    },\n    {\n      {'<','>', '?','s','u'},\n      \"PD4/c3U=\",\n      \"PD4_c3U\"\n    },\n    // clang-format on\n};\n\nstd::string byteRangeToString(std::string_view data) {\n  std::stringstream res;\n  res << '{';\n  auto f = data.begin();\n  auto l = data.end();\n\n  if (f != l) {\n    res << static_cast<std::int32_t>(*f);\n    while (++f != l) {\n      res << \", \" << static_cast<std::int32_t>(*f);\n    }\n  }\n  res << '}';\n  return std::move(res).str();\n}\n\ntemplate <typename TestRunner>\nconstexpr bool staticTests(TestRunner testRunner) {\n  for (const auto& test : staticTestCases) {\n    if (!testRunner(TestCase{test})) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename I, typename N, typename V>\nconstexpr I fill_n(I f, N n, V v) {\n  while (n--) {\n    *f++ = v;\n  }\n  return f;\n}\n\ntemplate <typename TestRunner>\nconstexpr bool manyZeroesTests(TestRunner testRunner) {\n  for (std::size_t inSize = 0; inSize != 129; ++inSize) {\n    TestCase test;\n\n    // Populate input\n    std::array<char, 256> buf = {}; // fill in 0s\n    buf[inSize + 1] = 15; // messing with the input\n    test.data = {buf.data(), inSize};\n\n    // Populate expected\n    std::array<char, 256> expectedBuf = {};\n    char* expectedL = fill_n(expectedBuf.begin(), inSize / 3 * 4, 'A');\n    char* expectedURLL = expectedL;\n\n    if (inSize % 3 == 2) {\n      *expectedL++ = 'A';\n      *expectedL++ = 'A';\n      *expectedL++ = 'A';\n      expectedURLL = expectedL;\n      *expectedL++ = '=';\n    } else if (inSize % 3 == 1) {\n      *expectedL++ = 'A';\n      *expectedL++ = 'A';\n      expectedURLL = expectedL;\n      *expectedL++ = '=';\n      *expectedL++ = '=';\n    }\n\n    test.encodedStd =\n        std::string_view(expectedBuf.data(), expectedL - expectedBuf.data());\n    test.encodedURL =\n        std::string_view(expectedBuf.data(), expectedURLL - expectedBuf.data());\n\n    // Run test\n    if (!testRunner(test)) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\ntemplate <typename TestRunner>\nconstexpr bool runEncodeTests(TestRunner testRunner) {\n  return staticTests(testRunner) && manyZeroesTests(testRunner);\n}\n\n// In constexpr we can have a non constexpr expression, as long\n// as it is not evaluated.\n//\n// There was a gcc bug with respect to it. Luckily this just affects how\n// much useful information will be output in case of a test failure.\n#if defined(__GNUC__) && !defined(__clang__)\n#define GCC_CONSTEXPR_BUG_ACTIVE\n#endif\n\nenum DecoderType { URLDecoder, RegularDecoder, PHPStrictDecoder };\n\ntemplate <DecoderType decoderType>\nstruct ConstexprTester {\n  constexpr bool encodeTest(TestCase test) const {\n    std::array<char, 1000> buf = {};\n    char* end =\n        base64EncodeScalar(test.data.begin(), test.data.end(), buf.data());\n    std::string_view actual(buf.data(), end - buf.data());\n\n    if (test.encodedStd == actual) {\n      return true;\n    }\n\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n    EXPECT_EQ(test.encodedStd, actual)\n        << \"Regular encoding mismatch. Input data:\\n\"\n        << byteRangeToString(test.data);\n#endif\n\n    return false;\n  }\n\n  constexpr bool decodeTest(TestCase test) const {\n    std::array<char, 1000> buf = {};\n    auto res = base64DecodeScalar(\n        test.encodedStd.data(),\n        test.encodedStd.data() + test.encodedStd.size(),\n        buf.begin());\n\n    std::string_view decoded(buf.begin(), res.o - buf.begin());\n\n    if (res.isSuccess && test.data == decoded) {\n      return true;\n    }\n\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n    EXPECT_TRUE(res.isSuccess) << \"encoded: \" << test.encodedStd;\n    EXPECT_EQ(test.data, decoded) << \"encoded: \" << test.encodedStd;\n#endif\n\n    return false;\n  }\n\n  constexpr bool encodeURLTest(TestCase test) const {\n    std::array<char, 1000> buf = {};\n    char* end =\n        base64URLEncodeScalar(test.data.begin(), test.data.end(), buf.data());\n    std::string_view actual(buf.data(), end - buf.data());\n\n    if (test.encodedURL == actual) {\n      return true;\n    }\n\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n    EXPECT_EQ(test.encodedURL, actual)\n        << \"URL encoding mismatch. Input data:\\n\"\n        << byteRangeToString(test.data);\n#endif\n\n    return false;\n  }\n\n  constexpr bool decodeURLTest(TestCase test) const {\n    auto oneInput = [&](std::string_view encoded) {\n      std::array<char, 1000> buf = {};\n      auto res = base64URLDecodeScalar(\n          encoded.data(), encoded.data() + encoded.size(), buf.begin());\n\n      std::string_view decoded(buf.begin(), res.o - buf.begin());\n      if (res.isSuccess && test.data == decoded) {\n        return true;\n      }\n\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n      EXPECT_TRUE(res.isSuccess) << \"encoded: \" << encoded;\n      EXPECT_EQ(test.data, decoded) << \"encoded: \" << encoded;\n#endif\n\n      return false;\n    };\n\n    return oneInput(test.encodedStd) && oneInput(test.encodedURL);\n  }\n\n  constexpr bool decodePHPTest(TestCase test) const {\n    std::array<char, 1000> buf = {};\n    auto res = base64PHPStrictDecode(\n        test.encodedStd.data(),\n        test.encodedStd.data() + test.encodedStd.size(),\n        buf.begin());\n\n    std::string_view decoded(buf.begin(), res.o - buf.begin());\n\n    if (res.isSuccess && test.data == decoded) {\n      return true;\n    }\n\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n    EXPECT_TRUE(res.isSuccess) << \"encoded: \" << test.encodedStd;\n    EXPECT_EQ(test.data, decoded) << \"encoded: \" << test.encodedStd;\n#endif\n\n    return false;\n  }\n\n  constexpr bool sizeTests(TestCase test) const {\n    std::size_t encodedSize = base64EncodedSize(test.data.size());\n    std::size_t encodedURLSize = base64URLEncodedSize(test.data.size());\n    std::size_t decodedSize = base64DecodedSize(\n        test.encodedStd.data(),\n        test.encodedStd.data() + test.encodedStd.size());\n    std::size_t decodedURLSize = base64URLDecodedSize(\n        test.encodedURL.data(),\n        test.encodedURL.data() + test.encodedURL.size());\n    std::size_t decodedStdWithURlSize = base64URLDecodedSize(\n        test.encodedStd.data(),\n        test.encodedStd.data() + test.encodedStd.size());\n    std::size_t decodedPHPSize = base64PHPStrictDecodeRequiredOutputSize(\n        test.encodedStd.data(),\n        test.encodedStd.data() + test.encodedStd.size());\n\n    if (encodedSize == test.encodedStd.size() &&\n        encodedURLSize == test.encodedURL.size() &&\n        decodedSize == test.data.size() && decodedURLSize == test.data.size() &&\n        decodedStdWithURlSize == test.data.size() &&\n        decodedPHPSize >= test.data.size()) {\n      return true;\n    }\n\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n    EXPECT_EQ(test.encodedStd.size(), encodedSize) << test.encodedStd;\n    EXPECT_EQ(test.encodedURL.size(), encodedURLSize) << test.encodedURL;\n    EXPECT_EQ(test.data.size(), decodedSize) << test.encodedStd;\n    EXPECT_EQ(test.data.size(), decodedStdWithURlSize) << test.encodedStd;\n    EXPECT_EQ(test.data.size(), decodedURLSize) << test.encodedURL;\n    EXPECT_LE(test.data.size(), decodedPHPSize) << test.encodedStd;\n#endif\n\n    return false;\n  }\n\n  constexpr bool operator()(TestCase test) const {\n    if (!sizeTests(test)) {\n      return false;\n    }\n\n    if constexpr (decoderType == DecoderType::RegularDecoder) {\n      return encodeTest(test) && decodeTest(test);\n    }\n\n    if constexpr (decoderType == DecoderType::URLDecoder) {\n      return encodeURLTest(test) && decodeURLTest(test);\n    }\n\n    if constexpr (decoderType == DecoderType::PHPStrictDecoder) {\n      return decodePHPTest(test);\n    }\n\n    return true;\n  }\n};\n\nstruct SimdTester {\n  using Encode = char* (*)(const char*, const char*, char*);\n  using Decode = Base64DecodeResult (*)(const char*, const char*, char*);\n\n  Encode encode;\n  Encode encodeURL;\n  Decode decode;\n  Decode decodeURL;\n\n  bool encodeTest(TestCase test) const {\n    std::string actual(base64EncodedSize(test.data.size()), 0);\n    encode(test.data.begin(), test.data.end(), actual.data());\n    if (test.encodedStd == actual) {\n      return true;\n    }\n    EXPECT_EQ(test.encodedStd, actual)\n        << \"Regular encoding mismatch. Input data:\\n\"\n        << byteRangeToString(test.data);\n    return false;\n  }\n\n  bool encodeURLTest(TestCase test) const {\n    std::string actual(base64URLEncodedSize(test.data.size()), 0);\n    encodeURL(test.data.begin(), test.data.end(), actual.data());\n    if (test.encodedURL == actual) {\n      return true;\n    }\n    EXPECT_EQ(test.encodedStd, actual)\n        << \"URL encoding mismatch. Input data:\\n\"\n        << byteRangeToString(test.data);\n    return false;\n  }\n\n  bool decodeTest(TestCase test) const {\n    std::string actual(\n        base64DecodedSize(\n            test.encodedStd.data(),\n            test.encodedStd.data() + test.encodedStd.size()),\n        0);\n    auto decodedResult = decode(\n        test.encodedStd.data(),\n        test.encodedStd.data() + test.encodedStd.size(),\n        actual.data());\n\n    if (decodedResult.isSuccess && test.data == actual) {\n      return true;\n    }\n\n    EXPECT_TRUE(decodedResult.isSuccess) << byteRangeToString(test.data);\n    EXPECT_EQ(test.data, actual) << byteRangeToString(test.data);\n    return false;\n  }\n\n  bool decodeURLTest(TestCase test) const {\n    auto oneInput = [&](std::string_view encoded) {\n      std::string decoded(\n          base64URLDecodedSize(encoded.data(), encoded.data() + encoded.size()),\n          0);\n      auto res = decodeURL(\n          encoded.data(), encoded.data() + encoded.size(), decoded.data());\n\n      if (res.isSuccess && test.data == decoded) {\n        return true;\n      }\n\n      EXPECT_TRUE(res.isSuccess) << \"encoded: \" << encoded;\n      EXPECT_EQ(test.data, decoded) << \"encoded: \" << encoded;\n      return false;\n    };\n\n    return oneInput(test.encodedStd) && oneInput(test.encodedURL);\n  }\n\n  bool operator()(TestCase test) const {\n    return encodeTest(test) && encodeURLTest(test) && decodeTest(test) &&\n        decodeURLTest(test);\n  }\n};\n\nTEST(Base64, ConstexprTests) {\n  // Comment out the static assert to debug\n  static_assert(runEncodeTests(ConstexprTester<DecoderType::RegularDecoder>{}));\n  static_assert(runEncodeTests(ConstexprTester<DecoderType::URLDecoder>{}));\n  static_assert(\n      runEncodeTests(ConstexprTester<DecoderType::PHPStrictDecoder>{}));\n  ASSERT_TRUE(runEncodeTests(ConstexprTester<DecoderType::RegularDecoder>{}));\n  ASSERT_TRUE(runEncodeTests(ConstexprTester<DecoderType::URLDecoder>{}));\n  ASSERT_TRUE(runEncodeTests(ConstexprTester<DecoderType::PHPStrictDecoder>{}));\n}\n\nTEST(Base64, SpecialCases) {\n  ASSERT_TRUE(runEncodeTests(\n      SimdTester{\n          base64EncodeScalar,\n          base64URLEncodeScalar,\n          base64DecodeSWAR,\n          base64URLDecodeSWAR}));\n#if FOLLY_SSE_PREREQ(4, 2)\n  ASSERT_TRUE(runEncodeTests(\n      SimdTester{\n          base64Encode_SSE4_2,\n          base64URLEncode_SSE4_2,\n          base64Decode_SSE4_2,\n          base64URLDecodeSWAR}));\n#endif\n}\n\nconstexpr char kHasNegative0[] = {'A', 'b', 'c', -15, '\\0'};\nconstexpr char kHasNegative1[] = {\n    'a', 'b', 'c', 'd', 'a', -15, 'c', 'd', 'a', 'b', 'c',\n    'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', '\\0'};\n\nstruct DecodingErrorDetectionTest {\n  bool isSuccess;\n  std::string_view input;\n} constexpr kDecodingErrorDetection[] = {\n    // clang-format off\n    { true,  \"\" },\n    { false, \"=\" },\n    { false, \"==\" },\n    { false, \"A\" },\n    { false, \"B=\" },\n    { false, \"ba=\" },\n    { true,  \"0w==\" },\n    { true,  \"000=\" },\n    { false, \"===\" },\n    { false, \"0===\" },\n    { false, \"aa=0\" },\n    { false, \"aaaa\"\"aaaa\"\"aaaa\"\"aaaa\"\"0\" },\n    { true,  \"aaaa\"\"aaaa\"\"aaaa\"\"aaaa\"\"0w==\" },\n    { true,  \"0aaa\"\"aaaa\"\"aaaa\"\"aaaa\"\"aaaa\"\"aaaa\" },\n    { false, \"$aaa\"\"aaaa\"\"aaaa\"\"aaaa\"\"aaaa\"\"aaaa\" },\n    { false, \"aaaa\"\"aa$a\"\"aaaa\"\"aaaa\"\"aaaa\"\"aaaa\" },\n    { false, \"aaaaa\"},\n    { false, kHasNegative0 },\n    { false, kHasNegative1 },\n    // clang-format on\n};\n\nconstexpr std::string_view kDecodingOnlyURLValid[] = {\n    \"ba\",\n    \"ba__\",\n    \"ba__ba--ba__\",\n    \"bA_/0a--ba+_\",\n    \"_-==\",\n    \"iZ==\",\n    \"00==\",\n    \"997=\",\n    \"+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-\",\n    \"bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_\"\n    \"bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_\",\n    \"bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba+_bA_/0a--ba==\",\n};\n\nconstexpr std::string_view kDecodingOnlyStrictPHPValid[] = {\n    \" \",\n    \"\\n\",\n    \"\\t\",\n    \"\\r\",\n    \"\\t\\n\\r \",\n    \"\\n        \\n\",\n    \"\\n\\n\\n\\n\\n\\n\\n\\n\",\n    \"\\ta\\t\\na\\n\\ra\\r a \",\n    \"a\\n\\na\\n\\na\\n\\na\\n\\n\"\n    \"0w\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n=\\n\\n=\\n\\n\",\n    \"\\r\\r\\r\\r\\r\\r\\r\\r\\r  0  0  0  \\t\\t\\t\\t\\n\\n\\n\\n   =   \\n\\n\\n\\n\",\n};\n\ntemplate <DecoderType decoderType>\nconstexpr size_t decodedSize(std::string_view in) {\n  const char* f = in.data();\n  const char* l = in.data() + in.size();\n\n  if constexpr (decoderType == DecoderType::URLDecoder) {\n    return base64URLDecodedSize(f, l);\n  } else if constexpr (decoderType == DecoderType::PHPStrictDecoder) {\n    return base64PHPStrictDecodeRequiredOutputSize(f, l);\n  } else {\n    return base64DecodedSize(f, l);\n  }\n}\n\ntemplate <DecoderType decoderType, typename Decoder>\nvoid triggerASANOnBadDecode(std::string_view in, Decoder decoder) {\n  std::vector<char> buf(decodedSize<decoderType>(in));\n  decoder(in.data(), in.data() + in.size(), buf.data());\n}\n\ntemplate <DecoderType decoderType, typename Decoder>\nconstexpr bool decodingErrorDetectionTest(Decoder decoder) {\n  std::array<char, 1000> buf = {};\n\n  auto sizeTest = [&](std::string_view in, Base64DecodeResult r) {\n    std::size_t allocatedSize = decodedSize<decoderType>(in);\n\n    std::size_t usedSize = static_cast<std::size_t>(r.o - buf.data());\n\n    if constexpr (decoderType == DecoderType::PHPStrictDecoder) {\n      if (usedSize <= allocatedSize) {\n        return true;\n      }\n    } else {\n      if (usedSize == allocatedSize) {\n        return true;\n      }\n    }\n\n    if (r.isSuccess) {\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n      if constexpr (decoderType == DecoderType::PHPStrictDecoder) {\n        EXPECT_LE(usedSize, allocatedSize) << in;\n      } else {\n        EXPECT_EQ(usedSize, allocatedSize)\n            << in << \" isURL: \" << (decoderType == DecoderType::URLDecoder);\n      }\n#endif\n      return false;\n    }\n\n    if (allocatedSize > 1000 || // overflow\n        usedSize > allocatedSize) {\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n      if constexpr (decoderType == DecoderType::PHPStrictDecoder) {\n        EXPECT_LE(usedSize, allocatedSize) << in;\n      } else {\n        EXPECT_LE(usedSize, allocatedSize)\n            << in << \" isURL: \" << (decoderType == DecoderType::URLDecoder);\n      }\n#endif\n      return false;\n    }\n    return true;\n  };\n\n  for (const auto& test : kDecodingErrorDetection) {\n    if (!folly::is_constant_evaluated_or(true)) {\n      triggerASANOnBadDecode<decoderType>(test.input, decoder);\n    }\n    auto r = decoder(\n        test.input.data(), test.input.data() + test.input.size(), buf.data());\n    if (test.isSuccess != r.isSuccess) {\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n      EXPECT_EQ(test.isSuccess, r.isSuccess) << test.input;\n#endif\n      return false;\n    }\n    if (!sizeTest(test.input, r)) {\n      return false;\n    }\n  }\n\n  for (std::string_view URLOnly : kDecodingOnlyURLValid) {\n    if (!folly::is_constant_evaluated_or(true)) {\n      triggerASANOnBadDecode<decoderType>(URLOnly, decoder);\n    }\n    auto r =\n        decoder(URLOnly.data(), URLOnly.data() + URLOnly.size(), buf.data());\n    auto isURLDecoder = decoderType == DecoderType::URLDecoder;\n    if (isURLDecoder != r.isSuccess) {\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n      EXPECT_EQ(isURLDecoder, r.isSuccess) << URLOnly;\n#endif\n      return false;\n    }\n\n    if (!sizeTest(URLOnly, r)) {\n      return false;\n    }\n  }\n\n  for (std::string_view PHPStrictOnly : kDecodingOnlyStrictPHPValid) {\n    if (!folly::is_constant_evaluated_or(true)) {\n      triggerASANOnBadDecode<decoderType>(PHPStrictOnly, decoder);\n    }\n    auto r = decoder(\n        PHPStrictOnly.data(),\n        PHPStrictOnly.data() + PHPStrictOnly.size(),\n        buf.data());\n    auto isPHPStrictDecoder = decoderType == DecoderType::PHPStrictDecoder;\n    if (isPHPStrictDecoder != r.isSuccess) {\n#ifndef GCC_CONSTEXPR_BUG_ACTIVE\n      EXPECT_EQ(isPHPStrictDecoder, r.isSuccess) << PHPStrictOnly;\n#endif\n      return false;\n    }\n\n    if (!sizeTest(PHPStrictOnly, r)) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nTEST(Base64, DecodingErrorDetection) {\n  static_assert(decodingErrorDetectionTest<DecoderType::RegularDecoder>(\n      base64DecodeScalar));\n  static_assert(decodingErrorDetectionTest<DecoderType::URLDecoder>(\n      base64URLDecodeScalar));\n  ASSERT_TRUE(\n      decodingErrorDetectionTest<DecoderType::RegularDecoder>(\n          base64DecodeScalar));\n  ASSERT_TRUE(\n      decodingErrorDetectionTest<DecoderType::URLDecoder>(\n          base64URLDecodeScalar));\n  ASSERT_TRUE(\n      decodingErrorDetectionTest<DecoderType::RegularDecoder>(\n          base64DecodeSWAR));\n  ASSERT_TRUE(\n      decodingErrorDetectionTest<DecoderType::URLDecoder>(base64URLDecodeSWAR));\n#if FOLLY_SSE_PREREQ(4, 2)\n  ASSERT_TRUE(\n      decodingErrorDetectionTest<DecoderType::RegularDecoder>(\n          base64Decode_SSE4_2));\n#endif\n  ASSERT_TRUE(\n      decodingErrorDetectionTest<DecoderType::PHPStrictDecoder>(\n          base64PHPStrictDecode));\n}\n\n} // namespace\n} // namespace folly::detail::base64_detail\n"
  },
  {
    "path": "folly/detail/test/AsyncTraceTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/AsyncTrace.h>\n\n#include <folly/portability/GTest.h>\n\nTEST(FollyCountersTest, Trivial) {\n  folly::Executor* exec = nullptr;\n  folly::IOExecutor* io_exec = nullptr;\n  folly::async_tracing::logSetGlobalCPUExecutor(exec);\n  folly::async_tracing::logGetGlobalCPUExecutor(exec);\n  folly::async_tracing::logSetGlobalIOExecutor(io_exec);\n  folly::async_tracing::logGetGlobalIOExecutor(io_exec);\n  folly::async_tracing::logSetGlobalCPUExecutorToImmutable();\n  folly::async_tracing::logGetImmutableCPUExecutor(exec);\n  folly::async_tracing::logGetImmutableIOExecutor(io_exec);\n\n  folly::Executor* lastExec = nullptr;\n  folly::async_tracing::logSemiFutureVia(lastExec, exec);\n  folly::async_tracing::logFutureVia(lastExec, exec);\n\n  folly::async_tracing::logSemiFutureVia(lastExec, exec);\n  folly::async_tracing::logFutureVia(lastExec, exec);\n\n  folly::async_tracing::logBlockingOperation(std::chrono::milliseconds{100});\n\n  folly::async_tracing::logSemiFutureDiscard(\n      folly::async_tracing::DiscardHasDeferred::NO_EXECUTOR);\n}\n"
  },
  {
    "path": "folly/detail/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_trace_test\",\n    srcs = [\"AsyncTraceTest.cpp\"],\n    deps = [\n        \"//folly/detail:async_trace\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"static_singleton_manager_test\",\n    srcs = [\"StaticSingletonManagerTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/detail:static_singleton_manager\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"unique_instance_test\",\n    srcs = [\"UniqueInstanceTest.cpp\"],\n    deps = [\n        \"//folly:string\",\n        \"//folly:traits\",\n        \"//folly/detail:unique_instance\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"file_util_detail_test\",\n    srcs = [\n        \"FileUtilDetailTest.cpp\",\n    ],\n    deps = [\n        \"//folly/detail:file_util_detail\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"perf_scoped_test\",\n    srcs = [\n        \"PerfScopedTest.cpp\",\n    ],\n    deps = [\n        \"//folly/detail:perf_scoped\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:unistd\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"simple_simd_string_utils_test\",\n    srcs = [\n        \"SimpleSimdStringUtilsTest.cpp\",\n    ],\n    deps = [\n        \"//folly/algorithm/simd/detail:simd_platform\",\n        \"//folly/detail:simple_simd_string_utils\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"split_string_simd_test\",\n    srcs = [\n        \"SplitStringSimdTest.cpp\",\n    ],\n    deps = [\n        \"//folly:fbstring\",\n        \"//folly:fbvector\",\n        \"//folly:range\",\n        \"//folly:small_vector\",\n        \"//folly/detail:split_string_simd\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"thread_local_detail_test\",\n    srcs = [\n        \"ThreadLocalDetailTest.cpp\",\n    ],\n    deps = [\n        \"//folly:synchronized\",\n        \"//folly:thread_local\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"thread_local_benchmark\",\n    srcs = [\"ThreadLocalBenchmark.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:thread_local\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"traponavx512_test_negative\",\n    srcs = [\"TrapOnAvx512TestNegative.cpp\"],\n    deps = [\n        \"//folly/detail:traponavx512\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"tuple_test\",\n    srcs = [\"tuple_test.cpp\"],\n    deps = [\n        \"//folly:utility\",\n        \"//folly/detail:tuple\",\n        \"//folly/functional:invoke\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"detail_typelist_test\",\n    srcs = [\"TypeListTest.cpp\"],\n    deps = [\n        \"//folly/detail:typelist\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"benchmark_adaptive_test\",\n    srcs = [\"BenchmarkAdaptiveTest.cpp\"],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/detail/test/BenchmarkAdaptiveTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/BenchmarkAdaptive.h>\n\n#include <folly/portability/GTest.h>\n\n#include <cmath>\n\nusing namespace folly::detail;\nusing namespace std::chrono_literals;\nusing folly::UserCounters;\nusing folly::UserMetric;\n\n// --- SortedSamples tests ---\n\nTEST(SortedSamplesTest, SingleSample) {\n  SortedSamples s({42.0});\n  EXPECT_EQ(s.size(), 1);\n  EXPECT_DOUBLE_EQ(s.percentile(0), 42.0);\n  EXPECT_DOUBLE_EQ(s.percentile(50), 42.0);\n  EXPECT_DOUBLE_EQ(s.percentile(100), 42.0);\n}\n\nTEST(SortedSamplesTest, PercentileInterpolation) {\n  // 5 samples: 10, 20, 30, 40, 50\n  SortedSamples s({30, 10, 50, 20, 40});\n\n  // pos = (5-1) * pct / 100 = 4 * pct / 100\n  EXPECT_DOUBLE_EQ(s.percentile(0), 10.0);\n  EXPECT_DOUBLE_EQ(s.percentile(25), 20.0);\n  EXPECT_DOUBLE_EQ(s.percentile(50), 30.0);\n  EXPECT_DOUBLE_EQ(s.percentile(75), 40.0);\n  EXPECT_DOUBLE_EQ(s.percentile(100), 50.0);\n\n  // Interpolation: pct=12.5 -> pos=0.5 -> 10*0.5 + 20*0.5 = 15\n  EXPECT_DOUBLE_EQ(s.percentile(12.5), 15.0);\n  EXPECT_DOUBLE_EQ(s.percentile(37.5), 25.0);\n}\n\nTEST(SortedSamplesTest, EmptySamplesDeath) {\n  SortedSamples s({});\n  EXPECT_EQ(s.size(), 0);\n  EXPECT_DEATH(s.percentile(50), \"percentile requires at least 1 sample\");\n}\n\nTEST(SortedSamplesTest, PercentileCIBasic) {\n  // 10 samples: 1, 2, ..., 10\n  std::vector<double> data;\n  for (int i = 1; i <= 10; ++i) {\n    data.push_back(i);\n  }\n  SortedSamples s(data);\n\n  // Median (p50) should be around 5.5\n  auto ci = s.percentileCI(50.0);\n  EXPECT_NEAR(ci.estimate, 5.5, 0.01);\n\n  // CI should contain the estimate\n  EXPECT_LE(ci.lo, ci.estimate);\n  EXPECT_GE(ci.hi, ci.estimate);\n\n  // With 10 uniform samples, CI should be non-trivial\n  EXPECT_GT(ci.relWidth(), 0.0);\n}\n\nTEST(SortedSamplesTest, PercentileCIExtremesCollapse) {\n  // At p=0 and p=100, SE is 0 so CI should collapse to a point\n  SortedSamples s({1.0, 2.0, 3.0, 4.0, 5.0});\n\n  auto ci0 = s.percentileCI(0.0);\n  EXPECT_DOUBLE_EQ(ci0.lo, 1.0);\n  EXPECT_DOUBLE_EQ(ci0.hi, 1.0);\n  EXPECT_DOUBLE_EQ(ci0.estimate, 1.0);\n\n  auto ci100 = s.percentileCI(100.0);\n  EXPECT_DOUBLE_EQ(ci100.lo, 5.0);\n  EXPECT_DOUBLE_EQ(ci100.hi, 5.0);\n  EXPECT_DOUBLE_EQ(ci100.estimate, 5.0);\n}\n\nTEST(SortedSamplesTest, RelWidthWithZeroEstimate) {\n  // relWidth returns 100.0 (\"we know nothing\") for zero estimate\n  SortedSamples s({0.0, 0.0, 0.0});\n  auto ci = s.percentileCI(50.0);\n  EXPECT_DOUBLE_EQ(ci.estimate, 0.0);\n  EXPECT_DOUBLE_EQ(ci.relWidth(), 100.0);\n}\n\nTEST(SortedSamplesTest, PercentileCIRelWidthWithTooFewSamples) {\n  // percentileCIRelWidth returns 100.0 for < 2 samples\n  SortedSamples s({42.0});\n  EXPECT_DOUBLE_EQ(s.percentileCIRelWidth(50.0), 100.0);\n}\n\nTEST(SortedSamplesTest, PercentileCIRequiresTwoSamples) {\n  SortedSamples s({42.0});\n  EXPECT_DEATH(\n      s.percentileCI(50.0), \"percentileCI requires at least 2 samples\");\n}\n\n// --- computeStabilityStats tests ---\n\nTEST(StabilityStatsTest, StableWithIdenticalHalves) {\n  // Same values in both halves -> stable\n  std::vector<double> samples = {10, 10, 10, 10};\n  auto stats = computeStabilityStats(samples, 50.0, 0.01);\n  EXPECT_TRUE(stats.isStable);\n  EXPECT_DOUBLE_EQ(stats.firstHalf.estimate, stats.secondHalf.estimate);\n}\n\nTEST(StabilityStatsTest, UnstableWithDisjointHalves) {\n  // Completely disjoint halves -> unstable\n  std::vector<double> samples = {1, 2, 100, 101};\n  auto stats = computeStabilityStats(samples, 50.0, 0.01);\n  EXPECT_FALSE(stats.isStable);\n}\n\nTEST(StabilityStatsTest, RequiresFourSamples) {\n  std::vector<double> samples = {1, 2, 3};\n  EXPECT_DEATH(\n      computeStabilityStats(samples, 50.0, 0.01),\n      \"computeStabilityStats requires at least 4 samples\");\n}\n\nTEST(StabilityStatsTest, EpsilonToleranceForSubNsValues) {\n  // Sub-picosecond differences should be considered stable due to epsilon\n  std::vector<double> samples = {0.0001, 0.0001, 0.0002, 0.0002};\n  auto stats = computeStabilityStats(samples, 50.0, 0.01);\n  EXPECT_TRUE(stats.isStable);\n}\n\nTEST(StabilityStatsTest, StableWhenDriftBelowTargetPrecision) {\n  // Many samples with tiny systematic drift (~0.05% of estimate).\n  // With a tight absolute epsilon this would be \"unstable\", but the relative\n  // epsilon tied to `targetPrecisionPct` makes it stable.\n  std::vector<double> samples;\n  for (int i = 0; i < 500; ++i) {\n    samples.push_back(2780.0); // first half: ~2780\n  }\n  for (int i = 0; i < 500; ++i) {\n    samples.push_back(2778.5); // second half: ~2778.5, drift ~0.05%\n  }\n  // With 0.4% target precision, drift of 0.05% is well within budget.\n  EXPECT_TRUE(computeStabilityStats(samples, 50.0, 0.4).isStable);\n  // With 0.01% target precision, the same drift exceeds the budget.\n  EXPECT_FALSE(computeStabilityStats(samples, 50.0, 0.01).isStable);\n}\n\n// --- runBenchmarksAdaptive integration tests ---\n\nnamespace {\nAdaptiveOptions defaultTestOptions() {\n  return AdaptiveOptions{\n      .sliceUsec = 100,\n      .targetPercentile = 50.0,\n      .targetPrecisionPct = 50.0, // High tolerance for fast convergence\n      .minSamples = 5,\n      .minSecs = 0,\n      .maxSecs = 1,\n      .verbose = true, // make logs more useful\n  };\n}\n\n// Standard baseline: 10ns/iter\nBenchmarkFun defaultBaseline() {\n  return [](unsigned n) { return TimeIterData{10ns * n, n, {}, 0}; };\n}\n\n// No-op suspender baseline for tests (no BENCHMARK_SUSPEND used in test funs)\nBenchmarkFun noOpSuspenderBaseline() {\n  return [](unsigned n) { return TimeIterData{10ns * n, n, {}, 0}; };\n}\n\n// Create a simple benchmark function with fixed ns/iter\nBenchmarkFun simpleBench(std::chrono::nanoseconds nsPerIter) {\n  return [nsPerIter](unsigned n) {\n    return TimeIterData{nsPerIter * n, n, {}, 0};\n  };\n}\n\n// Create benchmark registration with simple timing\nBenchmarkRegistration makeReg(\n    const char* name, std::chrono::nanoseconds nsPerIter) {\n  return {\"file.cpp\", name, simpleBench(nsPerIter)};\n}\n\n// Helper to convert vector of values to vector of pointers\nstd::vector<const BenchmarkRegistration*> toPtrs(\n    const std::vector<BenchmarkRegistration>& benchmarks) {\n  std::vector<const BenchmarkRegistration*> ptrs;\n  ptrs.reserve(benchmarks.size());\n  for (const auto& b : benchmarks) {\n    ptrs.push_back(&b);\n  }\n  return ptrs;\n}\n} // namespace\n\nTEST(AdaptiveTest, EmptyBenchmarks) {\n  auto result = runBenchmarksAdaptive(\n      {}, defaultBaseline(), noOpSuspenderBaseline(), defaultTestOptions());\n  EXPECT_TRUE(result.results.empty());\n  EXPECT_EQ(result.totalRounds, 0);\n}\n\nTEST(AdaptiveTest, SingleBenchmark) {\n  std::vector<BenchmarkRegistration> benchmarks{makeReg(\"bench1\", 15ns)};\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks),\n      defaultBaseline(),\n      noOpSuspenderBaseline(),\n      defaultTestOptions());\n\n  ASSERT_EQ(result.results.size(), 1);\n  EXPECT_EQ(result.results[0].name, \"bench1\");\n  // Baseline is 10ns, benchmark is 15ns, so adjusted should be ~5ns\n  EXPECT_NEAR(result.results[0].timeInNs, 5.0, 0.5);\n  EXPECT_GE(result.totalRounds, 5); // At least minSamples\n}\n\nTEST(AdaptiveTest, MultipleBenchmarks) {\n  std::vector<BenchmarkRegistration> benchmarks{\n      makeReg(\"fast\", 12ns), makeReg(\"slow\", 20ns)};\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks),\n      defaultBaseline(),\n      noOpSuspenderBaseline(),\n      defaultTestOptions());\n\n  ASSERT_EQ(result.results.size(), 2);\n  EXPECT_NEAR(result.results[0].timeInNs, 2.0, 0.5); // 12 - 10\n  EXPECT_NEAR(result.results[1].timeInNs, 10.0, 0.5); // 20 - 10\n}\n\nTEST(AdaptiveTest, BaselineSubtractionClampsToZero) {\n  // Benchmark faster than baseline (edge case)\n  std::vector<BenchmarkRegistration> benchmarks{\n      makeReg(\"faster_than_baseline\", 15ns)};\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks),\n      simpleBench(20ns), // baseline > benchmark\n      noOpSuspenderBaseline(),\n      defaultTestOptions());\n\n  ASSERT_EQ(result.results.size(), 1);\n  // Should clamp to 0, not go negative\n  EXPECT_EQ(result.results[0].timeInNs, 0.0);\n}\n\nTEST(AdaptiveTest, EqualSamplingAcrossBenchmarks) {\n  // Verify that all benchmarks are sampled the same number of times per round\n  auto opts = defaultTestOptions();\n  opts.minSamples = 10;\n\n  size_t baselineCalls = 0;\n  auto baselineFun = [&](unsigned n) {\n    ++baselineCalls;\n    return TimeIterData{10ns * n, n, {}, 0};\n  };\n\n  size_t bench1Calls = 0;\n  size_t bench2Calls = 0;\n\n  std::vector<BenchmarkRegistration> benchmarks{\n      {\"file.cpp\",\n       \"bench1\",\n       [&](unsigned n) {\n         ++bench1Calls;\n         return TimeIterData{15ns * n, n, {}, 0};\n       }},\n      {\"file.cpp\",\n       \"bench2\",\n       [&](unsigned n) {\n         ++bench2Calls;\n         return TimeIterData{20ns * n, n, {}, 0};\n       }},\n  };\n\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), baselineFun, noOpSuspenderBaseline(), opts);\n\n  // With constant values, SE=0, so converges at first check (round 10)\n  EXPECT_GE(result.totalRounds, 10u); // At least minSamples\n  // Both benchmarks should have equal sampling calls\n  EXPECT_EQ(bench1Calls, bench2Calls);\n}\n\nTEST(AdaptiveTest, ConvergenceWithVariance) {\n  // Test convergence with quasi-continuous stationary data.\n  // Cycles through 21 values (20-40) to simulate continuous stationary data.\n  auto opts = defaultTestOptions();\n  opts.minSamples = 5;\n  opts.targetPrecisionPct = 20.0; // Should converge once CI < 20%\n\n  size_t benchIdx = 0;\n  std::vector<BenchmarkRegistration> benchmarks{\n      {\"file.cpp\",\n       \"cycling\",\n       [&](unsigned n) {\n         auto t = std::chrono::nanoseconds(20 + (benchIdx++ % 21));\n         return TimeIterData{t * n, n, {}, 0};\n       }},\n  };\n\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), defaultBaseline(), noOpSuspenderBaseline(), opts);\n\n  EXPECT_GE(result.totalRounds, opts.minSamples);\n  EXPECT_LE(result.totalRounds, 1000u);\n  // Median of [20..40] is 30, baseline is 10, so result ≈ 20\n  EXPECT_NEAR(result.results[0].timeInNs, 20.0, 10.0);\n}\n\nTEST(AdaptiveTest, MinSamplesGuaranteed) {\n  // With constant values, SE=0, so would converge immediately. But we should\n  // still collect minSamples samples.\n  auto opts = defaultTestOptions();\n  opts.minSamples = 20;\n  opts.targetPrecisionPct =\n      100.0; // Very loose, would converge immediately if allowed\n\n  size_t benchCalls = 0;\n  std::vector<BenchmarkRegistration> benchmarks{\n      {\"file.cpp\",\n       \"constant\",\n       [&](unsigned n) {\n         ++benchCalls;\n         return TimeIterData{15ns * n, n, {}, 0};\n       }},\n  };\n\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), defaultBaseline(), noOpSuspenderBaseline(), opts);\n\n  EXPECT_GE(benchCalls, 20u);\n  EXPECT_GE(result.totalRounds, 20u);\n}\n\nTEST(AdaptiveTest, HighVarianceHitsTimeout) {\n  // Test that high-variance data eventually hits maxSecs timeout.\n  // With alternating 20ns/40ns values, the exact percentile CI stays wide.\n  auto opts = defaultTestOptions();\n  opts.sliceUsec = 50000; // 50ms per sample\n  opts.minSamples = 5;\n  opts.targetPrecisionPct = 10.0; // Tight threshold that won't converge\n  opts.minSecs = 0;\n  opts.maxSecs = 1; // Short timeout\n\n  // Baseline: 50ms duration per call, returning 10ns/iter value\n  auto baselineFun = [](unsigned n) {\n    return TimeIterData{50ms * n, 5'000'000u * n, {}, 0};\n  };\n\n  // Benchmark: alternating 20ns/40ns value, 50ms elapsed per call\n  size_t benchIdx = 0;\n  std::vector<BenchmarkRegistration> benchmarks{\n      {\"file.cpp\",\n       \"alternating\",\n       [&](unsigned n) {\n         unsigned niter = (benchIdx % 2 == 0)\n             ? 2'500'000u * n // 50ms / 2.5M = 20ns\n             : 1'250'000u * n; // 50ms / 1.25M = 40ns\n         ++benchIdx;\n         return TimeIterData{50ms * n, niter, {}, 0};\n       }},\n  };\n\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), baselineFun, noOpSuspenderBaseline(), opts);\n\n  // Should hit timeout since CI won't converge to 10%\n  EXPECT_GT(result.totalRounds, opts.minSamples);\n}\n\nTEST(AdaptiveTest, UserCountersFromPercentileMatchedSample) {\n  // Verify that user counters come from the sample closest to the percentile.\n  // With samples 20/25/30/35/40ns, p50=30ns has counter idx=300.\n  auto opts = defaultTestOptions();\n  opts.minSamples = 5;\n  opts.targetPrecisionPct = 100.0;\n\n  size_t benchIdx = 0;\n  std::vector<std::chrono::nanoseconds> times = {20ns, 25ns, 30ns, 35ns, 40ns};\n  std::vector<int64_t> counterVals = {100, 200, 300, 400, 500};\n  std::vector<BenchmarkRegistration> benchmarks{\n      {\"file.cpp\",\n       \"with_counters\",\n       [&](unsigned n) {\n         size_t idx = benchIdx++ % 5;\n         UserCounters counters{{\"idx\", UserMetric(counterVals[idx])}};\n         return TimeIterData{times[idx] * n, n, counters, 0};\n       }},\n  };\n\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), defaultBaseline(), noOpSuspenderBaseline(), opts);\n\n  ASSERT_EQ(result.results.size(), 1);\n  ASSERT_EQ(result.results[0].counters.count(\"idx\"), 1);\n  // p50 of {20,25,30,35,40} adjusted by baseline 10 = {10,15,20,25,30} ->\n  // p50=20 Closest sample to 20ns adjusted has counter 300\n  EXPECT_EQ(result.results[0].counters.at(\"idx\"), UserMetric(300));\n}\n\nTEST(AdaptiveTest, MinSecsEnforcesMinimumDuration) {\n  // minSecs forces continued sampling even when CI converges immediately.\n  auto opts = defaultTestOptions();\n  opts.sliceUsec = 50000; // 50ms per sample\n  opts.minSamples = 5;\n  opts.targetPrecisionPct = 100.0;\n  opts.minSecs = 1;\n  opts.maxSecs = 2;\n\n  std::vector<BenchmarkRegistration> benchmarks{makeReg(\"constant\", 60ms)};\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), simpleBench(50ms), noOpSuspenderBaseline(), opts);\n\n  // 50ms baseline + 50ms benchmark = 100ms/round, need 10 rounds for 1s\n  EXPECT_GE(result.totalRounds, 10u);\n}\n\nTEST(AdaptiveTest, SuspenderOverheadCorrection) {\n  // 50ns raw - 10ns baseline - 35ns suspender = 5ns adjusted\n  auto opts = defaultTestOptions();\n  opts.minSamples = 5;\n  opts.targetPrecisionPct = 100.0;\n\n  std::vector<BenchmarkRegistration> benchmarks{\n      {\"file.cpp\",\n       \"with_suspend\",\n       [](unsigned n) {\n         return TimeIterData{50ns * n, n, {}, n};\n       }}, // 1 suspend/iter\n  };\n\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), defaultBaseline(), simpleBench(35ns), opts);\n\n  ASSERT_EQ(result.results.size(), 1);\n  EXPECT_NEAR(result.results[0].timeInNs, 5.0, 0.5);\n}\n\nTEST(AdaptiveTest, SuspenderOverheadClampsToZero) {\n  // 30ns raw - 10ns baseline - 100ns suspender = -80ns, clamped to 0\n  auto opts = defaultTestOptions();\n  opts.minSamples = 5;\n  opts.targetPrecisionPct = 100.0;\n\n  std::vector<BenchmarkRegistration> benchmarks{\n      {\"file.cpp\",\n       \"clamped\",\n       [](unsigned n) { return TimeIterData{30ns * n, n, {}, n}; }},\n  };\n\n  auto result = runBenchmarksAdaptive(\n      toPtrs(benchmarks), defaultBaseline(), simpleBench(100ns), opts);\n\n  ASSERT_EQ(result.results.size(), 1);\n  EXPECT_EQ(result.results[0].timeInNs, 0.0);\n}\n\n// --- recalibrateIterCount tests ---\n\nTEST(RecalibrateIterCountTest, Basic) {\n  // Converges from 1: ceil(1 * 1'000'000 / 500) = 2000\n  EXPECT_EQ(recalibrateIterCount(1, 1ms, 500ns), 2000);\n  // Monotonically increasing: slow sample can't decrease iterCount\n  EXPECT_EQ(recalibrateIterCount(1000, 1ms, 2ms), 1000);\n  // Tiny duration (<= 10ns) doubles instead of dividing\n  EXPECT_EQ(recalibrateIterCount(4, 1ms, 5ns), 8);\n  EXPECT_EQ(recalibrateIterCount(4, 1ms, 0ns), 8);\n  // Never returns zero\n  EXPECT_GE(recalibrateIterCount(1, 1ms, 999'999'999ns), 1);\n}\n"
  },
  {
    "path": "folly/detail/test/FileUtilDetailTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/FileUtilDetail.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace fileutil_detail {\n\nclass FileUtilDetailTest : public ::testing::Test {};\n\nTEST_F(FileUtilDetailTest, TemporaryPathWithoutTemporaryDirectory) {\n  auto path = std::string{\"/a/b/c\"};\n  auto tempPath = getTemporaryFilePathString(path, std::string{});\n  EXPECT_EQ(tempPath, \"/a/b/c.XXXXXX\");\n}\n\nTEST_F(FileUtilDetailTest, TemporaryPathWithoutTemporaryDirectoryRelative) {\n  auto path = std::string{\"a/b/c\"};\n  auto tempPath = getTemporaryFilePathString(path, std::string{});\n  EXPECT_EQ(tempPath, \"a/b/c.XXXXXX\");\n}\n\nTEST_F(\n    FileUtilDetailTest,\n    TemporaryPathWithoutTemporaryDirectoryWithTempDirectoryAbsolute) {\n  auto path = std::string{\"a/b/c\"};\n  auto tempDirectory = std::string{\"/temp/directory/\"};\n  auto tempPath = getTemporaryFilePathString(path, tempDirectory);\n  EXPECT_EQ(tempPath, \"/temp/directory/tempForAtomicWrite.XXXXXX\");\n}\n\nTEST_F(\n    FileUtilDetailTest,\n    TemporaryPathWithoutTemporaryDirectoryWithTempDirectoryRelative) {\n  auto path = std::string{\"a/b/c\"};\n  auto tempDirectory = std::string{\"temp/directory/\"};\n  auto tempPath = getTemporaryFilePathString(path, tempDirectory);\n  EXPECT_EQ(tempPath, \"temp/directory/tempForAtomicWrite.XXXXXX\");\n}\n\n} // namespace fileutil_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/test/PerfScopedTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/PerfScoped.h>\n\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Unistd.h>\n#include <folly/test/TestUtils.h>\n\n#include <filesystem>\n#include <thread>\n\n#if FOLLY_PERF_IS_SUPPORTED\n\nnamespace folly {\nnamespace detail {\nnamespace {\n\n// This is obviously not amazing\n// but if perf didn't start properly,\n// we won't get any results.\ntemplate <typename Test>\nvoid retryWithTimeOuts(Test test) {\n  for (auto timeOut = std::chrono::seconds(1);\n       timeOut != std::chrono::seconds(10);\n       ++timeOut) {\n    if (test(timeOut)) {\n      return;\n    }\n  }\n}\n\nTEST(PerfScopedTest, Stat) {\n  SKIP_IF(!std::filesystem::exists(kPerfBinaryPath)) << \"Missing perf binary\";\n  std::string output;\n\n  retryWithTimeOuts([&](auto timeOut) {\n    {\n      PerfScoped perf{{\"stat\"}, &output};\n      std::this_thread::sleep_for(timeOut);\n    }\n    return !output.empty();\n  });\n\n  ASSERT_THAT(\n      output, ::testing::HasSubstr(\"Performance counter stats for process id\"));\n}\n\nTEST(PerfScopedTest, Move) {\n  SKIP_IF(!std::filesystem::exists(kPerfBinaryPath)) << \"Missing perf binary\";\n  std::string output;\n\n  retryWithTimeOuts([&](auto timeOut) {\n    {\n      PerfScoped assignHere;\n      {\n        PerfScoped fromHere{{\"stat\"}, &output};\n        assignHere = std::move(fromHere);\n        std::this_thread::sleep_for(timeOut);\n      }\n      // because assign here is still alive, should not do anything.\n      EXPECT_TRUE(output.empty());\n    }\n    std::this_thread::sleep_for(timeOut);\n\n    // Now that all guards are off, should be fine.\n    return !output.empty();\n  });\n\n  ASSERT_FALSE(output.empty());\n}\n\nTEST(PerfScopedTest, Record) {\n  SKIP_IF(!std::filesystem::exists(kPerfBinaryPath)) << \"Missing perf binary\";\n  std::string output;\n\n  retryWithTimeOuts([&](auto timeOut) {\n    {\n      PerfScoped perf{{\"record\"}, &output};\n      std::this_thread::sleep_for(timeOut);\n    }\n    return !output.empty();\n  });\n\n  ASSERT_FALSE(output.empty());\n}\n\nTEST(PerfScopedTest, StatNoOutput) {\n  SKIP_IF(!std::filesystem::exists(kPerfBinaryPath)) << \"Missing perf binary\";\n  // Just verifying that this doesn't crash.\n  PerfScoped perf{{\"stat\"}};\n  std::this_thread::sleep_for(std::chrono::seconds(1));\n}\n\n} // namespace\n} // namespace detail\n} // namespace folly\n\n#endif // defined(__linux__)\n"
  },
  {
    "path": "folly/detail/test/SimpleSimdStringUtilsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/SimpleSimdStringUtils.h>\n\n#include <folly/algorithm/simd/detail/SimdPlatform.h>\n#include <folly/detail/SimpleSimdStringUtilsImpl.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace detail {\nnamespace {\n\ntemplate <typename Platform>\nbool hasSpaceOrCntrlSymbolsForPlatform(folly::StringPiece s) {\n  return SimpleSimdStringUtilsImpl<Platform>::hasSpaceOrCntrlSymbols(s);\n}\n\nvoid testHasSpaceOrCntrlSymbols(folly::StringPiece s, bool r) {\n  ASSERT_EQ(r, simdHasSpaceOrCntrlSymbols(s)) << s;\n\n  using namespace simd::detail;\n  ASSERT_EQ(r, hasSpaceOrCntrlSymbolsForPlatform<void>(s)) << s;\n\n#if FOLLY_SSE_PREREQ(4, 2)\n  ASSERT_EQ(\n      r, hasSpaceOrCntrlSymbolsForPlatform<SimdSse42Platform<std::uint8_t>>(s))\n      << s;\n#if defined(__AVX2__)\n  ASSERT_EQ(\n      r, hasSpaceOrCntrlSymbolsForPlatform<SimdAvx2Platform<std::uint8_t>>(s))\n      << s;\n#endif\n#endif\n\n#if FOLLY_AARCH64\n  ASSERT_EQ(\n      r,\n      hasSpaceOrCntrlSymbolsForPlatform<SimdAarch64Platform<std::uint8_t>>(s))\n      << s;\n#endif\n}\n\n// We also substantially rely on fuzzers for our testing\n\nTEST(SpaceOrCntrl, One) {\n  testHasSpaceOrCntrlSymbols(\"!\", false);\n}\n\nTEST(SpaceOrCntrl, EachSymbol) {\n  for (std::uint16_t uChar = 0;\n       uChar <= std::numeric_limits<std::uint8_t>::max();\n       ++uChar) {\n    bool expected = std::isspace(uChar) || std::iscntrl(uChar);\n\n    char c = static_cast<char>(uChar);\n    ASSERT_NO_FATAL_FAILURE(testHasSpaceOrCntrlSymbols({&c, 1u}, expected));\n  }\n}\n\nTEST(SpaceOrCntrl, ExplicitExamples) {\n  using TestCase = std::pair<std::string, bool>;\n\n  std::pair<std::string, bool> testCases[] = {\n      TestCase{\" \", true},\n      TestCase{\"123\\t\", true},\n      TestCase{\"123\", false},\n      TestCase{\"abA__11\", false},\n      TestCase{std::string(\"123\") + char(1), true},\n  };\n  for (const auto& testCase : testCases) {\n    ASSERT_NO_FATAL_FAILURE(\n        testHasSpaceOrCntrlSymbols(testCase.first, testCase.second));\n  }\n}\n\n} // namespace\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/test/SplitStringSimdTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/SplitStringSimd.h>\n#include <folly/detail/SplitStringSimdImpl.h>\n\n#include <folly/FBString.h>\n#include <folly/FBVector.h>\n#include <folly/Range.h>\n#include <folly/portability/GTest.h>\n#include <folly/small_vector.h>\n\n#include <random>\n\nnamespace folly {\nnamespace detail {\n\n// making sure that basic scalar works\nTEST(SplitStringSimdTest, ByCharScalarKeepEmpty) {\n  using pieces = std::vector<folly::StringPiece>;\n\n  auto run = [](folly::StringPiece s) {\n    pieces res;\n    splitByCharScalar<false>(',', s, res);\n    return res;\n  };\n\n  ASSERT_EQ(run(\"\"), (pieces{\"\"}));\n  ASSERT_EQ(run(\"a\"), (pieces{\"a\"}));\n  ASSERT_EQ(run(\",a\"), (pieces{\"\", \"a\"}));\n  ASSERT_EQ(run(\"a,aa\"), (pieces{\"a\", \"aa\"}));\n  ASSERT_EQ(run(\"a,,aa\"), (pieces{\"a\", \"\", \"aa\"}));\n  ASSERT_EQ(run(\"aaaa,aaa,aa,a\"), (pieces{\"aaaa\", \"aaa\", \"aa\", \"a\"}));\n  ASSERT_EQ(run(\"aaaa,aaa,aa,a,\"), (pieces{\"aaaa\", \"aaa\", \"aa\", \"a\", \"\"}));\n  ASSERT_EQ(run(\",,,\"), (pieces{\"\", \"\", \"\", \"\"}));\n}\n\nTEST(SplitStringSimdTest, ByCharScalarIgnoreEmpty) {\n  using pieces = std::vector<folly::StringPiece>;\n\n  auto run = [](folly::StringPiece s) {\n    pieces res;\n    splitByCharScalar<true>(',', s, res);\n    return res;\n  };\n\n  ASSERT_EQ(run(\"\"), (pieces{}));\n  ASSERT_EQ(run(\"a\"), (pieces{\"a\"}));\n  ASSERT_EQ(run(\",a\"), (pieces{\"a\"}));\n  ASSERT_EQ(run(\"a,aa\"), (pieces{\"a\", \"aa\"}));\n  ASSERT_EQ(run(\"a,,aa\"), (pieces{\"a\", \"aa\"}));\n  ASSERT_EQ(run(\"aaaa,aaa,aa,a,\"), (pieces{\"aaaa\", \"aaa\", \"aa\", \"a\"}));\n  ASSERT_EQ(run(\",,,\"), (pieces{}));\n}\n\ntemplate <bool ignoreEmpty, typename Container>\nvoid testContainerSV(\n    folly::StringPiece s, const std::vector<folly::StringPiece>& expected) {\n  Container actual;\n  simdSplitByChar(',', s, actual, ignoreEmpty);\n\n  ASSERT_EQ(expected.size(), actual.size());\n\n  for (std::size_t i = 0; i != expected.size(); ++i) {\n    ASSERT_EQ(expected[i].data(), actual[i].data()) << s << \" : \" << i;\n    ASSERT_EQ(expected[i].size(), actual[i].size()) << s << \" : \" << i;\n  }\n}\n\n// shorter name to get 1 line auto format\ntemplate <bool ie>\nvoid testAllContainersOfSVs(\n    folly::StringPiece s, const std::vector<folly::StringPiece>& expected) {\n  testContainerSV<ie, folly::fbvector<folly::StringPiece>>(s, expected);\n  testContainerSV<ie, folly::fbvector<std::string_view>>(s, expected);\n\n  testContainerSV<ie, folly::small_vector<folly::StringPiece, 1>>(s, expected);\n  testContainerSV<ie, folly::small_vector<folly::StringPiece, 2>>(s, expected);\n  testContainerSV<ie, folly::small_vector<folly::StringPiece, 3>>(s, expected);\n  testContainerSV<ie, folly::small_vector<folly::StringPiece, 4>>(s, expected);\n  testContainerSV<ie, folly::small_vector<folly::StringPiece, 6>>(s, expected);\n  testContainerSV<ie, folly::small_vector<folly::StringPiece, 7>>(s, expected);\n  testContainerSV<ie, folly::small_vector<folly::StringPiece, 8>>(s, expected);\n\n  testContainerSV<ie, folly::small_vector<std::string_view, 1>>(s, expected);\n  testContainerSV<ie, folly::small_vector<std::string_view, 2>>(s, expected);\n  testContainerSV<ie, folly::small_vector<std::string_view, 3>>(s, expected);\n  testContainerSV<ie, folly::small_vector<std::string_view, 4>>(s, expected);\n  testContainerSV<ie, folly::small_vector<std::string_view, 6>>(s, expected);\n  testContainerSV<ie, folly::small_vector<std::string_view, 7>>(s, expected);\n  testContainerSV<ie, folly::small_vector<std::string_view, 8>>(s, expected);\n}\n\ntemplate <bool ignoreEmpty, typename Container>\nvoid testContainersOfStrings(\n    folly::StringPiece s, const std::vector<folly::StringPiece>& expected) {\n  Container actual;\n  simdSplitByChar(',', s, actual, ignoreEmpty);\n\n  ASSERT_EQ(expected.size(), actual.size()) << s;\n\n  for (std::size_t i = 0; i != expected.size(); ++i) {\n    ASSERT_EQ(expected[i], actual[i]) << s << \" : \" << i;\n  }\n}\n\ntemplate <bool ie>\nvoid testAllContainersOfStrings(\n    folly::StringPiece s, const std::vector<folly::StringPiece>& expected) {\n  testContainersOfStrings<ie, std::vector<std::string>>(s, expected);\n  testContainersOfStrings<ie, folly::fbvector<std::string>>(s, expected);\n\n  testContainersOfStrings<ie, std::vector<folly::fbstring>>(s, expected);\n  testContainersOfStrings<ie, folly::fbvector<folly::fbstring>>(s, expected);\n}\n\ntemplate <bool ignoreEmpty>\nvoid runTestStringSplitOneType(folly::StringPiece s) {\n  std::vector<folly::StringPiece> expected;\n  splitByCharScalar<ignoreEmpty>(',', s, expected);\n\n  std::vector<std::vector<folly::StringPiece>> actuals;\n\n  actuals.emplace_back();\n  simdSplitByChar(',', s, actuals.back(), ignoreEmpty);\n\n#if FOLLY_SSE_PREREQ(4, 2)\n  actuals.emplace_back();\n  PlatformSimdSplitByChar<\n      simd::detail::SimdSse42Platform<std::uint8_t>,\n      ignoreEmpty>{}(',', s, actuals.back());\n#if defined(__AVX2__)\n  actuals.emplace_back();\n  PlatformSimdSplitByChar<\n      simd::detail::SimdAvx2Platform<std::uint8_t>,\n      ignoreEmpty>{}(',', s, actuals.back());\n#endif\n#endif\n\n#if FOLLY_AARCH64\n  actuals.emplace_back();\n  PlatformSimdSplitByChar<\n      simd::detail::SimdAarch64Platform<std::uint8_t>,\n      ignoreEmpty>{}(',', s, actuals.back());\n#endif\n\n  for (const auto& actual : actuals) {\n    ASSERT_EQ(expected.size(), actual.size()) << s;\n\n    for (std::size_t i = 0; i != expected.size(); ++i) {\n      ASSERT_EQ(expected[i].data(), actual[i].data()) << s << \" : \" << i;\n      ASSERT_EQ(expected[i].size(), actual[i].size()) << s << \" : \" << i;\n    }\n  }\n\n  testAllContainersOfSVs<ignoreEmpty>(s, expected);\n  testAllContainersOfStrings<ignoreEmpty>(s, expected);\n}\n\nvoid runTestStringSplit(folly::StringPiece s) {\n  runTestStringSplitOneType<false>(s);\n  runTestStringSplitOneType<true>(s);\n}\n\nstd::string repeat(std::string_view substr, std::size_t n) {\n  std::string res;\n  while (n--) {\n    res.append(substr);\n  }\n  return res;\n}\n\n// We also do fuzzing for covering more cases.\nTEST(SplitStringSimd, ByChar) {\n  runTestStringSplit(\"\");\n  runTestStringSplit(\",\");\n  runTestStringSplit(\",,\");\n  runTestStringSplit(\",,,\");\n  runTestStringSplit(\",a,,aa\");\n  runTestStringSplit(\"aa,aaa,aaaa\");\n  runTestStringSplit(\"aa,aaa,,,aaa,,a\");\n\n  runTestStringSplit(repeat(\"a,\", 100));\n  runTestStringSplit(repeat(\"aa,\", 100));\n  // every char (the bigger cases come from older version)\n  runTestStringSplit(repeat(\",\", 255));\n  runTestStringSplit(repeat(\",\", 512));\n  runTestStringSplit(repeat(\",\", 512 + 16));\n  runTestStringSplit(repeat(\",\", 512 + 32));\n  runTestStringSplit(repeat(\",\", 512 + 64));\n  runTestStringSplit(\n      repeat(\",\", std::numeric_limits<std::uint16_t>::max() + 512 + 16));\n\n  // special case: triggered shift right by 32 on uint32\n  {\n    constexpr std::string_view kTestData = \"ong_history_by_pagetype_convr:0,\";\n    static_assert(kTestData.size() == 32);\n\n    alignas(32) std::array<char, 32> buf;\n    std::copy(kTestData.begin(), kTestData.end(), buf.begin());\n    runTestStringSplit({buf.data(), buf.size()});\n  }\n}\n\nTEST(SplitStringSimd, ByCharTestDifferentOffsets) {\n  alignas(32) std::array<char, 100> buf;\n\n  std::mt19937 gen;\n  std::uniform_int_distribution<> dis(0, 1);\n  for (auto& c : buf) {\n    c = dis(gen) ? ',' : 'a';\n  }\n\n  for (auto f = buf.begin(); f != buf.end(); ++f) {\n    for (auto l = f; l != buf.end(); ++l) {\n      runTestStringSplit({f, l});\n    }\n  }\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/test/StaticSingletonManagerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/StaticSingletonManager.h>\n\n#include <folly/lang/Keep.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace detail {\n\nFOLLY_ATTR_WEAK void check_doit() {}\n\nnamespace {\ntemplate <bool Noexcept>\nstruct MayThrow {\n  FOLLY_NOINLINE MayThrow() noexcept(Noexcept) { check_doit(); }\n  FOLLY_NOINLINE ~MayThrow() { check_doit(); }\n};\n} // namespace\n\nextern \"C\" FOLLY_KEEP int* check() {\n  return &createGlobal<int, void>();\n}\n\nextern \"C\" FOLLY_KEEP void* check_throw() {\n  return &createGlobal<MayThrow<false>, void>();\n}\n\nextern \"C\" FOLLY_KEEP void* check_nothrow() {\n  return &createGlobal<MayThrow<true>, void>();\n}\n\ntemplate <typename Impl>\nstruct StaticSingletonManagerTest : public testing::TestWithParam<Impl> {};\nTYPED_TEST_SUITE_P(StaticSingletonManagerTest);\n\ntemplate <typename Impl, typename T>\nstruct Tag {};\n\ntemplate <int I>\nusing Int = std::integral_constant<int, I>;\n\nTYPED_TEST_P(StaticSingletonManagerTest, example) {\n  using K = TypeParam;\n\n  using T = std::integral_constant<int, 3>;\n\n  auto& i = K::template create<T, Tag<K, char>>();\n  EXPECT_EQ(T::value, i);\n\n  auto& j = K::template create<T, Tag<K, char>>();\n  EXPECT_EQ(&i, &j);\n  EXPECT_EQ(T::value, j);\n\n  auto& k = K::template create<T, Tag<K, char*>>();\n  EXPECT_NE(&i, &k);\n  EXPECT_EQ(T::value, k);\n\n  typename K::template ArgCreate<true> m_arg{tag<T, Tag<K, int>>};\n  EXPECT_EQ(nullptr, K::template get_existing_cached<T>(m_arg));\n  EXPECT_EQ(nullptr, K::template get_existing<T>(m_arg));\n\n  auto& m = K::template create<T>(m_arg);\n  EXPECT_NE(&i, &m);\n  EXPECT_EQ(T::value, m);\n  EXPECT_EQ(&m, K::template get_existing_cached<T>(m_arg));\n  EXPECT_EQ(&m, K::template get_existing<T>(m_arg));\n\n  typename K::template ArgCreate<true> n_arg{tag<T, Tag<K, int>>};\n  EXPECT_EQ(nullptr, K::template get_existing_cached<T>(n_arg));\n  EXPECT_EQ(&m, K::template get_existing<T>(n_arg));\n  EXPECT_EQ(&m, &K::template create<T>(n_arg));\n}\n\nREGISTER_TYPED_TEST_SUITE_P( //\n    StaticSingletonManagerTest,\n    example);\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    sans_rtti, StaticSingletonManagerTest, StaticSingletonManagerSansRtti);\n#if FOLLY_HAS_RTTI\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    with_rtti, StaticSingletonManagerTest, StaticSingletonManagerWithRtti);\n#endif\nstruct StaticSingletonManagerTestType : StaticSingletonManager {};\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    selection, StaticSingletonManagerTest, StaticSingletonManagerTestType);\n\nstruct CreateGlobalTest : testing::Test {};\n\nTEST_F(CreateGlobalTest, example) {\n  using T = std::integral_constant<int, 3>;\n\n  auto& i = createGlobal<T, Tag<void, char>>();\n  EXPECT_EQ(T::value, i);\n\n  auto& j = createGlobal<T, Tag<void, char>>();\n  EXPECT_EQ(&i, &j);\n  EXPECT_EQ(T::value, j);\n\n  auto& k = createGlobal<T, Tag<void, char*>>();\n  EXPECT_NE(&i, &k);\n  EXPECT_EQ(T::value, k);\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/test/ThreadLocalBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/Benchmark.h>\n#include <folly/ThreadLocal.h>\n#include <folly/synchronization/test/Barrier.h>\n\nusing namespace folly;\n\nnamespace folly {\nnamespace threadlocal_detail {\n\ntemplate <typename Tag>\nstruct ThreadLocalTestHelper {\n  using Meta = StaticMeta<Tag, void>;\n  using TLElem = ThreadLocal<int, Tag>;\n  std::vector<TLElem> elements;\n};\n\nvoid measureAccessAllThreads(\n    int iters, uint32_t totalThreads, uint32_t tlCount, uint32_t spreadPerTL) {\n  struct Tag {};\n\n  ThreadLocalTestHelper<Tag> helper;\n\n  std::vector<std::thread> threads;\n  std::vector<std::unique_ptr<test::Barrier>> threadBarriers;\n  test::Barrier allThreadsBarriers{static_cast<size_t>(totalThreads + 1)};\n\n  BENCHMARK_SUSPEND {\n    helper.elements.reserve(tlCount);\n    for (uint32_t i = 0; i < tlCount; ++i) {\n      helper.elements.emplace_back();\n    }\n    for (uint32_t i = 0; i < totalThreads; ++i) {\n      threadBarriers.push_back(std::make_unique<test::Barrier>(2));\n      threads.emplace_back([&, index = i]() {\n        for (uint32_t tlIdx = 0; tlIdx < tlCount; ++tlIdx) {\n          uint32_t startIndex = tlIdx % totalThreads;\n          uint32_t endIndex = (tlIdx + spreadPerTL) % totalThreads;\n          if (startIndex < endIndex) {\n            if (index >= startIndex && index <= endIndex) {\n              *helper.elements[tlIdx] = tlIdx;\n            }\n          } else {\n            if (!(index >= endIndex && index <= startIndex)) {\n              *helper.elements[tlIdx] = tlIdx;\n            }\n          }\n        }\n        allThreadsBarriers.wait();\n        threadBarriers[index]->wait();\n      });\n    }\n\n    // Wait for all threads to start.\n    allThreadsBarriers.wait();\n  }\n\n  [[maybe_unused]] uint64_t countFound = 0;\n  [[maybe_unused]] uint64_t sum = 0;\n  for (int32_t k = 0; k < iters; k++) {\n    for (uint32_t i = 0; i < tlCount; ++i) {\n      for (auto& elem : helper.elements[i].accessAllThreads()) {\n        sum += elem;\n        countFound++;\n      }\n    }\n  }\n\n  BENCHMARK_SUSPEND {\n    while (!threads.empty()) {\n      threadBarriers.back()->wait();\n      threads.back().join();\n      threads.pop_back();\n      threadBarriers.pop_back();\n    }\n  }\n}\n\n} // namespace threadlocal_detail\n} // namespace folly\n\nvoid accessAllThreads(\n    int iters, uint32_t totalThreads, uint32_t tlCount, uint32_t spreadPerTL) {\n  folly::threadlocal_detail::measureAccessAllThreads(\n      iters, totalThreads, tlCount, spreadPerTL);\n}\n\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(accessAllThreads, 100t_100c_1s, 100, 100, 1)\nBENCHMARK_NAMED_PARAM(accessAllThreads, 100t_10000c_10s, 100, 10000, 10)\nBENCHMARK_NAMED_PARAM(accessAllThreads, 1000t_1000c_25s, 1000, 1000, 25)\nBENCHMARK_NAMED_PARAM(accessAllThreads, 2000t_20000c_50s, 2000, 20000, 50)\nBENCHMARK_DRAW_LINE();\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/detail/test/ThreadLocalDetailTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n#include <folly/Synchronized.h>\n#include <folly/ThreadLocal.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/test/Barrier.h>\n\nnamespace folly {\nnamespace threadlocal_detail {\n\nclass ThreadLocalDetailTest : public ::testing::Test {};\n\ntemplate <typename Tag>\nstruct ThreadLocalTestHelper {\n  using Meta = StaticMeta<Tag, void>;\n  using TLElem = ThreadLocal<int, Tag>;\n  std::vector<TLElem> elements;\n};\n\nTEST_F(ThreadLocalDetailTest, Basic) {\n  struct Tag {};\n\n  ThreadLocalTestHelper<Tag> helper;\n  auto& meta = ThreadLocalTestHelper<Tag>::Meta::instance();\n\n  // No TL object created. Count should be 0.\n  ASSERT_EQ(meta.totalElementWrappers_.load(), 0);\n\n  const int32_t count = 16;\n  helper.elements.reserve(count);\n  for (int32_t i = 0; i < count; ++i) {\n    helper.elements.emplace_back();\n  }\n\n  // TL wrapper obejcts created but no thread has accessed its\n  // local copy. Wrappers should still be 0.\n  ASSERT_EQ(meta.totalElementWrappers_.load(), 0);\n\n  // Access 1st element. A wrapper array will be allocated. One for\n  // the current thread. Vector growth is not precise to minimize churn. Can\n  // only check it should be >= 1.\n  *helper.elements[0] = 0;\n  ASSERT_GE(meta.totalElementWrappers_.load(), 1);\n\n  for (int32_t i = 0; i < count; ++i) {\n    *helper.elements[i] = i;\n  }\n  ASSERT_GE(meta.totalElementWrappers_.load(), count);\n}\n\n// Test the totalElementWrappers_ grows and shrinks as threads come and go.\nTEST_F(ThreadLocalDetailTest, MultiThreadedTest) {\n  struct Tag {};\n\n  ThreadLocalTestHelper<Tag> helper;\n  auto& meta = ThreadLocalTestHelper<Tag>::Meta::instance();\n\n  const int32_t count = 1000;\n  helper.elements.reserve(count);\n  for (int32_t i = 0; i < count; ++i) {\n    helper.elements.emplace_back();\n  }\n  ASSERT_EQ(meta.totalElementWrappers_.load(), 0);\n\n  for (int32_t i = 0; i < count; ++i) {\n    *helper.elements[i] = i;\n  }\n  ASSERT_GE(meta.totalElementWrappers_.load(), count);\n\n  std::vector<std::thread> threads;\n  std::vector<std::unique_ptr<test::Barrier>> threadBarriers(count);\n  test::Barrier allThreadsBarriers{count + 1};\n\n  for (int32_t i = 0; i < count; ++i) {\n    threadBarriers[i] = std::make_unique<test::Barrier>(2);\n    threads.emplace_back([&, index = i]() {\n      // This thread's vector will sized to have index elements at least.\n      *helper.elements[index] = index;\n      allThreadsBarriers.wait();\n      threadBarriers[index]->wait();\n    });\n  }\n\n  // Wait for all threads to start.\n  allThreadsBarriers.wait();\n\n  // check totalElementWrappers_ is within expected range. Due to vector growth,\n  // we cannot assume precise counts but can use a crude range. Thread i touches\n  // thread local with index i, and its array should be a bit over i in size.\n  // Total count will be count (baseline) plus summation(i) for i over\n  // range(num threads).\n  auto lowerBound = [](int32_t numThreads) {\n    return numThreads * (numThreads - 1) / 2 + (count);\n  };\n\n  auto upperBound = [](int32_t numThreads) { return (numThreads + 2) * count; };\n\n  int32_t threadBarriersIndex = count - 1;\n  while (!threads.empty()) {\n    ASSERT_GE(meta.totalElementWrappers_.load(), lowerBound(threads.size()));\n    ASSERT_LE(meta.totalElementWrappers_.load(), upperBound(threads.size()));\n    threadBarriers[threadBarriersIndex]->wait();\n    threads.back().join();\n    threads.pop_back();\n    threadBarriersIndex -= 1;\n  }\n}\n\n// Test the totalElementWrappers_ is stable if TL variables come and go.\nTEST_F(ThreadLocalDetailTest, TLObjectsChurn) {\n  struct Tag {};\n\n  Synchronized<ThreadLocalTestHelper<Tag>> helper;\n  auto& meta = ThreadLocalTestHelper<Tag>::Meta::instance();\n\n  const int32_t count = 1000;\n  helper.wlock()->elements.reserve(count);\n  for (int32_t i = 0; i < count; ++i) {\n    helper.wlock()->elements.emplace_back();\n  }\n  ASSERT_EQ(meta.totalElementWrappers_.load(), 0);\n\n  for (int32_t i = 0; i < count; ++i) {\n    *helper.wlock()->elements[i] = i;\n  }\n  ASSERT_GE(meta.totalElementWrappers_.load(), count);\n\n  std::vector<std::thread> threads;\n  std::vector<std::unique_ptr<test::Barrier>> threadBarriers;\n  test::Barrier allThreadsBarriers{count + 1};\n\n  for (int32_t i = 0; i < count; ++i) {\n    threadBarriers.push_back(std::make_unique<test::Barrier>(2));\n    threads.emplace_back([&, index = i]() {\n      *helper.wlock()->elements[index] = index;\n      allThreadsBarriers.wait();\n\n      // wait once for main thread to replace the index entry with a new TL\n      // variable.\n      threadBarriers[index]->wait();\n      // This thread's vector will sized to have index elements at least.\n      *helper.wlock()->elements[index] = index;\n      // Wait to exit.\n      allThreadsBarriers.wait();\n    });\n  }\n\n  // Wait for all threads to start.\n  allThreadsBarriers.wait();\n\n  auto lowerBound = [](int32_t numThreads) {\n    return numThreads * (numThreads - 1) / 2 + (count);\n  };\n\n  auto upperBound = [](int32_t numThreads) { return (numThreads + 2) * count; };\n\n  // Replace each element with a new one. Overall wrappers should stay stable as\n  // freed up id get recycled.\n  for (int32_t i = 0; i < count; ++i) {\n    helper.wlock()->elements[i] = {};\n    *helper.wlock()->elements[i] = 0;\n    ASSERT_EQ(meta.nextId_, count + 1);\n    threadBarriers[i]->wait();\n    ASSERT_GE(meta.totalElementWrappers_.load(), lowerBound(threads.size()));\n    ASSERT_LE(meta.totalElementWrappers_.load(), upperBound(threads.size()));\n  }\n\n  allThreadsBarriers.wait();\n  for (int32_t i = 0; i < count; ++i) {\n    threads[i].join();\n  }\n  threads.clear();\n  threadBarriers.clear();\n}\n\n// Test race between tl.reset() and threads exiting.\nTEST_F(ThreadLocalDetailTest, TLResetAndThreadExitRace) {\n  struct Data {\n    int value;\n  };\n\n  std::atomic<uint32_t> numTLObjects{0};\n\n  folly::ThreadLocalPtr<Data, Data>* tlObject =\n      new folly::ThreadLocalPtr<Data, Data>();\n\n  const int32_t count = 256;\n  std::vector<std::thread> threads;\n  test::Barrier allThreadsBarriers{count + 1};\n\n  threads.reserve(count);\n  for (int32_t i = 0; i < count; ++i) {\n    threads.emplace_back([&, index = i]() {\n      Data* d = new Data();\n      d->value = index;\n      numTLObjects++;\n      tlObject->reset(\n          d, [&, expected = index](Data* d, TLPDestructionMode mode) {\n            ASSERT_EQ(d->value, expected);\n            if (mode == TLPDestructionMode::THIS_THREAD) {\n              d->value = count + expected;\n            } else {\n              d->value = count * 2 + expected;\n            }\n            delete d;\n          });\n      allThreadsBarriers.wait();\n      // Thread will exit and delete tl version of tlObject.\n    });\n  }\n\n  // Wait for all threads to start.\n  allThreadsBarriers.wait();\n\n  // Destroy tlObject. The call will race with threads exiting\n  // and also trying to destroy their individual version of it.\n  delete tlObject;\n\n  for (int32_t i = 0; i < count; ++i) {\n    threads[i].join();\n  }\n  threads.clear();\n}\n\n// Test reallocation of elements array is not blocked by Accessor for\n// accessAllThreads\nTEST_F(ThreadLocalDetailTest, accessAllAndRealloc) {\n  struct Tag {};\n\n  ThreadLocalTestHelper<Tag> helper;\n\n  const int32_t count = 1000;\n  helper.elements.reserve(count);\n  for (int32_t i = 0; i < count; ++i) {\n    helper.elements.emplace_back();\n  }\n\n  // Elements 1..count are initialized\n  // under accessor later.\n  *helper.elements[0] = 0;\n\n  std::thread spawnMoreThreads;\n  const int32_t countSubThreads = 128;\n  std::vector<std::thread> threads;\n  test::Barrier threadsBarrier(countSubThreads + 1);\n  // Barrier to ensure all threads started by\n  // spawnMoreThreads is done after the Accessor is created.\n  test::Barrier spawnHelperBarrier(2);\n\n  spawnMoreThreads = std::thread([&]() {\n    spawnHelperBarrier.wait();\n    // create a bunch of threads. let them assign some value to the elemnts\n    // other than one at index 0. This will cause elements array to be\n    // reallocated a few times. They should not get blocked by presence of the\n    // accessAllThreads accessor.\n    for (int32_t i = 0; i < countSubThreads; ++i) {\n      threads.emplace_back([count = count, &helper, &threadsBarrier]() {\n        for (int32_t i = 1; i < count; ++i) {\n          *helper.elements[i] = 1;\n        }\n        threadsBarrier.wait();\n      });\n    }\n  });\n\n  {\n    auto accessor = helper.elements[0].accessAllThreads();\n    spawnHelperBarrier.wait();\n    // Thread holding accessor can itself also update any other\n    // TL, trigger reallocs from it and not get stuck.\n    for (int32_t i = 1; i < count; ++i) {\n      *helper.elements[i] = i;\n    }\n    // Helper thread will spawn all the countSubThreads and each\n    // will be able to setup a value for elements[1] without being\n    // blocked on 'accessor'. They will not be able to exit, so we\n    // only wait here for each to be done assigning to element[1]\n    // but not to exit.\n    threadsBarrier.wait();\n  }\n  spawnMoreThreads.join();\n  for (int32_t i = 0; i < countSubThreads; ++i) {\n    threads[i].join();\n  }\n}\n\n} // namespace threadlocal_detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/test/TrapOnAvx512TestNegative.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/TrapOnAvx512.h>\n\n#include <folly/portability/GTest.h>\n\nnamespace folly::detail {\n\n// This target is not built with BOLT `--trap-avx512`, so it should always\n// return false (including on non-x64 platforms).\nTEST(TrapOnAvx512TestNegative, Basic) {\n  ASSERT_FALSE(hasTrapOnAvx512());\n}\n\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/detail/test/TypeListTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/TypeList.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing namespace detail;\n\nnamespace {\ntemplate <class T, class Ts, class = void>\nstruct IsApplicable_ : std::false_type {};\ntemplate <class T, class... Ts>\nstruct IsApplicable_<T, TypeList<Ts...>, void_t<MetaApply<T, Ts...>>>\n    : std::true_type {};\ntemplate <class T, class... Ts>\nusing IsApplicable = IsApplicable_<T, TypeList<Ts...>>;\n} // namespace\n\nTEST(TypeList, Basic) {\n  static_assert(TypeList<>::size() == 0);\n  static_assert(TypeList<int>::size() == 1);\n  static_assert(TypeList<int, short, float>::size() == 3);\n}\n\ntemplate <class T>\nusing AddPointer = T*;\n\nTEST(TypeList, Defer) {\n  static_assert(\n      std::is_same<MetaApply<MetaDefer<AddPointer, int>>, int*>::value);\n  static_assert(!IsApplicable<MetaDefer<AddPointer, int, short>>::value);\n  static_assert(!IsApplicable<MetaDefer<AddPointer, int&>>::value);\n}\n\nTEST(TypeList, Transform) {\n  using Fn = MetaQuote<AddPointer>;\n  using T1 = TypeTransform<TypeList<>, Fn>;\n  static_assert(std::is_same<T1, TypeList<>>::value);\n  using T2 = TypeTransform<TypeList<int>, Fn>;\n  static_assert(std::is_same<T2, TypeList<int*>>::value);\n  using T3 = TypeTransform<TypeList<int, short, void>, Fn>;\n  static_assert(std::is_same<T3, TypeList<int*, short*, void*>>::value);\n}\n\nusing Nil = Empty;\ntemplate <class Car, class Cdr = Nil>\nstruct Cons {};\n\nTEST(TypeList, Fold) {\n  using Fn = MetaQuote<Cons>;\n  using T1 = TypeFold<TypeList<int, short, void>, Nil, Fn>;\n  using E1 = Cons<int, Cons<short, Cons<void, Nil>>>;\n  static_assert(std::is_same<T1, E1>::value);\n\n  using T2 = TypeFold<TypeList<int, short, void, int*, short*, void*>, Nil, Fn>;\n  using E2 = Cons<\n      int,\n      Cons<short, Cons<void, Cons<int*, Cons<short*, Cons<void*, Nil>>>>>>;\n  static_assert(std::is_same<T2, E2>::value);\n\n  using T3 = TypeReverseFold<TypeList<int, short, void>, Nil, MetaFlip<Fn>>;\n  using E3 = Cons<void, Cons<short, Cons<int, Nil>>>;\n  static_assert(std::is_same<T3, E3>::value);\n\n  using T4 = TypeReverseFold<\n      TypeList<int, short, void, int*, short*, void*>,\n      Nil,\n      MetaFlip<Fn>>;\n  using E4 = Cons<\n      void*,\n      Cons<short*, Cons<int*, Cons<void, Cons<short, Cons<int, Nil>>>>>>;\n  static_assert(std::is_same<T4, E4>::value);\n}\n\nTEST(TypeList, Unique) {\n  using T1 = TypeUnique<TypeList<int, int, int, short, int, short>>;\n  static_assert(std::is_same<T1, TypeList<int, short>>::value);\n\n  using T2 = TypeReverseUnique<TypeList<int, int, int, short, int, short>>;\n  static_assert(std::is_same<T2, TypeList<short, int>>::value);\n}\n\nTEST(TypeList, PushFront) {\n  using T1 = TypePushFront<TypeList<>, int, short>;\n  static_assert(std::is_same<T1, TypeList<int, short>>::value);\n\n  using T2 = TypePushFront<T1, float, double, struct XXX>;\n  static_assert(\n      std::is_same<T2, TypeList<float, double, struct XXX, int, short>>::value);\n}\n\nTEST(TypeList, PushBack) {\n  using T1 = TypePushBack<TypeList<>, int, short>;\n  static_assert(std::is_same<T1, TypeList<int, short>>::value);\n\n  using T2 = TypePushBack<T1, float, double, struct XXX>;\n  static_assert(\n      std::is_same<T2, TypeList<int, short, float, double, struct XXX>>::value);\n}\n\nTEST(TypeList, Join) {\n  using T1 = TypeJoin<\n      TypeList<TypeList<int>, TypeList<short, float>, TypeList<void*>>>;\n  static_assert(std::is_same<T1, TypeList<int, short, float, void*>>::value);\n}\n"
  },
  {
    "path": "folly/detail/test/UniqueInstanceTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/UniqueInstance.h>\n\n#include <tuple>\n\n#include <folly/String.h>\n#include <folly/Traits.h>\n#include <folly/portability/GTest.h>\n\nstruct Key1;\nstruct Key2;\nstruct TagA;\nstruct TagB;\n\ntemplate <typename...>\nstruct Template0 {};\ntemplate <typename...>\nstruct Template1 {};\n\nnamespace folly {\nnamespace detail {\n\nclass UniqueInstanceDeathTest : public testing::Test {};\n\ntemplate <template <typename...> class Z, typename... Key, typename... Mapped>\nstatic void make(tag_t<Key...> key, tag_t<Mapped...> mapped) {\n  std::ignore = UniqueInstance{tag<Z<Key..., Mapped...>>, key, mapped};\n}\n\nTEST_F(UniqueInstanceDeathTest, basic) {\n  make<Template0>(tag<Key1, Key2>, tag<TagA, TagB>);\n  make<Template0>(tag<Key1, Key2>, tag<TagA, TagB>); // same everything\n  make<Template1>(tag<Key1, Key2>, tag<TagA, TagB>); // different name\n  make<Template0>(tag<Key2, Key1>, tag<TagA, TagB>); // different key\n  EXPECT_DEATH( // different mapped\n      make<Template0>(tag<Key1, Key2>, tag<TagB, TagA>),\n      stripLeftMargin(R\"MESSAGE(\n        Overloaded unique instance over <Key1, Key2, ...> with differing trailing arguments:\n          Template0<Key1, Key2, TagA, TagB>\n          Template0<Key1, Key2, TagB, TagA>\n      )MESSAGE\"));\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/detail/test/tuple_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/tuple.h>\n\n#include <folly/Utility.h>\n#include <folly/functional/Invoke.h>\n#include <folly/portability/GTest.h>\n\n// IMPORTANT PRINCIPLES:\n//   - For a static test to run, it must be added to `check_static_tests()`.\n//   - Runtime test functions should be invoked one per gtest `TEST()`.\n//   - When possible, the tests here are `static_assert` compile-time.\n//     The goal is to ensure that the entire API is `constexpr`-correct.\n//   - The tests are written against the `litetup` and `stdtup` facades to make\n//     it clear where we deviate from the `std::tuple` interface.\n\nFOLLY_PUSH_WARNING\nFOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS\n\nnamespace folly::detail {\n\n// It's important that `lite_tuple` remain aggregate for fast runtime & builds.\nstatic_assert(std::is_aggregate_v<lite_tuple::tuple<>>);\nstatic_assert(std::is_aggregate_v<lite_tuple::tuple<int, char>>);\nstatic_assert(std::is_aggregate_v<lite_tuple::tuple<lite_tuple::tuple<int&&>>>);\n\n// The value list type `vtag_t` from `Traits.h` (and `value_list_concat_t`) is\n// fine, so it is not very compelling to use `lite_tuple::tuple` as a\n// structural type -- but it currently is one, unlike `std::tuple`.\ninline constexpr vtag_t<lite_tuple::tuple{1337, lite_tuple::tuple{'c'}}>\n    lite_tuple_is_structural;\n\n// Better UX than `assert()` in constexpr tests.\nconstexpr void test(bool ok) {\n  if (!ok) {\n    throw std::exception(); // Throwing in constexpr code is a compile error\n  }\n}\n\n// These forwarding facades for the utility functions let us test `std` and\n// `lite_tuple` with the same code without horrible macros.\nstruct stdtup {\n  template <size_t Idx>\n  static constexpr auto get = FOLLY_INVOKE_QUAL(std::get<Idx>);\n  static constexpr auto apply = FOLLY_INVOKE_QUAL(std::apply);\n  static constexpr auto forward_as_tuple =\n      FOLLY_INVOKE_QUAL(std::forward_as_tuple);\n  static constexpr auto tuple_cat = FOLLY_INVOKE_QUAL(std::tuple_cat);\n};\nstruct litetup {\n  template <size_t Idx>\n  static constexpr auto get = FOLLY_INVOKE_QUAL(lite_tuple::get<Idx>);\n  static constexpr auto apply = FOLLY_INVOKE_QUAL(lite_tuple::apply);\n  static constexpr auto forward_as_tuple =\n      FOLLY_INVOKE_QUAL(lite_tuple::forward_as_tuple);\n  static constexpr auto tuple_cat = FOLLY_INVOKE_QUAL(lite_tuple::tuple_cat);\n};\n\n// Records ctor order, and confirms that dtor order the reverse\n// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)\nstruct OrderTracker {\n  int myN_;\n  int& nRef_;\n  constexpr /*implicit*/ OrderTracker(int& n) : myN_(n++), nRef_(n) {}\n  constexpr ~OrderTracker() { test(myN_ == --nRef_); }\n};\n\ntemplate <template <typename...> class Tup, typename TupFn>\nstruct TupleTests {\n  // Also covers `std::tuple_size_v` and `std::tuple_element_t`\n  static constexpr bool check_get_refs_and_values() {\n    Tup e{};\n    static_assert(0 == std::tuple_size_v<decltype(e)>);\n\n    int n = 5;\n    // NOLINTNEXTLINE(performance-move-const-arg)\n    Tup<int, int&, int&&> t{n, n, std::move(n)};\n    static_assert(3 == std::tuple_size_v<decltype(t)>);\n    static_assert(std::is_same_v<int, std::tuple_element_t<0, decltype(t)>>);\n    static_assert(std::is_same_v<int&, std::tuple_element_t<1, decltype(t)>>);\n    static_assert(std::is_same_v<int&&, std::tuple_element_t<2, decltype(t)>>);\n\n    TupFn{}.template get<1>(t) += 10;\n    test(15 == TupFn{}.template get<2>(std::as_const(t))); // same address!\n    test(5 == TupFn{}.template get<0>(std::move(t)));\n\n    static_assert(std::is_same_v<int&, decltype(TupFn{}.template get<0>(t))>);\n    static_assert(std::is_same_v<int&, decltype(TupFn{}.template get<1>(t))>);\n    static_assert(std::is_same_v<int&, decltype(TupFn{}.template get<2>(t))>);\n\n    static_assert(\n        std::is_same_v<int&&, decltype(TupFn{}.template get<0>(std::move(t)))>);\n    static_assert(\n        std::is_same_v<int&, decltype(TupFn{}.template get<1>(std::move(t)))>);\n    static_assert(\n        std::is_same_v<int&&, decltype(TupFn{}.template get<2>(std::move(t)))>);\n\n    return true;\n  }\n\n  static constexpr bool check_move_only() {\n    Tup t{42, MoveOnly{}};\n    // Drive-by check of empty type optimization\n    static_assert(sizeof(int) == sizeof(t));\n    auto m = TupFn{}.template get<1>(std::move(t));\n    static_assert(std::is_same_v<decltype(m), MoveOnly>);\n    return true;\n  }\n\n  static constexpr bool check_ctor_dtor_ordering() {\n    int n = 0;\n    {\n      Tup<OrderTracker, OrderTracker, OrderTracker> t{n, n, n};\n      test(3 == n);\n      test(0 == TupFn{}.template get<0>(t).myN_);\n      test(1 == TupFn{}.template get<1>(t).myN_);\n      test(2 == TupFn{}.template get<2>(t).myN_);\n    }\n    test(0 == n);\n    return true;\n  }\n\n  static constexpr bool check_apply() {\n    Tup empty{};\n    test(17 == TupFn{}.apply([]() { return 17; }, empty));\n\n    Tup t{1, 2, 3};\n    TupFn{}.apply([t](auto... n) { test(t == Tup{n...}); }, t);\n    test(6 == TupFn{}.apply([](auto... n) { return (n + ...); }, t));\n    TupFn{}.apply([](auto&... n) { return ((n *= n), ...); }, t);\n    test(Tup{1, 4, 9} == t);\n\n    return true;\n  }\n\n  static constexpr bool check_apply_move() {\n    Tup moveT{MoveOnly{}, std::optional<MoveOnly>{}};\n    auto m = TupFn{}.apply(\n        [](auto&& m1, auto m2) {\n          static_assert(std::is_same_v<decltype(m1), MoveOnly&&>);\n          static_assert(std::is_same_v<decltype(m2), std::optional<MoveOnly>>);\n          return std::move(m1);\n        },\n        std::move(moveT));\n    static_assert(std::is_same_v<decltype(m), MoveOnly>);\n    return true;\n  }\n\n  static constexpr bool check_apply_ref() {\n    int n = 5;\n    TupFn{}.apply(\n        [](auto&& rref, auto&& lref) {\n          static_assert(std::is_same_v<int&&, decltype(rref)>);\n          static_assert(std::is_same_v<int&, decltype(lref)>);\n        },\n        // NOLINTNEXTLINE(performance-move-const-arg)\n        // @lint-ignore CLANGTIDY facebook-hte-MoveEvaluationOrder\n        TupFn{}.forward_as_tuple(std::move(n), n));\n    return true;\n  }\n\n  static constexpr bool check_forward_as_tuple() {\n    int x = 5;\n    // NOLINTNEXTLINE(performance-move-const-arg)\n    Tup t{x, std::move(x)};\n    static_assert(std::is_same_v<decltype(t), Tup<int, int>>);\n\n    int y = 5;\n    // NOLINTNEXTLINE(performance-move-const-arg)\n    // @lint-ignore CLANGTIDY facebook-hte-MoveEvaluationOrder\n    auto ft = TupFn{}.forward_as_tuple(y, std::move(y));\n    static_assert(std::is_same_v<decltype(ft), Tup<int&, int&&>>);\n    return true;\n  }\n\n  static constexpr bool check_tuple_cat() {\n    auto empty = TupFn{}.tuple_cat();\n    static_assert(0 == std::tuple_size_v<decltype(empty)>);\n\n    Tup a{2, 'a'};\n    test(TupFn{}.tuple_cat(a) == a);\n    test(TupFn{}.tuple_cat(empty, a) == a);\n    test(TupFn{}.tuple_cat(a, empty) == a);\n\n    Tup<Tup<>> b{};\n    Tup a_b{2, 'a', Tup{}};\n    test(TupFn{}.tuple_cat(a, b) == a_b);\n    test(TupFn{}.tuple_cat(a, empty, b) == a_b);\n\n    Tup<Tup<char>> c{{'c'}};\n    Tup a_b_c{2, 'a', Tup{}, Tup{'c'}};\n    test(TupFn{}.tuple_cat(a, b, c) == a_b_c);\n    test(TupFn{}.tuple_cat(empty, a, empty, b, empty, c, empty) == a_b_c);\n\n    // Identical types to ensure we correctly disambiguateby the \"outer\" base.\n    Tup t1{1}, t2{2}, t3{3}, t4{4};\n    test(TupFn{}.tuple_cat(t1, t2, t3, t4) == Tup{1, 2, 3, 4});\n\n    return true;\n  }\n\n  static constexpr bool check_tuple_cat_move() {\n    Tup t{MoveOnly{}};\n    auto t2 = TupFn{}.tuple_cat(std::move(t));\n    static_assert(std::is_same_v<decltype(t2), Tup<MoveOnly>>);\n    return true;\n  }\n\n  static constexpr bool check_tuple_cat_const() {\n    Tup t{2};\n    test(TupFn{}.tuple_cat(std::as_const(t)) == t);\n    return true;\n  }\n\n  static constexpr bool check_tuple_cat_refs() {\n    int n = 5;\n    { // lvalue ref only -- no `std::move`\n      auto t = TupFn{}.forward_as_tuple(n);\n      auto t2 = TupFn{}.tuple_cat(t);\n      test(&TupFn{}.template get<0>(t) == &TupFn{}.template get<0>(t2));\n      static_assert(std::is_same_v<decltype(t), decltype(t2)>);\n    }\n    { // adding an rvalue ref forces `std::move`\n      // NOLINTNEXTLINE(performance-move-const-arg)\n      // @lint-ignore CLANGTIDY facebook-hte-MoveEvaluationOrder\n      auto t = TupFn{}.forward_as_tuple(std::move(n), n);\n      // FIXME?: `lite_tuple` currently compiles without `std::move()` here,\n      // which is not ideal -- it'd be better to require it for rvalue refs.\n      auto t2 = TupFn{}.tuple_cat(std::move(t));\n      test(&TupFn{}.template get<0>(t) == &TupFn{}.template get<0>(t2));\n      test(&TupFn{}.template get<1>(t) == &TupFn{}.template get<1>(t2));\n      static_assert(std::is_same_v<decltype(t), decltype(t2)>);\n    }\n    return true;\n  }\n\n  static constexpr bool check_structured_binding() {\n    Tup t{'a', 7};\n    auto [a, seven] = t;\n    static_assert(std::is_same_v<decltype(a), char>);\n    test(a == 'a');\n    static_assert(std::is_same_v<decltype(seven), int>);\n    test(seven == 7);\n    return true;\n  }\n\n  static constexpr bool check_static_tests() {\n    constexpr bool is_lite = std::is_same_v<TupFn, litetup>;\n    static_assert(check_get_refs_and_values());\n    static_assert(check_move_only());\n    // `std::tuple` has unspecified ordering, which as of 2025 differs between\n    // `libc++` and `libstdc++` :'( :'(\n    if constexpr (is_lite) {\n      static_assert(check_ctor_dtor_ordering());\n    }\n    static_assert(check_apply());\n    static_assert(check_apply_move());\n    static_assert(check_apply_ref());\n    static_assert(check_forward_as_tuple());\n    static_assert(check_tuple_cat());\n    static_assert(check_tuple_cat_move());\n    static_assert(check_tuple_cat_const());\n    static_assert(check_tuple_cat_refs());\n    static_assert(check_structured_binding());\n    return true;\n  }\n\n  // Dynamic tests\n\n  static void check_move_only_unique_ptr() {\n    Tup t{std::make_unique<int>(7)};\n    auto p = TupFn{}.template get<0>(std::move(t));\n    EXPECT_EQ(7, *p);\n    EXPECT_EQ(nullptr, TupFn{}.template get<0>(t).get());\n  }\n\n  static void check_apply_move_unique_ptr() {\n    Tup moveT{std::make_unique<int>(7), MoveOnly{}};\n    auto p = TupFn{}.apply(\n        [](auto&& p, auto m) {\n          static_assert(std::is_same_v<decltype(m), MoveOnly>);\n          return std::move(p);\n        },\n        std::move(moveT));\n    EXPECT_EQ(7, *p);\n    EXPECT_EQ(nullptr, TupFn{}.template get<0>(moveT).get());\n  }\n\n  static constexpr void check_tuple_cat_move_unique_ptr() {\n    Tup t{std::make_unique<int>(2)};\n    auto t2 = TupFn{}.tuple_cat(std::move(t));\n    static_assert(std::is_same_v<decltype(t2), Tup<std::unique_ptr<int>>>);\n    EXPECT_EQ(2, *TupFn{}.template get<0>(t2));\n    EXPECT_EQ(nullptr, TupFn{}.template get<0>(t));\n  }\n};\n\nusing StdTests = TupleTests<std::tuple, stdtup>;\nusing LiteTests = TupleTests<lite_tuple::tuple, litetup>;\n\nstatic_assert(StdTests::check_static_tests());\nstatic_assert(LiteTests::check_static_tests());\n\nTEST(LiteTupleTest, std_move_only_unique_ptr) {\n  StdTests::check_move_only_unique_ptr();\n}\nTEST(LiteTupleTest, lite_move_only_unique_ptr) {\n  LiteTests::check_move_only_unique_ptr();\n}\n\nTEST(LiteTupleTest, std_apply_move_unique_ptr) {\n  StdTests::check_apply_move_unique_ptr();\n}\nTEST(LiteTupleTest, lite_apply_move_unique_ptr) {\n  LiteTests::check_apply_move_unique_ptr();\n}\n\nTEST(LiteTupleTest, std_tuple_cat_move_unique_ptr) {\n  StdTests::check_tuple_cat_move_unique_ptr();\n}\nTEST(LiteTupleTest, lite_tuple_cat_move_unique_ptr) {\n  LiteTests::check_tuple_cat_move_unique_ptr();\n}\n\nconstexpr bool check_lite_tuple_reverse_apply() { // no `std::reverse_apply`\n  lite_tuple::tuple empty{};\n  test(17 == lite_tuple::reverse_apply([]() { return 17; }, empty));\n\n  lite_tuple::tuple t{1, 2, 3};\n  lite_tuple::reverse_apply(\n      [](auto... n) {\n        test(lite_tuple::tuple{3, 2, 1} == lite_tuple::tuple{n...});\n      },\n      t);\n  lite_tuple::reverse_apply([](auto&... n) { return ((n *= n), ...); }, t);\n  test(lite_tuple::tuple{1, 4, 9} == t);\n\n  lite_tuple::tuple moveT{MoveOnly{}, std::optional<MoveOnly>{}};\n  auto m = lite_tuple::reverse_apply(\n      [](auto&& m1, auto m2) {\n        static_assert(std::is_same_v<decltype(m1), std::optional<MoveOnly>&&>);\n        static_assert(std::is_same_v<decltype(m2), MoveOnly>);\n        return std::move(m1);\n      },\n      std::move(moveT));\n  static_assert(std::is_same_v<decltype(m), std::optional<MoveOnly>>);\n\n  int n = 5;\n  lite_tuple::reverse_apply(\n      [](auto&& lref, auto&& rref) {\n        static_assert(std::is_same_v<int&&, decltype(rref)>);\n        static_assert(std::is_same_v<int&, decltype(lref)>);\n      },\n      // NOLINTNEXTLINE(performance-move-const-arg)\n      // @lint-ignore CLANGTIDY facebook-hte-MoveEvaluationOrder\n      lite_tuple::forward_as_tuple(std::move(n), n));\n\n  return true;\n}\n\nstatic_assert(check_lite_tuple_reverse_apply());\n\nTEST(LiteTupleTest, lite_tuple_reverse_apply) { // no `std::reverse_apply`\n  lite_tuple::tuple moveT{std::make_unique<int>(7), MoveOnly{}};\n  auto p = lite_tuple::reverse_apply(\n      [](auto m, auto&& p) {\n        static_assert(std::is_same_v<decltype(m), MoveOnly>);\n        return std::move(p);\n      },\n      std::move(moveT));\n  EXPECT_EQ(7, *p);\n  EXPECT_EQ(nullptr, lite_tuple::get<0>(moveT).get());\n}\n\n} // namespace folly::detail\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/detail/thread_local_globals.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/thread_local_globals.h>\n\n#include <system_error>\n\n#include <folly/detail/StaticSingletonManager.h>\n#include <folly/lang/Exception.h>\n#include <folly/portability/PThread.h>\n\nnamespace folly::detail {\n\nnamespace {\n\n/// thread_is_dying_global\n///\n/// This is a hack. The C runtime library provides no indication that a thread\n/// has ended the phase where thread_local variables may be destroyed. Were it\n/// to provide that indication, that would be used instead. Relies on library\n/// facilities which have signal to mark that a thread is dying.\n///\n/// In glibc, in the shutdown phase, start_thread first calls __call_tls_dtors,\n/// which runs destructors of thread_local variables, and then immediately calls\n/// __nptl_deallocate_tsd, which runs destructors of pthread thread-specific\n/// variables. There is no other indication of state change in the current\n/// thread.\n///\n/// https://github.com/bminor/glibc/blob/glibc-2.39/nptl/pthread_create.c#L451-L455\nstruct thread_is_dying_global {\n  pthread_key_t key{};\n\n  thread_is_dying_global() {\n    int ret = pthread_key_create(&key, nullptr);\n    if (ret != 0) {\n      throw_exception<std::system_error>(\n          ret, std::generic_category(), \"pthread_key_create failed\");\n    }\n  }\n};\n\n} // namespace\n\nbool thread_is_dying() {\n  auto& global = createGlobal<thread_is_dying_global, void>();\n  return !!pthread_getspecific(global.key);\n}\n\nvoid thread_is_dying_mark() {\n  auto& global = createGlobal<thread_is_dying_global, void>();\n  if (!pthread_getspecific(global.key)) {\n    pthread_setspecific(global.key, &global.key);\n  }\n}\n\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/detail/thread_local_globals.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly::detail {\n\n/// thread_is_dying\n///\n/// Queries whether the current thread is dying, as marked by companion function\n/// thread_is_dying_mark.\n///\n/// Useful to avoid constructing non-trivially-destructible thread_local\n/// variables, since they must later be destroyed.\n[[nodiscard]] bool thread_is_dying();\n\n/// thread_is_dying_mark\n///\n/// Marks the current thread as dying, to be queried by companion function\n/// thread_is_dying.\nvoid thread_is_dying_mark();\n\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/detail/tuple.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CppAttributes.h>\n#include <folly/Traits.h>\n#include <folly/lang/SafeAlias-fwd.h>\n\n/// Any file constructing `lite_tuple` should contain this after `#include`s:\n///   FOLLY_PUSH_WARNINGS\n///   FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS\n/// Followed by `FOLLY_POP_WARNING` at the bottom.  The reason is that\n/// `-Wmissing-braces` otherwise breaks deduction guides, and makes aggregate\n/// initialization a real headache to use:\n///   lite_tuple::tuple t{1, 2, 3}; // before :)\n///   lite_tuple::tuple<int> t{{{1}, {2}, {3}}}; // after :'(\n///\n/// At the same time, I don't want to give up on having it be an aggregate type,\n/// because (1) aggregate types should get copy elision when initialized from\n/// prvalues -- but a perfect-forwarding constructor would break that, and (2)\n/// not being an aggregate type **might** break register-pass optimizations.\n///\n/// So, instead I replace `-Wmissing-braces` by `-Wmissing-field-initializers`,\n/// which has its correctness benefits without any of the bad side effects.\n///\n/// FIXME: Remove when our warnings are fixed to be this way globally.\n#define FOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS \\\n  FOLLY_GNU_DISABLE_WARNING(\"-Wmissing-braces\") \\\n  FOLLY_GNU_ENABLE_WARNING(\"-Wmissing-field-initializers\")\n\nFOLLY_PUSH_WARNING\nFOLLY_DETAIL_LITE_TUPLE_ADJUST_WARNINGS\n\n/// A fast, incomplete `std::tuple` mimic with left-to-right construction\n/// ordering, for use in `folly/` internals.\n///\n/// IMPORTANT: Treat the class members of `entry` and `tuple_base` as PRIVATE,\n/// these implementation details are subject to change.  Only the `std::tuple`\n/// mimic APIs are supported as public.\n///\n/// BEWARE: This is NOT a drop-in `std::tuple` replacement. Some differences:\n///\n///   - `lite_tuple` prioritizes a minimal, fast-to-compile implementation, and\n///     so it lacks many `std::tuple` features.  For example, there are no\n///     converting constructors or comparators at present.\n///\n///   - NEW FEATURES SHOULD BE ADDED SPARINGLY -- at a minimum, benchmark the\n///     build of `//folly/coro/safe/...` (especially `test:async_closure_test`)\n///     to make sure there's no significant regression.\n///\n///   - Unlike `std::tuple`, `lite_tuple::tuple`:\n///       * Has a specified construction & destruction order -- left-to-right,\n///         of course.  Use curly-brace init to also order argument evaluation.\n///       * Is an aggregate type, which can provide better build & runtime\n///         performance.  Do not break this contract!\n///       * Is a structural type, but prefer `vtag_t` for most metaprogramming.\n///       * Likely has various minor differences of behavior, both due to\n///         aggregate initialization, and just overall lack of broad use.\n///         There's one such known caveat (see FIXME in the test).\n///\n/// That said, this IS an `std::tuple` mimic, and its test checks for identical\n/// behavior across a significant number of APIs.  Notably, `lite_tuple`:\n///   - Can store values, lvalue & rvalue references.\n///   - Has `get`, `tuple_cat`, `apply`, and `forward_as_tuple`.\n///   - Supports `std::tuple_size_v`, `std::tuple_element_t`, and therefore\n///     structured binding.\n///\n/// ## Why isn't this a public API?\n///\n/// Since the C++ committee has historically been unable to plan for ABI breaks,\n/// `std::tuple` will not get better in the foreseeable future, neither in terms\n/// of ordering, nor in terms of build speed.  For this reason, you MAY find it\n/// reasonable to graduate `lite_tuple` to a public `folly` API.  However, to\n/// make this a plausible idea, be prepared to put in much more work:\n///   - Hyrum's law will codify the implementation's actual behavior as\n///     contract.  To make this less problematic, you MUST add dramatically more\n///     API testing, closely following existing compliance tests for\n///     `std::tuple` (plus `tuplet` where appropriate) and clearly calling out\n///     where we deviate.\n///   - Put more effort into testing that stuff that had better not compile\n///     actually doesn't compile (see the rvalue FIXME, e.g.).\n///   - Implement runtime and build-time benchmarks, to help prevent\n///     future attempts at API bloat from regressing key metrics.\n///   - Document API extensions we MAY versus MUST NOT do.\n///\n///   - If you get a proper \"good tuple\" landed in `folly/` (or a dependency),\n///     that supersedes this I would be EXCITED to delete this code.  This is\n///     only used in internal implementation details, so it's easy to swap.\n///\n/// ## Prior art\n///\n/// The idea of an aggregate-type tuple is pretty old, and I am not sure where\n/// I've seen it first.  Some uses of this pattern:\n///   - `codeinred/tuplet` is a pretty nice standalone library, with an emphasis\n///     on runtime & build speed.  It is not used in `folly/` for two reasons:\n///     (a) it wouldn't pass the `lite_tuple` suite due to significant bugs in\n///     its rvalue reference support, (b) `folly` is cautious with taking deps,\n///     (c) it implements far more API than we need, and API bloat has costs.\n///   - `NVIDIA/stdexec/__detail/__tuple.hpp` is another notable example.\n///   - In 2024 (well after other such types), but before seeing either of the\n///     above, I wrote a named tuple called `type_idx_map`, using the same\n///     patterns.  The code wasn't committed, but is here for posterity:\n///     https://gist.github.com/snarkmaster/cfa209a4d05104a78769ca8062f382a0\n\nnamespace folly::detail::lite_tuple {\n\nnamespace detail {\n\ntemplate <size_t I, typename T>\nstruct entry {\n  using entry_type = T;\n  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] T entry_value;\n  auto operator<=>(const entry&) const = default;\n};\n\ntemplate <typename Seq, typename...>\nstruct tuple_base;\n\ntemplate <size_t... Is, typename... Ts>\nstruct tuple_base<std::index_sequence<Is...>, Ts...> : entry<Is, Ts>... {\n  using tuple_base_list = tag_t<entry<Is, Ts>...>;\n  auto operator<=>(const tuple_base&) const = default;\n};\n\n} // namespace detail\n\ntemplate <typename... Ts>\nstruct tuple : detail::tuple_base<std::index_sequence_for<Ts...>, Ts...> {\n  auto operator<=>(const tuple&) const = default;\n};\ntemplate <typename... Ts>\ntuple(Ts...) -> tuple<Ts...>;\n\ntemplate <size_t I, typename T>\nFOLLY_ALWAYS_INLINE constexpr T& get(detail::entry<I, T>& tup) noexcept {\n  return tup.entry_value;\n}\ntemplate <size_t I, typename T>\nFOLLY_ALWAYS_INLINE constexpr const T& get(\n    const detail::entry<I, T>& tup) noexcept {\n  return tup.entry_value;\n}\ntemplate <size_t I, typename T>\nFOLLY_ALWAYS_INLINE constexpr decltype(auto) get(\n    detail::entry<I, T>&& tup) noexcept {\n  using dst = decltype(static_cast<decltype(tup)>(tup).entry_value);\n  return static_cast<dst&&>(tup.entry_value);\n}\n\nFOLLY_ALWAYS_INLINE constexpr auto forward_as_tuple(auto&&... a) noexcept {\n  return tuple<decltype(a)&&...>{static_cast<decltype(a)>(a)...};\n}\n\nFOLLY_ALWAYS_INLINE constexpr decltype(auto) apply(auto&& fn, auto&& tup) {\n  using tupv = std::remove_reference_t<decltype(tup)>;\n  return [&]<size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {\n    return static_cast<decltype(fn)>(fn)(\n        get<Is>(static_cast<decltype(tup)>(tup))...);\n  }(std::make_index_sequence<std::tuple_size_v<tupv>>{});\n}\n\nFOLLY_ALWAYS_INLINE constexpr decltype(auto) reverse_apply(\n    auto&& fn, auto&& tup) {\n  using tupv = std::remove_reference_t<decltype(tup)>;\n  constexpr size_t Last = std::tuple_size_v<tupv> - 1;\n  return [&]<size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {\n    return static_cast<decltype(fn)>(fn)(\n        get<Last - Is>(static_cast<decltype(tup)>(tup))...);\n  }(std::make_index_sequence<Last + 1>{});\n}\n\n// `tuple_cat` implementation details.  Credit: This follows the `tuplet`\n// algorithm, which in turn appears to derive from Eric Niebler's\n// `tuple_cat.cpp` -- read its docs for another explanation:\n// https://github.com/ericniebler/meta/blob/master/example/tuple_cat.cpp\n//\n// Note that I did not keep `tuplet`'s GCC optimizations.\n//\n// NB: To be `folly`-idiomatic, this uses `type_list_concat_t`, which uses\n// recursive template instantiation.  This might compile slower than `tuplet`,\n// which uses fold expressions, but...  done is better than perfect here.\nnamespace detail {\n\n// Return a `tag_t` containing `sizeof...(ForEach)` copies of `T`\ntemplate <typename T, typename... ForEach>\nconsteval auto repeat_type(tag_t<ForEach...>) {\n  return tag<type_t<T, ForEach>...>;\n}\n\n// `Base` is a `struct entry` base class of a tuple-of-tuples.  Returns the\n// `tuple_base_list` of the inner tuple \"indexed\" by this `Base`.\ntemplate <typename Base>\nusing inner_tuple_base_list_t = typename std::remove_reference_t<\n    typename Base::entry_type>::tuple_base_list;\n\n// Given the bases of some `outerTuples...`, return a `tag_t` repeating the\n// corresponding base for each of the tuple's entries (cardinality of\n// `tuple_cat(outerTuples...)`).\n//\n// Concretely: If `B1` comes from `tuple<int, char>`, and `B2` from\n// `tuple<float>`, then the result is `tag_t<B1, B1, B2>`.\ntemplate <typename... Bases>\nconsteval auto outer_base_type_for_each_concat_entry(tag_t<Bases...>) {\n  return type_list_concat_t<\n      tag_t,\n      decltype(repeat_type<Bases>(inner_tuple_base_list_t<Bases>{}))...>{};\n}\n\n// Given the bases of some `outerTuples...`, return a `tag_t` containing the\n// \"inner\" base corresponding to each of the tuple's entries.\n//\n// IMPORTANT: Before looking up by inner base, one must `static_cast` to the\n// correct outer base -- otherwise, ambiguity would ensue when the same inner\n// base occurs in more than one outer tuple.\ntemplate <typename... Bases>\nconsteval auto inner_base_type_for_each_concat_entry(tag_t<Bases...>) {\n  return type_list_concat_t<tag_t, inner_tuple_base_list_t<Bases>...>{};\n}\n\n// `Outer` and `Inner` together form the base-class \"index\" for each entry in\n// the `tuple_cat` output.  To avoid ambiguity, we have to first resolve by the\n// `Outer` base, then by the `Inner` base to get the input tuple element.\ntemplate <typename... Outer, typename... Inner>\nconstexpr auto tuple_cat_impl(\n    auto tup_of_tup_refs, tag_t<Outer...>, tag_t<Inner...>)\n    -> tuple<typename Inner::entry_type...> {\n  return {\n      // These two casts & member accesses are meant as a build-time\n      // optimization so as to avoid using `get<>`.  I **think** the value\n      // category mapping is correct, but please suggest more tests.\n      //\n      // Needed if the destination type is an rref: check_tuple_cat_refs()\n      static_cast<typename Inner::entry_type>(\n          // Needed if a source tuple is by-rvalue: check_tuple_cat_move()\n          static_cast<typename Outer::entry_type&&>(\n              tup_of_tup_refs.Outer::entry_value)\n              .Inner::entry_value)...};\n}\n\n} // namespace detail\n\ntemplate <typename... Tups>\nFOLLY_ALWAYS_INLINE constexpr auto tuple_cat(Tups&&... tups) {\n  constexpr auto bases = typename tuple<Tups&&...>::tuple_base_list{};\n  return detail::tuple_cat_impl(\n      tuple<Tups&&...>{static_cast<Tups&&>(tups)...},\n      // Base class \"indexes\" into the tuple-of-tuples.  We'll walk through\n      // them in lockstep, querying (outer, inner) to pull out each entry.\n      detail::outer_base_type_for_each_concat_entry(bases),\n      detail::inner_base_type_for_each_concat_entry(bases));\n}\n\n} // namespace folly::detail::lite_tuple\n\nnamespace folly {\ntemplate <typename... As, safe_alias Default>\nstruct safe_alias_of<::folly::detail::lite_tuple::tuple<As...>, Default>\n    : safe_alias_of_pack<Default, As...> {};\n} // namespace folly\n\nnamespace std {\n\ntemplate <typename... Ts>\nstruct tuple_size<::folly::detail::lite_tuple::tuple<Ts...>>\n    : std::integral_constant<size_t, sizeof...(Ts)> {};\n\ntemplate <size_t I, typename... Ts>\nstruct tuple_element<I, ::folly::detail::lite_tuple::tuple<Ts...>> {\n  using type = ::folly::type_pack_element_t<I, Ts...>;\n};\n\n} // namespace std\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/docs/.gitignore",
    "content": "*.html\n"
  },
  {
    "path": "folly/docs/AtomicHashMap.md",
    "content": "`folly/AtomicHashmap.h`\n----------------------\n\n`folly/AtomicHashmap.h` introduces a synchronized UnorderedAssociativeContainer\nimplementation designed for extreme performance in heavily multithreaded\nenvironments (about 2-5x faster than tbb::concurrent_hash_map) and good memory\nusage properties.  Find and iteration are wait-free, insert has key-level lock\ngranularity, there is minimal memory overhead, and permanent 32-bit ids can be\nused to reference each element.\n\n\n### Limitations\n***\n\nAlthough it can provide extreme performance, AtomicHashmap has some unique\nlimitations as well.\n\n* The space for erased elements cannot be reclaimed (they are tombstoned\nforever) so it's generally not a good idea to use this if you're erasing things\na lot.\n\n* Only supports 32 or 64 bit keys - this is because they must be atomically\ncompare-and-swap'ed.\n\n* Growth beyond initialization reduces performance - if you don't know\nthe approximate number of elements you'll be inserting into the map, you\nprobably shouldn't use this class.\n\n* Must manage synchronization externally in order to modify values in the map\nafter insertion.  Lock pools are a common way to do this, or you may\nconsider using `folly::PackedSyncPtr<T>` as your `ValueT`.\n\n* Must define special reserved key values for empty, erased, and locked\nelements.\n\nFor a complete list of limitations and departures from the\nUnorderedAssociativeContainer concept, see `folly/AtomicHashMap.h`\n\n\n### Unique Features\n***\n\n* `value_type` references remain valid as long as the map itself.  Note this is\nnot true for most other probing hash maps which will move elements when\nrehashing, which is necessary for them to grow.  AtomicHashMap grows by chaining\nadditional slabs, so elements never need to be moved.\n\n* Unique 32-bit ids can be used to reference elements in the map via\n`iterator::getIndex()`.  This can be helpful to save memory in the rest of the\napplication by replacing 64-bit pointers or keys.\n\n* Iterators are never invalidated.  This means you can iterate through the map\nwhile simultaneously inserting and erasing.  This is particularly useful for\nnon-blocking map serialization.\n\n\n### Usage\n***\n\nUsage is similar to most maps, although note the conspicuous lack of operator[]\nwhich encourages non thread-safe access patterns.\n\nBelow is a synchronized key counter implementation that allows the counter\nvalues to be incremented in parallel with serializing all the values to a\nstring.\n\n```Cpp\n   class Counters {\n    private:\n     AtomicHashMap<int64_t,int64_t> ahm;\n\n    public:\n     explicit Counters(size_t numCounters) : ahm(numCounters) {}\n\n     void increment(int64_t obj_id) {\n       auto ret = ahm.insert(make_pair(obj_id, 1));\n       if (!ret.second) {\n         // obj_id already exists, increment\n         NoBarrier_AtomicIncrement(&ret.first->second, 1);\n       }\n     }\n\n     int64_t getValue(int64_t obj_id) {\n       auto ret = ahm.find(obj_id);\n       return ret != ahm.end() ? ret->second : 0;\n     }\n\n     // Serialize the counters without blocking increments\n     string toString() {\n       string ret = \"{\\n\";\n       ret.reserve(ahm.size() * 32);\n       for (const auto& e : ahm) {\n         ret += folly::to<string>(\n           \"  [\", e.first, \":\", NoBarrier_Load(&e.second), \"]\\n\");\n       }\n       ret += \"}\\n\";\n       return ret;\n     }\n   };\n```\n\n### Implementation\n***\n\nAtomicHashMap is a composition of AtomicHashArray submaps, which implement the\nmeat of the functionality.  Only one AHA is created on initialization, and\nadditional submaps are appended if the first one gets full.  If the AHM grows,\nthere will be multiple submaps that must be probed in series to find a given\nkey.  The more growth, the more submaps will be chained, and the slower it will\nget.  If the initial size estimate is good, only one submap will ever be created\nand performance will be optimal.\n\nAtomicHashArray is a fixed-size probing hash map (also referred to as an open\naddressed hash map) where hash collisions are resolved by checking subsequent\nelements.  This means that they can be allocated in slabs as arrays of\nvalue_type elements, have excellent cache performance, and have no memory\noverhead from storing pointers.\n\nThe algorithm is simple - when inserting, the key is hash-mod'ed to an offset,\nand that element-key is atomically compare-and-swap'ed with the locked key\nvalue.  If successful, the value is written and the element-key is unlocked by\nsetting it to the input key value.  If the compare fails, the next element is\ntried until success or the map is full.\n\nFinds are even simpler.  The key is hash-mod'ed to an offset, and the\nelement-key is examined.  If it is the same as the input key, the reference is\nreturned, if it's the empty key, failure is returned, otherwise the next key is\ntried.  This can be done wait-free without any atomic instructions because the\nelements are always in a valid state.\n\nErase is done by finding the key, then compare-and-swap'ing the element-key with\nthe reserved erased key value.  If the swap succeeds, return success, otherwise\nreturn failure (the element was erased by a competing thread).  If the key does\nnot exist, return failure.\n"
  },
  {
    "path": "folly/docs/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbsource//xplat/folly/docs:defs.bzl\", \"copy\", \"html\")\n\n#\n# Actual listing of source files\n#\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = html,\n    src = \"AtomicHashMap.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Benchmark.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Conv.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Dynamic.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"FBString.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"FBVector.md\",\n    support = [\"Fbvector--graphical_solutions.png\"],\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Format.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"GroupVarint.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Histogram.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"PackedSyncPtr.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Poly.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"ProducerConsumerQueue.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"SmallLocks.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"small_vector.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Synchronized.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"ThreadCachedInt.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"ThreadLocal.md\",\n)\n\nfbcode_target(\n    _kind = html,\n    src = \"Traits.md\",\n)\n\nfbcode_target(\n    _kind = copy,\n    path = \"style.css\",\n)\n"
  },
  {
    "path": "folly/docs/Benchmark.md",
    "content": "`folly/Benchmark.h`\n-----------------\n\n`folly/Benchmark.h` provides a simple framework for writing and\nexecuting benchmarks. Currently the framework targets only\nsingle-threaded testing (though you can internally use fork-join\nparallelism and measure total run time).\n\nAlso read `docs/BenchmarkAdaptive.md`, which describes`--bm_mode=adaptive`, an\neasier & quicker way to get reliable timings for your `BENCHMARK`s.\n\n### Overview\n***\n\nUsing `folly/Benchmark.h` is very simple. Here's an example:\n\n``` Cpp\n    #include <folly/Benchmark.h>\n    #include <vector>\n    using namespace std;\n    using namespace folly;\n    BENCHMARK(insertFrontVector) {\n      // Let's insert 100 elements at the front of a vector\n      vector<int> v;\n      for (unsigned int i = 0; i < 100; ++i) {\n        v.insert(v.begin(), i);\n      }\n    }\n    BENCHMARK(insertBackVector) {\n      // Let's insert 100 elements at the back of a vector\n      vector<int> v;\n      for (unsigned int i = 0; i < 100; ++i) {\n        v.insert(v.end(), i);\n      }\n    }\n    int main() {\n      runBenchmarks();\n    }\n```\n\nCompiling and running this code produces to the standard output:\n\n```\n    ===============================================================================\n    test.cpp                                              relative ns/iter  iters/s\n    ===============================================================================\n    insertFrontVector                                                3.84K  260.38K\n    insertBackVector                                                 1.61K  622.75K\n    ===============================================================================\n```\n\nLet's worry about the empty column \"relative\" later. The table\ncontains, for each benchmark, the time spent per call and the converse\nnumber of calls per second. Numbers are represented in metric notation\n(K for thousands, M for millions etc). As expected, in this example\nthe second function is much faster (fewer ns/iter and more iters/s).\n\nThe macro `BENCHMARK` introduces a function and also adds it to an\ninternal array containing all benchmarks in the system. The defined\nfunction takes no arguments and returns `void`.\n\nThe framework calls the function many times to collect statistics\nabout it. Sometimes the function itself would want to do that\niteration---for example how about inserting `n` elements instead of\n100 elements? To do the iteration internally, use `BENCHMARK` with two\nparameters. The second parameter is the number of iterations and is\npassed by the framework down to the function. The type of the count is\nimplicitly `unsigned`. Consider a slightly reworked example:\n\n``` Cpp\n    #include <folly/Benchmark.h>\n    #include <folly/container/Foreach.h>\n    #include <vector>\n    using namespace std;\n    using namespace folly;\n    BENCHMARK(insertFrontVector, n) {\n      vector<int> v;\n      for (unsigned int i = 0; i < n; ++i) {\n        v.insert(v.begin(), i);\n      }\n    }\n    BENCHMARK(insertBackVector, n) {\n      vector<int> v;\n      for (unsigned int i = 0; i < n; ++i) {\n        v.insert(v.end(), i);\n      }\n    }\n    int main() {\n      runBenchmarks();\n    }\n```\n\nThe produced numbers are substantially different:\n\n```\n    ===============================================================================\n    Benchmark                                             relative ns/iter  iters/s\n    ===============================================================================\n    insertFrontVector                                               39.92    25.05M\n    insertBackVector                                                 3.46   288.89M\n    ===============================================================================\n```\n\nNow the numbers indicate the speed of one single insertion because the\nframework assumed the user-defined function used internal iteration\n(which it does). So inserting at the back of a vector is more than 10\ntimes faster than inserting at the front! Speaking of comparisons...\n\n### Baselines\n***\n\nChoosing one or more good baselines is a crucial activity in any\nmeasurement. Without a baseline there is little information to derive\nfrom the sheer numbers. If, for example, you do experimentation with\nalgorithms, a good baseline is often an established approach (e.g. the\nbuilt-in `std::sort` for sorting). Essentially all experimental\nnumbers should be compared against some baseline.\n\nTo support baseline-driven measurements, `folly/Benchmark.h` defines\n`BENCHMARK_RELATIVE`, which works much like `BENCHMARK`, except it\nconsiders the most recent lexically-occurring `BENCHMARK` a baseline,\nand fills the \"relative\" column. Say, for example, we want to use\nfront insertion for a vector as a baseline and see how back insertion\ncompares with it:\n\n``` Cpp\n    #include <folly/Benchmark.h>\n    #include <folly/container/Foreach.h>\n    #include <vector>\n    using namespace std;\n    using namespace folly;\n    BENCHMARK(insertFrontVector, n) {\n      vector<int> v;\n      for (unsigned int i = 0; i < n; ++i) {\n        v.insert(v.begin(), i);\n      }\n    }\n    BENCHMARK_RELATIVE(insertBackVector, n) {\n      vector<int> v;\n      for (unsigned int i = 0; i < n; ++i) {\n        v.insert(v.end(), i);\n      }\n    }\n    int main() {\n      runBenchmarks();\n    }\n```\n\nThis program prints something like:\n\n```\n    ===============================================================================\n    Benchmark                                             relative ns/iter  iters/s\n    ===============================================================================\n    insertFrontVector                                               42.65    23.45M\n    insertBackVector                                     1208.24%    3.53   283.30M\n    ===============================================================================\n```\n\nshowing the 1208.24% relative speed advantage of inserting at the back\ncompared to front. The scale is chosen in such a way that 100% means\nidentical speed, numbers smaller than 100% indicate the benchmark is\nslower than the baseline, and numbers greater than 100% indicate the\nbenchmark is faster. For example, if you see 42% that means the speed\nof the benchmark is 0.42 of the baseline speed. If you see 123%, it\nmeans the benchmark is 23% or 1.23 times faster.\n\nTo close the current benchmark group and start another, simply use\n`BENCHMARK` again.\n\n### Ars Gratia Artis\n***\n\nIf you want to draw a horizontal line of dashes (e.g. at the end of a\ngroup or for whatever reason), use `BENCHMARK_DRAW_LINE()`. The line\nfulfills a purely aesthetic role; it doesn't interact with\nmeasurements in any way.\n\n``` Cpp\n    BENCHMARK(foo) {\n      Foo foo;\n      foo.doSomething();\n    }\n\n    BENCHMARK_DRAW_LINE();\n\n    BENCHMARK(bar) {\n      Bar bar;\n      bar.doSomething();\n    }\n```\n\nIf you want to print arbitrary text (e.g. a comment or for whatever\nreason), use `BENCHMARK_DRAW_TEXT(\"your text here\")`. The output will\nbe the literal value so escape codes like `\\n` or `\\t` will not work.\nTo add multiple lines of output use multiple `BENCHMARK_DRAW_TEXT`\ncalls.\n\n``` Cpp\n    BENCHMARK(foo) {\n      Foo foo;\n      foo.doSomething();\n    }\n\n    BENCHMARK_DRAW_TEXT(\"Note: bar is expected to be 2x more expensive.\");\n\n    BENCHMARK(bar) {\n      Bar bar;\n      bar.doSomething();\n    }\n```\n\n### Suspending a benchmark\n***\n\nSometimes benchmarking code must do some preparation work that is\nphysically inside the benchmark function, but should not take part to\nits time budget. To temporarily suspend the benchmark, use the\npseudo-statement `BENCHMARK_SUSPEND` as follows:\n\n``` Cpp\n    BENCHMARK(insertBackVector, n) {\n      vector<int> v;\n      BENCHMARK_SUSPEND {\n        v.reserve(n);\n      }\n      for (unsigned int i = 0; i < n; ++i) {\n        v.insert(v.end(), i);\n      }\n    }\n```\n\nThe preallocation effected with `v.reserve(n)` will not count toward\nthe total run time of the benchmark.\n\nOnly the main thread should call `BENCHMARK_SUSPEND` (and of course it\nshould not call it while other threads are doing actual work). This is\nbecause the timer is application-global.\n\nIf the scope introduced by `BENCHMARK_SUSPEND` is not desired, you may\nwant to \"manually\" use the `BenchmarkSuspender` type. Constructing\nsuch an object suspends time measurement, and destroying it resumes\nthe measurement. If you want to resume time measurement before the\ndestructor, call `dismiss` against the `BenchmarkSuspender`\nobject. The previous example could have been written like this:\n\n``` Cpp\n    BENCHMARK(insertBackVector, n) {\n      BenchmarkSuspender braces;\n      vector<int> v;\n      v.reserve(n);\n      braces.dismiss();\n      for (unsigned int i = 0; i < n; ++i) {\n        v.insert(v.end(), i);\n      }\n    }\n```\n\n### `doNotOptimizeAway`\n***\n\nFinally, the small utility function `doNotOptimizeAway` prevents\ncompiler optimizations that may interfere with benchmarking . Call\ndoNotOptimizeAway(var) against variables that you use for\nbenchmarking but otherwise are useless. The compiler tends to do a\ngood job at eliminating unused variables, and this function fools it\ninto thinking a variable is in fact needed. Example:\n\n``` Cpp\n    BENCHMARK(fpOps, n) {\n      double d = 1;\n      FOR_EACH_RANGE (i, 1, n) {\n        d += i;\n        d -= i;\n        d *= i;\n        d /= i;\n      }\n      doNotOptimizeAway(d);\n    }\n```\n\n### A look under the hood\n***\n\n`folly/Benchmark.h` has a simple, systematic approach to collecting\ntimings.\n\nFirst, it organizes measurements in several large epochs, and takes\nthe minimum over all epochs. Taking the minimum gives the closest\nresult to the real runtime. Benchmark timings are not a regular random\nvariable that fluctuates around an average. Instead, the real time\nwe're looking for is one to which there's a variety of additive noise\n(i.e. there is no noise that could actually shorten the benchmark time\nbelow its real value). In theory, taking an infinite amount of samples\nand keeping the minimum is the actual time that needs\nmeasuring. That's why the accuracy of benchmarking increases with the\nnumber of epochs.\n\nClearly, in real functioning there will also be noise and a variety of\neffects caused by the running context. But the noise during the\nbenchmark (straight setup, simple looping) is a poor model for the\nnoise in the real application. So taking the minimum across several\nepochs is the most informative result.\n\nInside each epoch, the function measured is iterated an increasing\nnumber of times until the total runtime is large enough to make noise\nnegligible. At that point the time is collected, and the time per\niteration is computed. As mentioned, the minimum time per iteration\nover all epochs is the final result.\n\nThe timer function used is `clock_gettime` with the `CLOCK_REALTIME`\nclock id. Note that you must use a recent Linux kernel (2.6.38 or\nnewer), otherwise the resolution of `CLOCK_REALTIME` is inadequate.\n"
  },
  {
    "path": "folly/docs/BenchmarkAdaptive.md",
    "content": "# Adaptive benchmark mode\n\n## What is adaptive mode?\n\nTraditional benchmarking runs each benchmark to completion, then reports minimum\nobserved time. Works on quiet systems. On noisy VMs, it fails — benchmark `A`\nruns during a slow period, `B` during a fast one, distorting their relative\nperformance.\n\n`--bm_mode=adaptive` interleaves short slices of all benchmarks in random order. All\nbenchmarks see the same system states. It runs until results are both *stable*\n(not oscillating) and *precise* (confidence interval narrow enough).\n\n**What adaptive gives you:**\n- **Within-run precision**: Benchmarks in the same run are fairly compared under\n  the same system conditions.\n- **Measurement stability**: When we detect the system state is oscillating, we\n  sample more until the \"first half\" measurements agree with the \"second half\".\n- **Faster convergence on quiet systems**: Gets precise results quicker than\n  best-of mode.\n\n**What adaptive does NOT guarantee:**\n- **Absolute accuracy**: Binary layout changes, thermal effects, and long-term\n  system state can still shift results.\n- **Run-to-run reproducibility**: A converged run today may differ from\n  tomorrow's converged run, by more than `--bm_target_precision_pct` might suggest.\n  But, in practice, run-to-run results are still very close.\n\nWhen adaptive mode converges, results closely agree with best-of mode's \"good\nruns\" — the ones where you got lucky. Best-of mode trends slightly lower since\nit targets minimum; adaptive targets a percentile (default: 33rd).\n\n**In summary**: adaptive tries to take the luck out of *within-run* comparisons,\nat the cost of occasionally longer runtime. For cross-run comparisons (e.g.,\nbefore/after your commit), run both builds under the same conditions and rely on\nrelative differences, not absolute values.\n\n---\n\n## Quick start\n\n```bash\nbuck2 run @//mode/opt //your:benchmark -- --bm_mode=adaptive\n```\n\nWith default options, this converges quickly on quiet systems. On noisy VMs,\nincrease the per-benchmark timeout:\n\n```bash\nbuck2 run @//mode/opt //your:benchmark -- --bm_mode=adaptive --bm_max_secs=30\n```\n\nPass `--bm_target_precision_pct` to control the convergence threshold — the\nmeasurement's 95% confidence interval must be narrower than this percentage of\nthe estimate (the default `0.4` means precise to 0.4ns out of 100ns).  Tighten\nit (e.g., `0.1`) if you need finer discrimination between benchmarks, but note\nthat binary layout effects (see below) often dominate at that level.\n\n*Watch out*: a \"converged\" measurement can still be wrong — see the next\nsection.\n\nAdd `--bm_verbose` for under-the-hood stats: sampling rounds,\nconvergence progress.\n\nUse `--bm_min_secs` if you want higher confidence over speed — ensures each\nbenchmark runs at least that long.\n\nFor all flag details, see `folly/Benchmark.cpp`.\n\n*Aside: All comparisons are with \"best-of\" mode. `--bm_estimate_time` computes\na genuinely different value and is slower. Its utility is unclear to the author\nof adaptive mode.*\n\n---\n\n## What adaptive mode cannot detect\n\n### Long-term system state changes\n\nSystem-state changes lasting longer than a run (tens of seconds) are invisible.\nYou can have a stable, converged run that doesn't match the next one.\n\n**For cross-run comparisons** (e.g., comparing your commit to baseline):\n- Run both builds back-to-back in the same session if possible\n- Repeat runs minutes to hours apart to catch long-term drift\n- Look at *relative* differences between benchmarks, not absolute values\n- Consider running on dedicated benchmark hardware if available\n\n### Binary layout variations\n\nMicrobenchmarks are sensitive to instruction cache alignment. Innocuous code\nchanges can shift timings by several percent — far exceeding requested accuracy.\n\n**Example**: Adding this dead code to `verboseLogFinal()` (which never runs when\n`--bm_verbose` is off) consistently shifted timings under\n`--bm_mode=adaptive --bm_max_secs=20 --bm_target_precision_pct=0.03`:\n\n```cpp\nstd::ostringstream rawOss;\nrawOss << \"\\n[bm_raw_samples]\";\nfor (const auto& s : states) {\n  rawOss << \"\\n\" << s.name;\n  for (const auto& sample : s.samples) {\n    rawOss << \"\\t\" << sample.first;\n  }\n}\nLOG(INFO) << rawOss.str();\n```\n\n**This is inherent to microbenchmarking.** There is no \"true\" runtime for a\nmicrobenchmark independent of its binary layout. Adaptive mode improves\n*relative* comparisons within a run (different benchmarks see the same system\nstate), but absolute values can shift between builds.\n\n**Implication**: Don't obsess over 0.1% accuracy targets. Binary layout effects\ncan easily introduce 5-20% variance. Use adaptive to get stable, precise\n*comparisons*, not \"perfect\" absolute measurements.\n\n---\n\n## When your system is having a bad day\n\nSometimes you'll see benchmarks stuck oscillating. Even if they eventually\nconverge, the numbers are suspect — thermal throttling, noisy neighbors,\nbackground tasks.\n\n**If you see persistent `[unstable]` warnings:**\n- Try again later (or on a quieter machine)\n- Consider if the instability is real (does your code have variance?)\n- If most benchmarks converge but one doesn't, the benchmark itself may be\n  inherently noisy (data-dependent branches, allocations, etc.)\n- Use `--bm_min_secs` to force longer runs that average out short-term noise\n- Or just accept the timeout — at least you know your numbers are questionable\n\n---\n\n## Why use the 33rd percentile?\n\nThe default `--bm_target_percentile=33.3` is a heuristic, not science.\n\n**Rationale:**\n- **p0 (minimum)** gives an over-optimistic estimate. For example, it can hide\n  code misbehavior caused by low-grade system contention.\n- **p50 (median)** is more robust but is biased by transient system slowdowns.\n- **p33** tries to be the \"median of good runs\" — filtering out top-end noise\n  while staying realistic.\n\n**Risk of low percentiles:** If your code has a bimodal distribution (fast path\nvs. slow path), low percentiles might hide the slow path. If you care about tail\nlatencies, try `--bm_target_percentile=90` etc.\n\n**Comparison to best-of mode:** Best-of mode reports p0 (minimum across all\niterations). Adaptive's p33 will trend slightly higher but is more repeatable\non noisy systems. If comparing adaptive to best-of, expect a small difference on\nthe same binary under quiet conditions.\n\n---\n\n## Understanding stability\n\nA benchmark is **stable** when first-half and second-half estimates agree.\n\nSplit samples chronologically. Compute percentile estimate and confidence\ninterval for each half. Stable means:\n- First-half estimate is within second-half's CI\n- Second-half estimate is within first-half's CI\n\nE.g., after 200 samples: first-half p33 = 4.2ns ± 0.1ns, second-half\np33 = 4.3ns ± 0.15ns — stable, because each estimate falls within the other's\ninterval.\n\nNever drops samples. As count grows, CIs shrink, halves must eventually agree\n(or timeout shows your system is too noisy).\n\n**Intuition**: If system state is stationary, both halves converge to the same\ntrue value. When they disagree, something changed — keep collecting until they\nagree.\n\n**Stability ≠ accuracy.** You can have a stable, precise measurement of the\n\"wrong\" number (due to binary layout, thermal throttling that persists all run,\netc.). But an *unstable* measurement is definitely unreliable.\n\n---\n\n## Design choices\n\n### Statistical robustness\n\nWe target a percentile, not a mean.  While costlier to compute than mean/stdev,\norder statistics are robust to outliers, and don't assume a distribution.\n**Aside:** we use neither `StreamingStats.h` (online mean/stdev) nor `TDigest.h`\n(approx quantiles) because we don't have to -- see below.\n\nOur percentile CI estimator approximates the binomial distribution, but in\npractice we always have enough samples to make that a good choice.\n\n### Low overhead\n\nMost CPU usage should go to the benchmarks, not the harness. Choices to achieve\nthis:\n\n**Rare convergence checks**: Every 150ms, not every sample.  The O(n log n)\nsorting for percentile CIs is amortized, making complex machinery like\n`TDigest.h` unnecessary for our use-case.\n\n**Downsampled baselines**: Baseline (empty benchmark loop) runs every 8 rounds\n(errors matter little since the baseline is cheap, ~0.3ns/iter). The suspender\nbaseline (cost of `BenchmarkSuspender` usage) runs every 2 rounds (expensive,\n~35ns/iter).\n\n**Measurement slices**: Each benchmark runs for ~`bm_slice_usec` per sample\n(default 1ms). Long enough to amortize harness overhead and avoid timing noise\nfrom function call boundaries.\n\n---\n\n## Algorithm overview\n\n**Self-calibrating iteration counts**\n\nEach benchmark starts at `iterCount=1` and self-adjusts after every sample so\nthat each measurement slice takes ~`bm_slice_usec`.  This avoids a separate\ncalibration phase, which was susceptible to cold-start effects (page faults,\nTLB misses) that could lock `iterCount` at 1 for the entire run.\n\n> *Note:* `bm_slice_usec` should be high enough to avoid interference from the\n> harness code.  The default of 1ms is the reasonable minimum -- 100μs already\n> shows serious interference.\n\n**Interleaved sampling**\n\n```\nwhile not converged:\n  measure baseline (every 8 rounds)\n  measure suspender baseline (every 2 rounds)\n  for each benchmark (random order):\n    measure calibrated iterations, record sample, recalibrate\n  check convergence every 150ms\n```\n\n**Are we done?**\n\nFor each benchmark:\n1. Check stability (split-half agreement)\n2. Check accuracy (CI width < target %)\n3. Done when both pass, or timeout\n\n**Phase 4: Report**\n\nSubtract baselines, pass to existing display logic.\n"
  },
  {
    "path": "folly/docs/Conv.md",
    "content": "`folly/Conv.h`\n-------------\n\n`folly/Conv.h` is a one-stop-shop for converting values across\ntypes. Its main features are simplicity of the API (only the\nnames `to` and `toAppend` must be memorized), speed\n(folly is significantly faster, sometimes by an order of magnitude,\nthan comparable APIs), and correctness.\n\n### Synopsis\n***\n\nAll examples below are assume to have included `folly/Conv.h`\nand issued `using namespace folly;` You will need:\n\n``` Cpp\n    // To format as text and append to a string, use toAppend.\n    fbstring str;\n    toAppend(2.5, &str);\n    CHECK_EQ(str, \"2.5\");\n\n    // Multiple arguments are okay, too. Just put the pointer to string at the end.\n    toAppend(\" is \", 2, \" point \", 5, &str);\n    CHECK_EQ(str, \"2.5 is 2 point 5\");\n\n    // You don't need to use fbstring (although it's much faster for conversions and in general).\n    std::string stdStr;\n    toAppend(\"Pi is about \", 22.0 / 7, &stdStr);\n    // In general, just use to<TargetType>(sourceValue). It returns its result by value.\n    stdStr = to<std::string>(\"Variadic \", \"arguments also accepted.\");\n\n    // to<fbstring> is 2.5x faster than to<std::string> for typical workloads.\n    str = to<fbstring>(\"Variadic \", \"arguments also accepted.\");\n```\n\n### Integral-to-integral conversion\n***\n\nUsing `to<Target>(value)` to convert one integral type to another\nwill behave as follows:\n\n* If the target type can accommodate all possible values of the\n  source value, the value is implicitly converted. No further\n  action is taken. Example:\n\n``` Cpp\n        short x;\n        unsigned short y;\n        ...\n        auto a = to<int>(x); // zero overhead conversion\n        auto b = to<int>(y); // zero overhead conversion\n```\n\n* Otherwise, `to` inserts bounds checks and throws\n  `std::range_error` if the target type cannot accommodate the\n  source value. Example:\n\n``` Cpp\n    short x;\n    unsigned short y;\n    long z;\n    ...\n    x = 123;\n    auto a = to<unsigned short>(x); // fine\n    x = -1;\n    a = to<unsigned short>(x); // THROWS\n    z = 2000000000;\n    auto b = to<int>(z); // fine\n    z += 1000000000;\n    b = to<int>(z); // THROWS\n    auto b = to<unsigned int>(z); // fine\n```\n\n### Anything-to-string conversion\n***\n\nAs mentioned, there are two primitives for converting anything to\nstring: `to` and `toAppend`. They support the same set of source\ntypes, literally by definition (`to` is implemented in terms of\n`toAppend` for all types). The call `toAppend(value, &str)`\nformats and appends `value` to `str` whereas\n`to<StringType>(value)` formats `value` as a `StringType` and\nreturns the result by value. Currently, the supported\n`StringType`s are `std::string` and `fbstring`\n\nBoth `toAppend` and `to` with a string type as a target support\nvariadic arguments. Each argument is converted in turn. For\n`toAppend` the last argument in a variadic list must be the\naddress of a supported string type (no need to specify the string\ntype as a template argument).\n\n#### Integral-to-string conversion\n\nNothing special here - integrals are converted to strings in\ndecimal format, with a '-' prefix for negative values. Example:\n\n``` Cpp\n    auto a = to<fbstring>(123);\n    assert(a == \"123\");\n    a = to<fbstring>(-456);\n    assert(a == \"-456\");\n```\n\nThe conversion implementation is aggressively optimized. It\nconverts two digits at a time assisted by fixed-size tables.\nConverting a `long` to an `fbstring` is 3.6x faster than using\n`boost::lexical_cast` and 2.5x faster than using `sprintf` even\nthough the latter is used in conjunction with a stack-allocated\nconstant-size buffer.\n\nNote that converting integral types to `fbstring` has a\nparticular advantage compared to converting to `std::string`\nNo integral type (<= 64 bits) has more than 20 decimal digits\nincluding sign. Since `fbstring` employs the small string\noptimization for up to 23 characters, converting an integral\nto `fbstring` is guaranteed to not allocate memory, resulting\nin significant speed and memory locality gains. Benchmarks\nreveal a 2x gain on a typical workload.\n\n#### `char` to string conversion\n\nAlthough `char` is technically an integral type, most of the time\nyou want the string representation of `'a'` to be `\"a\"`, not `96`\nThat's why `folly/Conv.h` handles `char` as a special case that\ndoes the expected thing. Note that `signed char` and `unsigned\nchar` are still considered integral types.\n\n\n#### Floating point to string conversion\n\n`folly/Conv.h` uses [V8's double conversion](http://code.google.com/p/double-conversion/)\nroutines. They are accurate and fast; on typical workloads,\n`to<fbstring>(doubleValue)` is 1.9x faster than `sprintf` and\n5.5x faster than `boost::lexical_cast` (It is also 1.3x faster\nthan `to<std::string>(doubleValue)`\n\n#### `const char*` to string conversion\n\nFor completeness, `folly/Conv.h` supports `const char*` including\ni.e. string literals. The \"conversion\" consists, of course, of\nthe string itself. Example:\n\n``` Cpp\n    auto s = to<fbstring>(\"Hello, world\");\n    assert(s == \"Hello, world\");\n```\n\n#### Anything from string conversion (i.e. parsing)\n***\n\n`folly/Conv.h` includes three kinds of parsing routines:\n\n* `to<Type>(const char* begin, const char* end)` rigidly\n  converts the range [begin, end) to `Type` These routines have\n  drastic restrictions (e.g. allow no leading or trailing\n  whitespace) and are intended as an efficient back-end for more\n  tolerant routines.\n* `to<Type>(stringy)` converts `stringy` to `Type` Value\n  `stringy` may be of type `const char*`, `StringPiece`,\n  `std::string`, or `fbstring` (Technically, the requirement is\n  that `stringy` implicitly converts to a `StringPiece`\n* `to<Type>(&stringPiece)` parses with progress information:\n  given `stringPiece` of type `StringPiece` it parses as much\n  as possible from it as type `Type` and alters `stringPiece`\n  to remove the munched characters. This is easiest clarified\n  by an example:\n\n``` Cpp\n    fbstring s = \" 1234 angels on a pin\";\n    StringPiece pc(s);\n    auto x = to<int>(&pc);\n    assert(x == 1234);\n    assert(pc == \" angels on a pin\";\n```\n\nNote how the routine ate the leading space but not the trailing one.\n\n#### Parsing integral types\n\nParsing integral types is unremarkable - decimal format is\nexpected, optional `'+'` or `'-'` sign for signed types, but no\noptional `'+'` is allowed for unsigned types. The one remarkable\nelement is speed - parsing typical `long` values is 6x faster than\n`sscanf`. `folly/Conv.h` uses aggressive loop unrolling and\ntable-assisted SIMD-style code arrangement that avoids integral\ndivision (slow) and data dependencies across operations\n(ILP-unfriendly). Example:\n\n``` Cpp\n    fbstring str = \"  12345  \";\n    assert(to<int>(str) == 12345);\n    str = \"  12345six seven eight\";\n    StringPiece pc(str);\n    assert(to<int>(&pc) == 12345);\n    assert(str == \"six seven eight\");\n```\n\n#### Parsing floating-point types\n\n`folly/Conv.h` uses, again, [V8's double-conversion](http://code.google.com/p/double-conversion/)\nroutines as back-end. The speed is 3x faster than `sscanf` and\n1.7x faster than in-home routines such as `parse<double>` But\nthe more important detail is accuracy - even if you do code a\nroutine that works faster than `to<double>` chances are it is\nincorrect and will fail in a variety of corner cases. Using\n`to<double>` is strongly recommended.\n\nNote that if the string \"NaN\" (with any capitalization) is passed to\n`to<double>` then `NaN` is returned, which can be tested for as follows:\n\n``` Cpp\n    fbstring str = \"nan\"; // \"NaN\", \"NAN\", etc.\n    double d = to<double>(str);\n    if (std::isnan(d)) {\n      // string was a valid representation of the double value NaN\n    }\n```\n\nNote that passing \"-NaN\" (with any capitalization) to `to<double>` also returns\n`NaN`.\n\nNote that if the strings \"inf\" or \"infinity\" (with any capitalization) are\npassed to `to<double>` then `infinity` is returned, which can be tested for\nas follows:\n\n``` Cpp\n    fbstring str = \"inf\"; // \"Inf\", \"INF\", \"infinity\", \"Infinity\", etc.\n    double d = to<double>(str);\n    if (std::isinf(d)) {\n      // string was a valid representation of one of the double values +Infinity\n      // or -Infinity\n    }\n```\n\nNote that passing \"-inf\" or \"-infinity\" (with any capitalization) to\n`to<double>` returns `-infinity` rather than `+infinity`. The sign of the\n`infinity` can be tested for as follows:\n\n``` Cpp\n    fbstring str = \"-inf\"; // or \"inf\", \"-Infinity\", \"+Infinity\", etc.\n    double d = to<double>(str);\n    if (d == std::numeric_limits<double>::infinity()) {\n      // string was a valid representation of the double value +Infinity\n    } else if (d == -std::numeric_limits<double>::infinity()) {\n      // string was a valid representation of the double value -Infinity\n    }\n```\n\nNote that if an unparseable string is passed to `to<double>` then an exception\nis thrown, rather than `NaN` being returned.  This can be tested for as follows:\n\n``` Cpp\n    fbstring str = \"not-a-double\"; // Or \"1.1.1\", \"\", \"$500.00\", etc.\n    double d;\n    try {\n      d = to<double>(str);\n    } catch (const std::range_error &) {\n      // string could not be parsed\n    }\n```\n\nNote that the empty string (`\"\"`) is an unparseable value, and will cause\n`to<double>` to throw an exception.\n\n#### Non-throwing interfaces\n\n`tryTo<T>` is the non-throwing variant of `to<T>`. It returns\nan `Expected<T, ConversionCode>`. You can think of `Expected`\nas like an `Optional<T>`, but if the conversion failed, `Expected`\nstores an error code instead of a `T`.\n\n`tryTo<T>` has similar performance as `to<T>` when the\nconversion is successful. On the error path, you can expect\n`tryTo<T>` to be roughly three orders of magnitude faster than\nthe throwing `to<T>` and to completely avoid any lock contention\narising from stack unwinding.\n\nHere is how to use non-throwing conversions:\n\n``` Cpp\n    auto t1 = tryTo<int>(str);\n    if (t1.hasValue()) {\n      use(t1.value());\n    }\n```\n\n`Expected` has a composability feature to make the above pattern simpler.\n\n``` Cpp\n    tryTo<int>(str).then([](int i) { use(i); });\n```\n"
  },
  {
    "path": "folly/docs/Dynamic.md",
    "content": "`folly/dynamic.h`\n-----------------\n\n`folly/dynamic.h` provides a runtime dynamically typed value for\nC++, similar to the way languages with runtime type systems work\n(e.g. Python). It can hold types from a predetermined set of types\n(ints, bools, arrays of other dynamics, etc), similar to something like\n`std::variant`, but the syntax is intended to be a little more like\nusing the native type directly.\n\n### Overview\n***\n\nHere are some code samples to get started (assumes a `using\nfolly::dynamic;` was used):\n\n``` Cpp\n    dynamic twelve = 12; // creates a dynamic that holds an integer\n    dynamic str = \"string\"; // yep, this one is an fbstring\n\n    // A few other types.\n    dynamic nul = nullptr;\n    dynamic boolean = false;\n\n    // Arrays can be initialized with dynamic::array.\n    dynamic array = dynamic::array(\"array \", \"of \", 4, \" elements\");\n    assert(array.size() == 4);\n    dynamic emptyArray = dynamic::array;\n    assert(emptyArray.empty());\n\n    // Maps from dynamics to dynamics are called objects.  The\n    // dynamic::object constant is how you make an empty map from dynamics\n    // to dynamics.\n    dynamic map = dynamic::object;\n    map[\"something\"] = 12;\n    map[\"another_something\"] = map[\"something\"] * 2;\n\n    // Dynamic objects may be initialized this way\n    dynamic map2 = dynamic::object(\"something\", 12)(\"another_something\", 24);\n```\n\n### Runtime Type Checking and Conversions\n***\n\nAny operation on a dynamic requires checking at runtime that the\ntype is compatible with the operation. If it isn't, you'll get a\n`folly::TypeError`. Other exceptions can also be thrown if\nyou try to do something impossible (e.g. if you put a very large\n64-bit integer in and try to read it out as a double).\n\nMore examples should hopefully clarify this:\n\n``` Cpp\n    dynamic dint = 42;\n\n    dynamic str = \"foo\";\n    dynamic anotherStr = str + \"something\"; // fine\n    dynamic thisThrows = str + dint; // TypeError is raised\n```\n\nExplicit type conversions can be requested for some of the basic types:\n\n``` Cpp\n    dynamic dint = 12345678;\n    dynamic doub = dint.asDouble(); // doub will hold 12345678.0\n    dynamic str = dint.asString(); // str == \"12345678\"\n\n    dynamic hugeInt = std::numeric_limits<int64_t>::max();\n    dynamic hugeDoub = hugeInt.asDouble();  // throws a folly/Conv.h error,\n                                            // since it can't fit in a double\n```\n\nFor more complicated conversions, see [DynamicConverter](DynamicConverter.md).\n\n### Comparison Operators and Hashing\n#### Equality Operators\nEquality operators (`==`, `!=`) are supported for all types.\n\nFor dynamics of the same type, the underlying equality operator shall apply.\n\nFor comparisons between different numeric types (double and int64), numeric\nequality will apply - thus `2.0 == 2`.\n\nValues of any other different types will be deemed to not be equal.\n\n#### Ordering operators\nOrdering operators (`<`, `<=`, `>`, `>=`) are supported for all types, except\n`dynamic::object` which will throw if it is involved in an ordering operator.\n\nFor dynamics of the same type, the underlying ordering operator shall apply.\n\nFor comparisons between different numeric types (double and int64), numeric\nordering will apply - thus `1.5 < 2`.\n\nOrdering of values between other different types will maintain total ordering\nproperties and be consistent within a given binary run, and thus safe for use in\ne.g. `std::set`. The actual ordering is undefined and could change across\nversions, thus a dependency should not be taken outside of the total ordering\nproperty within a given binary.\n\n#### Hashing\nHashing is supported by all types, and the hashes of two values will match if\nthey are equal per `dynamic::operator==`.\n\nAs a result, numerical types have the same numerical hashing regardless of int64\nvs double - so e.g. `std::hash<dynamic>()(2)` will give the same value as\n`std::hash<dynamic>()(2.0)`.\n\n### Iteration and Lookup\n***\n\nYou can iterate over dynamic arrays as you would over any C++ sequence container.\n\n``` Cpp\n    dynamic array = dynamic::array(2, 3, \"foo\");\n\n    for (auto& val : array) {\n      doSomethingWith(val);\n    }\n```\n\nYou can iterate over dynamic maps by calling `items()`, `keys()`,\n`values()`, which behave similarly to the homonymous methods of Python\ndictionaries.\n\n``` Cpp\n    dynamic obj = dynamic::object(2, 3)(\"hello\", \"world\")(\"x\", 4);\n\n    for (auto& pair : obj.items()) {\n      // Key is pair.first, value is pair.second\n      processKey(pair.first);\n      processValue(pair.second);\n    }\n\n    for (auto& key : obj.keys()) {\n      processKey(key);\n    }\n\n    for (auto& value : obj.values()) {\n      processValue(value);\n    }\n```\n\nYou can find an element by key in a dynamic map using the `find()` method,\nwhich returns an iterator compatible with `items()`:\n\n``` Cpp\n    dynamic obj = dynamic::object(2, 3)(\"hello\", \"world\")(\"x\", 4);\n\n    auto pos = obj.find(\"hello\");\n    // pos->first is \"hello\"\n    // pos->second is \"world\"\n\n    auto pos = obj.find(\"no_such_key\");\n    // pos == obj.items().end()\n```\n\n### Erasure\n\nYou can erase elements from dynamic arrays by calling member `dynamic::erase`,\nwhich works like and has overloads like `std::vector::erase`, and from dynamic\nobjects by calling member `dynamic::erase`, which works like and has overloads\nlike `std::unordered_map::erase`. You can also find and erase elements from\ndynamic arrays by calling free function `erase`, and from dynamic arrays or\nobjects by calling  free function `erase_if`, which work and have overloads\nlike `erase(std::vector)`, `erase_if(std::vector)`, and\n`erase_if(std::unordered_map)`.\n\nhttps://en.cppreference.com/w/cpp/container/vector/erase2\nhttps://en.cppreference.com/w/cpp/container/unordered_map/erase_if.html\n\n### Use for JSON\n***\n\nThe original motivation for implementing this type was to try to\nmake dealing with json documents in C++ almost as easy as it is\nin languages with dynamic type systems (php or javascript, etc).\nThe reader can judge whether we're anywhere near that goal, but\nhere's what it looks like:\n\n``` Cpp\n    // Parsing JSON strings and using them.\n    std::string jsonDocument = R\"({\"key\":12,\"key2\":[false, null, true, \"yay\"]})\";\n    dynamic parsed = folly::parseJson(jsonDocument);\n    assert(parsed[\"key\"] == 12);\n    assert(parsed[\"key2\"][0] == false);\n    assert(parsed[\"key2\"][1] == nullptr);\n\n    // Building the same document programmatically.\n    dynamic sonOfAJ = dynamic::object\n      (\"key\", 12)\n      (\"key2\", dynamic::array(false, nullptr, true, \"yay\"));\n\n    // Printing.  (See also folly::toPrettyJson)\n    auto str = folly::toJson(sonOfAJ);\n    assert(jsonDocument.compare(str) == 0);\n```\n\n### Performance\n***\n\nDynamic typing is more expensive than static typing, even when\nyou do it in C++. ;)\n\nHowever, some effort has been made to keep `folly::dynamic` and\nthe json (de)serialization at least reasonably performant for\ncommon cases. The heap is only used for arrays and objects, and\nmove construction is fully supported. String formatting\ninternally also uses the highly performant `folly::to<>` (see\n`folly/Conv.h`).\n\nA trade off to keep in mind though, is that\n`sizeof(folly::dynamic)` is 64 bytes. You probably don't want to\nuse it if you need to allocate large numbers of them (prefer\nstatic types, etc).\n\n### Some Design Rationale\n***\n\n**Q. Why doesn't a dynamic string support begin(), end(), and operator[]?**\n\nThe value_type of a dynamic iterator is `dynamic`, and `operator[]`\n(or the `at()` function) has to return a reference to a dynamic.  If\nwe wanted this to work for strings, this would mean we'd have to\nsupport dynamics with a character type, and moreover that the internal\nrepresentation of strings would be such that we can hand out\nreferences to dynamic as accessors on individual characters.  There\nare a lot of potential efficiency drawbacks with this, and it seems\nlike a feature that is not needed too often in practice.\n\n**Q. Isn't this just a poor imitation of the C# language feature?**\n\nPretty much.\n"
  },
  {
    "path": "folly/docs/DynamicConverter.md",
    "content": "`folly/DynamicConverter.h`\n--------------------------\n\nWhen dynamic objects contain data of a known type, it is sometimes\nuseful to have its well-typed representation. A broad set of\ntype-conversions are contained in `DynamicConverter.h`, and\nfacilitate the transformation of dynamic objects into their well-typed\nformat.\n\n### Usage\n***\n\nSimply pass a dynamic into a templated convertTo:\n\n```cpp\n    dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int\n    auto vvi = convertTo<fbvector<fbvector<int>>>(d);\n```\n\n### Supported Types\n***\n\nconvertTo naturally supports conversions to\n\n1. arithmetic types (such as int64_t, unsigned short, bool, and double)\n2. fbstring, std::string\n3. containers and map-containers\n\nNOTE:\n\nconvertTo<Type> will assume that Type is a container if\n* it has a Type::value_type, and\n* it has a Type::iterator, and\n* it has a constructor that accepts two InputIterators\n\nAdditionally, convertTo<Type> will assume that Type is a map if\n* it has a Type::key_type, and\n* it has a Type::mapped_type, and\n* value_type is a pair of const key_type and mapped_type\n\nIf Type meets the container criteria, then it will be constructed\nby calling its InputIterator constructor.\n\n### Customization\n***\n\nIf you want to use convertTo to convert dynamics into your own custom\nclass, then all you have to do is provide a template specialization\nof DynamicConverter with the static method convert. Make sure you put it\nin namespace folly.\n\nExample:\n\n``` Cpp\n    struct Token {\n      int kind_;\n      fbstring lexeme_;\n      \n      explicit Token(int kind, const fbstring& lexeme)\n        : kind_(kind), lexeme_(lexeme) {}\n    };\n    namespace folly {\n    template <> struct DynamicConverter<Token> {\n      static Token convert(const dynamic& d) {\n        int k = convertTo<int>(d[\"KIND\"]);\n        fbstring lex = convertTo<fbstring>(d[\"LEXEME\"]);\n        return Token(k, lex);\n      }\n    };\n    }\n```\n"
  },
  {
    "path": "folly/docs/Executors.md",
    "content": "<section class=\"dex_document\"><h1>Thread pools &amp; Executors</h1><p class=\"dex_introduction\">Run your concurrent code in a performant way</p><h2 id=\"all-about-thread-pools\">All about thread pools <a href=\"#all-about-thread-pools\" class=\"headerLink\">#</a></h2>\n\n<h3 id=\"how-do-i-use-the-thread\">How do I use the thread pools? <a href=\"#how-do-i-use-the-thread\" class=\"headerLink\">#</a></h3>\n\n<p>Folly provides two concrete thread pools (IOThreadPoolExecutor, CPUThreadPoolExecutor) as well as building them in as part of a complete async framework.  Generally you might want to grab the global executor, and use it with a future, like this:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">auto</span> <span class=\"no\">f</span> <span class=\"o\">=</span> <span class=\"nf\" data-symbol-name=\"someFutureFunction\">someFutureFunction</span><span class=\"o\">().</span><span class=\"nf\" data-symbol-name=\"via\">via</span><span class=\"o\">(</span><span class=\"nf\" data-symbol-name=\"getCPUExecutor\">getCPUExecutor</span><span class=\"o\">()).</span><span class=\"nf\" data-symbol-name=\"then\">then</span><span class=\"o\">(...)</span></pre></div>\n\n<p>Or maybe you need to construct a thrift/memcache client, and need an event base:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">auto</span> <span class=\"no\">f</span> <span class=\"o\">=</span> <span class=\"nf\" data-symbol-name=\"getClient\">getClient</span><span class=\"o\">(</span><span class=\"nf\" data-symbol-name=\"getIOExecutor\">getIOExecutor</span><span class=\"o\">()-&gt;</span><span class=\"na\" data-symbol-name=\"getEventBase\">getEventBase</span><span class=\"o\">())-&gt;</span><span class=\"na\" data-symbol-name=\"callSomeFunction\">callSomeFunction</span><span class=\"o\">(</span><span class=\"no\">args</span><span class=\"o\">...)</span>\n         <span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"via\">via</span><span class=\"o\">(</span><span class=\"nf\" data-symbol-name=\"getCPUExecutor\">getCPUExecutor</span><span class=\"o\">())</span>\n         <span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"then\">then</span><span class=\"o\">([](</span><span class=\"no\">Result</span> <span class=\"no\">r</span><span class=\"o\">)&#123;</span> <span class=\"o\">....</span> <span class=\"k\">do</span> <span class=\"no\">something</span> <span class=\"no\">with</span> <span class=\"no\">result</span><span class=\"o\">&#125;);</span></pre></div>\n\n<h3 id=\"vs-c-11-s-std-launch\">vs. C++11&#039;s std::launch <a href=\"#vs-c-11-s-std-launch\" class=\"headerLink\">#</a></h3>\n\n<p>The current C++11 std::launch only has two modes: async or deferred.  In a production system, neither is what you want:  async will launch a new thread for every launch without limit, while deferred will defer the work until it is needed lazily, but then do the work <strong>in the current thread synchronously</strong> when it is needed.</p>\n\n<p>Folly&#039;s thread pools always launch work as soon as possible, have limits to the maximum number of tasks / threads allowed, so we will never use more threads than absolutely needed.  See implementation details below about each type of executor.</p>\n\n<h3 id=\"why-do-we-need-yet-anoth\">Why do we need yet another set of thread pools? <a href=\"#why-do-we-need-yet-anoth\" class=\"headerLink\">#</a></h3>\n\n<p>Unfortunately none of the existing thread pools had every feature needed - things based on pipes are too slow.   Several older ones didn&#039;t support std::function.</p>\n\n<h3 id=\"why-do-we-need-several-d\">Why do we need several different types of thread pools? <a href=\"#why-do-we-need-several-d\" class=\"headerLink\">#</a></h3>\n\n<p>If you want epoll support, you need an fd - event_fd is the latest notification hotness.   Unfortunately, an active fd triggers all the epoll loops it is in, leading to thundering herd - so if you want a fair queue (one queue total vs. one queue per worker thread), you need to use some kind of semaphore.  Unfortunately semaphores can&#039;t be put in epoll loops, so they are incompatible with IO.   Fortunately, you usually want to separate the IO and CPU bound work anyway to give stronger tail latency guarantees on IO.</p>\n\n<h3 id=\"iothreadpoolexecutor\">IOThreadPoolExecutor <a href=\"#iothreadpoolexecutor\" class=\"headerLink\">#</a></h3>\n\n<ul>\n<li>Uses event_fd for notification, and waking an epoll loop.</li>\n<li>There is one queue (NotificationQueue specifically) per thread/epoll.</li>\n<li>If the thread is already running and not waiting on epoll, we don&#039;t make any additional syscalls to wake up the loop, just put the new task in the queue.</li>\n<li>If any thread has been waiting for more than a few seconds, its stack is madvised away.   Currently however tasks are scheduled round robin on the queues, so unless there is <strong>no</strong> work going on, this isn&#039;t very effective.</li>\n<li>::getEventBase() will return an EventBase you can schedule IO work on directly, chosen round-robin.</li>\n<li>Since there is one queue per thread, there is hardly any contention on the queues - so a simple spinlock around an std::deque is used for the tasks.  There is no max queue size.</li>\n<li>By default, there is one thread per core - it usually doesn&#039;t make sense to have more IO threads than this, assuming they don&#039;t block.</li>\n</ul>\n\n<h3 id=\"cputhreadpoolexecutor\">CPUThreadPoolExecutor <a href=\"#cputhreadpoolexecutor\" class=\"headerLink\">#</a></h3>\n\n<ul>\n<li>A single queue backed by folly/LifoSem and folly/MPMC queue.  Since there is only a single queue, contention can be quite high, since all the worker threads and all the producer threads hit the same queue.  MPMC queue excels in this situation.  MPMC queue dictates a max queue size.</li>\n<li>LifoSem wakes up threads in Lifo order - i.e. there are only few threads as necessary running, and we always try to reuse the same few threads for better cache locality.</li>\n<li>All Folly BlockingQueue implementations use either LifoSem or ThrottledLifoSem, which madvise away the stack of threads that are inactive for a long time.</li>\n<li>stop() will finish all outstanding tasks at exit</li>\n<li>Supports priorities - priorities are implemented as multiple queues - each worker thread checks the highest priority queue first.  Threads themselves don&#039;t have priorities set, so a series of long running low priority tasks could still hog all the threads.  (at last check pthreads thread priorities didn&#039;t work very well)</li>\n</ul>\n\n<h3 id=\"threadpoolexecutor\">ThreadPoolExecutor <a href=\"#threadpoolexecutor\" class=\"headerLink\">#</a></h3>\n\n<p>Base class that contains the thread startup/shutdown/stats logic, since this is pretty disjoint from how tasks are actually run</p>\n\n<h3 id=\"observers\">Observers <a href=\"#observers\" class=\"headerLink\">#</a></h3>\n\n<p>An observer interface is provided to listen for thread start/stop events.  This is useful to create objects that should be one-per-thread, but also have them work correctly if threads are added/removed from the thread pool.</p>\n\n<h3 id=\"stats\">Stats <a href=\"#stats\" class=\"headerLink\">#</a></h3>\n\n<p>PoolStats are provided to get task count, running time, waiting time, etc.</p>\n</section>\n"
  },
  {
    "path": "folly/docs/FBString.md",
    "content": "`folly/FBString.h`\n------------------\n\n`fbstring` is a drop-in replacement for `std::string`. The main\nbenefit of `fbstring` is significantly increased performance on\nvirtually all important primitives. This is achieved by using a\nthree-tiered storage strategy and by cooperating with the memory\nallocator. In particular, `fbstring` is designed to detect use of\njemalloc and cooperate with it to achieve significant improvements in\nspeed and memory usage.\n\n`fbstring` supports 32- and 64-bit and little- and big-endian\narchitectures.\n\n### Storage strategies\n***\n\n* Small strings (<= 23 chars) are stored in-situ without memory\n  allocation.\n\n* Medium strings (24 - 255 chars) are stored in malloc-allocated\n  memory and copied eagerly.\n\n* Large strings (> 255 chars) are stored in malloc-allocated memory and\n  copied lazily.\n\n### Implementation highlights\n***\n\n* 100% compatible with `std::string`.\n\n* Thread-safe reference counted copy-on-write for \"large\"\n  strings (> 255 chars).\n\n* Uses `malloc` instead of allocators.\n\n* Jemalloc-friendly. `fbstring` automatically detects if application\n  uses jemalloc and if so, significantly improves allocation\n  strategy by using non-standard jemalloc extensions.\n\n* `find()` is implemented using simplified Boyer-Moore\n  algorithm. Casual tests indicate a 30x speed improvement over\n  `string::find()` for successful searches and a 1.5x speed\n  improvement for failed searches.\n\n* Offers conversions to and from `std::string`.\n"
  },
  {
    "path": "folly/docs/FBVector.md",
    "content": "`folly/FBVector.h`\n------------------\n\nSimply replacing `std::vector` with `folly::fbvector` (after\nhaving included the `folly/FBVector.h` header file) will\nimprove the performance of your C++ code using vectors with\ncommon coding patterns. The improvements are always non-negative,\nalmost always measurable, frequently significant, sometimes\ndramatic, and occasionally spectacular.\n\n### Sample\n***\n```cpp\n    folly::fbvector<int> numbers({0, 1, 2, 3});\n    numbers.reserve(10);\n    for (int i = 4; i < 10; i++) {\n      numbers.push_back(i * 2);\n    }\n    assert(numbers[6] == 12);\n```\n### Motivation\n***\n\n`std::vector` is the stalwart abstraction many use for\ndynamically-allocated arrays in C++. It is also the best known\nand most used of all containers. It may therefore seem a\nsurprise that `std::vector` leaves important - and sometimes\nvital - efficiency opportunities on the table. This document\nexplains how our own drop-in abstraction `fbvector` improves key\nperformance aspects of `std::vector`. Refer to\nfolly/test/FBVectorTest.cpp for a few benchmarks.\n\n### Memory Handling\n***\n\nIt is well known that `std::vector` grows exponentially (at a\nconstant factor) in order to avoid quadratic growth performance.\nThe trick is choosing a good factor. Any factor greater than 1\nensures O(1) amortized append complexity towards infinity. But a\nfactor that's too small (say, 1.1) causes frequent vector reallocation, and\none that's too large (say, 3 or 4) forces the vector to consume much more\nmemory than needed.\n\nThe initial HP implementation by Stepanov used a\ngrowth factor of 2; i.e., whenever you'd `push_back` into a vector\nwithout there being room, it would double the current capacity. This\nwas not a good choice: it can be mathematically proven that a growth factor of\n2 is rigorously the <i>worst</i> possible because it never allows the vector \nto reuse any of its previously-allocated memory. Despite other compilers\nreducing the growth factor to 1.5, gcc has staunchly maintained its factor of\n2. This makes `std::vector` cache- unfriendly and memory manager unfriendly.\n\nTo see why that's the case, consider a large vector of capacity C.\nWhen there's a request to grow the vector, the vector\n(assuming no in-place resizing, see the appropriate section in\nthis document) will allocate a chunk of memory next to its current chunk,\ncopy its existing data to the new chunk, and then deallocate the old chunk.\nSo now we have a chunk of size C followed by a chunk of size k * C. Continuing\nthis process we'll then have a chunk of size k * k * C to the right and so on.\nThat leads to a series of the form (using ^^ for power):\n\n    C, C*k,  C*k^^2, C*k^^3, ...\n\nIf we choose k = 2 we know that every element in the series will\nbe strictly larger than the sum of all previous ones because of\nthe remarkable equality:\n\n    1 + 2^^1 + 2^^2 + 2^^3... + 2^^n = 2^^(n+1) - 1\n\nThis means that any new chunk requested will be larger\nthan all previously used chunks combined, so the vector must\ncrawl forward in memory; it can't move back to its deallocated chunks.\nBut any number smaller than 2 guarantees that you'll at some point be \nable to reuse the previous chunks. For instance, choosing 1.5 as the factor\nallows memory reuse after 4 reallocations; 1.45 allows memory reuse after 3\nreallocations; and 1.3 allows reuse after only 2 reallocations.\n\nOf course, the above makes a number of simplifying assumptions\nabout how the memory allocator works, but definitely you don't\nwant to choose the theoretically absolute worst growth factor.\n`fbvector` uses a growth factor of 1.5. That does not impede good\nperformance at small sizes because of the way `fbvector`\ncooperates with jemalloc (below).\n\n### The jemalloc Connection\n***\n\nVirtually all modern allocators allocate memory in fixed-size\nquanta that are chosen to minimize management overhead while at\nthe same time offering good coverage at low slack. For example, an\nallocator may choose blocks of doubling size (32, 64, 128,\n<t_co>, ...) up to 4096, and then blocks of size multiples of a\npage up until 1MB, and then 512KB increments and so on.\n\nAs discussed above, `std::vector` also needs to (re)allocate in\nquanta. The next quantum is usually defined in terms of the\ncurrent size times the infamous growth constant. Because of this\nsetup, `std::vector` has some slack memory at the end much like\nan allocated block has some slack memory at the end.\n\nIt doesn't take a rocket surgeon to figure out that an allocator-\naware `std::vector` would be a marriage made in heaven: the\nvector could directly request blocks of \"perfect\" size from the\nallocator so there would be virtually no slack in the allocator.\nAlso, the entire growth strategy could be adjusted to work\nperfectly with allocator's own block growth strategy. That's\nexactly what `fbvector` does - it automatically detects the use\nof jemalloc and adjusts its reallocation strategy accordingly.\n\nBut wait, there's more. Many memory allocators do not support in-\nplace reallocation, although most of them could. This comes from\nthe now notorious design of `realloc()` to opaquely perform\neither in-place reallocation or an allocate-memcpy-deallocate\ncycle. Such lack of control subsequently forced all clib-based\nallocator designs to avoid in-place reallocation, and that\nincludes C++'s `new` and `std::allocator`. This is a major loss of\nefficiency because an in-place reallocation, being very cheap,\nmay mean a much less aggressive growth strategy. In turn that\nmeans less slack memory and faster reallocations.\n\n### Object Relocation\n***\n\nOne particularly sensitive topic about handling C++ values is\nthat they are all conservatively considered <i>non-\nrelocatable</i>. In contrast, a relocatable value would preserve\nits invariant even if its bits were moved arbitrarily in memory.\nFor example, an `int32_t` is relocatable because moving its 4 bytes\nwould preserve its actual value, so the address of that value\ndoes not \"matter\" to its integrity.\n\nC++'s assumption of non-relocatable values hurts everybody for\nthe benefit of a few questionable designs. The issue is that\nmoving a C++ object \"by the book\" entails (a) creating a new copy\nfrom the existing value; (b) destroying the old value. This is\nquite vexing and violates common sense; consider this\nhypothetical conversation between Captain Picard and an\nincredulous alien:\n\nIncredulous Alien: \"So, this teleporter, how does it work?\"<br>\nPicard: \"It beams people and arbitrary matter from one place to\nanother.\"<br> Incredulous Alien: \"Hmmm... is it safe?\"<br>\nPicard: \"Yes, but earlier models were a hassle. They'd clone the\nperson to another location. Then the teleporting chief would have\nto shoot the original. Ask O'Brien, he was an intern during those\ntimes. A bloody mess, that's what it was.\"\n\nOnly a tiny minority of objects are genuinely non-relocatable:\n\n* Objects that use internal pointers, e.g.:\n```cpp\n    class Ew {\n      char buffer[1024];\n      char * pointerInsideBuffer;\n    public:\n      Ew() : pointerInsideBuffer(buffer) {}\n      ...\n    }\n```\n* Objects that need to update \"observers\" that store pointers to them.\n\nThe first class of designs can always be redone at small or no\ncost in efficiency. The second class of objects should not be\nvalues in the first place - they should be allocated with `new`\nand manipulated using (smart) pointers. It is highly unusual for\na value to have observers that alias pointers to it.\n\nRelocatable objects are of high interest to `std::vector` because\nsuch knowledge makes insertion into the vector and vector\nreallocation considerably faster: instead of going to Picard's\ncopy-destroy cycle, relocatable objects can be moved around\nsimply by using `memcpy` or `memmove`. This optimization can\nyield arbitrarily high wins in efficiency; for example, it\ntransforms `vector< vector<double> >` or `vector< hash_map<int,\nstring> >` from risky liabilities into highly workable\ncompositions.\n\nIn order to allow fast relocation without risk, `fbvector` uses a\ntrait `folly::IsRelocatable` defined in `\"folly/Traits.h\"`. By default,\n`folly::IsRelocatable::value` conservatively yields false. If\nyou know that your type `Widget` is in fact relocatable, go right\nafter `Widget`'s definition and write this:\n```cpp\n    // at global namespace level\n    namespace folly {\n      struct IsRelocatable<Widget> : boost::true_type {};\n    }\n```\nIf you don't do this, `fbvector<Widget>` will fail to compile\nwith a `static_assert`.\n\n### Miscellaneous\n***\n\n`fbvector` uses a careful implementation all around to make\nsure it doesn't lose efficiency through the cracks. Some future\ndirections may be in improving raw memory copying (`memcpy` is\nnot an intrinsic in gcc and does not work terribly well for\nlarge chunks) and in furthering the collaboration with\njemalloc. Have fun!\n"
  },
  {
    "path": "folly/docs/Format.md",
    "content": "`folly/Format.h`\n----------------\n\n`folly/Format.h` provides a fast, powerful, type-safe, flexible facility\nfor formatting text, using a specification language similar to Python's\n[str.format](http://docs.python.org/library/string.html#formatstrings).\nBy default, it can format strings, numbers (integral and floating point),\nand dynamically-typed `folly::dynamic` objects, and can extract values from\nrandom-access containers and string-keyed maps.  In many cases, `format` is\nfaster than `sprintf` as well as being fully type-safe.\n\n### Overview\n***\n\nHere are some code samples to get started:\n\n``` Cpp\nusing folly::format;\nusing folly::sformat;\n\n// Objects produced by format() can be streamed without creating\n// an intermediary string; {} yields the next argument using default\n// formatting.\nstd::cout << format(\"The answers are {} and {}\", 23, 42);\n// => \"The answers are 23 and 42\"\n\n// If you just want the string, though, you're covered.\nstd::string result = sformat(\"The answers are {} and {}\", 23, 42);\n// => \"The answers are 23 and 42\"\n\n// To insert a literal '{' or '}', just double it.\nstd::cout << format(\"{} {{}} {{{}}}\", 23, 42);\n// => \"23 {} {42}\"\n\n// Arguments can be referenced out of order, even multiple times\nstd::cout << format(\"The answers are {1}, {0}, and {1} again\", 23, 42);\n// => \"The answers are 42, 23, and 42 again\"\n\n// It's perfectly fine to not reference all arguments\nstd::cout << format(\"The only answer is {1}\", 23, 42);\n// => \"The only answer is 42\"\n\n// Values can be extracted from indexable containers\n// (random-access sequences and integral-keyed maps), and also from\n// string-keyed maps\nstd::vector<int> v {23, 42};\nstd::map<std::string, std::string> m { {\"what\", \"answer\"} };\nstd::cout << format(\"The only {1[what]} is {0[1]}\", v, m);\n// => \"The only answer is 42\"\n\n// format works with pairs and tuples\nstd::tuple<int, std::string, int> t {42, \"hello\", 23};\nstd::cout << format(\"{0} {2} {1}\", t);\n// => \"42 23 hello\"\n\n// Format supports width, alignment, arbitrary fill, and various\n// format specifiers, with meanings similar to printf\n// \"X<10\": fill with 'X', left-align ('<'), width 10\nstd::cout << format(\"{:X<10} {}\", \"hello\", \"world\");\n// => \"helloXXXXX world\"\n\n// Field width may be a runtime value rather than part of the format string\nint x = 6;\nstd::cout << format(\"{:-^*}\", x, \"hi\");\n// => \"--hi--\"\n\n// Explicit arguments work with dynamic field width, as long as indexes are\n// given for both the value and the field width.\nstd::cout << format(\"{2:+^*0}\",\n9, \"unused\", 456); // => \"+++456+++\"\n\n// Format supports printf-style format specifiers\nstd::cout << format(\"{0:05d} decimal = {0:04x} hex\", 42);\n// => \"00042 decimal = 002a hex\"\n\n// Formatter objects may be written to a string using folly::to or\n// folly::toAppend (see folly/Conv.h), or by calling their appendTo(),\n// and str() methods\nstd::string s = format(\"The only answer is {}\", 42).str();\nstd::cout << s;\n// => \"The only answer is 42\"\n\n// Decimal precision usage\nstd::cout << format(\"Only 2 decimals is {:.2f}\", 23.34134534535);\n// => \"Only 2 decimals is 23.34\"\n```\n\n\n### Format string syntax\n***\n\nFormat string (`format`):\n`\"{\" [arg_index] [\"[\" key \"]\"] [\":\" format_spec] \"}\"`\n\n- `arg_index`: index of argument to format; default = next argument.  Note\n  that a format string may have either default argument indexes or\n  non-default argument indexes, but not both (to avoid confusion).\n- `key`: if the argument is a container (C-style array or pointer,\n  `std::array`, vector, deque, map), you may use this\n  to select the element to format; works with random-access sequences and\n  integer- and string-keyed maps.  Multiple level keys work as well, with\n  components separated with \".\"; for example, given\n  `map<string, map<string, string>> m`, `{[foo.bar]}` selects\n  `m[\"foo\"][\"bar\"]`.\n- `format_spec`: format specification, see below\n\nFormat specification:\n`[[fill] align] [sign] [\"#\"] [\"0\"] [width] [\",\"] [\".\" precision] [\".\"] [type]`\n\n- `fill` (may only be specified if `align` is also specified): pad with this\n  character ('` `' (space) or '`0`' (zero) might be useful; space is default)\n- `align`: one of '`<`', '`>`', '`=`', '`^`':\n    - '`<`': left-align (default for most objects)\n    - '`>`': right-align (default for numbers)\n    - '`=`': pad after sign, but before significant digits; used to print\n            `-0000120`; only valid for numbers\n    - '`^`': center\n- `sign`: one of '`+`', '`-`', ' ' (space) (only valid for numbers)\n    - '`+`': output '`+`' if positive or zero, '`-`' if negative\n    - '`-`': output '`-`' if negative, nothing otherwise (default)\n    - '` `' (space): output '` `' (space) if positive or zero, '`-`' if negative\n- '`#`': output base prefix (`0` for octal, `0b` or `0B` for binary, `0x` or\n  `0X` for hexadecimal; only valid for integers)\n- '`0`': 0-pad after sign, same as specifying \"`0=`\" as the `fill` and\n  `align` parameters (only valid for numbers)\n- `width`: minimum field width. May be '`*`' to indicate that the field width\n  is given by an argument. Defaults to the next argument (preceding the value\n  to be formatted) but an explicit argument index may be given following the\n  '`*`'.\n- '`,`' (comma): output comma as thousands' separator (only valid for integers,\n  and only for decimal output)\n- `precision` (not allowed for integers):\n    - for floating point values, number of digits after decimal point ('`f`' or\n      '`F`' presentation) or number of significant digits ('`g`' or '`G`')\n    - for others, maximum field size (truncate subsequent characters)\n- '`.`' (when used after precision or in lieu of precision): Forces a trailing\n  decimal point to make it clear this is a floating point value.\n- `type`: presentation format, see below\n\nPresentation formats:\n\n- Strings (`folly::StringPiece`, `std::string`, `folly::fbstring`,\n  `const char*`):\n    - '`s`' (default)\n- Integers:\n    - '`b`': output in binary (base 2) (\"`0b`\" prefix if '`#`' specified)\n    - '`B`': output in binary (base 2) (\"`0B`\" prefix if '`#`' specified)\n    - '`c`': output as a character (cast to `char`)\n    - '`d`': output in decimal (base 10) (default)\n    - '`o`': output in octal (base 8)\n    - '`O`': output in octal (base 8) (same as '`o`')\n    - '`x`': output in hexadecimal (base 16) (lower-case digits above 9)\n    - '`X`': output in hexadecimal (base 16) (upper-case digits above 9)\n    - '`n`': locale-aware output (currently same as '`d`')\n- `bool`:\n    - default: output \"`true`\" or \"`false`\" as strings\n    - integer presentations allowed as well\n- `char`:\n    - same as other integers, but default is '`c`' instead of '`d`'\n- Floating point (`float`, `double`; `long double` is not implemented):\n    - '`e`': scientific notation using '`e`' as exponent character\n    - '`E`': scientific notation using '`E`' as exponent character\n    - '`f`': fixed point\n    - '`F`': fixed point (same as '`f`')\n    - '`g`': general; use either '`f`' or '`e`' depending on magnitude (default)\n    - '`G`': general; use either '`f`' or '`E`' depending on magnitude\n    - '`n`': locale-aware version of '`g`' (currently same as '`g`')\n    - '`%`': percentage: multiply by 100 then display as '`f`'\n\n\n### Extension\n***\n\nYou can extend `format` for your own class by providing a specialization for\n`folly::FormatValue`.  See `folly/Format.h` and `folly/FormatArg.h` for\ndetails, and the existing specialization for `folly::dynamic` in\n`folly/dynamic-inl.h` for an implementation example.\n"
  },
  {
    "path": "folly/docs/Function.md",
    "content": "`folly/Function.h`\n------------------\n\n`folly::Function` is a polymorphic function wrapper that is not copyable and does not require the wrapped function to be copy constructible. It is similar to `std::function`, but different with respect to some interesting features.\n\nThere are some limitations in `std::function` that `folly::Function` tries to avoid. `std::function` is copy-constructible and requires that the callable that it wraps is copy-constructible as well, which is a constraint that is often inconvenient. In most cases when using a `std::function` you don't make use of its copy-constructibility, so you might sometimes feel like you get back very little in return for a noticeable restriction.\n\nThis restriction becomes apparent when trying to use a lambda capturing a `unique_ptr` (or any non-copyable type) as a callback for a folly::Future.\n``` Cpp\nstd::unique_ptr<Foo> foo_ptr = new Foo;\n\nsome_future.then(\n    [foo_ptr = std::move(foo_ptr)] mutable\n    (int x)\n    { foo_ptr->setX(x); }\n);\n```\nThis piece of code did not compile before `folly::Future` started using `folly::Function` instead of `std::function` to store the callback. Because the lambda captures something non-copyable (the `unique_ptr`), it is not copyable itself. And `std::function` can only store copyable callables.\n\nThe implementation of folly::Future did not make use of the copy-constructibility of `std::function` at any point. There was no benefit from the fact that the `std::function` is copy-constructible, but the fact that it can only wrap copy-constructible callables posed a restriction.\n\nA workaround was available: `folly::MoveWrapper`, which wraps an object that may be non-copyable and implements copy operations by moving the embedded object. Using a `folly::MoveWrapper`, you can capture non-copyable objects in a lambda, and the lambda itself is still copyable and may be wrapped in a `std::function`. It is a pragmatic solution for the above problem, but you have to be a little careful. The problem is that you can’t use a `MoveWrapper` anywhere where copy operations are assumed to behave like actual copy operations. Also, a `folly::MoveWrapper<std::unique_ptr<T>>` essentially behaves like `auto_ptr<T>`. Ask yourself whether you’d want to use lots of `auto_ptr`s in your codebase. And the original question still persists: we very often don’t benefit from copy-constructibility of `std::function`, so why do we have to live with this restriction? I.e. why do we have to use `MoveWrapper`?\n\n`folly::Function` is an actual solution to this problem, as it can wrap non-copyable callables, at the cost of not being copy-constructible, which more often than not is not a relevant restriction. `folly::Future` now uses `folly::Function` to store callbacks, so the good news is: the code example from the top of this note is becoming a perfectly valid way to use future callbacks. The code compiles and behaves as you would expect.\n\nHere are more details about `folly::Function`: much like `std::function`, it wraps an arbitrary object that can be invoked like a given function type. E.g. a `folly::Function<int(std::string, double)>` can wrap any callable object that returns an `int` (or something that is convertible to an `int`) when invoked with a `std::string` and a `double` argument. The function type is a template parameter of `folly::Function`, but the specific type of the callable is not.\n\nOther than copyability, there is one more significant difference between `std::function` and `folly::Function`, and it concerns const-correctness. `std::function` does not enforce const-correctness: it allows you to store mutable callables (i.e. callables that may change their inner state when executed, such as a mutable lambda) and call them in a const context (i.e. when you only have access to a const reference to the `std::function` object). For example:\n``` Cpp\nclass FooBar {\n public:\n  void call_func() const {\n    func_();\n  }\n private:\n  std::function<void()> func_;\n};\n```\nThe `call_func` member function is declared const. However, when it calls `func_()`, it may change the inner state of `func_`, and thereby the inner state of the `FooBar` object. Inside the `FooBar` class, `func_` is like a non-const method that is callable from const methods.\n\nSome people consider `std::function` in the standard broken with respect to this. (Paper N4348 explains this problem in more detail.) It also lists possible ways to fix the problem. `folly::Function`, however, goes a different way: you have to declare whether you want to store a const function, in which case you can invoke any reference (const or non-const) of the `folly::Function`, or a non-const (mutable) function, in which case you need a non-const reference to the `folly::Function` to be able to invoke it. In the above example, let’s say that `func_` stores a const function, which makes it okay that it gets invoked from `call_func` (a const method). Instead of `std::function`, you could use `folly::Function<void() const>` for the `func_` member.\n\nConst-ness is part of a function type. To illustrate:\n``` Cpp\nclass Foo {\n public:\n  int operator()() { return 1; }\n  int operator()(char const*) { return 2; }\n  int operator()(int) { return 3; }\n  int operator()(int) const { return 4; }\n  int operator()(int, int) const { return 5; }\n};\n```\nYou can overload methods multiple times using different argument signatures. Const-ness is part of that signature, so even for the same set of argument types you can overload a const and a non-const version. It’s not even particularly unusual to do that. Take for instance the `begin()` method of STL container types: `begin()` returns an `iterator`, `begin() const` returns a `const_iterator`. `folly::Function` allows you to select a specific overload easily:\n``` Cpp\nfolly::Function<int()> uf1 = Foo();\n// uf1() returns 1\nfolly::Function<int(char const*)> uf2 = Foo();\n// uf2() returns 2\nfolly::Function<int(int)> uf3 = Foo();\n// uf3() returns 3\nfolly::Function<int(int) const> uf4 = Foo();\n// uf4() returns 4\nfolly::Function<int(int, int) const> uf5 = Foo();\n// uf5() returns 5\n```\nIf `cfoo` is a const-reference to a `Foo` object, `cfoo(int)` returns 4. If `foo` is a non-const reference to a `Foo` object, `foo(int)` returns 3. Normal const-to-non-const conversion behavior applies: if you call `foo(int, int)` it will return 5: a non-const reference will invoke the const method if no non-const method is defined. Which leads to the following behavior:\n``` Cpp\nfolly::Function<int(int, int)> uf5nc = Foo();\n// uf5nc() returns 5\n```\nIf you are wondering if the introduction of const function types means that you have to change lots of normal function types to const function types if you want to use `folly::Function`: not really, or at least not as much as you might think. There are only two reasons to use a `folly::Function` with a const function type:\n* a callable object defines both const and non-const `operator()` and you explicitly want to select the const one\n* you need to invoke a `folly::Function` from a const context (i.e. you only have a const reference to the `folly::Function`)\n\nIn practice, you will not need the const variant very often. Adding const to a function type adds a restriction for the callable: it must not change its inner state when invoked. If you don’t care whether it does or not, don’t worry about const!\n\nA `folly::Function<R(Args...) const>` can be converted into a `folly::Function<R(Args...)>`: either way the stored callable will not change its inner state when invoked. The former type expresses and guarantees that, the latter does not. When you get rid of the const, the selected function stays the same:\n``` Cpp\nfolly::Function<int(int)> uf4nc = std::move(uf4);\n// uf4nc() returns 4, not 3!\n```\nIf you want to go the other way, you are talking about a (potentially dangerous) const cast: a callable that may or may not change its inner state is declared as one that guarantees not to do that. Proceed at your own risk! This conversion does not happen implicitly, though:\n``` Cpp\nfolly::Function<int() const> uf1c = folly::constCastFunction(std::move(uf1));\n// uf1c() returns 1\n```\nAdmittedly, seeing const function types as template parameters is unfamiliar. As far as I am aware it happens nowhere in the standard library. But it is the most consistent way to deal with the issue of const-correctness here. Const qualifiers are part of a function type, as a matter of fact. If you require a const-qualified function to be wrapped in a `folly::Function`, just declare it as that! More often than not you will find that you do not need the const qualifier. While writing the `folly::Function` implementation, a good set of unit tests had existed before the const function types got introduced. Not a single one of those unit tests had to be changed: they all compiled and passed after the introduction of const function types. Obviously new ones were added to test the const-correctness. But in your day-to-day use of `folly::Function` you won’t have to worry about const very often.\n\nMemory usage\n============\nLike most implementations of `std::function`, `folly::Function` stores small callable objects in-line and larger callable objects will be stored on the heap. `folly::Function` returns the size of the allocation it has made from its `heapAllocatedMemory()` member function; naturally it will return `0` when the callable is stored in-line.\n"
  },
  {
    "path": "folly/docs/Futures.md",
    "content": "# Futures\n\n### Futures is a framework for expressing asynchronous code in C++ using the Promise/Future pattern.\n\n# Overview\n\nFolly Futures is an async C++ framework inspired by [Twitter's Futures](https://twitter.github.io/finagle/guide/Futures.html) implementation in Scala (see also [Future.scala](https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Future.scala), [Promise.scala](https://github.com/twitter/util/blob/master/util-core/src/main/scala/com/twitter/util/Promise.scala), and friends), and loosely builds upon the existing but anemic Futures code found in the C++11 standard ([std::future](https://en.cppreference.com/w/cpp/thread/future)) and [boost::future](https://www.boost.org/doc/libs/1_53_0/doc/html/thread/synchronization.html#thread.synchronization.futures) (especially >= 1.53.0). Although inspired by the C++11 std::future interface, it is not a drop-in replacement because some ideas don't translate well enough to maintain API compatibility.\n\nThe primary difference from std::future is that you can attach callbacks to Futures (with `thenValue` or `thenTry`), under the control of an executor to manage where work runs, which enables sequential and parallel composition of Futures for cleaner asynchronous code.\n\n# Brief Synopsis\n\n```cpp\n#include <folly/futures/Future.h>\n#include <folly/executors/ThreadedExecutor.h>\nusing namespace folly;\nusing namespace std;\n\nvoid foo(int x) {\n  // do something with x\n  cout << \"foo(\" << x << \")\" << endl;\n}\n\n// ...\n  folly::ThreadedExecutor executor;\n  cout << \"making Promise\" << endl;\n  Promise<int> p;\n  Future<int> f = p.getSemiFuture().via(&executor);\n  auto f2 = move(f).thenValue(foo);\n  cout << \"Future chain made\" << endl;\n\n// ... now perhaps in another event callback\n\n  cout << \"fulfilling Promise\" << endl;\n  p.setValue(42);\n  move(f2).get();\n  cout << \"Promise fulfilled\" << endl;\n```\n\nThis would print:\n\n```\nmaking Promise\nFuture chain made\nfulfilling Promise\nfoo(42)\nPromise fulfilled\n```\n\n## Blog Post\n\nIn addition to this document, there is [a blog post on code.facebook.com (June\n2015)](https://code.facebook.com/posts/1661982097368498/futures-for-c-11-at-facebook/).\n\n# Brief guide\nThis brief guide covers the basics. For a more in-depth coverage skip to the appropriate section.\n\nLet's begin with an example using an imaginary simplified Memcache client interface:\n\n```cpp\nusing std::string;\nclass MemcacheClient {\n public:\n  struct GetReply {\n    enum class Result {\n      FOUND,\n      NOT_FOUND,\n      SERVER_ERROR,\n    };\n\n    Result result;\n    // The value when result is FOUND,\n    // The error message when result is SERVER_ERROR or CLIENT_ERROR\n    // undefined otherwise\n    string value;\n  };\n\n  GetReply get(string key);\n};\n```\n\nThis API is synchronous, i.e. when you call `get()` you have to wait for the result. This is very simple, but unfortunately it is also very easy to write very slow code using synchronous APIs.\n\nNow, consider this traditional asynchronous signature for the same operation:\n\n```cpp\nint async_get(string key, std::function<void(GetReply)> callback);\n```\n\nWhen you call `async_get()`, your asynchronous operation begins and when it finishes your callback will be called with the result. Very performant code can be written with an API like this, but for nontrivial applications the code devolves into a special kind of spaghetti code affectionately referred to as \"callback hell\".\n\nThe Future-based API looks like this:\n\n```cpp\nSemiFuture<GetReply> future_get(string key);\n```\n\nA `SemiFuture<GetReply>` or `Future<GetReply>` is a placeholder for the `GetReply` that we will eventually get. For most of the descriptive text below, Future can refer to either `folly::SemiFuture` or `folly::Future` as the former is a safe subset of the latter. A Future usually starts life out \"unfulfilled\", or incomplete, i.e.:\n\n```cpp\nfut.isReady() == false\nfut.value()  // will throw an exception because the Future is not ready\n```\n\nAt some point in the future, the `Future` will have been fulfilled, and we can access its value.\n\n```cpp\nfut.isReady() == true\nGetReply& reply = fut.value();\n```\n\nFutures support exceptions. If the asynchronous producer fails with an exception, your Future may represent an exception instead of a value. In that case:\n\n```cpp\nfut.isReady() == true\nfut.value() // will rethrow the exception\n```\n\nJust what is exceptional depends on the API. In our example we have chosen not to raise exceptions for `SERVER_ERROR`, but represent this explicitly in the `GetReply` object. On the other hand, an astute Memcache veteran would notice that we left `CLIENT_ERROR` out of `GetReply::Result`, and perhaps a `CLIENT_ERROR` would have been raised as an exception, because `CLIENT_ERROR` means there's a bug in the library and this would be truly exceptional. These decisions are judgement calls by the API designer. The important thing is that exceptional conditions (including and especially spurious exceptions that nobody expects) get captured and can be handled higher up the \"stack\".\n\nSo far we have described a way to initiate an asynchronous operation via an API that returns a Future, and then sometime later after it is fulfilled, we get its value. This is slightly more useful than a synchronous API, but it's not yet ideal. There are two more very important pieces to the puzzle.\n\nFirst, we can aggregate Futures, to define a new Future that completes after some or all of the aggregated Futures complete. Consider two examples: fetching a batch of requests and waiting for all of them, and fetching a group of requests and waiting for only one of them.\n\n```cpp\nMemcacheClient mc;\n\nvector<SemiFuture<GetReply>> futs;\nfor (auto& key : keys) {\n  futs.push_back(mc.future_get(key));\n}\nauto all = collectAll(futs.begin(), futs.end());\n\nvector<SemiFuture<GetReply>> futs;\nfor (auto& key : keys) {\n  futs.push_back(mc.future_get(key));\n}\nauto any = collectAny(futs.begin(), futs.end());\n\nvector<SemiFuture<GetReply>> futs;\nfor (auto& key : keys) {\n  futs.push_back(mc.future_get(key));\n}\nauto anyv = collectAnyWithoutException(futs.begin(), futs.end());\n```\n\n`all` and `any` are Futures (for the exact type and usage see the header files). They will be complete when all/one of futs are complete, respectively. (There is also `collectN()` for when you need some, and `collectAnyWithoutException` when you need one value if some value would be available.)\n\nSecond, we can associate a Future with an executor. An executor specifies where work will run, and we detail this more later. In summary, given an executor we can convert a `SemiFuture` to a `Future` with an executor, or a `Future` on one executor to a `Future` on another executor.\n\nFor example:\n\n```cpp\nfolly::ThreadedExecutor executor;\nSemiFuture<GetReply> semiFut = mc.future_get(\"foo\");\nFuture<GetReply> fut1 = std::move(semiFut).via(&executor);\n```\n\nOnce an executor is attached, a `Future` allows continuations to be attached and chained together monadically. An example will clarify:\n\n```cpp\nSemiFuture<GetReply> semiFut = mc.future_get(\"foo\");\nFuture<GetReply> fut1 = std::move(semiFut).via(&executor);\n\nFuture<string> fut2 = std::move(fut1).thenValue(\n  [](GetReply reply) {\n    if (reply.result == MemcacheClient::GetReply::Result::FOUND)\n      return reply.value;\n    throw SomeException(\"No value\");\n  });\n\nFuture<Unit> fut3 = std::move(fut2)\n  .thenValue([](string str) {\n    cout << str << endl;\n  })\n  .thenTry([](folly::Try<string> strTry) {\n    cout << strTry.value() << endl;\n  })\n  .thenError(folly::tag_t<std::exception>{}, [](std::exception const& e) {\n    cerr << e.what() << endl;\n  });\n```\n\nThat example is a little contrived but the idea is that you can transform a result from one type to another, potentially in a chain, and unhandled errors propagate. Of course, the intermediate variables are optional.\n\nUsing `.thenValue` or `.thenTry` to add callbacks is idiomatic. It brings all the code into one place, which avoids callback hell. `.thenValue` appends a continuation that takes `T&&` for some `Future<T>` and an error bypasses the callback and is passed to the next, `thenTry` takes a callback taking `folly::Try<T>` which encapsulates both value and exception. `thenError(tag_t<ExceptionType>{},...` will bypass a value and only run if there is an exception, the `ExceptionType` template parameter to the tag type allows filtering by exception type; `tag_t<ExceptionType>{}` is optional and if no tag is passed passed the function will be parameterized with a `folly::exception_wrapper`. For C++17 there is a global inline variable and `folly::tag<ExceptionType>` may be passed directly without explicit construction.\n\n\n\nUp to this point we have skirted around the matter of waiting for Futures. You may never need to wait for a Future, because your code is event-driven and all follow-up action happens in a then-block. But if want to have a batch workflow, where you initiate a batch of asynchronous operations and then wait for them all to finish at a synchronization point, then you will want to wait for a Future. Futures have a blocking method called `wait()` that does exactly that and optionally takes a timeout.\n\nFutures are partially threadsafe. A Promise or Future can migrate between threads as long as there's a full memory barrier of some sort. `Future::thenValue` and `Promise::setValue` (and all variants that boil down to those two calls) can be called from different threads. **But**, be warned that you might be surprised about which thread your callback executes on. Let's consider an example, where we take a future straight from a promise, without going via the safer SemiFuture, and where we therefore have a `Future` that does not carry an executor. This is in general something to avoid.\n\n```cpp\n// Thread A\nPromise<Unit> p;\nauto f = p.getFuture();\n\n// Thread B\nstd::move(f).thenValue(x).thenValue(y).thenTry(z);\n\n// Thread A\np.setValue();\n```\n\nThis is legal and technically threadsafe. However, it is important to realize that you do not know in which thread `x`, `y`, and/or `z` will execute. Maybe they will execute in Thread A when `p.setValue()` is called. Or, maybe they will execute in Thread B when `f.thenValue` is called. Or, maybe `x` will execute in Thread A, but `y` and/or `z` will execute in Thread B. There's a race between `setValue` and `then`—whichever runs last will execute the callback. The only guarantee is that one of them will run the callback.\n\nFor safety, `.via` should be preferred. We can chain `.via` operations to give very strong control over where callbacks run:\n\n```cpp\nstd::move(aFuture)\n  .thenValue(x)\n  .via(e1).thenValue(y1).thenValue(y2)\n  .via(e2).thenValue(z);\n```\n\n`x` will execute in the context of the executor associated with `aFuture`. `y1` and `y2` will execute in the context of `e1`, and `z` will execute in the context of `e2`. If after `z` you want to get back to the original context, you need to get there with a call to `via` passing the original executor.\n\n# You make me Promises, Promises\n\nIf you are wrapping an asynchronous operation, or providing an asynchronous API to users, then you will want to make `Promise`s. Every Future has a corresponding Promise (except Futures that spring into existence already completed, with `makeFuture()`). Promises are simple: you make one, you extract the Future, and you fulfill it with a value or an exception. Example:\n\n```cpp\nPromise<int> p;\nSemiFuture<int> f = p.getSemiFuture();\n\nf.isReady() == false\n\np.setValue(42);\n\nf.isReady() == true\nf.value() == 42\n```\n\nand an exception example:\n\n```cpp\nPromise<int> p;\nSemiFuture<int> f = p.getSemiFuture();\n\nf.isReady() == false\n\np.setException(std::runtime_error(\"Fail\"));\n\nf.isReady() == true\nf.value() // throws the exception\n```\n\nIt's good practice to use setWith which takes a function and automatically captures exceptions, e.g.\n\n```cpp\nPromise<int> p;\np.setWith([]{\n  try {\n    // do stuff that may throw\n    return 42;\n  } catch (MySpecialException const& e) {\n    // handle it\n    return 7;\n  }\n  // Any exceptions that we didn't catch, will be caught for us\n});\n```\n"
  },
  {
    "path": "folly/docs/GroupVarint.md",
    "content": "`folly/GroupVarint.h`\n---------------------\n\n`folly/GroupVarint.h` is an implementation of variable-length encoding for 32-\nand 64-bit integers using the Group Varint encoding scheme as described in\nJeff Dean's [WSDM 2009 talk][wsdm] and in [Information Retrieval: Implementing\nand Evaluating Search Engines][irbook].\n\n[wsdm]: http://research.google.com/people/jeff/WSDM09-keynote.pdf\n[irbook]: http://www.ir.uwaterloo.ca/book/addenda-06-index-compression.html\n\nBriefly, a group of four 32-bit integers is encoded as a sequence of variable\nlength, between 5 and 17 bytes; the first byte encodes the length (in bytes)\nof each integer in the group.  A group of five 64-bit integers is encoded as a\nsequence of variable length, between 7 and 42 bytes; the first two bytes\nencode the length (in bytes) of each integer in the group.\n\n`GroupVarint.h` defines a few classes:\n\n* `GroupVarint<T>`, where `T` is `uint32_t` or `uint64_t`:\n\n    Basic encoding / decoding interface, mainly aimed at encoding / decoding\n    one group at a time.\n\n* `GroupVarintEncoder<T, Output>`, where `T` is `uint32_t` or `uint64_t`,\n  and `Output` is a functor that accepts `StringPiece` objects as arguments:\n\n    Streaming encoder: add values one at a time, and they will be\n    flushed to the output one group at a time.  Handles the case where\n    the last group is incomplete (the number of integers to encode isn't\n    a multiple of the group size)\n\n* `GroupVarintDecoder<T>`, where `T` is `uint32_t` or `uint64_t`:\n\n    Streaming decoder: extract values one at a time.  Handles the case where\n    the last group is incomplete.\n\nThe 32-bit implementation is significantly faster than the 64-bit\nimplementation; on platforms supporting the SSSE3 instruction set, we\nuse the PSHUFB instruction to speed up lookup, as described in [SIMD-Based\nDecoding of Posting Lists][cikmpaper] (CIKM 2011).\n\n[cikmpaper]: http://www.stepanovpapers.com/CIKM_2011.pdf\n\nFor more details, see the header file `folly/GroupVarint.h` and the\nassociated test file `folly/test/GroupVarintTest.cpp`.\n"
  },
  {
    "path": "folly/docs/Hazptr.md",
    "content": "RCU and Hazard Pointers\n-----------------------\n\n# Overview\n\nHazard pointers are a lifetime-protection mechanism for shared objects published\nby producers (aka writers) and observed by consumers (aka readers). They are\nasymmetric and biased towards consumers.\n\nBut that is a confusing paragraph! Let us start more simply.\n\nLet us consider the motivating case of why we need some lifetime-protection\nmechanism for shared objects in the first place. It looks like this:\n\n``` cpp\ntemplate <typename object>\nstruct shared_object {\nprivate:\n  std::shared_mutex mut_;\n  object* obj_{nullptr};\n\npublic:\n  /// release operation\n  void publish(std::unique_ptr<object> obj) noexcept {\n    std::unique_lock lock{mut_};\n    auto old = std::exchange(obj_, obj.release());\n    lock.unlock();\n    delete old;\n  }\n\n  /// acquire operation\n  object* observe() const noexcept {\n    std::shared_lock lock{mut_};\n    return obj_;\n  }\n};\n```\n\nThis example is utterly broken. Suppose we have these operations:\n``` cpp\nstruct config;\nvoid do_something_with(config&);\n\nshared_object<config> cfg;\n\nvoid producer() { // Thread A\n  cfg.publish(std::make_unique<config>(/*...*/));\n}\nvoid consumer() { // Thread B\n  if (config* ptr = cfg.observe()) {\n    do_something_with(*ptr);\n  }\n}\n```\nThen Thread B might be using the object pointed to by `ptr` concurrently with\nThread A deleting that very object! Formally, that is a category of undefined\nbehavior called a data-race, but it means that the program will, *at best*,\ncrash immediately or, in increasing order of badness, silently corrupt data,\nsilently mix data belonging to different users, or silently perform destructive\nactions like launching the missiles.\n\nWhat Thread B needs is a way to continue using the object pointed to by `ptr`\nby arranging that neither Thread B nor any other thread will delete that object\nuntil Thread B is done using it.\n\nThe most common technique to mitigate this problem is to extend the shared-lock\nlifetime to cover the entire critical section of observing the current object,\nnot just the critical section of copying the pointer to the current object. Here\nis how that looks:\n\n``` cpp\ntemplate <typename object>\nstruct shared_object {\nprivate:\n  mutable std::shared_mutex mut_;\n  object* obj_;\n\npublic:\n  struct protected_ptr {\n  private:\n    friend shared_object;\n\n    std::shared_lock<std::shared_mutex> lock_;\n    object* obj_;\n\n    protected_ptr(std::shared_mutex& mut, object*& obj) noexcept\n        : lock_{mut}, obj_{obj} {}\n\n    protected_ptr(protected_ptr&&) = delete;\n    protected_ptr(protected_ptr const&) = delete;\n    protected_ptr& operator=(protected_ptr&&) = delete;\n    protected_ptr& operator=(protected_ptr const&) = delete;\n\n  public:\n    explicit operator bool() const noexcept { return obj_; }\n    object* operator->() const noexcept { return obj_; }\n    object& operator*() const noexcept { return *obj_; }\n  };\n\n  ~shared_object() {\n    delete obj_;\n  }\n\n  void publish(std::unique_ptr<object> obj) noexcept {\n    std::unique_lock lock{mut_};\n    auto old = std::exchange(obj_, obj.release());\n    lock.unlock();\n    delete old;\n  }\n\n  protected_ptr observe() const noexcept {\n    return protected_ptr(mut_, obj_);\n  }\n};\n```\n\nBut there is a problem. The critical section of observing the current object may\neasily be very long. Indeed, there may be two or more threads all observing the\nshared object, and there might never be a moment when the publisher can acquire\nan exclusive lock on the shared mutex. If the shared mutex has reader-priority,\nthen the publisher may starve, waiting forever without making any progress.\nAlternatively, if the shared mutex has writer-priority, then the publisher may\nwait a very long time to acquire the exclusive mutex. As it waits, it blocks all\nnew observers that want to acquire a shared lock - new observers must wait for\nall existing observers to complete their critical sections before beginning\ntheir critical sections. Either way, that is a lot of waiting. But what we want\nis a technique that does not involve this risk of waiting.\n\nNow let us consider the typical publish/observe lifetime-protection mechanism\nthat we tend to use for shared objects which do not risk blocking publishers or\nobservers. It looks like this:\n\n``` cpp\ntemplate <typename object>\nstruct shared_object {\nprivate:\n  std::shared_mutex mut_;\n  std::shared_ptr<object> obj_;\n\npublic:\n  /// release operation\n  void publish(std::shared_ptr<object> obj) noexcept {\n    std::unique_lock lock{mut_};\n    std::swap(obj_, obj); // destroy only after lock expires\n  }\n\n  /// acquire operation\n  std::shared_ptr<object> observe() const noexcept {\n    std::shared_lock lock{mut_};\n    return obj_;\n  }\n};\n```\n\nA consumer can ask the `shared_object` instance for lifetime-protected access to\nwhatever shared object was most recently published and that it currently holds.\n\nThere are two key aspects to which to pay attention:\n* Lifetime protection, via the shared pointer.\n* Synchronization, via the shared mutex:\n  * Synchronization between two producers publishing.\n  * Synchronization between two consumers observing.\n  * Synchronization between a producer publishing and a consumer observing.\n\nAs we look at more techniques, the exact mechanisms for lifetime protection and\nsynchronization will change, but these will still be the problems being solved.\n\nWith this technique, there is still the risk of waiting. However, the critical\nsections in the observer with this technique are extremely short - a handful of\ninstructions - at least unless the operating system suspends the thread during\nthe critical section. As long as that does not happen, observers will not block\npublishers from publishing.\n\nA more advanced version looks like:\n\n``` cpp\ntemplate <typename object>\nstruct shared_object {\nprivate:\n  std::atomic<std::shared_ptr<object>> atom_;\n\npublic:\n  /// release operation\n  void publish(std::shared_ptr<object> obj) noexcept {\n    atom_.store(obj, std::memory_order_release);\n  }\n\n  /// acquire operation\n  std::shared_ptr<object> observe() const noexcept {\n    return atom_.load(std::memory_order_acquire);\n  }\n};\n```\n\nThis version swaps out a mutex and in an atomic. With the caveat that the easy\nimplementation of the atomic-shared-ptr is in terms of a shared-mutex! So YMMV.\n\nThere are alternative implementations of atomic-shared-ptr. Folly has one, for\nexample. A version using `folly::atomic_shared_ptr<object>` would look exactly\nlike the version using `std::atomic<std::shared_ptr<object>>` with just the one\nsubstitution, but may perform better.\n\nThese prototypical publish/observe lifetime-protection mechanisms are sufficient\nfor most use-cases. But not for all. The trouble with them in some cases is that\nthey are asymmetric: observation is extremely hot and dominates publication. But\nthe prototypical mechanisms have symmetric costs, with the cost of observation\nbeing comparable with the cost of publication, both involving several atomic\nread-modify-write operations. And the trouble is that atomic read-modify-write\noperations can be too costly for the hottest paths. For example, shared-mutex\nlock and unlock operations each requires at least one atomic compare-exchange.\nAnd a shared-ptr copy requires at least two atomic fetch-add operations, one in\nthe constructor and one in the destructor. Especially when many consumer threads\nare involved - atomic read-modify-write operations scale extremely poorly with\ncore count. So we need a technique which amortizes or minimizes use of atomic\nread-modify-write operations in the observation path and which scales very well\nwith core count.\n\nFolly offers some exotic solutions here. `folly::ReadMostlySharedPtr`is a\nversion of of shared-ptr accelerated with thread-local caches and thread-local\nrefcount operations. `folly::CoreCachedSharedPtr` is a version of shared-ptr\naccelerated with a fixed-size slab of copies to ensure that refcount operations\nbe relatively uncontended, but can be subject to exotic race conditions. And\n`folly::AtomicCoreCachedSharedPtr` is a version of that where the operations are\ntransactional and not subject to those exotic races. These facilities do help\nwith scaling over many cores, but are still costly.\n\nThere are two publish/observe lifetime-protection mechanisms for shared objects\nwhich are designed for these hot asymmetric cases: RCU and Hazard Pointers.\n\nRCU is slightly faster but coarse-grained, while Hazard Pointers is fine-grained\nbut slightly slower. But both optimize for hot paths needing lifetime-protected\nobservation of shared objects.\n\nRCU might also be thought of as a scalable alternative to reader-writer locking,\nand Hazard Pointers as a scalable alternative to reference counting. There is\nconsiderable overlap between the two, and considerable difference with locking\nand reference counting, but it is sometimes a helpful introductory mental model.\n\nBoth mechanisms require uses not to delete shared objects naively, since readers\nmay concurrently be reading the shared objects. Rather, uses must *retire* the\nshared objects, which is an operation that marks them for future deletion. This\nscheme is called *deferred reclamation*. Uses *retire* shared objects when they\nare no longer needed, and then a garbage collection pass at some later point\n*reclaims* all retired objects for which there cannot possibly be any concurrent\nreaders still reading them.\n\nWhat these mechanisms give up is deterministic deletion. With shared pointers,\nwhether protected by a mutex or by an atomic, the shared object will be deleted\nthe exact moment it is no longer needed. Either the publisher or the observer\nwill internally decrement the refcount and, when the refcount becomes zero, the\ndecrementing operation will internally delete the shared object. But with RCU\nand Hazard Pointers, this is not possible. Instead, there is another system, the\n*domain*, with which the publishers and the observers cooperate to track when\nit is safe to destroy shared objects - very much like the garbage collector in\nmany programming languages, but explicit and opt-in.\n\nThe basic tradeoff with these mechanisms is:\n* Observation is extremely fast, scales linearly with core count, and neither\n  waits nor causes any other operations to wait (modulo synchronization within\n  the allocator and when initializing global variables).\n* Publication is pessimized compared with typical techniques.\n* Reclamation is deferred rather than deterministic and is extremely pessimized.\n  It is batched so that the overhead may be amortized over many publications.\n  Because it is deferred, memory usage can be unexpectedly large.\n* These mechanisms can seem like black magic - they have a reputation for being\n  complex, unintuitive, and dangerous.\n\n# RCU Shared Object\n\nA typical implementation of a `shared_object` type that is similar to the prior\nsymmetric examples might look like this:\n\n``` cpp\ntemplate <typename object>\nstruct shared_object {\nprivate:\n  rcu_domain& domain_;\n  std::atomic<object*> atom_;\n\n  void retire(object* ptr) {\n    if (ptr) {\n      rcu_retire(ptr, {}, domain);\n    }\n  }\n\npublic:\n  struct protected_ptr {\n  private:\n    friend shared_object;\n\n    // curiously, the API is scoped_lock<rcu_domain>\n    std::scoped_lock<rcu_domain> prot_; // protects obj_ during prot_'s lifetime\n    object* obj_; // access is valid while prot_ remains alive\n\n    protected_ptr(\n        rcu_domain& domain,\n        std::atomic<object*>& atom) noexcept\n        : prot_{domain}, // init'd first to protect the load below\n          obj_{atom.load(std::memory_order_acquire)} {}\n\n    protected_ptr(protected_ptr&&) = delete;\n    protected_ptr(protected_ptr const&) = delete;\n    protected_ptr& operator=(protected_ptr&&) = delete;\n    protected_ptr& operator=(protected_ptr const&) = delete;\n\n  public:\n    explicit operator bool() const noexcept { return obj_; }\n    object* operator->() const noexcept { return obj_; }\n    object& operator*() const noexcept { return *obj_; }\n  };\n\n  ~shared_object() {\n    retire(atom_.load(std::memory_order_relaxed));\n  }\n\n  /// release operation\n  void publish(std::unique_ptr<object> obj) {\n    retire(atom_.exchange(obj.release(), std::memory_order_acq_rel));\n  }\n\n  /// acquire operation\n  protected_ptr observe() const {\n    return protected_ptr(domain_, atom_);\n  }\n};\n```\n\n# Hazard Pointers Shared Object\n\nA typical implementation of a `shared_object` type that is similar to the prior\nsymmetric examples might look like this:\n\n``` cpp\ntemplate <std::derived_from<hazptr_obj> object>\nstruct shared_object {\nprivate:\n  hazptr_domain& domain_;\n  std::atomic<object*> atom_;\n\n  void retire(object* ptr) {\n    if (ptr) {\n      domain_.retire(ptr);\n    }\n  }\n\npublic:\n  struct protected_ptr {\n  private:\n    friend shared_object;\n\n    hazptr_holder prot_; // protects obj_ during prot_'s lifetime\n    object* obj_; // access is valid while prot_ remains alive\n\n    protected_ptr(\n        hazptr_domain& domain,\n        std::atomic<object*>& atom) noexcept\n        : prot_{make_hazard_pointer(domain)}, // init'd first for use below\n          obj_{prot_.protect(atom)} {}\n\n    protected_ptr(protected_ptr&&) = delete;\n    protected_ptr(protected_ptr const&) = delete;\n    protected_ptr& operator=(protected_ptr&&) = delete;\n    protected_ptr& operator=(protected_ptr const&) = delete;\n\n  public:\n    explicit operator bool() const noexcept { return obj_; }\n    object* operator->() const noexcept { return obj_; }\n    object& operator*() const noexcept { return *obj_; }\n  };\n\n  ~shared_object() {\n    retire(atom_.load(std::memory_order_relaxed));\n  }\n\n  /// release operation\n  void publish(std::unique_ptr<object> obj) {\n    retire(atom_.exchange(obj.release(), std::memory_order_acq_rel));\n  }\n\n  /// acquire operation\n  protected_ptr observe() const {\n    return protected_ptr(domain_, atom_);\n  }\n};\n```\n\n# Explanation\n\nA call to `observe()` returns a `protected_ptr`, which may be dereferenced to\ngive access to the protected object - but only while the `protected_ptr` stays\nalive. This is similar to how `shared_object::observe()` with `std::shared_ptr`\nreturned a `std::shared_ptr`, which may also be dereferenced to give access to\nthe protected object - but only while the `std::shared_ptr` stays alive.\n\nThe call to `observe()` here is faster than using a `std::shared_mutex` and a\n`std::shared_ptr` together, and is also faster than using even a well-optimized\n`std::atomic<std::shared_ptr>`. But the price is that the call to `publish()` is\nmuch more expensive.\n\nSo it makes sense to use the RCU or the Hazard Pointers version when observation\ndominates publication by at least an order of magnitude.\n\nA `scoped_lock<rcu_domain>` can lock and unlock a domain extremely quickly,\nusing only load-acquire and store-release instructions for synchronization.\nProtection of the pointee's lifetime is reset when the lock is released or\nwhen its lifetime ends. This is faster than the symmetric versions. It is also\ntypically faster than hazard pointers in practical usage, but not always. It is\ncoarse-grained and does not specifically protect any individual shared objects,\nso a single domain-lock critical section can protect many shared objects all at\nonce, with the `lock` and `unlock` operations amortized across them. But for\ncases using the default domain, where only one object needs to be protected at\na time and with certain other constraints, hazard pointers can be faster than\nRCU.\n\nA `hazptr_holder` can load a pointer from an atomic-pointer and protect that\npointee's lifetime in a single transaction. This transaction is extremely fast,\nusing only load-acquire and store-release instructions for synchronization.\nProtection of that pointee's lifetime is reset when the `hazptr_holder` loads\nand protects another pointer, when its lifetime ends, or when protection is\nexplicitly reset.\n\nCreation and destruction of a `hazptr_holder` is fairly fast. The ideal case is,\nhowever, to use a single `hazptr_holder` repeatedly to protect and unprotect\nmultiple pointers, amortizing the cost of `make_hazard_pointer` across multiple\nprotections. For example, this makes sense when walking a linked data structure\nlike a linked list or linked tree, where each node during traversal requires its\nown separate lifetime-protection.\n\nHowever, even using a `hazptr_holder` just once for a single protection is still\nfaster than the prototypical mechanisms like shared-lock with shared-ptr or\natomic-shared-ptr.\n\n## Hazard Pointer Patterns\n\nWith Hazard Pointers, we conceptually have readers and writers. Writers store\nthe address of a shared object to an atomic-pointer, while readers load the\naddress of the shared object from the atomic-pointer.\n\nThe typical writer pattern is:\n* create a new object,\n* swap its address into the atomic-pointer using atomic-exchange, and\n* retire the old object.\n\nIn some cases, the new object is wholly new; in other cases, the new object is a\ncopy of the old object with point modifications.\n\nStoring the address of the new object into the atomic-pointer must be a release\noperation, while loading the address of the old object from the atomic-pointer\nmust be an acquire operation. Retirement of the old object must only occur after\nthe address of the old object has been removed from the atomic-pointer so that\nno new readers can begin protection on the old object concurrently with its\nretirement. Retirement of the old object may occur concurrently with existing\nreaders that have already protected the old object, but occurring concurrently\nwith new readers would be forbidden.\n\nRetirement of the old object schedules the old object for deferred reclamation.\nThat means the old object will be reclaimed - typically deleted - at some later\npoint, once all of the hazard pointers that protect its lifetime have expired.\n\n``` cpp\nclass object : public hazptr_obj_base<object> {\n  // ...\n};\n\nhazptr_domain& domain = default_hazptr_domain(); // or a non-default domain\nstd::atomic<object*> atom;\n\nobject make_from_scratch();\nobject make_from_update(object const&);\n\nobject make_object(object const* curr) {\n  return curr ? make_from_update(*curr) : make_from_scratch();\n}\n\n/// single writer, or multiple writers serialized by a mutex so they do not race\nvoid single_writer() {\n  object* prev = atom.load(std::memory_order_relaxed);\n  object* next = new object(make_from_scratch());\n  atom.store(next, std::memory_order_release);\n  if (prev) {\n    prev->retire(domain);\n  }\n}\n\n/// multiple writers that can race with each other\nvoid multi_writer() {\n  object* next = new object(make_from_scratch());\n  if (object* prev = atom.exchange(next, std::memory_order_acq_rel)) {\n    prev->retire(domain);\n  }\n}\n\n/// single writer, or multiple writers serialized by a mutex so they do not race\n///\n/// monotonic means that the writer must observe the current object, make a new\n/// object from it, and install publish the new object in a single transaction\nvoid single_writer_monotonic() {\n  object* prev = atom.load(std::memory_order_relaxed);\n  object* next = new object(make_object(prev));\n  atom.store(next, std::memory_order_release);\n  if (prev) {\n    prev->retire(domain);\n  }\n}\n\n/// multiple writers that can race with each other\n///\n/// monotonic means that the writer must observe the current object, make a new\n/// object from it, and install publish the new object in a single transaction\nvoid multi_writer_monotonic() {\n  auto hazptr = make_hazard_pointer(domain);\n  object* curr = hazptr.protect(atom); // load-acquire on atom\n  while (true) {\n    object* next = new object(make_object(curr));\n    if (atom.compare_exchange_strong(\n          curr, next, std::memory_order_acq_rel)) {\n      hazptr.reset_protection();\n      curr->retire(domain);\n      return;\n    }\n    delete next;\n    // this loop omits a single load-relaxed v.s. hazptr.protect(atom):\n    while (!hazptr.try_protect(curr, atom)) {} // load-acquire on atom\n  }\n}\n```\n\nThe typical reader pattern is:\n* create a `hazptr_holder`,\n* atomically load from the atomic-pointer and protect the resulting pointer, and\n* hold the `hazptr_holder` alive for the entire duration of accessing the shared\n  object.\n\n``` cpp\nclass object : public hazptr_obj_base<object> {\n  // ...\n};\n\nhazptr_domain<>& domain = default_hazptr_domain(); // or a non-default domain\nstd::atomic<object*> atom;\n\nvoid reader() {\n  auto hazptr = make_hazard_pointer(domain);\n  if (object const* curr = hazptr.protect(atom)) {\n    // the lifetime of the object pointed-to by `curr` will not end until hazard\n    // pointer `hazptr` leaves scope below\n    for (auto const& item : curr->data()) {\n      // use `item`, etc ...\n    }\n  }\n}\n```\n\n## Details\n\nA `hazptr_holder` does two things:\n* Extend the lifetime of a shared object.\n* Synchronize between readers and the deferred deletion arising from retirement.\n\nA `hazptr_holder` extends the lifetime of a shared object. But this does not\ninclude synchronizing between concurrent readers. So a shared object either must\nbe immutable or, if concurrent accesses would be data-races, must have its own\nsynchronization.\n* If immutable, there would structurally be no races between readers.\n* If mutable, concurrent accesses could be data-races. Readers must synchronize\n  their accesses to the shared object, such as with a shared mutex owned by the\n  object and protecting its internal state.\n\nA `hazptr_holder` extends the lifetime of a shared object from the moment the\nshared object pointer is returned by a call to member `protect` or gotten from a\nsuccessful call to member `try_protect`, until the moment protection is reset\nwith a call to member `reset_protection` or destruction of the `hazptr_holder`.\n\nThe `hazptr_holder` is not thread-safe. Its implementation does synchronize\ninternally with other `hazptr_holder`s and with the `hazptr_domain`, but that\ndoes not allow concurrent use of a single `hazptr_holder` instance. In a similar\nway, a single instance of `std::shared_ptr` is also not thread-safe, even though\nmultiple instances may internally synchronize between each other.\n\nIf constructed with the default domain, it is thread-sensitive: it must only be\nused during its lifetime in the specific thread in which it was constructed. The\ndefault domain is a global singleton which lives for the entire process and, as\noptimization, hazard pointers constructed with the default domain rely on state\nwhich is thread-local. Other domains can provide some measure of isolation, but\ndo not have this guaranteed lifetime or optimization. The benefits of using the\ndefault domain are: (1) global lifetime and (2) faster `make_hazptr_holder`. The\nbenefits of using the non-default domain are: (1) thread-insensitivity and lower\ncost of reclamation.\n\nA `hazptr_holder` synchronizes readers with the deferred deletion arising from\nretirement.\n* Explicit deletion is typically discouraged. Retirement is typically\n  recommended.\n* Explicit deletion concurrent with readers is undefined behavior and forbidden.\n  Hazard Pointers cannot protect readers from concurrent deletion. This is why\n  Hazard Pointers offers retirement as the way to mark shared objects for future\n  deletion.\n* Each object must be retired or deleted exactly once, and the caller must\n  arrange for this exactly-once retirement or deletion.\n* Multiple retirements or deletions of the same object, whether serialized or\n  concurrent, is undefined behavior and forbidden.\n\nTechnically, a `hazptr_holder` synchronizes readers with however the protected\nobject might implement its own reclamation. Reclamation as deletion is the most\ncommon form of reclamation and is the default, but, for example, an object can\nreclaim itself by reinsertion into an object-pool. Regardless, reclamation of an\nobject is scheduled by retirement of the object, and is deferred until all live\n`hazptr_holder`s which protect the lifetime of the object reset that protection.\n\nWriters must typically mutually-exclude themselves somehow.\n* Structurally, by having only a single writer thread.\n* Pessimistically, with an exclusive mutex.\n* Optimistically, with an atomic and the use of atomic-exchange.\n\nThe writer must only retire an object after having removed all pointers to it\nfrom all atomic variables which might be the object of hazptr-protection, such\nas with the `store`, `exchange`, or `compare_exchange` patterns noted above.\n\nFor both RCU and Hazard Pointers, there are two possible ways that a deferred\nreclamation pass happens:\n* Out-of-line in a special deferred-reclamation thread, if the domain has been\n  configured with a reclamation thread pool.\n* In-line during some arbitrary retirement, when the domain decides to perform\n  a batch reclamation pass. For example, if there are sufficiently many objects\n  marked as retired. A domain configured with a reclamation thread pool will not\n  perform in-line reclamation during retirement.\n\nIn either case, the constraints are:\n* Only retired objects are subject to reclamation.\n* Currently-protected objects are not subject to reclamation, even if retired.\n\n# For More Info\n\n* [Linux Kernel Review Checklist for RCU Patches](https://www.kernel.org/doc/Documentation/RCU/checklist.rst)\n  has a ton of useful information on RCU. The concrete details are specific to\n  usage of RCU within the Linux Kernel, but much of the document generalizes.\n* [Linux Kernel RCU Handbook](https://docs.kernel.org/RCU/index.html).\n"
  },
  {
    "path": "folly/docs/Histogram.md",
    "content": "`folly/stats/Histogram.h`\n-------------------\n\n### Classes\n***\n\n#### `Histogram`\n\n`Histogram.h` defines a simple histogram class, templated on the type of data\nyou want to store.  This class is useful for tracking a large stream of data\npoints, where you want to remember the overall distribution of the data, but do\nnot need to remember each data point individually.\n\nEach histogram bucket stores the number of data points that fell in the bucket,\nas well as the overall sum of the data points in the bucket.  Note that no\noverflow checking is performed, so if you have a bucket with a large number of\nvery large values, it may overflow and cause inaccurate data for this bucket.\nAs such, the histogram class is not well suited to storing data points with\nvery large values.  However, it works very well for smaller data points such as\nrequest latencies, request or response sizes, etc.\n\nIn addition to providing access to the raw bucket data, the `Histogram` class\nalso provides methods for estimating percentile values.  This allows you to\nestimate the median value (the 50th percentile) and other values such as the\n95th or 99th percentiles.\n\nAll of the buckets have the same width.  The number of buckets and bucket width\nis fixed for the lifetime of the histogram.  As such, you do need to know your\nexpected data range ahead of time in order to have accurate statistics.  The\nhistogram does keep one bucket to store all data points that fall below the\nhistogram minimum, and one bucket for the data points above the maximum.\nHowever, because these buckets don't have a good lower/upper bound, percentile\nestimates in these buckets may be inaccurate.\n\n#### `HistogramBuckets`\n\nThe `Histogram` class is built on top of `HistogramBuckets`.\n`HistogramBuckets` provides an API very similar to `Histogram`, but allows a\nuser-defined bucket class.  This allows users to implement more complex\nhistogram types that store more than just the count and sum in each bucket.\n\nWhen computing percentile estimates `HistogramBuckets` allows user-defined\nfunctions for computing the average value and data count in each bucket.  This\nallows you to define more complex buckets which may have multiple different\nways of computing the average value and the count.\n\nFor example, one use case could be tracking timeseries data in each bucket.\nEach set of timeseries data can have independent data in the bucket, which can\nshow how the data distribution is changing over time.\n\n### Example Usage\n***\n\nSay we have code that sends many requests to remote services, and want to\ngenerate a histogram showing how long the requests take.  The following code\nwill initialize histogram with 50 buckets, tracking values between 0 and 5000.\n(There are 50 buckets since the bucket width is specified as 100.  If the\nbucket width is not an even multiple of the histogram range, the last bucket\nwill simply be shorter than the others.)\n\n``` Cpp\n    folly::Histogram<int64_t> latencies(100, 0, 5000);\n```\n\nThe addValue() method is used to add values to the histogram.  Each time a\nrequest finishes we can add its latency to the histogram:\n\n``` Cpp\n    latencies.addValue(now - startTime);\n```\n\nYou can access each of the histogram buckets to display the overall\ndistribution.  Note that bucket 0 tracks all data points that were below the\nspecified histogram minimum, and the last bucket tracks the data points that\nwere above the maximum.\n\n``` Cpp\n    auto numBuckets = latencies.getNumBuckets();\n    cout << \"Below min: \" << latencies.getBucketByIndex(0).count << \"\\n\";\n    for (unsigned int n = 1; n < numBuckets - 1; ++n) {\n      cout << latencies.getBucketMin(n) << \"-\" << latencies.getBucketMax(n)\n           << \": \" << latencies.getBucketByIndex(n).count << \"\\n\";\n    }\n    cout << \"Above max: \"\n         << latencies.getBucketByIndex(numBuckets - 1).count << \"\\n\";\n```\n\nYou can also use the `getPercentileEstimate()` method to estimate the value at\nthe Nth percentile in the distribution.  For example, to estimate the median,\nas well as the 95th and 99th percentile values:\n\n``` Cpp\n    int64_t median = latencies.getPercentileEstimate(0.5);\n    int64_t p95 = latencies.getPercentileEstimate(0.95);\n    int64_t p99 = latencies.getPercentileEstimate(0.99);\n```\n\n### Thread Safety\n***\n\nNote that `Histogram` and `HistogramBuckets` objects are not thread-safe.  If\nyou wish to access a single `Histogram` from multiple threads, you must perform\nyour own locking to ensure that multiple threads do not access it at the same\ntime.\n"
  },
  {
    "path": "folly/docs/MapUtils.md",
    "content": "`folly/container/MapUtil.h`\n---------------------------\n\nMapUtil provides convenience functions to get a value from a map.\n\n### Motivation & Comparisons\n\nFetching a value in a map can be tedious. For example, `map.at()` will throw an exception if the key is not present:\n```cpp\ntry {\n    auto& v = m.at(1);\n} catch (...) {\n    // do something!\n}\n```\n\nWe can avoid the exception handling by using `find`:\n```cpp\nauto it = m.find(1);\nif (it != m.end()) {\n    auto& v = it->second;\n}\n```\n\nBoth of these cases can be written more succinctly and without exception handling using folly/MapUtil.h\n```cpp\nif (const auto* v = folly::get_ptr(m, 1)) {\n    // do stuff with `v` which is guaranteed to be present in this block\n}\n```\n\nWe can also easily lookup values in nested maps:\n```cpp\nstd::map<int, std::map<int, int>> m {{1, {{2, 3}}}};\n// using find\nauto it1 = m.find(1);\nif (it1 != m.end()) {\n    auto it2 = it1->second.find(2);\n    if (it2 != it1->second.end()) {\n        auto& v = it2->second;\n    }\n}\n// using folly/MapUtil.h\nif (const auto* v = folly::get_ptr(m, 1, 2)) {\n    // do stuff with `v`!\n}\n```\n\n### Sample\nThe corresponding unit test suite has a very thorough collection of examples: `folly/container/test/MapUtilTest.cpp`,\nfor brevity, here is a small glimpse:\n\n***\n```cpp\nstd::map<int, int> m {{1, 2}};\n\n// Return a pointer (nullptr if the key is not in the map)\nEXPECT_EQ(2, *get_ptr(m, 1));\nEXPECT_TRUE(get_ptr(m, 2) == nullptr);\n\n// Return a value (or default)\nEXPECT_EQ(2, get_default(m, 1, 42));\nEXPECT_EQ(42, get_default(m, 2, 42));\n\n// Return an optional (empty if the key is not in the map)\nEXPECT_TRUE(get_optional(m, 1).has_value());\nEXPECT_EQ(2, get_optional(m, 1).value());\n\n// Return a reference\nconst int i = 42;\nEXPECT_EQ(2, get_ref_default(m, 1, i));\nEXPECT_EQ(42, get_ref_default(m, 2, i));\n\n// Even works for nested maps!\nstd::map<int, map<int, map<int, map<int, int>>>> nested{{1, {{2, {{3, {{4, 5}}}}}}}};\nEXPECT_EQ(5, *get_ptr(nested, 1, 2, 3, 4));\nEXPECT_TRUE(get_ptr(nested, 1, 2, 3, 4));\nEXPECT_FALSE(get_ptr(nested, 1, 2, 3, 0));\n```\n***\n"
  },
  {
    "path": "folly/docs/Overview.md",
    "content": "### `folly/`\n\n### Components\n\nBelow is a list of (some) Folly components in alphabetical order, along with\na brief description of each.\n\n#### `Arena.h`, `ThreadCachedArena.h`\n\nSimple arena for memory allocation: multiple allocations get freed all\nat once. With threaded version.\n\n#### [`AtomicHashMap.h`](AtomicHashMap.md), `AtomicHashArray.h`, `AtomicLinkedList.h`, ...\n\nHigh-performance atomic data-structures. Many of these are built with very specific\ntradeoffs and constraints in mind that make them faster than their more general\ncounterparts. Each header should contain information about what these tradeoffs are.\n\n#### `Baton.h`\n\nA Baton allows a thread to block once and be awoken: it captures a single handoff. It is\nessentially a (very small, very fast) semaphore that supports only a single call to `sem_call`\nand `sem_wait`.\n\n#### [`Benchmark.h`](Benchmark.md)\n\nA small framework for benchmarking code. Client code registers\nbenchmarks, optionally with an argument that dictates the scale of the\nbenchmark (iterations, working set size etc). The framework runs\nbenchmarks (subject to a command-line flag) and produces formatted\noutput with timing information.\n\n#### `Bits.h`\n\nVarious bit manipulation utilities optimized for speed; includes functions\nthat wrap the\n[ffsl(l)](http://linux.die.net/man/3/ffsll) primitives in a uniform\ninterface.\n\n#### `ConcurrentSkipList.h`\n\nAn implementation of the structure described in [A Provably Correct\nScalable Concurrent Skip\nList](http://people.csail.mit.edu/shanir/publications/OPODIS2006-BA.pdf)\nby Herlihy et al.\n\n#### [`Conv.h`](Conv.md)\n\nA variety of data conversion routines (notably to and from string),\noptimized for speed and safety.\n\n#### `Demangle.h`\n\nPretty-printing C++ types.\n\n#### `DiscriminatedPtr.h`\n\nSimilar to `std::variant`, but restricted to pointers only. Uses the\nhighest-order unused 16 bits in a pointer as discriminator. So\n`sizeof(DiscriminatedPtr<int, string, Widget>) == sizeof(void*)`.\n\n#### [`dynamic.h`](Dynamic.md)\n\nDynamically-typed object, created with JSON objects in mind. `DynamicConverter.h` is\na utility for efficiently converting from a `dynamic` to a more concrete structure when\nthe scheme is known (e.g. json -> `map<int,int>`).\n\n#### `EvictingCacheMap.h`\n\nA simple LRU hash map.\n\n#### [`FBString.h`](FBString.md)\n\nA drop-in implementation of `std::string` with a variety of optimizations.\n\n#### [`FBVector.h`](FBVector.md)\n\nA mostly drop-in implementation of `std::vector` with a variety of\noptimizations.\n\n#### `File.h`\n\nA C++ abstraction around files.\n\n#### `Fingerprint.h`\n\nRabin fingerprinting.\n\n#### [`Function.h`](Function.md)\n\nA polymorphic wrapper for callables similar to `std::function` but not copyable and therefore able to wrap non-copyable callables, such as lambdas that capture move-only types like `std::unique_ptr` or `folly::Promise`.\n\n#### [`futures/`](Futures.md)\n\nFutures is a framework for expressing asynchronous code in C++ using the Promise/Future pattern.\n\n#### [`Format.h`](Format.md)\n\nPython-style formatting utilities.\n\n#### `gen/`\n\nThis library makes it possible to write declarative comprehensions for\nprocessing sequences of values efficiently in C++ akin to C#'s LINQ.\n\n#### [`GroupVarint.h`](GroupVarint.md)\n\n[Group Varint\nencoding](http://www.ir.uwaterloo.ca/book/addenda-06-index-compression.html)\nfor 32-bit values.\n\n#### `IPAddress.h`\n\nA collection of utilities to deal with IPAddresses, including ipv4 and ipv6.\n\n#### `io/`\n\nA collection of useful of abstractions for high-performance io. This is heavily relied upon\nin Facebook's internally networking code.\n\n#### `Hash.h`\n\nVarious popular hash function implementations.\n\n#### [`Histogram.h`](Histogram.md)\n\nA simple class for collecting histogram data.\n\n#### `IntrusiveList.h`\n\nConvenience type definitions for using `boost::intrusive_list`.\n\n#### `json.h`\n\nJSON serializer and deserializer. Uses `dynamic.h`.\n\n#### `Likely.h`\n\nWrappers around [`__builtin_expect`](http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html).\n\n#### `Malloc.h`, `Memory.h`\n\nMemory allocation helpers, particularly when using jemalloc.\n\n#### `MicroSpinLock.h`\n\nA really, *really* small spinlock for fine-grained locking of lots of teeny-tiny data.\n\n#### `MPMCQueue.h`\n\nMPMCQueue<typename> is a high-performance bounded concurrent queue that\nsupports multiple producers, multiple consumers, and optional blocking.\nThe queue has a fixed capacity, for which all memory will be allocated\n up front.\n\nThe additional utility `MPMCPipeline.h` is an extension that lets you\nchain several queues together with processing steps in between.\n\n#### [`PackedSyncPtr.h`](PackedSyncPtr.md)\n\nA highly specialized data structure consisting of a pointer, a 1-bit\nspin lock, and a 15-bit integer, all inside one 64-bit word.\n\n#### [`Poly.h`](Poly.md)\n\nA class template that makes it relatively easy to define a type-erasing\npolymorphic object wrapper.\n\n#### `Preprocessor.h`\n\nNecessarily evil stuff.\n\n#### [`ProducerConsumerQueue.h`](ProducerConsumerQueue.md)\n\nLock free single-reader, single-writer queue.\n\n#### `Random.h`\n\nDefines only one function---`randomNumberSeed()`.\n\n#### `Range.h`\n\nBoost-style range facility and the `StringPiece` specialization.\n\n#### `RWSpinLock.h`\n\nFast and compact reader-writer spin lock.\n\n#### `ScopeGuard.h`\n\nC++11 incarnation of the old [ScopeGuard](http://drdobbs.com/184403758) idiom.\n\n#### `Singleton.h`\n\nA singleton to rule the singletons. This is an attempt to insert a layer between\nC++ statics and the fiasco that ensues, so that things can be created, and destroyed,\ncorrectly upon program creation, program end and sometimes `dlopen` and `fork`.\n\nSingletons are bad for you, but this may help.\n\n#### [`SmallLocks.h`](SmallLocks.md)\n\nVery small spin locks (1 byte and 1 bit).\n\n#### `small_vector.h`\n\nVector with the small buffer optimization and an optional embedded\n`PicoSpinLock`.\n\n#### `sorted_vector_types.h`\n\nCollections similar to `std::map` but implemented as sorted vectors.\n\n#### `stats/`\n\nA collection of efficient utilities for collecting statistics:\n* time series counters, gauges, histograms, and quantiles;\n* single-pass mean and variance.\n\n#### `StlAllocator.h`\n\nSTL allocator wrapping a simple allocate/deallocate interface.\n\n#### `String.h`\n\nString utilities that connect `folly::fbstring` with `std::string`.\n\n#### `Subprocess.h`\n\nSubprocess library, modeled after Python's subprocess module.\n\n#### [`Synchronized.h`](Synchronized.md)\n\nHigh-level synchronization library.\n\n#### `System.h`\n\nDemangling and errno utilities.\n\n#### [`ThreadCachedInt.h`](ThreadCachedInt.md)\n\nHigh-performance atomic increment using thread caching.\n\n#### [`ThreadLocal.h`](ThreadLocal.md)\n\nImproved thread local storage for non-trivial types.\n\n#### `TimeoutQueue.h`\n\nQueue with per-item timeout.\n\n#### `Traits.h`\n\nType traits that complement those defined in the standard C++11 header\n`<traits>`.\n\n#### `Unicode.h`\n\nDefines the `codePointToUtf8` function.\n\n#### `Uri.h`\n\nA collection of utilities to deal with URIs.\n"
  },
  {
    "path": "folly/docs/PackedSyncPtr.md",
    "content": "`folly/PackedSyncPtr.h`\n----------------------\n\nA highly specialized data structure consisting of a pointer, a 1-bit\nspin lock, and a 15-bit integral packed into `sizeof(void*)`.\n\nTypical application is for microsharding of many elements within containers.\nBecause there is no memory overhead, an arbitrarily large number of locks can be\nused to minimize lock contention with no memory penalty.  Additionally,\nexcellent cache performance is obtained by storing the lock inline with the\npointer (no additional cache miss or false sharing).  Finally, because it uses a\nsimple spinlock mechanism, the cost of acquiring an uncontended lock is minimal.\n\n### Usage\n***\n\nThis is not a \"smart\" pointer: nothing automagical is going on\nhere.  Locking is up to the user.  Resource deallocation is up to\nthe user.  Locks are never acquired or released outside explicit\ncalls to lock() and unlock().\n\nChange the value of the raw pointer with set(), but you must hold\nthe lock when calling this function if multiple threads could be\nusing it.\n\nHere is an example of using a PackedSyncPtr to build a synchronized vector with\nno memory overhead - the spinlock and size are stored in the 16 unused bits of\npointer, the rest of which points to the actual data.  See\n`folly/small_vector.h` for a complete implementation of this concept.\n\n``` Cpp\n    template<typename T>\n    class SyncVec {\n      PackedSyncPtr<T> base;\n\n     public:\n      SyncVec() { base.init(); }\n\n      void push_back(const T& t) {\n        base.set(\n          static_cast<T*>(realloc(base.get(), (base.extra() + 1) * sizeof(T))));\n        base[base.extra()] = t;\n        base.setExtra(base.extra() + 1);\n      }\n\n      size_t size() const {\n        return base.extra();\n      }\n\n      void lock() {\n        base.lock();\n      }\n\n      void unlock() {\n        base.unlock();\n      }\n\n      T* begin() const {\n        return base.get();\n      }\n\n      T* end() const {\n        return base.get() + base.extra();\n      }\n    };\n```\n\n### Implementation\n***\n\nThis is using an x64-specific detail about the effective virtual\naddress space.  Long story short: the upper two bytes of all our\npointers will be zero in reality---and if you have a couple billion\nsuch pointers in core, it makes pretty good sense to try to make\nuse of that memory.  The exact details can be perused here:\n\n[http://en.wikipedia.org/wiki/X86-64#Canonical_form_addresses](http://en.wikipedia.org/wiki/X86-64#Canonical_form_addresses)\n"
  },
  {
    "path": "folly/docs/Poly.md",
    "content": "`folly/Poly.h`\n-------------------------------\n\n`Poly` is a class template that makes it relatively easy to define a\ntype-erasing polymorphic object wrapper.\n\n### Type-erasure\n***\n\n`std::function` is one example of a type-erasing polymorphic object wrapper;\n`folly::exception_wrapper` is another. Type-erasure is often used as an\nalternative to dynamic polymorphism via inheritance-based virtual dispatch.\nThe distinguishing characteristic of type-erasing wrappers are:\n\n* **Duck typing:** Types do not need to inherit from an abstract base\n    class in order to be assignable to a type-erasing wrapper; they merely\n    need to satisfy a particular interface.\n* **Value semantics:** Type-erasing wrappers are objects that can be\n    passed around _by value_. This is in contrast to abstract base classes\n    which must be passed by reference or by pointer or else suffer from\n    _slicing_, which causes them to lose their polymorphic behaviors.\n    Reference semantics make it difficult to reason locally about code.\n* **Automatic memory management:** When dealing with inheritance-based\n    dynamic polymorphism, it is often necessary to allocate and manage\n    objects on the heap. This leads to a proliferation of `shared_ptr`s and\n    `unique_ptr`s in APIs, complicating their point-of-use. APIs that take\n    type-erasing wrappers, on the other hand, can often store small objects\n    in-situ, with no dynamic allocation. The memory management, if any, is\n    handled for you, and leads to cleaner APIs: consumers of your API don't\n    need to pass `shared_ptr<AbstractBase>`; they can simply pass any object\n    that satisfies the interface you require. (`std::function` is a\n    particularly compelling example of this benefit. Far worse would be an\n    inheritance-based callable solution like\n    `shared_ptr<ICallable<void(int)>>`. )\n\n### Examples: Defining a type-erasing function wrapper with `folly::Poly`\n***\n\nDefining a polymorphic wrapper with `Poly` is a matter of defining two\nthings:\n\n* An *interface*, consisting of public member functions, and\n* A *mapping* from a concrete type to a set of member function bindings.\n\nBelow is a simple example program that defines a `drawable` wrapper for any type\nthat provides a `draw` member function. (The details will be explained later.)\n\n``` Cpp\n    // This example is an adaptation of one found in Louis Dionne's dyno library.\n    #include <folly/Poly.h>\n    #include <iostream>\n\n    struct IDrawable {\n      // Define the interface of something that can be drawn:\n      template <class Base> struct Interface : Base {\n        void draw(std::ostream& out) const { folly::poly_call<0>(*this, out);}\n      };\n      // Define how concrete types can fulfill that interface (in C++17):\n      template <class T> using Members = folly::PolyMembers<&T::draw>;\n    };\n\n    // Define an object that can hold anything that can be drawn:\n    using drawable = folly::Poly<IDrawable>;\n\n    struct Square {\n      void draw(std::ostream& out) const { out << \"Square\\n\"; }\n    };\n\n    struct Circle {\n      void draw(std::ostream& out) const { out << \"Circle\\n\"; }\n    };\n\n    void f(drawable const& d) {\n      d.draw(std::cout);\n    }\n\n    int main() {\n      f(Square{}); // prints Square\n      f(Circle{}); // prints Circle\n    }\n```\n\nThe above program prints:\n\n```\n    Square\n    Circle\n```\n\nHere is another (heavily commented) example of a simple implementation of a\n`std::function`-like polymorphic wrapper. Its interface has only a single\nmember function: `operator()`\n\n``` Cpp\n    // An interface for a callable object of a particular signature, Fun\n    // (most interfaces don't need to be templates, FWIW).\n    template <class Fun>\n    struct IFunction;\n\n    template <class R, class... As>\n    struct IFunction<R(As...)> {\n      // An interface is defined as a nested class template called\n      // Interface that takes a single template parameter, Base, from\n      // which it inherits.\n      template <class Base>\n      struct Interface : Base {\n        // The Interface has public member functions. These become the\n        // public interface of the resulting Poly instantiation.\n        // (Implementation note: Poly<IFunction<Sig>> will publicly\n        // inherit from this struct, which is what gives it the right\n        // member functions.)\n        R operator()(As... as) const {\n          // The definition of each member function in your interface will\n          // always consist of a single line dispatching to folly::poly_call<N>.\n          // The \"N\" corresponds to the N-th member function in the\n          // list of member function bindings, Members, defined below.\n          // The first argument will always be *this, and the rest of the\n          // arguments should simply forward (if necessary) the member\n          // function's arguments.\n          return static_cast<R>(\n              folly::poly_call<0>(*this, std::forward<As>(as)...));\n        }\n      };\n      // The \"Members\" alias template is a comma-separated list of bound\n      // member functions for a given concrete type \"T\". The\n      // \"FOLLY_POLY_MEMBERS\" macro accepts a comma-separated list, and the\n      // (optional) \"FOLLY_POLY_MEMBER\" macro lets you disambiguate overloads\n      // by explicitly specifying the function signature the target member\n      // function should have. In this case, we require \"T\" to have a\n      // function call operator with the signature `R(As...) const`.\n      //\n      // If you are using a C++17-compatible compiler, you can do away with\n      // the macros and write this as:\n      //\n      //   template <class T>\n      //   using Members =\n      //       folly::PolyMembers<folly::sig<R(As...) const>(&T::operator())>;\n      //\n      // And since `folly::sig` is only needed for disambiguation in case of\n      // overloads, if you are not concerned about objects with overloaded\n      // function call operators, it could be further simplified to:\n      //\n      //   template <class T>\n      //   using Members = folly::PolyMembers<&T::operator()>;\n      //\n      template <class T>\n      using Members = FOLLY_POLY_MEMBERS(\n          FOLLY_POLY_MEMBER(R(As...) const, &T::operator()));\n    };\n\n    // Now that we have defined the interface, we can pass it to Poly to\n    // create our type-erasing wrapper:\n    template <class Fun>\n    using Function = Poly<IFunction<Fun>>;\n```\n\nGiven the above definition of `Function`, users can now initialize instances\nof (say) `Function<int(int, int)>` with function objects like\n`std::plus<int>` and `std::multiplies<int>`, as below:\n\n``` Cpp\n    Function<int(int, int)> fun = std::plus<int>{};\n    assert(5 == fun(2, 3));\n    fun = std::multiplies<int>{};\n    assert(6 = fun(2, 3));\n```\n\n### Defining an interface with C++17\n***\n\nWith C++17, defining an interface to be used with `Poly` is fairly\nstraightforward. As in the `Function` example above, there is a struct with\na nested `Interface` class template and a nested `Members` alias template.\nNo macros are needed with C++17.\n\nImagine we were defining something like a Java-style iterator. If we are\nusing a C++17 compiler, our interface would look something like this:\n\n``` Cpp\n    template <class Value>\n    struct IJavaIterator {\n      template <class Base>\n      struct Interface : Base {\n        bool Done() const { return folly::poly_call<0>(*this); }\n        Value Current() const { return folly::poly_call<1>(*this); }\n        void Next() { folly::poly_call<2>(*this); }\n      };\n      // NOTE: This works in C++17 only:\n      template <class T>\n      using Members = folly::PolyMembers<&T::Done, &T::Current, &T::Next>;\n    };\n\n    template <class Value>\n    using JavaIterator = Poly<IJavaIterator<Value>>;\n```\n\nGiven the above definition, `JavaIterator<int>` can be used to hold instances\nof any type that has `Done`, `Current`, and `Next` member functions with the\ncorrect (or compatible) signatures.\n\nThe presence of overloaded member functions complicates this picture. Often,\nproperty members are faked in C++ with `const` and non-`const` member\nfunction overloads, like in the interface specified below:\n\n``` Cpp\n    struct IIntProperty {\n      template <class Base>\n      struct Interface : Base {\n        int Value() const { return folly::poly_call<0>(*this); }\n        void Value(int i) { folly::poly_call<1>(*this, i); }\n      };\n      // NOTE: This works in C++17 only:\n      template <class T>\n      using Members = folly::PolyMembers<\n        folly::sig<int() const>(&T::Value),\n        folly::sig<void(int)>(&T::Value)>;\n    };\n\n    using IntProperty = Poly<IIntProperty>;\n```\n\nNow, any object that has `Value` members of compatible signatures can be\nassigned to instances of `IntProperty` object. Note how `folly::sig` is used\nto disambiguate the overloads of `&T::Value`.\n\n### Defining an interface with C++14\n***\n\nIn C++14, the nice syntax above doesn't work, so we have to resort to macros.\nThe two examples above would look like this:\n\n``` Cpp\n    template <class Value>\n    struct IJavaIterator {\n      template <class Base>\n      struct Interface : Base {\n        bool Done() const { return folly::poly_call<0>(*this); }\n        Value Current() const { return folly::poly_call<1>(*this); }\n        void Next() { folly::poly_call<2>(*this); }\n      };\n      // NOTE: This works in C++14 and C++17:\n      template <class T>\n      using Members = FOLLY_POLY_MEMBERS(&T::Done, &T::Current, &T::Next);\n    };\n\n    template <class Value>\n    using JavaIterator = Poly<IJavaIterator<Value>>;\n```\n\nand\n\n``` Cpp\n    struct IIntProperty {\n      template <class Base>\n      struct Interface : Base {\n        int Value() const { return folly::poly_call<0>(*this); }\n        void Value(int i) { return folly::poly_call<1>(*this, i); }\n      };\n      // NOTE: This works in C++14 and C++17:\n      template <class T>\n      using Members = FOLLY_POLY_MEMBERS(\n        FOLLY_POLY_MEMBER(int() const, &T::Value),\n        FOLLY_POLY_MEMBER(void(int), &T::Value));\n    };\n\n    using IntProperty = Poly<IIntProperty>;\n```\n\n### Extending interfaces\n***\n\nOne typical advantage of inheritance-based solutions to runtime polymorphism\nis that one polymorphic interface could extend another through inheritance.\nThe same can be accomplished with type-erasing polymorphic wrappers. In\nthe `Poly` library, you can use `folly::PolyExtends` to say that one interface\nextends another.\n\n``` Cpp\n    struct IFoo {\n      template <class Base>\n      struct Interface : Base {\n        void Foo() const { return folly::poly_call<0>(*this); }\n      };\n      template <class T>\n      using Members = FOLLY_POLY_MEMBERS(&T::Foo);\n    };\n\n    // The IFooBar interface extends the IFoo interface\n    struct IFooBar : PolyExtends<IFoo> {\n      template <class Base>\n      struct Interface : Base {\n        void Bar() const { return folly::poly_call<0>(*this); }\n      };\n      template <class T>\n      using Members = FOLLY_POLY_MEMBERS(&T::Bar);\n    };\n\n    using FooBar = Poly<IFooBar>;\n```\n\nGiven the above definition, instances of type `FooBar` have both `Foo()` and\n`Bar()` member functions.\n\nThe sensible conversions exist between a wrapped derived type and a wrapped\nbase type. For instance, assuming `IDerived` extends `IBase` with `PolyExtends`:\n\n``` Cpp\n    Poly<IDerived> derived = ...;\n    Poly<IBase> base = derived; // This conversion is OK.\n```\n\nAs you would expect, there is no conversion in the other direction, and at\npresent there is no `Poly` equivalent to `dynamic_cast`.\n\n### Type-erasing polymorphic reference wrappers\n***\n\nSometimes you don't need to own a copy of an object; a reference will do. For\nthat you can use `Poly` to capture a _reference_ to an object satisfying an\ninterface rather than the whole object itself. The syntax is intuitive.\n\n``` Cpp\n    int i = 42;\n\n    // Capture a mutable reference to an object of any IRegular type:\n    Poly<IRegular &> intRef = i;\n\n    assert(42 == folly::poly_cast<int>(intRef));\n    // Assert that we captured the address of \"i\":\n    assert(&i == &folly::poly_cast<int>(intRef));\n```\n\nA reference-like `Poly` has a different interface than a value-like `Poly`.\nRather than calling member functions with the `obj.fun()` syntax, you would\nuse the `obj->fun()` syntax. This is for the sake of `const`-correctness.\nFor example, consider the code below:\n\n``` Cpp\n    struct IFoo {\n      template <class Base>\n      struct Interface {\n        void Foo() { folly::poly_call<0>(*this); }\n      };\n      template <class T>\n      using Members = folly::PolyMembers<&T::Foo>;\n    };\n\n    struct SomeFoo {\n      void Foo() { std::printf(\"SomeFoo::Foo\\n\"); }\n    };\n\n    SomeFoo foo;\n    Poly<IFoo &> const anyFoo = foo;\n    anyFoo->Foo(); // prints \"SomeFoo::Foo\"\n```\n\nNotice in the above code that the `Foo` member function is non-`const`.\nNotice also that the `anyFoo` object is `const`. However, since it has\ncaptured a non-`const` reference to the `foo` object, it should still be\npossible to dispatch to the non-`const` `Foo` member function. When\ninstantiated with a reference type, `Poly` has an overloaded `operator->`\nmember that returns a pointer to the `IFoo` interface with the correct\n`const`-ness, which makes this work.\n\nThe same mechanism also prevents users from calling non-`const` member\nfunctions on `Poly` objects that have captured `const` references, which\nwould violate `const`-correctness.\n\nSensible conversions exist between non-reference and reference `Poly`s. For\ninstance:\n\n``` Cpp\n    Poly<IRegular> value = 42;\n    Poly<IRegular &> mutable_ref = value;\n    Poly<IRegular const &> const_ref = mutable_ref;\n\n    assert(&poly_cast<int>(value) == &poly_cast<int>(mutable_ref));\n    assert(&poly_cast<int>(value) == &poly_cast<int>(const_ref));\n```\n\n### Non-member functions (C++17)\n***\n\nIf you wanted to write the interface `ILogicallyNegatable`, which captures\nall types that can be negated with unary `operator!`, you could do it\nas we've shown above, by binding `&T::operator!` in the nested `Members`\nalias template, but that has the problem that it won't work for types that\nhave defined unary `operator!` as a free function. To handle this case,\nthe `Poly` library lets you use a free function instead of a member function\nwhen creating a binding.\n\nWith C++17 you may use a lambda to create a binding, as shown in the example\nbelow:\n\n``` Cpp\n    struct ILogicallyNegatable {\n      template <class Base>\n      struct Interface : Base {\n        bool operator!() const { return folly::poly_call<0>(*this); }\n      };\n      template <class T>\n      using Members = folly::PolyMembers<\n        +[](T const& t) -> decltype(bool(!t)) { return bool(!t); }>;\n    };\n```\n\nThis requires some explanation. The unary `operator+` in front of the lambda\nis necessary! It causes the lambda to decay to a C-style function pointer,\nwhich is one of the types that `folly::PolyMembers` accepts. The `decltype` in\nthe lambda return type is also necessary. Through the magic of SFINAE, it\nwill cause `Poly<ILogicallyNegatable>` to reject any types that don't support\nunary `operator!`.\n\nIf you are using a free function to create a binding, the first parameter is\nimplicitly the `this` parameter. It will receive the type-erased object.\n\n### Non-member functions (C++14)\n***\n\nIf you are using a C++14 compiler, the definition of `ILogicallyNegatable`\nabove will fail because lambdas are not `constexpr`. We can get the same\neffect by writing the lambda as a named free function, as show below:\n\n``` Cpp\n    struct ILogicallyNegatable {\n      template <class Base>\n      struct Interface : Base {\n        bool operator!() const { return folly::poly_call<0>(*this); }\n      };\n      template <class T>\n      static auto negate(T const& t)\n        -> decltype(bool(!t)) { return bool(!t); }\n      template <class T>\n      using Members = FOLLY_POLY_MEMBERS(&negate<T>);\n    };\n```\n\nAs with the example that uses the lambda in the preceding section, the first\nparameter is implicitly the `this` parameter. It will receive the type-erased\nobject.\n\n### Multi-dispatch\n***\n\nWhat if you want to create an `IAddable` interface for things that can be\nadded? Adding requires _two_ objects, both of which are type-erased. This\ninterface requires dispatching on both objects, doing the addition only\nif the types are the same. For this we make use of the `PolySelf` template\nalias to define an interface that takes more than one object of the\nerased type.\n\n``` Cpp\n    struct IAddable {\n      template <class Base>\n      struct Interface : Base {\n        friend PolySelf<Base>\n        operator+(PolySelf<Base> const& a, PolySelf<Base> const& b) const {\n          return folly::poly_call<0>(a, b);\n        }\n      };\n      template <class T>\n      using Members = folly::PolyMembers<\n        +[](T const& a, T const& b) -> decltype(a + b) { return a + b; }>;\n    };\n```\n\nGiven the above definition of `IAddable` we would be able to do the following:\n\n``` Cpp\n    Poly<IAddable> a = 2, b = 3;\n    Poly<IAddable> c = a + b;\n    assert(poly_cast<int>(c) == 5);\n```\n\nIf `a` and `b` stored objects of different types, a `BadPolyCast` exception\nwould be thrown.\n\n### Move-only types\n***\n\nIf you want to store move-only types, then your interface should extend the\n`poly::IMoveOnly` interface.\n\n### Implementation notes\n***\n\n`Poly` will store \"small\" objects in an internal buffer, avoiding the cost of\nof dynamic allocations. At present, this size is not configurable; it is\npegged at the size of two `double`s.\n\n`Poly` objects are always nothrow movable. If you store an object in one that\nhas a potentially throwing move constructor, the object will be stored on the\nheap, even if it could fit in the internal storage of the `Poly` object.\n(So be sure to give your objects nothrow move constructors!)\n\n`Poly` implements type-erasure in a manner very similar to how the compiler\naccomplishes virtual dispatch. Every `Poly` object contains a pointer to a\ntable of function pointers. Member function calls involve a double-\nindirection: once through the v-pointer, and other indirect function call\nthrough the function pointer.\n"
  },
  {
    "path": "folly/docs/ProducerConsumerQueue.md",
    "content": "`folly/ProducerConsumerQueue.h`\n-------------------------------\n\nThe `folly::ProducerConsumerQueue` class is a one-producer\none-consumer queue with very low synchronization overhead.\n\nThe queue must be created with a fixed maximum size (and allocates\nthat many cells of sizeof(T)), and it provides just a few simple\noperations:\n\n * `read`: Attempt to read the value at the front to the queue into a variable,\n           returns `false` iff queue was empty.\n * `write`: Emplace a value at the end of the queue, returns `false` iff the\n            queue was full.\n * `frontPtr`: Retrieve a pointer to the item at the front of the queue, or\n               `nullptr` if it is empty.\n * `popFront`: Remove the item from the front of the queue (queue must not be\n               empty).\n * `isEmpty`: Check if the queue is empty.\n * `isFull`: Check if the queue is full.\n * `sizeGuess`: Returns the number of entries in the queue. Because of the\n                way we coordinate threads, this guess could be slightly wrong\n                when called by the producer/consumer thread, and it could be\n                wildly inaccurate if called from any other threads. Hence,\n                only call from producer/consumer threads!\n\nAll of these operations are wait-free.  The read operations (including\n`frontPtr` and `popFront`) and write operations must only be called by the\nreader and writer thread, respectively. `isFull`, `isEmpty`, and `sizeGuess`\nmay be called by either thread, but the return values from `read`, `write`, or\n`frontPtr` are sufficient for most cases.\n\n`write` may fail if the queue is full, and `read` may fail if the queue is\nempty, so in many situations it is important to choose the queue size such that\nthe queue filling  or staying empty for long is unlikely.\n\n### Example\n***\n\nA toy example that doesn't really do anything useful:\n\n``` Cpp\n    folly::ProducerConsumerQueue<folly::fbstring> queue{size};\n\n    std::thread reader([&queue] {\n      for (;;) {\n        folly::fbstring str;\n        while (!queue.read(str)) {\n          //spin until we get a value\n          continue;\n        }\n\n        sink(str);\n      }\n    });\n\n    // producer thread:\n    for (;;) {\n      folly::fbstring str = source();\n      while (!queue.write(str)) {\n        //spin until the queue has room\n        continue;\n      }\n    }\n```\n\nAlternatively, the consumer may be written as follows to use the 'front' value\nin place, thus avoiding moves or copies:\n\n``` Cpp\n    std::thread reader([&queue] {\n      for (;;) {\n        folly::fbstring* pval;\n        do {\n          pval = queue.frontPtr();\n        } while (!pval); // spin until we get a value;\n\n        sink(*pval);\n        queue.popFront();\n      }\n    });\n```\n"
  },
  {
    "path": "folly/docs/Rcu.md",
    "content": "`folly/synchronization/Rcu.h`\n----------------------\n\nC++ read-copy-update (RCU) functionality in folly.\n\n## Overview\n***\n\nRead-copy-update (RCU) is a low-overhead synchronization mechanism that provides\nguaranteed ordering between operations on shared data. In the simplest usage\npattern, readers enter a critical section, view some state, and leave the\ncritical section, while writers modify shared state and then defer some cleanup\noperations.\n\nProper use of the APIs provided by folly RCU will guarantee that a cleanup\noperation that is deferred during a reader critical section will not be executed\nuntil after that critical section is over.\n\n## Usage\n***\n\n### folly::rcu_domain\n\nThe main synchronization primitive in folly RCU is a `folly::rcu_domain`.  A\n`folly::rcu_domain` is a \"universe\" of deferred execution. Each domain has an\nexecutor on which deferred functions may execute. `folly::rcu_domain` provides\nthe requirements to be\n[BasicLockable](https://en.cppreference.com/w/cpp/named_req/BasicLockable), and\nreaders may enter a read region in a `folly::rcu_domain` by treating the domain\nas a mutex type that can be wrapped by C++ locking primitives.\n\nFor example, to enter and exit a read region using non-movable RAII semantics,\nyou could use an `std::scoped_lock`:\n\n```Cpp\n{\n  std::scoped_lock<folly::rcu_domain> lock(folly::rcu_default_domain());\n  protectedData.read();\n}\n```\n\nAlternatively, if you need to be able to move the lock, you could use\n`std::unique_lock`:\n\n```Cpp\nclass ProtectedData {\n  private:\n    std::unique_lock<folly::rcu_domain> lock_;\n    void* data_;\n}\n```\n\n### Default vs. custom domains\n\nThere is a global, default domain that can be accessed using\n`folly::rcu_default_domain()` as in the example above. If required, you can also\ncreate your own domain:\n\n```Cpp\n{\n  Executor* my_executor = getExecutor();\n  folly::rcu_domain domain(my_executor /* or nullptr to use default */);\n}\n```\n\nIn general, using the default domain is strongly encouraged as you will likely\nget better cache locality by sharing a domain that it used by other callers in\nprocess. If, however, you can't avoid blocking during reader critical sections,\nyour own custom domain should be used to avoid delaying reclamation from other\nupdaters. A custom domain can also be used if you want update callbacks to be\ninvoked on a specific executor.\n\n### Updates and retires\n\nA typical reader / updater synchronization will look something like this:\n\n```Cpp\nvoid doSomethingWith(IPAddress host);\n\nstatic std::atomic<ConfigData*> globalConfigData;\n\nvoid reader() {\n  while (true) {\n    IPAddress curManagementServer;\n    {\n      // We're about to do some reads we want to protect; if we read a\n      // pointer, we need to make sure that if the writer comes along and\n      // updates it, the writer's cleanup operation won't happen until we're\n      // done accessing the pointed-to data. We get a Guard on that\n      // domain; as long as it exists, no function subsequently passed to\n      // invokeEventually will execute.\n      std::scoped_lock<folly::rcu_domain> guard(folly::rcu_default_domain());\n      ConfigData* configData = globalConfigData.load(std::memory_order_consume);\n      // We created a guard before we read globalConfigData; we know that the\n      // pointer will remain valid until the guard is destroyed.\n      curManagementServer = configData->managementServerIP;\n      // RCU domain via the scoped mutex is released here; retired objects\n      // may be freed.\n    }\n    doSomethingWith(curManagementServer);\n  }\n}\n\nvoid writer() {\n  while (true) {\n    std::this_thread::sleep_for(std::chrono::seconds(60));\n    ConfigData* oldConfigData = globalConfigData.load(std::memory_order_relaxed);\n    ConfigData* newConfigData = loadConfigDataFromRemoteServer();\n    globalConfigData.store(newConfigData, std::memory_order_release);\n    folly::rcu_retire(oldConfigData);\n    // Alternatively, in a blocking manner:\n    //   folly::rcu_synchronize();\n    //   delete oldConfigData;\n  }\n}\n```\nIn the example above, a single writer updates `ConfigData*` in a loop, and then\ndefers freeing it until all RCU readers have exited their read regions. The\nwriter may use either of the following two APIs to safely defer freeing the\nold `ConfigData*`:\n\n* `rcu_retire()`: To schedule the asynchronous deletion of the `oldConfigData` pointer when all readers have exited their read regions.\n* `rcu_synchronize()`: To block the calling thread until all readers have exited their read regions, at which point the pointer is safe to be deleted.\n\nIf you expect there to be very long read regions, it may be required to use\n`rcu_synchronize()` or a periodic `rcu_barrier()` (described below) to avoid\nrunning out of memory due to delayed reclamation.\n\n\n### folly::rcu_barrier()\n\nAnother synchronization primitive provided by the folly RCU library is\n`rcu_barrier()`. Unlike `rcu_synchronize()`, which blocks until all outstanding\nreaders have exited their read regions, `rcu_barrier()` blocks until all\noutstanding *deleters* (specified in a call to `rcu_retire()`) are completed.\n\nAs mentioned above, one example of where this may be useful is avoiding\nout-of-memory errors due to scheduling too many objects whose reclamation is\ndelayed. Taking our example from above, we could avoid OOMs using a periodic\ninvocation to `rcu_barrier()` as follows:\n\n```Cpp\nstatic std::atomic<ConfigData*> globalConfigData;\n\nvoid writer() {\n  uint32_t retires = 0;\n  while (true) {\n    std::this_thread::sleep_for(std::chrono::seconds(60));\n    ConfigData* oldConfigData = globalConfigData.load(std::memory_order_relaxed);\n    ConfigData* newConfigData = loadConfigDataFromRemoteServer();\n    globalConfigData.store(newConfigData, std::memory_order_release);\n    if (retires++ % 1000 == 0) {\n      folly::rcu_barrier();\n    }\n    folly::rcu_retire(oldConfigData);\n  }\n}\n```\n\n### Custom deleter in folly::rcu_retire\n\nWhen invoking `folly::rcu_retire()`, you may optionally also pass a custom\ndeleter function that is invoked instead of\n[`std::default_delete`](https://en.cppreference.com/w/cpp/memory/default_delete):\n\n```Cpp\n#include <folly/logging/xlog.h>\n\nstatic std::atomic<ConfigData*> globalConfigData;\n\nvoid writer() {\n  while (true) {\n    std::this_thread::sleep_for(std::chrono::seconds(60));\n    ConfigData* oldConfigData = globalConfigData.load(std::memory_order_relaxed);\n    ConfigData* newConfigData = loadConfigDataFromRemoteServer();\n    globalConfigData.store(newConfigData, std::memory_order_release);\n    folly::rcu_retire(oldConfigData, [](ConfigData* obj) {\n      XLOG(INFO) << \"Deleting retired config \" << oldConfigData->version);\n      delete obj;\n    });\n  }\n}\n```\n\n### API limitations of updaters\n\nExceptions may not be thrown at any point in a retire callback. This includes\nboth the deleter, as well as the object's destructor. Other than this, any\noperation is safe from within a retired object's destructor, including retiring\nother objects, or even retiring the same object as long as the custom deleter\ndid not free it.\n\nWhen using the default domain or the default executor, it is not legal to hold a\nlock across an `rcu_retire()` that is acquired by the deleter.  This is normally\nnot a problem when using the default deleter `delete`, which does not acquire\nany user locks.  However, even when using the default deleter, an object having\na user-defined destructor that acquires locks held across the corresponding call\nto `rcu_retire()` can still deadlock.\n\nNote as well that there is no guarantee of the order in which retire callbacks\nare invoked. A retire callback is guaranteed to be invoked only after all\nreaders that were present when the callback was scheduled have exited.\nOtherwise, any ordering of callback invocation may occur.\n\n### Note on fork()\n\n`fork()` may not be invoked in a multithreaded program where any thread other\nthan the calling thread is in an RCU read region. Doing so will result in\nundefined behavior, and will likely lead to deadlock. If the forking thread is\ninside of an RCU read region, it must invoke `exec()` before exiting the read\nregion.\n\n## Performance\n***\n\n`std::scoped_lock<folly::rcu_domain>` creation/destruction is on the order of\n~5ns.  By comparison, `folly::SharedMutex::lock_shared()` followed by\n`folly::SharedMutex::unlock_shared()` is ~26ns.\n"
  },
  {
    "path": "folly/docs/SmallLocks.md",
    "content": "`folly/SmallLocks.h`\n--------------------\n\nThis module is currently x64 only.\n\nThis header defines two very small mutex types.  These are useful in\nhighly memory-constrained environments where contention is unlikely.\nThe purpose of these is to allow fine-grained locking in massive data\nstructures where memory is at a premium.  Often, each record may have\na spare bit or byte lying around, so sometimes these can be tacked on\nwith no additional memory cost.\n\nThere are two types exported from this header.  `MicroSpinLock` is a\nsingle byte lock, and `PicoSpinLock` can be wrapped around an\ninteger to use a single bit as a lock.  Why do we have both?\nBecause you can't use x64 `bts` on a single byte, so\n`sizeof(MicroSpinLock)` is smaller than `sizeof(PicoSpinLock)` can\nbe, giving it some use cases.\n\nBoth the locks in this header model the C++11 Lockable concept.  So\nyou can use `std::lock_guard` or `std::unique_lock` to lock them in an\nRAII way if you want.\n\nAdditional information is in the header.\n"
  },
  {
    "path": "folly/docs/Switch.md",
    "content": "`folly/lang/Switch.h`\n---------------------\n\n# `Switch.h`\n\nThere are 3 diagnostics for `switch` statements in C languages:\n`switch-enum`, `switch-default`, and `covered-switch-default`. As with all\nwarnings, they may be enabled via the command line by passing `-Wswitch-enum`,\n`-Wswitch-default`, `-Wcovered-switch`. Most codebases have these diagnostics\ndisabled, but we want to enable them in the appropriate combinations for our\ncodebases.\n\nThis `README` goes into detail, but as a brief overview, we want to enable\n*exhaustive* `switch` statements by default, and afford having *non-exhaustive*\n`switch` statements.  And we would like to do this such that the switch\nstatements are always compiled with the appropriate diagnostics (providing true\nexhaustiveness, or flexibility if needed), regardless of the user's compiler\nflags.\n\n**Exhaustive**:\n  - `switch-enum`: error\n  - `switch-default`: error\n  - `covered-switch-default`: ignored\n\nUser code can explicitely opt-in to exhaustive switch behavior with\n`FOLLY_EXHAUSTIVE_SWITCH()`.\n\n**Non-exhaustive**:\n  - `switch-enum`: ignored\n  - `switch-default`: error\n  - `covered-switch-default`: error\n\nUser code can explicitely opt-in to non-exhaustive switch behavior with\n`FOLLY_NON_EXHAUSTIVE_SWITCH()`.\n\nExamples:\n```\nenum E {\n  ONE,\n  TWO,\n  THREE\n};\n\nvoid exhaustive(E e) {\n  // Failure to exhaust all enumerators and a default case will result in a\n  // compiler error.\n  //\n  // This is true regardless of the user's compiler settings.\n  FOLLY_EXHAUSTIVE_SWITCH(switch(e) {\n    case E::ONE:\n      // handle case\n    case E::TWO:\n      // handle case\n    case E::THREE:\n      // handle case\n    default:\n      // handle default case\n  })\n}\n\nvoid non_exhaustive(E e) {\n  // The compiler will not force you to exhaust all cases regardless of the\n  // user's compiler settings. Accidentally exhausting all cases will result in\n  // an error, as it's now an exhaustive switch.\n  FOLLY_NON_EXHAUSTIVE_SWITCH(switch(e) {\n    case E::ONE:\n      // handle case\n    default:\n      // handle default case\n  })\n}\n```\n\n## Writing `switch` statements in a C++ codebase\n\nC++ permits casting any arbitrary integer value to any `enum class` instance as\nif we were casting to an integer of the underlying type of the `enum class`, so\nthey are opaque integers. `enum`s can be converted to-and-from any integer\nimplicitly, so they are just transluscent integers. Notably, this is regardless\nof whether the value is an enumerator. So a truly exhaustive switch-case\nstatement must cover all possible values of an enum variable, including the case\nwhen the value is not equivalent to any of the enum's enumerators (see\n[`std::byte`](https://en.cppreference.com/w/cpp/types/byte.html) for an example\nof an enum without any enumerators!). Unenumerated values also enable patterns\nlike the following, where the enumerators are themselves exceptional:\n\n```\nenum class logposition_t : std::int64_t {\n  sealed = -1,\n  nonexistent = -2,\n};\n\nvoid handle(logposition_t logpos) {\n  switch (logpos) {\n    case losposition_t::sealed:\n      // handle sealed case\n    case logposition_t::nonexistent:\n      // handle nonexistent case\n    default:\n      // handle non-exceptional case\n  }\n}\n```\n\nThough there are innumerable ways to write a `switch` statement, there are two\nrecommended ways to write a `switch` statement: exhaustive switches and\nnon-exhaustive switches (all others are discouraged).  Exhaustive switches\nshould be the default go-to pattern for switch statements.\n\nThe basis of what cases can be listed in a `switch` statement is the type of\nthe value being switched on. If the value being switched on is an enum, then\nthe cases can be its enumerators, and since enumerators are finite, that\n`switch` can have a case for every singled defined enumerator. i.e. a\n`switch` statement can *exhaust* all possible values of the switched enum\nvariable. If the value being switched on is not an enum (i.e. it's an integer\ntype), then the cases can be any value of the integer type, and though integer\ntypes are not infinite, they are expansive enough that it is impractical\n`switch` statement cannot reasonably have a case for every possible value of\nthe switched integer variable; i.e.  a `switch` statement must be\n*non-exhaustive* and only have cases for specific values of the expansive\npossibilities.\n\nWhen compiling, if `switch` argument is not an enum, it will *always* be a\n*non-exhaustive* statement (since there's no enum to exhaust). The `switch-enum`\ndiagnostic and the `covered-switch-default` diagnostic will be ignored. The\n`switch-default` diagnostic will still be enforced though (which is good).\n\nFor a `switch` statement that handles all enumerators of en enum. Not handling\nthe default value often leads to unexpected (or even undefined) behavior. This\nhas caused many, *many* SEVs for us. For example,\n\n```\nenum E {\n  ONE,\n  TWO,\n};\n\nstd::unordered_map<std::int64_t, std::string> data_;\n\nvoid cacheData(std::int64_t key, E e) {\n  if (!isKeyValid(key)) {\n    return;\n  }\n\n  switch (e) {\n    case E::ONE:\n      data_[key] = foo(key, e);\n      break;\n    case E::TWO:\n      data_[key] = bar(key, e);\n      break;\n  }\n\n  LOG(INFO) << \"Stored value \" << data_[key];\n}\n```\n\nSo, reiterating cogently:\n\n  * `switch` statements on non-enum values are always *non-exhaustive* and always need a `default` case\n  * `switch` statements on enum values must always handle non-enumerator values with a `default` case\n  * `switch` statements on enum values can be *exhaustive* or *non-exhaustive*, and we should default to *exhaustive*\n  * `switch` statements without a `default` case risk SEVs from **unexpected behavior** (and is why *Clang 18* has implemented `switch-default`)\n  * We want to have the compiler help us keep our `switch` statements explicit and safe. Regardless of the compiler flags used by the user's build.\n\n## The compiler diagnostics for `switch` statements\n\n- **`switch-enum`**\nRequire all defined enum values to be listed as cases in a `switch` statement\n\n- **`switch-default`**\nRequire a `default` case to be present in a `switch` statement. Added to Clang\n18 (was already in GCC):\nhttps://releases.llvm.org/18.1.6/tools/clang/docs/ReleaseNotes.html\n\n- **`covered-switch-default`**\nWarn when a `default` case is present but all enum values are listed as cases\n(most useful for scoping a *non-exhaustive* `switch` statement and detecting when it\nhas become *exhaustive* to either convert to an *exhaustive* `switch` statement\nor fix whatever expectation has been violated).\n\nThough technically feasible, it is a bad pattern to write a `switch` statement\nthat is non-exhaustive for an enum (i.e. one that does not exhaust the enum).\nThis is because if the enum should expand in the future, the `switch` statement\ncan neglect these new enum values, and there will be nothing to indicate the\nneed for the `switch` statement to be updated.  To combat this, we can use\n`switch-enum` to enforce that enums are exhausted in a `switch` statement.\n\nThough technically feasible, it is a bad pattern to write a `switch` statement\nthat is meant to be non-exhaustive, but actually exhausts all possible cases. This\nleads to confusion in the expectation for what the `default` is handling. It\nwould appear that a non-exhaustive `switch` statement with a `default` case is\ncovering some enumerators, but it is not actually covering any enumerators. To\ncombat this, we can use `covered-switch-default` to highlight that we have an\n*exhaustive* switch despite being meant to be *non-exhaustive*, where we can\nthen convert the `switch` statement to instead be exhaustive.\n\nThough technically feasible, it is a bad pattern to write a `switch` statement\nthat exhausts all possible cases, but does not have a `default` case. The\n`default` is not meant for handling any of the enumerators, but rather for\nhandling any non-enumerator values (since `enum class` types are just opaque\nintegers, and `enum`s are transluscent integers, you can pass a value that is\nnot defined in the enum as an enum value). To combat this, we can use\n`switch-default` to ensure that even exhaustive `switch` statements have a\n`default` case which ensures we truly exhaust the enum and avoid unexpected (or\neven undefined) behavior.\n\nThus... with these three compiler diagnostics, we can defined diagnostics to\nscope a `switch` statement in one of the 2 legitimate ways: either *exhaustive*\nor *non-exhaustive*.\n\n**Exhaustive**:\n  - `switch-enum`: error\n  - `switch-default`: error\n  - `covered-switch-default`: ignored\n\n**Non-exhaustive**:\n  - `switch-enum`: ignored\n  - `switch-default`: error\n  - `covered-switch-default`: error\n\nKeep in mind that when you `switch` on a non-enum value, it is always\n*non-exhaustive*, so only `switch-default` will have an effect.\n\n## Exhaustive switches\n\nWhen you `switch` on the enumerators of an `enum`, you write an exhaustive\nswitch.\n\nFor example:\n\n```\nenum class Color { Red, Green, Blue };\n\nvoid print_color(Color c) {\n  switch (c) {\n    case Color::Red:\n      std::cout << \"Red\";\n      break;\n    case Color::Green:\n      std::cout << \"Green\";\n      break;\n    case Color::Blue:\n      std::cout << \"Blue\";\n      break;\n    default:\n      std::cout << \"Unknown\";\n      break;\n  }\n}\n```\n\nWith a finite set of enum values, you exhaust the enum by listing all of the\nvalues as cases. But since an enum is nothing but a transluscent integer in\nC++ (as explained above), code should handle the (potentially) exceptional case\nof a non-enumerator value being passed to the switch. This is done by adding a\n`default` case to the switch.\n\nThis is the default use case for `switch` statements.\n\nFor the exhaustive `switch` use case, we want to enable `switch-enum` and\n`switch-default`, but disable `covered-switch-default`. For convenience,\nyou can use the `FOLLY_EXHAUSTIVE_SWITCH()` macro.\n\nNOTE: having the `switch` diagnostics configured for exhaustive switches OUGHT\nto be the default for all C++ codebases. However, due to the copious usage\n`switch` statements in our codebase without these improved diagnostics, it will\ntake time to get there. In the meantime, we can use the\n`FOLLY_EXHAUSTIVE_SWITCH()` macro to explicitely opt-in to the exhaustive switch\nbehavior.\n\n## Non-exhaustive switches\n\nWhen you `switch` on the value of a concretely defined enum with a very large\nset of values, and only need to address a sane subset of the values, you write\na non-exhaustive switch.  Additionally, when you `switch` on the a non-enum\nvalue (like an `int`), you write a non-exhaustive switch.\n\nFor example (big enum):\n\n```\nenum class Color {\n  ...\n  every color in a 64 color palette\n  named as an enum value\n  ...\n};\n\nbool is_red_color(Color c) {\n  switch (c) {\n    case Color::Red:\n    case Color::LightRed:\n    case Color::DarkRed:\n      return true;\n    default:\n      return false;\n  }\n}\n```\n\nFor example (non-enum):\n\n```\ntypedef char Color; // https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit\n\nbool is_red_color(Color c) {\n  switch (c) {\n    case 0x09: // \"high intensity\" red\n    case 0xC4: // mid red\n    case 0xC5: // light red\n    case 0xA0: // dark red\n      return true;\n    default:\n      return false;\n  }\n}\n```\n\nWith a large set of enum values (or non-enum integer values), you do not want\nto exhaust all the values as cases. Instead, you want to list the values you\ncare about as cases, and then use a `default` case to handle the remaining\ncases with a sane default behavior.\n\nThis is the less common use case for `switch` statements.\n\nTo gain compiler support for non-exhaustive switches, we can leverage the three\n`switch` behavior compiler diagnostics:\n\nFor the non-exhaustive `switch` use case, we want to `covered-switch-default`\nand `switch-default`, and disable `switch-enum`.  This way, if the code does\nexhaust all the enum values, the compiler will warn us so that we can flip over\nto the exhaustive switch behavior.\n\nFor convenience, you can use the `FOLLY_NON_EXHAUSTIVE_SWITCH()` macro.\n"
  },
  {
    "path": "folly/docs/Synchronized.md",
    "content": "`folly/Synchronized.h`\n----------------------\n\n`folly/Synchronized.h` introduces a simple abstraction for mutex-\nbased concurrency. It replaces convoluted, unwieldy, and just\nplain wrong code with simple constructs that are easy to get\nright and difficult to get wrong.\n\n### Motivation\n\nMany of our multithreaded C++ programs use shared data structures\nassociated with locks. This follows the time-honored adage of\nmutex-based concurrency control \"associate mutexes with data, not code\".\nConsider the following example:\n\n``` Cpp\n\n    class RequestHandler {\n      ...\n      RequestQueue requestQueue_;\n      SharedMutex requestQueueMutex_;\n\n      std::map<std::string, Endpoint> requestEndpoints_;\n      SharedMutex requestEndpointsMutex_;\n\n      HandlerState workState_;\n      SharedMutex workStateMutex_;\n      ...\n    };\n```\n\nWhenever the code needs to read or write some of the protected\ndata, it acquires the mutex for reading or for reading and\nwriting. For example:\n\n``` Cpp\n    void RequestHandler::processRequest(const Request& request) {\n      stop_watch<> watch;\n      checkRequestValidity(request);\n      std::unique_lock lock(requestQueueMutex_);\n      requestQueue_.push_back(request);\n      stats_->addStatValue(\"requestEnqueueLatency\", watch.elapsed());\n      LOG(INFO) << \"enqueued request ID \" << request.getID();\n    }\n```\n\nHowever, the correctness of the technique is entirely predicated on\nconvention.  Developers manipulating these data members must take care\nto explicitly acquire the correct lock for the data they wish to access.\nThere is no ostensible error for code that:\n\n* manipulates a piece of data without acquiring its lock first\n* acquires a different lock instead of the intended one\n* acquires a lock in read mode but modifies the guarded data structure\n* acquires a lock in read-write mode although it only has `const` access\n  to the guarded data\n\n### Introduction to `folly/Synchronized.h`\n\nThe same code sample could be rewritten with `Synchronized`\nas follows:\n\n``` Cpp\n    class RequestHandler {\n      ...\n      Synchronized<RequestQueue> requestQueue_;\n      Synchronized<std::map<std::string, Endpoint>> requestEndpoints_;\n      Synchronized<HandlerState> workState_;\n      ...\n    };\n\n    void RequestHandler::processRequest(const Request& request) {\n      stop_watch<> watch;\n      checkRequestValidity(request);\n      requestQueue_.wlock()->push_back(request);\n      stats_->addStatValue(\"requestEnqueueLatency\", watch.elapsed());\n      LOG(INFO) << \"enqueued request ID \" << request.getID();\n    }\n```\n\nThe rewrite does at maximum efficiency what needs to be done:\nacquires the lock associated with the `RequestQueue` object, writes to\nthe queue, and releases the lock immediately thereafter.\n\nOn the face of it, that's not much to write home about, and not\nan obvious improvement over the previous state of affairs. But\nthe features at work invisible in the code above are as important\nas those that are visible:\n\n* Unlike before, the data and the mutex protecting it are\n  inextricably encapsulated together.\n* If you tried to use `requestQueue_` without acquiring the lock you\n  wouldn't be able to; it is virtually impossible to access the queue\n  without acquiring the correct lock.\n* The lock is released immediately after the insert operation is\n  performed, and is not held for operations that do not need it.\n\nIf you need to perform several operations while holding the lock,\n`Synchronized` provides several options for doing this.\n\nThe `wlock()` method (or `lock()` if you have a non-shared mutex type)\nreturns a `LockedPtr` object that can be stored in a variable.  The lock\nwill be held for as long as this object exists, similar to a\n`std::unique_lock`.  This object can be used as if it were a pointer to\nthe underlying locked object:\n\n``` Cpp\n    {\n      auto lockedQueue = requestQueue_.wlock();\n      lockedQueue->push_back(request1);\n      lockedQueue->push_back(request2);\n    }\n```\n\nThe `rlock()` function is similar to `wlock()`, but acquires a shared lock\nrather than an exclusive lock.\n\nWe recommend explicitly opening a new nested scope whenever you store a\n`LockedPtr` object, to help visibly delineate the critical section, and\nto ensure that the `LockedPtr` is destroyed as soon as it is no longer\nneeded.\n\nAlternatively, `Synchronized` also provides mechanisms to run a function while\nholding the lock.  This makes it possible to use lambdas to define brief\ncritical sections:\n\n``` Cpp\n    void RequestHandler::processRequest(const Request& request) {\n      stop_watch<> watch;\n      checkRequestValidity(request);\n      requestQueue_.withWLock([&](auto& queue) {\n        // withWLock() automatically holds the lock for the\n        // duration of this lambda function\n        queue.push_back(request);\n      });\n      stats_->addStatValue(\"requestEnqueueLatency\", watch.elapsed());\n      LOG(INFO) << \"enqueued request ID \" << request.getID();\n    }\n```\n\nOne advantage of the `withWLock()` approach is that it forces a new\nscope to be used for the critical section, making the critical section\nmore obvious in the code, and helping to encourage code that releases\nthe lock as soon as possible.\n\n### Template class `Synchronized<T>`\n\n#### Template Parameters\n\n`Synchronized` is a template with two parameters, the data type and a\nmutex type: `Synchronized<T, Mutex>`.\n\nIf not specified, the mutex type defaults to `folly::SharedMutex`. However, any\nmutex type with an interface compatible with the standard mutex types is\nsupported. Additionally, any mutex type compatible with the extended protocol\nimplemented in `folly/synchronization/Lock.h` is supported.\n\n`Synchronized` provides slightly different APIs when instantiated with a\nshared mutex type or an upgrade mutex type then with a plain exclusive mutex.\nIf instantiated with either of the two mutex types above through having a\nmember called lock_shared(), the `Synchronized` object has corresponding\n`wlock`, `rlock` or `ulock` methods to acquire different lock types.  When\nusing a shared or upgrade mutex type, these APIs ensure that callers make an\nexplicit choice to acquire a shared, exclusive or upgrade lock and that\ncallers do not unintentionally lock the mutex in the incorrect mode.  The\n`rlock()` APIs only provide `const` access to the underlying data type,\nensuring that it cannot be modified when only holding a shared lock.\n\n#### Constructors\n\nThe default constructor default-initializes the data and its\nassociated mutex.\n\n\nThe copy constructor locks the source for reading and copies its\ndata into the target. (The target is not locked as an object\nunder construction is only accessed by one thread.)\n\nFinally, `Synchronized<T>` defines an explicit constructor that\ntakes an object of type `T` and copies it. For example:\n\n``` Cpp\n    // Default constructed\n    Synchronized<map<string, int>> syncMap1;\n\n    // Copy constructed\n    Synchronized<map<string, int>> syncMap2(syncMap1);\n\n    // Initializing from an existing map\n    map<string, int> init;\n    init[\"world\"] = 42;\n    Synchronized<map<string, int>> syncMap3(init);\n    EXPECT_EQ(syncMap3->size(), 1);\n```\n\n#### Assignment, swap, and copying\n\nThe copy assignment operator copies the underlying source data\ninto a temporary with the source mutex locked, and then move the\ntemporary into the destination data with the destination mutex\nlocked. This technique avoids the need to lock both mutexes at\nthe same time. Mutexes are not copied or moved.\n\nThe move assignment operator assumes the source object is a true\nrvalue and does lock the source mutex. It moves the source\ndata into the destination data with the destination mutex locked.\n\n`swap` acquires locks on both mutexes in increasing order of\nobject address, and then swaps the underlying data. This avoids\npotential deadlock, which may otherwise happen should one thread\ndo `a = b` while another thread does `b = a`.\n\nThe data copy assignment operator copies the parameter into the\ndestination data while the destination mutex is locked.\n\nThe data move assignment operator moves the parameter into the\ndestination data while the destination mutex is locked.\n\nTo get a copy of the guarded data, there are two methods\navailable: `void copy(T*)` and `T copy()`. The first copies data\nto a provided target and the second returns a copy by value. Both\noperations are done under a read lock. Example:\n\n``` Cpp\n    Synchronized<vector<string>> syncVec1, syncVec2;\n    vector<string> vec;\n\n    // Assign\n    syncVec1 = syncVec2;\n    // Assign straight from vector\n    syncVec1 = vec;\n\n    // Swap\n    syncVec1.swap(syncVec2);\n    // Swap with vector\n    syncVec1.swap(vec);\n\n    // Copy to given target\n    syncVec1.copy(&vec);\n    // Get a copy by value\n    auto copy = syncVec1.copy();\n```\n\n#### `lock()`\n\nIf the mutex type used with `Synchronized` is a simple exclusive mutex\ntype (as opposed to a shared mutex), `Synchronized<T>` provides a\n`lock()` method that returns a `LockedPtr<T>` to access the data while\nholding the lock.\n\nThe `LockedPtr` object returned by `lock()` holds the lock for as long\nas it exists.  Whenever possible, prefer declaring a separate inner\nscope for storing this variable, to make sure the `LockedPtr` is\ndestroyed as soon as the lock is no longer needed:\n\n``` Cpp\n    void fun(Synchronized<vector<string>, std::mutex>& vec) {\n      {\n        auto locked = vec.lock();\n        locked->push_back(\"hello\");\n        locked->push_back(\"world\");\n      }\n      LOG(INFO) << \"successfully added greeting\";\n    }\n```\n\n#### `wlock()` and `rlock()`\n\nIf the mutex type used with `Synchronized` is a shared mutex type,\n`Synchronized<T>` provides a `wlock()` method that acquires an exclusive\nlock, and an `rlock()` method that acquires a shared lock.\n\nThe `LockedPtr` returned by `rlock()` only provides const access to the\ninternal data, to ensure that it cannot be modified while only holding a\nshared lock.\n\n``` Cpp\n    int computeSum(const Synchronized<vector<int>>& vec) {\n      int sum = 0;\n      auto locked = vec.rlock();\n      for (int n : *locked) {\n        sum += n;\n      }\n      return sum;\n    }\n\n    void doubleValues(Synchronized<vector<int>>& vec) {\n      auto locked = vec.wlock();\n      for (int& n : *locked) {\n        n *= 2;\n      }\n    }\n```\n\nThis example brings us to a cautionary discussion.  The `LockedPtr`\nobject returned by `lock()`, `wlock()`, or `rlock()` only holds the lock\nas long as it exists.  This object makes it difficult to access the data\nwithout holding the lock, but not impossible.  In particular you should\nnever store a raw pointer or reference to the internal data for longer\nthan the lifetime of the `LockedPtr` object.\n\nFor instance, if we had written the following code in the examples\nabove, this would have continued accessing the vector after the lock had\nbeen released:\n\n``` Cpp\n    // No. NO. NO!\n    for (int& n : *vec.wlock()) {\n      n *= 2;\n    }\n```\n\nThe `vec.wlock()` return value is destroyed in this case as soon as the\ninternal range iterators are created.  The range iterators point into\nthe vector's data, but lock is released immediately, before executing\nthe loop body.\n\nNeedless to say, this is a crime punishable by long debugging nights.\n\nRange-based for loops are slightly subtle about the lifetime of objects\nused in the initializer statement.  Most other problematic use cases are\na bit easier to spot than this, since the lifetime of the `LockedPtr` is\nmore explicitly visible.\n\n#### `withLock()`\n\nAs an alternative to the `lock()` API, `Synchronized` also provides a\n`withLock()` method that executes a function or lambda expression while\nholding the lock.  The function receives a reference to the data as its\nonly argument.\n\nThis has a few benefits compared to `lock()`:\n\n* The lambda expression requires its own nested scope, making critical\n  sections more visible in the code.  Callers are recommended to define\n  a new scope when using `lock()` if they choose to, but this is not\n  required.  `withLock()` ensures that a new scope must always be\n  defined.\n* Because a new scope is required, `withLock()` also helps encourage\n  users to release the lock as soon as possible.  Because the critical\n  section scope is easily visible in the code, it is harder to\n  accidentally put extraneous code inside the critical section without\n  realizing it.\n* The separate lambda scope makes it more difficult to store raw\n  pointers or references to the protected data and continue using those\n  pointers outside the critical section.\n\nFor example, `withLock()` makes the range-based for loop mistake from\nabove much harder to accidentally run into:\n\n``` Cpp\n    vec.withLock([](auto& locked) {\n      for (int& n : locked) {\n        n *= 2;\n      }\n    });\n```\n\nThis code does not have the same problem as the counter-example with\n`wlock()` above, since the lock is held for the duration of the loop.\n\nWhen using `Synchronized` with a shared mutex type, it provides separate\n`withWLock()` and `withRLock()` methods instead of `withLock()`.\n\n#### `ulock()` and `withULockPtr()`\n\n`Synchronized` also supports upgrading and downgrading mutex lock levels as\nlong as the mutex type used to instantiate the `Synchronized` type has the\nsame interface as the mutex types in the C++ standard library. See below for an\nintro to upgrade mutexes.\n\nAn upgrade lock can be acquired as usual either with the `ulock()` method or\nthe `withULockPtr()` method as so\n\n``` Cpp\n    {\n      // only const access allowed to the underlying object when an upgrade lock\n      // is acquired\n      auto ulock = vec.ulock();\n      auto newSize = ulock->size();\n    }\n\n    auto newSize = vec.withULockPtr([](auto ulock) {\n      // only const access allowed to the underlying object when an upgrade lock\n      // is acquired\n      return ulock->size();\n    });\n```\n\nAn upgrade lock acquired via `ulock()` or `withULockPtr()` can be upgraded or\ndowngraded by calling any of the following methods on the `LockedPtr` proxy\n\n* `moveFromUpgradeToWrite()`\n* `moveFromWriteToUpgrade()`\n* `moveFromWriteToRead()`\n* `moveFromUpgradeToRead()`\n\nCalling these leaves the `LockedPtr` object on which the method was called in\nan invalid `null` state and returns another LockedPtr proxy holding the\nspecified lock.  The upgrade or downgrade is done atomically - the\n`Synchronized` object is never in an unlocked state during the lock state\ntransition.  For example\n\n``` Cpp\n    auto ulock = obj.ulock();\n    if (ulock->needsUpdate()) {\n      auto wlock = ulock.moveFromUpgradeToWrite();\n\n      // ulock is now null\n\n      wlock->updateObj();\n    }\n```\n\nThis \"move\" can also occur in the context of a `withULockPtr()`\n(`withWLockPtr()` or `withRLockPtr()` work as well!) function as so\n\n``` Cpp\n    auto newSize = obj.withULockPtr([](auto ulock) {\n      if (ulock->needsUpdate()) {\n\n        // release upgrade lock get write lock atomically\n        auto wlock = ulock.moveFromUpgradeToWrite();\n        // ulock is now null\n        wlock->updateObj();\n\n        // release write lock and acquire read lock atomically\n        auto rlock = wlock.moveFromWriteToRead();\n        // wlock is now null\n        return rlock->newSize();\n\n      } else {\n\n        // release upgrade lock and acquire read lock atomically\n        auto rlock = ulock.moveFromUpgradeToRead();\n        // ulock is now null\n        return rlock->newSize();\n      }\n    });\n```\n\n#### Intro to upgrade mutexes:\n\nAn upgrade mutex is a shared mutex with an extra state called `upgrade` and an\natomic state transition from `upgrade` to `unique`. The `upgrade` state is more\npowerful than the `shared` state but less powerful than the `unique` state.\n\nAn upgrade lock permits only const access to shared state for doing reads. It\ndoes not permit mutable access to shared state for doing writes. Only a unique\nlock permits mutable access for doing writes.\n\nAn upgrade lock may be held concurrently with any number of shared locks on the\nsame mutex. An upgrade lock is exclusive with other upgrade locks and unique\nlocks on the same mutex - only one upgrade lock or unique lock may be held at a\ntime.\n\nThe upgrade mutex solves the problem of doing a read of shared state and then\noptionally doing a write to shared state efficiently under contention. Consider\nthis scenario with a shared mutex:\n\n``` Cpp\n    struct MyObect {\n      bool isUpdateRequired() const;\n      void doUpdate();\n    };\n\n    struct MyContainingObject {\n      folly::Synchronized<MyObject> sync;\n\n      void mightHappenConcurrently() {\n        // first check\n        if (!sync.rlock()->isUpdateRequired()) {\n          return;\n        }\n        sync.withWLock([&](auto& state) {\n          // second check\n          if (!state.isUpdateRequired()) {\n            return;\n          }\n          state.doUpdate();\n        });\n      }\n    };\n```\n\nHere, the second `isUpdateRequired` check happens under a unique lock. This\nmeans that the second check cannot be done concurrently with other threads doing\nfirst `isUpdateRequired` checks under the shared lock, even though the second\ncheck, like the first check, is read-only and requires only const access to the\nshared state.\n\nThis may even introduce unnecessary blocking under contention. Since the default\nmutex type, `folly::SharedMutex`, has write priority, the unique lock protecting\nthe second check may introduce unnecessary blocking to all the other threads\nthat are attempting to acquire a shared lock to protect the first check. This\nproblem is called reader starvation.\n\nOne solution is to use a shared mutex type with read priority, such as\n`folly::SharedMutexReadPriority`. That can introduce less blocking under\ncontention to the other threads attempting to acquire a shared lock to do the\nfirst check. However, that may backfire and cause threads which are attempting\nto acquire a unique lock (for the second check) to stall, waiting for a moment\nin time when there are no shared locks held on the mutex, a moment in time that\nmay never even happen. This problem is called writer starvation.\n\nStarvation is a tricky problem to solve in general. But we can partially side-\nstep it in our case.\n\nAn alternative solution is to use an upgrade lock for the second check. Threads\nattempting to acquire an upgrade lock for the second check do not introduce\nunnecessary blocking to all other threads that are attempting to acquire a\nshared lock for the first check. Only after the second check passes, and the\nupgrade lock transitions atomically from an upgrade lock to a unique lock, does\nthe unique lock introduce *necessary* blocking to the other threads attempting\nto acquire a shared lock. With this solution, unlike the solution without the\nupgrade lock, the second check may be done concurrently with all other first\nchecks rather than blocking or being blocked by them.\n\nThe example would then look like:\n\n``` Cpp\n    struct MyObject {\n      bool isUpdateRequired() const;\n      void doUpdate();\n    };\n\n    struct MyContainingObject {\n      folly::Synchronized<MyObject> sync;\n\n      void mightHappenConcurrently() {\n        // first check\n        if (!sync.rlock()->isUpdateRequired()) {\n          return;\n        }\n        sync.withULockPtr([&](auto ulock) {\n          // second check\n          if (!ulock->isUpdateRequired()) {\n            return;\n          }\n          auto wlock = ulock.moveFromUpgradeToWrite();\n          wlock->doUpdate();\n        });\n      }\n    };\n```\n\nNote: Some shared mutex implementations offer an atomic state transition from\n`shared` to `unique` and some upgrade mutex implementations offer an atomic\nstate transition from `shared` to `upgrade`. These atomic state transitions are\ndangerous, however, and can deadlock when done concurrently on the same mutex.\nFor example, if threads A and B both hold shared locks on a mutex and are both\nattempting to transition atomically from shared to upgrade locks, the threads\nare deadlocked. Likewise if they are both attempting to transition atomically\nfrom shared to unique locks, or one is attempting to transition atomically from\nshared to upgrade while the other is attempting to transition atomically from\nshared to unique. Therefore, `Synchronized`'s `LockedPtr` proxies do not expose\neither of these dangerous atomic state transitions even when the underlying\nmutex type supports them.\n\n#### Timed Locking\n\nWhen `Synchronized` is used with a mutex type that supports timed lock\nacquisition, `lock()`, `wlock()`, and `rlock()` can all take an optional\n`std::chrono::duration` argument.  This argument specifies a timeout to\nuse for acquiring the lock.  If the lock is not acquired before the\ntimeout expires, a null `LockedPtr` object will be returned.  Callers\nmust explicitly check the return value before using it:\n\n``` Cpp\n    void fun(Synchronized<vector<string>>& vec) {\n      {\n        auto locked = vec.lock(10ms);\n        if (!locked) {\n          throw std::runtime_error(\"failed to acquire lock\");\n        }\n        locked->push_back(\"hello\");\n        locked->push_back(\"world\");\n      }\n      LOG(INFO) << \"successfully added greeting\";\n    }\n```\n\n#### `unlock()` and `scopedUnlock()`\n\n`Synchronized` is a good mechanism for enforcing scoped\nsynchronization, but it has the inherent limitation that it\nrequires the critical section to be, well, scoped. Sometimes the\ncode structure requires a fleeting \"escape\" from the iron fist of\nsynchronization, while still inside the critical section scope.\n\nOne common pattern is releasing the lock early on error code paths,\nprior to logging an error message.  The `LockedPtr` class provides an\n`unlock()` method that makes this possible:\n\n``` Cpp\n    Synchronized<map<int, string>> dic;\n    ...\n    {\n      auto locked = dic.rlock();\n      auto iter = locked->find(0);\n      if (iter == locked.end()) {\n        locked.unlock();  // don't hold the lock while logging\n        LOG(ERROR) << \"key 0 not found\";\n        return false;\n      }\n      processValue(*iter);\n    }\n    LOG(INFO) << \"succeeded\";\n```\n\nFor more complex nested control flow scenarios, `scopedUnlock()` returns\nan object that will release the lock for as long as it exists, and will\nreacquire the lock when it goes out of scope.\n\n``` Cpp\n\n    Synchronized<map<int, string>> dic;\n    ...\n    {\n      auto locked = dic.wlock();\n      auto iter = locked->find(0);\n      if (iter == locked->end()) {\n        {\n          auto unlocker = locked.scopedUnlock();\n          LOG(INFO) << \"Key 0 not found, inserting it.\"\n        }\n        locked->emplace(0, \"zero\");\n      } else {\n        *iter = \"zero\";\n      }\n    }\n```\n\nClearly `scopedUnlock()` comes with specific caveats and\nliabilities. You must assume that during the `scopedUnlock()`\nsection, other threads might have changed the protected structure\nin arbitrary ways. In the example above, you cannot use the\niterator `iter` and you cannot assume that the key `0` is not in the\nmap; another thread might have inserted it while you were\nbragging on `LOG(INFO)`.\n\nWhenever a `LockedPtr` object has been unlocked, whether with `unlock()`\nor `scopedUnlock()`, it will behave as if it is null.  `isNull()` will\nreturn true.  Dereferencing an unlocked `LockedPtr` is not allowed and\nwill result in undefined behavior.\n\n#### `Synchronized` and `std::condition_variable`\n\nWhen used with a `std::mutex`, `Synchronized` supports using a\n`std::condition_variable` with its internal mutex.  This allows a\n`condition_variable` to be used to wait for a particular change to occur\nin the internal data.\n\nThe `LockedPtr` returned by `Synchronized<T, std::mutex>::lock()` has a\n`as_lock()` method that returns a reference to a\n`std::unique_lock<std::mutex>`, which can be given to the\n`std::condition_variable`:\n\n``` Cpp\n    Synchronized<vector<string>, std::mutex> vec;\n    std::condition_variable emptySignal;\n\n    // Assuming some other thread will put data on vec and signal\n    // emptySignal, we can then wait on it as follows:\n    auto locked = vec.lock();\n    emptySignal.wait(locked.as_lock(),\n                     [&] { return !locked->empty(); });\n```\n\n### `acquireLocked()`\n\nSometimes locking just one object won't be able to cut the mustard. Consider a\nfunction that needs to lock two `Synchronized` objects at the\nsame time - for example, to copy some data from one to the other.\nAt first sight, it looks like sequential `wlock()` calls will work just\nfine:\n\n``` Cpp\n    void fun(Synchronized<vector<int>>& a, Synchronized<vector<int>>& b) {\n      auto lockedA = a.wlock();\n      auto lockedB = b.wlock();\n      ... use lockedA and lockedB ...\n    }\n```\n\nThis code compiles and may even run most of the time, but embeds\na deadly peril: if one threads call `fun(x, y)` and another\nthread calls `fun(y, x)`, then the two threads are liable to\ndeadlocking as each thread will be waiting for a lock the other\nis holding. This issue is a classic that applies regardless of\nthe fact the objects involved have the same type.\n\nThis classic problem has a classic solution: all threads must\nacquire locks in the same order. The actual order is not\nimportant, just the fact that the order is the same in all\nthreads. Many libraries simply acquire mutexes in increasing\norder of their address, which is what we'll do, too. The\n`acquireLocked()` function takes care of all details of proper\nlocking of two objects and offering their innards.  It returns a\n`std::tuple` of `LockedPtr`s:\n\n``` Cpp\n    void fun(Synchronized<vector<int>>& a, Synchronized<vector<int>>& b) {\n      auto ret = folly::acquireLocked(a, b);\n      auto& lockedA = std::get<0>(ret);\n      auto& lockedB = std::get<1>(ret);\n      ... use lockedA and lockedB ...\n    }\n```\n\nNote that C++ 17 introduces\n[structured binding syntax](http://wg21.link/P0144r2)\nwhich will make the returned tuple more convenient to use:\n\n``` Cpp\n    void fun(Synchronized<vector<int>>& a, Synchronized<vector<int>>& b) {\n      auto [lockedA, lockedB] = folly::acquireLocked(a, b);\n      ... use lockedA and lockedB ...\n    }\n```\n\nAn `acquireLockedPair()` function is also available, which returns a\n`std::pair` instead of a `std::tuple`.  This is more convenient to use\nin many situations, until compiler support for structured bindings is\nmore widely available.\n\n### Synchronizing several data items with one mutex\n\nThe library is geared at protecting one object of a given type\nwith a mutex. However, sometimes we'd like to protect two or more\nmembers with the same mutex. Consider for example a bidirectional\nmap, i.e. a map that holds an `int` to `string` mapping and also\nthe converse `string` to `int` mapping. The two maps would need\nto be manipulated simultaneously. There are at least two designs\nthat come to mind.\n\n#### Using a nested `struct`\n\nYou can easily pack the needed data items in a little struct.\nFor example:\n\n``` Cpp\n    class Server {\n      struct BiMap {\n        map<int, string> direct;\n        map<string, int> inverse;\n      };\n      Synchronized<BiMap> bimap_;\n      ...\n    };\n    ...\n    bimap_.withLock([](auto& locked) {\n      locked.direct[0] = \"zero\";\n      locked.inverse[\"zero\"] = 0;\n    });\n```\n\nWith this code in tow you get to use `bimap_` just like any other\n`Synchronized` object, without much effort.\n\n#### Using `std::tuple`\n\nIf you won't stop short of using a spaceship-era approach,\n`std::tuple` is there for you. The example above could be\nrewritten for the same functionality like this:\n\n``` Cpp\n    class Server {\n      Synchronized<tuple<map<int, string>, map<string, int>>> bimap_;\n      ...\n    };\n    ...\n    bimap_.withLock([](auto& locked) {\n      get<0>(locked)[0] = \"zero\";\n      get<1>(locked)[\"zero\"] = 0;\n    });\n```\n\nThe code uses `std::get` with compile-time integers to access the\nfields in the tuple. The relative advantages and disadvantages of\nusing a local struct vs. `std::tuple` are quite obvious - in the\nfirst case you need to invest in the definition, in the second\ncase you need to put up with slightly more verbose and less clear\naccess syntax.\n\n### Summary\n\n`Synchronized` and its supporting tools offer you a simple,\nrobust paradigm for mutual exclusion-based concurrency. Instead\nof manually pairing data with the mutexes that protect it and\nrelying on convention to use them appropriately, you can benefit\nof encapsulation and typechecking to offload a large part of that\ntask and to provide good guarantees.\n"
  },
  {
    "path": "folly/docs/ThreadCachedInt.md",
    "content": "`folly/ThreadCachedInt.h`\n----------------------\n\nHigh-performance atomic increment using thread caching.\n\n`folly/ThreadCachedInt.h` introduces an integer class designed for high\nperformance increments from multiple threads simultaneously without\nloss of precision.  It has two read modes, `readFast` gives a potentially stale\nvalue with one load, and `readFull` gives the exact value, but is much slower,\nas discussed below.\n\n\n### Performance\n***\n\nIncrement performance is up to 10x greater than `std::atomic_fetch_add` in high\ncontention environments.  See `folly/test/ThreadCachedIntTest.h` for more\ncomprehensive benchmarks.\n\n`readFast` is as fast as a single load.\n\n`readFull`, on the other hand, requires acquiring a mutex and iterating through\na list to accumulate the values of all the thread local counters, so is\nsignificantly slower than `readFast`.\n\n\n### Usage\n***\n\nCreate an instance and increment it with `increment` or the operator overloads.\nRead the value with `readFast` for quick, potentially stale data, or `readFull`\nfor a more expensive but precise result. There are additional convenience\nfunctions as well, such as `set`.\n\n``` Cpp\n    ThreadCachedInt<int64_t> val;\n    EXPECT_EQ(0, val.readFast());\n    ++val;                        // increment in thread local counter only\n    EXPECT_EQ(0, val.readFast()); // increment has not been flushed\n    EXPECT_EQ(1, val.readFull()); // accumulates all thread local counters\n    val.set(2);\n    EXPECT_EQ(2, val.readFast());\n    EXPECT_EQ(2, val.readFull());\n```\n\n### Implementation\n***\n\n`folly::ThreadCachedInt` uses `folly::ThreadLocal` to store thread specific\nobjects that each have a local counter.  When incrementing, the thread local\ninstance is incremented.  If the local counter passes the cache size, the value\nis flushed to the global counter with an atomic increment.  It is this global\ncounter that is read with `readFast` via a simple load, but will not count any\nof the updates that haven't been flushed.\n\nIn order to read the exact value, `ThreadCachedInt` uses the extended\n`readAllThreads()` API of `folly::ThreadLocal` to iterate through all the\nreferences to all the associated thread local object instances.  This currently\nrequires acquiring a global mutex and iterating through the references,\naccumulating the counters along with the global counter.  This also means that\nthe first use of the object from a new thread will acquire the mutex in order to\ninsert the thread local reference into the list.  By default, there is one\nglobal mutex per integer type used in `ThreadCachedInt`.  If you plan on using a\nlot of `ThreadCachedInt`s in your application, consider breaking up the\nglobal mutex by introducing additional `Tag` template parameters.\n\n`set` simply sets the global counter value, and marks all the thread local\ninstances as needing to be reset.  When iterating with `readFull`, thread local\ncounters that have been marked as reset are skipped.  When incrementing, thread\nlocal counters marked for reset are set to zero and unmarked for reset.\n\nUpon destruction, thread local counters are flushed to the parent so that counts\nare not lost after increments in temporary threads.  This requires grabbing the\nglobal mutex to make sure the parent itself wasn't destroyed in another thread\nalready.\n\n### Alternate Implementations\n***\n\nThere are of course many ways to skin a cat, and you may notice there is a\npartial alternate implementation in `folly/test/ThreadCachedIntTest.cpp` that\nprovides similar performance.  `ShardedAtomicInt` simply uses an array of\n`std::atomic<int64_t>`'s and hashes threads across them to do low-contention\natomic increments, and `readFull` just sums up all the ints.\n\nThis sounds great, but in order to get the contention low enough to get similar\nperformance as ThreadCachedInt with 24 threads, `ShardedAtomicInt` needs about\n2000 ints to hash across.  This uses about 20x more memory, and the lock-free\n`readFull` has to sum up all 2048 ints, which ends up being about 50x slower\nthan `ThreadCachedInt` in low contention situations, which is hopefully the\ncommon case since it's designed for high-write, low read access patterns.\nPerformance of `readFull` is about the same speed as `ThreadCachedInt` in high\ncontention environments.\n\nDepending on the operating conditions, it may make more sense to use one\nimplementation over the other.  For example, a lower contention environment will\nprobably be able to use a `ShardedAtomicInt` with a much smaller array without\nhurting performance, while improving memory consumption and perf of `readFull`.\n"
  },
  {
    "path": "folly/docs/ThreadLocal.md",
    "content": "`folly/ThreadLocal.h`\n----------------------\n\nImproved thread local storage for non-trivial types.\n\n * ~4x faster than `boost::thread_specific_ptr`.\n * Similar speed as using `pthread_getspecific` directly, but only consumes a\n   single `pthread_key_t` per `Tag` template param.\n * Expands on the `thread_specific_ptr` API with `accessAllThreads` and extended\n   custom deleter support.\n\n\n### Usage\n***\n\nThe API of `ThreadLocalPtr` is very close to `boost::thread_specific_ptr` with\nthe notable addition of the `accessAllThreads` method.  There is also a\n`ThreadLocal` class which is a thin wrapper around `ThreadLocalPtr` that manages\nallocation automatically (creates a new object the first time it is dereferenced\nfrom each thread).\n\n`ThreadLocalPtr` simply gives you a place to put and access a pointer local to\neach thread such that it will be destroyed appropriately.\n\n```Cpp\n{\n  folly::ThreadLocalPtr<Widget> w;\n  w.reset(new Widget(0), Widget::customDeleterA);\n  std::thread([&w]() {\n    w.reset(new Widget(1), Widget::customDeleterB);\n    w.get()->mangleWidget();\n  } // Widget(1) is destroyed with customDeleterB\n} // Widget(0) is destroyed with customDeleterA\n```\n\nNote that `customDeleterB` will get called with\n`TLPDestructionMode::THIS_THREAD` and `customerDeleterA` will get called with\n`TLPDestructionMode::ALL_THREADS`.  This is to distinguish between thread exit\nvs. the entire `ThreadLocalPtr` getting destroyed, in which case there is\ncleanup work that may be avoided.\n\nThe `accessAllThreads` interface is provided to walk all the thread local child\nobjects of a parent.  `accessAllThreads` initializes an accessor\nwhich holds a global lock that blocks all creation and destruction of\n`ThreadLocal` objects with the same `Tag` and can be used as an iterable\ncontainer. Typical use is for frequent write, infrequent read data access\npatterns such as counters.  Note that you must specify a unique Tag type so you\ndon't block other ThreadLocal object usage, and you should try to minimize the\nlifetime of the accessor so the lock is held for as short as possible).\n\nThe following example is a simplification of `folly/ThreadCachedInt.h`.  It\nkeeps track of a counter value and allows multiple threads to add to the count\nwithout synchronization.  In order to get the total count, `read()` iterates\nthrough all the thread local values via `accessAllThreads()` and sums them up.\n`class NewTag` is used to break the global mutex so that this class won't block\nother `ThreadLocal` usage when `read()` is called.\n\nNote that `read()` holds the global mutex which blocks construction,\ndestruction, and `read()` for other `SimpleThreadCachedInt`'s, but does not\nblock `add()`.  Also, since it uses the unique `NewTag`, `SimpleThreadCachedInt`\ndoes not affect other `ThreadLocal` usage.\n\n```Cpp\nclass SimpleThreadCachedInt {\n\n  class NewTag;  // Segments the global mutex\n  ThreadLocal<int,NewTag> val_;\n\n public:\n  void add(int val) {\n    *val_ += val;  // operator*() gives a reference to the thread local instance\n  }\n\n  int read() {\n    int ret = 0;\n    // accessAllThreads acquires the global lock\n    for (const auto& i : val_.accessAllThreads()) {\n      ret += i;\n    }  // Global lock is released on scope exit\n    return ret;\n  }\n};\n```\n\n\n### Implementation\n***\n\nWe keep a `__thread` array of pointers to objects (`ThreadEntry::elements`)\nwhere each array has an index for each unique instance of the `ThreadLocalPtr`\nobject.  Each `ThreadLocalPtr` object has a unique id that is an index into\nthese arrays so we can fetch the correct object from thread local storage\nvery efficiently.\n\nIn order to prevent unbounded growth of the id space and thus huge\n`ThreadEntry::elements` arrays, for example due to continuous creation and\ndestruction of `ThreadLocalPtr` objects, we keep track of all active instances\nby linking them together into a list.  When an instance is destroyed we remove\nit from the chain and insert the id into `freeIds_` for reuse.  These operations\nrequire a global mutex, but only happen at construction and destruction time.\n`accessAllThreads` also acquires this global mutex.\n\nWe use a single global `pthread_key_t` per `Tag` to manage object destruction\nand memory cleanup upon thread exit because there is a finite number of\n`pthread_key_t`'s available per machine.\n"
  },
  {
    "path": "folly/docs/Traits.md",
    "content": "'folly/Traits.h'\n-----------------\n\nImplements traits complementary to those provided in <type_traits>\n\n  * Implements `IsRelocatable` trait.\n  * Implements `IsOneOf` trait\n  * Macros to state the assumptions easily\n\n### Motivation\n***\n\n`<type_traits>` is the Standard type-traits library defining a variety of traits\nsuch as `is_integral` or `is_floating_point`. This helps to gain more\ninformation about a given type.\n\n`folly/Traits.h` implements traits complementing those present in the Standard.\n\n\n### IsRelocatable\n***\n\nIn C++, the default way to move an object is by\ncalling the copy constructor and destroying the old copy\ninstead of directly copying the memory contents by using memcpy().\nThe conservative approach of moving an object assumes that the copied\nobject is not relocatable.\nThe two following code sequences should be semantically equivalent for a\nrelocatable type:\n\n```Cpp\n{\n  void conservativeMove(T * from, T * to) {\n    new(to) T(from);\n    (*from).~T();\n  }\n}\n\n{\n  void optimizedMove(T * from, T * to) {\n    memcpy(to, from, sizeof(T));\n  }\n}\n```\n\nVery few C++ types are non-relocatable.\nThe type defined below maintains a pointer inside an embedded buffer and\nhence would be non-relocatable. Moving the object by simply copying its\nmemory contents would leave the internal pointer pointing to the old buffer.\n\n```Cpp\nclass NonRelocatableType {\nprivate:\n  char buffer[1024];\n  char * pointerToBuffer;\n  ...\npublic:\n  NonRelocatableType() : pointerToBuffer(buffer) {}\n  ...\n};\n```\n\nWe can optimize the task of moving a relocatable type T using memcpy.\nIsRelocatable<T>::value describes the ability of moving around memory\na value of type T by using memcpy.\n\n### Usage\n***\n\n  * Declaring types\n\n    ```Cpp\n    template <class T1, class T2>\n    class MyParameterizedType;\n\n    class MySimpleType;\n    ```\n\n  * Declaring a type as relocatable\n\n    Appending the lines below after definition of My*Type\n    (`MyParameterizedType` or `MySimpleType`) will declare it as relocatable\n\n    ```Cpp\n    /* Definition of My*Type goes here */\n    // global namespace (not inside any namespace)\n    namespace folly {\n      // defining specialization of IsRelocatable for MySimpleType\n      template <>\n      struct IsRelocatable<MySimpleType> : std::true_type {};\n      // defining specialization of IsRelocatable for MyParameterizedType\n      template <class T1, class T2>\n      struct IsRelocatable<MyParameterizedType<T1, T2>>\n          : ::std::true_type {};\n    }\n    ```\n\n  * To make it easy to state assumptions for a regular type or a family of\n    parameterized type, various macros can be used as shown below.\n\n  * Stating that a type is Relocatable using a macro\n\n    ```Cpp\n    // global namespace\n    namespace folly {\n      // For a Regular Type\n      FOLLY_ASSUME_RELOCATABLE(MySimpleType);\n      // For a Parameterized Type\n      FOLLY_ASSUME_RELOCATABLE(MyParameterizedType<T1, T2>);\n    }\n    ```\n\n`fbvector` only works with relocatable objects. If assumptions are not stated\nexplicitly, `fbvector<MySimpleType>` or `fbvector<MyParameterizedType>`\nwill fail to compile due to assertion below:\n\n```Cpp\nstatic_assert(IsRelocatable<My*Type>::value);\n```\n\nFOLLY_ASSUME_FBVECTOR_COMPATIBLE*(type) macros can be used to state that type\nis relocatable and has nothrow constructor.\n\n  * Stating that a type is `fbvector-compatible` using macros\n    i.e. relocatable and has nothrow default constructor\n\n    ```Cpp\n    // at global level, i.e no namespace\n    // macro for regular type\n    FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MySimpleType)\n    // macro for types having 2 template parameters (MyParameterizedType)\n    FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyParameterizedType)\n    ```\n\nSimilarly,\n\n  * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(MyTypeHavingOneParameter) macro is\n    for family of parameterized types having 1 parameter\n\n  * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(MyTypeHavingThreeParameters) macro is\n    for family of parameterized types having 3 parameters\n\n  * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(MyTypeHavingFourParameters) macro is\n    for family of parameterized types having 4 parameters\n\nFew common types, namely `std::basic_string`, `std::vector`, `std::list`,\n`std::map`, `std::deque`, `std::set`, `std::unique_ptr`, `std::shared_ptr`,\n`std::function`, which are compatible with `fbvector` are already instantiated\nand declared compatible with `fbvector`. `fbvector` can be directly used with\nany of these C++ types.\n\n`std::pair` can be safely assumed to be compatible with `fbvector` if both of\nits components are.\n\n### IsOneOf\n***\n\n`std::is_same<T1, T2>::value` can be used to test if types of T1 and T2 are\nsame. `folly::IsOneOf<T, T1, Ts...>::value` can be used to test if type of T\nmatches the type of one of the other template parameter, T1, T2, ...Tn.\nRecursion is used to implement this type trait.\n"
  },
  {
    "path": "folly/docs/defs.bzl",
    "content": "load(\"@bazel_skylib//lib:paths.bzl\", \"paths\")\nload(\"@bazel_skylib//lib:shell.bzl\", \"shell\")\nload(\"@fbcode_macros//build_defs:custom_rule.bzl\", \"custom_rule\")\n\n#\n# Helper functions to emit fbconfig rules\n#\n\n# Helper function to copy a file to the output directory\ndef copy(path):\n    custom_rule(\n        name = path,\n        srcs = [path],\n        build_args = shell.quote(path),\n        build_script_dep = \"//folly/docs/facebook:copy.py\",\n        output_gen_files = [path],\n        strict = False,  # Remove (https://fburl.com/strict-custom-rules)\n    )\n\n# Helper function to define a custom_rule() that will emit the HTML output.\ndef html(src, support = None, style = \"style.css\", copy_support = True):\n    html = paths.split_extension(src)[0] + \".html\"\n\n    if support == None:\n        support = []\n\n    custom_rule(\n        name = html,\n        srcs = [src, style] + support,\n        build_args =\n            \"--style %s %s\" % (shell.quote(style), shell.quote(src)) +\n            \" --pandoc-path $(exe fbsource//third-party/stackage-lts:pandoc)\",\n        build_script_dep = \"//folly/docs/facebook:build_html.py\",\n        output_gen_files = [html],\n        strict = False,  # Remove (https://fburl.com/strict-custom-rules)\n    )\n\n    if copy_support:\n        for path in support:\n            copy(path)\n"
  },
  {
    "path": "folly/docs/examples/folly/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"baton_demo\",\n    srcs = [\"synchronization/Baton.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"dynamic_demo\",\n    srcs = [\"dynamic.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/json:dynamic\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"dynamic_converter_demo\",\n    srcs = [\"DynamicConverter.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/json:dynamic\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"executor_guide\",\n    srcs = [\"ExecutorGuide.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:executor\",\n        \"//folly/coro:baton\",\n        \"//folly/coro:task\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"file_demo\",\n    srcs = [\"File.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:file\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"format_demo\",\n    srcs = [\"Format.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:format\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"likely_demo\",\n    srcs = [\"Likely.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:likely\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"function_demo\",\n    srcs = [\"Function.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:function\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"map_util_demo\",\n    srcs = [\"MapUtil.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:map_util\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"scope_guard2_demo\",\n    srcs = [\"ScopeGuard2.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:scope_guard\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"scope_guard_demo\",\n    srcs = [\"ScopeGuard.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:scope_guard\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"synchronized_demo\",\n    srcs = [\"Synchronized.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:synchronized\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ipaddress_demo\",\n    srcs = [\"ipaddress.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:network_address\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"scoped_event_base_thread_demo\",\n    srcs = [\"ScopedEventBaseThread.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"scoped_event_base_thread2_demo\",\n    srcs = [\"ScopedEventBaseThread2.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:optional\",\n        \"//folly/io/async:event_base_manager\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n        \"//folly/system:thread_name\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"cancellation_token_demo\",\n    srcs = [\"CancellationToken.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"cancellation_source_demo\",\n    srcs = [\"CancellationSource.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"cancellation_callback_demo\",\n    srcs = [\"CancellationCallback.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/CancellationCallback.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/CancellationToken.h>\n#include <folly/portability/GTest.h>\n\nusing folly::CancellationCallback;\nusing folly::CancellationSource;\n\nTEST(CancellationCallback, demo) {\n  CancellationSource src;\n\n  bool callbackExecuted = false;\n  CancellationCallback cb{src.getToken(), [&] { callbackExecuted = true; }};\n\n  EXPECT_FALSE(callbackExecuted);\n  src.requestCancellation();\n  EXPECT_TRUE(callbackExecuted);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/CancellationSource.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/CancellationToken.h>\n#include <folly/portability/GTest.h>\n\nusing folly::CancellationSource;\nusing folly::CancellationToken;\n\nTEST(CancellationSource, demo) {\n  CancellationSource src;\n  EXPECT_FALSE(src.isCancellationRequested());\n  EXPECT_TRUE(src.canBeCancelled());\n\n  CancellationToken token = src.getToken();\n  EXPECT_FALSE(token.isCancellationRequested());\n\n  src.requestCancellation();\n  EXPECT_TRUE(src.isCancellationRequested());\n  EXPECT_TRUE(token.isCancellationRequested());\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/CancellationToken.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/CancellationToken.h>\n#include <folly/portability/GTest.h>\n\nusing folly::CancellationSource;\nusing folly::CancellationToken;\n\nTEST(CancellationToken, demo) {\n  CancellationToken token;\n  EXPECT_FALSE(token.isCancellationRequested());\n  EXPECT_FALSE(token.canBeCancelled());\n\n  CancellationSource src;\n  CancellationToken token2 = src.getToken();\n  EXPECT_FALSE(token2.isCancellationRequested());\n  EXPECT_TRUE(token2.canBeCancelled());\n\n  src.requestCancellation();\n  EXPECT_TRUE(token2.isCancellationRequested());\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/DynamicConverter.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n#include <folly/json/DynamicConverter.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(converter, demo) {\n  dynamic arrayOfArrayOfInt =\n      dynamic::array(dynamic::array(1, 3, 5), dynamic::array(2, 4, 6));\n\n  auto concrete = convertTo<std::vector<std::vector<int>>>(arrayOfArrayOfInt);\n\n  EXPECT_EQ(concrete[0][2], 5);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/ExecutorGuide.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <folly/Executor.h>\n#include <folly/coro/Baton.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n\nclass LoggingExecutor : public folly::Executor {\n public:\n  void add(folly::Func f) override {\n    std::cout << \"- Adding work!\\n\";\n    e_.add(std::move(f));\n  }\n\n  void step() { e_.step(); }\n\n private:\n  folly::ManualExecutor e_;\n};\n\nfolly::coro::Task<int> baz() {\n  co_return 10;\n}\n\nfolly::coro::Task<int> foo(folly::coro::Baton& b, int x) {\n  std::cout << \"- foo(\" << x << \")\\n\";\n  int inc = co_await baz();\n  std::cout << \"- baz() returns an increment of \" << inc << \"\\n\";\n  co_await b;\n  int ret = x + inc;\n  std::cout << \"- baton has been posted, foo(\" << x << \") returning \" << ret\n            << \"\\n\";\n  co_return ret;\n}\n\nint bar(int x) {\n  int ret = x + 10;\n  std::cout << \"- bar(\" << x << \") returning \" << ret << \"\\n\";\n  return ret;\n}\n\nint main() {\n  LoggingExecutor le;\n  auto executor = folly::Executor::getKeepAliveToken(le);\n  folly::coro::Baton baton;\n\n  {\n    std::cout << \"1. Making a Task.\\n\";\n    folly::coro::Task t = foo(baton, 111);\n\n    std::cout << \"2. Assigning an Executor.\\n\";\n    folly::coro::TaskWithExecutor te = co_withExecutor(executor, std::move(t));\n\n    std::cout << \"3. Starting the Task.\\n\"\n              << \"   This is where the Coroutine calls executor.add().\\n\";\n    auto sf = std::move(te).start();\n\n    std::cout << \"4. Stepping the executor.\\n\"\n              << \"   The Coroutine runs until it is blocked.\\n\"\n              << \"   The call to baz() does not block, so is executed.\\n\"\n              << \"   The Baton isn't ready, so execution stops there.\\n\";\n    executor->step();\n\n    std::cout << \"5. Posting the Baton.\\n\"\n              << \"   This will unblock the Coroutine; it will automatically\"\n                 \" re-add itself to the executor.\\n\";\n    baton.post();\n\n    std::cout << \"6. Stepping the executor.\\n\"\n              << \"   The Coroutine will now complete its execution.\\n\";\n    executor->step();\n  }\n  std::cout << \"\\n\";\n  {\n    std::cout << \"1. Making a SemiFuture.\\n\";\n    auto sf1 = folly::makeSemiFuture(111);\n\n    std::cout << \"2. Adding 'defered' work to the SemiFuture.\\n\"\n              << \"   This work will be run as part of the initial execution.\\n\";\n    auto sf2 = std::move(sf1).deferValue(bar).deferValue(bar);\n\n    std::cout << \"3. Assigning an Executor.\\n\"\n              << \"   The now-Future will immediately call executor.add().\\n\";\n    auto f1 = std::move(sf2).via(executor);\n\n    std::cout << \"4. Adding a continuation.\\n\"\n              << \"   Since the first part of the Future hasn't run yet,\"\n                 \" this isn't ready to run, hence is not added.\\n\";\n    auto f2 = std::move(f1).thenValue(bar);\n\n    std::cout\n        << \"5. Stepping the executor.\\n\"\n        << \"   This will run the defered computation.\\n\"\n        << \"   Furthermore, this unblocks the second part of the Future\"\n           \" (the .thenValue() continuation), so more work will be added\"\n           \" to the executor.\\n\";\n    executor->step();\n\n    std::cout << \"6. Stepping the executor.\\n\"\n              << \"   This will run the .thenValue(bar) continuation.\\n\";\n    executor->step();\n\n    std::cout << \"7. Adding another continuation.\\n\"\n              << \"   Since the Future is ready, this adds immediately.\\n\";\n    auto f3 = std::move(f2).thenValue(bar);\n\n    std::cout << \"8. Stepping the executor.\\n\";\n    executor->step();\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/File.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/File.h>\n#include <folly/portability/GTest.h>\n\nTEST(File, Demo) {\n  auto temp = folly::File::temporary();\n\n  EXPECT_TRUE(bool(temp));\n\n  // Create a new object from existing fd\n  auto temp2 = folly::File(temp.fd());\n  temp2.close();\n\n  EXPECT_FALSE(bool(temp2));\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/Format.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Format.h>\n#include <folly/portability/GTest.h>\n\nTEST(Format, demo) {\n  std::string s1 = fmt::format(\"The answer to {} is {}\", \"life\", 42);\n  EXPECT_EQ(s1, \"The answer to life is 42\");\n\n  std::string s2 = folly::sformat(\"{0}{0}{0}{0} Batman!\", \"na\");\n  EXPECT_EQ(s2, \"nananana Batman!\");\n\n  EXPECT_EQ(folly::sformat(\"{:.2f}\", 3.1415926535), \"3.14\");\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/Function.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Function.h>\n#include <folly/portability/GTest.h>\n\nTEST(Function, demo) {\n  int count = 0;\n  folly::Function<void(int&)> increment = [](int& number) { number++; };\n  increment(count);\n  EXPECT_EQ(count, 1);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/Likely.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstdlib>\n#include <folly/Likely.h>\n#include <folly/portability/GTest.h>\n\nTEST(likely, demo) {\n  int x = std::rand() % 1;\n  if (FOLLY_LIKELY(x)) {\n    // Compiler optimizes for this branch...\n  } else {\n    // ...even if this one is actually hotter\n  }\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/MapUtil.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string>\n#include <unordered_map>\n#include <folly/MapUtil.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(maputil, demo) {\n  std::unordered_map<std::string, double> famous_constants = {\n      {\"pi\", 3.14159},\n      {\"e\", 2.71828},\n  };\n\n  const double* logs = get_ptr(famous_constants, \"e\");\n  ASSERT_NE(logs, nullptr);\n  EXPECT_EQ(*logs, 2.71828);\n\n  const double* beauty = get_ptr(famous_constants, \"golden_ratio\");\n  EXPECT_EQ(beauty, nullptr);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/ScopeGuard.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ScopeGuard.h>\n#include <folly/portability/GTest.h>\n\nTEST(scopeGuard, demo) {\n  int x = 0;\n  {\n    auto guard = folly::makeGuard([&]() { x += 1; });\n    EXPECT_EQ(x, 0);\n  }\n  EXPECT_EQ(x, 1);\n}\n\nTEST(scopeExit, demo) {\n  int y = 0;\n  {\n    SCOPE_EXIT {\n      y = 1234;\n    };\n    EXPECT_EQ(y, 0);\n  }\n  EXPECT_EQ(y, 1234);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/ScopeGuard2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ScopeGuard.h>\n#include <folly/portability/GTest.h>\n\nTEST(scopeGuard, demo) {\n  int x = 0;\n  {\n    auto guard = folly::makeGuard([&]() { x += 1; });\n    EXPECT_EQ(x, 0);\n  }\n  EXPECT_EQ(x, 1);\n\n  {\n    auto guard = folly::makeGuard([&]() { x += 1; });\n    guard.dismiss(); // Turn off the guard's function\n    EXPECT_EQ(x, 1);\n  }\n  EXPECT_EQ(x, 1);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/ScopedEventBaseThread.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/ScopedEventBaseThread.h>\n\n#include <chrono>\n\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing folly::Baton;\nusing folly::ScopedEventBaseThread;\nusing std::chrono::seconds;\n\nTEST(ScopedEventBaseThread, demo1) {\n  ScopedEventBaseThread t1;\n  Baton<> done;\n\n  t1.getEventBase()->runInEventBaseThread([&] { done.post(); });\n  ASSERT_TRUE(done.try_wait_for(seconds(1)));\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/ScopedEventBaseThread2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/ScopedEventBaseThread.h>\n\n#include <chrono>\n#include <string>\n\n#include <folly/Optional.h>\n#include <folly/io/async/EventBaseManager.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/system/ThreadName.h>\n\nusing folly::Baton;\nusing folly::EventBase;\nusing folly::EventBaseManager;\nusing folly::Optional;\nusing folly::ScopedEventBaseThread;\nusing folly::StringPiece;\nusing std::chrono::seconds;\n\nTEST(ScopedEventBaseThread, demo1) {\n  constexpr StringPiece kThreadName{\"my_thread\"};\n  ScopedEventBaseThread t2(kThreadName);\n  Optional<std::string> createdThreadName;\n  Baton<> done;\n\n  t2.getEventBase()->runInEventBaseThread([&] {\n    createdThreadName = folly::getCurrentThreadName();\n    done.post();\n  });\n\n  ASSERT_TRUE(done.try_wait_for(seconds(1)));\n  if (createdThreadName) {\n    ASSERT_EQ(kThreadName.toString(), createdThreadName.value());\n  }\n}\n\nTEST(ScopedEventBaseThread, demo2) {\n  EventBaseManager ebm;\n  ScopedEventBaseThread t3(&ebm);\n\n  auto t3_eb = t3.getEventBase();\n  auto ebm_eb = static_cast<EventBase*>(nullptr);\n  t3_eb->runInEventBaseThreadAndWait([&] { ebm_eb = ebm.getEventBase(); });\n  EXPECT_EQ(uintptr_t(t3_eb), uintptr_t(ebm_eb));\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/Synchronized.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n#include <folly/Synchronized.h>\n#include <folly/portability/GTest.h>\n\nTEST(Synchronized, demo) {\n  folly::Synchronized<int> si(123);\n  std::thread t;\n  int val = 0;\n\n  { // Best practice: open a new scope before acquiring lock\n    auto wlock = si.wlock();\n\n    t = std::thread([&]() {\n      // Acquire read lock, then read the value\n      auto rlock = si.rlock();\n      val = *rlock;\n    });\n\n    *wlock = 456;\n  } // wlock is released here, when it goes out of scope\n\n  t.join();\n\n  EXPECT_EQ(val, 456);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/container/Array.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/container/Array.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(array, fromlist) {\n  // Two different ways to make an array of squares\n  auto a = make_array<int>(0, 1, 4, 9, 16);\n\n  auto squareFn = [](int i) { return i * i; };\n  auto b = make_array_with<5>(squareFn);\n\n  EXPECT_EQ(a.size(), 5);\n  EXPECT_EQ(a, b);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/container/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"array_demo\",\n    srcs = [\"Array.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/container:array\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/AsyncScope.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncScope.h>\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/executors/GlobalExecutor.h>\n\nCO_TEST(AsyncScope, demo) {\n  std::atomic<int> count = 0;\n  auto incrementBy5 = [&]() -> folly::coro::Task<> {\n    count += 5;\n    co_return;\n  };\n  auto incrementBy10 = [&]() -> folly::coro::Task<> {\n    count += 10;\n    co_return;\n  };\n  auto incrementBy100 = [&]() -> folly::coro::Task<> {\n    count += 100;\n    co_return;\n  };\n\n  folly::coro::AsyncScope scope;\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), incrementBy5()));\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), incrementBy10()));\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), incrementBy100()));\n\n  co_await scope.joinAsync();\n  EXPECT_EQ(count, 115);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"promise_demo\",\n    srcs = [\"Promise.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:promise\",\n        \"//folly/coro:task\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"detach_on_cancel_demo\",\n    srcs = [\"DetachOnCancel.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/coro:detach_on_cancel\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:promise\",\n        \"//folly/coro:task\",\n        \"//folly/coro:timeout\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"with_cancellation_demo\",\n    srcs = [\"WithCancellation.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:sleep\",\n        \"//folly/coro:task\",\n        \"//folly/coro:timeout\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_scope_demo\",\n    srcs = [\"AsyncScope.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/executors:global_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"cancellable_async_scope_demo\",\n    srcs = [\"CancellableAsyncScope.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/coro:async_scope\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/executors:global_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"task_demo\",\n    srcs = [\"Task.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:task\",\n        \"//folly/executors:global_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"retry_demo\",\n    srcs = [\"Retry.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:retry\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/CancellableAsyncScope.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncScope.h>\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/executors/GlobalExecutor.h>\n\nCO_TEST(CancellableAsyncScope, demo) {\n  std::atomic<int> count = 0;\n  auto incrementBy5 = [&]() -> folly::coro::Task<> {\n    count += 5;\n    co_return;\n  };\n  auto incrementBy10 = [&]() -> folly::coro::Task<> {\n    count += 10;\n    co_return;\n  };\n  auto incrementBy100 = [&]() -> folly::coro::Task<> {\n    count += 100;\n    co_return;\n  };\n\n  folly::coro::CancellableAsyncScope scope;\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), incrementBy5()));\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), incrementBy10()));\n  scope.add(co_withExecutor(folly::getGlobalCPUExecutor(), incrementBy100()));\n\n  EXPECT_FALSE(scope.isScopeCancellationRequested());\n\n  co_await scope.cancelAndJoinAsync();\n\n  EXPECT_EQ(scope.remaining(), 0);\n  EXPECT_TRUE(scope.isScopeCancellationRequested());\n  EXPECT_EQ(count, 115);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/DetachOnCancel.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <chrono>\n#include <thread>\n\n#include <folly/coro/DetachOnCancel.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Promise.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Timeout.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals::chrono_literals;\n\nCO_TEST(CodeExamples, demoDetachOnCancel) {\n  co_await folly::coro::co_awaitTry(\n      folly::coro::timeout(\n          folly::coro::co_invoke([]() -> folly::coro::Task<> {\n            auto&& promiseFuture = folly::coro::makePromiseContract<void>();\n\n            std::thread longTask(\n                [promise = std::move(promiseFuture.first)]() mutable {\n                  // NOLINTNEXTLINE(facebook-hte-BadCall-sleep_for)\n                  std::this_thread::sleep_for(1s);\n                  LOG(INFO) << \"Long running task finishes\";\n                  promise.setValue();\n                });\n\n            try {\n              co_await folly::coro::detachOnCancel(\n                  std::move(promiseFuture.second));\n              LOG(INFO) << \"DONE\";\n            } catch (folly::OperationCancelled&) {\n              LOG(INFO) << \"cancelled\";\n            }\n            longTask.join();\n          }),\n          1s));\n  co_return;\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/Promise.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <chrono>\n#include <thread>\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Promise.h>\n#include <folly/coro/Task.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals::chrono_literals;\n\nauto sleepAndNotify(std::function<void(int)>&& cob) {\n  return std::thread(\n      [cob = std::forward<std::function<void(int)>>(cob)]() mutable {\n        // NOLINTNEXTLINE(facebook-hte-BadCall-sleep_for)\n        std::this_thread::sleep_for(1s);\n        cob(1);\n      });\n}\n\nCO_TEST(CodeExamples, promiseFuturePoc) {\n  auto&& pf = folly::coro::makePromiseContract<int>();\n\n  auto job = sleepAndNotify(\n      [promise = std::make_shared<folly::coro::Promise<int>>(\n           std::move(pf.first))](int i) mutable { promise->setValue(i); });\n\n  CO_ASSERT_EQ(co_await std::move(pf.second), 1);\n\n  job.join();\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/Retry.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <chrono>\n#include <thread>\n\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Retry.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals::chrono_literals;\n\nCO_TEST(CodeExamples, retryTest) {\n  int t = 0;\n\n  co_await folly::coro::retryWithExponentialBackoff(\n      3, 100ms, 500ms, 0.05, [&t]() -> folly::coro::Task<> {\n        if (t++ < 3) {\n          co_yield folly::coro::co_error(std::logic_error(\"\"));\n        }\n      });\n  CO_ASSERT_EQ(t, 4);\n\n  CO_ASSERT_THROW(\n      co_await folly::coro::retryWithExponentialBackoff(\n          3,\n          100ms,\n          500ms,\n          0.05,\n          [&t]() -> folly::coro::Task<> {\n            t++;\n            co_yield folly::coro::co_error(std::logic_error(\"\"));\n          }),\n      std::logic_error);\n  CO_ASSERT_EQ(t, 8);\n\n  co_return;\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/Task.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Task.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::coro;\n\nTask<int> co_generateFortyTwo() {\n  co_return 42;\n}\n\nTask<int> co_answerToLife() {\n  int answer = co_await co_generateFortyTwo();\n  co_return answer;\n}\n\nTEST(Task, demo) {\n  auto executor = folly::getGlobalCPUExecutor().get();\n  int answer = blockingWait(co_withExecutor(executor, co_answerToLife()));\n\n  EXPECT_EQ(answer, 42);\n\n  auto t1 = makeTask(1);\n  auto t2 = makeTask(2);\n  t1.swap(t2);\n\n  EXPECT_EQ(blockingWait(co_withExecutor(executor, std::move(t1))), 2);\n  EXPECT_EQ(blockingWait(co_withExecutor(executor, std::move(t2))), 1);\n\n  auto answerFuture = co_answerToLife().semi().via(executor).then(\n      [](folly::Try<int> semiResult) { EXPECT_EQ(semiResult.value(), 42); });\n\n  folly::collectAll(std::move(answerFuture));\n\n  auto voidReturnTask = makeTask();\n  EXPECT_NO_THROW(\n      blockingWait(co_withExecutor(executor, std::move(voidReturnTask))));\n\n  auto errorYieldingTask = makeErrorTask<void>(\n      folly::make_exception_wrapper<std::logic_error>(\"not really\"));\n  EXPECT_THROW(blockingWait(std::move(errorYieldingTask)), std::logic_error);\n\n  auto taskifiedTry = makeResultTask(folly::Try<int>(10));\n  EXPECT_EQ(\n      blockingWait(co_withExecutor(executor, std::move(taskifiedTry))), 10);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/coro/WithCancellation.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <chrono>\n#include <thread>\n\n#include <folly/CancellationToken.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Sleep.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Timeout.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals::chrono_literals;\n\nCO_TEST(CodeExamples, demoTimeoutAndWithCancellationCancel) {\n  folly::CancellationSource cs;\n  std::thread job([&cs] {\n    // NOLINTNEXTLINE(facebook-hte-BadCall-sleep_for)\n    std::this_thread::sleep_for(500ms);\n    cs.requestCancellation();\n  });\n\n  CO_ASSERT_THROW(\n      co_await folly::coro::co_withCancellation(\n          cs.getToken(), folly::coro::sleep(1s)),\n      folly::OperationCancelled);\n  job.join();\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/dynamic/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"array_demo\",\n    srcs = [\"array.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/json:dynamic\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"object_demo\",\n    srcs = [\"object.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/json:dynamic\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/dynamic/array.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/json/dynamic.h>\n#include <folly/portability/GTest.h>\n\nTEST(dynamic, arrayCtor) {\n  auto a = folly::dynamic::array(123, \"hello\", nullptr);\n\n  ASSERT_TRUE(a.isArray());\n  ASSERT_EQ(a.size(), 3);\n  EXPECT_EQ(a[0], 123);\n  EXPECT_EQ(a[1], \"hello\");\n  EXPECT_TRUE(a[2].isNull());\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/dynamic/object.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/json/dynamic.h>\n#include <folly/portability/GTest.h>\n\nTEST(dynamic, objectCtor) {\n  folly::dynamic o1 = folly::dynamic::object;\n  folly::dynamic o2 =\n      folly::dynamic::object(\"key\", \"value\")(1, 2)(nullptr, nullptr);\n\n  ASSERT_TRUE(o1.isObject());\n  ASSERT_TRUE(o2.isObject());\n  ASSERT_EQ(o1.size(), 0);\n  ASSERT_EQ(o2.size(), 3);\n\n  EXPECT_EQ(o2[\"key\"], \"value\");\n  EXPECT_EQ(o2[1], 2);\n  EXPECT_EQ(o2[nullptr], nullptr);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/dynamic.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/json/dynamic.h>\n#include <folly/portability/GTest.h>\n\nusing folly::dynamic;\n\nTEST(dynamic, demo) {\n  dynamic twelve = 12;\n  dynamic str = \"string\";\n  dynamic map = dynamic::object;\n  map[str] = twelve;\n  ++map[str];\n  EXPECT_EQ(map[str], 13);\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/hash/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"hash_demo\",\n    srcs = [\"Hash.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/hash/Hash.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/Hash.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::hash;\n\nTEST(hash, demo) {\n  uint64_t a = 12345;\n  uint64_t b = 67890;\n  uint64_t ha = twang_mix64(a);\n  uint64_t hb = twang_mix64(b);\n  uint64_t h = hash_128_to_64(ha, hb);\n\n  EXPECT_EQ(h, 3938132036471221536);\n\n  uint64_t switched = hash_128_to_64(hb, ha); // switch order of ha, hb\n  EXPECT_NE(h, switched); // hash_128_to_64 is order-dependent\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/io/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"i_o_buf_demo\",\n    srcs = [\"IOBuf.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/io:iobuf\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/io/IOBuf.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/IOBuf.h>\n#include <folly/portability/GTest.h>\n\nusing folly::IOBuf;\n\nTEST(IOBuf, demo) {\n  std::unique_ptr<IOBuf> buf(IOBuf::create(100));\n  uint32_t cap = buf->capacity();\n  EXPECT_LE(100, cap);\n  EXPECT_EQ(0, buf->headroom());\n  EXPECT_EQ(0, buf->length());\n  EXPECT_EQ(cap, buf->tailroom());\n\n  folly::StringPiece str{\"world\"};\n  EXPECT_LE(str.size(), buf->tailroom());\n  memcpy(buf->writableData(), str.data(), str.size());\n  buf->append(str.size());\n\n  buf->advance(10);\n  EXPECT_EQ(10, buf->headroom());\n  EXPECT_EQ(5, buf->length());\n  EXPECT_EQ(cap - 15, buf->tailroom());\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/ipaddress.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/IPAddress.h>\n#include <folly/portability/GTest.h>\n\nusing folly::IPAddress;\n\nTEST(IPAddress, demo) {\n  IPAddress v4addr(\"192.0.2.129\");\n  IPAddress v6map(\"::ffff:192.0.2.129\");\n  ASSERT_EQ(\n      v4addr.inSubnet(\"192.0.2.0/24\"),\n      v4addr.inSubnet(IPAddress(\"192.0.2.0\"), 24));\n  ASSERT_TRUE(v4addr.inSubnet(\"192.0.2.128/30\"));\n  ASSERT_FALSE(v4addr.inSubnet(\"192.0.2.128/32\"));\n  ASSERT_EQ(v4addr.asV4().toLong(), 2164392128);\n  ASSERT_EQ(v4addr.asV4().toLongHBO(), 3221226113);\n  ASSERT_TRUE(v4addr.isV4());\n  ASSERT_TRUE(v6map.isV6());\n  ASSERT_EQ(v4addr, v6map);\n  ASSERT_TRUE(v6map.isIPv4Mapped());\n  ASSERT_EQ(v4addr.asV4(), IPAddress::createIPv4(v6map));\n  ASSERT_EQ(IPAddress::createIPv6(v4addr), v6map.asV6());\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/synchronization/Baton.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\n#include <atomic>\n#include <chrono>\n#include <thread>\n\nusing folly::Baton;\nusing std::chrono_literals::operator\"\"s;\n\nTEST(Baton, demo) {\n  Baton<true, std::atomic> baton;\n  std::thread waiter([&]() {\n    // wait for an event before proceeding\n    auto posted = baton.try_wait_for(5s);\n    EXPECT_TRUE(posted);\n  });\n  std::thread poster([&]() { baton.post(); });\n  waiter.join();\n  poster.join();\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"test_util_demo\",\n    srcs = [\"TestUtils.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/test/TestUtils.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/portability/GTest.h>\n#include <folly/test/TestUtils.h>\n\nTEST(TestUtilsDemo, Print) {\n  using namespace testing;\n  const auto kHelloFbString = folly::fbstring(\"hello\");\n  const auto kHelloString = \"hello\";\n  EXPECT_EQ(PrintToString(kHelloString), PrintToString(kHelloFbString));\n}\n"
  },
  {
    "path": "folly/docs/examples/folly/test/common/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"test_main_demo\",\n    srcs = [\"TestMainDemo.cpp\"],\n    test_main = \"//folly/test/common:test_main_lib\",\n    deps = [\n        \"fbsource//third-party/googletest:gtest\",\n        \"//folly:singleton\",\n    ],\n)\n"
  },
  {
    "path": "folly/docs/examples/folly/test/common/TestMainDemo.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * This file demonstrates the usage of folly/test/common:test_main_lib,\n * which is a `main` implementation that initializes gtest & folly before\n * running tests.\n * @file\n */\n\n#include <folly/Singleton.h>\n\n#include <gtest/gtest.h>\n\nusing namespace ::testing;\n\nnamespace {\n/// This is a singleton that demonstrates folly will be initialized by\n/// `//folly/test/common:test_main_lib`.\nfolly::Singleton<int> DemoSingleton([]() { return new int(42); });\n} // namespace\n\nTEST(TestMainDemo, SingletonAccess) {\n  ASSERT_EQ(*DemoSingleton.try_get(), 42);\n}\n"
  },
  {
    "path": "folly/docs/small_vector.md",
    "content": "`folly/small_vector.h`\n----------------------\n\n`folly::small_vector<T,Int=1,...>` is a sequence container that\nimplements small buffer optimization. It behaves similarly to\nstd::vector, except until a certain number of elements are reserved it\ndoes not use the heap.\n\nLike standard vector, it is guaranteed to use contiguous memory.  (So,\nafter it spills to the heap all the elements live in the heap buffer.)\n\nSimple usage example:\n\n``` Cpp\n    small_vector<int,2> vec;\n    vec.push_back(0); // Stored in-place on stack\n    vec.push_back(1); // Still on the stack\n    vec.push_back(2); // Switches to heap buffer.\n```\n\n### Details\n***\n\nThis class is useful in either of following cases:\n\n* Short-lived stack vectors with few elements (or maybe with a\n  usually-known number of elements), if you want to avoid malloc.\n\n* If the vector(s) are usually under a known size and lookups are very\n  common, you'll save an extra cache miss in the common case when the\n  data is kept in-place.\n\n* You have billions of these vectors and don't want to waste space on\n  `std::vector`'s capacity tracking.  This vector lets `malloc` track our\n  allocation capacity.  (Note that this slows down the\n  insertion/reallocation code paths significantly; if you need those\n  to be fast you should use `fbvector`.)\n\nThe last two cases were the main motivation for implementing it.\n\nThere are also a couple of flags you can pass into this class\ntemplate to customize its behavior.  You can provide them in any\norder after the in-place count.  They are all in the `namespace\nsmall_vector_policy`.\n\n* `NoHeap` - Avoid the heap entirely.  (Throws `std::length_error` if\n  you would've spilled out of the in-place allocation.)\n\n* `<Any integral type>` - customizes the amount of space we spend on\n  tracking the size of the vector.\n\nA couple more examples:\n\n``` Cpp\n    // With space for 32 in situ unique pointers, and only using a\n    // 4-byte size_type.\n    small_vector<std::unique_ptr<int>, 32, uint32_t> v;\n\n    // A inline vector of up to 256 ints which will not use the heap.\n    small_vector<int, 256, NoHeap> v;\n\n    // Same as the above, but making the size_type smaller too.\n    small_vector<int, 256, NoHeap, uint16_t> v;\n```\n"
  },
  {
    "path": "folly/docs/style.css",
    "content": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n<style type=\"text/css\">\npre.literal-block, pre.doctest-block, pre.sourceCode {\n    margin-left: 2em;\n    margin-right: 2em;\n    background-color: #eeeeee\n}\n</style>\n"
  },
  {
    "path": "folly/dynamic-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/json/dynamic-inl.h> // @shim\n"
  },
  {
    "path": "folly/dynamic.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/json/dynamic.h> // @shim\n"
  },
  {
    "path": "folly/executors/Async.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/futures/Future.h>\n\nnamespace folly {\n\ntemplate <class F>\nauto async(F&& fn) {\n  return folly::via<F>(\n      getUnsafeMutableGlobalCPUExecutor().get(), std::forward<F>(fn));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"global_thread_pool_list\",\n    srcs = [\"GlobalThreadPoolList.cpp\"],\n    headers = [\"GlobalThreadPoolList.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:cpp_attributes\",\n        \"//folly/system:thread_id\",\n    ],\n    exported_deps = [\n        \"//folly:indestructible\",\n        \"//folly:synchronized\",\n        \"//folly:thread_local\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async\",\n    headers = [\"Async.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":global_executor\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"threaded_executor\",\n    srcs = [\"ThreadedExecutor.cpp\"],\n    headers = [\"ThreadedExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:scope_guard\",\n        \"//folly/executors/thread_factory:named_thread_factory\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \"//folly:executor\",\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors/thread_factory:thread_factory\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"function_scheduler\",\n    srcs = [\"FunctionScheduler.cpp\"],\n    headers = [\"FunctionScheduler.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:conv\",\n        \"//folly:random\",\n        \"//folly:string\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly:range\",\n        \"//folly/container:f14_hash\",\n        \"//folly/container:intrusive_heap\",\n        \"//folly/hash:hash\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"future_executor\",\n    headers = [\"FutureExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/functional:invoke\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_thread_pool_deadlock_detector_observer\",\n    srcs = [\"IOThreadPoolDeadlockDetectorObserver.cpp\"],\n    headers = [\"IOThreadPoolDeadlockDetectorObserver.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:singleton\",\n        \"//folly/concurrency:deadlock_detector\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/executors:thread_pool_executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"timekeeper_scheduled_executor\",\n    srcs = [\"TimekeeperScheduledExecutor.cpp\"],\n    headers = [\"TimekeeperScheduledExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":scheduled_executor\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"timed_drivable_executor\",\n    srcs = [\"TimedDrivableExecutor.cpp\"],\n    headers = [\"TimedDrivableExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":drivable_executor\",\n        \"//folly/concurrency:unbounded_queue\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"inline_executor\",\n    srcs = [\"InlineExecutor.cpp\"],\n    headers = [\"InlineExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:indestructible\",\n    ],\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:cpp_attributes\",\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"manual_executor\",\n    srcs = [\"ManualExecutor.cpp\"],\n    headers = [\"ManualExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/executors:drivable_executor\",\n        \"//folly/executors:scheduled_executor\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/synchronization:lifo_sem\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"queue_observer\",\n    srcs = [\"QueueObserver.cpp\"],\n    headers = [\n        \"QueueObserver.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly:portability\",\n        \"//folly:synchronized\",\n        \"//folly/portability:sys_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"soft_real_time_executor\",\n    srcs = [\"SoftRealTimeExecutor.cpp\"],\n    headers = [\"SoftRealTimeExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\"fbsource//third-party/glog:glog\"],\n    exported_deps = [\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"sequenced_executor\",\n    headers = [\"SequencedExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"serialized_executor\",\n    headers = [\"SerializedExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":sequenced_executor\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"global_executor\",\n    srcs = [\n        \"GlobalExecutor.cpp\",\n    ],\n    force_static = False,\n    raw_headers = [\n        \"GlobalExecutor.h\",\n    ],\n    deps = [\n        \"//xplat/folly:function\",\n        \"//xplat/folly:shared_mutex\",\n        \"//xplat/folly:singleton\",\n        \"//xplat/folly/detail:async_trace\",\n        \"//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"//xplat/folly/executors:inline_executor\",\n        \"//xplat/folly/executors:io_thread_pool_executor\",\n        \"//xplat/folly/system:hardware_concurrency\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"//xplat/folly:executor\",\n        \"//xplat/folly/executors:io_executor\",\n        \"//xplat/folly/executors:thread_pool_executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"serial_executor\",\n    headers = [\n        \"SerialExecutor.h\",\n        \"SerialExecutor-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":global_executor\",\n        \":serialized_executor\",\n        \"//folly:exception_string\",\n        \"//folly:scope_guard\",\n        \"//folly/io/async:request_context\",\n        \"//folly/synchronization:distributed_mutex\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"strand_executor\",\n    srcs = [\"StrandExecutor.cpp\"],\n    headers = [\"StrandExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":global_executor\",\n        \"//folly:exception_string\",\n    ],\n    exported_deps = [\n        \":serialized_executor\",\n        \"//folly:optional\",\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/io/async:request_context\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"drivable_executor\",\n    headers = [\"DrivableExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"executor_with_priority\",\n    srcs = [\"ExecutorWithPriority.cpp\"],\n    headers = [\n        \"ExecutorWithPriority.h\",\n        \"ExecutorWithPriority-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fiber_io_executor\",\n    headers = [\"FiberIOExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":io_executor\",\n        \"//folly/fibers:fiber_manager_map\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"queued_immediate_executor\",\n    srcs = [\"QueuedImmediateExecutor.cpp\"],\n    headers = [\"QueuedImmediateExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:indestructible\",\n        \"//folly:scope_guard\",\n    ],\n    exported_deps = [\n        \":inline_executor\",\n        \"//folly:executor\",\n        \"//folly:thread_local\",\n        \"//folly/io/async:request_context\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"codel\",\n    srcs = [\"Codel.cpp\"],\n    headers = [\"Codel.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"edf_thread_pool_executor\",\n    srcs = [\"EDFThreadPoolExecutor.cpp\"],\n    headers = [\"EDFThreadPoolExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/concurrency:process_local_unique_id\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:lifo_sem\",\n        \"//folly/synchronization:throttled_lifo_sem\",\n        \"//folly/tracing:static_tracepoint\",\n    ],\n    exported_deps = [\n        \":soft_real_time_executor\",\n        \":thread_pool_executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"cpu_thread_pool_executor\",\n    srcs = [\"CPUThreadPoolExecutor.cpp\"],\n    headers = [\"CPUThreadPoolExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:executor\",\n        \"//folly:memory\",\n        \"//folly:optional\",\n        \"//folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"//folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"//folly/executors/task_queue:unbounded_blocking_queue\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:throttled_lifo_sem\",\n    ],\n    exported_deps = [\n        \":queue_observer\",\n        \":thread_pool_executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"thread_pool_executor\",\n    srcs = [\"ThreadPoolExecutor.cpp\"],\n    headers = [\"ThreadPoolExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:exception_string\",\n        \"//folly:glog\",\n        \"//folly/concurrency:process_local_unique_id\",\n        \"//folly/portability:pthread\",\n        \"//folly/synchronization:asymmetric_thread_fence\",\n        \"//folly/tracing:static_tracepoint\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":global_thread_pool_list\",\n        \"//folly:default_keep_alive_executor\",\n        \"//folly:memory\",\n        \"//folly:shared_mutex\",\n        \"//folly/container:span\",\n        \"//folly/executors/task_queue:lifo_sem_mpmc_queue\",\n        \"//folly/executors/thread_factory:named_thread_factory\",\n        \"//folly/io/async:request_context\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:atomic_struct\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"scheduled_executor\",\n    headers = [\"ScheduledExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:executor\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_executor\",\n    headers = [\"IOExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"executors\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":async\",  # @manual\n        \":cpu_thread_pool_executor\",  # @manual\n        \":fiber_io_executor\",  # @manual\n        \":future_executor\",  # @manual\n        \":global_executor\",  # @manual\n        \":io_executor\",  # @manual\n        \":io_object_cache\",  # @manual\n        \":io_thread_pool_executor\",  # @manual\n        \":serial_executor\",  # @manual\n        \":thread_pool_executor\",  # @manual\n        \":threaded_executor\",  # @manual\n        \"//folly/executors/task_queue:blocking_queue\",  # @manual\n        \"//folly/executors/task_queue:lifo_sem_mpmc_queue\",  # @manual\n        \"//folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",  # @manual\n        \"//folly/executors/task_queue:unbounded_blocking_queue\",  # @manual\n        \"//folly/executors/thread_factory:named_thread_factory\",  # @manual\n        \"//folly/executors/thread_factory:priority_thread_factory\",  # @manual\n        \"//folly/executors/thread_factory:thread_factory\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_object_cache\",\n    headers = [\"IOObjectCache.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":global_executor\",\n        \"//folly:thread_local\",\n        \"//folly/io/async:async_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_thread_pool_executor\",\n    srcs = [\"IOThreadPoolExecutor.cpp\"],\n    headers = [\"IOThreadPoolExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/detail:memory_idler\",\n        \"//folly/portability:gflags\",\n    ],\n    exported_deps = [\n        \":io_executor\",\n        \":queue_observer\",\n        \":thread_pool_executor\",\n        \"//folly:portability\",\n        \"//folly/io/async:event_base_manager\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"metered_executor\",\n    headers = [\n        \"MeteredExecutor.h\",\n        \"MeteredExecutor-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":queue_observer\",\n        \"//folly:default_keep_alive_executor\",\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/io/async:async_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"virtual_executor\",\n    headers = [\n        \"VirtualExecutor.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:default_keep_alive_executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"threaded_repeating_function_runner\",\n    srcs = [\"ThreadedRepeatingFunctionRunner.cpp\"],\n    headers = [\"ThreadedRepeatingFunctionRunner.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \"//folly:function\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"execution_observer\",\n    srcs = [\"ExecutionObserver.cpp\"],\n    headers = [\"ExecutionObserver.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/tracing:static_tracepoint\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"global_executor\",\n    srcs = [\"GlobalExecutor.cpp\"],\n    headers = [\"GlobalExecutor.h\"],\n    deps = [\n        \":cpu_thread_pool_executor\",\n        \":inline_executor\",\n        \":io_thread_pool_executor\",\n        \"//folly:function\",\n        \"//folly:shared_mutex\",\n        \"//folly:singleton\",\n        \"//folly/detail:async_trace\",\n        \"//folly/system:hardware_concurrency\",\n    ],\n    exported_deps = [\n        \":io_executor\",\n        \"//folly:executor\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\n# !!!! fbcode/folly/executors/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfb_dirsync_cpp_library(\n    name = \"striped_edf_thread_pool_executor\",\n    srcs = [\n        \"StripedEDFThreadPoolExecutor.cpp\",\n    ],\n    headers = [\n        \"StripedEDFThreadPoolExecutor.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/container:intrusive_heap\",\n        \"//folly/synchronization:distributed_mutex\",\n        \"//folly/synchronization:striped_throttled_lifo_sem\",\n    ],\n    exported_deps = [\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:soft_real_time_executor\",\n        \"//folly/synchronization:throttled_lifo_sem\",\n    ],\n)\n"
  },
  {
    "path": "folly/executors/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async\n  HEADERS\n    Async.h\n  EXPORTED_DEPS\n    folly_executors_global_executor\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME codel\n  SRCS\n    Codel.cpp\n  HEADERS\n    Codel.h\n  DEPS\n    folly_portability_gflags\n)\n\nfolly_add_library(\n  NAME cpu_thread_pool_executor\n  SRCS\n    CPUThreadPoolExecutor.cpp\n  HEADERS\n    CPUThreadPoolExecutor.h\n  DEPS\n    folly_executor\n    folly_executors_task_queue_priority_lifo_sem_mpmc_queue\n    folly_executors_task_queue_priority_unbounded_blocking_queue\n    folly_executors_task_queue_unbounded_blocking_queue\n    folly_memory\n    folly_optional\n    folly_portability_gflags\n    folly_synchronization_throttled_lifo_sem\n  EXPORTED_DEPS\n    folly_executors_queue_observer\n    folly_executors_thread_pool_executor\n)\n\nfolly_add_library(\n  NAME drivable_executor\n  HEADERS\n    DrivableExecutor.h\n  EXPORTED_DEPS\n    folly_executor\n)\n\nfolly_add_library(\n  NAME edf_thread_pool_executor\n  SRCS\n    EDFThreadPoolExecutor.cpp\n  HEADERS\n    EDFThreadPoolExecutor.h\n  DEPS\n    folly_concurrency_process_local_unique_id\n    folly_portability_gflags\n    folly_synchronization_lifo_sem\n    folly_synchronization_throttled_lifo_sem\n    folly_tracing_static_tracepoint\n  EXPORTED_DEPS\n    folly_executors_soft_real_time_executor\n    folly_executors_thread_pool_executor\n)\n\nfolly_add_library(\n  NAME execution_observer\n  SRCS\n    ExecutionObserver.cpp\n  HEADERS\n    ExecutionObserver.h\n  DEPS\n    folly_tracing_static_tracepoint\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME executor_with_priority\n  SRCS\n    ExecutorWithPriority.cpp\n  HEADERS\n    ExecutorWithPriority-inl.h\n    ExecutorWithPriority.h\n  EXPORTED_DEPS\n    folly_executor\n)\n\nfolly_add_library(\n  NAME executors\n  EXPORTED_DEPS\n    folly_executors_async\n    folly_executors_cpu_thread_pool_executor\n    folly_executors_fiber_io_executor\n    folly_executors_future_executor\n    folly_executors_global_executor\n    folly_executors_io_executor\n    folly_executors_io_object_cache\n    folly_executors_io_thread_pool_executor\n    folly_executors_serial_executor\n    folly_executors_task_queue_blocking_queue\n    folly_executors_task_queue_lifo_sem_mpmc_queue\n    folly_executors_task_queue_priority_lifo_sem_mpmc_queue\n    folly_executors_task_queue_unbounded_blocking_queue\n    folly_executors_thread_factory_named_thread_factory\n    folly_executors_thread_factory_priority_thread_factory\n    folly_executors_thread_factory_thread_factory\n    folly_executors_thread_pool_executor\n    folly_executors_threaded_executor\n)\n\nfolly_add_library(\n  NAME fiber_io_executor\n  HEADERS\n    FiberIOExecutor.h\n  EXPORTED_DEPS\n    folly_executors_io_executor\n    folly_fibers_fiber_manager_map\n)\n\nfolly_add_library(\n  NAME function_scheduler\n  SRCS\n    FunctionScheduler.cpp\n  HEADERS\n    FunctionScheduler.h\n  DEPS\n    folly_conv\n    folly_random\n    folly_string\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_container_f14_hash\n    folly_container_intrusive_heap\n    folly_function\n    folly_hash_hash\n    folly_range\n)\n\nfolly_add_library(\n  NAME future_executor\n  HEADERS\n    FutureExecutor.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME global_executor\n  SRCS\n    GlobalExecutor.cpp\n  HEADERS\n    GlobalExecutor.h\n  DEPS\n    folly_detail_async_trace\n    folly_executors_cpu_thread_pool_executor\n    folly_executors_inline_executor\n    folly_executors_io_thread_pool_executor\n    folly_function\n    folly_shared_mutex\n    folly_singleton\n    folly_system_hardware_concurrency\n  EXPORTED_DEPS\n    folly_executor\n    folly_executors_io_executor\n    folly_portability_gflags\n)\n\nfolly_add_library(\n  NAME global_thread_pool_list\n  SRCS\n    GlobalThreadPoolList.cpp\n  HEADERS\n    GlobalThreadPoolList.h\n  DEPS\n    folly_cpp_attributes\n    folly_system_thread_id\n  EXPORTED_DEPS\n    folly_indestructible\n    folly_synchronized\n    folly_thread_local\n)\n\nfolly_add_library(\n  NAME inline_executor\n  SRCS\n    InlineExecutor.cpp\n  HEADERS\n    InlineExecutor.h\n  DEPS\n    folly_indestructible\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_cpp_attributes\n    folly_executor\n)\n\nfolly_add_library(\n  NAME io_executor\n  HEADERS\n    IOExecutor.h\n  EXPORTED_DEPS\n    folly_executor\n)\n\nfolly_add_library(\n  NAME io_object_cache\n  HEADERS\n    IOObjectCache.h\n  EXPORTED_DEPS\n    folly_executors_global_executor\n    folly_io_async_async_base\n    folly_thread_local\n)\n\nfolly_add_library(\n  NAME io_thread_pool_deadlock_detector_observer\n  SRCS\n    IOThreadPoolDeadlockDetectorObserver.cpp\n  HEADERS\n    IOThreadPoolDeadlockDetectorObserver.h\n  EXPORTED_DEPS\n    folly_concurrency_deadlock_detector\n    folly_executors_io_thread_pool_executor\n    folly_executors_thread_pool_executor\n    folly_singleton\n)\n\nfolly_add_library(\n  NAME io_thread_pool_executor\n  SRCS\n    IOThreadPoolExecutor.cpp\n  HEADERS\n    IOThreadPoolExecutor.h\n  DEPS\n    folly_detail_memory_idler\n    folly_portability_gflags\n  EXPORTED_DEPS\n    folly_executors_io_executor\n    folly_executors_queue_observer\n    folly_executors_thread_pool_executor\n    folly_io_async_event_base_manager\n    folly_portability\n    folly_synchronization_relaxed_atomic\n)\n\nfolly_add_library(\n  NAME manual_executor\n  SRCS\n    ManualExecutor.cpp\n  HEADERS\n    ManualExecutor.h\n  EXPORTED_DEPS\n    folly_executors_drivable_executor\n    folly_executors_scheduled_executor\n    folly_executors_sequenced_executor\n    folly_synchronization_lifo_sem\n)\n\nfolly_add_library(\n  NAME metered_executor\n  HEADERS\n    MeteredExecutor-inl.h\n    MeteredExecutor.h\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_default_keep_alive_executor\n    folly_executors_queue_observer\n    folly_io_async_async_base\n)\n\nfolly_add_library(\n  NAME queue_observer\n  SRCS\n    QueueObserver.cpp\n  HEADERS\n    QueueObserver.h\n  EXPORTED_DEPS\n    folly_function\n    folly_portability\n    folly_portability_sys_types\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME queued_immediate_executor\n  SRCS\n    QueuedImmediateExecutor.cpp\n  HEADERS\n    QueuedImmediateExecutor.h\n  DEPS\n    folly_indestructible\n    folly_scope_guard\n  EXPORTED_DEPS\n    folly_executor\n    folly_executors_inline_executor\n    folly_io_async_request_context\n    folly_thread_local\n)\n\nfolly_add_library(\n  NAME scheduled_executor\n  HEADERS\n    ScheduledExecutor.h\n  EXPORTED_DEPS\n    folly_executor\n    folly_lang_exception\n)\n\nfolly_add_library(\n  NAME sequenced_executor\n  HEADERS\n    SequencedExecutor.h\n  EXPORTED_DEPS\n    folly_executor\n)\n\nfolly_add_library(\n  NAME serial_executor\n  HEADERS\n    SerialExecutor-inl.h\n    SerialExecutor.h\n  EXPORTED_DEPS\n    folly_exception_string\n    folly_executors_global_executor\n    folly_executors_serialized_executor\n    folly_io_async_request_context\n    folly_scope_guard\n    folly_synchronization_distributed_mutex\n    folly_synchronization_relaxed_atomic\n)\n\nfolly_add_library(\n  NAME serialized_executor\n  HEADERS\n    SerializedExecutor.h\n  EXPORTED_DEPS\n    folly_executors_sequenced_executor\n)\n\nfolly_add_library(\n  NAME soft_real_time_executor\n  SRCS\n    SoftRealTimeExecutor.cpp\n  HEADERS\n    SoftRealTimeExecutor.h\n  EXPORTED_DEPS\n    folly_executor\n)\n\nfolly_add_library(\n  NAME strand_executor\n  SRCS\n    StrandExecutor.cpp\n  HEADERS\n    StrandExecutor.h\n  DEPS\n    folly_exception_string\n    folly_executors_global_executor\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_executors_serialized_executor\n    folly_io_async_request_context\n    folly_optional\n)\n\nfolly_add_library(\n  NAME striped_edf_thread_pool_executor\n  SRCS\n    StripedEDFThreadPoolExecutor.cpp\n  HEADERS\n    StripedEDFThreadPoolExecutor.h\n  DEPS\n    folly_concurrency_cache_locality\n    folly_container_intrusive_heap\n    folly_synchronization_distributed_mutex\n    folly_synchronization_striped_throttled_lifo_sem\n  EXPORTED_DEPS\n    folly_executors_cpu_thread_pool_executor\n    folly_executors_soft_real_time_executor\n    folly_synchronization_throttled_lifo_sem\n)\n\nfolly_add_library(\n  NAME thread_pool_executor\n  SRCS\n    ThreadPoolExecutor.cpp\n  HEADERS\n    ThreadPoolExecutor.h\n  DEPS\n    folly_concurrency_process_local_unique_id\n    folly_exception_string\n    folly_glog\n    folly_portability_pthread\n    folly_synchronization_asymmetric_thread_fence\n    folly_tracing_static_tracepoint\n  EXPORTED_DEPS\n    folly_container_span\n    folly_default_keep_alive_executor\n    folly_executors_global_thread_pool_list\n    folly_executors_task_queue_lifo_sem_mpmc_queue\n    folly_executors_thread_factory_named_thread_factory\n    folly_io_async_request_context\n    folly_memory\n    folly_portability_gflags\n    folly_shared_mutex\n    folly_synchronization_atomic_struct\n    folly_synchronization_baton\n    folly_synchronization_relaxed_atomic\n)\n\nfolly_add_library(\n  NAME threaded_executor\n  SRCS\n    ThreadedExecutor.cpp\n  HEADERS\n    ThreadedExecutor.h\n  DEPS\n    folly_executors_thread_factory_named_thread_factory\n    folly_scope_guard\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_container_f14_hash\n    folly_executor\n    folly_executors_thread_factory_thread_factory\n)\n\nfolly_add_library(\n  NAME threaded_repeating_function_runner\n  SRCS\n    ThreadedRepeatingFunctionRunner.cpp\n  HEADERS\n    ThreadedRepeatingFunctionRunner.h\n  DEPS\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_function\n)\n\nfolly_add_library(\n  NAME timed_drivable_executor\n  SRCS\n    TimedDrivableExecutor.cpp\n  HEADERS\n    TimedDrivableExecutor.h\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_executors_drivable_executor\n)\n\nfolly_add_library(\n  NAME timekeeper_scheduled_executor\n  SRCS\n    TimekeeperScheduledExecutor.cpp\n  HEADERS\n    TimekeeperScheduledExecutor.h\n  EXPORTED_DEPS\n    folly_executors_scheduled_executor\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME virtual_executor\n  HEADERS\n    VirtualExecutor.h\n  EXPORTED_DEPS\n    folly_default_keep_alive_executor\n)\n\nadd_subdirectory(task_queue)\nadd_subdirectory(thread_factory)\n"
  },
  {
    "path": "folly/executors/CPUThreadPoolExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Executor.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n\n#include <atomic>\n#include <folly/Memory.h>\n#include <folly/Optional.h>\n#include <folly/executors/QueueObserver.h>\n#include <folly/executors/task_queue/PriorityLifoSemMPMCQueue.h>\n#include <folly/executors/task_queue/PriorityUnboundedBlockingQueue.h>\n#include <folly/executors/task_queue/UnboundedBlockingQueue.h>\n#include <folly/portability/GFlags.h>\n#include <folly/synchronization/ThrottledLifoSem.h>\n\nFOLLY_GFLAGS_DEFINE_bool(\n    dynamic_cputhreadpoolexecutor,\n    true,\n    \"CPUThreadPoolExecutor will dynamically create and destroy threads\");\n\nFOLLY_GFLAGS_DEFINE_bool(\n    folly_cputhreadpoolexecutor_use_throttled_lifo_sem,\n    true,\n    \"CPUThreadPoolExecutor will use ThrottledLifoSem by default\");\n\nFOLLY_GFLAGS_DEFINE_bool(\n    folly_cputhreadpoolexecutor_always_dequeue_with_timeout,\n    false,\n    \"Always use BlockingQueue::try_take_for() even when the pool cannot be \"\n    \"scaled down. Only for testing purposes\");\n\nnamespace folly {\n\nconst size_t CPUThreadPoolExecutor::kDefaultMaxQueueSize = 1 << 14;\n\nCPUThreadPoolExecutor::CPUTask::CPUTask(\n    Func&& f,\n    std::chrono::milliseconds expiration,\n    Func&& expireCallback,\n    int8_t pri)\n    : Task(std::move(f), expiration, std::move(expireCallback), pri) {}\n\nCPUThreadPoolExecutor::CPUTask::CPUTask()\n    : Task(nullptr, std::chrono::milliseconds(0), nullptr) {}\n\n/* static */ auto CPUThreadPoolExecutor::makeDefaultQueue()\n    -> std::unique_ptr<BlockingQueue<CPUTask>> {\n  return FLAGS_folly_cputhreadpoolexecutor_use_throttled_lifo_sem\n      ? makeThrottledLifoSemQueue()\n      : makeLifoSemQueue();\n}\n\n/* static */ auto CPUThreadPoolExecutor::makeDefaultPriorityQueue(\n    int8_t numPriorities) -> std::unique_ptr<BlockingQueue<CPUTask>> {\n  return FLAGS_folly_cputhreadpoolexecutor_use_throttled_lifo_sem\n      ? makeThrottledLifoSemPriorityQueue(numPriorities)\n      : makeLifoSemPriorityQueue(numPriorities);\n}\n\n/* static */ auto CPUThreadPoolExecutor::makeLifoSemQueue()\n    -> std::unique_ptr<BlockingQueue<CPUTask>> {\n  return std::make_unique<UnboundedBlockingQueue<CPUTask, LifoSem>>();\n}\n\n/* static */ auto CPUThreadPoolExecutor::makeLifoSemPriorityQueue(\n    int8_t numPriorities) -> std::unique_ptr<BlockingQueue<CPUTask>> {\n  CHECK_GT(numPriorities, 0) << \"Number of priorities should be positive\";\n  return std::make_unique<PriorityUnboundedBlockingQueue<CPUTask, LifoSem>>(\n      numPriorities);\n}\n\n/* static */ auto CPUThreadPoolExecutor::makeThrottledLifoSemQueue(\n    std::chrono::nanoseconds wakeUpInterval)\n    -> std::unique_ptr<BlockingQueue<CPUTask>> {\n  ThrottledLifoSem::Options opts;\n  opts.wakeUpInterval = wakeUpInterval;\n  return std::make_unique<UnboundedBlockingQueue<CPUTask, ThrottledLifoSem>>(\n      opts);\n}\n\n/* static */ auto CPUThreadPoolExecutor::makeThrottledLifoSemPriorityQueue(\n    int8_t numPriorities, std::chrono::nanoseconds wakeUpInterval)\n    -> std::unique_ptr<BlockingQueue<CPUTask>> {\n  ThrottledLifoSem::Options opts;\n  opts.wakeUpInterval = wakeUpInterval;\n  return std::make_unique<\n      PriorityUnboundedBlockingQueue<CPUTask, ThrottledLifoSem>>(\n      numPriorities, opts);\n}\n\nCPUThreadPoolExecutor::CPUThreadPoolExecutor(\n    size_t numThreads,\n    std::unique_ptr<BlockingQueue<CPUTask>> taskQueue,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    Options opt)\n    : CPUThreadPoolExecutor(\n          std::make_pair(\n              numThreads, FLAGS_dynamic_cputhreadpoolexecutor ? 0 : numThreads),\n          std::move(taskQueue),\n          std::move(threadFactory),\n          std::move(opt)) {}\n\nCPUThreadPoolExecutor::CPUThreadPoolExecutor(\n    std::pair<size_t, size_t> numThreads,\n    std::unique_ptr<BlockingQueue<CPUTask>> taskQueue,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    Options opt)\n    : ThreadPoolExecutor(\n          numThreads.first, numThreads.second, std::move(threadFactory)),\n      taskQueue_(std::move(taskQueue)),\n      prohibitBlockingOnThreadPools_{opt.blocking} {\n  setNumThreads(numThreads.first);\n  if (numThreads.second == 0) {\n    minThreads_.store(1, std::memory_order_relaxed);\n  }\n  registerThreadPoolExecutor(this);\n}\n\nCPUThreadPoolExecutor::CPUThreadPoolExecutor(\n    size_t numThreads,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    Options opt)\n    : CPUThreadPoolExecutor(\n          std::make_pair(\n              numThreads, FLAGS_dynamic_cputhreadpoolexecutor ? 0 : numThreads),\n          std::move(threadFactory),\n          std::move(opt)) {}\n\nCPUThreadPoolExecutor::CPUThreadPoolExecutor(\n    std::pair<size_t, size_t> numThreads,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    Options opt)\n    : CPUThreadPoolExecutor(\n          numThreads,\n          makeDefaultQueue(),\n          std::move(threadFactory),\n          std::move(opt)) {}\n\nCPUThreadPoolExecutor::CPUThreadPoolExecutor(size_t numThreads, Options opt)\n    : CPUThreadPoolExecutor(\n          numThreads,\n          std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"),\n          std::move(opt)) {}\n\nCPUThreadPoolExecutor::CPUThreadPoolExecutor(\n    size_t numThreads,\n    int8_t numPriorities,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    Options opt)\n    : CPUThreadPoolExecutor(\n          numThreads,\n          makeDefaultPriorityQueue(numPriorities),\n          std::move(threadFactory),\n          std::move(opt)) {}\n\nCPUThreadPoolExecutor::CPUThreadPoolExecutor(\n    size_t numThreads,\n    int8_t numPriorities,\n    size_t maxQueueSize,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    Options opt)\n    : CPUThreadPoolExecutor(\n          numThreads,\n          std::make_unique<PriorityLifoSemMPMCQueue<CPUTask>>(\n              numPriorities, maxQueueSize),\n          std::move(threadFactory),\n          std::move(opt)) {}\n\nCPUThreadPoolExecutor::~CPUThreadPoolExecutor() {\n  deregisterThreadPoolExecutor(this);\n  stop();\n  CHECK(threadsToStop_ == 0);\n  if (getNumPriorities() == 1) {\n    delete queueObservers_[0];\n  } else {\n    for (auto& observer : queueObservers_) {\n      delete observer.load(std::memory_order_relaxed);\n    }\n  }\n}\n\nQueueObserver* FOLLY_NULLABLE\nCPUThreadPoolExecutor::getQueueObserver(int8_t pri) {\n  if (!queueObserverFactory_) {\n    return nullptr;\n  }\n\n  auto& slot = queueObservers_[folly::to_unsigned(pri)];\n  if (auto observer = slot.load(std::memory_order_acquire)) {\n    return observer;\n  }\n\n  // common case is only one queue, need only one observer\n  if (getNumPriorities() == 1 && pri != 0) {\n    auto sharedObserver = getQueueObserver(0);\n    slot.store(sharedObserver, std::memory_order_release);\n    return sharedObserver;\n  }\n  QueueObserver* existingObserver = nullptr;\n  auto newObserver = queueObserverFactory_->create(pri);\n  if (!slot.compare_exchange_strong(existingObserver, newObserver.get())) {\n    return existingObserver;\n  } else {\n    return newObserver.release();\n  }\n}\n\nvoid CPUThreadPoolExecutor::add(Func func) {\n  add(std::move(func), std::chrono::milliseconds(0));\n}\n\nvoid CPUThreadPoolExecutor::add(\n    Func func, std::chrono::milliseconds expiration, Func expireCallback) {\n  CPUTask task(std::move(func), expiration, std::move(expireCallback), 0);\n  addImpl(\n      [this](auto&& t) { return taskQueue_->add(std::move(t)); },\n      std::move(task));\n}\n\nvoid CPUThreadPoolExecutor::addWithPriority(Func func, int8_t priority) {\n  add(std::move(func), priority, std::chrono::milliseconds(0));\n}\n\nvoid CPUThreadPoolExecutor::add(\n    Func func,\n    int8_t priority,\n    std::chrono::milliseconds expiration,\n    Func expireCallback) {\n  CHECK_GT(getNumPriorities(), 0);\n  CPUTask task(\n      std::move(func), expiration, std::move(expireCallback), priority);\n  addImpl(\n      [this](auto&& t) {\n        auto pri = t.priority();\n        return taskQueue_->addWithPriority(std::move(t), pri);\n      },\n      std::move(task));\n}\n\nuint8_t CPUThreadPoolExecutor::getNumPriorities() const {\n  return taskQueue_->getNumPriorities();\n}\n\nsize_t CPUThreadPoolExecutor::getTaskQueueSize() const {\n  return taskQueue_->size();\n}\n\nWorkerProvider* CPUThreadPoolExecutor::getThreadIdCollector() {\n  return threadIdCollector_.get();\n}\n\nBlockingQueue<CPUThreadPoolExecutor::CPUTask>*\nCPUThreadPoolExecutor::getTaskQueue() {\n  return taskQueue_.get();\n}\n\n// Does not need threadListLock_ lock.\nbool CPUThreadPoolExecutor::shouldStopThread(bool isPoison) {\n  auto threadsToStop = threadsToStop_.load(std::memory_order_relaxed);\n  do {\n    if (threadsToStop == 0 ||\n        // If we're joining, do not allow early stopping: only stop threads when\n        // a poison task is received.\n        (!isPoison && isJoin_.load(std::memory_order_acquire))) {\n      return false;\n    }\n  } while (!threadsToStop_.compare_exchange_weak(\n      threadsToStop, threadsToStop - 1, std::memory_order_relaxed));\n  return true;\n}\n\n// threadListLock_ must be writelocked.\nvoid CPUThreadPoolExecutor::stopThread(const ThreadPtr& thread) {\n  for (auto& o : observers_) {\n    o->threadStopped(thread.get());\n  }\n  stoppedThreadProcessedTasks_ += thread->processedTasks;\n  thread->processedTasks = 0;\n  threadList_.remove(thread);\n  stoppedThreads_.add(folly::copy(thread));\n}\n\nvoid CPUThreadPoolExecutor::threadRun(ThreadPtr thread) {\n  this->threadPoolHook_.registerThread();\n  folly::Optional<ExecutorBlockingGuard> guard; // optional until C++17\n  if (prohibitBlockingOnThreadPools_ == Options::Blocking::prohibit) {\n    guard.emplace(ExecutorBlockingGuard::ProhibitTag{}, this, getName());\n  } else {\n    guard.emplace(ExecutorBlockingGuard::TrackTag{}, this, getName());\n  }\n\n  thread->startupBaton.post();\n  threadIdCollector_->addTid(folly::getOSThreadID());\n  // On thread exit, we should remove the thread ID from the tracking list.\n  auto threadIDsGuard = folly::makeGuard([this]() {\n    // The observer could be capturing a stack trace from this thread\n    // so it should block until the collection finishes to exit.\n    threadIdCollector_->removeTid(folly::getOSThreadID());\n  });\n  while (true) {\n    auto task =\n        (threadsCanTimeout_.load(std::memory_order_relaxed) ||\n         FLAGS_folly_cputhreadpoolexecutor_always_dequeue_with_timeout)\n        ? taskQueue_->try_take_for(\n              threadTimeout_.load(std::memory_order_relaxed))\n        : taskQueue_->take();\n\n    // Handle thread stopping, either by task timeout, or by 'poison' task added\n    // by stopThreads().\n    if (bool timeout = !task; FOLLY_UNLIKELY(timeout || !task->func_)) {\n      std::unique_lock w{threadListLock_};\n      if (shouldStopThread(/* isPoison */ !timeout) ||\n          (timeout && tryTimeoutThread())) {\n        stopThread(thread);\n        return;\n      }\n      continue;\n    }\n\n    if (auto queueObserver = getQueueObserver(task->priority())) {\n      queueObserver->onDequeued(task->queueObserverPayload_);\n    }\n    runTask(thread, std::move(task.value()));\n\n    if (shouldStopThread(/* isPoison */ false)) {\n      std::unique_lock w{threadListLock_};\n      stopThread(thread);\n      return;\n    }\n  }\n}\n\nvoid CPUThreadPoolExecutor::stopThreads(size_t n) {\n  threadsToStop_ += n;\n  for (size_t i = 0; i < n; i++) {\n    taskQueue_->addWithPriority(CPUTask(), Executor::LO_PRI);\n  }\n}\n\n// threadListLock_ is read (or write) locked.\nsize_t CPUThreadPoolExecutor::getPendingTaskCountImpl() const {\n  return taskQueue_->size();\n}\n\nstd::unique_ptr<folly::QueueObserverFactory>\nCPUThreadPoolExecutor::createQueueObserverFactory() {\n  for (auto& observer : queueObservers_) {\n    observer.store(nullptr, std::memory_order_release);\n  }\n  return QueueObserverFactory::make(\n      \"cpu.\" + getName(),\n      taskQueue_->getNumPriorities(),\n      threadIdCollector_.get());\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/CPUThreadPoolExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <limits.h>\n\n#include <array>\n\n#include <folly/executors/QueueObserver.h>\n#include <folly/executors/ThreadPoolExecutor.h>\n\nFOLLY_GFLAGS_DECLARE_bool(dynamic_cputhreadpoolexecutor);\n\nnamespace folly {\n\n/**\n * A Thread pool for CPU bound tasks.\n *\n * @note A single queue backed by:\n * - An efficient semaphore, such as:\n *   - folly::LifoSem\n *   - folly::ThrottledlifoSem\n * * An efficient unbounded concurrent queue, such as:\n *   - folly::UMPMCQueue\n * Therefore, this thread pool scales to very high levels of concurrent access.\n *\n * @note If a bounded queue (folly::QueueBehaviorIfFull::BLOCK) is used, and\n * tasks executing on a given thread pool schedule more tasks, deadlock is\n * possible if the queue becomes full. Deadlock is also possible if there is\n * a circular dependency among multiple thread pools with blocking queues.\n * To avoid this situation, either use non-blocking queue(s) only (default and\n * recommended), or schedule tasks only from threads not belonging to the given\n * thread pool(s).\n *\n * @note LifoSem and ThrottledLifoSem wake up threads in LIFO order - i.e. there\n * are only ever as few threads as necessary actually running, and we always try\n * to reuse the same few threads for better cache locality. The other threads\n * are be suspended continuously until they are needed to handle spikes in work,\n * and their stacks would be madvised away while the threads are suspended.\n *\n * @note Supports priorities - priorities are implemented as multiple queues -\n * each worker thread checks the highest priority queue first. Threads\n * themselves don't have priorities set, so a series of long running low\n * priority tasks could still hog all the threads. (at last check pthreads\n * thread priorities didn't work very well).\n */\nclass CPUThreadPoolExecutor\n    : public ThreadPoolExecutor,\n      public GetThreadIdCollector {\n public:\n  struct CPUTask;\n  struct Options {\n    enum class Blocking {\n      prohibit,\n      allow,\n    };\n\n    constexpr Options() noexcept : blocking{Blocking::allow} {}\n\n    Options& setBlocking(Blocking b) {\n      blocking = b;\n      return *this;\n    }\n\n    Blocking blocking;\n  };\n\n  // These function return unbounded blocking queues with the default semaphore.\n  static std::unique_ptr<BlockingQueue<CPUTask>> makeDefaultQueue();\n  static std::unique_ptr<BlockingQueue<CPUTask>> makeDefaultPriorityQueue(\n      int8_t numPriorities);\n\n  // These function return unbounded blocking queues with LifoSem.\n  static std::unique_ptr<BlockingQueue<CPUTask>> makeLifoSemQueue();\n  static std::unique_ptr<BlockingQueue<CPUTask>> makeLifoSemPriorityQueue(\n      int8_t numPriorities);\n\n  // These function return unbounded blocking queues with ThrottledLifoSem.\n  static std::unique_ptr<BlockingQueue<CPUTask>> makeThrottledLifoSemQueue(\n      std::chrono::nanoseconds wakeUpInterval = {});\n  static std::unique_ptr<BlockingQueue<CPUTask>>\n  makeThrottledLifoSemPriorityQueue(\n      int8_t numPriorities, std::chrono::nanoseconds wakeUpInterval = {});\n\n  CPUThreadPoolExecutor(\n      size_t numThreads,\n      std::unique_ptr<BlockingQueue<CPUTask>> taskQueue,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"),\n      Options opt = {});\n\n  CPUThreadPoolExecutor(\n      std::pair<size_t, size_t> numThreads,\n      std::unique_ptr<BlockingQueue<CPUTask>> taskQueue,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"),\n      Options opt = {});\n\n  explicit CPUThreadPoolExecutor(size_t numThreads, Options opt = {});\n\n  CPUThreadPoolExecutor(\n      size_t numThreads,\n      std::shared_ptr<ThreadFactory> threadFactory,\n      Options opt = {});\n\n  explicit CPUThreadPoolExecutor(\n      std::pair<size_t, size_t> numThreads,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"),\n      Options opt = {});\n\n  CPUThreadPoolExecutor(\n      size_t numThreads,\n      int8_t numPriorities,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"),\n      Options opt = {});\n\n  CPUThreadPoolExecutor(\n      size_t numThreads,\n      int8_t numPriorities,\n      size_t maxQueueSize,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"),\n      Options opt = {});\n\n  ~CPUThreadPoolExecutor() override;\n\n  void add(Func func) override;\n  void add(\n      Func func,\n      std::chrono::milliseconds expiration,\n      Func expireCallback = nullptr) override;\n\n  void addWithPriority(Func func, int8_t priority) override;\n  virtual void add(\n      Func func,\n      int8_t priority,\n      std::chrono::milliseconds expiration,\n      Func expireCallback = nullptr);\n\n  size_t getTaskQueueSize() const;\n\n  uint8_t getNumPriorities() const override;\n\n  /// Implements the GetThreadIdCollector interface\n  WorkerProvider* FOLLY_NULLABLE getThreadIdCollector() override;\n\n  struct CPUTask : public ThreadPoolExecutor::Task {\n    CPUTask(); // Poison.\n    CPUTask(\n        Func&& f,\n        std::chrono::milliseconds expiration,\n        Func&& expireCallback,\n        int8_t pri);\n\n   private:\n    friend class CPUThreadPoolExecutor;\n\n    intptr_t queueObserverPayload_;\n  };\n\n  static const size_t kDefaultMaxQueueSize;\n\n protected:\n  BlockingQueue<CPUTask>* FOLLY_NONNULL getTaskQueue();\n  template <typename EnqueueTask>\n  void addImpl(EnqueueTask&& enqueueTask, CPUTask&& task);\n\n  std::unique_ptr<ThreadIdWorkerProvider> threadIdCollector_{\n      std::make_unique<ThreadIdWorkerProvider>()};\n\n private:\n  void threadRun(ThreadPtr thread) override;\n  void stopThreads(size_t n) override;\n  size_t getPendingTaskCountImpl() const override final;\n\n  bool shouldStopThread(bool isPoison);\n  void stopThread(const ThreadPtr& thread);\n\n  std::unique_ptr<folly::QueueObserverFactory> createQueueObserverFactory();\n  QueueObserver* FOLLY_NULLABLE getQueueObserver(int8_t pri);\n\n  std::unique_ptr<BlockingQueue<CPUTask>> taskQueue_;\n  // It is possible to have as many detectors as there are priorities,\n  std::array<std::atomic<folly::QueueObserver*>, UCHAR_MAX + 1> queueObservers_;\n  std::unique_ptr<folly::QueueObserverFactory> queueObserverFactory_{\n      createQueueObserverFactory()};\n  std::atomic<size_t> threadsToStop_{0};\n  Options::Blocking prohibitBlockingOnThreadPools_ = Options::Blocking::allow;\n};\n\ntemplate <typename EnqueueTask>\nvoid CPUThreadPoolExecutor::addImpl(EnqueueTask&& enqueueTask, CPUTask&& task) {\n  if (!task.func_) {\n    // Reserve empty funcs as poison by logging the error inline.\n    invokeCatchingExns(\"ThreadPoolExecutor: func\", std::move(task.func_));\n    return;\n  }\n\n  if (auto queueObserver = getQueueObserver(task.priority())) {\n    task.queueObserverPayload_ = queueObserver->onEnqueued(task.context_.get());\n  }\n  registerTaskEnqueue(task);\n\n  // It's not safe to expect that the executor is alive after a task is added to\n  // the queue (this task could be holding the last KeepAlive and when finished\n  // - it may unblock the executor shutdown).\n  // If we need executor to be alive after adding into the queue, we have to\n  // acquire a KeepAlive.\n  bool mayNeedToAddThreads = minThreads_.load(std::memory_order_relaxed) == 0 ||\n      activeThreads_.load(std::memory_order_relaxed) <\n          maxThreads_.load(std::memory_order_relaxed);\n  folly::Executor::KeepAlive<> ka = mayNeedToAddThreads\n      ? getKeepAliveToken(this)\n      : folly::Executor::KeepAlive<>{};\n\n  auto result = enqueueTask(std::move(task));\n\n  if (mayNeedToAddThreads && !result.reusedThread) {\n    ensureActiveThreads();\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/Codel.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/Codel.h>\n\n#include <algorithm>\n#include <stdexcept>\n\n#include <folly/portability/GFlags.h>\n\nFOLLY_GFLAGS_DEFINE_int32(\n    codel_interval, 100, \"Codel default interval time in ms\");\nFOLLY_GFLAGS_DEFINE_int32(\n    codel_target_delay, 5, \"Target codel queueing delay in ms\");\n\nusing namespace std::chrono;\n\nnamespace folly {\n\nCodel::Codel()\n    : Codel(\n          Codel::Options()\n              .setInterval(milliseconds(FLAGS_codel_interval))\n              .setTargetDelay(milliseconds(FLAGS_codel_target_delay))) {}\n\nCodel::Codel(const Options& options)\n    : codelMinDelayNs_(0),\n      codelIntervalTimeNs_(\n          duration_cast<nanoseconds>(steady_clock::now().time_since_epoch())\n              .count()),\n      targetDelay_(options.targetDelay()),\n      interval_(options.interval()),\n      codelResetDelay_(true),\n      overloaded_(false) {}\n\nbool Codel::overloaded_explicit_now(\n    nanoseconds delay, steady_clock::time_point now) {\n  bool ret = false;\n\n  // Avoid another thread updating the value at the same time we are using it\n  // to calculate the overloaded state\n  auto minDelay = nanoseconds(codelMinDelayNs_);\n  // Get a snapshot of the parameters to determine overload condition\n  auto opts = getOptions();\n  auto sloughTimeout = getSloughTimeout(opts.targetDelay());\n\n  if (now > steady_clock::time_point(nanoseconds(codelIntervalTimeNs_)) &&\n      // testing before exchanging is more cacheline-friendly\n      (!codelResetDelay_.load(std::memory_order_acquire) &&\n       !codelResetDelay_.exchange(true))) {\n    codelIntervalTimeNs_ =\n        duration_cast<nanoseconds>((now + opts.interval()).time_since_epoch())\n            .count();\n\n    if (minDelay > opts.targetDelay()) {\n      overloaded_ = true;\n    } else {\n      overloaded_ = false;\n    }\n  }\n  // Care must be taken that only a single thread resets codelMinDelay_,\n  // and that it happens after the interval reset above\n  if (codelResetDelay_.load(std::memory_order_acquire) &&\n      codelResetDelay_.exchange(false)) {\n    codelMinDelayNs_ = delay.count();\n    // More than one request must come in during an interval before codel\n    // starts dropping requests\n    return false;\n  } else if (delay < nanoseconds(codelMinDelayNs_)) {\n    codelMinDelayNs_ = delay.count();\n  }\n\n  // Here is where we apply different logic than codel proper. Instead of\n  // adapting the interval until the next drop, we slough off requests with\n  // queueing delay > 2*target_delay while in the overloaded regime. This\n  // empirically works better for our services than the codel approach of\n  // increasingly often dropping packets.\n  if (overloaded_ && delay > sloughTimeout) {\n    ret = true;\n  }\n\n  return ret;\n}\n\nint Codel::getLoad() {\n  // it might be better to use the average delay instead of minDelay, but we'd\n  // have to track it. aspiring bootcamper?\n  auto opts = getOptions();\n  return std::min<int>(\n      100, 100 * getMinDelay() / getSloughTimeout(opts.targetDelay()));\n}\n\nvoid Codel::setOptions(Options const& options) {\n  // Carry out some basic sanity checks.\n  auto delay = options.targetDelay();\n  auto interval = options.interval();\n\n  if (interval <= delay || delay <= milliseconds::zero() ||\n      interval <= milliseconds::zero()) {\n    throw std::invalid_argument(\"Invalid arguments provided\");\n  }\n  interval_.store(interval, std::memory_order_relaxed);\n  targetDelay_.store(delay, std::memory_order_relaxed);\n}\n\nconst Codel::Options Codel::getOptions() const {\n  auto interval = interval_.load(std::memory_order_relaxed);\n  auto delay = targetDelay_.load(std::memory_order_relaxed);\n  // Enforcing the invariant that targetDelay <= interval. A violation could\n  // potentially occur if either parameter was updated by another concurrent\n  // thread via the setOptions() method.\n  delay = std::min(delay, interval);\n\n  return Codel::Options().setTargetDelay(delay).setInterval(interval);\n}\n\nnanoseconds Codel::getMinDelay() {\n  return nanoseconds(codelMinDelayNs_);\n}\n\nsteady_clock::time_point Codel::getIntervalTime() {\n  return steady_clock::time_point(nanoseconds(codelIntervalTimeNs_));\n}\n\nmilliseconds Codel::getSloughTimeout(milliseconds delay) const {\n  return delay * 2;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/Codel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <chrono>\n\nnamespace folly {\n\n/// CoDel (controlled delay) is an active queue management algorithm from\n/// networking for battling bufferbloat.\n///\n/// Services also have queues (of requests, not packets) and suffer from\n/// queueing delay when overloaded. This class adapts the codel algorithm for\n/// services.\n///\n/// Codel is discussed in depth on the web [1,2], but a basic sketch of the\n/// algorithm is this: if every request has experienced queueing delay greater\n/// than the target (5ms) during the past interval (100ms), then we shed load.\n///\n/// We have adapted the codel algorithm. TCP sheds load by changing windows in\n/// reaction to dropped packets. Codel in a network setting drops packets at\n/// increasingly shorter intervals (100 / sqrt(n)) to achieve a linear change\n/// in throughput. In our experience a different scheme works better for\n/// services: when overloaded slough off requests that we dequeue which have\n/// exceeded an alternate timeout (2 * target_delay).\n///\n/// So in summary, to use this class, calculate the time each request spent in\n/// the queue and feed that delay to overloaded(), which will tell you whether\n/// to expire this request.\n///\n/// You can also ask for an instantaneous load estimate and the minimum delay\n/// observed during this interval.\n///\n///\n/// 1. http://queue.acm.org/detail.cfm?id=2209336\n/// 2. https://en.wikipedia.org/wiki/CoDel\nclass Codel {\n public:\n  class Options {\n   public:\n    std::chrono::milliseconds interval() const { return interval_; }\n\n    Options& setInterval(std::chrono::milliseconds value) {\n      interval_ = value;\n      return *this;\n    }\n\n    std::chrono::milliseconds targetDelay() const { return targetDelay_; }\n\n    Options& setTargetDelay(std::chrono::milliseconds value) {\n      targetDelay_ = value;\n      return *this;\n    }\n\n   private:\n    std::chrono::milliseconds interval_;\n    std::chrono::milliseconds targetDelay_;\n  };\n\n  Codel();\n\n  /// Preferable construction method\n  explicit Codel(const Options& options);\n\n  /// Returns true if this request should be expired to reduce overload.\n  /// In detail, this returns true if min_delay > target_delay for the\n  /// interval, and this delay > 2 * target_delay.\n  ///\n  /// As you may guess, we observe the clock so this is time sensitive. Call\n  /// it promptly after calculating queueing delay.\n  bool overloaded(std::chrono::nanoseconds delay) {\n    return overloaded_explicit_now(delay, std::chrono::steady_clock::now());\n  }\n  bool overloaded_explicit_now(\n      std::chrono::nanoseconds delay,\n      std::chrono::steady_clock::time_point now);\n\n  /// Get the queue load, as seen by the codel algorithm\n  /// Gives a rough guess at how bad the queue delay is.\n  ///\n  ///   min(100%, min_delay / (2 * target_delay))\n  ///\n  /// Return:  0 = no delay, 100 = At the queueing limit\n  int getLoad();\n\n  /// Update the target delay and interval parameters by passing them\n  /// in as an Options instance. Note that target delay must be strictly\n  /// smaller than the interval. This is a no-op if invalid arguments are\n  /// provided.\n  ///\n  /// NOTE : Calls to setOptions must be externally synchronized since there\n  /// is no internal locking for parameter updates. Codel only guarantees\n  /// internal synchronization between calls to getOptions() and other members\n  /// but not between concurrent calls to getOptions().\n  ///\n  /// Throws std::runtime_error if arguments are invalid.\n  void setOptions(Options const& options);\n\n  /// Return a consistent snapshot of the two parameters used by Codel. Since\n  /// parameters may be updated with the setOptions() method provided above,\n  /// it is necessary to ensure that reads of the parameters return a consistent\n  /// pair in which the invariant of targetDelay <= interval is guaranteed; the\n  /// targetDelay value that is returned is the minimum of targetDelay and\n  /// interval.\n  const Options getOptions() const;\n\n  std::chrono::nanoseconds getMinDelay();\n  std::chrono::steady_clock::time_point getIntervalTime();\n\n  /// Returns the timeout condition for overload given a target delay period.\n  std::chrono::milliseconds getSloughTimeout(\n      std::chrono::milliseconds delay) const;\n\n private:\n  std::atomic<uint64_t> codelMinDelayNs_;\n  std::atomic<uint64_t> codelIntervalTimeNs_;\n\n  std::atomic<std::chrono::milliseconds> targetDelay_;\n  std::atomic<std::chrono::milliseconds> interval_;\n\n  // flag to make overloaded() thread-safe, since we only want\n  // to reset the delay once per time period\n  std::atomic<bool> codelResetDelay_;\n\n  std::atomic<bool> overloaded_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/DrivableExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n\nnamespace folly {\n\n/*\n * A DrivableExecutor can be driven via its drive() method\n * Examples include EventBase (via loopOnce()) and ManualExecutor\n * (via makeProgress()).\n *\n * This interface is most handy in conjunction with\n * Future<T>::getVia(DrivableExecutor*) and\n * Future<T>::waitVia(DrivableExecutor*)\n *\n * These call drive() * repeatedly until the Future is fulfilled.\n * getVia() returns the value (or throws the exception) and waitVia() returns\n * the same Future for chainability.\n *\n * These will be most helpful in tests, for instance if you need to pump a mock\n * EventBase until Futures complete.\n */\n\nclass DrivableExecutor : public virtual Executor {\n public:\n  ~DrivableExecutor() override = default;\n\n  // Make progress on this Executor's work.\n  //\n  // Drive *must not* busy wait if there is no work to do.  Instead,\n  // sleep (using a semaphore or similar) until at least one event is\n  // processed.\n  // I.e. make_future().via(foo).then(...).getVia(DrivableExecutor)\n  // must not spin, even though nothing happens on the drivable\n  // executor.\n  virtual void drive() = 0;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/EDFThreadPoolExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/EDFThreadPoolExecutor.h>\n\n#include <algorithm>\n#include <array>\n#include <atomic>\n#include <chrono>\n#include <cstddef>\n#include <exception>\n#include <limits>\n#include <memory>\n#include <queue>\n#include <utility>\n#include <vector>\n\n#include <glog/logging.h>\n#include <folly/concurrency/ProcessLocalUniqueId.h>\n#include <folly/portability/GFlags.h>\n#include <folly/synchronization/LifoSem.h>\n#include <folly/synchronization/ThrottledLifoSem.h>\n#include <folly/tracing/StaticTracepoint.h>\n\nFOLLY_GFLAGS_DEFINE_bool(\n    folly_edfthreadpoolexecutor_use_throttled_lifo_sem,\n    true,\n    \"EDFThreadPoolExecutor will use ThrottledLifoSem by default\");\n\nnamespace folly {\n\nclass EDFThreadPoolExecutor::Task {\n public:\n  struct Poison {};\n\n  explicit Task(Func&& f, uint64_t deadline)\n      : f_(std::move(f)), total_(1), deadline_(deadline) {}\n\n  explicit Task(std::vector<Func>&& fs, uint64_t deadline)\n      : fs_(std::move(fs)), total_(fs_.size()), deadline_(deadline) {}\n\n  explicit Task(Poison, int total) : total_(total), deadline_(kLatestDeadline) {\n    CHECK_GT(total, 0);\n  }\n\n  uint64_t getDeadline() const { return deadline_; }\n  uint64_t getEnqueueOrder() const { return enqueueOrder_; }\n\n  bool isPoison() const { return !f_ && fs_.empty() && total_ > 0; }\n\n  bool isDone() const {\n    return iter_.load(std::memory_order_relaxed) >= total_;\n  }\n\n  // Cannot be set in the ctor because known only after acquiring the lock.\n  void setEnqueueOrder(uint64_t enqueueOrder) { enqueueOrder_ = enqueueOrder; }\n\n  int next() {\n    if (isDone()) {\n      return -1;\n    }\n\n    int result = iter_.fetch_add(1, std::memory_order_relaxed);\n    return result < total_ ? result : -1;\n  }\n\n  void run(int i) {\n    folly::RequestContextScopeGuard guard(context_);\n    if (f_) {\n      f_();\n      if (i >= total_ - 1) {\n        std::exchange(f_, nullptr);\n      }\n    } else {\n      DCHECK(0 <= i && i < total_);\n      fs_[i]();\n      std::exchange(fs_[i], nullptr);\n    }\n  }\n\n  Func f_;\n  std::vector<Func> fs_;\n  std::atomic<int> iter_{0};\n  int total_;\n  uint64_t deadline_;\n  uint64_t enqueueOrder_ = 0;\n  std::shared_ptr<RequestContext> context_ = RequestContext::saveContext();\n  std::chrono::steady_clock::time_point enqueueTime_ =\n      std::chrono::steady_clock::now();\n  uint64_t taskId_ = processLocalUniqueId();\n};\n\nclass EDFThreadPoolExecutor::TaskQueue {\n public:\n  using TaskPtr = std::shared_ptr<Task>;\n\n  // This is not a `Synchronized` because we perform a few \"peek\" operations.\n  struct Bucket {\n    mutable SharedMutex mutex;\n\n    struct Compare {\n      bool operator()(const TaskPtr& lhs, const TaskPtr& rhs) const {\n        if (lhs->getDeadline() != rhs->getDeadline()) {\n          return lhs->getDeadline() > rhs->getDeadline();\n        }\n        return lhs->getEnqueueOrder() > rhs->getEnqueueOrder();\n      }\n    };\n\n    std::priority_queue<TaskPtr, std::vector<TaskPtr>, Compare> tasks;\n    std::atomic<bool> empty{true};\n    uint64_t enqueued = 0;\n  };\n\n  static constexpr std::size_t kNumBuckets = 2 << 5;\n\n  void push(TaskPtr task) {\n    auto deadline = task->getDeadline();\n    auto& bucket = getBucket(deadline);\n    {\n      std::unique_lock guard(bucket.mutex);\n      task->setEnqueueOrder(bucket.enqueued++);\n      bucket.tasks.push(std::move(task));\n      bucket.empty.store(bucket.tasks.empty(), std::memory_order_relaxed);\n    }\n\n    // Update current earliest deadline if necessary\n    uint64_t curDeadline = curDeadline_.load(std::memory_order_relaxed);\n    do {\n      if (curDeadline <= deadline) {\n        break;\n      }\n    } while (!curDeadline_.compare_exchange_weak(\n        curDeadline, deadline, std::memory_order_relaxed));\n  }\n\n  // Should only be called on a nonempty queue.\n  TaskPtr pop() {\n    bool needDeadlineUpdate = false;\n    for (;;) {\n      auto curDeadline = curDeadline_.load(std::memory_order_relaxed);\n      auto& bucket = getBucket(curDeadline);\n\n      if (needDeadlineUpdate || bucket.empty.load(std::memory_order_relaxed)) {\n        // Try setting the next earliest deadline. However no need to\n        // enforce as there might be insertion happening.\n        // If there is no next deadline, we set deadline to `kLatestDeadline`.\n        curDeadline_.compare_exchange_weak(\n            curDeadline,\n            findNextDeadline(curDeadline),\n            std::memory_order_relaxed);\n        needDeadlineUpdate = false;\n        continue;\n      }\n\n      {\n        // Fast path. Take bucket reader lock.\n        std::shared_lock guard(bucket.mutex);\n        if (bucket.tasks.empty()) {\n          continue;\n        }\n        const auto& task = bucket.tasks.top();\n        if (!task->isDone() && task->getDeadline() == curDeadline) {\n          return task;\n        }\n        // If the task is finished already, fall through to remove it.\n      }\n\n      {\n        // Take the writer lock to clean up the finished task.\n        std::unique_lock guard(bucket.mutex);\n        if (bucket.tasks.empty()) {\n          continue;\n        }\n        const auto& task = bucket.tasks.top();\n        if (task->isDone()) {\n          // Current task finished. Remove from the queue.\n          bucket.tasks.pop();\n          bucket.empty.store(bucket.tasks.empty(), std::memory_order_relaxed);\n        }\n      }\n\n      // We may have finished processing the current task / bucket. Going back\n      // to the beginning of the loop to find the next bucket.\n      needDeadlineUpdate = true;\n    }\n  }\n\n private:\n  Bucket& getBucket(uint64_t deadline) {\n    return buckets_[deadline % kNumBuckets];\n  }\n\n  uint64_t findNextDeadline(uint64_t prevDeadline) {\n    auto begin = prevDeadline % kNumBuckets;\n\n    uint64_t earliestDeadline = kLatestDeadline;\n    for (std::size_t i = 0; i < kNumBuckets; ++i) {\n      auto& bucket = buckets_[(begin + i) % kNumBuckets];\n\n      // Peek without locking first.\n      if (bucket.empty.load(std::memory_order_relaxed)) {\n        continue;\n      }\n\n      std::shared_lock guard(bucket.mutex);\n      auto curDeadline = curDeadline_.load(std::memory_order_relaxed);\n      if (prevDeadline != curDeadline) {\n        // Bail out early if something already happened\n        return curDeadline;\n      }\n\n      // Verify again after locking\n      if (bucket.tasks.empty()) {\n        continue;\n      }\n\n      const auto& task = bucket.tasks.top();\n      auto deadline = task->getDeadline();\n\n      if (deadline < earliestDeadline) {\n        earliestDeadline = deadline;\n      }\n\n      if ((deadline <= prevDeadline) ||\n          (deadline - prevDeadline < kNumBuckets)) {\n        // Found the next highest priority, or new tasks were added.\n        // No need to scan anymore.\n        break;\n      }\n    }\n\n    return earliestDeadline;\n  }\n\n  std::array<Bucket, kNumBuckets> buckets_;\n  std::atomic<uint64_t> curDeadline_ = kLatestDeadline;\n};\n\n/* static */ std::unique_ptr<EDFThreadPoolSemaphore>\nEDFThreadPoolExecutor::makeDefaultSemaphore() {\n  return FLAGS_folly_edfthreadpoolexecutor_use_throttled_lifo_sem\n      ? makeThrottledLifoSemSemaphore()\n      : makeLifoSemSemaphore();\n}\n\n/* static */ std::unique_ptr<EDFThreadPoolSemaphore>\nEDFThreadPoolExecutor::makeLifoSemSemaphore() {\n  return std::make_unique<EDFThreadPoolSemaphoreImpl<LifoSem>>();\n}\n\n/* static */ std::unique_ptr<EDFThreadPoolSemaphore>\nEDFThreadPoolExecutor::makeThrottledLifoSemSemaphore(\n    std::chrono::nanoseconds wakeUpInterval) {\n  ThrottledLifoSem::Options opts;\n  opts.wakeUpInterval = wakeUpInterval;\n  return std::make_unique<EDFThreadPoolSemaphoreImpl<ThrottledLifoSem>>(opts);\n}\n\nEDFThreadPoolExecutor::EDFThreadPoolExecutor(\n    std::size_t numThreads,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    std::unique_ptr<EDFThreadPoolSemaphore> semaphore)\n    : ThreadPoolExecutor(numThreads, numThreads, std::move(threadFactory)),\n      taskQueue_(std::make_unique<TaskQueue>()),\n      sem_(std::move(semaphore)) {\n  setNumThreads(numThreads);\n  registerThreadPoolExecutor(this);\n}\n\nEDFThreadPoolExecutor::~EDFThreadPoolExecutor() {\n  deregisterThreadPoolExecutor(this);\n  stop();\n}\n\nvoid EDFThreadPoolExecutor::add(Func f) {\n  add(std::move(f), kLatestDeadline);\n}\n\nvoid EDFThreadPoolExecutor::add(Func f, uint64_t deadline) {\n  auto task = std::make_shared<Task>(std::move(f), deadline);\n  registerTaskEnqueue(*task);\n  taskQueue_->push(std::move(task));\n  sem_->post(1);\n}\n\nvoid EDFThreadPoolExecutor::add(std::vector<Func> fs, uint64_t deadline) {\n  if (FOLLY_UNLIKELY(fs.empty())) {\n    return;\n  }\n\n  auto total = fs.size();\n  auto task = std::make_shared<Task>(std::move(fs), deadline);\n  registerTaskEnqueue(*task);\n  taskQueue_->push(std::move(task));\n  sem_->post(static_cast<uint32_t>(total));\n}\n\nsize_t EDFThreadPoolExecutor::getTaskQueueSize() const {\n  return sem_->valueGuess();\n}\n\nbool EDFThreadPoolExecutor::tryStopThread(\n    const ThreadPtr& thread, bool isPoison) {\n  auto threadsToStop = threadsToStop_.load(std::memory_order_relaxed);\n  do {\n    if (threadsToStop == 0 || (!isPoison && isJoin_)) {\n      return false;\n    }\n  } while (!threadsToStop_.compare_exchange_weak(\n      threadsToStop, threadsToStop - 1, std::memory_order_relaxed));\n\n  std::unique_lock w{threadListLock_};\n  for (auto& o : observers_) {\n    o->threadStopped(thread.get());\n  }\n  stoppedThreadProcessedTasks_ += thread->processedTasks;\n  thread->processedTasks = 0;\n  threadList_.remove(thread);\n  stoppedThreads_.add(folly::copy(thread));\n  return true;\n}\n\nvoid EDFThreadPoolExecutor::threadRun(ThreadPtr thread) {\n  this->threadPoolHook_.registerThread();\n  ExecutorBlockingGuard guard{\n      ExecutorBlockingGuard::TrackTag{}, this, getName()};\n\n  thread->startupBaton.post();\n  for (;;) {\n    sem_->wait();\n\n    // We consumed a post so the queue is non-empty, but we need to discard\n    // finished tasks.\n    int iter;\n    std::shared_ptr<EDFThreadPoolExecutor::Task> task;\n    do {\n      task = taskQueue_->pop();\n      iter = task->next();\n    } while (iter < 0);\n\n    if (task->isPoison()) {\n      if (tryStopThread(thread, /* isPoison */ true)) {\n        return;\n      } else {\n        continue; // Poison was consumed early by tryStopThread().\n      }\n    }\n\n    thread->idle.store(false, std::memory_order_relaxed);\n    auto startTime = std::chrono::steady_clock::now();\n    ProcessedTaskInfo taskInfo;\n    fillTaskInfo(*task, taskInfo);\n    taskInfo.waitTime = startTime - taskInfo.enqueueTime;\n    FOLLY_SDT(\n        folly,\n        thread_pool_executor_task_dequeued,\n        threadFactory_->getNamePrefix().c_str(),\n        taskInfo.requestId,\n        taskInfo.enqueueTime.time_since_epoch().count(),\n        taskInfo.waitTime.count(),\n        taskInfo.taskId);\n    forEachTaskObserver([&](auto& observer) {\n      observer.taskDequeued(taskInfo);\n    });\n\n    invokeCatchingExns(\"EDFThreadPoolExecutor: func\", [&] {\n      std::exchange(task, {})->run(iter);\n    });\n    taskInfo.runTime = std::chrono::steady_clock::now() - startTime;\n\n    FOLLY_SDT(\n        folly,\n        thread_pool_executor_task_taskInfo,\n        threadFactory_->getNamePrefix().c_str(),\n        taskInfo.requestId,\n        taskInfo.enqueueTime.time_since_epoch().count(),\n        taskInfo.waitTime.count(),\n        taskInfo.runTime.count(),\n        taskInfo.taskId);\n    forEachTaskObserver([&](auto& observer) {\n      observer.taskProcessed(taskInfo);\n    });\n\n    thread->processedTasks = thread->processedTasks + 1;\n\n    thread->idle.store(true, std::memory_order_relaxed);\n    thread->lastActiveTime.store(\n        std::chrono::steady_clock::now(), std::memory_order_relaxed);\n\n    if (tryStopThread(thread, /* isPoison */ false)) {\n      return;\n    }\n  }\n}\n\n// threadListLock_ is writelocked.\nvoid EDFThreadPoolExecutor::stopThreads(std::size_t numThreads) {\n  if (numThreads == 0) {\n    return;\n  }\n  threadsToStop_.fetch_add(numThreads, std::memory_order_relaxed);\n  taskQueue_->push(\n      std::make_shared<Task>(Task::Poison{}, static_cast<int>(numThreads)));\n  sem_->post(numThreads);\n}\n\n// threadListLock_ is read (or write) locked.\nstd::size_t EDFThreadPoolExecutor::getPendingTaskCountImpl() const {\n  return getTaskQueueSize();\n}\n\nvoid EDFThreadPoolExecutor::fillTaskInfo(const Task& task, TaskInfo& info) {\n  info.priority = 0; // Priorities are not supported.\n  if (task.context_) {\n    info.requestId = task.context_->getRootId();\n  }\n  info.enqueueTime = task.enqueueTime_;\n  info.taskId = task.taskId_;\n}\n\nvoid EDFThreadPoolExecutor::registerTaskEnqueue(const Task& task) {\n  TaskInfo info;\n  fillTaskInfo(task, info);\n  forEachTaskObserver([&](auto& observer) { observer.taskEnqueued(info); });\n  FOLLY_SDT(\n      folly,\n      thread_pool_executor_task_enqueued,\n      threadFactory_->getNamePrefix().c_str(),\n      info.requestId,\n      info.enqueueTime.time_since_epoch().count(),\n      info.taskId);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/EDFThreadPoolExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include <folly/executors/SoftRealTimeExecutor.h>\n#include <folly/executors/ThreadPoolExecutor.h>\n\nnamespace folly {\n\nclass EDFThreadPoolSemaphore {\n public:\n  virtual ~EDFThreadPoolSemaphore() = default;\n  virtual void post(uint32_t value) = 0;\n  virtual void wait() = 0;\n  virtual uint32_t valueGuess() const = 0;\n};\n\ntemplate <class Semaphore>\nclass EDFThreadPoolSemaphoreImpl : public EDFThreadPoolSemaphore {\n public:\n  template <class... Args>\n  explicit EDFThreadPoolSemaphoreImpl(Args&&... args)\n      : sem_(std::forward<Args>(args)...) {}\n\n  void post(uint32_t value) override { sem_.post(value); }\n  void wait() override { sem_.wait(); }\n  uint32_t valueGuess() const override { return sem_.valueGuess(); }\n\n private:\n  Semaphore sem_;\n};\n\n/**\n * `EDFThreadPoolExecutor` is a `SoftRealTimeExecutor` that implements the\n * earliest-deadline-first scheduling policy. Deadline ties are resolved by\n * submission order.\n */\nclass EDFThreadPoolExecutor\n    : public SoftRealTimeExecutor,\n      public ThreadPoolExecutor {\n public:\n  class Task;\n  class TaskQueue;\n\n  static constexpr uint64_t kEarliestDeadline = 0;\n  static constexpr uint64_t kLatestDeadline =\n      std::numeric_limits<uint64_t>::max();\n\n  static std::unique_ptr<EDFThreadPoolSemaphore> makeDefaultSemaphore();\n  static std::unique_ptr<EDFThreadPoolSemaphore> makeLifoSemSemaphore();\n  static std::unique_ptr<EDFThreadPoolSemaphore> makeThrottledLifoSemSemaphore(\n      std::chrono::nanoseconds wakeUpInterval = {});\n\n  explicit EDFThreadPoolExecutor(\n      std::size_t numThreads,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"EDFThreadPool\"),\n      std::unique_ptr<EDFThreadPoolSemaphore> semaphore =\n          makeDefaultSemaphore());\n\n  ~EDFThreadPoolExecutor() override;\n\n  using ThreadPoolExecutor::add;\n\n  void add(Func f) override;\n  void add(Func f, uint64_t deadline) override;\n  void add(std::vector<Func> fs, uint64_t deadline) override;\n\n  size_t getTaskQueueSize() const;\n\n protected:\n  void threadRun(ThreadPtr thread) override;\n  void stopThreads(std::size_t numThreads) override;\n  std::size_t getPendingTaskCountImpl() const override final;\n\n private:\n  bool tryStopThread(const ThreadPtr& thread, bool isPoison);\n\n  void fillTaskInfo(const Task& task, TaskInfo& info);\n  void registerTaskEnqueue(const Task& task);\n\n  std::unique_ptr<TaskQueue> taskQueue_;\n  std::unique_ptr<EDFThreadPoolSemaphore> sem_;\n  std::atomic<int> threadsToStop_{0};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ExecutionObserver.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ExecutionObserver.h>\n\n#include <folly/tracing/StaticTracepoint.h>\n\nnamespace folly {\n\nExecutionObserverScopeGuard::ExecutionObserverScopeGuard(\n    folly::ExecutionObserver::List* observerList,\n    void* id,\n    folly::ExecutionObserver::CallbackType callbackType)\n    : observerList_(observerList),\n      id_{reinterpret_cast<uintptr_t>(id)},\n      callbackType_(callbackType) {\n  FOLLY_SDT(\n      folly,\n      execution_observer_callbacks_starting,\n      id_,\n      static_cast<int>(callbackType_));\n  for (auto& observer : *observerList_) {\n    observer.starting(id_, callbackType_);\n  }\n}\n\nExecutionObserverScopeGuard::~ExecutionObserverScopeGuard() {\n  for (auto& observer : *observerList_) {\n    observer.stopped(id_, callbackType_);\n  }\n\n  FOLLY_SDT(\n      folly,\n      execution_observer_callbacks_stopped,\n      id_,\n      static_cast<int>(callbackType_));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ExecutionObserver.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n\n#include <boost/intrusive/list.hpp>\n\nnamespace folly {\n\n/**\n * Observes the execution of a task. Multiple execution observers can be chained\n * together. As a caveat, execution observers should not remove themselves from\n * the list of observers during execution\n */\nclass ExecutionObserver\n    : public boost::intrusive::list_base_hook<\n          boost::intrusive::link_mode<boost::intrusive::auto_unlink>> {\n public:\n  enum class CallbackType {\n    // Owned by EventBase.\n    Event,\n    Loop,\n    NotificationQueue,\n    // Owned by FiberManager.\n    Fiber,\n  };\n  // Constant time size = false to support auto_unlink behavior, options are\n  // mutually exclusive\n  using List = boost::intrusive::\n      list<ExecutionObserver, boost::intrusive::constant_time_size<false>>;\n\n  virtual ~ExecutionObserver() = default;\n\n  /**\n   * Called when a task is about to start executing.\n   *\n   * @param id Unique id for the task which is starting.\n   */\n  virtual void starting(uintptr_t id, CallbackType callbackType) noexcept = 0;\n\n  /**\n   * Called just after a task stops executing.\n   *\n   * @param id Unique id for the task which stopped.\n   */\n  virtual void stopped(uintptr_t id, CallbackType callbackType) noexcept = 0;\n};\n\nclass ExecutionObserverScopeGuard {\n public:\n  ExecutionObserverScopeGuard(\n      folly::ExecutionObserver::List* observerList,\n      void* id,\n      folly::ExecutionObserver::CallbackType callbackType);\n\n  ~ExecutionObserverScopeGuard();\n\n private:\n  folly::ExecutionObserver::List* observerList_;\n  uintptr_t id_;\n  folly::ExecutionObserver::CallbackType callbackType_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ExecutorWithPriority-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <glog/logging.h>\n\nnamespace folly {\nnamespace detail {\ntemplate <typename Callback>\nclass ExecutorWithPriorityImpl : public virtual Executor {\n public:\n  static Executor::KeepAlive<ExecutorWithPriorityImpl<std::decay_t<Callback>>>\n  create(Executor::KeepAlive<Executor> executor, Callback&& callback) {\n    return makeKeepAlive(new ExecutorWithPriorityImpl<std::decay_t<Callback>>(\n        executor, std::move(callback)));\n  }\n  ExecutorWithPriorityImpl(ExecutorWithPriorityImpl const&) = delete;\n  ExecutorWithPriorityImpl& operator=(ExecutorWithPriorityImpl const&) = delete;\n  ExecutorWithPriorityImpl(ExecutorWithPriorityImpl&&) = delete;\n  ExecutorWithPriorityImpl& operator=(ExecutorWithPriorityImpl&&) = delete;\n\n  void add(Func func) override {\n    int8_t priority = callback_();\n    executor_->addWithPriority(std::move(func), priority);\n  }\n\n protected:\n  bool keepAliveAcquire() noexcept override {\n    auto keepAliveCounter =\n        keepAliveCounter_.fetch_add(1, std::memory_order_relaxed);\n    DCHECK(keepAliveCounter > 0);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    auto keepAliveCounter =\n        keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel);\n    DCHECK(keepAliveCounter > 0);\n    if (keepAliveCounter == 1) {\n      delete this;\n    }\n  }\n\n private:\n  ExecutorWithPriorityImpl(\n      Executor::KeepAlive<Executor> executor, Callback&& callback)\n      : executor_(std::move(executor)), callback_(std::move(callback)) {}\n  std::atomic<int64_t> keepAliveCounter_{1};\n  Executor::KeepAlive<Executor> executor_;\n  Callback callback_;\n};\n} // namespace detail\n\ntemplate <typename Callback>\nExecutor::KeepAlive<> ExecutorWithPriority::createDynamic(\n    Executor::KeepAlive<Executor> executor, Callback&& callback) {\n  return detail::ExecutorWithPriorityImpl<std::decay_t<Callback>>::create(\n      executor, std::move(callback));\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ExecutorWithPriority.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ExecutorWithPriority.h>\n\nnamespace folly {\nExecutor::KeepAlive<> ExecutorWithPriority::create(\n    Executor::KeepAlive<Executor> executor, int8_t priority) {\n  return ExecutorWithPriority::createDynamic(executor, [priority]() {\n    return priority;\n  });\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ExecutorWithPriority.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/Executor.h>\n\nnamespace folly {\nclass ExecutorWithPriority {\n public:\n  template <typename Callback>\n  static Executor::KeepAlive<> createDynamic(\n      Executor::KeepAlive<Executor> executor, Callback&& callback);\n\n  static Executor::KeepAlive<> create(\n      Executor::KeepAlive<Executor> executor, int8_t priority);\n};\n} // namespace folly\n\n#include <folly/executors/ExecutorWithPriority-inl.h>\n"
  },
  {
    "path": "folly/executors/FiberIOExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/executors/IOExecutor.h>\n#include <folly/fibers/FiberManagerMap.h>\n\nnamespace folly {\n\n/**\n * @class FiberIOExecutor\n * @brief An IOExecutor that executes funcs under mapped fiber context\n *\n * A FiberIOExecutor wraps an IOExecutor, but executes funcs on the FiberManager\n * mapped to the underlying IOExector's event base.\n */\nclass FiberIOExecutor : public IOExecutor {\n public:\n  explicit FiberIOExecutor(\n      const std::shared_ptr<IOExecutor>& ioExecutor,\n      fibers::FiberManager::Options opts = fibers::FiberManager::Options())\n      : ioExecutor_(ioExecutor), options_(std::move(opts)) {}\n\n  virtual void add(folly::Function<void()> f) override {\n    auto eventBase = ioExecutor_->getEventBase();\n    folly::fibers::getFiberManager(*eventBase, options_).add(std::move(f));\n  }\n\n  virtual folly::EventBase* getEventBase() override {\n    return ioExecutor_->getEventBase();\n  }\n\n private:\n  std::shared_ptr<IOExecutor> ioExecutor_;\n  fibers::FiberManager::Options options_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/FunctionScheduler.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/FunctionScheduler.h>\n\n#include <random>\n\n#include <glog/logging.h>\n\n#include <folly/Conv.h>\n#include <folly/Random.h>\n#include <folly/String.h>\n#include <folly/system/ThreadName.h>\n\nusing std::chrono::microseconds;\nusing std::chrono::steady_clock;\n\nnamespace folly {\n\nnamespace {\n\nstruct ConsistentDelayFunctor {\n  const microseconds constInterval;\n\n  explicit ConsistentDelayFunctor(microseconds interval)\n      : constInterval(interval) {\n    if (interval < microseconds::zero()) {\n      throw std::invalid_argument(\n          \"FunctionScheduler: \"\n          \"time interval must be non-negative\");\n    }\n  }\n\n  steady_clock::time_point operator()(\n      steady_clock::time_point curNextRunTime,\n      steady_clock::time_point curTime) const {\n    auto intervalsPassed = (curTime - curNextRunTime) / constInterval;\n    return (intervalsPassed + 1) * constInterval + curNextRunTime;\n  }\n};\n\nstruct ConstIntervalFunctor {\n  const microseconds constInterval;\n\n  explicit ConstIntervalFunctor(microseconds interval)\n      : constInterval(interval) {\n    if (interval < microseconds::zero()) {\n      throw std::invalid_argument(\n          \"FunctionScheduler: \"\n          \"time interval must be non-negative\");\n    }\n  }\n\n  microseconds operator()() const { return constInterval; }\n};\n\nstruct PoissonDistributionFunctor {\n  std::default_random_engine generator;\n  std::poisson_distribution<microseconds::rep> poissonRandom;\n\n  explicit PoissonDistributionFunctor(microseconds meanPoissonUsec)\n      : poissonRandom(meanPoissonUsec.count()) {\n    if (meanPoissonUsec.count() < 0) {\n      throw std::invalid_argument(\n          \"FunctionScheduler: \"\n          \"Poisson mean interval must be non-negative\");\n    }\n  }\n\n  microseconds operator()() { return microseconds(poissonRandom(generator)); }\n};\n\nstruct UniformDistributionFunctor {\n  std::default_random_engine generator;\n  std::uniform_int_distribution<microseconds::rep> dist;\n\n  UniformDistributionFunctor(microseconds minInterval, microseconds maxInterval)\n      : generator(Random::rand32()),\n        dist(minInterval.count(), maxInterval.count()) {\n    if (minInterval > maxInterval) {\n      throw std::invalid_argument(\n          \"FunctionScheduler: \"\n          \"min time interval must be less or equal than max interval\");\n    }\n    if (minInterval < microseconds::zero()) {\n      throw std::invalid_argument(\n          \"FunctionScheduler: \"\n          \"time interval must be non-negative\");\n    }\n  }\n\n  microseconds operator()() { return microseconds(dist(generator)); }\n};\n\n} // namespace\n\nFunctionScheduler::FunctionScheduler() = default;\n\nFunctionScheduler::~FunctionScheduler() {\n  // make sure to stop the thread (if running)\n  shutdown();\n  clearHeap();\n}\n\nvoid FunctionScheduler::addFunction(\n    Function<void()>&& cb,\n    microseconds interval,\n    StringPiece nameID,\n    microseconds startDelay) {\n  addFunctionInternal(\n      std::move(cb),\n      ConstIntervalFunctor(interval),\n      nameID.str(),\n      to<std::string>(interval.count(), \"us\"),\n      startDelay,\n      false /*runOnce*/);\n}\n\nvoid FunctionScheduler::addFunction(\n    Function<void()>&& cb,\n    microseconds interval,\n    const LatencyDistribution& latencyDistr,\n    StringPiece nameID,\n    microseconds startDelay) {\n  if (latencyDistr.isPoisson) {\n    addFunctionInternal(\n        std::move(cb),\n        PoissonDistributionFunctor(latencyDistr.poissonMean),\n        nameID.str(),\n        to<std::string>(latencyDistr.poissonMean.count(), \"us (Poisson mean)\"),\n        startDelay,\n        false /*runOnce*/);\n  } else {\n    addFunction(std::move(cb), interval, nameID, startDelay);\n  }\n}\n\nvoid FunctionScheduler::addFunctionOnce(\n    Function<void()>&& cb, StringPiece nameID, microseconds startDelay) {\n  addFunctionInternal(\n      std::move(cb),\n      ConstIntervalFunctor(microseconds::zero()),\n      nameID.str(),\n      \"once\",\n      startDelay,\n      true /*runOnce*/);\n}\n\nvoid FunctionScheduler::addFunctionUniformDistribution(\n    Function<void()>&& cb,\n    microseconds minInterval,\n    microseconds maxInterval,\n    StringPiece nameID,\n    microseconds startDelay) {\n  addFunctionInternal(\n      std::move(cb),\n      UniformDistributionFunctor(minInterval, maxInterval),\n      nameID.str(),\n      to<std::string>(\n          \"[\", minInterval.count(), \" , \", maxInterval.count(), \"] us\"),\n      startDelay,\n      false /*runOnce*/);\n}\n\nvoid FunctionScheduler::addFunctionConsistentDelay(\n    Function<void()>&& cb,\n    microseconds interval,\n    StringPiece nameID,\n    microseconds startDelay) {\n  addFunctionInternal(\n      std::move(cb),\n      ConsistentDelayFunctor(interval),\n      nameID.str(),\n      to<std::string>(interval.count(), \"us\"),\n      startDelay,\n      false /*runOnce*/);\n}\n\nvoid FunctionScheduler::addFunctionGenericDistribution(\n    Function<void()>&& cb,\n    IntervalDistributionFunc&& intervalFunc,\n    const std::string& nameID,\n    const std::string& intervalDescr,\n    microseconds startDelay) {\n  addFunctionInternal(\n      std::move(cb),\n      std::move(intervalFunc),\n      nameID,\n      intervalDescr,\n      startDelay,\n      false /*runOnce*/);\n}\n\nvoid FunctionScheduler::addFunctionGenericNextRunTimeFunctor(\n    Function<void()>&& cb,\n    NextRunTimeFunc&& fn,\n    const std::string& nameID,\n    const std::string& intervalDescr,\n    microseconds startDelay) {\n  addFunctionInternal(\n      std::move(cb),\n      std::move(fn),\n      nameID,\n      intervalDescr,\n      startDelay,\n      false /*runOnce*/);\n}\n\ntemplate <typename RepeatFuncNextRunTimeFunc>\nvoid FunctionScheduler::addFunctionToHeapChecked(\n    Function<void()>&& cb,\n    RepeatFuncNextRunTimeFunc&& fn,\n    const std::string& nameID,\n    const std::string& intervalDescr,\n    microseconds startDelay,\n    bool runOnce) {\n  if (!cb) {\n    throw std::invalid_argument(\n        \"FunctionScheduler: Scheduled function must be set\");\n  }\n  if (!fn) {\n    throw std::invalid_argument(\n        \"FunctionScheduler: \"\n        \"interval distribution or next run time function must be set\");\n  }\n  if (startDelay < microseconds::zero()) {\n    throw std::invalid_argument(\n        \"FunctionScheduler: start delay must be non-negative\");\n  }\n\n  std::unique_lock l(mutex_);\n  auto it = functionsMap_.find(nameID);\n  // check if the nameID is unique\n  if (it != functionsMap_.end()) {\n    throw std::invalid_argument(\n        to<std::string>(\n            \"FunctionScheduler: a function named \\\"\",\n            nameID,\n            \"\\\" already exists\"));\n  }\n\n  if (currentFunction_ && currentFunction_->name == nameID) {\n    throw std::invalid_argument(\n        to<std::string>(\n            \"FunctionScheduler: a function named \\\"\",\n            nameID,\n            \"\\\" already exists\"));\n  }\n\n  addFunctionToHeap(\n      l,\n      std::make_unique<RepeatFunc>(\n          std::move(cb),\n          std::forward<RepeatFuncNextRunTimeFunc>(fn),\n          nameID,\n          intervalDescr,\n          startDelay,\n          runOnce));\n}\n\nvoid FunctionScheduler::addFunctionInternal(\n    Function<void()>&& cb,\n    NextRunTimeFunc&& fn,\n    const std::string& nameID,\n    const std::string& intervalDescr,\n    microseconds startDelay,\n    bool runOnce) {\n  return addFunctionToHeapChecked(\n      std::move(cb), std::move(fn), nameID, intervalDescr, startDelay, runOnce);\n}\n\nvoid FunctionScheduler::addFunctionInternal(\n    Function<void()>&& cb,\n    IntervalDistributionFunc&& fn,\n    const std::string& nameID,\n    const std::string& intervalDescr,\n    microseconds startDelay,\n    bool runOnce) {\n  return addFunctionToHeapChecked(\n      std::move(cb), std::move(fn), nameID, intervalDescr, startDelay, runOnce);\n}\n\nbool FunctionScheduler::cancelFunctionWithLock(\n    std::unique_lock<std::mutex>& lock, StringPiece nameID) {\n  CHECK_EQ(lock.owns_lock(), true);\n  if (currentFunction_ && currentFunction_->name == nameID) {\n    auto erased = functionsMap_.erase(currentFunction_->name);\n    DCHECK_NE(erased, 0);\n    // This function is currently being run. Clear currentFunction_\n    // The running thread will see this and won't reschedule the function.\n    currentFunction_ = nullptr;\n    cancellingCurrentFunction_ = true;\n    return true;\n  }\n  return false;\n}\n\nbool FunctionScheduler::cancelFunction(StringPiece nameID) {\n  std::unique_lock l(mutex_);\n  if (cancelFunctionWithLock(l, nameID)) {\n    return true;\n  }\n  auto it = functionsMap_.find(nameID);\n  if (it != functionsMap_.end()) {\n    cancelFunction(l, it->second);\n    return true;\n  }\n\n  return false;\n}\n\nbool FunctionScheduler::cancelFunctionAndWait(StringPiece nameID) {\n  std::unique_lock l(mutex_);\n\n  if (cancelFunctionWithLock(l, nameID)) {\n    runningCondvar_.wait(l, [this]() { return !cancellingCurrentFunction_; });\n    return true;\n  }\n\n  auto it = functionsMap_.find(nameID);\n  if (it != functionsMap_.end()) {\n    cancelFunction(l, it->second);\n    return true;\n  }\n  return false;\n}\n\nvoid FunctionScheduler::cancelFunction(\n    const std::unique_lock<std::mutex>& l, RepeatFunc* it) {\n  // This function should only be called with mutex_ already locked.\n  DCHECK(l.mutex() == &mutex_);\n  DCHECK(l.owns_lock());\n  functionsMap_.erase(it->name);\n  functions_.erase(it);\n  delete it;\n}\n\nbool FunctionScheduler::cancelAllFunctionsWithLock(\n    std::unique_lock<std::mutex>& lock) {\n  CHECK_EQ(lock.owns_lock(), true);\n  clearHeap();\n  functionsMap_.clear();\n  if (currentFunction_) {\n    cancellingCurrentFunction_ = true;\n  }\n  currentFunction_ = nullptr;\n  return cancellingCurrentFunction_;\n}\n\nvoid FunctionScheduler::cancelAllFunctions() {\n  std::unique_lock l(mutex_);\n  cancelAllFunctionsWithLock(l);\n}\n\nvoid FunctionScheduler::cancelAllFunctionsAndWait() {\n  std::unique_lock l(mutex_);\n  if (cancelAllFunctionsWithLock(l)) {\n    runningCondvar_.wait(l, [this]() { return !cancellingCurrentFunction_; });\n  }\n}\n\nbool FunctionScheduler::resetFunctionTimer(StringPiece nameID) {\n  std::unique_lock l(mutex_);\n  if (currentFunction_ && currentFunction_->name == nameID) {\n    if (cancellingCurrentFunction_ || currentFunction_->runOnce) {\n      return false;\n    }\n    currentFunction_->resetNextRunTime(steady_clock::now());\n    return true;\n  }\n\n  auto it = functionsMap_.find(nameID);\n  if (it != functionsMap_.end()) {\n    if (running_) {\n      it->second->resetNextRunTime(steady_clock::now());\n      functions_.update(it->second);\n      runningCondvar_.notify_one();\n    }\n    return true;\n  }\n  return false;\n}\n\nbool FunctionScheduler::start() {\n  std::unique_lock l(mutex_);\n  if (running_) {\n    return false;\n  }\n\n  VLOG(1) << \"Starting FunctionScheduler with \" << functionsMap_.size()\n          << \" functions.\";\n  auto now = steady_clock::now();\n  // Reset the next run time. for all functions.\n  // note: this is needed since one can shutdown() and start() again\n  functions_.visit([&now](RepeatFunc* f) {\n    f->resetNextRunTime(now);\n    VLOG(1) << \"   - func: \" << (f->name.empty() ? \"(anon)\" : f->name.c_str())\n            << \", period = \" << f->intervalDescr\n            << \", delay = \" << f->startDelay.count() << \"us\";\n  });\n\n  thread_ = std::thread([&] { this->run(); });\n  running_ = true;\n\n  return true;\n}\n\nbool FunctionScheduler::shutdown() {\n  {\n    std::lock_guard g(mutex_);\n    if (!running_) {\n      return false;\n    }\n\n    running_ = false;\n    runningCondvar_.notify_one();\n  }\n  thread_.join();\n  return true;\n}\n\n// Use a dedicated method for sleeping in the main loop, so it can be identified\n// easily in profiles and traces.\nFOLLY_NOINLINE void FunctionScheduler::waitForWork(\n    std::unique_lock<std::mutex>& lock) {\n  runningCondvar_.wait(lock);\n}\nFOLLY_NOINLINE void FunctionScheduler::waitForWork(\n    std::unique_lock<std::mutex>& lock,\n    std::chrono::steady_clock::duration timeout) {\n  runningCondvar_.wait_for(lock, timeout);\n}\n\nvoid FunctionScheduler::run() {\n  std::unique_lock lock(mutex_);\n\n  folly::setThreadName(threadName_);\n\n  while (running_) {\n    // If we have nothing to run, wait until a function is added or until we\n    // are stopped.\n    if (functions_.empty()) {\n      waitForWork(lock);\n      continue;\n    }\n\n    auto now = steady_clock::now();\n    auto sleepTime = functions_.top()->getNextRunTime() - now;\n    if (sleepTime <= steady_clock::duration::zero()) {\n      // We need to run this function now\n      runOneFunction(lock, now);\n      runningCondvar_.notify_all();\n    } else {\n      waitForWork(lock, sleepTime);\n    }\n  }\n}\n\nvoid FunctionScheduler::runOneFunction(\n    std::unique_lock<std::mutex>& lock, steady_clock::time_point now) {\n  DCHECK(lock.mutex() == &mutex_);\n  DCHECK(lock.owns_lock());\n\n  // Pop the function from the heap: we need to release mutex_ while we invoke\n  // this function, and we need to maintain the heap property on functions_\n  // while mutex_ is unlocked.\n  auto func = std::unique_ptr<RepeatFunc>(functions_.pop());\n  currentFunction_ = func.get();\n  // Update the function's next run time.\n  if (steady_) {\n    // This allows scheduler to catch up\n    func->setNextRunTimeSteady();\n  } else {\n    // Note that we set nextRunTime based on the current time where we started\n    // the function call, rather than the time when the function finishes.\n    // This ensures that we call the function once every time interval, as\n    // opposed to waiting time interval seconds between calls.  (These can be\n    // different if the function takes a significant amount of time to run.)\n    func->setNextRunTimeStrict(now);\n  }\n\n  // Release the lock while we invoke the user's function\n  lock.unlock();\n\n  // Invoke the function\n  try {\n    VLOG(5) << \"Now running \" << func->name;\n    func->cb();\n  } catch (const std::exception& ex) {\n    LOG(ERROR) << \"Error running the scheduled function <\" << func->name\n               << \">: \" << exceptionStr(ex);\n  }\n\n  // Re-acquire the lock\n  lock.lock();\n\n  if (!currentFunction_) {\n    // The function was cancelled while we were running it.\n    // We shouldn't reschedule it;\n    cancellingCurrentFunction_ = false;\n    return;\n  }\n  if (currentFunction_->runOnce) {\n    functionsMap_.erase(currentFunction_->name);\n  } else {\n    functions_.push(func.release());\n  }\n  currentFunction_ = nullptr;\n}\n\nvoid FunctionScheduler::addFunctionToHeap(\n    const std::unique_lock<std::mutex>& lock,\n    std::unique_ptr<RepeatFunc> func) {\n  // This function should only be called with mutex_ already locked.\n  DCHECK(lock.mutex() == &mutex_);\n  DCHECK(lock.owns_lock());\n\n  func->resetNextRunTime(steady_clock::now());\n  functionsMap_.emplace(func->name, func.get());\n  functions_.push(func.release()); // heap takes ownership\n  if (running_) {\n    // Signal the running thread to wake up and see if it needs to change\n    // its current scheduling decision.\n    runningCondvar_.notify_one();\n  }\n}\n\nvoid FunctionScheduler::setThreadName(StringPiece threadName) {\n  std::unique_lock l(mutex_);\n  threadName_ = threadName.str();\n}\n\nvoid FunctionScheduler::clearHeap() {\n  while (auto top = functions_.pop()) {\n    delete top;\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/FunctionScheduler.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <condition_variable>\n#include <mutex>\n#include <thread>\n\n#include <folly/Function.h>\n#include <folly/Range.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/IntrusiveHeap.h>\n#include <folly/hash/Hash.h>\n\nnamespace folly {\n\n/**\n * Schedules any number of functions to run at various intervals. E.g.,\n *\n *   FunctionScheduler fs;\n *\n *   fs.addFunction([&] { LOG(INFO) << \"tick...\"; }, seconds(1), \"ticker\");\n *   fs.addFunction(std::bind(&TestClass::doStuff, this), minutes(5), \"stuff\");\n *   fs.start();\n *   ........\n *   fs.cancelFunction(\"ticker\");\n *   fs.addFunction([&] { LOG(INFO) << \"tock...\"; }, minutes(3), \"tocker\");\n *   ........\n *   fs.shutdown();\n *\n *\n * Note: the class uses only one thread - if you want to use more than one\n *       thread, either use multiple FunctionScheduler objects, or check out\n *       ThreadedRepeatingFunctionRunner.h for a much simpler contract of\n *       \"run each function periodically in its own thread\".\n *\n * start() schedules the functions, while shutdown() terminates further\n * scheduling (after any running function terminates).\n */\nclass FunctionScheduler {\n public:\n  FunctionScheduler();\n\n  /**\n   * On destruction, ensures that this instance is shutdown prior to deletion.\n   *\n   * See `shutdown()`.\n   */\n  ~FunctionScheduler();\n\n  /**\n   * By default steady is false, meaning schedules may lag behind overtime.\n   * This could be due to long running tasks or time drift because of randomness\n   * in thread wakeup time.\n   * By setting steady to true, FunctionScheduler will attempt to catch up.\n   * i.e. more like a cronjob\n   *\n   * NOTE: it's only safe to set this before calling start()\n   */\n  void setSteady(bool steady) { steady_ = steady; }\n\n  /*\n   * Parameters to control the function interval.\n   *\n   * If isPoisson is true, then use std::poisson_distribution to pick the\n   * interval between each invocation of the function.\n   *\n   * If isPoisson is false, then always use the fixed interval specified to\n   * addFunction().\n   */\n  struct LatencyDistribution {\n    bool isPoisson;\n    std::chrono::microseconds poissonMean;\n\n    LatencyDistribution(bool poisson, std::chrono::microseconds mean)\n        : isPoisson(poisson), poissonMean(mean) {}\n  };\n\n  /**\n   * Adds a new function to the FunctionScheduler.\n   *\n   * Functions will not be run until start() is called.  When start() is\n   * called, each function will be run after its specified startDelay.\n   * Functions may also be added after start() has been called, in which case\n   * startDelay is still honored.\n   *\n   * Throws an exception on error.  In particular, each function must have a\n   * unique name--two functions cannot be added with the same name.\n   */\n  void addFunction(\n      Function<void()>&& cb,\n      std::chrono::microseconds interval,\n      StringPiece nameID = StringPiece(),\n      std::chrono::microseconds startDelay = std::chrono::microseconds(0));\n\n  /*\n   * Add a new function to the FunctionScheduler with a specified\n   * LatencyDistribution\n   */\n  void addFunction(\n      Function<void()>&& cb,\n      std::chrono::microseconds interval,\n      const LatencyDistribution& latencyDistr,\n      StringPiece nameID = StringPiece(),\n      std::chrono::microseconds startDelay = std::chrono::microseconds(0));\n\n  /**\n   * Adds a new function to the FunctionScheduler to run only once.\n   */\n  void addFunctionOnce(\n      Function<void()>&& cb,\n      StringPiece nameID = StringPiece(),\n      std::chrono::microseconds startDelay = std::chrono::microseconds(0));\n\n  /**\n   * Add a new function to the FunctionScheduler with the time\n   * interval being distributed uniformly within the given interval\n   * [minInterval, maxInterval].\n   */\n  void addFunctionUniformDistribution(\n      Function<void()>&& cb,\n      std::chrono::microseconds minInterval,\n      std::chrono::microseconds maxInterval,\n      StringPiece nameID,\n      std::chrono::microseconds startDelay);\n\n  /**\n   * Add a new function to the FunctionScheduler whose start times are attempted\n   * to be scheduled so that they are congruent modulo the interval.\n   * Note: The scheduling of the next run time happens right before the function\n   * invocation, so the first time a function takes more time than the interval,\n   * it will be reinvoked immediately.\n   */\n  void addFunctionConsistentDelay(\n      Function<void()>&& cb,\n      std::chrono::microseconds interval,\n      StringPiece nameID = StringPiece(),\n      std::chrono::microseconds startDelay = std::chrono::microseconds(0));\n\n  /**\n   * A type alias for function that is called to determine the time\n   * interval for the next scheduled run.\n   */\n  using IntervalDistributionFunc = Function<std::chrono::microseconds()>;\n  /**\n   * A type alias for function that returns the next run time, given the current\n   * run time and the current start time.\n   */\n  using NextRunTimeFunc = Function<std::chrono::steady_clock::time_point(\n      std::chrono::steady_clock::time_point,\n      std::chrono::steady_clock::time_point)>;\n\n  /**\n   * Add a new function to the FunctionScheduler. The scheduling interval\n   * is determined by the interval distribution functor, which is called\n   * every time the next function execution is scheduled. This allows\n   * for supporting custom interval distribution algorithms in addition\n   * to built in constant interval; and Poisson and jitter distributions\n   * (@see FunctionScheduler::addFunction and\n   * @see FunctionScheduler::addFunctionJitterInterval).\n   */\n  void addFunctionGenericDistribution(\n      Function<void()>&& cb,\n      IntervalDistributionFunc&& intervalFunc,\n      const std::string& nameID,\n      const std::string& intervalDescr,\n      std::chrono::microseconds startDelay);\n\n  /**\n   * Like addFunctionGenericDistribution, adds a new function to the\n   * FunctionScheduler, but the next run time is determined directly by the\n   * given functor, rather than by adding an interval.\n   */\n  void addFunctionGenericNextRunTimeFunctor(\n      Function<void()>&& cb,\n      NextRunTimeFunc&& fn,\n      const std::string& nameID,\n      const std::string& intervalDescr,\n      std::chrono::microseconds startDelay);\n\n  /**\n   * Cancels the function with the specified name, so it will no longer be run.\n   *\n   * Returns false if no function exists with the specified name.\n   */\n  bool cancelFunction(StringPiece nameID);\n  bool cancelFunctionAndWait(StringPiece nameID);\n\n  /**\n   * All functions registered will be canceled.\n   */\n  void cancelAllFunctions();\n  void cancelAllFunctionsAndWait();\n\n  /**\n   * Resets the specified function's timer.\n   * When resetFunctionTimer is called, the specified function's timer will\n   * be reset with the same parameters it was passed initially, including\n   * its startDelay. If the startDelay was 0, the function will be invoked\n   * immediately.\n   *\n   * Returns false if no function exists with the specified name.\n   */\n  bool resetFunctionTimer(StringPiece nameID);\n\n  /**\n   * Starts the scheduler.\n   *\n   * Returns false if the scheduler was already running.\n   */\n  bool start();\n\n  /**\n   * Stops the FunctionScheduler.\n   *\n   * This method blocks until any running function terminates. It is also called\n   * automatically on FunctionScheduler destruction.\n   *\n   * This FunctionScheduler may be restarted later by calling start() again.\n   *\n   * Returns false if the scheduler was not running (in which case this method\n   * was a no-op and did not block on any function running). Returns true\n   * otherwise.\n   *\n   * Thread-safe.\n   */\n  bool shutdown();\n\n  /**\n   * Set the name of the worker thread.\n   */\n  void setThreadName(StringPiece threadName);\n\n private:\n  struct RepeatFunc : public IntrusiveHeapNode<> {\n    Function<void()> cb;\n    NextRunTimeFunc nextRunTimeFunc;\n    std::chrono::steady_clock::time_point nextRunTime;\n    std::string name;\n    std::chrono::microseconds startDelay;\n    std::string intervalDescr;\n    bool runOnce;\n\n    RepeatFunc(\n        Function<void()>&& cback,\n        IntervalDistributionFunc&& intervalFn,\n        const std::string& nameID,\n        const std::string& intervalDistDescription,\n        std::chrono::microseconds delay,\n        bool once)\n        : RepeatFunc(\n              std::move(cback),\n              getNextRunTimeFunc(std::move(intervalFn)),\n              nameID,\n              intervalDistDescription,\n              delay,\n              once) {}\n\n    RepeatFunc(\n        Function<void()>&& cback,\n        NextRunTimeFunc&& nextRunTimeFn,\n        const std::string& nameID,\n        const std::string& intervalDistDescription,\n        std::chrono::microseconds delay,\n        bool once)\n        : cb(std::move(cback)),\n          nextRunTimeFunc(std::move(nextRunTimeFn)),\n          nextRunTime(),\n          name(nameID),\n          startDelay(delay),\n          intervalDescr(intervalDistDescription),\n          runOnce(once) {}\n\n    static NextRunTimeFunc getNextRunTimeFunc(\n        IntervalDistributionFunc&& intervalFn) {\n      return [intervalFn_2 = std::move(intervalFn)](\n                 std::chrono::steady_clock::time_point /* curNextRunTime */,\n                 std::chrono::steady_clock::time_point curTime) mutable {\n        return curTime + intervalFn_2();\n      };\n    }\n\n    std::chrono::steady_clock::time_point getNextRunTime() const {\n      return nextRunTime;\n    }\n    void setNextRunTimeStrict(std::chrono::steady_clock::time_point curTime) {\n      nextRunTime = nextRunTimeFunc(nextRunTime, curTime);\n    }\n    void setNextRunTimeSteady() {\n      nextRunTime = nextRunTimeFunc(nextRunTime, nextRunTime);\n    }\n    void resetNextRunTime(std::chrono::steady_clock::time_point curTime) {\n      nextRunTime = curTime + startDelay;\n    }\n  };\n\n  void run();\n  void runOneFunction(\n      std::unique_lock<std::mutex>& lock,\n      std::chrono::steady_clock::time_point now);\n  void cancelFunction(const std::unique_lock<std::mutex>& lock, RepeatFunc* it);\n  void addFunctionToHeap(\n      const std::unique_lock<std::mutex>& lock,\n      std::unique_ptr<RepeatFunc> func);\n\n  template <typename RepeatFuncNextRunTimeFunc>\n  void addFunctionToHeapChecked(\n      Function<void()>&& cb,\n      RepeatFuncNextRunTimeFunc&& fn,\n      const std::string& nameID,\n      const std::string& intervalDescr,\n      std::chrono::microseconds startDelay,\n      bool runOnce);\n\n  void addFunctionInternal(\n      Function<void()>&& cb,\n      NextRunTimeFunc&& fn,\n      const std::string& nameID,\n      const std::string& intervalDescr,\n      std::chrono::microseconds startDelay,\n      bool runOnce);\n  void addFunctionInternal(\n      Function<void()>&& cb,\n      IntervalDistributionFunc&& fn,\n      const std::string& nameID,\n      const std::string& intervalDescr,\n      std::chrono::microseconds startDelay,\n      bool runOnce);\n\n  // Return true if the current function is being canceled\n  bool cancelAllFunctionsWithLock(std::unique_lock<std::mutex>& lock);\n  bool cancelFunctionWithLock(\n      std::unique_lock<std::mutex>& lock, StringPiece nameID);\n\n  void clearHeap();\n\n  void waitForWork(std::unique_lock<std::mutex>& lock);\n  void waitForWork(\n      std::unique_lock<std::mutex>& lock,\n      std::chrono::steady_clock::duration timeout);\n\n  std::thread thread_;\n\n  // Mutex to protect our member variables.\n  std::mutex mutex_;\n  bool running_{false};\n\n  struct RunTimeOrder {\n    bool operator()(const RepeatFunc& f1, const RepeatFunc& f2) const {\n      return f1.getNextRunTime() > f2.getNextRunTime();\n    }\n  };\n  using FunctionHeap = IntrusiveHeap<RepeatFunc, RunTimeOrder>;\n  using FunctionMap = folly::F14FastMap<StringPiece, RepeatFunc*, Hash>;\n  FunctionHeap functions_;\n  FunctionMap functionsMap_;\n\n  // The function currently being invoked by the running thread.\n  // This is null when the running thread is idle\n  RepeatFunc* currentFunction_{nullptr};\n\n  // Condition variable that is signalled whenever a new function is added\n  // or when the FunctionScheduler is stopped.\n  std::condition_variable runningCondvar_;\n\n  std::string threadName_{\"FuncSched\"};\n  bool steady_{false};\n  bool cancellingCurrentFunction_{false};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/FutureExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/functional/Invoke.h>\n#include <folly/futures/Future.h>\n\nnamespace folly {\n\ntemplate <typename ExecutorImpl>\nclass FutureExecutor : public ExecutorImpl {\n public:\n  template <typename... Args>\n  explicit FutureExecutor(Args&&... args)\n      : ExecutorImpl(std::forward<Args>(args)...) {}\n\n  /*\n   * Given a function func that returns a Future<T>, adds that function to the\n   * contained Executor and returns a Future<T> which will be fulfilled with\n   * func's result once it has been executed.\n   *\n   * For example: auto f = futureExecutor.addFuture([](){\n   *                return doAsyncWorkAndReturnAFuture();\n   *              });\n   */\n  template <typename F>\n  typename std::enable_if<\n      folly::isFuture<invoke_result_t<F>>::value,\n      invoke_result_t<F>>::type\n  addFuture(F func) {\n    using T = typename invoke_result_t<F>::value_type;\n    folly::Promise<T> promise;\n    auto future = promise.getFuture();\n    ExecutorImpl::add([promise = std::move(promise),\n                       func = std::move(func)]() mutable {\n      func().then([promise = std::move(promise)](folly::Try<T>&& t) mutable {\n        promise.setTry(std::move(t));\n      });\n    });\n    return future;\n  }\n\n  /*\n   * Similar to addFuture above, but takes a func that returns some non-Future\n   * type T.\n   *\n   * For example: auto f = futureExecutor.addFuture([]() {\n   *                return 42;\n   *              });\n   */\n  template <typename F>\n  typename std::enable_if<\n      !folly::isFuture<invoke_result_t<F>>::value,\n      folly::Future<folly::lift_unit_t<invoke_result_t<F>>>>::type\n  addFuture(F func) {\n    using T = folly::lift_unit_t<invoke_result_t<F>>;\n    folly::Promise<T> promise;\n    auto future = promise.getFuture();\n    ExecutorImpl::add(\n        [promise = std::move(promise), func = std::move(func)]() mutable {\n          promise.setWith(std::move(func));\n        });\n    return future;\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/GlobalExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <thread>\n#include <folly/executors/GlobalExecutor.h>\n\n#include <folly/Function.h>\n#include <folly/SharedMutex.h>\n#include <folly/Singleton.h>\n#include <folly/detail/AsyncTrace.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/IOExecutor.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/system/HardwareConcurrency.h>\n\nusing namespace folly;\n\nFOLLY_GFLAGS_DEFINE_uint32(\n    folly_global_io_executor_threads,\n    0,\n    \"Number of threads global IOThreadPoolExecutor will create\");\n\nFOLLY_GFLAGS_DEFINE_uint32(\n    folly_global_cpu_executor_threads,\n    0,\n    \"Number of threads global CPUThreadPoolExecutor will create\");\n\nnamespace {\n\nusing ImmutableGlobalCPUExecutor = CPUThreadPoolExecutor;\n\nclass GlobalTag {};\n\n// aka InlineExecutor\nclass DefaultCPUExecutor : public InlineLikeExecutor {\n public:\n  FOLLY_NOINLINE void add(Func f) override { f(); }\n};\n\nSingleton<std::shared_ptr<DefaultCPUExecutor>> gDefaultGlobalCPUExecutor([] {\n  return new std::shared_ptr<DefaultCPUExecutor>(new DefaultCPUExecutor{});\n});\n\nSingleton<std::shared_ptr<ImmutableGlobalCPUExecutor>, GlobalTag>\n    gImmutableGlobalCPUExecutor([] {\n      size_t nthreads = FLAGS_folly_global_cpu_executor_threads;\n      nthreads = nthreads ? nthreads : folly::available_concurrency();\n      return new std::shared_ptr<ImmutableGlobalCPUExecutor>(\n          new ImmutableGlobalCPUExecutor(\n              nthreads,\n              std::make_shared<NamedThreadFactory>(\"GlobalCPUThreadPool\")));\n    });\n\nSingleton<std::shared_ptr<IOThreadPoolExecutor>, GlobalTag>\n    gImmutableGlobalIOExecutor([] {\n      size_t nthreads = FLAGS_folly_global_io_executor_threads;\n      nthreads = nthreads ? nthreads : folly::available_concurrency();\n      return new std::shared_ptr<IOThreadPoolExecutor>(new IOThreadPoolExecutor(\n          nthreads,\n          std::make_shared<NamedThreadFactory>(\"GlobalIOThreadPool\")));\n    });\n\ntemplate <class ExecutorBase>\nstd::shared_ptr<ExecutorBase> getImmutable();\n\ntemplate <>\nstd::shared_ptr<Executor> getImmutable() {\n  if (auto executorPtrPtr = gImmutableGlobalCPUExecutor.try_get()) {\n    return *executorPtrPtr;\n  }\n  return nullptr;\n}\n\ntemplate <>\nstd::shared_ptr<IOExecutor> getImmutable() {\n  if (auto executorPtrPtr = gImmutableGlobalIOExecutor.try_get()) {\n    return *executorPtrPtr;\n  }\n  return nullptr;\n}\n\ntemplate <class ExecutorBase>\nclass GlobalExecutor {\n public:\n  explicit GlobalExecutor(\n      Function<std::shared_ptr<ExecutorBase>()> constructDefault)\n      : getDefault_(std::move(constructDefault)) {}\n\n  std::shared_ptr<ExecutorBase> get() {\n    std::shared_lock guard(mutex_);\n    if (auto executor = executor_.lock()) {\n      return executor; // Fast path.\n    }\n\n    return getDefault_();\n  }\n\n  void set(std::weak_ptr<ExecutorBase> executor) {\n    std::unique_lock guard(mutex_);\n    executor_.swap(executor);\n  }\n\n  // Replace the constructDefault function to use the immutable singleton\n  // rather than the default singleton\n  void setFromImmutable() {\n    std::unique_lock guard(mutex_);\n\n    getDefault_ = [] { return getImmutable<ExecutorBase>(); };\n    executor_ = std::weak_ptr<ExecutorBase>{};\n  }\n\n private:\n  mutable SharedMutex mutex_;\n  std::weak_ptr<ExecutorBase> executor_;\n  Function<std::shared_ptr<ExecutorBase>()> getDefault_;\n};\n\nLeakySingleton<GlobalExecutor<Executor>> gGlobalCPUExecutor([] {\n  return new GlobalExecutor<Executor>(\n      // Default global CPU executor is an InlineExecutor.\n      [] {\n        if (auto executorPtrPtr = gDefaultGlobalCPUExecutor.try_get()) {\n          return *executorPtrPtr;\n        }\n        return std::shared_ptr<DefaultCPUExecutor>{};\n      });\n});\n\nLeakySingleton<GlobalExecutor<IOExecutor>> gGlobalIOExecutor([] {\n  return new GlobalExecutor<IOExecutor>(\n      // Default global IO executor is an IOThreadPoolExecutor.\n      [] { return getImmutable<IOExecutor>(); });\n});\n} // namespace\n\nnamespace folly {\n\nnamespace detail {\nstd::shared_ptr<Executor> tryGetImmutableCPUPtr() {\n  return getImmutable<Executor>();\n}\n} // namespace detail\n\nExecutor::KeepAlive<> getGlobalCPUExecutor() {\n  auto executorPtrPtr = gImmutableGlobalCPUExecutor.try_get();\n  if (!executorPtrPtr) {\n    throw std::runtime_error(\"Requested global CPU executor during shutdown.\");\n  }\n  async_tracing::logGetImmutableCPUExecutor(executorPtrPtr->get());\n  return folly::getKeepAliveToken(executorPtrPtr->get());\n}\n\nExecutor::KeepAlive<> getGlobalCPUExecutorWeakRef() {\n  auto executorPtrPtr = gImmutableGlobalCPUExecutor.try_get();\n  if (!executorPtrPtr) {\n    throw std::runtime_error(\"Requested global CPU executor during shutdown.\");\n  }\n  async_tracing::logGetImmutableCPUExecutor(executorPtrPtr->get());\n  return folly::getWeakRef(**executorPtrPtr);\n}\n\nGlobalCPUExecutorCounters getGlobalCPUExecutorCounters() {\n  auto executorPtrPtr = gImmutableGlobalCPUExecutor.try_get();\n  if (!executorPtrPtr) {\n    throw std::runtime_error(\"Requested global CPU executor during shutdown.\");\n  }\n  auto& executor = **executorPtrPtr;\n  GlobalCPUExecutorCounters counters;\n  counters.numThreads = executor.numThreads();\n  counters.numActiveThreads = executor.numActiveThreads();\n  counters.numPendingTasks = executor.getTaskQueueSize();\n  return counters;\n}\n\nExecutor::KeepAlive<IOExecutor> getGlobalIOExecutor() {\n  auto executorPtrPtr = gImmutableGlobalIOExecutor.try_get();\n  if (!executorPtrPtr) {\n    throw std::runtime_error(\"Requested global IO executor during shutdown.\");\n  }\n  async_tracing::logGetImmutableIOExecutor(executorPtrPtr->get());\n  return folly::getKeepAliveToken(executorPtrPtr->get());\n}\n\nstd::shared_ptr<Executor> getUnsafeMutableGlobalCPUExecutor() {\n  auto& singleton = gGlobalCPUExecutor.get();\n  auto executor = singleton.get();\n  async_tracing::logGetGlobalCPUExecutor(executor.get());\n  return executor;\n}\n\nstd::shared_ptr<Executor> getCPUExecutor() {\n  return getUnsafeMutableGlobalCPUExecutor();\n}\n\nvoid setUnsafeMutableGlobalCPUExecutorToGlobalCPUExecutor() {\n  async_tracing::logSetGlobalCPUExecutorToImmutable();\n  gGlobalCPUExecutor.get().setFromImmutable();\n}\n\nvoid setCPUExecutorToGlobalCPUExecutor() {\n  setUnsafeMutableGlobalCPUExecutorToGlobalCPUExecutor();\n}\n\nvoid setUnsafeMutableGlobalCPUExecutor(std::weak_ptr<Executor> executor) {\n  async_tracing::logSetGlobalCPUExecutor(executor.lock().get());\n  gGlobalCPUExecutor.get().set(std::move(executor));\n}\n\nvoid setCPUExecutor(std::weak_ptr<Executor> executor) {\n  setUnsafeMutableGlobalCPUExecutor(std::move(executor));\n}\n\nstd::shared_ptr<IOExecutor> getUnsafeMutableGlobalIOExecutor() {\n  auto& singleton = gGlobalIOExecutor.get();\n  auto executor = singleton.get();\n  async_tracing::logGetGlobalIOExecutor(executor.get());\n  return executor;\n}\n\nstd::shared_ptr<IOExecutor> getIOExecutor() {\n  return getUnsafeMutableGlobalIOExecutor();\n}\n\nvoid setUnsafeMutableGlobalIOExecutor(std::weak_ptr<IOExecutor> executor) {\n  async_tracing::logSetGlobalIOExecutor(executor.lock().get());\n  gGlobalIOExecutor.get().set(std::move(executor));\n}\n\nvoid setIOExecutor(std::weak_ptr<IOExecutor> executor) {\n  setUnsafeMutableGlobalIOExecutor(std::move(executor));\n}\n\nEventBase* getUnsafeMutableGlobalEventBase() {\n  auto executor = getUnsafeMutableGlobalIOExecutor();\n  if (FOLLY_LIKELY(!!executor)) {\n    return executor->getEventBase();\n  }\n\n  return nullptr;\n}\n\nEventBase* getEventBase() {\n  return getUnsafeMutableGlobalEventBase();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/GlobalExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Routines for managing executors global to a process\n *\n * @file executors/GlobalExecutor.h\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/Executor.h>\n#include <folly/executors/IOExecutor.h>\n#include <folly/portability/GFlags.h>\n\nFOLLY_GFLAGS_DECLARE_uint32(folly_global_cpu_executor_threads);\nFOLLY_GFLAGS_DECLARE_uint32(folly_global_io_executor_threads);\n\nnamespace folly {\n\nnamespace detail {\nstd::shared_ptr<Executor> tryGetImmutableCPUPtr();\n} // namespace detail\n\n/**\n * @methodset Executors\n *\n * Retrieve the global immutable executor.\n * This executor is a CPU thread pool of appropriate machine core size.\n *\n * Use to run CPU workloads.\n *\n * @return       KeepAlive wrapped global immutable CPU executor. May throw on\n * shutdown. If no throw, returned KeepAlive is valid.\n */\nfolly::Executor::KeepAlive<> getGlobalCPUExecutor();\n\n/**\n * @methodset Executors\n *\n * Same as getGlobalCPUExecutor(), but returns a weak keepalive: holding this\n * keepalive will not keep the global executor alive at shutdown, and tasks\n * scheduled on it during or after shutdown will not be executed. This can be\n * used for best-effort tasks, but it should not be used for future or coroutine\n * continuations, as they expect a guarantee of forward progress and can\n * deadlock if progress is not guaranteed.\n *\n * @copydetails getGlobalCPUExecutor()\n */\nfolly::Executor::KeepAlive<> getGlobalCPUExecutorWeakRef();\n\nstruct GlobalCPUExecutorCounters {\n  // Maximum number of threads that the executor can run concurrently.\n  size_t numThreads;\n  // Number of threads currently active (running but possibly waiting for tasks)\n  size_t numActiveThreads;\n  // Number of tasks pending execution in the executor's queue.\n  size_t numPendingTasks;\n};\n\n/**\n * @methodset Executors\n *\n * Retrieve counters from the global immutable CPU executor.\n * These counters should only be used to monitor the executor's load, as\n * retrieving the counters may be expensive.\n *\n * @return GlobalCPUExecutorCounters struct. Counters are not guaranteed to be\n * consistent with each other: each may be retrieved at a different point in\n * time. May throw on shutdown.\n */\nGlobalCPUExecutorCounters getGlobalCPUExecutorCounters();\n\n/**\n * @methodset Executors\n *\n * Retrieve the global immutable IO executor.\n * This executor is an IO thread pool of appropriate machine core size.\n *\n * Use to run IO workloads that require an event base.\n *\n * @return       KeepAlive wrapped global immutable IO executor. May throw\n * on shutdown. If no throw, returned KeepAlive is valid.\n */\nfolly::Executor::KeepAlive<IOExecutor> getGlobalIOExecutor();\n\n/**\n * @methodset Deprecated\n *\n * To use the global mutable executor use getUnsafeMutableGlobalCPUExecutor.\n * For a better solution use getGlobalCPUExecutor.\n */\n[[deprecated(\n    \"getCPUExecutor is deprecated. \"\n    \"To use the global mutable executor use getUnsafeMutableGlobalCPUExecutor. \"\n    \"For a better solution use getGlobalCPUExecutor.\")]] std::\n    shared_ptr<folly::Executor>\n    getCPUExecutor();\n/**\n * @methodset Executors\n *\n * Retrieve the global mutable Executor. If there is none, a default\n * InlineExecutor will be constructed and returned. This is named CPUExecutor to\n * distinguish it from IOExecutor below and to hint that it's intended for\n * CPU-bound tasks.\n *\n * For a better solution use getGlobalCPUExecutor.\n *\n * @return       Global mutable executor. Can return nullptr on shutdown.\n */\nstd::shared_ptr<folly::Executor> getUnsafeMutableGlobalCPUExecutor();\n\n/**\n * @methodset Deprecated\n *\n * To use the global mutable executor use setUnsafeMutableGlobalCPUExecutor.\n * For a better solution use getGlobalCPUExecutor and avoid calling set.\n */\n[[deprecated(\n    \"setCPUExecutor is deprecated. \"\n    \"To use the global mutable executor use setUnsafeMutableGlobalCPUExecutor. \"\n    \"For a better solution use getGlobalCPUExecutor and avoid calling set.\")]] void\nsetCPUExecutor(std::weak_ptr<folly::Executor> executor);\n/**\n * @methodset Executors\n *\n * Set an Executor to be the global mutable Executor which will be returned by\n * subsequent calls to getUnsafeMutableGlobalCPUExecutor()\n *\n * For a better solution use getGlobalCPUExecutor and avoid calling set.\n *\n * @param  executor       Executor to set\n */\nvoid setUnsafeMutableGlobalCPUExecutor(std::weak_ptr<folly::Executor> executor);\n\n/**\n * @methodset Deprecated\n *\n * Switch to setUnsafeMutableGlobalCPUExecutorToGlobalCPUExecutor.\n */\n[[deprecated(\n    \"setCPUExecutorToGlobalCPUExecutor is deprecated. \"\n    \"Switch to setUnsafeMutableGlobalCPUExecutorToGlobalCPUExecutor. \")]] void\nsetCPUExecutorToGlobalCPUExecutor();\n/**\n * @methodset Executors\n *\n * Set the mutable Executor to be the immutable default returned by\n * getGlobalCPUExecutor()\n *\n */\nvoid setUnsafeMutableGlobalCPUExecutorToGlobalCPUExecutor();\n\n/**\n * @methodset Deprecated\n *\n * To use the global mutable executor use getUnsafeMutableGlobalIOExecutor.\n * For a better solution use getGlobalIOExecutor.\n */\n[[deprecated(\n    \"getIOExecutor is deprecated. \"\n    \"To use the global mutable executor use getUnsafeMutableGlobalIOExecutor. \"\n    \"For a better solution use getGlobalIOExecutor.\")]] std::\n    shared_ptr<IOExecutor>\n    getIOExecutor();\n/**\n * @methodset Executors\n *\n * Retrieve the global mutable IO Executor. If there is none, a default\n * IOThreadPoolExecutor will be constructed and returned.\n *\n * For a better solution use getGlobalIOExecutor.\n *\n * @return       Global mutable IO executor\n */\nstd::shared_ptr<IOExecutor> getUnsafeMutableGlobalIOExecutor();\n\n/**\n * @methodset Deprecated\n *\n * To use the global mutable executor use setUnsafeMutableGlobalIOExecutor.\n * For a better solution use getGlobalIOExecutor and avoid calling set.\n */\n[[deprecated(\n    \"setIOExecutor is deprecated. \"\n    \"To use the global mutable executor use setUnsafeMutableGlobalIOExecutor. \"\n    \"For a better solution use getGlobalIOExecutor and avoid calling set.\")]] void\nsetIOExecutor(std::weak_ptr<IOExecutor> executor);\n/**\n * @methodset Executors\n *\n * Set an IO Executor to be the global IOExecutor which will be returned by\n * subsequent calls to getUnsafeMutableGlobalIOExecutor()\n *\n * For a better solution use getGlobalIOExecutor and avoid calling set.\n *\n * @param  executor       IO Executor to set\n */\nvoid setUnsafeMutableGlobalIOExecutor(std::weak_ptr<IOExecutor> executor);\n\n/**\n * @methodset Deprecated\n *\n * To use the global mutable executor use getUnsafeMutableGlobalEventBase.\n * For a better solution use getGlobalIOExecutor and request the EventBase from\n * there.\n *\n */\n[[deprecated(\n    \"getEventBase is deprecated. \"\n    \"To use the global mutable executor use getUnsafeMutableGlobalEventBase. \"\n    \"For a better solution use getGlobalIOExecutor and request the EventBase \"\n    \"from there.\")]] folly::EventBase*\ngetEventBase();\n/**\n * @methodset Executors\n *\n * Retrieve an event base from the global mutable IO Executor\n *\n * NOTE: This is not shutdown-safe, the returned pointer may be\n * invalid during shutdown.\n *\n * For a better solution use getGlobalIOExecutor and request the EventBase from\n * there.\n *\n * @return       Event base used by global mutable IO executor\n */\nfolly::EventBase* getUnsafeMutableGlobalEventBase();\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/GlobalThreadPoolList.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/GlobalThreadPoolList.h>\n#include <folly/system/ThreadId.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <folly/CppAttributes.h>\n#include <folly/Indestructible.h>\n#include <folly/Synchronized.h>\n#include <folly/ThreadLocal.h>\n\nnamespace folly {\n\nnamespace debugger_detail {\n\nclass ThreadListHook {\n public:\n  ThreadListHook(\n      ThreadPoolListHook* poolId,\n      std::thread::id threadId,\n      uint64_t osThreadId);\n  ~ThreadListHook();\n\n private:\n  ThreadListHook() = default;\n  ThreadPoolListHook* poolId_;\n  std::thread::id threadId_;\n  uint64_t osThreadId_;\n};\n\nclass GlobalThreadPoolListImpl {\n public:\n  GlobalThreadPoolListImpl() = default;\n\n  void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name);\n\n  void unregisterThreadPool(ThreadPoolListHook* threadPoolId);\n\n  void registerThreadPoolThread(\n      ThreadPoolListHook* threadPoolId,\n      std::thread::id threadId,\n      uint64_t osThreadId);\n\n  void unregisterThreadPoolThread(\n      ThreadPoolListHook* threadPoolId, std::thread::id threadId);\n\n private:\n  struct PoolInfo {\n    ThreadPoolListHook* addr;\n    std::string name;\n    std::vector<std::thread::id> threads;\n    // separate data structure for backwards compatibility\n    std::vector<uint64_t> osThreadIds;\n  };\n\n  struct Pools {\n    // Just a vector since ease of access from gdb is the most important\n    // property\n    std::vector<PoolInfo> poolsInfo_;\n\n    PoolInfo* FOLLY_NULLABLE getPool(void* threadPoolId) {\n      for (auto& elem : vector()) {\n        if (elem.addr == threadPoolId) {\n          return &elem;\n        }\n      }\n\n      return nullptr;\n    }\n\n    std::vector<PoolInfo>& vector() { return poolsInfo_; }\n  };\n\n  Pools pools_;\n};\n\nclass GlobalThreadPoolList {\n public:\n  GlobalThreadPoolList() noexcept { debug = this; }\n\n  static GlobalThreadPoolList& instance();\n\n  void registerThreadPool(ThreadPoolListHook* threadPoolId, std::string name);\n\n  void unregisterThreadPool(ThreadPoolListHook* threadPoolId);\n\n  void registerThreadPoolThread(\n      ThreadPoolListHook* threadPoolId,\n      std::thread::id threadId,\n      uint64_t osThreadId);\n\n  void unregisterThreadPoolThread(\n      ThreadPoolListHook* threadPoolId, std::thread::id threadId);\n\n  GlobalThreadPoolList(GlobalThreadPoolList const&) = delete;\n  void operator=(GlobalThreadPoolList const&) = delete;\n\n private:\n  // Make instance() available to the debugger\n  static GlobalThreadPoolList* debug;\n\n  folly::Synchronized<GlobalThreadPoolListImpl> globalListImpl_;\n  folly::ThreadLocalPtr<ThreadListHook> threadHook_;\n};\n\nGlobalThreadPoolList* GlobalThreadPoolList::debug;\n\nGlobalThreadPoolList& GlobalThreadPoolList::instance() {\n  static folly::Indestructible<GlobalThreadPoolList> ret;\n  return *ret;\n}\n\nvoid GlobalThreadPoolList::registerThreadPool(\n    ThreadPoolListHook* threadPoolId, std::string name) {\n  globalListImpl_.wlock()->registerThreadPool(threadPoolId, std::move(name));\n}\n\nvoid GlobalThreadPoolList::unregisterThreadPool(\n    ThreadPoolListHook* threadPoolId) {\n  globalListImpl_.wlock()->unregisterThreadPool(threadPoolId);\n}\n\nvoid GlobalThreadPoolList::registerThreadPoolThread(\n    ThreadPoolListHook* threadPoolId,\n    std::thread::id threadId,\n    uint64_t osThreadId) {\n  DCHECK(!threadHook_);\n  threadHook_.reset(\n      std::make_unique<ThreadListHook>(threadPoolId, threadId, osThreadId));\n\n  globalListImpl_.wlock()->registerThreadPoolThread(\n      threadPoolId, threadId, osThreadId);\n}\n\nvoid GlobalThreadPoolList::unregisterThreadPoolThread(\n    ThreadPoolListHook* threadPoolId, std::thread::id threadId) {\n  (void)threadPoolId;\n  (void)threadId;\n  globalListImpl_.wlock()->unregisterThreadPoolThread(threadPoolId, threadId);\n}\n\nvoid GlobalThreadPoolListImpl::registerThreadPool(\n    ThreadPoolListHook* threadPoolId, std::string name) {\n  PoolInfo info;\n  info.name = std::move(name);\n  info.addr = threadPoolId;\n  pools_.vector().push_back(std::move(info));\n}\n\nvoid GlobalThreadPoolListImpl::unregisterThreadPool(\n    ThreadPoolListHook* threadPoolId) {\n  auto& vector = pools_.vector();\n  vector.erase(\n      std::remove_if(\n          vector.begin(),\n          vector.end(),\n          [=](PoolInfo& i) { return i.addr == threadPoolId; }),\n      vector.end());\n}\n\nvoid GlobalThreadPoolListImpl::registerThreadPoolThread(\n    ThreadPoolListHook* threadPoolId,\n    std::thread::id threadId,\n    uint64_t osThreadId) {\n  auto* pool = pools_.getPool(threadPoolId);\n  if (pool == nullptr) {\n    return;\n  }\n\n  auto& threads = pool->threads;\n  auto& osids = pool->osThreadIds;\n  threads.push_back(threadId);\n  osids.push_back(osThreadId);\n}\n\nvoid GlobalThreadPoolListImpl::unregisterThreadPoolThread(\n    ThreadPoolListHook* threadPoolId, std::thread::id threadId) {\n  auto* pool = pools_.getPool(threadPoolId);\n  if (pool == nullptr) {\n    return;\n  }\n\n  auto& threads = pool->threads;\n  auto& osids = pool->osThreadIds;\n  DCHECK_EQ(threads.size(), osids.size());\n  for (unsigned i = 0; i < threads.size(); ++i) {\n    if (threads[i] == threadId) {\n      threads.erase(threads.begin() + i);\n      osids.erase(osids.begin() + i);\n      break;\n    }\n  }\n}\n\nThreadListHook::ThreadListHook(\n    ThreadPoolListHook* poolId, std::thread::id threadId, uint64_t osThreadId) {\n  poolId_ = poolId;\n  threadId_ = threadId;\n  osThreadId_ = osThreadId;\n}\n\nThreadListHook::~ThreadListHook() {\n  GlobalThreadPoolList::instance().unregisterThreadPoolThread(\n      poolId_, threadId_);\n}\n\n} // namespace debugger_detail\n\nThreadPoolListHook::ThreadPoolListHook(std::string name) {\n  debugger_detail::GlobalThreadPoolList::instance().registerThreadPool(\n      this, std::move(name));\n}\n\nThreadPoolListHook::~ThreadPoolListHook() {\n  debugger_detail::GlobalThreadPoolList::instance().unregisterThreadPool(this);\n}\n\nvoid ThreadPoolListHook::registerThread() {\n  uint64_t ostid = folly::getOSThreadID();\n  debugger_detail::GlobalThreadPoolList::instance().registerThreadPoolThread(\n      this, std::this_thread::get_id(), ostid);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/GlobalThreadPoolList.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <folly/Indestructible.h>\n#include <folly/Synchronized.h>\n#include <folly/ThreadLocal.h>\n\nnamespace folly {\n\n/**\n * A hook for tracking which threads belong to which thread pools.\n * This is used only by a gdb extension to aid in debugging. You won't be able\n * to see any useful information from within C++ code.\n *\n * An instance of ThreadPoolListHook should be created in the thread pool class\n * that you want to keep track of. Then, to register a thread you call\n * registerThread() on your instance of ThreadPoolListHook from that thread.\n *\n * When a thread exits it will be removed from the list\n * When the thread pool is destroyed, it will be removed from the list\n */\nclass ThreadPoolListHook {\n public:\n  /**\n   * Name is used to identify the thread pool when listing threads.\n   */\n  explicit ThreadPoolListHook(std::string name);\n  ~ThreadPoolListHook();\n\n  /**\n   * Call this from any new thread that the thread pool creates.\n   */\n  void registerThread();\n\n  ThreadPoolListHook(const ThreadPoolListHook& other) = delete;\n  ThreadPoolListHook& operator=(const ThreadPoolListHook&) = delete;\n\n private:\n  ThreadPoolListHook();\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/IOExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n\nnamespace folly {\nclass EventBase;\n} // namespace folly\n\nnamespace folly {\n\n// An IOExecutor is an executor that operates on at least one EventBase.  One of\n// these EventBases should be accessible via getEventBase(). The event base\n// returned by a call to getEventBase() is implementation dependent.\n//\n// Note that IOExecutors don't necessarily loop on the base themselves - for\n// instance, EventBase itself is an IOExecutor but doesn't drive itself.\n//\n// Implementations of IOExecutor are eligible to become the global IO executor,\n// returned on every call to getIOExecutor(), via setIOExecutor().\n// These functions are declared in GlobalExecutor.h\n//\n// If getIOExecutor is called and none has been set, a default global\n// IOThreadPoolExecutor will be created and returned.\nclass IOExecutor : public virtual folly::Executor {\n public:\n  ~IOExecutor() override = default;\n  virtual folly::EventBase* getEventBase() = 0;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/IOObjectCache.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n\n#include <folly/ThreadLocal.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\n\n/*\n * IOObjectCache manages objects of type T that are dependent on an EventBase\n * provided by the global IOExecutor.\n *\n * Provide a factory that creates T objects given an EventBase, and get() will\n * lazily create T objects based on an EventBase from the global IOExecutor.\n * These are stored thread locally - for a given pair of event base and calling\n * thread there will only be one T object created.\n *\n * The primary use case is for managing objects that need to do async IO on an\n * event base (e.g. thrift clients) that can be used outside the IO thread\n * without much hassle. For instance, you could use this to manage Thrift\n * clients that are only ever called from within other threads without the\n * calling thread needing to know anything about the IO threads that the clients\n * will do their work on.\n */\ntemplate <class T>\nclass IOObjectCache {\n public:\n  using TFactory = std::function<std::shared_ptr<T>(folly::EventBase*)>;\n\n  IOObjectCache() = default;\n  explicit IOObjectCache(TFactory factory) : factory_(std::move(factory)) {}\n\n  std::shared_ptr<T> get() {\n    CHECK(factory_);\n    auto eb = getIOExecutor()->getEventBase();\n    CHECK(eb);\n    auto it = cache_->find(eb);\n    if (it == cache_->end()) {\n      auto p = cache_->insert(std::make_pair(eb, factory_(eb)));\n      it = p.first;\n    }\n    return it->second;\n  }\n\n  void setFactory(TFactory factory) { factory_ = std::move(factory); }\n\n private:\n  folly::ThreadLocal<std::map<folly::EventBase*, std::shared_ptr<T>>> cache_;\n  TFactory factory_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/IOThreadPoolDeadlockDetectorObserver.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <folly/concurrency/DeadlockDetector.h>\n#include <folly/executors/IOThreadPoolDeadlockDetectorObserver.h>\n\nnamespace folly {\n\nIOThreadPoolDeadlockDetectorObserver::IOThreadPoolDeadlockDetectorObserver(\n    DeadlockDetectorFactory* deadlockDetectorFactory, const std::string& name)\n    : name_(name), deadlockDetectorFactory_(deadlockDetectorFactory) {}\n\nvoid IOThreadPoolDeadlockDetectorObserver::registerEventBase(\n    EventBase& evb) noexcept {\n  if (!deadlockDetectorFactory_) {\n    return;\n  }\n\n  evb.runInEventBaseThread([this, &evb] {\n    auto tid = folly::getOSThreadID();\n    auto name = name_ + \":\" + folly::to<std::string>(tid);\n    auto deadlockDetector = deadlockDetectorFactory_->create(&evb, name);\n    detectors_.wlock()->insert_or_assign(&evb, std::move(deadlockDetector));\n  });\n}\n\nvoid IOThreadPoolDeadlockDetectorObserver::unregisterEventBase(\n    EventBase& evb) noexcept {\n  if (!deadlockDetectorFactory_) {\n    return;\n  }\n\n  detectors_.wlock()->erase(&evb);\n}\n\n/* static */ std::unique_ptr<IOThreadPoolDeadlockDetectorObserver>\nIOThreadPoolDeadlockDetectorObserver::create(const std::string& name) {\n  auto* deadlockDetectorFactory = DeadlockDetectorFactory::instance();\n  return std::make_unique<IOThreadPoolDeadlockDetectorObserver>(\n      deadlockDetectorFactory, name);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/IOThreadPoolDeadlockDetectorObserver.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Singleton.h>\n#include <folly/concurrency/DeadlockDetector.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/executors/ThreadPoolExecutor.h>\n\nnamespace folly {\n\nclass IOThreadPoolDeadlockDetectorObserver\n    : public folly::IOThreadPoolExecutorBase::IOObserver {\n public:\n  IOThreadPoolDeadlockDetectorObserver(\n      folly::DeadlockDetectorFactory* deadlockDetectorFactory,\n      const std::string& name);\n\n  void registerEventBase(EventBase& evb) noexcept override;\n  void unregisterEventBase(EventBase& evb) noexcept override;\n\n  static std::unique_ptr<IOThreadPoolDeadlockDetectorObserver> create(\n      const std::string& name);\n\n private:\n  const std::string name_;\n  folly::DeadlockDetectorFactory* deadlockDetectorFactory_;\n  folly::Synchronized<std::unordered_map<\n      folly::EventBase*,\n      std::unique_ptr<folly::DeadlockDetector>>>\n      detectors_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/IOThreadPoolExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/IOThreadPoolExecutor.h>\n\n#include <glog/logging.h>\n\n#include <folly/detail/MemoryIdler.h>\n#include <folly/portability/GFlags.h>\n\nFOLLY_GFLAGS_DEFINE_bool(\n    dynamic_iothreadpoolexecutor,\n    true,\n    \"IOThreadPoolExecutor will dynamically create threads\");\n\nFOLLY_GFLAGS_DEFINE_int32(\n    folly_iothreadpoolexecutor_max_read_at_once,\n    -1,\n    \"IOThreadPoolExecutor will use this value as default for maxReadAtOnce in its event bases, valid values are [0, inf)\");\n\nnamespace folly {\n\nnamespace {\n\nusing folly::detail::MemoryIdler;\n\n/* Class that will free jemalloc caches and madvise the stack away\n * if the event loop is unused for some period of time\n */\nclass MemoryIdlerTimeout : public AsyncTimeout, public EventBase::LoopCallback {\n public:\n  explicit MemoryIdlerTimeout(EventBase* b) : AsyncTimeout(b), base_(b) {}\n\n  void timeoutExpired() noexcept override {\n    idled_ = true;\n    timerRunning_ = false;\n  }\n\n  void runLoopCallback() noexcept override {\n    if (idled_) {\n      if (num_ == 0) {\n        MemoryIdler::flushLocalMallocCaches();\n        MemoryIdler::unmapUnusedStack(MemoryIdler::kDefaultStackToRetain);\n      }\n\n      idled_ = false;\n      num_ = 0;\n    } else {\n      if (!timerRunning_) {\n        timerRunning_ = true;\n        std::chrono::steady_clock::duration idleTimeout =\n            MemoryIdler::defaultIdleTimeout.load(std::memory_order_acquire);\n\n        idleTimeout = MemoryIdler::getVariationTimeout(idleTimeout);\n\n        scheduleTimeout(\n            static_cast<uint32_t>(\n                std::chrono::duration_cast<std::chrono::milliseconds>(\n                    idleTimeout)\n                    .count()));\n      } else {\n        num_++;\n      }\n    }\n\n    // reschedule this callback for the next event loop.\n    base_->runBeforeLoop(this);\n  }\n\n private:\n  EventBase* base_;\n  bool idled_{false};\n  bool timerRunning_{false};\n  size_t num_{0};\n};\n\n} // namespace\n\n// IOThreadPoolExecutorBase\nEventBase* IOThreadPoolExecutor::getEventBase(\n    ThreadPoolExecutor::ThreadHandle* h) {\n  auto thread = dynamic_cast<IOThread*>(h);\n\n  if (thread) {\n    return thread->eventBase;\n  }\n\n  return nullptr;\n}\n\n// IOThreadPoolExecutor\nIOThreadPoolExecutor::IOThreadPoolExecutor(\n    size_t numThreads,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    EventBaseManager* ebm,\n    Options options)\n    : IOThreadPoolExecutor(\n          numThreads,\n          FLAGS_dynamic_iothreadpoolexecutor ? 0 : numThreads,\n          std::move(threadFactory),\n          ebm,\n          std::move(options)) {}\n\nIOThreadPoolExecutor::IOThreadPoolExecutor(\n    size_t maxThreads,\n    size_t minThreads,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    EventBaseManager* ebm,\n    Options options)\n    : IOThreadPoolExecutorBase(\n          maxThreads, minThreads, std::move(threadFactory)),\n      isWaitForAll_(options.waitForAll),\n      nextThread_(0),\n      eventBaseManager_(ebm),\n      maxReadAtOnce_(options.maxReadAtOnce) {\n  setNumThreads(maxThreads);\n  registerThreadPoolExecutor(this);\n  if (options.enableThreadIdCollection) {\n    threadIdCollector_ = std::make_unique<ThreadIdWorkerProvider>();\n  }\n}\n\nIOThreadPoolExecutor::~IOThreadPoolExecutor() {\n  deregisterThreadPoolExecutor(this);\n  stop();\n}\n\nvoid IOThreadPoolExecutor::add(Func func) {\n  add(std::move(func), std::chrono::milliseconds(0));\n}\n\nvoid IOThreadPoolExecutor::add(\n    Func func, std::chrono::milliseconds expiration, Func expireCallback) {\n  ensureActiveThreads();\n  std::shared_lock r{threadListLock_};\n  if (threadList_.get().empty()) {\n    throw std::runtime_error(\"No threads available\");\n  }\n  auto ioThread = pickThread();\n\n  auto task = Task(std::move(func), expiration, std::move(expireCallback));\n  registerTaskEnqueue(task);\n  auto wrappedFunc = [this, ioThread, task = std::move(task)]() mutable {\n    runTask(ioThread, std::move(task));\n    ioThread->pendingTasks--;\n  };\n\n  ioThread->pendingTasks++;\n  ioThread->eventBase->runInEventBaseThread(std::move(wrappedFunc));\n}\n\nstd::shared_ptr<IOThreadPoolExecutor::IOThread>\nIOThreadPoolExecutor::pickThread() {\n  auto& me = *thisThread_;\n  auto& ths = threadList_.get();\n  // When new task is added to IOThreadPoolExecutor, a thread is chosen for it\n  // to be executed on, thisThread_ is by default chosen, however, if the new\n  // task is added by the clean up operations on thread destruction, thisThread_\n  // is not an available thread anymore, thus, always check whether or not\n  // thisThread_ is an available thread before choosing it.\n  if (me && threadList_.contains(me)) {\n    return me;\n  }\n  auto n = ths.size();\n  if (n == 0) {\n    // XXX I think the only way this can happen is if somebody calls\n    // getEventBase (1) from one of the executor's threads while the executor\n    // is stopping or getting downsized to zero or (2) from outside the executor\n    // when it has no threads. In the first case, it's not obvious what the\n    // correct behavior should be-- do we really want to return ourselves even\n    // though we're about to exit? (The comment above seems to imply no.) In\n    // the second case, `!me` so we'll crash anyway.\n    return me;\n  }\n  auto thread = ths[nextThread_++ % n];\n  return std::static_pointer_cast<IOThread>(thread);\n}\n\nEventBase* IOThreadPoolExecutor::getEventBase() {\n  ensureActiveThreads();\n  std::shared_lock r{threadListLock_};\n  if (threadList_.get().empty()) {\n    throw std::runtime_error(\"No threads available\");\n  }\n  return pickThread()->eventBase;\n}\n\nstd::vector<Executor::KeepAlive<EventBase>>\nIOThreadPoolExecutor::getAllEventBases() {\n  ensureMaxActiveThreads();\n  std::vector<Executor::KeepAlive<EventBase>> evbs;\n  std::shared_lock r{threadListLock_};\n  const auto& threads = threadList_.get();\n  evbs.reserve(threads.size());\n  for (const auto& thr : threads) {\n    evbs.emplace_back(static_cast<IOThread&>(*thr).eventBase);\n  }\n  return evbs;\n}\n\nEventBaseManager* IOThreadPoolExecutor::getEventBaseManager() {\n  return eventBaseManager_;\n}\n\nstd::shared_ptr<ThreadPoolExecutor::Thread> IOThreadPoolExecutor::makeThread() {\n  return std::make_shared<IOThread>();\n}\n\nvoid IOThreadPoolExecutor::threadRun(ThreadPtr thread) {\n  this->threadPoolHook_.registerThread();\n\n  const auto& ioThread = *thisThread_ =\n      std::static_pointer_cast<IOThread>(thread);\n  ioThread->eventBase = eventBaseManager_->getEventBase();\n  if (maxReadAtOnce_) {\n    ioThread->eventBase->setMaxReadAtOnce(*maxReadAtOnce_);\n  }\n\n  auto tid = folly::getOSThreadID();\n  if (threadIdCollector_) {\n    threadIdCollector_->addTid(tid);\n  }\n  SCOPE_EXIT {\n    if (threadIdCollector_) {\n      threadIdCollector_->removeTid(tid);\n    }\n  };\n\n  auto idler = std::make_unique<MemoryIdlerTimeout>(ioThread->eventBase);\n  ioThread->eventBase->runBeforeLoop(idler.get());\n\n  ioThread->eventBase->runInEventBaseThread([thread] {\n    thread->startupBaton.post();\n  });\n  {\n    ExecutorBlockingGuard guard{\n        ExecutorBlockingGuard::TrackTag{}, this, getName()};\n    while (ioThread->shouldRun) {\n      ioThread->eventBase->loopForever();\n    }\n    if (isJoin_) {\n      while (ioThread->pendingTasks > 0) {\n        ioThread->eventBase->loopOnce();\n      }\n    }\n    idler.reset();\n    if (isWaitForAll_) {\n      // some tasks, like thrift asynchronous calls, create additional\n      // event base hookups, let's wait till all of them complete.\n      ioThread->eventBase->loop();\n    }\n  }\n\n  std::lock_guard guard(ioThread->eventBaseShutdownMutex_);\n  ioThread->eventBase = nullptr;\n  eventBaseManager_->clearEventBase();\n}\n\n// threadListLock_ is writelocked\nvoid IOThreadPoolExecutor::stopThreads(size_t n) {\n  std::vector<ThreadPtr> stoppedThreads;\n  stoppedThreads.reserve(n);\n  for (size_t i = 0; i < n; i++) {\n    const auto ioThread =\n        std::static_pointer_cast<IOThread>(threadList_.get()[i]);\n    for (auto& o : observers_) {\n      o->threadStopped(ioThread.get());\n      handleObserverUnregisterThread(ioThread.get(), *o);\n    }\n    ioThread->shouldRun = false;\n    stoppedThreads.push_back(ioThread);\n    std::lock_guard guard(ioThread->eventBaseShutdownMutex_);\n    if (ioThread->eventBase) {\n      ioThread->eventBase->terminateLoopSoon();\n    }\n  }\n  for (const auto& thread : stoppedThreads) {\n    stoppedThreadProcessedTasks_ += thread->processedTasks;\n    thread->processedTasks = 0;\n    stoppedThreads_.add(folly::copy(thread));\n    threadList_.remove(thread);\n  }\n}\n\n// threadListLock_ is readlocked\nsize_t IOThreadPoolExecutor::getPendingTaskCountImpl() const {\n  size_t count = 0;\n  for (const auto& thread : threadList_.get()) {\n    auto ioThread = std::static_pointer_cast<IOThread>(thread);\n    size_t pendingTasks = ioThread->pendingTasks;\n    if (pendingTasks > 0 && !ioThread->idle.load(std::memory_order_relaxed)) {\n      pendingTasks--;\n    }\n    count += pendingTasks;\n  }\n  return count;\n}\n\nvoid IOThreadPoolExecutor::handleObserverRegisterThread(\n    ThreadHandle* h, Observer& observer) noexcept {\n  auto thread = CHECK_NOTNULL(dynamic_cast<IOThread*>(h));\n  if (auto ioObserver = dynamic_cast<IOObserver*>(&observer)) {\n    ioObserver->registerEventBase(*thread->eventBase);\n  }\n}\n\nvoid IOThreadPoolExecutor::handleObserverUnregisterThread(\n    ThreadHandle* h, Observer& observer) noexcept {\n  auto thread = CHECK_NOTNULL(dynamic_cast<IOThread*>(h));\n  if (auto ioObserver = dynamic_cast<IOObserver*>(&observer)) {\n    ioObserver->unregisterEventBase(*thread->eventBase);\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/IOThreadPoolExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/executors/IOExecutor.h>\n#include <folly/executors/QueueObserver.h>\n#include <folly/executors/ThreadPoolExecutor.h>\n#include <folly/io/async/EventBaseManager.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\nFOLLY_GFLAGS_DECLARE_int32(folly_iothreadpoolexecutor_max_read_at_once);\n\nnamespace folly {\n\nFOLLY_PUSH_WARNING\n// Suppress \"IOThreadPoolExecutor inherits DefaultKeepAliveExecutor\n// keepAliveAcquire/keepAliveRelease via dominance\"\nFOLLY_MSVC_DISABLE_WARNING(4250)\n\nclass IOThreadPoolExecutorBase\n    : public ThreadPoolExecutor,\n      public IOExecutor,\n      public GetThreadIdCollector {\n public:\n  using ThreadPoolExecutor::ThreadPoolExecutor;\n\n  ~IOThreadPoolExecutorBase() override = default;\n\n  folly::EventBase* getEventBase() override = 0;\n\n  virtual std::vector<folly::Executor::KeepAlive<folly::EventBase>>\n  getAllEventBases() = 0;\n\n  virtual folly::EventBaseManager* getEventBaseManager() = 0;\n\n  class IOObserver : public Observer {\n   public:\n    virtual void registerEventBase(EventBase&) noexcept {}\n    virtual void unregisterEventBase(EventBase&) noexcept {}\n  };\n};\n\n/**\n * A Thread Pool for IO bound tasks\n *\n * @note Uses event_fd for notification, and waking an epoll loop.\n * There is one queue (NotificationQueue specifically) per thread/epoll.\n * If the thread is already running and not waiting on epoll,\n * we don't make any additional syscalls to wake up the loop,\n * just put the new task in the queue.\n * If any thread has been waiting for more than a few seconds,\n * its stack is madvised away. Currently however tasks are scheduled round\n * robin on the queues, so unless there is no work going on,\n * this isn't very effective.\n * Since there is one queue per thread, there is hardly any contention\n * on the queues - so a simple spinlock around an std::deque is used for\n * the tasks. There is no max queue size.\n * By default, there is one thread per core - it usually doesn't make sense to\n * have more IO threads than this, assuming they don't block.\n *\n * @note ::getEventBase() will return an EventBase you can schedule IO work on\n * directly, chosen round-robin.\n *\n * @note N.B. For this thread pool, stop() behaves like join() because\n * outstanding tasks belong to the event base and will be executed upon its\n * destruction.\n */\nclass IOThreadPoolExecutor : public IOThreadPoolExecutorBase {\n public:\n  struct Options {\n    Options()\n        : waitForAll(false),\n          enableThreadIdCollection(false),\n          maxReadAtOnce(\n              FLAGS_folly_iothreadpoolexecutor_max_read_at_once < 0\n                  ? decltype(maxReadAtOnce){}\n                  : decltype(maxReadAtOnce){\n                        FLAGS_folly_iothreadpoolexecutor_max_read_at_once}) {}\n\n    Options& setWaitForAll(bool b) {\n      this->waitForAll = b;\n      return *this;\n    }\n    Options& setEnableThreadIdCollection(bool b) {\n      this->enableThreadIdCollection = b;\n      return *this;\n    }\n    Options& setMaxReadAtOnce(uint32_t w) {\n      this->maxReadAtOnce = w;\n      return *this;\n    }\n\n    bool waitForAll;\n    bool enableThreadIdCollection;\n    std::optional<uint32_t> maxReadAtOnce;\n  };\n\n  explicit IOThreadPoolExecutor(\n      size_t numThreads,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"IOThreadPool\"),\n      folly::EventBaseManager* ebm = folly::EventBaseManager::get(),\n      Options options = Options());\n\n  IOThreadPoolExecutor(\n      size_t maxThreads,\n      size_t minThreads,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"IOThreadPool\"),\n      folly::EventBaseManager* ebm = folly::EventBaseManager::get(),\n      Options options = Options());\n\n  ~IOThreadPoolExecutor() override;\n\n  void add(Func func) override;\n  void add(\n      Func func,\n      std::chrono::milliseconds expiration,\n      Func expireCallback = nullptr) override;\n\n  folly::EventBase* getEventBase() override;\n\n  // Ensures that the maximum number of active threads is running and returns\n  // the EventBase associated with each thread.\n  std::vector<folly::Executor::KeepAlive<folly::EventBase>> getAllEventBases()\n      override;\n\n  static folly::EventBase* getEventBase(ThreadPoolExecutor::ThreadHandle* h);\n\n  folly::EventBaseManager* getEventBaseManager() override;\n\n  // Returns nullptr unless explicitly enabled through constructor\n  folly::WorkerProvider* getThreadIdCollector() override {\n    return threadIdCollector_.get();\n  }\n\n protected:\n  struct alignas(Thread) IOThread : public Thread {\n    std::atomic<bool> shouldRun{true};\n    std::atomic<size_t> pendingTasks{0};\n    folly::EventBase* eventBase{nullptr};\n    std::mutex eventBaseShutdownMutex_;\n  };\n\n  void handleObserverRegisterThread(\n      ThreadHandle* h, Observer& observer) noexcept override;\n  void handleObserverUnregisterThread(\n      ThreadHandle* h, Observer& observer) noexcept override;\n\n private:\n  ThreadPtr makeThread() override;\n  std::shared_ptr<IOThread> pickThread();\n  void threadRun(ThreadPtr thread) override;\n  void stopThreads(size_t n) override;\n  size_t getPendingTaskCountImpl() const override final;\n  const bool isWaitForAll_; // whether to wait till event base loop exits\n  relaxed_atomic<size_t> nextThread_;\n  folly::ThreadLocal<std::shared_ptr<IOThread>> thisThread_;\n  folly::EventBaseManager* eventBaseManager_;\n  std::unique_ptr<ThreadIdWorkerProvider> threadIdCollector_;\n  const std::optional<uint32_t> maxReadAtOnce_;\n};\n\nFOLLY_POP_WARNING\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/InlineExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/InlineExecutor.h>\n\n#include <folly/Indestructible.h>\n\nnamespace folly {\n\nInlineExecutor& InlineExecutor::instance_slow() noexcept {\n  static Indestructible<InlineExecutor> instance;\n  cache.store(&*instance, std::memory_order_release);\n  return *instance;\n}\n\nstd::atomic<InlineExecutor*> InlineExecutor::cache;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/InlineExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/CPortability.h>\n#include <folly/CppAttributes.h>\n#include <folly/Executor.h>\n\nnamespace folly {\n\nclass InlineLikeExecutor : public virtual Executor {\n public:\n  virtual ~InlineLikeExecutor() override {}\n};\n\n/// When work is \"queued\", execute it immediately inline.\n/// Usually when you think you want this, you actually want a\n/// QueuedImmediateExecutor.\nclass InlineExecutor : public InlineLikeExecutor {\n public:\n  FOLLY_ERASE static InlineExecutor& instance() noexcept {\n    auto const value = cache.load(std::memory_order_acquire);\n    return value ? *value : instance_slow();\n  }\n\n  void add(Func f) override { f(); }\n\n private:\n  [[FOLLY_ATTR_GNU_COLD]] static InlineExecutor& instance_slow() noexcept;\n\n  static std::atomic<InlineExecutor*> cache;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ManualExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ManualExecutor.h>\n\n#include <cstring>\n#include <string>\n#include <tuple>\n\nnamespace folly {\n\nManualExecutor::~ManualExecutor() {\n  while (keepAliveCount_.load(std::memory_order_relaxed)) {\n    drive();\n  }\n  drain();\n}\n\nvoid ManualExecutor::add(Func callback) {\n  std::lock_guard lock(lock_);\n  funcs_.emplace(std::move(callback));\n  sem_.post();\n}\n\nsize_t ManualExecutor::run() {\n  size_t count;\n  size_t n;\n  Func func;\n\n  {\n    std::lock_guard lock(lock_);\n\n    while (!scheduledFuncs_.empty()) {\n      auto& sf = scheduledFuncs_.top();\n      if (sf.time > now_) {\n        break;\n      }\n      funcs_.emplace(sf.moveOutFunc());\n      scheduledFuncs_.pop();\n    }\n\n    n = funcs_.size();\n  }\n\n  for (count = 0; count < n; count++) {\n    {\n      std::lock_guard lock(lock_);\n      if (funcs_.empty()) {\n        break;\n      }\n\n      // Balance the semaphore so it doesn't grow without bound\n      // if nobody is calling wait().\n      // This may fail (with EAGAIN), that's fine.\n      sem_.tryWait();\n\n      func = std::move(funcs_.front());\n      funcs_.pop();\n    }\n    func();\n    func = nullptr;\n  }\n\n  return count;\n}\n\nsize_t ManualExecutor::step() {\n  Func func;\n\n  {\n    std::lock_guard lock(lock_);\n\n    if (funcs_.empty()) {\n      return 0;\n    }\n\n    // Balance the semaphore so it doesn't grow without bound\n    // if nobody is calling wait().\n    // This may fail (with EAGAIN), that's fine.\n    sem_.tryWait();\n\n    func = std::move(funcs_.front());\n    funcs_.pop();\n  }\n  func();\n  return 1;\n}\n\nsize_t ManualExecutor::drain() {\n  size_t tasksRun = 0;\n  size_t tasksForSingleRun = 0;\n  while ((tasksForSingleRun = run()) != 0) {\n    tasksRun += tasksForSingleRun;\n  }\n  return tasksRun;\n}\n\nvoid ManualExecutor::wait() {\n  while (true) {\n    {\n      std::lock_guard lock(lock_);\n      if (!funcs_.empty()) {\n        break;\n      }\n    }\n\n    sem_.wait();\n  }\n}\n\nvoid ManualExecutor::advanceTo(TimePoint const& t) {\n  if (t > now_) {\n    now_ = t;\n  }\n  run();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ManualExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdio>\n#include <memory>\n#include <mutex>\n#include <queue>\n\n#include <folly/executors/DrivableExecutor.h>\n#include <folly/executors/ScheduledExecutor.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/synchronization/LifoSem.h>\n\nnamespace folly {\n/// A ManualExecutor only does work when you turn the crank, by calling\n/// run() or indirectly with makeProgress() or waitFor().\n///\n/// The clock for a manual executor starts at 0 and advances only when you\n/// ask it to. i.e. time is also under manual control.\n///\n/// NB No attempt has been made to make anything other than add and schedule\n/// threadsafe.\nclass ManualExecutor\n    : public DrivableExecutor,\n      public ScheduledExecutor,\n      public SequencedExecutor {\n public:\n  ~ManualExecutor() override;\n\n  void add(Func) override;\n\n  /// Do work. Returns the number of functions that were executed (maybe 0).\n  /// Non-blocking, in the sense that we don't wait for work (we can't\n  /// control whether one of the functions blocks).\n  /// This is stable, it will not chase an ever-increasing tail of work.\n  /// This also means, there may be more work available to perform at the\n  /// moment that this returns.\n  size_t run();\n\n  /// Execute only the function at the front of the queue (maybe 0)\n  /// and remove it from the queue.\n  /// Non blocking where it does not wait for work if none exists\n  /// Returns 1 if a function was executed and 0 otherwise\n  size_t step();\n\n  // Do work until there is no more work to do.\n  // Returns the number of functions that were executed (maybe 0).\n  // Unlike run, this method is not stable. It will chase an infinite tail of\n  // work so should be used with care.\n  // There will be no work available to perform at the moment that this\n  // returns.\n  size_t drain();\n\n  /// Wait for work to do.\n  void wait();\n\n  /// Wait for work to do, and do it.\n  void makeProgress() {\n    wait();\n    run();\n  }\n\n  /// Implements DrivableExecutor\n  void drive() override { makeProgress(); }\n\n  /// makeProgress until this Future is ready.\n  template <class F>\n  void waitFor(F const& f) {\n    // TODO(5427828)\n#if 0\n      while (!f.isReady())\n        makeProgress();\n#else\n    while (!f.isReady()) {\n      run();\n    }\n#endif\n  }\n\n  void scheduleAt(Func&& f, TimePoint const& t) override {\n    std::lock_guard lock(lock_);\n    scheduledFuncs_.emplace(t, std::move(f));\n    sem_.post();\n  }\n\n  /// Advance the clock. The clock never advances on its own.\n  /// Advancing the clock causes some work to be done, if work is available\n  /// to do (perhaps newly available because of the advanced clock).\n  /// If dur is <= 0 this is a noop.\n  void advance(Duration const& dur) { advanceTo(now_ + dur); }\n\n  /// Advance the clock to this absolute time. If t is <= now(),\n  /// this is a noop.\n  void advanceTo(TimePoint const& t);\n\n  TimePoint now() override { return now_; }\n\n  /// Flush the function queue. Destroys all stored functions without\n  /// executing them. Returns number of removed functions.\n  std::size_t clear() {\n    std::queue<Func> funcs;\n    std::priority_queue<ScheduledFunc> scheduled_funcs;\n\n    {\n      std::lock_guard lock(lock_);\n      funcs_.swap(funcs);\n      scheduledFuncs_.swap(scheduled_funcs);\n    }\n\n    return funcs.size() + scheduled_funcs.size();\n  }\n\n  bool keepAliveAcquire() noexcept override {\n    keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    auto keepAliveCount = keepAliveCount_.load(std::memory_order_relaxed);\n    do {\n      DCHECK(keepAliveCount > 0);\n      if (keepAliveCount == 1) {\n        add([this] {\n          // the final count *must* be released from this executor or else if we\n          // are mid-destructor we have a data race\n          keepAliveCount_.fetch_sub(1, std::memory_order_relaxed);\n        });\n        return;\n      }\n    } while (!keepAliveCount_.compare_exchange_weak(\n        keepAliveCount,\n        keepAliveCount - 1,\n        std::memory_order_release,\n        std::memory_order_relaxed));\n  }\n\n private:\n  std::mutex lock_;\n  std::queue<Func> funcs_;\n  LifoSem sem_;\n\n  // helper class to enable ordering of scheduled events in the priority\n  // queue\n  struct ScheduledFunc {\n    TimePoint time;\n    size_t ordinal;\n    Func mutable func;\n\n    ScheduledFunc(TimePoint const& t, Func&& f) : time(t), func(std::move(f)) {\n      static size_t seq = 0;\n      ordinal = seq++;\n    }\n\n    bool operator<(ScheduledFunc const& b) const {\n      // Earlier-scheduled things must be *higher* priority\n      // in the max-based std::priority_queue\n      if (time == b.time) {\n        return ordinal > b.ordinal;\n      }\n      return time > b.time;\n    }\n\n    Func&& moveOutFunc() const { return std::move(func); }\n  };\n  std::priority_queue<ScheduledFunc> scheduledFuncs_;\n  TimePoint now_ = TimePoint::min();\n\n  std::atomic<ssize_t> keepAliveCount_{0};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/MeteredExecutor-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <limits>\n\n#include <folly/io/async/AtomicNotificationQueue.h>\n\nnamespace folly {\nnamespace detail {\n\ntemplate <template <typename> class Atom>\nMeteredExecutorImpl<Atom>::MeteredExecutorImpl(\n    KeepAlive keepAlive, Options options)\n    : options_(std::move(options)), kaInner_(std::move(keepAlive)) {\n  CHECK_GE(options_.maxInQueue, 1);\n  CHECK_LT(options_.maxInQueue, uint32_t(1) << 31);\n}\n\ntemplate <template <typename> class Atom>\nMeteredExecutorImpl<Atom>::MeteredExecutorImpl(\n    std::unique_ptr<Executor> executor, Options options)\n    : MeteredExecutorImpl(getKeepAliveToken(*executor), std::move(options)) {\n  ownedExecutor_ = std::move(executor);\n}\n\ntemplate <template <typename> class Atom>\nstd::unique_ptr<QueueObserver> MeteredExecutorImpl<Atom>::setupQueueObserver() {\n  if (options_.enableQueueObserver) {\n    std::string name = \"unk\";\n    if (options_.name != \"\") {\n      name = options_.name;\n    }\n    if (auto factory = folly::QueueObserverFactory::make(\n            \"mex.\" + name, options_.numPriorities)) {\n      return factory->create(options_.priority);\n    }\n  }\n  return nullptr;\n}\n\ntemplate <template <typename> class Atom>\ntemplate <class F>\nvoid MeteredExecutorImpl<Atom>::modifyState(F f) {\n  uint64_t oldState = state_.load(std::memory_order_relaxed);\n  uint64_t newState;\n  do {\n    newState = f(oldState);\n    // Verify invariants: no more in-queue than allowed.\n    DCHECK_LE(newState >> kInQueueShift, options_.maxInQueue);\n    // No more in queue than pending tasks.\n    DCHECK_LE(newState >> kInQueueShift, newState & kSizeMask);\n  } while (!state_.compare_exchange_strong(\n      oldState,\n      newState,\n      std::memory_order_seq_cst,\n      std::memory_order_relaxed));\n}\n\ntemplate <template <typename> class Atom>\nvoid MeteredExecutorImpl<Atom>::add(Func func) {\n  auto task = Task(std::move(func), RequestContext::saveContext());\n  if (queueObserver_) {\n    auto payload = queueObserver_->onEnqueued(task.requestContext());\n    task.setQueueObserverPayload(payload);\n  }\n\n  queue_.enqueue(std::move(task));\n\n  bool shouldScheduleWorker;\n  modifyState([&](uint64_t state) {\n    state += kSizeInc;\n    CHECK_NE(state & kSizeMask, 0)\n        << \"Too many pending tasks in MeteredExecutor\";\n    if (!(state & kPausedBit) &&\n        ((state >> kInQueueShift) < options_.maxInQueue)) {\n      state += kInQueueInc;\n      shouldScheduleWorker = true;\n    } else {\n      shouldScheduleWorker = false;\n    }\n    return state;\n  });\n\n  if (shouldScheduleWorker) {\n    scheduleWorker();\n  }\n}\n\ntemplate <template <typename> class Atom>\nbool MeteredExecutorImpl<Atom>::pause() {\n  auto oldState = state_.fetch_or(kPausedBit, std::memory_order_relaxed);\n  return !(oldState & kPausedBit);\n}\n\ntemplate <template <typename> class Atom>\nbool MeteredExecutorImpl<Atom>::resume() {\n  bool wasPaused = false;\n  size_t workersToSchedule = 0;\n  modifyState([&](uint64_t state) {\n    if (state & kPausedBit) {\n      wasPaused = true;\n    } else {\n      wasPaused = false;\n      return state;\n    }\n    // Workers may have aborted without consuming tasks, reschedule them.\n    auto curSize = state & kSizeMask;\n    auto curInQueue = state >> kInQueueShift;\n    DCHECK_LE(curInQueue, options_.maxInQueue);\n    DCHECK_LE(curInQueue, curSize);\n    workersToSchedule =\n        std::min(static_cast<uint64_t>(options_.maxInQueue), curSize) -\n        curInQueue;\n    state &= ~kPausedBit;\n    state += workersToSchedule * kInQueueInc;\n    return state;\n  });\n\n  if (!wasPaused) {\n    return false;\n  }\n\n  for (size_t i = 0; i < workersToSchedule; ++i) {\n    scheduleWorker();\n  }\n  return true;\n}\n\ntemplate <template <typename> class Atom>\nvoid MeteredExecutorImpl<Atom>::Task::run() && {\n  folly::RequestContextScopeGuard rctxGuard{std::move(rctx_)};\n  invokeCatchingExns(\"MeteredExecutor\", std::exchange(func_, {}));\n}\n\ntemplate <template <typename> class Atom>\nvoid MeteredExecutorImpl<Atom>::worker() {\n  bool shouldAbort = false;\n  bool shouldRescheduleWorker = false;\n  modifyState([&](uint64_t state) {\n    if (state & kPausedBit) {\n      shouldAbort = true;\n      shouldRescheduleWorker = false;\n    } else {\n      shouldAbort = false;\n      // More work to do than workers in queue, re-schedule the worker without\n      // changing the in-queue count.\n      shouldRescheduleWorker = (state & kSizeMask) > (state >> kInQueueShift);\n      DCHECK_GT(state & kSizeMask, 0);\n      state -= kSizeInc;\n    }\n    if (!shouldRescheduleWorker) {\n      state -= kInQueueInc;\n    }\n    return state;\n  });\n\n  if (shouldAbort) {\n    return;\n  }\n  if (shouldRescheduleWorker) {\n    scheduleWorker();\n  }\n\n  Task task;\n  CHECK(queue_.try_dequeue(task));\n  std::move(task).run();\n}\n\ntemplate <template <typename> class Atom>\nvoid MeteredExecutorImpl<Atom>::scheduleWorker() {\n  folly::RequestContextScopeGuard rctxGuard{nullptr};\n  kaInner_->add([self = getKeepAliveToken(this)] { self->worker(); });\n}\n\ntemplate <template <typename> class Atom>\nMeteredExecutorImpl<Atom>::~MeteredExecutorImpl() {\n  joinKeepAlive();\n}\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/MeteredExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <optional>\n\n#include <folly/DefaultKeepAliveExecutor.h>\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/executors/QueueObserver.h>\n\nnamespace folly {\n\nnamespace detail {\n// Attaches a queue to an already existing executor and exposes Executor\n// interface to add tasks to that queue.\n// Consumption from this queue is \"metered\". Specifically, only a limited number\n// of tasks scheduled onto the resulting executor will be ever simultaneously\n// present in the wrapped executor's queue. This mechanism can be used e.g. to\n// attach lower priority queue to an already existing executor -\n// meaning tasks scheduled on the wrapper will, in practice, yield to task\n// scheduled directly on the wrapped executor.\n//\n// Notice that multi level priorities can be easily achieved via chaining,\n// for example:\n// auto hipri_exec = std::make_unique<CPUThreadPoolExecutor>(numThreads);\n// auto hipri_ka = getKeepAliveToken(hipri_exec.get());\n// auto mipri_exec = std::make_unique<MeteredExecutor>(hipri_ka);\n// auto mipri_ka = getKeepAliveToken(mipri_exec.get());\n// auto lopri_exec = std::make_unique<MeteredExecutor>(mipri_ka);\n// auto lopri_ka = getKeepAliveToken(lopri_exec.get());\ntemplate <template <typename> class Atom = std::atomic>\nclass MeteredExecutorImpl : public DefaultKeepAliveExecutor {\n public:\n  struct Options {\n    Options() {}\n    std::string name;\n    bool enableQueueObserver{false};\n    size_t numPriorities{1};\n    int8_t priority{0};\n    // Maximum number of tasks allowed in the wrapped executor's queue at any\n    // given time. This must be >= 1 and < 2^31.\n    uint32_t maxInQueue = 1;\n  };\n\n  using KeepAlive = Executor::KeepAlive<>;\n  // owning constructor\n  explicit MeteredExecutorImpl(\n      std::unique_ptr<Executor> exe, Options options = Options());\n  // non-owning constructor\n  explicit MeteredExecutorImpl(\n      KeepAlive keepAlive, Options options = Options());\n  ~MeteredExecutorImpl() override;\n\n  void add(Func func) override;\n\n  size_t pendingTasks() const { return state_ & kSizeMask; }\n\n  // Pause executing tasks. Subsequent resume() necessary to\n  // start executing tasks again. Can be invoked from any thread and only\n  // prevents scheduling of future tasks for executions i.e. doesn't impact\n  // tasks already scheduled. The method returns `false` if we invoke `pause`\n  // on an already paused executor.\n  bool pause();\n\n  // Resume executing tasks. no-op if not paused. Can be invoked from any\n  // thread. Returns `false` if `resume` is invoked on an executor that's\n  // not paused.\n  bool resume();\n\n private:\n  class Task {\n   public:\n    Task() = default;\n\n    explicit Task(Func&& func, std::shared_ptr<RequestContext>&& rctx)\n        : func_(std::move(func)), rctx_(std::move(rctx)) {}\n\n    void setQueueObserverPayload(intptr_t newValue) {\n      queueObserverPayload_ = newValue;\n    }\n\n    intptr_t getQueueObserverPayload() const { return queueObserverPayload_; }\n\n    void run() &&;\n\n    RequestContext* requestContext() const { return rctx_.get(); }\n\n   private:\n    Func func_;\n    std::shared_ptr<RequestContext> rctx_;\n    intptr_t queueObserverPayload_;\n  };\n\n  template <class F>\n  void modifyState(F f);\n\n  std::unique_ptr<folly::QueueObserver> setupQueueObserver();\n  void worker();\n  void scheduleWorker();\n\n  const Options options_;\n  std::unique_ptr<Executor> ownedExecutor_;\n  const KeepAlive kaInner_;\n  const std::unique_ptr<folly::QueueObserver> queueObserver_{\n      setupQueueObserver()};\n\n  // State is encoded in a 64-bit word so we can modify it atomically (using\n  // modifyState(), which verifies the invariants.\n  // Layout:\n  // [<workers in queue> 31 bits][<paused> 1 bit>][<pending tasks> 32 bits]\n  static constexpr uint64_t kSizeInc = 1;\n  static constexpr uint64_t kSizeMask = (uint64_t{1} << 32) - 1;\n  static constexpr uint64_t kPausedBit = uint64_t{1} << 32;\n  static constexpr uint64_t kInQueueShift = 33;\n  static constexpr uint64_t kInQueueInc = uint64_t{1} << kInQueueShift;\n  alignas(folly::cacheline_align_v) std::atomic<uint64_t> state_ = 0;\n  // Use a smaller segment size than default to limit memory usage in case there\n  // are a large number of MeteredExecutors instances in the process.\n  // The queue doesn't need blocking because we only dequeue when non-empty.\n  UMPMCQueue<Task, /* MayBlock */ false, /* LgSegmentSize */ 5> queue_;\n};\n\n} // namespace detail\n\nusing MeteredExecutor = detail::MeteredExecutorImpl<>;\n\n} // namespace folly\n\n#include <folly/executors/MeteredExecutor-inl.h>\n"
  },
  {
    "path": "folly/executors/QueueObserver.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/QueueObserver.h>\n\nnamespace {\n\nstd::unique_ptr<folly::QueueObserverFactory>\nmake_queue_observer_factory_fallback(\n    const std::string&, size_t, folly::WorkerProvider*) noexcept {\n  return std::unique_ptr<folly::QueueObserverFactory>();\n}\n\nclass WorkerKeepAlive : public folly::WorkerProvider::KeepAlive {\n public:\n  explicit WorkerKeepAlive(std::shared_lock<folly::SharedMutex> idsLock)\n      : threadsExitLock_(std::move(idsLock)) {}\n  ~WorkerKeepAlive() override {}\n\n private:\n  std::shared_lock<folly::SharedMutex> threadsExitLock_;\n};\n\n} // namespace\n\nnamespace folly {\n\nThreadIdWorkerProvider::IdsWithKeepAlive\nThreadIdWorkerProvider::collectThreadIds() {\n  auto keepAlive =\n      std::make_unique<WorkerKeepAlive>(std::shared_lock{threadsExitMutex_});\n  auto locked = osThreadIds_.rlock();\n  return {std::move(keepAlive), {locked->begin(), locked->end()}};\n}\n\nvoid ThreadIdWorkerProvider::addTid(pid_t tid) {\n  osThreadIds_.wlock()->insert(tid);\n}\n\nvoid ThreadIdWorkerProvider::removeTid(pid_t tid) {\n  osThreadIds_.wlock()->erase(tid);\n  // block until all WorkerKeepAlives have been destroyed\n  std::unique_lock w{threadsExitMutex_};\n}\n\nWorkerProvider::KeepAlive::~KeepAlive() {}\n\n/* static */ std::unique_ptr<QueueObserverFactory> QueueObserverFactory::make(\n    const std::string& context,\n    size_t numPriorities,\n    WorkerProvider* workerProvider) {\n  auto f = make_queue_observer_factory\n      ? make_queue_observer_factory\n      : make_queue_observer_factory_fallback;\n  return f(context, numPriorities, workerProvider);\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/QueueObserver.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <folly/Function.h>\n#include <folly/Portability.h>\n#include <folly/Synchronized.h>\n#include <folly/portability/SysTypes.h>\n\nnamespace folly {\n\nclass RequestContext;\n\n/**\n * WorkerProvider is a simple interface that can be used\n * to collect information about worker threads that are pulling work\n * from a given queue.\n */\nclass WorkerProvider {\n public:\n  virtual ~WorkerProvider() {}\n\n  /**\n   * Abstract type returned by the collectThreadIds() method.\n   * Implementations of the WorkerProvider interface need to define this class.\n   * The intent is to return a guard along with a list of worker IDs which can\n   * be removed on destruction of this object.\n   */\n  class KeepAlive {\n   public:\n    virtual ~KeepAlive() = 0;\n  };\n\n  // collectThreadIds() will return this aggregate type which includes an\n  // instance of the WorkersGuard.\n  struct IdsWithKeepAlive {\n    std::unique_ptr<KeepAlive> guard;\n    std::vector<pid_t> threadIds;\n  };\n\n  // Capture the Thread IDs of all threads consuming from a given queue.\n  // The provided vector should be populated with the OS Thread IDs and the\n  // method should return a SharedMutex which the caller can lock.\n  virtual IdsWithKeepAlive collectThreadIds() = 0;\n};\n\n/**\n * Interface for executors that provide a WorkerProvider to collect thread ids.\n */\nclass GetThreadIdCollector {\n public:\n  virtual ~GetThreadIdCollector() = default;\n  virtual WorkerProvider* getThreadIdCollector() = 0;\n};\n\nclass ThreadIdWorkerProvider : public WorkerProvider {\n public:\n  IdsWithKeepAlive collectThreadIds() override final;\n  void addTid(pid_t tid);\n\n  // Will block until all KeepAlives have been destroyed, if any exist\n  void removeTid(pid_t tid);\n\n private:\n  Synchronized<std::unordered_set<pid_t>> osThreadIds_;\n  mutable SharedMutex threadsExitMutex_;\n};\n\nclass QueueObserver {\n public:\n  virtual ~QueueObserver() {}\n\n  virtual intptr_t onEnqueued(const RequestContext*) = 0;\n  virtual void onDequeued(intptr_t) = 0;\n};\n\nclass QueueObserverFactory {\n public:\n  virtual ~QueueObserverFactory() {}\n  virtual std::unique_ptr<QueueObserver> create(int8_t pri) = 0;\n\n  static std::unique_ptr<QueueObserverFactory> make(\n      const std::string& context,\n      size_t numPriorities,\n      WorkerProvider* workerProvider = nullptr);\n};\n\nusing MakeQueueObserverFactory = std::unique_ptr<QueueObserverFactory>(\n    const std::string&, size_t, WorkerProvider*);\n#if FOLLY_HAVE_WEAK_SYMBOLS\nFOLLY_ATTR_WEAK MakeQueueObserverFactory make_queue_observer_factory;\n#else\nconstexpr MakeQueueObserverFactory* make_queue_observer_factory = nullptr;\n#endif\n\nstruct QueueInfo {\n  std::unordered_map<std::string, std::unordered_set<pid_t>> namesAndTids;\n  std::vector<std::unique_ptr<WorkerProvider::KeepAlive>> keepAlives;\n};\nusing LaggingQueueInfoFunc = folly::Function<QueueInfo()>;\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/QueuedImmediateExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/QueuedImmediateExecutor.h>\n\n#include <functional>\n\n#include <folly/Indestructible.h>\n#include <folly/ScopeGuard.h>\n\nnamespace folly {\n\nQueuedImmediateExecutor& QueuedImmediateExecutor::instance() {\n  static Indestructible<QueuedImmediateExecutor> instance;\n  return *instance;\n}\n\nvoid QueuedImmediateExecutor::add(Func func) {\n  auto& [running, queue] = *q_;\n  if (running) {\n    queue.push(Task{std::move(func), RequestContext::saveContext()});\n    return;\n  }\n\n  running = true;\n  auto cleanup = makeGuard([&r = running] { r = false; });\n\n  // No need to save/restore request context if this is the first call.\n  invokeCatchingExns(\"QueuedImmediateExecutor\", std::exchange(func, {}));\n\n  if (queue.empty()) {\n    return;\n  }\n\n  RequestContextSaverScopeGuard guard;\n  while (!queue.empty()) {\n    auto& task = queue.front();\n    RequestContext::setContext(std::move(task.ctx));\n    invokeCatchingExns(\"QueuedImmediateExecutor\", std::ref(task.func));\n    queue.pop();\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/QueuedImmediateExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <queue>\n#include <utility>\n\n#include <folly/Executor.h>\n#include <folly/ThreadLocal.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/io/async/Request.h>\n\nnamespace folly {\n\n/**\n * Runs inline like InlineExecutor, but with a queue so that any tasks added\n * to this executor by one of its own callbacks will be queued instead of\n * executed inline (nested). This is usually better behavior than Inline.\n */\nclass QueuedImmediateExecutor : public InlineLikeExecutor {\n public:\n  static QueuedImmediateExecutor& instance();\n\n  void add(Func func) override;\n\n private:\n  struct Task {\n    Func func;\n    std::shared_ptr<RequestContext> ctx;\n  };\n\n  using Queue = std::queue<Task>;\n  folly::ThreadLocal<std::pair</* running */ bool, Queue>> q_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ScheduledExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <memory>\n#include <stdexcept>\n\n#include <folly/Executor.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n// An executor that supports timed scheduling. Like RxScheduler.\nclass ScheduledExecutor : public virtual Executor {\n public:\n  // Reality is that better than millisecond resolution is very hard to\n  // achieve. However, we reserve the right to be incredible.\n  using Duration = std::chrono::microseconds;\n  using TimePoint = std::chrono::steady_clock::time_point;\n\n  ~ScheduledExecutor() override = default;\n\n  void add(Func) override = 0;\n\n  /// Alias for add() (for Rx consistency)\n  void schedule(Func&& a) { add(std::move(a)); }\n\n  /// Schedule a Func to be executed after dur time has elapsed\n  /// Expect millisecond resolution at best.\n  void schedule(Func&& a, Duration const& dur) {\n    scheduleAt(std::move(a), now() + dur);\n  }\n\n  /// Schedule a Func to be executed at time t, or as soon afterward as\n  /// possible. Expect millisecond resolution at best. Must be threadsafe.\n  virtual void scheduleAt(Func&& /* a */, TimePoint const& /* t */) {\n    throw_exception<std::logic_error>(\"unimplemented\");\n  }\n\n  /// Get this executor's notion of time. Must be threadsafe.\n  virtual TimePoint now() { return std::chrono::steady_clock::now(); }\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/SequencedExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n\nnamespace folly {\n\n// SequencedExecutor is an executor that provides the following guarantee:\n// if add(A) and add(B) were sequenced (i.e. add(B) was called after add(A) call\n// had returned to the caller) then execution of A and B will be sequenced\n// (i.e. B() can be called only after A() returns) too.\nclass SequencedExecutor : public virtual Executor {\n public:\n  virtual ~SequencedExecutor() override {}\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/SerialExecutor-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <glog/logging.h>\n\n#include <folly/ExceptionString.h>\n#include <folly/ScopeGuard.h>\n\nnamespace folly::detail {\n\ntemplate <template <typename> typename Queue>\nclass SerialExecutorImpl<Queue>::Worker {\n public:\n  explicit Worker(KeepAlive<SerialExecutorImpl> ka) : ka_(std::move(ka)) {}\n\n  ~Worker() {\n    if (ka_) {\n      ka_->drain(); // We own the queue but we did not run.\n    }\n  }\n\n  Worker(Worker&& other) : ka_(std::exchange(other.ka_, {})) {}\n\n  Worker(const Worker&) = delete;\n  Worker& operator=(const Worker&) = delete;\n  Worker& operator=(Worker&&) = delete;\n\n  void operator()() { std::exchange(ka_, {})->worker(); }\n\n private:\n  KeepAlive<SerialExecutorImpl> ka_;\n};\n\ntemplate <template <typename> typename Queue>\nSerialExecutorImpl<Queue>::SerialExecutorImpl(KeepAlive<Executor> parent)\n    : parent_(std::move(parent)) {}\n\ntemplate <template <typename> typename Queue>\nSerialExecutorImpl<Queue>::~SerialExecutorImpl() {\n  DCHECK(!keepAliveCounter_);\n}\n\ntemplate <template <typename> typename Queue>\nExecutor::KeepAlive<SerialExecutorImpl<Queue>>\nSerialExecutorImpl<Queue>::create(KeepAlive<Executor> parent) {\n  return makeKeepAlive<SerialExecutorImpl<Queue>>(\n      new SerialExecutorImpl<Queue>(std::move(parent)));\n}\n\ntemplate <template <typename> typename Queue>\ntypename SerialExecutorImpl<Queue>::UniquePtr\nSerialExecutorImpl<Queue>::createUnique(std::shared_ptr<Executor> parent) {\n  auto executor =\n      new SerialExecutorImpl<Queue>(getKeepAliveToken(parent.get()));\n  return {executor, Deleter{std::move(parent)}};\n}\n\ntemplate <template <typename> typename Queue>\nbool SerialExecutorImpl<Queue>::keepAliveAcquire() noexcept {\n  auto keepAliveCounter =\n      keepAliveCounter_.fetch_add(1, std::memory_order_relaxed);\n  DCHECK(keepAliveCounter > 0);\n  return true;\n}\n\ntemplate <template <typename> typename Queue>\nvoid SerialExecutorImpl<Queue>::keepAliveRelease() noexcept {\n  auto keepAliveCounter =\n      keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel);\n  DCHECK(keepAliveCounter > 0);\n  if (keepAliveCounter == 1) {\n    delete this;\n  }\n}\n\ntemplate <template <typename> typename Queue>\nvoid SerialExecutorImpl<Queue>::add(Func func) {\n  if (scheduleTask(std::move(func))) {\n    parent_->add(Worker{getKeepAliveToken(this)});\n  }\n}\n\ntemplate <template <typename> typename Queue>\nvoid SerialExecutorImpl<Queue>::addWithPriority(Func func, int8_t priority) {\n  if (scheduleTask(std::move(func))) {\n    parent_->addWithPriority(Worker{getKeepAliveToken(this)}, priority);\n  }\n}\n\ntemplate <template <typename> typename Queue>\nbool SerialExecutorImpl<Queue>::scheduleTask(Func&& func) {\n  queue_.enqueue(Task{std::move(func), RequestContext::saveContext()});\n  // If this thread is the first to mark the queue as non-empty, schedule the\n  // worker.\n  return scheduled_.fetch_add(1, std::memory_order_acq_rel) == 0;\n}\n\ntemplate <template <typename> typename Queue>\nvoid SerialExecutorImpl<Queue>::worker() {\n  std::size_t queueSize = scheduled_.load(std::memory_order_acquire);\n  DCHECK_NE(queueSize, 0);\n\n  std::size_t processed = 0;\n  RequestContextSaverScopeGuard ctxGuard;\n  while (true) {\n    Task task;\n    // This dequeue happens under the request context of the previous task, so\n    // that we can avoid switching context if the next task shares the same\n    // context. dequeue() is cheap, non-blocking, and doesn't run application\n    // logic, so it is fine to sneak it in the previous context.\n    queue_.dequeue(task);\n    RequestContext::setContext(std::move(task.ctx));\n    invokeCatchingExns(\"SerialExecutor: func\", std::exchange(task.func, {}));\n\n    if (++processed == queueSize) {\n      // NOTE: scheduled_ must be decremented after the task has been processed,\n      // or add() may concurrently start another worker.\n      queueSize = scheduled_.fetch_sub(queueSize, std::memory_order_acq_rel) -\n          queueSize;\n      if (queueSize == 0) {\n        // Queue is now empty\n        return;\n      }\n      processed = 0;\n    }\n  }\n}\n\ntemplate <template <typename> typename Queue>\nvoid SerialExecutorImpl<Queue>::drain() {\n  auto queueSize = scheduled_.load(std::memory_order_acquire);\n  if (queueSize == 0) {\n    return;\n  }\n\n  RequestContextSaverScopeGuard ctxGuard;\n  while (queueSize != 0) {\n    Task task;\n    queue_.dequeue(task);\n    RequestContext::setContext(std::move(task.ctx));\n    task.func = {};\n    queueSize = scheduled_.fetch_sub(1, std::memory_order_acq_rel) - 1;\n  }\n}\n\nclass NoopMutex {\n public:\n  template <class F>\n  auto lock_combine(F&& f) {\n#ifndef NDEBUG\n    CHECK(!locked_.exchange(true, std::memory_order_acq_rel));\n    auto&& g = folly::makeGuard([this] {\n      locked_.store(false, std::memory_order_release);\n    });\n#endif\n    return f();\n  }\n\n private:\n#ifndef NDEBUG\n  std::atomic<bool> locked_;\n#endif\n};\n\n/**\n * MPSC queue with the additional requirement that the queue must be non-empty\n * on dequeue(), and the enqueue() that makes the queue non-empty must complete\n * before the corresponding dequeue(). This is guaranteed in SerialExecutor by\n * release/acquire synchronization on scheduled_.\n *\n * Producers are internally synchronized using a mutex, while the consumer\n * relies entirely on external synchronization.\n */\ntemplate <class Task, class Mutex>\nclass SerialExecutorMPSCQueue {\n  static_assert(std::is_nothrow_move_constructible_v<Task>);\n\n public:\n  ~SerialExecutorMPSCQueue() {\n    // Queue must be consumed completely at destruction.\n    CHECK_EQ(head_, tail_);\n    CHECK_EQ(head_->readIdx.load(), head_->writeIdx.load());\n    CHECK(head_->next == nullptr);\n    deleteSegment(head_);\n    deleteSegment(segmentCache_.load(std::memory_order_acquire));\n  }\n\n  void enqueue(Task&& task) {\n    mutex_.lock_combine([&] {\n      // dequeue() will not delete a segment or try to read head_->next until\n      // the next write has completed, so this is safe.\n      if (tail_->writeIdx.load() == kSegmentSize) {\n        auto* segment =\n            segmentCache_.exchange(nullptr, std::memory_order_acquire);\n        if (segment == nullptr) {\n          segment = new Segment;\n        } else {\n          std::destroy_at(segment);\n          new (segment) Segment;\n        }\n        tail_->next = segment;\n        tail_ = segment;\n      }\n      auto idx = tail_->writeIdx.load();\n      new (&tail_->tasks[idx]) Task(std::move(task));\n      tail_->writeIdx.store(idx + 1);\n    });\n  }\n\n  void dequeue(Task& task) {\n    auto idx = head_->readIdx.load();\n    DCHECK_LE(idx, kSegmentSize);\n    if (idx == kSegmentSize) {\n      DCHECK(head_->next != nullptr);\n      auto* oldSegment = std::exchange(head_, head_->next);\n      // If there is already a segment in cache, replace it with the latest one,\n      // as it is more likely to still be warm in cache for the producer.\n      deleteSegment(\n          segmentCache_.exchange(oldSegment, std::memory_order_release));\n      DCHECK_EQ(head_->readIdx.load(), 0);\n      idx = 0;\n    }\n    DCHECK_LT(idx, head_->writeIdx.load());\n    task = std::move(*reinterpret_cast<Task*>(&head_->tasks[idx]));\n    std::destroy_at(&head_->tasks[idx]);\n    head_->readIdx.store(idx + 1);\n  }\n\n private:\n  static constexpr size_t kSegmentSize = 16;\n\n  struct Segment {\n    // Neither writeIdx or readIdx need to be atomic since each is exclusively\n    // owned by respectively producer and consumer, but we need atomicity for\n    // assertions.\n    // We avoid any padding to minimize memory usage, but at least we can\n    // separate write and read index by interposing the payloads.\n    relaxed_atomic<size_t> writeIdx = 0;\n    folly::aligned_storage_for_t<Task> tasks[kSegmentSize];\n    relaxed_atomic<size_t> readIdx = 0;\n    Segment* next = nullptr;\n  };\n  static_assert(std::is_trivially_destructible_v<Segment>);\n\n  void deleteSegment(Segment* segment) {\n    if (segment == &inlineSegment_) {\n      return;\n    }\n    delete segment;\n  }\n\n  [[FOLLY_ATTR_NO_UNIQUE_ADDRESS]] Mutex mutex_;\n  Segment* tail_ = &inlineSegment_;\n  Segment* head_ = tail_;\n  // Cache the allocation for exactly one segment, so that in the common case\n  // where the consumer keeps up with the producer no allocations are needed.\n  std::atomic<Segment*> segmentCache_{nullptr};\n\n  // Store the first segment inline. If this is a short-lived SerialExecutor\n  // which enqueues fewer than kSegmentSize tasks, this will save an allocation.\n  Segment inlineSegment_;\n};\n\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/executors/SerialExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <memory>\n#include <mutex>\n\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/executors/SerializedExecutor.h>\n#include <folly/io/async/Request.h>\n#include <folly/synchronization/DistributedMutex.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\nnamespace folly {\n\nnamespace detail {\n\n/**\n * @class SerialExecutor\n *\n * @brief Executor that guarantees serial non-concurrent execution of added\n *     tasks\n *\n * SerialExecutor is similar to boost asio's strand concept. A SerialExecutor\n * has a parent executor which is given at construction time (defaults to\n * folly's global CPUExecutor). Tasks added to SerialExecutor are executed\n * in the parent executor, however strictly non-concurrently and in the order\n * they were added.\n *\n * When a task is added to the executor while another one is running on the\n * parent executor, the new task is piggybacked on the running task to save the\n * cost of scheduling a task on the parent executor. This implies that the\n * parent executor may observe a smaller number of tasks than those added in the\n * SerialExecutor.\n *\n * The SerialExecutor may be deleted at any time. All tasks that have been\n * submitted will still be executed with the same guarantees, as long as the\n * parent executor is executing tasks.\n *\n * NOTE: This describes low-level executor tasks. Not coro::Task tasks.\n * This executor does not guarantee that coro::Task tasks will be completed in\n * the order that they are added. Rather, a coro::Task task may be suspended at\n * a co_await/co_yield point and another such task that has been added to this\n * executor may be resumed at that point.\n */\ntemplate <template <typename> typename Queue>\nclass SerialExecutorImpl : public SerializedExecutor {\n public:\n  SerialExecutorImpl(SerialExecutorImpl const&) = delete;\n  SerialExecutorImpl& operator=(SerialExecutorImpl const&) = delete;\n  SerialExecutorImpl(SerialExecutorImpl&&) = delete;\n  SerialExecutorImpl& operator=(SerialExecutorImpl&&) = delete;\n\n  static KeepAlive<SerialExecutorImpl> create(\n      KeepAlive<Executor> parent = getGlobalCPUExecutor());\n\n  const KeepAlive<Executor>& parent() const { return parent_; }\n\n  class Deleter {\n   public:\n    Deleter() {}\n\n    void operator()(SerialExecutorImpl* executor) {\n      executor->keepAliveRelease();\n    }\n\n   private:\n    friend class SerialExecutorImpl;\n    explicit Deleter(std::shared_ptr<Executor> parent)\n        : parent_(std::move(parent)) {}\n\n    std::shared_ptr<Executor> parent_;\n  };\n\n  using UniquePtr = std::unique_ptr<SerialExecutorImpl, Deleter>;\n  [[deprecated(\"Replaced by create\")]] static UniquePtr createUnique(\n      std::shared_ptr<Executor> parent);\n\n  /**\n   * Add one task for execution in the parent executor\n   */\n  void add(Func func) override;\n\n  /**\n   * Add one task for execution in the parent executor, and use the given\n   * priority for one task submission to parent executor.\n   *\n   * Since in-order execution of tasks submitted to SerialExecutor is\n   * guaranteed, the priority given here does not necessarily reflect the\n   * execution priority of the task submitted with this call to\n   * `addWithPriority`. The given priority is passed on to the parent executor\n   * for the execution of one of the SerialExecutor's tasks.\n   */\n  void addWithPriority(Func func, int8_t priority) override;\n  uint8_t getNumPriorities() const override {\n    return parent_->getNumPriorities();\n  }\n\n private:\n  struct Task {\n    Func func;\n    std::shared_ptr<RequestContext> ctx;\n  };\n\n  class Worker;\n\n  explicit SerialExecutorImpl(KeepAlive<Executor> parent);\n  ~SerialExecutorImpl() override;\n\n  bool keepAliveAcquire() noexcept override;\n\n  void keepAliveRelease() noexcept override;\n\n  bool scheduleTask(Func&& func);\n  void worker();\n  void drain();\n\n  KeepAlive<Executor> parent_;\n  std::atomic<std::size_t> scheduled_{0};\n  std::atomic<ssize_t> keepAliveCounter_{1};\n  Queue<Task> queue_;\n};\n\nclass NoopMutex;\n\ntemplate <class Task, class Mutex = folly::DistributedMutex>\nclass SerialExecutorMPSCQueue;\n\ntemplate <typename Task>\nusing SerialExecutorQueue = SerialExecutorMPSCQueue<Task>;\n\ntemplate <typename Task>\nusing SPSerialExecutorQueue = SerialExecutorMPSCQueue<Task, NoopMutex>;\n\n} // namespace detail\n\nusing SerialExecutor = detail::SerialExecutorImpl<detail::SerialExecutorQueue>;\n\n/**\n * Single-producer version of SmallExecutor. It is the responsibility of the\n * caller to guarantee that calls to add() are externally serialized, but it can\n * be slightly faster.\n *\n * It is very unlikely that you need this.\n */\nusing SPSerialExecutor =\n    detail::SerialExecutorImpl<detail::SPSerialExecutorQueue>;\n\n} // namespace folly\n\n#include <folly/executors/SerialExecutor-inl.h>\n"
  },
  {
    "path": "folly/executors/SerializedExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/executors/SequencedExecutor.h>\n\nnamespace folly {\n\n// SerializedExecutor is a SequencedExecutor with the additional guarantee that\n// no tasks added to the executor are run concurrently, even if their add()s\n// were not sequenced.\nclass SerializedExecutor : public SequencedExecutor {\n public:\n  virtual ~SerializedExecutor() override {}\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/SoftRealTimeExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/SoftRealTimeExecutor.h>\n\n#include <atomic>\n\n#include <glog/logging.h>\n\nnamespace folly {\n\nnamespace {\n\nclass DeadlineExecutor : public folly::Executor {\n public:\n  static KeepAlive<> create(\n      uint64_t deadline, KeepAlive<SoftRealTimeExecutor> executor) {\n    return makeKeepAlive(new DeadlineExecutor(deadline, std::move(executor)));\n  }\n\n  void add(folly::Func f) override { executor_->add(std::move(f), deadline_); }\n\n  bool keepAliveAcquire() noexcept override {\n    const auto count = keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    DCHECK_GT(count, 0);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    const auto count = keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);\n    DCHECK_GT(count, 0);\n    if (count == 1) {\n      delete this;\n    }\n  }\n\n private:\n  DeadlineExecutor(uint64_t deadline, KeepAlive<SoftRealTimeExecutor> executor)\n      : deadline_(deadline), executor_(std::move(executor)) {}\n\n  std::atomic<size_t> keepAliveCount_{1};\n  uint64_t deadline_;\n  KeepAlive<SoftRealTimeExecutor> executor_;\n};\n\n} // namespace\n\nfolly::Executor::KeepAlive<> SoftRealTimeExecutor::deadlineExecutor(\n    uint64_t deadline) {\n  return DeadlineExecutor::create(deadline, getKeepAliveToken(this));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/SoftRealTimeExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include <folly/Executor.h>\n\nnamespace folly {\n\n// `SoftRealTimeExecutor` is an executor that performs some priority-based\n// scheduling with a deadline assigned to each task. __Soft__ real-time\n// means that not every deadline is guaranteed to be met.\nclass SoftRealTimeExecutor : public virtual Executor {\n public:\n  using Executor::add;\n\n  // Add a task with an assigned abstract deadline.\n  //\n  // NOTE: The type of `deadline` was chosen to be an integral rather than\n  // a typed time point or duration (e.g., `std::chrono::time_point`) to allow\n  // for flexibility. While the deadline for a task may be a time point,\n  // it could also be a duration or the size of the task, which emulates\n  // rate-monotonic scheduling that prioritizes small tasks. It also enables,\n  // for example, tiered scheduling (strictly prioritizing a category of tasks)\n  // by assigning the high-bit of the deadline.\n  virtual void add(Func func, uint64_t deadline) = 0;\n  virtual void add(std::vector<Func>, uint64_t deadline) = 0;\n\n  folly::Executor::KeepAlive<> deadlineExecutor(uint64_t deadline);\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/StrandExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/StrandExecutor.h>\n\n#include <glog/logging.h>\n\n#include <folly/ExceptionString.h>\n#include <folly/executors/GlobalExecutor.h>\n\nnamespace folly {\n\n// Callable that will process a batch of tasks scheduled\n// for execution on the same executor when called.\nclass StrandContext::Task {\n public:\n  explicit Task(std::shared_ptr<StrandContext> ctx) noexcept\n      : context_(std::move(ctx)) {}\n\n  Task(Task&& t) noexcept : context_(std::move(t.context_)) {}\n\n  Task(const Task&) = delete;\n  Task& operator=(Task&&) = delete;\n  Task& operator=(const Task&) = delete;\n\n  ~Task() {\n    if (context_) {\n      // Task destructed without being invoked.\n      // This either happens because an error occurred when attempting\n      // to enqueue this task to an Executor, or if the Executor dropped\n      // the task without executing it. Both of these situations are fatal.\n      LOG(FATAL) << \"StrandExecutor Task destroyed without being executed\";\n    }\n  }\n\n  void operator()() noexcept {\n    DCHECK(context_);\n    StrandContext::executeNext(std::move(context_));\n  }\n\n private:\n  std::shared_ptr<StrandContext> context_;\n};\n\nstd::shared_ptr<StrandContext> StrandContext::create() {\n  return std::make_shared<StrandContext>(PrivateTag{});\n}\n\nvoid StrandContext::add(Func func, Executor::KeepAlive<> executor) {\n  addImpl(\n      QueueItem{\n          std::move(func),\n          std::move(executor),\n          folly::none,\n          RequestContext::saveContext()});\n}\n\nvoid StrandContext::addWithPriority(\n    Func func, Executor::KeepAlive<> executor, int8_t priority) {\n  addImpl(\n      QueueItem{\n          std::move(func),\n          std::move(executor),\n          priority,\n          RequestContext::saveContext()});\n}\n\nvoid StrandContext::addImpl(QueueItem&& item) {\n  queue_.enqueue(std::move(item));\n  if (scheduled_.fetch_add(1, std::memory_order_acq_rel) == 0) {\n    // This thread was first to mark queue as nonempty, so we are\n    // responsible for scheduling the first queued item onto the\n    // executor.\n\n    // Note that due to a potential race with another thread calling\n    // add[WithPriority]() concurrently, the first item in queue is\n    // not necessarily the item this thread just enqueued so need\n    // to re-query it from the queue.\n    dispatchFrontQueueItem(shared_from_this());\n  }\n}\n\nvoid StrandContext::executeNext(\n    std::shared_ptr<StrandContext> thisPtr) noexcept {\n  // Put a cap on the number of items we process in one batch before\n  // rescheduling on to the executor to avoid starvation of other\n  // items queued to the current executor.\n  const std::size_t maxItemsToProcessSynchronously = 32;\n\n  std::size_t queueSize = thisPtr->scheduled_.load(std::memory_order_acquire);\n  DCHECK(queueSize != 0u);\n\n  const QueueItem* nextItem = nullptr;\n\n  std::size_t pendingCount = 0;\n  {\n    RequestContextSaverScopeGuard ctxGuard;\n    for (std::size_t i = 0; i < maxItemsToProcessSynchronously; ++i) {\n      QueueItem item = thisPtr->queue_.dequeue();\n      RequestContext::setContext(std::move(item.requestCtx));\n      Executor::invokeCatchingExns(\n          \"StrandExecutor: func\", std::exchange(item.func, {}));\n\n      ++pendingCount;\n\n      if (pendingCount == queueSize) {\n        queueSize =\n            thisPtr->scheduled_.fetch_sub(\n                pendingCount, std::memory_order_acq_rel) -\n            pendingCount;\n        if (queueSize == 0) {\n          // Queue is now empty\n          return;\n        }\n\n        pendingCount = 0;\n      }\n\n      nextItem = thisPtr->queue_.try_peek();\n      DCHECK(nextItem != nullptr);\n\n      // Check if the next item has the same executor.\n      // If so we'll go around the loop again, otherwise\n      // we'll dispatch to the other executor and return.\n      if (nextItem->executor.get() != item.executor.get()) {\n        break;\n      }\n    }\n  }\n\n  DCHECK(nextItem != nullptr);\n  DCHECK(pendingCount < queueSize);\n\n  [[maybe_unused]] auto prevQueueSize =\n      thisPtr->scheduled_.fetch_sub(pendingCount, std::memory_order_relaxed);\n  DCHECK(pendingCount < prevQueueSize);\n\n  // Reuse the shared_ptr from the previous item.\n  dispatchFrontQueueItem(std::move(thisPtr));\n}\n\nvoid StrandContext::dispatchFrontQueueItem(\n    std::shared_ptr<StrandContext> thisPtr) noexcept {\n  const QueueItem* item = thisPtr->queue_.try_peek();\n  DCHECK(item != nullptr);\n\n  // NOTE: We treat any failure to schedule work onto the item's executor as a\n  // fatal error. This will either result in LOG(FATAL) being called from\n  // the Task destructor or std::terminate() being called by\n  // exception-unwinding, depending on the compiler/runtime.\n  Task task{std::move(thisPtr)};\n  if (item->priority.has_value()) {\n    item->executor->addWithPriority(std::move(task), item->priority.value());\n  } else {\n    item->executor->add(std::move(task));\n  }\n}\n\nExecutor::KeepAlive<StrandExecutor> StrandExecutor::create() {\n  return create(StrandContext::create(), getGlobalCPUExecutor());\n}\n\nExecutor::KeepAlive<StrandExecutor> StrandExecutor::create(\n    std::shared_ptr<StrandContext> context) {\n  return create(std::move(context), getGlobalCPUExecutor());\n}\n\nExecutor::KeepAlive<StrandExecutor> StrandExecutor::create(\n    Executor::KeepAlive<> parentExecutor) {\n  return create(StrandContext::create(), std::move(parentExecutor));\n}\n\nExecutor::KeepAlive<StrandExecutor> StrandExecutor::create(\n    std::shared_ptr<StrandContext> context,\n    Executor::KeepAlive<> parentExecutor) {\n  auto* ex = new StrandExecutor(std::move(context), std::move(parentExecutor));\n  return Executor::makeKeepAlive<StrandExecutor>(ex);\n}\n\nvoid StrandExecutor::add(Func f) {\n  context_->add(std::move(f), parent_);\n}\n\nvoid StrandExecutor::addWithPriority(Func f, int8_t priority) {\n  context_->addWithPriority(std::move(f), parent_, priority);\n}\n\nuint8_t StrandExecutor::getNumPriorities() const {\n  return parent_->getNumPriorities();\n}\n\nbool StrandExecutor::keepAliveAcquire() noexcept {\n  [[maybe_unused]] auto oldCount =\n      refCount_.fetch_add(1, std::memory_order_relaxed);\n  DCHECK(oldCount > 0);\n  return true;\n}\n\nvoid StrandExecutor::keepAliveRelease() noexcept {\n  const auto oldCount = refCount_.fetch_sub(1, std::memory_order_acq_rel);\n  DCHECK(oldCount > 0);\n  if (oldCount == 1) {\n    // Last reference.\n    delete this;\n  }\n}\n\nStrandExecutor::StrandExecutor(\n    std::shared_ptr<StrandContext> context,\n    Executor::KeepAlive<> parent) noexcept\n    : refCount_(1), parent_(std::move(parent)), context_(std::move(context)) {}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/StrandExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <memory>\n\n#include <folly/Optional.h>\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/executors/SerializedExecutor.h>\n#include <folly/io/async/Request.h>\n\nnamespace folly {\nclass StrandExecutor;\n\n// StrandExecutor / StrandContext\n//\n// A StrandExecutor, like a SerialExecutor, serialises execution of work added\n// to it, while deferring the actual execution of this work to another\n// Executor.\n//\n// However, unlike the SerialExecutor, a StrandExecutor allows multiple\n// StrandExecutors, each potentially delegating to a different parent Executor,\n// to share the same serialising queue.\n//\n// This can be used in cases where you want to execute work on a\n// caller-provided execution context, but want to make sure that work is\n// serialised with respect to other work that might be scheduled on other\n// underlying Executors.\n//\n// e.g. Using StrandExecutor with folly::coro::Task to enforce that only\n//      a single thread accesses the object at a time, while still allowing\n//      other other methods to execute while the current one is suspended\n//\n//    class MyObject {\n//    public:\n//\n//      folly::coro::Task<T> someMethod() {\n//        auto currentEx = co_await folly::coro::co_current_executor;\n//        auto strandEx = folly::StrandExecutor::create(strand_, currentEx);\n//        co_return co_await co_withExecutor(strandEx, someMethodImpl());\n//      }\n//\n//    private:\n//      folly::coro::Task<T> someMethodImpl() {\n//        // Safe to access otherState_ without further synchronisation.\n//        modify(otherState_);\n//\n//        // Other instances of someMethod() calls will be able to run\n//        // while this coroutine is suspended waiting for an RPC call\n//        // to complete.\n//        co_await getClient()->co_someRpcCall();\n//\n//        // When that call resumes, this coroutine is once again\n//        // executing with mutual exclusion.\n//        modify(otherState_);\n//      }\n//\n//      std::shared_ptr<StrandContext> strand_{StrandContext::create()};\n//      X otherState_;\n//    };\n//\nclass StrandContext : public std::enable_shared_from_this<StrandContext> {\n public:\n  // Create a new StrandContext object. This will allow scheduling work\n  // that will execute at most one task at a time but delegate the actual\n  // execution to an execution context associated with each particular\n  // function.\n  static std::shared_ptr<StrandContext> create();\n\n  // Schedule 'func()' to be called on 'executor' after all prior functions\n  // scheduled to this context have completed.\n  void add(Func func, Executor::KeepAlive<> executor);\n\n  // Schedule `func()` to be called on `executor` with specified priority\n  // after all prior functions scheduled to this context have completed.\n  //\n  // Note, that the priority will only affect the priority of the scheduling\n  // of this particular function once all prior tasks have finished executing.\n  void addWithPriority(\n      Func func, Executor::KeepAlive<> executor, int8_t priority);\n\n private:\n  struct PrivateTag {};\n  class Task;\n\n public:\n  // Public to allow construction using std::make_shared() but a logically\n  // private constructor. Try to enforce this by forcing use of a private\n  // tag-type as a parameter.\n  explicit StrandContext(PrivateTag) {}\n\n private:\n  struct QueueItem {\n    Func func;\n    Executor::KeepAlive<> executor;\n    Optional<std::int8_t> priority;\n    std::shared_ptr<RequestContext> requestCtx;\n  };\n\n  void addImpl(QueueItem&& item);\n  static void executeNext(std::shared_ptr<StrandContext> thisPtr) noexcept;\n  static void dispatchFrontQueueItem(\n      std::shared_ptr<StrandContext> thisPtr) noexcept;\n\n  std::atomic<std::size_t> scheduled_{0};\n  UMPSCQueue<QueueItem, /*MayBlock=*/false, /*LgSegmentSize=*/6> queue_;\n};\n\nclass StrandExecutor final : public SerializedExecutor {\n public:\n  // Creates a new StrandExecutor that is independent of other StrandExcutors.\n  //\n  // Work enqueued to the returned executor will be executed on the global CPU\n  // thread-pool but will only execute at most one function enqueued to this\n  // executor at a time.\n  static Executor::KeepAlive<StrandExecutor> create();\n\n  // Creates a new StrandExecutor that shares the serialised queue with other\n  // StrandExecutors constructed with the same context.\n  //\n  // Work enqueued to the returned executor will be executed on the global CPU\n  // thread-pool but will only execute at most one function enqueued to any\n  // StrandExecutor created with the same StrandContext.\n  static Executor::KeepAlive<StrandExecutor> create(\n      std::shared_ptr<StrandContext> context);\n\n  // Creates a new StrandExecutor that will serialise execution of any work\n  // enqueued to it, running the work on the parentExecutor.\n  static Executor::KeepAlive<StrandExecutor> create(\n      Executor::KeepAlive<> parentExecutor);\n\n  // Creates a new StrandExecutor that will execute work on 'parentExecutor'\n  // but that will execute at most one function at a time enqueued to any\n  // StrandExecutor created with the same StrandContext.\n  static Executor::KeepAlive<StrandExecutor> create(\n      std::shared_ptr<StrandContext> context,\n      Executor::KeepAlive<> parentExecutor);\n\n  void add(Func f) override;\n\n  void addWithPriority(Func f, int8_t priority) override;\n  uint8_t getNumPriorities() const override;\n\n protected:\n  bool keepAliveAcquire() noexcept override;\n  void keepAliveRelease() noexcept override;\n\n private:\n  explicit StrandExecutor(\n      std::shared_ptr<StrandContext> context,\n      Executor::KeepAlive<> parent) noexcept;\n\n private:\n  std::atomic<std::size_t> refCount_;\n  Executor::KeepAlive<> parent_;\n  std::shared_ptr<StrandContext> context_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/StripedEDFThreadPoolExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/StripedEDFThreadPoolExecutor.h>\n\n#include <optional>\n\n#include <glog/logging.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/container/IntrusiveHeap.h>\n#include <folly/synchronization/DistributedMutex.h>\n#include <folly/synchronization/StripedThrottledLifoSem.h>\n\nnamespace folly {\n\nnamespace {\n\ntemplate <class T>\nclass EDFPriorityQueue {\n public:\n  EDFPriorityQueue() = default;\n\n  ~EDFPriorityQueue() {\n    while (!heap_.empty()) {\n      delete heap_.pop();\n    }\n  }\n\n  void enqueue(T&& item, uint64_t deadline) {\n    std::unique_ptr<Node> node{new Node{std::move(item), deadline}};\n    mutex_.lock_combine([&] { heap_.push(node.release()); });\n  }\n\n  T dequeue() {\n    std::unique_ptr<Node> node;\n    mutex_.lock_combine([&] { node.reset(heap_.pop()); });\n    CHECK(node);\n    return std::move(node->item);\n  }\n\n private:\n  struct Node : IntrusiveHeapNode<> {\n    Node(T&& it, uint64_t d) : item(std::move(it)), deadline(d) {}\n\n    T item;\n    uint64_t deadline;\n\n    friend bool operator<(const Node& lhs, const Node& rhs) {\n      return lhs.deadline > rhs.deadline;\n    }\n  };\n\n  EDFPriorityQueue(const EDFPriorityQueue&) = delete;\n  EDFPriorityQueue(EDFPriorityQueue&&) = delete;\n  EDFPriorityQueue& operator=(const EDFPriorityQueue&) = delete;\n  EDFPriorityQueue& operator=(EDFPriorityQueue&&) = delete;\n\n  DistributedMutex mutex_;\n  IntrusiveHeap<Node> heap_;\n};\n\ntemplate <class T>\nclass BlockingQueueWithDeadline : public BlockingQueue<T> {\n public:\n  virtual BlockingQueueAddResult addWithDeadline(\n      T&& item, uint64_t deadline) = 0;\n};\n\ntemplate <class T>\nclass StripedEDFPriorityBlockingQueue final\n    : public BlockingQueueWithDeadline<T> {\n public:\n  explicit StripedEDFPriorityBlockingQueue(\n      const ThrottledLifoSem::Options& options)\n      : sem_(LLCAccessSpreader::get().numStripes(), std::tuple{}, options) {\n    StripedThrottledLifoSemBalancer::subscribe(sem_);\n  }\n\n  ~StripedEDFPriorityBlockingQueue() override {\n    StripedThrottledLifoSemBalancer::unsubscribe(sem_);\n  }\n\n  BlockingQueueAddResult addWithDeadline(T&& item, uint64_t deadline) override {\n    const auto stripeIdx = getStripeIdx();\n    sem_.payload(stripeIdx).enqueue(std::move(item), deadline);\n    return sem_.post(stripeIdx);\n  }\n\n  BlockingQueueAddResult add(T&& item) override {\n    return addWithDeadline(\n        std::move(item), StripedEDFThreadPoolExecutor::kLatestDeadline);\n  }\n\n  T take() override {\n    const auto stripeIdx = getStripeIdx();\n    auto foundStripeIdx = sem_.wait(stripeIdx);\n    return sem_.payload(foundStripeIdx).dequeue();\n  }\n\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    const auto stripeIdx = getStripeIdx();\n    if (auto foundStripeIdx = sem_.try_wait_for(stripeIdx, time)) {\n      return sem_.payload(*foundStripeIdx).dequeue();\n    }\n    return none;\n  }\n\n  size_t size() override { return sem_.valueGuess(); }\n\n private:\n  static size_t getStripeIdx() { return LLCAccessSpreader::get().current(); }\n\n  StripedThrottledLifoSem<EDFPriorityQueue<T>> sem_;\n};\n\n// Specialized implementation for the numStripes == 1 case, to avoid the\n// StripedThrottledLifoSem overhead when we don't need it.\ntemplate <class T>\nclass EDFPriorityBlockingQueue final : public BlockingQueueWithDeadline<T> {\n public:\n  explicit EDFPriorityBlockingQueue(const ThrottledLifoSem::Options& options)\n      : sem_(options) {}\n\n  BlockingQueueAddResult addWithDeadline(T&& item, uint64_t deadline) override {\n    pq_.enqueue(std::move(item), deadline);\n    return sem_.post();\n  }\n\n  BlockingQueueAddResult add(T&& item) override {\n    return addWithDeadline(\n        std::move(item), StripedEDFThreadPoolExecutor::kLatestDeadline);\n  }\n\n  T take() override {\n    sem_.wait();\n    return pq_.dequeue();\n  }\n\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    if (sem_.try_wait_for(time)) {\n      return pq_.dequeue();\n    }\n    return none;\n  }\n\n  size_t size() override { return sem_.valueGuess(); }\n\n private:\n  EDFPriorityQueue<T> pq_;\n  ThrottledLifoSem sem_;\n};\n\nstd::unique_ptr<BlockingQueue<CPUThreadPoolExecutor::CPUTask>> makeQueue(\n    const StripedEDFThreadPoolExecutor::Options& options) {\n  if (LLCAccessSpreader::get().numStripes() > 1) {\n    return std::make_unique<\n        StripedEDFPriorityBlockingQueue<CPUThreadPoolExecutor::CPUTask>>(\n        options.tlsOptions);\n  } else {\n    return std::make_unique<\n        EDFPriorityBlockingQueue<CPUThreadPoolExecutor::CPUTask>>(\n        options.tlsOptions);\n  }\n}\n\n} // namespace\n\nStripedEDFThreadPoolExecutor::StripedEDFThreadPoolExecutor(\n    std::pair<size_t, size_t> numThreads,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    const Options& options)\n    : CPUThreadPoolExecutor(\n          numThreads, makeQueue(options), std::move(threadFactory)) {}\n\nvoid StripedEDFThreadPoolExecutor::add(Func f, uint64_t deadline) {\n  CPUTask task(std::move(f), {}, {}, 0);\n  CPUThreadPoolExecutor::addImpl(\n      [this, deadline](auto&& task) {\n        using QueueType = BlockingQueueWithDeadline<CPUTask>;\n        auto* taskQueue = static_cast<QueueType*>(getTaskQueue());\n        DCHECK_EQ(taskQueue, dynamic_cast<QueueType*>(getTaskQueue()));\n        return taskQueue->addWithDeadline(std::move(task), deadline);\n      },\n      std::move(task));\n}\n\nvoid StripedEDFThreadPoolExecutor::add(\n    std::vector<Func> fs, uint64_t deadline) {\n  // It might be possible to optimize this if we implement post(n) in\n  // StripedThrottledLifoSem, but this variant is very rarely used.\n  for (auto& f : fs) {\n    add(std::move(f), deadline);\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/StripedEDFThreadPoolExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/SoftRealTimeExecutor.h>\n#include <folly/synchronization/ThrottledLifoSem.h>\n\nnamespace folly {\n\n/**\n * An approximate implementation of an Earliest Deadline First executor.\n *\n * Instead of having a global priority queue, we maintain one independent queue\n * for each LLC cache, to avoid expensive cross-LLC traffic. This implies that\n * the EDF policy is only honored among tasks submitted from CPUs sharing the\n * same LLC. In practice, each LLC should have enough CPUs to make the\n * approximation good enough for most use cases.\n */\nclass StripedEDFThreadPoolExecutor\n    : public SoftRealTimeExecutor,\n      public CPUThreadPoolExecutor {\n public:\n  struct Options {\n    ThrottledLifoSem::Options tlsOptions;\n  };\n\n  static constexpr uint64_t kEarliestDeadline = 0;\n  static constexpr uint64_t kLatestDeadline =\n      std::numeric_limits<uint64_t>::max();\n\n  explicit StripedEDFThreadPoolExecutor(\n      size_t numThreads,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"StripedEDFTP\"),\n      const Options& options = {})\n      : StripedEDFThreadPoolExecutor(\n            {numThreads, numThreads}, std::move(threadFactory), options) {}\n\n  explicit StripedEDFThreadPoolExecutor(\n      std::pair<size_t, size_t> numThreads,\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"StripedEDFTP\"),\n      const Options& options = {});\n\n  using CPUThreadPoolExecutor::add;\n\n  // SoftRealTimeExecutor\n  void add(Func f, uint64_t deadline) override;\n  void add(std::vector<Func> fs, uint64_t deadline) override;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ThreadPoolExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ThreadPoolExecutor.h>\n\n#include <ctime>\n\n#include <folly/ExceptionString.h>\n#include <folly/GLog.h>\n#include <folly/concurrency/ProcessLocalUniqueId.h>\n#include <folly/executors/GlobalThreadPoolList.h>\n#include <folly/portability/PThread.h>\n#include <folly/synchronization/AsymmetricThreadFence.h>\n#include <folly/tracing/StaticTracepoint.h>\n\nnamespace folly {\n\nusing SyncVecThreadPoolExecutors =\n    folly::Synchronized<std::vector<ThreadPoolExecutor*>>;\n\nSyncVecThreadPoolExecutors& getSyncVecThreadPoolExecutors() {\n  static Indestructible<SyncVecThreadPoolExecutors> storage;\n  return *storage;\n}\n\nvoid ThreadPoolExecutor::registerThreadPoolExecutor(ThreadPoolExecutor* tpe) {\n  getSyncVecThreadPoolExecutors().wlock()->push_back(tpe);\n}\n\nvoid ThreadPoolExecutor::deregisterThreadPoolExecutor(ThreadPoolExecutor* tpe) {\n  getSyncVecThreadPoolExecutors().withWLock([tpe](auto& tpes) {\n    tpes.erase(std::remove(tpes.begin(), tpes.end(), tpe), tpes.end());\n  });\n}\n\nFOLLY_GFLAGS_DEFINE_int64(\n    threadtimeout_ms,\n    60000,\n    \"Idle time before ThreadPoolExecutor threads are joined\");\n\nThreadPoolExecutor::ThreadPoolExecutor(\n    size_t /* maxThreads */,\n    size_t minThreads,\n    std::shared_ptr<ThreadFactory> threadFactory)\n    : threadFactory_(std::move(threadFactory)),\n      threadPoolHook_(\"folly::ThreadPoolExecutor\"),\n      minThreads_(minThreads) {\n  threadTimeout_ = std::chrono::milliseconds(FLAGS_threadtimeout_ms);\n}\n\nThreadPoolExecutor::~ThreadPoolExecutor() {\n  joinKeepAliveOnce();\n  CHECK_EQ(0, threadList_.get().size());\n  auto* taskObserver =\n      taskObservers_.exchange(nullptr, std::memory_order_acquire);\n  while (taskObserver != nullptr) {\n    delete std::exchange(taskObserver, taskObserver->next_);\n  }\n}\n\nThreadPoolExecutor::Task::Task(\n    Func&& func,\n    std::chrono::milliseconds expiration,\n    Func&& expireCallback,\n    int8_t pri)\n    : func_(std::move(func)),\n      // Assume that the task in enqueued on creation\n      enqueueTime_(std::chrono::steady_clock::now()),\n      context_(folly::RequestContext::saveContext()),\n      priority_(pri),\n      taskId_(processLocalUniqueId()) {\n  if (expiration > std::chrono::milliseconds::zero()) {\n    expiration_ = std::make_unique<Expiration>();\n    expiration_->expiration = expiration;\n    expiration_->expireCallback = std::move(expireCallback);\n  }\n}\n\n/* static */ void ThreadPoolExecutor::fillTaskInfo(\n    const Task& task, TaskInfo& info) {\n  info.priority = task.priority();\n  if (task.context_) {\n    info.requestId = task.context_->getRootId();\n  }\n  info.enqueueTime = task.enqueueTime_;\n  info.taskId = task.taskId_;\n}\n\nvoid ThreadPoolExecutor::registerTaskEnqueue(const Task& task) {\n  TaskInfo info;\n  fillTaskInfo(task, info);\n  forEachTaskObserver([&](auto& observer) { observer.taskEnqueued(info); });\n  FOLLY_SDT(\n      folly,\n      thread_pool_executor_task_enqueued,\n      threadFactory_->getNamePrefix().c_str(),\n      info.requestId,\n      info.enqueueTime.time_since_epoch().count(),\n      info.taskId);\n}\n\nvoid ThreadPoolExecutor::runTask(const ThreadPtr& thread, Task&& task) {\n  thread->idle.store(false, std::memory_order_relaxed);\n\n  auto startTime = std::chrono::steady_clock::now();\n  ProcessedTaskInfo taskInfo;\n  fillTaskInfo(task, taskInfo);\n  taskInfo.waitTime = startTime - task.enqueueTime_;\n  FOLLY_SDT(\n      folly,\n      thread_pool_executor_task_dequeued,\n      threadFactory_->getNamePrefix().c_str(),\n      taskInfo.requestId,\n      taskInfo.enqueueTime.time_since_epoch().count(),\n      taskInfo.waitTime.count(),\n      taskInfo.taskId);\n  forEachTaskObserver([&](auto& observer) { observer.taskDequeued(taskInfo); });\n\n  {\n    folly::RequestContextScopeGuard rctx(task.context_);\n    if (task.expiration_ != nullptr &&\n        taskInfo.waitTime >= task.expiration_->expiration) {\n      task.func_ = nullptr;\n      taskInfo.expired = true;\n      if (task.expiration_->expireCallback != nullptr) {\n        invokeCatchingExns(\n            \"ThreadPoolExecutor: expireCallback\",\n            std::exchange(task.expiration_->expireCallback, {}));\n      }\n    } else {\n      invokeCatchingExns(\n          \"ThreadPoolExecutor: func\", std::exchange(task.func_, {}));\n      if (task.expiration_ != nullptr) {\n        task.expiration_->expireCallback = nullptr;\n      }\n    }\n  }\n  if (!taskInfo.expired) {\n    taskInfo.runTime = std::chrono::steady_clock::now() - startTime;\n  }\n\n  // Times in this USDT use granularity of std::chrono::steady_clock::duration,\n  // which is platform dependent. On Facebook servers, the granularity is\n  // nanoseconds. We explicitly do not perform any unit conversions to avoid\n  // unnecessary costs and leave it to consumers of this data to know what\n  // effective clock resolution is.\n  FOLLY_SDT(\n      folly,\n      thread_pool_executor_task_stats,\n      threadFactory_->getNamePrefix().c_str(),\n      taskInfo.requestId,\n      taskInfo.enqueueTime.time_since_epoch().count(),\n      taskInfo.waitTime.count(),\n      taskInfo.runTime.count(),\n      taskInfo.taskId);\n  forEachTaskObserver([&](auto& observer) {\n    observer.taskProcessed(taskInfo);\n  });\n\n  thread->processedTasks = thread->processedTasks + 1;\n\n  thread->idle.store(true, std::memory_order_relaxed);\n  thread->lastActiveTime.store(\n      std::chrono::steady_clock::now(), std::memory_order_relaxed);\n}\n\nvoid ThreadPoolExecutor::add(Func, std::chrono::milliseconds, Func) {\n  throw std::runtime_error(\n      \"add() with expiration is not implemented for this Executor\");\n}\n\nsize_t ThreadPoolExecutor::numThreads() const {\n  return maxThreads_.load(std::memory_order_relaxed);\n}\n\nsize_t ThreadPoolExecutor::numActiveThreads() const {\n  return activeThreads_.load(std::memory_order_relaxed);\n}\n\n// Set the maximum number of running threads.\nvoid ThreadPoolExecutor::setNumThreads(size_t numThreads) {\n  /* Since ThreadPoolExecutor may be dynamically adjusting the number of\n     threads, we adjust the relevant variables instead of changing\n     the number of threads directly.  Roughly:\n\n     If numThreads < minthreads reset minThreads to numThreads.\n\n     If numThreads < active threads, reduce number of running threads.\n\n     If the number of pending tasks is > 0, then increase the currently\n     active number of threads such that we can run all the tasks, or reach\n     numThreads.\n\n     Note that if there are observers, we actually have to create all\n     the threads, because some observer implementations need to 'observe'\n     all thread creation (see tests for an example of this)\n  */\n\n  validateNumThreads(numThreads);\n  size_t numThreadsToJoin = 0;\n  {\n    std::unique_lock w{threadListLock_};\n    auto pending = getPendingTaskCountImpl();\n    maxThreads_.store(numThreads, std::memory_order_relaxed);\n    auto active = activeThreads_.load(std::memory_order_relaxed);\n    auto minthreads = minThreads_.load(std::memory_order_relaxed);\n    if (numThreads < minthreads) {\n      minthreads = numThreads;\n      minThreads_.store(numThreads, std::memory_order_relaxed);\n    }\n    // threadsCanTimeout_ can only transition from false to true if maxThreads_\n    // is increased (in which case minThreads_ will stay the same). So even if\n    // the current threads cannot timeout until the next wakeup, all the new\n    // threads can timeout, thus we can get down to minThreads_ active threads.\n    threadsCanTimeout_.store(minthreads != numThreads);\n    if (active > numThreads) {\n      numThreadsToJoin = active - numThreads;\n      assert(numThreadsToJoin <= active - minthreads);\n      ThreadPoolExecutor::removeThreads(numThreadsToJoin, false);\n      activeThreads_.store(numThreads, std::memory_order_relaxed);\n    } else if (pending > 0 || !observers_.empty() || active < minthreads) {\n      size_t numToAdd = std::min(pending, numThreads - active);\n      if (!observers_.empty()) {\n        numToAdd = numThreads - active;\n      }\n      if (active + numToAdd < minthreads) {\n        numToAdd = minthreads - active;\n      }\n      ThreadPoolExecutor::addThreads(numToAdd);\n      activeThreads_.store(active + numToAdd, std::memory_order_relaxed);\n    }\n  }\n\n  /* We may have removed some threads, attempt to join them */\n  joinStoppedThreads(numThreadsToJoin);\n}\n\n// threadListLock_ is writelocked\nvoid ThreadPoolExecutor::addThreads(size_t n) {\n  std::vector<ThreadPtr> newThreads;\n  for (size_t i = 0; i < n; i++) {\n    newThreads.push_back(makeThread());\n  }\n  for (auto& thread : newThreads) {\n    // TODO need a notion of failing to create the thread\n    // and then handling for that case\n    thread->handle = threadFactory_->newThread(\n        std::bind(&ThreadPoolExecutor::threadRun, this, thread));\n  }\n  afterConstructThreads(newThreads);\n}\n\n// threadListLock_ is writelocked\nbool ThreadPoolExecutor::tryAddOneThread() noexcept {\n  auto thread = makeThread();\n  try {\n    thread->handle = threadFactory_->newThread(\n        std::bind(&ThreadPoolExecutor::threadRun, this, thread));\n  } catch (...) {\n    FB_LOG_EVERY_MS(ERROR, 1000)\n        << \"ThreadPoolExecutor: thread creation failed: \"\n        << folly::exceptionStr(std::current_exception());\n    return false;\n  }\n  afterConstructThreads({&thread, 1});\n  return true;\n}\n\n// threadListLock_ is writelocked\nvoid ThreadPoolExecutor::afterConstructThreads(\n    folly::span<const ThreadPtr> newThreads) noexcept {\n  for (auto& thread : newThreads) {\n    threadList_.add(thread);\n  }\n  for (auto& thread : newThreads) {\n    thread->startupBaton.wait(\n        folly::Baton<>::wait_options().logging_enabled(false));\n  }\n  for (auto& o : observers_) {\n    for (auto& thread : newThreads) {\n      o->threadStarted(thread.get());\n      handleObserverRegisterThread(thread.get(), *o);\n    }\n  }\n}\n\n// threadListLock_ is writelocked\nvoid ThreadPoolExecutor::removeThreads(size_t n, bool isJoin) {\n  // Block early thread stopping.\n  isJoin_.store(isJoin, std::memory_order_release);\n  stopThreads(n);\n}\n\nvoid ThreadPoolExecutor::joinStoppedThreads(size_t n) noexcept {\n  for (size_t i = 0; i < n; i++) {\n    auto thread = stoppedThreads_.take();\n    thread->handle.join();\n  }\n}\n\nvoid ThreadPoolExecutor::stopAndJoinAllThreads(bool isJoin) {\n  size_t n = 0;\n  {\n    std::unique_lock w{threadListLock_};\n    maxThreads_.store(0, std::memory_order_release);\n    activeThreads_.store(0, std::memory_order_release);\n    n = threadList_.get().size();\n    removeThreads(n, isJoin);\n    n += threadsToJoin_.load(std::memory_order_relaxed);\n    threadsToJoin_.store(0, std::memory_order_relaxed);\n  }\n  joinStoppedThreads(n);\n  CHECK_EQ(0, threadList_.get().size());\n  CHECK_EQ(0, stoppedThreads_.size());\n}\n\nvoid ThreadPoolExecutor::stop() {\n  joinKeepAliveOnce();\n  stopAndJoinAllThreads(/* isJoin */ false);\n}\n\nvoid ThreadPoolExecutor::join() {\n  joinKeepAliveOnce();\n  stopAndJoinAllThreads(/* isJoin */ true);\n}\n\nvoid ThreadPoolExecutor::withAll(FunctionRef<void(ThreadPoolExecutor&)> f) {\n  getSyncVecThreadPoolExecutors().withRLock([f](auto& tpes) {\n    for (auto tpe : tpes) {\n      f(*tpe);\n    }\n  });\n}\n\nThreadPoolExecutor::PoolStats ThreadPoolExecutor::getPoolStats() const {\n  const auto now = std::chrono::steady_clock::now();\n  std::shared_lock r{threadListLock_};\n  ThreadPoolExecutor::PoolStats stats;\n  size_t activeTasks = 0;\n  size_t idleAlive = 0;\n  uint64_t processedTasks = stoppedThreadProcessedTasks_;\n  for (const auto& thread : threadList_.get()) {\n    if (thread->idle.load(std::memory_order_relaxed)) {\n      const std::chrono::nanoseconds idleTime =\n          now - thread->lastActiveTime.load(std::memory_order_relaxed);\n      stats.maxIdleTime = std::max(stats.maxIdleTime, idleTime);\n      idleAlive++;\n    } else {\n      activeTasks++;\n    }\n    processedTasks += thread->processedTasks;\n  }\n  stats.pendingTaskCount = getPendingTaskCountImpl();\n  stats.totalTaskCount = stats.pendingTaskCount + activeTasks;\n  stats.processedTaskCount = processedTasks;\n\n  stats.threadCount = maxThreads_.load(std::memory_order_relaxed);\n  stats.activeThreadCount =\n      activeThreads_.load(std::memory_order_relaxed) - idleAlive;\n  stats.idleThreadCount = stats.threadCount - stats.activeThreadCount;\n  return stats;\n}\n\nsize_t ThreadPoolExecutor::getPendingTaskCount() const {\n  std::shared_lock r{threadListLock_};\n  return getPendingTaskCountImpl();\n}\n\nconst std::string& ThreadPoolExecutor::getName() const {\n  return threadFactory_->getNamePrefix();\n}\n\nstd::atomic<uint64_t> ThreadPoolExecutor::Thread::nextId(0);\n\nstd::chrono::nanoseconds ThreadPoolExecutor::Thread::usedCpuTime() const {\n  using std::chrono::nanoseconds;\n  using std::chrono::seconds;\n  timespec tp{};\n#ifdef __linux__\n  clockid_t clockid;\n  auto th = const_cast<std::thread&>(handle).native_handle();\n  if (!pthread_getcpuclockid(th, &clockid)) {\n    clock_gettime(clockid, &tp);\n  }\n#endif\n  return nanoseconds(tp.tv_nsec) + seconds(tp.tv_sec);\n}\n\nvoid ThreadPoolExecutor::subscribeToTaskStats(TaskStatsCallback cb) {\n  class TaskStatsCallbackObserver : public TaskObserver {\n   public:\n    explicit TaskStatsCallbackObserver(TaskStatsCallback&& cob)\n        : cob_(std::move(cob)) {}\n\n    void taskProcessed(const ProcessedTaskInfo& info) noexcept override {\n      invokeCatchingExns(\"ThreadPoolExecutor: task stats callback\", [&] {\n        cob_(info);\n      });\n    }\n\n   private:\n    TaskStatsCallback cob_;\n  };\n\n  addTaskObserver(std::make_unique<TaskStatsCallbackObserver>(std::move(cb)));\n}\n\nvoid ThreadPoolExecutor::addTaskObserver(\n    std::unique_ptr<TaskObserver> taskObserver) {\n  auto taskObserverPtr = taskObserver.release();\n  auto* head = taskObservers_.load(std::memory_order_relaxed);\n  do {\n    taskObserverPtr->next_ = head;\n  } while (!taskObservers_.compare_exchange_weak(\n      head,\n      taskObserverPtr,\n      std::memory_order_acq_rel,\n      std::memory_order_relaxed));\n}\n\nBlockingQueueAddResult ThreadPoolExecutor::StoppedThreadQueue::add(\n    ThreadPoolExecutor::ThreadPtr&& item) {\n  std::lock_guard guard(mutex_);\n  queue_.push(std::move(item));\n  return sem_.post();\n}\n\nThreadPoolExecutor::ThreadPtr ThreadPoolExecutor::StoppedThreadQueue::take() {\n  while (true) {\n    {\n      std::lock_guard guard(mutex_);\n      if (!queue_.empty()) {\n        auto item = std::move(queue_.front());\n        queue_.pop();\n        return item;\n      }\n    }\n    sem_.wait();\n  }\n}\n\nfolly::Optional<ThreadPoolExecutor::ThreadPtr>\nThreadPoolExecutor::StoppedThreadQueue::try_take_for(\n    std::chrono::milliseconds time) {\n  while (true) {\n    {\n      std::lock_guard guard(mutex_);\n      if (!queue_.empty()) {\n        auto item = std::move(queue_.front());\n        queue_.pop();\n        return item;\n      }\n    }\n    if (!sem_.try_wait_for(time)) {\n      return folly::none;\n    }\n  }\n}\n\nsize_t ThreadPoolExecutor::StoppedThreadQueue::size() {\n  std::lock_guard guard(mutex_);\n  return queue_.size();\n}\n\nvoid ThreadPoolExecutor::addObserver(std::shared_ptr<Observer> o) {\n  {\n    std::unique_lock r{threadListLock_};\n    observers_.push_back(o);\n    for (auto& thread : threadList_.get()) {\n      o->threadPreviouslyStarted(thread.get());\n      handleObserverRegisterThread(thread.get(), *o);\n    }\n  }\n  ensureMaxActiveThreads();\n}\n\nvoid ThreadPoolExecutor::removeObserver(std::shared_ptr<Observer> o) {\n  std::unique_lock r{threadListLock_};\n  for (auto& thread : threadList_.get()) {\n    o->threadNotYetStopped(thread.get());\n    handleObserverUnregisterThread(thread.get(), *o);\n  }\n\n  for (auto it = observers_.begin(); it != observers_.end(); it++) {\n    if (*it == o) {\n      observers_.erase(it);\n      return;\n    }\n  }\n  DCHECK(false);\n}\n\n// Idle threads may have destroyed themselves, attempt to join\n// them here\nvoid ThreadPoolExecutor::ensureJoined() noexcept {\n  auto tojoin = threadsToJoin_.load(std::memory_order_relaxed);\n  if (tojoin) {\n    {\n      std::unique_lock w{threadListLock_};\n      tojoin = threadsToJoin_.load(std::memory_order_relaxed);\n      threadsToJoin_.store(0, std::memory_order_relaxed);\n    }\n    joinStoppedThreads(tojoin);\n  }\n}\n\n// threadListLock_ must be write locked.\nbool ThreadPoolExecutor::tryTimeoutThread() {\n  // Try to stop based on idle thread timeout (try_take_for),\n  // if there are at least minThreads running.\n  if (!minActive()) {\n    return false;\n  }\n\n  // Remove thread from active count\n  activeThreads_.store(\n      activeThreads_.load(std::memory_order_relaxed) - 1,\n      std::memory_order_relaxed);\n\n  // There is a memory ordering constraint w.r.t the queue\n  // implementation's add() and getPendingTaskCountImpl() - while many\n  // queues have seq_cst ordering, some do not, so add an explicit\n  // barrier.  tryTimeoutThread is the slow path and only happens once\n  // every thread timeout; use asymmetric barrier to keep add() fast.\n  asymmetric_thread_fence_heavy(std::memory_order_seq_cst);\n\n  // If this is based on idle thread timeout, then\n  // adjust vars appropriately (otherwise stop() or join()\n  // does this).\n  if (getPendingTaskCountImpl() > 0) {\n    // There are still pending tasks, we can't stop yet.\n    // re-up active threads and return.\n    activeThreads_.store(\n        activeThreads_.load(std::memory_order_relaxed) + 1,\n        std::memory_order_relaxed);\n    return false;\n  }\n\n  threadsToJoin_.store(\n      threadsToJoin_.load(std::memory_order_relaxed) + 1,\n      std::memory_order_relaxed);\n\n  return true;\n}\n\n// If we can't ensure that we were able to hand off a task to a thread,\n// attempt to start a thread that handled the task, if we aren't already\n// running the maximum number of threads.\nvoid ThreadPoolExecutor::ensureActiveThreads() noexcept {\n  ensureJoined();\n\n  // Matches barrier in tryTimeoutThread().  Ensure task added\n  // is seen before loading activeThreads_ below.\n  asymmetric_thread_fence_light(std::memory_order_seq_cst);\n\n  // Fast path assuming we are already at max threads.\n  auto active = activeThreads_.load(std::memory_order_relaxed);\n  auto total = maxThreads_.load(std::memory_order_relaxed);\n\n  if (active >= total) {\n    return;\n  }\n\n  std::unique_lock w{threadListLock_};\n  // Double check behind lock.\n  active = activeThreads_.load(std::memory_order_relaxed);\n  total = maxThreads_.load(std::memory_order_relaxed);\n  if (active >= total) {\n    return;\n  }\n  if (tryAddOneThread()) {\n    activeThreads_.store(active + 1, std::memory_order_relaxed);\n  }\n}\n\nvoid ThreadPoolExecutor::ensureMaxActiveThreads() {\n  ensureJoined();\n  asymmetric_thread_fence_light(std::memory_order_seq_cst);\n  auto active = activeThreads_.load(std::memory_order_relaxed);\n  auto total = maxThreads_.load(std::memory_order_relaxed);\n  if (active >= total) {\n    return;\n  }\n  std::unique_lock w{threadListLock_};\n  active = activeThreads_.load(std::memory_order_relaxed);\n  total = maxThreads_.load(std::memory_order_relaxed);\n  if (active >= total) {\n    return;\n  }\n  ThreadPoolExecutor::addThreads(total - active);\n  activeThreads_.store(total, std::memory_order_relaxed);\n}\n\n// If an idle thread times out, only join it if there are at least\n// minThreads threads.\nbool ThreadPoolExecutor::minActive() {\n  return activeThreads_.load(std::memory_order_relaxed) >\n      minThreads_.load(std::memory_order_relaxed);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ThreadPoolExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <mutex>\n#include <queue>\n\n#include <glog/logging.h>\n\n#include <folly/DefaultKeepAliveExecutor.h>\n#include <folly/Memory.h>\n#include <folly/SharedMutex.h>\n#include <folly/container/span.h>\n#include <folly/executors/GlobalThreadPoolList.h>\n#include <folly/executors/task_queue/LifoSemMPMCQueue.h>\n#include <folly/executors/thread_factory/NamedThreadFactory.h>\n#include <folly/io/async/Request.h>\n#include <folly/portability/GFlags.h>\n#include <folly/synchronization/AtomicStruct.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\nnamespace folly {\n\n/* Base class for implementing threadpool based executors.\n *\n * Dynamic thread behavior:\n *\n * ThreadPoolExecutors may vary their actual running number of threads\n * between minThreads_ and maxThreads_, tracked by activeThreads_.\n * The actual implementation of joining an idle thread is left to the\n * ThreadPoolExecutors' subclass (typically by LifoSem try_take_for\n * timing out).  Idle threads should be removed from threadList_, and\n * threadsToJoin incremented, and activeThreads_ decremented.\n *\n * On task add(), if an executor can guarantee there is an active\n * thread that will handle the task, then nothing needs to be done.\n * If not, then ensureActiveThreads() should be called to possibly\n * start another pool thread, up to maxThreads_.\n *\n * ensureJoined() is called on add(), such that we can join idle\n * threads that were destroyed (which can't be joined from\n * themselves).\n *\n * Thread pool stats accounting:\n *\n * Derived classes must register instances to keep stats on all thread\n * pools by calling registerThreadPoolExecutor(this) on constructions\n * and deregisterThreadPoolExecutor(this) on destruction.\n *\n * Registration must be done wherever getPendingTaskCountImpl is implemented\n * and getPendingTaskCountImpl should be marked 'final' to avoid data races.\n */\nclass ThreadPoolExecutor : public DefaultKeepAliveExecutor {\n public:\n  explicit ThreadPoolExecutor(\n      size_t maxThreads,\n      size_t minThreads,\n      std::shared_ptr<ThreadFactory> threadFactory);\n\n  ~ThreadPoolExecutor() override;\n\n  void add(Func func) override = 0;\n  /**\n   * If func doesn't get started within expiration time after its enqueued,\n   * expireCallback will be run\n   *\n   * @param func  Main function to be executed\n   * @param expiration Maximum time to wait for func to start execution\n   * @param expireCallback If expiration limit is reached, execute this callback\n   */\n  virtual void add(\n      Func func, std::chrono::milliseconds expiration, Func expireCallback);\n\n  void setThreadFactory(std::shared_ptr<ThreadFactory> threadFactory) {\n    CHECK(numThreads() == 0);\n    threadFactory_ = std::move(threadFactory);\n  }\n\n  std::shared_ptr<ThreadFactory> getThreadFactory() const {\n    return threadFactory_;\n  }\n\n  size_t numThreads() const;\n  void setNumThreads(size_t numThreads);\n\n  // Return actual number of active threads -- this could be different from\n  // numThreads() due to ThreadPoolExecutor's dynamic behavior.\n  size_t numActiveThreads() const;\n\n  /*\n   * stop() is best effort - there is no guarantee that unexecuted tasks won't\n   * be executed before it returns. Specifically, IOThreadPoolExecutor's stop()\n   * behaves like join().\n   */\n  virtual void stop();\n  virtual void join();\n\n  /**\n   * Execute f against all ThreadPoolExecutors, primarily for retrieving and\n   * exporting stats.\n   */\n  static void withAll(FunctionRef<void(ThreadPoolExecutor&)> f);\n\n  struct PoolStats {\n    PoolStats()\n        : threadCount(0),\n          idleThreadCount(0),\n          activeThreadCount(0),\n          pendingTaskCount(0),\n          totalTaskCount(0),\n          processedTaskCount(0),\n          maxIdleTime(0) {}\n    size_t threadCount, idleThreadCount, activeThreadCount;\n    uint64_t pendingTaskCount, totalTaskCount, processedTaskCount;\n    std::chrono::nanoseconds maxIdleTime;\n  };\n\n  PoolStats getPoolStats() const;\n  size_t getPendingTaskCount() const;\n  const std::string& getName() const;\n\n  /**\n   * Return the cumulative CPU time used by all threads in the pool, including\n   * those that are no longer alive. Requires system support for per-thread CPU\n   * clocks. If not available, the function returns 0. This operation can be\n   * expensive.\n   */\n  std::chrono::nanoseconds getUsedCpuTime() const {\n    std::shared_lock r{threadListLock_};\n    return threadList_.getUsedCpuTime();\n  }\n\n  /**\n   * Base class for threads created with ThreadPoolExecutor.\n   * Some subclasses have methods that operate on these\n   * handles.\n   */\n  class ThreadHandle {\n   public:\n    virtual ~ThreadHandle() = default;\n  };\n\n  /**\n   * Observer interface for thread start/stop.\n   * Provides hooks so actions can be taken when\n   * threads are created\n   */\n  class Observer {\n   public:\n    virtual ~Observer() = default;\n\n    virtual void threadStarted(ThreadHandle*) noexcept {}\n    virtual void threadStopped(ThreadHandle*) noexcept {}\n    virtual void threadPreviouslyStarted(ThreadHandle* h) noexcept {\n      threadStarted(h);\n    }\n    virtual void threadNotYetStopped(ThreadHandle* h) noexcept {\n      threadStopped(h);\n    }\n  };\n\n  virtual void addObserver(std::shared_ptr<Observer>);\n  virtual void removeObserver(std::shared_ptr<Observer>);\n\n  struct TaskInfo {\n    int8_t priority;\n    uint64_t requestId = 0;\n    std::chrono::steady_clock::time_point enqueueTime;\n    uint64_t taskId;\n  };\n\n  struct DequeuedTaskInfo : TaskInfo {\n    std::chrono::nanoseconds waitTime{0}; // Dequeue time - enqueueTime.\n  };\n\n  struct ProcessedTaskInfo : DequeuedTaskInfo {\n    bool expired = false;\n    std::chrono::nanoseconds runTime{0};\n  };\n\n  class TaskObserver {\n   public:\n    virtual ~TaskObserver() = default;\n\n    virtual void taskEnqueued(const TaskInfo& /* info */) noexcept {}\n    virtual void taskDequeued(const DequeuedTaskInfo& /* info */) noexcept {}\n    virtual void taskProcessed(const ProcessedTaskInfo& /* info */) noexcept {}\n\n   private:\n    friend class ThreadPoolExecutor;\n\n    TaskObserver* next_ = nullptr;\n  };\n\n  // For performance reasons, TaskObservers can be added but not removed. All\n  // added observers will be destroyed on executor destruction.\n  void addTaskObserver(std::unique_ptr<TaskObserver> taskObserver);\n\n  // TODO(ott): Migrate call sites to the TaskObserver interface.\n  using TaskStats = ProcessedTaskInfo;\n  using TaskStatsCallback = std::function<void(const TaskStats&)>;\n  [[deprecated(\"Use addTaskObserver()\")]] void subscribeToTaskStats(\n      TaskStatsCallback cb);\n\n  void setThreadDeathTimeout(std::chrono::milliseconds timeout) {\n    threadTimeout_ = timeout;\n  }\n\n protected:\n  // Prerequisite: threadListLock_ writelocked\n  void addThreads(size_t n);\n  // Prerequisite: threadListLock_ writelocked\n  bool tryAddOneThread() noexcept;\n  // Prerequisite: threadListLock_ writelocked\n  void removeThreads(size_t n, bool isJoin);\n\n  struct //\n      alignas(folly::cacheline_align_v) //\n      alignas(folly::AtomicStruct<std::chrono::steady_clock::time_point>) //\n      Thread : public ThreadHandle {\n    explicit Thread()\n        : id(nextId++),\n          handle(),\n          idle(true),\n          lastActiveTime(std::chrono::steady_clock::now()) {}\n\n    ~Thread() override = default;\n\n    std::chrono::nanoseconds usedCpuTime() const;\n\n    static std::atomic<uint64_t> nextId;\n    uint64_t id;\n\n    // Number of tasks processed by this worker.  Reset to zero when\n    // the thread stops.\n    folly::relaxed_atomic<uint64_t> processedTasks;\n\n    std::thread handle;\n    std::atomic<bool> idle;\n    folly::AtomicStruct<std::chrono::steady_clock::time_point> lastActiveTime;\n    folly::Baton<> startupBaton;\n  };\n\n  using ThreadPtr = std::shared_ptr<Thread>;\n\n  // Prerequisite: threadListLock_ writelocked\n  void afterConstructThreads(folly::span<const ThreadPtr> newThreads) noexcept;\n\n  struct Task {\n    struct Expiration {\n      std::chrono::milliseconds expiration;\n      Func expireCallback;\n    };\n\n    Task(\n        Func&& func,\n        std::chrono::milliseconds expiration,\n        Func&& expireCallback,\n        int8_t pri = 0);\n\n    int8_t priority() const { return priority_; }\n\n    Func func_;\n    std::chrono::steady_clock::time_point enqueueTime_;\n    std::shared_ptr<folly::RequestContext> context_;\n    std::unique_ptr<Expiration> expiration_;\n\n   private:\n    friend class ThreadPoolExecutor;\n\n    int8_t priority_;\n    uint64_t taskId_;\n  };\n\n  static void fillTaskInfo(const Task& task, TaskInfo& info);\n  void registerTaskEnqueue(const Task& task);\n  template <class F>\n  void forEachTaskObserver(F&& f) const {\n    auto* taskObserver = taskObservers_.load(std::memory_order_acquire);\n    while (taskObserver != nullptr) {\n      f(*taskObserver);\n      taskObserver = taskObserver->next_;\n    }\n  }\n\n  void runTask(const ThreadPtr& thread, Task&& task);\n\n  virtual void validateNumThreads(size_t /* numThreads */) {}\n\n  // The function that will be bound to pool threads. It must call\n  // thread->startupBaton.post() when it's ready to consume work.\n  virtual void threadRun(ThreadPtr thread) = 0;\n\n  // Stop n threads and put their ThreadPtrs in the stoppedThreads_ queue\n  // and remove them from threadList_, either synchronize or asynchronize\n  // Prerequisite: threadListLock_ writelocked\n  virtual void stopThreads(size_t n) = 0;\n\n  // Join n stopped threads and remove them from waitingForJoinThreads_ queue.\n  // Should not hold a lock because joining thread operation may invoke some\n  // cleanup operations on the thread, and those cleanup operations may\n  // require a lock on ThreadPoolExecutor.\n  void joinStoppedThreads(size_t n) noexcept;\n\n  // To implement shutdown.\n  void stopAndJoinAllThreads(bool isJoin);\n\n  // Create a suitable Thread struct\n  virtual ThreadPtr makeThread() { return std::make_shared<Thread>(); }\n\n  static void registerThreadPoolExecutor(ThreadPoolExecutor* tpe);\n  static void deregisterThreadPoolExecutor(ThreadPoolExecutor* tpe);\n\n  // Prerequisite: threadListLock_ readlocked or writelocked\n  virtual size_t getPendingTaskCountImpl() const = 0;\n\n  // Called with threadListLock_ readlocked or writelocked.\n  virtual void handleObserverRegisterThread(ThreadHandle*, Observer&) noexcept {\n  }\n  virtual void handleObserverUnregisterThread(\n      ThreadHandle*, Observer&) noexcept {}\n\n  class ThreadList {\n   public:\n    void add(const ThreadPtr& state) {\n      auto it = std::lower_bound(vec_.begin(), vec_.end(), state, Compare{});\n      vec_.insert(it, state);\n    }\n\n    void remove(const ThreadPtr& state) {\n      auto itPair =\n          std::equal_range(vec_.begin(), vec_.end(), state, Compare{});\n      CHECK(itPair.first != vec_.end());\n      CHECK(std::next(itPair.first) == itPair.second);\n      vec_.erase(itPair.first);\n      pastCpuUsed_ += state->usedCpuTime();\n    }\n\n    bool contains(const ThreadPtr& ts) const {\n      return std::binary_search(vec_.cbegin(), vec_.cend(), ts, Compare{});\n    }\n\n    const std::vector<ThreadPtr>& get() const { return vec_; }\n\n    std::chrono::nanoseconds getUsedCpuTime() const {\n      auto acc{pastCpuUsed_};\n      for (const auto& thread : vec_) {\n        acc += thread->usedCpuTime();\n      }\n      return acc;\n    }\n\n   private:\n    struct Compare {\n      bool operator()(const ThreadPtr& ts1, const ThreadPtr& ts2) const {\n        return ts1->id < ts2->id;\n      }\n    };\n    std::vector<ThreadPtr> vec_;\n    // cpu time used by threads that are no longer alive\n    std::chrono::nanoseconds pastCpuUsed_{0};\n  };\n\n  class StoppedThreadQueue : public BlockingQueue<ThreadPtr> {\n   public:\n    BlockingQueueAddResult add(ThreadPtr&& item) override;\n    ThreadPtr take() override;\n    size_t size() override;\n    folly::Optional<ThreadPtr> try_take_for(\n        std::chrono::milliseconds /*timeout */) override;\n\n   private:\n    folly::LifoSem sem_;\n    std::mutex mutex_;\n    std::queue<ThreadPtr> queue_;\n  };\n\n  std::shared_ptr<ThreadFactory> threadFactory_;\n\n  ThreadList threadList_;\n  mutable SharedMutex threadListLock_;\n  StoppedThreadQueue stoppedThreads_;\n  std::atomic<bool> isJoin_{false}; // whether the current downsizing is a join\n\n  std::vector<std::shared_ptr<Observer>> observers_;\n  folly::ThreadPoolListHook threadPoolHook_;\n\n  // Dynamic thread sizing functions and variables\n  void ensureMaxActiveThreads();\n  void ensureActiveThreads() noexcept;\n  void ensureJoined() noexcept;\n  bool minActive();\n  bool tryTimeoutThread();\n\n  // These are only modified while holding threadListLock_, but\n  // are read without holding the lock.\n  std::atomic<size_t> maxThreads_{0};\n  std::atomic<size_t> minThreads_{0};\n  std::atomic<size_t> activeThreads_{0};\n  std::atomic<bool> threadsCanTimeout_{true};\n\n  std::atomic<size_t> threadsToJoin_{0};\n  std::atomic<std::chrono::milliseconds> threadTimeout_;\n\n  // Number of tasks processed by stopped or joined threads.  Updated\n  // when a thread stops, which preceeds joining.  Requires holding\n  // the threadListLock_.\n  uint64_t stoppedThreadProcessedTasks_{0};\n\n  bool joinKeepAliveOnce() {\n    if (!std::exchange(keepAliveJoined_, true)) {\n      joinKeepAlive();\n      return true;\n    }\n    return false;\n  }\n\n  bool keepAliveJoined_{false};\n\n private:\n  std::atomic<TaskObserver*> taskObservers_{nullptr};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ThreadedExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ThreadedExecutor.h>\n\n#include <chrono>\n#include <utility>\n\n#include <glog/logging.h>\n\n#include <folly/ScopeGuard.h>\n#include <folly/executors/thread_factory/NamedThreadFactory.h>\n#include <folly/system/ThreadName.h>\n\nnamespace folly {\n\nThreadedExecutor::ThreadedExecutor(std::shared_ptr<ThreadFactory> threadFactory)\n    : threadFactory_(std::move(threadFactory)),\n      controlThread_([this] { control(); }) {}\n\nThreadedExecutor::~ThreadedExecutor() {\n  stopping_.store(true, std::memory_order_release);\n  controlMessages_.enqueue({Message::Type::StopControl, {}, {}});\n  controlThread_.join();\n  CHECK(running_.empty());\n  CHECK(controlMessages_.empty());\n}\n\nvoid ThreadedExecutor::add(Func func) {\n  CHECK(!stopping_.load(std::memory_order_acquire));\n  controlMessages_.enqueue({Message::Type::Start, std::move(func), {}});\n}\n\nstd::shared_ptr<ThreadFactory> ThreadedExecutor::newDefaultThreadFactory() {\n  return std::make_shared<NamedThreadFactory>(\"Threaded\");\n}\n\nvoid ThreadedExecutor::work(Func& func) {\n  invokeCatchingExns(\"ThreadedExecutor: func\", std::exchange(func, {}));\n  controlMessages_.enqueue(\n      {Message::Type::Join, {}, std::this_thread::get_id()});\n}\n\nvoid ThreadedExecutor::control() {\n  folly::setThreadName(\"ThreadedCtrl\");\n  bool controlStopping = false;\n  while (!(controlStopping && running_.empty())) {\n    auto msg = controlMessages_.dequeue();\n    switch (msg.type) {\n      case Message::Type::Start: {\n        auto th = threadFactory_->newThread(\n            [this, func = std::move(msg.startFunc)]() mutable { work(func); });\n        auto id = th.get_id();\n        running_[id] = std::move(th);\n        break;\n      }\n      case Message::Type::Join: {\n        auto it = running_.find(msg.joinTid);\n        CHECK(it != running_.end());\n        it->second.join();\n        running_.erase(it);\n        break;\n      }\n      case Message::Type::StopControl: {\n        CHECK(!std::exchange(controlStopping, true));\n        break;\n      }\n    }\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ThreadedExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <memory>\n#include <thread>\n\n#include <folly/Executor.h>\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/container/F14Map.h>\n#include <folly/executors/thread_factory/ThreadFactory.h>\n\nnamespace folly {\n\n/***\n *  ThreadedExecutor\n *\n *  An executor for blocking tasks.\n *\n *  This executor runs each task in its own thread. It works well for tasks\n *  which mostly sleep, but works poorly for tasks which mostly compute.\n *\n *  For each task given to the executor with `add`, the executor spawns a new\n *  thread for that task, runs the task in that thread, and joins the thread\n *  after the task has completed.\n *\n *  Spawning and joining task threads are done in the executor's internal\n *  control thread. Calls to `add` put the tasks to be run into a queue, where\n *  the control thread will find them.\n *\n *  There is currently no limitation on, or throttling of, concurrency.\n *\n *  This executor is not currently optimized for performance. For example, it\n *  makes no attempt to re-use task threads. Rather, it exists primarily to\n *  offload sleep-heavy tasks from the CPU executor, where they might otherwise\n *  be run.\n */\nclass ThreadedExecutor : public virtual folly::Executor {\n public:\n  explicit ThreadedExecutor(\n      std::shared_ptr<ThreadFactory> threadFactory = newDefaultThreadFactory());\n  ~ThreadedExecutor() override;\n\n  ThreadedExecutor(ThreadedExecutor const&) = delete;\n  ThreadedExecutor(ThreadedExecutor&&) = delete;\n\n  ThreadedExecutor& operator=(ThreadedExecutor const&) = delete;\n  ThreadedExecutor& operator=(ThreadedExecutor&&) = delete;\n\n  void add(Func func) override;\n\n private:\n  // TODO(ott): Switch to std::variant when available everywhere.\n  struct Message {\n    enum class Type { Start, Join, StopControl };\n    Type type;\n    Func startFunc;\n    std::thread::id joinTid;\n  };\n\n  static std::shared_ptr<ThreadFactory> newDefaultThreadFactory();\n\n  void work(Func& func);\n  void control();\n\n  std::shared_ptr<ThreadFactory> threadFactory_;\n\n  std::atomic<bool> stopping_{false};\n\n  UMPSCQueue<Message, /* MayBlock */ true> controlMessages_;\n  std::thread controlThread_;\n\n  // Accessed only by the control thread, so no synchronization.\n  F14FastMap<std::thread::id, std::thread> running_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ThreadedRepeatingFunctionRunner.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ThreadedRepeatingFunctionRunner.h>\n\n#include <iostream>\n\n#include <folly/system/ThreadName.h>\n\n#include <glog/logging.h>\n\nnamespace folly {\n\nThreadedRepeatingFunctionRunner::ThreadedRepeatingFunctionRunner() = default;\n\nThreadedRepeatingFunctionRunner::~ThreadedRepeatingFunctionRunner() {\n  if (stopImpl()) {\n    LOG(ERROR)\n        << \"ThreadedRepeatingFunctionRunner::stop() should already have been \"\n        << \"called, since we are now in the Runner's destructor. This is \"\n        << \"because it means that its threads may be accessing object state \"\n        << \"that was already destroyed -- e.g. members that were declared \"\n        << \"after the ThreadedRepeatingFunctionRunner.\";\n  }\n}\n\nvoid ThreadedRepeatingFunctionRunner::stop() {\n  stopImpl();\n}\n\nbool ThreadedRepeatingFunctionRunner::stopImpl() {\n  {\n    std::unique_lock lock(stopMutex_);\n    if (stopping_) {\n      return false; // Do nothing if stop() is called twice.\n    }\n    stopping_ = true;\n  }\n  stopCv_.notify_all();\n  for (auto& t : threads_) {\n    t.join();\n  }\n  return true;\n}\n\nvoid ThreadedRepeatingFunctionRunner::add(\n    std::string name, RepeatingFn fn, std::chrono::milliseconds initialSleep) {\n  threads_.emplace_back(\n      [name = std::move(name),\n       fn = std::move(fn),\n       initialSleep,\n       this]() mutable {\n        setThreadName(name);\n        executeInLoop(std::move(fn), initialSleep);\n      });\n}\n\nbool ThreadedRepeatingFunctionRunner::waitFor(\n    std::chrono::milliseconds duration) noexcept {\n  using clock = std::chrono::steady_clock;\n  const auto deadline = clock::now() + duration;\n  std::unique_lock lock(stopMutex_);\n  stopCv_.wait_until(lock, deadline, [&] {\n    return stopping_ || clock::now() > deadline;\n  });\n  return !stopping_;\n}\n\nvoid ThreadedRepeatingFunctionRunner::executeInLoop(\n    RepeatingFn fn, std::chrono::milliseconds initialSleep) noexcept {\n  auto duration = initialSleep;\n  while (waitFor(duration)) {\n    duration = fn();\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/ThreadedRepeatingFunctionRunner.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <condition_variable>\n#include <thread>\n#include <vector>\n\n#include <folly/Function.h>\n\nnamespace folly {\n\n/**\n * For each function `fn` you add to this object, `fn` will be run in a loop\n * in its own thread, with the thread sleeping between invocations of `fn`\n * for the duration returned by `fn`'s previous run.\n *\n * To clean up these threads, invoke `stop()`, which will interrupt sleeping\n * threads.  `stop()` will wait for already-running functions to return.\n *\n * == Alternatives ==\n *\n * If you want to multiplex multiple functions on the same thread, you can\n * either use EventBase with AsyncTimeout objects, or FunctionScheduler for\n * a slightly simpler API.\n *\n * == Thread-safety ==\n *\n * This type follows the common rule that:\n *  (1) const member functions are safe to call concurrently with const\n *      member functions, but\n *  (2) non-const member functions are not safe to call concurrently with\n *      any member functions.\n *\n * == Pitfalls ==\n *\n * Threads and classes don't mix well in C++, so you have to be very careful\n * if you want to have ThreadedRepeatingFunctionRunner as a member of your\n * class.  A reasonable pattern looks like this:\n *\n * // Your class must be `final` because inheriting from a class with\n * // threads can cause all sorts of subtle issues:\n * //  - Your base class might start threads that attempt to access derived\n * //    class state **before** that state was constructed.\n * //  - Your base class's destructor will only be able to stop threads\n * //    **after** the derived class state was destroyed -- and that state\n * //    might be accessed by the threads.\n * // In short, any derived class would have to do work to manage the\n * // threads itself, which makes inheritance a poor means of composition.\n * struct MyClass final {\n *   // Note that threads are NOT added in the constructor, for two reasons:\n *   //\n *   //   (1) If you first added some threads, and then had additional\n *   //       initialization (e.g. derived class constructors), `this` might\n *   //       not be fully constructed by the time the function threads\n *   //       started running, causing heisenbugs.\n *   //\n *   //   (2) If your constructor threw after thread creation, the class\n *   //       destructor would not be invoked, potentially leaving the\n *   //       threads running too long.\n *   //\n *   // It is much safer to have explicit two-step initialization, or to\n *   // lazily add threads the first time they are needed.\n *   MyClass() : count_(0) {}\n *\n *   // You must stop the threads as early as possible in the destruction\n *   // process (or even before).  If MyClass had derived classes, the final\n *   // derived class MUST always call stop() as the first thing in its\n *   // destructor -- otherwise, the worker threads might access already-\n *   // destroyed state.\n *   ~MyClass() {\n *     threads_.stop();  // Stop threads BEFORE destroying any state they use.\n *   }\n *\n *   // See the constructor for why two-stage initialization is preferred.\n *   void init() {\n *     threads_.add(bind(&MyClass::incrementCount, this));\n *   }\n *\n *   std::chrono::milliseconds incrementCount() {\n *     ++count_;\n *     return 10;\n *   }\n *\n * private:\n *   std::atomic<int> count_;\n *   // CAUTION: Declare last since the threads access other members of `this`.\n *   ThreadedRepeatingFunctionRunner threads_;\n * };\n */\nclass ThreadedRepeatingFunctionRunner final {\n public:\n  // Returns how long to wait before the next repetition. Must not throw.\n  using RepeatingFn = folly::Function<std::chrono::milliseconds() noexcept>;\n\n  ThreadedRepeatingFunctionRunner();\n  ~ThreadedRepeatingFunctionRunner();\n\n  /**\n   * Ideally, you will call this before initiating the destruction of the\n   * host object.  Otherwise, this should be the first thing in the\n   * destruction sequence.  If it comes any later, worker threads may access\n   * class state that had already been destroyed.\n   */\n  void stop();\n\n  /**\n   * Run your noexcept function `f` in a background loop, sleeping between\n   * calls for a duration returned by `f`.  Optionally waits for\n   * `initialSleep` before calling `f` for the first time.  Names the thread\n   * using up to the first 15 chars of `name`.\n   *\n   * DANGER: If a non-final class has a ThreadedRepeatingFunctionRunner\n   * member (which, by the way, must be declared last in the class), then\n   * you must not call add() in your constructor.  Otherwise, your thread\n   * risks accessing uninitialized data belonging to a child class.  To\n   * avoid this design bug, prefer to use two-stage initialization to start\n   * your threads.\n   */\n  void add(\n      std::string name,\n      RepeatingFn f,\n      std::chrono::milliseconds initialSleep = std::chrono::milliseconds(0));\n\n  size_t size() const { return threads_.size(); }\n\n private:\n  // Returns true if this is the first stop().\n  bool stopImpl();\n\n  // Sleep for a duration, or until stop() is called.\n  bool waitFor(std::chrono::milliseconds duration) noexcept;\n\n  // Noexcept allows us to get a good backtrace on crashes -- otherwise,\n  // std::terminate would get called **outside** of the thread function.\n  void executeInLoop(\n      RepeatingFn, std::chrono::milliseconds initialSleep) noexcept;\n\n  std::mutex stopMutex_;\n  bool stopping_{false}; // protected by stopMutex_\n  std::condition_variable stopCv_;\n\n  std::vector<std::thread> threads_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/TimedDrivableExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/TimedDrivableExecutor.h>\n\n#include <cstring>\n#include <ctime>\n#include <string>\n#include <tuple>\n\nnamespace folly {\n\nTimedDrivableExecutor::TimedDrivableExecutor() = default;\n\nTimedDrivableExecutor::~TimedDrivableExecutor() noexcept {\n  // Drain on destruction so that if work is added here during the collapse\n  // of a future train, it will propagate.\n  drain();\n}\n\nvoid TimedDrivableExecutor::add(Func callback) {\n  queue_.enqueue(std::move(callback));\n}\n\nvoid TimedDrivableExecutor::drive() noexcept {\n  wait();\n  run();\n}\n\nbool TimedDrivableExecutor::try_drive() noexcept {\n  return try_wait() && run() > 0;\n}\n\nsize_t TimedDrivableExecutor::run() noexcept {\n  size_t count = 0;\n  size_t n = queue_.size();\n\n  // If we have waited already, then func_ may have a value\n  if (func_) {\n    auto f = std::move(func_);\n    f();\n    count = 1;\n  }\n\n  while (count < n && queue_.try_dequeue(func_)) {\n    auto f = std::move(func_);\n    f();\n    ++count;\n  }\n\n  return count;\n}\n\nsize_t TimedDrivableExecutor::drain() noexcept {\n  size_t tasksRun = 0;\n  size_t tasksForSingleRun = 0;\n  while ((tasksForSingleRun = run()) != 0) {\n    tasksRun += tasksForSingleRun;\n  }\n  return tasksRun;\n}\n\nvoid TimedDrivableExecutor::wait() noexcept {\n  if (!func_) {\n    queue_.dequeue(func_);\n  }\n}\n\nbool TimedDrivableExecutor::try_wait() noexcept {\n  return func_ || queue_.try_dequeue(func_);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/TimedDrivableExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/executors/DrivableExecutor.h>\n\nnamespace folly {\n\n/*\n * A DrivableExecutor can be driven via its drive() method or its driveUntil()\n * that drives until some time point.\n */\nclass TimedDrivableExecutor : public DrivableExecutor {\n public:\n  TimedDrivableExecutor();\n  ~TimedDrivableExecutor() noexcept override;\n\n  /// Implements DrivableExecutor\n  void drive() noexcept override;\n\n  // Make progress if there is work to do and return true. Otherwise return\n  // false.\n  bool try_drive() noexcept;\n\n  // Make progress on this Executor's work. Acts as drive, except it will only\n  // wait for a period of timeout for work to be enqueued. If no work is\n  // enqueued by that point, it will return.\n  template <typename Rep, typename Period>\n  bool try_drive_for(\n      const std::chrono::duration<Rep, Period>& timeout) noexcept {\n    return try_wait_for(timeout) && run() > 0;\n  }\n\n  // Make progress on this Executor's work. Acts as drive, except it will only\n  // wait until deadline for work to be enqueued. If no work is enqueued by\n  // that point, it will return.\n  template <typename Clock, typename Duration>\n  bool try_drive_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    return try_wait_until(deadline) && run() > 0;\n  }\n\n  void add(Func) override;\n\n  /// Do work. Returns the number of functions that were executed (maybe 0).\n  /// Non-blocking, in the sense that we don't wait for work (we can't\n  /// control whether one of the functions blocks).\n  /// This is stable, it will not chase an ever-increasing tail of work.\n  /// This also means, there may be more work available to perform at the\n  /// moment that this returns.\n  size_t run() noexcept;\n\n  // Do work until there is no more work to do.\n  // Returns the number of functions that were executed (maybe 0).\n  // Unlike run, this method is not stable. It will chase an infinite tail of\n  // work so should be used with care.\n  // There will be no work available to perform at the moment that this\n  // returns.\n  size_t drain() noexcept;\n\n  /// Wait for work to do.\n  void wait() noexcept;\n\n  // Return true if there is work to do, false otherwise\n  bool try_wait() noexcept;\n\n  /// Wait for work to do or for a period of timeout, whichever is sooner.\n  template <typename Rep, typename Period>\n  bool try_wait_for(\n      const std::chrono::duration<Rep, Period>& timeout) noexcept {\n    return func_ || queue_.try_dequeue_for(func_, timeout);\n  }\n\n  /// Wait for work to do or until deadline passes, whichever is sooner.\n  template <typename Clock, typename Duration>\n  bool try_wait_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) noexcept {\n    return func_ || queue_.try_dequeue_until(func_, deadline);\n  }\n\n private:\n  UMPSCQueue<Func, true> queue_;\n  Func func_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/TimekeeperScheduledExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/TimekeeperScheduledExecutor.h>\n\n#include <folly/futures/Future.h>\n\nnamespace folly {\n\n/* static */ Executor::KeepAlive<TimekeeperScheduledExecutor>\nTimekeeperScheduledExecutor::create(\n    Executor::KeepAlive<> parent,\n    Function<std::shared_ptr<Timekeeper>()> getTimekeeper) {\n  return makeKeepAlive<TimekeeperScheduledExecutor>(\n      new TimekeeperScheduledExecutor(\n          std::move(parent), std::move(getTimekeeper)));\n}\n\nvoid TimekeeperScheduledExecutor::run(Func func) {\n  invokeCatchingExns(\n      \"TimekeeperScheduledExecutor: func\", std::exchange(func, {}));\n}\n\nvoid TimekeeperScheduledExecutor::add(Func func) {\n  parent_->add(\n      [keepAlive = getKeepAliveToken(this), f = std::move(func)]() mutable {\n        keepAlive->run(std::move(f));\n      });\n}\n\nvoid TimekeeperScheduledExecutor::scheduleAt(\n    Func&& func, ScheduledExecutor::TimePoint const& t) {\n  auto delay = std::chrono::duration_cast<folly::HighResDuration>(\n      t - std::chrono::steady_clock::now());\n  if (delay.count() > 0) {\n    auto tk = getTimekeeper_();\n    if (FOLLY_UNLIKELY(!tk)) {\n      throw TimekeeperScheduledExecutorNoTimekeeper();\n    }\n    tk->after(delay)\n        .via(parent_.copy())\n        .thenValue([keepAlive = getKeepAliveToken(this), f = std::move(func)](\n                       auto&&) mutable { keepAlive->run(std::move(f)); });\n  } else {\n    add(std::move(func));\n  }\n}\n\nbool TimekeeperScheduledExecutor::keepAliveAcquire() noexcept {\n  auto keepAliveCounter =\n      keepAliveCounter_.fetch_add(1, std::memory_order_relaxed);\n  DCHECK(keepAliveCounter > 0);\n  return true;\n}\n\nvoid TimekeeperScheduledExecutor::keepAliveRelease() noexcept {\n  auto keepAliveCounter =\n      keepAliveCounter_.fetch_sub(1, std::memory_order_acq_rel);\n  DCHECK(keepAliveCounter > 0);\n  if (keepAliveCounter == 1) {\n    delete this;\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/TimekeeperScheduledExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <glog/logging.h>\n\n#include <folly/executors/ScheduledExecutor.h>\n#include <folly/futures/Future.h>\n\nnamespace folly {\n\nstruct FOLLY_EXPORT TimekeeperScheduledExecutorNoTimekeeper\n    : public std::logic_error {\n  TimekeeperScheduledExecutorNoTimekeeper()\n      : std::logic_error(\"No Timekeeper available\") {}\n};\n\n// This class turns a Executor into a ScheduledExecutor.\nclass TimekeeperScheduledExecutor : public ScheduledExecutor {\n public:\n  TimekeeperScheduledExecutor(TimekeeperScheduledExecutor const&) = delete;\n  TimekeeperScheduledExecutor& operator=(TimekeeperScheduledExecutor const&) =\n      delete;\n  TimekeeperScheduledExecutor(TimekeeperScheduledExecutor&&) = delete;\n  TimekeeperScheduledExecutor& operator=(TimekeeperScheduledExecutor&&) =\n      delete;\n\n  static Executor::KeepAlive<TimekeeperScheduledExecutor> create(\n      Executor::KeepAlive<> parent,\n      Function<std::shared_ptr<Timekeeper>()> getTimekeeper =\n          detail::getTimekeeperSingleton);\n\n  virtual void add(Func func) override;\n\n  virtual void scheduleAt(\n      Func&& func, ScheduledExecutor::TimePoint const& t) override;\n\n protected:\n  bool keepAliveAcquire() noexcept override;\n  void keepAliveRelease() noexcept override;\n\n private:\n  TimekeeperScheduledExecutor(\n      KeepAlive<Executor>&& parent,\n      Function<std::shared_ptr<Timekeeper>()> getTimekeeper)\n      : parent_(std::move(parent)), getTimekeeper_(std::move(getTimekeeper)) {}\n\n  ~TimekeeperScheduledExecutor() override { DCHECK(!keepAliveCounter_); }\n\n  void run(Func);\n\n  KeepAlive<Executor> parent_;\n  Function<std::shared_ptr<Timekeeper>()> getTimekeeper_;\n  std::atomic<ssize_t> keepAliveCounter_{1};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/VirtualExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/DefaultKeepAliveExecutor.h>\n\nnamespace folly {\n\n/**\n * VirtualExecutor implements a light-weight view onto existing Executor.\n *\n * Multiple VirtualExecutors can be backed by a single Executor.\n *\n * VirtualExecutor's destructor blocks until all tasks scheduled through it are\n * complete. Executor's destructor also blocks until all VirtualExecutors\n * backed by it are released.\n */\nclass VirtualExecutor : public DefaultKeepAliveExecutor {\n  auto wrapFunc(Func f) {\n    class FuncAndKeepAlive {\n     public:\n      FuncAndKeepAlive(Func&& f, VirtualExecutor* executor)\n          : keepAlive_(getKeepAliveToken(executor)), f_(std::move(f)) {}\n\n      void operator()() { f_(); }\n\n     private:\n      Executor::KeepAlive<VirtualExecutor> keepAlive_;\n      Func f_;\n    };\n\n    return FuncAndKeepAlive(std::move(f), this);\n  }\n\n public:\n  explicit VirtualExecutor(KeepAlive<> executor)\n      : executor_(std::move(executor)) {\n    assert(!isKeepAliveDummy(executor_));\n  }\n\n  explicit VirtualExecutor(Executor* executor)\n      : VirtualExecutor(getKeepAliveToken(executor)) {}\n\n  explicit VirtualExecutor(Executor& executor)\n      : VirtualExecutor(getKeepAliveToken(executor)) {}\n\n  VirtualExecutor(const VirtualExecutor&) = delete;\n  VirtualExecutor& operator=(const VirtualExecutor&) = delete;\n\n  uint8_t getNumPriorities() const override {\n    return executor_->getNumPriorities();\n  }\n\n  void add(Func f) override { executor_->add(wrapFunc(std::move(f))); }\n\n  void addWithPriority(Func f, int8_t priority) override {\n    executor_->addWithPriority(wrapFunc(std::move(f)), priority);\n  }\n\n  ~VirtualExecutor() override { joinKeepAlive(); }\n\n private:\n  const KeepAlive<> executor_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/task_queue/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_cxx_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"priority_lifo_sem_mpmc_queue\",\n    headers = [\"PriorityLifoSemMPMCQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":blocking_queue\",\n        \"//folly:executor\",\n        \"//folly:mpmc_queue\",\n        \"//folly:range\",\n        \"//folly/synchronization:lifo_sem\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"blocking_queue\",\n    headers = [\"BlockingQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:c_portability\",\n        \"//folly:optional\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lifo_sem_mpmc_queue\",\n    headers = [\"LifoSemMPMCQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":blocking_queue\",\n        \"//folly:mpmc_queue\",\n        \"//folly/synchronization:lifo_sem\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"priority_unbounded_blocking_queue\",\n    headers = [\"PriorityUnboundedBlockingQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":blocking_queue\",\n        \"//folly:constexpr_math\",\n        \"//folly:executor\",\n        \"//folly/concurrency:priority_unbounded_queue_set\",\n        \"//folly/lang:exception\",\n        \"//folly/synchronization:lifo_sem\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"unbounded_blocking_queue\",\n    headers = [\"UnboundedBlockingQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":blocking_queue\",\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/synchronization:lifo_sem\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"striped_priority_unbounded_blocking_queue\",\n    headers = [\"StripedPriorityUnboundedBlockingQueue.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:constexpr_math\",\n        \"//folly:executor\",\n        \"//folly:glog\",\n        \"//folly/concurrency:cache_locality\",\n        \"//folly/concurrency:priority_unbounded_queue_set\",\n        \"//folly/executors/task_queue:blocking_queue\",\n        \"//folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"//folly/lang:exception\",\n        \"//folly/synchronization:striped_throttled_lifo_sem\",\n    ],\n)\n"
  },
  {
    "path": "folly/executors/task_queue/BlockingQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <exception>\n#include <stdexcept>\n\n#include <glog/logging.h>\n\n#include <folly/CPortability.h>\n#include <folly/Optional.h>\n\nnamespace folly {\n\n// Some queue implementations (for example, LifoSemMPMCQueue or\n// PriorityLifoSemMPMCQueue) support both blocking (BLOCK) and\n// non-blocking (THROW) behaviors.\nenum class QueueBehaviorIfFull { THROW, BLOCK };\n\nclass FOLLY_EXPORT QueueFullException : public std::runtime_error {\n  using std::runtime_error::runtime_error; // Inherit constructors.\n};\n\nstruct BlockingQueueAddResult {\n  BlockingQueueAddResult(bool reused = false) : reusedThread(reused) {}\n  bool reusedThread;\n};\n\ntemplate <class T>\nclass BlockingQueue {\n public:\n  virtual ~BlockingQueue() = default;\n  // Adds item to the queue (with priority).\n  //\n  // Returns true if an existing thread was able to work on it (used\n  // for dynamically sizing thread pools), false otherwise.  Return false\n  // if this feature is not supported.\n  virtual BlockingQueueAddResult add(T&& item) = 0;\n  virtual BlockingQueueAddResult addWithPriority(\n      T&& item, int8_t /* priority */) {\n    return add(std::move(item));\n  }\n  virtual uint8_t getNumPriorities() { return 1; }\n  virtual T take() = 0;\n  virtual folly::Optional<T> try_take_for(std::chrono::milliseconds time) = 0;\n  virtual size_t size() = 0;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/task_queue/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME blocking_queue\n  HEADERS\n    BlockingQueue.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_optional\n)\n\nfolly_add_library(\n  NAME lifo_sem_mpmc_queue\n  HEADERS\n    LifoSemMPMCQueue.h\n  EXPORTED_DEPS\n    folly_executors_task_queue_blocking_queue\n    folly_mpmc_queue\n    folly_synchronization_lifo_sem\n)\n\nfolly_add_library(\n  NAME priority_lifo_sem_mpmc_queue\n  HEADERS\n    PriorityLifoSemMPMCQueue.h\n  EXPORTED_DEPS\n    folly_executor\n    folly_executors_task_queue_blocking_queue\n    folly_mpmc_queue\n    folly_range\n    folly_synchronization_lifo_sem\n)\n\nfolly_add_library(\n  NAME priority_unbounded_blocking_queue\n  HEADERS\n    PriorityUnboundedBlockingQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_priority_unbounded_queue_set\n    folly_constexpr_math\n    folly_executor\n    folly_executors_task_queue_blocking_queue\n    folly_lang_exception\n    folly_synchronization_lifo_sem\n)\n\nfolly_add_library(\n  NAME striped_priority_unbounded_blocking_queue\n  HEADERS\n    StripedPriorityUnboundedBlockingQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_cache_locality\n    folly_concurrency_priority_unbounded_queue_set\n    folly_constexpr_math\n    folly_executor\n    folly_executors_task_queue_blocking_queue\n    folly_executors_task_queue_priority_unbounded_blocking_queue\n    folly_glog\n    folly_lang_exception\n    folly_synchronization_striped_throttled_lifo_sem\n)\n\nfolly_add_library(\n  NAME unbounded_blocking_queue\n  HEADERS\n    UnboundedBlockingQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_executors_task_queue_blocking_queue\n    folly_synchronization_lifo_sem\n)\n"
  },
  {
    "path": "folly/executors/task_queue/LifoSemMPMCQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/MPMCQueue.h>\n#include <folly/executors/task_queue/BlockingQueue.h>\n#include <folly/synchronization/LifoSem.h>\n\nnamespace folly {\n\ntemplate <\n    class T,\n    QueueBehaviorIfFull kBehavior = QueueBehaviorIfFull::THROW,\n    class Semaphore = folly::LifoSem>\nclass LifoSemMPMCQueue : public BlockingQueue<T> {\n public:\n  // Note: The queue pre-allocates all memory for max_capacity\n  explicit LifoSemMPMCQueue(\n      size_t max_capacity,\n      const typename Semaphore::Options& semaphoreOptions = {})\n      : sem_(semaphoreOptions), queue_(max_capacity) {}\n\n  BlockingQueueAddResult add(T&& item) override {\n    switch (kBehavior) { // static\n      case QueueBehaviorIfFull::THROW:\n        if (!queue_.writeIfNotFull(std::move(item))) {\n          throw QueueFullException(\"LifoSemMPMCQueue full, can't add item\");\n        }\n        break;\n      case QueueBehaviorIfFull::BLOCK:\n        queue_.blockingWrite(std::move(item));\n        break;\n    }\n    return sem_.post();\n  }\n\n  T take() override {\n    sem_.wait();\n    T item;\n    while (!queue_.readIfNotEmpty(item)) {\n    }\n    return item;\n  }\n\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    if (!sem_.try_wait_for(time)) {\n      return folly::none;\n    }\n    T item;\n    while (!queue_.readIfNotEmpty(item)) {\n    }\n    return item;\n  }\n\n  size_t capacity() { return queue_.capacity(); }\n\n  size_t size() override { return sem_.valueGuess(); }\n\n private:\n  Semaphore sem_;\n  folly::MPMCQueue<T> queue_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/task_queue/PriorityLifoSemMPMCQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <glog/logging.h>\n\n#include <folly/Executor.h>\n#include <folly/MPMCQueue.h>\n#include <folly/Range.h>\n#include <folly/executors/task_queue/BlockingQueue.h>\n#include <folly/synchronization/LifoSem.h>\n\nnamespace folly {\n\ntemplate <\n    class T,\n    QueueBehaviorIfFull kBehavior = QueueBehaviorIfFull::THROW,\n    class Semaphore = folly::LifoSem>\nclass PriorityLifoSemMPMCQueue : public BlockingQueue<T> {\n public:\n  // Note A: The queue pre-allocates all memory for max_capacity\n  // Note B: To use folly::Executor::*_PRI, for numPriorities == 2\n  //         MID_PRI and HI_PRI are treated at the same priority level.\n  PriorityLifoSemMPMCQueue(\n      uint8_t numPriorities,\n      size_t max_capacity,\n      const typename Semaphore::Options& semaphoreOptions = {})\n      : sem_(semaphoreOptions) {\n    CHECK_GT(numPriorities, 0) << \"Number of priorities should be positive\";\n    queues_.reserve(numPriorities);\n    for (int8_t i = 0; i < numPriorities; i++) {\n      queues_.emplace_back(max_capacity);\n    }\n  }\n\n  PriorityLifoSemMPMCQueue(\n      folly::Range<const size_t*> capacities,\n      const typename Semaphore::Options& semaphoreOptions = {})\n      : sem_(semaphoreOptions) {\n    CHECK_GT(capacities.size(), 0) << \"Number of priorities should be positive\";\n    CHECK_LT(capacities.size(), 256) << \"At most 255 priorities supported\";\n\n    queues_.reserve(capacities.size());\n    for (auto capacity : capacities) {\n      queues_.emplace_back(capacity);\n    }\n  }\n\n  uint8_t getNumPriorities() override { return queues_.size(); }\n\n  // Add at medium priority by default\n  BlockingQueueAddResult add(T&& item) override {\n    return addWithPriority(std::move(item), folly::Executor::MID_PRI);\n  }\n\n  BlockingQueueAddResult addWithPriority(T&& item, int8_t priority) override {\n    int mid = getNumPriorities() / 2;\n    size_t queue = priority < 0\n        ? std::max(0, mid + priority)\n        : std::min(getNumPriorities() - 1, mid + priority);\n    CHECK_LT(queue, queues_.size());\n    switch (kBehavior) { // static\n      case QueueBehaviorIfFull::THROW:\n        if (!queues_[queue].writeIfNotFull(std::move(item))) {\n          throw QueueFullException(\"LifoSemMPMCQueue full, can't add item\");\n        }\n        break;\n      case QueueBehaviorIfFull::BLOCK:\n        queues_[queue].blockingWrite(std::move(item));\n        break;\n    }\n    return sem_.post();\n  }\n\n  T take() override {\n    sem_.wait();\n    T item;\n    while (true) {\n      if (nonBlockingTake(item)) {\n        return item;\n      }\n    }\n  }\n\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    if (!sem_.try_wait_for(time)) {\n      return folly::none;\n    }\n    T item;\n    while (true) {\n      if (nonBlockingTake(item)) {\n        return item;\n      }\n    }\n  }\n\n  bool nonBlockingTake(T& item) {\n    for (auto it = queues_.rbegin(); it != queues_.rend(); it++) {\n      if (it->readIfNotEmpty(item)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  size_t size() override { return sem_.valueGuess(); }\n\n private:\n  Semaphore sem_;\n  std::vector<folly::MPMCQueue<T>> queues_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/task_queue/PriorityUnboundedBlockingQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ConstexprMath.h>\n#include <folly/Executor.h>\n#include <folly/concurrency/PriorityUnboundedQueueSet.h>\n#include <folly/executors/task_queue/BlockingQueue.h>\n#include <folly/lang/Exception.h>\n#include <folly/synchronization/LifoSem.h>\n\nnamespace folly {\n\ntemplate <class T, class Semaphore = folly::LifoSem>\nclass PriorityUnboundedBlockingQueue : public BlockingQueue<T> {\n public:\n  // Note: To use folly::Executor::*_PRI, for numPriorities == 2\n  //       MID_PRI and HI_PRI are treated at the same priority level.\n  explicit PriorityUnboundedBlockingQueue(\n      uint8_t numPriorities,\n      const typename Semaphore::Options& semaphoreOptions = {})\n      : sem_(semaphoreOptions), queue_(numPriorities) {}\n\n  uint8_t getNumPriorities() override { return queue_.priorities(); }\n\n  // Add at medium priority by default\n  BlockingQueueAddResult add(T&& item) override {\n    return addWithPriority(std::move(item), folly::Executor::MID_PRI);\n  }\n\n  BlockingQueueAddResult addWithPriority(T&& item, int8_t priority) override {\n    queue_.at_priority(translatePriority(priority)).enqueue(std::move(item));\n    return sem_.post();\n  }\n\n  T take() override {\n    sem_.wait();\n    return dequeue();\n  }\n\n  folly::Optional<T> try_take() {\n    if (!sem_.try_wait()) {\n      return none;\n    }\n    return dequeue();\n  }\n\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    if (!sem_.try_wait_for(time)) {\n      return none;\n    }\n    return dequeue();\n  }\n\n  size_t size() override { return sem_.valueGuess(); }\n\n private:\n  size_t translatePriority(int8_t const priority) {\n    size_t const priorities = queue_.priorities();\n    assert(priorities <= 255);\n    int8_t const hi = (priorities + 1) / 2 - 1;\n    int8_t const lo = hi - (priorities - 1);\n    return hi - constexpr_clamp(priority, lo, hi);\n  }\n\n  T dequeue() {\n    // must follow a successful sem wait\n    if (auto obj = queue_.try_dequeue()) {\n      return std::move(*obj);\n    }\n    terminate_with<std::logic_error>(\"bug in task queue\");\n  }\n\n  Semaphore sem_;\n  PriorityUMPMCQueueSet<T, /* MayBlock = */ true> queue_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/task_queue/StripedPriorityUnboundedBlockingQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ConstexprMath.h>\n#include <folly/Executor.h>\n#include <folly/GLog.h>\n#include <folly/concurrency/CacheLocality.h>\n#include <folly/concurrency/PriorityUnboundedQueueSet.h>\n#include <folly/executors/task_queue/BlockingQueue.h>\n#include <folly/executors/task_queue/PriorityUnboundedBlockingQueue.h>\n#include <folly/lang/Exception.h>\n#include <folly/synchronization/StripedThrottledLifoSem.h>\n\nnamespace folly {\n\n/**\n * A BlockingQueue sharded by LLC cache. Compared to the default\n * PriorityUnboundedBlockingQueue, this can reduce contention on systems with a\n * large number of LLC caches, at the cost of unfairness and work conservation;\n * see the StripedThrottledLifoSem documentation for a detailed explanation.\n *\n * This queue should preferably be used on thread pools with numThreads equal to\n * the number of available cores, in order to guarantee good balancing across\n * the LLCs. For the same reason, dynamic resizing should be disabled.\n *\n * NOTE: StripeGetter is customizable only for testing purposes.\n */\ntemplate <typename T, class StripeGetter = LLCAccessSpreader>\nclass StripedPriorityUnboundedBlockingQueue : public BlockingQueue<T> {\n  struct PrivateTag {};\n\n public:\n  static std::unique_ptr<BlockingQueue<T>> create(\n      uint8_t numPriorities,\n      const typename ThrottledLifoSem::Options& semaphoreOptions = {}) {\n    const auto numStripes = StripeGetter::get().numStripes();\n    if (numStripes == 1) {\n      // Don't need the overhead, just use the non-striped version.\n      return std::make_unique<\n          PriorityUnboundedBlockingQueue<T, folly::ThrottledLifoSem>>(\n          numPriorities, semaphoreOptions);\n    }\n    return std::make_unique<\n        StripedPriorityUnboundedBlockingQueue<T, StripeGetter>>(\n        PrivateTag{}, numStripes, numPriorities, semaphoreOptions);\n  }\n\n  // Private, use create().\n  StripedPriorityUnboundedBlockingQueue(\n      PrivateTag,\n      size_t numStripes,\n      uint8_t numPriorities,\n      const typename ThrottledLifoSem::Options& semaphoreOptions = {})\n      : numPriorities_(numPriorities),\n        sem_(numStripes, std::tuple{numPriorities_}, semaphoreOptions) {\n    StripedThrottledLifoSemBalancer::subscribe(sem_);\n  }\n\n  ~StripedPriorityUnboundedBlockingQueue() override {\n    StripedThrottledLifoSemBalancer::unsubscribe(sem_);\n  }\n\n  uint8_t getNumPriorities() override { return numPriorities_; }\n\n  BlockingQueueAddResult add(T&& item) override {\n    return addWithPriority(std::move(item), folly::Executor::MID_PRI);\n  }\n\n  BlockingQueueAddResult addWithPriority(T&& item, int8_t priority) override {\n    auto stripeIdx = getStripeIdx();\n    sem_.payload(stripeIdx)\n        .at_priority(translatePriority(priority))\n        .enqueue(std::move(item));\n    return sem_.post(stripeIdx);\n  }\n\n  T take() override {\n    const auto stripeIdx = getStripeIdx();\n    auto foundStripeIdx = sem_.wait(stripeIdx);\n    return dequeue(foundStripeIdx);\n  }\n\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    const auto stripeIdx = getStripeIdx();\n    if (auto foundStripeIdx = sem_.try_wait_for(stripeIdx, time)) {\n      return dequeue(*foundStripeIdx);\n    }\n    return none;\n  }\n\n  size_t size() override { return sem_.valueGuess(); }\n\n private:\n  size_t translatePriority(int8_t const priority) {\n    int8_t const hi = (numPriorities_ + 1) / 2 - 1;\n    int8_t const lo = hi - (numPriorities_ - 1);\n    return hi - constexpr_clamp(priority, lo, hi);\n  }\n\n  T dequeue(size_t stripeIdx) {\n    // must follow a successful sem wait\n    if (auto obj = sem_.payload(stripeIdx).try_dequeue()) {\n      return std::move(*obj);\n    }\n    terminate_with<std::logic_error>(\"bug in task queue\");\n  }\n\n  static size_t getStripeIdx() { return StripeGetter::get().current(); }\n\n  using Queue = PriorityUMPMCQueueSet<T, /* MayBlock = */ false>;\n\n  const size_t numPriorities_;\n  StripedThrottledLifoSem<Queue> sem_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/task_queue/UnboundedBlockingQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/executors/task_queue/BlockingQueue.h>\n#include <folly/synchronization/LifoSem.h>\n\nnamespace folly {\n\ntemplate <class T, class Semaphore = folly::LifoSem>\nclass UnboundedBlockingQueue : public BlockingQueue<T> {\n public:\n  explicit UnboundedBlockingQueue(\n      const typename Semaphore::Options& semaphoreOptions = {})\n      : sem_(semaphoreOptions) {}\n\n  BlockingQueueAddResult add(T&& item) override {\n    queue_.enqueue(std::move(item));\n    return sem_.post();\n  }\n\n  T take() override {\n    sem_.wait();\n    return queue_.dequeue();\n  }\n\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    if (!sem_.try_wait_for(time)) {\n      return folly::none;\n    }\n    return queue_.dequeue();\n  }\n\n  size_t size() override { return sem_.valueGuess(); }\n\n private:\n  Semaphore sem_;\n  UMPMCQueue<T, false, 6> queue_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/task_queue/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"UnboundedBlockingQueueBench\",\n    srcs = [\"UnboundedBlockingQueueBench.cpp\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly/executors/task_queue:unbounded_blocking_queue\",\n        \"//folly/init:init\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"PriorityUnboundedBlockingQueueTest\",\n    srcs = [\"PriorityUnboundedBlockingQueueTest.cpp\"],\n    deps = [\n        \"//folly/container:enumerate\",\n        \"//folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"UnboundedBlockingQueueTest\",\n    srcs = [\"UnboundedBlockingQueueTest.cpp\"],\n    deps = [\n        \"//folly/executors/task_queue:unbounded_blocking_queue\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"PriorityLifoSemMPMCQueueTest\",\n    srcs = [\"PriorityLifoSemMPMCQueueTest.cpp\"],\n    deps = [\n        \"//folly:range\",\n        \"//folly/container:enumerate\",\n        \"//folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"striped_priority_unbounded_blocking_queue_test\",\n    srcs = [\n        \"StripedPriorityUnboundedBlockingQueueTest.cpp\",\n    ],\n    deps = [\n        \"//folly:random\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors/task_queue:striped_priority_unbounded_blocking_queue\",\n        \"//folly/portability:gtest\",\n        \"//folly/system:hardware_concurrency\",\n    ],\n)\n"
  },
  {
    "path": "folly/executors/task_queue/test/PriorityLifoSemMPMCQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/task_queue/PriorityLifoSemMPMCQueue.h>\n\n#include <vector>\n\n#include <folly/Range.h>\n#include <folly/container/Enumerate.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(PriorityLifoSemMPMCQueue, Capacities) {\n  const std::vector<size_t> capacities = {1, 2, 3};\n  PriorityLifoSemMPMCQueue<int, QueueBehaviorIfFull::THROW> q(\n      folly::range(capacities));\n\n  for (auto capacity : folly::enumerate(capacities)) {\n    auto pri = static_cast<int8_t>(capacity.index) - 1;\n    for (size_t i = 0; i < *capacity; ++i) {\n      EXPECT_NO_THROW(q.addWithPriority(0, pri)) << *capacity << \" \" << i;\n    }\n    EXPECT_THROW(q.addWithPriority(0, pri), QueueFullException) << *capacity;\n  }\n}\n"
  },
  {
    "path": "folly/executors/task_queue/test/PriorityUnboundedBlockingQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/task_queue/PriorityUnboundedBlockingQueue.h>\n\n#include <vector>\n\n#include <folly/container/Enumerate.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nclass PriorityUnboundedBlockingQueueTest : public testing::Test {};\n\nTEST_F(PriorityUnboundedBlockingQueueTest, push_pop) {\n  PriorityUnboundedBlockingQueue<int> q(3);\n  q.add(42);\n  EXPECT_EQ(42, q.take());\n}\n\nTEST_F(PriorityUnboundedBlockingQueueTest, multiple_push_pop) {\n  PriorityUnboundedBlockingQueue<int> q(3);\n  q.add(42);\n  q.add(77);\n  EXPECT_EQ(42, q.take());\n  EXPECT_EQ(77, q.take());\n}\n\nTEST_F(PriorityUnboundedBlockingQueueTest, size) {\n  PriorityUnboundedBlockingQueue<int> q(3);\n  EXPECT_EQ(0, q.size());\n  q.add(42);\n  EXPECT_EQ(1, q.size());\n  q.take();\n  EXPECT_EQ(0, q.size());\n}\n\nTEST_F(PriorityUnboundedBlockingQueueTest, concurrent_push_pop) {\n  PriorityUnboundedBlockingQueue<int> q(3);\n  Baton<> b1, b2;\n  std::thread t([&] {\n    b1.post();\n    EXPECT_EQ(42, q.take());\n    EXPECT_EQ(0, q.size());\n    b2.post();\n  });\n  b1.wait();\n  q.add(42);\n  b2.wait();\n  EXPECT_EQ(0, q.size());\n  t.join();\n}\n\nTEST_F(PriorityUnboundedBlockingQueueTest, priority_order) {\n  PriorityUnboundedBlockingQueue<int> q(3);\n  EXPECT_EQ(0, q.size());\n  q.addWithPriority(27, 0);\n  q.addWithPriority(42, 1);\n  q.addWithPriority(55, 0);\n  q.addWithPriority(12, -1);\n  EXPECT_EQ(4, q.size());\n  EXPECT_EQ(42, q.take());\n  EXPECT_EQ(27, q.take());\n  EXPECT_EQ(55, q.take());\n  EXPECT_EQ(12, q.take());\n  EXPECT_EQ(0, q.size());\n}\n\n// Since PriorityUnboundedBlockingQueue implements folly::BlockingQueue<T>,\n// addWithPriority method has to accept priority as int_8. This means invalid\n// values for priority (such as negative or very large numbers) might get\n// passed. Verify this behavior.\nTEST_F(PriorityUnboundedBlockingQueueTest, invalid_priorities) {\n  PriorityUnboundedBlockingQueue<int> q(2);\n  q.addWithPriority(1, -1); // expected to be converted to the lowest priority\n  q.addWithPriority(2, 50); // expected to be converted to the highest priority\n\n  EXPECT_EQ(q.take(), 2);\n  EXPECT_EQ(q.take(), 1);\n}\n\nTEST_F(PriorityUnboundedBlockingQueueTest, invalid_priorities_edge) {\n  PriorityUnboundedBlockingQueue<int> q(2);\n  q.addWithPriority(1, Executor::LO_PRI);\n  q.addWithPriority(2, Executor::HI_PRI);\n\n  EXPECT_EQ(q.take(), 2);\n  EXPECT_EQ(q.take(), 1);\n}\n"
  },
  {
    "path": "folly/executors/task_queue/test/StripedPriorityUnboundedBlockingQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/task_queue/StripedPriorityUnboundedBlockingQueue.h>\n#include <folly/system/HardwareConcurrency.h>\n\n#include <atomic>\n\n#include <folly/Random.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\n\n// Ensure that we test on a large number of stripes independent of the hardware\n// the test runs on.\nstruct RandomStripe {\n  static const RandomStripe& get() {\n    static RandomStripe instance;\n    return instance;\n  }\n\n  size_t current() const { return folly::Random::rand64(numStripes()); }\n  size_t numStripes() const { return 16; }\n};\n\n} // namespace\n\nTEST(StripedPriorityUnboundedBlockingQueue, SmokeTest) {\n  const size_t kNumThreads = folly::available_concurrency();\n  const size_t kNumTasksPerThread = 1024;\n\n  folly::CPUThreadPoolExecutor consumers(\n      std::make_pair(kNumThreads, kNumThreads),\n      folly::StripedPriorityUnboundedBlockingQueue<\n          folly::CPUThreadPoolExecutor::CPUTask,\n          RandomStripe>::create(2));\n\n  folly::CPUThreadPoolExecutor producers(\n      std::make_pair(kNumThreads, kNumThreads));\n\n  std::atomic<size_t> tasksDone{0};\n  for (size_t t = 0; t < kNumThreads; ++t) {\n    producers.add([&] {\n      for (size_t i = 0; i < kNumTasksPerThread; ++i) {\n        consumers.addWithPriority(\n            [&] { ++tasksDone; }, folly::Random::rand32(2));\n      }\n    });\n  }\n\n  producers.join();\n  consumers.join();\n\n  EXPECT_EQ(tasksDone.load(), kNumTasksPerThread * kNumThreads);\n}\n"
  },
  {
    "path": "folly/executors/task_queue/test/UnboundedBlockingQueueBench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/task_queue/UnboundedBlockingQueue.h>\n\n#include <cstddef>\n#include <numeric>\n#include <thread>\n#include <vector>\n\n#include <glog/logging.h>\n#include <folly/synchronization/test/Barrier.h>\n\n#include <folly/Benchmark.h>\n#include <folly/init/Init.h>\n\nDEFINE_int32(\n    drain_full_queue_qlen,\n    4096,\n    \"drain_full_queue initial queue length multiplier\");\nDEFINE_int32(\n    drain_full_queue_nthreads,\n    4,\n    \"drain_full_queue number of draining threads\");\n\nBENCHMARK(drain_full_queue, iters) {\n  // goal: measure contention in take()\n  folly::BenchmarkSuspender braces;\n\n  auto const qlen = size_t(FLAGS_drain_full_queue_qlen * iters);\n  auto const nthreads = size_t(FLAGS_drain_full_queue_nthreads);\n\n  CHECK_GE(qlen, nthreads);\n\n  folly::UnboundedBlockingQueue<int> q;\n  for (auto i = qlen; i != 0; --i) {\n    q.add(i);\n  }\n  for (auto i = nthreads; i != 0; --i) {\n    q.add(0); // poison\n  }\n\n  std::vector<size_t> counts(nthreads);\n  std::vector<std::thread> threads(nthreads);\n\n  folly::test::Barrier barrier(1 + nthreads);\n\n  for (auto i = 0u; i < nthreads; ++i) {\n    threads[i] = std::thread([&, i] {\n      barrier.wait(); // A - wait for thread start\n      barrier.wait(); // B - init the work\n      while (auto j = q.take()) { // take until poison\n        counts[i] += j;\n      }\n      barrier.wait(); // C - join the work\n    });\n  }\n\n  barrier.wait(); // A - wait for thread start\n  braces.dismissing([&] {\n    barrier.wait(); // B - init the work\n    barrier.wait(); // C - join the work\n  });\n\n  for (auto i = 0u; i < nthreads; ++i) {\n    threads[i].join();\n  }\n\n  size_t sum = std::accumulate(counts.begin(), counts.end(), size_t(0));\n  CHECK_EQ((qlen * (qlen + 1) / 2), sum);\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/executors/task_queue/test/UnboundedBlockingQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/task_queue/UnboundedBlockingQueue.h>\n\n#include <thread>\n\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nTEST(UnboundedQueuee, pushPop) {\n  UnboundedBlockingQueue<int> q;\n  q.add(42);\n  EXPECT_EQ(42, q.take());\n}\nTEST(UnboundedBlockingQueue, size) {\n  UnboundedBlockingQueue<int> q;\n  EXPECT_EQ(0, q.size());\n  q.add(42);\n  EXPECT_EQ(1, q.size());\n  q.take();\n  EXPECT_EQ(0, q.size());\n}\n\nTEST(UnboundedBlockingQueue, concurrentPushPop) {\n  UnboundedBlockingQueue<int> q;\n  Baton<> b1, b2;\n  std::thread t([&] {\n    b1.post();\n    EXPECT_EQ(42, q.take());\n    EXPECT_EQ(0, q.size());\n    b2.post();\n  });\n  b1.wait();\n  q.add(42);\n  b2.wait();\n  EXPECT_EQ(0, q.size());\n  t.join();\n}\n"
  },
  {
    "path": "folly/executors/test/AsyncTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/Async.h>\n\n#include <memory>\n\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(AsyncFunc, manualExecutor) {\n  FOLLY_PUSH_WARNING\n  FOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\")\n  auto x = std::make_shared<ManualExecutor>();\n  auto oldX = getCPUExecutor();\n  setCPUExecutor(x);\n  auto f = async([] { return 42; });\n  EXPECT_FALSE(f.isReady());\n  x->run();\n  EXPECT_EQ(42, f.value());\n  setCPUExecutor(oldX);\n  FOLLY_POP_WARNING\n}\n\nTEST(AsyncFunc, valueLambda) {\n  auto lambda = [] { return 42; };\n  auto future = async(lambda);\n  EXPECT_EQ(42, std::move(future).get());\n}\n\nTEST(AsyncFunc, voidLambda) {\n  auto lambda = [] { /*do something*/ return; };\n  auto future = async(lambda);\n  // Futures with a void returning function, return Unit type\n  EXPECT_TRUE((std::is_same<Unit, decltype(std::move(future).get())>::value));\n}\n\nTEST(AsyncFunc, moveonlyLambda) {\n  auto lambda = [] { return std::make_unique<int>(42); };\n  auto future = async(lambda);\n  EXPECT_EQ(42, *std::move(future).get());\n}\n"
  },
  {
    "path": "folly/executors/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"AsyncTest\",\n    srcs = [\"AsyncTest.cpp\"],\n    deps = [\n        \"//folly/executors:async\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"CodelTest\",\n    srcs = [\"CodelTest.cpp\"],\n    deps = [\n        \"//folly/executors:codel\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"EDFThreadPoolExecutorBenchmark\",\n    srcs = [\"EDFThreadPoolExecutorBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:mpmc_queue\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:edf_thread_pool_executor\",\n        \"//folly/executors:thread_pool_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"FiberIOExecutorTest\",\n    srcs = [\"FiberIOExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:fiber_io_executor\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"executor_test\",\n    srcs = [\"ExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:inline_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:queued_immediate_executor\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:request_context\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"GlobalExecutorTest\",\n    srcs = [\"GlobalExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:io_executor\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/executors:virtual_executor\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:saturating_semaphore\",\n        \"//folly/system:hardware_concurrency\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"GlobalCPUExecutorTest\",\n    srcs = [\"GlobalCPUExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"GlobalIOExecutorTest\",\n    srcs = [\"GlobalCPUExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"GlobalExecutorAssignmentTest\",\n    srcs = [\"GlobalExecutorAssignmentTest.cpp\"],\n    deps = [\n        \"//folly:singleton\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"SerialExecutorTest\",\n    srcs = [\"SerialExecutorTest.cpp\"],\n    deps = [\n        \"//folly:random\",\n        \"//folly:scope_guard\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/io/async:request_context\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"SequencedExecutorTest\",\n    srcs = [\"SequencedExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"StrandExecutorTest\",\n    srcs = [\"StrandExecutorTest.cpp\"],\n    deps = [\n        \"//folly:cancellation_token\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:strand_executor\",\n        \"//folly/io/async:request_context\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ThreadedExecutorTest\",\n    srcs = [\"ThreadedExecutorTest.cpp\"],\n    deps = [\n        \"//folly:conv\",\n        \"//folly/executors:threaded_executor\",\n        \"//folly/futures:core\",\n        \"//folly/gen:base\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ThreadPoolExecutorTest\",\n    srcs = [\"ThreadPoolExecutorTest.cpp\"],\n    deps = [\n        \"//folly:c_portability\",\n        \"//folly:default_keep_alive_executor\",\n        \"//folly:exception\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:edf_thread_pool_executor\",\n        \"//folly/executors:future_executor\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/executors:thread_pool_executor\",\n        \"//folly/executors:virtual_executor\",\n        \"//folly/executors/task_queue:lifo_sem_mpmc_queue\",\n        \"//folly/executors/task_queue:unbounded_blocking_queue\",\n        \"//folly/executors/thread_factory:init_thread_factory\",\n        \"//folly/executors/thread_factory:priority_thread_factory\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:pthread\",\n        \"//folly/portability:sys_resource\",\n        \"//folly/synchronization:latch\",\n        \"//folly/synchronization/detail:spin\",\n        \"//folly/system:thread_id\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_thread\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"TimedDrivableExecutorTest\",\n    srcs = [\"TimedDrivableExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:timed_drivable_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"TimekeeperScheduledExecutorTest\",\n    srcs = [\"TimekeeperScheduledExecutorTest.cpp\"],\n    deps = [\n        \"//folly/executors:inline_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/executors:threaded_executor\",\n        \"//folly/executors:timekeeper_scheduled_executor\",\n        \"//folly/futures:manual_timekeeper\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ExecutorWithPriorityTest\",\n    srcs = [\"ExecutorWithPriorityTest.cpp\"],\n    deps = [\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:executor_with_priority\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"MeteredExecutorTest\",\n    srcs = [\"MeteredExecutorTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:synchronized\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:task\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:metered_executor\",\n        \"//folly/init:init\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:latch\",\n        \"//folly/synchronization:lifo_sem\",\n        \"//folly/system:hardware_concurrency\",\n        \"//folly/test:deterministic_schedule\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"IOThreadPoolDeadlockDetectorObserverTest\",\n    srcs = [\"IOThreadPoolDeadlockDetectorObserverTest.cpp\"],\n    labels = [\"oss-broken\"],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/concurrency:deadlock_detector\",\n        \"//folly/executors:io_thread_pool_deadlock_detector_observer\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"IOThreadPoolExecutorBaseTestLib\",\n    headers = [\n        \"IOThreadPoolExecutorBaseTestLib.h\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:random\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"IOThreadPoolExecutorTest\",\n    srcs = [\"IOThreadPoolExecutorTest.cpp\"],\n    labels = [\"oss-broken\"],\n    supports_static_listing = False,\n    deps = [\n        \":IOThreadPoolExecutorBaseTestLib\",\n        \"//folly/executors:io_thread_pool_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"function_scheduler_test\",\n    srcs = [\"FunctionSchedulerTest.cpp\"],\n    headers = [],\n    labels = [\"slow\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:random\",\n        \"//folly/executors:function_scheduler\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_thread\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"threaded_repeating_function_runner_test\",\n    srcs = [\"ThreadedRepeatingFunctionRunnerTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/executors:threaded_repeating_function_runner\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"striped_edf_thread_pool_executor_test\",\n    srcs = [\n        \"StripedEDFThreadPoolExecutorTest.cpp\",\n    ],\n    deps = [\n        \"//folly:random\",\n        \"//folly/executors:striped_edf_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sched\",\n        \"//folly/portability:unistd\",\n    ],\n)\n"
  },
  {
    "path": "folly/executors/test/CodelTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/Codel.h>\n\n#include <chrono>\n#include <thread>\n\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\nDECLARE_int32(codel_target_delay);\n\nusing std::chrono::milliseconds;\nusing std::this_thread::sleep_for;\n\nTEST(CodelTest, Basic) {\n  folly::Codel c;\n  auto now = c.getIntervalTime();\n\n  now += milliseconds(110);\n  // This interval is overloaded\n  EXPECT_FALSE(c.overloaded_explicit_now(milliseconds(100), now));\n\n  now += milliseconds(90);\n  // At least two requests must happen in an interval before they will fail\n  EXPECT_FALSE(c.overloaded_explicit_now(milliseconds(50), now));\n  EXPECT_TRUE(c.overloaded_explicit_now(milliseconds(50), now));\n\n  now += milliseconds(110);\n  // Previous interval is overloaded, but 2ms isn't enough to fail\n  EXPECT_FALSE(c.overloaded_explicit_now(milliseconds(2), now));\n\n  now += milliseconds(90);\n  // 20 ms > target interval * 2\n  EXPECT_TRUE(c.overloaded_explicit_now(milliseconds(20), now));\n}\n\nTEST(CodelTest, highLoad) {\n  folly::Codel c;\n  c.overloaded(milliseconds(40));\n  EXPECT_EQ(100, c.getLoad());\n}\n\nTEST(CodelTest, mediumLoad) {\n  folly::Codel c;\n  c.overloaded(milliseconds(20));\n  sleep_for(milliseconds(90));\n  // this is overloaded but this request shouldn't drop because it's not >\n  // slough timeout\n  EXPECT_FALSE(c.overloaded(milliseconds(8)));\n  EXPECT_GT(100, c.getLoad());\n}\n\nTEST(CodelTest, reducingLoad) {\n  folly::Codel c;\n  c.overloaded(milliseconds(20));\n  sleep_for(milliseconds(90));\n  EXPECT_FALSE(c.overloaded(milliseconds(4)));\n}\n\nTEST(CodelTest, oneRequestNoDrop) {\n  folly::Codel c;\n  EXPECT_FALSE(c.overloaded(milliseconds(20)));\n}\n\nTEST(CodelTest, getLoadSanity) {\n  folly::Codel c;\n  // should be 100% but leave a litte wiggle room.\n  c.overloaded(milliseconds(10));\n  EXPECT_LT(99, c.getLoad());\n  EXPECT_GT(101, c.getLoad());\n\n  // should be 70% but leave a litte wiggle room.\n  c.overloaded(milliseconds(7));\n  EXPECT_LT(60, c.getLoad());\n  EXPECT_GT(80, c.getLoad());\n\n  // should be 20% but leave a litte wiggle room.\n  c.overloaded(milliseconds(2));\n  EXPECT_LT(10, c.getLoad());\n  EXPECT_GT(30, c.getLoad());\n\n  // this test demonstrates how silly getLoad() is, but silly isn't\n  // necessarily useless\n}\n\nTEST(CodelTest, updateTargetDelay) {\n  folly::Codel c;\n  folly::Codel::Options opts;\n  c.overloaded(milliseconds(40));\n  EXPECT_EQ(100, c.getLoad());\n  EXPECT_EQ(milliseconds(5), c.getOptions().targetDelay());\n\n  // Increase the target delay and test again.\n  opts.setTargetDelay(std::chrono::milliseconds(40));\n  opts.setInterval(std::chrono::milliseconds(100));\n  c.setOptions(opts);\n  EXPECT_EQ(milliseconds(40), c.getOptions().targetDelay());\n  EXPECT_FALSE(c.overloaded(milliseconds(40)));\n\n  // Decrease the target delay and test again.\n  opts.setTargetDelay(std::chrono::milliseconds(5));\n  c.setOptions(opts);\n  EXPECT_EQ(milliseconds(5), c.getOptions().targetDelay());\n  sleep_for(milliseconds(110));\n  EXPECT_FALSE(c.overloaded(milliseconds(40)));\n  EXPECT_TRUE(c.overloaded(milliseconds(40)));\n}\n\nTEST(CodelTest, updateInterval) {\n  folly::Codel c;\n  folly::Codel::Options opts;\n  c.overloaded(milliseconds(50));\n  EXPECT_EQ(100, c.getLoad());\n\n  // Make sure the default interval is correct.\n  EXPECT_EQ(milliseconds(100), c.getOptions().interval());\n  sleep_for(milliseconds(110));\n\n  // Two delayed requests lead to overload.\n  EXPECT_FALSE(c.overloaded(milliseconds(50)));\n  EXPECT_TRUE(c.overloaded(milliseconds(50)));\n\n  // Increase the interval to 200 ms and test again.\n  opts.setInterval(std::chrono::milliseconds(200));\n  opts.setTargetDelay(std::chrono::milliseconds(FLAGS_codel_target_delay));\n\n  c.setOptions(opts);\n  EXPECT_EQ(milliseconds(200), c.getOptions().interval());\n  sleep_for(milliseconds(100));\n  EXPECT_FALSE(c.overloaded(milliseconds(20)));\n  EXPECT_TRUE(c.overloaded(milliseconds(20)));\n}\n\nTEST(CodelTest, invalidParamUpdates) {\n  folly::Codel c;\n  folly::Codel::Options opts = c.getOptions();\n  EXPECT_EQ(milliseconds(5), c.getOptions().targetDelay());\n  EXPECT_EQ(milliseconds(100), c.getOptions().interval());\n\n  // Set target delay to an invalid value.\n  // Can't be greater than the existing interval period.\n  opts.setTargetDelay(std::chrono::milliseconds(110));\n  try {\n    c.setOptions(opts);\n    FAIL() << \"Expected a std::runtime_error\";\n  } catch (std::invalid_argument const& err) {\n    std::string error = err.what();\n    EXPECT_EQ(\"Invalid arguments provided\", error);\n  }\n  EXPECT_EQ(milliseconds(5), c.getOptions().targetDelay());\n\n  // Set the target delay to a valid value.\n  opts.setTargetDelay(std::chrono::milliseconds(20));\n  opts.setInterval(std::chrono::milliseconds(100));\n  c.setOptions(opts);\n  EXPECT_EQ(milliseconds(20), c.getOptions().targetDelay());\n\n  // Set the interval to a value smaller than the target delay.\n  opts.setInterval(std::chrono::milliseconds(5));\n  try {\n    c.setOptions(opts);\n    FAIL() << \"Expected a std::runtime_error\";\n  } catch (std::invalid_argument const& err) {\n    std::string error = err.what();\n    EXPECT_EQ(\"Invalid arguments provided\", error);\n  }\n  EXPECT_EQ(milliseconds(100), c.getOptions().interval());\n\n  // Set the params to a valid combination.\n  opts.setInterval(std::chrono::milliseconds(200));\n  opts.setTargetDelay(std::chrono::milliseconds(10));\n\n  c.setOptions(opts);\n  EXPECT_EQ(milliseconds(10), c.getOptions().targetDelay());\n  EXPECT_EQ(milliseconds(200), c.getOptions().interval());\n}\n"
  },
  {
    "path": "folly/executors/test/EDFThreadPoolExecutorBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <atomic>\n#include <chrono>\n#include <mutex>\n#include <thread>\n\n#include <folly/Benchmark.h>\n#include <folly/MPMCQueue.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/EDFThreadPoolExecutor.h>\n#include <folly/executors/ThreadPoolExecutor.h>\n\nusing namespace folly;\n\n// Use 19 threads because it's common to use 0.8 * numCores, and 24 is a common\n// number of cores.\nstatic constexpr size_t kNumThreads = 19;\n\nvoid throughput(uint32_t n, std::unique_ptr<ThreadPoolExecutor> ex) {\n  while (n--) {\n    ex->add([]() {});\n  }\n  ex->join();\n}\n\nBENCHMARK_NAMED_PARAM(\n    throughput, CPUEx, std::make_unique<CPUThreadPoolExecutor>(kNumThreads))\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    throughput, EDFEx, std::make_unique<EDFThreadPoolExecutor>(kNumThreads))\n\nvoid saturated(\n    uint32_t n, std::unique_ptr<ThreadPoolExecutor> ex, size_t numTasks) {\n  std::atomic<size_t> numAlive{0};\n  std::atomic<bool> finish{false};\n\n  size_t expected = 0;\n  while (n--) {\n    while (numAlive.load(std::memory_order_relaxed) == numTasks) {\n      // spin\n    }\n\n    while (!numAlive.compare_exchange_weak(\n        expected, expected + 1, std::memory_order_relaxed)) {\n      // try again\n    }\n\n    ex->add([&numAlive, &finish, numTasks]() {\n      size_t expectedNumTasks = numTasks;\n      const size_t wanted = numTasks - 1;\n      while (!numAlive.compare_exchange_weak(\n          expectedNumTasks, wanted, std::memory_order_relaxed)) {\n        if (finish.load(std::memory_order_acquire)) {\n          break;\n        }\n        expectedNumTasks = numTasks;\n      }\n    });\n  }\n\n  finish.store(true, std::memory_order_release);\n\n  ex->join();\n}\n\nBENCHMARK_NAMED_PARAM(\n    saturated, CPUEx_1, std::make_unique<CPUThreadPoolExecutor>(kNumThreads), 1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    saturated, EDFEx_1, std::make_unique<EDFThreadPoolExecutor>(kNumThreads), 1)\n\nBENCHMARK_NAMED_PARAM(\n    saturated,\n    CPUEx_10,\n    std::make_unique<CPUThreadPoolExecutor>(kNumThreads),\n    10)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    saturated,\n    EDFEx_10,\n    std::make_unique<EDFThreadPoolExecutor>(kNumThreads),\n    10)\n\nBENCHMARK_NAMED_PARAM(\n    saturated,\n    CPUEx_100,\n    std::make_unique<CPUThreadPoolExecutor>(kNumThreads),\n    100)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    saturated,\n    EDFEx_100,\n    std::make_unique<EDFThreadPoolExecutor>(kNumThreads),\n    100)\n\nvoid multiThreaded(uint32_t n, std::unique_ptr<ThreadPoolExecutor> ex) {\n  static constexpr size_t kMallocSize = 128;\n  static constexpr size_t kNumTasks = 8;\n\n  struct WorkItem {\n    std::array<std::atomic<char*>, kNumTasks> ptrs{};\n    std::atomic<int> tasksDone{0};\n  };\n\n  std::array<WorkItem, 1024> workItems{};\n  folly::MPMCQueue<WorkItem*> mallocQueue{workItems.size()};\n  folly::MPMCQueue<WorkItem*> memsetQueue{workItems.size()};\n  folly::MPMCQueue<WorkItem*> freeQueue{workItems.size()};\n\n  for (size_t i = 0; i < std::min(static_cast<size_t>(n), workItems.size());\n       i++) {\n    mallocQueue.write(&workItems[i]);\n  }\n\n  auto maybeFinishState =\n      [](WorkItem* workItem, folly::MPMCQueue<WorkItem*>& nextQueue) {\n        int tasksDone = workItem->tasksDone.fetch_add(1);\n        if (tasksDone + 1 == kNumTasks) {\n          workItem->tasksDone.store(0);\n          nextQueue.write(workItem);\n        }\n      };\n\n  auto mallocThread = std::thread([&]() {\n    for (uint32_t i = 0; i < n; i++) {\n      WorkItem* workItem;\n      mallocQueue.blockingRead(workItem);\n      for (size_t taskIndex = 0; taskIndex < kNumTasks; taskIndex++) {\n        ex->add([workItem, taskIndex, &maybeFinishState, &memsetQueue]() {\n          workItem->ptrs[taskIndex].store(\n              static_cast<char*>(malloc(kMallocSize)));\n          maybeFinishState(workItem, /*nextQueue=*/memsetQueue);\n        });\n      }\n    }\n  });\n\n  auto memsetThread = std::thread([&]() {\n    for (uint32_t i = 0; i < n; i++) {\n      WorkItem* workItem;\n      memsetQueue.blockingRead(workItem);\n      for (size_t taskIndex = 0; taskIndex < kNumTasks; taskIndex++) {\n        int ch = static_cast<int>(i);\n        ex->add([workItem, taskIndex, ch, &maybeFinishState, &freeQueue]() {\n          memset(workItem->ptrs[taskIndex].load(), ch, kMallocSize);\n          maybeFinishState(workItem, /*nextQueue=*/freeQueue);\n        });\n      }\n    }\n  });\n\n  auto freeThread = std::thread([&]() {\n    for (uint32_t i = 0; i < n; i++) {\n      WorkItem* workItem;\n      freeQueue.blockingRead(workItem);\n      for (size_t taskIndex = 0; taskIndex < kNumTasks; taskIndex++) {\n        ex->add([workItem, taskIndex, &maybeFinishState, &mallocQueue]() {\n          free(workItem->ptrs[taskIndex].load());\n          workItem->ptrs[taskIndex].store(nullptr);\n          maybeFinishState(workItem, /*nextQueue=*/mallocQueue);\n        });\n      }\n    }\n  });\n\n  mallocThread.join();\n  memsetThread.join();\n  freeThread.join();\n  ex->join();\n\n  for (auto& workItem : workItems) {\n    for (size_t i = 0; i < kNumTasks; i++) {\n      free(workItem.ptrs[i]);\n    }\n  }\n}\n\nBENCHMARK_NAMED_PARAM(\n    multiThreaded, CPUEx, std::make_unique<CPUThreadPoolExecutor>(kNumThreads))\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    multiThreaded, EDFEx, std::make_unique<EDFThreadPoolExecutor>(kNumThreads))\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/executors/test/ExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/InlineExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/QueuedImmediateExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/Request.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nTEST(ManualExecutor, runIsStable) {\n  ManualExecutor x;\n  size_t count = 0;\n  auto f1 = [&]() { count++; };\n  auto f2 = [&]() {\n    x.add(f1);\n    x.add(f1);\n  };\n  x.add(f2);\n  x.run();\n  EXPECT_EQ(count, 0);\n\n  // ManualExecutor's destructor drains, so explicitly clear the two added by\n  // f2.\n  EXPECT_EQ(2, x.clear());\n}\n\nTEST(ManualExecutor, drainIsNotStable) {\n  ManualExecutor x;\n  size_t count = 0;\n  auto f1 = [&]() { count++; };\n  auto f2 = [&]() {\n    x.add(f1);\n    x.add(f1);\n  };\n  x.add(f2);\n  x.drain();\n  EXPECT_EQ(count, 2);\n}\n\nTEST(ManualExecutor, scheduleDur) {\n  ManualExecutor x;\n  size_t count = 0;\n  std::chrono::milliseconds dur{10};\n  x.schedule([&] { count++; }, dur);\n  EXPECT_EQ(count, 0);\n  x.run();\n  EXPECT_EQ(count, 0);\n  x.advance(dur / 2);\n  EXPECT_EQ(count, 0);\n  x.advance(dur / 2);\n  EXPECT_EQ(count, 1);\n}\n\nTEST(ManualExecutor, laterThingsDontBlockEarlierOnes) {\n  ManualExecutor x;\n  auto first = false;\n  std::chrono::milliseconds dur{10};\n  x.schedule([&] { first = true; }, dur);\n  x.schedule([] {}, 2 * dur);\n  EXPECT_FALSE(first);\n  x.advance(dur);\n  EXPECT_TRUE(first);\n}\n\nTEST(ManualExecutor, orderWillNotBeQuestioned) {\n  ManualExecutor x;\n  auto first = false;\n  auto second = false;\n  std::chrono::milliseconds dur{10};\n  x.schedule([&] { first = true; }, dur);\n  x.schedule([&] { second = true; }, 2 * dur);\n  EXPECT_FALSE(first);\n  EXPECT_FALSE(second);\n  x.advance(dur);\n  EXPECT_TRUE(first);\n  EXPECT_FALSE(second);\n  x.advance(dur);\n  EXPECT_TRUE(first);\n  EXPECT_TRUE(second);\n}\n\nTEST(ManualExecutor, evenWhenYouSkipAheadEventsRunInProperOrder) {\n  ManualExecutor x;\n  auto counter = 0;\n  auto first = 0;\n  auto second = 0;\n  std::chrono::milliseconds dur{10};\n  x.schedule([&] { first = ++counter; }, dur);\n  x.schedule([&] { second = ++counter; }, 2 * dur);\n  EXPECT_EQ(first, 0);\n  EXPECT_EQ(second, 0);\n  x.advance(3 * dur);\n  EXPECT_EQ(first, 1);\n  EXPECT_EQ(second, 2);\n}\n\nTEST(ManualExecutor, clockStartsAt0) {\n  ManualExecutor x;\n  EXPECT_EQ(x.now(), x.now().min());\n}\n\nTEST(ManualExecutor, scheduleAbs) {\n  ManualExecutor x;\n  size_t count = 0;\n  x.scheduleAt([&] { count++; }, x.now() + std::chrono::milliseconds(10));\n  EXPECT_EQ(count, 0);\n  x.advance(std::chrono::milliseconds(10));\n  EXPECT_EQ(count, 1);\n}\n\nTEST(ManualExecutor, advanceTo) {\n  ManualExecutor x;\n  size_t count = 0;\n  x.scheduleAt([&] { count++; }, std::chrono::steady_clock::now());\n  EXPECT_EQ(count, 0);\n  x.advanceTo(std::chrono::steady_clock::now());\n  EXPECT_EQ(count, 1);\n}\n\nTEST(ManualExecutor, advanceBack) {\n  ManualExecutor x;\n  size_t count = 0;\n  x.advance(std::chrono::microseconds(5));\n  x.schedule([&] { count++; }, std::chrono::microseconds(6));\n  EXPECT_EQ(count, 0);\n  x.advanceTo(x.now() - std::chrono::microseconds(1));\n  EXPECT_EQ(count, 0);\n}\n\nTEST(ManualExecutor, advanceNeg) {\n  ManualExecutor x;\n  size_t count = 0;\n  x.advance(std::chrono::microseconds(5));\n  x.schedule([&] { count++; }, std::chrono::microseconds(6));\n  EXPECT_EQ(count, 0);\n  x.advance(std::chrono::microseconds(-1));\n  EXPECT_EQ(count, 0);\n}\n\nTEST(ManualExecutor, waitForDoesNotDeadlock) {\n  ManualExecutor east, west;\n  folly::Baton<> baton;\n  auto f =\n      makeFuture()\n          .via(&east)\n          .then([](Try<Unit>) { return makeFuture(); })\n          .via(&west);\n  std::thread t([&] {\n    baton.post();\n    west.waitFor(f);\n  });\n  baton.wait();\n  east.run();\n  t.join();\n}\n\nTEST(ManualExecutor, getViaDoesNotDeadlock) {\n  ManualExecutor east, west;\n  folly::Baton<> baton;\n  auto f =\n      makeFuture()\n          .via(&east)\n          .then([](Try<Unit>) { return makeFuture(); })\n          .via(&west);\n  std::thread t([&] {\n    baton.post();\n    std::move(f).getVia(&west);\n  });\n  baton.wait();\n  east.run();\n  t.join();\n}\n\nTEST(ManualExecutor, clear) {\n  ManualExecutor x;\n  size_t count = 0;\n  x.add([&] { ++count; });\n  x.scheduleAt([&] { ++count; }, x.now() + std::chrono::milliseconds(10));\n  EXPECT_EQ(0, count);\n\n  x.clear();\n  x.advance(std::chrono::milliseconds(10));\n  x.run();\n  EXPECT_EQ(0, count);\n}\n\nTEST(ManualExecutor, drainsOnDestruction) {\n  size_t count = 0;\n  {\n    ManualExecutor x;\n    x.add([&] { ++count; });\n  }\n  EXPECT_EQ(1, count);\n}\n\nTEST(ManualExecutor, keepAlive) {\n  auto future = [] {\n    ManualExecutor ex;\n    return futures::sleep(std::chrono::milliseconds{100})\n        .via(&ex)\n        .thenValue([](auto) { return 42; })\n        .semi();\n  }();\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(42, std::move(future).get());\n}\n\nTEST(Executor, InlineExecutor) {\n  InlineExecutor x;\n  size_t counter = 0;\n  x.add([&] {\n    x.add([&] {\n      EXPECT_EQ(counter, 0);\n      counter++;\n    });\n    EXPECT_EQ(counter, 1);\n    counter++;\n  });\n  EXPECT_EQ(counter, 2);\n}\n\nTEST(Executor, QueuedImmediateExecutor) {\n  QueuedImmediateExecutor x;\n  size_t counter = 0;\n  x.add([&] {\n    x.add([&] {\n      EXPECT_EQ(1, counter);\n      counter++;\n    });\n    EXPECT_EQ(0, counter);\n    counter++;\n  });\n  EXPECT_EQ(2, counter);\n}\n\nTEST(Executor, QueuedImmediateExecutorRequestContext) {\n  QueuedImmediateExecutor x;\n  RequestContextScopeGuard guard1;\n  auto* ctx1 = RequestContext::try_get();\n  x.add([&] {\n    EXPECT_EQ(ctx1, RequestContext::try_get());\n    RequestContextScopeGuard guard2;\n    x.add([&, ctx2 = RequestContext::try_get()] {\n      EXPECT_EQ(ctx2, RequestContext::try_get());\n    });\n  });\n  EXPECT_EQ(ctx1, RequestContext::try_get());\n}\n\nTEST(Executor, Runnable) {\n  InlineExecutor x;\n  size_t counter = 0;\n  struct Runnable {\n    std::function<void()> fn;\n    void operator()() { fn(); }\n  };\n  Runnable f;\n  f.fn = [&] { counter++; };\n  x.add(f);\n  EXPECT_EQ(counter, 1);\n}\n\nTEST(Executor, ThrowableThen) {\n  InlineExecutor x;\n  auto f = Future<Unit>().thenValue([](auto&&) {\n    throw std::runtime_error(\"Faildog\");\n  });\n\n  /*\n  auto f = Future<Unit>().via(&x).then([](){\n    throw std::runtime_error(\"Faildog\");\n  });*/\n  EXPECT_THROW(f.value(), std::exception);\n}\n\nclass CrappyExecutor : public Executor {\n public:\n  void add(Func /* f */) override { throw std::runtime_error(\"bad\"); }\n};\n\nTEST(Executor, CrappyExecutor) {\n  CrappyExecutor x;\n  bool flag = false;\n  auto f = folly::via(&x).thenError(\n      folly::tag_t<std::runtime_error>{}, [&](const std::runtime_error& e) {\n        EXPECT_STREQ(\"bad\", e.what());\n        flag = true;\n      });\n  EXPECT_TRUE(flag);\n}\n\nclass DoNothingExecutor : public Executor {\n public:\n  void add(Func f) override { storedFunc_ = std::move(f); }\n\n private:\n  Func storedFunc_;\n};\n\nTEST(Executor, DoNothingExecutor) {\n  DoNothingExecutor x;\n\n  // Submit future callback to DoNothingExecutor\n  auto f = folly::via(&x).thenValue([](auto&&) { return 42; });\n\n  // Callback function is stored in DoNothingExecutor, but not executed.\n  EXPECT_FALSE(f.isReady());\n\n  // Destroy the function stored in DoNothingExecutor. The future callback\n  // will never get executed.\n  x.add({});\n\n  EXPECT_TRUE(f.isReady());\n  EXPECT_THROW(std::move(f).get(), folly::BrokenPromise);\n}\n"
  },
  {
    "path": "folly/executors/test/ExecutorWithPriorityTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ExecutorWithPriority.h>\n\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(ExecutorWithPriorityTest, addWithCorrectPriorityTest) {\n  bool tookLopri = false;\n  auto completed = 0;\n  auto hipri = [&] {\n    EXPECT_FALSE(tookLopri);\n    completed++;\n  };\n  auto lopri = [&] {\n    tookLopri = true;\n    completed++;\n  };\n  auto pool = std::make_shared<CPUThreadPoolExecutor>(\n      0 /*numThreads*/, 2 /*numPriorities*/);\n  {\n    auto loPriExecutor = ExecutorWithPriority::create(\n        getKeepAliveToken(pool.get()), Executor::LO_PRI);\n    auto hiPriExecutor = ExecutorWithPriority::create(\n        getKeepAliveToken(pool.get()), Executor::HI_PRI);\n    for (int i = 0; i < 50; i++) {\n      loPriExecutor->add(lopri);\n    }\n    for (int i = 0; i < 50; i++) {\n      hiPriExecutor->add(hipri);\n    }\n    pool->setNumThreads(1);\n  }\n  pool->join();\n  EXPECT_EQ(100, completed);\n}\n\nTEST(ExecutorWithPriorityTest, updatePriorityUsingCallback) {\n  std::atomic<int8_t> curPri = -25;\n  auto completed = 0;\n  auto callback = [&curPri]() { return curPri += 1; };\n  // priorities range from -25 to 25\n  auto pool = std::make_shared<CPUThreadPoolExecutor>(\n      0 /*numThreads*/, 51 /*numPriorities*/);\n  {\n    auto executorWithPriority = ExecutorWithPriority::createDynamic(\n        getKeepAliveToken(pool.get()), callback);\n    for (int i = 0; i < 50; i++) {\n      auto task = [&completed, i]() {\n        completed++;\n        EXPECT_EQ(completed, 50 - i);\n      };\n      executorWithPriority->add(std::move(task));\n    }\n    pool->setNumThreads(1);\n  }\n  pool->join();\n  EXPECT_EQ(50, completed);\n  EXPECT_EQ(25, curPri);\n}\n"
  },
  {
    "path": "folly/executors/test/FiberIOExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/FiberIOExecutor.h>\n\n#include <memory>\n\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\n\nclass FiberIOExecutorTest : public testing::Test {};\n} // namespace\n\nTEST_F(FiberIOExecutorTest, event_base) {\n  auto tpe = std::make_shared<folly::IOThreadPoolExecutor>(1);\n  folly::FiberIOExecutor e(tpe);\n\n  ASSERT_NE(e.getEventBase(), nullptr);\n  ASSERT_EQ(e.getEventBase(), tpe->getEventBase());\n}\n\nTEST_F(FiberIOExecutorTest, basic_execution) {\n  auto tpe = std::make_shared<folly::IOThreadPoolExecutor>(1);\n  folly::FiberIOExecutor e(tpe);\n\n  // FiberIOExecutor should add tasks using the FiberManager mapped to the\n  // IOThreadPoolExecutor's event base.\n  folly::Baton<> baton;\n  bool inContext = false;\n\n  e.add([&]() {\n    inContext = folly::fibers::onFiber();\n    auto& lc = dynamic_cast<folly::fibers::EventBaseLoopController&>(\n        folly::fibers::getFiberManager(*e.getEventBase()).loopController());\n    auto& eb = lc.getEventBase()->getEventBase();\n    inContext =\n        inContext && &eb == folly::EventBaseManager::get()->getEventBase();\n    baton.post();\n  });\n  baton.wait();\n\n  ASSERT_TRUE(inContext);\n}\n"
  },
  {
    "path": "folly/executors/test/FunctionSchedulerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <atomic>\n#include <cassert>\n#include <random>\n\n#include <boost/thread.hpp>\n#include <glog/logging.h>\n\n#include <folly/Random.h>\n#include <folly/executors/FunctionScheduler.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\nusing std::atomic;\nusing std::chrono::duration_cast;\nusing std::chrono::microseconds;\nusing std::chrono::milliseconds;\nusing std::chrono::steady_clock;\n\nnamespace {\n\n/*\n * Helper functions for controlling how long this test takes.\n *\n * Using larger intervals here will make the tests less flaky when run on\n * heavily loaded systems.  However, this will also make the tests take longer\n * to run.\n */\nstatic const auto timeFactor = std::chrono::milliseconds(400);\nstd::chrono::milliseconds testInterval(int n) {\n  return n * timeFactor;\n}\nint getTicksWithinRange(int n, int min, int max) {\n  assert(min <= max);\n  n = std::max(min, n);\n  n = std::min(max, n);\n  return n;\n}\nvoid delay(float n) {\n  microseconds usec(\n      static_cast<microseconds::rep>(\n          duration_cast<microseconds>(timeFactor).count() * n));\n  usleep(usec.count());\n}\n\n} // namespace\n\nTEST(FunctionScheduler, StartAndShutdown) {\n  FunctionScheduler fs;\n  EXPECT_TRUE(fs.start());\n  EXPECT_FALSE(fs.start());\n  EXPECT_TRUE(fs.shutdown());\n  EXPECT_FALSE(fs.shutdown());\n  // start again\n  EXPECT_TRUE(fs.start());\n  EXPECT_FALSE(fs.start());\n  EXPECT_TRUE(fs.shutdown());\n  EXPECT_FALSE(fs.shutdown());\n}\n\nTEST(FunctionScheduler, SimpleAdd) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\");\n  fs.start();\n  delay(1);\n  EXPECT_EQ(2, total);\n  fs.shutdown();\n  delay(2);\n  EXPECT_EQ(2, total);\n}\n\nTEST(FunctionScheduler, AddCancel) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\");\n  fs.start();\n  delay(1);\n  EXPECT_EQ(2, total);\n  delay(2);\n  EXPECT_EQ(4, total);\n  EXPECT_TRUE(fs.cancelFunction(\"add2\"));\n  EXPECT_FALSE(fs.cancelFunction(\"NO SUCH FUNC\"));\n  delay(2);\n  EXPECT_EQ(4, total);\n  fs.addFunction([&] { total += 1; }, testInterval(2), \"add2\");\n  delay(1);\n  EXPECT_EQ(5, total);\n  delay(2);\n  EXPECT_EQ(6, total);\n  fs.shutdown();\n}\n\nTEST(FunctionScheduler, AddCancel2) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n\n  // Test adds and cancels while the scheduler is stopped\n  EXPECT_FALSE(fs.cancelFunction(\"add2\"));\n  fs.addFunction([&] { total += 1; }, testInterval(2), \"add2\");\n  EXPECT_TRUE(fs.cancelFunction(\"add2\"));\n  EXPECT_FALSE(fs.cancelFunction(\"add2\"));\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\");\n  fs.addFunction([&] { total += 3; }, testInterval(3), \"add3\");\n\n  EXPECT_EQ(0, total);\n  fs.start();\n  delay(1);\n  EXPECT_EQ(5, total);\n\n  // Cancel add2 while the scheduler is running\n  EXPECT_TRUE(fs.cancelFunction(\"add2\"));\n  EXPECT_FALSE(fs.cancelFunction(\"add2\"));\n  EXPECT_FALSE(fs.cancelFunction(\"bogus\"));\n\n  delay(3);\n  EXPECT_EQ(8, total);\n  EXPECT_TRUE(fs.cancelFunction(\"add3\"));\n\n  // Test a function that cancels itself\n  atomic<int> selfCancelCount{0};\n  fs.addFunction(\n      [&] {\n        ++selfCancelCount;\n        if (selfCancelCount > 2) {\n          fs.cancelFunction(\"selfCancel\");\n        }\n      },\n      testInterval(1),\n      \"selfCancel\",\n      testInterval(1));\n  delay(4);\n  EXPECT_EQ(3, selfCancelCount);\n  EXPECT_FALSE(fs.cancelFunction(\"selfCancel\"));\n\n  // Test a function that schedules another function\n  atomic<int> adderCount{0};\n  atomic<int> fn2Count = 0;\n  auto fn2 = [&] { ++fn2Count; };\n  auto fnAdder = [&] {\n    ++adderCount;\n    if (adderCount == 2) {\n      fs.addFunction(fn2, testInterval(3), \"fn2\", testInterval(2));\n    }\n  };\n  fs.addFunction(fnAdder, testInterval(4), \"adder\");\n  // t0: adder fires\n  delay(1); // t1\n  EXPECT_EQ(1, adderCount);\n  EXPECT_EQ(0, fn2Count);\n  // t4: adder fires, schedules fn2\n  delay(4); // t5\n  EXPECT_EQ(2, adderCount);\n  EXPECT_EQ(0, fn2Count);\n  // t6: fn2 fires\n  delay(2); // t7\n  EXPECT_EQ(2, adderCount);\n  EXPECT_EQ(1, fn2Count);\n  // t8: adder fires\n  // t9: fn2 fires\n  delay(3); // t10\n  EXPECT_EQ(3, adderCount);\n  EXPECT_EQ(2, fn2Count);\n  EXPECT_TRUE(fs.cancelFunction(\"fn2\"));\n  EXPECT_TRUE(fs.cancelFunction(\"adder\"));\n  delay(5); // t10\n  EXPECT_EQ(3, adderCount);\n  EXPECT_EQ(2, fn2Count);\n\n  EXPECT_EQ(8, total);\n  EXPECT_EQ(3, selfCancelCount);\n}\n\nTEST(FunctionScheduler, AddCancelInitialDelayStress) {\n  FunctionScheduler fs;\n  folly::Baton<> startTest;\n  fs.addFunctionOnce([&] { startTest.wait(); }, \"entryBarrier\");\n  fs.start();\n\n  auto seed = folly::randomNumberSeed();\n  LOG(INFO) << \"seed: \" << seed;\n  std::mt19937 rng(seed);\n  auto numFunctions = folly::Random::rand32(1, 1000, rng);\n  std::vector<int64_t> indices(numFunctions);\n  std::iota(indices.begin(), indices.end(), 1);\n\n  int64_t lastIndexRan{0};\n  auto addFunction = [&](int64_t index, bool removeBeforeAdding) {\n    std::string name = fmt::format(\"f{}\", index);\n    if (removeBeforeAdding) {\n      fs.cancelFunction(name);\n    }\n    fs.addFunctionOnce(\n        [&, index = index] {\n          EXPECT_LT(lastIndexRan, index);\n          lastIndexRan = index;\n        },\n        name,\n        std::chrono::milliseconds(index * 50));\n  };\n\n  std::shuffle(indices.begin(), indices.end(), rng);\n  for (int idx : indices) {\n    addFunction(idx, false);\n  }\n  std::shuffle(indices.begin(), indices.end(), rng);\n  for (int idx : indices) {\n    if (folly::Random::oneIn(2, rng)) {\n      addFunction(idx, true);\n    }\n    if (folly::Random::oneIn(7, rng)) {\n      fs.cancelFunction(fmt::format(\"f{}\", idx));\n    }\n  }\n  folly::Baton<> finishTest;\n  fs.addFunctionOnce(\n      [&] { finishTest.post(); },\n      \"exitBarrier\",\n      std::chrono::milliseconds(numFunctions * 60));\n  startTest.post();\n  finishTest.wait();\n  fs.shutdown();\n}\n\nTEST(FunctionScheduler, AddMultiple) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\");\n  fs.addFunction([&] { total += 3; }, testInterval(3), \"add3\");\n  EXPECT_THROW(\n      fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\"),\n      std::invalid_argument); // function name already exists\n\n  fs.start();\n  delay(1);\n  EXPECT_EQ(5, total);\n  delay(4);\n  EXPECT_EQ(12, total);\n  EXPECT_TRUE(fs.cancelFunction(\"add2\"));\n  delay(2);\n  EXPECT_EQ(15, total);\n  fs.shutdown();\n  delay(3);\n  EXPECT_EQ(15, total);\n  fs.shutdown();\n}\n\nTEST(FunctionScheduler, AddAfterStart) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\");\n  fs.addFunction([&] { total += 3; }, testInterval(2), \"add3\");\n  fs.start();\n  delay(3);\n  EXPECT_EQ(10, total);\n  fs.addFunction([&] { total += 2; }, testInterval(3), \"add22\");\n  delay(2);\n  EXPECT_EQ(17, total);\n}\n\nTEST(FunctionScheduler, ShutdownStart) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\");\n  fs.start();\n  delay(1);\n  fs.shutdown();\n  fs.start();\n  delay(1);\n  EXPECT_EQ(4, total);\n  EXPECT_FALSE(fs.cancelFunction(\"add3\")); // non existing\n  delay(2);\n  EXPECT_EQ(6, total);\n}\n\nTEST(FunctionScheduler, ResetFunc) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction([&] { total += 2; }, testInterval(3), \"add2\");\n  fs.addFunction([&] { total += 3; }, testInterval(3), \"add3\");\n  fs.start();\n  delay(1);\n  EXPECT_EQ(5, total);\n  EXPECT_FALSE(fs.resetFunctionTimer(\"NON_EXISTING\"));\n  EXPECT_TRUE(fs.resetFunctionTimer(\"add2\"));\n  delay(1);\n  // t2: after the reset, add2 should have been invoked immediately\n  EXPECT_EQ(7, total);\n  delay(1.5);\n  // t3.5: add3 should have been invoked. add2 should not\n  EXPECT_EQ(10, total);\n  delay(1);\n  // t4.5: add2 should have been invoked once more (it was reset at t1)\n  EXPECT_EQ(12, total);\n}\n\nTEST(FunctionScheduler, ResetFunc2) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunctionOnce([&] { total += 2; }, \"add2\", testInterval(1));\n  fs.addFunctionOnce([&] { total += 3; }, \"add3\", testInterval(1));\n  fs.start();\n  delay(2);\n  fs.addFunctionOnce([&] { total += 3; }, \"add4\", testInterval(2));\n  EXPECT_TRUE(fs.resetFunctionTimer(\"add4\"));\n  fs.addFunctionOnce([&] { total += 3; }, \"add6\", testInterval(2));\n  delay(1);\n  EXPECT_TRUE(fs.resetFunctionTimer(\"add4\"));\n  delay(3);\n  EXPECT_FALSE(fs.resetFunctionTimer(\"add3\"));\n  fs.addFunctionOnce([&] { total += 3; }, \"add4\", testInterval(1));\n}\n\nTEST(FunctionScheduler, ResetFuncWhileRunning) {\n  struct State {\n    boost::barrier barrier_a{2};\n    boost::barrier barrier_b{2};\n    boost::barrier barrier_c{2};\n    boost::barrier barrier_d{2};\n    atomic<bool> set = false;\n    atomic<size_t> count = 0;\n  };\n\n  State state; // held by ref\n  auto mv = std::make_shared<size_t>(); // gets moved\n\n  FunctionScheduler fs;\n  fs.addFunction(\n      [&, mv /* ref + shared_ptr fit in in-situ storage */] {\n        if (!state.set) { // first invocation\n          state.barrier_a.wait();\n          // ensure that resetFunctionTimer is called in this critical section\n          state.barrier_b.wait();\n          ++state.count;\n          EXPECT_TRUE(bool(mv)) << \"bug repro: mv was moved-out\";\n          state.barrier_c.wait();\n          // main thread checks count here\n          state.barrier_d.wait();\n        } else { // subsequent invocations\n          ++state.count;\n        }\n      },\n      testInterval(3),\n      \"nada\");\n  fs.start();\n\n  state.barrier_a.wait();\n  state.set = true;\n  fs.resetFunctionTimer(\"nada\");\n  EXPECT_EQ(0, state.count) << \"sanity check\";\n  state.barrier_b.wait();\n  // fn thread increments count and checks mv here\n  state.barrier_c.wait();\n  EXPECT_EQ(1, state.count) << \"sanity check\";\n  state.barrier_d.wait();\n  delay(1);\n  EXPECT_EQ(2, state.count) << \"sanity check\";\n}\n\nTEST(FunctionScheduler, AddInvalid) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  // interval may not be negative\n  EXPECT_THROW(\n      fs.addFunction([&] { total += 2; }, testInterval(-1), \"add2\"),\n      std::invalid_argument);\n\n  EXPECT_FALSE(fs.cancelFunction(\"addNoFunc\"));\n}\n\nTEST(FunctionScheduler, NoFunctions) {\n  FunctionScheduler fs;\n  EXPECT_TRUE(fs.start());\n  fs.shutdown();\n  FunctionScheduler fs2;\n  fs2.shutdown();\n}\n\nTEST(FunctionScheduler, AddWhileRunning) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.start();\n  delay(1);\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\");\n  // The function should be invoked nearly immediately when we add it\n  // and the FunctionScheduler is already running\n  delay(0.5);\n  auto t = total.load();\n  EXPECT_EQ(2, t);\n  delay(2);\n  t = total.load();\n  EXPECT_EQ(4, t);\n}\n\nTEST(FunctionScheduler, NoShutdown) {\n  atomic<int> total{0};\n  {\n    FunctionScheduler fs;\n    fs.addFunction([&] { total += 2; }, testInterval(1), \"add2\");\n    fs.start();\n    delay(0.5);\n    EXPECT_EQ(2, total);\n  }\n  // Destroyed the FunctionScheduler without calling shutdown.\n  // Everything should have been cleaned up, and the function will no longer\n  // get called.\n  delay(2);\n  EXPECT_EQ(2, total);\n}\n\nTEST(FunctionScheduler, StartDelay) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction([&] { total += 2; }, testInterval(2), \"add2\", testInterval(2));\n  fs.addFunction([&] { total += 3; }, testInterval(3), \"add3\", testInterval(2));\n  EXPECT_THROW(\n      fs.addFunction(\n          [&] { total += 2; }, testInterval(3), \"addX\", testInterval(-1)),\n      std::invalid_argument);\n  fs.start();\n  delay(1); // t1\n  EXPECT_EQ(0, total);\n  // t2 : add2 total=2\n  // t2 : add3 total=5\n  delay(2); // t3\n  EXPECT_EQ(5, total);\n  // t4 : add2: total=7\n  // t5 : add3: total=10\n  // t6 : add2: total=12\n  delay(4); // t7\n  EXPECT_EQ(12, total);\n  fs.cancelFunction(\"add2\");\n  // t8 : add3: total=15\n  delay(2); // t9\n  EXPECT_EQ(15, total);\n  fs.shutdown();\n  delay(3);\n  EXPECT_EQ(15, total);\n  fs.shutdown();\n}\n\nTEST(FunctionScheduler, NoSteadyCatchup) {\n  std::atomic<int> ticks(0);\n  FunctionScheduler fs;\n  // fs.setSteady(false); is the default\n  fs.addFunction(\n      [&ticks] {\n        if (++ticks == 2) {\n          std::this_thread::sleep_for(std::chrono::milliseconds(200));\n        }\n      },\n      milliseconds(5));\n  fs.start();\n  std::this_thread::sleep_for(std::chrono::milliseconds(500));\n\n  // no steady catch up means we'd tick once for 200ms, then remaining\n  // 300ms / 5 = 60 times\n  EXPECT_LE(ticks.load(), 61);\n}\n\nTEST(FunctionScheduler, SteadyCatchup) {\n  std::atomic<int> ticks(0);\n  FunctionScheduler fs;\n  fs.setSteady(true);\n  fs.addFunction(\n      [&ticks] {\n        if (++ticks == 2) {\n          std::this_thread::sleep_for(std::chrono::milliseconds(200));\n        }\n      },\n      milliseconds(5));\n  fs.start();\n\n  std::this_thread::sleep_for(std::chrono::milliseconds(500));\n\n  // tick every 5ms. Despite tick == 2 is slow, later ticks should be fast\n  // enough to catch back up to schedule\n  EXPECT_NEAR(100, ticks.load(), 10);\n}\n\nTEST(FunctionScheduler, UniformDistribution) {\n  atomic<int> total{0};\n  const int kTicks = 2;\n  std::chrono::milliseconds minInterval =\n      testInterval(kTicks) - (timeFactor / 5);\n  std::chrono::milliseconds maxInterval =\n      testInterval(kTicks) + (timeFactor / 5);\n  FunctionScheduler fs;\n  fs.addFunctionUniformDistribution(\n      [&] { total += 2; },\n      minInterval,\n      maxInterval,\n      \"UniformDistribution\",\n      std::chrono::milliseconds(0));\n  fs.start();\n  delay(1);\n  EXPECT_EQ(2, total);\n  delay(kTicks);\n  EXPECT_EQ(4, total);\n  delay(kTicks);\n  EXPECT_EQ(6, total);\n  fs.shutdown();\n  delay(2);\n  EXPECT_EQ(6, total);\n}\n\nTEST(FunctionScheduler, ConsistentDelay) {\n  std::atomic<int> ticks(0);\n  FunctionScheduler fs;\n\n  std::atomic<long long> epoch(0);\n  epoch = duration_cast<milliseconds>(steady_clock::now().time_since_epoch())\n              .count();\n\n  // We should have runs at t = 0, 600, 800, 1200, or 4 total.\n  // If at const interval, it would be t = 0, 600, 1000, or 3 total.\n  fs.addFunctionConsistentDelay(\n      [&ticks, &epoch] {\n        auto now =\n            duration_cast<milliseconds>(steady_clock::now().time_since_epoch())\n                .count();\n        int t = ++ticks;\n        if (t != 2) {\n          // Sensitive to delays above 100ms.\n          EXPECT_NEAR((now - epoch) - (t - 1) * 400, 0, 100);\n        }\n        if (t == 1) {\n          /* sleep override */\n          std::this_thread::sleep_for(std::chrono::milliseconds(600));\n        }\n      },\n      milliseconds(400),\n      \"ConsistentDelay\");\n\n  fs.start();\n\n  /* sleep override */\n  std::this_thread::sleep_for(std::chrono::milliseconds(1300));\n  EXPECT_EQ(ticks.load(), 4);\n}\n\nTEST(FunctionScheduler, ExponentialBackoff) {\n  atomic<int> total{0};\n  atomic<int> expectedInterval{0};\n  atomic<int> nextInterval{2};\n  FunctionScheduler fs;\n  fs.addFunctionGenericDistribution(\n      [&] { total += 2; },\n      [&expectedInterval, &nextInterval]() mutable {\n        auto interval = nextInterval.load();\n        expectedInterval = interval;\n        nextInterval = interval * interval;\n        return testInterval(interval);\n      },\n      \"ExponentialBackoff\",\n      \"2^n * 100ms\",\n      std::chrono::milliseconds(0));\n  fs.start();\n  delay(1);\n  EXPECT_EQ(2, total);\n  delay(expectedInterval);\n  EXPECT_EQ(4, total);\n  delay(expectedInterval);\n  EXPECT_EQ(6, total);\n  fs.shutdown();\n  delay(2);\n  EXPECT_EQ(6, total);\n}\n\nTEST(FunctionScheduler, GammaIntervalDistribution) {\n  atomic<int> total{0};\n  atomic<int> expectedInterval{0};\n  FunctionScheduler fs;\n  std::default_random_engine generator(folly::Random::rand32());\n  // The alpha and beta arguments are selected, somewhat randomly, to be 2.0.\n  // These values do not matter much in this test, as we are not testing the\n  // std::gamma_distribution itself...\n  std::gamma_distribution<double> gamma(2.0, 2.0);\n  fs.addFunctionGenericDistribution(\n      [&] { total += 2; },\n      [&expectedInterval, generator, gamma]() mutable {\n        expectedInterval =\n            getTicksWithinRange(static_cast<int>(gamma(generator)), 2, 10);\n        return testInterval(expectedInterval);\n      },\n      \"GammaDistribution\",\n      \"gamma(2.0,2.0)*100ms\",\n      std::chrono::milliseconds(0));\n  fs.start();\n  delay(1);\n  EXPECT_EQ(2, total);\n  delay(expectedInterval);\n  EXPECT_EQ(4, total);\n  delay(expectedInterval);\n  EXPECT_EQ(6, total);\n  fs.shutdown();\n  delay(2);\n  EXPECT_EQ(6, total);\n}\n\nTEST(FunctionScheduler, PoissonDistribution) {\n  auto interval = std::chrono::hours(24 * 365 * 10);\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction(\n      [&] { total += 2; },\n      interval,\n      folly::FunctionScheduler::LatencyDistribution(true, interval),\n      \"PoissonDistribution\",\n      std::chrono::milliseconds(0));\n  fs.start();\n  delay(1);\n  EXPECT_EQ(2, total);\n}\n\nTEST(FunctionScheduler, AddWithRunOnce) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunctionOnce([&] { total += 2; }, \"add2\");\n  fs.start();\n  delay(1);\n  EXPECT_EQ(2, total);\n  delay(2);\n  EXPECT_EQ(2, total);\n\n  fs.addFunctionOnce([&] { total += 2; }, \"add2\");\n  delay(1);\n  EXPECT_EQ(4, total);\n  delay(2);\n  EXPECT_EQ(4, total);\n\n  fs.shutdown();\n}\n\nTEST(FunctionScheduler, cancelFunctionAndWait) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n  fs.addFunction(\n      [&] {\n        delay(5);\n        total += 2;\n      },\n      testInterval(100),\n      \"add2\");\n\n  fs.start();\n  delay(1);\n  EXPECT_EQ(0, total); // add2 is still sleeping\n\n  EXPECT_TRUE(fs.cancelFunctionAndWait(\"add2\"));\n  EXPECT_EQ(2, total); // add2 should have completed\n\n  EXPECT_FALSE(fs.cancelFunction(\"add2\")); // add2 has been canceled\n  fs.shutdown();\n}\n\nTEST(FunctionScheduler, cancelAllFunctionsAndWait) {\n  atomic<int> total{0};\n  FunctionScheduler fs;\n\n  fs.addFunction(\n      [&] {\n        delay(5);\n        total += 2;\n      },\n      testInterval(100),\n      \"add2\");\n\n  fs.start();\n  delay(1);\n  EXPECT_EQ(0, total); // add2 is still sleeping\n\n  fs.cancelAllFunctionsAndWait();\n  EXPECT_EQ(2, total);\n\n  EXPECT_FALSE(fs.cancelFunction(\"add2\")); // add2 has been canceled\n  fs.shutdown();\n}\n\nTEST(FunctionScheduler, CancelAndWaitOnRunningFunc) {\n  folly::Baton<> baton;\n  std::thread th([&baton]() {\n    FunctionScheduler fs;\n    fs.addFunction([] { delay(10); }, testInterval(2), \"func\");\n    fs.start();\n    delay(1);\n    EXPECT_TRUE(fs.cancelFunctionAndWait(\"func\"));\n    baton.post();\n  });\n\n  ASSERT_TRUE(baton.try_wait_for(testInterval(15)));\n  th.join();\n}\n\nTEST(FunctionScheduler, CancelAllAndWaitWithRunningFunc) {\n  folly::Baton<> baton;\n  std::thread th([&baton]() {\n    FunctionScheduler fs;\n    fs.addFunction([] { delay(10); }, testInterval(2), \"func\");\n    fs.start();\n    delay(1);\n    fs.cancelAllFunctionsAndWait();\n    baton.post();\n  });\n\n  ASSERT_TRUE(baton.try_wait_for(testInterval(15)));\n  th.join();\n}\n\nTEST(FunctionScheduler, CancelAllAndWaitWithOneRunningAndOneWaiting) {\n  folly::Baton<> baton;\n  std::thread th([&baton]() {\n    std::atomic<int> nExecuted(0);\n    FunctionScheduler fs;\n    fs.addFunction(\n        [&nExecuted] {\n          nExecuted++;\n          delay(10);\n        },\n        testInterval(2),\n        \"func0\");\n    fs.addFunction(\n        [&nExecuted] {\n          nExecuted++;\n          delay(10);\n        },\n        testInterval(2),\n        \"func1\",\n        testInterval(5));\n    fs.start();\n    delay(1);\n    fs.cancelAllFunctionsAndWait();\n    EXPECT_EQ(nExecuted, 1);\n    baton.post();\n  });\n\n  ASSERT_TRUE(baton.try_wait_for(testInterval(15)));\n  th.join();\n}\n\nTEST(FunctionScheduler, ConcurrentCancelFunctionAndWait) {\n  FunctionScheduler fs;\n  fs.addFunction([] { delay(10); }, testInterval(2), \"func\");\n\n  fs.start();\n  delay(1);\n  std::thread th1([&fs] { EXPECT_TRUE(fs.cancelFunctionAndWait(\"func\")); });\n  delay(1);\n  std::thread th2([&fs] { EXPECT_FALSE(fs.cancelFunctionAndWait(\"func\")); });\n  th1.join();\n  th2.join();\n}\n"
  },
  {
    "path": "folly/executors/test/GlobalCPUExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/GlobalExecutor.h>\n\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/portability/GTest.h>\n\nTEST(GlobalCPUExecutorTest, CPUThreadCountFlagSet) {\n  folly::gflags::FlagSaver flagsaver;\n\n  FLAGS_folly_global_cpu_executor_threads = 100;\n  auto cpu_threadpool = dynamic_cast<folly::CPUThreadPoolExecutor*>(\n      folly::getGlobalCPUExecutor().get());\n\n  EXPECT_EQ(cpu_threadpool->numThreads(), 100);\n}\n"
  },
  {
    "path": "folly/executors/test/GlobalExecutorAssignmentTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Singleton.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nTEST(GlobalExecutorAssignmentTest, GlobalCPUExecutorAsImmutable) {\n  folly::Baton b;\n  // The default CPU executor is a synchronous inline executor, lets verify\n  // that work we add is executed\n  auto count = 0;\n  auto f = [&]() {\n    count++;\n    b.post();\n  };\n\n  FOLLY_PUSH_WARNING\n  FOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\") {\n    auto inlineExec = getCPUExecutor();\n    EXPECT_EQ(\n        dynamic_cast<folly::CPUThreadPoolExecutor*>(inlineExec.get()), nullptr);\n\n    setCPUExecutorToGlobalCPUExecutor();\n\n    auto cpuExec = getCPUExecutor();\n    EXPECT_NE(\n        dynamic_cast<folly::CPUThreadPoolExecutor*>(cpuExec.get()), nullptr);\n\n    // Verify execution\n    getCPUExecutor()->add(f);\n    b.wait();\n    EXPECT_EQ(1, count);\n  }\n  FOLLY_POP_WARNING\n}\n"
  },
  {
    "path": "folly/executors/test/GlobalExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/GlobalExecutor.h>\n\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/IOExecutor.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/executors/VirtualExecutor.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/SaturatingSemaphore.h>\n#include <folly/system/HardwareConcurrency.h>\n\nusing namespace folly;\n\nTEST(GlobalExecutorTest, GlobalImmutableCPUExecutor) {\n  folly::Baton<> b;\n\n  auto count = 0;\n  auto f = [&]() {\n    count++;\n    b.post();\n  };\n\n  // Don't explode, we should create the default global CPUExecutor lazily here.\n  getGlobalCPUExecutor()->add(f);\n  b.wait();\n  EXPECT_EQ(1, count);\n}\n\nTEST(GlobalExecutorTest, GlobalImmutableIOExecutor) {\n  folly::Baton<> b;\n\n  auto count = 0;\n  auto f = [&]() {\n    count++;\n    b.post();\n  };\n\n  // Don't explode, we should create the default global CPUExecutor lazily here.\n  getGlobalIOExecutor()->add(f);\n  b.wait();\n  EXPECT_EQ(1, count);\n}\n\nTEST(GlobalExecutorTest, GlobalCPUExecutor) {\n  class DummyExecutor : public folly::Executor {\n   public:\n    void add(Func fn) override {\n      fn();\n      count++;\n    }\n    int count{0};\n  };\n\n  // The default CPU executor is a thread pool\n  auto f = [&]() {};\n\n  // Don't explode, we should create the default global CPUExecutor lazily here.\n  FOLLY_PUSH_WARNING\n  FOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\")\n  getCPUExecutor()->add(f);\n\n  {\n    auto dummy = std::make_shared<DummyExecutor>();\n    setCPUExecutor(dummy);\n    getCPUExecutor()->add(f);\n    // Make sure we were properly installed.\n    EXPECT_EQ(1, dummy->count);\n  }\n\n  // Don't explode, we should restore the default global CPUExecutor because our\n  // weak reference to dummy has expired\n  getCPUExecutor()->add(f);\n  FOLLY_POP_WARNING\n}\n\nTEST(GlobalExecutorTest, GlobalIOExecutor) {\n  class DummyExecutor : public IOExecutor {\n   public:\n    void add(Func) override { count++; }\n    folly::EventBase* getEventBase() override { return nullptr; }\n    int count{0};\n  };\n\n  auto f = []() {};\n\n  FOLLY_PUSH_WARNING\n  FOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\")\n  // Don't explode, we should create the default global IOExecutor lazily here.\n  getIOExecutor()->add(f);\n\n  {\n    auto dummy = std::make_shared<DummyExecutor>();\n    setIOExecutor(dummy);\n    getIOExecutor()->add(f);\n    // Make sure we were properly installed.\n    EXPECT_EQ(1, dummy->count);\n  }\n\n  // Don't explode, we should restore the default global IOExecutor because our\n  // weak reference to dummy has expired\n  getIOExecutor()->add(f);\n  FOLLY_POP_WARNING\n}\n\nTEST(GlobalExecutorTest, IOThreadCountFlagUnset) {\n  folly::gflags::FlagSaver flagsaver;\n\n  auto io_threadpool = dynamic_cast<folly::IOThreadPoolExecutor*>(\n      folly::getGlobalIOExecutor().get());\n\n  EXPECT_EQ(io_threadpool->numThreads(), folly::available_concurrency());\n}\n\nTEST(GlobalExecutorTest, CPUThreadCountFlagUnset) {\n  folly::gflags::FlagSaver flagsaver;\n\n  EXPECT_EQ(\n      getGlobalCPUExecutorCounters().numThreads,\n      folly::available_concurrency());\n}\n\nTEST(GlobalExecutorTest, GetGlobalCPUExecutorCounters) {\n  const size_t numThreads = getGlobalCPUExecutorCounters().numThreads;\n  const size_t numTasks = 1000 + numThreads;\n  // Makes all tasks block until we're done.\n  folly::SaturatingSemaphore<> block;\n  // Ensures that the semaphore is alive until all tasks have run.\n  folly::VirtualExecutor ex(getGlobalCPUExecutor());\n  for (size_t i = 0; i < numTasks; ++i) {\n    ex.add([&] { block.wait(); });\n  }\n  while (true) {\n    auto counters = getGlobalCPUExecutorCounters();\n    // We don't know how many tasks have been picked up, but we know they have\n    // to be at most numThreads because they're blocked.\n    EXPECT_GE(counters.numPendingTasks, numTasks - numThreads);\n    EXPECT_LE(counters.numPendingTasks, numTasks);\n    // Eventually, the executor should start all the available threads. If not,\n    // the test will deadlock (and thus time out).\n    if (counters.numActiveThreads == counters.numThreads) {\n      break;\n    }\n    /* sleep override */\n    std::this_thread::sleep_for(std::chrono::microseconds(1));\n  }\n\n  // Unblock everything so we can shut down.\n  block.post();\n}\n"
  },
  {
    "path": "folly/executors/test/GlobalIOExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/GlobalExecutor.h>\n\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/IOExecutor.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/system/HardwareConcurrency.h>\n\nusing namespace folly;\n\nTEST(GlobalExecutorTest, IOThreadCountFlagSet) {\n  folly::gflags::FlagSaver flagsaver;\n\n  FLAGS_folly_global_io_executor_threads = 100;\n  auto io_threadpool = dynamic_cast<folly::IOThreadPoolExecutor*>(\n      folly::getGlobalIOExecutor().get());\n\n  EXPECT_EQ(io_threadpool->numThreads(), 100);\n}\n"
  },
  {
    "path": "folly/executors/test/IOThreadPoolDeadlockDetectorObserverTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n\n#include <fmt/core.h>\n\n#include <glog/logging.h>\n#include <folly/concurrency/DeadlockDetector.h>\n#include <folly/executors/IOThreadPoolDeadlockDetectorObserver.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nclass DeadlockDetectorMock : public DeadlockDetector {\n public:\n  DeadlockDetectorMock(std::shared_ptr<std::atomic<int32_t>> counter)\n      : counter_(counter) {\n    counter_->fetch_add(1);\n  }\n  ~DeadlockDetectorMock() override { counter_->fetch_sub(1); }\n\n private:\n  std::shared_ptr<std::atomic<int32_t>> counter_;\n};\n\nclass DeadlockDetectorFactoryMock : public DeadlockDetectorFactory {\n public:\n  std::unique_ptr<DeadlockDetector> create(\n      folly::Executor* executor, const std::string& name) override {\n    EXPECT_TRUE(executor != nullptr);\n    auto tid = folly::getOSThreadID();\n    std::string expectedName = fmt::format(\"TestPool:{}\", tid);\n    EXPECT_EQ(expectedName, name);\n    {\n      auto* getter =\n          CHECK_NOTNULL(dynamic_cast<GetThreadIdCollector*>(executor));\n      auto* tidCollector = CHECK_NOTNULL(getter->getThreadIdCollector());\n      auto tids = tidCollector->collectThreadIds();\n      EXPECT_EQ(1, tids.threadIds.size());\n      EXPECT_EQ(tid, tids.threadIds[0]);\n    }\n    name_ = name;\n    auto retval = std::make_unique<DeadlockDetectorMock>(counter_);\n    baton.post();\n    return retval;\n  }\n\n  int32_t getCounter() { return counter_->load(); }\n\n  std::string getName() const { return name_.copy(); }\n\n  folly::Baton<> baton;\n\n private:\n  std::shared_ptr<std::atomic<int32_t>> counter_ =\n      std::make_shared<std::atomic<int32_t>>(0);\n  folly::Synchronized<std::string> name_;\n};\n\nTEST(\n    IOThreadPoolDeadlockDetectorObserverTest,\n    VerifyDeadlockDetectorCreatedAndDestroyedOnAddRemoveObserver) {\n  auto deadlockDetectorFactory =\n      std::make_shared<DeadlockDetectorFactoryMock>();\n\n  auto executor = std::make_shared<IOThreadPoolExecutor>(\n      1, std::make_shared<folly::NamedThreadFactory>(\"TestPool\"));\n  pid_t thid;\n  executor->getEventBase()->runInEventBaseThreadAndWait([&] {\n    thid = folly::getOSThreadID();\n  });\n\n  auto observer = std::make_shared<folly::IOThreadPoolDeadlockDetectorObserver>(\n      deadlockDetectorFactory.get(), \"TestPool\");\n\n  ASSERT_EQ(0, deadlockDetectorFactory->getCounter())\n      << \"Deadlock detector not yet registered\";\n\n  executor->addObserver(observer);\n  deadlockDetectorFactory->baton.wait();\n  deadlockDetectorFactory->baton.reset();\n  ASSERT_EQ(1, deadlockDetectorFactory->getCounter())\n      << \"Deadlock detector must be registered by observer\";\n  EXPECT_EQ(\n      fmt::format(\"TestPool:{}\", thid), deadlockDetectorFactory->getName());\n\n  executor->removeObserver(observer);\n  ASSERT_EQ(0, deadlockDetectorFactory->getCounter())\n      << \"Removing observer must destroy deadlock detector\";\n\n  executor->addObserver(observer);\n  deadlockDetectorFactory->baton.wait();\n  deadlockDetectorFactory->baton.reset();\n  ASSERT_EQ(1, deadlockDetectorFactory->getCounter())\n      << \"Deadlock detector must be registered by observer\";\n\n  executor->stop();\n  ASSERT_EQ(0, deadlockDetectorFactory->getCounter())\n      << \"Stopping threadpool must deregister all observers\";\n}\n"
  },
  {
    "path": "folly/executors/test/IOThreadPoolExecutorBaseTestLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <atomic>\n#include <memory>\n#include <optional>\n#include <random>\n\n#include <fmt/format.h>\n#include <glog/logging.h>\n#include <folly/Random.h>\n#include <folly/container/F14Set.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nnamespace folly {\nnamespace test {\n\ntemplate <typename T>\nclass IOThreadPoolExecutorBaseTest : public ::testing::Test {\n public:\n  IOThreadPoolExecutorBaseTest() = default;\n};\n\nTYPED_TEST_SUITE_P(IOThreadPoolExecutorBaseTest);\n\nTYPED_TEST_P(IOThreadPoolExecutorBaseTest, IOObserver) {\n  struct EventBaseAccumulator : IOThreadPoolExecutorBase::IOObserver {\n    void registerEventBase(EventBase& evb) noexcept override {\n      // Observers should be called while the evbs are running, so this\n      // operation should complete.\n      evb.runInEventBaseThreadAndWait([&] { evbs.insert(&evb); });\n    }\n    void unregisterEventBase(EventBase& evb) noexcept override {\n      // Same as registerEventBase().\n      evb.runInEventBaseThreadAndWait([&] { evbs.erase(&evb); });\n    }\n\n    F14FastSet<EventBase*> evbs;\n  };\n\n  static constexpr size_t kNumThreads = 16;\n\n  TypeParam ex{kNumThreads};\n  auto observer1 = std::make_shared<EventBaseAccumulator>();\n  auto observer2 = std::make_shared<EventBaseAccumulator>();\n  ex.addObserver(observer1);\n  ex.addObserver(observer2);\n  EXPECT_EQ(observer1->evbs.size(), kNumThreads);\n  EXPECT_EQ(observer2->evbs.size(), kNumThreads);\n  ex.removeObserver(observer1);\n  EXPECT_EQ(observer1->evbs.size(), 0);\n  EXPECT_EQ(observer2->evbs.size(), kNumThreads);\n  ex.join();\n  EXPECT_EQ(observer1->evbs.size(), 0);\n  EXPECT_EQ(observer2->evbs.size(), 0);\n}\n\nTYPED_TEST_P(IOThreadPoolExecutorBaseTest, GetEventBaseFromEvb) {\n  static constexpr size_t kNumThreads = 16;\n  TypeParam ex{kNumThreads};\n  auto evbs = ex.getAllEventBases();\n  std::shuffle(evbs.begin(), evbs.end(), std::default_random_engine{});\n\n  for (auto ka : evbs) {\n    auto* evb = ka.get();\n    // If called from the EventBase thread, getEventBase() and add() stick to\n    // the current EventBase.\n    evb->runInEventBaseThreadAndWait([&] {\n      EXPECT_EQ(evb, EventBaseManager::get()->getExistingEventBase());\n      EXPECT_EQ(evb, ex.getEventBase());\n      ex.add([&ex, evb] {\n        EXPECT_EQ(evb, ex.getEventBase());\n        EXPECT_EQ(evb, EventBaseManager::get()->getExistingEventBase());\n      });\n    });\n  }\n}\n\nTYPED_TEST_P(IOThreadPoolExecutorBaseTest, StressTimeouts) {\n  // Exercise multiplexed executors.\n  static constexpr size_t kNumExecutors = 2;\n  static constexpr size_t kNumThreads = 16;\n  static constexpr size_t kNumTimersPerEvb = 100;\n  static constexpr size_t kNumItersPerTimer = 16;\n  static constexpr uint32_t kMaxTimeoutMs = 100;\n\n  struct Timer : AsyncTimeout {\n    using AsyncTimeout::AsyncTimeout;\n    using Clock = std::chrono::steady_clock;\n\n    void schedule() {\n      timeout =\n          std::chrono::milliseconds(folly::Random::rand32(1, kMaxTimeoutMs));\n      start = Clock::now();\n      scheduleTimeout(timeout);\n    }\n\n    void timeoutExpired() noexcept override {\n      auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(\n          Clock::now() - (start + timeout));\n      EXPECT_GE(delay.count(), 0);\n      sumDelay += delay;\n      maxDelay = std::max(maxDelay, delay);\n\n      if (++count < kNumItersPerTimer) {\n        schedule();\n      } else {\n        done.post();\n      }\n    }\n\n    std::chrono::milliseconds timeout;\n    Clock::time_point start;\n\n    size_t count = 0;\n    std::chrono::milliseconds sumDelay{0};\n    std::chrono::milliseconds maxDelay{0};\n    Baton<> done;\n  };\n\n  std::vector<std::unique_ptr<TypeParam>> executors;\n  std::vector<folly::Executor::KeepAlive<folly::EventBase>> evbs;\n  for (size_t i = 0; i < kNumExecutors; ++i) {\n    auto& ex = executors.emplace_back(std::make_unique<TypeParam>(kNumThreads));\n    auto exEvbs = ex->getAllEventBases();\n    evbs.insert(evbs.end(), exEvbs.begin(), exEvbs.end());\n  }\n\n  using TimerList = std::array<Timer, kNumTimersPerEvb>;\n  std::vector<std::unique_ptr<TimerList>> timerLists;\n  timerLists.reserve(evbs.size());\n  for (const auto& evb : evbs) {\n    auto& timerList = timerLists.emplace_back();\n    timerList = std::make_unique<TimerList>();\n\n    evb->runInEventBaseThread([&timerList, &evb] {\n      for (auto& timer : *timerList) {\n        timer.attachEventBase(evb.get());\n        timer.schedule();\n      }\n    });\n  }\n\n  size_t count = 0;\n  std::chrono::milliseconds sumDelay{0};\n  std::chrono::milliseconds maxDelay{0};\n  for (auto& timerList : timerLists) {\n    for (auto& timer : *timerList) {\n      timer.done.wait();\n      count += timer.count;\n      sumDelay += timer.sumDelay;\n      maxDelay = std::max(maxDelay, timer.maxDelay);\n    }\n  }\n\n  EXPECT_EQ(\n      count,\n      kNumExecutors * kNumThreads * kNumTimersPerEvb * kNumItersPerTimer);\n  LOG(INFO) << fmt::format(\n      \"Avg delay: {:.3f}ms, max delay: {}ms\",\n      static_cast<double>(sumDelay.count()) / count,\n      maxDelay.count());\n}\n\nREGISTER_TYPED_TEST_SUITE_P(\n    IOThreadPoolExecutorBaseTest,\n    IOObserver,\n    GetEventBaseFromEvb,\n    StressTimeouts);\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/test/IOThreadPoolExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/executors/test/IOThreadPoolExecutorBaseTestLib.h>\n\nnamespace folly {\nnamespace test {\n\nTEST(IOThreadPoolExecutor, MaxReadAtOnce) {\n  {\n    auto executor = IOThreadPoolExecutor{1};\n    EXPECT_EQ(executor.getEventBase()->getMaxReadAtOnce(), 10);\n  }\n\n  {\n    auto saver = FlagSaver{};\n    FLAGS_folly_iothreadpoolexecutor_max_read_at_once = 0;\n    auto executor = IOThreadPoolExecutor{1};\n    EXPECT_EQ(executor.getEventBase()->getMaxReadAtOnce(), 0);\n  }\n\n  {\n    auto saver = FlagSaver{};\n    FLAGS_folly_iothreadpoolexecutor_max_read_at_once = 0;\n    auto options = IOThreadPoolExecutor::Options{};\n    options.setMaxReadAtOnce(42);\n    auto executor = IOThreadPoolExecutor{\n        1,\n        std::make_shared<NamedThreadFactory>(\"IOThreadPool\"),\n        EventBaseManager::get(),\n        std::move(options)};\n\n    EXPECT_EQ(executor.getEventBase()->getMaxReadAtOnce(), 42);\n  }\n}\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    IOThreadPoolExecutorTest,\n    IOThreadPoolExecutorBaseTest,\n    IOThreadPoolExecutor);\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/test/MeteredExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <list>\n\n#include <folly/Benchmark.h>\n#include <folly/Synchronized.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/MeteredExecutor.h>\n#include <folly/init/Init.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/Latch.h>\n#include <folly/synchronization/LifoSem.h>\n#include <folly/system/HardwareConcurrency.h>\n#include <folly/test/DeterministicSchedule.h>\n\nusing namespace folly;\n\nclass MeteredExecutorTest : public testing::Test {\n protected:\n  template <template <typename> class Atom = std::atomic>\n  void createAdapter(\n      int numLevels,\n      std::unique_ptr<Executor> exc =\n          std::make_unique<CPUThreadPoolExecutor>(1)) {\n    executors_.resize(numLevels + 1);\n    executors_[0] = exc.get();\n    for (int i = 0; i < numLevels; i++) {\n      auto mlsa =\n          std::make_unique<detail::MeteredExecutorImpl<Atom>>(std::move(exc));\n      exc = std::move(mlsa);\n      executors_[i + 1] = exc.get();\n    }\n    owning_ = std::move(exc);\n  }\n\n  void add(Func f, uint8_t level = 0) { executors_[level]->add(std::move(f)); }\n\n  void join() {\n    folly::Baton baton;\n    executors_.back()->add([&] { baton.post(); });\n    baton.wait();\n  }\n\n  Executor::KeepAlive<> getKeepAlive(int level) { return executors_[level]; }\n\n protected:\n  std::shared_ptr<void> owning_;\n  std::vector<Executor*> executors_;\n};\n\nTEST_F(MeteredExecutorTest, SingleLevel) {\n  createAdapter(1);\n\n  folly::Baton baton;\n  add([&] { baton.wait(); });\n\n  int32_t v = 0;\n  add([&] { EXPECT_EQ(0, v++); });\n  // first lopri task executes in the scheduling order with hipri\n  // tasks, but all subsequent lopri tasks yield.\n  add([&] { EXPECT_EQ(1, v++); }, 1);\n  add([&] { EXPECT_EQ(4, v++); }, 1);\n  add([&] { EXPECT_EQ(5, v++); }, 1);\n  add([&] { EXPECT_EQ(6, v++); }, 1);\n  add([&] { EXPECT_EQ(2, v++); });\n  add([&] { EXPECT_EQ(3, v++); });\n  baton.post();\n\n  join();\n  EXPECT_EQ(v, 7);\n}\n\nTEST_F(MeteredExecutorTest, TwoLevels) {\n  int32_t v = 0;\n\n  createAdapter(2);\n\n  folly::Baton baton;\n  add([&] { baton.wait(); });\n  add([&] { EXPECT_EQ(0, v++); });\n  add([&] { EXPECT_EQ(1, v++); }, 1);\n  add([&] { EXPECT_EQ(4, v++); }, 1);\n  add([&] { EXPECT_EQ(5, v++); }, 1);\n  add([&] { EXPECT_EQ(6, v++); }, 1);\n  add([&] { EXPECT_EQ(7, v++); }, 1);\n  add([&] { EXPECT_EQ(8, v++); }, 2);\n  add([&] { EXPECT_EQ(13, v++); }, 2);\n  add([&] { EXPECT_EQ(14, v++); }, 2);\n  add([&] { EXPECT_EQ(15, v++); }, 2);\n  add([&] { EXPECT_EQ(16, v++); }, 2);\n  add([&] { EXPECT_EQ(9, v++); }, 1);\n  add([&] { EXPECT_EQ(10, v++); }, 1);\n  add([&] { EXPECT_EQ(11, v++); }, 1);\n  add([&] { EXPECT_EQ(12, v++); }, 1);\n  add([&] { EXPECT_EQ(2, v++); });\n  add([&] { EXPECT_EQ(3, v++); });\n  baton.post();\n\n  join();\n  EXPECT_EQ(v, 17);\n}\n\nTEST_F(MeteredExecutorTest, PreemptTest) {\n  int32_t v = 0;\n\n  createAdapter(1);\n\n  folly::Baton baton1, baton2, baton3;\n  add([&] { baton1.wait(); });\n\n  add([&] { EXPECT_EQ(0, v++); });\n  // first lopri task executes in the scheduling order with hipri\n  // tasks, but all subsequent lopri tasks yield.\n  add([&] { EXPECT_EQ(1, v++); }, 1);\n  add([&] { EXPECT_EQ(4, v++); }, 1);\n  add([&] { EXPECT_EQ(5, v++); }, 1);\n  add([&] { EXPECT_EQ(2, v++); });\n  add([&] { EXPECT_EQ(3, v++); });\n  add(\n      [&] {\n        EXPECT_EQ(6, v++);\n        baton2.post();\n        baton3.wait();\n      },\n      1);\n\n  // These P1 tasks should be run AFTER the 3 P0 tasks are added below. We\n  // should expect the P0 tasks to preempt these.\n  add([&] { EXPECT_EQ(7, v++); }, 1);\n  add([&] { EXPECT_EQ(11, v++); }, 1);\n  add([&] { EXPECT_EQ(12, v++); }, 1);\n  add([&] { EXPECT_EQ(13, v++); }, 1);\n\n  // Kick off the tasks, but we'll halt part-way through the P1 tasks to add\n  // some P0 tasks into the mix.\n  baton1.post();\n  baton2.wait();\n\n  // Throw in the high pris.\n  add([&] { EXPECT_EQ(8, v++); });\n  add([&] { EXPECT_EQ(9, v++); });\n  add([&] { EXPECT_EQ(10, v++); });\n\n  baton3.post();\n\n  join();\n  EXPECT_EQ(v, 14);\n}\n\nTEST_F(MeteredExecutorTest, TwoLevelsWithKeepAlives) {\n  auto hipri_exec = std::make_unique<CPUThreadPoolExecutor>(1);\n  auto hipri_ka = getKeepAliveToken(hipri_exec.get());\n  auto mipri_exec = std::make_unique<MeteredExecutor>(hipri_ka);\n  auto mipri_ka = getKeepAliveToken(mipri_exec.get());\n  auto lopri_exec = std::make_unique<MeteredExecutor>(mipri_ka);\n  executors_ = {hipri_exec.get(), mipri_exec.get(), lopri_exec.get()};\n\n  int32_t v = 0;\n  folly::Baton baton;\n  add([&] { baton.wait(); });\n  add([&] { EXPECT_EQ(0, v++); });\n  add([&] { EXPECT_EQ(1, v++); }, 1);\n  add([&] { EXPECT_EQ(4, v++); }, 1);\n  add([&] { EXPECT_EQ(5, v++); }, 1);\n  add([&] { EXPECT_EQ(6, v++); }, 1);\n  add([&] { EXPECT_EQ(7, v++); }, 1);\n  add([&] { EXPECT_EQ(8, v++); }, 2);\n  add([&] { EXPECT_EQ(13, v++); }, 2);\n  add([&] { EXPECT_EQ(14, v++); }, 2);\n  add([&] { EXPECT_EQ(15, v++); }, 2);\n  add([&] { EXPECT_EQ(16, v++); }, 2);\n  add([&] { EXPECT_EQ(9, v++); }, 1);\n  add([&] { EXPECT_EQ(10, v++); }, 1);\n  add([&] { EXPECT_EQ(11, v++); }, 1);\n  add([&] { EXPECT_EQ(12, v++); }, 1);\n  add([&] { EXPECT_EQ(2, v++); });\n  add([&] { EXPECT_EQ(3, v++); });\n  baton.post();\n\n  lopri_exec.reset();\n  EXPECT_EQ(v, 17);\n}\n\nTEST_F(MeteredExecutorTest, RequestContext) {\n  createAdapter(3);\n\n  folly::Baton baton;\n  add([&] { baton.wait(); });\n\n  auto addAndCheckRCTX = [this](int8_t pri = 0) {\n    RequestContextScopeGuard g;\n    auto f = [rctx = RequestContext::saveContext()]() mutable {\n      EXPECT_EQ(rctx.get(), RequestContext::get());\n\n      // Verify we do not have dangling reference to finished requests.\n      static std::shared_ptr<folly::RequestContext> lastFinished;\n      EXPECT_TRUE(!lastFinished || lastFinished.unique());\n      lastFinished = std::move(rctx);\n    };\n    add(std::move(f), pri);\n  };\n\n  addAndCheckRCTX();\n  addAndCheckRCTX(3);\n  addAndCheckRCTX(3);\n  addAndCheckRCTX(3);\n  addAndCheckRCTX(1);\n  addAndCheckRCTX(1);\n  addAndCheckRCTX(1);\n  addAndCheckRCTX(2);\n  addAndCheckRCTX(2);\n  addAndCheckRCTX(2);\n  baton.post();\n  join();\n}\n\nTEST_F(MeteredExecutorTest, ResetJoins) {\n  createAdapter(2);\n\n  folly::Baton baton;\n  add([&] { baton.wait(); });\n\n  int v = 0;\n  add([&] { ++v; });\n  add([&] { ++v; }, 2);\n  add([&] { ++v; }, 2);\n  add([&] { ++v; }, 1);\n  add([&] { ++v; }, 1);\n\n  baton.post();\n  owning_.reset();\n  EXPECT_EQ(v, 5);\n}\n\nTEST_F(MeteredExecutorTest, ConcurrentShutdown) {\n  // ensure no data races on shutdown when executor has 2 threads\n  createAdapter(2, std::make_unique<CPUThreadPoolExecutor>(2));\n}\n\nTEST_F(MeteredExecutorTest, CostOfMeteredExecutors) {\n  // This test is to demonstrate how many tasks are scheduled\n  // on the primarty executor when it is wrapped by MeteredExecutors\n  class MyExecutor : public folly::Executor {\n   public:\n    int count{0};\n    bool driveWhenAdded{false};\n    std::list<folly::Func> queue;\n    void add(folly::Func f) override {\n      ++count;\n      queue.push_back(std::move(f));\n      if (driveWhenAdded) {\n        drive();\n      }\n    }\n    void drive() {\n      while (!queue.empty()) {\n        auto ff = std::move(queue.front());\n        queue.pop_front();\n        ff();\n      }\n    }\n  };\n\n  auto exc = std::make_unique<MyExecutor>();\n  auto drive = [exc = exc.get()] { exc->drive(); };\n  auto getCount = [exc = exc.get()] { return std::exchange(exc->count, 0); };\n  auto driveOnAdd = [exc = exc.get()] { exc->driveWhenAdded = true; };\n  createAdapter(3, std::move(exc));\n\n  // Whether or not the queues are empty, we schedule exactly one task on the\n  // primary executor for each schedule task, regardless of the chain depth.\n  add([&] {}, 0);\n  drive();\n  EXPECT_EQ(1, getCount());\n\n  add([&] {}, 1);\n  drive();\n  EXPECT_EQ(1, getCount());\n\n  add([&] {}, 2);\n  drive();\n  EXPECT_EQ(1, getCount());\n\n  add([&] {}, 3);\n  drive();\n  EXPECT_EQ(1, getCount());\n\n  add([&] {}, 3);\n  drive();\n  EXPECT_EQ(1, getCount());\n\n  add([&] {}, 3);\n  add([&] {}, 3);\n  add([&] {}, 3);\n  drive();\n  EXPECT_EQ(3, getCount());\n\n  // To allow shutting down properly\n  driveOnAdd();\n}\n\nTEST_F(MeteredExecutorTest, ExceptionHandling) {\n  createAdapter(2);\n\n  folly::Baton baton;\n  add([&] {\n    baton.wait();\n    throw std::runtime_error(\"dummy\");\n  });\n\n  bool thrown = false;\n  add(\n      [&] {\n        thrown = true;\n        throw std::runtime_error(\"dummy\");\n      },\n      1);\n  baton.post();\n  join();\n  EXPECT_EQ(true, thrown);\n}\n\nnamespace {\n\ncoro::Task<bool> co_isOnCPUExc() {\n  auto executor = co_await coro::co_current_executor;\n  auto cpuexec = dynamic_cast<CPUThreadPoolExecutor*>(executor);\n  co_return cpuexec != nullptr;\n}\n\ntemplate <typename T>\ncoro::Task<bool> co_run(Executor::KeepAlive<> ka, coro::Task<T> f) {\n  auto cpuexec = dynamic_cast<CPUThreadPoolExecutor*>(ka.get());\n  EXPECT_TRUE(cpuexec != nullptr);\n  co_return co_await co_withExecutor(cpuexec, std::move(f));\n}\n\n} // namespace\n\nTEST_F(MeteredExecutorTest, UnderlyingExecutor) {\n  createAdapter(1);\n  EXPECT_FALSE(\n      coro::blockingWait(co_withExecutor(getKeepAlive(1), co_isOnCPUExc())));\n  EXPECT_TRUE(\n      coro::blockingWait(co_withExecutor(\n          getKeepAlive(0), co_run(getKeepAlive(0), co_isOnCPUExc()))));\n}\n\nTEST_F(MeteredExecutorTest, PauseResume) {\n  createAdapter(1, std::make_unique<ManualExecutor>());\n  ManualExecutor* manual = dynamic_cast<ManualExecutor*>(getKeepAlive(0).get());\n  MeteredExecutor* metered =\n      dynamic_cast<MeteredExecutor*>(getKeepAlive(1).get());\n\n  size_t executed = 0;\n  add([&] { executed++; }, 1);\n  add([&] { executed++; }, 1);\n  manual->run();\n  ASSERT_EQ(1, executed);\n  ASSERT_EQ(1, metered->pendingTasks());\n\n  ASSERT_TRUE(metered->pause());\n  ASSERT_FALSE(metered->pause()); // pausing a paused executor\n  add([&] { executed++; }, 1);\n  manual->drain();\n  ASSERT_EQ(1, executed);\n  ASSERT_EQ(2, metered->pendingTasks());\n\n  ASSERT_TRUE(metered->resume());\n  ASSERT_FALSE(metered->resume()); // resuming a non-paused executor\n  manual->drain();\n  ASSERT_EQ(3, executed);\n  ASSERT_EQ(0, metered->pendingTasks());\n}\n\nTEST_F(MeteredExecutorTest, PauseResumeStress) {\n  using DSched = folly::test::DeterministicSchedule;\n  DSched sched(DSched::uniform(0));\n\n  class DeterministicExecutor : public folly::Executor {\n   public:\n    explicit DeterministicExecutor(int threads) {\n      for (int i = 0; i < threads; i++) {\n        threads_.push_back(DSched::thread([this]() { loop(); }));\n      }\n    }\n    void join() {\n      sem_.shutdown();\n      DSched::joinAll(threads_);\n      threads_.clear();\n    }\n    void add(folly::Func f) override {\n      tasks_.lock()->push(std::move(f));\n      sem_.post();\n    }\n\n   private:\n    void loop() {\n      while (true) {\n        try {\n          sem_.wait();\n          auto fn = tasks_.withLock([](auto&& tasks) {\n            if (tasks.empty()) {\n              return folly::Func{};\n            }\n            auto ret = std::move(tasks.front());\n            tasks.pop();\n            return ret;\n          });\n          if (fn) {\n            fn();\n          }\n        } catch (const ShutdownSemError&) {\n          break;\n        } catch (const std::exception& e) {\n          LOG(ERROR) << \"Failed: \" << e.what();\n        }\n      }\n    }\n\n    LifoSemImpl<test::DeterministicAtomic> sem_;\n    folly::Synchronized<std::queue<folly::Func>, test::DeterministicMutex>\n        tasks_;\n    std::vector<std::thread> threads_;\n  };\n\n  createAdapter<test::DeterministicAtomic>(\n      1, std::make_unique<DeterministicExecutor>(5));\n\n  auto exec = getKeepAlive(1);\n  auto metered =\n      dynamic_cast<detail::MeteredExecutorImpl<test::DeterministicAtomic>*>(\n          exec.get());\n  auto dexec = dynamic_cast<DeterministicExecutor*>(getKeepAlive(0).get());\n\n  const auto numElems = 150;\n  std::atomic<uint64_t> executed{0};\n  std::atomic<uint64_t> active{0};\n  for (int pass = 0; pass < 10; pass++) {\n    executed = 0;\n    active = 0;\n    const auto numProducers = 2;\n    std::vector<std::thread> producers;\n    for (int p = 0; p < numProducers; p++) {\n      producers.push_back(DSched::thread([&]() {\n        for (int i = 0; i < numElems; i++) {\n          exec->add([&]() {\n            executed++;\n            bool paused = false;\n            if (active.fetch_add(1) == 3) {\n              metered->pause();\n              paused = true;\n            }\n            if (paused) {\n              active.fetch_sub(1);\n              metered->resume();\n            }\n          });\n        }\n      }));\n    }\n    DSched::joinAll(producers);\n    folly::Baton<true, test::DeterministicAtomic> b;\n    exec->add([&]() { b.post(); });\n    b.wait();\n    EXPECT_EQ(numElems * numProducers, executed);\n  }\n\n  dexec->join();\n}\n\nnamespace {\n\n// Simulate saturation regime (queue almost always non-empty) on a\n// high-concurrency executor to measure to what extent MeteredExecutor\n// artificially limits concurrency in a situation where it is the only producer\n// in the executor (so it should ideally not impose any overhead).\nvoid benchmarkSaturation(\n    size_t meteredExecutorChainDepth, uint32_t maxInQueue, size_t iters) {\n  const size_t numThreads = folly::available_concurrency();\n\n  BenchmarkSuspender suspender;\n\n  CPUThreadPoolExecutor producer(\n      std::make_pair(numThreads, numThreads),\n      CPUThreadPoolExecutor::makeThrottledLifoSemQueue());\n  CPUThreadPoolExecutor consumerParent(\n      std::make_pair(numThreads, numThreads),\n      CPUThreadPoolExecutor::makeThrottledLifoSemQueue());\n\n  Executor* consumer = &consumerParent;\n  std::unique_ptr<MeteredExecutor> lastMeteredExecutor;\n  for (size_t i = 0; i < meteredExecutorChainDepth; ++i) {\n    MeteredExecutor::Options options;\n    options.maxInQueue = maxInQueue;\n    if (lastMeteredExecutor == nullptr) {\n      lastMeteredExecutor = std::make_unique<MeteredExecutor>(\n          &consumerParent, std::move(options));\n    } else {\n      lastMeteredExecutor = std::make_unique<MeteredExecutor>(\n          std::move(lastMeteredExecutor), std::move(options));\n    }\n    consumer = lastMeteredExecutor.get();\n  }\n\n  suspender.dismiss();\n\n  Latch done{static_cast<ptrdiff_t>(iters * numThreads)};\n\n  for (size_t t = 0; t < numThreads; ++t) {\n    producer.add([&] {\n      for (size_t i = 0; i < iters; ++i) {\n        consumer->add([&] { done.count_down(); });\n      }\n    });\n  }\n\n  done.wait();\n  suspender.rehire(); // Do not measure destruction.\n}\n\n} // namespace\n\nBENCHMARK(Saturation_no_MeteredExecutor, iters) {\n  benchmarkSaturation(0, /* unused */ 0, iters);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Saturation_MeteredExecutor_chain_1_maxInQueue_1, iters) {\n  benchmarkSaturation(1, 1, iters);\n}\nBENCHMARK(Saturation_MeteredExecutor_chain_1_maxInQueue_2, iters) {\n  benchmarkSaturation(1, 2, iters);\n}\nBENCHMARK(Saturation_MeteredExecutor_chain_1_maxInQueue_4, iters) {\n  benchmarkSaturation(1, 4, iters);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Saturation_MeteredExecutor_chain_2_maxInQueue_1, iters) {\n  benchmarkSaturation(2, 1, iters);\n}\nBENCHMARK(Saturation_MeteredExecutor_chain_2_maxInQueue_2, iters) {\n  benchmarkSaturation(2, 2, iters);\n}\nBENCHMARK(Saturation_MeteredExecutor_chain_2_maxInQueue_4, iters) {\n  benchmarkSaturation(2, 4, iters);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Saturation_MeteredExecutor_chain_3_maxInQueue_1, iters) {\n  benchmarkSaturation(3, 1, iters);\n}\nBENCHMARK(Saturation_MeteredExecutor_chain_3_maxInQueue_2, iters) {\n  benchmarkSaturation(3, 2, iters);\n}\nBENCHMARK(Saturation_MeteredExecutor_chain_3_maxInQueue_4, iters) {\n  benchmarkSaturation(3, 4, iters);\n}\n\nint main(int argc, char** argv) {\n  // Enable glog logging to stderr by default.\n  FLAGS_logtostderr = true;\n  ::testing::InitGoogleTest(&argc, argv);\n  folly::Init init(&argc, &argv);\n  folly::runBenchmarksOnFlag();\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "folly/executors/test/SequencedExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <future>\n\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nbool isSequencedExecutor(folly::Executor& executor) {\n  // Add can be called from different threads, but it should be sequenced.\n  auto cpuExecutor = std::make_shared<CPUThreadPoolExecutor>(4);\n  auto producer =\n      SerialExecutor::create(Executor::getKeepAliveToken(cpuExecutor.get()));\n\n  std::atomic<size_t> nextCallIndex{0};\n  std::atomic<bool> result{true};\n\n  auto joinPromise = std::make_shared<std::promise<void>>();\n  auto joinFuture = joinPromise->get_future();\n\n  constexpr size_t kNumCalls = 10000;\n  for (size_t callIndex = 0; callIndex < kNumCalls; ++callIndex) {\n    producer->add([&result, &executor, &nextCallIndex, callIndex, joinPromise] {\n      executor.add([&result, &nextCallIndex, callIndex, joinPromise] {\n        if (nextCallIndex != callIndex) {\n          result = false;\n        }\n        std::this_thread::yield();\n        if (nextCallIndex.exchange(callIndex + 1) != callIndex) {\n          result = false;\n        }\n      });\n    });\n  }\n\n  joinPromise.reset();\n  joinFuture.wait();\n\n  return result;\n}\n\nvoid testExecutor(folly::Executor& executor) {\n  EXPECT_FALSE(isSequencedExecutor(executor));\n}\n\nvoid testExecutor(folly::SequencedExecutor& executor) {\n  EXPECT_TRUE(isSequencedExecutor(executor));\n}\n\nTEST(SequencedExecutor, CPUThreadPoolExecutor) {\n  CPUThreadPoolExecutor executor(4);\n  testExecutor(executor);\n}\n\nTEST(SequencedExecutor, SerialCPUThreadPoolExecutor) {\n  auto cpuExecutor = std::make_shared<CPUThreadPoolExecutor>(4);\n  auto executor =\n      SerialExecutor::create(Executor::getKeepAliveToken(cpuExecutor.get()));\n  testExecutor(*executor);\n}\n\nTEST(SequencedExecutor, EventBase) {\n  testExecutor(*ScopedEventBaseThread().getEventBase());\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/test/SerialExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/SerialExecutor.h>\n\n#include <chrono>\n#include <optional>\n\n#include <folly/Random.h>\n#include <folly/ScopeGuard.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/io/async/Request.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nnamespace {\n\nvoid sleepMs(uint64_t ms) {\n  /* sleep override */ std::this_thread::sleep_for(\n      std::chrono::milliseconds(ms));\n}\n\n} // namespace\n\ntemplate <typename T>\nclass SerialExecutorTest : public testing::Test {};\n\nusing SerialExecutorTypes = ::testing::Types<folly::SerialExecutor>;\nTYPED_TEST_SUITE(SerialExecutorTest, SerialExecutorTypes);\n\nTYPED_TEST(SerialExecutorTest, Accessors) {\n  folly::CPUThreadPoolExecutor parent{1};\n  auto executor = TypeParam::create(&parent);\n  EXPECT_EQ(&parent, executor->parent().get());\n}\n\ntemplate <class SerialExecutorType>\nvoid simpleTest(folly::Executor& parent) {\n  class SerialExecutorContextData : public folly::RequestData {\n   public:\n    static std::string kCtxKey() {\n      return typeid(SerialExecutorContextData).name();\n    }\n    explicit SerialExecutorContextData(int id) : id_(id) {}\n    bool hasCallback() override { return false; }\n    int getId() const { return id_; }\n\n   private:\n    const int id_;\n  };\n\n  auto executor = SerialExecutorType::create(&parent);\n\n  std::vector<int> values;\n  std::vector<int> expected;\n\n  for (int i = 0; i < 20; ++i) {\n    folly::RequestContextScopeGuard ctxGuard;\n    folly::RequestContext::try_get()->setContextData(\n        SerialExecutorContextData::kCtxKey(),\n        std::make_unique<SerialExecutorContextData>(i));\n    auto checkReqCtx = [i] {\n      EXPECT_EQ(\n          i,\n          dynamic_cast<SerialExecutorContextData*>(\n              folly::RequestContext::get()->getContextData(\n                  SerialExecutorContextData::kCtxKey()))\n              ->getId());\n    };\n    executor->add([i, checkReqCtx, g = folly::makeGuard(checkReqCtx), &values] {\n      checkReqCtx();\n      // make this extra vulnerable to concurrent execution\n      values.push_back(0);\n      sleepMs(10);\n      values.back() = i;\n    });\n    expected.push_back(i);\n  }\n\n  // wait until last task has executed\n  folly::Baton<> finished_baton;\n  executor->add([&finished_baton] { finished_baton.post(); });\n  finished_baton.wait();\n\n  EXPECT_EQ(expected, values);\n}\n\nTYPED_TEST(SerialExecutorTest, Simple) {\n  folly::CPUThreadPoolExecutor parent{4};\n  simpleTest<TypeParam>(parent);\n}\nTYPED_TEST(SerialExecutorTest, SimpleInline) {\n  simpleTest<TypeParam>(folly::InlineExecutor::instance());\n}\n\n// The Afterlife test only works with an asynchronous executor (not the\n// InlineExecutor), because we want execution of tasks to happen after we\n// destroy the SerialExecutor\nTYPED_TEST(SerialExecutorTest, Afterlife) {\n  folly::CPUThreadPoolExecutor parent{4};\n  auto executor = TypeParam::create(&parent);\n\n  // block executor until we call start_baton.post()\n  folly::Baton<> start_baton;\n  executor->add([&start_baton] { start_baton.wait(); });\n\n  std::vector<int> values;\n  std::vector<int> expected;\n\n  for (int i = 0; i < 20; ++i) {\n    executor->add([i, &values] {\n      // make this extra vulnerable to concurrent execution\n      values.push_back(0);\n      sleepMs(10);\n      values.back() = i;\n    });\n    expected.push_back(i);\n  }\n\n  folly::Baton<> finished_baton;\n  executor->add([&finished_baton] { finished_baton.post(); });\n\n  // destroy SerialExecutor\n  executor.reset();\n\n  // now kick off the tasks\n  start_baton.post();\n\n  // wait until last task has executed\n  finished_baton.wait();\n\n  EXPECT_EQ(expected, values);\n}\n\ntemplate <class SerialExecutorType>\nvoid recursiveAddTest(folly::Executor& parent) {\n  auto executor = SerialExecutorType::create(&parent);\n\n  folly::Baton<> finished_baton;\n\n  std::vector<int> values;\n  std::vector<int> expected = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};\n\n  int i = 0;\n  std::function<void()> lambda = [&] {\n    if (i < 10) {\n      // make this extra vulnerable to concurrent execution\n      values.push_back(0);\n      sleepMs(10);\n      values.back() = i;\n      executor->add(lambda);\n    } else if (i < 12) {\n      // Below we will post this lambda three times to the executor. When\n      // executed, the lambda will re-post itself during the first ten\n      // executions. Afterwards we do nothing twice (this else-branch), and\n      // then on the 13th execution signal that we are finished.\n    } else {\n      finished_baton.post();\n    }\n    ++i;\n  };\n\n  executor->add(lambda);\n  executor->add(lambda);\n  executor->add(lambda);\n\n  // wait until last task has executed\n  finished_baton.wait();\n\n  EXPECT_EQ(expected, values);\n}\n\nTYPED_TEST(SerialExecutorTest, RecursiveAdd) {\n  folly::CPUThreadPoolExecutor parent{4};\n  recursiveAddTest<TypeParam>(parent);\n}\nTYPED_TEST(SerialExecutorTest, RecursiveAddInline) {\n  recursiveAddTest<TypeParam>(folly::InlineExecutor::instance());\n}\n\nTYPED_TEST(SerialExecutorTest, ExecutionThrows) {\n  auto executor = TypeParam::create();\n\n  // an empty Func will throw std::bad_function_call when invoked,\n  // but SerialExecutor should catch that exception\n  executor->add(folly::Func{});\n}\n\nTYPED_TEST(SerialExecutorTest, ParentExecutorDiscardsFunc) {\n  struct FakeExecutor : folly::Executor {\n    void add(folly::Func f) override { queue.push_back(std::move(f)); }\n\n    std::vector<folly::Func> queue;\n  };\n\n  std::optional<FakeExecutor> ex{std::in_place};\n  auto se = TypeParam::create(&*ex);\n  bool ran = false;\n  bool destructorRan = false;\n  {\n    folly::RequestContextScopeGuard ctxGuard;\n    auto destructionGuard = folly::makeGuard(\n        [&destructorRan, ctx = folly::RequestContext::try_get()] {\n          destructorRan = true;\n          EXPECT_EQ(ctx, folly::RequestContext::try_get());\n        });\n    se->add([&,\n             ka = folly::getKeepAliveToken(se.get()),\n             dg = std::move(destructionGuard)] { ran = true; });\n    EXPECT_FALSE(destructorRan); // Task is still stuck in the queue.\n  }\n  se.reset();\n  ex.reset();\n  EXPECT_TRUE(destructorRan);\n  ASSERT_FALSE(ran);\n}\n\nTYPED_TEST(SerialExecutorTest, Stress) {\n  folly::CPUThreadPoolExecutor parent{4};\n  auto se = TypeParam::create(&parent);\n\n  size_t tasksRan = 0;\n  constexpr size_t kNumProducers = 16;\n  static constexpr size_t kNumIterations = 4096;\n  folly::CPUThreadPoolExecutor producers{kNumProducers};\n  for (size_t i = 0; i < kNumProducers; ++i) {\n    producers.add([i, se, &tasksRan] {\n      for (size_t j = 0; j < kNumIterations; ++j) {\n        se->add([i, j, se, &tasksRan] {\n          if (i == j) {\n            // Do a few recursive adds just in case.\n            se->add([&tasksRan] { ++tasksRan; });\n          }\n          ++tasksRan;\n        });\n      }\n    });\n  }\n\n  producers.join();\n  se = {};\n  parent.join();\n  EXPECT_EQ(tasksRan, kNumProducers * (kNumIterations + 1));\n}\n\n// Basic test for SerialExecutorMPSCQueue, does not exercise concurrent access\n// but just ensure that the state stays consistent under different\n// enqueue/dequeue patterns.\nTEST(SerialExecutorTest2, SerialExecutorMPSCQueue) {\n  folly::detail::SerialExecutorMPSCQueue<std::unique_ptr<size_t>> q;\n  size_t size = 0;\n\n  auto produce = [&q, &size, nextWrite = 0](size_t n) mutable {\n    for (size_t i = 0; i < n; ++i) {\n      q.enqueue(std::make_unique<size_t>(nextWrite++));\n    }\n    size += n;\n  };\n\n  auto consume = [&q, &size, nextRead = 0](size_t n) mutable {\n    for (size_t j = 0; j < n; ++j) {\n      std::unique_ptr<size_t> entry;\n      q.dequeue(entry);\n      EXPECT_EQ(*entry, nextRead++);\n    }\n    size -= n;\n  };\n\n  for (size_t round = 0; round < 1024; ++round) {\n    size_t toEnqueue = folly::Random::rand64(128) + 1;\n    produce(toEnqueue);\n\n    // Consume completely with 20% probability, otherwise leave several entries\n    // (possibly multiple segments).\n    size_t toDequeue = folly::Random::oneIn(5)\n        ? size\n        : size - folly::Random::rand64(std::min<size_t>(size, 128));\n    consume(toDequeue);\n  }\n  consume(size);\n}\n\nTEST(SerialExecutorTest2, SPSerialExecutor) {\n  folly::CPUThreadPoolExecutor parent{1};\n  auto se = folly::SPSerialExecutor::create(&parent);\n  constexpr size_t kNumTasks = 100;\n  size_t numRan = 0;\n  folly::Baton<> done;\n  for (size_t i = 0; i < kNumTasks; ++i) {\n    se->add([&] { ++numRan; });\n  }\n  se->add([&] { done.post(); });\n  done.wait();\n  EXPECT_EQ(numRan, kNumTasks);\n}\n"
  },
  {
    "path": "folly/executors/test/StrandExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/StrandExecutor.h>\n\n#include <atomic>\n#include <chrono>\n#include <thread>\n#include <vector>\n\n#include <folly/CancellationToken.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/io/async/Request.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\nusing namespace std::chrono_literals;\n\nnamespace {\ntemplate <typename Duration>\nvoid burnTime(Duration d) {\n  /* sleep override */ std::this_thread::sleep_for(d);\n}\n} // namespace\n\nTEST(StrandExecutor, SimpleTest) {\n  auto exec = StrandExecutor::create();\n\n  // Checks that tasks are serialised (ie. that we don't corrupt the vector)\n  // and that task are processed in-order.\n  std::vector<int> v;\n  for (int i = 0; i < 20; ++i) {\n    exec->add([&, i] {\n      v.emplace_back();\n      burnTime(1ms);\n      v.back() = i;\n    });\n  }\n\n  folly::Baton baton;\n  exec->add([&] { baton.post(); });\n  baton.wait();\n\n  CHECK_EQ(20, v.size());\n  for (int i = 0; i < 20; ++i) {\n    CHECK_EQ(i, v[i]);\n  }\n}\n\nTEST(StrandExecutor, ThreadSafetyTest) {\n  auto strandContext = StrandContext::create();\n\n  ManualExecutor ex1;\n  ManualExecutor ex2;\n\n  CancellationSource cancelSrc;\n\n  auto runUntilStopped = [&](ManualExecutor& ex) {\n    CancellationCallback cb(cancelSrc.getToken(), [&]() noexcept {\n      ex.add([] {});\n    });\n    while (!cancelSrc.isCancellationRequested()) {\n      ex.makeProgress();\n    }\n  };\n\n  std::thread t1{[&] { runUntilStopped(ex1); }};\n  std::thread t2{[&] { runUntilStopped(ex2); }};\n\n  int value = 0;\n\n  auto incrementValue = [&]() noexcept { ++value; };\n\n  auto strandEx1 =\n      StrandExecutor::create(strandContext, getKeepAliveToken(ex1));\n  auto strandEx2 =\n      StrandExecutor::create(strandContext, getKeepAliveToken(ex2));\n\n  auto submitSomeTasks = [&]() {\n    for (int i = 0; i < 10'000; ++i) {\n      strandEx1->add(incrementValue);\n      strandEx2->add(incrementValue);\n    }\n  };\n\n  std::thread submitter1{submitSomeTasks};\n  std::thread submitter2{submitSomeTasks};\n\n  submitter1.join();\n  submitter2.join();\n\n  folly::Baton b1;\n  folly::Baton b2;\n  strandEx1->add([&] { b1.post(); });\n  strandEx2->add([&] { b2.post(); });\n\n  b1.wait();\n  b2.wait();\n\n  CHECK_EQ(40'000, value);\n\n  cancelSrc.requestCancellation();\n\n  t1.join();\n  t2.join();\n}\n\nTEST(StrandExecutor, RequestContextPropagation) {\n  auto exec = StrandExecutor::create();\n  // Use a number larger than maxItemsToProcessSynchronously so we exercise\n  // worker reschedules.\n  constexpr size_t kNumTasks = 128;\n\n  size_t numTasksRan = 0;\n  for (size_t i = 0; i < kNumTasks; ++i) {\n    // Create a unique RequestContext for each task and verify that it is\n    // propagated correctly.\n    RequestContextScopeGuard ctxGuard;\n    auto f = [&, ctx = RequestContext::try_get()] {\n      EXPECT_EQ(ctx, RequestContext::try_get());\n      // Spend enough time that it is very likely that the queue is never empty.\n      burnTime(100us);\n      ++numTasksRan;\n    };\n    if (i % 2 == 0) {\n      exec->add(std::move(f));\n    } else {\n      exec->addWithPriority(std::move(f), -1);\n    }\n  }\n\n  folly::Baton baton;\n  exec->add([&] { baton.post(); });\n  baton.wait();\n\n  EXPECT_EQ(numTasksRan, kNumTasks);\n}\n"
  },
  {
    "path": "folly/executors/test/StripedEDFThreadPoolExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/StripedEDFThreadPoolExecutor.h>\n\n#include <algorithm>\n#include <vector>\n\n#include <folly/Random.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Sched.h>\n#include <folly/portability/Unistd.h>\n\nnamespace {\n\nvoid pinProcessToCurrentCPU() {\n  cpu_set_t set;\n  CPU_ZERO(&set);\n  auto cpu = sched_getcpu();\n  ASSERT_GE(cpu, 0);\n  CPU_SET(cpu, &set);\n  auto ret = sched_setaffinity(getpid(), sizeof(set), &set);\n  ASSERT_EQ(ret, 0);\n}\n\n} // namespace\n\n// StripedEDFThreadPoolExecutor is a CPUThreadPoolExecutor, so all the thread\n// pool functionality is covered by its test. We just need to verify the\n// ordering by deadline, which is only guaranteed for tasks submitted from the\n// same LLC, so we pin the process to a processor and verify the order in a SPSC\n// scenario.\nTEST(StripedEDFThreadPoolExecutor, Basic) {\n  // Start with an empty pool, we'll start a single thread after all the tasks\n  // have been submitted.\n  folly::StripedEDFThreadPoolExecutor executor(std::pair<size_t, size_t>{0, 0});\n\n  pinProcessToCurrentCPU();\n  std::vector<uint64_t> order;\n  constexpr size_t kNumTasks = 100;\n  for (size_t i = 0; i < kNumTasks; ++i) {\n    auto deadline = folly::Random::rand64();\n    executor.add([deadline, &order] { order.push_back(deadline); }, deadline);\n  }\n\n  ASSERT_EQ(executor.numThreads(), 0);\n  ASSERT_EQ(order.size(), 0);\n  executor.setNumThreads(1);\n\n  executor.join();\n  EXPECT_EQ(order.size(), kNumTasks);\n  EXPECT_TRUE(std::is_sorted(order.begin(), order.end()));\n}\n\nTEST(StripedEDFThreadPoolExecutor, Stop) {\n  folly::StripedEDFThreadPoolExecutor executor(std::pair<size_t, size_t>{0, 0});\n  executor.add([] {}, 10);\n  // There are no threads, the task will be dropped.\n  executor.stop();\n}\n"
  },
  {
    "path": "folly/executors/test/ThreadPoolExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n#include <memory>\n#include <thread>\n\n#include <boost/thread.hpp>\n#include <folly/CPortability.h>\n#include <folly/DefaultKeepAliveExecutor.h>\n#include <folly/Exception.h>\n#include <folly/container/F14Map.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/EDFThreadPoolExecutor.h>\n#include <folly/executors/FutureExecutor.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/executors/ThreadPoolExecutor.h>\n#include <folly/executors/VirtualExecutor.h>\n#include <folly/executors/task_queue/LifoSemMPMCQueue.h>\n#include <folly/executors/task_queue/UnboundedBlockingQueue.h>\n#include <folly/executors/thread_factory/InitThreadFactory.h>\n#include <folly/executors/thread_factory/PriorityThreadFactory.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/PThread.h>\n#include <folly/portability/SysResource.h>\n#include <folly/synchronization/Latch.h>\n#include <folly/synchronization/detail/Spin.h>\n#include <folly/system/ThreadId.h>\n\nusing namespace folly;\nusing namespace std::chrono;\n\n// Like ASSERT_NEAR, for chrono duration types\n#define ASSERT_NEAR_NS(a, b, c)  \\\n  do {                           \\\n    ASSERT_NEAR(                 \\\n        nanoseconds(a).count(),  \\\n        nanoseconds(b).count(),  \\\n        nanoseconds(c).count()); \\\n  } while (0)\n\nstatic Func burnMs(uint64_t ms) {\n  return [ms]() { std::this_thread::sleep_for(milliseconds(ms)); };\n}\n\n#ifdef __linux__\nstatic std::chrono::nanoseconds thread_clock_now() {\n  timespec tp;\n  clockid_t clockid;\n  CHECK(!pthread_getcpuclockid(pthread_self(), &clockid));\n  CHECK(!clock_gettime(clockid, &tp));\n  return std::chrono::nanoseconds(tp.tv_nsec) + std::chrono::seconds(tp.tv_sec);\n}\n\n// Loop and burn cpu cycles\nstatic void burnThreadCpu(milliseconds ms) {\n  auto expires = thread_clock_now() + ms;\n  while (thread_clock_now() < expires) {\n  }\n}\n\n// Loop without using much cpu time\nstatic void idleLoopFor(milliseconds ms) {\n  using clock = high_resolution_clock;\n  auto expires = clock::now() + ms;\n  while (clock::now() < expires) {\n    /* sleep override */ std::this_thread::sleep_for(100ms);\n  }\n}\n#endif\n\nstatic WorkerProvider* kWorkerProviderGlobal = nullptr;\n\nnamespace folly {\n\n#if FOLLY_HAVE_WEAK_SYMBOLS\nFOLLY_KEEP std::unique_ptr<QueueObserverFactory> make_queue_observer_factory(\n    const std::string&, size_t, WorkerProvider* workerProvider) {\n  kWorkerProviderGlobal = workerProvider;\n  return {};\n}\n#endif\n\n} // namespace folly\n\ntemplate <typename T>\nclass ThreadPoolExecutorTypedTest : public ::testing::Test {};\n\nusing ValueTypes = ::testing::\n    Types<CPUThreadPoolExecutor, IOThreadPoolExecutor, EDFThreadPoolExecutor>;\n\nTYPED_TEST_SUITE(ThreadPoolExecutorTypedTest, ValueTypes);\n\ntemplate <class TPE>\nstatic void basic() {\n  // Create and destroy\n  TPE tpe(10);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, Basic) {\n  basic<TypeParam>();\n}\n\ntemplate <class TPE>\nstatic void resize() {\n  TPE tpe(100);\n  EXPECT_EQ(100, tpe.numThreads());\n  tpe.setNumThreads(50);\n  EXPECT_EQ(50, tpe.numThreads());\n  tpe.setNumThreads(150);\n  EXPECT_EQ(150, tpe.numThreads());\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, Resize) {\n  resize<TypeParam>();\n}\n\ntemplate <class TPE>\nstatic void stop() {\n  TPE tpe(1);\n  std::atomic<int> completed(0);\n  auto f = [&]() {\n    burnMs(10)();\n    completed++;\n  };\n  for (int i = 0; i < 1000; i++) {\n    tpe.add(f);\n  }\n  tpe.stop();\n  EXPECT_GT(1000, completed);\n}\n\n// IOThreadPoolExecutor's stop() behaves like join(). Outstanding tasks belong\n// to the event base, will be executed upon its destruction, and cannot be\n// taken back.\ntemplate <>\nvoid stop<IOThreadPoolExecutor>() {\n  IOThreadPoolExecutor tpe(1);\n  std::atomic<int> completed(0);\n  auto f = [&]() {\n    burnMs(10)();\n    completed++;\n  };\n  for (int i = 0; i < 10; i++) {\n    tpe.add(f);\n  }\n  tpe.stop();\n  EXPECT_EQ(10, completed);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, Stop) {\n  stop<TypeParam>();\n}\n\ntemplate <class TPE>\nstatic void join() {\n  TPE tpe(10);\n  std::atomic<int> completed(0);\n  auto f = [&]() {\n    burnMs(1)();\n    completed++;\n  };\n  for (int i = 0; i < 1000; i++) {\n    tpe.add(f);\n  }\n  tpe.join();\n  EXPECT_EQ(1000, completed);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, Join) {\n  join<TypeParam>();\n}\n\ntemplate <class TPE>\nstatic void destroy() {\n  TPE tpe(1);\n  std::atomic<int> completed(0);\n  auto f = [&]() {\n    burnMs(10)();\n    completed++;\n  };\n  for (int i = 0; i < 1000; i++) {\n    tpe.add(f);\n  }\n  tpe.stop();\n  EXPECT_GT(1000, completed);\n}\n\n// IOThreadPoolExecutor's destuctor joins all tasks. Outstanding tasks belong\n// to the event base, will be executed upon its destruction, and cannot be\n// taken back.\ntemplate <>\nvoid destroy<IOThreadPoolExecutor>() {\n  Optional<IOThreadPoolExecutor> tpe(std::in_place, 1);\n  std::atomic<int> completed(0);\n  auto f = [&]() {\n    burnMs(10)();\n    completed++;\n  };\n  for (int i = 0; i < 10; i++) {\n    tpe->add(f);\n  }\n  tpe.reset();\n  EXPECT_EQ(10, completed);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, Destroy) {\n  destroy<TypeParam>();\n}\n\ntemplate <class TPE>\nstatic void resizeUnderLoad() {\n  TPE tpe(10);\n  std::atomic<int> completed(0);\n  auto f = [&]() {\n    burnMs(1)();\n    completed++;\n  };\n  for (int i = 0; i < 1000; i++) {\n    tpe.add(f);\n  }\n  tpe.setNumThreads(5);\n  tpe.setNumThreads(15);\n  tpe.join();\n  EXPECT_EQ(1000, completed);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, ResizeUnderLoad) {\n  resizeUnderLoad<TypeParam>();\n}\n\ntemplate <class TPE>\nstatic void poolStats() {\n  folly::Baton<> startBaton, endBaton;\n  TPE tpe(1);\n  auto stats = tpe.getPoolStats();\n  EXPECT_GE(1, stats.threadCount);\n  EXPECT_GE(1, stats.idleThreadCount);\n  EXPECT_EQ(0, stats.activeThreadCount);\n  EXPECT_EQ(0, stats.pendingTaskCount);\n  EXPECT_EQ(0, tpe.getPendingTaskCount());\n  EXPECT_EQ(0, stats.totalTaskCount);\n  tpe.add([&]() {\n    startBaton.post();\n    endBaton.wait();\n  });\n  tpe.add([&]() {});\n  startBaton.wait();\n  stats = tpe.getPoolStats();\n  EXPECT_EQ(1, stats.threadCount);\n  EXPECT_EQ(0, stats.idleThreadCount);\n  EXPECT_EQ(1, stats.activeThreadCount);\n  EXPECT_EQ(1, stats.pendingTaskCount);\n  EXPECT_EQ(1, tpe.getPendingTaskCount());\n  EXPECT_EQ(2, stats.totalTaskCount);\n  endBaton.post();\n}\n\nTEST(ThreadPoolExecutorTest, CPUPoolStats) {\n  poolStats<CPUThreadPoolExecutor>();\n}\n\nTEST(ThreadPoolExecutorTest, EDFPoolStats) {\n  poolStats<EDFThreadPoolExecutor>();\n}\n\nTEST(ThreadPoolExecutorTest, IOPoolStats) {\n  poolStats<IOThreadPoolExecutor>();\n}\n\ntemplate <class TPE>\nstatic void taskStats() {\n  TPE tpe(1);\n  std::atomic<int> c(0);\n  auto now = std::chrono::steady_clock::now();\n  tpe.subscribeToTaskStats([&](const ThreadPoolExecutor::TaskStats& stats) {\n    int i = c++;\n    EXPECT_LT(now, stats.enqueueTime);\n    EXPECT_LT(milliseconds(0), stats.runTime);\n    if (i == 1) {\n      EXPECT_LT(milliseconds(0), stats.waitTime);\n      EXPECT_NE(0, stats.requestId);\n    } else {\n      EXPECT_EQ(0, stats.requestId);\n    }\n  });\n  tpe.add(burnMs(10));\n  RequestContextScopeGuard rctx;\n  tpe.add(burnMs(10));\n  tpe.join();\n  EXPECT_EQ(2, c);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, TaskStats) {\n  taskStats<TypeParam>();\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, TaskObserver) {\n  struct TestTaskObserver : ThreadPoolExecutor::TaskObserver {\n    struct TaskState {\n      std::chrono::steady_clock::time_point enqueueTime;\n      std::optional<std::chrono::nanoseconds> waitTime;\n      bool ran = false;\n    };\n\n    void taskEnqueued(\n        const ThreadPoolExecutor::TaskInfo& info) noexcept override {\n      auto ts = taskStates.wlock();\n      auto [it, inserted] = ts->try_emplace(info.taskId);\n      ASSERT_TRUE(inserted);\n      it->second.enqueueTime = info.enqueueTime;\n    }\n\n    void taskDequeued(\n        const ThreadPoolExecutor::DequeuedTaskInfo& info) noexcept override {\n      auto ts = taskStates.wlock();\n      auto it = ts->find(info.taskId);\n      ASSERT_TRUE(it != ts->end());\n      EXPECT_EQ(it->second.enqueueTime, info.enqueueTime);\n      ASSERT_FALSE(std::exchange(it->second.waitTime, info.waitTime));\n    }\n\n    void taskProcessed(\n        const ThreadPoolExecutor::ProcessedTaskInfo& info) noexcept override {\n      auto ts = taskStates.wlock();\n      auto it = ts->find(info.taskId);\n      ASSERT_TRUE(it != ts->end());\n      EXPECT_EQ(it->second.enqueueTime, info.enqueueTime);\n      ASSERT_TRUE(it->second.waitTime);\n      EXPECT_EQ(*it->second.waitTime, info.waitTime);\n      EXPECT_GT(info.runTime.count(), 0);\n      it->second.ran = true;\n    }\n\n    Synchronized<F14FastMap<uint64_t, TaskState>> taskStates;\n  };\n\n  TypeParam ex{4};\n  auto observer = std::make_unique<TestTaskObserver>();\n  auto* observerPtr = observer.get();\n  ex.addTaskObserver(std::move(observer));\n\n  static constexpr size_t kNumTasks = 10;\n  for (size_t i = 0; i < kNumTasks; ++i) {\n    ex.add(burnMs(10));\n  }\n\n  ex.join();\n\n  auto ts = observerPtr->taskStates.exchange({});\n  EXPECT_EQ(ts.size(), kNumTasks);\n  for (auto& [_, taskState] : ts) {\n    EXPECT_TRUE(taskState.ran);\n  }\n}\n\nTEST(ThreadPoolExecutorTest, GetUsedCpuTime) {\n#ifdef __linux__\n  CPUThreadPoolExecutor e(4);\n  ASSERT_EQ(e.numActiveThreads(), 0);\n  ASSERT_EQ(e.getUsedCpuTime(), nanoseconds(0));\n  // get busy\n  Latch latch(4);\n  auto busy_loop = [&] {\n    burnThreadCpu(1s);\n    latch.count_down();\n  };\n  auto idle_loop = [&] {\n    idleLoopFor(1s);\n    latch.count_down();\n  };\n  e.add(busy_loop); // +1s cpu time\n  e.add(busy_loop); // +1s cpu time\n  e.add(idle_loop); // +0s cpu time\n  e.add(idle_loop); // +0s cpu time\n  latch.wait();\n  // pool should have used 2s cpu time (in 1s wall clock time)\n  auto elapsed0 = e.getUsedCpuTime();\n  ASSERT_NEAR_NS(elapsed0, 2s, 100ms);\n  // stop all threads\n  e.setNumThreads(0);\n  ASSERT_EQ(e.numActiveThreads(), 0);\n  // total pool CPU time should not have changed\n  auto elapsed1 = e.getUsedCpuTime();\n  ASSERT_NEAR_NS(elapsed0, elapsed1, 100ms);\n  // add a thread, do nothing, cpu time should stay the same\n  e.setNumThreads(1);\n  Baton<> baton;\n  e.add([&] { baton.post(); });\n  baton.wait();\n  ASSERT_EQ(e.numActiveThreads(), 1);\n  auto elapsed2 = e.getUsedCpuTime();\n  ASSERT_NEAR_NS(elapsed1, elapsed2, 100ms);\n  // now burn some more cycles\n  baton.reset();\n  e.add([&] {\n    burnThreadCpu(500ms);\n    baton.post();\n  });\n  baton.wait();\n  auto elapsed3 = e.getUsedCpuTime();\n  ASSERT_NEAR_NS(elapsed3, elapsed2 + 500ms, 100ms);\n#else\n  CPUThreadPoolExecutor e(1);\n  // Just make sure 0 is returned\n  ASSERT_EQ(e.getUsedCpuTime(), nanoseconds(0));\n  Baton<> baton;\n  e.add([&] {\n    auto expires = steady_clock::now() + 500ms;\n    while (steady_clock::now() < expires) {\n    }\n    baton.post();\n  });\n  baton.wait();\n  ASSERT_EQ(e.getUsedCpuTime(), nanoseconds(0));\n#endif\n}\n\ntemplate <class TPE>\nstatic void expiration() {\n  TPE tpe(1);\n  std::atomic<int> statCbCount(0);\n  tpe.subscribeToTaskStats([&](const ThreadPoolExecutor::TaskStats& stats) {\n    int i = statCbCount++;\n    if (i == 0) {\n      EXPECT_FALSE(stats.expired);\n    } else if (i == 1) {\n      EXPECT_TRUE(stats.expired);\n    } else {\n      FAIL();\n    }\n  });\n  std::atomic<int> expireCbCount(0);\n  auto expireCb = [&]() { expireCbCount++; };\n  tpe.add(burnMs(10), seconds(60), expireCb);\n  tpe.add(burnMs(10), milliseconds(10), expireCb);\n  tpe.join();\n  EXPECT_EQ(2, statCbCount);\n  EXPECT_EQ(1, expireCbCount);\n}\n\nTEST(ThreadPoolExecutorTest, CPUExpiration) {\n  expiration<CPUThreadPoolExecutor>();\n}\n\nTEST(ThreadPoolExecutorTest, IOExpiration) {\n  expiration<IOThreadPoolExecutor>();\n}\n\ntemplate <typename TPE>\nstatic void futureExecutor() {\n  FutureExecutor<TPE> fe(2);\n  std::atomic<int> c{0};\n  fe.addFuture([]() { return makeFuture<int>(42); }).then([&](Try<int>&& t) {\n    c++;\n    EXPECT_EQ(42, t.value());\n  });\n  fe.addFuture([]() { return 100; }).then([&](Try<int>&& t) {\n    c++;\n    EXPECT_EQ(100, t.value());\n  });\n  fe.addFuture([]() { return makeFuture(); }).then([&](Try<Unit>&& t) {\n    c++;\n    EXPECT_NO_THROW(t.value());\n  });\n  fe.addFuture([]() { return; }).then([&](Try<Unit>&& t) {\n    c++;\n    EXPECT_NO_THROW(t.value());\n  });\n  fe.addFuture([]() {\n      throw std::runtime_error(\"oops\");\n    }).then([&](Try<Unit>&& t) {\n    c++;\n    EXPECT_THROW(t.value(), std::runtime_error);\n  });\n  // Test doing actual async work\n  folly::Baton<> baton;\n  fe.addFuture([&]() {\n      auto p = std::make_shared<Promise<int>>();\n      std::thread t([p]() {\n        burnMs(10)();\n        p->setValue(42);\n      });\n      t.detach();\n      return p->getFuture();\n    }).then([&](Try<int>&& t) {\n    EXPECT_EQ(42, t.value());\n    c++;\n    baton.post();\n  });\n  baton.wait();\n  fe.join();\n  EXPECT_EQ(6, c);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, FuturePool) {\n  futureExecutor<TypeParam>();\n}\n\nTEST(ThreadPoolExecutorTest, PriorityPreemptionTest) {\n  bool tookLopri = false;\n  auto completed = 0;\n  auto hipri = [&] {\n    EXPECT_FALSE(tookLopri);\n    completed++;\n  };\n  auto lopri = [&] {\n    tookLopri = true;\n    completed++;\n  };\n  CPUThreadPoolExecutor pool(0, 2);\n  {\n    VirtualExecutor ve(pool);\n    for (int i = 0; i < 50; i++) {\n      ve.addWithPriority(lopri, Executor::LO_PRI);\n    }\n    for (int i = 0; i < 50; i++) {\n      ve.addWithPriority(hipri, Executor::HI_PRI);\n    }\n    pool.setNumThreads(1);\n  }\n  EXPECT_EQ(100, completed);\n}\n\nclass TestObserver : public ThreadPoolExecutor::Observer {\n public:\n  void threadStarted(ThreadPoolExecutor::ThreadHandle*) noexcept override {\n    threads_++;\n  }\n  void threadStopped(ThreadPoolExecutor::ThreadHandle*) noexcept override {\n    threads_--;\n  }\n  void threadPreviouslyStarted(\n      ThreadPoolExecutor::ThreadHandle*) noexcept override {\n    threads_++;\n  }\n  void threadNotYetStopped(\n      ThreadPoolExecutor::ThreadHandle*) noexcept override {\n    threads_--;\n  }\n  void checkCalls() { ASSERT_EQ(threads_, 0); }\n\n private:\n  std::atomic<int> threads_{0};\n};\n\ntemplate <typename TPE>\nstatic void testObserver() {\n  auto observer = std::make_shared<TestObserver>();\n\n  {\n    TPE exe(10);\n    exe.addObserver(observer);\n    exe.setNumThreads(3);\n    exe.setNumThreads(0);\n    exe.setNumThreads(7);\n    exe.removeObserver(observer);\n    exe.setNumThreads(10);\n  }\n\n  observer->checkCalls();\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, Observer) {\n  testObserver<TypeParam>();\n}\n\nTEST(ThreadPoolExecutorTest, AddWithPriority) {\n  std::atomic_int c{0};\n  auto f = [&] { c++; };\n\n  // IO exe doesn't support priorities\n  IOThreadPoolExecutor ioExe(10);\n  EXPECT_THROW(ioExe.addWithPriority(f, 0), std::runtime_error);\n\n  // EDF exe doesn't support priorities\n  EDFThreadPoolExecutor edfExe(10);\n  EXPECT_THROW(edfExe.addWithPriority(f, 0), std::runtime_error);\n\n  CPUThreadPoolExecutor cpuExe(10, 3);\n  cpuExe.addWithPriority(f, -1);\n  cpuExe.addWithPriority(f, 0);\n  cpuExe.addWithPriority(f, 1);\n  cpuExe.addWithPriority(f, -2); // will add at the lowest priority\n  cpuExe.addWithPriority(f, 2); // will add at the highest priority\n  cpuExe.addWithPriority(f, Executor::LO_PRI);\n  cpuExe.addWithPriority(f, Executor::HI_PRI);\n  cpuExe.join();\n\n  EXPECT_EQ(7, c);\n}\n\nTEST(ThreadPoolExecutorTest, BlockingQueue) {\n  std::atomic_int c{0};\n  auto f = [&] {\n    burnMs(1)();\n    c++;\n  };\n  const int kQueueCapacity = 1;\n  const int kThreads = 1;\n\n  auto queue = std::make_unique<LifoSemMPMCQueue<\n      CPUThreadPoolExecutor::CPUTask,\n      QueueBehaviorIfFull::BLOCK>>(kQueueCapacity);\n\n  CPUThreadPoolExecutor cpuExe(\n      kThreads,\n      std::move(queue),\n      std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"));\n\n  // Add `f` five times. It sleeps for 1ms every time. Calling\n  // `cppExec.add()` is *almost* guaranteed to block because there's\n  // only 1 cpu worker thread.\n  for (int i = 0; i < 5; i++) {\n    EXPECT_NO_THROW(cpuExe.add(f));\n  }\n  cpuExe.join();\n\n  EXPECT_EQ(5, c);\n}\n\nTEST(ThreadPoolExecutorTest, NoThreadPriorityInheritance) {\n  constexpr size_t kNumThreads = 16;\n  const auto initialPriority = getpriority(PRIO_PROCESS, 0);\n\n  // If minThreads == maxThreads, no threads should be created on add(), and\n  // thus they should not inherit the parent thread's priority. Instead, all\n  // threads should inherit the priority at the time of construction.\n  CPUThreadPoolExecutor exe{std::pair(kNumThreads, kNumThreads)};\n\n  // Nice the current thread.\n  setpriority(PRIO_PROCESS, 0, 19);\n\n  Latch ready{kNumThreads + 1};\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    exe.add([&] {\n      EXPECT_EQ(getpriority(PRIO_PROCESS, 0), initialPriority);\n      ready.arrive_and_wait();\n    });\n  }\n  ready.arrive_and_wait();\n  exe.join();\n}\n\nnamespace {\n\ntemplate <class T>\nclass NoTimeoutBlockingQueue : public UnboundedBlockingQueue<T> {\n public:\n  folly::Optional<T> try_take_for(std::chrono::milliseconds time) override {\n    ADD_FAILURE() << \"try_take_for() should not be called\";\n    return UnboundedBlockingQueue<T>::try_take_for(time);\n  }\n};\n\n} // namespace\n\nTEST(ThreadPoolExecutorTest, NoTimeoutInNonDynamicPool) {\n  constexpr size_t kNumThreads = 16;\n  // If minThreads == maxThreads, only non-timeout take() should be used on the\n  // queue.\n  CPUThreadPoolExecutor exe{\n      std::pair(kNumThreads, kNumThreads),\n      std::make_unique<\n          NoTimeoutBlockingQueue<CPUThreadPoolExecutor::CPUTask>>()};\n  exe.add([] {});\n  exe.join();\n}\n\nTEST(PriorityThreadFactoryTest, ThreadPriority) {\n  errno = 0;\n  auto currentPriority = getpriority(PRIO_PROCESS, 0);\n  if (errno != 0) {\n    throwSystemError(\"failed to get current priority\");\n  }\n\n  // Non-root users can only increase the priority value.  Make sure we are\n  // trying to go to a higher priority than we are currently running as, up to\n  // the maximum allowed of 20.\n  int desiredPriority = std::min(20, currentPriority + 1);\n\n  PriorityThreadFactory factory(\n      std::make_shared<NamedThreadFactory>(\"stuff\"), desiredPriority);\n  int actualPriority = -21;\n  factory.newThread([&]() { actualPriority = getpriority(PRIO_PROCESS, 0); })\n      .join();\n  EXPECT_EQ(desiredPriority, actualPriority);\n}\n\nTEST(InitThreadFactoryTest, InitializerCalled) {\n  int initializerCalledCount = 0;\n  InitThreadFactory factory(\n      std::make_shared<NamedThreadFactory>(\"test\"),\n      [&initializerCalledCount] { initializerCalledCount++; });\n  factory\n      .newThread([&initializerCalledCount]() {\n        EXPECT_EQ(initializerCalledCount, 1);\n      })\n      .join();\n  EXPECT_EQ(initializerCalledCount, 1);\n}\n\nTEST(InitThreadFactoryTest, InitializerAndFinalizerCalled) {\n  bool initializerCalled = false;\n  bool taskBodyCalled = false;\n  bool finalizerCalled = false;\n\n  InitThreadFactory factory(\n      std::make_shared<NamedThreadFactory>(\"test\"),\n      [&] {\n        // thread initializer\n        EXPECT_FALSE(initializerCalled);\n        EXPECT_FALSE(taskBodyCalled);\n        EXPECT_FALSE(finalizerCalled);\n        initializerCalled = true;\n      },\n      [&] {\n        // thread finalizer\n        EXPECT_TRUE(initializerCalled);\n        EXPECT_TRUE(taskBodyCalled);\n        EXPECT_FALSE(finalizerCalled);\n        finalizerCalled = true;\n      });\n\n  factory\n      .newThread([&]() {\n        EXPECT_TRUE(initializerCalled);\n        EXPECT_FALSE(taskBodyCalled);\n        EXPECT_FALSE(finalizerCalled);\n        taskBodyCalled = true;\n      })\n      .join();\n\n  EXPECT_TRUE(initializerCalled);\n  EXPECT_TRUE(taskBodyCalled);\n  EXPECT_TRUE(finalizerCalled);\n}\n\nclass TestData : public folly::RequestData {\n public:\n  explicit TestData(int data) : data_(data) {}\n  ~TestData() override {}\n\n  bool hasCallback() override { return false; }\n\n  int data_;\n};\n\nTEST(ThreadPoolExecutorTest, RequestContext) {\n  RequestContextScopeGuard rctx; // create new request context for this scope\n  EXPECT_EQ(nullptr, RequestContext::get()->getContextData(\"test\"));\n  RequestContext::get()->setContextData(\"test\", std::make_unique<TestData>(42));\n  auto data = RequestContext::get()->getContextData(\"test\");\n  EXPECT_EQ(42, dynamic_cast<TestData*>(data)->data_);\n\n  static constexpr auto verifyRequestContext = +[] {\n    auto data2 = RequestContext::get()->getContextData(\"test\");\n    EXPECT_TRUE(data2 != nullptr);\n    if (data2 != nullptr) {\n      EXPECT_EQ(42, dynamic_cast<TestData*>(data2)->data_);\n    }\n  };\n\n  {\n    CPUThreadPoolExecutor executor(1);\n    executor.add([] { verifyRequestContext(); });\n    executor.add([x = makeGuard(verifyRequestContext)] {});\n  }\n}\n\nstd::atomic<int> g_sequence{};\n\nstruct SlowMover {\n  explicit SlowMover(bool slow_ = false) : slow(slow_) {}\n  SlowMover(SlowMover&& other) noexcept { *this = std::move(other); }\n  SlowMover& operator=(SlowMover&& other) noexcept {\n    ++g_sequence;\n    slow = other.slow;\n    if (slow) {\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(50));\n    }\n    ++g_sequence;\n    return *this;\n  }\n\n  bool slow;\n};\n\ntemplate <typename Q>\nvoid bugD3527722_test() {\n  // Test that the queue does not get stuck if writes are completed in\n  // order opposite to how they are initiated.\n  Q q(1024);\n  std::atomic<int> turn{};\n\n  std::thread consumer1([&] {\n    ++turn;\n    q.take();\n  });\n  std::thread consumer2([&] {\n    ++turn;\n    q.take();\n  });\n\n  std::thread producer1([&] {\n    ++turn;\n    while (turn < 4) {\n      ;\n    }\n    ++turn;\n    q.add(SlowMover(true));\n  });\n  std::thread producer2([&] {\n    ++turn;\n    while (turn < 5) {\n      ;\n    }\n    q.add(SlowMover(false));\n  });\n\n  producer1.join();\n  producer2.join();\n  consumer1.join();\n  consumer2.join();\n}\n\nTEST(ThreadPoolExecutorTest, LifoSemMPMCQueueBugD3527722) {\n  bugD3527722_test<LifoSemMPMCQueue<SlowMover>>();\n}\n\ntemplate <typename T>\nstruct UBQ : public UnboundedBlockingQueue<T> {\n  explicit UBQ(int) {}\n};\n\nTEST(ThreadPoolExecutorTest, UnboundedBlockingQueueBugD3527722) {\n  bugD3527722_test<UBQ<SlowMover>>();\n}\n\ntemplate <typename Q>\nvoid nothrow_not_full_test() {\n  /* LifoSemMPMCQueue should not throw when not full when active\n     consumers are delayed. */\n  Q q(2);\n  g_sequence = 0;\n\n  std::thread consumer1([&] {\n    while (g_sequence < 4) {\n      ;\n    }\n    q.take(); // ++g_sequence to 5 then slow\n  });\n  std::thread consumer2([&] {\n    while (g_sequence < 5) {\n      ;\n    }\n    q.take(); // ++g_sequence to 6 and 7 - fast\n  });\n\n  std::thread producer([&] {\n    q.add(SlowMover(true)); // ++g_sequence to 1 and 2\n    q.add(SlowMover(false)); // ++g_sequence to 3 and 4\n    while (g_sequence < 7) { // g_sequence == 7 implies queue is not full\n      ;\n    }\n    EXPECT_NO_THROW(q.add(SlowMover(false)));\n  });\n\n  producer.join();\n  consumer1.join();\n  consumer2.join();\n}\n\nTEST(ThreadPoolExecutorTest, LifoSemMPMCQueueNoThrowNotFull) {\n  nothrow_not_full_test<LifoSemMPMCQueue<SlowMover>>();\n}\n\ntemplate <typename TPE>\nstatic void removeThreadTest() {\n  // test that adding a .then() after we have removed some threads\n  // doesn't cause deadlock and they are executed on different threads\n  folly::Optional<folly::Future<int>> f;\n  std::thread::id id1, id2;\n  TPE fe(2);\n  f = folly::makeFuture()\n          .via(&fe)\n          .thenValue([&id1](auto&&) {\n            burnMs(100)();\n            id1 = std::this_thread::get_id();\n          })\n          .thenValue([&id2](auto&&) {\n            return 77;\n            id2 = std::this_thread::get_id();\n          });\n  fe.setNumThreads(1);\n\n  // future::then should be fulfilled because there is other thread available\n  EXPECT_EQ(77, std::move(*f).get());\n  // two thread should be different because then part should be rescheduled to\n  // the other thread\n  EXPECT_NE(id1, id2);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, RemoveThread) {\n  removeThreadTest<TypeParam>();\n}\n\ntemplate <typename TPE>\nstatic void resizeThreadWhileExecutingTest() {\n  TPE tpe(10);\n  EXPECT_EQ(10, tpe.numThreads());\n\n  std::atomic<int> completed(0);\n  auto f = [&]() {\n    burnMs(10)();\n    completed++;\n  };\n  for (int i = 0; i < 1000; i++) {\n    tpe.add(f);\n  }\n  tpe.setNumThreads(8);\n  EXPECT_EQ(8, tpe.numThreads());\n  tpe.setNumThreads(5);\n  EXPECT_EQ(5, tpe.numThreads());\n  tpe.setNumThreads(15);\n  EXPECT_EQ(15, tpe.numThreads());\n  tpe.join();\n  EXPECT_EQ(1000, completed);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, ResizeThreadWhileExecuting) {\n  resizeThreadWhileExecutingTest<TypeParam>();\n}\n\ntemplate <typename TPE>\nvoid keepAliveTest() {\n  auto executor = std::make_unique<TPE>(4);\n\n  auto f =\n      futures::sleep(std::chrono::milliseconds{100})\n          .via(executor.get())\n          .thenValue([keepAlive = getKeepAliveToken(executor.get())](auto&&) {\n            return 42;\n          })\n          .semi();\n\n  executor.reset();\n\n  EXPECT_TRUE(f.isReady());\n  EXPECT_EQ(42, std::move(f).get());\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, KeepAlive) {\n  keepAliveTest<TypeParam>();\n}\n\nint getNumThreadPoolExecutors() {\n  int count = 0;\n  ThreadPoolExecutor::withAll([&count](ThreadPoolExecutor&) { count++; });\n  return count;\n}\n\ntemplate <typename TPE>\nstatic void registersToExecutorListTest() {\n  EXPECT_EQ(0, getNumThreadPoolExecutors());\n  {\n    TPE tpe(10);\n    EXPECT_EQ(1, getNumThreadPoolExecutors());\n    {\n      TPE tpe2(5);\n      EXPECT_EQ(2, getNumThreadPoolExecutors());\n    }\n    EXPECT_EQ(1, getNumThreadPoolExecutors());\n  }\n  EXPECT_EQ(0, getNumThreadPoolExecutors());\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, RegistersToExecutorList) {\n  registersToExecutorListTest<TypeParam>();\n}\n\ntemplate <typename TPE>\nstatic void testUsesNameFromNamedThreadFactory() {\n  // Verify that the name is propagated even if the NamedThreadFactory is\n  // wrapped.\n  auto tf = std::make_shared<InitThreadFactory>(\n      std::make_shared<NamedThreadFactory>(\"my_executor\"), [] {});\n  TPE tpe(10, tf);\n  EXPECT_EQ(\"my_executor\", tpe.getName());\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, UsesNameFromNamedThreadFactory) {\n  testUsesNameFromNamedThreadFactory<TypeParam>();\n}\n\nTEST(ThreadPoolExecutorTest, DynamicThreadsTest) {\n  boost::barrier barrier{3};\n  auto twice_waiting_task = [&] { barrier.wait(), barrier.wait(); };\n  CPUThreadPoolExecutor e(2);\n  e.setThreadDeathTimeout(std::chrono::milliseconds(100));\n  e.add(twice_waiting_task);\n  e.add(twice_waiting_task);\n  barrier.wait(); // ensure both tasks are mid-flight\n  EXPECT_EQ(2, e.getPoolStats().activeThreadCount) << \"sanity check\";\n\n  auto pred = [&] { return e.getPoolStats().activeThreadCount == 0; };\n  EXPECT_FALSE(pred()) << \"sanity check\";\n  barrier.wait(); // let both mid-flight tasks complete\n  EXPECT_EQ(\n      folly::detail::spin_result::success,\n      folly::detail::spin_yield_until(\n          std::chrono::steady_clock::now() + std::chrono::seconds(1), pred));\n}\n\nTEST(ThreadPoolExecutorTest, GetThreadIdCollector) {\n  CPUThreadPoolExecutor e(1);\n  auto* collector = e.getThreadIdCollector();\n  ASSERT_TRUE(collector != nullptr);\n\n  EXPECT_THAT(collector->collectThreadIds().threadIds, testing::IsEmpty());\n\n  pid_t tid;\n  Baton<> ready;\n  Baton<> unblock;\n  e.add([&] {\n    tid = getOSThreadID();\n    ready.post();\n    unblock.wait(); // Wait until we acquire the keepalive.\n  });\n\n  ready.wait();\n  auto ids = collector->collectThreadIds();\n  EXPECT_THAT(ids.threadIds, testing::ElementsAre(tid));\n  unblock.post();\n\n  Baton<> joined;\n  std::thread t([&] {\n    e.join();\n    joined.post();\n  });\n  // Until we release ids, the executor join cannot complete\n  EXPECT_FALSE(joined.try_wait_for(100ms));\n  // But things should eventually complete when released.\n  ids = {};\n  t.join();\n  EXPECT_THAT(collector->collectThreadIds().threadIds, testing::IsEmpty());\n}\n\nTEST(ThreadPoolExecutorTest, DynamicThreadAddRemoveRace) {\n  CPUThreadPoolExecutor e(1);\n  e.setThreadDeathTimeout(std::chrono::milliseconds(0));\n  std::atomic<uint64_t> count{0};\n  for (int i = 0; i < 10000; i++) {\n    Baton<> b;\n    e.add([&]() {\n      count.fetch_add(1, std::memory_order_relaxed);\n      b.post();\n    });\n    b.wait();\n  }\n  e.join();\n  EXPECT_EQ(count, 10000);\n}\n\nTEST(ThreadPoolExecutorTest, AddPerf) {\n  CPUThreadPoolExecutor e(\n      kIsSanitizeThread ? 25 : 1000,\n      std::make_shared<NamedThreadFactory>(\"CPUThreadPool\"));\n  e.setThreadDeathTimeout(std::chrono::milliseconds(1));\n  for (int i = 0; i < 10000; i++) {\n    e.add([&, ka = getKeepAliveToken(e)]() {\n      // holding a keep-alive here permits the following add()\n      // to occur safely, concurrently with the stop() below\n      e.add([]() { /* sleep override */ usleep(1000); });\n    });\n  }\n  e.stop();\n}\n\nclass ExecutorWorkerProviderTest : public ::testing::Test {\n protected:\n  void SetUp() override { kWorkerProviderGlobal = nullptr; }\n  void TearDown() override { kWorkerProviderGlobal = nullptr; }\n};\n\nTEST_F(ExecutorWorkerProviderTest, ThreadCollectorBasicTest) {\n  // Start 4 threads and have all of them work on a task.\n  // Then invoke the ThreadIdCollector::collectThreadIds()\n  // method to capture the set of active thread ids.\n  boost::barrier barrier{5};\n  Synchronized<std::vector<pid_t>> expectedTids;\n  auto task = [&]() {\n    expectedTids.wlock()->push_back(folly::getOSThreadID());\n    barrier.wait();\n  };\n  CPUThreadPoolExecutor e(4);\n  for (int i = 0; i < 4; ++i) {\n    e.add(task);\n  }\n  barrier.wait();\n  {\n    const auto threadIdsWithKA = kWorkerProviderGlobal->collectThreadIds();\n    const auto& ids = threadIdsWithKA.threadIds;\n    auto locked = expectedTids.rlock();\n    EXPECT_EQ(ids.size(), locked->size());\n    EXPECT_TRUE(std::is_permutation(ids.begin(), ids.end(), locked->begin()));\n  }\n  e.join();\n}\n\nTEST_F(ExecutorWorkerProviderTest, ThreadCollectorMultipleInvocationTest) {\n  // Run some tasks via the executor and invoke\n  // WorkerProvider::collectThreadIds() at least twice to make sure that there\n  // is no deadlock in repeated invocations.\n  CPUThreadPoolExecutor e(1);\n  e.add([&]() {});\n  {\n    auto idsWithKA1 = kWorkerProviderGlobal->collectThreadIds();\n    auto idsWithKA2 = kWorkerProviderGlobal->collectThreadIds();\n    auto& ids1 = idsWithKA1.threadIds;\n    auto& ids2 = idsWithKA2.threadIds;\n    EXPECT_EQ(ids1.size(), 1);\n    EXPECT_EQ(ids1.size(), ids2.size());\n    EXPECT_EQ(ids1, ids2);\n  }\n  // Add some more threads and schedule tasks while the collector\n  // is capturing thread Ids.\n  std::array<folly::Baton<>, 4> bats;\n  {\n    auto idsWithKA1 = kWorkerProviderGlobal->collectThreadIds();\n    e.setNumThreads(4);\n    for (size_t i = 0; i < 4; ++i) {\n      e.add([i, &bats]() { bats[i].wait(); });\n    }\n    for (auto& bat : bats) {\n      bat.post();\n    }\n    auto idsWithKA2 = kWorkerProviderGlobal->collectThreadIds();\n    auto& ids1 = idsWithKA1.threadIds;\n    auto& ids2 = idsWithKA2.threadIds;\n    EXPECT_EQ(ids1.size(), 1);\n    EXPECT_EQ(ids2.size(), 4);\n  }\n  e.join();\n}\n\nTEST_F(ExecutorWorkerProviderTest, ThreadCollectorBlocksThreadExitTest) {\n  // We need to ensure that the collector's keep alive effectively\n  // blocks the executor's threads from exiting. This is done by verifying\n  // that a call to reduce the worker count via setNumThreads() does not\n  // actually reduce the workers (kills threads) while  the keep alive is\n  // in scope.\n  constexpr size_t kNumThreads = 4;\n  std::array<folly::Baton<>, kNumThreads> bats;\n  CPUThreadPoolExecutor e(kNumThreads);\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    e.add([i, &bats]() { bats[i].wait(); });\n  }\n  Baton<> baton;\n  Baton<> threadCountBaton;\n  auto bgCollector = std::thread([&]() {\n    {\n      auto idsWithKA = kWorkerProviderGlobal->collectThreadIds();\n      baton.post();\n      // Since this thread is holding the KeepAlive, it should block\n      // the main thread's `setNumThreads()` call which is trying to\n      // reduce the thread count of the executor. We verify that by\n      // checking that the baton isn't posted after a 100ms wait.\n      auto posted =\n          threadCountBaton.try_wait_for(std::chrono::milliseconds(100));\n      EXPECT_FALSE(posted);\n      auto& ids = idsWithKA.threadIds;\n      // The thread count should still be 4 since the collector's\n      // keep alive is active. To further verify that the threads are\n      EXPECT_EQ(ids.size(), kNumThreads);\n    }\n  });\n  baton.wait();\n  for (auto& bat : bats) {\n    bat.post();\n  }\n  e.setNumThreads(2);\n  threadCountBaton.post();\n  bgCollector.join();\n  // The thread count should now be reduced to 2.\n  EXPECT_EQ(e.numThreads(), 2);\n  e.join();\n}\n\ntemplate <typename TPE>\nstatic void WeakRefTest() {\n  // test that adding a .then() after we have\n  // started shutting down does not deadlock\n  folly::Optional<folly::Future<folly::Unit>> f;\n  int counter{0};\n  {\n    TPE fe(1);\n    f = folly::makeFuture()\n            .via(&fe)\n            .thenValue([](auto&&) { burnMs(100)(); })\n            .thenValue([&](auto&&) { ++counter; })\n            .via(getWeakRef(fe))\n            .thenValue([](auto&&) { burnMs(100)(); })\n            .thenValue([&](auto&&) { ++counter; });\n  }\n  EXPECT_THROW(std::move(*f).get(), folly::BrokenPromise);\n  EXPECT_EQ(1, counter);\n}\n\ntemplate <typename TPE>\nstatic void virtualExecutorTest() {\n  using namespace std::literals;\n\n  folly::Optional<folly::SemiFuture<folly::Unit>> f;\n  int counter{0};\n  {\n    TPE fe(1);\n    {\n      VirtualExecutor ve(fe);\n      f = futures::sleep(100ms)\n              .via(&ve)\n              .thenValue([&](auto&&) {\n                ++counter;\n                return futures::sleep(100ms);\n              })\n              .via(&fe)\n              .thenValue([&](auto&&) { ++counter; })\n              .semi();\n    }\n    EXPECT_EQ(1, counter);\n\n    bool functionDestroyed{false};\n    bool functionCalled{false};\n    {\n      VirtualExecutor ve(fe);\n      auto guard = makeGuard([&functionDestroyed] {\n        std::this_thread::sleep_for(100ms);\n        functionDestroyed = true;\n      });\n      ve.add([&functionCalled, guard = std::move(guard)] {\n        functionCalled = true;\n      });\n    }\n    EXPECT_TRUE(functionCalled);\n    EXPECT_TRUE(functionDestroyed);\n  }\n  EXPECT_TRUE(f->isReady());\n  EXPECT_NO_THROW(std::move(*f).get());\n  EXPECT_EQ(2, counter);\n}\n\nclass SingleThreadedCPUThreadPoolExecutor\n    : public CPUThreadPoolExecutor,\n      public SequencedExecutor {\n public:\n  explicit SingleThreadedCPUThreadPoolExecutor(size_t)\n      : CPUThreadPoolExecutor(1) {}\n  ~SingleThreadedCPUThreadPoolExecutor() override { stop(); }\n};\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, WeakRef) {\n  WeakRefTest<TypeParam>();\n}\n\nTEST(ThreadPoolExecutorTest, WeakRefTestSingleThreadedCPU) {\n  WeakRefTest<SingleThreadedCPUThreadPoolExecutor>();\n}\n\nTEST(ThreadPoolExecutorTest, WeakRefTestSequential) {\n  SingleThreadedCPUThreadPoolExecutor ex(1);\n  auto weakRef = getWeakRef(ex);\n  EXPECT_TRUE((std::is_same_v<\n               decltype(weakRef),\n               Executor::KeepAlive<SequencedExecutor>>));\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, VirtualExecutor) {\n  virtualExecutorTest<TypeParam>();\n}\n\n// Test use of guard inside executors\ntemplate <class TPE>\nstatic void currentThreadTest(folly::StringPiece executorName) {\n  folly::Optional<ExecutorBlockingContext> ctx{};\n  TPE tpe(1, std::make_shared<NamedThreadFactory>(executorName));\n  tpe.add([&ctx]() { ctx = getExecutorBlockingContext(); });\n  tpe.join();\n  EXPECT_EQ(ctx->tag, executorName);\n}\n\n// Test the nesting of the permit guard\ntemplate <class TPE>\nstatic void currentThreadTestDisabled(folly::StringPiece executorName) {\n  folly::Optional<ExecutorBlockingContext> ctxPermit{};\n  folly::Optional<ExecutorBlockingContext> ctxForbid{};\n  TPE tpe(1, std::make_shared<NamedThreadFactory>(executorName));\n  tpe.add([&]() {\n    {\n      // Nest the guard that permits blocking\n      ExecutorBlockingGuard guard{ExecutorBlockingGuard::PermitTag{}};\n      ctxPermit = getExecutorBlockingContext();\n    }\n    ctxForbid = getExecutorBlockingContext();\n  });\n  tpe.join();\n  EXPECT_TRUE(!ctxPermit.has_value());\n  EXPECT_EQ(ctxForbid->tag, executorName);\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, CurrentThreadExecutor) {\n  currentThreadTest<TypeParam>(\"ExecutorName\");\n  currentThreadTestDisabled<TypeParam>(\"ExecutorName\");\n}\n\nTYPED_TEST(ThreadPoolExecutorTypedTest, ProcessedTasksCountStartsAtZero) {\n  TypeParam e(4);\n  EXPECT_EQ(0, e.getPoolStats().processedTaskCount);\n  e.join();\n  EXPECT_EQ(0, e.getPoolStats().processedTaskCount);\n}\n\nTYPED_TEST(\n    ThreadPoolExecutorTypedTest, ProcessedTasksIncrementsAfterCompletion) {\n  TypeParam e(1);\n  EXPECT_EQ(0, e.getPoolStats().processedTaskCount);\n\n  folly::Baton<> task1;\n\n  e.add([&]() { // 1\n    EXPECT_EQ(0, e.getPoolStats().processedTaskCount);\n    task1.post();\n  });\n\n  task1.wait();\n\n  folly::Baton<> task2;\n\n  e.add([&]() { // 2\n    EXPECT_EQ(1, e.getPoolStats().processedTaskCount);\n    task2.post();\n  });\n\n  task2.wait();\n\n  folly::Baton<> task3;\n\n  e.add([&]() { // 3\n    EXPECT_EQ(2, e.getPoolStats().processedTaskCount);\n    task3.post();\n  });\n\n  task3.wait();\n\n  e.join();\n\n  EXPECT_EQ(3, e.getPoolStats().processedTaskCount);\n}\n\n// ThreadFactory wrapper that throws std::runtime_error after N successful\n// newThread() calls, simulating pthread_create failure under thread pressure.\nclass FailingThreadFactory : public ThreadFactory {\n public:\n  explicit FailingThreadFactory(\n      std::shared_ptr<ThreadFactory> delegate, size_t failAfter)\n      : delegate_(std::move(delegate)), failAfter_(failAfter) {}\n\n  std::thread newThread(Func&& func) override {\n    if (count_.fetch_add(1) >= failAfter_) {\n      throw std::runtime_error(\"newThread: simulated failure\");\n    }\n    return delegate_->newThread(std::move(func));\n  }\n\n  const std::string& getNamePrefix() const override {\n    return delegate_->getNamePrefix();\n  }\n\n private:\n  std::shared_ptr<ThreadFactory> delegate_;\n  std::atomic<size_t> count_{0};\n  size_t failAfter_;\n};\n\nTEST(ThreadPoolExecutorTest, AddImplToleratesThreadCreationFailure) {\n  auto delegate = std::make_shared<NamedThreadFactory>(\"test\");\n  auto factory = std::make_shared<FailingThreadFactory>(delegate, 1);\n\n  // Dynamic executor: max=4, min=0\n  CPUThreadPoolExecutor exe(std::pair(4, 0), std::move(factory));\n\n  // Warm up: create one thread successfully, then make factory fail.\n  folly::Baton<> warmup;\n  exe.add([&] { warmup.post(); });\n  warmup.wait();\n  EXPECT_GE(exe.numActiveThreads(), 1);\n\n  // Now the factory will fail for all subsequent newThread() calls.\n  // The existing thread should process enqueued tasks without crashing.\n  constexpr int kTasks = 10;\n  std::atomic<int> completed{0};\n  folly::Baton<> done;\n  for (int i = 0; i < kTasks; ++i) {\n    EXPECT_NO_THROW(exe.add([&] {\n      if (completed.fetch_add(1) + 1 == kTasks) {\n        done.post();\n      }\n    }));\n  }\n  done.wait();\n  EXPECT_EQ(kTasks, completed.load());\n\n  exe.join();\n}\n\nTEST(ThreadPoolExecutorTest, AddImplNoThreadsAndCreationFails) {\n  auto delegate = std::make_shared<NamedThreadFactory>(\"test\");\n  // Fail from the very first newThread() call.\n  auto factory = std::make_shared<FailingThreadFactory>(delegate, 0);\n\n  // Dynamic executor: max=1, min=0. No threads will be created at startup.\n  CPUThreadPoolExecutor exe(std::pair(1, 0), std::move(factory));\n\n  // add() should NOT throw even though thread creation fails.\n  EXPECT_NO_THROW(exe.add([] {}));\n  EXPECT_EQ(0, exe.numActiveThreads());\n\n  exe.stop();\n}\n"
  },
  {
    "path": "folly/executors/test/ThreadedExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ThreadedExecutor.h>\n\n#include <stdexcept>\n\n#include <folly/Conv.h>\n#include <folly/futures/Future.h>\n#include <folly/gen/Base.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\n\nclass ThreadedExecutorTest : public testing::Test {};\n} // namespace\n\nTEST_F(ThreadedExecutorTest, example) {\n  folly::ThreadedExecutor x;\n  auto ret =\n      folly::via(&x)\n          .thenValue([&](auto&&) { return 42; })\n          .thenValue([&](int n) { return folly::to<std::string>(n); })\n          .get();\n\n  EXPECT_EQ(\"42\", ret);\n}\n\nTEST_F(ThreadedExecutorTest, exception) {\n  folly::ThreadedExecutor x;\n  x.add([] { throw std::runtime_error(\"This should not crash the program\"); });\n}\n\nTEST_F(ThreadedExecutorTest, dtor_waits) {\n  constexpr auto kDelay = std::chrono::milliseconds(100);\n  auto x = std::make_unique<folly::ThreadedExecutor>();\n  auto fut = folly::via(&*x, [&] { /* sleep override */\n                                   std::this_thread::sleep_for(kDelay);\n  });\n  x = nullptr;\n\n  EXPECT_TRUE(fut.isReady());\n}\n\nTEST_F(ThreadedExecutorTest, many) {\n  constexpr auto kNumTasks = 1024;\n  folly::ThreadedExecutor x;\n  auto rets =\n      folly::collect(\n          folly::gen::range<size_t>(0, kNumTasks) |\n          folly::gen::map([&](size_t i) {\n            return folly::via(&x)\n                .thenValue([=](auto&&) { return i; })\n                .thenValue([](size_t k) { return folly::to<std::string>(k); });\n          }) |\n          folly::gen::as<std::vector>())\n          .get();\n\n  EXPECT_EQ(\"42\", rets[42]);\n}\n\nTEST_F(ThreadedExecutorTest, many_sleeping_constant_time) {\n  constexpr auto kNumTasks = 256;\n  constexpr auto kDelay = std::chrono::milliseconds(100);\n  folly::ThreadedExecutor x;\n  auto rets =\n      folly::collect(\n          folly::gen::range<size_t>(0, kNumTasks) |\n          folly::gen::map([&](size_t i) {\n            return folly::via(&x)\n                .thenValue([=](auto&&) {\n                  /* sleep override */ std::this_thread::sleep_for(kDelay);\n                })\n                .thenValue([=](auto&&) { return i; })\n                .thenValue([](size_t k) { return folly::to<std::string>(k); });\n          }) |\n          folly::gen::as<std::vector>())\n          .get();\n\n  EXPECT_EQ(\"42\", rets[42]);\n}\n\nTEST_F(ThreadedExecutorTest, many_sleeping_decreasing_time) {\n  constexpr auto kNumTasks = 256;\n  constexpr auto kDelay = std::chrono::milliseconds(100);\n  folly::ThreadedExecutor x;\n  auto rets =\n      folly::collect(\n          folly::gen::range<size_t>(0, kNumTasks) |\n          folly::gen::map([&](size_t i) {\n            return folly::via(&x)\n                .thenValue([=](auto&&) {\n                  auto delay = kDelay * (kNumTasks - i) / kNumTasks;\n                  /* sleep override */ std::this_thread::sleep_for(delay);\n                })\n                .thenValue([=](auto&&) { return i; })\n                .thenValue([](size_t k) { return folly::to<std::string>(k); });\n          }) |\n          folly::gen::as<std::vector>())\n          .get();\n\n  EXPECT_EQ(\"42\", rets[42]);\n}\n"
  },
  {
    "path": "folly/executors/test/ThreadedRepeatingFunctionRunnerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ThreadedRepeatingFunctionRunner.h>\n\n#include <atomic>\n\n#include <folly/portability/GTest.h>\n\nusing namespace std;\n\nstruct Foo {\n  explicit Foo(std::atomic<int>& d) : data(d) {}\n  ~Foo() { runner_.stop(); }\n\n  void start() {\n    runner_.add(\"Foo\", [this]() noexcept {\n      ++data;\n      return std::chrono::seconds(0);\n    });\n  }\n\n  std::atomic<int>& data;\n  folly::ThreadedRepeatingFunctionRunner runner_; // Must be declared last\n};\n\nstruct FooLongSleep {\n  explicit FooLongSleep(std::atomic<int>& d) : data(d) {}\n  ~FooLongSleep() {\n    runner_.stop();\n    data.store(-1);\n  }\n\n  void start() {\n    runner_.add(\"FooLongSleep\", [this]() noexcept {\n      data.store(1);\n      return 1000h; // Test would time out if we waited\n    });\n  }\n\n  std::atomic<int>& data;\n  folly::ThreadedRepeatingFunctionRunner runner_; // Must be declared last\n};\n\nTEST(TestThreadedRepeatingFunctionRunner, HandleBackgroundLoop) {\n  std::atomic<int> data(0);\n  {\n    Foo f(data);\n    EXPECT_EQ(0, data.load());\n    f.start(); // Runs increment thread in background\n    while (data.load() == 0) {\n      /* sleep override */ this_thread::sleep_for(chrono::milliseconds(10));\n    }\n  }\n  // The increment thread should have been destroyed\n  auto prev_val = data.load();\n  /* sleep override */ this_thread::sleep_for(chrono::milliseconds(100));\n  EXPECT_EQ(data.load(), prev_val);\n}\n\nTEST(TestThreadedRepeatingFunctionRunner, HandleLongSleepingThread) {\n  std::atomic<int> data(0);\n  {\n    FooLongSleep f(data);\n    EXPECT_EQ(0, data.load());\n    f.start();\n    while (data.load() == 0) {\n      /* sleep override */ this_thread::sleep_for(chrono::milliseconds(10));\n    }\n    EXPECT_EQ(1, data.load());\n  }\n  // Foo should have been destroyed, which stopped the thread!\n  EXPECT_EQ(-1, data.load());\n}\n"
  },
  {
    "path": "folly/executors/test/TimedDrivableExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/TimedDrivableExecutor.h>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nTEST(TimedDrivableExecutor, runIsStable) {\n  size_t count = 0;\n  TimedDrivableExecutor x;\n  auto f1 = [&]() { count++; };\n  auto f2 = [&]() {\n    x.add(f1);\n    x.add(f1);\n  };\n  x.add(f2);\n  x.run();\n  EXPECT_EQ(count, 0);\n}\n\nTEST(TimedDrivableExecutor, drainIsNotStable) {\n  size_t count = 0;\n  TimedDrivableExecutor x;\n  auto f1 = [&]() { count++; };\n  auto f2 = [&]() {\n    x.add(f1);\n    x.add(f1);\n  };\n  x.add(f2);\n  x.drain();\n  EXPECT_EQ(count, 2);\n}\n\nTEST(TimedDrivableExecutor, tryDrive) {\n  size_t count = 0;\n  TimedDrivableExecutor x;\n  auto f1 = [&]() { count++; };\n  x.try_drive();\n  EXPECT_EQ(count, 0);\n  x.add(f1);\n  x.try_drive();\n  EXPECT_EQ(count, 1);\n}\n\nTEST(TimedDrivableExecutor, tryDriveFor) {\n  size_t count = 0;\n  TimedDrivableExecutor x;\n  auto f1 = [&]() { count++; };\n  x.try_drive_for(std::chrono::milliseconds(100));\n  EXPECT_EQ(count, 0);\n  x.add(f1);\n  x.try_drive_for(std::chrono::milliseconds(100));\n  EXPECT_EQ(count, 1);\n}\n\nTEST(TimedDrivableExecutor, tryDriveUntil) {\n  size_t count = 0;\n  TimedDrivableExecutor x;\n  auto f1 = [&]() { count++; };\n  x.try_drive_until(\n      std::chrono::system_clock::now() + std::chrono::milliseconds(100));\n  EXPECT_EQ(count, 0);\n  x.add(f1);\n  x.try_drive_until(\n      std::chrono::system_clock::now() + std::chrono::milliseconds(100));\n  EXPECT_EQ(count, 1);\n}\n"
  },
  {
    "path": "folly/executors/test/TimekeeperScheduledExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/TimekeeperScheduledExecutor.h>\n\n#include <atomic>\n#include <chrono>\n\n#include <folly/executors/InlineExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/executors/ThreadedExecutor.h>\n#include <folly/futures/ManualTimekeeper.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing folly::ManualTimekeeper;\nusing folly::TimekeeperScheduledExecutor;\n\nnamespace {\nvoid simpleTest(std::unique_ptr<folly::Executor> const& parent) {\n  auto tk = std::make_shared<ManualTimekeeper>();\n  auto executor = TimekeeperScheduledExecutor::create(\n      folly::getKeepAliveToken(parent.get()), [tk]() { return tk; });\n\n  // Test add()\n  constexpr int reps = 20;\n  std::atomic<int> repsLeft(reps), sum(0);\n  folly::Baton<> finished;\n  int expectedSum = 0;\n  for (int i = 0; i < reps; ++i) {\n    executor->add([i, &sum, &repsLeft, &finished] {\n      sum += i;\n      if (--repsLeft == 0) {\n        finished.post();\n      }\n    });\n    EXPECT_EQ(tk->numScheduled(), 0);\n    expectedSum += i;\n  }\n  finished.wait();\n  EXPECT_EQ(sum, expectedSum);\n\n  // Test scheduleAt()\n  finished.reset();\n  executor->scheduleAt(\n      [&finished]() { finished.post(); }, tk->now() + std::chrono::seconds(2));\n  EXPECT_EQ(tk->numScheduled(), 1);\n  EXPECT_FALSE(finished.ready());\n  tk->advance(std::chrono::seconds(1));\n  EXPECT_EQ(tk->numScheduled(), 1);\n  EXPECT_FALSE(finished.ready());\n  tk->advance(std::chrono::seconds(1));\n  EXPECT_EQ(tk->numScheduled(), 0);\n  finished.wait();\n}\n\nTEST(TimekeeperScheduledExecutor, SimpleThreaded) {\n  simpleTest(std::make_unique<folly::ThreadedExecutor>());\n}\n\nTEST(TimekeeperScheduledExecutor, SimpleInline) {\n  simpleTest(std::make_unique<folly::InlineExecutor>());\n}\n\nTEST(TimekeeperScheduledExecutor, Afterlife) {\n  auto grandparent = std::make_unique<folly::ThreadedExecutor>();\n  auto parent =\n      folly::SerialExecutor::create(getKeepAliveToken(grandparent.get()));\n  auto executor = TimekeeperScheduledExecutor::create(\n      folly::getKeepAliveToken(parent.get()));\n\n  folly::Baton<> startBaton;\n  executor->add([&startBaton] { startBaton.wait(); });\n\n  constexpr int reps = 20;\n  std::atomic<int> sum(0);\n  int expectedSum = 0;\n  for (int i = 0; i < reps; ++i) {\n    executor->add([i, &sum] { sum += i; });\n    expectedSum += i;\n  }\n\n  folly::Baton<> finishedBaton;\n  executor->add([&finishedBaton] { finishedBaton.post(); });\n\n  // drop our reference to TimekeeperScheduledExecutor\n  executor.reset();\n  // Verify no tasks have started yet\n  EXPECT_EQ(sum, 0);\n\n  // now kick off the tasks\n  startBaton.post();\n\n  // wait until last task has executed\n  finishedBaton.wait();\n\n  EXPECT_EQ(sum, expectedSum);\n}\n\nvoid RecursiveAddTest(std::unique_ptr<folly::Executor> const& grandparent) {\n  auto parent =\n      folly::SerialExecutor::create(getKeepAliveToken(grandparent.get()));\n  auto executor = TimekeeperScheduledExecutor::create(\n      folly::getKeepAliveToken(parent.get()));\n\n  folly::Baton<> finishedBaton;\n  int i = 0, sum = 0;\n  std::function<void()> lambda = [&]() {\n    if (i < 10) {\n      executor->add(lambda);\n    } else if (i < 12) {\n      // Below we will post this lambda three times to the executor. When\n      // executed, the lambda will re-post itself during the first ten\n      // executions. Afterwards we do nothing twice (this else-branch), and\n      // then on the 13th execution signal that we are finished.\n    } else {\n      finishedBaton.post();\n      return;\n    }\n    sum += ++i;\n  };\n\n  executor->add(lambda);\n  executor->add(lambda);\n  executor->add(lambda);\n\n  // wait until last task has executed\n  finishedBaton.wait();\n\n  EXPECT_EQ(sum, 78);\n}\n\nTEST(TimekeeperScheduledExecutor, RecursiveAdd) {\n  RecursiveAddTest(std::make_unique<folly::ThreadedExecutor>());\n}\n\nTEST(TimekeeperScheduledExecutor, RecursiveAddInline) {\n  RecursiveAddTest(std::make_unique<folly::InlineExecutor>());\n}\n\nTEST(TimekeeperScheduledExecutor, ExecutionThrows) {\n  auto parent = std::make_unique<folly::ThreadedExecutor>();\n  auto executor =\n      TimekeeperScheduledExecutor::create(getKeepAliveToken(parent.get()));\n  // An empty Func will throw std::bad_function_call when invoked,\n  // but TimekeeperScheduledExecutor should catch that exception.\n  executor->add(folly::Func{});\n}\n\nTEST(TimekeeperScheduledExecutor, NoTimekeeper) {\n  auto parent = std::make_unique<folly::ThreadedExecutor>();\n  // A TimekeeperScheduledExecutor that can't access its Timekeeper\n  //   should throw the appropriate exception.\n  auto executor = TimekeeperScheduledExecutor::create(\n      getKeepAliveToken(parent.get()), []() { return nullptr; });\n  executor->add([]() {});\n  EXPECT_THROW(\n      executor->schedule([]() {}, std::chrono::seconds(9)),\n      folly::TimekeeperScheduledExecutorNoTimekeeper);\n}\n\n} // namespace\n"
  },
  {
    "path": "folly/executors/thread_factory/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"init_thread_factory\",\n    headers = [\"InitThreadFactory.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":thread_factory\",\n        \"//folly:scope_guard\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"priority_thread_factory\",\n    srcs = [\"PriorityThreadFactory.cpp\"],\n    headers = [\"PriorityThreadFactory.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:string\",\n        \"//folly/portability:sys_resource\",\n        \"//folly/portability:sys_time\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \":init_thread_factory\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"thread_factory\",\n    headers = [\"ThreadFactory.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"named_thread_factory\",\n    headers = [\"NamedThreadFactory.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":thread_factory\",\n        \"//folly:conv\",\n        \"//folly:range\",\n        \"//folly/system:thread_name\",\n    ],\n)\n"
  },
  {
    "path": "folly/executors/thread_factory/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME init_thread_factory\n  HEADERS\n    InitThreadFactory.h\n  EXPORTED_DEPS\n    folly_executors_thread_factory_thread_factory\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME named_thread_factory\n  HEADERS\n    NamedThreadFactory.h\n  EXPORTED_DEPS\n    folly_conv\n    folly_executors_thread_factory_thread_factory\n    folly_range\n    folly_system_thread_name\n)\n\nfolly_add_library(\n  NAME priority_thread_factory\n  SRCS\n    PriorityThreadFactory.cpp\n  HEADERS\n    PriorityThreadFactory.h\n  DEPS\n    folly_portability_sys_resource\n    folly_portability_sys_time\n    folly_string\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_executors_thread_factory_init_thread_factory\n)\n\nfolly_add_library(\n  NAME thread_factory\n  HEADERS\n    ThreadFactory.h\n  EXPORTED_DEPS\n    folly_executor\n)\n"
  },
  {
    "path": "folly/executors/thread_factory/InitThreadFactory.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <thread>\n\n#include <folly/ScopeGuard.h>\n#include <folly/executors/thread_factory/ThreadFactory.h>\n\nnamespace folly {\n\nclass InitThreadFactory : public ThreadFactory {\n public:\n  explicit InitThreadFactory(\n      std::shared_ptr<ThreadFactory> threadFactory,\n      Func&& threadInitializer,\n      Func&& threadFinializer = [] {})\n      : threadFactory_(std::move(threadFactory)),\n        threadInitFini_(\n            std::make_shared<ThreadInitFini>(\n                std::move(threadInitializer), std::move(threadFinializer))) {}\n\n  std::thread newThread(Func&& func) override {\n    return threadFactory_->newThread(\n        [func = std::move(func), threadInitFini = threadInitFini_]() mutable {\n          threadInitFini->initializer();\n          SCOPE_EXIT {\n            threadInitFini->finalizer();\n          };\n          func();\n        });\n  }\n\n  const std::string& getNamePrefix() const override {\n    return threadFactory_->getNamePrefix();\n  }\n\n private:\n  std::shared_ptr<ThreadFactory> threadFactory_;\n  struct ThreadInitFini {\n    ThreadInitFini(Func&& init, Func&& fini)\n        : initializer(std::move(init)), finalizer(std::move(fini)) {}\n\n    Func initializer;\n    Func finalizer;\n  };\n  std::shared_ptr<ThreadInitFini> threadInitFini_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/thread_factory/NamedThreadFactory.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <string>\n#include <thread>\n\n#include <folly/Conv.h>\n#include <folly/Range.h>\n#include <folly/executors/thread_factory/ThreadFactory.h>\n#include <folly/system/ThreadName.h>\n\nnamespace folly {\n\nclass NamedThreadFactory : public ThreadFactory {\n public:\n  explicit NamedThreadFactory(folly::StringPiece prefix)\n      : prefix_(prefix.str()), suffix_(0) {}\n\n  std::thread newThread(Func&& func) override {\n    auto name = folly::to<std::string>(prefix_, suffix_++);\n    return std::thread(\n        [func_2 = std::move(func), name_2 = std::move(name)]() mutable {\n          folly::setThreadName(name_2);\n          func_2();\n        });\n  }\n\n  void setNamePrefix(folly::StringPiece prefix) { prefix_ = prefix.str(); }\n\n  const std::string& getNamePrefix() const override { return prefix_; }\n\n protected:\n  std::string prefix_;\n  std::atomic<uint64_t> suffix_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/thread_factory/PriorityThreadFactory.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/thread_factory/PriorityThreadFactory.h>\n\n#include <glog/logging.h>\n#include <folly/String.h>\n#include <folly/portability/SysResource.h>\n#include <folly/portability/SysTime.h>\n#include <folly/system/ThreadName.h>\n\nnamespace folly {\n\nPriorityThreadFactory::PriorityThreadFactory(\n    std::shared_ptr<ThreadFactory> factory, int priority)\n    : InitThreadFactory(std::move(factory), [priority] {\n        if (setpriority(PRIO_PROCESS, 0, priority) == 0) {\n          return;\n        }\n        int errnoCopy = errno;\n        auto message = [&](std::ostream& os) {\n          // Likely cause of failure is lacking the necessary permissions to\n          // change thread priority; note that we may need higher permissions\n          // even if trying to set the default priority while the current\n          // priority is lower (niced), for example if the thread is spawned\n          // from a lower-priority thread.\n          os << \"setpriority(\" << priority << \") on thread \\\"\"\n             << folly::getCurrentThreadName().value_or(\"<unknown>\") << \"\\\" \"\n             << \"failed with error \" << errnoCopy << \" (\" << errnoStr(errnoCopy)\n             << \")\";\n\n          errno = 0;\n          if (int p = getpriority(PRIO_PROCESS, 0); p != -1 || errno == 0) {\n            os << \". Current priority: \" << p;\n          }\n        };\n        message(LOG(WARNING));\n      }) {}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/thread_factory/PriorityThreadFactory.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/executors/thread_factory/InitThreadFactory.h>\n\nnamespace folly {\n\n/**\n * A ThreadFactory that sets nice values for each thread.  The main\n * use case for this class is if there are multiple\n * CPUThreadPoolExecutors in a single process, or between multiple\n * processes, where some should have a higher priority than the others.\n *\n * Note that per-thread nice values are not POSIX standard, but both\n * pthreads and linux support per-thread nice.  The default linux\n * scheduler uses these values to do smart thread prioritization.\n * sched_priority function calls only affect real-time schedulers.\n */\nclass PriorityThreadFactory : public InitThreadFactory {\n public:\n  PriorityThreadFactory(std::shared_ptr<ThreadFactory> factory, int priority);\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/executors/thread_factory/ThreadFactory.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <thread>\n\n#include <folly/Executor.h>\n\nnamespace folly {\n\nclass ThreadFactory {\n public:\n  ThreadFactory() = default;\n\n  virtual ~ThreadFactory() = default;\n  virtual std::thread newThread(Func&& func) = 0;\n\n  virtual const std::string& getNamePrefix() const = 0;\n\n private:\n  ThreadFactory(const ThreadFactory&) = delete;\n  ThreadFactory& operator=(const ThreadFactory&) = delete;\n  ThreadFactory(ThreadFactory&&) = delete;\n  ThreadFactory& operator=(ThreadFactory&&) = delete;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/experimental/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"event_count\",\n    headers = [\n        \"EventCount.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/synchronization:event_count\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"flat_combining_priority_queue\",\n    headers = [\n        \"FlatCombiningPriorityQueue.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/concurrency/container:flat_combining_priority_queue\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"function_scheduler\",\n    headers = [\n        \"FunctionScheduler.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/executors:function_scheduler\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"threaded_repeating_function_runner\",\n    headers = [\n        \"ThreadedRepeatingFunctionRunner.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/executors:threaded_repeating_function_runner\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME event_count\n  HEADERS\n    EventCount.h\n  EXPORTED_DEPS\n    folly_synchronization_event_count\n)\n\nfolly_add_library(\n  NAME flat_combining_priority_queue\n  HEADERS\n    FlatCombiningPriorityQueue.h\n  EXPORTED_DEPS\n    folly_concurrency_container_flat_combining_priority_queue\n)\n\nfolly_add_library(\n  NAME function_scheduler\n  HEADERS\n    FunctionScheduler.h\n  EXPORTED_DEPS\n    folly_executors_function_scheduler\n)\n\nfolly_add_library(\n  NAME threaded_repeating_function_runner\n  HEADERS\n    ThreadedRepeatingFunctionRunner.h\n  EXPORTED_DEPS\n    folly_executors_threaded_repeating_function_runner\n)\n\nadd_subdirectory(channels)\nadd_subdirectory(coro)\nadd_subdirectory(crypto)\nadd_subdirectory(exception_tracer)\nadd_subdirectory(flat_combining)\nadd_subdirectory(observer)\nadd_subdirectory(settings)\nadd_subdirectory(symbolizer)\n"
  },
  {
    "path": "folly/experimental/EventCount.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/synchronization/EventCount.h> // @shim\n"
  },
  {
    "path": "folly/experimental/FlatCombiningPriorityQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/concurrency/container/FlatCombiningPriorityQueue.h> // @shim\n"
  },
  {
    "path": "folly/experimental/FunctionScheduler.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/FunctionScheduler.h> // @shim\n"
  },
  {
    "path": "folly/experimental/ThreadedRepeatingFunctionRunner.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/ThreadedRepeatingFunctionRunner.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"channel\",\n    headers = [\n        \"Channel.h\",\n        \"Channel-fwd.h\",\n        \"Channel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"channel_callback_handle\",\n    headers = [\n        \"ChannelCallbackHandle.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel_callback_handle\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"channel_processor\",\n    headers = [\n        \"ChannelProcessor.h\",\n        \"ChannelProcessor-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:channel_processor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"consume_channel\",\n    headers = [\n        \"ConsumeChannel.h\",\n        \"ConsumeChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:consume_channel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"fanout_channel\",\n    headers = [\n        \"FanoutChannel.h\",\n        \"FanoutChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:fanout_channel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"fanout_sender\",\n    headers = [\n        \"FanoutSender.h\",\n        \"FanoutSender-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:fanout_sender\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"merge\",\n    headers = [\n        \"Merge.h\",\n        \"Merge-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:merge\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"merge_channel\",\n    headers = [\n        \"MergeChannel.h\",\n        \"MergeChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:merge_channel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"multiplex_channel\",\n    headers = [\n        \"MultiplexChannel.h\",\n        \"MultiplexChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:multiplex_channel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"on_closed_exception\",\n    headers = [\n        \"OnClosedException.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:on_closed_exception\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"producer\",\n    headers = [\n        \"Producer.h\",\n        \"Producer-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:producer\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"proxy_channel\",\n    headers = [\n        \"ProxyChannel.h\",\n        \"ProxyChannel-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:proxy_channel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"rate_limiter\",\n    headers = [\n        \"RateLimiter.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:rate_limiter\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"max_concurrent_rate_limiter\",\n    headers = [\n        \"MaxConcurrentRateLimiter.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:max_concurrent_rate_limiter\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"transform\",\n    headers = [\n        \"Transform.h\",\n        \"Transform-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels:transform\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/channels/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME channel\n  HEADERS\n    Channel-fwd.h\n    Channel-inl.h\n    Channel.h\n  EXPORTED_DEPS\n    folly_channels_channel\n)\n\nfolly_add_library(\n  NAME channel_callback_handle\n  HEADERS\n    ChannelCallbackHandle.h\n  EXPORTED_DEPS\n    folly_channels_channel_callback_handle\n)\n\nfolly_add_library(\n  NAME channel_processor\n  HEADERS\n    ChannelProcessor-inl.h\n    ChannelProcessor.h\n  EXPORTED_DEPS\n    folly_channels_channel_processor\n)\n\nfolly_add_library(\n  NAME consume_channel\n  HEADERS\n    ConsumeChannel-inl.h\n    ConsumeChannel.h\n  EXPORTED_DEPS\n    folly_channels_consume_channel\n)\n\nfolly_add_library(\n  NAME fanout_channel\n  HEADERS\n    FanoutChannel-inl.h\n    FanoutChannel.h\n  EXPORTED_DEPS\n    folly_channels_fanout_channel\n)\n\nfolly_add_library(\n  NAME fanout_sender\n  HEADERS\n    FanoutSender-inl.h\n    FanoutSender.h\n  EXPORTED_DEPS\n    folly_channels_fanout_sender\n)\n\nfolly_add_library(\n  NAME max_concurrent_rate_limiter\n  HEADERS\n    MaxConcurrentRateLimiter.h\n  EXPORTED_DEPS\n    folly_channels_max_concurrent_rate_limiter\n)\n\nfolly_add_library(\n  NAME merge\n  HEADERS\n    Merge-inl.h\n    Merge.h\n  EXPORTED_DEPS\n    folly_channels_merge\n)\n\nfolly_add_library(\n  NAME merge_channel\n  HEADERS\n    MergeChannel-inl.h\n    MergeChannel.h\n  EXPORTED_DEPS\n    folly_channels_merge_channel\n)\n\nfolly_add_library(\n  NAME multiplex_channel\n  HEADERS\n    MultiplexChannel-inl.h\n    MultiplexChannel.h\n  EXPORTED_DEPS\n    folly_channels_multiplex_channel\n)\n\nfolly_add_library(\n  NAME on_closed_exception\n  HEADERS\n    OnClosedException.h\n  EXPORTED_DEPS\n    folly_channels_on_closed_exception\n)\n\nfolly_add_library(\n  NAME producer\n  HEADERS\n    Producer-inl.h\n    Producer.h\n  EXPORTED_DEPS\n    folly_channels_producer\n)\n\nfolly_add_library(\n  NAME proxy_channel\n  HEADERS\n    ProxyChannel-inl.h\n    ProxyChannel.h\n  EXPORTED_DEPS\n    folly_channels_proxy_channel\n)\n\nfolly_add_library(\n  NAME rate_limiter\n  HEADERS\n    RateLimiter.h\n  EXPORTED_DEPS\n    folly_channels_rate_limiter\n)\n\nfolly_add_library(\n  NAME transform\n  HEADERS\n    Transform-inl.h\n    Transform.h\n  EXPORTED_DEPS\n    folly_channels_transform\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/experimental/channels/Channel-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Channel-fwd.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Channel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Channel-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Channel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Channel.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/ChannelCallbackHandle.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ChannelCallbackHandle.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/ChannelProcessor-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ChannelProcessor-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/ChannelProcessor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ChannelProcessor.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/ConsumeChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ConsumeChannel-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/ConsumeChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ConsumeChannel.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/FanoutChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/FanoutChannel-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/FanoutChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/FanoutChannel.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/FanoutSender-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/FanoutSender-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/FanoutSender.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/FanoutSender.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/MaxConcurrentRateLimiter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/MaxConcurrentRateLimiter.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Merge-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Merge-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Merge.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Merge.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/MergeChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/MergeChannel-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/MergeChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/MergeChannel.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/MultiplexChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/MultiplexChannel-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/MultiplexChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/MultiplexChannel.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/OnClosedException.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/OnClosedException.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Producer-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Producer-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Producer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Producer.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/ProxyChannel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ProxyChannel-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/ProxyChannel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/ProxyChannel.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/RateLimiter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/RateLimiter.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Transform-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Transform-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/Transform.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/Transform.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/detail/AtomicQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/detail/AtomicQueue.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"atomic_queue\",\n    headers = [\n        \"AtomicQueue.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/channels/detail:atomic_queue\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"channel_bridge\",\n    headers = [\n        \"ChannelBridge.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/channels/detail:channel_bridge\",\n    ],\n)\n\n# !!!! fbcode/folly/experimental/channels/detail/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"intrusive_ptr\",\n    headers = [\n        \"IntrusivePtr.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels/detail:intrusive_ptr\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"multiplexer_traits\",\n    headers = [\n        \"MultiplexerTraits.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels/detail:multiplexer_traits\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"pointer_variant\",\n    headers = [\n        \"PointerVariant.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels/detail:pointer_variant\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"utility\",\n    headers = [\n        \"Utility.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels/detail:utility\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/channels/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME atomic_queue\n  HEADERS\n    AtomicQueue.h\n  EXPORTED_DEPS\n    folly_channels_detail_atomic_queue\n)\n\nfolly_add_library(\n  NAME channel_bridge\n  HEADERS\n    ChannelBridge.h\n  EXPORTED_DEPS\n    folly_channels_detail_channel_bridge\n)\n\nfolly_add_library(\n  NAME intrusive_ptr\n  HEADERS\n    IntrusivePtr.h\n  EXPORTED_DEPS\n    folly_channels_detail_intrusive_ptr\n)\n\nfolly_add_library(\n  NAME multiplexer_traits\n  HEADERS\n    MultiplexerTraits.h\n  EXPORTED_DEPS\n    folly_channels_detail_multiplexer_traits\n)\n\nfolly_add_library(\n  NAME pointer_variant\n  HEADERS\n    PointerVariant.h\n  EXPORTED_DEPS\n    folly_channels_detail_pointer_variant\n)\n\nfolly_add_library(\n  NAME utility\n  HEADERS\n    Utility.h\n  EXPORTED_DEPS\n    folly_channels_detail_utility\n)\n"
  },
  {
    "path": "folly/experimental/channels/detail/ChannelBridge.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/detail/ChannelBridge.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/detail/FunctionTraits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/detail/FunctionTraits.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/detail/IntrusivePtr.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/detail/IntrusivePtr.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/detail/MultiplexerTraits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/detail/MultiplexerTraits.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/detail/PointerVariant.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/detail/PointerVariant.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/detail/Utility.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/detail/Utility.h> // @shim\n"
  },
  {
    "path": "folly/experimental/channels/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"channel_test_util\",\n    headers = [\n        \"ChannelTestUtil.h\",\n    ],\n    exported_deps = [\n        \"//folly/channels/test:channel_test_util\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/channels/test/ChannelTestUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/channels/test/ChannelTestUtil.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/AsyncGenerator.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncGenerator.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/AsyncPipe.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncPipe.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/AsyncScope.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncScope.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/AsyncStack.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AsyncStack.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/AutoCleanup-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AutoCleanup-fwd.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/AutoCleanup.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/AutoCleanup.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"async_generator\",\n    raw_headers = [\"AsyncGenerator.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:async_generator\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"async_pipe\",\n    raw_headers = [\"AsyncPipe.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:async_pipe\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"async_scope\",\n    raw_headers = [\"AsyncScope.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:async_scope\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"async_stack\",\n    raw_headers = [\n        \"AsyncStack.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:async_stack\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"baton\",\n    raw_headers = [\"Baton.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:baton\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"blocking_wait\",\n    raw_headers = [\"BlockingWait.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:blocking_wait\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"collect\",\n    raw_headers = [\n        \"Collect.h\",\n        \"Collect-inl.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:collect\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"concat\",\n    raw_headers = [\n        \"Concat.h\",\n        \"Concat-inl.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:concat\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"coroutine\",\n    raw_headers = [\n        \"Coroutine.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:coroutine\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"current_executor\",\n    raw_headers = [\"CurrentExecutor.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:current_executor\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detach_on_cancel\",\n    raw_headers = [\"DetachOnCancel.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detach_on_cancel\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detail_barrier\",\n    raw_headers = [\"detail/Barrier.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detail_barrier\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detail_barrier_task\",\n    raw_headers = [\"detail/BarrierTask.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detail_barrier_task\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detail_current_async_frame\",\n    raw_headers = [\"detail/CurrentAsyncFrame.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detail_current_async_frame\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detail_helpers\",\n    raw_headers = [\"detail/Helpers.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detail_helpers\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detail_malloc\",\n    raw_headers = [\"detail/Malloc.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detail_malloc\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detail_manual_lifetime\",\n    raw_headers = [\"detail/ManualLifetime.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detail_manual_lifetime\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"detail_traits\",\n    raw_headers = [\"detail/Traits.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:detail_traits\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"filter\",\n    raw_headers = [\n        \"Filter.h\",\n        \"Filter-inl.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:filter\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"future_util\",\n    raw_headers = [\"FutureUtil.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:future_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"generator\",\n    raw_headers = [\"Generator.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:generator\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"gtest_helpers\",\n    raw_headers = [\"GtestHelpers.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:gtest_helpers\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"gmock_helpers\",\n    raw_headers = [\"GmockHelpers.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:gmock_helpers\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"inline_task\",\n    raw_headers = [\"detail/InlineTask.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:inline_task\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"invoke\",\n    raw_headers = [\"Invoke.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:invoke\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"merge\",\n    raw_headers = [\n        \"Merge.h\",\n        \"Merge-inl.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:merge\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"mutex\",\n    raw_headers = [\"Mutex.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:mutex\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"promise\",\n    raw_headers = [\"Promise.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:promise\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"shared_promise\",\n    raw_headers = [\"SharedPromise.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:shared_promise\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"result\",\n    raw_headers = [\"Result.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:result\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"retry\",\n    raw_headers = [\"Retry.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:retry\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"scope_exit\",\n    raw_headers = [\"ScopeExit.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:scope_exit\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"shared_lock\",\n    raw_headers = [\"SharedLock.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:shared_lock\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"shared_mutex\",\n    raw_headers = [\"SharedMutex.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:shared_mutex\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"sleep\",\n    raw_headers = [\n        \"Sleep.h\",\n        \"Sleep-inl.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:sleep\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"small_unbounded_queue\",\n    raw_headers = [\"SmallUnboundedQueue.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:small_unbounded_queue\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"task\",\n    raw_headers = [\"Task.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:task\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"timed_wait\",\n    raw_headers = [\"TimedWait.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:timed_wait\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"timeout\",\n    raw_headers = [\n        \"Timeout.h\",\n        \"Timeout-inl.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:timeout\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"traits\",\n    raw_headers = [\"Traits.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:traits\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"transform\",\n    raw_headers = [\n        \"Transform.h\",\n        \"Transform-inl.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:transform\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"bounded_queue\",\n    raw_headers = [\"BoundedQueue.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:bounded_queue\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"unbounded_queue\",\n    raw_headers = [\"UnboundedQueue.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:unbounded_queue\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"via_if_async\",\n    raw_headers = [\"ViaIfAsync.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:via_if_async\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"with_async_stack\",\n    raw_headers = [\"WithAsyncStack.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:with_async_stack\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"with_cancellation\",\n    raw_headers = [\"WithCancellation.h\"],\n    exported_deps = [\n        \"//xplat/folly/coro:with_cancellation\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"cleanup\",\n    raw_headers = [\n        \"Cleanup.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:cleanup\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"auto_cleanup_fwd\",\n    raw_headers = [\n        \"AutoCleanup-fwd.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:auto_cleanup_fwd\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"auto_cleanup\",\n    raw_headers = [\n        \"AutoCleanup.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/coro:auto_cleanup\",\n    ],\n)\n\n# !!!! fbcode/folly/experimental/coro/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"async_generator\",\n    headers = [\n        \"AsyncGenerator.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:async_generator\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"async_pipe\",\n    headers = [\n        \"AsyncPipe.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:async_pipe\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"async_scope\",\n    headers = [\n        \"AsyncScope.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:async_scope\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"async_stack\",\n    headers = [\n        \"AsyncStack.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:async_stack\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"baton\",\n    headers = [\n        \"Baton.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"blocking_wait\",\n    headers = [\n        \"BlockingWait.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:blocking_wait\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"collect\",\n    headers = [\n        \"Collect.h\",\n        \"Collect-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:collect\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"concat\",\n    headers = [\n        \"Concat.h\",\n        \"Concat-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:concat\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"coroutine\",\n    headers = [\n        \"Coroutine.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:coroutine\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"current_executor\",\n    headers = [\n        \"CurrentExecutor.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:current_executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detach_on_cancel\",\n    headers = [\n        \"DetachOnCancel.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detach_on_cancel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detail_barrier\",\n    headers = [\n        \"detail/Barrier.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detail_barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detail_barrier_task\",\n    headers = [\n        \"detail/BarrierTask.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detail_barrier_task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detail_current_async_frame\",\n    headers = [\n        \"detail/CurrentAsyncFrame.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detail_current_async_frame\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detail_helpers\",\n    headers = [\n        \"detail/Helpers.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detail_helpers\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detail_malloc\",\n    headers = [\n        \"detail/Malloc.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detail_malloc\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detail_manual_lifetime\",\n    headers = [\n        \"detail/ManualLifetime.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detail_manual_lifetime\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"detail_traits\",\n    headers = [\n        \"detail/Traits.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:detail_traits\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"filter\",\n    headers = [\n        \"Filter.h\",\n        \"Filter-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:filter\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"future_util\",\n    headers = [\n        \"FutureUtil.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:future_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"generator\",\n    headers = [\n        \"Generator.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:generator\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"gmock_helpers\",\n    headers = [\n        \"GmockHelpers.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:gmock_helpers\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"gtest_helpers\",\n    headers = [\n        \"GtestHelpers.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:gtest_helpers\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"inline_task\",\n    headers = [\n        \"detail/InlineTask.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:inline_task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"invoke\",\n    headers = [\n        \"Invoke.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:invoke\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"merge\",\n    headers = [\n        \"Merge.h\",\n        \"Merge-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:merge\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"mutex\",\n    headers = [\n        \"Mutex.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:mutex\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"promise\",\n    headers = [\n        \"Promise.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:promise\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"result\",\n    headers = [\n        \"Result.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:result\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"retry\",\n    headers = [\n        \"Retry.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:retry\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"rust_adaptors\",\n    headers = [\n        \"RustAdaptors.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:rust_adaptors\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"scope_exit\",\n    headers = [\n        \"ScopeExit.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:scope_exit\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"shared_lock\",\n    headers = [\n        \"SharedLock.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:shared_lock\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"shared_mutex\",\n    headers = [\n        \"SharedMutex.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:shared_mutex\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"sleep\",\n    headers = [\n        \"Sleep.h\",\n        \"Sleep-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:sleep\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"small_unbounded_queue\",\n    headers = [\n        \"SmallUnboundedQueue.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:small_unbounded_queue\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"task\",\n    headers = [\n        \"Task.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"timed_wait\",\n    headers = [\n        \"TimedWait.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:timed_wait\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"timeout\",\n    headers = [\n        \"Timeout.h\",\n        \"Timeout-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:timeout\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"traits\",\n    headers = [\n        \"Traits.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:traits\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"transform\",\n    headers = [\n        \"Transform.h\",\n        \"Transform-inl.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:transform\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"unbounded_queue\",\n    headers = [\n        \"UnboundedQueue.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:unbounded_queue\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"via_if_async\",\n    headers = [\n        \"ViaIfAsync.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:via_if_async\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"with_async_stack\",\n    headers = [\n        \"WithAsyncStack.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:with_async_stack\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"with_cancellation\",\n    headers = [\n        \"WithCancellation.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:with_cancellation\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"bounded_queue\",\n    headers = [\n        \"BoundedQueue.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:bounded_queue\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"shared_promise\",\n    headers = [\n        \"SharedPromise.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:shared_promise\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"cleanup\",\n    headers = [\n        \"Cleanup.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:cleanup\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"auto_cleanup_fwd\",\n    headers = [\n        \"AutoCleanup-fwd.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:auto_cleanup_fwd\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"auto_cleanup\",\n    headers = [\n        \"AutoCleanup.h\",\n    ],\n    exported_deps = [\n        \"//folly/coro:auto_cleanup\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/coro/Baton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Baton.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/BlockingWait.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/BlockingWait.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/BoundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/BoundedQueue.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async_generator\n  HEADERS\n    AsyncGenerator.h\n  EXPORTED_DEPS\n    folly_coro_async_generator\n)\n\nfolly_add_library(\n  NAME async_pipe\n  HEADERS\n    AsyncPipe.h\n  EXPORTED_DEPS\n    folly_coro_async_pipe\n)\n\nfolly_add_library(\n  NAME async_scope\n  HEADERS\n    AsyncScope.h\n  EXPORTED_DEPS\n    folly_coro_async_scope\n)\n\nfolly_add_library(\n  NAME async_stack\n  HEADERS\n    AsyncStack.h\n  EXPORTED_DEPS\n    folly_coro_async_stack\n)\n\nfolly_add_library(\n  NAME auto_cleanup\n  HEADERS\n    AutoCleanup.h\n  EXPORTED_DEPS\n    folly_coro_auto_cleanup\n)\n\nfolly_add_library(\n  NAME auto_cleanup_fwd\n  HEADERS\n    AutoCleanup-fwd.h\n  EXPORTED_DEPS\n    folly_coro_auto_cleanup_fwd\n)\n\nfolly_add_library(\n  NAME baton\n  HEADERS\n    Baton.h\n  EXPORTED_DEPS\n    folly_coro_baton\n)\n\nfolly_add_library(\n  NAME blocking_wait\n  HEADERS\n    BlockingWait.h\n  EXPORTED_DEPS\n    folly_coro_blocking_wait\n)\n\nfolly_add_library(\n  NAME bounded_queue\n  HEADERS\n    BoundedQueue.h\n  EXPORTED_DEPS\n    folly_coro_bounded_queue\n)\n\nfolly_add_library(\n  NAME cleanup\n  HEADERS\n    Cleanup.h\n  EXPORTED_DEPS\n    folly_coro_cleanup\n)\n\nfolly_add_library(\n  NAME collect\n  HEADERS\n    Collect-inl.h\n    Collect.h\n  EXPORTED_DEPS\n    folly_coro_collect\n)\n\nfolly_add_library(\n  NAME concat\n  HEADERS\n    Concat-inl.h\n    Concat.h\n  EXPORTED_DEPS\n    folly_coro_concat\n)\n\nfolly_add_library(\n  NAME coroutine\n  HEADERS\n    Coroutine.h\n  EXPORTED_DEPS\n    folly_coro_coroutine\n)\n\nfolly_add_library(\n  NAME current_executor\n  HEADERS\n    CurrentExecutor.h\n  EXPORTED_DEPS\n    folly_coro_current_executor\n)\n\nfolly_add_library(\n  NAME detach_on_cancel\n  HEADERS\n    DetachOnCancel.h\n  EXPORTED_DEPS\n    folly_coro_detach_on_cancel\n)\n\nfolly_add_library(\n  NAME detail_barrier\n  HEADERS\n    detail/Barrier.h\n  EXPORTED_DEPS\n    folly_coro_detail_barrier\n)\n\nfolly_add_library(\n  NAME detail_barrier_task\n  HEADERS\n    detail/BarrierTask.h\n  EXPORTED_DEPS\n    folly_coro_detail_barrier_task\n)\n\nfolly_add_library(\n  NAME detail_current_async_frame\n  HEADERS\n    detail/CurrentAsyncFrame.h\n  EXPORTED_DEPS\n    folly_coro_detail_current_async_frame\n)\n\nfolly_add_library(\n  NAME detail_helpers\n  HEADERS\n    detail/Helpers.h\n  EXPORTED_DEPS\n    folly_coro_detail_helpers\n)\n\nfolly_add_library(\n  NAME detail_malloc\n  HEADERS\n    detail/Malloc.h\n  EXPORTED_DEPS\n    folly_coro_detail_malloc\n)\n\nfolly_add_library(\n  NAME detail_manual_lifetime\n  HEADERS\n    detail/ManualLifetime.h\n  EXPORTED_DEPS\n    folly_coro_detail_manual_lifetime\n)\n\nfolly_add_library(\n  NAME detail_traits\n  HEADERS\n    detail/Traits.h\n  EXPORTED_DEPS\n    folly_coro_detail_traits\n)\n\nfolly_add_library(\n  NAME filter\n  HEADERS\n    Filter-inl.h\n    Filter.h\n  EXPORTED_DEPS\n    folly_coro_filter\n)\n\nfolly_add_library(\n  NAME future_util\n  HEADERS\n    FutureUtil.h\n  EXPORTED_DEPS\n    folly_coro_future_util\n)\n\nfolly_add_library(\n  NAME generator\n  HEADERS\n    Generator.h\n  EXPORTED_DEPS\n    folly_coro_generator\n)\n\nfolly_add_library(\n  NAME gmock_helpers\n  HEADERS\n    GmockHelpers.h\n  EXPORTED_DEPS\n    folly_coro_gmock_helpers\n)\n\nfolly_add_library(\n  NAME gtest_helpers\n  HEADERS\n    GtestHelpers.h\n  EXPORTED_DEPS\n    folly_coro_gtest_helpers\n)\n\nfolly_add_library(\n  NAME inline_task\n  HEADERS\n    detail/InlineTask.h\n  EXPORTED_DEPS\n    folly_coro_inline_task\n)\n\nfolly_add_library(\n  NAME invoke\n  HEADERS\n    Invoke.h\n  EXPORTED_DEPS\n    folly_coro_invoke\n)\n\nfolly_add_library(\n  NAME merge\n  HEADERS\n    Merge-inl.h\n    Merge.h\n  EXPORTED_DEPS\n    folly_coro_merge\n)\n\nfolly_add_library(\n  NAME mutex\n  HEADERS\n    Mutex.h\n  EXPORTED_DEPS\n    folly_coro_mutex\n)\n\nfolly_add_library(\n  NAME promise\n  HEADERS\n    Promise.h\n  EXPORTED_DEPS\n    folly_coro_promise\n)\n\nfolly_add_library(\n  NAME result\n  HEADERS\n    Result.h\n  EXPORTED_DEPS\n    folly_coro_result\n)\n\nfolly_add_library(\n  NAME retry\n  HEADERS\n    Retry.h\n  EXPORTED_DEPS\n    folly_coro_retry\n)\n\nfolly_add_library(\n  NAME rust_adaptors\n  HEADERS\n    RustAdaptors.h\n  EXPORTED_DEPS\n    folly_coro_rust_adaptors\n)\n\nfolly_add_library(\n  NAME scope_exit\n  HEADERS\n    ScopeExit.h\n  EXPORTED_DEPS\n    folly_coro_scope_exit\n)\n\nfolly_add_library(\n  NAME shared_lock\n  HEADERS\n    SharedLock.h\n  EXPORTED_DEPS\n    folly_coro_shared_lock\n)\n\nfolly_add_library(\n  NAME shared_mutex\n  HEADERS\n    SharedMutex.h\n  EXPORTED_DEPS\n    folly_coro_shared_mutex\n)\n\nfolly_add_library(\n  NAME shared_promise\n  HEADERS\n    SharedPromise.h\n  EXPORTED_DEPS\n    folly_coro_shared_promise\n)\n\nfolly_add_library(\n  NAME sleep\n  HEADERS\n    Sleep-inl.h\n    Sleep.h\n  EXPORTED_DEPS\n    folly_coro_sleep\n)\n\nfolly_add_library(\n  NAME small_unbounded_queue\n  HEADERS\n    SmallUnboundedQueue.h\n  EXPORTED_DEPS\n    folly_coro_small_unbounded_queue\n)\n\nfolly_add_library(\n  NAME task\n  HEADERS\n    Task.h\n  EXPORTED_DEPS\n    folly_coro_task\n)\n\nfolly_add_library(\n  NAME timed_wait\n  HEADERS\n    TimedWait.h\n  EXPORTED_DEPS\n    folly_coro_timed_wait\n)\n\nfolly_add_library(\n  NAME timeout\n  HEADERS\n    Timeout-inl.h\n    Timeout.h\n  EXPORTED_DEPS\n    folly_coro_timeout\n)\n\nfolly_add_library(\n  NAME traits\n  HEADERS\n    Traits.h\n  EXPORTED_DEPS\n    folly_coro_traits\n)\n\nfolly_add_library(\n  NAME transform\n  HEADERS\n    Transform-inl.h\n    Transform.h\n  EXPORTED_DEPS\n    folly_coro_transform\n)\n\nfolly_add_library(\n  NAME unbounded_queue\n  HEADERS\n    UnboundedQueue.h\n  EXPORTED_DEPS\n    folly_coro_unbounded_queue\n)\n\nfolly_add_library(\n  NAME via_if_async\n  HEADERS\n    ViaIfAsync.h\n  EXPORTED_DEPS\n    folly_coro_via_if_async\n)\n\nfolly_add_library(\n  NAME with_async_stack\n  HEADERS\n    WithAsyncStack.h\n  EXPORTED_DEPS\n    folly_coro_with_async_stack\n)\n\nfolly_add_library(\n  NAME with_cancellation\n  HEADERS\n    WithCancellation.h\n  EXPORTED_DEPS\n    folly_coro_with_cancellation\n)\n"
  },
  {
    "path": "folly/experimental/coro/Cleanup.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Cleanup.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Collect-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Collect-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Collect.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Collect.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Concat-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Concat-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Concat.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Concat.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Coroutine.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Coroutine.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/CurrentExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/CurrentExecutor.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/DetachOnCancel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/DetachOnCancel.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Filter-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Filter-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Filter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Filter.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/FutureUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/FutureUtil.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Generator.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Generator.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/GmockHelpers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/GmockHelpers.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/GtestHelpers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/GtestHelpers.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Invoke.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Invoke.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Merge-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Merge-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Merge.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Merge.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Mutex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Mutex.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Promise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Promise.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Result.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Result.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Retry.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Retry.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/RustAdaptors.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/RustAdaptors.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/ScopeExit.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/ScopeExit.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/SharedLock.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/SharedLock.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/SharedMutex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/SharedMutex.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/SharedPromise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/SharedPromise.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Sleep-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Sleep-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Sleep.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Sleep.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/SmallUnboundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/SmallUnboundedQueue.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Task.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Task.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/TimedWait.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/TimedWait.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Timeout-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Timeout-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Timeout.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Timeout.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Traits.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Transform-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Transform-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/Transform.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/Transform.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/UnboundedQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/UnboundedQueue.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/ViaIfAsync.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/ViaIfAsync.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/WithAsyncStack.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/WithAsyncStack.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/WithCancellation.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/WithCancellation.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/Barrier.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/Barrier.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/BarrierTask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/BarrierTask.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/CurrentAsyncFrame.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/CurrentAsyncFrame.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/Helpers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/Helpers.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/InlineTask.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/InlineTask.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/Malloc.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/Malloc.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/ManualLifetime.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/ManualLifetime.h> // @shim\n"
  },
  {
    "path": "folly/experimental/coro/detail/Traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/coro/detail/Traits.h> // @shim\n"
  },
  {
    "path": "folly/experimental/crypto/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"lt_hash\",\n    headers = [\n        \"LtHash.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/crypto:lt_hash\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lt_hash_sse2\",\n    headers = [\n        \"LtHash.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/crypto:lt_hash\",\n        \"//folly/crypto:lt_hash_sse2\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"lt_hash_avx2\",\n    headers = [\n        \"LtHash.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/crypto:lt_hash\",\n        \"//folly/crypto:lt_hash_avx2\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"blake2xb\",\n    headers = [\n        \"Blake2xb.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/crypto:blake2xb\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/crypto/Blake2xb.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/crypto/Blake2xb.h> // @shim\n"
  },
  {
    "path": "folly/experimental/crypto/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME blake2xb\n  HEADERS\n    Blake2xb.h\n  EXPORTED_DEPS\n    folly_crypto_blake2xb\n)\n\nfolly_add_library(\n  NAME lt_hash\n  HEADERS\n    LtHash.h\n  EXPORTED_DEPS\n    folly_crypto_lt_hash\n)\n\nfolly_add_library(\n  NAME lt_hash_avx2\n  EXCLUDE_FROM_MONOLITH\n  HEADERS\n    LtHash.h\n  EXPORTED_DEPS\n    folly_crypto_lt_hash\n    folly_crypto_lt_hash_avx2\n)\n\nfolly_add_library(\n  NAME lt_hash_sse2\n  EXCLUDE_FROM_MONOLITH\n  HEADERS\n    LtHash.h\n  EXPORTED_DEPS\n    folly_crypto_lt_hash\n    folly_crypto_lt_hash_sse2\n)\n"
  },
  {
    "path": "folly/experimental/crypto/LtHash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/crypto/LtHash.h> // @shim\n"
  },
  {
    "path": "folly/experimental/exception_tracer/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\n    \"@fbsource//tools/build_defs:platform_defs.bzl\",\n    \"IOS\",\n    \"MACOSX\",\n)\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_tracer_base\",\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"ExceptionTracer.h\",\n    ],\n    deps = [\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"exception_abi\",\n    headers = [\"ExceptionAbi.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:exception_abi\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"smart_exception_tracer\",\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    raw_headers = [\n        \"SmartExceptionTracer.h\",\n    ],\n    deps = [\n        \"//xplat/folly/debugging/exception_tracer:smart_exception_tracer\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"smart_exception_tracer_singleton\",\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"SmartExceptionTracerSingleton.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/debugging/exception_tracer:smart_exception_tracer_singleton\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"smart_exception_stack_trace_hooks\",\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    deps = [\n        \"//xplat/folly/debugging/exception_tracer:smart_exception_stack_trace_hooks\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_counter\",\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    raw_headers = [\n        \"ExceptionCounterLib.h\",\n    ],\n    deps = [\n        \"//xplat/folly/debugging/exception_tracer:exception_counter\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_tracer\",\n    apple_sdks = (IOS, MACOSX),\n    link_whole = True,\n    deps = [\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"stacktrace\",\n    apple_sdks = (IOS, MACOSX),\n    raw_headers = [\n        \"StackTrace.h\",\n    ],\n    deps = [\n        \"//xplat/folly/debugging/exception_tracer:stacktrace\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"exception_tracer_callbacks\",\n    link_whole = True,\n    raw_headers = [\n        \"ExceptionTracerLib.h\",\n    ],\n    deps = [\n        \"//xplat/folly/debugging/exception_tracer:exception_tracer_callbacks\",\n    ],\n)\n\n# !!!! fbcode/folly/experimental/exception_tracer/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_counter\",\n    headers = [\"ExceptionCounterLib.h\"],\n    link_whole = True,\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:exception_counter\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer\",\n    headers = [],\n    link_whole = True,\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:exception_tracer\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer_base\",\n    headers = [\"ExceptionTracer.h\"],\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:exception_tracer_base\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"exception_tracer_callbacks\",\n    headers = [\"ExceptionTracerLib.h\"],\n    link_whole = True,\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:exception_tracer_callbacks\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"stacktrace\",\n    headers = [\"StackTrace.h\"],\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:stacktrace\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"smart_exception_tracer_singleton\",\n    headers = [\n        \"SmartExceptionTracerSingleton.h\",\n    ],\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:smart_exception_tracer_singleton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"smart_exception_stack_trace_hooks\",\n    link_whole = True,\n    deps = [\n        \"//folly/debugging/exception_tracer:smart_exception_stack_trace_hooks\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"smart_exception_tracer\",\n    headers = [\"SmartExceptionTracer.h\"],\n    exported_deps = [\n        \"//folly/debugging/exception_tracer:smart_exception_tracer\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/exception_tracer/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME exception_abi\n  HEADERS\n    ExceptionAbi.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_exception_abi\n)\n\nfolly_add_library(\n  NAME exception_counter\n  HEADERS\n    ExceptionCounterLib.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_exception_counter\n)\n\nfolly_add_library(\n  NAME exception_tracer\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_exception_tracer\n)\n\nfolly_add_library(\n  NAME exception_tracer_base\n  HEADERS\n    ExceptionTracer.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_exception_tracer_base\n)\n\nfolly_add_library(\n  NAME exception_tracer_callbacks\n  HEADERS\n    ExceptionTracerLib.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_exception_tracer_callbacks\n)\n\nfolly_add_library(\n  NAME smart_exception_stack_trace_hooks\n  DEPS\n    folly_debugging_exception_tracer_smart_exception_stack_trace_hooks\n)\n\nfolly_add_library(\n  NAME smart_exception_tracer\n  HEADERS\n    SmartExceptionTracer.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_smart_exception_tracer\n)\n\nfolly_add_library(\n  NAME smart_exception_tracer_singleton\n  HEADERS\n    SmartExceptionTracerSingleton.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_smart_exception_tracer_singleton\n)\n\nfolly_add_library(\n  NAME stacktrace\n  HEADERS\n    StackTrace.h\n  EXPORTED_DEPS\n    folly_debugging_exception_tracer_stacktrace\n)\n"
  },
  {
    "path": "folly/experimental/exception_tracer/ExceptionAbi.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionAbi.h> // @shim\n"
  },
  {
    "path": "folly/experimental/exception_tracer/ExceptionCounterLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionCounterLib.h> // @shim\n"
  },
  {
    "path": "folly/experimental/exception_tracer/ExceptionTracer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionTracer.h> // @shim\n"
  },
  {
    "path": "folly/experimental/exception_tracer/ExceptionTracerLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/ExceptionTracerLib.h> // @shim\n"
  },
  {
    "path": "folly/experimental/exception_tracer/SmartExceptionTracer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/SmartExceptionTracer.h> // @shim\n"
  },
  {
    "path": "folly/experimental/exception_tracer/SmartExceptionTracerSingleton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/SmartExceptionTracerSingleton.h> // @shim\n"
  },
  {
    "path": "folly/experimental/exception_tracer/StackTrace.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/exception_tracer/StackTrace.h> // @shim\n"
  },
  {
    "path": "folly/experimental/flat_combining/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"flat_combining\",\n    headers = [\n        \"FlatCombining.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/synchronization:flat_combining\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/flat_combining/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME flat_combining\n  HEADERS\n    FlatCombining.h\n  EXPORTED_DEPS\n    folly_synchronization_flat_combining\n)\n"
  },
  {
    "path": "folly/experimental/flat_combining/FlatCombining.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/synchronization/FlatCombining.h> // @shim\n"
  },
  {
    "path": "folly/experimental/flat_combining/test/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"flat_combining_examples\",\n    headers = [\n        \"FlatCombiningExamples.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/synchronization/test:flat_combining_examples\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"flat_combining_test_helpers\",\n    headers = [\n        \"FlatCombiningTestHelpers.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/synchronization/test:flat_combining_test_helpers\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/flat_combining/test/FlatCombiningExamples.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/synchronization/test/FlatCombiningExamples.h> // @shim\n"
  },
  {
    "path": "folly/experimental/flat_combining/test/FlatCombiningTestHelpers.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/synchronization/test/FlatCombiningTestHelpers.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"observable\",\n    headers = [\n        \"Observable.h\",\n        \"Observable-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/observer:observable\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"with_jitter\",\n    headers = [\n        \"WithJitter.h\",\n        \"WithJitter-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/observer:with_jitter\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"observer\",\n    headers = [\n        \"Observer.h\",\n        \"Observer-inl.h\",\n        \"Observer-pre.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/observer:observer\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"simple_observable\",\n    headers = [\n        \"SimpleObservable.h\",\n        \"SimpleObservable-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/observer:simple_observable\",\n    ],\n)\n\n# !!!! fbcode/folly/experimental/observer/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"hazptr_observer\",\n    headers = [\n        \"HazptrObserver.h\",\n    ],\n    exported_deps = [\n        \"//folly/observer:hazptr_observer\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"read_mostly_tl_observer\",\n    headers = [\n        \"ReadMostlyTLObserver.h\",\n    ],\n    exported_deps = [\n        \"//folly/observer:read_mostly_tl_observer\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"core_cached_observer\",\n    headers = [\n        \"CoreCachedObserver.h\",\n    ],\n    exported_deps = [\n        \"//folly/observer:core_cached_observer\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/observer/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME core_cached_observer\n  HEADERS\n    CoreCachedObserver.h\n  EXPORTED_DEPS\n    folly_observer_core_cached_observer\n)\n\nfolly_add_library(\n  NAME hazptr_observer\n  HEADERS\n    HazptrObserver.h\n  EXPORTED_DEPS\n    folly_observer_hazptr_observer\n)\n\nfolly_add_library(\n  NAME observable\n  HEADERS\n    Observable-inl.h\n    Observable.h\n  EXPORTED_DEPS\n    folly_observer_observable\n)\n\nfolly_add_library(\n  NAME observer\n  HEADERS\n    Observer-inl.h\n    Observer-pre.h\n    Observer.h\n  EXPORTED_DEPS\n    folly_observer_observer\n)\n\nfolly_add_library(\n  NAME read_mostly_tl_observer\n  HEADERS\n    ReadMostlyTLObserver.h\n  EXPORTED_DEPS\n    folly_observer_read_mostly_tl_observer\n)\n\nfolly_add_library(\n  NAME simple_observable\n  HEADERS\n    SimpleObservable-inl.h\n    SimpleObservable.h\n  EXPORTED_DEPS\n    folly_observer_simple_observable\n)\n\nfolly_add_library(\n  NAME with_jitter\n  HEADERS\n    WithJitter-inl.h\n    WithJitter.h\n  EXPORTED_DEPS\n    folly_observer_with_jitter\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/experimental/observer/CoreCachedObserver.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/CoreCachedObserver.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/HazptrObserver.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/HazptrObserver.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/Observable-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/Observable-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/Observable.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/Observable.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/Observer-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/Observer-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/Observer-pre.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/Observer-pre.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/Observer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/Observer.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/ReadMostlyTLObserver.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/ReadMostlyTLObserver.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/SimpleObservable-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/SimpleObservable-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/SimpleObservable.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/SimpleObservable.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/WithJitter-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/WithJitter-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/WithJitter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/WithJitter.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"../../../defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"observer_manager\",\n    force_static = False,\n    raw_headers = [\n        \"Core.h\",\n        \"ObserverManager.h\",\n    ],\n    exported_deps = [\n        \"//xplat/folly/observer/detail:observer_manager\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"graph_cycle_detector\",\n    headers = [\n        \"GraphCycleDetector.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/observer/detail:graph_cycle_detector\",\n    ],\n)\n\n# !!!! fbcode/folly/experimental/observer/detail/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"observer_manager\",\n    headers = [\n        \"Core.h\",\n        \"ObserverManager.h\",\n    ],\n    exported_deps = [\n        \"//folly/observer/detail:observer_manager\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/observer/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME graph_cycle_detector\n  HEADERS\n    GraphCycleDetector.h\n  EXPORTED_DEPS\n    folly_observer_detail_graph_cycle_detector\n)\n\nfolly_add_library(\n  NAME observer_manager\n  HEADERS\n    Core.h\n    ObserverManager.h\n  EXPORTED_DEPS\n    folly_observer_detail_observer_manager\n)\n"
  },
  {
    "path": "folly/experimental/observer/detail/Core.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/detail/Core.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/detail/GraphCycleDetector.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/detail/GraphCycleDetector.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/detail/ObserverManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/observer/detail/ObserverManager.h> // @shim\n"
  },
  {
    "path": "folly/experimental/observer/test/BUCK",
    "content": "oncall(\"fbcode_entropy_wardens_folly\")\n"
  },
  {
    "path": "folly/experimental/settings/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat build rules\n\nfb_dirsync_cpp_library(\n    name = \"settings\",\n    headers = [\n        \"Settings.h\",\n        \"detail/SettingsImpl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/settings:settings\",\n    ],\n)\n\n# fbcode build rules\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"types\",\n    headers = [\n        \"Types.h\",\n    ],\n    exported_deps = [\n        \"//folly/settings:types\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"immutables\",\n    headers = [\n        \"Immutables.h\",\n    ],\n    exported_deps = [\n        \"//folly/settings:immutables\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/settings/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME immutables\n  HEADERS\n    Immutables.h\n  EXPORTED_DEPS\n    folly_settings_immutables\n)\n\nfolly_add_library(\n  NAME settings\n  HEADERS\n    Settings.h\n    detail/SettingsImpl.h\n  EXPORTED_DEPS\n    folly_settings_settings\n)\n\nfolly_add_library(\n  NAME types\n  HEADERS\n    Types.h\n  EXPORTED_DEPS\n    folly_settings_types\n)\n"
  },
  {
    "path": "folly/experimental/settings/Immutables.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/settings/Immutables.h> // @shim\n"
  },
  {
    "path": "folly/experimental/settings/Settings.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/settings/Settings.h> // @shim\n"
  },
  {
    "path": "folly/experimental/settings/Types.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/settings/Types.h> // @shim\n"
  },
  {
    "path": "folly/experimental/settings/detail/SettingsImpl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/settings/detail/SettingsImpl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"dwarf\",\n    headers = [\n        \"Dwarf.h\",\n        \"DwarfImpl.h\",\n        \"DwarfLineNumberVM.h\",\n        \"DwarfSection.h\",\n        \"DwarfUtil.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer:dwarf\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"elf\",\n    headers = [\n        \"Elf.h\",\n        \"Elf-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer:elf\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"elf_cache\",\n    headers = [\n        \"ElfCache.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer:elf_cache\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"line_reader\",\n    headers = [\n        \"LineReader.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer:line_reader\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"stack_trace\",\n    headers = [\n        \"StackTrace.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer:stack_trace\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"symbolized_frame\",\n    headers = [\n        \"SymbolizedFrame.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer:symbolized_frame\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"symbolize_printer\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":symbolizer\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"symbolizer\",\n    headers = [\n        \"SignalHandler.h\",\n        \"SymbolizePrinter.h\",\n        \"Symbolizer.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer:symbolizer\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"signal_handler\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":symbolizer\"],\n)\n"
  },
  {
    "path": "folly/experimental/symbolizer/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME dwarf\n  HEADERS\n    Dwarf.h\n    DwarfImpl.h\n    DwarfLineNumberVM.h\n    DwarfSection.h\n    DwarfUtil.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_dwarf\n)\n\nfolly_add_library(\n  NAME elf\n  HEADERS\n    Elf-inl.h\n    Elf.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_elf\n)\n\nfolly_add_library(\n  NAME elf_cache\n  HEADERS\n    ElfCache.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_elf_cache\n)\n\nfolly_add_library(\n  NAME line_reader\n  HEADERS\n    LineReader.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_line_reader\n)\n\nfolly_add_library(\n  NAME signal_handler\n  EXPORTED_DEPS\n    folly_experimental_symbolizer_symbolizer\n)\n\nfolly_add_library(\n  NAME stack_trace\n  HEADERS\n    StackTrace.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_stack_trace\n)\n\nfolly_add_library(\n  NAME symbolize_printer\n  EXPORTED_DEPS\n    folly_experimental_symbolizer_symbolizer\n)\n\nfolly_add_library(\n  NAME symbolized_frame\n  HEADERS\n    SymbolizedFrame.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_symbolized_frame\n)\n\nfolly_add_library(\n  NAME symbolizer\n  HEADERS\n    SignalHandler.h\n    SymbolizePrinter.h\n    Symbolizer.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_symbolizer\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/experimental/symbolizer/Dwarf.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Dwarf.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/DwarfImpl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/DwarfImpl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/DwarfLineNumberVM.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/DwarfLineNumberVM.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/DwarfSection.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/DwarfSection.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/DwarfUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/DwarfUtil.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/Elf-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Elf-inl.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/Elf.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Elf.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/ElfCache.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/ElfCache.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/LineReader.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/LineReader.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/SignalHandler.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/SignalHandler.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/StackTrace.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/StackTrace.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/SymbolizePrinter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/SymbolizePrinter.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/SymbolizedFrame.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/SymbolizedFrame.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/Symbolizer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/Symbolizer.h> // @shim\n"
  },
  {
    "path": "folly/experimental/symbolizer/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"debug\",\n    headers = [\n        \"Debug.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/debugging/symbolizer/detail:debug\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/symbolizer/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME debug\n  HEADERS\n    Debug.h\n  EXPORTED_DEPS\n    folly_debugging_symbolizer_detail_debug\n)\n"
  },
  {
    "path": "folly/experimental/symbolizer/detail/Debug.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/debugging/symbolizer/detail/Debug.h> // @shim\n"
  },
  {
    "path": "folly/experimental/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"coding_test_utils\",\n    headers = [\n        \"CodingTestUtils.h\",\n    ],\n    exported_deps = [\n        \"//folly/compression/test:coding_test_utils\",\n    ],\n)\n"
  },
  {
    "path": "folly/experimental/test/CodingTestUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/compression/test/CodingTestUtils.h> // @shim\n"
  },
  {
    "path": "folly/ext/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"test_ext\",\n    srcs = [\"test_ext.cpp\"],\n    headers = [\"test_ext.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n"
  },
  {
    "path": "folly/ext/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME test_ext\n  SRCS\n    test_ext.cpp\n  HEADERS\n    test_ext.h\n)\n"
  },
  {
    "path": "folly/ext/buck2/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"test_ext\",\n    srcs = [\"test_ext.cpp\"],\n    link_whole = True,\n    deps = [\"//xplat/folly:dynamic\"],\n    exported_deps = [\n        \"fbsource//xplat/folly/io:fs_util\",\n        \"//third-party/boost:boost_filesystem\",\n        \"//xplat/folly:json\",\n        \"//xplat/folly/ext:test_ext\",\n    ],\n)\n\n# !!!! fbcode/folly/ext/buck2/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"test_ext\",\n    srcs = [\"test_ext.cpp\"],\n    # @fb-only[end= ]: autodeps_keep = True,\n    # @lint-ignore BUCKLINT\n    link_whole = True,\n    deps = [\n        \"//folly/ext:test_ext\",\n        \"//folly/io:fs_util\",\n        \"//folly/json:dynamic\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_filesystem\"),\n    ],\n)\n"
  },
  {
    "path": "folly/ext/buck2/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfolly_add_library(\n  NAME test_ext\n  SRCS\n    test_ext.cpp\n  DEPS\n    folly_experimental_io_fs_util\n    folly_ext_test_ext\n    folly_json_dynamic\n)\n"
  },
  {
    "path": "folly/ext/buck2/test_ext.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ext/test_ext.h>\n\n#include <fstream>\n#include <sstream>\n\n#include <boost/filesystem.hpp>\n\n#include <folly/io/FsUtil.h>\n#include <folly/json/dynamic.h>\n#include <folly/json/json.h>\n\nnamespace folly {\nnamespace ext {\n\nstatic std::string test_find_resource_buck2(std::string_view resource) {\n  auto const exe = fs::executable_path();\n  auto const bfn = exe.filename().string() + \".resources.json\";\n  auto const res = exe.parent_path() / bfn;\n  std::stringstream buf;\n  buf << std::ifstream(res.string()).rdbuf();\n  auto const dyn = folly::parseJson(buf.str());\n  auto const tgt = dyn[resource].asString();\n  auto const ret = exe.parent_path() / tgt;\n  return ret.string();\n}\n\nstatic const auto& test_find_resource_ctor_ = //\n    test_find_resource = test_find_resource_buck2;\n\n} // namespace ext\n} // namespace folly\n"
  },
  {
    "path": "folly/ext/test_ext.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/ext/test_ext.h>\n\nnamespace folly {\nnamespace ext {\n\ntest_find_resource_t* test_find_resource = nullptr;\n\n} // namespace ext\n} // namespace folly\n"
  },
  {
    "path": "folly/ext/test_ext.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#ifdef _WIN32\n#include <string>\n#endif\n#include <string_view>\n\nnamespace folly {\nnamespace ext {\n\nusing test_find_resource_t = std::string(std::string_view);\nextern test_find_resource_t* test_find_resource;\n\n} // namespace ext\n} // namespace folly\n"
  },
  {
    "path": "folly/external/.clang-format",
    "content": "---\nDisableFormat:   true\nSortIncludes: false\n...\n"
  },
  {
    "path": "folly/external/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nadd_subdirectory(aor)\nadd_subdirectory(farmhash)\nadd_subdirectory(fast-crc32)\nadd_subdirectory(nvidia)\nadd_subdirectory(rapidhash)\n"
  },
  {
    "path": "folly/external/aor/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memcpy_aarch64\",\n    srcs = [\n        \"memcpy-advsimd.S\",\n        \"memcpy-armv8.S\",\n        \"memcpy-mops.S\",\n        \"memcpy_sve.S\",\n        \"memmove-mops.S\",\n    ],\n    headers = [\n        \"asmdefs.h\",\n    ],\n    target_compatible_with = [\n        \"ovr_config//os:linux-arm64\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memcpy_aarch64-use\",\n    srcs = [\n        \"memcpy-advsimd.S\",\n        \"memcpy-armv8.S\",\n        \"memcpy-mops.S\",\n        \"memcpy_sve.S\",\n        \"memmove-mops.S\",\n    ],\n    headers = [\n        \"asmdefs.h\",\n    ],\n    preprocessor_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:arm64\": [\n            \"-DFOLLY_MEMCPY_IS_MEMCPY\",\n        ],\n    }),\n    target_compatible_with = [\n        \"ovr_config//os:linux-arm64\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memset_aarch64\",\n    srcs = [\n        \"memset-advsimd.S\",\n        \"memset-mops.S\",\n        \"memset-sve.S\",\n    ],\n    headers = [\n        \"asmdefs.h\",\n    ],\n    target_compatible_with = [\n        \"ovr_config//os:linux-arm64\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"memset_aarch64-use\",\n    srcs = [\n        \"memset-advsimd.S\",\n        \"memset-mops.S\",\n        \"memset-sve.S\",\n    ],\n    headers = [\n        \"asmdefs.h\",\n    ],\n    preprocessor_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:arm64\": [\n            \"-DFOLLY_MEMSET_IS_MEMSET\",\n        ],\n    }),\n    target_compatible_with = [\n        \"ovr_config//os:linux-arm64\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/aor/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# NOTE: This file is manually maintained, not auto-generated.\n# Update this file when the corresponding BUCK file changes.\n\n# ARM-only optimized memory operations (aarch64 assembly)\n# Only build on Linux aarch64, not Darwin arm64 - these assembly files use\n# Linux ELF directives (.size, etc.) that Darwin's assembler doesn't support\nif(IS_AARCH64_ARCH)\n\nfolly_add_library(\n  NAME memcpy_aarch64\n  SRCS\n    memcpy-advsimd.S\n    memcpy-armv8.S\n    memcpy-mops.S\n    memcpy_sve.S\n    memmove-mops.S\n  HEADERS\n    asmdefs.h\n)\n\nfolly_add_library(\n  NAME memcpy_aarch64-use\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    memcpy-advsimd.S\n    memcpy-armv8.S\n    memcpy-mops.S\n    memcpy_sve.S\n    memmove-mops.S\n  HEADERS\n    asmdefs.h\n  COMPILE_OPTIONS\n    -DFOLLY_MEMCPY_IS_MEMCPY\n)\n\nfolly_add_library(\n  NAME memset_aarch64\n  SRCS\n    memset-advsimd.S\n    memset-mops.S\n    memset-sve.S\n  HEADERS\n    asmdefs.h\n)\n\nfolly_add_library(\n  NAME memset_aarch64-use\n  EXCLUDE_FROM_MONOLITH\n  SRCS\n    memset-advsimd.S\n    memset-mops.S\n    memset-sve.S\n  HEADERS\n    asmdefs.h\n  COMPILE_OPTIONS\n    -DFOLLY_MEMSET_IS_MEMSET\n)\n\nendif()\n"
  },
  {
    "path": "folly/external/aor/asmdefs.h",
    "content": "/*\n * Macros for asm code.  AArch64 version.\n *\n * Copyright (c) 2019-2023, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n#ifndef _ASMDEFS_H\n#define _ASMDEFS_H\n\n/* Branch Target Identitication support.  */\n#define BTI_C\t\thint\t34\n#define BTI_J\t\thint\t36\n/* Return address signing support (pac-ret).  */\n#define PACIASP\t\thint\t25; .cfi_window_save\n#define AUTIASP\t\thint\t29; .cfi_window_save\n\n/* GNU_PROPERTY_AARCH64_* macros from elf.h.  */\n#define FEATURE_1_AND 0xc0000000\n#define FEATURE_1_BTI 1\n#define FEATURE_1_PAC 2\n\n/* Add a NT_GNU_PROPERTY_TYPE_0 note.  */\n#ifdef __ILP32__\n#define GNU_PROPERTY(type, value)\t\\\n  .section .note.gnu.property, \"a\";\t\\\n  .p2align 2;\t\t\t\t\\\n  .word 4;\t\t\t\t\\\n  .word 12;\t\t\t\t\\\n  .word 5;\t\t\t\t\\\n  .asciz \"GNU\";\t\t\t\t\\\n  .word type;\t\t\t\t\\\n  .word 4;\t\t\t\t\\\n  .word value;\t\t\t\t\\\n  .text\n#else\n#define GNU_PROPERTY(type, value)\t\\\n  .section .note.gnu.property, \"a\";\t\\\n  .p2align 3;\t\t\t\t\\\n  .word 4;\t\t\t\t\\\n  .word 16;\t\t\t\t\\\n  .word 5;\t\t\t\t\\\n  .asciz \"GNU\";\t\t\t\t\\\n  .word type;\t\t\t\t\\\n  .word 4;\t\t\t\t\\\n  .word value;\t\t\t\t\\\n  .word 0;\t\t\t\t\\\n  .text\n#endif\n\n/* If set then the GNU Property Note section will be added to\n   mark objects to support BTI and PAC-RET.  */\n#ifndef WANT_GNU_PROPERTY\n#define WANT_GNU_PROPERTY 1\n#endif\n\n#if WANT_GNU_PROPERTY\n/* Add property note with supported features to all asm files.  */\nGNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_PAC)\n#endif\n\n#define ENTRY_ALIGN(name, alignment)\t\\\n  .global name;\t\t\\\n  .type name,%function;\t\\\n  .align alignment;\t\t\\\n  name:\t\t\t\\\n  .cfi_startproc;\t\\\n  BTI_C;\n\n#define ENTRY(name)\tENTRY_ALIGN(name, 6)\n\n#define ENTRY_ALIAS(name)\t\\\n  .global name;\t\t\\\n  .type name,%function;\t\\\n  name:\n\n#define END(name)\t\\\n  .cfi_endproc;\t\t\\\n  .size name, .-name;\n\n#define L(l) .L ## l\n\n#ifdef __ILP32__\n  /* Sanitize padding bits of pointer arguments as per aapcs64 */\n#define PTR_ARG(n)  mov w##n, w##n\n#else\n#define PTR_ARG(n)\n#endif\n\n#ifdef __ILP32__\n  /* Sanitize padding bits of size arguments as per aapcs64 */\n#define SIZE_ARG(n)  mov w##n, w##n\n#else\n#define SIZE_ARG(n)\n#endif\n\n/* Compiler supports SVE instructions  */\n#ifndef HAVE_SVE\n# if __aarch64__ && (__GNUC__ >= 8 || __clang_major__ >= 5)\n#   define HAVE_SVE 1\n# else\n#   define HAVE_SVE 0\n# endif\n#endif\n\n#endif\n"
  },
  {
    "path": "folly/external/aor/memcpy-advsimd.S",
    "content": "/*\n * memcpy - copy memory area\n *\n * Copyright (c) 2019-2023, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n/* Assumptions:\n *\n * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses.\n *\n */\n\n#include \"asmdefs.h\"\n\n#define dstin\tx0\n#define src\tx1\n#define count\tx2\n#define dst\tx3\n#define srcend\tx4\n#define dstend\tx5\n#define A_l\tx6\n#define A_lw\tw6\n#define A_h\tx7\n#define B_l\tx8\n#define B_lw\tw8\n#define B_h\tx9\n#define C_lw\tw10\n#define tmp1\tx14\n\n#define A_q\tq0\n#define B_q\tq1\n#define C_q\tq2\n#define D_q\tq3\n#define E_q\tq4\n#define F_q\tq5\n#define G_q\tq6\n#define H_q\tq7\n\n/* This implementation handles overlaps and supports both memcpy and memmove\n   from a single entry point.  It uses unaligned accesses and branchless\n   sequences to keep the code small, simple and improve performance.\n\n   Copies are split into 3 main cases: small copies of up to 32 bytes, medium\n   copies of up to 128 bytes, and large copies.  The overhead of the overlap\n   check is negligible since it is only required for large copies.\n\n   Large copies use a software pipelined loop processing 64 bytes per iteration.\n   The source pointer is 16-byte aligned to minimize unaligned accesses.\n   The loop tail is handled by always copying 64 bytes from the end.\n*/\n\nENTRY_ALIAS (__folly_memmove_aarch64_simd)\nENTRY (__folly_memcpy_aarch64_simd)\n\tPTR_ARG (0)\n\tPTR_ARG (1)\n\tSIZE_ARG (2)\n\tadd\tsrcend, src, count\n\tcmp\tcount, 128\n\tb.hi\tL(copy_long)\n\tadd\tdstend, dstin, count\n\tcmp\tcount, 32\n\tb.hi\tL(copy32_128)\n\tnop\n\n\t/* Small copies: 0..32 bytes.  */\n\tcmp\tcount, 16\n\tb.lo\tL(copy16)\n\tldr\tA_q, [src]\n\tldr\tB_q, [srcend, -16]\n\tstr\tA_q, [dstin]\n\tstr\tB_q, [dstend, -16]\n\tret\n\n\t.p2align 4\n\t/* Medium copies: 33..128 bytes.  */\nL(copy32_128):\n\tldp\tA_q, B_q, [src]\n\tldp\tC_q, D_q, [srcend, -32]\n\tcmp\tcount, 64\n\tb.hi\tL(copy128)\n\tstp\tA_q, B_q, [dstin]\n\tstp\tC_q, D_q, [dstend, -32]\n\tret\n\n\t.p2align 4\n\t/* Copy 8-15 bytes.  */\nL(copy16):\n\ttbz\tcount, 3, L(copy8)\n\tldr\tA_l, [src]\n\tldr\tA_h, [srcend, -8]\n\tstr\tA_l, [dstin]\n\tstr\tA_h, [dstend, -8]\n\tret\n\n\t/* Copy 4-7 bytes.  */\nL(copy8):\n\ttbz\tcount, 2, L(copy4)\n\tldr\tA_lw, [src]\n\tldr\tB_lw, [srcend, -4]\n\tstr\tA_lw, [dstin]\n\tstr\tB_lw, [dstend, -4]\n\tret\n\n\t/* Copy 65..128 bytes.  */\nL(copy128):\n\tldp\tE_q, F_q, [src, 32]\n\tcmp\tcount, 96\n\tb.ls\tL(copy96)\n\tldp\tG_q, H_q, [srcend, -64]\n\tstp\tG_q, H_q, [dstend, -64]\nL(copy96):\n\tstp\tA_q, B_q, [dstin]\n\tstp\tE_q, F_q, [dstin, 32]\n\tstp\tC_q, D_q, [dstend, -32]\n\tret\n\n\t/* Copy 0..3 bytes using a branchless sequence.  */\nL(copy4):\n\tcbz\tcount, L(copy0)\n\tlsr\ttmp1, count, 1\n\tldrb\tA_lw, [src]\n\tldrb\tC_lw, [srcend, -1]\n\tldrb\tB_lw, [src, tmp1]\n\tstrb\tA_lw, [dstin]\n\tstrb\tB_lw, [dstin, tmp1]\n\tstrb\tC_lw, [dstend, -1]\nL(copy0):\n\tret\n\n\t.p2align 3\n\t/* Copy more than 128 bytes.  */\nL(copy_long):\n\tadd\tdstend, dstin, count\n\n\t/* Use backwards copy if there is an overlap.  */\n\tsub\ttmp1, dstin, src\n\tcmp\ttmp1, count\n\tb.lo\tL(copy_long_backwards)\n\n\t/* Copy 16 bytes and then align src to 16-byte alignment.  */\n\tldr\tD_q, [src]\n\tand\ttmp1, src, 15\n\tbic\tsrc, src, 15\n\tsub\tdst, dstin, tmp1\n\tadd\tcount, count, tmp1\t/* Count is now 16 too large.  */\n\tldp\tA_q, B_q, [src, 16]\n\tstr\tD_q, [dstin]\n\tldp\tC_q, D_q, [src, 48]\n\tsubs\tcount, count, 128 + 16\t/* Test and readjust count.  */\n\tb.ls\tL(copy64_from_end)\nL(loop64):\n\tstp\tA_q, B_q, [dst, 16]\n\tldp\tA_q, B_q, [src, 80]\n\tstp\tC_q, D_q, [dst, 48]\n\tldp\tC_q, D_q, [src, 112]\n\tadd\tsrc, src, 64\n\tadd\tdst, dst, 64\n\tsubs\tcount, count, 64\n\tb.hi\tL(loop64)\n\n\t/* Write the last iteration and copy 64 bytes from the end.  */\nL(copy64_from_end):\n\tldp\tE_q, F_q, [srcend, -64]\n\tstp\tA_q, B_q, [dst, 16]\n\tldp\tA_q, B_q, [srcend, -32]\n\tstp\tC_q, D_q, [dst, 48]\n\tstp\tE_q, F_q, [dstend, -64]\n\tstp\tA_q, B_q, [dstend, -32]\n\tret\n\n\t.p2align 4\n\tnop\n\n\t/* Large backwards copy for overlapping copies.\n\t   Copy 16 bytes and then align srcend to 16-byte alignment.  */\nL(copy_long_backwards):\n\tcbz\ttmp1, L(copy0)\n\tldr\tD_q, [srcend, -16]\n\tand\ttmp1, srcend, 15\n\tbic\tsrcend, srcend, 15\n\tsub\tcount, count, tmp1\n\tldp\tA_q, B_q, [srcend, -32]\n\tstr\tD_q, [dstend, -16]\n\tldp\tC_q, D_q, [srcend, -64]\n\tsub\tdstend, dstend, tmp1\n\tsubs\tcount, count, 128\n\tb.ls\tL(copy64_from_start)\n\nL(loop64_backwards):\n\tstr\tB_q, [dstend, -16]\n\tstr\tA_q, [dstend, -32]\n\tldp\tA_q, B_q, [srcend, -96]\n\tstr\tD_q, [dstend, -48]\n\tstr\tC_q, [dstend, -64]!\n\tldp\tC_q, D_q, [srcend, -128]\n\tsub\tsrcend, srcend, 64\n\tsubs\tcount, count, 64\n\tb.hi\tL(loop64_backwards)\n\n\t/* Write the last iteration and copy 64 bytes from the start.  */\nL(copy64_from_start):\n\tldp\tE_q, F_q, [src, 32]\n\tstp\tA_q, B_q, [dstend, -32]\n\tldp\tA_q, B_q, [src]\n\tstp\tC_q, D_q, [dstend, -64]\n\tstp\tE_q, F_q, [dstin, 32]\n\tstp\tA_q, B_q, [dstin]\n\tret\n\nEND (__folly_memcpy_aarch64_simd)\n\n"
  },
  {
    "path": "folly/external/aor/memcpy-armv8.S",
    "content": "/*\n * memcpy - copy memory area\n *\n * Copyright (c) 2012-2022, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n/* Assumptions:\n *\n * ARMv8-a, AArch64, unaligned accesses.\n *\n */\n\n#include \"asmdefs.h\"\n\n#define dstin\tx0\n#define src\tx1\n#define count\tx2\n#define dst\tx3\n#define srcend\tx4\n#define dstend\tx5\n#define A_l\tx6\n#define A_lw\tw6\n#define A_h\tx7\n#define B_l\tx8\n#define B_lw\tw8\n#define B_h\tx9\n#define C_l\tx10\n#define C_lw\tw10\n#define C_h\tx11\n#define D_l\tx12\n#define D_h\tx13\n#define E_l\tx14\n#define E_h\tx15\n#define F_l\tx16\n#define F_h\tx17\n#define G_l\tcount\n#define G_h\tdst\n#define H_l\tsrc\n#define H_h\tsrcend\n#define tmp1\tx14\n\n/* This implementation handles overlaps and supports both memcpy and memmove\n   from a single entry point.  It uses unaligned accesses and branchless\n   sequences to keep the code small, simple and improve performance.\n\n   Copies are split into 3 main cases: small copies of up to 32 bytes, medium\n   copies of up to 128 bytes, and large copies.  The overhead of the overlap\n   check is negligible since it is only required for large copies.\n\n   Large copies use a software pipelined loop processing 64 bytes per iteration.\n   The destination pointer is 16-byte aligned to minimize unaligned accesses.\n   The loop tail is handled by always copying 64 bytes from the end.\n*/\n\nENTRY_ALIAS (__folly_memmove_aarch64)\nENTRY (__folly_memcpy_aarch64)\n\tPTR_ARG (0)\n\tPTR_ARG (1)\n\tSIZE_ARG (2)\n\tadd\tsrcend, src, count\n\tadd\tdstend, dstin, count\n\tcmp\tcount, 128\n\tb.hi\tL(copy_long)\n\tcmp\tcount, 32\n\tb.hi\tL(copy32_128)\n\n\t/* Small copies: 0..32 bytes.  */\n\tcmp\tcount, 16\n\tb.lo\tL(copy16)\n\tldp\tA_l, A_h, [src]\n\tldp\tD_l, D_h, [srcend, -16]\n\tstp\tA_l, A_h, [dstin]\n\tstp\tD_l, D_h, [dstend, -16]\n\tret\n\n\t/* Copy 8-15 bytes.  */\nL(copy16):\n\ttbz\tcount, 3, L(copy8)\n\tldr\tA_l, [src]\n\tldr\tA_h, [srcend, -8]\n\tstr\tA_l, [dstin]\n\tstr\tA_h, [dstend, -8]\n\tret\n\n\t.p2align 3\n\t/* Copy 4-7 bytes.  */\nL(copy8):\n\ttbz\tcount, 2, L(copy4)\n\tldr\tA_lw, [src]\n\tldr\tB_lw, [srcend, -4]\n\tstr\tA_lw, [dstin]\n\tstr\tB_lw, [dstend, -4]\n\tret\n\n\t/* Copy 0..3 bytes using a branchless sequence.  */\nL(copy4):\n\tcbz\tcount, L(copy0)\n\tlsr\ttmp1, count, 1\n\tldrb\tA_lw, [src]\n\tldrb\tC_lw, [srcend, -1]\n\tldrb\tB_lw, [src, tmp1]\n\tstrb\tA_lw, [dstin]\n\tstrb\tB_lw, [dstin, tmp1]\n\tstrb\tC_lw, [dstend, -1]\nL(copy0):\n\tret\n\n\t.p2align 4\n\t/* Medium copies: 33..128 bytes.  */\nL(copy32_128):\n\tldp\tA_l, A_h, [src]\n\tldp\tB_l, B_h, [src, 16]\n\tldp\tC_l, C_h, [srcend, -32]\n\tldp\tD_l, D_h, [srcend, -16]\n\tcmp\tcount, 64\n\tb.hi\tL(copy128)\n\tstp\tA_l, A_h, [dstin]\n\tstp\tB_l, B_h, [dstin, 16]\n\tstp\tC_l, C_h, [dstend, -32]\n\tstp\tD_l, D_h, [dstend, -16]\n\tret\n\n\t.p2align 4\n\t/* Copy 65..128 bytes.  */\nL(copy128):\n\tldp\tE_l, E_h, [src, 32]\n\tldp\tF_l, F_h, [src, 48]\n\tcmp\tcount, 96\n\tb.ls\tL(copy96)\n\tldp\tG_l, G_h, [srcend, -64]\n\tldp\tH_l, H_h, [srcend, -48]\n\tstp\tG_l, G_h, [dstend, -64]\n\tstp\tH_l, H_h, [dstend, -48]\nL(copy96):\n\tstp\tA_l, A_h, [dstin]\n\tstp\tB_l, B_h, [dstin, 16]\n\tstp\tE_l, E_h, [dstin, 32]\n\tstp\tF_l, F_h, [dstin, 48]\n\tstp\tC_l, C_h, [dstend, -32]\n\tstp\tD_l, D_h, [dstend, -16]\n\tret\n\n\t.p2align 4\n\t/* Copy more than 128 bytes.  */\nL(copy_long):\n\t/* Use backwards copy if there is an overlap.  */\n\tsub\ttmp1, dstin, src\n\tcbz\ttmp1, L(copy0)\n\tcmp\ttmp1, count\n\tb.lo\tL(copy_long_backwards)\n\n\t/* Copy 16 bytes and then align dst to 16-byte alignment.  */\n\n\tldp\tD_l, D_h, [src]\n\tand\ttmp1, dstin, 15\n\tbic\tdst, dstin, 15\n\tsub\tsrc, src, tmp1\n\tadd\tcount, count, tmp1\t/* Count is now 16 too large.  */\n\tldp\tA_l, A_h, [src, 16]\n\tstp\tD_l, D_h, [dstin]\n\tldp\tB_l, B_h, [src, 32]\n\tldp\tC_l, C_h, [src, 48]\n\tldp\tD_l, D_h, [src, 64]!\n\tsubs\tcount, count, 128 + 16\t/* Test and readjust count.  */\n\tb.ls\tL(copy64_from_end)\n\nL(loop64):\n\tstp\tA_l, A_h, [dst, 16]\n\tldp\tA_l, A_h, [src, 16]\n\tstp\tB_l, B_h, [dst, 32]\n\tldp\tB_l, B_h, [src, 32]\n\tstp\tC_l, C_h, [dst, 48]\n\tldp\tC_l, C_h, [src, 48]\n\tstp\tD_l, D_h, [dst, 64]!\n\tldp\tD_l, D_h, [src, 64]!\n\tsubs\tcount, count, 64\n\tb.hi\tL(loop64)\n\n\t/* Write the last iteration and copy 64 bytes from the end.  */\nL(copy64_from_end):\n\tldp\tE_l, E_h, [srcend, -64]\n\tstp\tA_l, A_h, [dst, 16]\n\tldp\tA_l, A_h, [srcend, -48]\n\tstp\tB_l, B_h, [dst, 32]\n\tldp\tB_l, B_h, [srcend, -32]\n\tstp\tC_l, C_h, [dst, 48]\n\tldp\tC_l, C_h, [srcend, -16]\n\tstp\tD_l, D_h, [dst, 64]\n\tstp\tE_l, E_h, [dstend, -64]\n\tstp\tA_l, A_h, [dstend, -48]\n\tstp\tB_l, B_h, [dstend, -32]\n\tstp\tC_l, C_h, [dstend, -16]\n\tret\n\n\t.p2align 4\n\n\t/* Large backwards copy for overlapping copies.\n\t   Copy 16 bytes and then align dst to 16-byte alignment.  */\nL(copy_long_backwards):\n\tldp\tD_l, D_h, [srcend, -16]\n\tand\ttmp1, dstend, 15\n\tsub\tsrcend, srcend, tmp1\n\tsub\tcount, count, tmp1\n\tldp\tA_l, A_h, [srcend, -16]\n\tstp\tD_l, D_h, [dstend, -16]\n\tldp\tB_l, B_h, [srcend, -32]\n\tldp\tC_l, C_h, [srcend, -48]\n\tldp\tD_l, D_h, [srcend, -64]!\n\tsub\tdstend, dstend, tmp1\n\tsubs\tcount, count, 128\n\tb.ls\tL(copy64_from_start)\n\nL(loop64_backwards):\n\tstp\tA_l, A_h, [dstend, -16]\n\tldp\tA_l, A_h, [srcend, -16]\n\tstp\tB_l, B_h, [dstend, -32]\n\tldp\tB_l, B_h, [srcend, -32]\n\tstp\tC_l, C_h, [dstend, -48]\n\tldp\tC_l, C_h, [srcend, -48]\n\tstp\tD_l, D_h, [dstend, -64]!\n\tldp\tD_l, D_h, [srcend, -64]!\n\tsubs\tcount, count, 64\n\tb.hi\tL(loop64_backwards)\n\n\t/* Write the last iteration and copy 64 bytes from the start.  */\nL(copy64_from_start):\n\tldp\tG_l, G_h, [src, 48]\n\tstp\tA_l, A_h, [dstend, -16]\n\tldp\tA_l, A_h, [src, 32]\n\tstp\tB_l, B_h, [dstend, -32]\n\tldp\tB_l, B_h, [src, 16]\n\tstp\tC_l, C_h, [dstend, -48]\n\tldp\tC_l, C_h, [src]\n\tstp\tD_l, D_h, [dstend, -64]\n\tstp\tG_l, G_h, [dstin, 48]\n\tstp\tA_l, A_h, [dstin, 32]\n\tstp\tB_l, B_h, [dstin, 16]\n\tstp\tC_l, C_h, [dstin]\n\tret\n\nEND (__folly_memcpy_aarch64)\n\n"
  },
  {
    "path": "folly/external/aor/memcpy-mops.S",
    "content": "/*\n * memcpy using MOPS extension.\n *\n * Copyright (c) 2023, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n#include \"asmdefs.h\"\n\nENTRY (__folly_memcpy_aarch64_mops)\n\tPTR_ARG (0)\n\tPTR_ARG (1)\n\tSIZE_ARG (2)\n\n\tmov\tx3, x0\n\t.inst\t0x19010443\t/* cpyfp   [x3]!, [x1]!, x2!  */\n\t.inst\t0x19410443\t/* cpyfm   [x3]!, [x1]!, x2!  */\n\t.inst\t0x19810443\t/* cpyfe   [x3]!, [x1]!, x2!  */\n\tret\n\nEND (__folly_memcpy_aarch64_mops)\n"
  },
  {
    "path": "folly/external/aor/memcpy_sve.S",
    "content": "/*\n * memcpy - copy memory area\n *\n * Copyright (c) 2019-2023, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n/* Assumptions:\n *\n * ARMv8-a, AArch64, Advanced SIMD, SVE, unaligned accesses.\n *\n */\n\n#include \"asmdefs.h\"\n\n.arch armv8-a+sve\n\n#define dstin\tx0\n#define src\tx1\n#define count\tx2\n#define dst\tx3\n#define srcend\tx4\n#define dstend\tx5\n#define tmp1\tx6\n#define vlen\tx6\n\n#define A_q\tq0\n#define B_q\tq1\n#define C_q\tq2\n#define D_q\tq3\n#define E_q\tq4\n#define F_q\tq5\n#define G_q\tq6\n#define H_q\tq7\n\n/* This implementation handles overlaps and supports both memcpy and memmove\n   from a single entry point.  It uses unaligned accesses and branchless\n   sequences to keep the code small, simple and improve performance.\n   SVE vectors are used to speedup small copies.\n\n   Copies are split into 3 main cases: small copies of up to 32 bytes, medium\n   copies of up to 128 bytes, and large copies.  The overhead of the overlap\n   check is negligible since it is only required for large copies.\n\n   Large copies use a software pipelined loop processing 64 bytes per iteration.\n   The source pointer is 16-byte aligned to minimize unaligned accesses.\n   The loop tail is handled by always copying 64 bytes from the end.\n*/\n\nENTRY_ALIAS (__folly_memmove_aarch64_sve)\nENTRY (__folly_memcpy_aarch64_sve)\n\tPTR_ARG (0)\n\tPTR_ARG (1)\n\tSIZE_ARG (2)\n\n\tcmp\tcount, 128\n\tb.hi\tL(copy_long)\n\tcntb\tvlen\n\tcmp\tcount, vlen, lsl 1\n\tb.hi\tL(copy32_128)\n\n\twhilelo p0.b, xzr, count\n\twhilelo p1.b, vlen, count\n\tld1b\tz0.b, p0/z, [src, 0, mul vl]\n\tld1b\tz1.b, p1/z, [src, 1, mul vl]\n\tst1b\tz0.b, p0, [dstin, 0, mul vl]\n\tst1b\tz1.b, p1, [dstin, 1, mul vl]\n\tret\n\n\t/* Medium copies: 33..128 bytes.  */\nL(copy32_128):\n\tadd\tsrcend, src, count\n\tadd\tdstend, dstin, count\n\tldp\tA_q, B_q, [src]\n\tldp\tC_q, D_q, [srcend, -32]\n\tcmp\tcount, 64\n\tb.hi\tL(copy128)\n\tstp\tA_q, B_q, [dstin]\n\tstp\tC_q, D_q, [dstend, -32]\n\tret\n\n\t/* Copy 65..128 bytes.  */\nL(copy128):\n\tldp\tE_q, F_q, [src, 32]\n\tcmp\tcount, 96\n\tb.ls\tL(copy96)\n\tldp\tG_q, H_q, [srcend, -64]\n\tstp\tG_q, H_q, [dstend, -64]\nL(copy96):\n\tstp\tA_q, B_q, [dstin]\n\tstp\tE_q, F_q, [dstin, 32]\n\tstp\tC_q, D_q, [dstend, -32]\n\tret\n\n\t/* Copy more than 128 bytes.  */\nL(copy_long):\n\tadd\tsrcend, src, count\n\tadd\tdstend, dstin, count\n\n\t/* Use backwards copy if there is an overlap.  */\n\tsub\ttmp1, dstin, src\n\tcmp\ttmp1, count\n\tb.lo\tL(copy_long_backwards)\n\n\t/* Copy 16 bytes and then align src to 16-byte alignment.  */\n\tldr\tD_q, [src]\n\tand\ttmp1, src, 15\n\tbic\tsrc, src, 15\n\tsub\tdst, dstin, tmp1\n\tadd\tcount, count, tmp1\t/* Count is now 16 too large.  */\n\tldp\tA_q, B_q, [src, 16]\n\tstr\tD_q, [dstin]\n\tldp\tC_q, D_q, [src, 48]\n\tsubs\tcount, count, 128 + 16\t/* Test and readjust count.  */\n\tb.ls\tL(copy64_from_end)\nL(loop64):\n\tstp\tA_q, B_q, [dst, 16]\n\tldp\tA_q, B_q, [src, 80]\n\tstp\tC_q, D_q, [dst, 48]\n\tldp\tC_q, D_q, [src, 112]\n\tadd\tsrc, src, 64\n\tadd\tdst, dst, 64\n\tsubs\tcount, count, 64\n\tb.hi\tL(loop64)\n\n\t/* Write the last iteration and copy 64 bytes from the end.  */\nL(copy64_from_end):\n\tldp\tE_q, F_q, [srcend, -64]\n\tstp\tA_q, B_q, [dst, 16]\n\tldp\tA_q, B_q, [srcend, -32]\n\tstp\tC_q, D_q, [dst, 48]\n\tstp\tE_q, F_q, [dstend, -64]\n\tstp\tA_q, B_q, [dstend, -32]\n\tret\n\n\t/* Large backwards copy for overlapping copies.\n\t   Copy 16 bytes and then align srcend to 16-byte alignment.  */\nL(copy_long_backwards):\n\tcbz\ttmp1, L(return)\n\tldr\tD_q, [srcend, -16]\n\tand\ttmp1, srcend, 15\n\tbic\tsrcend, srcend, 15\n\tsub\tcount, count, tmp1\n\tldp\tA_q, B_q, [srcend, -32]\n\tstr\tD_q, [dstend, -16]\n\tldp\tC_q, D_q, [srcend, -64]\n\tsub\tdstend, dstend, tmp1\n\tsubs\tcount, count, 128\n\tb.ls\tL(copy64_from_start)\n\nL(loop64_backwards):\n\tstr\tB_q, [dstend, -16]\n\tstr\tA_q, [dstend, -32]\n\tldp\tA_q, B_q, [srcend, -96]\n\tstr\tD_q, [dstend, -48]\n\tstr\tC_q, [dstend, -64]!\n\tldp\tC_q, D_q, [srcend, -128]\n\tsub\tsrcend, srcend, 64\n\tsubs\tcount, count, 64\n\tb.hi\tL(loop64_backwards)\n\n\t/* Write the last iteration and copy 64 bytes from the start.  */\nL(copy64_from_start):\n\tldp\tE_q, F_q, [src, 32]\n\tstp\tA_q, B_q, [dstend, -32]\n\tldp\tA_q, B_q, [src]\n\tstp\tC_q, D_q, [dstend, -64]\n\tstp\tE_q, F_q, [dstin, 32]\n\tstp\tA_q, B_q, [dstin]\nL(return):\n\tret\n\nEND (__folly_memcpy_aarch64_sve)\n"
  },
  {
    "path": "folly/external/aor/memmove-mops.S",
    "content": "/*\n * memmove using MOPS extension.\n *\n * Copyright (c) 2023, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n#include \"asmdefs.h\"\n\nENTRY (__folly_memmove_aarch64_mops)\n\tPTR_ARG (0)\n\tPTR_ARG (1)\n\tSIZE_ARG (2)\n\n\tmov\tx3, x0\n\t.inst\t0x1d010443\t/* cpyp    [x3]!, [x1]!, x2!  */\n\t.inst\t0x1d410443\t/* cpym    [x3]!, [x1]!, x2!  */\n\t.inst\t0x1d810443\t/* cpye    [x3]!, [x1]!, x2!  */\n\tret\n\nEND (__folly_memmove_aarch64_mops)\n"
  },
  {
    "path": "folly/external/aor/memset-advsimd.S",
    "content": "/*\n * memset - fill memory with a constant byte\n *\n * Copyright (c) 2012-2022, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n/* Assumptions:\n *\n * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses.\n *\n */\n\n#include \"asmdefs.h\"\n\n#define dstin\tx0\n#define val\tx1\n#define valw\tw1\n#define count\tx2\n#define dst\tx3\n#define dstend\tx4\n#define zva_val\tx5\n\nENTRY (__folly_memset_aarch64_simd)\n\tPTR_ARG (0)\n\tSIZE_ARG (2)\n\n\tdup\tv0.16B, valw\n\tadd\tdstend, dstin, count\n\n\tcmp\tcount, 96\n\tb.hi\tL(set_long)\n\tcmp\tcount, 16\n\tb.hs\tL(set_medium)\n\tmov\tval, v0.D[0]\n\n\t/* Set 0..15 bytes.  */\n\ttbz\tcount, 3, 1f\n\tstr\tval, [dstin]\n\tstr\tval, [dstend, -8]\n\tret\n\t.p2align 4\n1:\ttbz\tcount, 2, 2f\n\tstr\tvalw, [dstin]\n\tstr\tvalw, [dstend, -4]\n\tret\n2:\tcbz\tcount, 3f\n\tstrb\tvalw, [dstin]\n\ttbz\tcount, 1, 3f\n\tstrh\tvalw, [dstend, -2]\n3:\tret\n\n\t/* Set 17..96 bytes.  */\nL(set_medium):\n\tstr\tq0, [dstin]\n\ttbnz\tcount, 6, L(set96)\n\tstr\tq0, [dstend, -16]\n\ttbz\tcount, 5, 1f\n\tstr\tq0, [dstin, 16]\n\tstr\tq0, [dstend, -32]\n1:\tret\n\n\t.p2align 4\n\t/* Set 64..96 bytes.  Write 64 bytes from the start and\n\t   32 bytes from the end.  */\nL(set96):\n\tstr\tq0, [dstin, 16]\n\tstp\tq0, q0, [dstin, 32]\n\tstp\tq0, q0, [dstend, -32]\n\tret\n\n\t.p2align 4\nL(set_long):\n\tand\tvalw, valw, 255\n\tbic\tdst, dstin, 15\n\tstr\tq0, [dstin]\n\tcmp\tcount, 160\n\tccmp\tvalw, 0, 0, hs\n\tb.ne\tL(no_zva)\n\n#ifndef SKIP_ZVA_CHECK\n\tmrs\tzva_val, dczid_el0\n\tand\tzva_val, zva_val, 31\n\tcmp\tzva_val, 4\t\t/* ZVA size is 64 bytes.  */\n\tb.ne\tL(no_zva)\n#endif\n\tstr\tq0, [dst, 16]\n\tstp\tq0, q0, [dst, 32]\n\tbic\tdst, dst, 63\n\tsub\tcount, dstend, dst\t/* Count is now 64 too large.  */\n\tsub\tcount, count, 128\t/* Adjust count and bias for loop.  */\n\n\t.p2align 4\nL(zva_loop):\n\tadd\tdst, dst, 64\n\tdc\tzva, dst\n\tsubs\tcount, count, 64\n\tb.hi\tL(zva_loop)\n\tstp\tq0, q0, [dstend, -64]\n\tstp\tq0, q0, [dstend, -32]\n\tret\n\nL(no_zva):\n\tsub\tcount, dstend, dst\t/* Count is 16 too large.  */\n\tsub\tdst, dst, 16\t\t/* Dst is biased by -32.  */\n\tsub\tcount, count, 64 + 16\t/* Adjust count and bias for loop.  */\nL(no_zva_loop):\n\tstp\tq0, q0, [dst, 32]\n\tstp\tq0, q0, [dst, 64]!\n\tsubs\tcount, count, 64\n\tb.hi\tL(no_zva_loop)\n\tstp\tq0, q0, [dstend, -64]\n\tstp\tq0, q0, [dstend, -32]\n\tret\n\nEND (__folly_memset_aarch64_simd)\n"
  },
  {
    "path": "folly/external/aor/memset-mops.S",
    "content": "/*\n * memset using MOPS extension.\n *\n * Copyright (c) 2023, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n#include \"asmdefs.h\"\n\nENTRY (__folly_memset_aarch64_mops)\n\tPTR_ARG (0)\n\tSIZE_ARG (2)\n\n\tmov     x3, x0\n\t.inst   0x19c10443\t/* setp    [x3]!, x2!, x1  */\n\t.inst   0x19c14443\t/* setm    [x3]!, x2!, x1  */\n\t.inst   0x19c18443\t/* sete    [x3]!, x2!, x1  */\n\tret\n\nEND (__folly_memset_aarch64_mops)\n"
  },
  {
    "path": "folly/external/aor/memset-sve.S",
    "content": "/*\n * memset - fill memory with a constant byte\n *\n * Copyright (c) 2024-2024, Arm Limited.\n * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception\n */\n\n/* Assumptions:\n *\n * ARMv8-a, AArch64, Advanced SIMD, SVE, unaligned accesses.\n *\n */\n\n#include \"asmdefs.h\"\n\n.arch armv8-a+sve\n\n#define dstin\tx0\n#define val\tx1\n#define valw\tw1\n#define count\tx2\n#define dst\tx3\n#define dstend\tx4\n#define zva_val\tx5\n#define off\tx3\n#define dstend2 x5\n\nENTRY (__folly_memset_aarch64_sve)\n\tdup\tv0.16B, valw\n\tcmp\tcount, 16\n\tb.lo\tL(set_16)\n\n\tadd\tdstend, dstin, count\n\tcmp\tcount, 64\n\tb.hi\tL(set_128)\n\n\t/* Set 16..64 bytes.  */\n\tmov\toff, 48\n\tand\toff, off, count, lsr 1\n\tsub\tdstend2, dstend, off\n\tstr\tq0, [dstin]\n\tstr\tq0, [dstin, off]\n\tstr\tq0, [dstend2, -16]\n\tstr\tq0, [dstend, -16]\n\tret\n\n\t.p2align 4\nL(set_16):\n\twhilelo p0.b, xzr, count\n\tst1b\tz0.b, p0, [dstin]\n\tret\n\n\t.p2align 4\nL(set_128):\n\tbic\tdst, dstin, 15\n\tcmp\tcount, 128\n\tb.hi\tL(set_long)\n\tstp\tq0, q0, [dstin]\n\tstp\tq0, q0, [dstin, 32]\n\tstp\tq0, q0, [dstend, -64]\n\tstp\tq0, q0, [dstend, -32]\n\tret\n\n\t.p2align 4\nL(set_long):\n\tcmp\tcount, 256\n\tb.lo\tL(no_zva)\n\ttst\tvalw, 255\n\tb.ne\tL(no_zva)\n\n\tstr\tq0, [dstin]\n\tstr\tq0, [dst, 16]\n\tbic\tdst, dstin, 31\n\tstp\tq0, q0, [dst, 32]\n\tbic\tdst, dstin, 63\n\tsub\tcount, dstend, dst\t/* Count is now 64 too large.  */\n\tsub\tcount, count, 128\t/* Adjust count and bias for loop.  */\n\n\tsub\tx8, dstend, 1\t\t/* Write last bytes before ZVA loop.  */\n\tbic\tx8, x8, 15\n\tstp\tq0, q0, [x8, -48]\n\tstr\tq0, [x8, -16]\n\tstr\tq0, [dstend, -16]\n\n\t.p2align 4\nL(zva64_loop):\n\tadd\tdst, dst, 64\n\tdc\tzva, dst\n\tsubs\tcount, count, 64\n\tb.hi\tL(zva64_loop)\n\tret\n\nL(no_zva):\n\tstr\tq0, [dstin]\n\tsub\tcount, dstend, dst\t/* Count is 16 too large.  */\n\tsub\tcount, count, 64 + 16\t/* Adjust count and bias for loop.  */\nL(no_zva_loop):\n\tstp\tq0, q0, [dst, 16]\n\tstp\tq0, q0, [dst, 48]\n\tadd\tdst, dst, 64\n\tsubs\tcount, count, 64\n\tb.hi\tL(no_zva_loop)\n\tstp\tq0, q0, [dstend, -64]\n\tstp\tq0, q0, [dstend, -32]\n\tret\n\nEND (__folly_memset_aarch64_sve)\n"
  },
  {
    "path": "folly/external/farmhash/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"farmhash\",\n    srcs = [\"farmhash.cpp\"],\n    headers = [\"farmhash.h\"],\n    exported_deps = [\n        \"//folly/portability:config\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/farmhash/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME farmhash\n  SRCS\n    farmhash.cpp\n  HEADERS\n    farmhash.h\n  EXPORTED_DEPS\n    folly_portability_config\n)\n"
  },
  {
    "path": "folly/external/farmhash/farmhash.cpp",
    "content": "// Copyright (c) 2014 Google, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// FarmHash, by Geoff Pike\n\n// clang-format off\n\n#include <folly/external/farmhash/farmhash.h>\n\n#include <exception>\n\n// FARMHASH ASSUMPTIONS: Modify as needed, or use -DFARMHASH_ASSUME_SSE42 etc.\n// Note that if you use -DFARMHASH_ASSUME_SSE42 you likely need -msse42\n// (or its equivalent for your compiler); if you use -DFARMHASH_ASSUME_AESNI\n// you likely need -maes (or its equivalent for your compiler).\n\n#ifdef FARMHASH_ASSUME_SSSE3\n#undef FARMHASH_ASSUME_SSSE3\n#define FARMHASH_ASSUME_SSSE3 1\n#endif\n\n#ifdef FARMHASH_ASSUME_SSE41\n#undef FARMHASH_ASSUME_SSE41\n#define FARMHASH_ASSUME_SSE41 1\n#endif\n\n#ifdef FARMHASH_ASSUME_SSE42\n#undef FARMHASH_ASSUME_SSE42\n#define FARMHASH_ASSUME_SSE42 1\n#endif\n\n#ifdef FARMHASH_ASSUME_AESNI\n#undef FARMHASH_ASSUME_AESNI\n#define FARMHASH_ASSUME_AESNI 1\n#endif\n\n#ifdef FARMHASH_ASSUME_AVX\n#undef FARMHASH_ASSUME_AVX\n#define FARMHASH_ASSUME_AVX 1\n#endif\n\n#if !defined(FARMHASH_CAN_USE_CXX11) && defined(LANG_CXX11)\n#define FARMHASH_CAN_USE_CXX11 1\n#else\n#undef FARMHASH_CAN_USE_CXX11\n#define FARMHASH_CAN_USE_CXX11 0\n#endif\n\n// FARMHASH PORTABILITY LAYER: Runtime error if misconfigured\n\n#ifndef FARMHASH_DIE_IF_MISCONFIGURED\n#define FARMHASH_DIE_IF_MISCONFIGURED      \\\n  do {                                     \\\n    if (test::returnZeroIfMisconfigured) { \\\n      return 0;                            \\\n    } else {                               \\\n      std::terminate();                    \\\n    }                                      \\\n  } while (0)\n#endif\n\n// FARMHASH PORTABILITY LAYER: \"static inline\" or similar\n\n#ifndef STATIC_INLINE\n#define STATIC_INLINE static inline\n#endif\n\n// FARMHASH PORTABILITY LAYER: LIKELY and UNLIKELY\n\n#if !defined(LIKELY)\n#if defined(FARMHASH_NO_BUILTIN_EXPECT) || (defined(FARMHASH_OPTIONAL_BUILTIN_EXPECT) && !defined(HAVE_BUILTIN_EXPECT)) || !defined(__GNUC__)\n#define LIKELY(x) (x)\n#else\n#define LIKELY(x) (__builtin_expect(!!(x), 1))\n#endif\n#endif\n\n#undef UNLIKELY\n#define UNLIKELY(x) !LIKELY(!(x))\n\n// FARMHASH PORTABILITY LAYER: endianness and byteswapping functions\n\n#ifdef WORDS_BIGENDIAN\n#undef FARMHASH_BIG_ENDIAN\n#define FARMHASH_BIG_ENDIAN 1\n#endif\n\n#if defined(FARMHASH_LITTLE_ENDIAN) && defined(FARMHASH_BIG_ENDIAN)\n#error\n#endif\n\n#if !defined(FARMHASH_LITTLE_ENDIAN) && !defined(FARMHASH_BIG_ENDIAN)\n#define FARMHASH_UNKNOWN_ENDIAN 1\n#endif\n\n#if !defined(bswap_32) || !defined(bswap_64)\n#undef bswap_32\n#undef bswap_64\n\n#if defined(HAVE_BUILTIN_BSWAP) || defined(__clang__) ||                \\\n  (defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) ||      \\\n                         __GNUC__ >= 5))\n// Easy case for bswap: no header file needed.\n#define bswap_32(x) __builtin_bswap32(x)\n#define bswap_64(x) __builtin_bswap64(x)\n#endif\n\n#endif\n\n#if defined(FARMHASH_UNKNOWN_ENDIAN) || !defined(bswap_64)\n\n#ifdef _MSC_VER\n\n#undef bswap_32\n#undef bswap_64\n#define bswap_32(x) _byteswap_ulong(x)\n#define bswap_64(x) _byteswap_uint64(x)\n\n#elif defined(__APPLE__)\n\n// Mac OS X / Darwin features\n#include <libkern/OSByteOrder.h>\n#undef bswap_32\n#undef bswap_64\n#define bswap_32(x) OSSwapInt32(x)\n#define bswap_64(x) OSSwapInt64(x)\n\n#elif defined(__sun) || defined(sun)\n\n#include <sys/byteorder.h>\n#undef bswap_32\n#undef bswap_64\n#define bswap_32(x) BSWAP_32(x)\n#define bswap_64(x) BSWAP_64(x)\n\n#elif defined(__FreeBSD__)\n\n#include <sys/endian.h>\n#undef bswap_32\n#undef bswap_64\n#define bswap_32(x) bswap32(x)\n#define bswap_64(x) bswap64(x)\n\n#elif defined(__OpenBSD__)\n\n#include <sys/types.h>\n#undef bswap_32\n#undef bswap_64\n#define bswap_32(x) swap32(x)\n#define bswap_64(x) swap64(x)\n\n#elif defined(__NetBSD__)\n\n#include <sys/types.h>\n#include <machine/bswap.h>\n#if defined(__BSWAP_RENAME) && !defined(__bswap_32)\n#undef bswap_32\n#undef bswap_64\n#define bswap_32(x) bswap32(x)\n#define bswap_64(x) bswap64(x)\n#endif\n\n#else\n\n#undef bswap_32\n#undef bswap_64\n#include <byteswap.h>\n\n#endif\n\n#ifdef WORDS_BIGENDIAN\n#define FARMHASH_BIG_ENDIAN 1\n#endif\n\n#endif\n\n#ifdef FARMHASH_BIG_ENDIAN\n#define uint32_in_expected_order(x) (bswap_32(x))\n#define uint64_in_expected_order(x) (bswap_64(x))\n#else\n#define uint32_in_expected_order(x) (x)\n#define uint64_in_expected_order(x) (x)\n#endif\n\nnamespace folly {\nnamespace external {\nnamespace farmhash {\n\nnamespace test {\nbool returnZeroIfMisconfigured = false;\n}\n\nSTATIC_INLINE uint64_t Fetch64(const char *p) {\n  uint64_t result;\n  memcpy(&result, p, sizeof(result));\n  return uint64_in_expected_order(result);\n}\n\nSTATIC_INLINE uint32_t Fetch32(const char *p) {\n  uint32_t result;\n  memcpy(&result, p, sizeof(result));\n  return uint32_in_expected_order(result);\n}\n\nSTATIC_INLINE uint32_t Bswap32(uint32_t val) { return bswap_32(val); }\nSTATIC_INLINE uint64_t Bswap64(uint64_t val) { return bswap_64(val); }\n\n// FARMHASH PORTABILITY LAYER: bitwise rot\n\nSTATIC_INLINE uint32_t BasicRotate32(uint32_t val, int shift) {\n  // Avoid shifting by 32: doing so yields an undefined result.\n  return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));\n}\n\nSTATIC_INLINE uint64_t BasicRotate64(uint64_t val, int shift) {\n  // Avoid shifting by 64: doing so yields an undefined result.\n  return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));\n}\n\n#if defined(_MSC_VER) && defined(FARMHASH_ROTR)\n\nSTATIC_INLINE uint32_t Rotate32(uint32_t val, int shift) {\n  return sizeof(unsigned long) == sizeof(val) ?\n      _lrotr(val, shift) :\n      BasicRotate32(val, shift);\n}\n\nSTATIC_INLINE uint64_t Rotate64(uint64_t val, int shift) {\n  return sizeof(unsigned long) == sizeof(val) ?\n      _lrotr(val, shift) :\n      BasicRotate64(val, shift);\n}\n\n#else\n\nSTATIC_INLINE uint32_t Rotate32(uint32_t val, int shift) {\n  return BasicRotate32(val, shift);\n}\nSTATIC_INLINE uint64_t Rotate64(uint64_t val, int shift) {\n  return BasicRotate64(val, shift);\n}\n\n#endif\n\n} // namespace farmhash\n} // namespace external\n} // namespace folly\n\n// FARMHASH PORTABILITY LAYER: debug mode or max speed?\n// One may use -DFARMHASH_DEBUG=1 or -DFARMHASH_DEBUG=0 to force the issue.\n\n#if !defined(FARMHASH_DEBUG) && (!defined(NDEBUG) || defined(_DEBUG))\n#define FARMHASH_DEBUG 1\n#endif\n\n#undef debug_mode\n#if FARMHASH_DEBUG\n#define debug_mode 1\n#else\n#define debug_mode 0\n#endif\n\n// PLATFORM-SPECIFIC FUNCTIONS AND MACROS\n\n#undef x86_64\n#if defined (__x86_64) || defined (__x86_64__)\n#define x86_64 1\n#else\n#define x86_64 0\n#endif\n\n#undef x86\n#if defined(__i386__) || defined(__i386) || defined(__X86__)\n#define x86 1\n#else\n#define x86 x86_64\n#endif\n\n#if !defined(is_64bit)\n#define is_64bit (x86_64 || (sizeof(void*) == 8))\n#endif\n\n#undef can_use_ssse3\n#if defined(__SSSE3__) || defined(FARMHASH_ASSUME_SSSE3)\n\n#include <immintrin.h>\n#define can_use_ssse3 1\n// Now we can use _mm_hsub_epi16 and so on.\n\n#else\n#define can_use_ssse3 0\n#endif\n\n#undef can_use_sse41\n#if defined(__SSE4_1__) || defined(FARMHASH_ASSUME_SSE41)\n\n#include <immintrin.h>\n#define can_use_sse41 1\n// Now we can use _mm_insert_epi64 and so on.\n\n#else\n#define can_use_sse41 0\n#endif\n\n#undef can_use_sse42\n#if defined(__SSE4_2__) || defined(FARMHASH_ASSUME_SSE42)\n\n#include <nmmintrin.h>\n#define can_use_sse42 1\n// Now we can use _mm_crc32_u{32,16,8}.  And on 64-bit platforms, _mm_crc32_u64.\n\n#else\n#define can_use_sse42 0\n#endif\n\n#undef can_use_aesni\n#if defined(__AES__) || defined(FARMHASH_ASSUME_AESNI)\n\n#include <wmmintrin.h>\n#define can_use_aesni 1\n// Now we can use _mm_aesimc_si128 and so on.\n\n#else\n#define can_use_aesni 0\n#endif\n\n#undef can_use_avx\n#if defined(__AVX__) || defined(FARMHASH_ASSUME_AVX)\n\n#include <immintrin.h>\n#define can_use_avx 1\n\n#else\n#define can_use_avx 0\n#endif\n\n#if can_use_sse42 || (can_use_sse41 && x86_64)\nSTATIC_INLINE __m128i Fetch128(const char* s) {\n  return _mm_loadu_si128(reinterpret_cast<const __m128i*>(s));\n}\n#endif\n// Building blocks for hash functions\n\n// std::swap() was in <algorithm> but is in <utility> from C++11 on.\n#if !FARMHASH_CAN_USE_CXX11\n#include <algorithm>\n#endif\n\n#undef PERMUTE3\n#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0)\n\nnamespace folly {\nnamespace external {\nnamespace farmhash {\n\n// Some primes between 2^63 and 2^64 for various uses.\nstatic const uint64_t k0 = 0xc3a5c85c97cb3127ULL;\nstatic const uint64_t k1 = 0xb492b66fbe98f273ULL;\nstatic const uint64_t k2 = 0x9ae16a3b2f90404fULL;\n\n// Magic numbers for 32-bit hashing.  Copied from Murmur3.\nstatic const uint32_t c1 = 0xcc9e2d51;\nstatic const uint32_t c2 = 0x1b873593;\n\n// A 32-bit to 32-bit integer hash copied from Murmur3.\nSTATIC_INLINE uint32_t fmix(uint32_t h)\n{\n  h ^= h >> 16;\n  h *= 0x85ebca6b;\n  h ^= h >> 13;\n  h *= 0xc2b2ae35;\n  h ^= h >> 16;\n  return h;\n}\n\nSTATIC_INLINE uint32_t Mur(uint32_t a, uint32_t h) {\n  // Helper from Murmur3 for combining two 32-bit values.\n  a *= c1;\n  a = Rotate32(a, 17);\n  a *= c2;\n  h ^= a;\n  h = Rotate32(h, 19);\n  return h * 5 + 0xe6546b64;\n}\n\ntemplate <typename T> STATIC_INLINE T DebugTweak(T x) {\n  if (debug_mode) {\n    if (sizeof(x) == 4) {\n      x = ~Bswap32(x * c1);\n    } else {\n      x = ~Bswap64(x * k1);\n    }\n  }\n  return x;\n}\n\ntemplate <> uint128_t DebugTweak(uint128_t x) {\n  if (debug_mode) {\n    uint64_t y = DebugTweak(Uint128Low64(x));\n    uint64_t z = DebugTweak(Uint128High64(x));\n    y += z;\n    z += y;\n    x = Uint128(y, z * k1);\n  }\n  return x;\n}\n\nusing namespace std;\n\nnamespace farmhashna {\n#undef Fetch\n#define Fetch Fetch64\n\n#undef Rotate\n#define Rotate Rotate64\n\n#undef Bswap\n#define Bswap Bswap64\n\nSTATIC_INLINE uint64_t ShiftMix(uint64_t val) {\n  return val ^ (val >> 47);\n}\n\nSTATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v) {\n  return Hash128to64(Uint128(u, v));\n}\n\nSTATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) {\n  // Murmur-inspired hashing.\n  uint64_t a = (u ^ v) * mul;\n  a ^= (a >> 47);\n  uint64_t b = (v ^ a) * mul;\n  b ^= (b >> 47);\n  b *= mul;\n  return b;\n}\n\nSTATIC_INLINE uint64_t HashLen0to16(const char *s, size_t len) {\n  if (len >= 8) {\n    uint64_t mul = k2 + len * 2;\n    uint64_t a = Fetch(s) + k2;\n    uint64_t b = Fetch(s + len - 8);\n    uint64_t c = Rotate(b, 37) * mul + a;\n    uint64_t d = (Rotate(a, 25) + b) * mul;\n    return HashLen16(c, d, mul);\n  }\n  if (len >= 4) {\n    uint64_t mul = k2 + len * 2;\n    uint64_t a = Fetch32(s);\n    return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);\n  }\n  if (len > 0) {\n    uint8_t a = s[0];\n    uint8_t b = s[len >> 1];\n    uint8_t c = s[len - 1];\n    uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);\n    uint32_t z = len + (static_cast<uint32_t>(c) << 2);\n    return ShiftMix(y * k2 ^ z * k0) * k2;\n  }\n  return k2;\n}\n\n// This probably works well for 16-byte strings as well, but it may be overkill\n// in that case.\nSTATIC_INLINE uint64_t HashLen17to32(const char *s, size_t len) {\n  uint64_t mul = k2 + len * 2;\n  uint64_t a = Fetch(s) * k1;\n  uint64_t b = Fetch(s + 8);\n  uint64_t c = Fetch(s + len - 8) * mul;\n  uint64_t d = Fetch(s + len - 16) * k2;\n  return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,\n                   a + Rotate(b + k2, 18) + c, mul);\n}\n\n// Return a 16-byte hash for 48 bytes.  Quick and dirty.\n// Callers do best to use \"random-looking\" values for a and b.\nSTATIC_INLINE pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(\n    uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, uint64_t b) {\n  a += w;\n  b = Rotate(b + a + z, 21);\n  uint64_t c = a;\n  a += x;\n  a += y;\n  b += Rotate(a, 44);\n  return make_pair(a + z, b + c);\n}\n\n// Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.\nSTATIC_INLINE pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(\n    const char* s, uint64_t a, uint64_t b) {\n  return WeakHashLen32WithSeeds(Fetch(s),\n                                Fetch(s + 8),\n                                Fetch(s + 16),\n                                Fetch(s + 24),\n                                a,\n                                b);\n}\n\n// Return an 8-byte hash for 33 to 64 bytes.\nSTATIC_INLINE uint64_t HashLen33to64(const char *s, size_t len) {\n  uint64_t mul = k2 + len * 2;\n  uint64_t a = Fetch(s) * k2;\n  uint64_t b = Fetch(s + 8);\n  uint64_t c = Fetch(s + len - 8) * mul;\n  uint64_t d = Fetch(s + len - 16) * k2;\n  uint64_t y = Rotate(a + b, 43) + Rotate(c, 30) + d;\n  uint64_t z = HashLen16(y, a + Rotate(b + k2, 18) + c, mul);\n  uint64_t e = Fetch(s + 16) * mul;\n  uint64_t f = Fetch(s + 24);\n  uint64_t g = (y + Fetch(s + len - 32)) * mul;\n  uint64_t h = (z + Fetch(s + len - 24)) * mul;\n  return HashLen16(Rotate(e + f, 43) + Rotate(g, 30) + h,\n                   e + Rotate(f + a, 18) + g, mul);\n}\n\nuint64_t Hash64(const char *s, size_t len) {\n  const uint64_t seed = 81;\n  if (len <= 32) {\n    if (len <= 16) {\n      return HashLen0to16(s, len);\n    } else {\n      return HashLen17to32(s, len);\n    }\n  } else if (len <= 64) {\n    return HashLen33to64(s, len);\n  }\n\n  // For strings over 64 bytes we loop.  Internal state consists of\n  // 56 bytes: v, w, x, y, and z.\n  uint64_t x = seed;\n  uint64_t y = seed * k1 + 113;\n  uint64_t z = ShiftMix(y * k2 + 113) * k2;\n  pair<uint64_t, uint64_t> v = make_pair(0, 0);\n  pair<uint64_t, uint64_t> w = make_pair(0, 0);\n  x = x * k2 + Fetch(s);\n\n  // Set end so that after the loop we have 1 to 64 bytes left to process.\n  const char* end = s + ((len - 1) / 64) * 64;\n  const char* last64 = end + ((len - 1) & 63) - 63;\n  assert(s + len - 64 == last64);\n  do {\n    x = Rotate(x + y + v.first + Fetch(s + 8), 37) * k1;\n    y = Rotate(y + v.second + Fetch(s + 48), 42) * k1;\n    x ^= w.second;\n    y += v.first + Fetch(s + 40);\n    z = Rotate(z + w.first, 33) * k1;\n    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);\n    w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16));\n    std::swap(z, x);\n    s += 64;\n  } while (s != end);\n  uint64_t mul = k1 + ((z & 0xff) << 1);\n  // Make s point to the last 64 bytes of input.\n  s = last64;\n  w.first += ((len - 1) & 63);\n  v.first += w.first;\n  w.first += v.first;\n  x = Rotate(x + y + v.first + Fetch(s + 8), 37) * mul;\n  y = Rotate(y + v.second + Fetch(s + 48), 42) * mul;\n  x ^= w.second * 9;\n  y += v.first * 9 + Fetch(s + 40);\n  z = Rotate(z + w.first, 33) * mul;\n  v = WeakHashLen32WithSeeds(s, v.second * mul, x + w.first);\n  w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16));\n  std::swap(z, x);\n  return HashLen16(HashLen16(v.first, w.first, mul) + ShiftMix(y) * k0 + z,\n                   HashLen16(v.second, w.second, mul) + x,\n                   mul);\n}\n\nuint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1);\n\nuint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) {\n  return Hash64WithSeeds(s, len, k2, seed);\n}\n\nuint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) {\n  return HashLen16(Hash64(s, len) - seed0, seed1);\n}\n}  // namespace farmhashna\nnamespace farmhashuo {\n#undef Fetch\n#define Fetch Fetch64\n\n#undef Rotate\n#define Rotate Rotate64\n\nSTATIC_INLINE uint64_t H(uint64_t x, uint64_t y, uint64_t mul, int r) {\n  uint64_t a = (x ^ y) * mul;\n  a ^= (a >> 47);\n  uint64_t b = (y ^ a) * mul;\n  return Rotate(b, r) * mul;\n}\n\nuint64_t Hash64WithSeeds(const char *s, size_t len,\n                         uint64_t seed0, uint64_t seed1) {\n  if (len <= 64) {\n    return farmhashna::Hash64WithSeeds(s, len, seed0, seed1);\n  }\n\n  // For strings over 64 bytes we loop.  Internal state consists of\n  // 64 bytes: u, v, w, x, y, and z.\n  uint64_t x = seed0;\n  uint64_t y = seed1 * k2 + 113;\n  uint64_t z = farmhashna::ShiftMix(y * k2) * k2;\n  pair<uint64_t, uint64_t> v = make_pair(seed0, seed1);\n  pair<uint64_t, uint64_t> w = make_pair(0, 0);\n  uint64_t u = x - z;\n  x *= k2;\n  uint64_t mul = k2 + (u & 0x82);\n\n  // Set end so that after the loop we have 1 to 64 bytes left to process.\n  const char* end = s + ((len - 1) / 64) * 64;\n  const char* last64 = end + ((len - 1) & 63) - 63;\n  assert(s + len - 64 == last64);\n  do {\n    uint64_t a0 = Fetch(s);\n    uint64_t a1 = Fetch(s + 8);\n    uint64_t a2 = Fetch(s + 16);\n    uint64_t a3 = Fetch(s + 24);\n    uint64_t a4 = Fetch(s + 32);\n    uint64_t a5 = Fetch(s + 40);\n    uint64_t a6 = Fetch(s + 48);\n    uint64_t a7 = Fetch(s + 56);\n    x += a0 + a1;\n    y += a2;\n    z += a3;\n    v.first += a4;\n    v.second += a5 + a1;\n    w.first += a6;\n    w.second += a7;\n\n    x = Rotate(x, 26);\n    x *= 9;\n    y = Rotate(y, 29);\n    z *= mul;\n    v.first = Rotate(v.first, 33);\n    v.second = Rotate(v.second, 30);\n    w.first ^= x;\n    w.first *= 9;\n    z = Rotate(z, 32);\n    z += w.second;\n    w.second += z;\n    z *= 9;\n    std::swap(u, y);\n\n    z += a0 + a6;\n    v.first += a2;\n    v.second += a3;\n    w.first += a4;\n    w.second += a5 + a6;\n    x += a1;\n    y += a7;\n\n    y += v.first;\n    v.first += x - y;\n    v.second += w.first;\n    w.first += v.second;\n    w.second += x - y;\n    x += w.second;\n    w.second = Rotate(w.second, 34);\n    std::swap(u, z);\n    s += 64;\n  } while (s != end);\n  // Make s point to the last 64 bytes of input.\n  s = last64;\n  u *= 9;\n  v.second = Rotate(v.second, 28);\n  v.first = Rotate(v.first, 20);\n  w.first += ((len - 1) & 63);\n  u += y;\n  y += u;\n  x = Rotate(y - x + v.first + Fetch(s + 8), 37) * mul;\n  y = Rotate(y ^ v.second ^ Fetch(s + 48), 42) * mul;\n  x ^= w.second * 9;\n  y += v.first + Fetch(s + 40);\n  z = Rotate(z + w.first, 33) * mul;\n  v = farmhashna::WeakHashLen32WithSeeds(s, v.second * mul, x + w.first);\n  w = farmhashna::WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16));\n  return H(farmhashna::HashLen16(v.first + x, w.first ^ y, mul) + z - u,\n           H(v.second + y, w.second + z, k2, 30) ^ x,\n           k2,\n           31);\n}\n\nuint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) {\n  return len <= 64 ? farmhashna::Hash64WithSeed(s, len, seed) :\n      Hash64WithSeeds(s, len, 0, seed);\n}\n\nuint64_t Hash64(const char *s, size_t len) {\n  return len <= 64 ? farmhashna::Hash64(s, len) :\n      Hash64WithSeeds(s, len, 81, 0);\n}\n}  // namespace farmhashuo\nnamespace farmhashxo {\n#undef Fetch\n#define Fetch Fetch64\n\n#undef Rotate\n#define Rotate Rotate64\n\nSTATIC_INLINE uint64_t H32(const char *s, size_t len, uint64_t mul,\n                           uint64_t seed0 = 0, uint64_t seed1 = 0) {\n  uint64_t a = Fetch(s) * k1;\n  uint64_t b = Fetch(s + 8);\n  uint64_t c = Fetch(s + len - 8) * mul;\n  uint64_t d = Fetch(s + len - 16) * k2;\n  uint64_t u = Rotate(a + b, 43) + Rotate(c, 30) + d + seed0;\n  uint64_t v = a + Rotate(b + k2, 18) + c + seed1;\n  a = farmhashna::ShiftMix((u ^ v) * mul);\n  b = farmhashna::ShiftMix((v ^ a) * mul);\n  return b;\n}\n\n// Return an 8-byte hash for 33 to 64 bytes.\nSTATIC_INLINE uint64_t HashLen33to64(const char *s, size_t len) {\n  uint64_t mul0 = k2 - 30;\n  uint64_t mul1 = k2 - 30 + 2 * len;\n  uint64_t h0 = H32(s, 32, mul0);\n  uint64_t h1 = H32(s + len - 32, 32, mul1);\n  return ((h1 * mul1) + h0) * mul1;\n}\n\n// Return an 8-byte hash for 65 to 96 bytes.\nSTATIC_INLINE uint64_t HashLen65to96(const char *s, size_t len) {\n  uint64_t mul0 = k2 - 114;\n  uint64_t mul1 = k2 - 114 + 2 * len;\n  uint64_t h0 = H32(s, 32, mul0);\n  uint64_t h1 = H32(s + 32, 32, mul1);\n  uint64_t h2 = H32(s + len - 32, 32, mul1, h0, h1);\n  return (h2 * 9 + (h0 >> 17) + (h1 >> 21)) * mul1;\n}\n\nuint64_t Hash64(const char *s, size_t len) {\n  if (len <= 32) {\n    if (len <= 16) {\n      return farmhashna::HashLen0to16(s, len);\n    } else {\n      return farmhashna::HashLen17to32(s, len);\n    }\n  } else if (len <= 64) {\n    return HashLen33to64(s, len);\n  } else if (len <= 96) {\n    return HashLen65to96(s, len);\n  } else if (len <= 256) {\n    return farmhashna::Hash64(s, len);\n  } else {\n    return farmhashuo::Hash64(s, len);\n  }\n}\n\nuint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) {\n  return farmhashuo::Hash64WithSeeds(s, len, seed0, seed1);\n}\n\nuint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) {\n  return farmhashuo::Hash64WithSeed(s, len, seed);\n}\n}  // namespace farmhashxo\nnamespace farmhashte {\n#if !can_use_sse41 || !x86_64\n\nuint64_t Hash64(const char *s, size_t len) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return s == NULL ? 0 : len;\n}\n\nuint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return seed + Hash64(s, len);\n}\n\nuint64_t Hash64WithSeeds(const char *s, size_t len,\n                         uint64_t seed0, uint64_t seed1) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return seed0 + seed1 + Hash64(s, len);\n}\n\n#else\n\n#undef Fetch\n#define Fetch Fetch64\n\n#undef Rotate\n#define Rotate Rotate64\n\n#undef Bswap\n#define Bswap Bswap64\n\n// Helpers for data-parallel operations (1x 128 bits or 2x 64 or 4x 32).\nSTATIC_INLINE __m128i Add(__m128i x, __m128i y) { return _mm_add_epi64(x, y); }\nSTATIC_INLINE __m128i Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); }\nSTATIC_INLINE __m128i Mul(__m128i x, __m128i y) { return _mm_mullo_epi32(x, y); }\nSTATIC_INLINE __m128i Shuf(__m128i x, __m128i y) { return _mm_shuffle_epi8(y, x); }\n\n// Requires n >= 256.  Requires SSE4.1. Should be slightly faster if the\n// compiler uses AVX instructions (e.g., use the -mavx flag with GCC).\nSTATIC_INLINE uint64_t Hash64Long(const char* s, size_t n,\n                                  uint64_t seed0, uint64_t seed1) {\n  const __m128i kShuf =\n      _mm_set_epi8(4, 11, 10, 5, 8, 15, 6, 9, 12, 2, 14, 13, 0, 7, 3, 1);\n  const __m128i kMult =\n      _mm_set_epi8(0xbd, 0xd6, 0x33, 0x39, 0x45, 0x54, 0xfa, 0x03,\n                   0x34, 0x3e, 0x33, 0xed, 0xcc, 0x9e, 0x2d, 0x51);\n  uint64_t seed2 = (seed0 + 113) * (seed1 + 9);\n  uint64_t seed3 = (Rotate(seed0, 23) + 27) * (Rotate(seed1, 30) + 111);\n  __m128i d0 = _mm_cvtsi64_si128(seed0);\n  __m128i d1 = _mm_cvtsi64_si128(seed1);\n  __m128i d2 = Shuf(kShuf, d0);\n  __m128i d3 = Shuf(kShuf, d1);\n  __m128i d4 = Xor(d0, d1);\n  __m128i d5 = Xor(d1, d2);\n  __m128i d6 = Xor(d2, d4);\n  __m128i d7 = _mm_set1_epi32(seed2 >> 32);\n  __m128i d8 = Mul(kMult, d2);\n  __m128i d9 = _mm_set1_epi32(seed3 >> 32);\n  __m128i d10 = _mm_set1_epi32(seed3);\n  __m128i d11 = Add(d2, _mm_set1_epi32(seed2));\n  const char* end = s + (n & ~static_cast<size_t>(255));\n  do {\n    __m128i z;\n    z = Fetch128(s);\n    d0 = Add(d0, z);\n    d1 = Shuf(kShuf, d1);\n    d2 = Xor(d2, d0);\n    d4 = Xor(d4, z);\n    d4 = Xor(d4, d1);\n    std::swap(d0, d6);\n    z = Fetch128(s + 16);\n    d5 = Add(d5, z);\n    d6 = Shuf(kShuf, d6);\n    d8 = Shuf(kShuf, d8);\n    d7 = Xor(d7, d5);\n    d0 = Xor(d0, z);\n    d0 = Xor(d0, d6);\n    std::swap(d5, d11);\n    z = Fetch128(s + 32);\n    d1 = Add(d1, z);\n    d2 = Shuf(kShuf, d2);\n    d4 = Shuf(kShuf, d4);\n    d5 = Xor(d5, z);\n    d5 = Xor(d5, d2);\n    std::swap(d10, d4);\n    z = Fetch128(s + 48);\n    d6 = Add(d6, z);\n    d7 = Shuf(kShuf, d7);\n    d0 = Shuf(kShuf, d0);\n    d8 = Xor(d8, d6);\n    d1 = Xor(d1, z);\n    d1 = Add(d1, d7);\n    z = Fetch128(s + 64);\n    d2 = Add(d2, z);\n    d5 = Shuf(kShuf, d5);\n    d4 = Add(d4, d2);\n    d6 = Xor(d6, z);\n    d6 = Xor(d6, d11);\n    std::swap(d8, d2);\n    z = Fetch128(s + 80);\n    d7 = Xor(d7, z);\n    d8 = Shuf(kShuf, d8);\n    d1 = Shuf(kShuf, d1);\n    d0 = Add(d0, d7);\n    d2 = Add(d2, z);\n    d2 = Add(d2, d8);\n    std::swap(d1, d7);\n    z = Fetch128(s + 96);\n    d4 = Shuf(kShuf, d4);\n    d6 = Shuf(kShuf, d6);\n    d8 = Mul(kMult, d8);\n    d5 = Xor(d5, d11);\n    d7 = Xor(d7, z);\n    d7 = Add(d7, d4);\n    std::swap(d6, d0);\n    z = Fetch128(s + 112);\n    d8 = Add(d8, z);\n    d0 = Shuf(kShuf, d0);\n    d2 = Shuf(kShuf, d2);\n    d1 = Xor(d1, d8);\n    d10 = Xor(d10, z);\n    d10 = Xor(d10, d0);\n    std::swap(d11, d5);\n    z = Fetch128(s + 128);\n    d4 = Add(d4, z);\n    d5 = Shuf(kShuf, d5);\n    d7 = Shuf(kShuf, d7);\n    d6 = Add(d6, d4);\n    d8 = Xor(d8, z);\n    d8 = Xor(d8, d5);\n    std::swap(d4, d10);\n    z = Fetch128(s + 144);\n    d0 = Add(d0, z);\n    d1 = Shuf(kShuf, d1);\n    d2 = Add(d2, d0);\n    d4 = Xor(d4, z);\n    d4 = Xor(d4, d1);\n    z = Fetch128(s + 160);\n    d5 = Add(d5, z);\n    d6 = Shuf(kShuf, d6);\n    d8 = Shuf(kShuf, d8);\n    d7 = Xor(d7, d5);\n    d0 = Xor(d0, z);\n    d0 = Xor(d0, d6);\n    std::swap(d2, d8);\n    z = Fetch128(s + 176);\n    d1 = Add(d1, z);\n    d2 = Shuf(kShuf, d2);\n    d4 = Shuf(kShuf, d4);\n    d5 = Mul(kMult, d5);\n    d5 = Xor(d5, z);\n    d5 = Xor(d5, d2);\n    std::swap(d7, d1);\n    z = Fetch128(s + 192);\n    d6 = Add(d6, z);\n    d7 = Shuf(kShuf, d7);\n    d0 = Shuf(kShuf, d0);\n    d8 = Add(d8, d6);\n    d1 = Xor(d1, z);\n    d1 = Xor(d1, d7);\n    std::swap(d0, d6);\n    z = Fetch128(s + 208);\n    d2 = Add(d2, z);\n    d5 = Shuf(kShuf, d5);\n    d4 = Xor(d4, d2);\n    d6 = Xor(d6, z);\n    d6 = Xor(d6, d9);\n    std::swap(d5, d11);\n    z = Fetch128(s + 224);\n    d7 = Add(d7, z);\n    d8 = Shuf(kShuf, d8);\n    d1 = Shuf(kShuf, d1);\n    d0 = Xor(d0, d7);\n    d2 = Xor(d2, z);\n    d2 = Xor(d2, d8);\n    std::swap(d10, d4);\n    z = Fetch128(s + 240);\n    d3 = Add(d3, z);\n    d4 = Shuf(kShuf, d4);\n    d6 = Shuf(kShuf, d6);\n    d7 = Mul(kMult, d7);\n    d5 = Add(d5, d3);\n    d7 = Xor(d7, z);\n    d7 = Xor(d7, d4);\n    std::swap(d3, d9);\n    s += 256;\n  } while (s != end);\n  d6 = Add(Mul(kMult, d6), _mm_cvtsi64_si128(n));\n  if (n % 256 != 0) {\n    d7 = Add(_mm_shuffle_epi32(d8, (0 << 6) + (3 << 4) + (2 << 2) + (1 << 0)), d7);\n    d8 = Add(Mul(kMult, d8), _mm_cvtsi64_si128(farmhashxo::Hash64(s, n % 256)));\n  }\n  __m128i t[8];\n  d0 = Mul(kMult, Shuf(kShuf, Mul(kMult, d0)));\n  d3 = Mul(kMult, Shuf(kShuf, Mul(kMult, d3)));\n  d9 = Mul(kMult, Shuf(kShuf, Mul(kMult, d9)));\n  d1 = Mul(kMult, Shuf(kShuf, Mul(kMult, d1)));\n  d0 = Add(d11, d0);\n  d3 = Xor(d7, d3);\n  d9 = Add(d8, d9);\n  d1 = Add(d10, d1);\n  d4 = Add(d3, d4);\n  d5 = Add(d9, d5);\n  d6 = Xor(d1, d6);\n  d2 = Add(d0, d2);\n  t[0] = d0;\n  t[1] = d3;\n  t[2] = d9;\n  t[3] = d1;\n  t[4] = d4;\n  t[5] = d5;\n  t[6] = d6;\n  t[7] = d2;\n  return farmhashxo::Hash64(reinterpret_cast<const char*>(t), sizeof(t));\n}\n\nuint64_t Hash64(const char *s, size_t len) {\n  // Empirically, farmhashxo seems faster until length 512.\n  return len >= 512 ? Hash64Long(s, len, k2, k1) : farmhashxo::Hash64(s, len);\n}\n\nuint64_t Hash64WithSeed(const char *s, size_t len, uint64_t seed) {\n  return len >= 512 ? Hash64Long(s, len, k1, seed) :\n      farmhashxo::Hash64WithSeed(s, len, seed);\n}\n\nuint64_t Hash64WithSeeds(const char *s, size_t len, uint64_t seed0, uint64_t seed1) {\n  return len >= 512 ? Hash64Long(s, len, seed0, seed1) :\n      farmhashxo::Hash64WithSeeds(s, len, seed0, seed1);\n}\n\n#endif\n}  // namespace farmhashte\nnamespace farmhashnt {\n#if !can_use_sse41 || !x86_64\n\nuint32_t Hash32(const char *s, size_t len) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return s == NULL ? 0 : len;\n}\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return seed + Hash32(s, len);\n}\n\n#else\n\nuint32_t Hash32(const char *s, size_t len) {\n  return static_cast<uint32_t>(farmhashte::Hash64(s, len));\n}\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  return static_cast<uint32_t>(farmhashte::Hash64WithSeed(s, len, seed));\n}\n\n#endif\n}  // namespace farmhashnt\nnamespace farmhashmk {\n#undef Fetch\n#define Fetch Fetch32\n\n#undef Rotate\n#define Rotate Rotate32\n\n#undef Bswap\n#define Bswap Bswap32\n\nSTATIC_INLINE uint32_t Hash32Len13to24(const char *s, size_t len, uint32_t seed = 0) {\n  uint32_t a = Fetch(s - 4 + (len >> 1));\n  uint32_t b = Fetch(s + 4);\n  uint32_t c = Fetch(s + len - 8);\n  uint32_t d = Fetch(s + (len >> 1));\n  uint32_t e = Fetch(s);\n  uint32_t f = Fetch(s + len - 4);\n  uint32_t h = d * c1 + len + seed;\n  a = Rotate(a, 12) + f;\n  h = Mur(c, h) + a;\n  a = Rotate(a, 3) + c;\n  h = Mur(e, h) + a;\n  a = Rotate(a + f, 12) + d;\n  h = Mur(b ^ seed, h) + a;\n  return fmix(h);\n}\n\nSTATIC_INLINE uint32_t Hash32Len0to4(const char *s, size_t len, uint32_t seed = 0) {\n  uint32_t b = seed;\n  uint32_t c = 9;\n  for (size_t i = 0; i < len; i++) {\n    signed char v = s[i];\n    b = b * c1 + v;\n    c ^= b;\n  }\n  return fmix(Mur(b, Mur(len, c)));\n}\n\nSTATIC_INLINE uint32_t Hash32Len5to12(const char *s, size_t len, uint32_t seed = 0) {\n  uint32_t a = len, b = len * 5, c = 9, d = b + seed;\n  a += Fetch(s);\n  b += Fetch(s + len - 4);\n  c += Fetch(s + ((len >> 1) & 4));\n  return fmix(seed ^ Mur(c, Mur(b, Mur(a, d))));\n}\n\nuint32_t Hash32(const char *s, size_t len) {\n  if (len <= 24) {\n    return len <= 12 ?\n        (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) :\n        Hash32Len13to24(s, len);\n  }\n\n  // len > 24\n  uint32_t h = len, g = c1 * len, f = g;\n  uint32_t a0 = Rotate(Fetch(s + len - 4) * c1, 17) * c2;\n  uint32_t a1 = Rotate(Fetch(s + len - 8) * c1, 17) * c2;\n  uint32_t a2 = Rotate(Fetch(s + len - 16) * c1, 17) * c2;\n  uint32_t a3 = Rotate(Fetch(s + len - 12) * c1, 17) * c2;\n  uint32_t a4 = Rotate(Fetch(s + len - 20) * c1, 17) * c2;\n  h ^= a0;\n  h = Rotate(h, 19);\n  h = h * 5 + 0xe6546b64;\n  h ^= a2;\n  h = Rotate(h, 19);\n  h = h * 5 + 0xe6546b64;\n  g ^= a1;\n  g = Rotate(g, 19);\n  g = g * 5 + 0xe6546b64;\n  g ^= a3;\n  g = Rotate(g, 19);\n  g = g * 5 + 0xe6546b64;\n  f += a4;\n  f = Rotate(f, 19) + 113;\n  size_t iters = (len - 1) / 20;\n  do {\n    uint32_t a = Fetch(s);\n    uint32_t b = Fetch(s + 4);\n    uint32_t c = Fetch(s + 8);\n    uint32_t d = Fetch(s + 12);\n    uint32_t e = Fetch(s + 16);\n    h += a;\n    g += b;\n    f += c;\n    h = Mur(d, h) + e;\n    g = Mur(c, g) + a;\n    f = Mur(b + e * c1, f) + d;\n    f += g;\n    g += f;\n    s += 20;\n  } while (--iters != 0);\n  g = Rotate(g, 11) * c1;\n  g = Rotate(g, 17) * c1;\n  f = Rotate(f, 11) * c1;\n  f = Rotate(f, 17) * c1;\n  h = Rotate(h + g, 19);\n  h = h * 5 + 0xe6546b64;\n  h = Rotate(h, 17) * c1;\n  h = Rotate(h + f, 19);\n  h = h * 5 + 0xe6546b64;\n  h = Rotate(h, 17) * c1;\n  return h;\n}\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  if (len <= 24) {\n    if (len >= 13) return Hash32Len13to24(s, len, seed * c1);\n    else if (len >= 5) return Hash32Len5to12(s, len, seed);\n    else return Hash32Len0to4(s, len, seed);\n  }\n  uint32_t h = Hash32Len13to24(s, 24, seed ^ len);\n  return Mur(Hash32(s + 24, len - 24) + seed, h);\n}\n}  // namespace farmhashmk\nnamespace farmhashsu {\n#if !can_use_sse42 || !can_use_aesni\n\nuint32_t Hash32(const char *s, size_t len) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return s == nullptr ? 0 : len;\n}\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return seed + Hash32(s, len);\n}\n\n#else\n\n#undef Fetch\n#define Fetch Fetch32\n\n#undef Rotate\n#define Rotate Rotate32\n\n#undef Bswap\n#define Bswap Bswap32\n\n// Helpers for data-parallel operations (4x 32-bits).\nSTATIC_INLINE __m128i Add(__m128i x, __m128i y) { return _mm_add_epi32(x, y); }\nSTATIC_INLINE __m128i Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); }\nSTATIC_INLINE __m128i Or(__m128i x, __m128i y) { return _mm_or_si128(x, y); }\nSTATIC_INLINE __m128i Mul(__m128i x, __m128i y) { return _mm_mullo_epi32(x, y); }\nSTATIC_INLINE __m128i Mul5(__m128i x) { return Add(x, _mm_slli_epi32(x, 2)); }\nSTATIC_INLINE __m128i RotateLeft(__m128i x, int c) {\n  return Or(_mm_slli_epi32(x, c),\n            _mm_srli_epi32(x, 32 - c));\n}\nSTATIC_INLINE __m128i Rol17(__m128i x) { return RotateLeft(x, 17); }\nSTATIC_INLINE __m128i Rol19(__m128i x) { return RotateLeft(x, 19); }\nSTATIC_INLINE __m128i Shuffle0321(__m128i x) {\n  return _mm_shuffle_epi32(x, (0 << 6) + (3 << 4) + (2 << 2) + (1 << 0));\n}\n\nuint32_t Hash32(const char *s, size_t len) {\n  const uint32_t seed = 81;\n  if (len <= 24) {\n    return len <= 12 ?\n        (len <= 4 ?\n         farmhashmk::Hash32Len0to4(s, len) :\n         farmhashmk::Hash32Len5to12(s, len)) :\n        farmhashmk::Hash32Len13to24(s, len);\n  }\n\n  if (len < 40) {\n    uint32_t a = len, b = seed * c2, c = a + b;\n    a += Fetch(s + len - 4);\n    b += Fetch(s + len - 20);\n    c += Fetch(s + len - 16);\n    uint32_t d = a;\n    a = folly::external::farmhash::Rotate32(a, 21);\n    a = Mur(a, Mur(b, _mm_crc32_u32(c, d)));\n    a += Fetch(s + len - 12);\n    b += Fetch(s + len - 8);\n    d += a;\n    a += d;\n    b = Mur(b, d) * c2;\n    a = _mm_crc32_u32(a, b + c);\n    return farmhashmk::Hash32Len13to24(s, (len + 1) / 2, a) + b;\n  }\n\n#undef Mulc1\n#define Mulc1(x) Mul((x), cc1)\n\n#undef Mulc2\n#define Mulc2(x) Mul((x), cc2)\n\n#undef Murk\n#define Murk(a, h)                              \\\n  Add(k,                                        \\\n      Mul5(                                     \\\n          Rol19(                                \\\n              Xor(                              \\\n                  Mulc2(                        \\\n                      Rol17(                    \\\n                          Mulc1(a))),           \\\n                  (h)))))\n\n  const __m128i cc1 = _mm_set1_epi32(c1);\n  const __m128i cc2 = _mm_set1_epi32(c2);\n  __m128i h = _mm_set1_epi32(seed);\n  __m128i g = _mm_set1_epi32(c1 * seed);\n  __m128i f = g;\n  __m128i k = _mm_set1_epi32(0xe6546b64);\n  __m128i q;\n  if (len < 80) {\n    __m128i a = Fetch128(s);\n    __m128i b = Fetch128(s + 16);\n    __m128i c = Fetch128(s + (len - 15) / 2);\n    __m128i d = Fetch128(s + len - 32);\n    __m128i e = Fetch128(s + len - 16);\n    h = Add(h, a);\n    g = Add(g, b);\n    q = g;\n    g = Shuffle0321(g);\n    f = Add(f, c);\n    __m128i be = Add(b, Mulc1(e));\n    h = Add(h, f);\n    f = Add(f, h);\n    h = Add(Murk(d, h), e);\n    k = Xor(k, _mm_shuffle_epi8(g, f));\n    g = Add(Xor(c, g), a);\n    f = Add(Xor(be, f), d);\n    k = Add(k, be);\n    k = Add(k, _mm_shuffle_epi8(f, h));\n    f = Add(f, g);\n    g = Add(g, f);\n    g = Add(_mm_set1_epi32(len), Mulc1(g));\n  } else {\n    // len >= 80\n    // The following is loosely modelled after farmhashmk::Hash32.\n    size_t iters = (len - 1) / 80;\n    len -= iters * 80;\n\n#undef Chunk\n#define Chunk() do {                            \\\n  __m128i a = Fetch128(s);                      \\\n  __m128i b = Fetch128(s + 16);                 \\\n  __m128i c = Fetch128(s + 32);                 \\\n  __m128i d = Fetch128(s + 48);                 \\\n  __m128i e = Fetch128(s + 64);                 \\\n  h = Add(h, a);                                \\\n  g = Add(g, b);                                \\\n  g = Shuffle0321(g);                           \\\n  f = Add(f, c);                                \\\n  __m128i be = Add(b, Mulc1(e));                \\\n  h = Add(h, f);                                \\\n  f = Add(f, h);                                \\\n  h = Add(h, d);                                \\\n  q = Add(q, e);                                \\\n  h = Rol17(h);                                 \\\n  h = Mulc1(h);                                 \\\n  k = Xor(k, _mm_shuffle_epi8(g, f));           \\\n  g = Add(Xor(c, g), a);                        \\\n  f = Add(Xor(be, f), d);                       \\\n  std::swap(f, q);                              \\\n  q = _mm_aesimc_si128(q);                      \\\n  k = Add(k, be);                               \\\n  k = Add(k, _mm_shuffle_epi8(f, h));           \\\n  f = Add(f, g);                                \\\n  g = Add(g, f);                                \\\n  f = Mulc1(f);                                 \\\n} while (0)\n\n    q = g;\n    while (iters-- != 0) {\n      Chunk();\n      s += 80;\n    }\n\n    if (len != 0) {\n      h = Add(h, _mm_set1_epi32(len));\n      s = s + len - 80;\n      Chunk();\n    }\n  }\n\n  g = Shuffle0321(g);\n  k = Xor(k, g);\n  k = Xor(k, q);\n  h = Xor(h, q);\n  f = Mulc1(f);\n  k = Mulc2(k);\n  g = Mulc1(g);\n  h = Mulc2(h);\n  k = Add(k, _mm_shuffle_epi8(g, f));\n  h = Add(h, f);\n  f = Add(f, h);\n  g = Add(g, k);\n  k = Add(k, g);\n  k = Xor(k, _mm_shuffle_epi8(f, h));\n  __m128i buf[4];\n  buf[0] = f;\n  buf[1] = g;\n  buf[2] = k;\n  buf[3] = h;\n  s = reinterpret_cast<char*>(buf);\n  uint32_t x = Fetch(s);\n  uint32_t y = Fetch(s+4);\n  uint32_t z = Fetch(s+8);\n  x = _mm_crc32_u32(x, Fetch(s+12));\n  y = _mm_crc32_u32(y, Fetch(s+16));\n  z = _mm_crc32_u32(z * c1, Fetch(s+20));\n  x = _mm_crc32_u32(x, Fetch(s+24));\n  y = _mm_crc32_u32(y * c1, Fetch(s+28));\n  uint32_t o = y;\n  z = _mm_crc32_u32(z, Fetch(s+32));\n  x = _mm_crc32_u32(x * c1, Fetch(s+36));\n  y = _mm_crc32_u32(y, Fetch(s+40));\n  z = _mm_crc32_u32(z * c1, Fetch(s+44));\n  x = _mm_crc32_u32(x, Fetch(s+48));\n  y = _mm_crc32_u32(y * c1, Fetch(s+52));\n  z = _mm_crc32_u32(z, Fetch(s+56));\n  x = _mm_crc32_u32(x, Fetch(s+60));\n  return (o - x + y - z) * c1;\n}\n\n#undef Chunk\n#undef Murk\n#undef Mulc2\n#undef Mulc1\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  if (len <= 24) {\n    if (len >= 13) return farmhashmk::Hash32Len13to24(s, len, seed * c1);\n    else if (len >= 5) return farmhashmk::Hash32Len5to12(s, len, seed);\n    else return farmhashmk::Hash32Len0to4(s, len, seed);\n  }\n  uint32_t h = farmhashmk::Hash32Len13to24(s, 24, seed ^ len);\n  return _mm_crc32_u32(Hash32(s + 24, len - 24) + seed, h);\n}\n\n#endif\n}  // namespace farmhashsu\nnamespace farmhashsa {\n#if !can_use_sse42\n\nuint32_t Hash32(const char *s, size_t len) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return s == NULL ? 0 : len;\n}\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  FARMHASH_DIE_IF_MISCONFIGURED;\n  return seed + Hash32(s, len);\n}\n\n#else\n\n#undef Fetch\n#define Fetch Fetch32\n\n#undef Rotate\n#define Rotate Rotate32\n\n#undef Bswap\n#define Bswap Bswap32\n\n// Helpers for data-parallel operations (4x 32-bits).\nSTATIC_INLINE __m128i Add(__m128i x, __m128i y) { return _mm_add_epi32(x, y); }\nSTATIC_INLINE __m128i Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); }\nSTATIC_INLINE __m128i Or(__m128i x, __m128i y) { return _mm_or_si128(x, y); }\nSTATIC_INLINE __m128i Mul(__m128i x, __m128i y) { return _mm_mullo_epi32(x, y); }\nSTATIC_INLINE __m128i Mul5(__m128i x) { return Add(x, _mm_slli_epi32(x, 2)); }\nSTATIC_INLINE __m128i Rotate(__m128i x, int c) {\n  return Or(_mm_slli_epi32(x, c),\n            _mm_srli_epi32(x, 32 - c));\n}\nSTATIC_INLINE __m128i Rot17(__m128i x) { return Rotate(x, 17); }\nSTATIC_INLINE __m128i Rot19(__m128i x) { return Rotate(x, 19); }\nSTATIC_INLINE __m128i Shuffle0321(__m128i x) {\n  return _mm_shuffle_epi32(x, (0 << 6) + (3 << 4) + (2 << 2) + (1 << 0));\n}\n\nuint32_t Hash32(const char *s, size_t len) {\n  const uint32_t seed = 81;\n  if (len <= 24) {\n    return len <= 12 ?\n        (len <= 4 ?\n         farmhashmk::Hash32Len0to4(s, len) :\n         farmhashmk::Hash32Len5to12(s, len)) :\n        farmhashmk::Hash32Len13to24(s, len);\n  }\n\n  if (len < 40) {\n    uint32_t a = len, b = seed * c2, c = a + b;\n    a += Fetch(s + len - 4);\n    b += Fetch(s + len - 20);\n    c += Fetch(s + len - 16);\n    uint32_t d = a;\n    a = folly::external::farmhash::Rotate32(a, 21);\n    a = Mur(a, Mur(b, Mur(c, d)));\n    a += Fetch(s + len - 12);\n    b += Fetch(s + len - 8);\n    d += a;\n    a += d;\n    b = Mur(b, d) * c2;\n    a = _mm_crc32_u32(a, b + c);\n    return farmhashmk::Hash32Len13to24(s, (len + 1) / 2, a) + b;\n  }\n\n#undef Mulc1\n#define Mulc1(x) Mul((x), cc1)\n\n#undef Mulc2\n#define Mulc2(x) Mul((x), cc2)\n\n#undef Murk\n#define Murk(a, h)                              \\\n  Add(k,                                        \\\n      Mul5(                                     \\\n          Rot19(                                \\\n              Xor(                              \\\n                  Mulc2(                        \\\n                      Rot17(                    \\\n                          Mulc1(a))),           \\\n                  (h)))))\n\n  const __m128i cc1 = _mm_set1_epi32(c1);\n  const __m128i cc2 = _mm_set1_epi32(c2);\n  __m128i h = _mm_set1_epi32(seed);\n  __m128i g = _mm_set1_epi32(c1 * seed);\n  __m128i f = g;\n  __m128i k = _mm_set1_epi32(0xe6546b64);\n  if (len < 80) {\n    __m128i a = Fetch128(s);\n    __m128i b = Fetch128(s + 16);\n    __m128i c = Fetch128(s + (len - 15) / 2);\n    __m128i d = Fetch128(s + len - 32);\n    __m128i e = Fetch128(s + len - 16);\n    h = Add(h, a);\n    g = Add(g, b);\n    g = Shuffle0321(g);\n    f = Add(f, c);\n    __m128i be = Add(b, Mulc1(e));\n    h = Add(h, f);\n    f = Add(f, h);\n    h = Add(Murk(d, h), e);\n    k = Xor(k, _mm_shuffle_epi8(g, f));\n    g = Add(Xor(c, g), a);\n    f = Add(Xor(be, f), d);\n    k = Add(k, be);\n    k = Add(k, _mm_shuffle_epi8(f, h));\n    f = Add(f, g);\n    g = Add(g, f);\n    g = Add(_mm_set1_epi32(len), Mulc1(g));\n  } else {\n    // len >= 80\n    // The following is loosely modelled after farmhashmk::Hash32.\n    size_t iters = (len - 1) / 80;\n    len -= iters * 80;\n\n#undef Chunk\n#define Chunk() do {                            \\\n  __m128i a = Fetch128(s);                       \\\n  __m128i b = Fetch128(s + 16);                  \\\n  __m128i c = Fetch128(s + 32);                  \\\n  __m128i d = Fetch128(s + 48);                  \\\n  __m128i e = Fetch128(s + 64);                  \\\n  h = Add(h, a);                                \\\n  g = Add(g, b);                                \\\n  g = Shuffle0321(g);                           \\\n  f = Add(f, c);                                \\\n  __m128i be = Add(b, Mulc1(e));                \\\n  h = Add(h, f);                                \\\n  f = Add(f, h);                                \\\n  h = Add(Murk(d, h), e);                       \\\n  k = Xor(k, _mm_shuffle_epi8(g, f));           \\\n  g = Add(Xor(c, g), a);                        \\\n  f = Add(Xor(be, f), d);                       \\\n  k = Add(k, be);                               \\\n  k = Add(k, _mm_shuffle_epi8(f, h));           \\\n  f = Add(f, g);                                \\\n  g = Add(g, f);                                \\\n  f = Mulc1(f);                                 \\\n} while (0)\n\n    while (iters-- != 0) {\n      Chunk();\n      s += 80;\n    }\n\n    if (len != 0) {\n      h = Add(h, _mm_set1_epi32(len));\n      s = s + len - 80;\n      Chunk();\n    }\n  }\n\n  g = Shuffle0321(g);\n  k = Xor(k, g);\n  f = Mulc1(f);\n  k = Mulc2(k);\n  g = Mulc1(g);\n  h = Mulc2(h);\n  k = Add(k, _mm_shuffle_epi8(g, f));\n  h = Add(h, f);\n  f = Add(f, h);\n  g = Add(g, k);\n  k = Add(k, g);\n  k = Xor(k, _mm_shuffle_epi8(f, h));\n  __m128i buf[4];\n  buf[0] = f;\n  buf[1] = g;\n  buf[2] = k;\n  buf[3] = h;\n  s = reinterpret_cast<char*>(buf);\n  uint32_t x = Fetch(s);\n  uint32_t y = Fetch(s+4);\n  uint32_t z = Fetch(s+8);\n  x = _mm_crc32_u32(x, Fetch(s+12));\n  y = _mm_crc32_u32(y, Fetch(s+16));\n  z = _mm_crc32_u32(z * c1, Fetch(s+20));\n  x = _mm_crc32_u32(x, Fetch(s+24));\n  y = _mm_crc32_u32(y * c1, Fetch(s+28));\n  uint32_t o = y;\n  z = _mm_crc32_u32(z, Fetch(s+32));\n  x = _mm_crc32_u32(x * c1, Fetch(s+36));\n  y = _mm_crc32_u32(y, Fetch(s+40));\n  z = _mm_crc32_u32(z * c1, Fetch(s+44));\n  x = _mm_crc32_u32(x, Fetch(s+48));\n  y = _mm_crc32_u32(y * c1, Fetch(s+52));\n  z = _mm_crc32_u32(z, Fetch(s+56));\n  x = _mm_crc32_u32(x, Fetch(s+60));\n  return (o - x + y - z) * c1;\n}\n\n#undef Chunk\n#undef Murk\n#undef Mulc2\n#undef Mulc1\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  if (len <= 24) {\n    if (len >= 13) return farmhashmk::Hash32Len13to24(s, len, seed * c1);\n    else if (len >= 5) return farmhashmk::Hash32Len5to12(s, len, seed);\n    else return farmhashmk::Hash32Len0to4(s, len, seed);\n  }\n  uint32_t h = farmhashmk::Hash32Len13to24(s, 24, seed ^ len);\n  return _mm_crc32_u32(Hash32(s + 24, len - 24) + seed, h);\n}\n\n#endif\n}  // namespace farmhashsa\nnamespace farmhashcc {\n// This file provides a 32-bit hash equivalent to CityHash32 (v1.1.1)\n// and a 128-bit hash equivalent to CityHash128 (v1.1.1).  It also provides\n// a seeded 32-bit hash function similar to CityHash32.\n\n#undef Fetch\n#define Fetch Fetch32\n\n#undef Rotate\n#define Rotate Rotate32\n\n#undef Bswap\n#define Bswap Bswap32\n\nSTATIC_INLINE uint32_t Hash32Len13to24(const char *s, size_t len) {\n  uint32_t a = Fetch(s - 4 + (len >> 1));\n  uint32_t b = Fetch(s + 4);\n  uint32_t c = Fetch(s + len - 8);\n  uint32_t d = Fetch(s + (len >> 1));\n  uint32_t e = Fetch(s);\n  uint32_t f = Fetch(s + len - 4);\n  uint32_t h = len;\n\n  return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));\n}\n\nSTATIC_INLINE uint32_t Hash32Len0to4(const char *s, size_t len) {\n  uint32_t b = 0;\n  uint32_t c = 9;\n  for (size_t i = 0; i < len; i++) {\n    signed char v = s[i];\n    b = b * c1 + v;\n    c ^= b;\n  }\n  return fmix(Mur(b, Mur(len, c)));\n}\n\nSTATIC_INLINE uint32_t Hash32Len5to12(const char *s, size_t len) {\n  uint32_t a = len, b = len * 5, c = 9, d = b;\n  a += Fetch(s);\n  b += Fetch(s + len - 4);\n  c += Fetch(s + ((len >> 1) & 4));\n  return fmix(Mur(c, Mur(b, Mur(a, d))));\n}\n\nuint32_t Hash32(const char *s, size_t len) {\n  if (len <= 24) {\n    return len <= 12 ?\n        (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) :\n        Hash32Len13to24(s, len);\n  }\n\n  // len > 24\n  uint32_t h = len, g = c1 * len, f = g;\n  {\n    uint32_t a0 = Rotate(Fetch(s + len - 4) * c1, 17) * c2;\n    uint32_t a1 = Rotate(Fetch(s + len - 8) * c1, 17) * c2;\n    uint32_t a2 = Rotate(Fetch(s + len - 16) * c1, 17) * c2;\n    uint32_t a3 = Rotate(Fetch(s + len - 12) * c1, 17) * c2;\n    uint32_t a4 = Rotate(Fetch(s + len - 20) * c1, 17) * c2;\n    h ^= a0;\n    h = Rotate(h, 19);\n    h = h * 5 + 0xe6546b64;\n    h ^= a2;\n    h = Rotate(h, 19);\n    h = h * 5 + 0xe6546b64;\n    g ^= a1;\n    g = Rotate(g, 19);\n    g = g * 5 + 0xe6546b64;\n    g ^= a3;\n    g = Rotate(g, 19);\n    g = g * 5 + 0xe6546b64;\n    f += a4;\n    f = Rotate(f, 19);\n    f = f * 5 + 0xe6546b64;\n  }\n  size_t iters = (len - 1) / 20;\n  do {\n    uint32_t a0 = Rotate(Fetch(s) * c1, 17) * c2;\n    uint32_t a1 = Fetch(s + 4);\n    uint32_t a2 = Rotate(Fetch(s + 8) * c1, 17) * c2;\n    uint32_t a3 = Rotate(Fetch(s + 12) * c1, 17) * c2;\n    uint32_t a4 = Fetch(s + 16);\n    h ^= a0;\n    h = Rotate(h, 18);\n    h = h * 5 + 0xe6546b64;\n    f += a1;\n    f = Rotate(f, 19);\n    f = f * c1;\n    g += a2;\n    g = Rotate(g, 18);\n    g = g * 5 + 0xe6546b64;\n    h ^= a3 + a1;\n    h = Rotate(h, 19);\n    h = h * 5 + 0xe6546b64;\n    g ^= a4;\n    g = Bswap(g) * 5;\n    h += a4 * 5;\n    h = Bswap(h);\n    f += a0;\n    PERMUTE3(f, h, g);\n    s += 20;\n  } while (--iters != 0);\n  g = Rotate(g, 11) * c1;\n  g = Rotate(g, 17) * c1;\n  f = Rotate(f, 11) * c1;\n  f = Rotate(f, 17) * c1;\n  h = Rotate(h + g, 19);\n  h = h * 5 + 0xe6546b64;\n  h = Rotate(h, 17) * c1;\n  h = Rotate(h + f, 19);\n  h = h * 5 + 0xe6546b64;\n  h = Rotate(h, 17) * c1;\n  return h;\n}\n\nuint32_t Hash32WithSeed(const char *s, size_t len, uint32_t seed) {\n  if (len <= 24) {\n    if (len >= 13) return farmhashmk::Hash32Len13to24(s, len, seed * c1);\n    else if (len >= 5) return farmhashmk::Hash32Len5to12(s, len, seed);\n    else return farmhashmk::Hash32Len0to4(s, len, seed);\n  }\n  uint32_t h = farmhashmk::Hash32Len13to24(s, 24, seed ^ len);\n  return Mur(Hash32(s + 24, len - 24) + seed, h);\n}\n\n#undef Fetch\n#define Fetch Fetch64\n\n#undef Rotate\n#define Rotate Rotate64\n\n#undef Bswap\n#define Bswap Bswap64\n\nSTATIC_INLINE uint64_t ShiftMix(uint64_t val) {\n  return val ^ (val >> 47);\n}\n\nSTATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v) {\n  return Hash128to64(Uint128(u, v));\n}\n\nSTATIC_INLINE uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) {\n  // Murmur-inspired hashing.\n  uint64_t a = (u ^ v) * mul;\n  a ^= (a >> 47);\n  uint64_t b = (v ^ a) * mul;\n  b ^= (b >> 47);\n  b *= mul;\n  return b;\n}\n\nSTATIC_INLINE uint64_t HashLen0to16(const char *s, size_t len) {\n  if (len >= 8) {\n    uint64_t mul = k2 + len * 2;\n    uint64_t a = Fetch(s) + k2;\n    uint64_t b = Fetch(s + len - 8);\n    uint64_t c = Rotate(b, 37) * mul + a;\n    uint64_t d = (Rotate(a, 25) + b) * mul;\n    return HashLen16(c, d, mul);\n  }\n  if (len >= 4) {\n    uint64_t mul = k2 + len * 2;\n    uint64_t a = Fetch32(s);\n    return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);\n  }\n  if (len > 0) {\n    uint8_t a = s[0];\n    uint8_t b = s[len >> 1];\n    uint8_t c = s[len - 1];\n    uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);\n    uint32_t z = len + (static_cast<uint32_t>(c) << 2);\n    return ShiftMix(y * k2 ^ z * k0) * k2;\n  }\n  return k2;\n}\n\n// Return a 16-byte hash for 48 bytes.  Quick and dirty.\n// Callers do best to use \"random-looking\" values for a and b.\nSTATIC_INLINE pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(\n    uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, uint64_t b) {\n  a += w;\n  b = Rotate(b + a + z, 21);\n  uint64_t c = a;\n  a += x;\n  a += y;\n  b += Rotate(a, 44);\n  return make_pair(a + z, b + c);\n}\n\n// Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.\nSTATIC_INLINE pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(\n    const char* s, uint64_t a, uint64_t b) {\n  return WeakHashLen32WithSeeds(Fetch(s),\n                                Fetch(s + 8),\n                                Fetch(s + 16),\n                                Fetch(s + 24),\n                                a,\n                                b);\n}\n\n\n\n// A subroutine for CityHash128().  Returns a decent 128-bit hash for strings\n// of any length representable in signed long.  Based on City and Murmur.\nSTATIC_INLINE uint128_t CityMurmur(const char *s, size_t len, uint128_t seed) {\n  uint64_t a = Uint128Low64(seed);\n  uint64_t b = Uint128High64(seed);\n  uint64_t c = 0;\n  uint64_t d = 0;\n  signed long l = len - 16;\n  if (l <= 0) {  // len <= 16\n    a = ShiftMix(a * k1) * k1;\n    c = b * k1 + HashLen0to16(s, len);\n    d = ShiftMix(a + (len >= 8 ? Fetch(s) : c));\n  } else {  // len > 16\n    c = HashLen16(Fetch(s + len - 8) + k1, a);\n    d = HashLen16(b + len, c + Fetch(s + len - 16));\n    a += d;\n    do {\n      a ^= ShiftMix(Fetch(s) * k1) * k1;\n      a *= k1;\n      b ^= a;\n      c ^= ShiftMix(Fetch(s + 8) * k1) * k1;\n      c *= k1;\n      d ^= c;\n      s += 16;\n      l -= 16;\n    } while (l > 0);\n  }\n  a = HashLen16(a, c);\n  b = HashLen16(d, b);\n  return Uint128(a ^ b, HashLen16(b, a));\n}\n\nuint128_t CityHash128WithSeed(const char *s, size_t len, uint128_t seed) {\n  if (len < 128) {\n    return CityMurmur(s, len, seed);\n  }\n\n  // We expect len >= 128 to be the common case.  Keep 56 bytes of state:\n  // v, w, x, y, and z.\n  pair<uint64_t, uint64_t> v, w;\n  uint64_t x = Uint128Low64(seed);\n  uint64_t y = Uint128High64(seed);\n  uint64_t z = len * k1;\n  v.first = Rotate(y ^ k1, 49) * k1 + Fetch(s);\n  v.second = Rotate(v.first, 42) * k1 + Fetch(s + 8);\n  w.first = Rotate(y + z, 35) * k1 + x;\n  w.second = Rotate(x + Fetch(s + 88), 53) * k1;\n\n  // This is the same inner loop as CityHash64(), manually unrolled.\n  do {\n    x = Rotate(x + y + v.first + Fetch(s + 8), 37) * k1;\n    y = Rotate(y + v.second + Fetch(s + 48), 42) * k1;\n    x ^= w.second;\n    y += v.first + Fetch(s + 40);\n    z = Rotate(z + w.first, 33) * k1;\n    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);\n    w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16));\n    std::swap(z, x);\n    s += 64;\n    x = Rotate(x + y + v.first + Fetch(s + 8), 37) * k1;\n    y = Rotate(y + v.second + Fetch(s + 48), 42) * k1;\n    x ^= w.second;\n    y += v.first + Fetch(s + 40);\n    z = Rotate(z + w.first, 33) * k1;\n    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);\n    w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch(s + 16));\n    std::swap(z, x);\n    s += 64;\n    len -= 128;\n  } while (LIKELY(len >= 128));\n  x += Rotate(v.first + z, 49) * k0;\n  y = y * k0 + Rotate(w.second, 37);\n  z = z * k0 + Rotate(w.first, 27);\n  w.first *= 9;\n  v.first *= k0;\n  // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.\n  for (size_t tail_done = 0; tail_done < len; ) {\n    tail_done += 32;\n    y = Rotate(x + y, 42) * k0 + v.second;\n    w.first += Fetch(s + len - tail_done + 16);\n    x = x * k0 + w.first;\n    z += w.second + Fetch(s + len - tail_done);\n    w.second += v.first;\n    v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second);\n    v.first *= k0;\n  }\n  // At this point our 56 bytes of state should contain more than\n  // enough information for a strong 128-bit hash.  We use two\n  // different 56-byte-to-8-byte hashes to get a 16-byte final result.\n  x = HashLen16(x, v.first);\n  y = HashLen16(y + z, w.first);\n  return Uint128(HashLen16(x + v.second, w.second) + y,\n                 HashLen16(x + w.second, y + v.second));\n}\n\nSTATIC_INLINE uint128_t CityHash128(const char *s, size_t len) {\n  return len >= 16 ?\n      CityHash128WithSeed(s + 16, len - 16,\n                          Uint128(Fetch(s), Fetch(s + 8) + k0)) :\n      CityHash128WithSeed(s, len, Uint128(k0, k1));\n}\n\nuint128_t Fingerprint128(const char* s, size_t len) {\n  return CityHash128(s, len);\n}\n}  // namespace farmhashcc\n\n// BASIC STRING HASHING\n\n// Hash function for a byte array.  See also Hash(), below.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint32_t Hash32(const char* s, size_t len) {\n  return DebugTweak(\n      (can_use_sse41 & x86_64) ? farmhashnt::Hash32(s, len) :\n      (can_use_sse42 & can_use_aesni) ? farmhashsu::Hash32(s, len) :\n      can_use_sse42 ? farmhashsa::Hash32(s, len) :\n      farmhashmk::Hash32(s, len));\n}\n\n// Hash function for a byte array.  For convenience, a 32-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed) {\n  return DebugTweak(\n      (can_use_sse41 & x86_64) ? farmhashnt::Hash32WithSeed(s, len, seed) :\n      (can_use_sse42 & can_use_aesni) ? farmhashsu::Hash32WithSeed(s, len, seed) :\n      can_use_sse42 ? farmhashsa::Hash32WithSeed(s, len, seed) :\n      farmhashmk::Hash32WithSeed(s, len, seed));\n}\n\n// Hash function for a byte array.  For convenience, a 64-bit seed is also\n// hashed into the result.  See also Hash(), below.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint64_t Hash64(const char* s, size_t len) {\n  return DebugTweak(\n      (can_use_sse42 & x86_64) ?\n      farmhashte::Hash64(s, len) :\n      farmhashxo::Hash64(s, len));\n}\n\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nsize_t Hash(const char* s, size_t len) {\n  return sizeof(size_t) == 8 ? Hash64(s, len) : Hash32(s, len);\n}\n\n// Hash function for a byte array.  For convenience, a 64-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed) {\n  return DebugTweak(farmhashna::Hash64WithSeed(s, len, seed));\n}\n\n// Hash function for a byte array.  For convenience, two seeds are also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint64_t Hash64WithSeeds(const char* s, size_t len, uint64_t seed0, uint64_t seed1) {\n  return DebugTweak(farmhashna::Hash64WithSeeds(s, len, seed0, seed1));\n}\n\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint128_t Hash128(const char* s, size_t len) {\n  return DebugTweak(farmhashcc::Fingerprint128(s, len));\n}\n\n// Hash function for a byte array.  For convenience, a 128-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint128_t Hash128WithSeed(const char* s, size_t len, uint128_t seed) {\n  return DebugTweak(farmhashcc::CityHash128WithSeed(s, len, seed));\n}\n\n// BASIC NON-STRING HASHING\n\n// FINGERPRINTING (i.e., good, portable, forever-fixed hash functions)\n\n// Fingerprint function for a byte array.  Most useful in 32-bit binaries.\nuint32_t Fingerprint32(const char* s, size_t len) {\n  return farmhashmk::Hash32(s, len);\n}\n\n// Fingerprint function for a byte array.\nuint64_t Fingerprint64(const char* s, size_t len) {\n  return farmhashna::Hash64(s, len);\n}\n\n// Fingerprint function for a byte array.\nuint128_t Fingerprint128(const char* s, size_t len) {\n  return farmhashcc::Fingerprint128(s, len);\n}\n\n// Older and still available but perhaps not as fast as the above:\n//   farmhashns::Hash32{,WithSeed}()\n\n} // namespace farmhash\n} // namespace external\n} // namespace folly\n"
  },
  {
    "path": "folly/external/farmhash/farmhash.h",
    "content": "// Copyright (c) 2014 Google, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// FarmHash, by Geoff Pike\n\n//\n// http://code.google.com/p/farmhash/\n//\n// This file provides a few functions for hashing strings and other\n// data.  All of them are high-quality functions in the sense that\n// they do well on standard tests such as Austin Appleby's SMHasher.\n// They're also fast.  FarmHash is the successor to CityHash.\n//\n// Functions in the FarmHash family are not suitable for cryptography.\n//\n// WARNING: This code has been only lightly tested on big-endian platforms!\n// It is known to work well on little-endian platforms that have a small penalty\n// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.\n// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;\n// bug reports are welcome.\n//\n// By the way, for some hash functions, given strings a and b, the hash\n// of a+b is easily derived from the hashes of a and b.  This property\n// doesn't hold for any hash functions in this file.\n\n// clang-format off\n\n#pragma once\n\n#include <assert.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>   // for memcpy and memset\n#include <utility>\n\n#include <folly/portability/Config.h>\n\nnamespace folly {\nnamespace external {\nnamespace farmhash {\n\n#if FOLLY_HAVE_INT128_T\nusing uint128_t = unsigned __int128;\n\ninline uint64_t Uint128Low64(const uint128_t x) {\n  return static_cast<uint64_t>(x);\n}\ninline uint64_t Uint128High64(const uint128_t x) {\n  return static_cast<uint64_t>(x >> 64);\n}\ninline uint128_t Uint128(uint64_t lo, uint64_t hi) {\n  return lo + (((uint128_t)hi) << 64);\n}\n#else\ntypedef std::pair<uint64_t, uint64_t> uint128_t;\ninline uint64_t Uint128Low64(const uint128_t x) { return x.first; }\ninline uint64_t Uint128High64(const uint128_t x) { return x.second; }\ninline uint128_t Uint128(uint64_t lo, uint64_t hi) { return uint128_t(lo, hi); }\n#endif\n\n\n// BASIC STRING HASHING\n\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nsize_t Hash(const char* s, size_t len);\n\n// Hash function for a byte array.  Most useful in 32-bit binaries.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint32_t Hash32(const char* s, size_t len);\n\n// Hash function for a byte array.  For convenience, a 32-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\n\n// Hash 128 input bits down to 64 bits of output.\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint64_t Hash64(const char* s, size_t len);\n\n// Hash function for a byte array.  For convenience, a 64-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);\n\n// Hash function for a byte array.  For convenience, two seeds are also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint64_t Hash64WithSeeds(const char* s, size_t len,\n                       uint64_t seed0, uint64_t seed1);\n\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint128_t Hash128(const char* s, size_t len);\n\n// Hash function for a byte array.  For convenience, a 128-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\nuint128_t Hash128WithSeed(const char* s, size_t len, uint128_t seed);\n\n// BASIC NON-STRING HASHING\n\n// This is intended to be a reasonably good hash function.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ninline uint64_t Hash128to64(uint128_t x) {\n  // Murmur-inspired hashing.\n  const uint64_t kMul = 0x9ddfea08eb382d69ULL;\n  uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;\n  a ^= (a >> 47);\n  uint64_t b = (Uint128High64(x) ^ a) * kMul;\n  b ^= (b >> 47);\n  b *= kMul;\n  return b;\n}\n\n// FINGERPRINTING (i.e., good, portable, forever-fixed hash functions)\n\n// Fingerprint function for a byte array.  Most useful in 32-bit binaries.\nuint32_t Fingerprint32(const char* s, size_t len);\n\n// Fingerprint function for a byte array.\nuint64_t Fingerprint64(const char* s, size_t len);\n\n// Fingerprint function for a byte array.\nuint128_t Fingerprint128(const char* s, size_t len);\n\n// This is intended to be a good fingerprinting primitive.\n// See below for more overloads.\ninline uint64_t Fingerprint(uint128_t x) {\n  // Murmur-inspired hashing.\n  const uint64_t kMul = 0x9ddfea08eb382d69ULL;\n  uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;\n  a ^= (a >> 47);\n  uint64_t b = (Uint128High64(x) ^ a) * kMul;\n  b ^= (b >> 44);\n  b *= kMul;\n  b ^= (b >> 41);\n  b *= kMul;\n  return b;\n}\n\n// This is intended to be a good fingerprinting primitive.\ninline uint64_t Fingerprint(uint64_t x) {\n  // Murmur-inspired hashing.\n  const uint64_t kMul = 0x9ddfea08eb382d69ULL;\n  uint64_t b = x * kMul;\n  b ^= (b >> 44);\n  b *= kMul;\n  b ^= (b >> 41);\n  b *= kMul;\n  return b;\n}\n\n// Convenience functions to hash or fingerprint C++ strings.\n// These require that Str::data() return a pointer to the first char\n// (as a const char*) and that Str::length() return the string's length;\n// they work with std::string, for example.\n\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline size_t Hash(const Str& s) {\n  assert(sizeof(s[0]) == 1);\n  return Hash(s.data(), s.length());\n}\n\n// Hash function for a byte array.  Most useful in 32-bit binaries.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline uint32_t Hash32(const Str& s) {\n  assert(sizeof(s[0]) == 1);\n  return Hash32(s.data(), s.length());\n}\n\n// Hash function for a byte array.  For convenience, a 32-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline uint32_t Hash32WithSeed(const Str& s, uint32_t seed) {\n  assert(sizeof(s[0]) == 1);\n  return Hash32WithSeed(s.data(), s.length(), seed);\n}\n\n// Hash 128 input bits down to 64 bits of output.\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline uint64_t Hash64(const Str& s) {\n  assert(sizeof(s[0]) == 1);\n  return Hash64(s.data(), s.length());\n}\n\n// Hash function for a byte array.  For convenience, a 64-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline uint64_t Hash64WithSeed(const Str& s, uint64_t seed) {\n  assert(sizeof(s[0]) == 1);\n  return Hash64WithSeed(s.data(), s.length(), seed);\n}\n\n// Hash function for a byte array.  For convenience, two seeds are also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline uint64_t Hash64WithSeeds(const Str& s, uint64_t seed0, uint64_t seed1) {\n  assert(sizeof(s[0]) == 1);\n  return Hash64WithSeeds(s.data(), s.length(), seed0, seed1);\n}\n\n// Hash function for a byte array.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline uint128_t Hash128(const Str& s) {\n  assert(sizeof(s[0]) == 1);\n  return Hash128(s.data(), s.length());\n}\n\n// Hash function for a byte array.  For convenience, a 128-bit seed is also\n// hashed into the result.\n// May change from time to time, may differ on different platforms, may differ\n// depending on NDEBUG.\ntemplate <typename Str>\ninline uint128_t Hash128WithSeed(const Str& s, uint128_t seed) {\n  assert(sizeof(s[0]) == 1);\n  return Hash128(s.data(), s.length(), seed);\n}\n\n// FINGERPRINTING (i.e., good, portable, forever-fixed hash functions)\n\n// Fingerprint function for a byte array.  Most useful in 32-bit binaries.\ntemplate <typename Str>\ninline uint32_t Fingerprint32(const Str& s) {\n  assert(sizeof(s[0]) == 1);\n  return Fingerprint32(s.data(), s.length());\n}\n\n// Fingerprint 128 input bits down to 64 bits of output.\n// Fingerprint function for a byte array.\ntemplate <typename Str>\ninline uint64_t Fingerprint64(const Str& s) {\n  assert(sizeof(s[0]) == 1);\n  return Fingerprint64(s.data(), s.length());\n}\n\n// Fingerprint function for a byte array.\ntemplate <typename Str>\ninline uint128_t Fingerprint128(const Str& s) {\n  assert(sizeof(s[0]) == 1);\n  return Fingerprint128(s.data(), s.length());\n}\n\n//// internal variants\n\nnamespace test {\nextern bool returnZeroIfMisconfigured;\n}\n\nnamespace farmhashna {\nuint64_t Hash64(const char* s, size_t len);\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);\nuint64_t\nHash64WithSeeds(const char* s, size_t len, uint64_t seed0, uint64_t seed1);\n} // namespace farmhashna\nnamespace farmhashuo {\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);\nuint64_t Hash64(const char* s, size_t len);\n} // namespace farmhashuo\nnamespace farmhashxo {\nuint64_t Hash64(const char* s, size_t len);\nuint64_t\nHash64WithSeeds(const char* s, size_t len, uint64_t seed0, uint64_t seed1);\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);\n} // namespace farmhashxo\nnamespace farmhashte {\nuint64_t Hash64(const char* s, size_t len);\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);\nuint64_t Hash64(const char* s, size_t len);\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);\nuint64_t\nHash64WithSeeds(const char* s, size_t len, uint64_t seed0, uint64_t seed1);\n} // namespace farmhashte\nnamespace farmhashnt {\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\n} // namespace farmhashnt\nnamespace farmhashmk {\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\n} // namespace farmhashmk\nnamespace farmhashsu {\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\n} // namespace farmhashsu\nnamespace farmhashsa {\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\n} // namespace farmhashsa\nnamespace farmhashcc {\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\nuint128_t CityHash128WithSeed(const char* s, size_t len, uint128_t seed);\nuint128_t Fingerprint128(const char* s, size_t len);\nuint32_t Hash32(const char* s, size_t len);\nuint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);\nuint64_t Hash64(const char* s, size_t len);\nsize_t Hash(const char* s, size_t len);\nuint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);\nuint64_t\nHash64WithSeeds(const char* s, size_t len, uint64_t seed0, uint64_t seed1);\nuint128_t Hash128(const char* s, size_t len);\nuint128_t Hash128WithSeed(const char* s, size_t len, uint128_t seed);\nuint32_t Fingerprint32(const char* s, size_t len);\nuint64_t Fingerprint64(const char* s, size_t len);\nuint128_t Fingerprint128(const char* s, size_t len);\n} // namespace farmhashcc\n\n} // namespace farmhash\n} // namespace external\n} // namespace folly\n"
  },
  {
    "path": "folly/external/farmhash/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"farmhash_test\",\n    srcs = [\"farmhash_test.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/external/farmhash:farmhash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/farmhash/test/farmhash_test.cpp",
    "content": "// Copyright (c) 2014 Google, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// FarmHash, by Geoff Pike\n\n// clang-format off\n\n#define FARMHASH_DEBUG 0\n\n#include <folly/external/farmhash/farmhash.h>\n\n#include <folly/portability/GTest.h>\n\n// Some primes between 2^63 and 2^64 for various uses.\nstatic const uint64_t k0 = 0xc3a5c85c97cb3127ULL;\n//static const uint64_t k1 = 0xb492b66fbe98f273ULL;\n//static const uint64_t k2 = 0x9ae16a3b2f90404fULL;\n\n// Magic numbers for 32-bit hashing.  Copied from Murmur3.\nstatic const uint32_t c1 = 0xcc9e2d51;\n//static const uint32_t c2 = 0x1b873593;\n\nusing namespace folly::external::farmhash;\n\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\n//static const uint64_t kSeed0 = 1234567;\n//static const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    test::returnZeroIfMisconfigured = true;\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#if 0\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n#endif\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashccTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n4223616069u,\n3696677242u,\n1039179260u, 1690343979u, 1018511555u, 2464489001u,\n20368522u, 2663783964u, 175201532u, 1619210592u,\n4081014168u,\n2576519988u,\n3285042206u, 502478099u, 739479538u, 1500332790u,\n13754768u, 3789353455u, 3473868058u, 1909255088u,\n2212771159u,\n1112731063u,\n826915357u, 2893489933u, 118369799u, 1848668220u,\n1308219822u, 249416982u, 64306364u, 4221800195u,\n1020067935u,\n3955445564u,\n563346294u, 550236731u, 2339016688u, 1826259714u,\n3872358639u, 2295981050u, 1870005390u, 4015628802u,\n1451961420u,\n653440099u,\n1292493871u, 164377749u, 1717712483u, 463414587u,\n3924343675u, 1050492084u, 3566618804u, 2046983362u,\n31917516u,\n2957164615u,\n230718965u, 999595115u, 3534822176u, 2175709186u,\n965707431u, 441796222u, 2481718051u, 1827777486u,\n2590087362u,\n3879448744u,\n3515079898u, 1601433082u, 982764532u, 254808716u,\n1293372530u, 4205605817u, 947001462u, 1138890052u,\n176305566u,\n2447367541u,\n2973802542u, 4123621138u, 3083865840u, 1706367795u,\n792114347u, 2880110657u, 440613768u, 195054868u,\n1359016305u,\n3363804638u,\n649488537u, 1624045597u, 1441938215u, 3147758996u,\n3199173578u, 2597283203u, 2191333609u, 3763129144u,\n1117290165u,\n1062549743u,\n2565615889u, 1046361554u, 1581968261u, 1058773671u,\n1123053168u, 3807622275u, 1486749916u, 3900816089u,\n2437877004u,\n1894455839u,\n1912520953u, 1914997013u, 561048608u, 1643267444u,\n3671572006u, 194811086u, 1468911468u, 2179206286u,\n673206794u,\n3486923651u,\n3741426466u, 3292160512u, 697001377u, 1900763774u,\n3726097344u, 629282039u, 3578723715u, 2868028489u,\n3269862919u,\n2303349487u,\n3643953525u, 2307255916u, 849996280u, 732080434u,\n909961480u, 3542445214u, 2628347095u, 4236856917u,\n1380660650u,\n2631821908u,\n2007289004u, 3509705198u, 3788541675u, 789457322u,\n3090670546u, 638977894u, 3503881773u, 947102987u,\n1525325287u,\n1816697045u,\n2706647405u, 288763142u, 3505438495u, 481308609u,\n2882636782u, 3745162621u, 3503467033u, 428247823u,\n176408838u,\n333551502u,\n1001068721u, 1681483651u, 75380831u, 4191469679u,\n3627361839u, 2736617386u, 3120737438u, 1297502456u,\n864896482u,\n85674920u,\n2886047255u, 4119881331u, 2496990525u, 3442502055u,\n1806582817u, 3186345024u, 4099591287u, 2560171465u,\n3489229104u,\n3065015872u,\n2755089808u, 3098442882u, 378524719u, 2664097023u,\n1771960725u, 2901182183u, 55258521u, 1266621443u,\n581644891u,\n37790450u,\n1800731704u, 3601350920u, 53428754u, 2759476837u,\n3391093099u, 1496510311u, 2511119507u, 2636877410u,\n631613207u,\n1573846064u,\n260484875u, 1088212603u, 2369525206u, 322522428u,\n3191396600u, 2076543340u, 1552496658u, 2739811558u,\n3867875546u,\n2051584261u,\n2126250818u, 901517871u, 3651631165u, 1323139145u,\n1521111765u, 477802997u, 3508559783u, 383954241u,\n3804516756u,\n4250206331u,\n2655954340u, 2484996477u, 1417544845u, 1520282298u,\n2745204366u, 2869345147u, 1872738335u, 2592877343u,\n1619744564u,\n1804962124u,\n3458679890u, 423948620u, 273645618u, 4187865426u,\n376057175u, 2943431463u, 3581950599u, 1035398331u,\n1088213445u,\n861988903u,\n1323370244u, 777069428u, 506235917u, 369720851u,\n2789995854u, 230915180u, 1505086948u, 940361236u,\n3727873235u,\n1159167499u,\n1860302871u, 3456858862u, 3923555152u, 2131072714u,\n2910461068u, 3671950363u, 2010742682u, 4088068851u,\n3616470388u,\n2087714788u,\n221675509u, 1230154072u, 3450704646u, 1463226695u,\n1998357699u, 266026801u, 619568740u, 3560427266u,\n4148162586u,\n3150417316u,\n1356375822u, 2056097622u, 627905802u, 3881675638u,\n2309738053u, 971916703u, 3447805361u, 1673575328u,\n673084328u,\n3317849401u,\n2836362782u, 2377208890u, 3275350588u, 158350552u,\n2553241779u, 2497264995u, 3262882649u, 3897937187u,\n1598963653u,\n3068514414u,\n601541505u, 374517071u, 3380795976u, 235752573u,\n284670003u, 2990192160u, 904937105u, 2306579150u,\n2117362589u,\n1635274830u,\n3355572906u, 170799903u, 1226685528u, 664567688u,\n413219134u, 878324258u, 4026159448u, 3620649295u,\n1823625377u,\n3175888439u,\n1759344347u, 2640637095u, 3549558u, 2192984935u,\n978623493u, 804017880u, 3877562323u, 3843116489u,\n1641748342u,\n1853539444u,\n3001178468u, 3443560727u, 2685426077u, 1653064722u,\n349231508u, 2726789654u, 3136215581u, 768402830u,\n269384321u,\n531936536u,\n2592883487u, 1343156334u, 3628619802u, 1477143570u,\n4269458419u, 3285611028u, 959104925u, 2712290710u,\n3480237248u,\n835796333u,\n2020636251u, 1191914589u, 126521603u, 4288023938u,\n3731699932u, 2136758855u, 985780142u, 193807575u,\n1850544433u,\n653947619u,\n3929316796u, 381871169u, 950486363u, 1787262279u,\n360480382u, 1800636585u, 1039258631u, 3682073259u,\n1262819303u,\n1786000319u,\n1570627191u, 893065837u, 301304916u, 1478469809u,\n623018819u, 2742232545u, 2058913014u, 1706060059u,\n2421125401u,\n1315829592u,\n3208766775u, 1805586156u, 575853086u, 3085025513u,\n4010908260u, 2344058256u, 3814407434u, 1458485673u,\n2474514786u,\n3581895658u,\n2710719679u, 190812706u, 2135454262u, 2620080728u,\n3400757986u, 1669914857u, 1559978393u, 1629811331u,\n3096616493u,\n1391424435u,\n4158376003u, 1015657076u, 794783832u, 479952178u,\n1150290207u, 2497437906u, 231815090u, 755078067u,\n3832053281u,\n63649475u,\n2415822606u, 4105027719u, 1706992318u, 1106598740u,\n3941945667u, 1271300761u, 505882259u, 760186809u,\n2657183368u,\n1925422058u,\n1039773764u, 880219458u, 4275949176u, 1556833823u,\n925882132u, 4216310340u, 757497522u, 461833914u,\n3884002070u,\n2790957660u,\n2100050089u, 651959176u, 1380301291u, 1289124125u,\n452314403u, 226156280u, 3306924715u, 1750807758u,\n2290180542u,\n1953760569u,\n2253069096u, 3960924806u, 1786291620u, 60736185u,\n2569018293u, 3870479674u, 2247005661u, 2239850953u,\n4261808536u,\n3282975782u,\n780945879u, 3349849383u, 1579362556u, 2265045884u,\n905088740u, 725212379u, 3156479246u, 2501620391u,\n3062836263u,\n4070422690u,\n996797869u, 4082582315u, 976105756u, 303983602u,\n1862104804u, 3864508254u, 3383979677u, 2835500286u,\n2798364010u,\n519359476u,\n3447342725u, 194373889u, 3313466630u, 232399983u,\n2841787856u, 1672751454u, 3345183154u, 1805381384u,\n2226129336u,\n2847829057u,\n2350774567u, 2838540121u, 2757948482u, 1017002062u,\n2329150951u, 2171488196u, 3668619047u, 3874977844u,\n3287966998u,\n262346753u,\n2493054715u, 2298644430u, 2926101182u, 1528457638u,\n598656233u, 2615845874u, 989110727u, 820441411u,\n253617372u,\n2201077208u,\n2047569338u, 3114356329u, 3335563734u, 2967673540u,\n768438341u, 1417708203u, 3873718246u, 1538441843u,\n1279167650u,\n3917966776u,\n2218481734u, 1015935150u, 1957845042u, 1318150213u,\n3146423971u, 4218994877u, 1162470863u, 1519718292u,\n2594658906u,\n665870414u,\n3430347817u, 3933868731u, 1597041394u, 3138684682u,\n3398212027u, 1064647658u, 1576321132u, 14792918u,\n224938029u,\n3706456050u,\n847274786u, 2645698692u, 1743374687u, 2343133224u,\n3066596790u, 2857270120u, 200596308u, 452055528u,\n2319312082u,\n3488655402u,\n4146865894u, 608206438u, 2699777051u, 3687240713u,\n327957508u, 3664730153u, 568134564u, 2993484554u,\n4159860363u,\n4274533921u,\n1079994063u, 2360220210u, 3609597760u, 3639708902u,\n2836180437u, 1069910270u, 1892427666u, 1874729790u,\n1267712826u,\n121886940u,\n3572289214u, 2475945610u, 783779452u, 588827737u,\n1531395014u, 2085084212u, 2219189792u, 3981444548u,\n2218885336u,\n1691622694u,\n2053232885u, 1386558530u, 2182946189u, 2365247285u,\n1871081313u, 2935751853u, 38413723u, 543465863u,\n900691890u,\n2899905665u,\n575120562u, 93133904u, 457154948u, 2983705792u,\n4232229200u, 2038565963u, 614693984u, 3405328302u,\n4083090010u,\n2088004171u,\n244031209u, 1861889294u, 2417109253u, 3299562328u,\n4158642443u, 4199064449u, 3161611046u, 885015950u,\n3677904099u,\n2969861785u,\n772348805u, 1712263832u, 3219357614u, 484271305u,\n3645706114u, 2059620251u, 409557488u, 2278896731u,\n224475749u,\n3523022952u,\n2057140088u, 449131785u, 1149879244u, 4255363996u,\n3602720135u, 1690010854u, 2503998822u, 2750828466u,\n3340671802u,\n1447583863u,\n2649684943u, 2764747249u, 3046070595u, 3441726138u,\n3840332559u, 3156747501u, 1288666680u, 1472744459u,\n3452391933u,\n1617542784u,\n217869690u, 3718469527u, 348639731u, 590532355u,\n43789787u, 22606314u, 1621559290u, 2231743261u,\n2234620879u,\n544748955u,\n3169387920u, 203343594u, 3272552527u, 1078282365u,\n809576321u, 854207584u, 3625491053u, 1193737267u,\n1628966807u,\n2661421060u,\n2433442061u, 3886639039u, 2149304418u, 303000565u,\n1432830882u, 137378235u, 1135974068u, 318705754u,\n2491227157u,\n2627534472u,\n3520352233u, 2488397682u, 3969194920u, 3843962181u,\n2135981459u, 2611933220u, 799460731u, 2300968851u,\n3412851628u,\n3070914013u,\n3555224260u, 4125937572u, 240359903u, 722496673u,\n2061023600u, 3843919221u, 2759960043u, 1191155322u,\n1504041490u,\n3735253656u,\n1773124736u, 101110011u, 1627699578u, 2645634551u,\n263603947u, 1388368439u, 677146538u, 1644201982u,\n2625699644u,\n2403862553u,\n2426069017u, 3613511705u, 915141802u, 2981654265u,\n3474818167u, 2611101773u, 627891434u, 762754924u,\n2143021902u,\n51067670u,\n4017746573u, 2269879853u, 3037857950u, 2388899692u,\n582729171u, 1886116725u, 2281219772u, 264704948u,\n3509984037u,\n4078683368u,\n2172959411u, 1807195632u, 3357092302u, 2253764928u,\n2320369390u, 3076335959u, 2623583210u, 168378015u,\n1435562650u,\n1100977467u,\n3160490319u, 2550328495u, 2396855930u, 1347823908u,\n1617990918u, 3849653099u, 3224111576u, 1681539821u,\n4171542880u,\n552200045u,\n3562947778u, 1676237880u, 3747732307u, 2453332913u,\n865530667u, 3566636849u, 3485502777u, 336779723u,\n2535942410u,\n1685000184u,\n820545711u, 1893670486u, 1273910461u, 1193758569u,\n970365241u, 381205962u, 3612810852u, 1160577445u,\n541488143u,\n4005031080u,\n2333965236u, 2419855455u, 3484533538u, 3073937876u,\n908466956u, 661391539u, 2342122412u, 1467049112u,\n1785800827u,\n135343033u,\n139643209u, 2438375667u, 974654058u, 3216478230u,\n3807620420u, 779043363u, 2812846449u, 333254784u,\n1025244024u,\n2242303095u,\n2476683742u, 350018683u, 174652916u, 933097576u,\n826905896u, 559603581u, 2777181260u, 164915169u,\n4070353203u,\n1459055748u,\n297303985u, 3103837241u, 3812514233u, 232265137u,\n2032819099u, 1523091376u, 3531238208u, 1403510182u,\n2886832080u,\n2599705941u,\n2789695716u, 68437968u, 3823813791u, 1040994569u,\n3024194990u, 2461740520u, 3735391266u, 2042207153u,\n2461678616u,\n3519231840u,\n1344224923u, 411442756u, 1179779351u, 7661528u,\n778352196u, 3288808867u, 589356197u, 2627504511u,\n3374744599u,\n3312172905u,\n357423007u, 3539567796u, 4044452215u, 1445118403u,\n2937983820u, 184089910u, 346201845u, 2427295202u,\n1345448010u,\n2884434843u,\n3085001879u, 2640105409u, 315310640u, 3530289798u,\n3362974764u, 963602652u, 75228477u, 3509381180u,\n4012777756u,\n2380345941u,\n1073137836u, 2083960378u, 1220315185u, 3628720934u,\n3508867818u, 67148343u, 3558085158u, 1753943368u,\n863309561u,\n2844713625u,\n441921850u, 854732254u, 816793316u, 2555428747u,\n3440623414u, 1707304366u, 3189874375u, 1623229221u,\n1220335976u,\n806745430u,\n3909262947u, 1680369031u, 2926179486u, 3410391660u,\n3991630434u, 2876458763u, 1179167079u, 536360759u,\n1592117159u,\n1514343977u,\n1032622306u, 2057494855u, 784938958u, 178402996u,\n1152907972u, 2326185495u, 2939973666u, 4181120253u,\n552831733u,\n664251856u,\n1297139539u, 1969357631u, 1474065957u, 3055419017u,\n3395829380u, 3316562752u, 2168409017u, 614624786u,\n3585854336u,\n668291094u,\n1162889217u, 3773171307u, 2263271126u, 355089668u,\n3195850578u, 3396793277u, 3519870267u, 527857605u,\n3972392320u,\n2224315010u,\n4047225561u, 3271434798u, 3192704713u, 2798505213u,\n3932215896u, 3792924012u, 3796843756u, 453872975u,\n4050552799u,\n1056432676u,\n928166947u, 121311642u, 930989547u, 2087070683u,\n1288978057u, 1556325239u, 1812435626u, 1682385724u,\n1214364933u,\n904760776u,\n3957045528u, 3949822847u, 2411065880u, 3716420732u,\n3424837835u, 3833550693u, 1799375326u, 2012368921u,\n2768764136u,\n1786111037u,\n4055479315u, 3751639533u, 2808224623u, 3492656387u,\n1306824780u, 2624000170u, 3134795218u, 1778409297u,\n3900821801u,\n593336325u,\n2772069220u, 2980873673u, 3574497158u, 3994780459u,\n4246519854u, 3482758570u, 4228015183u, 33101083u,\n1769887734u,\n4158035314u,\n3690638998u, 1119035482u, 4134969651u, 2483207353u,\n3932823321u, 285829887u, 3485140138u, 1304815138u,\n995608264u,\n3133997465u,\n1195477617u, 2147693728u, 3506673112u, 4234467492u,\n1183174337u, 1395340482u, 769199343u, 193262308u,\n2798920256u,\n3827889422u,\n3399695609u, 3036045724u, 2999477386u, 3567001759u,\n2682864314u, 1414023907u, 3699872975u, 3369870701u,\n2662284872u,\n2179640019u,\n2485080099u, 3234415609u, 3755915606u, 1339453220u,\n1567403399u, 2076272391u, 293946298u, 3861962750u,\n1291949822u,\n2916864995u,\n132642326u, 2215117062u, 2205863575u, 2488805750u,\n405632860u, 3248129390u, 2952606864u, 896734759u,\n2047417173u,\n3865951392u,\n657296855u, 1328547532u, 3966511825u, 3959682388u,\n4171801020u, 2981416957u, 1868896247u, 790081075u,\n3143666398u,\n2950766549u,\n2065854887u, 2737081890u, 995061774u, 1510712611u,\n2865954809u, 565044286u, 1565631102u, 1500654931u,\n494822108u,\n2803515503u,\n1058154996u, 3506280187u, 856885925u, 4204610546u,\n800905649u, 1130711562u, 558146282u, 2053400666u,\n449794061u,\n2643520245u,\n2101248725u, 3123292429u, 3583524041u, 983372394u,\n1587743780u, 672870813u, 444833475u, 100741452u,\n366232251u,\n1717951248u,\n524144122u, 1362432726u, 1304947719u, 674306020u,\n405665887u, 4081931036u, 1580408204u, 2343242778u,\n3901654006u,\n2627173567u,\n3015148205u, 814686701u, 1327920712u, 1346494176u,\n2468632605u, 2259795544u, 2519278184u, 2129281928u,\n2860266380u,\n4001619412u,\n1154910973u, 2841022216u, 1199925485u, 1372200293u,\n2713179055u, 3609776550u, 2896463880u, 1056406892u,\n177413841u,\n40180172u,\n3274788406u, 660921784u, 1686225028u, 4003382965u,\n2532691887u, 4256809101u, 1186018983u, 667359096u,\n2375266493u,\n2760222015u,\n745187078u, 312264012u, 396822261u, 2588536966u,\n2026998998u, 1766454365u, 3218807676u, 3915487497u,\n2630550356u,\n4130063378u,\n4231937074u, 752212123u, 3085144349u, 3267186363u,\n4103872100u, 4193207863u, 1306401710u, 3014853131u,\n1067760598u,\n2306188342u,\n2437881506u, 4258185052u, 2506507580u, 130876929u,\n1076894205u, 4106981702u, 2799540844u, 945747327u,\n1436722291u,\n2499772225u,\n2571537041u, 2038830635u, 2066826058u, 2892892912u,\n524875858u, 3392572161u, 2869992096u, 1308273341u,\n923668994u,\n1980407857u,\n2275009652u, 240598096u, 2658376530u, 3505603048u,\n1022603789u, 582423424u, 846379327u, 4092636095u,\n4177298326u,\n1004173023u,\n2154027018u, 2993634669u, 1098364089u, 3035642175u,\n1335688126u, 1376393415u, 1252369770u, 3815033328u,\n1999309358u,\n1234054757u,\n1388595255u, 2859334775u, 366532860u, 3453410395u,\n4226967708u, 1321729870u, 2078463405u, 156766592u,\n3157683394u,\n3549293384u,\n3348214547u, 2879648344u, 1144813399u, 2758966254u,\n647753581u, 813615926u, 2035441590u, 1961053117u,\n600168686u,\n2192833387u,\n3156481401u, 3627320321u, 383550248u, 81209584u,\n2339331745u, 1284116690u, 1980144976u, 2955724163u,\n789301728u,\n3842040415u,\n1115881490u, 965249078u, 4098663322u, 1870257033u,\n2923150701u, 4217108433u, 183816559u, 2104089285u,\n2640095343u,\n3173757052u,\n927847464u, 2383114981u, 4287174363u, 1886129652u,\n70635161u, 1182924521u, 1121440038u, 4246220730u,\n3890583049u,\n975913757u,\n2436253031u, 1074894869u, 1301280627u, 992471939u,\n735658128u, 244441856u, 1541612456u, 3457776165u,\n3503534059u,\n1931651133u,\n349142786u, 3669028584u, 1828812038u, 99128389u,\n1364272849u, 1963678455u, 3971963311u, 2316950886u,\n1308901796u,\n2789591580u,\n1460494965u, 2380227479u, 1577190651u, 1755822080u,\n2911014607u, 859387544u, 13023113u, 2319243370u,\n2522582211u,\n2299110490u,\n3342378874u, 2589323490u, 1884430765u, 3739058655u,\n2419330954u, 355389916u, 273950915u, 3670136553u,\n410946824u,\n3174041420u,\n2609010298u, 3059091350u, 2300275014u, 725729828u,\n2548380995u, 1738849964u, 1257081412u, 79430455u,\n810321297u,\n3246190593u,\n1007937684u, 912115394u, 40880059u, 3450073327u,\n4289832174u, 2253485111u, 1065639151u, 2953189309u,\n124779113u,\n654299738u,\n115760833u, 1250932069u, 884995826u, 3998908281u,\n1382882981u, 1134187162u, 3202324501u, 487502928u,\n3032756345u,\n4057517628u,\n933197381u, 2319223127u, 2044528655u, 2554572663u,\n4049450620u, 1620812836u, 2832905391u, 2273005481u,\n1913090121u,\n1055456023u,\n510593296u, 3285343192u, 2912822536u, 1645225063u,\n638418430u, 452701300u, 1025483165u, 1639370512u,\n167948643u,\n2809842730u,\n2983135664u, 407521332u, 1543756616u, 3949773145u,\n4283462892u, 659962275u, 3878013463u, 1000748756u,\n4053212051u,\n4099239406u,\n3467581965u, 354635541u, 21301844u, 3831212473u,\n3189450571u, 2264401966u, 4096484849u, 1736448515u,\n3976926096u,\n3727194724u,\n2243487039u, 585209095u, 3143046007u, 969558123u,\n3037113502u, 3594170243u, 2835860223u, 3775493975u,\n2787220812u,\n2274252217u,\n2915380701u, 3077533278u, 1252871826u, 1519790952u,\n205297661u, 2950557658u, 3956882191u, 2724439401u,\n3694608025u,\n124028038u,\n216019153u, 1533010676u, 2259986336u, 2014061617u,\n2068617849u, 3078123052u, 2692046098u, 1582812948u,\n396916232u,\n1470894001u,\n1694309312u, 300268215u, 1553892743u, 671176040u,\n1544988994u, 2793402821u, 4194972569u, 2296476154u,\n748354332u,\n3491325898u,\n4261053291u, 1104998242u, 797816835u, 243564059u,\n2197717393u, 299029458u, 1675252188u, 3139770041u,\n583018574u,\n2532106100u,\n2099391658u, 3760526730u, 3422719327u, 3556917689u,\n2374009285u, 2130865894u, 3710563151u, 1437538307u,\n3938030842u,\n2006930694u,\n2151243336u, 1939741287u, 1957068175u, 2135147479u,\n649553342u, 1713643042u, 4188696599u, 1698739939u,\n3549427584u,\n1016382174u,\n322644378u, 2476164549u, 2037263020u, 88036019u,\n2548960923u, 539867919u, 2871157727u, 4031659929u,\n754087252u,\n972656559u,\n4246379429u, 3877308578u, 2059459630u, 3614934323u,\n1410565271u, 2102980459u, 215395636u, 1083393481u,\n3775523015u,\n2062750105u,\n2475645882u, 3041186774u, 3534315423u, 758607219u,\n1686100614u, 180500983u, 1155581185u, 1476664671u,\n2918661695u,\n3812731350u,\n4003853737u, 4148884881u, 1468469436u, 3278880418u,\n1045838071u, 1049161262u, 360450415u, 3158065524u,\n814443735u,\n3391401707u,\n729968410u, 738771593u, 3662738792u, 1672830580u,\n4199496163u, 188487238u, 219098233u, 2141731267u,\n3890250614u,\n2988780375u,\n4026279523u, 3489429375u, 2468433807u, 1178270701u,\n2685094218u, 2716621497u, 3718335529u, 2273344755u,\n701110882u,\n1925717409u,\n1515176562u, 2325460593u, 3954798930u, 784566105u,\n3769422266u, 1641530321u, 2703876862u, 2907480267u,\n1828076455u,\n1805635221u,\n3883381245u, 1476756210u, 2072514392u, 3658557081u,\n2003610746u, 2556845550u, 729594004u, 3303898266u,\n1968227254u,\n423204951u,\n231828688u, 4223697811u, 698619045u, 3636824418u,\n2738779239u, 2333529003u, 2833158642u, 580285428u,\n3038148234u,\n1012378004u,\n1113647298u, 1424593483u, 4053247723u, 1167152941u,\n2677383578u, 3419485379u, 2135673840u, 440478166u,\n1682229112u,\n3226724137u,\n1217439806u, 3828726923u, 3636576271u, 3467643156u,\n2005614908u, 2655346461u, 2345488441u, 1027557096u,\n3594084220u,\n1372306343u,\n2342583762u, 4291342905u, 4094931814u, 3254771759u,\n821978248u, 2404930117u, 1143937655u, 3156949255u,\n3460606610u,\n449701786u,\n3474906110u, 1932585294u, 2283357584u, 1808481478u,\n3522851029u, 3040164731u, 1530172182u, 2950426149u,\n1402416557u,\n756419859u,\n4132576145u, 724994790u, 2852015871u, 2177908339u,\n899914731u, 139675671u, 1423281870u, 3198458070u,\n807581308u,\n2021611521u,\n1801452575u, 1425984297u, 2833835949u, 1536827865u,\n3902351840u, 164546042u, 1872840974u, 3986194780u,\n792156290u,\n3378681896u,\n941547959u, 3931328334u, 3661060482u, 2386420777u,\n3920146272u, 3458621279u, 3348500844u, 2269586542u,\n797371473u,\n3188953649u,\n80514771u, 2913333490u, 1246325623u, 3253846094u,\n1723906239u, 1606413555u, 587500718u, 1412413859u,\n2310046829u,\n2113313263u,\n3855635608u, 47271944u, 1112281934u, 3440228404u,\n2633519166u, 425094457u, 307659635u, 67338587u,\n2412987939u,\n2363930989u,\n2853008596u, 2844637339u, 922568813u, 130379293u,\n2825204405u, 2904442145u, 1176875333u, 1511685505u,\n599177514u,\n1872681372u,\n682394826u, 1888849790u, 3635304282u, 1761257265u,\n1571292431u, 355247075u, 1177210823u, 1691529530u,\n3629531121u,\n3760474006u,\n1129340625u, 868116266u, 3908237785u, 1942124366u,\n1266630014u, 3214841995u, 334023850u, 1110037019u,\n369650727u,\n1288666741u,\n70535706u, 20230114u, 4284225520u, 727856157u,\n293696779u, 1244943770u, 3976592462u, 560421917u,\n4171688499u,\n2438786950u,\n1218144639u, 3809125983u, 1302395746u, 534542359u,\n2121993015u, 2899519374u, 3192177626u, 1761707794u,\n3101683464u,\n1555403906u,\n3225675390u, 1875263768u, 4278894569u, 651707603u,\n2111591484u, 3802716028u, 2900262228u, 1181469202u,\n3254743797u,\n1822684466u,\n860641829u, 3046128268u, 1284833012u, 1125261608u,\n461384524u, 2331344566u, 1274400010u, 990498321u,\n3462536298u,\n3796842585u,\n2346607194u, 279495949u, 3951194590u, 3522664971u,\n3169688303u, 726831706u, 1123875117u, 1816166599u,\n3759808754u,\n2918558151u,\n3713203220u, 3369939267u, 466047109u, 384042536u,\n587271104u, 2191634696u, 2449929095u, 1157932232u,\n2084466674u,\n841370485u,\n3241372562u, 4277738486u, 2150836793u, 1173569449u,\n778768930u, 2594706485u, 3065269405u, 3019263663u,\n2660146610u,\n2789946230u,\n77056913u, 728174395u, 3647185904u, 804562358u,\n2697276483u, 881311175u, 1178696435u, 2059173891u,\n2308303791u,\n221481230u,\n50241451u, 3689414100u, 1969074761u, 2732071529u,\n1900890356u, 840789500u, 2100609300u, 985565597u,\n1220850414u,\n2456636259u,\n223607678u, 1016310244u, 1937434395u, 85717256u,\n275058190u, 3712011133u, 171916016u, 2389569096u,\n3679765802u,\n3575358777u,\n3481108261u, 3178286380u, 2489642395u, 2931039055u,\n3086601621u, 3079518902u, 3027718495u, 2506894644u,\n2976869602u,\n2134336365u,\n2420172217u, 918054427u, 661522682u, 1403791357u,\n3587174388u, 2623673551u, 1355661457u, 4159477684u,\n1109013587u,\n3112183488u,\n2217849279u, 3500291996u, 2419603731u, 2929886201u,\n3854470013u, 1358382103u, 1357666555u, 21053566u,\n2716621233u,\n3094836862u,\n3309729704u, 57086558u, 839187419u, 2757944838u,\n3651040558u, 3607536716u, 3691257732u, 2312878285u,\n1202511724u,\n183479927u,\n2509829803u, 109313218u, 478173887u, 2072044014u,\n190631406u, 2495604975u, 1010416260u, 3679857586u,\n726566957u,\n258500881u,\n1805873908u, 3081447051u, 2352101327u, 534922207u,\n1584552873u, 813470716u, 255914637u, 249169434u,\n3193498057u,\n1038802706u,\n2590158653u, 3147907290u, 663060128u, 1156177857u,\n634616100u, 312879189u, 1545020368u, 2054634247u,\n3271451914u,\n3438291534u,\n2181454946u, 3864535432u, 2398586877u, 896491075u,\n2810631478u, 2770357487u, 3372930052u, 898070638u,\n2051007323u,\n392959778u,\n36645539u, 3743556044u, 4134529680u, 4124451188u,\n566806297u, 2936523982u, 1304761965u, 537399498u,\n1940818842u,\n40862381u,\n36288410u, 3063605629u, 2826611650u, 3961972098u,\n1871578006u, 2392095486u, 1136931591u, 513864488u,\n173276451u,\n3039055682u,\n3543322032u, 1943592006u, 657217094u, 1751698246u,\n2969618445u, 456616022u, 900309519u, 113892716u,\n1126392103u,\n1235651045u,\n1882073852u, 2136610853u, 2353639710u, 2819956700u,\n3980083530u, 828773559u, 224069850u, 902434120u,\n2802008036u,\n94358995u,\n2777723394u, 2812641403u, 2525832595u, 4157388110u,\n4235563782u, 937800324u, 141690749u, 568062536u,\n550123849u,\n1330316521u,\n1949488696u, 2296431366u, 1958465262u, 3564751729u,\n3748252207u, 120455129u, 1607318832u, 2525729790u,\n2640987481u,\n2332096657u,\n1775969159u, 1555085077u, 2913525137u, 1347085183u,\n2376253113u, 3194050574u, 1806090610u, 678641356u,\n1499146713u,\n383849715u,\n3299835823u, 2284860330u, 2614269636u, 3913628844u,\n2761334210u, 1959484587u, 529797021u, 239966995u,\n3102194829u,\n3602307804u,\n1122192627u, 3577510006u, 164486066u, 1680137310u,\n1473396395u, 1467801424u, 903493660u, 1185943071u,\n2798556505u,\n2306744492u,\n3167201310u, 3577947177u, 3067592134u, 2905506289u,\n1210366329u, 204484056u, 2347778932u, 3862374472u,\n3277439508u,\n4187414621u,\n1646699310u, 621385800u, 3934869089u, 3975491588u,\n3580085916u, 1925674500u, 2436305348u, 3983301539u,\n2739439523u,\n3291507446u,\n3395637920u, 3753389171u, 2955202032u, 2654255623u,\n3771089254u, 2140443405u, 2779834738u, 3261942805u,\n3526889244u,\n1842009139u,\n4048484340u, 2106218403u, 2161244271u, 772152700u,\n1158647659u, 3776791619u, 3882186721u, 699525237u,\n2954670460u,\n1007105869u,\n3359152025u, 1146388699u, 1401550303u, 2326582541u,\n4181783540u, 1085644043u, 1942143795u, 1038368308u,\n1526153809u,\n4042547244u,\n1891441000u, 2573991874u, 1281441253u, 3635098284u,\n1980545715u, 825985487u, 3934748116u, 4228386979u,\n1480870944u,\n1042194545u,\n2397771642u, 2248490001u, 3817869868u, 878654626u,\n3785629484u, 1672470870u, 3229367873u, 1894538933u,\n1010692731u,\n1733824268u,\n656620328u, 3048283803u, 3353340056u, 2324965120u,\n4192585951u, 2284524675u, 3483884368u, 1510168293u,\n1554942691u,\n1309709396u,\n1241133168u, 3162179280u, 4046378054u, 3171681593u,\n1165297136u, 3496703563u, 150437903u, 1948622072u,\n1076332463u,\n2292479143u,\n1464229958u, 3479738093u, 2328067598u, 2334503110u,\n833324834u, 3981605747u, 3002629155u, 2854644186u,\n2832201336u,\n95796957u,\n3269249397u, 2358313329u, 3411860910u, 4283292480u,\n2802208697u, 1305947955u, 2156803420u, 1991340283u,\n189678024u,\n447602599u,\n1055411517u, 1531748363u, 1555852656u, 412402681u,\n3774988152u, 20597551u, 2925024131u, 1423989620u,\n3749428061u,\n1541439448u,\n112270416u, 1936224776u, 132162941u, 3772011507u,\n3814102518u, 1908807815u, 444154079u, 823765347u,\n3362275567u,\n3419047430u,\n2108287005u, 2315102125u, 658593738u, 3195094029u,\n3721937534u, 3176229204u, 3398835373u, 1271898712u,\n1142546577u,\n3185986817u,\n3562705803u, 2046119567u, 912990621u, 1829977672u,\n3459576979u, 1118045834u, 1369529376u, 3320601076u,\n3954988953u,\n4002467635u,\n3359456351u, 1314849568u, 1766750942u, 2998874853u,\n1181800239u, 707328036u, 3314954697u, 2066721120u,\n598194215u,\n1124451278u,\n3156679616u, 3742684743u, 2960199690u, 2683497915u,\n2566077529u, 937014607u, 102095219u, 4262922475u,\n3132264275u,\n1262099830u,\n862722905u, 2717653494u, 3245583534u, 3427209989u,\n3220278124u, 85457091u, 2222333500u, 3513997967u,\n3522324951u,\n2830855552u,\n2215004781u, 3482411840u, 4227160614u, 2030964411u,\n1741393851u, 2643723748u, 942813508u, 403442675u,\n3112048748u,\n530556423u,\n3817755244u, 3543286628u, 2247276090u, 1532920842u,\n4101962711u, 1446540991u, 3297821473u, 1861255389u,\n1984398u,\n2366525138u,\n377589481u, 3549193828u, 1427765914u, 506831657u,\n277278988u, 1447652775u, 3214362239u, 3142198690u,\n2843087541u,\n468915015u,\n807895062u, 2198723907u, 4031145069u, 2417156212u,\n4027298697u, 637175947u, 1229254212u, 1773257887u,\n1659444818u,\n451148891u,\n2099741368u, 735351990u, 2534775713u, 3261804619u,\n712519954u, 3527962772u, 3758642738u, 4245823575u,\n1281314264u,\n1167866160u,\n1489546151u, 1197354389u, 1043278102u, 2563326586u,\n371937794u, 2320164817u, 3189512691u, 573685198u,\n4108603513u,\n3758899588u,\n3507030163u, 2947201212u, 2529492585u, 578234375u,\n3362349842u, 3318878925u, 3611203517u, 3059253190u,\n4270755916u,\n4291274625u,\n4237586791u, 4137422245u, 2927218651u, 2444687041u,\n797128811u, 2043057612u, 396533859u, 2665256178u,\n3346510674u,\n1779586176u,\n3076562062u, 1882746214u, 921095362u, 2026988397u,\n514514911u, 3886379478u, 4218272420u, 1480386793u,\n3900160816u,\n2292273451u,\n1276138356u, 1125461821u, 1912885715u, 3365266013u,\n1333211627u, 4085009861u, 1390530102u, 3347984752u,\n2721771301u,\n1419492325u,\n4066766256u, 3250852311u, 820111852u, 1382201318u,\n2366036798u, 938032241u, 3100979439u, 487048687u,\n2292851045u,\n3241399180u,\n3912670510u, 2416437067u, 2973194517u, 3507707986u,\n1935099406u, 2533441488u, 104616731u, 2892622820u,\n3801190339u,\n4239188808u,\n807238241u, 3300121546u, 2249406147u, 4032114017u,\n3713738189u, 3324425575u, 4275607376u, 3663120298u,\n4173658372u,\n3984289690u,\n1827636846u, 3264588778u, 3297165529u, 558623533u,\n2728945672u, 1566297318u, 3447249966u, 481719551u,\n1596842050u,\n1838185946u,\n265271620u, 1050246315u, 4046655705u, 1844193138u,\n3807563245u, 1075384804u, 1292554949u, 1506525927u,\n2921816148u,\n2051885269u,\n1930534041u, 3872721086u, 1564489377u, 2272482181u,\n2849358683u, 589618304u, 2262072443u, 290363051u,\n299168363u,\n3867603931u,\n2868688756u, 2545263115u, 1092098533u, 3885725603u,\n2352430409u, 1981595469u, 2047946646u, 1332642839u,\n793806516u,\n214858837u,\n1061484659u, 3192394476u, 1115054785u, 3690637234u,\n996792368u, 2023479706u, 3046498231u, 4205835102u,\n3870714754u,\n257472875u,\n3549864599u, 2040276129u, 2414778670u, 812235477u,\n2674248196u, 1864096101u, 2257492689u, 1332556794u,\n1079540713u,\n465530720u,\n2304763972u, 830724724u, 3354588920u, 2510713652u,\n3103749409u, 468835585u, 1707620787u, 3038024846u,\n1000303198u,\n3462270146u,\n2748698899u, 2100348093u, 511537258u, 1237187486u,\n102049383u, 2268226698u, 3162251739u, 4219404629u,\n838822407u,\n1481440623u,\n2989224077u, 2676681975u, 3246551821u, 3812079906u,\n370572963u, 2283154352u, 3084789986u, 1961085583u,\n1955640586u,\n2409348147u,\n2284780581u, 1634818716u, 4018221729u, 2320761377u,\n3566831899u, 1799560520u, 91431959u, 1754113747u,\n1459430477u,\n3613658517u,\n924489906u, 3406317699u, 866289774u, 3924821603u,\n1265394945u, 1870668109u, 151949856u, 2747006534u,\n3111906201u,\n64039467u,\n2314447545u, 2600195638u, 4095795204u, 4162096026u,\n1026756826u, 2460047982u, 52686887u, 823198739u,\n1518045160u,\n2867527376u,\n566410761u, 2200433819u, 2114146405u, 2893790965u,\n881504901u, 974783212u, 490815659u, 937300283u,\n1523735309u,\n2511976468u,\n2634644947u, 355119367u, 1373773092u, 309232995u,\n3088671051u, 787126032u, 3442836843u, 4289194567u,\n2177850062u,\n1174136430u,\n3248982914u, 3129039732u, 1166851580u, 2196451882u,\n469595580u, 2130837700u, 3783349021u, 3745262548u,\n1236930515u,\n3032131496u,\n1525591437u, 1823628217u, 1939019255u, 1950270463u,\n3659899927u, 3688643445u, 3004399289u, 1155199552u,\n357547234u,\n2213110526u,\n3122658210u, 2667800490u, 2718690333u, 3512372076u,\n1098611683u, 2657518392u, 4248458835u, 3109874532u,\n1592908438u,\n2864927516u,\n3635248840u, 1251777186u, 3797340158u, 3508496870u,\n303354834u, 1482394062u, 2087100120u, 1595931912u,\n608574156u,\n723367884u,\n907938402u, 3357047807u, 1619629851u, 3092082995u,\n89030300u, 916336992u, 1861180168u, 3436334155u,\n1375000544u,\n3472936241u,\n1321217853u, 791356402u, 2872410224u, 2326250297u,\n2657644088u, 1748314108u, 4146771421u, 2913114440u,\n2924821844u,\n2101101496u,\n3268017251u, 2109603066u, 690665520u, 1830067573u,\n951427661u, 2982533150u, 3884512506u, 2358657479u,\n2833210784u,\n3419798214u,\n3785893994u, 2103940206u, 86759766u, 4031230616u,\n3745237192u, 2739453927u, 497038072u, 3303159408u,\n1251537249u,\n1993408196u,\n3185905715u, 2885948408u, 3154277110u, 2444150313u,\n2505582079u, 2120610195u, 3266465773u, 1814611964u,\n3080050407u,\n1079915522u,\n1819346505u, 2529946763u, 892097374u, 3740257161u,\n3618100441u, 1079900094u, 3607172225u, 737863389u,\n360704560u,\n3341993089u,\n1139047381u, 3132219631u, 1248981859u, 1109338159u,\n2004908615u, 4022302594u, 4166640860u, 2959140950u,\n3949235962u,\n2832278473u,\n2200524012u, 2634933043u, 2495844522u, 2613799818u,\n4034096813u, 683271795u, 1673546817u, 1363163726u,\n1805395136u,\n511749501u,\n1231032599u, 2305979751u, 345737783u, 3339868854u,\n2931857933u, 2323251738u, 1332068477u, 51846558u,\n3927238177u,\n1387182179u,\n1701238601u, 1419275173u, 2580882268u, 3357874599u,\n1726558907u, 1292901039u, 1371322339u, 1311713044u,\n3526735232u,\n4017884184u,\n3366093428u, 77140994u, 2128996229u, 1357915765u,\n4019691901u, 483989024u, 2390311750u, 2766065288u,\n3938587520u,\n3064810344u,\n1054589198u, 1274997019u, 4040589616u, 1277751144u,\n2274907047u, 4170399945u, 2886368209u, 4168922115u,\n3901237033u,\n3252972311u,\n2205185840u, 3403097556u, 3385493699u, 2809751370u,\n555319628u, 399539034u, 2998971454u, 1521596214u,\n178870216u,\n1471733541u,\n519629198u, 514159209u, 1500582242u, 1928616587u,\n2686427928u, 4133138798u, 1225914083u, 1432713584u,\n3559310915u,\n3925489366u,\n1055613123u, 4126676029u, 2723867653u, 3290604111u,\n1377022957u, 2373608155u, 3615237379u, 594338683u,\n2645257602u,\n2408427260u,\n917033274u, 750455097u, 625657657u, 121713200u,\n2191273413u, 4043949724u, 3293146785u, 3809297972u,\n3947296919u,\n115456894u,\n1529576616u, 1459278275u, 2157117997u, 1747859293u,\n4106665903u, 996939232u, 2007976332u, 4274649009u,\n1017725787u,\n4244666096u,\n1219631331u, 3072426253u, 3547691720u, 1620822012u,\n1397717508u, 2031597325u, 3345983430u, 2459068000u,\n3645130467u,\n2308642742u,\n359955852u, 1348467968u, 1133123059u, 2435919062u,\n2800365907u, 4213217210u, 4056565603u, 2811666556u,\n2318007236u,\n3823652401u,\n3654086429u, 1273260424u, 1591610446u, 943349350u,\n3441227678u, 3779964757u, 233818224u, 3469971032u,\n3764095096u,\n4009204587u,\n678472092u, 1990559652u, 2583121088u, 2978143652u,\n2496370864u, 2139539656u, 4287972050u, 295832576u,\n3536742861u,\n2257466133u,\n2738052161u, 1988611898u, 2466189642u, 3294419573u,\n2311186273u, 474374532u, 3081964174u, 2515138278u,\n835731677u,\n1178182694u,\n3352119543u, 2884763225u, 3462399574u, 2900817210u,\n1993698511u, 2868445043u, 2746444849u, 1205258179u,\n2353442946u,\n4079040070u,\n3624133102u, 2907136076u, 2902521697u, 426813211u,\n1418185512u, 3711189488u, 1351506552u, 1934749519u,\n46595543u,\n401688809u,\n3514602124u, 1396852607u, 1951477943u, 2502249173u,\n3199695820u, 2890250638u, 4205072507u, 1715623846u,\n3266686789u,\n3218688128u,\n1697759742u, 851227671u, 2358709645u, 4174233268u,\n500583683u, 3805940955u, 736234120u, 2710563712u,\n1949664540u,\n3139414003u,\n4293073253u, 1284406972u, 1785182449u, 1051548274u,\n2994248357u, 2499882522u, 717208669u, 2039517285u,\n518424929u,\n143136433u,\n2303774671u, 1272930860u, 2286410920u, 788459311u,\n273225293u, 2439291703u, 2254505236u, 3446287701u,\n3655156558u,\n1546628787u,\n340081500u, 3285722006u, 1324810435u, 1053980860u,\n1779472859u, 2700355724u, 686005017u, 3762376315u,\n3963193100u,\n1370881135u,\n661300087u, 1152753704u, 2349891598u, 3910051187u,\n2109444785u, 1311123870u, 2639837565u, 1896770931u,\n1081414128u,\n869877586u,\n4284220400u, 63045374u, 235968615u, 184451062u,\n1271099822u, 1319179857u, 3274963209u, 4172272710u,\n3388797445u,\n2965973320u,\n3793110097u, 3327241723u, 2991804005u, 1199544355u,\n771553759u, 2031749842u, 2596517372u, 1199888213u,\n858347951u,\n3340178832u,\n2903875412u, 763490382u, 76949161u, 2056544406u,\n1145227689u, 998233136u, 2354530024u, 427713587u,\n3537837347u,\n604661755u,\n923986833u, 1023730418u, 798294227u, 432557449u,\n801802449u, 1861313429u, 3899128441u, 4068407979u,\n2352677083u,\n3783539925u,\n10731973u, 3390767975u, 3949540249u, 1920121661u,\n3248580201u, 641956426u, 2104847395u, 604835744u,\n1491663404u,\n4255204651u,\n1520970746u, 2845653368u, 3247412938u, 3730629005u,\n855569514u, 3073294700u, 2429691698u, 3818342476u,\n3938869985u,\n2731201328u,\n2335202643u, 778117742u, 13298408u, 228780590u,\n2871715314u, 3253688653u, 4150999702u, 3846220408u,\n930808u,\n1397128726u,\n1964216488u, 2781092828u, 116285375u, 2271239476u,\n3724347554u, 2931203895u, 3893169206u, 1883912528u,\n2093892660u,\n3658787024u,\n3095016046u, 1094059199u, 3640239610u, 558564267u,\n2102812456u, 464734873u, 925262247u, 1609838036u,\n588364741u,\n1731409233u,\n1576165139u, 3933979268u, 375316394u, 4247099643u,\n3670508019u, 4080496835u, 2371248533u, 183762693u,\n2078935389u,\n2699810414u,\n1491815683u, 2999180789u, 1831158425u, 1603373553u,\n2006136905u, 3210230591u, 416748595u, 1536971415u,\n3271869367u,\n1266062739u,\n2138414557u, 3337114778u, 1634586826u, 36472629u,\n4482244u, 568009609u, 2721216780u, 4037289545u,\n2235138807u,\n1789351460u,\n4067539527u, 1323062829u, 3864620647u, 4192026301u,\n4278901241u, 1399025382u, 2826652805u, 1363860382u,\n1801770651u,\n1613381526u,\n1165249276u, 4046576622u, 2535596946u, 3260388176u,\n1078898578u, 2259750862u, 643387587u, 237144235u,\n4199571427u,\n3440917581u,\n3067939258u, 2018625455u, 1460528353u, 3138629939u,\n1666223528u, 3841139376u, 2528281125u, 885565193u,\n2609492686u,\n2517257479u,\n560864620u, 2261471820u, 3491559165u, 1329620416u,\n622383582u, 1759597655u, 2877873893u, 584692817u,\n1901728399u,\n2599000260u,\n3169771644u, 296332336u, 774719455u, 4175920823u,\n2287316070u, 4115615023u, 1073335619u, 4240292725u,\n1359158837u,\n1960974237u,\n3173724597u, 1619084286u, 2876340752u, 4065675347u,\n480741335u, 1237329941u, 701055566u, 3729009837u,\n1314736422u,\n4003180069u,\n3118519317u, 3035354420u, 3380357671u, 4020909015u,\n253958714u, 3545798863u, 3008185002u, 2624719888u,\n3219955575u,\n3060719376u,\n573101682u, 1580316843u, 2610493412u, 3490983536u,\n3601975611u, 851470366u, 635384901u, 3427048824u,\n1470002757u,\n3592460087u,\n2265226856u, 4124282457u, 2106385486u, 3334305617u,\n4208282753u, 3798749815u, 225396466u, 118791182u,\n2523395972u,\n194595464u,\n2563824631u, 2521301383u, 4224409406u, 468670274u,\n1761966400u, 1300908277u, 2570709228u, 1847901526u,\n1470099163u,\n2690466752u,\n1472536718u, 2399279735u, 4150607803u, 1775080054u,\n2082537685u, 4080034578u, 1256001880u, 392967725u,\n2055838940u,\n3349115816u,\n1745947263u, 2213925887u, 1836572741u, 2417722792u,\n636223705u, 2423329294u, 3960951311u, 1543591052u,\n1547914361u,\n2760945653u,\n3519014111u, 313543871u, 4119598884u, 1071003714u,\n2192556597u, 1526995535u, 3929839778u, 536388591u,\n3040873792u,\n3752682932u,\n1640614237u, 2432794021u, 385337403u, 2794410617u,\n2386128075u, 1055206708u, 1422747714u, 3759330929u,\n2533597496u,\n30440955u,\n1482899460u, 3350385050u, 616259409u, 3980103795u,\n1211364140u, 1040071544u, 594746920u, 1645973936u,\n2547331531u,\n1097726368u,\n700666526u, 2976247482u, 1144906608u, 996506677u,\n1997130756u, 800321417u, 1392942823u, 1601662248u,\n2079778663u,\n529512908u,\n2925120134u, 4106433085u, 630221833u, 2423086156u,\n1119859778u, 1726827981u, 1870859181u, 2559832707u,\n1792284257u,\n2059356387u,\n3572353364u, 3229407475u, 575621095u, 3221893291u,\n2372428048u, 2020123035u, 961449593u, 2243824063u,\n3803906611u,\n3735348189u,\n2981620804u, 4180681078u, 1555330629u, 230736535u,\n2075526640u, 749652975u, 713664372u, 2152096659u,\n2142067223u,\n3322302242u,\n1421646830u, 2092832615u, 1213735101u, 3192136753u,\n1106723940u, 3455398230u, 2541685524u, 2529956739u,\n3789430647u,\n1950084508u,\n2157395621u, 850457360u, 2758902426u, 2848030169u,\n6506379u, 1162213157u, 2981459221u, 272690871u,\n3059420255u,\n4242691285u,\n588065598u, 1206949936u, 3968214184u, 566348532u,\n126142880u, 1480567086u, 2959621988u, 2050218418u,\n2242731195u,\n3833514449u,\n1898070331u, 3687399477u, 3891859374u, 868185955u,\n2335308774u, 3676335246u, 3871121805u, 2189032743u,\n3275728647u,\n860492892u,\n1590764344u, 4130384758u, 262871548u, 3004764525u,\n2685542071u, 991231482u, 435122019u, 3031116998u,\n2898921700u,\n2917932604u,\n4238665148u, 2459072654u, 3444612545u, 4207731740u,\n1808564313u, 2798532269u, 3944553556u, 3926395409u,\n1633200670u,\n4138335224u,\n2524878605u, 4184292650u, 3563398268u, 4288943552u,\n3802121210u, 957502058u, 2410820887u, 4227117506u,\n4018625153u,\n4284329158u,\n530216712u, 2978986531u, 863452221u, 1910162118u,\n4088211378u, 4091971261u, 3150811451u, 4200871487u,\n3794038652u,\n3041564310u,\n2045287082u, 887805614u, 2889167251u, 4120352181u,\n1699912580u, 3478922097u, 3211994687u, 3136177842u,\n1500806861u,\n3211881347u,\n2147976385u, 3342722260u, 3359650541u, 4197378460u,\n781354073u, 1533623029u, 2204677828u, 3228172832u,\n3248592437u,\n3355841359u,\n560815159u, 1144951236u, 4027015711u, 2882625391u,\n339363613u, 2354572719u, 1769831876u, 4238589331u,\n1519732871u,\n2185834614u,\n1601096831u, 129709881u, 39655633u, 367604993u,\n1737681770u, 3259114599u, 2767070452u, 872365177u,\n1574125529u,\n3405020189u,\n4181346685u, 1134030380u, 403769171u, 2193351164u,\n1426232618u, 2885309450u, 3033612627u, 924948363u,\n935514094u,\n3202053329u,\n912294839u, 1618472324u, 4159158431u, 3744999487u,\n777064358u, 3974213124u, 1990246048u, 309725290u,\n2449849392u,\n1943692420u,\n2288635750u, 2433793635u, 2168904061u, 683315308u,\n3081493019u, 3477759434u, 3815496269u, 2823504699u,\n586945121u,\n3088963200u,\n3492287335u, 636875049u, 1111206944u, 2037346120u,\n1282050044u, 1409681512u, 1786128584u, 755810950u,\n2332676758u,\n2178142310u,\n957827166u, 1014983590u, 1888800725u, 3608595803u,\n3200072714u, 2534008478u, 659336139u, 1281728287u,\n4060560529u,\n2915575125u,\n3521503774u, 2926487340u, 1096297674u, 653489861u,\n2352326980u, 2561136777u, 1224141198u, 1250479629u,\n1297625391u,\n2409997371u,\n1942483722u, 2481835750u, 1394715707u, 1673070941u,\n2456039704u, 3980558014u, 3547934764u, 1882038812u,\n1078160498u,\n2488279087u,\n1848235245u, 1211914722u, 2264928765u, 2807773070u,\n270145554u, 583747883u, 3826009010u, 2996618216u,\n425727157u,\n992726957u,\n3384462280u, 726650661u, 1955043265u, 1923879512u,\n1854693773u, 2987614542u, 2660044993u, 2457260810u,\n426299370u,\n2671892900u,\n1827308087u, 3083953443u, 1791749638u, 3265087416u,\n2119752201u, 2547122538u, 3990783236u, 1912713468u,\n3688865211u,\n1815780016u,\n303699291u, 2416763742u, 2690891610u, 1535193548u,\n1107803989u, 1504143133u, 2235270371u, 2545884083u,\n2276278682u,\n411724404u,\n3416925704u, 2565792091u, 3383911757u, 546058824u,\n3374654444u, 2364630415u, 2693473470u, 2622125691u,\n261864817u,\n55682470u,\n857617568u, 141304067u, 1885488541u, 155368182u,\n1281949051u, 3384522408u, 3254816901u, 1959816782u,\n1452224057u,\n2830267691u,\n3709231247u, 58988202u, 4218130458u, 2984061349u,\n1888707848u, 4223605071u, 4241442486u, 375269213u,\n3208327038u,\n2199916493u,\n550337252u, 2855061437u, 276088636u, 114362204u,\n2321163647u, 2127813633u, 3289403024u, 2686973202u,\n2717376797u,\n3593428039u,\n3648831666u, 890925902u, 3289404818u, 3289516821u,\n4248913260u, 1858916580u, 3303932308u, 1752797086u,\n1628149686u,\n3245893605u,\n1568537311u, 2844194502u, 1593855770u, 2408174109u,\n124797514u, 2085649512u, 3188565660u, 2264996276u,\n1926696513u,\n3053957740u,\n2238806881u, 2189050973u, 203685243u, 379855590u,\n3920271562u, 1058600179u, 3698061923u, 4255106849u,\n608401664u,\n1598041932u,\n3318266418u, 2535016555u, 852760884u, 1918098822u,\n2200437599u, 1532285043u, 3425662132u, 3561293706u,\n2231633206u,\n4108785088u,\n3359152801u, 173534780u, 208383607u, 2862988169u,\n2406642243u, 426814583u, 2777335795u, 3322703596u,\n954190623u,\n615093090u,\n4179102978u, 2452847930u, 100239619u, 42471741u,\n818352432u, 2190624654u, 504379960u, 3631619975u,\n633412456u,\n1018421783u,\n842645419u, 711808707u, 3424580813u, 2132457941u,\n1158335882u, 3567952480u, 2302183699u, 1145788151u,\n3474264138u,\n3105085243u,\n3115506027u, 2783713015u, 3871785309u, 539583269u,\n1400252405u, 3857849984u, 4231186588u, 1278653799u,\n1760227022u,\n761044088u,\n3838185417u, 2439542532u, 585283357u, 2055995220u,\n937117124u, 3831944855u, 1823586038u, 3287917855u,\n485082427u,\n3209172809u,\n1984570176u, 2818337297u, 2691869057u, 3790476953u,\n839035557u, 3203129010u, 669981176u, 4121157385u,\n3519870450u,\n3792633352u,\n3017650322u, 1603459507u, 4225677666u, 376555451u,\n473780127u, 2018786277u, 3299822439u, 1010254499u,\n2383887565u,\n3155009499u,\n3108110655u, 2641738274u, 3684908622u, 1606463047u,\n3311068174u, 52708046u, 754181455u, 1018079176u,\n3915670272u,\n3366999425u,\n1012880204u, 1339439715u, 466437962u, 1402662350u,\n2504046911u, 736323938u, 2037800124u, 1725908589u,\n716341840u,\n1750123474u,\n3366342464u, 1743666195u, 2975303189u, 3821364027u,\n3253707772u, 3635548377u, 3840413796u, 1955642085u,\n1018315169u,\n1258092848u,\n2095540656u, 1076256607u, 117289557u, 1311658655u,\n2118301000u, 68721550u, 2886814107u, 2712432819u,\n4201862886u,\n753807148u,\n1940229047u, 731347296u, 1068901393u, 3873155894u,\n2852787666u, 1973464853u, 79735652u, 3966380587u,\n3245740712u,\n2525773438u,\n734938109u, 3045656416u, 3335746354u, 4099732691u,\n1911896517u, 1697006473u, 1145487066u, 1605663299u,\n3053606724u,\n2386289465u,\n3821211369u, 1006215345u, 1256304829u, 1053001668u,\n1289194958u, 118761054u, 1853688730u, 2803418011u,\n188650809u,\n3763686458u,\n1006829556u, 2961984133u, 3390525025u, 2061199893u,\n141792681u, 2439893463u, 2652982650u, 1804942682u,\n1546510005u,\n1246961405u,\n2407577046u, 565772575u, 3751844810u, 2943166103u,\n3750052451u, 3022527280u, 25162928u, 397381043u,\n1818337632u,\n3447363730u,\n3936437150u, 2569420703u, 2215592390u, 2171555672u,\n3665571006u, 4021712412u, 2939158353u, 4057813172u,\n1823237318u,\n103999245u,\n3251978010u, 3591914940u, 3582495283u, 2519035265u,\n3905726135u, 3180393349u, 2743117123u, 55247368u,\n3325286701u,\n705195946u,\n1857526853u, 1480518550u, 3809990433u, 1398189338u,\n3126362926u, 3959531492u, 1503658285u, 1977847740u,\n3043964489u,\n2613086143u,\n1518119282u, 4238434900u, 3905746486u, 3064949667u,\n1028122931u, 3309119457u, 4071194920u, 3096098907u,\n4137180520u,\n494467959u,\n1231408687u, 1691606157u, 1793452569u, 2722196118u,\n3478603952u, 1059665738u, 2282032278u, 3990268388u,\n1719514651u,\n4248311578u,\n3799146721u, 898026304u, 3367808954u, 4162472815u,\n170495870u, 1308116609u, 3428285344u, 1714716475u,\n395576794u,\n4153638621u,\n2999745812u, 3483315953u, 304980828u, 595337120u,\n3486516729u, 2331563143u, 2583609459u, 1885928417u,\n3834283777u,\n979337825u,\n932057378u, 3124081189u, 1930356777u, 3865887996u,\n4178282217u, 4214219408u, 3669465884u, 1472413856u,\n3356866587u,\n1012769806u,\n3043639963u, 996996396u, 207308216u, 982967331u,\n2991319933u, 318066902u, 721489670u, 1249967713u,\n749240921u,\n591392325u,\n2379365192u, 2250868849u, 2163259329u, 143191325u,\n3778285606u, 982149096u, 3536906200u, 2244353244u,\n1443862317u,\n3161549210u,\n2183127464u, 2015409516u, 547003700u, 2032484282u,\n523677821u, 4275663308u, 3827205526u, 3903778273u,\n2444530525u,\n2543645801u,\n1173958423u, 784740616u, 2878693675u, 3127696736u,\n3832037316u, 3161002398u, 4084166400u, 4213346853u,\n223390424u,\n4273380883u,\n2130315482u, 3429606032u, 3367732613u, 1912357694u,\n422632590u, 1266957023u, 3437535648u, 736404240u,\n2281709372u,\n415859912u,\n212948797u, 351612650u, 3920561440u, 112963586u,\n2230727543u, 2851076612u, 1990662634u, 2264296857u,\n3131463650u,\n2704034623u,\n3541637839u, 2954232792u, 533986918u, 4158757533u,\n65174248u, 4232639593u, 865906667u, 1948225652u,\n779656112u,\n3873989249u,\n2372984749u, 2346988193u, 1104345713u, 1165654138u,\n4045762610u, 3588205178u, 461363991u, 1111215752u,\n1389675192u,\n2404325151u,\n2152228101u, 3808973622u, 1901235912u, 3458690696u,\n314513238u, 2539459143u, 2847998873u, 952026138u,\n2325705328u,\n407844712u,\n3727960715u, 2996448351u, 2374336760u, 3138756390u,\n2600015243u, 539980418u, 1876285352u, 1670330799u,\n1709360377u,\n2868531654u,\n494777964u, 2773053597u, 599486162u, 3962209577u,\n1871328846u, 2171933018u, 110279472u, 384074780u,\n4147021936u,\n2333589647u,\n4251778066u, 40493468u, 3099342316u, 4108779767u,\n2812424588u, 954542332u, 2040682331u, 2251152306u,\n45915516u,\n259525626u,\n1045384743u, 4134656562u, 749389261u, 874399445u,\n616549904u, 2200447504u, 436024539u, 78972290u,\n3210485762u,\n1907985531u,\n3013721395u, 4214533685u, 4198804243u, 534879265u,\n1517190881u, 3756787754u, 1152563554u, 1718750948u,\n777737463u,\n1402478860u,\n1824562784u, 1879401449u, 3515818786u, 513165201u,\n1423491227u, 2103067918u, 2291777410u, 1097943000u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; IsAlive(farmhashcc::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashcc::Hash32(data, len++)); { uint128_t u = farmhashcc::Fingerprint128(data, len++); uint64_t h = Uint128Low64(u); IsAlive(h >> 32); IsAlive((h << 32) >> 32); h = Uint128High64(u); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; }\nCheck(farmhashcc::Hash32WithSeed(data + offset, len, SEED));\nCheck(farmhashcc::Hash32(data + offset, len));\n{ uint128_t u = farmhashcc::Fingerprint128(data + offset, len); uint64_t h = Uint128Low64(u); Check(h >> 32); Check((h << 32) >> 32); h = Uint128High64(u); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint128_t u = farmhashcc::CityHash128WithSeed(data + offset, len, Uint128(SEED0, SEED1)); uint64_t h = Uint128Low64(u); Check(h >> 32); Check((h << 32) >> 32); h = Uint128High64(u); Check(h >> 32); Check((h << 32) >> 32); }\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashccTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\ncout << farmhashcc::Hash32WithSeed(data + offset, len, SEED) << \"u,\" << endl;\ncout << farmhashcc::Hash32(data + offset, len) << \"u,\" << endl;\n{ uint128_t u = farmhashcc::Fingerprint128(data + offset, len); uint64_t h = Uint128Low64(u); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u, \"; h = Uint128High64(u); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint128_t u = farmhashcc::CityHash128WithSeed(data + offset, len, Uint128(SEED0, SEED1)); uint64_t h = Uint128Low64(u); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u, \"; h = Uint128High64(u); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashccTest\n\n#if TESTING\n\n//static int farmhashccTestResult = farmhashccTest::RunTest();\nTEST(farmhash, cc) { farmhashccTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashccTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashccTest::Dump(0, i);\n  }\n  farmhashccTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashmkTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n4223616069u,\n3696677242u,\n4081014168u,\n2576519988u,\n2212771159u,\n1112731063u,\n1020067935u,\n3955445564u,\n1451961420u,\n653440099u,\n31917516u,\n2957164615u,\n2590087362u,\n3879448744u,\n176305566u,\n2447367541u,\n1359016305u,\n3363804638u,\n1117290165u,\n1062549743u,\n2437877004u,\n1894455839u,\n673206794u,\n3486923651u,\n3269862919u,\n2303349487u,\n1380660650u,\n595525107u,\n1525325287u,\n2025609358u,\n176408838u,\n1592885012u,\n864896482u,\n2101378090u,\n3489229104u,\n2118965695u,\n581644891u,\n2718789079u,\n631613207u,\n4228658372u,\n3867875546u,\n3531368319u,\n3804516756u,\n3317755099u,\n1619744564u,\n2884717286u,\n1088213445u,\n2667691076u,\n3727873235u,\n2330406762u,\n3616470388u,\n967660719u,\n4148162586u,\n315219121u,\n673084328u,\n3047602355u,\n1598963653u,\n1267826661u,\n2117362589u,\n2861192253u,\n1823625377u,\n1380350078u,\n1641748342u,\n1176094482u,\n269384321u,\n2178982315u,\n3480237248u,\n2660755208u,\n1850544433u,\n3429699438u,\n1262819303u,\n640556464u,\n2421125401u,\n2188368608u,\n2612932825u,\n1474432581u,\n173790449u,\n2124882189u,\n831272654u,\n622960146u,\n4238751051u,\n3250317967u,\n2120810248u,\n1948231495u,\n1389029321u,\n2200398357u,\n2134232963u,\n2948072329u,\n617717625u,\n681164587u,\n114859387u,\n430545646u,\n57239089u,\n3163338012u,\n3482496399u,\n557662576u,\n1102441413u,\n2670159360u,\n991116729u,\n846014240u,\n4233741566u,\n1802317242u,\n3129528802u,\n1459456375u,\n1305643039u,\n3258671612u,\n1578285833u,\n868590079u,\n1631034517u,\n1695432937u,\n561078856u,\n1004115553u,\n3086090507u,\n3818348650u,\n731596645u,\n780926790u,\n2544205955u,\n158479164u,\n3983514188u,\n2004735250u,\n3436218400u,\n673684751u,\n1463431419u,\n2880490219u,\n3223748024u,\n2218318859u,\n1474466194u,\n2636437533u,\n2206794961u,\n140995728u,\n1186394086u,\n1805716888u,\n1640037724u,\n3942729099u,\n1944727013u,\n918951560u,\n498666871u,\n3486974657u,\n2967205462u,\n1167253804u,\n1884281041u,\n2866015002u,\n4158319270u,\n2627220079u,\n3733319624u,\n3317092271u,\n438323662u,\n3195868065u,\n3426606709u,\n360708338u,\n1905491012u,\n650004803u,\n1351266252u,\n3133279000u,\n3722811115u,\n2722412434u,\n918432408u,\n3678271248u,\n269599647u,\n621514057u,\n3117077855u,\n1545425390u,\n2597567410u,\n1221437820u,\n3493254589u,\n102787342u,\n918861168u,\n348795089u,\n3439883229u,\n2353641807u,\n2209585469u,\n4035884492u,\n2686995435u,\n1649888022u,\n3852893848u,\n3042700028u,\n314103172u,\n726977769u,\n2489830276u,\n2872753660u,\n1316214989u,\n1488801501u,\n1811420390u,\n639581627u,\n2362837215u,\n3634581834u,\n3648576802u,\n1257314182u,\n762118371u,\n4268447045u,\n730167096u,\n755561509u,\n882614845u,\n3696972894u,\n228263661u,\n1478636142u,\n2767751651u,\n1532617116u,\n3838657661u,\n1944359935u,\n1401102137u,\n3772933173u,\n1050098254u,\n1658079354u,\n1846025728u,\n2204244794u,\n2017217424u,\n1275162853u,\n1429816745u,\n2175565479u,\n1716109139u,\n1187506761u,\n2434641075u,\n2725597783u,\n1795687662u,\n1393312782u,\n3511565397u,\n627885430u,\n4145733164u,\n2519005353u,\n231414775u,\n1242015635u,\n2760723497u,\n2185540568u,\n727314436u,\n2358790354u,\n1186393454u,\n4234795645u,\n350567813u,\n866773875u,\n3145590392u,\n1158374055u,\n3903123687u,\n1862119793u,\n2204587556u,\n4266276976u,\n4151548555u,\n915250402u,\n2874695320u,\n2360311410u,\n1099212769u,\n1271542714u,\n3473148363u,\n1637325418u,\n1807795989u,\n2493819794u,\n3800917924u,\n4001205856u,\n2582153621u,\n3365872040u,\n2890146216u,\n2626363824u,\n3133351295u,\n4046827296u,\n3053118771u,\n4113026751u,\n884356716u,\n3828347401u,\n10608262u,\n830987972u,\n1841080500u,\n3202717763u,\n3561778749u,\n1906000052u,\n3058284660u,\n1432904514u,\n2567431677u,\n2550162530u,\n665557986u,\n936887821u,\n2101205308u,\n4253535847u,\n1662043545u,\n1253611611u,\n2091370094u,\n2635077370u,\n2602176041u,\n3624115809u,\n748442714u,\n2709749154u,\n1023493343u,\n860291012u,\n3924715584u,\n1536436740u,\n2551145800u,\n2391782865u,\n1467705048u,\n2583909796u,\n3616666170u,\n1162857372u,\n4228631071u,\n1510132376u,\n2739165009u,\n2656606142u,\n3454996358u,\n3155038853u,\n1022087316u,\n100044110u,\n494208296u,\n2746186477u,\n4216782431u,\n225448834u,\n3728320521u,\n335282866u,\n3148194874u,\n953503703u,\n1293353960u,\n202372387u,\n1326119870u,\n4045123735u,\n3819994846u,\n1629004186u,\n1081099186u,\n3591584153u,\n1670825804u,\n3404257979u,\n3262192301u,\n2572846095u,\n3714992543u,\n4264142572u,\n529616678u,\n2882154574u,\n3006354178u,\n3865969421u,\n2007174907u,\n308283107u,\n2629833703u,\n3159124075u,\n1146492131u,\n494104332u,\n493149727u,\n1342910585u,\n521642387u,\n2201695937u,\n2517980959u,\n2426821287u,\n777374655u,\n2228189792u,\n4027055486u,\n228976000u,\n3842083468u,\n1723920223u,\n1192126094u,\n787744493u,\n2740368380u,\n2284153001u,\n2773829458u,\n442000614u,\n387830783u,\n2169780670u,\n2253144627u,\n3532502484u,\n1969684059u,\n1165351416u,\n3055056536u,\n3582324253u,\n231419363u,\n770979865u,\n3213983597u,\n3690452836u,\n935794639u,\n3230602762u,\n2841762457u,\n407598927u,\n1164479891u,\n3721799696u,\n354738136u,\n1801566618u,\n3206038542u,\n2621379981u,\n1943487262u,\n3534745636u,\n1074424589u,\n1304517521u,\n4133400969u,\n2339317978u,\n2135116860u,\n4180643791u,\n2415309340u,\n1855926417u,\n3418648630u,\n1968113037u,\n597304222u,\n3668824865u,\n3810008716u,\n3014702569u,\n3151212026u,\n156057449u,\n373134533u,\n2068234004u,\n191580563u,\n3832754488u,\n2924104199u,\n2026044494u,\n4065780435u,\n122565840u,\n4194985167u,\n2744823717u,\n2494098735u,\n3753793370u,\n1885739217u,\n2488161225u,\n3643797615u,\n2653367310u,\n2494061477u,\n189968132u,\n899646597u,\n392100396u,\n4012318310u,\n3855777086u,\n3566860954u,\n2698574996u,\n2414249905u,\n1330623339u,\n1263222732u,\n1277741760u,\n2194959402u,\n1629656136u,\n120494320u,\n1072368005u,\n1084245077u,\n4011372748u,\n1366613353u,\n3108643228u,\n3332219532u,\n2114746095u,\n3964007334u,\n371687128u,\n1084813876u,\n126459896u,\n4292782331u,\n321283184u,\n398168499u,\n3604983506u,\n560701543u,\n2073961354u,\n4240841868u,\n4151211362u,\n1338986875u,\n4093476832u,\n2269279497u,\n3500846299u,\n2510225147u,\n598000444u,\n1330391422u,\n1432533385u,\n4171226231u,\n426821154u,\n2932270996u,\n3378981077u,\n2217871549u,\n1619647984u,\n4051608043u,\n3180237819u,\n12919578u,\n1375401767u,\n371320427u,\n2986640571u,\n2336669859u,\n3796464715u,\n1892383284u,\n306814912u,\n2125823211u,\n1863678891u,\n3249703818u,\n3840225752u,\n281579900u,\n264680257u,\n4266359110u,\n4182229890u,\n2239659703u,\n3627947372u,\n2373929191u,\n224082765u,\n4053639058u,\n1862360303u,\n3187739624u,\n3392706679u,\n948039509u,\n817505760u,\n1215842393u,\n3462222651u,\n536021853u,\n182346832u,\n2731944883u,\n2346674384u,\n2640961678u,\n3446695687u,\n2271722179u,\n1301069656u,\n2803881468u,\n2832614405u,\n1691544398u,\n698756814u,\n3980620906u,\n3565421410u,\n754769376u,\n4115923404u,\n3909962218u,\n2747614077u,\n2888289845u,\n1016920862u,\n2790946178u,\n3067070960u,\n3173251481u,\n1572132982u,\n255048203u,\n2996538818u,\n3405398987u,\n136106013u,\n3581605228u,\n4277437977u,\n2147300534u,\n3728426265u,\n3483629996u,\n1478452694u,\n20756076u,\n2774992067u,\n432987927u,\n1516771026u,\n3511588664u,\n2130994978u,\n509385406u,\n873090347u,\n2163904107u,\n4192239086u,\n2532489989u,\n1090772651u,\n3910797408u,\n3710882132u,\n155010959u,\n1369823531u,\n1599664937u,\n4035593587u,\n1212746925u,\n795822552u,\n116689518u,\n3674240941u,\n1135576664u,\n756750261u,\n1027431362u,\n390555140u,\n2228460216u,\n1506940482u,\n3733857700u,\n3048762971u,\n2511703196u,\n548609887u,\n1607354252u,\n659053982u,\n259884450u,\n1793130460u,\n4083364495u,\n3148555881u,\n1764350138u,\n2436485683u,\n4031563025u,\n3261860724u,\n2475833430u,\n2101726086u,\n3191176464u,\n2646658847u,\n2127042126u,\n771316100u,\n2115922959u,\n3208515045u,\n2355437783u,\n3621147793u,\n1580163615u,\n3211555675u,\n3299188490u,\n191613920u,\n466733956u,\n2939029038u,\n1509152039u,\n130591314u,\n1892874677u,\n1646908044u,\n3452406523u,\n3998376606u,\n1199243832u,\n2187108812u,\n3189230066u,\n4161151481u,\n3371454980u,\n3681788646u,\n180842187u,\n3685022399u,\n3058749895u,\n3250165163u,\n2895367943u,\n2627101723u,\n771755098u,\n1332921024u,\n3638871848u,\n514215135u,\n3591227378u,\n2300310870u,\n3689533503u,\n851607114u,\n114330368u,\n2709027386u,\n1743034877u,\n1013693860u,\n288169008u,\n3545190686u,\n1052165084u,\n3995862307u,\n96902755u,\n1097819851u,\n2645431442u,\n2184148618u,\n2151206566u,\n350979797u,\n3467920900u,\n421116779u,\n1246252u,\n4057835428u,\n329324407u,\n4104482417u,\n844624570u,\n3306265806u,\n3787625025u,\n4263241191u,\n3251413927u,\n2921204431u,\n2931915325u,\n992134330u,\n3986338354u,\n1327895216u,\n1458363596u,\n1480608532u,\n728594368u,\n3804366693u,\n794404223u,\n1643240863u,\n793417255u,\n4167916443u,\n2683488959u,\n3124925324u,\n4184843652u,\n3750971752u,\n308509829u,\n1054550805u,\n2797511972u,\n4043123412u,\n1587158240u,\n4050518606u,\n3030062190u,\n2589912753u,\n603440067u,\n937013191u,\n1071662315u,\n2100661456u,\n2602005741u,\n435516078u,\n2260470147u,\n1256268350u,\n3612035u,\n3368856141u,\n151516099u,\n3081868591u,\n3363755681u,\n2049963149u,\n2885320434u,\n84682005u,\n2411758308u,\n2695174275u,\n3099904644u,\n1787308684u,\n1132379308u,\n564634346u,\n510236510u,\n2804443681u,\n3931864252u,\n2064427949u,\n1893979229u,\n2916544974u,\n1885887717u,\n2978018250u,\n494192125u,\n2642662373u,\n901112508u,\n636035003u,\n1658643797u,\n172746975u,\n517504890u,\n3440019372u,\n4144498044u,\n1854755456u,\n3672653905u,\n4176892856u,\n382159097u,\n282871690u,\n3629300472u,\n2500754041u,\n1677659759u,\n1067175061u,\n161654075u,\n1672575536u,\n346120493u,\n2730229631u,\n203466442u,\n1244549529u,\n199761971u,\n2744895408u,\n3195315331u,\n2124618519u,\n3261045496u,\n985339699u,\n3385585455u,\n1545740710u,\n3636652160u,\n2167020081u,\n1207897204u,\n28752417u,\n2895834146u,\n3640845375u,\n3750293073u,\n548997850u,\n4207814196u,\n4183030708u,\n2462810989u,\n3929965401u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; IsAlive(farmhashmk::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashmk::Hash32(data, len++)); IsAlive(farmhashmk::Hash32(data, len++)); len -= 3; return alive > 0; }\nCheck(farmhashmk::Hash32WithSeed(data + offset, len, SEED));\nCheck(farmhashmk::Hash32(data + offset, len));\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashmkTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\ncout << farmhashmk::Hash32WithSeed(data + offset, len, SEED) << \"u,\" << endl;\ncout << farmhashmk::Hash32(data + offset, len) << \"u,\" << endl;\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashmkTest\n\n#if TESTING\n\n// static int farmhashmkTestResult = farmhashmkTest::RunTest();\nTEST(farmhash, mk) { farmhashmkTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashmkTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashmkTest::Dump(0, i);\n  }\n  farmhashmkTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashnaTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n1140953930u, 861465670u,\n3277735313u, 2681724312u,\n2598464059u, 797982799u,\n890626835u, 800175912u,\n2603993599u, 921001710u,\n1410420968u, 2134990486u,\n3283896453u, 1867689945u,\n2914424215u, 2244477846u,\n255297188u, 2992121793u,\n1110588164u, 4186314283u,\n161451183u, 3943596029u,\n4019337850u, 452431531u,\n283198166u, 2741341286u,\n3379021470u, 2557197665u,\n299850021u, 2532580744u,\n452473466u, 1706958772u,\n1298374911u, 3099673830u,\n2199864459u, 3696623795u,\n236935126u, 2976578695u,\n4055299123u, 3281581178u,\n1053458494u, 1882212500u,\n2305012065u, 2169731866u,\n3456121707u, 275903667u,\n458884671u, 3033004529u,\n3058973506u, 2379411653u,\n1898235244u, 1402319660u,\n2700149065u, 2699376854u,\n147814787u, 720739346u,\n2433714046u, 4222949502u,\n4220361840u, 1712034059u,\n3425469811u, 3690733394u,\n4148372108u, 1330324210u,\n594028478u, 2921867846u,\n1635026870u, 192883107u,\n780716741u, 1728752234u,\n3280331829u, 326029180u,\n3969463346u, 1436364519u,\n393215742u, 3349570000u,\n3824583307u, 1612122221u,\n2859809759u, 3808705738u,\n1379537552u, 1646032583u,\n2233466664u, 1432476832u,\n4023053163u, 2650381482u,\n2052294713u, 3552092450u,\n1628777059u, 1499109081u,\n3476440786u, 3829307897u,\n2960536756u, 1554038301u,\n1145519619u, 3190844552u,\n2902102606u, 3600725550u,\n237495366u, 540224401u,\n65721842u, 489963606u,\n1448662590u, 397635823u,\n1596489240u, 1562872448u,\n1790705123u, 2128624475u,\n180854224u, 2604346966u,\n1435705557u, 1262831810u,\n155445229u, 1672724608u,\n1669465176u, 1341975128u,\n663607706u, 2077310004u,\n3610042449u, 1911523866u,\n1043692997u, 1454396064u,\n2563776023u, 294527927u,\n1099072299u, 1389770549u,\n703505868u, 678706990u,\n2952353448u, 2026137563u,\n3603803785u, 629449419u,\n1933894405u, 3043213226u,\n226132789u, 2489287368u,\n1552847036u, 645684964u,\n3828089804u, 3632594520u,\n187883449u, 230403464u,\n3151491850u, 3272648435u,\n3729087873u, 1303930448u,\n2002861219u, 165370827u,\n916494250u, 1230085527u,\n3103338579u, 3064290191u,\n3807265751u, 3628174014u,\n231181488u, 851743255u,\n2295806711u, 1781190011u,\n2988893883u, 1554380634u,\n1142264800u, 3667013118u,\n1968445277u, 315203929u,\n2638023604u, 2290487377u,\n732137533u, 1909203251u,\n440398219u, 1891630171u,\n1380301172u, 1498556724u,\n4072067757u, 4165088768u,\n4204318635u, 441430649u,\n3931792696u, 197618179u,\n956300927u, 914413116u,\n3010839769u, 2837339569u,\n2148126371u, 1913303225u,\n3074915312u, 3117299654u,\n4139181436u, 2993479124u,\n3178848746u, 1357272220u,\n1438494951u, 507436733u,\n667183474u, 2084369203u,\n3854939912u, 1413396341u,\n126024219u, 146044391u,\n1016656857u, 3022024459u,\n3254014218u, 429095991u,\n165589978u, 1578546616u,\n985653208u, 1718653828u,\n623071693u, 366414107u,\n249776086u, 1207522198u,\n3047342438u, 2991127487u,\n3120876698u, 1684583131u,\n46987739u, 1157614300u,\n863214540u, 1087193030u,\n199124911u, 520792961u,\n3614377032u, 586863115u,\n3331828431u, 1013201099u,\n1716848157u, 4033596884u,\n1164298657u, 4140791139u,\n1146169032u, 1434258493u,\n3824360466u, 3242407770u,\n3725511003u, 232064808u,\n872586426u, 762243036u,\n2736953692u, 816692935u,\n512845449u, 3748861010u,\n2266795890u, 3781899767u,\n4290630595u, 517646945u,\n22638523u, 648000590u,\n959214578u, 558910384u,\n1283799121u, 3047062993u,\n1024246061u, 4027776454u,\n3544509313u, 622325861u,\n834785312u, 382936554u,\n411505255u, 1973395102u,\n1825135056u, 2725923798u,\n580988377u, 2826990641u,\n3474970689u, 1029055034u,\n812546227u, 2506885666u,\n2584372201u, 1758123094u,\n589567754u, 325737734u,\n345313518u, 2022370576u,\n3886113119u, 3338548567u,\n257578986u, 3698087965u,\n1776047957u, 1771384107u,\n3604937815u, 3198590202u,\n2305332220u, 191910725u,\n4232136669u, 427759438u,\n4244322689u, 542201663u,\n3315355162u, 2135941665u,\n556609672u, 45845311u,\n1175961330u, 3948351189u,\n23075771u, 3252374102u,\n1634635545u, 4151937410u,\n713127376u, 1467786451u,\n663013031u, 3444053918u,\n2638154051u, 810082938u,\n3077742128u, 1062268187u,\n2115441882u, 4081398201u,\n3735739145u, 2794294783u,\n2335576331u, 2560479831u,\n1379288194u, 4225182569u,\n2442302747u, 3948961926u,\n3958366652u, 3067277639u,\n3667516477u, 1709989541u,\n1516711748u, 2339636583u,\n4188504038u, 59581167u,\n2725013602u, 3639843023u,\n2658147000u, 2643979752u,\n3758739543u, 4189944477u,\n2470483982u, 877580602u,\n2995362413u, 118817200u,\n3252925478u, 2062343506u,\n3981838403u, 3762572073u,\n1231633714u, 4168280671u,\n2931588131u, 3284356565u,\n1129162571u, 732225574u,\n4173605289u, 1407328702u,\n1677744031u, 3532596884u,\n3232041815u, 1652884780u,\n2256541290u, 3459463480u,\n3740979556u, 259034107u,\n2227121257u, 1426140634u,\n3606709555u, 3424793077u,\n315836068u, 3200749877u,\n1386256573u, 24035717u,\n2982018998u, 1811050648u,\n234531934u, 1115203611u,\n1598686658u, 3146815575u,\n1603559457u, 323296368u,\n2632963283u, 1778459926u,\n739944537u, 579625482u,\n3486330348u, 492621815u,\n1231665285u, 2457048126u,\n3903349120u, 389846205u,\n3355404249u, 3275550588u,\n1052645068u, 862072556u,\n2834153464u, 1481069623u,\n2657392572u, 4279236653u,\n1688445808u, 701920051u,\n3740748788u, 3388062747u,\n1873358321u, 2152785640u,\n883382081u, 1005815394u,\n1020177209u, 734239551u,\n2371453141u, 100326520u,\n3488500412u, 1279682138u,\n2610427744u, 49703572u,\n3026361211u, 605900428u,\n302392721u, 2509302188u,\n1416453607u, 2815915291u,\n1862819968u, 519710058u,\n2450888314u, 4017598378u,\n937074653u, 3035635454u,\n1590230729u, 3268013438u,\n2710029305u, 12886044u,\n3711259084u, 2627383582u,\n3895772404u, 648534979u,\n260307902u, 855990313u,\n3669691805u, 263366740u,\n2938543471u, 414331688u,\n3080542944u, 3405007814u,\n3565059103u, 1190977418u,\n390836981u, 1606450012u,\n2649808239u, 2514169310u,\n2747519432u, 4129538640u,\n1721522849u, 492099164u,\n792990594u, 3625507637u,\n2271095827u, 2993032712u,\n2302363854u, 4013112951u,\n1111617969u, 2183845740u,\n795918276u, 1116991810u,\n3110898804u, 3963062126u,\n2737064702u, 462795667u,\n937372240u, 1343017609u,\n1091041189u, 2790555455u,\n277024217u, 25485284u,\n1166522068u, 1623631848u,\n241727183u, 2836158787u,\n3112996740u, 573836428u,\n2721658101u, 1937681565u,\n4175169209u, 3190765433u,\n1970000788u, 1668258120u,\n114616703u, 954762543u,\n199237753u, 4094644498u,\n2522281978u, 732086117u,\n1756889687u, 2936126607u,\n2437031370u, 4103143808u,\n3883389541u, 3171090854u,\n2483004780u, 1927385370u,\n2360538162u, 2740855009u,\n4241185118u, 1492209542u,\n1672737098u, 2148675559u,\n1789864670u, 2434313103u,\n2319172611u, 2760941207u,\n2636210123u, 1338083267u,\n1128080590u, 822806371u,\n1199583556u, 314727461u,\n1335160250u, 2084630531u,\n1156261526u, 316766066u,\n112090465u, 3129033323u,\n2746885618u, 636616055u,\n2582210744u, 1721064910u,\n3468394263u, 470463518u,\n2076016059u, 408721884u,\n2121041886u, 378460278u,\n1915948002u, 357324860u,\n2301682622u, 2691859523u,\n1869756364u, 2429314418u,\n2193146527u, 1185564327u,\n2614088922u, 1975527044u,\n919067651u, 2855948894u,\n3662539576u, 1943802836u,\n3529473373u, 1490330107u,\n366036094u, 3384241033u,\n4276268604u, 448403661u,\n4271796078u, 1910401882u,\n3077107698u, 299427366u,\n2035665349u, 3201262636u,\n3738454258u, 2554452696u,\n3588997135u, 3363895827u,\n1267505995u, 1852004679u,\n2237827073u, 2803250686u,\n3468044908u, 2143572850u,\n1728158656u, 1022551180u,\n1996680960u, 839529273u,\n2400647871u, 2201096054u,\n3606433628u, 2597259793u,\n3544595875u, 3909443124u,\n819278607u, 3447346709u,\n806136613u, 2711436388u,\n3656063205u, 837475154u,\n694525336u, 4070212073u,\n4011303412u, 1068395209u,\n438095290u, 484603494u,\n2673730227u, 737767009u,\n642310823u, 3914002299u,\n308425103u, 268427550u,\n1334387085u, 4069797497u,\n4280783219u, 2914011058u,\n4243643405u, 2849988118u,\n2504230175u, 1817156623u,\n2804200483u, 3406991497u,\n2948254999u, 2102063419u,\n1071272117u, 514889942u,\n571972433u, 1246595599u,\n1735616066u, 1539151988u,\n1230831543u, 277987182u,\n4269526481u, 991511607u,\n95237878u, 2005032160u,\n1291113144u, 626619670u,\n3560835907u, 164940926u,\n1433635018u, 116647396u,\n3039097112u, 2868163232u,\n1141645918u, 1764165478u,\n881378302u, 2159170082u,\n2953647681u, 1011320066u,\n184856151u, 1723308975u,\n336034862u, 2017579106u,\n1476681709u, 147523618u,\n3896252223u, 2264728166u,\n944743644u, 1694443528u,\n2690700128u, 1947321519u,\n735478508u, 4058183171u,\n260177668u, 505662155u,\n2391691262u, 1920739747u,\n3216960415u, 1898176786u,\n3722741628u, 1511077569u,\n449636564u, 983350414u,\n2580237367u, 2055059789u,\n1103819072u, 2089123665u,\n3873755579u, 2718467458u,\n3124338704u, 3204250304u,\n2475035432u, 1120017626u,\n3873758287u, 1982999824u,\n2950794582u, 780634378u,\n2842141483u, 4029205195u,\n1656892865u, 3330993377u,\n80890710u, 1953796601u,\n3873078673u, 136118734u,\n2317676604u, 4199091610u,\n1864448181u, 3063437608u,\n1699452298u, 1403506686u,\n1513069466u, 2348491299u,\n4273657745u, 4055855649u,\n1805475756u, 2562064338u,\n973124563u, 4197091358u,\n172861513u, 2858726767u,\n4271866024u, 3071338162u,\n3590386266u, 2328277259u,\n1096050703u, 1189614342u,\n459509140u, 771592405u,\n817999971u, 3740825152u,\n520400189u, 1941874618u,\n185232757u, 4032960199u,\n3928245258u, 3527721294u,\n1301118856u, 752188080u,\n3512945009u, 308584855u,\n2105373972u, 752872278u,\n3823368815u, 3760952096u,\n4250142168u, 2565680167u,\n3646354146u, 1259957455u,\n1085857127u, 3471066607u,\n38924274u, 3770488806u,\n1083869477u, 3312508103u,\n71956383u, 3738784936u,\n3099963860u, 1255084262u,\n4286969992u, 3621849251u,\n1190908967u, 1831557743u,\n2363435042u, 54945052u,\n4059585566u, 4023974274u,\n1788578453u, 3442180039u,\n2534883189u, 2432427547u,\n3909757989u, 731996369u,\n4168347425u, 1356028512u,\n2741583197u, 1280920000u,\n312887059u, 3259015297u,\n3946278527u, 4135481831u,\n1281043691u, 1121403845u,\n3312292477u, 1819941269u,\n1741932545u, 3293015483u,\n2127558730u, 713121337u,\n2635469238u, 486003418u,\n4015067527u, 2976737859u,\n2108187161u, 927011680u,\n1970188338u, 4177613234u,\n1799789551u, 2118505126u,\n4134691985u, 1958963937u,\n1929210029u, 2555835851u,\n2768832862u, 910892050u,\n2567532373u, 4075249328u,\n86689814u, 3726640307u,\n1392137718u, 1240000030u,\n4104757832u, 3026358429u,\n313797689u, 1435798509u,\n3101500919u, 1241665335u,\n3573008472u, 3615577014u,\n3767659003u, 3134294021u,\n4063565523u, 2296824134u,\n1541946015u, 3087190425u,\n2693152531u, 2199672572u,\n2123763822u, 1034244398u,\n857839960u, 2515339233u,\n2228007483u, 1628096047u,\n2116502287u, 2502657424u,\n2809830736u, 460237542u,\n450205998u, 3646921704u,\n3818199357u, 1808504491u,\n1950698961u, 2069753399u,\n3657033172u, 3734547671u,\n4067859590u, 3292597295u,\n1106466069u, 356742959u,\n2469567432u, 3495418823u,\n183440071u, 3248055817u,\n3662626864u, 1750561299u,\n3926138664u, 4088592524u,\n567122118u, 3810297651u,\n992181339u, 3384018814u,\n3272124369u, 3177596743u,\n320086295u, 2316548367u,\n100741310u, 451656820u,\n4086604273u, 3759628395u,\n2553391092u, 1745659881u,\n3650357479u, 2390172694u,\n330172533u, 767377322u,\n526742034u, 4102497288u,\n2088767754u, 164402616u,\n2482632320u, 2352347393u,\n1873658044u, 3861555476u,\n2751052984u, 1767810825u,\n20037241u, 545143220u,\n2594532522u, 472304191u,\n3441135892u, 3323383489u,\n258785117u, 2977745165u,\n2781737565u, 2963590112u,\n2756998822u, 207428029u,\n2581558559u, 3824717027u,\n1258619503u, 3472047571u,\n2648427775u, 2360400900u,\n2393763818u, 2332399088u,\n3932701729u, 884421165u,\n1396468647u, 1377764574u,\n4061795938u, 1559119087u,\n3343596838u, 3604258095u,\n1435134775u, 1099809675u,\n908163739u, 1418405656u,\n368446627u, 3741651161u,\n3374512975u, 3542220540u,\n3244772570u, 200009340u,\n3198975081u, 2521038253u,\n4081637863u, 337070226u,\n3235259030u, 3897262827u,\n736956644u, 641040550u,\n644850146u, 1306761320u,\n4219448634u, 193750500u,\n3293278106u, 1383997679u,\n1242645122u, 4109252858u,\n450747727u, 3716617561u,\n362725793u, 2252520167u,\n3377483696u, 1788337208u,\n8130777u, 3226734120u,\n759239140u, 1012411364u,\n1658628529u, 2911512007u,\n1002580201u, 1681898320u,\n3039016929u, 4294520281u,\n367022558u, 3071359622u,\n3205848570u, 152989999u,\n3839042136u, 2357687350u,\n4273132307u, 3898950547u,\n1176841812u, 1314157485u,\n75443951u, 1027027239u,\n1858986613u, 2040551642u,\n36574105u, 2603059541u,\n3456147251u, 2137668425u,\n4077477194u, 3565689036u,\n491832241u, 363703593u,\n2579177168u, 3589545214u,\n265993036u, 1864569342u,\n4149035573u, 3189253455u,\n1072259310u, 3153745937u,\n923017956u, 490608221u,\n855846773u, 845706553u,\n1018226240u, 1604548872u,\n3833372385u, 3287246572u,\n2757959551u, 2452872151u,\n1553870564u, 1713154780u,\n2649450292u, 500120236u,\n84251717u, 661869670u,\n1444911517u, 2489716881u,\n2810524030u, 1561519055u,\n3884088359u, 2509890699u,\n4247155916u, 1005636939u,\n3224066062u, 2774151984u,\n2035978240u, 2514910366u,\n1478837908u, 3144450144u,\n2107011431u, 96459446u,\n3587732908u, 2389230590u,\n3287635953u, 250533792u,\n1235983679u, 4237425634u,\n3704645833u, 3882376657u,\n2976369049u, 1187061987u,\n276949224u, 4100839753u,\n1698347543u, 1629662314u,\n1556151829u, 3784939568u,\n427484362u, 4246879223u,\n3155311770u, 4285163791u,\n1693376813u, 124492786u,\n1858777639u, 3476334357u,\n1941442701u, 1121980173u,\n3485932087u, 820852908u,\n358032121u, 2511026735u,\n1873607283u, 2556067450u,\n2248275536u, 1528632094u,\n1535473864u, 556796152u,\n1499201704u, 1472623890u,\n1526518503u, 3692729434u,\n1476438092u, 2913077464u,\n335109599u, 2167614601u,\n4121131078u, 3158127917u,\n3051522276u, 4046477658u,\n2857717851u, 1863977403u,\n1341023343u, 692059110u,\n1802040304u, 990407433u,\n3285847572u, 319814144u,\n561105582u, 1540183799u,\n4052924496u, 2926590471u,\n2244539806u, 439121871u,\n3317903224u, 3178387550u,\n4265214507u, 82077489u,\n1978918971u, 4279668976u,\n128732476u, 2853224222u,\n464407878u, 4190838199u,\n997819001u, 3250520802u,\n2330081301u, 4095846095u,\n733509243u, 1583801700u,\n722314527u, 3552883023u,\n1403784280u, 432327540u,\n1877837196u, 3912423882u,\n505219998u, 696031431u,\n908238873u, 4189387259u,\n8759461u, 2540185277u,\n3385159748u, 381355877u,\n2519951681u, 1679786240u,\n2019419351u, 4051584612u,\n1933923923u, 3768201861u,\n1670133081u, 3454981037u,\n700836153u, 1675560450u,\n371560700u, 338262316u,\n847351840u, 2222395828u,\n3130433948u, 405251683u,\n3037574880u, 184098830u,\n453340528u, 1385561439u,\n2224044848u, 4071581802u,\n1431235296u, 5570097u,\n570114376u, 2287305551u,\n2272418128u, 803575837u,\n3943113491u, 414959787u,\n708083137u, 2452657767u,\n4019147902u, 3841480082u,\n3791794715u, 2965956183u,\n2763690963u, 2350937598u,\n3424361375u, 779434428u,\n1274947212u, 686105485u,\n3426668051u, 3692865672u,\n3057021940u, 2285701422u,\n349809124u, 1379278508u,\n3623750518u, 215970497u,\n1783152480u, 823305654u,\n216118434u, 1787189830u,\n3692048450u, 2272612521u,\n3032187389u, 4159715581u,\n1388133148u, 1611772864u,\n2544383526u, 552925303u,\n3420960112u, 3198900547u,\n3503230228u, 2603352423u,\n2318375898u, 4064071435u,\n3006227299u, 4194096960u,\n1283392422u, 1510460996u,\n174272138u, 3671038966u,\n1775955687u, 1719108984u,\n1763892006u, 1385029063u,\n4083790740u, 406757708u,\n684087286u, 531310503u,\n3329923157u, 3492083607u,\n1059031410u, 3037314475u,\n3105682208u, 3382290593u,\n2292208503u, 426380557u,\n97373678u, 3842309471u,\n777173623u, 3241407531u,\n303065016u, 1477104583u,\n4234905200u, 2512514774u,\n2649684057u, 1397502982u,\n1802596032u, 3973022223u,\n2543566442u, 3139578968u,\n3193669211u, 811750340u,\n4013496209u, 567361887u,\n4169410406u, 3622282782u,\n3403136990u, 2540585554u,\n895210040u, 3862229802u,\n1145435213u, 4146963980u,\n784952939u, 943914610u,\n573034522u, 464420660u,\n2356867109u, 3054347639u,\n3985088434u, 1911188923u,\n583391304u, 176468511u,\n2990150068u, 2338031599u,\n519948041u, 3181425568u,\n496106033u, 4110294665u,\n2736756930u, 1196757691u,\n1089679033u, 240953857u,\n3399092928u, 4040779538u,\n2843673626u, 240495962u,\n3017658263u, 3828377737u,\n4243717901u, 2448373688u,\n2759616657u, 2246245780u,\n308018483u, 4262383425u,\n2731780771u, 328023017u,\n2884443148u, 841480070u,\n3188015819u, 4051263539u,\n2298178908u, 2944209234u,\n1372958390u, 4164532914u,\n4074952232u, 1683612329u,\n2155036654u, 1872815858u,\n2041174279u, 2368092311u,\n206775997u, 2283918569u,\n645945606u, 115406202u,\n4206471368u, 3923500892u,\n2217060665u, 350160869u,\n706531239u, 2824302286u,\n509981657u, 1469342315u,\n140980u, 1891558063u,\n164887091u, 3094962711u,\n3437115622u, 13327420u,\n422986366u, 330624974u,\n3630863408u, 2425505046u,\n824008515u, 3543885677u,\n918718096u, 376390582u,\n3224043675u, 3724791476u,\n1837192976u, 2968738516u,\n3424344721u, 3187805406u,\n1550978788u, 1743089918u,\n4251270061u, 645016762u,\n3855037968u, 1928519266u,\n1373803416u, 2289007286u,\n1889218686u, 1610271373u,\n3059200728u, 2108753646u,\n582042641u, 812347242u,\n3188172418u, 191994904u,\n1343511943u, 2247006571u,\n463291708u, 2697254095u,\n1534175504u, 1106275740u,\n622521957u, 917121602u,\n4095777215u, 3955972648u,\n3852234638u, 2845309942u,\n3299763344u, 2864033668u,\n2554947496u, 799569078u,\n2551629074u, 1102873346u,\n2661022773u, 2006922227u,\n2900438444u, 1448194126u,\n1321567432u, 1983773590u,\n1237256330u, 3449066284u,\n1691553115u, 3274671549u,\n4271625619u, 2741371614u,\n3285899651u, 786322314u,\n1586632825u, 564385522u,\n2530557509u, 2974240289u,\n1244759631u, 3263135197u,\n3592389776u, 3570296884u,\n2749873561u, 521432811u,\n987586766u, 3206261120u,\n1327840078u, 4078716491u,\n1753812954u, 976892272u,\n1827135136u, 1781944746u,\n1328622957u, 1015377974u,\n3439601008u, 2209584557u,\n2482286699u, 1109175923u,\n874877499u, 2036083451u,\n483570344u, 1091877599u,\n4190721328u, 1129462471u,\n640035849u, 1867372700u,\n920761165u, 3273688770u,\n1623777358u, 3389003793u,\n3241132743u, 2734783008u,\n696674661u, 2502161880u,\n1646071378u, 1164309901u,\n350411888u, 1978005963u,\n2253937037u, 7371540u,\n989577914u, 3626554867u,\n3214796883u, 531343826u,\n398899695u, 1145247203u,\n1516846461u, 3656006011u,\n529303412u, 3318455811u,\n3062828129u, 1696355359u,\n3698796465u, 3155218919u,\n1457595996u, 3191404246u,\n1395609912u, 2917345728u,\n1237411891u, 1854985978u,\n1091884675u, 3504488111u,\n3109924189u, 1628881950u,\n3939149151u, 878608872u,\n778235395u, 1052990614u,\n903730231u, 2069566979u,\n2437686324u, 3163786257u,\n2257884264u, 2123173186u,\n939764916u, 2933010098u,\n1235300371u, 1256485167u,\n1950274665u, 2180372319u,\n2648400302u, 122035049u,\n1883344352u, 2083771672u,\n3712110541u, 321199441u,\n1896357377u, 508560958u,\n3066325351u, 2770847216u,\n3177982504u, 296902736u,\n1486926688u, 456842861u,\n601221482u, 3992583643u,\n2794121515u, 1533934172u,\n1706465470u, 4281971893u,\n2557027816u, 900741486u,\n227175484u, 550595824u,\n690918144u, 2825943628u,\n90375300u, 300318232u,\n1985329734u, 1440763373u,\n3670603707u, 2533900859u,\n3253901179u, 542270815u,\n3677388841u, 307706478u,\n2570910669u, 3320103693u,\n1273768482u, 1216399252u,\n1652924805u, 1043647584u,\n1120323676u, 639941430u,\n325675502u, 3652676161u,\n4241680335u, 1545838362u,\n1991398008u, 4100211814u,\n1097584090u, 3262252593u,\n2254324292u, 1765019121u,\n4060211241u, 2315856188u,\n3704419305u, 411263051u,\n238929055u, 3540688404u,\n3094544537u, 3250435765u,\n3460621305u, 1967599860u,\n2016157366u, 847389916u,\n1659615591u, 4020453639u,\n901109753u, 2682611693u,\n1661364280u, 177155177u,\n3210561911u, 3802058181u,\n797089608u, 3286110054u,\n2110358240u, 1353279028u,\n2479975820u, 471725410u,\n2219863904u, 3623364733u,\n3167128228u, 1052188336u,\n3656587111u, 721788662u,\n3061255808u, 1615375832u,\n924941453u, 2547780700u,\n3328169224u, 1310964134u,\n2701956286u, 4145497671u,\n1421461094u, 1221397398u,\n1589183618u, 1492533854u,\n449740816u, 2686506989u,\n3035198924u, 1682886232u,\n2529760244u, 3342031659u,\n1235084019u, 2151665147u,\n2315686577u, 3282027660u,\n1140138691u, 2754346599u,\n2091754612u, 1178454681u,\n4226896579u, 2942520471u,\n2122168506u, 3751680858u,\n3213794286u, 2601416506u,\n4142747914u, 3951404257u,\n4243249649u, 748595836u,\n4004834921u, 238887261u,\n1927321047u, 2217148444u,\n205977665u, 1885975275u,\n186020771u, 2367569534u,\n2941662631u, 2608559272u,\n3342096731u, 741809437u,\n1962659444u, 3539886328u,\n3036596491u, 2282550094u,\n2366462727u, 2748286642u,\n2144472852u, 1390394371u,\n1257385924u, 2205425874u,\n2119055686u, 46865323u,\n3597555910u, 3188438773u,\n2372320753u, 3641116924u,\n3116286108u, 2680722658u,\n3371014971u, 2058751609u,\n2966943726u, 2345078707u,\n2330535244u, 4013841927u,\n1169588594u, 857915866u,\n1875260989u, 3175831309u,\n3193475664u, 1955181430u,\n923161569u, 4068653043u,\n776445899u, 954196929u,\n61509556u, 4248237857u,\n3808667664u, 581227317u,\n2893240187u, 4159497403u,\n4212264930u, 3973886195u,\n2077539039u, 851579036u,\n2957587591u, 772351886u,\n1173659554u, 946748363u,\n2794103714u, 2094375930u,\n4234750213u, 3671645488u,\n2614250782u, 2620465358u,\n3122317317u, 2365436865u,\n3393973390u, 523513960u,\n3645735309u, 2766686992u,\n2023960931u, 2312244996u,\n1875932218u, 3253711056u,\n3622416881u, 3274929205u,\n612094988u, 1555465129u,\n2114270406u, 3553762793u,\n1832633644u, 1087551556u,\n3306195841u, 1702313921u,\n3675066046u, 1735998785u,\n1690923980u, 1482649756u,\n1171351291u, 2043136409u,\n1962596992u, 461214626u,\n3278253346u, 1392428048u,\n3744621107u, 1028502697u,\n3991171462u, 1014064003u,\n3642345425u, 3186995039u,\n6114625u, 3359104346u,\n414856965u, 2814387514u,\n3583605071u, 2497896367u,\n1024572712u, 1927582962u,\n2892797583u, 845302635u,\n328548052u, 1523379748u,\n3392622118u, 1347167673u,\n1012316581u, 37767602u,\n2647726017u, 1070326065u,\n2075035198u, 4202817168u,\n2502924707u, 2612406822u,\n2187115553u, 1180137213u,\n701024148u, 1481965992u,\n3223787553u, 2083541843u,\n203230202u, 3876887380u,\n1334816273u, 2870251538u,\n2186205850u, 3985213979u,\n333533378u, 806507642u,\n1010064531u, 713520765u,\n3084131515u, 2637421459u,\n1703168933u, 1517562266u,\n4089081247u, 3231042924u,\n3079916123u, 3154574447u,\n2253948262u, 1725190035u,\n2452539325u, 1343734533u,\n213706059u, 2519409656u,\n108055211u, 2916327746u,\n587001593u, 1917607088u,\n4202913084u, 926304016u,\n469255411u, 4042080256u,\n3498936874u, 246692543u,\n495780578u, 438717281u,\n2259272650u, 4011324645u,\n2836854664u, 2317249321u,\n946828752u, 1280403658u,\n1905648354u, 2034241661u,\n774652981u, 1285694082u,\n2200307766u, 2158671727u,\n1135162148u, 232040752u,\n397012087u, 1717527689u,\n1720414106u, 918797022u,\n2580119304u, 3568069742u,\n2904461070u, 3893453420u,\n973817938u, 667499332u,\n3785870412u, 2088861715u,\n1565179401u, 600903026u,\n591806775u, 3512242245u,\n997964515u, 2339605347u,\n1134342772u, 3234226304u,\n4084179455u, 302315791u,\n2445626811u, 2590372496u,\n345572299u, 2274770442u,\n3600587867u, 3706939009u,\n1430507980u, 2656330434u,\n1079209397u, 2122849632u,\n1423705223u, 3826321888u,\n3683385276u, 1057038163u,\n1242840526u, 3987000643u,\n2398253089u, 1538190921u,\n1295898647u, 3570196893u,\n3065138774u, 3111336863u,\n2524949549u, 4203895425u,\n3025864372u, 968800353u,\n1023721001u, 3763083325u,\n526350786u, 635552097u,\n2308118370u, 2166472723u,\n2196937373u, 2643841788u,\n3040011470u, 4010301879u,\n2782379560u, 3474682856u,\n4201389782u, 4223278891u,\n1457302296u, 2251842132u,\n1090062008u, 3188219189u,\n292733931u, 1424229089u,\n1590782640u, 1365212370u,\n3975957073u, 3982969588u,\n2927147928u, 1048291071u,\n2766680094u, 884908196u,\n35237839u, 2221180633u,\n2490333812u, 4098360768u,\n4029081103u, 3490831871u,\n2392516272u, 3455379186u,\n3948800722u, 335456628u,\n2105117968u, 4181629008u,\n1044201772u, 3335754111u,\n540133451u, 3313113759u,\n3786107905u, 2627207327u,\n3540337875u, 3473113388u,\n3430536378u, 2514123129u,\n2124531276u, 3872633376u,\n3272957388u, 3501994650u,\n2418881542u, 487365389u,\n3877672368u, 1512866656u,\n3486531087u, 2102955203u,\n1136054817u, 3004241477u,\n1549075351u, 1302002008u,\n3936430045u, 2258587644u,\n4109233936u, 3679809321u,\n3467083076u, 2484463221u,\n1594979755u, 529218470u,\n3527024461u, 1147434678u,\n106799023u, 1823161970u,\n1704656738u, 1675883700u,\n3308746763u, 1875093248u,\n1352868568u, 1898561846u,\n2508994984u, 3177750780u,\n4217929592u, 400784472u,\n80090315u, 3564414786u,\n3841585648u, 3379293868u,\n160353261u, 2413172925u,\n2378499279u, 673436726u,\n1505702418u, 1330977363u,\n1853298225u, 3201741245u,\n2135714208u, 4069554166u,\n3715612384u, 3692488887u,\n3680311316u, 4274382900u,\n914186796u, 2264886523u,\n3869634032u, 1254199592u,\n1131020455u, 194781179u,\n429923922u, 2763792336u,\n2052895198u, 3997373194u,\n3440090658u, 2165746386u,\n1575500242u, 3463310191u,\n2064974716u, 3779513671u,\n3106421434u, 880320527u,\n3281914119u, 286569042u,\n3909096631u, 122359727u,\n1429837716u, 252230074u,\n4111461225u, 762273136u,\n93658514u, 2766407143u,\n3623657004u, 3869801679u,\n3925695921u, 2390397316u,\n2499025338u, 2741806539u,\n2507199021u, 1659221866u,\n361292116u, 4048761557u,\n3797133396u, 1517903247u,\n3121647246u, 3884308578u,\n1697201500u, 1558800262u,\n4150812360u, 3161302278u,\n2610217849u, 641564641u,\n183814518u, 2075245419u,\n611996508u, 2223461433u,\n329123979u, 121860586u,\n860985829u, 1137889144u,\n4018949439u, 2904348960u,\n947795261u, 1992594155u,\n4255427501u, 2281583851u,\n2892637604u, 1478186924u,\n3050771207u, 2767035539u,\n373510582u, 1963520320u,\n3763848370u, 3756817798u,\n627269409u, 1806905031u,\n1814444610u, 3646665053u,\n1822693920u, 278515794u,\n584050483u, 4142579188u,\n2149745808u, 3193071606u,\n1179706341u, 2693495182u,\n3259749808u, 644172091u,\n880509048u, 3340630542u,\n3365160815u, 2384445068u,\n3053081915u, 2840648309u,\n1986990122u, 1084703471u,\n2370410550u, 1627743573u,\n2244943480u, 4057483496u,\n2611595995u, 2470013639u,\n4024732359u, 3987190386u,\n873421687u, 2447660175u,\n3226583022u, 767655877u,\n2528024413u, 1962070688u,\n1233635843u, 2163464207u,\n659054446u, 854207134u,\n258410943u, 4197831420u,\n2515400215u, 3100476924u,\n1961549594u, 2219491151u,\n3997658851u, 163850514u,\n470325051u, 2598261204u,\n3052145580u, 59836528u,\n1376188597u, 966733415u,\n850667549u, 3622479237u,\n1083731990u, 1525777459u,\n4005126532u, 1428155540u,\n2781907007u, 943739431u,\n1493961005u, 2839096988u,\n2000057832u, 1941829603u,\n1901484772u, 939810041u,\n3377407371u, 3090115837u,\n3310840540u, 2068409688u,\n3261383939u, 2212130277u,\n2594774045u, 2912652418u,\n4179816101u, 3534504531u,\n3349254805u, 2796552902u,\n1385421283u, 4259908631u,\n3714780837u, 3070073945u,\n3372846298u, 3835884044u,\n3047965714u, 3009018735u,\n744091167u, 1861124263u,\n2764936304u, 1338171648u,\n4222019554u, 1395200692u,\n1371426007u, 3338031581u,\n2525665319u, 4196233786u,\n2332743921u, 1474702008u,\n2274266301u, 4255175517u,\n2290169528u, 1793910997u,\n2188254024u, 354202001u,\n3864458796u, 4280290498u,\n1554419340u, 1733094688u,\n2010552302u, 1561807039u,\n664313606u, 2548990879u,\n1084699349u, 3233936866u,\n973895284u, 2386881969u,\n1831995860u, 2961465052u,\n1428704144u, 3269904970u,\n231648253u, 2602483763u,\n4125013173u, 3319187387u,\n3347011944u, 1892898231u,\n4019114049u, 868879116u,\n4085937045u, 2378411019u,\n1072588531u, 3547435717u,\n2208070766u, 1069899078u,\n3142980597u, 2337088907u,\n1593338562u, 919414554u,\n688077849u, 3625708135u,\n1472447348u, 1947711896u,\n3953006207u, 877438080u,\n845995820u, 3150361443u,\n3053496713u, 2484577841u,\n224271045u, 2914958001u,\n2682612949u, 806655563u,\n2436224507u, 1907729235u,\n2920583824u, 1251814062u,\n2070814520u, 4034325578u,\n497847539u, 2714317144u,\n385182008u, 640855184u,\n1327075087u, 1062468773u,\n1757405994u, 1374270191u,\n4263183176u, 3041193150u,\n1037871524u, 3633173991u,\n4231821821u, 2830131945u,\n3505072908u, 2830570613u,\n4195208715u, 575398021u,\n3992840257u, 3691788221u,\n1949847968u, 2999344380u,\n3183782163u, 3723754342u,\n759716128u, 3284107364u,\n1714496583u, 15918244u,\n820509475u, 2553936299u,\n2201876606u, 4237151697u,\n2605688266u, 3253705097u,\n1008333207u, 712158730u,\n1722280252u, 1933868287u,\n4152736859u, 2097020806u,\n584426382u, 2836501956u,\n2522777566u, 1996172430u,\n2122199776u, 1069285218u,\n1474209360u, 690831894u,\n107482532u, 3695525410u,\n670591796u, 768977505u,\n2412057331u, 3647886687u,\n3110327607u, 1072658422u,\n379861934u, 1557579480u,\n4124127129u, 2271365865u,\n3880613089u, 739218494u,\n547346027u, 388559045u,\n3147335977u, 176230425u,\n3094853730u, 2554321205u,\n1495176194u, 4093461535u,\n3521297827u, 4108148413u,\n1913727929u, 1177947623u,\n1911655402u, 1053371241u,\n3265708874u, 1266515850u,\n1045540427u, 3194420196u,\n3717104621u, 1144474110u,\n1464392345u, 52070157u,\n4144237690u, 3350490823u,\n4166253320u, 2747410691u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; { uint64_t h = farmhashna::Hash64WithSeeds(data, len++, SEED0, SEED1); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashna::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashna::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; }\n{ uint64_t h = farmhashna::Hash64WithSeeds(data + offset, len, SEED0, SEED1); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint64_t h = farmhashna::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint64_t h = farmhashna::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); }\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashnaTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\n{ uint64_t h = farmhashna::Hash64WithSeeds(data + offset, len, SEED0, SEED1); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint64_t h = farmhashna::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint64_t h = farmhashna::Hash64(data + offset, len); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashnaTest\n\n#if TESTING\n\n//static int farmhashnaTestResult = farmhashnaTest::RunTest();\nTEST(farmhash, na) { farmhashnaTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashnaTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashnaTest::Dump(0, i);\n  }\n  farmhashnaTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashntTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n2681724312u,\n797982799u,\n921001710u,\n2134990486u,\n2244477846u,\n2992121793u,\n3943596029u,\n452431531u,\n2557197665u,\n2532580744u,\n3099673830u,\n3696623795u,\n3281581178u,\n1882212500u,\n275903667u,\n3033004529u,\n1402319660u,\n2699376854u,\n4222949502u,\n1712034059u,\n1330324210u,\n2921867846u,\n1728752234u,\n326029180u,\n3349570000u,\n1612122221u,\n1646032583u,\n1432476832u,\n3552092450u,\n1499109081u,\n1554038301u,\n3190844552u,\n540224401u,\n489963606u,\n1562872448u,\n2128624475u,\n1262831810u,\n1672724608u,\n2077310004u,\n1911523866u,\n294527927u,\n1389770549u,\n2026137563u,\n629449419u,\n2489287368u,\n645684964u,\n230403464u,\n3272648435u,\n165370827u,\n1230085527u,\n3628174014u,\n851743255u,\n1554380634u,\n3667013118u,\n2290487377u,\n1909203251u,\n1498556724u,\n4165088768u,\n197618179u,\n914413116u,\n1913303225u,\n3117299654u,\n1357272220u,\n507436733u,\n1413396341u,\n146044391u,\n429095991u,\n3056862311u,\n366414107u,\n2293458109u,\n1684583131u,\n1170404994u,\n520792961u,\n1577421232u,\n4033596884u,\n4229339322u,\n3242407770u,\n2649785113u,\n816692935u,\n3555213933u,\n517646945u,\n2180594090u,\n3047062993u,\n2391606125u,\n382936554u,\n788479970u,\n2826990641u,\n3167748333u,\n1758123094u,\n389974094u,\n3338548567u,\n2583576230u,\n3198590202u,\n4155628142u,\n542201663u,\n2856634168u,\n3948351189u,\n4194218315u,\n1467786451u,\n2743592929u,\n1062268187u,\n3810665822u,\n2560479831u,\n997658837u,\n3067277639u,\n1211737169u,\n59581167u,\n1389679610u,\n4189944477u,\n100876854u,\n2062343506u,\n3088828656u,\n3284356565u,\n3130054947u,\n3532596884u,\n3887208531u,\n259034107u,\n3233195759u,\n3200749877u,\n760633989u,\n1115203611u,\n1516407838u,\n1778459926u,\n2146672889u,\n2457048126u,\n2217471853u,\n862072556u,\n3745267835u,\n701920051u,\n581695350u,\n1410111809u,\n3326135446u,\n2187968410u,\n4267859263u,\n479241367u,\n2868987960u,\n704325635u,\n1418509533u,\n735688735u,\n3283299459u,\n813690332u,\n1439630796u,\n3195309868u,\n1616408198u,\n3254795114u,\n2799925823u,\n3929484338u,\n1798536177u,\n4205965408u,\n1499475160u,\n4247675634u,\n3779953975u,\n785893184u,\n2778575413u,\n1160134629u,\n823113169u,\n4116162021u,\n4167766971u,\n2487440590u,\n4004655503u,\n4044418876u,\n1462554406u,\n2011102035u,\n4265993528u,\n576405853u,\n4038839101u,\n2425317635u,\n1401013391u,\n3062418115u,\n3167030094u,\n2602636307u,\n4264167741u,\n4017058800u,\n1029665228u,\n4036354071u,\n2670703363u,\n688472265u,\n1054670286u,\n338058159u,\n1539305024u,\n146827036u,\n4060134777u,\n2502815838u,\n1603444633u,\n2448966429u,\n3891353218u,\n1082330589u,\n201837927u,\n2848283092u,\n883849006u,\n1982110346u,\n541496720u,\n133643215u,\n3847827123u,\n4015671361u,\n2849988118u,\n3452457457u,\n2102063419u,\n3281002516u,\n1539151988u,\n1147951686u,\n2005032160u,\n2415262714u,\n116647396u,\n1029284767u,\n2159170082u,\n1919171906u,\n2017579106u,\n2473524405u,\n1694443528u,\n3671562289u,\n505662155u,\n1019936943u,\n1511077569u,\n773792826u,\n2089123665u,\n484732447u,\n1120017626u,\n2809286837u,\n4029205195u,\n1097806406u,\n136118734u,\n4017075736u,\n1403506686u,\n1516736273u,\n2562064338u,\n2984955003u,\n3071338162u,\n1923531348u,\n771592405u,\n2586632018u,\n4032960199u,\n2687561076u,\n308584855u,\n1692079268u,\n2565680167u,\n3674576684u,\n3770488806u,\n69201295u,\n1255084262u,\n3593730713u,\n54945052u,\n1939595371u,\n2432427547u,\n2295501078u,\n1280920000u,\n82177963u,\n1121403845u,\n2889101923u,\n713121337u,\n1747052377u,\n927011680u,\n4142246789u,\n1958963937u,\n1636932722u,\n4075249328u,\n2025886508u,\n3026358429u,\n1845587644u,\n3615577014u,\n1363253259u,\n3087190425u,\n341851980u,\n2515339233u,\n1276595523u,\n460237542u,\n4198897105u,\n2069753399u,\n4278599955u,\n356742959u,\n3735275001u,\n1750561299u,\n668829411u,\n3384018814u,\n4233785523u,\n451656820u,\n107312677u,\n2390172694u,\n1216645846u,\n164402616u,\n1689811113u,\n1767810825u,\n1397772514u,\n3323383489u,\n2986430557u,\n207428029u,\n2260498180u,\n2360400900u,\n1263709570u,\n1377764574u,\n4252610345u,\n1099809675u,\n2776960536u,\n3542220540u,\n3752806924u,\n337070226u,\n3267551635u,\n1306761320u,\n2220373824u,\n4109252858u,\n896322512u,\n1788337208u,\n1336556841u,\n2911512007u,\n3712582785u,\n3071359622u,\n2561488770u,\n3898950547u,\n536047554u,\n2040551642u,\n3528794619u,\n3565689036u,\n1197100813u,\n1864569342u,\n3329594980u,\n490608221u,\n1174785921u,\n3287246572u,\n2163330264u,\n500120236u,\n2520062970u,\n1561519055u,\n4042710240u,\n2774151984u,\n3160666939u,\n96459446u,\n1878067032u,\n4237425634u,\n2952135524u,\n4100839753u,\n1265237690u,\n4246879223u,\n834830418u,\n3476334357u,\n4277111759u,\n2511026735u,\n3065234219u,\n556796152u,\n198182691u,\n2913077464u,\n1535115487u,\n4046477658u,\n140762681u,\n990407433u,\n2198985327u,\n2926590471u,\n559702706u,\n82077489u,\n1096697687u,\n4190838199u,\n3046872820u,\n1583801700u,\n2185339100u,\n3912423882u,\n3703603898u,\n2540185277u,\n1446869792u,\n4051584612u,\n2719373510u,\n1675560450u,\n1996164093u,\n405251683u,\n2864244470u,\n4071581802u,\n2028708916u,\n803575837u,\n557660441u,\n3841480082u,\n255451671u,\n779434428u,\n3452203069u,\n2285701422u,\n1568745354u,\n823305654u,\n3184047862u,\n4159715581u,\n3160134214u,\n3198900547u,\n1566527339u,\n4194096960u,\n1496132623u,\n1719108984u,\n2584236470u,\n531310503u,\n3456882941u,\n3382290593u,\n467441309u,\n3241407531u,\n2540270567u,\n1397502982u,\n3348545480u,\n811750340u,\n1017047954u,\n2540585554u,\n3531646869u,\n943914610u,\n1903578924u,\n1911188923u,\n241574049u,\n3181425568u,\n3529565564u,\n240953857u,\n2964595704u,\n3828377737u,\n4260564140u,\n4262383425u,\n383233885u,\n4051263539u,\n919677938u,\n1683612329u,\n4204155962u,\n2283918569u,\n4153726847u,\n350160869u,\n1387233546u,\n1891558063u,\n740563169u,\n330624974u,\n2948665536u,\n376390582u,\n3799363969u,\n3187805406u,\n2263421398u,\n1928519266u,\n2746577402u,\n2108753646u,\n768287270u,\n2247006571u,\n212490675u,\n917121602u,\n2549835613u,\n2864033668u,\n3738062408u,\n2006922227u,\n2616619070u,\n3449066284u,\n431292293u,\n786322314u,\n1415970351u,\n3263135197u,\n2954777083u,\n3206261120u,\n2287507921u,\n1781944746u,\n4081586725u,\n1109175923u,\n1813855658u,\n1129462471u,\n1037031473u,\n3389003793u,\n3122687303u,\n1164309901u,\n3193251135u,\n3626554867u,\n3071568023u,\n3656006011u,\n1167681812u,\n3155218919u,\n2704165015u,\n1854985978u,\n1712976649u,\n878608872u,\n4155949943u,\n3163786257u,\n1626463554u,\n1256485167u,\n582664250u,\n2083771672u,\n804336148u,\n2770847216u,\n1674051445u,\n3992583643u,\n2966108111u,\n900741486u,\n4014551783u,\n300318232u,\n3517585534u,\n542270815u,\n760762191u,\n1216399252u,\n643179562u,\n3652676161u,\n2990167340u,\n3262252593u,\n2134299399u,\n411263051u,\n1342880802u,\n1967599860u,\n853593042u,\n2682611693u,\n850464484u,\n3286110054u,\n3842907484u,\n3623364733u,\n3693536939u,\n1615375832u,\n2318423400u,\n4145497671u,\n1728968857u,\n2686506989u,\n1502282913u,\n2151665147u,\n3651607391u,\n1178454681u,\n4146839064u,\n2601416506u,\n1448097974u,\n238887261u,\n4093725287u,\n2367569534u,\n679517009u,\n3539886328u,\n3086277222u,\n1390394371u,\n119173722u,\n1766260771u,\n751439914u,\n215917713u,\n2656990891u,\n1570750352u,\n3533987737u,\n3576119563u,\n963183826u,\n3796810515u,\n136547246u,\n2592925324u,\n427154472u,\n1228758574u,\n1464255968u,\n2984611177u,\n2001585786u,\n1525438381u,\n1348536411u,\n2861338018u,\n764077711u,\n3785343245u,\n457568934u,\n4104954272u,\n2381948487u,\n3148473363u,\n2180270337u,\n1387729170u,\n951677556u,\n2721005055u,\n66786703u,\n1149351924u,\n1895026827u,\n3711056516u,\n3638638708u,\n2263003308u,\n3448840877u,\n225333538u,\n3797521928u,\n3262952567u,\n2078619498u,\n1178073973u,\n3288261538u,\n1496966875u,\n2481012988u,\n114945840u,\n1632780103u,\n2087949619u,\n3787017905u,\n2575395164u,\n2971726178u,\n3642087909u,\n3894199764u,\n203853421u,\n425935223u,\n3565833278u,\n1748785729u,\n580966986u,\n2124704694u,\n1107045577u,\n1067532701u,\n1406028344u,\n18613994u,\n3476683808u,\n3762914298u,\n1844996900u,\n904215228u,\n1118521573u,\n3657647605u,\n3136157065u,\n2287683323u,\n126005630u,\n3555092974u,\n49515858u,\n1010661841u,\n1902040126u,\n1400735275u,\n2771676666u,\n2225229957u,\n3454177594u,\n2883475137u,\n4144472319u,\n1051332394u,\n542648229u,\n1669710469u,\n553041029u,\n584127807u,\n2993670925u,\n3587959456u,\n1745399498u,\n1404723176u,\n1334333531u,\n3239516985u,\n1275954779u,\n367320647u,\n3684418197u,\n4030809053u,\n484559105u,\n4255931645u,\n4271715616u,\n3171911678u,\n928543347u,\n2159512867u,\n313902234u,\n647086234u,\n577214736u,\n1130129573u,\n995791646u,\n1645086060u,\n4122335794u,\n1064648931u,\n2752145076u,\n3312498873u,\n4238535494u,\n1471227427u,\n633688562u,\n1959779970u,\n766642813u,\n1380896111u,\n3647601207u,\n1733961041u,\n521947915u,\n189164145u,\n486382294u,\n3770038872u,\n3235740744u,\n1912506671u,\n2276864677u,\n1588060152u,\n2504457929u,\n1471020554u,\n3623212998u,\n3026631806u,\n2342164722u,\n1674890530u,\n3011542850u,\n3549160092u,\n4290680005u,\n3943068002u,\n2273781461u,\n2127663659u,\n1646681121u,\n447810651u,\n2366308558u,\n970504950u,\n2008155560u,\n2695940969u,\n3444688454u,\n1739318893u,\n2683090634u,\n2774816580u,\n437560100u,\n512012738u,\n3305170944u,\n665292744u,\n3580039116u,\n1579404983u,\n3397891494u,\n710590371u,\n2514565805u,\n3624609754u,\n3516075816u,\n1314000850u,\n1935166880u,\n3257747610u,\n3776931214u,\n3183054185u,\n675129307u,\n3333261712u,\n1154611403u,\n2759854023u,\n1963228038u,\n505138315u,\n1803966773u,\n4032705384u,\n798395739u,\n3473799845u,\n476400898u,\n602972493u,\n3289878097u,\n2520311409u,\n3214794876u,\n748160407u,\n1326769504u,\n902775872u,\n1372805534u,\n1213925114u,\n3009384989u,\n3781981134u,\n2835608783u,\n2716786748u,\n1669490957u,\n1089334066u,\n250756920u,\n4041016629u,\n2495807367u,\n2008251381u,\n106212622u,\n1927268995u,\n2251978818u,\n3788056262u,\n3678660147u,\n2656772270u,\n1997584981u,\n2668998785u,\n2954162084u,\n845687881u,\n776018378u,\n2066910012u,\n918315064u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; IsAlive(farmhashnt::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashnt::Hash32(data, len++)); IsAlive(farmhashnt::Hash32(data, len++)); len -= 3; return alive > 0; }\nCheck(farmhashnt::Hash32WithSeed(data + offset, len, SEED));\nCheck(farmhashnt::Hash32(data + offset, len));\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashntTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\ncout << farmhashnt::Hash32WithSeed(data + offset, len, SEED) << \"u,\" << endl;\ncout << farmhashnt::Hash32(data + offset, len) << \"u,\" << endl;\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashntTest\n\n#if TESTING\n\n//static int farmhashntTestResult = farmhashntTest::RunTest();\nTEST(farmhash, nt) { farmhashntTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashntTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashntTest::Dump(0, i);\n  }\n  farmhashntTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashsaTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n4223616069u,\n3696677242u,\n4081014168u,\n2576519988u,\n2212771159u,\n1112731063u,\n1020067935u,\n3955445564u,\n1451961420u,\n653440099u,\n31917516u,\n2957164615u,\n2590087362u,\n3879448744u,\n176305566u,\n2447367541u,\n1359016305u,\n3363804638u,\n1117290165u,\n1062549743u,\n2437877004u,\n1894455839u,\n673206794u,\n3486923651u,\n3269862919u,\n2303349487u,\n1380660650u,\n595525107u,\n1525325287u,\n2025609358u,\n176408838u,\n1592885012u,\n864896482u,\n2101378090u,\n3489229104u,\n2118965695u,\n581644891u,\n2718789079u,\n631613207u,\n4228658372u,\n3867875546u,\n3531368319u,\n3804516756u,\n3317755099u,\n1619744564u,\n2884717286u,\n1088213445u,\n2667691076u,\n3727873235u,\n2330406762u,\n858590707u,\n123802208u,\n4150036245u,\n182283099u,\n1478882570u,\n3282617403u,\n819171187u,\n1172627392u,\n4254302102u,\n2957028020u,\n437030323u,\n2452147680u,\n2868246750u,\n3530169402u,\n3154852132u,\n215019192u,\n357580983u,\n1354454461u,\n1108813287u,\n2324008118u,\n2315997713u,\n4181601562u,\n1360882441u,\n92423273u,\n3048866755u,\n3369188505u,\n3664371439u,\n2920710428u,\n1027891570u,\n2653166430u,\n3461888315u,\n1475780447u,\n292769636u,\n1737473313u,\n4064110516u,\n4170160075u,\n762850927u,\n3630603695u,\n2803307356u,\n844987665u,\n460980967u,\n3005635467u,\n2802568977u,\n588668033u,\n2148940781u,\n3239099984u,\n1266953698u,\n3197808789u,\n3519942533u,\n2511995334u,\n2553810188u,\n871667697u,\n1358675720u,\n1499319171u,\n2044931270u,\n1210355103u,\n807152540u,\n3262320756u,\n2810214575u,\n1813386141u,\n4089465863u,\n903928165u,\n1388899322u,\n3209183659u,\n834536144u,\n2733354550u,\n2742289921u,\n3689042563u,\n2655593281u,\n4169686303u,\n415985561u,\n138892376u,\n516115393u,\n65683883u,\n4162865100u,\n889944635u,\n313566528u,\n3346420907u,\n1504303591u,\n2256809275u,\n742243229u,\n779775302u,\n3140940172u,\n2312556111u,\n2304095772u,\n1151741606u,\n2194712422u,\n1714084652u,\n3272736835u,\n1311540658u,\n191179665u,\n3996605106u,\n1657345233u,\n4205442903u,\n1553339212u,\n2351843044u,\n1647502006u,\n2525516233u,\n292202846u,\n1498646290u,\n1429323381u,\n974274898u,\n3759331561u,\n2881238887u,\n826787221u,\n1069622448u,\n221991032u,\n1462969082u,\n2799661508u,\n364022781u,\n2594244377u,\n797773898u,\n4097839290u,\n1529150125u,\n2456805570u,\n541503425u,\n3936326142u,\n3112719954u,\n775223581u,\n3074018423u,\n3198488875u,\n1772191849u,\n2456535211u,\n3154686028u,\n1520862019u,\n4005829426u,\n1306433767u,\n1943028506u,\n2246000782u,\n1057766454u,\n3761996982u,\n3441075333u,\n898641979u,\n3450209088u,\n3941329307u,\n3289922449u,\n3085075827u,\n1814193220u,\n690422997u,\n2627846676u,\n2653520704u,\n3739145533u,\n3996776010u,\n2287072592u,\n1346671698u,\n3082629900u,\n2298811274u,\n3639722036u,\n1729419228u,\n1836765953u,\n3708118742u,\n213436u,\n950223749u,\n3734247682u,\n2924575678u,\n1382024841u,\n2431637732u,\n3448846682u,\n1341301397u,\n4206956590u,\n1730650902u,\n2581075456u,\n1542359141u,\n707222542u,\n2925350541u,\n3846303536u,\n3579103295u,\n3932175763u,\n1339615732u,\n848825750u,\n1070170828u,\n1964973818u,\n577060344u,\n607721296u,\n4031023048u,\n406883794u,\n3991905552u,\n1198544082u,\n872468460u,\n1044847096u,\n3159976313u,\n3020028266u,\n2108700400u,\n3373767922u,\n264431841u,\n2817097007u,\n3700061048u,\n1733731531u,\n3459415893u,\n80378591u,\n1479875104u,\n19735612u,\n1382658977u,\n3416562245u,\n1959852842u,\n2384002344u,\n124683828u,\n3725782174u,\n2300301222u,\n393852269u,\n1302492002u,\n3623776492u,\n3787086417u,\n1730024749u,\n1710531361u,\n443700716u,\n1461987482u,\n671998131u,\n3018380746u,\n2592292305u,\n3390799372u,\n3945101155u,\n3743494852u,\n3716045582u,\n996005166u,\n320698449u,\n3420221765u,\n1518157951u,\n2555810666u,\n3381929684u,\n2019638523u,\n3088262796u,\n2072178906u,\n3433649364u,\n203906916u,\n34663784u,\n290301305u,\n1188021504u,\n3754681145u,\n3920313139u,\n2840496520u,\n1656802962u,\n2288475489u,\n3399185138u,\n1296000826u,\n2362384746u,\n309633360u,\n2719851778u,\n776035930u,\n3200733043u,\n365690832u,\n3326378243u,\n1500331457u,\n1625708592u,\n4230903462u,\n715344888u,\n3363777768u,\n2243620288u,\n2890765789u,\n553154234u,\n4044100108u,\n4056887320u,\n1185656496u,\n3671476744u,\n1064586897u,\n1154949698u,\n3493481974u,\n1294573722u,\n1869224012u,\n2530084956u,\n995321553u,\n833419249u,\n563815282u,\n250258043u,\n2970801822u,\n441007535u,\n42246961u,\n2820426655u,\n2878882436u,\n2363245780u,\n2138489282u,\n2972360481u,\n2312619393u,\n3598664848u,\n3071556076u,\n776990325u,\n3220427357u,\n2257939577u,\n3817305903u,\n1502979698u,\n3159755934u,\n3955997276u,\n2423850008u,\n1959927572u,\n1219782288u,\n4119776679u,\n1124253854u,\n3678052422u,\n2620644947u,\n1262408666u,\n3480072280u,\n2627137665u,\n807538749u,\n3276646337u,\n518510128u,\n1137828655u,\n1498449110u,\n3031692317u,\n1125635969u,\n1130096111u,\n780007336u,\n3111856399u,\n1014917264u,\n780877352u,\n2909458336u,\n4235949214u,\n2423879289u,\n275888892u,\n3891926795u,\n3538163953u,\n54815161u,\n162228302u,\n258154068u,\n3554455591u,\n1801469029u,\n2801563220u,\n726560058u,\n2450221940u,\n3677582978u,\n440993800u,\n424762443u,\n2624525253u,\n2587715329u,\n2292264424u,\n1074856749u,\n3294752007u,\n3164112672u,\n2399146799u,\n1920182465u,\n3858835361u,\n193755240u,\n3333610311u,\n1757504059u,\n2576027039u,\n2775253365u,\n2939191561u,\n1046147275u,\n235149906u,\n4262218222u,\n2900542726u,\n2260154702u,\n1019551635u,\n1194720570u,\n3519118691u,\n3039483153u,\n84918216u,\n3053381097u,\n2572396843u,\n3849763371u,\n2782686780u,\n3710049554u,\n3403430713u,\n2346080784u,\n2496307442u,\n1597281872u,\n696018239u,\n704625714u,\n623026921u,\n3182413559u,\n3794540330u,\n305497722u,\n1592680199u,\n2377854072u,\n3060601746u,\n3953057908u,\n3941551588u,\n1033716182u,\n2765716854u,\n1309699058u,\n3519400181u,\n3073370877u,\n115583008u,\n4032909296u,\n2944563574u,\n3762753718u,\n192842727u,\n1711348701u,\n3086147235u,\n1658229443u,\n1479783872u,\n3839977157u,\n225619117u,\n1349684817u,\n1964813173u,\n565753187u,\n2530252046u,\n840014353u,\n1645183704u,\n3668429078u,\n3438418557u,\n639704059u,\n360837811u,\n2531807958u,\n1572353913u,\n2116037299u,\n1948437512u,\n744553393u,\n2380697034u,\n3775234105u,\n3816065157u,\n301868653u,\n2960939561u,\n3306528247u,\n2389296549u,\n805918610u,\n1759358265u,\n1760876328u,\n2827601706u,\n2944594708u,\n3313666458u,\n2022601495u,\n730938791u,\n193539397u,\n2026103244u,\n802928398u,\n2630934308u,\n782805818u,\n3499326016u,\n293509489u,\n3646131514u,\n3182478647u,\n854800333u,\n2284531628u,\n438528022u,\n2339298129u,\n1692289216u,\n2427728723u,\n46501288u,\n350652353u,\n1355971222u,\n889682372u,\n944799254u,\n2763906061u,\n2807550612u,\n2683762637u,\n100870317u,\n2449357318u,\n2638348436u,\n4206088869u,\n1788948473u,\n3537588549u,\n2782490204u,\n134406470u,\n2409190528u,\n2362439849u,\n1861661528u,\n2101513194u,\n1424834765u,\n3581765745u,\n3185999525u,\n2057487100u,\n2303941176u,\n3639628788u,\n1180265315u,\n230437935u,\n2108319366u,\n1131685143u,\n1055685292u,\n1509007009u,\n1258485140u,\n560525005u,\n3598799040u,\n3835680585u,\n1851859628u,\n332858996u,\n641769248u,\n4252450037u,\n865386707u,\n720719117u,\n3133612164u,\n3833045874u,\n3492515435u,\n2465970289u,\n4234420011u,\n573859916u,\n252532886u,\n870392318u,\n4051320920u,\n894929092u,\n3748361688u,\n699355960u,\n1885212350u,\n1609756949u,\n461896870u,\n1337065461u,\n1775211059u,\n1786193749u,\n2815154643u,\n2128729882u,\n969639529u,\n3960427545u,\n859416958u,\n2739758802u,\n2698032197u,\n2813292418u,\n1985467524u,\n396604317u,\n4122172759u,\n1201259789u,\n4282051702u,\n3270018895u,\n961215209u,\n961075860u,\n4211926998u,\n4088374597u,\n577510509u,\n3058349487u,\n4025377754u,\n2815478438u,\n471023164u,\n3947959608u,\n4161486934u,\n2299888461u,\n1103571511u,\n2450153872u,\n1839939275u,\n108299608u,\n858086440u,\n1030152945u,\n3895328530u,\n3009080718u,\n3690840454u,\n3847025277u,\n152331362u,\n161365689u,\n831319961u,\n2166017294u,\n3945322722u,\n4059970216u,\n1420824131u,\n2770648308u,\n1567250186u,\n2181067149u,\n1939743488u,\n3080158120u,\n3435218248u,\n2495237495u,\n3814085102u,\n3180983013u,\n3199054292u,\n2204745908u,\n1140337267u,\n2213569784u,\n1941879842u,\n2105562605u,\n3618835614u,\n2247103645u,\n2492473487u,\n856414299u,\n166022030u,\n4080104712u,\n3218935344u,\n3284220561u,\n4261581452u,\n1206944836u,\n3496705432u,\n2215996876u,\n3154627465u,\n3384005496u,\n742170556u,\n1333047620u,\n802680366u,\n156833431u,\n2682100354u,\n2493654830u,\n584848366u,\n1691693131u,\n2169934170u,\n779968026u,\n2099545800u,\n1423039695u,\n4292110968u,\n4266576788u,\n149142597u,\n748501873u,\n3865014822u,\n1913588198u,\n130285614u,\n3500768879u,\n915458923u,\n3071792750u,\n1339986633u,\n4143929149u,\n4048379479u,\n725193827u,\n1375113643u,\n2425277412u,\n4144659274u,\n465714768u,\n226991589u,\n2212127704u,\n3936145258u,\n2891024846u,\n3816000225u,\n979331165u,\n1749907536u,\n53847318u,\n1462525833u,\n2961425455u,\n368859113u,\n3572721452u,\n453048644u,\n1628629918u,\n3497673923u,\n3619079585u,\n139870565u,\n1518176798u,\n3933074281u,\n1878623729u,\n2074035641u,\n3016759257u,\n1313053591u,\n2557706970u,\n2348296582u,\n962370022u,\n2337285014u,\n1618936717u,\n1915877085u,\n2743743122u,\n3250783882u,\n1346652536u,\n143311109u,\n2443788461u,\n1048248964u,\n2806619339u,\n3263266976u,\n1668146349u,\n3397428868u,\n3276188862u,\n1774196343u,\n1993847813u,\n2771079610u,\n476672419u,\n2119050359u,\n2918326659u,\n2245402721u,\n2692910474u,\n2374383269u,\n342400227u,\n2961437795u,\n3899230368u,\n337787132u,\n3664444935u,\n1269451153u,\n2971526729u,\n1486511182u,\n791070133u,\n2570319890u,\n3482497490u,\n2134230518u,\n4273391202u,\n1825511330u,\n3947753714u,\n1389755724u,\n3995075516u,\n2081052615u,\n3626343470u,\n4213603435u,\n2137917278u,\n2898987303u,\n3059215715u,\n3383237881u,\n3003674434u,\n409174425u,\n1911915604u,\n2087728055u,\n2942005882u,\n3386522440u,\n714936074u,\n261924004u,\n3268784033u,\n1141188757u,\n2413217552u,\n1515163433u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; IsAlive(farmhashsa::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashsa::Hash32(data, len++)); IsAlive(farmhashsa::Hash32(data, len++)); len -= 3; return alive > 0; }\nCheck(farmhashsa::Hash32WithSeed(data + offset, len, SEED));\nCheck(farmhashsa::Hash32(data + offset, len));\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashsaTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\ncout << farmhashsa::Hash32WithSeed(data + offset, len, SEED) << \"u,\" << endl;\ncout << farmhashsa::Hash32(data + offset, len) << \"u,\" << endl;\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashsaTest\n\n#if TESTING\n\n//static int farmhashsaTestResult = farmhashsaTest::RunTest();\nTEST(farmhash, sa) { farmhashsaTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashsaTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashsaTest::Dump(0, i);\n  }\n  farmhashsaTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashsuTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n4223616069u,\n3696677242u,\n4081014168u,\n2576519988u,\n2212771159u,\n1112731063u,\n1020067935u,\n3955445564u,\n1451961420u,\n653440099u,\n31917516u,\n2957164615u,\n2590087362u,\n3879448744u,\n176305566u,\n2447367541u,\n1359016305u,\n3363804638u,\n1117290165u,\n1062549743u,\n2437877004u,\n1894455839u,\n673206794u,\n3486923651u,\n3269862919u,\n2303349487u,\n1380660650u,\n595525107u,\n1525325287u,\n2025609358u,\n176408838u,\n1592885012u,\n864896482u,\n2101378090u,\n3489229104u,\n2118965695u,\n581644891u,\n2718789079u,\n631613207u,\n4228658372u,\n3867875546u,\n3531368319u,\n3804516756u,\n3317755099u,\n1619744564u,\n2884717286u,\n1088213445u,\n2667691076u,\n3727873235u,\n2330406762u,\n858590707u,\n457744844u,\n4150036245u,\n2000404290u,\n1478882570u,\n901678172u,\n819171187u,\n195942998u,\n4254302102u,\n3967266927u,\n437030323u,\n4018009204u,\n2868246750u,\n3540087514u,\n3154852132u,\n3319116625u,\n357580983u,\n3177665294u,\n1108813287u,\n1253366798u,\n2315997713u,\n510718750u,\n1360882441u,\n2770216279u,\n3048866755u,\n3406961221u,\n3664371439u,\n1151145514u,\n1027891570u,\n2699067992u,\n3461888315u,\n198061905u,\n292769636u,\n1106771795u,\n4064110516u,\n3258279756u,\n762850927u,\n1818699721u,\n2803307356u,\n3919169404u,\n460980967u,\n3125535078u,\n2802568977u,\n3582546426u,\n2148940781u,\n3963274378u,\n1266953698u,\n204185123u,\n1100034381u,\n3009193601u,\n4200651967u,\n274889605u,\n2700589508u,\n952511689u,\n3765324859u,\n3465498478u,\n4014967037u,\n2070988082u,\n2972423530u,\n3068638223u,\n4156773651u,\n489509804u,\n1323863238u,\n3731914806u,\n2846098469u,\n2728930632u,\n346814072u,\n848146907u,\n551160669u,\n4165126521u,\n2039095001u,\n4179859388u,\n2434936359u,\n2764414551u,\n238491210u,\n732483969u,\n3366512764u,\n478307468u,\n4124179572u,\n4142733597u,\n1953448206u,\n4199329278u,\n865077060u,\n2627662116u,\n2802499360u,\n3141206831u,\n1959218197u,\n911371451u,\n125987200u,\n2821366175u,\n2530992747u,\n2409206225u,\n117991880u,\n2133402461u,\n895510531u,\n428719601u,\n3036014536u,\n1223783733u,\n733793540u,\n970650405u,\n547701766u,\n570764615u,\n3224485368u,\n3192714940u,\n319942831u,\n3940200341u,\n362056204u,\n2832368105u,\n1853281226u,\n3296434636u,\n3752508307u,\n604292768u,\n2231940616u,\n1204094681u,\n866194005u,\n2405201650u,\n2466384396u,\n380829379u,\n230033818u,\n2783417588u,\n4249886729u,\n829569301u,\n2988322580u,\n2299983554u,\n74748560u,\n737514425u,\n3153050211u,\n652642663u,\n1270205115u,\n227197032u,\n2773091790u,\n325849216u,\n49998791u,\n4043203010u,\n3662748068u,\n1709364383u,\n1179105165u,\n1478504366u,\n2980456610u,\n1167476429u,\n1590390732u,\n1306256496u,\n292008135u,\n374690995u,\n1809200819u,\n1680595904u,\n646040226u,\n1742445560u,\n2435776844u,\n3703683804u,\n478742495u,\n814967947u,\n2698190177u,\n1003617993u,\n1436118705u,\n217056304u,\n1412287094u,\n2738417466u,\n2933279339u,\n3461877733u,\n1203141205u,\n2119492857u,\n1134895723u,\n1560001021u,\n3786320122u,\n3748116258u,\n3486219595u,\n702138030u,\n1062984182u,\n232789133u,\n1566523968u,\n3885443778u,\n1820171888u,\n3655858585u,\n2316903005u,\n2678779620u,\n395625433u,\n1609107564u,\n3108726411u,\n2937837224u,\n3911907151u,\n557272509u,\n3893435978u,\n1542613576u,\n1079886893u,\n2624566322u,\n1413700616u,\n2796974006u,\n1922556114u,\n562820464u,\n2845409784u,\n54180312u,\n1898782464u,\n3681814953u,\n2417064617u,\n1815464483u,\n911626132u,\n2964575550u,\n1852696128u,\n2319647785u,\n1998904590u,\n619992689u,\n3073207513u,\n1238163512u,\n3199435982u,\n828667254u,\n3561155502u,\n3943095163u,\n1045711849u,\n2238679131u,\n2114975398u,\n713808403u,\n3871787494u,\n2572031161u,\n2360934075u,\n2337781107u,\n262596504u,\n693836699u,\n2129369850u,\n3543189427u,\n962205222u,\n3685581020u,\n692974477u,\n725182211u,\n646123906u,\n2368836544u,\n2505872733u,\n1999977610u,\n1639885802u,\n1475058032u,\n207023609u,\n2773581234u,\n3524857793u,\n3433371102u,\n3243027613u,\n1787668353u,\n985757946u,\n3896012929u,\n702356957u,\n3559331129u,\n884084870u,\n4009998120u,\n648888720u,\n1403349048u,\n1624342778u,\n1766674171u,\n2518582204u,\n3251243146u,\n792751003u,\n1377201813u,\n3629686054u,\n1583734324u,\n3647107626u,\n4258564381u,\n1469878609u,\n1940598241u,\n2755003690u,\n1907120418u,\n109916701u,\n775347954u,\n2090960874u,\n611281803u,\n3470490146u,\n3301663253u,\n1835412158u,\n1803066146u,\n591872433u,\n550703713u,\n1495089683u,\n826492808u,\n817200035u,\n4177474571u,\n688070143u,\n971427632u,\n1442499481u,\n3568640348u,\n2789993738u,\n85808128u,\n2058346726u,\n394058570u,\n3466511434u,\n318905230u,\n4149248030u,\n415308316u,\n165997598u,\n1219639412u,\n1648022659u,\n2857432523u,\n1422508004u,\n468095522u,\n296968649u,\n430250611u,\n1775562314u,\n2976361671u,\n1040036362u,\n1372510167u,\n292746272u,\n3408238954u,\n626061886u,\n1317637569u,\n1237775792u,\n1218490455u,\n2224234499u,\n590942419u,\n713995643u,\n3541889330u,\n4140218960u,\n3529791107u,\n354462673u,\n842607274u,\n365048533u,\n2638303414u,\n3560458014u,\n31621379u,\n4210854794u,\n1273118792u,\n2572743762u,\n3513175801u,\n402066986u,\n602524471u,\n565029192u,\n180576438u,\n1288605959u,\n2896244423u,\n1420543484u,\n1329862227u,\n1791567324u,\n4248690247u,\n12917038u,\n3483481310u,\n2082050731u,\n1611921143u,\n2443766548u,\n2216338811u,\n2528006095u,\n2984009021u,\n674210884u,\n2857608106u,\n2155534809u,\n1023105067u,\n2968955846u,\n3303624302u,\n2502112850u,\n245749006u,\n3175229091u,\n3342796184u,\n3613785362u,\n1614168851u,\n2582149283u,\n895403488u,\n416205023u,\n3792242000u,\n529397534u,\n299415203u,\n4284673348u,\n2096851282u,\n1864524731u,\n2012577738u,\n3426363316u,\n1387308508u,\n1143610148u,\n2027467219u,\n3772856163u,\n3453862623u,\n2661437174u,\n2047145955u,\n2533381447u,\n2059534115u,\n439426587u,\n1537543414u,\n2384289877u,\n3174229055u,\n2658017753u,\n2293148474u,\n2359450158u,\n3930242475u,\n1510302397u,\n3354288821u,\n920095603u,\n2415746928u,\n2729472638u,\n2261143371u,\n848667611u,\n919157153u,\n3322393117u,\n4103299943u,\n413569608u,\n68911216u,\n3334990170u,\n1228068652u,\n1570056373u,\n1905477543u,\n2622302276u,\n2935063895u,\n3224810004u,\n4211768578u,\n828688131u,\n3556122839u,\n1930935348u,\n2605825202u,\n1540993970u,\n3209115883u,\n122847500u,\n665638794u,\n506571051u,\n2691795295u,\n3996966556u,\n714660621u,\n3662432239u,\n470651837u,\n1807432621u,\n3755290953u,\n359878860u,\n2793081615u,\n4065031431u,\n904653062u,\n2317800777u,\n568501094u,\n3492871707u,\n2738806116u,\n2883859610u,\n3242080257u,\n364246691u,\n3601786516u,\n3159362524u,\n1578272201u,\n1283574375u,\n2912186103u,\n2256279032u,\n1540671086u,\n2356088973u,\n2892277779u,\n3441449267u,\n2225005503u,\n3846428419u,\n2014549218u,\n2290734767u,\n2126684614u,\n4235463487u,\n3811556204u,\n174739661u,\n767525888u,\n47684458u,\n4211168099u,\n889063422u,\n469864411u,\n767407110u,\n413337343u,\n1618456644u,\n2814499820u,\n2401124192u,\n632089437u,\n1234980238u,\n1288585402u,\n3153169944u,\n2917822069u,\n1843320264u,\n3794359132u,\n3074573530u,\n258629454u,\n3813357060u,\n3806887248u,\n1665524736u,\n3324533324u,\n3005091922u,\n793108368u,\n1529669805u,\n2332660395u,\n2217730223u,\n2634687611u,\n442806463u,\n1968135266u,\n454523002u,\n3177866230u,\n2808960136u,\n4259114138u,\n4103264843u,\n3103714075u,\n2462967542u,\n1466891491u,\n477973764u,\n834565647u,\n741089037u,\n218837573u,\n1710536528u,\n2469088212u,\n1229072375u,\n2828341u,\n176923431u,\n985763350u,\n4095477420u,\n1984145538u,\n1870791084u,\n674956677u,\n1978138947u,\n1296493993u,\n1818183554u,\n3443333721u,\n2124949983u,\n2549590262u,\n2700850794u,\n2662736367u,\n739638109u,\n4061447096u,\n2960078422u,\n2453781158u,\n929570940u,\n3200328383u,\n2406328791u,\n1419180666u,\n2152455739u,\n2805741044u,\n3305999074u,\n3183816361u,\n2303165050u,\n4922104u,\n63096005u,\n936656347u,\n3104453886u,\n1088673880u,\n1113407526u,\n1457890086u,\n453478383u,\n1107686695u,\n3626027824u,\n1159687359u,\n2248467888u,\n2004578380u,\n3274954621u,\n1787958646u,\n2628726704u,\n1138419798u,\n3735442315u,\n692385301u,\n313807213u,\n2329068673u,\n59375364u,\n3261084359u,\n2088644507u,\n2471153194u,\n788336435u,\n4024527246u,\n141504460u,\n2307553888u,\n1930559950u,\n48975711u,\n2745693338u,\n230161982u,\n3429230862u,\n1335968626u,\n609591304u,\n57435073u,\n4279281136u,\n3152151665u,\n3984484924u,\n3459883943u,\n397478330u,\n1738762229u,\n3033590066u,\n3611539498u,\n1363463523u,\n3319364965u,\n2671169141u,\n3819548561u,\n1691193757u,\n2423834608u,\n2820147055u,\n1378120632u,\n1240565187u,\n3180720050u,\n680831086u,\n3309658414u,\n1986166490u,\n762099827u,\n510883662u,\n2047373648u,\n3606742294u,\n3894965352u,\n2342078853u,\n1091255717u,\n776594727u,\n3217317445u,\n1574468485u,\n3844504016u,\n2819598918u,\n1037401010u,\n2550943503u,\n3867184001u,\n1687911772u,\n165313836u,\n1679575281u,\n2418947263u,\n2038774952u,\n3913543652u,\n3209155736u,\n149905221u,\n3859604717u,\n713919631u,\n4069810796u,\n1882959164u,\n1019939034u,\n2379867302u,\n3666323035u,\n1157389013u,\n2422300650u,\n3366777340u,\n2526452062u,\n1313747885u,\n1039617868u,\n1620553692u,\n2032976978u,\n578789528u,\n1592846839u,\n2270630604u,\n897850577u,\n1603294178u,\n3105664807u,\n1442670138u,\n1728019360u,\n79313861u,\n1683031101u,\n1913067024u,\n4070719870u,\n708986470u,\n2586453359u,\n3993348863u,\n3358251279u,\n3003552537u,\n750174793u,\n836888956u,\n4190747426u,\n4251291318u,\n4145164938u,\n1366883260u,\n1912910955u,\n510192669u,\n1851315039u,\n3574241274u,\n3220062924u,\n2821142039u,\n1317082195u,\n2274293302u,\n1839219569u,\n126586168u,\n3989293643u,\n2680178207u,\n347056948u,\n799681430u,\n2864517481u,\n3180404853u,\n213140045u,\n1956305184u,\n1474675286u,\n3085723423u,\n2841859626u,\n308421914u,\n3670309263u,\n1765052231u,\n245459238u,\n113434331u,\n4079521092u,\n2115235526u,\n2943408816u,\n1055476938u,\n1506442339u,\n2291296392u,\n3267864332u,\n1282145528u,\n3700108015u,\n1932843667u,\n2677701670u,\n6041177u,\n3889648557u,\n1461025478u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; IsAlive(farmhashsu::Hash32WithSeed(data, len++, SEED)); IsAlive(farmhashsu::Hash32(data, len++)); IsAlive(farmhashsu::Hash32(data, len++)); len -= 3; return alive > 0; }\nCheck(farmhashsu::Hash32WithSeed(data + offset, len, SEED));\nCheck(farmhashsu::Hash32(data + offset, len));\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashsuTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\ncout << farmhashsu::Hash32WithSeed(data + offset, len, SEED) << \"u,\" << endl;\ncout << farmhashsu::Hash32(data + offset, len) << \"u,\" << endl;\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashsuTest\n\n#if TESTING\n\n//static int farmhashsuTestResult = farmhashsuTest::RunTest();\nTEST(farmhash, su) { farmhashsuTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashsuTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashsuTest::Dump(0, i);\n  }\n  farmhashsuTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashteTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n1140953930u, 861465670u,\n3277735313u, 2681724312u,\n2598464059u, 797982799u,\n890626835u, 800175912u,\n2603993599u, 921001710u,\n1410420968u, 2134990486u,\n3283896453u, 1867689945u,\n2914424215u, 2244477846u,\n255297188u, 2992121793u,\n1110588164u, 4186314283u,\n161451183u, 3943596029u,\n4019337850u, 452431531u,\n283198166u, 2741341286u,\n3379021470u, 2557197665u,\n299850021u, 2532580744u,\n452473466u, 1706958772u,\n1298374911u, 3099673830u,\n2199864459u, 3696623795u,\n236935126u, 2976578695u,\n4055299123u, 3281581178u,\n1053458494u, 1882212500u,\n2305012065u, 2169731866u,\n3456121707u, 275903667u,\n458884671u, 3033004529u,\n3058973506u, 2379411653u,\n1898235244u, 1402319660u,\n2700149065u, 2699376854u,\n147814787u, 720739346u,\n2433714046u, 4222949502u,\n4220361840u, 1712034059u,\n3425469811u, 3690733394u,\n4148372108u, 1330324210u,\n594028478u, 2921867846u,\n1635026870u, 192883107u,\n780716741u, 1728752234u,\n3280331829u, 326029180u,\n3969463346u, 1436364519u,\n393215742u, 3349570000u,\n3824583307u, 1612122221u,\n2859809759u, 3808705738u,\n1379537552u, 1646032583u,\n2233466664u, 1432476832u,\n4023053163u, 2650381482u,\n2052294713u, 3552092450u,\n1628777059u, 1499109081u,\n3476440786u, 3829307897u,\n2960536756u, 1554038301u,\n1145519619u, 3190844552u,\n2902102606u, 3600725550u,\n237495366u, 540224401u,\n65721842u, 489963606u,\n1448662590u, 397635823u,\n1596489240u, 1562872448u,\n1790705123u, 2128624475u,\n180854224u, 2604346966u,\n1435705557u, 1262831810u,\n155445229u, 1672724608u,\n1669465176u, 1341975128u,\n663607706u, 2077310004u,\n3610042449u, 1911523866u,\n1043692997u, 1454396064u,\n2563776023u, 294527927u,\n1099072299u, 1389770549u,\n703505868u, 678706990u,\n2952353448u, 2026137563u,\n3603803785u, 629449419u,\n1933894405u, 3043213226u,\n226132789u, 2489287368u,\n1552847036u, 645684964u,\n3828089804u, 3632594520u,\n187883449u, 230403464u,\n3151491850u, 3272648435u,\n3729087873u, 1303930448u,\n2002861219u, 165370827u,\n916494250u, 1230085527u,\n3103338579u, 3064290191u,\n3807265751u, 3628174014u,\n231181488u, 851743255u,\n2295806711u, 1781190011u,\n2988893883u, 1554380634u,\n1142264800u, 3667013118u,\n1968445277u, 315203929u,\n2638023604u, 2290487377u,\n732137533u, 1909203251u,\n440398219u, 1891630171u,\n1380301172u, 1498556724u,\n4072067757u, 4165088768u,\n4204318635u, 441430649u,\n3931792696u, 197618179u,\n956300927u, 914413116u,\n3010839769u, 2837339569u,\n2148126371u, 1913303225u,\n3074915312u, 3117299654u,\n4139181436u, 2993479124u,\n3178848746u, 1357272220u,\n1438494951u, 507436733u,\n667183474u, 2084369203u,\n3854939912u, 1413396341u,\n126024219u, 146044391u,\n1016656857u, 3022024459u,\n3254014218u, 429095991u,\n990500595u, 3056862311u,\n985653208u, 1718653828u,\n623071693u, 366414107u,\n1771289760u, 2293458109u,\n3047342438u, 2991127487u,\n3120876698u, 1684583131u,\n3638043310u, 1170404994u,\n863214540u, 1087193030u,\n199124911u, 520792961u,\n3169775996u, 1577421232u,\n3331828431u, 1013201099u,\n1716848157u, 4033596884u,\n1770708857u, 4229339322u,\n1146169032u, 1434258493u,\n3824360466u, 3242407770u,\n1926419493u, 2649785113u,\n872586426u, 762243036u,\n2736953692u, 816692935u,\n1571283333u, 3555213933u,\n2266795890u, 3781899767u,\n4290630595u, 517646945u,\n3006163611u, 2180594090u,\n959214578u, 558910384u,\n1283799121u, 3047062993u,\n3830962609u, 2391606125u,\n3544509313u, 622325861u,\n834785312u, 382936554u,\n1421463872u, 788479970u,\n1825135056u, 2725923798u,\n580988377u, 2826990641u,\n247825043u, 3167748333u,\n812546227u, 2506885666u,\n2584372201u, 1758123094u,\n1891789696u, 389974094u,\n345313518u, 2022370576u,\n3886113119u, 3338548567u,\n1083486947u, 2583576230u,\n1776047957u, 1771384107u,\n3604937815u, 3198590202u,\n3027522813u, 4155628142u,\n4232136669u, 427759438u,\n4244322689u, 542201663u,\n1549591985u, 2856634168u,\n556609672u, 45845311u,\n1175961330u, 3948351189u,\n4165739882u, 4194218315u,\n1634635545u, 4151937410u,\n713127376u, 1467786451u,\n1327394015u, 2743592929u,\n2638154051u, 810082938u,\n3077742128u, 1062268187u,\n4084325664u, 3810665822u,\n3735739145u, 2794294783u,\n2335576331u, 2560479831u,\n690240711u, 997658837u,\n2442302747u, 3948961926u,\n3958366652u, 3067277639u,\n2059157774u, 1211737169u,\n1516711748u, 2339636583u,\n4188504038u, 59581167u,\n2767897792u, 1389679610u,\n2658147000u, 2643979752u,\n3758739543u, 4189944477u,\n1454470782u, 100876854u,\n2995362413u, 118817200u,\n3252925478u, 2062343506u,\n2804483644u, 3088828656u,\n1231633714u, 4168280671u,\n2931588131u, 3284356565u,\n1255909792u, 3130054947u,\n4173605289u, 1407328702u,\n1677744031u, 3532596884u,\n3162657845u, 3887208531u,\n2256541290u, 3459463480u,\n3740979556u, 259034107u,\n392987633u, 3233195759u,\n3606709555u, 3424793077u,\n315836068u, 3200749877u,\n4065431359u, 760633989u,\n2982018998u, 1811050648u,\n234531934u, 1115203611u,\n3897494162u, 1516407838u,\n1603559457u, 323296368u,\n2632963283u, 1778459926u,\n2879836826u, 2146672889u,\n3486330348u, 492621815u,\n1231665285u, 2457048126u,\n3438440082u, 2217471853u,\n3355404249u, 3275550588u,\n1052645068u, 862072556u,\n4110617119u, 3745267835u,\n2657392572u, 4279236653u,\n1688445808u, 701920051u,\n956734128u, 581695350u,\n3157862788u, 2585726058u,\n1192588249u, 1410111809u,\n1651193125u, 3326135446u,\n1073280453u, 97376972u,\n2513844237u, 2187968410u,\n3976859649u, 4267859263u,\n3429034542u, 564493077u,\n3000537321u, 479241367u,\n3845637831u, 2868987960u,\n51544337u, 1029173765u,\n393624922u, 704325635u,\n2357610553u, 1418509533u,\n2007814586u, 3866658271u,\n3082385053u, 735688735u,\n916110004u, 3283299459u,\n1051684175u, 1083796807u,\n4074716319u, 813690332u,\n144264390u, 1439630796u,\n1508556987u, 675582689u,\n3748881891u, 3195309868u,\n362884708u, 1616408198u,\n43233176u, 837301135u,\n881504822u, 3254795114u,\n1385506591u, 2799925823u,\n1469874582u, 3464841997u,\n497175391u, 3929484338u,\n3975771289u, 1798536177u,\n2926265846u, 1374242438u,\n3675707838u, 4205965408u,\n3153165629u, 1499475160u,\n187287713u, 548490821u,\n3255259608u, 4247675634u,\n1940181471u, 3779953975u,\n687167150u, 2319566715u,\n1742785722u, 785893184u,\n2296977392u, 2778575413u,\n1794720651u, 48131484u,\n4084891412u, 1160134629u,\n3737623280u, 823113169u,\n3423207646u, 3803213486u,\n710625654u, 4116162021u,\n3693420287u, 4167766971u,\n1666602807u, 295320990u,\n3513255468u, 2487440590u,\n234080704u, 4004655503u,\n2971762528u, 1479656873u,\n4090178629u, 4044418876u,\n391947536u, 1462554406u,\n3909295855u, 1239580330u,\n1515601363u, 2011102035u,\n1442068334u, 4265993528u,\n1191921695u, 2291355695u,\n4257172787u, 576405853u,\n314332944u, 4038839101u,\n55559918u, 2378985842u,\n711098718u, 2425317635u,\n1644327317u, 1401013391u,\n4193760037u, 2958260436u,\n3167371443u, 3062418115u,\n3800755475u, 3167030094u,\n3489648204u, 1405430357u,\n526177822u, 2602636307u,\n915406019u, 4264167741u,\n1484090483u, 3070944737u,\n254529415u, 4017058800u,\n1702710265u, 1029665228u,\n2000382906u, 3185573940u,\n1381258384u, 4036354071u,\n2900841028u, 2670703363u,\n2921748807u, 2899069938u,\n4130543625u, 688472265u,\n4186808827u, 1054670286u,\n1132985391u, 2840525968u,\n4175776103u, 338058159u,\n1735964501u, 1539305024u,\n3497121710u, 1568260669u,\n2227290760u, 146827036u,\n3977176001u, 4060134777u,\n857488494u, 250055052u,\n4284109679u, 2502815838u,\n2592281721u, 1603444633u,\n1390562014u, 1556658131u,\n616327404u, 2448966429u,\n3051191726u, 3891353218u,\n1213304082u, 762328245u,\n2239052397u, 1082330589u,\n2455957292u, 201837927u,\n405397452u, 3079886794u,\n2583939798u, 2848283092u,\n3750724631u, 883849006u,\n3204198988u, 3341327098u,\n1855234968u, 1982110346u,\n1485529487u, 541496720u,\n4117290321u, 3607433551u,\n2168864636u, 133643215u,\n1055817409u, 3847827123u,\n2960769387u, 4046101649u,\n1176127003u, 4015671361u,\n4243643405u, 2849988118u,\n517111221u, 1796672358u,\n2045051700u, 3452457457u,\n2948254999u, 2102063419u,\n1556410577u, 1536380876u,\n3776661467u, 3281002516u,\n1735616066u, 1539151988u,\n1087795162u, 3332431596u,\n685631442u, 1147951686u,\n95237878u, 2005032160u,\n4012206915u, 4224354805u,\n3204999386u, 2415262714u,\n1433635018u, 116647396u,\n83167836u, 2881562655u,\n2729416454u, 1029284767u,\n881378302u, 2159170082u,\n555057366u, 1169104445u,\n3963877000u, 1919171906u,\n336034862u, 2017579106u,\n4059340529u, 3020819343u,\n865146997u, 2473524405u,\n944743644u, 1694443528u,\n1804513294u, 2904752429u,\n617975720u, 3671562289u,\n260177668u, 505662155u,\n1885941445u, 2504509403u,\n2260041112u, 1019936943u,\n3722741628u, 1511077569u,\n3100701179u, 1379422864u,\n1535670711u, 773792826u,\n1103819072u, 2089123665u,\n1157547425u, 329152940u,\n4142587430u, 484732447u,\n2475035432u, 1120017626u,\n412145504u, 965125959u,\n324924679u, 2809286837u,\n2842141483u, 4029205195u,\n2974306813u, 515627448u,\n3791551981u, 1097806406u,\n3873078673u, 136118734u,\n1872130856u, 3632422367u,\n3574135531u, 4017075736u,\n1699452298u, 1403506686u,\n344414660u, 1189129691u,\n3487080616u, 1516736273u,\n1805475756u, 2562064338u,\n163335594u, 2732147834u,\n4077452507u, 2984955003u,\n4271866024u, 3071338162u,\n2347111903u, 873829983u,\n1948409509u, 1923531348u,\n459509140u, 771592405u,\n1750124750u, 2334938333u,\n213811117u, 2586632018u,\n185232757u, 4032960199u,\n2447383637u, 284777551u,\n1654276320u, 2687561076u,\n3512945009u, 308584855u,\n1861027147u, 4102279334u,\n3203802620u, 1692079268u,\n4250142168u, 2565680167u,\n1507046104u, 841195925u,\n520565830u, 3674576684u,\n38924274u, 3770488806u,\n2414430882u, 3978473838u,\n3703994407u, 69201295u,\n3099963860u, 1255084262u,\n690971838u, 3539996781u,\n3696902571u, 3593730713u,\n2363435042u, 54945052u,\n1785765213u, 184911581u,\n1586241476u, 1939595371u,\n2534883189u, 2432427547u,\n2374171993u, 2039128933u,\n2955715987u, 2295501078u,\n2741583197u, 1280920000u,\n686818699u, 1238742497u,\n3843660102u, 82177963u,\n1281043691u, 1121403845u,\n1697846708u, 284852964u,\n278661677u, 2889101923u,\n2127558730u, 713121337u,\n872502474u, 511142139u,\n1261140657u, 1747052377u,\n2108187161u, 927011680u,\n955328267u, 3821994995u,\n2707230761u, 4142246789u,\n4134691985u, 1958963937u,\n2498463509u, 1977988705u,\n1419293714u, 1636932722u,\n2567532373u, 4075249328u,\n240575705u, 1956681213u,\n2598802768u, 2025886508u,\n4104757832u, 3026358429u,\n3242615202u, 4026813725u,\n255108733u, 1845587644u,\n3573008472u, 3615577014u,\n1222733548u, 1205557630u,\n917608574u, 1363253259u,\n1541946015u, 3087190425u,\n1138008081u, 1444019663u,\n109793386u, 341851980u,\n857839960u, 2515339233u,\n156283211u, 1906768669u,\n3886713057u, 1276595523u,\n2809830736u, 460237542u,\n3420452099u, 142985419u,\n205970448u, 4198897105u,\n1950698961u, 2069753399u,\n1142216925u, 1113051162u,\n1033680610u, 4278599955u,\n1106466069u, 356742959u,\n531521052u, 3494863964u,\n225629455u, 3735275001u,\n3662626864u, 1750561299u,\n1012864651u, 2101846429u,\n1074553219u, 668829411u,\n992181339u, 3384018814u,\n3330664522u, 860966321u,\n1885071395u, 4233785523u,\n100741310u, 451656820u,\n2148187612u, 1063001151u,\n360256231u, 107312677u,\n3650357479u, 2390172694u,\n22452685u, 237319043u,\n3600462351u, 1216645846u,\n2088767754u, 164402616u,\n2418980170u, 926137824u,\n94638678u, 1689811113u,\n2751052984u, 1767810825u,\n271289013u, 3896132233u,\n103797041u, 1397772514u,\n3441135892u, 3323383489u,\n2491268371u, 1662561885u,\n1612872497u, 2986430557u,\n2756998822u, 207428029u,\n937973965u, 2791656726u,\n1949717207u, 2260498180u,\n2648427775u, 2360400900u,\n2080496169u, 486358863u,\n1582022990u, 1263709570u,\n1396468647u, 1377764574u,\n363008508u, 1293502429u,\n224580012u, 4252610345u,\n1435134775u, 1099809675u,\n533671980u, 1533438766u,\n1820532305u, 2776960536u,\n3374512975u, 3542220540u,\n822810075u, 3716663290u,\n1157398049u, 3752806924u,\n4081637863u, 337070226u,\n3866585976u, 359270190u,\n2110942730u, 3267551635u,\n644850146u, 1306761320u,\n746972907u, 934259457u,\n2341378668u, 2220373824u,\n1242645122u, 4109252858u,\n1625266099u, 1173698481u,\n383517064u, 896322512u,\n3377483696u, 1788337208u,\n455496839u, 3194373887u,\n1837689083u, 1336556841u,\n1658628529u, 2911512007u,\n3838343487u, 2757664765u,\n1537187340u, 3712582785u,\n367022558u, 3071359622u,\n3926147070u, 35432879u,\n3093195926u, 2561488770u,\n4273132307u, 3898950547u,\n2838251049u, 2103926083u,\n2549435227u, 536047554u,\n1858986613u, 2040551642u,\n1147412575u, 1972369852u,\n4166184983u, 3528794619u,\n4077477194u, 3565689036u,\n808048238u, 3826350461u,\n1359641525u, 1197100813u,\n265993036u, 1864569342u,\n725164342u, 2264788336u,\n1831223342u, 3329594980u,\n923017956u, 490608221u,\n3818634478u, 258154469u,\n1441714797u, 1174785921u,\n3833372385u, 3287246572u,\n1677395563u, 3569218731u,\n868981704u, 2163330264u,\n2649450292u, 500120236u,\n465161780u, 746438382u,\n1145009669u, 2520062970u,\n2810524030u, 1561519055u,\n1479878006u, 3864969305u,\n2686075657u, 4042710240u,\n3224066062u, 2774151984u,\n2226179547u, 1643626042u,\n2328730865u, 3160666939u,\n2107011431u, 96459446u,\n3920328742u, 3336407558u,\n829404209u, 1878067032u,\n1235983679u, 4237425634u,\n466519055u, 3870676863u,\n934312076u, 2952135524u,\n276949224u, 4100839753u,\n424001484u, 1955120893u,\n4015478120u, 1265237690u,\n427484362u, 4246879223u,\n3452969617u, 1724363362u,\n1553513184u, 834830418u,\n1858777639u, 3476334357u,\n4144030366u, 2450047160u,\n2950762705u, 4277111759u,\n358032121u, 2511026735u,\n167923105u, 2059208280u,\n251949572u, 3065234219u,\n1535473864u, 556796152u,\n1513237478u, 3150857516u,\n1103404394u, 198182691u,\n1476438092u, 2913077464u,\n207119516u, 3963810232u,\n2954651680u, 1535115487u,\n3051522276u, 4046477658u,\n917804636u, 864395565u,\n632704095u, 140762681u,\n1802040304u, 990407433u,\n3771506212u, 4106024923u,\n1287729497u, 2198985327u,\n4052924496u, 2926590471u,\n3084557148u, 1472898694u,\n1009870118u, 559702706u,\n4265214507u, 82077489u,\n3067891003u, 3295678907u,\n2402308151u, 1096697687u,\n464407878u, 4190838199u,\n4269578403u, 3060919438u,\n2899950405u, 3046872820u,\n733509243u, 1583801700u,\n40453902u, 3879773881u,\n1993425202u, 2185339100u,\n1877837196u, 3912423882u,\n3293122640u, 4104318469u,\n1679617763u, 3703603898u,\n8759461u, 2540185277u,\n1152198475u, 2038345882u,\n2503579743u, 1446869792u,\n2019419351u, 4051584612u,\n3178289407u, 3992503830u,\n2879018745u, 2719373510u,\n700836153u, 1675560450u,\n4121245793u, 2064715719u,\n343595772u, 1996164093u,\n3130433948u, 405251683u,\n2804817126u, 1607133689u,\n463852893u, 2864244470u,\n2224044848u, 4071581802u,\n2537107938u, 2246347953u,\n3207234525u, 2028708916u,\n2272418128u, 803575837u,\n38655481u, 2170452091u,\n3272166407u, 557660441u,\n4019147902u, 3841480082u,\n298459606u, 2600943364u,\n2440657523u, 255451671u,\n3424361375u, 779434428u,\n3088526123u, 490671625u,\n1322855877u, 3452203069u,\n3057021940u, 2285701422u,\n2014993457u, 2390431709u,\n2002090272u, 1568745354u,\n1783152480u, 823305654u,\n4053862835u, 2200236540u,\n3009412313u, 3184047862u,\n3032187389u, 4159715581u,\n2966902888u, 252986948u,\n1849329144u, 3160134214u,\n3420960112u, 3198900547u,\n749160960u, 379139040u,\n1208883495u, 1566527339u,\n3006227299u, 4194096960u,\n556075248u, 497404038u,\n1717327230u, 1496132623u,\n1775955687u, 1719108984u,\n1014328900u, 4189966956u,\n2108574735u, 2584236470u,\n684087286u, 531310503u,\n4264509527u, 773405691u,\n3088905079u, 3456882941u,\n3105682208u, 3382290593u,\n2289363624u, 3296306400u,\n4168438718u, 467441309u,\n777173623u, 3241407531u,\n1183994815u, 1132983260u,\n1610606159u, 2540270567u,\n2649684057u, 1397502982u,\n146657385u, 3318434267u,\n2109315753u, 3348545480u,\n3193669211u, 811750340u,\n1073256162u, 3571673088u,\n546596661u, 1017047954u,\n3403136990u, 2540585554u,\n1477047647u, 4145867423u,\n2826408201u, 3531646869u,\n784952939u, 943914610u,\n2717443875u, 3657384638u,\n1806867885u, 1903578924u,\n3985088434u, 1911188923u,\n1764002686u, 3672748083u,\n1832925325u, 241574049u,\n519948041u, 3181425568u,\n2939747257u, 1634174593u,\n3429894862u, 3529565564u,\n1089679033u, 240953857u,\n2025369941u, 2695166650u,\n517086873u, 2964595704u,\n3017658263u, 3828377737u,\n2144895011u, 994799311u,\n1184683823u, 4260564140u,\n308018483u, 4262383425u,\n1374752558u, 3431057723u,\n1572637805u, 383233885u,\n3188015819u, 4051263539u,\n233319221u, 3794788167u,\n2017406667u, 919677938u,\n4074952232u, 1683612329u,\n4213676186u, 327142514u,\n3032591014u, 4204155962u,\n206775997u, 2283918569u,\n2395147154u, 3427505379u,\n2211319468u, 4153726847u,\n2217060665u, 350160869u,\n2493667051u, 1648200185u,\n3441709766u, 1387233546u,\n140980u, 1891558063u,\n760080239u, 2088061981u,\n1580964938u, 740563169u,\n422986366u, 330624974u,\n4264507722u, 150928357u,\n2738323042u, 2948665536u,\n918718096u, 376390582u,\n3966098971u, 717653678u,\n3219466255u, 3799363969u,\n3424344721u, 3187805406u,\n375347278u, 3490350144u,\n1992212097u, 2263421398u,\n3855037968u, 1928519266u,\n3866327955u, 1129127000u,\n1782515131u, 2746577402u,\n3059200728u, 2108753646u,\n2738070963u, 1336849395u,\n1705302106u, 768287270u,\n1343511943u, 2247006571u,\n1956142255u, 1780259453u,\n3475618043u, 212490675u,\n622521957u, 917121602u,\n1852992332u, 1267987847u,\n3170016833u, 2549835613u,\n3299763344u, 2864033668u,\n3378768767u, 1236609378u,\n4169365948u, 3738062408u,\n2661022773u, 2006922227u,\n2760592161u, 3828932355u,\n2636387819u, 2616619070u,\n1237256330u, 3449066284u,\n2871755260u, 3729280948u,\n3862686086u, 431292293u,\n3285899651u, 786322314u,\n2531158535u, 724901242u,\n2377363130u, 1415970351u,\n1244759631u, 3263135197u,\n965248856u, 174024139u,\n2297418515u, 2954777083u,\n987586766u, 3206261120u,\n4059515114u, 3903854066u,\n1931934525u, 2287507921u,\n1827135136u, 1781944746u,\n574617451u, 2299034788u,\n2650140034u, 4081586725u,\n2482286699u, 1109175923u,\n458483596u, 618705848u,\n4059852729u, 1813855658u,\n4190721328u, 1129462471u,\n4089998050u, 3575732749u,\n2375584220u, 1037031473u,\n1623777358u, 3389003793u,\n546597541u, 352770237u,\n1383747654u, 3122687303u,\n1646071378u, 1164309901u,\n290870767u, 830691298u,\n929335420u, 3193251135u,\n989577914u, 3626554867u,\n591974737u, 3996958215u,\n3163711272u, 3071568023u,\n1516846461u, 3656006011u,\n2698625268u, 2510865430u,\n340274176u, 1167681812u,\n3698796465u, 3155218919u,\n4102288238u, 1673474350u,\n3069708839u, 2704165015u,\n1237411891u, 1854985978u,\n3646837503u, 3625406022u,\n921552000u, 1712976649u,\n3939149151u, 878608872u,\n3406359248u, 1068844551u,\n1834682077u, 4155949943u,\n2437686324u, 3163786257u,\n2645117577u, 1988168803u,\n747285578u, 1626463554u,\n1235300371u, 1256485167u,\n1914142538u, 4141546431u,\n3838102563u, 582664250u,\n1883344352u, 2083771672u,\n2611657933u, 2139079047u,\n2250573853u, 804336148u,\n3066325351u, 2770847216u,\n4275641370u, 1455750577u,\n3346357270u, 1674051445u,\n601221482u, 3992583643u,\n1402445097u, 3622527604u,\n2509017299u, 2966108111u,\n2557027816u, 900741486u,\n1790771021u, 2912643797u,\n2631381069u, 4014551783u,\n90375300u, 300318232u,\n3269968032u, 2679371729u,\n2664752123u, 3517585534u,\n3253901179u, 542270815u,\n1188641600u, 365479232u,\n2210121140u, 760762191u,\n1273768482u, 1216399252u,\n3484324231u, 4287337666u,\n16322182u, 643179562u,\n325675502u, 3652676161u,\n3120716054u, 3330259752u,\n1011990087u, 2990167340u,\n1097584090u, 3262252593u,\n1829409951u, 3665087267u,\n1214854475u, 2134299399u,\n3704419305u, 411263051u,\n1625446136u, 549838529u,\n4283196353u, 1342880802u,\n3460621305u, 1967599860u,\n4282843369u, 1275671016u,\n2544665755u, 853593042u,\n901109753u, 2682611693u,\n110631633u, 797487791u,\n1472073141u, 850464484u,\n797089608u, 3286110054u,\n350397471u, 2775631060u,\n366448238u, 3842907484u,\n2219863904u, 3623364733u,\n1850985302u, 4009616991u,\n294963924u, 3693536939u,\n3061255808u, 1615375832u,\n1920066675u, 4113028420u,\n4032223840u, 2318423400u,\n2701956286u, 4145497671u,\n3991532344u, 2536338351u,\n1679099863u, 1728968857u,\n449740816u, 2686506989u,\n685242457u, 97590863u,\n3258354115u, 1502282913u,\n1235084019u, 2151665147u,\n528459289u, 231097464u,\n2477280726u, 3651607391u,\n2091754612u, 1178454681u,\n980597335u, 1604483865u,\n1842333726u, 4146839064u,\n3213794286u, 2601416506u,\n754220096u, 3571436033u,\n488595746u, 1448097974u,\n4004834921u, 238887261u,\n3320337489u, 1416989070u,\n2928916831u, 4093725287u,\n186020771u, 2367569534u,\n3046087671u, 4090084518u,\n3548184546u, 679517009u,\n1962659444u, 3539886328u,\n4192003933u, 1678423485u,\n3827951761u, 3086277222u,\n2144472852u, 1390394371u,\n2976322029u, 1574517163u,\n3553313841u, 119173722u,\n1702434637u, 1766260771u,\n3629581771u, 1407497759u,\n895654784u, 751439914u,\n4008409498u, 215917713u,\n1482103833u, 695551833u,\n1288382231u, 2656990891u,\n2581779077u, 1570750352u,\n3710689053u, 1741390464u,\n2666411616u, 3533987737u,\n4289478316u, 3576119563u,\n4118694920u, 108199666u,\n3869794273u, 963183826u,\n2081410737u, 3796810515u,\n791123882u, 2525792704u,\n1036883117u, 136547246u,\n875691100u, 2592925324u,\n614302599u, 3013176417u,\n2689342539u, 427154472u,\n532957601u, 1228758574u,\n1898117151u, 1181643858u,\n1908591042u, 1464255968u,\n446980910u, 2984611177u,\n58509511u, 1046943619u,\n3508927906u, 2001585786u,\n2544767379u, 1525438381u,\n552181222u, 1959725830u,\n879448844u, 1348536411u,\n4242243590u, 2861338018u,\n1082052441u, 1034351453u,\n601175800u, 764077711u,\n530635011u, 3785343245u,\n2178026726u, 117256687u,\n2378297261u, 457568934u,\n76438221u, 4104954272u,\n956793873u, 3783168634u,\n2485968477u, 2381948487u,\n4226929450u, 3148473363u,\n2518273601u, 3569490233u,\n879369091u, 2180270337u,\n3674375989u, 1387729170u,\n977997984u, 4270646856u,\n568650985u, 951677556u,\n4213877384u, 2721005055u,\n1073364549u, 2563403831u,\n1678669911u, 66786703u,\n2273631661u, 1149351924u,\n3651298990u, 1581883443u,\n246723096u, 1895026827u,\n3810605772u, 3711056516u,\n4058833288u, 2193790614u,\n2080120290u, 3638638708u,\n2915672708u, 2263003308u,\n2361934197u, 4136767460u,\n1976115991u, 3448840877u,\n2019238520u, 225333538u,\n874340815u, 2976159827u,\n1555273378u, 3797521928u,\n1942347150u, 3262952567u,\n435997738u, 340403353u,\n2817830907u, 2078619498u,\n749534111u, 1178073973u,\n894654712u, 3361226032u,\n841092198u, 3288261538u,\n1696412169u, 1496966875u,\n697501571u, 1059158875u,\n3739946319u, 2481012988u,\n568983526u, 114945840u,\n1559249010u, 2218244008u,\n2841706923u, 1632780103u,\n4020169654u, 2087949619u,\n2438736103u, 24032648u,\n833416317u, 3787017905u,\n2373238993u, 2575395164u,\n3434544481u, 3228481067u,\n2542976862u, 2971726178u,\n2880371864u, 3642087909u,\n2407477975u, 2239080836u,\n1043714217u, 3894199764u,\n2235879182u, 203853421u,\n2933669448u, 2504940536u,\n834683330u, 425935223u,\n3560796393u, 3565833278u,\n1668000829u, 3683399154u,\n3414330886u, 1748785729u,\n1023171602u, 580966986u,\n2531038985u, 3227325488u,\n2657385925u, 2124704694u,\n233442446u, 1107045577u,\n3407293834u, 552770757u,\n3899097693u, 1067532701u,\n115667924u, 1406028344u,\n1707768231u, 3724015962u,\n2419657149u, 18613994u,\n2532882091u, 3476683808u,\n1560838678u, 811220224u,\n895961699u, 3762914298u,\n1328752423u, 1844996900u,\n1420427894u, 1848067707u,\n1210281744u, 904215228u,\n4055325594u, 1118521573u,\n2496554183u, 2579259919u,\n3996647489u, 3657647605u,\n325254059u, 3136157065u,\n3951522674u, 4052925250u,\n3341068436u, 2287683323u,\n1313073005u, 126005630u,\n2505120084u, 1194725057u,\n853746559u, 3555092974u,\n2689238752u, 49515858u,\n1244776042u, 1069300695u,\n61073168u, 1010661841u,\n1269521335u, 1902040126u,\n990632502u, 2378708922u,\n3858321250u, 1400735275u,\n2974699176u, 2771676666u,\n170995186u, 2877798589u,\n545726212u, 2225229957u,\n1086473152u, 3454177594u,\n3859483262u, 1499729584u,\n2088002891u, 2883475137u,\n3222194252u, 4144472319u,\n2212229854u, 4146740722u,\n567988835u, 1051332394u,\n3932046135u, 542648229u,\n3017852446u, 1277887997u,\n162888005u, 1669710469u,\n1492500905u, 553041029u,\n1434876932u, 533989516u,\n3817492747u, 584127807u,\n4147115982u, 2993670925u,\n4020312558u, 710021255u,\n3509733475u, 3587959456u,\n2088550465u, 1745399498u,\n2952242967u, 1259815443u,\n869648362u, 1404723176u,\n3947542735u, 1334333531u,\n3873471582u, 229399758u,\n59634866u, 3239516985u,\n3844250972u, 1275954779u,\n492891666u, 1029533080u,\n1552951157u, 367320647u,\n699480890u, 3684418197u,\n3707014310u, 471105777u,\n1824587258u, 4030809053u,\n3489914436u, 484559105u,\n1235750398u, 1428453396u,\n4230459084u, 4255931645u,\n1848597055u, 4271715616u,\n331780381u, 482425775u,\n2435323270u, 3171911678u,\n3507210587u, 928543347u,\n4197807526u, 3680046204u,\n2766042024u, 2159512867u,\n179373257u, 313902234u,\n4024837592u, 294795361u,\n1622282562u, 647086234u,\n2825039429u, 577214736u,\n4043412446u, 2426981244u,\n1277736097u, 1130129573u,\n2601395338u, 995791646u,\n36668922u, 3344746679u,\n1521532225u, 1645086060u,\n2622763015u, 4122335794u,\n2936887705u, 494465807u,\n2580840343u, 1064648931u,\n1247887787u, 2752145076u,\n1277612417u, 1249660507u,\n2288678613u, 3312498873u,\n2459273912u, 4238535494u,\n3117488020u, 2571979978u,\n2680188909u, 1471227427u,\n1616494033u, 633688562u,\n2268653416u, 3268237290u,\n3021962815u, 1959779970u,\n3321382074u, 766642813u,\n204429780u, 1323319858u,\n3676032891u, 1380896111u,\n4030639049u, 3647601207u,\n1830028502u, 2830263774u,\n1375962216u, 1733961041u,\n939765180u, 521947915u,\n3903267364u, 497472767u,\n1619700946u, 189164145u,\n3115593885u, 486382294u,\n1262445920u, 4062496162u,\n2464795849u, 3770038872u,\n4032121374u, 3235740744u,\n3757765258u, 1777199847u,\n2167243108u, 1912506671u,\n4180515317u, 2276864677u,\n536034089u, 2384915026u,\n162938278u, 1588060152u,\n4018349945u, 2504457929u,\n841450426u, 2790120722u,\n2719983588u, 1471020554u,\n1390856732u, 3623212998u,\n2506944218u, 1035080801u,\n348812127u, 3026631806u,\n746483541u, 2342164722u,\n122104390u, 4074122771u,\n3986865419u, 1674890530u,\n3693306023u, 3011542850u,\n1294951725u, 899303190u,\n3577146915u, 3549160092u,\n1241677652u, 4290680005u,\n3193053279u, 2029187390u,\n3298063095u, 3943068002u,\n3946220635u, 2273781461u,\n889053698u, 1376304022u,\n1486839612u, 2127663659u,\n344127443u, 1646681121u,\n2780117810u, 2142045764u,\n2694572773u, 447810651u,\n2185527146u, 2366308558u,\n290335413u, 584901173u,\n2012370276u, 970504950u,\n3258236042u, 2008155560u,\n3945579565u, 614796295u,\n24452072u, 2695940969u,\n3983727134u, 3444688454u,\n1327044473u, 3545633451u,\n1875293322u, 1739318893u,\n1707527799u, 2683090634u,\n2848082386u, 2814622471u,\n4111401777u, 2774816580u,\n3849839194u, 437560100u,\n2238350150u, 2462124836u,\n665017710u, 512012738u,\n2945294779u, 3305170944u,\n819477765u, 59419271u,\n155125658u, 665292744u,\n444722813u, 3580039116u,\n2355675635u, 663735032u,\n3247800169u, 1579404983u,\n1985115003u, 3397891494u,\n358696453u, 1474896279u,\n516388613u, 710590371u,\n3490497111u, 2514565805u,\n2386143445u, 477509654u,\n412854590u, 3624609754u,\n3214388668u, 3516075816u,\n2731288520u, 1369482895u,\n4033204378u, 1314000850u,\n829769325u, 1935166880u,\n1608191643u, 2607067237u,\n423820371u, 3257747610u,\n1355298041u, 3776931214u,\n4105054901u, 2107080812u,\n1911521879u, 3183054185u,\n3910177801u, 675129307u,\n1209358971u, 4205727791u,\n1435726287u, 3333261712u,\n1400982708u, 1154611403u,\n1663501483u, 2837596667u,\n3164734053u, 2759854023u,\n4012043629u, 1963228038u,\n3981675284u, 2677557877u,\n520119591u, 505138315u,\n897271356u, 1803966773u,\n1016663294u, 616691903u,\n2254742522u, 4032705384u,\n2468470796u, 798395739u,\n3025169002u, 3570037122u,\n1461093710u, 3473799845u,\n3702624858u, 476400898u,\n1043039728u, 2304070437u,\n181576948u, 602972493u,\n3996616030u, 3289878097u,\n2068516226u, 3922247304u,\n1299968266u, 2520311409u,\n1968824721u, 3214794876u,\n1581813122u, 2668800905u,\n3297613974u, 748160407u,\n1145536484u, 1326769504u,\n2973323521u, 3775262814u,\n3218653169u, 902775872u,\n3498603433u, 1372805534u,\n704686363u, 3626542352u,\n2271580579u, 1213925114u,\n46329775u, 3009384989u,\n1330254048u, 1194824134u,\n514204310u, 3781981134u,\n442526164u, 2835608783u,\n3460471867u, 510634034u,\n546406434u, 2716786748u,\n2840500021u, 1669490957u,\n2536189149u, 3251421224u,\n1358736072u, 1089334066u,\n3260749330u, 250756920u,\n2974806681u, 1513718866u,\n82635635u, 4041016629u,\n3391765744u, 2495807367u,\n3962674316u, 2822889695u,\n753413337u, 2008251381u,\n3123390177u, 106212622u,\n490570565u, 1684884205u,\n793892547u, 1927268995u,\n2344148164u, 2251978818u,\n437424236u, 2774023200u,\n2674940754u, 3788056262u,\n2597882666u, 3678660147u,\n3797434193u, 3838215866u,\n279687080u, 2656772270u,\n2190204787u, 1997584981u,\n3384401882u, 3160208845u,\n3629379425u, 2668998785u,\n1050036757u, 2954162084u,\n917091826u, 1744374041u,\n1454282570u, 845687881u,\n2997173625u, 776018378u,\n1137560602u, 1938378389u,\n1748082354u, 2066910012u,\n2677675207u, 918315064u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; { uint64_t h = farmhashte::Hash64WithSeeds(data, len++, SEED0, SEED1); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashte::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashte::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; }\n{ uint64_t h = farmhashte::Hash64WithSeeds(data + offset, len, SEED0, SEED1); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint64_t h = farmhashte::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint64_t h = farmhashte::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); }\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashteTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\n{ uint64_t h = farmhashte::Hash64WithSeeds(data + offset, len, SEED0, SEED1); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint64_t h = farmhashte::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint64_t h = farmhashte::Hash64(data + offset, len); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashteTest\n\n#if TESTING\n\n//static int farmhashteTestResult = farmhashteTest::RunTest();\nTEST(farmhash, te) { farmhashteTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashteTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashteTest::Dump(0, i);\n  }\n  farmhashteTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashuoTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n3277735313u, 2681724312u,\n2598464059u, 797982799u,\n2603993599u, 921001710u,\n1410420968u, 2134990486u,\n2914424215u, 2244477846u,\n255297188u, 2992121793u,\n161451183u, 3943596029u,\n4019337850u, 452431531u,\n3379021470u, 2557197665u,\n299850021u, 2532580744u,\n1298374911u, 3099673830u,\n2199864459u, 3696623795u,\n4055299123u, 3281581178u,\n1053458494u, 1882212500u,\n3456121707u, 275903667u,\n458884671u, 3033004529u,\n1898235244u, 1402319660u,\n2700149065u, 2699376854u,\n2433714046u, 4222949502u,\n4220361840u, 1712034059u,\n4148372108u, 1330324210u,\n594028478u, 2921867846u,\n780716741u, 1728752234u,\n3280331829u, 326029180u,\n393215742u, 3349570000u,\n3824583307u, 1612122221u,\n1379537552u, 1646032583u,\n2233466664u, 1432476832u,\n2052294713u, 3552092450u,\n1628777059u, 1499109081u,\n2960536756u, 1554038301u,\n1145519619u, 3190844552u,\n237495366u, 540224401u,\n65721842u, 489963606u,\n1596489240u, 1562872448u,\n1790705123u, 2128624475u,\n1435705557u, 1262831810u,\n155445229u, 1672724608u,\n663607706u, 2077310004u,\n3610042449u, 1911523866u,\n2563776023u, 294527927u,\n1099072299u, 1389770549u,\n2952353448u, 2026137563u,\n3603803785u, 629449419u,\n226132789u, 2489287368u,\n1552847036u, 645684964u,\n187883449u, 230403464u,\n3151491850u, 3272648435u,\n2002861219u, 165370827u,\n916494250u, 1230085527u,\n3807265751u, 3628174014u,\n231181488u, 851743255u,\n2988893883u, 1554380634u,\n1142264800u, 3667013118u,\n2638023604u, 2290487377u,\n732137533u, 1909203251u,\n1380301172u, 1498556724u,\n4072067757u, 4165088768u,\n3931792696u, 197618179u,\n956300927u, 914413116u,\n2148126371u, 1913303225u,\n3074915312u, 3117299654u,\n3178848746u, 1357272220u,\n1438494951u, 507436733u,\n3854939912u, 1413396341u,\n126024219u, 146044391u,\n3254014218u, 429095991u,\n165589978u, 1578546616u,\n623071693u, 366414107u,\n249776086u, 1207522198u,\n3120876698u, 1684583131u,\n46987739u, 1157614300u,\n199124911u, 520792961u,\n3614377032u, 586863115u,\n1716848157u, 4033596884u,\n1164298657u, 4140791139u,\n3824360466u, 3242407770u,\n3725511003u, 232064808u,\n2736953692u, 816692935u,\n512845449u, 3748861010u,\n4290630595u, 517646945u,\n22638523u, 648000590u,\n1283799121u, 3047062993u,\n1024246061u, 4027776454u,\n834785312u, 382936554u,\n411505255u, 1973395102u,\n580988377u, 2826990641u,\n3474970689u, 1029055034u,\n2584372201u, 1758123094u,\n589567754u, 325737734u,\n3886113119u, 3338548567u,\n257578986u, 3698087965u,\n3604937815u, 3198590202u,\n2305332220u, 191910725u,\n4244322689u, 542201663u,\n3315355162u, 2135941665u,\n1175961330u, 3948351189u,\n23075771u, 3252374102u,\n713127376u, 1467786451u,\n663013031u, 3444053918u,\n3077742128u, 1062268187u,\n2115441882u, 4081398201u,\n2335576331u, 2560479831u,\n1379288194u, 4225182569u,\n3958366652u, 3067277639u,\n3667516477u, 1709989541u,\n4188504038u, 59581167u,\n2725013602u, 3639843023u,\n3758739543u, 4189944477u,\n2470483982u, 877580602u,\n3252925478u, 2062343506u,\n3981838403u, 3762572073u,\n2931588131u, 3284356565u,\n1129162571u, 732225574u,\n1677744031u, 3532596884u,\n3232041815u, 1652884780u,\n3740979556u, 259034107u,\n2227121257u, 1426140634u,\n315836068u, 3200749877u,\n1386256573u, 24035717u,\n234531934u, 1115203611u,\n1598686658u, 3146815575u,\n2632963283u, 1778459926u,\n739944537u, 579625482u,\n1231665285u, 2457048126u,\n3903349120u, 389846205u,\n1052645068u, 862072556u,\n2834153464u, 1481069623u,\n1688445808u, 701920051u,\n3740748788u, 3388062747u,\n1192588249u, 1410111809u,\n2633463887u, 4050419847u,\n2513844237u, 2187968410u,\n2951683019u, 3015806005u,\n3000537321u, 479241367u,\n252167538u, 1231057113u,\n393624922u, 704325635u,\n1467197045u, 2066433573u,\n3082385053u, 735688735u,\n956434529u, 4028590195u,\n4074716319u, 813690332u,\n2124740535u, 804073145u,\n3748881891u, 3195309868u,\n841856605u, 2585865274u,\n881504822u, 3254795114u,\n1241815736u, 970796142u,\n497175391u, 3929484338u,\n4264993211u, 1835322201u,\n3675707838u, 4205965408u,\n300298607u, 3858319990u,\n3255259608u, 4247675634u,\n1095823272u, 1197245408u,\n1742785722u, 785893184u,\n1702965674u, 850401405u,\n4084891412u, 1160134629u,\n2555998391u, 1972759056u,\n710625654u, 4116162021u,\n3352753742u, 85121177u,\n3513255468u, 2487440590u,\n2480032715u, 2287747045u,\n4090178629u, 4044418876u,\n1703944517u, 486290428u,\n1515601363u, 2011102035u,\n573985957u, 3536053779u,\n4257172787u, 576405853u,\n1523550693u, 1014952061u,\n711098718u, 2425317635u,\n3460807169u, 3688987163u,\n3167371443u, 3062418115u,\n3330028292u, 1713171303u,\n526177822u, 2602636307u,\n1245357025u, 3346699703u,\n254529415u, 4017058800u,\n1829738451u, 2164236533u,\n1381258384u, 4036354071u,\n1749181924u, 4118435443u,\n4130543625u, 688472265u,\n2731071299u, 2547657502u,\n4175776103u, 338058159u,\n3729582129u, 4181845558u,\n2227290760u, 146827036u,\n2459178427u, 1025353883u,\n4284109679u, 2502815838u,\n825124804u, 2533140036u,\n616327404u, 2448966429u,\n413992636u, 2334782461u,\n2239052397u, 1082330589u,\n3381164715u, 199381437u,\n2583939798u, 2848283092u,\n2300168091u, 2156336315u,\n1855234968u, 1982110346u,\n2482046810u, 3158163887u,\n2168864636u, 133643215u,\n3904021624u, 3646514568u,\n1176127003u, 4015671361u,\n100525019u, 3534706803u,\n2045051700u, 3452457457u,\n1492267772u, 2308393828u,\n3776661467u, 3281002516u,\n4246334524u, 743955039u,\n685631442u, 1147951686u,\n2040912376u, 2911148054u,\n3204999386u, 2415262714u,\n313209105u, 777065474u,\n2729416454u, 1029284767u,\n1632078298u, 1817552554u,\n3963877000u, 1919171906u,\n3843219958u, 3073580867u,\n865146997u, 2473524405u,\n2593817617u, 3643076308u,\n617975720u, 3671562289u,\n121812599u, 2902367378u,\n2260041112u, 1019936943u,\n320945955u, 2337845588u,\n1535670711u, 773792826u,\n3152195900u, 4090794518u,\n4142587430u, 484732447u,\n419191319u, 3377973345u,\n324924679u, 2809286837u,\n1562277603u, 1378362199u,\n3791551981u, 1097806406u,\n1386297408u, 2304900033u,\n3574135531u, 4017075736u,\n1161238398u, 1358056883u,\n3487080616u, 1516736273u,\n851615042u, 2927899494u,\n4077452507u, 2984955003u,\n3907754394u, 3578173844u,\n1948409509u, 1923531348u,\n3578472493u, 3710074193u,\n213811117u, 2586632018u,\n1922589216u, 274958014u,\n1654276320u, 2687561076u,\n2569061755u, 3122046057u,\n3203802620u, 1692079268u,\n477806878u, 140587742u,\n520565830u, 3674576684u,\n91246882u, 1010215946u,\n3703994407u, 69201295u,\n776213083u, 3677771507u,\n3696902571u, 3593730713u,\n2907901228u, 3239753796u,\n1586241476u, 1939595371u,\n2268396558u, 3468719670u,\n2955715987u, 2295501078u,\n2775848696u, 1358532390u,\n3843660102u, 82177963u,\n4094477877u, 191727221u,\n278661677u, 2889101923u,\n1352525614u, 2844977667u,\n1261140657u, 1747052377u,\n2334120653u, 645125282u,\n2707230761u, 4142246789u,\n1068639717u, 2288162940u,\n1419293714u, 1636932722u,\n3252686293u, 318543902u,\n2598802768u, 2025886508u,\n2250788464u, 2711763065u,\n255108733u, 1845587644u,\n3719270134u, 3940707863u,\n917608574u, 1363253259u,\n788659330u, 673256220u,\n109793386u, 341851980u,\n2698465479u, 3011229884u,\n3886713057u, 1276595523u,\n2439962760u, 2700515456u,\n205970448u, 4198897105u,\n875511891u, 371715572u,\n1033680610u, 4278599955u,\n3120038721u, 1256300069u,\n225629455u, 3735275001u,\n3961944123u, 1769389163u,\n1074553219u, 668829411u,\n1098679359u, 2573697509u,\n1885071395u, 4233785523u,\n2513878053u, 2030193788u,\n360256231u, 107312677u,\n310517502u, 2618936366u,\n3600462351u, 1216645846u,\n2970730323u, 4278812598u,\n94638678u, 1689811113u,\n4125738800u, 3103759730u,\n103797041u, 1397772514u,\n1669653333u, 572567964u,\n1612872497u, 2986430557u,\n214990655u, 3117607990u,\n1949717207u, 2260498180u,\n1493936866u, 3554860960u,\n1582022990u, 1263709570u,\n1244120487u, 3416600761u,\n224580012u, 4252610345u,\n286306391u, 814956796u,\n1820532305u, 2776960536u,\n3082703465u, 1659265982u,\n1157398049u, 3752806924u,\n3508246460u, 2902716664u,\n2110942730u, 3267551635u,\n902835431u, 405228165u,\n2341378668u, 2220373824u,\n3303626294u, 1175118221u,\n383517064u, 896322512u,\n1697257567u, 2202820683u,\n1837689083u, 1336556841u,\n914535232u, 3634083711u,\n1537187340u, 3712582785u,\n1088201893u, 3270984620u,\n3093195926u, 2561488770u,\n1962968100u, 236189500u,\n2549435227u, 536047554u,\n422609195u, 2958815818u,\n4166184983u, 3528794619u,\n1042329086u, 3914176886u,\n1359641525u, 1197100813u,\n1269739674u, 3301844628u,\n1831223342u, 3329594980u,\n2433669782u, 494908536u,\n1441714797u, 1174785921u,\n1933050423u, 958901065u,\n868981704u, 2163330264u,\n3243110680u, 1443133429u,\n1145009669u, 2520062970u,\n3851564853u, 2664619323u,\n2686075657u, 4042710240u,\n2125408249u, 4165697916u,\n2328730865u, 3160666939u,\n588683409u, 2126275847u,\n829404209u, 1878067032u,\n2567792910u, 897670516u,\n934312076u, 2952135524u,\n504832490u, 3312698056u,\n4015478120u, 1265237690u,\n3376133707u, 967674402u,\n1553513184u, 834830418u,\n2396504772u, 3278582098u,\n2950762705u, 4277111759u,\n4159211303u, 1290097509u,\n251949572u, 3065234219u,\n1832020534u, 312136369u,\n1103404394u, 198182691u,\n1369599600u, 3906710870u,\n2954651680u, 1535115487u,\n2389327507u, 1813520230u,\n632704095u, 140762681u,\n3123202913u, 3336005523u,\n1287729497u, 2198985327u,\n2470730783u, 3821758006u,\n1009870118u, 559702706u,\n4274686257u, 3187546567u,\n2402308151u, 1096697687u,\n678932329u, 3716363135u,\n2899950405u, 3046872820u,\n3754655641u, 2021741414u,\n1993425202u, 2185339100u,\n2838253700u, 3099212100u,\n1679617763u, 3703603898u,\n1135665833u, 3559875668u,\n2503579743u, 1446869792u,\n879818611u, 3788305533u,\n2879018745u, 2719373510u,\n3606051203u, 2166567748u,\n343595772u, 1996164093u,\n1577656121u, 475248376u,\n463852893u, 2864244470u,\n1332049663u, 3326459767u,\n3207234525u, 2028708916u,\n938916154u, 3115246264u,\n3272166407u, 557660441u,\n1265684026u, 245033807u,\n2440657523u, 255451671u,\n3811885130u, 1399880284u,\n1322855877u, 3452203069u,\n1324994449u, 3796404024u,\n2002090272u, 1568745354u,\n3700047753u, 31799506u,\n3009412313u, 3184047862u,\n728680761u, 3848624873u,\n1849329144u, 3160134214u,\n1272923193u, 1474278816u,\n1208883495u, 1566527339u,\n4136466541u, 630825649u,\n1717327230u, 1496132623u,\n2449386742u, 128106940u,\n2108574735u, 2584236470u,\n2872246579u, 397338552u,\n3088905079u, 3456882941u,\n1715915153u, 2940716269u,\n4168438718u, 467441309u,\n872996731u, 3206901319u,\n1610606159u, 2540270567u,\n1301658081u, 2379410194u,\n2109315753u, 3348545480u,\n2041927873u, 2644077493u,\n546596661u, 1017047954u,\n2596792972u, 2783958892u,\n2826408201u, 3531646869u,\n2219352672u, 4217451852u,\n1806867885u, 1903578924u,\n2076465705u, 2373061493u,\n1832925325u, 241574049u,\n1509517110u, 3703614272u,\n3429894862u, 3529565564u,\n4010000614u, 2256197939u,\n517086873u, 2964595704u,\n3501035294u, 4079457298u,\n1184683823u, 4260564140u,\n2339268412u, 3871564102u,\n1572637805u, 383233885u,\n3351411126u, 3419328182u,\n2017406667u, 919677938u,\n29804156u, 46276077u,\n3032591014u, 4204155962u,\n1172319502u, 969309871u,\n2211319468u, 4153726847u,\n3094193193u, 4240669441u,\n3441709766u, 1387233546u,\n4048882438u, 1217896566u,\n1580964938u, 740563169u,\n3691850348u, 3176426539u,\n2738323042u, 2948665536u,\n1474029445u, 3513354882u,\n3219466255u, 3799363969u,\n3961796122u, 1055550923u,\n1992212097u, 2263421398u,\n4289759174u, 2516844140u,\n1782515131u, 2746577402u,\n721928440u, 3529570984u,\n1705302106u, 768287270u,\n3474902815u, 4000011125u,\n3475618043u, 212490675u,\n549130471u, 2970128275u,\n3170016833u, 2549835613u,\n3691104824u, 2694324482u,\n4169365948u, 3738062408u,\n602930397u, 2148954730u,\n2636387819u, 2616619070u,\n301617872u, 374657036u,\n3862686086u, 431292293u,\n4225245165u, 1358580562u,\n2377363130u, 1415970351u,\n3885060756u, 1438379807u,\n2297418515u, 2954777083u,\n3970368221u, 1229801760u,\n1931934525u, 2287507921u,\n1713471510u, 2145608111u,\n2650140034u, 4081586725u,\n4196863572u, 1896558394u,\n4059852729u, 1813855658u,\n2618400836u, 1396056469u,\n2375584220u, 1037031473u,\n249284003u, 2450077637u,\n1383747654u, 3122687303u,\n2664431743u, 3855028730u,\n929335420u, 3193251135u,\n137313762u, 1850894384u,\n3163711272u, 3071568023u,\n418541677u, 3621223039u,\n340274176u, 1167681812u,\n4106647531u, 4022465625u,\n3069708839u, 2704165015u,\n2332023349u, 641449034u,\n921552000u, 1712976649u,\n1876484273u, 2343049860u,\n1834682077u, 4155949943u,\n2061821157u, 4240649383u,\n747285578u, 1626463554u,\n165503115u, 359629739u,\n3838102563u, 582664250u,\n3878924635u, 4117237498u,\n2250573853u, 804336148u,\n331393443u, 4242530387u,\n3346357270u, 1674051445u,\n3348019777u, 1722242971u,\n2509017299u, 2966108111u,\n4189102509u, 3323592310u,\n2631381069u, 4014551783u,\n4250787412u, 3448394212u,\n2664752123u, 3517585534u,\n3605365141u, 1669471183u,\n2210121140u, 760762191u,\n249697459u, 3416920106u,\n16322182u, 643179562u,\n1564226597u, 2134630675u,\n1011990087u, 2990167340u,\n2349550842u, 1642428946u,\n1214854475u, 2134299399u,\n2704221532u, 2104175211u,\n4283196353u, 1342880802u,\n198529755u, 2004468390u,\n2544665755u, 853593042u,\n2090611294u, 2970943872u,\n1472073141u, 850464484u,\n1407609278u, 3062461105u,\n366448238u, 3842907484u,\n488797416u, 1432670231u,\n294963924u, 3693536939u,\n3390549825u, 1583234720u,\n4032223840u, 2318423400u,\n2965642867u, 930822729u,\n1679099863u, 1728968857u,\n900822335u, 702309817u,\n3258354115u, 1502282913u,\n2811888503u, 3924947660u,\n2477280726u, 3651607391u,\n3788310204u, 1300369123u,\n1842333726u, 4146839064u,\n2468893861u, 4091095953u,\n488595746u, 1448097974u,\n1159634090u, 1738834113u,\n2928916831u, 4093725287u,\n530850094u, 291657799u,\n3548184546u, 679517009u,\n399175380u, 2658337143u,\n3827951761u, 3086277222u,\n2067718397u, 3632376023u,\n3553313841u, 119173722u,\n1702434637u, 1766260771u,\n895654784u, 751439914u,\n4008409498u, 215917713u,\n1288382231u, 2656990891u,\n2581779077u, 1570750352u,\n2666411616u, 3533987737u,\n4289478316u, 3576119563u,\n3869794273u, 963183826u,\n2081410737u, 3796810515u,\n1036883117u, 136547246u,\n875691100u, 2592925324u,\n2689342539u, 427154472u,\n532957601u, 1228758574u,\n1908591042u, 1464255968u,\n446980910u, 2984611177u,\n3508927906u, 2001585786u,\n2544767379u, 1525438381u,\n879448844u, 1348536411u,\n4242243590u, 2861338018u,\n601175800u, 764077711u,\n530635011u, 3785343245u,\n2378297261u, 457568934u,\n76438221u, 4104954272u,\n2485968477u, 2381948487u,\n4226929450u, 3148473363u,\n879369091u, 2180270337u,\n3674375989u, 1387729170u,\n568650985u, 951677556u,\n4213877384u, 2721005055u,\n1678669911u, 66786703u,\n2273631661u, 1149351924u,\n246723096u, 1895026827u,\n3810605772u, 3711056516u,\n2080120290u, 3638638708u,\n2915672708u, 2263003308u,\n1976115991u, 3448840877u,\n2019238520u, 225333538u,\n1555273378u, 3797521928u,\n1942347150u, 3262952567u,\n2817830907u, 2078619498u,\n749534111u, 1178073973u,\n841092198u, 3288261538u,\n1696412169u, 1496966875u,\n3739946319u, 2481012988u,\n568983526u, 114945840u,\n2841706923u, 1632780103u,\n4020169654u, 2087949619u,\n833416317u, 3787017905u,\n2373238993u, 2575395164u,\n2542976862u, 2971726178u,\n2880371864u, 3642087909u,\n1043714217u, 3894199764u,\n2235879182u, 203853421u,\n834683330u, 425935223u,\n3560796393u, 3565833278u,\n3414330886u, 1748785729u,\n1023171602u, 580966986u,\n2657385925u, 2124704694u,\n233442446u, 1107045577u,\n3899097693u, 1067532701u,\n115667924u, 1406028344u,\n2419657149u, 18613994u,\n2532882091u, 3476683808u,\n895961699u, 3762914298u,\n1328752423u, 1844996900u,\n1210281744u, 904215228u,\n4055325594u, 1118521573u,\n3996647489u, 3657647605u,\n325254059u, 3136157065u,\n3341068436u, 2287683323u,\n1313073005u, 126005630u,\n853746559u, 3555092974u,\n2689238752u, 49515858u,\n61073168u, 1010661841u,\n1269521335u, 1902040126u,\n3858321250u, 1400735275u,\n2974699176u, 2771676666u,\n545726212u, 2225229957u,\n1086473152u, 3454177594u,\n2088002891u, 2883475137u,\n3222194252u, 4144472319u,\n567988835u, 1051332394u,\n3932046135u, 542648229u,\n162888005u, 1669710469u,\n1492500905u, 553041029u,\n3817492747u, 584127807u,\n4147115982u, 2993670925u,\n3509733475u, 3587959456u,\n2088550465u, 1745399498u,\n869648362u, 1404723176u,\n3947542735u, 1334333531u,\n59634866u, 3239516985u,\n3844250972u, 1275954779u,\n2512155003u, 1685649437u,\n639306006u, 2524620206u,\n576786501u, 655707039u,\n2864351838u, 3736264674u,\n1200907897u, 2384379464u,\n15823708u, 206117476u,\n1193310960u, 1093099415u,\n3696538026u, 4112584792u,\n2069527017u, 547588820u,\n4178147211u, 2827259351u,\n940846775u, 1054995047u,\n2976960697u, 1934305529u,\n2199137382u, 1005722394u,\n1875867180u, 2064356511u,\n4019734130u, 3096333006u,\n2069509024u, 2906358341u,\n2232866485u, 1456016086u,\n1422674894u, 867282151u,\n1612503136u, 1739843072u,\n134947567u, 2978775774u,\n1284167756u, 1090844589u,\n831688783u, 2079216362u,\n1626991196u, 3644714163u,\n3678110059u, 898470030u,\n3916646913u, 3182422972u,\n3630426828u, 969847973u,\n3427164640u, 3463937250u,\n3044785046u, 897322257u,\n3443872170u, 4185408854u,\n2557463241u, 4080940424u,\n2048168570u, 2429169982u,\n3174690447u, 2513494106u,\n1213061732u, 3143736628u,\n3482268149u, 1250714337u,\n31648125u, 3872383625u,\n1565760579u, 36665130u,\n751041229u, 2257179590u,\n2915361862u, 280819225u,\n2907818413u, 4254297769u,\n3493178615u, 3755944354u,\n4043533423u, 1134196225u,\n4177134659u, 127246419u,\n2442615581u, 923049607u,\n1004426206u, 782768297u,\n2410586681u, 1430106871u,\n4103323427u, 3168399477u,\n3716682375u, 3616334719u,\n3413209549u, 656672786u,\n2876965944u, 182894450u,\n456581318u, 2683752067u,\n3877875910u, 3190666241u,\n3240336907u, 4024807233u,\n1681224377u, 1576191191u,\n3599250276u, 2381111980u,\n3495321877u, 3956024585u,\n1611608524u, 3815677453u,\n2062334396u, 1656117707u,\n5457134u, 3234118251u,\n470187419u, 2688566989u,\n3259870297u, 660100446u,\n442236198u, 2542452448u,\n493137955u, 392411099u,\n947967568u, 1234595917u,\n4230082284u, 2762976773u,\n2870085764u, 1455086530u,\n2762099647u, 4011882747u,\n1215981925u, 3227517889u,\n3269061963u, 4037515364u,\n3168911474u, 4255057396u,\n2026092260u, 1736192508u,\n3909727042u, 3114708966u,\n1938800693u, 680793595u,\n1525265867u, 2808224480u,\n2122290603u, 1211197714u,\n3520488321u, 3979192396u,\n3540779343u, 4192918639u,\n2736030448u, 1120335563u,\n1698949078u, 3993310631u,\n1966048551u, 2228221363u,\n597941119u, 3498018399u,\n393987327u, 454500547u,\n1222959566u, 567151340u,\n3774764786u, 1492844524u,\n3308300614u, 805568076u,\n868414882u, 177406999u,\n1608110313u, 642061169u,\n1027515771u, 3131251981u,\n2851936150u, 4272755262u,\n1532845092u, 709643652u,\n682573592u, 1244104217u,\n796769556u, 2500467040u,\n3002618826u, 1112998535u,\n1780193104u, 1243644607u,\n3691719535u, 2958853053u,\n466635014u, 2277292580u,\n4082276003u, 1030800045u,\n1750863246u, 379050598u,\n3576413281u, 731493104u,\n132259176u, 4115195437u,\n1769890695u, 2715470335u,\n1819263183u, 2028531518u,\n2154809766u, 3672399742u,\n76727603u, 4198182186u,\n2304993586u, 1666387627u,\n284366017u, 3359785538u,\n3469807328u, 2926494787u,\n3829072836u, 2493478921u,\n3738499303u, 3311304980u,\n932916545u, 2235559063u,\n2909742396u, 1765719309u,\n1456588655u, 508290328u,\n1490719640u, 3356513470u,\n2908490783u, 251085588u,\n830410677u, 3172220325u,\n3897208579u, 1940535730u,\n151909546u, 2384458112u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; { uint64_t h = farmhashuo::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashuo::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashuo::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; }\n{ uint64_t h = farmhashuo::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint64_t h = farmhashuo::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); }\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashuoTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\n{ uint64_t h = farmhashuo::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint64_t h = farmhashuo::Hash64(data + offset, len); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashuoTest\n\n#if TESTING\n\n//static int farmhashuoTestResult = farmhashuoTest::RunTest();\nTEST(farmhash, uo) { farmhashuoTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashuoTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashuoTest::Dump(0, i);\n  }\n  farmhashuoTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n#ifndef FARMHASH_SELF_TEST_GUARD\n#define FARMHASH_SELF_TEST_GUARD\n#include <cstdio>\n#include <iostream>\n#include <string.h>\n\nusing std::cout;\nusing std::cerr;\nusing std::endl;\nusing std::hex;\n\nstatic const uint64_t kSeed0 = 1234567;\nstatic const uint64_t kSeed1 = k0;\nstatic const int kDataSize = 1 << 20;\nstatic const int kTestSize = 300;\n#define kSeed128 Uint128(kSeed0, kSeed1)\n\nstatic char data[kDataSize];\n\nstatic int completed_self_tests = 0;\nstatic int errors = 0;\n\n// Initialize data to pseudorandom values.\nvoid Setup() {\n  if (completed_self_tests == 0) {\n    uint64_t a = 9;\n    uint64_t b = 777;\n    for (int i = 0; i < kDataSize; i++) {\n      a += b;\n      b += a;\n      a = (a ^ (a >> 41)) * k0;\n      b = (b ^ (b >> 41)) * k0 + i;\n      uint8_t u = b >> 37;\n      memcpy(data + i, &u, 1);  // uint8_t -> char\n    }\n  }\n}\n\nint NoteErrors() {\n#define NUM_SELF_TESTS 9\n  if (++completed_self_tests == NUM_SELF_TESTS)\n    std::exit(errors > 0);\n  return errors;\n}\n\ntemplate <typename T> inline bool IsNonZero(T x) {\n  return x != 0;\n}\n\ntemplate <> inline bool IsNonZero<uint128_t>(uint128_t x) {\n  return x != Uint128(0, 0);\n}\n\n#endif  // FARMHASH_SELF_TEST_GUARD\n\nnamespace farmhashxoTest {\n\nuint32_t CreateSeed(int offset, int salt) {\n  uint32_t h = static_cast<uint32_t>(salt & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h += static_cast<uint32_t>(offset & 0xffffffff);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  h = h * c1;\n  h ^= (h >> 17);\n  return h;\n}\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n#define SEED CreateSeed(offset, -1)\n#define SEED0 CreateSeed(offset, 0)\n#define SEED1 CreateSeed(offset, 1)\n\n#undef TESTING\n#define TESTING 1\n#if TESTING\nuint32_t expected[] = {\n1140953930u, 861465670u,\n3277735313u, 2681724312u,\n2598464059u, 797982799u,\n890626835u, 800175912u,\n2603993599u, 921001710u,\n1410420968u, 2134990486u,\n3283896453u, 1867689945u,\n2914424215u, 2244477846u,\n255297188u, 2992121793u,\n1110588164u, 4186314283u,\n161451183u, 3943596029u,\n4019337850u, 452431531u,\n283198166u, 2741341286u,\n3379021470u, 2557197665u,\n299850021u, 2532580744u,\n452473466u, 1706958772u,\n1298374911u, 3099673830u,\n2199864459u, 3696623795u,\n236935126u, 2976578695u,\n4055299123u, 3281581178u,\n1053458494u, 1882212500u,\n2305012065u, 2169731866u,\n3456121707u, 275903667u,\n458884671u, 3033004529u,\n3058973506u, 2379411653u,\n1898235244u, 1402319660u,\n2700149065u, 2699376854u,\n147814787u, 720739346u,\n2433714046u, 4222949502u,\n4220361840u, 1712034059u,\n3425469811u, 3690733394u,\n4148372108u, 1330324210u,\n594028478u, 2921867846u,\n1635026870u, 192883107u,\n780716741u, 1728752234u,\n3280331829u, 326029180u,\n3969463346u, 1436364519u,\n393215742u, 3349570000u,\n3824583307u, 1612122221u,\n2859809759u, 3808705738u,\n1379537552u, 1646032583u,\n2233466664u, 1432476832u,\n4023053163u, 2650381482u,\n2052294713u, 3552092450u,\n1628777059u, 1499109081u,\n3476440786u, 3829307897u,\n2960536756u, 1554038301u,\n1145519619u, 3190844552u,\n2902102606u, 3600725550u,\n237495366u, 540224401u,\n65721842u, 489963606u,\n1448662590u, 397635823u,\n1596489240u, 1562872448u,\n1790705123u, 2128624475u,\n180854224u, 2604346966u,\n1435705557u, 1262831810u,\n155445229u, 1672724608u,\n1669465176u, 1341975128u,\n663607706u, 2077310004u,\n3610042449u, 1911523866u,\n1043692997u, 1454396064u,\n2563776023u, 294527927u,\n1099072299u, 1389770549u,\n703505868u, 678706990u,\n2952353448u, 2026137563u,\n3603803785u, 629449419u,\n1933894405u, 3043213226u,\n226132789u, 2489287368u,\n1552847036u, 645684964u,\n3828089804u, 3632594520u,\n187883449u, 230403464u,\n3151491850u, 3272648435u,\n3729087873u, 1303930448u,\n2002861219u, 165370827u,\n916494250u, 1230085527u,\n3103338579u, 3064290191u,\n3807265751u, 3628174014u,\n231181488u, 851743255u,\n2295806711u, 1781190011u,\n2988893883u, 1554380634u,\n1142264800u, 3667013118u,\n1968445277u, 315203929u,\n2638023604u, 2290487377u,\n732137533u, 1909203251u,\n440398219u, 1891630171u,\n1380301172u, 1498556724u,\n4072067757u, 4165088768u,\n4204318635u, 441430649u,\n3931792696u, 197618179u,\n956300927u, 914413116u,\n3010839769u, 2837339569u,\n2148126371u, 1913303225u,\n3074915312u, 3117299654u,\n4139181436u, 2993479124u,\n3178848746u, 1357272220u,\n1438494951u, 507436733u,\n667183474u, 2084369203u,\n3854939912u, 1413396341u,\n126024219u, 146044391u,\n1016656857u, 3022024459u,\n3254014218u, 429095991u,\n990500595u, 3056862311u,\n985653208u, 1718653828u,\n623071693u, 366414107u,\n1771289760u, 2293458109u,\n3047342438u, 2991127487u,\n3120876698u, 1684583131u,\n3638043310u, 1170404994u,\n863214540u, 1087193030u,\n199124911u, 520792961u,\n3169775996u, 1577421232u,\n3331828431u, 1013201099u,\n1716848157u, 4033596884u,\n1770708857u, 4229339322u,\n1146169032u, 1434258493u,\n3824360466u, 3242407770u,\n1926419493u, 2649785113u,\n872586426u, 762243036u,\n2736953692u, 816692935u,\n1571283333u, 3555213933u,\n2266795890u, 3781899767u,\n4290630595u, 517646945u,\n3006163611u, 2180594090u,\n959214578u, 558910384u,\n1283799121u, 3047062993u,\n3830962609u, 2391606125u,\n3544509313u, 622325861u,\n834785312u, 382936554u,\n1421463872u, 788479970u,\n1825135056u, 2725923798u,\n580988377u, 2826990641u,\n247825043u, 3167748333u,\n812546227u, 2506885666u,\n2584372201u, 1758123094u,\n1891789696u, 389974094u,\n345313518u, 2022370576u,\n3886113119u, 3338548567u,\n1083486947u, 2583576230u,\n1776047957u, 1771384107u,\n3604937815u, 3198590202u,\n3027522813u, 4155628142u,\n4232136669u, 427759438u,\n4244322689u, 542201663u,\n1549591985u, 2856634168u,\n556609672u, 45845311u,\n1175961330u, 3948351189u,\n4165739882u, 4194218315u,\n1634635545u, 4151937410u,\n713127376u, 1467786451u,\n1327394015u, 2743592929u,\n2638154051u, 810082938u,\n3077742128u, 1062268187u,\n4084325664u, 3810665822u,\n3735739145u, 2794294783u,\n2335576331u, 2560479831u,\n690240711u, 997658837u,\n2442302747u, 3948961926u,\n3958366652u, 3067277639u,\n2059157774u, 1211737169u,\n1516711748u, 2339636583u,\n4188504038u, 59581167u,\n2767897792u, 1389679610u,\n2658147000u, 2643979752u,\n3758739543u, 4189944477u,\n1454470782u, 100876854u,\n2995362413u, 118817200u,\n3252925478u, 2062343506u,\n2804483644u, 3088828656u,\n1231633714u, 4168280671u,\n2931588131u, 3284356565u,\n1255909792u, 3130054947u,\n4173605289u, 1407328702u,\n1677744031u, 3532596884u,\n3162657845u, 3887208531u,\n2256541290u, 3459463480u,\n3740979556u, 259034107u,\n392987633u, 3233195759u,\n3606709555u, 3424793077u,\n315836068u, 3200749877u,\n4065431359u, 760633989u,\n2982018998u, 1811050648u,\n234531934u, 1115203611u,\n3897494162u, 1516407838u,\n1603559457u, 323296368u,\n2632963283u, 1778459926u,\n2879836826u, 2146672889u,\n3486330348u, 492621815u,\n1231665285u, 2457048126u,\n3438440082u, 2217471853u,\n3355404249u, 3275550588u,\n1052645068u, 862072556u,\n4110617119u, 3745267835u,\n2657392572u, 4279236653u,\n1688445808u, 701920051u,\n956734128u, 581695350u,\n3157862788u, 2585726058u,\n1192588249u, 1410111809u,\n1651193125u, 3326135446u,\n1073280453u, 97376972u,\n2513844237u, 2187968410u,\n3976859649u, 4267859263u,\n3429034542u, 564493077u,\n3000537321u, 479241367u,\n3845637831u, 2868987960u,\n51544337u, 1029173765u,\n393624922u, 704325635u,\n2357610553u, 1418509533u,\n2007814586u, 3866658271u,\n3082385053u, 735688735u,\n916110004u, 3283299459u,\n1051684175u, 1083796807u,\n4074716319u, 813690332u,\n144264390u, 1439630796u,\n1508556987u, 675582689u,\n3748881891u, 3195309868u,\n362884708u, 1616408198u,\n43233176u, 837301135u,\n881504822u, 3254795114u,\n1385506591u, 2799925823u,\n1469874582u, 3464841997u,\n497175391u, 3929484338u,\n3975771289u, 1798536177u,\n2926265846u, 1374242438u,\n3675707838u, 4205965408u,\n3153165629u, 1499475160u,\n187287713u, 548490821u,\n3255259608u, 4247675634u,\n1940181471u, 3779953975u,\n687167150u, 2319566715u,\n1742785722u, 785893184u,\n2296977392u, 2778575413u,\n1794720651u, 48131484u,\n4084891412u, 1160134629u,\n3737623280u, 823113169u,\n3423207646u, 3803213486u,\n710625654u, 4116162021u,\n3693420287u, 4167766971u,\n1666602807u, 295320990u,\n3513255468u, 2487440590u,\n234080704u, 4004655503u,\n2971762528u, 1479656873u,\n4090178629u, 4044418876u,\n391947536u, 1462554406u,\n3909295855u, 1239580330u,\n1515601363u, 2011102035u,\n1442068334u, 4265993528u,\n1191921695u, 2291355695u,\n4257172787u, 576405853u,\n314332944u, 4038839101u,\n55559918u, 2378985842u,\n711098718u, 2425317635u,\n1644327317u, 1401013391u,\n4193760037u, 2958260436u,\n3167371443u, 3062418115u,\n3800755475u, 3167030094u,\n3489648204u, 1405430357u,\n526177822u, 2602636307u,\n915406019u, 4264167741u,\n1484090483u, 3070944737u,\n254529415u, 4017058800u,\n1702710265u, 1029665228u,\n2000382906u, 3185573940u,\n1381258384u, 4036354071u,\n2900841028u, 2670703363u,\n2921748807u, 2899069938u,\n4130543625u, 688472265u,\n4186808827u, 1054670286u,\n1132985391u, 2840525968u,\n4175776103u, 338058159u,\n1735964501u, 1539305024u,\n3497121710u, 1568260669u,\n2227290760u, 146827036u,\n3977176001u, 4060134777u,\n857488494u, 250055052u,\n4284109679u, 2502815838u,\n2592281721u, 1603444633u,\n1390562014u, 1556658131u,\n616327404u, 2448966429u,\n3051191726u, 3891353218u,\n1213304082u, 762328245u,\n2239052397u, 1082330589u,\n2455957292u, 201837927u,\n405397452u, 3079886794u,\n2583939798u, 2848283092u,\n3750724631u, 883849006u,\n3204198988u, 3341327098u,\n1855234968u, 1982110346u,\n1485529487u, 541496720u,\n4117290321u, 3607433551u,\n2168864636u, 133643215u,\n1055817409u, 3847827123u,\n2960769387u, 4046101649u,\n1176127003u, 4015671361u,\n4243643405u, 2849988118u,\n517111221u, 1796672358u,\n2045051700u, 3452457457u,\n2948254999u, 2102063419u,\n1556410577u, 1536380876u,\n3776661467u, 3281002516u,\n1735616066u, 1539151988u,\n1087795162u, 3332431596u,\n685631442u, 1147951686u,\n95237878u, 2005032160u,\n4012206915u, 4224354805u,\n3204999386u, 2415262714u,\n1433635018u, 116647396u,\n83167836u, 2881562655u,\n2729416454u, 1029284767u,\n881378302u, 2159170082u,\n555057366u, 1169104445u,\n3963877000u, 1919171906u,\n336034862u, 2017579106u,\n4059340529u, 3020819343u,\n865146997u, 2473524405u,\n944743644u, 1694443528u,\n1804513294u, 2904752429u,\n617975720u, 3671562289u,\n260177668u, 505662155u,\n1885941445u, 2504509403u,\n2260041112u, 1019936943u,\n3722741628u, 1511077569u,\n3100701179u, 1379422864u,\n1535670711u, 773792826u,\n1103819072u, 2089123665u,\n1157547425u, 329152940u,\n4142587430u, 484732447u,\n2475035432u, 1120017626u,\n412145504u, 965125959u,\n324924679u, 2809286837u,\n2842141483u, 4029205195u,\n2974306813u, 515627448u,\n3791551981u, 1097806406u,\n3873078673u, 136118734u,\n1872130856u, 3632422367u,\n3574135531u, 4017075736u,\n1699452298u, 1403506686u,\n344414660u, 1189129691u,\n3487080616u, 1516736273u,\n1805475756u, 2562064338u,\n163335594u, 2732147834u,\n4077452507u, 2984955003u,\n4271866024u, 3071338162u,\n2347111903u, 873829983u,\n1948409509u, 1923531348u,\n459509140u, 771592405u,\n1750124750u, 2334938333u,\n213811117u, 2586632018u,\n185232757u, 4032960199u,\n2447383637u, 284777551u,\n1654276320u, 2687561076u,\n3512945009u, 308584855u,\n1861027147u, 4102279334u,\n3203802620u, 1692079268u,\n4250142168u, 2565680167u,\n1507046104u, 841195925u,\n520565830u, 3674576684u,\n38924274u, 3770488806u,\n2414430882u, 3978473838u,\n3703994407u, 69201295u,\n3099963860u, 1255084262u,\n690971838u, 3539996781u,\n3696902571u, 3593730713u,\n2363435042u, 54945052u,\n1785765213u, 184911581u,\n1586241476u, 1939595371u,\n2534883189u, 2432427547u,\n2374171993u, 2039128933u,\n2955715987u, 2295501078u,\n2741583197u, 1280920000u,\n686818699u, 1238742497u,\n3843660102u, 82177963u,\n1281043691u, 1121403845u,\n1697846708u, 284852964u,\n278661677u, 2889101923u,\n2127558730u, 713121337u,\n872502474u, 511142139u,\n1261140657u, 1747052377u,\n2108187161u, 927011680u,\n955328267u, 3821994995u,\n2707230761u, 4142246789u,\n4134691985u, 1958963937u,\n2498463509u, 1977988705u,\n1419293714u, 1636932722u,\n2567532373u, 4075249328u,\n240575705u, 1956681213u,\n2598802768u, 2025886508u,\n4104757832u, 3026358429u,\n3242615202u, 4026813725u,\n255108733u, 1845587644u,\n3573008472u, 3615577014u,\n1222733548u, 1205557630u,\n917608574u, 1363253259u,\n1541946015u, 3087190425u,\n1138008081u, 1444019663u,\n109793386u, 341851980u,\n857839960u, 2515339233u,\n156283211u, 1906768669u,\n3886713057u, 1276595523u,\n2809830736u, 460237542u,\n3420452099u, 142985419u,\n205970448u, 4198897105u,\n1950698961u, 2069753399u,\n1142216925u, 1113051162u,\n1033680610u, 4278599955u,\n1106466069u, 356742959u,\n531521052u, 3494863964u,\n225629455u, 3735275001u,\n3662626864u, 1750561299u,\n1012864651u, 2101846429u,\n1074553219u, 668829411u,\n992181339u, 3384018814u,\n3330664522u, 860966321u,\n1885071395u, 4233785523u,\n100741310u, 451656820u,\n2148187612u, 1063001151u,\n360256231u, 107312677u,\n3650357479u, 2390172694u,\n22452685u, 237319043u,\n3600462351u, 1216645846u,\n2088767754u, 164402616u,\n2418980170u, 926137824u,\n94638678u, 1689811113u,\n2751052984u, 1767810825u,\n271289013u, 3896132233u,\n103797041u, 1397772514u,\n3441135892u, 3323383489u,\n2491268371u, 1662561885u,\n1612872497u, 2986430557u,\n2756998822u, 207428029u,\n937973965u, 2791656726u,\n1949717207u, 2260498180u,\n2648427775u, 2360400900u,\n2080496169u, 486358863u,\n1582022990u, 1263709570u,\n1396468647u, 1377764574u,\n363008508u, 1293502429u,\n224580012u, 4252610345u,\n1435134775u, 1099809675u,\n533671980u, 1533438766u,\n1820532305u, 2776960536u,\n3374512975u, 3542220540u,\n822810075u, 3716663290u,\n1157398049u, 3752806924u,\n4081637863u, 337070226u,\n3866585976u, 359270190u,\n2110942730u, 3267551635u,\n644850146u, 1306761320u,\n746972907u, 934259457u,\n2341378668u, 2220373824u,\n1242645122u, 4109252858u,\n1625266099u, 1173698481u,\n383517064u, 896322512u,\n3377483696u, 1788337208u,\n455496839u, 3194373887u,\n1837689083u, 1336556841u,\n1658628529u, 2911512007u,\n3838343487u, 2757664765u,\n1537187340u, 3712582785u,\n367022558u, 3071359622u,\n3926147070u, 35432879u,\n3093195926u, 2561488770u,\n4273132307u, 3898950547u,\n2838251049u, 2103926083u,\n2549435227u, 536047554u,\n1858986613u, 2040551642u,\n1147412575u, 1972369852u,\n4166184983u, 3528794619u,\n4077477194u, 3565689036u,\n808048238u, 3826350461u,\n1359641525u, 1197100813u,\n265993036u, 1864569342u,\n725164342u, 2264788336u,\n1831223342u, 3329594980u,\n923017956u, 490608221u,\n3818634478u, 258154469u,\n1441714797u, 1174785921u,\n3833372385u, 3287246572u,\n1677395563u, 3569218731u,\n868981704u, 2163330264u,\n2649450292u, 500120236u,\n465161780u, 746438382u,\n1145009669u, 2520062970u,\n2810524030u, 1561519055u,\n1479878006u, 3864969305u,\n2686075657u, 4042710240u,\n3224066062u, 2774151984u,\n2226179547u, 1643626042u,\n2328730865u, 3160666939u,\n2107011431u, 96459446u,\n3920328742u, 3336407558u,\n829404209u, 1878067032u,\n1235983679u, 4237425634u,\n466519055u, 3870676863u,\n934312076u, 2952135524u,\n276949224u, 4100839753u,\n424001484u, 1955120893u,\n4015478120u, 1265237690u,\n427484362u, 4246879223u,\n3452969617u, 1724363362u,\n1553513184u, 834830418u,\n1858777639u, 3476334357u,\n4144030366u, 2450047160u,\n2950762705u, 4277111759u,\n358032121u, 2511026735u,\n167923105u, 2059208280u,\n251949572u, 3065234219u,\n1535473864u, 556796152u,\n1513237478u, 3150857516u,\n1103404394u, 198182691u,\n1476438092u, 2913077464u,\n207119516u, 3963810232u,\n2954651680u, 1535115487u,\n3051522276u, 4046477658u,\n917804636u, 864395565u,\n632704095u, 140762681u,\n1802040304u, 990407433u,\n3771506212u, 4106024923u,\n1287729497u, 2198985327u,\n4052924496u, 2926590471u,\n3084557148u, 1472898694u,\n1009870118u, 559702706u,\n4265214507u, 82077489u,\n3067891003u, 3295678907u,\n2402308151u, 1096697687u,\n464407878u, 4190838199u,\n4269578403u, 3060919438u,\n2899950405u, 3046872820u,\n733509243u, 1583801700u,\n40453902u, 3879773881u,\n1993425202u, 2185339100u,\n1877837196u, 3912423882u,\n3293122640u, 4104318469u,\n1679617763u, 3703603898u,\n8759461u, 2540185277u,\n1152198475u, 2038345882u,\n2503579743u, 1446869792u,\n2019419351u, 4051584612u,\n3178289407u, 3992503830u,\n2879018745u, 2719373510u,\n700836153u, 1675560450u,\n4121245793u, 2064715719u,\n343595772u, 1996164093u,\n3130433948u, 405251683u,\n2804817126u, 1607133689u,\n463852893u, 2864244470u,\n2224044848u, 4071581802u,\n2537107938u, 2246347953u,\n3207234525u, 2028708916u,\n2272418128u, 803575837u,\n38655481u, 2170452091u,\n3272166407u, 557660441u,\n4019147902u, 3841480082u,\n298459606u, 2600943364u,\n2440657523u, 255451671u,\n3424361375u, 779434428u,\n3088526123u, 490671625u,\n1322855877u, 3452203069u,\n3057021940u, 2285701422u,\n2014993457u, 2390431709u,\n2002090272u, 1568745354u,\n1783152480u, 823305654u,\n4053862835u, 2200236540u,\n3009412313u, 3184047862u,\n3032187389u, 4159715581u,\n2966902888u, 252986948u,\n1849329144u, 3160134214u,\n3420960112u, 3198900547u,\n749160960u, 379139040u,\n1208883495u, 1566527339u,\n3006227299u, 4194096960u,\n556075248u, 497404038u,\n1717327230u, 1496132623u,\n1775955687u, 1719108984u,\n1014328900u, 4189966956u,\n2108574735u, 2584236470u,\n684087286u, 531310503u,\n4264509527u, 773405691u,\n3088905079u, 3456882941u,\n3105682208u, 3382290593u,\n2289363624u, 3296306400u,\n4168438718u, 467441309u,\n777173623u, 3241407531u,\n1183994815u, 1132983260u,\n1610606159u, 2540270567u,\n2649684057u, 1397502982u,\n146657385u, 3318434267u,\n2109315753u, 3348545480u,\n3193669211u, 811750340u,\n1073256162u, 3571673088u,\n546596661u, 1017047954u,\n3403136990u, 2540585554u,\n1477047647u, 4145867423u,\n2826408201u, 3531646869u,\n784952939u, 943914610u,\n2717443875u, 3657384638u,\n1806867885u, 1903578924u,\n3985088434u, 1911188923u,\n1764002686u, 3672748083u,\n1832925325u, 241574049u,\n519948041u, 3181425568u,\n2939747257u, 1634174593u,\n3429894862u, 3529565564u,\n1089679033u, 240953857u,\n2025369941u, 2695166650u,\n517086873u, 2964595704u,\n3017658263u, 3828377737u,\n2144895011u, 994799311u,\n1184683823u, 4260564140u,\n308018483u, 4262383425u,\n1374752558u, 3431057723u,\n1572637805u, 383233885u,\n3188015819u, 4051263539u,\n233319221u, 3794788167u,\n2017406667u, 919677938u,\n4074952232u, 1683612329u,\n4213676186u, 327142514u,\n3032591014u, 4204155962u,\n206775997u, 2283918569u,\n2395147154u, 3427505379u,\n2211319468u, 4153726847u,\n2217060665u, 350160869u,\n2493667051u, 1648200185u,\n3441709766u, 1387233546u,\n140980u, 1891558063u,\n760080239u, 2088061981u,\n1580964938u, 740563169u,\n422986366u, 330624974u,\n4264507722u, 150928357u,\n2738323042u, 2948665536u,\n918718096u, 376390582u,\n3966098971u, 717653678u,\n3219466255u, 3799363969u,\n3424344721u, 3187805406u,\n375347278u, 3490350144u,\n1992212097u, 2263421398u,\n3855037968u, 1928519266u,\n3866327955u, 1129127000u,\n1782515131u, 2746577402u,\n3059200728u, 2108753646u,\n2738070963u, 1336849395u,\n1705302106u, 768287270u,\n1343511943u, 2247006571u,\n1956142255u, 1780259453u,\n3475618043u, 212490675u,\n622521957u, 917121602u,\n1852992332u, 1267987847u,\n3170016833u, 2549835613u,\n3299763344u, 2864033668u,\n3378768767u, 1236609378u,\n4169365948u, 3738062408u,\n2661022773u, 2006922227u,\n2760592161u, 3828932355u,\n2636387819u, 2616619070u,\n1237256330u, 3449066284u,\n2871755260u, 3729280948u,\n3862686086u, 431292293u,\n3285899651u, 786322314u,\n2531158535u, 724901242u,\n2377363130u, 1415970351u,\n1244759631u, 3263135197u,\n965248856u, 174024139u,\n2297418515u, 2954777083u,\n987586766u, 3206261120u,\n4059515114u, 3903854066u,\n1931934525u, 2287507921u,\n1827135136u, 1781944746u,\n574617451u, 2299034788u,\n2650140034u, 4081586725u,\n2482286699u, 1109175923u,\n458483596u, 618705848u,\n4059852729u, 1813855658u,\n4190721328u, 1129462471u,\n4089998050u, 3575732749u,\n2375584220u, 1037031473u,\n1623777358u, 3389003793u,\n546597541u, 352770237u,\n1383747654u, 3122687303u,\n1646071378u, 1164309901u,\n290870767u, 830691298u,\n929335420u, 3193251135u,\n989577914u, 3626554867u,\n591974737u, 3996958215u,\n3163711272u, 3071568023u,\n1516846461u, 3656006011u,\n2698625268u, 2510865430u,\n340274176u, 1167681812u,\n3698796465u, 3155218919u,\n4102288238u, 1673474350u,\n3069708839u, 2704165015u,\n1237411891u, 1854985978u,\n3646837503u, 3625406022u,\n921552000u, 1712976649u,\n3939149151u, 878608872u,\n3406359248u, 1068844551u,\n1834682077u, 4155949943u,\n2437686324u, 3163786257u,\n2645117577u, 1988168803u,\n747285578u, 1626463554u,\n1235300371u, 1256485167u,\n1914142538u, 4141546431u,\n3838102563u, 582664250u,\n1883344352u, 2083771672u,\n2611657933u, 2139079047u,\n2250573853u, 804336148u,\n3066325351u, 2770847216u,\n4275641370u, 1455750577u,\n3346357270u, 1674051445u,\n601221482u, 3992583643u,\n1402445097u, 3622527604u,\n2509017299u, 2966108111u,\n2557027816u, 900741486u,\n1790771021u, 2912643797u,\n2631381069u, 4014551783u,\n90375300u, 300318232u,\n3269968032u, 2679371729u,\n2664752123u, 3517585534u,\n3253901179u, 542270815u,\n1188641600u, 365479232u,\n2210121140u, 760762191u,\n1273768482u, 1216399252u,\n3484324231u, 4287337666u,\n16322182u, 643179562u,\n325675502u, 3652676161u,\n3120716054u, 3330259752u,\n1011990087u, 2990167340u,\n1097584090u, 3262252593u,\n1829409951u, 3665087267u,\n1214854475u, 2134299399u,\n3704419305u, 411263051u,\n1625446136u, 549838529u,\n4283196353u, 1342880802u,\n3460621305u, 1967599860u,\n4282843369u, 1275671016u,\n2544665755u, 853593042u,\n901109753u, 2682611693u,\n110631633u, 797487791u,\n1472073141u, 850464484u,\n797089608u, 3286110054u,\n350397471u, 2775631060u,\n366448238u, 3842907484u,\n2219863904u, 3623364733u,\n1850985302u, 4009616991u,\n294963924u, 3693536939u,\n3061255808u, 1615375832u,\n1920066675u, 4113028420u,\n4032223840u, 2318423400u,\n2701956286u, 4145497671u,\n3991532344u, 2536338351u,\n1679099863u, 1728968857u,\n449740816u, 2686506989u,\n685242457u, 97590863u,\n3258354115u, 1502282913u,\n1235084019u, 2151665147u,\n528459289u, 231097464u,\n2477280726u, 3651607391u,\n2091754612u, 1178454681u,\n980597335u, 1604483865u,\n1842333726u, 4146839064u,\n3213794286u, 2601416506u,\n754220096u, 3571436033u,\n488595746u, 1448097974u,\n4004834921u, 238887261u,\n3320337489u, 1416989070u,\n2928916831u, 4093725287u,\n186020771u, 2367569534u,\n3046087671u, 4090084518u,\n3548184546u, 679517009u,\n1962659444u, 3539886328u,\n4192003933u, 1678423485u,\n3827951761u, 3086277222u,\n2144472852u, 1390394371u,\n2976322029u, 1574517163u,\n3553313841u, 119173722u,\n1702434637u, 1766260771u,\n3629581771u, 1407497759u,\n895654784u, 751439914u,\n4008409498u, 215917713u,\n1482103833u, 695551833u,\n1288382231u, 2656990891u,\n2581779077u, 1570750352u,\n3710689053u, 1741390464u,\n2666411616u, 3533987737u,\n4289478316u, 3576119563u,\n4118694920u, 108199666u,\n3869794273u, 963183826u,\n2081410737u, 3796810515u,\n791123882u, 2525792704u,\n1036883117u, 136547246u,\n875691100u, 2592925324u,\n614302599u, 3013176417u,\n2689342539u, 427154472u,\n532957601u, 1228758574u,\n1898117151u, 1181643858u,\n1908591042u, 1464255968u,\n446980910u, 2984611177u,\n58509511u, 1046943619u,\n3508927906u, 2001585786u,\n2544767379u, 1525438381u,\n552181222u, 1959725830u,\n879448844u, 1348536411u,\n4242243590u, 2861338018u,\n1082052441u, 1034351453u,\n601175800u, 764077711u,\n530635011u, 3785343245u,\n2178026726u, 117256687u,\n2378297261u, 457568934u,\n76438221u, 4104954272u,\n956793873u, 3783168634u,\n2485968477u, 2381948487u,\n4226929450u, 3148473363u,\n2518273601u, 3569490233u,\n879369091u, 2180270337u,\n3674375989u, 1387729170u,\n977997984u, 4270646856u,\n568650985u, 951677556u,\n4213877384u, 2721005055u,\n1073364549u, 2563403831u,\n1678669911u, 66786703u,\n2273631661u, 1149351924u,\n3651298990u, 1581883443u,\n246723096u, 1895026827u,\n3810605772u, 3711056516u,\n4058833288u, 2193790614u,\n2080120290u, 3638638708u,\n2915672708u, 2263003308u,\n2361934197u, 4136767460u,\n1976115991u, 3448840877u,\n2019238520u, 225333538u,\n874340815u, 2976159827u,\n1555273378u, 3797521928u,\n1942347150u, 3262952567u,\n435997738u, 340403353u,\n2817830907u, 2078619498u,\n749534111u, 1178073973u,\n894654712u, 3361226032u,\n841092198u, 3288261538u,\n1696412169u, 1496966875u,\n697501571u, 1059158875u,\n3739946319u, 2481012988u,\n568983526u, 114945840u,\n1559249010u, 2218244008u,\n2841706923u, 1632780103u,\n4020169654u, 2087949619u,\n2438736103u, 24032648u,\n833416317u, 3787017905u,\n2373238993u, 2575395164u,\n3434544481u, 3228481067u,\n2542976862u, 2971726178u,\n2880371864u, 3642087909u,\n2407477975u, 2239080836u,\n1043714217u, 3894199764u,\n2235879182u, 203853421u,\n2933669448u, 2504940536u,\n834683330u, 425935223u,\n3560796393u, 3565833278u,\n1668000829u, 3683399154u,\n3414330886u, 1748785729u,\n1023171602u, 580966986u,\n2531038985u, 3227325488u,\n2657385925u, 2124704694u,\n233442446u, 1107045577u,\n3407293834u, 552770757u,\n3899097693u, 1067532701u,\n115667924u, 1406028344u,\n1707768231u, 3724015962u,\n2419657149u, 18613994u,\n2532882091u, 3476683808u,\n1560838678u, 811220224u,\n895961699u, 3762914298u,\n1328752423u, 1844996900u,\n1420427894u, 1848067707u,\n1210281744u, 904215228u,\n4055325594u, 1118521573u,\n2496554183u, 2579259919u,\n3996647489u, 3657647605u,\n325254059u, 3136157065u,\n3951522674u, 4052925250u,\n3341068436u, 2287683323u,\n1313073005u, 126005630u,\n2505120084u, 1194725057u,\n853746559u, 3555092974u,\n2689238752u, 49515858u,\n1244776042u, 1069300695u,\n61073168u, 1010661841u,\n1269521335u, 1902040126u,\n990632502u, 2378708922u,\n3858321250u, 1400735275u,\n2974699176u, 2771676666u,\n170995186u, 2877798589u,\n545726212u, 2225229957u,\n1086473152u, 3454177594u,\n3859483262u, 1499729584u,\n2088002891u, 2883475137u,\n3222194252u, 4144472319u,\n2212229854u, 4146740722u,\n567988835u, 1051332394u,\n3932046135u, 542648229u,\n3017852446u, 1277887997u,\n162888005u, 1669710469u,\n1492500905u, 553041029u,\n1434876932u, 533989516u,\n3817492747u, 584127807u,\n4147115982u, 2993670925u,\n4020312558u, 710021255u,\n3509733475u, 3587959456u,\n2088550465u, 1745399498u,\n2952242967u, 1259815443u,\n869648362u, 1404723176u,\n3947542735u, 1334333531u,\n3873471582u, 229399758u,\n59634866u, 3239516985u,\n3844250972u, 1275954779u,\n1385684948u, 2243700741u,\n2512155003u, 1685649437u,\n639306006u, 2524620206u,\n955360345u, 1646776457u,\n576786501u, 655707039u,\n2864351838u, 3736264674u,\n655621239u, 362070173u,\n1200907897u, 2384379464u,\n15823708u, 206117476u,\n3652870937u, 122927134u,\n1193310960u, 1093099415u,\n3696538026u, 4112584792u,\n1834541277u, 845639252u,\n2069527017u, 547588820u,\n4178147211u, 2827259351u,\n1764455305u, 3312003602u,\n940846775u, 1054995047u,\n2976960697u, 1934305529u,\n3095615046u, 3354962706u,\n2199137382u, 1005722394u,\n1875867180u, 2064356511u,\n3363633633u, 2688499147u,\n4019734130u, 3096333006u,\n2069509024u, 2906358341u,\n3247463123u, 4191788132u,\n2232866485u, 1456016086u,\n1422674894u, 867282151u,\n1851386407u, 1268304058u,\n1612503136u, 1739843072u,\n134947567u, 2978775774u,\n2051592101u, 1017127033u,\n1284167756u, 1090844589u,\n831688783u, 2079216362u,\n2079309682u, 1950585801u,\n1626991196u, 3644714163u,\n3678110059u, 898470030u,\n1117570988u, 2517572125u,\n3916646913u, 3182422972u,\n3630426828u, 969847973u,\n2835126238u, 53541366u,\n3427164640u, 3463937250u,\n3044785046u, 897322257u,\n103038235u, 3804506837u,\n3443872170u, 4185408854u,\n2557463241u, 4080940424u,\n3669923099u, 2789619871u,\n2048168570u, 2429169982u,\n3174690447u, 2513494106u,\n3099587829u, 2627855577u,\n1213061732u, 3143736628u,\n3482268149u, 1250714337u,\n3553412672u, 2689632914u,\n31648125u, 3872383625u,\n1565760579u, 36665130u,\n1282106920u, 359361724u,\n751041229u, 2257179590u,\n2915361862u, 280819225u,\n954406473u, 4101682199u,\n2907818413u, 4254297769u,\n3493178615u, 3755944354u,\n3539557658u, 3330196096u,\n4043533423u, 1134196225u,\n4177134659u, 127246419u,\n4213770762u, 1978302978u,\n2442615581u, 923049607u,\n1004426206u, 782768297u,\n2702745496u, 1880389457u,\n2410586681u, 1430106871u,\n4103323427u, 3168399477u,\n201787012u, 3105353527u,\n3716682375u, 3616334719u,\n3413209549u, 656672786u,\n526032790u, 2895072131u,\n2876965944u, 182894450u,\n456581318u, 2683752067u,\n1287916294u, 1270745752u,\n3877875910u, 3190666241u,\n3240336907u, 4024807233u,\n4227999465u, 2389301430u,\n1681224377u, 1576191191u,\n3599250276u, 2381111980u,\n3995044500u, 995595530u,\n3495321877u, 3956024585u,\n1611608524u, 3815677453u,\n1520987487u, 3669102590u,\n2062334396u, 1656117707u,\n5457134u, 3234118251u,\n4242065111u, 596879987u,\n470187419u, 2688566989u,\n3259870297u, 660100446u,\n1042378442u, 2206034096u,\n442236198u, 2542452448u,\n493137955u, 392411099u,\n3111186954u, 438250493u,\n947967568u, 1234595917u,\n4230082284u, 2762976773u,\n421203727u, 3728409592u,\n2870085764u, 1455086530u,\n2762099647u, 4011882747u,\n1785430706u, 3684427488u,\n1215981925u, 3227517889u,\n3269061963u, 4037515364u,\n1749401388u, 2167451566u,\n3168911474u, 4255057396u,\n2026092260u, 1736192508u,\n4123254745u, 2319366806u,\n3909727042u, 3114708966u,\n1938800693u, 680793595u,\n3933041672u, 616863613u,\n1525265867u, 2808224480u,\n2122290603u, 1211197714u,\n1186177814u, 2395325006u,\n3520488321u, 3979192396u,\n3540779343u, 4192918639u,\n1763872074u, 3402419930u,\n2736030448u, 1120335563u,\n1698949078u, 3993310631u,\n2947659998u, 1461045789u,\n1966048551u, 2228221363u,\n597941119u, 3498018399u,\n1441110751u, 2229999711u,\n393987327u, 454500547u,\n1222959566u, 567151340u,\n2496952483u, 1708770195u,\n3774764786u, 1492844524u,\n3308300614u, 805568076u,\n4068812294u, 3404648243u,\n868414882u, 177406999u,\n1608110313u, 642061169u,\n2093999089u, 222470301u,\n1027515771u, 3131251981u,\n2851936150u, 4272755262u,\n2763002551u, 1881527822u,\n1532845092u, 709643652u,\n682573592u, 1244104217u,\n440905170u, 1111321746u,\n796769556u, 2500467040u,\n3002618826u, 1112998535u,\n1188525643u, 4212674512u,\n1780193104u, 1243644607u,\n3691719535u, 2958853053u,\n2813437721u, 4036584207u,\n466635014u, 2277292580u,\n4082276003u, 1030800045u,\n1899531424u, 609466946u,\n1750863246u, 379050598u,\n3576413281u, 731493104u,\n2707384133u, 2289193651u,\n132259176u, 4115195437u,\n1769890695u, 2715470335u,\n3348954692u, 2166575624u,\n1819263183u, 2028531518u,\n2154809766u, 3672399742u,\n1142139448u, 88299682u,\n76727603u, 4198182186u,\n2304993586u, 1666387627u,\n2488475423u, 3832777692u,\n284366017u, 3359785538u,\n3469807328u, 2926494787u,\n1914195188u, 1134129972u,\n3829072836u, 2493478921u,\n3738499303u, 3311304980u,\n726951526u, 911080963u,\n932916545u, 2235559063u,\n2909742396u, 1765719309u,\n465269850u, 3803621553u,\n1456588655u, 508290328u,\n1490719640u, 3356513470u,\n2262196163u, 1451774941u,\n2908490783u, 251085588u,\n830410677u, 3172220325u,\n4039692645u, 1383603170u,\n3897208579u, 1940535730u,\n151909546u, 2384458112u,\n};\n\n// Return false only if offset is -1 and a spot check of 3 hashes all yield 0.\nbool Test(int offset, int len = 0) {\n#undef Check\n#undef IsAlive\n\n#define Check(x) do {                                                   \\\n  const uint32_t actual = (x), e = expected[index++];                   \\\n  bool ok = actual == e;                                                \\\n  if (!ok) {                                                            \\\n    cerr << \"expected \" << hex << e << \" but got \" << actual << endl;   \\\n    ++errors;                                                           \\\n  }                                                                     \\\n  assert(ok);                                                           \\\n} while (0)\n\n#define IsAlive(x) do { alive += IsNonZero(x); } while (0)\n\n  // After the following line is where the uses of \"Check\" and such will go.\n  static int index = 0;\nif (offset == -1) { int alive = 0; { uint64_t h = farmhashxo::Hash64WithSeeds(data, len++, SEED0, SEED1); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashxo::Hash64WithSeed(data, len++, SEED); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } { uint64_t h = farmhashxo::Hash64(data, len++); IsAlive(h >> 32); IsAlive((h << 32) >> 32); } len -= 3; return alive > 0; }\n{ uint64_t h = farmhashxo::Hash64WithSeeds(data + offset, len, SEED0, SEED1); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint64_t h = farmhashxo::Hash64WithSeed(data + offset, len, SEED); Check(h >> 32); Check((h << 32) >> 32); }\n{ uint64_t h = farmhashxo::Hash64(data + offset, len); Check(h >> 32); Check((h << 32) >> 32); }\n\n  return true;\n#undef Check\n#undef IsAlive\n}\n\nint RunTest() {\n  Setup();\n  int i = 0;\n  cout << \"Running farmhashxoTest\";\n  if (!Test(-1)) {\n    cout << \"... Unavailable\\n\";\n    return NoteErrors();\n  }\n  // Good.  The function is attempting to hash, so run the full test.\n  int errors_prior_to_test = errors;\n  for ( ; i < kTestSize - 1; i++) {\n    Test(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    Test(0, i);\n  }\n  Test(0, kDataSize);\n  cout << (errors == errors_prior_to_test ? \"... OK\\n\" : \"... Failed\\n\");\n  return NoteErrors();\n}\n\n#else\n\n// After the following line is where the code to print hash codes will go.\nvoid Dump(int offset, int len) {\n{ uint64_t h = farmhashxo::Hash64WithSeeds(data + offset, len, SEED0, SEED1); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint64_t h = farmhashxo::Hash64WithSeed(data + offset, len, SEED); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n{ uint64_t h = farmhashxo::Hash64(data + offset, len); cout << (h >> 32) << \"u, \" << ((h << 32) >> 32) << \"u,\" << endl; }\n}\n\n#endif\n\n#undef SEED\n#undef SEED1\n#undef SEED0\n\n}  // namespace farmhashxoTest\n\n#if TESTING\n\n//static int farmhashxoTestResult = farmhashxoTest::RunTest();\nTEST(farmhash, xo) { farmhashxoTest::RunTest(); }\n\n#else\nint main(int argc, char** argv) {\n  Setup();\n  cout << \"uint32_t expected[] = {\\n\";\n  int i = 0;\n  for ( ; i < kTestSize - 1; i++) {\n    farmhashxoTest::Dump(i * i, i);\n  }\n  for ( ; i < kDataSize; i += i / 7) {\n    farmhashxoTest::Dump(0, i);\n  }\n  farmhashxoTest::Dump(0, kDataSize);\n  cout << \"};\\n\";\n}\n#endif\n"
  },
  {
    "path": "folly/external/fast-crc32/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"avx512_crc32c_v8s3x4\",\n    srcs = [\n        \"avx512_crc32c_v8s3x4.cpp\",\n    ],\n    headers = [\n        \"avx512_crc32c_v8s3x4.h\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:x86_64\": [\n            \"-mavx512f\",\n            \"-mavx512vl\",\n            \"-mpclmul\",\n        ],\n        \"ovr_config//os:android-x86_64\": [\"-mpclmul\"],\n    }),\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"sse_crc32c_v8s3x3\",\n    srcs = [\n        \"sse_crc32c_v8s3x3.cpp\",\n    ],\n    headers = [\n        \"sse_crc32c_v8s3x3.h\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:android-x86_64\": [\"-mpclmul\"],\n    }),\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"neon_crc32c_v3s4x2e_v2\",\n    srcs = [\n        \"neon_crc32c_v3s4x2e_v2.cpp\",\n    ],\n    headers = [\n        \"neon_crc32c_v3s4x2e_v2.h\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//cpu:arm64\": [\n            \"-march=armv8-a+crypto+crc\",\n        ],\n    }),\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:portability\",\n        \"//folly/system:aux_vector\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"neon_eor3_crc32c_v8s2x4e_s2x1\",\n    srcs = [\n        \"neon_eor3_crc32c_v8s2x4e_s2x1.cpp\",\n    ],\n    headers = [\n        \"neon_eor3_crc32c_v8s2x4e_s2x1.h\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:android-arm64\": [\"-march=armv8.2-a+crypto+sha3\"],\n    }),\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:portability\",\n        \"//folly/system:aux_vector\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"neon_eor3_crc32_v8s2x4e_s1x2\",\n    srcs = [\n        \"neon_eor3_crc32_v8s2x4e_s1x2.cpp\",\n    ],\n    headers = [\n        \"neon_eor3_crc32_v8s2x4e_s1x2.h\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:android-arm64\": [\"-march=armv8.2-a+crypto+sha3\"],\n    }),\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:portability\",\n        \"//folly/system:aux_vector\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/fast-crc32/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# NOTE: This file is manually maintained, not auto-generated.\n# Update this file when the corresponding BUCK file changes.\n\n# x86 CRC32 implementations - built on all platforms\n# Source files provide abort() stubs on non-x86 via #if guards\nfolly_add_library(\n  NAME avx512_crc32c_v8s3x4\n  SRCS\n    avx512_crc32c_v8s3x4.cpp\n  HEADERS\n    avx512_crc32c_v8s3x4.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\n# Apply AVX512 flags only on x86 (not needed on Windows)\nif (IS_X86_64_ARCH AND NOT CMAKE_SYSTEM_NAME STREQUAL \"Windows\")\n  target_compile_options(folly_external_fast-crc32_avx512_crc32c_v8s3x4_obj\n    PRIVATE -mavx512f -mavx512vl)\nendif()\nif (COMPILER_HAS_M_PCLMUL)\n  target_compile_options(folly_external_fast-crc32_avx512_crc32c_v8s3x4_obj\n    PRIVATE -mpclmul)\nendif()\n\nfolly_add_library(\n  NAME sse_crc32c_v8s3x3\n  SRCS\n    sse_crc32c_v8s3x3.cpp\n  HEADERS\n    sse_crc32c_v8s3x3.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nif (COMPILER_HAS_M_PCLMUL)\n  target_compile_options(folly_external_fast-crc32_sse_crc32c_v8s3x3_obj\n    PRIVATE -mpclmul)\nendif()\n\n# ARM NEON CRC32 implementations - built on all platforms\n# Source files provide abort() stubs on non-ARM via #if guards\nfolly_add_library(\n  NAME neon_crc32c_v3s4x2e_v2\n  SRCS\n    neon_crc32c_v3s4x2e_v2.cpp\n  HEADERS\n    neon_crc32c_v3s4x2e_v2.h\n  DEPS\n    folly_portability\n    folly_system_aux_vector\n)\n\nfolly_add_library(\n  NAME neon_eor3_crc32c_v8s2x4e_s2x1\n  SRCS\n    neon_eor3_crc32c_v8s2x4e_s2x1.cpp\n  HEADERS\n    neon_eor3_crc32c_v8s2x4e_s2x1.h\n  DEPS\n    folly_portability\n    folly_system_aux_vector\n)\n\nfolly_add_library(\n  NAME neon_eor3_crc32_v8s2x4e_s1x2\n  SRCS\n    neon_eor3_crc32_v8s2x4e_s1x2.cpp\n  HEADERS\n    neon_eor3_crc32_v8s2x4e_s1x2.h\n  DEPS\n    folly_portability\n    folly_system_aux_vector\n)\n"
  },
  {
    "path": "folly/external/fast-crc32/avx512_crc32c_v8s3x4.cpp",
    "content": "/* Generated by https://github.com/corsix/fast-crc32/ using: */\n/* ./generate -i avx512 -p crc32c -a v8s3x4 */\n/* MIT licensed */\n\n#include \"folly/external/fast-crc32/avx512_crc32c_v8s3x4.h\"\n\n#include <stdint.h>\n\n#define CRC_EXPORT extern\n#if !defined(FOLLY_ENABLE_AVX512_CRC32C_V8S3X4)\n#include <stdlib.h>\nnamespace folly::detail {\nCRC_EXPORT uint32_t avx512_crc32c_v8s3x4(const uint8_t*, size_t, uint32_t) {\n  abort(); // not implemented on this platform\n}\n} // namespace folly::detail\n#else\n#include <nmmintrin.h> // @donotremove\n#include <wmmintrin.h>\n#include <immintrin.h>\n\n#if defined(_MSC_VER)\n#define CRC_AINLINE static __forceinline\n#define CRC_ALIGN(n) __declspec(align(n))\n#else\n#define CRC_AINLINE static __inline __attribute__((always_inline))\n#define CRC_ALIGN(n) __attribute__((aligned(n)))\n#endif\n\n#define clmul_lo(a, b) (_mm_clmulepi64_si128((a), (b), 0))\n#define clmul_hi(a, b) (_mm_clmulepi64_si128((a), (b), 17))\n\nnamespace folly::detail {\nCRC_AINLINE __m128i clmul_scalar(uint32_t a, uint32_t b) {\n  return _mm_clmulepi64_si128(_mm_cvtsi32_si128(a), _mm_cvtsi32_si128(b), 0);\n}\n\nstatic uint32_t xnmodp(uint64_t n) /* x^n mod P, in log(n) time */ {\n  uint64_t stack = ~(uint64_t)1;\n  uint32_t acc, low;\n  for (; n > 191; n = (n >> 1) - 16) {\n    stack = (stack << 1) + (n & 1);\n  }\n  stack = ~stack;\n  acc = ((uint32_t)0x80000000) >> (n & 31);\n  for (n >>= 5; n; --n) {\n    acc = _mm_crc32_u32(acc, 0);\n  }\n  while ((low = stack & 1), stack >>= 1) {\n    __m128i x = _mm_cvtsi32_si128(acc);\n    uint64_t y = _mm_cvtsi128_si64(_mm_clmulepi64_si128(x, x, 0));\n    acc = _mm_crc32_u64(0, y << low);\n  }\n  return acc;\n}\n\nCRC_AINLINE __m128i crc_shift(uint32_t crc, size_t nbytes) {\n  return clmul_scalar(crc, xnmodp(nbytes * 8 - 33));\n}\n\nFOLLY_TARGET_ATTRIBUTE(\"avx512f,avx512vl,sse4.2\")\nCRC_EXPORT uint32_t avx512_crc32c_v8s3x4(const uint8_t* buf, size_t len, uint32_t crc0) {\n  for (; len && ((uintptr_t)buf & 7); --len) {\n    crc0 = _mm_crc32_u8(crc0, *buf++);\n  }\n  if (((uintptr_t)buf & 8) && len >= 8) {\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n    buf += 8;\n    len -= 8;\n  }\n  if (len >= 224) {\n    size_t blk = (len - 0) / 224;\n    size_t klen = blk * 32;\n    const uint8_t* buf2 = buf + klen * 3;\n    uint32_t crc1 = 0;\n    uint32_t crc2 = 0;\n    __m128i vc0;\n    __m128i vc1;\n    __m128i vc2;\n    uint64_t vc;\n    /* First vector chunk. */\n    __m128i x0 = _mm_loadu_si128((const __m128i*)buf2), y0;\n    __m128i x1 = _mm_loadu_si128((const __m128i*)(buf2 + 16)), y1;\n    __m128i x2 = _mm_loadu_si128((const __m128i*)(buf2 + 32)), y2;\n    __m128i x3 = _mm_loadu_si128((const __m128i*)(buf2 + 48)), y3;\n    __m128i x4 = _mm_loadu_si128((const __m128i*)(buf2 + 64)), y4;\n    __m128i x5 = _mm_loadu_si128((const __m128i*)(buf2 + 80)), y5;\n    __m128i x6 = _mm_loadu_si128((const __m128i*)(buf2 + 96)), y6;\n    __m128i x7 = _mm_loadu_si128((const __m128i*)(buf2 + 112)), y7;\n    __m128i k;\n    k = _mm_setr_epi32(0x6992cea2, 0, 0x0d3b6092, 0);\n    buf2 += 128;\n    len -= 224;\n    /* Main loop. */\n    while (len >= 224) {\n      y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n      y1 = clmul_lo(x1, k), x1 = clmul_hi(x1, k);\n      y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n      y3 = clmul_lo(x3, k), x3 = clmul_hi(x3, k);\n      y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n      y5 = clmul_lo(x5, k), x5 = clmul_hi(x5, k);\n      y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n      y7 = clmul_lo(x7, k), x7 = clmul_hi(x7, k);\n      x0 = _mm_ternarylogic_epi64(x0, y0, _mm_loadu_si128((const __m128i*)buf2), 0x96);\n      x1 = _mm_ternarylogic_epi64(x1, y1, _mm_loadu_si128((const __m128i*)(buf2 + 16)), 0x96);\n      x2 = _mm_ternarylogic_epi64(x2, y2, _mm_loadu_si128((const __m128i*)(buf2 + 32)), 0x96);\n      x3 = _mm_ternarylogic_epi64(x3, y3, _mm_loadu_si128((const __m128i*)(buf2 + 48)), 0x96);\n      x4 = _mm_ternarylogic_epi64(x4, y4, _mm_loadu_si128((const __m128i*)(buf2 + 64)), 0x96);\n      x5 = _mm_ternarylogic_epi64(x5, y5, _mm_loadu_si128((const __m128i*)(buf2 + 80)), 0x96);\n      x6 = _mm_ternarylogic_epi64(x6, y6, _mm_loadu_si128((const __m128i*)(buf2 + 96)), 0x96);\n      x7 = _mm_ternarylogic_epi64(x7, y7, _mm_loadu_si128((const __m128i*)(buf2 + 112)), 0x96);\n      crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n      crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen));\n      crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2));\n      crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 8));\n      crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 8));\n      crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 8));\n      crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 16));\n      crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 16));\n      crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 16));\n      crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 24));\n      crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 24));\n      crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 24));\n      buf += 32;\n      buf2 += 128;\n      len -= 224;\n    }\n    /* Reduce x0 ... x7 to just x0. */\n    k = _mm_setr_epi32(0xf20c0dfe, 0, 0x493c7d27, 0);\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n    x0 = _mm_ternarylogic_epi64(x0, y0, x1, 0x96);\n    x2 = _mm_ternarylogic_epi64(x2, y2, x3, 0x96);\n    x4 = _mm_ternarylogic_epi64(x4, y4, x5, 0x96);\n    x6 = _mm_ternarylogic_epi64(x6, y6, x7, 0x96);\n    k = _mm_setr_epi32(0x3da6d0cb, 0, 0xba4fc28e, 0);\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    x0 = _mm_ternarylogic_epi64(x0, y0, x2, 0x96);\n    x4 = _mm_ternarylogic_epi64(x4, y4, x6, 0x96);\n    k = _mm_setr_epi32(0x740eef02, 0, 0x9e4addf8, 0);\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    x0 = _mm_ternarylogic_epi64(x0, y0, x4, 0x96);\n    /* Final scalar chunk. */\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n    crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen));\n    crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2));\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 8));\n    crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 8));\n    crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 8));\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 16));\n    crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 16));\n    crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 16));\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 24));\n    crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 24));\n    crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 24));\n    vc0 = crc_shift(crc0, klen * 2 + blk * 128);\n    vc1 = crc_shift(crc1, klen + blk * 128);\n    vc2 = crc_shift(crc2, 0 + blk * 128);\n    vc = _mm_extract_epi64(_mm_ternarylogic_epi64(vc0, vc1, vc2, 0x96), 0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    crc0 = _mm_crc32_u64(0, _mm_extract_epi64(x0, 0));\n    crc0 = _mm_crc32_u64(crc0, vc ^ _mm_extract_epi64(x0, 1));\n    buf = buf2;\n  }\n  for (; len >= 8; buf += 8, len -= 8) {\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n  }\n  for (; len; --len) {\n    crc0 = _mm_crc32_u8(crc0, *buf++);\n  }\n  return crc0;\n}\n} // namespace folly::detail\n#endif\n"
  },
  {
    "path": "folly/external/fast-crc32/avx512_crc32c_v8s3x4.h",
    "content": "#pragma once\n#include <cstddef>\n#include <cstdint>\n\n#include <folly/Portability.h>\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2) && defined(__AVX512VL__) && defined(__AVX512F__)\n#define FOLLY_ENABLE_AVX512_CRC32C_V8S3X4 1\n#endif\n\nnamespace folly::detail {\nuint32_t avx512_crc32c_v8s3x4(const uint8_t* buf, size_t len, uint32_t crc0);\n}\n"
  },
  {
    "path": "folly/external/fast-crc32/neon_crc32c_v3s4x2e_v2.cpp",
    "content": "/* Generated by https://github.com/corsix/fast-crc32/ using: */\n/* ./generate -i neon -p crc32c -a v3s4x2e_v2 */\n/* MIT licensed */\n\n#include \"folly/external/fast-crc32/neon_crc32c_v3s4x2e_v2.h\"\n#include <folly/system/AuxVector.h>\n#include <folly/Portability.h>\n\n#include <stddef.h>\n#include <stdint.h>\n\n#define CRC_EXPORT extern\n\nnamespace folly::detail {\n[[maybe_unused]] static ElfHwCaps hwcaps() {\n  static ElfHwCaps caps;\n  return caps;\n}\n}\n\n#if !(FOLLY_AARCH64 && FOLLY_NEON && FOLLY_ARM_FEATURE_CRYPTO && FOLLY_ARM_FEATURE_CRC32)\n#include <stdlib.h>\nnamespace folly::detail {\nCRC_EXPORT uint32_t neon_crc32c_v3s4x2e_v2(const uint8_t*, size_t, uint32_t) {\n  abort(); // not implemented on this platform\n}\n\nCRC_EXPORT bool has_neon_crc32c_v3s4x2e_v2() {\n  return false;\n}\n}\n#else\n#include <arm_acle.h>\n#include <arm_neon.h>\n\n#if defined(_MSC_VER)\n#define CRC_AINLINE static __forceinline\n#define CRC_ALIGN(n) __declspec(align(n))\n#else\n#define CRC_AINLINE static __inline __attribute__((always_inline))\n#define CRC_ALIGN(n) __attribute__((aligned(n)))\n#endif\n\nnamespace folly::detail {\nCRC_AINLINE uint64x2_t clmul_lo_e(uint64x2_t a, uint64x2_t b, uint64x2_t c) {\n  return veorq_u64(vreinterpretq_u64_p128(vmull_p64(a[0], b[0])), c);\n}\n\nCRC_AINLINE uint64x2_t clmul_hi_e(uint64x2_t a, uint64x2_t b, uint64x2_t c) {\n  return veorq_u64(vreinterpretq_u64_p128(vmull_high_p64(vreinterpretq_p64_u64(a), vreinterpretq_p64_u64(b))), c);\n}\n\nCRC_AINLINE uint64x2_t clmul_scalar(uint32_t a, uint32_t b) {\n  return vreinterpretq_u64_p128(vmull_p64(a, b));\n}\n\nstatic uint32_t xnmodp(uint64_t n) /* x^n mod P, in log(n) time */ {\n  uint64_t stack = ~(uint64_t)1;\n  uint32_t acc, low;\n  for (; n > 191; n = (n >> 1) - 16) {\n    stack = (stack << 1) + (n & 1);\n  }\n  stack = ~stack;\n  acc = ((uint32_t)0x80000000) >> (n & 31);\n  for (n >>= 5; n; --n) {\n    acc = __crc32cw(acc, 0);\n  }\n  while ((low = stack & 1), stack >>= 1) {\n    poly8x8_t x = vreinterpret_p8_u64(vmov_n_u64(acc));\n    uint64_t y = vgetq_lane_u64(vreinterpretq_u64_p16(vmull_p8(x, x)), 0);\n    acc = __crc32cd(0, y << low);\n  }\n  return acc;\n}\n\nCRC_AINLINE uint64x2_t crc_shift(uint32_t crc, size_t nbytes) {\n  return clmul_scalar(crc, xnmodp(nbytes * 8 - 33));\n}\n\nFOLLY_TARGET_ATTRIBUTE(\"+crc\")\nCRC_EXPORT bool has_neon_crc32c_v3s4x2e_v2() {\n  auto caps = hwcaps();\n\n  return caps.aarch64_fp() && caps.aarch64_asimd() && caps.aarch64_pmull() &&\n      caps.aarch64_crc32();\n}\n\nCRC_EXPORT uint32_t neon_crc32c_v3s4x2e_v2(const uint8_t* buf, size_t len, uint32_t crc0) {\n  for (; len && ((uintptr_t)buf & 7); --len) {\n    crc0 = __crc32cb(crc0, *buf++);\n  }\n  if (((uintptr_t)buf & 8) && len >= 8) {\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n    buf += 8;\n    len -= 8;\n  }\n  if (len >= 112) {\n    const uint8_t* end = buf + len;\n    size_t blk = (len - 0) / 112;\n    size_t klen = blk * 16;\n    const uint8_t* buf2 = buf + klen * 4;\n    const uint8_t* limit = buf + klen - 32;\n    uint32_t crc1 = 0;\n    uint32_t crc2 = 0;\n    uint32_t crc3 = 0;\n    uint64x2_t vc0;\n    uint64x2_t vc1;\n    uint64x2_t vc2;\n    uint64x2_t vc3;\n    uint64_t vc;\n    /* First vector chunk. */\n    uint64x2_t x0 = vld1q_u64((const uint64_t*)buf2), y0;\n    uint64x2_t x1 = vld1q_u64((const uint64_t*)(buf2 + 16)), y1;\n    uint64x2_t x2 = vld1q_u64((const uint64_t*)(buf2 + 32)), y2;\n    uint64x2_t k;\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x1c291d04, 0xddc0152b}; k = vld1q_u64(k_); }\n    buf2 += 48;\n    /* Main loop. */\n    while (buf <= limit) {\n      y0 = clmul_lo_e(x0, k, vld1q_u64((const uint64_t*)buf2)), x0 = clmul_hi_e(x0, k, y0);\n      y1 = clmul_lo_e(x1, k, vld1q_u64((const uint64_t*)(buf2 + 16))), x1 = clmul_hi_e(x1, k, y1);\n      y2 = clmul_lo_e(x2, k, vld1q_u64((const uint64_t*)(buf2 + 32))), x2 = clmul_hi_e(x2, k, y2);\n      crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n      crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen));\n      crc2 = __crc32cd(crc2, *(const uint64_t*)(buf + klen * 2));\n      crc3 = __crc32cd(crc3, *(const uint64_t*)(buf + klen * 3));\n      crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n      crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 8));\n      crc2 = __crc32cd(crc2, *(const uint64_t*)(buf + klen * 2 + 8));\n      crc3 = __crc32cd(crc3, *(const uint64_t*)(buf + klen * 3 + 8));\n      buf += 16;\n      buf2 += 48;\n    }\n    /* Reduce x0 ... x2 to just x0. */\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xf20c0dfe, 0x493c7d27}; k = vld1q_u64(k_); }\n    y0 = clmul_lo_e(x0, k, x1), x0 = clmul_hi_e(x0, k, y0);\n    x1 = x2;\n    y0 = clmul_lo_e(x0, k, x1), x0 = clmul_hi_e(x0, k, y0);\n    /* Final scalar chunk. */\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n    crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen));\n    crc2 = __crc32cd(crc2, *(const uint64_t*)(buf + klen * 2));\n    crc3 = __crc32cd(crc3, *(const uint64_t*)(buf + klen * 3));\n    crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n    crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 8));\n    crc2 = __crc32cd(crc2, *(const uint64_t*)(buf + klen * 2 + 8));\n    crc3 = __crc32cd(crc3, *(const uint64_t*)(buf + klen * 3 + 8));\n    vc0 = crc_shift(crc0, klen * 3 + blk * 48);\n    vc1 = crc_shift(crc1, klen * 2 + blk * 48);\n    vc2 = crc_shift(crc2, klen + blk * 48);\n    vc3 = crc_shift(crc3, 0 + blk * 48);\n    vc = vgetq_lane_u64(veorq_u64(veorq_u64(vc0, vc1), veorq_u64(vc2, vc3)), 0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    crc0 = __crc32cd(0, vgetq_lane_u64(x0, 0));\n    crc0 = __crc32cd(crc0, vc ^ vgetq_lane_u64(x0, 1));\n    buf = buf2;\n    len = end - buf;\n  }\n  if (len >= 32) {\n    /* First vector chunk. */\n    uint64x2_t x0 = vld1q_u64((const uint64_t*)buf), y0;\n    uint64x2_t x1 = vld1q_u64((const uint64_t*)(buf + 16)), y1;\n    uint64x2_t k;\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x3da6d0cb, 0xba4fc28e}; k = vld1q_u64(k_); }\n    x0 = veorq_u64((uint64x2_t){crc0, 0}, x0);\n    buf += 32;\n    len -= 32;\n    /* Main loop. */\n    while (len >= 32) {\n      y0 = clmul_lo_e(x0, k, vld1q_u64((const uint64_t*)buf)), x0 = clmul_hi_e(x0, k, y0);\n      y1 = clmul_lo_e(x1, k, vld1q_u64((const uint64_t*)(buf + 16))), x1 = clmul_hi_e(x1, k, y1);\n      buf += 32;\n      len -= 32;\n    }\n    /* Reduce x0 ... x1 to just x0. */\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xf20c0dfe, 0x493c7d27}; k = vld1q_u64(k_); }\n    y0 = clmul_lo_e(x0, k, x1), x0 = clmul_hi_e(x0, k, y0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    crc0 = __crc32cd(0, vgetq_lane_u64(x0, 0));\n    crc0 = __crc32cd(crc0, vgetq_lane_u64(x0, 1));\n  }\n  for (; len >= 8; buf += 8, len -= 8) {\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n  }\n  for (; len; --len) {\n    crc0 = __crc32cb(crc0, *buf++);\n  }\n  return crc0;\n}\n} // namespace folly::detail\n#endif\n"
  },
  {
    "path": "folly/external/fast-crc32/neon_crc32c_v3s4x2e_v2.h",
    "content": "#pragma once\n#include <cstddef>\n#include <cstdint>\n\nnamespace folly::detail {\nuint32_t neon_crc32c_v3s4x2e_v2(const uint8_t* buf, size_t len, uint32_t crc0);\nbool has_neon_crc32c_v3s4x2e_v2();\n}\n"
  },
  {
    "path": "folly/external/fast-crc32/neon_eor3_crc32_v8s2x4e_s1x2.cpp",
    "content": "/* @Generated by https://github.com/corsix/fast-crc32/ using: */\n/* ./generate -i neon_eor3 -p crc32 -a v9s3x2e_s3 */\n/* MIT licensed */\n\n#include \"folly/external/fast-crc32/neon_eor3_crc32_v8s2x4e_s1x2.h\"\n#include <folly/system/AuxVector.h>\n#include <folly/Portability.h>\n\n#include <stddef.h>\n#include <stdint.h>\n\n#define CRC_EXPORT extern\n\nnamespace folly::detail {\n[[maybe_unused]] static ElfHwCaps hwcaps() {\n  static ElfHwCaps caps;\n  return caps;\n}\n}\n\n#if !(FOLLY_AARCH64 && FOLLY_NEON && FOLLY_ARM_FEATURE_CRYPTO && FOLLY_ARM_FEATURE_CRC32 && FOLLY_ARM_FEATURE_SHA3)\n#include <stdlib.h>\nnamespace folly::detail {\nCRC_EXPORT uint32_t neon_eor3_crc32_v8s2x4e_s1x2(const uint8_t*, size_t, uint32_t) {\n  abort(); // not implemented on this platform\n}\n\nCRC_EXPORT uint32_t neon_eor3_crc32_small(const uint8_t*, size_t, uint32_t) {\n  abort(); // not implemented on this platform\n}\n\nCRC_EXPORT bool has_neon_eor3_crc32_v8s2x4e_s1x2() {\n  return false;\n}\n}\n#else\n#include <arm_acle.h>\n#include <arm_neon.h>\n\n#if defined(_MSC_VER)\n#define CRC_AINLINE static __forceinline\n#define CRC_ALIGN(n) __declspec(align(n))\n#else\n#define CRC_AINLINE static __inline __attribute__((always_inline))\n#define CRC_ALIGN(n) __attribute__((aligned(n)))\n#endif\n\nnamespace folly::detail {\nCRC_AINLINE uint64x2_t clmul_lo(uint64x2_t a, uint64x2_t b) {\n  return vreinterpretq_u64_p128(vmull_p64(a[0], b[0]));\n}\n\nCRC_AINLINE uint64x2_t clmul_hi(uint64x2_t a, uint64x2_t b) {\n  return vreinterpretq_u64_p128(vmull_high_p64(vreinterpretq_p64_u64(a), vreinterpretq_p64_u64(b)));\n}\n\nCRC_AINLINE uint64x2_t clmul_scalar(uint32_t a, uint32_t b) {\n  return vreinterpretq_u64_p128(vmull_p64(a, b));\n}\n\nstatic uint32_t xnmodp(uint64_t n) /* x^n mod P, in log(n) time */ {\n  uint64_t stack = ~(uint64_t)1;\n  uint32_t acc, low;\n  for (; n > 191; n = (n >> 1) - 16) {\n    stack = (stack << 1) + (n & 1);\n  }\n  stack = ~stack;\n  acc = ((uint32_t)0x80000000) >> (n & 31);\n  for (n >>= 5; n; --n) {\n    acc = __crc32w(acc, 0);\n  }\n  while ((low = stack & 1), stack >>= 1) {\n    poly8x8_t x = vreinterpret_p8_u64(vmov_n_u64(acc));\n    uint64_t y = vgetq_lane_u64(vreinterpretq_u64_p16(vmull_p8(x, x)), 0);\n    acc = __crc32d(0, y << low);\n  }\n  return acc;\n}\n\nCRC_AINLINE uint64x2_t crc_shift(uint32_t crc, size_t nbytes) {\n  return clmul_scalar(crc, xnmodp(nbytes * 8 - 33));\n}\n\nFOLLY_TARGET_ATTRIBUTE(\"+crc\")\nCRC_EXPORT bool has_neon_eor3_crc32_v8s2x4e_s1x2() {\n  auto caps = hwcaps();\n\n  return caps.aarch64_fp() && caps.aarch64_asimd() && caps.aarch64_pmull() &&\n      caps.aarch64_crc32() && caps.aarch64_sha3();\n}\n\n// Mix v2s1x2 and s1x2\nCRC_EXPORT uint32_t neon_eor3_crc32_small(const uint8_t* buf, size_t len, uint32_t crc0) {\n  for (; len && ((uintptr_t)buf & 7); --len) {\n    crc0 = __crc32b(crc0, *buf++);\n  }\n  if (len > 384) {\n    if (((uintptr_t)buf & 8)) {\n      crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n      buf += 8;\n      len -= 8;\n    }\n    size_t blk = (len - 0) / 48;\n    size_t klen = blk * 16;\n    const uint8_t* buf2 = buf + klen;\n    uint64x2_t vc0;\n    uint64_t vc;\n    /* First vector chunk. */\n    uint64x2_t x0 = vld1q_u64((const uint64_t*)buf2), y0;\n    uint64x2_t x1 = vld1q_u64((const uint64_t*)(buf2 + 16)), y1;\n    uint64x2_t k;\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xf1da05aa, 0x81256527}; k = vld1q_u64(k_); }\n    buf2 += 32;\n    len -= 48;\n    /* Main loop. */\n    do {\n      y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n      y1 = clmul_lo(x1, k), x1 = clmul_hi(x1, k);\n      x0 = veor3q_u64(x0, y0, vld1q_u64((const uint64_t*)buf2));\n      x1 = veor3q_u64(x1, y1, vld1q_u64((const uint64_t*)(buf2 + 16)));\n      crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n      crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 8));\n      buf += 16;\n      buf2 += 32;\n      len -= 48;\n    } while (len >= 48);\n    /* Reduce x0 ... x1 to just x0. */\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xae689191, 0xccaa009e}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    x0 = veor3q_u64(x0, y0, x1);\n    /* Final scalar chunk. */\n    crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n    crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 8));\n    vc0 = crc_shift(crc0, 0 + blk * 32);\n    vc = vgetq_lane_u64(vc0, 0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    crc0 = __crc32d(0, vgetq_lane_u64(x0, 0));\n    crc0 = __crc32d(crc0, vc ^ vgetq_lane_u64(x0, 1));\n    buf = buf2;\n  }\n  if (len >= 16) {\n    /* Main loop. */\n    do {\n      crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n      crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 8));\n      buf += 16;\n      len -= 16;\n    } while (len >= 16);\n  }\n  if (len >= 8) {\n    crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n    len -= 8;\n    buf += 8;\n  }\n  for (; len; --len) {\n    crc0 = __crc32b(crc0, *buf++);\n  }\n  return crc0;\n}\n\nCRC_EXPORT uint32_t neon_eor3_crc32_v8s2x4e_s1x2(const uint8_t* buf, size_t len, uint32_t crc0) {\n  for (; len && ((uintptr_t)buf & 7); --len) {\n    crc0 = __crc32b(crc0, *buf++);\n  }\n  if (((uintptr_t)buf & 8) && len >= 8) {\n    crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n    buf += 8;\n    len -= 8;\n  }\n  if (len >= 192) {\n    const uint8_t* end = buf + len;\n    size_t blk = (len - 0) / 192;\n    size_t klen = blk * 32;\n    const uint8_t* buf2 = buf + klen * 2;\n    const uint8_t* limit = buf + klen - 64;\n    uint32_t crc1 = 0;\n    uint64x2_t vc0;\n    uint64x2_t vc1;\n    uint64_t vc;\n    /* First vector chunk. */\n    uint64x2_t x0 = vld1q_u64((const uint64_t*)buf2), y0;\n    uint64x2_t x1 = vld1q_u64((const uint64_t*)(buf2 + 16)), y1;\n    uint64x2_t x2 = vld1q_u64((const uint64_t*)(buf2 + 32)), y2;\n    uint64x2_t x3 = vld1q_u64((const uint64_t*)(buf2 + 48)), y3;\n    uint64x2_t x4 = vld1q_u64((const uint64_t*)(buf2 + 64)), y4;\n    uint64x2_t x5 = vld1q_u64((const uint64_t*)(buf2 + 80)), y5;\n    uint64x2_t x6 = vld1q_u64((const uint64_t*)(buf2 + 96)), y6;\n    uint64x2_t x7 = vld1q_u64((const uint64_t*)(buf2 + 112)), y7;\n    uint64x2_t k;\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x33fff533, 0x910eeec1}; k = vld1q_u64(k_); }\n    buf2 += 128;\n    /* Main loop. */\n    while (buf <= limit) {\n      y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n      y1 = clmul_lo(x1, k), x1 = clmul_hi(x1, k);\n      y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n      y3 = clmul_lo(x3, k), x3 = clmul_hi(x3, k);\n      y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n      y5 = clmul_lo(x5, k), x5 = clmul_hi(x5, k);\n      y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n      y7 = clmul_lo(x7, k), x7 = clmul_hi(x7, k);\n      x0 = veor3q_u64(x0, y0, vld1q_u64((const uint64_t*)buf2));\n      x1 = veor3q_u64(x1, y1, vld1q_u64((const uint64_t*)(buf2 + 16)));\n      x2 = veor3q_u64(x2, y2, vld1q_u64((const uint64_t*)(buf2 + 32)));\n      x3 = veor3q_u64(x3, y3, vld1q_u64((const uint64_t*)(buf2 + 48)));\n      x4 = veor3q_u64(x4, y4, vld1q_u64((const uint64_t*)(buf2 + 64)));\n      x5 = veor3q_u64(x5, y5, vld1q_u64((const uint64_t*)(buf2 + 80)));\n      x6 = veor3q_u64(x6, y6, vld1q_u64((const uint64_t*)(buf2 + 96)));\n      x7 = veor3q_u64(x7, y7, vld1q_u64((const uint64_t*)(buf2 + 112)));\n      crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n      crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen));\n      crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 8));\n      crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen + 8));\n      crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 16));\n      crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen + 16));\n      crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 24));\n      crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen + 24));\n      buf += 32;\n      buf2 += 128;\n    }\n    /* Reduce x0 ... x7 to just x0. */\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xae689191, 0xccaa009e}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n    x0 = veor3q_u64(x0, y0, x1);\n    x2 = veor3q_u64(x2, y2, x3);\n    x4 = veor3q_u64(x4, y4, x5);\n    x6 = veor3q_u64(x6, y6, x7);\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xf1da05aa, 0x81256527}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    x0 = veor3q_u64(x0, y0, x2);\n    x4 = veor3q_u64(x4, y4, x6);\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x8f352d95, 0x1d9513d7}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    x0 = veor3q_u64(x0, y0, x4);\n    /* Final scalar chunk. */\n    crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n    crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen));\n    crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 8));\n    crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen + 8));\n    crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 16));\n    crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen + 16));\n    crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 24));\n    crc1 = __crc32d(crc1, *(const uint64_t*)(buf + klen + 24));\n    vc0 = crc_shift(crc0, klen + blk * 128);\n    vc1 = crc_shift(crc1, 0 + blk * 128);\n    vc = vgetq_lane_u64(veorq_u64(vc0, vc1), 0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    crc0 = __crc32d(0, vgetq_lane_u64(x0, 0));\n    crc0 = __crc32d(crc0, vc ^ vgetq_lane_u64(x0, 1));\n    buf = buf2;\n    len = end - buf;\n  }\n  if (len >= 16) {\n    /* Main loop. */\n    do {\n      crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n      crc0 = __crc32d(crc0, *(const uint64_t*)(buf + 8));\n      buf += 16;\n      len -= 16;\n    } while (len >= 16);\n  }\n  if (len >= 8) {\n    crc0 = __crc32d(crc0, *(const uint64_t*)buf);\n    len -= 8;\n    buf += 8; \n  }\n  for (; len; --len) {\n    crc0 = __crc32b(crc0, *buf++);\n  }\n  return crc0;\n}\n} // namespace folly::detail\n#endif\n"
  },
  {
    "path": "folly/external/fast-crc32/neon_eor3_crc32_v8s2x4e_s1x2.h",
    "content": "#pragma once\n#include <cstddef>\n#include <cstdint>\n\nnamespace folly::detail {\nuint32_t neon_eor3_crc32_v8s2x4e_s1x2(const uint8_t* buf, size_t len, uint32_t crc0);\nuint32_t neon_eor3_crc32_small(const uint8_t* buf, size_t len, uint32_t crc0);\nbool has_neon_eor3_crc32_v8s2x4e_s1x2();\n}\n"
  },
  {
    "path": "folly/external/fast-crc32/neon_eor3_crc32c_v8s2x4e_s2x1.cpp",
    "content": "/* Generated by https://github.com/corsix/fast-crc32/ using: */\n/* ./generate -i neon_eor3 -p crc32c -a v8s2x4e_s1x2 */\n/* MIT licensed */\n\n#include \"folly/external/fast-crc32/neon_eor3_crc32c_v8s2x4e_s2x1.h\"\n#include <folly/system/AuxVector.h>\n#include <folly/Portability.h>\n\n#include <stdint.h>\n#include <stddef.h>\n\n#define CRC_EXPORT extern\n\nnamespace folly::detail {\n[[maybe_unused]] static ElfHwCaps hwcaps() {\n  static ElfHwCaps caps;\n  return caps;\n}\n}\n\n#if !(FOLLY_AARCH64 && FOLLY_NEON && FOLLY_ARM_FEATURE_CRYPTO && FOLLY_ARM_FEATURE_CRC32 && FOLLY_ARM_FEATURE_SHA3)\n#include <stdlib.h>\nnamespace folly::detail {\nCRC_EXPORT uint32_t neon_eor3_crc32c_v8s2x4e_s2x1(const uint8_t*, size_t, uint32_t) {\n  abort(); // not implemented on this platform\n}\n\nCRC_EXPORT uint32_t neon_eor3_crc32c_small(const uint8_t*, size_t, uint32_t) {\n  abort(); // not implemented on this platform\n}\n\nCRC_EXPORT bool has_neon_eor3_crc32c_v8s2x4e_s2x1() {\n  return false;\n}\n}\n#else\n#include <arm_acle.h>\n#include <arm_neon.h>\n\n#if defined(_MSC_VER)\n#define CRC_AINLINE static __forceinline\n#define CRC_ALIGN(n) __declspec(align(n))\n#else\n#define CRC_AINLINE static __inline __attribute__((always_inline))\n#define CRC_ALIGN(n) __attribute__((aligned(n)))\n#endif\n\nnamespace folly::detail {\nCRC_AINLINE uint64x2_t clmul_lo(uint64x2_t a, uint64x2_t b) {\n  return vreinterpretq_u64_p128(vmull_p64(a[0], b[0]));\n}\n\nCRC_AINLINE uint64x2_t clmul_hi(uint64x2_t a, uint64x2_t b) {\n  return vreinterpretq_u64_p128(vmull_high_p64(vreinterpretq_p64_u64(a), vreinterpretq_p64_u64(b)));\n}\n\nCRC_AINLINE uint64x2_t clmul_scalar(uint32_t a, uint32_t b) {\n  return vreinterpretq_u64_p128(vmull_p64(a, b));\n}\n\nstatic uint32_t xnmodp(uint64_t n) /* x^n mod P, in log(n) time */ {\n  uint64_t stack = ~(uint64_t)1;\n  uint32_t acc, low;\n  for (; n > 191; n = (n >> 1) - 16) {\n    stack = (stack << 1) + (n & 1);\n  }\n  stack = ~stack;\n  acc = ((uint32_t)0x80000000) >> (n & 31);\n  for (n >>= 5; n; --n) {\n    acc = __crc32cw(acc, 0);\n  }\n  while ((low = stack & 1), stack >>= 1) {\n    poly8x8_t x = vreinterpret_p8_u64(vmov_n_u64(acc));\n    uint64_t y = vgetq_lane_u64(vreinterpretq_u64_p16(vmull_p8(x, x)), 0);\n    acc = __crc32cd(0, y << low);\n  }\n  return acc;\n}\n\nCRC_AINLINE uint64x2_t crc_shift(uint32_t crc, size_t nbytes) {\n  return clmul_scalar(crc, xnmodp(nbytes * 8 - 33));\n}\n\nFOLLY_TARGET_ATTRIBUTE(\"+crc\")\nCRC_EXPORT bool has_neon_eor3_crc32c_v8s2x4e_s2x1() {\n  auto caps = hwcaps();\n\n  return caps.aarch64_fp() && caps.aarch64_asimd() && caps.aarch64_pmull() &&\n      caps.aarch64_crc32() && caps.aarch64_sha3();\n}\n\n// Mix v2s1x2 and s1x2\nCRC_EXPORT uint32_t neon_eor3_crc32c_small(const uint8_t* buf, size_t len, uint32_t crc0) {\n  for (; len && ((uintptr_t)buf & 7); --len) {\n    crc0 = __crc32cb(crc0, *buf++);\n  }\n  if (len > 384) {\n    if (((uintptr_t)buf & 8) && len >= 8) {\n      crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n      buf += 8;\n      len -= 8;\n    }\n    size_t blk = (len - 0) / 48;\n    size_t klen = blk * 16;\n    const uint8_t* buf2 = buf + klen;\n    uint64x2_t vc0;\n    uint64_t vc;\n    /* First vector chunk. */\n    uint64x2_t x0 = vld1q_u64((const uint64_t*)buf2), y0;\n    uint64x2_t x1 = vld1q_u64((const uint64_t*)(buf2 + 16)), y1;\n    uint64x2_t k;\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x3da6d0cb, 0xba4fc28e}; k = vld1q_u64(k_); }\n    buf2 += 32;\n    len -= 48;\n    /* Main loop. */\n    while (len >= 48) {\n      y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n      y1 = clmul_lo(x1, k), x1 = clmul_hi(x1, k);\n      x0 = veor3q_u64(x0, y0, vld1q_u64((const uint64_t*)buf2));\n      x1 = veor3q_u64(x1, y1, vld1q_u64((const uint64_t*)(buf2 + 16)));\n      crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n      crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n      buf += 16;\n      buf2 += 32;\n      len -= 48;\n    }\n    /* Reduce x0 ... x1 to just x0. */\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xf20c0dfe, 0x493c7d27}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    x0 = veor3q_u64(x0, y0, x1);\n    /* Final scalar chunk. */\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n    crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n    vc0 = crc_shift(crc0, 0 + blk * 32);\n    vc = vgetq_lane_u64(vc0, 0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    crc0 = __crc32cd(0, vgetq_lane_u64(x0, 0));\n    crc0 = __crc32cd(crc0, vc ^ vgetq_lane_u64(x0, 1));\n    buf = buf2;\n  }\n  if (len >= 16) {\n    /* Main loop. */\n    do {\n      crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n      crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n      buf += 16;\n      len -= 16;\n    } while (len >= 16);\n  }\n  if (len >= 8) {\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n    len -= 8;\n    buf += 8;\n  }\n  for (; len; --len) {\n    crc0 = __crc32cb(crc0, *buf++);\n  }\n  return crc0;\n}\n\nCRC_EXPORT uint32_t neon_eor3_crc32c_v8s2x4e_s2x1(const uint8_t* buf, size_t len, uint32_t crc0) {\n  for (; len && ((uintptr_t)buf & 7); --len) {\n    crc0 = __crc32cb(crc0, *buf++);\n  }\n  if (((uintptr_t)buf & 8) && len >= 8) {\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n    buf += 8;\n    len -= 8;\n  }\n  if (len >= 192) {\n    const uint8_t* end = buf + len;\n    size_t blk = (len - 0) / 192;\n    size_t klen = blk * 32;\n    const uint8_t* buf2 = buf + klen * 2;\n    const uint8_t* limit = buf + klen - 64;\n    uint32_t crc1 = 0;\n    uint64x2_t vc0;\n    uint64x2_t vc1;\n    uint64_t vc;\n    /* First vector chunk. */\n    uint64x2_t x0 = vld1q_u64((const uint64_t*)buf2), y0;\n    uint64x2_t x1 = vld1q_u64((const uint64_t*)(buf2 + 16)), y1;\n    uint64x2_t x2 = vld1q_u64((const uint64_t*)(buf2 + 32)), y2;\n    uint64x2_t x3 = vld1q_u64((const uint64_t*)(buf2 + 48)), y3;\n    uint64x2_t x4 = vld1q_u64((const uint64_t*)(buf2 + 64)), y4;\n    uint64x2_t x5 = vld1q_u64((const uint64_t*)(buf2 + 80)), y5;\n    uint64x2_t x6 = vld1q_u64((const uint64_t*)(buf2 + 96)), y6;\n    uint64x2_t x7 = vld1q_u64((const uint64_t*)(buf2 + 112)), y7;\n    uint64x2_t k;\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x6992cea2, 0x0d3b6092}; k = vld1q_u64(k_); }\n    buf2 += 128;\n    /* Main loop. */\n    while (buf <= limit) {\n      y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n      y1 = clmul_lo(x1, k), x1 = clmul_hi(x1, k);\n      y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n      y3 = clmul_lo(x3, k), x3 = clmul_hi(x3, k);\n      y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n      y5 = clmul_lo(x5, k), x5 = clmul_hi(x5, k);\n      y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n      y7 = clmul_lo(x7, k), x7 = clmul_hi(x7, k);\n      x0 = veor3q_u64(x0, y0, vld1q_u64((const uint64_t*)buf2));\n      x1 = veor3q_u64(x1, y1, vld1q_u64((const uint64_t*)(buf2 + 16)));\n      x2 = veor3q_u64(x2, y2, vld1q_u64((const uint64_t*)(buf2 + 32)));\n      x3 = veor3q_u64(x3, y3, vld1q_u64((const uint64_t*)(buf2 + 48)));\n      x4 = veor3q_u64(x4, y4, vld1q_u64((const uint64_t*)(buf2 + 64)));\n      x5 = veor3q_u64(x5, y5, vld1q_u64((const uint64_t*)(buf2 + 80)));\n      x6 = veor3q_u64(x6, y6, vld1q_u64((const uint64_t*)(buf2 + 96)));\n      x7 = veor3q_u64(x7, y7, vld1q_u64((const uint64_t*)(buf2 + 112)));\n      crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n      crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen));\n      crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n      crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 8));\n      crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 16));\n      crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 16));\n      crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 24));\n      crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 24));\n      buf += 32;\n      buf2 += 128;\n    }\n    /* Reduce x0 ... x7 to just x0. */\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0xf20c0dfe, 0x493c7d27}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n    x0 = veor3q_u64(x0, y0, x1);\n    x2 = veor3q_u64(x2, y2, x3);\n    x4 = veor3q_u64(x4, y4, x5);\n    x6 = veor3q_u64(x6, y6, x7);\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x3da6d0cb, 0xba4fc28e}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    x0 = veor3q_u64(x0, y0, x2);\n    x4 = veor3q_u64(x4, y4, x6);\n    { static const uint64_t CRC_ALIGN(16) k_[] = {0x740eef02, 0x9e4addf8}; k = vld1q_u64(k_); }\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    x0 = veor3q_u64(x0, y0, x4);\n    /* Final scalar chunk. */\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n    crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen));\n    crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n    crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 8));\n    crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 16));\n    crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 16));\n    crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 24));\n    crc1 = __crc32cd(crc1, *(const uint64_t*)(buf + klen + 24));\n    vc0 = crc_shift(crc0, klen + blk * 128);\n    vc1 = crc_shift(crc1, 0 + blk * 128);\n    vc = vgetq_lane_u64(veorq_u64(vc0, vc1), 0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    crc0 = __crc32cd(0, vgetq_lane_u64(x0, 0));\n    crc0 = __crc32cd(crc0, vc ^ vgetq_lane_u64(x0, 1));\n    buf = buf2;\n    len = end - buf;\n  }\n  if (len >= 16) {\n    /* Main loop. */\n    do {\n      crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n      crc0 = __crc32cd(crc0, *(const uint64_t*)(buf + 8));\n      buf += 16;\n      len -= 16;\n    } while (len >= 16);\n  }\n  if (len >= 8) {\n    crc0 = __crc32cd(crc0, *(const uint64_t*)buf);\n    len -= 8;\n    buf += 8; \n  }\n  for (; len; --len) {\n    crc0 = __crc32cb(crc0, *buf++);\n  }\n  return crc0;\n}\n} // namespace folly::detail\n#endif\n"
  },
  {
    "path": "folly/external/fast-crc32/neon_eor3_crc32c_v8s2x4e_s2x1.h",
    "content": "#pragma once\n#include <cstddef>\n#include <cstdint>\n\nnamespace folly::detail {\nuint32_t neon_eor3_crc32c_v8s2x4e_s2x1(const uint8_t* buf, size_t len, uint32_t crc0);\nuint32_t neon_eor3_crc32c_small(const uint8_t* buf, size_t len, uint32_t crc0);\nbool has_neon_eor3_crc32c_v8s2x4e_s2x1();\n}\n"
  },
  {
    "path": "folly/external/fast-crc32/sse_crc32c_v8s3x3.cpp",
    "content": "/* Generated by https://github.com/corsix/fast-crc32/ using: */\n/* ./generate -i sse -p crc32c -a v8s3x3 */\n/* MIT licensed */\n\n#include \"folly/external/fast-crc32/sse_crc32c_v8s3x3.h\"\n\n#include <stdint.h>\n\n#define CRC_EXPORT extern\n#if !defined(FOLLY_ENABLE_SSE42_CRC32C_V8S3X3)\n#include <stdlib.h>\nnamespace folly::detail {\nCRC_EXPORT uint32_t sse_crc32c_v8s3x3(const uint8_t*, size_t, uint32_t) {\n  abort(); // not implemented on this platform\n}\n} // namespace folly::detail\n#else\n#include <nmmintrin.h>\n#include <wmmintrin.h>\n\n#if defined(_MSC_VER)\n#define CRC_AINLINE static __forceinline\n#define CRC_ALIGN(n) __declspec(align(n))\n#else\n#define CRC_AINLINE static __inline __attribute__((always_inline))\n#define CRC_ALIGN(n) __attribute__((aligned(n)))\n#endif\n\n#define clmul_lo(a, b) (_mm_clmulepi64_si128((a), (b), 0))\n#define clmul_hi(a, b) (_mm_clmulepi64_si128((a), (b), 17))\n\nnamespace folly::detail {\nCRC_AINLINE __m128i clmul_scalar(uint32_t a, uint32_t b) {\n  return _mm_clmulepi64_si128(_mm_cvtsi32_si128(a), _mm_cvtsi32_si128(b), 0);\n}\n\nstatic uint32_t xnmodp(uint64_t n) /* x^n mod P, in log(n) time */ {\n  uint64_t stack = ~(uint64_t)1;\n  uint32_t acc, low;\n  for (; n > 191; n = (n >> 1) - 16) {\n    stack = (stack << 1) + (n & 1);\n  }\n  stack = ~stack;\n  acc = ((uint32_t)0x80000000) >> (n & 31);\n  for (n >>= 5; n; --n) {\n    acc = _mm_crc32_u32(acc, 0);\n  }\n  while ((low = stack & 1), stack >>= 1) {\n    __m128i x = _mm_cvtsi32_si128(acc);\n    uint64_t y = _mm_cvtsi128_si64(_mm_clmulepi64_si128(x, x, 0));\n    acc = _mm_crc32_u64(0, y << low);\n  }\n  return acc;\n}\n\nCRC_AINLINE __m128i crc_shift(uint32_t crc, size_t nbytes) {\n  return clmul_scalar(crc, xnmodp(nbytes * 8 - 33));\n}\n\nFOLLY_TARGET_ATTRIBUTE(\"sse4.2\")\nCRC_EXPORT uint32_t sse_crc32c_v8s3x3(const uint8_t* buf, size_t len, uint32_t crc0) {\n  for (; len && ((uintptr_t)buf & 7); --len) {\n    crc0 = _mm_crc32_u8(crc0, *buf++);\n  }\n  if (((uintptr_t)buf & 8) && len >= 8) {\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n    buf += 8;\n    len -= 8;\n  }\n  if (len >= 208) {\n    size_t blk = (len - 8) / 200;\n    size_t klen = blk * 24;\n    const uint8_t* buf2 = buf + 0;\n    uint32_t crc1 = 0;\n    uint32_t crc2 = 0;\n    __m128i vc0;\n    __m128i vc1;\n    uint64_t vc;\n    /* First vector chunk. */\n    __m128i x0 = _mm_loadu_si128((const __m128i*)buf2), y0;\n    __m128i x1 = _mm_loadu_si128((const __m128i*)(buf2 + 16)), y1;\n    __m128i x2 = _mm_loadu_si128((const __m128i*)(buf2 + 32)), y2;\n    __m128i x3 = _mm_loadu_si128((const __m128i*)(buf2 + 48)), y3;\n    __m128i x4 = _mm_loadu_si128((const __m128i*)(buf2 + 64)), y4;\n    __m128i x5 = _mm_loadu_si128((const __m128i*)(buf2 + 80)), y5;\n    __m128i x6 = _mm_loadu_si128((const __m128i*)(buf2 + 96)), y6;\n    __m128i x7 = _mm_loadu_si128((const __m128i*)(buf2 + 112)), y7;\n    __m128i k;\n    k = _mm_setr_epi32(0x6992cea2, 0, 0x0d3b6092, 0);\n    x0 = _mm_xor_si128(_mm_cvtsi32_si128(crc0), x0);\n    crc0 = 0;\n    buf2 += 128;\n    len -= 200;\n    buf += blk * 128;\n    /* Main loop. */\n    while (len >= 208) {\n      y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n      y1 = clmul_lo(x1, k), x1 = clmul_hi(x1, k);\n      y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n      y3 = clmul_lo(x3, k), x3 = clmul_hi(x3, k);\n      y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n      y5 = clmul_lo(x5, k), x5 = clmul_hi(x5, k);\n      y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n      y7 = clmul_lo(x7, k), x7 = clmul_hi(x7, k);\n      y0 = _mm_xor_si128(y0, _mm_loadu_si128((const __m128i*)buf2)), x0 = _mm_xor_si128(x0, y0);\n      y1 = _mm_xor_si128(y1, _mm_loadu_si128((const __m128i*)(buf2 + 16))), x1 = _mm_xor_si128(x1, y1);\n      y2 = _mm_xor_si128(y2, _mm_loadu_si128((const __m128i*)(buf2 + 32))), x2 = _mm_xor_si128(x2, y2);\n      y3 = _mm_xor_si128(y3, _mm_loadu_si128((const __m128i*)(buf2 + 48))), x3 = _mm_xor_si128(x3, y3);\n      y4 = _mm_xor_si128(y4, _mm_loadu_si128((const __m128i*)(buf2 + 64))), x4 = _mm_xor_si128(x4, y4);\n      y5 = _mm_xor_si128(y5, _mm_loadu_si128((const __m128i*)(buf2 + 80))), x5 = _mm_xor_si128(x5, y5);\n      y6 = _mm_xor_si128(y6, _mm_loadu_si128((const __m128i*)(buf2 + 96))), x6 = _mm_xor_si128(x6, y6);\n      y7 = _mm_xor_si128(y7, _mm_loadu_si128((const __m128i*)(buf2 + 112))), x7 = _mm_xor_si128(x7, y7);\n      crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n      crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen));\n      crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2));\n      crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 8));\n      crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 8));\n      crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 8));\n      crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 16));\n      crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 16));\n      crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 16));\n      buf += 24;\n      buf2 += 128;\n      len -= 200;\n    }\n    /* Reduce x0 ... x7 to just x0. */\n    k = _mm_setr_epi32(0xf20c0dfe, 0, 0x493c7d27, 0);\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y2 = clmul_lo(x2, k), x2 = clmul_hi(x2, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    y6 = clmul_lo(x6, k), x6 = clmul_hi(x6, k);\n    y0 = _mm_xor_si128(y0, x1), x0 = _mm_xor_si128(x0, y0);\n    y2 = _mm_xor_si128(y2, x3), x2 = _mm_xor_si128(x2, y2);\n    y4 = _mm_xor_si128(y4, x5), x4 = _mm_xor_si128(x4, y4);\n    y6 = _mm_xor_si128(y6, x7), x6 = _mm_xor_si128(x6, y6);\n    k = _mm_setr_epi32(0x3da6d0cb, 0, 0xba4fc28e, 0);\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y4 = clmul_lo(x4, k), x4 = clmul_hi(x4, k);\n    y0 = _mm_xor_si128(y0, x2), x0 = _mm_xor_si128(x0, y0);\n    y4 = _mm_xor_si128(y4, x6), x4 = _mm_xor_si128(x4, y4);\n    k = _mm_setr_epi32(0x740eef02, 0, 0x9e4addf8, 0);\n    y0 = clmul_lo(x0, k), x0 = clmul_hi(x0, k);\n    y0 = _mm_xor_si128(y0, x4), x0 = _mm_xor_si128(x0, y0);\n    /* Final scalar chunk. */\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n    crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen));\n    crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2));\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 8));\n    crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 8));\n    crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 8));\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)(buf + 16));\n    crc1 = _mm_crc32_u64(crc1, *(const uint64_t*)(buf + klen + 16));\n    crc2 = _mm_crc32_u64(crc2, *(const uint64_t*)(buf + klen * 2 + 16));\n    buf += 24;\n    vc0 = crc_shift(crc0, klen * 2 + 8);\n    vc1 = crc_shift(crc1, klen + 8);\n    vc = _mm_extract_epi64(_mm_xor_si128(vc0, vc1), 0);\n    /* Reduce 128 bits to 32 bits, and multiply by x^32. */\n    vc ^= _mm_extract_epi64(crc_shift(_mm_crc32_u64(_mm_crc32_u64(0, _mm_extract_epi64(x0, 0)), _mm_extract_epi64(x0, 1)), klen * 3 + 8), 0);\n    /* Final 8 bytes. */\n    buf += klen * 2;\n    crc0 = crc2;\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf ^ vc), buf += 8;\n    len -= 8;\n  }\n  for (; len >= 8; buf += 8, len -= 8) {\n    crc0 = _mm_crc32_u64(crc0, *(const uint64_t*)buf);\n  }\n  for (; len; --len) {\n    crc0 = _mm_crc32_u8(crc0, *buf++);\n  }\n  return crc0;\n}\n} // namespace folly::detail\n#endif\n"
  },
  {
    "path": "folly/external/fast-crc32/sse_crc32c_v8s3x3.h",
    "content": "#pragma once\n#include <cstddef>\n#include <cstdint>\n\n#include <folly/Portability.h>\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n#define FOLLY_ENABLE_SSE42_CRC32C_V8S3X3 1\n#endif\n\nnamespace folly::detail {\nuint32_t sse_crc32c_v8s3x3(const uint8_t* buf, size_t len, uint32_t crc0);\n}\n"
  },
  {
    "path": "folly/external/nvidia/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nadd_subdirectory(detail)\nadd_subdirectory(hash)\n"
  },
  {
    "path": "folly/external/nvidia/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"range_sve2\",\n    srcs = [\"RangeSve2.cpp\"],\n    headers = [\"RangeSve2.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:portability\",\n    ],\n    exported_deps = [\n        \"//folly/detail:range_common\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/nvidia/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME range_sve2\n  SRCS\n    RangeSve2.cpp\n  HEADERS\n    RangeSve2.h\n  DEPS\n    folly_portability\n  EXPORTED_DEPS\n    folly_detail_range_common\n)\n"
  },
  {
    "path": "folly/external/nvidia/detail/RangeSve2.cpp",
    "content": "/*\n * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/external/nvidia/detail/RangeSve2.h>\n\n#include <folly/Portability.h>\n\n#if !FOLLY_ARM_FEATURE_SVE2\nnamespace folly {\nnamespace detail {\nsize_t qfind_first_byte_of_sve2(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  return qfind_first_byte_of_nosimd(haystack, needles);\n}\n} // namespace detail\n} // namespace folly\n#else\n#include <cassert>\n\n#include <arm_sve.h>\n\nnamespace folly {\nnamespace detail {\n\n// helper method for case where needles.size() <= 16\nsize_t qfind_first_byte_of_needles16(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  assert(haystack.size() > 0u);\n  assert(needles.size() > 0u);\n  assert(needles.size() <= 16u);\n\n  svuint8_t arr1, arr2;\n  svbool_t pc, pn, pg = svwhilelt_b8(0, 16);  // pg = ptrue VL16\n\n  // Only read the needles that are valid, otherwise `match' may report\n  // incorrect matches. Also, copy a valid needle to inactive elements to avoid\n  // incorrectly matching with zero.\n  pn = svwhilelt_b8_u64(0, needles.size());\n  arr2 = svld1_u8(pn, reinterpret_cast<uint8_t const*>(needles.data()));\n  arr2 = svsel(pn, arr2, svdup_lane(arr2, 0));\n\n  size_t index = 0;\n\n  // Read valid chunks of 16 bytes.\n  for (; index+16 <= haystack.size(); index += 16) {\n    arr1 = svld1_u8(pg, reinterpret_cast<uint8_t const*>(haystack.data()+index));\n    pc = svmatch_u8(pg, arr1, arr2);\n\n    if (svptest_any(pg, pc)) {\n      pc = svbrkb_z(pg, pc);\n      return index+svcntp_b8(pg, pc);\n    }\n  }\n\n  // Handle remainder.\n  if (index < haystack.size()) {\n    // Get a predicate just for the valid elements, otherwise same as above.\n    pg = svwhilelt_b8(index, haystack.size());\n\n    arr1 = svld1_u8(pg, reinterpret_cast<uint8_t const*>(haystack.data()+index));\n    pc = svmatch_u8(pg, arr1, arr2);\n\n    if (svptest_any(pg, pc)) {\n      pc = svbrkb_z(pg, pc);\n      return index+svcntp_b8(pg, pc);\n    }\n  }\n\n  // No matches found.\n  return std::string::npos;\n}\n\nsize_t qfind_first_byte_of_sve2(\n    const StringPieceLite haystack, const StringPieceLite needles) {\n  if (FOLLY_UNLIKELY(needles.empty() || haystack.empty())) {\n    return std::string::npos;\n  } else if (needles.size() <= 16) {\n    return qfind_first_byte_of_needles16(haystack, needles);\n  }\n\n  // This is pretty much the same as in `qfind_first_byte_of_needles16', just\n  // looping through the needles.\n\n  svuint8_t arr1, arr2;\n  svbool_t pc, pn, pg;\n\n  // Loop through haystacks of 16 (or potentially less) bytes.\n  // Note: We could have an epilogue to avoid the min and `whilelt' below (see\n  // qfind_first_byte_of_needles16).\n  for (size_t index = 0; index < haystack.size(); index += 16) {\n    // Load the haystack.\n    pg = svwhilelt_b8(index, std::min(index+16, haystack.size()));\n    arr1 = svld1_u8(pg, reinterpret_cast<uint8_t const*>(haystack.data()+index));\n\n    // Loop through the needles in groups of 16 at a time.\n    size_t j = 0;\n    pn = svwhilelt_b8(0, 16);  // pn = ptrue VL16\n    for (; j+16 <= needles.size(); j += 16) {\n      // Load them.\n      arr2 = svld1_u8(pn, reinterpret_cast<uint8_t const*>(needles.data()+j));\n\n      // Carry the match.\n      pc = svmatch_u8(pg, arr1, arr2);\n\n      // If any match is found count and return the index of the first match\n      // via a sequence of brkb+cntp.\n      if (svptest_any(pg, pc)) {\n        pc = svbrkb_z(pg, pc);\n        return index+svcntp_b8(pg, pc);\n      }\n    }\n\n    // Handle remainder needles.\n    if (j < needles.size()) {\n      // Get a predicate just for the valid elements, otherwise same as above.\n      pn = svwhilelt_b8(j, needles.size());\n\n      arr2 = svld1_u8(pn, reinterpret_cast<uint8_t const*>(needles.data()+j));\n      arr2 = svsel(pn, arr2, svdup_lane(arr2, 0));\n\n      pc = svmatch_u8(pg, arr1, arr2);\n      if (svptest_any(pg, pc)) {\n        pc = svbrkb_z(pg, pc);\n        return index+svcntp_b8(pg, pc);\n      }\n    }\n  }\n\n  return std::string::npos;\n}\n} // namespace detail\n} // namespace folly\n#endif\n"
  },
  {
    "path": "folly/external/nvidia/detail/RangeSve2.h",
    "content": "/*\n * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n\n#include <folly/detail/RangeCommon.h>\n\nnamespace folly {\nnamespace detail {\n\nsize_t qfind_first_byte_of_sve2(\n    const StringPieceLite haystack, const StringPieceLite needles);\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/external/nvidia/hash/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"checksum\",\n    srcs = [\"Checksum.cpp\"],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:portability\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/nvidia/hash/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME checksum\n  SRCS\n    Checksum.cpp\n  DEPS\n    folly_portability\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/external/nvidia/hash/Checksum.cpp",
    "content": "/*\n * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if defined(__aarch64__)\n\n#include <cstring>\n#include <cstddef>\n\n#include <folly/Portability.h>\n\n#if FOLLY_ARM_FEATURE_CRC32\n\n#include <arm_acle.h>\n\nnamespace folly::detail {\n\nuint32_t crc32_hw(const uint8_t* buf, size_t len, uint32_t crc) {\n  while (len >= 8) {\n    uint64_t val = 0;\n    std::memcpy(&val, buf, 8);\n    crc = __crc32d(crc, val);\n    len -= 8;\n    buf += 8;\n  }\n\n  if (len % 8 >= 4) {\n    uint32_t val = 0;\n    std::memcpy(&val, buf, 4);\n    crc = __crc32w(crc, val);\n    len -= 4;\n    buf += 4;\n  }\n\n  if (len % 4 >= 2) {\n    uint16_t val = 0;\n    std::memcpy(&val, buf, 2);\n    crc = __crc32h(crc, val);\n    len -= 2;\n    buf += 2;\n  }\n\n  if (len % 2 >= 1) {\n    crc = __crc32b(crc, *buf);\n  }\n\n  return crc;\n}\n\n} // namespace folly::detail\n\n#endif // FOLLY_ARM_FEATURE_CRC32\n\n#endif // __aarch64__\n"
  },
  {
    "path": "folly/external/nvidia/hash/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"crc32c_detail\",\n    srcs = [\"Crc32cDetail.cpp\"],\n    headers = [\"Crc32cCombineDetail.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/nvidia/hash/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME crc32c_detail\n  SRCS\n    Crc32cDetail.cpp\n  HEADERS\n    Crc32cCombineDetail.h\n  EXPORTED_DEPS\n    folly_portability\n)\n"
  },
  {
    "path": "folly/external/nvidia/hash/detail/Crc32cCombineDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Portability.h>\n\n#if FOLLY_NEON && FOLLY_ARM_FEATURE_CRC32 && FOLLY_ARM_FEATURE_AES && \\\n    FOLLY_ARM_FEATURE_SHA2\n\n#include <arm_acle.h>\n#include <arm_neon.h>\n\nnamespace folly::detail {\n\ninline uint32_t gf_multiply_crc32c_hw(uint64_t crc1, uint64_t crc2, uint32_t) {\n  const uint64x2_t count = vsetq_lane_u64(0, vdupq_n_u64(1), 1);\n\n  const poly128_t res0 = vmull_p64(crc2, crc1);\n  const uint64x2_t res1 =\n      vshlq_u64(vreinterpretq_u64_p128(res0), vreinterpretq_s64_u64(count));\n\n  // Use hardware crc32c to do reduction from 64 -> 32 bytes\n  const uint64_t res2 = vgetq_lane_u64(res1, 0);\n  const uint32_t res3 = __crc32cw(0, res2);\n  const uint32_t res4 = vgetq_lane_u32(vreinterpretq_u32_u64(res1), 1);\n\n  return res3 ^ res4;\n}\n\ninline uint32_t gf_multiply_crc32_hw(uint64_t crc1, uint64_t crc2, uint32_t) {\n  const uint64x2_t count = vsetq_lane_u64(0, vdupq_n_u64(1), 1);\n\n  const poly128_t res0 = vmull_p64(crc2, crc1);\n  const uint64x2_t res1 =\n      vshlq_u64(vreinterpretq_u64_p128(res0), vreinterpretq_s64_u64(count));\n\n  // Use hardware crc32 to do reduction from 64 -> 32 bytes\n  const uint64_t res2 = vgetq_lane_u64(res1, 0);\n  const uint32_t res3 = __crc32w(0, res2);\n  const uint32_t res4 = vgetq_lane_u32(vreinterpretq_u32_u64(res1), 1);\n\n  return res3 ^ res4;\n}\n\n} // namespace folly\n\n#endif // FOLLY_ARM_FEATURE_CRC32\n"
  },
  {
    "path": "folly/external/nvidia/hash/detail/Crc32cDetail.cpp",
    "content": "/*\n * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if defined(__aarch64__)\n\n#include <cstring>\n\n#include <folly/Portability.h>\n\n#if FOLLY_ARM_FEATURE_CRC32\n\n#include <arm_acle.h>\n\nnamespace folly::detail {\n\nuint32_t crc32c_hw(const uint8_t* buf, size_t len, uint32_t crc) {\n  while (len >= 8) {\n    uint64_t val = 0;\n    std::memcpy(&val, buf, 8);\n    crc = __crc32cd(crc, val);\n    len -= 8;\n    buf += 8;\n  }\n\n  if (len >= 4) {\n    uint32_t val = 0;\n    std::memcpy(&val, buf, 4);\n    crc = __crc32cw(crc, val);\n    len -= 4;\n    buf += 4;\n  }\n\n  if (len >= 2) {\n    uint16_t val = 0;\n    std::memcpy(&val, buf, 2);\n    crc = __crc32ch(crc, val);\n    len -= 2;\n    buf += 2;\n  }\n\n  if (len >= 1) {\n    crc = __crc32cb(crc, *buf);\n  }\n\n  return crc;\n}\n\n} // namespace folly::detail\n\n#endif // FOLLY_ARM_FEATURE_CRC32\n\n#endif // __aarch64__\n"
  },
  {
    "path": "folly/external/rapidhash/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"rapidhash\",\n    headers = [\"rapidhash.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly/lang:bits\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n"
  },
  {
    "path": "folly/external/rapidhash/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME rapidhash\n  HEADERS\n    rapidhash.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_lang_bits\n    folly_portability_constexpr\n)\n"
  },
  {
    "path": "folly/external/rapidhash/rapidhash.h",
    "content": "/*\n * rapidhash V3 - Very fast, high quality, platform-independent hashing\nalgorithm.\n *\n * Based on 'wyhash', by Wang Yi <godspeed_china@yeah.net>\n *\n * Copyright (C) 2025 Nicolas De Carli\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\nall\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n *\n * You can contact the author at:\n *   - rapidhash source repository: https://github.com/Nicoshev/rapidhash\n */\n\n#pragma once\n\n/*\n *  Includes.\n */\n#include <stdint.h>\n#include <string.h>\n#if defined(_MSC_VER)\n#include <intrin.h>\n#if defined(_M_X64) && !defined(_M_ARM64EC)\n#pragma intrinsic(_umul128)\n#endif\n#endif\n\n#include <folly/CPortability.h>\n#include <folly/lang/Bits.h>\n#include <folly/portability/Constexpr.h>\n\nnamespace folly {\nnamespace external {\nnamespace rapidhash_detail {\n\n/*\n *  C++ macros.\n */\n#if __cplusplus >= 201402L && !defined(_MSC_VER)\n#define FOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR FOLLY_ALWAYS_INLINE constexpr\n#else\n#define FOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR FOLLY_ALWAYS_INLINE\n#endif\n\n/*\n *  Unrolling macros, changes code definition for main hash function.\n *\n *  FOLLY_EXTERNAL_RAPIDHASH_COMPACT: Legacy variant, each loop process 48 bytes.\n *  FOLLY_EXTERNAL_RAPIDHASH_UNROLLED: Unrolled variant, each loop process 96 bytes.\n *\n *  Most modern CPUs should benefit from having RAPIDHASH_UNROLLED.\n *\n *  These macros do not alter the output hash.\n */\n#ifndef FOLLY_EXTERNAL_RAPIDHASH_COMPACT\n#define FOLLY_EXTERNAL_RAPIDHASH_UNROLLED\n#elif defined(FOLLY_EXTERNAL_RAPIDHASH_UNROLLED)\n#error \"cannot define FOLLY_EXTERNAL_RAPIDHASH_COMPACT and FOLLY_EXTERNAL_RAPIDHASH_UNROLLED simultaneously.\"\n#endif\n\n/*\n *  Default secret parameters.\n */\n constexpr uint64_t rapidhash_secret[8] = {\n    0x2d358dccaa6c78a5ull,\n    0x8bb84b93962eacc9ull,\n    0x4b33a62ed433d4a3ull,\n    0x4d5a2da51de1aa47ull,\n    0xa0761d6478bd642full,\n    0xe7037ed1a0b428dbull,\n    0x90ed1765281c388cull,\n    0xaaaaaaaaaaaaaaaaull};\n\n/*\n *  64*64 -> 128bit multiply function.\n *\n *  @param A  Address of 64-bit number.\n *  @param B  Address of 64-bit number.\n *\n *  Calculates 128-bit C = *A * *B.\n *\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR void rapidhash_mum(uint64_t* A, uint64_t* B)\n    noexcept {\n#if defined(__SIZEOF_INT128__)\n  __uint128_t r = *A;\n  r *= *B;\n  *A = static_cast<uint64_t>(r);\n  *B = static_cast<uint64_t>(r >> 64);\n#elif defined(_MSC_VER) && (defined(_WIN64) || defined(_M_HYBRID_CHPE_ARM64))\n#if defined(_M_X64)\n  *A = _umul128(*A, *B, B);\n#else\n  uint64_t c = __umulh(*A, *B);\n  *A = *A * *B;\n  *B = c;\n#endif\n#else\n  uint64_t ha = *A >> 32, hb = *B >> 32, la = (uint32_t)*A, lb = (uint32_t)*B;\n  uint64_t rh = ha * hb, rm0 = ha * lb, rm1 = hb * la, rl = la * lb,\n           t = rl + (rm0 << 32), c = t < rl;\n  uint64_t lo = t + (rm1 << 32);\n  c += lo < t;\n  uint64_t hi = rh + (rm0 >> 32) + (rm1 >> 32) + c;\n  *A = lo;\n  *B = hi;\n#endif\n}\n\n/*\n *  Multiply and xor mix function.\n *\n *  @param A  64-bit number.\n *  @param B  64-bit number.\n *\n *  Calculates 128-bit C = A * B.\n *  Returns 64-bit xor between high and low 64 bits of C.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t\nrapidhash_mix(uint64_t A, uint64_t B) noexcept {\n  rapidhash_mum(&A, &B);\n  return A ^ B;\n}\n\n/*\n *  Read functions.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint32_t\nrapidhash_read32(const char* p) noexcept {\n  return folly::constexprLoadUnaligned<uint32_t, char>(p);\n}\n\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t\nrapidhash_read64(const char* p) noexcept {\n  return folly::constexprLoadUnaligned<uint64_t, char>(p);\n}\n\n/*\n *  rapidhash main function.\n *\n *  @param p       Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *  @param seed    64-bit seed used to alter the hash result predictably.\n *  @param secret  Triplet of 64-bit secrets used to alter hash result\n * predictably.\n *\n *  Returns a 64-bit hash.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_internal(\n    const char* p, size_t len, uint64_t seed, const uint64_t* secret)\n    noexcept {\n  seed ^= rapidhash_mix(seed ^ secret[2], secret[1]);\n  uint64_t a = 0, b = 0;\n  size_t i = len;\n  if (FOLLY_LIKELY(len <= 16)) {\n    if (len >= 4) {\n      seed ^= len;\n      if (len >= 8) {\n        const char* plast = p + len - 8;\n        a = rapidhash_read64(p);\n        b = rapidhash_read64(plast);\n      } else {\n        const char* plast = p + len - 4;\n        a = rapidhash_read32(p);\n        b = rapidhash_read32(plast);\n      }\n    } else if (len > 0) {\n      a = (static_cast<uint64_t>(p[0]) << 45) | static_cast<uint64_t>(p[len - 1]);\n      b = static_cast<uint64_t>(p[len >> 1]);\n    } else\n      a = b = 0;\n  } else {\n    uint64_t see1 = seed, see2 = seed;\n    uint64_t see3 = seed, see4 = seed;\n    uint64_t see5 = seed, see6 = seed;\n#ifdef FOLLY_EXTERNAL_RAPIDHASH_COMPACT\n    if (i > 112) {\n      do {\n        seed =\n            rapidhash_mix(rapidhash_read64(p) ^ secret[0], rapidhash_read64(p + 8) ^ seed);\n        see1 = rapidhash_mix(\n            rapidhash_read64(p + 16) ^ secret[1], rapidhash_read64(p + 24) ^ see1);\n        see2 = rapidhash_mix(\n            rapidhash_read64(p + 32) ^ secret[2], rapidhash_read64(p + 40) ^ see2);\n        see3 = rapidhash_mix(\n            rapidhash_read64(p + 48) ^ secret[3], rapidhash_read64(p + 56) ^ see3);\n        see4 = rapidhash_mix(\n            rapidhash_read64(p + 64) ^ secret[4], rapidhash_read64(p + 72) ^ see4);\n        see5 = rapidhash_mix(\n            rapidhash_read64(p + 80) ^ secret[5], rapidhash_read64(p + 88) ^ see5);\n        see6 = rapidhash_mix(\n            rapidhash_read64(p + 96) ^ secret[6], rapidhash_read64(p + 104) ^ see6);\n        p += 112;\n        i -= 112;\n      } while (i > 112);\n      seed ^= see1;\n      see2 ^= see3;\n      see4 ^= see5;\n      seed ^= see6;\n      see2 ^= see4;\n      seed ^= see2;\n    }\n#else\n    if (i > 224) {\n      do {\n        seed =\n            rapidhash_mix(rapidhash_read64(p) ^ secret[0], rapidhash_read64(p + 8) ^ seed);\n        see1 = rapidhash_mix(\n            rapidhash_read64(p + 16) ^ secret[1], rapidhash_read64(p + 24) ^ see1);\n        see2 = rapidhash_mix(\n            rapidhash_read64(p + 32) ^ secret[2], rapidhash_read64(p + 40) ^ see2);\n        see3 = rapidhash_mix(\n            rapidhash_read64(p + 48) ^ secret[3], rapidhash_read64(p + 56) ^ see3);\n        see4 = rapidhash_mix(\n            rapidhash_read64(p + 64) ^ secret[4], rapidhash_read64(p + 72) ^ see4);\n        see5 = rapidhash_mix(\n            rapidhash_read64(p + 80) ^ secret[5], rapidhash_read64(p + 88) ^ see5);\n        see6 = rapidhash_mix(\n            rapidhash_read64(p + 96) ^ secret[6], rapidhash_read64(p + 104) ^ see6);\n        seed = rapidhash_mix(\n            rapidhash_read64(p + 112) ^ secret[0], rapidhash_read64(p + 120) ^ seed);\n        see1 = rapidhash_mix(\n            rapidhash_read64(p + 128) ^ secret[1], rapidhash_read64(p + 136) ^ see1);\n        see2 = rapidhash_mix(\n            rapidhash_read64(p + 144) ^ secret[2], rapidhash_read64(p + 152) ^ see2);\n        see3 = rapidhash_mix(\n            rapidhash_read64(p + 160) ^ secret[3], rapidhash_read64(p + 168) ^ see3);\n        see4 = rapidhash_mix(\n            rapidhash_read64(p + 176) ^ secret[4], rapidhash_read64(p + 184) ^ see4);\n        see5 = rapidhash_mix(\n            rapidhash_read64(p + 192) ^ secret[5], rapidhash_read64(p + 200) ^ see5);\n        see6 = rapidhash_mix(\n            rapidhash_read64(p + 208) ^ secret[6], rapidhash_read64(p + 216) ^ see6);\n        p += 224;\n        i -= 224;\n      } while (i > 224);\n    }\n    if (i > 112) {\n      seed = rapidhash_mix(rapidhash_read64(p) ^ secret[0], rapidhash_read64(p + 8) ^ seed);\n      see1 = rapidhash_mix(\n          rapidhash_read64(p + 16) ^ secret[1], rapidhash_read64(p + 24) ^ see1);\n      see2 = rapidhash_mix(\n          rapidhash_read64(p + 32) ^ secret[2], rapidhash_read64(p + 40) ^ see2);\n      see3 = rapidhash_mix(\n          rapidhash_read64(p + 48) ^ secret[3], rapidhash_read64(p + 56) ^ see3);\n      see4 = rapidhash_mix(\n          rapidhash_read64(p + 64) ^ secret[4], rapidhash_read64(p + 72) ^ see4);\n      see5 = rapidhash_mix(\n          rapidhash_read64(p + 80) ^ secret[5], rapidhash_read64(p + 88) ^ see5);\n      see6 = rapidhash_mix(\n          rapidhash_read64(p + 96) ^ secret[6], rapidhash_read64(p + 104) ^ see6);\n      p += 112;\n      i -= 112;\n    }\n    seed ^= see1;\n    see2 ^= see3;\n    see4 ^= see5;\n    seed ^= see6;\n    see2 ^= see4;\n    seed ^= see2;\n#endif\n    if (i > 16) {\n      seed = rapidhash_mix(rapidhash_read64(p) ^ secret[2], rapidhash_read64(p + 8) ^ seed);\n      if (i > 32) {\n        seed = rapidhash_mix(\n            rapidhash_read64(p + 16) ^ secret[2], rapidhash_read64(p + 24) ^ seed);\n        if (i > 48) {\n          seed = rapidhash_mix(\n              rapidhash_read64(p + 32) ^ secret[1], rapidhash_read64(p + 40) ^ seed);\n          if (i > 64) {\n            seed = rapidhash_mix(\n                rapidhash_read64(p + 48) ^ secret[1], rapidhash_read64(p + 56) ^ seed);\n            if (i > 80) {\n              seed = rapidhash_mix(\n                  rapidhash_read64(p + 64) ^ secret[2],\n                  rapidhash_read64(p + 72) ^ seed);\n              if (i > 96) {\n                seed = rapidhash_mix(\n                    rapidhash_read64(p + 80) ^ secret[1],\n                    rapidhash_read64(p + 88) ^ seed);\n              }\n            }\n          }\n        }\n      }\n    }\n    a = rapidhash_read64(p + i - 16) ^ i;\n    b = rapidhash_read64(p + i - 8);\n  }\n  a ^= secret[1];\n  b ^= seed;\n  rapidhash_mum(&a, &b);\n  return rapidhash_mix(a ^ secret[7], b ^ secret[1] ^ i);\n}\n\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_internal(\n    const char* p, size_t len, uint64_t seed, const uint64_t* secret)\n    noexcept {\n  seed ^= rapidhash_mix(seed ^ secret[2], secret[1]);\n  uint64_t a = 0, b = 0;\n  size_t i = len;\n  if (FOLLY_LIKELY(len <= 16)) {\n    if (len >= 4) {\n      seed ^= len;\n      if (len >= 8) {\n        const char* plast = p + len - 8;\n        a = rapidhash_read64(p);\n        b = rapidhash_read64(plast);\n      } else {\n        const char* plast = p + len - 4;\n        a = rapidhash_read32(p);\n        b = rapidhash_read32(plast);\n      }\n    } else if (len > 0) {\n      a = (static_cast<uint64_t>(p[0]) << 45) | static_cast<uint64_t>(p[len - 1]);\n      b = static_cast<uint64_t>(p[len >> 1]);\n    } else\n      a = b = 0;\n  } else {\n    if (i > 80) {\n      uint64_t see1 = seed, see2 = seed;\n      uint64_t see3 = seed, see4 = seed;\n      do {\n        seed =\n            rapidhash_mix(rapidhash_read64(p) ^ secret[0], rapidhash_read64(p + 8) ^ seed);\n        see1 = rapidhash_mix(\n            rapidhash_read64(p + 16) ^ secret[1], rapidhash_read64(p + 24) ^ see1);\n        see2 = rapidhash_mix(\n            rapidhash_read64(p + 32) ^ secret[2], rapidhash_read64(p + 40) ^ see2);\n        see3 = rapidhash_mix(\n            rapidhash_read64(p + 48) ^ secret[3], rapidhash_read64(p + 56) ^ see3);\n        see4 = rapidhash_mix(\n            rapidhash_read64(p + 64) ^ secret[4], rapidhash_read64(p + 72) ^ see4);\n        p += 80;\n        i -= 80;\n      } while (i > 80);\n      seed ^= see1;\n      see2 ^= see3;\n      seed ^= see4;\n      seed ^= see2;\n    }\n    if (i > 16) {\n      seed = rapidhash_mix(rapidhash_read64(p) ^ secret[2], rapidhash_read64(p + 8) ^ seed);\n      if (i > 32) {\n        seed = rapidhash_mix(\n            rapidhash_read64(p + 16) ^ secret[2], rapidhash_read64(p + 24) ^ seed);\n        if (i > 48) {\n          seed = rapidhash_mix(\n              rapidhash_read64(p + 32) ^ secret[1], rapidhash_read64(p + 40) ^ seed);\n          if (i > 64) {\n            seed = rapidhash_mix(\n                rapidhash_read64(p + 48) ^ secret[1], rapidhash_read64(p + 56) ^ seed);\n          }\n        }\n      }\n    }\n    a = rapidhash_read64(p + i - 16) ^ i;\n    b = rapidhash_read64(p + i - 8);\n  }\n  a ^= secret[1];\n  b ^= seed;\n  rapidhash_mum(&a, &b);\n  return rapidhash_mix(a ^ secret[7], b ^ secret[1] ^ i);\n}\n\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_internal(\n    const char* p, size_t len, uint64_t seed, const uint64_t* secret)\n    noexcept {\n  seed ^= rapidhash_mix(seed ^ secret[2], secret[1]);\n  uint64_t a = 0, b = 0;\n  size_t i = len;\n  if (FOLLY_LIKELY(len <= 16)) {\n    if (len >= 4) {\n      seed ^= len;\n      if (len >= 8) {\n        const char* plast = p + len - 8;\n        a = rapidhash_read64(p);\n        b = rapidhash_read64(plast);\n      } else {\n        const char* plast = p + len - 4;\n        a = rapidhash_read32(p);\n        b = rapidhash_read32(plast);\n      }\n    } else if (len > 0) {\n      a = (static_cast<uint64_t>(p[0]) << 45) | static_cast<uint64_t>(p[len - 1]);\n      b = static_cast<uint64_t>(p[len >> 1]);\n    } else\n      a = b = 0;\n  } else {\n    if (i > 48) {\n      uint64_t see1 = seed, see2 = seed;\n      do {\n        seed =\n            rapidhash_mix(rapidhash_read64(p) ^ secret[0], rapidhash_read64(p + 8) ^ seed);\n        see1 = rapidhash_mix(\n            rapidhash_read64(p + 16) ^ secret[1], rapidhash_read64(p + 24) ^ see1);\n        see2 = rapidhash_mix(\n            rapidhash_read64(p + 32) ^ secret[2], rapidhash_read64(p + 40) ^ see2);\n        p += 48;\n        i -= 48;\n      } while (i > 48);\n      seed ^= see1;\n      seed ^= see2;\n    }\n    if (i > 16) {\n      seed = rapidhash_mix(rapidhash_read64(p) ^ secret[2], rapidhash_read64(p + 8) ^ seed);\n      if (i > 32) {\n        seed = rapidhash_mix(\n            rapidhash_read64(p + 16) ^ secret[2], rapidhash_read64(p + 24) ^ seed);\n      }\n    }\n    a = rapidhash_read64(p + i - 16) ^ i;\n    b = rapidhash_read64(p + i - 8);\n  }\n  a ^= secret[1];\n  b ^= seed;\n  rapidhash_mum(&a, &b);\n  return rapidhash_mix(a ^ secret[7], b ^ secret[1] ^ i);\n}\n\n} // namespace rapidhash\n\n/*\n *  rapidhash seeded hash function.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *  @param seed    64-bit seed used to alter the hash result predictably.\n *\n *  Calls rapidhash_internal using provided parameters and default secrets.\n *\n *  Returns a 64-bit hash.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_with_seed(\n    const char* key, size_t len, uint64_t seed) noexcept {\n  return rapidhash_detail::rapidhash_internal(key, len, seed, rapidhash_detail::rapidhash_secret);\n}\n\n/*\n *  rapidhash general purpose hash function.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *\n *  Calls rapidhash_withSeed using provided parameters and the default seed.\n *\n *  Returns a 64-bit hash.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t\nrapidhash(const char* key, size_t len) noexcept {\n  return rapidhash_with_seed(key, len, 0);\n}\n\n/*\n *  rapidhashMicro seeded hash function.\n *\n *  Designed for HPC and server applications, where cache misses make a\n * noticeable performance detriment. Clang-18+ compiles it to ~140 instructions\n * without stack usage, both on x86-64 and aarch64. Faster for sizes up to 512\n * bytes, just 15%-20% slower for inputs above 1kb.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *  @param seed    64-bit seed used to alter the hash result predictably.\n *\n *  Calls rapidhash_internal using provided parameters and default secrets.\n *\n *  Returns a 64-bit hash.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_with_seed(\n    const char* key, size_t len, uint64_t seed) noexcept {\n  return rapidhash_detail::rapidhashMicro_internal(key, len, seed, rapidhash_detail::rapidhash_secret);\n}\n\n/*\n *  rapidhashMicro hash function.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *\n *  Calls rapidhash_withSeed using provided parameters and the default seed.\n *\n *  Returns a 64-bit hash.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t\nrapidhashMicro(const char* key, size_t len) noexcept {\n  return rapidhashMicro_with_seed(key, len, 0);\n}\n\n/*\n *  rapidhashNano seeded hash function.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *  @param seed    64-bit seed used to alter the hash result predictably.\n *\n *  Calls rapidhash_internal using provided parameters and default secrets.\n *\n *  Returns a 64-bit hash.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_with_seed(\n    const char* key, size_t len, uint64_t seed) noexcept {\n  return rapidhash_detail::rapidhashNano_internal(key, len, seed, rapidhash_detail::rapidhash_secret);\n}\n\n/*\n *  rapidhashNano hash function.\n *\n *  Designed for Mobile and embedded applications, where keeping a small code\n * size is a top priority. Clang-18+ compiles it to less than 100 instructions\n * without stack usage, both on x86-64 and aarch64. The fastest for sizes up to\n * 48 bytes, but may be considerably slower for larger inputs.\n *\n *  @param key     Buffer to be hashed.\n *  @param len     @key length, in bytes.\n *\n *  Calls rapidhash_withSeed using provided parameters and the default seed.\n *\n *  Returns a 64-bit hash.\n */\nFOLLY_EXTERNAL_RAPIDHASH_INLINE_CONSTEXPR uint64_t\nrapidhashNano(const char* key, size_t len) noexcept {\n  return rapidhashNano_with_seed(key, len, 0);\n}\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/AddTasks-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <vector>\n\nnamespace folly {\nnamespace fibers {\n\ntemplate <typename T>\nTaskIterator<T>::TaskIterator(TaskIterator&& other) noexcept\n    : context_(std::move(other.context_)), id_(other.id_), fm_(other.fm_) {}\n\ntemplate <typename T>\ninline bool TaskIterator<T>::hasCompleted() const {\n  return context_->tasksConsumed < context_->results.size();\n}\n\ntemplate <typename T>\ninline bool TaskIterator<T>::hasPending() const {\n  return context_.use_count() > 1;\n}\n\ntemplate <typename T>\ninline bool TaskIterator<T>::hasNext() const {\n  return hasPending() || hasCompleted();\n}\n\ntemplate <typename T>\nfolly::Try<T> TaskIterator<T>::awaitNextResult() {\n  assert(hasCompleted() || hasPending());\n  reserve(1);\n\n  size_t i = context_->tasksConsumed++;\n  id_ = context_->results[i].first;\n  return std::move(context_->results[i].second);\n}\n\ntemplate <typename T>\ninline T TaskIterator<T>::awaitNext() {\n  return std::move(awaitNextResult().value());\n}\n\ntemplate <>\ninline void TaskIterator<void>::awaitNext() {\n  awaitNextResult().value();\n}\n\ntemplate <typename T>\ninline void TaskIterator<T>::reserve(size_t n) {\n  size_t tasksReady = context_->results.size() - context_->tasksConsumed;\n\n  // we don't need to do anything if there are already n or more tasks complete\n  // or if we have no tasks left to execute.\n  if (!hasPending() || tasksReady >= n) {\n    return;\n  }\n\n  n -= tasksReady;\n  size_t tasksLeft = context_->totalTasks - context_->results.size();\n  n = std::min(n, tasksLeft);\n\n  await_async([this, n](Promise<void> promise) {\n    context_->tasksToFulfillPromise = n;\n    context_->promise.assign(std::move(promise));\n  });\n}\n\ntemplate <typename T>\ninline size_t TaskIterator<T>::getTaskID() const {\n  assert(id_ != static_cast<size_t>(-1));\n  return id_;\n}\n\ntemplate <typename T>\ntemplate <typename F>\nvoid TaskIterator<T>::addTask(F&& func) {\n  static_assert(\n      std::is_convertible<invoke_result_t<F>, T>::value,\n      \"TaskIterator<T>: T must be convertible from func()'s return type\");\n\n  auto taskId = context_->totalTasks++;\n\n  fm_.addTask(\n      [taskId, context = context_, func = std::forward<F>(func)]() mutable {\n        context->results.emplace_back(\n            taskId, folly::makeTryWith(std::move(func)));\n\n        // Check for awaiting iterator.\n        if (context->promise.hasValue()) {\n          if (--context->tasksToFulfillPromise == 0) {\n            context->promise->setValue();\n            context->promise.clear();\n          }\n        }\n      });\n}\n\ntemplate <class InputIterator>\nTaskIterator<\n    invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>>\naddTasks(InputIterator first, InputIterator last) {\n  typedef invoke_result_t<\n      typename std::iterator_traits<InputIterator>::value_type>\n      ResultType;\n  typedef TaskIterator<ResultType> IteratorType;\n\n  IteratorType iterator;\n\n  for (; first != last; ++first) {\n    iterator.addTask(std::move(*first));\n  }\n\n  iterator.context_->results.reserve(iterator.context_->totalTasks);\n\n  return iterator;\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/AddTasks.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <vector>\n\n#include <folly/Optional.h>\n#include <folly/Try.h>\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/fibers/Promise.h>\n\nnamespace folly {\nnamespace fibers {\n\ntemplate <typename T>\nclass TaskIterator;\n\n/**\n * Schedules several tasks and immediately returns an iterator, that\n * allow one to traverse tasks in the order of their completion. All results\n * and exceptions thrown are stored alongside with the task id and are\n * accessible via iterator.\n *\n * @param first Range of tasks to be scheduled\n * @param last\n *\n * @return movable, non-copyable iterator\n */\ntemplate <class InputIterator>\nTaskIterator<invoke_result_t<\n    typename std::iterator_traits<InputIterator>::\n        value_type>> inline addTasks(InputIterator first, InputIterator last);\n\ntemplate <typename T>\nclass TaskIterator {\n public:\n  using value_type = T;\n\n  TaskIterator() : fm_(FiberManager::getFiberManager()) {}\n\n  // not copyable\n  TaskIterator(const TaskIterator& other) = delete;\n  TaskIterator& operator=(const TaskIterator& other) = delete;\n\n  // movable\n  TaskIterator(TaskIterator&& other) noexcept;\n  TaskIterator& operator=(TaskIterator&& other) = delete;\n\n  ~TaskIterator() = default;\n\n  /**\n   * Add one more task to the TaskIterator.\n   *\n   * @param func task to be added, will be scheduled on current FiberManager\n   */\n  template <typename F>\n  void addTask(F&& func);\n\n  /**\n   * @return True if there are tasks immediately available to be consumed (no\n   *         need to await on them).\n   */\n  bool hasCompleted() const;\n\n  /**\n   * @return True if there are tasks pending execution (need to awaited on).\n   */\n  bool hasPending() const;\n\n  /**\n   * @return True if there are any tasks (hasCompleted() || hasPending()).\n   */\n  bool hasNext() const;\n\n  /**\n   * Await for another task to complete. Will not await if the result is\n   * already available.\n   *\n   * @return result of the task completed.\n   * @throw exception thrown by the task.\n   */\n  T awaitNext();\n\n  /**\n   * Await until the specified number of tasks completes or there are no\n   * tasks left to await for.\n   * Note: Will not await if there are already the specified number of tasks\n   * available.\n   *\n   * @param n   Number of tasks to await for completition.\n   */\n  void reserve(size_t n);\n\n  /**\n   * @return id of the last task that was processed by awaitNext().\n   */\n  size_t getTaskID() const;\n\n private:\n  template <class InputIterator>\n  friend TaskIterator<\n      invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>>\n  addTasks(InputIterator first, InputIterator last);\n\n  struct Context {\n    std::vector<std::pair<size_t, folly::Try<T>>> results;\n    folly::Optional<Promise<void>> promise;\n    size_t totalTasks{0};\n    size_t tasksConsumed{0};\n    size_t tasksToFulfillPromise{0};\n  };\n\n  std::shared_ptr<Context> context_{std::make_shared<Context>()};\n  size_t id_{std::numeric_limits<size_t>::max()};\n  FiberManager& fm_;\n\n  folly::Try<T> awaitNextResult();\n};\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/AddTasks-inl.h>\n"
  },
  {
    "path": "folly/fibers/AtomicBatchDispatcher-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nnamespace folly {\nnamespace fibers {\n\ntemplate <typename InputT, typename ResultT>\nstruct AtomicBatchDispatcher<InputT, ResultT>::DispatchBaton {\n  DispatchBaton(DispatchFunctionT&& dispatchFunction)\n      : expectedCount_(0), dispatchFunction_(std::move(dispatchFunction)) {}\n\n  ~DispatchBaton() { fulfillPromises(); }\n\n  void reserve(size_t numEntries) { optEntries_.reserve(numEntries); }\n\n  void setExceptionWrapper(folly::exception_wrapper&& exWrapper) {\n    exceptionWrapper_ = std::move(exWrapper);\n  }\n\n  void setExpectedCount(size_t expectedCount) {\n    assert(expectedCount_ == 0 && \"expectedCount_ being set more than once\");\n    expectedCount_ = expectedCount;\n    optEntries_.resize(expectedCount_);\n  }\n\n  Future<ResultT> getFutureResult(InputT&& input, size_t sequenceNumber) {\n    if (sequenceNumber >= optEntries_.size()) {\n      optEntries_.resize(sequenceNumber + 1);\n    }\n    folly::Optional<Entry>& optEntry = optEntries_[sequenceNumber];\n    assert(!optEntry && \"Multiple inputs have the same token sequence number\");\n    optEntry = Entry(std::move(input));\n    return optEntry->promise.getFuture();\n  }\n\n private:\n  void setExceptionResults(folly::exception_wrapper&& ew_) {\n    auto ew = std::move(ew_);\n    for (auto& optEntry : optEntries_) {\n      if (optEntry) {\n        optEntry->promise.setException(ew);\n      }\n    }\n  }\n\n  void fulfillPromises() {\n    try {\n      // If an error message is set, set all promises to exception with message\n      if (exceptionWrapper_) {\n        return setExceptionResults(std::move(exceptionWrapper_));\n      }\n\n      // Validate entries count same as expectedCount_\n      assert(\n          optEntries_.size() == expectedCount_ &&\n          \"Entries vector did not have expected size\");\n      std::vector<size_t> vecTokensNotDispatched;\n      for (size_t i = 0; i < expectedCount_; ++i) {\n        if (!optEntries_[i]) {\n          vecTokensNotDispatched.push_back(i);\n        }\n      }\n      if (!vecTokensNotDispatched.empty()) {\n        return setExceptionResults(\n            make_exception_wrapper<ABDTokenNotDispatchedException>(\n                detail::createABDTokenNotDispatchedExMsg(\n                    vecTokensNotDispatched)));\n      }\n\n      // Create the inputs vector\n      std::vector<InputT> inputs;\n      inputs.reserve(expectedCount_);\n      for (auto& optEntry : optEntries_) {\n        inputs.emplace_back(std::move(optEntry->input));\n      }\n\n      // Call the user provided batch dispatch function to get all results\n      // and make sure that we have the expected number of results returned\n      auto results = dispatchFunction_(std::move(inputs));\n      if (results.size() != expectedCount_) {\n        return setExceptionResults(\n            make_exception_wrapper<ABDUsageException>(\n                detail::createUnexpectedNumResultsABDUsageExMsg(\n                    expectedCount_, results.size())));\n      }\n\n      // Fulfill the promises with the results from the batch dispatch\n      for (size_t i = 0; i < expectedCount_; ++i) {\n        optEntries_[i]->promise.setValue(std::move(results[i]));\n      }\n    } catch (...) {\n      // Set exceptions thrown when executing the user provided dispatch func\n      return setExceptionResults(exception_wrapper{current_exception()});\n    }\n  }\n\n  struct Entry {\n    InputT input;\n    folly::Promise<ResultT> promise;\n\n    Entry(Entry&& other) noexcept\n        : input(std::move(other.input)), promise(std::move(other.promise)) {}\n\n    Entry& operator=(Entry&& other) noexcept {\n      input = std::move(other.input);\n      promise = std::move(other.promise);\n      return *this;\n    }\n\n    explicit Entry(InputT&& input_) : input(std::move(input_)) {}\n  };\n\n  size_t expectedCount_;\n  DispatchFunctionT dispatchFunction_;\n  std::vector<folly::Optional<Entry>> optEntries_;\n  folly::exception_wrapper exceptionWrapper_;\n};\n\ntemplate <typename InputT, typename ResultT>\nAtomicBatchDispatcher<InputT, ResultT>::Token::Token(\n    std::shared_ptr<DispatchBaton> baton, size_t sequenceNumber)\n    : baton_(std::move(baton)), sequenceNumber_(sequenceNumber) {}\n\ntemplate <typename InputT, typename ResultT>\nsize_t AtomicBatchDispatcher<InputT, ResultT>::Token::sequenceNumber() const {\n  return sequenceNumber_;\n}\n\ntemplate <typename InputT, typename ResultT>\nFuture<ResultT> AtomicBatchDispatcher<InputT, ResultT>::Token::dispatch(\n    InputT input) {\n  auto baton = std::move(baton_);\n  if (!baton) {\n    throw ABDUsageException(\n        \"Dispatch called more than once on the same Token object\");\n  }\n  return baton->getFutureResult(std::move(input), sequenceNumber_);\n}\n\ntemplate <typename InputT, typename ResultT>\nAtomicBatchDispatcher<InputT, ResultT>::AtomicBatchDispatcher(\n    DispatchFunctionT&& dispatchFunc)\n    : numTokensIssued_(0),\n      baton_(std::make_shared<DispatchBaton>(std::move(dispatchFunc))) {}\n\ntemplate <typename InputT, typename ResultT>\nAtomicBatchDispatcher<InputT, ResultT>::~AtomicBatchDispatcher() {\n  if (baton_) {\n    // Set error here rather than throw because we do not want to throw from\n    // the destructor of AtomicBatchDispatcher\n    baton_->setExceptionWrapper(\n        folly::make_exception_wrapper<ABDCommitNotCalledException>());\n    commit();\n  }\n}\n\ntemplate <typename InputT, typename ResultT>\nvoid AtomicBatchDispatcher<InputT, ResultT>::reserve(size_t numEntries) {\n  if (!baton_) {\n    throw ABDUsageException(\"Cannot call reserve(....) after calling commit()\");\n  }\n  baton_->reserve(numEntries);\n}\n\ntemplate <typename InputT, typename ResultT>\nauto AtomicBatchDispatcher<InputT, ResultT>::getToken() -> Token {\n  if (!baton_) {\n    throw ABDUsageException(\"Cannot issue more tokens after calling commit()\");\n  }\n  return Token(baton_, numTokensIssued_++);\n}\n\ntemplate <typename InputT, typename ResultT>\nvoid AtomicBatchDispatcher<InputT, ResultT>::commit() {\n  auto baton = std::move(baton_);\n  if (!baton) {\n    throw ABDUsageException(\n        \"Cannot call commit() more than once on the same dispatcher\");\n  }\n  baton->setExpectedCount(numTokensIssued_);\n}\n\ntemplate <typename InputT, typename ResultT>\nAtomicBatchDispatcher<InputT, ResultT> createAtomicBatchDispatcher(\n    folly::Function<std::vector<ResultT>(std::vector<InputT>&&)> dispatchFunc,\n    size_t initialCapacity) {\n  auto abd = AtomicBatchDispatcher<InputT, ResultT>(std::move(dispatchFunc));\n  if (initialCapacity) {\n    abd.reserve(initialCapacity);\n  }\n  return abd;\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/AtomicBatchDispatcher.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <stdexcept>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <folly/CPortability.h>\n#include <folly/Function.h>\n#include <folly/Optional.h>\n#include <folly/fibers/detail/AtomicBatchDispatcher.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * An exception class that gets thrown when the AtomicBatchDispatcher is used\n * incorrectly. This is indicative of a bug in the user code.\n * Examples are, multiple dispatch calls on the same token, trying to get more\n * tokens from the dispatcher after commit has been called, etc.\n */\nclass FOLLY_EXPORT ABDUsageException : public std::logic_error {\n  using std::logic_error::logic_error;\n};\n\n/**\n * An exception class that gets set on the promise for dispatched tokens, when\n * the AtomicBatchDispatcher was destroyed before commit was called on it.\n */\nclass FOLLY_EXPORT ABDCommitNotCalledException : public std::runtime_error {\n public:\n  ABDCommitNotCalledException()\n      : std::runtime_error(\n            \"AtomicBatchDispatcher destroyed before commit() was called\") {}\n};\n\n/**\n * An exception class that gets set on the promise for dispatched tokens, when\n * one or more other tokens in the batch were destroyed before dispatch was\n * called on them.\n * Only here so that the caller can distinguish the real failure cause\n * rather than these subsequently thrown exceptions.\n */\nclass FOLLY_EXPORT ABDTokenNotDispatchedException : public std::runtime_error {\n  using std::runtime_error::runtime_error;\n};\n\n/**\n * AtomicBatchDispatcher should be used if you want to process fiber tasks in\n * parallel, but require to synchronize them at some point. The canonical\n * example is to create a database transaction dispatch round. This API notably\n * enforces that all tasks in the batch have reached the synchronization point\n * before the user provided dispatch function is called with all the inputs\n * provided in one function call. It also provides a guarantee that the inputs\n * in the vector of inputs passed to the user provided dispatch function will be\n * in the same order as the order in which the token for the job was issued.\n *\n * Use this when you want all the inputs in the batch to be processed by a\n * single function call to the user provided dispatch function.\n * The user provided dispatch function takes a vector of InputT as input and\n * returns a vector of ResultT.\n * To use an AtomicBatchDispatcher, create it by providing a dispatch function:\n * TO EITHER the constructor of the AtomicBatchDispatcher class\n * (can call reserve method on the dispatcher to reserve space (for number of\n *  inputs expected)),\n * OR the createAtomicBatchDispatcher function in folly::fibers namespace\n *    (optionally specify an initial capacity (for number of inputs expected)).\n * The AtomicBatchDispatcher object created using this call (dispatcher),\n * is the only object that can issue tokens (Token objects) that are used to\n * add an input to the batch. A single Token is issued when the user calls\n * the getToken function on the dispatcher.\n * Token objects cannot be copied (can only be moved). User can call the public\n * dispatch function on the Token providing a single input value. The dispatch\n * function returns a folly::Future<ResultT> value that the user can then wait\n * on to obtain a ResultT value. The ResultT value will only be available once\n * the dispatch function has been called on all the Tokens in the batch and the\n * user has called dispatcher.commit() to indicate no more batched transactions\n * are to be added.\n * User code pertaining to a task can be run between the point where a token for\n * the task has been issued and before calling the dispatch function on the\n * token. Since this code can potentially throw, the token issued for a task\n * should be moved into this processing code in such a way that if an exception\n * is thrown and then handled, the token object for the task is destroyed.\n * The batch query dispatcher will wait until all tokens have either been\n * destroyed or have had the dispatch function called on them. Leaking an\n * issued token will cause the batch dispatch to wait forever to happen.\n *\n * The AtomicBatchDispatcher object is referred to as the dispatcher below.\n *\n * POSSIBLE ERRORS:\n * 1) The dispatcher is destroyed before calling commit on it, for example\n *    because the user forgot to call commit OR an exception was thrown\n *    in user code before the call to commit:\n *    - The future ResultT has an exception of type ABDCommitNotCalledException\n *      set for all tokens that were issued by the dispatcher (once all tokens\n *      are either destroyed or have called dispatch)\n * 2) Calling the dispatch function more than once on the same Token object\n *    (or a moved version of the same Token):\n *    - Subsequent calls to dispatch (after the first one) will throw an\n *      ABDUsageException exception (the batch itself will not have any errors\n *      and will get processed)\n * 3) One/more of the Tokens issued are destroyed before calling dispatch on\n *    it/them:\n *    - The future ResultT has an ABDTokenNotDispatchedException set for all\n *      tokens that were issued by the dispatcher (once all tokens are either\n *      destroyed or have called dispatch)\n * 4) dispatcher.getToken() is called after calling dispatcher.commit()\n *    - the call to getToken() will throw an ABDUsageException exception\n *      (the batch itself will not have any errors and will get processed).\n * 5) All tokens were issued and called dispatch, the user provided batch\n *    dispatch function is called, but that function throws any exception.\n *    - The future ResultT has exception for all tokens that were issued by\n *      the dispatcher. The result will contain the wrapped user exception.\n *\n * EXAMPLE (There are other ways to achieve this, but this is one example):\n * - User creates an AtomicBatchDispatcher on stack\n *     auto dispatcher =\n *         folly::fibers::createAtomicBatchDispatcher(dispatchFunc, count);\n * - User creates \"count\" number of token objects by calling \"getToken\" count\n *   number of times\n *     std::vector<Job> jobs;\n *     for (size_t i = 0; i < count; ++i) {\n *       auto token = dispatcher.getToken();\n *       jobs.push_back(Job(std::move(token), singleInputValueToProcess);\n *     }\n * - User calls commit() on the dispatcher to indicate that no new tokens will\n *   be issued for this batch\n *     dispatcher.commit();\n * - Use any single threaded executor that will process the jobs\n * - On each execution (fiber) preprocess a single \"Job\" that has been moved in\n *   from the original vector \"jobs\". This way if the preprocessing throws\n *   the Job object being processed is destroyed and so is the token.\n * - On each execution (fiber) call the dispatch on the token\n *     auto future = job.token.dispatch(job.input);\n * - Save the future returned so that eventually you can wait on the results\n *     ResultT result;\n *     try {\n *       result = future.value();\n *       // future.hasValue() is true\n *     } catch (...) {\n *       // future.hasException() is true\n *       <DO WHATEVER YOU WANT IN CASE OF ERROR> }\n *     }\n *\n * NOTES:\n * - AtomicBatchDispatcher is not thread safe.\n * - Works for executors that run tasks on a single thread.\n */\ntemplate <typename InputT, typename ResultT>\nclass AtomicBatchDispatcher {\n private:\n  struct DispatchBaton;\n  friend struct DispatchBaton;\n\n public:\n  using DispatchFunctionT =\n      folly::Function<std::vector<ResultT>(std::vector<InputT>&&)>;\n\n  class Token {\n   public:\n    explicit Token(std::shared_ptr<DispatchBaton> baton, size_t sequenceNumber);\n\n    Future<ResultT> dispatch(InputT input);\n\n    // Allow moving a Token object\n    Token(Token&&) = default;\n    Token& operator=(Token&&) = default;\n\n    size_t sequenceNumber() const;\n\n   private:\n    // Disallow copying a Token object\n    Token(const Token&) = delete;\n    Token& operator=(const Token&) = delete;\n\n    std::shared_ptr<DispatchBaton> baton_;\n    size_t sequenceNumber_;\n  };\n\n  explicit AtomicBatchDispatcher(DispatchFunctionT&& dispatchFunc);\n\n  ~AtomicBatchDispatcher();\n\n  // numEntries is a *hint* about the number of inputs to expect:\n  // - It is used purely to reserve space for storing vector of inputs etc.,\n  //   so that reeallocation and move copy are reduced / not needed.\n  // - It is provided purely for performance reasons\n  void reserve(size_t numEntries);\n\n  Token getToken();\n\n  void commit();\n\n  // Allow moving an AtomicBatchDispatcher object\n  AtomicBatchDispatcher(AtomicBatchDispatcher&&) = default;\n  AtomicBatchDispatcher& operator=(AtomicBatchDispatcher&&) = default;\n\n private:\n  // Disallow copying an AtomicBatchDispatcher object\n  AtomicBatchDispatcher(const AtomicBatchDispatcher&) = delete;\n  AtomicBatchDispatcher& operator=(const AtomicBatchDispatcher&) = delete;\n\n  size_t numTokensIssued_;\n  std::shared_ptr<DispatchBaton> baton_;\n};\n\n// initialCapacity is a *hint* about the number of inputs to expect:\n// - It is used purely to reserve space for storing vector of inputs etc.,\n//   so that reeallocation and move copy are reduced / not needed.\n// - It is provided purely for performance reasons\ntemplate <typename InputT, typename ResultT>\nAtomicBatchDispatcher<InputT, ResultT> createAtomicBatchDispatcher(\n    folly::Function<std::vector<ResultT>(std::vector<InputT>&&)> dispatchFunc,\n    size_t initialCapacity = 0);\n\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/AtomicBatchDispatcher-inl.h>\n"
  },
  {
    "path": "folly/fibers/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:auto_headers.bzl\", \"AutoHeaders\")\nload(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"add_tasks\",\n    headers = [\n        \"AddTasks.h\",\n        \"AddTasks-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly:optional\",\n        \"//folly:try\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"atomic_batch_dispatcher\",\n    headers = [\n        \"AtomicBatchDispatcher.h\",\n        \"AtomicBatchDispatcher-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:function\",\n        \"//folly:optional\",\n        \"//folly/fibers/detail:atomic_batch_dispatcher\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"batch_dispatcher\",\n    headers = [\"BatchDispatcher.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"batch_semaphore\",\n    srcs = [\"BatchSemaphore.cpp\"],\n    headers = [\"BatchSemaphore.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":semaphore_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"boost_context_compatibility\",\n    headers = [\"BoostContextCompatibility.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:function\",\n    ],\n    exported_external_deps = [\n        (\"boost\", None, \"boost_context\"),\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"core\",\n    srcs = [\n        \"Baton.cpp\",\n        \"Fiber.cpp\",\n        \"FiberManager.cpp\",\n    ],\n    raw_headers = [\n        \"Baton.h\",\n        \"Baton-inl.h\",\n        \"Fiber.h\",\n        \"Fiber-inl.h\",\n        \"FiberManagerInternal.h\",\n        \"FiberManagerInternal-inl.h\",\n        \"Promise.h\",\n        \"Promise-inl.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:config\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:unistd\",\n        \"fbsource//xplat/folly/synchronization:sanitize_thread\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:constexpr_math\",\n        \"//xplat/folly:singleton_thread_local\",\n        \"//xplat/folly/detail:memory_idler\",\n        \"//xplat/folly/memory:sanitize_address\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:atomic_linked_list\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:executor\",\n        \"//xplat/folly:function\",\n        \"//xplat/folly:intrusive_list\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:try\",\n        \"//xplat/folly/detail:futex\",\n        \"//xplat/folly/executors:execution_observer\",\n        \"//xplat/folly/experimental/coro:coroutine\",\n        \"//xplat/folly/fibers:boost_context_compatibility\",\n        \"//xplat/folly/fibers:guard_page_allocator\",\n        \"//xplat/folly/fibers:loop_controller\",\n        \"//xplat/folly/fibers:traits\",\n        \"//xplat/folly/functional:invoke\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:request_context\",\n        \"//xplat/folly/lang:thunk\",\n        \"//xplat/folly/tracing:async_stack\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"core\",\n    srcs = [\n        \"Baton.cpp\",\n        \"Fiber.cpp\",\n        \"FiberManager.cpp\",\n    ],\n    auto_headers = AutoHeaders.NONE,\n    headers = [\n        \"Baton.h\",\n        \"Baton-inl.h\",\n        \"Fiber.h\",\n        \"Fiber-inl.h\",\n        \"FiberManagerInternal.h\",\n        \"FiberManagerInternal-inl.h\",\n        \"Promise.h\",\n        \"Promise-inl.h\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:constexpr_math\",\n        \"//folly:singleton_thread_local\",\n        \"//folly/detail:memory_idler\",\n        \"//folly/memory:sanitize_address\",\n        \"//folly/portability:asm\",\n        \"//folly/portability:config\",\n        \"//folly/portability:sys_syscall\",\n        \"//folly/portability:unistd\",\n        \"//folly/synchronization:sanitize_thread\",\n    ],\n    exported_deps = [\n        \":boost_context_compatibility\",\n        \":guard_page_allocator\",\n        \":loop_controller\",\n        \":traits\",\n        \"//folly:atomic_linked_list\",\n        \"//folly:c_portability\",\n        \"//folly:executor\",\n        \"//folly:function\",\n        \"//folly:intrusive_list\",\n        \"//folly:likely\",\n        \"//folly:memory\",\n        \"//folly:optional\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:try\",\n        \"//folly/coro:coroutine\",\n        \"//folly/detail:async_trace\",\n        \"//folly/detail:futex\",\n        \"//folly/executors:execution_observer\",\n        \"//folly/functional:invoke\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:request_context\",\n        \"//folly/lang:thunk\",\n        \"//folly/portability:pthread\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"core_manager\",\n    headers = [\n        \"FiberManager.h\",\n        \"FiberManager-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly/functional:invoke\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"event_base_loop_controller\",\n    headers = [\n        \"EventBaseLoopController.h\",\n        \"EventBaseLoopController-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \":executor_based_loop_controller\",\n        \"//folly:cancellation_token\",\n        \"//folly:memory\",\n        \"//folly/io/async:async_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"executor_based_loop_controller\",\n    headers = [\"ExecutorBasedLoopController.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":loop_controller\",\n        \"//folly:executor\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"executor_loop_controller\",\n    headers = [\n        \"ExecutorLoopController.h\",\n        \"ExecutorLoopController-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \":executor_based_loop_controller\",\n        \"//folly:executor\",\n        \"//folly:scope_guard\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fiber_manager_map\",\n    headers = [\n        \"FiberManagerMap.h\",\n        \"FiberManagerMap-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \":event_base_loop_controller\",\n        \"//folly:function\",\n        \"//folly:scope_guard\",\n        \"//folly:singleton_thread_local\",\n        \"//folly:synchronized\",\n        \"//folly/container:f14_hash\",\n        \"//folly/io/async:async_base\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fibers\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":add_tasks\",  # @manual\n        \":atomic_batch_dispatcher\",  # @manual\n        \":batch_dispatcher\",  # @manual\n        \":batch_semaphore\",  # @manual\n        \":boost_context_compatibility\",  # @manual\n        \":core\",  # @manual\n        \":core_manager\",  # @manual\n        \":event_base_loop_controller\",  # @manual\n        \":fiber_manager_map\",  # @manual\n        \":for_each\",  # @manual\n        \":generic_baton\",  # @manual\n        \":guard_page_allocator\",  # @manual\n        \":loop_controller\",  # @manual\n        \":semaphore\",  # @manual\n        \":semaphore_base\",  # @manual\n        \":simple_loop_controller\",  # @manual\n        \":timed_mutex\",  # @manual\n        \":traits\",  # @manual\n        \":when_n\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"for_each\",\n    headers = [\n        \"ForEach.h\",\n        \"ForEach-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly/functional:invoke\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"generic_baton\",\n    headers = [\"GenericBaton.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"guard_page_allocator\",\n    srcs = [\"GuardPageAllocator.cpp\"],\n    headers = [\"GuardPageAllocator.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:singleton\",\n        \"//folly:spin_lock\",\n        \"//folly:synchronized\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:unistd\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"loop_controller\",\n    headers = [\"LoopController.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/io/async:async_base_fwd\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"semaphore\",\n    srcs = [\"Semaphore.cpp\"],\n    headers = [\"Semaphore.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly:intrusive_list\",\n        \"//folly:synchronized\",\n        \"//folly/coro:task\",\n        \"//folly/coro:timeout\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"semaphore_base\",\n    srcs = [\"SemaphoreBase.cpp\"],\n    headers = [\"SemaphoreBase.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly:intrusive_list\",\n        \"//folly:synchronized\",\n        \"//folly/coro:task\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"simple_loop_controller\",\n    srcs = [\"SimpleLoopController.cpp\"],\n    headers = [\"SimpleLoopController.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/io/async:async_base\",\n    ],\n    exported_deps = [\n        \":core_manager\",\n        \":loop_controller\",\n        \"//folly:function\",\n        \"//folly:likely\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"timed_mutex\",\n    headers = [\n        \"CallOnce.h\",\n        \"TimedMutex.h\",\n        \"TimedMutex-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":generic_baton\",\n        \"//folly:intrusive_list\",\n        \"//folly:portability\",\n        \"//folly:spin_lock\",\n        \"//folly/synchronization:call_once\",\n        \"//folly/synchronization/detail:sleeper\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"traits\",\n    headers = [\"traits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"when_n\",\n    headers = [\n        \"WhenN.h\",\n        \"WhenN-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \":for_each\",\n        \"//folly:optional\",\n        \"//folly/functional:invoke\",\n    ],\n)\n"
  },
  {
    "path": "folly/fibers/BatchDispatcher.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <exception>\n#include <memory>\n#include <stdexcept>\n#include <vector>\n\n#include <folly/Function.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * BatchDispatcher is useful for batching values while doing I/O.\n * For example, if you are launching multiple tasks which take a\n * single id and each task fetches from database, you can use BatchDispatcher\n * to batch those ids and do a single query requesting all those ids.\n *\n * To use this, create a BatchDispatcher with a dispatch function\n * which consumes a vector of values and returns a vector of results\n * in the same order. Add values to BatchDispatcher using add function,\n * which returns a future to the result set in your dispatch function.\n *\n * Implementation Logic:\n *  - using FiberManager as executor example, user creates a\n *    thread_local BatchDispatcher, on which user calls add(value).\n *  - add(value) adds the value in a vector and also schedules a new\n *    task(BatchDispatchFunction) which will read the vector of values and call\n *    user's DispatchFunction() on it.\n *  - assuming the executor queues all the task and runs them in order of their\n *    creation time, then BatchDispatcher will run later than all the tasks\n *    already created. Depending on this, all the values were added in these\n *    tasks would be picked up by BatchDispatchFunction()\n *\n * Example:\n *  - User schedules Task1, Task2, Task3 each of them calls BatchDispatch.add()\n *    with id1, id2, id3 respectively.\n *  - Executor's state {Task1, Task2, Task3}, BatchDispatchers state {}\n *  - After Task1 calls BatchDispatcher.add():\n *    Executor's state {Task2, Task3, BatchDispatchFunction},\n *    BatchDispatcher's state {id1}\n *  - After Task2 calls BatchDispatcher.add():\n *    Executor's state {Task3, BatchDispatchFunction},\n *    BatchDispatcher's state {id1, id2}\n *  - After Task3 calls BatchDispatcher.add():\n *    Executor's state {BatchDispatchFunction},\n *    BatchDispatcher's state {id1, id2, id3}\n *  - Now BatchDispatcher calls user's Dispatch function with {id1, id2, id3}\n *\n * Note:\n *  - This only works with executors which runs\n *    the tasks in order of their schedule time.\n *  - BatchDispatcher is not thread safe.\n */\ntemplate <typename ValueT, typename ResultT, typename ExecutorT>\nclass BatchDispatcher {\n public:\n  using ValueBatchT = std::vector<ValueT>;\n  using ResultBatchT = std::vector<ResultT>;\n  using PromiseBatchT = std::vector<folly::Promise<ResultT>>;\n  using DispatchFunctionT = folly::Function<ResultBatchT(ValueBatchT&&)>;\n\n  BatchDispatcher(ExecutorT& executor, DispatchFunctionT dispatchFunc)\n      : executor_(executor),\n        state_(new DispatchState(std::move(dispatchFunc))) {}\n\n  Future<ResultT> add(ValueT value) {\n    if (state_->values.empty()) {\n      executor_.add([state = state_]() { dispatchFunctionWrapper(*state); });\n    }\n\n    folly::Promise<ResultT> resultPromise;\n    auto resultFuture = resultPromise.getFuture();\n\n    state_->values.emplace_back(std::move(value));\n    state_->promises.emplace_back(std::move(resultPromise));\n\n    return resultFuture;\n  }\n\n private:\n  struct DispatchState {\n    explicit DispatchState(DispatchFunctionT&& dispatchFunction)\n        : dispatchFunc(std::move(dispatchFunction)) {}\n\n    DispatchFunctionT dispatchFunc;\n    ValueBatchT values;\n    PromiseBatchT promises;\n  };\n\n  static void dispatchFunctionWrapper(DispatchState& state) {\n    ValueBatchT values;\n    PromiseBatchT promises;\n    state.values.swap(values);\n    state.promises.swap(promises);\n\n    try {\n      auto results = state.dispatchFunc(std::move(values));\n      if (results.size() != promises.size()) {\n        throw std::logic_error(\n            \"Unexpected number of results returned from dispatch function\");\n      }\n\n      for (size_t i = 0; i < promises.size(); i++) {\n        promises[i].setValue(std::move(results[i]));\n      }\n    } catch (...) {\n      for (size_t i = 0; i < promises.size(); i++) {\n        promises[i].setException(exception_wrapper(current_exception()));\n      }\n    }\n  }\n\n  ExecutorT& executor_;\n  std::shared_ptr<DispatchState> state_;\n};\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/BatchSemaphore.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/BatchSemaphore.h>\n\nnamespace folly {\nnamespace fibers {\n\nvoid BatchSemaphore::signal(int64_t tokens) {\n  signalSlow(tokens);\n}\n\nvoid BatchSemaphore::wait(int64_t tokens) {\n  wait_common(tokens);\n}\n\nbool BatchSemaphore::try_wait(Waiter& waiter, int64_t tokens) {\n  return try_wait_common(waiter, tokens);\n}\n\nbool BatchSemaphore::try_wait(int64_t tokens) {\n  return try_wait_common(tokens);\n}\n\n#if FOLLY_HAS_COROUTINES\n\ncoro::Task<void> BatchSemaphore::co_wait(int64_t tokens) {\n  return co_wait_common(tokens);\n}\n\n#endif\n\nSemiFuture<Unit> BatchSemaphore::future_wait(int64_t tokens) {\n  return future_wait_common(tokens);\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/BatchSemaphore.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/fibers/SemaphoreBase.h>\n\nnamespace folly {\nnamespace fibers {\n\n/*\n * Fiber-compatible batch semaphore with ability to perform batch token\n * increment/decrement. Will safely block fibers that wait when no tokens are\n * available and wake fibers when signalled.\n */\nclass BatchSemaphore : public SemaphoreBase {\n public:\n  explicit BatchSemaphore(size_t tokenCount) : SemaphoreBase{tokenCount} {}\n\n  BatchSemaphore(const BatchSemaphore&) = delete;\n  BatchSemaphore(BatchSemaphore&&) = delete;\n  BatchSemaphore& operator=(const BatchSemaphore&) = delete;\n  BatchSemaphore& operator=(BatchSemaphore&&) = delete;\n\n  /*\n   * Release requested tokens in the semaphore. Signal the waiter if necessary.\n   */\n  void signal(int64_t tokens);\n\n  /*\n   * Wait for requested tokens in the semaphore.\n   */\n  void wait(int64_t tokens);\n\n  /**\n   * Try to wait on the semaphore.\n   * Return true on success.\n   * On failure, the passed waiter is enqueued, its baton will be posted once\n   * semaphore has requested tokens. Caller is responsible to wait then signal.\n   */\n  bool try_wait(Waiter& waiter, int64_t tokens);\n\n  /**\n   * If the semaphore has capacity, removes a token and returns true. Otherwise\n   * returns false and leaves the semaphore unchanged.\n   */\n  bool try_wait(int64_t tokens);\n\n#if FOLLY_HAS_COROUTINES\n\n  /*\n   * Wait for requested tokens in the semaphore.\n   *\n   * Note that this wait-operation can be cancelled by requesting cancellation\n   * on the awaiting coroutine's associated CancellationToken.\n   * If the operation is successfully cancelled then it will complete with\n   * an error of type folly::OperationCancelled.\n   *\n   * Note that requesting cancellation of the operation will only have an\n   * effect if the operation does not complete synchronously (ie. was not\n   * already in a signalled state).\n   *\n   * If the semaphore was already in a signalled state prior to awaiting the\n   * returned Task then the operation will complete successfully regardless\n   * of whether cancellation was requested.\n   */\n  coro::Task<void> co_wait(int64_t tokens);\n\n#endif\n\n  /*\n   * Wait for requested tokens in the semaphore.\n   */\n  SemiFuture<Unit> future_wait(int64_t tokens);\n};\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Baton-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/detail/AsyncTrace.h>\n#include <folly/fibers/Fiber.h>\n#include <folly/fibers/FiberManagerInternal.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass Baton::FiberWaiter : public Baton::Waiter {\n public:\n  void setFiber(Fiber& fiber) {\n    DCHECK(!fiber_);\n    fiber_ = &fiber;\n  }\n\n  void post() override { fiber_->resume(); }\n\n private:\n  Fiber* fiber_{nullptr};\n};\n\ninline Baton::Baton() noexcept : Baton(NO_WAITER) {\n  assert(Baton(NO_WAITER).futex_.futex == static_cast<uint32_t>(NO_WAITER));\n  assert(Baton(POSTED).futex_.futex == static_cast<uint32_t>(POSTED));\n  assert(Baton(TIMEOUT).futex_.futex == static_cast<uint32_t>(TIMEOUT));\n  assert(\n      Baton(THREAD_WAITING).futex_.futex ==\n      static_cast<uint32_t>(THREAD_WAITING));\n\n  assert(futex_.futex.is_lock_free());\n  assert(waiter_.is_lock_free());\n}\n\ntemplate <typename F>\nvoid Baton::wait(F&& mainContextFunc) {\n  auto fm = FiberManager::getFiberManagerUnsafe();\n  if (!fm || !fm->activeFiber_) {\n    mainContextFunc();\n    return waitThread();\n  }\n\n  return waitFiber(*fm, std::forward<F>(mainContextFunc));\n}\n\ntemplate <typename F>\nvoid Baton::waitFiber(FiberManager& fm, F&& mainContextFunc) {\n  FiberWaiter waiter;\n  auto f = [this, &mainContextFunc, &waiter](Fiber& fiber) mutable {\n    waiter.setFiber(fiber);\n    setWaiter(waiter);\n\n    mainContextFunc();\n  };\n\n  fm.awaitFunc_ = std::ref(f);\n  fm.activeFiber_->preempt(Fiber::AWAITING);\n}\n\ntemplate <typename Clock, typename Duration>\nbool Baton::timedWaitThread(\n    const std::chrono::time_point<Clock, Duration>& deadline) {\n  auto waiter = waiter_.load();\n\n  folly::async_tracing::logBlockingOperation(\n      std::chrono::duration_cast<std::chrono::milliseconds>(\n          deadline - Clock::now()));\n\n  if (FOLLY_LIKELY(\n          waiter == NO_WAITER &&\n          waiter_.compare_exchange_strong(waiter, THREAD_WAITING))) {\n    do {\n      auto* futex = &futex_.futex;\n      const auto wait_rv = folly::detail::futexWaitUntil(\n          futex, uint32_t(THREAD_WAITING), deadline);\n      if (wait_rv == folly::detail::FutexResult::TIMEDOUT) {\n        return false;\n      }\n      waiter = waiter_.load(std::memory_order_acquire);\n    } while (waiter == THREAD_WAITING);\n  }\n\n  if (FOLLY_LIKELY(waiter == POSTED)) {\n    return true;\n  }\n\n  // Handle errors\n  if (waiter == TIMEOUT) {\n    throw std::logic_error(\"Thread baton can't have timeout status\");\n  }\n  if (waiter == THREAD_WAITING) {\n    throw std::logic_error(\"Other thread is already waiting on this baton\");\n  }\n  throw std::logic_error(\"Other waiter is already waiting on this baton\");\n}\n\ntemplate <typename Rep, typename Period, typename F>\nbool Baton::try_wait_for(\n    const std::chrono::duration<Rep, Period>& timeout, F&& mainContextFunc) {\n  const auto deadline = std::chrono::steady_clock::now() + timeout;\n  return try_wait_until(deadline, static_cast<F&&>(mainContextFunc));\n}\n\ntemplate <typename Clock, typename Duration, typename F>\nbool Baton::try_wait_until(\n    const std::chrono::time_point<Clock, Duration>& deadline,\n    F&& mainContextFunc) {\n  auto fm = FiberManager::getFiberManagerUnsafe();\n\n  if (!fm || !fm->activeFiber_) {\n    mainContextFunc();\n    return timedWaitThread(deadline);\n  }\n\n  assert(Clock::is_steady); // fiber timer assumes deadlines are steady\n\n  auto timeoutFunc = [this]() mutable { this->postHelper(TIMEOUT); };\n  TimeoutHandler handler;\n  handler.timeoutFunc_ = std::ref(timeoutFunc);\n\n  // TODO: have timer support arbitrary clocks\n  const auto now = Clock::now();\n  const auto timeoutMs = std::chrono::duration_cast<std::chrono::milliseconds>(\n      FOLLY_LIKELY(now <= deadline) ? deadline - now : Duration{});\n  fm->loopController_->timer()->scheduleTimeout(&handler, timeoutMs);\n  waitFiber(*fm, static_cast<F&&>(mainContextFunc));\n\n  return waiter_ == POSTED;\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Baton.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/Baton.h>\n\n#include <chrono>\n\n#include <folly/detail/MemoryIdler.h>\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/portability/Asm.h>\n\nnamespace folly {\nnamespace fibers {\n\nusing folly::detail::futexWake;\n\nvoid Baton::setWaiter(Waiter& waiter) {\n  auto curr_waiter = waiter_.load();\n  do {\n    if (FOLLY_LIKELY(curr_waiter == NO_WAITER)) {\n      continue;\n    } else if (curr_waiter == POSTED || curr_waiter == TIMEOUT) {\n      waiter.post();\n      break;\n    } else {\n      throw std::logic_error(\"Some waiter is already waiting on this Baton.\");\n    }\n  } while (!waiter_.compare_exchange_weak(\n      curr_waiter, reinterpret_cast<intptr_t>(&waiter)));\n}\n\nvoid Baton::wait() {\n  wait([]() {});\n}\n\nvoid Baton::wait(TimeoutHandler& timeoutHandler) {\n  auto timeoutFunc = [this] {\n    if (!try_wait()) {\n      postHelper(TIMEOUT);\n    }\n  };\n  timeoutHandler.timeoutFunc_ = std::ref(timeoutFunc);\n  timeoutHandler.fiberManager_ = FiberManager::getFiberManagerUnsafe();\n  wait();\n  timeoutHandler.cancelTimeout();\n}\n\nvoid Baton::waitThread() {\n  auto waiter = waiter_.load();\n\n  auto waitStart = std::chrono::steady_clock::now();\n\n  if (FOLLY_LIKELY(\n          waiter == NO_WAITER &&\n          waiter_.compare_exchange_strong(waiter, THREAD_WAITING))) {\n    do {\n      folly::detail::MemoryIdler::futexWait(\n          futex_.futex, uint32_t(THREAD_WAITING));\n      waiter = waiter_.load(std::memory_order_acquire);\n    } while (waiter == THREAD_WAITING);\n  }\n\n  folly::async_tracing::logBlockingOperation(\n      std::chrono::duration_cast<std::chrono::milliseconds>(\n          std::chrono::steady_clock::now() - waitStart));\n\n  if (FOLLY_LIKELY(waiter == POSTED)) {\n    return;\n  }\n\n  // Handle errors\n  if (waiter == TIMEOUT) {\n    throw std::logic_error(\"Thread baton can't have timeout status\");\n  }\n  if (waiter == THREAD_WAITING) {\n    throw std::logic_error(\"Other thread is already waiting on this baton\");\n  }\n  throw std::logic_error(\"Other waiter is already waiting on this baton\");\n}\n\nvoid Baton::post() {\n  postHelper(POSTED);\n}\n\nvoid Baton::postHelper(intptr_t new_value) {\n  auto waiter = waiter_.load();\n\n  do {\n    if (waiter == THREAD_WAITING) {\n      assert(new_value == POSTED);\n\n      return postThread();\n    }\n\n    if (waiter == POSTED) {\n      return;\n    }\n  } while (!waiter_.compare_exchange_weak(waiter, new_value));\n\n  if (waiter != NO_WAITER && waiter != TIMEOUT) {\n    reinterpret_cast<Waiter*>(waiter)->post();\n  }\n}\n\nbool Baton::try_wait() {\n  return ready();\n}\n\nvoid Baton::postThread() {\n  auto expected = THREAD_WAITING;\n\n  auto* futex = &futex_.futex;\n  if (!waiter_.compare_exchange_strong(expected, POSTED)) {\n    return;\n  }\n  futexWake(futex, 1);\n}\n\nvoid Baton::reset() {\n  waiter_.store(NO_WAITER, std::memory_order_relaxed);\n}\n\nvoid Baton::TimeoutHandler::scheduleTimeout(std::chrono::milliseconds timeout) {\n  assert(fiberManager_ != nullptr);\n  assert(timeoutFunc_ != nullptr);\n\n  if (timeout.count() > 0) {\n    fiberManager_->loopController_->timer()->scheduleTimeout(this, timeout);\n  }\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Baton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/Portability.h>\n#include <folly/coro/Coroutine.h>\n#include <folly/detail/Futex.h>\n#include <folly/io/async/HHWheelTimer.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass Fiber;\nclass FiberManager;\n\n/**\n * @class Baton\n *\n * Primitive which allows one to put current Fiber to sleep and wake it from\n * another Fiber/thread.\n */\nclass Baton {\n public:\n  class TimeoutHandler;\n\n  class Waiter {\n   public:\n    virtual void post() = 0;\n\n    virtual ~Waiter() {}\n  };\n\n  Baton() noexcept;\n\n  ~Baton() noexcept = default;\n\n  bool ready() const noexcept {\n    auto state = waiter_.load();\n    return state == POSTED;\n  }\n\n  /**\n   * Registers a waiter for the baton. The waiter will be notified when\n   * the baton is posted.\n   *\n   * Precondition: No waiter is currently registered. Throws logic_error.\n   */\n  void setWaiter(Waiter& waiter);\n\n  /**\n   * Puts active fiber to sleep. Returns when post is called.\n   */\n  void wait();\n\n  /**\n   * Put active fiber to sleep indefinitely. However, timeoutHandler may\n   * be used elsewhere on the same thread in order to schedule a wakeup\n   * for the active fiber.  Users of timeoutHandler must be on the same thread\n   * as the active fiber and may only schedule one timeout, which must occur\n   * after the active fiber calls wait.\n   */\n  void wait(TimeoutHandler& timeoutHandler);\n\n  /**\n   * Puts active fiber to sleep. Returns when post is called.\n   *\n   * @param mainContextFunc this function is immediately executed on the main\n   *        context.\n   */\n  template <typename F>\n  void wait(F&& mainContextFunc);\n\n  /**\n   * Checks if the baton has been posted without blocking.\n   *\n   * @return    true iff the baton has been posted.\n   */\n  bool try_wait();\n\n  /**\n   * Puts active fiber to sleep. Returns when post is called or the timeout\n   * expires.\n   *\n   * @param timeout Baton will be automatically awaken if timeout expires\n   *\n   * @return true if was posted, false if timeout expired\n   */\n  template <typename Rep, typename Period>\n  bool try_wait_for(const std::chrono::duration<Rep, Period>& timeout) {\n    return try_wait_for(timeout, [] {});\n  }\n\n  /**\n   * Puts active fiber to sleep. Returns when post is called or the timeout\n   * expires.\n   *\n   * @param timeout Baton will be automatically awaken if timeout expires\n   * @param mainContextFunc this function is immediately executed on the main\n   *        context.\n   *\n   * @return true if was posted, false if timeout expired\n   */\n  template <typename Rep, typename Period, typename F>\n  bool try_wait_for(\n      const std::chrono::duration<Rep, Period>& timeout, F&& mainContextFunc);\n\n  /**\n   * Puts active fiber to sleep. Returns when post is called or the deadline\n   * expires.\n   *\n   * @param deadline Baton will be automatically awaken if deadline expires\n   *\n   * @return true if was posted, false if timeout expired\n   */\n  template <typename Clock, typename Duration>\n  bool try_wait_until(\n      const std::chrono::time_point<Clock, Duration>& deadline) {\n    return try_wait_until(deadline, [] {});\n  }\n\n  /**\n   * Puts active fiber to sleep. Returns when post is called or the deadline\n   * expires.\n   *\n   * @param deadline Baton will be automatically awaken if deadline expires\n   * @param mainContextFunc this function is immediately executed on the main\n   *        context.\n   *\n   * @return true if was posted, false if timeout expired\n   */\n  template <typename Clock, typename Duration, typename F>\n  bool try_wait_until(\n      const std::chrono::time_point<Clock, Duration>& deadline,\n      F&& mainContextFunc);\n\n  /**\n   * Puts active fiber to sleep. Returns when post is called or the deadline\n   * expires.\n   *\n   * @param deadline Baton will be automatically awaken if deadline expires\n   * @param mainContextFunc this function is immediately executed on the main\n   *        context.\n   *\n   * @return true if was posted, false if timeout expired\n   */\n  template <typename Clock, typename Duration, typename F>\n  bool try_wait_for(\n      const std::chrono::time_point<Clock, Duration>& deadline,\n      F&& mainContextFunc);\n\n  /// Alias to try_wait_for. Deprecated.\n  template <typename Rep, typename Period>\n  bool timed_wait(const std::chrono::duration<Rep, Period>& timeout) {\n    return try_wait_for(timeout);\n  }\n\n  /// Alias to try_wait_for. Deprecated.\n  template <typename Rep, typename Period, typename F>\n  bool timed_wait(\n      const std::chrono::duration<Rep, Period>& timeout, F&& mainContextFunc) {\n    return try_wait_for(timeout, static_cast<F&&>(mainContextFunc));\n  }\n\n  /// Alias to try_wait_until. Deprecated.\n  template <typename Clock, typename Duration>\n  bool timed_wait(const std::chrono::time_point<Clock, Duration>& deadline) {\n    return try_wait_until(deadline);\n  }\n\n  /// Alias to try_wait_until. Deprecated.\n  template <typename Clock, typename Duration, typename F>\n  bool timed_wait(\n      const std::chrono::time_point<Clock, Duration>& deadline,\n      F&& mainContextFunc) {\n    return try_wait_until(deadline, static_cast<F&&>(mainContextFunc));\n  }\n\n  /**\n   * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,\n   * next wait() call will return immediately).\n   */\n  void post();\n\n  /**\n   * Reset's the baton (equivalent to destroying the object and constructing\n   * another one in place).\n   * Caller is responsible for making sure no one is waiting on/posting the\n   * baton when reset() is called.\n   */\n  void reset();\n\n  /**\n   * Provides a way to schedule a wakeup for a wait()ing fiber.\n   * A TimeoutHandler must be passed to Baton::wait(TimeoutHandler&)\n   * before a timeout is scheduled. It is only safe to use the\n   * TimeoutHandler on the same thread as the wait()ing fiber.\n   * scheduleTimeout() may only be called once prior to the end of the\n   * associated Baton's life.\n   */\n  class TimeoutHandler final : public HHWheelTimer::Callback {\n   public:\n    void scheduleTimeout(std::chrono::milliseconds timeout);\n\n   private:\n    friend class Baton;\n\n    std::function<void()> timeoutFunc_{nullptr};\n    FiberManager* fiberManager_{nullptr};\n\n    void timeoutExpired() noexcept override {\n      assert(timeoutFunc_ != nullptr);\n      timeoutFunc_();\n    }\n\n    void callbackCanceled() noexcept override {}\n  };\n\n private:\n  class FiberWaiter;\n\n  explicit Baton(intptr_t state) : waiter_(state) {}\n\n  void postHelper(intptr_t new_value);\n  void postThread();\n  void waitThread();\n\n  template <typename F>\n  inline void waitFiber(FiberManager& fm, F&& mainContextFunc);\n\n  template <typename Clock, typename Duration>\n  bool timedWaitThread(\n      const std::chrono::time_point<Clock, Duration>& deadline);\n\n  static constexpr intptr_t NO_WAITER = 0;\n  static constexpr intptr_t POSTED = -1;\n  static constexpr intptr_t TIMEOUT = -2;\n  static constexpr intptr_t THREAD_WAITING = -3;\n\n  struct _futex_wrapper {\n    folly::detail::Futex<> futex{};\n    int32_t _unused_packing;\n  };\n\n  union {\n    std::atomic<intptr_t> waiter_;\n    struct _futex_wrapper futex_;\n  };\n};\n\n#if FOLLY_HAS_COROUTINES\nnamespace detail {\n\n// `BatonAwaitableWaiter` intentionally does NOT support value-only await\n// protocols (`value_or_error`, `co_awaitTry`) and MUST NOT gain them.\n//\n// Value-only protocols require `await_ready` and `await_suspend` to be\n// noexcept, but this can't be arranged for Baton:\n//  - Making `await_suspend` noexcept is a breaking change -- existing code\n//    may rely on `Baton` throwing on double-wait misuse.\n//  - Value-only support could in theory be implemented via awaitable wrappers\n//    like `fatal_if_await_throws` or `unsafe_throwing_await` (ref D91285418).\n//    But, `Baton` is immovable (has `std::atomic`) and awaitable only by\n//    lvalue, so it's especially annoying to wrap.\n//\n// The best fix for value-only support -- have `Baton` emit an explicit\n// must-use-immediately awaitable instead of being awaited by lvalue. This\n// would also let us deprecate the current lvalue-await API, and once it's\n// gone, much `folly/coro` code simplifies (see `ViaIfAsync.h` for breadcrumbs\n// on lvalue-awaitable handling).\nclass BatonAwaitableWaiter : public Baton::Waiter {\n public:\n  explicit BatonAwaitableWaiter(Baton& baton) : baton_(baton) {}\n\n  void post() override {\n    assert(h_);\n    h_();\n  }\n\n  bool await_ready() const noexcept { return baton_.ready(); }\n\n  void await_resume() {}\n\n  // Precondition: No waiter may already be registered, or `setWaiter` will\n  // throw `logic_error`.  The class doc explains why throwing is a problem.\n  void await_suspend(coro::coroutine_handle<> h) {\n    assert(!h_);\n    h_ = std::move(h);\n    baton_.setWaiter(*this);\n  }\n\n private:\n  coro::coroutine_handle<> h_;\n  Baton& baton_;\n};\n} // namespace detail\n\ninline detail::BatonAwaitableWaiter /* implicit */ operator co_await(\n    Baton& baton) {\n  return detail::BatonAwaitableWaiter(baton);\n}\n#endif\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/Baton-inl.h>\n"
  },
  {
    "path": "folly/fibers/BoostContextCompatibility.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <boost/context/detail/fcontext.hpp>\n#include <glog/logging.h>\n\n/**\n * Wrappers for different versions of boost::context library\n * API reference for different versions\n * Boost 1.61:\n * https://github.com/boostorg/context/blob/boost-1.61.0/include/boost/context/detail/fcontext.hpp\n */\n\n#include <folly/Function.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass FiberImpl {\n  using FiberContext = boost::context::detail::fcontext_t;\n\n  using MainContext = boost::context::detail::fcontext_t;\n\n public:\n  FiberImpl(\n      folly::Function<void()> func, unsigned char* stackLimit, size_t stackSize)\n      : func_(std::move(func)) {\n    auto stackBase = stackLimit + stackSize;\n    stackBase_ = stackBase;\n    fiberContext_ =\n        boost::context::detail::make_fcontext(stackBase, stackSize, &fiberFunc);\n  }\n\n  void activate() {\n    auto transfer = boost::context::detail::jump_fcontext(fiberContext_, this);\n    fiberContext_ = transfer.fctx;\n    auto context = reinterpret_cast<intptr_t>(transfer.data);\n    DCHECK_EQ(0, context);\n  }\n\n  void deactivate() {\n    auto transfer =\n        boost::context::detail::jump_fcontext(mainContext_, nullptr);\n    mainContext_ = transfer.fctx;\n    fixStackUnwinding();\n    auto context = reinterpret_cast<intptr_t>(transfer.data);\n    DCHECK_EQ(this, reinterpret_cast<FiberImpl*>(context));\n  }\n\n  void* getStackPointer() const {\n    if constexpr (kIsLinux) {\n      if constexpr (kIsArchAmd64) {\n        // RBP at offset 0x30 (index 6) in the x86_64 fcontext layout.\n        return reinterpret_cast<void**>(fiberContext_)[6];\n      } else if constexpr (kIsArchAArch64) {\n        // FP (x29) at offset 0x90 (index 18) in the arm64 fcontext layout.\n        return reinterpret_cast<void**>(fiberContext_)[18];\n      }\n    }\n    return nullptr;\n  }\n\n private:\n  static void fiberFunc(boost::context::detail::transfer_t transfer) {\n    auto fiberImpl = reinterpret_cast<FiberImpl*>(transfer.data);\n    fiberImpl->mainContext_ = transfer.fctx;\n    fiberImpl->fixStackUnwinding();\n    fiberImpl->func_();\n  }\n\n  void fixStackUnwinding() {\n    if (kIsLinux) {\n      // Stitch main context stack and fiber stack so that frame-pointer-based\n      // stack walkers (e.g. jemalloc prof_backtrace_impl) can terminate\n      // cleanly at the fiber boundary.\n      auto stackBase = reinterpret_cast<void**>(stackBase_);\n      auto mainContext = reinterpret_cast<void**>(mainContext_);\n      if constexpr (kIsArchAmd64) {\n        // Extract RBP and RIP from main context (offsets 6 and 7).\n        stackBase[-2] = mainContext[6];\n        stackBase[-1] = mainContext[7];\n      } else if constexpr (kIsArchAArch64) {\n        // Extract FP (x29) and LR (x30) from main context\n        // (offsets 0x90 and 0x98 in the fcontext layout).\n        stackBase[-2] = mainContext[18];\n        stackBase[-1] = mainContext[19];\n      }\n    }\n  }\n\n  unsigned char* stackBase_;\n  folly::Function<void()> func_;\n  FiberContext fiberContext_;\n  MainContext mainContext_;\n};\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME add_tasks\n  HEADERS\n    AddTasks-inl.h\n    AddTasks.h\n  EXPORTED_DEPS\n    folly_fibers_core\n    folly_optional\n    folly_try\n)\n\nfolly_add_library(\n  NAME atomic_batch_dispatcher\n  HEADERS\n    AtomicBatchDispatcher-inl.h\n    AtomicBatchDispatcher.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_fibers_detail_atomic_batch_dispatcher\n    folly_function\n    folly_futures_core\n    folly_optional\n)\n\nfolly_add_library(\n  NAME batch_dispatcher\n  HEADERS\n    BatchDispatcher.h\n  EXPORTED_DEPS\n    folly_function\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME batch_semaphore\n  SRCS\n    BatchSemaphore.cpp\n  HEADERS\n    BatchSemaphore.h\n  EXPORTED_DEPS\n    folly_fibers_semaphore_base\n)\n\nfolly_add_library(\n  NAME boost_context_compatibility\n  HEADERS\n    BoostContextCompatibility.h\n  EXPORTED_DEPS\n    folly_function\n  EXTERNAL_DEPS\n    Boost::context\n)\n\nfolly_add_library(\n  NAME core\n  SRCS\n    Baton.cpp\n    Fiber.cpp\n    FiberManager.cpp\n  HEADERS\n    Baton-inl.h\n    Baton.h\n    Fiber-inl.h\n    Fiber.h\n    FiberManagerInternal-inl.h\n    FiberManagerInternal.h\n    Promise-inl.h\n    Promise.h\n  DEPS\n    folly_constexpr_math\n    folly_detail_memory_idler\n    folly_memory_sanitize_address\n    folly_portability_asm\n    folly_portability_config\n    folly_portability_sys_syscall\n    folly_portability_unistd\n    folly_singleton_thread_local\n    folly_synchronization_sanitize_thread\n  EXPORTED_DEPS\n    folly_atomic_linked_list\n    folly_c_portability\n    folly_coro_coroutine\n    folly_detail_async_trace\n    folly_detail_futex\n    folly_executor\n    folly_executors_execution_observer\n    folly_fibers_boost_context_compatibility\n    folly_fibers_guard_page_allocator\n    folly_fibers_loop_controller\n    folly_fibers_traits\n    folly_function\n    folly_functional_invoke\n    folly_intrusive_list\n    folly_io_async_async_base\n    folly_io_async_request_context\n    folly_lang_thunk\n    folly_likely\n    folly_memory\n    folly_optional\n    folly_portability\n    folly_portability_pthread\n    folly_scope_guard\n    folly_tracing_async_stack\n    folly_try\n)\n\nfolly_add_library(\n  NAME core_manager\n  HEADERS\n    FiberManager-inl.h\n    FiberManager.h\n  EXPORTED_DEPS\n    folly_fibers_core\n    folly_functional_invoke\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME event_base_loop_controller\n  HEADERS\n    EventBaseLoopController-inl.h\n    EventBaseLoopController.h\n  EXPORTED_DEPS\n    folly_cancellation_token\n    folly_fibers_core\n    folly_fibers_executor_based_loop_controller\n    folly_io_async_async_base\n    folly_memory\n)\n\nfolly_add_library(\n  NAME executor_based_loop_controller\n  HEADERS\n    ExecutorBasedLoopController.h\n  EXPORTED_DEPS\n    folly_executor\n    folly_fibers_loop_controller\n)\n\nfolly_add_library(\n  NAME executor_loop_controller\n  HEADERS\n    ExecutorLoopController-inl.h\n    ExecutorLoopController.h\n  EXPORTED_DEPS\n    folly_executor\n    folly_fibers_core\n    folly_fibers_executor_based_loop_controller\n    folly_futures_core\n    folly_scope_guard\n)\n\nfolly_add_library(\n  NAME fiber_manager_map\n  HEADERS\n    FiberManagerMap-inl.h\n    FiberManagerMap.h\n  EXPORTED_DEPS\n    folly_container_f14_hash\n    folly_fibers_core\n    folly_fibers_event_base_loop_controller\n    folly_function\n    folly_io_async_async_base\n    folly_scope_guard\n    folly_singleton_thread_local\n    folly_synchronization_relaxed_atomic\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME fibers\n  EXPORTED_DEPS\n    folly_fibers_add_tasks\n    folly_fibers_atomic_batch_dispatcher\n    folly_fibers_batch_dispatcher\n    folly_fibers_batch_semaphore\n    folly_fibers_boost_context_compatibility\n    folly_fibers_core\n    folly_fibers_core_manager\n    folly_fibers_event_base_loop_controller\n    folly_fibers_fiber_manager_map\n    folly_fibers_for_each\n    folly_fibers_generic_baton\n    folly_fibers_guard_page_allocator\n    folly_fibers_loop_controller\n    folly_fibers_semaphore\n    folly_fibers_semaphore_base\n    folly_fibers_simple_loop_controller\n    folly_fibers_timed_mutex\n    folly_fibers_traits\n    folly_fibers_when_n\n)\n\nfolly_add_library(\n  NAME for_each\n  HEADERS\n    ForEach-inl.h\n    ForEach.h\n  EXPORTED_DEPS\n    folly_fibers_core\n    folly_functional_invoke\n)\n\nfolly_add_library(\n  NAME generic_baton\n  HEADERS\n    GenericBaton.h\n  EXPORTED_DEPS\n    folly_fibers_core\n    folly_synchronization_baton\n)\n\nfolly_add_library(\n  NAME guard_page_allocator\n  SRCS\n    GuardPageAllocator.cpp\n  HEADERS\n    GuardPageAllocator.h\n  DEPS\n    folly_portability_sys_mman\n    folly_portability_unistd\n    folly_singleton\n    folly_spin_lock\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME loop_controller\n  HEADERS\n    LoopController.h\n  EXPORTED_DEPS\n    folly_io_async_async_base_fwd\n)\n\nfolly_add_library(\n  NAME semaphore\n  SRCS\n    Semaphore.cpp\n  HEADERS\n    Semaphore.h\n  EXPORTED_DEPS\n    folly_coro_task\n    folly_coro_timeout\n    folly_fibers_core\n    folly_futures_core\n    folly_intrusive_list\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME semaphore_base\n  SRCS\n    SemaphoreBase.cpp\n  HEADERS\n    SemaphoreBase.h\n  EXPORTED_DEPS\n    folly_coro_task\n    folly_fibers_core\n    folly_futures_core\n    folly_intrusive_list\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME simple_loop_controller\n  SRCS\n    SimpleLoopController.cpp\n  HEADERS\n    SimpleLoopController.h\n  DEPS\n    folly_io_async_async_base\n  EXPORTED_DEPS\n    folly_fibers_core_manager\n    folly_fibers_loop_controller\n    folly_function\n    folly_likely\n)\n\nfolly_add_library(\n  NAME timed_mutex\n  HEADERS\n    CallOnce.h\n    TimedMutex-inl.h\n    TimedMutex.h\n  EXPORTED_DEPS\n    folly_fibers_generic_baton\n    folly_intrusive_list\n    folly_portability\n    folly_spin_lock\n    folly_synchronization_call_once\n    folly_synchronization_detail_sleeper\n)\n\nfolly_add_library(\n  NAME traits\n  HEADERS\n    traits.h\n)\n\nfolly_add_library(\n  NAME when_n\n  HEADERS\n    WhenN-inl.h\n    WhenN.h\n  EXPORTED_DEPS\n    folly_fibers_core\n    folly_fibers_for_each\n    folly_functional_invoke\n    folly_optional\n)\n\nadd_subdirectory(async)\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/fibers/CallOnce.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <folly/fibers/TimedMutex.h>\n#include <folly/synchronization/CallOnce.h>\n\nnamespace folly {\nnamespace fibers {\n\nusing once_flag = basic_once_flag<TimedMutex>;\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/EventBaseLoopController-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Memory.h>\n\nnamespace folly {\nnamespace fibers {\n\ninline EventBaseLoopController::EventBaseLoopController() : callback_(*this) {}\n\ninline EventBaseLoopController::~EventBaseLoopController() {\n  callback_.cancelLoopCallback();\n  eventBaseKeepAlive_.reset();\n}\n\ninline void EventBaseLoopController::attachEventBase(EventBase& eventBase) {\n  attachEventBase(eventBase.getVirtualEventBase());\n}\n\ninline void EventBaseLoopController::attachEventBase(\n    VirtualEventBase& eventBase) {\n  if (eventBaseAttached_.exchange(true)) {\n    LOG(ERROR) << \"Attempt to reattach EventBase to LoopController\";\n    return;\n  }\n\n  eventBase_ = &eventBase;\n\n  CancellationSource source;\n  eventBaseShutdownToken_ = source.getToken();\n  eventBase_->runOnDestruction([source = std::move(source)] {\n    source.requestCancellation();\n  });\n\n  if (awaitingScheduling_) {\n    schedule();\n  }\n}\n\ninline void EventBaseLoopController::setFiberManager(FiberManager* fm) {\n  fm_ = fm;\n}\n\ninline void EventBaseLoopController::schedule() {\n  if (eventBase_ == nullptr) {\n    // In this case we need to postpone scheduling.\n    awaitingScheduling_ = true;\n  } else {\n    // Schedule it to run in current iteration.\n\n    if (!eventBaseKeepAlive_) {\n      eventBaseKeepAlive_ = getKeepAliveToken(eventBase_);\n    }\n    eventBase_->getEventBase().runInLoop(&callback_, true);\n    awaitingScheduling_ = false;\n  }\n}\n\ninline void EventBaseLoopController::runLoop() {\n  if (!eventBaseKeepAlive_) {\n    // runLoop can be called twice if both schedule() and scheduleThreadSafe()\n    // were called.\n    if (!fm_->hasTasks()) {\n      return;\n    }\n    eventBaseKeepAlive_ = getKeepAliveToken(eventBase_);\n  }\n  if (loopRunner_) {\n    if (fm_->hasReadyTasks()) {\n      loopRunner_->run([&] { fm_->loopUntilNoReadyImpl(); });\n    }\n  } else {\n    fm_->loopUntilNoReadyImpl();\n  }\n  if (!fm_->hasTasks()) {\n    eventBaseKeepAlive_.reset();\n  }\n}\n\ninline void EventBaseLoopController::runEagerFiber(Fiber* fiber) {\n  if (!eventBaseKeepAlive_) {\n    eventBaseKeepAlive_ = getKeepAliveToken(eventBase_);\n  }\n  if (loopRunner_) {\n    loopRunner_->run([&] { fm_->runEagerFiberImpl(fiber); });\n  } else {\n    fm_->runEagerFiberImpl(fiber);\n  }\n  if (!fm_->hasTasks()) {\n    eventBaseKeepAlive_.reset();\n  }\n}\n\ninline void EventBaseLoopController::scheduleThreadSafe() {\n  /* The only way we could end up here is if\n     1) Fiber thread creates a fiber that awaits (which means we must\n        have already attached, fiber thread wouldn't be running).\n     2) We move the promise to another thread (this move is a memory fence)\n     3) We fulfill the promise from the other thread. */\n  assert(eventBaseAttached_);\n\n  eventBase_->runInEventBaseThread(\n      [this, eventBaseKeepAlive = getKeepAliveToken(eventBase_)]() {\n        if (fm_->shouldRunLoopRemote()) {\n          return runLoop();\n        }\n\n        if (!fm_->hasTasks()) {\n          eventBaseKeepAlive_.reset();\n        }\n      });\n}\n\ninline HHWheelTimer* EventBaseLoopController::timer() {\n  assert(eventBaseAttached_);\n\n  if (FOLLY_UNLIKELY(eventBaseShutdownToken_.isCancellationRequested())) {\n    return nullptr;\n  }\n\n  return &eventBase_->timer();\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/EventBaseLoopController.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <memory>\n\n#include <folly/CancellationToken.h>\n#include <folly/fibers/ExecutorBasedLoopController.h>\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/io/async/VirtualEventBase.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass EventBaseLoopController : public ExecutorBasedLoopController {\n public:\n  explicit EventBaseLoopController();\n  ~EventBaseLoopController() override;\n\n  /**\n   * Attach EventBase after LoopController was created.\n   */\n  void attachEventBase(EventBase& eventBase);\n  void attachEventBase(VirtualEventBase& eventBase);\n\n  VirtualEventBase* getEventBase() { return eventBase_; }\n\n  void setLoopRunner(InlineFunctionRunner* loopRunner) {\n    loopRunner_ = loopRunner;\n  }\n\n  folly::Executor* executor() const override { return eventBase_; }\n\n  bool isInLoopThread() override {\n    return eventBase_->getEventBase().inRunningEventBaseThread();\n  }\n\n private:\n  class ControllerCallback : public folly::EventBase::LoopCallback {\n   public:\n    explicit ControllerCallback(EventBaseLoopController& controller)\n        : controller_(controller) {}\n\n    void runLoopCallback() noexcept override { controller_.runLoop(); }\n\n   private:\n    EventBaseLoopController& controller_;\n  };\n\n  folly::CancellationToken eventBaseShutdownToken_;\n\n  VirtualEventBase* eventBase_{nullptr};\n  Executor::KeepAlive<VirtualEventBase> eventBaseKeepAlive_;\n  ControllerCallback callback_;\n  FiberManager* fm_{nullptr};\n  InlineFunctionRunner* loopRunner_{nullptr};\n  std::atomic<bool> eventBaseAttached_{false};\n  bool awaitingScheduling_{false};\n\n  /* LoopController interface */\n\n  void setFiberManager(FiberManager* fm) override;\n  void schedule() override;\n  void runLoop() override;\n  void runEagerFiber(Fiber*) override;\n  void scheduleThreadSafe() override;\n  HHWheelTimer* timer() override;\n\n  friend class FiberManager;\n};\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/EventBaseLoopController-inl.h>\n"
  },
  {
    "path": "folly/fibers/ExecutorBasedLoopController.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Executor.h>\n#include <folly/fibers/LoopController.h>\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * Interface for LoopController with publicly acessible executor\n */\nclass ExecutorBasedLoopController : public fibers::LoopController {\n public:\n  virtual folly::Executor* executor() const = 0;\n};\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/ExecutorLoopController-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <thread>\n#include <folly/ScopeGuard.h>\n#include <folly/futures/Future.h>\n\nnamespace folly {\nnamespace fibers {\n\ninline ExecutorLoopController::ExecutorLoopController(folly::Executor* executor)\n    : executor_(executor),\n      timeoutManager_(executor_),\n      timer_(HHWheelTimer::newTimer(&timeoutManager_)) {}\n\ninline ExecutorLoopController::~ExecutorLoopController() {}\n\ninline void ExecutorLoopController::setFiberManager(fibers::FiberManager* fm) {\n  fm_ = fm;\n}\n\ninline void ExecutorLoopController::schedule() {\n  // add() is thread-safe, so this isn't properly optimized for addTask()\n  if (!executorKeepAlive_) {\n    executorKeepAlive_ = getKeepAliveToken(executor_);\n  }\n  auto guard = localCallbackControlBlock_->trySchedule();\n  if (!guard) {\n    return;\n  }\n  executor_->add([this, guard = std::move(guard)]() {\n    if (guard->isCancelled()) {\n      return;\n    }\n    runLoop();\n  });\n}\n\ninline void ExecutorLoopController::runLoop() {\n  auto oldLoopThread = loopThread_.exchange(std::this_thread::get_id());\n  DCHECK(oldLoopThread == std::thread::id{});\n  SCOPE_EXIT {\n    loopThread_ = std::thread::id{};\n  };\n\n  if (!executorKeepAlive_) {\n    if (!fm_->hasTasks()) {\n      return;\n    }\n    executorKeepAlive_ = getKeepAliveToken(executor_);\n  }\n  fm_->loopUntilNoReadyImpl();\n  if (!fm_->hasTasks()) {\n    executorKeepAlive_.reset();\n  }\n}\n\ninline void ExecutorLoopController::runEagerFiber(Fiber* fiber) {\n  DCHECK(loopThread_ == std::this_thread::get_id());\n  if (!executorKeepAlive_) {\n    executorKeepAlive_ = getKeepAliveToken(executor_);\n  }\n  fm_->runEagerFiberImpl(fiber);\n  if (!fm_->hasTasks()) {\n    executorKeepAlive_.reset();\n  }\n}\n\ninline void ExecutorLoopController::scheduleThreadSafe() {\n  executor_->add(\n      [this, executorKeepAlive = getKeepAliveToken(executor_)]() mutable {\n        if (fm_->shouldRunLoopRemote()) {\n          return runLoop();\n        }\n      });\n}\n\ninline HHWheelTimer* ExecutorLoopController::timer() {\n  return timer_.get();\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/ExecutorLoopController.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/Executor.h>\n#include <folly/fibers/ExecutorBasedLoopController.h>\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/futures/Future.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass ExecutorTimeoutManager : public TimeoutManager {\n public:\n  explicit ExecutorTimeoutManager(folly::Executor* executor)\n      : executor_(executor) {}\n\n  ExecutorTimeoutManager(ExecutorTimeoutManager&&) = default;\n  ExecutorTimeoutManager& operator=(ExecutorTimeoutManager&&) = default;\n  ExecutorTimeoutManager(const ExecutorTimeoutManager&) = delete;\n  ExecutorTimeoutManager& operator=(const ExecutorTimeoutManager&) = delete;\n\n  void attachTimeoutManager(\n      AsyncTimeout* /* unused */, InternalEnum /* unused */) final {}\n\n  void detachTimeoutManager(AsyncTimeout* /* unused */) final {\n    throw std::logic_error(\n        \"detachTimeoutManager() call isn't supported by ExecutorTimeoutManager.\");\n  }\n\n  bool scheduleTimeout(AsyncTimeout* obj, timeout_type timeout) final {\n    folly::futures::sleep(timeout).via(executor_).thenValue([obj](folly::Unit) {\n      obj->timeoutExpired();\n    });\n    return true;\n  }\n\n  void cancelTimeout(AsyncTimeout* /* unused */) final {\n    throw std::logic_error(\n        \"cancelTimeout() call isn't supported by ExecutorTimeoutManager.\");\n  }\n\n  void bumpHandlingTime() final {\n    throw std::logic_error(\n        \"bumpHandlingTime() call isn't supported by ExecutorTimeoutManager.\");\n  }\n\n  bool isInTimeoutManagerThread() final {\n    throw std::logic_error(\n        \"isInTimeoutManagerThread() call isn't supported by ExecutorTimeoutManager.\");\n  }\n\n private:\n  folly::Executor* executor_;\n};\n\n/**\n * A fiber loop controller that works for arbitrary folly::Executor\n */\nclass ExecutorLoopController : public fibers::ExecutorBasedLoopController {\n public:\n  explicit ExecutorLoopController(folly::Executor* executor);\n  ~ExecutorLoopController() override;\n\n  folly::Executor* executor() const override { return executor_; }\n\n private:\n  folly::Executor* executor_;\n  Executor::KeepAlive<> executorKeepAlive_;\n  fibers::FiberManager* fm_{nullptr};\n  ExecutorTimeoutManager timeoutManager_;\n  HHWheelTimer::UniquePtr timer_;\n  std::atomic<std::thread::id> loopThread_;\n\n  class LocalCallbackControlBlock {\n   public:\n    struct DeleteOrCancel {\n      void operator()(LocalCallbackControlBlock* controlBlock) {\n        if (controlBlock->scheduled_) {\n          controlBlock->cancelled_ = true;\n        } else {\n          delete controlBlock;\n        }\n      }\n    };\n    using Ptr = std::unique_ptr<LocalCallbackControlBlock, DeleteOrCancel>;\n\n    struct GuardDeleter {\n      void operator()(LocalCallbackControlBlock* controlBlock) {\n        DCHECK(controlBlock->scheduled_);\n        controlBlock->scheduled_ = false;\n        if (controlBlock->cancelled_) {\n          delete controlBlock;\n        }\n      }\n    };\n    using Guard = std::unique_ptr<LocalCallbackControlBlock, GuardDeleter>;\n\n    static Ptr create() { return Ptr(new LocalCallbackControlBlock()); }\n\n    Guard trySchedule() {\n      if (scheduled_) {\n        return {};\n      }\n      scheduled_ = true;\n      return Guard(this);\n    }\n\n    bool isCancelled() const { return cancelled_; }\n\n   private:\n    LocalCallbackControlBlock() {}\n\n    bool cancelled_{false};\n    bool scheduled_{false};\n  };\n  LocalCallbackControlBlock::Ptr localCallbackControlBlock_{\n      LocalCallbackControlBlock::create()};\n\n  void setFiberManager(fibers::FiberManager* fm) override;\n  void schedule() override;\n  void runLoop() override;\n  void runEagerFiber(Fiber*) override;\n  void scheduleThreadSafe() override;\n  HHWheelTimer* timer() override;\n  bool isInLoopThread() override {\n    return loopThread_ == std::this_thread::get_id();\n  }\n\n  friend class fibers::FiberManager;\n};\n\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/ExecutorLoopController-inl.h>\n"
  },
  {
    "path": "folly/fibers/Fiber-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n\nnamespace folly {\nnamespace fibers {\nnamespace thread_clock {\ninline std::chrono::steady_clock::time_point now() {\n#ifdef __linux__\n  using std::chrono::nanoseconds;\n  using std::chrono::seconds;\n  timespec tp;\n  clockid_t clockid;\n  if (!pthread_getcpuclockid(pthread_self(), &clockid) &&\n      !clock_gettime(clockid, &tp)) {\n    return std::chrono::steady_clock::time_point(\n        nanoseconds(tp.tv_nsec) + seconds(tp.tv_sec));\n  }\n#endif\n  return std::chrono::steady_clock::now();\n}\n} // namespace thread_clock\n\ntemplate <typename F>\nvoid Fiber::setFunction(F&& func, TaskOptions taskOptions) {\n  assert(state_ == INVALID);\n  func_ = std::forward<F>(func);\n  state_ = NOT_STARTED;\n  taskOptions_ = std::move(taskOptions);\n}\n\ntemplate <typename F, typename G>\nvoid Fiber::setFunctionFinally(F&& resultFunc, G&& finallyFunc) {\n  assert(state_ == INVALID);\n  resultFunc_ = std::forward<F>(resultFunc);\n  finallyFunc_ = std::forward<G>(finallyFunc);\n  state_ = NOT_STARTED;\n  taskOptions_ = TaskOptions();\n}\n\ninline void* Fiber::getUserBuffer() {\n  return &userBuffer_;\n}\n\ntemplate <typename T>\nT& Fiber::LocalData::getSlow() {\n  vtable_ = VTable::get<T>();\n  T* data = nullptr;\n  if constexpr (sizeof(T) <= sizeof(Buffer) && alignof(T) <= alignof(Buffer)) {\n    data = new (&buffer_) T();\n  } else {\n    data = new T();\n  }\n  data_ = data;\n  return *data;\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Fiber.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/Fiber.h>\n\n#include <algorithm>\n#include <cstring>\n\n#include <glog/logging.h>\n\n#include <folly/Likely.h>\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/portability/SysSyscall.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\nnamespace fibers {\n\nnamespace {\nconst uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;\n\n/* Size of the region from p + nBytes down to the last non-magic value */\nsize_t nonMagicInBytes(unsigned char* stackLimit, size_t stackSize) {\n  CHECK_EQ(reinterpret_cast<intptr_t>(stackLimit) % sizeof(uint64_t), 0u);\n  CHECK_EQ(stackSize % sizeof(uint64_t), 0u);\n  auto begin = reinterpret_cast<uint64_t*>(stackLimit);\n  auto end = reinterpret_cast<uint64_t*>(stackLimit + stackSize);\n\n  auto firstNonMagic = std::find_if(begin, end, [](uint64_t val) {\n    return val != kMagic8Bytes;\n  });\n\n  return (end - firstNonMagic) * sizeof(uint64_t);\n}\n\n} // namespace\n\nvoid Fiber::resume() {\n  DCHECK_EQ(state_, AWAITING);\n  state_ = READY_TO_RUN;\n\n  if (LIKELY(fiberManager_.loopController_->isInLoopThread())) {\n    fiberManager_.readyFibers_.push_back(*this);\n    fiberManager_.ensureLoopScheduled();\n  } else {\n    fiberManager_.remoteReadyInsert(this);\n  }\n}\n\nFiber::Fiber(FiberManager& fiberManager)\n    : fiberManager_(fiberManager),\n      fiberStackSize_(fiberManager_.options_.stackSize),\n      fiberStackHighWatermark_(0),\n      fiberStackLimit_(fiberManager_.stackAllocator_.allocate(fiberStackSize_)),\n      fiberImpl_([this] { fiberFunc(); }, fiberStackLimit_, fiberStackSize_) {\n  fiberManager_.allFibers_.push_back(*this);\n\n#ifdef FOLLY_SANITIZE_THREAD\n  tsanCtx_ = __tsan_create_fiber(0);\n#endif\n}\n\nvoid Fiber::init(bool recordStackUsed) {\n// It is necessary to disable the logic for ASAN because we change\n// the fiber's stack.\n#ifndef FOLLY_SANITIZE_ADDRESS\n  recordStackUsed_ = recordStackUsed;\n  if (FOLLY_UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) {\n    CHECK_EQ(\n        reinterpret_cast<intptr_t>(fiberStackLimit_) % sizeof(uint64_t), 0u);\n    CHECK_EQ(fiberStackSize_ % sizeof(uint64_t), 0u);\n    std::fill(\n        reinterpret_cast<uint64_t*>(fiberStackLimit_),\n        reinterpret_cast<uint64_t*>(fiberStackLimit_ + fiberStackSize_),\n        kMagic8Bytes);\n\n    stackFilledWithMagic_ = true;\n\n    // newer versions of boost allocate context on fiber stack,\n    // need to create a new one\n    fiberImpl_ =\n        FiberImpl([this] { fiberFunc(); }, fiberStackLimit_, fiberStackSize_);\n  }\n#else\n  (void)recordStackUsed;\n#endif\n}\n\nFiber::~Fiber() {\n#ifdef FOLLY_SANITIZE_ADDRESS\n  if (asanFakeStack_ != nullptr) {\n    fiberManager_.freeFakeStack(asanFakeStack_);\n  }\n  fiberManager_.unpoisonFiberStack(this);\n#endif\n\n#ifdef FOLLY_SANITIZE_THREAD\n  __tsan_destroy_fiber(tsanCtx_);\n#endif\n\n  fiberManager_.stackAllocator_.deallocate(fiberStackLimit_, fiberStackSize_);\n}\n\nvoid Fiber::recordStackPosition() {\n  // For ASAN builds, functions may run on fake stack.\n  // So we cannot get meaningful stack position.\n#ifndef FOLLY_SANITIZE_ADDRESS\n  int stackDummy;\n  auto currentPosition = static_cast<size_t>(\n      fiberStackLimit_ + fiberStackSize_ -\n      static_cast<unsigned char*>(static_cast<void*>(&stackDummy)));\n  fiberStackHighWatermark_ =\n      std::max(fiberStackHighWatermark_, currentPosition);\n  fiberManager_.recordStackPosition(currentPosition);\n  VLOG(4) << \"Stack usage: \" << currentPosition;\n#endif\n}\n\n[[noreturn]] void Fiber::fiberFunc() {\n#ifdef FOLLY_SANITIZE_ADDRESS\n  fiberManager_.registerFinishSwitchStackWithAsan(\n      nullptr, &asanMainStackBase_, &asanMainStackSize_);\n#endif\n\n  while (true) {\n    DCHECK_EQ(state_, NOT_STARTED);\n\n    if (taskOptions_.logRunningTime) {\n      prevDuration_ = std::chrono::microseconds(0);\n      currStartTime_ = thread_clock::now();\n    }\n    state_ = RUNNING;\n\n    try {\n      if (resultFunc_) {\n        DCHECK(finallyFunc_);\n        DCHECK(!func_);\n\n        resultFunc_();\n      } else {\n        DCHECK(func_);\n        func_();\n      }\n    } catch (...) {\n      fiberManager_.exceptionCallback_(\n          current_exception(), \"running Fiber func_/resultFunc_\");\n    }\n\n    if (FOLLY_UNLIKELY(recordStackUsed_)) {\n      auto currentPosition = nonMagicInBytes(fiberStackLimit_, fiberStackSize_);\n      fiberStackHighWatermark_ =\n          std::max(fiberStackHighWatermark_, currentPosition);\n      auto newHighWatermark =\n          fiberManager_.recordStackPosition(currentPosition);\n      VLOG(3) << \"Max stack usage: \" << newHighWatermark;\n      CHECK_LT(newHighWatermark, fiberManager_.options_.stackSize - 64)\n          << \"Fiber stack overflow\";\n    }\n\n    state_ = INVALID;\n\n    fiberManager_.deactivateFiber(this);\n  }\n}\n\nvoid Fiber::preempt(State state) {\n  auto preemptImpl = [&]() mutable {\n    DCHECK_EQ(fiberManager_.activeFiber_, this);\n    DCHECK_EQ(state_, RUNNING);\n    DCHECK_NE(state, RUNNING);\n    if (state != AWAITING_IMMEDIATE) {\n      CHECK(fiberManager_.currentException_ == current_exception());\n      CHECK_EQ(fiberManager_.numUncaughtExceptions_, uncaught_exceptions());\n    }\n\n    if (taskOptions_.logRunningTime) {\n      auto now = thread_clock::now();\n      prevDuration_ += now - currStartTime_;\n      currStartTime_ = now;\n    }\n    state_ = state;\n\n    recordStackPosition();\n\n    fiberManager_.deactivateFiber(this);\n\n    // Resumed from preemption\n    DCHECK_EQ(fiberManager_.activeFiber_, this);\n    DCHECK_EQ(state_, READY_TO_RUN);\n    if (taskOptions_.logRunningTime) {\n      currStartTime_ = thread_clock::now();\n    }\n    state_ = RUNNING;\n  };\n\n  if (fiberManager_.preemptRunner_) {\n    fiberManager_.preemptRunner_->run(std::ref(preemptImpl));\n  } else {\n    preemptImpl();\n  }\n}\n\nfolly::Optional<std::chrono::nanoseconds> Fiber::getRunningTime() const {\n  if (taskOptions_.logRunningTime) {\n    auto elapsed = prevDuration_;\n    if (state_ == Fiber::RUNNING &&\n        fiberManager_.loopController_->isInLoopThread()) {\n      elapsed += thread_clock::now() - currStartTime_;\n    }\n    return elapsed;\n  }\n  return folly::none;\n}\n\nFiber::LocalData::~LocalData() {\n  reset();\n}\n\nFiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {\n  *this = other;\n}\n\nFiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {\n  reset();\n  if (!other.data_) {\n    return *this;\n  }\n\n  vtable_ = other.vtable_;\n  if (other.data_ == &other.buffer_) {\n    data_ = vtable_.ctor_copy(&buffer_, other.data_);\n  } else {\n    data_ = vtable_.make_copy(other.data_);\n  }\n\n  return *this;\n}\n\nvoid Fiber::LocalData::reset() {\n  if (!data_) {\n    return;\n  }\n\n  if (data_ == &buffer_) {\n    vtable_.dtor(data_);\n  } else {\n    vtable_.ruin(data_);\n  }\n  vtable_ = {};\n  data_ = nullptr;\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Fiber.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <thread>\n#include <typeinfo>\n\n#include <folly/AtomicIntrusiveLinkedList.h>\n#include <folly/CPortability.h>\n#include <folly/Function.h>\n#include <folly/IntrusiveList.h>\n#include <folly/Portability.h>\n#include <folly/fibers/BoostContextCompatibility.h>\n#include <folly/io/async/Request.h>\n#include <folly/lang/Thunk.h>\n#include <folly/portability/PThread.h>\n\n// include after CPortability.h defines this\n#ifdef FOLLY_SANITIZE_THREAD\n#include <sanitizer/tsan_interface.h>\n#endif\n\nnamespace folly {\nstruct AsyncStackRoot;\n\nnamespace fibers {\n\nclass Baton;\nclass FiberManager;\n\nstruct TaskOptions {\n  TaskOptions() {}\n  /**\n   * Should log the running time of the task? Refer to\n   * getCurrentTaskRunningTime() for details.\n   */\n  bool logRunningTime = false;\n};\n\n/**\n * @class Fiber\n * @brief Fiber object used by FiberManager to execute tasks.\n *\n * Each Fiber object can be executing at most one task at a time. In active\n * phase it is running the task function and keeps its context.\n * Fiber is also used to pass data to blocked task and thus unblock it.\n * Each Fiber may be associated with a single FiberManager.\n */\nclass Fiber {\n public:\n  /**\n   * Resume the blocked task\n   */\n  void resume();\n\n  Fiber(const Fiber&) = delete;\n  Fiber& operator=(const Fiber&) = delete;\n\n  ~Fiber();\n\n  /**\n   * Retrieve this fiber's base stack and stack size.\n   *\n   * @return This fiber's base stack pointer and stack size.\n   */\n  std::pair<void*, size_t> getStack() const {\n    return {fiberStackLimit_, fiberStackSize_};\n  }\n\n  size_t stackHighWatermark() const { return fiberStackHighWatermark_; }\n\n  folly::Optional<std::chrono::nanoseconds> getRunningTime() const;\n\n  enum State : char {\n    INVALID, /**< Does't have task function */\n    NOT_STARTED, /**< Has task function, not started */\n    READY_TO_RUN, /**< Was started, blocked, then unblocked */\n    RUNNING, /**< Is running right now */\n    AWAITING, /**< Is currently blocked */\n    AWAITING_IMMEDIATE, /**< Was preempted to run an immediate function,\n                             and will be resumed right away */\n    YIELDED, /**< The fiber yielded execution voluntarily */\n  };\n\n  State getState() const { return state_; }\n\n  /**\n   * Retrieve this fiber's stack pointer. This is only meant for debugging.\n   *\n   * @return This fiber's current stack pointer.\n   */\n  void* getStackPointer() const { return fiberImpl_.getStackPointer(); }\n\n private:\n  State state_{INVALID}; /**< current Fiber state */\n\n  friend class Baton;\n  friend class FiberManager;\n\n  explicit Fiber(FiberManager& fiberManager);\n\n  void init(bool recordStackUsed);\n\n  template <typename F>\n  void setFunction(F&& func, TaskOptions taskOptions);\n\n  template <typename F, typename G>\n  void setFunctionFinally(F&& func, G&& finally);\n\n  [[noreturn]] void fiberFunc();\n\n  /**\n   * Switch out of fiber context into the main context,\n   * performing necessary housekeeping for the new state.\n   *\n   * @param state New state, must not be RUNNING.\n   */\n  void preempt(State state);\n\n  /**\n   * Examines how much of the stack we used at this moment and\n   * registers with the FiberManager (for monitoring).\n   */\n  void recordStackPosition();\n\n  TaskOptions taskOptions_;\n  bool recordStackUsed_{false};\n  bool stackFilledWithMagic_{false};\n  FiberManager& fiberManager_; /**< Associated FiberManager */\n  size_t fiberStackSize_;\n  size_t fiberStackHighWatermark_;\n  unsigned char* fiberStackLimit_;\n  FiberImpl fiberImpl_; /**< underlying fiber implementation */\n  std::shared_ptr<RequestContext> rcontext_; /**< current RequestContext */\n  folly::AsyncStackRoot* asyncRoot_ = nullptr;\n  folly::Function<void()> func_; /**< task function */\n  std::chrono::steady_clock::time_point currStartTime_;\n  std::chrono::steady_clock::duration prevDuration_{0};\n#ifdef FOLLY_SANITIZE_THREAD\n  void* tsanCtx_{nullptr};\n#endif\n\n  /**\n   * Points to next fiber in remote ready list\n   */\n  folly::AtomicIntrusiveLinkedListHook<Fiber> nextRemoteReady_;\n\n  static constexpr size_t kUserBufferSize = 256;\n  folly::aligned_storage_t<kUserBufferSize> userBuffer_;\n\n  void* getUserBuffer();\n\n  folly::Function<void()> resultFunc_;\n  folly::Function<void()> finallyFunc_;\n\n  class LocalData {\n   public:\n    LocalData() {}\n    ~LocalData();\n    LocalData(const LocalData& other);\n    LocalData& operator=(const LocalData& other);\n\n    template <typename T>\n    T& get() {\n      if (data_) {\n        assert(*vtable_.type == typeid(T));\n        return *reinterpret_cast<T*>(data_);\n      }\n      return getSlow<T>();\n    }\n\n    void reset();\n\n    // private:\n    template <typename T>\n    FOLLY_NOINLINE T& getSlow();\n\n    static constexpr size_t kBufferSize = 128;\n    using Buffer = folly::aligned_storage_t<kBufferSize, cacheline_align_v>;\n    struct VTable {\n      std::type_info const* type;\n      // on-heap\n      void* (*make_copy)(void const*);\n      void (*ruin)(void*);\n      // in-situ\n      void* (*ctor_copy)(void*, void const*);\n      void (*dtor)(void*);\n\n      template <typename T>\n      static constexpr VTable get() noexcept {\n        using t = folly::detail::thunk;\n        return {\n            &typeid(T),\n            t::make_copy<T>,\n            t::ruin<T>,\n            t::ctor_copy<T>,\n            t::dtor<T>};\n      }\n    };\n\n    Buffer buffer_;\n    VTable vtable_{};\n    void* data_{nullptr};\n  };\n\n  LocalData localData_;\n\n  folly::IntrusiveListHook listHook_; /**< list hook for different FiberManager\n                                           queues */\n  folly::IntrusiveListHook globalListHook_; /**< list hook for global list */\n\n#ifdef FOLLY_SANITIZE_ADDRESS\n  void* asanFakeStack_{nullptr};\n  const void* asanMainStackBase_{nullptr};\n  size_t asanMainStackSize_{0};\n#endif\n};\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/Fiber-inl.h>\n"
  },
  {
    "path": "folly/fibers/FiberManager-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/functional/Invoke.h>\n#include <folly/futures/Promise.h>\n\nnamespace folly {\nnamespace fibers {\n\ntemplate <typename F>\nauto FiberManager::addTaskFuture(F&& func)\n    -> folly::Future<folly::lift_unit_t<invoke_result_t<F>>> {\n  using T = invoke_result_t<F>;\n  using FutureT = folly::lift_unit_t<T>;\n\n  folly::Promise<FutureT> p;\n  auto f = p.getFuture();\n  addTaskFinally(\n      [func = std::forward<F>(func)]() mutable { return func(); },\n      [p = std::move(p)](folly::Try<T>&& t) mutable {\n        p.setTry(std::move(t));\n      });\n  return f;\n}\n\ntemplate <typename F>\nauto FiberManager::addTaskEagerFuture(F&& func)\n    -> folly::Future<folly::lift_unit_t<invoke_result_t<F>>> {\n  using T = invoke_result_t<F>;\n  using FutureT = typename folly::lift_unit<T>::type;\n\n  folly::Promise<FutureT> p;\n  auto f = p.getFuture();\n  addTaskFinallyEager(\n      [func = std::forward<F>(func)]() mutable { return func(); },\n      [p = std::move(p)](folly::Try<T>&& t) mutable {\n        p.setTry(std::move(t));\n      });\n  return f;\n}\n\ntemplate <typename F>\nauto FiberManager::addTaskRemoteFuture(F&& func)\n    -> folly::Future<folly::lift_unit_t<invoke_result_t<F>>> {\n  folly::Promise<folly::lift_unit_t<invoke_result_t<F>>> p;\n  auto f = p.getFuture();\n  addTaskRemote(\n      [p = std::move(p), func = std::forward<F>(func), this]() mutable {\n        auto t = folly::makeTryWithNoUnwrap(std::forward<F>(func));\n        runInMainContext([&]() { p.setTry(std::move(t)); });\n      });\n  return f;\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/FiberManager.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/FiberManagerInternal.h>\n\n#include <csignal>\n\n#include <cassert>\n\n#include <glog/logging.h>\n\n#include <folly/fibers/Fiber.h>\n#include <folly/fibers/LoopController.h>\n\n#include <folly/ConstexprMath.h>\n#include <folly/SingletonThreadLocal.h>\n#include <folly/memory/SanitizeAddress.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/SysSyscall.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/SanitizeThread.h>\n\nnamespace std {\ntemplate <>\nstruct hash<folly::fibers::FiberManager::Options> {\n  ssize_t operator()(const folly::fibers::FiberManager::Options& opts) const {\n    return hash<decltype(opts.hash())>()(opts.hash());\n  }\n};\n} // namespace std\n\nnamespace folly {\nnamespace fibers {\n\nvoid FiberManager::defaultExceptionCallback(\n    const std::exception_ptr& eptr, StringPiece context) {\n  LOG(DFATAL) << \"Exception thrown in FiberManager with context '\" << context\n              << \"': \" << exceptionStr(eptr);\n}\n\nauto FiberManager::FrozenOptions::create(const Options& options) -> ssize_t {\n  return std::hash<Options>()(options);\n}\n\n/* static */ FiberManager*& FiberManager::getCurrentFiberManager() {\n  struct Tag {};\n  folly::annotate_ignore_thread_sanitizer_guard g(__FILE__, __LINE__);\n  return SingletonThreadLocal<FiberManager*, Tag>::get();\n}\n\nFiberManager::FiberManager(\n    std::unique_ptr<LoopController> loopController, Options options)\n    : FiberManager(LocalType<void>(), std::move(loopController), options) {}\n\nFiberManager::~FiberManager() {\n  loopController_.reset();\n\n  while (!fibersPool_.empty()) {\n    fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });\n  }\n  assert(readyFibers_.empty());\n  assert(!hasTasks());\n}\n\nLoopController& FiberManager::loopController() {\n  return *loopController_;\n}\n\nconst LoopController& FiberManager::loopController() const {\n  return *loopController_;\n}\n\nbool FiberManager::hasTasks() const {\n  return fibersActive_.load(std::memory_order_relaxed) > 0 ||\n      !remoteReadyQueue_.empty() || !remoteTaskQueue_.empty() ||\n      remoteCount_ > 0;\n}\n\nbool FiberManager::isRemoteScheduled() const {\n  return remoteCount_ > 0;\n}\n\nFiber* FiberManager::getFiber() {\n  Fiber* fiber = nullptr;\n\n  if (options_.fibersPoolResizePeriodMs > 0 && !fibersPoolResizerScheduled_) {\n    fibersPoolResizer_.run();\n    fibersPoolResizerScheduled_ = true;\n  }\n\n  if (fibersPool_.empty()) {\n    fiber = new Fiber(*this);\n    fibersAllocated_.store(fibersAllocated() + 1, std::memory_order_relaxed);\n  } else {\n    fiber = &fibersPool_.front();\n    fibersPool_.pop_front();\n    auto fibersPoolSize = fibersPoolSize_.load(std::memory_order_relaxed);\n    assert(fibersPoolSize > 0);\n    fibersPoolSize_.store(fibersPoolSize - 1, std::memory_order_relaxed);\n  }\n  assert(fiber);\n  auto active = 1 + fibersActive_.fetch_add(1, std::memory_order_relaxed);\n  if (active > maxFibersActiveLastPeriod_) {\n    maxFibersActiveLastPeriod_ = active;\n  }\n  ++fiberId_;\n  bool recordStack = (options_.recordStackEvery != 0) &&\n      (fiberId_ % options_.recordStackEvery == 0);\n  fiber->init(recordStack);\n  return fiber;\n}\n\nvoid FiberManager::setExceptionCallback(FiberManager::ExceptionCallback ec) {\n  assert(ec);\n  exceptionCallback_ = std::move(ec);\n}\n\nsize_t FiberManager::fibersAllocated() const {\n  return fibersAllocated_.load(std::memory_order_relaxed);\n}\n\nsize_t FiberManager::fibersPoolSize() const {\n  return fibersPoolSize_.load(std::memory_order_relaxed);\n}\n\nsize_t FiberManager::stackHighWatermark() const {\n  return stackHighWatermark_.load(std::memory_order_relaxed);\n}\n\nvoid FiberManager::remoteReadyInsert(Fiber* fiber) {\n  if (remoteReadyQueue_.insertHead(fiber)) {\n    loopController_->scheduleThreadSafe();\n  }\n}\n\nvoid FiberManager::addObserver(ExecutionObserver* observer) {\n  observerList_.push_back(*observer);\n}\n\nvoid FiberManager::removeObserver(ExecutionObserver* observer) {\n  observerList_.erase(observerList_.iterator_to(*observer));\n}\n\nExecutionObserver::List& FiberManager::getObserverList() {\n  return observerList_;\n}\n\nvoid FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {\n  preemptRunner_ = preemptRunner;\n}\n\nvoid FiberManager::doFibersPoolResizing() {\n  while (true) {\n    auto fibersAllocated = this->fibersAllocated();\n    auto fibersPoolSize = this->fibersPoolSize();\n    if (!(fibersAllocated > maxFibersActiveLastPeriod_ &&\n          fibersPoolSize > options_.maxFibersPoolSize)) {\n      break;\n    }\n    auto fiber = &fibersPool_.front();\n    assert(fiber != nullptr);\n    fibersPool_.pop_front();\n    delete fiber;\n    fibersPoolSize_.store(fibersPoolSize - 1, std::memory_order_relaxed);\n    fibersAllocated_.store(fibersAllocated - 1, std::memory_order_relaxed);\n  }\n\n  maxFibersActiveLastPeriod_ = fibersActive_.load(std::memory_order_relaxed);\n}\n\nvoid FiberManager::FibersPoolResizer::run() {\n  fiberManager_.doFibersPoolResizing();\n  if (auto timer = fiberManager_.loopController_->timer()) {\n    RequestContextScopeGuard rctxGuard(std::shared_ptr<RequestContext>{});\n    timer->scheduleTimeout(\n        this,\n        std::chrono::milliseconds(\n            fiberManager_.options_.fibersPoolResizePeriodMs));\n  }\n}\n\nvoid FiberManager::registerStartSwitchStackWithAsan(\n    void** saveFakeStack, const void* stackBottom, size_t stackSize) {\n  sanitizer_start_switch_fiber(saveFakeStack, stackBottom, stackSize);\n}\n\nvoid FiberManager::registerFinishSwitchStackWithAsan(\n    void* saveFakeStack, const void** saveStackBottom, size_t* saveStackSize) {\n  sanitizer_finish_switch_fiber(saveFakeStack, saveStackBottom, saveStackSize);\n}\n\nvoid FiberManager::freeFakeStack(void* fakeStack) {\n  void* saveFakeStack;\n  const void* stackBottom;\n  size_t stackSize;\n  sanitizer_start_switch_fiber(&saveFakeStack, nullptr, 0);\n  sanitizer_finish_switch_fiber(fakeStack, &stackBottom, &stackSize);\n  sanitizer_start_switch_fiber(nullptr, stackBottom, stackSize);\n  sanitizer_finish_switch_fiber(saveFakeStack, nullptr, nullptr);\n}\n\nvoid FiberManager::unpoisonFiberStack(const Fiber* fiber) {\n  auto stack = fiber->getStack();\n  asan_unpoison_memory_region(stack.first, stack.second);\n}\n\n// TVOS and WatchOS platforms have SIGSTKSZ but not sigaltstack\n#if defined(SIGSTKSZ) && !FOLLY_APPLE_TVOS && !FOLLY_APPLE_WATCHOS\n\nnamespace {\n\nbool hasAlternateStack() {\n  stack_t ss;\n  sigaltstack(nullptr, &ss);\n  return !(ss.ss_flags & SS_DISABLE);\n}\n\nint setAlternateStack(char* sp, size_t size) {\n  CHECK(sp);\n  stack_t ss{};\n  ss.ss_sp = sp;\n  ss.ss_size = size;\n  return sigaltstack(&ss, nullptr);\n}\n\nint unsetAlternateStack() {\n  stack_t ss{};\n  ss.ss_flags = SS_DISABLE;\n  return sigaltstack(&ss, nullptr);\n}\n\nclass ScopedAlternateSignalStack {\n public:\n  ScopedAlternateSignalStack() {\n    if (hasAlternateStack()) {\n      return;\n    }\n\n    // SIGSTKSZ (8 kB on our architectures) isn't always enough for\n    // folly::symbolizer, so allocate 32 kB.\n    size_t kAltStackSize = std::max(size_t(SIGSTKSZ), size_t(32 * 1024));\n\n    stack_ = std::unique_ptr<char[]>(new char[kAltStackSize]);\n\n    setAlternateStack(stack_.get(), kAltStackSize);\n  }\n\n  ScopedAlternateSignalStack(ScopedAlternateSignalStack&&) = default;\n  ScopedAlternateSignalStack& operator=(ScopedAlternateSignalStack&&) = default;\n\n  ~ScopedAlternateSignalStack() {\n    if (stack_) {\n      unsetAlternateStack();\n    }\n  }\n\n private:\n  std::unique_ptr<char[]> stack_;\n};\n} // namespace\n\nvoid FiberManager::maybeRegisterAlternateSignalStack() {\n  SingletonThreadLocal<ScopedAlternateSignalStack>::get();\n\n  alternateSignalStackRegistered_ = true;\n}\n\n#else\n\nvoid FiberManager::maybeRegisterAlternateSignalStack() {\n  // no-op\n}\n\n#endif\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/FiberManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/fibers/FiberManagerInternal.h>\n\n#include <folly/fibers/FiberManager-inl.h>\n"
  },
  {
    "path": "folly/fibers/FiberManagerInternal-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n\n#include <folly/CPortability.h>\n#include <folly/Memory.h>\n#include <folly/Optional.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Try.h>\n#include <folly/fibers/Baton.h>\n#include <folly/fibers/Fiber.h>\n#include <folly/fibers/LoopController.h>\n#include <folly/fibers/Promise.h>\n#include <folly/tracing/AsyncStack.h>\n\nnamespace folly {\nnamespace fibers {\n\nnamespace {\n\ninline FiberManager::Options preprocessOptions(FiberManager::Options opts) {\n  /**\n   * Adjust the stack size according to the multiplier config.\n   * Typically used with sanitizers, which need a lot of extra stack space.\n   */\n  opts.stackSize *= std::exchange(opts.stackSizeMultiplier, 1);\n  return opts;\n}\n\ntemplate <class F>\nFOLLY_NOINLINE invoke_result_t<F> runNoInline(F&& func) {\n  return func();\n}\n\ntemplate <class Result, class F>\nFOLLY_NOINLINE void tryEmplaceWithNoInline(\n    folly::Try<Result>& result, F&& func) {\n  folly::tryEmplaceWith(result, std::forward<F>(func));\n}\n\n} // namespace\n\ninline void FiberManager::ensureLoopScheduled() {\n  if (isLoopScheduled_) {\n    return;\n  }\n\n  isLoopScheduled_ = true;\n  loopController_->schedule();\n}\n\ninline void FiberManager::activateFiber(Fiber* fiber) {\n  DCHECK_EQ(activeFiber_, (Fiber*)nullptr);\n\n#ifdef FOLLY_SANITIZE_ADDRESS\n  DCHECK(!fiber->asanMainStackBase_);\n  DCHECK(!fiber->asanMainStackSize_);\n  auto stack = fiber->getStack();\n  void* asanFakeStack;\n  registerStartSwitchStackWithAsan(&asanFakeStack, stack.first, stack.second);\n  SCOPE_EXIT {\n    registerFinishSwitchStackWithAsan(asanFakeStack, nullptr, nullptr);\n    fiber->asanMainStackBase_ = nullptr;\n    fiber->asanMainStackSize_ = 0;\n  };\n#endif\n\n  activeFiber_ = fiber;\n\n#ifdef FOLLY_SANITIZE_THREAD\n  auto tsanCtx = __tsan_get_current_fiber();\n  __tsan_switch_to_fiber(fiber->tsanCtx_, 0);\n#endif\n\n  fiber->fiberImpl_.activate();\n\n#ifdef FOLLY_SANITIZE_THREAD\n  __tsan_switch_to_fiber(tsanCtx, 0);\n#endif\n}\n\ninline void FiberManager::deactivateFiber(Fiber* fiber) {\n  DCHECK_EQ(activeFiber_, fiber);\n\n#ifdef FOLLY_SANITIZE_ADDRESS\n  DCHECK(fiber->asanMainStackBase_);\n  DCHECK(fiber->asanMainStackSize_);\n\n  registerStartSwitchStackWithAsan(\n      &fiber->asanFakeStack_,\n      fiber->asanMainStackBase_,\n      fiber->asanMainStackSize_);\n  SCOPE_EXIT {\n    registerFinishSwitchStackWithAsan(\n        fiber->asanFakeStack_,\n        &fiber->asanMainStackBase_,\n        &fiber->asanMainStackSize_);\n    fiber->asanFakeStack_ = nullptr;\n  };\n#endif\n\n  activeFiber_ = nullptr;\n  fiber->fiberImpl_.deactivate();\n}\n\ninline void FiberManager::runReadyFiber(Fiber* fiber) {\n  SCOPE_EXIT {\n    assert(currentFiber_ == nullptr);\n    assert(activeFiber_ == nullptr);\n  };\n\n  assert(\n      fiber->state_ == Fiber::NOT_STARTED ||\n      fiber->state_ == Fiber::READY_TO_RUN);\n  currentFiber_ = fiber;\n  // Note: resetting the context is handled by the loop\n  RequestContext::setContext(std::move(fiber->rcontext_));\n\n  (void)folly::exchangeCurrentAsyncStackRoot(\n      std::exchange(fiber->asyncRoot_, nullptr));\n\n  folly::Optional<ExecutionObserverScopeGuard> observersGuard{\n      std::in_place,\n      &observerList_,\n      fiber,\n      folly::ExecutionObserver::CallbackType::Fiber};\n  SCOPE_EXIT {\n    // Ensure that the guard is explicitly destroyed for all terminal states, so\n    // that it is done at the right time.\n    assert(!observersGuard);\n  };\n\n  while (fiber->state_ == Fiber::NOT_STARTED ||\n         fiber->state_ == Fiber::READY_TO_RUN) {\n    activateFiber(fiber);\n    if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) {\n      try {\n        immediateFunc_();\n      } catch (...) {\n        exceptionCallback_(current_exception(), \"running immediateFunc_\");\n      }\n      immediateFunc_ = nullptr;\n      fiber->state_ = Fiber::READY_TO_RUN;\n    }\n  }\n\n  if (fiber->state_ == Fiber::AWAITING) {\n    awaitFunc_(*fiber);\n    awaitFunc_ = nullptr;\n    observersGuard.reset();\n    currentFiber_ = nullptr;\n    fiber->rcontext_ = RequestContext::saveContext();\n    fiber->asyncRoot_ = folly::exchangeCurrentAsyncStackRoot(nullptr);\n  } else if (fiber->state_ == Fiber::INVALID) {\n    assert(fibersActive_.load(std::memory_order_relaxed) > 0);\n    fibersActive_.fetch_sub(1, std::memory_order_relaxed);\n    // Making sure that task functor is deleted once task is complete.\n    // NOTE: we must do it on main context, as the fiber is not\n    // running at this point.\n    fiber->func_ = nullptr;\n    fiber->resultFunc_ = nullptr;\n    fiber->taskOptions_ = TaskOptions();\n    if (fiber->finallyFunc_) {\n      try {\n        fiber->finallyFunc_();\n      } catch (...) {\n        exceptionCallback_(current_exception(), \"running finallyFunc_\");\n      }\n      fiber->finallyFunc_ = nullptr;\n    }\n    observersGuard.reset();\n\n    // Make sure LocalData is not accessible from its destructor\n    currentFiber_ = nullptr;\n    fiber->rcontext_ = RequestContext::saveContext();\n    // Async stack roots should have been popped by the time the\n    // func_() call has returned.\n    fiber->asyncRoot_ = folly::exchangeCurrentAsyncStackRoot(nullptr);\n    CHECK(fiber->asyncRoot_ == nullptr);\n\n    fiber->localData_.reset();\n    fiber->rcontext_.reset();\n\n    if (fibersPoolSize_ < options_.maxFibersPoolSize ||\n        options_.fibersPoolResizePeriodMs > 0) {\n      fiber->fiberStackHighWatermark_ = 0;\n      fibersPool_.push_front(*fiber);\n      ++fibersPoolSize_;\n    } else {\n      delete fiber;\n      assert(fibersAllocated_ > 0);\n      --fibersAllocated_;\n    }\n  } else if (fiber->state_ == Fiber::YIELDED) {\n    observersGuard.reset();\n    currentFiber_ = nullptr;\n    fiber->rcontext_ = RequestContext::saveContext();\n    fiber->asyncRoot_ = folly::exchangeCurrentAsyncStackRoot(nullptr);\n    fiber->state_ = Fiber::READY_TO_RUN;\n    yieldedFibers_->push_back(*fiber);\n  }\n}\n\ninline void FiberManager::loopUntilNoReady() {\n  return loopController_->runLoop();\n}\n\ntemplate <typename LoopFunc>\nvoid FiberManager::runFibersHelper(LoopFunc&& loopFunc) {\n  if (FOLLY_UNLIKELY(!alternateSignalStackRegistered_)) {\n    maybeRegisterAlternateSignalStack();\n  }\n\n  // Support nested FiberManagers\n  auto originalFiberManager = std::exchange(getCurrentFiberManager(), this);\n\n  numUncaughtExceptions_ = uncaught_exceptions();\n  currentException_ = current_exception();\n\n  // Save current context, and reset it after executing all fibers.\n  // This can avoid a lot of context swapping,\n  // if the Fibers share the same context\n  auto curCtx = RequestContext::saveContext();\n\n  auto* curAsyncRoot = folly::exchangeCurrentAsyncStackRoot(nullptr);\n\n  FiberTailQueue yieldedFibers;\n  auto prevYieldedFibers = std::exchange(yieldedFibers_, &yieldedFibers);\n\n  SCOPE_EXIT {\n    // Restore the previous AsyncStackRoot and make sure that none of\n    // the fibers left any AsyncStackRoot pointers lying around.\n    auto* oldAsyncRoot = folly::exchangeCurrentAsyncStackRoot(curAsyncRoot);\n    CHECK(oldAsyncRoot == nullptr);\n\n    yieldedFibers_ = prevYieldedFibers;\n    readyFibers_.splice(readyFibers_.end(), yieldedFibers);\n    RequestContext::setContext(std::move(curCtx));\n    if (!readyFibers_.empty()) {\n      ensureLoopScheduled();\n    }\n    std::swap(getCurrentFiberManager(), originalFiberManager);\n    CHECK_EQ(this, originalFiberManager);\n  };\n\n  loopFunc();\n}\n\ninline size_t FiberManager::recordStackPosition(size_t position) {\n  auto newPosition = std::max(stackHighWatermark(), position);\n  stackHighWatermark_.store(newPosition, std::memory_order_relaxed);\n  return newPosition;\n}\n\ninline void FiberManager::loopUntilNoReadyImpl() {\n  runFibersHelper([&] {\n    SCOPE_EXIT {\n      isLoopScheduled_ = false;\n    };\n\n    bool hadRemote = true;\n    while (hadRemote) {\n      while (!readyFibers_.empty()) {\n        auto& fiber = readyFibers_.front();\n        readyFibers_.pop_front();\n        runReadyFiber(&fiber);\n      }\n\n      auto hadRemoteFiber = remoteReadyQueue_.sweepOnce([this](Fiber* fiber) {\n        runReadyFiber(fiber);\n      });\n\n      if (hadRemoteFiber) {\n        ++remoteCount_;\n      }\n\n      auto hadRemoteTask =\n          remoteTaskQueue_.sweepOnce([this](RemoteTask* taskPtr) {\n            std::unique_ptr<RemoteTask> task(taskPtr);\n            auto fiber = getFiber();\n            if (task->localData) {\n              fiber->localData_ = *task->localData;\n            }\n            fiber->rcontext_ = std::move(task->rcontext);\n\n            fiber->setFunction(std::move(task->func), TaskOptions());\n            runReadyFiber(fiber);\n          });\n\n      if (hadRemoteTask) {\n        ++remoteCount_;\n      }\n\n      hadRemote = hadRemoteTask || hadRemoteFiber;\n    }\n  });\n}\n\ninline void FiberManager::runEagerFiber(Fiber* fiber) {\n  loopController_->runEagerFiber(fiber);\n}\n\ninline void FiberManager::runEagerFiberImpl(Fiber* fiber) {\n  folly::fibers::runInMainContext([&] {\n    auto prevCurrentFiber = std::exchange(currentFiber_, fiber);\n    SCOPE_EXIT {\n      currentFiber_ = prevCurrentFiber;\n    };\n    runFibersHelper([&] { runReadyFiber(fiber); });\n  });\n}\n\ninline bool FiberManager::shouldRunLoopRemote() {\n  --remoteCount_;\n  return !remoteReadyQueue_.empty() || !remoteTaskQueue_.empty();\n}\n\ninline bool FiberManager::hasReadyTasks() const {\n  return !readyFibers_.empty() || !remoteReadyQueue_.empty() ||\n      !remoteTaskQueue_.empty();\n}\n\n// We need this to be in a struct, not inlined in addTask, because clang crashes\n// otherwise.\ntemplate <typename F>\nstruct FiberManager::AddTaskHelper {\n  class Func;\n\n  static constexpr bool allocateInBuffer =\n      sizeof(Func) <= Fiber::kUserBufferSize;\n\n  class Func {\n   public:\n    Func(F&& func, FiberManager& fm) : func_(std::forward<F>(func)), fm_(fm) {}\n\n    void operator()() {\n      try {\n        func_();\n      } catch (...) {\n        fm_.exceptionCallback_(current_exception(), \"running Func functor\");\n      }\n      if (allocateInBuffer) {\n        this->~Func();\n      } else {\n        delete this;\n      }\n    }\n\n   private:\n    F func_;\n    FiberManager& fm_;\n  };\n};\n\ntemplate <typename F>\nFiber* FiberManager::createTask(F&& func, TaskOptions taskOptions) {\n  typedef AddTaskHelper<F> Helper;\n\n  auto fiber = getFiber();\n  initLocalData(*fiber);\n\n  if (Helper::allocateInBuffer) {\n    auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());\n    new (funcLoc) typename Helper::Func(std::forward<F>(func), *this);\n\n    fiber->setFunction(std::ref(*funcLoc), std::move(taskOptions));\n  } else {\n    auto funcLoc = new typename Helper::Func(std::forward<F>(func), *this);\n\n    fiber->setFunction(std::ref(*funcLoc), std::move(taskOptions));\n  }\n\n  return fiber;\n}\n\ntemplate <typename F>\nvoid FiberManager::addTask(F&& func, TaskOptions taskOptions) {\n  readyFibers_.push_back(\n      *createTask(std::forward<F>(func), std::move(taskOptions)));\n  ensureLoopScheduled();\n}\n\ntemplate <typename F>\nvoid FiberManager::addTaskEager(F&& func) {\n  runEagerFiber(createTask(std::forward<F>(func), TaskOptions()));\n}\n\ntemplate <typename F>\nvoid FiberManager::addTaskRemote(F&& func) {\n  auto task = [&]() {\n    auto currentFm = getFiberManagerUnsafe();\n    if (currentFm && currentFm->currentFiber_ &&\n        currentFm->localType_ == localType_) {\n      return std::make_unique<RemoteTask>(\n          std::forward<F>(func), currentFm->currentFiber_->localData_);\n    }\n    return std::make_unique<RemoteTask>(std::forward<F>(func));\n  }();\n  if (remoteTaskQueue_.insertHead(task.release())) {\n    loopController_->scheduleThreadSafe();\n  }\n}\n\ntemplate <typename X>\nstruct IsRvalueRefTry {\n  static const bool value = false;\n};\ntemplate <typename T>\nstruct IsRvalueRefTry<folly::Try<T>&&> {\n  static const bool value = true;\n};\n\n// We need this to be in a struct, not inlined in addTaskFinally, because clang\n// crashes otherwise.\ntemplate <typename F, typename G>\nstruct FiberManager::AddTaskFinallyHelper {\n  class Func;\n\n  typedef invoke_result_t<F> Result;\n\n  class Finally {\n   public:\n    Finally(G finally, FiberManager& fm)\n        : finally_(std::move(finally)), fm_(fm) {}\n\n    void operator()() {\n      try {\n        finally_(std::move(result_));\n      } catch (...) {\n        fm_.exceptionCallback_(current_exception(), \"running Finally functor\");\n      }\n\n      if (allocateInBuffer) {\n        this->~Finally();\n      } else {\n        delete this;\n      }\n    }\n\n   private:\n    friend class Func;\n\n    G finally_;\n    folly::Try<Result> result_;\n    FiberManager& fm_;\n  };\n\n  class Func {\n   public:\n    Func(F func, Finally& finally)\n        : func_(std::move(func)), result_(finally.result_) {}\n\n    void operator()() {\n      folly::tryEmplaceWith(result_, std::move(func_));\n\n      if (allocateInBuffer) {\n        this->~Func();\n      } else {\n        delete this;\n      }\n    }\n\n   private:\n    F func_;\n    folly::Try<Result>& result_;\n  };\n\n  static constexpr bool allocateInBuffer =\n      sizeof(Func) + sizeof(Finally) <= Fiber::kUserBufferSize;\n};\n\ntemplate <typename F, typename G>\nFiber* FiberManager::createTaskFinally(F&& func, G&& finally) {\n  typedef invoke_result_t<F> Result;\n\n  static_assert(\n      IsRvalueRefTry<typename FirstArgOf<G>::type>::value,\n      \"finally(arg): arg must be Try<T>&&\");\n  static_assert(\n      std::is_convertible<\n          Result,\n          typename std::remove_reference<\n              typename FirstArgOf<G>::type>::type::element_type>::value,\n      \"finally(Try<T>&&): T must be convertible from func()'s return type\");\n\n  auto fiber = getFiber();\n  initLocalData(*fiber);\n\n  typedef AddTaskFinallyHelper<\n      typename std::decay<F>::type,\n      typename std::decay<G>::type>\n      Helper;\n\n  if (Helper::allocateInBuffer) {\n    auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());\n    auto finallyLoc =\n        static_cast<typename Helper::Finally*>(static_cast<void*>(funcLoc + 1));\n\n    new (finallyLoc) typename Helper::Finally(std::forward<G>(finally), *this);\n    new (funcLoc) typename Helper::Func(std::forward<F>(func), *finallyLoc);\n\n    fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));\n  } else {\n    auto finallyLoc =\n        new typename Helper::Finally(std::forward<G>(finally), *this);\n    auto funcLoc =\n        new typename Helper::Func(std::forward<F>(func), *finallyLoc);\n\n    fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));\n  }\n\n  return fiber;\n}\n\ntemplate <typename F, typename G>\nvoid FiberManager::addTaskFinally(F&& func, G&& finally) {\n  readyFibers_.push_back(\n      *createTaskFinally(std::forward<F>(func), std::forward<G>(finally)));\n  ensureLoopScheduled();\n}\n\ntemplate <typename F, typename G>\nvoid FiberManager::addTaskFinallyEager(F&& func, G&& finally) {\n  runEagerFiber(\n      createTaskFinally(std::forward<F>(func), std::forward<G>(finally)));\n}\n\ntemplate <typename F>\ninvoke_result_t<F> FiberManager::runInMainContext(F&& func) {\n  if (FOLLY_UNLIKELY(activeFiber_ == nullptr)) {\n    return runNoInline(std::forward<F>(func));\n  }\n\n  typedef invoke_result_t<F> Result;\n\n  folly::Try<Result> result;\n  auto f = [&func, &result]() mutable {\n    tryEmplaceWithNoInline(result, std::forward<F>(func));\n  };\n\n  immediateFunc_ = std::ref(f);\n  activeFiber_->preempt(Fiber::AWAITING_IMMEDIATE);\n\n  return std::move(result).value();\n}\n\ninline FiberManager& FiberManager::getFiberManager() {\n  assert(getCurrentFiberManager() != nullptr);\n  return *getCurrentFiberManager();\n}\n\ninline FiberManager* FiberManager::getFiberManagerUnsafe() {\n  return getCurrentFiberManager();\n}\n\ninline bool FiberManager::hasActiveFiber() const {\n  return activeFiber_ != nullptr;\n}\n\ninline folly::Optional<std::chrono::nanoseconds>\nFiberManager::getCurrentTaskRunningTime() const {\n  return currentFiber_ ? currentFiber_->getRunningTime() : folly::none;\n}\n\ninline void FiberManager::yield() {\n  assert(getCurrentFiberManager() == this);\n  assert(activeFiber_ != nullptr);\n  assert(activeFiber_->state_ == Fiber::RUNNING);\n  activeFiber_->preempt(Fiber::YIELDED);\n}\n\ntemplate <typename T>\nT& FiberManager::local() {\n  if (std::type_index(typeid(T)) == localType_ && currentFiber_) {\n    return currentFiber_->localData_.get<T>();\n  }\n  return localThread<T>();\n}\n\ntemplate <typename T>\nT& FiberManager::localThread() {\n  static thread_local T t;\n  return t;\n}\n\ninline void FiberManager::initLocalData(Fiber& fiber) {\n  auto fm = getFiberManagerUnsafe();\n  if (fm && fm->currentFiber_ && fm->localType_ == localType_) {\n    fiber.localData_ = fm->currentFiber_->localData_;\n  }\n  fiber.rcontext_ = RequestContext::saveContext();\n}\n\ntemplate <typename LocalT>\nFiberManager::FiberManager(\n    LocalType<LocalT>,\n    std::unique_ptr<LoopController> loopController__,\n    Options options)\n    : loopController_(std::move(loopController__)),\n      stackAllocator_(options.guardPagesPerStack),\n      options_(preprocessOptions(std::move(options))),\n      exceptionCallback_(defaultExceptionCallback),\n      fibersPoolResizer_(*this),\n      localType_(typeid(LocalT)) {\n  loopController_->setFiberManager(this);\n}\n\ntemplate <typename F>\ntypename FirstArgOf<F>::type::value_type inline await_async(F&& func) {\n  typedef typename FirstArgOf<F>::type::value_type Result;\n  typedef typename FirstArgOf<F>::type::baton_type BatonT;\n\n  return Promise<Result, BatonT>::await_async(std::forward<F>(func));\n}\n\ntemplate <typename F>\ninvoke_result_t<F> inline runInMainContext(F&& func) {\n  auto fm = FiberManager::getFiberManagerUnsafe();\n  if (FOLLY_UNLIKELY(fm == nullptr)) {\n    return runNoInline(std::forward<F>(func));\n  }\n  return fm->runInMainContext(std::forward<F>(func));\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/FiberManagerInternal.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <memory>\n#include <queue>\n#include <thread>\n#include <type_traits>\n#include <typeindex>\n#include <unordered_set>\n#include <vector>\n\n#include <folly/AtomicIntrusiveLinkedList.h>\n#include <folly/CPortability.h>\n#include <folly/Executor.h>\n#include <folly/IntrusiveList.h>\n#include <folly/Likely.h>\n#include <folly/Portability.h>\n#include <folly/Try.h>\n#include <folly/functional/Invoke.h>\n#include <folly/io/async/HHWheelTimer.h>\n#include <folly/io/async/Request.h>\n\n#include <folly/executors/ExecutionObserver.h>\n#include <folly/fibers/BoostContextCompatibility.h>\n#include <folly/fibers/Fiber.h>\n#include <folly/fibers/GuardPageAllocator.h>\n#include <folly/fibers/LoopController.h>\n#include <folly/fibers/traits.h>\n\nnamespace folly {\n\ntemplate <class T>\nclass Future;\n\nnamespace fibers {\n\nclass Baton;\nclass Fiber;\n\nstruct TaskOptions;\n\ntemplate <typename T>\nclass LocalType {};\n\nclass InlineFunctionRunner {\n public:\n  virtual ~InlineFunctionRunner() {}\n\n  /**\n   * func must be executed inline and only once.\n   */\n  virtual void run(folly::Function<void()> func) = 0;\n};\n\n/**\n * @class FiberManager\n * @brief Single-threaded task execution engine.\n *\n * FiberManager allows semi-parallel task execution on the same thread. Each\n * task can notify FiberManager that it is blocked on something (via await())\n * call. This will pause execution of this task and it will be resumed only\n * when it is unblocked (via setData()).\n */\nclass FiberManager : public ::folly::Executor {\n public:\n  struct Options {\n    static constexpr size_t kDefaultStackSize{16 * 1024};\n\n    /**\n     * Maximum stack size for fibers which will be used for executing all the\n     * tasks.\n     */\n    size_t stackSize{kDefaultStackSize};\n\n    /**\n     * Sanitizers need a lot of extra stack space. Benchmarks results at\n     * https://www.usenix.org/system/files/conference/atc12/atc12-final39.pdf,\n     * table 2, show that average stack increase with ASAN is 10% and extreme is\n     * 306%. Setting to 400% as \"works for majority of clients\" setting. If\n     * stack data layout of the service leads to even higher ASAN memory\n     * overhead, those services can tune their fiber stack sizes further.\n     *\n     * Even in the absence of ASAN, debug builds still need extra stack space\n     * due to reduced inlining.\n     *\n     */\n    size_t stackSizeMultiplier{kIsSanitize ? 4 : (!kIsOptimize ? 2 : 1)};\n\n    /**\n     * Record exact amount of stack used.\n     *\n     * This is fairly expensive: we fill each newly allocated stack\n     * with some known value and find the boundary of unused stack\n     * with linear search every time we surrender the stack back to fibersPool.\n     * 0 disables stack recording.\n     */\n    size_t recordStackEvery{0};\n\n    /**\n     * Keep at most this many free fibers in the pool.\n     * This way the total number of fibers in the system is always bounded\n     * by the number of active fibers + maxFibersPoolSize.\n     */\n    size_t maxFibersPoolSize{1000};\n\n    /**\n     * Protect a small number of fiber stacks with this many guard pages.\n     */\n    size_t guardPagesPerStack{1};\n\n    /**\n     * Free unnecessary fibers in the fibers pool every fibersPoolResizePeriodMs\n     * milliseconds. If value is 0, periodic resizing of the fibers pool is\n     * disabled.\n     */\n    uint32_t fibersPoolResizePeriodMs{0};\n\n    constexpr Options() {}\n\n    auto hash() const {\n      return std::make_tuple(\n          stackSize,\n          stackSizeMultiplier,\n          recordStackEvery,\n          maxFibersPoolSize,\n          guardPagesPerStack,\n          fibersPoolResizePeriodMs);\n    }\n  };\n\n  /**\n   * A (const) Options instance with a dedicated unique identifier,\n   * which is used as a key in FiberManagerMap.\n   * This is relevant if you want to run different FiberManager,\n   * with different Option, on the same EventBase.\n   */\n  struct FrozenOptions {\n    explicit FrozenOptions(Options opts)\n        : options(std::move(opts)), token(create(options)) {}\n\n    const Options options;\n    const ssize_t token;\n\n   private:\n    static ssize_t create(const Options&);\n  };\n\n  using ExceptionCallback =\n      folly::Function<void(const std::exception_ptr&, StringPiece context)>;\n\n  FiberManager(const FiberManager&) = delete;\n  FiberManager& operator=(const FiberManager&) = delete;\n\n  /**\n   * Initializes, but doesn't start FiberManager loop\n   *\n   * @param loopController A LoopController object\n   * @param options FiberManager options\n   */\n  explicit FiberManager(\n      std::unique_ptr<LoopController> loopController,\n      Options options = Options());\n\n  /**\n   * Initializes, but doesn't start FiberManager loop\n   *\n   * @param loopController A LoopController object\n   * @param options FiberManager options\n   * @tparam LocalT only local of this type may be stored on fibers.\n   *                Locals of other types will be considered thread-locals.\n   */\n  template <typename LocalT>\n  FiberManager(\n      LocalType<LocalT>,\n      std::unique_ptr<LoopController> loopController,\n      Options options = Options());\n\n  ~FiberManager() override;\n\n  /**\n   * Controller access.\n   */\n  LoopController& loopController();\n  const LoopController& loopController() const;\n\n  /**\n   * Keeps running ready tasks until the list of ready tasks is empty.\n   */\n  void loopUntilNoReady();\n\n  /**\n   * This should only be called by a LoopController.\n   */\n  void loopUntilNoReadyImpl();\n\n  /**\n   * This should only be called by a LoopController.\n   */\n  void runEagerFiberImpl(Fiber*);\n\n  /**\n   * This should only be called by a LoopController.\n   */\n  bool shouldRunLoopRemote();\n\n  /**\n   * @return true if there are outstanding tasks.\n   */\n  bool hasTasks() const;\n  bool isRemoteScheduled() const;\n\n  /**\n   * @return The number of currently active fibers (ready to run or blocked).\n   * Does not include the number of remotely enqueued tasks that have not been\n   * run yet.\n   */\n  size_t numActiveTasks() const noexcept {\n    return fibersActive_.load(std::memory_order_relaxed);\n  }\n\n  /**\n   * @return true if there are tasks ready to run.\n   */\n  bool hasReadyTasks() const;\n\n  /**\n   * Sets exception callback which will be called if any of the tasks throws an\n   * exception.\n   *\n   * @param ec An ExceptionCallback object.\n   */\n  void setExceptionCallback(ExceptionCallback ec);\n\n  /**\n   * Add a new task to be executed. Must be called from FiberManager's thread.\n   *\n   * @param func Task functor; must have a signature of `void func()`.\n   *             The object will be destroyed once task execution is complete.\n   * @param taskOptions Task specific configs.\n   */\n  template <typename F>\n  void addTask(F&& func, TaskOptions taskOptions = TaskOptions());\n\n  /**\n   * Add a new task to be executed and return a future that will be set on\n   * return from func. Must be called from FiberManager's thread.\n   *\n   * @param func Task functor; must have a signature of `void func()`.\n   *             The object will be destroyed once task execution is complete.\n   */\n  template <typename F>\n  auto addTaskFuture(F&& func)\n      -> folly::Future<folly::lift_unit_t<invoke_result_t<F>>>;\n\n  /**\n   * Add a new task to be executed. Must be called from FiberManager's thread.\n   * The new task is run eagerly. addTaskEager will return only once the new\n   * task reaches its first suspension point or is completed.\n   *\n   * @param func Task functor; must have a signature of `void func()`.\n   *             The object will be destroyed once task execution is complete.\n   */\n  template <typename F>\n  void addTaskEager(F&& func);\n\n  /**\n   * Add a new task to be executed and return a future that will be set on\n   * return from func. Must be called from FiberManager's thread.\n   * The new task is run eagerly. addTaskEager will return only once the new\n   * task reaches its first suspension point or is completed.\n   *\n   * @param func Task functor; must have a signature of `void func()`.\n   *             The object will be destroyed once task execution is complete.\n   */\n  template <typename F>\n  auto addTaskEagerFuture(F&& func)\n      -> folly::Future<folly::lift_unit_t<invoke_result_t<F>>>;\n\n  /**\n   * Add a new task to be executed. Safe to call from other threads.\n   *\n   * @param func Task function; must have a signature of `void func()`.\n   *             The object will be destroyed once task execution is complete.\n   */\n  template <typename F>\n  void addTaskRemote(F&& func);\n\n  /**\n   * Add a new task to be executed and return a future that will be set on\n   * return from func. Safe to call from other threads.\n   *\n   * @param func Task function; must have a signature of `void func()`.\n   *             The object will be destroyed once task execution is complete.\n   */\n  template <typename F>\n  auto addTaskRemoteFuture(F&& func)\n      -> folly::Future<folly::lift_unit_t<invoke_result_t<F>>>;\n\n  // Executor interface calls addTaskRemote\n  void add(folly::Func f) override { addTaskRemote(std::move(f)); }\n\n  /**\n   * Add a new task. When the task is complete, execute finally(Try<Result>&&)\n   * on the main context.\n   *\n   * @param func Task functor; must have a signature of `T func()` for some T.\n   * @param finally Finally functor; must have a signature of\n   *                `void finally(Try<T>&&)` and will be passed\n   *                the result of func() (including the exception if occurred).\n   */\n  template <typename F, typename G>\n  void addTaskFinally(F&& func, G&& finally);\n\n  /**\n   * Add a new task. When the task is complete, execute finally(Try<Result>&&)\n   * on the main context.\n   * The new task is run eagerly. addTaskEager will return only once the new\n   * task reaches its first suspension point or is completed.\n   *\n   * @param func Task functor; must have a signature of `T func()` for some T.\n   * @param finally Finally functor; must have a signature of\n   *                `void finally(Try<T>&&)` and will be passed\n   *                the result of func() (including the exception if occurred).\n   */\n  template <typename F, typename G>\n  void addTaskFinallyEager(F&& func, G&& finally);\n\n  /**\n   * If called from a fiber, immediately switches to the FiberManager's context\n   * and runs func(), going back to the Fiber's context after completion.\n   * Outside a fiber, just calls func() directly.\n   *\n   * @return value returned by func().\n   */\n  template <typename F>\n  invoke_result_t<F> runInMainContext(F&& func);\n\n  /**\n   * Returns a refference to a fiber-local context for given Fiber. Should be\n   * always called with the same T for each fiber. Fiber-local context is lazily\n   * default-constructed on first request.\n   * When new task is scheduled via addTask / addTaskRemote from a fiber its\n   * fiber-local context is copied into the new fiber.\n   */\n  template <typename T>\n  T& local();\n\n  template <typename T>\n  FOLLY_EXPORT static T& localThread();\n\n  /**\n   * @return How many fiber objects (and stacks) has this manager allocated.\n   */\n  size_t fibersAllocated() const;\n\n  /**\n   * @return How many of the allocated fiber objects are currently\n   * in the free pool.\n   */\n  size_t fibersPoolSize() const;\n\n  /**\n   * @return true if running activeFiber_ is not nullptr.\n   */\n  bool hasActiveFiber() const;\n\n  /**\n   * @return How long has the currently running task on the fiber ran, in\n   * terms of wallclock time. This excludes the time spent in preempted or\n   * waiting stages. This only works if TaskOptions.logRunningTime is true\n   * during addTask().\n   */\n  folly::Optional<std::chrono::nanoseconds> getCurrentTaskRunningTime() const;\n\n  /**\n   * @return The currently running fiber or null if no fiber is executing.\n   */\n  Fiber* currentFiber() const { return currentFiber_; }\n\n  /**\n   * @return What was the most observed fiber stack usage (in bytes).\n   */\n  size_t stackHighWatermark() const;\n\n  /**\n   * Yield execution of the currently running fiber. Must only be called from a\n   * fiber executing on this FiberManager. The calling fiber will be scheduled\n   * when all other fibers have had a chance to run and the event loop is\n   * serviced.\n   */\n  void yield();\n\n  /**\n   * Setup fibers execution observation/instrumentation. Fiber locals are\n   * available to observer.\n   *\n   * @param observer  Fiber's execution observer.\n   */\n  void addObserver(ExecutionObserver* observer);\n\n  void removeObserver(ExecutionObserver* observer);\n\n  /**\n   * @return Current observer for this FiberManager. Returns nullptr\n   * if no observer has been set.\n   */\n  ExecutionObserver::List& getObserverList();\n\n  /**\n   * Setup fibers preempt runner.\n   */\n  void setPreemptRunner(InlineFunctionRunner* preemptRunner);\n\n  /**\n   * Returns an estimate of the number of fibers which are waiting to run (does\n   * not include fibers or tasks scheduled remotely).\n   */\n  size_t runQueueSize() const {\n    return readyFibers_.size() + (yieldedFibers_ ? yieldedFibers_->size() : 0);\n  }\n\n  static FiberManager& getFiberManager();\n  static FiberManager* getFiberManagerUnsafe();\n\n  const Options& getOptions() const { return options_; }\n\n private:\n  friend class Baton;\n  friend class Fiber;\n  template <typename F>\n  struct AddTaskHelper;\n  template <typename F, typename G>\n  struct AddTaskFinallyHelper;\n\n  struct RemoteTask {\n    template <typename F>\n    explicit RemoteTask(F&& f)\n        : func(std::forward<F>(f)), rcontext(RequestContext::saveContext()) {}\n    template <typename F>\n    RemoteTask(F&& f, const Fiber::LocalData& localData_)\n        : func(std::forward<F>(f)),\n          localData(std::make_unique<Fiber::LocalData>(localData_)),\n          rcontext(RequestContext::saveContext()) {}\n    folly::Function<void()> func;\n    std::unique_ptr<Fiber::LocalData> localData;\n    std::shared_ptr<RequestContext> rcontext;\n    AtomicIntrusiveLinkedListHook<RemoteTask> nextRemoteTask;\n  };\n\n  static void defaultExceptionCallback(\n      const std::exception_ptr& eptr, StringPiece context);\n\n  template <typename F>\n  Fiber* createTask(F&& func, TaskOptions taskOptions);\n\n  template <typename F, typename G>\n  Fiber* createTaskFinally(F&& func, G&& finally);\n\n  void runEagerFiber(Fiber* fiber);\n\n  void activateFiber(Fiber* fiber);\n  void deactivateFiber(Fiber* fiber);\n\n  template <typename LoopFunc>\n  void runFibersHelper(LoopFunc&& loopFunc);\n\n  size_t recordStackPosition(size_t position);\n\n  using FiberTailQueue = folly::IntrusiveList<Fiber, &Fiber::listHook_>;\n  using GlobalFiberTailQueue =\n      folly::IntrusiveList<Fiber, &Fiber::globalListHook_>;\n\n  Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */\n  /**\n   * Same as active fiber, but also set for functions run from fiber on main\n   * context.\n   */\n  Fiber* currentFiber_{nullptr};\n\n  FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */\n  FiberTailQueue* yieldedFibers_{nullptr}; /**< queue of fibers which have\n                                      yielded execution */\n  FiberTailQueue fibersPool_; /**< pool of uninitialized Fiber objects */\n\n  GlobalFiberTailQueue allFibers_; /**< list of all Fiber objects owned */\n\n  // total number of fibers allocated\n  std::atomic<size_t> fibersAllocated_{0};\n  // total number of fibers in the free pool\n  std::atomic<size_t> fibersPoolSize_{0};\n  std::atomic<size_t> fibersActive_{\n      0}; /**< number of running or blocked fibers */\n  size_t fiberId_{0}; /**< id of last fiber used */\n\n  /**\n   * Maximum number of active fibers in the last period lasting\n   * Options::fibersPoolResizePeriod milliseconds.\n   */\n  size_t maxFibersActiveLastPeriod_{0};\n\n  std::unique_ptr<LoopController> loopController_;\n  bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */\n\n  /**\n   * When we are inside FiberManager loop this points to FiberManager. Otherwise\n   * it's nullptr\n   */\n  static FiberManager*& getCurrentFiberManager();\n\n  /**\n   * Allocator used to allocate stack for Fibers in the pool.\n   * Allocates stack on the stack of the main context.\n   */\n  GuardPageAllocator stackAllocator_;\n\n  const Options options_; /**< FiberManager options */\n\n  /**\n   * Largest observed individual Fiber stack usage in bytes.\n   */\n  std::atomic<size_t> stackHighWatermark_{0};\n\n  /**\n   * Schedules a loop with loopController (unless already scheduled before).\n   */\n  void ensureLoopScheduled();\n\n  /**\n   * @return An initialized Fiber object from the pool\n   */\n  Fiber* getFiber();\n\n  /**\n   * Sets local data for given fiber if all conditions are met.\n   */\n  void initLocalData(Fiber& fiber);\n\n  /**\n   * Function passed to the await call.\n   */\n  folly::Function<void(Fiber&)> awaitFunc_;\n\n  /**\n   * Function passed to the runInMainContext call.\n   */\n  folly::Function<void()> immediateFunc_;\n\n  /**\n   * Preempt runner.\n   */\n  InlineFunctionRunner* preemptRunner_{nullptr};\n\n  /**\n   * Fiber's execution observer.\n   */\n  ExecutionObserver::List observerList_{};\n\n  ExceptionCallback exceptionCallback_; /**< task exception callback */\n\n  folly::AtomicIntrusiveLinkedList<Fiber, &Fiber::nextRemoteReady_>\n      remoteReadyQueue_;\n\n  folly::AtomicIntrusiveLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>\n      remoteTaskQueue_;\n\n  ssize_t remoteCount_{0};\n\n  /**\n   * Number of uncaught exceptions when FiberManager loop was called.\n   */\n  ssize_t numUncaughtExceptions_{0};\n  /**\n   * Current exception when FiberManager loop was called.\n   */\n  std::exception_ptr currentException_;\n\n  class FibersPoolResizer final : private HHWheelTimer::Callback {\n   public:\n    explicit FibersPoolResizer(FiberManager& fm) : fiberManager_(fm) {}\n    void run();\n\n   private:\n    FiberManager& fiberManager_;\n    void timeoutExpired() noexcept override { run(); }\n    void callbackCanceled() noexcept override {}\n  };\n\n  FibersPoolResizer fibersPoolResizer_;\n  bool fibersPoolResizerScheduled_{false};\n\n  void doFibersPoolResizing();\n\n  /**\n   * Only local of this type will be available for fibers.\n   */\n  std::type_index localType_;\n\n  void runReadyFiber(Fiber* fiber);\n  void remoteReadyInsert(Fiber* fiber);\n\n  // These methods notify ASAN when a fiber is entered/exited so that ASAN can\n  // find the right stack extents when it needs to poison/unpoison the stack.\n  void registerStartSwitchStackWithAsan(\n      void** saveFakeStack, const void* stackBase, size_t stackSize);\n  void registerFinishSwitchStackWithAsan(\n      void* fakeStack, const void** saveStackBase, size_t* saveStackSize);\n  void freeFakeStack(void* fakeStack);\n  void unpoisonFiberStack(const Fiber* fiber);\n\n  bool alternateSignalStackRegistered_{false};\n\n  void maybeRegisterAlternateSignalStack();\n};\n\n/**\n * @return      true iff we are running in a fiber's context\n */\ninline bool onFiber() {\n  auto fm = FiberManager::getFiberManagerUnsafe();\n  return fm ? fm->hasActiveFiber() : false;\n}\n\n/**\n * Add a new task to be executed.\n *\n * @param func Task functor; must have a signature of `void func()`.\n *             The object will be destroyed once task execution is complete.\n */\ntemplate <typename F>\ninline void addTask(F&& func) {\n  return FiberManager::getFiberManager().addTask(std::forward<F>(func));\n}\n\ntemplate <typename F>\ninline void addTaskEager(F&& func) {\n  return FiberManager::getFiberManager().addTaskEager(std::forward<F>(func));\n}\n\n/**\n * Add a new task. When the task is complete, execute finally(Try<Result>&&)\n * on the main context.\n * Task functor is run and destroyed on the fiber context.\n * Finally functor is run and destroyed on the main context.\n *\n * @param func Task functor; must have a signature of `T func()` for some T.\n * @param finally Finally functor; must have a signature of\n *                `void finally(Try<T>&&)` and will be passed\n *                the result of func() (including the exception if occurred).\n */\ntemplate <typename F, typename G>\ninline void addTaskFinally(F&& func, G&& finally) {\n  return FiberManager::getFiberManager().addTaskFinally(\n      std::forward<F>(func), std::forward<G>(finally));\n}\n\ntemplate <typename F, typename G>\ninline void addTaskFinallyEager(F&& func, G&& finally) {\n  return FiberManager::getFiberManager().addTaskFinallyEager(\n      std::forward<F>(func), std::forward<G>(finally));\n}\n\n/**\n * Blocks task execution until given promise is fulfilled.\n *\n * Calls function passing in a Promise<T>, which has to be fulfilled.\n *\n * @return data which was used to fulfill the promise.\n */\ntemplate <typename F>\ntypename FirstArgOf<F>::type::value_type inline await_async(F&& func);\n#if !defined(_MSC_VER)\ntemplate <typename F>\nFOLLY_ERASE typename FirstArgOf<F>::type::value_type await(F&& func) {\n  return await_async(static_cast<F&&>(func));\n}\n#endif\n\n/**\n * If called from a fiber, immediately switches to the FiberManager's context\n * and runs func(), going back to the Fiber's context after completion.\n * Outside a fiber, just calls func() directly.\n *\n * @return value returned by func().\n */\ntemplate <typename F>\ninvoke_result_t<F> inline runInMainContext(F&& func);\n\n/**\n * Returns a refference to a fiber-local context for given Fiber. Should be\n * always called with the same T for each fiber. Fiber-local context is lazily\n * default-constructed on first request.\n * When new task is scheduled via addTask / addTaskRemote from a fiber its\n * fiber-local context is copied into the new fiber.\n */\ntemplate <typename T>\nT& local() {\n  auto fm = FiberManager::getFiberManagerUnsafe();\n  if (fm) {\n    return fm->local<T>();\n  }\n  return FiberManager::localThread<T>();\n}\n\ninline void yield() {\n  auto fm = FiberManager::getFiberManagerUnsafe();\n  if (fm) {\n    fm->yield();\n  } else {\n    std::this_thread::yield();\n  }\n}\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/FiberManagerInternal-inl.h>\n"
  },
  {
    "path": "folly/fibers/FiberManagerMap-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n\n#include <folly/Function.h>\n#include <folly/ScopeGuard.h>\n#include <folly/SingletonThreadLocal.h>\n#include <folly/Synchronized.h>\n#include <folly/container/F14Map.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\nnamespace folly {\nnamespace fibers {\n\nnamespace detail {\n\n// ssize_t is a hash of FiberManager::Options\ntemplate <typename EventBaseT>\nusing Key = std::tuple<EventBaseT*, ssize_t, const std::type_info*>;\n\ntemplate <typename EventBaseT>\nFunction<void()> makeOnEventBaseDestructionCallback(const Key<EventBaseT>& key);\n\ntemplate <typename EventBaseT>\nclass GlobalCache {\n public:\n  using TypeMap = std::unordered_map< //\n      std::type_index,\n      std::unordered_set<const std::type_info*>>;\n  template <typename LocalT>\n  static FiberManager& get(\n      const Key<EventBaseT>& key,\n      EventBaseT& evb,\n      const FiberManager::Options& opts) {\n    return instance().template getImpl<LocalT>(key, evb, opts);\n  }\n\n  static std::unique_ptr<FiberManager> erase(const Key<EventBaseT>& key) {\n    return instance().eraseImpl(key);\n  }\n\n  static TypeMap getTypeMap() { return instance().getTypeMapImpl(); }\n\n private:\n  GlobalCache() = default;\n\n  // Leak this intentionally. During shutdown, we may call getFiberManager,\n  // and want access to the fiber managers during that time.\n  static GlobalCache& instance() {\n    static auto ret = new GlobalCache();\n    return *ret;\n  }\n\n  template <typename LocalT>\n  FiberManager& getImpl(\n      const Key<EventBaseT>& key,\n      EventBaseT& evb,\n      const FiberManager::Options& opts) {\n    bool constructed = false;\n    SCOPE_EXIT {\n      if (constructed) {\n        evb.runOnDestruction(makeOnEventBaseDestructionCallback(key));\n      }\n    };\n\n    auto mkey = MapKey(std::get<0>(key), std::get<1>(key), *std::get<2>(key));\n\n    std::lock_guard lg(mutex_);\n\n    types_[std::get<2>(mkey)].insert(std::get<2>(key));\n\n    auto& fmPtrRef = map_[mkey];\n\n    if (!fmPtrRef) {\n      constructed = true;\n      auto loopController = std::make_unique<EventBaseLoopController>();\n      loopController->attachEventBase(evb);\n      fmPtrRef = std::make_unique<FiberManager>(\n          LocalType<LocalT>(), std::move(loopController), opts);\n    }\n\n    return *fmPtrRef;\n  }\n\n  std::unique_ptr<FiberManager> eraseImpl(const Key<EventBaseT>& key) {\n    auto mkey = MapKey(std::get<0>(key), std::get<1>(key), *std::get<2>(key));\n\n    std::lock_guard lg(mutex_);\n\n    DCHECK_EQ(map_.count(mkey), 1u);\n\n    auto ret = std::move(map_[mkey]);\n    map_.erase(mkey);\n    return ret;\n  }\n\n  TypeMap getTypeMapImpl() {\n    std::lock_guard lg(mutex_);\n\n    return types_;\n  }\n\n  using MapKey = std::tuple<EventBaseT*, ssize_t, std::type_index>;\n\n  std::mutex mutex_;\n  std::unordered_map<MapKey, std::unique_ptr<FiberManager>> map_;\n  TypeMap types_; // can have multiple type_info obj's for one type_index\n};\n\nconstexpr size_t kEraseListMaxSize = 64;\n\ntemplate <typename EventBaseT>\nclass ThreadLocalCache {\n public:\n  template <typename LocalT>\n  static FiberManager& get(\n      uint64_t token, EventBaseT& evb, const FiberManager::Options& opts) {\n    return STL::get().template getImpl<LocalT>(token, evb, opts);\n  }\n\n  static void erase(const Key<EventBaseT>& key) {\n    for (auto& localInstance : STL::accessAllThreads()) {\n      localInstance.eraseInfo_.withWLock([&](auto& info) {\n        if (info.eraseList.size() >= kEraseListMaxSize) {\n          info.eraseAll = true;\n        } else {\n          info.eraseList.push_back(key);\n        }\n        localInstance.eraseRequested_ = true;\n      });\n    }\n  }\n\n private:\n  struct TLTag {};\n  template <typename Base>\n  struct Derived : Base {\n    using Base::Base;\n  };\n  using STL = SingletonThreadLocal<Derived<ThreadLocalCache>, TLTag>;\n\n  ThreadLocalCache() = default;\n\n  template <typename LocalT>\n  FiberManager& getImpl(\n      uint64_t token, EventBaseT& evb, const FiberManager::Options& opts) {\n    if (eraseRequested_) {\n      eraseImpl();\n    }\n\n    auto key = Key<EventBaseT>(&evb, token, &typeid(LocalT));\n    auto it = map_.find(key);\n    if (it != map_.end()) {\n      DCHECK(it->second != nullptr);\n      return *it->second;\n    }\n\n    return getSlowImpl<LocalT>(key, evb, opts);\n  }\n\n  template <typename LocalT>\n  FOLLY_NOINLINE FiberManager& getSlowImpl(\n      Key<EventBaseT> key, EventBaseT& evb, const FiberManager::Options& opts) {\n    auto& ref = GlobalCache<EventBaseT>::template get<LocalT>(key, evb, opts);\n    map_.emplace(key, &ref);\n    return ref;\n  }\n\n  FOLLY_NOINLINE void eraseImpl() {\n    auto types = GlobalCache<EventBaseT>::getTypeMap(); // big copy!!!\n    eraseInfo_.withWLock([&](auto& info) {\n      if (info.eraseAll) {\n        map_.clear();\n      } else {\n        for (auto& key : info.eraseList) {\n          for (auto type : types[*std::get<2>(key)]) {\n            map_.erase( // can have multiple type_info obj's for one type_index\n                std::make_tuple(std::get<0>(key), std::get<1>(key), type));\n          }\n        }\n      }\n\n      info.eraseList.clear();\n      info.eraseAll = false;\n      eraseRequested_ = false;\n    });\n  }\n\n  folly::F14FastMap<Key<EventBaseT>, FiberManager*> map_;\n  relaxed_atomic<bool> eraseRequested_{false};\n\n  struct EraseInfo {\n    bool eraseAll{false};\n    std::vector<Key<EventBaseT>> eraseList;\n  };\n\n  folly::Synchronized<EraseInfo> eraseInfo_;\n};\n\ntemplate <typename EventBaseT>\nFunction<void()> makeOnEventBaseDestructionCallback(\n    const Key<EventBaseT>& key) {\n  return [key] {\n    auto fm = GlobalCache<EventBaseT>::erase(key);\n    DCHECK(fm.get() != nullptr);\n    ThreadLocalCache<EventBaseT>::erase(key);\n  };\n}\n\n} // namespace detail\n\ntemplate <typename LocalT>\nFiberManager& getFiberManagerT(\n    EventBase& evb, const FiberManager::Options& opts) {\n  return detail::ThreadLocalCache<EventBase>::get<LocalT>(0, evb, opts);\n}\n\nFiberManager& getFiberManager(\n    folly::EventBase& evb, const FiberManager::Options& opts) {\n  return detail::ThreadLocalCache<EventBase>::get<void>(0, evb, opts);\n}\n\nFiberManager& getFiberManager(\n    VirtualEventBase& evb, const FiberManager::Options& opts) {\n  return detail::ThreadLocalCache<VirtualEventBase>::get<void>(0, evb, opts);\n}\n\nFiberManager& getFiberManager(\n    folly::EventBase& evb, const FiberManager::FrozenOptions& opts) {\n  return detail::ThreadLocalCache<EventBase>::get<void>(\n      opts.token, evb, opts.options);\n}\n\nFiberManager& getFiberManager(\n    VirtualEventBase& evb, const FiberManager::FrozenOptions& opts) {\n  return detail::ThreadLocalCache<VirtualEventBase>::get<void>(\n      opts.token, evb, opts.options);\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/FiberManagerMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/fibers/EventBaseLoopController.h>\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/io/async/VirtualEventBase.h>\n\nnamespace folly {\nnamespace fibers {\n\ntemplate <typename Local>\nstatic inline FiberManager& getFiberManagerT(\n    folly::EventBase& evb,\n    const FiberManager::Options& opts = FiberManager::Options());\n\nstatic inline FiberManager& getFiberManager(\n    folly::EventBase& evb,\n    const FiberManager::Options& opts = FiberManager::Options());\n\nstatic inline FiberManager& getFiberManager(\n    folly::VirtualEventBase& evb,\n    const FiberManager::Options& opts = FiberManager::Options());\n\nstatic inline FiberManager& getFiberManager(\n    folly::EventBase& evb, const FiberManager::FrozenOptions& opts);\n\nstatic inline FiberManager& getFiberManager(\n    folly::VirtualEventBase& evb, const FiberManager::FrozenOptions& opts);\n} // namespace fibers\n} // namespace folly\n#include <folly/fibers/FiberManagerMap-inl.h>\n"
  },
  {
    "path": "folly/fibers/ForEach-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\nnamespace fibers {\n\nnamespace {\n\ntemplate <class F, class G>\ntypename std::enable_if<!std::is_same<invoke_result_t<F>, void>::value, void>::\n    type inline callFuncs(F&& f, G&& g, size_t id) {\n  g(id, f());\n}\n\ntemplate <class F, class G>\ntypename std::enable_if<std::is_same<invoke_result_t<F>, void>::value, void>::\n    type inline callFuncs(F&& f, G&& g, size_t id) {\n  f();\n  g(id);\n}\n\n} // namespace\n\ntemplate <class InputIterator, class F>\ninline void forEach(InputIterator first, InputIterator last, F&& f) {\n  if (first == last) {\n    return;\n  }\n\n  using FuncType = typename std::iterator_traits<InputIterator>::value_type;\n\n  size_t tasksTodo = 1;\n  std::exception_ptr e;\n  Baton baton;\n\n  auto taskFunc = [&tasksTodo, &e, &f, &baton](size_t id, FuncType&& func) {\n    return\n        [id,\n         &tasksTodo,\n         &e,\n         &f,\n         &baton,\n         func_ = std::forward<FuncType>(func)]() mutable {\n          try {\n            callFuncs(std::forward<FuncType>(func_), f, id);\n          } catch (...) {\n            e = current_exception();\n          }\n          if (--tasksTodo == 0) {\n            baton.post();\n          }\n        };\n  };\n\n  auto firstTask = first;\n  ++first;\n\n  for (size_t i = 1; first != last; ++i, ++first, ++tasksTodo) {\n    addTask(taskFunc(i, std::move(*first)));\n  }\n\n  taskFunc(0, std::move(*firstTask))();\n  baton.wait();\n\n  if (e != std::exception_ptr()) {\n    std::rethrow_exception(e);\n  }\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/ForEach.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * Schedules several tasks and blocks until all of them are completed.\n * In the process of their successful completion given callback would be called\n * for each of them with the index of the task and the result it returned (if\n * not void).\n * If any of these n tasks throws an exception, this exception will be\n * re-thrown, but only when all tasks are complete. If several tasks throw\n * exceptions one of them will be re-thrown. Callback won't be called for\n * tasks that throw exception.\n *\n * @param first  Range of tasks to be scheduled\n * @param last\n * @param F      callback to call for each result.\n *               In case of each task returning void it should be callable\n *                  F(size_t id)\n *               otherwise should be callable\n *                  F(size_t id, Result)\n */\ntemplate <class InputIterator, class F>\ninline void forEach(InputIterator first, InputIterator last, F&& f);\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/ForEach-inl.h>\n"
  },
  {
    "path": "folly/fibers/GenericBaton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/synchronization/Baton.h>\n\n#include <folly/fibers/Baton.h>\n\nnamespace folly {\nnamespace fibers {\n\nusing GenericBaton = Baton;\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/GuardPageAllocator.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/GuardPageAllocator.h>\n\n#ifndef _WIN32\n#include <dlfcn.h>\n#endif\n\n#include <csignal>\n#include <iostream>\n#include <mutex>\n\n#include <glog/logging.h>\n\n#include <folly/Singleton.h>\n#include <folly/SpinLock.h>\n#include <folly/Synchronized.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * Each stack with a guard page creates two memory mappings.\n * Since this is a limited resource, we don't want to create too many of these.\n *\n * The upper bound on total number of mappings created\n * is kNumGuarded * kMaxInUse.\n */\n\n/**\n * Number of guarded stacks per allocator instance\n */\nconstexpr size_t kNumGuarded = 100;\n\n/**\n * Maximum number of allocator instances with guarded stacks enabled\n */\nconstexpr size_t kMaxInUse = 100;\n\n/**\n * A cache for kNumGuarded stacks of a given size\n *\n * Thread safe.\n */\nclass StackCache {\n public:\n  explicit StackCache(size_t stackSize, size_t guardPagesPerStack)\n      : allocSize_(allocSize(stackSize, guardPagesPerStack)),\n        guardPagesPerStack_(guardPagesPerStack) {\n    auto p = ::mmap(\n        nullptr,\n        allocSize_ * kNumGuarded,\n        PROT_READ | PROT_WRITE,\n        MAP_PRIVATE | MAP_ANONYMOUS,\n        -1,\n        0);\n    PCHECK(p != (void*)(-1));\n    storage_ = reinterpret_cast<unsigned char*>(p);\n\n    /* Protect the bottommost page of every stack allocation */\n    freeList_.reserve(kNumGuarded);\n    for (size_t i = 0; i < kNumGuarded; ++i) {\n      auto allocBegin = storage_ + allocSize_ * i;\n      freeList_.emplace_back(allocBegin, /* protected= */ false);\n    }\n  }\n\n  unsigned char* borrow(size_t size) {\n    std::lock_guard lg(lock_);\n\n    assert(storage_);\n\n    auto as = allocSize(size, guardPagesPerStack_);\n    if (as != allocSize_ || freeList_.empty()) {\n      return nullptr;\n    }\n\n    auto p = freeList_.back().first;\n    if (!freeList_.back().second) {\n      PCHECK(0 == ::mprotect(p, pagesize() * guardPagesPerStack_, PROT_NONE));\n      protectedRanges().wlock()->insert(\n          std::make_pair(\n              reinterpret_cast<intptr_t>(p),\n              reinterpret_cast<intptr_t>(\n                  p + pagesize() * guardPagesPerStack_)));\n    }\n    freeList_.pop_back();\n\n    /* We allocate minimum number of pages required, plus a guard page.\n       Since we use this for stack storage, requested allocation is aligned\n       at the top of the allocated pages, while the guard page is at the bottom.\n\n               -- increasing addresses -->\n             Guard page     Normal pages\n            |xxxxxxxxxx|..........|..........|\n            <- allocSize_ ------------------->\n         p -^                <- size -------->\n                      limit -^\n    */\n    auto limit = p + allocSize_ - size;\n    assert(limit >= p + pagesize() * guardPagesPerStack_);\n    return limit;\n  }\n\n  bool giveBack(unsigned char* limit, size_t size) {\n    std::lock_guard lg(lock_);\n\n    assert(storage_);\n\n    auto as = allocSize(size, guardPagesPerStack_);\n    if (std::less_equal<void*>{}(limit, storage_) ||\n        std::less_equal<void*>{}(storage_ + allocSize_ * kNumGuarded, limit)) {\n      /* not mine */\n      return false;\n    }\n\n    auto p = limit + size - as;\n    assert(as == allocSize_);\n    assert((p - storage_) % allocSize_ == 0);\n    freeList_.emplace_back(p, /* protected= */ true);\n    return true;\n  }\n\n  ~StackCache() {\n    assert(storage_);\n    protectedRanges().withWLock([&](auto& ranges) {\n      for (const auto& item : freeList_) {\n        ranges.erase(\n            std::make_pair(\n                reinterpret_cast<intptr_t>(item.first),\n                reinterpret_cast<intptr_t>(\n                    item.first + pagesize() * guardPagesPerStack_)));\n      }\n    });\n    PCHECK(0 == ::munmap(storage_, allocSize_ * kNumGuarded));\n  }\n\n  static bool isProtected(intptr_t addr) {\n    // Use a read lock for reading.\n    return protectedRanges().withRLock([&](auto const& ranges) {\n      for (const auto& range : ranges) {\n        if (range.first <= addr && addr < range.second) {\n          return true;\n        }\n      }\n      return false;\n    });\n  }\n\n private:\n  folly::SpinLock lock_;\n  unsigned char* storage_{nullptr};\n  const size_t allocSize_{0};\n  const size_t guardPagesPerStack_{0};\n\n  /**\n   * LIFO free list. Each pair contains stack pointer and protected flag.\n   */\n  std::vector<std::pair<unsigned char*, bool>> freeList_;\n\n  static size_t pagesize() {\n    static const auto pagesize = size_t(sysconf(_SC_PAGESIZE));\n    return pagesize;\n  }\n\n  /**\n   * Returns a multiple of pagesize() enough to store size + a few guard pages\n   */\n  static size_t allocSize(size_t size, size_t guardPages) {\n    return pagesize() * ((size + pagesize() * guardPages - 1) / pagesize() + 1);\n  }\n\n  /**\n   * For each [b, e) range in this set, the bytes in the range were mprotected.\n   */\n  static folly::Synchronized<std::unordered_set<std::pair<intptr_t, intptr_t>>>&\n  protectedRanges() {\n    static auto instance = new folly::Synchronized<\n        std::unordered_set<std::pair<intptr_t, intptr_t>>>();\n    return *instance;\n  }\n};\n\n#ifndef _WIN32\n\nnamespace {\n\nstruct sigaction oldSigsegvAction;\n\nFOLLY_NOINLINE void FOLLY_FIBERS_STACK_OVERFLOW_DETECTED(\n    int signum, siginfo_t* info, void* ucontext) {\n  std::cerr << \"folly::fibers Fiber stack overflow detected.\" << std::endl;\n  // Let the old signal handler handle the signal, but make this function name\n  // present in the stack trace.\n  if (oldSigsegvAction.sa_flags & SA_SIGINFO) {\n    oldSigsegvAction.sa_sigaction(signum, info, ucontext);\n  } else {\n    oldSigsegvAction.sa_handler(signum);\n  }\n  // Prevent tail call optimization.\n  std::cerr << \"\";\n}\n\nvoid sigsegvSignalHandler(int signum, siginfo_t* info, void* ucontext) {\n  // Restore old signal handler\n  sigaction(signum, &oldSigsegvAction, nullptr);\n\n  if (signum != SIGSEGV) {\n    std::cerr << \"GuardPageAllocator signal handler called for signal: \"\n              << signum;\n    return;\n  }\n\n  if (info &&\n      StackCache::isProtected(reinterpret_cast<intptr_t>(info->si_addr))) {\n    FOLLY_FIBERS_STACK_OVERFLOW_DETECTED(signum, info, ucontext);\n    return;\n  }\n\n  // Let the old signal handler handle the signal. Invoke this synchronously\n  // within our own signal handler to ensure that the kernel siginfo context\n  // is not lost.\n  if (oldSigsegvAction.sa_flags & SA_SIGINFO) {\n    oldSigsegvAction.sa_sigaction(signum, info, ucontext);\n  } else {\n    oldSigsegvAction.sa_handler(signum);\n  }\n}\n\nbool isInJVM() {\n  auto getCreated = dlsym(RTLD_DEFAULT, \"JNI_GetCreatedJavaVMs\");\n  return getCreated;\n}\n\nvoid maybeInstallGuardPageSignalHandler() {\n  static std::once_flag onceFlag;\n  std::call_once(onceFlag, installGuardPageSignalHandler);\n}\n} // namespace\n\nvoid installGuardPageSignalHandler() {\n  if (isInJVM()) {\n    // Don't install signal handler, since JVM internal signal handler doesn't\n    // work with SA_ONSTACK\n    return;\n  }\n\n  struct sigaction sa;\n  memset(&sa, 0, sizeof(sa));\n  sigemptyset(&sa.sa_mask);\n  // By default signal handlers are run on the signaled thread's stack.\n  // In case of stack overflow running the SIGSEGV signal handler on\n  // the same stack leads to another SIGSEGV and crashes the program.\n  // Use SA_ONSTACK, so alternate stack is used (only if configured via\n  // sigaltstack).\n  sa.sa_flags |= SA_SIGINFO | SA_ONSTACK;\n  sa.sa_sigaction = &sigsegvSignalHandler;\n  sigaction(SIGSEGV, &sa, &oldSigsegvAction);\n}\n\n#endif // _WIN32\n\n/*\n * RAII Wrapper around a StackCache that calls\n * CacheManager::giveBack() on destruction.\n */\nclass StackCacheEntry {\n public:\n  explicit StackCacheEntry(size_t stackSize, size_t guardPagesPerStack)\n      : stackCache_(\n            std::make_unique<StackCache>(stackSize, guardPagesPerStack)) {}\n\n  StackCache& cache() const noexcept { return *stackCache_; }\n\n  ~StackCacheEntry();\n\n private:\n  std::unique_ptr<StackCache> stackCache_;\n};\n\nclass CacheManager {\n public:\n  static CacheManager& instance() {\n    static auto inst = new CacheManager();\n    return *inst;\n  }\n\n  std::unique_ptr<StackCacheEntry> getStackCache(\n      size_t stackSize, size_t guardPagesPerStack) {\n    auto used = inUse_.load(std::memory_order_relaxed);\n    do {\n      if (used >= kMaxInUse) {\n        return nullptr;\n      }\n    } while (!inUse_.compare_exchange_weak(\n        used, used + 1, std::memory_order_acquire, std::memory_order_relaxed));\n    return std::make_unique<StackCacheEntry>(stackSize, guardPagesPerStack);\n  }\n\n private:\n  std::atomic<size_t> inUse_{0};\n\n  friend class StackCacheEntry;\n\n  void giveBack(std::unique_ptr<StackCache> /* stackCache_ */) {\n    [[maybe_unused]] auto wasUsed =\n        inUse_.fetch_sub(1, std::memory_order_release);\n    assert(wasUsed > 0);\n    /* Note: we can add a free list for each size bucket\n       if stack re-use is important.\n       In this case this needs to be a folly::Singleton\n       to make sure the free list is cleaned up on fork.\n\n       TODO(t7351705): fix Singleton destruction order\n    */\n  }\n};\n\nStackCacheEntry::~StackCacheEntry() {\n  CacheManager::instance().giveBack(std::move(stackCache_));\n}\n\nGuardPageAllocator::GuardPageAllocator(size_t guardPagesPerStack)\n    : guardPagesPerStack_(guardPagesPerStack) {\n#ifndef _WIN32\n  maybeInstallGuardPageSignalHandler();\n#endif\n}\n\nGuardPageAllocator::~GuardPageAllocator() = default;\n\nunsigned char* GuardPageAllocator::allocate(size_t size) {\n  if (guardPagesPerStack_ && !stackCache_) {\n    stackCache_ =\n        CacheManager::instance().getStackCache(size, guardPagesPerStack_);\n  }\n\n  if (stackCache_) {\n    auto p = stackCache_->cache().borrow(size);\n    if (p != nullptr) {\n      return p;\n    }\n  }\n  return fallbackAllocator_.allocate(size);\n}\n\nvoid GuardPageAllocator::deallocate(unsigned char* limit, size_t size) {\n  if (!(stackCache_ && stackCache_->cache().giveBack(limit, size))) {\n    fallbackAllocator_.deallocate(limit, size);\n  }\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/GuardPageAllocator.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\nnamespace folly {\nnamespace fibers {\n\nclass StackCacheEntry;\n\n/**\n * Stack allocator that protects an extra memory page after\n * the end of the stack.\n * Will only add extra memory pages up to a certain number of allocations\n * to avoid creating too many memory maps for the process.\n */\nclass GuardPageAllocator {\n public:\n  /**\n   * @param guardPagesPerStack  Protect a small number of fiber stacks\n   *   with this many guard pages.  If 0, acts as std::allocator.\n   */\n  explicit GuardPageAllocator(size_t guardPagesPerStack);\n  ~GuardPageAllocator();\n\n  /**\n   * @return pointer to the bottom of the allocated stack of `size' bytes.\n   */\n  unsigned char* allocate(size_t size);\n\n  /**\n   * Deallocates the previous result of an `allocate(size)' call.\n   */\n  void deallocate(unsigned char* limit, size_t size);\n\n private:\n  std::unique_ptr<StackCacheEntry> stackCache_;\n  std::allocator<unsigned char> fallbackAllocator_;\n  size_t guardPagesPerStack_{0};\n};\n\n#ifndef _WIN32\n/**\n * Unconditionally install the SIGSEGV handler that detects fiber stack\n * overflow. Can be useful if the handler has been overridden by some other code\n * overriding the SIGSEGV handler\n */\nvoid installGuardPageSignalHandler();\n#endif\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/LoopController.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <functional>\n\n#include <folly/io/async/HHWheelTimer-fwd.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass Fiber;\nclass FiberManager;\n\nclass LoopController {\n public:\n  using Clock = std::chrono::steady_clock;\n  using TimePoint = std::chrono::time_point<Clock>;\n\n  virtual ~LoopController() {}\n\n  /**\n   * Called by FiberManager to associate itself with the LoopController.\n   */\n  virtual void setFiberManager(FiberManager*) = 0;\n\n  /**\n   * Called by FiberManager to schedule the loop function run\n   * at some point in the future.\n   */\n  virtual void schedule() = 0;\n\n  /**\n   * Run FiberManager loopUntilNoReadyImpl(). May have additional logic specific\n   * to a LoopController.\n   */\n  virtual void runLoop() = 0;\n\n  /**\n   * Run FiberManager runEagerFiberImpl(fiber). May have additional logic\n   * specific to a LoopController.\n   */\n  virtual void runEagerFiber(Fiber*) = 0;\n\n  /**\n   * Same as schedule(), but safe to call from any thread.\n   */\n  virtual void scheduleThreadSafe() = 0;\n\n  /**\n   * Used by FiberManager to schedule some function to be run at some time.\n   * May return null, but only if called outside of runLoop() call (e.g. if\n   * Executor backing the timer is already destroyed).\n   */\n  virtual HHWheelTimer* timer() = 0;\n\n  /**\n   * Should return true only if is the same thread that is (if called from\n   * within runLoop()) or will be calling runLoop() or a thread that is\n   * syncronized with the thread that will be calling runLoop() (i.e. the\n   * currently executing \"task\" has to be completed before runLoop() is called).\n   */\n  virtual bool isInLoopThread() = 0;\n};\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Promise-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/Baton.h>\n\nnamespace folly {\nnamespace fibers {\n\ntemplate <class T, class BatonT>\nPromise<T, BatonT>::Promise(folly::Try<T>& value, BatonT& baton)\n    : value_(&value), baton_(&baton) {}\n\ntemplate <class T, class BatonT>\nPromise<T, BatonT>::Promise(Promise&& other) noexcept\n    : value_(other.value_), baton_(other.baton_) {\n  other.value_ = nullptr;\n  other.baton_ = nullptr;\n}\n\ntemplate <class T, class BatonT>\nPromise<T, BatonT>& Promise<T, BatonT>::operator=(Promise&& other) {\n  std::swap(value_, other.value_);\n  std::swap(baton_, other.baton_);\n  return *this;\n}\n\ntemplate <class T, class BatonT>\nvoid Promise<T, BatonT>::throwIfFulfilled() const {\n  if (!value_) {\n    throw std::logic_error(\"promise already fulfilled\");\n  }\n}\n\ntemplate <class T, class BatonT>\nPromise<T, BatonT>::~Promise() {\n  if (value_) {\n    setException(\n        folly::make_exception_wrapper<std::logic_error>(\n            \"promise not fulfilled\"));\n  }\n}\n\ntemplate <class T, class BatonT>\nvoid Promise<T, BatonT>::setException(folly::exception_wrapper e) {\n  setTry(folly::Try<T>(e));\n}\n\ntemplate <class T, class BatonT>\nvoid Promise<T, BatonT>::setTry(folly::Try<T>&& t) {\n  throwIfFulfilled();\n\n  *value_ = std::move(t);\n  value_ = nullptr;\n\n  // Baton::post has to be the last step here, since if Promise is not owned by\n  // the posting thread, it may be destroyed right after Baton::post is called.\n  baton_->post();\n}\n\ntemplate <class T, class BatonT>\ntemplate <class M>\nvoid Promise<T, BatonT>::setValue(M&& v) {\n  static_assert(!std::is_same<T, void>::value, \"Use setValue() instead\");\n\n  setTry(folly::Try<T>(std::forward<M>(v)));\n}\n\ntemplate <class T, class BatonT>\nvoid Promise<T, BatonT>::setValue() {\n  static_assert(std::is_same<T, void>::value, \"Use setValue(value) instead\");\n\n  setTry(folly::Try<void>());\n}\n\ntemplate <class T, class BatonT>\ntemplate <class F>\nvoid Promise<T, BatonT>::setWith(F&& func) {\n  setTry(makeTryWith(std::forward<F>(func)));\n}\n\ntemplate <class T, class BatonT>\ntemplate <class F>\ntypename Promise<T, BatonT>::value_type Promise<T, BatonT>::await_async(\n    F&& func) {\n  folly::Try<value_type> result;\n  std::exception_ptr funcException;\n\n  BatonT baton;\n  baton.wait([&func, &result, &baton, &funcException]() mutable {\n    try {\n      func(Promise<value_type, BatonT>(result, baton));\n    } catch (...) {\n      // Save the exception, but still wait for baton to be posted by user code\n      // or promise destructor.\n      funcException = current_exception();\n    }\n  });\n\n  if (FOLLY_UNLIKELY(funcException != nullptr)) {\n    std::rethrow_exception(funcException);\n  }\n\n  return std::move(result).value();\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Promise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Try.h>\n#include <folly/fibers/traits.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass Baton;\n\ntemplate <typename T, typename BatonT = Baton>\nclass Promise {\n public:\n  using value_type = T;\n  using baton_type = BatonT;\n\n  ~Promise();\n\n  // not copyable\n  Promise(const Promise&) = delete;\n  Promise& operator=(const Promise&) = delete;\n\n  // movable\n  Promise(Promise&&) noexcept;\n  Promise& operator=(Promise&&);\n\n  /** Fulfill this promise (only for Promise<void>) */\n  void setValue();\n\n  /** Set the value (use perfect forwarding for both move and copy) */\n  template <class M>\n  void setValue(M&& value);\n\n  /**\n   * Fulfill the promise with a given try\n   *\n   * @param t A Try with either a value or an error.\n   */\n  void setTry(folly::Try<T>&& t);\n\n  /** Fulfill this promise with the result of a function that takes no\n    arguments and returns something implicitly convertible to T.\n    Captures exceptions. e.g.\n\n    p.setWith([] { do something that may throw; return a T; });\n  */\n  template <class F>\n  void setWith(F&& func);\n\n  /** Fulfill the Promise with an exception_wrapper, e.g.\n    auto ew = folly::try_and_catch([]{ ... });\n    if (ew) {\n      p.setException(std::move(ew));\n    }\n    */\n  void setException(folly::exception_wrapper);\n\n  /**\n   * Blocks task execution until given promise is fulfilled.\n   *\n   * Calls function passing in a Promise<T>, which has to be fulfilled.\n   *\n   * @return data which was used to fulfill the promise.\n   */\n  template <class F>\n  static value_type await_async(F&& func);\n\n#if !defined(_MSC_VER)\n  template <class F>\n  FOLLY_ERASE static value_type await(F&& func) {\n    return await_sync(static_cast<F&&>(func));\n  }\n#endif\n\n private:\n  Promise(folly::Try<T>& value, BatonT& baton);\n  folly::Try<T>* value_;\n  BatonT* baton_;\n\n  void throwIfFulfilled() const;\n\n  template <class F>\n  typename std::enable_if<\n      std::is_convertible<invoke_result_t<F>, T>::value &&\n      !std::is_same<T, void>::value>::type\n  fulfilHelper(F&& func);\n\n  template <class F>\n  typename std::enable_if<\n      std::is_same<invoke_result_t<F>, void>::value &&\n      std::is_same<T, void>::value>::type\n  fulfilHelper(F&& func);\n};\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/Promise-inl.h>\n"
  },
  {
    "path": "folly/fibers/README.md",
    "content": "<!-- This file is generated from internal wiki guide by folly/facebook/fibers-update-readme.sh. -->\n<section class=\"dex_guide\"><h1 class=\"dex_title\">folly::fibers</h1><section class=\"dex_document\"><h1></h1><p class=\"dex_introduction\">folly::fibers is an async C++ framework, which uses fibers for parallelism.</p><h2 id=\"overview\">Overview <a href=\"#overview\" class=\"headerLink\">#</a></h2>\n\n<p>Fibers (or coroutines) are lightweight application threads. Multiple fibers can be running on top of a single system thread. Unlike system threads, all the context switching between fibers is happening explicitly. Because of this every such context switch is very fast (~200 million of fiber context switches can be made per second on a single CPU core).</p>\n\n<p>folly::fibers implements a task manager (FiberManager), which executes scheduled tasks on fibers. It also provides some fiber-compatible synchronization primitives.</p>\n\n<h2 id=\"basic-example\">Basic example <a href=\"#basic-example\" class=\"headerLink\">#</a></h2>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">...</span>\n<span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"EventBase\">EventBase</span> <span class=\"no\">evb</span><span class=\"o\">;</span>\n<span class=\"no\">auto</span><span class=\"o\">&amp;</span> <span class=\"no\">fiberManager</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"getFiberManager\">getFiberManager</span><span class=\"o\">(</span><span class=\"no\">evb</span><span class=\"o\">);</span>\n<span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Baton\">Baton</span> <span class=\"no\">baton</span><span class=\"o\">;</span>\n\n<span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"cout\">cout</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">&quot;Task 1: start&quot;</span> <span class=\"o\">&lt;&lt;</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"endl\">endl</span><span class=\"o\">;</span>\n  <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"wait\">wait</span><span class=\"o\">();</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"cout\">cout</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">&quot;Task 1: after baton.wait()&quot;</span> <span class=\"o\">&lt;&lt;</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"endl\">endl</span><span class=\"o\">;</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"cout\">cout</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">&quot;Task 2: start&quot;</span> <span class=\"o\">&lt;&lt;</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"endl\">endl</span><span class=\"o\">;</span>\n  <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"post\">post</span><span class=\"o\">();</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"cout\">cout</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">&quot;Task 2: after baton.post()&quot;</span> <span class=\"o\">&lt;&lt;</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"endl\">endl</span><span class=\"o\">;</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">evb</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"loop\">loop</span><span class=\"o\">();</span>\n<span class=\"o\">...</span></pre></div>\n\n<p>This would print:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">Task</span> <span class=\"mi\">1</span><span class=\"o\">:</span> <span class=\"no\">start</span>\n<span class=\"no\">Task</span> <span class=\"mi\">2</span><span class=\"o\">:</span> <span class=\"no\">start</span>\n<span class=\"no\">Task</span> <span class=\"mi\">2</span><span class=\"o\">:</span> <span class=\"no\">after</span> <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"post\">post</span><span class=\"o\">()</span>\n<span class=\"no\">Task</span> <span class=\"mi\">1</span><span class=\"o\">:</span> <span class=\"no\">after</span> <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"wait\">wait</span><span class=\"o\">()</span></pre></div>\n\n<p>It&#039;s very important to note that both tasks in this example were executed on the same system thread. Task 1 was suspended by <tt>baton.wait()</tt> call. Task 2 then started and called <tt>baton.post()</tt>, resuming Task 1.</p>\n\n<h2 id=\"features\">Features <a href=\"#features\" class=\"headerLink\">#</a></h2>\n\n<ul>\n<li>Fibers creation and scheduling is performed by FiberManager</li>\n<li>Integration with any event-management system (e.g. EventBase)</li>\n<li>Low-level synchronization primitives (Baton) as well as higher-level primitives built on top of them (await, collectN, mutexes, ... )</li>\n<li>Synchronization primitives have timeout support</li>\n<li>Built-in mechanisms for fiber stack-overflow detection</li>\n<li>Optional fiber-local data (i.e. equivalent of thread locals)</li>\n</ul>\n\n<h2 id=\"non-features\">Non-features <a href=\"#non-features\" class=\"headerLink\">#</a></h2>\n\n<ul>\n<li>Individual fibers scheduling can&#039;t be directly controlled by application</li>\n<li>FiberManager is not thread-safe (we recommend to keep one FiberManager per thread). Application is responsible for managing its own threads and distributing load between them</li>\n<li>We don&#039;t support automatic stack size adjustments. Each fiber has a stack of fixed size.</li>\n</ul>\n\n<h2 id=\"why-would-i-not-want-to\">Why would I not want to use fibers ? <a href=\"#why-would-i-not-want-to\" class=\"headerLink\">#</a></h2>\n\n<p>The only real downside to using fibers is the need to keep a pre-allocated stack for every fiber being run. That either makes your application use a lot of memory (if you have many concurrent tasks and each of them uses large stacks) or creates a risk of stack overflow bugs (if you try to reduce the stack size).</p>\n\n<p>We believe these problems can be addressed (and we provide some tooling for that), as fibers library is used in many critical applications at Facebook (mcrouter, TAO, Service Router). However, it&#039;s important to be aware of the risks and be ready to deal with stack issues if you decide to use fibers library in your application.</p>\n\n<h2 id=\"what-are-the-alternative\">What are the alternatives ? <a href=\"#what-are-the-alternative\" class=\"headerLink\">#</a></h2>\n\n<ul>\n<li><a href=\"https://github.com/facebook/folly/blob/master/folly/futures/\" target=\"_blank\">Futures</a> library works great for asynchronous high-level application code. Yet code written using fibers library is generally much simpler and much more efficient (you are not paying the penalty of heap allocation).</li>\n<li>You can always keep writing your asynchronous code using traditional callback approach. Such code quickly becomes hard to manage and is error-prone. Even though callback approach seems to allow you writing the most efficient code, inefficiency still comes from heap allocations (<tt>std::function</tt>s used for callbacks, context objects to be passed between callbacks etc.)</li>\n</ul></section><section class=\"dex_document\"><h1>Quick guide</h1><p class=\"dex_introduction\"></p><p>Let&#039;s take a look at this basic example:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">...</span>\n<span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"EventBase\">EventBase</span> <span class=\"no\">evb</span><span class=\"o\">;</span>\n<span class=\"no\">auto</span><span class=\"o\">&amp;</span> <span class=\"no\">fiberManager</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"getFiberManager\">getFiberManager</span><span class=\"o\">(</span><span class=\"no\">evb</span><span class=\"o\">);</span>\n<span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Baton\">Baton</span> <span class=\"no\">baton</span><span class=\"o\">;</span>\n\n<span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"cout\">cout</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">&quot;Task: start&quot;</span> <span class=\"o\">&lt;&lt;</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"endl\">endl</span><span class=\"o\">;</span>\n  <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"wait\">wait</span><span class=\"o\">();</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"cout\">cout</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">&quot;Task: after baton.wait()&quot;</span> <span class=\"o\">&lt;&lt;</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"endl\">endl</span><span class=\"o\">;</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">evb</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"loop\">loop</span><span class=\"o\">();</span>\n\n<span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"post\">post</span><span class=\"o\">();</span>\n<span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"cout\">cout</span> <span class=\"o\">&lt;&lt;</span> <span class=\"s2\">&quot;Baton posted&quot;</span> <span class=\"o\">&lt;&lt;</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"endl\">endl</span><span class=\"o\">;</span>\n\n<span class=\"no\">evb</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"loop\">loop</span><span class=\"o\">();</span>\n\n<span class=\"o\">...</span></pre></div>\n\n<p>This would print:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">Task</span><span class=\"o\">:</span> <span class=\"no\">start</span>\n<span class=\"no\">Baton</span> <span class=\"no\">posted</span>\n<span class=\"no\">Task</span><span class=\"o\">:</span> <span class=\"no\">after</span> <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"wait\">wait</span><span class=\"o\">()</span></pre></div>\n\n<p>What makes fiber-task different from any other task run on e.g. an <tt>folly::EventBase</tt> is the ability to suspend such task, without blocking the system thread. So how do you suspend a fiber-task ?</p>\n\n<h3 id=\"fibers-baton\">fibers::Baton <a href=\"#fibers-baton\" class=\"headerLink\">#</a></h3>\n\n<p><tt>fibers::Baton</tt> is the core synchronization primitive which is used to suspend a fiber-task and notify when the task may be resumed. <tt>fibers::Baton</tt> supports two basic operations: <tt>wait()</tt> and <tt>post()</tt>. Calling <tt>wait()</tt> on a Baton will suspend current fiber-task until <tt>post()</tt> is called on the same Baton.</p>\n\n<p>Please refer to <a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/Baton.h\" target=\"_blank\">Baton</a> for more detailed documentation.</p>\n\n<div class=\"remarkup-note\"><span class=\"remarkup-note-word\">NOTE:</span> <tt>fibers::Baton</tt> is the only native synchronization primitive of folly::fibers library. All other synchronization primitives provided by folly::fibers are built on top of <tt>fibers::Baton</tt>.</div>\n\n<h3 id=\"integrating-with-other-a\">Integrating with other asynchronous APIs (callbacks) <a href=\"#integrating-with-other-a\" class=\"headerLink\">#</a></h3>\n\n<p>Let&#039;s say we have some existing library which provides a classic callback-style asynchronous API.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">void</span> <span class=\"nf\" data-symbol-name=\"asyncCall\">asyncCall</span><span class=\"o\">(</span><span class=\"no\">Request</span> <span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"Function\">Function</span><span class=\"o\">&lt;</span><span class=\"nf\" data-symbol-name=\"void\">void</span><span class=\"o\">(</span><span class=\"no\">Response</span><span class=\"o\">)&gt;</span> <span class=\"no\">cb</span><span class=\"o\">);</span></pre></div>\n\n<p>If we use folly::fibers we can just make an async call from a fiber-task and wait until callback is run:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"no\">Response</span> <span class=\"no\">response</span><span class=\"o\">;</span>\n  <span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"fibers\" data-symbol-name=\"Baton\">Baton</span> <span class=\"no\">baton</span><span class=\"o\">;</span>\n\n  <span class=\"nf\" data-symbol-name=\"asyncCall\">asyncCall</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"o\">[&amp;](</span><span class=\"no\">Response</span> <span class=\"no\">r</span><span class=\"o\">)</span> <span class=\"no\">mutable</span> <span class=\"o\">&#123;</span>\n     <span class=\"no\">response</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">r</span><span class=\"o\">);</span>\n     <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"post\">post</span><span class=\"o\">();</span>\n  <span class=\"o\">&#125;);</span>\n  <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"wait\">wait</span><span class=\"o\">();</span>\n\n  <span class=\"c\">// Now response holds response returned by the async call</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p>Using <tt>fibers::Baton</tt> directly is generally error-prone. To make the task above simpler, folly::fibers provide <tt>fibers::await</tt> function.</p>\n\n<p>With <tt>fibers::await</tt>, the code above transforms into:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"no\">auto</span> <span class=\"no\">response</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"fibers\" data-symbol-name=\"await\">await</span><span class=\"o\">([&amp;](</span><span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"fibers\" data-symbol-name=\"Promise\">Promise</span><span class=\"o\">&lt;</span><span class=\"no\">Response</span><span class=\"o\">&gt;</span> <span class=\"no\">promise</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n    <span class=\"nf\" data-symbol-name=\"asyncCall\">asyncCall</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"o\">[</span><span class=\"no\">promise</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">promise</span><span class=\"o\">)](</span><span class=\"no\">Response</span> <span class=\"no\">r</span><span class=\"o\">)</span> <span class=\"no\">mutable</span> <span class=\"o\">&#123;</span>\n      <span class=\"no\">promise</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"setValue\">setValue</span><span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">r</span><span class=\"o\">));</span>\n    <span class=\"o\">&#125;);</span>\n  <span class=\"o\">&#125;);</span>\n\n  <span class=\"c\">// Now response holds response returned by the async call</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p>Callback passed to <tt>fibers::await</tt> is executed immediately and then fiber-task is suspended until <tt>fibers::Promise</tt> is fulfilled. When <tt>fibers::Promise</tt> is fulfilled with a value or exception, fiber-task will be resumed and &#039;fibers::await&#039; returns that value (or throws an exception, if exception was used to fulfill the <tt>Promise</tt>).</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"k\">try</span> <span class=\"o\">&#123;</span>\n    <span class=\"no\">auto</span> <span class=\"no\">response</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"fibers\" data-symbol-name=\"await\">await</span><span class=\"o\">([&amp;](</span><span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"fibers\" data-symbol-name=\"Promise\">Promise</span><span class=\"o\">&lt;</span><span class=\"no\">Response</span><span class=\"o\">&gt;</span> <span class=\"no\">promise</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n      <span class=\"nf\" data-symbol-name=\"asyncCall\">asyncCall</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"o\">[</span><span class=\"no\">promise</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">promise</span><span class=\"o\">)](</span><span class=\"no\">Response</span> <span class=\"no\">r</span><span class=\"o\">)</span> <span class=\"no\">mutable</span> <span class=\"o\">&#123;</span>\n        <span class=\"no\">promise</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"setException\">setException</span><span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"runtime_error\">runtime_error</span><span class=\"o\">(</span><span class=\"s2\">&quot;Await will re-throw me&quot;</span><span class=\"o\">));</span>\n      <span class=\"o\">&#125;);</span>\n    <span class=\"o\">&#125;);</span>\n    <span class=\"nf\" data-symbol-name=\"assert\">assert</span><span class=\"o\">(</span><span class=\"kc\">false</span><span class=\"o\">);</span> <span class=\"c\">// We should never get here</span>\n  <span class=\"o\">&#125;</span> <span class=\"k\">catch</span> <span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"const\">const</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"exception\">exception</span><span class=\"o\">&amp;</span> <span class=\"no\">e</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n    <span class=\"nf\" data-symbol-name=\"assert\">assert</span><span class=\"o\">(</span><span class=\"no\">e</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"what\">what</span><span class=\"o\">()</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;Await will re-throw me&quot;</span><span class=\"o\">);</span>\n  <span class=\"o\">&#125;</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p>If <tt>fibers::Promise</tt> is not fulfilled, <tt>fibers::await</tt> will throw a <tt>std::logic_error</tt>.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"k\">try</span> <span class=\"o\">&#123;</span>\n    <span class=\"no\">auto</span> <span class=\"no\">response</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"fibers\" data-symbol-name=\"await\">await</span><span class=\"o\">([&amp;](</span><span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"fibers\" data-symbol-name=\"Promise\">Promise</span><span class=\"o\">&lt;</span><span class=\"no\">Response</span><span class=\"o\">&gt;</span> <span class=\"no\">promise</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n      <span class=\"c\">// We forget about the promise</span>\n    <span class=\"o\">&#125;);</span>\n    <span class=\"nf\" data-symbol-name=\"assert\">assert</span><span class=\"o\">(</span><span class=\"kc\">false</span><span class=\"o\">);</span> <span class=\"c\">// We should never get here</span>\n  <span class=\"o\">&#125;</span> <span class=\"k\">catch</span> <span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"const\">const</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"logic_error\">logic_error</span><span class=\"o\">&amp;</span> <span class=\"no\">e</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n    <span class=\"o\">...</span>\n  <span class=\"o\">&#125;</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p>Please refer to <a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/Promise.h\" target=\"_blank\">await</a> for more detailed documentation.</p>\n\n<div class=\"remarkup-note\"><span class=\"remarkup-note-word\">NOTE:</span> most of your code written with folly::fibers, won&#039;t be using <tt>fibers::Baton</tt> or <tt>fibers::await</tt>. These primitives should only be used to integrate with other asynchronous API which are not fibers-compatible.</div>\n\n<h3 id=\"integrating-with-other-a-1\">Integrating with other asynchronous APIs (folly::Future) <a href=\"#integrating-with-other-a-1\" class=\"headerLink\">#</a></h3>\n\n<p>Let&#039;s say we have some existing library which provides a Future-based asynchronous API.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"Future\">Future</span><span class=\"o\">&lt;</span><span class=\"no\">Response</span><span class=\"o\">&gt;</span> <span class=\"nf\" data-symbol-name=\"asyncCallFuture\">asyncCallFuture</span><span class=\"o\">(</span><span class=\"no\">Request</span> <span class=\"no\">request</span><span class=\"o\">);</span></pre></div>\n\n<p>The good news are, <tt>folly::Future</tt> is already fibers-compatible. You can simply write:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"no\">auto</span> <span class=\"no\">response</span> <span class=\"o\">=</span> <span class=\"nf\" data-symbol-name=\"asyncCallFuture\">asyncCallFuture</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">).</span><span class=\"nf\" data-symbol-name=\"get\">get</span><span class=\"o\">();</span>\n\n  <span class=\"c\">// Now response holds response returned by the async call</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p>Calling <tt>get()</tt> on a <tt>folly::Future</tt> object will only suspend the calling fiber-task. It won&#039;t block the system thread, letting it process other tasks.</p>\n\n<h2 id=\"writing-code-with-folly\">Writing code with folly::fibers <a href=\"#writing-code-with-folly\" class=\"headerLink\">#</a></h2>\n\n<h3 id=\"building-fibers-compatib\">Building fibers-compatible API <a href=\"#building-fibers-compatib\" class=\"headerLink\">#</a></h3>\n\n<p>Following the explanations above we may wrap an existing asynchronous API in a function:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">Response</span> <span class=\"nf\" data-symbol-name=\"fiberCall\">fiberCall</span><span class=\"o\">(</span><span class=\"no\">Request</span> <span class=\"no\">request</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n  <span class=\"k\">return</span> <span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"fibers\" data-symbol-name=\"await\">await</span><span class=\"o\">([&amp;](</span><span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"fibers\" data-symbol-name=\"Promise\">Promise</span><span class=\"o\">&lt;</span><span class=\"no\">Response</span><span class=\"o\">&gt;</span> <span class=\"no\">promise</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n    <span class=\"nf\" data-symbol-name=\"asyncCall\">asyncCall</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"o\">[</span><span class=\"no\">promise</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">promise</span><span class=\"o\">)](</span><span class=\"no\">Response</span> <span class=\"no\">r</span><span class=\"o\">)</span> <span class=\"no\">mutable</span> <span class=\"o\">&#123;</span>\n      <span class=\"no\">promise</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"setValue\">setValue</span><span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">r</span><span class=\"o\">));</span>\n    <span class=\"o\">&#125;);</span>\n  <span class=\"o\">&#125;);</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p>We can then call it from a fiber-task:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"no\">auto</span> <span class=\"no\">response</span> <span class=\"o\">=</span> <span class=\"nf\" data-symbol-name=\"fiberCall\">fiberCall</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">);</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;);</span></pre></div>\n\n<p>But what happens if we just call <tt>fiberCall</tt> not from within a fiber-task, but directly from a system thread ? Here another important feature of <tt>fibers::Baton</tt> (and thus all other folly::fibers synchronization primitives built on top of it) comes into play. Calling <tt>wait()</tt> on a <tt>fibers::Baton</tt> within a system thread context just blocks the thread until <tt>post()</tt> is called on the same <tt>folly::Baton</tt>.</p>\n\n<p>What this means is that you no longer need to write separate code for synchronous and asynchronous APIs. If you use only folly::fibers synchronization primitives for all blocking calls inside of your synchronous function, it automatically becomes asynchronous when run inside a fiber-task.</p>\n\n<h3 id=\"passing-by-reference\">Passing by reference <a href=\"#passing-by-reference\" class=\"headerLink\">#</a></h3>\n\n<p>Classic asynchronous APIs (same applies to folly::Future-based APIs) generally rely on copying/moving-in input arguments and often require you to copy/move in some context variables into the callback. E.g.:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">...</span>\n<span class=\"no\">Context</span> <span class=\"no\">context</span><span class=\"o\">;</span>\n\n<span class=\"nf\" data-symbol-name=\"asyncCall\">asyncCall</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"o\">[</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"no\">context</span><span class=\"o\">](</span><span class=\"no\">Response</span> <span class=\"no\">response</span><span class=\"o\">)</span> <span class=\"no\">mutable</span> <span class=\"o\">&#123;</span>\n  <span class=\"nf\" data-symbol-name=\"doSomething\">doSomething</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"no\">response</span><span class=\"o\">,</span> <span class=\"no\">context</span><span class=\"o\">);</span>\n<span class=\"o\">&#125;);</span>\n<span class=\"o\">...</span></pre></div>\n\n<p>Fibers-compatible APIs look more like synchronous APIs, so you can actually pass input arguments by reference and you don&#039;t have to think about passing context at all. E.g.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"no\">Context</span> <span class=\"no\">context</span><span class=\"o\">;</span>\n\n  <span class=\"no\">auto</span> <span class=\"no\">response</span> <span class=\"o\">=</span> <span class=\"nf\" data-symbol-name=\"fiberCall\">fiberCall</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">);</span>\n\n  <span class=\"nf\" data-symbol-name=\"doSomething\">doSomething</span><span class=\"o\">(</span><span class=\"no\">request</span><span class=\"o\">,</span> <span class=\"no\">response</span><span class=\"o\">,</span> <span class=\"no\">context</span><span class=\"o\">);</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;);</span></pre></div>\n\n<p>Same logic applies to <tt>fibers::await</tt>. Since <tt>fibers::await</tt> call blocks until promise is fulfilled, it&#039;s safe to pass everything by reference.</p>\n\n<h3 id=\"limited-stack-space\">Limited stack space <a href=\"#limited-stack-space\" class=\"headerLink\">#</a></h3>\n\n<p>So should you just run all the code inside a fiber-task ? No exactly.</p>\n\n<p>Similarly to system threads, every fiber-task has some stack space assigned to it. Stack usage goes up with the number of nested function calls and objects allocated on the stack. folly::fibers implementation only supports fiber-tasks with fixed stack size. If you want to have many fiber-tasks running concurrently - you need to reduce the amount of stack assigned to each fiber-task, otherwise you may run out of memory.</p>\n\n<div class=\"remarkup-important\"><span class=\"remarkup-note-word\">IMPORTANT:</span> If a fiber-task runs out of stack space (e.g. calls a function which does a lot of stack allocations) your program will fail.</div>\n\n<p>However if you know that some function never suspends a fiber-task, you can use <tt>fibers::runInMainContext</tt> to safely call it from a fiber-task, without any risk of running out of stack space of the fiber-task.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">Result</span> <span class=\"nf\" data-symbol-name=\"useALotOfStack\">useALotOfStack</span><span class=\"o\">()</span> <span class=\"o\">&#123;</span>\n  <span class=\"no\">char</span> <span class=\"no\">buffer</span><span class=\"o\">[</span><span class=\"mi\">1024</span><span class=\"o\">*</span><span class=\"mi\">1024</span><span class=\"o\">];</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;</span>\n\n<span class=\"o\">...</span>\n<span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"no\">auto</span> <span class=\"no\">result</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"fibers\" data-symbol-name=\"runInMainContext\">runInMainContext</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n    <span class=\"k\">return</span> <span class=\"nf\" data-symbol-name=\"useALotOfStack\">useALotOfStack</span><span class=\"o\">();</span>\n  <span class=\"o\">&#125;);</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;);</span>\n<span class=\"o\">...</span></pre></div>\n\n<p><tt>fibers::runInMainContext</tt> will switch to the stack of the system thread (main context), run the functor passed to it and then switch back to the fiber-task stack.</p>\n\n<div class=\"remarkup-important\"><span class=\"remarkup-note-word\">IMPORTANT:</span> Make sure you don&#039;t do any blocking calls on main context though. It will suspend the whole system thread, not just the fiber-task which was running.</div>\n\n<p>Remember that it&#039;s fine to use <tt>fibers::runInMainContext</tt> in general purpose functions (those which may be called both from fiber-task and non from fiber-task). When called in non-fiber-task context <tt>fibers::runInMainContext</tt> would simply execute passed functor right away.</p>\n\n<div class=\"remarkup-note\"><span class=\"remarkup-note-word\">NOTE:</span> Besides <tt>fibers::runInMainContext</tt> some other functions in folly::fibers are also executing some of the passed functors on the main context. E.g. functor passes to <tt>fibers::await</tt> is executed on main context, finally-functor passed to <tt>FiberManager::addTaskFinally</tt> is also executed on main context etc. Relying on this can help you avoid extra <tt>fibers::runInMainContext</tt> calls (and avoid extra context switches).</div>\n\n<h3 id=\"using-locks\">Using locks <a href=\"#using-locks\" class=\"headerLink\">#</a></h3>\n\n<p>Consider the following example:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">...</span>\n<span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"EventBase\">EventBase</span> <span class=\"no\">evb</span><span class=\"o\">;</span>\n<span class=\"no\">auto</span><span class=\"o\">&amp;</span> <span class=\"no\">fiberManager</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"getFiberManager\">getFiberManager</span><span class=\"o\">(</span><span class=\"no\">evb</span><span class=\"o\">);</span>\n<span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"mutex\">mutex</span> <span class=\"no\">lock</span><span class=\"o\">;</span>\n<span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Baton\">Baton</span> <span class=\"no\">baton</span><span class=\"o\">;</span>\n\n<span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"lock_guard\">lock_guard</span><span class=\"o\">&lt;</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"mutex\">mutex</span><span class=\"o\">&gt;</span> <span class=\"nf\" data-symbol-name=\"lg\">lg</span><span class=\"o\">(</span><span class=\"no\">lock</span><span class=\"o\">);</span>\n  <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"wait\">wait</span><span class=\"o\">();</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"lock_guard\">lock_guard</span><span class=\"o\">&lt;</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"mutex\">mutex</span><span class=\"o\">&gt;</span> <span class=\"nf\" data-symbol-name=\"lg\">lg</span><span class=\"o\">(</span><span class=\"no\">lock</span><span class=\"o\">);</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">evb</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"loop\">loop</span><span class=\"o\">();</span>\n<span class=\"c\">// We won&#039;t get here :(</span>\n<span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"post\">post</span><span class=\"o\">();</span>\n<span class=\"o\">...</span></pre></div>\n\n<p>First fiber-task will grab a lock and then suspend waiting on a <tt>fibers::Baton</tt>. Then second fiber-task will be run and it will try to grab a lock. Unlike system threads, fiber-task can be only suspended explicitly, so the whole system thread will be blocked waiting on the lock, and we end up with a dead-lock.</p>\n\n<p>There&#039;re generally two ways we can solve this problem. Ideally we would re-design the program to never not hold any locks when fiber-task is suspended. However if we are absolutely sure we need that lock - folly::fibers library provides some fiber-task-aware lock implementations (e.g.\n<a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/TimedMutex.h\" target=\"_blank\">TimedMutex</a>).</p></section><section class=\"dex_document\"><h1>APIs</h1><p class=\"dex_introduction\"></p><h2 id=\"fibers-baton\">fibers::Baton <a href=\"#fibers-baton\" class=\"headerLink\">#</a></h2>\n\n<p>All of the features of folly::fibers library are actually built on top a single synchronization primitive called Baton. <tt>fibers::Baton</tt> is a fiber-specific version of <tt>folly::Baton</tt>. It only  supports two basic operations: <tt>wait()</tt> and <tt>post()</tt>. Whenever <tt>wait()</tt> is called on the Baton, the current thread or fiber-task is suspended, until <tt>post()</tt> is called on the same Baton. <tt>wait()</tt> does not suspend the thread or fiber-task if <tt>post()</tt> was already called on the Baton. Please refer to <a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/Baton.h\" target=\"_blank\">Baton</a> for more detailed documentation.</p>\n\n<p>Baton is thread-safe, so <tt>wait()</tt> and <tt>post()</tt> can be (and should be :) ) called from different threads or fiber-tasks.</p>\n\n<div class=\"remarkup-note\"><span class=\"remarkup-note-word\">NOTE:</span> Because Baton transparently works both on threads and fiber-tasks, any synchronization primitive built using it would have the same property. This means that any library with a synchronous API, which uses only <tt>fibers::Baton</tt> for synchronization, becomes asynchronous when used in fiber context.</div>\n\n<h3 id=\"timed-wait\">timed_wait() <a href=\"#timed-wait\" class=\"headerLink\">#</a></h3>\n\n<p><tt>fibers::Baton</tt> also supports wait with timeout.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([=]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"no\">auto</span> <span class=\"no\">baton</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"make_shared\">make_shared</span><span class=\"o\">&lt;</span><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Baton\">Baton</span><span class=\"o\">&gt;();</span>\n  <span class=\"no\">auto</span> <span class=\"no\">result</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"make_shared\">make_shared</span><span class=\"o\">&lt;</span><span class=\"no\">Result</span><span class=\"o\">&gt;();</span>\n\n  <span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([=]()</span> <span class=\"o\">&#123;</span>\n    <span class=\"o\">*</span><span class=\"no\">result</span> <span class=\"o\">=</span> <span class=\"nf\" data-symbol-name=\"sendRequest\">sendRequest</span><span class=\"o\">(...);</span>\n    <span class=\"no\">baton</span><span class=\"o\">-&gt;</span><span class=\"na\" data-symbol-name=\"post\">post</span><span class=\"o\">();</span>\n  <span class=\"o\">&#125;);</span>\n\n  <span class=\"no\">bool</span> <span class=\"no\">success</span> <span class=\"o\">=</span> <span class=\"no\">baton</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"timed_wait\">timed_wait</span><span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"chrono\">chrono</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"milliseconds\">milliseconds</span><span class=\"o\">&#123;</span><span class=\"mi\">10</span><span class=\"o\">&#125;);</span>\n  <span class=\"k\">if</span> <span class=\"o\">(</span><span class=\"no\">success</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n    <span class=\"c\">// request successful</span>\n    <span class=\"o\">...</span>\n  <span class=\"o\">&#125;</span> <span class=\"k\">else</span> <span class=\"o\">&#123;</span>\n    <span class=\"c\">// handle timeout</span>\n    <span class=\"o\">...</span>\n  <span class=\"o\">&#125;</span>\n<span class=\"o\">&#125;);</span></pre></div>\n\n<div class=\"remarkup-important\"><span class=\"remarkup-note-word\">IMPORTANT:</span> unlike <tt>wait()</tt> when using <tt>timed_wait()</tt> API it&#039;s generally not safe to pass <tt>fibers::Baton</tt> by reference. You have to make sure that task, which fulfills the Baton is either cancelled in case of timeout, or have shared ownership for the Baton.</div>\n\n<h2 id=\"task-creation\">Task creation <a href=\"#task-creation\" class=\"headerLink\">#</a></h2>\n\n<h3 id=\"addtask-addtaskremote\">addTask() / addTaskRemote() <a href=\"#addtask-addtaskremote\" class=\"headerLink\">#</a></h3>\n\n<p>As you could see from previous examples, the easiest way to create a new fiber-task is to call <tt>addTask()</tt>:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;);</span></pre></div>\n\n<p>It is important to remember that <tt>addTask()</tt> is not thread-safe. I.e. it can only be safely called from the thread, which is running the <tt>folly::FiberManager</tt> loop.</p>\n\n<p>If you need to create a fiber-task from a different thread, you have to use <tt>addTaskRemote()</tt>:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"EventBase\">EventBase</span> <span class=\"no\">evb</span><span class=\"o\">;</span>\n<span class=\"no\">auto</span><span class=\"o\">&amp;</span> <span class=\"no\">fiberManager</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"getFiberManager\">getFiberManager</span><span class=\"o\">(</span><span class=\"no\">evb</span><span class=\"o\">);</span>\n\n<span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"thread\">thread</span> <span class=\"nf\" data-symbol-name=\"t\">t</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTaskRemote\">addTaskRemote</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n    <span class=\"o\">...</span>\n  <span class=\"o\">&#125;);</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">evb</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"loopForever\">loopForever</span><span class=\"o\">();</span></pre></div>\n\n<h3 id=\"addtaskfinally\">addTaskFinally() <a href=\"#addtaskfinally\" class=\"headerLink\">#</a></h3>\n\n<p><tt>addTaskFinally()</tt> is useful when you need to run some code on the main context in the end of a fiber-task.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTaskFinally\">addTaskFinally</span><span class=\"o\">(</span>\n  <span class=\"o\">[=]()</span> <span class=\"o\">&#123;</span>\n    <span class=\"o\">...</span>\n    <span class=\"k\">return</span> <span class=\"no\">result</span><span class=\"o\">;</span>\n  <span class=\"o\">&#125;,</span>\n  <span class=\"o\">[=](</span><span class=\"no\">Result</span><span class=\"o\">&amp;&amp;</span> <span class=\"no\">result</span><span class=\"o\">)</span> <span class=\"o\">&#123;</span>\n    <span class=\"nf\" data-symbol-name=\"callUserCallbacks\">callUserCallbacks</span><span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">result</span><span class=\"o\">),</span> <span class=\"o\">...)</span>\n  <span class=\"o\">&#125;</span>\n<span class=\"o\">);</span></pre></div>\n\n<p>Of course you could achieve the same by calling <tt>fibers::runInMainContext()</tt>, but <tt>addTaskFinally()</tt> reduces the number of fiber context switches:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([=]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n  <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"runInMainContext\">runInMainContext</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n    <span class=\"c\">// Switched to main context</span>\n    <span class=\"nf\" data-symbol-name=\"callUserCallbacks\">callUserCallbacks</span><span class=\"o\">(</span><span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"nf\" data-symbol-context=\"std\" data-symbol-name=\"move\">move</span><span class=\"o\">(</span><span class=\"no\">result</span><span class=\"o\">),</span> <span class=\"o\">...)</span>\n  <span class=\"o\">&#125;</span>\n  <span class=\"c\">// Switched back to fiber context</span>\n\n  <span class=\"c\">// On fiber context we realize there&#039;s no more work to be done.</span>\n  <span class=\"c\">// Fiber-task is complete, switching back to main context.</span>\n<span class=\"o\">&#125;);</span></pre></div>\n\n<p></p>\n\n<h3 id=\"addtaskfuture-addtaskrem\">addTaskFuture() / addTaskRemoteFuture() <a href=\"#addtaskfuture-addtaskrem\" class=\"headerLink\">#</a></h3>\n\n<p><tt>addTask()</tt> and <tt>addTaskRemote()</tt> are creating detached fiber-tasks. If you need to know when fiber-task is complete and/or have some return value for it -  <tt>addTaskFuture()</tt> / <tt>addTaskRemoteFuture()</tt> can be used.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"EventBase\">EventBase</span> <span class=\"no\">evb</span><span class=\"o\">;</span>\n<span class=\"no\">auto</span><span class=\"o\">&amp;</span> <span class=\"no\">fiberManager</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"getFiberManager\">getFiberManager</span><span class=\"o\">(</span><span class=\"no\">evb</span><span class=\"o\">);</span>\n\n<span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"thread\">thread</span> <span class=\"nf\" data-symbol-name=\"t\">t</span><span class=\"o\">([&amp;]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"no\">auto</span> <span class=\"no\">future1</span> <span class=\"o\">=</span> <span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTaskRemoteFuture\">addTaskRemoteFuture</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n    <span class=\"o\">...</span>\n  <span class=\"o\">&#125;);</span>\n  <span class=\"no\">auto</span> <span class=\"no\">future2</span> <span class=\"o\">=</span> <span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTaskRemoteFuture\">addTaskRemoteFuture</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n    <span class=\"o\">...</span>\n  <span class=\"o\">&#125;);</span>\n\n  <span class=\"no\">auto</span> <span class=\"no\">result1</span> <span class=\"o\">=</span> <span class=\"no\">future1</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"get\">get</span><span class=\"o\">();</span>\n  <span class=\"no\">auto</span> <span class=\"no\">result2</span> <span class=\"o\">=</span> <span class=\"no\">future2</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"get\">get</span><span class=\"o\">();</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">evb</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"loopForever\">loopForever</span><span class=\"o\">();</span></pre></div>\n\n<h2 id=\"other-synchronization-pr\">Other synchronization primitives <a href=\"#other-synchronization-pr\" class=\"headerLink\">#</a></h2>\n\n<p>All the listed synchronization primitives are built using <tt>fiber::Baton</tt>. Please check their source code for detailed documentation.</p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/Promise.h\" target=\"_blank\">await</a></p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/WhenN.h\" target=\"_blank\">collectN</a></p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/WhenN.h\" target=\"_blank\">collectAll</a></p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/WhenN.h\" target=\"_blank\">collectAny</a></p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/ForEach.h\" target=\"_blank\">forEach</a></p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/AddTasks.h\" target=\"_blank\">addTasks</a></p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/TimedMutex.h\" target=\"_blank\">TimedMutex</a></p>\n\n<p><a href=\"https://github.com/facebook/folly/blob/master/folly/fibers/TimedMutex.h\" target=\"_blank\">TimedRWMutex</a></p></section><section class=\"dex_document\"><h1>Fiber stacks</h1><p class=\"dex_introduction\"></p><p>Similarly to system threads, every fiber-task has some stack space assigned to it. Stack usage goes up with the number of nested function calls and objects allocated on the stack. folly::fibers implementation only supports fiber-tasks with fixed stack size. If you want to have many fiber-tasks running concurrently - you need to reduce the amount of stack assigned to each fiber-task, otherwise you may run out of memory.</p>\n\n<h3 id=\"selecting-stack-size\">Selecting stack size <a href=\"#selecting-stack-size\" class=\"headerLink\">#</a></h3>\n\n<p>Stack size used for every fiber-task is part of FiberManager configuration. But how do you pick the right stack size ?</p>\n\n<p>First of all you need to figure out the maximum number of concurrent fiber-tasks your application may have. E.g. if you are writing a Thrift-service you will probably have a single fiber-task for every request in-fly (but remember that e.g. <tt>fibers::collectAll</tt> and some other synchronization primitives may create extra fiber-tasks). It&#039;s very important to get that number first, because if you will at most need 100 concurrent fiber-tasks, even 1MB stacks will result in at most 100MB used for fiber stacks. On the other hand if you need to have 100,000 concurrent fiber-tasks, even 16KB stacks will result in 1.6GB peak memory usage just for fiber stacks.</p>\n\n<p>folly::fibers also supports recording stack usage (it can be enabled via <tt>recordStackEvery</tt> option of <tt>FiberManager</tt>). When enabled, the stack of each fiber-task will be filled with magic values. Later linear search can be performed to find the boundary of unused stack space.</p>\n\n<h3 id=\"stack-overflow-detection\">Stack overflow detection <a href=\"#stack-overflow-detection\" class=\"headerLink\">#</a></h3>\n\n<p>By default every fiber-task stack is allocated with a special guard page next to it (this can be controlled via <tt>guardPagesPerStack</tt> option of <tt>FiberManager</tt>). If a stack overflow happens - this guard page will be accessed, which will result in immediate segmentation fault.</p>\n\n<div class=\"remarkup-important\"><span class=\"remarkup-note-word\">IMPORTANT:</span> disabling guard page protection may result in unnoticed stack overflows. Those will inevitably cause memory corruptions, which are usually very hard to debug.</div></section><section class=\"dex_document\"><h1>Event Loops</h1><p class=\"dex_introduction\"></p><p>folly::fibers library doesn&#039;t implement its own event system. Instead it allows <tt>fibers::FiberManager</tt> to work with any other event system by implementing <tt>fibers::LoopController</tt> interface.</p>\n\n<h2 id=\"folly-eventbase-integrat\">folly::EventBase integration <a href=\"#folly-eventbase-integrat\" class=\"headerLink\">#</a></h2>\n\n<p>The easiest way to create a <tt>fibers::FiberManager</tt> attached to a <tt>folly::EventBase</tt> is by using <tt>fibers::getFiberManager</tt> function:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"EventBase\">EventBase</span> <span class=\"no\">evb</span><span class=\"o\">;</span>\n<span class=\"no\">auto</span><span class=\"o\">&amp;</span> <span class=\"no\">fiberManager</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"getFiberManager\">getFiberManager</span><span class=\"o\">(</span><span class=\"no\">evb</span><span class=\"o\">);</span>\n\n<span class=\"no\">fiberManager</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"addTask\">addTask</span><span class=\"o\">([]()</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">...</span>\n<span class=\"o\">&#125;);</span>\n\n<span class=\"no\">evb</span><span class=\"o\">.</span><span class=\"nf\" data-symbol-name=\"loop\">loop</span><span class=\"o\">();</span></pre></div>\n\n<p>Such <tt>fibers::FiberManager</tt> will be automatically destroyed, when <tt>folly::EventBase</tt> is destroyed.</p>\n\n<div class=\"remarkup-note\"><span class=\"remarkup-note-word\">NOTE:</span> folly::fibers doesn&#039;t support killing fiber-tasks in-flight (for similar reasons you can&#039;t kill a thread). If <tt>fibers::FiberManager</tt> has any outstanding fiber-tasks, when <tt>folly::EventBase</tt> is being destroyed, it will keep running the event loop until all those tasks are finished.</div></section><section class=\"dex_document\"><h1>GDB integration</h1><p class=\"dex_introduction\"></p><p>folly::fibers provides some GDB extensions which can be very useful for debugging. To load them simply run the following in GDB console:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"no\">fbload</span> <span class=\"no\">folly_fibers</span></pre></div>\n\n<h3 id=\"find-all-fibermanagers\">Find all FiberManagers <a href=\"#find-all-fibermanagers\" class=\"headerLink\">#</a></h3>\n\n<p>You can use <tt>$get_fiber_manager_map_evb()</tt> and <tt>$get_fiber_manager_map_vevb()</tt> to get <tt>folly::EventBase</tt> =&gt; <tt>fibers::FiberManager</tt> and <tt>folly::VirtualEventBase</tt> =&gt; <tt>fibers::FiberManager</tt> mappings respectively:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">fbload</span> <span class=\"nf\" data-symbol-name=\"stl\">stl</span>\n<span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">p</span> <span class=\"nv\">$get_fiber_manager_map_evb</span><span class=\"o\">()</span>\n$<span class=\"mi\">2</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"unordered_map\">unordered_map</span> <span class=\"no\">with</span> <span class=\"mi\">2</span> <span class=\"no\">elements</span> <span class=\"o\">=</span> <span class=\"o\">&#123;</span>\n  <span class=\"o\">[</span><span class=\"mh\">0x7fffffffda80</span><span class=\"o\">]</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"unique_ptr\">unique_ptr</span><span class=\"o\">&lt;</span><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"FiberManager\">FiberManager</span><span class=\"o\">&gt;</span> <span class=\"no\">containing</span> <span class=\"mh\">0x7ffff5c22a00</span><span class=\"o\">,</span>\n  <span class=\"o\">[</span><span class=\"mh\">0x7fffffffd850</span><span class=\"o\">]</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"std\">std</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"std\" data-symbol-name=\"unique_ptr\">unique_ptr</span><span class=\"o\">&lt;</span><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"FiberManager\">FiberManager</span><span class=\"o\">&gt;</span> <span class=\"no\">containing</span> <span class=\"mh\">0x7ffff5c22800</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p>This will only list <tt>fibers::FiberManager</tt>s created using <tt>fibers::getFiberManager()</tt> function.</p>\n\n<h3 id=\"printing-a-fibermanager\">Printing a FiberManager <a href=\"#printing-a-fibermanager\" class=\"headerLink\">#</a></h3>\n\n<p>Given a pointer to a <tt>fibers::FiberManager</tt> you can get a list of all its active fibers:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">p</span> <span class=\"o\">*((</span><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"FiberManager\">FiberManager</span><span class=\"o\">*)</span><span class=\"mh\">0x7ffff5c22800</span><span class=\"o\">)</span>\n$<span class=\"mi\">4</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"FiberManager\">FiberManager</span> <span class=\"o\">=</span> <span class=\"o\">&#123;</span>\n  <span class=\"mh\">0x7ffff5d23380</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Fiber\">Fiber</span> <span class=\"o\">=</span> <span class=\"o\">&#123;</span>\n    <span class=\"no\">state</span> <span class=\"o\">=</span> <span class=\"no\">Awaiting</span> <span class=\"no\">immediate</span><span class=\"o\">,</span>\n    <span class=\"no\">backtrace</span> <span class=\"no\">available</span> <span class=\"o\">=</span> <span class=\"kc\">true</span>\n  <span class=\"o\">&#125;</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<p><tt>fiber-print-limit</tt> command can be used to change the maximum number of fibers printed for a <tt>fibers::FiberManager</tt> (default value is 100).</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">fiber</span><span class=\"o\">-</span><span class=\"no\">print</span><span class=\"o\">-</span><span class=\"no\">limit</span> <span class=\"mi\">10</span>\n<span class=\"k\">New</span> <span class=\"nc\" data-symbol-name=\"fiber\">fiber</span> <span class=\"no\">limit</span> <span class=\"k\">for</span> <span class=\"no\">FiberManager</span> <span class=\"no\">printer</span> <span class=\"no\">set</span> <span class=\"no\">to</span> <span class=\"mi\">10</span></pre></div>\n\n<h3 id=\"printing-a-fiber-task\">Printing a fiber-task <a href=\"#printing-a-fiber-task\" class=\"headerLink\">#</a></h3>\n\n<p>Given a pointer to a <tt>fibers::Fiber</tt>, which is running some fiber-task, you can get its current state:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">p</span> <span class=\"o\">*((</span><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Fiber\">Fiber</span><span class=\"o\">*)</span><span class=\"mh\">0x7ffff5d23380</span><span class=\"o\">)</span>\n$<span class=\"mi\">5</span> <span class=\"o\">=</span> <span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Fiber\">Fiber</span> <span class=\"o\">=</span> <span class=\"o\">&#123;</span>\n  <span class=\"no\">state</span> <span class=\"o\">=</span> <span class=\"no\">Awaiting</span> <span class=\"no\">immediate</span><span class=\"o\">,</span>\n  <span class=\"no\">backtrace</span> <span class=\"no\">available</span> <span class=\"o\">=</span> <span class=\"kc\">true</span>\n<span class=\"o\">&#125;</span></pre></div>\n\n<h3 id=\"activating-a-fiber-task\">Activating a fiber-task <a href=\"#activating-a-fiber-task\" class=\"headerLink\">#</a></h3>\n\n<p>Every <tt>fibers::Fiber</tt>, which is suspended (and so has its backtrace available), can be activated. To activate a fiber-task you can either use <tt>fiber</tt> GDB command, passing a <tt>fibers::Fiber</tt> pointer to it:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">fiber</span> <span class=\"mh\">0x7ffff5d23380</span>\n<span class=\"no\">Fiber</span> <span class=\"mi\">140737317581696</span> <span class=\"no\">activated</span><span class=\"o\">.</span> <span class=\"no\">You</span> <span class=\"no\">can</span> <span class=\"no\">call</span> <span class=\"s1\">&#039;bt&#039;</span> <span class=\"no\">now</span><span class=\"o\">.</span></pre></div>\n\n<p>or simply call <tt>activate()</tt> on a <tt>fibers::Fiber</tt> object:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"nf\" data-symbol-name=\"p\">p</span> <span class=\"o\">((</span><span class=\"nc\" data-symbol-name=\"folly\">folly</span><span class=\"o\">::</span><span class=\"na\" data-symbol-context=\"folly\" data-symbol-name=\"fibers\">fibers</span><span class=\"o\">::</span><span class=\"na\" data-symbol-name=\"Fiber\">Fiber</span><span class=\"o\">*)</span><span class=\"mh\">0x7ffff5d23380</span><span class=\"o\">)-&gt;</span><span class=\"na\" data-symbol-name=\"activate\">activate</span><span class=\"o\">()</span>\n$<span class=\"mi\">6</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;Fiber 0x7ffff5d23380 activated. You can call &#039;bt&#039; now.&quot;</span></pre></div>\n\n<p>Once fiber-task is activated you can explore its stack using <tt>bt</tt> and <tt>frame</tt> commands, just like a regular thread.</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">bt</span>\n<span class=\"c\">#1  0x00000000005497e9 in folly::fibers::FiberImpl::deactivate() (this=0x7ffff5d233a0)</span>\n    <span class=\"no\">at</span> <span class=\"no\">buck</span><span class=\"o\">-</span><span class=\"no\">out</span><span class=\"o\">/</span><span class=\"no\">dbg</span><span class=\"o\">/</span><span class=\"no\">gen</span><span class=\"o\">/</span><span class=\"no\">folly</span><span class=\"o\">/</span><span class=\"no\">fibers</span><span class=\"o\">/</span><span class=\"no\">fibers_core</span><span class=\"c\">#default,headers/folly/fibers/BoostContextCompatibility.h:105</span>\n<span class=\"c\">#2  0x000000000054996d in folly::fibers::FiberManager::deactivateFiber(folly::fibers::Fiber*) (this=0x7ffff5c22800, fiber=0x7ffff5d23380)</span>\n    <span class=\"no\">at</span> <span class=\"no\">buck</span><span class=\"o\">-</span><span class=\"no\">out</span><span class=\"o\">/</span><span class=\"no\">dbg</span><span class=\"o\">/</span><span class=\"no\">gen</span><span class=\"o\">/</span><span class=\"no\">folly</span><span class=\"o\">/</span><span class=\"no\">fibers</span><span class=\"o\">/</span><span class=\"no\">fibers_core</span><span class=\"c\">#default,headers/folly/fibers/FiberManagerInternal-inl.h:103</span>\n<span class=\"c\">#3  0x0000000000548b91 in folly::fibers::Fiber::&lt;lambda()&gt;::operator()(void) (__closure=0x7ffff59ffb20) at folly/fibers/Fiber.cpp:175</span>\n<span class=\"c\">#4  0x0000000000548d78 in folly::fibers::Fiber::preempt(folly::fibers::Fiber::State) (this=0x7ffff5d23380, state=folly::fibers::Fiber::AWAITING_IMMEDIATE)</span>\n    <span class=\"no\">at</span> <span class=\"no\">folly</span><span class=\"o\">/</span><span class=\"no\">fibers</span><span class=\"o\">/</span><span class=\"no\">Fiber</span><span class=\"o\">.</span><span class=\"no\">cpp</span><span class=\"o\">:</span><span class=\"mi\">185</span>\n<span class=\"c\">#5  0x000000000043bcc6 in folly::fibers::FiberManager::runInMainContext&lt;FiberManager_nestedFiberManagers_Test::TestBody()::&lt;lambda()&gt;::&lt;lambda()&gt; &gt;(&lt;unknown type in /mnt/fio0/andrii/fbsource/fbcode/buck-out/dbg/gen/folly/fibers/test/fibers_test, CU 0x31b, DIE 0x111bdb&gt;) (this=0x7ffff5c22800, func=&lt;unknown type in   /mnt/fio0/andrii/fbsource/fbcode/buck-out/dbg/gen/folly/fibers/test/fibers_test, CU 0x31b, DIE 0x111bdb&gt;)</span>\n    <span class=\"no\">at</span> <span class=\"no\">buck</span><span class=\"o\">-</span><span class=\"no\">out</span><span class=\"o\">/</span><span class=\"no\">dbg</span><span class=\"o\">/</span><span class=\"no\">gen</span><span class=\"o\">/</span><span class=\"no\">folly</span><span class=\"o\">/</span><span class=\"no\">fibers</span><span class=\"o\">/</span><span class=\"no\">fibers_core</span><span class=\"c\">#default,headers/folly/fibers/FiberManagerInternal-inl.h:459</span>\n<span class=\"c\">#6  0x00000000004300f3 in folly::fibers::runInMainContext&lt;FiberManager_nestedFiberManagers_Test::TestBody()::&lt;lambda()&gt;::&lt;lambda()&gt; &gt;(&lt;unknown type in /mnt/fio0/andrii/fbsource/fbcode/buck-out/dbg/gen/folly/fibers/test/fibers_test, CU 0x31b, DIE 0xf7caa&gt;) (func=&lt;unknown type in   /mnt/fio0/andrii/fbsource/fbcode/buck-out/dbg/gen/folly/fibers/test/fibers_test, CU 0x31b, DIE 0xf7caa&gt;)</span>\n    <span class=\"no\">at</span> <span class=\"no\">buck</span><span class=\"o\">-</span><span class=\"no\">out</span><span class=\"o\">/</span><span class=\"no\">dbg</span><span class=\"o\">/</span><span class=\"no\">gen</span><span class=\"o\">/</span><span class=\"no\">folly</span><span class=\"o\">/</span><span class=\"no\">fibers</span><span class=\"o\">/</span><span class=\"no\">fibers_core</span><span class=\"c\">#default,headers/folly/fibers/FiberManagerInternal.h:551</span>\n<span class=\"c\">#7  0x0000000000422101 in FiberManager_nestedFiberManagers_Test::&lt;lambda()&gt;::operator()(void) const (__closure=0x7ffff5d23450)</span>\n    <span class=\"no\">at</span> <span class=\"no\">folly</span><span class=\"o\">/</span><span class=\"no\">fibers</span><span class=\"o\">/</span><span class=\"no\">test</span><span class=\"o\">/</span><span class=\"no\">FibersTest</span><span class=\"o\">.</span><span class=\"no\">cpp</span><span class=\"o\">:</span><span class=\"mi\">1537</span>\n<span class=\"o\">...</span></pre></div>\n\n<p>To deactivate previously activated fiber-task and switch back to the stack of current thread simply use <tt>fiber-deactivate</tt> GDB command:</p>\n\n<div class=\"remarkup-code-block\" data-code-lang=\"php\"><pre class=\"remarkup-code\"><span class=\"o\">(</span><span class=\"no\">gdb</span><span class=\"o\">)</span> <span class=\"no\">fiber</span><span class=\"o\">-</span><span class=\"no\">deactivate</span>\n<span class=\"no\">Fiber</span> <span class=\"no\">de</span><span class=\"o\">-</span><span class=\"no\">activated</span><span class=\"o\">.</span></pre></div>\n\n<div class=\"remarkup-note\"><span class=\"remarkup-note-word\">NOTE:</span> For running (i.e. not suspended) fiber-task, you can simply switch to the system thread which owns it and use regular GDB commands for debugging.</div></section></section>\n"
  },
  {
    "path": "folly/fibers/Semaphore.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/Semaphore.h>\n\nnamespace folly {\nnamespace fibers {\n\nbool Semaphore::signalSlow() {\n  Waiter* waiter = nullptr;\n  {\n    // If we signalled a release, notify the waitlist\n    auto waitListLock = waitList_.wlock();\n    auto& waitList = *waitListLock;\n\n    auto testVal = tokens_.load(std::memory_order_acquire);\n    if (testVal != 0) {\n      return false;\n    }\n\n    if (waitList.empty()) {\n      // If the waitlist is now empty, ensure the token count increments\n      // No need for CAS here as we will always be under the mutex\n      CHECK(tokens_.compare_exchange_strong(\n          testVal, testVal + 1, std::memory_order_release));\n      return true;\n    }\n    waiter = &waitList.front();\n    waitList.pop_front();\n  }\n  // Trigger waiter if there is one\n  // Do it after releasing the waitList mutex, in case the waiter\n  // eagerly calls signal\n  waiter->baton.post();\n  return true;\n}\n\nvoid Semaphore::signal() {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal == 0) {\n      if (signalSlow()) {\n        return;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal + 1,\n      std::memory_order_release,\n      std::memory_order_acquire));\n}\n\nbool Semaphore::waitSlow(Waiter& waiter) {\n  // Slow path, create a baton and acquire a mutex to update the wait list\n  {\n    auto waitListLock = waitList_.wlock();\n    auto& waitList = *waitListLock;\n\n    auto testVal = tokens_.load(std::memory_order_acquire);\n    if (testVal != 0) {\n      return false;\n    }\n    // prepare baton and add to queue\n    waitList.push_back(waiter);\n    assert(!waitList.empty());\n  }\n  // Signal to caller that we managed to push a waiter\n  return true;\n}\n\nvoid Semaphore::wait() {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal == 0) {\n      Waiter waiter;\n      // If waitSlow fails it is because the token is non-zero by the time\n      // the lock is taken, so we can just continue round the loop\n      if (waitSlow(waiter)) {\n        waiter.baton.wait();\n        return;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - 1,\n      std::memory_order_release,\n      std::memory_order_acquire));\n}\n\nbool Semaphore::try_wait(Waiter& waiter) {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal == 0) {\n      if (waitSlow(waiter)) {\n        return false;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - 1,\n      std::memory_order_release,\n      std::memory_order_acquire));\n  return true;\n}\n\nbool Semaphore::try_wait() {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    if (oldVal == 0) {\n      return false;\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - 1,\n      std::memory_order_release,\n      std::memory_order_acquire));\n  return true;\n}\n\n#if FOLLY_HAS_COROUTINES\n\ncoro::Task<void> Semaphore::co_wait() {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal == 0) {\n      Waiter waiter;\n      // If waitSlow fails it is because the token is non-zero by the time\n      // the lock is taken, so we can just continue round the loop\n      if (waitSlow(waiter)) {\n        bool cancelled = false;\n        {\n          const auto& ct = co_await folly::coro::co_current_cancellation_token;\n          folly::CancellationCallback cb{\n              ct, [&] {\n                {\n                  auto waitListLock = waitList_.wlock();\n                  auto& waitList = *waitListLock;\n\n                  if (!waiter.hook_.is_linked()) {\n                    // Already dequeued by signalSlow()\n                    return;\n                  }\n\n                  cancelled = true;\n                  waitList.erase(waitList.iterator_to(waiter));\n                }\n\n                waiter.baton.post();\n              }};\n\n          co_await waiter.baton;\n        }\n\n        // Check 'cancelled' flag only after deregistering the callback so we're\n        // sure that we aren't reading it concurrently with a potential write\n        // from a thread requesting cancellation.\n        // TODO: This is not unreachable code, but the compiler wrongly thinks\n        // it is. Once the compiler is fixed we can remove this.\n        if (cancelled) {\n          co_yield folly::coro::co_stopped_may_throw;\n        }\n\n        co_return;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - 1,\n      std::memory_order_release,\n      std::memory_order_acquire));\n}\n\n#endif\n\nnamespace {\n\nclass FutureWaiter final : public fibers::Baton::Waiter {\n public:\n  FutureWaiter() { semaphoreWaiter.baton.setWaiter(*this); }\n\n  void post() override {\n    std::unique_ptr<FutureWaiter> destroyOnReturn{this};\n    promise.setValue();\n  }\n\n  Semaphore::Waiter semaphoreWaiter;\n  folly::Promise<Unit> promise;\n};\n\n} // namespace\n\nSemiFuture<Unit> Semaphore::future_wait() {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal == 0) {\n      auto batonWaiterPtr = std::make_unique<FutureWaiter>();\n      // If waitSlow fails it is because the token is non-zero by the time\n      // the lock is taken, so we can just continue round the loop\n      auto future = batonWaiterPtr->promise.getSemiFuture();\n      if (waitSlow(batonWaiterPtr->semaphoreWaiter)) {\n        (void)batonWaiterPtr.release();\n        return future;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - 1,\n      std::memory_order_release,\n      std::memory_order_acquire));\n  return makeSemiFuture();\n}\n\nsize_t Semaphore::getCapacity() const {\n  return capacity_;\n}\n\nsize_t Semaphore::getAvailableTokens() const {\n  return tokens_.load(std::memory_order_relaxed);\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/Semaphore.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/IntrusiveList.h>\n#include <folly/Synchronized.h>\n#include <folly/coro/Task.h>\n#include <folly/coro/Timeout.h>\n#include <folly/fibers/Baton.h>\n#include <folly/futures/Future.h>\n\n#include <deque>\n\nnamespace folly {\nnamespace fibers {\n\n/*\n * Fiber-compatible semaphore. Will safely block fibers that wait when no\n * tokens are available and wake fibers when signalled.\n *\n * Fair. Waiters are awoken in FIFO order. (Note: whether the callers see FIFO\n * order depends on the executors, wrapping async types, and the existence of\n * happens-before relationships between async wait operations.)\n */\nclass Semaphore {\n public:\n  explicit Semaphore(size_t tokenCount)\n      : capacity_(tokenCount), tokens_(int64_t(capacity_)) {}\n\n  Semaphore(const Semaphore&) = delete;\n  Semaphore(Semaphore&&) = delete;\n  Semaphore& operator=(const Semaphore&) = delete;\n  Semaphore& operator=(Semaphore&&) = delete;\n\n  /*\n   * Release a token in the semaphore. Signal the waiter if necessary.\n   */\n  void signal();\n\n  /*\n   * Wait for capacity in the semaphore.\n   */\n  void wait();\n\n  struct Waiter {\n    Waiter() noexcept {}\n\n    // The baton will be signalled when this waiter acquires the semaphore.\n    Baton baton;\n\n   private:\n    friend Semaphore;\n    folly::SafeIntrusiveListHook hook_;\n  };\n\n  /**\n   * Try to wait on the semaphore.\n   * Return true on success.\n   * On failure, the passed waiter is enqueued, its baton will be posted once\n   * semaphore has capacity. Caller is responsible to wait then signal.\n   */\n  bool try_wait(Waiter& waiter);\n\n  /**\n   * If the semaphore has capacity, removes a token and returns true. Otherwise\n   * returns false and leaves the semaphore unchanged.\n   */\n  bool try_wait();\n\n#if FOLLY_HAS_COROUTINES\n\n  /*\n   * Wait for capacity in the semaphore.\n   *\n   * Note that this wait-operation can be cancelled by requesting cancellation\n   * on the awaiting coroutine's associated CancellationToken.\n   * If the operation is successfully cancelled then it will complete with\n   * an error of type folly::OperationCancelled.\n   *\n   * Note that requesting cancellation of the operation will only have an\n   * effect if the operation does not complete synchronously (ie. was not\n   * already in a signalled state).\n   *\n   * If the semaphore was already in a signalled state prior to awaiting the\n   * returned Task then the operation will complete successfully regardless\n   * of whether cancellation was requested.\n   */\n  coro::Task<void> co_wait();\n\n  /*\n   * Wait for capacity in the semaphore with a timeout.\n   *\n   * Same as co_wait() but with a timeout.\n   *\n   * The timeout just requests cancellation after a timer expires. It has the\n   * same effect as requesting cancellation.\n   *\n   * If cancellation or timeout happen after the semaphore was already in a\n   * signalled state, no exception will be thrown, and the method will just\n   * return as if no cancellation or timeout happened.\n   */\n  template <typename Duration>\n  coro::Task<void> co_try_wait_for(Duration timeout) {\n    return folly::coro::timeoutNoDiscard(co_wait(), timeout);\n  }\n\n#endif\n\n  /*\n   * Wait for capacity in the semaphore.\n   */\n  SemiFuture<Unit> future_wait();\n\n  size_t getCapacity() const;\n\n  size_t getAvailableTokens() const;\n\n private:\n  bool waitSlow(Waiter& waiter);\n  bool signalSlow();\n\n  size_t capacity_;\n  // Atomic counter\n  std::atomic<int64_t> tokens_;\n\n  using WaiterList = folly::SafeIntrusiveList<Waiter, &Waiter::hook_>;\n\n  folly::Synchronized<WaiterList> waitList_;\n};\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/SemaphoreBase.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/SemaphoreBase.h>\n\nnamespace folly {\nnamespace fibers {\n\nbool SemaphoreBase::signalSlow(int64_t tokens) {\n  do {\n    auto testVal = tokens_.load(std::memory_order_acquire);\n\n    Waiter* waiter = nullptr;\n    {\n      // If we signalled a release, notify the waitlist\n      auto waitListLock = waitList_.wlock();\n      auto& waitList = *waitListLock;\n\n      if (waitList.empty() || waitList.front().tokens_ > testVal + tokens) {\n        // If the waitlist is now empty or not enough tokens to resume next in a\n        // waitlist, ensure the token count increments. No need for CAS here as\n        // we will always be under the mutex\n        if (tokens_.compare_exchange_strong(\n                testVal, testVal + tokens, std::memory_order_release)) {\n          return true;\n        }\n        continue;\n      }\n\n      waiter = &waitList.front();\n\n      // Check for tokens shortage and keep the waiter until tokens acquired\n      int64_t release =\n          (testVal > waiter->tokens_) ? 0 : waiter->tokens_ - testVal;\n      if (!tokens_.compare_exchange_strong(\n              testVal,\n              testVal + release - waiter->tokens_,\n              std::memory_order_relaxed)) {\n        continue;\n      }\n\n      tokens -= release;\n      waitList.pop_front();\n    }\n\n    // Trigger waiter if there is one\n    // Do it after releasing the waitList mutex, in case the waiter\n    // eagerly calls signal\n    waiter->baton.post();\n  } while (tokens > 0);\n\n  return true;\n}\n\nbool SemaphoreBase::waitSlow(Waiter& waiter, int64_t tokens) {\n  // Slow path, create a baton and acquire a mutex to update the wait list\n  {\n    auto waitListLock = waitList_.wlock();\n    auto& waitList = *waitListLock;\n\n    auto testVal = tokens_.load(std::memory_order_acquire);\n    if (testVal >= tokens) {\n      return false;\n    }\n    // prepare baton and add to queue\n    waitList.push_back(waiter);\n    assert(!waitList.empty());\n  }\n  // Signal to caller that we managed to push a waiter\n  return true;\n}\n\nvoid SemaphoreBase::wait_common(int64_t tokens) {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal < tokens) {\n      Waiter waiter{tokens};\n      // If waitSlow fails it is because the capacity is greater than\n      // requested by the time the lock is taken, so we can just continue\n      // round the loop\n      if (waitSlow(waiter, tokens)) {\n        waiter.baton.wait();\n        return;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - tokens,\n      std::memory_order_release,\n      std::memory_order_acquire));\n}\n\nbool SemaphoreBase::try_wait_common(Waiter& waiter, int64_t tokens) {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal < tokens) {\n      if (waitSlow(waiter, tokens)) {\n        return false;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - tokens,\n      std::memory_order_release,\n      std::memory_order_acquire));\n  return true;\n}\n\nbool SemaphoreBase::try_wait_common(int64_t tokens) {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    if (oldVal < tokens) {\n      return false;\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - tokens,\n      std::memory_order_release,\n      std::memory_order_acquire));\n  return true;\n}\n\n#if FOLLY_HAS_COROUTINES\n\ncoro::Task<void> SemaphoreBase::co_wait_common(int64_t tokens) {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal < tokens) {\n      Waiter waiter{tokens};\n      // If waitSlow fails it is because the capacity is greater than\n      // requested by the time the lock is taken, so we can just continue\n      // round the loop\n      if (waitSlow(waiter, tokens)) {\n        bool cancelled = false;\n        {\n          const auto& ct = co_await folly::coro::co_current_cancellation_token;\n          folly::CancellationCallback cb{\n              ct, [&] {\n                {\n                  auto waitListLock = waitList_.wlock();\n                  auto& waitList = *waitListLock;\n\n                  if (!waiter.hook_.is_linked()) {\n                    // Already dequeued by signalSlow()\n                    return;\n                  }\n\n                  cancelled = true;\n                  waitList.erase(waitList.iterator_to(waiter));\n                }\n\n                waiter.baton.post();\n              }};\n\n          co_await waiter.baton;\n        }\n\n        // Check 'cancelled' flag only after deregistering the callback so\n        // we're sure that we aren't reading it concurrently with a potential\n        // write from a thread requesting cancellation.\n        if (cancelled) {\n          co_yield folly::coro::co_stopped_may_throw;\n        }\n\n        co_return;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - tokens,\n      std::memory_order_release,\n      std::memory_order_acquire));\n}\n\n#endif\n\nnamespace {\n\nclass FutureWaiter final : public fibers::Baton::Waiter {\n public:\n  explicit FutureWaiter(int64_t tokens) : semaphoreWaiter(tokens) {\n    semaphoreWaiter.baton.setWaiter(*this);\n  }\n\n  void post() override {\n    std::unique_ptr<FutureWaiter> destroyOnReturn{this};\n    promise.setValue();\n  }\n\n  SemaphoreBase::Waiter semaphoreWaiter;\n  folly::Promise<Unit> promise;\n};\n\n} // namespace\n\nSemiFuture<Unit> SemaphoreBase::future_wait_common(int64_t tokens) {\n  auto oldVal = tokens_.load(std::memory_order_acquire);\n  do {\n    while (oldVal < tokens) {\n      auto batonWaiterPtr = std::make_unique<FutureWaiter>(tokens);\n      // If waitSlow fails it is because the capacity is greater than\n      // requested by the time the lock is taken, so we can just continue\n      // round the loop\n      auto future = batonWaiterPtr->promise.getSemiFuture();\n      if (waitSlow(batonWaiterPtr->semaphoreWaiter, tokens)) {\n        (void)batonWaiterPtr.release();\n        return future;\n      }\n      oldVal = tokens_.load(std::memory_order_acquire);\n    }\n  } while (!tokens_.compare_exchange_weak(\n      oldVal,\n      oldVal - tokens,\n      std::memory_order_release,\n      std::memory_order_acquire));\n  return makeSemiFuture();\n}\n\nsize_t SemaphoreBase::getCapacity() const {\n  return capacity_;\n}\n\nsize_t SemaphoreBase::getAvailableTokens() const {\n  return tokens_.load(std::memory_order_relaxed);\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/SemaphoreBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/IntrusiveList.h>\n#include <folly/Synchronized.h>\n#include <folly/fibers/Baton.h>\n#include <folly/futures/Future.h>\n#if FOLLY_HAS_COROUTINES\n#include <folly/coro/Task.h>\n#endif\n\n#include <deque>\n\nnamespace folly {\nnamespace fibers {\n\n/*\n * Fiber-compatible semaphore base. Will safely block fibers that wait when no\n * tokens are available and wake fibers when signalled.\n */\nclass SemaphoreBase {\n public:\n  explicit SemaphoreBase(size_t tokenCount)\n      : capacity_(tokenCount), tokens_(int64_t(capacity_)) {}\n\n  SemaphoreBase(const SemaphoreBase&) = delete;\n  SemaphoreBase(SemaphoreBase&&) = delete;\n  SemaphoreBase& operator=(const SemaphoreBase&) = delete;\n  SemaphoreBase& operator=(SemaphoreBase&&) = delete;\n\n  struct Waiter {\n    explicit Waiter(int64_t tokens = 1) noexcept : tokens_{tokens} {}\n\n    // The baton will be signalled when this waiter acquires the semaphore.\n    Baton baton;\n\n   private:\n    friend SemaphoreBase;\n    folly::SafeIntrusiveListHook hook_;\n    int64_t tokens_;\n  };\n\n  size_t getCapacity() const;\n\n  size_t getAvailableTokens() const;\n\n protected:\n  /*\n   * Wait for request capacity in the semaphore.\n   */\n  void wait_common(int64_t tokens);\n\n  /**\n   * Try to wait on the semaphore.\n   * Return true on success.\n   * On failure, the passed waiter is enqueued, its baton will be posted once\n   * semaphore has capacity. Caller is responsible to wait then signal.\n   */\n  bool try_wait_common(Waiter& waiter, int64_t tokens);\n\n  /**\n   * If the semaphore has capacity, removes a token and returns true. Otherwise\n   * returns false and leaves the semaphore unchanged.\n   */\n  bool try_wait_common(int64_t tokens);\n\n#if FOLLY_HAS_COROUTINES\n\n  /*\n   * Wait for request capacity in the semaphore.\n   *\n   * Note that this wait-operation can be cancelled by requesting cancellation\n   * on the awaiting coroutine's associated CancellationToken.\n   * If the operation is successfully cancelled then it will complete with\n   * an error of type folly::OperationCancelled.\n   *\n   * Note that requesting cancellation of the operation will only have an\n   * effect if the operation does not complete synchronously (ie. was not\n   * already in a signalled state).\n   *\n   * If the semaphore was already in a signalled state prior to awaiting the\n   * returned Task then the operation will complete successfully regardless\n   * of whether cancellation was requested.\n   */\n  coro::Task<void> co_wait_common(int64_t tokens);\n\n#endif\n\n  /*\n   * Wait for request capacity in the semaphore.\n   */\n  SemiFuture<Unit> future_wait_common(int64_t tokens);\n\n  bool waitSlow(Waiter& waiter, int64_t tokens);\n  bool signalSlow(int64_t tokens);\n\n  size_t capacity_;\n  // Atomic counter\n  std::atomic<int64_t> tokens_;\n\n  using WaiterList = folly::SafeIntrusiveList<Waiter, &Waiter::hook_>;\n\n  folly::Synchronized<WaiterList> waitList_;\n};\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/SimpleLoopController.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/SimpleLoopController.h>\n\n#include <folly/io/async/TimeoutManager.h>\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * A simple version of TimeoutManager that maintains only a single AsyncTimeout\n * object that is used by HHWheelTimer in SimpleLoopController.\n */\nclass SimpleLoopController::SimpleTimeoutManager : public TimeoutManager {\n public:\n  explicit SimpleTimeoutManager(SimpleLoopController& loopController)\n      : loopController_(loopController) {}\n\n  void attachTimeoutManager(\n      AsyncTimeout* /* unused */, InternalEnum /* unused */) final {}\n  void detachTimeoutManager(AsyncTimeout* /* unused */) final {}\n\n  bool scheduleTimeout(AsyncTimeout* obj, timeout_type timeout) final {\n    // Make sure that we don't try to use this manager with two timeouts.\n    CHECK(!timeout_ || timeout_->first == obj);\n    timeout_.emplace(obj, std::chrono::steady_clock::now() + timeout);\n    return true;\n  }\n\n  void cancelTimeout(AsyncTimeout* obj) final {\n    CHECK(timeout_ && timeout_->first == obj);\n    timeout_.reset();\n  }\n\n  void bumpHandlingTime() final {}\n\n  bool isInTimeoutManagerThread() final {\n    return loopController_.isInLoopThread();\n  }\n\n  void runTimeouts() {\n    std::chrono::steady_clock::time_point tp = std::chrono::steady_clock::now();\n    if (!timeout_ || tp < timeout_->second) {\n      return;\n    }\n\n    auto* timeout = timeout_->first;\n    timeout_.reset();\n    timeout->timeoutExpired();\n  }\n\n private:\n  SimpleLoopController& loopController_;\n  folly::Optional<\n      std::pair<AsyncTimeout*, std::chrono::steady_clock::time_point>>\n      timeout_;\n};\n\nSimpleLoopController::SimpleLoopController()\n    : fm_(nullptr),\n      stopRequested_(false),\n      loopThread_(),\n      timeoutManager_(std::make_unique<SimpleTimeoutManager>(*this)),\n      timer_(HHWheelTimer::newTimer(timeoutManager_.get())) {}\n\nSimpleLoopController::~SimpleLoopController() {\n  scheduled_ = false;\n}\n\nvoid SimpleLoopController::runTimeouts() {\n  timeoutManager_->runTimeouts();\n}\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/SimpleLoopController.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\n#include <folly/Function.h>\n#include <folly/Likely.h>\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/LoopController.h>\n\nnamespace folly {\nnamespace fibers {\n\nclass FiberManager;\n\nclass SimpleLoopController : public LoopController {\n public:\n  SimpleLoopController();\n  ~SimpleLoopController();\n\n  /**\n   * Run FiberManager loop; if no ready task are present,\n   * run provided function. Stops after both stop() has been called\n   * and no waiting tasks remain.\n   */\n  template <typename F>\n  void loop(F&& func) {\n    loopThread_.store(std::this_thread::get_id(), std::memory_order_release);\n\n    bool waiting = false;\n    stopRequested_ = false;\n\n    while (FOLLY_LIKELY(waiting || !stopRequested_)) {\n      func();\n      runTimeouts();\n      if (scheduled_) {\n        scheduled_ = false;\n        runLoop();\n        waiting = fm_->hasTasks();\n      }\n    }\n\n    loopThread_.store({}, std::memory_order_release);\n  }\n\n  /**\n   * Requests exit from loop() as soon as all waiting tasks complete.\n   */\n  void stop() { stopRequested_ = true; }\n\n  int remoteScheduleCalled() const { return remoteScheduleCalled_; }\n\n  void runLoop() override {\n    runLoopThread_.store(std::this_thread::get_id(), std::memory_order_release);\n    do {\n      if (remoteLoopRun_ < remoteScheduleCalled_) {\n        for (; remoteLoopRun_ < remoteScheduleCalled_; ++remoteLoopRun_) {\n          if (fm_->shouldRunLoopRemote()) {\n            fm_->loopUntilNoReadyImpl();\n          }\n        }\n      } else {\n        fm_->loopUntilNoReadyImpl();\n      }\n    } while (remoteLoopRun_ < remoteScheduleCalled_);\n    runLoopThread_.store({}, std::memory_order_release);\n  }\n\n  void runEagerFiber(Fiber* fiber) override { fm_->runEagerFiberImpl(fiber); }\n\n  void schedule() override { scheduled_ = true; }\n\n  HHWheelTimer* timer() override { return timer_.get(); }\n\n  bool isInLoopThread() override {\n    // One of the two will be set depending on how FiberManager is being looped\n    // in tests.\n    auto loopThread = loopThread_.load(std::memory_order_relaxed);\n    auto runInLoopThread = runLoopThread_.load(std::memory_order_relaxed);\n    auto thisThread = std::this_thread::get_id();\n    return loopThread == thisThread || runInLoopThread == thisThread;\n  }\n\n private:\n  FiberManager* fm_;\n  std::atomic<bool> scheduled_{false};\n  bool stopRequested_;\n  std::atomic<int> remoteScheduleCalled_{0};\n  int remoteLoopRun_{0};\n  std::atomic<std::thread::id> loopThread_;\n  std::atomic<std::thread::id> runLoopThread_;\n\n  class SimpleTimeoutManager;\n  std::unique_ptr<SimpleTimeoutManager> timeoutManager_;\n  std::shared_ptr<HHWheelTimer> timer_;\n\n  /* LoopController interface */\n\n  void setFiberManager(FiberManager* fm) override { fm_ = fm; }\n\n  void scheduleThreadSafe() override {\n    ++remoteScheduleCalled_;\n    scheduled_ = true;\n  }\n\n  void runTimeouts();\n\n  friend class FiberManager;\n};\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/TimedMutex-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <mutex>\n#include <folly/synchronization/detail/Sleeper.h>\n\nnamespace folly {\nnamespace fibers {\n\nnamespace detail {\n\ntemplate <class BatonType>\nMutexWaiter<BatonType>::~MutexWaiter() {\n  if (posted_.load(std::memory_order_acquire)) {\n    return;\n  }\n  // If baton supported retrying wait() after wait()/try_wait_until(), like\n  // SaturatingSemaphore, this could simply be replaced with baton_.wait(), and\n  // posted_ could be removed.\n  folly::detail::Sleeper sleeper;\n  while (!posted_.load(std::memory_order_acquire)) {\n    sleeper.wait();\n  }\n}\n\ntemplate <class BatonType>\nvoid MutexWaiter<BatonType>::wait() {\n  baton_.wait();\n}\n\ntemplate <class BatonType>\ntemplate <class Deadline>\nbool MutexWaiter<BatonType>::try_wait_until(Deadline deadline) {\n  return baton_.try_wait_until(deadline);\n}\n\ntemplate <class BatonType>\nvoid MutexWaiter<BatonType>::wake() {\n  baton_.post();\n  posted_.store(true, std::memory_order_release);\n}\n\n} // namespace detail\n\n//\n// TimedMutex implementation\n//\n\ntemplate <typename WaitFunc>\nTimedMutex::LockResult TimedMutex::lockHelper(WaitFunc&& waitFunc) {\n  std::unique_lock ulock(lock_);\n  if (!locked_) {\n    locked_ = true;\n    return LockResult::SUCCESS;\n  }\n\n  const auto isOnFiber = options_.stealing_ && onFiber();\n\n  if (!isOnFiber && notifiedFiber_ != nullptr) {\n    // lock() was called on a thread and while some other fiber was already\n    // notified, it hasn't be run yet. We steal the lock from that fiber then\n    // to avoid potential deadlock.\n    DCHECK(threadWaiters_.empty());\n    notifiedFiber_ = nullptr;\n    return LockResult::SUCCESS;\n  }\n\n  // Delay constructing the waiter until it is actually required.\n  // This makes a huge difference, at least in the benchmarks,\n  // when the mutex isn't locked.\n  MutexWaiter waiter;\n  MutexWaiterList& waiters = isOnFiber ? fiberWaiters_ : threadWaiters_;\n  waiters.push_back(waiter);\n\n  ulock.unlock();\n\n  if (!waitFunc(waiters, waiter)) {\n    return LockResult::TIMEOUT;\n  }\n\n  if (isOnFiber) {\n    auto lockStolen = [&] {\n      std::lock_guard lg(lock_);\n\n      auto stolen = notifiedFiber_ != &waiter;\n      if (!stolen) {\n        notifiedFiber_ = nullptr;\n      }\n      return stolen;\n    }();\n\n    if (lockStolen) {\n      return LockResult::STOLEN;\n    }\n  }\n\n  return LockResult::SUCCESS;\n}\n\ninline void TimedMutex::lock() {\n  LockResult result;\n  do {\n    result = lockHelper([](MutexWaiterList&, MutexWaiter& waiter) {\n      waiter.wait();\n      return true;\n    });\n\n    DCHECK(result != LockResult::TIMEOUT);\n  } while (result != LockResult::SUCCESS);\n}\n\ntemplate <typename Rep, typename Period>\nbool TimedMutex::try_lock_for(\n    const std::chrono::duration<Rep, Period>& timeout) {\n  return try_lock_until(std::chrono::steady_clock::now() + timeout);\n}\n\ntemplate <typename Clock, typename Duration>\nbool TimedMutex::try_lock_until(\n    const std::chrono::time_point<Clock, Duration>& deadline) {\n  auto result = lockHelper([&](MutexWaiterList& waiters, MutexWaiter& waiter) {\n    if (!waiter.try_wait_until(deadline)) {\n      // We timed out. Two cases:\n      // 1. We're still in the waiter list and we truly timed out\n      // 2. We're not in the waiter list anymore. This could happen if the baton\n      //    times out but the mutex is unlocked before we reach this code. In\n      //    this\n      //    case we'll pretend we got the lock on time.\n      std::unique_lock lg(lock_);\n      if (waiter.hook.is_linked()) {\n        waiters.erase(waiters.iterator_to(waiter));\n        lg.unlock();\n        waiter.wake(); // Ensure that destruction doesn't block.\n        return false;\n      }\n    }\n    return true;\n  });\n\n  switch (result) {\n    case LockResult::SUCCESS:\n      return true;\n    case LockResult::TIMEOUT:\n      return false;\n    case LockResult::STOLEN:\n      // We don't respect the duration if lock was stolen\n      lock();\n      return true;\n  }\n  assume_unreachable();\n}\n\ninline bool TimedMutex::try_lock() {\n  std::lock_guard lg(lock_);\n  if (locked_) {\n    return false;\n  }\n  locked_ = true;\n  return true;\n}\n\ninline void TimedMutex::unlock() {\n  std::unique_lock lg(lock_);\n  if (!threadWaiters_.empty()) {\n    auto& to_wake = threadWaiters_.front();\n    threadWaiters_.pop_front();\n    lg.unlock();\n    to_wake.wake();\n    return;\n  }\n\n  if (!fiberWaiters_.empty()) {\n    auto& to_wake = fiberWaiters_.front();\n    fiberWaiters_.pop_front();\n    notifiedFiber_ = &to_wake;\n    lg.unlock();\n    to_wake.wake();\n    return;\n  }\n\n  locked_ = false;\n}\n\n//\n// TimedRWMutexImpl implementation\n//\n\ntemplate <bool ReaderPriority, typename BatonType>\nclass TimedRWMutexImpl<ReaderPriority, BatonType>::StateLock {\n public:\n  // Spin until the state is not locked to apply mutation. The callback can\n  // abort at any time.\n  template <class Mutation>\n  FOLLY_ALWAYS_INLINE static bool tryMutate(\n      std::atomic<uint64_t>& stateRef, Mutation mutation) {\n    folly::detail::Sleeper sleeper;\n    auto curState = stateRef.load(std::memory_order_relaxed);\n    while (true) {\n      while (FOLLY_UNLIKELY(curState & kStateLocked)) {\n        sleeper.wait();\n        curState = stateRef.load(std::memory_order_relaxed);\n      }\n      auto newState = curState;\n      if (!mutation(newState)) {\n        return false;\n      }\n      if (stateRef.compare_exchange_strong(\n              curState,\n              newState,\n              std::memory_order_acq_rel,\n              std::memory_order_relaxed)) {\n        return true;\n      }\n    }\n  }\n\n  explicit StateLock(std::atomic<uint64_t>& stateRef) : stateRef_(stateRef) {\n    tryMutate(stateRef_, [&](uint64_t& state) {\n      state_ = (state |= kStateLocked);\n#ifndef NDEBUG\n      initialState_ = state_;\n#endif\n      return true;\n    });\n  }\n\n  ~StateLock() {\n    if (locked_) {\n      unlock();\n    }\n  }\n\n  uint64_t& state() {\n    assert(locked_);\n    return state_;\n  }\n\n  void unlock() {\n    // All state modifications while locked should be done through state().\n    assert(stateRef_.load(std::memory_order_relaxed) == initialState_);\n    // Verify reader/writer exclusion.\n    assert(!(state_ & kWriteLocked) || (state_ >> kReadersShift) == 0);\n    stateRef_.store(state_ & ~kStateLocked, std::memory_order_release);\n    locked_ = false;\n  }\n\n  StateLock(const StateLock&) = delete;\n  StateLock(StateLock&&) = delete;\n  StateLock& operator=(const StateLock&) = delete;\n  StateLock& operator=(StateLock&&) = delete;\n\n private:\n  std::atomic<uint64_t>& stateRef_;\n  uint64_t state_;\n#ifndef NDEBUG\n  uint64_t initialState_;\n#endif\n  bool locked_ = true;\n};\n\n// invariants that must hold when the lock is not held by anyone\ntemplate <bool ReaderPriority, typename BatonType>\nvoid TimedRWMutexImpl<ReaderPriority, BatonType>::verify_unlocked_properties(\n    [[maybe_unused]] StateLock& slock) const {\n  assert(slock.state() == kStateLocked); // Only bit set is the state lock.\n  assert(read_waiters_.empty());\n  assert(write_waiters_.empty());\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait(\n    StateLock& slock) const {\n  assert(!(slock.state() & kHasWriteWaiters) == write_waiters_.empty());\n  return shouldReadersWait(slock.state());\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\n/* static */ bool\nTimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait(uint64_t state) {\n  return (state & kWriteLocked) ||\n      (!ReaderPriority && (state & kHasWriteWaiters));\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_fast() {\n  return StateLock::tryMutate(state_, [](uint64_t& state) {\n    if (FOLLY_UNLIKELY(shouldReadersWait(state))) {\n      return false;\n    }\n    state += kReadersInc;\n    return true;\n  });\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_unlock_shared_fast() {\n  return StateLock::tryMutate(state_, [](uint64_t& state) {\n    auto numReaders = state >> kReadersShift;\n    assert(!(state & kWriteLocked) && numReaders > 0);\n    if (FOLLY_UNLIKELY(numReaders == 1 && (state & kHasWriteWaiters))) {\n      return false; // We may need to wake up a writer.\n    }\n    state -= kReadersInc;\n    return true;\n  });\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nvoid TimedRWMutexImpl<ReaderPriority, BatonType>::lock_shared() {\n  if (try_lock_shared_fast()) {\n    return;\n  }\n\n  StateLock slock{state_};\n  assert(!(slock.state() & kHasWriteWaiters) == write_waiters_.empty());\n  if (shouldReadersWait(slock)) {\n    MutexWaiter waiter;\n    read_waiters_.push_back(waiter);\n    slock.unlock();\n    waiter.wait();\n    assert((state_.load(std::memory_order_relaxed) >> kReadersShift) > 0);\n    return;\n  }\n  assert(!(slock.state() & kWriteLocked));\n  slock.state() += kReadersInc;\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\ntemplate <typename Rep, typename Period>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_for(\n    const std::chrono::duration<Rep, Period>& timeout) {\n  return try_lock_shared_until(std::chrono::steady_clock::now() + timeout);\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\ntemplate <typename Clock, typename Duration>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_until(\n    const std::chrono::time_point<Clock, Duration>& deadline) {\n  if (try_lock_shared_fast()) {\n    return true;\n  }\n  StateLock slock{state_};\n  if (shouldReadersWait(slock)) {\n    MutexWaiter waiter;\n    read_waiters_.push_back(waiter);\n    slock.unlock();\n\n    if (!waiter.try_wait_until(deadline)) {\n      // We timed out. Two cases:\n      // 1. We're still in the waiter list and we truly timed out\n      // 2. We're not in the waiter list anymore. This could happen if the baton\n      //    times out but the mutex is unlocked before we reach this code. In\n      //    this case we'll pretend we got the lock on time.\n      StateLock slock2{state_};\n      if (waiter.hook.is_linked()) {\n        read_waiters_.erase(read_waiters_.iterator_to(waiter));\n        slock2.unlock();\n        waiter.wake(); // Ensure that destruction doesn't block.\n        return false;\n      }\n    }\n    return true;\n  }\n  assert(!(slock.state() & kWriteLocked));\n  slock.state() += kReadersInc;\n  return true;\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared() {\n  if (try_lock_shared_fast()) {\n    return true;\n  }\n\n  StateLock slock{state_};\n  if (shouldReadersWait(slock)) {\n    return false;\n  }\n  assert(!(slock.state() & kWriteLocked));\n  slock.state() += kReadersInc;\n  return true;\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nvoid TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_shared() {\n  if (try_unlock_shared_fast()) {\n    return;\n  }\n\n  assert((state_.load(std::memory_order_relaxed) >> kReadersShift) > 0);\n  unlock_();\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_(StateLock& slock) {\n  if (!(slock.state() & kWriteLocked) &&\n      ((slock.state() >> kReadersShift) == 0)) {\n    verify_unlocked_properties(slock);\n    slock.state() |= kWriteLocked;\n    return true;\n  }\n  return false;\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nvoid TimedRWMutexImpl<ReaderPriority, BatonType>::lock() {\n  StateLock slock{state_};\n  if (try_lock_(slock)) {\n    return;\n  }\n  MutexWaiter waiter;\n  write_waiters_.push_back(waiter);\n  slock.state() |= kHasWriteWaiters;\n  slock.unlock();\n  waiter.wait();\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\ntemplate <typename Rep, typename Period>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_for(\n    const std::chrono::duration<Rep, Period>& timeout) {\n  return try_lock_until(std::chrono::steady_clock::now() + timeout);\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\ntemplate <typename Clock, typename Duration>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_until(\n    const std::chrono::time_point<Clock, Duration>& deadline) {\n  StateLock slock{state_};\n  if (try_lock_(slock)) {\n    return true;\n  }\n  MutexWaiter waiter;\n  {\n    write_waiters_.push_back(waiter);\n    slock.state() |= kHasWriteWaiters;\n    slock.unlock();\n  }\n\n  if (!waiter.try_wait_until(deadline)) {\n    // We timed out. Two cases:\n    // 1. We're still in the waiter list and we truly timed out\n    // 2. We're not in the waiter list anymore. This could happen if the baton\n    //    times out but the mutex is unlocked before we reach this code. In\n    //    this case we'll pretend we got the lock on time.\n    StateLock slock2{state_};\n    if (waiter.hook.is_linked()) {\n      write_waiters_.erase(write_waiters_.iterator_to(waiter));\n      if (write_waiters_.empty() && (slock2.state() & kHasWriteWaiters)) {\n        slock2.state() &= ~kHasWriteWaiters;\n      }\n      slock2.unlock();\n      waiter.wake(); // Ensure that destruction doesn't block.\n      return false;\n    }\n  }\n  assert(state_.load(std::memory_order_relaxed) & kWriteLocked);\n  return true;\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nbool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock() {\n  StateLock slock{state_};\n  return try_lock_(slock);\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nvoid TimedRWMutexImpl<ReaderPriority, BatonType>::unlock() {\n  assert(state_.load(std::memory_order_relaxed) & kWriteLocked);\n  unlock_();\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nvoid TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_() {\n  StateLock slock{state_};\n  assert(\n      (slock.state() & kWriteLocked) ||\n      (!(slock.state() & kWriteLocked) &&\n       ((slock.state() >> kReadersShift) > 0)));\n  if ((slock.state() >> kReadersShift) > 0) {\n    slock.state() -= kReadersInc;\n  }\n\n  if (!read_waiters_.empty() && (ReaderPriority || write_waiters_.empty())) {\n    assert(\n        (slock.state() & kWriteLocked) &&\n        ((slock.state() >> kReadersShift) == 0) &&\n        \"read waiters can only accumulate while write locked\");\n    slock.state() &= ~kWriteLocked;\n    slock.state() += read_waiters_.size() * kReadersInc;\n\n    MutexWaiterList waiters_to_wake = std::move(read_waiters_);\n    slock.unlock();\n\n    while (!waiters_to_wake.empty()) {\n      MutexWaiter& to_wake = waiters_to_wake.front();\n      waiters_to_wake.pop_front();\n      to_wake.wake();\n    }\n\n    return;\n  }\n\n  if ((slock.state() >> kReadersShift) == 0) {\n    if (!write_waiters_.empty()) {\n      assert(read_waiters_.empty() || !ReaderPriority);\n      assert(slock.state() & kHasWriteWaiters);\n      slock.state() |= kWriteLocked;\n\n      // Wake a single writer (after releasing the spin lock)\n      MutexWaiter& to_wake = write_waiters_.front();\n      write_waiters_.pop_front();\n      if (write_waiters_.empty()) {\n        slock.state() &= ~kHasWriteWaiters;\n      }\n      slock.unlock();\n      to_wake.wake();\n      return;\n    }\n\n    slock.state() &= ~kWriteLocked;\n    verify_unlocked_properties(slock);\n    return;\n  }\n\n  assert((slock.state() >> kReadersShift) > 0);\n}\n\ntemplate <bool ReaderPriority, typename BatonType>\nvoid TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_and_lock_shared() {\n  StateLock slock{state_};\n  assert(\n      (slock.state() & kWriteLocked) &&\n      ((slock.state() >> kReadersShift) == 0));\n  slock.state() &= ~kWriteLocked;\n  slock.state() += kReadersInc;\n\n  if (!read_waiters_.empty()) {\n    slock.state() += read_waiters_.size() * kReadersInc;\n\n    MutexWaiterList waiters_to_wake = std::move(read_waiters_);\n    slock.unlock();\n\n    while (!waiters_to_wake.empty()) {\n      MutexWaiter& to_wake = waiters_to_wake.front();\n      waiters_to_wake.pop_front();\n      to_wake.wake();\n    }\n  }\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/TimedMutex.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/IntrusiveList.h>\n#include <folly/Portability.h>\n#include <folly/SpinLock.h>\n#include <folly/fibers/GenericBaton.h>\n\nnamespace folly {\nnamespace fibers {\n\nnamespace detail {\n\n// Represents a waiter waiting for the lock. The waiter waits on the baton until\n// it is woken up by a post or timeout expires.\n//\n// The destructor blocks until wake() is called. This is to ensure that if a\n// waiter times out, it is not invalidated for a waker that might have already\n// have acquired a reference to it. Hence, whoever removes the waiter from a\n// list is responsible for waking it.\ntemplate <class BatonType>\nclass MutexWaiter {\n public:\n  MutexWaiter() = default;\n  ~MutexWaiter();\n\n  void wait();\n\n  template <class Deadline>\n  bool try_wait_until(Deadline deadline);\n\n  void wake();\n\n  folly::SafeIntrusiveListHook hook;\n\n private:\n  BatonType baton_;\n  // This is silly, but Baton implementations do not allow to check the state\n  // after a timed out wait, so we need to duplicate the state.\n  std::atomic<bool> posted_{false};\n};\n\n} // namespace detail\n\n/**\n * @class TimedMutex\n *\n * Like mutex but allows timed_lock in addition to lock and try_lock.\n **/\nclass TimedMutex {\n public:\n  struct Options {\n    constexpr Options(bool stealing = true) : stealing_(stealing) {}\n    /**\n     * Prefer thread waiter and steal from fiber waiters if possible\n     */\n    bool stealing_ = true;\n  };\n\n  TimedMutex(Options options = Options()) noexcept\n      : options_(std::move(options)) {}\n\n  ~TimedMutex() {\n    DCHECK(threadWaiters_.empty());\n    DCHECK(fiberWaiters_.empty());\n    DCHECK(notifiedFiber_ == nullptr);\n  }\n\n  TimedMutex(const TimedMutex& rhs) = delete;\n  TimedMutex& operator=(const TimedMutex& rhs) = delete;\n  TimedMutex(TimedMutex&& rhs) = delete;\n  TimedMutex& operator=(TimedMutex&& rhs) = delete;\n\n  // Lock the mutex. The thread / fiber is blocked until the mutex is free\n  void lock();\n\n  // Lock the mutex. The thread / fiber will be blocked until a timeout elapses.\n  //\n  // @return        true if the mutex was locked, false otherwise\n  template <typename Rep, typename Period>\n  bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout);\n\n  // Lock the mutex. The thread / fiber will be blocked until a deadline\n  //\n  // @return        true if the mutex was locked, false otherwise\n  template <typename Clock, typename Duration>\n  bool try_lock_until(const std::chrono::time_point<Clock, Duration>& deadline);\n\n  // Try to obtain lock without blocking the thread or fiber\n  bool try_lock();\n\n  // Unlock the mutex and wake up a waiter if there is one\n  void unlock();\n\n private:\n  enum class LockResult { SUCCESS, TIMEOUT, STOLEN };\n\n  using MutexWaiter = detail::MutexWaiter<Baton>;\n  using MutexWaiterList =\n      folly::SafeIntrusiveList<MutexWaiter, &MutexWaiter::hook>;\n\n  template <typename WaitFunc>\n  LockResult lockHelper(WaitFunc&& waitFunc);\n\n  const Options options_;\n  folly::SpinLock lock_; //< lock to protect waiter list\n  bool locked_ = false; //< is this locked by some thread?\n  MutexWaiterList threadWaiters_; //< list of waiters\n  MutexWaiterList fiberWaiters_; //< list of waiters\n  MutexWaiter* notifiedFiber_{nullptr}; //< Fiber waiter which has been notified\n};\n\n/**\n * @class TimedRWMutexImpl\n *\n * A readers-writer lock which allows multiple readers to hold the\n * lock simultaneously or only one writer.\n *\n * NOTE: When ReaderPriority is set to true then the lock is a reader-preferred\n * RWLock i.e. readers are given priority when there are both readers and\n * writers waiting to get the lock.\n *\n * When ReaderPriority is set to false then the lock is a writer-preferred\n * RWLock i.e. writers are given priority when there are both readers and\n * writers waiting to get the lock. Note that when the lock is in\n * writer-preferred mode, the readers are not re-entrant (e.g. if a caller owns\n * a read lock, it can't attempt to acquire the read lock again as it can\n * deadlock.)\n **/\ntemplate <bool ReaderPriority, typename BatonType>\nclass TimedRWMutexImpl {\n public:\n  TimedRWMutexImpl() = default;\n  ~TimedRWMutexImpl() = default;\n\n  TimedRWMutexImpl(const TimedRWMutexImpl& rhs) = delete;\n  TimedRWMutexImpl& operator=(const TimedRWMutexImpl& rhs) = delete;\n  TimedRWMutexImpl(TimedRWMutexImpl&& rhs) = delete;\n  TimedRWMutexImpl& operator=(TimedRWMutexImpl&& rhs) = delete;\n\n  // Lock for shared access. The thread / fiber is blocked until the lock\n  // can be acquired.\n  void lock_shared();\n\n  // Like lock_shared except the thread / fiber is blocked until a timeout\n  // elapses\n  // @return        true if locked successfully, false otherwise.\n  template <typename Rep, typename Period>\n  bool try_lock_shared_for(const std::chrono::duration<Rep, Period>& timeout);\n\n  // Like lock_shared except the thread / fiber is blocked until a deadline\n  // @return        true if locked successfully, false otherwise.\n  template <typename Clock, typename Duration>\n  bool try_lock_shared_until(\n      const std::chrono::time_point<Clock, Duration>& deadline);\n\n  // Like lock_shared but doesn't block the thread / fiber if the lock can't\n  // be acquired.\n  // @return        true if lock was acquired, false otherwise.\n  bool try_lock_shared();\n\n  // Release the lock. The thread / fiber will wake up a writer if there is one\n  // and if this is the last concurrently-held read lock to be released.\n  void unlock_shared();\n\n  // Obtain an exclusive lock. The thread / fiber is blocked until the lock\n  // is available.\n  void lock();\n\n  // Like lock except the thread / fiber is blocked until a timeout elapses\n  // @return        true if locked successfully, false otherwise.\n  template <typename Rep, typename Period>\n  bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout);\n\n  // Like lock except the thread / fiber is blocked until a deadline\n  // @return        true if locked successfully, false otherwise.\n  template <typename Clock, typename Duration>\n  bool try_lock_until(const std::chrono::time_point<Clock, Duration>& deadline);\n\n  // Like lock but doesn't block the thread / fiber if the lock cant be\n  // obtained.\n  // @return        true if lock was acquired, false otherwise.\n  bool try_lock();\n\n  // Realease the lock. The thread / fiber will wake up all readers if there are\n  // any. If there are waiting writers then only one of them will be woken up.\n  // NOTE: readers are given priority over writers (see above comment)\n  void unlock();\n\n  // Downgrade the lock. The thread / fiber will wake up all readers if there\n  // are any.\n  void unlock_and_lock_shared();\n\n private:\n  class StateLock;\n\n  void verify_unlocked_properties(StateLock& slock) const;\n  FOLLY_ALWAYS_INLINE bool shouldReadersWait(StateLock& slock) const;\n  FOLLY_ALWAYS_INLINE static bool shouldReadersWait(uint64_t state);\n\n  FOLLY_ALWAYS_INLINE bool try_lock_shared_fast();\n  FOLLY_ALWAYS_INLINE bool try_unlock_shared_fast();\n\n  bool try_lock_(StateLock& slock);\n  void unlock_();\n\n  using MutexWaiter = detail::MutexWaiter<BatonType>;\n  using MutexWaiterList =\n      folly::CountedIntrusiveList<MutexWaiter, &MutexWaiter::hook>;\n\n  // State word layout:\n  // [... <#readers:32> <has write waiters:1> <write locked:1> <state locked:1>]\n  constexpr static uint64_t kStateLocked = uint64_t{1} << 0;\n  constexpr static uint64_t kWriteLocked = uint64_t{1} << 1;\n  constexpr static uint64_t kHasWriteWaiters = uint64_t{1} << 2;\n  constexpr static uint64_t kReadersShift = 3;\n  constexpr static uint64_t kReadersInc = uint64_t{1} << kReadersShift;\n  std::atomic<uint64_t> state_ = 0;\n\n  MutexWaiterList write_waiters_; //< List of thread / fibers waiting for\n  //  exclusive access\n\n  MutexWaiterList read_waiters_; //< List of thread / fibers waiting for\n  //  shared access\n};\n\ntemplate <typename BatonType>\nusing TimedRWMutexReadPriority = TimedRWMutexImpl<true, BatonType>;\n\ntemplate <typename BatonType>\nusing TimedRWMutexWritePriority = TimedRWMutexImpl<false, BatonType>;\n\ntemplate <typename BatonType>\nusing TimedRWMutex = TimedRWMutexReadPriority<BatonType>;\n\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/TimedMutex-inl.h>\n"
  },
  {
    "path": "folly/fibers/WhenN-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Optional.h>\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/fibers/ForEach.h>\n\nnamespace folly {\nnamespace fibers {\n\ntemplate <class InputIterator>\ntypename std::enable_if<\n    !std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    std::vector<std::pair<\n        size_t,\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>>>>::type\ncollectN(InputIterator first, InputIterator last, size_t n) {\n  using Result =\n      invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>;\n  assert(n > 0);\n  assert(std::distance(first, last) >= 0);\n  assert(n <= static_cast<size_t>(std::distance(first, last)));\n\n  struct Context {\n    std::vector<std::pair<size_t, Result>> results;\n    size_t tasksTodo;\n    std::exception_ptr e;\n    folly::Optional<Promise<void>> promise;\n\n    Context(size_t tasksTodo_) : tasksTodo(tasksTodo_) {\n      this->results.reserve(tasksTodo_);\n    }\n  };\n  auto context = std::make_shared<Context>(n);\n\n  await_async([first, last, context](Promise<void> promise) mutable {\n    context->promise = std::move(promise);\n    for (size_t i = 0; first != last; ++i, ++first) {\n      addTask([i, context, f = std::move(*first)]() {\n        try {\n          auto result = f();\n          if (context->tasksTodo == 0) {\n            return;\n          }\n          context->results.emplace_back(i, std::move(result));\n        } catch (...) {\n          if (context->tasksTodo == 0) {\n            return;\n          }\n          context->e = current_exception();\n        }\n        if (--context->tasksTodo == 0) {\n          context->promise->setValue();\n        }\n      });\n    }\n  });\n\n  if (context->e != std::exception_ptr()) {\n    std::rethrow_exception(context->e);\n  }\n\n  return std::move(context->results);\n}\n\ntemplate <class InputIterator>\ntypename std::enable_if<\n    std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    std::vector<size_t>>::type\ncollectN(InputIterator first, InputIterator last, size_t n) {\n  assert(n > 0);\n  assert(std::distance(first, last) >= 0);\n  assert(n <= static_cast<size_t>(std::distance(first, last)));\n\n  struct Context {\n    std::vector<size_t> taskIndices;\n    std::exception_ptr e;\n    size_t tasksTodo;\n    folly::Optional<Promise<void>> promise;\n\n    Context(size_t tasksTodo_) : tasksTodo(tasksTodo_) {\n      this->taskIndices.reserve(tasksTodo_);\n    }\n  };\n  auto context = std::make_shared<Context>(n);\n\n  await_async([first, last, context](Promise<void> promise) mutable {\n    context->promise = std::move(promise);\n    for (size_t i = 0; first != last; ++i, ++first) {\n      addTask([i, context, f = std::move(*first)]() {\n        try {\n          f();\n          if (context->tasksTodo == 0) {\n            return;\n          }\n          context->taskIndices.push_back(i);\n        } catch (...) {\n          if (context->tasksTodo == 0) {\n            return;\n          }\n          context->e = current_exception();\n        }\n        if (--context->tasksTodo == 0) {\n          context->promise->setValue();\n        }\n      });\n    }\n  });\n\n  if (context->e != std::exception_ptr()) {\n    std::rethrow_exception(context->e);\n  }\n\n  return context->taskIndices;\n}\n\ntemplate <class InputIterator>\ntypename std::vector<\n    typename std::enable_if<\n        !std::is_same<\n            invoke_result_t<\n                typename std::iterator_traits<InputIterator>::value_type>,\n            void>::value,\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>>::\n        type> inline collectAll(InputIterator first, InputIterator last) {\n  using Result =\n      invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>;\n  size_t n = size_t(std::distance(first, last));\n  std::vector<Result> results;\n  std::vector<size_t> order(n);\n  results.reserve(n);\n\n  forEach(first, last, [&results, &order](size_t id, Result result) {\n    order[id] = results.size();\n    results.emplace_back(std::move(result));\n  });\n  assert(results.size() == n);\n\n  std::vector<Result> orderedResults;\n  orderedResults.reserve(n);\n\n  for (size_t i = 0; i < n; ++i) {\n    orderedResults.emplace_back(std::move(results[order[i]]));\n  }\n\n  return orderedResults;\n}\n\ntemplate <class InputIterator>\ntypename std::enable_if<\n    std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    void>::type inline collectAll(InputIterator first, InputIterator last) {\n  forEach(first, last, [](size_t /* id */) {});\n}\n\ntemplate <class InputIterator>\ntypename std::enable_if<\n    !std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    typename std::pair<\n        size_t,\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>>>::\n    type inline collectAny(InputIterator first, InputIterator last) {\n  auto result = collectN(first, last, 1);\n  assert(result.size() == 1);\n  return std::move(result[0]);\n}\n\ntemplate <class InputIterator>\ntypename std::enable_if<\n    std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    size_t>::type inline collectAny(InputIterator first, InputIterator last) {\n  auto result = collectN(first, last, 1);\n  assert(result.size() == 1);\n  return std::move(result[0]);\n}\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/WhenN.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <iterator>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * Schedules several tasks and blocks until n of these tasks are completed.\n * If any of these n tasks throws an exception, this exception will be\n * re-thrown, but only when n tasks are complete. If several tasks throw\n * exceptions one of them will be re-thrown.\n *\n * @param first Range of tasks to be scheduled\n * @param last\n * @param n Number of tasks to wait for\n *\n * @return vector of pairs (task index, return value of task)\n */\ntemplate <class InputIterator>\ntypename std::enable_if<\n    !std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    std::vector<std::pair<\n        size_t,\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>>>>::\n    type inline collectN(InputIterator first, InputIterator last, size_t n);\n\n/**\n * collectN specialization for functions returning void\n *\n * @param first Range of tasks to be scheduled\n * @param last\n * @param n Number of tasks to wait for\n *\n * @return vector of completed task indices\n */\ntemplate <class InputIterator>\ntypename std::enable_if<\n    std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    std::vector<size_t>>::\n    type inline collectN(InputIterator first, InputIterator last, size_t n);\n\n/**\n * Schedules several tasks and blocks until all of these tasks are completed.\n * If any of the tasks throws an exception, this exception will be re-thrown,\n * but only when all the tasks are complete. If several tasks throw exceptions\n * one of them will be re-thrown.\n *\n * @param first Range of tasks to be scheduled\n * @param last\n *\n * @return vector of values returned by tasks\n */\ntemplate <class InputIterator>\ntypename std::vector<\n    typename std::enable_if<\n        !std::is_same<\n            invoke_result_t<\n                typename std::iterator_traits<InputIterator>::value_type>,\n            void>::value,\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>>::\n        type> inline collectAll(InputIterator first, InputIterator last);\n\n/**\n * collectAll specialization for functions returning void\n *\n * @param first Range of tasks to be scheduled\n * @param last\n */\ntemplate <class InputIterator>\ntypename std::enable_if<\n    std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    void>::type inline collectAll(InputIterator first, InputIterator last);\n\n/**\n * Schedules several tasks and blocks until one of them is completed.\n * If this task throws an exception, this exception will be re-thrown.\n * Exceptions thrown by all other tasks will be ignored.\n *\n * @param first Range of tasks to be scheduled\n * @param last\n *\n * @return pair of index of the first completed task and its return value\n */\ntemplate <class InputIterator>\ntypename std::enable_if<\n    !std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    typename std::pair<\n        size_t,\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>>>::\n    type inline collectAny(InputIterator first, InputIterator last);\n\n/**\n * WhenAny specialization for functions returning void.\n *\n * @param first Range of tasks to be scheduled\n * @param last\n *\n * @return index of the first completed task\n */\ntemplate <class InputIterator>\ntypename std::enable_if<\n    std::is_same<\n        invoke_result_t<\n            typename std::iterator_traits<InputIterator>::value_type>,\n        void>::value,\n    size_t>::type inline collectAny(InputIterator first, InputIterator last);\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/WhenN-inl.h>\n"
  },
  {
    "path": "folly/fibers/async/Async.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/async/Async.h>\n\n#include <folly/fibers/FiberManagerInternal.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\nnamespace detail {\n\nbool onFiber() {\n  return folly::fibers::onFiber();\n}\n\n} // namespace detail\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/Async.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <utility>\n\n#include <glog/logging.h>\n\n#include <folly/Traits.h>\n#include <folly/Unit.h>\n#include <folly/functional/Invoke.h>\n#include <folly/lang/CustomizationPoint.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\ntemplate <typename T>\nclass Async;\n\nnamespace detail {\n/**\n * Define in source to avoid including FiberManager header and keep this file\n * cheap to include\n */\nbool onFiber();\n} // namespace detail\n\nstruct await_fn {\n  template <typename T>\n  auto operator()(Async<T>&& async) const\n      noexcept(is_nothrow_tag_invocable<await_fn, Async<T>&&>::value)\n          -> tag_invoke_result_t<await_fn, Async<T>&&> {\n    return tag_invoke(*this, static_cast<Async<T>&&>(async));\n  }\n};\n/**\n * Function to retrieve the result from the Async wrapper\n * A function calling await must return an Async wrapper itself\n * for the wrapper to serve its intended purpose (the best way to enforce this\n * is static analysis)\n */\nFOLLY_DEFINE_CPO(await_fn, await_async)\n#if !defined(_MSC_VER)\nstatic constexpr auto& await = await_async;\n#endif\n\n/**\n * Asynchronous fiber result wrapper\n *\n * Syntactic sugar to indicate that can be used as the return type of a\n * function, indicating that a fiber can be preempted within that function.\n * Wraps the eagerly executed result of the function and must be 'await'ed to\n * retrieve the result.\n *\n * Since fibers context switches are implicit, it can be difficult to tell if a\n * function does I/O. In large codebases, it can also be difficult to tell if a\n * given function is running on fibers or not. The Async<> return type makes\n * I/O explicit and provides a good way to identify code paths running on fiber.\n *\n * Async must be combined with static analysis (eg. lints) that forces a\n * function that calls 'await' to also return an Async wrapped result.\n *\n * Runtime Consideration:\n * - The wrapper is currently 0 cost (in optimized builds), and this will\n *   remain a guarentee\n * - It provides protection (in debug builds) against running Async-annotated\n *   code on main context.\n * - It does NOT provide protection against doing I/O in non Async-annotated\n *   code, both asynchronously (on fiber) or blocking (main context).\n * - It does NOT provide protection from fiber's stack overflow.\n */\ntemplate <typename T>\nclass [[nodiscard]] Async {\n public:\n  using inner_type = T;\n\n  // General use constructor\n  template <typename... Us>\n  /* implicit */ Async(Us&&... val) : val_(std::forward<Us>(val)...) {}\n\n  // Move constructor to allow eager-return of async without using await\n  template <typename U>\n  /* implicit */ Async(Async<U>&& async) noexcept\n      : val_(static_cast<U&&>(async.val_)) {}\n\n  Async(const Async&) = delete;\n  Async(Async&& other) = default;\n  Async& operator=(const Async&) = delete;\n  Async& operator=(Async&&) = delete;\n\n  friend T&& tag_invoke(await_fn, Async&& async) noexcept {\n    DCHECK(detail::onFiber());\n    return static_cast<T&&>(async.val_);\n  }\n\n private:\n  T val_;\n\n  template <typename U>\n  friend class Async;\n};\n\ntemplate <>\nclass [[nodiscard]] Async<void> {\n public:\n  using inner_type = void;\n\n  /* implicit */ Async() {}\n  /* implicit */ Async(Unit) {}\n  /* implicit */ Async(Async<Unit>&&) {}\n\n  Async(const Async&) = delete;\n  Async(Async&& other) = default;\n  Async& operator=(const Async&) = delete;\n  Async operator=(Async&&) = delete;\n\n  friend void tag_invoke(await_fn, Async&&) noexcept {\n    DCHECK(detail::onFiber());\n  }\n};\n\n/**\n * Deduction guide to make it easier to construct and return Async objects.\n * The guide doesn't permit constructing and returning by reference.\n */\ntemplate <typename T>\nexplicit Async(T) -> Async<T>;\n\n/**\n * A utility to start annotating at top of stack (eg. the task which is added to\n * fiber manager) A function must not return an Async wrapper if it uses\n * `init_await` instead of `await` (again, enforce via static analysis)\n */\ntemplate <typename T>\nT&& init_await(Async<T>&& async) {\n  return await_async(std::move(async));\n}\n\ninline void init_await(Async<void>&& async) {\n  await_async(std::move(async));\n}\n\n// is_async\ntemplate <typename T>\nconstexpr bool is_async_v = folly::is_instantiation_of_v<Async, T>;\n\n// async_inner_type\ntemplate <typename T>\nstruct async_inner_type;\n\ntemplate <typename T>\nstruct async_inner_type<Async<T>> {\n  using type = T;\n};\n\ntemplate <typename T>\nusing async_inner_type_t = typename async_inner_type<T>::type;\n\n// async_invocable_inner_type\ntemplate <typename F, typename... Args>\nusing async_invocable_inner_type =\n    async_inner_type<invoke_result_t<F, Args...>>;\n\ntemplate <typename F, typename... Args>\nusing async_invocable_inner_type_t =\n    typename async_invocable_inner_type<F, Args...>::type;\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/AsyncStack.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <glog/logging.h>\n#include <folly/CPortability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/fibers/async/Async.h>\n#include <folly/tracing/AsyncStack.h>\n\nnamespace folly::fibers::async {\nnamespace detail {\n\ntemplate <typename F>\nFOLLY_NOINLINE invoke_result_t<F> executeWithNewRoot(\n    F&& func, AsyncStackFrame* FOLLY_NULLABLE callerFrame) {\n  AsyncStackRoot newRoot;\n  newRoot.setStackFrameContext();\n\n  AsyncStackFrame frame;\n  frame.setReturnAddress();\n  if (callerFrame) {\n    frame.setParentFrame(*callerFrame);\n  }\n\n  auto* oldRoot = exchangeCurrentAsyncStackRoot(&newRoot);\n  activateAsyncStackFrame(newRoot, frame);\n\n  SCOPE_EXIT {\n    deactivateAsyncStackFrame(frame);\n    CHECK_EQ(&newRoot, exchangeCurrentAsyncStackRoot(oldRoot));\n  };\n\n  return func();\n}\n} // namespace detail\n\n/**\n * Executes `F` with a new async-stack-root, making it possible\n * to stitch stacks across async hops.\n *\n * callerFrame, if not null, must outlive the execution of `F`.\n */\ntemplate <typename F>\nAsync<async_invocable_inner_type_t<F>> executeWithNewRoot(\n    F&& func, AsyncStackFrame* FOLLY_NULLABLE callerFrame) {\n  return detail::executeWithNewRoot(std::forward<F>(func), callerFrame);\n}\n\n/**\n * Connects the execution of synchronous function `F` (on the main thread\n * context) to the calling fiber\n *\n * Note: The calling fiber must have a valid async-stack-root for meaningful\n * stacks.\n */\ntemplate <typename F>\ninvoke_result_t<F> runInMainContextWithTracing(F&& func) {\n  DCHECK(detail::onFiber());\n  auto* callerFrame = []() {\n    auto* root = tryGetCurrentAsyncStackRoot();\n    return root ? root->getTopFrame() : nullptr;\n  }();\n  return runInMainContext([&]() {\n    return detail::executeWithNewRoot(std::forward<F>(func), callerFrame);\n  });\n}\n} // namespace folly::fibers::async\n"
  },
  {
    "path": "folly/fibers/async/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"wait_utils\",\n    headers = [\n        \"WaitUtils.h\",\n    ],\n    exported_deps = [\n        \":core\",\n        \":fiber_manager\",\n        \"//folly/fibers:core\",\n        \"//folly/fibers:fiber_manager_map\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"core\",\n    srcs = [\"Async.cpp\"],\n    headers = [\n        \"Async.h\",\n    ],\n    deps = [\n        \"//folly/fibers:core\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:traits\",\n        \"//folly:unit\",\n        \"//folly/functional:invoke\",\n        \"//folly/lang:customization_point\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"collect\",\n    headers = [\n        \"Collect.h\",\n        \"Collect-inl.h\",\n    ],\n    exported_deps = [\n        \":baton\",\n        \":core\",\n        \":fiber_manager\",\n        \":future\",\n        \"//folly:traits\",\n        \"//folly:try\",\n        \"//folly/fibers:core_manager\",\n        \"//folly/fibers:when_n\",\n        \"//folly/functional:invoke\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"baton\",\n    headers = [\"Baton.h\"],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":core\",\n        \"//folly/fibers:core\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"fiber_manager\",\n    headers = [\n        \"FiberManager.h\",\n    ],\n    exported_deps = [\n        \":core\",\n        \"//folly/fibers:core_manager\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"future\",\n    headers = [\n        \"Future.h\",\n    ],\n    exported_deps = [\n        \":core\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"promise\",\n    headers = [\n        \"Promise.h\",\n    ],\n    exported_deps = [\n        \":core\",\n        \"//folly/fibers:core_manager\",\n        \"//folly/fibers:traits\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"task\",\n    headers = [\n        \"Task.h\",\n    ],\n    exported_deps = [\n        \":core\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:task\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"stack_tracing\",\n    headers = [\n        \"AsyncStack.h\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":core\",\n        \"//folly:c_portability\",\n        \"//folly:scope_guard\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n"
  },
  {
    "path": "folly/fibers/async/Baton.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <utility>\n\n#include <glog/logging.h>\n\n#include <folly/fibers/Baton.h>\n#include <folly/fibers/async/Async.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\ntemplate <typename... Args>\nAsync<void> baton_wait(Baton& baton, Args&&... args) {\n  // Call into blocking API\n  baton.wait(std::forward<Args>(args)...);\n  return {};\n}\n\ntemplate <typename... Args>\nAsync<bool> baton_try_wait_for(Baton& baton, Args&&... args) {\n  // Call into blocking API\n  return baton.try_wait_for(std::forward<Args>(args)...);\n}\n\ntemplate <typename... Args>\nAsync<bool> baton_try_wait_until(Baton& baton, Args&&... args) {\n  // Call into blocking API\n  return baton.try_wait_until(std::forward<Args>(args)...);\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME baton\n  HEADERS\n    Baton.h\n  EXPORTED_DEPS\n    folly_fibers_async_core\n    folly_fibers_core\n)\n\nfolly_add_library(\n  NAME collect\n  HEADERS\n    Collect-inl.h\n    Collect.h\n  EXPORTED_DEPS\n    folly_fibers_async_baton\n    folly_fibers_async_core\n    folly_fibers_async_fiber_manager\n    folly_fibers_async_future\n    folly_fibers_core_manager\n    folly_fibers_when_n\n    folly_functional_invoke\n    folly_traits\n    folly_try\n)\n\nfolly_add_library(\n  NAME core\n  SRCS\n    Async.cpp\n  HEADERS\n    Async.h\n  DEPS\n    folly_fibers_core\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_lang_customization_point\n    folly_traits\n    folly_unit\n)\n\nfolly_add_library(\n  NAME fiber_manager\n  HEADERS\n    FiberManager.h\n  EXPORTED_DEPS\n    folly_fibers_async_core\n    folly_fibers_core_manager\n)\n\nfolly_add_library(\n  NAME future\n  HEADERS\n    Future.h\n  EXPORTED_DEPS\n    folly_fibers_async_core\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME promise\n  HEADERS\n    Promise.h\n  EXPORTED_DEPS\n    folly_fibers_async_core\n    folly_fibers_core_manager\n    folly_fibers_traits\n)\n\nfolly_add_library(\n  NAME stack_tracing\n  HEADERS\n    AsyncStack.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_fibers_async_core\n    folly_scope_guard\n    folly_tracing_async_stack\n)\n\nfolly_add_library(\n  NAME task\n  HEADERS\n    Task.h\n  EXPORTED_DEPS\n    folly_coro_blocking_wait\n    folly_coro_task\n    folly_fibers_async_core\n)\n\nfolly_add_library(\n  NAME wait_utils\n  HEADERS\n    WaitUtils.h\n  EXPORTED_DEPS\n    folly_fibers_async_core\n    folly_fibers_async_fiber_manager\n    folly_fibers_core\n    folly_fibers_fiber_manager_map\n)\n"
  },
  {
    "path": "folly/fibers/async/Collect-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\nnamespace detail {\n/**\n * Wrapper around the input iterators to return the wrapped functors\n */\ntemplate <class InnerIterator>\nstruct await_iterator {\n  /**\n   * Wrapper around the input functor to apply `init_await` on invocation\n   */\n  struct AwaitWrapper {\n    explicit AwaitWrapper(InnerIterator it) : it_(std::move(it)) {}\n    auto operator()() { return init_await((*it_)()); }\n\n   private:\n    InnerIterator it_;\n  };\n\n  using iterator_category = std::input_iterator_tag;\n  using value_type = AwaitWrapper;\n  using difference_type = std::size_t;\n  using pointer = value_type*;\n  using reference = value_type&;\n\n  explicit await_iterator(InnerIterator it) : it_(it) {}\n\n  await_iterator& operator++() {\n    ++it_;\n    return *this;\n  }\n\n  await_iterator operator++(int) {\n    await_iterator retval = *this;\n    ++(*this);\n    return retval;\n  }\n\n  bool operator==(await_iterator other) const { return it_ == other.it_; }\n\n  bool operator!=(await_iterator other) const { return !(*this == other); }\n\n  value_type operator*() const { return AwaitWrapper(it_); }\n\n private:\n  InnerIterator it_;\n};\n\ntemplate <typename Func, typename... Ts, std::size_t... I>\nvoid foreach(std::index_sequence<I...>, Func&& func, Ts&&... ts) {\n  (func(index_constant<I>{}, std::forward<Ts>(ts)), ...);\n}\n\ntemplate <typename Out, typename T>\ntypename std::enable_if<\n    !std::is_same<invoke_result_t<T>, Async<void>>::value,\n    Async<void>>::type\nexecuteAndMaybeAssign(Out& outref, T&& task) {\n  tryEmplaceWith(outref, [task = static_cast<T&&>(task)] {\n    return init_await(task());\n  });\n  return {};\n}\n\ntemplate <typename Out, typename T>\ntypename std::enable_if<\n    std::is_same<invoke_result_t<T>, Async<void>>::value,\n    Async<void>>::type\nexecuteAndMaybeAssign(Out& outref, T&& task) {\n  tryEmplaceWith(outref, [task = static_cast<T&&>(task)] {\n    init_await(task());\n    return unit;\n  });\n  return {};\n}\n\ntemplate <typename T>\nusing collected_result_t = lift_unit_t<async_inner_type_t<invoke_result_t<T>>>;\n\ntemplate <\n    typename T,\n    typename... Ts,\n    typename TResult =\n        std::tuple<collected_result_t<T>, collected_result_t<Ts>...>>\nAsync<TResult> collectAllImpl(T&& firstTask, Ts&&... tasks) {\n  std::tuple<Try<collected_result_t<T>>, Try<collected_result_t<Ts>>...> result;\n  size_t numPending{std::tuple_size<TResult>::value};\n  Baton b;\n\n  auto taskFunc = [&](auto& outref, auto&& task) -> Async<void> {\n    await_async(\n        executeAndMaybeAssign(outref, std::forward<decltype(task)>(task)));\n\n    --numPending;\n    if (numPending == 0) {\n      b.post();\n    }\n\n    return {};\n  };\n\n  auto& fm = FiberManager::getFiberManager();\n  detail::foreach(\n      std::index_sequence_for<Ts...>{},\n      [&](auto i, auto&& task) {\n        addFiber(\n            [&]() {\n              return taskFunc(\n                  std::get<i + 1>(result), std::forward<decltype(task)>(task));\n            },\n            fm);\n      },\n      std::forward<Ts>(tasks)...);\n\n  // Use the current fiber to execute first task\n  await_async(taskFunc(std::get<0>(result), std::forward<T>(firstTask)));\n\n  // Wait for other tasks to complete\n  b.wait();\n\n  return unwrapTryTuple(result);\n}\n} // namespace detail\n\ntemplate <class InputIterator, typename FuncType, typename ResultType>\nAsync<std::vector<\n    typename std::enable_if<\n        !std::is_same<ResultType, Async<void>>::value,\n        async_inner_type_t<ResultType>>::\n        type>> inline collectAll(InputIterator first, InputIterator last) {\n  return Async(\n      folly::fibers::collectAll(\n          detail::await_iterator<InputIterator>(first),\n          detail::await_iterator<InputIterator>(last)));\n}\n\ntemplate <class InputIterator, typename FuncType, typename ResultType>\ntypename std::\n    enable_if<std::is_same<ResultType, Async<void>>::value, Async<void>>::\n        type inline collectAll(InputIterator first, InputIterator last) {\n  folly::fibers::collectAll(\n      detail::await_iterator<InputIterator>(first),\n      detail::await_iterator<InputIterator>(last));\n\n  return {};\n}\n\ntemplate <typename... Ts, typename TResult>\nAsync<TResult> collectAll(Ts&&... tasks) {\n  static_assert(sizeof...(Ts) > 0);\n  return detail::collectAllImpl(std::forward<Ts>(tasks)...);\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/Collect.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <vector>\n\n#include <folly/Traits.h>\n#include <folly/Try.h>\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/WhenN.h>\n#include <folly/fibers/async/Async.h>\n#include <folly/fibers/async/Baton.h>\n#include <folly/fibers/async/FiberManager.h>\n#include <folly/fibers/async/Future.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\n/**\n * Schedules several async annotated functors and blocks until all of these are\n * completed. If any of the functors throws an exception, this exception will be\n * re-thrown, but only when all the tasks are complete. If several throw\n * exceptions one of them will be re-thrown.\n *\n * Returns a vector of the results of the functors.\n */\ntemplate <\n    class InputIterator,\n    typename FuncType =\n        typename std::iterator_traits<InputIterator>::value_type,\n    typename ResultType = invoke_result_t<FuncType>>\nAsync<std::vector<typename std::enable_if<\n    !std::is_same<ResultType, Async<void>>::value,\n    async_inner_type_t<ResultType>>::type>>\ncollectAll(InputIterator first, InputIterator last);\n\n/**\n * collectAll specialization for functions returning void\n */\ntemplate <\n    class InputIterator,\n    typename FuncType =\n        typename std::iterator_traits<InputIterator>::value_type,\n    typename ResultType = invoke_result_t<FuncType>>\ntypename std::\n    enable_if<std::is_same<ResultType, Async<void>>::value, Async<void>>::\n        type inline collectAll(InputIterator first, InputIterator last);\n\n/**\n * collectAll version that takes a container instead of iterators for\n * convenience\n */\ntemplate <class Collection>\nauto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {\n  return collectAll(c.begin(), c.end());\n}\n\n/**\n * collectAll version that takes a varying number of functors instead of a\n * container or iterators\n */\ntemplate <typename... Ts>\nAsync<std::tuple<lift_unit_t<async_invocable_inner_type_t<Ts>>...>> collectAll(\n    Ts&&... tasks) {\n  auto future = folly::collectAllUnsafe(addFiberFuture(\n      std::forward<Ts>(tasks), FiberManager::getFiberManager())...);\n  auto tuple = await_async(futureWait(std::move(future)));\n  return Async(folly::unwrapTryTuple(std::move(tuple)));\n}\n\ntemplate <typename F>\nAsync<Try<async_invocable_inner_type_t<F>>> awaitTry(F&& func) {\n  return makeTryWithNoUnwrap([&]() { return await(func()); });\n}\n\ntemplate <typename T>\nAsync<T> fromTry(folly::Try<T>&& result) {\n  if constexpr (std::is_void_v<T>) {\n    result.throwUnlessValue();\n    return {};\n  } else {\n    return std::move(*result);\n  }\n}\n\n/*\n * Run an async-annotated functor on a new fiber, blocking the current fiber.\n *\n * Should be used sparingly to reset the fiber stack usage and avoid fiber stack\n * overflows\n */\ntemplate <typename F>\nAsync<async_invocable_inner_type_t<F>> executeOnNewFiber(F&& func) {\n  DCHECK(detail::onFiber());\n  folly::Try<async_invocable_inner_type_t<F>> result;\n  Baton baton;\n  addFiber(\n      [&, g = folly::makeGuard([&] { baton.post(); })]() -> Async<void> {\n        result = await(awaitTry(std::forward<F>(func)));\n        return {};\n      },\n      FiberManager::getFiberManager());\n  await(baton_wait(baton));\n  return fromTry(std::move(result));\n}\n\n/*\n * Run an async-annotated functor on a new fiber on remote thread,\n * blocking the current fiber.\n */\ntemplate <typename F>\nAsync<async_invocable_inner_type_t<F>> executeOnRemoteFiber(\n    F&& func, FiberManager& fm) {\n  DCHECK(detail::onFiber());\n  return futureWait(addFiberRemoteFuture(std::forward<F>(func), fm));\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n\n#include <folly/fibers/async/Collect-inl.h>\n"
  },
  {
    "path": "folly/fibers/async/FiberManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/async/Async.h>\n\n#pragma once\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\n// These functions wrap the corresponding FiberManager members.\n// The difference is that these functions accept callables which\n// return Async results.\n//\n// Implementation notes:\n// These functions don't respect some design principles of the library:\n// - Strict separation of functions running on main context and fiber context\n// - Avoid using the Awaitable of the other async frameworks in the public\n//   interface, under long-term goal of decoupling the frameworks\n// - Keep FiberManager as an implementation detail, in favor of Executor in\n//   the public interface\n//\n// These functions should ultimately live in detail::, but are public to\n// enable early adoption of the library.\n\n/**\n * Schedule an async-annotated functor to run on a fiber manager.\n */\ntemplate <typename F>\nvoid addFiber(F&& func, FiberManager& fm) {\n  fm.addTask([func = std::forward<F>(func)]() mutable {\n    return init_await(func());\n  });\n}\n\n/**\n * Schedule an async-annotated functor to run on a remote thread's\n * fiber manager.\n */\ntemplate <typename F>\nvoid addFiberRemote(F&& func, FiberManager& fm) {\n  fm.addTaskRemote([func = std::forward<F>(func)]() mutable {\n    return init_await(func());\n  });\n}\n\n/**\n * Schedule an async-annotated functor to run on a fiber manager.\n * Returns a Future for the result.\n *\n * In most cases, prefer those options instead:\n * - executeOnNewFiber: reset fiber stack usage from fiber context\n * - executeOnFiberAndWait: initialize fiber context from main context\n *   then block thread\n */\ntemplate <typename F>\nFuture<lift_unit_t<async_invocable_inner_type_t<F>>> addFiberFuture(\n    F&& func, FiberManager& fm) {\n  return fm.addTaskFuture([func = std::forward<F>(func)]() mutable {\n    return init_await(func());\n  });\n}\n\n/**\n * Schedule an async-annotated functor to run on a remote thread's\n * fiber manager.\n * Returns a future for the result.\n *\n * In most cases, prefer those options instead:\n * - executeOnRemoteFiber: wait on remote fiber from (local) fiber context\n * - executeOnRemoteFiberAndWait: wait on remote fiber from (local) main context\n */\ntemplate <typename F>\nFuture<lift_unit_t<async_invocable_inner_type_t<F>>> addFiberRemoteFuture(\n    F&& func, FiberManager& fm) {\n  return fm.addTaskRemoteFuture([func = std::forward<F>(func)]() mutable {\n    return init_await(func());\n  });\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/Future.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/fibers/async/Async.h>\n#include <folly/futures/Future.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\n/**\n * Async tagged helpers to perform blocking future operations. Ensures that\n * functions that block on futures are annotated as well.\n */\ntemplate <typename T>\nAsync<T> futureWait(SemiFuture<T>&& semi) {\n  // Any deferred work will be executed inline on main-context\n  return std::move(semi).get();\n}\n\ntemplate <typename T>\nAsync<T> futureWait(Future<T>&& fut) {\n  return std::move(fut).get();\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/Promise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/async/Async.h>\n#include <folly/fibers/traits.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\n/**\n * Async annotated wrapper around fibers::await\n */\ntemplate <typename F>\nAsync<typename FirstArgOf<F>::type::value_type> promiseWait(F&& func) {\n  // Call into blocking API\n  return fibers::await_async(std::forward<F>(func));\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/README.md",
    "content": "# folly/fibers/async: Async annotation framework for folly/fibers\n--------\nThis library provides syntactic annotations to improve developer experience while using `folly/fibers` by make fiber-concurrency explicit.\n\nThis library is designed for projects *already* using `folly/fibers`. If you are looking for a new asynchronous framework, try `folly::coro` instead.\n\n## Why use async annotations?\nFibers are designed to behave like threads, but are more lightweight using user-mode switching. `folly/fibers` is an implementation of fibers that integrates with folly's executors and futures. Fibers are switched out when they encounter blocking operations.\n\nHowever these blocking operations are unobvious and implicit in code, and therefore it can be difficult to determine if a given function can block without carefully examining all its children. Note that where a blocking thread simply waits, inefficient, potentially deadlock prone, but predictable, a blocking fiber switches to another fiber - this introduces invisible concurrency in the system.\n\nThis makes development error prone and produces bugs:\n- Codepaths can silently switch to the main thread stack, making code expected to behave as multiple threads to end up behaving as one when it blocks.\n- Blocking functions run iteratively in a for loop (performing I/Os sequentially vs fanning out)\n\nThese async annotations for fibers library were designed for two main use cases:\n\n### Improve developer experience for performance sensitive applications that must remain on fiber\nThe annotations:\n- Provide the familiar `Async/await` syntax.\n- Explicitly mark functions that can block.\n- Enforce (in debug mode) against running blocking functions on the main thread stack (the \"main context\") potentially leading to unexpected, implicit thread-blocking I/O.\n- Help developers (even unfamiliar with the codebase) avoid accidentally scheduling concurrent I/O operations in apparently serial code (for example: await in for-loop).\n- Enforce clear separation of I/O and non-I/O code paths, avoiding painful tech-debts.\n\n### Helping migrate to folly::coro\nAnnotations are a safe and 0 cost intermediate step in a migration to `folly::coro`:\n- Async codepaths are already identified and explicit\n- Required separation of I/O and non-I/O code paths is already done\n- Enforce (in debug mode) against calling (Async annotated) fibers aware code on coro context (implicitly block the thread)\n- Conversion from `Async/await` to `folly::coro` can be automated, at least partially (beware eager-return, references...)\n\n### Limitations\n- All blocking operations need to be identified manually. No protection is provided for unannotated functions\n- Annotations do not protect against fiber stack overflows. However, they can make it easier to decide whether it is safe to run code on main context.\n\n## Overview\nThis library provides two main syntactic primitives: `Async` and `await`. To declare that a function can block (and therefore must be run on fiber), its return type *must* be annotated with `Async<>`. `Async` is a simple zero cost wrapper, that must be unpacked through a call to `await`. `await` *must* be called only from fiber context (runtime enforced only in debug builds). This ensures that all annotated functions are executed on fiber, and are not executed on main-context\n\nThe library also provides APIs to convert most blocking operations encountered in fiber into `Async<>` returning function calls:\n- `promiseWait` to block on generic async callbacks. (See `Promise.h`)\n- `baton_wait` etc to block on `fibers::Baton`. (see `Baton.h`)\n- `futureWait` to wait for a `Future/SemiFuture` to become ready and execute deferred work. (see `Future.h`)\n- `taskWait` to execute block on a `coro::Task`. (see `Task.h`)\n\nNext, the library provides APIs to fan-out (see `Collect.h`) and to schedule annotated functions onto the FiberManager (see `FiberManager.h`).\n\nFinally, the library provides a handy utility to execute annotated functions on a FiberManager without the boilerplate (see `WaitUtils.h`)\n\n## Basic example\nVanilla fibers code:\n```cpp\nusing namespace folly;\n\n...\nconstexpr auto kWaitTime = std::chrono::seconds{1};\n\nauto blockingOperation1 = [&] {\n  fibers::Baton b;\n  b.try_wait_for(kWaitTime);\n};\n\nauto blockingOperation2 = [&] { futures::sleep(kWaitTime).get(); };\n\nauto blockingOperation3 = [&] {\n  coro::blockingWait(coro::co_invoke(\n      [&]() -> coro::Task<void> { co_await coro::sleep(kWaitTime); }));\n};\n\nEventBase evb;\nfibers::getFiberManager(evb)\n    .addTaskFuture([&] {\n      std::vector<Function<void()>> tasks;\n      tasks.emplace_back(blockingOperation1);\n      tasks.emplace_back(blockingOperation2);\n      tasks.emplace_back(blockingOperation3);\n      fibers::collectAll(tasks.begin(), tasks.end());\n    })\n    .getVia(&evb);\n```\n\nAnnotated fibers code:\n```cpp\nusing namespace folly;\n\nconstexpr auto kWaitTime = std::chrono::seconds{1};\n\nauto blockingOperation1 = [&]() -> fibers::async::Async<bool> {\n  fibers::Baton b;\n  return fibers::async::baton_try_wait_for(b, kWaitTime);\n};\n\nauto blockingOperation2 = [&]() -> fibers::async::Async<Unit> {\n  return fibers::async::futureWait(futures::sleep(kWaitTime));\n};\n\nauto blockingOperation3 = [&]() -> fibers::async::Async<Unit> {\n  return fibers::async::taskWait(coro::co_invoke([&]() -> coro::Task<Unit> {\n    co_await coro::sleep(kWaitTime);\n    co_return{};\n  }));\n};\n\nfibers::async::executeOnFiberAndWait([&]() -> fibers::async::Async<void> {\n  fibers::async::await(fibers::async::collectAll(\n      blockingOperation1, blockingOperation2, blockingOperation3));\n  return {};\n});\n```\n\n## Annotating an entire codebase\nTo ensure that all functions that can block are annotated, the following workflow is recommended:\n- Identify blocking operations in your code (`future.get()` etc) and use the APIs provided by the library to translate them into annotated function calls. Use `await` to unwrap the result of the function call, and annotate the current function to return `Async<>`.\n- Implement static analysis / linters that will force you annotate any function calling `await` to itself return `Async<>`.\n- It can be difficult to migrate the whole codebase in a single change, so use `init_await` instead of `await` to unpack the result of the functions annotated in the current code-change. Static analysis can be used to ensure that a function calling `init_await` should not be annotated.\n- Use the provided APIs to fan-out (`collectFibers` etc) or schedule more work onto the `FiberManager` (`addFiber`). These APIS require annotated functors as input.\n- Replace any `init_await`s with `await`s and continue annotating higher up the stack. At the end of your migration there should be no `init_await`s left in your code.\n- Finally, use `executeOnFiberAndWait` as an entry point into fiber code to ensure that even the top of the stack is annotated.\n"
  },
  {
    "path": "folly/fibers/async/Task.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Task.h>\n#include <folly/fibers/async/Async.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\n/**\n * Block on a task's execution. Should be called from an Async annotated\n * function. The fiber executing task_wait will block while the task is\n * suspended, and the task's work will be executed inline on the fiber main\n * context.\n */\ntemplate <typename T>\nAsync<T> taskWait(folly::coro::Task<T>&& task) {\n  return folly::coro::blockingWait(std::move(task));\n}\n\ninline Async<void> taskWait(folly::coro::Task<void>&& task) {\n  folly::coro::blockingWait(std::move(task));\n  return {};\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/WaitUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/FiberManagerInternal.h>\n#include <folly/fibers/FiberManagerMap.h>\n#include <folly/fibers/async/Async.h>\n#include <folly/fibers/async/FiberManager.h>\n\n#pragma once\n\nnamespace folly {\nnamespace fibers {\nnamespace async {\n\nnamespace detail {\ntemplate <typename F>\nlift_unit_t<async_invocable_inner_type_t<F>> executeOnFiberAndWait(\n    F&& func, folly::EventBase& evb, FiberManager& fm) {\n  DCHECK(!detail::onFiber());\n  return addFiberFuture(std::forward<F>(func), fm).getVia(&evb);\n}\n} // namespace detail\n\n/**\n * Run an async-annotated functor `func`, to completion on a fiber manager `fm`\n * associated with Event Base `evb`, blocking the current thread.\n *\n * Should not be called from fiber context.\n *\n * Several overloads are provided to configure the executor / fiber manager\n * options\n */\ntemplate <typename F>\nlift_unit_t<async_invocable_inner_type_t<F>> executeOnFiberAndWait(\n    F&& func, const FiberManager::Options& opts = FiberManager::Options()) {\n  folly::EventBase evb;\n  return detail::executeOnFiberAndWait(\n      std::forward<F>(func), evb, getFiberManager(evb, opts));\n}\n\ntemplate <typename F>\nlift_unit_t<async_invocable_inner_type_t<F>> executeOnFiberAndWait(\n    F&& func, const FiberManager::FrozenOptions& opts) {\n  folly::EventBase evb;\n  return detail::executeOnFiberAndWait(\n      std::forward<F>(func), evb, getFiberManager(evb, opts));\n}\n\ntemplate <typename F>\nlift_unit_t<async_invocable_inner_type_t<F>> executeOnFiberAndWait(\n    F&& func,\n    folly::EventBase& evb,\n    const FiberManager::Options& opts = FiberManager::Options()) {\n  return detail::executeOnFiberAndWait(\n      std::forward<F>(func), evb, getFiberManager(evb, opts));\n}\n\ntemplate <typename F>\nlift_unit_t<async_invocable_inner_type_t<F>> executeOnFiberAndWait(\n    F&& func, folly::EventBase& evb, const FiberManager::FrozenOptions& opts) {\n  return detail::executeOnFiberAndWait(\n      std::forward<F>(func), evb, getFiberManager(evb, opts));\n}\n\n/**\n * Run an async-annotated functor `func`, to completion on a remote\n * fiber manager, blocking the current thread.\n *\n * Should not be called from fiber context.\n *\n * This is similar to the above functions (sugar to initialize\n * Async annotated fiber context) but handle the case where the\n * library uses a dedicated thread pool to run fibers.\n */\ntemplate <typename F>\nlift_unit_t<async_invocable_inner_type_t<F>> executeOnRemoteFiberAndWait(\n    F&& func, FiberManager& fm) {\n  DCHECK(!detail::onFiber());\n  return addFiberRemoteFuture(std::forward<F>(func), fm).get();\n}\n\n} // namespace async\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/async/test/AsyncTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/async/Async.h>\n\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/FiberManagerMap.h>\n#include <folly/fibers/async/AsyncStack.h>\n#include <folly/fibers/async/Baton.h>\n#include <folly/fibers/async/Collect.h>\n#include <folly/fibers/async/FiberManager.h>\n#include <folly/fibers/async/Future.h>\n#include <folly/fibers/async/Promise.h>\n#include <folly/fibers/async/WaitUtils.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/Sleep.h>\n#include <folly/fibers/async/Task.h>\n\nusing namespace ::testing;\nusing namespace folly::fibers;\n\nnamespace {\nstd::string getString() {\n  return \"foo\";\n}\nasync::Async<void> getAsyncNothing() {\n  return {};\n}\nasync::Async<std::string> getAsyncString() {\n  return getString();\n}\nasync::Async<folly::Optional<std::string>> getOptionalAsyncString() {\n  // use move constructor to convert Async<std::string> to\n  // Async<folly::Optional<std::string>>\n  return getAsyncString();\n}\nasync::Async<std::tuple<int, float, std::string>> getTuple() {\n  return {0, 0.0, \"0\"};\n}\n\nstruct NonCopyableNonMoveable {\n  constexpr NonCopyableNonMoveable() noexcept = default;\n  ~NonCopyableNonMoveable() = default;\n\n  NonCopyableNonMoveable(const NonCopyableNonMoveable&) = delete;\n  NonCopyableNonMoveable(NonCopyableNonMoveable&&) = delete;\n  NonCopyableNonMoveable& operator=(NonCopyableNonMoveable const&) = delete;\n  NonCopyableNonMoveable& operator=(NonCopyableNonMoveable&&) = delete;\n};\n\nasync::Async<NonCopyableNonMoveable const&> getReference() {\n  thread_local NonCopyableNonMoveable const value{};\n\n  return value;\n}\n\n} // namespace\n\nTEST(AsyncTest, asyncAwait) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  EXPECT_NO_THROW(\n      fm.addTaskFuture([&]() {\n          async::init_await(getAsyncNothing());\n          EXPECT_EQ(getString(), async::init_await(getAsyncString()));\n          EXPECT_EQ(getString(), *async::init_await(getOptionalAsyncString()));\n          async::init_await(getTuple());\n          decltype(auto) ref = async::init_await(getReference());\n          static_assert(\n              std::is_same<decltype(ref), NonCopyableNonMoveable const&>::value,\n              \"\");\n        }).getVia(&evb));\n}\n\nTEST(AsyncTest, asyncBaton) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  std::chrono::steady_clock::time_point start;\n\n  EXPECT_NO_THROW(\n      fm.addTaskFuture([&]() {\n          constexpr auto kTimeout = std::chrono::milliseconds(230);\n          {\n            Baton baton;\n            baton.post();\n            EXPECT_NO_THROW(async::await(async::baton_wait(baton)));\n          }\n          {\n            Baton baton;\n            start = std::chrono::steady_clock::now();\n            auto res = async::await(async::baton_try_wait_for(baton, kTimeout));\n            EXPECT_FALSE(res);\n            EXPECT_LE(start + kTimeout, std::chrono::steady_clock::now());\n          }\n          {\n            Baton baton;\n            start = std::chrono::steady_clock::now();\n            auto res = async::await(\n                async::baton_try_wait_until(baton, start + kTimeout));\n            EXPECT_FALSE(res);\n            EXPECT_LE(start + kTimeout, std::chrono::steady_clock::now());\n          }\n        }).getVia(&evb));\n}\n\nTEST(AsyncTest, asyncPromise) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  fm.addTaskFuture([&]() {\n      auto res = async::await(async::promiseWait([](Promise<int> p) {\n        p.setValue(42);\n      }));\n      EXPECT_EQ(res, 42);\n    }).getVia(&evb);\n}\n\nTEST(AsyncTest, asyncFuture) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  // Return format: Info about where continuation runs (thread_id,\n  // in_fiber_loop, on_active_fiber)\n  auto getSemiFuture = []() {\n    return folly::futures::sleep(std::chrono::milliseconds(1))\n        .defer([](auto&&) {\n          return std::make_tuple(\n              std::this_thread::get_id(),\n              FiberManager::getFiberManagerUnsafe() != nullptr,\n              onFiber());\n        });\n  };\n\n  fm.addTaskFuture([&]() {\n      auto this_thread_id = std::this_thread::get_id();\n      {\n        // Unspecified executor: Deferred work will be executed inline on\n        // producer thread\n        auto res = async::init_await(\n            async::futureWait(getSemiFuture().toUnsafeFuture()));\n        EXPECT_NE(this_thread_id, std::get<0>(res));\n        EXPECT_FALSE(std::get<1>(res));\n        EXPECT_FALSE(std::get<2>(res));\n      }\n\n      {\n        // Specified executor: Deferred work will be executed on evb\n        auto res =\n            async::init_await(async::futureWait(getSemiFuture().via(&evb)));\n        EXPECT_EQ(this_thread_id, std::get<0>(res));\n        EXPECT_FALSE(std::get<1>(res));\n        EXPECT_FALSE(std::get<2>(res));\n      }\n\n      {\n        // Unspecified executor: Deferred work will be executed inline on\n        // consumer thread main-context\n        auto res = async::init_await(async::futureWait(getSemiFuture()));\n        EXPECT_EQ(this_thread_id, std::get<0>(res));\n        EXPECT_TRUE(std::get<1>(res));\n        EXPECT_FALSE(std::get<2>(res));\n      }\n    }).getVia(&evb);\n}\n\n#if FOLLY_HAS_COROUTINES\nTEST(AsyncTest, asyncTask) {\n  auto coroFn =\n      []() -> folly::coro::Task<std::tuple<std::thread::id, bool, bool>> {\n    co_await folly::coro::sleep(std::chrono::milliseconds(1));\n    co_return std::make_tuple(\n        std::this_thread::get_id(),\n        FiberManager::getFiberManagerUnsafe() != nullptr,\n        onFiber());\n  };\n\n  auto voidCoroFn = []() -> folly::coro::Task<void> {\n    co_await folly::coro::sleep(std::chrono::milliseconds(1));\n  };\n\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  fm.addTaskFuture([&]() {\n      // Coroutine should run to completion on fiber main context\n      EXPECT_EQ(\n          std::make_tuple(std::this_thread::get_id(), true, false),\n          async::init_await(async::taskWait(coroFn())));\n      async::init_await(async::taskWait(voidCoroFn()));\n    }).getVia(&evb);\n}\n#endif\n\nTEST(AsyncTest, asyncTraits) {\n  static_assert(!async::is_async_v<int>);\n  static_assert(async::is_async_v<async::Async<int>>);\n  static_assert(\n      std::is_same<int, async::async_inner_type_t<async::Async<int>>>::value);\n  static_assert(\n      std::is_same<int&, async::async_inner_type_t<async::Async<int&>>>::value);\n}\n\nTEST(AsyncTest, asyncConstructorGuides) {\n  auto getLiteral = []() { return async::Async(1); };\n  // int&& -> int\n  static_assert(\n      std::is_same<int, async::async_inner_type_t<decltype(getLiteral())>>::\n          value);\n\n  int i = 0;\n  auto tryGetRef = [&]() { return async::Async(static_cast<int&>(i)); };\n  // int& -> int\n  static_assert(\n      std::is_same<int, async::async_inner_type_t<decltype(tryGetRef())>>::\n          value);\n\n  auto tryGetConstRef = [&]() {\n    return async::Async(static_cast<const int&>(i));\n  };\n  // const int& -> int\n  static_assert(\n      std::is_same<int, async::async_inner_type_t<decltype(tryGetConstRef())>>::\n          value);\n\n  // int& explicitly constructed\n  auto getRef = [&]() { return async::Async<int&>(i); };\n  static_assert(\n      std::is_same<int&, async::async_inner_type_t<decltype(getRef())>>::value);\n}\n\nTEST(FiberManager, asyncFiberManager) {\n  {\n    folly::EventBase evb;\n    bool completed = false;\n    async::addFiberFuture(\n        [&]() -> async::Async<void> {\n          completed = true;\n          return {};\n        },\n        getFiberManager(evb))\n        .getVia(&evb);\n    EXPECT_TRUE(completed);\n\n    size_t count = 0;\n    EXPECT_EQ(\n        1,\n        async::addFiberFuture(\n            [count]() mutable -> async::Async<int> { return ++count; },\n            getFiberManager(evb))\n            .getVia(&evb));\n  }\n\n  {\n    bool outerCompleted = false;\n    async::executeOnFiberAndWait([&]() -> async::Async<void> {\n      bool innerCompleted = false;\n      async::await(async::executeOnNewFiber([&]() -> async::Async<void> {\n        innerCompleted = true;\n        return {};\n      }));\n      EXPECT_TRUE(innerCompleted);\n      outerCompleted = true;\n      return {};\n    });\n    EXPECT_TRUE(outerCompleted);\n  }\n\n  {\n    bool outerCompleted = false;\n    bool innerCompleted = false;\n    async::executeOnFiberAndWait([&]() -> async::Async<void> {\n      outerCompleted = true;\n      async::addFiber(\n          [&]() -> async::Async<void> {\n            innerCompleted = true;\n            return {};\n          },\n          FiberManager::getFiberManager());\n      return {};\n    });\n    EXPECT_TRUE(outerCompleted);\n    EXPECT_TRUE(innerCompleted);\n  }\n}\n\nTEST(AsyncTest, collect) {\n  auto makeVoidTask = [](bool& ref) {\n    return [&]() -> async::Async<void> {\n      ref = true;\n      return {};\n    };\n  };\n  auto makeRetTask = [](int val) {\n    return [=]() -> async::Async<int> { return val; };\n  };\n  async::executeOnFiberAndWait([&]() -> async::Async<void> {\n    {\n      std::array<bool, 3> cs{{false, false, false}};\n      std::vector<folly::Function<async::Async<void>()>> tasks;\n      tasks.emplace_back(makeVoidTask(cs[0]));\n      tasks.emplace_back(makeVoidTask(cs[1]));\n      tasks.emplace_back(makeVoidTask(cs[2]));\n      async::await(async::collectAll(tasks));\n      EXPECT_THAT(cs, ElementsAre(true, true, true));\n    }\n\n    {\n      std::vector<folly::Function<async::Async<int>()>> tasks;\n      tasks.emplace_back(makeRetTask(1));\n      tasks.emplace_back(makeRetTask(2));\n      tasks.emplace_back(makeRetTask(3));\n      EXPECT_THAT(async::await(async::collectAll(tasks)), ElementsAre(1, 2, 3));\n    }\n\n    {\n      std::array<bool, 3> cs{{false, false, false}};\n      async::await(\n          async::collectAll(\n              makeVoidTask(cs[0]), makeVoidTask(cs[1]), makeVoidTask(cs[2])));\n      EXPECT_THAT(cs, ElementsAre(true, true, true));\n    }\n\n    {\n      EXPECT_EQ(\n          std::make_tuple(1, 2, 3),\n          async::await(\n              async::collectAll(\n                  makeRetTask(1), makeRetTask(2), makeRetTask(3))));\n    }\n\n    return {};\n  });\n}\n\nTEST(FiberManager, remoteFiberManager) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  bool test1Finished = false;\n  bool test2Finished = false;\n  std::atomic<bool> remoteFinished = false;\n  std::thread remote([&]() {\n    async::executeOnRemoteFiberAndWait(\n        [&]() -> async::Async<void> {\n          test1Finished = true;\n          return {};\n        },\n        fm);\n\n    async::executeOnFiberAndWait([&] {\n      return async::executeOnRemoteFiber(\n          [&]() -> async::Async<void> {\n            test2Finished = true;\n            return {};\n          },\n          fm);\n    });\n\n    remoteFinished = true;\n  });\n\n  while (!remoteFinished) {\n    evb.loopOnce();\n  }\n  remote.join();\n\n  EXPECT_TRUE(test1Finished);\n  EXPECT_TRUE(test2Finished);\n}\n\nfolly::AsyncStackFrame* FOLLY_NULLABLE currentFrame() {\n  auto* root = folly::tryGetCurrentAsyncStackRoot();\n  return root ? root->getTopFrame() : nullptr;\n}\n\nFOLLY_NOINLINE std::vector<std::uintptr_t> getAsyncStack() {\n  static constexpr size_t maxFrames = 100;\n  std::array<std::uintptr_t, maxFrames> result;\n\n  result[0] =\n      reinterpret_cast<std::uintptr_t>(FOLLY_ASYNC_STACK_RETURN_ADDRESS());\n  auto numFrames = folly::getAsyncStackTraceFromInitialFrame(\n      currentFrame(), result.data() + 1, maxFrames - 1);\n\n  return std::vector<std::uintptr_t>(\n      std::make_move_iterator(result.begin()),\n      std::make_move_iterator(result.begin()) + numFrames + 1);\n}\n\ntemplate <typename F>\nvoid expectStackSize(size_t size, F&& func) {\n  EXPECT_EQ(size, func().size());\n}\n\nTEST(AsyncStack, fiberEntryPoint) {\n  // Only frame will be getAsyncStack. No connection to callers\n  expectStackSize(1, []() {\n    return async::executeOnFiberAndWait(\n        []() -> async::Async<std::vector<std::uintptr_t>> {\n          // fiber\n          return getAsyncStack();\n        });\n  });\n\n  // [0] - getAsyncStack\n  // [1] - fiber\n  expectStackSize(2, []() {\n    return async::executeOnFiberAndWait([]() {\n      return async::executeWithNewRoot(\n          [&]() -> async::Async<std::vector<std::uintptr_t>> {\n            // fiber\n            return getAsyncStack();\n          },\n          currentFrame());\n    });\n  });\n}\n\nTEST(AsyncStack, awaitTry) {\n  async::executeOnFiberAndWait([]() -> async::Async<void> {\n    auto res =\n        async::await(async::awaitTry([]() -> async::Async<int> { return 1; }));\n    EXPECT_EQ(*res, 1);\n    res = async::await(async::awaitTry([]() -> async::Async<int> {\n      throw std::logic_error(\"\");\n    }));\n    EXPECT_THROW(*res, std::logic_error);\n\n    auto void_res = async::await(async::awaitTry([]() -> async::Async<void> {\n      return {};\n    }));\n    EXPECT_NO_THROW(*void_res);\n    void_res = async::await(async::awaitTry([]() -> async::Async<void> {\n      throw std::logic_error(\"\");\n    }));\n    EXPECT_THROW(*void_res, std::logic_error);\n\n    auto try_res = async::await(\n        async::awaitTry([]() -> async::Async<folly::Try<int>> { return 1; }));\n    EXPECT_EQ(**try_res, 1);\n    try_res =\n        async::await(async::awaitTry([]() -> async::Async<folly::Try<int>> {\n          return folly::makeTryWith([]() -> int {\n            throw std::logic_error(\"\");\n          });\n        }));\n    EXPECT_NO_THROW(*try_res);\n    EXPECT_THROW(**try_res, std::logic_error);\n    return {};\n  });\n}\n\nTEST(AsyncStack, fiberToFiber) {\n  // Only frame will be getAsyncStack. No connection to callers\n  expectStackSize(1, []() {\n    return async::executeOnFiberAndWait([]() {\n      // fiber1\n      return async::executeOnNewFiber(\n          []() -> async::Async<std::vector<std::uintptr_t>> {\n            // fiber2\n            return getAsyncStack();\n          });\n    });\n  });\n\n  // [0] - getAsyncStack\n  // [1] - fiber2\n  // [2] - fiber1\n  expectStackSize(3, []() {\n    return async::executeOnFiberAndWait([]() {\n      return async::executeWithNewRoot(\n          [&]() {\n            // fiber1\n            auto* cf = CHECK_NOTNULL(currentFrame());\n            return async::executeOnNewFiber([cf]() {\n              return async::executeWithNewRoot(\n                  []() -> async::Async<std::vector<std::uintptr_t>> {\n                    // fiber2\n                    return getAsyncStack();\n                  },\n                  cf);\n            });\n          },\n          currentFrame());\n    });\n  });\n}\n\nTEST(AsyncStack, fiberToMainContext) {\n  // Only frame will be getAsyncStack. No connection to callers\n  expectStackSize(1, []() {\n    return async::executeOnFiberAndWait(\n        []() -> async::Async<std::vector<std::uintptr_t>> {\n          // fiber\n          return runInMainContext([]() { return getAsyncStack(); });\n        });\n  });\n\n  // [0] - getAsyncStack\n  // [1] - mainContext\n  // [2] - fiber\n  expectStackSize(3, []() {\n    return async::executeOnFiberAndWait([]() {\n      return async::executeWithNewRoot(\n          [&]() -> async::Async<std::vector<std::uintptr_t>> {\n            // fiber\n            return async::runInMainContextWithTracing([]() {\n              // mainContext\n              return getAsyncStack();\n            });\n          },\n          currentFrame());\n    });\n  });\n}\n\n#if FOLLY_HAS_COROUTINES\nTEST(AsyncStack, coroToFiber) {\n  // Only frame will be getAsyncStack. No connection to callers\n  expectStackSize(1, [&]() {\n    folly::EventBase evb;\n    return folly::coro::blockingWait(\n        [&]() -> folly::coro::Task<std::vector<std::uintptr_t>> {\n          // coro\n          co_return co_await async::addFiberFuture(\n              []() -> async::Async<std::vector<std::uintptr_t>> {\n                // fiber\n                return getAsyncStack();\n              },\n              getFiberManager(evb));\n        }(),\n        &evb);\n  });\n\n  // [0] - getAsyncStack\n  // [1] - fiber\n  // [2] - coro\n  // [3] - BlockingWaitTask\n  // [4] - blockingWait\n  expectStackSize(5, [&]() {\n    folly::EventBase evb;\n    return folly::coro::blockingWait(\n        [&]() -> folly::coro::Task<std::vector<std::uintptr_t>> {\n          // coro\n          auto* cf = currentFrame();\n          co_return co_await async::addFiberFuture(\n              [cf]() {\n                return async::executeWithNewRoot(\n                    []() -> async::Async<std::vector<std::uintptr_t>> {\n                      // fiber\n                      return getAsyncStack();\n                    },\n                    cf);\n              },\n              getFiberManager(evb));\n        }(),\n        &evb);\n  });\n}\n#endif\n"
  },
  {
    "path": "folly/fibers/async/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_test\",\n    srcs = [\"AsyncTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:sleep\",\n        \"//folly/fibers:core_manager\",\n        \"//folly/fibers:fiber_manager_map\",\n        \"//folly/fibers/async:baton\",\n        \"//folly/fibers/async:collect\",\n        \"//folly/fibers/async:core\",\n        \"//folly/fibers/async:fiber_manager\",\n        \"//folly/fibers/async:future\",\n        \"//folly/fibers/async:promise\",\n        \"//folly/fibers/async:stack_tracing\",\n        \"//folly/fibers/async:task\",\n        \"//folly/fibers/async:wait_utils\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/fibers/detail/AtomicBatchDispatcher.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/detail/AtomicBatchDispatcher.h>\n\n#include <cassert>\n\n#include <fmt/core.h>\n\nnamespace folly {\nnamespace fibers {\nnamespace detail {\n\nstd::string createABDTokenNotDispatchedExMsg(\n    const std::vector<size_t>& vecTokensNotDispatched) {\n  size_t numTokensNotDispatched = vecTokensNotDispatched.size();\n  assert(numTokensNotDispatched > 0);\n  size_t numSeqNumToPrint =\n      (numTokensNotDispatched > 10 ? 10 : numTokensNotDispatched);\n  std::string strInputsNotFound = fmt::format(\"{}\", vecTokensNotDispatched[0]);\n  for (size_t i = 1; i < numSeqNumToPrint; ++i) {\n    strInputsNotFound += fmt::format(\", {}\", vecTokensNotDispatched[i]);\n  }\n  if (numSeqNumToPrint < numTokensNotDispatched) {\n    strInputsNotFound += \"...\";\n  }\n  return fmt::format(\n      \"{} input tokens (seq nums: {}) destroyed before calling dispatch\",\n      numTokensNotDispatched,\n      strInputsNotFound);\n}\n\nstd::string createUnexpectedNumResultsABDUsageExMsg(\n    size_t numExpectedResults, size_t numActualResults) {\n  return fmt::format(\n      \"Unexpected number of results ({}) returned from dispatch function, \"\n      \"expected ({})\",\n      numActualResults,\n      numExpectedResults);\n}\n\n} // namespace detail\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/detail/AtomicBatchDispatcher.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\nnamespace folly {\nnamespace fibers {\nnamespace detail {\n\nstd::string createABDTokenNotDispatchedExMsg(\n    const std::vector<size_t>& vecTokensNotDispatched);\n\nstd::string createUnexpectedNumResultsABDUsageExMsg(\n    size_t numExpectedResults, size_t numActualResults);\n\n} // namespace detail\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/fibers/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"atomic_batch_dispatcher\",\n    srcs = [\"AtomicBatchDispatcher.cpp\"],\n    headers = [\"AtomicBatchDispatcher.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n    ],\n)\n"
  },
  {
    "path": "folly/fibers/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME atomic_batch_dispatcher\n  SRCS\n    AtomicBatchDispatcher.cpp\n  HEADERS\n    AtomicBatchDispatcher.h\n)\n"
  },
  {
    "path": "folly/fibers/scripts/gdb.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport collections\nimport functools\nimport itertools\n\nimport gdb\nimport gdb.printing\nimport gdb.types\nimport gdb.unwinder\nimport gdb.xmethod\n\n\n#\n# Pretty Printers\n#\n\n\nclass FiberPrinter:\n    \"\"\"PrettyPrint a folly::fibers::Fiber\"\"\"\n\n    def __init__(self, val):\n        self.val = val\n\n        state = self.val[\"state_\"]\n        d = gdb.types.make_enum_dict(state.type)\n        d = {v: k for k, v in d.items()}\n        self.state = d[int(state)]\n\n    def state_to_string(self):\n        if self.state == \"folly::fibers::Fiber::INVALID\":\n            return \"Invalid\"\n        if self.state == \"folly::fibers::Fiber::NOT_STARTED\":\n            return \"Not started\"\n        if self.state == \"folly::fibers::Fiber::READY_TO_RUN\":\n            return \"Ready to run\"\n        if self.state == \"folly::fibers::Fiber::RUNNING\":\n            return \"Running\"\n        if self.state == \"folly::fibers::Fiber::AWAITING\":\n            return \"Awaiting\"\n        if self.state == \"folly::fibers::Fiber::AWAITING_IMMEDIATE\":\n            return \"Awaiting immediate\"\n        if self.state == \"folly::fibers::Fiber::YIELDED\":\n            return \"Yielded\"\n        return \"Unknown\"\n\n    def backtrace_available(self):\n        return (\n            self.state != \"folly::fibers::Fiber::INVALID\"\n            and self.state != \"folly::fibers::Fiber::NOT_STARTED\"\n            and self.state != \"folly::fibers::Fiber::RUNNING\"\n        )\n\n    def to_string(self):\n        return 'folly::fibers::Fiber(state=\"{state}\", backtrace={bt})'.format(\n            state=self.state_to_string(), bt=self.backtrace_available()\n        )\n\n\nclass FiberManagerPrinter:\n    \"\"\"PrettyPrint a folly::fibers::FiberManager\"\"\"\n\n    def __init__(self, fm):\n        self.fm = fm\n\n    def children(self):\n        def limit_with_dots(fibers_iterator):\n            num_items = 0\n            fiber_print_limit = gdb.parameter(\"fiber manager-print-limit\")\n            for fiber in fibers_iterator:\n                if fiber_print_limit and num_items >= fiber_print_limit:\n                    yield (\"address\", \"...\")\n                    yield (\"fiber\", \"...\")\n                    return\n                yield (\"address\", str(fiber.address))\n                yield (\"fiber\", fiber)\n                num_items += 1\n\n        return limit_with_dots(fiber_manager_active_fibers(self.fm))\n\n    def to_string(self):\n        return \"folly::fibers::FiberManager\"\n\n    def display_hint(self):\n        return \"map\"\n\n\n#\n# XMethods\n#\n\n\nclass GetFiberXMethodWorker(gdb.xmethod.XMethodWorker):\n    def get_arg_types(self):\n        return gdb.lookup_type(\"int\")\n\n    def get_result_type(self):\n        return gdb.lookup_type(\"int\")\n\n    def __call__(self, *args):\n        fm = args[0]\n        index = int(args[1])\n        fiber = next(itertools.islice(fiber_manager_active_fibers(fm), index, None))\n        if fiber is None:\n            raise gdb.GdbError(\"Index out of range\")\n        else:\n            return fiber\n\n\nclass GetFiberXMethodMatcher(gdb.xmethod.XMethodMatcher):\n    def __init__(self):\n        super().__init__(\"Fiber address method matcher\")\n        self.worker = GetFiberXMethodWorker()\n\n    def match(self, class_type, method_name):\n        if (\n            class_type.name == \"folly::fibers::FiberManager\"\n            and method_name == \"get_fiber\"\n        ):\n            return self.worker\n        return None\n\n\nclass FiberXMethodWorker(gdb.xmethod.XMethodWorker):\n    def get_arg_types(self):\n        return None\n\n    def get_result_type(self):\n        return None\n\n    def __call__(self, *args):\n        return fiber_activate(args[0])\n\n\nclass FiberXMethodMatcher(gdb.xmethod.XMethodMatcher):\n    def __init__(self):\n        super().__init__(\"Fiber method matcher\")\n        self.worker = FiberXMethodWorker()\n\n    def match(self, class_type, method_name):\n        if class_type.name == \"folly::fibers::Fiber\" and method_name == \"activate\":\n            return self.worker\n        return None\n\n\n#\n# Unwinder\n#\n\n\nclass FrameId:\n    def __init__(self, sp, pc):\n        self.sp = sp\n        self.pc = pc\n\n\nclass FiberUnwinder(gdb.unwinder.Unwinder):\n    instance = None\n\n    @classmethod\n    def init(cls):\n        if cls.instance is None:\n            cls.instance = FiberUnwinder()\n            gdb.unwinder.register_unwinder(None, cls.instance)\n\n    @classmethod\n    def get_fiber(cls):\n        cls.init()\n        return cls.instance.fiber\n\n    @classmethod\n    def set_fiber(cls, fiber):\n        cls.init()\n\n        if not fiber:\n            if not cls.instance.fiber:\n                return \"No active fiber.\"\n\n        # get the relevant fiber's info, new one if specified, old one if not\n        info = FiberInfo(fiber or cls.instance.fiber)\n\n        if fiber:\n            if not FiberPrinter(fiber).backtrace_available():\n                return \"Can not activate a non-waiting fiber.\"\n            # set the unwinder to do the right thing\n            cls.instance.fiber_context_ptr = fiber[\"fiberImpl_\"][\"fiberContext_\"]\n        else:\n            # disable the unwinder\n            cls.instance.fiber_context_ptr = None\n\n        # track the active fiber (or lack thereof)\n        cls.instance.fiber = fiber\n\n        # Clear frame cache to make sure we actually use the right unwinder\n        gdb.invalidate_cached_frames()\n\n        return \"[{action} fiber {id} ({info})]\".format(\n            action=\"Switching to\" if fiber else \"Deactivating\", id=info.id, info=info\n        )\n\n    def __init__(self):\n        super().__init__(\"Fiber unwinder\")\n        self.fiber_context_ptr = None\n        self.fiber = None\n\n    def __call__(self, pending_frame):\n        # We only unwind the first frame, bail if already done\n        if not self.fiber_context_ptr:\n            return None\n\n        orig_rsp = pending_frame.read_register(\"rsp\")\n        orig_rip = pending_frame.read_register(\"rip\")\n\n        void_star_star = gdb.lookup_type(\"uint64_t\").pointer()\n        ptr = self.fiber_context_ptr.cast(void_star_star)\n\n        # This code may need to be adjusted to newer versions of boost::context.\n        #\n        # The easiest way to get these offsets is to first attach to any\n        # program which uses folly::fibers and add a break point in\n        # boost::context::jump_fcontext. You then need to save information about\n        # frame 1 via 'info frame 1' command.\n        #\n        # After that you need to resume program until fiber switch is complete\n        # and expore the contents of saved fiber context via\n        # 'x/16gx {fiber pointer}->fiberImpl_.fiberContext_' command.\n        # You then need to match those to the following values you've previously\n        # observed in the output of 'info frame 1'  command.\n        #\n        # Value found at \"rbp at X\" of 'info frame 1' output:\n        rbp = (ptr + 6).dereference()\n        # Value found at \"rip = X\" of 'info frame 1' output:\n        rip = (ptr + 7).dereference()\n        # Value found at \"caller of frame at X\" of 'info frame 1' output:\n        rsp = rbp - 96\n\n        # we play a horrible trick on gdb here, to make it unwind the fiber rather\n        # than the stack of the currently selected frame.  Essentially, we take\n        # whatever frame is currently newest, and lie to gdb saying that we are\n        # the \"next older\" frame by creating fake unwind info that points to our\n        # fiber's frame.  This means that frame #0 is actually from the currently\n        # selected thread, so we filter that out when we inspect it elsewhere.\n        frame_id = FrameId(orig_rsp, orig_rip)\n        unwind_info = pending_frame.create_unwind_info(frame_id)\n        unwind_info.add_saved_register(\"rbp\", rbp)\n        unwind_info.add_saved_register(\"rsp\", rsp)\n        unwind_info.add_saved_register(\"rip\", rip)\n\n        # \"unregister\" ourselves from further frame processing; we only need to\n        # handle the top-level sentinel frame\n        self.fiber_context_ptr = None\n\n        return unwind_info\n\n\nclass FiberFrameFilter:\n    \"\"\"Frame filter for fiber stacks\n\n    This class is used to \"skip\" past the innermost frame when parsing backtraces,\n    which is actually from the currently selected thread.\n    \"\"\"\n\n    def __init__(self):\n        self.name = \"fibers-skip-first-frame\"\n        self.priority = 100\n        self.enabled = True\n        gdb.frame_filters[self.name] = self\n\n    def filter(self, frames):\n        # skip first frame filter if a fiber is active\n        if FiberUnwinder.get_fiber():\n            next(frames)\n        return frames\n\n\n#\n# Commands\n#\n\n\n# small utility to make sure a method always runs in a particular language mode\n# and sets it back when donex\ndef use_language(lang):\n    def wrapper(fn):\n        @functools.wraps(fn)\n        def wrapped(*args, **kwds):\n            orig = gdb.parameter(\"language\")\n            if lang == orig:\n                return fn(*args, **kwds)\n            try:\n                gdb.execute(\"set language \" + lang)\n                return fn(*args, **kwds)\n            finally:\n                gdb.execute(\"set language \" + orig)\n\n        return wrapped\n\n    return wrapper\n\n\nclass FiberCommand(gdb.Command):\n    \"\"\"Use this command to switch between fibers.\n\n    Due to gdb limitations, this will *override* the current thread, and you will\n    need to run \"fiber deactivate\" in order to get normal thread processing back.\n    \"\"\"\n\n    def __init__(self):\n        super().__init__(\"fiber\", gdb.COMMAND_USER, prefix=True)\n\n    @use_language(\"c++\")\n    def invoke(self, arg, from_tty):\n        self.dont_repeat()\n        argv = gdb.string_to_argv(arg)\n\n        if not argv:\n            if not FiberUnwinder.instance or not FiberUnwinder.instance.fiber:\n                print(\"No fiber selected\")\n            else:\n                info = FiberInfo(FiberUnwinder.instance.fiber)\n                print(f\"[Current fiber is {info.id} ({info})]\")\n            return\n\n        # look up fiber\n        fiber_ptr = get_fiber(arg)\n        if not fiber_ptr:\n            print(\"Invalid fiber id or address: \" + arg)\n            return\n\n        # activate\n        print(fiber_activate(fiber_ptr))\n\n\nclass FiberNameCommand(gdb.Command):\n    \"\"\"Set the current fiber's name.\n\n    Usage: fiber name [NAME]\n    If NAME is not given, then any existing name is removed\"\"\"\n\n    def __init__(self):\n        super().__init__(\"fiber name\", gdb.COMMAND_USER)\n\n    def invoke(self, arg, from_tty):\n        fiber = FiberUnwinder.get_fiber()\n        if not fiber:\n            print(\"No fiber activated.\")\n            return\n        FiberInfo(fiber).name = arg or None\n\n\nclass FiberInfoCommand(gdb.Command):\n    \"\"\"info on fibers\n\n    Can filter output with \"M.F\" or \"M\" format, e.g. \"2.3\" for manager 2 fiber 3\n    or \"1\" for all fibers from manager 1. Can specify multiple times.\n\n    See `help set fiber` for ways to alter the information printed.\n    \"\"\"\n\n    def __init__(self):\n        super().__init__(\"info fibers\", gdb.COMMAND_STATUS, prefix=True)\n\n    def trace_info(self, fiber, skip=()):\n        # get the fiber symbol info if we can\n        if not FiberPrinter(fiber).backtrace_available():\n            return \"[backtrace unavailable]\"\n\n        # save original fiber state\n        orig = FiberUnwinder.get_fiber()\n        try:\n            fiber_activate(fiber)\n            # first frame is always bogus\n            frame = gdb.selected_frame().older()\n            frame_id = 1\n\n            # find last \"useful\" frame for display\n            while any(word in frame.name() for word in skip):\n                # check for unwinding failures and if so use selected frame\n                if frame.unwind_stop_reason() != gdb.FRAME_UNWIND_NO_REASON:\n                    frame = gdb.selected_frame()\n                    break\n                frame = frame.older()\n                frame_id += 1\n\n            if not frame.is_valid():\n                return \"[invalid frame]\"\n\n            sal = frame.find_sal()\n            return \"#{frame_id} 0x{pc:016x} in {function} at {filename}:{line}\".format(\n                frame_id=frame_id,\n                pc=frame.pc(),\n                function=frame.function(),\n                filename=sal.symtab.filename,\n                line=sal.line,\n            )\n        finally:\n            # restore original fiber state\n            FiberUnwinder.set_fiber(orig)\n\n    @use_language(\"c++\")\n    def invoke(self, arg, to_tty):\n        only = gdb.string_to_argv(arg)\n        orig = FiberUnwinder.get_fiber()\n        frame_skip_words = gdb.parameter(\"fiber info-frame-skip-words\").split(\" \")\n\n        seen = set()\n        for (mid, fid), info in get_fiber_info(only, managers=True):\n            # track that we've seen this fiber/manager\n            if fid:\n                seen.add(f\"{mid}.{fid}\")\n                seen.add(str(mid))\n\n            # If it's a manager, print the header and continue\n            if not fid:\n                manager = info\n                print(\n                    \"  {mid:<4} {fmtype} {address}\".format(\n                        mid=mid, fmtype=manager.type, address=manager.address\n                    )\n                )\n                continue\n\n            # print the fiber info\n            print(\n                \"{active}   {id:<6} {info} {trace_info}\".format(\n                    active=\"*\" if info.fiber == orig else \" \",\n                    id=info.id,\n                    info=info,\n                    trace_info=self.trace_info(info.fiber, frame_skip_words),\n                )\n            )\n\n        # print warning if there are no fibers\n        if not seen:\n            print(\"No fibers.\")\n\n        # print warnings for any filters we didn't find\n        for match in only or []:\n            if match not in seen:\n                if \".\" in match:\n                    print(\"Invalid fiber id: \" + match)\n                else:\n                    print(\"Invalid fiber manager id: \" + match)\n\n\nclass FiberApplyCommand(gdb.Command):\n    \"\"\"Apply a command to a list of fibers.\n\n    Usage: fiber apply ( ( FIBER_ID | MANAGER_ID ) [ ... ] | all ) COMMAND\n\n    This will cause each specified FIBER_ID (or all fibers for a specified MANAGER_ID)\n    to be activated in turn, running the specified gdb COMMAND after each activation.\n    \"\"\"\n\n    def __init__(self):\n        super().__init__(\"fiber apply\", gdb.COMMAND_USER)\n\n    def usage(self):\n        raise gdb.Error(\"usage: fiber apply [ FIBER_FILTER [ ... ] | apply ] COMMAND\")\n\n    @use_language(\"c++\")\n    def invoke(self, arg, from_tty):\n        self.dont_repeat()\n        argv = gdb.string_to_argv(arg)\n\n        if not argv:\n            return self.usage()\n\n        # parse fiber ids and command\n        targets = []\n        if argv[0] == \"all\":\n            argv.pop(0)  # use empty filter for all fibers\n        else:\n            while argv and argv[0][0].isdigit():\n                targets.append(argv.pop(0))\n        command = \" \".join(argv)\n        if not argv:\n            return self.usage()\n\n        # save original fiber state\n        orig = FiberUnwinder.get_fiber()\n        try:\n            # loop over, activating and running command in turn\n            for _, info in get_fiber_info(targets):\n                print(f\"Fiber {info.id} ({info})\")\n                fiber_activate(info.fiber)\n                gdb.execute(command, from_tty=from_tty)\n        finally:\n            # restore original fiber state\n            FiberUnwinder.set_fiber(orig)\n\n\nclass FiberDeactivateCommand(gdb.Command):\n    \"\"\"Deactivates fiber processing, returning to normal thread processing\"\"\"\n\n    def __init__(self):\n        super().__init__(\"fiber deactivate\", gdb.COMMAND_USER)\n\n    def invoke(self, arg, from_tty):\n        if arg:\n            print(\"fiber deactivate takes no arguments.\")\n            return\n        print(fiber_deactivate())\n\n\n#\n# Parameters\n#\n\n\nclass SetFiberCommand(gdb.Command):\n    \"\"\"Generic command for setting how fibers are handled\"\"\"\n\n    def __init__(self):\n        super().__init__(\"set fiber\", gdb.COMMAND_DATA, prefix=True)\n\n    def invoke(self, arg, from_tty):\n        print('\"set fiber\" must be followed by the name of a fiber subcommand')\n        gdb.execute(\"help set fiber\")\n\n\nclass ShowFiberCommand(gdb.Command):\n    \"\"\"Generic command for showing fiber settings\"\"\"\n\n    REGISTRY = set()\n\n    def __init__(self):\n        super().__init__(\"show fiber\", gdb.COMMAND_DATA, prefix=True)\n\n    def invoke(self, arg, from_tty):\n        for name in sorted(self.REGISTRY):\n            print(\n                \"{name}: {value}\".format(\n                    name=name, value=gdb.parameter(\"fiber \" + name)\n                )\n            )\n\n\nclass FiberParameter(gdb.Parameter):\n    \"\"\"track fiber parameters to print all on \"show fiber\" command\"\"\"\n\n    def __init__(self, name, *args, **kwds):\n        super().__init__(name, *args, **kwds)\n        (category, _, parameter) = name.partition(\" \")\n        assert category == \"fiber\"\n        ShowFiberCommand.REGISTRY.add(parameter)\n\n\nclass FiberManagerPrintLimitParameter(FiberParameter):\n    \"\"\"Manage the limit of fibers displayed when printing a FiberManager\"\"\"\n\n    show_doc = \"Show limit of fibers of a fiber manager to print\"\n    set_doc = \"Set limit of fibers of a fiber manager to print\"\n\n    def __init__(self):\n        super().__init__(\n            \"fiber manager-print-limit\", gdb.COMMAND_DATA, gdb.PARAM_UINTEGER\n        )\n        self.value = 100\n\n\nclass FiberInfoFrameSkipWordsParameter(FiberParameter):\n    \"\"\"Manage which frames are skipped when showing fiber info.\n\n    Usage: set fiber info-frame-skip-words [ WORD [ ... ] ]\n\n    Space-separated list of strings that, if present in the function name, will\n    cause \"info fibers\" to skip that frame when printing current location. Set\n    to no words to disable behavior and always pring the topmost frame.\n\n    Default: folly::fibers wait\n    \"\"\"\n\n    show_doc = \"Show words to skip frames of in fiber info printing\"\n    set_doc = \"Set words to skip frames of in fiber info printing\"\n\n    def __init__(self):\n        super().__init__(\n            \"fiber info-frame-skip-words\", gdb.COMMAND_DATA, gdb.PARAM_STRING\n        )\n        self.value = \"folly::fibers wait\"\n\n\nclass Shortcut(gdb.Function):\n    def __init__(self, function_name, value_lambda):\n        super().__init__(function_name)\n        self.value_lambda = value_lambda\n\n    def invoke(self, *args):\n        return self.value_lambda(*args)\n\n\n#\n# Internal\n#\n\n\n# This class is responsible for maintaining the name/address:fiberinfo mapping.\n# Creating a FiberInfo object adds it to the cache, and trying to create one\n# for a cached fiber will just return the same cached FiberInfo.\nclass FiberInfo:\n    NAMES = {}\n\n    # Lookup of fiber address/name to (mid, fid)\n    def __new__(cls, fiber):\n        # return cached info object if it exists\n        cached = cls.NAMES.get(str(fiber.address), None)\n        if cached:\n            return cached\n\n        # otherwise create a new one\n        obj = super().__new__(cls)\n        obj.fiber = fiber\n        obj._name = \"\"\n        obj.mid = None\n        obj.fid = None\n\n        # make sure it's in the cache\n        obj.NAMES[str(fiber.address)] = obj\n        return obj\n\n    @property\n    def id(self):\n        return \"{}.{}\".format(self.mid or \"?\", self.fid or \"?\")\n\n    @property\n    def name(self):\n        return self._name\n\n    @name.setter\n    def name(self, name):\n        if self._name:\n            # clear any existing name in cache\n            del self.NAMES[self._name]\n        if name:\n            # clear any old holder of the name\n            if name in self.NAMES:\n                self.NAMES[name]._name = \"\"\n            # update cache to point to us\n            self.NAMES[name] = self\n        # update the name\n        self._name = name or \"\"\n\n    @classmethod\n    def by_name(cls, name):\n        return cls.NAMES.get(name, None)\n\n    def __repr__(self):\n        return 'FiberInfo(address={}, id={}, name=\"{}\")'.format(\n            self.fiber.address, self.id, self.name\n        )\n\n    def __str__(self):\n        return \"Fiber {address} ({state}){name}\".format(\n            address=self.fiber.address,\n            state=FiberPrinter(self.fiber).state_to_string(),\n            name=f' \"{self._name}\"' if self._name else \"\",\n        )\n\n    def __eq__(self, other):\n        return self.fiber.address == other.fiber.address\n\n\ndef fiber_activate(fiber):\n    return FiberUnwinder.set_fiber(fiber)\n\n\ndef fiber_deactivate():\n    return FiberUnwinder.set_fiber(None)\n\n\ndef get_fiber(fid):\n    \"\"\"return a fiber for the given id, name, or address, or None\"\"\"\n    # check if it's a named fiber\n    info = FiberInfo.by_name(fid)\n    if info:\n        return info.fiber\n\n    # next check if it's an assigned id\n    if \".\" in fid:\n        try:\n            # Just grab the first result and return\n            return next(get_fiber_info([fid]))[1].fiber\n        except IndexError:\n            return None\n\n    # finally assume it's an address to a fiber\n    try:\n        return (\n            gdb.parse_and_eval(fid)\n            .cast(gdb.lookup_type(\"folly::fibers::Fiber\").pointer())\n            .dereference()\n        )\n    except gdb.error:\n        return None\n\n\ndef get_fiber_info(only=None, managers=False):\n    \"\"\"iterator of tuples of ((mid, fid), info)\n\n    Can filter output with \"M.F\" or \"M\" format, e.g. \"2.3\" for manager 2 fiber 3\n    or \"1\" for all fibers from manager 1.  If \"fid\" is None, then the value is\n    a gdb.Value of a fiber manager, and if it's non-None, then it's of a FiberInfo.\n    Guaranteed to return values in ascending order, managers before their fibers.\n    Manager output only occurs when `managers` is set to True.\n\n    Implemented as iterators for interactive speed purposes. A binary can have\n    very many fibers, so we don't want to process everything before printing\n    some data to the user.  Results of full fiber managers are cached.\n    \"\"\"\n    for mid, manager in get_fiber_managers(only):\n        if managers:\n            yield ((mid, None), manager)\n\n        # first check if pre-cached\n        if mid in get_fiber_info.cache:\n            for fid, info in get_fiber_info.cache[mid].items():\n                fiber_id = f\"{mid}.{fid}\"\n                if not only or str(mid) in only or fiber_id in only:\n                    yield ((mid, fid), info)\n            continue\n\n        # list fibers from the manager\n        fibers = collections.OrderedDict()\n        fid = 1\n        for fiber in fiber_manager_active_fibers(manager):\n            # look up/create cached info\n            info = FiberInfo(fiber)\n            # associate mid.fid with info\n            info.mid = mid\n            info.fid = fid\n            fibers[fid] = info\n            # output only if matching the filter\n            fiber_id = f\"{mid}.{fid}\"\n            if not only or str(mid) in only or fiber_id in only:\n                yield ((mid, fid), info)\n            fid += 1\n        get_fiber_info.cache[mid] = fibers\n\n\n# Cache of {mid: {fid: FiberInfo}}\n# If the mid is there, then all of the fibers for it are there\nget_fiber_info.cache = collections.OrderedDict()\n\n\ndef get_fiber_managers(only=None):\n    \"\"\"iterator of (mid, manager) tuples\n\n    Can filter output with \"M.*\" or \"M\" format, e.g. \"2.3\" for manager 2 (fiber is\n    ignored) or \"1\" for manager 1.\n    \"\"\"\n    # first check if pre-cached\n    if get_fiber_managers.cache:\n        for mid, manager in get_fiber_managers.cache.items():\n            # output only if matching filter\n            if not only or str(mid) in only:\n                yield (mid, manager)\n        return\n\n    # extract the unique managers from the fiber filters\n    only = {i.partition(\".\")[0] for i in only or []}\n    managers = collections.OrderedDict()\n    mgr_map = None\n    for evb_type in (\"folly::EventBase\", \"folly::VirtualEventBase\"):\n        # this can possibly return an empty map, even if it exists\n        try:\n            mgr_map = get_fiber_manager_map(evb_type)\n        except gdb.GdbError:\n            continue\n        # the map pretty printer knows how to extract map entries\n        map_pp = gdb.default_visualizer(mgr_map)\n\n        # The children are alternating pairs of (_, (evb, int))/(_, uptr<FiberManager>)\n        # the first entry is irrelevant, just an internal name for the pretty printer\n        mid = 0\n\n        for _, entry in map_pp.children():\n            # The \"key\" in this map is std::pair<EventBaseT, long>, and the long is\n            # almost always 0 (used for frozen options).  We'll ignore it and just use\n            # our own internal id.  We unwrap the unique_ptr, though.\n            if \"unique_ptr\" not in entry.type.tag:\n                mid += 1\n                continue\n\n            # we have a value, make sure we have a unique key\n            assert mid not in managers\n            value = gdb.default_visualizer(entry)\n\n            # Before GCC9, the stl gdb libs don't expose the unique_ptr target\n            # address except through the pretty printer, as the last\n            # space-delimited word. From GCC9 forward, the unique_ptr visualizer\n            # exposes a children iterator with the target address. We extract\n            # that address whicever way we can, then create a new gdb.Value of\n            # that address cast to the fibermanager type.\n            address = None\n            if callable(getattr(value, \"children\", None)):\n                for _, pointer in value.children():\n                    address = pointer\n            else:\n                address = int(value.to_string().split(\" \")[-1], 16)\n\n            if address is not None:\n                manager = (\n                    gdb.Value(address)\n                    .cast(gdb.lookup_type(\"folly::fibers::FiberManager\").pointer())\n                    .dereference()\n                )\n\n                # output only if matching filter\n                if not only or str(mid) in only:\n                    yield (mid, manager)\n\n        # set cache\n        get_fiber_managers.cache = managers\n\n\n# Cache of {mid: manager}\nget_fiber_managers.cache = collections.OrderedDict()\n\n\ndef fiber_manager_active_fibers(fm):\n    \"\"\"iterator of Fiber values for a given fiber manager\"\"\"\n    all_fibers = fm[\"allFibers_\"][\"data_\"][\"root_plus_size_\"][\"m_header\"]\n    fiber_hook = all_fibers[\"next_\"]\n\n    while fiber_hook != all_fibers.address:\n        fiber = fiber_hook.cast(gdb.lookup_type(\"int64_t\"))\n        fiber = fiber - gdb.parse_and_eval(\n            \"(int64_t)&'folly::fibers::Fiber'::globalListHook_\"\n        )\n        fiber = fiber.cast(\n            gdb.lookup_type(\"folly::fibers::Fiber\").pointer()\n        ).dereference()\n\n        if FiberPrinter(fiber).state != \"folly::fibers::Fiber::INVALID\":\n            yield fiber\n\n        fiber_hook = fiber_hook.dereference()[\"next_\"]\n\n\ndef get_fiber_manager_map(evb_type):\n    # global cache was moved from anonymous namespace to \"detail\", we want to\n    # work in both cases.\n    for ns in (\"detail\", \"(anonymous namespace)\"):\n        try:\n            # Exception thrown if unable to find type\n            global_cache_type = gdb.lookup_type(\n                \"folly::fibers::{ns}::GlobalCache<{evb_type}>\".format(\n                    ns=ns, evb_type=evb_type\n                )\n            )\n            break\n        except gdb.error:\n            pass\n    else:\n        raise gdb.GdbError(\n            \"Unable to find types. \"\n            \"Please make sure debug info is available for this binary.\\n\"\n            \"Have you run 'fbload debuginfo_fbpkg'?\"\n        )\n\n    global_cache_instance_ptr_ptr = gdb.parse_and_eval(\n        \"&'\" + global_cache_type.name + \"::instance()::ret'\"\n    )\n    global_cache_instance_ptr = global_cache_instance_ptr_ptr.cast(\n        global_cache_type.pointer().pointer()\n    ).dereference()\n    if global_cache_instance_ptr == 0x0:\n        raise gdb.GdbError(\"FiberManager map is empty.\")\n\n    global_cache_instance = global_cache_instance_ptr.dereference()\n    return global_cache_instance[\"map_\"]\n\n\ndef get_fiber_manager_map_evb():\n    return get_fiber_manager_map(\"folly::EventBase\")\n\n\ndef get_fiber_manager_map_vevb():\n    return get_fiber_manager_map(\"folly::VirtualEventBase\")\n\n\n# reset the caches when we continue; the current fibers will almost certainly change\ndef clear_fiber_caches(*args):\n    # default to only clearing manager and info caches\n    caches = {arg.string() for arg in args} or {\"managers\", \"info\"}\n    cleared = set()\n    if \"managers\" in caches:\n        cleared.add(\"manager\")\n        get_fiber_managers.cache.clear()\n    if \"info\" in caches:\n        cleared.add(\"info\")\n        get_fiber_info.cache.clear()\n    # This one we may want to keep, so we can reference known fibers by name/address\n    # even though their assigned ids will almost certainly change\n    if \"names\" in caches:\n        cleared.add(\"names\")\n        FiberInfo.NAMES.clear()\n\n    result = \"Cleared {}.\".format(\", \".join(cleared))\n    unknown = caches - cleared\n    if unknown:\n        # print a warning if we asked for any unknown caches to clear\n        result += \" Skipped unknown \" + \", \".join(unknown)\n    return result\n\n\ndef build_pretty_printer():\n    pp = gdb.printing.RegexpCollectionPrettyPrinter(\"folly_fibers\")\n    pp.add_printer(\"fibers::Fiber\", \"^folly::fibers::Fiber$\", FiberPrinter)\n    pp.add_printer(\n        \"fibers::FiberManager\", \"^folly::fibers::FiberManager$\", FiberManagerPrinter\n    )\n    return pp\n\n\ndef load():\n    gdb.printing.register_pretty_printer(gdb, build_pretty_printer())\n    gdb.xmethod.register_xmethod_matcher(gdb, FiberXMethodMatcher())\n    gdb.xmethod.register_xmethod_matcher(gdb, GetFiberXMethodMatcher())\n    FiberFrameFilter()\n    SetFiberCommand()\n    ShowFiberCommand()\n    FiberManagerPrintLimitParameter()\n    FiberInfoFrameSkipWordsParameter()\n    FiberCommand()\n    FiberDeactivateCommand()\n    FiberInfoCommand()\n    FiberApplyCommand()\n    FiberNameCommand()\n    Shortcut(\"get_fiber_manager_map_evb\", get_fiber_manager_map_evb)\n    Shortcut(\"get_fiber_manager_map_vevb\", get_fiber_manager_map_vevb)\n    Shortcut(\"clear_fiber_caches\", lambda _: clear_fiber_caches())\n    gdb.events.cont.connect(lambda _: clear_fiber_caches())\n\n\ndef info():\n    return \"Pretty printers for folly::fibers\"\n"
  },
  {
    "path": "folly/fibers/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"timed_mutex_benchmark\",\n    srcs = [\"TimedMutexBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/fibers:timed_mutex\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"fibers_test\",\n    srcs = [\"FibersTest.cpp\"],\n    headers = [],\n    labels = [\"heavyweight\"],\n    deps = [\n        \"//folly:conv\",\n        \"//folly:memory\",\n        \"//folly:random\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:gtest_helpers\",\n        \"//folly/coro:timeout\",\n        \"//folly/coro:with_cancellation\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/fibers:add_tasks\",\n        \"//folly/fibers:atomic_batch_dispatcher\",\n        \"//folly/fibers:batch_dispatcher\",\n        \"//folly/fibers:batch_semaphore\",\n        \"//folly/fibers:core_manager\",\n        \"//folly/fibers:event_base_loop_controller\",\n        \"//folly/fibers:executor_loop_controller\",\n        \"//folly/fibers:fiber_manager_map\",\n        \"//folly/fibers:generic_baton\",\n        \"//folly/fibers:semaphore\",\n        \"//folly/fibers:simple_loop_controller\",\n        \"//folly/fibers:timed_mutex\",\n        \"//folly/fibers:when_n\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:lock\",\n        \"//folly/tracing:async_stack\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"fibers_test_app\",\n    srcs = [\"FibersTestApp.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:memory\",\n        \"//folly/fibers:core_manager\",\n        \"//folly/fibers:simple_loop_controller\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"stack_overflow\",\n    srcs = [\"StackOverflow.cpp\"],\n    headers = [],\n    compiler_flags = [\"-Wno-infinite-recursion\"],\n    deps = [\n        \"//folly/fibers:fiber_manager_map\",\n        \"//folly/init:init\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"semaphore_test\",\n    srcs = [\"SemaphoreTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/fibers:semaphore\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/synchronization/detail:sleeper\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"batch_semaphore_test\",\n    srcs = [\"BatchSemaphoreTest.cpp\"],\n    deps = [\n        \"//folly/fibers:batch_semaphore\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/fibers/test/BatchSemaphoreTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/BatchSemaphore.h>\n#include <folly/portability/GTest.h>\n\nTEST(BatchSemaphoreTest, WaitSignalSynchronization) {\n  folly::fibers::BatchSemaphore sem{0};\n\n  int64_t data = 0;\n  folly::relaxed_atomic_bool signalled = false;\n\n  std::jthread t{[&]() {\n    while (!signalled) {\n      std::this_thread::yield();\n    }\n\n    sem.wait(1);\n    EXPECT_NE(data, 0);\n  }};\n\n  data = 1;\n  sem.signal(1);\n  signalled = true;\n}\n"
  },
  {
    "path": "folly/fibers/test/FibersBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <queue>\n\n#include <folly/Benchmark.h>\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/FiberManagerMap.h>\n#include <folly/fibers/SimpleLoopController.h>\n#include <folly/init/Init.h>\n#include <folly/io/async/EventBase.h>\n\nusing namespace folly::fibers;\n\nstatic size_t sNumAwaits;\n\nvoid runBenchmark(size_t numAwaits, size_t toSend, bool logRunningTime) {\n  sNumAwaits = numAwaits;\n\n  FiberManager fiberManager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());\n\n  std::queue<Promise<int>> pendingRequests;\n  static const size_t maxOutstanding = 5;\n\n  auto loop =\n      [&fiberManager,\n       &loopController,\n       &pendingRequests,\n       &toSend,\n       logRunningTime]() {\n        TaskOptions tOpt;\n        tOpt.logRunningTime = logRunningTime;\n        if (pendingRequests.size() == maxOutstanding || toSend == 0) {\n          if (pendingRequests.empty()) {\n            return;\n          }\n          pendingRequests.front().setValue(0);\n          pendingRequests.pop();\n        } else {\n          fiberManager.addTask(\n              [&pendingRequests]() {\n                for (size_t i = 0; i < sNumAwaits; ++i) {\n                  auto result = await([&pendingRequests](Promise<int> promise) {\n                    pendingRequests.push(std::move(promise));\n                  });\n                  DCHECK_EQ(result, 0);\n                }\n              },\n              std::move(tOpt));\n\n          if (--toSend == 0) {\n            loopController.stop();\n          }\n        }\n      };\n\n  loopController.loop(std::move(loop));\n}\n\nBENCHMARK(FiberManagerBasicOneAwait, iters) {\n  runBenchmark(1, iters, false);\n}\n\nBENCHMARK(FiberManagerBasicOneAwaitLogged, iters) {\n  runBenchmark(1, iters, true);\n}\n\nBENCHMARK(FiberManagerBasicFiveAwaits, iters) {\n  runBenchmark(5, iters, false);\n}\n\nBENCHMARK(FiberManagerBasicFiveAwaitsLogged, iters) {\n  runBenchmark(5, iters, true);\n}\n\nBENCHMARK(FiberManagerGet, iters) {\n  constexpr size_t nevbs = 64;\n  folly::BenchmarkSuspender braces;\n  std::vector<folly::EventBase> evbs{nevbs};\n  for (auto& evb : evbs) {\n    auto& fm = folly::fibers::getFiberManager(evb);\n    folly::doNotOptimizeAway(fm);\n  }\n  braces.dismissing([&] {\n    for (size_t i = 0; i < iters; ++i) {\n      auto& evb = evbs[i * 7753 % nevbs];\n      auto& fm = folly::fibers::getFiberManager(evb);\n      folly::doNotOptimizeAway(fm);\n    }\n  });\n}\n\nBENCHMARK(FiberManagerCreateDestroy, iters) {\n  for (size_t i = 0; i < iters; ++i) {\n    folly::EventBase evb;\n    auto& fm = folly::fibers::getFiberManager(evb);\n    fm.addTask([]() {});\n    evb.loop();\n  }\n}\n\nBENCHMARK(FiberManagerAllocateDeallocatePattern, iters) {\n  static const size_t kNumAllocations = 10000;\n\n  FiberManager::Options opts;\n  opts.maxFibersPoolSize = 0;\n\n  FiberManager fiberManager(std::make_unique<SimpleLoopController>(), opts);\n\n  for (size_t iter = 0; iter < iters; ++iter) {\n    DCHECK_EQ(0, fiberManager.fibersPoolSize());\n\n    size_t fibersRun = 0;\n\n    for (size_t i = 0; i < kNumAllocations; ++i) {\n      fiberManager.addTask([&fibersRun] { ++fibersRun; });\n      fiberManager.loopUntilNoReady();\n    }\n\n    DCHECK_EQ(10000, fibersRun);\n    DCHECK_EQ(0, fiberManager.fibersPoolSize());\n  }\n}\n\nBENCHMARK(FiberManagerAllocateLargeChunk, iters) {\n  static const size_t kNumAllocations = 10000;\n\n  FiberManager::Options opts;\n  opts.maxFibersPoolSize = 0;\n\n  FiberManager fiberManager(std::make_unique<SimpleLoopController>(), opts);\n\n  for (size_t iter = 0; iter < iters; ++iter) {\n    DCHECK_EQ(0, fiberManager.fibersPoolSize());\n\n    size_t fibersRun = 0;\n\n    for (size_t i = 0; i < kNumAllocations; ++i) {\n      fiberManager.addTask([&fibersRun] { ++fibersRun; });\n    }\n\n    fiberManager.loopUntilNoReady();\n\n    DCHECK_EQ(10000, fibersRun);\n    DCHECK_EQ(0, fiberManager.fibersPoolSize());\n  }\n}\n\nvoid runTimeoutsBenchmark(std::vector<size_t> timeouts) {\n  constexpr size_t kNumIters = 100000;\n  constexpr size_t kNumFibers = 100;\n\n  size_t iter = 0;\n  std::vector<folly::fibers::Baton> batons(kNumFibers);\n\n  FiberManager manager(std::make_unique<EventBaseLoopController>());\n\n  folly::EventBase evb(false /* enableTimeMeasurement */);\n  dynamic_cast<EventBaseLoopController&>(manager.loopController())\n      .attachEventBase(evb);\n\n  for (size_t i = 0; i < kNumFibers; ++i) {\n    manager.addTask([i, &iter, &timeouts, &batons] {\n      while (iter < kNumIters) {\n        auto tmo = timeouts[iter++ % timeouts.size()];\n        batons[i].timed_wait(std::chrono::milliseconds(tmo));\n        batons[i].reset();\n      }\n    });\n  }\n\n  while (iter < kNumIters) {\n    evb.loopOnce();\n    for (auto& b : batons) {\n      b.post();\n    }\n  }\n  evb.loopOnce();\n}\n\nBENCHMARK(FiberManagerCancelledTimeouts_Single_300) {\n  runTimeoutsBenchmark({300});\n}\n\nBENCHMARK(FiberManagerCancelledTimeouts_Five) {\n  runTimeoutsBenchmark({300, 350, 500, 1000, 2000});\n}\n\nBENCHMARK(FiberManagerCancelledTimeouts_TenThousand) {\n  constexpr size_t kNumTimeouts = 10000;\n\n  std::vector<size_t> tmos(kNumTimeouts);\n  for (size_t i = 0; i < kNumTimeouts; ++i) {\n    tmos[i] = 200 + 50 * i;\n  }\n  runTimeoutsBenchmark(std::move(tmos));\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv, true);\n\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/fibers/test/FibersTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <atomic>\n#include <chrono>\n#include <thread>\n#include <vector>\n\n#include <folly/Conv.h>\n#include <folly/Memory.h>\n#include <folly/Random.h>\n#include <folly/coro/BlockingWait.h>\n#include <folly/coro/GtestHelpers.h>\n#include <folly/coro/Timeout.h>\n#include <folly/coro/WithCancellation.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/fibers/AddTasks.h>\n#include <folly/fibers/AtomicBatchDispatcher.h>\n#include <folly/fibers/BatchDispatcher.h>\n#include <folly/fibers/BatchSemaphore.h>\n#include <folly/fibers/EventBaseLoopController.h>\n#include <folly/fibers/ExecutorLoopController.h>\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/FiberManagerMap.h>\n#include <folly/fibers/GenericBaton.h>\n#include <folly/fibers/Semaphore.h>\n#include <folly/fibers/SimpleLoopController.h>\n#include <folly/fibers/TimedMutex.h>\n#include <folly/fibers/WhenN.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Lock.h>\n#include <folly/tracing/AsyncStack.h>\n\nusing namespace folly::fibers;\n\nusing folly::Try;\n\nTEST(FiberManager, batonTimedWaitTimeout) {\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n  std::chrono::steady_clock::time_point start;\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        Baton baton;\n\n        start = std::chrono::steady_clock::now();\n        constexpr auto kTimeout = std::chrono::milliseconds(230);\n        auto res = baton.try_wait_for(kTimeout);\n        auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(\n            std::chrono::steady_clock::now() - start);\n\n        EXPECT_FALSE(res);\n        EXPECT_LE(kTimeout, elapsedMs);\n\n        loopController.stop();\n      });\n      taskAdded = true;\n    } else {\n      std::this_thread::sleep_for(std::chrono::milliseconds(50));\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, batonTimedWaitPost) {\n  bool taskAdded = false;\n  size_t iterations = 0;\n  Baton* baton_ptr;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        Baton baton;\n        baton_ptr = &baton;\n\n        auto res = baton.try_wait_for(std::chrono::milliseconds(130));\n\n        EXPECT_TRUE(res);\n        EXPECT_EQ(2, iterations);\n\n        loopController.stop();\n      });\n      taskAdded = true;\n    } else {\n      std::this_thread::sleep_for(std::chrono::milliseconds(50));\n      iterations++;\n      if (iterations == 2) {\n        baton_ptr->post();\n      }\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, batonTimedWaitTimeoutEvb) {\n  size_t tasksComplete = 0;\n\n  folly::EventBase evb;\n\n  FiberManager manager(std::make_unique<EventBaseLoopController>());\n  dynamic_cast<EventBaseLoopController&>(manager.loopController())\n      .attachEventBase(evb);\n\n  auto task = [&](size_t timeout_ms) {\n    Baton baton;\n\n    auto start = EventBaseLoopController::Clock::now();\n    auto res = baton.try_wait_for(std::chrono::milliseconds(timeout_ms));\n    auto finish = EventBaseLoopController::Clock::now();\n\n    EXPECT_FALSE(res);\n\n    auto duration_ms =\n        std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);\n\n    EXPECT_GT(duration_ms.count(), timeout_ms - 50);\n    EXPECT_LT(duration_ms.count(), timeout_ms + 50);\n\n    if (++tasksComplete == 2) {\n      evb.terminateLoopSoon();\n    }\n  };\n\n  evb.runInEventBaseThread([&]() {\n    manager.addTask([&]() { task(500); });\n    manager.addTask([&]() { task(250); });\n  });\n\n  evb.loopForever();\n\n  EXPECT_EQ(2, tasksComplete);\n}\n\nTEST(FiberManager, batonTimedWaitPostEvb) {\n  size_t tasksComplete = 0;\n\n  folly::EventBase evb;\n\n  FiberManager manager(std::make_unique<EventBaseLoopController>());\n  dynamic_cast<EventBaseLoopController&>(manager.loopController())\n      .attachEventBase(evb);\n\n  evb.runInEventBaseThread([&]() {\n    manager.addTask([&]() {\n      Baton baton;\n\n      evb.tryRunAfterDelay([&]() { baton.post(); }, 100);\n\n      auto start = EventBaseLoopController::Clock::now();\n      auto res = baton.try_wait_for(std::chrono::milliseconds(130));\n      auto finish = EventBaseLoopController::Clock::now();\n\n      EXPECT_TRUE(res);\n\n      auto duration_ms =\n          std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);\n\n      EXPECT_TRUE(duration_ms.count() > 95 && duration_ms.count() < 110);\n\n      if (++tasksComplete == 1) {\n        evb.terminateLoopSoon();\n      }\n    });\n  });\n\n  evb.loopForever();\n\n  EXPECT_EQ(1, tasksComplete);\n}\n\nTEST(FiberManager, fiberTimeLogged) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  TaskOptions tOpt;\n  tOpt.logRunningTime = true;\n  manager.addTask(\n      [&]() {\n        LOG(INFO) << \"logging time.\";\n        EXPECT_LT(-1, manager.getCurrentTaskRunningTime()->count());\n      },\n      tOpt /*logRunningTime = true*/);\n  EXPECT_FALSE(manager.getCurrentTaskRunningTime());\n  tOpt.logRunningTime = false;\n  manager.addTask(\n      [&]() {\n        LOG(INFO) << \"Not logging time.\";\n        EXPECT_FALSE(manager.getCurrentTaskRunningTime());\n      },\n      tOpt /*logRunningTime = false*/);\n  manager.loopUntilNoReady();\n}\n\n#ifdef __linux__\n\nnamespace {\nvoid burnCpu(std::chrono::milliseconds duration) {\n  auto expires = folly::fibers::thread_clock::now() + duration;\n  while (folly::fibers::thread_clock::now() < expires) {\n  }\n}\n\nvoid sleepFor(std::chrono::milliseconds duration) {\n  /* sleep override */ std::this_thread::sleep_for(duration);\n}\n} // namespace\n\nTEST(FiberManager, fiberTimeWhileRunning) {\n  using namespace std::chrono;\n  FiberManager fm(std::make_unique<SimpleLoopController>());\n  TaskOptions tOpt;\n  tOpt.logRunningTime = true;\n  fm.addTask(\n      [&]() {\n        burnCpu(200ms);\n        sleepFor(1s); // (not included in running time)\n        fm.yield();\n        burnCpu(100ms);\n        auto dur = fm.getCurrentTaskRunningTime();\n        ASSERT_TRUE(dur); // ~300ms\n        EXPECT_GE(*dur, 250ms);\n        EXPECT_LE(*dur, 350ms);\n      },\n      tOpt);\n  fm.addTask(\n      [&]() {\n        burnCpu(300ms);\n        sleepFor(1s); // (not included in running time)\n        fm.yield();\n        burnCpu(200ms);\n        auto dur = fm.getCurrentTaskRunningTime();\n        ASSERT_TRUE(dur); // ~500ms\n        EXPECT_GE(*dur, 450ms);\n        EXPECT_LE(*dur, 550ms);\n      },\n      tOpt);\n  while (fm.hasTasks()) {\n    fm.loopUntilNoReady();\n  }\n}\n\nTEST(FiberManager, fiberTimeWhileAwaiting) {\n  using namespace std::chrono;\n  FiberManager fm(std::make_unique<SimpleLoopController>());\n  TaskOptions tOpt;\n  tOpt.logRunningTime = true;\n  fm.addTask(\n      [&]() {\n        burnCpu(200ms);\n        sleepFor(1s); // (not included in running time)\n        fm.yield();\n        burnCpu(100ms);\n        runInMainContext([&] {\n          burnCpu(200ms); // (not included in running time)\n          ASSERT_TRUE(fm.currentFiber());\n          auto dur = fm.getCurrentTaskRunningTime();\n          ASSERT_TRUE(dur); // ~300ms\n          EXPECT_GE(*dur, 250ms);\n          EXPECT_LE(*dur, 350ms);\n        });\n      },\n      tOpt);\n  while (fm.hasTasks()) {\n    fm.loopUntilNoReady();\n  }\n}\n\n#endif // __linux__\n\nTEST(FiberManager, batonTryWait) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n\n  // Check if try_wait and post work as expected\n  Baton b;\n\n  manager.addTask([&]() {\n    while (!b.try_wait()) {\n    }\n  });\n  auto thr = std::thread([&]() {\n    std::this_thread::sleep_for(std::chrono::milliseconds(300));\n    b.post();\n  });\n\n  manager.loopUntilNoReady();\n  thr.join();\n\n  Baton c;\n\n  // Check try_wait without post\n  manager.addTask([&]() {\n    int cnt = 100;\n    while (cnt && !c.try_wait()) {\n      cnt--;\n    }\n    EXPECT_TRUE(!c.try_wait()); // must still hold\n    EXPECT_EQ(cnt, 0);\n  });\n\n  manager.loopUntilNoReady();\n}\n\nTEST(FiberManager, batonTryWaitConsistent) {\n  folly::EventBase evb;\n  struct ExpectedException {};\n  auto& fm = getFiberManager(evb);\n\n  // Check if try_wait and post have a consistent behavior on a timed out\n  // baton\n  Baton b;\n\n  b.try_wait_for(std::chrono::milliseconds(1));\n  b.try_wait(); // returns false\n\n  b.post();\n  EXPECT_TRUE(b.try_wait());\n  b.reset();\n\n  fm.addTask([&]() {\n    b.try_wait_for(std::chrono::milliseconds(1));\n    b.try_wait(); // returns false\n\n    b.post();\n    EXPECT_TRUE(b.try_wait());\n  });\n\n  while (fm.hasTasks()) {\n    evb.loopOnce();\n  }\n}\n\nTEST(FiberManager, genericBatonFiberWait) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n\n  GenericBaton b;\n  bool fiberRunning = false;\n\n  manager.addTask([&]() {\n    EXPECT_EQ(manager.hasActiveFiber(), true);\n    fiberRunning = true;\n    b.wait();\n    fiberRunning = false;\n  });\n\n  EXPECT_FALSE(fiberRunning);\n  manager.loopUntilNoReady();\n  EXPECT_TRUE(fiberRunning); // ensure fiber still active\n\n  auto thr = std::thread([&]() {\n    std::this_thread::sleep_for(std::chrono::milliseconds(300));\n    b.post();\n  });\n\n  while (fiberRunning) {\n    manager.loopUntilNoReady();\n  }\n\n  thr.join();\n}\n\nTEST(FiberManager, genericBatonThreadWait) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  GenericBaton b;\n  std::atomic<bool> threadWaiting(false);\n\n  auto thr = std::thread([&]() {\n    threadWaiting = true;\n    b.wait();\n    threadWaiting = false;\n  });\n\n  while (!threadWaiting) {\n  }\n  std::this_thread::sleep_for(std::chrono::milliseconds(300));\n\n  manager.addTask([&]() {\n    EXPECT_EQ(manager.hasActiveFiber(), true);\n    EXPECT_TRUE(threadWaiting);\n    b.post();\n    while (threadWaiting) {\n    }\n  });\n\n  manager.loopUntilNoReady();\n  thr.join();\n}\n\nTEST(FiberManager, addTasksNoncopyable) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<std::unique_ptr<int>()>> funcs;\n        for (int i = 0; i < 3; ++i) {\n          funcs.emplace_back([i, &pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            return std::make_unique<int>(i * 2 + 1);\n          });\n        }\n\n        auto iter = addTasks(funcs.begin(), funcs.end());\n\n        size_t n = 0;\n        while (iter.hasNext()) {\n          auto result = iter.awaitNext();\n          EXPECT_EQ(2 * iter.getTaskID() + 1, *result);\n          EXPECT_GE(2 - n, pendingFibers.size());\n          ++n;\n        }\n        EXPECT_EQ(3, n);\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, awaitThrow) {\n  folly::EventBase evb;\n  struct ExpectedException {};\n  getFiberManager(evb)\n      .addTaskFuture([&] {\n        EXPECT_THROW(\n            await_async([](Promise<int> p) {\n              p.setValue(42);\n              throw ExpectedException();\n            }),\n            ExpectedException);\n\n        EXPECT_THROW(\n            await_async([&](Promise<int> p) {\n              evb.runInEventBaseThread([p = std::move(p)]() mutable {\n                p.setValue(42);\n              });\n              throw ExpectedException();\n            }),\n            ExpectedException);\n      })\n      .waitVia(&evb);\n}\n\nTEST(FiberManager, addTasksThrow) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<int()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([i, &pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            if (i % 2 == 0) {\n              throw std::runtime_error(\"Runtime\");\n            }\n            return i * 2 + 1;\n          });\n        }\n\n        auto iter = addTasks(funcs.begin(), funcs.end());\n\n        size_t n = 0;\n        while (iter.hasNext()) {\n          try {\n            int result = iter.awaitNext();\n            EXPECT_EQ(1, iter.getTaskID() % 2);\n            EXPECT_EQ(2 * iter.getTaskID() + 1, result);\n          } catch (...) {\n            EXPECT_EQ(0, iter.getTaskID() % 2);\n          }\n          EXPECT_GE(2 - n, pendingFibers.size());\n          ++n;\n        }\n        EXPECT_EQ(3, n);\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, addTasksVoid) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<void()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([&pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n          });\n        }\n\n        auto iter = addTasks(funcs.begin(), funcs.end());\n\n        size_t n = 0;\n        while (iter.hasNext()) {\n          iter.awaitNext();\n          EXPECT_GE(2 - n, pendingFibers.size());\n          ++n;\n        }\n        EXPECT_EQ(3, n);\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, addTasksVoidThrow) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<void()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([i, &pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            if (i % 2 == 0) {\n              throw std::runtime_error(\"\");\n            }\n          });\n        }\n\n        auto iter = addTasks(funcs.begin(), funcs.end());\n\n        size_t n = 0;\n        while (iter.hasNext()) {\n          try {\n            iter.awaitNext();\n            EXPECT_EQ(1, iter.getTaskID() % 2);\n          } catch (...) {\n            EXPECT_EQ(0, iter.getTaskID() % 2);\n          }\n          EXPECT_GE(2 - n, pendingFibers.size());\n          ++n;\n        }\n        EXPECT_EQ(3, n);\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, addTasksReserve) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<void()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([&pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n          });\n        }\n\n        auto iter = addTasks(funcs.begin(), funcs.end());\n\n        iter.reserve(2);\n        EXPECT_TRUE(iter.hasCompleted());\n        EXPECT_TRUE(iter.hasPending());\n        EXPECT_TRUE(iter.hasNext());\n\n        iter.awaitNext();\n        EXPECT_TRUE(iter.hasCompleted());\n        EXPECT_TRUE(iter.hasPending());\n        EXPECT_TRUE(iter.hasNext());\n\n        iter.awaitNext();\n        EXPECT_FALSE(iter.hasCompleted());\n        EXPECT_TRUE(iter.hasPending());\n        EXPECT_TRUE(iter.hasNext());\n\n        iter.awaitNext();\n        EXPECT_FALSE(iter.hasCompleted());\n        EXPECT_FALSE(iter.hasPending());\n        EXPECT_FALSE(iter.hasNext());\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, addTaskDynamic) {\n  folly::EventBase evb;\n\n  Baton batons[3];\n\n  auto makeTask = [&](size_t taskId) {\n    return [&, taskId]() -> size_t {\n      batons[taskId].wait();\n      return taskId;\n    };\n  };\n\n  getFiberManager(evb)\n      .addTaskFuture([&]() {\n        TaskIterator<size_t> iterator;\n\n        iterator.addTask(makeTask(0));\n        iterator.addTask(makeTask(1));\n\n        batons[1].post();\n\n        EXPECT_EQ(1, iterator.awaitNext());\n\n        iterator.addTask(makeTask(2));\n\n        batons[2].post();\n\n        EXPECT_EQ(2, iterator.awaitNext());\n\n        batons[0].post();\n\n        EXPECT_EQ(0, iterator.awaitNext());\n      })\n      .waitVia(&evb);\n}\n\nTEST(FiberManager, forEach) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<int()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([i, &pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            return i * 2 + 1;\n          });\n        }\n\n        std::vector<std::pair<size_t, int>> results;\n        forEach(funcs.begin(), funcs.end(), [&results](size_t id, int result) {\n          results.emplace_back(id, result);\n        });\n        EXPECT_EQ(3, results.size());\n        EXPECT_TRUE(pendingFibers.empty());\n        for (size_t i = 0; i < 3; ++i) {\n          EXPECT_EQ(results[i].first * 2 + 1, results[i].second);\n        }\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, collectN) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<int()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([i, &pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            return i * 2 + 1;\n          });\n        }\n\n        auto results = collectN(funcs.begin(), funcs.end(), 2);\n        EXPECT_EQ(2, results.size());\n        EXPECT_EQ(1, pendingFibers.size());\n        for (size_t i = 0; i < 2; ++i) {\n          EXPECT_EQ(results[i].first * 2 + 1, results[i].second);\n        }\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, collectNThrow) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<int()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([&pendingFibers]() -> size_t {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            throw std::runtime_error(\"Runtime\");\n          });\n        }\n\n        try {\n          collectN(funcs.begin(), funcs.end(), 2);\n        } catch (...) {\n          EXPECT_EQ(1, pendingFibers.size());\n        }\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, collectNVoid) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<void()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([&pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n          });\n        }\n\n        auto results = collectN(funcs.begin(), funcs.end(), 2);\n        EXPECT_EQ(2, results.size());\n        EXPECT_EQ(1, pendingFibers.size());\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, collectNVoidThrow) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<void()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([&pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            throw std::runtime_error(\"Runtime\");\n          });\n        }\n\n        try {\n          collectN(funcs.begin(), funcs.end(), 2);\n        } catch (...) {\n          EXPECT_EQ(1, pendingFibers.size());\n        }\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, collectAll) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<int()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([i, &pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            return i * 2 + 1;\n          });\n        }\n\n        auto results = collectAll(funcs.begin(), funcs.end());\n        EXPECT_TRUE(pendingFibers.empty());\n        for (size_t i = 0; i < 3; ++i) {\n          EXPECT_EQ(i * 2 + 1, results[i]);\n        }\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, collectAllVoid) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<void()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([&pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n          });\n        }\n\n        collectAll(funcs.begin(), funcs.end());\n        EXPECT_TRUE(pendingFibers.empty());\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nTEST(FiberManager, collectAny) {\n  std::vector<Promise<int>> pendingFibers;\n  bool taskAdded = false;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  auto loopFunc = [&]() {\n    if (!taskAdded) {\n      manager.addTask([&]() {\n        std::vector<std::function<int()>> funcs;\n        for (size_t i = 0; i < 3; ++i) {\n          funcs.emplace_back([i, &pendingFibers]() {\n            await_async([&pendingFibers](Promise<int> promise) {\n              pendingFibers.push_back(std::move(promise));\n            });\n            if (i == 1) {\n              throw std::runtime_error(\"This exception will be ignored\");\n            }\n            return i * 2 + 1;\n          });\n        }\n\n        auto result = collectAny(funcs.begin(), funcs.end());\n        EXPECT_EQ(2, pendingFibers.size());\n        EXPECT_EQ(2, result.first);\n        EXPECT_EQ(2 * 2 + 1, result.second);\n      });\n      taskAdded = true;\n    } else if (pendingFibers.size()) {\n      pendingFibers.back().setValue(0);\n      pendingFibers.pop_back();\n    } else {\n      loopController.stop();\n    }\n  };\n\n  loopController.loop(std::move(loopFunc));\n}\n\nnamespace {\n/* Checks that this function was run from a main context,\n   by comparing an address on a stack to a known main stack address\n   and a known related fiber stack address.  The assumption\n   is that fiber stack and main stack will be far enough apart,\n   while any two values on the same stack will be close. */\nvoid expectMainContext(bool& ran, int* mainLocation, int* fiberLocation) {\n  int here;\n  /* 2 pages is a good guess */\n  constexpr auto const kHereToFiberMaxDist = 0x2000 / sizeof(int);\n\n  // With ASAN's detect_stack_use_after_return=1, this must be much larger\n  // I measured 410028 on x86_64, so allow for quadruple that, just in case.\n  constexpr auto const kHereToMainMaxDist =\n      folly::kIsSanitizeAddress ? 4 * 410028 : kHereToFiberMaxDist;\n\n  if (fiberLocation) {\n    EXPECT_GT(std::abs(&here - fiberLocation), kHereToFiberMaxDist);\n  }\n  if (mainLocation) {\n    EXPECT_LT(std::abs(&here - mainLocation), kHereToMainMaxDist);\n  }\n\n  EXPECT_FALSE(ran);\n  ran = true;\n}\n} // namespace\n\nTEST(FiberManager, runInMainContext) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  bool checkRan = false;\n\n  int mainLocation;\n  manager.runInMainContext([&]() {\n    expectMainContext(checkRan, &mainLocation, nullptr);\n  });\n  EXPECT_TRUE(checkRan);\n\n  checkRan = false;\n\n  struct A {\n    explicit A(int value_) : value(value_) {}\n    A(const A&) = delete;\n    A(A&&) = default;\n\n    int value;\n  };\n\n  manager.addTask([&]() {\n    int stackLocation;\n    auto ret = runInMainContext([&]() {\n      expectMainContext(checkRan, &mainLocation, &stackLocation);\n      return A(42);\n    });\n    EXPECT_TRUE(checkRan);\n    EXPECT_EQ(42, ret.value);\n  });\n\n  loopController.loop([&]() { loopController.stop(); });\n\n  EXPECT_TRUE(checkRan);\n}\n\nnamespace {\n\nFOLLY_NOINLINE int runHugeStackInMainContext(bool& checkRan) {\n  auto ret = runInMainContext([&checkRan]() {\n    std::array<unsigned char, 1 << 20> buf;\n    buf.fill(42);\n    checkRan = true;\n    return buf[time(nullptr) % buf.size()];\n  });\n  EXPECT_TRUE(checkRan);\n  EXPECT_EQ(42, ret);\n  return ret;\n}\n\n} // namespace\n\nTEST(FiberManager, runInMainContextNoInline) {\n  FiberManager::Options opts;\n  opts.recordStackEvery = 1;\n  FiberManager manager(std::make_unique<SimpleLoopController>(), opts);\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  bool checkRan = false;\n  manager.addTask([&] { return runHugeStackInMainContext(checkRan); });\n\n  EXPECT_TRUE(manager.stackHighWatermark() < 100000);\n\n  loopController.loop([&]() { loopController.stop(); });\n\n  EXPECT_TRUE(checkRan);\n}\n\nTEST(FiberManager, addTaskFinally) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  bool checkRan = false;\n\n  int mainLocation;\n\n  manager.addTaskFinally(\n      [&]() { return 1234; },\n      [&](Try<int>&& result) {\n        EXPECT_EQ(result.value(), 1234);\n\n        expectMainContext(checkRan, &mainLocation, nullptr);\n      });\n\n  EXPECT_FALSE(checkRan);\n\n  loopController.loop([&]() { loopController.stop(); });\n\n  EXPECT_TRUE(checkRan);\n}\n\nTEST(FiberManager, fibersPoolWithinLimit) {\n  FiberManager::Options opts;\n  opts.maxFibersPoolSize = 5;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>(), opts);\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  size_t fibersRun = 0;\n\n  for (size_t i = 0; i < 5; ++i) {\n    manager.addTask([&]() { ++fibersRun; });\n  }\n  loopController.loop([&]() { loopController.stop(); });\n\n  EXPECT_EQ(5, fibersRun);\n  EXPECT_EQ(5, manager.fibersAllocated());\n  EXPECT_EQ(5, manager.fibersPoolSize());\n\n  for (size_t i = 0; i < 5; ++i) {\n    manager.addTask([&]() { ++fibersRun; });\n  }\n  loopController.loop([&]() { loopController.stop(); });\n\n  EXPECT_EQ(10, fibersRun);\n  EXPECT_EQ(5, manager.fibersAllocated());\n  EXPECT_EQ(5, manager.fibersPoolSize());\n}\n\nTEST(FiberManager, fibersPoolOverLimit) {\n  FiberManager::Options opts;\n  opts.maxFibersPoolSize = 5;\n\n  FiberManager manager(std::make_unique<SimpleLoopController>(), opts);\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  size_t fibersRun = 0;\n\n  for (size_t i = 0; i < 10; ++i) {\n    manager.addTask([&]() { ++fibersRun; });\n  }\n\n  EXPECT_EQ(0, fibersRun);\n  EXPECT_EQ(10, manager.fibersAllocated());\n  EXPECT_EQ(0, manager.fibersPoolSize());\n\n  loopController.loop([&]() { loopController.stop(); });\n\n  EXPECT_EQ(10, fibersRun);\n  EXPECT_EQ(5, manager.fibersAllocated());\n  EXPECT_EQ(5, manager.fibersPoolSize());\n}\n\nTEST(FiberManager, remoteFiberBasic) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  int result[2];\n  result[0] = result[1] = 0;\n  folly::Optional<Promise<int>> savedPromise[2];\n  manager.addTask([&]() {\n    result[0] = await_async([&](Promise<int> promise) {\n      savedPromise[0] = std::move(promise);\n    });\n  });\n  manager.addTask([&]() {\n    result[1] = await_async([&](Promise<int> promise) {\n      savedPromise[1] = std::move(promise);\n    });\n  });\n\n  manager.loopUntilNoReady();\n\n  EXPECT_TRUE(savedPromise[0].has_value());\n  EXPECT_TRUE(savedPromise[1].has_value());\n  EXPECT_EQ(0, result[0]);\n  EXPECT_EQ(0, result[1]);\n\n  std::thread remoteThread0{[&]() { savedPromise[0]->setValue(42); }};\n  std::thread remoteThread1{[&]() { savedPromise[1]->setValue(43); }};\n  remoteThread0.join();\n  remoteThread1.join();\n  EXPECT_EQ(0, result[0]);\n  EXPECT_EQ(0, result[1]);\n  /* Should only have scheduled once */\n  EXPECT_EQ(1, loopController.remoteScheduleCalled());\n\n  manager.loopUntilNoReady();\n  EXPECT_EQ(42, result[0]);\n  EXPECT_EQ(43, result[1]);\n}\n\nTEST(FiberManager, addTaskRemoteBasic) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n\n  int result[2];\n  result[0] = result[1] = 0;\n  folly::Optional<Promise<int>> savedPromise[2];\n\n  std::thread remoteThread0{[&]() {\n    manager.addTaskRemote([&]() {\n      result[0] = await_async([&](Promise<int> promise) {\n        savedPromise[0] = std::move(promise);\n      });\n    });\n  }};\n  std::thread remoteThread1{[&]() {\n    manager.addTaskRemote([&]() {\n      result[1] = await_async([&](Promise<int> promise) {\n        savedPromise[1] = std::move(promise);\n      });\n    });\n  }};\n  remoteThread0.join();\n  remoteThread1.join();\n\n  manager.loopUntilNoReady();\n\n  EXPECT_TRUE(savedPromise[0].has_value());\n  EXPECT_TRUE(savedPromise[1].has_value());\n  EXPECT_EQ(0, result[0]);\n  EXPECT_EQ(0, result[1]);\n\n  savedPromise[0]->setValue(42);\n  savedPromise[1]->setValue(43);\n\n  EXPECT_EQ(0, result[0]);\n  EXPECT_EQ(0, result[1]);\n\n  manager.loopUntilNoReady();\n  EXPECT_EQ(42, result[0]);\n  EXPECT_EQ(43, result[1]);\n}\n\nTEST(FiberManager, remoteHasTasks) {\n  size_t counter = 0;\n  FiberManager fm(std::make_unique<SimpleLoopController>());\n  std::thread remote([&]() { fm.addTaskRemote([&]() { ++counter; }); });\n\n  remote.join();\n\n  while (fm.hasTasks()) {\n    fm.loopUntilNoReady();\n  }\n\n  EXPECT_FALSE(fm.hasTasks());\n  EXPECT_EQ(counter, 1);\n}\n\nTEST(FiberManager, remoteHasReadyTasks) {\n  int result = 0;\n  folly::Optional<Promise<int>> savedPromise;\n  FiberManager fm(std::make_unique<SimpleLoopController>());\n  std::thread remote([&]() {\n    fm.addTaskRemote([&]() {\n      result = await_async([&](Promise<int> promise) {\n        savedPromise = std::move(promise);\n      });\n      EXPECT_TRUE(fm.hasTasks());\n    });\n  });\n\n  remote.join();\n  EXPECT_TRUE(fm.hasTasks());\n\n  fm.loopUntilNoReady();\n  EXPECT_TRUE(fm.hasTasks());\n\n  std::thread remote2([&]() { savedPromise->setValue(47); });\n  remote2.join();\n  EXPECT_TRUE(fm.hasTasks());\n\n  fm.loopUntilNoReady();\n  EXPECT_FALSE(fm.hasTasks());\n\n  EXPECT_EQ(result, 47);\n}\n\ntemplate <typename Data>\nvoid testFiberLocal() {\n  FiberManager fm(LocalType<Data>(), std::make_unique<SimpleLoopController>());\n\n  fm.addTask([]() {\n    EXPECT_EQ(42, local<Data>().value);\n\n    local<Data>().value = 43;\n\n    addTask([]() {\n      EXPECT_EQ(43, local<Data>().value);\n\n      local<Data>().value = 44;\n\n      addTask([]() { EXPECT_EQ(44, local<Data>().value); });\n    });\n  });\n\n  fm.addTask([&]() {\n    EXPECT_EQ(42, local<Data>().value);\n\n    local<Data>().value = 43;\n\n    fm.addTaskRemote([]() { EXPECT_EQ(43, local<Data>().value); });\n  });\n\n  fm.addTask([]() {\n    EXPECT_EQ(42, local<Data>().value);\n    local<Data>().value = 43;\n\n    auto task = []() {\n      EXPECT_EQ(43, local<Data>().value);\n      local<Data>().value = 44;\n    };\n    std::vector<std::function<void()>> tasks{task};\n    collectAny(tasks.begin(), tasks.end());\n\n    EXPECT_EQ(43, local<Data>().value);\n  });\n\n  fm.loopUntilNoReady();\n  EXPECT_FALSE(fm.hasTasks());\n}\n\nTEST(FiberManager, fiberLocal) {\n  struct SimpleData {\n    int value{42};\n  };\n\n  testFiberLocal<SimpleData>();\n}\n\nTEST(FiberManager, fiberLocalHeap) {\n  struct LargeData {\n    char _[1024 * 1024];\n    int value{42};\n  };\n\n  testFiberLocal<LargeData>();\n}\n\nTEST(FiberManager, fiberLocalDestructor) {\n  struct CrazyData {\n    size_t data{42};\n\n    ~CrazyData() {\n      if (data == 41) {\n        addTask([]() {\n          EXPECT_EQ(42, local<CrazyData>().data);\n          // Make sure we don't have infinite loop\n          local<CrazyData>().data = 0;\n        });\n      }\n    }\n  };\n\n  FiberManager fm(\n      LocalType<CrazyData>(), std::make_unique<SimpleLoopController>());\n\n  fm.addTask([]() { local<CrazyData>().data = 41; });\n\n  fm.loopUntilNoReady();\n  EXPECT_FALSE(fm.hasTasks());\n}\n\nTEST(FiberManager, yieldTest) {\n  FiberManager manager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(manager.loopController());\n\n  bool checkRan = false;\n\n  manager.addTask([&]() {\n    manager.yield();\n    checkRan = true;\n  });\n\n  loopController.loop([&]() {\n    if (checkRan) {\n      loopController.stop();\n    }\n  });\n\n  EXPECT_TRUE(checkRan);\n}\n\nTEST(FiberManager, RequestContext) {\n  FiberManager fm(std::make_unique<SimpleLoopController>());\n\n  bool checkRun1 = false;\n  bool checkRun2 = false;\n  bool checkRun3 = false;\n  bool checkRun4 = false;\n  folly::fibers::Baton baton1;\n  folly::fibers::Baton baton2;\n  folly::fibers::Baton baton3;\n  folly::fibers::Baton baton4;\n\n  {\n    folly::RequestContextScopeGuard rctx;\n    auto rcontext1 = folly::RequestContext::get();\n    fm.addTask([&, rcontext1]() {\n      EXPECT_EQ(rcontext1, folly::RequestContext::get());\n      baton1.wait([&]() {\n        EXPECT_EQ(rcontext1, folly::RequestContext::get());\n      });\n      EXPECT_EQ(rcontext1, folly::RequestContext::get());\n      runInMainContext([&]() {\n        EXPECT_EQ(rcontext1, folly::RequestContext::get());\n      });\n      checkRun1 = true;\n    });\n  }\n  {\n    folly::RequestContextScopeGuard rctx;\n    auto rcontext2 = folly::RequestContext::get();\n    fm.addTaskRemote([&, rcontext2]() {\n      EXPECT_EQ(rcontext2, folly::RequestContext::get());\n      baton2.wait();\n      EXPECT_EQ(rcontext2, folly::RequestContext::get());\n      checkRun2 = true;\n    });\n  }\n  {\n    folly::RequestContextScopeGuard rctx;\n    auto rcontext3 = folly::RequestContext::get();\n    fm.addTaskFinally(\n        [&, rcontext3]() {\n          EXPECT_EQ(rcontext3, folly::RequestContext::get());\n          baton3.wait();\n          EXPECT_EQ(rcontext3, folly::RequestContext::get());\n\n          return folly::Unit();\n        },\n        [&, rcontext3](Try<folly::Unit>&& /* t */) {\n          EXPECT_EQ(rcontext3, folly::RequestContext::get());\n          checkRun3 = true;\n        });\n  }\n  {\n    folly::RequestContextScopeGuard rctx;\n    fm.addTask([&]() {\n      folly::RequestContextScopeGuard rctx2;\n      auto rcontext4 = folly::RequestContext::get();\n      baton4.wait();\n      EXPECT_EQ(rcontext4, folly::RequestContext::get());\n      checkRun4 = true;\n    });\n  }\n  {\n    folly::RequestContextScopeGuard rctx;\n    auto rcontext = folly::RequestContext::get();\n\n    fm.loopUntilNoReady();\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n\n    baton1.post();\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n    fm.loopUntilNoReady();\n    EXPECT_TRUE(checkRun1);\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n\n    baton2.post();\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n    fm.loopUntilNoReady();\n    EXPECT_TRUE(checkRun2);\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n\n    baton3.post();\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n    fm.loopUntilNoReady();\n    EXPECT_TRUE(checkRun3);\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n\n    baton4.post();\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n    fm.loopUntilNoReady();\n    EXPECT_TRUE(checkRun4);\n    EXPECT_EQ(rcontext, folly::RequestContext::get());\n  }\n}\n\nTEST(FiberManager, resizePeriodically) {\n  FiberManager::Options opts;\n  opts.fibersPoolResizePeriodMs = 300;\n  opts.maxFibersPoolSize = 5;\n\n  FiberManager manager(std::make_unique<EventBaseLoopController>(), opts);\n\n  folly::EventBase evb;\n  dynamic_cast<EventBaseLoopController&>(manager.loopController())\n      .attachEventBase(evb);\n\n  std::vector<Baton> batons(10);\n\n  size_t tasksRun = 0;\n  for (size_t i = 0; i < 30; ++i) {\n    manager.addTask([i, &batons, &tasksRun]() {\n      ++tasksRun;\n      // Keep some fibers active indefinitely\n      if (i < batons.size()) {\n        batons[i].wait();\n      }\n    });\n  }\n\n  EXPECT_EQ(0, tasksRun);\n  EXPECT_EQ(30, manager.fibersAllocated());\n  EXPECT_EQ(0, manager.fibersPoolSize());\n\n  evb.loopOnce();\n  EXPECT_EQ(30, tasksRun);\n  EXPECT_EQ(30, manager.fibersAllocated());\n  // Can go over maxFibersPoolSize, 10 of 30 fibers still active\n  EXPECT_EQ(20, manager.fibersPoolSize());\n\n  std::this_thread::sleep_for(std::chrono::milliseconds(400));\n  evb.loopOnce(); // no fibers active in this period\n  EXPECT_EQ(30, manager.fibersAllocated());\n  EXPECT_EQ(20, manager.fibersPoolSize());\n\n  std::this_thread::sleep_for(std::chrono::milliseconds(400));\n  evb.loopOnce(); // should shrink fibers pool to maxFibersPoolSize\n  EXPECT_EQ(15, manager.fibersAllocated());\n  EXPECT_EQ(5, manager.fibersPoolSize());\n\n  for (size_t i = 0; i < batons.size(); ++i) {\n    batons[i].post();\n  }\n  evb.loopOnce();\n  EXPECT_EQ(15, manager.fibersAllocated());\n  EXPECT_EQ(15, manager.fibersPoolSize());\n\n  std::this_thread::sleep_for(std::chrono::milliseconds(400));\n  evb.loopOnce(); // 10 fibers active in last period\n  EXPECT_EQ(10, manager.fibersAllocated());\n  EXPECT_EQ(10, manager.fibersPoolSize());\n\n  std::this_thread::sleep_for(std::chrono::milliseconds(400));\n  evb.loopOnce();\n  EXPECT_EQ(5, manager.fibersAllocated());\n  EXPECT_EQ(5, manager.fibersPoolSize());\n\n  // Sleep again before destruction to force the case where the\n  // resize timer fires during destruction of the EventBase.\n  std::this_thread::sleep_for(std::chrono::milliseconds(400));\n}\n\nTEST(FiberManager, batonWaitTimeoutHandler) {\n  FiberManager manager(std::make_unique<EventBaseLoopController>());\n\n  folly::EventBase evb;\n  dynamic_cast<EventBaseLoopController&>(manager.loopController())\n      .attachEventBase(evb);\n\n  size_t fibersRun = 0;\n  Baton baton;\n  Baton::TimeoutHandler timeoutHandler;\n\n  manager.addTask([&]() {\n    baton.wait(timeoutHandler);\n    ++fibersRun;\n  });\n  manager.loopUntilNoReady();\n\n  EXPECT_FALSE(baton.try_wait());\n  EXPECT_EQ(0, fibersRun);\n\n  timeoutHandler.scheduleTimeout(std::chrono::milliseconds(250));\n  std::this_thread::sleep_for(std::chrono::milliseconds(500));\n\n  EXPECT_FALSE(baton.try_wait());\n  EXPECT_EQ(0, fibersRun);\n\n  evb.loopOnce();\n  manager.loopUntilNoReady();\n\n  EXPECT_EQ(1, fibersRun);\n}\n\nTEST(FiberManager, batonWaitTimeoutHandlerExecutor) {\n  Baton baton2;\n  folly::CPUThreadPoolExecutor executor(1);\n  FiberManager manager(std::make_unique<ExecutorLoopController>(&executor));\n  auto task = [&](size_t timeout_ms) {\n    Baton baton;\n    auto start = std::chrono::steady_clock::now();\n    auto res = baton.try_wait_for(std::chrono::milliseconds(timeout_ms));\n    auto finish = std::chrono::steady_clock::now();\n    EXPECT_FALSE(res);\n    auto duration_ms =\n        std::chrono::duration_cast<std::chrono::milliseconds>(finish - start)\n            .count();\n\n    EXPECT_GT(duration_ms, timeout_ms - 10);\n    EXPECT_LT(duration_ms, timeout_ms + 100);\n    baton2.post();\n  };\n  manager.addTask([&]() { task(300); });\n  baton2.wait();\n  executor.join();\n}\n\nTEST(FiberManager, batonWaitTimeoutMany) {\n  FiberManager manager(std::make_unique<EventBaseLoopController>());\n\n  folly::EventBase evb;\n  dynamic_cast<EventBaseLoopController&>(manager.loopController())\n      .attachEventBase(evb);\n\n  // TODO(T71050527): It appears that TSAN does not yet maintain the shadow\n  // stack correctly upon fiber switches, resulting in a shadow stack overflow\n  // if we push too many tasks here. Cap it in the meantime.\n  constexpr size_t kNumTimeoutTasks = folly::kIsSanitizeThread ? 1000 : 10000;\n  size_t tasksCount = kNumTimeoutTasks;\n\n  // We add many tasks to hit timeout queue deallocation logic.\n  for (size_t i = 0; i < kNumTimeoutTasks; ++i) {\n    manager.addTask([&]() {\n      Baton baton;\n      Baton::TimeoutHandler timeoutHandler;\n\n      folly::fibers::addTask([&] {\n        timeoutHandler.scheduleTimeout(std::chrono::milliseconds(1000));\n      });\n\n      baton.wait(timeoutHandler);\n      if (--tasksCount == 0) {\n        evb.terminateLoopSoon();\n      }\n    });\n  }\n\n  evb.loopForever();\n}\n\nTEST(FiberManager, remoteFutureTest) {\n  FiberManager fiberManager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());\n\n  int testValue1 = 5;\n  int testValue2 = 7;\n  auto f1 = fiberManager.addTaskFuture([&]() { return testValue1; });\n  auto f2 = fiberManager.addTaskRemoteFuture([&]() { return testValue2; });\n  loopController.loop([&]() { loopController.stop(); });\n  auto v1 = std::move(f1).get();\n  auto v2 = std::move(f2).get();\n\n  EXPECT_EQ(v1, testValue1);\n  EXPECT_EQ(v2, testValue2);\n}\n\n// Test that a void function produes a Future<Unit>.\nTEST(FiberManager, remoteFutureVoidUnitTest) {\n  FiberManager fiberManager(std::make_unique<SimpleLoopController>());\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());\n\n  bool ranLocal = false;\n  folly::Future<folly::Unit> futureLocal = fiberManager.addTaskFuture([&]() {\n    ranLocal = true;\n  });\n\n  bool ranRemote = false;\n  folly::Future<folly::Unit> futureRemote =\n      fiberManager.addTaskRemoteFuture([&]() { ranRemote = true; });\n\n  loopController.loop([&]() { loopController.stop(); });\n\n  futureLocal.wait();\n  ASSERT_TRUE(ranLocal);\n\n  futureRemote.wait();\n  ASSERT_TRUE(ranRemote);\n}\n\nTEST(FiberManager, nestedFiberManagers) {\n  folly::EventBase outerEvb;\n  folly::EventBase innerEvb;\n\n  getFiberManager(outerEvb).addTask([&]() {\n    EXPECT_EQ(\n        &getFiberManager(outerEvb), FiberManager::getFiberManagerUnsafe());\n\n    runInMainContext([&]() {\n      getFiberManager(innerEvb).addTask([&]() {\n        EXPECT_EQ(\n            &getFiberManager(innerEvb), FiberManager::getFiberManagerUnsafe());\n\n        innerEvb.terminateLoopSoon();\n      });\n\n      innerEvb.loopForever();\n    });\n\n    EXPECT_EQ(\n        &getFiberManager(outerEvb), FiberManager::getFiberManagerUnsafe());\n\n    outerEvb.terminateLoopSoon();\n  });\n\n  outerEvb.loopForever();\n}\n\nTEST(FiberManager, nestedFiberManagersSameEvb) {\n  folly::EventBase evb;\n  auto& fm1 = getFiberManager(evb);\n  EXPECT_EQ(&fm1, &getFiberManager(evb));\n\n  // Always return the same fm by default\n  FiberManager::Options unused;\n  unused.stackSize = 1024;\n  EXPECT_EQ(&fm1, &getFiberManager(evb, unused));\n\n  // Use frozen options\n  FiberManager::Options used;\n  used.stackSize = 2048;\n  FiberManager::FrozenOptions options{used};\n  auto& fm2 = getFiberManager(evb, options);\n  EXPECT_NE(&fm1, &fm2);\n\n  // Same option\n  EXPECT_EQ(&fm2, &getFiberManager(evb, options));\n  EXPECT_EQ(&fm2, &getFiberManager(evb, FiberManager::FrozenOptions{used}));\n  FiberManager::Options same;\n  same.stackSize = 2048;\n  EXPECT_EQ(&fm2, &getFiberManager(evb, FiberManager::FrozenOptions{same}));\n\n  // Different option\n  FiberManager::Options differ;\n  differ.stackSize = 4096;\n  auto& fm3 = getFiberManager(evb, FiberManager::FrozenOptions{differ});\n  EXPECT_NE(&fm1, &fm3);\n  EXPECT_NE(&fm2, &fm3);\n\n  // Nested usage\n  getFiberManager(evb)\n      .addTaskFuture([&] {\n        EXPECT_EQ(&fm1, FiberManager::getFiberManagerUnsafe());\n\n        getFiberManager(evb, options)\n            .addTaskFuture([&] {\n              EXPECT_EQ(&fm2, FiberManager::getFiberManagerUnsafe());\n            })\n            .wait();\n      })\n      .waitVia(&evb);\n}\n\nTEST(FiberManager, virtualEvbFiberManager) {\n  folly::EventBase evb;\n  auto& vevb = evb.getVirtualEventBase();\n  // Eventbase vs VirtualEventBase used for multiplexing\n  auto& fm1 = getFiberManager(evb);\n  auto& fm2 = getFiberManager(vevb);\n  EXPECT_NE(&fm1, &fm2);\n\n  FiberManager::Options opt;\n  opt.stackSize = 1024;\n\n  // Option is not used in multiplexing\n  auto& fm3 = getFiberManager(evb, opt);\n  auto& fm4 = getFiberManager(vevb, opt);\n  EXPECT_EQ(&fm1, &fm3);\n  EXPECT_EQ(&fm2, &fm4);\n\n  // Frozen option used in multiplexing\n  FiberManager::FrozenOptions fopt(opt);\n  auto& fm5 = getFiberManager(evb, fopt);\n  auto& fm6 = getFiberManager(vevb, fopt);\n  EXPECT_NE(&fm1, &fm5);\n  EXPECT_NE(&fm2, &fm6);\n}\n\nTEST(FiberManager, semaphore) {\n  static constexpr size_t kTasks = 10;\n  static constexpr size_t kIterations = 10000;\n  static constexpr size_t kNumTokens = 10;\n  static constexpr size_t kNumThreads = 16;\n\n  Semaphore sem(kNumTokens);\n  EXPECT_EQ(sem.getCapacity(), kNumTokens);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens);\n\n  sem.wait();\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens - 1);\n  sem.wait();\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens - 2);\n\n  sem.signal();\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens - 1);\n  sem.signal();\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens);\n\n  struct Worker {\n    explicit Worker(Semaphore& s) : sem(s), t([&] { run(); }) {}\n\n    void run() {\n      FiberManager manager(std::make_unique<EventBaseLoopController>());\n      folly::EventBase evb;\n      dynamic_cast<EventBaseLoopController&>(manager.loopController())\n          .attachEventBase(evb);\n\n      {\n        std::shared_ptr<folly::EventBase> completionCounter(\n            &evb, [](folly::EventBase* evb_) { evb_->terminateLoopSoon(); });\n\n        for (size_t i = 0; i < kTasks; ++i) {\n          manager.addTask([&, completionCounter]() {\n            for (size_t j = 0; j < kIterations; ++j) {\n              switch (j % 4) {\n                case 0:\n                  sem.wait();\n                  break;\n                case 1:\n                  sem.future_wait().get();\n                  break;\n                case 2: {\n                  Semaphore::Waiter waiter;\n                  bool acquired = sem.try_wait(waiter);\n                  if (!acquired) {\n                    waiter.baton.wait();\n                  }\n                  break;\n                }\n                case 3:\n                  if (!sem.try_wait()) {\n                    sem.wait();\n                  }\n                  break;\n              }\n              ++counter;\n              sem.signal();\n              --counter;\n\n              EXPECT_LT(counter, kNumTokens);\n              EXPECT_GE(counter, 0);\n            }\n          });\n        }\n      }\n      evb.loopForever();\n    }\n\n    Semaphore& sem;\n    int counter{0};\n    std::thread t;\n  };\n\n  std::vector<Worker> workers;\n  workers.reserve(kNumThreads);\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    workers.emplace_back(sem);\n  }\n\n  for (auto& worker : workers) {\n    worker.t.join();\n  }\n\n  for (auto& worker : workers) {\n    EXPECT_EQ(0, worker.counter);\n  }\n\n  EXPECT_EQ(sem.getCapacity(), kNumTokens);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens);\n}\n\n#if FOLLY_HAS_COROUTINES\n\nCO_TEST(FiberManager, SemaphoreCoTryWaitForCanCancelOrTimeout) {\n  using namespace std::chrono_literals;\n\n  {\n    // Pre-cancel\n    Semaphore sem{0};\n    folly::CancellationSource cancelSource;\n    cancelSource.requestCancellation();\n    EXPECT_THROW(\n        co_await folly::coro::co_withCancellation(\n            cancelSource.getToken(), sem.co_try_wait_for(1h)),\n        folly::OperationCancelled);\n  }\n  {\n    // Cancel\n    Semaphore sem{0};\n    folly::CancellationSource cancelSource;\n    auto t = std::thread([&]() { cancelSource.requestCancellation(); });\n    EXPECT_THROW(\n        co_await folly::coro::co_withCancellation(\n            cancelSource.getToken(), sem.co_try_wait_for(1h)),\n        folly::OperationCancelled);\n    t.join();\n  }\n  {\n    // Timeout\n    Semaphore sem{0};\n    folly::CancellationSource cancelSource;\n    EXPECT_THROW(\n        co_await folly::coro::co_withCancellation(\n            cancelSource.getToken(), sem.co_try_wait_for(1ms)),\n        folly::OperationCancelled);\n  }\n}\n\nCO_TEST(FiberManager, StressTestSemaphoreCoTryWaitFor) {\n  static constexpr size_t kIterations = 10000;\n\n  Semaphore readyToRead{0};\n  Semaphore sem{0};\n\n  auto t = std::thread([&]() {\n    for (size_t i = 0; i < kIterations; ++i) {\n      readyToRead.wait();\n      sem.signal();\n    }\n  });\n\n  auto waitForSignal = [&](size_t i) -> folly::coro::Task<void> {\n    std::chrono::milliseconds timeout{0};\n    auto increaseTimeout = [&]() {\n      if (timeout.count() == 0) {\n        timeout = std::chrono::milliseconds{1};\n      } else if (timeout < std::chrono::milliseconds{10000}) {\n        timeout *= 2;\n      }\n    };\n    readyToRead.signal();\n    while (true) {\n      try {\n        co_await sem.co_try_wait_for(timeout);\n        co_return;\n      } catch (folly::OperationCancelled&) {\n        increaseTimeout();\n      }\n    }\n  };\n\n  for (size_t i = 0; i < kIterations; ++i) {\n    co_await waitForSignal(i);\n  }\n\n  t.join();\n}\n\n#endif\n\nTEST(FiberManager, batchSemaphore) {\n  static constexpr size_t kTasks = 10;\n  static constexpr size_t kIterations = 10000;\n  static constexpr size_t kNumTokens = 60;\n  static constexpr size_t kNumThreads = 16;\n\n  BatchSemaphore sem(kNumTokens);\n  EXPECT_EQ(sem.getCapacity(), kNumTokens);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens);\n\n  sem.wait(20);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens - 20);\n  sem.wait(30);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens - 20 - 30);\n\n  sem.signal(30);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens - 20);\n  sem.signal(20);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens);\n\n  struct Worker {\n    explicit Worker(BatchSemaphore& s) : sem(s), t([&] { run(); }) {}\n\n    void run() {\n      FiberManager manager(std::make_unique<EventBaseLoopController>());\n      folly::EventBase evb;\n      dynamic_cast<EventBaseLoopController&>(manager.loopController())\n          .attachEventBase(evb);\n\n      {\n        std::shared_ptr<folly::EventBase> completionCounter(\n            &evb, [](folly::EventBase* evb_) { evb_->terminateLoopSoon(); });\n\n        for (size_t i = 0; i < kTasks; ++i) {\n          manager.addTask([&, completionCounter]() {\n            for (size_t j = 0; j < kIterations; ++j) {\n              int tokens = j % 5 + 1;\n              switch (j % 5) {\n                case 0:\n                  sem.wait(tokens);\n                  break;\n                case 1:\n                  sem.future_wait(tokens).get();\n                  break;\n                case 2: {\n                  BatchSemaphore::Waiter waiter{tokens};\n                  bool acquired = sem.try_wait(waiter, tokens);\n                  if (!acquired) {\n                    waiter.baton.wait();\n                  }\n                  break;\n                }\n                case 3:\n                  folly::coro::blockingWait(sem.co_wait(tokens));\n                  break;\n                case 4: {\n                  if (!sem.try_wait(tokens)) {\n                    sem.wait(tokens);\n                  }\n                  break;\n                }\n              }\n              counter += tokens;\n              sem.signal(tokens);\n              counter -= tokens;\n\n              EXPECT_LT(counter, kNumTokens);\n              EXPECT_GE(counter, 0);\n            }\n          });\n        }\n      }\n      evb.loopForever();\n    }\n\n    BatchSemaphore& sem;\n    int counter{0};\n    std::thread t;\n  };\n\n  std::vector<Worker> workers;\n  workers.reserve(kNumThreads);\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    workers.emplace_back(sem);\n  }\n\n  for (auto& worker : workers) {\n    worker.t.join();\n  }\n\n  for (auto& worker : workers) {\n    EXPECT_EQ(0, worker.counter);\n  }\n\n  EXPECT_EQ(sem.getCapacity(), kNumTokens);\n  EXPECT_EQ(sem.getAvailableTokens(), kNumTokens);\n}\n\n/**\n * Verify that BatchSemaphore signals all waiters or fail by timeout.\n * Overall idea is to linearize waiters in the semaphore's list,\n * requesting incremental number of token. For example, [1, 2, 3, 4, 5] for a\n * total semaphore capacity of 5 tokens. When releasing all 5 tokens at once an\n * expected behavior is:\n *  - Return 5 tokens: notify [1, 2] and block [3, 4, 5] - 2 tokens left\n *  - Return 1 token: notify [3] and block [4, 5] - 0 tokens left\n *  - Return 2 tokens: and block [4, 5] - 2 tokens left\n *  - Return 3 tokens: notify [4] and block [5] - 1 token left\n *  - Return 4 tokens: notify [5] - 0 tokens left\n *  - Return 5 tokens: done - 5 tokens left\n */\nTEST(FiberManager, batchSemaphoreSignalAll) {\n  static constexpr size_t kNumWaiters = 5;\n\n  BatchSemaphore sem(kNumWaiters);\n  sem.wait(kNumWaiters);\n\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  for (size_t task = 0; task < kNumWaiters; task++) {\n    fm.addTask([&, tokens = int64_t(task) + 1] {\n      // Wait for semaphore and fail if not notified\n      BatchSemaphore::Waiter waiter{tokens};\n      bool acquired = sem.try_wait(waiter, tokens);\n      if (!acquired &&\n          !waiter.baton.try_wait_for(std::chrono::milliseconds(1000))) {\n        FAIL() << \"BatchSemaphore::Waiter has never been notified\";\n      }\n\n      // Rotate to the next task\n      Baton b;\n      b.try_wait_for(std::chrono::milliseconds(1));\n\n      sem.signal(tokens);\n    });\n  }\n\n  fm.addTask([&] {\n    // Release all tokens and notify waiters\n    sem.signal(kNumWaiters);\n  });\n\n  evb.loop();\n  EXPECT_FALSE(fm.hasTasks());\n}\n\ntemplate <typename ExecutorT>\nvoid singleBatchDispatch(ExecutorT& executor, int batchSize, int index) {\n  thread_local BatchDispatcher<int, std::string, ExecutorT> batchDispatcher(\n      executor, [batchSize](std::vector<int>&& batch) {\n        EXPECT_EQ(batchSize, batch.size());\n        std::vector<std::string> results;\n        for (auto& it : batch) {\n          results.push_back(folly::to<std::string>(it));\n        }\n        return results;\n      });\n\n  auto indexCopy = index;\n  auto result = batchDispatcher.add(std::move(indexCopy));\n  EXPECT_EQ(folly::to<std::string>(index), std::move(result).get());\n}\n\nTEST(FiberManager, batchDispatchTest) {\n  folly::EventBase evb;\n  auto& executor = getFiberManager(evb);\n\n  // Launch multiple fibers with a single id.\n  executor.add([&]() {\n    int batchSize = 10;\n    for (int i = 0; i < batchSize; i++) {\n      executor.add([=, &executor]() {\n        singleBatchDispatch(executor, batchSize, i);\n      });\n    }\n  });\n  evb.loop();\n\n  // Reuse the same BatchDispatcher to batch once again.\n  executor.add([&]() {\n    int batchSize = 10;\n    for (int i = 0; i < batchSize; i++) {\n      executor.add([=, &executor]() {\n        singleBatchDispatch(executor, batchSize, i);\n      });\n    }\n  });\n  evb.loop();\n}\n\ntemplate <typename ExecutorT>\nfolly::Future<std::vector<std::string>> doubleBatchInnerDispatch(\n    ExecutorT& executor, int totalNumberOfElements, std::vector<int> input) {\n  thread_local BatchDispatcher<\n      std::vector<int>,\n      std::vector<std::string>,\n      ExecutorT>\n      batchDispatcher(\n          executor,\n          [totalNumberOfElements](std::vector<std::vector<int>>&& batch) {\n            std::vector<std::vector<std::string>> results;\n            int numberOfElements = 0;\n            for (auto& unit : batch) {\n              numberOfElements += unit.size();\n              std::vector<std::string> result;\n              for (auto& element : unit) {\n                result.push_back(folly::to<std::string>(element));\n              }\n              results.push_back(std::move(result));\n            }\n            EXPECT_EQ(totalNumberOfElements, numberOfElements);\n            return results;\n          });\n\n  return batchDispatcher.add(std::move(input));\n}\n\n/**\n * Batch values in groups of 5, and then call inner dispatch.\n */\ntemplate <typename ExecutorT>\nvoid doubleBatchOuterDispatch(\n    ExecutorT& executor, int totalNumberOfElements, int index) {\n  thread_local BatchDispatcher<int, std::string, ExecutorT> batchDispatcher(\n      executor, [=, &executor](std::vector<int>&& batch) {\n        EXPECT_EQ(totalNumberOfElements, batch.size());\n        std::vector<std::string> results;\n        std::vector<folly::Future<std::vector<std::string>>>\n            innerDispatchResultFutures;\n\n        std::vector<int> group;\n        for (auto unit : batch) {\n          group.push_back(unit);\n          if (group.size() == 5) {\n            auto localGroup = group;\n            group.clear();\n\n            innerDispatchResultFutures.push_back(doubleBatchInnerDispatch(\n                executor, totalNumberOfElements, localGroup));\n          }\n        }\n\n        folly::collectAll(\n            innerDispatchResultFutures.begin(),\n            innerDispatchResultFutures.end())\n            .deferValue([&](std::vector<Try<std::vector<std::string>>>\n                                innerDispatchResults) {\n              for (auto& unit : innerDispatchResults) {\n                for (auto& element : unit.value()) {\n                  results.push_back(element);\n                }\n              }\n            })\n            .get();\n        return results;\n      });\n\n  auto indexCopy = index;\n  auto result = batchDispatcher.add(std::move(indexCopy));\n  EXPECT_EQ(folly::to<std::string>(index), std::move(result).get());\n}\n\nTEST(FiberManager, doubleBatchDispatchTest) {\n  folly::EventBase evb;\n  auto& executor = getFiberManager(evb);\n\n  // Launch multiple fibers with a single id.\n  executor.add([&]() {\n    int totalNumberOfElements = 20;\n    for (int i = 0; i < totalNumberOfElements; i++) {\n      executor.add([=, &executor]() {\n        doubleBatchOuterDispatch(executor, totalNumberOfElements, i);\n      });\n    }\n  });\n  evb.loop();\n}\n\ntemplate <typename ExecutorT>\nvoid batchDispatchExceptionHandling(ExecutorT& executor, int i) {\n  thread_local BatchDispatcher<int, int, ExecutorT> batchDispatcher(\n      executor, [](std::vector<int>&&) -> std::vector<int> {\n        throw std::runtime_error(\"Surprise!!\");\n      });\n\n  EXPECT_THROW(batchDispatcher.add(i).get(), std::runtime_error);\n}\n\nTEST(FiberManager, batchDispatchExceptionHandlingTest) {\n  folly::EventBase evb;\n  auto& executor = getFiberManager(evb);\n\n  // Launch multiple fibers with a single id.\n  executor.add([&]() {\n    int totalNumberOfElements = 5;\n    for (int i = 0; i < totalNumberOfElements; i++) {\n      executor.add([=, &executor]() {\n        batchDispatchExceptionHandling(executor, i);\n      });\n    }\n  });\n  evb.loop();\n}\n\nnamespace AtomicBatchDispatcherTesting {\n\nusing ValueT = size_t;\nusing ResultT = std::string;\nusing DispatchFunctionT =\n    folly::Function<std::vector<ResultT>(std::vector<ValueT>&&)>;\n\n#define ENABLE_TRACE_IN_TEST 0 // Set to 1 to debug issues in ABD tests\n#if ENABLE_TRACE_IN_TEST\n#define OUTPUT_TRACE std::cerr\n#else // ENABLE_TRACE_IN_TEST\nstruct DevNullPiper {\n  template <typename T>\n  DevNullPiper& operator<<(const T&) {\n    return *this;\n  }\n\n  DevNullPiper& operator<<(std::ostream& (*)(std::ostream&)) { return *this; }\n} devNullPiper;\n#define OUTPUT_TRACE devNullPiper\n#endif // ENABLE_TRACE_IN_TEST\n\nstruct Job {\n  AtomicBatchDispatcher<ValueT, ResultT>::Token token;\n  ValueT input;\n\n  void preprocess(FiberManager& executor, bool die) {\n    // Yield for a random duration [0, 10] ms to simulate I/O in preprocessing\n    clock_t msecToDoIO = folly::Random::rand32() % 10;\n    double start = (1000.0 * clock()) / CLOCKS_PER_SEC;\n    double endAfter = start + msecToDoIO;\n    while ((1000.0 * clock()) / CLOCKS_PER_SEC < endAfter) {\n      executor.yield();\n    }\n    if (die) {\n      throw std::logic_error(\"Simulating preprocessing failure\");\n    }\n  }\n\n  Job(AtomicBatchDispatcher<ValueT, ResultT>::Token&& t, ValueT i)\n      : token(std::move(t)), input(i) {}\n\n  Job(Job&&) = default;\n  Job& operator=(Job&&) = default;\n};\n\nResultT processSingleInput(ValueT&& input) {\n  return folly::to<ResultT>(std::move(input));\n}\n\nstd::vector<ResultT> userDispatchFunc(std::vector<ValueT>&& inputs) {\n  size_t expectedCount = inputs.size();\n  std::vector<ResultT> results;\n  results.reserve(expectedCount);\n  for (size_t i = 0; i < expectedCount; ++i) {\n    results.emplace_back(processSingleInput(std::move(inputs[i])));\n  }\n  return results;\n}\n\nvoid createJobs(\n    AtomicBatchDispatcher<ValueT, ResultT>& atomicBatchDispatcher,\n    std::vector<Job>& jobs,\n    size_t count) {\n  jobs.clear();\n  for (size_t i = 0; i < count; ++i) {\n    jobs.emplace_back(atomicBatchDispatcher.getToken(), i);\n  }\n}\n\nenum class DispatchProblem {\n  None,\n  PreprocessThrows,\n  DuplicateDispatch,\n};\n\nvoid dispatchJobs(\n    FiberManager& executor,\n    std::vector<Job>& jobs,\n    std::vector<folly::Optional<folly::Future<ResultT>>>& results,\n    DispatchProblem dispatchProblem = DispatchProblem::None,\n    size_t problemIndex = size_t(-1)) {\n  EXPECT_TRUE(\n      dispatchProblem == DispatchProblem::None || problemIndex < jobs.size());\n  results.clear();\n  results.resize(jobs.size());\n  for (size_t i = 0; i < jobs.size(); ++i) {\n    executor.add(\n        [i, &executor, &jobs, &results, dispatchProblem, problemIndex]() {\n          try {\n            Job job(std::move(jobs[i]));\n\n            if (dispatchProblem == DispatchProblem::PreprocessThrows) {\n              if (i == problemIndex) {\n                EXPECT_THROW(job.preprocess(executor, true), std::logic_error);\n                return;\n              }\n            }\n\n            job.preprocess(executor, false);\n            OUTPUT_TRACE << \"Dispatching job #\" << i << std::endl;\n            results[i] = job.token.dispatch(job.input);\n            OUTPUT_TRACE << \"Result future filled for job #\" << i << std::endl;\n\n            if (dispatchProblem == DispatchProblem::DuplicateDispatch) {\n              if (i == problemIndex) {\n                EXPECT_THROW(job.token.dispatch(job.input), ABDUsageException);\n              }\n            }\n          } catch (...) {\n            OUTPUT_TRACE << \"Preprocessing failed for job #\" << i << std::endl;\n          }\n        });\n  }\n}\n\nvoid validateResult(\n    std::vector<folly::Optional<folly::Future<ResultT>>>& results, size_t i) {\n  try {\n    OUTPUT_TRACE << \"results[\" << i << \"].value() : \" << results[i]->value()\n                 << std::endl;\n  } catch (std::exception& e) {\n    OUTPUT_TRACE << \"Exception : \" << e.what() << std::endl;\n    throw;\n  }\n}\n\ntemplate <typename TException>\nvoid validateResults(\n    std::vector<folly::Optional<folly::Future<ResultT>>>& results,\n    size_t expectedNumResults) {\n  size_t numResultsFilled = 0;\n  for (size_t i = 0; i < results.size(); ++i) {\n    if (!results[i]) {\n      continue;\n    }\n    ++numResultsFilled;\n    EXPECT_THROW(validateResult(results, i), TException);\n  }\n  EXPECT_EQ(numResultsFilled, expectedNumResults);\n}\n\nvoid validateResults(\n    std::vector<folly::Optional<folly::Future<ResultT>>>& results,\n    size_t expectedNumResults) {\n  size_t numResultsFilled = 0;\n  for (size_t i = 0; i < results.size(); ++i) {\n    if (!results[i]) {\n      continue;\n    }\n    ++numResultsFilled;\n    EXPECT_NO_THROW(validateResult(results, i));\n    ValueT expectedInput = i;\n    EXPECT_EQ(\n        results[i]->value(), processSingleInput(std::move(expectedInput)));\n  }\n  EXPECT_EQ(numResultsFilled, expectedNumResults);\n}\n\n} // namespace AtomicBatchDispatcherTesting\n\n#define SET_UP_TEST_FUNC                                        \\\n  using namespace AtomicBatchDispatcherTesting;                 \\\n  folly::EventBase evb;                                         \\\n  auto& executor = getFiberManager(evb);                        \\\n  const size_t COUNT = 11;                                      \\\n  std::vector<Job> jobs;                                        \\\n  jobs.reserve(COUNT);                                          \\\n  std::vector<folly::Optional<folly::Future<ResultT>>> results; \\\n  results.reserve(COUNT);                                       \\\n  DispatchFunctionT dispatchFunc\n\nTEST(FiberManager, ABDTest) {\n  SET_UP_TEST_FUNC;\n\n  //\n  // Testing AtomicBatchDispatcher with explicit call to commit()\n  //\n  dispatchFunc = userDispatchFunc;\n  auto atomicBatchDispatcher =\n      createAtomicBatchDispatcher(std::move(dispatchFunc));\n  createJobs(atomicBatchDispatcher, jobs, COUNT);\n  dispatchJobs(executor, jobs, results);\n  atomicBatchDispatcher.commit();\n  evb.loop();\n  validateResults(results, COUNT);\n}\n\nTEST(FiberManager, ABDDispatcherdestroyedbeforecallingcommit) {\n  SET_UP_TEST_FUNC;\n\n  //\n  // Testing AtomicBatchDispatcher destroyed before calling commit.\n  // Handles error cases for:\n  // - User might have forgotten to add the call to commit() in the code\n  // - An unexpected exception got thrown in user code before commit() is called\n  //\n  try {\n    dispatchFunc = userDispatchFunc;\n    auto atomicBatchDispatcher =\n        createAtomicBatchDispatcher(std::move(dispatchFunc));\n    createJobs(atomicBatchDispatcher, jobs, COUNT);\n    dispatchJobs(executor, jobs, results);\n    throw std::runtime_error(\n        \"Unexpected exception in user code before commit called\");\n    // atomicBatchDispatcher.commit();\n  } catch (...) {\n    /* User code handles the exception and does not exit process */\n  }\n  evb.loop();\n  validateResults<ABDCommitNotCalledException>(results, COUNT);\n}\n\nTEST(FiberManager, ABDPreprocessingfailuretest) {\n  SET_UP_TEST_FUNC;\n\n  //\n  // Testing preprocessing failure on a job throws\n  //\n  dispatchFunc = userDispatchFunc;\n  auto atomicBatchDispatcher =\n      createAtomicBatchDispatcher(std::move(dispatchFunc));\n  createJobs(atomicBatchDispatcher, jobs, COUNT);\n  dispatchJobs(executor, jobs, results, DispatchProblem::PreprocessThrows, 8);\n  atomicBatchDispatcher.commit();\n  evb.loop();\n  validateResults<ABDTokenNotDispatchedException>(results, COUNT - 1);\n}\n\nTEST(FiberManager, ABDMultipledispatchonsametokenerrortest) {\n  SET_UP_TEST_FUNC;\n\n  //\n  // Testing that calling dispatch more than once on the same token throws\n  //\n  dispatchFunc = userDispatchFunc;\n  auto atomicBatchDispatcher =\n      createAtomicBatchDispatcher(std::move(dispatchFunc));\n  createJobs(atomicBatchDispatcher, jobs, COUNT);\n  dispatchJobs(executor, jobs, results, DispatchProblem::DuplicateDispatch, 4);\n  atomicBatchDispatcher.commit();\n  evb.loop();\n}\n\nTEST(FiberManager, ABDGettokencalledaftercommittest) {\n  SET_UP_TEST_FUNC;\n\n  //\n  // Testing that exception set on attempt to call getToken after commit called\n  //\n  dispatchFunc = userDispatchFunc;\n  auto atomicBatchDispatcher =\n      createAtomicBatchDispatcher(std::move(dispatchFunc));\n  createJobs(atomicBatchDispatcher, jobs, COUNT);\n  atomicBatchDispatcher.commit();\n  EXPECT_THROW(atomicBatchDispatcher.getToken(), ABDUsageException);\n  dispatchJobs(executor, jobs, results);\n  EXPECT_THROW(atomicBatchDispatcher.getToken(), ABDUsageException);\n  evb.loop();\n  validateResults(results, COUNT);\n  EXPECT_THROW(atomicBatchDispatcher.getToken(), ABDUsageException);\n}\n\nTEST(FiberManager, ABDUserprovidedbatchdispatchthrowstest) {\n  SET_UP_TEST_FUNC;\n\n  //\n  // Testing that exception is set if user provided batch dispatch throws\n  //\n  dispatchFunc = [](std::vector<ValueT>&& inputs) -> std::vector<ResultT> {\n    (void)userDispatchFunc(std::move(inputs));\n    throw std::runtime_error(\"Unexpected exception in user dispatch function\");\n  };\n  auto atomicBatchDispatcher =\n      createAtomicBatchDispatcher(std::move(dispatchFunc));\n  createJobs(atomicBatchDispatcher, jobs, COUNT);\n  dispatchJobs(executor, jobs, results);\n  atomicBatchDispatcher.commit();\n  evb.loop();\n  validateResults<std::runtime_error>(results, COUNT);\n}\n\nTEST(FiberManager, VirtualEventBase) {\n  bool done1{false};\n  bool done2{false};\n  {\n    folly::ScopedEventBaseThread thread;\n\n    auto evb1 =\n        std::make_unique<folly::VirtualEventBase>(*thread.getEventBase());\n    auto& evb2 = thread.getEventBase()->getVirtualEventBase();\n\n    getFiberManager(*evb1).addTaskRemote([&] {\n      Baton baton;\n      baton.try_wait_for(std::chrono::milliseconds{100});\n\n      done1 = true;\n    });\n\n    getFiberManager(evb2).addTaskRemote([&] {\n      Baton baton;\n      baton.try_wait_for(std::chrono::milliseconds{200});\n\n      done2 = true;\n    });\n\n    EXPECT_FALSE(done1);\n    EXPECT_FALSE(done2);\n\n    evb1.reset();\n    EXPECT_TRUE(done1);\n    EXPECT_FALSE(done2);\n  }\n  EXPECT_TRUE(done2);\n}\n\nTEST(TimedMutex, ThreadsAndFibersDontDeadlock) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  TimedMutex mutex;\n  std::thread testThread([&] {\n    for (int i = 0; i < 100; i++) {\n      mutex.lock();\n      mutex.unlock();\n      {\n        Baton b;\n        b.try_wait_for(std::chrono::milliseconds(1));\n      }\n    }\n  });\n\n  for (int numFibers = 0; numFibers < 100; numFibers++) {\n    fm.addTask([&] {\n      for (int i = 0; i < 20; i++) {\n        mutex.lock();\n        {\n          Baton b;\n          b.try_wait_for(std::chrono::milliseconds(1));\n        }\n        mutex.unlock();\n        {\n          Baton b;\n          b.try_wait_for(std::chrono::milliseconds(1));\n        }\n      }\n    });\n  }\n\n  evb.loop();\n  EXPECT_EQ(0, fm.hasTasks());\n  testThread.join();\n}\n\nTEST(TimedMutex, ThreadFiberDeadlockOrder) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  TimedMutex mutex;\n\n  mutex.lock();\n  std::thread unlockThread([&] {\n    /* sleep override */ std::this_thread::sleep_for(\n        std::chrono::milliseconds{100});\n    mutex.unlock();\n  });\n\n  fm.addTask([&] { std::lock_guard lg(mutex); });\n  fm.addTask([&] {\n    runInMainContext([&] {\n      auto locked = mutex.try_lock_for(std::chrono::seconds{1});\n      EXPECT_TRUE(locked);\n      if (locked) {\n        mutex.unlock();\n      }\n    });\n  });\n\n  evb.loopOnce();\n  EXPECT_EQ(0, fm.hasTasks());\n\n  unlockThread.join();\n}\n\nTEST(TimedMutex, ThreadFiberDeadlockRace) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  TimedMutex mutex;\n\n  mutex.lock();\n\n  fm.addTask([&] {\n    auto locked = mutex.try_lock_for(std::chrono::seconds{1});\n    EXPECT_TRUE(locked);\n    if (locked) {\n      mutex.unlock();\n    }\n  });\n  fm.addTask([&] {\n    mutex.unlock();\n    runInMainContext([&] {\n      auto locked = mutex.try_lock_for(std::chrono::seconds{1});\n      EXPECT_TRUE(locked);\n      if (locked) {\n        mutex.unlock();\n      }\n    });\n  });\n\n  evb.loopOnce();\n  EXPECT_EQ(0, fm.hasTasks());\n}\n\nnamespace {\n\ntemplate <class Mutex>\nvoid testTimedRWMutex() {\n  constexpr size_t kNumReadTasks = 1000;\n  constexpr size_t kNumReadIters = 10;\n  constexpr size_t kNumWriteTasks = 100;\n  constexpr size_t kNumThreads = 4;\n\n  std::atomic<size_t> numReadSections = 0;\n  std::atomic<bool> writeLocked = false;\n  size_t numWriteSections = 0;\n\n  Mutex mutex;\n  std::vector<std::unique_ptr<folly::EventBase>> evbs{kNumThreads};\n  for (auto& evb : evbs) {\n    evb = std::make_unique<folly::EventBase>();\n    auto& fm = getFiberManager(*evb);\n\n    for (size_t i = 0; i < kNumReadTasks; ++i) {\n      fm.addTask([&] {\n        for (size_t j = 0; j < kNumReadIters; ++j) {\n          std::shared_lock lock(mutex);\n          ASSERT_FALSE(writeLocked.load());\n          ++numReadSections;\n          Baton b;\n          b.try_wait_for(std::chrono::milliseconds(1));\n        }\n      });\n    }\n\n    for (size_t i = 0; i < kNumWriteTasks; ++i) {\n      fm.addTask([&, i] {\n        std::unique_lock lock(mutex);\n        ASSERT_FALSE(writeLocked.exchange(true));\n        ++numWriteSections;\n        ASSERT_TRUE(writeLocked.exchange(false));\n        if (i % 10 == 0) {\n          auto rlock = folly::transition_lock<std::shared_lock>(lock);\n        }\n      });\n    }\n  }\n\n  std::vector<std::thread> threads;\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    threads.emplace_back([&evbs, i] { evbs[i]->loop(); });\n  }\n\n  for (auto& t : threads) {\n    t.join();\n  }\n\n  EXPECT_EQ(\n      numReadSections.load(), kNumThreads * kNumReadTasks * kNumReadIters);\n  EXPECT_EQ(numWriteSections, kNumThreads * kNumWriteTasks);\n}\n\n} // namespace\n\nTEST(TimedRWMutex, MultipleThreadsReadPriority) {\n  testTimedRWMutex<TimedRWMutexReadPriority<Baton>>();\n}\n\nTEST(TimedRWMutex, MultipleThreadsWritePriority) {\n  testTimedRWMutex<TimedRWMutexWritePriority<Baton>>();\n}\n\nnamespace {\n// Checks whether stackHighWatermark is set for non-ASAN builds,\n// and not set for ASAN builds.\n#ifndef FOLLY_SANITIZE_ADDRESS\nvoid expectStackHighWatermark(size_t minStackSize, size_t stackHighWatermark) {\n  // Check that we properly accounted fiber stack usage\n  EXPECT_NE(0, stackHighWatermark);\n  EXPECT_LT(minStackSize, stackHighWatermark);\n}\n#else\nvoid expectStackHighWatermark(size_t, size_t stackHighWatermark) {\n  // For ASAN, stackHighWatermark is not tracked.\n  EXPECT_EQ(0, stackHighWatermark);\n}\n#endif\n} // namespace\n\n/**\n * Test that we can properly track fiber stack usage, via recordStackEvery\n * For ASAN builds, it is not recorded.\n */\n\nTEST(FiberManager, highWaterMarkViaRecordStackEvery) {\n  auto f = [] {\n    folly::fibers::FiberManager::Options opts;\n    opts.recordStackEvery = 1;\n\n    FiberManager fm(std::make_unique<SimpleLoopController>(), opts);\n    auto& loopController =\n        dynamic_cast<SimpleLoopController&>(fm.loopController());\n\n    static constexpr size_t n = 1000;\n    int s = 0;\n    fm.addTask([&]() {\n      int b[n] = {0};\n      for (size_t i = 0; i < n; ++i) {\n        b[i] = i;\n      }\n      for (size_t i = 0; i + 1 < n; ++i) {\n        s += b[i] * b[i + 1];\n      }\n    });\n\n    (void)s;\n\n    loopController.loop([&]() { loopController.stop(); });\n    expectStackHighWatermark(n * sizeof(int), fm.stackHighWatermark());\n  };\n  std::thread(f).join();\n}\n\n/**\n * Test that we can properly track fiber stack usage,\n * via current position estimate. For ASAN builds, it is not recorded.\n */\nTEST(FiberManager, highWaterMarkViaRecordCurrentPosition) {\n  auto f = [] {\n    FiberManager fm(std::make_unique<SimpleLoopController>());\n    auto& loopController =\n        dynamic_cast<SimpleLoopController&>(fm.loopController());\n\n    static constexpr size_t n = 1000;\n    int s = 0;\n    fm.addTask([&]() {\n      int b[n] = {0};\n      for (size_t i = 0; i < n; ++i) {\n        b[i] = i;\n      }\n      for (size_t i = 0; i + 1 < n; ++i) {\n        s += b[i] * b[i + 1];\n      }\n      // Calls preempt, which calls recordStackPosition.\n      fm.runInMainContext([]() {});\n    });\n\n    (void)s;\n\n    loopController.loop([&]() { loopController.stop(); });\n    expectStackHighWatermark(n * sizeof(int), fm.stackHighWatermark());\n  };\n  std::thread(f).join();\n}\n\nTEST(FiberManager, addTaskEager) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  bool eagerTaskStarted{false};\n  bool eagerTaskDone{false};\n  bool firstTaskDone{false};\n\n  fm.addTask([&] { firstTaskDone = true; });\n\n  fm.addTaskEager([&] {\n    EXPECT_FALSE(firstTaskDone);\n    eagerTaskStarted = true;\n    fm.yield();\n    EXPECT_TRUE(firstTaskDone);\n    eagerTaskDone = true;\n  });\n\n  EXPECT_TRUE(eagerTaskStarted);\n\n  evb.loop();\n\n  EXPECT_TRUE(eagerTaskDone);\n  EXPECT_TRUE(firstTaskDone);\n}\n\nTEST(FiberManager, addTaskEagerFuture) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  bool eagerTaskStarted{false};\n  bool eagerTaskDone{false};\n\n  EXPECT_TRUE(fm.addTaskEagerFuture([&] {}).isReady());\n\n  auto f = fm.addTaskEagerFuture([&] {\n    eagerTaskStarted = true;\n    fm.yield();\n    eagerTaskDone = true;\n  });\n\n  EXPECT_TRUE(eagerTaskStarted);\n\n  evb.loop();\n\n  EXPECT_TRUE(f.isReady());\n\n  EXPECT_TRUE(eagerTaskDone);\n}\n\nTEST(FiberManager, addTaskEagerNested) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  bool eagerTaskStarted{false};\n  bool eagerTaskDone{false};\n  bool firstTaskDone{false};\n  bool secondTaskDone{false};\n\n  fm.addTask([&] {\n    fm.addTaskEager([&] {\n      EXPECT_FALSE(firstTaskDone);\n      EXPECT_FALSE(secondTaskDone);\n      fm.runInMainContext([&] { eagerTaskStarted = true; });\n      fm.yield();\n      EXPECT_TRUE(firstTaskDone);\n      EXPECT_TRUE(secondTaskDone);\n      eagerTaskDone = true;\n    });\n    EXPECT_TRUE(eagerTaskStarted);\n    firstTaskDone = true;\n  });\n\n  fm.addTask([&] {\n    EXPECT_TRUE(firstTaskDone);\n    secondTaskDone = true;\n  });\n\n  evb.loop();\n\n  EXPECT_TRUE(eagerTaskDone);\n  EXPECT_TRUE(secondTaskDone);\n}\n\nTEST(FiberManager, addTaskEagerNestedFiberManager) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  FiberManager::Options opts;\n  opts.stackSize *= 2;\n  auto& fm2 = getFiberManager(evb, FiberManager::FrozenOptions(opts));\n\n  bool eagerTaskDone{false};\n\n  fm.addTask([&] {\n    fm2.addTaskEager([&] {\n      EXPECT_FALSE(fm.hasActiveFiber());\n      eagerTaskDone = true;\n    });\n  });\n\n  evb.loop();\n\n  EXPECT_TRUE(eagerTaskDone);\n}\n\nTEST(FiberManager, swapWithException) {\n  folly::EventBase evb;\n  FiberManager::Options opts;\n  // ASSERT_DEATH takes a lot of stack space\n  opts.stackSize = 65536;\n  auto& fm = getFiberManager(evb, FiberManager::FrozenOptions{opts});\n  bool done = false;\n\n  fm.addTask([&] {\n    try {\n      throw std::logic_error(\"test\");\n    } catch (const std::exception&) {\n      // Ok to call runInMainContext in exception unwinding\n      runInMainContext([&] { done = true; });\n    }\n  });\n\n  evb.loop();\n  EXPECT_TRUE(done);\n\n  fm.addTask([&] {\n    try {\n      throw std::logic_error(\"test\");\n    } catch (const std::exception&) {\n      Baton b;\n      // Can't block during exception unwinding\n      ASSERT_DEATH(b.try_wait_for(std::chrono::milliseconds(1)), \"\");\n    }\n  });\n  evb.loop();\n}\n\nTEST(FiberManager, loopInCatch) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  bool started = false;\n  folly::fibers::Baton baton;\n  bool done = false;\n\n  fm.addTask([&] {\n    started = true;\n    baton.wait();\n    done = true;\n  });\n\n  try {\n    throw std::logic_error(\"expected\");\n  } catch (...) {\n    EXPECT_FALSE(started);\n    evb.drive();\n    EXPECT_TRUE(started);\n    EXPECT_FALSE(done);\n    baton.post();\n    evb.drive();\n    EXPECT_TRUE(done);\n  }\n}\n\nTEST(FiberManager, loopInUnwind) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  bool started = false;\n  folly::fibers::Baton baton;\n  bool done = false;\n\n  fm.addTask([&] {\n    started = true;\n    baton.wait();\n    done = true;\n  });\n\n  try {\n    SCOPE_EXIT {\n      EXPECT_FALSE(started);\n      evb.drive();\n      EXPECT_TRUE(started);\n      EXPECT_FALSE(done);\n      baton.post();\n      evb.drive();\n      EXPECT_TRUE(done);\n    };\n    throw std::logic_error(\"expected\");\n  } catch (...) {\n  }\n}\n\nTEST(FiberManager, addTaskRemoteFutureTry) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  EXPECT_EQ(\n      42,\n      fm.addTaskRemoteFuture([&]() -> folly::Try<int> {\n          return folly::Try<int>(42);\n        })\n          .getVia(&evb)\n          .value());\n}\n\nTEST(FiberManager, addTaskEagerKeepAlive) {\n  auto f = [&] {\n    folly::EventBase evb;\n    return getFiberManager(evb).addTaskEagerFuture([&] {\n      folly::futures::sleep(std::chrono::milliseconds{100}).get();\n      return 42;\n    });\n  }();\n\n  EXPECT_TRUE(f.isReady());\n  EXPECT_EQ(42, std::move(f).get());\n}\n\nTEST(FiberManager, fibersPreserveAsyncStackRoots) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n\n  {\n    folly::detail::ScopedAsyncStackRoot root;\n\n    auto f = [&] {\n      // Should be launched with a no active AsyncStackRoot\n      EXPECT_TRUE(folly::tryGetCurrentAsyncStackRoot() == nullptr);\n\n      folly::detail::ScopedAsyncStackRoot scopedRoot1;\n\n      auto* root1 = folly::tryGetCurrentAsyncStackRoot();\n      EXPECT_TRUE(root1 != nullptr);\n\n      fm.yield();\n\n      EXPECT_EQ(root1, folly::tryGetCurrentAsyncStackRoot());\n\n      {\n        folly::detail::ScopedAsyncStackRoot scopedRoot2;\n\n        auto* root2 = folly::tryGetCurrentAsyncStackRoot();\n\n        folly::AsyncStackFrame frame1;\n\n        folly::AsyncStackFrame frame2;\n        frame2.setParentFrame(frame1);\n\n        scopedRoot2.activateFrame(frame2);\n\n        fm.yield();\n\n        EXPECT_EQ(root2, folly::tryGetCurrentAsyncStackRoot());\n\n        folly::deactivateAsyncStackFrame(frame2);\n      }\n    };\n\n    auto* originalRoot = folly::tryGetCurrentAsyncStackRoot();\n\n    auto task1 = fm.addTaskFuture(f);\n    auto task2 = fm.addTaskFuture(f);\n\n    EXPECT_EQ(originalRoot, folly::tryGetCurrentAsyncStackRoot());\n\n    std::move(task1).getVia(&evb);\n\n    EXPECT_EQ(originalRoot, folly::tryGetCurrentAsyncStackRoot());\n\n    std::move(task2).getVia(&evb);\n\n    EXPECT_EQ(originalRoot, folly::tryGetCurrentAsyncStackRoot());\n  }\n}\n\nTEST(FiberManager, EventBaseMigratingThreads) {\n  folly::EventBase evb;\n  auto& fm = getFiberManager(evb);\n  folly::fibers::Baton baton;\n  auto f = fm.addTaskFuture([&baton] { baton.wait(); });\n  evb.drive();\n\n  std::thread anotherThread([&] { evb.drive(); });\n\n  std::this_thread::sleep_for(std::chrono::milliseconds{100});\n  baton.post();\n  anotherThread.join();\n\n  EXPECT_TRUE(f.isReady());\n}\n"
  },
  {
    "path": "folly/fibers/test/FibersTestApp.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <queue>\n\n#include <folly/Memory.h>\n#include <folly/fibers/FiberManager.h>\n#include <folly/fibers/SimpleLoopController.h>\n\nusing namespace folly::fibers;\n\nstruct Application {\n public:\n  Application()\n      : fiberManager(std::make_unique<SimpleLoopController>()),\n        toSend(20),\n        maxOutstanding(5) {}\n\n  void loop() {\n    if (pendingRequests.size() == maxOutstanding || toSend == 0) {\n      if (pendingRequests.empty()) {\n        return;\n      }\n      intptr_t value = rand() % 1000;\n      std::cout << \"Completing request with data = \" << value << std::endl;\n\n      pendingRequests.front().setValue(value);\n      pendingRequests.pop();\n    } else {\n      static size_t id_counter = 1;\n      size_t id = id_counter++;\n      std::cout << \"Adding new request with id = \" << id << std::endl;\n\n      fiberManager.addTask([this, id]() {\n        std::cout << \"Executing fiber with id = \" << id << std::endl;\n\n        auto result1 = await([this](Promise<int> fiber) {\n          pendingRequests.push(std::move(fiber));\n        });\n\n        std::cout << \"Fiber id = \" << id << \" got result1 = \" << result1\n                  << std::endl;\n\n        auto result2 = await([this](Promise<int> fiber) {\n          pendingRequests.push(std::move(fiber));\n        });\n        std::cout << \"Fiber id = \" << id << \" got result2 = \" << result2\n                  << std::endl;\n      });\n\n      if (--toSend == 0) {\n        auto& loopController =\n            dynamic_cast<SimpleLoopController&>(fiberManager.loopController());\n        loopController.stop();\n      }\n    }\n  }\n\n  FiberManager fiberManager;\n\n  std::queue<Promise<int>> pendingRequests;\n  size_t toSend;\n  size_t maxOutstanding;\n};\n\nint main() {\n  Application app;\n\n  auto loop = [&app]() { app.loop(); };\n\n  auto& loopController =\n      dynamic_cast<SimpleLoopController&>(app.fiberManager.loopController());\n\n  loopController.loop(std::move(loop));\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/fibers/test/SemaphoreTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/Semaphore.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n#include <folly/synchronization/detail/Sleeper.h>\n\nusing namespace folly::fibers;\n\nTEST(SemaphoreTest, MessagePassing) {\n  int data = 0;\n  Semaphore sem{0};\n\n  // Provides no memory ordering: just used to reproduce conditions for a\n  // bug caught by TSAN in an earlier version of the implementation.\n  folly::relaxed_atomic<bool> signalled{false};\n\n  std::thread t{[&]() {\n    folly::detail::Sleeper sleeper;\n    while (!signalled) {\n      sleeper.wait();\n    }\n    sem.wait();\n    EXPECT_NE(0, data);\n  }};\n\n  data = 1;\n  sem.signal();\n  signalled = true;\n\n  t.join();\n}\n"
  },
  {
    "path": "folly/fibers/test/StackOverflow.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/fibers/FiberManagerMap.h>\n#include <folly/init/Init.h>\n\nFOLLY_PUSH_WARNING\nFOLLY_CLANG_DISABLE_WARNING(\"-Winfinite-recursion\")\n\nvoid f(int* p) {\n  LOG(INFO) << \"f()\";\n  // Make sure recursion is not optimized out\n  int a[100];\n  for (size_t i = 0; i < 100; ++i) {\n    a[i] = i;\n    ++(a[i]);\n    if (p) {\n      a[i] += p[i];\n    }\n  }\n  f(a);\n}\n\nFOLLY_POP_WARNING\n\nint main(int argc, char* argv[]) {\n  folly::Init init(&argc, &argv);\n\n  folly::EventBase evb;\n  folly::fibers::getFiberManager(evb).addTask([&]() { f(nullptr); });\n  evb.loop();\n}\n"
  },
  {
    "path": "folly/fibers/test/TimedMutexBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n#include <vector>\n\n#include <folly/Benchmark.h>\n#include <folly/fibers/TimedMutex.h>\n#include <folly/init/Init.h>\n\nusing namespace folly::fibers;\n\nnamespace {\n\ntemplate <class Mutex>\nvoid concurrentReadersBenchmark(int iters, size_t numThreads) {\n  Mutex mutex;\n\n  std::vector<std::thread> threads{numThreads};\n  for (auto& t : threads) {\n    t = std::thread([&] {\n      for (int i = 0; i < iters; ++i) {\n        std::shared_lock lock(mutex);\n        folly::doNotOptimizeAway(lock.owns_lock());\n      }\n    });\n  }\n\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\n} // namespace\n\nBENCHMARK(TimedRWMutexWritePriority_readers_1, iters) {\n  concurrentReadersBenchmark<TimedRWMutexWritePriority<Baton>>(iters, 1);\n}\n\nBENCHMARK(TimedRWMutexWritePriority_readers_2, iters) {\n  concurrentReadersBenchmark<TimedRWMutexWritePriority<Baton>>(iters, 2);\n}\n\nBENCHMARK(TimedRWMutexWritePriority_readers_4, iters) {\n  concurrentReadersBenchmark<TimedRWMutexWritePriority<Baton>>(iters, 4);\n}\n\nBENCHMARK(TimedRWMutexWritePriority_readers_8, iters) {\n  concurrentReadersBenchmark<TimedRWMutexWritePriority<Baton>>(iters, 8);\n}\n\nBENCHMARK(TimedRWMutexWritePriority_readers_16, iters) {\n  concurrentReadersBenchmark<TimedRWMutexWritePriority<Baton>>(iters, 16);\n}\n\nBENCHMARK(TimedRWMutexWritePriority_readers_32, iters) {\n  concurrentReadersBenchmark<TimedRWMutexWritePriority<Baton>>(iters, 32);\n}\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv, true);\n\n  folly::runBenchmarks();\n  return 0;\n}\n\n#if 0\n// On Intel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz\n$ buck2 run @mode/opt folly/fibers/test:timed_mutex_benchmark  -- --bm_slice_usec 500000\n============================================================================\n[...]y/fibers/test/TimedMutexBenchmark.cpp     relative  time/iter   iters/s\n============================================================================\nTimedRWMutexWritePriority_readers_1                        21.95ns    45.55M\nTimedRWMutexWritePriority_readers_2                       132.08ns     7.57M\nTimedRWMutexWritePriority_readers_4                       306.85ns     3.26M\nTimedRWMutexWritePriority_readers_8                       954.79ns     1.05M\nTimedRWMutexWritePriority_readers_16                        2.34us   427.52K\nTimedRWMutexWritePriority_readers_32                        5.38us   185.74K\n#endif\n"
  },
  {
    "path": "folly/fibers/traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\nnamespace folly {\nnamespace fibers {\n\n/**\n * For any functor F taking >= 1 argument,\n * FirstArgOf<F>::type is the type of F's first parameter.\n *\n * Rationale: we want to declare a function func(F), where F has the\n * signature `void(X)` and func should return T<X> (T and X are some types).\n * Solution:\n *\n * template <typename F>\n * T<typename FirstArgOf<F>::type>\n * func(F&& f);\n */\n\nnamespace detail {\n\ntemplate <typename F>\nstruct ExtractFirstArg;\n\ntemplate <typename Ret, typename T, typename First, typename... Args>\nstruct ExtractFirstArg<Ret (T::*)(First, Args...)> {\n  using type = First;\n};\n\ntemplate <typename Ret, typename T, typename First, typename... Args>\nstruct ExtractFirstArg<Ret (T::*)(First, Args...) const> {\n  using type = First;\n};\n\ntemplate <typename Ret, typename First, typename... Args>\nstruct ExtractFirstArg<Ret(First, Args...)> {\n  using type = First;\n};\n\n} // namespace detail\n\ntemplate <typename F, typename Enable = void>\nstruct FirstArgOf;\n\n/** Specialization for non-function-object callables */\ntemplate <typename F>\nstruct FirstArgOf<F, typename std::enable_if<!std::is_class<F>::value>::type> {\n  using type = typename detail::ExtractFirstArg<\n      typename std::remove_pointer<F>::type>::type;\n};\n\n/** Specialization for function objects */\ntemplate <typename F>\nstruct FirstArgOf<F, typename std::enable_if<std::is_class<F>::value>::type> {\n  using type = typename FirstArgOf<decltype(&F::operator())>::type;\n};\n\n} // namespace fibers\n} // namespace folly\n"
  },
  {
    "path": "folly/folly_extended_library.bzl",
    "content": "load(\"@fbsource//tools/build_defs:default_platform_defs.bzl\", \"ANDROID\", \"APPLE\", \"CXX\", \"FBCODE\", \"WINDOWS\")\nload(\":defs.bzl\", \"folly_xplat_library\")\n\ndef folly_extended_xplat_library(name, **kwargs):\n    folly_xplat_library(\n        name,\n        force_static = False,\n        compiler_flags = [\n            \"-Wno-shadow\",\n        ],\n        fbandroid_deps = [\n            \"fbsource//xplat/third-party/linker_lib:atomic\",\n            \"fbsource//third-party/toolchains:log\",\n        ],\n        linker_flags = select({\n            \"DEFAULT\": [],\n            \"ovr_config//os:android\": [\n                \"-Wl,--no-undefined\",\n            ],\n        }),\n        platforms = (CXX, ANDROID, APPLE, FBCODE, WINDOWS),\n        deps = [\n            \"fbsource//xplat/folly:memory\",\n            \"fbsource//third-party/glog:glog\",\n        ],\n        **kwargs\n    )\n"
  },
  {
    "path": "folly/functional/ApplyTuple.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <tuple>\n#include <utility>\n\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\n\n//////////////////////////////////////////////////////////////////////\n\n/**\n * Helper to generate an index sequence from a tuple like type\n */\ntemplate <typename Tuple>\nusing index_sequence_for_tuple =\n    std::make_index_sequence<std::tuple_size<Tuple>::value>;\n\nnamespace detail {\nnamespace apply_tuple {\n\nnamespace adl {\n\nusing std::get;\n\ntemplate <std::size_t I>\nstruct invoke_get_fn {\n  template <typename T>\n  constexpr auto operator()(T&& t) const\n      noexcept(noexcept(get<I>(static_cast<T&&>(t))))\n          -> decltype(get<I>(static_cast<T&&>(t))) {\n    return get<I>(static_cast<T&&>(t));\n  }\n};\n\n} // namespace adl\n\ntemplate <\n    typename Tuple,\n    std::size_t... Indices,\n    typename ReturnTuple = std::tuple<\n        decltype(adl::invoke_get_fn<Indices>{}(std::declval<Tuple>()))...>>\nauto forward_tuple(Tuple&& tuple, std::index_sequence<Indices...>)\n    -> ReturnTuple {\n  return ReturnTuple{\n      adl::invoke_get_fn<Indices>{}(static_cast<Tuple&&>(tuple))...};\n}\n\n} // namespace apply_tuple\n} // namespace detail\n\nstruct ApplyInvoke {\n private:\n  template <typename T>\n  using seq = index_sequence_for_tuple<std::remove_reference_t<T>>;\n\n  template <std::size_t I>\n  using get = detail::apply_tuple::adl::invoke_get_fn<I>;\n\n  template <typename F, typename T, std::size_t... I>\n  static constexpr auto\n  invoke_(F&& f, T&& t, std::index_sequence<I...>) noexcept(\n      noexcept(invoke(static_cast<F&&>(f), get<I>{}(static_cast<T&&>(t))...)))\n      -> decltype(invoke(\n          static_cast<F&&>(f), get<I>{}(static_cast<T&&>(t))...)) {\n    return invoke(static_cast<F&&>(f), get<I>{}(static_cast<T&&>(t))...);\n  }\n\n public:\n  template <typename F, typename T>\n  constexpr auto operator()(F&& f, T&& t) const noexcept(\n      noexcept(invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{})))\n      -> decltype(invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{})) {\n    return invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{});\n  }\n};\n\n//////////////////////////////////////////////////////////////////////\n\n/* using override */ using std::apply;\n\n/**\n * Get a tuple of references from the passed tuple, forwarding will be applied\n * on the individual types of the tuple based on the value category of the\n * passed tuple\n *\n * For example\n *\n *    forward_tuple(std::make_tuple(1, 2))\n *\n * Returns a std::tuple<int&&, int&&>,\n *\n *    auto tuple = std::make_tuple(1, 2);\n *    forward_tuple(tuple)\n *\n * Returns a std::tuple<int&, int&>\n */\ntemplate <typename Tuple>\nauto forward_tuple(Tuple&& tuple) noexcept\n    -> decltype(detail::apply_tuple::forward_tuple(\n        std::declval<Tuple>(),\n        std::declval<\n            index_sequence_for_tuple<std::remove_reference_t<Tuple>>>())) {\n  return detail::apply_tuple::forward_tuple(\n      static_cast<Tuple&&>(tuple),\n      index_sequence_for_tuple<std::remove_reference_t<Tuple>>{});\n}\n\n/**\n * Mimic the invoke suite of traits for tuple based apply invocation\n */\ntemplate <typename F, typename Tuple>\nusing apply_result = invoke_result<ApplyInvoke, F, Tuple>;\ntemplate <typename F, typename Tuple>\nusing apply_result_t = invoke_result_t<ApplyInvoke, F, Tuple>;\ntemplate <typename F, typename Tuple>\ninline constexpr bool is_applicable_v = is_invocable_v<ApplyInvoke, F, Tuple>;\ntemplate <typename F, typename Tuple>\nusing is_applicable = is_invocable<ApplyInvoke, F, Tuple>;\ntemplate <typename R, typename F, typename Tuple>\ninline constexpr bool is_applicable_r_v =\n    is_invocable_r_v<R, ApplyInvoke, F, Tuple>;\ntemplate <typename R, typename F, typename Tuple>\nusing is_applicable_r = is_invocable_r<R, ApplyInvoke, F, Tuple>;\ntemplate <typename F, typename Tuple>\ninline constexpr bool is_nothrow_applicable_v =\n    is_nothrow_invocable_v<ApplyInvoke, F, Tuple>;\ntemplate <typename F, typename Tuple>\nusing is_nothrow_applicable = is_nothrow_invocable<ApplyInvoke, F, Tuple>;\ntemplate <typename R, typename F, typename Tuple>\ninline constexpr bool is_nothrow_applicable_r_v =\n    is_nothrow_invocable_r_v<R, ApplyInvoke, F, Tuple>;\ntemplate <typename R, typename F, typename Tuple>\nusing is_nothrow_applicable_r =\n    is_nothrow_invocable_r<R, ApplyInvoke, F, Tuple>;\n\nnamespace detail {\nnamespace apply_tuple {\n\ntemplate <class F>\nclass Uncurry {\n public:\n  explicit Uncurry(F&& func) : func_(std::move(func)) {}\n  explicit Uncurry(const F& func) : func_(func) {}\n\n  template <class Tuple>\n  auto operator()(Tuple&& tuple) const\n      -> decltype(apply(std::declval<F>(), std::forward<Tuple>(tuple))) {\n    return apply(func_, std::forward<Tuple>(tuple));\n  }\n\n private:\n  F func_;\n};\n} // namespace apply_tuple\n} // namespace detail\n\n/**\n * Wraps a function taking N arguments into a function which accepts a tuple of\n * N arguments. Note: This function will also accept an std::pair if N == 2.\n *\n * For example, given the below code:\n *\n *    std::vector<std::tuple<int, int, int>> rows = ...;\n *    auto test = [](std::tuple<int, int, int>& row) {\n *      return std::get<0>(row) * std::get<1>(row) * std::get<2>(row) == 24;\n *    };\n *    auto found = std::find_if(rows.begin(), rows.end(), test);\n *\n *\n * 'test' could be rewritten as:\n *\n *    auto test =\n *        folly::uncurry([](int a, int b, int c) { return a * b * c == 24; });\n *\n */\ntemplate <class F>\nauto uncurry(F&& f)\n    -> detail::apply_tuple::Uncurry<typename std::decay<F>::type> {\n  return detail::apply_tuple::Uncurry<typename std::decay<F>::type>(\n      std::forward<F>(f));\n}\n\n//////////////////////////////////////////////////////////////////////\n} // namespace folly\n"
  },
  {
    "path": "folly/functional/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"apply_tuple\",\n    headers = [\"ApplyTuple.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":invoke\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"invoke\",\n    headers = [\"Invoke.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:cpp_attributes\",\n        \"//folly:portability\",\n        \"//folly:preprocessor\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/lang:customization_point\",\n    ],\n    exported_external_deps = [\n        (\"boost\", None, \"boost_preprocessor\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"partial\",\n    headers = [\"Partial.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":invoke\",\n        \"//folly:utility\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"protocol\",\n    headers = [\"protocol.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":invoke\",\n        \":traits\",\n        \"//folly:portability\",\n        \"//folly:traits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"traits\",\n    headers = [\"traits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:traits\",\n    ],\n)\n"
  },
  {
    "path": "folly/functional/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME apply_tuple\n  HEADERS\n    ApplyTuple.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME invoke\n  HEADERS\n    Invoke.h\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_lang_customization_point\n    folly_portability\n    folly_preprocessor\n    folly_traits\n    folly_utility\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME partial\n  HEADERS\n    Partial.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_utility\n)\n\nfolly_add_library(\n  NAME protocol\n  HEADERS\n    protocol.h\n  EXPORTED_DEPS\n    folly_functional_invoke\n    folly_functional_traits\n    folly_portability\n    folly_traits\n)\n\nfolly_add_library(\n  NAME traits\n  HEADERS\n    traits.h\n  EXPORTED_DEPS\n    folly_traits\n)\n"
  },
  {
    "path": "folly/functional/Invoke.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <type_traits>\n\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/facilities/is_empty_variadic.hpp>\n#include <boost/preprocessor/list/for_each.hpp>\n#include <boost/preprocessor/logical/not.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n\n#include <folly/CppAttributes.h>\n#include <folly/Portability.h>\n#include <folly/Preprocessor.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/lang/CustomizationPoint.h>\n\n#define FOLLY_DETAIL_FORWARD_REF(a) static_cast<decltype(a)&&>(a)\n\n/**\n *  include or backport:\n *  * std::invoke\n *  * std::invoke_result\n *  * std::invoke_result_t\n *  * std::is_invocable\n *  * std::is_invocable_v\n *  * std::is_invocable_r\n *  * std::is_invocable_r_v\n *  * std::is_nothrow_invocable\n *  * std::is_nothrow_invocable_v\n *  * std::is_nothrow_invocable_r\n *  * std::is_nothrow_invocable_r_v\n */\n\nnamespace folly {\n\n//  invoke_fn\n//  invoke\n//\n//  mimic: std::invoke, C++17\nstruct invoke_fn {\n  template <typename F, typename... A>\n  FOLLY_ERASE constexpr auto operator()(F&& f, A&&... a) const\n      noexcept(noexcept(static_cast<F&&>(f)(static_cast<A&&>(a)...)))\n          -> decltype(static_cast<F&&>(f)(static_cast<A&&>(a)...)) {\n    return static_cast<F&&>(f)(static_cast<A&&>(a)...);\n  }\n  template <typename M, typename C, typename... A>\n  FOLLY_ERASE constexpr auto operator()(M C::* f, A&&... a) const\n      noexcept(noexcept(std::mem_fn(f)(static_cast<A&&>(a)...)))\n          -> decltype(std::mem_fn(f)(static_cast<A&&>(a)...)) {\n    return std::mem_fn(f)(static_cast<A&&>(a)...);\n  }\n};\n\ninline constexpr invoke_fn invoke;\n\n} // namespace folly\n\nnamespace folly {\n\nnamespace invoke_detail {\n\n//  ok_one_\n//\n//  A quoted-metafunction which, when applied to type T, enforces that T is a\n//  complete type, (possibly cv-qualified) void, or an array of unknown bound.\n//  Substitutes as void if that holds; otherwise fails a static-assert.\nstruct ok_one_ {\n  template <typename T>\n  static constexpr bool pass_v =\n      ( //\n          std::is_void_v<T> || //\n          std::is_reference_v<T> || //\n          std::is_function_v<T> || //\n          is_unbounded_array_v<T> || //\n          false);\n\n  // note: void return type with no function body to enforce that, in the\n  // typical case of complete non-function types, to minimize the quantity of\n  // evaluations and instantiations\n  template <typename T, std::size_t = sizeof(T)>\n  static void test(int);\n\n  // note: auto return type with no trailing return type to force the\n  // instantiation of the function and, therefore, to force the\n  // evaluation of she static-assert\n  template <typename T>\n  static auto test(...) {\n    static_assert(pass_v<T>, \"must be complete, cv-void, or unbounded-array\");\n    return;\n  }\n\n  template <typename T>\n  using apply = decltype(test<T>(0));\n};\n\n//  ok_\n//\n//  Enforce that each A... is a complete type, (possibly cv-qualified) void, or\n//  an array of unknown bound. Substitutes as T if that holds; otherwise fails a\n//  static-assert.\n//\n//  The reason to fail a static-assert and not, say, to fail to substitute is\n//  to force application to incomplete types to fail the compile rather than to\n//  allow the compile to succumb to ODR violation. The failing static-assert is\n//  a diagnosis of undefined behavior.\n//\n//  See:\n//    https://en.cppreference.com/w/cpp/types/is_invocable\n//    https://github.com/gcc-mirror/gcc/blob/releases/gcc-13.2.0/libstdc%2B%2B-v3/include/std/type_traits#L272-L287\ntemplate <typename T, typename... A>\nusing ok_ = type_t<T, ok_one_::apply<A>...>;\n\ntemplate <typename F>\nstruct traits {\n  template <typename... A>\n  using result = decltype( //\n      FOLLY_DECLVAL(F&&)(FOLLY_DECLVAL(A&&)...));\n#if defined(_MSC_VER) && defined(__NVCC__)\n  template <typename... A>\n  using nothrow_t = std::bool_constant<noexcept( //\n      FOLLY_DECLVAL(F&&)(FOLLY_DECLVAL(A&&)...))>;\n#else\n  template <typename... A>\n  static constexpr bool nothrow_v = noexcept( //\n      FOLLY_DECLVAL(F&&)(FOLLY_DECLVAL(A&&)...));\n#endif\n};\ntemplate <typename P>\nstruct traits_member_ptr {\n  template <typename... A>\n  using result = decltype( //\n      std::mem_fn(FOLLY_DECLVAL(P))(FOLLY_DECLVAL(A&&)...));\n#if defined(_MSC_VER) && defined(__NVCC__)\n  template <typename... A>\n  using nothrow_t = std::bool_constant<noexcept( //\n      std::mem_fn(FOLLY_DECLVAL(P))(FOLLY_DECLVAL(A&&)...))>;\n#else\n  template <typename... A>\n  static constexpr bool nothrow_v = noexcept( //\n      std::mem_fn(FOLLY_DECLVAL(P))(FOLLY_DECLVAL(A&&)...));\n#endif\n};\ntemplate <typename M, typename C>\nstruct traits<M C::*> : traits_member_ptr<M C::*> {};\ntemplate <typename M, typename C>\nstruct traits<M C::* const> : traits_member_ptr<M C::*> {};\ntemplate <typename M, typename C>\nstruct traits<M C::*&> : traits_member_ptr<M C::*> {};\ntemplate <typename M, typename C>\nstruct traits<M C::* const&> : traits_member_ptr<M C::*> {};\ntemplate <typename M, typename C>\nstruct traits<M C::*&&> : traits_member_ptr<M C::*> {};\ntemplate <typename M, typename C>\nstruct traits<M C::* const&&> : traits_member_ptr<M C::*> {};\n\n#if defined(_MSC_VER) && defined(__NVCC__)\ntemplate <typename P, typename... A>\nusing is_nothrow = traits<P>::template nothrow_t<A...>;\n#else\ntemplate <typename P, typename... A>\nusing is_nothrow = std::bool_constant<traits<P>::template nothrow_v<A...>>;\n#endif\n\ntemplate <bool IsVoid>\nstruct conv_r_;\ntemplate <>\nstruct conv_r_<true> {\n  template <bool NX, typename R, typename FR>\n  using apply = std::true_type;\n};\ntemplate <>\nstruct conv_r_<false> {\n  template <typename R>\n  static void conv(R, decay_t<R>* = nullptr) noexcept;\n  template <\n      bool NX,\n      typename R,\n      typename FR,\n      bool C = noexcept(conv<R>(FOLLY_DECLVAL(FR)))>\n  static std::bool_constant<!NX || C> test(int);\n  template <bool NX, typename R, typename FR>\n  static std::false_type test(...);\n  template <bool NX, typename R, typename FR>\n  using apply = decltype(test<NX, R, FR>(0));\n};\n\ntemplate <bool NX, typename R, typename FR>\nstatic inline constexpr bool conv_r_v_ =\n    conv_r_<std::is_void_v<R>>::template apply<NX, R, FR>::value;\n\n//  adapted from: http://en.cppreference.com/w/cpp/types/result_of, CC-BY-SA\n\ntemplate <typename F, typename... A>\nusing invoke_result_t = typename traits<F>::template result<A...>;\n\ntemplate <typename Void, typename F, typename... A>\nstruct invoke_result {};\n\ntemplate <typename F, typename... A>\nstruct invoke_result<void_t<invoke_result_t<F, A...>>, F, A...> {\n  using type = invoke_result_t<F, A...>;\n};\n\ntemplate <typename Void, typename F, typename... A>\ninline constexpr bool is_invocable_v = ok_<bool, F, A...>{false};\n\ntemplate <typename F, typename... A>\ninline constexpr bool\n    is_invocable_v<void_t<invoke_result_t<F, A...>>, F, A...> = true;\n\ntemplate <typename Void, typename R, typename F, typename... A>\ninline constexpr bool is_invocable_r_v = ok_<bool, R, F, A...>{false};\n\n// clang-format off\ntemplate <typename R, typename F, typename... A>\ninline constexpr bool\n    is_invocable_r_v<void_t<invoke_result_t<F, A...>>, R, F, A...> =\n        conv_r_v_<false, R, invoke_result_t<F, A...>>;\n// clang-format on\n\ntemplate <typename Void, typename F, typename... A>\ninline constexpr bool is_nothrow_invocable_v = ok_<bool, F, A...>{false};\n\ntemplate <typename F, typename... A>\ninline constexpr bool\n    is_nothrow_invocable_v<void_t<invoke_result_t<F, A...>>, F, A...> =\n        is_nothrow<F, A...>::value;\n\ntemplate <typename Void, typename R, typename F, typename... A>\ninline constexpr bool is_nothrow_invocable_r_v = ok_<bool, R, F, A...>{false};\n\n// clang-format off\ntemplate <typename R, typename F, typename... A>\ninline constexpr bool\n    is_nothrow_invocable_r_v<void_t<invoke_result_t<F, A...>>, R, F, A...> =\n        is_nothrow<F, A...>::value &&\n            conv_r_v_<true, R, invoke_result_t<F, A...>>;\n// clang-format on\n\n} // namespace invoke_detail\n\n//  mimic: std::invoke_result, C++17\ntemplate <typename F, typename... A>\nusing invoke_result = invoke_detail::invoke_result<void, F, A...>;\n\n//  mimic: std::invoke_result_t, C++17\nusing invoke_detail::invoke_result_t;\n\n//  mimic: std::is_invocable_v, C++17\ntemplate <typename F, typename... A>\ninline constexpr bool is_invocable_v =\n    invoke_detail::is_invocable_v<void, F, A...>;\n\n//  mimic: std::is_invocable, C++17\ntemplate <typename F, typename... A>\nstruct is_invocable : std::bool_constant<is_invocable_v<F, A...>> {};\n\n//  mimic: std::is_invocable_r_v, C++17\ntemplate <typename R, typename F, typename... A>\ninline constexpr bool is_invocable_r_v =\n    invoke_detail::is_invocable_r_v<void, R, F, A...>;\n\n//  mimic: std::is_invocable_r, C++17\ntemplate <typename R, typename F, typename... A>\nstruct is_invocable_r : std::bool_constant<is_invocable_r_v<R, F, A...>> {};\n\n//  mimic: std::is_nothrow_invocable_v, C++17\ntemplate <typename F, typename... A>\ninline constexpr bool is_nothrow_invocable_v =\n    invoke_detail::is_nothrow_invocable_v<void, F, A...>;\n\n//  mimic: std::is_nothrow_invocable, C++17\ntemplate <typename F, typename... A>\nstruct is_nothrow_invocable\n    : std::bool_constant<is_nothrow_invocable_v<F, A...>> {};\n\n//  mimic: std::is_nothrow_invocable_r_v, C++17\ntemplate <typename R, typename F, typename... Args>\ninline constexpr bool is_nothrow_invocable_r_v =\n    invoke_detail::is_nothrow_invocable_r_v<void, R, F, Args...>;\n\n//  mimic: std::is_nothrow_invocable_r, C++17\ntemplate <typename R, typename F, typename... A>\nstruct is_nothrow_invocable_r\n    : std::bool_constant<is_nothrow_invocable_r_v<R, F, A...>> {};\n\n} // namespace folly\n\nnamespace folly {\n\nnamespace detail {\n\nstruct invoke_private_overload;\n\ntemplate <bool, typename I>\nstruct invoke_traits_base_ {};\ntemplate <typename I>\nstruct invoke_traits_base_<false, I> {};\ntemplate <typename I>\nstruct invoke_traits_base_<true, I> {\n  inline static constexpr I invoke{};\n};\ntemplate <typename I>\nusing invoke_traits_base =\n    invoke_traits_base_<is_constexpr_default_constructible_v<I>, I>;\n\n} // namespace detail\n\n//  invoke_traits\n//\n//  A traits container struct with the following member types, type aliases, and\n//  variables:\n//\n//  * invoke_result\n//  * invoke_result_t\n//  * is_invocable\n//  * is_invocable_v\n//  * is_invocable_r\n//  * is_invocable_r_v\n//  * is_nothrow_invocable\n//  * is_nothrow_invocable_v\n//  * is_nothrow_invocable_r\n//  * is_nothrow_invocable_r_v\n//\n//  These members have behavior matching the behavior of C++17's corresponding\n//  invocation traits types, type aliases, and variables, but using invoke_type\n//  as the invocable argument passed to the usual nivocation traits.\n//\n//  The traits container struct also has a member type alias:\n//\n//  * invoke_type\n//\n//  And an invocable variable as a default-constructed instance of invoke_type,\n//  if the latter is constexpr default-constructible:\n//\n//  * invoke\ntemplate <typename I>\nstruct invoke_traits : detail::invoke_traits_base<I> {\n public:\n  using invoke_type = I;\n\n  //  If invoke_type is constexpr default-constructible:\n  //\n  //    inline static constexpr invoke_type invoke{};\n\n  template <typename... A>\n  using invoke_result = invoke_detail::invoke_result<void, I, A...>;\n  template <typename... A>\n  using invoke_result_t = invoke_detail::invoke_result_t<I, A...>;\n  template <typename... A>\n  inline static constexpr bool is_invocable_v =\n      invoke_detail::is_invocable_v<void, I, A...>;\n  template <typename... A>\n  struct is_invocable //\n      : std::bool_constant<invoke_detail::is_invocable_v<void, I, A...>> {};\n  template <typename R, typename... A>\n  inline static constexpr bool is_invocable_r_v =\n      invoke_detail::is_invocable_r_v<void, R, I, A...>;\n  template <typename R, typename... A>\n  struct is_invocable_r //\n      : std::bool_constant< //\n            invoke_detail::is_invocable_r_v<void, R, I, A...>> {};\n  template <typename... A>\n  inline static constexpr bool is_nothrow_invocable_v =\n      invoke_detail::is_nothrow_invocable_v<void, I, A...>;\n  template <typename... A>\n  struct is_nothrow_invocable //\n      : std::bool_constant<\n            invoke_detail::is_nothrow_invocable_v<void, I, A...>> {};\n  template <typename R, typename... A>\n  inline static constexpr bool is_nothrow_invocable_r_v =\n      invoke_detail::is_nothrow_invocable_r_v<void, R, I, A...>;\n  template <typename R, typename... A>\n  struct is_nothrow_invocable_r //\n      : std::bool_constant<\n            invoke_detail::is_nothrow_invocable_r_v<void, R, I, A...>> {};\n};\n\n//  invoke_first_match\n//\n//  A composite invoker which delegates to the first invoker parameter matching\n//  the call.\n//\n//  Example:\n//\n//      FOLLY_CREATE_QUAL_INVOKER(invoke_x_fn, x);\n//      FOLLY_CREATE_QUAL_INVOKER(invoke_y_fn, y);\n//\n//      using invoke_x_or_y_fn = invoke_first_match<invoke_x_fn, invoke_y_fn>;\n//      inline constexpr invoke_x_or_y_fn invoke_x_or_y;\n//\n//      void go(int a, int b) { invoke_x_or_y(a, b); }\n//\n//  In this example, go(...) will delegate to x(...) if it exists and is a match\n//  for the arguments, or otherwise will delegate to y(...).\ntemplate <typename... Invoker>\nstruct invoke_first_match : private Invoker... {\n private:\n  using iseq = std::index_sequence_for<Invoker...>;\n  template <size_t Idx>\n  using at = type_pack_element_t<Idx, Invoker...>;\n  template <size_t... Idx, typename... A>\n  static constexpr size_t first_(std::index_sequence<Idx...>, tag_t<A...>) {\n    constexpr bool r[] = {is_invocable_v<at<Idx> const&, A...>..., false};\n    for (size_t i = 0; i < sizeof...(Invoker); ++i) {\n      if (r[i]) {\n        return i;\n      }\n    }\n    return sizeof...(Invoker);\n  }\n  template <typename... A>\n  static constexpr size_t first = first_(iseq{}, tag<A...>);\n\n public:\n  template <typename... A, typename Inv = at<first<A...>>>\n  FOLLY_ERASE constexpr auto operator()(A&&... a) const\n      noexcept(is_nothrow_invocable_v<Inv const&, A...>)\n          -> invoke_result_t<Inv const&, A...> {\n    return static_cast<Inv const&>(*this)(static_cast<A&&>(a)...);\n  }\n};\n\n} // namespace folly\n\n#define FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING_1(_, funcname, ns) \\\n  using ns::funcname;\n\n#define FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING(_, funcname, ...) \\\n  BOOST_PP_EXPR_IIF(                                                   \\\n      BOOST_PP_NOT(BOOST_PP_IS_EMPTY(__VA_ARGS__)),                    \\\n      BOOST_PP_LIST_FOR_EACH(                                          \\\n          FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING_1,              \\\n          funcname,                                                    \\\n          BOOST_PP_TUPLE_TO_LIST((__VA_ARGS__))))\n\n/***\n *  FOLLY_CREATE_FREE_INVOKER\n *\n *  Used to create an invoker type bound to a specific free-invocable name.\n *\n *  Example:\n *\n *    FOLLY_CREATE_FREE_INVOKER(foo_invoker, foo);\n *\n *  The type `foo_invoker` is generated in the current namespace and may be used\n *  as follows:\n *\n *    namespace Deep {\n *    struct CanFoo {};\n *    int foo(CanFoo const&, Bar&) { return 1; }\n *    int foo(CanFoo&&, Car&&) noexcept { return 2; }\n *    }\n *\n *    using traits = folly::invoke_traits<foo_invoker>;\n *\n *    traits::invoke(Deep::CanFoo{}, Car{}) // 2\n *\n *    traits::invoke_result<Deep::CanFoo, Bar&> // has member\n *    traits::invoke_result_t<Deep::CanFoo, Bar&> // int\n *    traits::invoke_result<Deep::CanFoo, Bar&&> // empty\n *    traits::invoke_result_t<Deep::CanFoo, Bar&&> // error\n *\n *    traits::is_invocable_v<CanFoo, Bar&> // true\n *    traits::is_invocable_v<CanFoo, Bar&&> // false\n *\n *    traits::is_invocable_r_v<int, CanFoo, Bar&> // true\n *    traits::is_invocable_r_v<char*, CanFoo, Bar&> // false\n *\n *    traits::is_nothrow_invocable_v<CanFoo, Bar&> // false\n *    traits::is_nothrow_invocable_v<CanFoo, Car&&> // true\n *\n *    traits::is_nothrow_invocable_v<int, CanFoo, Bar&> // false\n *    traits::is_nothrow_invocable_v<char*, CanFoo, Bar&> // false\n *    traits::is_nothrow_invocable_v<int, CanFoo, Car&&> // true\n *    traits::is_nothrow_invocable_v<char*, CanFoo, Car&&> // false\n *\n *  When a name has one or more primary definition in a fixed set of namespaces\n *  and alternate definitions in the namespaces of its arguments, the primary\n *  definitions may automatically be found as follows:\n *\n *    FOLLY_CREATE_FREE_INVOKER(swap_invoker, swap, std);\n *\n *  In this case, `swap_invoke_traits::invoke(int&, int&)` will use the primary\n *  definition found in `namespace std` relative to the current namespace, which\n *  may be equivalent to `namespace ::std`. In contrast:\n *\n *    namespace Deep {\n *    struct HasData {};\n *    void swap(HasData&, HasData&) { throw 7; }\n *    }\n *\n *    using traits = invoke_traits<swap_invoker>;\n *\n *    HasData a, b;\n *    traits::invoke(a, b); // throw 7\n */\n#define FOLLY_CREATE_FREE_INVOKER(classname, funcname, ...)                    \\\n  namespace classname##__folly_detail_invoke_ns {                              \\\n    [[maybe_unused]] void funcname(::folly::detail::invoke_private_overload&); \\\n    FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING(_, funcname, __VA_ARGS__)     \\\n    struct __folly_detail_invoke_obj {                                         \\\n      template <typename... Args>                                              \\\n      [[maybe_unused]] FOLLY_ERASE_HACK_GCC constexpr auto operator()(         \\\n          Args&&... args) const                                                \\\n          noexcept(noexcept(funcname(static_cast<Args&&>(args)...)))           \\\n              -> decltype(funcname(static_cast<Args&&>(args)...)) {            \\\n        return funcname(static_cast<Args&&>(args)...);                         \\\n      }                                                                        \\\n    };                                                                         \\\n  }                                                                            \\\n  struct classname                                                             \\\n      : classname##__folly_detail_invoke_ns::__folly_detail_invoke_obj {}\n\n/***\n *  FOLLY_CREATE_FREE_INVOKER_SUITE\n *\n *  Used to create an invoker type and associated variable bound to a specific\n *  free-invocable name. The invoker variable is named like the free-invocable\n *  name and the invoker type is named with a suffix of _fn.\n *\n *  See FOLLY_CREATE_FREE_INVOKER.\n */\n#define FOLLY_CREATE_FREE_INVOKER_SUITE(funcname, ...)             \\\n  FOLLY_CREATE_FREE_INVOKER(funcname##_fn, funcname, __VA_ARGS__); \\\n  [[maybe_unused]] inline constexpr funcname##_fn funcname {}\n\n/***\n *  FOLLY_CREATE_QUAL_INVOKER\n *\n *  Used to create an invoker type bound to a specific free-invocable qualified\n *  name. It is permitted that the qualification be empty and that the name be\n *  unqualified in practice. This differs from FOLLY_CREATE_FREE_INVOKER in that\n *  it is required that the name be in scope and that it is not possible to\n *  provide a list of namespaces in which to look up the name..\n */\n#define FOLLY_CREATE_QUAL_INVOKER(classname, funcpath)                        \\\n  struct classname {                                                          \\\n    template <typename... A>                                                  \\\n    [[maybe_unused]] FOLLY_ERASE_HACK_GCC constexpr auto operator()(A&&... a) \\\n        const FOLLY_DETAIL_FORWARD_BODY(funcpath(static_cast<A&&>(a)...))     \\\n  }\n\n/***\n *  FOLLY_CREATE_QUAL_INVOKER_SUITE\n *\n *  Used to create an invoker type and associated variable bound to a specific\n *  free-invocable qualified name.\n *\n *  See FOLLY_CREATE_QUAL_INVOKER.\n */\n#define FOLLY_CREATE_QUAL_INVOKER_SUITE(name, funcpath) \\\n  FOLLY_CREATE_QUAL_INVOKER(name##_fn, funcpath);       \\\n  [[maybe_unused]] inline constexpr name##_fn name {}\n\n/***\n *  FOLLY_INVOKE_QUAL\n *\n *  An invoker expression resulting in an invocable which, when invoked, invokes\n *  the free-invocable qualified name with the given arguments.\n */\n#define FOLLY_INVOKE_QUAL(funcpath)                                  \\\n  [](auto&&... __folly_param_a) constexpr FOLLY_DETAIL_FORWARD_BODY( \\\n      funcpath(FOLLY_DETAIL_FORWARD_REF(__folly_param_a)...))\n\n/***\n *  FOLLY_CREATE_MEMBER_INVOKER\n *\n *  Used to create an invoker type bound to a specific member-invocable name.\n *\n *  Example:\n *\n *    FOLLY_CREATE_MEMBER_INVOKER(foo_invoker, foo);\n *\n *  The type `foo_invoker` is generated in the current namespace and may be used\n *  as follows:\n *\n *    struct CanFoo {\n *      int foo(Bar&) { return 1; }\n *      int foo(Car&&) noexcept { return 2; }\n *    };\n *\n *    using traits = folly::invoke_traits<foo_invoker>;\n *\n *    traits::invoke(CanFoo{}, Car{}) // 2\n *\n *    traits::invoke_result<CanFoo, Bar&> // has member\n *    traits::invoke_result_t<CanFoo, Bar&> // int\n *    traits::invoke_result<CanFoo, Bar&&> // empty\n *    traits::invoke_result_t<CanFoo, Bar&&> // error\n *\n *    traits::is_invocable_v<CanFoo, Bar&> // true\n *    traits::is_invocable_v<CanFoo, Bar&&> // false\n *\n *    traits::is_invocable_r_v<int, CanFoo, Bar&> // true\n *    traits::is_invocable_r_v<char*, CanFoo, Bar&> // false\n *\n *    traits::is_nothrow_invocable_v<CanFoo, Bar&> // false\n *    traits::is_nothrow_invocable_v<CanFoo, Car&&> // true\n *\n *    traits::is_nothrow_invocable_v<int, CanFoo, Bar&> // false\n *    traits::is_nothrow_invocable_v<char*, CanFoo, Bar&> // false\n *    traits::is_nothrow_invocable_v<int, CanFoo, Car&&> // true\n *    traits::is_nothrow_invocable_v<char*, CanFoo, Car&&> // false\n */\n#define FOLLY_CREATE_MEMBER_INVOKER(classname, membername)                 \\\n  struct classname {                                                       \\\n    template <typename O, typename... Args>                                \\\n    [[maybe_unused]] FOLLY_ERASE_HACK_GCC constexpr auto                   \\\n    operator()(O&& o, Args&&... args) const noexcept(noexcept(             \\\n        static_cast<O&&>(o).membername(static_cast<Args&&>(args)...)))     \\\n        -> decltype(static_cast<O&&>(o).membername(                        \\\n            static_cast<Args&&>(args)...)) {                               \\\n      return static_cast<O&&>(o).membername(static_cast<Args&&>(args)...); \\\n    }                                                                      \\\n  }\n\n/***\n *  FOLLY_CREATE_MEMBER_INVOKER_SUITE\n *\n *  Used to create an invoker type and associated variable bound to a specific\n *  member-invocable name. The invoker variable is named like the member-\n *  invocable  name and the invoker type is named with a suffix of _fn.\n *\n *  See FOLLY_CREATE_MEMBER_INVOKER.\n */\n#define FOLLY_CREATE_MEMBER_INVOKER_SUITE(membername)       \\\n  FOLLY_CREATE_MEMBER_INVOKER(membername##_fn, membername); \\\n  [[maybe_unused]] inline constexpr membername##_fn membername {}\n\nnamespace folly {\n// Power users can call `is_instantiation_of<invoke_member_wrapper_fn, T>`\n// to ascertain that a callable was made via `FOLLY_INVOKE_MEMBER`.  This\n// can be important e.g. when you want to be sure that the first argument\n// of the callable becomes the implicit object parameter of the class.\ntemplate <typename F>\nstruct invoke_member_wrapper_fn : private F {\n  template <typename G, typename = decltype(F(FOLLY_DECLVAL(G&&)))>\n  constexpr explicit invoke_member_wrapper_fn(G&& f) noexcept(\n      noexcept(F(static_cast<G&&>(f))))\n      : F(static_cast<G&&>(f)) {}\n  using F::operator();\n};\ntemplate <typename F>\ninvoke_member_wrapper_fn(F) -> invoke_member_wrapper_fn<F>;\n} // namespace folly\n\n/***\n *  FOLLY_INVOKE_MEMBER\n *\n *  An invoker expression resulting in an invocable which, when invoked, invokes\n *  the member on the object with the given arguments.\n *\n *  Example:\n *\n *    FOLLY_INVOKE_MEMBER(find)(map, key)\n *\n *  Equivalent to:\n *\n *    map.find(key)\n *\n *  But also equivalent to:\n *\n *    std::invoke(FOLLY_INVOKE_MEMBER(find), map, key)\n *\n *  As an implementation detail, the resulting callable uses a lambda.  This\n *  has two observable consequences.\n *  * Since C++17 only, lambda invocations may be marked constexpr.\n *  * Since C++20 only, lambda definitions may appear in an unevaluated context,\n *    namely, in an operand to decltype, noexcept, sizeof, or typeid.\n */\n#define FOLLY_INVOKE_MEMBER(membername)                                                          \\\n  ::folly::invoke_member_wrapper_fn(                                                             \\\n      [](auto&& __folly_param_o, auto&&... __folly_param_a) constexpr FOLLY_DETAIL_FORWARD_BODY( \\\n          FOLLY_DETAIL_FORWARD_REF(__folly_param_o)                                              \\\n              .membername(FOLLY_DETAIL_FORWARD_REF(__folly_param_a)...)))\n\n/***\n *  FOLLY_CREATE_STATIC_MEMBER_INVOKER\n *\n *  Used to create an invoker type template bound to a specific static-member-\n *  invocable name.\n *\n *  Example:\n *\n *    FOLLY_CREATE_STATIC_MEMBER_INVOKER(foo_invoker, foo);\n *\n *  The type template `foo_invoker` is generated in the current namespace and\n *  may be used as follows:\n *\n *    struct CanFoo {\n *      static int foo(Bar&) { return 1; }\n *      static int foo(Car&&) noexcept { return 2; }\n *    };\n *\n *    using traits = folly::invoke_traits<foo_invoker<CanFoo>>;\n *\n *    traits::invoke(Car{}) // 2\n *\n *    traits::invoke_result<Bar&> // has member\n *    traits::invoke_result_t<Bar&> // int\n *    traits::invoke_result<Bar&&> // empty\n *    traits::invoke_result_t<Bar&&> // error\n *\n *    traits::is_invocable_v<Bar&> // true\n *    traits::is_invocable_v<Bar&&> // false\n *\n *    traits::is_invocable_r_v<int, Bar&> // true\n *    traits::is_invocable_r_v<char*, Bar&> // false\n *\n *    traits::is_nothrow_invocable_v<Bar&> // false\n *    traits::is_nothrow_invocable_v<Car&&> // true\n *\n *    traits::is_nothrow_invocable_v<int, Bar&> // false\n *    traits::is_nothrow_invocable_v<char*, Bar&> // false\n *    traits::is_nothrow_invocable_v<int, Car&&> // true\n *    traits::is_nothrow_invocable_v<char*, Car&&> // false\n */\n#define FOLLY_CREATE_STATIC_MEMBER_INVOKER(classname, membername)       \\\n  template <typename T>                                                 \\\n  struct classname {                                                    \\\n    template <typename... Args, typename U = T>                         \\\n    [[maybe_unused]] FOLLY_ERASE_HACK_GCC constexpr auto operator()(    \\\n        Args&&... args) const                                           \\\n        noexcept(noexcept(U::membername(static_cast<Args&&>(args)...))) \\\n            -> decltype(U::membername(static_cast<Args&&>(args)...)) {  \\\n      return U::membername(static_cast<Args&&>(args)...);               \\\n    }                                                                   \\\n  }\n\n/***\n *  FOLLY_CREATE_STATIC_MEMBER_INVOKER_SUITE\n *\n *  Used to create an invoker type template and associated variable template\n *  bound to a specific static-member-invocable name. The invoker variable\n *  template is named like the static-member-invocable name and the invoker type\n *  template is named with a suffix of _fn.\n *\n *  See FOLLY_CREATE_STATIC_MEMBER_INVOKER.\n */\n#define FOLLY_CREATE_STATIC_MEMBER_INVOKER_SUITE(membername)       \\\n  FOLLY_CREATE_STATIC_MEMBER_INVOKER(membername##_fn, membername); \\\n  template <typename T>                                            \\\n  [[maybe_unused]] inline constexpr membername##_fn<T> membername {}\n\n/***\n *  FOLLY_CREATE_MEMBER_ACCESSOR\n *\n *  Used to create an accessor type bound to a specific data-member name,\n *  providing access to that data-member.\n */\n#define FOLLY_CREATE_MEMBER_ACCESSOR(classname, membername)            \\\n  struct classname {                                                   \\\n    template <typename T>                                              \\\n    [[maybe_unused]] FOLLY_ERASE_HACK_GCC constexpr auto&& operator()( \\\n        T&& val) const noexcept {                                      \\\n      return static_cast<T&&>(val).membername;                         \\\n    }                                                                  \\\n  }\n\n/***\n *  FOLLY_CREATE_MEMBER_ACCESSOR_SUITE\n *\n *  Used to create an accessor type and associated variable bound to a specific\n *  data-member name, providing access to that data-member. The accessor\n *  variable is named like the member name and the accessor type is named with a\n *  suffix of _fn.\n */\n#define FOLLY_CREATE_MEMBER_ACCESSOR_SUITE(membername)       \\\n  FOLLY_CREATE_MEMBER_ACCESSOR(membername##_fn, membername); \\\n  [[maybe_unused]] inline constexpr membername##_fn membername {}\n\nnamespace folly {\n\nnamespace detail_tag_invoke_fn {\n\nvoid tag_invoke();\n\nstruct tag_invoke_fn {\n  template <typename Tag, typename... Args>\n  constexpr auto operator()(Tag tag, Args&&... args) const noexcept(noexcept(\n      tag_invoke(static_cast<Tag&&>(tag), static_cast<Args&&>(args)...)))\n      -> decltype(tag_invoke(\n          static_cast<Tag&&>(tag), static_cast<Args&&>(args)...)) {\n    return tag_invoke(static_cast<Tag&&>(tag), static_cast<Args&&>(args)...);\n  }\n};\n\n// Manually implement the traits here rather than defining them in terms of\n// the corresponding std::invoke_result/is_invocable/is_nothrow_invocable\n// traits to improve compile-times. We don't need all of the generality of\n// the std:: traits and the tag_invoke traits can be used heavily in CPO-based\n// code so optimising them for compile times can make a big difference.\n\n// Use the immediately-invoked function-pointer trick here to avoid\n// instantiating the std::declval<T>() template.\n\ntemplate <typename Tag, typename... Args>\nusing tag_invoke_result_t = decltype(tag_invoke(\n    static_cast<Tag && (*)() noexcept>(nullptr)(),\n    static_cast<Args && (*)() noexcept>(nullptr)()...));\n\ntemplate <typename Tag, typename... Args>\nauto try_tag_invoke(int) noexcept(\n    noexcept(tag_invoke(FOLLY_DECLVAL(Tag&&), FOLLY_DECLVAL(Args&&)...)))\n    -> decltype(static_cast<void>(tag_invoke(FOLLY_DECLVAL(Tag&&), FOLLY_DECLVAL(Args&&)...)), std::true_type{});\n\ntemplate <typename Tag, typename... Args>\nstd::false_type try_tag_invoke(...) noexcept(false);\n\ntemplate <template <typename...> class T, typename... Args>\nstruct defer {\n  using type = T<Args...>;\n};\n\nstruct empty {};\n\n} // namespace detail_tag_invoke_fn\n\n// The expression folly::tag_invoke(tag, args...) is equivalent to performing\n// a call to the expression tag_invoke(tag, args...) using argument-dependent\n// lookup (ADL).\n//\n// This is intended to be used by customization-point objects, which dispatch\n// a call to the CPO to an ADL call to tag_invoke(cpo, args...), using the type\n// of the first argument to disambiguate between customisations for different\n// CPOs rather than using different ADL names for this.\n//\n// For example: Defining a new CPO in terms of tag_invoke.\n//   struct FooCpo {\n//     template<typename A, typename B>\n//     auto operator()(A&& a, B&& b) const\n//         noexcept(folly::is_nothrow_tag_invocable_v<FooCpo, A, B>)\n//         -> folly::tag_invoke_result_t<FooCpo, A, B> {\n//       return folly::tag_invoke(*this, (A&&)a, (B&&)b);\n//     }\n//   };\n//   FOLLY_DEFINE_CPO(FooCpo, Foo)\n//\n// And then customising the Foo CPO for a particular type:\n//   class SomeType {\n//     ...\n//     template<typename B>\n//     friend int tag_invoke(folly::cpo_t<Foo>, const SomeType& a, B&& b) {\n//       // implementation goes here\n//     }\n//   };\n//\n// For more details see the C++ standards proposal: https://wg21.link/P1895R0.\nFOLLY_DEFINE_CPO(detail_tag_invoke_fn::tag_invoke_fn, tag_invoke)\n\n// Query if the 'folly::tag_invoke()' CPO can be invoked with a tag and\n// arguments of the the specified types.\n//\n// This checks whether an overload of the free-function tag_invoke() found\n// by ADL can be invoked with the specified types.\n\ntemplate <typename Tag, typename... Args>\ninline constexpr bool is_tag_invocable_v =\n    decltype(detail_tag_invoke_fn::try_tag_invoke<Tag, Args...>(0))::value;\n\ntemplate <typename Tag, typename... Args>\nstruct is_tag_invocable //\n    : std::bool_constant<is_tag_invocable_v<Tag, Args...>> {};\n\n// Query whether the 'folly::tag_invoke()' CPO can be invoked with a tag\n// and arguments of the specified type and that such an invocation is\n// noexcept.\n\ntemplate <typename Tag, typename... Args>\ninline constexpr bool is_nothrow_tag_invocable_v =\n    noexcept(detail_tag_invoke_fn::try_tag_invoke<Tag, Args...>(0));\n\ntemplate <typename Tag, typename... Args>\nstruct is_nothrow_tag_invocable\n    : std::bool_constant<is_nothrow_tag_invocable_v<Tag, Args...>> {};\n\n// Versions of the above that check in addition that the result is\n// convertible to the given return type R.\n\ntemplate <typename R, typename Tag, typename... Args>\nusing is_tag_invocable_r =\n    folly::is_invocable_r<R, decltype(folly::tag_invoke), Tag, Args...>;\n\ntemplate <typename R, typename Tag, typename... Args>\ninline constexpr bool is_tag_invocable_r_v =\n    is_tag_invocable_r<R, Tag, Args...>::value;\n\ntemplate <typename R, typename Tag, typename... Args>\nusing is_nothrow_tag_invocable_r =\n    folly::is_nothrow_invocable_r<R, decltype(folly::tag_invoke), Tag, Args...>;\n\ntemplate <typename R, typename Tag, typename... Args>\ninline constexpr bool is_nothrow_tag_invocable_r_v =\n    is_nothrow_tag_invocable_r<R, Tag, Args...>::value;\n\nusing detail_tag_invoke_fn::tag_invoke_result_t;\n\ntemplate <typename Tag, typename... Args>\nstruct tag_invoke_result\n    : conditional_t<\n          is_tag_invocable_v<Tag, Args...>,\n          detail_tag_invoke_fn::defer<tag_invoke_result_t, Tag, Args...>,\n          detail_tag_invoke_fn::empty> {};\n\n#if defined(__cpp_concepts)\n\n/// passable_to\n///\n/// Useful for enabling n^k overloads all at once. Example below shows cases of\n/// n=5 with k=1 and k=2.\n///\n/// Useful for transparent hash and key-equal functions for a set which needs\n/// heterogeneous lookup.\n///\n/// Law:\n///   passable_to<Arg, Fun> = invocable<Fun, Arg>\n///\n/// Discussion:\n/// Q:  Why require a one-param function type and not support a multi-param\n///     function type?\n/// A:  Too much arbitrary choice. Interface would be confusing.\n///     To provide an interface like:\n///         passable_to<Slog, Fun, Arg...>\n///     Then `Arg...` would need exactly one \"hole\", which would be represented\n///     by perhaps `void` or perhaps `std::placeholder::_1` or another sentinel.\n///     Alternatively:\n///         passable_to<Slog, Fun, tag_t<ArgFront...>, tag_t<ArgBack...>>\n///     Now the usage gets convoluted.\n///     But, in the end, this can be worked around by passing a function type\n///     that is the result of std::bind_front (C++20) or std::bind_back (C++23).\n///\n/// Example:\n///\n///     struct to_key_fn {\n///       string_view operator()(string_view val) const noexcept { return val; }\n///       string_view operator()(object const&) const noexcept;\n///       string_view operator()(object const*) const noexcept;\n///       string_view operator()(unique_ptr<object> const&) const noexcept;\n///       string_view operator()(shared_ptr<object> const&) const noexcept;\n///     };\n///     static constexpr to_key_fn to_key{};\n///\n///     struct obj_hash : hash<string_view> {\n///       using is_transparent = void;\n///       size_t operator()(passable_to<to_key_fn> auto const& val) noexcept {\n///         return hash<string_view>::operator()(to_key(val));\n///       }\n///     };\n///     struct obj_key_equal : equal_to<string_view> {\n///       using is_transparent = void;\n///       bool operator()(\n///           passable_to<to_key_fn> auto const& lhs,\n///           passable_to<to_key_fn> auto const& rhs) noexcept {\n///         return equal_to<string_view>::operator()(to_key(lhs), to_key(rhs));\n///       }\n///     };\n///\n///     using object_set = std::unordered_set<\n///         std::shared_ptr<object>,\n///         obj_hash,\n///         obj_key_equal>;\n///     object_set objs;\ntemplate <typename Arg, typename Fun>\nconcept passable_to = is_invocable_v<Fun, Arg>;\n\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/functional/Partial.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <tuple>\n#include <utility>\n\n#include <folly/Utility.h>\n#include <folly/functional/Invoke.h>\n\nnamespace folly {\n\nnamespace detail {\nnamespace partial {\n\n// helper type to make sure that the templated constructor in Partial does\n// not accidentally act as copy or move constructor\nstruct PartialConstructFromCallable {};\n\ntemplate <typename F, typename... StoredArg>\nclass Partial {\n  using Tuple = std::tuple<StoredArg...>;\n  using Indexes = std::index_sequence_for<StoredArg...>;\n\n  template <typename Self, std::size_t... I, typename... Args>\n  static auto invokeForward(\n      Self&& self, std::index_sequence<I...>, Args&&... args)\n      -> invoke_result_t<\n          like_t<Self&&, F>,\n          like_t<Self&&, StoredArg>...,\n          Args&&...> {\n    return invoke(\n        std::forward<Self>(self).f_,\n        std::get<I>(std::forward<Self>(self).stored_args_)...,\n        std::forward<Args>(args)...);\n  }\n\n public:\n  template <typename Callable, typename... Args>\n  Partial(PartialConstructFromCallable, Callable&& callable, Args&&... args)\n      : f_(std::forward<Callable>(callable)),\n        stored_args_(std::forward<Args>(args)...) {}\n\n  template <typename... CArgs>\n  auto operator()(CArgs&&... cargs) & -> decltype(invokeForward(\n      std::declval<Partial&>(), Indexes{}, std::declval<CArgs>()...)) {\n    return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...);\n  }\n  template <typename... CArgs>\n  auto operator()(CArgs&&... cargs) const& -> decltype(invokeForward(\n      std::declval<const Partial&>(), Indexes{}, std::declval<CArgs>()...)) {\n    return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...);\n  }\n  template <typename... As>\n  auto operator()(As&&... a) && -> decltype(invokeForward(\n      std::declval<Partial&&>(), Indexes{}, std::declval<As>()...)) {\n    return invokeForward(std::move(*this), Indexes{}, std::forward<As>(a)...);\n  }\n  template <typename... As>\n  auto operator()(As&&... as) const&& -> decltype(invokeForward(\n      std::declval<const Partial&&>(), Indexes{}, std::declval<As>()...)) {\n    return invokeForward(std::move(*this), Indexes{}, std::forward<As>(as)...);\n  }\n\n private:\n  // the stored callable\n  F f_;\n  // the stored arguments, these will be forwarded along with the actual\n  // argumnets to the callable above\n  Tuple stored_args_;\n};\n\n} // namespace partial\n} // namespace detail\n\n/**\n * Partially applies arguments to a callable\n *\n * `partial` takes a callable and zero or more additional arguments and returns\n * a callable object, which when called with zero or more arguments, will invoke\n * the original callable with the additional arguments passed to `partial`,\n * followed by those passed to the call.\n *\n * E.g. `partial(Foo, 1, 2)(3)` is equivalent to `Foo(1, 2, 3)`.\n *\n * `partial` can be used to bind a class method to an instance:\n * `partial(&Foo::method, foo_pointer)` returns a callable object that can be\n * invoked in the same way as `foo_pointer->method`. In case the first\n * argument in a call to `partial` is a member pointer, the second argument\n * can be a reference, pointer or any object that can be dereferenced to\n * an object of type Foo (like `std::shared_ptr` or `std::unique_ptr`).\n *\n * `partial` is similar to `std::bind`, but you don't have to use placeholders\n * to have arguments passed on. Any number of arguments passed to the object\n * returned by `partial` when called will be added to those passed to `partial`\n * and passed to the original callable.\n */\ntemplate <typename F, typename... Args>\nauto partial(F&& f, Args&&... args)\n    -> detail::partial::Partial< //\n        typename std::decay<F>::type,\n        typename std::decay<Args>::type...> {\n  return {\n      detail::partial::PartialConstructFromCallable{},\n      std::forward<F>(f),\n      std::forward<Args>(args)...};\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/functional/protocol.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/functional/Invoke.h>\n#include <folly/functional/traits.h>\n\nnamespace folly {\n\n//  match_empty_function_protocol\n//  match_empty_function_protocol_fn\n//\n//  Evaluates the empty-function protocol for a given value, returning true if\n//  the value matches the empty-function protocol and false otherwise.\n//\n//  A value matches the empty-function protocol when all of the following\n//  conditions hold:\n//  * its type admits explicit construction with the special value nullptr,\n//  * its type admits equality-comparison with the special value nullptr,\n//  * it compares-equal-to the special value nullptr.\n//\n//  In particular, default-constructed values of the following native and\n//  standard-library types match the empty-function protocol:\n//  * pointer-to-function\n//  * pointer-to-member-function\n//  * std::function\n//  * std::move_only_function\n//\n//  Likewise, move-constructed-from and move-assigned-from values of the\n//  following standard-library types match the empty-function protocol:\n//  * std::function\n//  * std::move_only_function\n//  However, it is unspecified whether self-move-assigned-from values of the\n//  above types match the empty-function protocol.\nstruct match_empty_function_protocol_fn {\n private:\n  template <typename T>\n  using detect_from_eq_nullptr =\n      decltype(static_cast<bool>(static_cast<T const&>(T(nullptr)) == nullptr));\n\n  template <typename T>\n  static constexpr bool cx_matches_v =\n      require_sizeof<T> && is_detected_v<detect_from_eq_nullptr, T>;\n\n public:\n  template <typename T, std::enable_if_t<!cx_matches_v<T>, int> = 0>\n  constexpr bool operator()(T const&) const noexcept {\n    return false;\n  }\n  template <typename T, std::enable_if_t<cx_matches_v<T>, int> = 0>\n  constexpr bool operator()(T const& t) const noexcept {\n    return static_cast<bool>(t == nullptr);\n  }\n};\ninline constexpr match_empty_function_protocol_fn\n    match_empty_function_protocol{};\n\n//  ----\n\n//  match_static_lambda_protocol_v\n//\n//  Evaluates the static-lambda protocol for a given type.\n//\n//  A value type T matches the static-lambda protocol when all of the following\n//  conditions hold:\n//  * T is empty\n//  * T is trivially-copyable\n//\n//  In particular, capture-free lambdas match the static-lambda protocol. Static\n//  lambdas, introduced in C++23, are capture-free and therefore match the\n//  static-lambda protocol as well.\n//\n//  In particular, certain function-objects in the standard library match the\n//  static-lambda protocol:\n//  * std::identity\n//  * std::less, std::equal_to, std::greater\n//  * std::hash\n//  * std::default_delete\ntemplate <typename F>\nstatic constexpr bool match_static_lambda_protocol_v =\n    ( //\n        std::is_empty<F>::value && //\n        std::is_trivially_copyable_v<F> && //\n        true);\n\n//  ----\n\nnamespace detail {\n\ntemplate <typename S>\nstruct match_safely_invocable_as_protocol_impl_ {\n  using traits = function_traits<S>;\n\n  using sig_r = typename traits::result;\n  static constexpr bool sig_nx = traits::is_nothrow;\n\n  template <typename F>\n  using fun_cvref_0 = conditional_t<std::is_reference<F>::value, F, F&>;\n  template <typename F>\n  using fun_cvref = fun_cvref_0<typename traits::template value_like<F>>;\n\n  template <typename F>\n  struct fun_r_ {\n    template <typename... A>\n    using apply = invoke_result_t<F, A...>;\n  };\n  template <typename F>\n  using fun_r =\n      typename traits::template arguments<fun_r_<fun_cvref<F>>::template apply>;\n\n  template <typename F>\n  struct fun_inv_ {\n    template <typename... A>\n    using apply = is_invocable_r<sig_r, F, A...>;\n  };\n  template <typename F>\n  struct fun_inv_nx_ {\n    template <typename... A>\n    using apply = is_nothrow_invocable_r<sig_r, F, A...>;\n  };\n\n  template <typename F>\n  using is_invocable_r_ = typename traits::template arguments<\n      conditional_t<sig_nx, fun_inv_nx_<F>, fun_inv_<F>>::template apply>;\n\n  template <typename F, typename FCVR = fun_cvref<F>>\n  static constexpr bool is_invocable_r_v = std::is_reference<FCVR>::value\n      ? is_invocable_r_<F>::value\n      : is_invocable_r_<F&>::value && is_invocable_r_<F&&>::value;\n};\n\ntemplate <\n    typename Fun,\n    typename Sig,\n    typename Impl = match_safely_invocable_as_protocol_impl_<Sig>,\n    typename FunR = typename Impl::template fun_r<Fun>,\n    typename SigR = typename Impl::sig_r,\n    typename FunQ = typename Impl::template fun_cvref<Fun>,\n    bool SigNx = Impl::sig_nx,\n    // forbid reference-to-expired-temporary\n    typename = std::enable_if_t<\n        !std::is_reference<SigR>::value || std::is_reference<FunR>::value>,\n    // forbid object slicing: derived maybe-ref to base non-ref conversion\n    typename = std::enable_if_t<\n        std::is_same<decay_t<SigR>, decay_t<FunR>>::value ||\n        std::is_reference<SigR>::value ||\n        !std::is_base_of<decay_t<SigR>, decay_t<FunR>>::value>,\n    // forbid mismatched cvref\n    // forbid pointer with signature cvref\n    // forbid noexcept wrapping non-noexcept\n    // forbid non-convertible return-type\n    // forbid noexcept wrapping non-noexcept return-type conversion\n    typename = std::enable_if_t<Impl::template is_invocable_r_v<FunQ>>>\nusing match_safely_invocable_as_protocol_detect_1_ = void;\n\ntemplate <typename Fun, typename Sig>\nusing match_safely_invocable_as_protocol_detect_ =\n    match_safely_invocable_as_protocol_detect_1_<Fun, Sig>;\n\n} // namespace detail\n\n//  match_safely_invocable_as_protocol_v\n//\n//  Evaluates the safely-invocable-as protocol for a given type with respect to\n//  a given signature. This protocol determines whether a given type honors a\n//  set of implied constraints imposed by the given function type (signature).\n//\n//  A type F matches the safely-invocable-as protocol for a given signature S\n//  when all of the following conditions hold:\n//  * invocation compatibility\n//    * argument-type-list conversions\n//    * return-type conversion\n//    * cvref compatibility\n//    * noexcept compatibility\n//  * no reference-to-temporary in return-type conversion\n//  * no object slicing in return-type conversion\n//\n//  The meaning of invocation compatibility above is as per the is-callable-from\n//  table in the documentation of std::move_only_function (C++23), extended with\n//  volatile qualifications. See:\n//    http://eel.is/c++draft/func.wrap.move\ntemplate <typename F, typename Sig>\ninline constexpr bool match_safely_invocable_as_protocol_v =\n    is_detected_v<detail::match_safely_invocable_as_protocol_detect_, F, Sig>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/functional/test/ApplyTupleTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/functional/ApplyTuple.h>\n\n#include <algorithm>\n#include <array>\n#include <iostream>\n#include <memory>\n#include <utility>\n\n#include <folly/Overload.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\n\nvoid func(int a, int b, double c) {\n  EXPECT_EQ(a, 1);\n  EXPECT_EQ(b, 2);\n  EXPECT_EQ(c, 3.0);\n}\n\nstruct Wat {\n  void func(int a, int b, double c) { ::func(a, b, c); }\n\n  double retVal(int a, double b) { return a + b; }\n\n  Wat() {}\n  Wat(Wat const&) = delete;\n\n  int foo;\n};\n\nstruct Overloaded {\n  int func(int) { return 0; }\n  bool func(bool) { return true; }\n};\n\nstruct CopyCount {\n  CopyCount() {}\n  CopyCount(CopyCount const&) { std::cout << \"copy count copy ctor\\n\"; }\n};\n\nvoid anotherFunc(CopyCount const&) {}\n\nstd::function<void(int, int, double)> makeFunc() {\n  return &func;\n}\n\nstruct GuardObjBase {\n  GuardObjBase() {}\n  GuardObjBase(GuardObjBase const&) = delete;\n  [[maybe_unused]] GuardObjBase(GuardObjBase&&) noexcept {}\n  GuardObjBase& operator=(GuardObjBase const&) = delete;\n  [[maybe_unused]] GuardObjBase& operator=(GuardObjBase&&) noexcept {\n    return *this;\n  }\n};\n\ntemplate <class F, class Tuple>\nstruct GuardObj : GuardObjBase {\n  explicit GuardObj(F&& f, Tuple&& args)\n      : f_(std::forward<F>(f)), args_(std::forward<Tuple>(args)) {}\n  GuardObj(GuardObj&& g) noexcept\n      : GuardObjBase(std::move(g)),\n        f_(std::move(g.f_)),\n        args_(std::move(g.args_)) {}\n\n  ~GuardObj() { folly::apply(f_, args_); }\n\n  GuardObj(const GuardObj&) = delete;\n  GuardObj& operator=(const GuardObj&) = delete;\n\n private:\n  F f_;\n  Tuple args_;\n};\n\ntemplate <class F, class... Args>\nGuardObj<typename std::decay<F>::type, std::tuple<Args...>> guard(\n    F&& f, Args&&... args) {\n  return GuardObj<typename std::decay<F>::type, std::tuple<Args...>>(\n      std::forward<F>(f), std::tuple<Args...>(std::forward<Args>(args)...));\n}\n\nstruct Mover {\n  Mover() {}\n  Mover(const Mover&) = delete;\n  [[maybe_unused]] Mover(Mover&&) noexcept {}\n  Mover& operator=(const Mover&) = delete;\n  [[maybe_unused]] Mover& operator=(Mover&&) noexcept { return *this; }\n};\n\nvoid move_only_func(Mover&&) {}\n\n} // namespace\n\nTEST(ApplyTuple, Test) {\n  auto argsTuple = std::make_tuple(1, 2, 3.0);\n  auto func2 = func;\n  folly::apply(func2, argsTuple);\n  folly::apply(func, argsTuple);\n  folly::apply(func, std::make_tuple(1, 2, 3.0));\n  folly::apply(makeFunc(), std::make_tuple(1, 2, 3.0));\n  folly::apply(makeFunc(), argsTuple);\n\n  std::unique_ptr<Wat> wat(new Wat);\n  folly::apply(&Wat::func, std::make_tuple(wat.get(), 1, 2, 3.0));\n  auto argsTuple2 = std::make_tuple(wat.get(), 1, 2, 3.0);\n  folly::apply(&Wat::func, argsTuple2);\n\n  EXPECT_EQ(\n      10.0, folly::apply(&Wat::retVal, std::make_tuple(wat.get(), 1, 9.0)));\n\n  auto test = guard(func, 1, 2, 3.0);\n  CopyCount cpy;\n  auto test2 = guard(anotherFunc, cpy);\n  auto test3 = guard(anotherFunc, std::cref(cpy));\n\n  Overloaded ovl;\n  EXPECT_EQ(\n      0,\n      folly::apply(\n          static_cast<int (Overloaded::*)(int)>(&Overloaded::func),\n          std::make_tuple(&ovl, 12)));\n  EXPECT_EQ(\n      /* do not code-mode to EXPECT_TRUE */ true,\n      folly::apply(\n          static_cast<bool (Overloaded::*)(bool)>(&Overloaded::func),\n          std::make_tuple(&ovl, false)));\n\n  int x = folly::apply(std::plus<int>(), std::make_tuple(12, 12));\n  EXPECT_EQ(24, x);\n\n  Mover m;\n  folly::apply(\n      move_only_func, std::forward_as_tuple(std::forward<Mover>(Mover())));\n  const auto tuple3 = std::make_tuple(1, 2, 3.0);\n  folly::apply(func, tuple3);\n}\n\nTEST(ApplyTuple, Mutable) {\n  auto argsTuple = std::make_tuple(1, 2, 3.0);\n\n  folly::apply(\n      [](int a, int b, double c) mutable { func(a, b, c); }, argsTuple);\n}\n\nTEST(ApplyTuple, ConstOverloads) {\n  struct ConstOverloaded {\n    ConstOverloaded() {}\n    int operator()() { return 101; }\n    int operator()() const { return 102; }\n  };\n\n  ConstOverloaded covl;\n\n  // call operator()()\n  EXPECT_EQ(folly::apply(covl, std::make_tuple()), 101);\n  EXPECT_EQ(folly::apply(std::ref(covl), std::make_tuple()), 101);\n  EXPECT_EQ(folly::apply(std::move(covl), std::make_tuple()), 101);\n\n  // call operator()() const\n  EXPECT_EQ(\n      folly::apply(const_cast<ConstOverloaded const&>(covl), std::make_tuple()),\n      102);\n  EXPECT_EQ(folly::apply(std::cref(covl), std::make_tuple()), 102);\n}\n\nTEST(ApplyTuple, RefOverloads) {\n  struct RefOverloaded {\n    RefOverloaded() {}\n    int operator()() & { return 201; }\n    int operator()() const& { return 202; }\n    int operator()() && { return 203; }\n  };\n\n  RefOverloaded rovl;\n\n  // call operator()() &\n  EXPECT_EQ(folly::apply(rovl, std::make_tuple()), 201);\n  EXPECT_EQ(folly::apply(std::ref(rovl), std::make_tuple()), 201);\n\n  // call operator()() const &\n  EXPECT_EQ(\n      folly::apply(const_cast<RefOverloaded const&>(rovl), std::make_tuple()),\n      202);\n  EXPECT_EQ(folly::apply(std::cref(rovl), std::make_tuple()), 202);\n\n  // call operator()() &&\n  EXPECT_EQ(folly::apply(std::move(rovl), std::make_tuple()), 203);\n}\n\nstruct MemberFunc {\n  int x;\n  int getX() const { return x; }\n  void setX(int xx) { x = xx; }\n};\n\nTEST(ApplyTuple, MemberFunction) {\n  MemberFunc mf;\n  mf.x = 123;\n\n  // call getter\n  EXPECT_EQ(folly::apply(&MemberFunc::getX, std::make_tuple(&mf)), 123);\n\n  // call setter\n  folly::apply(&MemberFunc::setX, std::make_tuple(&mf, 234));\n  EXPECT_EQ(mf.x, 234);\n  EXPECT_EQ(folly::apply(&MemberFunc::getX, std::make_tuple(&mf)), 234);\n}\n\nTEST(ApplyTuple, MemberFunctionWithRefWrapper) {\n  MemberFunc mf;\n  mf.x = 234;\n\n  EXPECT_EQ(\n      folly::apply(&MemberFunc::getX, std::make_tuple(std::ref(mf))), 234);\n}\n\nTEST(ApplyTuple, MemberFunctionWithConstPointer) {\n  MemberFunc mf;\n  mf.x = 234;\n\n  EXPECT_EQ(\n      folly::apply(\n          &MemberFunc::getX,\n          std::make_tuple(const_cast<MemberFunc const*>(&mf))),\n      234);\n}\n\nTEST(ApplyTuple, MemberFunctionWithSharedPtr) {\n  MemberFunc mf;\n  mf.x = 234;\n\n  EXPECT_EQ(\n      folly::apply(\n          &MemberFunc::getX, std::make_tuple(std::make_shared<MemberFunc>(mf))),\n      234);\n}\n\nTEST(ApplyTuple, MemberFunctionWithUniquePtr) {\n  MemberFunc mf;\n  mf.x = 234;\n\n  EXPECT_EQ(\n      folly::apply(\n          &MemberFunc::getX, std::make_tuple(std::make_unique<MemberFunc>(mf))),\n      234);\n}\n\nTEST(ApplyTuple, Array) {\n  folly::apply(func, std::array<int, 3>{{1, 2, 3}});\n  folly::apply(func, std::array<double, 3>{{1, 2, 3}});\n}\n\nTEST(ApplyTuple, Pair) {\n  auto add = [](int x, int y) { return x + y; };\n\n  EXPECT_EQ(folly::apply(add, std::pair<int, int>{1200, 34}), 1234);\n}\n\nTEST(ApplyTuple, MultipleTuples) {\n  auto add = [](int x, int y, int z) { return x * 100 + y * 10 + z; };\n\n  EXPECT_EQ(123, folly::apply(add, std::make_tuple(1, 2, 3)));\n  EXPECT_EQ(\n      123,\n      folly::apply(\n          add, std::tuple_cat(std::make_tuple(1, 2, 3), std::make_tuple())));\n  EXPECT_EQ(\n      123,\n      folly::apply(\n          add, std::tuple_cat(std::make_tuple(1, 2), std::make_tuple(3))));\n  EXPECT_EQ(\n      123,\n      folly::apply(\n          add, std::tuple_cat(std::make_tuple(1), std::make_tuple(2, 3))));\n  EXPECT_EQ(\n      123,\n      folly::apply(\n          add, std::tuple_cat(std::make_tuple(), std::make_tuple(1, 2, 3))));\n\n  EXPECT_EQ(\n      123,\n      folly::apply(\n          add,\n          std::tuple_cat(\n              std::make_tuple(1, 2, 3), std::make_tuple(), std::make_tuple())));\n  EXPECT_EQ(\n      123,\n      folly::apply(\n          add,\n          std::tuple_cat(\n              std::make_tuple(1), std::make_tuple(2), std::make_tuple(3))));\n  EXPECT_EQ(\n      123,\n      folly::apply(\n          add,\n          std::tuple_cat(\n              std::make_tuple(1), std::make_tuple(), std::make_tuple(2, 3))));\n}\n\nTEST(ApplyTuple, UncurryCopyMove) {\n  std::string separator = \"================================\\n\";\n  auto formatRow = folly::uncurry([=](std::string a, std::string b) {\n    // capture separator by copy\n    return separator + a + \"\\n\" + b + \"\\n\" + separator;\n  });\n  auto row = std::make_tuple(\"hello\", \"world\");\n  auto expected = separator + \"hello\\nworld\\n\" + separator;\n  EXPECT_EQ(expected, formatRow(row));\n  auto formatRowCopy = formatRow;\n  EXPECT_EQ(expected, formatRowCopy(row));\n  auto formatRowMove = std::move(formatRow);\n  EXPECT_EQ(expected, formatRowMove(row));\n\n  // capture value moved out from formatRow\n  EXPECT_NE(expected, formatRow(row));\n}\n\nTEST(ApplyTuple, Uncurry) {\n  EXPECT_EQ(42, folly::uncurry([](int x, int y) {\n              return x * y;\n            })(std::pair<int, int>(6, 7)));\n  EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) {\n              return x * y;\n            })(std::pair<int&&, int&&>(6, 7)));\n  EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) {\n              return x * y;\n            })(std::pair<int&&, int&&>(6, 7)));\n\n  std::string long1 = \"a long string exceeding small string size\";\n  std::string long2 = \"and here is another one!\";\n  std::string expected = long1 + long2;\n\n  auto cat = folly::uncurry([](std::string a, std::string b) {\n    return std::move(a) + std::move(b);\n  });\n\n  EXPECT_EQ(expected, cat(std::make_pair(long1, long2)));\n  EXPECT_FALSE(long1.empty());\n  EXPECT_FALSE(long2.empty());\n  EXPECT_EQ(expected, cat(std::tie(long1, long2)));\n  EXPECT_FALSE(long1.empty());\n  EXPECT_FALSE(long2.empty());\n  EXPECT_EQ(\n      expected, cat(std::forward_as_tuple(std::move(long1), std::move(long2))));\n  EXPECT_TRUE(long1.empty());\n  EXPECT_TRUE(long2.empty());\n}\n\nTEST(ApplyTuple, UncurryStdFind) {\n  std::vector<std::pair<int, int>> v{{1, 9}, {2, 8}, {3, 7}, {4, 6}, {5, 5}};\n  EXPECT_EQ(\n      3, std::count_if(v.begin(), v.end(), folly::uncurry([](int a, int b) {\n                         return b % a == 0;\n                       })));\n}\n\nnamespace {\nstruct S {\n  template <typename... Args>\n  explicit S(Args&&... args) : tuple_(std::forward<Args>(args)...) {}\n\n  std::tuple<int, double, std::string> tuple_;\n};\n} // namespace\n\nTEST(MakeIndexSequenceFromTuple, Basic) {\n  using folly::index_sequence_for_tuple;\n  using OneElementTuple = std::tuple<int>;\n  using TwoElementTuple = std::tuple<int>;\n\n  EXPECT_TRUE((std::is_same<\n               index_sequence_for_tuple<OneElementTuple>,\n               std::index_sequence<0>>::value));\n  EXPECT_TRUE((std::is_same<\n               index_sequence_for_tuple<const OneElementTuple>,\n               std::index_sequence<0>>::value));\n\n  EXPECT_TRUE((std::is_same<\n               index_sequence_for_tuple<TwoElementTuple>,\n               std::index_sequence<0>>::value));\n  EXPECT_TRUE((std::is_same<\n               index_sequence_for_tuple<const TwoElementTuple>,\n               std::index_sequence<0>>::value));\n}\n\nTEST(ApplyResult, Basic) {\n  {\n    auto f = [](auto) -> int { return {}; };\n    using F = decltype(f);\n    EXPECT_TRUE(\n        (std::is_same<folly::apply_result_t<F, std::tuple<int>>, int>{}));\n  }\n\n  {\n    auto f = folly::overload(\n        [](int) {},\n        [](double) -> double { return {}; },\n        [](int, int) -> int { return {}; });\n    using F = decltype(f);\n\n    EXPECT_TRUE(\n        (std::is_same<folly::apply_result_t<F, std::tuple<int>>, void>::value));\n    EXPECT_TRUE((\n        std::is_same<folly::apply_result_t<F, std::tuple<double>>, double>::\n            value));\n    EXPECT_TRUE((\n        std::is_same<folly::apply_result_t<F, std::tuple<int, int>>, int>::\n            value));\n  }\n}\n\nTEST(IsApplicable, Basic) {\n  {\n    auto f = [] {};\n    using F = decltype(f);\n    EXPECT_TRUE((folly::is_applicable_v<F, std::tuple<>>));\n    EXPECT_FALSE((folly::is_applicable_v<F, std::tuple<int>>));\n  }\n  {\n    auto f = folly::overload([](int) {}, [](double) -> double { return {}; });\n    using F = decltype(f);\n    EXPECT_TRUE((folly::is_applicable_v<F, std::tuple<double>>));\n    EXPECT_TRUE((folly::is_applicable_v<F, std::tuple<int>>));\n    EXPECT_FALSE((folly::is_applicable_v<F, std::tuple<>>));\n    EXPECT_FALSE((folly::is_applicable_v<F, std::tuple<int, double>>));\n  }\n}\n\nTEST(IsNothrowApplicable, Basic) {\n  {\n    auto f = []() noexcept {};\n    using F = decltype(f);\n    EXPECT_TRUE((folly::is_nothrow_applicable_v<F, std::tuple<>>));\n    EXPECT_FALSE((folly::is_nothrow_applicable_v<F, std::tuple<int>>));\n  }\n  {\n    auto f = folly::overload(\n        [](int) noexcept {}, [](double) -> double { return {}; });\n    using F = decltype(f);\n    EXPECT_FALSE((folly::is_nothrow_applicable_v<F, std::tuple<double>>));\n    EXPECT_TRUE((folly::is_nothrow_applicable_v<F, std::tuple<int>>));\n    EXPECT_FALSE((folly::is_nothrow_applicable_v<F, std::tuple<>>));\n    EXPECT_FALSE((folly::is_nothrow_applicable_v<F, std::tuple<int, double>>));\n  }\n}\n\nTEST(IsApplicableR, Basic) {\n  {\n    auto f = []() -> int { return {}; };\n    using F = decltype(f);\n    EXPECT_TRUE((folly::is_applicable_r_v<double, F, std::tuple<>>));\n    EXPECT_FALSE((folly::is_applicable_r_v<double, F, std::tuple<int>>));\n  }\n  {\n    auto f = folly::overload(\n        [](int) noexcept {}, [](double) -> double { return {}; });\n    using F = decltype(f);\n    EXPECT_TRUE((folly::is_applicable_r_v<float, F, std::tuple<double>>));\n    EXPECT_TRUE((folly::is_applicable_r_v<void, F, std::tuple<int>>));\n    EXPECT_FALSE((folly::is_applicable_r_v<void, F, std::tuple<>>));\n    EXPECT_FALSE(\n        (folly::is_applicable_r_v<double, F, std::tuple<int, double>>));\n  }\n}\n\nTEST(IsNothrowApplicableR, Basic) {\n  {\n    auto f = []() noexcept -> int { return {}; };\n    using F = decltype(f);\n    EXPECT_TRUE((folly::is_nothrow_applicable_r_v<double, F, std::tuple<>>));\n    EXPECT_FALSE(\n        (folly::is_nothrow_applicable_r_v<double, F, std::tuple<int>>));\n  }\n  {\n    auto f = folly::overload(\n        [](int) noexcept {}, [](double) -> double { return {}; });\n    using F = decltype(f);\n    EXPECT_FALSE(\n        (folly::is_nothrow_applicable_r_v<float, F, std::tuple<double>>));\n    EXPECT_TRUE((folly::is_nothrow_applicable_r_v<void, F, std::tuple<int>>));\n    EXPECT_FALSE((folly::is_nothrow_applicable_r_v<void, F, std::tuple<>>));\n    EXPECT_FALSE(\n        (folly::is_nothrow_applicable_r_v<double, F, std::tuple<int, double>>));\n  }\n}\n\nTEST(ForwardTuple, Basic) {\n  auto tuple = std::make_tuple(1, 2.0);\n\n  EXPECT_TRUE((std::is_same<\n               decltype(folly::forward_tuple(tuple)),\n               std::tuple<int&, double&>>::value));\n  EXPECT_EQ(folly::forward_tuple(tuple), tuple);\n  EXPECT_TRUE((std::is_same<\n               decltype(folly::forward_tuple(std::as_const(tuple))),\n               std::tuple<const int&, const double&>>::value));\n  EXPECT_EQ(folly::forward_tuple(std::as_const(tuple)), tuple);\n\n  EXPECT_TRUE((std::is_same<\n               decltype(folly::forward_tuple(std::move(tuple))),\n               std::tuple<int&&, double&&>>::value));\n  EXPECT_EQ(folly::forward_tuple(std::move(tuple)), tuple);\n#if defined(__GLIBCXX__) && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE < 8)\n  constexpr bool before_lwg2485 = true;\n#else\n  constexpr bool before_lwg2485 = false;\n#endif\n  EXPECT_TRUE(\n      (std::is_same<\n          decltype(folly::forward_tuple(std::move(std::as_const(tuple)))),\n          std::conditional_t<\n              before_lwg2485,\n              std::tuple<const int&, const double&>,\n              std::tuple<const int&&, const double&&>>>::value));\n  EXPECT_EQ(folly::forward_tuple(std::move(std::as_const(tuple))), tuple);\n\n  auto integer = 1;\n  auto floating_point = 2.0;\n  auto ref_tuple = std::forward_as_tuple(integer, std::move(floating_point));\n\n  EXPECT_TRUE((std::is_same<\n               decltype(folly::forward_tuple(ref_tuple)),\n               std::tuple<int&, double&>>::value));\n\n  EXPECT_TRUE((std::is_same<\n               decltype(folly::forward_tuple(std::move(ref_tuple))),\n               std::tuple<int&, double&&>>::value));\n\n  EXPECT_TRUE(\n      (std::is_same<\n          decltype(std::tuple_cat(\n              folly::forward_tuple(tuple),\n              folly::forward_tuple(std::move(tuple)))),\n          std::tuple<int&, double&, int&&, double&&>>::value));\n}\n"
  },
  {
    "path": "folly/functional/test/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_unittest.bzl\", \"fb_dirsync_cpp_unittest\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_cxx_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat unit tests\n\nfb_dirsync_cpp_unittest(\n    name = \"apply_tuple_test\",\n    srcs = [\"ApplyTupleTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly:overload\",\n        \"//folly/functional:apply_tuple\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"invoke_test\",\n    srcs = [\"InvokeTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/functional:invoke\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"partial_test\",\n    srcs = [\"PartialTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly:function\",\n        \"//folly/functional:partial\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"protocol_test\",\n    srcs = [\"protocol_test.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/functional:protocol\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_unittest(\n    name = \"traits_test\",\n    srcs = [\"traits_test.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/functional:traits\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\n# fbcode unit tests\n"
  },
  {
    "path": "folly/functional/test/InvokeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/functional/Invoke.h>\n\n#include <folly/portability/GTest.h>\n\nclass InvokeTest : public testing::Test {};\n\nnamespace {\n\nstruct from_any {\n  template <typename T>\n  /* implicit */ from_any(T&&) {}\n};\n\nstruct Cv {\n  /* implicit */ [[maybe_unused]] operator std::false_type() const;\n  /* implicit */ [[maybe_unused]] operator std::true_type() const noexcept;\n};\n\nstruct ImmCv : Cv {\n  ImmCv(ImmCv const&) = delete;\n  ImmCv(ImmCv&&) = delete;\n  void operator=(ImmCv const&) = delete;\n  void operator=(ImmCv&&) = delete;\n};\n\nstruct Fn {\n  char operator()(int, int) noexcept { return 'a'; }\n  int volatile&& operator()(int, char const*) { return std::move(x_); }\n  [[maybe_unused]] float operator()(float, float) { return 3.14; }\n  /* implicit */ [[maybe_unused]] Cv operator()(Cv*) noexcept;\n  /* implicit */ [[maybe_unused]] ImmCv operator()(ImmCv*) noexcept;\n  int volatile x_ = 17;\n};\n\nnamespace invoker {\n\nFOLLY_CREATE_MEMBER_INVOKER_SUITE(test);\n\n}\n\nstruct Obj {\n  char test(int, int) noexcept { return 'a'; }\n  int volatile&& test(int, char const*) { return std::move(x_); }\n  [[maybe_unused]] float test(float, float) { return 3.14; }\n  int volatile x_ = 17;\n};\n\nnamespace x {\nstruct Obj {};\nint go(Obj const&, int) noexcept {\n  return 3;\n}\n} // namespace x\n\nnamespace y {\nstruct Obj {};\nchar go(Obj const&, char const*) {\n  return 'a';\n}\n} // namespace y\n\nnamespace z {\nstruct Obj {};\n} // namespace z\n[[maybe_unused]] float go(z::Obj const&, int) {\n  return 9;\n}\n\nnamespace swappable {\nstruct Obj {\n  int x_;\n};\nvoid swap(Obj&, Obj&) noexcept {} // no-op\n} // namespace swappable\n\nstruct AltSwappable {};\nstruct AltSwappableRet {};\nnamespace unswappable {\n[[maybe_unused]] AltSwappableRet swap(AltSwappable&, AltSwappable&);\n} // namespace unswappable\n\nnamespace invoker {\n\nFOLLY_CREATE_FREE_INVOKER_SUITE(go);\nFOLLY_CREATE_FREE_INVOKER_SUITE(swap, std, unswappable);\nFOLLY_CREATE_FREE_INVOKER_SUITE(no_such_thing);\nFOLLY_CREATE_QUAL_INVOKER_SUITE(std_swap, ::std::swap);\n\n} // namespace invoker\n\n} // namespace\n\nTEST_F(InvokeTest, invoke) {\n  Fn fn;\n\n  EXPECT_TRUE(noexcept(folly::invoke(fn, 1, 2)));\n  EXPECT_FALSE(noexcept(folly::invoke(fn, 1, \"2\")));\n\n  EXPECT_EQ('a', folly::invoke(fn, 1, 2));\n  EXPECT_EQ(17, folly::invoke(fn, 1, \"2\"));\n\n  using FnA = char (Fn::*)(int, int);\n  using FnB = int volatile && (Fn::*)(int, char const*);\n  EXPECT_EQ('a', folly::invoke(static_cast<FnA>(&Fn::operator()), fn, 1, 2));\n  EXPECT_EQ(17, folly::invoke(static_cast<FnB>(&Fn::operator()), fn, 1, \"2\"));\n}\n\nTEST_F(InvokeTest, invoke_result) {\n  EXPECT_TRUE(\n      (std::is_same<char, folly::invoke_result_t<Fn, int, char>>::value));\n  EXPECT_TRUE((\n      std::is_same<int volatile&&, folly::invoke_result_t<Fn, int, char*>>::\n          value));\n}\n\nTEST_F(InvokeTest, is_invocable) {\n  EXPECT_TRUE((folly::is_invocable_v<Fn, int, char>));\n  EXPECT_TRUE((folly::is_invocable_v<Fn, int, char*>));\n  EXPECT_FALSE((folly::is_invocable_v<Fn, int>));\n  EXPECT_TRUE((folly::is_invocable_v<Fn, Cv*>));\n  EXPECT_TRUE((folly::is_invocable_v<Fn, ImmCv*>));\n}\n\nTEST_F(InvokeTest, is_invocable_r) {\n  EXPECT_TRUE((folly::is_invocable_r_v<int, Fn, int, char>));\n  EXPECT_TRUE((folly::is_invocable_r_v<int, Fn, int, char*>));\n  EXPECT_FALSE((folly::is_invocable_r_v<int, Fn, int>));\n  EXPECT_TRUE((folly::is_invocable_r_v<std::false_type, Fn, Cv*>));\n  EXPECT_TRUE((folly::is_invocable_r_v<std::true_type, Fn, Cv*>));\n  EXPECT_TRUE((folly::is_invocable_r_v<void, Fn, Cv*>));\n  EXPECT_TRUE((folly::is_invocable_r_v<ImmCv, Fn, ImmCv*>));\n  EXPECT_TRUE((folly::is_invocable_r_v<void, Fn, ImmCv*>));\n  EXPECT_TRUE((folly::is_invocable_r_v<std::false_type, Fn, ImmCv*>));\n  EXPECT_TRUE((folly::is_invocable_r_v<std::true_type, Fn, ImmCv*>));\n}\n\nTEST_F(InvokeTest, is_nothrow_invocable) {\n  EXPECT_TRUE((folly::is_nothrow_invocable_v<Fn, int, char>));\n  EXPECT_FALSE((folly::is_nothrow_invocable_v<Fn, int, char*>));\n  EXPECT_FALSE((folly::is_nothrow_invocable_v<Fn, int>));\n  EXPECT_TRUE((folly::is_nothrow_invocable_v<Fn, Cv*>));\n  EXPECT_TRUE((folly::is_nothrow_invocable_v<Fn, ImmCv*>));\n}\n\nTEST_F(InvokeTest, is_nothrow_invocable_r) {\n  EXPECT_TRUE((folly::is_nothrow_invocable_r_v<int, Fn, int, char>));\n  EXPECT_FALSE((folly::is_nothrow_invocable_r_v<int, Fn, int, char*>));\n  EXPECT_FALSE((folly::is_nothrow_invocable_r_v<int, Fn, int>));\n  EXPECT_FALSE((folly::is_nothrow_invocable_r_v<std::false_type, Fn, Cv*>));\n  EXPECT_TRUE((folly::is_nothrow_invocable_r_v<std::true_type, Fn, Cv*>));\n  EXPECT_TRUE((folly::is_nothrow_invocable_r_v<void, Fn, Cv*>));\n  EXPECT_TRUE((folly::is_nothrow_invocable_r_v<ImmCv, Fn, ImmCv*>));\n  EXPECT_TRUE((folly::is_nothrow_invocable_r_v<void, Fn, ImmCv*>));\n  EXPECT_FALSE((folly::is_nothrow_invocable_r_v<int, Fn, ImmCv*>));\n  EXPECT_FALSE((folly::is_nothrow_invocable_r_v<std::false_type, Fn, ImmCv*>));\n  EXPECT_TRUE((folly::is_nothrow_invocable_r_v<std::true_type, Fn, ImmCv*>));\n}\n\nTEST_F(InvokeTest, free_invoke) {\n  using traits = folly::invoke_traits<decltype(invoker::go)>;\n\n  x::Obj x_;\n  y::Obj y_;\n\n  EXPECT_TRUE(noexcept(traits::invoke(x_, 3)));\n  EXPECT_FALSE(noexcept(traits::invoke(y_, \"hello\")));\n\n  EXPECT_EQ(3, traits::invoke(x_, 3));\n  EXPECT_EQ('a', traits::invoke(y_, \"hello\"));\n}\n\nTEST_F(InvokeTest, free_invoke_result) {\n  using traits = folly::invoke_traits<decltype(invoker::go)>;\n\n  EXPECT_TRUE((std::is_same<int, traits::invoke_result_t<x::Obj, int>>::value));\n  EXPECT_TRUE((\n      std::is_same<char, traits::invoke_result_t<y::Obj, char const*>>::value));\n}\n\nTEST_F(InvokeTest, free_is_invocable) {\n  using traits = folly::invoke_traits<decltype(invoker::go)>;\n\n  EXPECT_TRUE((traits::is_invocable_v<x::Obj, int>));\n  EXPECT_TRUE((traits::is_invocable_v<y::Obj, char const*>));\n  EXPECT_FALSE((traits::is_invocable_v<z::Obj, int>));\n  EXPECT_FALSE((traits::is_invocable_v<float>));\n}\n\nTEST_F(InvokeTest, free_is_invocable_r) {\n  using traits = folly::invoke_traits<decltype(invoker::go)>;\n\n  EXPECT_TRUE((traits::is_invocable_r_v<int, x::Obj, int>));\n  EXPECT_TRUE((traits::is_invocable_r_v<char, y::Obj, char const*>));\n  EXPECT_FALSE((traits::is_invocable_r_v<float, z::Obj, int>));\n  EXPECT_FALSE((traits::is_invocable_r_v<from_any, float>));\n}\n\nTEST_F(InvokeTest, free_is_nothrow_invocable) {\n  using traits = folly::invoke_traits<decltype(invoker::go)>;\n\n  EXPECT_TRUE((traits::is_nothrow_invocable_v<x::Obj, int>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<y::Obj, char const*>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<z::Obj, int>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<float>));\n}\n\nTEST_F(InvokeTest, free_is_nothrow_invocable_r) {\n  using traits = folly::invoke_traits<decltype(invoker::go)>;\n\n  EXPECT_TRUE((traits::is_nothrow_invocable_r_v<int, x::Obj, int>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<char, y::Obj, char const*>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<float, z::Obj, int>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<from_any, float>));\n}\n\nTEST_F(InvokeTest, free_invoke_swap) {\n  using traits = folly::invoke_traits<decltype(invoker::swap)>;\n\n  int a = 3;\n  int b = 4;\n\n  traits::invoke(a, b);\n  EXPECT_EQ(4, a);\n  EXPECT_EQ(3, b);\n\n  swappable::Obj x{3};\n  swappable::Obj y{4};\n\n  traits::invoke(x, y);\n  EXPECT_EQ(3, x.x_);\n  EXPECT_EQ(4, y.x_);\n\n  std::swap(x, y);\n  EXPECT_EQ(4, x.x_);\n  EXPECT_EQ(3, y.x_);\n\n  EXPECT_TRUE((\n      traits::is_invocable_r_v<AltSwappableRet, AltSwappable&, AltSwappable&>));\n}\n\nTEST_F(InvokeTest, qual_invoke_swap) {\n  using traits = folly::invoke_traits<decltype(invoker::std_swap)>;\n\n  int a = 3;\n  int b = 4;\n\n  traits::invoke(a, b);\n  EXPECT_EQ(4, a);\n  EXPECT_EQ(3, b);\n\n  swappable::Obj x{3};\n  swappable::Obj y{4};\n\n  traits::invoke(x, y);\n  EXPECT_EQ(4, x.x_);\n  EXPECT_EQ(3, y.x_);\n\n  std::swap(x, y);\n  EXPECT_EQ(3, x.x_);\n  EXPECT_EQ(4, y.x_);\n}\n\nTEST_F(InvokeTest, invoke_qual) {\n  auto go = FOLLY_INVOKE_QUAL(::std::swap);\n\n  int a = 3;\n  int b = 4;\n  EXPECT_TRUE(noexcept(go(a, b)));\n\n  go(a, b);\n  EXPECT_EQ(4, a);\n  EXPECT_EQ(3, b);\n}\n\nTEST_F(InvokeTest, member_invoke) {\n  using traits = folly::invoke_traits<decltype(invoker::test)>;\n\n  Obj fn;\n\n  EXPECT_TRUE(noexcept(traits::invoke(fn, 1, 2)));\n  EXPECT_FALSE(noexcept(traits::invoke(fn, 1, \"2\")));\n\n  EXPECT_EQ('a', traits::invoke(fn, 1, 2));\n  EXPECT_EQ(17, traits::invoke(fn, 1, \"2\"));\n}\n\nTEST_F(InvokeTest, member_invoke_result) {\n  using traits = folly::invoke_traits<invoker::test_fn>;\n\n  EXPECT_TRUE(\n      (std::is_same<char, traits::invoke_result_t<Obj, int, char>>::value));\n  EXPECT_TRUE((\n      std::is_same<int volatile&&, traits::invoke_result_t<Obj, int, char*>>::\n          value));\n}\n\nTEST_F(InvokeTest, member_is_invocable) {\n  using traits = folly::invoke_traits<decltype(invoker::test)>;\n\n  EXPECT_TRUE((traits::is_invocable_v<Obj, int, char>));\n  EXPECT_TRUE((traits::is_invocable_v<Obj, int, char*>));\n  EXPECT_FALSE((traits::is_invocable_v<Obj, int>));\n}\n\nTEST_F(InvokeTest, member_is_invocable_r) {\n  using traits = folly::invoke_traits<decltype(invoker::test)>;\n\n  EXPECT_TRUE((traits::is_invocable_r_v<int, Obj, int, char>));\n  EXPECT_TRUE((traits::is_invocable_r_v<int, Obj, int, char*>));\n  EXPECT_FALSE((traits::is_invocable_r_v<int, Obj, int>));\n}\n\nTEST_F(InvokeTest, member_is_nothrow_invocable) {\n  using traits = folly::invoke_traits<decltype(invoker::test)>;\n\n  EXPECT_TRUE((traits::is_nothrow_invocable_v<Obj, int, char>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<Obj, int, char*>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<Obj, int>));\n}\n\nTEST_F(InvokeTest, member_is_nothrow_invocable_r) {\n  using traits = folly::invoke_traits<decltype(invoker::test)>;\n\n  EXPECT_TRUE((traits::is_nothrow_invocable_r_v<int, Obj, int, char>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, Obj, int, char*>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, Obj, int>));\n}\n\nTEST_F(InvokeTest, invoke_member) {\n  auto test = FOLLY_INVOKE_MEMBER(test);\n  static_assert( //\n      folly::is_instantiation_of_v<\n          folly::invoke_member_wrapper_fn,\n          decltype(test)>);\n\n  Obj fn;\n\n  EXPECT_TRUE(noexcept(test(fn, 1, 2)));\n  EXPECT_FALSE(noexcept(test(fn, 1, \"2\")));\n\n  EXPECT_EQ('a', test(fn, 1, 2));\n  EXPECT_EQ(17, test(fn, 1, \"2\"));\n}\n\nnamespace {\n\nnamespace invoker {\n\nFOLLY_CREATE_STATIC_MEMBER_INVOKER_SUITE(stat);\n\n}\n\n} // namespace\n\nTEST_F(InvokeTest, static_member_invoke) {\n  struct HasStat {\n    static char stat(int, int) noexcept { return 'a'; }\n    static int volatile&& stat(int, char const*) {\n      static int volatile x_ = 17;\n      return std::move(x_);\n    }\n    [[maybe_unused]] static float stat(float, float) { return 3.14; }\n  };\n  using traits = folly::invoke_traits<decltype(invoker::stat<HasStat>)>;\n\n  EXPECT_TRUE((traits::is_invocable_v<int, char>));\n  EXPECT_TRUE((traits::is_invocable_v<int, char>));\n  EXPECT_TRUE((traits::is_invocable_v<int, char*>));\n  EXPECT_FALSE((traits::is_invocable_v<int>));\n\n  EXPECT_TRUE((traits::is_invocable_r_v<int, int, char>));\n  EXPECT_TRUE((traits::is_invocable_r_v<int, int, char*>));\n  EXPECT_FALSE((traits::is_invocable_r_v<int, int>));\n\n  EXPECT_TRUE((traits::is_nothrow_invocable_v<int, char>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<int, char*>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<int>));\n\n  EXPECT_TRUE((traits::is_nothrow_invocable_r_v<int, int, char>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, int, char*>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, int>));\n}\n\nTEST_F(InvokeTest, static_member_no_invoke) {\n  struct HasNoStat {};\n\n  using traits = folly::invoke_traits<decltype(invoker::stat<HasNoStat>)>;\n\n  EXPECT_FALSE((traits::is_invocable_v<>));\n  EXPECT_FALSE((traits::is_invocable_v<int>));\n\n  EXPECT_FALSE((traits::is_invocable_r_v<int>));\n  EXPECT_FALSE((traits::is_invocable_r_v<int, int>));\n\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_v<int>));\n\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int>));\n  EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, int>));\n}\n\nTEST_F(InvokeTest, invoke_first_match) {\n  struct a {\n    [[maybe_unused]] void operator()(int) const;\n  };\n  struct b {\n    [[maybe_unused]] void operator()(int) const;\n  };\n  struct c {\n    [[maybe_unused]] void operator()() const;\n  };\n  using inv = folly::invoke_first_match<a, b, c>;\n  EXPECT_TRUE((folly::is_invocable_v<inv const&, int>));\n  EXPECT_TRUE((folly::is_invocable_v<inv const&>));\n  EXPECT_FALSE((folly::is_invocable_v<inv const&, int, int>));\n}\n\nnamespace {\n\nstruct TestCustomisationPointFn {\n  template <typename T, typename U>\n  constexpr auto operator()(T&& t, U&& u) const noexcept(\n      folly::is_nothrow_tag_invocable_v<TestCustomisationPointFn, T, U>)\n      -> folly::tag_invoke_result_t<TestCustomisationPointFn, T, U> {\n    return folly::tag_invoke(*this, static_cast<T&&>(t), static_cast<U&&>(u));\n  }\n};\n\nFOLLY_DEFINE_CPO(TestCustomisationPointFn, testCustomisationPoint)\n\nstruct TypeA {\n  constexpr friend int tag_invoke(\n      folly::cpo_t<testCustomisationPoint>, const TypeA&, int value) {\n    return value * 2;\n  }\n  constexpr friend bool tag_invoke(\n      folly::cpo_t<testCustomisationPoint>, const TypeA&, bool value) noexcept {\n    return !value;\n  }\n};\n\n} // namespace\n\nstatic_assert(\n    folly::is_invocable<decltype(testCustomisationPoint), TypeA, int>::value);\nstatic_assert(\n    !folly::is_invocable<decltype(testCustomisationPoint), TypeA, TypeA>::\n        value);\nstatic_assert(\n    folly::is_nothrow_invocable<decltype(testCustomisationPoint), TypeA, bool>::\n        value);\nstatic_assert(\n    !folly::is_nothrow_invocable<decltype(testCustomisationPoint), TypeA, int>::\n        value);\nstatic_assert(\n    std::is_same<\n        folly::invoke_result_t<decltype(testCustomisationPoint), TypeA, int>,\n        int>::value);\n\n// Test that the CPO forwards through constexpr-ness of the\n// customisations by evaluating the CPO in a static_assert()\n// which forces compile-time evaluation.\nstatic_assert(testCustomisationPoint(TypeA{}, 10) == 20);\nstatic_assert(!testCustomisationPoint(TypeA{}, true));\n\nTEST_F(InvokeTest, TagInvokeCustomisationPoint) {\n  const TypeA a;\n  EXPECT_EQ(10, testCustomisationPoint(a, 5));\n  EXPECT_EQ(false, testCustomisationPoint(a, true));\n}\n\nnamespace {\n\nnamespace accessor {\n\nFOLLY_CREATE_MEMBER_ACCESSOR_SUITE(value);\nFOLLY_CREATE_MEMBER_ACCESSOR_SUITE(data);\nFOLLY_CREATE_MEMBER_ACCESSOR_SUITE(name);\n\n} // namespace accessor\n\nstruct DataHolder {\n  int value = 42;\n  std::string data = \"hello\";\n  const char* name = \"test\";\n};\n\nstruct ConstDataHolder {\n  const int value = 99;\n  const std::string data = \"world\";\n};\n\n} // namespace\n\nTEST_F(InvokeTest, member_accessor_basic) {\n  DataHolder obj;\n\n  EXPECT_EQ(42, accessor::value(obj));\n  EXPECT_EQ(\"hello\", accessor::data(obj));\n  EXPECT_STREQ(\"test\", accessor::name(obj));\n\n  accessor::value(obj) = 100;\n  EXPECT_EQ(100, obj.value);\n\n  accessor::data(obj) = \"modified\";\n  EXPECT_EQ(\"modified\", obj.data);\n}\n\nTEST_F(InvokeTest, member_accessor_forwarding) {\n  DataHolder obj;\n\n  {\n    decltype(auto) ref = accessor::value(obj);\n    static_assert(std::is_same<decltype(ref), int&>::value);\n    EXPECT_EQ(&obj.value, &ref);\n  }\n\n  {\n    decltype(auto) ref = accessor::value(std::as_const(obj));\n    static_assert(std::is_same<decltype(ref), const int&>::value);\n    EXPECT_EQ(&obj.value, &ref);\n  }\n\n  {\n    decltype(auto) ref = accessor::value(std::move(obj));\n    static_assert(std::is_same<decltype(ref), int&&>::value);\n    EXPECT_EQ(&obj.value, &ref);\n  }\n\n  {\n    decltype(auto) ref = accessor::value(std::move(std::as_const(obj)));\n    static_assert(std::is_same<decltype(ref), int const&&>::value);\n    EXPECT_EQ(&obj.value, &ref);\n  }\n}\n\nTEST_F(InvokeTest, member_accessor_noexcept) {\n  DataHolder obj;\n\n  EXPECT_TRUE(noexcept(accessor::value(obj)));\n  EXPECT_TRUE(noexcept(accessor::data(obj)));\n  EXPECT_TRUE(noexcept(accessor::name(obj)));\n}\n"
  },
  {
    "path": "folly/functional/test/PartialTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/functional/Partial.h>\n\n#include <memory>\n\n#include <folly/Function.h>\n#include <folly/portability/GTest.h>\n\nusing folly::partial;\n\nint add3(int x, int y, int z) {\n  return 100 * x + 10 * y + z;\n}\n\nTEST(Partial, Simple) {\n  auto p0 = partial(&add3);\n  EXPECT_EQ(123, p0(1, 2, 3));\n\n  auto p1 = partial(&add3, 2);\n  EXPECT_EQ(234, p1(3, 4));\n\n  auto p2 = partial(&add3, 3, 4);\n  EXPECT_EQ(345, p2(5));\n\n  auto p3 = partial(&add3, 4, 5, 6);\n  EXPECT_EQ(456, p3());\n}\n\nstruct Foo {\n  int method(int& x, int& y, int& z) { return 1000 + 100 * x + 10 * y + z; }\n  int constMethod(int const& x, int const& y, int const& z) const {\n    return 2000 + 100 * x + 10 * y + z;\n  }\n  int tempMethod(int&& x, int&& y, int&& z) {\n    return 3000 + 100 * x + 10 * y + z;\n  }\n};\n\nTEST(Partial, ReferenceArguments) {\n  auto p0 = partial(&Foo::method, Foo{}, 2, 3);\n  int four = 4;\n  EXPECT_EQ(1234, p0(four));\n\n  auto const p1 = partial(&Foo::constMethod, Foo{}, 3, 4);\n  EXPECT_EQ(2345, p1(5));\n\n  auto p2 = partial(&Foo::tempMethod, Foo{}, 4, 5);\n  EXPECT_EQ(3456, std::move(p2)(6));\n}\n\nstruct RefQualifiers {\n  int operator()(int x, int y, int z) & { return 1000 + 100 * x + 10 * y + z; }\n  int operator()(int x, int y, int z) const& {\n    return 2000 + 100 * x + 10 * y + z;\n  }\n  int operator()(int x, int y, int z) && { return 3000 + 100 * x + 10 * y + z; }\n};\n\nTEST(Partial, RefQualifiers) {\n  auto p = partial(RefQualifiers{});\n  auto const& pconst = p;\n\n  EXPECT_EQ(1234, p(2, 3, 4));\n  EXPECT_EQ(2345, pconst(3, 4, 5));\n  EXPECT_EQ(3456, std::move(p)(4, 5, 6));\n}\n\nstruct RefQualifiers2 {\n  int operator()(int& x, int const& y, int z) & {\n    return 1000 + 100 * x + 10 * y + z;\n  }\n  int operator()(int const& x, int y, int z) const& {\n    return 2000 + 100 * x + 10 * y + z;\n  }\n  int operator()(int&& x, int const& y, int z) && {\n    return 3000 + 100 * x + 10 * y + z;\n  }\n};\n\nTEST(Partial, RefQualifiers2) {\n  auto p = partial(RefQualifiers2{}, 9, 8);\n  auto const& pconst = p;\n\n  EXPECT_EQ(1984, p(4));\n  EXPECT_EQ(2985, pconst(5));\n  EXPECT_EQ(3986, std::move(p)(6));\n}\n\nstd::unique_ptr<int> calc_uptr(std::unique_ptr<int> x, std::unique_ptr<int> y) {\n  *x = 100 * *x + 10 * *y;\n  return x;\n}\n\nTEST(Partial, MoveOnly) {\n  auto five = std::make_unique<int>(5);\n  auto six = std::make_unique<int>(6);\n\n  // create a partial object which holds a pointer to the `calc_uptr` function\n  // and a `unique_ptr<int>` for the first argument\n  auto p = partial(&calc_uptr, std::move(five));\n\n  // `five` should be moved out of\n  EXPECT_FALSE(five);\n\n  // call to the partial object as rvalue, which allows the call to consume\n  // captured data (here: the `unique_ptr<int>` storing 5), and pass it\n  // the other `unique_ptr`\n  auto result = std::move(p)(std::move(six));\n\n  // ...which now should be moved out of\n  EXPECT_FALSE(six);\n\n  EXPECT_EQ(560, *result);\n}\n\nTEST(Partial, WrapInStdFunction) {\n  auto p1 = partial(&add3, 2);\n  std::function<int(int, int)> func = p1;\n  EXPECT_EQ(234, func(3, 4));\n}\n\nTEST(Partial, WrapInFollyFunction) {\n  auto p1 = partial(&add3, 2);\n  folly::Function<int(int, int)> func = p1;\n  EXPECT_EQ(234, func(3, 4));\n}\n"
  },
  {
    "path": "folly/functional/test/protocol_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/functional/protocol.h>\n\n#include <folly/portability/GTest.h>\n\nstruct ProtocolTest : testing::Test {\n  template <typename F>\n  static constexpr bool empty_function(F const& f) {\n    return folly::match_empty_function_protocol(f);\n  }\n\n  template <typename F>\n  static constexpr bool static_lambda_v =\n      folly::match_static_lambda_protocol_v<F>;\n\n  template <typename F, typename S>\n  static constexpr bool safely_invocable_v =\n      folly::match_safely_invocable_as_protocol_v<F, S>;\n};\n\nTEST_F(ProtocolTest, match_empty_function_protocol) {\n  constexpr auto lambda = [] {};\n  struct nada {\n    void go() {}\n  };\n\n  using sig = void();\n  using mfnp = void (nada::*)();\n\n  EXPECT_FALSE((std::bool_constant<empty_function(lambda)>{}));\n\n  EXPECT_TRUE(empty_function(static_cast<sig*>(nullptr)));\n  EXPECT_FALSE(empty_function(static_cast<sig*>(lambda)));\n\n  EXPECT_TRUE(empty_function(static_cast<mfnp>(nullptr)));\n  EXPECT_FALSE(empty_function(static_cast<mfnp>(&nada::go)));\n\n  EXPECT_TRUE(empty_function(std::function<sig>{}));\n  EXPECT_TRUE(empty_function(std::function<sig>{nullptr}));\n  EXPECT_FALSE(empty_function(std::function<sig>{lambda}));\n}\n\nTEST_F(ProtocolTest, match_static_lambda_protocol) {\n  struct not_empty {\n    int dummy;\n  };\n  EXPECT_FALSE(static_lambda_v<not_empty>);\n\n  struct not_trivially_copyable {\n    not_trivially_copyable(not_trivially_copyable const&);\n    not_trivially_copyable& operator=(not_trivially_copyable const&);\n  };\n  EXPECT_FALSE(static_lambda_v<not_trivially_copyable>);\n\n  struct empty_trivially_copyable {\n    empty_trivially_copyable() = delete;\n  };\n  EXPECT_TRUE(static_lambda_v<empty_trivially_copyable>);\n\n  auto lambda = [] {};\n  EXPECT_TRUE(static_lambda_v<decltype(lambda)>);\n\n  EXPECT_TRUE(static_lambda_v<std::default_delete<int>>);\n  EXPECT_TRUE(static_lambda_v<std::less<int>>);\n  EXPECT_TRUE(static_lambda_v<std::greater<int>>);\n  EXPECT_TRUE(static_lambda_v<std::equal_to<int>>);\n  EXPECT_TRUE(static_lambda_v<std::hash<int>>);\n}\n\nTEST_F(ProtocolTest, match_safely_invocable_as_protocol) {\n  // non-const non-noexcept invocation\n  {\n    struct fun {\n      [[maybe_unused]] void operator()();\n    };\n    EXPECT_TRUE((safely_invocable_v<fun, void()>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() const>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() noexcept>));\n  }\n\n  // non-const noexcept-invocation\n  {\n    struct fun {\n      [[maybe_unused]] void operator()() noexcept;\n    };\n    EXPECT_TRUE((safely_invocable_v<fun, void()>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() const>));\n    EXPECT_TRUE((safely_invocable_v<fun, void() noexcept>));\n  }\n\n  // const non-noexcept invocation\n  {\n    struct fun {\n      [[maybe_unused]] void operator()() const;\n    };\n    EXPECT_TRUE((safely_invocable_v<fun, void()>));\n    EXPECT_TRUE((safely_invocable_v<fun, void() const>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() noexcept>));\n  }\n\n  // return-type conversions\n  {\n    struct foo {};\n    struct bar : foo {};\n    struct wiz_x {\n      /* implicit */ [[maybe_unused]] wiz_x(bar);\n    };\n    struct wiz_nx {\n      /* implicit */ [[maybe_unused]] wiz_nx(bar) noexcept;\n    };\n    struct fun {\n      [[maybe_unused]] bar operator()() noexcept;\n    };\n    EXPECT_TRUE((safely_invocable_v<fun, bar()>));\n    EXPECT_FALSE((safely_invocable_v<fun, foo()>)); // slicing\n    EXPECT_TRUE((safely_invocable_v<fun, void()>));\n    EXPECT_FALSE((safely_invocable_v<fun, bar const&()>)); // dangling-ref\n    EXPECT_FALSE((safely_invocable_v<fun, foo const&()>)); // dangling-ref\n    EXPECT_TRUE((safely_invocable_v<fun, wiz_x()>));\n    EXPECT_TRUE((safely_invocable_v<fun, wiz_nx()>));\n    EXPECT_FALSE((safely_invocable_v<fun, wiz_x() noexcept>));\n    EXPECT_TRUE((safely_invocable_v<fun, wiz_nx() noexcept>));\n  }\n\n  // return-type ref conversions\n  {\n    struct foo {};\n    struct bar : foo {};\n    struct fun {\n      [[maybe_unused]] bar&& operator()() noexcept;\n    };\n    using bar_rr = bar&&; // workaround for clang-format fail case\n    using foo_rr = foo&&; // workaround for clang-format fail case\n    EXPECT_TRUE((safely_invocable_v<fun, bar()>));\n    EXPECT_TRUE((safely_invocable_v<fun, bar_rr()>));\n    EXPECT_TRUE((safely_invocable_v<fun, bar const&()>));\n    EXPECT_FALSE((safely_invocable_v<fun, foo()>)); // slicing\n    EXPECT_TRUE((safely_invocable_v<fun, foo_rr()>)); // maybe not slicing\n    EXPECT_TRUE((safely_invocable_v<fun, foo const&()>)); // maybe not slicing\n    EXPECT_TRUE((safely_invocable_v<fun, void()>));\n  }\n\n  // cvref compatibility\n  {\n    struct fun {\n      [[maybe_unused]] void operator()() const&&;\n    };\n    EXPECT_FALSE((safely_invocable_v<fun, void()>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() const>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() volatile>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() const volatile>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() &>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() const&>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() volatile&>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() const volatile&>));\n    EXPECT_TRUE((safely_invocable_v<fun, void() &&>));\n    EXPECT_TRUE((safely_invocable_v<fun, void() const&&>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() volatile&&>));\n    EXPECT_FALSE((safely_invocable_v<fun, void() const volatile&&>));\n  }\n}\n"
  },
  {
    "path": "folly/functional/test/traits_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/functional/traits.h>\n\n#include <folly/portability/GTest.h>\n\nstruct TraitsTest : testing::Test {\n  template <typename... T>\n  static constexpr bool same_v = std::is_same<T...>::value;\n\n  template <typename S>\n  using traits = folly::function_traits<S>;\n\n  template <typename S>\n  using result_t = typename traits<S>::result;\n\n  template <typename R, typename S>\n  static constexpr bool is_result_v = std::is_same<R, result_t<S>>::value;\n\n  template <typename S>\n  static constexpr bool is_nothrow_v = traits<S>::is_nothrow;\n\n  template <size_t I, typename S>\n  using argument_type_t = typename traits<S>::template argument<I>;\n\n  template <typename R, size_t I, typename S>\n  static constexpr bool is_arg_type_v =\n      std::is_same<R, argument_type_t<I, S>>::value;\n\n  template <typename S>\n  static constexpr size_t args_size_v =\n      traits<S>::template arguments<folly::type_pack_size_t>::value;\n\n  template <typename S>\n  static constexpr bool is_variadic_v = traits<S>::is_variadic;\n\n  template <typename T, typename S>\n  using cvref_t = typename traits<S>::template value_like<T>;\n\n  template <typename D, typename T, typename S>\n  static constexpr bool is_cvref_t = std::is_same<D, cvref_t<T, S>>::value;\n\n  template <typename F>\n  using rem_cvref_t = folly::function_remove_cvref_t<F>;\n\n  template <typename D, typename S, typename I>\n  static constexpr bool is_like_v =\n      same_v<D, folly::function_like_value_t<S, I>>;\n};\n\nTEST_F(TraitsTest, function_traits) {\n  using vc = void const;\n\n  //  result_type\n\n  EXPECT_TRUE((is_result_v<vc, vc()>));\n  EXPECT_TRUE((is_result_v<vc, vc() const>));\n  EXPECT_TRUE((is_result_v<vc, vc() volatile>));\n  EXPECT_TRUE((is_result_v<vc, vc() const volatile>));\n  EXPECT_TRUE((is_result_v<vc, vc() &>));\n  EXPECT_TRUE((is_result_v<vc, vc() const&>));\n  EXPECT_TRUE((is_result_v<vc, vc() volatile&>));\n  EXPECT_TRUE((is_result_v<vc, vc() const volatile&>));\n  EXPECT_TRUE((is_result_v<vc, vc() &&>));\n  EXPECT_TRUE((is_result_v<vc, vc() const&&>));\n  EXPECT_TRUE((is_result_v<vc, vc() volatile&&>));\n  EXPECT_TRUE((is_result_v<vc, vc() const volatile&&>));\n  EXPECT_TRUE((is_result_v<vc, vc(...)>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) volatile>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const volatile>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) &>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const&>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) volatile&>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const volatile&>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) &&>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const&&>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) volatile&&>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const volatile&&>));\n  EXPECT_TRUE((is_result_v<vc, vc() noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc() const noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc() volatile noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc() const volatile noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc() & noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc() const & noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc() volatile & noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc() const volatile & noexcept>));\n  EXPECT_TRUE((is_result_v < vc, vc() && noexcept >));\n  EXPECT_TRUE((is_result_v < vc, vc() const&& noexcept >));\n  EXPECT_TRUE((is_result_v < vc, vc() volatile && noexcept >));\n  EXPECT_TRUE((is_result_v < vc, vc() const volatile&& noexcept >));\n  EXPECT_TRUE((is_result_v<vc, vc(...) noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) volatile noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const volatile noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) & noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const & noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) volatile & noexcept>));\n  EXPECT_TRUE((is_result_v<vc, vc(...) const volatile & noexcept>));\n  EXPECT_TRUE((is_result_v < vc, vc(...) && noexcept >));\n  EXPECT_TRUE((is_result_v < vc, vc(...) const&& noexcept >));\n  EXPECT_TRUE((is_result_v < vc, vc(...) volatile && noexcept >));\n  EXPECT_TRUE((is_result_v < vc, vc(...) const volatile&& noexcept >));\n\n  //  is_nothrow\n\n  EXPECT_FALSE((is_nothrow_v<void()>));\n  EXPECT_FALSE((is_nothrow_v<void() const>));\n  EXPECT_FALSE((is_nothrow_v<void() volatile>));\n  EXPECT_FALSE((is_nothrow_v<void() const volatile>));\n  EXPECT_FALSE((is_nothrow_v<void() &>));\n  EXPECT_FALSE((is_nothrow_v<void() const&>));\n  EXPECT_FALSE((is_nothrow_v<void() volatile&>));\n  EXPECT_FALSE((is_nothrow_v<void() const volatile&>));\n  EXPECT_FALSE((is_nothrow_v<void() &&>));\n  EXPECT_FALSE((is_nothrow_v<void() const&&>));\n  EXPECT_FALSE((is_nothrow_v<void() volatile&&>));\n  EXPECT_FALSE((is_nothrow_v<void() const volatile&&>));\n  EXPECT_FALSE((is_nothrow_v<void(...)>));\n  EXPECT_FALSE((is_nothrow_v<void(...) const>));\n  EXPECT_FALSE((is_nothrow_v<void(...) volatile>));\n  EXPECT_FALSE((is_nothrow_v<void(...) const volatile>));\n  EXPECT_FALSE((is_nothrow_v<void(...) &>));\n  EXPECT_FALSE((is_nothrow_v<void(...) const&>));\n  EXPECT_FALSE((is_nothrow_v<void(...) volatile&>));\n  EXPECT_FALSE((is_nothrow_v<void(...) const volatile&>));\n  EXPECT_FALSE((is_nothrow_v<void(...) &&>));\n  EXPECT_FALSE((is_nothrow_v<void(...) const&&>));\n  EXPECT_FALSE((is_nothrow_v<void(...) volatile&&>));\n  EXPECT_FALSE((is_nothrow_v<void(...) const volatile&&>));\n  EXPECT_TRUE((is_nothrow_v<void() noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void() const noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void() volatile noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void() const volatile noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void() & noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void() const & noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void() volatile & noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void() const volatile & noexcept>));\n  EXPECT_TRUE((is_nothrow_v < void() && noexcept >));\n  EXPECT_TRUE((is_nothrow_v < void() const&& noexcept >));\n  EXPECT_TRUE((is_nothrow_v < void() volatile && noexcept >));\n  EXPECT_TRUE((is_nothrow_v < void() const volatile&& noexcept >));\n  EXPECT_TRUE((is_nothrow_v<void(...) noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void(...) const noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void(...) volatile noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void(...) const volatile noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void(...) & noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void(...) const & noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void(...) volatile & noexcept>));\n  EXPECT_TRUE((is_nothrow_v<void(...) const volatile & noexcept>));\n  EXPECT_TRUE((is_nothrow_v < void(...) && noexcept >));\n  EXPECT_TRUE((is_nothrow_v < void(...) const&& noexcept >));\n  EXPECT_TRUE((is_nothrow_v < void(...) volatile && noexcept >));\n  EXPECT_TRUE((is_nothrow_v < void(...) const volatile&& noexcept >));\n\n  //  variadic\n\n  EXPECT_FALSE((is_variadic_v<void()>));\n  EXPECT_FALSE((is_variadic_v<void() const>));\n  EXPECT_FALSE((is_variadic_v<void() volatile>));\n  EXPECT_FALSE((is_variadic_v<void() const volatile>));\n  EXPECT_FALSE((is_variadic_v<void() &>));\n  EXPECT_FALSE((is_variadic_v<void() const&>));\n  EXPECT_FALSE((is_variadic_v<void() volatile&>));\n  EXPECT_FALSE((is_variadic_v<void() const volatile&>));\n  EXPECT_FALSE((is_variadic_v<void() &&>));\n  EXPECT_FALSE((is_variadic_v<void() const&&>));\n  EXPECT_FALSE((is_variadic_v<void() volatile&&>));\n  EXPECT_FALSE((is_variadic_v<void() const volatile&&>));\n  EXPECT_TRUE((is_variadic_v<void(...)>));\n  EXPECT_TRUE((is_variadic_v<void(...) const>));\n  EXPECT_TRUE((is_variadic_v<void(...) volatile>));\n  EXPECT_TRUE((is_variadic_v<void(...) const volatile>));\n  EXPECT_TRUE((is_variadic_v<void(...) &>));\n  EXPECT_TRUE((is_variadic_v<void(...) const&>));\n  EXPECT_TRUE((is_variadic_v<void(...) volatile&>));\n  EXPECT_TRUE((is_variadic_v<void(...) const volatile&>));\n  EXPECT_TRUE((is_variadic_v<void(...) &&>));\n  EXPECT_TRUE((is_variadic_v<void(...) const&&>));\n  EXPECT_TRUE((is_variadic_v<void(...) volatile&&>));\n  EXPECT_TRUE((is_variadic_v<void(...) const volatile&&>));\n  EXPECT_FALSE((is_variadic_v<void() noexcept>));\n  EXPECT_FALSE((is_variadic_v<void() const noexcept>));\n  EXPECT_FALSE((is_variadic_v<void() volatile noexcept>));\n  EXPECT_FALSE((is_variadic_v<void() const volatile noexcept>));\n  EXPECT_FALSE((is_variadic_v<void() & noexcept>));\n  EXPECT_FALSE((is_variadic_v<void() const & noexcept>));\n  EXPECT_FALSE((is_variadic_v<void() volatile & noexcept>));\n  EXPECT_FALSE((is_variadic_v<void() const volatile & noexcept>));\n  EXPECT_FALSE((is_variadic_v < void() && noexcept >));\n  EXPECT_FALSE((is_variadic_v < void() const&& noexcept >));\n  EXPECT_FALSE((is_variadic_v < void() volatile && noexcept >));\n  EXPECT_FALSE((is_variadic_v < void() const volatile&& noexcept >));\n  EXPECT_TRUE((is_variadic_v<void(...) noexcept>));\n  EXPECT_TRUE((is_variadic_v<void(...) const noexcept>));\n  EXPECT_TRUE((is_variadic_v<void(...) volatile noexcept>));\n  EXPECT_TRUE((is_variadic_v<void(...) const volatile noexcept>));\n  EXPECT_TRUE((is_variadic_v<void(...) & noexcept>));\n  EXPECT_TRUE((is_variadic_v<void(...) const & noexcept>));\n  EXPECT_TRUE((is_variadic_v<void(...) volatile & noexcept>));\n  EXPECT_TRUE((is_variadic_v<void(...) const volatile & noexcept>));\n  EXPECT_TRUE((is_variadic_v < void(...) && noexcept >));\n  EXPECT_TRUE((is_variadic_v < void(...) const&& noexcept >));\n  EXPECT_TRUE((is_variadic_v < void(...) volatile && noexcept >));\n  EXPECT_TRUE((is_variadic_v < void(...) const volatile&& noexcept >));\n\n  //  argument\n\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*)>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) &>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) &&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const&&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile&&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile&&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...)>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) &>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) &&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const&&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile&&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile&&>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const volatile noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) & noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) const & noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*) volatile & noexcept>));\n  EXPECT_TRUE(\n      (is_arg_type_v<vc*, 1, void(int, vc*) const volatile & noexcept>));\n  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*) && noexcept >));\n  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*) const&& noexcept >));\n  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*) volatile && noexcept >));\n  EXPECT_TRUE(\n      ( //\n          is_arg_type_v < vc*,\n          1,\n          void(int, vc*) const volatile&& noexcept >));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile noexcept>));\n  EXPECT_TRUE(( //\n      is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) & noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) const & noexcept>));\n  EXPECT_TRUE((is_arg_type_v<vc*, 1, void(int, vc*, ...) volatile & noexcept>));\n  EXPECT_TRUE(( //\n      is_arg_type_v<vc*, 1, void(int, vc*, ...) const volatile & noexcept>));\n  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*, ...) && noexcept >));\n  EXPECT_TRUE((is_arg_type_v < vc*, 1, void(int, vc*, ...) const&& noexcept >));\n  EXPECT_TRUE(\n      (is_arg_type_v < vc*, 1, void(int, vc*, ...) volatile && noexcept >));\n  EXPECT_TRUE(\n      ( //\n          is_arg_type_v < vc*,\n          1,\n          void(int, vc*, ...) const volatile&& noexcept >));\n\n  //  arguments\n\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float)>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) &>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) &&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const&&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile&&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile&&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...)>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const volatile>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) &>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const volatile&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) &&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const&&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile&&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const volatile&&>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) & noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const & noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) volatile & noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float) const volatile & noexcept>));\n  EXPECT_EQ(3, (args_size_v < void(int, vc*, float) && noexcept >));\n  EXPECT_EQ(3, (args_size_v < void(int, vc*, float) const&& noexcept >));\n  EXPECT_EQ(3, (args_size_v < void(int, vc*, float) volatile && noexcept >));\n  EXPECT_EQ(\n      3, (args_size_v < void(int, vc*, float) const volatile&& noexcept >));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile noexcept>));\n  EXPECT_EQ(\n      3, (args_size_v<void(int, vc*, float, ...) const volatile noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) & noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) const & noexcept>));\n  EXPECT_EQ(3, (args_size_v<void(int, vc*, float, ...) volatile & noexcept>));\n  EXPECT_EQ(\n      3, (args_size_v<void(int, vc*, float, ...) const volatile & noexcept>));\n  EXPECT_EQ(3, (args_size_v < void(int, vc*, float, ...) && noexcept >));\n  EXPECT_EQ(3, (args_size_v < void(int, vc*, float, ...) const&& noexcept >));\n  EXPECT_EQ(\n      3, (args_size_v < void(int, vc*, float, ...) volatile && noexcept >));\n  EXPECT_EQ(\n      3,\n      (args_size_v < void(int, vc*, float, ...) const volatile&& noexcept >));\n\n  //  value_like\n\n  EXPECT_TRUE((is_cvref_t<int, int, void()>));\n  EXPECT_TRUE((is_cvref_t<int const, int, void() const>));\n  EXPECT_TRUE((is_cvref_t<int volatile, int, void() volatile>));\n  EXPECT_TRUE((is_cvref_t<int const volatile, int, void() const volatile>));\n  EXPECT_TRUE((is_cvref_t<int&, int, void() &>));\n  EXPECT_TRUE((is_cvref_t<int const&, int, void() const&>));\n  EXPECT_TRUE((is_cvref_t<int volatile&, int, void() volatile&>));\n  EXPECT_TRUE((is_cvref_t<int const volatile&, int, void() const volatile&>));\n  EXPECT_TRUE((is_cvref_t<int&&, int, void() &&>));\n  EXPECT_TRUE((is_cvref_t<int const&&, int, void() const&&>));\n  EXPECT_TRUE((is_cvref_t<int volatile&&, int, void() volatile&&>));\n  EXPECT_TRUE((is_cvref_t<int const volatile&&, int, void() const volatile&&>));\n  EXPECT_TRUE((is_cvref_t<int, int, void(...)>));\n  EXPECT_TRUE((is_cvref_t<int const, int, void(...) const>));\n  EXPECT_TRUE((is_cvref_t<int volatile, int, void(...) volatile>));\n  EXPECT_TRUE((is_cvref_t<int const volatile, int, void(...) const volatile>));\n  EXPECT_TRUE((is_cvref_t<int&, int, void(...) &>));\n  EXPECT_TRUE((is_cvref_t<int const&, int, void(...) const&>));\n  EXPECT_TRUE((is_cvref_t<int volatile&, int, void(...) volatile&>));\n  EXPECT_TRUE(( //\n      is_cvref_t<int const volatile&, int, void(...) const volatile&>));\n  EXPECT_TRUE((is_cvref_t<int&&, int, void(...) &&>));\n  EXPECT_TRUE((is_cvref_t<int const&&, int, void(...) const&&>));\n  EXPECT_TRUE((is_cvref_t<int volatile&&, int, void(...) volatile&&>));\n  EXPECT_TRUE(( //\n      is_cvref_t<int const volatile&&, int, void(...) const volatile&&>));\n  EXPECT_TRUE((is_cvref_t<int, int, void() noexcept>));\n  EXPECT_TRUE((is_cvref_t<int const, int, void() const noexcept>));\n  EXPECT_TRUE((is_cvref_t<int volatile, int, void() volatile noexcept>));\n  EXPECT_TRUE(( //\n      is_cvref_t<int const volatile, int, void() const volatile noexcept>));\n  EXPECT_TRUE((is_cvref_t<int&, int, void() & noexcept>));\n  EXPECT_TRUE((is_cvref_t<int const&, int, void() const & noexcept>));\n  EXPECT_TRUE((is_cvref_t<int volatile&, int, void() volatile & noexcept>));\n  EXPECT_TRUE(( //\n      is_cvref_t<int const volatile&, int, void() const volatile & noexcept>));\n  EXPECT_TRUE((is_cvref_t < int&&, int, void() && noexcept >));\n  EXPECT_TRUE((is_cvref_t < int const&&, int, void() const&& noexcept >));\n  EXPECT_TRUE((is_cvref_t < int volatile&&, int, void() volatile&& noexcept >));\n  EXPECT_TRUE((\n      is_cvref_t < int const volatile&&,\n      int,\n      void() const volatile&& noexcept >));\n  EXPECT_TRUE((is_cvref_t<int, int, void(...) noexcept>));\n  EXPECT_TRUE((is_cvref_t<int const, int, void(...) const noexcept>));\n  EXPECT_TRUE((is_cvref_t<int volatile, int, void(...) volatile noexcept>));\n  EXPECT_TRUE(( //\n      is_cvref_t<int const volatile, int, void(...) const volatile noexcept>));\n  EXPECT_TRUE((is_cvref_t<int&, int, void(...) & noexcept>));\n  EXPECT_TRUE((is_cvref_t<int const&, int, void(...) const & noexcept>));\n  EXPECT_TRUE((is_cvref_t<int volatile&, int, void(...) volatile & noexcept>));\n  EXPECT_TRUE(( //\n      is_cvref_t<\n          int const volatile&,\n          int,\n          void(...) const volatile & noexcept>));\n  EXPECT_TRUE((is_cvref_t < int&&, int, void(...) && noexcept >));\n  EXPECT_TRUE((is_cvref_t < int const&&, int, void(...) const&& noexcept >));\n  EXPECT_TRUE(\n      (is_cvref_t < int volatile&&, int, void(...) volatile&& noexcept >));\n  EXPECT_TRUE(\n      ( //\n          is_cvref_t < int const volatile&&,\n          int,\n          void(...) const volatile&& noexcept >));\n}\n\nTEST_F(TraitsTest, function_remove_cvref) {\n  using f = char(int*);\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*)>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) volatile>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const volatile>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) &>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const&>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) volatile&>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const volatile&>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) &&>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const&&>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) volatile&&>>));\n  EXPECT_TRUE((same_v<f, rem_cvref_t<char(int*) const volatile&&>>));\n\n  using fv = char(int*, ...);\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...)>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) volatile>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const volatile>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) &>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const&>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) volatile&>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const volatile&>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) &&>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const&&>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) volatile&&>>));\n  EXPECT_TRUE((same_v<fv, rem_cvref_t<char(int*, ...) const volatile&&>>));\n\n  using g = char(int*) noexcept;\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) volatile noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const volatile noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) & noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const & noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) volatile & noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const volatile & noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) && noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const && noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) volatile && noexcept>>));\n  EXPECT_TRUE((same_v<g, rem_cvref_t<char(int*) const volatile && noexcept>>));\n\n  using gv = char(int*, ...) noexcept;\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) const noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) volatile noexcept>>));\n  EXPECT_TRUE(( //\n      same_v<gv, rem_cvref_t<char(int*, ...) const volatile noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) & noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) const & noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) volatile & noexcept>>));\n  EXPECT_TRUE(( //\n      same_v<gv, rem_cvref_t<char(int*, ...) const volatile & noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) && noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) const && noexcept>>));\n  EXPECT_TRUE((same_v<gv, rem_cvref_t<char(int*, ...) volatile && noexcept>>));\n  EXPECT_TRUE(( //\n      same_v<gv, rem_cvref_t<char(int*, ...) const volatile && noexcept>>));\n}\n\nTEST_F(TraitsTest, function_like_value) {\n  using f = char(int*);\n  EXPECT_TRUE((is_like_v<char(int*), int, f>));\n  EXPECT_TRUE((is_like_v<char(int*) const, int const, f>));\n  EXPECT_TRUE((is_like_v<char(int*) volatile, int volatile, f>));\n  EXPECT_TRUE((is_like_v<char(int*) const volatile, int const volatile, f>));\n  EXPECT_TRUE((is_like_v<char(int*) &, int&, f>));\n  EXPECT_TRUE((is_like_v<char(int*) const&, int const&, f>));\n  EXPECT_TRUE((is_like_v<char(int*) volatile&, int volatile&, f>));\n  EXPECT_TRUE((is_like_v<char(int*) const volatile&, int const volatile&, f>));\n  EXPECT_TRUE((is_like_v<char(int*) &&, int&&, f>));\n  EXPECT_TRUE((is_like_v<char(int*) const&&, int const&&, f>));\n  EXPECT_TRUE((is_like_v<char(int*) volatile&&, int volatile&&, f>));\n  EXPECT_TRUE(( //\n      is_like_v<char(int*) const volatile&&, int const volatile&&, f>));\n\n  using fv = char(int*, ...);\n  EXPECT_TRUE((is_like_v<char(int*, ...), int, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) const, int const, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) volatile, int volatile, fv>));\n  EXPECT_TRUE(( //\n      is_like_v<char(int*, ...) const volatile, int const volatile, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) &, int&, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) const&, int const&, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) volatile&, int volatile&, fv>));\n  EXPECT_TRUE(( //\n      is_like_v<char(int*, ...) const volatile&, int const volatile&, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) &&, int&&, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) const&&, int const&&, fv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) volatile&&, int volatile&&, fv>));\n  EXPECT_TRUE(( //\n      is_like_v<char(int*, ...) const volatile&&, int const volatile&&, fv>));\n\n  using g = char(int*) noexcept;\n  EXPECT_TRUE((is_like_v<char(int*) noexcept, int, g>));\n  EXPECT_TRUE((is_like_v<char(int*) const noexcept, int const, g>));\n  EXPECT_TRUE((is_like_v<char(int*) volatile noexcept, int volatile, g>));\n  EXPECT_TRUE(( //\n      is_like_v<char(int*) const volatile noexcept, int const volatile, g>));\n  EXPECT_TRUE((is_like_v<char(int*) & noexcept, int&, g>));\n  EXPECT_TRUE((is_like_v<char(int*) const & noexcept, int const&, g>));\n  EXPECT_TRUE((is_like_v<char(int*) volatile & noexcept, int volatile&, g>));\n  EXPECT_TRUE(( //\n      is_like_v<char(int*) const volatile & noexcept, int const volatile&, g>));\n  EXPECT_TRUE((is_like_v < char(int*) && noexcept, int&&, g >));\n  EXPECT_TRUE((is_like_v < char(int*) const&& noexcept, int const&&, g >));\n  EXPECT_TRUE(\n      (is_like_v < char(int*) volatile && noexcept, int volatile&&, g >));\n  EXPECT_TRUE(\n      ( //\n          is_like_v < char(int*) const volatile&& noexcept,\n          int const volatile&&,\n          g >));\n\n  using gv = char(int*, ...) noexcept;\n  EXPECT_TRUE((is_like_v<char(int*, ...) noexcept, int, gv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) const noexcept, int const, gv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) volatile noexcept, int volatile, gv>));\n  EXPECT_TRUE(( //\n      is_like_v<\n          char(int*, ...) const volatile noexcept,\n          int const volatile,\n          gv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) & noexcept, int&, gv>));\n  EXPECT_TRUE((is_like_v<char(int*, ...) const & noexcept, int const&, gv>));\n  EXPECT_TRUE(( //\n      is_like_v<char(int*, ...) volatile & noexcept, int volatile&, gv>));\n  EXPECT_TRUE(( //\n      is_like_v<\n          char(int*, ...) const volatile & noexcept,\n          int const volatile&,\n          gv>));\n  EXPECT_TRUE((is_like_v < char(int*, ...) && noexcept, int&&, gv >));\n  EXPECT_TRUE(\n      (is_like_v < char(int*, ...) const&& noexcept, int const&&, gv >));\n  EXPECT_TRUE(\n      ( //\n          is_like_v < char(int*, ...) volatile && noexcept,\n          int volatile&&,\n          gv >));\n  EXPECT_TRUE(\n      ( //\n          is_like_v < char(int*, ...) const volatile&& noexcept,\n          int const volatile&&,\n          gv >));\n}\n"
  },
  {
    "path": "folly/functional/traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Traits.h>\n\nnamespace folly {\n\nnamespace detail {\n\ntemplate <typename>\nstruct function_traits_base_;\n\ntemplate <typename R, typename... A>\nstruct function_traits_base_<R(A...)> {\n  using result = R;\n\n  template <std::size_t Idx>\n  using argument = type_pack_element_t<Idx, A...>;\n\n  template <template <typename...> class F>\n  using arguments = F<A...>;\n};\n\ntemplate <bool Nx>\nstruct function_traits_nx_ {\n  static constexpr bool is_nothrow = Nx;\n};\n\ntemplate <bool Var>\nstruct function_traits_var_ {\n  static constexpr bool is_variadic = Var;\n};\n\ntemplate <typename T>\nstruct function_traits_cvref_ {\n  template <typename D>\n  using value_like = copy_cvref_t<T, D>;\n};\n\n} // namespace detail\n\n//  function_traits\n//\n//  Incomplete except when instantiated over any type matching std::is_function.\n//  Namely, types S over R, A..., NX of the form:\n//\n//      S = R(A...) [const] [volatile] (|&|&&) noexcept(NX)\n//\n//  When complete, has a class body of the form:\n//\n//      struct function_traits<S> {\n//        using result = R;\n//        static constexpr bool is_nothrow = NX;\n//\n//        template <std::size_t Index>\n//        using argument = type_pack_element_t<Index, A...>;\n//        template <typename F>\n//        using arguments = F<A...>;\n//        template <typename Model>\n//        using value_like = Model [const] [volatile] (|&|&&);\n//      };\n//\n//  Member argument is a metafunction allowing access to one argument type at a\n//  time, by positional index:\n//\n//      using second_argument_type = function_traits<S>::argument<1>;\n//\n//  Member arguments is a metafunction allowing access to all argument types at\n//  once:\n//\n//      using arguments_tuple_type =\n//          function_traits<S>::arguments<std::tuple>;\n//\n//  Member value_like is a metafunction allowing access to the const-,\n//  volatile-, and reference-qualifiers using copy_cvref_t to transport all\n//  these qualifiers to a destination type which may then be queried:\n//\n//      constexpr bool is_rvalue_reference = std::is_rvalue_reverence_v<\n//          function_traits<S>::value_like<int>>;\n//\n//  Keep in mind that member types or type-aliases must be referenced with\n//  keyword typename when dependent and that member templates must likewise be\n//  referenced with keyword template when in dependent:\n//\n//      template <typename... A>\n//      using get_size = index_constant<sizeof...(A);\n//      template <typename S>\n//      using arguments_size_t =\n//          typename function_traits<S>::template arguments<get_size>;\n//\n//  Every fact of a function type S is thus discoverable from function_traits<S>\n//  without requiring further class template specializations or further overload\n//  set searches, all as types or constexpr values.\n//\n//  Further specializations are forbidden.\ntemplate <typename>\nstruct function_traits;\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...)> //\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) volatile>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const volatile>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) &> //\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) volatile&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const volatile&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) &&> //\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const&&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) volatile&&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int volatile&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const volatile&&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const volatile&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...)> //\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) volatile>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const volatile>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) &> //\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) volatile&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const volatile&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) &&> //\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const&&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) volatile&&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int volatile&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const volatile&&>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<false>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const volatile&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) volatile noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const volatile noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) volatile & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const volatile & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) volatile && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int volatile&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A...) const volatile && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<false>,\n      detail::function_traits_cvref_<int const volatile&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) volatile noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const volatile noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const volatile> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) volatile & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const volatile & noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const volatile&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) volatile && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int volatile&&> {};\n\ntemplate <typename R, typename... A>\nstruct function_traits<R(A..., ...) const volatile && noexcept>\n    : detail::function_traits_base_<R(A...)>,\n      detail::function_traits_nx_<true>,\n      detail::function_traits_var_<true>,\n      detail::function_traits_cvref_<int const volatile&&> {};\n\n//  ----\n\n//  function_result_t\n//\n//  The result type of the given function type.\ntemplate <typename F>\nusing function_result_t = typename function_traits<F>::result;\n\n//  function_arguments_size_t\n//\n//  The size of the arguments list of the given function type, as an\n//  instantiation of integral_constant.\ntemplate <typename F>\nusing function_arguments_size_t =\n    typename function_traits<F>::template arguments<type_pack_size_t>;\n\n//  function_arguments_size_t\n//\n//  The size of the arguments list of the given function type.\ntemplate <typename F>\nconstexpr std::size_t function_arguments_size_v =\n    function_arguments_size_t<F>::value;\n\n//  function_arguments_element_t\n//\n//  The type of the argument at the given index of the given function type.\ntemplate <std::size_t Idx, typename F>\nusing function_arguments_element_t =\n    typename function_traits<F>::template argument<Idx>;\n\n//  function_is_nothrow_v\n//\n//  True precisely when the given function type is marked noexcept.\ntemplate <typename F>\nconstexpr bool function_is_nothrow_v = function_traits<F>::is_nothrow;\n\n//  function_is_variadic_v\n//\n//  True precisely when the given function type is variadic.\n//\n//  Note: C-style variadic, like in printf. Not C++-style variadic-template,\n//  since concrete function types cannot also be function type templates.\ntemplate <typename F>\nconstexpr bool function_is_variadic_v = function_traits<F>::is_variadic;\n\n//  ----\n\nnamespace detail {\n\ntemplate <bool Nx, bool Var, typename R>\nstruct function_remove_cvref_;\ntemplate <typename R>\nstruct function_remove_cvref_<false, false, R> {\n  template <typename... A>\n  using apply = R(A...);\n};\ntemplate <typename R>\nstruct function_remove_cvref_<false, true, R> {\n  template <typename... A>\n  using apply = R(A..., ...);\n};\ntemplate <typename R>\nstruct function_remove_cvref_<true, false, R> {\n  template <typename... A>\n  using apply = R(A...) noexcept;\n};\ntemplate <typename R>\nstruct function_remove_cvref_<true, true, R> {\n  template <typename... A>\n  using apply = R(A..., ...) noexcept;\n};\n\ntemplate <typename F, typename T = function_traits<F>>\nusing function_remove_cvref_t_ = typename T::template arguments<\n    function_remove_cvref_<T::is_nothrow, T::is_variadic, typename T::result>::\n        template apply>;\n\n} // namespace detail\n\n//  function_remove_cvref\n//  function_remove_cvref_t\n//\n//  Given a function type of the form:\n//      S = R(A...) [const] [volatile] (|&|&&) noexcept(NX)\n//  Yields another function type:\n//      R(A...) noexcept(NX)\n\ntemplate <typename F>\nusing function_remove_cvref_t = detail::function_remove_cvref_t_<F>;\n\ntemplate <typename F>\nstruct function_remove_cvref {\n  using type = function_remove_cvref_t<F>;\n};\n\n//  ----\n\nnamespace detail {\n\ntemplate <typename Src, bool Var>\nstruct function_like_src_;\ntemplate <typename Src>\nstruct function_like_src_<Src, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) const noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src volatile, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) volatile noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const volatile, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) const volatile noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) & noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) const& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src volatile&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) volatile& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const volatile&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) const volatile& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src&&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) && noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const&&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) const&& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src volatile&&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) volatile&& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const volatile&&, 0> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A...) const volatile&& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) const noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src volatile, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) volatile noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const volatile, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) const volatile noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) & noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) const& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src volatile&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) volatile& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const volatile&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) const volatile& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src&&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) && noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const&&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) const&& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src volatile&&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) volatile&& noexcept(Nx);\n};\ntemplate <typename Src>\nstruct function_like_src_<Src const volatile&&, 1> {\n  template <bool Nx, typename R, typename... A>\n  using apply = R(A..., ...) const volatile&& noexcept(Nx);\n};\n\ntemplate <typename Dst>\nstruct function_like_dst_ : function_like_dst_<function_remove_cvref_t<Dst>> {};\ntemplate <typename R, typename... A>\nstruct function_like_dst_<R(A...)> {\n  template <typename Src>\n  using apply = typename function_like_src_<Src, 0>::template apply<0, R, A...>;\n};\ntemplate <typename R, typename... A>\nstruct function_like_dst_<R(A..., ...)> {\n  template <typename Src>\n  using apply = typename function_like_src_<Src, 1>::template apply<0, R, A...>;\n};\ntemplate <typename R, typename... A>\nstruct function_like_dst_<R(A...) noexcept> {\n  template <typename Src>\n  using apply = typename function_like_src_<Src, 0>::template apply<1, R, A...>;\n};\ntemplate <typename R, typename... A>\nstruct function_like_dst_<R(A..., ...) noexcept> {\n  template <typename Src>\n  using apply = typename function_like_src_<Src, 1>::template apply<1, R, A...>;\n};\n\n} // namespace detail\n\n//  function_like_value\n//  function_like_value_t\n//\n//  Given a possibly-cvref-qualified value type Src and a possibly-cvref-\n//  qualified function type Dst,  transports any cvref-qualifications found on\n//  Src onto the base function type of Dst, which is Dst stripped of its own\n//  cvref-qualifications.\n//\n//  Example:\n//      function_like_value_t<int volatile, void() const&&> -> void() volatile\n//      function_like_value_t<int const&&, void() volatile> -> void() const&&\n\ntemplate <typename Src, typename Dst>\nusing function_like_value_t =\n    typename detail::function_like_dst_<Dst>::template apply<Src>;\n\ntemplate <typename Src, typename Dst>\nstruct function_like_value {\n  using type = function_like_value_t<Src, Dst>;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"barrier\",\n    srcs = [\"Barrier.cpp\"],\n    headers = [\"Barrier.h\"],\n    force_static = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:scope_guard\",\n        \"//folly/lang:new\",\n    ],\n    exported_deps = [\n        \":core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"cleanup\",\n    headers = [\"Cleanup.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"core\",\n    srcs = [\n        \"Future.cpp\",\n        \"HeapTimekeeper.cpp\",\n        \"Promise.cpp\",\n        \"ThreadWheelTimekeeper.cpp\",\n    ],\n    headers = [\n        \"Future.h\",\n        \"Future-inl.h\",\n        \"Future-pre.h\",\n        \"HeapTimekeeper.h\",\n        \"Promise.h\",\n        \"Promise-inl.h\",\n        \"Retrying.h\",\n        \"ThreadWheelTimekeeper.h\",\n        \"WTCallback.h\",\n    ],\n    force_static = False,\n    labels = [\n        # This library should be used via :futures. Depslint\n        # doesn't support the concept of target-reexports (nor do we know the\n        # best design for such a feature), so we just exclude this target from\n        # being added.\n        \"depslint_never_add\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:likely\",\n        \"//folly:singleton\",\n        \"//folly/container:intrusive_heap\",\n        \"//folly/lang:safe_assert\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:distributed_mutex\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/synchronization:saturating_semaphore\",\n        \"//folly/synchronization:wait_options\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \":portability\",\n        \"//folly:chrono\",\n        \"//folly:optional\",\n        \"//folly:portability\",\n        \"//folly:random\",\n        \"//folly:scope_guard\",\n        \"//folly:traits\",\n        \"//folly:try\",\n        \"//folly:unit\",\n        \"//folly:utility\",\n        \"//folly/container:foreach\",\n        \"//folly/coro:traits\",\n        \"//folly/detail:async_trace\",\n        \"//folly/executors:drivable_executor\",\n        \"//folly/executors:executor_with_priority\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/executors:queued_immediate_executor\",\n        \"//folly/executors:timed_drivable_executor\",\n        \"//folly/fibers:core\",\n        \"//folly/functional:invoke\",\n        \"//folly/futures/detail:core\",\n        \"//folly/io/async:async_base\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:pretty\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"future_splitter\",\n    headers = [\"FutureSplitter.h\"],\n    force_static = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \":shared_promise\",\n        \"//folly/lang:exception\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"futures\",\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":barrier\",  # @manual\n        \":core\",  # @manual\n        \":future_splitter\",  # @manual\n        \":shared_promise\",  # @manual\n        \"//folly/executors:inline_executor\",  # @manual\n        \"//folly/executors:manual_executor\",  # @manual\n        \"//folly/executors:queued_immediate_executor\",  # @manual\n        \"//folly/executors:scheduled_executor\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"manual_timekeeper\",\n    srcs = [\"ManualTimekeeper.cpp\"],\n    headers = [\"ManualTimekeeper.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly/synchronization:atomic_util\",\n    ],\n    exported_deps = [\n        \"//folly:synchronized\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"portability\",\n    headers = [\"Portability.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"shared_promise\",\n    srcs = [\"SharedPromise.cpp\"],\n    headers = [\n        \"SharedPromise.h\",\n        \"SharedPromise-inl.h\",\n    ],\n    force_static = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly:portability\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/lang:exception\",\n    ],\n)\n"
  },
  {
    "path": "folly/futures/Barrier.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Barrier.h>\n\n#include <glog/logging.h>\n\n#include <folly/ScopeGuard.h>\n#include <folly/lang/New.h>\n\nnamespace folly {\nnamespace futures {\n\nBarrier::Barrier(uint32_t n)\n    : size_(n), controlBlock_(allocateControlBlock()) {}\n\nBarrier::~Barrier() {\n  auto block = controlBlock_.load(std::memory_order_relaxed);\n  auto prev = block->valueAndReaderCount.load(std::memory_order_relaxed);\n  DCHECK_EQ(prev >> kReaderShift, 0u);\n  auto val = prev & kValueMask;\n  auto p = promises(block);\n\n  for (uint32_t i = 0; i < val; ++i) {\n    p[i].setException(\n        folly::make_exception_wrapper<std::runtime_error>(\"Barrier destroyed\"));\n  }\n\n  freeControlBlock(controlBlock_);\n}\n\nauto Barrier::allocateControlBlock() -> ControlBlock* {\n  auto storage = operator_new(\n      controlBlockSize(size_),\n      std::align_val_t(alignof(ControlBlockAndPromise)));\n  auto block = ::new (storage) ControlBlock();\n\n  auto p = promises(block);\n  uint32_t i = 0;\n  auto rollback = makeGuard([&] {\n    for (; i != 0; --i) {\n      p[i - 1].~BoolPromise();\n    }\n  });\n  for (i = 0; i < size_; ++i) {\n    ::new (p + i) BoolPromise();\n  }\n  rollback.dismiss();\n\n  return block;\n}\n\nvoid Barrier::freeControlBlock(ControlBlock* block) {\n  auto p = promises(block);\n  for (uint32_t i = size_; i != 0; --i) {\n    p[i - 1].~BoolPromise();\n  }\n  operator_delete(\n      block,\n      controlBlockSize(size_),\n      std::align_val_t(alignof(ControlBlockAndPromise)));\n}\n\nfolly::Future<bool> Barrier::wait() {\n  // Load the current control block first. As we know there is at least\n  // one thread in the current epoch (us), this means that the value is\n  // < size_, so controlBlock_ can't change until we bump the value below.\n  auto block = controlBlock_.load(std::memory_order_acquire);\n  auto p = promises(block);\n\n  // Bump the value and record ourselves as reader.\n  // This ensures that block stays allocated, as the reader count is > 0.\n  auto prev = block->valueAndReaderCount.fetch_add(\n      kReader + 1, std::memory_order_acquire);\n\n  auto prevValue = static_cast<uint32_t>(prev & kValueMask);\n  DCHECK_LT(prevValue, size_);\n  auto future = p[prevValue].getFuture();\n\n  if (prevValue + 1 == size_) {\n    // Need to reset the barrier before fulfilling any futures. This is\n    // when the epoch is flipped to the next.\n    controlBlock_.store(allocateControlBlock(), std::memory_order_release);\n\n    p[0].setValue(true);\n    for (uint32_t i = 1; i < size_; ++i) {\n      p[i].setValue(false);\n    }\n  }\n\n  // Free the control block if we're the last reader at max value.\n  prev =\n      block->valueAndReaderCount.fetch_sub(kReader, std::memory_order_acq_rel);\n  if (prev == (kReader | uint64_t(size_))) {\n    freeControlBlock(block);\n  }\n\n  return future;\n}\n\n} // namespace futures\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Barrier.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstdint>\n\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n\nnamespace folly {\nnamespace futures {\n\n// A folly::Future-istic Barrier synchronization primitive\n//\n// The barrier is initialized with a count N.\n//\n// The first N-1 calls to wait() return uncompleted futures.\n//\n// The Nth call to wait() completes the previous N-1 futures successfully,\n// returns a future that is already completed successfully, and resets the\n// barrier; the barrier may be reused immediately, as soon as at least one\n// of the future completions has been observed.\n//\n// Of these N futures, exactly one is completed with true, while the others are\n// completed with false; it is unspecified which future completes with true.\n// (This may be used to elect a \"leader\" among a group of threads.)\n//\n// If the barrier is destroyed, any futures already returned by wait() will\n// complete with an error.\nclass Barrier {\n public:\n  explicit Barrier(uint32_t n);\n  ~Barrier();\n\n  folly::Future<bool> wait();\n\n private:\n  using BoolPromise = folly::Promise<bool>;\n\n  static constexpr uint64_t kReaderShift = 32;\n  static constexpr uint64_t kReader = uint64_t(1) << kReaderShift;\n  static constexpr uint64_t kValueMask = kReader - 1;\n\n  // For each \"epoch\" that the barrier is active, we have a different\n  // ControlBlock. The ControlBlock contains the current barrier value\n  // and the number of readers (currently inside wait()) packed into a\n  // 64-bit value.\n  //\n  // The ControlBlock is allocated as long as either:\n  // - there are threads currently inside wait() (reader count > 0), or\n  // - the value has not yet reached size_ (value < size_)\n  //\n  // The array of size_ Promise objects is allocated immediately following\n  // valueAndReaderCount.\n\n  struct ControlBlock {\n    // Reader count in most significant 32 bits\n    // Value in least significant 32 bits\n    std::atomic<uint64_t> valueAndReaderCount{0};\n  };\n\n  struct ControlBlockAndPromise {\n    ControlBlock cb;\n    BoolPromise promises[1];\n  };\n\n  static BoolPromise* promises(ControlBlock* cb) {\n    return reinterpret_cast<ControlBlockAndPromise*>(cb)->promises;\n  }\n\n  static size_t controlBlockSize(size_t n) {\n    return offsetof(ControlBlockAndPromise, promises) + n * sizeof(BoolPromise);\n  }\n\n  ControlBlock* allocateControlBlock();\n  void freeControlBlock(ControlBlock* b);\n\n  uint32_t size_;\n  std::atomic<ControlBlock*> controlBlock_;\n};\n\n} // namespace futures\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME barrier\n  SRCS\n    Barrier.cpp\n  HEADERS\n    Barrier.h\n  DEPS\n    folly_lang_new\n    folly_scope_guard\n  EXPORTED_DEPS\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME cleanup\n  HEADERS\n    Cleanup.h\n  EXPORTED_DEPS\n    folly_futures_core\n)\n\nfolly_add_library(\n  NAME core\n  SRCS\n    Future.cpp\n    HeapTimekeeper.cpp\n    Promise.cpp\n    ThreadWheelTimekeeper.cpp\n  HEADERS\n    Future-inl.h\n    Future-pre.h\n    Future.h\n    HeapTimekeeper.h\n    Promise-inl.h\n    Promise.h\n    Retrying.h\n    ThreadWheelTimekeeper.h\n    WTCallback.h\n  DEPS\n    folly_container_intrusive_heap\n    folly_lang_safe_assert\n    folly_likely\n    folly_portability_gflags\n    folly_singleton\n    folly_synchronization_distributed_mutex\n    folly_synchronization_relaxed_atomic\n    folly_synchronization_saturating_semaphore\n    folly_synchronization_wait_options\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_chrono\n    folly_container_foreach\n    folly_coro_traits\n    folly_detail_async_trace\n    folly_executors_drivable_executor\n    folly_executors_executor_with_priority\n    folly_executors_global_executor\n    folly_executors_inline_executor\n    folly_executors_queued_immediate_executor\n    folly_executors_timed_drivable_executor\n    folly_fibers_core\n    folly_functional_invoke\n    folly_futures_detail_core\n    folly_futures_portability\n    folly_io_async_async_base\n    folly_lang_exception\n    folly_lang_pretty\n    folly_optional\n    folly_portability\n    folly_random\n    folly_scope_guard\n    folly_traits\n    folly_try\n    folly_unit\n    folly_utility\n)\n\nfolly_add_library(\n  NAME future_splitter\n  HEADERS\n    FutureSplitter.h\n  EXPORTED_DEPS\n    folly_futures_core\n    folly_futures_shared_promise\n    folly_lang_exception\n)\n\nfolly_add_library(\n  NAME futures\n  EXPORTED_DEPS\n    folly_executors_inline_executor\n    folly_executors_manual_executor\n    folly_executors_queued_immediate_executor\n    folly_executors_scheduled_executor\n    folly_futures_barrier\n    folly_futures_core\n    folly_futures_future_splitter\n    folly_futures_shared_promise\n)\n\nfolly_add_library(\n  NAME manual_timekeeper\n  SRCS\n    ManualTimekeeper.cpp\n  HEADERS\n    ManualTimekeeper.h\n  DEPS\n    folly_synchronization_atomic_util\n  EXPORTED_DEPS\n    folly_futures_core\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME portability\n  HEADERS\n    Portability.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME shared_promise\n  SRCS\n    SharedPromise.cpp\n  HEADERS\n    SharedPromise-inl.h\n    SharedPromise.h\n  EXPORTED_DEPS\n    folly_executors_inline_executor\n    folly_futures_core\n    folly_lang_exception\n    folly_portability\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/futures/Cleanup.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <mutex>\n\n#include <glog/logging.h>\n\n#include <folly/futures/Future.h>\n\nnamespace folly {\n\n// Structured Async Cleanup\n//\n\n// Structured Async Cleanup - traits\n//\n\nnamespace detail {\nstruct cleanup_fn {\n  template <\n      class T,\n      class R = decltype(std::declval<T>().cleanup()),\n      std::enable_if_t<std::is_same_v<R, folly::SemiFuture<folly::Unit>>, int> =\n          0>\n  R operator()(T&& t) const {\n    return ((T&&)t).cleanup();\n  }\n};\n} // namespace detail\n\ntemplate <class T>\nconstexpr bool is_cleanup_v = folly::is_invocable_v<detail::cleanup_fn, T>;\n\ntemplate <typename T>\nusing is_cleanup = std::bool_constant<is_cleanup_v<T>>;\n\n// Structured Async Cleanup\n//\n// This helps compose a task with async cleanup\n// The task result is stored until cleanup completes and is then produced\n// The cleanup task is not allowed to fail.\n//\n// This can be used with collectAll to combine multiple async resources\n//\n// ensureCleanupAfterTask(collectAll(a.run(), b.run()), collectAll(a.cleanup(),\n// b.cleanup())).wait();\n//\ntemplate <typename T>\nfolly::SemiFuture<T> ensureCleanupAfterTask(\n    folly::SemiFuture<T> task, folly::SemiFuture<folly::Unit> cleanup) {\n  return folly::makeSemiFuture()\n      .deferValue([task_ = std::move(task)](folly::Unit) mutable {\n        return std::move(task_);\n      })\n      .defer([cleanup_ = std::move(cleanup)](folly::Try<T> taskResult) mutable {\n        return std::move(cleanup_).defer(\n            [taskResult_ = std::move(taskResult)](folly::Try<folly::Unit> t) {\n              if (t.hasException()) {\n                terminate_with<std::logic_error>(\"cleanup must not throw\");\n              }\n              return std::move(taskResult_).value();\n            });\n      });\n}\n\n// Structured Async Cleanup\n//\n// This implementation is a base class that collects a set of cleanup tasks\n// and runs them in reverse order.\n//\n// A class derived from Cleanup\n//  - only allows cleanup to be run once\n//  - is required to complete cleanup before running the destructor\n//  - *should not* run cleanup tasks. Running the cleanup task should be\n//    delegated to the owner of the derived class\n//  - *should not* be owned by a shared_ptr. Cleanup is intended to remove\n//    shared ownership.\n//\nclass Cleanup {\n public:\n  Cleanup() : safe_to_destruct_(false), cleanup_(folly::makeSemiFuture()) {}\n  ~Cleanup() {\n    if (!safe_to_destruct_) {\n      LOG(FATAL) << \"Cleanup must complete before it is destructed.\";\n    }\n  }\n\n  // Returns: a SemiFuture that, just like destructors, sequences the cleanup\n  // tasks added in reverse of the order they were added.\n  //\n  // calls to cleanup() do not mutate state. The returned SemiFuture, once\n  // it has been given an executor, does mutate state and must not overlap with\n  // any calls to addCleanup().\n  //\n  folly::SemiFuture<folly::Unit> cleanup() {\n    return folly::makeSemiFuture()\n        .deferValue([this](folly::Unit) {\n          if (!cleanup_.valid()) {\n            LOG(FATAL) << \"cleanup already run - cleanup task invalid.\";\n          }\n          return std::move(cleanup_);\n        })\n        .defer([this](folly::Try<folly::Unit> t) {\n          if (t.hasException()) {\n            LOG(FATAL) << \"Cleanup actions must be noexcept.\";\n          }\n          this->safe_to_destruct_ = true;\n        });\n  }\n\n protected:\n  // includes the provided SemiFuture under the scope of this.\n  //\n  // when the cleanup() for this started it will get this SemiFuture first.\n  //\n  // order matters, just like destructors, cleanup tasks will be run in reverse\n  // of the order they were added.\n  //\n  // all gets will use the Executor provided to the SemiFuture returned by\n  // cleanup()\n  //\n  // calls to addCleanup() must not overlap with each other and must not overlap\n  // with a running SemiFuture returned from addCleanup().\n  //\n  void addCleanup(folly::SemiFuture<folly::Unit> c) {\n    if (!cleanup_.valid()) {\n      LOG(FATAL)\n          << \"Cleanup::addCleanup must not be called after Cleanup::cleanup.\";\n    }\n    cleanup_ = std::move(c).deferValue(\n        [nested = std::move(cleanup_)](folly::Unit) mutable {\n          return std::move(nested);\n        });\n  }\n\n  // includes the provided model of Cleanup under the scope of this\n  //\n  // when the cleanup() for this started it will cleanup this first.\n  //\n  // order matters, just like destructors, cleanup tasks will be run in reverse\n  // of the order they were added.\n  //\n  // all gets will use the Executor provided to the SemiFuture returned by\n  // cleanup()\n  //\n  // calls to addCleanup() must not overlap with each other and must not overlap\n  // with a running SemiFuture returned from addCleanup().\n  //\n  template <\n      class OtherCleanup,\n      std::enable_if_t<is_cleanup_v<OtherCleanup>, int> = 0>\n  void addCleanup(OtherCleanup&& c) {\n    addCleanup(((OtherCleanup&&)c).cleanup());\n  }\n\n private:\n  bool safe_to_destruct_;\n  folly::SemiFuture<folly::Unit> cleanup_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Future-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <atomic>\n#include <cassert>\n#include <chrono>\n#include <deque>\n#include <thread>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Optional.h>\n#include <folly/Traits.h>\n#include <folly/container/Foreach.h>\n#include <folly/detail/AsyncTrace.h>\n#include <folly/executors/ExecutorWithPriority.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/executors/QueuedImmediateExecutor.h>\n#include <folly/futures/detail/Core.h>\n#include <folly/lang/Pretty.h>\n\nnamespace folly {\n\nclass Timekeeper;\n\nnamespace futures {\nnamespace detail {\ntypedef folly::fibers::Baton FutureBatonType;\n} // namespace detail\n} // namespace futures\n\nnamespace detail {\n// For access to the singleton in tests.\nstruct TimekeeperSingletonTag {};\nstd::shared_ptr<Timekeeper> getTimekeeperSingleton();\n} // namespace detail\n\nnamespace futures {\nnamespace detail {\n\n// InvokeResultWrapper and wrapInvoke enable wrapping a result value in its\n// nearest Future-type counterpart capable of also carrying an exception.\n// e.g.\n//    (semi)Future<T> -> (semi)Future<T>           (no change)\n//    Try<T>          -> Try<T>                    (no change)\n//    void            -> Try<folly::Unit>\n//    T               -> Try<T>\ntemplate <typename T>\nstruct InvokeResultWrapperBase {\n  template <typename F>\n  static T wrapResult(F fn) {\n    return T(fn());\n  }\n  static T wrapException(exception_wrapper&& e) { return T(std::move(e)); }\n};\ntemplate <typename T>\nstruct InvokeResultWrapper : InvokeResultWrapperBase<Try<T>> {};\ntemplate <typename T>\nstruct InvokeResultWrapper<Try<T>> : InvokeResultWrapperBase<Try<T>> {};\ntemplate <typename T>\nstruct InvokeResultWrapper<SemiFuture<T>>\n    : InvokeResultWrapperBase<SemiFuture<T>> {};\ntemplate <typename T>\nstruct InvokeResultWrapper<Future<T>> : InvokeResultWrapperBase<Future<T>> {};\ntemplate <>\nstruct InvokeResultWrapper<void> : InvokeResultWrapperBase<Try<Unit>> {\n  template <typename F>\n  static Try<Unit> wrapResult(F fn) {\n    fn();\n    return Try<Unit>(unit);\n  }\n};\n\ntemplate <typename T, typename F>\nauto wrapInvoke(folly::Try<T>&& t, F&& f) {\n  auto fn = [&]() { return static_cast<F&&>(f)(t.template get<false, T&&>()); };\n  using FnResult = decltype(fn());\n  using Wrapper = InvokeResultWrapper<FnResult>;\n  if (t.hasException()) {\n    return Wrapper::wrapException(std::move(t).exception());\n  }\n  return Wrapper::wrapResult(fn);\n}\n\n//  Guarantees that the stored functor is destructed before the stored promise\n//  may be fulfilled. Assumes the stored functor to be noexcept-destructible.\ntemplate <typename T, typename F>\nclass CoreCallbackState {\n  using DF = folly::decay_t<F>;\n\n public:\n  CoreCallbackState(Promise<T>&& promise, F&& func) noexcept(\n      noexcept(DF(static_cast<F&&>(func))))\n      : func_(static_cast<F&&>(func)),\n        core_(std::exchange(promise.core_, nullptr)) {\n    assert(before_barrier());\n  }\n\n  CoreCallbackState(CoreCallbackState&& that) noexcept(\n      noexcept(DF(static_cast<F&&>(that.func_)))) {\n    if (that.before_barrier()) {\n      new (&func_) DF(static_cast<F&&>(that.func_));\n      that.func_.~DF();\n      core_ = std::exchange(that.core_, nullptr);\n    }\n  }\n\n  CoreCallbackState& operator=(CoreCallbackState&&) = delete;\n\n  ~CoreCallbackState() {\n    if (before_barrier()) {\n      stealPromise();\n    }\n  }\n\n  template <typename... Args>\n  auto invoke(Args&&... args) noexcept(\n      noexcept(FOLLY_DECLVAL(F&&)(static_cast<Args&&>(args)...))) {\n    assert(before_barrier());\n    return static_cast<F&&>(func_)(static_cast<Args&&>(args)...);\n  }\n\n  template <typename... Args>\n  auto tryInvoke(Args&&... args) noexcept {\n    return makeTryWith([&] { return invoke(static_cast<Args&&>(args)...); });\n  }\n\n  void setTry(Executor::KeepAlive<>&& keepAlive, Try<T>&& t) {\n    stealPromise().setTry(std::move(keepAlive), std::move(t));\n  }\n\n  void setException(Executor::KeepAlive<>&& keepAlive, exception_wrapper&& ew) {\n    setTry(std::move(keepAlive), Try<T>(std::move(ew)));\n  }\n\n  Promise<T> stealPromise() noexcept {\n    assert(before_barrier());\n    func_.~DF();\n    return Promise<T>{\n        MakeRetrievedFromStolenCoreTag{}, *std::exchange(core_, nullptr)};\n  }\n\n private:\n  bool before_barrier() const noexcept { return core_ && !core_->hasResult(); }\n\n  union {\n    DF func_;\n  };\n  Core<T>* core_ = nullptr; // Promise<T> is 2 ptrs but Core<T>* is 1 ptr wide\n};\n\ntemplate <typename T, typename F>\nauto makeCoreCallbackState(Promise<T>&& p, F&& f) noexcept(\n    noexcept(CoreCallbackState<T, F>(std::move(p), static_cast<F&&>(f)))) {\n  return CoreCallbackState<T, F>(std::move(p), static_cast<F&&>(f));\n}\n\ntemplate <typename T, typename R, typename... Args>\nauto makeCoreCallbackState(Promise<T>&& p, R (&f)(Args...)) noexcept {\n  return CoreCallbackState<T, R (*)(Args...)>(std::move(p), &f);\n}\n\ntemplate <class T>\nFutureBase<T>::FutureBase(SemiFuture<T>&& other) noexcept : core_(other.core_) {\n  other.core_ = nullptr;\n}\n\ntemplate <class T>\nFutureBase<T>::FutureBase(Future<T>&& other) noexcept : core_(other.core_) {\n  other.core_ = nullptr;\n}\n\ntemplate <class T>\ntemplate <class T2, typename>\nFutureBase<T>::FutureBase(T2&& val)\n    : core_(Core::make(Try<T>(static_cast<T2&&>(val)))) {}\n\ntemplate <class T>\ntemplate <typename T2>\nFutureBase<T>::FutureBase(\n    typename std::enable_if<std::is_same<Unit, T2>::value>::type*)\n    : core_(Core::make(Try<T>(T()))) {}\n\ntemplate <class T>\nvoid FutureBase<T>::assign(FutureBase<T>&& other) noexcept {\n  detach();\n  core_ = std::exchange(other.core_, nullptr);\n}\n\ntemplate <class T>\nFutureBase<T>::~FutureBase() {\n  detach();\n}\n\ntemplate <class T>\nT& FutureBase<T>::value() & {\n  return result().value();\n}\n\ntemplate <class T>\nT const& FutureBase<T>::value() const& {\n  return result().value();\n}\n\ntemplate <class T>\nT&& FutureBase<T>::value() && {\n  return std::move(result().value());\n}\n\ntemplate <class T>\nT const&& FutureBase<T>::value() const&& {\n  return std::move(result().value());\n}\n\ntemplate <class T>\nTry<T>& FutureBase<T>::result() & {\n  return getCoreTryChecked();\n}\n\ntemplate <class T>\nTry<T> const& FutureBase<T>::result() const& {\n  return getCoreTryChecked();\n}\n\ntemplate <class T>\nTry<T>&& FutureBase<T>::result() && {\n  return std::move(getCoreTryChecked());\n}\n\ntemplate <class T>\nTry<T> const&& FutureBase<T>::result() const&& {\n  return std::move(getCoreTryChecked());\n}\n\ntemplate <class T>\nbool FutureBase<T>::isReady() const {\n  return getCore().hasResult();\n}\n\ntemplate <class T>\nbool FutureBase<T>::hasValue() const {\n  return result().hasValue();\n}\n\ntemplate <class T>\nbool FutureBase<T>::hasException() const {\n  return result().hasException();\n}\n\ntemplate <class T>\nvoid FutureBase<T>::detach() {\n  if (core_) {\n    core_->detachFuture();\n    core_ = nullptr;\n  }\n}\n\ntemplate <class T>\nvoid FutureBase<T>::throwIfInvalid() const {\n  if (!core_) {\n    throw_exception<FutureInvalid>();\n  }\n}\n\ntemplate <class T>\nvoid FutureBase<T>::throwIfContinued() const {\n  if (!core_ || core_->hasCallback()) {\n    throw_exception<FutureAlreadyContinued>();\n  }\n}\n\ntemplate <class T>\nOptional<Try<T>> FutureBase<T>::poll() {\n  auto& core = getCore();\n  return core.hasResult()\n      ? Optional<Try<T>>(std::move(core.getTry()))\n      : Optional<Try<T>>();\n}\n\ntemplate <class T>\nvoid FutureBase<T>::raise(exception_wrapper exception) {\n  getCore().raise(std::move(exception));\n}\n\ntemplate <class T>\ntemplate <class F>\nvoid FutureBase<T>::setCallback_(\n    F&& func,\n    std::shared_ptr<folly::RequestContext>&& context,\n    futures::detail::InlineContinuation allowInline) {\n  throwIfContinued();\n  getCore().setCallback(\n      static_cast<F&&>(func), std::move(context), allowInline);\n}\n\ntemplate <class T>\ntemplate <class F>\nvoid FutureBase<T>::setCallback_(\n    F&& func, futures::detail::InlineContinuation allowInline) {\n  setCallback_(\n      static_cast<F&&>(func), RequestContext::saveContext(), allowInline);\n}\n\ntemplate <class T>\nFutureBase<T>::FutureBase(futures::detail::EmptyConstruct) noexcept\n    : core_(nullptr) {}\n\n// MSVC 2017 Update 7 released with a bug that causes issues expanding to an\n// empty parameter pack when invoking a templated member function. It should\n// be fixed for MSVC 2017 Update 8.\n// TODO: Remove.\nnamespace detail_msvc_15_7_workaround {\ntemplate <typename R, std::size_t S>\nusing IfArgsSizeIs = std::enable_if_t<R::Arg::ArgsSize::value == S, int>;\ntemplate <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0>\ndecltype(auto) invoke(\n    R, State& state, Executor::KeepAlive<>&&, Try<T>&& /* t */) {\n  return state.invoke();\n}\ntemplate <typename R, typename State, typename T, IfArgsSizeIs<R, 2> = 0>\ndecltype(auto) invoke(R, State& state, Executor::KeepAlive<>&& ka, Try<T>&& t) {\n  using Arg1 = typename R::Arg::ArgList::Tail::FirstArg;\n  return state.invoke(\n      std::move(ka), std::move(t).template get<R::Arg::isTry(), Arg1>());\n}\ntemplate <typename R, typename State, typename T, IfArgsSizeIs<R, 0> = 0>\ndecltype(auto) tryInvoke(\n    R, State& state, Executor::KeepAlive<>&&, Try<T>&& /* t */) {\n  return state.tryInvoke();\n}\ntemplate <typename R, typename State, typename T, IfArgsSizeIs<R, 2> = 0>\ndecltype(auto) tryInvoke(\n    R, State& state, Executor::KeepAlive<>&& ka, Try<T>&& t) {\n  using Arg1 = typename R::Arg::ArgList::Tail::FirstArg;\n  return state.tryInvoke(\n      std::move(ka), std::move(t).template get<R::Arg::isTry(), Arg1>());\n}\n} // namespace detail_msvc_15_7_workaround\n\nclass FutureBaseHelper {\n public:\n  // note: using std::pair instead would regress build speed\n  template <typename T>\n  struct FuturePromisePair {\n    Future<T> future;\n    Promise<T> promise;\n  };\n  template <typename T>\n  static FuturePromisePair<T> makePromiseContractForThen(\n      CoreBase& core, Executor* exec) {\n    Promise<T> p;\n    p.core_->initCopyInterruptHandlerFrom(core);\n    auto sf = p.getSemiFuture();\n    sf.setExecutor(folly::Executor::KeepAlive<>{exec});\n    auto f = Future<T>(sf.core_);\n    sf.core_ = nullptr;\n    return {std::move(f), std::move(p)};\n  }\n};\n\n// then\n\n// Variant: returns a value\n// e.g. f.then([](Try<T>&& t){ return t.value(); });\ntemplate <class T>\ntemplate <typename F, typename R>\ntypename std::enable_if< //\n    !R::ReturnsFuture::value,\n    Future<typename R::value_type>>::type\nFutureBase<T>::thenImplementation(\n    F&& func, R, futures::detail::InlineContinuation allowInline) {\n  static_assert(R::Arg::ArgsSize::value == 2, \"Then must take two arguments\");\n  using B = typename R::ReturnsFuture::Inner;\n  auto fp = FutureBaseHelper::makePromiseContractForThen<B>(\n      this->getCore(), this->getExecutor());\n  this->setCallback_(\n      [state = futures::detail::makeCoreCallbackState(\n           std::move(fp.promise), static_cast<F&&>(func))](\n          Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {\n        if (!R::Arg::isTry() && t.hasException()) {\n          state.setException(std::move(ka), std::move(t.exception()));\n        } else {\n          auto propagateKA = ka.copy();\n          state.setTry(std::move(propagateKA), makeTryWith([&] {\n                         return detail_msvc_15_7_workaround::invoke(\n                             R{}, state, std::move(ka), std::move(t));\n                       }));\n        }\n      },\n      allowInline);\n  return std::move(fp.future);\n}\n\n// Pass through a simple future as it needs no deferral adaptation\ntemplate <class T>\nFuture<T> chainExecutor(Executor::KeepAlive<>, Future<T>&& f) {\n  return std::move(f);\n}\n\n// Correctly chain a SemiFuture for deferral\ntemplate <class T>\nFuture<T> chainExecutor(Executor::KeepAlive<> e, SemiFuture<T>&& f) {\n  if (!e) {\n    e = folly::getKeepAliveToken(InlineExecutor::instance());\n  }\n  return std::move(f).via(std::move(e));\n}\n\n// Variant: returns a Future\n// e.g. f.then([](T&& t){ return makeFuture<T>(t); });\ntemplate <class T>\ntemplate <typename F, typename R>\ntypename std::enable_if< //\n    R::ReturnsFuture::value,\n    Future<typename R::value_type>>::type\nFutureBase<T>::thenImplementation(\n    F&& func, R, futures::detail::InlineContinuation allowInline) {\n  static_assert(R::Arg::ArgsSize::value == 2, \"Then must take two arguments\");\n  using B = typename R::ReturnsFuture::Inner;\n  auto fp = FutureBaseHelper::makePromiseContractForThen<B>(\n      this->getCore(), this->getExecutor());\n  this->setCallback_(\n      [state = futures::detail::makeCoreCallbackState(\n           std::move(fp.promise), static_cast<F&&>(func))](\n          Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {\n        if (!R::Arg::isTry() && t.hasException()) {\n          state.setException(std::move(ka), std::move(t.exception()));\n        } else {\n          // Ensure that if function returned a SemiFuture we correctly chain\n          // potential deferral.\n          auto tf2 = detail_msvc_15_7_workaround::tryInvoke(\n              R{}, state, ka.copy(), std::move(t));\n          if (tf2.hasException()) {\n            state.setException(std::move(ka), std::move(tf2.exception()));\n          } else {\n            auto statePromise = state.stealPromise();\n            auto tf3 = chainExecutor(std::move(ka), *std::move(tf2));\n            std::exchange(statePromise.core_, nullptr)\n                ->setProxy(std::exchange(tf3.core_, nullptr));\n          }\n        }\n      },\n      allowInline);\n\n  return std::move(fp.future);\n}\n\nclass WaitExecutor final : public folly::Executor {\n public:\n  void add(Func func) override {\n    bool empty;\n    {\n      auto wQueue = queue_.wlock();\n      if (wQueue->detached) {\n        return;\n      }\n      empty = wQueue->funcs.empty();\n      wQueue->funcs.push_back(std::move(func));\n    }\n    if (empty) {\n      baton_.post();\n    }\n  }\n\n  void drive() {\n    baton_.wait();\n\n    fibers::runInMainContext([&]() {\n      baton_.reset();\n      auto funcs = std::move(queue_.wlock()->funcs);\n      for (auto& func : funcs) {\n        std::exchange(func, nullptr)();\n      }\n    });\n  }\n\n  using Clock = std::chrono::steady_clock;\n\n  bool driveUntil(Clock::time_point deadline) {\n    if (!baton_.try_wait_until(deadline)) {\n      return false;\n    }\n    return fibers::runInMainContext([&]() {\n      baton_.reset();\n      auto funcs = std::move(queue_.wlock()->funcs);\n      for (auto& func : funcs) {\n        std::exchange(func, nullptr)();\n      }\n      return true;\n    });\n  }\n\n  void detach() {\n    // Make sure we don't hold the lock while destroying funcs.\n    [&] {\n      auto wQueue = queue_.wlock();\n      wQueue->detached = true;\n      return std::move(wQueue->funcs);\n    }();\n  }\n\n  static KeepAlive<WaitExecutor> create() {\n    return makeKeepAlive<WaitExecutor>(new WaitExecutor());\n  }\n\n private:\n  WaitExecutor() {}\n\n  bool keepAliveAcquire() noexcept override {\n    auto keepAliveCount =\n        keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    DCHECK(keepAliveCount > 0);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    auto keepAliveCount =\n        keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);\n    DCHECK(keepAliveCount > 0);\n    if (keepAliveCount == 1) {\n      delete this;\n    }\n  }\n\n  struct Queue {\n    std::deque<Func> funcs;\n    bool detached{false};\n  };\n\n  folly::Synchronized<Queue> queue_;\n  FutureBatonType baton_;\n\n  std::atomic<ssize_t> keepAliveCount_{1};\n};\n\n// Vector-like structure to play with window,\n// which otherwise expects a vector of size `times`,\n// which would be expensive with large `times` sizes.\nstruct WindowFakeVector {\n  using iterator = std::vector<size_t>::iterator;\n\n  WindowFakeVector(size_t size) : size_(size) {}\n\n  size_t operator[](const size_t index) const { return index; }\n  size_t size() const { return size_; }\n\n private:\n  size_t size_;\n};\n} // namespace detail\n} // namespace futures\n\ntemplate <class T>\nSemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) {\n  return makeSemiFuture(Try<typename std::decay<T>::type>(static_cast<T&&>(t)));\n}\n\n// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>\ntemplate <class F>\ntypename std::enable_if<\n    isFutureOrSemiFuture<invoke_result_t<F>>::value,\n    SemiFuture<typename invoke_result_t<F>::value_type>>::type\nmakeSemiFutureWith(F&& func) {\n  using InnerType = typename isFutureOrSemiFuture<invoke_result_t<F>>::Inner;\n  try {\n    return static_cast<F&&>(func)();\n  } catch (...) {\n    return makeSemiFuture<InnerType>(exception_wrapper(current_exception()));\n  }\n}\n\n// makeSemiFutureWith(T()) -> SemiFuture<T>\n// makeSemiFutureWith(void()) -> SemiFuture<Unit>\ntemplate <class F>\ntypename std::enable_if<\n    !(isFutureOrSemiFuture<invoke_result_t<F>>::value),\n    SemiFuture<lift_unit_t<invoke_result_t<F>>>>::type\nmakeSemiFutureWith(F&& func) {\n  using LiftedResult = lift_unit_t<invoke_result_t<F>>;\n  return makeSemiFuture<LiftedResult>(makeTryWith([&func]() mutable {\n    return static_cast<F&&>(func)();\n  }));\n}\n\ntemplate <class T>\nSemiFuture<T> makeSemiFuture(std::exception_ptr const& e) {\n  return makeSemiFuture(Try<T>(e));\n}\n\ntemplate <class T>\nSemiFuture<T> makeSemiFuture(exception_wrapper ew) {\n  return makeSemiFuture(Try<T>(std::move(ew)));\n}\n\ntemplate <class T, class E>\nstd::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, SemiFuture<T>>\nmakeSemiFuture(E&& e) {\n  return makeSemiFuture(Try<T>(make_exception_wrapper<E>(std::forward<E>(e))));\n}\n\n// DEPRECATED const-ref overload for users who EXPLICITLY specify BOTH template\n// parameters\ntemplate <class T, class E>\nstd::enable_if_t<std::is_base_of_v<std::exception, E>, SemiFuture<T>>\nmakeSemiFuture(const folly::type_identity_t<E>& e) {\n  return makeSemiFuture(Try<T>(make_exception_wrapper<E>(e)));\n}\n\ntemplate <class T>\nSemiFuture<T> makeSemiFuture(Try<T> t) {\n  return SemiFuture<T>(SemiFuture<T>::Core::make(std::move(t)));\n}\n\n// This must be defined after the constructors to avoid a bug in MSVC\n// https://connect.microsoft.com/VisualStudio/feedback/details/3142777/out-of-line-constructor-definition-after-implicit-reference-causes-incorrect-c2244\ninline SemiFuture<Unit> makeSemiFuture() {\n  return makeSemiFuture(Unit{});\n}\n\ntemplate <class T>\nSemiFuture<T> SemiFuture<T>::makeEmpty() {\n  return SemiFuture<T>(futures::detail::EmptyConstruct{});\n}\n\ntemplate <class T>\nfutures::detail::DeferredWrapper SemiFuture<T>::stealDeferredExecutor() {\n  return this->getCore().stealDeferredExecutor();\n}\n\ntemplate <class T>\nvoid SemiFuture<T>::releaseDeferredExecutor(Core* core) {\n  if (!core || core->hasCallback()) {\n    return;\n  }\n  auto executor = core->stealDeferredExecutor();\n  async_tracing::logSemiFutureDiscard(\n      executor.get() ? async_tracing::DiscardHasDeferred::DEFERRED_EXECUTOR\n                     : async_tracing::DiscardHasDeferred::NO_EXECUTOR);\n  if (executor) {\n    executor.get()->detach();\n  }\n}\n\ntemplate <class T>\nSemiFuture<T>::~SemiFuture() {\n  releaseDeferredExecutor(this->core_);\n}\n\ntemplate <class T>\nSemiFuture<T>::SemiFuture(SemiFuture<T>&& other) noexcept\n    : futures::detail::FutureBase<T>(std::move(other)) {}\n\ntemplate <class T>\nSemiFuture<T>::SemiFuture(Future<T>&& other) noexcept\n    : futures::detail::FutureBase<T>(std::move(other)) {\n  // SemiFuture should not have an executor on construction\n  if (this->core_) {\n    this->setExecutor(futures::detail::KeepAliveOrDeferred{});\n  }\n}\n\ntemplate <class T>\nSemiFuture<T>& SemiFuture<T>::operator=(SemiFuture<T>&& other) noexcept {\n  releaseDeferredExecutor(this->core_);\n  this->assign(std::move(other));\n  return *this;\n}\n\ntemplate <class T>\nSemiFuture<T>& SemiFuture<T>::operator=(Future<T>&& other) noexcept {\n  releaseDeferredExecutor(this->core_);\n  this->assign(std::move(other));\n  // SemiFuture should not have an executor on construction\n  if (this->core_) {\n    this->setExecutor(Executor::KeepAlive<>{});\n  }\n  return *this;\n}\n\ntemplate <class T>\nFuture<T> SemiFuture<T>::via(Executor::KeepAlive<> executor) && {\n  folly::async_tracing::logSemiFutureVia(this->getExecutor(), executor.get());\n\n  if (!executor) {\n    throw_exception<FutureNoExecutor>();\n  }\n\n  if (auto deferredExecutor = this->getDeferredExecutor()) {\n    deferredExecutor->setExecutor(executor.copy());\n  }\n\n  auto newFuture = Future<T>(this->core_);\n  this->core_ = nullptr;\n  newFuture.setExecutor(std::move(executor));\n\n  return newFuture;\n}\n\ntemplate <class T>\nFuture<T> SemiFuture<T>::viaInlineUnsafe(Executor::KeepAlive<> executor) && {\n  folly::async_tracing::logSemiFutureVia(this->getExecutor(), executor.get());\n\n  if (!executor) {\n    throw_exception<FutureNoExecutor>();\n  }\n\n  if (auto deferredExecutor = this->getDeferredExecutor()) {\n    deferredExecutor->setExecutor(executor.copy(), /* inlineUnsafe = */ true);\n  }\n\n  auto newFuture = Future<T>(this->core_);\n  this->core_ = nullptr;\n  newFuture.setExecutor(std::move(executor));\n\n  return newFuture;\n}\n\ntemplate <class T>\nFuture<T> SemiFuture<T>::via(\n    Executor::KeepAlive<> executor, int8_t priority) && {\n  return std::move(*this).via(\n      ExecutorWithPriority::create(std::move(executor), priority));\n}\n\ntemplate <class T>\nFuture<T> SemiFuture<T>::toUnsafeFuture() && {\n  return std::move(*this).via(&InlineExecutor::instance());\n}\n\ntemplate <class T>\ntemplate <typename F>\nSemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>\nSemiFuture<T>::defer(F&& func) && {\n  auto deferredExecutorPtr = this->getDeferredExecutor();\n  futures::detail::KeepAliveOrDeferred deferredExecutor = [&]() {\n    if (deferredExecutorPtr) {\n      return futures::detail::KeepAliveOrDeferred{deferredExecutorPtr->copy()};\n    } else {\n      auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred(\n          futures::detail::DeferredExecutor::create());\n      this->setExecutor(newDeferredExecutor.copy());\n      return newDeferredExecutor;\n    }\n  }();\n\n  auto sf = Future<T>(this->core_).thenTryInline(static_cast<F&&>(func)).semi();\n  this->core_ = nullptr;\n  // Carry deferred executor through chain as constructor from Future will\n  // nullify it\n  sf.setExecutor(std::move(deferredExecutor));\n  return sf;\n}\n\ntemplate <class T>\ntemplate <typename F>\nSemiFuture<\n    typename futures::detail::tryExecutorCallableResult<T, F>::value_type>\nSemiFuture<T>::deferExTry(F&& func) && {\n  auto deferredExecutorPtr = this->getDeferredExecutor();\n  futures::detail::DeferredWrapper deferredExecutor = [&]() mutable {\n    if (deferredExecutorPtr) {\n      return deferredExecutorPtr->copy();\n    } else {\n      auto newDeferredExecutor = futures::detail::DeferredExecutor::create();\n      this->setExecutor(\n          futures::detail::KeepAliveOrDeferred{newDeferredExecutor->copy()});\n      return newDeferredExecutor;\n    }\n  }();\n\n  auto sf =\n      Future<T>(this->core_)\n          .thenExTryInline(\n              [func_2 = static_cast<F&&>(func)](\n                  folly::Executor::KeepAlive<>&& keepAlive,\n                  folly::Try<T>&& val) mutable {\n                return static_cast<F&&>(func_2)(\n                    std::move(keepAlive), static_cast<decltype(val)>(val));\n              })\n          .semi();\n  this->core_ = nullptr;\n  // Carry deferred executor through chain as constructor from Future will\n  // nullify it\n  sf.setExecutor(\n      futures::detail::KeepAliveOrDeferred{std::move(deferredExecutor)});\n  return sf;\n}\n\ntemplate <class T>\ntemplate <typename F>\nSemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>\nSemiFuture<T>::deferValue(F&& func) && {\n  return std::move(*this).defer(\n      [f = static_cast<F&&>(func)](folly::Try<T>&& t) mutable {\n        return futures::detail::wrapInvoke(std::move(t), static_cast<F&&>(f));\n      });\n}\n\ntemplate <class T>\ntemplate <typename F>\nSemiFuture<\n    typename futures::detail::valueExecutorCallableResult<T, F>::value_type>\nSemiFuture<T>::deferExValue(F&& func) && {\n  return std::move(*this).deferExTry(\n      [f = static_cast<F&&>(func)](\n          folly::Executor::KeepAlive<> ka, folly::Try<T>&& t) mutable {\n        return static_cast<F&&>(f)(ka, t.template get<false, T&&>());\n      });\n}\n\ntemplate <class T>\ntemplate <class ExceptionType, class F>\nSemiFuture<T> SemiFuture<T>::deferError(tag_t<ExceptionType>, F&& func) && {\n  return std::move(*this).defer(\n      [func_2 = static_cast<F&&>(func)](Try<T>&& t) mutable {\n        if (const auto* e = t.template tryGetExceptionObject<ExceptionType>()) {\n          return makeSemiFutureWith([&]() mutable {\n            return static_cast<F&&>(func_2)(*e);\n          });\n        } else {\n          return makeSemiFuture<T>(std::move(t));\n        }\n      });\n}\n\ntemplate <class T>\ntemplate <class F>\nSemiFuture<T> SemiFuture<T>::deferError(F&& func) && {\n  return std::move(*this).defer(\n      [func_2 = static_cast<F&&>(func)](Try<T> t) mutable {\n        if (t.hasException()) {\n          return makeSemiFutureWith([&]() mutable {\n            return static_cast<F&&>(func_2)(std::move(t.exception()));\n          });\n        } else {\n          return makeSemiFuture<T>(std::move(t));\n        }\n      });\n}\n\ntemplate <class T>\ntemplate <class F>\nSemiFuture<T> SemiFuture<T>::deferEnsure(F&& func) && {\n  return std::move(*this).defer(\n      [func_2 = static_cast<F&&>(func)](Try<T>&& t) mutable {\n        static_cast<F&&>(func_2)();\n        return makeSemiFuture<T>(std::move(t));\n      });\n}\n\ntemplate <class T>\nSemiFuture<Unit> SemiFuture<T>::unit() && {\n  return std::move(*this).deferValue(variadic_noop);\n}\n\ntemplate <typename T>\nSemiFuture<T> SemiFuture<T>::delayed(HighResDuration dur, Timekeeper* tk) && {\n  return collectAll(*this, futures::sleep(dur, tk))\n      .deferValue([](std::tuple<Try<T>, Try<Unit>> tup) {\n        Try<T>& t = std::get<0>(tup);\n        return makeFuture<T>(std::move(t));\n      });\n}\n\ntemplate <class T>\nFuture<T> Future<T>::makeEmpty() {\n  return Future<T>(futures::detail::EmptyConstruct{});\n}\n\ntemplate <class T>\nFuture<T>::Future(Future<T>&& other) noexcept\n    : futures::detail::FutureBase<T>(std::move(other)) {}\n\ntemplate <class T>\nFuture<T>& Future<T>::operator=(Future<T>&& other) noexcept {\n  this->assign(std::move(other));\n  return *this;\n}\n\n// unwrap\n\ntemplate <class T>\ntemplate <class F>\ntypename std::\n    enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type\n    Future<T>::unwrap() && {\n  return std::move(*this).thenValue(\n      [](Future<typename isFuture<T>::Inner> internal_future) {\n        return internal_future;\n      });\n}\n\ntemplate <class T>\nFuture<T> Future<T>::via(Executor::KeepAlive<> executor) && {\n  folly::async_tracing::logFutureVia(this->getExecutor(), executor.get());\n\n  this->setExecutor(std::move(executor));\n\n  auto newFuture = Future<T>(this->core_);\n  this->core_ = nullptr;\n  return newFuture;\n}\n\ntemplate <class T>\nFuture<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) && {\n  return std::move(*this).via(\n      ExecutorWithPriority::create(std::move(executor), priority));\n}\n\ntemplate <class T>\nFuture<T> Future<T>::via(Executor::KeepAlive<> executor) & {\n  folly::async_tracing::logFutureVia(this->getExecutor(), executor.get());\n\n  this->throwIfInvalid();\n  Promise<T> p;\n  auto sf = p.getSemiFuture();\n  auto func =\n      [p_2 = std::move(p)](Executor::KeepAlive<>&&, Try<T>&& t) mutable {\n        p_2.setTry(std::move(t));\n      };\n  using R = futures::detail::tryExecutorCallableResult<T, decltype(func)>;\n  this->thenImplementation(\n      std::move(func), R{}, futures::detail::InlineContinuation::forbid);\n  // Construct future from semifuture manually because this may not have\n  // an executor set due to legacy code. This means we can bypass the executor\n  // check in SemiFuture::via\n  auto f = Future<T>(sf.core_);\n  sf.core_ = nullptr;\n  return std::move(f).via(std::move(executor));\n}\n\ntemplate <class T>\nFuture<T> Future<T>::via(Executor::KeepAlive<> executor, int8_t priority) & {\n  return this->via(ExecutorWithPriority::create(std::move(executor), priority));\n}\n\ntemplate <typename T>\ntemplate <typename R, typename Caller, typename... Args>\nFuture<typename isFuture<R>::Inner> Future<T>::then(\n    R (Caller::*func)(Args...), Caller* instance) && {\n  using FirstArg =\n      remove_cvref_t<typename futures::detail::ArgType<Args...>::FirstArg>;\n\n  return std::move(*this).thenTry([instance, func](Try<T>&& t) {\n    return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);\n  });\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::tryCallableResult<T, F>::value_type>\nFuture<T>::thenTry(F&& func) && {\n  auto lambdaFunc =\n      [f = static_cast<F&&>(func)](\n          folly::Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable {\n        return static_cast<F&&>(f)(std::move(t));\n      };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::forbid;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::tryCallableResult<T, F>::value_type>\nFuture<T>::thenTryInline(F&& func) && {\n  auto lambdaFunc =\n      [f = static_cast<F&&>(func)](\n          folly::Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable {\n        return static_cast<F&&>(f)(std::move(t));\n      };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::permit;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>\nFuture<T>::thenExTry(F&& func) && {\n  auto lambdaFunc = [f = static_cast<F&&>(func)](\n                        Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {\n    // Enforce that executor cannot be null\n    DCHECK(ka);\n    return static_cast<F&&>(f)(std::move(ka), std::move(t));\n  };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::forbid;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>\nFuture<T>::thenExTryInline(F&& func) && {\n  auto lambdaFunc = [f = static_cast<F&&>(func)](\n                        Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {\n    // Enforce that executor cannot be null\n    DCHECK(ka);\n    return static_cast<F&&>(f)(std::move(ka), std::move(t));\n  };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::permit;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::valueCallableResult<T, F>::value_type>\nFuture<T>::thenValue(F&& func) && {\n  auto lambdaFunc = [f = static_cast<F&&>(func)](\n                        Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable {\n    return futures::detail::wrapInvoke(std::move(t), static_cast<F&&>(f));\n  };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::forbid;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::valueCallableResult<T, F>::value_type>\nFuture<T>::thenValueInline(F&& func) && {\n  auto lambdaFunc = [f = static_cast<F&&>(func)](\n                        Executor::KeepAlive<>&&, folly::Try<T>&& t) mutable {\n    return futures::detail::wrapInvoke(std::move(t), static_cast<F&&>(f));\n  };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::permit;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::valueExecutorCallableResult<T, F>::value_type>\nFuture<T>::thenExValue(F&& func) && {\n  auto lambdaFunc = [f = static_cast<F&&>(func)](\n                        Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {\n    // Enforce that executor cannot be null\n    DCHECK(ka);\n    return static_cast<F&&>(f)(std::move(ka), t.template get<false, T&&>());\n  };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::forbid;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <typename F>\nFuture<typename futures::detail::valueExecutorCallableResult<T, F>::value_type>\nFuture<T>::thenExValueInline(F&& func) && {\n  auto lambdaFunc = [f = static_cast<F&&>(func)](\n                        Executor::KeepAlive<>&& ka, folly::Try<T>&& t) mutable {\n    // Enforce that executor cannot be null\n    DCHECK(ka);\n    return static_cast<F&&>(f)(std::move(ka), t.template get<false, T&&>());\n  };\n  using W = decltype(lambdaFunc);\n  using R = futures::detail::tryExecutorCallableResult<T, W>;\n  auto policy = futures::detail::InlineContinuation::permit;\n  return this->thenImplementation(static_cast<W&&>(lambdaFunc), R{}, policy);\n}\n\ntemplate <class T>\ntemplate <class ExceptionType, class F>\ntypename std::enable_if<\n    isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n    Future<T>>::type\nFuture<T>::thenError(tag_t<ExceptionType>, F&& func) && {\n  return std::move(*this).thenErrorImpl(\n      tag_t<ExceptionType>{}, std::forward<F>(func));\n}\n\ntemplate <class T>\ntemplate <class ExceptionType, class F>\n// typename std::enable_if<\n//     isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n//     Future<T>>::type\nFuture<T> Future<T>::thenErrorInline(tag_t<ExceptionType>, F&& func) && {\n  return std::move(*this).thenErrorImpl(\n      tag_t<ExceptionType>{},\n      std::forward<F>(func),\n      futures::detail::InlineContinuation::permit);\n}\n\ntemplate <class T>\ntemplate <class ExceptionType, class F>\ntypename std::enable_if<\n    isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n    Future<T>>::type\nFuture<T>::thenErrorImpl(\n    tag_t<ExceptionType>,\n    F&& func,\n    futures::detail::InlineContinuation allowInline) && {\n  Promise<T> p;\n  p.core_->initCopyInterruptHandlerFrom(this->getCore());\n  auto sf = p.getSemiFuture();\n  auto* ePtr = this->getExecutor();\n  auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());\n\n  this->setCallback_(\n      [state = futures::detail::makeCoreCallbackState(\n           std::move(p), static_cast<F&&>(func)),\n       allowInline](Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {\n        if (const auto* ex = t.template tryGetExceptionObject<\n                             std::remove_reference_t<ExceptionType>>()) {\n          auto tf2 = state.tryInvoke(*ex);\n          if (tf2.hasException()) {\n            state.setException(std::move(ka), std::move(tf2.exception()));\n          } else {\n            tf2->setCallback_(\n                [p_2 = state.stealPromise()](\n                    Executor::KeepAlive<>&& innerKA, Try<T>&& t3) mutable {\n                  p_2.setTry(std::move(innerKA), std::move(t3));\n                },\n                allowInline);\n          }\n        } else {\n          state.setTry(std::move(ka), std::move(t));\n        }\n      },\n      allowInline);\n\n  return std::move(sf).via(std::move(e));\n}\n\ntemplate <class T>\ntemplate <class ExceptionType, class F>\ntypename std::enable_if<\n    !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n    Future<T>>::type\nFuture<T>::thenError(tag_t<ExceptionType>, F&& func) && {\n  return std::move(*this).thenErrorImpl(\n      tag_t<ExceptionType>{}, std::forward<F>(func));\n}\n\ntemplate <class T>\ntemplate <class ExceptionType, class F>\ntypename std::enable_if<\n    !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n    Future<T>>::type\nFuture<T>::thenErrorImpl(\n    tag_t<ExceptionType>,\n    F&& func,\n    futures::detail::InlineContinuation allowInline) && {\n  Promise<T> p;\n  p.core_->initCopyInterruptHandlerFrom(this->getCore());\n  auto sf = p.getSemiFuture();\n  auto* ePtr = this->getExecutor();\n  auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());\n\n  this->setCallback_(\n      [state = futures::detail::makeCoreCallbackState(\n           std::move(p), static_cast<F&&>(func))](\n          Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {\n        if (const auto* ex = t.template tryGetExceptionObject<\n                             std::remove_reference_t<ExceptionType>>()) {\n          state.setTry(\n              std::move(ka), makeTryWith([&] { return state.invoke(*ex); }));\n        } else {\n          state.setTry(std::move(ka), std::move(t));\n        }\n      },\n      allowInline);\n\n  return std::move(sf).via(std::move(e));\n}\n\ntemplate <class T>\ntemplate <class F>\ntypename std::enable_if<\n    isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n    Future<T>>::type\nFuture<T>::thenError(F&& func) && {\n  return std::move(*this).thenErrorImpl(std::forward<F>(func));\n}\n\ntemplate <class T>\ntemplate <class F>\ntypename std::enable_if<\n    !isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n    Future<T>>::type\nFuture<T>::thenError(F&& func) && {\n  return std::move(*this).thenErrorImpl(std::forward<F>(func));\n}\n\ntemplate <class T>\ntemplate <class F>\nFuture<T> Future<T>::thenErrorInline(F&& func) && {\n  return std::move(*this).thenErrorImpl(\n      std::forward<F>(func), futures::detail::InlineContinuation::permit);\n}\n\ntemplate <class T>\ntemplate <class F>\ntypename std::enable_if<\n    isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n    Future<T>>::type\nFuture<T>::thenErrorImpl(\n    F&& func, futures::detail::InlineContinuation allowInline) && {\n  auto* ePtr = this->getExecutor();\n  auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());\n\n  Promise<T> p;\n  p.core_->initCopyInterruptHandlerFrom(this->getCore());\n  auto sf = p.getSemiFuture();\n  this->setCallback_(\n      [state = futures::detail::makeCoreCallbackState(\n           std::move(p), static_cast<F&&>(func)),\n       allowInline](Executor::KeepAlive<>&& ka, Try<T> t) mutable {\n        if (t.hasException()) {\n          auto tf2 = state.tryInvoke(std::move(t.exception()));\n          if (tf2.hasException()) {\n            state.setException(std::move(ka), std::move(tf2.exception()));\n          } else {\n            tf2->setCallback_(\n                [p_2 = state.stealPromise()](\n                    Executor::KeepAlive<>&& innerKA, Try<T>&& t3) mutable {\n                  p_2.setTry(std::move(innerKA), std::move(t3));\n                },\n                allowInline);\n          }\n        } else {\n          state.setTry(std::move(ka), std::move(t));\n        }\n      },\n      allowInline);\n\n  return std::move(sf).via(std::move(e));\n}\n\ntemplate <class T>\ntemplate <class F>\ntypename std::enable_if<\n    !isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n    Future<T>>::type\nFuture<T>::thenErrorImpl(\n    F&& func, futures::detail::InlineContinuation allowInline) && {\n  auto* ePtr = this->getExecutor();\n  auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());\n\n  Promise<T> p;\n  p.core_->initCopyInterruptHandlerFrom(this->getCore());\n  auto sf = p.getSemiFuture();\n  this->setCallback_(\n      [state = futures::detail::makeCoreCallbackState(\n           std::move(p), static_cast<F&&>(func))](\n          Executor::KeepAlive<>&& ka, Try<T>&& t) mutable {\n        if (t.hasException()) {\n          state.setTry(std::move(ka), makeTryWith([&] {\n                         return state.invoke(std::move(t.exception()));\n                       }));\n        } else {\n          state.setTry(std::move(ka), std::move(t));\n        }\n      },\n      allowInline);\n\n  return std::move(sf).via(std::move(e));\n}\n\ntemplate <class T>\nFuture<Unit> Future<T>::then() && {\n  return std::move(*this).thenValue(variadic_noop);\n}\n\ntemplate <class T>\ntemplate <class F>\nFuture<T> Future<T>::ensure(F&& func) && {\n  return std::move(*this).thenTry(\n      [funcw = static_cast<F&&>(func)](Try<T>&& t) mutable {\n        static_cast<F&&>(funcw)();\n        return makeFuture(std::move(t));\n      });\n}\n\ntemplate <class T>\ntemplate <class F>\nFuture<T> Future<T>::ensureInline(F&& func) && {\n  return std::move(*this).thenTryInline(\n      [funcw = static_cast<F&&>(func)](Try<T>&& t) mutable {\n        static_cast<F&&>(funcw)();\n        return makeFuture(std::move(t));\n      });\n}\n\ntemplate <class T>\ntemplate <class F>\nFuture<T> Future<T>::onTimeout(\n    HighResDuration dur, F&& func, Timekeeper* tk) && {\n  return std::move(*this).within(dur, tk).thenError(\n      tag_t<FutureTimeout>{},\n      [funcw = static_cast<F&&>(func)](auto const&) mutable {\n        return static_cast<F&&>(funcw)();\n      });\n}\n\ntemplate <class Func>\nauto via(Executor::KeepAlive<> x, Func&& func)\n    -> Future<typename isFutureOrSemiFuture<\n        decltype(static_cast<Func&&>(func)())>::Inner> {\n  return via(std::move(x))\n      .thenValue([f = static_cast<Func&&>(func)](auto&&) mutable {\n        return static_cast<Func&&>(f)();\n      });\n}\n\n// makeFuture\n\ntemplate <class T>\nFuture<typename std::decay<T>::type> makeFuture(T&& t) {\n  return makeFuture(Try<typename std::decay<T>::type>(static_cast<T&&>(t)));\n}\n\ninline Future<Unit> makeFuture() {\n  return makeFuture(Unit{});\n}\n\n// makeFutureWith(Future<T>()) -> Future<T>\ntemplate <class F>\ntypename std::\n    enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type\n    makeFutureWith(F&& func) {\n  using InnerType = typename isFuture<invoke_result_t<F>>::Inner;\n  try {\n    return static_cast<F&&>(func)();\n  } catch (...) {\n    return makeFuture<InnerType>(exception_wrapper(current_exception()));\n  }\n}\n\n// makeFutureWith(T()) -> Future<T>\n// makeFutureWith(void()) -> Future<Unit>\ntemplate <class F>\ntypename std::enable_if<\n    !(isFuture<invoke_result_t<F>>::value),\n    Future<lift_unit_t<invoke_result_t<F>>>>::type\nmakeFutureWith(F&& func) {\n  using LiftedResult = lift_unit_t<invoke_result_t<F>>;\n  return makeFuture<LiftedResult>(makeTryWith([&func]() mutable {\n    return static_cast<F&&>(func)();\n  }));\n}\n\ntemplate <class T>\nFuture<T> makeFuture(std::exception_ptr const& e) {\n  return makeFuture(Try<T>(e));\n}\n\ntemplate <class T>\nFuture<T> makeFuture(exception_wrapper ew) {\n  return makeFuture(Try<T>(std::move(ew)));\n}\n\ntemplate <class T, class E>\nstd::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, Future<T>>\nmakeFuture(E&& e) {\n  return makeFuture(Try<T>(make_exception_wrapper<E>(std::forward<E>(e))));\n}\n\n// DEPRECATED const-ref overload for users who EXPLICITLY specify BOTH template\n// parameters\ntemplate <class T, class E>\nstd::enable_if_t<std::is_base_of_v<std::exception, E>, Future<T>> makeFuture(\n    const folly::type_identity_t<E>& e) {\n  return makeFuture(Try<T>(make_exception_wrapper<E>(e)));\n}\n\ntemplate <class T>\nFuture<T> makeFuture(Try<T> t) {\n  return Future<T>(Future<T>::Core::make(std::move(t)));\n}\n\n// via\nFuture<Unit> via(Executor::KeepAlive<> executor) {\n  return makeFuture().via(std::move(executor));\n}\n\nFuture<Unit> via(Executor::KeepAlive<> executor, int8_t priority) {\n  return makeFuture().via(std::move(executor), priority);\n}\n\nnamespace futures {\nnamespace detail {\n\ntemplate <typename V, typename... Fs, std::size_t... Is>\nFOLLY_ERASE void foreach_(std::index_sequence<Is...>, V&& v, Fs&&... fs) {\n  ((void(v(index_constant<Is>{}, static_cast<Fs&&>(fs)))), ...);\n}\ntemplate <typename V, typename... Fs>\nFOLLY_ERASE void foreach(V&& v, Fs&&... fs) {\n  using _ = std::index_sequence_for<Fs...>;\n  foreach_(_{}, static_cast<V&&>(v), static_cast<Fs&&>(fs)...);\n}\n\ntemplate <typename T>\nfutures::detail::DeferredExecutor* getDeferredExecutor(SemiFuture<T>& future) {\n  return future.getDeferredExecutor();\n}\n\ntemplate <typename T>\nfutures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future) {\n  return future.stealDeferredExecutor();\n}\n\ntemplate <typename T>\nfutures::detail::DeferredWrapper stealDeferredExecutor(Future<T>&) {\n  return {};\n}\n\ntemplate <typename... Ts>\nvoid stealDeferredExecutorsVariadic(\n    std::vector<futures::detail::DeferredWrapper>& executors, Ts&... ts) {\n  foreach(\n      [&](auto, auto& future) {\n        if (auto executor = stealDeferredExecutor(future)) {\n          executors.push_back(std::move(executor));\n        }\n      },\n      ts...);\n}\n\ntemplate <class InputIterator>\nvoid stealDeferredExecutors(\n    std::vector<futures::detail::DeferredWrapper>& executors,\n    InputIterator first,\n    InputIterator last) {\n  for (auto it = first; it != last; ++it) {\n    if (auto executor = stealDeferredExecutor(*it)) {\n      executors.push_back(std::move(executor));\n    }\n  }\n}\n} // namespace detail\n} // namespace futures\n\n// collectAll (variadic)\n\ntemplate <typename... Fs>\nSemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>\ncollectAll(Fs&&... fs) {\n  using Result = std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>;\n  struct Context {\n    ~Context() { p.setValue(std::move(results)); }\n    Promise<Result> p;\n    Result results;\n  };\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutorsVariadic(executors, fs...);\n\n  auto ctx = std::make_shared<Context>();\n  futures::detail::foreach(\n      [&](auto i, auto&& f) {\n        f.setCallback_([i, ctx](auto&&, auto&& t) {\n          std::get<i.value>(ctx->results) = std::move(t);\n        });\n      },\n      static_cast<Fs&&>(fs)...);\n\n  auto future = ctx->p.getSemiFuture();\n  if (!executors.empty()) {\n    auto work = [](Try<typename decltype(future)::value_type>&& t) {\n      return std::move(t).value();\n    };\n    future = std::move(future).defer(work);\n    auto deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\ntemplate <typename... Fs>\nFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>\ncollectAllUnsafe(Fs&&... fs) {\n  return collectAll(static_cast<Fs&&>(fs)...).toUnsafeFuture();\n}\n\n// collectAll (iterator)\n\ntemplate <class InputIterator>\nSemiFuture<std::vector<\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>\ncollectAll(InputIterator first, InputIterator last) {\n  using F = typename std::iterator_traits<InputIterator>::value_type;\n  using T = typename F::value_type;\n\n  struct Context {\n    explicit Context(size_t n) : results(n), count(n) {}\n    ~Context() {\n      futures::detail::setTry(\n          p, std::move(ka), Try<std::vector<Try<T>>>(std::move(results)));\n    }\n    Promise<std::vector<Try<T>>> p;\n    Executor::KeepAlive<> ka;\n    std::vector<Try<T>> results;\n    std::atomic<size_t> count;\n  };\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutors(executors, first, last);\n\n  auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)));\n\n  for (size_t i = 0; first != last; ++first, ++i) {\n    first->setCallback_(\n        [i, ctx](Executor::KeepAlive<>&& ka, Try<T>&& t) {\n          ctx->results[i] = std::move(t);\n          if (ctx->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {\n            ctx->ka = std::move(ka);\n          }\n        },\n        futures::detail::InlineContinuation::permit);\n  }\n\n  auto future = ctx->p.getSemiFuture();\n  if (!executors.empty()) {\n    future = std::move(future).defer(\n        [](Try<typename decltype(future)::value_type>&& t) {\n          return std::move(t).value();\n        });\n    auto deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\ntemplate <class InputIterator>\nFuture<std::vector<\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>\ncollectAllUnsafe(InputIterator first, InputIterator last) {\n  return collectAll(first, last).toUnsafeFuture();\n}\n\n// collect (iterator)\n\ntemplate <class InputIterator>\nSemiFuture<std::vector<\n    typename std::iterator_traits<InputIterator>::value_type::value_type>>\ncollect(InputIterator first, InputIterator last) {\n  using F = typename std::iterator_traits<InputIterator>::value_type;\n  using T = typename F::value_type;\n\n  struct Context {\n    explicit Context(size_t n) : result(n), count(n) { finalResult.reserve(n); }\n    ~Context() {\n      if (!threw.load(std::memory_order_relaxed)) {\n        // map Optional<T> -> T\n        for (auto& value : result) {\n          // if any of the input futures were off the end of a weakRef(), the\n          // logic added in setCallback_ will not execute as an executor\n          // weakRef() drops all callbacks added silently without executing them\n          if (!value.has_value()) {\n            p.setException(BrokenPromise{tag<std::vector<T>>});\n            return;\n          }\n\n          finalResult.push_back(std::move(value.value()));\n        }\n\n        futures::detail::setTry(\n            p, std::move(ka), Try<std::vector<T>>(std::move(finalResult)));\n      }\n    }\n    Promise<std::vector<T>> p;\n    Executor::KeepAlive<> ka;\n    std::vector<Optional<T>> result;\n    std::vector<T> finalResult;\n    std::atomic<bool> threw{false};\n    std::atomic<size_t> count;\n  };\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutors(executors, first, last);\n\n  auto ctx = std::make_shared<Context>(std::distance(first, last));\n  for (size_t i = 0; first != last; ++first, ++i) {\n    first->setCallback_(\n        [i, ctx](Executor::KeepAlive<>&& ka, Try<T>&& t) {\n          if (t.hasException()) {\n            if (!ctx->threw.exchange(true, std::memory_order_relaxed)) {\n              ctx->p.setException(std::move(t.exception()));\n            }\n          } else if (!ctx->threw.load(std::memory_order_relaxed)) {\n            if (ctx->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {\n              ctx->ka = std::move(ka);\n            }\n            ctx->result[i] = std::move(t.value());\n          }\n        },\n        futures::detail::InlineContinuation::permit);\n  }\n\n  auto future = ctx->p.getSemiFuture();\n  if (!executors.empty()) {\n    auto work = [](Try<typename decltype(future)::value_type>&& t) {\n      return std::move(t);\n    };\n    future = std::move(future).defer(work);\n    const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\ntemplate <class InputIterator>\nFuture<std::vector<\n    typename std::iterator_traits<InputIterator>::value_type::value_type>>\ncollectUnsafe(InputIterator first, InputIterator last) {\n  return collect(first, last).toUnsafeFuture();\n}\n\n// collect (variadic)\n\ntemplate <typename... Fs>\nSemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect(\n    Fs&&... fs) {\n  using Result = std::tuple<typename remove_cvref_t<Fs>::value_type...>;\n  struct Context {\n    ~Context() {\n      if (!threw.load(std::memory_order_relaxed)) {\n        // if any of the input futures were off the end of a weakRef(), the\n        // logic added in setCallback_ will not execute as an executor\n        // weakRef() drops all callbacks added silently without executing them\n        auto brokenPromise = false;\n        folly::for_each(results, [&](auto& result) {\n          if (!result.hasValue() && !result.hasException()) {\n            brokenPromise = true;\n          }\n        });\n\n        if (brokenPromise) {\n          p.setException(BrokenPromise{tag<Result>});\n        } else {\n          auto res = unwrapTryTuple(std::move(results));\n          futures::detail::setTry(\n              p, std::move(ka), Try<decltype(res)>(std::move(res)));\n        }\n      }\n    }\n    Promise<Result> p;\n    std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...> results;\n    Executor::KeepAlive<> ka;\n    std::atomic<bool> threw{false};\n    std::atomic<size_t> count{std::tuple_size<decltype(results)>::value};\n  };\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutorsVariadic(executors, fs...);\n\n  auto ctx = std::make_shared<Context>();\n  futures::detail::foreach(\n      [&](auto i, auto&& f) {\n        f.setCallback_(\n            [i, ctx](Executor::KeepAlive<>&& ka, auto&& t) {\n              if (t.hasException()) {\n                if (!ctx->threw.exchange(true, std::memory_order_relaxed)) {\n                  ctx->p.setException(std::move(t.exception()));\n                }\n              } else if (!ctx->threw.load(std::memory_order_relaxed)) {\n                if (ctx->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {\n                  ctx->ka = std::move(ka);\n                }\n                std::get<i.value>(ctx->results) = std::move(t);\n              }\n            },\n            futures::detail::InlineContinuation::permit);\n      },\n      static_cast<Fs&&>(fs)...);\n\n  auto future = ctx->p.getSemiFuture();\n  if (!executors.empty()) {\n    auto work = [](Try<typename decltype(future)::value_type>&& t) {\n      return std::move(t).value();\n    };\n    future = std::move(future).defer(work);\n    const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\ntemplate <typename... Fs>\nFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collectUnsafe(\n    Fs&&... fs) {\n  return collect(static_cast<Fs&&>(fs)...).toUnsafeFuture();\n}\n\n// collectAny (iterator)\n\ntemplate <class InputIterator>\nSemiFuture<std::pair<\n    size_t,\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>\ncollectAny(InputIterator first, InputIterator last) {\n  using F = typename std::iterator_traits<InputIterator>::value_type;\n  using T = typename F::value_type;\n\n  struct Context {\n    Promise<std::pair<size_t, Try<T>>> p;\n    std::atomic<bool> done{false};\n  };\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutors(executors, first, last);\n\n  auto ctx = std::make_shared<Context>();\n  for (size_t i = 0; first != last; ++first, ++i) {\n    first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) {\n      if (!ctx->done.exchange(true, std::memory_order_relaxed)) {\n        ctx->p.setValue(std::make_pair(i, std::move(t)));\n      }\n    });\n  }\n  auto future = ctx->p.getSemiFuture();\n  if (!executors.empty()) {\n    future = std::move(future).defer(\n        [](Try<typename decltype(future)::value_type>&& t) {\n          return std::move(t).value();\n        });\n    const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\n// collectAnyWithoutException (iterator)\n\ntemplate <class InputIterator>\nSemiFuture<std::pair<\n    size_t,\n    typename std::iterator_traits<InputIterator>::value_type::value_type>>\ncollectAnyWithoutException(InputIterator first, InputIterator last) {\n  using F = typename std::iterator_traits<InputIterator>::value_type;\n  using T = typename F::value_type;\n\n  struct Context {\n    Context(size_t n) : nTotal(n) {}\n    Promise<std::pair<size_t, T>> p;\n    std::atomic<bool> done{false};\n    std::atomic<size_t> nFulfilled{0};\n    size_t nTotal;\n  };\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutors(executors, first, last);\n\n  auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)));\n  for (size_t i = 0; first != last; ++first, ++i) {\n    first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) {\n      if (!t.hasException() &&\n          !ctx->done.exchange(true, std::memory_order_relaxed)) {\n        ctx->p.setValue(std::make_pair(i, std::move(t.value())));\n      } else if (\n          ctx->nFulfilled.fetch_add(1, std::memory_order_relaxed) + 1 ==\n          ctx->nTotal) {\n        ctx->p.setException(t.exception());\n      }\n    });\n  }\n\n  auto future = ctx->p.getSemiFuture();\n  if (!executors.empty()) {\n    future = std::move(future).defer(\n        [](Try<typename decltype(future)::value_type>&& t) {\n          return std::move(t).value();\n        });\n    const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\n// collectN (iterator)\n\ntemplate <class InputIterator>\nSemiFuture<std::vector<std::pair<\n    size_t,\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>\ncollectN(InputIterator first, InputIterator last, size_t n) {\n  using F = typename std::iterator_traits<InputIterator>::value_type;\n  using T = typename F::value_type;\n  using Result = std::vector<std::pair<size_t, Try<T>>>;\n\n  struct Context {\n    explicit Context(size_t numFutures, size_t min_)\n        : v(numFutures), min(min_) {}\n\n    std::vector<Optional<Try<T>>> v;\n    size_t min;\n    std::atomic<size_t> completed = {0}; // # input futures completed\n    std::atomic<size_t> stored = {0}; // # output values stored\n    Promise<Result> p;\n  };\n\n  assert(n > 0);\n  assert(std::distance(first, last) >= 0);\n\n  if (size_t(std::distance(first, last)) < n) {\n    return SemiFuture<Result>(\n        exception_wrapper(std::runtime_error(\"Not enough futures\")));\n  }\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutors(executors, first, last);\n\n  // for each completed Future, increase count and add to vector, until we\n  // have n completed futures at which point we fulfil our Promise with the\n  // vector\n  auto ctx = std::make_shared<Context>(size_t(std::distance(first, last)), n);\n  for (size_t i = 0; first != last; ++first, ++i) {\n    first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<T>&& t) {\n      // relaxed because this guards control but does not guard data\n      auto const c = 1 + ctx->completed.fetch_add(1, std::memory_order_relaxed);\n      if (c > ctx->min) {\n        return;\n      }\n      ctx->v[i] = std::move(t);\n\n      // release because the stored values in all threads must be visible below\n      // acquire because no stored value is permitted to be fetched early\n      auto const s = 1 + ctx->stored.fetch_add(1, std::memory_order_acq_rel);\n      if (s < ctx->min) {\n        return;\n      }\n      Result result;\n      result.reserve(ctx->completed.load());\n      for (size_t j = 0; j < ctx->v.size(); ++j) {\n        auto& entry = ctx->v[j];\n        if (entry.hasValue()) {\n          result.emplace_back(j, std::move(entry).value());\n        }\n      }\n      ctx->p.setTry(Try<Result>(std::move(result)));\n    });\n  }\n\n  auto future = ctx->p.getSemiFuture();\n  if (!executors.empty()) {\n    future = std::move(future).defer(\n        [](Try<typename decltype(future)::value_type>&& t) {\n          return std::move(t).value();\n        });\n    const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\n// reduce (iterator)\n\ntemplate <class It, class T, class F>\nFuture<T> reduce(It first, It last, T&& initial, F&& func) {\n  if (first == last) {\n    return makeFuture(static_cast<T&&>(initial));\n  }\n\n  typedef typename std::iterator_traits<It>::value_type::value_type ItT;\n  typedef typename std::\n      conditional<is_invocable<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type\n          Arg;\n  typedef isTry<Arg> IsTry;\n\n  auto sfunc = std::make_shared<std::decay_t<F>>(static_cast<F&&>(func));\n\n  auto f = std::move(*first).thenTry(\n      [initial_2 = static_cast<T&&>(initial), sfunc](Try<ItT>&& head) mutable {\n        return (*sfunc)(\n            std::move(initial_2), head.template get<IsTry::value, Arg&&>());\n      });\n\n  for (++first; first != last; ++first) {\n    f = collectAllUnsafe(f, *first).thenValue(\n        [sfunc](std::tuple<Try<T>, Try<ItT>>&& t) {\n          return (*sfunc)(\n              std::move(std::get<0>(t).value()),\n              // Either return a ItT&& or a Try<ItT>&& depending\n              // on the type of the argument of func.\n              std::get<1>(t).template get<IsTry::value, Arg&&>());\n        });\n  }\n\n  return f;\n}\n\n// window (collection)\n\ntemplate <class Collection, class F, class ItT, class Result>\nstd::vector<Future<Result>> window(Collection input, F func, size_t n) {\n  // Use global QueuedImmediateExecutor singleton to avoid stack overflow.\n  auto executor = &QueuedImmediateExecutor::instance();\n  return window(executor, std::move(input), std::move(func), n);\n}\n\ntemplate <class F>\nauto window(size_t times, F func, size_t n)\n    -> std::vector<invoke_result_t<F, size_t>> {\n  return window(futures::detail::WindowFakeVector(times), std::move(func), n);\n}\n\ntemplate <class Collection, class F, class ItT, class Result>\nstd::vector<Future<Result>> window(\n    Executor::KeepAlive<> executor, Collection input, F func, size_t n) {\n  struct WindowContext {\n    WindowContext(\n        Executor::KeepAlive<> executor_, Collection&& input_, F&& func_)\n        : executor(std::move(executor_)),\n          input(std::move(input_)),\n          promises(input.size()),\n          func(std::move(func_)) {}\n    std::atomic<size_t> i{0};\n    Executor::KeepAlive<> executor;\n    Collection input;\n    std::vector<Promise<Result>> promises;\n    F func;\n\n    static void spawn(std::shared_ptr<WindowContext> ctx) {\n      size_t i = ctx->i.fetch_add(1, std::memory_order_relaxed);\n      if (i < ctx->input.size()) {\n        auto fut = makeSemiFutureWith([&] {\n                     return ctx->func(std::move(ctx->input[i]));\n                   }).via(ctx->executor.get());\n\n        fut.setCallback_(\n            [ctx_2 = std::move(ctx),\n             i](Executor::KeepAlive<>&& ka, Try<Result>&& t) mutable {\n              // Use futures::detail::setTry() with the KeepAlive to correctly\n              // propagate the executor down the chain for callback inlining\n              // purposes.\n              futures::detail::setTry(\n                  ctx_2->promises[i], std::move(ka), std::move(t));\n              // Chain another future onto this one\n              spawn(std::move(ctx_2));\n            });\n      }\n    }\n  };\n\n  auto max = std::min(n, input.size());\n\n  auto ctx = std::make_shared<WindowContext>(\n      executor.copy(), std::move(input), std::move(func));\n\n  // Start the first n Futures\n  for (size_t i = 0; i < max; ++i) {\n    executor->add([ctx]() mutable { WindowContext::spawn(std::move(ctx)); });\n  }\n\n  std::vector<Future<Result>> futures;\n  futures.reserve(ctx->promises.size());\n  for (auto& promise : ctx->promises) {\n    futures.emplace_back(promise.getSemiFuture().via(executor.copy()));\n  }\n\n  return futures;\n}\n\n// reduce\n\ntemplate <class T>\ntemplate <class In, class F>\nFuture<In> Future<T>::reduce(In&& initial, F&& func) && {\n  return std::move(*this).thenValue(\n      [minitial = static_cast<In&&>(initial),\n       mfunc = static_cast<F&&>(func)](T&& vals) mutable {\n        auto ret = std::move(minitial);\n        for (auto& val : vals) {\n          ret = mfunc(std::move(ret), std::move(val));\n        }\n        return ret;\n      });\n}\n\n// unorderedReduce (iterator)\n\ntemplate <class It, class T, class F>\nSemiFuture<T> unorderedReduceSemiFuture(It first, It last, T initial, F func) {\n  using ItF = typename std::iterator_traits<It>::value_type;\n  using ItT = typename ItF::value_type;\n  using Arg = MaybeTryArg<F, T, ItT>;\n\n  if (first == last) {\n    return makeFuture(std::move(initial));\n  }\n\n  typedef isTry<Arg> IsTry;\n\n  struct Context {\n    Context(T&& memo, F&& fn, size_t n)\n        : lock_(),\n          memo_(makeFuture<T>(std::move(memo))),\n          func_(std::move(fn)),\n          numThens_(0),\n          numFutures_(n),\n          promise_() {}\n\n    folly::MicroSpinLock lock_; // protects memo_ and numThens_\n    Future<T> memo_;\n    F func_;\n    size_t numThens_; // how many Futures completed and called .then()\n    size_t numFutures_; // how many Futures in total\n    Promise<T> promise_;\n  };\n\n  struct Fulfill {\n    void operator()(Promise<T>&& p, T&& v) const { p.setValue(std::move(v)); }\n    void operator()(Promise<T>&& p, Future<T>&& f) const {\n      f.setCallback_(\n          [p_2 = std::move(p)](Executor::KeepAlive<>&&, Try<T>&& t) mutable {\n            p_2.setTry(std::move(t));\n          });\n    }\n  };\n\n  std::vector<futures::detail::DeferredWrapper> executors;\n  futures::detail::stealDeferredExecutors(executors, first, last);\n\n  auto ctx = std::make_shared<Context>(\n      std::move(initial), std::move(func), std::distance(first, last));\n  for (size_t i = 0; first != last; ++first, ++i) {\n    first->setCallback_([i, ctx](Executor::KeepAlive<>&&, Try<ItT>&& t) {\n      (void)i;\n      // Futures can be completed in any order, simultaneously.\n      // To make this non-blocking, we create a new Future chain in\n      // the order of completion to reduce the values.\n      // The spinlock just protects chaining a new Future, not actually\n      // executing the reduce, which should be really fast.\n      Promise<T> p;\n      auto f = p.getFuture();\n      {\n        folly::MSLGuard lock(ctx->lock_);\n        f = std::exchange(ctx->memo_, std::move(f));\n        if (++ctx->numThens_ == ctx->numFutures_) {\n          // After reducing the value of the last Future, fulfill the Promise\n          ctx->memo_.setCallback_([ctx](Executor::KeepAlive<>&&, Try<T>&& t2) {\n            ctx->promise_.setValue(std::move(t2));\n          });\n        }\n      }\n      f.setCallback_([ctx, mp = std::move(p), mt = std::move(t)](\n                         Executor::KeepAlive<>&&, Try<T>&& v) mutable {\n        if (v.hasValue()) {\n          exception_wrapper ew;\n          try {\n            Fulfill{}(\n                std::move(mp),\n                ctx->func_(\n                    std::move(v.value()),\n                    mt.template get<IsTry::value, Arg&&>()));\n          } catch (...) {\n            ew = exception_wrapper{current_exception()};\n          }\n          if (ew) {\n            mp.setException(std::move(ew));\n          }\n        } else {\n          mp.setTry(std::move(v));\n        }\n      });\n    });\n  }\n\n  auto future = ctx->promise_.getSemiFuture();\n  if (!executors.empty()) {\n    future = std::move(future).defer(\n        [](Try<typename decltype(future)::value_type>&& t) {\n          return std::move(t).value();\n        });\n    const auto& deferredExecutor = futures::detail::getDeferredExecutor(future);\n    deferredExecutor->setNestedExecutors(std::move(executors));\n  }\n  return future;\n}\n\ntemplate <class It, class T, class F>\nFuture<T> unorderedReduce(It first, It last, T initial, F func) {\n  return unorderedReduceSemiFuture(\n             first, last, std::move(initial), std::move(func))\n      .via(&InlineExecutor::instance());\n}\n\n// within\n\ntemplate <class T>\nFuture<T> Future<T>::within(HighResDuration dur, Timekeeper* tk) && {\n  return std::move(*this).within(dur, FutureTimeout(), tk);\n}\n\ntemplate <class T>\ntemplate <class E>\nFuture<T> Future<T>::within(HighResDuration dur, E e, Timekeeper* tk) && {\n  if (this->isReady()) {\n    return std::move(*this);\n  }\n\n  auto* ePtr = this->getExecutor();\n  auto exe =\n      folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());\n  return std::move(*this).semi().within(dur, e, tk).via(std::move(exe));\n}\n\nnamespace futures {\nnamespace detail {\n\nstruct WithinInterruptHandler {\n  std::weak_ptr<CoreBase> ptr;\n  void operator()(exception_wrapper const& ew) const;\n};\n\nstruct WithinContextBase {\n  using PromiseSetExceptionSig = void(WithinContextBase&, exception_wrapper&&);\n  PromiseSetExceptionSig* doPromiseSetException{};\n  exception_wrapper exception;\n  SemiFuture<Unit> thisFuture{SemiFuture<Unit>::makeEmpty()};\n  SemiFuture<Unit> afterFuture{SemiFuture<Unit>::makeEmpty()};\n  std::atomic<bool> token{false};\n  explicit WithinContextBase(\n      PromiseSetExceptionSig* fun, exception_wrapper&& e) noexcept\n      : doPromiseSetException{fun}, exception{std::move(e)} {}\n};\n\nstruct WithinAfterFutureCallback {\n  std::weak_ptr<WithinContextBase> ctx;\n  void operator()(Try<Unit>&& t);\n};\n\n} // namespace detail\n} // namespace futures\n\ntemplate <class T>\ntemplate <typename E>\nSemiFuture<T> SemiFuture<T>::within(\n    HighResDuration dur, E e, Timekeeper* tk) && {\n  if (this->isReady()) {\n    return std::move(*this);\n  }\n\n  using ContextBase = futures::detail::WithinContextBase;\n  struct Context : ContextBase {\n    static void goPromiseSetException(\n        ContextBase& base, exception_wrapper&& e) {\n      static_cast<Context&>(base).promise.setException(std::move(e));\n    }\n    Promise<T> promise;\n    explicit Context(E ex)\n        : ContextBase(goPromiseSetException, exception_wrapper(std::move(ex))) {\n    }\n  };\n\n  std::shared_ptr<Timekeeper> tks;\n  if (FOLLY_LIKELY(!tk)) {\n    tks = folly::detail::getTimekeeperSingleton();\n    tk = tks.get();\n  }\n\n  if (FOLLY_UNLIKELY(!tk)) {\n    return makeSemiFuture<T>(FutureNoTimekeeper());\n  }\n\n  auto ctx = std::make_shared<Context>(std::move(e));\n\n  ctx->thisFuture = std::move(*this).defer([ctx](Try<T>&& t) {\n    if (!ctx->token.exchange(true, std::memory_order_relaxed)) {\n      ctx->promise.setTry(std::move(t));\n      ctx->afterFuture.cancel();\n    }\n  });\n\n  // Have time keeper use a weak ptr to hold ctx,\n  // so that ctx can be deallocated as soon as the future job finished.\n  ctx->afterFuture =\n      tk->after(dur).defer(futures::detail::WithinAfterFutureCallback{ctx});\n\n  // Properly propagate interrupt values through futures chained after within()\n  ctx->promise.setInterruptHandler(\n      futures::detail::WithinInterruptHandler{\n          to_weak_ptr_aliasing(ctx, ctx->thisFuture.core_)});\n\n  // Construct the future to return, create a fresh DeferredExecutor and\n  // nest the other two inside it, in case they already carry nested executors.\n  auto fut = ctx->promise.getSemiFuture();\n  auto newDeferredExecutor = futures::detail::KeepAliveOrDeferred(\n      futures::detail::DeferredExecutor::create());\n  fut.setExecutor(std::move(newDeferredExecutor));\n\n  std::vector<folly::futures::detail::DeferredWrapper> nestedExecutors;\n  nestedExecutors.emplace_back(ctx->thisFuture.stealDeferredExecutor());\n  nestedExecutors.emplace_back(ctx->afterFuture.stealDeferredExecutor());\n  // Set trivial callbacks to treat the futures as consumed\n  ctx->thisFuture.setCallback_(variadic_noop);\n  ctx->afterFuture.setCallback_(variadic_noop);\n  futures::detail::getDeferredExecutor(fut)->setNestedExecutors(\n      std::move(nestedExecutors));\n  return fut;\n}\n\n// delayed\n\ntemplate <class T>\nFuture<T> Future<T>::delayed(HighResDuration dur, Timekeeper* tk) && {\n  auto e = this->getExecutor();\n  return collectAll(*this, futures::sleep(dur, tk))\n      .via(e ? e : &InlineExecutor::instance())\n      .thenValue([](std::tuple<Try<T>, Try<Unit>>&& tup) {\n        return makeFuture<T>(std::get<0>(std::move(tup)));\n      });\n}\n\nnamespace futures {\nnamespace detail {\n\ntemplate <class FutureType, typename T = typename FutureType::value_type>\nvoid waitImpl(FutureType& f) {\n  if (std::is_base_of<Future<T>, FutureType>::value) {\n    f = std::move(f).via(&InlineExecutor::instance());\n  }\n  // short-circuit if there's nothing to do\n  if (f.isReady()) {\n    return;\n  }\n\n  Promise<T> promise;\n  auto ret = convertFuture(promise.getSemiFuture(), f);\n  FutureBatonType baton;\n  f.setCallback_([&baton, promise_2 = std::move(promise)](\n                     Executor::KeepAlive<>&&, Try<T>&& t) mutable {\n    promise_2.setTry(std::move(t));\n    baton.post();\n  });\n  f = std::move(ret);\n  baton.wait();\n  assert(f.isReady());\n}\n\ntemplate <class T>\nFuture<T> convertFuture(SemiFuture<T>&& sf, const Future<T>& f) {\n  // Carry executor from f, inserting an inline executor if it did not have one\n  auto* exe = f.getExecutor();\n  auto newFut = std::move(sf).via(exe ? exe : &InlineExecutor::instance());\n  newFut.core_->initCopyInterruptHandlerFrom(*f.core_);\n  return newFut;\n}\n\ntemplate <class T>\nSemiFuture<T> convertFuture(SemiFuture<T>&& sf, const SemiFuture<T>&) {\n  return std::move(sf);\n}\n\ntemplate <class FutureType, typename T = typename FutureType::value_type>\nvoid waitImpl(FutureType& f, HighResDuration dur) {\n  if (std::is_base_of<Future<T>, FutureType>::value) {\n    f = std::move(f).via(&InlineExecutor::instance());\n  }\n  // short-circuit if there's nothing to do\n  if (f.isReady()) {\n    return;\n  }\n\n  Promise<T> promise;\n  auto ret = convertFuture(promise.getSemiFuture(), f);\n  auto baton = std::make_shared<FutureBatonType>();\n  f.setCallback_([baton, promise_2 = std::move(promise)](\n                     Executor::KeepAlive<>&&, Try<T>&& t) mutable {\n    promise_2.setTry(std::move(t));\n    baton->post();\n  });\n  f = std::move(ret);\n  if (baton->try_wait_for(dur)) {\n    assert(f.isReady());\n  }\n}\n\ntemplate <class T>\nvoid waitViaImpl(Future<T>& f, DrivableExecutor* e) {\n  // Set callback so to ensure that the via executor has something on it\n  // so that once the preceding future triggers this callback, drive will\n  // always have a callback to satisfy it\n  if (f.isReady()) {\n    return;\n  }\n  f = std::move(f).via(e).thenTry([](Try<T>&& t) { return std::move(t); });\n  while (!f.isReady()) {\n    e->drive();\n  }\n  assert(f.isReady());\n  f = std::move(f).via(&InlineExecutor::instance());\n}\n\ntemplate <class T, typename Rep, typename Period>\nvoid waitViaImpl(\n    Future<T>& f,\n    TimedDrivableExecutor* e,\n    const std::chrono::duration<Rep, Period>& timeout) {\n  // Set callback so to ensure that the via executor has something on it\n  // so that once the preceding future triggers this callback, drive will\n  // always have a callback to satisfy it\n  if (f.isReady()) {\n    return;\n  }\n  // Chain operations, ensuring that the executor is kept alive for the duration\n  f = std::move(f).via(e).thenValue([keepAlive = getKeepAliveToken(e)](T&& t) {\n    return std::move(t);\n  });\n  auto now = std::chrono::steady_clock::now();\n  auto deadline = now + timeout;\n  while (!f.isReady() && (now < deadline)) {\n    e->try_drive_until(deadline);\n    now = std::chrono::steady_clock::now();\n  }\n  assert(f.isReady() || (now >= deadline));\n  if (f.isReady()) {\n    f = std::move(f).via(&InlineExecutor::instance());\n  }\n}\n\n} // namespace detail\n} // namespace futures\n\ntemplate <class T>\nSemiFuture<T>& SemiFuture<T>::wait() & {\n  if (auto deferredExecutor = this->getDeferredExecutor()) {\n    // Make sure that the last callback in the future chain will be run on the\n    // WaitExecutor.\n    Promise<T> promise;\n    auto ret = promise.getSemiFuture();\n    setCallback_(\n        [p = std::move(promise)](Executor::KeepAlive<>&&, auto&& r) mutable {\n          p.setTry(std::move(r));\n        });\n    auto waitExecutor = futures::detail::WaitExecutor::create();\n    deferredExecutor->setExecutor(waitExecutor.copy());\n    while (!ret.isReady()) {\n      waitExecutor->drive();\n    }\n    waitExecutor->detach();\n    this->detach();\n    *this = std::move(ret);\n  } else {\n    futures::detail::waitImpl(*this);\n  }\n  return *this;\n}\n\ntemplate <class T>\nSemiFuture<T>&& SemiFuture<T>::wait() && {\n  return std::move(wait());\n}\n\ntemplate <class T>\nSemiFuture<T>& SemiFuture<T>::wait(HighResDuration dur) & {\n  if (auto deferredExecutor = this->getDeferredExecutor()) {\n    // Make sure that the last callback in the future chain will be run on the\n    // WaitExecutor.\n    Promise<T> promise;\n    auto ret = promise.getSemiFuture();\n    setCallback_(\n        [p = std::move(promise)](Executor::KeepAlive<>&&, auto&& r) mutable {\n          p.setTry(std::move(r));\n        });\n    auto waitExecutor = futures::detail::WaitExecutor::create();\n    auto deadline = futures::detail::WaitExecutor::Clock::now() + dur;\n    deferredExecutor->setExecutor(waitExecutor.copy());\n    while (!ret.isReady()) {\n      if (!waitExecutor->driveUntil(deadline)) {\n        break;\n      }\n    }\n    waitExecutor->detach();\n    this->detach();\n    *this = std::move(ret);\n  } else {\n    futures::detail::waitImpl(*this, dur);\n  }\n  return *this;\n}\n\ntemplate <class T>\nbool SemiFuture<T>::wait(HighResDuration dur) && {\n  auto future = std::move(*this);\n  future.wait(dur);\n  return future.isReady();\n}\n\ntemplate <class T>\nT SemiFuture<T>::get() && {\n  return std::move(*this).getTry().value();\n}\n\ntemplate <class T>\nT SemiFuture<T>::get(HighResDuration dur) && {\n  return std::move(*this).getTry(dur).value();\n}\n\ntemplate <class T>\nTry<T> SemiFuture<T>::getTry() && {\n  wait();\n  auto future = folly::Future<T>(this->core_);\n  this->core_ = nullptr;\n  return std::move(std::move(future).result());\n}\n\ntemplate <class T>\nTry<T> SemiFuture<T>::getTry(HighResDuration dur) && {\n  wait(dur);\n  auto future = folly::Future<T>(this->core_);\n  this->core_ = nullptr;\n\n  if (!future.isReady()) {\n    throw_exception<FutureTimeout>();\n  }\n  return std::move(std::move(future).result());\n}\n\ntemplate <class T>\nFuture<T>& Future<T>::wait() & {\n  futures::detail::waitImpl(*this);\n  return *this;\n}\n\ntemplate <class T>\nFuture<T>&& Future<T>::wait() && {\n  futures::detail::waitImpl(*this);\n  return std::move(*this);\n}\n\ntemplate <class T>\nFuture<T>& Future<T>::wait(HighResDuration dur) & {\n  futures::detail::waitImpl(*this, dur);\n  return *this;\n}\n\ntemplate <class T>\nFuture<T>&& Future<T>::wait(HighResDuration dur) && {\n  futures::detail::waitImpl(*this, dur);\n  return std::move(*this);\n}\n\ntemplate <class T>\nFuture<T>& Future<T>::waitVia(DrivableExecutor* e) & {\n  futures::detail::waitViaImpl(*this, e);\n  return *this;\n}\n\ntemplate <class T>\nFuture<T>&& Future<T>::waitVia(DrivableExecutor* e) && {\n  futures::detail::waitViaImpl(*this, e);\n  return std::move(*this);\n}\n\ntemplate <class T>\nFuture<T>& Future<T>::waitVia(TimedDrivableExecutor* e, HighResDuration dur) & {\n  futures::detail::waitViaImpl(*this, e, dur);\n  return *this;\n}\n\ntemplate <class T>\nFuture<T>&& Future<T>::waitVia(\n    TimedDrivableExecutor* e, HighResDuration dur) && {\n  futures::detail::waitViaImpl(*this, e, dur);\n  return std::move(*this);\n}\n\ntemplate <class T>\nT Future<T>::get() && {\n  return std::move(*this).getTry().value();\n}\n\ntemplate <class T>\nT Future<T>::get(HighResDuration dur) && {\n  return std::move(*this).getTry(dur).value();\n}\n\ntemplate <class T>\nTry<T> Future<T>::getTry() && {\n  return std::move(*this).semi().getTry();\n}\n\ntemplate <class T>\nTry<T> Future<T>::getTry(HighResDuration dur) && {\n  return std::move(*this).semi().getTry(dur);\n}\n\ntemplate <class T>\nT Future<T>::getVia(DrivableExecutor* e) && {\n  return std::move(waitVia(e).value());\n}\n\ntemplate <class T>\nT Future<T>::getVia(TimedDrivableExecutor* e, HighResDuration dur) && {\n  waitVia(e, dur);\n  if (!this->isReady()) {\n    throw_exception<FutureTimeout>();\n  }\n  return std::move(value());\n}\n\ntemplate <class T>\nTry<T> Future<T>::getTryVia(DrivableExecutor* e) && {\n  return std::move(waitVia(e).result());\n}\n\ntemplate <class T>\nTry<T> Future<T>::getTryVia(TimedDrivableExecutor* e, HighResDuration dur) && {\n  waitVia(e, dur);\n  if (!this->isReady()) {\n    throw_exception<FutureTimeout>();\n  }\n  return std::move(result());\n}\n\nnamespace futures {\nnamespace detail {\ntemplate <class T>\nstruct TryEquals {\n  static bool equals(const Try<T>& t1, const Try<T>& t2) {\n    return t1.value() == t2.value();\n  }\n};\n} // namespace detail\n} // namespace futures\n\ntemplate <class T>\nFuture<bool> Future<T>::willEqual(Future<T>& f) {\n  return collectAllUnsafe(*this, f).thenValue(\n      [](const std::tuple<Try<T>, Try<T>>& t) {\n        if (std::get<0>(t).hasValue() && std::get<1>(t).hasValue()) {\n          return futures::detail::TryEquals<T>::equals(\n              std::get<0>(t), std::get<1>(t));\n        } else {\n          return false;\n        }\n      });\n}\n\ntemplate <class T>\ntemplate <class F>\nFuture<T> Future<T>::filter(F&& predicate) && {\n  return std::move(*this).thenValue([p = static_cast<F&&>(predicate)](T val) {\n    T const& valConstRef = val;\n    if (!p(valConstRef)) {\n      throw_exception<FuturePredicateDoesNotObtain>();\n    }\n    return val;\n  });\n}\n\ntemplate <class F>\nauto when(bool p, F&& thunk) -> decltype(static_cast<F&&>(thunk)().unit()) {\n  return p ? static_cast<F&&>(thunk)().unit() : makeFuture();\n}\n\ntemplate <class P, class F>\ntypename std::\n    enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type\n    whileDo(P&& predicate, F&& thunk) {\n  if (predicate()) {\n    auto future = thunk();\n    return std::move(future).deferExValue(\n        [predicate_2 = static_cast<P&&>(predicate),\n         thunk_2 = static_cast<F&&>(thunk)](auto&& ex, auto&&) mutable {\n          return whileDo(\n                     static_cast<P&&>(predicate_2), static_cast<F&&>(thunk_2))\n              .via(std::move(ex));\n        });\n  }\n  return makeSemiFuture();\n}\n\ntemplate <class P, class F>\ntypename std::enable_if<isFuture<invoke_result_t<F>>::value, Future<Unit>>::type\nwhileDo(P&& predicate, F&& thunk) {\n  if (predicate()) {\n    auto future = thunk();\n    return std::move(future).thenValue(\n        [predicate_2 = static_cast<P&&>(predicate),\n         thunk_2 = static_cast<F&&>(thunk)](auto&&) mutable {\n          return whileDo(\n              static_cast<P&&>(predicate_2), static_cast<F&&>(thunk_2));\n        });\n  }\n  return makeFuture();\n}\n\ntemplate <class F>\nauto times(const int n, F&& thunk) {\n  return folly::whileDo(\n      [n, count = std::make_unique<std::atomic<int>>(0)]() mutable {\n        return count->fetch_add(1, std::memory_order_relaxed) < n;\n      },\n      static_cast<F&&>(thunk));\n}\n\nnamespace futures {\ntemplate <class It, class F, class ItT, class Tag, class Result>\nstd::vector<Future<Result>> mapValue(It first, It last, F func) {\n  std::vector<Future<Result>> results;\n  results.reserve(std::distance(first, last));\n  for (auto it = first; it != last; it++) {\n    results.push_back(std::move(*it).thenValue(func));\n  }\n  return results;\n}\n\ntemplate <class It, class F, class ItT, class Tag, class Result>\nstd::vector<Future<Result>> mapTry(It first, It last, F func, int) {\n  std::vector<Future<Result>> results;\n  results.reserve(std::distance(first, last));\n  for (auto it = first; it != last; it++) {\n    results.push_back(std::move(*it).thenTry(func));\n  }\n  return results;\n}\n\ntemplate <class It, class F, class ItT, class Tag, class Result>\nstd::vector<Future<Result>> mapValue(\n    Executor& exec, It first, It last, F func) {\n  std::vector<Future<Result>> results;\n  results.reserve(std::distance(first, last));\n  for (auto it = first; it != last; it++) {\n    results.push_back(std::move(*it).via(&exec).thenValue(func));\n  }\n  return results;\n}\n\ntemplate <class It, class F, class ItT, class Tag, class Result>\nstd::vector<Future<Result>> mapTry(\n    Executor& exec, It first, It last, F func, int) {\n  std::vector<Future<Result>> results;\n  results.reserve(std::distance(first, last));\n  for (auto it = first; it != last; it++) {\n    results.push_back(std::move(*it).via(&exec).thenTry(func));\n  }\n  return results;\n}\n\ntemplate <typename F, class Ensure>\nauto ensure(F&& f, Ensure&& ensure) {\n  return makeSemiFuture()\n      .deferValue([f_2 = static_cast<F&&>(f)](auto) mutable { return f_2(); })\n      .defer(\n          [ensure_2 = static_cast<Ensure&&>(ensure)](auto resultTry) mutable {\n            ensure_2();\n            return std::move(resultTry).value();\n          });\n}\n\ntemplate <class T>\nvoid detachOn(folly::Executor::KeepAlive<> exec, folly::SemiFuture<T>&& fut) {\n  std::move(fut).via(exec).detach();\n}\n\ntemplate <class T>\nvoid detachOnGlobalCPUExecutor(folly::SemiFuture<T>&& fut) {\n  detachOn(folly::getGlobalCPUExecutor(), std::move(fut));\n}\n\ntemplate <class T>\nvoid maybeDetachOnGlobalExecutorAfter(\n    HighResDuration dur, folly::SemiFuture<T>&& fut) {\n  sleep(dur).toUnsafeFuture().thenValue(\n      [fut_2 = std::move(fut)](auto&&) mutable {\n        if (auto ptr = folly::detail::tryGetImmutableCPUPtr()) {\n          detachOn(folly::getKeepAliveToken(ptr.get()), std::move(fut_2));\n        }\n      });\n}\n\ntemplate <class T>\nvoid detachWithoutExecutor(folly::SemiFuture<T>&& fut) {\n  auto executor = futures::detail::stealDeferredExecutor(fut);\n  // Fail if we try to detach a SemiFuture with deferred work\n  DCHECK(executor.get() == nullptr);\n  if (executor) {\n    executor.get()->detach();\n  }\n}\n\n} // namespace futures\n\ntemplate <class Clock>\nSemiFuture<Unit> Timekeeper::at(std::chrono::time_point<Clock> when) {\n  auto now = Clock::now();\n\n  if (when <= now) {\n    return makeSemiFuture();\n  }\n\n  return after(std::chrono::duration_cast<HighResDuration>(when - now));\n}\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\n// limited to the instances unconditionally forced by the futures library\nnamespace futures {\nnamespace detail {\nextern template class FutureBase<Unit>;\n} // namespace detail\n} // namespace futures\nextern template class Future<Unit>;\nextern template class SemiFuture<Unit>;\n#endif\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Future-pre.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// included by Future.h, do not include directly.\n\nnamespace folly {\n\ntemplate <class>\nclass Promise;\n\ntemplate <class T>\nclass SemiFuture;\n\ntemplate <typename T>\nstruct isSemiFuture : std::false_type {\n  using Inner = lift_unit_t<T>;\n};\n\ntemplate <typename T>\nstruct isSemiFuture<SemiFuture<T>> : std::true_type {\n  typedef T Inner;\n};\n\ntemplate <typename T>\nstruct isFuture : std::false_type {\n  using Inner = lift_unit_t<T>;\n};\n\ntemplate <typename T>\nstruct isFuture<Future<T>> : std::true_type {\n  typedef T Inner;\n};\n\ntemplate <typename T>\nstruct isFutureOrSemiFuture : std::false_type {\n  using Inner = lift_unit_t<T>;\n};\n\ntemplate <typename T>\nstruct isFutureOrSemiFuture<Try<T>> : std::false_type {\n  using Inner = lift_unit_t<T>;\n};\n\ntemplate <typename T>\nstruct isFutureOrSemiFuture<Future<T>> : std::true_type {\n  typedef T Inner;\n};\n\ntemplate <typename T>\nstruct isFutureOrSemiFuture<Future<Try<T>>> : std::true_type {\n  typedef T Inner;\n};\n\ntemplate <typename T>\nstruct isFutureOrSemiFuture<SemiFuture<T>> : std::true_type {\n  typedef T Inner;\n};\n\ntemplate <typename T>\nstruct isFutureOrSemiFuture<SemiFuture<Try<T>>> : std::true_type {\n  typedef T Inner;\n};\n\nnamespace futures {\nnamespace detail {\n\ntemplate <typename...>\nstruct ArgType;\n\ntemplate <typename Arg, typename... Args>\nstruct ArgType<Arg, Args...> {\n  typedef Arg FirstArg;\n  typedef ArgType<Args...> Tail;\n};\n\ntemplate <>\nstruct ArgType<> {\n  typedef void FirstArg;\n};\n\ntemplate <bool isTry_, typename F, typename... Args>\nstruct argResult {\n  using ArgList = ArgType<Args...>;\n  using Result = invoke_result_t<F, Args...>;\n  using ArgsSize = index_constant<sizeof...(Args)>;\n  static constexpr bool isTry() { return isTry_; }\n};\n\ntemplate <typename T, typename F>\nstruct tryCallableResult {\n  typedef detail::argResult<true, F, Try<T>&&> Arg;\n  typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;\n  typedef typename ReturnsFuture::Inner value_type;\n};\n\ntemplate <typename T, typename F>\nstruct tryExecutorCallableResult {\n  typedef detail::argResult<true, F, Executor::KeepAlive<>&&, Try<T>&&> Arg;\n  typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;\n  typedef typename ReturnsFuture::Inner value_type;\n};\n\ntemplate <typename T, typename F>\nstruct valueCallableResult {\n  typedef detail::argResult<false, F, T&&> Arg;\n  typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;\n  typedef typename ReturnsFuture::Inner value_type;\n};\n\ntemplate <typename T, typename F>\nstruct valueExecutorCallableResult {\n  typedef detail::argResult<false, F, Executor::KeepAlive<>&&, T&&> Arg;\n  typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;\n  typedef typename ReturnsFuture::Inner value_type;\n};\n\nclass DeferredExecutor;\n\n} // namespace detail\n} // namespace futures\n\nclass Timekeeper;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Future.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n\n#include <folly/Likely.h>\n#include <folly/Singleton.h>\n#include <folly/futures/HeapTimekeeper.h>\n#include <folly/futures/ThreadWheelTimekeeper.h>\n#include <folly/portability/GFlags.h>\n\nFOLLY_GFLAGS_DEFINE_bool(\n    folly_futures_use_thread_wheel_timekeeper,\n    false,\n    \"Use ThreadWheelTimekeeper for the default Future timekeeper singleton\");\n\nnamespace folly {\nnamespace futures {\n\nnamespace detail {\n\nvoid WithinInterruptHandler::operator()(exception_wrapper const& ew) const {\n  if (auto locked = ptr.lock()) {\n    locked->raise(ew);\n  }\n}\n\nvoid WithinAfterFutureCallback::operator()(Try<Unit>&& t) {\n  if (t.hasException() &&\n      t.exception().is_compatible_with<FutureCancellation>()) {\n    // This got cancelled by thisFuture so we can just return.\n    return;\n  }\n\n  auto lockedCtx = ctx.lock();\n  if (!lockedCtx) {\n    // ctx already released. \"this\" completed first, cancel \"after\"\n    return;\n  }\n  // \"after\" completed first, cancel \"this\"\n  lockedCtx->thisFuture.raise(FutureTimeout());\n  if (!lockedCtx->token.exchange(true, std::memory_order_relaxed)) {\n    auto& exn = t.hasException() ? t.exception() : lockedCtx->exception;\n    lockedCtx->doPromiseSetException(*lockedCtx, std::move(exn));\n  }\n}\n\n} // namespace detail\n\nSemiFuture<Unit> sleep(HighResDuration dur, Timekeeper* tk) {\n  std::shared_ptr<Timekeeper> tks;\n  if (FOLLY_LIKELY(!tk)) {\n    tks = folly::detail::getTimekeeperSingleton();\n    tk = tks.get();\n  }\n\n  if (FOLLY_UNLIKELY(!tk)) {\n    return makeSemiFuture<Unit>(FutureNoTimekeeper());\n  }\n\n  return tk->after(dur);\n}\n\nFuture<Unit> sleepUnsafe(HighResDuration dur, Timekeeper* tk) {\n  return sleep(dur, tk).toUnsafeFuture();\n}\n\nnamespace {\ntemplate <typename Ptr>\nclass FutureWaiter : public fibers::Baton::Waiter {\n public:\n  FutureWaiter(Promise<Unit> promise, Ptr baton)\n      : promise_(std::move(promise)), baton_(std::move(baton)) {\n    baton_->setWaiter(*this);\n  }\n\n  void post() override {\n    promise_.setValue();\n    delete this;\n  }\n\n private:\n  Promise<Unit> promise_;\n  Ptr baton_;\n};\n} // namespace\n\nSemiFuture<Unit> wait(std::unique_ptr<fibers::Baton> baton) {\n  Promise<Unit> promise;\n  auto sf = promise.getSemiFuture();\n  new FutureWaiter<std::unique_ptr<fibers::Baton>>(\n      std::move(promise), std::move(baton));\n  return sf;\n}\nSemiFuture<Unit> wait(std::shared_ptr<fibers::Baton> baton) {\n  Promise<Unit> promise;\n  auto sf = promise.getSemiFuture();\n  new FutureWaiter<std::shared_ptr<fibers::Baton>>(\n      std::move(promise), std::move(baton));\n  return sf;\n}\n\n} // namespace futures\n\nnamespace detail {\n\nFOLLY_EXPORT FOLLY_ALWAYS_INLINE\n    folly::Singleton<Timekeeper, TimekeeperSingletonTag>&\n    getOrCreateTimekeeperSingleton() {\n  using TimekeeperSingleton =\n      folly::Singleton<Timekeeper, TimekeeperSingletonTag>;\n  static TimekeeperSingleton sTimekeeperSingleton([]() -> Timekeeper* {\n    if (FLAGS_folly_futures_use_thread_wheel_timekeeper) {\n      return new ThreadWheelTimekeeper;\n    } else {\n      return new HeapTimekeeper;\n    }\n  });\n  return sTimekeeperSingleton;\n}\n\nnamespace {\nclass TimekeeperSingletonInstantiator {\n public:\n  TimekeeperSingletonInstantiator() {\n    // call this function to force the timekeeper singleton to be created\n    folly::detail::getOrCreateTimekeeperSingleton();\n  }\n};\nTimekeeperSingletonInstantiator gTimekeeperSingletonInstantiator;\n} // namespace\n\nstd::shared_ptr<Timekeeper> getTimekeeperSingleton() {\n  (void)gTimekeeperSingletonInstantiator;\n  return getOrCreateTimekeeperSingleton().try_get();\n}\n\n} // namespace detail\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\nnamespace futures {\nnamespace detail {\ntemplate class FutureBase<Unit>;\n} // namespace detail\n} // namespace futures\n\ntemplate class Future<Unit>;\ntemplate class SemiFuture<Unit>;\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Future.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <exception>\n#include <functional>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/Optional.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Try.h>\n#include <folly/Unit.h>\n#include <folly/Utility.h>\n#include <folly/coro/Traits.h>\n#include <folly/executors/DrivableExecutor.h>\n#include <folly/executors/TimedDrivableExecutor.h>\n#include <folly/fibers/Baton.h>\n#include <folly/functional/Invoke.h>\n#include <folly/futures/Portability.h>\n#include <folly/futures/Promise.h>\n#include <folly/futures/detail/Types.h>\n#include <folly/lang/Exception.h>\n\n// boring predeclarations and details\n#include <folly/futures/Future-pre.h>\n\nnamespace folly {\n\nclass FOLLY_EXPORT FutureException : public std::logic_error {\n public:\n  using std::logic_error::logic_error;\n  FutureException() : std::logic_error{\"\"} {}\n};\n\nclass FOLLY_EXPORT FutureInvalid : public FutureException {\n public:\n  FutureInvalid() = default;\n  char const* what() const noexcept override { return \"Future invalid\"; }\n};\n\n/// At most one continuation may be attached to any given Future.\n///\n/// If a continuation is attached to a future to which another continuation has\n/// already been attached, then an instance of FutureAlreadyContinued will be\n/// thrown instead.\nclass FOLLY_EXPORT FutureAlreadyContinued : public FutureException {\n public:\n  FutureAlreadyContinued() = default;\n  char const* what() const noexcept override {\n    return \"Future already continued\";\n  }\n};\n\nclass FOLLY_EXPORT FutureNotReady : public FutureException {\n public:\n  FutureNotReady() = default;\n  char const* what() const noexcept override { return \"Future not ready\"; }\n};\n\nclass FOLLY_EXPORT FutureCancellation : public FutureException {\n public:\n  FutureCancellation() = default;\n  char const* what() const noexcept override { return \"Future was cancelled\"; }\n};\n\nclass FOLLY_EXPORT FutureTimeout : public FutureException {\n public:\n  FutureTimeout() = default;\n  char const* what() const noexcept override { return \"Timed out\"; }\n};\n\nclass FOLLY_EXPORT FuturePredicateDoesNotObtain : public FutureException {\n public:\n  FuturePredicateDoesNotObtain() = default;\n  char const* what() const noexcept override {\n    return \"Predicate does not obtain\";\n  }\n};\n\nclass FOLLY_EXPORT FutureNoTimekeeper : public FutureException {\n public:\n  FutureNoTimekeeper() = default;\n  char const* what() const noexcept override {\n    return \"No timekeeper available\";\n  }\n};\n\nclass FOLLY_EXPORT FutureNoExecutor : public FutureException {\n public:\n  FutureNoExecutor() = default;\n  char const* what() const noexcept override {\n    return \"No executor provided to via\";\n  }\n};\n\ntemplate <class T>\nclass Future;\n\ntemplate <class T>\nclass SemiFuture;\n\ntemplate <class T>\nstruct PromiseContract {\n  Promise<T> promise;\n  Future<T> future;\n};\n\ntemplate <class T>\nstruct SemiPromiseContract {\n  Promise<T> promise;\n  SemiFuture<T> future;\n};\n\ntemplate <class T>\nclass FutureSplitter;\n\nnamespace futures {\nnamespace detail {\nclass FutureBaseHelper;\n\ntemplate <class T>\nclass FutureBase {\n protected:\n  using Core = futures::detail::Core<T>;\n\n public:\n  using value_type = T;\n\n  /// Construct from a value (perfect forwarding)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  template <\n      class T2 = T,\n      typename = typename std::enable_if<\n          !isFuture<typename std::decay<T2>::type>::value &&\n          !isSemiFuture<typename std::decay<T2>::type>::value &&\n          std::is_constructible<Try<T>, T2>::value>::type>\n  /* implicit */ FutureBase(T2&& val);\n\n  /// Construct a (logical) FutureBase-of-void.\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  template <class T2 = T>\n  /* implicit */ FutureBase(\n      typename std::enable_if<std::is_same<Unit, T2>::value>::type*);\n\n  template <\n      class... Args,\n      typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::\n          type = 0>\n  explicit FutureBase(std::in_place_t, Args&&... args)\n      : core_(Core::make(std::in_place, static_cast<Args&&>(args)...)) {}\n\n  FutureBase(FutureBase<T> const&) = delete;\n  FutureBase(SemiFuture<T>&&) noexcept;\n  FutureBase(Future<T>&&) noexcept;\n\n  // not copyable\n  FutureBase(Future<T> const&) = delete;\n  FutureBase(SemiFuture<T> const&) = delete;\n\n  ~FutureBase();\n\n  /// true if this has a shared state;\n  /// false if this has been either moved-out or created without a shared state.\n  bool valid() const noexcept { return core_ != nullptr; }\n\n  /// Returns a reference to the result value if it is ready, with a reference\n  /// category and const-qualification like those of the future.\n  ///\n  /// Does not `wait()`; see `get()` for that.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  /// - `isReady() == true` (else throws FutureNotReady)\n  ///\n  /// Postconditions:\n  ///\n  /// - If an exception has been captured (i.e., if `hasException() == true`),\n  ///   throws that exception.\n  /// - This call does not mutate the future's value.\n  /// - However calling code may mutate that value (including moving it out by\n  ///   move-constructing or move-assigning another value from it), for\n  ///   example, via the `&` or the `&&` overloads or via casts.\n  T& value() &;\n  T const& value() const&;\n  T&& value() &&;\n  T const&& value() const&&;\n\n  /// Returns a reference to the result's Try if it is ready, with a reference\n  /// category and const-qualification like those of the future.\n  ///\n  /// Does not `wait()`; see `get()` for that.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  /// - `isReady() == true` (else throws FutureNotReady)\n  ///\n  /// Postconditions:\n  ///\n  /// - This call does not mutate the future's result.\n  /// - However calling code may mutate that result (including moving it out by\n  ///   move-constructing or move-assigning another result from it), for\n  ///   example, via the `&` or the `&&` overloads or via casts.\n  Try<T>& result() &;\n  Try<T> const& result() const&;\n  Try<T>&& result() &&;\n  Try<T> const&& result() const&&;\n\n  /// True when the result (or exception) is ready; see value(), result(), etc.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  bool isReady() const;\n\n  /// True if the result is a value (not an exception) on a future for which\n  ///   isReady returns true.\n  ///\n  /// Equivalent to result().hasValue()\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  /// - `isReady() == true` (else throws FutureNotReady)\n  bool hasValue() const;\n\n  /// True if the result is an exception (not a value) on a future for which\n  ///   isReady returns true.\n  ///\n  /// Equivalent to result().hasException()\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  /// - `isReady() == true` (else throws FutureNotReady)\n  bool hasException() const;\n\n  /// Returns either an Optional holding the result or an empty Optional\n  ///   depending on whether or not (respectively) the promise has been\n  ///   fulfilled (i.e., `isReady() == true`).\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (note however that this moves-out the result when\n  ///   it returns a populated `Try<T>`, which effects any subsequent use of\n  ///   that result, e.g., `poll()`, `result()`, `value()`, `get()`, etc.)\n  Optional<Try<T>> poll();\n\n  /// This is not the method you're looking for.\n  ///\n  /// This needs to be public because it's used by make* and when*, and it's\n  /// not worth listing all those and their fancy template signatures as\n  /// friends. But it's not for public consumption.\n  template <class F>\n  void setCallback_(\n      F&& func,\n      std::shared_ptr<folly::RequestContext>&& context,\n      InlineContinuation = InlineContinuation::forbid);\n  template <class F>\n  void setCallback_(F&& func, InlineContinuation = InlineContinuation::forbid);\n\n  /// Provides a threadsafe back-channel so the consumer's thread can send an\n  ///   interrupt-object to the producer's thread.\n  ///\n  /// If the promise-holder registers an interrupt-handler and consumer thread\n  ///   raises an interrupt early enough (details below), the promise-holder\n  ///   will typically halt its work, fulfilling the future with an exception\n  ///   or some special non-exception value.\n  ///\n  /// However this interrupt request is voluntary, asynchronous, & advisory:\n  ///\n  /// - Voluntary: the producer will see the interrupt only if the producer uses\n  ///   a `Promise` object and registers an interrupt-handler;\n  ///   see `Promise::setInterruptHandler()`.\n  /// - Asynchronous: the producer will see the interrupt only if `raise()` is\n  ///   called before (or possibly shortly after) the producer is done producing\n  ///   its result, which is asynchronous with respect to the call to `raise()`.\n  /// - Advisory: the producer's interrupt-handler can do whatever it wants,\n  ///   including ignore the interrupt or perform some action other than halting\n  ///   its producer-work.\n  ///\n  /// Guidelines:\n  ///\n  /// - It is ideal if the promise-holder can both halt its work and fulfill the\n  ///   promise early, typically with the same exception that was delivered to\n  ///   the promise-holder in the form of an interrupt.\n  /// - If the promise-holder does not do this, and if it holds the promise\n  ///   alive for a long time, then the whole continuation chain will not be\n  ///   invoked and the whole future chain will be kept alive for that long time\n  ///   as well.\n  /// - It is also ideal if the promise-holder can invalidate the promise.\n  /// - The promise-holder must also track whether it has set a result in the\n  ///   interrupt handler so that it does not attempt to do so outside the\n  ///   interrupt handler, and must track whether it has set a result in its\n  ///   normal flow so that it does not attempt to do so in the interrupt\n  ///   handler, since setting a result twice is an error. Because the interrupt\n  ///   handler can be invoked in some other thread, this tracking may have to\n  ///   be done with some form of concurrency control.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - has no visible effect if `raise()` was previously called on `this` or\n  ///   any other Future/SemiFuture that uses the same shared state as `this`.\n  /// - has no visible effect if the producer never (either in the past or in\n  ///   the future) registers an interrupt-handler.\n  /// - has no visible effect if the producer fulfills its promise (sets the\n  ///   result) before (or possibly also shortly after) receiving the interrupt.\n  /// - otherwise the promise-holder's interrupt-handler is called, passing the\n  ///   exception (within an `exception_wrapper`).\n  ///\n  /// The specific thread used to invoke the producer's interrupt-handler (if\n  ///   it is called at all) depends on timing:\n  ///\n  /// - if the interrupt-handler is registered prior to `raise()` (or possibly\n  ///   concurrently within the call to `raise()`), the interrupt-handler will\n  ///   be executed using this current thread within the call to `raise()`.\n  /// - if the interrupt-handler is registered after `raise()` (and possibly\n  ///   concurrently within the call to `raise()`), the interrupt-handler will\n  ///   be executed using the producer's thread within the call to\n  ///   `Promise::setInterruptHandler()`.\n  ///\n  /// Synchronizes between `raise()` (in the consumer's thread)\n  ///   and `Promise::setInterruptHandler()` (in the producer's thread).\n  void raise(exception_wrapper exception);\n\n  /// Raises the specified exception-interrupt.\n  /// See `raise(exception_wrapper)` for details.\n  template <class E>\n  void raise(E&& exception) {\n    raise(\n        make_exception_wrapper<typename std::remove_reference<E>::type>(\n            static_cast<E&&>(exception)));\n  }\n\n  /// Raises a FutureCancellation interrupt.\n  /// See `raise(exception_wrapper)` for details.\n  void cancel() { raise(FutureCancellation()); }\n\n protected:\n  friend class FutureBaseHelper;\n  friend class Promise<T>;\n  template <class>\n  friend class SemiFuture;\n  template <class>\n  friend class Future;\n\n  // Throws FutureInvalid if there is no shared state object; else returns it\n  // by ref.\n  //\n  // Implementation methods should usually use this instead of `this->core_`.\n  // The latter should be used only when you need the possibly-null pointer.\n  Core& getCore() { return getCoreImpl(*this); }\n  Core const& getCore() const { return getCoreImpl(*this); }\n\n  template <typename Self>\n  static decltype(auto) getCoreImpl(Self& self) {\n    if (!self.core_) {\n      throw_exception<FutureInvalid>();\n    }\n    return *self.core_;\n  }\n\n  Try<T>& getCoreTryChecked() { return getCoreTryChecked(*this); }\n  Try<T> const& getCoreTryChecked() const { return getCoreTryChecked(*this); }\n\n  template <typename Self>\n  static decltype(auto) getCoreTryChecked(Self& self) {\n    auto& core = self.getCore();\n    if (!core.hasResult()) {\n      throw_exception<FutureNotReady>();\n    }\n    return core.getTry();\n  }\n\n  // shared core state object\n  // usually you should use `getCore()` instead of directly accessing `core_`.\n  Core* core_;\n\n  explicit FutureBase(Core* obj) : core_(obj) {}\n\n  explicit FutureBase(futures::detail::EmptyConstruct) noexcept;\n\n  void detach();\n\n  void throwIfInvalid() const;\n  void throwIfContinued() const;\n\n  void assign(FutureBase<T>&& other) noexcept;\n\n  Executor* getExecutor() const { return getCore().getExecutor(); }\n\n  DeferredExecutor* getDeferredExecutor() const {\n    return getCore().getDeferredExecutor();\n  }\n\n  // Sets the Executor within the Core state object of `this`.\n  // Must be called either before attaching a callback or after the callback\n  // has already been invoked, but not concurrently with anything which might\n  // trigger invocation of the callback.\n  void setExecutor(futures::detail::KeepAliveOrDeferred x) {\n    getCore().setExecutor(std::move(x));\n  }\n\n  // Variant: returns a value\n  // e.g. f.thenTry([](Try<T> t){ return t.value(); });\n  template <typename F, typename R>\n  typename std::enable_if< //\n      !R::ReturnsFuture::value,\n      Future<typename R::value_type>>::type\n  thenImplementation(F&& func, R, InlineContinuation);\n\n  // Variant: returns a Future\n  // e.g. f.thenTry([](Try<T> t){ return makeFuture<T>(t); });\n  template <typename F, typename R>\n  typename std::enable_if< //\n      R::ReturnsFuture::value,\n      Future<typename R::value_type>>::type\n  thenImplementation(F&& func, R, InlineContinuation);\n};\ntemplate <class T>\nFuture<T> convertFuture(SemiFuture<T>&& sf, const Future<T>& f);\n\nclass DeferredExecutor;\n\ntemplate <typename T>\nDeferredExecutor* getDeferredExecutor(SemiFuture<T>& future);\n\ntemplate <typename T>\nfutures::detail::DeferredWrapper stealDeferredExecutor(SemiFuture<T>& future);\n} // namespace detail\n\n// Detach the SemiFuture by scheduling work onto exec.\ntemplate <class T>\nvoid detachOn(folly::Executor::KeepAlive<> exec, folly::SemiFuture<T>&& fut);\n\n// Detach the SemiFuture by detaching work onto the global CPU executor.\ntemplate <class T>\nvoid detachOnGlobalCPUExecutor(folly::SemiFuture<T>&& fut);\n\n// Detach the SemiFuture onto the global CPU executor after dur.\n// This will only hold a weak ref to the global executor and during\n// shutdown will cleanly drop the work.\ntemplate <class T>\nvoid maybeDetachOnGlobalExecutorAfter(\n    HighResDuration dur, folly::SemiFuture<T>&& fut);\n\n// Detach the SemiFuture with no executor.\n// NOTE: If there is deferred work of any sort on this SemiFuture\n// will leak and not be run.\n// Use at your own risk.\ntemplate <class T>\nvoid detachWithoutExecutor(folly::SemiFuture<T>&& fut);\n} // namespace futures\n\n/// The interface (along with Future) for the consumer-side of a\n///   producer/consumer pair.\n///\n/// Future vs. SemiFuture:\n///\n/// - The consumer-side should generally start with a SemiFuture, not a Future.\n/// - Example, when a library creates and returns a future, it should usually\n///   return a `SemiFuture`, not a Future.\n/// - Reason: so the thread policy for continuations (`.thenValue`, etc.) can be\n///   specified by the library's caller (using `.via()`).\n/// - A SemiFuture is converted to a Future using `.via()`.\n/// - Use `makePromiseContract()` when creating both a Promise and an associated\n///   SemiFuture/Future.\n///\n/// When practical, prefer SemiFuture/Future's nonblocking style/pattern:\n///\n/// - the nonblocking style uses continuations, e.g., `.thenValue`, etc.; the\n///   continuations are deferred until the result is available.\n/// - the blocking style blocks until complete, e.g., `.wait()`, `.get()`, etc.\n/// - the two styles cannot be mixed within the same future; use one or the\n///   other.\n///\n/// SemiFuture/Future also provide a back-channel so an interrupt can\n///   be sent from consumer to producer; see SemiFuture/Future's `raise()`\n///   and Promise's `setInterruptHandler()`.\n///\n/// The consumer-side SemiFuture/Future objects should generally be accessed\n///   via a single thread. That thread is referred to as the 'consumer thread.'\ntemplate <class T>\nclass SemiFuture : private futures::detail::FutureBase<T> {\n private:\n  using Base = futures::detail::FutureBase<T>;\n  using DeferredExecutor = futures::detail::DeferredExecutor;\n  using TimePoint = std::chrono::system_clock::time_point;\n\n public:\n  ~SemiFuture();\n\n  /// Creates/returns an invalid SemiFuture, that is, one with no shared state.\n  ///\n  /// Postcondition:\n  ///\n  /// - `RESULT.valid() == false`\n  static SemiFuture<T> makeEmpty();\n\n  /// Type of the value that the producer, when successful, produces.\n  using typename Base::value_type;\n\n  /// Construct a SemiFuture from a value (perfect forwarding)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  /// - `hasException() == false`\n  /// - `value()`, `get()`, `result()` will return the forwarded `T`\n  template <\n      class T2 = T,\n      typename = typename std::enable_if<\n          !isFuture<typename std::decay<T2>::type>::value &&\n          !isSemiFuture<typename std::decay<T2>::type>::value &&\n          std::is_constructible<Try<T>, T2>::value>::type>\n  /* implicit */ SemiFuture(T2&& val) : Base(static_cast<T2&&>(val)) {}\n\n  /// Construct a (logical) SemiFuture-of-void.\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  template <class T2 = T>\n  /* implicit */ SemiFuture(\n      typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)\n      : Base(p) {}\n\n  /// Construct a SemiFuture from a `T` constructed from `args`\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  /// - `hasException() == false`\n  /// - `value()`, `get()`, `result()` will return the newly constructed `T`\n  template <\n      class... Args,\n      typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::\n          type = 0>\n  explicit SemiFuture(std::in_place_t, Args&&... args)\n      : Base(std::in_place, static_cast<Args&&>(args)...) {}\n\n  SemiFuture(SemiFuture<T> const&) = delete;\n  // movable\n  SemiFuture(SemiFuture<T>&&) noexcept;\n  // safe move-constructabilty from Future\n  /* implicit */ SemiFuture(Future<T>&&) noexcept;\n\n  using Base::cancel;\n  using Base::hasException;\n  using Base::hasValue;\n  using Base::isReady;\n  using Base::poll;\n  using Base::raise;\n  using Base::result;\n  using Base::setCallback_;\n  using Base::valid;\n  using Base::value;\n\n  SemiFuture& operator=(SemiFuture const&) = delete;\n  SemiFuture& operator=(SemiFuture&&) noexcept;\n  SemiFuture& operator=(Future<T>&&) noexcept;\n\n  /// Blocks until the promise is fulfilled, either by value (which is returned)\n  ///   or exception (which is thrown).\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  /// - must not have a continuation, e.g., via `.thenValue()` or similar\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  T get() &&;\n\n  /// Blocks until the semifuture is fulfilled, or until `dur` elapses. Returns\n  /// the value (moved-out), or throws the exception (which might be a\n  /// FutureTimeout exception).\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  T get(HighResDuration dur) &&;\n\n  /// Blocks until the future is fulfilled. Returns the Try of the result\n  ///   (moved-out).\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  Try<T> getTry() &&;\n\n  /// Blocks until the future is fulfilled, or until `dur` elapses.\n  /// Returns the Try of the result (moved-out), or throws FutureTimeout\n  /// exception.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  Try<T> getTry(HighResDuration dur) &&;\n\n  /// Blocks the caller's thread until this Future `isReady()`, i.e., until the\n  ///   asynchronous producer has stored a result or exception.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `&RESULT == this`\n  SemiFuture<T>& wait() &;\n\n  /// Blocks the caller's thread until this Future `isReady()`, i.e., until the\n  ///   asynchronous producer has stored a result or exception.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (but the calling code can trivially move-out `*this`\n  ///   by assigning or constructing the result into a distinct object).\n  /// - `&RESULT == this`\n  /// - `isReady() == true`\n  SemiFuture<T>&& wait() &&;\n\n  /// Blocks until the future is fulfilled, or `dur` elapses.\n  /// Returns true if the future was fulfilled.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  bool wait(HighResDuration dur) &&;\n\n  /// Returns a Future which will call back on the other side of executor.\n  Future<T> via(Executor::KeepAlive<> executor) &&;\n  /// Same as via() but:\n  /// - executor MUST be identical to the executor running the task from which\n  ///   viaInlineUnsafe is called.\n  /// - MAY run some deferred callbacks inline\n  Future<T> viaInlineUnsafe(Executor::KeepAlive<> executor) &&;\n  Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &&;\n\n  /// Defer work to run on the consumer of the future.\n  /// Function must take a Try as a parameter.\n  /// This work will be run either on an executor that the caller sets on the\n  /// SemiFuture, or inline with the call to .get().\n  ///\n  /// NB: This is a custom method because boost-blocking executors is a\n  /// special-case for work deferral in folly. With more general boost-blocking\n  /// support all executors would boost block and we would simply use some form\n  /// of driveable executor here.\n  ///\n  /// All forms of defer will run the continuation inline with the execution of\n  /// the  previous callback in the chain if the callback attached to the\n  /// previous future that triggers execution of func runs on the same executor\n  /// that func would be executed on.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <typename F>\n  SemiFuture<typename futures::detail::tryCallableResult<T, F>::value_type>\n  defer(F&& func) &&;\n\n  /// Defer work to run on the consumer of the future.\n  /// Function must take a const Executor::KeepAlive<>& and a Try as parameters.\n  ///\n  /// As for defer(F&& func) except as the first parameter to func a KeepAlive\n  /// representing the executor running the work will be provided.\n  template <typename F>\n  SemiFuture<\n      typename futures::detail::tryExecutorCallableResult<T, F>::value_type>\n  deferExTry(F&& func) &&;\n\n  /// Defer work to run on the consumer of the future.\n  /// Function must take a Try as a parameter.\n  ///\n  /// As for defer(F&& func) but supporting function references.\n  template <typename R, typename... Args>\n  auto defer(R (&func)(Args...)) && {\n    return std::move(*this).defer(&func);\n  }\n\n  /// Defer for functions taking a T rather than a Try<T>.\n  ///\n  /// All forms of defer will run the continuation inline with the execution of\n  /// the  previous callback in the chain if the callback attached to the\n  /// previous future that triggers execution of func runs on the same executor\n  /// that func would be executed on.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <typename F>\n  SemiFuture<typename futures::detail::valueCallableResult<T, F>::value_type>\n  deferValue(F&& func) &&;\n\n  /// Defer for functions taking a T rather than a Try<T>.\n  /// Function must take a const Executor::KeepAlive<>& and a T as parameters.\n  ///\n  /// As for deferValue(F&& func) except as the first parameter to func a\n  /// KeepAlive representing the executor running the work will be provided.\n  template <typename F>\n  SemiFuture<\n      typename futures::detail::valueExecutorCallableResult<T, F>::value_type>\n  deferExValue(F&& func) &&;\n\n  /// Defer work to run on the consumer of the future.\n  /// Function must take a T as a parameter.\n  ///\n  /// As for deferValue(F&& func) but supporting function references.\n  template <typename R, typename... Args>\n  auto deferValue(R (&func)(Args...)) && {\n    return std::move(*this).deferValue(&func);\n  }\n\n  /// Set an error continuation for this SemiFuture where the continuation can\n  /// be called with a known exception type and returns a `T`, `Future<T>`, or\n  /// `SemiFuture<T>`.\n  ///\n  /// Example:\n  ///\n  /// ```\n  /// makeSemiFuture()\n  ///   .defer([] {\n  ///     throw std::runtime_error(\"oh no!\");\n  ///     return 42;\n  ///   })\n  ///   .deferError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) {\n  ///     LOG(INFO) << \"std::runtime_error: \" << e.what();\n  ///     return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)\n  ///   });\n  /// ```\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <class ExceptionType, class F>\n  SemiFuture<T> deferError(tag_t<ExceptionType>, F&& func) &&;\n\n  /// As for deferError(tag_t<ExceptionType>, F&& func) but supporting function\n  /// references.\n  template <class ExceptionType, class R, class... Args>\n  SemiFuture<T> deferError(tag_t<ExceptionType> tag, R (&func)(Args...)) && {\n    return std::move(*this).deferError(tag, &func);\n  }\n\n  /// As for deferError(tag_t<ExceptionType>, F&& func) but makes the exception\n  /// explicit as a template argument rather than using a tag type.\n  template <class ExceptionType, class F>\n  SemiFuture<T> deferError(F&& func) && {\n    return std::move(*this).deferError(\n        tag_t<ExceptionType>{}, static_cast<F&&>(func));\n  }\n\n  /// Set an error continuation for this SemiFuture where the continuation can\n  /// be called with `exception_wrapper&&` and returns a `T`, `Future<T>`, or\n  /// `SemiFuture<T>`.\n  ///\n  /// Example:\n  ///\n  ///   makeSemiFuture()\n  ///     .defer([] {\n  ///       throw std::runtime_error(\"oh no!\");\n  ///       return 42;\n  ///     })\n  ///     .deferError([] (exception_wrapper&& e) {\n  ///       LOG(INFO) << e.what();\n  ///       return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)\n  ///     });\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <class F>\n  SemiFuture<T> deferError(F&& func) &&;\n\n  /// As for deferError(tag_t<ExceptionType>, F&& func) but supporting function\n  /// references.\n  template <class R, class... Args>\n  SemiFuture<T> deferError(R (&func)(Args...)) && {\n    return std::move(*this).deferError(&func);\n  }\n\n  /// func is like std::function<void()> and is executed unconditionally\n  /// provided that the Semifuture is waited or given an executor, and\n  /// the value/exception is passed through to the resulting SemiFuture.\n  /// func shouldn't throw, but if it does it will be captured and propagated,\n  /// and discard any value/exception that this Semifuture has obtained.\n  ///\n  /// Caution: if the SemiFuture is detached - i.e., neither waited nor given an\n  /// executor - then func will not be invoked.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class F>\n  SemiFuture<T> deferEnsure(F&& func) &&;\n\n  /// Convenience method for ignoring the value and creating a Future<Unit>.\n  /// Exceptions still propagate.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  SemiFuture<Unit> unit() &&;\n\n  /// If this SemiFuture completes within duration dur from now, propagate its\n  /// value. Otherwise satisfy the returned SemiFuture with a FutureTimeout\n  /// exception.\n  ///\n  /// The optional Timekeeper is as with futures::sleep().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  SemiFuture<T> within(HighResDuration dur, Timekeeper* tk = nullptr) && {\n    return std::move(*this).within(dur, FutureTimeout(), tk);\n  }\n\n  /// If this SemiFuture completes within duration dur from now, propagate its\n  /// value. Otherwise satisfy the returned SemiFuture with exception e.\n  ///\n  /// The optional Timekeeper is as with futures::sleep().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class E>\n  SemiFuture<T> within(HighResDuration dur, E e, Timekeeper* tk = nullptr) &&;\n\n  /// Delay the completion of this SemiFuture for at least this duration from\n  /// now. The optional Timekeeper is as with futures::sleep().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  SemiFuture<T> delayed(HighResDuration dur, Timekeeper* tk = nullptr) &&;\n\n  /// Returns a future that completes inline, as if the future had no executor.\n  /// Intended for porting legacy code without behavioral change, and for rare\n  /// cases where this is really the intended behavior.\n  /// Future is unsafe in the sense that the executor it completes on is\n  /// non-deterministic in the standard case.\n  /// For new code, or to update code that temporarily uses this, please\n  /// use via and pass a meaningful executor.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  Future<T> toUnsafeFuture() &&;\n\n#if FOLLY_HAS_COROUTINES\n\n  // Customise the co_viaIfAsync() operator so that SemiFuture<T> can be\n  // directly awaited within a folly::coro::Task coroutine.\n  friend Future<T> co_viaIfAsync(\n      folly::Executor::KeepAlive<> executor, SemiFuture<T>&& future) noexcept {\n    return std::move(future).viaInlineUnsafe(std::move(executor));\n  }\n\n#endif\n\n private:\n  friend class Promise<T>;\n  friend class futures::detail::FutureBaseHelper;\n  template <class>\n  friend class futures::detail::FutureBase;\n  template <class>\n  friend class SemiFuture;\n  template <class>\n  friend class Future;\n  friend futures::detail::DeferredWrapper\n  futures::detail::stealDeferredExecutor<T>(SemiFuture<T>&);\n  friend DeferredExecutor* futures::detail::getDeferredExecutor<T>(\n      SemiFuture<T>&);\n\n  using Base::setExecutor;\n  using Base::throwIfInvalid;\n  using typename Base::Core;\n\n  template <class T2>\n  friend SemiFuture<T2> makeSemiFuture(Try<T2>);\n\n  explicit SemiFuture(Core* obj) : Base(obj) {}\n\n  explicit SemiFuture(futures::detail::EmptyConstruct) noexcept\n      : Base(futures::detail::EmptyConstruct{}) {}\n\n  // Throws FutureInvalid if !this->core_\n  futures::detail::DeferredWrapper stealDeferredExecutor();\n\n  /// Blocks until the future is fulfilled, or `dur` elapses.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `&RESULT == this`\n  /// - `isReady()` will be indeterminate - may or may not be true\n  SemiFuture<T>& wait(HighResDuration dur) &;\n\n  static void releaseDeferredExecutor(Core* core);\n};\n\ntemplate <class T>\nSemiPromiseContract<T> makePromiseContract() {\n  auto p = Promise<T>();\n  auto f = p.getSemiFuture();\n  return {std::move(p), std::move(f)};\n}\n\n/// The interface (along with SemiFuture) for the consumer-side of a\n///   producer/consumer pair.\n///\n/// Future vs. SemiFuture:\n///\n/// - The consumer-side should generally start with a SemiFuture, not a Future.\n/// - Example, when a library creates and returns a future, it should usually\n///   return a `SemiFuture`, not a Future.\n/// - Reason: so the thread policy for continuations (`.thenValue`, etc.) can be\n///   specified by the library's caller (using `.via()`).\n/// - A SemiFuture is converted to a Future using `.via()`.\n/// - Use `makePromiseContract()` when creating both a Promise and an associated\n///   SemiFuture/Future.\n///\n/// When practical, prefer SemiFuture/Future's nonblocking style/pattern:\n///\n/// - the nonblocking style uses continuations, e.g., `.thenValue`, etc.; the\n///   continuations are deferred until the result is available.\n/// - the blocking style blocks until complete, e.g., `.wait()`, `.get()`, etc.\n/// - the two styles cannot be mixed within the same future; use one or the\n///   other.\n///\n/// SemiFuture/Future also provide a back-channel so an interrupt can\n///   be sent from consumer to producer; see SemiFuture/Future's `raise()`\n///   and Promise's `setInterruptHandler()`.\n///\n/// The consumer-side SemiFuture/Future objects should generally be accessed\n///   via a single thread. That thread is referred to as the 'consumer thread.'\ntemplate <class T>\nclass Future : private futures::detail::FutureBase<T> {\n private:\n  using Base = futures::detail::FutureBase<T>;\n\n public:\n  /// Type of the value that the producer, when successful, produces.\n  using typename Base::value_type;\n\n  /// Construct a Future from a value (perfect forwarding)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  /// - `value()`, `get()`, `result()` will return the forwarded `T`\n  template <\n      class T2 = T,\n      typename = typename std::enable_if<\n          !isFuture<typename std::decay<T2>::type>::value &&\n          !isSemiFuture<typename std::decay<T2>::type>::value &&\n          std::is_constructible<Try<T>, T2>::value>::type>\n  /* implicit */ Future(T2&& val) : Base(static_cast<T2&&>(val)) {}\n\n  /// Construct a (logical) Future-of-void.\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  template <class T2 = T>\n  /* implicit */ Future(\n      typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)\n      : Base(p) {}\n\n  /// Construct a Future from a `T` constructed from `args`\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `isReady() == true`\n  /// - `hasValue() == true`\n  /// - `hasException() == false`\n  /// - `value()`, `get()`, `result()` will return the newly constructed `T`\n  template <\n      class... Args,\n      typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::\n          type = 0>\n  explicit Future(std::in_place_t, Args&&... args)\n      : Base(std::in_place, static_cast<Args&&>(args)...) {}\n\n  Future(Future<T> const&) = delete;\n  // movable\n  Future(Future<T>&&) noexcept;\n\n  // converting move\n  template <\n      class T2,\n      typename std::enable_if<\n          !std::is_same<T, typename std::decay<T2>::type>::value &&\n              std::is_constructible<T, T2&&>::value &&\n              std::is_convertible<T2&&, T>::value,\n          int>::type = 0>\n  /* implicit */ Future(Future<T2>&& other)\n      : Future(std::move(other).thenValue([](T2&& v) {\n          return T(std::move(v));\n        })) {}\n\n  template <\n      class T2,\n      typename std::enable_if<\n          !std::is_same<T, typename std::decay<T2>::type>::value &&\n              std::is_constructible<T, T2&&>::value &&\n              !std::is_convertible<T2&&, T>::value,\n          int>::type = 0>\n  explicit Future(Future<T2>&& other)\n      : Future(std::move(other).thenValue([](T2&& v) {\n          return T(std::move(v));\n        })) {}\n\n  template <\n      class T2,\n      typename std::enable_if<\n          !std::is_same<T, typename std::decay<T2>::type>::value &&\n              std::is_constructible<T, T2&&>::value,\n          int>::type = 0>\n  Future& operator=(Future<T2>&& other) {\n    return operator=(std::move(other).thenValue([](T2 && v) {\n      return T(std::move(v));\n    }));\n  }\n\n  using Base::cancel;\n  using Base::hasException;\n  using Base::hasValue;\n  using Base::isReady;\n  using Base::poll;\n  using Base::raise;\n  using Base::result;\n  using Base::setCallback_;\n  using Base::valid;\n  using Base::value;\n\n  /// Creates/returns an invalid Future, that is, one with no shared state.\n  ///\n  /// Postcondition:\n  ///\n  /// - `RESULT.valid() == false`\n  static Future<T> makeEmpty();\n\n  // not copyable\n  Future& operator=(Future const&) = delete;\n\n  // movable\n  Future& operator=(Future&&) noexcept;\n\n  /// Call e->drive() repeatedly until the future is fulfilled.\n  ///\n  /// Examples of DrivableExecutor include EventBase and ManualExecutor.\n  ///\n  /// Returns the fulfilled value (moved-out) or throws the fulfilled exception.\n  T getVia(DrivableExecutor* e) &&;\n\n  /// Call e->drive() repeatedly until the future is fulfilled, or `dur`\n  /// elapses.\n  ///\n  /// Returns the fulfilled value (moved-out), throws the fulfilled exception,\n  /// or on timeout throws FutureTimeout.\n  T getVia(TimedDrivableExecutor* e, HighResDuration dur) &&;\n\n  /// Call e->drive() repeatedly until the future is fulfilled. Examples\n  /// of DrivableExecutor include EventBase and ManualExecutor. Returns a\n  /// reference to the Try of the value.\n  Try<T> getTryVia(DrivableExecutor* e) &&;\n\n  /// getTryVia but will wait only until `dur` elapses. Returns the\n  /// Try of the value (moved-out) or may throw a FutureTimeout exception.\n  Try<T> getTryVia(TimedDrivableExecutor* e, HighResDuration dur) &&;\n\n  /// Unwraps the case of a Future<Future<T>> instance, and returns a simple\n  /// Future<T> instance.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class F = T>\n  typename std::\n      enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type\n      unwrap() &&;\n\n  /// Returns a Future which will call back on the other side of executor.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  Future<T> via(Executor::KeepAlive<> executor) &&;\n  Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &&;\n\n  /// Returns a Future which will call back on the other side of executor.\n  ///\n  /// When practical, use the rvalue-qualified overload instead - it's faster.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `RESULT.valid() == true`\n  /// - when `this` gets fulfilled, it automatically fulfills RESULT\n  Future<T> via(Executor::KeepAlive<> executor) &;\n  Future<T> via(Executor::KeepAlive<> executor, int8_t priority) &;\n\n  /// When this Future has completed, execute func which is a function that\n  /// can be called with either `T&&` or `Try<T>&&`.\n  ///\n  /// Func shall return either another Future or a value.\n  ///\n  /// thenInline will run the continuation inline with the execution of the\n  /// previous callback in the chain if the callback attached to the previous\n  /// future that triggers execution of func runs on the same executor that func\n  /// would be executed on.\n  ///\n  /// A Future for the return type of func is returned.\n  ///\n  /// Versions of these functions with Inline in the name will run the\n  /// continuation inline if the executor the previous task completes on matches\n  /// the executor the next is to be enqueued on to.\n  ///\n  ///   Future<string> f2 = f1.thenTry([](Try<T>&&) { return string(\"foo\"); });\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <typename F>\n  Future<typename futures::detail::tryCallableResult<T, F>::value_type> then(\n      F&& func) && {\n    return std::move(*this).thenTry(static_cast<F&&>(func));\n  }\n  template <typename F>\n  Future<typename futures::detail::tryCallableResult<T, F>::value_type>\n  thenInline(F&& func) && {\n    return std::move(*this).thenTryInline(static_cast<F&&>(func));\n  }\n\n  /// Variant where func is an member function\n  ///\n  ///   struct Worker { R doWork(Try<T>); }\n  ///\n  ///   Worker *w;\n  ///   Future<R> f2 = f1.thenTry(&Worker::doWork, w);\n  ///\n  /// This is just sugar for\n  ///\n  ///   f1.thenTry(std::bind(&Worker::doWork, w));\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <typename R, typename Caller, typename... Args>\n  Future<typename isFuture<R>::Inner> then(\n      R (Caller::*func)(Args...), Caller* instance) &&;\n\n  /// Execute the callback via the given Executor. The executor doesn't stick.\n  ///\n  /// Contrast\n  ///\n  ///   f.via(x).then(b).then(c)\n  ///\n  /// with\n  ///\n  ///   f.then(x, b).then(c)\n  ///\n  /// In the former both b and c execute via x. In the latter, only b executes\n  /// via x, and c executes via the same executor (if any) that f had.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class Arg>\n  auto then(Executor::KeepAlive<> x, Arg&& arg) && = delete;\n\n  /// When this Future has completed, execute func which is a function that\n  /// can be called with `Try<T>&&` (often a lambda with parameter type\n  /// `auto&&` or `auto`).\n  ///\n  /// Func shall return either another Future or a value.\n  ///\n  /// Versions of these functions with Inline in the name will run the\n  /// continuation inline with the execution of the previous callback in the\n  /// chain if the callback attached to the previous future that triggers\n  /// execution of func runs on the same executor that func would be executed\n  /// on.\n  ///\n  /// A Future for the return type of func is returned.\n  ///\n  ///   Future<string> f2 = std::move(f1).thenTry([](auto&& t) {\n  ///     ...\n  ///     return string(\"foo\");\n  ///   });\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <typename F>\n  Future<typename futures::detail::tryCallableResult<T, F>::value_type> thenTry(\n      F&& func) &&;\n\n  template <typename F>\n  Future<typename futures::detail::tryCallableResult<T, F>::value_type>\n  thenTryInline(F&& func) &&;\n\n  template <typename F>\n  Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>\n  thenExTry(F&& func) &&;\n\n  template <typename F>\n  Future<typename futures::detail::tryExecutorCallableResult<T, F>::value_type>\n  thenExTryInline(F&& func) &&;\n\n  template <typename R, typename... Args>\n  auto thenTry(R (&func)(Args...)) && {\n    return std::move(*this).thenTry(&func);\n  }\n\n  template <typename R, typename... Args>\n  auto thenTryInline(R (&func)(Args...)) && {\n    return std::move(*this).thenTryInline(&func);\n  }\n\n  /// When this Future has completed, execute func which is a function that\n  /// can be called with `T&&` (often a lambda with parameter type\n  /// `auto&&` or `auto`).\n  ///\n  /// Func shall return either another Future or a value.\n  ///\n  /// Versions of these functions with Inline in the name will run the\n  /// continuation inline with the execution of the previous callback in the\n  /// chain if the callback attached to the previous future that triggers\n  /// execution of func runs on the same executor that func would be executed\n  /// on.\n  ///\n  /// A Future for the return type of func is returned.\n  ///\n  ///   Future<string> f2 = f1.thenValue([](auto&& v) {\n  ///     ...\n  ///     return string(\"foo\");\n  ///   });\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <typename F>\n  Future<typename futures::detail::valueCallableResult<T, F>::value_type>\n  thenValue(F&& func) &&;\n\n  template <typename F>\n  Future<typename futures::detail::valueCallableResult<T, F>::value_type>\n  thenValueInline(F&& func) &&;\n\n  template <typename F>\n  Future<\n      typename futures::detail::valueExecutorCallableResult<T, F>::value_type>\n  thenExValue(F&& func) &&;\n\n  template <typename F>\n  Future<\n      typename futures::detail::valueExecutorCallableResult<T, F>::value_type>\n  thenExValueInline(F&& func) &&;\n\n  template <typename R, typename... Args>\n  auto thenValue(R (&func)(Args...)) && {\n    return std::move(*this).thenValue(&func);\n  }\n\n  template <typename R, typename... Args>\n  auto thenValueInline(R (&func)(Args...)) && {\n    return std::move(*this).thenValueInline(&func);\n  }\n\n  /// Set an error continuation for this Future where the continuation can\n  /// be called with a known exception type and returns a `T`, `Future<T>`, or\n  /// `SemiFuture<T>`.\n  ///\n  /// Example:\n  ///\n  ///   makeFuture()\n  ///     .thenTry([] {\n  ///       throw std::runtime_error(\"oh no!\");\n  ///       return 42;\n  ///     })\n  ///     .thenError(folly::tag_t<std::runtime_error>{}, [] (auto const& e) {\n  ///       LOG(INFO) << \"std::runtime_error: \" << e.what();\n  ///       return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)\n  ///     });\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <class ExceptionType, class F>\n  typename std::enable_if<\n      isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n      Future<T>>::type\n  thenError(tag_t<ExceptionType>, F&& func) &&;\n\n  template <class ExceptionType, class F>\n  typename std::enable_if<\n      !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n      Future<T>>::type\n  thenError(tag_t<ExceptionType>, F&& func) &&;\n\n  template <class ExceptionType, class F>\n  Future<T> thenErrorInline(tag_t<ExceptionType>, F&& func) &&;\n\n  template <class ExceptionType, class R, class... Args>\n  Future<T> thenError(tag_t<ExceptionType> tag, R (&func)(Args...)) && {\n    return std::move(*this).thenError(tag, &func);\n  }\n\n  template <class ExceptionType, class F>\n  Future<T> thenError(F&& func) && {\n    return std::move(*this).thenError(\n        tag_t<ExceptionType>{}, static_cast<F&&>(func));\n  }\n\n  /// Set an error continuation for this Future where the continuation can\n  /// be called with `exception_wrapper&&` and returns a `T`, `Future<T>`, or\n  /// `SemiFuture<T>`.\n  ///\n  /// Example:\n  ///\n  ///   makeFuture()\n  ///     .thenTry([] {\n  ///       throw std::runtime_error(\"oh no!\");\n  ///       return 42;\n  ///     })\n  ///     .thenError([] (exception_wrapper&& e) {\n  ///       LOG(INFO) << e.what();\n  ///       return -1; // or makeFuture<int>(-1) or makeSemiFuture<int>(-1)\n  ///     });\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  template <class F>\n  typename std::enable_if<\n      isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n      Future<T>>::type\n  thenError(F&& func) &&;\n\n  template <class F>\n  typename std::enable_if<\n      !isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n      Future<T>>::type\n  thenError(F&& func) &&;\n\n  template <class F>\n  Future<T> thenErrorInline(F&& func) &&;\n\n  template <class R, class... Args>\n  Future<T> thenError(R (&func)(Args...)) && {\n    return std::move(*this).thenError(&func);\n  }\n\n  /// Convenience method for ignoring the value and creating a Future<Unit>.\n  /// Exceptions still propagate.\n  /// This function is identical to .unit().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  Future<Unit> then() &&;\n\n  /// Convenience method for ignoring the value and creating a Future<Unit>.\n  /// Exceptions still propagate.\n  /// This function is identical to parameterless .then().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  Future<Unit> unit() && { return std::move(*this).then(); }\n\n  /// func is like std::function<void()> and is executed unconditionally, and\n  /// the value/exception is passed through to the resulting Future.\n  /// func shouldn't throw, but if it does it will be captured and propagated,\n  /// and discard any value/exception that this Future has obtained.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class F>\n  Future<T> ensure(F&& func) &&;\n\n  template <class F>\n  Future<T> ensureInline(F&& func) &&;\n\n  // clang-format on\n\n  /// Like thenError, but for timeouts. example:\n  ///\n  ///   Future<int> f = makeFuture<int>(42)\n  ///     .delayed(long_time)\n  ///     .onTimeout(short_time,\n  ///       [] { return -1; });\n  ///\n  /// or perhaps\n  ///\n  ///   Future<int> f = makeFuture<int>(42)\n  ///     .delayed(long_time)\n  ///     .onTimeout(short_time,\n  ///       [] { return makeFuture<int>(some_exception); });\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class F>\n  Future<T> onTimeout(HighResDuration, F&& func, Timekeeper* = nullptr) &&;\n\n  /// If this Future completes within duration dur from now, propagate its\n  /// value. Otherwise satisfy the returned SemiFuture with a FutureTimeout\n  /// exception.\n  ///\n  /// The optional Timekeeper is as with futures::sleep().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  Future<T> within(HighResDuration dur, Timekeeper* tk = nullptr) &&;\n\n  /// If this SemiFuture completes within duration dur from now, propagate its\n  /// value. Otherwise satisfy the returned SemiFuture with exception e.\n  ///\n  /// The optional Timekeeper is as with futures::sleep().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class E>\n  Future<T> within(\n      HighResDuration dur, E exception, Timekeeper* tk = nullptr) &&;\n\n  /// Delay the completion of this Future for at least this duration from\n  /// now. The optional Timekeeper is as with futures::sleep().\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  /// - `RESULT.valid() == true`\n  Future<T> delayed(HighResDuration, Timekeeper* = nullptr) &&;\n\n  /// Blocks until the future is fulfilled. Returns the value (moved-out), or\n  /// throws the exception. The future must not already have a continuation.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  T get() &&;\n\n  /// Blocks until the future is fulfilled, or until `dur` elapses. Returns the\n  /// value (moved-out), or throws the exception (which might be a FutureTimeout\n  /// exception).\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  T get(HighResDuration dur) &&;\n\n  /// Blocks until the future is fulfilled. Returns the Try of the result\n  ///   (moved-out).\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  Try<T> getTry() &&;\n\n  /// Blocks until the future is fulfilled, or until `dur` elapses.\n  /// Returns the Try of the result (moved-out), or throws FutureTimeout\n  /// exception.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == false`\n  Try<T> getTry(HighResDuration dur) &&;\n\n  /// Blocks until this Future is complete.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true`\n  /// - `&RESULT == this`\n  /// - `isReady() == true`\n  Future<T>& wait() &;\n\n  /// Blocks until this Future is complete.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (but the calling code can trivially move-out `*this`\n  ///   by assigning or constructing the result into a distinct object).\n  /// - `&RESULT == this`\n  /// - `isReady() == true`\n  Future<T>&& wait() &&;\n\n  /// Blocks until this Future is complete, or `dur` elapses.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (so you may call `wait(...)` repeatedly)\n  /// - `&RESULT == this`\n  /// - `isReady()` will be indeterminate - may or may not be true\n  Future<T>& wait(HighResDuration dur) &;\n\n  /// Blocks until this Future is complete or until `dur` passes.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (but the calling code can trivially move-out `*this`\n  ///   by assigning or constructing the result into a distinct object).\n  /// - `&RESULT == this`\n  /// - `isReady()` will be indeterminate - may or may not be true\n  Future<T>&& wait(HighResDuration dur) &&;\n\n  /// Call e->drive() repeatedly until the future is fulfilled. Examples\n  /// of DrivableExecutor include EventBase and ManualExecutor. Returns a\n  /// reference to this Future so that you can chain calls if desired.\n  /// value (moved-out), or throws the exception.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (does not move-out `*this`)\n  /// - `&RESULT == this`\n  Future<T>& waitVia(DrivableExecutor* e) &;\n\n  /// Overload of waitVia() for rvalue Futures\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (but the calling code can trivially move-out `*this`\n  ///   by assigning or constructing the result into a distinct object).\n  /// - `&RESULT == this`\n  Future<T>&& waitVia(DrivableExecutor* e) &&;\n\n  /// As waitVia but may return early after dur passes.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (does not move-out `*this`)\n  /// - `&RESULT == this`\n  Future<T>& waitVia(TimedDrivableExecutor* e, HighResDuration dur) &;\n\n  /// Overload of waitVia() for rvalue Futures\n  /// As waitVia but may return early after dur passes.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (but the calling code can trivially move-out `*this`\n  ///   by assigning or constructing the result into a distinct object).\n  /// - `&RESULT == this`\n  Future<T>&& waitVia(TimedDrivableExecutor* e, HighResDuration dur) &&;\n\n  /// If the value in this Future is equal to the given Future, when they have\n  /// both completed, the value of the resulting Future<bool> will be true. It\n  /// will be false otherwise (including when one or both Futures have an\n  /// exception)\n  Future<bool> willEqual(Future<T>&);\n\n  /// predicate behaves like std::function<bool(T const&)>\n  /// If the predicate does not obtain with the value, the result\n  /// is a folly::FuturePredicateDoesNotObtain exception\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class F>\n  Future<T> filter(F&& predicate) &&;\n\n  /// Like reduce, but works on a Future<std::vector<T / Try<T>>>, for example\n  /// the result of collect or collectAll\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws FutureInvalid)\n  ///\n  /// Postconditions:\n  ///\n  /// - Calling code should act as if `valid() == false`,\n  ///   i.e., as if `*this` was moved into RESULT.\n  /// - `RESULT.valid() == true`\n  template <class In, class F>\n  Future<In> reduce(In&& initial, F&& func) &&;\n\n  /// Moves-out `*this`, creating/returning a corresponding SemiFuture.\n  /// Result will behave like `*this` except result won't have an Executor.\n  ///\n  /// Postconditions:\n  ///\n  /// - `RESULT.valid() ==` the original value of `this->valid()`\n  /// - RESULT will not have an Executor regardless of whether `*this` had one\n  SemiFuture<T> semi() && { return SemiFuture<T>{std::move(*this)}; }\n\n#if FOLLY_HAS_COROUTINES\n\n  // Overload needed to customise behaviour of awaiting a Future<T>\n  // inside a folly::coro::Task coroutine.\n  friend Future<T> co_viaIfAsync(\n      folly::Executor::KeepAlive<> executor, Future<T>&& future) noexcept {\n    return std::move(future).via(std::move(executor));\n  }\n\n#endif\n\n protected:\n  friend class Promise<T>;\n  friend class futures::detail::FutureBaseHelper;\n  template <class>\n  friend class futures::detail::FutureBase;\n  template <class>\n  friend class Future;\n  template <class>\n  friend class SemiFuture;\n  template <class>\n  friend class FutureSplitter;\n\n  using Base::setExecutor;\n  using Base::throwIfContinued;\n  using Base::throwIfInvalid;\n  using typename Base::Core;\n\n  explicit Future(Core* obj) : Base(obj) {}\n\n  explicit Future(futures::detail::EmptyConstruct) noexcept\n      : Base(futures::detail::EmptyConstruct{}) {}\n\n  template <class T2>\n  friend Future<T2> makeFuture(Try<T2>);\n\n  template <class FT>\n  friend Future<FT> futures::detail::convertFuture(\n      SemiFuture<FT>&& sf, const Future<FT>& f);\n\n  using Base::detach;\n  template <class T2>\n  friend void futures::detachOn(\n      folly::Executor::KeepAlive<> exec, folly::SemiFuture<T2>&& fut);\n\n  template <class ExceptionType, class F>\n  typename std::enable_if<\n      isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n      Future<T>>::type\n  thenErrorImpl(\n      tag_t<ExceptionType>,\n      F&& func,\n      futures::detail::InlineContinuation allowInline =\n          futures::detail::InlineContinuation::forbid) &&;\n\n  template <class ExceptionType, class F>\n  typename std::enable_if<\n      !isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,\n      Future<T>>::type\n  thenErrorImpl(\n      tag_t<ExceptionType>,\n      F&& func,\n      futures::detail::InlineContinuation allowInline =\n          futures::detail::InlineContinuation::forbid) &&;\n\n  template <class F>\n  typename std::enable_if<\n      isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n      Future<T>>::type\n  thenErrorImpl(\n      F&& func,\n      futures::detail::InlineContinuation allowInline =\n          futures::detail::InlineContinuation::forbid) &&;\n\n  template <class F>\n  typename std::enable_if<\n      !isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,\n      Future<T>>::type\n  thenErrorImpl(\n      F&& func,\n      futures::detail::InlineContinuation allowInline =\n          futures::detail::InlineContinuation::forbid) &&;\n};\n\n/// A Timekeeper handles the details of keeping time and fulfilling delay\n/// promises. The returned Future<Unit> will either complete after the\n/// elapsed time, or in the event of some kind of exceptional error may hold\n/// an exception. These Futures respond to cancellation. If you use a lot of\n/// Delays and many of them ultimately are unneeded (as would be the case for\n/// Delays that are used to trigger timeouts of async operations), then you\n/// can and should cancel them to reclaim resources.\n///\n/// Users will typically get one of these via Future::sleep(HighResDuration dur)\n/// or use them implicitly behind the scenes by passing a timeout to some Future\n/// operation.\n///\n/// Although we don't formally alias Delay = Future<Unit>,\n/// that's an appropriate term for it. People will probably also call these\n/// Timeouts, and that's ok I guess, but that term is so overloaded I thought\n/// it made sense to introduce a cleaner term.\n///\n/// Remember that HighResDuration is a std::chrono duration (millisecond\n/// resolution at the time of writing). When writing code that uses specific\n/// durations, prefer using the explicit std::chrono type, e.g.\n/// std::chrono::milliseconds over HighResDuration. This makes the code more\n/// legible and means you won't be unpleasantly surprised if we redefine\n/// HighResDuration to microseconds, or something.\n///\n///   timekeeper.after(std::chrono::duration_cast<HighResDuration>(someNanoseconds))\nclass Timekeeper {\n public:\n  virtual ~Timekeeper() = default;\n\n  /// Returns a future that will complete after the given duration with the\n  /// elapsed time. Exceptional errors can happen but they must be\n  /// exceptional. Use the steady (monotonic) clock.\n  ///\n  /// The consumer thread may cancel this Future to reclaim resources.\n  virtual SemiFuture<Unit> after(HighResDuration dur) = 0;\n\n  /// Unsafe version of after that returns an inline Future.\n  /// Any work added to this future will run inline on the Timekeeper's thread.\n  /// This can potentially cause problems with timing.\n  ///\n  /// Please migrate to use after + a call to via with a valid, non-inline\n  /// executor.\n  Future<Unit> afterUnsafe(HighResDuration dur) {\n    return after(dur).toUnsafeFuture();\n  }\n\n  /// Returns a future that will complete at the requested time.\n  ///\n  /// You may cancel this SemiFuture to reclaim resources.\n  ///\n  /// NB This is sugar for `after(when - now)`, so while you are welcome to\n  /// use a std::chrono::system_clock::time_point it will not track changes to\n  /// the system clock but rather execute that many milliseconds in the future\n  /// according to the steady clock.\n  template <class Clock>\n  SemiFuture<Unit> at(std::chrono::time_point<Clock> when);\n\n  /// Unsafe version of at that returns an inline Future.\n  /// Any work added to this future will run inline on the Timekeeper's thread.\n  /// This can potentially cause problems with timing.\n  ///\n  /// Please migrate to use at + a call to via with a valid, non-inline\n  /// executor.\n  template <class Clock>\n  Future<Unit> atUnsafe(std::chrono::time_point<Clock> when) {\n    return at(when).toUnsafeFuture();\n  }\n};\n\ntemplate <class T>\nPromiseContract<T> makePromiseContract(Executor::KeepAlive<> e) {\n  auto p = Promise<T>();\n  auto f = p.getSemiFuture().via(std::move(e));\n  return {std::move(p), std::move(f)};\n}\n\ntemplate <class F>\nauto makeAsyncTask(folly::Executor::KeepAlive<> ka, F&& func) {\n  return [func_2 = static_cast<F&&>(func),\n          ka_2 = std::move(ka)](auto&& param) mutable {\n    return via(\n        ka_2,\n        [func_3 = std::move(func_2),\n         param_2 = static_cast<decltype(param)>(param)]() mutable {\n          return static_cast<F&&>(func_3)(\n              static_cast<decltype(param_2)&&>(param_2));\n        });\n  };\n}\n\n/// This namespace is for utility functions that would usually be static\n/// members of Future, except they don't make sense there because they don't\n/// depend on the template type (rather, on the type of their arguments in\n/// some cases). This is the least-bad naming scheme we could think of. Some\n/// of the functions herein have really-likely-to-collide names, like \"map\"\n/// and \"sleep\".\nnamespace futures {\n/// Returns a Future that will complete after the specified duration. The\n/// HighResDuration typedef of a `std::chrono` duration type indicates the\n/// resolution you can expect to be meaningful (milliseconds at the time of\n/// writing). Normally you wouldn't need to specify a Timekeeper, we will\n/// use the global futures timekeeper (we run a thread whose job it is to\n/// keep time for futures timeouts) but we provide the option for power\n/// users.\n///\n/// The Timekeeper thread will be lazily created the first time it is\n/// needed. If your program never uses any timeouts or other time-based\n/// Futures you will pay no Timekeeper thread overhead.\nSemiFuture<Unit> sleep(HighResDuration, Timekeeper* = nullptr);\n[[deprecated(\n    \"futures::sleep now returns a SemiFuture<Unit>. \"\n    \"sleepUnsafe is deprecated. \"\n    \"Please call futures::sleep and apply an executor with .via\")]] Future<Unit>\nsleepUnsafe(HighResDuration, Timekeeper* = nullptr);\n\n/**\n * Set func as the callback for each input Future and return a vector of\n * Futures containing the results in the input order.\n */\ntemplate <\n    class It,\n    class F,\n    class ItT = typename std::iterator_traits<It>::value_type,\n    class Tag = std::enable_if_t<is_invocable_v<F, typename ItT::value_type&&>>,\n    class Result = typename decltype(std::declval<ItT>().thenValue(\n        std::declval<F>()))::value_type>\nstd::vector<Future<Result>> mapValue(It first, It last, F func);\n\n/**\n * Set func as the callback for each input Future and return a vector of\n * Futures containing the results in the input order.\n */\ntemplate <\n    class It,\n    class F,\n    class ItT = typename std::iterator_traits<It>::value_type,\n    class Tag =\n        std::enable_if_t<!is_invocable_v<F, typename ItT::value_type&&>>,\n    class Result = typename decltype(std::declval<ItT>().thenTry(\n        std::declval<F>()))::value_type>\nstd::vector<Future<Result>> mapTry(It first, It last, F func, int = 0);\n\n/**\n * Set func as the callback for each input Future and return a vector of\n * Futures containing the results in the input order and completing on\n * exec.\n */\ntemplate <\n    class It,\n    class F,\n    class ItT = typename std::iterator_traits<It>::value_type,\n    class Tag = std::enable_if_t<is_invocable_v<F, typename ItT::value_type&&>>,\n    class Result =\n        typename decltype(std::move(std::declval<ItT>())\n                              .via(std::declval<Executor*>())\n                              .thenValue(std::declval<F>()))::value_type>\nstd::vector<Future<Result>> mapValue(Executor& exec, It first, It last, F func);\n\n/**\n * Set func as the callback for each input Future and return a vector of\n * Futures containing the results in the input order and completing on\n * exec.\n */\ntemplate <\n    class It,\n    class F,\n    class ItT = typename std::iterator_traits<It>::value_type,\n    class Tag =\n        std::enable_if_t<!is_invocable_v<F, typename ItT::value_type&&>>,\n    class Result =\n        typename decltype(std::move(std::declval<ItT>())\n                              .via(std::declval<Executor*>())\n                              .thenTry(std::declval<F>()))::value_type>\nstd::vector<Future<Result>> mapTry(\n    Executor& exec, It first, It last, F func, int = 0);\n\n// Sugar for the most common case\ntemplate <class Collection, class F>\nauto mapValue(Collection&& c, F&& func)\n    -> decltype(mapValue(c.begin(), c.end(), func)) {\n  return mapValue(c.begin(), c.end(), static_cast<F&&>(func));\n}\n\ntemplate <class Collection, class F>\nauto mapTry(Collection&& c, F&& func)\n    -> decltype(mapTry(c.begin(), c.end(), func)) {\n  return mapTry(c.begin(), c.end(), static_cast<F&&>(func));\n}\n\n// Sugar for the most common case\ntemplate <class Collection, class F>\nauto mapValue(Executor& exec, Collection&& c, F&& func)\n    -> decltype(mapValue(exec, c.begin(), c.end(), func)) {\n  return mapValue(exec, c.begin(), c.end(), static_cast<F&&>(func));\n}\n\ntemplate <class Collection, class F>\nauto mapTry(Executor& exec, Collection&& c, F&& func)\n    -> decltype(mapTry(exec, c.begin(), c.end(), func)) {\n  return mapTry(exec, c.begin(), c.end(), static_cast<F&&>(func));\n}\n\n/// Carry out the computation contained in the given future if\n/// the predicate holds.\n///\n/// thunk behaves like std::function<Future<T2>(void)> or\n/// std::function<SemiFuture<T2>(void)>\ntemplate <class F>\nauto when(bool p, F&& thunk) -> decltype(static_cast<F&&>(thunk)().unit());\n\nSemiFuture<Unit> wait(std::unique_ptr<fibers::Baton> baton);\nSemiFuture<Unit> wait(std::shared_ptr<fibers::Baton> baton);\n\n/**\n * Returns a lazy SemiFuture constructed by f, which also ensures that ensure is\n * called before completion.\n * f doesn't get called until the SemiFuture is activated (e.g. through a .get()\n * or .via() call). If f gets called, ensure is guaranteed to be called as well.\n */\ntemplate <typename F, class Ensure>\nauto ensure(F&& f, Ensure&& ensure);\n\n} // namespace futures\n\n/**\n  Make a completed SemiFuture by moving in a value. e.g.\n\n    string foo = \"foo\";\n    auto f = makeSemiFuture(std::move(foo));\n\n  or\n\n    auto f = makeSemiFuture<string>(\"foo\");\n*/\ntemplate <class T>\nSemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t);\n\n/** Make a completed void SemiFuture. */\nSemiFuture<Unit> makeSemiFuture();\n\n/**\n  Make a SemiFuture by executing a function.\n\n  If the function returns a value of type T, makeSemiFutureWith\n  returns a completed SemiFuture<T>, capturing the value returned\n  by the function.\n\n  If the function returns a SemiFuture<T> already, makeSemiFutureWith\n  returns just that.\n\n  Either way, if the function throws, a failed Future is\n  returned that captures the exception.\n*/\n\n// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>\ntemplate <class F>\ntypename std::enable_if<\n    isFutureOrSemiFuture<invoke_result_t<F>>::value,\n    SemiFuture<typename invoke_result_t<F>::value_type>>::type\nmakeSemiFutureWith(F&& func);\n\n// makeSemiFutureWith(T()) -> SemiFuture<T>\n// makeSemiFutureWith(void()) -> SemiFuture<Unit>\ntemplate <class F>\ntypename std::enable_if<\n    !(isFutureOrSemiFuture<invoke_result_t<F>>::value),\n    SemiFuture<lift_unit_t<invoke_result_t<F>>>>::type\nmakeSemiFutureWith(F&& func);\n\n/// Make a failed Future from an exception_ptr.\n/// Because the Future's type cannot be inferred you have to specify it, e.g.\n///\n///   auto f = makeSemiFuture<string>(std::current_exception());\ntemplate <class T>\n[[deprecated(\"use makeSemiFuture(exception_wrapper)\")]] SemiFuture<T>\nmakeSemiFuture(std::exception_ptr const& e);\n\n/// Make a failed SemiFuture from an exception_wrapper.\ntemplate <class T>\nSemiFuture<T> makeSemiFuture(exception_wrapper ew);\n\n/** Make a SemiFuture from an exception type E that can be passed to\n  std::make_exception_ptr(). */\ntemplate <class T, class E>\nstd::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, SemiFuture<T>>\nmakeSemiFuture(E&& e);\n\n/** Make a SemiFuture from an exception type E that can be passed to\n  std::make_exception_ptr().\n  NOTE: This is a deprecated const-ref overload for users who explicitly specify\n  both template parameters. Please leave exception type deduction to the\n  compiler.\n */\ntemplate <class T, class E>\n[[deprecated(\"do not specify exception type template parameter explicitly\")]]\nstd::enable_if_t<std::is_base_of_v<std::exception, E>, SemiFuture<T>>\nmakeSemiFuture(const folly::type_identity_t<E>& e);\n\n/** Make a Future out of a Try */\ntemplate <class T>\nSemiFuture<T> makeSemiFuture(Try<T> t);\n\n/**\n  Make a completed Future by moving in a value. e.g.\n\n    string foo = \"foo\";\n    auto f = makeFuture(std::move(foo));\n\n  or\n\n    auto f = makeFuture<string>(\"foo\");\n\n  NOTE: This function is deprecated. Please use makeSemiFuture and pass the\n       appropriate executor to .via on the returned SemiFuture to get a\n       valid Future where necessary.\n*/\ntemplate <class T>\nFuture<typename std::decay<T>::type> makeFuture(T&& t);\n\n/**\n  Make a completed void Future.\n\n  NOTE: This function is deprecated. Please use makeSemiFuture and pass the\n       appropriate executor to .via on the returned SemiFuture to get a\n       valid Future where necessary.\n */\nFuture<Unit> makeFuture();\n\n/**\n  Make a Future by executing a function.\n\n  If the function returns a value of type T, makeFutureWith\n  returns a completed Future<T>, capturing the value returned\n  by the function.\n\n  If the function returns a Future<T> already, makeFutureWith\n  returns just that.\n\n  Either way, if the function throws, a failed Future is\n  returned that captures the exception.\n\n  Calling makeFutureWith(func) is equivalent to calling\n  makeFuture().then(func).\n\n  NOTE: This function is deprecated. Please use makeSemiFutureWith and pass the\n       appropriate executor to .via on the returned SemiFuture to get a\n       valid Future where necessary.\n*/\n\n// makeFutureWith(Future<T>()) -> Future<T>\ntemplate <class F>\ntypename std::\n    enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type\n    makeFutureWith(F&& func);\n\n// makeFutureWith(T()) -> Future<T>\n// makeFutureWith(void()) -> Future<Unit>\ntemplate <class F>\ntypename std::enable_if<\n    !(isFuture<invoke_result_t<F>>::value),\n    Future<lift_unit_t<invoke_result_t<F>>>>::type\nmakeFutureWith(F&& func);\n\n/// Make a failed Future from an exception_ptr.\n/// Because the Future's type cannot be inferred you have to specify it, e.g.\n///\n///   auto f = makeFuture<string>(std::current_exception());\ntemplate <class T>\n[[deprecated(\"use makeSemiFuture(exception_wrapper)\")]] Future<T> makeFuture(\n    std::exception_ptr const& e);\n\n/// Make a failed Future from an exception_wrapper.\n/// NOTE: This function is deprecated. Please use makeSemiFuture and pass the\n///     appropriate executor to .via on the returned SemiFuture to get a\n///     valid Future where necessary.\ntemplate <class T>\nFuture<T> makeFuture(exception_wrapper ew);\n\n/** Make a Future from an exception type E that can be passed to\n  std::make_exception_ptr().\n\n  NOTE: This function is deprecated. Please use makeSemiFuture and pass the\n       appropriate executor to .via on the returned SemiFuture to get a\n       valid Future where necessary.\n */\ntemplate <class T, class E>\nstd::enable_if_t<std::is_base_of_v<std::exception, decay_t<E>>, Future<T>>\nmakeFuture(E&& e);\n\n/** Make a Future from an exception type E that can be passed to\n  std::make_exception_ptr().\n  NOTE: This is a deprecated const-ref overload for users who explicitly specify\n  both template parameters. Please leave exception type deduction to the\n  compiler.\n */\ntemplate <class T, class E>\n[[deprecated(\"do not specify exception type template parameter explicitly\")]]\nstd::enable_if_t<std::is_base_of_v<std::exception, E>, Future<T>> makeFuture(\n    const folly::type_identity_t<E>& e);\n\n/**\n  Make a Future out of a Try\n\n  NOTE: This function is deprecated. Please use makeSemiFuture and pass the\n       appropriate executor to .via on the returned SemiFuture to get a\n       valid Future where necessary.\n */\ntemplate <class T>\nFuture<T> makeFuture(Try<T> t);\n\n/*\n * Return a new Future that will call back on the given Executor.\n * This is just syntactic sugar for makeFuture().via(executor)\n *\n * @param executor the Executor to call back on\n * @param priority optionally, the priority to add with. Defaults to 0 which\n * represents medium priority.\n *\n * @returns a void Future that will call back on the given executor\n */\ninline Future<Unit> via(Executor::KeepAlive<> executor);\ninline Future<Unit> via(Executor::KeepAlive<> executor, int8_t priority);\n\n/// Execute a function via the given executor and return a future.\n/// This is semantically equivalent to via(executor).then(func), but\n/// easier to read and slightly more efficient.\ntemplate <class Func>\nauto via(Executor::KeepAlive<>, Func&& func)\n    -> Future<typename isFutureOrSemiFuture<\n        decltype(static_cast<Func&&>(func)())>::Inner>;\n\n/** When all the input Futures complete, the returned Future will complete.\n  Errors do not cause early termination; this Future will always succeed\n  after all its Futures have finished (whether successfully or with an\n  error).\n\n  The Futures are moved in, so your copies are invalid. If you need to\n  chain further from these Futures, use the variant with an output iterator.\n\n  This function is thread-safe for Futures running on different threads. But\n  if you are doing anything non-trivial after, you will probably want to\n  follow with `via(executor)` because it will complete in whichever thread the\n  last Future completes in.\n\n  The return type for Future<T> input is a SemiFuture<std::vector<Try<T>>>\n  for collectX.\n\n  collectXUnsafe returns an inline Future that erases the executor from the\n  incoming Futures/SemiFutures. collectXUnsafe should be phased out and\n  replaced with collectX(...).via(e) where e is a valid non-inline executor.\n  */\n// Unsafe variant, see above comment for details\ntemplate <class InputIterator>\nFuture<std::vector<\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>\ncollectAllUnsafe(InputIterator first, InputIterator last);\n\n// Unsafe variant sugar, see above comment for details\ntemplate <class Collection>\nauto collectAllUnsafe(Collection&& c)\n    -> decltype(collectAllUnsafe(c.begin(), c.end())) {\n  return collectAllUnsafe(c.begin(), c.end());\n}\n\ntemplate <class InputIterator>\nSemiFuture<std::vector<\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>\ncollectAll(InputIterator first, InputIterator last);\n\ntemplate <class Collection>\nauto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {\n  return collectAll(c.begin(), c.end());\n}\n\n// Unsafe variant of collectAll, see comment above for details. Returns\n// a Future<std::tuple<Try<T1>, Try<T2>, ...>> on the Inline executor.\ntemplate <typename... Fs>\nFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>\ncollectAllUnsafe(Fs&&... fs);\n\n/// This version takes a varying number of Futures instead of an iterator.\n/// The return type for (Future<T1>, Future<T2>, ...) input\n/// is a SemiFuture<std::tuple<Try<T1>, Try<T2>, ...>>.\n/// The Futures are moved in, so your copies are invalid.\ntemplate <typename... Fs>\nSemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>\ncollectAll(Fs&&... fs);\n\n/// Like collectAll, but will short circuit on the first exception. Thus, the\n/// type of the returned SemiFuture is std::vector<T> instead of\n/// std::vector<Try<T>>\ntemplate <class InputIterator>\nSemiFuture<std::vector<\n    typename std::iterator_traits<InputIterator>::value_type::value_type>>\ncollect(InputIterator first, InputIterator last);\n\n/// Sugar for the most common case\ntemplate <class Collection>\nauto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) {\n  return collect(c.begin(), c.end());\n}\n\n// Unsafe variant of collect. Returns a Future<std::vector<T>> that\n// completes inline.\ntemplate <class InputIterator>\nFuture<std::vector<\n    typename std::iterator_traits<InputIterator>::value_type::value_type>>\ncollectUnsafe(InputIterator first, InputIterator last);\n\n/// Sugar for the most common unsafe case. Returns a Future<std::vector<T>>\n// that completes inline.\ntemplate <class Collection>\nauto collectUnsafe(Collection&& c)\n    -> decltype(collectUnsafe(c.begin(), c.end())) {\n  return collectUnsafe(c.begin(), c.end());\n}\n\n/// Like collectAll, but will short circuit on the first exception. Thus, the\n/// type of the returned SemiFuture is std::tuple<T1, T2, ...> instead of\n/// std::tuple<Try<T1>, Try<T2>, ...>\ntemplate <typename... Fs>\nSemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect(\n    Fs&&... fs);\n\n/** The result is a pair of the index of the first Future to complete and\n  the Try. If multiple Futures complete at the same time (or are already\n  complete when passed in), the \"winner\" is chosen non-deterministically.\n\n  This function is thread-safe for Futures running on different threads.\n  */\ntemplate <class InputIterator>\nSemiFuture<std::pair<\n    size_t,\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>\ncollectAny(InputIterator first, InputIterator last);\n\n/// Sugar for the most common case\ntemplate <class Collection>\nauto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) {\n  return collectAny(c.begin(), c.end());\n}\n\n/** Similar to collectAny, collectAnyWithoutException return the first Future to\n * complete without exceptions. If none of the future complete without\n * exceptions, the last exception will be returned as a result.\n */\ntemplate <class InputIterator>\nSemiFuture<std::pair<\n    size_t,\n    typename std::iterator_traits<InputIterator>::value_type::value_type>>\ncollectAnyWithoutException(InputIterator first, InputIterator last);\n\n/// Sugar for the most common case\ntemplate <class Collection>\nauto collectAnyWithoutException(Collection&& c)\n    -> decltype(collectAnyWithoutException(c.begin(), c.end())) {\n  return collectAnyWithoutException(c.begin(), c.end());\n}\n\n/** when n Futures have completed, the Future completes with a vector of\n  the index and Try of those n Futures (the indices refer to the original\n  order, but the result vector will be in an arbitrary order)\n\n  Not thread safe.\n  */\ntemplate <class InputIterator>\nSemiFuture<std::vector<std::pair<\n    size_t,\n    Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>\ncollectN(InputIterator first, InputIterator last, size_t n);\n\n/// Sugar for the most common case\ntemplate <class Collection>\nauto collectN(Collection&& c, size_t n)\n    -> decltype(collectN(c.begin(), c.end(), n)) {\n  return collectN(c.begin(), c.end(), n);\n}\n\n/** window creates up to n Futures using the values\n    in the collection, and then another Future for each Future\n    that completes\n\n    this is basically a sliding window of Futures of size n\n\n    func must return a Future for each value in input\n  */\ntemplate <\n    class Collection,\n    class F,\n    class ItT = typename std::iterator_traits<\n        typename Collection::iterator>::value_type,\n    class Result = typename invoke_result_t<F, ItT&&>::value_type>\nstd::vector<Future<Result>> window(Collection input, F func, size_t n);\n\ntemplate <\n    class Collection,\n    class F,\n    class ItT = typename std::iterator_traits<\n        typename Collection::iterator>::value_type,\n    class Result = typename invoke_result_t<F, ItT&&>::value_type>\nstd::vector<Future<Result>> window(\n    Executor::KeepAlive<> executor, Collection input, F func, size_t n);\n\ntemplate <typename F, typename T, typename ItT>\nusing MaybeTryArg = typename std::\n    conditional<is_invocable_v<F, T&&, Try<ItT>&&>, Try<ItT>, ItT>::type;\n\n/** repeatedly calls func on every result, e.g.\n    reduce(reduce(reduce(T initial, result of first), result of second), ...)\n\n    The type of the final result is a Future of the type of the initial value.\n\n    Func can either return a T, or a Future<T>\n\n    func is called in order of the input, see unorderedReduce if that is not\n    a requirement\n  */\ntemplate <class It, class T, class F>\nFuture<T> reduce(It first, It last, T&& initial, F&& func);\n\n/// Sugar for the most common case\ntemplate <class Collection, class T, class F>\nauto reduce(Collection&& c, T&& initial, F&& func) -> decltype(folly::reduce(\n    c.begin(), c.end(), static_cast<T&&>(initial), static_cast<F&&>(func))) {\n  return folly::reduce(\n      c.begin(), c.end(), static_cast<T&&>(initial), static_cast<F&&>(func));\n}\n\n/** like reduce, but calls func on finished futures as they complete\n    does NOT keep the order of the input\n  */\ntemplate <class It, class T, class F>\nFuture<T> unorderedReduce(It first, It last, T initial, F func);\n\n/// Sugar for the most common case\ntemplate <class Collection, class T, class F>\nauto unorderedReduce(Collection&& c, T&& initial, F&& func)\n    -> decltype(folly::unorderedReduce(\n        c.begin(),\n        c.end(),\n        static_cast<T&&>(initial),\n        static_cast<F&&>(func))) {\n  return folly::unorderedReduce(\n      c.begin(), c.end(), static_cast<T&&>(initial), static_cast<F&&>(func));\n}\n\n/// Carry out the computation contained in the given future if\n/// while the predicate continues to hold.\n///\n/// if thunk behaves like std::function<Future<T2>(void)>\n///    returns Future<Unit>\n/// if thunk behaves like std::function<SemiFuture<T2>(void)>\n///    returns SemiFuture<Unit>\n/// predicate behaves like std::function<bool(void)>\ntemplate <class P, class F>\ntypename std::enable_if<isFuture<invoke_result_t<F>>::value, Future<Unit>>::type\nwhileDo(P&& predicate, F&& thunk);\ntemplate <class P, class F>\ntypename std::\n    enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type\n    whileDo(P&& predicate, F&& thunk);\n\n/// Repeat the given future (i.e., the computation it contains) n times.\n///\n/// thunk behaves like\n///   std::function<Future<T2>(void)>\n/// or\n///   std::function<SemiFuture<T2>(void)>\ntemplate <class F>\nauto times(int n, F&& thunk);\n} // namespace folly\n\n#if FOLLY_HAS_COROUTINES\n\nnamespace folly {\nnamespace detail {\n\ntemplate <typename T>\nclass FutureAwaiter {\n public:\n  explicit FutureAwaiter(folly::Future<T>&& future) noexcept\n      : future_(std::move(future)) {}\n\n  bool await_ready() noexcept {\n    if (future_.isReady()) {\n      result_ = std::move(future_.result());\n      return true;\n    }\n    return false;\n  }\n\n  T await_resume() { return std::move(result_).value(); }\n\n  Try<drop_unit_t<T>> await_resume_try() {\n    return static_cast<Try<drop_unit_t<T>>>(std::move(result_));\n  }\n\n  // Precondition: The Future must not already have a callback, or\n  // `setCallback_` will throw `FutureAlreadyContinued` when resumed.\n  FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES bool await_suspend(\n      coro::coroutine_handle<> h) noexcept {\n    // FutureAwaiter may get destroyed as soon as the callback is executed.\n    // Make sure the future object doesn't get destroyed until setCallback_\n    // returns.\n    auto future = std::move(future_);\n    try {\n      future.setCallback_(\n          [this, h](Executor::KeepAlive<>&&, Try<T>&& result) mutable {\n            result_ = std::move(result);\n            h.resume();\n          });\n      return true; // Suspend: callback will resume later\n    } catch (...) {\n      // Before `await_suspend` was made noexcept, FutureAlreadyContinued\n      // could escape. Production code may rely on this, so we forward the\n      // error to `await_resume` instead of crashing.\n      //\n      // Only FutureAlreadyContinued should be catchable here:\n      // - FutureInvalid can't happen because co_viaIfAsync (noexcept) would\n      //   have already terminated if the future was invalid.\n      // - In debug builds, the DCHECK in Core::setExecutor fires inside\n      //   co_viaIfAsync if a callback was already attached.\n      // - In opt builds, we reach here and catch FutureAlreadyContinued.\n      DCHECK(\n          current_exception_wrapper()\n              .is_compatible_with<FutureAlreadyContinued>());\n      result_ = Try<T>(current_exception_wrapper());\n      return false; // Don't suspend: resume immediately with error\n    }\n  }\n\n private:\n  folly::Future<T> future_;\n  folly::Try<T> result_;\n};\n\n} // namespace detail\n\ntemplate <typename T>\ninline detail::FutureAwaiter<T>\n/* implicit */ operator co_await(Future<T>&& future) noexcept {\n  return detail::FutureAwaiter<T>(std::move(future));\n}\n\n} // namespace folly\n#endif\n\n#include <folly/futures/Future-inl.h> // IWYU pragma: export\n"
  },
  {
    "path": "folly/futures/FutureSplitter.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/futures/Future.h>\n#include <folly/futures/SharedPromise.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\nclass FOLLY_EXPORT FutureSplitterInvalid : public FutureException {\n public:\n  FutureSplitterInvalid() = default;\n  char const* what() const noexcept override {\n    return \"No Future in this FutureSplitter\";\n  }\n};\n\n/*\n * FutureSplitter provides a `getFuture()' method which can be called multiple\n * times, returning a new Future each time. These futures are completed when the\n * original Future passed to the FutureSplitter constructor is completed, and\n * are completed on the same executor (if any) and at the same priority as the\n * original Future. Calls to `getFuture()' after that time return a completed\n * Future.\n */\ntemplate <class T>\nclass FutureSplitter {\n public:\n  /**\n   * Default constructor for convenience only. It is an error to call\n   * `getFuture()` on a default-constructed FutureSplitter which has not had\n   * a correctly-constructed FutureSplitter copy- or move-assigned into it.\n   */\n  FutureSplitter() = default;\n\n  /**\n   * Provide a way to split a Future<T>.\n   */\n  explicit FutureSplitter(Future<T>&& future)\n      : promise_(std::make_shared<SharedPromise<T>>()),\n        e_(getExecutorFrom(future)) {\n    std::move(future).thenTry([promise = promise_](Try<T>&& theTry) {\n      promise->setTry(std::move(theTry));\n    });\n  }\n\n  /**\n   * This can be called an unlimited number of times per FutureSplitter.\n   */\n  Future<T> getFuture() {\n    if (promise_ == nullptr) {\n      throw_exception<FutureSplitterInvalid>();\n    }\n    return promise_->getSemiFuture().via(e_);\n  }\n\n  /**\n   * This can be called an unlimited number of times per FutureSplitter.\n   */\n  SemiFuture<T> getSemiFuture() {\n    if (promise_ == nullptr) {\n      throw_exception<FutureSplitterInvalid>();\n    }\n    return promise_->getSemiFuture();\n  }\n\n private:\n  std::shared_ptr<SharedPromise<T>> promise_;\n  Executor::KeepAlive<> e_;\n\n  static Executor* getExecutorFrom(Future<T>& f) {\n    // If the passed future had a null executor, use an inline executor\n    // to ensure that .via is safe\n    auto* e = f.getExecutor();\n    return e ? e : &InlineExecutor::instance();\n  }\n};\n\n/**\n * Convenience function, allowing us to exploit template argument deduction to\n * improve readability.\n */\ntemplate <class T>\nFutureSplitter<T> splitFuture(Future<T>&& future) {\n  return FutureSplitter<T>{std::move(future)};\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/HeapTimekeeper.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/HeapTimekeeper.h>\n\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include <folly/Portability.h>\n#include <folly/container/IntrusiveHeap.h>\n#include <folly/lang/SafeAssert.h>\n#include <folly/synchronization/DistributedMutex.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n#include <folly/synchronization/SaturatingSemaphore.h>\n#include <folly/synchronization/WaitOptions.h>\n#include <folly/system/ThreadName.h>\n\nnamespace folly {\n\nclass HeapTimekeeper::Timeout : public IntrusiveHeapNode<> {\n public:\n  struct DecRef {\n    void operator()(Timeout* timeout) const { timeout->decRef(); }\n  };\n  using Ref = std::unique_ptr<Timeout, DecRef>;\n\n  static std::pair<Ref, SemiFuture<Unit>> create(\n      HeapTimekeeper& parent, Clock::time_point expiration);\n\n  void decRef();\n  bool tryFulfill(Try<Unit> t);\n\n  bool operator<(const Timeout& other) const {\n    return expiration > other.expiration;\n  }\n\n  const Clock::time_point expiration;\n\n private:\n  static void interruptHandler(\n      Ref self, std::shared_ptr<State> state, exception_wrapper ew);\n\n  Timeout(HeapTimekeeper& parent, Clock::time_point exp, Promise<Unit> promise);\n\n  std::atomic<uint8_t> refCount_ = 2; // Heap and interrupt handler.\n  relaxed_atomic<bool> fulfilled_ = false;\n  Promise<Unit> promise_;\n};\n\nclass HeapTimekeeper::State {\n public:\n  struct Op {\n    enum class Type { kSchedule, kCancel };\n\n    Type type;\n    Timeout::Ref timeout;\n  };\n\n  State() { clearAndAdjustCapacity(queue_); }\n  ~State() {\n    // On some Windows configurations, threads may be (uncleanly) terminated on\n    // process exit before singleton destructors run, so we cannot guarantee any\n    // invariants on destruction.\n    if constexpr (kIsWindows) {\n      return;\n    }\n\n    // State is shared with future, but it should only be destroyed once the\n    // worker thread has drained it completely.\n    CHECK_EQ(queue_.size(), 0);\n    CHECK(heap_.empty());\n  }\n\n  static void clearAndAdjustCapacity(std::vector<Op>& queue);\n\n  void enqueue(Op::Type type, Timeout::Ref&& timeout);\n  void shutdown();\n\n  void worker();\n\n private:\n  using Semaphore = SaturatingSemaphore<>;\n\n  static constexpr size_t kQueueBatchSize = 256;\n  // Queue capacity is kept in this band to make sure that it is reallocated\n  // under the lock as infrequently as possible.\n  static constexpr size_t kDefaultQueueCapacity = 2 * kQueueBatchSize;\n  static constexpr size_t kMaxQueueCapacity = 2 * kDefaultQueueCapacity;\n\n  DistributedMutex mutex_;\n  // These variables are synchronized using mutex_. nextWakeUp_ is only modified\n  // by the worker thread, so it can be read back in that thread without a lock.\n  bool stop_ = false;\n  std::vector<Op> queue_;\n  Clock::time_point nextWakeUp_ = Clock::time_point::max();\n  Semaphore* wakeUp_ = nullptr;\n\n  // Only accessed by the worker thread.\n  IntrusiveHeap<Timeout> heap_;\n};\n\n/* static */ std::pair<HeapTimekeeper::Timeout::Ref, SemiFuture<Unit>>\nHeapTimekeeper::Timeout::create(\n    HeapTimekeeper& parent, Clock::time_point expiration) {\n  auto [promise, sf] = makePromiseContract<Unit>();\n  auto timeout =\n      Timeout::Ref{new Timeout{parent, expiration, std::move(promise)}};\n  return {std::move(timeout), std::move(sf)};\n}\n\nHeapTimekeeper::Timeout::Timeout(\n    HeapTimekeeper& parent, Clock::time_point exp, Promise<Unit> promise)\n    : expiration(exp), promise_(std::move(promise)) {\n  promise_.setInterruptHandler(\n      [self = Ref{this}, state = parent.state_](exception_wrapper ew) mutable {\n        interruptHandler(std::move(self), std::move(state), std::move(ew));\n      });\n}\n\n/* static */ void HeapTimekeeper::Timeout::interruptHandler(\n    Ref self, std::shared_ptr<State> state, exception_wrapper ew) {\n  if (!self->tryFulfill(Try<Unit>{std::move(ew)})) {\n    return; // Timeout has already expired, nothing to do.\n  }\n\n  state->enqueue(State::Op::Type::kCancel, std::move(self));\n}\n\nbool HeapTimekeeper::Timeout::tryFulfill(Try<Unit> t) {\n  if (fulfilled_.exchange(true)) {\n    return false;\n  }\n  // Break the refcount cycle between promise and interrupt handler.\n  auto promise = std::move(promise_);\n  promise.setTry(std::move(t));\n  return true;\n}\n\nvoid HeapTimekeeper::Timeout::decRef() {\n  auto before = refCount_.fetch_sub(1, std::memory_order_acq_rel);\n  FOLLY_SAFE_DCHECK(before > 0);\n  if (before == 1) {\n    delete this;\n  }\n}\n\n/* static */ void HeapTimekeeper::State::clearAndAdjustCapacity(\n    std::vector<Op>& queue) {\n  queue.clear();\n  if (queue.capacity() > kMaxQueueCapacity) {\n    std::vector<Op>{}.swap(queue);\n  }\n  if (queue.capacity() < kDefaultQueueCapacity) {\n    queue.reserve(kDefaultQueueCapacity);\n  }\n}\n\nvoid HeapTimekeeper::State::enqueue(Op::Type type, Timeout::Ref&& timeout) {\n  const auto* timeoutPtr = timeout.get();\n  Op op;\n  op.type = type;\n  op.timeout = std::move(timeout);\n\n  auto wakeUp = mutex_.lock_combine([&]() -> Semaphore* {\n    if (stop_) {\n      CHECK(type == Op::Type::kCancel)\n          << \"after() called on a destroying HeapTimekeeper\";\n      // If the timekeeper is shut down it won't process the queue anymore, so\n      // just decRef the timeout inline, there is nothing to cancel.\n      return nullptr;\n    }\n\n    queue_.push_back(std::move(op));\n    if (wakeUp_ == nullptr) {\n      // No semaphore set, so the worker thread won't go to sleep before\n      // processing this op.\n      return nullptr;\n    }\n    // Wake up the worker only if we have enough ops to process or we need to\n    // update the wake-up time. We don't care about cancellations being\n    // processed in a timely fashion, as the promise is already fulfilled, so we\n    // can avoid an unnecessary wake-up.\n    if (queue_.size() == kQueueBatchSize ||\n        (type == Op::Type::kSchedule && nextWakeUp_ > timeoutPtr->expiration)) {\n      // Signal that we are waking up the worker and others don't have to.\n      return std::exchange(wakeUp_, nullptr);\n    }\n\n    return nullptr;\n  });\n\n  if (wakeUp) {\n    wakeUp->post();\n  }\n}\n\nvoid HeapTimekeeper::State::shutdown() {\n  auto wakeUp = mutex_.lock_combine([&] {\n    stop_ = true;\n    return std::exchange(wakeUp_, nullptr);\n  });\n  if (wakeUp) {\n    wakeUp->post();\n  }\n}\n\nvoid HeapTimekeeper::State::worker() {\n  setThreadName(\"FutureTimekeepr\");\n  std::vector<Op> queue;\n  while (true) {\n    clearAndAdjustCapacity(queue);\n    std::optional<Semaphore> wakeUp;\n    bool stop = false;\n    mutex_.lock_combine([&] {\n      FOLLY_SAFE_DCHECK(wakeUp_ == nullptr);\n\n      if (!queue_.empty()) {\n        queue_.swap(queue);\n        return;\n      }\n      if (stop_) {\n        // Only stop if the queue is empty, as we need to manage the lifetime of\n        // the timeouts in it.\n        stop = true;\n        return;\n      }\n\n      // No queue to process, wait for the next timeout, but allow callers to\n      // wake us up if we need to update the next wakeup time.\n      wakeUp.emplace();\n      wakeUp_ = &*wakeUp;\n      nextWakeUp_ =\n          heap_.empty() ? Clock::time_point::max() : heap_.top()->expiration;\n    });\n\n    if (stop) {\n      break;\n    }\n\n    if (wakeUp) {\n      WaitOptions wo;\n      // It's very likely that the wait will timeout, so there is no point in\n      // spinning unless the wait time is shorter than the default spin time.\n      wo.spin_max(\n          nextWakeUp_ - Clock::now() > wo.spin_max()\n              ? std::chrono::nanoseconds{0}\n              : wo.spin_max());\n      if (!wakeUp->try_wait_until(nextWakeUp_)) {\n        if (mutex_.lock_combine([&] {\n              return std::exchange(wakeUp_, nullptr) == nullptr;\n            })) {\n          // Someone stole the reference to the semaphore, we must wait for them\n          // to post it so we can destroy it.\n          wakeUp->wait();\n        }\n      }\n    }\n\n    for (auto& op : queue) {\n      switch (op.type) {\n        case Op::Type::kSchedule:\n          heap_.push(op.timeout.release()); // Heap takes ownership.\n          break;\n        case Op::Type::kCancel:\n          if (op.timeout->isLinked()) {\n            heap_.erase(op.timeout.get());\n            op.timeout->decRef();\n          }\n          break;\n      }\n    }\n\n    while (!heap_.empty() && heap_.top()->expiration <= Clock::now()) {\n      auto* timeout = heap_.pop();\n      timeout->tryFulfill(Try<Unit>{unit});\n      timeout->decRef();\n    }\n  }\n\n  // Cancel all the leftover timeouts.\n  while (!heap_.empty()) {\n    auto* timeout = heap_.pop();\n    timeout->tryFulfill(Try<Unit>{exception_wrapper{FutureNoTimekeeper{}}});\n    timeout->decRef();\n  }\n}\n\nHeapTimekeeper::HeapTimekeeper() : state_(std::make_shared<State>()) {\n  thread_ = std::thread{[this] { worker(); }};\n}\n\nHeapTimekeeper::~HeapTimekeeper() {\n  state_->shutdown();\n  thread_.join();\n}\n\nSemiFuture<Unit> HeapTimekeeper::after(HighResDuration dur) {\n  auto [timeout, sf] = Timeout::create(*this, Clock::now() + dur);\n  state_->enqueue(State::Op::Type::kSchedule, std::move(timeout));\n  return std::move(sf);\n}\n\nvoid HeapTimekeeper::worker() const {\n  state_->worker();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/HeapTimekeeper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <thread>\n\n#include <folly/futures/Future.h>\n\nnamespace folly {\n\n/**\n * A Timekeeper with a dedicated thread that manages the timeouts using a\n * heap. Timeouts can be scheduled with microsecond resolution, though in\n * practice the accuracy depends on the OS scheduler's ability to wake up the\n * worker thread in a timely fashion.\n */\nclass HeapTimekeeper : public Timekeeper {\n public:\n  HeapTimekeeper();\n  ~HeapTimekeeper() override;\n\n  SemiFuture<Unit> after(HighResDuration) override;\n\n protected:\n  virtual void worker() const;\n\n private:\n  using Clock = std::chrono::steady_clock;\n\n  class Timeout;\n  class State;\n\n  // Shared with the futures, so that they can survive the timekeeper.\n  std::shared_ptr<State> state_;\n  std::thread thread_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/ManualTimekeeper.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/ManualTimekeeper.h>\n\n#include <folly/synchronization/AtomicUtil.h>\n\nnamespace folly {\n\nManualTimekeeper::ManualTimekeeper() : now_{std::chrono::steady_clock::now()} {}\n\nSemiFuture<Unit> ManualTimekeeper::after(HighResDuration dur) {\n  auto [promise, future] = folly::makePromiseContract<Unit>();\n  if (dur.count() == 0) {\n    promise.setValue(folly::unit);\n  } else {\n    auto handler = TimeoutHandler::create(std::move(promise));\n    schedule_.withWLock([&handler, key = now() + dur](auto& schedule) {\n      schedule.emplace(key, std::move(handler));\n    });\n  }\n  return std::move(future);\n}\n\nvoid ManualTimekeeper::fulfillReady(TimeoutSchedule& schedule) {\n  auto start = schedule.begin();\n  auto end = schedule.upper_bound(now());\n  for (auto iter = start; iter != end; iter++) {\n    iter->second->trySetTimeout();\n  }\n  schedule.erase(start, end);\n}\n\nvoid ManualTimekeeper::advance(Duration dur) {\n  atomic_fetch_modify(\n      now_, [=](auto val) { return val + dur; }, std::memory_order_relaxed);\n\n  schedule_.withWLock([this](auto& schedule) { fulfillReady(schedule); });\n}\n\nvoid ManualTimekeeper::advanceToNext() {\n  schedule_.withWLock([this](auto& sched) {\n    if (!sched.empty()) {\n      // NOTE: +1 to avoid rounding errors.\n      atomic_fetch_modify(\n          now_,\n          [=](auto) { return sched.begin()->first + Duration{1}; },\n          std::memory_order_relaxed);\n\n      fulfillReady(sched);\n    }\n  });\n}\n\nstd::chrono::steady_clock::time_point ManualTimekeeper::now() const {\n  return now_.load(std::memory_order_relaxed);\n}\n\nstd::size_t ManualTimekeeper::numScheduled() const {\n  return schedule_.withRLock([](const auto& sched) { return sched.size(); });\n}\n\n/* static */ std::shared_ptr<ManualTimekeeper::TimeoutHandler>\nManualTimekeeper::TimeoutHandler::create(Promise<Unit>&& promise) {\n  auto handler = std::make_shared<TimeoutHandler>(std::move(promise));\n  handler->promise_.setInterruptHandler(\n      [handler = std::weak_ptr(handler)](exception_wrapper ew) {\n        if (auto localTimeout = handler.lock()) {\n          localTimeout->trySetException(std::move(ew));\n        }\n      });\n  return handler;\n}\n\nManualTimekeeper::TimeoutHandler::TimeoutHandler(Promise<Unit>&& promise)\n    : promise_(std::move(promise)) {}\n\nvoid ManualTimekeeper::TimeoutHandler::trySetTimeout() {\n  if (canSet()) {\n    promise_.setValue(unit);\n  }\n}\nvoid ManualTimekeeper::TimeoutHandler::trySetException(exception_wrapper&& ex) {\n  if (canSet()) {\n    promise_.setException(std::move(ex));\n  }\n}\n\nbool ManualTimekeeper::TimeoutHandler::canSet() {\n  bool expected = false;\n  return fulfilled_.compare_exchange_strong(expected, /* desired */ true);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/ManualTimekeeper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <chrono>\n#include <map>\n#include <memory>\n\n#include <folly/Synchronized.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n\nnamespace folly {\n\n// Manually controlled Timekeeper for unit testing.\n//\n// We assume advance(), now(), and numScheduled() are called from only a single\n// thread, while after() can safely be called from multiple threads.\nclass ManualTimekeeper : public folly::Timekeeper {\n public:\n  explicit ManualTimekeeper();\n\n  /// The returned future is completed when someone calls advance and pushes the\n  /// executor's clock to a value greater than or equal to (now() + dur)\n  SemiFuture<Unit> after(folly::HighResDuration dur) override;\n\n  /// Advance the timekeeper's clock to (now() + dur).  All futures with target\n  /// time points less than or equal to (now() + dur) are fulfilled after the\n  /// call to advance() returns\n  void advance(folly::Duration dur);\n\n  /// Returns the current clock value in the timekeeper.  This is advanced only\n  /// when someone calls advance()\n  std::chrono::steady_clock::time_point now() const;\n\n  /// Returns the number of futures that are pending and have not yet been\n  /// fulfilled\n  std::size_t numScheduled() const;\n\n  /// Advance the timekeeper's clock at least enough to release the next pending\n  /// event.\n  void advanceToNext();\n\n private:\n  class TimeoutHandler {\n   public:\n    explicit TimeoutHandler(Promise<Unit>&& promise);\n\n    static std::shared_ptr<TimeoutHandler> create(Promise<Unit>&& promise);\n\n    void trySetTimeout();\n    void trySetException(exception_wrapper&& ex);\n\n   private:\n    std::atomic_bool fulfilled_{false};\n    Promise<Unit> promise_;\n\n    bool canSet();\n  };\n  using TimeoutSchedule = std::multimap<\n      std::chrono::steady_clock::time_point,\n      std::shared_ptr<TimeoutHandler>>;\n\n  void fulfillReady(TimeoutSchedule& schedule);\n\n  std::atomic<std::chrono::steady_clock::time_point> now_;\n  folly::Synchronized<TimeoutSchedule> schedule_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Portability.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h> // @shim\n"
  },
  {
    "path": "folly/futures/Promise-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <thread>\n#include <utility>\n\n#include <folly/executors/InlineExecutor.h>\n#include <folly/futures/detail/Core.h>\n\nnamespace folly {\n\nnamespace futures {\nnamespace detail {\ntemplate <typename T>\nvoid coreDetachPromiseMaybeWithResult(Core<T>& core) {\n  if (!core.hasResult()) {\n    core.setResult(Try<T>(exception_wrapper(BrokenPromise(tag<T>))));\n  }\n  core.detachPromise();\n}\ntemplate <typename T>\nvoid setTry(Promise<T>& p, Executor::KeepAlive<>&& ka, Try<T>&& t) {\n  p.setTry(std::move(ka), std::move(t));\n}\n} // namespace detail\n} // namespace futures\n\ntemplate <class T>\nPromise<T> Promise<T>::makeEmpty() noexcept {\n  return Promise<T>(futures::detail::EmptyConstruct{});\n}\n\ntemplate <class T>\nPromise<T>::Promise() : retrieved_(false), core_(Core::make()) {}\n\ntemplate <class T>\nPromise<T>::Promise(Promise<T>&& other) noexcept\n    : retrieved_(std::exchange(other.retrieved_, false)),\n      core_(std::exchange(other.core_, nullptr)) {}\n\ntemplate <class T>\nPromise<T>& Promise<T>::operator=(Promise<T>&& other) noexcept {\n  detach();\n  retrieved_ = std::exchange(other.retrieved_, false);\n  core_ = std::exchange(other.core_, nullptr);\n  return *this;\n}\n\ntemplate <class T>\nvoid Promise<T>::throwIfFulfilled() const {\n  if (getCore().hasResult()) {\n    throw_exception<PromiseAlreadySatisfied>();\n  }\n}\n\ntemplate <class T>\nPromise<T>::Promise(futures::detail::EmptyConstruct) noexcept\n    : retrieved_(false), core_(nullptr) {}\n\ntemplate <class T>\nPromise<T>::~Promise() {\n  detach();\n}\n\ntemplate <class T>\nvoid Promise<T>::detach() {\n  if (core_) {\n    if (!retrieved_) {\n      core_->detachFuture();\n    }\n    futures::detail::coreDetachPromiseMaybeWithResult(*core_);\n    core_ = nullptr;\n  }\n}\n\ntemplate <class T>\nSemiFuture<T> Promise<T>::getSemiFuture() {\n  if (retrieved_) {\n    throw_exception<FutureAlreadyRetrieved>();\n  }\n  retrieved_ = true;\n  return SemiFuture<T>(&getCore());\n}\n\ntemplate <class T>\nFuture<T> Promise<T>::getFuture() {\n  // An InlineExecutor approximates the old behaviour of continuations\n  // running inine on setting the value of the promise.\n  return getSemiFuture().via(&InlineExecutor::instance());\n}\n\ntemplate <class T>\ntemplate <class E>\ntypename std::enable_if<\n    std::is_base_of<std::exception, typename std::decay<E>::type>::value>::type\nPromise<T>::setException(E&& e) {\n  setException(\n      make_exception_wrapper<typename std::decay<E>::type>(std::forward<E>(e)));\n}\n\ntemplate <class T>\nvoid Promise<T>::setException(exception_wrapper ew) {\n  setTry(Try<T>(std::move(ew)));\n}\n\ntemplate <class T>\ntemplate <typename F>\nvoid Promise<T>::setInterruptHandler(F&& fn) {\n  getCore().setInterruptHandler(static_cast<F&&>(fn));\n}\n\ntemplate <class T>\nvoid Promise<T>::setTry(Try<T>&& t) {\n  throwIfFulfilled();\n  core_->setResult(std::move(t));\n}\n\ntemplate <class T>\nvoid Promise<T>::setTry(Executor::KeepAlive<>&& ka, Try<T>&& t) {\n  throwIfFulfilled();\n  core_->setResult(std::move(ka), std::move(t));\n}\n\ntemplate <class T>\ntemplate <class M>\nvoid Promise<T>::setValue(M&& v) {\n  static_assert(!std::is_same<T, void>::value, \"Use setValue() instead\");\n\n  setTry(Try<T>(static_cast<M&&>(v)));\n}\n\ntemplate <class T>\ntemplate <class F>\nvoid Promise<T>::setWith(F&& func) {\n  throwIfFulfilled();\n  setTry(makeTryWith(static_cast<F&&>(func)));\n}\n\ntemplate <class T>\nbool Promise<T>::isFulfilled() const noexcept {\n  if (core_) {\n    return core_->hasResult();\n  }\n  return true;\n}\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\n// limited to the instances unconditionally forced by the futures library\nextern template class Promise<Unit>;\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Promise.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Promise.h>\n\nnamespace folly {\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\ntemplate class Promise<Unit>;\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/Promise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <functional>\n\n#include <folly/Portability.h>\n#include <folly/Try.h>\n#include <folly/futures/detail/Core.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Pretty.h>\n\nnamespace folly {\n\nclass FOLLY_EXPORT PromiseException : public std::logic_error {\n public:\n  using std::logic_error::logic_error;\n  PromiseException() : std::logic_error{\"\"} {}\n};\n\nclass FOLLY_EXPORT PromiseInvalid : public PromiseException {\n public:\n  PromiseInvalid() = default;\n  char const* what() const noexcept override { return \"Promise invalid\"; }\n};\n\nclass FOLLY_EXPORT PromiseAlreadySatisfied : public PromiseException {\n public:\n  PromiseAlreadySatisfied() = default;\n  char const* what() const noexcept override {\n    return \"Promise already satisfied\";\n  }\n};\n\nclass FOLLY_EXPORT FutureAlreadyRetrieved : public PromiseException {\n public:\n  FutureAlreadyRetrieved() = default;\n  char const* what() const noexcept override {\n    return \"Future already retrieved\";\n  }\n};\n\nclass FOLLY_EXPORT BrokenPromise : public PromiseException {\n private:\n  template <typename T>\n  static consteval auto make_error_message() {\n    constexpr auto prefix =\n        detail::pretty_carray_from(\"Broken promise for type name `\");\n    constexpr auto prefix_size = std::size(prefix.data);\n    constexpr auto name = detail::pretty_name_carray<T>();\n    constexpr auto name_size = std::size(name.data);\n    c_array<char, name_size - 1 + prefix_size - 1 + 2> ret{};\n    char* dest = ret.data;\n    dest = detail::pretty_carray_copy(dest, prefix.data, prefix_size - 1);\n    dest = detail::pretty_carray_copy(dest, name.data, name_size - 1);\n    detail::pretty_carray_copy(dest, \"`\", 2);\n    return ret;\n  }\n  template <typename T>\n  static constexpr auto error_message = make_error_message<T>();\n\n  const char* const what_{};\n\n public:\n  template <typename T>\n  explicit BrokenPromise(tag_t<T>) : what_{error_message<T>.data} {}\n  char const* what() const noexcept override { return what_; }\n};\n\n// forward declaration\ntemplate <class T>\nclass SemiFuture;\ntemplate <class T>\nclass Future;\ntemplate <class T>\nclass Promise;\n\nnamespace futures {\nnamespace detail {\nclass FutureBaseHelper;\ntemplate <class T>\nclass FutureBase;\nstruct EmptyConstruct {};\ntemplate <typename T, typename F>\nclass CoreCallbackState;\ntemplate <typename T>\nvoid setTry(Promise<T>& p, Executor::KeepAlive<>&& ka, Try<T>&& t);\n\nstruct MakeRetrievedFromStolenCoreTag {};\n} // namespace detail\n} // namespace futures\n\n/// Promises and futures provide a potentially nonblocking mechanism\n///   to execute a producer/consumer operation concurrently, with\n///   threading/pools controlled via an executor. There are multiple potential\n///   patterns for using promises and futures including some that block the\n///   caller, though that is discouraged; it should be used only when necessary.\n///\n/// One typical pattern uses a series of calls to set up a small, limited\n///   program that...\n///\n/// - ...performs the desired operations (based on a lambda)...\n/// - ...on an asynchronously provided input (an exception or a value)...\n/// - ...lazily, when that input is ready (without blocking the caller)...\n/// - ...using appropriate execution resources (determined by the executor)...\n/// - ...then after constructing the 'program,' launches the asynchronous\n///   producer.\n///\n/// That usage pattern looks roughly like this:\n///\n///   auto [p, f] = makePromiseContract(executor);\n///   g = std::move(f).then([](MyValue&& x) {\n///       ...executor runs this code if/when a MyValue is ready...\n///     });\n///   ...launch the async producer that eventually calls p.setResult()...\n///\n/// This is just one of many potential usage patterns. It has the desired\n/// property of being nonblocking to the caller. Of course the `.then()`\n/// code is deferred until the produced value (or exception) is ready,\n/// but no code actually blocks pending completion of other operations.\n///\n/// The promise/future mechanism is limited to a single object of some arbitrary\n/// type. It also supports a (logically) void result, i.e., in cases where the\n/// continuation/consumer (the `.then()` code if using the above pattern) is not\n/// expecting a value because the 'producer' is running for its side-effects.\n///\n/// The primary data movement is from producer to consumer, however Promise and\n/// Future also provide a mechanism where the consumer can send an interruption\n/// message to the producer. The meaning and response to that interruption\n/// message is controlled by the promise; see `Promise::setInterruptHandler()`.\n///\n/// Neither Promise nor Future is thread-safe. All internal interactions\n/// between a promise and its associated future are thread-safe, provided that\n/// callers otherwise honor the promise's contract and the future's contract.\n///\n/// Logically there are up to three threads (though in practice there are often\n/// fewer - one thread might take on more than one role):\n///\n/// - Set-up thread: thread used to construct the Promise, and often also to\n///   set up the SemiFuture/Future.\n/// - Producer thread: thread that produces the result.\n/// - Consumer thread: thread in which the continuation is invoked (a\n///   continuation is a callback provided to `.then` or to a variant).\n///\n/// For description purposes, the term 'shared state' is used to describe the\n///   logical state shared by the promise and the future. This 'third object'\n///   represents things like whether the result has been fulfilled, the value or\n///   exception in that result, and the data needed to handle interruption\n///   requests.\n///\n/// A promise can be in various logical states:\n///\n/// - valid vs. invalid (has vs. does not have a shared state, respectfully).\n/// - fulfilled vs. unfulfilled (an invalid promise is always fulfilled; a valid\n///   promise is fulfilled if the shared-state has a result).\n///\n/// A promise `p` may optionally have an associated future. This future, if it\n///   exists, may be either a SemiFuture or a Future, and is defined as the\n///   future (if any) that holds the same shared state as promise `p`.\n///   The associated future is initially the future returned from\n///   `p.getFuture()` or `p.getSemiFuture()`, but various operations\n///   may transfer the shared state from one future to another.\ntemplate <class T>\nclass Promise {\n public:\n  /// Returns an invalid promise.\n  ///\n  /// Postconditions:\n  ///\n  /// - `RESULT.valid() == false`\n  /// - `RESULT.isFulfilled() == true`\n  static Promise<T> makeEmpty() noexcept;\n\n  /// Constructs a valid but unfulfilled promise.\n  ///\n  /// Postconditions:\n  ///\n  /// - `valid() == true` (it will have a shared state)\n  /// - `isFulfilled() == false` (its shared state won't have a result)\n  Promise();\n\n  /// Postconditions:\n  ///\n  /// - If `valid()` and `!isFulfilled()`, the associated future (if any) will\n  ///   be completed with a `BrokenPromise` exception *as if* by\n  ///   `setException(...)`.\n  /// - If `valid()`, releases, possibly destroying, the shared state.\n  ~Promise();\n\n  // not copyable\n  Promise(Promise const&) = delete;\n  Promise& operator=(Promise const&) = delete;\n\n  /// Move ctor\n  ///\n  /// Postconditions:\n  ///\n  /// - `this` will have whatever shared-state was previously held by `other`\n  ///   (if any)\n  /// - `other.valid()` will be false (`other` will not have any shared state)\n  Promise(Promise<T>&& other) noexcept;\n\n  /// Move assignment\n  ///\n  /// Postconditions:\n  ///\n  /// - If `valid()` and `!isFulfilled()`, the associated future (if any) will\n  ///   be completed with a `BrokenPromise` exception *as if* by\n  ///   `setException(...)`.\n  /// - If `valid()`, releases, possibly destroying, the original shared state.\n  /// - `this` will have whatever shared-state was previously held by `other`\n  ///   (if any)\n  /// - `other.valid()` will be false (`other` will not have any shared state)\n  Promise& operator=(Promise<T>&& other) noexcept;\n\n  /// Return a SemiFuture associated with this Promise, sharing the same shared\n  /// state as `this`.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - neither getSemiFuture() nor getFuture() may have been called previously\n  ///   on `this` Promise (else throws FutureAlreadyRetrieved)\n  ///\n  /// Postconditions:\n  ///\n  /// - `RESULT.valid() == true`\n  /// - RESULT will share the same shared-state as `this`\n  ///\n  /// DEPRECATED: use `folly::makePromiseContract()` instead.\n  SemiFuture<T> getSemiFuture();\n\n  /// Return a Future associated with this Promise, sharing the same shared\n  /// state as `this`.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - neither getSemiFuture() nor getFuture() may have been called previously\n  ///   on `this` Promise (else throws FutureAlreadyRetrieved)\n  ///\n  /// Postconditions:\n  ///\n  /// - `RESULT.valid() == true`\n  /// - RESULT will share the same shared-state as `this`\n  ///\n  /// DEPRECATED: use `folly::makePromiseContract()` instead. If you can't use\n  ///   that, use `this->getSemiFuture()` then get a Future by calling `.via()`\n  ///   with an appropriate executor.\n  Future<T> getFuture();\n\n  /// Fulfill the Promise with an exception_wrapper.\n  ///\n  /// Sample usage:\n  ///\n  ///   Promise<MyValue> p = ...\n  ///   ...\n  ///   auto const ep = std::exception_ptr();\n  ///   auto const ew = exception_wrapper{ep};\n  ///   p.setException(ew);\n  ///\n  /// Functionally equivalent to `setTry(Try<T>(std::move(ew)))`\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)\n  ///\n  /// Postconditions:\n  ///\n  /// - `isFulfilled() == true`\n  /// - `valid() == true` (unchanged)\n  /// - The associated future (if any) will complete with the exception.\n  void setException(exception_wrapper ew);\n\n  /// Fulfill the Promise with exception `e` *as if* by\n  ///   `setException(make_exception_wrapper<E>(e))`.\n  ///\n  /// Please see `setException(exception_wrapper)` for semantics/contract.\n  template <class E>\n  typename std::enable_if<\n      std::is_base_of<std::exception, typename std::decay<E>::type>::value>::\n      type\n      setException(E&& e);\n\n  /// Sets a handler for the producer to receive a (logical) interruption\n  ///   request (exception) sent from the consumer via `future.raise()`.\n  ///\n  /// Details: The consumer calls `future.raise()` when it wishes to send a\n  ///   logical interruption message (an exception), and that exception/message\n  ///   is passed to `fn()`. The thread used to call `fn()` depends on timing\n  ///   (see Postconditions for threading details).\n  ///\n  /// Handler `fn()` can do anything you want, but if you bother to set one\n  ///   then you probably will want to (more or less immediately) fulfill the\n  ///   promise with an exception (or other special value) indicating how the\n  ///   interrupt was handled.\n  ///\n  /// This call silently does nothing if `isFulfilled()`.\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - `fn` must be copyable and must be invocable with\n  ///   `exception_wrapper const&`\n  /// - the code within `fn()` must be safe to run either synchronously within\n  ///   the `setInterruptHandler()` call or asynchronously within the consumer\n  ///   thread's call to `future.raise()`.\n  /// - the code within `fn()` must also be safe to run after this promise is\n  ///   fulfilled; this may have lifetime/race-case ramifications, e.g., if the\n  ///   code of `fn()` might access producer-resources that will be destroyed,\n  ///   then the destruction of those producer-resources must be deferred beyond\n  ///   the moment when this promise is fulfilled.\n  ///\n  /// Postconditions:\n  ///\n  /// - if the consumer calls `future.raise()` early enough (up to a particular\n  ///   moment within the `setInterruptHandler()` call), `fn()` will be called\n  ///   synchronously (in the current thread, during this call).\n  /// - if the consumer calls `future.raise()` after that moment within\n  ///   `setInterruptHandler()` but before this promise is fulfilled, `fn()`\n  ///   will be called asynchronously (in the consumer's thread, within the call\n  ///   to `future.raise()`).\n  /// - if the consumer calls `future.raise()` after this promise is fulfilled,\n  ///   `fn()` may or may not be called at all, and if it is called, it will be\n  ///   called asynchronously (within the consumer's call to `future.raise()`).\n  ///\n  /// IMPORTANT: `fn()` should return quickly since it could block this call\n  ///   to `promise.setInterruptHandler()` and/or a concurrent call to\n  ///   `future.raise()`. Those two functions contend on the same lock; those\n  ///   calls could block if `fn()` is invoked within one of those while the\n  ///   lock is held.\n  template <typename F>\n  void setInterruptHandler(F&& fn);\n\n  /// Fulfills a (logically) void Promise, that is, Promise<Unit>.\n  /// (If you want a void-promise, use Promise<Unit>, not Promise<void>.)\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)\n  ///\n  /// Postconditions:\n  ///\n  /// - `isFulfilled() == true`\n  /// - `valid() == true` (unchanged)\n  template <class B = T>\n  typename std::enable_if<std::is_same<Unit, B>::value, void>::type setValue() {\n    setTry(Try<T>(T()));\n  }\n\n  /// Fulfill the Promise with the specified value using perfect forwarding.\n  ///\n  /// Functionally equivalent to `setTry(Try<T>(std::forward<M>(value)))`\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)\n  ///\n  /// Postconditions:\n  ///\n  /// - `isFulfilled() == true`\n  /// - `valid() == true` (unchanged)\n  /// - The associated future will see the value, e.g., in its continuation.\n  template <class M>\n  void setValue(M&& value);\n\n  /// Fulfill the Promise with the specified Try (value or exception).\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)\n  ///\n  /// Postconditions:\n  ///\n  /// - `isFulfilled() == true`\n  /// - `valid() == true` (unchanged)\n  /// - The associated future will see the result, e.g., in its continuation.\n  void setTry(Try<T>&& t);\n\n  /// Fulfill this Promise with the result of a function that takes no\n  ///   arguments and returns something implicitly convertible to T.\n  ///\n  /// Example:\n  ///\n  ///   p.setWith([] { do something that may throw; return a T; });\n  ///\n  /// Functionally equivalent to `setTry(makeTryWith(static_cast<F&&>(func)));`\n  ///\n  /// Preconditions:\n  ///\n  /// - `valid() == true` (else throws PromiseInvalid)\n  /// - `isFulfilled() == false` (else throws PromiseAlreadySatisfied)\n  ///\n  /// Postconditions:\n  ///\n  /// - `func()` will be run synchronously (in this thread, during this call)\n  /// - If `func()` returns, the return value will be captured as if via\n  ///   `setValue()`\n  /// - If `func()` throws, the exception will be captured as if via\n  ///   `setException()`\n  /// - `isFulfilled() == true`\n  /// - `valid() == true` (unchanged)\n  /// - The associated future will see the result, e.g., in its continuation.\n  template <class F>\n  void setWith(F&& func);\n\n  /// true if this has a shared state;\n  ///   false if this has been consumed/moved-out.\n  bool valid() const noexcept { return core_ != nullptr; }\n\n  /// True if either this promise was fulfilled or is invalid.\n  ///\n  /// - True if `!valid()`\n  /// - True if `valid()` and this was fulfilled (a prior call to `setValue()`,\n  ///   `setTry()`, `setException()`, or `setWith()`)\n  bool isFulfilled() const noexcept;\n\n private:\n  friend class futures::detail::FutureBaseHelper;\n  template <class>\n  friend class futures::detail::FutureBase;\n  template <class>\n  friend class SemiFuture;\n  template <class>\n  friend class Future;\n  template <class, class>\n  friend class futures::detail::CoreCallbackState;\n  friend void futures::detail::setTry<T>(\n      Promise<T>& p, Executor::KeepAlive<>&& ka, Try<T>&& t);\n\n  // Whether the Future has been retrieved (a one-time operation).\n  bool retrieved_;\n\n  using Core = futures::detail::Core<T>;\n\n  // Throws PromiseInvalid if there is no shared state object; else returns it\n  // by ref.\n  //\n  // Implementation methods should usually use this instead of `this->core_`.\n  // The latter should be used only when you need the possibly-null pointer.\n  Core& getCore() { return getCoreImpl(core_); }\n  Core const& getCore() const { return getCoreImpl(core_); }\n\n  template <typename CoreT>\n  static CoreT& getCoreImpl(CoreT* core) {\n    if (!core) {\n      throw_exception<PromiseInvalid>();\n    }\n    return *core;\n  }\n\n  /// Fulfill the Promise with the specified Try (value or exception) and\n  /// propagate the completing executor.\n  void setTry(Executor::KeepAlive<>&& ka, Try<T>&& t);\n\n  // shared core state object\n  // usually you should use `getCore()` instead of directly accessing `core_`.\n  Core* core_;\n\n  explicit Promise(futures::detail::EmptyConstruct) noexcept;\n\n  void throwIfFulfilled() const;\n  void detach();\n\n  Promise(futures::detail::MakeRetrievedFromStolenCoreTag, Core& core) noexcept\n      : retrieved_{true}, core_{&core} {}\n};\n\n} // namespace folly\n\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise-inl.h>\n"
  },
  {
    "path": "folly/futures/Retrying.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Random.h>\n#include <folly/functional/Invoke.h>\n#include <folly/futures/Future.h>\n\nnamespace folly {\nnamespace futures {\n\n/**\n *  retrying and retryingUnsafe\n *\n *  Given a policy and a future-factory, creates futures according to the\n *  policy.\n *\n *  The policy must be moveable - retrying will move it a lot - and callable of\n *  any of the forms:\n *  - Future<bool>(size_t, exception_wrapper)\n *  - SemiFuture<bool>(size_t, exception_wrapper)\n *  - bool(size_t, exception_wrapper)\n *  Internally, the latter is transformed into the former in the obvious way.\n *  The first parameter is the attempt number of the next prospective attempt;\n *  the second parameter is the most recent exception. The policy returns a\n *  (Semi)Future<bool>  which, when completed with true, indicates that a retry\n *  is desired.\n *\n *  retrying always returns a SemiFuture and enforces transfer onto an executor\n *  in the implementation. This ensures that recursive policies are safe and\n *  run in the right place.\n *  retryingUnsafe returns a Future and restricts both the callable and policy\n *  to return Future<bool> or bool. SemiFuture forms are not valid.\n *  As the name suggests, prefer retrying to retryingUnsafe to avoid surprising\n *  recursion or putting work on the TimeKeeper that should not run there.\n\n *  Note that, consistent with other SemiFuture-returning functions retrying\n *  should be assumed to be lazy: it may do nothing until .wait()/.get() is\n *  called on the result or an executor is attached with .via.\n *\n *  We provide a few generic policies:\n *  - Basic\n *  - CappedJitteredexponentialBackoff\n *\n *  Custom policies may use the most recent try number and exception to decide\n *  whether to retry and optionally to do something interesting like delay\n *  before the retry. Users may pass inline lambda expressions as policies, or\n *  may define their own data types meeting the above requirements. Users are\n *  responsible for managing the lifetimes of anything pointed to or referred to\n *  from inside the policy.\n *\n *  For example, one custom policy may try up to k times, but only if the most\n *  recent exception is one of a few types or has one of a few error codes\n *  indicating that the failure was transitory.\n *\n *  Cancellation is not supported.\n *\n *  If both FF and Policy inline executes, then it is possible to hit a stack\n *  overflow due to the recursive nature of the retry implementation\n */\ntemplate <class Policy, class FF>\nFuture<typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>\nretryingUnsafe(Policy&& p, FF&& ff);\ntemplate <class Policy, class FF>\n[[nodiscard]] SemiFuture<\n    typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>\nretrying(Policy&& p, FF&& ff);\n\nnamespace detail {\n\nstruct retrying_policy_raw_tag {};\nstruct retrying_policy_fut_tag {};\n\ntemplate <class Policy>\nstruct retrying_policy_traits {\n  using result = invoke_result_t<Policy, size_t, const exception_wrapper&>;\n  using is_raw = std::is_same<result, bool>;\n  using is_fut = std::is_same<result, Future<bool>>;\n  using is_semi_fut = std::is_same<result, SemiFuture<bool>>;\n  using tag = typename std::conditional<\n      is_raw::value,\n      retrying_policy_raw_tag,\n      typename std::conditional<\n          is_fut::value || is_semi_fut::value,\n          retrying_policy_fut_tag,\n          void>::type>::type;\n};\n\ntemplate <class Policy, class FF, class Prom>\nvoid retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) {\n  using F = invoke_result_t<FF, size_t>;\n  using T = typename F::value_type;\n  auto f = makeFutureWith([&] { return ff(k++); });\n  std::move(f).thenTry(\n      [k,\n       prom = std::move(prom),\n       pm = static_cast<Policy&&>(p),\n       ffm = static_cast<FF&&>(ff)](Try<T>&& t) mutable {\n        if (t.hasValue()) {\n          prom.setValue(std::move(t).value());\n          return;\n        }\n        auto& x = t.exception();\n        auto q = makeFutureWith([&] { return pm(k, x); });\n        std::move(q).thenTry(\n            [k,\n             prom = std::move(prom),\n             xm = std::move(x),\n             pm = std::move(pm),\n             ffm = std::move(ffm)](Try<bool> shouldRetry) mutable {\n              if (shouldRetry.hasValue() && shouldRetry.value()) {\n                retryingImpl(k, std::move(pm), std::move(ffm), std::move(prom));\n              } else if (shouldRetry.hasValue()) {\n                prom.setException(std::move(xm));\n              } else {\n                prom.setException(std::move(shouldRetry.exception()));\n              }\n            });\n      });\n}\n\ntemplate <class Policy, class FF>\nFuture<typename invoke_result_t<FF, size_t>::value_type> retryingFuture(\n    size_t k, Policy&& p, FF&& ff) {\n  using F = invoke_result_t<FF, size_t>;\n  using T = typename F::value_type;\n  auto prom = Promise<T>();\n  auto f = prom.getFuture();\n  retryingImpl(\n      k, static_cast<Policy&&>(p), static_cast<FF&&>(ff), std::move(prom));\n  return f;\n}\n\ntemplate <class Policy, class FF>\nSemiFuture<typename invoke_result_t<FF, size_t>::value_type> retryingSemiFuture(\n    size_t k, Policy&& p, FF&& ff) {\n  auto sf = folly::makeSemiFuture().deferExValue(\n      [k, p = static_cast<Policy&&>(p), ff = static_cast<FF&&>(ff)](\n          Executor::KeepAlive<> ka, auto&&) mutable {\n        auto futureP = [p = static_cast<Policy&&>(p),\n                        ka](size_t kk, exception_wrapper e) mutable {\n          return p(kk, std::move(e)).via(ka);\n        };\n        auto futureFF =\n            [ff = static_cast<FF&&>(ff), ka = std::move(ka)](size_t v) mutable {\n              return ff(std::move(v)).via(ka);\n            };\n        return retryingFuture(k, std::move(futureP), std::move(futureFF));\n      });\n  return sf;\n}\n\ntemplate <class Policy, class FF>\nFuture<typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>\nretryingFuture(Policy&& p, FF&& ff, retrying_policy_raw_tag) {\n  auto q = [pm = static_cast<Policy&&>(p)](size_t k, exception_wrapper x) {\n    return makeFuture<bool>(pm(k, x));\n  };\n  return retryingFuture(0, std::move(q), static_cast<FF&&>(ff));\n}\n\ntemplate <class Policy, class FF>\nSemiFuture<typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>\nretryingSemiFuture(Policy&& p, FF&& ff, retrying_policy_raw_tag) {\n  auto q = [pm = static_cast<Policy&&>(p)](size_t k, exception_wrapper x) {\n    return makeSemiFuture<bool>(pm(k, x));\n  };\n  return retryingSemiFuture(0, std::move(q), static_cast<FF&&>(ff));\n}\n\ntemplate <class Policy, class FF>\nFuture<typename invoke_result_t<FF, size_t>::value_type> retryingFuture(\n    Policy&& p, FF&& ff, retrying_policy_fut_tag) {\n  return retryingFuture(0, static_cast<Policy&&>(p), static_cast<FF&&>(ff));\n}\n\ntemplate <class Policy, class FF>\nSemiFuture<typename invoke_result_t<FF, size_t>::value_type> retryingSemiFuture(\n    Policy&& p, FF&& ff, retrying_policy_fut_tag) {\n  return retryingSemiFuture(0, static_cast<Policy&&>(p), static_cast<FF&&>(ff));\n}\n\n//  jittered exponential backoff, clamped to [backoff_min, backoff_max]\ntemplate <class URNG>\nDuration retryingJitteredExponentialBackoffDur(\n    size_t n,\n    Duration backoff_min,\n    Duration backoff_max,\n    double jitter_param,\n    URNG& rng) {\n  using dist = std::normal_distribution<double>;\n\n  // short-circuit to avoid 0 * inf = nan when computing backoff_rep below\n  if (FOLLY_UNLIKELY(backoff_min == Duration(0))) {\n    return Duration(0);\n  }\n  auto jitter = jitter_param > 0 ? std::exp(dist{0., jitter_param}(rng)) : 1.;\n  auto backoff_rep =\n      jitter * static_cast<double>(backoff_min.count()) * std::pow(2, n - 1);\n  if (FOLLY_UNLIKELY(backoff_rep >= static_cast<double>(backoff_max.count()))) {\n    return std::max(backoff_min, backoff_max);\n  }\n  auto backoff = Duration(Duration::rep(backoff_rep));\n  return std::max(backoff_min, std::min(backoff_max, backoff));\n}\n\ntemplate <class Policy, class URNG>\nstd::function<Future<bool>(size_t, const exception_wrapper&)>\nretryingPolicyCappedJitteredExponentialBackoff(\n    size_t max_tries,\n    Duration backoff_min,\n    Duration backoff_max,\n    double jitter_param,\n    URNG&& rng,\n    Policy&& p) {\n  return\n      [pm = static_cast<Policy&&>(p),\n       max_tries,\n       backoff_min,\n       backoff_max,\n       jitter_param,\n       rngp = static_cast<URNG&&>(rng)](\n          size_t n, const exception_wrapper& ex) mutable {\n        if (n == max_tries) {\n          return makeFuture(false);\n        }\n        return pm(n, ex).thenValue(\n            [n, backoff_min, backoff_max, jitter_param, rngp = std::move(rngp)](\n                bool v) mutable {\n              if (!v) {\n                return makeFuture(false);\n              }\n              auto backoff = detail::retryingJitteredExponentialBackoffDur(\n                  n, backoff_min, backoff_max, jitter_param, rngp);\n              return futures::sleep(backoff).toUnsafeFuture().thenValue(\n                  [](auto&&) { return true; });\n            });\n      };\n}\n\ntemplate <class Policy, class URNG>\nstd::function<Future<bool>(size_t, const exception_wrapper&)>\nretryingPolicyCappedJitteredExponentialBackoff(\n    size_t max_tries,\n    Duration backoff_min,\n    Duration backoff_max,\n    double jitter_param,\n    URNG&& rng,\n    Policy&& p,\n    retrying_policy_raw_tag) {\n  auto q =\n      [pm = static_cast<Policy&&>(p)](size_t n, const exception_wrapper& e) {\n        return makeFuture(pm(n, e));\n      };\n  return retryingPolicyCappedJitteredExponentialBackoff(\n      max_tries,\n      backoff_min,\n      backoff_max,\n      jitter_param,\n      static_cast<URNG&&>(rng),\n      std::move(q));\n}\n\ntemplate <class Policy, class URNG>\nstd::function<Future<bool>(size_t, const exception_wrapper&)>\nretryingPolicyCappedJitteredExponentialBackoff(\n    size_t max_tries,\n    Duration backoff_min,\n    Duration backoff_max,\n    double jitter_param,\n    URNG&& rng,\n    Policy&& p,\n    retrying_policy_fut_tag) {\n  return retryingPolicyCappedJitteredExponentialBackoff(\n      max_tries,\n      backoff_min,\n      backoff_max,\n      jitter_param,\n      static_cast<URNG&&>(rng),\n      static_cast<Policy&&>(p));\n}\n\n} // namespace detail\n\ntemplate <class Policy, class FF>\nFuture<typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>\nretryingUnsafe(Policy&& p, FF&& ff) {\n  using tag = typename detail::retrying_policy_traits<Policy>::tag;\n  return detail::retryingFuture(\n      static_cast<Policy&&>(p), static_cast<FF&&>(ff), tag());\n}\n\ntemplate <class Policy, class FF>\nSemiFuture<typename isFutureOrSemiFuture<invoke_result_t<FF, size_t>>::Inner>\nretrying(Policy&& p, FF&& ff) {\n  using tag = typename detail::retrying_policy_traits<Policy>::tag;\n  return detail::retryingSemiFuture(\n      static_cast<Policy&&>(p), static_cast<FF&&>(ff), tag());\n}\n\ninline std::function<bool(size_t, const exception_wrapper&)>\nretryingPolicyBasic(size_t max_tries) {\n  return [=](size_t n, const exception_wrapper&) { return n < max_tries; };\n}\n\ntemplate <class Policy, class URNG>\nstd::function<Future<bool>(size_t, const exception_wrapper&)>\nretryingPolicyCappedJitteredExponentialBackoff(\n    size_t max_tries,\n    Duration backoff_min,\n    Duration backoff_max,\n    double jitter_param,\n    URNG&& rng,\n    Policy&& p) {\n  using tag = typename detail::retrying_policy_traits<Policy>::tag;\n  return detail::retryingPolicyCappedJitteredExponentialBackoff(\n      max_tries,\n      backoff_min,\n      backoff_max,\n      jitter_param,\n      static_cast<URNG&&>(rng),\n      static_cast<Policy&&>(p),\n      tag());\n}\n\ninline std::function<Future<bool>(size_t, const exception_wrapper&)>\nretryingPolicyCappedJitteredExponentialBackoff(\n    size_t max_tries,\n    Duration backoff_min,\n    Duration backoff_max,\n    double jitter_param) {\n  auto p = [](size_t, const exception_wrapper&) { return true; };\n  return retryingPolicyCappedJitteredExponentialBackoff(\n      max_tries,\n      backoff_min,\n      backoff_max,\n      jitter_param,\n      ThreadLocalPRNG(),\n      std::move(p));\n}\n\n} // namespace futures\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/SharedPromise-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly {\n\ntemplate <class T>\nsize_t SharedPromise<T>::size() const {\n  std::lock_guard g(mutex_);\n  return size_.value;\n}\n\ntemplate <class T>\nSemiFuture<T> SharedPromise<T>::getSemiFuture() const {\n  std::lock_guard g(mutex_);\n  size_.value++;\n  if (hasResult()) {\n    return makeFuture<T>(Try<T>(try_.value));\n  }\n  auto& promise = promises_.emplace_back();\n  if (interruptHandler_) {\n    promise.setInterruptHandler(interruptHandler_);\n  }\n  return promise.getSemiFuture();\n}\n\ntemplate <class T>\nFuture<T> SharedPromise<T>::getFuture() const {\n  return getSemiFuture().via(&InlineExecutor::instance());\n}\n\ntemplate <class T>\ntemplate <class E>\ntypename std::enable_if<std::is_base_of<std::exception, E>::value>::type\nSharedPromise<T>::setException(E const& e) {\n  setTry(Try<T>(e));\n}\n\ntemplate <class T>\nvoid SharedPromise<T>::setException(exception_wrapper ew) {\n  setTry(Try<T>(std::move(ew)));\n}\n\ntemplate <class T>\nvoid SharedPromise<T>::setInterruptHandler(\n    std::function<void(exception_wrapper const&)> fn) {\n  std::lock_guard g(mutex_);\n  if (hasResult()) {\n    return;\n  }\n  interruptHandler_ = fn;\n  for (auto& p : promises_) {\n    p.setInterruptHandler(fn);\n  }\n}\n\ntemplate <class T>\ntemplate <class M>\nvoid SharedPromise<T>::setValue(M&& v) {\n  setTry(Try<T>(static_cast<M&&>(v)));\n}\n\ntemplate <class T>\ntemplate <class F>\nvoid SharedPromise<T>::setWith(F&& func) {\n  setTry(makeTryWith(static_cast<F&&>(func)));\n}\n\ntemplate <class T>\nvoid SharedPromise<T>::setTry(Try<T>&& t) {\n  std::vector<Promise<T>> promises;\n\n  {\n    std::lock_guard g(mutex_);\n    if (hasResult()) {\n      throw_exception<PromiseAlreadySatisfied>();\n    }\n    try_.value = std::move(t);\n    promises.swap(promises_);\n  }\n\n  for (auto& p : promises) {\n    p.setTry(Try<T>(try_.value));\n  }\n}\n\ntemplate <class T>\nbool SharedPromise<T>::isFulfilled() const {\n  std::lock_guard g(mutex_);\n  return hasResult();\n}\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\n// limited to the instances unconditionally forced by the futures library\nextern template class SharedPromise<Unit>;\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/SharedPromise.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/SharedPromise.h>\n\nnamespace folly {\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\ntemplate class SharedPromise<Unit>;\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/SharedPromise.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/futures/Promise.h>\n#include <folly/lang/Exception.h>\n\nnamespace folly {\n\n/*\n * SharedPromise provides the same interface as Promise, but you can extract\n * multiple Futures from it, i.e. you can call getFuture() as many times as\n * you'd like. When the SharedPromise is fulfilled, all of the Futures are\n * completed. Calls to getFuture() after the SharedPromise is fulfilled return\n * a completed Future. If you find yourself constructing collections of Promises\n * and fulfilling them simultaneously with the same value, consider this\n * utility instead. Likewise, if you find yourself in need of setting multiple\n * callbacks on the same Future (which is indefinitely unsupported), consider\n * refactoring to use SharedPromise to \"split\" the Future.\n *\n * The SharedPromise must be kept alive manually. Consider FutureSplitter for\n * automatic lifetime management.\n */\ntemplate <class T>\nclass SharedPromise {\n public:\n  /**\n   * Return a Future tied to the shared core state. Unlike Promise::getFuture,\n   * this can be called an unlimited number of times per SharedPromise.\n   */\n  SemiFuture<T> getSemiFuture() const;\n\n  /**\n   * Return a Future tied to the shared core state. Unlike Promise::getFuture,\n   * this can be called an unlimited number of times per SharedPromise.\n   * NOTE: This function is deprecated. Please use getSemiFuture and pass the\n   *       appropriate executor to .via on the returned SemiFuture to get a\n   *       valid Future where necessary.\n   */\n  Future<T> getFuture() const;\n\n  /** Return the number of Futures associated with this SharedPromise */\n  size_t size() const;\n\n  /** Fulfill the SharedPromise with an exception_wrapper */\n  void setException(exception_wrapper ew);\n\n  /** Fulfill the SharedPromise with an exception type E, which can be passed to\n    make_exception_wrapper(). Useful for originating exceptions. If you\n    caught an exception the exception_wrapper form is more appropriate.\n    */\n  template <class E>\n  typename std::enable_if<std::is_base_of<std::exception, E>::value>::type\n  setException(E const&);\n\n  /// Set an interrupt handler to handle interrupts. See the documentation for\n  /// Future::raise(). Your handler can do whatever it wants, but if you\n  /// bother to set one then you probably will want to fulfill the SharedPromise\n  /// with an exception (or special value) indicating how the interrupt was\n  /// handled.\n  void setInterruptHandler(std::function<void(exception_wrapper const&)>);\n\n  /// Sugar to fulfill this SharedPromise<Unit>\n  template <class B = T>\n  typename std::enable_if<std::is_same<Unit, B>::value, void>::type setValue() {\n    setTry(Try<T>(T()));\n  }\n\n  /** Set the value (use perfect forwarding for both move and copy) */\n  template <class M>\n  void setValue(M&& value);\n\n  void setTry(Try<T>&& t);\n\n  /** Fulfill this SharedPromise with the result of a function that takes no\n    arguments and returns something implicitly convertible to T.\n    Captures exceptions. e.g.\n\n    p.setWith([] { do something that may throw; return a T; });\n  */\n  template <class F>\n  void setWith(F&& func);\n\n  bool isFulfilled() const;\n\n private:\n  // this allows SharedPromise move-ctor/move-assign to be defaulted\n  struct Mutex : std::mutex {\n    Mutex() = default;\n    Mutex(Mutex&&) noexcept {}\n    Mutex& operator=(Mutex&&) noexcept { return *this; }\n  };\n\n  template <typename V>\n  struct Defaulted {\n    using Noexcept = StrictConjunction<\n        std::is_nothrow_default_constructible<V>,\n        std::is_nothrow_move_constructible<V>,\n        std::is_nothrow_move_assignable<V>>;\n    V value{V()};\n    Defaulted() = default;\n    Defaulted(Defaulted&& that) noexcept(Noexcept::value)\n        : value(std::exchange(that.value, V())) {}\n    Defaulted& operator=(Defaulted&& that) noexcept(Noexcept::value) {\n      value = std::exchange(that.value, V());\n      return *this;\n    }\n  };\n\n  bool hasResult() const {\n    return try_.value.hasValue() || try_.value.hasException();\n  }\n\n  mutable Mutex mutex_;\n  mutable Defaulted<size_t> size_;\n  Defaulted<Try<T>> try_;\n  mutable std::vector<Promise<T>> promises_;\n  std::function<void(exception_wrapper const&)> interruptHandler_;\n};\n\n} // namespace folly\n\n#include <folly/futures/Future.h>\n#include <folly/futures/SharedPromise-inl.h>\n"
  },
  {
    "path": "folly/futures/ThreadWheelTimekeeper.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/ThreadWheelTimekeeper.h>\n\n#include <future>\n\n#include <folly/Chrono.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/WTCallback.h>\n\nnamespace folly {\n\nThreadWheelTimekeeper::ThreadWheelTimekeeper()\n    : EventBaseThreadTimekeeper(eventBase_),\n      thread_([this] { eventBase_.loopForever(); }) {\n  eventBase_.waitUntilRunning();\n  eventBase_.runInEventBaseThread([this] {\n    // 15 characters max\n    eventBase_.setName(\"FutureTimekeepr\");\n  });\n}\n\nThreadWheelTimekeeper::~ThreadWheelTimekeeper() {\n  eventBase_.runInEventBaseThreadAndWait([this] {\n    eventBase_.timer().cancelAll();\n    eventBase_.terminateLoopSoon();\n  });\n  thread_.join();\n}\n\nSemiFuture<Unit> EventBaseThreadTimekeeper::after(HighResDuration dur) {\n  auto [cob, sf] = WTCallback<HHWheelTimer>::create(&eventBaseRef_);\n\n  // Even shared_ptr of cob is captured in lambda this is still somewhat *racy*\n  // because it will be released once timeout is scheduled. So technically there\n  // is no gurantee that EventBase thread can safely call timeout callback.\n  // However due to fact that we are having circular reference here:\n  // WTCallback->Promise->Core->WTCallbak, so three of them won't go away until\n  // we break the circular reference. The break happens either in\n  // WTCallback::timeoutExpired or WTCallback::interruptHandler. Former means\n  // timeout callback is being safely executed. Latter captures shared_ptr of\n  // WTCallback again in another lambda for canceling timeout. The moment\n  // canceling timeout is executed in EventBase thread, the actual timeout\n  // callback has either been executed, or will never be executed. So we are\n  // fine here.\n  eventBaseRef_.runInEventBaseThread([this, cob2 = std::move(cob), dur] {\n    eventBaseRef_.timer().scheduleTimeout(\n        cob2.get(), folly::chrono::ceil<Duration>(dur));\n  });\n  return std::move(sf);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/ThreadWheelTimekeeper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <thread>\n\n#include <folly/futures/Future.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/HHWheelTimer.h>\n\nnamespace folly {\n\nclass EventBaseThreadTimekeeper : public Timekeeper {\n public:\n  EventBaseThreadTimekeeper() = delete;\n  explicit EventBaseThreadTimekeeper(folly::EventBase& eventBase)\n      : eventBaseRef_(eventBase) {}\n  ~EventBaseThreadTimekeeper() override = default;\n\n  /// Implement the Timekeeper interface\n  SemiFuture<Unit> after(HighResDuration) override;\n\n protected:\n  folly::EventBase& eventBaseRef_;\n};\n\n/// The default Timekeeper implementation which uses a HHWheelTimer on an\n/// EventBase in a dedicated thread. Users needn't deal with this directly, it\n/// is used by default by Future methods that work with timeouts.\nclass ThreadWheelTimekeeper : public EventBaseThreadTimekeeper {\n public:\n  /// But it doesn't *have* to be a singleton.\n  ThreadWheelTimekeeper();\n  ~ThreadWheelTimekeeper() override;\n\n protected:\n  folly::EventBase eventBase_{folly::EventBase::Options().setTimerTickInterval(\n      std::chrono::milliseconds(1))};\n  std::thread thread_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/WTCallback.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <optional>\n\n#include <folly/Chrono.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/HHWheelTimer.h>\n\nnamespace folly {\n\n// Callback object for HHWheelTimer\ntemplate <class TBase>\nstruct WTCallback : public TBase::Callback {\n  struct PrivateConstructorTag {};\n\n public:\n  explicit WTCallback(PrivateConstructorTag) {}\n\n  // Only allow creation by this factory, to ensure heap allocation.\n  static std::pair<std::shared_ptr<WTCallback>, SemiFuture<Unit>> create(\n      EventBase* base) {\n    // optimization opportunity: memory pool\n    auto cob = std::make_shared<WTCallback>(PrivateConstructorTag{});\n    auto& state = cob->state_.unsafeGetUnlocked().emplace(State{base, {}});\n    // Capture shared_ptr of cob in lambda so that Core inside Promise will\n    // hold a ref count to it. The ref count will be released when Core goes\n    // away which happens when both Promise and Future go away\n    state.promise.setInterruptHandler([cob](exception_wrapper ew) mutable {\n      interruptHandler(std::move(cob), std::move(ew));\n    });\n    return {std::move(cob), state.promise.getSemiFuture()};\n  }\n\n protected:\n  struct State {\n    EventBase* base;\n    Promise<Unit> promise;\n  };\n\n  // First thread that can fulfill the promise unsets the state, breaking the\n  // circular reference WTCallback -> promise -> core -> WTCallback.\n  folly::Synchronized<std::optional<State>> state_;\n\n  void timeoutExpired() noexcept override {\n    if (auto state = state_.exchange({})) {\n      state->promise.setValue();\n    }\n  }\n\n  void callbackCanceled() noexcept override {\n    if (auto state = state_.exchange({})) {\n      state->promise.setException(FutureNoTimekeeper{});\n    }\n  }\n\n  static void interruptHandler(\n      std::shared_ptr<WTCallback> self, exception_wrapper ew) {\n    // Hold the lock while scheduling the callback, so that callbackCanceled()\n    // blocks the timekeeper destructor keeping the base pointer valid.\n    auto wState = self->state_.wlock();\n    if (!*wState) {\n      return;\n    }\n\n    auto state = std::exchange(*wState, {});\n    auto* base_2 = state->base;\n    base_2->runInEventBaseThreadAlwaysEnqueue(\n        [self, state_2 = std::move(state), ew_2 = std::move(ew)]() mutable {\n          self->cancelTimeout();\n          state_2->promise.setException(std::move(ew_2));\n        });\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/detail/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"core\",\n    srcs = [\n        \"Core.cpp\",\n    ],\n    headers = [\n        \"Core.h\",\n        \"Types.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:executor\",\n        \"//folly:function\",\n        \"//folly:optional\",\n        \"//folly:scope_guard\",\n        \"//folly:try\",\n        \"//folly:utility\",\n        \"//folly/io/async:request_context\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:switch\",\n        \"//folly/synchronization:atomic_util\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"types\",\n    headers = [\"Types.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n"
  },
  {
    "path": "folly/futures/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME core\n  SRCS\n    Core.cpp\n  HEADERS\n    Core.h\n    Types.h\n  EXPORTED_DEPS\n    folly_executor\n    folly_function\n    folly_io_async_request_context\n    folly_lang_assume\n    folly_lang_exception\n    folly_lang_switch\n    folly_optional\n    folly_scope_guard\n    folly_synchronization_atomic_util\n    folly_try\n    folly_utility\n)\n\nfolly_add_library(\n  NAME types\n  HEADERS\n    Types.h\n)\n"
  },
  {
    "path": "folly/futures/detail/Core.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/detail/Core.h>\n\n#include <new>\n\n#include <fmt/core.h>\n#include <folly/Utility.h>\n#include <folly/lang/Assume.h>\n\nnamespace folly {\nnamespace futures {\nnamespace detail {\n\nnamespace {\n\ntemplate <class Enum>\nvoid terminate_unexpected_state(fmt::string_view context, Enum state) {\n  terminate_with<std::logic_error>(\n      fmt::format(\"{} unexpected state: {}\", context, to_underlying(state)));\n}\n\n} // namespace\n\nvoid UniqueDeleter::operator()(DeferredExecutor* ptr) {\n  if (ptr) {\n    ptr->release();\n  }\n}\n\nKeepAliveOrDeferred::KeepAliveOrDeferred(KeepAliveOrDeferred&& other) noexcept\n    : state_(other.state_) {\n  switch (state_) {\n    case State::Deferred:\n      ::new (&deferred_) DW{std::move(other.deferred_)};\n      break;\n    case State::KeepAlive:\n      ::new (&keepAlive_) KA{std::move(other.keepAlive_)};\n      break;\n  }\n}\n\nKeepAliveOrDeferred::~KeepAliveOrDeferred() {\n  switch (state_) {\n    case State::Deferred:\n      deferred_.~DW();\n      break;\n    case State::KeepAlive:\n      keepAlive_.~KA();\n      break;\n  }\n}\n\nKeepAliveOrDeferred& KeepAliveOrDeferred::operator=(\n    KeepAliveOrDeferred&& other) noexcept {\n  // This is safe to do because KeepAliveOrDeferred is nothrow\n  // move-constructible.\n  this->~KeepAliveOrDeferred();\n  ::new (this) KeepAliveOrDeferred{std::move(other)};\n  return *this;\n}\n\nDeferredExecutor* KeepAliveOrDeferred::getDeferredExecutor() const noexcept {\n  switch (state_) {\n    case State::Deferred:\n      return deferred_.get();\n    case State::KeepAlive:\n      return nullptr;\n  }\n  assume_unreachable();\n}\n\nExecutor* KeepAliveOrDeferred::getKeepAliveExecutor() const noexcept {\n  switch (state_) {\n    case State::Deferred:\n      return nullptr;\n    case State::KeepAlive:\n      return keepAlive_.get();\n  }\n  assume_unreachable();\n}\n\nKeepAliveOrDeferred::KA KeepAliveOrDeferred::stealKeepAlive() && noexcept {\n  switch (state_) {\n    case State::Deferred:\n      return KA{};\n    case State::KeepAlive:\n      return std::move(keepAlive_);\n  }\n  assume_unreachable();\n}\n\nKeepAliveOrDeferred::DW KeepAliveOrDeferred::stealDeferred() && noexcept {\n  switch (state_) {\n    case State::Deferred:\n      return std::move(deferred_);\n    case State::KeepAlive:\n      return DW{};\n  }\n  assume_unreachable();\n}\n\nKeepAliveOrDeferred KeepAliveOrDeferred::copy() const {\n  switch (state_) {\n    case State::Deferred:\n      if (auto def = getDeferredExecutor()) {\n        return KeepAliveOrDeferred{def->copy()};\n      } else {\n        return KeepAliveOrDeferred{};\n      }\n    case State::KeepAlive:\n      return KeepAliveOrDeferred{keepAlive_};\n  }\n  assume_unreachable();\n}\n\n/* explicit */ KeepAliveOrDeferred::operator bool() const noexcept {\n  return getDeferredExecutor() || getKeepAliveExecutor();\n}\n\nvoid DeferredExecutor::addFrom(\n    Executor::KeepAlive<>&& completingKA,\n    Executor::KeepAlive<>::KeepAliveFunc func) {\n  auto state = state_.load(std::memory_order_acquire);\n  if (state == State::DETACHED) {\n    return;\n  }\n\n  // If we are completing on the current executor, call inline, otherwise\n  // add\n  auto addWithInline =\n      [&](Executor::KeepAlive<>::KeepAliveFunc&& addFunc) mutable {\n        if (completingKA.get() == executor_.get()) {\n          addFunc(std::move(completingKA));\n        } else {\n          executor_.copy().add(std::move(addFunc));\n        }\n      };\n\n  if (state == State::HAS_EXECUTOR) {\n    addWithInline(std::move(func));\n    return;\n  }\n  if (state != State::EMPTY) {\n    terminate_unexpected_state(\"DeferredExecutor::addFrom\", state);\n  }\n  func_ = std::move(func);\n  if (folly::atomic_compare_exchange_strong_explicit(\n          &state_,\n          &state,\n          State::HAS_FUNCTION,\n          std::memory_order_release,\n          std::memory_order_acquire)) {\n    return;\n  }\n\n  if (state == State::DETACHED) {\n    std::exchange(func_, nullptr);\n  } else if (state == State::HAS_EXECUTOR) {\n    addWithInline(std::exchange(func_, nullptr));\n  } else {\n    terminate_unexpected_state(\"DeferredExecutor::addFrom\", state);\n  }\n}\n\nExecutor* DeferredExecutor::getExecutor() const {\n  assert(executor_.get());\n  return executor_.get();\n}\n\nvoid DeferredExecutor::setExecutor(\n    folly::Executor::KeepAlive<> executor, bool inlineUnsafe) {\n  if (nestedExecutors_) {\n    auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);\n    for (auto& nestedExecutor : *nestedExecutors) {\n      assert(nestedExecutor.get());\n      nestedExecutor.get()->setExecutor(executor.copy());\n    }\n  }\n  executor_ = std::move(executor);\n  auto state = state_.load(std::memory_order_acquire);\n  if (state == State::EMPTY &&\n      folly::atomic_compare_exchange_strong_explicit(\n          &state_,\n          &state,\n          State::HAS_EXECUTOR,\n          std::memory_order_release,\n          std::memory_order_acquire)) {\n    return;\n  }\n\n  if (state != State::HAS_FUNCTION ||\n      !folly::atomic_compare_exchange_strong_explicit(\n          &state_,\n          &state,\n          State::HAS_EXECUTOR,\n          std::memory_order_release,\n          std::memory_order_relaxed)) {\n    terminate_unexpected_state(\"DeferredExecutor::setExecutor\", state);\n  }\n  if (inlineUnsafe) {\n    std::exchange(func_, nullptr)(executor_.copy());\n  } else {\n    executor_.copy().add(std::exchange(func_, nullptr));\n  }\n}\n\nvoid DeferredExecutor::setNestedExecutors(\n    std::vector<DeferredWrapper> executors) {\n  DCHECK(!nestedExecutors_);\n  nestedExecutors_ =\n      std::make_unique<std::vector<DeferredWrapper>>(std::move(executors));\n}\n\nvoid DeferredExecutor::detach() {\n  if (nestedExecutors_) {\n    auto nestedExecutors = std::exchange(nestedExecutors_, nullptr);\n    for (auto& nestedExecutor : *nestedExecutors) {\n      assert(nestedExecutor.get());\n      nestedExecutor.get()->detach();\n    }\n  }\n  auto state = state_.load(std::memory_order_acquire);\n  if (state == State::EMPTY &&\n      folly::atomic_compare_exchange_strong_explicit(\n          &state_,\n          &state,\n          State::DETACHED,\n          std::memory_order_release,\n          std::memory_order_acquire)) {\n    return;\n  }\n\n  if (state != State::HAS_FUNCTION ||\n      !folly::atomic_compare_exchange_strong_explicit(\n          &state_,\n          &state,\n          State::DETACHED,\n          std::memory_order_release,\n          std::memory_order_relaxed)) {\n    terminate_unexpected_state(\"DeferredExecutor::detach\", state);\n  }\n  std::exchange(func_, nullptr);\n}\n\nDeferredWrapper DeferredExecutor::copy() {\n  acquire();\n  return DeferredWrapper(this);\n}\n\n/* static */ DeferredWrapper DeferredExecutor::create() {\n  return DeferredWrapper(new DeferredExecutor{});\n}\n\nDeferredExecutor::DeferredExecutor() {}\n\nvoid DeferredExecutor::acquire() {\n  auto keepAliveCount = keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n  DCHECK_GT(keepAliveCount, 0);\n}\n\nvoid DeferredExecutor::release() {\n  auto keepAliveCount = keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);\n  DCHECK_GT(keepAliveCount, 0);\n  if (keepAliveCount == 1) {\n    delete this;\n  }\n}\n\nInterruptHandler::~InterruptHandler() = default;\n\nvoid InterruptHandler::acquire() {\n  auto refCount = refCount_.fetch_add(1, std::memory_order_relaxed);\n  DCHECK_GT(refCount, 0);\n}\n\nvoid InterruptHandler::release() {\n  auto refCount = refCount_.fetch_sub(1, std::memory_order_acq_rel);\n  DCHECK_GT(refCount, 0);\n  if (refCount == 1) {\n    delete this;\n  }\n}\n\nbool CoreBase::hasResult() const noexcept {\n  constexpr auto allowed = State::OnlyResult | State::Done;\n  auto core = this;\n  auto state = core->state_.load(std::memory_order_acquire);\n  while (state == State::Proxy) {\n    core = core->proxy_;\n    state = core->state_.load(std::memory_order_acquire);\n  }\n  return State() != (state & allowed);\n}\n\nDeferredWrapper CoreBase::stealDeferredExecutor() {\n  if (executor_.isKeepAlive()) {\n    return {};\n  }\n\n  return std::move(executor_).stealDeferred();\n}\n\nvoid CoreBase::raise(exception_wrapper e) {\n  if (hasResult()) {\n    return;\n  }\n  auto interrupt = interrupt_.load(std::memory_order_acquire);\n  switch (interrupt & InterruptMask) {\n    case InterruptInitial: { // store the object\n      assert(!interrupt);\n      auto object = new exception_wrapper(std::move(e));\n      auto exchanged = folly::atomic_compare_exchange_strong_explicit(\n          &interrupt_,\n          &interrupt,\n          reinterpret_cast<uintptr_t>(object) | InterruptHasObject,\n          std::memory_order_release,\n          std::memory_order_acquire);\n      if (exchanged) {\n        return;\n      }\n      // lost the race!\n      e = std::move(*object);\n      delete object;\n      if (interrupt & InterruptHasObject) { // ignore all calls after the first\n        return;\n      }\n      assert(interrupt & InterruptHasHandler);\n      [[fallthrough]];\n    }\n    case InterruptHasHandler: { // invoke the stored handler\n      auto pointer = interrupt & ~InterruptMask;\n      auto exchanged = interrupt_.compare_exchange_strong(\n          interrupt, pointer | InterruptTerminal, std::memory_order_relaxed);\n      if (!exchanged) { // ignore all calls after the first\n        return;\n      }\n      auto handler = reinterpret_cast<InterruptHandler*>(pointer);\n      handler->handle(e);\n      return;\n    }\n    case InterruptHasObject: // ignore all calls after the first\n      return;\n    case InterruptTerminal: // ignore all calls after the first\n      return;\n  }\n}\n\nvoid CoreBase::initCopyInterruptHandlerFrom(const CoreBase& other) {\n  assert(!interrupt_.load(std::memory_order_relaxed));\n  auto interrupt = other.interrupt_.load(std::memory_order_acquire);\n  switch (interrupt & InterruptMask) {\n    case InterruptHasHandler: { // copy the handler\n      auto pointer = interrupt & ~InterruptMask;\n      auto handler = reinterpret_cast<InterruptHandler*>(pointer);\n      handler->acquire();\n      interrupt_.store(\n          pointer | InterruptHasHandler, std::memory_order_release);\n      break;\n    }\n    case InterruptTerminal: { // copy the handler, if any\n      auto pointer = interrupt & ~InterruptMask;\n      auto handler = reinterpret_cast<InterruptHandler*>(pointer);\n      if (handler) {\n        handler->acquire();\n        interrupt_.store(\n            pointer | InterruptHasHandler, std::memory_order_release);\n      }\n      break;\n    }\n  }\n}\n\nclass CoreBase::CoreAndCallbackReference {\n public:\n  explicit CoreAndCallbackReference(CoreBase* core) noexcept : core_(core) {}\n\n  ~CoreAndCallbackReference() noexcept { detach(); }\n\n  CoreAndCallbackReference(CoreAndCallbackReference const& o) = delete;\n  CoreAndCallbackReference& operator=(CoreAndCallbackReference const& o) =\n      delete;\n  CoreAndCallbackReference& operator=(CoreAndCallbackReference&&) = delete;\n\n  CoreAndCallbackReference(CoreAndCallbackReference&& o) noexcept\n      : core_(std::exchange(o.core_, nullptr)) {}\n\n  CoreBase* getCore() const noexcept { return core_; }\n\n private:\n  void detach() noexcept {\n    if (core_) {\n      core_->derefCallback();\n      core_->detachOne();\n    }\n  }\n\n  CoreBase* core_{nullptr};\n};\n\nCoreBase::~CoreBase() {\n  auto interrupt = interrupt_.load(std::memory_order_acquire);\n  auto pointer = interrupt & ~InterruptMask;\n  switch (interrupt & InterruptMask) {\n    case InterruptHasHandler: {\n      auto handler = reinterpret_cast<InterruptHandler*>(pointer);\n      handler->release();\n      break;\n    }\n    case InterruptHasObject: {\n      auto object = reinterpret_cast<exception_wrapper*>(pointer);\n      delete object;\n      break;\n    }\n    case InterruptTerminal: {\n      auto handler = reinterpret_cast<InterruptHandler*>(pointer);\n      if (handler) {\n        handler->release();\n      }\n      break;\n    }\n  }\n}\n\nvoid CoreBase::setCallback_(\n    Callback&& callback,\n    std::shared_ptr<folly::RequestContext>&& context,\n    futures::detail::InlineContinuation allowInline) {\n  DCHECK(!hasCallback());\n\n  callback_ = std::move(callback);\n  context_ = std::move(context);\n\n  auto state = state_.load(std::memory_order_acquire);\n  State nextState = allowInline == futures::detail::InlineContinuation::permit\n      ? State::OnlyCallbackAllowInline\n      : State::OnlyCallback;\n\n  if (state == State::Start) {\n    if (folly::atomic_compare_exchange_strong_explicit(\n            &state_,\n            &state,\n            nextState,\n            std::memory_order_release,\n            std::memory_order_acquire)) {\n      return;\n    }\n  }\n\n  if (state == State::OnlyResult) {\n    if (!folly::atomic_compare_exchange_strong_explicit(\n            &state_,\n            &state,\n            State::Done,\n            std::memory_order_relaxed,\n            std::memory_order_relaxed)) {\n      terminate_unexpected_state(\"setCallback\", state);\n    }\n    doCallback(Executor::KeepAlive<>{}, state);\n  } else if (state == State::Proxy) {\n    if (!folly::atomic_compare_exchange_strong_explicit(\n            &state_,\n            &state,\n            State::Empty,\n            std::memory_order_relaxed,\n            std::memory_order_relaxed)) {\n      terminate_unexpected_state(\"setCallback\", state);\n    }\n    return proxyCallback(state);\n  } else {\n    terminate_unexpected_state(\"setCallback\", state);\n  }\n}\n\nvoid CoreBase::setResult_(Executor::KeepAlive<>&& completingKA) {\n  auto state = state_.load(std::memory_order_acquire);\n  switch (state) {\n    case State::Start:\n      if (folly::atomic_compare_exchange_strong_explicit(\n              &state_,\n              &state,\n              State::OnlyResult,\n              std::memory_order_release,\n              std::memory_order_acquire)) {\n        return;\n      }\n      [[fallthrough]];\n\n    case State::OnlyCallback:\n    case State::OnlyCallbackAllowInline:\n      if ((state != State::OnlyCallback &&\n           state != State::OnlyCallbackAllowInline) ||\n          !folly::atomic_compare_exchange_strong_explicit(\n              &state_,\n              &state,\n              State::Done,\n              std::memory_order_relaxed,\n              std::memory_order_relaxed)) {\n        terminate_unexpected_state(\"setResult\", state);\n      }\n      doCallback(std::move(completingKA), state);\n      return;\n    case State::OnlyResult:\n    case State::Proxy:\n    case State::Done:\n    case State::Empty:\n    default:\n      terminate_unexpected_state(\"setResult\", state);\n  }\n}\n\nvoid CoreBase::setProxy_(CoreBase* proxy) {\n  proxy_ = proxy;\n\n  auto state = state_.load(std::memory_order_acquire);\n  switch (state) {\n    case State::Start:\n      if (folly::atomic_compare_exchange_strong_explicit(\n              &state_,\n              &state,\n              State::Proxy,\n              std::memory_order_release,\n              std::memory_order_acquire)) {\n        break;\n      }\n      [[fallthrough]];\n\n    case State::OnlyCallback:\n    case State::OnlyCallbackAllowInline:\n      if ((state != State::OnlyCallback &&\n           state != State::OnlyCallbackAllowInline) ||\n          !folly::atomic_compare_exchange_strong_explicit(\n              &state_,\n              &state,\n              State::Empty,\n              std::memory_order_relaxed,\n              std::memory_order_relaxed)) {\n        terminate_unexpected_state(\"setCallback\", state);\n      }\n      proxyCallback(state);\n      break;\n    case State::OnlyResult:\n    case State::Proxy:\n    case State::Done:\n    case State::Empty:\n    default:\n      terminate_unexpected_state(\"setCallback\", state);\n  }\n\n  detachOne();\n}\n\n// May be called at most once.\nvoid CoreBase::doCallback(\n    Executor::KeepAlive<>&& completingKA, State priorState) {\n  DCHECK(state_ == State::Done);\n\n  auto executor = std::exchange(executor_, KeepAliveOrDeferred{});\n\n  // Customise inline behaviour\n  // If addCompletingKA is non-null, then we are allowing inline execution\n  auto doAdd =\n      [](Executor::KeepAlive<>&& addCompletingKA,\n         KeepAliveOrDeferred&& currentExecutor,\n         auto&& keepAliveFunc) mutable {\n        if (auto deferredExecutorPtr = currentExecutor.getDeferredExecutor()) {\n          deferredExecutorPtr->addFrom(\n              std::move(addCompletingKA), std::move(keepAliveFunc));\n        } else {\n          // If executors match call inline\n          auto currentKeepAlive = std::move(currentExecutor).stealKeepAlive();\n          if (addCompletingKA.get() == currentKeepAlive.get()) {\n            keepAliveFunc(std::move(currentKeepAlive));\n          } else {\n            std::move(currentKeepAlive).add(std::move(keepAliveFunc));\n          }\n        }\n      };\n\n  if (executor) {\n    // If we are not allowing inline, clear the completing KA to disallow\n    if (!(priorState == State::OnlyCallbackAllowInline)) {\n      completingKA = Executor::KeepAlive<>{};\n    }\n    exception_wrapper ew;\n    // We need to reset `callback_` after it was executed (which can happen\n    // through the executor or, if `Executor::add` throws, below). The\n    // executor might discard the function without executing it (now or\n    // later), in which case `callback_` also needs to be reset.\n    // The `Core` has to be kept alive throughout that time, too. Hence we\n    // increment `attached_` and `callbackReferences_` by two, and construct\n    // exactly two `CoreAndCallbackReference` objects, which call\n    // `derefCallback` and `detachOne` in their destructor. One will guard\n    // this scope, the other one will guard the lambda passed to the executor.\n    attached_.fetch_add(2, std::memory_order_relaxed);\n    callbackReferences_.fetch_add(2, std::memory_order_relaxed);\n    CoreAndCallbackReference guard_local_scope(this);\n    CoreAndCallbackReference guard_lambda(this);\n    try {\n      doAdd(\n          std::move(completingKA),\n          std::move(executor),\n          [core_ref =\n               std::move(guard_lambda)](Executor::KeepAlive<>&& ka) mutable {\n            auto cr = std::move(core_ref);\n            CoreBase* const core = cr.getCore();\n            RequestContextScopeGuard rctx(std::move(core->context_));\n            core->callback_(*core, std::move(ka), nullptr);\n          });\n    } catch (...) {\n      ew = exception_wrapper(current_exception());\n    }\n    if (ew) {\n      RequestContextScopeGuard rctx(std::move(context_));\n      callback_(*this, Executor::KeepAlive<>{}, &ew);\n    }\n  } else {\n    attached_.fetch_add(1, std::memory_order_relaxed);\n    SCOPE_EXIT {\n      context_ = {};\n      callback_ = {};\n      detachOne();\n    };\n    RequestContextScopeGuard rctx(std::move(context_));\n    callback_(*this, std::move(completingKA), nullptr);\n  }\n}\n\nvoid CoreBase::proxyCallback(State priorState) {\n  // If the state of the core being proxied had a callback that allows inline\n  // execution, maintain this information in the proxy\n  futures::detail::InlineContinuation allowInline =\n      (priorState == State::OnlyCallbackAllowInline\n           ? futures::detail::InlineContinuation::permit\n           : futures::detail::InlineContinuation::forbid);\n  proxy_->setExecutor(std::move(executor_));\n  proxy_->setCallback_(std::move(callback_), std::move(context_), allowInline);\n  proxy_->detachFuture();\n  context_ = {};\n  callback_ = {};\n}\n\nvoid CoreBase::detachOne() noexcept {\n  auto a = attached_.fetch_sub(1, std::memory_order_acq_rel);\n  assert(a >= 1);\n  if (a == 1) {\n    delete this;\n  }\n}\n\nvoid CoreBase::derefCallback() noexcept {\n  auto c = callbackReferences_.fetch_sub(1, std::memory_order_acq_rel);\n  assert(c >= 1);\n  if (c == 1) {\n    context_ = {};\n    callback_ = {};\n  }\n}\n\nbool CoreBase::destroyDerived() noexcept {\n  DCHECK(attached_ == 0);\n  auto state = state_.load(std::memory_order_relaxed);\n  switch (state) {\n    case State::OnlyResult:\n      [[fallthrough]];\n\n    case State::Done:\n      return true;\n\n    case State::Proxy:\n      proxy_->detachFuture();\n      [[fallthrough]];\n\n    case State::Empty:\n      return false;\n\n    case State::Start:\n    case State::OnlyCallback:\n    case State::OnlyCallbackAllowInline:\n    default:\n      terminate_with<std::logic_error>(\"~Core unexpected state\");\n  }\n}\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\ntemplate class Core<folly::Unit>;\n#endif\n\n} // namespace detail\n} // namespace futures\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/detail/Core.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <mutex>\n#include <stdexcept>\n#include <utility>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/Executor.h>\n#include <folly/Function.h>\n#include <folly/Optional.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Try.h>\n#include <folly/Utility.h>\n#include <folly/futures/detail/Types.h>\n#include <folly/io/async/Request.h>\n#include <folly/lang/Assume.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Switch.h>\n#include <folly/synchronization/AtomicUtil.h>\n\nnamespace folly {\nnamespace futures {\nnamespace detail {\n\n/// See `Core` for details\nenum class State : uint8_t {\n  Start = 1 << 0,\n  OnlyResult = 1 << 1,\n  OnlyCallback = 1 << 2,\n  OnlyCallbackAllowInline = 1 << 3,\n  Proxy = 1 << 4,\n  Done = 1 << 5,\n  Empty = 1 << 6,\n};\nconstexpr State operator&(State a, State b) {\n  return State(uint8_t(a) & uint8_t(b));\n}\nconstexpr State operator|(State a, State b) {\n  return State(uint8_t(a) | uint8_t(b));\n}\nconstexpr State operator^(State a, State b) {\n  return State(uint8_t(a) ^ uint8_t(b));\n}\nconstexpr State operator~(State a) {\n  return State(~uint8_t(a));\n}\n\nclass DeferredExecutor;\n\nclass UniqueDeleter {\n public:\n  void operator()(DeferredExecutor* ptr);\n};\n\nusing DeferredWrapper = std::unique_ptr<DeferredExecutor, UniqueDeleter>;\n\n/**\n * Wrapper type that represents either a KeepAlive or a DeferredExecutor.\n * Acts as if a type-safe tagged union of the two using knowledge that the two\n * can safely be distinguished.\n */\nclass KeepAliveOrDeferred {\n private:\n  using KA = Executor::KeepAlive<>;\n  using DW = DeferredWrapper;\n\n public:\n  KeepAliveOrDeferred() noexcept : state_(State::Deferred), deferred_(DW{}) {}\n\n  /* implicit */ KeepAliveOrDeferred(KA ka) noexcept\n      : state_(State::KeepAlive) {\n    ::new (&keepAlive_) KA{std::move(ka)};\n  }\n\n  /* implicit */ KeepAliveOrDeferred(DW deferred) noexcept\n      : state_(State::Deferred) {\n    ::new (&deferred_) DW{std::move(deferred)};\n  }\n\n  KeepAliveOrDeferred(KeepAliveOrDeferred&& other) noexcept;\n\n  ~KeepAliveOrDeferred();\n\n  KeepAliveOrDeferred& operator=(KeepAliveOrDeferred&& other) noexcept;\n\n  DeferredExecutor* getDeferredExecutor() const noexcept;\n\n  Executor* getKeepAliveExecutor() const noexcept;\n\n  KA stealKeepAlive() && noexcept;\n\n  DW stealDeferred() && noexcept;\n\n  bool isDeferred() const noexcept;\n\n  bool isKeepAlive() const noexcept;\n\n  KeepAliveOrDeferred copy() const;\n\n  explicit operator bool() const noexcept;\n\n private:\n  friend class DeferredExecutor;\n\n  enum class State { Deferred, KeepAlive } state_;\n  union {\n    DW deferred_;\n    KA keepAlive_;\n  };\n};\n\ninline bool KeepAliveOrDeferred::isDeferred() const noexcept {\n  return state_ == State::Deferred;\n}\n\ninline bool KeepAliveOrDeferred::isKeepAlive() const noexcept {\n  return state_ == State::KeepAlive;\n}\n\n/**\n * Defer work until executor is actively boosted.\n */\nclass DeferredExecutor final {\n public:\n  // addFrom will:\n  //  * run func inline if there is a stored executor and completingKA matches\n  //    the stored executor\n  //  * enqueue func into the stored executor if one exists\n  //  * store func until an executor is set otherwise\n  void addFrom(\n      Executor::KeepAlive<>&& completingKA,\n      Executor::KeepAlive<>::KeepAliveFunc func);\n\n  Executor* getExecutor() const;\n\n  void setExecutor(\n      folly::Executor::KeepAlive<> executor, bool inlineUnsafe = false);\n\n  void setNestedExecutors(std::vector<DeferredWrapper> executors);\n\n  void detach();\n\n  DeferredWrapper copy();\n\n  static DeferredWrapper create();\n\n private:\n  friend class UniqueDeleter;\n\n  DeferredExecutor();\n\n  void acquire();\n  void release();\n\n  enum class State { EMPTY, HAS_FUNCTION, HAS_EXECUTOR, DETACHED };\n\n  std::atomic<State> state_{State::EMPTY};\n  Executor::KeepAlive<>::KeepAliveFunc func_;\n  folly::Executor::KeepAlive<> executor_;\n  std::unique_ptr<std::vector<DeferredWrapper>> nestedExecutors_;\n  std::atomic<ssize_t> keepAliveCount_{1};\n};\n\nclass InterruptHandler {\n public:\n  virtual ~InterruptHandler();\n\n  virtual void handle(const folly::exception_wrapper& ew) = 0;\n\n  void acquire();\n  void release();\n\n private:\n  std::atomic<ssize_t> refCount_{1};\n};\n\ntemplate <class F>\nclass InterruptHandlerImpl final : public InterruptHandler {\n public:\n  template <typename R>\n  explicit InterruptHandlerImpl(R&& f) noexcept(\n      noexcept(F(static_cast<R&&>(f))))\n      : f_(static_cast<R&&>(f)) {}\n\n  void handle(const folly::exception_wrapper& ew) override { f_(ew); }\n\n private:\n  F f_;\n};\n\n/// The shared state object for Future and Promise.\n///\n/// Nomenclature:\n///\n/// - \"result\": a `Try` object which, when set, contains a `T` or exception.\n/// - \"move-out the result\": used to mean the `Try` object and/or its contents\n///   are moved-out by a move-constructor or move-assignment. After the result\n///   is set, Core itself never modifies (including moving out) the result;\n///   however the docs refer to both since caller-code can move-out the result\n///   implicitly (see below for examples) whereas other types of modifications\n///   are more explicit in the caller code.\n/// - \"callback\": a function provided by the future which Core may invoke. The\n///   thread in which the callback is invoked depends on the executor; if there\n///   is no executor or an inline executor the thread-choice depends on timing.\n/// - \"executor\": an object which may in the future invoke a provided function\n///   (some executors may, as a matter of policy, simply destroy provided\n///   functions without executing them).\n/// - \"consumer thread\": the thread which currently owns the Future and which\n///   may provide the callback and/or the interrupt.\n/// - \"producer thread\": the thread which owns the Future and which may provide\n///   the result and which may provide the interrupt handler.\n/// - \"interrupt\": if provided, an object managed by (if non-empty)\n///   `exception_wrapper`.\n/// - \"interrupt handler\": if provided, a function-object passed to\n///   `promise.setInterruptHandler()`. Core invokes the interrupt handler with\n///   the interrupt when both are provided (and, best effort, if there is not\n///   yet any result).\n///\n/// Core holds three sets of data, each of which is concurrency-controlled:\n///\n/// - The primary producer-to-consumer info-flow: this info includes the result,\n///   callback, executor, and a priority for running the callback. Management of\n///   and concurrency control for this info is by an FSM based on `enum class\n///   State`. All state transitions are atomic; other producer-to-consumer data\n///   is sometimes modified within those transitions; see below for details.\n/// - The consumer-to-producer interrupt-request flow: this info includes an\n///   interrupt-handler and an interrupt.\n/// - Lifetime control info: this includes two reference counts, both which are\n///   internally synchronized (atomic).\n///\n/// The FSM to manage the primary producer-to-consumer info-flow has these\n///   allowed (atomic) transitions:\n///\n///   +----------------------------------------------------------------+\n///   |                       ---> OnlyResult -----                    |\n///   |                     /                       \\                  |\n///   |                  (setResult())             (setCallback())     |\n///   |                   /                           \\                |\n///   |   Start --------->                              ------> Done   |\n///   |     \\             \\                           /                |\n///   |      \\           (setCallback())           (setResult())       |\n///   |       \\             \\                       /                  |\n///   |        \\              ---> OnlyCallback ---                    |\n///   |         \\           or OnlyCallbackAllowInline                 |\n///   |          \\                                  \\                  |\n///   |      (setProxy())                          (setProxy())        |\n///   |            \\                                  \\                |\n///   |             \\                                   ------> Empty  |\n///   |              \\                                /                |\n///   |               \\                            (setCallback())     |\n///   |                \\                            /                  |\n///   |                  --------> Proxy ----------                    |\n///   +----------------------------------------------------------------+\n///\n/// States and the corresponding producer-to-consumer data status & ownership:\n///\n/// - Start: has neither result nor callback. While in this state, the producer\n///   thread may set the result (`setResult()`) or the consumer thread may set\n///   the callback (`setCallback()`).\n/// - OnlyResult: producer thread has set the result and must never access it.\n///   The result is logically owned by, and possibly modified or moved-out by,\n///   the consumer thread. Callers of the future object can do arbitrary\n///   modifications, including moving-out, via continuations or via non-const\n///   and/or rvalue-qualified `future.result()`, `future.value()`, etc.\n///   Future/SemiFuture proper also move-out the result in some cases, e.g.,\n///   in `wait()`, `get()`, when passing through values or results from core to\n///   core, as `then-value` and `then-error`, etc.\n/// - OnlyCallback: consumer thread has set a callback/continuation. From this\n///   point forward only the producer thread can safely access that callback\n///   (see `setResult()` and `doCallback()` where the producer thread can both\n///   read and modify the callback).\n/// - OnlyCallbackAllowInline: as for OnlyCallback but the core is allowed to\n///   run the callback inline with the setResult call, and therefore in the\n///   execution context and on the executor that executed the callback on the\n///   previous core, rather than adding the callback to the current Core's\n///   executor. This will only happen if the executor on which the previous\n///   callback is executing, and on which it is calling setResult, is the same\n///   as the executor the current core would add the callback to.\n/// - Proxy: producer thread has set a proxy core which the callback should be\n///   proxied to.\n/// - Done: callback can be safely accessed only within `doCallback()`, which\n///   gets called on exactly one thread exactly once just after the transition\n///   to Done. The future object will have determined whether that callback\n///   has/will move-out the result, but either way the result remains logically\n///   owned exclusively by the consumer thread (the code of Future/SemiFuture,\n///   of the continuation, and/or of callers of `future.result()`, etc.).\n/// - Empty: the core successfully proxied the callback and is now empty.\n///\n/// Start state:\n///\n/// - Start: e.g., `Core<X>::make()`.\n/// - (See also `Core<X>::make(x)` which logically transitions Start =>\n///   OnlyResult within the underlying constructor.)\n///\n/// Terminal states:\n///\n/// - OnlyResult: a terminal state when a callback is never attached, and also\n///   sometimes when a callback is provided, e.g., sometimes when\n///   `future.wait()` and/or `future.get()` are used.\n/// - Done: a terminal state when `future.then()` is used, and sometimes also\n///   when `future.wait()` and/or `future.get()` are used.\n/// - Proxy: a terminal state if proxy core was set, but callback was never set.\n/// - Empty: a terminal state when proxying a callback was successful.\n///\n/// Notes and caveats:\n///\n/// - Unfortunately, there are things that users can do to break concurrency and\n///   we can't detect that. However users should be ok if they follow move\n///   semantics religiously wrt threading.\n/// - Futures and/or Promises can and usually will migrate between threads,\n///   though this usually happens within the API code. For example, an async\n///   operation will probably make a promise-future pair (see overloads of\n///   `makePromiseContract()`), then move the Promise into another thread that\n///   will eventually fulfill it.\n/// - Things get slightly more complicated with executors and via, but the\n///   principle is the same.\n/// - In general, as long as the user doesn't access a future or promise object\n///   from more than one thread at a time there won't be any problems.\n//\n/// Implementation is split between CoreBase and Core<T>. T-independent bits are\n/// in CoreBase in order to minimize the instantiation cost of Core<T>.\nclass CoreBase {\n protected:\n  using Context = std::shared_ptr<RequestContext>;\n  using Callback = folly::Function<void(\n      CoreBase&, Executor::KeepAlive<>&&, exception_wrapper* ew)>;\n\n public:\n  // not copyable\n  CoreBase(CoreBase const&) = delete;\n  CoreBase& operator=(CoreBase const&) = delete;\n\n  // not movable (see comment in the implementation of Future::then)\n  CoreBase(CoreBase&&) noexcept = delete;\n  CoreBase& operator=(CoreBase&&) = delete;\n\n  /// May call from any thread\n  bool hasCallback() const noexcept {\n    constexpr auto allowed = State::OnlyCallback |\n        State::OnlyCallbackAllowInline | State::Done | State::Empty;\n    auto const state = state_.load(std::memory_order_acquire);\n    return State() != (state & allowed);\n  }\n\n  /// May call from any thread\n  ///\n  /// True if state is OnlyResult or Done.\n  ///\n  /// Identical to `this->ready()`\n  bool hasResult() const noexcept;\n\n  /// May call from any thread\n  ///\n  /// True if state is OnlyResult or Done.\n  ///\n  /// Identical to `this->hasResult()`\n  bool ready() const noexcept { return hasResult(); }\n\n  /// Called by a destructing Future (in the consumer thread, by definition).\n  /// Calls `delete this` if there are no more references to `this`\n  /// (including if `detachPromise()` is called previously or concurrently).\n  void detachFuture() noexcept { detachOne(); }\n\n  /// Called by a destructing Promise (in the producer thread, by definition).\n  /// Calls `delete this` if there are no more references to `this`\n  /// (including if `detachFuture()` is called previously or concurrently).\n  void detachPromise() noexcept {\n    DCHECK(hasResult());\n    detachOne();\n  }\n\n  /// Call only from consumer thread, either before attaching a callback or\n  /// after the callback has already been invoked, but not concurrently with\n  /// anything which might trigger invocation of the callback.\n  void setExecutor(KeepAliveOrDeferred&& x) {\n    DCHECK(\n        state_ != State::OnlyCallback &&\n        state_ != State::OnlyCallbackAllowInline);\n    executor_ = std::move(x);\n  }\n\n  Executor* getExecutor() const;\n\n  DeferredExecutor* getDeferredExecutor() const;\n\n  DeferredWrapper stealDeferredExecutor();\n\n  /// Call only from consumer thread\n  ///\n  /// Eventual effect is to pass `e` to the Promise's interrupt handler, either\n  /// synchronously within this call or asynchronously within\n  /// `setInterruptHandler()`, depending on which happens first (a coin-toss if\n  /// the two calls are racing).\n  ///\n  /// Has no effect if it was called previously.\n  /// Has no effect if State is OnlyResult or Done.\n  void raise(exception_wrapper e);\n\n  /// Copy the interrupt handler from another core. This should be done only\n  /// when initializing a new core (interruptHandler_ must be nullptr).\n  void initCopyInterruptHandlerFrom(const CoreBase& other);\n\n  /// Call only from producer thread\n  ///\n  /// May invoke `fn()` (passing the interrupt) synchronously within this call\n  /// (if `raise()` preceded or perhaps if `raise()` is called concurrently).\n  ///\n  /// Has no effect if State is OnlyResult or Done.\n  ///\n  /// Note: `fn()` must not touch resources that are destroyed immediately after\n  ///   `setResult()` is called. Reason: it is possible for `fn()` to get called\n  ///   asynchronously (in the consumer thread) after the producer thread calls\n  ///   `setResult()`.\n  template <typename F>\n  void setInterruptHandler(F&& fn) {\n    using handler_type = InterruptHandlerImpl<std::decay_t<F>>;\n    if (hasResult()) {\n      return;\n    }\n    handler_type* handler = nullptr;\n    auto interrupt = interrupt_.load(std::memory_order_acquire);\n    FOLLY_EXHAUSTIVE_SWITCH({\n      switch (interrupt & InterruptMask) {\n        case InterruptInitial: { // store the handler\n          assert(!interrupt);\n          handler = new handler_type(static_cast<F&&>(fn));\n          auto exchanged = folly::atomic_compare_exchange_strong_explicit(\n              &interrupt_,\n              &interrupt,\n              reinterpret_cast<uintptr_t>(handler) | InterruptHasHandler,\n              std::memory_order_release,\n              std::memory_order_acquire);\n          if (exchanged) {\n            return;\n          }\n          // lost the race!\n          if (interrupt & InterruptHasHandler) {\n            terminate_with<std::logic_error>(\"set-interrupt-handler race\");\n          }\n          assert(interrupt & InterruptHasObject);\n          [[fallthrough]];\n        }\n        case InterruptHasObject: { // invoke over the stored object\n          auto exchanged = interrupt_.compare_exchange_strong(\n              interrupt, InterruptTerminal, std::memory_order_relaxed);\n          if (!exchanged) {\n            terminate_with<std::logic_error>(\"set-interrupt-handler race\");\n          }\n          auto pointer = interrupt & ~InterruptMask;\n          auto object = reinterpret_cast<exception_wrapper*>(pointer);\n          if (handler) {\n            handler->handle(*object);\n            delete handler;\n          } else {\n            // mimic constructing and invoking a handler: 1 copy; non-const\n            // invoke\n            auto fn_ = static_cast<F&&>(fn);\n            fn_(std::as_const(*object));\n          }\n          delete object;\n          return;\n        }\n        case InterruptHasHandler: // fail all calls after the first\n          terminate_with<std::logic_error>(\"set-interrupt-handler duplicate\");\n        case InterruptTerminal: // fail all calls after the first\n          terminate_with<std::logic_error>(\"set-interrupt-handler after done\");\n        default:\n          folly::assume_unreachable();\n      }\n    }); // FOLLY_EXHAUSTIVE_SWITCH\n  }\n\n protected:\n  CoreBase(State state, unsigned char attached) noexcept\n      : state_(state), attached_(attached) {}\n\n  virtual ~CoreBase();\n\n  // Helper class that stores a pointer to the `Core` object and calls\n  // `derefCallback` and `detachOne` in the destructor.\n  class CoreAndCallbackReference;\n\n  // interrupt_ is an atomic acyclic finite state machine with guarded state\n  // which takes the form of either a pointer to a copy of the object passed to\n  // raise or a pointer to a copy of the handler passed to setInterruptHandler\n  //\n  // the object and the handler values are both at least pointer-aligned so they\n  // leave the bottom 2 bits free on all supported platforms; these bits are\n  // stolen for the state machine\n  enum : uintptr_t {\n    InterruptMask = 0x3u,\n  };\n  enum InterruptState : uintptr_t {\n    InterruptInitial = 0x0u,\n    InterruptHasHandler = 0x1u,\n    InterruptHasObject = 0x2u,\n    InterruptTerminal = 0x3u,\n  };\n\n  void setCallback_(\n      Callback&& callback,\n      std::shared_ptr<folly::RequestContext>&& context,\n      futures::detail::InlineContinuation allowInline);\n\n  void setResult_(Executor::KeepAlive<>&& completingKA);\n  void setProxy_(CoreBase* proxy);\n  void doCallback(Executor::KeepAlive<>&& completingKA, State priorState);\n  void proxyCallback(State priorState);\n\n  void detachOne() noexcept;\n\n  void derefCallback() noexcept;\n\n  template <typename Self>\n  FOLLY_ERASE static Self& walkProxyChainImpl(Self& self) noexcept {\n    DCHECK(self.hasResult());\n    auto core = &self;\n    while (core->state_.load(std::memory_order_relaxed) == State::Proxy) {\n      core = core->proxy_;\n    }\n    return *core;\n  }\n  FOLLY_ERASE CoreBase& walkProxyChain() noexcept {\n    return walkProxyChainImpl(*this);\n  }\n  FOLLY_ERASE CoreBase const& walkProxyChain() const noexcept {\n    return walkProxyChainImpl(*this);\n  }\n\n  bool destroyDerived() noexcept;\n\n  Callback callback_;\n  std::atomic<State> state_;\n  std::atomic<unsigned char> attached_;\n  std::atomic<unsigned char> callbackReferences_{0};\n  KeepAliveOrDeferred executor_;\n  Context context_;\n  std::atomic<uintptr_t> interrupt_{}; // see InterruptMask, InterruptState\n  CoreBase* proxy_;\n};\n\ntemplate <typename T>\nclass ResultHolder {\n protected:\n  ResultHolder() {}\n  ~ResultHolder() {}\n  // Using a separate base class allows us to control the placement of result_,\n  // making sure that it's in the same cache line as the vtable pointer and the\n  // callback_ (assuming it's small enough).\n  union {\n    Try<T> result_;\n  };\n};\n\ntemplate <typename T>\nclass Core final : private ResultHolder<T>, public CoreBase {\n  static_assert(\n      !std::is_void<T>::value,\n      \"void futures are not supported. Use Unit instead.\");\n\n public:\n  using Result = Try<T>;\n\n  /// State will be Start\n  static Core* make() { return new Core(); }\n\n  /// State will be OnlyResult\n  /// Result held will be move-constructed from `t`\n  static Core* make(Try<T>&& t) { return new Core(std::move(t)); }\n\n  /// State will be OnlyResult\n  /// Result held will be the `T` constructed from forwarded `args`\n  template <typename... Args>\n  static Core<T>* make(std::in_place_t, Args&&... args) {\n    return new Core<T>(std::in_place, static_cast<Args&&>(args)...);\n  }\n\n  /// Call only from consumer thread (since the consumer thread can modify the\n  ///   referenced Try object; see non-const overloads of `future.result()`,\n  ///   etc., and certain Future-provided callbacks which move-out the result).\n  ///\n  /// Unconditionally returns a reference to the result.\n  ///\n  /// State dependent preconditions:\n  ///\n  /// - Start, OnlyCallback or OnlyCallbackAllowInline: Never safe - do not\n  /// call. (Access in those states\n  ///   would be undefined behavior since the producer thread can, in those\n  ///   states, asynchronously set the referenced Try object.)\n  /// - OnlyResult: Always safe. (Though the consumer thread should not use the\n  ///   returned reference after it attaches a callback unless it knows that\n  ///   the callback does not move-out the referenced result.)\n  /// - Done: Safe but sometimes unusable. (Always returns a valid reference,\n  ///   but the referenced result may or may not have been modified, including\n  ///   possibly moved-out, depending on what the callback did; some but not\n  ///   all callbacks modify (possibly move-out) the result.)\n  Try<T>& getTry() {\n    return static_cast<decltype(*this)&>(walkProxyChain()).result_;\n  }\n  Try<T> const& getTry() const {\n    return static_cast<decltype(*this)&>(walkProxyChain()).result_;\n  }\n\n  /// Call only from consumer thread.\n  /// Call only once - else undefined behavior.\n  ///\n  /// See FSM graph for allowed transitions.\n  ///\n  /// If it transitions to Done, synchronously initiates a call to the callback,\n  /// and might also synchronously execute that callback (e.g., if there is no\n  /// executor or if the executor is inline).\n  template <class F>\n  void setCallback(\n      F&& func,\n      std::shared_ptr<folly::RequestContext>&& context,\n      futures::detail::InlineContinuation allowInline) {\n    Callback callback =\n        [func_2 = static_cast<F&&>(func)](\n            CoreBase& coreBase,\n            Executor::KeepAlive<>&& ka,\n            exception_wrapper* ew) mutable {\n          func_2(std::move(ka), setCallbackGetResult(coreBase, ew));\n        };\n\n    setCallback_(std::move(callback), std::move(context), allowInline);\n  }\n\n  /// Call only from producer thread.\n  /// Call only once - else undefined behavior.\n  ///\n  /// See FSM graph for allowed transitions.\n  ///\n  /// If it transitions to Done, synchronously initiates a call to the callback,\n  /// and might also synchronously execute that callback (e.g., if there is no\n  /// executor or if the executor is inline).\n  void setResult(Try<T>&& t) {\n    setResult(Executor::KeepAlive<>{}, std::move(t));\n  }\n\n  /// Call only from producer thread.\n  /// Call only once - else undefined behavior.\n  ///\n  /// See FSM graph for allowed transitions.\n  ///\n  /// If it transitions to Done, synchronously initiates a call to the callback,\n  /// and might also synchronously execute that callback (e.g., if there is no\n  /// executor, if the executor is inline or if completingKA represents the\n  /// same executor as does executor_).\n  void setResult(Executor::KeepAlive<>&& completingKA, Try<T>&& t) {\n    ::new (&this->result_) Result(std::move(t));\n    setResult_(std::move(completingKA));\n  }\n\n  /// Call only from producer thread.\n  /// Call only once - else undefined behavior.\n  ///\n  /// See FSM graph for allowed transitions.\n  ///\n  /// This can not be called concurrently with setResult().\n  void setProxy(Core* proxy) {\n    // NOTE: We could just expose this from the base, but that accepts any\n    // CoreBase, while we want to enforce the same Core<T> in the interface.\n    setProxy_(proxy);\n  }\n\n private:\n  Core() : CoreBase(State::Start, 2) {}\n\n  explicit Core(Try<T>&& t) : CoreBase(State::OnlyResult, 1) {\n    new (&this->result_) Result(std::move(t));\n  }\n\n  template <typename... Args>\n  explicit Core(std::in_place_t, Args&&... args) noexcept(\n      std::is_nothrow_constructible<T, Args&&...>::value)\n      : CoreBase(State::OnlyResult, 1) {\n    new (&this->result_) Result(std::in_place, static_cast<Args&&>(args)...);\n  }\n\n  ~Core() override {\n    if (destroyDerived()) {\n      this->result_.~Result();\n    }\n  }\n\n  static Try<T>&& setCallbackGetResult(\n      CoreBase& coreBase, exception_wrapper* ew) {\n    auto& core = static_cast<Core&>(coreBase);\n    if (ew != nullptr) {\n      core.result_.emplaceException(std::move(*ew));\n    }\n    return std::move(core.result_);\n  }\n};\n\ninline Executor* CoreBase::getExecutor() const {\n  if (!executor_.isKeepAlive()) {\n    return nullptr;\n  }\n  return executor_.getKeepAliveExecutor();\n}\n\ninline DeferredExecutor* CoreBase::getDeferredExecutor() const {\n  if (!executor_.isDeferred()) {\n    return {};\n  }\n\n  return executor_.getDeferredExecutor();\n}\n\n#if FOLLY_USE_EXTERN_FUTURE_UNIT\n// limited to the instances unconditionally forced by the futures library\nextern template class Core<folly::Unit>;\n#endif\n\n} // namespace detail\n} // namespace futures\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/detail/Types.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n\nnamespace folly {\n\n/// folly::Duration is an alias for the best resolution we offer/work with.\n/// However, it is not intended to be used for client code - you should use a\n/// descriptive std::chrono::duration type instead. e.g. do not write this:\n///\n///   futures::sleep(Duration(1000))...\n///\n/// rather this:\n///\n///   futures::sleep(std::chrono::milliseconds(1000));\n///\n/// or this:\n///\n///   futures::sleep(std::chrono::seconds(1));\nusing Duration = std::chrono::milliseconds;\nusing HighResDuration = std::chrono::microseconds;\n\nnamespace futures {\nnamespace detail {\nenum class InlineContinuation { permit, forbid };\n} // namespace detail\n} // namespace futures\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbsource//tools/build_defs:default_platform_defs.bzl\", \"ANDROID\", \"APPLE\", \"CXX\", \"WINDOWS\")\nload(\"../../defs.bzl\", \"CXXFLAGS\", \"FBANDROID_CPPFLAGS\", \"FBANDROID_CXXFLAGS\", \"FBOBJC_CXXFLAGS\", \"folly_xplat_cxx_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat build rules\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"test\",\n    srcs = [\n        \"BarrierTest.cpp\",\n        \"ContextTest.cpp\",\n        \"CoreTest.cpp\",\n        \"EnsureTest.cpp\",\n        \"FilterTest.cpp\",\n        \"HeaderCompileTest.cpp\",\n        \"MapTest.cpp\",\n        \"NonCopyableLambdaTest.cpp\",\n        \"PollTest.cpp\",\n        \"PromiseTest.cpp\",\n        \"ReduceTest.cpp\",\n        \"SelfDestructTest.cpp\",\n        \"SharedPromiseTest.cpp\",\n        \"TestExecutor.cpp\",\n        \"ThenCompileTest.cpp\",\n        \"ThenTest.cpp\",\n        \"TimesTest.cpp\",\n        \"UnwrapTest.cpp\",\n        \"ViaTest.cpp\",\n        \"WhenTest.cpp\",\n        \"WhileDoTest.cpp\",\n        \"WillEqualTest.cpp\",\n    ],\n    compiler_flags = CXXFLAGS + select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:android\": FBANDROID_CXXFLAGS + [\n            \"-Wno-deprecated-declarations\",\n        ],\n    }),\n    # TODO(T188948036): Fix xplat/folly/futures/test:test & folly_xplat_cxx_test\n    # The arguments below are unused by folly_xplat_cxx_test.\n    # They need to be investigated and fixed (i.e., correctly used or removed).\n    cxx_srcs = [\n        \"FutureTest.cpp\",\n        \"InterruptTest.cpp\",\n        \"TimekeeperTest.cpp\",\n        \"WaitTest.cpp\",\n        \"WindowTest.cpp\",\n    ],\n    fbobjc_compiler_flags = FBOBJC_CXXFLAGS,\n    fbobjc_srcs = [\n        \"FutureTest.cpp\",\n        \"InterruptTest.cpp\",\n        \"TimekeeperTest.cpp\",\n        \"WaitTest.cpp\",\n        \"WindowTest.cpp\",\n    ],\n    include_directories = [\n        \"../..\",\n    ],\n    platforms = (ANDROID, APPLE, CXX, WINDOWS),\n    preprocessor_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:linux\": FBANDROID_CPPFLAGS,\n    }),\n    raw_headers = [\n        \"TestExecutor.h\",\n        \"ThenCompileTest.h\",\n    ],\n    use_instrumentation_test = True,\n    deps = [\n        \"fbsource//third-party/boost:boost\",\n        \"fbsource//third-party/boost:boost_filesystem\",\n        \"fbsource//third-party/boost:boost_system\",\n        \"fbsource//third-party/double-conversion:double-conversion\",\n        \"fbsource//third-party/glog:glog\",\n        \"fbsource//xplat/folly:atomic_hash_map\",\n        \"fbsource//xplat/folly:atomic_linked_list\",\n        \"fbsource//xplat/folly:c_portability\",\n        \"fbsource//xplat/folly:chrono\",\n        \"fbsource//xplat/folly:config\",\n        \"fbsource//xplat/folly:constexpr_math\",\n        \"fbsource//xplat/folly:conv\",\n        \"fbsource//xplat/folly:cpp_attributes\",\n        \"fbsource//xplat/folly:demangle\",\n        \"fbsource//xplat/folly:discriminated_ptr\",\n        \"fbsource//xplat/folly:dynamic\",\n        \"fbsource//xplat/folly:exception\",\n        \"fbsource//xplat/folly:exception_string\",\n        \"fbsource//xplat/folly:executor\",\n        \"fbsource//xplat/folly:expected\",\n        \"fbsource//xplat/folly:fbstring\",\n        \"fbsource//xplat/folly:fbvector\",\n        \"fbsource//xplat/folly:file\",\n        \"fbsource//xplat/folly:format\",\n        \"fbsource//xplat/folly:format_traits\",\n        \"fbsource//xplat/folly:function\",\n        \"fbsource//xplat/folly:glog\",\n        \"fbsource//xplat/folly:indestructible\",\n        \"fbsource//xplat/folly:indexed_mem_pool\",\n        \"fbsource//xplat/folly:intrusive_list\",\n        \"fbsource//xplat/folly:likely\",\n        \"fbsource//xplat/folly:math\",\n        \"fbsource//xplat/folly:memory\",\n        \"fbsource//xplat/folly:micro_lock\",\n        \"fbsource//xplat/folly:move_wrapper\",\n        \"fbsource//xplat/folly:mpmc_queue\",\n        \"fbsource//xplat/folly:observer_container\",\n        \"fbsource//xplat/folly:optional\",\n        \"fbsource//xplat/folly:overload\",\n        \"fbsource//xplat/folly:portability\",\n        \"fbsource//xplat/folly:preprocessor\",\n        \"fbsource//xplat/folly:random\",\n        \"fbsource//xplat/folly:range\",\n        \"fbsource//xplat/folly:scope_guard\",\n        \"fbsource//xplat/folly:shared_mutex\",\n        \"fbsource//xplat/folly:singleton\",\n        \"fbsource//xplat/folly:singleton_thread_local\",\n        \"fbsource//xplat/folly:small_vector\",\n        \"fbsource//xplat/folly:sorted_vector_types\",\n        \"fbsource//xplat/folly:spin_lock\",\n        \"fbsource//xplat/folly:stop_watch\",\n        \"fbsource//xplat/folly:synchronized\",\n        \"fbsource//xplat/folly:thread_local\",\n        \"fbsource//xplat/folly:traits\",\n        \"fbsource//xplat/folly:unit\",\n        \"fbsource//xplat/folly/concurrency:cache_locality\",\n        \"fbsource//xplat/folly/concurrency:concurrent_hash_map\",\n        \"fbsource//xplat/folly/concurrency:priority_unbounded_queue_set\",\n        \"fbsource//xplat/folly/concurrency:unbounded_queue\",\n        \"fbsource//xplat/folly/container:array\",\n        \"fbsource//xplat/folly/container:bit_iterator\",\n        \"fbsource//xplat/folly/container:enumerate\",\n        \"fbsource//xplat/folly/container:evicting_cache_map\",\n        \"fbsource//xplat/folly/container:f14_hash\",\n        \"fbsource//xplat/folly/container:foreach\",\n        \"fbsource//xplat/folly/container:heap_vector_types\",\n        \"fbsource//xplat/folly/container:iterator\",\n        \"fbsource//xplat/folly/container:sparse_byte_set\",\n        \"fbsource//xplat/folly/detail:discriminated_ptr_detail\",\n        \"fbsource//xplat/folly/detail:futex\",\n        \"fbsource//xplat/folly/detail:socket_fast_open\",\n        \"fbsource//xplat/folly/detail:turn_sequencer\",\n        \"fbsource//xplat/folly/executors:cpu_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:global_executor\",\n        \"fbsource//xplat/folly/executors:inline_executor\",\n        \"fbsource//xplat/folly/executors:io_thread_pool_executor\",\n        \"fbsource//xplat/folly/executors:serial_executor\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_lifo_sem_mpmc_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:priority_unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/executors/task_queue:unbounded_blocking_queue\",\n        \"fbsource//xplat/folly/fibers:core\",\n        \"fbsource//xplat/folly/fibers:event_base_loop_controller\",\n        \"fbsource//xplat/folly/fibers:fiber_manager_map\",\n        \"fbsource//xplat/folly/functional:apply_tuple\",\n        \"fbsource//xplat/folly/functional:invoke\",\n        \"fbsource//xplat/folly/functional:partial\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:typed_io_buf\",\n        \"fbsource//xplat/folly/io/async:async_base\",\n        \"fbsource//xplat/folly/io/async:async_pipe\",\n        \"fbsource//xplat/folly/io/async:async_signal_handler\",\n        \"fbsource//xplat/folly/io/async:async_socket\",\n        \"fbsource//xplat/folly/io/async:async_socket_base\",\n        \"fbsource//xplat/folly/io/async:async_socket_exception\",\n        \"fbsource//xplat/folly/io/async:async_ssl_socket\",\n        \"fbsource//xplat/folly/io/async:async_transport\",\n        \"fbsource//xplat/folly/io/async:async_transport_certificate\",\n        \"fbsource//xplat/folly/io/async:async_udp_server_socket\",\n        \"fbsource//xplat/folly/io/async:async_udp_socket\",\n        \"fbsource//xplat/folly/io/async:decorated_async_transport_wrapper\",\n        \"fbsource//xplat/folly/io/async:destructor_check\",\n        \"fbsource//xplat/folly/io/async:scoped_event_base_thread\",\n        \"fbsource//xplat/folly/io/async:server_socket\",\n        \"fbsource//xplat/folly/io/async:ssl_context\",\n        \"fbsource//xplat/folly/io/async:ssl_options\",\n        \"fbsource//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"fbsource//xplat/folly/io/async/ssl:openssl_utils\",\n        \"fbsource//xplat/folly/io/async/ssl:ssl_errors\",\n        \"fbsource//xplat/folly/io/async/ssl:tls_definitions\",\n        \"fbsource//xplat/folly/lang:bits\",\n        \"fbsource//xplat/folly/lang:checked_math\",\n        \"fbsource//xplat/folly/lang:exception\",\n        \"fbsource//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"fbsource//xplat/folly/lang:uncaught_exceptions\",\n        \"fbsource//xplat/folly/logging:init\",\n        \"fbsource//xplat/folly/logging:init_weak\",\n        \"fbsource//xplat/folly/logging:log_handler\",\n        \"fbsource//xplat/folly/logging:log_level\",\n        \"fbsource//xplat/folly/logging:log_name\",\n        \"fbsource//xplat/folly/logging:logging\",\n        \"fbsource//xplat/folly/logging:rate_limiter\",\n        \"fbsource//xplat/folly/memory:arena\",\n        \"fbsource//xplat/folly/memory:malloc\",\n        \"fbsource//xplat/folly/net:tcpinfo\",\n        \"fbsource//xplat/folly/net:tcpinfo_dispatcher\",\n        \"fbsource//xplat/folly/portability:asm\",\n        \"fbsource//xplat/folly/portability:atomic\",\n        \"fbsource//xplat/folly/portability:builtins\",\n        \"fbsource//xplat/folly/portability:dirent\",\n        \"fbsource//xplat/folly/portability:event\",\n        \"fbsource//xplat/folly/portability:fcntl\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:iovec\",\n        \"fbsource//xplat/folly/portability:math\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:stdlib\",\n        \"fbsource//xplat/folly/portability:sys_mman\",\n        \"fbsource//xplat/folly/portability:sys_resource\",\n        \"fbsource//xplat/folly/portability:sys_stat\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \"fbsource//xplat/folly/portability:sys_time\",\n        \"fbsource//xplat/folly/portability:syslog\",\n        \"fbsource//xplat/folly/portability:time\",\n        \"fbsource//xplat/folly/portability:windows\",\n        \"fbsource//xplat/folly/ssl:openssl_cert_utils\",\n        \"fbsource//xplat/folly/ssl:openssl_hash\",\n        \"fbsource//xplat/folly/ssl:openssl_ptr_types\",\n        \"fbsource//xplat/folly/ssl:ssl_session\",\n        \"fbsource//xplat/folly/ssl:ssl_session_manager\",\n        \"fbsource//xplat/folly/synchronization:call_once\",\n        \"fbsource//xplat/folly/synchronization:hazptr\",\n        \"fbsource//xplat/folly/synchronization:lifo_sem\",\n        \"fbsource//xplat/folly/synchronization:micro_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:pico_spin_lock\",\n        \"fbsource//xplat/folly/synchronization:rcu\",\n        \"fbsource//xplat/folly/synchronization:small_locks\",\n        \"fbsource//xplat/folly/system:thread_id\",\n        \"fbsource//xplat/folly/system:thread_name\",\n        \"fbsource//xplat/folly/tracing:scoped_trace_section\",\n        \"fbsource//xplat/folly/tracing:static_tracepoint\",\n        \"fbsource//xplat/third-party/event:event\",\n        \"fbsource//xplat/third-party/linker_lib:atomic\",\n        \"fbsource//xplat/third-party/openssl:crypto\",\n        \"fbsource//xplat/third-party/openssl:ssl\",\n        \"//third-party/boost:boost\",\n        \"//third-party/boost:boost_system\",\n        \"//third-party/boost:boost_thread\",\n        \"//third-party/double-conversion:double-conversion\",\n        \"//third-party/glog:glog\",\n        \"//third-party/googletest:gtest_main\",\n        \"//xplat/folly:atomic_hash_map\",\n        \"//xplat/folly:atomic_linked_list\",\n        \"//xplat/folly:c_portability\",\n        \"//xplat/folly:config\",\n        \"//xplat/folly:constexpr_math\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:cpp_attributes\",\n        \"//xplat/folly:demangle\",\n        \"//xplat/folly:dynamic\",\n        \"//xplat/folly:exception\",\n        \"//xplat/folly:exception_string\",\n        \"//xplat/folly:executor\",\n        \"//xplat/folly:expected\",\n        \"//xplat/folly:fbstring\",\n        \"//xplat/folly:fbvector\",\n        \"//xplat/folly:file\",\n        \"//xplat/folly:format\",\n        \"//xplat/folly:format_traits\",\n        \"//xplat/folly:function\",\n        \"//xplat/folly:futures\",\n        \"//xplat/folly:glog\",\n        \"//xplat/folly:indestructible\",\n        \"//xplat/folly:intrusive_list\",\n        \"//xplat/folly:likely\",\n        \"//xplat/folly:math\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly:micro_lock\",\n        \"//xplat/folly:move_wrapper\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:overload\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:preprocessor\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly:shared_mutex\",\n        \"//xplat/folly:singleton_thread_local\",\n        \"//xplat/folly:small_vector\",\n        \"//xplat/folly:sorted_vector_types\",\n        \"//xplat/folly:spin_lock\",\n        \"//xplat/folly:test-headers\",\n        \"//xplat/folly:thread_local\",\n        \"//xplat/folly:traits\",\n        \"//xplat/folly:unit\",\n        \"//xplat/folly/concurrency:cache_locality\",\n        \"//xplat/folly/container:array\",\n        \"//xplat/folly/container:enumerate\",\n        \"//xplat/folly/container:foreach\",\n        \"//xplat/folly/container:heap_vector_types\",\n        \"//xplat/folly/container:iterator\",\n        \"//xplat/folly/container:sparse_byte_set\",\n        \"//xplat/folly/detail:futex\",\n        \"//xplat/folly/functional:apply_tuple\",\n        \"//xplat/folly/functional:invoke\",\n        \"//xplat/folly/functional:partial\",\n        \"//xplat/folly/io:iobuf\",\n        \"//xplat/folly/lang:bits\",\n        \"//xplat/folly/lang:checked_math\",\n        \"//xplat/folly/lang:exception\",\n        \"//xplat/folly/lang:rvalue_reference_wrapper\",\n        \"//xplat/folly/lang:uncaught_exceptions\",\n        \"//xplat/folly/memory:arena\",\n        \"//xplat/folly/memory:malloc\",\n        \"//xplat/folly/system:thread_id\",\n        \"//xplat/folly/system:thread_name\",\n        \"//xplat/folly/test/common:test_main\",\n    ],\n)\n\n# fbcode build rules\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"barrier_test\",\n    srcs = [\"BarrierTest.cpp\"],\n    deps = [\n        \"//folly:random\",\n        \"//folly/futures:barrier\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"callback_lifetime_test\",\n    srcs = [\"CallbackLifetimeTest.cpp\"],\n    deps = [\n        \":test_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"collect_test\",\n    srcs = [\"CollectTest.cpp\"],\n    deps = [\n        \"//folly:default_keep_alive_executor\",\n        \"//folly:random\",\n        \"//folly:small_vector\",\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"context_test\",\n    srcs = [\"ContextTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"core_test\",\n    srcs = [\"CoreTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/futures/detail:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ensure_test\",\n    srcs = [\"EnsureTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"filter_test\",\n    srcs = [\"FilterTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"future_splitter_test\",\n    srcs = [\"FutureSplitterTest.cpp\"],\n    deps = [\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:future_splitter\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"future_test\",\n    srcs = [\"FutureTest.cpp\"],\n    deps = [\n        \"//folly:executor\",\n        \"//folly:memory\",\n        \"//folly:unit\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/json:dynamic\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"semi_future_test\",\n    srcs = [\"SemiFutureTest.cpp\"],\n    deps = [\n        \"//folly:executor\",\n        \"//folly:memory\",\n        \"//folly:unit\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:async_base\",\n        \"//folly/json:dynamic\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"futures_benchmark\",\n    srcs = [\"Benchmark.cpp\"],\n    headers = [],\n    args = [\n        \"--json\",\n    ],\n    deps = [\n        \":test_executor\",\n        \"//folly:benchmark\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:native_semaphore\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"header_compile_test\",\n    srcs = [\"HeaderCompileTest.cpp\"],\n    deps = [\n        \"//folly:try\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"interrupt_test\",\n    srcs = [\"InterruptTest.cpp\"],\n    labels = [\"load-sensitive-timing-test\"],\n    deps = [\n        \":test_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"manual_timekeeper_test\",\n    srcs = [\"ManualTimekeeperTest.cpp\"],\n    deps = [\n        \"//folly/futures:manual_timekeeper\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"map_test\",\n    srcs = [\"MapTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"non_copyable_lambda_test\",\n    srcs = [\"NonCopyableLambdaTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"poll_test\",\n    srcs = [\"PollTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"promise_test\",\n    srcs = [\"PromiseTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"reduce_test\",\n    srcs = [\"ReduceTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"retrying_test\",\n    srcs = [\"RetryingTest.cpp\"],\n    deps = [\n        \":test_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sys_resource\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"self_destruct_test\",\n    srcs = [\"SelfDestructTest.cpp\"],\n    deps = [\n        \"//folly/executors:inline_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"shared_promise_test\",\n    srcs = [\"SharedPromiseTest.cpp\"],\n    deps = [\n        \"//folly/futures:shared_promise\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"test_executor\",\n    srcs = [\"TestExecutor.cpp\"],\n    headers = [\"TestExecutor.h\"],\n    exported_deps = [\n        \"//folly:executor\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"test_executor_test\",\n    srcs = [\"TestExecutorTest.cpp\"],\n    deps = [\n        \":test_executor\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"then_compile_test\",\n    srcs = [\"ThenCompileTest.cpp\"],\n    headers = [\"ThenCompileTest.h\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"then_test\",\n    srcs = [\"ThenTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"timekeeper_test\",\n    srcs = [\"TimekeeperTest.cpp\"],\n    deps = [\n        \"//folly:singleton\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"timekeeper_test_lib\",\n    headers = [\"TimekeeperTestLib.h\"],\n    exported_deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:default_keep_alive_executor\",\n        \"//folly:random\",\n        \"//folly:singleton\",\n        \"//folly/executors:global_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/executors:serial_executor\",\n        \"//folly/executors:virtual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"thread_wheel_timekeeper_test\",\n    srcs = [\"ThreadWheelTimekeeperTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \":timekeeper_test_lib\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"heap_timekeeper_test\",\n    srcs = [\"HeapTimekeeperTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \":timekeeper_test_lib\",\n        \"//folly/futures:core\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"times_test\",\n    srcs = [\"TimesTest.cpp\"],\n    deps = [\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"unwrap_test\",\n    srcs = [\"UnwrapTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"via_test\",\n    srcs = [\"ViaTest.cpp\"],\n    deps = [\n        \"//folly:mpmc_queue\",\n        \"//folly/executors:drivable_executor\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"wait_test\",\n    srcs = [\"WaitTest.cpp\"],\n    deps = [\n        \":test_executor\",\n        \"//folly/executors:inline_executor\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"when_test\",\n    srcs = [\"WhenTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"while_do_test\",\n    srcs = [\"WhileDoTest.cpp\"],\n    deps = [\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"will_equal_test\",\n    srcs = [\"WillEqualTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"window_test\",\n    srcs = [\"WindowTest.cpp\"],\n    deps = [\n        \"//folly:conv\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"conversion_operator\",\n    srcs = [\"ConversionOperatorTest.cpp\"],\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"cleanup_test\",\n    srcs = [\"CleanupTest.cpp\"],\n    headers = [],\n    labels = [\"case-isolation-failure\"],\n    deps = [\n        \"//folly/executors:cpu_thread_pool_executor\",\n        \"//folly/executors:manual_executor\",\n        \"//folly/futures:cleanup\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/futures/test/BarrierTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Barrier.h>\n\n#include <atomic>\n#include <condition_variable>\n#include <mutex>\n\n#include <folly/Random.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace futures {\nnamespace test {\n\nTEST(BarrierTest, Simple) {\n  constexpr uint32_t numThreads = 10;\n\n  std::mutex mutex;\n  std::condition_variable b1DoneCond;\n  std::condition_variable b2DoneCond;\n  std::atomic<uint32_t> b1TrueSeen(0);\n  std::atomic<uint32_t> b1Passed(0);\n  std::atomic<uint32_t> b2TrueSeen(0);\n  std::atomic<uint32_t> b2Passed(0);\n\n  Barrier barrier(numThreads + 1);\n\n  std::vector<std::thread> threads;\n  threads.reserve(numThreads);\n  for (uint32_t i = 0; i < numThreads; ++i) {\n    threads.emplace_back([&]() {\n      barrier.wait()\n          .thenValue([&](bool v) {\n            std::unique_lock lock(mutex);\n            b1TrueSeen += uint32_t(v);\n            if (++b1Passed == numThreads) {\n              b1DoneCond.notify_one();\n            }\n            return barrier.wait();\n          })\n          .thenValue([&](bool v) {\n            std::unique_lock lock(mutex);\n            b2TrueSeen += uint32_t(v);\n            if (++b2Passed == numThreads) {\n              b2DoneCond.notify_one();\n            }\n          })\n          .get();\n    });\n  }\n\n  /* sleep override */\n  std::this_thread::sleep_for(std::chrono::milliseconds(50));\n  EXPECT_EQ(0, b1Passed);\n  EXPECT_EQ(0, b1TrueSeen);\n\n  b1TrueSeen += barrier.wait().get();\n\n  {\n    std::unique_lock lock(mutex);\n    while (b1Passed != numThreads) {\n      b1DoneCond.wait(lock);\n    }\n    EXPECT_EQ(1, b1TrueSeen);\n  }\n\n  /* sleep override */\n  std::this_thread::sleep_for(std::chrono::milliseconds(50));\n  EXPECT_EQ(0, b2Passed);\n  EXPECT_EQ(0, b2TrueSeen);\n\n  b2TrueSeen += barrier.wait().get();\n\n  {\n    std::unique_lock lock(mutex);\n    while (b2Passed != numThreads) {\n      b2DoneCond.wait(lock);\n    }\n    EXPECT_EQ(1, b2TrueSeen);\n  }\n\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\nTEST(BarrierTest, Random) {\n  // Create numThreads threads.\n  //\n  // Each thread repeats the following numIterations times:\n  //   - grab a randomly chosen number of futures from the barrier, waiting\n  //     for a short random time between each\n  //   - wait for all futures to complete\n  //   - record whether the one future returning true was seen among them\n  //\n  // At the end, we verify that exactly one future returning true was seen\n  // for each iteration.\n  static constexpr uint32_t numIterations = 1;\n  auto numThreads = folly::Random::rand32(30, 91);\n\n  struct ThreadInfo {\n    ThreadInfo() {}\n    std::thread thread;\n    uint32_t iteration = 0;\n    uint32_t numFutures;\n    std::vector<uint32_t> trueSeen;\n  };\n\n  std::vector<ThreadInfo> threads;\n  threads.resize(numThreads);\n\n  uint32_t totalFutures = 0;\n  for (auto& tinfo : threads) {\n    tinfo.numFutures = folly::Random::rand32(100);\n    tinfo.trueSeen.resize(numIterations);\n    totalFutures += tinfo.numFutures;\n  }\n\n  Barrier barrier(totalFutures);\n\n  for (auto& tinfo : threads) {\n    auto pinfo = &tinfo;\n    tinfo.thread = std::thread([pinfo, &barrier] {\n      std::vector<folly::Future<bool>> futures;\n      futures.reserve(pinfo->numFutures);\n      for (uint32_t i = 0; i < numIterations; ++i, ++pinfo->iteration) {\n        futures.clear();\n        for (uint32_t j = 0; j < pinfo->numFutures; ++j) {\n          futures.push_back(barrier.wait());\n          auto nanos = folly::Random::rand32(10 * 1000 * 1000);\n          /* sleep override */\n          std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));\n        }\n        auto results = folly::collect(futures).get();\n        pinfo->trueSeen[i] = std::count(results.begin(), results.end(), true);\n      }\n    });\n  }\n\n  for (auto& tinfo : threads) {\n    tinfo.thread.join();\n    EXPECT_EQ(numIterations, tinfo.iteration);\n  }\n\n  for (uint32_t i = 0; i < numIterations; ++i) {\n    uint32_t trueCount = 0;\n    for (auto& tinfo : threads) {\n      trueCount += tinfo.trueSeen[i];\n    }\n    EXPECT_EQ(1, trueCount);\n  }\n}\n\n} // namespace test\n} // namespace futures\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/Benchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n\n#include <vector>\n\n#include <folly/executors/InlineExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n#include <folly/futures/test/TestExecutor.h>\n#include <folly/portability/GFlags.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/NativeSemaphore.h>\n\nusing namespace folly;\n\nnamespace {\n\ntemplate <class T>\nT incr(Try<T>&& t) {\n  return t.value() + 1;\n}\n\nFuture<int> thens(Future<int> f, size_t n, bool runInline = false) {\n  for (size_t i = 0; i < n; i++) {\n    if (runInline) {\n      f = std::move(f).thenInline(incr<int>);\n    } else {\n      f = std::move(f).then(incr<int>);\n    }\n  }\n  return f;\n}\n\nvoid someThens(size_t n) {\n  auto f = makeFuture<int>(42);\n  f = thens(std::move(f), n);\n}\n\nvoid someThensOnThread(size_t n, bool runInline = false) {\n  auto executor = std::make_unique<TestExecutor>(1);\n  auto f = makeFuture<int>(42).via(executor.get());\n  f = thens(std::move(f), n / 2, runInline);\n  f = thens(std::move(f), 1, false);\n  f = thens(std::move(f), n / 2, runInline);\n  f.wait();\n}\n\n} // namespace\n\nBENCHMARK(constantFuture) {\n  makeFuture(42);\n}\n\nBENCHMARK_RELATIVE(promiseAndFuture) {\n  Promise<int> p;\n  Future<int> f = p.getFuture();\n  p.setValue(42);\n  f.value();\n}\n\nBENCHMARK_RELATIVE(withThen) {\n  Promise<int> p;\n  Future<int> f = p.getFuture().then(incr<int>);\n  p.setValue(42);\n  f.value();\n}\n\n// thens\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(oneThen) {\n  someThens(1);\n}\n\n// look for >= 50% relative\nBENCHMARK_RELATIVE(twoThens) {\n  someThens(2);\n}\n\n// look for >= 25% relative\nBENCHMARK_RELATIVE(fourThens) {\n  someThens(4);\n}\n\n// look for >= 1% relative\nBENCHMARK_RELATIVE(hundredThens) {\n  someThens(100);\n}\n\nBENCHMARK_DRAW_LINE();\n\n// look for >= 25% relative\nBENCHMARK_RELATIVE(fourThensOnThread) {\n  someThensOnThread(4);\n}\n\n// look for >= 25% relative\nBENCHMARK_RELATIVE(fourThensOnThreadInline) {\n  someThensOnThread(4, true);\n}\n\n// look for >= 1% relative\nBENCHMARK_RELATIVE(hundredThensOnThread) {\n  someThensOnThread(100);\n}\n\n// look for >= 1% relative\nBENCHMARK_RELATIVE(hundredThensOnThreadInline) {\n  someThensOnThread(100, true);\n}\n\n// Lock contention. Although in practice fulfills tend to be temporally\n// separate from then()s, still sometimes they will be concurrent. So the\n// higher this number is, the better.\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(no_contention) {\n  std::vector<Promise<int>> promises(10000);\n  std::vector<Future<int>> futures;\n  std::thread producer, consumer;\n\n  BENCHMARK_SUSPEND {\n    folly::Baton<> b1, b2;\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    consumer = std::thread([&] {\n      b1.post();\n      for (auto& f : futures) {\n        std::move(f).then(incr<int>);\n      }\n    });\n    consumer.join();\n\n    producer = std::thread([&] {\n      b2.post();\n      for (auto& p : promises) {\n        p.setValue(42);\n      }\n    });\n\n    b1.wait();\n    b2.wait();\n  }\n\n  // The only thing we are measuring is how long fulfill + callbacks take\n  producer.join();\n}\n\nBENCHMARK_RELATIVE(contention) {\n  std::vector<Promise<int>> promises(10000);\n  std::vector<Future<int>> futures;\n  std::thread producer, consumer;\n  folly::NativeSemaphore sem;\n\n  BENCHMARK_SUSPEND {\n    folly::Baton<> b1, b2;\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    consumer = std::thread([&] {\n      b1.post();\n      for (auto& f : futures) {\n        sem.wait();\n        std::move(f).then(incr<int>);\n      }\n    });\n\n    producer = std::thread([&] {\n      b2.post();\n      for (auto& p : promises) {\n        sem.post();\n        p.setValue(42);\n      }\n    });\n\n    b1.wait();\n    b2.wait();\n  }\n\n  // The astute reader will notice that we're not *precisely* comparing apples\n  // to apples here. Well, maybe it's like comparing Granny Smith to\n  // Braeburn or something. In the serial version, we waited for the futures\n  // to be all set up, but here we are probably still doing that work\n  // (although in parallel). But even though there is more work (on the order\n  // of 2x), it is being done by two threads. Hopefully most of the difference\n  // we see is due to lock contention and not false parallelism.\n  //\n  // Be warned that if the box is under heavy load, this will greatly skew\n  // these results (scheduling overhead will begin to dwarf lock contention).\n  // I'm not sure but I'd guess in Windtunnel this will mean large variance,\n  // because I expect they load the boxes as much as they can?\n  consumer.join();\n  producer.join();\n}\n\nBENCHMARK_DRAW_LINE();\n\n// The old way. Throw an exception, and rethrow to access it upstream.\nvoid throwAndCatchImpl() {\n  makeFuture()\n      .then([](Try<Unit>&&) { throw std::runtime_error(\"oh no\"); })\n      .then([](Try<Unit>&& t) {\n        try {\n          t.value();\n        } catch (const std::runtime_error&) {\n          // ...\n          return;\n        }\n        CHECK(false);\n      });\n}\n\n// Not much better. Throw an exception, and access it via the wrapper upstream.\n// Actually a little worse due to wrapper overhead. then() won't know that the\n// exception is a runtime_error, so will have to store it as an exception_ptr\n// anyways. withException will therefore have to rethrow. Note that if we threw\n// std::exception instead, we would see some wins, as that's the type then()\n// will try to wrap, so no exception_ptrs/rethrows are necessary.\nvoid throwAndCatchWrappedImpl() {\n  makeFuture()\n      .then([](Try<Unit>&&) { throw std::runtime_error(\"oh no\"); })\n      .then([](Try<Unit>&& t) {\n        auto caught = t.withException<std::runtime_error>(\n            [](const std::runtime_error& /* e */) {\n              // ...\n            });\n        CHECK(caught);\n      });\n}\n\n// Better. Wrap an exception, and rethrow to access it upstream.\nvoid throwWrappedAndCatchImpl() {\n  makeFuture()\n      .then([](Try<Unit>&&) {\n        return makeFuture<Unit>(std::runtime_error(\"oh no\"));\n      })\n      .then([](Try<Unit>&& t) {\n        try {\n          t.value();\n        } catch (const std::runtime_error&) {\n          // ...\n          return;\n        }\n        CHECK(false);\n      });\n}\n\n// The new way. Wrap an exception, and access it via the wrapper upstream\nvoid throwWrappedAndCatchWrappedImpl() {\n  makeFuture()\n      .then([](Try<Unit>&&) {\n        return makeFuture<Unit>(std::runtime_error(\"oh no\"));\n      })\n      .then([](Try<Unit>&& t) {\n        auto caught = t.withException<std::runtime_error>(\n            [](const std::runtime_error& /* e */) {\n              // ...\n            });\n        CHECK(caught);\n      });\n}\n\n// Apparently OSX doesn't implement barriers since it is an optional part of\n// POSIX realtime threads extension, therefore we test for its presence.\n// Details:\n// https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap02.html\n#if defined(_POSIX_BARRIERS) && (_POSIX_BARRIERS > 0)\n// Simulate heavy contention on func\nvoid contend(void (*func)()) {\n  folly::BenchmarkSuspender s;\n  const int N = 100;\n  const int iters = 1000;\n  pthread_barrier_t barrier;\n  pthread_barrier_init(&barrier, nullptr, N + 1);\n  std::vector<std::thread> threads;\n  for (int i = 0; i < N; i++) {\n    threads.emplace_back([&]() {\n      pthread_barrier_wait(&barrier);\n      for (int j = 0; j < iters; j++) {\n        func();\n      }\n    });\n  }\n  pthread_barrier_wait(&barrier);\n  s.dismiss();\n  for (auto& t : threads) {\n    t.join();\n  }\n  s.rehire();\n  pthread_barrier_destroy(&barrier);\n}\n#endif\n\nBENCHMARK(throwAndCatch) {\n  throwAndCatchImpl();\n}\n\nBENCHMARK_RELATIVE(throwAndCatchWrapped) {\n  throwAndCatchWrappedImpl();\n}\n\nBENCHMARK_RELATIVE(throwWrappedAndCatch) {\n  throwWrappedAndCatchImpl();\n}\n\nBENCHMARK_RELATIVE(throwWrappedAndCatchWrapped) {\n  throwWrappedAndCatchWrappedImpl();\n}\n\nBENCHMARK_DRAW_LINE();\n\n#if defined(_POSIX_BARRIERS) && (_POSIX_BARRIERS > 0)\nBENCHMARK(throwAndCatchContended) {\n  contend(throwAndCatchImpl);\n}\n\nBENCHMARK_RELATIVE(throwAndCatchWrappedContended) {\n  contend(throwAndCatchWrappedImpl);\n}\n\nBENCHMARK_RELATIVE(throwWrappedAndCatchContended) {\n  contend(throwWrappedAndCatchImpl);\n}\n\nBENCHMARK_RELATIVE(throwWrappedAndCatchWrappedContended) {\n  contend(throwWrappedAndCatchWrappedImpl);\n}\nBENCHMARK_DRAW_LINE();\n#endif\n\nnamespace {\nstruct Bulky {\n  explicit Bulky(std::string message) : message_(message) {}\n  [[maybe_unused]] std::string message() & { return message_; }\n  std::string&& message() && { return std::move(message_); }\n\n private:\n  std::string message_;\n  FOLLY_PUSH_WARNING\n  FOLLY_CLANG_DISABLE_WARNING(\"-Wunused-private-field\")\n  std::array<int, 1024> ints_;\n  FOLLY_POP_WARNING\n};\n} // anonymous namespace\n\nBENCHMARK(lvalue_get) {\n  BenchmarkSuspender suspender;\n  Optional<Future<Bulky>> future;\n  future = makeFuture(Bulky(\"Hello\"));\n  suspender.dismissing([&] {\n    std::string message = std::move(future.value()).get().message();\n    doNotOptimizeAway(message);\n  });\n}\n\nBENCHMARK_RELATIVE(rvalue_get) {\n  BenchmarkSuspender suspender;\n  Optional<Future<Bulky>> future;\n  future = makeFuture(Bulky(\"Hello\"));\n  suspender.dismissing([&] {\n    std::string message = std::move(future.value()).get().message();\n    doNotOptimizeAway(message);\n  });\n}\n\nInlineExecutor exe;\n\ntemplate <class T>\nFuture<T> fGen() {\n  Promise<T> p;\n  auto f =\n      p.getFuture()\n          .thenValue([](T&& t) { return std::move(t); })\n          .thenValue([](T&& t) { return makeFuture(std::move(t)); })\n          .via(&exe)\n          .thenValue([](T&& t) { return std::move(t); })\n          .thenValue([](T&& t) { return makeFuture(std::move(t)); });\n  p.setValue(T());\n  return f;\n}\n\ntemplate <class T>\nstd::vector<Future<T>> fsGen() {\n  std::vector<Future<T>> fs;\n  for (auto i = 0; i < 10; i++) {\n    fs.push_back(fGen<T>());\n  }\n  return fs;\n}\n\ntemplate <class T>\nvoid complexBenchmark() {\n  collect(fsGen<T>());\n  collectAll(fsGen<T>());\n  collectAny(fsGen<T>());\n  futures::mapValue(fsGen<T>(), [](const T& t) { return t; });\n  futures::mapValue(fsGen<T>(), [](const T& t) { return makeFuture(T(t)); });\n}\n\nBENCHMARK_DRAW_LINE();\n\ntemplate <size_t S>\nstruct Blob {\n  char buf[S];\n};\n\nBENCHMARK(complexUnit) {\n  complexBenchmark<Unit>();\n}\n\nBENCHMARK_RELATIVE(complexBlob4) {\n  complexBenchmark<Blob<4>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob8) {\n  complexBenchmark<Blob<8>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob64) {\n  complexBenchmark<Blob<64>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob128) {\n  complexBenchmark<Blob<128>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob256) {\n  complexBenchmark<Blob<256>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob512) {\n  complexBenchmark<Blob<512>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob1024) {\n  complexBenchmark<Blob<1024>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob2048) {\n  complexBenchmark<Blob<2048>>();\n}\n\nBENCHMARK_RELATIVE(complexBlob4096) {\n  complexBenchmark<Blob<4096>>();\n}\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/futures/test/CallbackLifetimeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/futures/Future.h>\n#include <folly/futures/test/TestExecutor.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nnamespace {\n\n/***\n *  The basic premise is to check that the callback passed to then or thenError\n *  is destructed before wait returns on the resulting future.\n *\n *  The approach is to use callbacks where the destructor sleeps 500ms and then\n *  mutates a counter allocated on the caller stack. The caller checks the\n *  counter immediately after calling wait. Were the callback not destructed\n *  before wait returns, then we would very likely see an unchanged counter just\n *  after wait returns. But if, as we expect, the callback were destructed\n *  before wait returns, then we must be guaranteed to see a mutated counter\n *  just after wait returns.\n *\n *  Note that the failure condition is not strictly guaranteed under load. :(\n */\nclass CallbackLifetimeTest : public testing::Test {\n public:\n  using CounterPtr = std::unique_ptr<size_t>;\n\n  static bool kRaiseWillThrow() { return true; }\n  static constexpr auto kDelay() { return std::chrono::milliseconds(500); }\n\n  auto mkC() { return std::make_unique<size_t>(0); }\n  auto mkCGuard(CounterPtr& ptr) {\n    return makeGuard([&] {\n      /* sleep override */ std::this_thread::sleep_for(kDelay());\n      ++*ptr;\n    });\n  }\n\n  static void raise(folly::Unit = folly::Unit{}) {\n    if (kRaiseWillThrow()) { // to avoid marking [[noreturn]]\n      throw std::runtime_error(\"raise\");\n    }\n  }\n  static Future<Unit> raiseFut() {\n    raise();\n    return makeFuture();\n  }\n\n  TestExecutor executor{2}; // need at least 2 threads for internal futures\n};\n} // namespace\n\nTEST_F(CallbackLifetimeTest, thenReturnsValue) {\n  auto c = mkC();\n  via(&executor).thenValue([_ = mkCGuard(c)](auto&&) {}).wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenReturnsValueThrows) {\n  auto c = mkC();\n  via(&executor).thenValue([_ = mkCGuard(c)](auto&&) { raise(); }).wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenReturnsFuture) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue([_ = mkCGuard(c)](auto&&) { return makeFuture(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenReturnsFutureThrows) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue([_ = mkCGuard(c)](auto&&) { return raiseFut(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueMatch) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(folly::tag_t<std::exception>{}, [_ = mkCGuard(c)](auto&&) {})\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueMatchThrows) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(\n          folly::tag_t<std::exception>{},\n          [_ = mkCGuard(c)](auto&&) { raise(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueWrong) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(folly::tag_t<std::logic_error>{}, [_ = mkCGuard(c)](auto&&) {})\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueWrongThrows) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(\n          folly::tag_t<std::logic_error>{},\n          [_ = mkCGuard(c)](auto&&) { raise(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureMatch) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(\n          folly::tag_t<std::exception>{},\n          [_ = mkCGuard(c)](auto&&) { return makeFuture(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureMatchThrows) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(\n          folly::tag_t<std::exception>{},\n          [_ = mkCGuard(c)](auto&&) { return raiseFut(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureWrong) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(\n          folly::tag_t<std::logic_error>{},\n          [_ = mkCGuard(c)](auto&&) { return makeFuture(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureWrongThrows) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError(\n          folly::tag_t<std::logic_error>{},\n          [_ = mkCGuard(c)](auto&&) { return raiseFut(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsValue) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError([_ = mkCGuard(c)](exception_wrapper&&) {})\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsValueThrows) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError([_ = mkCGuard(c)](exception_wrapper&&) { raise(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsFuture) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError([_ = mkCGuard(c)](exception_wrapper&&) {\n        return makeFuture();\n      })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n\nTEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsFutureThrows) {\n  auto c = mkC();\n  via(&executor)\n      .thenValue(raise)\n      .thenError([_ = mkCGuard(c)](exception_wrapper&&) { return raiseFut(); })\n      .wait();\n  EXPECT_EQ(1, *c);\n}\n"
  },
  {
    "path": "folly/futures/test/CleanupTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Cleanup.h>\n\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals::chrono_literals;\n\nclass Cleaned : public folly::Cleanup {\n  folly::CPUThreadPoolExecutor pool_;\n\n public:\n  Cleaned() : pool_(4) {\n    addCleanup(folly::makeSemiFuture().defer([this](auto&&) {\n      this->pool_.join();\n    }));\n  }\n\n  using folly::Cleanup::addCleanup;\n};\n\nTEST(CleanupTest, Basic) {\n  EXPECT_TRUE(folly::is_cleanup_v<Cleaned>);\n\n  Cleaned cleaned;\n  int phase = 0;\n  int index = 0;\n\n  cleaned.addCleanup(\n      folly::makeSemiFuture().deferValue([&, expected = index++](folly::Unit) {\n        EXPECT_EQ(phase, 1);\n        EXPECT_EQ(--index, expected);\n      }));\n  cleaned.addCleanup(\n      folly::makeSemiFuture().deferValue([&, expected = index++](folly::Unit) {\n        EXPECT_EQ(phase, 1);\n        EXPECT_EQ(--index, expected);\n      }));\n  EXPECT_EQ(index, 2);\n\n  folly::ManualExecutor exec;\n  phase = 1;\n  cleaned.cleanup()\n      .within(1s)\n      .via(folly::getKeepAliveToken(exec))\n      .getVia(&exec);\n  phase = 2;\n  EXPECT_EQ(index, 0);\n}\n\nTEST(CleanupTest, EnsureCleanupAfterTaskBasic) {\n  Cleaned cleaned;\n  int phase = 0;\n  int index = 0;\n\n  cleaned.addCleanup(\n      folly::makeSemiFuture().deferValue([&, expected = index++](folly::Unit) {\n        EXPECT_EQ(phase, 1);\n        EXPECT_EQ(--index, expected);\n      }));\n\n  auto task =\n      folly::makeSemiFuture().deferValue([&, expected = index++](folly::Unit) {\n        EXPECT_EQ(phase, 1);\n        EXPECT_EQ(--index, expected);\n      });\n  EXPECT_EQ(index, 2);\n\n  folly::ManualExecutor exec;\n  phase = 1;\n  folly::ensureCleanupAfterTask(std::move(task), cleaned.cleanup())\n      .within(1s)\n      .via(folly::getKeepAliveToken(exec))\n      .getVia(&exec);\n  phase = 2;\n  EXPECT_EQ(index, 0);\n}\n\nTEST(CleanupTest, Errors) {\n  auto cleaned = std::make_unique<Cleaned>();\n\n  cleaned->addCleanup(folly::makeSemiFuture().deferValue([](folly::Unit) {\n    EXPECT_TRUE(false);\n  }));\n\n  cleaned->addCleanup(\n      folly::makeSemiFuture<folly::Unit>(std::runtime_error(\"failed cleanup\")));\n\n  folly::ManualExecutor exec;\n  EXPECT_EXIT(\n      cleaned->cleanup()\n          .within(1s)\n          .via(folly::getKeepAliveToken(exec))\n          .getVia(&exec),\n      testing::KilledBySignal(SIGABRT),\n      \".*noexcept.*\");\n\n  EXPECT_EXIT(\n      cleaned.reset(), testing::KilledBySignal(SIGABRT), \".*destructed.*\");\n\n  // must leak the Cleaned as its destructor will abort.\n  (void)cleaned.release();\n}\n\nTEST(CleanupTest, Invariants) {\n  Cleaned cleaned;\n\n  auto ranCleanup = false;\n  cleaned.addCleanup(folly::makeSemiFuture().deferValue([&](folly::Unit) {\n    ranCleanup = true;\n  }));\n\n  EXPECT_FALSE(ranCleanup);\n\n  {\n    folly::ManualExecutor exec;\n    cleaned.cleanup()\n        .within(1s)\n        .via(folly::getKeepAliveToken(exec))\n        .getVia(&exec);\n  }\n\n  EXPECT_TRUE(ranCleanup);\n\n  EXPECT_EXIT(\n      cleaned.addCleanup(folly::makeSemiFuture().deferValue([](folly::Unit) {\n        EXPECT_TRUE(false);\n      })),\n      testing::KilledBySignal(SIGABRT),\n      \".*addCleanup.*\");\n\n  {\n    folly::ManualExecutor exec;\n    EXPECT_EXIT(\n        cleaned.cleanup().via(folly::getKeepAliveToken(exec)).getVia(&exec),\n        testing::KilledBySignal(SIGABRT),\n        \".*already.*\");\n  }\n}\n"
  },
  {
    "path": "folly/futures/test/CollectTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <numeric>\n\n#include <folly/synchronization/test/Barrier.h>\n\n#include <folly/DefaultKeepAliveExecutor.h>\n#include <folly/Random.h>\n#include <folly/executors/CPUThreadPoolExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n#include <folly/small_vector.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nusing eggs_t = FutureException;\nstatic eggs_t eggs(\"eggs\");\n\nauto rng = std::mt19937(folly::randomNumberSeed());\n\nTEST(Collect, collectAll) {\n  // returns a vector variant\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collectAll(futures);\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (auto& p : promises) {\n      EXPECT_FALSE(allf.isReady());\n      p.setValue(42);\n    }\n\n    EXPECT_TRUE(allf.isReady());\n    auto& results = allf.value();\n    for (auto& t : results) {\n      EXPECT_EQ(42, t.value());\n    }\n  }\n\n  // check error semantics\n  {\n    std::vector<Promise<int>> promises(4);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collectAll(futures);\n\n    promises[0].setValue(42);\n    promises[1].setException(eggs);\n\n    EXPECT_FALSE(allf.isReady());\n\n    promises[2].setValue(42);\n\n    EXPECT_FALSE(allf.isReady());\n\n    promises[3].setException(eggs);\n\n    EXPECT_TRUE(allf.isReady());\n    EXPECT_FALSE(allf.result().hasException());\n\n    auto& results = allf.value();\n    EXPECT_EQ(42, results[0].value());\n    EXPECT_TRUE(results[1].hasException());\n    EXPECT_EQ(42, results[2].value());\n    EXPECT_TRUE(results[3].hasException());\n  }\n\n  // check that futures are ready in thenValue()\n  {\n    std::vector<Promise<Unit>> promises(10);\n    std::vector<Future<Unit>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf =\n        collectAllUnsafe(futures).thenTry([](Try<std::vector<Try<Unit>>>&& ts) {\n          for (auto& f : ts.value()) {\n            f.value();\n          }\n        });\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (auto& p : promises) {\n      p.setValue();\n    }\n    EXPECT_TRUE(allf.isReady());\n  }\n}\n\nTEST(Collect, collectAllUnsafe) {\n  // returns a vector variant\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collectAllUnsafe(futures);\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (auto& p : promises) {\n      EXPECT_FALSE(allf.isReady());\n      p.setValue(42);\n    }\n\n    EXPECT_TRUE(allf.isReady());\n    auto& results = allf.value();\n    for (auto& t : results) {\n      EXPECT_EQ(42, t.value());\n    }\n  }\n\n  // check error semantics\n  {\n    std::vector<Promise<int>> promises(4);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collectAllUnsafe(futures);\n\n    promises[0].setValue(42);\n    promises[1].setException(eggs);\n\n    EXPECT_FALSE(allf.isReady());\n\n    promises[2].setValue(42);\n\n    EXPECT_FALSE(allf.isReady());\n\n    promises[3].setException(eggs);\n\n    EXPECT_TRUE(allf.isReady());\n    EXPECT_FALSE(allf.result().hasException());\n\n    auto& results = allf.value();\n    EXPECT_EQ(42, results[0].value());\n    EXPECT_TRUE(results[1].hasException());\n    EXPECT_EQ(42, results[2].value());\n    EXPECT_TRUE(results[3].hasException());\n  }\n\n  // check that futures are ready in thenValue()\n  {\n    std::vector<Promise<Unit>> promises(10);\n    std::vector<Future<Unit>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf =\n        collectAllUnsafe(futures).thenTry([](Try<std::vector<Try<Unit>>>&& ts) {\n          for (auto& f : ts.value()) {\n            f.value();\n          }\n        });\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (auto& p : promises) {\n      p.setValue();\n    }\n    EXPECT_TRUE(allf.isReady());\n  }\n}\n\nTEST(Collect, collectAllInline) {\n  // inline future collection on same executor\n  {\n    ManualExecutor x;\n    std::vector<Future<int>> futures;\n    futures.emplace_back(makeFuture(42).via(&x));\n    futures.emplace_back(makeFuture(42).via(&x));\n    futures.emplace_back(makeFuture(42).via(&x));\n\n    auto allf = collectAll(futures).via(&x).thenTryInline([](auto&&) {});\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(3, x.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n  // inline defered semi-future collection on same executor\n  {\n    ManualExecutor x;\n    std::vector<SemiFuture<int>> futures;\n    futures.emplace_back(makeSemiFuture(42).defer([](auto&&) { return 42; }));\n    futures.emplace_back(makeSemiFuture(42).defer([](auto&&) { return 42; }));\n    futures.emplace_back(makeSemiFuture(42).defer([](auto&&) { return 42; }));\n\n    auto allf = collectAll(futures).defer([](auto&&) {}).via(&x);\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(3, x.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n  // inline future collection lastly fullfilled on same executor\n  {\n    ManualExecutor x1, x2;\n    std::vector<Future<int>> futures;\n    futures.emplace_back(makeFuture(42).via(&x1));\n    futures.emplace_back(makeFuture(42).via(&x2));\n\n    auto allf = collectAll(futures).defer([](auto&&) {}).via(&x1);\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x2.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x1.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n  // prevent inlining of future collection lastly fullfilled on different\n  // executor\n  {\n    ManualExecutor x1, x2;\n    std::vector<Future<int>> futures;\n    futures.emplace_back(makeFuture(42).via(&x1));\n    futures.emplace_back(makeFuture(42).via(&x2));\n\n    auto allf = collectAll(futures).defer([](auto&&) {}).via(&x1);\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x1.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x2.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x1.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n}\n\nTEST(Collect, collectInline) {\n  // inline future collection on same executor\n  {\n    ManualExecutor x;\n    std::vector<Future<int>> futures;\n    futures.emplace_back(makeFuture(42).via(&x));\n    futures.emplace_back(makeFuture(42).via(&x));\n    futures.emplace_back(makeFuture(42).via(&x));\n\n    auto allf = collect(futures).via(&x).thenTryInline([](auto&&) {});\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(3, x.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n  // inline defered semi-future collection on same executor\n  {\n    ManualExecutor x;\n    std::vector<SemiFuture<int>> futures;\n    futures.emplace_back(makeSemiFuture(42).defer([](auto&&) { return 42; }));\n    futures.emplace_back(makeSemiFuture(42).defer([](auto&&) { return 42; }));\n    futures.emplace_back(makeSemiFuture(42).defer([](auto&&) { return 42; }));\n\n    auto allf = collect(futures).defer([](auto&&) {}).via(&x);\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(3, x.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n  // inline future collection lastly fullfilled on same executor\n  {\n    ManualExecutor x1, x2;\n    std::vector<Future<int>> futures;\n    futures.emplace_back(makeFuture(42).via(&x1));\n    futures.emplace_back(makeFuture(42).via(&x2));\n\n    auto allf = collect(futures).defer([](auto&&) {}).via(&x1);\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x2.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x1.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n  // prevent inlining of future collection lastly fullfilled on different\n  // executor\n  {\n    ManualExecutor x1, x2;\n    std::vector<Future<int>> futures;\n    futures.emplace_back(makeFuture(42).via(&x1));\n    futures.emplace_back(makeFuture(42).via(&x2));\n\n    auto allf = collect(futures).defer([](auto&&) {}).via(&x1);\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x1.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x2.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x1.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n}\n\nTEST(Collect, collect) {\n  // success case\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collect(futures);\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (auto& p : promises) {\n      EXPECT_FALSE(allf.isReady());\n      p.setValue(42);\n    }\n\n    EXPECT_TRUE(allf.isReady());\n    for (auto i : allf.value()) {\n      EXPECT_EQ(42, i);\n    }\n  }\n\n  // failure case\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collect(futures);\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (int i = 0; i < 10; i++) {\n      if (i < 5) {\n        // everthing goes well so far...\n        EXPECT_FALSE(allf.isReady());\n        promises[i].setValue(42);\n      } else if (i == 5) {\n        // short circuit with an exception\n        EXPECT_FALSE(allf.isReady());\n        promises[i].setException(eggs);\n        EXPECT_TRUE(allf.isReady());\n      } else if (i < 8) {\n        // don't blow up on further values\n        EXPECT_TRUE(allf.isReady());\n        promises[i].setValue(42);\n      } else {\n        // don't blow up on further exceptions\n        EXPECT_TRUE(allf.isReady());\n        promises[i].setException(eggs);\n      }\n    }\n\n    EXPECT_THROW(allf.value(), eggs_t);\n  }\n\n  // void futures success case\n  {\n    std::vector<Promise<Unit>> promises(10);\n    std::vector<Future<Unit>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collect(futures);\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (auto& p : promises) {\n      EXPECT_FALSE(allf.isReady());\n      p.setValue();\n    }\n\n    EXPECT_TRUE(allf.isReady());\n  }\n\n  // void futures failure case\n  {\n    std::vector<Promise<Unit>> promises(10);\n    std::vector<Future<Unit>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto allf = collect(futures);\n\n    std::shuffle(promises.begin(), promises.end(), rng);\n    for (int i = 0; i < 10; i++) {\n      if (i < 5) {\n        // everthing goes well so far...\n        EXPECT_FALSE(allf.isReady());\n        promises[i].setValue();\n      } else if (i == 5) {\n        // short circuit with an exception\n        EXPECT_FALSE(allf.isReady());\n        promises[i].setException(eggs);\n        EXPECT_TRUE(allf.isReady());\n      } else if (i < 8) {\n        // don't blow up on further values\n        EXPECT_TRUE(allf.isReady());\n        promises[i].setValue();\n      } else {\n        // don't blow up on further exceptions\n        EXPECT_TRUE(allf.isReady());\n        promises[i].setException(eggs);\n      }\n    }\n\n    EXPECT_THROW(allf.value(), eggs_t);\n  }\n\n  // move only compiles\n  {\n    std::vector<Promise<std::unique_ptr<int>>> promises(10);\n    std::vector<Future<std::unique_ptr<int>>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    collect(futures);\n  }\n}\n\nstruct NotDefaultConstructible {\n  NotDefaultConstructible() = delete;\n  explicit NotDefaultConstructible(int arg) : i(arg) {}\n  int i;\n};\n\n// We have a specialized implementation for non-default-constructible objects\n// Ensure that it works and preserves order\nTEST(Collect, collectNotDefaultConstructible) {\n  std::vector<Promise<NotDefaultConstructible>> promises(10);\n  std::vector<Future<NotDefaultConstructible>> futures;\n  std::vector<int> indices(10);\n  std::iota(indices.begin(), indices.end(), 0);\n  std::shuffle(indices.begin(), indices.end(), rng);\n\n  for (auto& p : promises) {\n    futures.push_back(p.getFuture());\n  }\n\n  auto allf = collect(futures);\n\n  for (auto i : indices) {\n    EXPECT_FALSE(allf.isReady());\n    promises[i].setValue(NotDefaultConstructible(i));\n  }\n\n  EXPECT_TRUE(allf.isReady());\n  int i = 0;\n  for (auto val : allf.value()) {\n    EXPECT_EQ(i, val.i);\n    i++;\n  }\n}\n\nTEST(Collect, collectAny) {\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    for (auto& f : futures) {\n      EXPECT_FALSE(f.isReady());\n    }\n\n    auto anyf = collectAny(futures);\n\n    /* futures were moved in, so these are invalid now */\n    EXPECT_FALSE(anyf.isReady());\n\n    promises[7].setValue(42);\n    EXPECT_TRUE(anyf.isReady());\n    auto& idx_fut = anyf.value();\n\n    auto i = idx_fut.first;\n    EXPECT_EQ(7, i);\n\n    auto& f = idx_fut.second;\n    EXPECT_EQ(42, f.value());\n  }\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<SemiFuture<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getSemiFuture());\n    }\n\n    for (auto& f : futures) {\n      EXPECT_FALSE(f.isReady());\n    }\n\n    auto anyf = collectAny(futures);\n\n    /* futures were moved in, so these are invalid now */\n    EXPECT_FALSE(anyf.isReady());\n\n    promises[7].setValue(42);\n    EXPECT_TRUE(anyf.isReady());\n    auto& idx_fut = anyf.value();\n\n    auto i = idx_fut.first;\n    EXPECT_EQ(7, i);\n\n    auto& f = idx_fut.second;\n    EXPECT_EQ(42, f.value());\n  }\n\n  // error\n  {\n    std::vector<Promise<Unit>> promises(10);\n    std::vector<Future<Unit>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    for (auto& f : futures) {\n      EXPECT_FALSE(f.isReady());\n    }\n\n    auto anyf = collectAny(futures);\n\n    EXPECT_FALSE(anyf.isReady());\n\n    promises[3].setException(eggs);\n    EXPECT_TRUE(anyf.isReady());\n    EXPECT_TRUE(anyf.value().second.hasException());\n  }\n}\n\nTEST(Collect, collectAnyWithoutException) {\n  auto& executor = folly::InlineExecutor::instance();\n\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto onef = collectAnyWithoutException(futures).via(&executor);\n\n    /* futures were moved in, so these are invalid now */\n    EXPECT_FALSE(onef.isReady());\n\n    promises[7].setValue(42);\n    EXPECT_TRUE(onef.isReady());\n    auto& idx_fut = onef.value();\n    EXPECT_EQ(7, idx_fut.first);\n    EXPECT_EQ(42, idx_fut.second);\n  }\n\n  // some exception before ready\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto onef = collectAnyWithoutException(futures).via(&executor);\n\n    EXPECT_FALSE(onef.isReady());\n\n    promises[3].setException(eggs);\n    EXPECT_FALSE(onef.isReady());\n    promises[4].setException(eggs);\n    EXPECT_FALSE(onef.isReady());\n    promises[0].setValue(99);\n    EXPECT_TRUE(onef.isReady());\n    auto& idx_fut = onef.value();\n    EXPECT_EQ(0, idx_fut.first);\n    EXPECT_EQ(99, idx_fut.second);\n  }\n\n  // all exceptions\n  {\n    std::vector<Promise<int>> promises(10);\n    std::vector<Future<int>> futures;\n\n    for (auto& p : promises) {\n      futures.push_back(p.getFuture());\n    }\n\n    auto onef = collectAnyWithoutException(futures).via(&executor);\n\n    EXPECT_FALSE(onef.isReady());\n    for (int i = 0; i < 9; ++i) {\n      promises[i].setException(eggs);\n    }\n    EXPECT_FALSE(onef.isReady());\n\n    promises[9].setException(eggs);\n    EXPECT_TRUE(onef.isReady());\n    EXPECT_TRUE(onef.hasException());\n  }\n\n  // Deferred work\n  {\n    std::vector<Promise<int>> promises(10);\n\n    auto onef = [&] {\n      std::vector<SemiFuture<int>> futures;\n\n      for (auto& p : promises) {\n        futures.push_back(p.getSemiFuture().deferValue([](auto v) {\n          return v;\n        }));\n      }\n      return collectAnyWithoutException(futures);\n    }();\n\n    /* futures were moved in, so these are invalid now */\n\n    promises[7].setValue(42);\n    auto idx_fut = std::move(onef).get();\n    EXPECT_EQ(7, idx_fut.first);\n    EXPECT_EQ(42, idx_fut.second);\n  }\n}\n\nTEST(Collect, alreadyCompleted) {\n  {\n    std::vector<Future<Unit>> fs;\n    for (int i = 0; i < 10; i++) {\n      fs.push_back(makeFuture());\n    }\n\n    collectAllUnsafe(fs).thenValue([&](std::vector<Try<Unit>> ts) {\n      EXPECT_EQ(fs.size(), ts.size());\n    });\n  }\n  {\n    std::vector<Future<Unit>> fs;\n    for (int i = 0; i < 10; i++) {\n      fs.push_back(makeFuture());\n    }\n\n    collectAllUnsafe(fs).thenValue([&](std::vector<Try<Unit>> ts) {\n      EXPECT_EQ(fs.size(), ts.size());\n    });\n  }\n  {\n    std::vector<Future<int>> fs;\n    for (int i = 0; i < 10; i++) {\n      fs.push_back(makeFuture(i));\n    }\n\n    collectAny(fs).toUnsafeFuture().thenValue(\n        [&](std::pair<size_t, Try<int>> p) {\n          EXPECT_EQ(p.first, p.second.value());\n        });\n  }\n}\n\nTEST(Collect, parallel) {\n  std::vector<Promise<int>> ps(10);\n  std::vector<Future<int>> fs;\n  for (size_t i = 0; i < ps.size(); i++) {\n    fs.emplace_back(ps[i].getFuture());\n  }\n  auto f = collect(fs);\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      ps[i].setValue(i);\n    });\n  }\n\n  barrier.wait();\n\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts[i].join();\n  }\n\n  EXPECT_TRUE(f.isReady());\n  for (size_t i = 0; i < ps.size(); i++) {\n    EXPECT_EQ(i, f.value()[i]);\n  }\n}\n\nTEST(Collect, parallelWithError) {\n  std::vector<Promise<int>> ps(10);\n  std::vector<Future<int>> fs;\n  for (size_t i = 0; i < ps.size(); i++) {\n    fs.emplace_back(ps[i].getFuture());\n  }\n  auto f = collect(fs);\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      if (i == (ps.size() / 2)) {\n        ps[i].setException(eggs);\n      } else {\n        ps[i].setValue(i);\n      }\n    });\n  }\n\n  barrier.wait();\n\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts[i].join();\n  }\n\n  EXPECT_TRUE(f.isReady());\n  EXPECT_THROW(f.value(), eggs_t);\n}\n\nTEST(Collect, allParallel) {\n  std::vector<Promise<int>> ps(10);\n  std::vector<Future<int>> fs;\n  for (size_t i = 0; i < ps.size(); i++) {\n    fs.emplace_back(ps[i].getFuture());\n  }\n  auto f = collectAll(fs);\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      ps[i].setValue(i);\n    });\n  }\n\n  barrier.wait();\n\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts[i].join();\n  }\n\n  EXPECT_TRUE(f.isReady());\n  for (size_t i = 0; i < ps.size(); i++) {\n    EXPECT_TRUE(f.value()[i].hasValue());\n    EXPECT_EQ(i, f.value()[i].value());\n  }\n}\n\nTEST(Collect, allParallelWithError) {\n  std::vector<Promise<int>> ps(10);\n  std::vector<Future<int>> fs;\n  for (size_t i = 0; i < ps.size(); i++) {\n    fs.emplace_back(ps[i].getFuture());\n  }\n  auto f = collectAll(fs);\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      if (i == (ps.size() / 2)) {\n        ps[i].setException(eggs);\n      } else {\n        ps[i].setValue(i);\n      }\n    });\n  }\n\n  barrier.wait();\n\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts[i].join();\n  }\n\n  EXPECT_TRUE(f.isReady());\n  for (size_t i = 0; i < ps.size(); i++) {\n    if (i == (ps.size() / 2)) {\n      EXPECT_THROW(f.value()[i].value(), eggs_t);\n    } else {\n      EXPECT_TRUE(f.value()[i].hasValue());\n      EXPECT_EQ(i, f.value()[i].value());\n    }\n  }\n}\n\nTEST(Collect, collectN) {\n  std::vector<Promise<Unit>> promises(10);\n  std::vector<Future<Unit>> futures;\n\n  for (auto& p : promises) {\n    futures.push_back(p.getFuture());\n  }\n\n  bool flag = false;\n  size_t n = 3;\n  collectN(futures, n)\n      .via(&InlineExecutor::instance())\n      .thenValue([&](std::vector<std::pair<size_t, Try<Unit>>> v) {\n        flag = true;\n        EXPECT_EQ(n, v.size());\n        for (auto& tt : v) {\n          EXPECT_TRUE(tt.second.hasValue());\n        }\n      });\n\n  promises[0].setValue();\n  EXPECT_FALSE(flag);\n  promises[1].setValue();\n  EXPECT_FALSE(flag);\n  promises[2].setValue();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Collect, collectNParallel) {\n  std::vector<Promise<Unit>> ps(100);\n  std::vector<Future<Unit>> futures;\n\n  for (auto& p : ps) {\n    futures.push_back(p.getFuture());\n  }\n\n  bool flag = false;\n  size_t n = 90;\n  collectN(futures, n)\n      .via(&InlineExecutor::instance())\n      .thenValue([&](std::vector<std::pair<size_t, Try<Unit>>> v) {\n        flag = true;\n        EXPECT_EQ(n, v.size());\n        for (auto& tt : v) {\n          EXPECT_TRUE(tt.second.hasValue());\n        }\n      });\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      ps[i].setValue();\n    });\n  }\n\n  barrier.wait();\n\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts[i].join();\n  }\n\n  EXPECT_TRUE(flag);\n}\n\n/// Ensure that we can compile collectAll/Any with folly::small_vector\nTEST(Collect, smallVector) {\n  static_assert(\n      !std::is_trivially_copyable<Future<Unit>>::value,\n      \"Futures should not be trivially copyable\");\n  static_assert(\n      !std::is_trivially_copyable<Future<int>>::value,\n      \"Futures should not be trivially copyable\");\n\n  {\n    folly::small_vector<Future<Unit>> futures;\n\n    for (int i = 0; i < 10; i++) {\n      futures.push_back(makeFuture());\n    }\n\n    auto anyf = collectAny(futures);\n  }\n  {\n    folly::small_vector<Future<Unit>> futures;\n\n    for (int i = 0; i < 10; i++) {\n      futures.push_back(makeFuture());\n    }\n\n    auto allf = collectAll(futures);\n  }\n}\n\nTEST(Collect, collectAllVariadic) {\n  Promise<bool> pb;\n  Promise<int> pi;\n  Future<bool> fb = pb.getFuture();\n  Future<int> fi = pi.getFuture();\n  bool flag = false;\n  collectAllUnsafe(std::move(fb), std::move(fi))\n      .thenValue([&](std::tuple<Try<bool>, Try<int>> tup) {\n        flag = true;\n        EXPECT_TRUE(std::get<0>(tup).hasValue());\n        EXPECT_EQ(std::get<0>(tup).value(), true);\n        EXPECT_TRUE(std::get<1>(tup).hasValue());\n        EXPECT_EQ(std::get<1>(tup).value(), 42);\n      });\n  pb.setValue(true);\n  EXPECT_FALSE(flag);\n  pi.setValue(42);\n  EXPECT_TRUE(flag);\n}\n\nTEST(Collect, collectAllUnsafeVariadic) {\n  Promise<bool> pb;\n  Promise<int> pi;\n  Future<bool> fb = pb.getFuture();\n  Future<int> fi = pi.getFuture();\n  bool flag = false;\n  collectAllUnsafe(std::move(fb), std::move(fi))\n      .thenValue([&](std::tuple<Try<bool>, Try<int>> tup) {\n        flag = true;\n        EXPECT_TRUE(std::get<0>(tup).hasValue());\n        EXPECT_EQ(std::get<0>(tup).value(), true);\n        EXPECT_TRUE(std::get<1>(tup).hasValue());\n        EXPECT_EQ(std::get<1>(tup).value(), 42);\n      });\n  pb.setValue(true);\n  EXPECT_FALSE(flag);\n  pi.setValue(42);\n  EXPECT_TRUE(flag);\n}\n\nTEST(Collect, collectAllVariadicReferences) {\n  Promise<bool> pb;\n  Promise<int> pi;\n  Future<bool> fb = pb.getFuture();\n  Future<int> fi = pi.getFuture();\n  bool flag = false;\n  collectAllUnsafe(fb, fi).thenValue([&](std::tuple<Try<bool>, Try<int>> tup) {\n    flag = true;\n    EXPECT_TRUE(std::get<0>(tup).hasValue());\n    EXPECT_EQ(std::get<0>(tup).value(), true);\n    EXPECT_TRUE(std::get<1>(tup).hasValue());\n    EXPECT_EQ(std::get<1>(tup).value(), 42);\n  });\n  pb.setValue(true);\n  EXPECT_FALSE(flag);\n  pi.setValue(42);\n  EXPECT_TRUE(flag);\n}\n\nTEST(Collect, collectAllVariadicWithException) {\n  Promise<bool> pb;\n  Promise<int> pi;\n  Future<bool> fb = pb.getFuture();\n  Future<int> fi = pi.getFuture();\n  bool flag = false;\n  collectAllUnsafe(std::move(fb), std::move(fi))\n      .thenValue([&](std::tuple<Try<bool>, Try<int>> tup) {\n        flag = true;\n        EXPECT_TRUE(std::get<0>(tup).hasValue());\n        EXPECT_EQ(std::get<0>(tup).value(), true);\n        EXPECT_TRUE(std::get<1>(tup).hasException());\n        EXPECT_THROW(std::get<1>(tup).value(), eggs_t);\n      });\n  pb.setValue(true);\n  EXPECT_FALSE(flag);\n  pi.setException(eggs);\n  EXPECT_TRUE(flag);\n}\n\nTEST(Collect, collectVariadic) {\n  Promise<bool> pb;\n  Promise<int> pi;\n  Future<bool> fb = pb.getFuture();\n  Future<int> fi = pi.getFuture();\n  bool flag = false;\n  collect(std::move(fb), std::move(fi))\n      .toUnsafeFuture()\n      .thenValue([&](std::tuple<bool, int> tup) {\n        flag = true;\n        EXPECT_EQ(std::get<0>(tup), true);\n        EXPECT_EQ(std::get<1>(tup), 42);\n      });\n  pb.setValue(true);\n  EXPECT_FALSE(flag);\n  pi.setValue(42);\n  EXPECT_TRUE(flag);\n}\n\nTEST(Collect, collectUnsafeVariadic) {\n  Promise<bool> pb;\n  Promise<int> pi;\n  Future<bool> fb = pb.getFuture();\n  Future<int> fi = pi.getFuture();\n  bool flag = false;\n  collectUnsafe(std::move(fb), std::move(fi))\n      .thenValue([&](std::tuple<bool, int> tup) {\n        flag = true;\n        EXPECT_EQ(std::get<0>(tup), true);\n        EXPECT_EQ(std::get<1>(tup), 42);\n      });\n  pb.setValue(true);\n  EXPECT_FALSE(flag);\n  pi.setValue(42);\n  EXPECT_TRUE(flag);\n}\n\nTEST(Collect, collectVariadicWithException) {\n  Promise<bool> pb;\n  Promise<int> pi;\n  Future<bool> fb = pb.getFuture();\n  Future<int> fi = pi.getFuture();\n  auto f = collect(std::move(fb), std::move(fi));\n  pb.setValue(true);\n  EXPECT_FALSE(f.isReady());\n  pi.setException(eggs);\n  EXPECT_TRUE(f.isReady());\n  EXPECT_TRUE(f.result().hasException());\n  EXPECT_THROW(std::move(f).get(), eggs_t);\n}\n\nTEST(Collect, collectAllNone) {\n  std::vector<Future<int>> fs;\n  auto f = collectAll(fs);\n  EXPECT_TRUE(f.isReady());\n}\n\nTEST(Collect, noDefaultConstructor) {\n  struct A {\n    explicit A(size_t /* x */) {}\n  };\n\n  auto f1 = makeFuture(A(1));\n  auto f2 = makeFuture(A(2));\n\n  auto f = collect(std::move(f1), std::move(f2));\n}\n\nTEST(Collect, CollectVariadicWithDestroyedWeakRef) {\n  auto one = std::make_unique<folly::CPUThreadPoolExecutor>(1);\n  auto two = std::make_unique<folly::CPUThreadPoolExecutor>(1);\n  auto reachedFirstCallback = folly::Baton<>{};\n  auto hasExecutorBeenDestroyed = folly::Baton<>{};\n\n  auto future = folly::collect(\n      folly::makeSemiFuture(),\n      folly::makeSemiFuture()\n          .via(one.get())\n          .thenValue([&](auto) {\n            reachedFirstCallback.post();\n            hasExecutorBeenDestroyed.wait();\n          })\n          .via(two->weakRef())\n          .thenValue([](auto) {}),\n      folly::makeSemiFuture());\n\n  reachedFirstCallback.wait();\n  two.reset();\n  hasExecutorBeenDestroyed.post();\n\n  EXPECT_THROW(std::move(future).get(), folly::BrokenPromise);\n}\n\nTEST(Collect, CollectRangeWithDestroyedWeakRef) {\n  auto one = std::make_unique<folly::CPUThreadPoolExecutor>(1);\n  auto two = std::make_unique<folly::CPUThreadPoolExecutor>(1);\n  auto reachedFirstCallback = folly::Baton<>{};\n  auto hasExecutorBeenDestroyed = folly::Baton<>{};\n\n  auto futures = std::vector<folly::SemiFuture<folly::Unit>>{};\n  futures.push_back(folly::makeSemiFuture());\n  futures.emplace_back(\n      folly::makeSemiFuture()\n          .via(one.get())\n          .thenValue([&](auto) {\n            reachedFirstCallback.post();\n            hasExecutorBeenDestroyed.wait();\n          })\n          .via(two->weakRef())\n          .thenValue([](auto) {}));\n  futures.push_back(folly::makeSemiFuture());\n  auto future = folly::collect(futures.begin(), futures.end());\n\n  reachedFirstCallback.wait();\n  two.reset();\n  hasExecutorBeenDestroyed.post();\n\n  EXPECT_THROW(std::move(future).get(), folly::BrokenPromise);\n}\n"
  },
  {
    "path": "folly/futures/test/ContextTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nclass TestData : public RequestData {\n public:\n  explicit TestData(int data) : data_(data) {}\n  ~TestData() override {}\n\n  bool hasCallback() override { return false; }\n\n  int data_;\n};\n\nTEST(Context, basic) {\n  // Start a new context\n  folly::RequestContextScopeGuard rctx;\n\n  EXPECT_EQ(nullptr, RequestContext::get()->getContextData(\"test\"));\n\n  // Set some test data\n  RequestContext::get()->setContextData(\"test\", std::make_unique<TestData>(10));\n\n  // Start a future\n  Promise<Unit> p;\n  auto future = p.getFuture().thenValue([&](auto&&) {\n    // Check that the context followed the future\n    EXPECT_TRUE(RequestContext::get() != nullptr);\n    auto a =\n        dynamic_cast<TestData*>(RequestContext::get()->getContextData(\"test\"));\n    auto data = a->data_;\n    EXPECT_EQ(10, data);\n  });\n\n  // Clear the context\n  folly::RequestContextScopeGuard rctx2;\n\n  EXPECT_EQ(nullptr, RequestContext::get()->getContextData(\"test\"));\n\n  // Fulfill the promise\n  p.setValue();\n}\n"
  },
  {
    "path": "folly/futures/test/ConversionOperatorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nnamespace {\nstruct Widget {\n  int v_;\n  /* implicit */ Widget(int v) : v_(v) {}\n  Widget(const Widget& other) = default;\n  Widget(Widget&& other) noexcept = default;\n  Widget& operator=(const Widget& /* other */) {\n    throw std::logic_error(\"unexpected copy assignment\");\n  }\n  Widget& operator=(Widget&& /* other */) {\n    throw std::logic_error(\"unexpected move assignment\");\n  }\n  explicit operator int() && { return v_; }\n};\n} // namespace\n\nTEST(ConverstionOperator, DirectInitialization) {\n  auto future = makeFuture<Widget>(23);\n  EXPECT_EQ(future.value().v_, 23);\n  Future<int> secondFuture{std::move(future)};\n  EXPECT_EQ(secondFuture.value(), 23);\n}\n\nTEST(ConverstionOperator, StaticCast) {\n  auto future = makeFuture<Widget>(23);\n  EXPECT_EQ(future.value().v_, 23);\n  EXPECT_EQ(static_cast<Future<int>>(std::move(future)).value(), 23);\n}\n"
  },
  {
    "path": "folly/futures/test/CoreTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/detail/Core.h>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(Core, size) {\n  struct KeepAliveOrDeferredGold {\n    enum class State {};\n    State state_;\n    Executor* executor_;\n  };\n  class CoreBaseGold {\n   public:\n    virtual ~CoreBaseGold() = 0;\n\n   private:\n    folly::Function<void(Try<Unit>&&)> callback_;\n    std::atomic<futures::detail::State> state_;\n    std::atomic<unsigned char> attached_;\n    std::atomic<unsigned char> callbackReferences_;\n    KeepAliveOrDeferredGold executor_;\n    std::shared_ptr<RequestContext> context_;\n    std::atomic<uintptr_t> interrupt_;\n    CoreBaseGold* proxy_;\n  };\n  class CoreGold : Try<Unit>, public CoreBaseGold {};\n  // If this number goes down, it's fine!\n  // If it goes up, please seek professional advice ;-)\n  EXPECT_EQ(sizeof(CoreGold), sizeof(futures::detail::Core<Unit>));\n  EXPECT_EQ(alignof(CoreGold), alignof(futures::detail::Core<Unit>));\n}\n"
  },
  {
    "path": "folly/futures/test/EnsureTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <unordered_set>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(Ensure, basic) {\n  size_t count = 0;\n  auto cob = [&] { count++; };\n  auto f =\n      makeFuture(42)\n          .ensure(cob)\n          .thenValue([](int) { throw std::runtime_error(\"ensure\"); })\n          .ensure(cob);\n\n  EXPECT_THROW(std::move(f).get(), std::runtime_error);\n  EXPECT_EQ(2, count);\n}\n\nTEST(Ensure, mutableLambda) {\n  auto set = std::make_shared<std::unordered_set<int>>();\n  set->insert(1);\n  set->insert(2);\n\n  auto f =\n      makeFuture(4)\n          .ensure([set]() mutable { set->clear(); })\n          .thenValue([](auto&&) { throw std::runtime_error(\"ensure\"); });\n\n  EXPECT_EQ(0, set->size());\n  EXPECT_THROW(std::move(f).get(), std::runtime_error);\n}\n"
  },
  {
    "path": "folly/futures/test/FilterTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(Filter, alwaysTrye) {\n  EXPECT_EQ(42, makeFuture(42).filter([](int) { return true; }).get());\n}\n\nTEST(Filter, alwaysFalse) {\n  EXPECT_THROW(\n      makeFuture(42).filter([](int) { return false; }).get(),\n      folly::FuturePredicateDoesNotObtain);\n}\n\nTEST(Filter, moveOnlyValue) {\n  EXPECT_EQ(\n      42,\n      *makeFuture(std::make_unique<int>(42))\n           .filter([](std::unique_ptr<int> const&) { return true; })\n           .get());\n}\n"
  },
  {
    "path": "folly/futures/test/FutureSplitterTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/FutureSplitter.h>\n\n#include <folly/executors/ManualExecutor.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(FutureSplitter, splitFutureSuccess) {\n  Promise<int> p;\n  folly::FutureSplitter<int> sp(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  auto f1 = sp.getFuture();\n  EXPECT_FALSE(f1.isReady());\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  auto f2 = sp.getFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasValue());\n}\n\nTEST(FutureSplitter, splitFutureSuccessSemiFuture) {\n  Promise<int> p;\n  folly::FutureSplitter<int> sp(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  auto f1 = sp.getSemiFuture();\n  EXPECT_FALSE(f1.isReady());\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  auto f2 = sp.getSemiFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasValue());\n}\n\nTEST(FutureSplitter, splitFutureSuccessNullExecutor) {\n  Promise<int> p;\n  auto sf = p.getSemiFuture();\n  // Double via because a null executor to SemiFuture.via is invalid but we\n  // are testing a situation where we have a FutureSplitter from a future with\n  // a null executor to account for legacy code.\n  auto f = std::move(sf).via(&InlineExecutor::instance()).via(nullptr);\n  folly::FutureSplitter<int> sp(std::move(f));\n  auto f1 = sp.getFuture();\n  EXPECT_FALSE(f1.isReady());\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  auto f2 = sp.getFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasValue());\n}\n\nTEST(FutureSplitter, splitFutureCopyable) {\n  Promise<int> p;\n  folly::FutureSplitter<int> sp1(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  folly::FutureSplitter<int> sp2(sp1);\n  auto f1 = sp1.getFuture();\n  EXPECT_FALSE(f1.isReady());\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  auto f2 = sp2.getFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasValue());\n  folly::FutureSplitter<int> sp3(sp1);\n  auto f3 = sp3.getFuture();\n  EXPECT_TRUE(f3.isReady());\n  EXPECT_TRUE(f3.hasValue());\n}\n\nTEST(FutureSplitter, splitFutureMovable) {\n  Promise<int> p;\n  folly::FutureSplitter<int> sp1(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  auto f1 = sp1.getFuture();\n  folly::FutureSplitter<int> sp2(std::move(sp1));\n  EXPECT_FALSE(f1.isReady());\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  auto f2 = sp2.getFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasValue());\n  folly::FutureSplitter<int> sp3(std::move(sp2));\n  auto f3 = sp3.getFuture();\n  EXPECT_TRUE(f3.isReady());\n  EXPECT_TRUE(f3.hasValue());\n}\n\nTEST(FutureSplitter, splitFutureCopyAssignable) {\n  Promise<int> p;\n  folly::FutureSplitter<int> sp1(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  folly::FutureSplitter<int> sp2{};\n  sp2 = sp1;\n  auto f1 = sp1.getFuture();\n  EXPECT_FALSE(f1.isReady());\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  auto f2 = sp2.getFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasValue());\n  folly::FutureSplitter<int> sp3(sp1);\n  auto f3 = sp3.getFuture();\n  EXPECT_TRUE(f3.isReady());\n  EXPECT_TRUE(f3.hasValue());\n}\n\nTEST(FutureSplitter, splitFutureMoveAssignable) {\n  Promise<int> p;\n  folly::FutureSplitter<int> sp1(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  auto f1 = sp1.getFuture();\n  folly::FutureSplitter<int> sp2{};\n  sp2 = std::move(sp1);\n  EXPECT_FALSE(f1.isReady());\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  auto f2 = sp2.getFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasValue());\n  folly::FutureSplitter<int> sp3(std::move(sp2));\n  auto f3 = sp3.getFuture();\n  EXPECT_TRUE(f3.isReady());\n  EXPECT_TRUE(f3.hasValue());\n}\n\nTEST(FutureSplitter, splitFutureScope) {\n  Promise<int> p;\n  auto pSP = std::make_unique<folly::FutureSplitter<int>>(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  auto f1 = pSP->getFuture();\n  EXPECT_FALSE(f1.isReady());\n  pSP.reset();\n  EXPECT_NO_THROW(EXPECT_FALSE(f1.isReady()));\n  p.setValue(1);\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasValue());\n  EXPECT_EQ(1, std::move(f1).get());\n}\n\nTEST(FutureSplitter, splitFutureFailure) {\n  Promise<int> p;\n  folly::FutureSplitter<int> sp(\n      p.getSemiFuture().via(&InlineExecutor::instance()));\n  auto f1 = sp.getFuture();\n  EXPECT_FALSE(f1.isReady());\n  p.setException(exception_wrapper{std::runtime_error(\"Oops\")});\n  EXPECT_TRUE(f1.isReady());\n  EXPECT_TRUE(f1.hasException());\n  auto f2 = sp.getFuture();\n  EXPECT_TRUE(f2.isReady());\n  EXPECT_TRUE(f2.hasException());\n}\n\nTEST(FutureSplitter, lifetime) {\n  struct ManualExecutorWithPriority : folly::ManualExecutor {\n    void addWithPriority(Func func, int8_t) override { add(std::move(func)); }\n  };\n  ManualExecutorWithPriority ex;\n  auto ka = folly::ExecutorWithPriority::create(\n      folly::getKeepAliveToken(ex), folly::Executor::MID_PRI);\n  auto split = folly::splitFuture(folly::via(std::move(ka), [] { return 3; }));\n  ex.drain();\n  auto fut = split.getFuture().thenValue([](auto i) { return i + 1; });\n  ex.drain();\n  EXPECT_EQ(4, fut.value());\n}\n"
  },
  {
    "path": "folly/futures/test/FutureTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n\n#include <algorithm>\n#include <atomic>\n#include <memory>\n#include <numeric>\n#include <queue>\n#include <string>\n#include <thread>\n#include <type_traits>\n\n#include <folly/Executor.h>\n#include <folly/Memory.h>\n#include <folly/Unit.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/json/dynamic.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\n#define EXPECT_TYPE(x, T) EXPECT_TRUE((std::is_same<decltype(x), T>::value))\n\nusing eggs_t = FutureException;\nstatic eggs_t eggs(\"eggs\");\n\n// Future\n\nTEST(Future, makeEmpty) {\n  auto f = Future<int>::makeEmpty();\n  EXPECT_THROW(f.isReady(), FutureInvalid);\n}\n\nTEST(Future, futureDefaultCtor) {\n  Future<Unit>();\n}\n\nTEST(Future, futureToUnit) {\n  Future<Unit> fu = makeFuture(42).unit();\n  fu.value();\n  EXPECT_TRUE(makeFuture<int>(eggs).unit().hasException());\n}\n\nTEST(Future, voidFutureToUnit) {\n  Future<Unit> fu = makeFuture().unit();\n  fu.value();\n  EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());\n}\n\nTEST(Future, unitFutureToUnitIdentity) {\n  Future<Unit> fu = makeFuture(Unit{}).unit();\n  fu.value();\n  EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());\n}\n\nTEST(Future, toUnitWhileInProgress) {\n  Promise<int> p;\n  Future<Unit> fu = p.getFuture().unit();\n  EXPECT_FALSE(fu.isReady());\n  p.setValue(42);\n  EXPECT_TRUE(fu.isReady());\n}\n\nTEST(Future, makeFutureWithUnit) {\n  int count = 0;\n  Future<Unit> fu = makeFutureWith([&] { count++; });\n  EXPECT_EQ(1, count);\n}\n\nTEST(Future, getRequiresOnlyMoveCtor) {\n  struct MoveCtorOnly {\n    explicit MoveCtorOnly(int id) : id_(id) {}\n    MoveCtorOnly(const MoveCtorOnly&) = delete;\n    MoveCtorOnly(MoveCtorOnly&&) = default;\n    void operator=(MoveCtorOnly const&) = delete;\n    void operator=(MoveCtorOnly&&) = delete;\n    int id_;\n  };\n  {\n    auto f = makeFuture<MoveCtorOnly>(MoveCtorOnly(42));\n    EXPECT_TRUE(f.valid());\n    EXPECT_TRUE(f.isReady());\n    auto v = std::move(f).get();\n    EXPECT_EQ(v.id_, 42);\n  }\n  {\n    auto f = makeFuture<MoveCtorOnly>(MoveCtorOnly(42));\n    EXPECT_TRUE(f.valid());\n    EXPECT_TRUE(f.isReady());\n    auto v = std::move(f).get(std::chrono::milliseconds(10));\n    EXPECT_EQ(v.id_, 42);\n  }\n}\n\nTEST(Future, makeFutureFromMoveOnlyException) {\n#ifdef __APPLE__\n  // On macOS, std::make_exception_ptr copies the exception internally,\n  // causing this test to fail. Skip until a proper fix is implemented.\n  GTEST_SKIP() << \"Test disabled on macOS due to exception_ptr copy behavior\";\n#endif\n  using ::testing::StrEq;\n  using ::testing::ThrowsMessage;\n\n  struct MoveOnlyException : std::runtime_error {\n    using std::runtime_error::runtime_error;\n    [[noreturn]] MoveOnlyException(const MoveOnlyException& other)\n        : std::runtime_error(other) {\n      throw std::logic_error(\"Copy constructor is called\");\n    }\n    MoveOnlyException(MoveOnlyException&&) = default;\n    MoveOnlyException& operator=(MoveOnlyException const&) {\n      throw std::logic_error(\"Copy assignment operator is called\");\n    }\n    MoveOnlyException& operator=(MoveOnlyException&&) = default;\n  };\n\n  std::string msg = \"exception message\";\n\n  auto f = makeFuture<int>(MoveOnlyException(msg));\n  EXPECT_THAT([&] { f.value(); }, ThrowsMessage<MoveOnlyException>(StrEq(msg)));\n}\n\nTEST(Future, makeFutureFromExceptionSpecifyingBothTemplateParams) {\n  using ::testing::StrEq;\n  using ::testing::ThrowsMessage;\n\n  std::string msg = \"exception message\";\n\n  auto f = makeFuture<int, std::runtime_error>(std::runtime_error(msg));\n  EXPECT_THAT(\n      [&] { f.value(); }, ThrowsMessage<std::runtime_error>(StrEq(msg)));\n}\n\nnamespace {\nauto makeValid() {\n  auto valid = makeFuture<int>(42);\n  EXPECT_TRUE(valid.valid());\n  return valid;\n}\nauto makeInvalid() {\n  auto invalid = Future<int>::makeEmpty();\n  EXPECT_FALSE(invalid.valid());\n  return invalid;\n}\n} // namespace\n\nTEST(Future, ctorPostconditionValid) {\n  // Ctors/factories that promise valid -- postcondition: valid()\n\n#define DOIT(CREATION_EXPR)    \\\n  do {                         \\\n    auto f1 = (CREATION_EXPR); \\\n    EXPECT_TRUE(f1.valid());   \\\n    auto f2 = std::move(f1);   \\\n    EXPECT_FALSE(f1.valid());  \\\n    EXPECT_TRUE(f2.valid());   \\\n  } while (false)\n\n  auto const except = std::logic_error(\"foo\");\n  auto const ewrap = folly::exception_wrapper(except);\n\n  DOIT(makeValid());\n  DOIT(Future<int>(42));\n  DOIT(Future<int>{42});\n  DOIT(Future<Unit>());\n  DOIT(Future<Unit>{});\n  DOIT(makeFuture());\n  DOIT(makeFuture(Unit{}));\n  DOIT(makeFuture<Unit>(Unit{}));\n  DOIT(makeFuture(42));\n  DOIT(makeFuture<int>(42));\n  DOIT(makeFuture<int>(except));\n  DOIT(makeFuture<int>(ewrap));\n  DOIT(makeFuture(Try<int>(42)));\n  DOIT(makeFuture<int>(Try<int>(42)));\n  DOIT(makeFuture<int>(Try<int>(ewrap)));\n\n#undef DOIT\n}\n\nTEST(Future, ctorPostconditionInvalid) {\n  // Ctors/factories that promise invalid -- postcondition: !valid()\n\n#define DOIT(CREATION_EXPR)    \\\n  do {                         \\\n    auto f1 = (CREATION_EXPR); \\\n    EXPECT_FALSE(f1.valid());  \\\n    auto f2 = std::move(f1);   \\\n    EXPECT_FALSE(f1.valid());  \\\n    EXPECT_FALSE(f2.valid());  \\\n  } while (false)\n\n  DOIT(makeInvalid());\n  DOIT(Future<int>::makeEmpty());\n\n#undef DOIT\n}\n\nTEST(Future, lacksPreconditionValid) {\n  // Ops that don't throw FutureInvalid if !valid() --\n  // without precondition: valid()\n\n#define DOIT(STMT)         \\\n  do {                     \\\n    auto f = makeValid();  \\\n    {                      \\\n      STMT;                \\\n    }                      \\\n    copy(std::move(f));    \\\n    EXPECT_NO_THROW(STMT); \\\n  } while (false)\n\n  // .valid() itself\n  DOIT(f.valid());\n\n  // move-ctor - move-copy to local, copy(), pass-by-move-value\n  DOIT(auto other = std::move(f));\n  DOIT(copy(std::move(f)));\n  DOIT(([](auto) {})(std::move(f)));\n\n  // move-assignment into either {valid | invalid}\n  DOIT({\n    auto other = makeValid();\n    other = std::move(f);\n  });\n  DOIT({\n    auto other = makeInvalid();\n    other = std::move(f);\n  });\n\n#undef DOIT\n}\n\nTEST(Future, hasPreconditionValid) {\n  // Ops that require validity; precondition: valid();\n  // throw FutureInvalid if !valid()\n\n#define DOIT(STMT)                     \\\n  do {                                 \\\n    auto f = makeValid();              \\\n    EXPECT_NO_THROW(STMT);             \\\n    copy(std::move(f));                \\\n    EXPECT_THROW(STMT, FutureInvalid); \\\n  } while (false)\n\n  DOIT(f.isReady());\n  DOIT(f.result());\n  DOIT(std::move(f).getTry());\n  DOIT(std::move(f).get());\n  DOIT(std::move(f).get(std::chrono::milliseconds(10)));\n  DOIT(f.hasValue());\n  DOIT(f.hasException());\n  DOIT(f.value());\n  DOIT(f.poll());\n  DOIT(std::move(f).then());\n  DOIT(std::move(f).thenValue([](auto&&) {}));\n\n#undef DOIT\n}\n\nTEST(Future, hasPostconditionValid) {\n  // Ops that preserve validity -- postcondition: valid()\n\n#define DOIT(STMT)          \\\n  do {                      \\\n    auto f = makeValid();   \\\n    EXPECT_NO_THROW(STMT);  \\\n    EXPECT_TRUE(f.valid()); \\\n  } while (false)\n\n  auto const swallow = [](auto) {};\n\n  DOIT(swallow(f.valid())); // f.valid() itself preserves validity\n  DOIT(swallow(f.isReady()));\n  DOIT(swallow(f.hasValue()));\n  DOIT(swallow(f.hasException()));\n  DOIT(swallow(f.value()));\n  DOIT(swallow(f.poll()));\n  DOIT(f.raise(std::logic_error(\"foo\")));\n  DOIT(f.cancel());\n  DOIT(f.wait());\n  DOIT(std::move(f).wait());\n\n#undef DOIT\n}\n\nTEST(Future, hasPostconditionInvalid) {\n  // Ops that consume *this -- postcondition: !valid()\n\n#define DOIT(CTOR, STMT)     \\\n  do {                       \\\n    auto f = (CTOR);         \\\n    EXPECT_NO_THROW(STMT);   \\\n    EXPECT_FALSE(f.valid()); \\\n  } while (false)\n\n  // move-ctor of {valid|invalid}\n  DOIT(makeValid(), { auto other{std::move(f)}; });\n  DOIT(makeInvalid(), { auto other{std::move(f)}; });\n\n  // move-assignment of {valid|invalid} into {valid|invalid}\n  DOIT(makeValid(), {\n    auto other = makeValid();\n    other = std::move(f);\n  });\n  DOIT(makeValid(), {\n    auto other = makeInvalid();\n    other = std::move(f);\n  });\n  DOIT(makeInvalid(), {\n    auto other = makeValid();\n    other = std::move(f);\n  });\n  DOIT(makeInvalid(), {\n    auto other = makeInvalid();\n    other = std::move(f);\n  });\n\n  // pass-by-value of {valid|invalid}\n  DOIT(makeValid(), {\n    auto const byval = [](auto) {};\n    byval(std::move(f));\n  });\n  DOIT(makeInvalid(), {\n    auto const byval = [](auto) {};\n    byval(std::move(f));\n  });\n\n  // other consuming ops\n  auto const swallow = [](auto) {};\n  DOIT(makeValid(), swallow(std::move(f).wait()));\n  DOIT(makeValid(), swallow(std::move(f.wait())));\n  DOIT(makeValid(), swallow(std::move(f).get()));\n  DOIT(makeValid(), swallow(std::move(f).getTry()));\n  DOIT(makeValid(), swallow(std::move(f).get(std::chrono::milliseconds(10))));\n  DOIT(makeValid(), swallow(std::move(f).semi()));\n\n#undef DOIT\n}\n\nnamespace {\nFuture<int> thenErrorHelperEggs(const eggs_t&) {\n  return makeFuture(10);\n}\nFuture<int> thenErrorHelperGeneric(const std::exception&) {\n  return makeFuture(20);\n}\nFuture<int> thenErrorHelperWrapper(folly::exception_wrapper&&) {\n  return makeFuture(30);\n}\n} // namespace\n\nTEST(Future, thenError) {\n  bool theFlag = false;\n  auto flag = [&] { theFlag = true; };\n#define EXPECT_FLAG()     \\\n  do {                    \\\n    EXPECT_TRUE(theFlag); \\\n    theFlag = false;      \\\n  } while (0)\n\n#define EXPECT_NO_FLAG()   \\\n  do {                     \\\n    EXPECT_FALSE(theFlag); \\\n    theFlag = false;       \\\n  } while (0)\n\n  // By reference\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) { flag(); });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  // By value\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto /* e */) { flag(); });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto /* e */) {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  // Polymorphic\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<std::exception>{}, [&](auto&& /* e */) {\n              flag();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<std::exception>{}, [&](auto&& /* e */) {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  // Non-exceptions\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw -1; })\n            .thenError(folly::tag_t<int>{}, [&](int /* e */) { flag(); });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw -1; })\n            .thenError(folly::tag_t<int>{}, [&](int /* e */) {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  // Mutable lambda\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) mutable {\n              flag();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) mutable {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  // Function pointer\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs)\n            .thenError(folly::tag_t<std::exception>{}, thenErrorHelperGeneric);\n    EXPECT_EQ(10, f.value());\n  }\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw std::runtime_error(\"test\"); })\n            .thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs)\n            .thenError(folly::tag_t<std::exception>{}, thenErrorHelperGeneric);\n    EXPECT_EQ(20, f.value());\n  }\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw std::runtime_error(\"test\"); })\n            .thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs);\n    EXPECT_THROW(f.value(), std::runtime_error);\n  }\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError(tag_t<eggs_t>{}, thenErrorHelperEggs)\n            .thenError<std::exception>(thenErrorHelperGeneric);\n    EXPECT_EQ(10, f.value());\n  }\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw std::runtime_error(\"test\"); })\n            .thenError(tag_t<eggs_t>{}, thenErrorHelperEggs)\n            .thenError(thenErrorHelperWrapper);\n    EXPECT_EQ(30, f.value());\n  }\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw std::runtime_error(\"test\"); })\n            .thenError(tag_t<eggs_t>{}, thenErrorHelperEggs);\n    EXPECT_THROW(f.value(), std::runtime_error);\n  }\n\n  // No throw\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { return 42; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {\n              flag();\n              return -1;\n            });\n    EXPECT_NO_FLAG();\n    EXPECT_EQ(42, f.value());\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { return 42; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {\n              flag();\n              return makeFuture<int>(-1);\n            });\n    EXPECT_NO_FLAG();\n    EXPECT_EQ(42, f.value());\n  }\n\n  // Catch different exception\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<std::runtime_error>{}, [&](auto&& /* e */) {\n              flag();\n            });\n    EXPECT_NO_FLAG();\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError(folly::tag_t<std::runtime_error>{}, [&](auto&& /* e */) {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_NO_FLAG();\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n\n  // Returned value propagates\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {\n              return 42;\n            });\n    EXPECT_EQ(42, f.value());\n  }\n\n  // Returned future propagates\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {\n              return makeFuture<int>(42);\n            });\n    EXPECT_EQ(42, f.value());\n  }\n\n  // Throw in callback\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& e) -> int {\n              throw std::move(e);\n            });\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError(folly::tag_t<eggs_t>{}, [&](auto&& e) -> Future<int> {\n              throw std::move(e);\n            });\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n\n  // exception_wrapper, return Future<T>\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError([&](exception_wrapper /* e */) {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n\n  // exception_wrapper, return Future<T> but throw\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError([&](exception_wrapper /* e */) -> Future<int> {\n              flag();\n              throw eggs;\n            });\n    EXPECT_FLAG();\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n\n  // exception_wrapper, return T\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError([&](exception_wrapper /* e */) {\n              flag();\n              return -1;\n            });\n    EXPECT_FLAG();\n    EXPECT_EQ(-1, f.value());\n  }\n\n  // exception_wrapper, return T but throw\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) -> int { throw eggs; })\n            .thenError([&](exception_wrapper /* e */) -> int {\n              flag();\n              throw eggs;\n            });\n    EXPECT_FLAG();\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n\n  // const exception_wrapper&\n  {\n    auto f =\n        makeFuture()\n            .thenValue([](auto&&) { throw eggs; })\n            .thenError([&](const exception_wrapper& /* e */) {\n              flag();\n              return makeFuture();\n            });\n    EXPECT_FLAG();\n    EXPECT_NO_THROW(f.value());\n  }\n#undef EXPECT_FLAG\n#undef EXPECT_NO_FLAG\n}\n\nTEST(Future, special) {\n  EXPECT_FALSE(std::is_copy_constructible<Future<int>>::value);\n  EXPECT_FALSE(std::is_copy_assignable<Future<int>>::value);\n  EXPECT_TRUE(std::is_move_constructible<Future<int>>::value);\n  EXPECT_TRUE(std::is_move_assignable<Future<int>>::value);\n}\n\nTEST(Future, then) {\n  auto f =\n      makeFuture<std::string>(\"0\")\n          .thenValue([](auto&&) { return makeFuture<std::string>(\"1\"); })\n          .then([](Try<std::string>&& t) {\n            return makeFuture(t.value() + \";2\");\n          })\n          .then([](const Try<std::string>&& t) {\n            return makeFuture(t.value() + \";3\");\n          })\n          .then([](const Try<std::string>& t) {\n            return makeFuture(t.value() + \";4\");\n          })\n          .then([](Try<std::string> t) { return makeFuture(t.value() + \";5\"); })\n          .then([](const Try<std::string> t) {\n            return makeFuture(t.value() + \";6\");\n          })\n          .thenValue([](std::string&& s) { return makeFuture(s + \";7\"); })\n          .thenValue([](const std::string&& s) { return makeFuture(s + \";8\"); })\n          .thenValue([](const std::string& s) { return makeFuture(s + \";9\"); })\n          .thenValue([](std::string s) { return makeFuture(s + \";10\"); })\n          .thenValue([](const std::string s) { return makeFuture(s + \";11\"); });\n  EXPECT_EQ(f.value(), \"1;2;3;4;5;6;7;8;9;10;11\");\n}\n\nstatic folly::Future<std::string> doWorkStaticTry(Try<std::string>&& t) {\n  return makeFuture(t.value() + \";7\");\n}\n\nTEST(Future, thenTrythenValue) {\n  auto f =\n      makeFuture()\n          .thenTry([](auto&&) { return makeFuture<std::string>(\"1\"); })\n          .thenTry([](Try<std::string>&& t) {\n            return makeFuture(t.value() + \";2\");\n          })\n          .thenTry([](const Try<std::string>&& t) {\n            return makeFuture(t.value() + \";3\");\n          })\n          .thenTry([](const Try<std::string>& t) {\n            return makeFuture(t.value() + \";4\");\n          })\n          .thenTry([](Try<std::string> t) {\n            return makeFuture(t.value() + \";5\");\n          })\n          .thenTry([](const Try<std::string> t) {\n            return makeFuture(t.value() + \";6\");\n          })\n          .thenTry(doWorkStaticTry)\n          .thenValue([](std::string&& s) { return makeFuture(s + \";8\"); })\n          .thenValue([](const std::string&& s) { return makeFuture(s + \";9\"); })\n          .thenValue([](const std::string& s) { return makeFuture(s + \";10\"); })\n          .thenValue([](std::string s) { return makeFuture(s + \";11\"); })\n          .thenValue([](const std::string s) { return makeFuture(s + \";12\"); });\n  EXPECT_EQ(f.value(), \"1;2;3;4;5;6;7;8;9;10;11;12\");\n}\n\nTEST(Future, thenTry) {\n  bool flag = false;\n\n  makeFuture<int>(42).then([&](Try<int>&& t) {\n    flag = true;\n    EXPECT_EQ(42, t.value());\n  });\n  EXPECT_TRUE(flag);\n  flag = false;\n\n  makeFuture<int>(42)\n      .then([](Try<int>&& t) { return t.value(); })\n      .then([&](Try<int>&& t) {\n        flag = true;\n        EXPECT_EQ(42, t.value());\n      });\n  EXPECT_TRUE(flag);\n  flag = false;\n\n  makeFuture().then([&](Try<Unit>&& t) {\n    flag = true;\n    t.value();\n  });\n  EXPECT_TRUE(flag);\n  flag = false;\n\n  Promise<Unit> p;\n  auto f = p.getFuture().then([&](Try<Unit>&& /* t */) { flag = true; });\n  EXPECT_FALSE(flag);\n  EXPECT_FALSE(f.isReady());\n  p.setValue();\n  EXPECT_TRUE(flag);\n  EXPECT_TRUE(f.isReady());\n}\n\nTEST(Future, ThenTryWithExecutor) {\n  ManualExecutor executor;\n  auto sf = makeFuture().via(&executor).thenExTry(\n      [&](const Executor::KeepAlive<>& e, Try<Unit>) {\n        EXPECT_EQ(&executor, e.get());\n      });\n  std::move(sf).getVia(&executor);\n}\n\nTEST(Future, thenValue) {\n  bool flag = false;\n  makeFuture<int>(42).thenValue([&](int i) {\n    EXPECT_EQ(42, i);\n    flag = true;\n  });\n  EXPECT_TRUE(flag);\n  flag = false;\n\n  makeFuture<int>(42).thenValue([](int i) { return i; }).thenValue([&](int i) {\n    flag = true;\n    EXPECT_EQ(42, i);\n  });\n  EXPECT_TRUE(flag);\n  flag = false;\n\n  makeFuture().thenValue([&](auto&&) { flag = true; });\n  EXPECT_TRUE(flag);\n  flag = false;\n\n  auto f = makeFuture<int>(eggs).thenValue([&](int /* i */) {});\n  EXPECT_THROW(f.value(), eggs_t);\n\n  f = makeFuture<Unit>(eggs).thenValue([&](auto&&) {});\n  EXPECT_THROW(f.value(), eggs_t);\n}\n\nTEST(Future, ThenValueWithExecutor) {\n  ManualExecutor executor;\n  auto sf = makeFuture(42).via(&executor).thenExValue(\n      [&](const Executor::KeepAlive<>& e, int val) {\n        EXPECT_EQ(&executor, e.get());\n        EXPECT_EQ(val, 42);\n      });\n  std::move(sf).getVia(&executor);\n}\n\nTEST(Future, thenValueFuture) {\n  bool flag = false;\n  makeFuture<int>(42)\n      .thenValue([](int i) { return makeFuture<int>(std::move(i)); })\n      .then([&](Try<int>&& t) {\n        flag = true;\n        EXPECT_EQ(42, t.value());\n      });\n  EXPECT_TRUE(flag);\n  flag = false;\n\n  makeFuture()\n      .thenValue([](auto&&) { return makeFuture(); })\n      .then([&](Try<Unit>&& /* t */) { flag = true; });\n  EXPECT_TRUE(flag);\n  flag = false;\n}\n\nstatic std::string doWorkStatic(Try<std::string>&& t) {\n  return t.value() + \";static\";\n}\n\nstatic std::string doWorkStaticValue(std::string&& t) {\n  return t + \";value\";\n}\n\nTEST(Future, thenFunction) {\n  struct Worker {\n    std::string doWork(Try<std::string>&& t) { return t.value() + \";class\"; }\n    static std::string doWorkStatic(Try<std::string>&& t) {\n      return t.value() + \";class-static\";\n    }\n  } w;\n\n  auto f =\n      makeFuture<std::string>(\"start\")\n          .then(doWorkStatic)\n          .then(Worker::doWorkStatic)\n          .then(&Worker::doWork, &w)\n          .thenValue(doWorkStaticValue);\n\n  EXPECT_EQ(f.value(), \"start;static;class-static;class;value\");\n}\n\nstatic Future<std::string> doWorkStaticFuture(Try<std::string>&& t) {\n  return makeFuture(t.value() + \";static\");\n}\n\nTEST(Future, thenFunctionFuture) {\n  struct Worker {\n    Future<std::string> doWorkFuture(Try<std::string>&& t) {\n      return makeFuture(t.value() + \";class\");\n    }\n    static Future<std::string> doWorkStaticFuture(Try<std::string>&& t) {\n      return makeFuture(t.value() + \";class-static\");\n    }\n  } w;\n\n  auto f =\n      makeFuture<std::string>(\"start\")\n          .then(doWorkStaticFuture)\n          .then(Worker::doWorkStaticFuture)\n          .then(&Worker::doWorkFuture, &w);\n\n  EXPECT_EQ(f.value(), \"start;static;class-static;class\");\n}\n\nTEST(Future, thenStdFunction) {\n  {\n    std::function<int(folly::Unit)> fn = [](folly::Unit) { return 42; };\n    auto f = makeFuture().thenValue(std::move(fn));\n    EXPECT_EQ(f.value(), 42);\n  }\n  {\n    std::function<int(int)> fn = [](int i) { return i + 23; };\n    auto f = makeFuture(19).thenValue(std::move(fn));\n    EXPECT_EQ(f.value(), 42);\n  }\n  {\n    std::function<int(Try<int>)> fn = [](Try<int> t) { return t.value() + 2; };\n    auto f = makeFuture(1).then(std::move(fn));\n    EXPECT_EQ(f.value(), 3);\n  }\n  {\n    bool flag = false;\n    std::function<void(folly::Unit)> fn = [&flag](folly::Unit) { flag = true; };\n    auto f = makeFuture().thenValue(std::move(fn));\n    EXPECT_TRUE(f.isReady());\n    EXPECT_TRUE(flag);\n  }\n}\n\nTEST(Future, thenBind) {\n  auto l = [](folly::Unit) { return makeFuture(\"bind\"); };\n  auto b = std::bind(l, std::placeholders::_1);\n  auto f = makeFuture().thenValue(std::move(b));\n  EXPECT_EQ(f.value(), \"bind\");\n}\n\nTEST(Future, thenBindTry) {\n  auto l = [](Try<std::string>&& t) { return makeFuture(t.value() + \";bind\"); };\n  auto b = std::bind(l, std::placeholders::_1);\n  auto f = makeFuture<std::string>(\"start\").then(std::move(b));\n\n  EXPECT_EQ(f.value(), \"start;bind\");\n}\n\nTEST(Future, value) {\n  auto f = makeFuture(std::make_unique<int>(42));\n  auto up = std::move(f.value());\n  EXPECT_EQ(42, *up);\n\n  EXPECT_THROW(makeFuture<int>(eggs).value(), eggs_t);\n}\n\nTEST(Future, isReady) {\n  Promise<int> p;\n  auto f = p.getFuture();\n  EXPECT_FALSE(f.isReady());\n  p.setValue(42);\n  EXPECT_TRUE(f.isReady());\n}\n\nTEST(Future, futureNotReady) {\n  Promise<int> p;\n  Future<int> f = p.getFuture();\n  EXPECT_THROW(f.value(), eggs_t);\n}\n\nTEST(Future, hasException) {\n  EXPECT_TRUE(makeFuture<int>(eggs).getTry().hasException());\n  EXPECT_FALSE(makeFuture(42).getTry().hasException());\n}\n\nTEST(Future, hasValue) {\n  EXPECT_TRUE(makeFuture(42).getTry().hasValue());\n  EXPECT_FALSE(makeFuture<int>(eggs).getTry().hasValue());\n}\n\nTEST(Future, makeFuture) {\n  EXPECT_TYPE(makeFuture(42), Future<int>);\n  EXPECT_EQ(42, makeFuture(42).value());\n\n  EXPECT_TYPE(makeFuture<float>(42), Future<float>);\n  EXPECT_EQ(42, makeFuture<float>(42).value());\n\n  auto fun = [] { return 42; };\n  EXPECT_TYPE(makeFutureWith(fun), Future<int>);\n  EXPECT_EQ(42, makeFutureWith(fun).value());\n\n  auto funf = [] { return makeFuture<int>(43); };\n  EXPECT_TYPE(makeFutureWith(funf), Future<int>);\n  EXPECT_EQ(43, makeFutureWith(funf).value());\n\n  auto failfun = []() -> int { throw eggs; };\n  EXPECT_TYPE(makeFutureWith(failfun), Future<int>);\n  EXPECT_NO_THROW(makeFutureWith(failfun));\n  EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t);\n\n  auto failfunf = []() -> Future<int> { throw eggs; };\n  EXPECT_TYPE(makeFutureWith(failfunf), Future<int>);\n  EXPECT_NO_THROW(makeFutureWith(failfunf));\n  EXPECT_THROW(makeFutureWith(failfunf).value(), eggs_t);\n\n  EXPECT_TYPE(makeFuture(), Future<Unit>);\n}\n\nTEST(Future, finish) {\n  auto x = std::make_shared<int>(0);\n\n  Promise<int> p;\n  auto f = p.getFuture().then([x](Try<int>&& t) { *x = t.value(); });\n\n  // The callback hasn't executed\n  EXPECT_EQ(0, *x);\n\n  // The callback has a reference to x\n  EXPECT_EQ(2, x.use_count());\n\n  p.setValue(42);\n\n  // the callback has executed\n  EXPECT_EQ(42, *x);\n\n  // the callback has been destructed\n  // and has released its reference to x\n  EXPECT_EQ(1, x.use_count());\n}\n\nTEST(Future, finishBigLambda) {\n  auto x = std::make_shared<int>(0);\n\n  // bulk_data, to be captured in the lambda passed to Future::then.\n  // This is meant to force that the lambda can't be stored inside\n  // the Future object.\n  std::array<char, sizeof(futures::detail::Core<int>)> bulk_data = {{0}};\n\n  // suppress gcc warning about bulk_data not being used\n  EXPECT_EQ(bulk_data[0], 0);\n\n  Promise<int> p;\n  auto f = p.getFuture().then([x, bulk_data](Try<int>&& t) {\n    (void)bulk_data;\n    *x = t.value();\n  });\n\n  // The callback hasn't executed\n  EXPECT_EQ(0, *x);\n\n  // The callback has a reference to x\n  EXPECT_EQ(2, x.use_count());\n\n  p.setValue(42);\n\n  // the callback has executed\n  EXPECT_EQ(42, *x);\n\n  // the callback has been destructed\n  // and has released its reference to x\n  EXPECT_EQ(1, x.use_count());\n}\n\nTEST(Future, unwrap) {\n  Promise<int> a;\n  Promise<int> b;\n\n  auto fa = a.getFuture();\n  auto fb = b.getFuture();\n\n  bool flag1 = false;\n  bool flag2 = false;\n\n  // do a, then do b, and get the result of a + b.\n  Future<int> f = std::move(fa).then([&](Try<int>&& ta) {\n    auto va = ta.value();\n    flag1 = true;\n    return std::move(fb).then([va, &flag2](Try<int>&& tb) {\n      flag2 = true;\n      return va + tb.value();\n    });\n  });\n\n  EXPECT_FALSE(flag1);\n  EXPECT_FALSE(flag2);\n  EXPECT_FALSE(f.isReady());\n\n  a.setValue(3);\n  EXPECT_TRUE(flag1);\n  EXPECT_FALSE(flag2);\n  EXPECT_FALSE(f.isReady());\n\n  b.setValue(4);\n  EXPECT_TRUE(flag1);\n  EXPECT_TRUE(flag2);\n  EXPECT_EQ(7, f.value());\n}\n\nTEST(Future, throwCaughtInImmediateThen) {\n  // Neither of these should throw \"Promise already satisfied\"\n  makeFuture().then([=](Try<Unit>&&) -> int { throw std::exception(); });\n  makeFuture().then([=](Try<Unit>&&) -> Future<int> {\n    throw std::exception();\n  });\n}\n\nTEST(Future, throwIfFailed) {\n  makeFuture<Unit>(eggs).then([=](Try<Unit>&& t) {\n    EXPECT_THROW(t.throwUnlessValue(), eggs_t);\n  });\n  makeFuture().then([=](Try<Unit>&& t) {\n    EXPECT_NO_THROW(t.throwUnlessValue());\n  });\n\n  makeFuture<int>(eggs).then([=](Try<int>&& t) {\n    EXPECT_THROW(t.throwUnlessValue(), eggs_t);\n  });\n  makeFuture<int>(42).then([=](Try<int>&& t) {\n    EXPECT_NO_THROW(t.throwUnlessValue());\n  });\n}\n\nTEST(Future, getFutureAfterSetValue) {\n  Promise<int> p;\n  p.setValue(42);\n  EXPECT_EQ(42, p.getFuture().value());\n}\n\nTEST(Future, getFutureAfterSetException) {\n  Promise<Unit> p;\n  p.setWith([]() -> void { throw std::logic_error(\"foo\"); });\n  EXPECT_THROW(p.getFuture().value(), std::logic_error);\n}\n\nTEST(Future, detachRace) {\n  // Task #5438209\n  // This test is designed to detect a race that was in Core::detachOne()\n  // where detached_ was incremented and then tested, and that\n  // allowed a race where both Promise and Future would think they were the\n  // second and both try to delete. This showed up at scale but was very\n  // difficult to reliably repro in a test. As it is, this only fails about\n  // once in every 1,000 executions. Doing this 1,000 times is going to make a\n  // slow test so I won't do that but if it ever fails, take it seriously, and\n  // run the test binary with \"--gtest_repeat=10000 --gtest_filter=*detachRace\"\n  // (Don't forget to enable ASAN)\n  auto p = std::make_unique<Promise<bool>>();\n  auto f = std::make_unique<Future<bool>>(p->getFuture());\n  folly::Baton<> baton;\n  std::thread t1([&] {\n    baton.post();\n    p.reset();\n  });\n  baton.wait();\n  f.reset();\n  t1.join();\n}\n\n// Test of handling of a circular dependency. It's never recommended\n// to have one because of possible memory leaks. Here we test that\n// we can handle freeing of the Future while it is running.\nTEST(Future, CircularDependencySharedPtrSelfReset) {\n  Promise<int64_t> promise;\n  auto ptr = std::make_shared<Future<int64_t>>(promise.getFuture());\n\n  std::move(*ptr).thenTry([ptr](folly::Try<int64_t>&& /* uid */) mutable {\n    EXPECT_EQ(1, ptr.use_count());\n\n    // Leaving no references to ourselves.\n    ptr.reset();\n    EXPECT_EQ(0, ptr.use_count());\n  });\n\n  EXPECT_EQ(2, ptr.use_count());\n\n  ptr.reset();\n\n  promise.setValue(1);\n}\n\nTEST(Future, Constructor) {\n  auto f1 = []() -> Future<int> { return Future<int>(3); }();\n  EXPECT_EQ(f1.value(), 3);\n  auto f2 = []() -> Future<Unit> { return Future<Unit>(); }();\n  EXPECT_NO_THROW(f2.value());\n}\n\nTEST(Future, ImplicitConstructor) {\n  auto f1 = []() -> Future<int> { return 3; }();\n  EXPECT_EQ(f1.value(), 3);\n  // Unfortunately, the C++ standard does not allow the\n  // following implicit conversion to work:\n  // auto f2 = []() -> Future<Unit> { }();\n}\n\nTEST(Future, InPlaceConstructor) {\n  auto f = Future<std::pair<int, double>>(std::in_place, 5, 3.2);\n  EXPECT_EQ(5, f.value().first);\n}\n\nTEST(Future, thenDynamic) {\n  // folly::dynamic has a constructor that takes any T, this test makes\n  // sure that we call the then lambda with folly::dynamic and not\n  // Try<folly::dynamic> because that then fails to compile\n  Promise<folly::dynamic> p;\n  Future<folly::dynamic> f = p.getFuture().thenValue(\n      [](const folly::dynamic& d) { return folly::dynamic(d.asInt() + 3); });\n  p.setValue(2);\n  EXPECT_EQ(std::move(f).get(), 5);\n}\n\nTEST(Future, RequestContext) {\n  class NewThreadExecutor : public Executor {\n   public:\n    ~NewThreadExecutor() override {\n      std::for_each(v_.begin(), v_.end(), [](std::thread& t) { t.join(); });\n    }\n    void add(Func f) override {\n      if (throwsOnAdd_) {\n        throw std::exception();\n      }\n      v_.emplace_back(std::move(f));\n    }\n    void addWithPriority(Func f, int8_t /* prio */) override {\n      add(std::move(f));\n    }\n    uint8_t getNumPriorities() const override { return numPriorities_; }\n\n    void setHandlesPriorities() { numPriorities_ = 2; }\n    void setThrowsOnAdd() { throwsOnAdd_ = true; }\n\n   private:\n    std::vector<std::thread> v_;\n    uint8_t numPriorities_ = 1;\n    bool throwsOnAdd_ = false;\n  };\n\n  struct MyRequestData : RequestData {\n    MyRequestData(bool value_ = false) : value(value_) {}\n\n    bool hasCallback() override { return false; }\n\n    bool value;\n  };\n\n  Promise<int> p1, p2;\n  NewThreadExecutor e;\n  {\n    folly::RequestContextScopeGuard rctx;\n    RequestContext::get()->setContextData(\n        \"key\", std::make_unique<MyRequestData>(true));\n    auto checker = [](int lineno) {\n      return [lineno](Try<int>&& /* t */) {\n        auto d = static_cast<MyRequestData*>(\n            RequestContext::get()->getContextData(\"key\"));\n        EXPECT_TRUE(d && d->value) << \"on line \" << lineno;\n      };\n    };\n\n    makeFuture(1).via(&e).then(checker(__LINE__));\n\n    e.setHandlesPriorities();\n    makeFuture(2).via(&e).then(checker(__LINE__));\n\n    p1.getFuture().then(checker(__LINE__));\n\n    e.setThrowsOnAdd();\n    p2.getFuture().via(&e).then(checker(__LINE__));\n  }\n  // Assert that no RequestContext is set\n  EXPECT_FALSE(RequestContext::saveContext());\n  p1.setValue(3);\n  p2.setValue(4);\n}\n\nTEST(Future, makeFutureNoThrow) {\n  makeFuture().value();\n}\n\nTEST(Future, invokeCallbackReturningValueAsRvalue) {\n  struct Foo {\n    int operator()(int x) & { return x + 1; }\n    int operator()(int x) const& { return x + 2; }\n    int operator()(int x) && { return x + 3; }\n  };\n\n  Foo foo;\n  Foo const cfoo;\n\n  // The continuation will be forward-constructed - copied if given as & and\n  // moved if given as && - everywhere construction is required.\n  // The continuation will be invoked with the same cvref as it is passed.\n  EXPECT_EQ(101, makeFuture<int>(100).thenValue(foo).value());\n  EXPECT_EQ(202, makeFuture<int>(200).thenValue(cfoo).value());\n  EXPECT_EQ(303, makeFuture<int>(300).thenValue(Foo()).value());\n}\n\nTEST(Future, invokeCallbackReturningFutureAsRvalue) {\n  struct Foo {\n    Future<int> operator()(int x) & { return x + 1; }\n    Future<int> operator()(int x) const& { return x + 2; }\n    Future<int> operator()(int x) && { return x + 3; }\n  };\n\n  Foo foo;\n  Foo const cfoo;\n\n  // The continuation will be forward-constructed - copied if given as & and\n  // moved if given as && - everywhere construction is required.\n  // The continuation will be invoked with the same cvref as it is passed.\n  EXPECT_EQ(101, makeFuture<int>(100).thenValue(foo).value());\n  EXPECT_EQ(202, makeFuture<int>(200).thenValue(cfoo).value());\n  EXPECT_EQ(303, makeFuture<int>(300).thenValue(Foo()).value());\n}\n\nTEST(Future, futureWithinCtxCleanedUpWhenTaskFinishedInTime) {\n  // Used to track the use_count of callbackInput even outside of its scope\n  std::weak_ptr<int> target;\n  {\n    Promise<std::shared_ptr<int>> promise;\n    auto input = std::make_shared<int>(1);\n    auto longEnough = std::chrono::milliseconds(1000);\n\n    promise.getFuture()\n        .within(longEnough)\n        .then([&target](\n                  folly::Try<std::shared_ptr<int>>&& callbackInput) mutable {\n          target = callbackInput.value();\n        });\n    promise.setValue(input);\n  }\n  // After promise's life cycle is finished, make sure no one is holding the\n  // input anymore, in other words, ctx should have been cleaned up.\n  EXPECT_EQ(0, target.use_count());\n}\n\nTEST(Future, futureWithinNoValueReferenceWhenTimeOut) {\n  Promise<std::shared_ptr<int>> promise;\n  auto veryShort = std::chrono::milliseconds(1);\n\n  promise.getFuture().within(veryShort).then(\n      [](folly::Try<std::shared_ptr<int>>&& callbackInput) {\n        // Timeout is fired. Verify callbackInput is not referenced\n        EXPECT_EQ(0, callbackInput.value().use_count());\n      });\n}\n\nTEST(Future, makePromiseContract) {\n  class ManualExecutor : public Executor {\n   private:\n    std::queue<Func> queue_;\n\n   public:\n    void add(Func f) override { queue_.push(std::move(f)); }\n    void drain() {\n      while (!queue_.empty()) {\n        auto f = std::move(queue_.front());\n        queue_.pop();\n        f();\n      }\n    }\n  };\n\n  ManualExecutor e;\n  auto [p, f] = makePromiseContract<int>(&e);\n  f = std::move(f).thenValue([](int _) { return _ + 1; });\n  EXPECT_FALSE(f.isReady());\n  p.setValue(3);\n  EXPECT_FALSE(f.isReady());\n  e.drain();\n  ASSERT_TRUE(f.isReady());\n  EXPECT_EQ(4, std::move(f).get());\n}\n\nTEST(Future, ThenRecursion) {\n  struct Helpers {\n    static Future<bool> call(int depth, Executor* executor) {\n      return makeFuture().thenValueInline(makeAsyncTask(executor, [=](auto&&) {\n        return depth == 0;\n      }));\n    }\n\n    static Future<int> recursion(Executor* executor, int depth) {\n      return makeFuture().thenValue([=](auto) {\n        return call(depth, executor).thenValue([=](auto result) {\n          if (result) {\n            return folly::makeFuture(42);\n          }\n\n          return recursion(executor, depth - 1);\n        });\n      });\n    }\n  };\n\n  ManualExecutor executor;\n\n  EXPECT_EQ(42, Helpers::recursion(&executor, 100000).getVia(&executor));\n}\n\n// We want to detect if the Try value is being dereferenced before being\n// checked for validity. The only way to do that is with a custom Try impl.\nstruct NoThrowTestResult {};\nnamespace folly {\n// Forward all methods except throwIfFailed().\ntemplate <>\nclass Try<NoThrowTestResult> : public Try<void> {\n public:\n  using Try<void>::Try;\n\n  NoThrowTestResult value_;\n\n  explicit Try(const NoThrowTestResult&) : Try<void>() {}\n\n  NoThrowTestResult const& value() const& {\n    throwIfFailed();\n    return value_;\n  }\n  NoThrowTestResult const&& value() const&& {\n    throwIfFailed();\n    return std::move(value_);\n  }\n  NoThrowTestResult& value() & {\n    throwIfFailed();\n    return value_;\n  }\n  NoThrowTestResult&& value() && {\n    throwIfFailed();\n    return std::move(value_);\n  }\n\n  // If the Try contains an exception, throws it\n  inline void throwIfFailed() const {\n    EXPECT_FALSE(this->hasException())\n        << \"throwIfFailed() should never have been invoked.\";\n    Try<void>::throwIfFailed();\n  }\n\n  template <bool isTry, typename R>\n  typename std::enable_if<isTry, R>::type get() {\n    return std::forward<R>(*this);\n  }\n\n  template <bool isTry, typename R>\n  typename std::enable_if<!isTry, R>::type get() {\n    return std::forward<R>(value());\n  }\n};\n} // namespace folly\n\nTEST(Future, NoThrow) {\n  // Test that the Futures implementation never invokes c++ throw, by\n  // accessing the value without first checking whether the value exists.\n  const std::string kErrorMessage = \"NoThrow test\";\n  // Test thenValue\n  {\n    Try<NoThrowTestResult> t =\n        Future<NoThrowTestResult>(std::runtime_error(kErrorMessage))\n            .thenValue([](NoThrowTestResult&& value) {\n              ADD_FAILURE() << \"This code should be unreachable\";\n              return std::move(value);\n            })\n            .result();\n\n    EXPECT_TRUE(t.hasException());\n    EXPECT_EQ(t.exception().get_exception()->what(), kErrorMessage);\n  }\n\n  // Test deferValue\n  {\n    Try<NoThrowTestResult> t =\n        SemiFuture<NoThrowTestResult>(std::runtime_error(kErrorMessage))\n            .deferValue([](NoThrowTestResult&& value) {\n              ADD_FAILURE() << \"This code should be unreachable\";\n              return std::move(value);\n            })\n            .via(&InlineExecutor::instance())\n            .result();\n\n    EXPECT_TRUE(t.hasException());\n    EXPECT_EQ(t.exception().get_exception()->what(), kErrorMessage);\n  }\n}\n\nTEST(Future, DetachTest) {\n  folly::Baton<> b1, b2, b3, b4;\n  folly::ManualExecutor exec;\n  std::atomic<int> result(0);\n\n  folly::futures::detachOn(&exec, makeSemiFuture().deferValue([&](auto&&) {\n    result++;\n    b1.post();\n  }));\n\n  folly::futures::detachOnGlobalCPUExecutor(\n      makeSemiFuture().deferValue([&](auto&&) {\n        result++;\n        b2.post();\n      }));\n\n  // This will run correctly, but cleanly detach because we know for sure\n  // this is safe\n  folly::futures::detachWithoutExecutor( //\n      makeSemiFuture()\n          .via(&exec)\n          .thenValue([&](auto&&) {\n            result++;\n            b3.post();\n          })\n          .semi());\n\n  folly::futures::maybeDetachOnGlobalExecutorAfter(\n      std::chrono::milliseconds{100}, makeSemiFuture().deferValue([&](auto&&) {\n        result++;\n        b4.post();\n      }));\n\n  exec.drain();\n  b1.wait();\n  b2.wait();\n  b3.wait();\n  b4.wait();\n  EXPECT_TRUE(result == 4);\n}\n\nTEST(Future, SimpleGet) {\n  Promise<int> p;\n  auto sf = p.getFuture();\n  p.setValue(3);\n  auto v = std::move(sf).get();\n  ASSERT_EQ(v, 3);\n}\n\nTEST(Future, SimpleGetTry) {\n  Promise<int> p;\n  auto sf = p.getFuture();\n  p.setValue(3);\n  auto v = std::move(sf).getTry();\n  ASSERT_EQ(v.value(), 3);\n}\n\nTEST(Future, SimpleTimedGet) {\n  Promise<folly::Unit> p;\n  auto sf = p.getFuture();\n  EXPECT_THROW(\n      std::move(sf).get(std::chrono::milliseconds(100)), FutureTimeout);\n}\n\nTEST(Future, SimpleTimedGetTry) {\n  Promise<folly::Unit> p;\n  auto sf = p.getFuture();\n  EXPECT_THROW(\n      std::move(sf).getTry(std::chrono::milliseconds(100)), FutureTimeout);\n}\n\nTEST(Future, BatonWait) {\n  auto baton = std::make_unique<fibers::Baton>();\n  bool posted{false};\n\n  ManualExecutor executor;\n  auto postFuture =\n      futures::sleep(std::chrono::milliseconds{100})\n          .via(&executor)\n          .thenValue([&posted, batonPtr = baton.get()](auto) {\n            posted = true;\n            batonPtr->post();\n          });\n  futures::wait(std::move(baton))\n      .via(&executor)\n      .thenValue([&](auto) { EXPECT_TRUE(posted); })\n      .getVia(&executor);\n  EXPECT_TRUE(postFuture.isReady());\n}\n"
  },
  {
    "path": "folly/futures/test/HeaderCompileTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// amazing what things can go wrong if you include things in an unexpected\n// order.\n#include <folly/Try.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nTEST(Basic, compiles) {\n  SUCCEED();\n}\n"
  },
  {
    "path": "folly/futures/test/HeapTimekeeperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/HeapTimekeeper.h>\n#include <folly/futures/test/TimekeeperTestLib.h>\n\nnamespace folly {\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    HeapTimekeeperTest, TimekeeperTest, HeapTimekeeper);\n\nTEST(TimekeeperSingletonTest, ExpectedType) {\n  // This is just to check that the un-mocked default timekeeper singleton\n  // Implementation is covered by some instantiation of the test suite. If the\n  // default implementation is changed this test should be moved accordingly.\n  ASSERT_TRUE(\n      dynamic_cast<HeapTimekeeper*>(detail::getTimekeeperSingleton().get()) !=\n      nullptr);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/InterruptTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n#include <folly/futures/test/TestExecutor.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nTEST(Interrupt, raise) {\n  using eggs_t = std::runtime_error;\n  Promise<Unit> p;\n  p.setInterruptHandler([&](const exception_wrapper& e) {\n    EXPECT_THROW(e.throw_exception(), eggs_t);\n  });\n  p.getFuture().raise(eggs_t(\"eggs\"));\n}\n\nTEST(Interrupt, cancel) {\n  Promise<Unit> p;\n  p.setInterruptHandler([&](const exception_wrapper& e) {\n    EXPECT_THROW(e.throw_exception(), FutureCancellation);\n  });\n  p.getFuture().cancel();\n}\n\nTEST(Interrupt, handleThenInterrupt) {\n  Promise<int> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getFuture().cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, interruptThenHandle) {\n  Promise<int> p;\n  bool flag = false;\n  p.getFuture().cancel();\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, interruptAfterFulfilNoop) {\n  Promise<Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.setValue();\n  p.getFuture().cancel();\n  EXPECT_FALSE(flag);\n}\n\nTEST(Interrupt, secondInterruptNoop) {\n  Promise<Unit> p;\n  int count = 0;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { count++; });\n  auto f = p.getFuture();\n  f.cancel();\n  f.cancel();\n  EXPECT_EQ(1, count);\n}\n\nTEST(Interrupt, futureWithinTimedOut) {\n  Promise<int> p;\n  Baton<> done;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { done.post(); });\n  p.getFuture().within(std::chrono::milliseconds(1));\n  // Give it 100ms to time out and call the interrupt handler\n  EXPECT_TRUE(done.try_wait_for(std::chrono::milliseconds(100)));\n}\n\nTEST(Interrupt, semiFutureWithinTimedOut) {\n  folly::TestExecutor ex(1);\n  Promise<int> p;\n  Baton<> done;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { done.post(); });\n  p.getSemiFuture().within(std::chrono::milliseconds(1)).via(&ex);\n  // Give it 100ms to time out and call the interrupt handler\n  EXPECT_TRUE(done.try_wait_for(std::chrono::milliseconds(100)));\n}\n\nTEST(Interrupt, futureThenValue) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getFuture().thenValue([](folly::Unit&&) {}).cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, semiFutureDeferValue) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getSemiFuture().deferValue([](folly::Unit&&) {}).cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, futureThenValueFuture) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getFuture()\n      .thenValue([](folly::Unit&&) { return folly::makeFuture(); })\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, semiFutureDeferValueSemiFuture) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getSemiFuture()\n      .deferValue([](folly::Unit&&) { return folly::makeSemiFuture(); })\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, futureThenError) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getFuture().thenError([](const exception_wrapper& /* e */) {}).cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, semiFutureDeferError) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getSemiFuture()\n      .deferError([](const exception_wrapper& /* e */) {})\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, futureThenErrorTagged) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getFuture()\n      .thenError(\n          folly::tag_t<std::runtime_error>{},\n          [](const exception_wrapper& /* e */) {})\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, semiFutureDeferErrorTagged) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getSemiFuture()\n      .deferError(\n          folly::tag_t<std::runtime_error>{},\n          [](const exception_wrapper& /* e */) {})\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, futureThenErrorFuture) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getFuture()\n      .thenError([](const exception_wrapper& /* e */) { return makeFuture(); })\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, semiFutureDeferErrorSemiFuture) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getSemiFuture()\n      .deferError([](const exception_wrapper& /* e */) {\n        return makeSemiFuture();\n      })\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, futureThenErrorTaggedFuture) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getFuture()\n      .thenError(\n          folly::tag_t<std::runtime_error>{},\n          [](const exception_wrapper& /* e */) { return makeFuture(); })\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(Interrupt, semiFutureDeferErrorTaggedSemiFuture) {\n  folly::TestExecutor ex(1);\n  Promise<folly::Unit> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper& /* e */) { flag = true; });\n  p.getSemiFuture()\n      .deferError(\n          folly::tag_t<std::runtime_error>{},\n          [](const exception_wrapper& /* e */) { return makeSemiFuture(); })\n      .cancel();\n  EXPECT_TRUE(flag);\n}\n"
  },
  {
    "path": "folly/futures/test/ManualTimekeeperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/ManualTimekeeper.h>\n\n#include <chrono>\n\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals;\n\nnamespace folly {\n\nclass ManualTimekeeperTest : public ::testing::Test {};\n\nTEST_F(ManualTimekeeperTest, Basic) {\n  auto timekeeper = folly::ManualTimekeeper{};\n  auto future = timekeeper.after(100s);\n  timekeeper.advance(100s);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(ManualTimekeeperTest, AdvanceWithoutAnyFutures) {\n  auto timekeeper = folly::ManualTimekeeper{};\n  timekeeper.advance(100s);\n  auto future = timekeeper.after(100s);\n  EXPECT_FALSE(future.isReady());\n  timekeeper.advance(100s);\n  EXPECT_TRUE(future.isReady());\n}\n\nTEST_F(ManualTimekeeperTest, advanceToNext) {\n  auto timekeeper = folly::ManualTimekeeper{};\n\n  // Add a few events to the timekeeper\n  auto f100s = timekeeper.after(100s);\n  auto f200s = timekeeper.after(200s);\n  auto f200sNum2 = timekeeper.after(200s);\n  auto f201s = timekeeper.after(201s);\n  EXPECT_FALSE(f100s.isReady());\n  EXPECT_FALSE(f200s.isReady());\n  EXPECT_FALSE(f200sNum2.isReady());\n  EXPECT_FALSE(f201s.isReady());\n  timekeeper.advanceToNext();\n\n  EXPECT_TRUE(f100s.isReady());\n  EXPECT_FALSE(f200s.isReady());\n  EXPECT_FALSE(f200sNum2.isReady());\n  EXPECT_FALSE(f201s.isReady());\n\n  auto f150s = timekeeper.after(50s);\n  timekeeper.advanceToNext();\n  EXPECT_TRUE(f150s.isReady());\n  EXPECT_FALSE(f200s.isReady());\n  EXPECT_FALSE(f200sNum2.isReady());\n  EXPECT_FALSE(f201s.isReady());\n\n  timekeeper.advance(49s);\n  EXPECT_FALSE(f200s.isReady());\n  EXPECT_FALSE(f200sNum2.isReady());\n  EXPECT_FALSE(f201s.isReady());\n\n  timekeeper.advanceToNext();\n  EXPECT_TRUE(f200s.isReady());\n  EXPECT_TRUE(f200sNum2.isReady());\n  EXPECT_FALSE(f201s.isReady());\n\n  timekeeper.advance(1s);\n  EXPECT_TRUE(f201s.isReady());\n}\n\nTEST_F(ManualTimekeeperTest, AdvanceWithManyFutures) {\n  auto timekeeper = folly::ManualTimekeeper{};\n\n  auto one = timekeeper.after(100s);\n  auto two = timekeeper.after(200s);\n  auto three = timekeeper.after(300s);\n\n  EXPECT_FALSE(one.isReady());\n  EXPECT_FALSE(two.isReady());\n  EXPECT_FALSE(three.isReady());\n\n  timekeeper.advance(100s);\n\n  EXPECT_TRUE(one.isReady());\n  EXPECT_FALSE(two.isReady());\n  EXPECT_FALSE(three.isReady());\n\n  timekeeper.advance(100s);\n\n  EXPECT_TRUE(one.isReady());\n  EXPECT_TRUE(two.isReady());\n  EXPECT_FALSE(three.isReady());\n\n  timekeeper.advance(100s);\n\n  EXPECT_TRUE(one.isReady());\n  EXPECT_TRUE(two.isReady());\n  EXPECT_TRUE(three.isReady());\n\n  timekeeper.advance(100s);\n\n  EXPECT_TRUE(one.isReady());\n  EXPECT_TRUE(two.isReady());\n  EXPECT_TRUE(three.isReady());\n\n  auto four = timekeeper.after(100s);\n\n  EXPECT_FALSE(four.isReady());\n\n  timekeeper.advance(100s);\n\n  EXPECT_TRUE(one.isReady());\n  EXPECT_TRUE(two.isReady());\n  EXPECT_TRUE(three.isReady());\n  EXPECT_TRUE(four.isReady());\n}\n\nTEST_F(ManualTimekeeperTest, Cancel) {\n  auto timekeeper = folly::ManualTimekeeper{};\n  auto future = timekeeper.after(100s);\n  future.cancel();\n  ASSERT_TRUE(future.isReady());\n  EXPECT_TRUE(future.result().hasException<FutureCancellation>());\n  timekeeper.advance(100s);\n  EXPECT_TRUE(future.result().hasException<FutureCancellation>());\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/MapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(Map, basic) {\n  Promise<int> p1;\n  Promise<int> p2;\n  Promise<int> p3;\n\n  std::vector<Future<int>> fs;\n  fs.push_back(p1.getFuture());\n  fs.push_back(p2.getFuture());\n  fs.push_back(p3.getFuture());\n\n  int c = 0;\n  std::vector<Future<Unit>> fs2 = futures::mapValue(fs, [&](int i) { c += i; });\n\n  // Ensure we call the callbacks as the futures complete regardless of order\n  p2.setValue(1);\n  EXPECT_EQ(1, c);\n  p3.setValue(1);\n  EXPECT_EQ(2, c);\n  p1.setValue(1);\n  EXPECT_EQ(3, c);\n\n  EXPECT_TRUE(collect(fs2).isReady());\n}\n\nTEST(Map, basicTry) {\n  Promise<int> p1;\n  Promise<int> p2;\n  Promise<int> p3;\n\n  std::vector<Future<int>> fs;\n  fs.push_back(p1.getFuture());\n  fs.push_back(p2.getFuture());\n  fs.push_back(p3.getFuture());\n\n  int c = 0;\n  std::vector<Future<Unit>> fs2 = futures::mapTry(fs, [&](folly::Try<int> i) {\n    c += i.value();\n  });\n\n  // Ensure we call the callbacks as the futures complete regardless of order\n  p2.setValue(1);\n  EXPECT_EQ(1, c);\n  p3.setValue(1);\n  EXPECT_EQ(2, c);\n  p1.setValue(1);\n  EXPECT_EQ(3, c);\n\n  EXPECT_TRUE(collect(fs2).isReady());\n}\n\nTEST(Map, executor) {\n  Promise<int> p1;\n  Promise<int> p2;\n  Promise<int> p3;\n  folly::InlineExecutor exec;\n\n  std::vector<Future<int>> fs;\n  fs.push_back(p1.getFuture());\n  fs.push_back(p2.getFuture());\n  fs.push_back(p3.getFuture());\n\n  int c = 0;\n  std::vector<Future<Unit>> fs2 = futures::mapValue(exec, fs, [&](int i) {\n    c += i;\n  });\n\n  // Ensure we call the callbacks as the futures complete regardless of order\n  p2.setValue(1);\n  EXPECT_EQ(1, c);\n  p3.setValue(1);\n  EXPECT_EQ(2, c);\n  p1.setValue(1);\n  EXPECT_EQ(3, c);\n\n  EXPECT_TRUE(collect(fs2).isReady());\n}\n\nTEST(Map, executorTry) {\n  Promise<int> p1;\n  Promise<int> p2;\n  Promise<int> p3;\n  folly::InlineExecutor exec;\n\n  std::vector<Future<int>> fs;\n  fs.push_back(p1.getFuture());\n  fs.push_back(p2.getFuture());\n  fs.push_back(p3.getFuture());\n\n  int c = 0;\n  std::vector<Future<Unit>> fs2 =\n      futures::mapTry(exec, fs, [&](folly::Try<int> i) { c += i.value(); });\n\n  // Ensure we call the callbacks as the futures complete regardless of order\n  p2.setValue(1);\n  EXPECT_EQ(1, c);\n  p3.setValue(1);\n  EXPECT_EQ(2, c);\n  p1.setValue(1);\n  EXPECT_EQ(3, c);\n\n  EXPECT_TRUE(collect(fs2).isReady());\n}\n\nTEST(Map, semifuture) {\n  Promise<int> p1;\n  Promise<int> p2;\n  Promise<int> p3;\n  folly::InlineExecutor exec;\n\n  std::vector<SemiFuture<int>> fs;\n  fs.push_back(p1.getSemiFuture());\n  fs.push_back(p2.getSemiFuture());\n  fs.push_back(p3.getSemiFuture());\n\n  int c = 0;\n  std::vector<Future<Unit>> fs2 = futures::mapValue(exec, fs, [&](int i) {\n    c += i;\n  });\n\n  // Ensure we call the callbacks as the futures complete regardless of order\n  p2.setValue(1);\n  EXPECT_EQ(1, c);\n  p3.setValue(1);\n  EXPECT_EQ(2, c);\n  p1.setValue(1);\n  EXPECT_EQ(3, c);\n\n  EXPECT_TRUE(collect(fs2).isReady());\n}\n"
  },
  {
    "path": "folly/futures/test/NonCopyableLambdaTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(NonCopyableLambda, basic) {\n  Promise<int> promise;\n  Future<int> future = promise.getFuture();\n\n  Future<Unit>().thenValue([promise = std::move(promise)](auto&&) mutable {\n    promise.setValue(123);\n  });\n\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(std::move(future).get(), 123);\n}\n\nTEST(NonCopyableLambda, uniquePtr) {\n  Promise<Unit> promise;\n  auto int_ptr = std::make_unique<int>(1);\n\n  EXPECT_EQ(*int_ptr, 1);\n\n  auto future = promise.getFuture().thenValue(\n      [int_ptr = std::move(int_ptr)](auto&&) mutable {\n        ++*int_ptr;\n        return std::move(int_ptr);\n      });\n\n  EXPECT_FALSE(future.isReady());\n  promise.setValue();\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(*std::move(future).get(), 2);\n}\n\nTEST(NonCopyableLambda, Function) {\n  Promise<int> promise;\n\n  Function<int(int)> callback = [](int x) { return x + 1; };\n\n  auto future = promise.getFuture().thenValue(std::move(callback));\n  EXPECT_THROW(callback(0), std::bad_function_call);\n\n  EXPECT_FALSE(future.isReady());\n  promise.setValue(100);\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(std::move(future).get(), 101);\n}\n\nTEST(NonCopyableLambda, FunctionConst) {\n  Promise<int> promise;\n\n  Function<int(int) const> callback = [](int x) { return x + 1; };\n\n  auto future = promise.getFuture().thenValue(std::move(callback));\n  EXPECT_THROW(callback(0), std::bad_function_call);\n\n  EXPECT_FALSE(future.isReady());\n  promise.setValue(100);\n  EXPECT_TRUE(future.isReady());\n  EXPECT_EQ(std::move(future).get(), 101);\n}\n"
  },
  {
    "path": "folly/futures/test/PollTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(Poll, ready) {\n  Promise<int> p;\n  auto f = p.getFuture();\n  p.setValue(42);\n  EXPECT_EQ(42, f.poll().value().value());\n}\n\nTEST(Poll, notReady) {\n  Promise<int> p;\n  auto f = p.getFuture();\n  EXPECT_FALSE(f.poll().has_value());\n}\n\nTEST(Poll, exception) {\n  Promise<Unit> p;\n  auto f = p.getFuture();\n  p.setWith([] { throw std::runtime_error(\"Runtime\"); });\n  EXPECT_TRUE(f.poll().value().hasException());\n}\n"
  },
  {
    "path": "folly/futures/test/PromiseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing std::string;\n\nusing std::unique_ptr;\nusing eggs_t = FutureException;\nstatic eggs_t eggs(\"eggs\");\n\nTEST(Promise, makeEmpty) {\n  auto p = Promise<int>::makeEmpty();\n  EXPECT_TRUE(p.isFulfilled());\n}\n\nTEST(Promise, special) {\n  EXPECT_FALSE(std::is_copy_constructible<Promise<int>>::value);\n  EXPECT_FALSE(std::is_copy_assignable<Promise<int>>::value);\n  EXPECT_TRUE(std::is_move_constructible<Promise<int>>::value);\n  EXPECT_TRUE(std::is_move_assignable<Promise<int>>::value);\n}\n\nTEST(Promise, getSemiFuture) {\n  Promise<int> p;\n  SemiFuture<int> f = p.getSemiFuture();\n  EXPECT_FALSE(f.isReady());\n}\n\nTEST(Promise, getFuture) {\n  Promise<int> p;\n  Future<int> f = p.getFuture();\n  EXPECT_FALSE(f.isReady());\n}\n\nTEST(Promise, setValueUnit) {\n  Promise<Unit> p;\n  p.setValue();\n}\n\nnamespace {\nauto makeValid() {\n  auto valid = Promise<int>();\n  EXPECT_TRUE(valid.valid());\n  return valid;\n}\nauto makeInvalid() {\n  auto invalid = Promise<int>::makeEmpty();\n  EXPECT_FALSE(invalid.valid());\n  return invalid;\n}\n} // namespace\n\nTEST(Promise, ctorPostconditionValid) {\n  // Ctors/factories that promise valid -- postcondition: valid()\n\n#define DOIT(CREATION_EXPR)    \\\n  do {                         \\\n    auto p1 = (CREATION_EXPR); \\\n    EXPECT_TRUE(p1.valid());   \\\n    auto p2 = std::move(p1);   \\\n    EXPECT_FALSE(p1.valid());  \\\n    EXPECT_TRUE(p2.valid());   \\\n  } while (false)\n\n  DOIT(makeValid());\n  DOIT(Promise<int>());\n  DOIT(Promise<int>{});\n  DOIT(Promise<Unit>());\n  DOIT(Promise<Unit>{});\n\n#undef DOIT\n}\n\nTEST(Promise, ctorPostconditionInvalid) {\n  // Ctors/factories that promise invalid -- postcondition: !valid()\n\n#define DOIT(CREATION_EXPR)    \\\n  do {                         \\\n    auto p1 = (CREATION_EXPR); \\\n    EXPECT_FALSE(p1.valid());  \\\n    auto p2 = std::move(p1);   \\\n    EXPECT_FALSE(p1.valid());  \\\n    EXPECT_FALSE(p2.valid());  \\\n  } while (false)\n\n  DOIT(makeInvalid());\n  DOIT(Promise<int>::makeEmpty());\n\n#undef DOIT\n}\n\nTEST(Promise, lacksPreconditionValid) {\n  // Ops that don't throw PromiseInvalid if !valid() --\n  // without precondition: valid()\n\n#define DOIT(STMT)         \\\n  do {                     \\\n    auto p = makeValid();  \\\n    {                      \\\n      STMT;                \\\n    }                      \\\n    copy(std::move(p));    \\\n    EXPECT_NO_THROW(STMT); \\\n  } while (false)\n\n  // misc methods that don't require isValid()\n  DOIT(p.valid());\n  DOIT(p.isFulfilled());\n\n  // move-ctor - move-copy to local, copy(), pass-by-move-value\n  DOIT(auto other = std::move(p));\n  DOIT(copy(std::move(p)));\n  DOIT(([](auto) {})(std::move(p)));\n\n  // move-assignment into either {valid | invalid}\n  DOIT({\n    auto other = makeValid();\n    other = std::move(p);\n  });\n  DOIT({\n    auto other = makeInvalid();\n    other = std::move(p);\n  });\n\n#undef DOIT\n}\n\nTEST(Promise, hasPreconditionValid) {\n  // Ops that require validity; precondition: valid();\n  // throw PromiseInvalid if !valid()\n\n#define DOIT(STMT)                      \\\n  do {                                  \\\n    auto p = makeValid();               \\\n    EXPECT_NO_THROW(STMT);              \\\n    copy(std::move(p));                 \\\n    EXPECT_THROW(STMT, PromiseInvalid); \\\n  } while (false)\n\n  auto const except = std::logic_error(\"foo\");\n  auto const ewrap = folly::exception_wrapper(except);\n\n  DOIT(p.getSemiFuture());\n  DOIT(p.getFuture());\n  DOIT(p.setException(except));\n  DOIT(p.setException(ewrap));\n  DOIT(p.setInterruptHandler([](auto&) {}));\n  DOIT(p.setValue(42));\n  DOIT(p.setTry(Try<int>(42)));\n  DOIT(p.setTry(Try<int>(ewrap)));\n  DOIT(p.setWith([] { return 42; }));\n\n#undef DOIT\n}\n\nTEST(Promise, hasPostconditionValid) {\n  // Ops that preserve validity -- postcondition: valid()\n\n#define DOIT(STMT)          \\\n  do {                      \\\n    auto p = makeValid();   \\\n    EXPECT_NO_THROW(STMT);  \\\n    EXPECT_TRUE(p.valid()); \\\n  } while (false)\n\n  auto const swallow = [](auto) {};\n\n  DOIT(swallow(p.valid())); // p.valid() itself preserves validity\n  DOIT(swallow(p.isFulfilled()));\n\n#undef DOIT\n}\n\nTEST(Promise, hasPostconditionInvalid) {\n  // Ops that consume *this -- postcondition: !valid()\n\n#define DOIT(CTOR, STMT)     \\\n  do {                       \\\n    auto p = (CTOR);         \\\n    EXPECT_NO_THROW(STMT);   \\\n    EXPECT_FALSE(p.valid()); \\\n  } while (false)\n\n  // move-ctor of {valid|invalid}\n  DOIT(makeValid(), { auto other{std::move(p)}; });\n  DOIT(makeInvalid(), { auto other{std::move(p)}; });\n\n  // move-assignment of {valid|invalid} into {valid|invalid}\n  DOIT(makeValid(), {\n    auto other = makeValid();\n    other = std::move(p);\n  });\n  DOIT(makeValid(), {\n    auto other = makeInvalid();\n    other = std::move(p);\n  });\n  DOIT(makeInvalid(), {\n    auto other = makeValid();\n    other = std::move(p);\n  });\n  DOIT(makeInvalid(), {\n    auto other = makeInvalid();\n    other = std::move(p);\n  });\n\n  // pass-by-value of {valid|invalid}\n  DOIT(makeValid(), {\n    auto const byval = [](auto) {};\n    byval(std::move(p));\n  });\n  DOIT(makeInvalid(), {\n    auto const byval = [](auto) {};\n    byval(std::move(p));\n  });\n\n#undef DOIT\n}\n\nTEST(Promise, setValueSemiFuture) {\n  Promise<int> fund;\n  auto ffund = fund.getSemiFuture();\n  fund.setValue(42);\n  EXPECT_EQ(42, ffund.value());\n\n  struct Foo {\n    string name;\n    int value;\n  };\n\n  Promise<Foo> pod;\n  auto fpod = pod.getSemiFuture();\n  Foo f = {\"the answer\", 42};\n  pod.setValue(f);\n  Foo f2 = fpod.value();\n  EXPECT_EQ(f.name, f2.name);\n  EXPECT_EQ(f.value, f2.value);\n\n  pod = Promise<Foo>();\n  fpod = pod.getSemiFuture();\n  pod.setValue(std::move(f2));\n  Foo f3 = fpod.value();\n  EXPECT_EQ(f.name, f3.name);\n  EXPECT_EQ(f.value, f3.value);\n\n  Promise<unique_ptr<int>> mov;\n  auto fmov = mov.getSemiFuture();\n  mov.setValue(std::make_unique<int>(42));\n  unique_ptr<int> ptr = std::move(fmov.value());\n  EXPECT_EQ(42, *ptr);\n\n  Promise<Unit> v;\n  auto fv = v.getSemiFuture();\n  v.setValue();\n  EXPECT_TRUE(fv.isReady());\n}\n\nTEST(Promise, setValue) {\n  Promise<int> fund;\n  auto ffund = fund.getFuture();\n  fund.setValue(42);\n  EXPECT_EQ(42, ffund.value());\n\n  struct Foo {\n    string name;\n    int value;\n  };\n\n  Promise<Foo> pod;\n  auto fpod = pod.getFuture();\n  Foo f = {\"the answer\", 42};\n  pod.setValue(f);\n  Foo f2 = fpod.value();\n  EXPECT_EQ(f.name, f2.name);\n  EXPECT_EQ(f.value, f2.value);\n\n  pod = Promise<Foo>();\n  fpod = pod.getFuture();\n  pod.setValue(std::move(f2));\n  Foo f3 = fpod.value();\n  EXPECT_EQ(f.name, f3.name);\n  EXPECT_EQ(f.value, f3.value);\n\n  Promise<unique_ptr<int>> mov;\n  auto fmov = mov.getFuture();\n  mov.setValue(std::make_unique<int>(42));\n  unique_ptr<int> ptr = std::move(fmov.value());\n  EXPECT_EQ(42, *ptr);\n\n  Promise<Unit> v;\n  auto fv = v.getFuture();\n  v.setValue();\n  EXPECT_TRUE(fv.isReady());\n}\n\nTEST(Promise, setException) {\n  {\n    Promise<Unit> p;\n    auto f = p.getFuture();\n    p.setException(eggs);\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n  {\n    Promise<Unit> p;\n    auto f = p.getFuture();\n    p.setException(exception_wrapper(eggs));\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n}\n\nTEST(Promise, setWith) {\n  {\n    Promise<int> p;\n    auto f = p.getFuture();\n    p.setWith([] { return 42; });\n    EXPECT_EQ(42, f.value());\n  }\n  {\n    Promise<int> p;\n    auto f = p.getFuture();\n    p.setWith([]() -> int { throw eggs; });\n    EXPECT_THROW(f.value(), eggs_t);\n  }\n}\n\nTEST(Promise, isFulfilled) {\n  Promise<int> p;\n\n  EXPECT_FALSE(p.isFulfilled());\n  p.setValue(42);\n  EXPECT_TRUE(p.isFulfilled());\n}\n\nTEST(Promise, isFulfilledWithFuture) {\n  Promise<int> p;\n  auto f = p.getFuture(); // so core_ will become null\n\n  EXPECT_FALSE(p.isFulfilled());\n  p.setValue(42); // after here\n  EXPECT_TRUE(p.isFulfilled());\n}\n\nTEST(Promise, brokenOnDelete) {\n  auto p = std::make_unique<Promise<int>>();\n  auto f = p->getFuture();\n\n  EXPECT_FALSE(f.isReady());\n\n  p.reset();\n\n  EXPECT_TRUE(f.isReady());\n\n  auto t = f.result();\n\n  EXPECT_TRUE(t.hasException<BrokenPromise>());\n}\n\nTEST(Promise, brokenPromiseHasTypeInfo) {\n  auto pInt = std::make_unique<Promise<int>>();\n  auto fInt = pInt->getFuture();\n\n  auto pFloat = std::make_unique<Promise<float>>();\n  auto fFloat = pFloat->getFuture();\n\n  pInt.reset();\n  pFloat.reset();\n\n  std::string whatInt = fInt.result().exception().get_exception()->what();\n  std::string whatFloat = fFloat.result().exception().get_exception()->what();\n  EXPECT_EQ(whatInt, \"Broken promise for type name `int`\");\n  EXPECT_EQ(whatFloat, \"Broken promise for type name `float`\");\n}\n"
  },
  {
    "path": "folly/futures/test/ReduceTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(Reduce, basic) {\n  auto makeFutures = [](int count) {\n    std::vector<Future<int>> fs;\n    for (int i = 1; i <= count; ++i) {\n      fs.emplace_back(makeFuture(i));\n    }\n    return fs;\n  };\n\n  // Empty (Try)\n  {\n    auto fs = makeFutures(0);\n\n    Future<double> f1 = reduce(fs, 1.2, [](double a, Try<int>&& b) {\n      return a + *b + 0.1;\n    });\n    EXPECT_EQ(1.2, std::move(f1).get());\n  }\n\n  // One (Try)\n  {\n    auto fs = makeFutures(1);\n\n    Future<double> f1 = reduce(fs, 0.0, [](double a, Try<int>&& b) {\n      return a + *b + 0.1;\n    });\n    EXPECT_EQ(1.1, std::move(f1).get());\n  }\n\n  // Returning values (Try)\n  {\n    auto fs = makeFutures(3);\n\n    Future<double> f1 = reduce(fs, 0.0, [](double a, Try<int>&& b) {\n      return a + *b + 0.1;\n    });\n    EXPECT_EQ(6.3, std::move(f1).get());\n  }\n\n  // Returning values\n  {\n    auto fs = makeFutures(3);\n\n    Future<double> f1 = reduce(fs, 0.0, [](double a, int&& b) {\n      return a + b + 0.1;\n    });\n    EXPECT_EQ(6.3, std::move(f1).get());\n  }\n\n  // Returning futures (Try)\n  {\n    auto fs = makeFutures(3);\n\n    Future<double> f2 = reduce(fs, 0.0, [](double a, Try<int>&& b) {\n      return makeFuture<double>(a + *b + 0.1);\n    });\n    EXPECT_EQ(6.3, std::move(f2).get());\n  }\n\n  // Returning futures\n  {\n    auto fs = makeFutures(3);\n\n    Future<double> f2 = reduce(fs, 0.0, [](double a, int&& b) {\n      return makeFuture<double>(a + b + 0.1);\n    });\n    EXPECT_EQ(6.3, std::move(f2).get());\n  }\n}\n\nTEST(Reduce, chain) {\n  auto makeFutures = [](int count) {\n    std::vector<Future<int>> fs;\n    for (int i = 1; i <= count; ++i) {\n      fs.emplace_back(makeFuture(i));\n    }\n    return fs;\n  };\n\n  {\n    auto f =\n        collectAll(makeFutures(3))\n            .toUnsafeFuture()\n            .reduce(0, [](int a, Try<int>&& b) { return a + *b; });\n    EXPECT_EQ(6, std::move(f).get());\n  }\n  {\n    auto f =\n        collect(makeFutures(3)).toUnsafeFuture().reduce(0, [](int a, int&& b) {\n          return a + b;\n        });\n    EXPECT_EQ(6, std::move(f).get());\n  }\n}\n\nTEST(Reduce, unorderedReduce) {\n  {\n    std::vector<Future<int>> fs;\n    fs.push_back(makeFuture(1));\n    fs.push_back(makeFuture(2));\n    fs.push_back(makeFuture(3));\n\n    Future<double> f =\n        unorderedReduce(fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {\n          return double(b);\n        });\n    EXPECT_EQ(3.0, std::move(f).get());\n  }\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    Promise<int> p3;\n\n    std::vector<Future<int>> fs;\n    fs.push_back(p1.getFuture());\n    fs.push_back(p2.getFuture());\n    fs.push_back(p3.getFuture());\n\n    Future<double> f =\n        unorderedReduce(fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {\n          return double(b);\n        });\n    p3.setValue(3);\n    p2.setValue(2);\n    p1.setValue(1);\n    EXPECT_EQ(1.0, std::move(f).get());\n  }\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    Promise<int> p3;\n\n    std::vector<SemiFuture<int>> fs;\n    fs.push_back(p1.getSemiFuture());\n    fs.push_back(p2.getSemiFuture());\n    fs.push_back(p3.getSemiFuture());\n\n    SemiFuture<double> f = unorderedReduceSemiFuture(\n        fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {\n          return double(b);\n        });\n    p3.setValue(3);\n    p2.setValue(2);\n    p1.setValue(1);\n    EXPECT_EQ(1.0, std::move(f).get());\n  }\n}\n\nTEST(Reduce, unorderedReduceException) {\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    Promise<int> p3;\n\n    std::vector<Future<int>> fs;\n    fs.push_back(p1.getFuture());\n    fs.push_back(p2.getFuture());\n    fs.push_back(p3.getFuture());\n\n    Future<double> f =\n        unorderedReduce(fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {\n          return b + 0.0;\n        });\n    p3.setValue(3);\n    p2.setException(exception_wrapper(std::runtime_error(\"blah\")));\n    p1.setValue(1);\n    EXPECT_THROW(std::move(f).get(), std::runtime_error);\n  }\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    Promise<int> p3;\n\n    std::vector<SemiFuture<int>> fs;\n    fs.push_back(p1.getSemiFuture());\n    fs.push_back(p2.getSemiFuture());\n    fs.push_back(p3.getSemiFuture());\n\n    SemiFuture<double> f = unorderedReduceSemiFuture(\n        fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {\n          return b + 0.0;\n        });\n    p3.setValue(3);\n    p2.setException(exception_wrapper(std::runtime_error(\"blah\")));\n    p1.setValue(1);\n    EXPECT_THROW(std::move(f).get(), std::runtime_error);\n  }\n}\n\nTEST(Reduce, unorderedReduceFuture) {\n  Promise<int> p1;\n  Promise<int> p2;\n  Promise<int> p3;\n\n  std::vector<Future<int>> fs;\n  fs.push_back(p1.getFuture());\n  fs.push_back(p2.getFuture());\n  fs.push_back(p3.getFuture());\n\n  std::vector<Promise<double>> ps(3);\n\n  Future<int> f =\n      unorderedReduce(fs.begin(), fs.end(), 0.0, [&](double /* a */, int&& b) {\n        return ps[b - 1].getFuture();\n      });\n  p3.setValue(3);\n  p2.setValue(2);\n  p1.setValue(1);\n\n  ps[0].setValue(1.0);\n  ps[1].setValue(2.0);\n  ps[2].setValue(3.0);\n\n  EXPECT_EQ(1.0, std::move(f).get());\n}\n"
  },
  {
    "path": "folly/futures/test/RetryingTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Retrying.h>\n\n#include <algorithm>\n#include <atomic>\n#include <vector>\n\n#include <folly/futures/test/TestExecutor.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/SysResource.h>\n\nusing namespace std;\nusing namespace std::chrono;\nusing namespace folly;\n\n// Runs func num_times in parallel, expects that all of them will take\n// at least min_duration and at least 1 execution will take less than\n// max_duration.\ntemplate <typename D, typename F>\nvoid multiAttemptExpectDurationWithin(\n    size_t num_tries, D min_duration, D max_duration, const F& func) {\n  vector<thread> threads(num_tries);\n  vector<D> durations(num_tries, D::min());\n  for (size_t i = 0; i < num_tries; ++i) {\n    threads[i] = thread([&, i] {\n      auto start = steady_clock::now();\n      func();\n      durations[i] = duration_cast<D>(steady_clock::now() - start);\n    });\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n  sort(durations.begin(), durations.end());\n  for (auto d : durations) {\n    EXPECT_GE(d.count(), min_duration.count());\n  }\n  EXPECT_LE(durations[0].count(), max_duration.count());\n}\n\nTEST(RetryingTest, hasOpCall) {\n  using ew = exception_wrapper;\n  auto policy_raw = [](size_t n, const ew&) { return n < 3; };\n  auto policy_fut = [](size_t n, const ew&) { return makeFuture(n < 3); };\n  auto policy_semi_fut = [](size_t n, const ew&) {\n    return makeSemiFuture(n < 3);\n  };\n  using namespace futures::detail;\n  EXPECT_TRUE(retrying_policy_traits<decltype(policy_raw)>::is_raw::value);\n  EXPECT_TRUE(retrying_policy_traits<decltype(policy_fut)>::is_fut::value);\n  EXPECT_TRUE(\n      retrying_policy_traits<decltype(policy_semi_fut)>::is_semi_fut::value);\n}\n\nTEST(RetryingTest, basic) {\n  auto r =\n      futures::retrying(\n          [](size_t n, const exception_wrapper&) { return n < 3; },\n          [](size_t n) {\n            return n < 2\n                ? makeFuture<size_t>(runtime_error(\"ha\"))\n                : makeFuture(n);\n          })\n          .wait();\n  EXPECT_EQ(2, r.value());\n}\n\nTEST(RetryingTest, basicUnsafe) {\n  auto r =\n      futures::retryingUnsafe(\n          [](size_t n, const exception_wrapper&) { return n < 3; },\n          [](size_t n) {\n            return n < 2\n                ? makeFuture<size_t>(runtime_error(\"ha\"))\n                : makeFuture(n);\n          })\n          .wait();\n  EXPECT_EQ(2, r.value());\n}\n\nTEST(RetryingTest, futureFactoryThrows) {\n  struct ReturnedException : exception {};\n  struct ThrownException : exception {};\n  auto result =\n      futures::retrying(\n          [](size_t n, const exception_wrapper&) { return n < 2; },\n          [](size_t n) {\n            switch (n) {\n              case 0:\n                return makeFuture<size_t>(\n                    make_exception_wrapper<ReturnedException>());\n              case 1:\n                throw ThrownException();\n              default:\n                return makeFuture(n);\n            }\n          })\n          .wait()\n          .result();\n  EXPECT_THROW(result.throwUnlessValue(), ThrownException);\n}\n\nTEST(RetryingTest, futureFactoryThrowsUnsafe) {\n  struct ReturnedException : exception {};\n  struct ThrownException : exception {};\n  auto result =\n      futures::retryingUnsafe(\n          [](size_t n, const exception_wrapper&) { return n < 2; },\n          [](size_t n) {\n            switch (n) {\n              case 0:\n                return makeFuture<size_t>(\n                    make_exception_wrapper<ReturnedException>());\n              case 1:\n                throw ThrownException();\n              default:\n                return makeFuture(n);\n            }\n          })\n          .wait()\n          .result();\n  EXPECT_THROW(result.throwUnlessValue(), ThrownException);\n}\n\nTEST(RetryingTest, policyThrows) {\n  struct eggs : exception {};\n  auto r = futures::retrying(\n      [](size_t, exception_wrapper) -> bool { throw eggs(); },\n      [](size_t) -> Future<size_t> { throw std::runtime_error(\"ha\"); });\n  EXPECT_THROW(std::move(r).get(), eggs);\n}\n\nTEST(RetryingTest, policyThrowsUnsafe) {\n  struct eggs : exception {};\n  auto r = futures::retryingUnsafe(\n      [](size_t, exception_wrapper) -> bool { throw eggs(); },\n      [](size_t) -> Future<size_t> { throw std::runtime_error(\"ha\"); });\n  EXPECT_THROW(std::move(r).get(), eggs);\n}\n\nTEST(RetryingTest, policyFuture) {\n  atomic<size_t> sleeps{0};\n  auto r =\n      futures::retrying(\n          [&](size_t n, const exception_wrapper&) {\n            return n < 3\n                ? makeFuture(++sleeps).thenValue([](auto&&) { return true; })\n                : makeFuture(false);\n          },\n          [](size_t n) {\n            return n < 2\n                ? makeFuture<size_t>(runtime_error(\"ha\"))\n                : makeFuture(n);\n          })\n          .wait();\n  EXPECT_EQ(2, r.value());\n  EXPECT_EQ(2, sleeps);\n}\n\nTEST(RetryingTest, policyFutureUnsafe) {\n  atomic<size_t> sleeps{0};\n  auto r =\n      futures::retryingUnsafe(\n          [&](size_t n, const exception_wrapper&) {\n            return n < 3\n                ? makeFuture(++sleeps).thenValue([](auto&&) { return true; })\n                : makeFuture(false);\n          },\n          [](size_t n) {\n            return n < 2\n                ? makeFuture<size_t>(runtime_error(\"ha\"))\n                : makeFuture(n);\n          })\n          .wait();\n  EXPECT_EQ(2, r.value());\n  EXPECT_EQ(2, sleeps);\n}\n\nTEST(RetryingTest, policySemiFuture) {\n  atomic<size_t> sleeps{0};\n  auto r =\n      futures::retrying(\n          [&](size_t n, const exception_wrapper&) {\n            return n < 3 ? makeSemiFuture(++sleeps).deferValue([](auto&&) {\n              return true;\n            })\n                         : makeSemiFuture(false);\n          },\n          [](size_t n) {\n            return n < 2\n                ? makeFuture<size_t>(runtime_error(\"ha\"))\n                : makeFuture(n);\n          })\n          .wait();\n  EXPECT_EQ(2, r.value());\n  EXPECT_EQ(2, sleeps);\n}\n\nTEST(RetryingTest, policyBasic) {\n  auto r =\n      futures::retrying(futures::retryingPolicyBasic(3), [](size_t n) {\n        return n < 2 ? makeFuture<size_t>(runtime_error(\"ha\")) : makeFuture(n);\n      }).wait();\n  EXPECT_EQ(2, r.value());\n}\n\nTEST(RetryingTest, policyBasicUnsafe) {\n  auto r =\n      futures::retryingUnsafe(futures::retryingPolicyBasic(3), [](size_t n) {\n        return n < 2 ? makeFuture<size_t>(runtime_error(\"ha\")) : makeFuture(n);\n      }).wait();\n  EXPECT_EQ(2, r.value());\n}\n\nTEST(RetryingTest, semifuturePolicyBasic) {\n  auto r = futures::retrying(futures::retryingPolicyBasic(3), [](size_t n) {\n             return n < 2\n                 ? makeSemiFuture<size_t>(runtime_error(\"ha\"))\n                 : makeSemiFuture(n);\n           }).wait();\n  EXPECT_EQ(2, r.value());\n}\n\nTEST(RetryingTest, policyCappedJitteredExponentialBackoff) {\n  multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(600), [] {\n    using ms = milliseconds;\n    auto r =\n        futures::retrying(\n            futures::retryingPolicyCappedJitteredExponentialBackoff(\n                3,\n                ms(100),\n                ms(1500),\n                0.1,\n                mt19937_64(0),\n                [](size_t, const exception_wrapper&) { return true; }),\n            [](size_t n) {\n              return n < 2\n                  ? makeFuture<size_t>(runtime_error(\"ha\"))\n                  : makeFuture(n);\n            })\n            .wait();\n    EXPECT_EQ(2, r.value());\n  });\n}\n\nTEST(RetryingTest, policyCappedJitteredExponentialBackoffUnsafe) {\n  multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(600), [] {\n    using ms = milliseconds;\n    auto r =\n        futures::retryingUnsafe(\n            futures::retryingPolicyCappedJitteredExponentialBackoff(\n                3,\n                ms(100),\n                ms(1500),\n                0.1,\n                mt19937_64(0),\n                [](size_t, const exception_wrapper&) { return true; }),\n            [](size_t n) {\n              return n < 2\n                  ? makeFuture<size_t>(runtime_error(\"ha\"))\n                  : makeFuture(n);\n            })\n            .wait();\n    EXPECT_EQ(2, r.value());\n  });\n}\n\nTEST(RetryingTest, policyCappedJitteredExponentialBackoffManyRetries) {\n  using namespace futures::detail;\n  mt19937_64 rng(0);\n  Duration min_backoff(1);\n\n  Duration max_backoff(10000000);\n  Duration backoff = retryingJitteredExponentialBackoffDur(\n      80, min_backoff, max_backoff, 0, rng);\n  EXPECT_EQ(backoff.count(), max_backoff.count());\n\n  max_backoff = Duration(std::numeric_limits<int64_t>::max());\n  backoff = retryingJitteredExponentialBackoffDur(\n      63, min_backoff, max_backoff, 0, rng);\n  EXPECT_LT(backoff.count(), max_backoff.count());\n\n  max_backoff = Duration(std::numeric_limits<int64_t>::max());\n  backoff = retryingJitteredExponentialBackoffDur(\n      64, min_backoff, max_backoff, 0, rng);\n  EXPECT_EQ(backoff.count(), max_backoff.count());\n}\n\nTEST(RetryingTest, policyCappedJitteredExponentialBackoffMinZero) {\n  using namespace futures::detail;\n  mt19937_64 rng(0);\n\n  Duration min_backoff(0);\n  Duration max_backoff(2000);\n\n  EXPECT_EQ(\n      retryingJitteredExponentialBackoffDur(5, min_backoff, max_backoff, 0, rng)\n          .count(),\n      0);\n\n  EXPECT_EQ(\n      retryingJitteredExponentialBackoffDur(5, min_backoff, max_backoff, 1, rng)\n          .count(),\n      0);\n\n  EXPECT_EQ(\n      retryingJitteredExponentialBackoffDur(\n          1025, min_backoff, max_backoff, 0, rng)\n          .count(),\n      0);\n}\n\nTEST(RetryingTest, policySleepDefaults) {\n  multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(600), [] {\n    //  To ensure that this compiles with default params.\n    using ms = milliseconds;\n    auto r =\n        futures::retrying(\n            futures::retryingPolicyCappedJitteredExponentialBackoff(\n                3, ms(100), ms(1500), 0.1),\n            [](size_t n) {\n              return n < 2\n                  ? makeFuture<size_t>(runtime_error(\"ha\"))\n                  : makeFuture(n);\n            })\n            .wait();\n    EXPECT_EQ(2, r.value());\n  });\n}\n\nTEST(RetryingTest, largeRetries) {\n#ifndef _WIN32\n  rlimit oldMemLimit;\n  PCHECK(getrlimit(RLIMIT_AS, &oldMemLimit) == 0);\n\n  rlimit newMemLimit;\n  newMemLimit.rlim_cur =\n      std::min(static_cast<rlim_t>(1UL << 30), oldMemLimit.rlim_max);\n  newMemLimit.rlim_max = oldMemLimit.rlim_max;\n  auto const lowered = // sanitizers reserve outside of the rlimit\n      !folly::kIsSanitize && setrlimit(RLIMIT_AS, &newMemLimit) != 0;\n  SCOPE_EXIT {\n    PCHECK(!lowered || setrlimit(RLIMIT_AS, &oldMemLimit) == 0);\n  };\n#endif\n\n  TestExecutor executor(4);\n  // size of implicit promise is at least the size of the return.\n  using LargeReturn = array<uint64_t, 16000>;\n  auto func = [&executor](size_t retryNum) -> Future<LargeReturn> {\n    return via(&executor).thenValue([retryNum](auto&&) {\n      return retryNum < 10000\n          ? makeFuture<LargeReturn>(\n                make_exception_wrapper<std::runtime_error>(\"keep trying\"))\n          : makeFuture<LargeReturn>(LargeReturn());\n    });\n  };\n\n  vector<SemiFuture<LargeReturn>> futures;\n  for (auto idx = 0; idx < 40; ++idx) {\n    futures.emplace_back(\n        futures::retrying(\n            [&executor](size_t, const exception_wrapper&) {\n              return via(&executor).thenValue([](auto&&) { return true; });\n            },\n            func));\n  }\n\n  // 40 * 10,000 * 16,000B > 1GB; we should avoid OOM\n\n  for (auto& f : futures) {\n    f.wait();\n    EXPECT_TRUE(f.hasValue());\n  }\n}\n\nTEST(RetryingTest, retryingJitteredExponentialBackoffDur) {\n  mt19937_64 rng(0);\n\n  auto backoffMin = milliseconds(100);\n  auto backoffMax = milliseconds(1000);\n\n  EXPECT_EQ(\n      100,\n      futures::detail::retryingJitteredExponentialBackoffDur(\n          1, backoffMin, backoffMax, 0, rng)\n          .count());\n\n  EXPECT_EQ(\n      200,\n      futures::detail::retryingJitteredExponentialBackoffDur(\n          2, backoffMin, backoffMax, 0, rng)\n          .count());\n\n  EXPECT_EQ(\n      400,\n      futures::detail::retryingJitteredExponentialBackoffDur(\n          3, backoffMin, backoffMax, 0, rng)\n          .count());\n\n  EXPECT_EQ(\n      800,\n      futures::detail::retryingJitteredExponentialBackoffDur(\n          4, backoffMin, backoffMax, 0, rng)\n          .count());\n\n  EXPECT_EQ(\n      1000,\n      futures::detail::retryingJitteredExponentialBackoffDur(\n          5, backoffMin, backoffMax, 0, rng)\n          .count());\n\n  // Invalid usage: backoffMin > backoffMax\n  backoffMax = milliseconds(0);\n\n  EXPECT_EQ(\n      100,\n      futures::detail::retryingJitteredExponentialBackoffDur(\n          1, backoffMin, backoffMax, 0, rng)\n          .count());\n\n  EXPECT_EQ(\n      100,\n      futures::detail::retryingJitteredExponentialBackoffDur(\n          1000, backoffMin, backoffMax, 0, rng)\n          .count());\n}\n\n/*\nTEST(RetryingTest, policySleepCancel) {\n  multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{\n    mt19937_64 rng(0);\n    using ms = milliseconds;\n    auto r = futures::retrying(\n        futures::retryingPolicyCappedJitteredExponentialBackoff(\n          5, ms(100), ms(1000), 0.1, rng,\n          [](size_t n, const exception_wrapper&) { return true; }),\n        [](size_t n) {\n            return n < 4\n              ? makeFuture<size_t>(runtime_error(\"ha\"))\n              : makeFuture(n);\n        }\n    );\n    r.cancel();\n    r.wait();\n    EXPECT_EQ(2, r.value());\n  });\n}\n*/\n"
  },
  {
    "path": "folly/futures/test/SelfDestructTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/executors/InlineExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(SelfDestruct, then) {\n  auto* p = new Promise<int>();\n  auto future = p->getFuture().thenValue([p](int x) {\n    delete p;\n    return x + 1;\n  });\n  p->setValue(123);\n  EXPECT_EQ(124, std::move(future).get());\n}\n\nTEST(SelfDestruct, ensure) {\n  auto* p = new Promise<int>();\n  auto future = p->getFuture().ensure([p] { delete p; });\n  p->setValue(123);\n  EXPECT_EQ(123, std::move(future).get());\n}\n\nclass ThrowingExecutorError : public std::runtime_error {\n public:\n  using std::runtime_error::runtime_error;\n};\n\nclass ThrowingExecutor : public folly::Executor {\n public:\n  void add(folly::Func) override {\n    throw ThrowingExecutorError(\"ThrowingExecutor::add\");\n  }\n};\n\nTEST(SelfDestruct, throwingExecutor) {\n  ThrowingExecutor executor;\n  auto* p = new Promise<int>();\n  auto future = p->getFuture().via(&executor).thenError(\n      folly::tag_t<ThrowingExecutorError>{}, [p](auto const&) {\n        delete p;\n        return 456;\n      });\n  p->setValue(123);\n  EXPECT_EQ(456, std::move(future).get());\n}\n\nTEST(SelfDestruct, throwingInlineExecutor) {\n  InlineExecutor executor;\n\n  auto* p = new Promise<int>();\n  auto future =\n      p->getFuture()\n          .via(&executor)\n          .thenValue([p](auto&&) -> int {\n            delete p;\n            throw ThrowingExecutorError(\"callback throws\");\n          })\n          .thenError(folly::tag_t<ThrowingExecutorError>{}, [](auto const&) {\n            return 456;\n          });\n  p->setValue(123);\n  EXPECT_EQ(456, std::move(future).get());\n}\n"
  },
  {
    "path": "folly/futures/test/SemiFutureTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <atomic>\n#include <future>\n#include <memory>\n#include <numeric>\n#include <string>\n#include <thread>\n#include <type_traits>\n\n#include <folly/Executor.h>\n#include <folly/Memory.h>\n#include <folly/Unit.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/json/dynamic.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\n#define EXPECT_TYPE(x, T) EXPECT_TRUE((std::is_same<decltype(x), T>::value))\n\nusing eggs_t = FutureException;\nstatic eggs_t eggs(\"eggs\");\n\n// Future\n\nTEST(SemiFuture, makeEmpty) {\n  auto f = SemiFuture<int>::makeEmpty();\n  EXPECT_THROW(f.isReady(), FutureInvalid);\n}\n\nTEST(SemiFuture, futureDefaultCtor) {\n  SemiFuture<Unit>();\n}\n\nTEST(SemiFuture, semiFutureToUnit) {\n  SemiFuture<Unit> fu = makeSemiFuture(42).unit();\n  std::move(fu).get();\n  EXPECT_THROW(makeSemiFuture<int>(eggs).unit().get(), eggs_t);\n}\n\nTEST(SemiFuture, makeSemiFutureWithUnit) {\n  int count = 0;\n  SemiFuture<Unit> fu = makeSemiFutureWith([&] { count++; });\n  EXPECT_EQ(1, count);\n}\n\nnamespace {\nauto makeValid() {\n  auto valid = makeSemiFuture<int>(42);\n  EXPECT_TRUE(valid.valid());\n  return valid;\n}\nauto makeInvalid() {\n  auto invalid = SemiFuture<int>::makeEmpty();\n  EXPECT_FALSE(invalid.valid());\n  return invalid;\n}\n} // namespace\n\nTEST(SemiFuture, ctorPostconditionValid) {\n  // Ctors/factories that promise valid -- postcondition: valid()\n\n#define DOIT(CREATION_EXPR)    \\\n  do {                         \\\n    auto f1 = (CREATION_EXPR); \\\n    EXPECT_TRUE(f1.valid());   \\\n    auto f2 = std::move(f1);   \\\n    EXPECT_FALSE(f1.valid());  \\\n    EXPECT_TRUE(f2.valid());   \\\n  } while (false)\n\n  auto const except = std::logic_error(\"foo\");\n  auto const ewrap = folly::exception_wrapper(except);\n\n  DOIT(makeValid());\n  DOIT(SemiFuture<int>(42));\n  DOIT(SemiFuture<int>{42});\n  DOIT(SemiFuture<Unit>());\n  DOIT(SemiFuture<Unit>{});\n  DOIT(makeSemiFuture());\n  DOIT(makeSemiFuture(Unit{}));\n  DOIT(makeSemiFuture<Unit>(Unit{}));\n  DOIT(makeSemiFuture(42));\n  DOIT(makeSemiFuture<int>(42));\n  DOIT(makeSemiFuture<int>(except));\n  DOIT(makeSemiFuture<int>(ewrap));\n  DOIT(makeSemiFuture(Try<int>(42)));\n  DOIT(makeSemiFuture<int>(Try<int>(42)));\n  DOIT(makeSemiFuture<int>(Try<int>(ewrap)));\n\n#undef DOIT\n}\n\nTEST(SemiFuture, ctorPostconditionInvalid) {\n  // Ctors/factories that promise invalid -- postcondition: !valid()\n\n#define DOIT(CREATION_EXPR)    \\\n  do {                         \\\n    auto f1 = (CREATION_EXPR); \\\n    EXPECT_FALSE(f1.valid());  \\\n    auto f2 = std::move(f1);   \\\n    EXPECT_FALSE(f1.valid());  \\\n    EXPECT_FALSE(f2.valid());  \\\n  } while (false)\n\n  DOIT(makeInvalid());\n  DOIT(SemiFuture<int>::makeEmpty());\n\n#undef DOIT\n}\n\nTEST(SemiFuture, lacksPreconditionValid) {\n  // Ops that don't throw FutureInvalid if !valid() --\n  // without precondition: valid()\n\n#define DOIT(STMT)         \\\n  do {                     \\\n    auto f = makeValid();  \\\n    {                      \\\n      STMT;                \\\n    }                      \\\n    copy(std::move(f));    \\\n    EXPECT_NO_THROW(STMT); \\\n  } while (false)\n\n  // .valid() itself\n  DOIT(f.valid());\n\n  // move-ctor - move-copy to local, copy(), pass-by-move-value\n  DOIT(auto other = std::move(f));\n  DOIT(copy(std::move(f)));\n  DOIT(([](auto) {})(std::move(f)));\n\n  // move-assignment into either {valid | invalid}\n  DOIT({\n    auto other = makeValid();\n    other = std::move(f);\n  });\n  DOIT({\n    auto other = makeInvalid();\n    other = std::move(f);\n  });\n\n#undef DOIT\n}\n\nTEST(SemiFuture, hasPreconditionValid) {\n  // Ops that require validity; precondition: valid();\n  // throw FutureInvalid if !valid()\n\n#define DOIT(STMT)                     \\\n  do {                                 \\\n    auto f = makeValid();              \\\n    EXPECT_NO_THROW(STMT);             \\\n    copy(std::move(f));                \\\n    EXPECT_THROW(STMT, FutureInvalid); \\\n  } while (false)\n\n  DOIT(f.isReady());\n  DOIT(f.result());\n  DOIT(std::move(f).getTry());\n  DOIT(f.hasValue());\n  DOIT(f.hasException());\n  DOIT(f.value());\n\n#undef DOIT\n}\n\nTEST(SemiFuture, hasPostconditionValid) {\n  // Ops that preserve validity -- postcondition: valid()\n\n#define DOIT(STMT)          \\\n  do {                      \\\n    auto f = makeValid();   \\\n    EXPECT_NO_THROW(STMT);  \\\n    EXPECT_TRUE(f.valid()); \\\n  } while (false)\n\n  auto const swallow = [](auto) {};\n\n  DOIT(swallow(f.valid())); // f.valid() itself preserves validity\n  DOIT(swallow(f.isReady()));\n  DOIT(swallow(f.hasValue()));\n  DOIT(swallow(f.hasException()));\n  DOIT(swallow(f.value()));\n  DOIT(swallow(f.poll()));\n  DOIT(f.raise(std::logic_error(\"foo\")));\n  DOIT(f.cancel());\n  DOIT(f.wait());\n  DOIT(std::move(f).wait());\n\n#undef DOIT\n}\n\nTEST(SemiFuture, hasPostconditionInvalid) {\n  // Ops that consume *this -- postcondition: !valid()\n\n#define DOIT(CTOR, STMT)     \\\n  do {                       \\\n    auto f = (CTOR);         \\\n    EXPECT_NO_THROW(STMT);   \\\n    EXPECT_FALSE(f.valid()); \\\n  } while (false)\n\n  // move-ctor of {valid|invalid}\n  DOIT(makeValid(), { auto other{std::move(f)}; });\n  DOIT(makeInvalid(), { auto other{std::move(f)}; });\n\n  // move-assignment of {valid|invalid} into {valid|invalid}\n  DOIT(makeValid(), {\n    auto other = makeValid();\n    other = std::move(f);\n  });\n  DOIT(makeValid(), {\n    auto other = makeInvalid();\n    other = std::move(f);\n  });\n  DOIT(makeInvalid(), {\n    auto other = makeValid();\n    other = std::move(f);\n  });\n  DOIT(makeInvalid(), {\n    auto other = makeInvalid();\n    other = std::move(f);\n  });\n\n  // pass-by-value of {valid|invalid}\n  DOIT(makeValid(), {\n    auto const byval = [](auto) {};\n    byval(std::move(f));\n  });\n  DOIT(makeInvalid(), {\n    auto const byval = [](auto) {};\n    byval(std::move(f));\n  });\n\n  // other consuming ops\n  auto const swallow = [](auto) {};\n  DOIT(makeValid(), swallow(std::move(f).get()));\n  DOIT(makeValid(), swallow(std::move(f).getTry()));\n  DOIT(makeValid(), swallow(std::move(f).wait()));\n  DOIT(makeValid(), swallow(std::move(f.wait())));\n\n#undef DOIT\n}\n\nnamespace {\nint onErrorHelperEggs(const eggs_t&) {\n  return 10;\n}\nint onErrorHelperGeneric(const folly::exception_wrapper&) {\n  return 20;\n}\n} // namespace\n\nTEST(SemiFuture, special) {\n  EXPECT_FALSE(std::is_copy_constructible<SemiFuture<int>>::value);\n  EXPECT_FALSE(std::is_copy_assignable<SemiFuture<int>>::value);\n  EXPECT_TRUE(std::is_move_constructible<SemiFuture<int>>::value);\n  EXPECT_TRUE(std::is_move_assignable<SemiFuture<int>>::value);\n}\n\nTEST(SemiFuture, value) {\n  auto f = makeSemiFuture(std::make_unique<int>(42));\n  auto up = std::move(f.value());\n  EXPECT_EQ(42, *up);\n\n  EXPECT_THROW(makeSemiFuture<int>(eggs).value(), eggs_t);\n\n  EXPECT_TYPE(std::declval<SemiFuture<int>&>().value(), int&);\n  EXPECT_TYPE(std::declval<SemiFuture<int> const&>().value(), int const&);\n  EXPECT_TYPE(std::declval<SemiFuture<int>&&>().value(), int&&);\n  EXPECT_TYPE(std::declval<SemiFuture<int> const&&>().value(), int const&&);\n}\n\nTEST(SemiFuture, hasException) {\n  EXPECT_TRUE(makeSemiFuture<int>(eggs).getTry().hasException());\n  EXPECT_FALSE(makeSemiFuture(42).getTry().hasException());\n}\n\nTEST(SemiFuture, hasValue) {\n  EXPECT_TRUE(makeSemiFuture(42).getTry().hasValue());\n  EXPECT_FALSE(makeSemiFuture<int>(eggs).getTry().hasValue());\n}\n\nTEST(SemiFuture, makeSemiFuture) {\n  EXPECT_TYPE(makeSemiFuture(42), SemiFuture<int>);\n  EXPECT_EQ(42, makeSemiFuture(42).value());\n\n  EXPECT_TYPE(makeSemiFuture<float>(42), SemiFuture<float>);\n  EXPECT_EQ(42, makeSemiFuture<float>(42).value());\n\n  auto fun = [] { return 42; };\n  EXPECT_TYPE(makeSemiFutureWith(fun), SemiFuture<int>);\n  EXPECT_EQ(42, makeSemiFutureWith(fun).value());\n\n  auto funf = [] { return makeSemiFuture<int>(43); };\n  EXPECT_TYPE(makeSemiFutureWith(funf), SemiFuture<int>);\n  EXPECT_EQ(43, makeSemiFutureWith(funf).value());\n\n  auto failfun = []() -> int { throw eggs; };\n  EXPECT_TYPE(makeSemiFutureWith(failfun), SemiFuture<int>);\n  EXPECT_NO_THROW(makeSemiFutureWith(failfun));\n  EXPECT_THROW(makeSemiFutureWith(failfun).value(), eggs_t);\n\n  auto failfunf = []() -> SemiFuture<int> { throw eggs; };\n  EXPECT_TYPE(makeSemiFutureWith(failfunf), SemiFuture<int>);\n  EXPECT_NO_THROW(makeSemiFutureWith(failfunf));\n  EXPECT_THROW(makeSemiFutureWith(failfunf).value(), eggs_t);\n\n  auto futurefun = [] { return makeFuture<int>(44); };\n  EXPECT_TYPE(makeSemiFutureWith(futurefun), SemiFuture<int>);\n  EXPECT_EQ(44, makeSemiFutureWith(futurefun).value());\n\n  EXPECT_TYPE(makeSemiFuture(), SemiFuture<Unit>);\n}\n\nTEST(SemiFuture, Constructor) {\n  auto f1 = []() -> SemiFuture<int> { return SemiFuture<int>(3); }();\n  EXPECT_EQ(f1.value(), 3);\n  auto f2 = []() -> SemiFuture<Unit> { return SemiFuture<Unit>(); }();\n  EXPECT_NO_THROW(f2.value());\n}\n\nTEST(SemiFuture, ImplicitConstructor) {\n  auto f1 = []() -> SemiFuture<int> { return 3; }();\n  EXPECT_EQ(f1.value(), 3);\n}\n\nTEST(SemiFuture, InPlaceConstructor) {\n  auto f = SemiFuture<std::pair<int, double>>(std::in_place, 5, 3.2);\n  EXPECT_EQ(5, f.value().first);\n}\n\nTEST(SemiFuture, makeSemiFutureNoThrow) {\n  makeSemiFuture().value();\n}\n\nTEST(SemiFuture, ViaThrowOnNull) {\n  EXPECT_THROW(makeSemiFuture().via(nullptr), FutureNoExecutor);\n}\n\nTEST(SemiFuture, ConstructSemiFutureFromEmptyFuture) {\n  auto f = SemiFuture<int>{Future<int>::makeEmpty()};\n  EXPECT_THROW(f.isReady(), FutureInvalid);\n}\n\nTEST(SemiFuture, ConstructSemiFutureFromFutureDefaultCtor) {\n  SemiFuture<Unit>(Future<Unit>{});\n}\n\nTEST(SemiFuture, MakeSemiFutureFromFutureWithUnit) {\n  int count = 0;\n  SemiFuture<Unit> fu = SemiFuture<Unit>{makeFutureWith([&] { count++; })};\n  EXPECT_EQ(1, count);\n}\n\nTEST(SemiFuture, MakeSemiFutureFromFutureWithValue) {\n  auto f =\n      SemiFuture<std::unique_ptr<int>>{makeFuture(std::make_unique<int>(42))};\n  auto up = std::move(f.value());\n  EXPECT_EQ(42, *up);\n}\n\nTEST(SemiFuture, MakeSemiFutureFromReadyFuture) {\n  Promise<int> p;\n  auto f = p.getSemiFuture();\n  EXPECT_FALSE(f.isReady());\n  p.setValue(42);\n  EXPECT_TRUE(f.isReady());\n}\n\nTEST(SemiFuture, MakeSemiFutureFromNotReadyFuture) {\n  Promise<int> p;\n  auto f = p.getSemiFuture();\n  EXPECT_THROW(f.value(), eggs_t);\n}\n\nTEST(SemiFuture, MakeFutureFromSemiFuture) {\n  folly::EventBase e;\n  Promise<int> p;\n  std::atomic<int> result{0};\n  auto f = p.getSemiFuture();\n  auto future = std::move(f).via(&e).thenValue([&](int value) {\n    result = value;\n    return value;\n  });\n  e.loopOnce();\n  EXPECT_EQ(result, 0);\n  EXPECT_FALSE(future.isReady());\n  p.setValue(42);\n  e.loopOnce();\n  EXPECT_TRUE(future.isReady());\n  ASSERT_EQ(future.value(), 42);\n  ASSERT_EQ(result, 42);\n}\n\nTEST(SemiFuture, MakeFutureFromSemiFutureReturnFuture) {\n  folly::EventBase e;\n  Promise<int> p;\n  int result{0};\n  auto f = p.getSemiFuture();\n  auto future = std::move(f).via(&e).thenValue([&](int value) {\n    result = value;\n    return folly::makeFuture(std::move(value));\n  });\n  e.loopOnce();\n  EXPECT_EQ(result, 0);\n  EXPECT_FALSE(future.isReady());\n  p.setValue(42);\n  e.loopOnce();\n  EXPECT_TRUE(future.isReady());\n  ASSERT_EQ(future.value(), 42);\n  ASSERT_EQ(result, 42);\n}\n\nTEST(SemiFuture, MakeFutureFromSemiFutureReturnSemiFuture) {\n  folly::EventBase e;\n  Promise<int> p;\n  int result{0};\n  auto f = p.getSemiFuture();\n  auto future =\n      std::move(f)\n          .via(&e)\n          .thenValue([&](int value) {\n            result = value;\n            return folly::makeSemiFuture(std::move(value));\n          })\n          .thenValue([&](int value) {\n            return folly::makeSemiFuture(std::move(value));\n          });\n  e.loopOnce();\n  EXPECT_EQ(result, 0);\n  EXPECT_FALSE(future.isReady());\n  p.setValue(42);\n  e.loopOnce();\n  EXPECT_TRUE(future.isReady());\n  ASSERT_EQ(future.value(), 42);\n  ASSERT_EQ(result, 42);\n}\n\nTEST(SemiFuture, MakeFutureFromSemiFutureLValue) {\n  folly::EventBase e;\n  Promise<int> p;\n  std::atomic<int> result{0};\n  auto f = p.getSemiFuture();\n  auto future = std::move(f).via(&e).thenValue([&](int value) {\n    result = value;\n    return value;\n  });\n  e.loopOnce();\n  EXPECT_EQ(result, 0);\n  EXPECT_FALSE(future.isReady());\n  p.setValue(42);\n  e.loopOnce();\n  EXPECT_TRUE(future.isReady());\n  ASSERT_EQ(future.value(), 42);\n  ASSERT_EQ(result, 42);\n}\n\nTEST(SemiFuture, SimpleGet) {\n  EventBase e2;\n  Promise<int> p;\n  auto sf = p.getSemiFuture();\n  p.setValue(3);\n  auto v = std::move(sf).get();\n  ASSERT_EQ(v, 3);\n}\n\nTEST(SemiFuture, SimpleGetTry) {\n  EventBase e2;\n  Promise<int> p;\n  auto sf = p.getSemiFuture();\n  p.setValue(3);\n  auto v = std::move(sf).getTry();\n  ASSERT_EQ(v.value(), 3);\n}\n\nTEST(SemiFuture, SimpleTimedGet) {\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture();\n  EXPECT_THROW(\n      std::move(sf).get(std::chrono::milliseconds(100)), FutureTimeout);\n}\n\nTEST(SemiFuture, SimpleTimedGetViaFromSemiFuture) {\n  TimedDrivableExecutor e2;\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture();\n  EXPECT_THROW(\n      std::move(sf).via(&e2).getVia(&e2, std::chrono::milliseconds(100)),\n      FutureTimeout);\n}\n\nTEST(SemiFuture, SimpleTimedGetTry) {\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture();\n  EXPECT_THROW(\n      std::move(sf).getTry(std::chrono::milliseconds(100)), FutureTimeout);\n}\n\nTEST(SemiFuture, SimpleTimedGetTryViaFromSemiFuture) {\n  TimedDrivableExecutor e2;\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture();\n  EXPECT_THROW(\n      std::move(sf).via(&e2).getTryVia(&e2, std::chrono::milliseconds(100)),\n      FutureTimeout);\n}\n\nTEST(SemiFuture, SimpleValue) {\n  Promise<int> p;\n  auto sf = p.getSemiFuture();\n  p.setValue(3);\n  auto v = std::move(sf).value();\n  ASSERT_EQ(v, 3);\n}\n\nTEST(SemiFuture, SimpleValueThrow) {\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture();\n  EXPECT_THROW(std::move(sf).value(), FutureNotReady);\n}\n\nTEST(SemiFuture, SimpleResult) {\n  Promise<int> p;\n  auto sf = p.getSemiFuture();\n  p.setValue(3);\n  auto v = std::move(sf).result();\n  ASSERT_EQ(v.value(), 3);\n}\n\nTEST(SemiFuture, SimpleResultThrow) {\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture();\n  EXPECT_THROW(std::move(sf).result(), FutureNotReady);\n}\n\nTEST(SemiFuture, SimpleDefer) {\n  std::atomic<int> innerResult{0};\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture().defer([&](auto&&) { innerResult = 17; });\n  p.setValue();\n  // Run \"F\" here inline in the calling thread\n  std::move(sf).get();\n  ASSERT_EQ(innerResult, 17);\n}\n\nTEST(SemiFuture, DeferWithDelayedSetValue) {\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture().defer([&](auto&&) { return 17; });\n\n  // Start thread and have it blocking in the semifuture before we satisfy the\n  // promise\n  auto resultF = std::async(std::launch::async, [&]() {\n    return std::move(sf).get();\n  });\n\n  // Check that future is not already satisfied before setting the promise\n  // Async task should be blocked on sf.\n  ASSERT_EQ(\n      resultF.wait_for(std::chrono::milliseconds(100)),\n      std::future_status::timeout);\n  p.setValue();\n  ASSERT_EQ(resultF.get(), 17);\n}\n\nTEST(SemiFuture, DeferWithViaAndDelayedSetValue) {\n  EventBase e2;\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture().defer([&](auto&&) { return 17; }).via(&e2);\n  // Start thread and have it blocking in the semifuture before we satisfy the\n  // promise.\n  auto resultF = std::async(std::launch::async, [&]() {\n    return std::move(sf).get();\n  });\n  std::thread t([&]() { e2.loopForever(); });\n  // Check that future is not already satisfied before setting the promise\n  // Async task should be blocked on sf.\n  ASSERT_EQ(\n      resultF.wait_for(std::chrono::milliseconds(100)),\n      std::future_status::timeout);\n  p.setValue();\n  e2.terminateLoopSoon();\n  t.join();\n  ASSERT_EQ(resultF.get(), 17);\n}\n\nTEST(SemiFuture, DeferWithGetTimedGet) {\n  std::atomic<int> innerResult{0};\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture().defer([&](auto&&) { innerResult = 17; });\n  EXPECT_THROW(\n      std::move(sf).get(std::chrono::milliseconds(100)), FutureTimeout);\n  ASSERT_EQ(innerResult, 0);\n}\n\nTEST(SemiFuture, DeferWithVia) {\n  std::atomic<int> innerResult{0};\n  EventBase e2;\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture().defer([&](auto&&) { innerResult = 17; });\n  // Run \"F\" here inline in the calling thread\n  auto tf = std::move(sf).via(&e2);\n  p.setValue();\n  std::move(tf).getVia(&e2);\n  ASSERT_EQ(innerResult, 17);\n}\n\nTEST(SemiFuture, ChainingDefertoThen) {\n  std::atomic<int> innerResult{0};\n  std::atomic<int> result{0};\n  EventBase e2;\n  Promise<folly::Unit> p;\n  auto sf = p.getSemiFuture().defer([&](auto&&) { innerResult = 17; });\n  // Run \"F\" here inline in a task running on the eventbase\n  auto tf = std::move(sf).via(&e2).thenValue([&](auto&&) { result = 42; });\n  p.setValue();\n  std::move(tf).getVia(&e2);\n  ASSERT_EQ(innerResult, 17);\n  ASSERT_EQ(result, 42);\n}\n\nTEST(SemiFuture, SimpleDeferWithValue) {\n  std::atomic<int> innerResult{0};\n  Promise<int> p;\n  auto sf = p.getSemiFuture().deferValue([&](int a) { innerResult = a; });\n  p.setValue(7);\n  // Run \"F\" here inline in the calling thread\n  std::move(sf).get();\n  ASSERT_EQ(innerResult, 7);\n}\n\nnamespace {\nint deferValueHelper(int a) {\n  return a;\n}\n\n} // namespace\nTEST(SemiFuture, SimpleDeferWithValueFunctionReference) {\n  Promise<int> p;\n  auto sf = p.getSemiFuture().deferValue(deferValueHelper);\n  p.setValue(7);\n  // Run \"F\" here inline in the calling thread\n  ASSERT_EQ(std::move(sf).get(), 7);\n}\n\nTEST(SemiFuture, ChainingDefertoThenWithValue) {\n  std::atomic<int> innerResult{0};\n  std::atomic<int> result{0};\n  EventBase e2;\n  Promise<int> p;\n  auto sf = p.getSemiFuture().deferValue([&](int a) {\n    innerResult = a;\n    return a;\n  });\n  // Run \"F\" here inline in a task running on the eventbase\n  auto tf = std::move(sf).via(&e2).thenValue([&](int a) { result = a; });\n  p.setValue(7);\n  std::move(tf).getVia(&e2);\n  ASSERT_EQ(innerResult, 7);\n  ASSERT_EQ(result, 7);\n}\n\nTEST(SemiFuture, MakeSemiFutureFromFutureWithTry) {\n  Promise<int> p;\n  auto sf = p.getSemiFuture().defer([&](Try<int> t) {\n    if (auto err = t.tryGetExceptionObject<std::logic_error>()) {\n      return Try<std::string>(err->what());\n    }\n    return Try<std::string>(\n        make_exception_wrapper<std::logic_error>(\"Exception\"));\n  });\n  p.setException(make_exception_wrapper<std::logic_error>(\"Try\"));\n  auto tryResult = std::move(sf).get();\n  ASSERT_EQ(tryResult, \"Try\");\n}\n\nnamespace {\n[[noreturn]] void deferHelper(folly::Try<folly::Unit>&&) {\n  throw eggs;\n}\n} // namespace\n\nTEST(SemiFuture, DeferWithinContinuation) {\n  std::atomic<int> innerResult{0};\n  std::atomic<int> result{0};\n  EventBase e2;\n  Promise<int> p;\n  Promise<int> p2;\n  auto f = p.getSemiFuture().via(&e2);\n  auto resultF =\n      std::move(f).thenValue([&, p3 = std::move(p2)](int outer) mutable {\n        result = outer;\n        return makeSemiFuture<int>(std::move(outer))\n            .deferValue([&, p4 = std::move(p3)](int inner) mutable {\n              innerResult = inner;\n              p4.setValue(inner);\n              return inner;\n            });\n      });\n  p.setValue(7);\n  auto r = std::move(resultF).getVia(&e2);\n  ASSERT_EQ(r, 7);\n  ASSERT_EQ(innerResult, 7);\n  ASSERT_EQ(result, 7);\n}\n\nTEST(SemiFuture, DeferError) {\n  bool theFlag = false;\n  auto flag = [&] { theFlag = true; };\n#define EXPECT_FLAG()     \\\n  do {                    \\\n    EXPECT_TRUE(theFlag); \\\n    theFlag = false;      \\\n  } while (0)\n\n#define EXPECT_NO_FLAG()   \\\n  do {                     \\\n    EXPECT_FALSE(theFlag); \\\n    theFlag = false;       \\\n  } while (0)\n\n  // By reference\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, [&](eggs_t const& /* e */) {\n              flag();\n            });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  {\n    auto f =\n        makeSemiFuture()\n            .defer(deferHelper)\n            .deferError(tag_t<eggs_t>{}, [&](eggs_t const& /* e */) {\n              flag();\n            });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // By auto reference\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, [&](auto& /* e */) { flag(); });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // By value\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, [&](eggs_t /* e */) { flag(); });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // auto value\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, [&](auto /* e */) { flag(); });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // Polymorphic\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError(tag_t<std::exception>{}, [&](auto const& /* e */) {\n              flag();\n            });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // Non-exceptions\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw -1; })\n            .deferError(tag_t<int>{}, [&](auto /* e */) { flag(); });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // Mutable lambda\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, [&](auto const& /* e */) mutable {\n              flag();\n            });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // Function pointer\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) -> int { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, onErrorHelperEggs)\n            .deferError(onErrorHelperGeneric);\n    EXPECT_EQ(10, std::move(f).get());\n  }\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) -> int { throw std::runtime_error(\"test\"); })\n            .deferError(tag_t<eggs_t>{}, onErrorHelperEggs)\n            .deferError(onErrorHelperGeneric);\n    EXPECT_EQ(20, std::move(f).get());\n  }\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) -> int { throw std::runtime_error(\"test\"); })\n            .deferError(tag_t<eggs_t>{}, onErrorHelperEggs);\n    EXPECT_THROW(std::move(f).get(), std::runtime_error);\n  }\n\n  // No throw\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { return 42; })\n            .deferError(tag_t<eggs_t>{}, [&](auto& /* e */) {\n              flag();\n              return -1;\n            });\n    EXPECT_NO_FLAG();\n    EXPECT_EQ(42, std::move(f).get());\n    EXPECT_NO_FLAG();\n  }\n\n  // Catch different exception\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError(tag_t<std::runtime_error>{}, [&](auto const& /* e */) {\n              flag();\n            });\n    EXPECT_THROW(std::move(f).get(), eggs_t);\n    EXPECT_NO_FLAG();\n  }\n\n  // Returned value propagates\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) -> int { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, [&](auto const& /* e */) {\n              return 42;\n            });\n    EXPECT_EQ(42, std::move(f).get());\n  }\n\n  // Throw in callback\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) -> int { throw eggs; })\n            .deferError(tag_t<eggs_t>{}, [&](auto const& e) -> int {\n              throw e;\n            });\n    EXPECT_THROW(std::move(f).get(), eggs_t);\n  }\n\n  // exception_wrapper, return T\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) -> int { throw eggs; })\n            .deferError([&](exception_wrapper /* e */) {\n              flag();\n              return -1;\n            });\n    EXPECT_EQ(-1, std::move(f).get());\n    EXPECT_FLAG();\n  }\n\n  // exception_wrapper, return T but throw\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) -> int { throw eggs; })\n            .deferError([&](exception_wrapper /* e */) -> int {\n              flag();\n              throw eggs;\n            });\n    EXPECT_THROW(std::move(f).get(), eggs_t);\n    EXPECT_FLAG();\n  }\n\n  // const exception_wrapper&\n  {\n    auto f =\n        makeSemiFuture()\n            .defer([](auto&&) { throw eggs; })\n            .deferError([&](const exception_wrapper& /* e */) { flag(); });\n    EXPECT_NO_THROW(std::move(f).get());\n    EXPECT_FLAG();\n  }\n}\n\nTEST(SemiFuture, makePromiseContract) {\n  auto [p, f] = makePromiseContract<int>();\n  p.setValue(3);\n  f = std::move(f).deferValue([](int _) { return _ + 1; });\n  EXPECT_EQ(4, std::move(f).get());\n}\n\nTEST(SemiFuture, invokeCallbackWithOriginalCVRef) {\n  struct Foo {\n    int operator()(int x) & { return x + 1; }\n    int operator()(int x) const& { return x + 2; }\n    int operator()(int x) && { return x + 3; }\n  };\n\n  Foo foo;\n  Foo const cfoo;\n\n  // The continuation will be forward-constructed - copied if given as & and\n  // moved if given as && - everywhere construction is required.\n  // The continuation will be invoked with the same cvref as it is passed.\n  EXPECT_EQ(101, makeSemiFuture<int>(100).deferValue(foo).wait().value());\n  EXPECT_EQ(202, makeSemiFuture<int>(200).deferValue(cfoo).wait().value());\n  EXPECT_EQ(303, makeSemiFuture<int>(300).deferValue(Foo()).wait().value());\n}\n\nTEST(SemiFuture, semiFutureWithinCtxCleanedUpWhenTaskFinishedInTime) {\n  // Used to track the use_count of callbackInput even outside of its scope\n  std::weak_ptr<int> target;\n  {\n    Promise<std::shared_ptr<int>> promise;\n    auto input = std::make_shared<int>(1);\n    auto longEnough = std::chrono::milliseconds(1000);\n\n    promise.getSemiFuture()\n        .within(longEnough)\n        .toUnsafeFuture()\n        .thenTry([&target](\n                     folly::Try<std::shared_ptr<int>>&& callbackInput) mutable {\n          target = callbackInput.value();\n        });\n    promise.setValue(input);\n  }\n  // After promise's life cycle is finished, make sure no one is holding the\n  // input anymore, in other words, ctx should have been cleaned up.\n  EXPECT_EQ(0, target.use_count());\n}\n\nTEST(SemiFuture, semiFutureWithinNoValueReferenceWhenTimeOut) {\n  Promise<std::shared_ptr<int>> promise;\n  auto veryShort = std::chrono::milliseconds(1);\n\n  auto f = promise.getSemiFuture().within(veryShort).toUnsafeFuture().thenTry(\n      [](folly::Try<std::shared_ptr<int>>&& callbackInput) {\n        EXPECT_THROW(callbackInput.throwUnlessValue(), FutureTimeout);\n      });\n  std::move(f).get();\n}\n\nTEST(SemiFuture, collectAllDeferredWork) {\n  {\n    Promise<int> promise1;\n    Promise<int> promise2;\n\n    auto future = collectAll(\n        promise1.getSemiFuture().deferValue([](int x) { return x * 2; }),\n        promise2.getSemiFuture().deferValue([](int x) { return x * 2; }));\n\n    promise1.setValue(1);\n    promise2.setValue(2);\n\n    auto result = std::move(future).getTry(std::chrono::milliseconds{100});\n\n    EXPECT_TRUE(result.hasValue());\n\n    EXPECT_EQ(2, *std::get<0>(*result));\n    EXPECT_EQ(4, *std::get<1>(*result));\n  }\n\n  {\n    Promise<int> promise1;\n    Promise<int> promise2;\n\n    auto future = collectAll(\n        promise1.getSemiFuture().deferValue([](int x) { return x * 2; }),\n        promise2.getSemiFuture().deferValue([](int x) { return x * 2; }));\n\n    promise1.setValue(1);\n    promise2.setValue(2);\n\n    ManualExecutor executor;\n\n    auto value = std::move(future).via(&executor).getVia(&executor);\n\n    EXPECT_EQ(2, *std::get<0>(value));\n    EXPECT_EQ(4, *std::get<1>(value));\n  }\n\n  {\n    Promise<int> promise1;\n    Promise<int> promise2;\n\n    std::vector<SemiFuture<int>> futures;\n    futures.push_back(promise1.getSemiFuture().deferValue([](int x) {\n      return x * 2;\n    }));\n    futures.push_back(promise2.getSemiFuture().deferValue([](int x) {\n      return x * 2;\n    }));\n\n    auto future = collectAll(futures);\n\n    promise1.setValue(1);\n    promise2.setValue(2);\n\n    EXPECT_TRUE(future.wait().isReady());\n\n    auto value = std::move(future).get();\n    EXPECT_EQ(2, *value[0]);\n    EXPECT_EQ(4, *value[1]);\n  }\n\n  {\n    bool deferredDestroyed = false;\n\n    {\n      Promise<int> promise;\n      auto guard = makeGuard([&] { deferredDestroyed = true; });\n      collectAll(promise.getSemiFuture().deferValue(\n          [guard = std::move(guard)](int x) { return x; }));\n    }\n\n    EXPECT_TRUE(deferredDestroyed);\n  }\n}\n\nTEST(SemiFuture, collectDeferredWork) {\n  {\n    Promise<int> promise1;\n    Promise<int> promise2;\n\n    auto future = collect(\n        promise1.getSemiFuture().deferValue([](int x) { return x * 2; }),\n        promise2.getSemiFuture().deferValue([](int x) { return x * 2; }));\n\n    promise1.setValue(1);\n    promise2.setValue(2);\n\n    auto result = std::move(future).getTry(std::chrono::milliseconds{100});\n\n    EXPECT_TRUE(result.hasValue());\n\n    EXPECT_EQ(2, std::get<0>(*result));\n    EXPECT_EQ(4, std::get<1>(*result));\n  }\n\n  {\n    Promise<int> promise1;\n    Promise<int> promise2;\n\n    auto future = collect(\n        promise1.getSemiFuture().deferValue([](int x) { return x * 2; }),\n        promise2.getSemiFuture().deferValue([](int x) { return x * 2; }));\n\n    promise1.setValue(1);\n    promise2.setValue(2);\n\n    ManualExecutor executor;\n\n    auto value = std::move(future).via(&executor).getVia(&executor);\n\n    EXPECT_EQ(2, std::get<0>(value));\n    EXPECT_EQ(4, std::get<1>(value));\n  }\n\n  {\n    Promise<int> promise1;\n    Promise<int> promise2;\n\n    std::vector<SemiFuture<int>> futures;\n    futures.push_back(promise1.getSemiFuture().deferValue([](int x) {\n      return x * 2;\n    }));\n    futures.push_back(promise2.getSemiFuture().deferValue([](int x) {\n      return x * 2;\n    }));\n\n    auto future = collect(futures);\n\n    promise1.setValue(1);\n    promise2.setValue(2);\n\n    EXPECT_TRUE(future.wait().isReady());\n\n    auto value = std::move(future).get();\n    EXPECT_EQ(2, value[0]);\n    EXPECT_EQ(4, value[1]);\n  }\n\n  {\n    bool deferredDestroyed = false;\n\n    {\n      Promise<int> promise;\n      auto guard = makeGuard([&] { deferredDestroyed = true; });\n      collect(promise.getSemiFuture().deferValue(\n          [guard = std::move(guard)](int x) { return x; }));\n    }\n\n    EXPECT_TRUE(deferredDestroyed);\n  }\n}\n\nTEST(SemiFuture, collectNDeferredWork) {\n  Promise<int> promise1;\n  Promise<int> promise2;\n  Promise<int> promise3;\n\n  std::vector<SemiFuture<int>> futures;\n  futures.push_back(promise1.getSemiFuture().deferValue([](int x) {\n    return x * 2;\n  }));\n  futures.push_back(promise2.getSemiFuture().deferValue([](int x) {\n    return x * 2;\n  }));\n  futures.push_back(promise3.getSemiFuture().deferValue([](int x) {\n    return x * 2;\n  }));\n\n  auto future = collectN(std::move(futures), 2);\n\n  promise1.setValue(1);\n  promise3.setValue(3);\n\n  EXPECT_TRUE(future.wait().isReady());\n\n  auto value = std::move(future).get();\n  EXPECT_EQ(2, *value[0].second);\n  EXPECT_EQ(6, *value[1].second);\n}\n\nTEST(SemiFuture, DeferWithNestedSemiFuture) {\n  auto start = std::chrono::steady_clock::now();\n  auto future =\n      futures::sleep(std::chrono::milliseconds{100}).deferValue([](auto&&) {\n        return futures::sleep(std::chrono::milliseconds{200});\n      });\n  future.wait();\n  EXPECT_TRUE(future.hasValue());\n  EXPECT_GE(\n      std::chrono::steady_clock::now() - start, std::chrono::milliseconds{300});\n}\n\nTEST(SemiFuture, DeferWithExecutor) {\n  ManualExecutor executor;\n  auto sf = makeSemiFuture().deferExTry(\n      [&](const Executor::KeepAlive<>& e, Try<Unit>) {\n        EXPECT_EQ(&executor, e.get());\n      });\n  std::move(sf).via(&executor).getVia(&executor);\n}\n\nTEST(SemiFuture, DeferValueWithExecutor) {\n  ManualExecutor executor;\n  auto sf = makeSemiFuture(42).deferExValue(\n      [&](const Executor::KeepAlive<>& e, int val) {\n        EXPECT_EQ(&executor, e.get());\n        EXPECT_EQ(val, 42);\n      });\n  std::move(sf).via(&executor).getVia(&executor);\n}\n\nTEST(SemiFuture, within) {\n  {\n    auto sf =\n        makeSemiFuture(42)\n            .deferValue([](int x) { return x / 2; })\n            .within(std::chrono::seconds{10})\n            .deferValue([](int x) { return x * 2; });\n    EXPECT_EQ(42, std::move(sf).get());\n  }\n  {\n    folly::Promise<folly::Unit> p;\n    auto sf =\n        p.getSemiFuture()\n            .deferValue([](auto) {\n              CHECK(false);\n              return -1;\n            })\n            .within(std::chrono::seconds{1})\n            .deferError(tag_t<FutureTimeout>{}, [](auto) { return 42; });\n    EXPECT_EQ(42, std::move(sf).get());\n    p.setValue();\n  }\n}\n\nTEST(SemiFuture, ensure) {\n  {\n    bool fCalled{false};\n    bool ensureCalled{false};\n    auto sf = futures::ensure(\n        [&] {\n          fCalled = true;\n          return 42;\n        },\n        [&] { ensureCalled = true; });\n    EXPECT_EQ(42, std::move(sf).get());\n    EXPECT_TRUE(fCalled);\n    EXPECT_TRUE(ensureCalled);\n  }\n  {\n    bool fCalled{false};\n    bool ensureCalled{false};\n    futures::ensure(\n        [&] {\n          fCalled = true;\n          return 42;\n        },\n        [&] { ensureCalled = true; });\n    EXPECT_FALSE(fCalled);\n    EXPECT_FALSE(ensureCalled);\n  }\n  struct ExpectedException : public std::exception {};\n  {\n    bool fCalled{false};\n    bool ensureCalled{false};\n    auto sf = futures::ensure(\n        [&] {\n          fCalled = true;\n          throw ExpectedException();\n        },\n        [&] { ensureCalled = true; });\n    EXPECT_THROW(std::move(sf).get(), ExpectedException);\n    EXPECT_TRUE(fCalled);\n    EXPECT_TRUE(ensureCalled);\n  }\n  {\n    bool ensureCalled{false};\n    auto sf = makeSemiFuture(42).deferEnsure([&] { ensureCalled = true; });\n    EXPECT_FALSE(ensureCalled);\n    EXPECT_EQ(42, std::move(sf).get());\n    EXPECT_TRUE(ensureCalled);\n  }\n  {\n    bool ensureCalled{false};\n    auto sf = makeSemiFuture().defer([](auto) { return 42; }).deferEnsure([&] {\n      ensureCalled = true;\n    });\n    EXPECT_FALSE(ensureCalled);\n    EXPECT_EQ(42, std::move(sf).get());\n    EXPECT_TRUE(ensureCalled);\n  }\n  {\n    bool ensureCalled{false};\n    auto sf =\n        makeSemiFuture()\n            .defer([](auto) { throw ExpectedException(); })\n            .deferEnsure([&] { ensureCalled = true; });\n    EXPECT_FALSE(ensureCalled);\n    EXPECT_THROW(std::move(sf).get(), ExpectedException);\n    EXPECT_TRUE(ensureCalled);\n  }\n}\n\nTEST(SemiFuture, deferredExecutorInlineTest) {\n  bool a = false, b = false, c = false;\n  auto manualExec1 = ManualExecutor{};\n  auto manualExec1KA = getKeepAliveToken(manualExec1);\n  auto manualExec2 = ManualExecutor{};\n  auto manualExec2KA = getKeepAliveToken(manualExec2);\n  auto dw = futures::detail::DeferredExecutor::create();\n  auto* de = dw.get();\n  de->setExecutor(manualExec1KA);\n  de->addFrom(Executor::KeepAlive<>{}, [&](auto&&) { a = true; });\n  EXPECT_FALSE(a);\n  manualExec1.run();\n  EXPECT_TRUE(a);\n  de->addFrom(manualExec2KA.copy(), [&](auto&&) { b = true; });\n  EXPECT_FALSE(b);\n  manualExec1.run();\n  EXPECT_TRUE(b);\n  de->addFrom(manualExec1KA.copy(), [&](auto&&) { c = true; });\n  EXPECT_TRUE(c);\n}\n\nTEST(Future, makeSemiFutureFromMoveOnlyException) {\n#ifdef __APPLE__\n  // On macOS, std::make_exception_ptr copies the exception internally,\n  // causing this test to fail. Skip until a proper fix is implemented.\n  GTEST_SKIP() << \"Test disabled on macOS due to exception_ptr copy behavior\";\n#endif\n  using ::testing::StrEq;\n  using ::testing::ThrowsMessage;\n\n  struct MoveOnlyException : std::runtime_error {\n    using std::runtime_error::runtime_error;\n    [[noreturn]] MoveOnlyException(const MoveOnlyException& other)\n        : std::runtime_error(other) {\n      throw std::logic_error(\"Copy constructor is called\");\n    }\n    MoveOnlyException(MoveOnlyException&&) = default;\n    [[maybe_unused]] MoveOnlyException& operator=(MoveOnlyException const&) {\n      throw std::logic_error(\"Copy assignment operator is called\");\n    }\n    [[maybe_unused]] MoveOnlyException& operator=(MoveOnlyException&&) =\n        default;\n  };\n\n  std::string msg = \"exception message\";\n\n  auto f = makeSemiFuture<int>(MoveOnlyException(msg));\n  EXPECT_THAT([&] { f.value(); }, ThrowsMessage<MoveOnlyException>(StrEq(msg)));\n}\n\nTEST(Future, makeSemiFutureFromExceptionSpecifyingBothTemplateParams) {\n  using ::testing::StrEq;\n  using ::testing::ThrowsMessage;\n\n  std::string msg = \"exception message\";\n\n  auto f = makeSemiFuture<int, std::runtime_error>(std::runtime_error(msg));\n  EXPECT_THAT(\n      [&] { f.value(); }, ThrowsMessage<std::runtime_error>(StrEq(msg)));\n}\n"
  },
  {
    "path": "folly/futures/test/SharedPromiseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/SharedPromise.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(SharedPromise, setGetSemiFuture) {\n  SharedPromise<int> p;\n  p.setValue(1);\n  auto f1 = p.getSemiFuture();\n  auto f2 = p.getSemiFuture();\n  EXPECT_EQ(1, f1.value());\n  EXPECT_EQ(1, f2.value());\n}\n\nTEST(SharedPromise, setGetMixed) {\n  SharedPromise<int> p;\n  p.setValue(1);\n  auto f1 = p.getSemiFuture();\n  auto f2 = p.getFuture();\n  EXPECT_EQ(1, f1.value());\n  EXPECT_EQ(1, f2.value());\n}\n\nTEST(SharedPromise, setGet) {\n  SharedPromise<int> p;\n  p.setValue(1);\n  auto f1 = p.getFuture();\n  auto f2 = p.getFuture();\n  EXPECT_EQ(1, f1.value());\n  EXPECT_EQ(1, f2.value());\n}\nTEST(SharedPromise, getSet) {\n  SharedPromise<int> p;\n  auto f1 = p.getFuture();\n  auto f2 = p.getFuture();\n  p.setValue(1);\n  EXPECT_EQ(1, f1.value());\n  EXPECT_EQ(1, f2.value());\n}\n\nTEST(SharedPromise, getSetGet) {\n  SharedPromise<int> p;\n  auto f1 = p.getFuture();\n  p.setValue(1);\n  auto f2 = p.getFuture();\n  EXPECT_EQ(1, f1.value());\n  EXPECT_EQ(1, f2.value());\n}\n\nTEST(SharedPromise, reset) {\n  SharedPromise<int> p;\n\n  auto f1 = p.getFuture();\n  p.setValue(1);\n  EXPECT_EQ(1, f1.value());\n\n  p = SharedPromise<int>();\n  auto f2 = p.getFuture();\n  EXPECT_FALSE(f2.isReady());\n  p.setValue(2);\n  EXPECT_EQ(2, f2.value());\n}\n\nTEST(SharedPromise, getMoveSet) {\n  SharedPromise<int> p;\n  auto f = p.getFuture();\n  auto p2 = std::move(p);\n  p2.setValue(1);\n  EXPECT_EQ(1, f.value());\n}\n\nTEST(SharedPromise, setMoveGet) {\n  SharedPromise<int> p;\n  p.setValue(1);\n  auto p2 = std::move(p);\n  auto f = p2.getFuture();\n  EXPECT_EQ(1, f.value());\n}\n\nTEST(SharedPromise, moveSetGet) {\n  SharedPromise<int> p;\n  auto p2 = std::move(p);\n  p2.setValue(1);\n  auto f = p2.getFuture();\n  EXPECT_EQ(1, f.value());\n}\n\nTEST(SharedPromise, moveGetSet) {\n  SharedPromise<int> p;\n  auto p2 = std::move(p);\n  auto f = p2.getFuture();\n  p2.setValue(1);\n  EXPECT_EQ(1, f.value());\n}\n\nTEST(SharedPromise, moveMove) {\n  SharedPromise<std::shared_ptr<int>> p;\n  auto f1 = p.getFuture();\n  auto f2 = p.getFuture();\n  auto p2 = std::move(p);\n  EXPECT_EQ(2, p2.size());\n  p = std::move(p2);\n  EXPECT_EQ(0, p2.size());\n  p.setValue(std::make_shared<int>(1));\n}\n\nTEST(SharedPromise, setWith) {\n  SharedPromise<int> p;\n  p.setWith([] { return 1; });\n  EXPECT_EQ(1, p.getFuture().value());\n}\n\nTEST(SharedPromise, isFulfilled) {\n  SharedPromise<int> p;\n  EXPECT_FALSE(p.isFulfilled());\n  auto p2 = std::move(p);\n  EXPECT_FALSE(p2.isFulfilled());\n  p2.setValue(1);\n  EXPECT_TRUE(p2.isFulfilled());\n  p = std::move(p2);\n  EXPECT_TRUE(p.isFulfilled());\n  EXPECT_FALSE(p2.isFulfilled());\n}\n\nTEST(SharedPromise, interruptHandler) {\n  SharedPromise<int> p;\n  bool flag = false;\n  p.setInterruptHandler([&](const exception_wrapper&) { flag = true; });\n  auto f = p.getFuture();\n  f.cancel();\n  EXPECT_TRUE(flag);\n}\n\nTEST(SharedPromise, ConstMethods) {\n  SharedPromise<int> p;\n  EXPECT_FALSE(std::as_const(p).isFulfilled());\n  auto fut = std::as_const(p).getFuture();\n  EXPECT_FALSE(fut.isReady());\n  p.setValue(42);\n  EXPECT_TRUE(fut.isReady());\n  EXPECT_EQ(42, std::move(fut).get());\n}\n\nTEST(SharedPromise, InterruptHandlerSetsException) {\n  folly::SharedPromise<int> p;\n  p.setInterruptHandler([&](auto&& ew) { p.setException(ew); });\n  auto f = p.getSemiFuture();\n  f.cancel();\n  ASSERT_TRUE(f.isReady());\n  EXPECT_THROW(std::move(f).get(), FutureCancellation);\n}\n"
  },
  {
    "path": "folly/futures/test/TestExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/test/TestExecutor.h>\n\nusing namespace std;\n\nnamespace folly {\n\nTestExecutor::TestExecutor(size_t numThreads) {\n  const auto kWorkers = std::max(size_t(1), numThreads);\n  for (auto idx = 0u; idx < kWorkers; ++idx) {\n    workers_.emplace_back([this] {\n      while (true) {\n        Func work;\n        {\n          unique_lock<mutex> lk(m_);\n          cv_.wait(lk, [this] { return !workItems_.empty(); });\n          work = std::move(workItems_.front());\n          workItems_.pop();\n        }\n        if (!work) {\n          break;\n        }\n        work();\n      }\n    });\n  }\n}\n\nTestExecutor::~TestExecutor() {\n  for (auto& worker : workers_) {\n    (void)worker;\n    addImpl({});\n  }\n\n  for (auto& worker : workers_) {\n    worker.join();\n  }\n}\n\nvoid TestExecutor::add(Func f) {\n  if (f) {\n    addImpl(std::move(f));\n  }\n}\n\nsize_t TestExecutor::numThreads() const {\n  return workers_.size();\n}\n\nvoid TestExecutor::addImpl(Func f) {\n  {\n    lock_guard<mutex> g(m_);\n    workItems_.push(std::move(f));\n  }\n  cv_.notify_one();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/TestExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <condition_variable>\n#include <queue>\n#include <thread>\n\n#include <folly/Executor.h>\n\nnamespace folly {\n\n/**\n * A simple multithreaded executor for use in tests etc\n */\nclass TestExecutor : public Executor {\n public:\n  explicit TestExecutor(size_t numThreads);\n\n  ~TestExecutor() override;\n\n  void add(Func f) override;\n\n  size_t numThreads() const;\n\n private:\n  void addImpl(Func f);\n\n  std::mutex m_;\n  std::queue<Func> workItems_;\n  std::condition_variable cv_;\n\n  std::vector<std::thread> workers_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/TestExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/test/TestExecutor.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace std;\nusing namespace std::chrono;\nusing namespace folly;\n\nTEST(TestExecutor, parallelRun) {\n  mutex m;\n  set<thread::id> ids;\n  auto executor = std::make_unique<TestExecutor>(4);\n  const auto numThreads = executor->numThreads();\n  EXPECT_EQ(4, numThreads);\n  for (auto idx = 0U; idx < numThreads * 10; ++idx) {\n    executor->add([&m, &ids]() mutable {\n      /* sleep override */ this_thread::sleep_for(milliseconds(100));\n      lock_guard<mutex> lg(m);\n      ids.insert(this_thread::get_id());\n    });\n  }\n\n  executor = nullptr;\n  EXPECT_EQ(ids.size(), numThreads);\n}\n"
  },
  {
    "path": "folly/futures/test/ThenCompileTest.cpp",
    "content": "// This file is @generated by then_compile_test.rb. Do not edit directly.\n\n#include <folly/futures/test/ThenCompileTest.h>\n\nusing namespace folly;\n\nTEST(Basic, thenVariants) {\n  SomeClass anObject;\n\n  { Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>&&>); }\n  {\n    Future<B> f =\n        someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>&&>);\n  }\n  { Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>&&>()); }\n  {\n    Future<B> f =\n        someFuture<A>().then([&](Try<A>&&) { return someFuture<B>(); });\n  }\n  { Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A> const&>); }\n  {\n    Future<B> f = someFuture<A>().then(\n        &SomeClass::aStaticMethod<Future<B>, Try<A> const&>);\n  }\n  {\n    Future<B> f =\n        someFuture<A>().then(aStdFunction<Future<B>, Try<A> const&>());\n  }\n  {\n    Future<B> f =\n        someFuture<A>().then([&](Try<A> const&) { return someFuture<B>(); });\n  }\n  { Future<B> f = someFuture<A>().then(&aFunction<Future<B>, Try<A>>); }\n  {\n    Future<B> f =\n        someFuture<A>().then(&SomeClass::aStaticMethod<Future<B>, Try<A>>);\n  }\n  { Future<B> f = someFuture<A>().then(aStdFunction<Future<B>, Try<A>>()); }\n  {\n    Future<B> f = someFuture<A>().then([&](Try<A>) { return someFuture<B>(); });\n  }\n  { Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>&&>); }\n  {\n    Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>&&>);\n  }\n  { Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>&&>()); }\n  {\n    Future<B> f = someFuture<A>().then([&](Try<A>&&) { return B(); });\n  }\n  { Future<B> f = someFuture<A>().then(&aFunction<B, Try<A> const&>); }\n  {\n    Future<B> f =\n        someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A> const&>);\n  }\n  { Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A> const&>()); }\n  {\n    Future<B> f = someFuture<A>().then([&](Try<A> const&) { return B(); });\n  }\n  { Future<B> f = someFuture<A>().then(&aFunction<B, Try<A>>); }\n  { Future<B> f = someFuture<A>().then(&SomeClass::aStaticMethod<B, Try<A>>); }\n  { Future<B> f = someFuture<A>().then(aStdFunction<B, Try<A>>()); }\n  {\n    Future<B> f = someFuture<A>().then([&](Try<A>) { return B(); });\n  }\n  { Future<B> f = someFuture<A>().thenValue(&aFunction<Future<B>, A&&>); }\n  {\n    Future<B> f =\n        someFuture<A>().thenValue(&SomeClass::aStaticMethod<Future<B>, A&&>);\n  }\n  { Future<B> f = someFuture<A>().thenValue(aStdFunction<Future<B>, A&&>()); }\n  {\n    Future<B> f =\n        someFuture<A>().thenValue([&](A&&) { return someFuture<B>(); });\n  }\n  { Future<B> f = someFuture<A>().thenValue(&aFunction<Future<B>, A const&>); }\n  {\n    Future<B> f = someFuture<A>().thenValue(\n        &SomeClass::aStaticMethod<Future<B>, A const&>);\n  }\n  {\n    Future<B> f =\n        someFuture<A>().thenValue(aStdFunction<Future<B>, A const&>());\n  }\n  {\n    Future<B> f =\n        someFuture<A>().thenValue([&](A const&) { return someFuture<B>(); });\n  }\n  { Future<B> f = someFuture<A>().thenValue(&aFunction<Future<B>, A>); }\n  {\n    Future<B> f =\n        someFuture<A>().thenValue(&SomeClass::aStaticMethod<Future<B>, A>);\n  }\n  { Future<B> f = someFuture<A>().thenValue(aStdFunction<Future<B>, A>()); }\n  {\n    Future<B> f = someFuture<A>().thenValue([&](A) { return someFuture<B>(); });\n  }\n  { Future<B> f = someFuture<A>().thenValue(&aFunction<B, A&&>); }\n  {\n    Future<B> f = someFuture<A>().thenValue(&SomeClass::aStaticMethod<B, A&&>);\n  }\n  { Future<B> f = someFuture<A>().thenValue(aStdFunction<B, A&&>()); }\n  {\n    Future<B> f = someFuture<A>().thenValue([&](A&&) { return B(); });\n  }\n  { Future<B> f = someFuture<A>().thenValue(&aFunction<B, A const&>); }\n  {\n    Future<B> f =\n        someFuture<A>().thenValue(&SomeClass::aStaticMethod<B, A const&>);\n  }\n  { Future<B> f = someFuture<A>().thenValue(aStdFunction<B, A const&>()); }\n  {\n    Future<B> f = someFuture<A>().thenValue([&](A const&) { return B(); });\n  }\n  { Future<B> f = someFuture<A>().thenValue(&aFunction<B, A>); }\n  { Future<B> f = someFuture<A>().thenValue(&SomeClass::aStaticMethod<B, A>); }\n  { Future<B> f = someFuture<A>().thenValue(aStdFunction<B, A>()); }\n  {\n    Future<B> f = someFuture<A>().thenValue([&](A) { return B(); });\n  }\n  {\n    Future<B> f = someFuture<A>().then(\n        &SomeClass::aMethod<Future<B>, Try<A>&&>, &anObject);\n  }\n  {\n    Future<B> f = someFuture<A>().then(\n        &SomeClass::aMethod<Future<B>, Try<A> const&>, &anObject);\n  }\n  {\n    Future<B> f =\n        someFuture<A>().then(&SomeClass::aMethod<Future<B>, Try<A>>, &anObject);\n  }\n  {\n    Future<B> f =\n        someFuture<A>().then(&SomeClass::aMethod<B, Try<A>&&>, &anObject);\n  }\n  {\n    Future<B> f =\n        someFuture<A>().then(&SomeClass::aMethod<B, Try<A> const&>, &anObject);\n  }\n  {\n    Future<B> f =\n        someFuture<A>().then(&SomeClass::aMethod<B, Try<A>>, &anObject);\n  }\n}\n"
  },
  {
    "path": "folly/futures/test/ThenCompileTest.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nusing A = std::unique_ptr<int>;\nstruct B {};\n\ntemplate <class T>\nusing EnableIfFuture = typename std::enable_if<isFuture<T>::value>::type;\n\ntemplate <class T>\nusing EnableUnlessFuture = typename std::enable_if<!isFuture<T>::value>::type;\n\ntemplate <class T>\nFuture<T> someFuture() {\n  return makeFuture(T());\n}\n\ntemplate <class Ret, class... Params>\ntypename std::enable_if<isFuture<Ret>::value, Ret>::type aFunction(Params...) {\n  using T = typename Ret::value_type;\n  return makeFuture(T());\n}\n\ntemplate <class Ret, class... Params>\ntypename std::enable_if<!isFuture<Ret>::value, Ret>::type aFunction(Params...) {\n  return Ret();\n}\n\ntemplate <class Ret, class... Params>\nstd::function<Ret(Params...)> aStdFunction(\n    typename std::enable_if<!isFuture<Ret>::value, bool>::type = false) {\n  return [](Params...) -> Ret { return Ret(); };\n}\n\ntemplate <class Ret, class... Params>\nstd::function<Ret(Params...)> aStdFunction(\n    typename std::enable_if<isFuture<Ret>::value, bool>::type = true) {\n  using T = typename Ret::value_type;\n  return [](Params...) -> Future<T> { return makeFuture(T()); };\n}\n\nclass SomeClass {\n public:\n  template <class Ret, class... Params>\n  static typename std::enable_if<!isFuture<Ret>::value, Ret>::type\n  aStaticMethod(Params...) {\n    return Ret();\n  }\n\n  template <class Ret, class... Params>\n  static typename std::enable_if<isFuture<Ret>::value, Ret>::type aStaticMethod(\n      Params...) {\n    using T = typename Ret::value_type;\n    return makeFuture(T());\n  }\n\n  template <class Ret, class... Params>\n  typename std::enable_if<!isFuture<Ret>::value, Ret>::type aMethod(Params...) {\n    return Ret();\n  }\n\n  template <class Ret, class... Params>\n  typename std::enable_if<isFuture<Ret>::value, Ret>::type aMethod(Params...) {\n    using T = typename Ret::value_type;\n    return makeFuture(T());\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/ThenTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nnamespace {\nstruct Widget {\n  int v_, copied_, moved_;\n  /* implicit */ Widget(int v) : v_(v), copied_(0), moved_(0) {}\n  Widget(const Widget& other)\n      : v_(other.v_), copied_(other.copied_ + 1), moved_(other.moved_) {}\n  Widget(Widget&& other) noexcept\n      : v_(other.v_), copied_(other.copied_), moved_(other.moved_ + 1) {}\n  Widget& operator=(const Widget& /* other */) {\n    throw std::logic_error(\"unexpected copy assignment\");\n  }\n  [[maybe_unused]] Widget& operator=(Widget&& /* other */) {\n    throw std::logic_error(\"unexpected move assignment\");\n  }\n};\n\nstruct CountedWidget : Widget {\n  static std::vector<Widget*> instances_;\n  bool alive = true;\n  /* implicit */ CountedWidget(int v) : Widget(v) {\n    instances_.push_back(this);\n  }\n  CountedWidget(const CountedWidget& other) : Widget(other) {\n    instances_.push_back(this);\n  }\n  CountedWidget(CountedWidget&& other) noexcept(false)\n      : Widget(std::move(other)) {\n    other.alive = false;\n    other.remove();\n    instances_.push_back(this);\n  }\n  ~CountedWidget() {\n    if (alive) {\n      remove();\n    }\n  }\n\n private:\n  CountedWidget& operator=(const CountedWidget&) = delete;\n  CountedWidget& operator=(CountedWidget&&) = delete;\n\n  void remove() {\n    auto iter = std::find(instances_.begin(), instances_.end(), this);\n    EXPECT_TRUE(iter != instances_.end());\n    instances_.erase(iter);\n  }\n};\n\nstd::vector<Widget*> CountedWidget::instances_;\n} // namespace\n\nTEST(Then, tryConstructor) {\n  auto t = Try<Widget>(23);\n  EXPECT_EQ(t.value().v_, 23);\n  EXPECT_EQ(t.value().copied_, 0);\n  EXPECT_EQ(t.value().moved_, 1);\n}\n\nTEST(Then, makeFuture) {\n  auto future = makeFuture<Widget>(23);\n  EXPECT_EQ(future.value().v_, 23);\n  EXPECT_EQ(future.value().copied_, 0);\n  EXPECT_EQ(future.value().moved_, 2);\n}\n\nTEST(Then, tryConstRValueReference) {\n  auto future = makeFuture<Widget>(23).then([](const Try<Widget>&& t) {\n    EXPECT_EQ(t.value().copied_, 0);\n    EXPECT_EQ(t.value().moved_, 2);\n    return t.value().v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, tryRValueReference) {\n  auto future = makeFuture<Widget>(23).then([](Try<Widget>&& t) {\n    EXPECT_EQ(t.value().copied_, 0);\n    EXPECT_EQ(t.value().moved_, 2);\n    return t.value().v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, tryConstLValueReference) {\n  auto future = makeFuture<Widget>(23).then([](const Try<Widget>& t) {\n    EXPECT_EQ(t.value().copied_, 0);\n    EXPECT_EQ(t.value().moved_, 2);\n    return t.value().v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, tryValue) {\n  auto future = makeFuture<Widget>(23).then([](Try<Widget> t) {\n    EXPECT_EQ(t.value().copied_, 0);\n    EXPECT_EQ(t.value().moved_, 3);\n    return t.value().v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, tryConstValue) {\n  auto future = makeFuture<Widget>(23).then([](const Try<Widget> t) {\n    EXPECT_EQ(t.value().copied_, 0);\n    EXPECT_EQ(t.value().moved_, 3);\n    return t.value().v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, constRValueReference) {\n  auto future = makeFuture<Widget>(23).thenValue([](const Widget&& w) {\n    EXPECT_EQ(w.copied_, 0);\n    EXPECT_EQ(w.moved_, 2);\n    return w.v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, rValueReference) {\n  auto future = makeFuture<Widget>(23).thenValue([](Widget&& w) {\n    EXPECT_EQ(w.copied_, 0);\n    EXPECT_EQ(w.moved_, 2);\n    return w.v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, constLValueReference) {\n  auto future = makeFuture<Widget>(23).thenValue([](const Widget& w) {\n    EXPECT_EQ(w.copied_, 0);\n    EXPECT_EQ(w.moved_, 2);\n    return w.v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, value) {\n  auto future = makeFuture<Widget>(23).thenValue([](Widget w) {\n    EXPECT_EQ(w.copied_, 0);\n    EXPECT_EQ(w.moved_, 3);\n    return w.v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, constValue) {\n  auto future = makeFuture<Widget>(23).thenValue([](const Widget w) {\n    EXPECT_EQ(w.copied_, 0);\n    EXPECT_EQ(w.moved_, 3);\n    return w.v_;\n  });\n  EXPECT_EQ(future.value(), 23);\n}\n\nTEST(Then, objectAliveDuringImmediateNoParamContinuation) {\n  auto f = makeFuture<CountedWidget>(23);\n  auto called = false;\n  std::move(f).thenValue([&](auto&&) {\n    EXPECT_EQ(CountedWidget::instances_.size(), 1u);\n    EXPECT_EQ(CountedWidget::instances_[0]->v_, 23);\n    called = true;\n  });\n  EXPECT_EQ(true, called);\n}\n\nTEST(Then, objectAliveDuringDeferredNoParamContinuation) {\n  auto p = Promise<CountedWidget>{};\n  bool called = false;\n  p.getFuture().thenValue([&](auto&&) {\n    EXPECT_EQ(CountedWidget::instances_.size(), 1u);\n    EXPECT_EQ(CountedWidget::instances_[0]->v_, 23);\n    called = true;\n  });\n  p.setValue(CountedWidget{23});\n  EXPECT_EQ(true, called);\n}\n\nTEST(Then, voidThenShouldPropagateExceptions) {\n  EXPECT_FALSE(makeFuture(42).then().hasException());\n  EXPECT_TRUE(makeFuture<int>(std::runtime_error(\"err\")).then().hasException());\n}\n"
  },
  {
    "path": "folly/futures/test/ThreadWheelTimekeeperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/ThreadWheelTimekeeper.h>\n#include <folly/futures/test/TimekeeperTestLib.h>\n\nnamespace folly {\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    ThreadWheelTimekeeperTest, TimekeeperTest, ThreadWheelTimekeeper);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/TimekeeperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Singleton.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing namespace std::chrono_literals;\n\n// These tests only exercise the timekeeper plumbing when the default timekeeper\n// is replaced/mocked. Timekeeper functionality and future integration is tested\n// separately for each implementation in their respective test suites.\nclass TimekeeperBase : public testing::Test {\n protected:\n  using TimekeeperSingleton =\n      Singleton<Timekeeper, detail::TimekeeperSingletonTag>;\n\n  void TearDown() override {\n    // Invalidate any mocks that were installed.\n    folly::SingletonVault::singleton()->destroyInstances();\n    folly::SingletonVault::singleton()->reenableInstances();\n  }\n};\n\nTEST_F(TimekeeperBase, FutureSleepHandlesNullTimekeeperSingleton) {\n  TimekeeperSingleton::make_mock([] { return nullptr; });\n  EXPECT_THROW(futures::sleep(1ms).get(), FutureNoTimekeeper);\n}\n\nTEST_F(TimekeeperBase, FutureWithinHandlesNullTimekeeperSingleton) {\n  TimekeeperSingleton::make_mock([] { return nullptr; });\n  Promise<int> p;\n  auto f = p.getFuture().within(1ms);\n  EXPECT_THROW(std::move(f).get(), FutureNoTimekeeper);\n}\n\nTEST_F(TimekeeperBase, SemiFutureWithinHandlesNullTimekeeperSingleton) {\n  TimekeeperSingleton::make_mock([] { return nullptr; });\n  Promise<int> p;\n  auto f = p.getSemiFuture().within(1ms);\n  EXPECT_THROW(std::move(f).get(), FutureNoTimekeeper);\n}\n\nTEST_F(TimekeeperBase, SemiFutureWithinCancelsTimeout) {\n  struct MockTimekeeper : Timekeeper {\n    MockTimekeeper() {\n      p_.setInterruptHandler([this](const exception_wrapper& ew) {\n        ew.handle([this](const FutureCancellation&) { cancelled_ = true; });\n        p_.setException(ew);\n      });\n    }\n\n    SemiFuture<Unit> after(HighResDuration) override {\n      return p_.getSemiFuture();\n    }\n\n    Promise<Unit> p_;\n    bool cancelled_{false};\n  };\n\n  MockTimekeeper tk;\n\n  Promise<int> p;\n  auto f = p.getSemiFuture().within(10s, static_cast<Timekeeper*>(&tk));\n  p.setValue(1);\n  f.wait();\n  EXPECT_TRUE(tk.cancelled_);\n}\n\nTEST_F(TimekeeperBase, SemiFutureWithinInlineAfter) {\n  struct MockTimekeeper : Timekeeper {\n    SemiFuture<Unit> after(HighResDuration) override {\n      return folly::makeSemiFuture<folly::Unit>(folly::FutureNoTimekeeper());\n    }\n  };\n\n  MockTimekeeper tk;\n\n  Promise<int> p;\n  auto f = p.getSemiFuture().within(10s, static_cast<Timekeeper*>(&tk));\n  EXPECT_THROW(std::move(f).get(), folly::FutureNoTimekeeper);\n}\n\nTEST_F(TimekeeperBase, SemiFutureWithinReady) {\n  struct MockTimekeeper : Timekeeper {\n    SemiFuture<Unit> after(HighResDuration) override {\n      called_ = true;\n      return folly::makeSemiFuture<folly::Unit>(folly::FutureNoTimekeeper());\n    }\n\n    bool called_{false};\n  };\n\n  MockTimekeeper tk;\n\n  Promise<int> p;\n  p.setValue(1);\n  auto f = p.getSemiFuture().within(10s, static_cast<Timekeeper*>(&tk));\n  f.wait();\n  EXPECT_FALSE(tk.called_);\n}\n"
  },
  {
    "path": "folly/futures/test/TimekeeperTestLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <thread>\n#include <vector>\n\n#include <fmt/format.h>\n#include <glog/logging.h>\n#include <folly/DefaultKeepAliveExecutor.h>\n#include <folly/Random.h>\n#include <folly/Singleton.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/executors/SerialExecutor.h>\n#include <folly/executors/VirtualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nusing namespace std::chrono_literals;\nusing std::chrono::steady_clock;\n\ntemplate <class Tk>\nclass TimekeeperTest : public testing::Test {\n protected:\n  void SetUp() override {\n    // Replace the default timekeeper with the class under test, and verify that\n    // the replacement was successful.\n    Singleton<Timekeeper, detail::TimekeeperSingletonTag>::make_mock([] {\n      return new Tk;\n    });\n    ASSERT_TRUE(\n        dynamic_cast<Tk*>(detail::getTimekeeperSingleton().get()) != nullptr);\n  }\n\n  void TearDown() override {\n    // Invalidate any mocks that were installed.\n    folly::SingletonVault::singleton()->destroyInstances();\n    folly::SingletonVault::singleton()->reenableInstances();\n  }\n};\n\nTYPED_TEST_SUITE_P(TimekeeperTest);\n\nTYPED_TEST_P(TimekeeperTest, After) {\n  auto t1 = steady_clock::now();\n  auto f = detail::getTimekeeperSingleton()->after(10ms);\n  EXPECT_FALSE(f.isReady());\n  std::move(f).get();\n  auto t2 = steady_clock::now();\n\n  EXPECT_GE(t2 - t1, 10ms);\n}\n\nTYPED_TEST_P(TimekeeperTest, AfterUnsafe) {\n  auto t1 = steady_clock::now();\n  auto f = detail::getTimekeeperSingleton()->afterUnsafe(10ms);\n  EXPECT_FALSE(f.isReady());\n  std::move(f).get();\n  auto t2 = steady_clock::now();\n\n  EXPECT_GE(t2 - t1, 10ms);\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureGet) {\n  Promise<int> p;\n  auto t = std::thread([&] { p.setValue(42); });\n  EXPECT_EQ(42, p.getFuture().get());\n  t.join();\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureGetBeforeTimeout) {\n  Promise<int> p;\n  auto t = std::thread([&] { p.setValue(42); });\n  // Technically this is a race and if the test server is REALLY overloaded\n  // and it takes more than a second to do that thread it could be flaky. But\n  // I want a low timeout (in human terms) so if this regresses and someone\n  // runs it by hand they're not sitting there forever wondering why it's\n  // blocked, and get a useful error message instead. If it does get flaky,\n  // empirically increase the timeout to the point where it's very improbable.\n  EXPECT_EQ(42, p.getFuture().get(std::chrono::seconds(2)));\n  t.join();\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureGetTimeout) {\n  Promise<int> p;\n  EXPECT_THROW(p.getFuture().get(1ms), folly::FutureTimeout);\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureSleep) {\n  auto t1 = steady_clock::now();\n  futures::sleep(1ms).get();\n  EXPECT_GE(steady_clock::now() - t1, 1ms);\n}\n\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wdeprecated-declarations\")\nTYPED_TEST_P(TimekeeperTest, FutureSleepUnsafe) {\n  auto t1 = steady_clock::now();\n  futures::sleepUnsafe(1ms).get();\n  EXPECT_GE(steady_clock::now() - t1, 1ms);\n}\nFOLLY_POP_WARNING\n\nTYPED_TEST_P(TimekeeperTest, FutureDelayed) {\n  auto t1 = steady_clock::now();\n  auto dur =\n      makeFuture()\n          .delayed(1ms)\n          .thenValue([=](auto&&) { return steady_clock::now() - t1; })\n          .get();\n\n  EXPECT_GE(dur, 1ms);\n}\n\nTYPED_TEST_P(TimekeeperTest, SemiFutureDelayed) {\n  auto t1 = steady_clock::now();\n  auto dur =\n      makeSemiFuture()\n          .delayed(1ms)\n          .toUnsafeFuture()\n          .thenValue([=](auto&&) { return steady_clock::now() - t1; })\n          .get();\n\n  EXPECT_GE(dur, 1ms);\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureDelayedStickyExecutor) {\n  // Check that delayed without an executor binds the inline executor.\n  {\n    auto t1 = steady_clock::now();\n    std::thread::id timekeeper_thread_id =\n        folly::detail::getTimekeeperSingleton()\n            // Ensure that the continuation is run almost certainly in the\n            // timekeeper's thread.\n            ->after(100ms)\n            .toUnsafeFuture()\n            .thenValue([](auto&&) { return std::this_thread::get_id(); })\n            .get();\n    std::thread::id task_thread_id{};\n    auto dur =\n        makeFuture()\n            .delayed(1ms)\n            .thenValue([=, &task_thread_id](auto&&) {\n              task_thread_id = std::this_thread::get_id();\n              return steady_clock::now() - t1;\n            })\n            .get();\n\n    EXPECT_GE(dur, 1ms);\n    EXPECT_EQ(timekeeper_thread_id, task_thread_id);\n  }\n\n  // Check that delayed applied to an executor returns a future that binds\n  // to the same executor as was input.\n  {\n    auto t1 = steady_clock::now();\n    std::thread::id driver_thread_id{};\n    std::thread::id first_task_thread_id{};\n    std::thread::id second_task_thread_id{};\n    folly::ManualExecutor me;\n    std::atomic<bool> stop_signal{false};\n    std::thread me_driver{[&me, &driver_thread_id, &stop_signal] {\n      driver_thread_id = std::this_thread::get_id();\n      while (!stop_signal) {\n        me.run();\n      }\n    }};\n    auto dur =\n        makeSemiFuture()\n            .via(&me)\n            .thenValue([&first_task_thread_id](auto&&) {\n              first_task_thread_id = std::this_thread::get_id();\n            })\n            .delayed(1ms)\n            .thenValue([=, &second_task_thread_id](auto&&) {\n              second_task_thread_id = std::this_thread::get_id();\n              return steady_clock::now() - t1;\n            })\n            .get();\n    stop_signal = true;\n    me_driver.join();\n    EXPECT_GE(dur, 1ms);\n    EXPECT_EQ(driver_thread_id, first_task_thread_id);\n    EXPECT_EQ(driver_thread_id, second_task_thread_id);\n  }\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureWithinThrows) {\n  Promise<int> p;\n  auto f = p.getFuture().within(1ms).thenError(\n      tag_t<FutureTimeout>{}, [](auto&&) { return -1; });\n\n  EXPECT_EQ(-1, std::move(f).get());\n}\n\nTYPED_TEST_P(TimekeeperTest, SemiFutureWithinThrows) {\n  Promise<int> p;\n  auto f = p.getSemiFuture().within(1ms).toUnsafeFuture().thenError(\n      tag_t<FutureTimeout>{}, [](auto&&) { return -1; });\n\n  EXPECT_EQ(-1, std::move(f).get());\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureWithinAlreadyComplete) {\n  auto f = makeFuture(42).within(1ms).thenError(\n      tag_t<FutureTimeout>{}, [&](auto&&) { return -1; });\n\n  EXPECT_EQ(42, std::move(f).get());\n}\n\nTYPED_TEST_P(TimekeeperTest, SemiFutureWithinAlreadyComplete) {\n  auto f = makeSemiFuture(42).within(1ms).toUnsafeFuture().thenError(\n      tag_t<FutureTimeout>{}, [&](auto&&) { return -1; });\n\n  EXPECT_EQ(42, std::move(f).get());\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureWithinFinishesInTime) {\n  Promise<int> p;\n  auto f =\n      p.getFuture()\n          .within(std::chrono::minutes(1))\n          .thenError(tag_t<FutureTimeout>{}, [&](auto&&) { return -1; });\n  p.setValue(42);\n\n  EXPECT_EQ(42, std::move(f).get());\n}\n\nTYPED_TEST_P(TimekeeperTest, SemiFutureWithinFinishesInTime) {\n  Promise<int> p;\n  auto f =\n      p.getSemiFuture()\n          .within(std::chrono::minutes(1))\n          .toUnsafeFuture()\n          .thenError(tag_t<FutureTimeout>{}, [&](auto&&) { return -1; });\n  p.setValue(42);\n\n  EXPECT_EQ(42, std::move(f).get());\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureWithinVoidSpecialization) {\n  makeFuture().within(1ms);\n}\n\nTYPED_TEST_P(TimekeeperTest, SemiFutureWithinVoidSpecialization) {\n  makeSemiFuture().within(1ms);\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureWithinException) {\n  Promise<Unit> p;\n  auto f = p.getFuture().within(10ms, std::runtime_error(\"expected\"));\n  EXPECT_THROW(std::move(f).get(), std::runtime_error);\n}\n\nTYPED_TEST_P(TimekeeperTest, SemiFutureWithinException) {\n  Promise<Unit> p;\n  auto f = p.getSemiFuture().within(10ms, std::runtime_error(\"expected\"));\n  EXPECT_THROW(std::move(f).get(), std::runtime_error);\n}\n\nTYPED_TEST_P(TimekeeperTest, OnTimeout) {\n  bool flag = false;\n  makeFuture(42)\n      .delayed(10 * 1ms)\n      .onTimeout(\n          0ms,\n          [&] {\n            flag = true;\n            return -1;\n          })\n      .get();\n  EXPECT_TRUE(flag);\n}\n\nTYPED_TEST_P(TimekeeperTest, OnTimeoutComplete) {\n  bool flag = false;\n  makeFuture(42)\n      .onTimeout(\n          0ms,\n          [&] {\n            flag = true;\n            return -1;\n          })\n      .get();\n  EXPECT_FALSE(flag);\n}\n\nTYPED_TEST_P(TimekeeperTest, OnTimeoutReturnsFuture) {\n  bool flag = false;\n  makeFuture(42)\n      .delayed(10 * 1ms)\n      .onTimeout(\n          0ms,\n          [&] {\n            flag = true;\n            return makeFuture(-1);\n          })\n      .get();\n  EXPECT_TRUE(flag);\n}\n\nTYPED_TEST_P(TimekeeperTest, OnTimeoutVoid) {\n  makeFuture().delayed(1ms).onTimeout(0ms, [&] {});\n  makeFuture().delayed(1ms).onTimeout(0ms, [&] {\n    return makeFuture<Unit>(std::runtime_error(\"expected\"));\n  });\n  // just testing compilation here\n}\n\nTYPED_TEST_P(TimekeeperTest, InterruptDoesntCrash) {\n  auto f = futures::sleep(10s);\n  f.cancel();\n}\n\nTYPED_TEST_P(TimekeeperTest, ChainedInterruptTest) {\n  bool test = false;\n  auto f = futures::sleep(100ms).deferValue([&](auto&&) { test = true; });\n  f.cancel();\n  f.wait();\n  EXPECT_FALSE(test);\n}\n\nTYPED_TEST_P(TimekeeperTest, FutureWithinChainedInterruptTest) {\n  bool test = false;\n  Promise<Unit> p;\n  p.setInterruptHandler([&test, &p](const exception_wrapper& ex) {\n    ex.handle([&test](const FutureCancellation& /* cancellation */) {\n      test = true;\n    });\n    p.setException(ex);\n  });\n  auto f = p.getFuture().within(100ms);\n  EXPECT_FALSE(test) << \"Sanity check\";\n  f.cancel();\n  f.wait();\n  EXPECT_TRUE(test);\n}\n\nTYPED_TEST_P(TimekeeperTest, SemiFutureWithinChainedInterruptTest) {\n  bool test = false;\n  Promise<Unit> p;\n  p.setInterruptHandler([&test, &p](const exception_wrapper& ex) {\n    ex.handle([&test](const FutureCancellation& /* cancellation */) {\n      test = true;\n    });\n    p.setException(ex);\n  });\n  auto f = p.getSemiFuture().within(100ms);\n  EXPECT_FALSE(test) << \"Sanity check\";\n  f.cancel();\n  f.wait();\n  EXPECT_TRUE(test);\n}\n\nTYPED_TEST_P(TimekeeperTest, Executor) {\n  class ExecutorTester : public DefaultKeepAliveExecutor {\n   public:\n    virtual void add(Func f) override {\n      count++;\n      f();\n    }\n    void join() { joinKeepAlive(); }\n    std::atomic<int> count{0};\n  };\n\n  {\n    Promise<Unit> p;\n    ExecutorTester tester;\n    auto f =\n        p.getFuture()\n            .via(&tester)\n            .within(std::chrono::seconds(10))\n            .thenValue([&](auto&&) {});\n    p.setValue();\n    std::move(f).get();\n    tester.join();\n    EXPECT_EQ(3, tester.count);\n  }\n\n  {\n    Promise<Unit> p;\n    ExecutorTester tester;\n    auto f =\n        p.getFuture()\n            .via(&tester)\n            .within(std::chrono::milliseconds(10))\n            .thenValue([&](auto&&) {});\n    EXPECT_THROW(std::move(f).get(), FutureTimeout);\n    p.setValue();\n    tester.join();\n    EXPECT_EQ(3, tester.count);\n  }\n}\n\n// TODO(5921764)\n/*\nTYPED_TEST_P(TimekeeperTest, OnTimeoutPropagates) {\n  bool flag = false;\n  EXPECT_THROW(\n    makeFuture(42).delayed(1ms)\n      .onTimeout(0ms, [&]{ flag = true; })\n      .get(),\n    FutureTimeout);\n  EXPECT_TRUE(flag);\n}\n*/\n\nTYPED_TEST_P(TimekeeperTest, AtBeforeNow) {\n  auto f = detail::getTimekeeperSingleton()->at(steady_clock::now() - 10s);\n  EXPECT_TRUE(f.isReady());\n  EXPECT_FALSE(f.hasException());\n}\n\nTYPED_TEST_P(TimekeeperTest, HowToCastDuration) {\n  // I'm not sure whether this rounds up or down but it's irrelevant for the\n  // purpose of this example.\n  auto f = detail::getTimekeeperSingleton()->after(\n      std::chrono::duration_cast<Duration>(std::chrono::nanoseconds(1)));\n}\n\nTYPED_TEST_P(TimekeeperTest, Destruction) {\n  folly::Optional<TypeParam> tk{std::in_place};\n  auto f = tk->after(std::chrono::seconds(10));\n  EXPECT_FALSE(f.isReady());\n  tk.reset();\n  EXPECT_TRUE(f.isReady());\n  EXPECT_TRUE(f.hasException());\n}\n\nTYPED_TEST_P(TimekeeperTest, ConcurrentDestructionAndCancellation) {\n  folly::Optional<TypeParam> tk{std::in_place};\n  auto f = tk->after(std::chrono::seconds(10));\n  EXPECT_FALSE(f.isReady());\n  std::thread t{[&] { f.cancel(); }};\n  tk.reset();\n  t.join();\n  EXPECT_TRUE(f.isReady());\n  EXPECT_TRUE(f.hasException());\n}\n\nnamespace {\n\ntemplate <class Tk>\nvoid stressTest(\n    std::chrono::microseconds duration, std::chrono::microseconds period) {\n  using usec = std::chrono::microseconds;\n\n  folly::Optional<Tk> tk{std::in_place};\n  std::vector<std::thread> workers;\n\n  // Run continuations on a serial executor so we don't need synchronization to\n  // modify shared state.\n  folly::Optional<VirtualExecutor> continuationsThread{\n      std::in_place, SerialExecutor::create(folly::getGlobalCPUExecutor())};\n  size_t numCompletions = 0;\n  usec sumDelay{0};\n  usec maxDelay{0};\n\n  // Wait for any lazy initialization in the timekeeper and executor.\n  tk->after(1ms).via(&*continuationsThread).then([](auto&&) {}).get();\n\n  static const auto jitter = [](usec avg) {\n    // Center around average.\n    return usec(folly::Random::rand64(2 * avg.count()));\n  };\n\n  static const auto jitterSleep = [](steady_clock::time_point& now, usec avg) {\n    now += jitter(avg);\n    if (now - steady_clock::now() < 10us) {\n      // Busy-sleep if yielding the CPU would take too long.\n      while (now > steady_clock::now()) {\n      }\n    } else {\n      /* sleep override */ std::this_thread::sleep_until(now);\n    }\n  };\n\n  for (size_t i = 0; i < 8; ++i) {\n    workers.emplace_back([&] {\n      std::vector<Future<Unit>> futures;\n      for (auto start = steady_clock::now(), now = start;\n           now < start + duration;\n           jitterSleep(now, period)) {\n        // Use the test duration as rough range for the timeouts.\n        auto dur = jitter(duration);\n        auto expected = steady_clock::now() + dur;\n        futures.push_back(\n            tk->after(dur)\n                .toUnsafeFuture()\n                .thenValue([](auto&&) { return steady_clock::now(); })\n                .via(&*continuationsThread)\n                .thenValue([&, expected](auto fired) {\n                  auto delay =\n                      std::chrono::duration_cast<usec>(fired - expected);\n                  // TODO(ott): HHWheelTimer-based timekeepers round down the\n                  // timeout, so they may fire early, for now ignore this.\n                  if (delay < 0us && delay > -1ms) {\n                    delay = 0us;\n                  }\n                  ASSERT_GE(delay.count(), 0);\n                  ++numCompletions;\n                  sumDelay += delay;\n                  maxDelay = std::max(maxDelay, delay);\n                }));\n      }\n\n      for (auto& f : futures) {\n        // While at it, check that canceling the future after it has been\n        // fulfilled has no effect. To do so, we wait non-destructively.\n        while (!f.isReady()) {\n          /* sleep override */ std::this_thread::sleep_for(1ms);\n        }\n        f.cancel();\n        EXPECT_NO_THROW(std::move(f).get());\n      }\n    });\n  }\n\n  // Add a worker that cancels all its futures.\n  size_t numAttemptedCancellations = 0;\n  size_t numCancellations = 0;\n  workers.emplace_back([&] {\n    std::vector<SemiFuture<Unit>> futures;\n    for (auto start = steady_clock::now(), now = start; now < start + duration;\n         jitterSleep(now, 1ms)) {\n      // Pick a wide range of durations to exercise various positions in the\n      // sequence of timeouts.\n      auto dur = 5ms + jitter(5s);\n      futures.push_back(tk->after(dur));\n      // Cancel the future scheduled in the previous iteration.\n      if (futures.size() > 1) {\n        futures[futures.size() - 2].cancel();\n      }\n    }\n\n    futures.back().cancel();\n    numAttemptedCancellations = futures.size();\n\n    for (auto& f : futures) {\n      if (std::move(f).getTry().hasException<FutureCancellation>()) {\n        ++numCancellations;\n      }\n    }\n  });\n\n  // Add a few timeouts that will not survive the timekeeper.\n  std::vector<SemiFuture<Unit>> shutdownFutures;\n  for (size_t i = 0; i < 10; ++i) {\n    shutdownFutures.push_back(tk->after(10min));\n  }\n\n  for (auto& worker : workers) {\n    worker.join();\n  }\n\n  continuationsThread.reset(); // Wait for all continuations.\n  ASSERT_GT(numCompletions, 0);\n\n  // In principle the delay is unbounded (depending on the state of the system),\n  // so we cannot have any upper bound that is both meaningful and reliable, but\n  // we can log it to manually inspect the behavior.\n  LOG(INFO) << fmt::format(\n      \"Successful completions: {}, avg delay: {} us, max delay: {} us \",\n      numCompletions,\n      sumDelay.count() / numCompletions,\n      maxDelay.count());\n\n  // Similarly, a cancellation may be processed only after the future has fired,\n  // but in normal conditions this should never happen.\n  LOG(INFO) << fmt::format(\n      \"Attempted cancellations: {}, successful: {}\",\n      numAttemptedCancellations,\n      numCancellations);\n\n  tk.reset();\n  for (auto& f : shutdownFutures) {\n    EXPECT_TRUE(std::move(f).getTry().hasException<FutureNoTimekeeper>());\n  }\n}\n\n} // namespace\n\nTYPED_TEST_P(TimekeeperTest, Stress) {\n  stressTest<TypeParam>(/* duration */ 1s, /* period */ 10ms);\n}\n\nTYPED_TEST_P(TimekeeperTest, StressHighContention) {\n  // Test that nothing breaks when scheduling a large number of timeouts\n  // concurrently. In this case the timekeeper thread will be overloaded, so the\n  // measured delays are going to be large.\n  stressTest<TypeParam>(/* duration */ 50ms, /* period */ 5us);\n}\n\nREGISTER_TYPED_TEST_SUITE_P(\n    TimekeeperTest,\n    After,\n    AfterUnsafe,\n    FutureGet,\n    FutureGetBeforeTimeout,\n    FutureGetTimeout,\n    FutureSleep,\n    FutureSleepUnsafe,\n    FutureDelayed,\n    SemiFutureDelayed,\n    FutureDelayedStickyExecutor,\n    FutureWithinThrows,\n    SemiFutureWithinThrows,\n    FutureWithinAlreadyComplete,\n    SemiFutureWithinAlreadyComplete,\n    FutureWithinFinishesInTime,\n    SemiFutureWithinFinishesInTime,\n    FutureWithinVoidSpecialization,\n    SemiFutureWithinVoidSpecialization,\n    FutureWithinException,\n    SemiFutureWithinException,\n    OnTimeout,\n    OnTimeoutComplete,\n    OnTimeoutReturnsFuture,\n    OnTimeoutVoid,\n    InterruptDoesntCrash,\n    ChainedInterruptTest,\n    FutureWithinChainedInterruptTest,\n    SemiFutureWithinChainedInterruptTest,\n    Executor,\n    AtBeforeNow,\n    HowToCastDuration,\n    Destruction,\n    ConcurrentDestructionAndCancellation,\n    Stress,\n    StressHighContention);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/futures/test/TimesTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <mutex>\n#include <queue>\n\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\ninline void popAndFulfillPromise(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps, std::mutex& ps_mutex) {\n  ps_mutex.lock();\n  auto p = ps.front();\n  ps.pop();\n  ps_mutex.unlock();\n  p->setValue();\n}\n\ninline std::function<Future<Unit>(void)> makeThunk(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    int& interrupt,\n    std::mutex& ps_mutex) {\n  return [&]() mutable {\n    auto p = std::make_shared<Promise<Unit>>();\n    p->setInterruptHandler([&](exception_wrapper const& /* e */) {\n      ++interrupt;\n    });\n    ps_mutex.lock();\n    ps.push(p);\n    ps_mutex.unlock();\n\n    return p->getFuture();\n  };\n}\n\ninline std::function<bool(void)> makePred(int& i) {\n  return [&]() {\n    bool res = i < 3;\n    ++i;\n    return res;\n  };\n}\n\ntemplate <class F>\ninline void successTest(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    std::mutex& ps_mutex,\n    F& thunk) {\n  folly::ManualExecutor executor;\n  bool complete = false;\n  bool failure = false;\n\n  auto f =\n      folly::times(3, thunk)\n          .via(&executor)\n          .thenValue([&](auto&&) mutable { complete = true; })\n          .thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {\n            failure = true;\n          });\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n  EXPECT_FALSE(complete);\n  EXPECT_FALSE(failure);\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n  EXPECT_FALSE(complete);\n  EXPECT_FALSE(failure);\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n\n  executor.drain();\n  EXPECT_TRUE(f.isReady());\n  EXPECT_TRUE(complete);\n  EXPECT_FALSE(failure);\n}\n\nTEST(Times, success) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = makeThunk(ps, interrupt, ps_mutex);\n  successTest(ps, ps_mutex, thunk);\n}\n\nTEST(Times, semiFutureSuccess) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {\n    return t().semi();\n  };\n  successTest(ps, ps_mutex, thunk);\n}\n\ntemplate <class F>\ninline void failureTest(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    std::mutex& ps_mutex,\n    F& thunk) {\n  folly::ManualExecutor executor;\n  bool complete = false;\n  bool failure = false;\n\n  auto f =\n      folly::times(3, thunk)\n          .via(&executor)\n          .thenValue([&](auto&&) mutable { complete = true; })\n          .thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {\n            failure = true;\n          });\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n\n  executor.drain();\n  EXPECT_FALSE(complete);\n  EXPECT_FALSE(failure);\n\n  ps_mutex.lock();\n  auto p2 = ps.front();\n  ps.pop();\n  ps_mutex.unlock();\n  FutureException eggs(\"eggs\");\n  p2->setException(eggs);\n\n  executor.drain();\n  EXPECT_TRUE(f.isReady());\n  EXPECT_FALSE(complete);\n  EXPECT_TRUE(failure);\n}\n\nTEST(Times, failure) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = makeThunk(ps, interrupt, ps_mutex);\n  failureTest(ps, ps_mutex, thunk);\n}\n\nTEST(Times, semiFutureFailure) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {\n    return t().semi();\n  };\n  failureTest(ps, ps_mutex, thunk);\n}\n\ntemplate <class F>\ninline void interruptTest(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    std::mutex& ps_mutex,\n    int& interrupt,\n    F& thunk) {\n  folly::ManualExecutor executor;\n\n  auto f = folly::times(3, thunk).via(&executor);\n\n  executor.drain();\n\n  EXPECT_EQ(0, interrupt);\n\n  FutureException eggs(\"eggs\");\n  f.raise(eggs);\n\n  executor.drain();\n  for (int i = 1; i <= 3; ++i) {\n    EXPECT_EQ(1, interrupt);\n    popAndFulfillPromise(ps, ps_mutex);\n    executor.drain();\n  }\n}\n\nTEST(Times, interrupt) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = makeThunk(ps, interrupt, ps_mutex);\n  interruptTest(ps, ps_mutex, interrupt, thunk);\n}\n\nTEST(Times, semiFutureInterrupt) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {\n    return t().semi();\n  };\n  interruptTest(ps, ps_mutex, interrupt, thunk);\n}\n"
  },
  {
    "path": "folly/futures/test/UnwrapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\n// A simple scenario for the unwrap call, when the promise was fulfilled\n// before calling to unwrap.\nTEST(Unwrap, simpleScenario) {\n  Future<int> encapsulated_future = makeFuture(5484);\n  Future<Future<int>> future = makeFuture(std::move(encapsulated_future));\n  EXPECT_EQ(5484, std::move(future).unwrap().value());\n}\n\n// Makes sure that unwrap() works when chaning Future's commands.\nTEST(Unwrap, chainCommands) {\n  Future<Future<int>> future = makeFuture(makeFuture(5484));\n  auto unwrapped = std::move(future).unwrap().thenValue([](int i) {\n    return i;\n  });\n  EXPECT_EQ(5484, unwrapped.value());\n}\n\n// Makes sure that the unwrap call also works when the promise was not yet\n// fulfilled, and that the returned Future<T> becomes ready once the promise\n// is fulfilled.\nTEST(Unwrap, futureNotReady) {\n  Promise<Future<int>> p;\n  Future<Future<int>> future = p.getFuture();\n  Future<int> unwrapped = std::move(future).unwrap();\n  // Sanity - should not be ready before the promise is fulfilled.\n  ASSERT_FALSE(unwrapped.isReady());\n  // Fulfill the promise and make sure the unwrapped future is now ready.\n  p.setValue(makeFuture(5484));\n  ASSERT_TRUE(unwrapped.isReady());\n  EXPECT_EQ(5484, unwrapped.value());\n}\n"
  },
  {
    "path": "folly/futures/test/ViaTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <folly/MPMCQueue.h>\n#include <folly/executors/DrivableExecutor.h>\n#include <folly/executors/InlineExecutor.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nstruct ManualWaiter : public DrivableExecutor {\n  explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex_) : ex(ex_) {}\n\n  void add(Func f) override { ex->add(std::move(f)); }\n\n  void drive() override {\n    ex->wait();\n    ex->run();\n  }\n\n  std::shared_ptr<ManualExecutor> ex;\n};\n\nstruct ViaFixture : public testing::Test {\n  ViaFixture()\n      : westExecutor(new ManualExecutor),\n        eastExecutor(new ManualExecutor),\n        waiter(new ManualWaiter(westExecutor)),\n        done(false) {\n    th = std::thread([=, this] {\n      ManualWaiter eastWaiter(eastExecutor);\n      while (!done) {\n        eastWaiter.drive();\n      }\n    });\n  }\n\n  ~ViaFixture() override {\n    done = true;\n    eastExecutor->add([=]() {});\n    th.join();\n  }\n\n  void addAsync(int a, int b, std::function<void(int&&)>&& cob) {\n    eastExecutor->add([=]() { cob(a + b); });\n  }\n\n  std::shared_ptr<ManualExecutor> westExecutor;\n  std::shared_ptr<ManualExecutor> eastExecutor;\n  std::shared_ptr<ManualWaiter> waiter;\n  InlineExecutor inlineExecutor;\n  std::atomic<bool> done;\n  std::thread th;\n};\n\nTEST(Via, exceptionOnLaunch) {\n  auto future = makeFuture<int>(std::runtime_error(\"E\"));\n  EXPECT_THROW(future.value(), std::runtime_error);\n}\n\nTEST(Via, thenValue) {\n  auto future = makeFuture(std::move(1)).thenTry([](Try<int>&& t) {\n    return t.value() == 1;\n  });\n\n  EXPECT_TRUE(future.value());\n}\n\nTEST(Via, thenFuture) {\n  auto future = makeFuture(1).thenTry([](Try<int>&& t) {\n    return makeFuture(t.value() == 1);\n  });\n  EXPECT_TRUE(future.value());\n}\n\nstatic Future<std::string> doWorkStatic(Try<std::string>&& t) {\n  return makeFuture(t.value() + \";static\");\n}\n\nTEST(Via, thenFunction) {\n  struct Worker {\n    Future<std::string> doWork(Try<std::string>&& t) {\n      return makeFuture(t.value() + \";class\");\n    }\n    static Future<std::string> doWorkStatic(Try<std::string>&& t) {\n      return makeFuture(t.value() + \";class-static\");\n    }\n  } w;\n\n  auto f =\n      makeFuture(std::string(\"start\"))\n          .thenTry(doWorkStatic)\n          .thenTry(Worker::doWorkStatic)\n          .then(&Worker::doWork, &w);\n\n  EXPECT_EQ(f.value(), \"start;static;class-static;class\");\n}\n\nTEST_F(ViaFixture, threadHops) {\n  auto westThreadId = std::this_thread::get_id();\n  auto f =\n      via(eastExecutor.get())\n          .thenTry([=](Try<Unit>&& /* t */) {\n            EXPECT_NE(std::this_thread::get_id(), westThreadId);\n            return makeFuture<int>(1);\n          })\n          .via(westExecutor.get())\n          .thenTry([=](Try<int>&& t) {\n            EXPECT_EQ(std::this_thread::get_id(), westThreadId);\n            return t.value();\n          });\n  EXPECT_EQ(std::move(f).getVia(waiter.get()), 1);\n}\n\nTEST_F(ViaFixture, chainVias) {\n  auto westThreadId = std::this_thread::get_id();\n  auto f =\n      via(eastExecutor.get())\n          .thenValue([=](auto&&) {\n            EXPECT_NE(std::this_thread::get_id(), westThreadId);\n            return 1;\n          })\n          .thenValue([=, this](int val) {\n            return makeFuture(val)\n                .via(westExecutor.get())\n                .thenValue([=](int v) mutable {\n                  EXPECT_EQ(std::this_thread::get_id(), westThreadId);\n                  return v + 1;\n                });\n          })\n          .thenValue([=](int val) {\n            // even though ultimately the future that triggers this one\n            // executed in the west thread, this thenValue() inherited the\n            // executor from its predecessor, ie the eastExecutor.\n            EXPECT_NE(std::this_thread::get_id(), westThreadId);\n            return val + 1;\n          })\n          .via(westExecutor.get())\n          .thenValue([=](int val) {\n            // go back to west, so we can wait on it\n            EXPECT_EQ(std::this_thread::get_id(), westThreadId);\n            return val + 1;\n          });\n\n  EXPECT_EQ(std::move(f).getVia(waiter.get()), 4);\n}\n\nTEST_F(ViaFixture, bareViaAssignment) {\n  auto f = via(eastExecutor.get());\n}\nTEST_F(ViaFixture, viaAssignment) {\n  // via()&&\n  auto f = makeFuture().via(eastExecutor.get());\n  // via()&\n  auto f2 = f.via(eastExecutor.get());\n}\n\nstruct PriorityExecutor : public Executor {\n  void add(Func /* f */) override { count1++; }\n\n  void addWithPriority(Func f, int8_t priority) override {\n    int mid = getNumPriorities() / 2;\n    int p = priority < 0\n        ? std::max(0, mid + priority)\n        : std::min(getNumPriorities() - 1, mid + priority);\n    EXPECT_LT(p, 3);\n    EXPECT_GE(p, 0);\n    if (p == 0) {\n      count0++;\n    } else if (p == 1) {\n      count1++;\n    } else if (p == 2) {\n      count2++;\n    }\n    f();\n  }\n\n  uint8_t getNumPriorities() const override { return 3; }\n\n  int count0{0};\n  int count1{0};\n  int count2{0};\n};\n\nTEST(Via, priority) {\n  PriorityExecutor exe;\n  via(&exe, -1).thenValue([](auto&&) {});\n  via(&exe, 0).thenValue([](auto&&) {});\n  via(&exe, 1).thenValue([](auto&&) {});\n  via(&exe, 42).thenValue([](auto&&) {}); // overflow should go to max priority\n  via(&exe, -42).thenValue([](auto&&) {\n  }); // underflow should go to min priority\n  via(&exe).thenValue([](auto&&) {}); // default to mid priority\n  via(&exe, Executor::LO_PRI).thenValue([](auto&&) {});\n  via(&exe, Executor::HI_PRI).thenValue([](auto&&) {});\n  EXPECT_EQ(3, exe.count0);\n  EXPECT_EQ(2, exe.count1);\n  EXPECT_EQ(3, exe.count2);\n}\n\nTEST(Via, then2) {\n  ManualExecutor x1, x2;\n  bool a = false, b = false, c = false;\n  via(&x1)\n      .thenValue([&](auto&&) { a = true; })\n      .thenValue([&](auto&&) { b = true; })\n      .thenValueInline(folly::makeAsyncTask(&x2, [&](auto&&) { c = true; }));\n\n  EXPECT_FALSE(a);\n  EXPECT_FALSE(b);\n\n  x1.run();\n  EXPECT_TRUE(a);\n  EXPECT_FALSE(b);\n  EXPECT_FALSE(c);\n\n  x1.run();\n  EXPECT_TRUE(b);\n  EXPECT_FALSE(c);\n\n  x2.run();\n  EXPECT_TRUE(c);\n}\n\nTEST(Via, allowInline) {\n  ManualExecutor x1, x2;\n  bool a = false, b = false, c = false, d = false, e = false, f = false,\n       g = false, h = false, i = false, j = false, k = false, l = false,\n       m = false, n = false, o = false, p = false, q = false, r = false;\n  via(&x1)\n      .thenValue([&](auto&&) { a = true; })\n      .thenTryInline([&](auto&&) { b = true; })\n      .thenTry([&](auto&&) { c = true; })\n      .via(&x2)\n      .thenTryInline([&](auto&&) { d = true; })\n      .thenValue([&](auto&&) {\n        e = true;\n        return via(&x2).thenValue([&](auto&&) { f = true; });\n      })\n      .thenErrorInline(tag_t<std::exception>{}, [](auto&&) {})\n      .thenValueInline([&](auto&&) { g = true; })\n      .thenValue([&](auto&&) {\n        h = true;\n        return via(&x1).thenValue([&](auto&&) { i = true; });\n      })\n      .thenValueInline([&](auto&&) { j = true; })\n      .semi()\n      .deferValue([&](auto&&) { k = true; })\n      .via(&x2)\n      .thenValueInline([&](auto&&) { l = true; })\n      .semi()\n      .deferValue([&](auto&&) { m = true; })\n      .via(&x1)\n      .thenValue([&](auto&&) { n = true; })\n      .semi()\n      .deferValue([&](auto&&) { o = true; })\n      .deferValue([&](auto&&) { p = true; })\n      .via(&x1)\n      .semi()\n      .deferValue([&](auto&&) { q = true; })\n      .deferValue([&](auto&&) { r = true; })\n      .via(&x2);\n\n  EXPECT_FALSE(a);\n  EXPECT_FALSE(b);\n\n  // Expect b to be satisfied inline with the task x1\n  x1.run();\n  EXPECT_TRUE(a);\n  EXPECT_TRUE(b);\n  EXPECT_FALSE(c);\n\n  x1.run();\n  EXPECT_TRUE(c);\n  EXPECT_FALSE(d);\n\n  // Demonstrate that the executor transition did not allow inline execution\n  x2.run();\n  EXPECT_TRUE(d);\n  EXPECT_FALSE(e);\n\n  x2.run();\n  EXPECT_TRUE(e);\n  EXPECT_FALSE(f);\n  EXPECT_FALSE(g);\n\n  // Completing nested continuation should satisfy inline continuation\n  x2.run();\n  EXPECT_TRUE(f);\n  EXPECT_TRUE(g);\n  EXPECT_FALSE(h);\n\n  x2.run();\n  EXPECT_TRUE(h);\n  EXPECT_FALSE(i);\n  EXPECT_FALSE(j);\n\n  // Nested continuation on different executor should not complete next entry\n  // inline\n  x1.run();\n  EXPECT_TRUE(i);\n  EXPECT_FALSE(j);\n\n  // Defer should run on x1 and therefore not inline\n  // Subsequent deferred work is run on x1 and hence not inlined.\n  x2.run();\n  EXPECT_TRUE(j);\n  EXPECT_TRUE(k);\n  EXPECT_TRUE(l);\n  EXPECT_FALSE(m);\n\n  // Complete the deferred task\n  x1.run();\n  EXPECT_TRUE(m);\n  EXPECT_FALSE(n);\n\n  // Here defer and the above thenValue are both on x1, defer should be\n  // inline\n  x1.run();\n  EXPECT_TRUE(n);\n  EXPECT_TRUE(o);\n  EXPECT_TRUE(p);\n  EXPECT_FALSE(q);\n\n  // Change of executor in deferred executor so now run x2 to complete\n  x2.run();\n  EXPECT_TRUE(q);\n  EXPECT_TRUE(r);\n}\n\n#ifndef __APPLE__ // TODO #7372389\n/// Simple executor that does work in another thread\nclass ThreadExecutor : public Executor {\n  folly::MPMCQueue<Func> funcs;\n  std::atomic<bool> done{false};\n  std::thread worker;\n  folly::Baton<> baton;\n\n  void work() {\n    baton.post();\n    Func fn;\n    while (!done) {\n      while (!funcs.isEmpty()) {\n        funcs.blockingRead(fn);\n        fn();\n      }\n    }\n  }\n\n public:\n  explicit ThreadExecutor(size_t n = 1024) : funcs(n) {\n    worker = std::thread(std::bind(&ThreadExecutor::work, this));\n  }\n\n  ~ThreadExecutor() override {\n    done = true;\n    funcs.write([] {});\n    worker.join();\n  }\n\n  void add(Func fn) override { funcs.blockingWrite(std::move(fn)); }\n\n  void waitForStartup() { baton.wait(); }\n};\n\nTEST(Via, viaThenGetWasRacy) {\n  ThreadExecutor x;\n  std::unique_ptr<int> val =\n      folly::via(&x)\n          .thenValue([](auto&&) { return std::make_unique<int>(42); })\n          .get();\n  ASSERT_TRUE(!!val);\n  EXPECT_EQ(42, *val);\n}\n\nTEST(Via, callbackRace) {\n  ThreadExecutor x;\n\n  auto fn = [&x] {\n    auto promises = std::make_shared<std::vector<Promise<Unit>>>(4);\n    std::vector<Future<Unit>> futures;\n\n    for (auto& p : *promises) {\n      futures.emplace_back(p.getFuture().via(&x).thenTry([](Try<Unit>&&) {}));\n    }\n\n    x.waitForStartup();\n    x.add([promises] {\n      for (auto& p : *promises) {\n        p.setValue();\n      }\n    });\n\n    return collectAll(futures);\n  };\n\n  fn().wait();\n}\n#endif\n\nclass DummyDrivableExecutor : public DrivableExecutor {\n public:\n  void add(Func /* f */) override {}\n  void drive() override { ran = true; }\n  bool ran{false};\n};\n\nTEST(Via, getVia) {\n  {\n    // non-void\n    ManualExecutor x;\n    auto f = via(&x).thenValue([](auto&&) { return true; });\n    EXPECT_TRUE(std::move(f).getVia(&x));\n  }\n\n  {\n    // void\n    ManualExecutor x;\n    auto f = via(&x).then();\n    std::move(f).getVia(&x);\n  }\n\n  {\n    DummyDrivableExecutor x;\n    auto f = makeFuture(true);\n    EXPECT_TRUE(std::move(f).getVia(&x));\n    EXPECT_FALSE(x.ran);\n  }\n}\n\nTEST(Via, SimpleTimedGetVia) {\n  TimedDrivableExecutor e2;\n  Promise<folly::Unit> p;\n  auto f = p.getFuture();\n  EXPECT_THROW(\n      std::move(f).getVia(&e2, std::chrono::seconds(1)), FutureTimeout);\n}\n\nTEST(Via, getTryVia) {\n  {\n    // non-void\n    ManualExecutor x;\n    auto f = via(&x).thenValue([](auto&&) { return 23; });\n    EXPECT_FALSE(f.isReady());\n    EXPECT_EQ(23, std::move(f).getTryVia(&x).value());\n  }\n\n  {\n    // void\n    ManualExecutor x;\n    auto f = via(&x).then();\n    EXPECT_FALSE(f.isReady());\n    auto t = std::move(f).getTryVia(&x);\n    EXPECT_TRUE(t.hasValue());\n  }\n\n  {\n    DummyDrivableExecutor x;\n    auto f = makeFuture(23);\n    EXPECT_EQ(23, std::move(f).getTryVia(&x).value());\n    EXPECT_FALSE(x.ran);\n  }\n}\n\nTEST(Via, SimpleTimedGetTryVia) {\n  TimedDrivableExecutor e2;\n  Promise<folly::Unit> p;\n  auto f = p.getFuture();\n  EXPECT_THROW(\n      std::move(f).getTryVia(&e2, std::chrono::seconds(1)), FutureTimeout);\n}\n\nTEST(Via, waitVia) {\n  {\n    ManualExecutor x;\n    auto f = via(&x).then();\n    EXPECT_FALSE(f.isReady());\n    f.waitVia(&x);\n    EXPECT_TRUE(f.isReady());\n  }\n\n  {\n    // try rvalue as well\n    ManualExecutor x;\n    auto f = via(&x).then().waitVia(&x);\n    EXPECT_TRUE(f.isReady());\n  }\n\n  {\n    DummyDrivableExecutor x;\n    makeFuture(true).waitVia(&x);\n    EXPECT_FALSE(x.ran);\n  }\n}\n\nTEST(Via, viaRaces) {\n  ManualExecutor x;\n  Promise<Unit> p;\n  auto tid = std::this_thread::get_id();\n  bool done = false;\n\n  std::thread t1([&] {\n    p.getFuture()\n        .via(&x)\n        .thenTry([&](Try<Unit>&&) {\n          EXPECT_EQ(tid, std::this_thread::get_id());\n        })\n        .thenTry([&](Try<Unit>&&) {\n          EXPECT_EQ(tid, std::this_thread::get_id());\n        })\n        .thenTry([&](Try<Unit>&&) { done = true; });\n  });\n\n  std::thread t2([&] { p.setValue(); });\n\n  while (!done) {\n    x.run();\n  }\n  t1.join();\n  t2.join();\n}\n\nTEST(Via, viaDummyExecutorFutureSetValueFirst) {\n  // The callback object will get destroyed when passed to the executor.\n\n  // A promise will be captured by the callback lambda so we can observe that\n  // it will be destroyed.\n  Promise<Unit> captured_promise;\n  auto captured_promise_future = captured_promise.getFuture();\n\n  DummyDrivableExecutor x;\n  auto future = makeFuture().via(&x).thenValue(\n      [c = std::move(captured_promise)](auto&&) { return 42; });\n\n  EXPECT_THROW(std::move(future).get(std::chrono::seconds(5)), BrokenPromise);\n  EXPECT_THROW(\n      std::move(captured_promise_future).get(std::chrono::seconds(5)),\n      BrokenPromise);\n}\n\nTEST(Via, viaDummyExecutorFutureSetCallbackFirst) {\n  // The callback object will get destroyed when passed to the executor.\n\n  // A promise will be captured by the callback lambda so we can observe that\n  // it will be destroyed.\n  Promise<Unit> captured_promise;\n  auto captured_promise_future = captured_promise.getFuture();\n\n  DummyDrivableExecutor x;\n  Promise<Unit> trigger;\n  auto future = trigger.getFuture().via(&x).thenValue(\n      [c = std::move(captured_promise)](auto&&) { return 42; });\n  trigger.setValue();\n\n  EXPECT_THROW(std::move(future).get(std::chrono::seconds(5)), BrokenPromise);\n  EXPECT_THROW(\n      std::move(captured_promise_future).get(std::chrono::seconds(5)),\n      BrokenPromise);\n}\n\nTEST(Via, viaExecutorDiscardsTaskFutureSetValueFirst) {\n  // The callback object will get destroyed when the ManualExecutor runs out\n  // of scope.\n\n  // A promise will be captured by the callback lambda so we can observe that\n  // it will be destroyed.\n  Promise<Unit> captured_promise;\n  auto captured_promise_future = captured_promise.getFuture();\n\n  Optional<SemiFuture<int>> future;\n  {\n    ManualExecutor x;\n    future =\n        makeFuture()\n            .via(&x)\n            .thenValue([c = std::move(captured_promise)](auto&&) { return 42; })\n            .semi();\n    x.clear();\n  }\n\n  EXPECT_THROW(std::move(*future).get(std::chrono::seconds(5)), BrokenPromise);\n  EXPECT_THROW(\n      std::move(captured_promise_future).get(std::chrono::seconds(5)),\n      BrokenPromise);\n}\n\nTEST(Via, viaExecutorDiscardsTaskFutureSetCallbackFirst) {\n  // The callback object will get destroyed when the ManualExecutor runs out\n  // of scope.\n\n  // A promise will be captured by the callback lambda so we can observe that\n  // it will be destroyed.\n  Promise<Unit> captured_promise;\n  auto captured_promise_future = captured_promise.getFuture();\n\n  Optional<SemiFuture<int>> future;\n  {\n    ManualExecutor x;\n    Promise<Unit> trigger;\n    future =\n        trigger.getFuture()\n            .via(&x)\n            .thenValue([c = std::move(captured_promise)](auto&&) { return 42; })\n            .semi();\n    trigger.setValue();\n    x.clear();\n  }\n\n  EXPECT_THROW(std::move(*future).get(std::chrono::seconds(5)), BrokenPromise);\n  EXPECT_THROW(\n      std::move(captured_promise_future).get(std::chrono::seconds(5)),\n      BrokenPromise);\n}\n\nTEST(ViaFunc, liftsVoid) {\n  ManualExecutor x;\n  int count = 0;\n  Future<Unit> f = via(&x, [&] { count++; });\n\n  EXPECT_EQ(0, count);\n  x.run();\n  EXPECT_EQ(1, count);\n}\n\nTEST(ViaFunc, value) {\n  ManualExecutor x;\n  EXPECT_EQ(42, via(&x, [] { return 42; }).getVia(&x));\n}\n\nTEST(ViaFunc, exception) {\n  ManualExecutor x;\n  EXPECT_THROW(\n      via(&x, []() -> int { throw std::runtime_error(\"expected\"); }).getVia(&x),\n      std::runtime_error);\n}\n\nTEST(ViaFunc, future) {\n  ManualExecutor x;\n  EXPECT_EQ(42, via(&x, [] { return makeFuture(42); }).getVia(&x));\n}\n\nTEST(ViaFunc, semiFuture) {\n  ManualExecutor x;\n  EXPECT_EQ(42, via(&x, [] { return makeSemiFuture(42); }).getVia(&x));\n}\n\nTEST(ViaFunc, voidFuture) {\n  ManualExecutor x;\n  int count = 0;\n  via(&x, [&] { count++; }).getVia(&x);\n  EXPECT_EQ(1, count);\n}\n\nTEST(ViaFunc, isSticky) {\n  ManualExecutor x;\n  int count = 0;\n\n  auto f = via(&x, [&] { count++; });\n  x.run();\n\n  std::move(f).thenValue([&](auto&&) { count++; });\n  EXPECT_EQ(1, count);\n  x.run();\n  EXPECT_EQ(2, count);\n}\n\nTEST(ViaFunc, moveOnly) {\n  ManualExecutor x;\n  auto intp = std::make_unique<int>(42);\n\n  EXPECT_EQ(42, via(&x, [intp = std::move(intp)] { return *intp; }).getVia(&x));\n}\n\nTEST(ViaFunc, valueKeepAlive) {\n  ManualExecutor x;\n  EXPECT_EQ(42, via(getKeepAliveToken(&x), [] { return 42; }).getVia(&x));\n}\n\nTEST(ViaFunc, thenValueKeepAlive) {\n  ManualExecutor x;\n  EXPECT_EQ(\n      42,\n      via(getKeepAliveToken(&x))\n          .thenValue([](auto&&) { return 42; })\n          .getVia(&x));\n}\n"
  },
  {
    "path": "folly/futures/test/WaitTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <queue>\n\n#include <folly/executors/InlineExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/test/TestExecutor.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\nusing std::vector;\nusing std::chrono::milliseconds;\n\nTEST(Wait, waitImmediate) {\n  makeFuture().wait();\n  auto done = makeFuture(42).wait().value();\n  EXPECT_EQ(42, done);\n\n  vector<int> v{1, 2, 3};\n  auto done_v = makeFuture(v).wait().value();\n  EXPECT_EQ(v.size(), done_v.size());\n  EXPECT_EQ(v, done_v);\n\n  vector<Future<Unit>> v_f;\n  v_f.push_back(makeFuture());\n  v_f.push_back(makeFuture());\n  auto done_v_f = collectAll(v_f).wait().value();\n  EXPECT_EQ(2, done_v_f.size());\n\n  vector<Future<bool>> v_fb;\n  v_fb.push_back(makeFuture(true));\n  v_fb.push_back(makeFuture(false));\n  auto fut = collectAll(v_fb);\n  auto done_v_fb = std::move(fut.wait().value());\n  EXPECT_EQ(2, done_v_fb.size());\n}\n\nTEST(Wait, wait) {\n  Promise<int> p;\n  Future<int> f = p.getFuture();\n  std::atomic<bool> flag{false};\n  std::atomic<int> result{1};\n  std::atomic<std::thread::id> id;\n\n  std::thread th(\n      [&](Future<int>&& tf) {\n        auto n = std::move(tf).thenTry([&](Try<int>&& t) {\n          id = std::this_thread::get_id();\n          return t.value();\n        });\n        flag = true;\n        result.store(n.wait().value());\n      },\n      std::move(f));\n  while (!flag) {\n  }\n  EXPECT_EQ(result.load(), 1);\n  p.setValue(42);\n  th.join();\n  // validate that the callback ended up executing in this thread, which\n  // is more to ensure that this test actually tests what it should\n  EXPECT_EQ(id, std::this_thread::get_id());\n  EXPECT_EQ(result.load(), 42);\n}\n\nstruct MoveFlag {\n  MoveFlag() = default;\n  MoveFlag& operator=(const MoveFlag&) = delete;\n  MoveFlag(const MoveFlag&) = delete;\n  MoveFlag(MoveFlag&& other) noexcept { other.moved = true; }\n  bool moved{false};\n};\n\nTEST(Wait, waitReplacesSelf) {\n  // wait\n  {\n    // lvalue\n    auto f1 = makeFuture(MoveFlag());\n    f1.wait();\n    EXPECT_FALSE(f1.value().moved);\n\n    // rvalue\n    auto f2 = makeFuture(MoveFlag()).wait();\n    EXPECT_FALSE(f2.value().moved);\n  }\n\n  // wait(Duration)\n  {\n    // lvalue\n    auto f1 = makeFuture(MoveFlag());\n    f1.wait(milliseconds(1));\n    EXPECT_FALSE(f1.value().moved);\n\n    // rvalue\n    auto f2 = makeFuture(MoveFlag()).wait(milliseconds(1));\n    EXPECT_FALSE(f2.value().moved);\n  }\n\n  // waitVia\n  {\n    folly::EventBase eb;\n    // lvalue\n    auto f1 = makeFuture(MoveFlag());\n    f1.waitVia(&eb);\n    EXPECT_FALSE(f1.value().moved);\n\n    // rvalue\n    auto f2 = makeFuture(MoveFlag()).waitVia(&eb);\n    EXPECT_FALSE(f2.value().moved);\n  }\n}\n\nTEST(Wait, waitWithDuration) {\n  {\n    Promise<int> p;\n    Future<int> f = p.getFuture();\n    f.wait(milliseconds(1));\n    EXPECT_FALSE(f.isReady());\n    p.setValue(1);\n    EXPECT_TRUE(f.isReady());\n  }\n  {\n    Promise<int> p;\n    Future<int> f = p.getFuture();\n    p.setValue(1);\n    f.wait(milliseconds(1));\n    EXPECT_TRUE(f.isReady());\n  }\n  {\n    vector<Future<bool>> v_fb;\n    v_fb.push_back(makeFuture(true));\n    v_fb.push_back(makeFuture(false));\n    auto f = collectAll(v_fb).toUnsafeFuture();\n    f.wait(milliseconds(1));\n    EXPECT_TRUE(f.isReady());\n    EXPECT_EQ(2, f.value().size());\n  }\n  {\n    vector<Future<bool>> v_fb;\n    Promise<bool> p1;\n    Promise<bool> p2;\n    v_fb.push_back(p1.getFuture());\n    v_fb.push_back(p2.getFuture());\n    auto f = collectAll(v_fb).toUnsafeFuture();\n    f.wait(milliseconds(1));\n    EXPECT_FALSE(f.isReady());\n    p1.setValue(true);\n    EXPECT_FALSE(f.isReady());\n    p2.setValue(true);\n    EXPECT_TRUE(f.isReady());\n  }\n  {\n    auto f = makeFuture().wait(milliseconds(1));\n    EXPECT_TRUE(f.isReady());\n  }\n\n  {\n    Promise<Unit> p;\n    auto start = std::chrono::steady_clock::now();\n    auto f = p.getFuture().wait(milliseconds(100));\n    auto elapsed = std::chrono::steady_clock::now() - start;\n    EXPECT_GE(elapsed, milliseconds(100));\n    EXPECT_FALSE(f.isReady());\n    p.setValue();\n    EXPECT_TRUE(f.isReady());\n  }\n\n  {\n    // Try to trigger the race where the resultant Future is not yet complete\n    // even if we didn't hit the timeout, and make sure we deal with it properly\n    Promise<Unit> p;\n    folly::Baton<> b;\n    auto t = std::thread([&] {\n      b.post();\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(100));\n      p.setValue();\n    });\n    b.wait();\n    auto f = p.getFuture().wait(std::chrono::seconds(3600));\n    EXPECT_TRUE(f.isReady());\n    t.join();\n  }\n\n  {\n    // `Future::wait(Duration) &` when promise is fulfilled during the wait\n    Promise<int> p;\n\n    auto f = p.getFuture();\n    EXPECT_FALSE(f.isReady());\n\n    folly::Baton<> b;\n    auto t = std::thread([&] {\n      b.post();\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(100));\n      p.setValue(42);\n    });\n    b.wait();\n\n    f.wait(std::chrono::seconds(10));\n    EXPECT_TRUE(f.valid());\n    EXPECT_TRUE(f.isReady());\n    EXPECT_EQ(f.value(), 42);\n\n    t.join();\n    EXPECT_TRUE(f.isReady());\n    EXPECT_EQ(f.value(), 42);\n  }\n\n  {\n    // `Future::wait(Duration) &&` when promise is fulfilled during the wait\n    Promise<int> p;\n\n    auto f1 = p.getFuture();\n    EXPECT_FALSE(f1.isReady());\n\n    folly::Baton<> b;\n    auto t = std::thread([&] {\n      b.post();\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(100));\n      p.setValue(42);\n    });\n    b.wait();\n\n    auto f2 = std::move(f1).wait(std::chrono::seconds(10));\n    EXPECT_FALSE(f1.valid());\n    EXPECT_TRUE(f2.valid());\n    EXPECT_TRUE(f2.isReady());\n    EXPECT_EQ(f2.value(), 42);\n\n    t.join();\n    EXPECT_TRUE(f2.valid());\n    EXPECT_TRUE(f2.isReady());\n    EXPECT_EQ(f2.value(), 42);\n  }\n\n  {\n    // `SemiFuture::get(Duration) &&` when promise is fulfilled during the get\n    Promise<int> p;\n\n    auto f = p.getSemiFuture();\n    EXPECT_FALSE(f.isReady());\n\n    folly::Baton<> b;\n    auto t = std::thread([&] {\n      b.post();\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(100));\n      p.setValue(42);\n    });\n    b.wait();\n\n    EXPECT_EQ(std::move(f).get(std::chrono::seconds(10)), 42);\n\n    t.join();\n  }\n}\n\nTEST(Wait, multipleWait) {\n  folly::TestExecutor executor(1);\n  auto f = futures::sleep(milliseconds(100)).via(&executor);\n  for (size_t i = 0; i < 5; ++i) {\n    EXPECT_FALSE(f.isReady());\n    f.wait(milliseconds(3));\n  }\n  EXPECT_FALSE(f.isReady());\n  f.wait();\n  EXPECT_TRUE(f.isReady());\n  f.wait();\n  EXPECT_TRUE(f.isReady());\n}\n\nTEST(Wait, WaitPlusThen) {\n  // Validate expected behavior of `f.wait(...).then([](auto&){...})`.\n  // There are 10 sub-cases:\n  //   - Future fulfilled {prior to, during} call to wait().\n  //   - Future fulfilled {prior to, during, after} call to wait(dur).\n  //   - then repeat those 5 cases for SemiFuture\n\n  {\n    // Sub-case: Future fulfilled before `wait()` is called.\n    // Expect call to `.then()` to succeed & continuation to run immediately.\n    Promise<int> p;\n    auto f = p.getFuture();\n    p.setValue(42);\n    EXPECT_TRUE(f.isReady());\n    EXPECT_EQ(f.value(), 42);\n    f.wait();\n    auto continuation = 0;\n    EXPECT_NO_THROW(std::move(f).thenValue([&](auto&& v) {\n      continuation = v;\n    }));\n    EXPECT_EQ(continuation, 42);\n  }\n\n  {\n    // Sub-case: Future fulfilled after `wait()` actually has to wait.\n    // Expect call to `.then()` to fail (throw std::logic_error).\n    Promise<int> p;\n    auto f = p.getFuture();\n\n    folly::Baton<> b;\n    auto t = std::thread([&] {\n      b.post();\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(100));\n      p.setValue(42);\n    });\n    b.wait();\n\n    EXPECT_FALSE(f.isReady()); // deterministically passes in practice\n    f.wait();\n    EXPECT_TRUE(f.isReady());\n    auto continuation = 0;\n    EXPECT_NO_THROW(std::move(f).thenValue([&](auto&& v) {\n      continuation = v;\n    }));\n    EXPECT_EQ(continuation, 42);\n    t.join();\n  }\n\n  {\n    // Sub-case: Future fulfilled before `wait(dur)` is called.\n    // Expect call to `.then()` to succeed & continuation to run immediately.\n    Promise<int> p;\n    auto f = p.getFuture();\n    p.setValue(42);\n    EXPECT_TRUE(f.isReady());\n    EXPECT_EQ(f.value(), 42);\n    f.wait(std::chrono::seconds(10));\n    auto continuation = 0;\n    EXPECT_NO_THROW(std::move(f).thenValue([&](auto&& v) {\n      continuation = v;\n    }));\n    EXPECT_EQ(continuation, 42);\n  }\n\n  {\n    // Sub-case: Future fulfilled after `wait(dur)` actually starts waiting.\n    // Expect call to `.then()` to succeed & continuation to when result ready.\n    Promise<int> p;\n    auto f = p.getFuture();\n\n    folly::Baton<> b;\n    auto t = std::thread([&] {\n      b.post();\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(100));\n      p.setValue(42);\n    });\n    b.wait();\n\n    EXPECT_FALSE(f.isReady()); // deterministically passes in practice\n    f.wait(std::chrono::seconds(10));\n    EXPECT_TRUE(f.isReady()); // deterministically passes in practice\n    auto continuation = 0;\n    EXPECT_NO_THROW(std::move(f).thenValue([&](auto&& v) {\n      continuation = v;\n    }));\n    EXPECT_EQ(continuation, 42);\n    t.join();\n  }\n\n  {\n    // Sub-case: Future not fulfilled - `wait(dur)` times out.\n    // Expect call to `.then()` to succeed; continuation to not run.\n    Promise<int> p;\n    auto f = p.getFuture();\n    f.wait(milliseconds(1));\n    auto continuation = 0;\n    EXPECT_NO_THROW(std::move(f).thenValue([&](auto&& v) {\n      continuation = v;\n    }));\n    EXPECT_EQ(continuation, 0);\n  }\n\n  {\n    // Sub-case: SemiFuture fulfilled before `wait()` is called.\n    // Expect call to `.then()` to succeed & continuation to run immediately.\n    Promise<int> p;\n    auto f = p.getSemiFuture();\n    p.setValue(42);\n    EXPECT_TRUE(f.isReady());\n    EXPECT_EQ(f.value(), 42);\n    f.wait();\n    auto continuation = 0;\n    InlineExecutor e;\n    auto f2 = std::move(f).via(&e);\n    EXPECT_NO_THROW(std::move(f2).thenValue([&](auto&& v) {\n      continuation = v;\n    }));\n    EXPECT_EQ(continuation, 42);\n  }\n\n  {\n    // Sub-case: SemiFuture fulfilled after `wait()` actually has to wait.\n    // Expect call to `.then()` to fail (throw std::logic_error).\n    Promise<int> p;\n    auto f = p.getSemiFuture();\n\n    folly::Baton<> b;\n    auto t = std::thread([&] {\n      b.post();\n      /* sleep override */ std::this_thread::sleep_for(milliseconds(100));\n      p.setValue(42);\n    });\n    b.wait();\n\n    EXPECT_FALSE(f.isReady()); // deterministically passes in practice\n    f.wait();\n    EXPECT_TRUE(f.isReady());\n    auto continuation = 0;\n    InlineExecutor e;\n    auto f2 = std::move(f).via(&e);\n    EXPECT_NO_THROW(std::move(f2).thenValue([&](auto&& v) {\n      continuation = v;\n    }));\n    EXPECT_EQ(continuation, 42);\n    t.join();\n  }\n}\n\nTEST(Wait, cancelAfterWait) {\n  folly::TestExecutor executor(1);\n  Promise<folly::Unit> p;\n  p.setInterruptHandler([&](const exception_wrapper& e) {\n    EXPECT_THROW(e.throw_exception(), FutureCancellation);\n  });\n\n  auto fut = p.getSemiFuture().within(std::chrono::seconds(1)).via(&executor);\n  fut.wait(std::chrono::milliseconds(1));\n  fut.cancel();\n  fut.wait();\n}\n"
  },
  {
    "path": "folly/futures/test/WhenTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <mutex>\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(When, predicateFalse) {\n  int i = 0;\n  auto thunk = [&] { return makeFuture().thenValue([&](auto&&) { i += 1; }); };\n\n  // false\n  auto f1 = folly::when(false, thunk);\n  f1.wait();\n  EXPECT_EQ(0, i);\n\n  auto sfThunk = [&] {\n    return makeSemiFuture().deferValue([&](auto&&) { i += 1; });\n  };\n\n  // false\n  auto f1s = folly::when(false, sfThunk);\n  f1s.wait();\n  EXPECT_EQ(0, i);\n}\n\nTEST(When, predicateTrue) {\n  int i = 0;\n  auto thunk = [&] { return makeFuture().thenValue([&](auto&&) { i += 1; }); };\n\n  // true\n  auto f2 = folly::when(true, thunk);\n  f2.wait();\n  EXPECT_EQ(1, i);\n\n  auto sfThunk = [&] {\n    return makeSemiFuture().deferValue([&](auto&&) { i += 1; });\n  };\n\n  // true\n  auto f2s = folly::when(true, sfThunk);\n  f2s.wait();\n  EXPECT_EQ(2, i);\n}\n"
  },
  {
    "path": "folly/futures/test/WhileDoTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <mutex>\n#include <queue>\n\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\ninline void popAndFulfillPromise(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps, std::mutex& ps_mutex) {\n  ps_mutex.lock();\n  auto p = ps.front();\n  ps.pop();\n  ps_mutex.unlock();\n  p->setValue();\n}\n\ninline std::function<Future<Unit>(void)> makeThunk(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    int& interrupt,\n    std::mutex& ps_mutex) {\n  return [&]() mutable {\n    auto p = std::make_shared<Promise<Unit>>();\n    p->setInterruptHandler([&](exception_wrapper const& /* e */) {\n      ++interrupt;\n    });\n    ps_mutex.lock();\n    ps.push(p);\n    ps_mutex.unlock();\n\n    return p->getFuture();\n  };\n}\n\ninline std::function<bool(void)> makePred(int& i) {\n  return [&]() {\n    bool res = i < 3;\n    ++i;\n    return res;\n  };\n}\n\ntemplate <class F>\ninline void successTest(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    std::mutex& ps_mutex,\n    F& thunk) {\n  folly::ManualExecutor executor;\n  int i = 0;\n  bool complete = false;\n  bool failure = false;\n\n  auto pred = makePred(i);\n  auto f =\n      folly::whileDo(pred, thunk)\n          .via(&executor)\n          .thenValue([&](auto&&) mutable { complete = true; })\n          .thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {\n            failure = true;\n          });\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n  EXPECT_FALSE(complete);\n  EXPECT_FALSE(failure);\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n  EXPECT_FALSE(complete);\n  EXPECT_FALSE(failure);\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n  executor.drain();\n  EXPECT_TRUE(f.isReady());\n  EXPECT_TRUE(complete);\n  EXPECT_FALSE(failure);\n}\n\nTEST(WhileDo, success) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = makeThunk(ps, interrupt, ps_mutex);\n  successTest(ps, ps_mutex, thunk);\n}\n\nTEST(WhileDo, semiFutureSuccess) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {\n    return t().semi();\n  };\n  successTest(ps, ps_mutex, thunk);\n}\n\ntemplate <class F>\ninline void failureTest(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    std::mutex& ps_mutex,\n    F& thunk) {\n  folly::ManualExecutor executor;\n  int i = 0;\n  bool complete = false;\n  bool failure = false;\n\n  auto pred = makePred(i);\n  auto f =\n      folly::whileDo(pred, thunk)\n          .via(&executor)\n          .thenValue([&](auto&&) mutable { complete = true; })\n          .thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {\n            failure = true;\n          });\n\n  executor.drain();\n  popAndFulfillPromise(ps, ps_mutex);\n  executor.drain();\n  EXPECT_FALSE(complete);\n  EXPECT_FALSE(failure);\n\n  ps_mutex.lock();\n  auto p2 = ps.front();\n  ps.pop();\n  ps_mutex.unlock();\n  FutureException eggs(\"eggs\");\n  p2->setException(eggs);\n\n  executor.drain();\n  EXPECT_TRUE(f.isReady());\n  EXPECT_FALSE(complete);\n  EXPECT_TRUE(failure);\n}\n\nTEST(WhileDo, failure) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = makeThunk(ps, interrupt, ps_mutex);\n  failureTest(ps, ps_mutex, thunk);\n}\n\nTEST(WhileDo, semiFutureFailure) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {\n    return t().semi();\n  };\n  failureTest(ps, ps_mutex, thunk);\n}\n\ntemplate <class F>\ninline void interruptTest(\n    std::queue<std::shared_ptr<Promise<Unit>>>& ps,\n    std::mutex& ps_mutex,\n    int& interrupt,\n    F& thunk) {\n  folly::ManualExecutor executor;\n\n  int i = 0;\n  auto pred = makePred(i);\n  auto f = folly::whileDo(pred, thunk).via(&executor);\n\n  executor.drain();\n  EXPECT_EQ(0, interrupt);\n\n  FutureException eggs(\"eggs\");\n  f.raise(eggs);\n\n  executor.drain();\n  for (int j = 1; j <= 3; ++j) {\n    EXPECT_EQ(1, interrupt);\n    popAndFulfillPromise(ps, ps_mutex);\n    executor.drain();\n  }\n}\n\nTEST(WhileDo, interrupt) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = makeThunk(ps, interrupt, ps_mutex);\n  interruptTest(ps, ps_mutex, interrupt, thunk);\n}\n\nTEST(WhileDo, semiFutureInterrupt) {\n  std::queue<std::shared_ptr<Promise<Unit>>> ps;\n  std::mutex ps_mutex;\n  int interrupt = 0;\n  auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {\n    return t().semi();\n  };\n  interruptTest(ps, ps_mutex, interrupt, thunk);\n}\n"
  },
  {
    "path": "folly/futures/test/WillEqualTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nTEST(WillEqual, basic) {\n  // both p1 and p2 already fulfilled\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    p1.setValue(27);\n    p2.setValue(27);\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    EXPECT_TRUE(f1.willEqual(f2).get());\n  }\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    p1.setValue(27);\n    p2.setValue(36);\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    EXPECT_FALSE(f1.willEqual(f2).get());\n  }\n  // both p1 and p2 not yet fulfilled\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    auto f3 = f1.willEqual(f2);\n    p1.setValue(27);\n    p2.setValue(27);\n    EXPECT_TRUE(std::move(f3).get());\n  }\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    auto f3 = f1.willEqual(f2);\n    p1.setValue(27);\n    p2.setValue(36);\n    EXPECT_FALSE(std::move(f3).get());\n  }\n  // p1 already fulfilled, p2 not yet fulfilled\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    p1.setValue(27);\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    auto f3 = f1.willEqual(f2);\n    p2.setValue(27);\n    EXPECT_TRUE(std::move(f3).get());\n  }\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    p1.setValue(27);\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    auto f3 = f1.willEqual(f2);\n    p2.setValue(36);\n    EXPECT_FALSE(std::move(f3).get());\n  }\n  // p2 already fulfilled, p1 not yet fulfilled\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    p2.setValue(27);\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    auto f3 = f1.willEqual(f2);\n    p1.setValue(27);\n    EXPECT_TRUE(std::move(f3).get());\n  }\n  {\n    Promise<int> p1;\n    Promise<int> p2;\n    p2.setValue(36);\n    auto f1 = p1.getFuture();\n    auto f2 = p2.getFuture();\n    auto f3 = f1.willEqual(f2);\n    p1.setValue(27);\n    EXPECT_FALSE(std::move(f3).get());\n  }\n}\n"
  },
  {
    "path": "folly/futures/test/WindowTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <vector>\n\n#include <folly/synchronization/test/Barrier.h>\n\n#include <folly/Conv.h>\n#include <folly/executors/ManualExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nusing eggs_t = FutureException;\nstatic eggs_t eggs(\"eggs\");\n\nTEST(Window, basic) {\n  // int -> Future<int>\n  auto fn = [](std::vector<int> input, size_t window_size, size_t expect) {\n    auto res =\n        reduce(\n            window(\n                input, [](int i) { return makeFuture(i); }, window_size),\n            0,\n            [](int sum, const Try<int>& b) { return sum + *b; })\n            .get();\n    EXPECT_EQ(expect, res);\n  };\n  {\n    SCOPED_TRACE(\"2 in-flight at a time\");\n    std::vector<int> input = {1, 2, 3};\n    fn(input, 2, 6);\n  }\n  {\n    SCOPED_TRACE(\"4 in-flight at a time\");\n    std::vector<int> input = {1, 2, 3};\n    fn(input, 4, 6);\n  }\n  {\n    SCOPED_TRACE(\"empty input\");\n    std::vector<int> input;\n    fn(input, 1, 0);\n  }\n  {\n    // int -> Future<Unit>\n    auto res =\n        reduce(\n            window(\n                std::vector<int>({1, 2, 3}),\n                [](int /* i */) { return makeFuture(); },\n                2),\n            0,\n            [](int sum, const Try<Unit>& b) {\n              EXPECT_TRUE(b.hasValue());\n              return sum + 1;\n            })\n            .get();\n    EXPECT_EQ(3, res);\n  }\n  {\n    // string -> return Future<int>\n    auto res =\n        reduce(\n            window(\n                std::vector<std::string>{\"1\", \"2\", \"3\"},\n                [](std::string s) {\n                  return makeFuture<int>(folly::to<int>(s));\n                },\n                2),\n            0,\n            [](int sum, const Try<int>& b) { return sum + *b; })\n            .get();\n    EXPECT_EQ(6, res);\n  }\n  {\n    // string -> return SemiFuture<int>\n    auto res =\n        reduce(\n            window(\n                std::vector<std::string>{\"1\", \"2\", \"3\"},\n                [](std::string s) {\n                  return makeSemiFuture<int>(folly::to<int>(s));\n                },\n                2),\n            0,\n            [](int sum, const Try<int>& b) { return sum + *b; })\n            .get();\n    EXPECT_EQ(6, res);\n  }\n  {\n    SCOPED_TRACE(\"repeat same fn\");\n    auto res =\n        reduce(\n            window(\n                size_t(5),\n                [](size_t iteration) { return folly::makeFuture(iteration); },\n                2),\n            size_t{0},\n            [](size_t sum, const Try<size_t>& b) { return sum + b.value(); })\n            .get();\n    EXPECT_EQ(0 + 1 + 2 + 3 + 4, res);\n  }\n}\n\nTEST(Window, inline) {\n  // inline future collection on same executor\n  {\n    ManualExecutor x;\n    auto allf =\n        collectAll(\n            window(\n                &x,\n                std::vector<int>{42, 42, 42},\n                [&](int i) { return makeFuture(i).via(&x); },\n                2))\n            .via(&x)\n            .thenTryInline([](auto&&) {});\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(2, x.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(2, x.run());\n    EXPECT_FALSE(allf.isReady());\n    EXPECT_EQ(1, x.run());\n    EXPECT_TRUE(allf.isReady());\n  }\n}\n\nTEST(Window, exception) {\n  std::vector<int> ints = {1, 2, 3, 4};\n  std::vector<Promise<int>> ps(4);\n\n  auto res = reduce(\n      window(\n          ints,\n          [&ps](int i) {\n            if (i > 2) {\n              throw std::runtime_error(\"exception should not kill process\");\n            }\n            return ps[i].getFuture();\n          },\n          2),\n      0,\n      [](int sum, const Try<int>& b) {\n        sum += b.hasException<std::exception>() ? 1 : 0;\n        return sum;\n      });\n\n  for (auto& p : ps) {\n    p.setValue(0);\n  }\n\n  // Should have received 2 exceptions.\n  EXPECT_EQ(2, std::move(res).get());\n}\n\nTEST(Window, stackOverflow) {\n  // Number of futures to spawn.\n  static constexpr size_t m = 1000;\n  // Size of each block of input and output.\n  static constexpr size_t n = 1000;\n\n  std::vector<std::array<int, n>> ints;\n  int64_t expectedSum = 0;\n  for (size_t i = 0; i < m; i++) {\n    std::array<int, n> next{};\n    next[i % n] = i;\n    ints.emplace_back(next);\n    expectedSum += i;\n  }\n\n  // Try to overflow window's executor.\n  auto res = reduce(\n      window(\n          ints,\n          [](std::array<int, n> i) {\n            return folly::Future<std::array<int, n>>(i);\n          },\n          1),\n      static_cast<int64_t>(0),\n      [](int64_t sum, const Try<std::array<int, n>>& b) {\n        for (int a : b.value()) {\n          sum += a;\n        }\n        return sum;\n      });\n\n  EXPECT_EQ(std::move(res).get(), expectedSum);\n}\n\nTEST(Window, parallel) {\n  std::vector<int> input;\n  std::vector<Promise<int>> ps(10);\n  for (size_t i = 0; i < ps.size(); i++) {\n    input.emplace_back(i);\n  }\n  auto f = collect(window(input, [&](int i) { return ps[i].getFuture(); }, 3));\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      ps[i].setValue(i);\n    });\n  }\n\n  barrier.wait();\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  EXPECT_TRUE(f.isReady());\n  for (size_t i = 0; i < ps.size(); i++) {\n    EXPECT_EQ(i, f.value()[i]);\n  }\n}\n\nTEST(Window, parallelWithError) {\n  std::vector<int> input;\n  std::vector<Promise<int>> ps(10);\n  for (size_t i = 0; i < ps.size(); i++) {\n    input.emplace_back(i);\n  }\n  auto f = collect(window(input, [&](int i) { return ps[i].getFuture(); }, 3));\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      if (i == (ps.size() / 2)) {\n        ps[i].setException(eggs);\n      } else {\n        ps[i].setValue(i);\n      }\n    });\n  }\n\n  barrier.wait();\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  EXPECT_TRUE(f.isReady());\n  EXPECT_THROW(f.value(), eggs_t);\n}\n\nTEST(Window, allParallelWithError) {\n  std::vector<int> input;\n  std::vector<Promise<int>> ps(10);\n  for (size_t i = 0; i < ps.size(); i++) {\n    input.emplace_back(i);\n  }\n  auto f =\n      collectAll(window(input, [&](int i) { return ps[i].getFuture(); }, 3));\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      if (i == (ps.size() / 2)) {\n        ps[i].setException(eggs);\n      } else {\n        ps[i].setValue(i);\n      }\n    });\n  }\n\n  barrier.wait();\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  EXPECT_TRUE(f.isReady());\n  for (size_t i = 0; i < ps.size(); i++) {\n    if (i == (ps.size() / 2)) {\n      EXPECT_THROW(f.value()[i].value(), eggs_t);\n    } else {\n      EXPECT_TRUE(f.value()[i].hasValue());\n      EXPECT_EQ(i, f.value()[i].value());\n    }\n  }\n}\n\nTEST(WindowExecutor, basic) {\n  ManualExecutor executor;\n\n  // int -> Future<int>\n  auto fn = [executor_ = &executor](\n                std::vector<int> input, size_t window_size, size_t expect) {\n    auto res = reduce(\n        window(\n            executor_, input, [](int i) { return makeFuture(i); }, window_size),\n        0,\n        [](int sum, const Try<int>& b) { return sum + *b; });\n    executor_->waitFor(res);\n    EXPECT_EQ(expect, std::move(res).get());\n  };\n  {\n    SCOPED_TRACE(\"2 in-flight at a time\");\n    std::vector<int> input = {1, 2, 3};\n    fn(input, 2, 6);\n  }\n  {\n    SCOPED_TRACE(\"4 in-flight at a time\");\n    std::vector<int> input = {1, 2, 3};\n    fn(input, 4, 6);\n  }\n  {\n    SCOPED_TRACE(\"empty input\");\n    std::vector<int> input;\n    fn(input, 1, 0);\n  }\n  {\n    // int -> Future<Unit>\n    auto res = reduce(\n        window(\n            &executor,\n            std::vector<int>({1, 2, 3}),\n            [](int /* i */) { return makeFuture(); },\n            2),\n        0,\n        [](int sum, const Try<Unit>& b) {\n          EXPECT_TRUE(b.hasValue());\n          return sum + 1;\n        });\n    executor.waitFor(res);\n    EXPECT_EQ(3, std::move(res).get());\n  }\n  {\n    // string -> return Future<int>\n    auto res = reduce(\n        window(\n            &executor,\n            std::vector<std::string>{\"1\", \"2\", \"3\"},\n            [](std::string s) { return makeFuture<int>(folly::to<int>(s)); },\n            2),\n        0,\n        [](int sum, const Try<int>& b) { return sum + *b; });\n    executor.waitFor(res);\n    EXPECT_EQ(6, std::move(res).get());\n  }\n}\n\nTEST(WindowExecutor, parallel) {\n  ManualExecutor executor;\n\n  std::vector<int> input;\n  std::vector<Promise<int>> ps(10);\n  for (size_t i = 0; i < ps.size(); i++) {\n    input.emplace_back(i);\n  }\n  auto f = collect(\n      window(&executor, input, [&](int i) { return ps[i].getFuture(); }, 3));\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      ps[i].setValue(i);\n    });\n  }\n\n  barrier.wait();\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  executor.drain();\n  EXPECT_TRUE(f.isReady());\n  for (size_t i = 0; i < ps.size(); i++) {\n    EXPECT_EQ(i, f.value()[i]);\n  }\n}\n\nTEST(WindowExecutor, parallelWithError) {\n  ManualExecutor executor;\n\n  std::vector<int> input;\n  std::vector<Promise<int>> ps(10);\n  for (size_t i = 0; i < ps.size(); i++) {\n    input.emplace_back(i);\n  }\n  auto f = collect(\n      window(&executor, input, [&](int i) { return ps[i].getFuture(); }, 3));\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      if (i == (ps.size() / 2)) {\n        ps[i].setException(eggs);\n      } else {\n        ps[i].setValue(i);\n      }\n    });\n  }\n\n  barrier.wait();\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  executor.drain();\n  EXPECT_TRUE(f.isReady());\n  EXPECT_THROW(f.value(), eggs_t);\n}\n\nTEST(WindowExecutor, allParallelWithError) {\n  ManualExecutor executor;\n\n  std::vector<int> input;\n  std::vector<Promise<int>> ps(10);\n  for (size_t i = 0; i < ps.size(); i++) {\n    input.emplace_back(i);\n  }\n  auto f = collectAll(\n      window(&executor, input, [&](int i) { return ps[i].getFuture(); }, 3));\n\n  std::vector<std::thread> ts;\n  folly::test::Barrier barrier(ps.size() + 1);\n  for (size_t i = 0; i < ps.size(); i++) {\n    ts.emplace_back([&ps, &barrier, i]() {\n      barrier.wait();\n      if (i == (ps.size() / 2)) {\n        ps[i].setException(eggs);\n      } else {\n        ps[i].setValue(i);\n      }\n    });\n  }\n\n  barrier.wait();\n\n  for (auto& t : ts) {\n    t.join();\n  }\n\n  executor.waitFor(f);\n  EXPECT_TRUE(f.isReady());\n  for (size_t i = 0; i < ps.size(); i++) {\n    if (i == (ps.size() / 2)) {\n      EXPECT_THROW(f.value()[i].value(), eggs_t);\n    } else {\n      EXPECT_TRUE(f.value()[i].hasValue());\n      EXPECT_EQ(i, f.value()[i].value());\n    }\n  }\n}\n"
  },
  {
    "path": "folly/futures/test/then_compile_test.rb",
    "content": "#!/usr/bin/env ruby\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# cd folly/futures/test && ruby then_compile_test.rb > ThenCompileTest.cpp\n\n# An exercise in combinatorics.\n# (ordinary/static function, member function, std::function, lambda)\n# X\n# returns (Future<R>, R)\n# X\n# accepts (Try<T>&&, Try<T> const&, Try<T>, T&&, T const&, T, nothing)\n\ndef test(form, *args)\n  args = args.join(\", \")\n  [\n  \"{Future<B> f = someFuture<A>().#{form}(#{args});}\",\n  #\"{Future<B> f = makeFuture(A()).then(#{args}, anExecutor);}\",\n  ]\nend\n\ndef retval(ret)\n  {\n    \"Future<B>\" => \"someFuture<B>()\",\n    \"Try<B>\" => \"Try<B>(B())\",\n    \"B\" => \"B()\"\n  }[ret]\nend\n\nreturn_types = [\n  \"Future<B>\",\n  \"B\",\n  #\"Try<B>\",\n]\ntry_param_types = [\n    \"Try<A>&&\",\n    \"Try<A> const&\",\n    \"Try<A>\",\n  ]\nvalue_param_types = [\n    \"A&&\",\n    \"A const&\",\n    \"A\",\n  ]\n\ndef doMap(form, param_types, return_types)\n  (\n    return_types.map { |ret|\n      param_types.map { |param|\n        if param != \"\" then\n          both = \"#{ret}, #{param}\"\n          [\n            [\"&aFunction<#{both}>\"],\n            [\"&SomeClass::aStaticMethod<#{both}>\"],\n            #[\"&SomeClass::aMethod<#{both}>\", \"&anObject\"],\n            [\"aStdFunction<#{both}>()\"],\n            [\"[&](#{param}){return #{retval(ret)};}\"],\n          ]\n        else\n          [[\"[&](){return #{retval(ret)};}\"]]\n        end\n      }\n    }.flatten(2)\n  ).map {|a| test(form, a)}.flatten\nend\n\ndef doMapMultiArg(form, param_types, return_types)\n  (\n    return_types.map { |ret|\n      param_types.map { |param|\n        if param != \"\" then\n          both = \"#{ret}, #{param}\"\n          [\n            [\"&SomeClass::aMethod<#{both}>\", \"&anObject\"],\n          ]\n        else\n          [[\"[&](){return #{retval(ret)};}\"]]\n        end\n      }\n    }.flatten(2)\n  ).map {|a| test(form, a)}.flatten\nend\n\n# TODO(#T29171940): Modify this to thenTry on deletion of the Try form of then\ntests = doMap(\"then\", try_param_types, return_types)\ntests.concat doMap(\"thenValue\", value_param_types, return_types)\ntests.concat doMapMultiArg(\"then\", try_param_types, return_types)\n\nprint <<EOF\n// This file is #{\"@\"}generated by then_compile_test.rb. Do not edit directly.\n\n#include <folly/futures/test/ThenCompileTest.h>\n\nusing namespace folly;\n\nTEST(Basic, thenVariants) {\n  SomeClass anObject;\n\n  #{tests.join(\"\\n  \")}\n}\nEOF\n"
  },
  {
    "path": "folly/gen/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"@fbsource//xplat/folly:defs.bzl\",\n    \"DEFAULT_APPLE_SDKS\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat build rules\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"base\",\n    apple_sdks = DEFAULT_APPLE_SDKS,\n    raw_headers = [\n        \"Base.h\",\n        \"Base-inl.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/gen:core\",\n        \"//third-party/range-v3:range-v3\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:optional\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:range\",\n        \"//xplat/folly:utility\",\n        \"//xplat/folly/container:access\",\n        \"//xplat/folly/container:f14_hash\",\n        \"//xplat/folly/functional:invoke\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"combine\",\n    headers = [\n        \"Combine.h\",\n        \"Combine-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\":base\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"core\",\n    headers = [\n        \"Core.h\",\n        \"Core-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\"//folly:portability\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"file\",\n    headers = [\n        \"File.h\",\n        \"File-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base\",\n        \":string\",\n        \"//folly:exception\",\n        \"//folly:file\",\n        \"//folly/io:iobuf\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"istream\",\n    headers = [\n        \"IStream.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"parallel\",\n    headers = [\n        \"Parallel.h\",\n        \"Parallel-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base\",\n        \"//folly:mpmc_queue\",\n        \"//folly:scope_guard\",\n        \"//folly/synchronization:event_count\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"parallel_map\",\n    headers = [\n        \"ParallelMap.h\",\n        \"ParallelMap-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":core\",\n        \"//folly:expected\",\n        \"//folly:mpmc_pipeline\",\n        \"//folly/functional:invoke\",\n        \"//folly/synchronization:event_count\",\n        \"//folly/system:hardware_concurrency\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"string\",\n    headers = [\n        \"String.h\",\n        \"String-inl.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":base\",\n        \"//folly:conv\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly:string\",\n        \"//folly/io:iobuf\",\n    ],\n)\n\n# fbcode build rules\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"base\",\n    headers = [\n        \"Base.h\",\n        \"Base-inl.h\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/range-v3:range-v3\",\n        \":core\",\n        \"//folly:conv\",\n        \"//folly:function\",\n        \"//folly:optional\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly:utility\",\n        \"//folly/container:access\",\n        \"//folly/container:f14_hash\",\n        \"//folly/functional:invoke\",\n    ],\n)\n"
  },
  {
    "path": "folly/gen/Base-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_GEN_BASE_H_\n#error This file may only be included from folly/gen/Base.h\n#endif\n\n#include <folly/Function.h>\n#include <folly/Portability.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/functional/Invoke.h>\n\n// inner condition from:\n// https://github.com/ericniebler/range-v3/blob/0.11.0/include/range/v3/detail/config.hpp#L222\n#define FOLLY_DETAIL_GEN_BASE_HAS_RANGEV3 __has_include(<range/v3/version.hpp>)\n\n#if FOLLY_DETAIL_GEN_BASE_HAS_RANGEV3\n#include <range/v3/view/filter.hpp>\n#include <range/v3/view/transform.hpp>\n#endif\n\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n\nnamespace folly {\nnamespace gen {\n\n/**\n * ArgumentReference - For determining ideal argument type to receive a value.\n */\ntemplate <class T>\nstruct ArgumentReference\n    : public std::conditional<\n          std::is_reference<T>::value,\n          T, // T& -> T&, T&& -> T&&, const T& -> const T&\n          typename std::conditional<\n              std::is_const<T>::value,\n              T&, // const int -> const int&\n              T&& // int -> int&&\n              >::type> {};\n\n/**\n * Group - The output objects from the GroupBy operator\n */\ntemplate <class Key, class Value>\nclass Group : public GenImpl<Value&&, Group<Key, Value>> {\n public:\n  static_assert(\n      !std::is_reference<Key>::value && !std::is_reference<Value>::value,\n      \"Key and Value must be decayed types\");\n\n  typedef std::vector<Value> VectorType;\n  typedef Key KeyType;\n  typedef Value ValueType;\n\n  Group(Key key, VectorType values)\n      : key_(std::move(key)), values_(std::move(values)) {}\n\n  const Key& key() const { return key_; }\n\n  size_t size() const { return values_.size(); }\n  const VectorType& values() const { return values_; }\n  VectorType& values() { return values_; }\n\n  VectorType operator|(const detail::Collect<VectorType>&) const {\n    return values();\n  }\n\n  VectorType operator|(const detail::CollectTemplate<std::vector>&) const {\n    return values();\n  }\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    for (auto& value : values_) {\n      body(std::move(value));\n    }\n  }\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    for (auto& value : values_) {\n      if (!handler(std::move(value))) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // GroupBy only takes in finite generators, so we only have finite groups\n  static constexpr bool infinite = false;\n\n private:\n  Key key_;\n  mutable VectorType values_;\n};\n\nnamespace detail {\n\n// Classes used for the implementation of Sources, Operators, and Sinks\n\n/*\n ******************************* Sources ***************************************\n */\n\n/*\n * ReferencedSource - Generate values from an STL-like container using\n * iterators from .begin() until .end(). Value type defaults to the type of\n * *container->begin(). For std::vector<int>, this would be int&. Note that the\n * value here is a reference, so the values in the vector will be passed by\n * reference to downstream operators.\n *\n * This type is primarily used through the 'from' helper method, like:\n *\n *   string& longestName = from(names)\n *                       | maxBy([](string& s) { return s.size() });\n */\ntemplate <class Container, class Value>\nclass ReferencedSource\n    : public GenImpl<Value, ReferencedSource<Container, Value>> {\n  Container* container_;\n\n public:\n  explicit ReferencedSource(Container* container) : container_(container) {}\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    for (auto&& value : *container_) {\n      body(std::forward<Value>(value));\n    }\n  }\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    for (auto&& value : *container_) {\n      if (!handler(std::forward<Value>(value))) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // from takes in a normal stl structure, which are all finite\n  static constexpr bool infinite = false;\n};\n\n/**\n * CopiedSource - For producing values from eagerly from a sequence of values\n * whose storage is owned by this class. Useful for preparing a generator for\n * use after a source collection will no longer be available, or for when the\n * values are specified literally with an initializer list.\n *\n * This type is primarily used through the 'fromCopy' function, like:\n *\n *   auto sourceCopy = fromCopy(makeAVector());\n *   auto sum = sourceCopy | sum;\n *   auto max = sourceCopy | max;\n *\n * Though it is also used for the initializer_list specialization of from().\n */\ntemplate <class StorageType, class Container>\nclass CopiedSource\n    : public GenImpl<const StorageType&, CopiedSource<StorageType, Container>> {\n  static_assert(\n      !std::is_reference<StorageType>::value, \"StorageType must be decayed\");\n\n public:\n  // Generator objects are often copied during normal construction as they are\n  // encapsulated by downstream generators. It would be bad if this caused\n  // a copy of the entire container each time, and since we're only exposing a\n  // const reference to the value, it's safe to share it between multiple\n  // generators.\n  static_assert(\n      !std::is_reference<Container>::value, \"Can't copy into a reference\");\n  std::shared_ptr<const Container> copy_;\n\n public:\n  typedef Container ContainerType;\n\n  template <class SourceContainer>\n  explicit CopiedSource(const SourceContainer& container)\n      : copy_(new Container(access::begin(container), access::end(container))) {\n  }\n\n  explicit CopiedSource(Container&& container)\n      : copy_(new Container(std::move(container))) {}\n\n  // To enable re-use of cached results.\n  CopiedSource(const CopiedSource<StorageType, Container>& source)\n      : copy_(source.copy_) {}\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    for (const auto& value : *copy_) {\n      body(value);\n    }\n  }\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    // The collection may be reused by others, we can't allow it to be changed.\n    for (const auto& value : *copy_) {\n      if (!handler(value)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // from takes in a normal stl structure, which are all finite\n  static constexpr bool infinite = false;\n};\n\n/**\n * RangeSource - For producing values from a folly::Range. Useful for referring\n * to a slice of some container.\n *\n * This type is primarily used through the 'from' function, like:\n *\n *   auto rangeSource = from(folly::range(v.begin(), v.end()));\n *   auto sum = rangeSource | sum;\n *\n * Reminder: Be careful not to invalidate iterators when using ranges like this.\n */\ntemplate <class Iterator>\nclass RangeSource\n    : public GenImpl<\n          typename Range<Iterator>::reference,\n          RangeSource<Iterator>> {\n  Range<Iterator> range_;\n\n public:\n  RangeSource() = default;\n  explicit RangeSource(Range<Iterator> range) : range_(std::move(range)) {}\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    for (auto& value : range_) {\n      if (!handler(value)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    for (auto& value : range_) {\n      body(value);\n    }\n  }\n\n  // folly::Range only supports finite ranges\n  static constexpr bool infinite = false;\n};\n\n/**\n * Sequence - For generating values from beginning value, incremented along the\n * way with the ++ and += operators. Iteration may continue indefinitely.\n * Value type specified explicitly.\n *\n * This type is primarily used through the 'seq' and 'range' function, like:\n *\n *   int total = seq(1, 10) | sum;\n *   auto indexes = range(0, 10);\n *   auto endless = seq(0); // 0, 1, 2, 3, ...\n */\ntemplate <class Value, class SequenceImpl>\nclass Sequence : public GenImpl<const Value&, Sequence<Value, SequenceImpl>> {\n  static_assert(\n      !std::is_reference<Value>::value && !std::is_const<Value>::value,\n      \"Value mustn't be const or ref.\");\n  Value start_;\n  SequenceImpl impl_;\n\n public:\n  explicit Sequence(Value start, SequenceImpl impl)\n      : start_(std::move(start)), impl_(std::move(impl)) {}\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    for (Value current = start_; impl_.test(current); impl_.step(current)) {\n      if (!handler(current)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    for (Value current = start_; impl_.test(current); impl_.step(current)) {\n      body(current);\n    }\n  }\n\n  // Let the implementation say if we are infinite or not\n  static constexpr bool infinite = SequenceImpl::infinite;\n};\n\n/**\n * Sequence implementations (range, sequence, infinite, with/without step)\n **/\ntemplate <class Value>\nclass RangeImpl {\n  Value end_;\n\n public:\n  explicit RangeImpl(Value end) : end_(std::move(end)) {}\n  bool test(const Value& current) const { return current < end_; }\n  void step(Value& current) const { ++current; }\n  static constexpr bool infinite = false;\n};\n\ntemplate <class Value, class Distance>\nclass RangeWithStepImpl {\n  Value end_;\n  Distance step_;\n\n public:\n  explicit RangeWithStepImpl(Value end, Distance step)\n      : end_(std::move(end)), step_(std::move(step)) {}\n  bool test(const Value& current) const { return current < end_; }\n  void step(Value& current) const { current += step_; }\n  static constexpr bool infinite = false;\n};\n\ntemplate <class Value>\nclass SeqImpl {\n  Value end_;\n\n public:\n  explicit SeqImpl(Value end) : end_(std::move(end)) {}\n  bool test(const Value& current) const { return current <= end_; }\n  void step(Value& current) const { ++current; }\n  static constexpr bool infinite = false;\n};\n\ntemplate <class Value, class Distance>\nclass SeqWithStepImpl {\n  Value end_;\n  Distance step_;\n\n public:\n  explicit SeqWithStepImpl(Value end, Distance step)\n      : end_(std::move(end)), step_(std::move(step)) {}\n  bool test(const Value& current) const { return current <= end_; }\n  void step(Value& current) const { current += step_; }\n  static constexpr bool infinite = false;\n};\n\ntemplate <class Value>\nclass InfiniteImpl {\n public:\n  bool test(const Value& /* current */) const { return true; }\n  void step(Value& current) const { ++current; }\n  static constexpr bool infinite = true;\n};\n\n/**\n * GenratorBuilder - Helper for GENERTATOR macro.\n **/\ntemplate <class Value>\nstruct GeneratorBuilder {\n  template <class Source, class Yield = detail::Yield<Value, Source>>\n  Yield operator+(Source&& source) {\n    return Yield(std::forward<Source>(source));\n  }\n};\n\n/**\n * Yield - For producing values from a user-defined generator by way of a\n * 'yield' function.\n **/\ntemplate <class Value, class Source>\nclass Yield : public GenImpl<Value, Yield<Value, Source>> {\n  Source source_;\n\n public:\n  explicit Yield(Source source) : source_(std::move(source)) {}\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    struct Break {};\n    auto body = [&](Value value) {\n      if (!handler(std::forward<Value>(value))) {\n        throw Break();\n      }\n    };\n    try {\n      source_(body);\n      return true;\n    } catch (Break&) {\n      return false;\n    }\n  }\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    source_(std::forward<Body>(body));\n  }\n};\n\ntemplate <class Value>\nclass Empty : public GenImpl<Value, Empty<Value>> {\n public:\n  Empty() = default;\n  template <class Handler>\n  bool apply(Handler&&) const {\n    return true;\n  }\n\n  template <class Body>\n  void foreach(Body&&) const {}\n\n  // No values, so finite\n  static constexpr bool infinite = false;\n};\n\ntemplate <class Value>\nclass SingleReference : public GenImpl<Value&, SingleReference<Value>> {\n  static_assert(\n      !std::is_reference<Value>::value,\n      \"SingleReference requires non-ref types\");\n  Value* ptr_;\n\n public:\n  explicit SingleReference(Value& ref) : ptr_(&ref) {}\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    return handler(*ptr_);\n  }\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    body(*ptr_);\n  }\n\n  // One value, so finite\n  static constexpr bool infinite = false;\n};\n\ntemplate <class Value>\nclass SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {\n  static_assert(\n      !std::is_reference<Value>::value, \"SingleCopy requires non-ref types\");\n  Value value_;\n\n public:\n  explicit SingleCopy(Value value) : value_(std::forward<Value>(value)) {}\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    return handler(value_);\n  }\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    body(value_);\n  }\n\n  // One value, so finite\n  static constexpr bool infinite = false;\n};\n\n/*\n ***************************** Operators ***************************************\n */\n\n/**\n * Map - For producing a sequence of values by passing each value from a source\n * collection through a predicate.\n *\n * This type is usually used through the 'map' or 'mapped' helper function:\n *\n *   auto squares = seq(1, 10) | map(square) | as<std::vector>();\n */\ntemplate <class Predicate>\nclass Map : public Operator<Map<Predicate>> {\n  Predicate pred_;\n\n public:\n  Map() = default;\n\n  explicit Map(Predicate pred) : pred_(std::move(pred)) {}\n\n  template <\n      class Value,\n      class Source,\n      class Result =\n          typename ArgumentReference<invoke_result_t<Predicate, Value>>::type>\n  class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {\n    Source source_;\n    Predicate pred_;\n\n   public:\n    explicit Generator(Source source, const Predicate& pred)\n        : source_(std::move(source)), pred_(pred) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      source_.foreach([&](Value value) {\n        body(pred_(std::forward<Value>(value)));\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Value value) {\n        return handler(pred_(std::forward<Value>(value)));\n      });\n    }\n\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <\n      class Source,\n      class Gen =\n          Generator<typename Source::ValueType, typename Source::SelfType>>\n  Gen compose(Source source) const {\n    return Gen(std::move(source.self()), pred_);\n  }\n};\n\n/**\n * Filter - For filtering values from a source sequence by a predicate.\n *\n * This type is usually used through the 'filter' helper function, like:\n *\n *   auto nonEmpty = from(strings)\n *                 | filter([](const string& str) -> bool {\n *                     return !str.empty();\n *                   });\n *\n * Note that if no predicate is provided, the values are casted to bool and\n * filtered based on that. So if pointers is a vector of pointers,\n *\n *   auto nonNull = from(pointers) | filter();\n *\n * will give a vector of all the pointers != nullptr.\n */\ntemplate <class Predicate>\nclass Filter : public Operator<Filter<Predicate>> {\n  Predicate pred_;\n\n public:\n  Filter() = default;\n  explicit Filter(Predicate pred) : pred_(std::move(pred)) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    Predicate pred_;\n\n   public:\n    explicit Generator(Source source, const Predicate& pred)\n        : source_(std::move(source)), pred_(pred) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      source_.foreach([&](Value value) {\n        // NB: Argument not forwarded to avoid accidental move-construction\n        if (pred_(value)) {\n          body(std::forward<Value>(value));\n        }\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Value value) -> bool {\n        // NB: Argument not forwarded to avoid accidental move-construction\n        if (pred_(value)) {\n          return handler(std::forward<Value>(value));\n        }\n        return true;\n      });\n    }\n\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), pred_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), pred_);\n  }\n};\n\n/**\n * Until - For producing values from a source until a predicate is satisfied.\n *\n * This type is usually used through the 'until' helper function, like:\n *\n *   auto best = from(sortedItems)\n *             | until([](Item& item) { return item.score > 100; })\n *             | as<std::vector>();\n */\ntemplate <class Predicate>\nclass Until : public Operator<Until<Predicate>> {\n  Predicate pred_;\n\n public:\n  Until() = default;\n  explicit Until(Predicate pred) : pred_(std::move(pred)) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    Predicate pred_;\n\n   public:\n    explicit Generator(Source source, const Predicate& pred)\n        : source_(std::move(source)), pred_(pred) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      bool cancelled = false;\n      source_.apply([&](Value value) -> bool {\n        if (pred_(value)) { // un-forwarded to disable move\n          return false;\n        }\n        if (!handler(std::forward<Value>(value))) {\n          cancelled = true;\n          return false;\n        }\n        return true;\n      });\n      return !cancelled;\n    }\n\n    // Theoretically an 'until' might stop an infinite\n    static constexpr bool infinite = false;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), pred_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), pred_);\n  }\n};\n\n/**\n * Take - For producing up to N values from a source.\n *\n * This type is usually used through the 'take' helper function, like:\n *\n *   auto best = from(docs)\n *             | orderByDescending(scoreDoc)\n *             | take(10);\n */\nclass Take : public Operator<Take> {\n  size_t count_;\n\n public:\n  explicit Take(size_t count) : count_(count) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    size_t count_;\n\n   public:\n    explicit Generator(Source source, size_t count)\n        : source_(std::move(source)), count_(count) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      if (count_ == 0) {\n        return false;\n      }\n      size_t n = count_;\n      bool cancelled = false;\n      source_.apply([&](Value value) -> bool {\n        if (!handler(std::forward<Value>(value))) {\n          cancelled = true;\n          return false;\n        }\n        return --n;\n      });\n      return !cancelled;\n    }\n\n    // take will stop an infinite generator\n    static constexpr bool infinite = false;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), count_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), count_);\n  }\n};\n\n/**\n * Visit - For calling a function on each item before passing it down the\n * pipeline.\n *\n * This type is usually used through the 'visit' helper function:\n *\n *   auto printedValues = seq(1) | visit(debugPrint);\n *   // nothing printed yet\n *   auto results = take(10) | as<std::vector>();\n *   // results now populated, 10 values printed\n */\ntemplate <class Visitor>\nclass Visit : public Operator<Visit<Visitor>> {\n  Visitor visitor_;\n\n public:\n  Visit() = default;\n\n  explicit Visit(Visitor visitor) : visitor_(std::move(visitor)) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    Visitor visitor_;\n\n   public:\n    explicit Generator(Source source, const Visitor& visitor)\n        : source_(std::move(source)), visitor_(visitor) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      source_.foreach([&](Value value) {\n        visitor_(value); // not forwarding to avoid accidental moves\n        body(std::forward<Value>(value));\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Value value) {\n        visitor_(value); // not forwarding to avoid accidental moves\n        return handler(std::forward<Value>(value));\n      });\n    }\n\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), visitor_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), visitor_);\n  }\n};\n\n/**\n * Stride - For producing every Nth value from a source.\n *\n * This type is usually used through the 'stride' helper function, like:\n *\n *   auto half = from(samples)\n *             | stride(2);\n */\nclass Stride : public Operator<Stride> {\n  size_t stride_;\n\n public:\n  explicit Stride(size_t stride) : stride_(stride) {\n    if (stride == 0) {\n      throw std::invalid_argument(\"stride must not be 0\");\n    }\n  }\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    size_t stride_;\n\n   public:\n    explicit Generator(Source source, size_t stride)\n        : source_(std::move(source)), stride_(stride) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      size_t distance = stride_;\n      return source_.apply([&](Value value) -> bool {\n        if (++distance >= stride_) {\n          distance = 0;\n          return handler(std::forward<Value>(value));\n        }\n        return true;\n      });\n    }\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      size_t distance = stride_;\n      source_.foreach([&](Value value) {\n        if (++distance >= stride_) {\n          body(std::forward<Value>(value));\n          distance = 0;\n        }\n      });\n    }\n\n    // Taking every Nth of an infinite list is still infinte\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), stride_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), stride_);\n  }\n};\n\n/**\n * Sample - For taking a random sample of N elements from a sequence\n * (without replacement).\n */\ntemplate <class Random>\nclass Sample : public Operator<Sample<Random>> {\n  size_t count_;\n  Random rng_;\n\n public:\n  explicit Sample(size_t count, Random rng)\n      : count_(count), rng_(std::move(rng)) {}\n\n  template <\n      class Value,\n      class Source,\n      class Rand,\n      class StorageType = typename std::decay<Value>::type>\n  class Generator\n      : public GenImpl<\n            StorageType&&,\n            Generator<Value, Source, Rand, StorageType>> {\n    static_assert(!Source::infinite, \"Cannot sample infinite source!\");\n    // It's too easy to bite ourselves if random generator is only 16-bit\n    static_assert(\n        Random::max() >= std::numeric_limits<int32_t>::max() - 1,\n        \"Random number generator must support big values\");\n    Source source_;\n    size_t count_;\n    mutable Rand rng_;\n\n   public:\n    explicit Generator(Source source, size_t count, Random rng)\n        : source_(std::move(source)), count_(count), rng_(std::move(rng)) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      if (count_ == 0) {\n        return false;\n      }\n      std::vector<StorageType> v;\n      v.reserve(count_);\n      // use reservoir sampling to give each source value an equal chance\n      // of appearing in our output.\n      size_t n = 1;\n      source_.foreach([&](Value value) -> void {\n        if (v.size() < count_) {\n          v.push_back(std::forward<Value>(value));\n        } else {\n          // alternatively, we could create a std::uniform_int_distribution\n          // instead of using modulus, but benchmarks show this has\n          // substantial overhead.\n          size_t index = rng_() % n;\n          if (index < v.size()) {\n            v[index] = std::forward<Value>(value);\n          }\n        }\n        ++n;\n      });\n\n      // output is unsorted!\n      for (auto& val : v) {\n        if (!handler(std::move(val))) {\n          return false;\n        }\n      }\n      return true;\n    }\n\n    // Only takes N elements, so finite\n    static constexpr bool infinite = false;\n  };\n\n  template <\n      class Source,\n      class Value,\n      class Gen = Generator<Value, Source, Random>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), count_, rng_);\n  }\n\n  template <\n      class Source,\n      class Value,\n      class Gen = Generator<Value, Source, Random>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), count_, rng_);\n  }\n};\n\n/**\n * Skip - For skipping N items from the beginning of a source generator.\n *\n * This type is usually used through the 'skip' helper function, like:\n *\n *   auto page = from(results)\n *             | skip(pageSize * startPage)\n *             | take(10);\n */\nclass Skip : public Operator<Skip> {\n  size_t count_;\n\n public:\n  explicit Skip(size_t count) : count_(count) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    size_t count_;\n\n   public:\n    explicit Generator(Source source, size_t count)\n        : source_(std::move(source)), count_(count) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      if (count_ == 0) {\n        source_.foreach(body);\n        return;\n      }\n      size_t n = 0;\n      source_.foreach([&](Value value) {\n        if (n < count_) {\n          ++n;\n        } else {\n          body(std::forward<Value>(value));\n        }\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      if (count_ == 0) {\n        return source_.apply(std::forward<Handler>(handler));\n      }\n      size_t n = 0;\n      return source_.apply([&](Value value) -> bool {\n        if (n < count_) {\n          ++n;\n          return true;\n        }\n        return handler(std::forward<Value>(value));\n      });\n    }\n\n    // Skipping N items of an infinite source is still infinite\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), count_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), count_);\n  }\n};\n\n/**\n * Order - For ordering a sequence of values from a source by key.\n * The key is extracted by the given selector functor, and this key is then\n * compared using the specified comparator.\n *\n * This type is usually used through the 'order' helper function, like:\n *\n *   auto closest = from(places)\n *                | orderBy([](Place& p) {\n *                    return -distance(p.location, here);\n *                  })\n *                | take(10);\n */\ntemplate <class Selector, class Comparer>\nclass Order : public Operator<Order<Selector, Comparer>> {\n  Selector selector_;\n  Comparer comparer_;\n\n public:\n  Order() = default;\n\n  explicit Order(Selector selector) : selector_(std::move(selector)) {}\n\n  Order(Selector selector, Comparer comparer)\n      : selector_(std::move(selector)), comparer_(std::move(comparer)) {}\n\n  template <\n      class Value,\n      class Source,\n      class StorageType = typename std::decay<Value>::type,\n      class Result = invoke_result_t<Selector, Value>>\n  class Generator\n      : public GenImpl<\n            StorageType&&,\n            Generator<Value, Source, StorageType, Result>> {\n    static_assert(!Source::infinite, \"Cannot sort infinite source!\");\n    Source source_;\n    Selector selector_;\n    Comparer comparer_;\n\n    typedef std::vector<StorageType> VectorType;\n\n    VectorType asVector() const {\n      auto comparer = [&](const StorageType& a, const StorageType& b) {\n        return comparer_(selector_(a), selector_(b));\n      };\n      auto vals = source_ | as<VectorType>();\n      std::sort(vals.begin(), vals.end(), comparer);\n      return vals;\n    }\n\n   public:\n    Generator(Source source, Selector selector, Comparer comparer)\n        : source_(std::move(source)),\n          selector_(std::move(selector)),\n          comparer_(std::move(comparer)) {}\n\n    VectorType operator|(const Collect<VectorType>&) const {\n      return asVector();\n    }\n\n    VectorType operator|(const CollectTemplate<std::vector>&) const {\n      return asVector();\n    }\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      for (auto& value : asVector()) {\n        body(std::move(value));\n      }\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      auto comparer = [&](const StorageType& a, const StorageType& b) {\n        // swapped for minHeap\n        return comparer_(selector_(b), selector_(a));\n      };\n      auto heap = source_ | as<VectorType>();\n      std::make_heap(heap.begin(), heap.end(), comparer);\n      while (!heap.empty()) {\n        std::pop_heap(heap.begin(), heap.end(), comparer);\n        if (!handler(std::move(heap.back()))) {\n          return false;\n        }\n        heap.pop_back();\n      }\n      return true;\n    }\n\n    // Can only be run on and produce finite generators\n    static constexpr bool infinite = false;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), selector_, comparer_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), selector_, comparer_);\n  }\n};\n\n/**\n * GroupBy - Group values by a given key selector, producing a sequence of\n * groups.\n *\n * This type is usually used through the 'groupBy' helper function, like:\n *\n *   auto bests\n *     = from(places)\n *     | groupBy([](const Place& p) {\n *         return p.city;\n *       })\n *     | [](Group<std::string, Place>&& g) {\n *         cout << g.key() << \": \" << (g | first).description;\n *       };\n */\ntemplate <class Selector>\nclass GroupBy : public Operator<GroupBy<Selector>> {\n  Selector selector_;\n\n public:\n  GroupBy() {}\n\n  explicit GroupBy(Selector selector) : selector_(std::move(selector)) {}\n\n  template <\n      class Value,\n      class Source,\n      class ValueDecayed = typename std::decay<Value>::type,\n      class Key = invoke_result_t<Selector, Value>,\n      class KeyDecayed = typename std::decay<Key>::type>\n  class Generator\n      : public GenImpl<\n            Group<KeyDecayed, ValueDecayed>&&,\n            Generator<Value, Source, ValueDecayed, Key, KeyDecayed>> {\n    static_assert(!Source::infinite, \"Cannot group infinite source!\");\n    Source source_;\n    Selector selector_;\n\n   public:\n    Generator(Source source, Selector selector)\n        : source_(std::move(source)), selector_(std::move(selector)) {}\n\n    typedef Group<KeyDecayed, ValueDecayed> GroupType;\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      folly::F14FastMap<KeyDecayed, typename GroupType::VectorType> groups;\n      source_ | [&](Value value) {\n        const Value& cv = value;\n        auto& group = groups[selector_(cv)];\n        group.push_back(std::forward<Value>(value));\n      };\n      for (auto& kg : groups) {\n        GroupType group(kg.first, std::move(kg.second));\n        if (!handler(std::move(group))) {\n          return false;\n        }\n        kg.second.clear();\n      }\n      return true;\n    }\n\n    // Can only be run on and produce finite generators\n    static constexpr bool infinite = false;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), selector_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), selector_);\n  }\n};\n\n/**\n * GroupByAdjacent - Group adjacent values by a given key selector, producing a\n * sequence of groups. This differs from GroupBy in that only contiguous sets\n * of values with the same key are considered part of the same group. Unlike\n * GroupBy, this can be used on infinite sequences.\n *\n * This type is usually used through the 'groupByAdjacent' helper function:\n *\n *   auto tens\n *     = seq(0)\n *     | groupByAdjacent([](int i){ return (i / 10) % 2; })\n *\n * This example results in a list like [ 0:[0-9], 1:[10-19], 0:[20-29], ... ]\n */\ntemplate <class Selector>\nclass GroupByAdjacent : public Operator<GroupByAdjacent<Selector>> {\n  Selector selector_;\n\n public:\n  GroupByAdjacent() {}\n\n  explicit GroupByAdjacent(Selector selector)\n      : selector_(std::move(selector)) {}\n\n  template <\n      class Value,\n      class Source,\n      class ValueDecayed = typename std::decay<Value>::type,\n      class Key = invoke_result_t<Selector, Value>,\n      class KeyDecayed = typename std::decay<Key>::type>\n  class Generator\n      : public GenImpl<\n            Group<KeyDecayed, ValueDecayed>&&,\n            Generator<Value, Source, ValueDecayed, Key, KeyDecayed>> {\n    Source source_;\n    Selector selector_;\n\n   public:\n    Generator(Source source, Selector selector)\n        : source_(std::move(source)), selector_(std::move(selector)) {}\n\n    typedef Group<KeyDecayed, ValueDecayed> GroupType;\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      Optional<KeyDecayed> key = none;\n      typename GroupType::VectorType values;\n\n      bool result = source_.apply([&](Value value) mutable {\n        KeyDecayed newKey = selector_(value);\n\n        // start the first group\n        if (!key.hasValue()) {\n          key.emplace(newKey);\n        }\n\n        if (key == newKey) {\n          // grow the current group\n          values.push_back(std::forward<Value>(value));\n        } else {\n          // flush the current group\n          GroupType group(key.value(), std::move(values));\n          if (!handler(std::move(group))) {\n            return false;\n          }\n\n          // start a new group\n          key.emplace(newKey);\n          values.clear();\n          values.push_back(std::forward<Value>(value));\n        }\n        return true;\n      });\n\n      if (!result) {\n        return false;\n      }\n\n      if (!key.hasValue()) {\n        return true;\n      }\n\n      // flush the final group\n      GroupType group(key.value(), std::move(values));\n      return handler(std::move(group));\n    }\n\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), selector_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), selector_);\n  }\n};\n\n/*\n * TypeAssertion - For verifying the exact type of the value produced by a\n * generator. Useful for testing and debugging, and acts as a no-op at runtime.\n * Pass-through at runtime. Used through the 'assert_type<>()' factory method\n * like so:\n *\n *   auto c =  from(vector) | assert_type<int&>() | sum;\n *\n */\ntemplate <class Expected>\nclass TypeAssertion : public Operator<TypeAssertion<Expected>> {\n public:\n  TypeAssertion() = default;\n  template <class Source, class Value>\n  const Source& compose(const GenImpl<Value, Source>& source) const {\n    static_assert(\n        std::is_same<Expected, Value>::value, \"assert_type() check failed\");\n    return source.self();\n  }\n\n  template <class Source, class Value>\n  Source&& compose(GenImpl<Value, Source>&& source) const {\n    static_assert(\n        std::is_same<Expected, Value>::value, \"assert_type() check failed\");\n    return std::move(source.self());\n  }\n};\n\n/**\n * Distinct - For filtering duplicates out of a sequence. A selector may be\n * provided to generate a key to uniquify for each value.\n *\n * This type is usually used through the 'distinct' helper function, like:\n *\n *   auto closest = from(results)\n *                | distinctBy([](Item& i) {\n *                    return i.target;\n *                  })\n *                | take(10);\n */\ntemplate <class Selector>\nclass Distinct : public Operator<Distinct<Selector>> {\n  Selector selector_;\n\n public:\n  Distinct() = default;\n\n  explicit Distinct(Selector selector) : selector_(std::move(selector)) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    Selector selector_;\n\n    typedef typename std::decay<Value>::type StorageType;\n\n    // selector_ cannot be passed an rvalue or it would end up passing the husk\n    // of a value to the downstream operators.\n    typedef const StorageType& ParamType;\n\n    typedef invoke_result_t<Selector, ParamType> KeyType;\n    typedef typename std::decay<KeyType>::type KeyStorageType;\n\n   public:\n    Generator(Source source, Selector selector)\n        : source_(std::move(source)), selector_(std::move(selector)) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      folly::F14FastSet<KeyStorageType> keysSeen;\n      source_.foreach([&](Value value) {\n        if (keysSeen.insert(selector_(ParamType(value))).second) {\n          body(std::forward<Value>(value));\n        }\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      folly::F14FastSet<KeyStorageType> keysSeen;\n      return source_.apply([&](Value value) -> bool {\n        if (keysSeen.insert(selector_(ParamType(value))).second) {\n          return handler(std::forward<Value>(value));\n        }\n        return true;\n      });\n    }\n\n    // While running distinct on an infinite sequence might produce a\n    // conceptually finite sequence, it will take infinite time\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), selector_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), selector_);\n  }\n};\n\n/**\n * Composer - Helper class for adapting pipelines into functors. Primarily used\n * for 'mapOp'.\n */\ntemplate <class Operators>\nclass Composer {\n  Operators op_;\n\n public:\n  explicit Composer(Operators op) : op_(std::move(op)) {}\n\n  template <\n      class Source,\n      class Ret =\n          decltype(std::declval<Operators>().compose(std::declval<Source>()))>\n  Ret operator()(Source&& source) const {\n    return op_.compose(std::forward<Source>(source));\n  }\n};\n\n/**\n * Batch - For producing fixed-size batches of each value from a source.\n *\n * This type is usually used through the 'batch' helper function:\n *\n *   auto batchSums\n *     = seq(1, 10)\n *     | batch(3)\n *     | map([](const std::vector<int>& batch) {\n *         return from(batch) | sum;\n *       })\n *     | as<vector>();\n */\nclass Batch : public Operator<Batch> {\n  size_t batchSize_;\n\n public:\n  explicit Batch(size_t batchSize) : batchSize_(batchSize) {\n    if (batchSize_ == 0) {\n      throw std::invalid_argument(\"Batch size must be non-zero!\");\n    }\n  }\n\n  template <\n      class Value,\n      class Source,\n      class StorageType = typename std::decay<Value>::type,\n      class VectorType = std::vector<StorageType>>\n  class Generator\n      : public GenImpl<\n            VectorType&,\n            Generator<Value, Source, StorageType, VectorType>> {\n    Source source_;\n    size_t batchSize_;\n\n   public:\n    explicit Generator(Source source, size_t batchSize)\n        : source_(std::move(source)), batchSize_(batchSize) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      VectorType batch_;\n      batch_.reserve(batchSize_);\n      bool shouldContinue = source_.apply([&](Value value) -> bool {\n        batch_.push_back(std::forward<Value>(value));\n        if (batch_.size() == batchSize_) {\n          bool needMore = handler(batch_);\n          batch_.clear();\n          return needMore;\n        }\n        // Always need more if the handler is not called.\n        return true;\n      });\n      // Flush everything, if and only if `handler` hasn't returned false.\n      if (shouldContinue && !batch_.empty()) {\n        shouldContinue = handler(batch_);\n        batch_.clear();\n      }\n      return shouldContinue;\n    }\n\n    // Taking n-tuples of an infinite source is still infinite\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), batchSize_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), batchSize_);\n  }\n};\n\n/**\n * Window - For overlapping the lifetimes of pipeline values, especially with\n * Futures.\n *\n * This type is usually used through the 'window' helper function:\n *\n *   auto responses\n *     = byLine(STDIN)\n *     | map(makeRequestFuture)\n *     | window(1000)\n *     | map(waitFuture)\n *     | as<vector>();\n */\nclass Window : public Operator<Window> {\n  size_t windowSize_;\n\n public:\n  explicit Window(size_t windowSize) : windowSize_(windowSize) {\n    if (windowSize_ == 0) {\n      throw std::invalid_argument(\"Window size must be non-zero!\");\n    }\n  }\n\n  template <\n      class Value,\n      class Source,\n      class StorageType = typename std::decay<Value>::type>\n  class Generator\n      : public GenImpl<StorageType&&, Generator<Value, Source, StorageType>> {\n    Source source_;\n    size_t windowSize_;\n\n   public:\n    explicit Generator(Source source, size_t windowSize)\n        : source_(std::move(source)), windowSize_(windowSize) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      std::vector<StorageType> buffer;\n      buffer.reserve(windowSize_);\n      size_t readIndex = 0;\n      bool shouldContinue = source_.apply([&](Value value) -> bool {\n        if (buffer.size() < windowSize_) {\n          buffer.push_back(std::forward<Value>(value));\n        } else {\n          StorageType& entry = buffer[readIndex++];\n          if (readIndex == windowSize_) {\n            readIndex = 0;\n          }\n          if (!handler(std::move(entry))) {\n            return false;\n          }\n          entry = std::forward<Value>(value);\n        }\n        return true;\n      });\n      if (!shouldContinue) {\n        return false;\n      }\n      if (buffer.size() < windowSize_) {\n        for (StorageType& entry : buffer) {\n          if (!handler(std::move(entry))) {\n            return false;\n          }\n        }\n      } else {\n        for (size_t i = readIndex;;) {\n          StorageType& entry = buffer[i++];\n          if (!handler(std::move(entry))) {\n            return false;\n          }\n          if (i == windowSize_) {\n            i = 0;\n          }\n          if (i == readIndex) {\n            break;\n          }\n        }\n      }\n      return true;\n    }\n\n    // Taking n-tuples of an infinite source is still infinite\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), windowSize_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), windowSize_);\n  }\n};\n\n/**\n * Concat - For flattening generators of generators.\n *\n * This type is usually used through the 'concat' static value, like:\n *\n *   auto edges =\n *       from(nodes)\n *     | map([](Node& x) {\n *           return from(x.neighbors)\n *                | map([&](Node& y) {\n *                    return Edge(x, y);\n *                  });\n *         })\n *     | concat\n *     | as<std::set>();\n */\nclass Concat : public Operator<Concat> {\n public:\n  Concat() = default;\n\n  template <\n      class Inner,\n      class Source,\n      class InnerValue = typename std::decay<Inner>::type::ValueType>\n  class Generator\n      : public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {\n    Source source_;\n\n   public:\n    explicit Generator(Source source) : source_(std::move(source)) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Inner inner) -> bool {\n        return inner.apply(std::forward<Handler>(handler));\n      });\n    }\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      source_.foreach([&](Inner inner) {\n        inner.foreach(std::forward<Body>(body));\n      });\n    }\n\n    // Resulting concatenation is only finite if both Source and Inner are also\n    // finite. In one sence, if dosn't make sence to call concat when the Inner\n    // generator is infinite (you could just call first), so we could also just\n    // static_assert if the inner is infinite. Taking the less restrictive\n    // approch for now.\n    static constexpr bool infinite =\n        Source::infinite || std::decay<Inner>::type::infinite;\n  };\n\n  template <class Value, class Source, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()));\n  }\n\n  template <class Value, class Source, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self());\n  }\n};\n\n/**\n * RangeConcat - For flattening generators of iterables.\n *\n * This type is usually used through the 'rconcat' static value, like:\n *\n *   map<int, vector<int>> adjacency;\n *   auto sinks =\n *       from(adjacency)\n *     | get<1>()\n *     | rconcat()\n *     | as<std::set>();\n */\nclass RangeConcat : public Operator<RangeConcat> {\n public:\n  RangeConcat() = default;\n\n  template <\n      class Range,\n      class Source,\n      class InnerValue = typename ValueTypeOfRange<Range>::RefType>\n  class Generator\n      : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {\n    Source source_;\n\n   public:\n    explicit Generator(Source source) : source_(std::move(source)) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      source_.foreach([&](Range range) {\n        for (auto& value : range) {\n          body(value);\n        }\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Range range) -> bool {\n        for (auto& value : range) {\n          if (!handler(value)) {\n            return false;\n          }\n        }\n        return true;\n      });\n    }\n\n    // This is similar to concat, except that the inner iterables all are finite\n    // so the only thing that matters is that the source is infinite.\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Value, class Source, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()));\n  }\n\n  template <class Value, class Source, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self());\n  }\n};\n\n/**\n * GuardImpl - For handling exceptions from downstream computation. Requires the\n * type of exception to catch, and handler function to invoke in the event of\n * the exception. Note that the handler may:\n *   1) return true to continue processing the sequence\n *   2) return false to end the sequence immediately\n *   3) throw, to pass the exception to the next catch\n * The handler must match the signature 'bool(Exception&, Value)'.\n *\n * This type is used through the `guard` helper, like so:\n *\n *  auto indexes\n *    = byLine(STDIN_FILENO)\n *    | guard<std::runtime_error>([](std::runtime_error& e,\n *                                   StringPiece sp) {\n *        LOG(ERROR) << sp << \": \" << e.str();\n *        return true; // continue processing subsequent lines\n *      })\n *    | eachTo<int>()\n *    | as<vector>();\n *\n *  KNOWN ISSUE: This only guards pipelines through operators which do not\n *  retain resulting values. Exceptions thrown after operators like pmap, order,\n *  batch, cannot be caught from here.\n **/\ntemplate <class Exception, class ErrorHandler>\nclass GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {\n  ErrorHandler exceptionHandler_;\n\n public:\n  explicit GuardImpl(ErrorHandler handler)\n      : exceptionHandler_(std::move(handler)) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    ErrorHandler exceptionHandler_;\n\n   public:\n    explicit Generator(Source source, ErrorHandler handler)\n        : source_(std::move(source)), exceptionHandler_(std::move(handler)) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Value value) -> bool {\n        try {\n          return handler(std::forward<Value>(value));\n        } catch (Exception& e) {\n          return exceptionHandler_(e, std::forward<Value>(value));\n        }\n      });\n    }\n\n    // Just passes value though, length unaffected\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Value, class Source, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), exceptionHandler_);\n  }\n\n  template <class Value, class Source, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), exceptionHandler_);\n  }\n};\n\n/**\n * Dereference - For dereferencing a sequence of pointers while filtering out\n * null pointers.\n *\n * This type is usually used through the 'dereference' static value, like:\n *\n *   auto refs = from(ptrs) | dereference;\n */\nclass Dereference : public Operator<Dereference> {\n public:\n  Dereference() = default;\n\n  template <\n      class Value,\n      class Source,\n      class Result = decltype(*std::declval<Value>())>\n  class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {\n    Source source_;\n\n   public:\n    explicit Generator(Source source) : source_(std::move(source)) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      source_.foreach([&](Value value) {\n        if (value) {\n          return body(*std::forward<Value>(value));\n        }\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Value value) -> bool {\n        return !value || handler(*std::forward<Value>(value));\n      });\n    }\n\n    // Just passes value though, length unaffected\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()));\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self());\n  }\n};\n\n/**\n * Indirect - For producing a sequence of the addresses of the values in the\n * input.\n *\n * This type is usually used through the 'indirect' static value, like:\n *\n *   auto ptrs = from(refs) | indirect;\n */\nclass Indirect : public Operator<Indirect> {\n public:\n  Indirect() = default;\n\n  template <\n      class Value,\n      class Source,\n      class Result = typename std::remove_reference<Value>::type*>\n  class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {\n    Source source_;\n    static_assert(\n        !std::is_rvalue_reference<Value>::value,\n        \"Cannot use indirect on an rvalue\");\n\n   public:\n    explicit Generator(Source source) : source_(std::move(source)) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      source_.foreach([&](Value value) {\n        return body(&std::forward<Value>(value));\n      });\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      return source_.apply([&](Value value) -> bool {\n        return handler(&std::forward<Value>(value));\n      });\n    }\n\n    // Just passes value though, length unaffected\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()));\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self());\n  }\n};\n\n/**\n * Cycle - For repeating a sequence forever.\n *\n * This type is usually used through the 'cycle' static value, like:\n *\n *   auto tests\n *     = from(samples)\n *     | cycle\n *     | take(100);\n *\n * or in the finite case:\n *\n *   auto thrice = g | cycle(3);\n */\ntemplate <bool forever>\nclass Cycle : public Operator<Cycle<forever>> {\n  off_t limit_; // not used if forever == true\n public:\n  Cycle() = default;\n\n  explicit Cycle(off_t limit) : limit_(limit) {\n    static_assert(\n        !forever,\n        \"Cycle limit constructor should not be used when forever == true.\");\n  }\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    off_t limit_;\n\n   public:\n    explicit Generator(Source source, off_t limit)\n        : source_(std::move(source)), limit_(limit) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      bool cont;\n      auto handler2 = [&](Value value) {\n        cont = handler(std::forward<Value>(value));\n        return cont;\n      };\n      // Becomes an infinte loop if forever == true\n      for (off_t count = 0; (forever || count != limit_); ++count) {\n        cont = false;\n        source_.apply(handler2);\n        if (!cont) {\n          return false;\n        }\n      }\n      return true;\n    }\n\n    // This is the hardest one to infer. If we are simply doing a finite cycle,\n    // then (gen | cycle(n)) is infinite if and only if gen is infinite.\n    // However, if we are doing an infinite cycle, (gen | cycle) is infinite\n    // unless gen is empty. However, we will always mark (gen | cycle) as\n    // infinite, because patterns such as (gen | cycle | count) can either take\n    // on exactly one value, or infinite loop.\n    static constexpr bool infinite = forever || Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), limit_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), limit_);\n  }\n\n  /**\n   * Convenience function for finite cycles used like:\n   *\n   *  auto tripled = gen | cycle(3);\n   */\n  Cycle<false> operator()(off_t limit) const { return Cycle<false>(limit); }\n};\n\n/*\n ******************************* Sinks *****************************************\n */\n\n/**\n * FoldLeft - Left-associative functional fold. For producing an aggregate value\n * from a seed and a folder function. Useful for custom aggregators on a\n * sequence.\n *\n * This type is primarily used through the 'foldl' helper method, like:\n *\n *   double movingAverage = from(values)\n *                        | foldl(0.0, [](double avg, double sample) {\n *                            return sample * 0.1 + avg * 0.9;\n *                          });\n */\ntemplate <class Seed, class Fold>\nclass FoldLeft : public Operator<FoldLeft<Seed, Fold>> {\n  Seed seed_;\n  Fold fold_;\n\n public:\n  FoldLeft() = default;\n  FoldLeft(Seed seed, Fold fold)\n      : seed_(std::move(seed)), fold_(std::move(fold)) {}\n\n  template <class Source, class Value>\n  Seed compose(const GenImpl<Value, Source>& source) const {\n    static_assert(!Source::infinite, \"Cannot foldl infinite source\");\n    Seed accum = seed_;\n    source | [&](Value v) {\n      accum = fold_(std::move(accum), std::forward<Value>(v));\n    };\n    return accum;\n  }\n};\n\n/**\n * First - For finding the first value in a sequence.\n *\n * This type is primarily used through the 'first' static value, like:\n *\n *   int firstThreeDigitPrime = seq(100) | filter(isPrime) | first;\n */\nclass First : public Operator<First> {\n public:\n  First() = default;\n\n  template <\n      class Source,\n      class Value,\n      class StorageType = typename std::decay<Value>::type>\n  Optional<StorageType> compose(const GenImpl<Value, Source>& source) const {\n    Optional<StorageType> accum;\n    source | [&](Value v) -> bool {\n      accum = std::forward<Value>(v);\n      return false;\n    };\n    return accum;\n  }\n};\n\n/**\n * IsEmpty - a helper class for isEmpty and notEmpty\n *\n * Essentially returns 'result' if the source is empty. Note that this cannot be\n * called on an infinite source, because then there is only one possible return\n * value.\n *\n *\n *  Used primarily through 'isEmpty' and 'notEmpty' static values\n *\n *  bool hasPrimes = g | filter(prime) | notEmpty;\n *  bool lacksEvens = g | filter(even) | isEmpty;\n *\n *  Also used in the implementation of 'any' and 'all'\n */\ntemplate <bool emptyResult>\nclass IsEmpty : public Operator<IsEmpty<emptyResult>> {\n public:\n  IsEmpty() = default;\n\n  template <class Source, class Value>\n  bool compose(const GenImpl<Value, Source>& source) const {\n    static_assert(\n        !Source::infinite,\n        \"Cannot call 'all', 'any', 'isEmpty', or 'notEmpty' on \"\n        \"infinite source. 'all' and 'isEmpty' will either return \"\n        \"false or hang. 'any' or 'notEmpty' will either return true \"\n        \"or hang.\");\n    bool ans = emptyResult;\n    source | [&](Value /* v */) -> bool {\n      ans = !emptyResult;\n      return false;\n    };\n    return ans;\n  }\n};\n\n/**\n * Reduce - Functional reduce, for recursively combining values from a source\n * using a reducer function until there is only one item left. Useful for\n * combining values when an empty sequence doesn't make sense.\n *\n * This type is primarily used through the 'reduce' helper method, like:\n *\n *   sring longest = from(names)\n *                 | reduce([](string&& best, string& current) {\n *                     return best.size() >= current.size() ? best : current;\n *                   });\n */\ntemplate <class Reducer>\nclass Reduce : public Operator<Reduce<Reducer>> {\n  Reducer reducer_;\n\n public:\n  Reduce() = default;\n  explicit Reduce(Reducer reducer) : reducer_(std::move(reducer)) {}\n\n  template <\n      class Source,\n      class Value,\n      class StorageType = typename std::decay<Value>::type>\n  Optional<StorageType> compose(const GenImpl<Value, Source>& source) const {\n    static_assert(!Source::infinite, \"Cannot reduce infinite source\");\n    Optional<StorageType> accum;\n    source | [&](Value v) {\n      if (auto target = accum.get_pointer()) {\n        *target = reducer_(std::move(*target), std::forward<Value>(v));\n      } else {\n        accum = std::forward<Value>(v);\n      }\n    };\n    return accum;\n  }\n};\n\n/**\n * Count - for simply counting the items in a collection.\n *\n * This type is usually used through its singleton, 'count':\n *\n *   auto shortPrimes = seq(1, 100) | filter(isPrime) | count;\n */\nclass Count : public Operator<Count> {\n public:\n  Count() = default;\n\n  template <class Source, class Value>\n  size_t compose(const GenImpl<Value, Source>& source) const {\n    static_assert(!Source::infinite, \"Cannot count infinite source\");\n    return foldl(\n               size_t(0), [](size_t accum, Value /* v */) { return accum + 1; })\n        .compose(source);\n  }\n};\n\n/**\n * Sum - For simply summing up all the values from a source.\n *\n * This type is usually used through its singleton, 'sum':\n *\n *   auto gaussSum = seq(1, 100) | sum;\n */\nclass Sum : public Operator<Sum> {\n public:\n  Sum() = default;\n\n  template <\n      class Source,\n      class Value,\n      class StorageType = typename std::decay<Value>::type>\n  StorageType compose(const GenImpl<Value, Source>& source) const {\n    static_assert(!Source::infinite, \"Cannot sum infinite source\");\n    return foldl(\n               StorageType(0),\n               [](StorageType&& accum, Value v) {\n                 return std::move(accum) + std::forward<Value>(v);\n               })\n        .compose(source);\n  }\n};\n\n/**\n * Contains - For testing whether a value matching the given value is contained\n * in a sequence.\n *\n * This type should be used through the 'contains' helper method, like:\n *\n *   bool contained = seq(1, 10) | map(square) | contains(49);\n */\ntemplate <class Needle>\nclass Contains : public Operator<Contains<Needle>> {\n  Needle needle_;\n\n public:\n  explicit Contains(Needle needle) : needle_(std::move(needle)) {}\n\n  template <\n      class Source,\n      class Value,\n      class StorageType = typename std::decay<Value>::type>\n  bool compose(const GenImpl<Value, Source>& source) const {\n    static_assert(\n        !Source::infinite,\n        \"Calling contains on an infinite source might cause \"\n        \"an infinite loop.\");\n    return !(source | [this](Value value) {\n      return !(needle_ == std::forward<Value>(value));\n    });\n  }\n};\n\n/**\n * Min - For a value which minimizes a key, where the key is determined by a\n * given selector, and compared by given comparer.\n *\n * This type is usually used through the singletone 'min' or through the helper\n * functions 'minBy' and 'maxBy'.\n *\n *   auto oldest = from(people)\n *               | minBy([](Person& p) {\n *                   return p.dateOfBirth;\n *                 });\n */\ntemplate <class Selector, class Comparer>\nclass Min : public Operator<Min<Selector, Comparer>> {\n  Selector selector_;\n  Comparer comparer_;\n\n  template <typename T>\n  const T& asConst(const T& t) const {\n    return t;\n  }\n\n public:\n  Min() = default;\n\n  explicit Min(Selector selector) : selector_(std::move(selector)) {}\n\n  Min(Selector selector, Comparer comparer)\n      : selector_(std::move(selector)), comparer_(std::move(comparer)) {}\n\n  template <\n      class Value,\n      class Source,\n      class StorageType = typename std::decay<Value>::type,\n      class Key = typename std::decay<invoke_result_t<Selector, Value>>::type>\n  Optional<StorageType> compose(const GenImpl<Value, Source>& source) const {\n    static_assert(\n        !Source::infinite,\n        \"Calling min or max on an infinite source will cause \"\n        \"an infinite loop.\");\n    Optional<StorageType> min;\n    Optional<Key> minKey;\n    source | [&](Value v) {\n      Key key = selector_(asConst(v)); // so that selector_ cannot mutate v\n      if (auto lastKey = minKey.get_pointer()) {\n        if (!comparer_(key, *lastKey)) {\n          return;\n        }\n      }\n      minKey = std::move(key);\n      min = std::forward<Value>(v);\n    };\n    return min;\n  }\n};\n\n/**\n * Append - For collecting values from a source into a given output container\n * by appending.\n *\n * This type is usually used through the helper function 'appendTo', like:\n *\n *   vector<int64_t> ids;\n *   from(results) | map([](Person& p) { return p.id })\n *                 | appendTo(ids);\n */\ntemplate <class Collection>\nclass Append : public Operator<Append<Collection>> {\n  Collection* collection_;\n\n public:\n  explicit Append(Collection* collection) : collection_(collection) {}\n\n  template <class Value, class Source>\n  Collection& compose(const GenImpl<Value, Source>& source) const {\n    static_assert(!Source::infinite, \"Cannot appendTo with infinite source\");\n    source | [&](Value v) {\n      collection_->insert(collection_->end(), std::forward<Value>(v));\n    };\n    return *collection_;\n  }\n};\n\n/**\n * Collect - For collecting values from a source in a collection of the desired\n * type.\n *\n * This type is usually used through the helper function 'as', like:\n *\n *   std::string upper = from(stringPiece)\n *                     | map(&toupper)\n *                     | as<std::string>();\n */\ntemplate <class Collection>\nclass Collect : public Operator<Collect<Collection>> {\n public:\n  Collect() = default;\n\n  template <\n      class Value,\n      class Source,\n      class StorageType = typename std::decay<Value>::type>\n  Collection compose(const GenImpl<Value, Source>& source) const {\n    static_assert(\n        !Source::infinite, \"Cannot convert infinite source to object with as.\");\n    Collection collection;\n    source | [&](Value v) {\n      collection.insert(collection.end(), std::forward<Value>(v));\n    };\n    return collection;\n  }\n};\n\n/**\n * CollectTemplate - For collecting values from a source in a collection\n * constructed using the specified template type. Given the type of values\n * produced by the given generator, the collection type will be:\n *   Container<Value, Allocator<Value>>\n *\n * The allocator defaults to std::allocator, so this may be used for the STL\n * containers by simply using operators like 'as<set>', 'as<deque>',\n * 'as<vector>'. 'as', here is the helper method which is the usual means of\n * constructing this operator.\n *\n * Example:\n *\n *   set<string> uniqueNames = from(names) | as<set>();\n */\ntemplate <\n    template <class, class> class Container,\n    template <class> class Allocator>\nclass CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {\n public:\n  CollectTemplate() = default;\n\n  template <\n      class Value,\n      class Source,\n      class StorageType = typename std::decay<Value>::type,\n      class Collection = Container<StorageType, Allocator<StorageType>>>\n  Collection compose(const GenImpl<Value, Source>& source) const {\n    static_assert(\n        !Source::infinite, \"Cannot convert infinite source to object with as.\");\n    Collection collection;\n    source | [&](Value v) {\n      collection.insert(collection.end(), std::forward<Value>(v));\n    };\n    return collection;\n  }\n};\n\n/**\n * UnwrapOr - For unwrapping folly::Optional values, or providing the given\n * fallback value. Usually used through the 'unwrapOr' helper like so:\n *\n *   auto best = from(scores) | max | unwrapOr(-1);\n *\n * Note that the fallback value needn't match the value in the Optional it is\n * unwrapping. If mis-matched types are supported, the common type of the two is\n * returned by value. If the types match, a reference (T&& > T& > const T&) is\n * returned.\n */\ntemplate <class T>\nclass UnwrapOr {\n public:\n  explicit UnwrapOr(T&& value) : value_(std::move(value)) {}\n  explicit UnwrapOr(const T& value) : value_(value) {}\n\n  T& value() { return value_; }\n  const T& value() const { return value_; }\n\n private:\n  T value_;\n};\n\ntemplate <class T>\nT&& operator|(Optional<T>&& opt, UnwrapOr<T>&& fallback) {\n  if (T* p = opt.get_pointer()) {\n    return std::move(*p);\n  }\n  return std::move(fallback.value());\n}\n\ntemplate <class T>\nT& operator|(Optional<T>& opt, UnwrapOr<T>& fallback) {\n  if (T* p = opt.get_pointer()) {\n    return *p;\n  }\n  return fallback.value();\n}\n\ntemplate <class T>\nconst T& operator|(const Optional<T>& opt, const UnwrapOr<T>& fallback) {\n  if (const T* p = opt.get_pointer()) {\n    return *p;\n  }\n  return fallback.value();\n}\n\n// Mixed type unwrapping always returns values, moving where possible\ntemplate <\n    class T,\n    class U,\n    class R = typename std::enable_if<\n        !std::is_same<T, U>::value,\n        typename std::common_type<T, U>::type>::type>\nR operator|(Optional<T>&& opt, UnwrapOr<U>&& fallback) {\n  if (T* p = opt.get_pointer()) {\n    return std::move(*p);\n  }\n  return std::move(fallback.value());\n}\n\ntemplate <\n    class T,\n    class U,\n    class R = typename std::enable_if<\n        !std::is_same<T, U>::value,\n        typename std::common_type<T, U>::type>::type>\nR operator|(const Optional<T>& opt, UnwrapOr<U>&& fallback) {\n  if (const T* p = opt.get_pointer()) {\n    return *p;\n  }\n  return std::move(fallback.value());\n}\n\ntemplate <\n    class T,\n    class U,\n    class R = typename std::enable_if<\n        !std::is_same<T, U>::value,\n        typename std::common_type<T, U>::type>::type>\nR operator|(Optional<T>&& opt, const UnwrapOr<U>& fallback) {\n  if (T* p = opt.get_pointer()) {\n    return std::move(*p);\n  }\n  return fallback.value();\n}\n\ntemplate <\n    class T,\n    class U,\n    class R = typename std::enable_if<\n        !std::is_same<T, U>::value,\n        typename std::common_type<T, U>::type>::type>\nR operator|(const Optional<T>& opt, const UnwrapOr<U>& fallback) {\n  if (const T* p = opt.get_pointer()) {\n    return *p;\n  }\n  return fallback.value();\n}\n\n/**\n * Unwrap - For unwrapping folly::Optional values in a folly::gen style. Usually\n * used through the 'unwrap' instace like so:\n *\n *   auto best = from(scores) | max | unwrap; // may throw\n */\nclass Unwrap {};\n\ntemplate <class T>\nT&& operator|(Optional<T>&& opt, const Unwrap&) {\n  return std::move(opt.value());\n}\n\ntemplate <class T>\nT& operator|(Optional<T>& opt, const Unwrap&) {\n  return opt.value();\n}\n\ntemplate <class T>\nconst T& operator|(const Optional<T>& opt, const Unwrap&) {\n  return opt.value();\n}\n\nclass ToVirtualGen : public Operator<ToVirtualGen> {\n public:\n  using Operator<ToVirtualGen>::Operator;\n  template <\n      class Source,\n      class Generator = VirtualGenMoveOnly<typename Source::ValueType>>\n  Generator compose(Source source) const {\n    return Generator(std::move(source.self()));\n  }\n};\n\n#if FOLLY_DETAIL_GEN_BASE_HAS_RANGEV3\ntemplate <class RangeV3, class Value>\nclass RangeV3Source\n    : public gen::GenImpl<Value, RangeV3Source<RangeV3, Value>> {\n  mutable RangeV3 r_; // mutable since some ranges are not const-iteratable\n\n public:\n  explicit RangeV3Source(RangeV3 const& r) : r_(r) {}\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    for (auto const& value : r_) {\n      body(value);\n    }\n  }\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    for (auto const& value : r_) {\n      if (!handler(value)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  static constexpr bool infinite = false;\n};\n\ntemplate <class RangeV3, class Value>\nclass RangeV3CopySource\n    : public gen::GenImpl<Value, RangeV3CopySource<RangeV3, Value>> {\n  mutable RangeV3 r_; // mutable since some ranges are not const-iteratable\n\n public:\n  explicit RangeV3CopySource(RangeV3&& r) : r_(std::move(r)) {}\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    for (auto const& value : r_) {\n      body(value);\n    }\n  }\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    for (auto const& value : r_) {\n      if (!handler(value)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  static constexpr bool infinite = false;\n};\n\nstruct from_container_fn {\n  template <typename Container>\n  friend auto operator|(Container&& c, from_container_fn) {\n    return gen::from(std::forward<Container>(c));\n  }\n};\n\nstruct from_rangev3_fn {\n  template <typename Range>\n  friend auto operator|(Range&& r, from_rangev3_fn) {\n    using DecayedRange = std::decay_t<Range>;\n    using DecayedValue = std::decay_t<decltype(*r.begin())>;\n    return RangeV3Source<DecayedRange, DecayedValue>(r);\n  }\n};\n\nstruct from_rangev3_copy_fn {\n  template <typename Range>\n  friend auto operator|(Range&& r, from_rangev3_copy_fn) {\n    using RangeDecay = std::decay_t<Range>;\n    using Value = std::decay_t<decltype(*r.begin())>;\n    return RangeV3CopySource<RangeDecay, Value>(std::move(r));\n  }\n};\n#endif // FOLLY_DETAIL_GEN_BASE_HAS_RANGEV3\n} // namespace detail\n\n#if FOLLY_DETAIL_GEN_BASE_HAS_RANGEV3\n/*\n ******************************************************************************\n * Pipe fittings between a container/range-v3 and a folly::gen.\n * Example: vec | gen::from_container | folly::gen::filter(...);\n * Example: vec | ranges::views::filter(...) | gen::from_rangev3 | gen::xxx;\n ******************************************************************************\n */\nconstexpr detail::from_container_fn from_container;\nconstexpr detail::from_rangev3_fn from_rangev3;\nconstexpr detail::from_rangev3_copy_fn from_rangev3_copy;\n\ntemplate <typename Range>\nauto from_rangev3_call(Range&& r) {\n  using Value = std::decay_t<decltype(*r.begin())>;\n  return detail::RangeV3Source<Range, Value>(r);\n}\n\n// it is safe to pipe an rvalue into a range-v3 view if the rest of the pipeline\n// will finish its traversal within the current full-expr, a condition provided\n// by folly::gen.\ntemplate <typename Range>\nauto rangev3_will_be_consumed(Range&& r) {\n  // intentionally use `r` instead of `std::forward<Range>(r)`; see above.\n  // range-v3 ranges copy in O(1) so it is appropriate.\n  return ::ranges::views::all(r);\n}\n#endif // FOLLY_DETAIL_GEN_BASE_HAS_RANGEV3\n\n/**\n * VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.\n **/\ntemplate <class Value, class Self>\nclass VirtualGenBase : public GenImpl<Value, Self> {\n protected:\n  class WrapperBase {\n   public:\n    virtual ~WrapperBase() noexcept {}\n    virtual bool apply(const FunctionRef<bool(Value)>& handler) const = 0;\n    virtual void foreach(const FunctionRef<void(Value)>& body) const = 0;\n    virtual std::unique_ptr<const WrapperBase> clone() const {\n      // clone() is only ever called from VirtualGen<>, where it\n      // is overridden with a real implementation.\n      return nullptr;\n    }\n  };\n\n  template <class Wrapped>\n  class WrapperImpl : public WrapperBase {\n   protected:\n    Wrapped wrapped_;\n\n   public:\n    explicit WrapperImpl(Wrapped wrapped) : wrapped_(std::move(wrapped)) {}\n\n    bool apply(const FunctionRef<bool(Value)>& handler) const final {\n      return wrapped_.apply(handler);\n    }\n\n    void foreach(const FunctionRef<void(Value)>& body) const final {\n      wrapped_.foreach(body);\n    }\n  };\n\n  std::unique_ptr<const WrapperBase> wrapper_;\n\n  VirtualGenBase() = default;\n  VirtualGenBase(VirtualGenBase&&) = default;\n  VirtualGenBase& operator=(VirtualGenBase&&) = default;\n\n public:\n  bool apply(const FunctionRef<bool(Value)>& handler) const {\n    return wrapper_->apply(handler);\n  }\n\n  void foreach(const FunctionRef<void(Value)>& body) const {\n    wrapper_->foreach(body);\n  }\n};\n\ntemplate <class Value>\nclass VirtualGen : public VirtualGenBase<Value, VirtualGen<Value>> {\n  using Base = VirtualGenBase<Value, VirtualGen>;\n  using WrapperBase = typename Base::WrapperBase;\n  template <class Wrapped>\n  using WrapperImpl = typename Base::template WrapperImpl<Wrapped>;\n\n  template <class Wrapped>\n  class CloneableWrapperImpl : public WrapperImpl<Wrapped> {\n   public:\n    using WrapperImpl<Wrapped>::WrapperImpl;\n\n    std::unique_ptr<const WrapperBase> clone() const final {\n      return std::make_unique<CloneableWrapperImpl<Wrapped>>(this->wrapped_);\n    }\n  };\n\n public:\n  VirtualGen() = default;\n  VirtualGen(VirtualGen&& source) = default;\n  VirtualGen& operator=(VirtualGen&& source) = default;\n\n  template <class Source>\n  /* implicit */ VirtualGen(Source source) {\n    this->wrapper_ =\n        std::make_unique<CloneableWrapperImpl<Source>>(std::move(source));\n  }\n\n  VirtualGen(const VirtualGen& source) {\n    this->wrapper_ = source.wrapper_->clone();\n  }\n\n  VirtualGen& operator=(const VirtualGen& source) {\n    return *this = VirtualGen(source);\n  }\n};\n\ntemplate <class Value>\nclass VirtualGenMoveOnly\n    : public VirtualGenBase<Value, VirtualGenMoveOnly<Value>> {\n public:\n  VirtualGenMoveOnly() = default;\n  VirtualGenMoveOnly(VirtualGenMoveOnly&& other) = default;\n  VirtualGenMoveOnly& operator=(VirtualGenMoveOnly&&) = default;\n\n  template <class Source>\n  /* implicit */ VirtualGenMoveOnly(Source source) {\n    this->wrapper_ = std::make_unique<\n        typename VirtualGenBase<Value, VirtualGenMoveOnly<Value>>::\n            template WrapperImpl<Source>>(std::move(source));\n  }\n};\n\n/**\n * non-template operators, statically defined to avoid the need for anything but\n * the header.\n */\nconstexpr detail::Sum sum{};\n\nconstexpr detail::Count count{};\n\nconstexpr detail::First first{};\n\nconstexpr detail::IsEmpty<true> isEmpty{};\n\nconstexpr detail::IsEmpty<false> notEmpty{};\n\nconstexpr detail::Min<Identity, Less> min{};\n\nconstexpr detail::Min<Identity, Greater> max{};\n\nconstexpr detail::Order<Identity> order{};\n\nconstexpr detail::Distinct<Identity> distinct{};\n\nconstexpr detail::Map<Move> move{};\n\nconstexpr detail::Concat concat{};\n\nconstexpr detail::RangeConcat rconcat{};\n\nconstexpr detail::Cycle<true> cycle{};\n\nconstexpr detail::Dereference dereference{};\n\nconstexpr detail::Indirect indirect{};\n\nconstexpr detail::Unwrap unwrap{};\n\nconstexpr detail::ToVirtualGen virtualize{};\n\ntemplate <class Number>\ninline detail::Take take(Number count) {\n  if (count < 0) {\n    throw std::invalid_argument(\"Negative value passed to take()\");\n  }\n  return detail::Take(static_cast<size_t>(count));\n}\n\ninline detail::Stride stride(size_t s) {\n  return detail::Stride(s);\n}\n\ntemplate <class Random = std::default_random_engine>\ninline detail::Sample<Random> sample(size_t count, Random rng = Random()) {\n  return detail::Sample<Random>(count, std::move(rng));\n}\n\ninline detail::Skip skip(size_t count) {\n  return detail::Skip(count);\n}\n\ninline detail::Batch batch(size_t batchSize) {\n  return detail::Batch(batchSize);\n}\n\ninline detail::Window window(size_t windowSize) {\n  return detail::Window(windowSize);\n}\n\n} // namespace gen\n} // namespace folly\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/gen/Base.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_GEN_BASE_H_\n\n#include <algorithm>\n#include <functional>\n#include <memory>\n#include <random>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/Conv.h>\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/Utility.h>\n#include <folly/container/Access.h>\n#include <folly/gen/Core.h>\n\n/**\n * Generator-based Sequence Comprehensions in C++, akin to C#'s LINQ\n *\n * This library makes it possible to write declarative comprehensions for\n * processing sequences of values efficiently in C++. The operators should be\n * familiar to those with experience in functional programming, and the\n * performance will be virtually identical to the equivalent, boilerplate C++\n * implementations.\n *\n * Generator objects may be created from either an stl-like container (anything\n * supporting begin() and end()), from sequences of values, or from another\n * generator (see below). To create a generator that pulls values from a vector,\n * for example, one could write:\n *\n *   vector<string> names { \"Jack\", \"Jill\", \"Sara\", \"Tom\" };\n *   auto gen = from(names);\n *\n * Generators are composed by building new generators out of old ones through\n * the use of operators. These are reminiscent of shell pipelines, and afford\n * similar composition. Lambda functions are used liberally to describe how to\n * handle individual values:\n *\n *   auto lengths = gen\n *                | mapped([](const fbstring& name) { return name.size(); });\n *\n * Generators are lazy; they don't actually perform any work until they need to.\n * As an example, the 'lengths' generator (above) won't actually invoke the\n * provided lambda until values are needed:\n *\n *   auto lengthVector = lengths | as<std::vector>();\n *   auto totalLength = lengths | sum;\n *\n * 'auto' is useful in here because the actual types of the generators objects\n * are usually complicated and implementation-sensitive.\n *\n * If a simpler type is desired (for returning, as an example), VirtualGen<T>\n * may be used to wrap the generator in a polymorphic wrapper:\n *\n *  VirtualGen<float> powersOfE() {\n *    return seq(1) | mapped(&expf);\n *  }\n *\n * To learn more about this library, including the use of infinite generators,\n * see the examples in the comments, or the docs (coming soon).\n */\n\nnamespace folly {\nnamespace gen {\n\nclass Less {\n public:\n  template <class First, class Second>\n  auto operator()(const First& first, const Second& second) const\n      -> decltype(first < second) {\n    return first < second;\n  }\n};\n\nclass Greater {\n public:\n  template <class First, class Second>\n  auto operator()(const First& first, const Second& second) const\n      -> decltype(first > second) {\n    return first > second;\n  }\n};\n\ntemplate <int n>\nclass Get {\n public:\n  template <class Value>\n  auto operator()(Value&& value) const\n      -> decltype(std::get<n>(std::forward<Value>(value))) {\n    return std::get<n>(std::forward<Value>(value));\n  }\n};\n\ntemplate <class Class, class Result>\nclass MemberFunction {\n public:\n  using MemberPtr = Result (Class::*)();\n\n private:\n  MemberPtr member_;\n\n public:\n  explicit MemberFunction(MemberPtr member) : member_(member) {}\n\n  Result operator()(Class&& x) const { return (x.*member_)(); }\n\n  Result operator()(Class& x) const { return (x.*member_)(); }\n\n  Result operator()(Class* x) const { return (x->*member_)(); }\n};\n\ntemplate <class Class, class Result>\nclass ConstMemberFunction {\n public:\n  using MemberPtr = Result (Class::*)() const;\n\n private:\n  MemberPtr member_;\n\n public:\n  explicit ConstMemberFunction(MemberPtr member) : member_(member) {}\n\n  Result operator()(const Class& x) const { return (x.*member_)(); }\n\n  Result operator()(const Class* x) const { return (x->*member_)(); }\n};\n\ntemplate <class Class, class FieldType>\nclass Field {\n public:\n  using FieldPtr = FieldType Class::*;\n\n private:\n  FieldPtr field_;\n\n public:\n  explicit Field(FieldPtr field) : field_(field) {}\n\n  const FieldType& operator()(const Class& x) const { return x.*field_; }\n\n  const FieldType& operator()(const Class* x) const { return x->*field_; }\n\n  FieldType& operator()(Class& x) const { return x.*field_; }\n\n  FieldType& operator()(Class* x) const { return x->*field_; }\n\n  FieldType&& operator()(Class&& x) const { return std::move(x.*field_); }\n};\n\nclass Move {\n public:\n  template <class Value>\n  auto operator()(Value&& value) const\n      -> decltype(std::move(std::forward<Value>(value))) {\n    return std::move(std::forward<Value>(value));\n  }\n};\n\n/**\n * Class and helper function for negating a boolean Predicate\n */\ntemplate <class Predicate>\nclass Negate {\n  Predicate pred_;\n\n public:\n  Negate() = default;\n\n  explicit Negate(Predicate pred) : pred_(std::move(pred)) {}\n\n  template <class Arg>\n  bool operator()(Arg&& arg) const {\n    return !pred_(std::forward<Arg>(arg));\n  }\n};\ntemplate <class Predicate>\nNegate<Predicate> negate(Predicate pred) {\n  return Negate<Predicate>(std::move(pred));\n}\n\ntemplate <class Dest>\nclass Cast {\n public:\n  template <class Value>\n  Dest operator()(Value&& value) const {\n    return Dest(std::forward<Value>(value));\n  }\n};\n\ntemplate <class Dest>\nclass To {\n public:\n  template <class Value>\n  Dest operator()(Value&& value) const {\n    return ::folly::to<Dest>(std::forward<Value>(value));\n  }\n};\n\ntemplate <class Dest>\nclass TryTo {\n public:\n  template <class Value>\n  Expected<Dest, ConversionCode> operator()(Value&& value) const {\n    return ::folly::tryTo<Dest>(std::forward<Value>(value));\n  }\n};\n\n// Specialization to allow String->StringPiece conversion\ntemplate <>\nclass To<StringPiece> {\n public:\n  StringPiece operator()(StringPiece src) const { return src; }\n};\n\ntemplate <class Key, class Value>\nclass Group;\n\nnamespace detail {\n\ntemplate <class Self>\nstruct FBounded;\n\n/*\n * Type Traits\n */\ntemplate <class Container>\nstruct ValueTypeOfRange {\n public:\n  using RefType = decltype(*access::begin(std::declval<Container&>()));\n  using StorageType = typename std::decay<RefType>::type;\n};\n\n/*\n * Sources\n */\ntemplate <\n    class Container,\n    class Value = typename ValueTypeOfRange<Container>::RefType>\nclass ReferencedSource;\n\ntemplate <\n    class Value,\n    class Container = std::vector<typename std::decay<Value>::type>>\nclass CopiedSource;\n\ntemplate <class Value, class SequenceImpl>\nclass Sequence;\n\ntemplate <class Value>\nclass RangeImpl;\n\ntemplate <class Value, class Distance>\nclass RangeWithStepImpl;\n\ntemplate <class Value>\nclass SeqImpl;\n\ntemplate <class Value, class Distance>\nclass SeqWithStepImpl;\n\ntemplate <class Value>\nclass InfiniteImpl;\n\ntemplate <class Value, class Source>\nclass Yield;\n\ntemplate <class Value>\nclass Empty;\n\ntemplate <class Value>\nclass SingleReference;\n\ntemplate <class Value>\nclass SingleCopy;\n\n/*\n * Operators\n */\ntemplate <class Predicate>\nclass Map;\n\ntemplate <class Predicate>\nclass Filter;\n\ntemplate <class Predicate>\nclass Until;\n\nclass Take;\n\nclass Stride;\n\ntemplate <class Rand>\nclass Sample;\n\nclass Skip;\n\ntemplate <class Visitor>\nclass Visit;\n\ntemplate <class Selector, class Comparer = Less>\nclass Order;\n\ntemplate <class Selector>\nclass GroupBy;\n\ntemplate <class Selector>\nclass GroupByAdjacent;\n\ntemplate <class Selector>\nclass Distinct;\n\ntemplate <class Operators>\nclass Composer;\n\ntemplate <class Expected>\nclass TypeAssertion;\n\nclass Concat;\n\nclass RangeConcat;\n\ntemplate <bool forever>\nclass Cycle;\n\nclass Batch;\n\nclass Window;\n\nclass Dereference;\n\nclass Indirect;\n\n/*\n * Sinks\n */\ntemplate <class Seed, class Fold>\nclass FoldLeft;\n\nclass First;\n\ntemplate <bool result>\nclass IsEmpty;\n\ntemplate <class Reducer>\nclass Reduce;\n\nclass Sum;\n\ntemplate <class Selector, class Comparer>\nclass Min;\n\ntemplate <class Container>\nclass Collect;\n\ntemplate <\n    template <class, class> class Collection = std::vector,\n    template <class> class Allocator = std::allocator>\nclass CollectTemplate;\n\ntemplate <class Collection>\nclass Append;\n\ntemplate <class Value>\nstruct GeneratorBuilder;\n\ntemplate <class Needle>\nclass Contains;\n\ntemplate <class Exception, class ErrorHandler>\nclass GuardImpl;\n\ntemplate <class T>\nclass UnwrapOr;\n\nclass Unwrap;\n\n} // namespace detail\n\n/**\n * Polymorphic wrapper\n **/\ntemplate <class Value>\nclass VirtualGen;\n\ntemplate <class Value>\nclass VirtualGenMoveOnly;\n\n/*\n * Source Factories\n */\ntemplate <\n    class Container,\n    class From = detail::ReferencedSource<const Container>>\nFrom fromConst(const Container& source) {\n  return From(&source);\n}\n\ntemplate <class Container, class From = detail::ReferencedSource<Container>>\nFrom from(Container& source) {\n  return From(&source);\n}\n\ntemplate <\n    class Container,\n    class Value = typename detail::ValueTypeOfRange<Container>::StorageType,\n    class CopyOf = detail::CopiedSource<Value>>\nCopyOf fromCopy(Container&& source) {\n  return CopyOf(std::forward<Container>(source));\n}\n\ntemplate <class Value, class From = detail::CopiedSource<Value>>\nFrom from(std::initializer_list<Value> source) {\n  return From(source);\n}\n\ntemplate <\n    class Container,\n    class From =\n        detail::CopiedSource<typename Container::value_type, Container>>\nFrom from(Container&& source) {\n  return From(std::move(source));\n}\n\ntemplate <\n    class Value,\n    class Impl = detail::RangeImpl<Value>,\n    class Gen = detail::Sequence<Value, Impl>>\nGen range(Value begin, Value end) {\n  return Gen{std::move(begin), Impl{std::move(end)}};\n}\n\ntemplate <\n    class Value,\n    class Distance,\n    class Impl = detail::RangeWithStepImpl<Value, Distance>,\n    class Gen = detail::Sequence<Value, Impl>>\nGen range(Value begin, Value end, Distance step) {\n  return Gen{std::move(begin), Impl{std::move(end), std::move(step)}};\n}\n\ntemplate <\n    class Value,\n    class Impl = detail::SeqImpl<Value>,\n    class Gen = detail::Sequence<Value, Impl>>\nGen seq(Value first, Value last) {\n  return Gen{std::move(first), Impl{std::move(last)}};\n}\n\ntemplate <\n    class Value,\n    class Distance,\n    class Impl = detail::SeqWithStepImpl<Value, Distance>,\n    class Gen = detail::Sequence<Value, Impl>>\nGen seq(Value first, Value last, Distance step) {\n  return Gen{std::move(first), Impl{std::move(last), std::move(step)}};\n}\n\ntemplate <\n    class Value,\n    class Impl = detail::InfiniteImpl<Value>,\n    class Gen = detail::Sequence<Value, Impl>>\nGen seq(Value first) {\n  return Gen{std::move(first), Impl{}};\n}\n\ntemplate <class Value, class Source, class Yield = detail::Yield<Value, Source>>\nYield generator(Source&& source) {\n  return Yield(std::forward<Source>(source));\n}\n\n/*\n * Create inline generator, used like:\n *\n *  auto gen = GENERATOR(int) { yield(1); yield(2); };\n *\n * GENERATOR_REF can be useful for creating a generator that doesn't\n * leave its original scope.\n */\n#define GENERATOR(TYPE) \\\n  ::folly::gen::detail::GeneratorBuilder<TYPE>() + [=](auto&& yield)\n#define GENERATOR_WITH_THIS(TYPE) \\\n  ::folly::gen::detail::GeneratorBuilder<TYPE>() + [ =, this ](auto&& yield)\n#define GENERATOR_REF(TYPE) \\\n  ::folly::gen::detail::GeneratorBuilder<TYPE>() + [&](auto&& yield)\n\n/*\n * empty() - for producing empty sequences.\n */\ntemplate <class Value>\ndetail::Empty<Value> empty() {\n  return {};\n}\n\ntemplate <\n    class Value,\n    class Just = typename std::conditional<\n        std::is_reference<Value>::value,\n        detail::SingleReference<typename std::remove_reference<Value>::type>,\n        detail::SingleCopy<Value>>::type>\nJust just(Value&& value) {\n  return Just(std::forward<Value>(value));\n}\n\n/*\n * Operator Factories\n */\ntemplate <class Predicate, class Map = detail::Map<Predicate>>\nMap mapped(Predicate pred = Predicate()) {\n  return Map(std::move(pred));\n}\n\ntemplate <class Predicate, class Map = detail::Map<Predicate>>\nMap map(Predicate pred = Predicate()) {\n  return Map(std::move(pred));\n}\n\n/**\n * mapOp - Given a generator of generators, maps the application of the given\n * operator on to each inner gen. Especially useful in aggregating nested data\n * structures:\n *\n *   chunked(samples, 256)\n *     | mapOp(filter(sampleTest) | count)\n *     | sum;\n */\ntemplate <class Operator, class Map = detail::Map<detail::Composer<Operator>>>\nMap mapOp(Operator op) {\n  return Map(detail::Composer<Operator>(std::move(op)));\n}\n\n/*\n * member(...) - For extracting a member from each value.\n *\n *  vector<string> strings = ...;\n *  auto sizes = from(strings) | member(&string::size);\n *\n * If a member is const overridden (like 'front()'), pass template parameter\n * 'Const' to select the const version, or 'Mutable' to select the non-const\n * version:\n *\n *  auto heads = from(strings) | member<Const>(&string::front);\n */\nenum MemberType {\n  Const,\n  Mutable,\n};\n\n/**\n * These exist because MSVC has problems with expression SFINAE in templates\n * assignment and comparisons don't work properly without being pulled out\n * of the template declaration\n */\ntemplate <MemberType Constness>\nstruct ExprIsConst {\n  enum {\n    value = Constness == Const,\n  };\n};\n\ntemplate <MemberType Constness>\nstruct ExprIsMutable {\n  enum {\n    value = Constness == Mutable,\n  };\n};\n\ntemplate <\n    MemberType Constness = Const,\n    class Class,\n    class Return,\n    class Mem = ConstMemberFunction<Class, Return>,\n    class Map = detail::Map<Mem>>\ntypename std::enable_if<ExprIsConst<Constness>::value, Map>::type member(\n    Return (Class::*member)() const) {\n  return Map(Mem(member));\n}\n\ntemplate <\n    MemberType Constness = Mutable,\n    class Class,\n    class Return,\n    class Mem = MemberFunction<Class, Return>,\n    class Map = detail::Map<Mem>>\ntypename std::enable_if<ExprIsMutable<Constness>::value, Map>::type member(\n    Return (Class::*member)()) {\n  return Map(Mem(member));\n}\n\n/*\n * field(...) - For extracting a field from each value.\n *\n *  vector<Item> items = ...;\n *  auto names = from(items) | field(&Item::name);\n *\n * Note that if the values of the generator are rvalues, any non-reference\n * fields will be rvalues as well. As an example, the code below does not copy\n * any strings, only moves them:\n *\n *  auto namesVector = from(items)\n *                   | move\n *                   | field(&Item::name)\n *                   | as<vector>();\n */\ntemplate <\n    class Class,\n    class FieldType,\n    class Field = Field<Class, FieldType>,\n    class Map = detail::Map<Field>>\nMap field(FieldType Class::* field) {\n  return Map(Field(field));\n}\n\ntemplate <class Predicate = Identity, class Filter = detail::Filter<Predicate>>\nFilter filter(Predicate pred = Predicate()) {\n  return Filter(std::move(pred));\n}\n\ntemplate <class Visitor = Ignore, class Visit = detail::Visit<Visitor>>\nVisit visit(Visitor visitor = Visitor()) {\n  return Visit(std::move(visitor));\n}\n\ntemplate <class Predicate = Identity, class Until = detail::Until<Predicate>>\nUntil until(Predicate pred = Predicate()) {\n  return Until(std::move(pred));\n}\n\ntemplate <\n    class Predicate = Identity,\n    class TakeWhile = detail::Until<Negate<Predicate>>>\nTakeWhile takeWhile(Predicate pred = Predicate()) {\n  return TakeWhile(Negate<Predicate>(std::move(pred)));\n}\n\ntemplate <\n    class Selector = Identity,\n    class Comparer = Less,\n    class Order = detail::Order<Selector, Comparer>>\nOrder orderBy(Selector selector = Selector(), Comparer comparer = Comparer()) {\n  return Order(std::move(selector), std::move(comparer));\n}\n\ntemplate <\n    class Selector = Identity,\n    class Order = detail::Order<Selector, Greater>>\nOrder orderByDescending(Selector selector = Selector()) {\n  return Order(std::move(selector));\n}\n\ntemplate <class Selector = Identity, class GroupBy = detail::GroupBy<Selector>>\nGroupBy groupBy(Selector selector = Selector()) {\n  return GroupBy(std::move(selector));\n}\n\ntemplate <\n    class Selector = Identity,\n    class GroupByAdjacent = detail::GroupByAdjacent<Selector>>\nGroupByAdjacent groupByAdjacent(Selector selector = Selector()) {\n  return GroupByAdjacent(std::move(selector));\n}\n\ntemplate <\n    class Selector = Identity,\n    class Distinct = detail::Distinct<Selector>>\nDistinct distinctBy(Selector selector = Selector()) {\n  return Distinct(std::move(selector));\n}\n\ntemplate <int n, class Get = detail::Map<Get<n>>>\nGet get() {\n  return Get();\n}\n\n// construct Dest from each value\ntemplate <class Dest, class Cast = detail::Map<Cast<Dest>>>\nCast eachAs() {\n  return Cast();\n}\n\n// call folly::to on each value\ntemplate <class Dest, class EachTo = detail::Map<To<Dest>>>\nEachTo eachTo() {\n  return EachTo();\n}\n\n// call folly::tryTo on each value\ntemplate <class Dest, class EachTryTo = detail::Map<TryTo<Dest>>>\nEachTryTo eachTryTo() {\n  return EachTryTo();\n}\n\ntemplate <class Value>\ndetail::TypeAssertion<Value> assert_type() {\n  return {};\n}\n\n/*\n * Sink Factories\n */\n\n/**\n * any() - For determining if any value in a sequence satisfies a predicate.\n *\n * The following is an example for checking if any computer is broken:\n *\n *   bool schrepIsMad = from(computers) | any(isBroken);\n *\n * (because everyone knows Schrep hates broken computers).\n *\n * Note that if no predicate is provided, 'any()' checks if any of the values\n * are true when cased to bool. To check if any of the scores are nonZero:\n *\n *   bool somebodyScored = from(scores) | any();\n *\n * Note: Passing an empty sequence through 'any()' will always return false. In\n * fact, 'any()' is equivilent to the composition of 'filter()' and 'notEmpty'.\n *\n *   from(source) | any(pred) == from(source) | filter(pred) | notEmpty\n */\n\ntemplate <\n    class Predicate = Identity,\n    class Filter = detail::Filter<Predicate>,\n    class NotEmpty = detail::IsEmpty<false>,\n    class Composed = detail::Composed<Filter, NotEmpty>>\nComposed any(Predicate pred = Predicate()) {\n  return Composed(Filter(std::move(pred)), NotEmpty());\n}\n\n/**\n * all() - For determining whether all values in a sequence satisfy a predicate.\n *\n * The following is an example for checking if all members of a team are cool:\n *\n *   bool isAwesomeTeam = from(team) | all(isCool);\n *\n * Note that if no predicate is provided, 'all()'' checks if all of the values\n * are true when cased to bool.\n * The following makes sure none of 'pointers' are nullptr:\n *\n *   bool allNonNull = from(pointers) | all();\n *\n * Note: Passing an empty sequence through 'all()' will always return true. In\n * fact, 'all()' is equivilent to the composition of 'filter()' with the\n * reversed predicate and 'isEmpty'.\n *\n *   from(source) | all(pred) == from(source) | filter(negate(pred)) | isEmpty\n */\ntemplate <\n    class Predicate = Identity,\n    class Filter = detail::Filter<Negate<Predicate>>,\n    class IsEmpty = detail::IsEmpty<true>,\n    class Composed = detail::Composed<Filter, IsEmpty>>\nComposed all(Predicate pred = Predicate()) {\n  return Composed(Filter(negate(pred)), IsEmpty());\n}\n\ntemplate <class Seed, class Fold, class FoldLeft = detail::FoldLeft<Seed, Fold>>\nFoldLeft foldl(Seed seed = Seed(), Fold fold = Fold()) {\n  return FoldLeft(std::move(seed), std::move(fold));\n}\n\ntemplate <class Reducer, class Reduce = detail::Reduce<Reducer>>\nReduce reduce(Reducer reducer = Reducer()) {\n  return Reduce(std::move(reducer));\n}\n\ntemplate <class Selector = Identity, class Min = detail::Min<Selector, Less>>\nMin minBy(Selector selector = Selector()) {\n  return Min(std::move(selector));\n}\n\ntemplate <class Selector, class MaxBy = detail::Min<Selector, Greater>>\nMaxBy maxBy(Selector selector = Selector()) {\n  return MaxBy(std::move(selector));\n}\n\ntemplate <class Collection, class Collect = detail::Collect<Collection>>\nCollect as() {\n  return Collect();\n}\n\ntemplate <\n    template <class, class> class Container = std::vector,\n    template <class> class Allocator = std::allocator,\n    class Collect = detail::CollectTemplate<Container, Allocator>>\nCollect as() {\n  return Collect();\n}\n\ntemplate <class Collection, class Append = detail::Append<Collection>>\nAppend appendTo(Collection& collection) {\n  return Append(&collection);\n}\n\ntemplate <\n    class Needle,\n    class Contains = detail::Contains<typename std::decay<Needle>::type>>\nContains contains(Needle&& needle) {\n  return Contains(std::forward<Needle>(needle));\n}\n\ntemplate <\n    class Exception,\n    class ErrorHandler,\n    class GuardImpl =\n        detail::GuardImpl<Exception, typename std::decay<ErrorHandler>::type>>\nGuardImpl guard(ErrorHandler&& handler) {\n  return GuardImpl(std::forward<ErrorHandler>(handler));\n}\n\ntemplate <\n    class Fallback,\n    class UnwrapOr = detail::UnwrapOr<typename std::decay<Fallback>::type>>\nUnwrapOr unwrapOr(Fallback&& fallback) {\n  return UnwrapOr(std::forward<Fallback>(fallback));\n}\n\n} // namespace gen\n} // namespace folly\n\n#include <folly/gen/Base-inl.h>\n"
  },
  {
    "path": "folly/gen/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME base\n  HEADERS\n    Base-inl.h\n    Base.h\n  EXPORTED_DEPS\n    folly_container_access\n    folly_container_f14_hash\n    folly_conv\n    folly_function\n    folly_functional_invoke\n    folly_gen_core\n    folly_optional\n    folly_portability\n    folly_range\n    folly_utility\n)\n\nfolly_add_library(\n  NAME combine\n  HEADERS\n    Combine-inl.h\n    Combine.h\n  EXPORTED_DEPS\n    folly_gen_base\n)\n\nfolly_add_library(\n  NAME core\n  HEADERS\n    Core-inl.h\n    Core.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME file\n  HEADERS\n    File-inl.h\n    File.h\n  EXPORTED_DEPS\n    folly_exception\n    folly_file\n    folly_gen_base\n    folly_gen_string\n    folly_io_iobuf\n)\n\nfolly_add_library(\n  NAME istream\n  HEADERS\n    IStream.h\n  EXPORTED_DEPS\n    folly_gen_core\n)\n\nfolly_add_library(\n  NAME parallel\n  HEADERS\n    Parallel-inl.h\n    Parallel.h\n  EXPORTED_DEPS\n    folly_gen_base\n    folly_mpmc_queue\n    folly_scope_guard\n    folly_synchronization_event_count\n)\n\nfolly_add_library(\n  NAME parallel_map\n  HEADERS\n    ParallelMap-inl.h\n    ParallelMap.h\n  EXPORTED_DEPS\n    folly_expected\n    folly_functional_invoke\n    folly_gen_core\n    folly_mpmc_pipeline\n    folly_synchronization_event_count\n    folly_system_hardware_concurrency\n)\n\nfolly_add_library(\n  NAME string\n  HEADERS\n    String-inl.h\n    String.h\n  EXPORTED_DEPS\n    folly_conv\n    folly_gen_base\n    folly_io_iobuf\n    folly_portability\n    folly_range\n    folly_string\n)\n"
  },
  {
    "path": "folly/gen/Combine-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_GEN_COMBINE_H_\n#error This file may only be included from folly/gen/Combine.h\n#endif\n\n#include <iterator>\n#include <system_error>\n#include <tuple>\n#include <type_traits>\n\nnamespace folly {\nnamespace gen {\nnamespace detail {\n\n/**\n * Interleave\n *\n * Alternate values from a sequence with values from a sequence container.\n * Stops once we run out of values from either source.\n */\ntemplate <class Container>\nclass Interleave : public Operator<Interleave<Container>> {\n  // see comment about copies in CopiedSource\n  const std::shared_ptr<Container> container_;\n\n public:\n  explicit Interleave(Container container)\n      : container_(new Container(std::move(container))) {}\n\n  template <class Value, class Source>\n  class Generator : public GenImpl<Value, Generator<Value, Source>> {\n    Source source_;\n    const std::shared_ptr<Container> container_;\n\n   public:\n    explicit Generator(\n        Source source, const std::shared_ptr<Container> container)\n        : source_(std::move(source)), container_(container) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      auto iter = container_->begin();\n      return source_.apply([&](Value value) -> bool {\n        if (iter == container_->end()) {\n          return false;\n        }\n        if (!handler(std::forward<Value>(value))) {\n          return false;\n        }\n        if (!handler(std::move(*iter))) {\n          return false;\n        }\n        iter++;\n        return true;\n      });\n    }\n  };\n\n  template <class Value2, class Source, class Gen = Generator<Value2, Source>>\n  Gen compose(GenImpl<Value2, Source>&& source) const {\n    return Gen(std::move(source.self()), container_);\n  }\n\n  template <class Value2, class Source, class Gen = Generator<Value2, Source>>\n  Gen compose(const GenImpl<Value2, Source>& source) const {\n    return Gen(source.self(), container_);\n  }\n};\n\n/**\n * Zip\n *\n * Combine inputs from Source with values from a sequence container by merging\n * them into a tuple.\n *\n */\ntemplate <class Container>\nclass Zip : public Operator<Zip<Container>> {\n  // see comment about copies in CopiedSource\n  const std::shared_ptr<Container> container_;\n\n public:\n  explicit Zip(Container container)\n      : container_(new Container(std::move(container))) {}\n\n  template <\n      class Value,\n      class Source,\n      class Result = std::tuple<\n          typename std::decay<Value>::type,\n          typename std::decay<typename Container::value_type>::type>>\n  class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {\n    Source source_;\n    const std::shared_ptr<Container> container_;\n\n   public:\n    explicit Generator(\n        Source source, const std::shared_ptr<Container> container)\n        : source_(std::move(source)), container_(container) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      auto iter = container_->begin();\n      return source_.apply([&](Value value) -> bool {\n        if (iter == container_->end()) {\n          return false;\n        }\n        if (!handler(\n                std::make_tuple(\n                    std::forward<Value>(value), std::move(*iter)))) {\n          return false;\n        }\n        ++iter;\n        return true;\n      });\n    }\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), container_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), container_);\n  }\n};\n\ntemplate <class... Types1, class... Types2>\nauto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2)\n    -> std::tuple<Types1..., Types2...> {\n  return std::tuple_cat(std::move(t1), std::move(t2));\n}\n\ntemplate <class... Types1, class Type2>\nauto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2)\n    -> decltype(std::tuple_cat(\n        std::move(t1), std::make_tuple(std::forward<Type2>(t2)))) {\n  return std::tuple_cat(\n      std::move(t1), std::make_tuple(std::forward<Type2>(t2)));\n}\n\ntemplate <class Type1, class... Types2>\nauto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2)\n    -> decltype(std::tuple_cat(\n        std::make_tuple(std::forward<Type1>(t1)), std::move(t2))) {\n  return std::tuple_cat(\n      std::make_tuple(std::forward<Type1>(t1)), std::move(t2));\n}\n\ntemplate <class Type1, class Type2>\nauto add_to_tuple(Type1&& t1, Type2&& t2) -> decltype(std::make_tuple(\n    std::forward<Type1>(t1), std::forward<Type2>(t2))) {\n  return std::make_tuple(std::forward<Type1>(t1), std::forward<Type2>(t2));\n}\n\n// Merges a 2-tuple into a single tuple (get<0> could already be a tuple)\nclass MergeTuples {\n public:\n  template <class Tuple>\n  auto operator()(Tuple&& value) const -> decltype(add_to_tuple(\n      std::get<0>(std::forward<Tuple>(value)),\n      std::get<1>(std::forward<Tuple>(value)))) {\n    static_assert(\n        std::tuple_size<typename std::remove_reference<Tuple>::type>::value ==\n            2,\n        \"Can only merge tuples of size 2\");\n    return add_to_tuple(\n        std::get<0>(std::forward<Tuple>(value)),\n        std::get<1>(std::forward<Tuple>(value)));\n  }\n};\n\n} // namespace detail\n\n// TODO(mcurtiss): support zip() for N>1 operands.\ntemplate <\n    class Source,\n    class Zip = detail::Zip<typename std::decay<Source>::type>>\nZip zip(Source&& source) {\n  return Zip(std::forward<Source>(source));\n}\n\n} // namespace gen\n} // namespace folly\n"
  },
  {
    "path": "folly/gen/Combine.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_GEN_COMBINE_H_\n\n#include <folly/gen/Base.h>\n\nnamespace folly {\nnamespace gen {\nnamespace detail {\n\ntemplate <class Container>\nclass Interleave;\n\ntemplate <class Container>\nclass Zip;\n\n} // namespace detail\n\ntemplate <\n    class Source2,\n    class Source2Decayed = typename std::decay<Source2>::type,\n    class Interleave = detail::Interleave<Source2Decayed>>\nInterleave interleave(Source2&& source2) {\n  return Interleave(std::forward<Source2>(source2));\n}\n\n} // namespace gen\n} // namespace folly\n\n#include <folly/gen/Combine-inl.h>\n"
  },
  {
    "path": "folly/gen/Core-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_GEN_CORE_H_\n#error This file may only be included from folly/gen/Core.h\n#endif\n\n#include <type_traits>\n#include <utility>\n\n#include <folly/Portability.h>\n\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_PUSH_WARNING\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n\nnamespace folly {\nnamespace gen {\n\n/**\n * IsCompatibleSignature - Trait type for testing whether a given Functor\n * matches an expected signature.\n *\n * Usage:\n *   IsCompatibleSignature<FunctorType, bool(int, float)>::value\n */\ntemplate <class Candidate, class Expected>\nclass IsCompatibleSignature {\n  static constexpr bool value = false;\n};\n\ntemplate <class Candidate, class ExpectedReturn, class... ArgTypes>\nclass IsCompatibleSignature<Candidate, ExpectedReturn(ArgTypes...)> {\n  template <\n      class F,\n      class ActualReturn =\n          decltype(std::declval<F>()(std::declval<ArgTypes>()...)),\n      bool good = std::is_same<ExpectedReturn, ActualReturn>::value>\n  static constexpr bool testArgs(int*) {\n    return good;\n  }\n\n  template <class F>\n  static constexpr bool testArgs(...) {\n    return false;\n  }\n\n public:\n  static constexpr bool value = testArgs<Candidate>(nullptr);\n};\n\n/**\n * FBounded - Helper type for the curiously recurring template pattern, used\n * heavily here to enable inlining and obviate virtual functions\n */\ntemplate <class Self>\nstruct FBounded {\n  using SelfType = Self;\n  const Self& self() const { return *static_cast<const Self*>(this); }\n\n  Self& self() { return *static_cast<Self*>(this); }\n};\n\n/**\n * Operator - Core abstraction of an operation which may be applied to a\n * generator. All operators implement a method compose(), which takes a\n * generator and produces an output generator.\n */\ntemplate <class Self>\nclass Operator : public FBounded<Self> {\n public:\n  /**\n   * compose() - Must be implemented by child class to compose a new Generator\n   * out of a given generator. This function left intentionally unimplemented.\n   */\n  template <class Source, class Value, class ResultGen = void>\n  ResultGen compose(const GenImpl<Value, Source>& source) const;\n\n protected:\n  Operator() = default;\n  Operator(Operator&&) noexcept = default;\n  Operator(const Operator&) = default;\n  Operator& operator=(Operator&&) noexcept = default;\n  Operator& operator=(const Operator&) = default;\n};\n\n/**\n * operator|() - For composing two operators without binding it to a\n * particular generator.\n */\ntemplate <\n    class Left,\n    class Right,\n    class Composed = detail::Composed<Left, Right>>\nComposed operator|(const Operator<Left>& left, const Operator<Right>& right) {\n  return Composed(left.self(), right.self());\n}\n\ntemplate <\n    class Left,\n    class Right,\n    class Composed = detail::Composed<Left, Right>>\nComposed operator|(const Operator<Left>& left, Operator<Right>&& right) {\n  return Composed(left.self(), std::move(right.self()));\n}\n\ntemplate <\n    class Left,\n    class Right,\n    class Composed = detail::Composed<Left, Right>>\nComposed operator|(Operator<Left>&& left, const Operator<Right>& right) {\n  return Composed(std::move(left.self()), right.self());\n}\n\ntemplate <\n    class Left,\n    class Right,\n    class Composed = detail::Composed<Left, Right>>\nComposed operator|(Operator<Left>&& left, Operator<Right>&& right) {\n  return Composed(std::move(left.self()), std::move(right.self()));\n}\n\n/**\n * GenImpl - Core abstraction of a generator, an object which produces values by\n * passing them to a given handler lambda. All generator implementations must\n * implement apply(). foreach() may also be implemented to special case the\n * condition where the entire sequence is consumed.\n */\ntemplate <class Value, class Self>\nclass GenImpl : public FBounded<Self> {\n protected:\n  // To prevent slicing\n  GenImpl() = default;\n  GenImpl(GenImpl&&) = default;\n  GenImpl(const GenImpl&) = default;\n  GenImpl& operator=(GenImpl&&) = default;\n  GenImpl& operator=(const GenImpl&) = default;\n\n public:\n  typedef Value ValueType;\n  typedef typename std::decay<Value>::type StorageType;\n\n  /**\n   * apply() - Send all values produced by this generator to given handler until\n   * the handler returns false. Returns false if and only if the handler passed\n   * in returns false. Note: It should return true even if it completes (without\n   * the handler returning false), as 'Chain' uses the return value of apply to\n   * determine if it should process the second object in its chain.\n   */\n  template <class Handler>\n  bool apply(Handler&& handler) const;\n\n  /**\n   * foreach() - Send all values produced by this generator to given lambda.\n   */\n  template <class Body>\n  void foreach(Body&& body) const {\n    this->self().apply([&](Value value) -> bool {\n      static_assert(!infinite, \"Cannot call foreach on infinite GenImpl\");\n      body(std::forward<Value>(value));\n      return true;\n    });\n  }\n\n  // Child classes should override if the sequence generated is *definitely*\n  // infinite. 'infinite' may be false_type for some infinite sequences\n  // (due to the Halting Problem).\n  //\n  // In general, almost all sources are finite (only seq(n) produces an infinite\n  // source), almost all operators keep the finiteness of the source (only cycle\n  // makes an infinite generator from a finite one, only until and take make a\n  // finite generator from an infinite one, and concat needs both the inner and\n  // outer generators to be finite to make a finite one), and most sinks\n  // cannot accept and infinite generators (first being the expection).\n  static constexpr bool infinite = false;\n};\n\ntemplate <\n    class LeftValue,\n    class Left,\n    class RightValue,\n    class Right,\n    class Chain = detail::Chain<LeftValue, Left, Right>>\nChain operator+(\n    const GenImpl<LeftValue, Left>& left,\n    const GenImpl<RightValue, Right>& right) {\n  static_assert(\n      std::is_same<LeftValue, RightValue>::value,\n      \"Generators may ony be combined if Values are the exact same type.\");\n  return Chain(left.self(), right.self());\n}\n\ntemplate <\n    class LeftValue,\n    class Left,\n    class RightValue,\n    class Right,\n    class Chain = detail::Chain<LeftValue, Left, Right>>\nChain operator+(\n    const GenImpl<LeftValue, Left>& left, GenImpl<RightValue, Right>&& right) {\n  static_assert(\n      std::is_same<LeftValue, RightValue>::value,\n      \"Generators may ony be combined if Values are the exact same type.\");\n  return Chain(left.self(), std::move(right.self()));\n}\n\ntemplate <\n    class LeftValue,\n    class Left,\n    class RightValue,\n    class Right,\n    class Chain = detail::Chain<LeftValue, Left, Right>>\nChain operator+(\n    GenImpl<LeftValue, Left>&& left, const GenImpl<RightValue, Right>& right) {\n  static_assert(\n      std::is_same<LeftValue, RightValue>::value,\n      \"Generators may ony be combined if Values are the exact same type.\");\n  return Chain(std::move(left.self()), right.self());\n}\n\ntemplate <\n    class LeftValue,\n    class Left,\n    class RightValue,\n    class Right,\n    class Chain = detail::Chain<LeftValue, Left, Right>>\nChain operator+(\n    GenImpl<LeftValue, Left>&& left, GenImpl<RightValue, Right>&& right) {\n  static_assert(\n      std::is_same<LeftValue, RightValue>::value,\n      \"Generators may ony be combined if Values are the exact same type.\");\n  return Chain(std::move(left.self()), std::move(right.self()));\n}\n\n/**\n * operator|() which enables foreach-like usage:\n *   gen | [](Value v) -> void {...};\n */\ntemplate <class Value, class Gen, class Handler>\ntypename std::enable_if<\n    IsCompatibleSignature<Handler, void(Value)>::value>::type\noperator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {\n  static_assert(\n      !Gen::infinite, \"Cannot pull all values from an infinite sequence.\");\n  gen.self().foreach(std::forward<Handler>(handler));\n}\n\n/**\n * operator|() which enables foreach-like usage with 'break' support:\n *   gen | [](Value v) -> bool { return shouldContinue(); };\n */\ntemplate <class Value, class Gen, class Handler>\ntypename std::\n    enable_if<IsCompatibleSignature<Handler, bool(Value)>::value, bool>::type\n    operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {\n  return gen.self().apply(std::forward<Handler>(handler));\n}\n\n/**\n * operator|() for composing generators with operators, similar to boosts' range\n * adaptors:\n *   gen | map(square) | sum\n */\ntemplate <class Value, class Gen, class Op>\nauto operator|(const GenImpl<Value, Gen>& gen, const Operator<Op>& op)\n    -> decltype(op.self().compose(gen.self())) {\n  return op.self().compose(gen.self());\n}\n\ntemplate <class Value, class Gen, class Op>\nauto operator|(GenImpl<Value, Gen>&& gen, const Operator<Op>& op)\n    -> decltype(op.self().compose(std::move(gen.self()))) {\n  return op.self().compose(std::move(gen.self()));\n}\n\nnamespace detail {\n\n/**\n * Composed - For building up a pipeline of operations to perform, absent any\n * particular source generator. Useful for building up custom pipelines.\n *\n * This type is usually used by just piping two operators together:\n *\n * auto valuesOf = filter([](Optional<int>& o) { return o.hasValue(); })\n *               | map([](Optional<int>& o) -> int& { return o.value(); });\n *\n *  auto valuesIncluded = from(optionals) | valuesOf | as<vector>();\n */\ntemplate <class First, class Second>\nclass Composed : public Operator<Composed<First, Second>> {\n  First first_;\n  Second second_;\n\n public:\n  Composed() = default;\n\n  Composed(First first, Second second)\n      : first_(std::move(first)), second_(std::move(second)) {}\n\n  template <\n      class Source,\n      class Value,\n      class FirstRet =\n          decltype(std::declval<First>().compose(std::declval<Source>())),\n      class SecondRet =\n          decltype(std::declval<Second>().compose(std::declval<FirstRet>()))>\n  SecondRet compose(const GenImpl<Value, Source>& source) const {\n    return second_.compose(first_.compose(source.self()));\n  }\n\n  template <\n      class Source,\n      class Value,\n      class FirstRet =\n          decltype(std::declval<First>().compose(std::declval<Source>())),\n      class SecondRet =\n          decltype(std::declval<Second>().compose(std::declval<FirstRet>()))>\n  SecondRet compose(GenImpl<Value, Source>&& source) const {\n    return second_.compose(first_.compose(std::move(source.self())));\n  }\n};\n\n/**\n * Chain - For concatenating the values produced by two Generators.\n *\n * This type is primarily used through using '+' to combine generators, like:\n *\n *   auto nums = seq(1, 10) + seq(20, 30);\n *   int total = nums | sum;\n */\ntemplate <class Value, class First, class Second>\nclass Chain : public GenImpl<Value, Chain<Value, First, Second>> {\n  First first_;\n  Second second_;\n\n public:\n  explicit Chain(First first, Second second)\n      : first_(std::move(first)), second_(std::move(second)) {}\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    return first_.apply(std::forward<Handler>(handler)) &&\n        second_.apply(std::forward<Handler>(handler));\n  }\n\n  template <class Body>\n  void foreach(Body&& body) const {\n    first_.foreach(std::forward<Body>(body));\n    second_.foreach(std::forward<Body>(body));\n  }\n\n  static constexpr bool infinite = First::infinite || Second::infinite;\n};\n\n} // namespace detail\n} // namespace gen\n} // namespace folly\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/gen/Core.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_GEN_CORE_H_\n\nnamespace folly {\nnamespace gen {\n\ntemplate <class Value, class Self>\nclass GenImpl;\n\ntemplate <class Self>\nclass Operator;\n\nnamespace detail {\n\ntemplate <class Self>\nstruct FBounded;\n\ntemplate <class First, class Second>\nclass Composed;\n\ntemplate <class Value, class First, class Second>\nclass Chain;\n\n} // namespace detail\n} // namespace gen\n} // namespace folly\n\n#include <folly/gen/Core-inl.h>\n"
  },
  {
    "path": "folly/gen/File-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_GEN_FILE_H_\n#error This file may only be included from folly/gen/File.h\n#endif\n\n#include <system_error>\n\n#include <folly/Exception.h>\n#include <folly/gen/String.h>\n\nnamespace folly {\nnamespace gen {\nnamespace detail {\n\nclass FileReader : public GenImpl<ByteRange, FileReader> {\n public:\n  FileReader(File file, std::unique_ptr<IOBuf> buffer)\n      : file_(std::move(file)), buffer_(std::move(buffer)) {\n    buffer_->clear();\n  }\n\n  template <class Body>\n  bool apply(Body&& body) const {\n    for (;;) {\n      ssize_t n;\n      do {\n        n = folly::fileops::read(\n            file_.fd(), buffer_->writableTail(), buffer_->capacity());\n      } while (n == -1 && errno == EINTR);\n      if (n == -1) {\n        throw std::system_error(\n            errno, errorCategoryForErrnoDomain(), \"read failed\");\n      }\n      if (n == 0) {\n        return true;\n      }\n      if (!body(ByteRange(buffer_->tail(), size_t(n)))) {\n        return false;\n      }\n    }\n  }\n\n  // Technically, there could be infinite files (e.g. /dev/random), but people\n  // who open those can do so at their own risk.\n  static constexpr bool infinite = false;\n\n private:\n  File file_;\n  std::unique_ptr<IOBuf> buffer_;\n};\n\nclass FileWriter : public Operator<FileWriter> {\n public:\n  FileWriter(File file, std::unique_ptr<IOBuf> buffer)\n      : file_(std::move(file)), buffer_(std::move(buffer)) {\n    if (buffer_) {\n      buffer_->clear();\n    }\n  }\n\n  template <class Source, class Value>\n  void compose(const GenImpl<Value, Source>& source) const {\n    auto fn = [&](ByteRange v) {\n      if (!this->buffer_ || v.size() >= this->buffer_->capacity()) {\n        this->flushBuffer();\n        this->write(v);\n      } else {\n        if (v.size() > this->buffer_->tailroom()) {\n          this->flushBuffer();\n        }\n        memcpy(this->buffer_->writableTail(), v.data(), v.size());\n        this->buffer_->append(v.size());\n      }\n    };\n\n    // Iterate\n    source.foreach(std::move(fn));\n\n    flushBuffer();\n    file_.close();\n  }\n\n private:\n  void write(ByteRange v) const {\n    ssize_t n;\n    while (!v.empty()) {\n      do {\n        n = fileops::write(file_.fd(), v.data(), v.size());\n      } while (n == -1 && errno == EINTR);\n      if (n == -1) {\n        throw std::system_error(\n            errno, errorCategoryForErrnoDomain(), \"write() failed\");\n      }\n      v.advance(size_t(n));\n    }\n  }\n\n  void flushBuffer() const {\n    if (buffer_ && buffer_->length() != 0) {\n      write(ByteRange(buffer_->data(), buffer_->length()));\n      buffer_->clear();\n    }\n  }\n\n  mutable File file_;\n  std::unique_ptr<IOBuf> buffer_;\n};\n\ninline auto byLineImpl(File file, char delim, bool keepDelimiter) {\n  // clang-format off\n  return fromFile(std::move(file))\n      | eachAs<StringPiece>()\n      | resplit(delim, keepDelimiter);\n  // clang-format on\n}\n\n} // namespace detail\n\n/**\n * Generator which reads lines from a file.\n * Note: This produces StringPieces which reference temporary strings which are\n * only valid during iteration.\n */\ninline auto byLineFull(File file, char delim = '\\n') {\n  return detail::byLineImpl(std::move(file), delim, true);\n}\n\ninline auto byLineFull(int fd, char delim = '\\n') {\n  return byLineFull(File(fd), delim);\n}\n\ninline auto byLineFull(const char* f, char delim = '\\n') {\n  return byLineFull(File(f), delim);\n}\n\ninline auto byLine(File file, char delim = '\\n') {\n  return detail::byLineImpl(std::move(file), delim, false);\n}\n\ninline auto byLine(int fd, char delim = '\\n') {\n  return byLine(File(fd), delim);\n}\n\ninline auto byLine(const char* f, char delim = '\\n') {\n  return byLine(File(f), delim);\n}\n\n} // namespace gen\n} // namespace folly\n"
  },
  {
    "path": "folly/gen/File.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_GEN_FILE_H_\n\n#include <folly/File.h>\n#include <folly/gen/Base.h>\n#include <folly/io/IOBuf.h>\n\nnamespace folly {\nnamespace gen {\n\nnamespace detail {\nclass FileReader;\nclass FileWriter;\n} // namespace detail\n\n/**\n * Generator that reads from a file with a buffer of the given size.\n * Reads must be buffered (the generator interface expects the generator\n * to hold each value).\n */\ntemplate <class S = detail::FileReader>\nS fromFile(File file, size_t bufferSize = 4096) {\n  return S(std::move(file), IOBuf::create(bufferSize));\n}\n\n/**\n * Generator that reads from a file using a given buffer.\n */\ntemplate <class S = detail::FileReader>\nS fromFile(File file, std::unique_ptr<IOBuf> buffer) {\n  return S(std::move(file), std::move(buffer));\n}\n\n/**\n * Sink that writes to a file with a buffer of the given size.\n * If bufferSize is 0, writes will be unbuffered.\n */\ntemplate <class S = detail::FileWriter>\nS toFile(File file, size_t bufferSize = 4096) {\n  return S(std::move(file), bufferSize ? nullptr : IOBuf::create(bufferSize));\n}\n\n/**\n * Sink that writes to a file using a given buffer.\n * If the buffer is nullptr, writes will be unbuffered.\n */\ntemplate <class S = detail::FileWriter>\nS toFile(File file, std::unique_ptr<IOBuf> buffer) {\n  return S(std::move(file), std::move(buffer));\n}\n} // namespace gen\n} // namespace folly\n\n#include <folly/gen/File-inl.h>\n"
  },
  {
    "path": "folly/gen/IStream.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <istream>\n#include <string>\n\n#include <folly/gen/Core.h>\n\nnamespace folly {\nnamespace gen {\nnamespace detail {\n\n/**\n * Generates lines by calling std::getline() on a given istream.\n */\nclass IStreamByLine : public GenImpl<std::string&&, IStreamByLine> {\n public:\n  IStreamByLine(std::istream& in) : in_(in) {}\n\n  template <class Body>\n  bool apply(Body&& body) const {\n    for (std::string line; std::getline(in_, line);) {\n      if (!body(std::move(line))) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // Technically, there could be infinite files (e.g. /dev/random), but people\n  // who open those can do so at their own risk.\n  static constexpr bool infinite = false;\n\n private:\n  std::istream& in_;\n};\n\n} // namespace detail\n\ninline detail::IStreamByLine byLine(std::istream& in) {\n  return detail::IStreamByLine(in);\n}\n\n} // namespace gen\n} // namespace folly\n"
  },
  {
    "path": "folly/gen/Parallel-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_GEN_PARALLEL_H_\n#error This file may only be included from folly/gen/Parallel.h\n#endif\n\n#include <atomic>\n#include <thread>\n#include <vector>\n\n#include <folly/MPMCQueue.h>\n#include <folly/ScopeGuard.h>\n#include <folly/synchronization/EventCount.h>\n\nnamespace folly {\nnamespace gen {\nnamespace detail {\n\ntemplate <typename T>\nclass ClosableMPMCQueue {\n  MPMCQueue<T> queue_;\n  std::atomic<size_t> producers_{0};\n  std::atomic<size_t> consumers_{0};\n  folly::EventCount wakeProducer_;\n  folly::EventCount wakeConsumer_;\n\n public:\n  explicit ClosableMPMCQueue(size_t capacity) : queue_(capacity) {}\n\n  ~ClosableMPMCQueue() {\n    CHECK(!producers());\n    CHECK(!consumers());\n  }\n\n  void openProducer() { ++producers_; }\n  void openConsumer() { ++consumers_; }\n\n  void closeInputProducer() {\n    size_t producers = producers_--;\n    CHECK(producers);\n    if (producers == 1) { // last producer\n      wakeConsumer_.notifyAll();\n    }\n  }\n\n  void closeOutputConsumer() {\n    size_t consumers = consumers_--;\n    CHECK(consumers);\n    if (consumers == 1) { // last consumer\n      wakeProducer_.notifyAll();\n    }\n  }\n\n  size_t producers() const {\n    return producers_.load(std::memory_order_acquire);\n  }\n\n  size_t consumers() const {\n    return consumers_.load(std::memory_order_acquire);\n  }\n\n  template <typename... Args>\n  bool writeUnlessFull(Args&&... args) noexcept {\n    if (queue_.write(std::forward<Args>(args)...)) {\n      // wake consumers to pick up new value\n      wakeConsumer_.notify();\n      return true;\n    }\n    return false;\n  }\n\n  template <typename... Args>\n  bool writeUnlessClosed(Args&&... args) {\n    // write if there's room\n    if (!queue_.writeIfNotFull(std::forward<Args>(args)...)) {\n      while (true) {\n        auto key = wakeProducer_.prepareWait();\n        // if write fails, check if there are still consumers listening\n        if (!consumers()) {\n          // no consumers left; bail out\n          wakeProducer_.cancelWait();\n          return false;\n        }\n        if (queue_.writeIfNotFull(std::forward<Args>(args)...)) {\n          wakeProducer_.cancelWait();\n          break;\n        }\n        wakeProducer_.wait(key);\n      }\n    }\n    // wake consumers to pick up new value\n    wakeConsumer_.notify();\n    return true;\n  }\n\n  bool readUnlessEmpty(T& out) {\n    if (queue_.read(out)) {\n      // wake producers to fill empty space\n      wakeProducer_.notify();\n      return true;\n    }\n    return false;\n  }\n\n  bool readUnlessClosed(T& out) {\n    if (!queue_.readIfNotEmpty(out)) {\n      while (true) {\n        auto key = wakeConsumer_.prepareWait();\n        if (queue_.readIfNotEmpty(out)) {\n          wakeConsumer_.cancelWait();\n          break;\n        }\n        if (!producers()) {\n          wakeConsumer_.cancelWait();\n          // wake producers to fill empty space\n          wakeProducer_.notify();\n          return false;\n        }\n        wakeConsumer_.wait(key);\n      }\n    }\n    // wake writers blocked by full queue\n    wakeProducer_.notify();\n    return true;\n  }\n};\n\ntemplate <class Sink>\nclass Sub : public Operator<Sub<Sink>> {\n  Sink sink_;\n\n public:\n  explicit Sub(Sink sink) : sink_(sink) {}\n\n  template <\n      class Value,\n      class Source,\n      class Result =\n          decltype(std::declval<Sink>().compose(std::declval<Source>())),\n      class Just = SingleCopy<typename std::decay<Result>::type>>\n  Just compose(const GenImpl<Value, Source>& source) const {\n    return Just(source | sink_);\n  }\n};\n\ntemplate <class Ops>\nclass Parallel : public Operator<Parallel<Ops>> {\n  Ops ops_;\n  size_t threads_;\n\n public:\n  Parallel(Ops ops, size_t threads) : ops_(std::move(ops)), threads_(threads) {}\n\n  template <\n      class Input,\n      class Source,\n      class InputDecayed = typename std::decay<Input>::type,\n      class Composed =\n          decltype(std::declval<Ops>().compose(Empty<InputDecayed&&>())),\n      class Output = typename Composed::ValueType,\n      class OutputDecayed = typename std::decay<Output>::type>\n  class Generator\n      : public GenImpl<\n            OutputDecayed&&,\n            Generator<\n                Input,\n                Source,\n                InputDecayed,\n                Composed,\n                Output,\n                OutputDecayed>> {\n    Source source_;\n    Ops ops_;\n    size_t threads_;\n\n    using InQueue = ClosableMPMCQueue<InputDecayed>;\n    using OutQueue = ClosableMPMCQueue<OutputDecayed>;\n\n    class Puller : public GenImpl<InputDecayed&&, Puller> {\n      InQueue* queue_;\n\n     public:\n      explicit Puller(InQueue* queue) : queue_(queue) {}\n\n      template <class Handler>\n      bool apply(Handler&& handler) const {\n        InputDecayed input;\n        while (queue_->readUnlessClosed(input)) {\n          if (!handler(std::move(input))) {\n            return false;\n          }\n        }\n        return true;\n      }\n\n      template <class Body>\n      void foreach(Body&& body) const {\n        InputDecayed input;\n        while (queue_->readUnlessClosed(input)) {\n          body(std::move(input));\n        }\n      }\n    };\n\n    template <bool all = false>\n    class Pusher : public Operator<Pusher<all>> {\n      OutQueue* queue_;\n\n     public:\n      explicit Pusher(OutQueue* queue) : queue_(queue) {}\n\n      template <class Value, class InnerSource>\n      void compose(const GenImpl<Value, InnerSource>& source) const {\n        if (all) {\n          source.self().foreach([&](Value value) {\n            queue_->writeUnlessClosed(std::forward<Value>(value));\n          });\n        } else {\n          source.self().apply([&](Value value) {\n            return queue_->writeUnlessClosed(std::forward<Value>(value));\n          });\n        }\n      }\n    };\n\n    template <bool all = false>\n    class Executor {\n      InQueue inQueue_;\n      OutQueue outQueue_;\n      Puller puller_;\n      Pusher<all> pusher_;\n      std::vector<std::thread> workers_;\n      const Ops* ops_;\n\n      void work() { puller_ | *ops_ | pusher_; }\n\n     public:\n      Executor(size_t threads, const Ops* ops)\n          : inQueue_(threads * 4),\n            outQueue_(threads * 4),\n            puller_(&inQueue_),\n            pusher_(&outQueue_),\n            ops_(ops) {\n        inQueue_.openProducer();\n        outQueue_.openConsumer();\n        for (size_t t = 0; t < threads; ++t) {\n          inQueue_.openConsumer();\n          outQueue_.openProducer();\n          workers_.emplace_back([this] {\n            SCOPE_EXIT {\n              inQueue_.closeOutputConsumer();\n              outQueue_.closeInputProducer();\n            };\n            this->work();\n          });\n        }\n      }\n\n      ~Executor() {\n        if (inQueue_.producers()) {\n          inQueue_.closeInputProducer();\n        }\n        if (outQueue_.consumers()) {\n          outQueue_.closeOutputConsumer();\n        }\n        while (!workers_.empty()) {\n          workers_.back().join();\n          workers_.pop_back();\n        }\n        CHECK(!inQueue_.consumers());\n        CHECK(!outQueue_.producers());\n      }\n\n      void closeInputProducer() { inQueue_.closeInputProducer(); }\n\n      void closeOutputConsumer() { outQueue_.closeOutputConsumer(); }\n\n      bool writeUnlessClosed(Input&& input) {\n        return inQueue_.writeUnlessClosed(std::forward<Input>(input));\n      }\n\n      bool writeUnlessFull(Input&& input) {\n        return inQueue_.writeUnlessFull(std::forward<Input>(input));\n      }\n\n      bool readUnlessClosed(OutputDecayed& output) {\n        return outQueue_.readUnlessClosed(output);\n      }\n\n      bool readUnlessEmpty(OutputDecayed& output) {\n        return outQueue_.readUnlessEmpty(output);\n      }\n    };\n\n   public:\n    Generator(Source source, Ops ops, size_t threads)\n        : source_(std::move(source)),\n          ops_(std::move(ops)),\n          threads_(\n              threads\n                  ? threads\n                  : size_t(std::max<long>(1, sysconf(_SC_NPROCESSORS_CONF)))) {}\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      Executor<false> executor(threads_, &ops_);\n      bool more = true;\n      source_.apply([&](Input input) {\n        if (executor.writeUnlessFull(std::forward<Input>(input))) {\n          return true;\n        }\n        OutputDecayed output;\n        while (executor.readUnlessEmpty(output)) {\n          if (!handler(std::move(output))) {\n            more = false;\n            return false;\n          }\n        }\n        if (!executor.writeUnlessClosed(std::forward<Input>(input))) {\n          return false;\n        }\n        return true;\n      });\n      executor.closeInputProducer();\n\n      if (more) {\n        OutputDecayed output;\n        while (executor.readUnlessClosed(output)) {\n          if (!handler(std::move(output))) {\n            more = false;\n            break;\n          }\n        }\n      }\n      executor.closeOutputConsumer();\n\n      return more;\n    }\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      Executor<true> executor(threads_, &ops_);\n      source_.foreach([&](Input input) {\n        if (executor.writeUnlessFull(std::forward<Input>(input))) {\n          return;\n        }\n        OutputDecayed output;\n        while (executor.readUnlessEmpty(output)) {\n          body(std::move(output));\n        }\n        CHECK(executor.writeUnlessClosed(std::forward<Input>(input)));\n      });\n      executor.closeInputProducer();\n\n      OutputDecayed output;\n      while (executor.readUnlessClosed(output)) {\n        body(std::move(output));\n      }\n      executor.closeOutputConsumer();\n    }\n  };\n\n  template <class Value, class Source>\n  Generator<Value, Source> compose(const GenImpl<Value, Source>& source) const {\n    return Generator<Value, Source>(source.self(), ops_, threads_);\n  }\n\n  template <class Value, class Source>\n  Generator<Value, Source> compose(GenImpl<Value, Source>&& source) const {\n    return Generator<Value, Source>(std::move(source.self()), ops_, threads_);\n  }\n};\n\n/**\n * ChunkedRangeSource - For slicing up ranges into a sequence of chunks given a\n * maximum chunk size.\n *\n * Usually used through the 'chunked' helper, like:\n *\n *   int n\n *     = chunked(values)\n *     | parallel  // each thread processes a chunk\n *     | concat   // but can still process values one at a time\n *     | filter(isPrime)\n *     | atomic_count;\n */\ntemplate <class Iterator>\nclass ChunkedRangeSource\n    : public GenImpl<RangeSource<Iterator>&&, ChunkedRangeSource<Iterator>> {\n  int chunkSize_;\n  Range<Iterator> range_;\n\n public:\n  ChunkedRangeSource() = default;\n  ChunkedRangeSource(int chunkSize, Range<Iterator> range)\n      : chunkSize_(chunkSize), range_(std::move(range)) {}\n\n  template <class Handler>\n  bool apply(Handler&& handler) const {\n    auto remaining = range_;\n    while (!remaining.empty()) {\n      auto chunk = remaining.subpiece(0, chunkSize_);\n      remaining.advance(chunk.size());\n      auto gen = RangeSource<Iterator>(chunk);\n      if (!handler(std::move(gen))) {\n        return false;\n      }\n    }\n    return true;\n  }\n};\n\n} // namespace detail\n\n} // namespace gen\n} // namespace folly\n"
  },
  {
    "path": "folly/gen/Parallel.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_GEN_PARALLEL_H_\n\n#include <mutex>\n\n#include <folly/gen/Base.h>\n\nnamespace folly {\nnamespace gen {\nnamespace detail {\n\ntemplate <class Ops>\nclass Parallel;\n\ntemplate <class Sink>\nclass Sub;\n\ntemplate <class Iterator>\nclass ChunkedRangeSource;\n\n} // namespace detail\n\n/**\n * chunked() - For producing values from a container in slices.\n *\n * Especially for use with 'parallel()', chunked can be used to process values\n * from a persistent container in chunks larger than one value at a time. The\n * values produced are generators for slices of the input container. */\ntemplate <\n    class Container,\n    class Iterator = typename Container::const_iterator,\n    class Chunked = detail::ChunkedRangeSource<Iterator>>\nChunked chunked(const Container& container, int chunkSize = 256) {\n  return Chunked(chunkSize, folly::range(container.begin(), container.end()));\n}\n\ntemplate <\n    class Container,\n    class Iterator = typename Container::iterator,\n    class Chunked = detail::ChunkedRangeSource<Iterator>>\nChunked chunked(Container& container, int chunkSize = 256) {\n  return Chunked(chunkSize, folly::range(container.begin(), container.end()));\n}\n\n/**\n * parallel - A parallelization operator.\n *\n * 'parallel(ops)' can be used with any generator to process a segment\n * of the pipeline in parallel. Multiple threads are used to apply the\n * operations ('ops') to the input sequence, with the resulting sequence\n * interleaved to be processed on the client thread.\n *\n *   auto scoredResults\n *     = from(ids)\n *     | parallel(map(fetchObj) | filter(isValid) | map(scoreObj))\n *     | as<vector>();\n *\n * Operators specified for parallel execution must yield sequences, not just\n * individual values. If a sink function such as 'count' is desired, it must be\n * wrapped in 'sub' to produce a subcount, since any such aggregation must be\n * re-aggregated.\n *\n *   auto matches\n *     = from(docs)\n *     | parallel(filter(expensiveTest) | sub(count))\n *     | sum;\n *\n * Here, each thread counts its portion of the result, then the sub-counts are\n * summed up to produce the total count.\n */\ntemplate <class Ops, class Parallel = detail::Parallel<Ops>>\nParallel parallel(Ops ops, size_t threads = 0) {\n  return Parallel(std::move(ops), threads);\n}\n\n/**\n * sub - For sub-summarization of a sequence.\n *\n * 'sub' can be used to apply a sink function to a generator, but wrap the\n * single value in another generator. Note that the sink is eagerly evaluated on\n * the input sequence.\n *\n *   auto sum = from(list) | sub(count) | first;\n *\n * This is primarily used with 'parallel', as noted above.\n */\ntemplate <class Sink, class Sub = detail::Sub<Sink>>\nSub sub(Sink sink) {\n  return Sub(std::move(sink));\n}\n} // namespace gen\n} // namespace folly\n\n#include <folly/gen/Parallel-inl.h>\n"
  },
  {
    "path": "folly/gen/ParallelMap-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_GEN_PARALLELMAP_H_\n#error This file may only be included from folly/gen/ParallelMap.h\n#endif\n\n#include <atomic>\n#include <exception>\n#include <thread>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include <folly/Expected.h>\n#include <folly/MPMCPipeline.h>\n#include <folly/functional/Invoke.h>\n#include <folly/synchronization/EventCount.h>\n#include <folly/system/HardwareConcurrency.h>\n\nnamespace folly::gen::detail {\n\n/**\n * PMap - Map in parallel (using threads). For producing a sequence of\n * values by passing each value from a source collection through a\n * predicate while running the predicate in parallel in different\n * threads.\n *\n * This type is usually used through the 'pmap' helper function:\n *\n *   auto squares = seq(1, 10) | pmap(fibonacci, 4) | sum;\n */\ntemplate <class Predicate>\nclass PMap : public Operator<PMap<Predicate>> {\n  Predicate pred_;\n  size_t nThreads_;\n\n public:\n  PMap() = default;\n\n  PMap(Predicate pred, size_t nThreads)\n      : pred_(std::move(pred)), nThreads_(nThreads) {}\n\n  template <\n      class Value,\n      class Source,\n      class Input = typename std::decay<Value>::type,\n      class Output =\n          typename std::decay<invoke_result_t<Predicate, Value>>::type>\n  class Generator\n      : public GenImpl<Output, Generator<Value, Source, Input, Output>> {\n    Source source_;\n    Predicate pred_;\n    const size_t nThreads_;\n\n    using Result = folly::Expected<Output, std::exception_ptr>;\n    class ExecutionPipeline {\n      std::vector<std::thread> workers_;\n      std::atomic<bool> done_{false};\n      const Predicate& pred_;\n      using Pipeline = MPMCPipeline<Input, Result>;\n      Pipeline pipeline_;\n      EventCount wake_;\n\n      // Total active item count as observed by the single thread owning *this.\n      size_t active_ = 0;\n\n     public:\n      ExecutionPipeline(const Predicate& pred, size_t nThreads)\n          : pred_(pred), pipeline_(nThreads, nThreads) {\n        workers_.reserve(nThreads);\n        for (size_t i = 0; i < nThreads; i++) {\n          workers_.push_back(std::thread([this] { this->predApplier(); }));\n        }\n      }\n\n      ~ExecutionPipeline() {\n        done_.store(true, std::memory_order_release);\n        wake_.notifyAll();\n        // Drain the pipeline in cases where all results were not consumed,\n        // either due to an exception or do to termination requested by consumer\n        // like take(n).\n        for (Result result; readPendingResult(result);) {\n        }\n        for (auto& w : workers_) {\n          w.join();\n        }\n      }\n\n      bool write(Value&& value) {\n        if (!pipeline_.write(std::forward<Value>(value))) {\n          return false;\n        }\n        ++active_;\n        wake_.notify();\n        return true;\n      }\n\n      void blockingWrite(Value&& value) {\n        pipeline_.blockingWrite(std::forward<Value>(value));\n        ++active_;\n        wake_.notify();\n      }\n\n      bool read(Result& result) {\n        if (!pipeline_.read(result)) {\n          return false;\n        }\n        --active_;\n        return true;\n      }\n\n      bool readPendingResult(Result& result) {\n        if (active_ == 0) {\n          return false;\n        }\n        pipeline_.blockingRead(result);\n        --active_;\n        return true;\n      }\n\n     private:\n      void predApplier() {\n        // Each thread takes a value from the pipeline_, runs the\n        // predicate and enqueues the result. The pipeline preserves\n        // ordering. NOTE: don't use blockingReadStage<0> to read from\n        // the pipeline_ as there may not be any: end-of-data is signaled\n        // separately using done_/wake_.\n        Input in;\n        for (;;) {\n          auto key = wake_.prepareWait();\n\n          typename Pipeline::template Ticket<0> ticket;\n          if (pipeline_.template readStage<0>(ticket, in)) {\n            wake_.cancelWait();\n            try {\n              Output out = pred_(std::move(in));\n              pipeline_.template blockingWriteStage<0>(ticket, std::move(out));\n            } catch (...) {\n              pipeline_.template blockingWriteStage<0>(\n                  ticket, makeUnexpected(current_exception()));\n            }\n            continue;\n          }\n\n          if (done_.load(std::memory_order_acquire)) {\n            wake_.cancelWait();\n            break;\n          }\n\n          // Not done_, but no items in the queue.\n          wake_.wait(key);\n        }\n      }\n    };\n\n    static Output&& getOutput(Result& result) {\n      if (result.hasError()) {\n        std::rethrow_exception(std::move(result).error());\n      }\n      return std::move(result).value();\n    }\n\n   public:\n    Generator(Source source, const Predicate& pred, size_t nThreads)\n        : source_(std::move(source)),\n          pred_(pred),\n          nThreads_(nThreads ? nThreads : folly::available_concurrency()) {}\n\n    template <class Body>\n    void foreach(Body&& body) const {\n      ExecutionPipeline pipeline(pred_, nThreads_);\n\n      source_.foreach([&](Value value) {\n        if (pipeline.write(std::forward<Value>(value))) {\n          // input queue not yet full, saturate it before we process\n          // anything downstream\n          return;\n        }\n\n        // input queue full; drain ready items from the queue\n        for (Result result; pipeline.read(result);) {\n          body(getOutput(result));\n        }\n\n        // write the value we were going to write before we made room.\n        pipeline.blockingWrite(std::forward<Value>(value));\n      });\n      // flush the output queue\n      for (Result result; pipeline.readPendingResult(result);) {\n        body(getOutput(result));\n      }\n    }\n\n    template <class Handler>\n    bool apply(Handler&& handler) const {\n      ExecutionPipeline pipeline(pred_, nThreads_);\n\n      if (!source_.apply([&](Value value) {\n            if (pipeline.write(std::forward<Value>(value))) {\n              // input queue not yet full, saturate it before we process\n              // anything downstream\n              return true;\n            }\n\n            // input queue full; drain ready items from the queue\n            for (Result result; pipeline.read(result);) {\n              if (!handler(getOutput(result))) {\n                return false;\n              }\n            }\n\n            // write the value we were going to write before we made room.\n            pipeline.blockingWrite(std::forward<Value>(value));\n            return true;\n          })) {\n        return false;\n      }\n\n      // flush the output queue\n      for (Result result; pipeline.readPendingResult(result);) {\n        if (!handler(getOutput(result))) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), pred_, nThreads_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Value, Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), pred_, nThreads_);\n  }\n};\n\n} // namespace folly::gen::detail\n"
  },
  {
    "path": "folly/gen/ParallelMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_GEN_PARALLELMAP_H_\n\n#include <folly/gen/Core.h>\n\nnamespace folly {\nnamespace gen {\n\nnamespace detail {\n\ntemplate <class Predicate>\nclass PMap;\n\n} // namespace detail\n\n/**\n * Run `pred` in parallel in nThreads. Results are returned in the\n * same order in which they were retrieved from the source generator\n * (similar to map).\n *\n * NOTE: Only `pred` is run from separate threads; the source\n *       generator and the rest of the pipeline is executed in the\n *       caller thread.\n */\ntemplate <class Predicate, class PMap = detail::PMap<Predicate>>\nPMap pmap(Predicate pred = Predicate(), size_t nThreads = 0) {\n  return PMap(std::move(pred), nThreads);\n}\n} // namespace gen\n} // namespace folly\n\n#include <folly/gen/ParallelMap-inl.h>\n"
  },
  {
    "path": "folly/gen/String-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_GEN_STRING_H_\n#error This file may only be included from folly/gen/String.h\n#endif\n\n#include <folly/Conv.h>\n#include <folly/Portability.h>\n#include <folly/String.h>\n\nnamespace folly {\nnamespace gen {\nnamespace detail {\n\n/**\n * Finds the first occurrence of delimiter in \"in\", advances \"in\" past the\n * delimiter.  Populates \"prefix\" with the consumed bytes, including the\n * delimiter.\n *\n * Returns the number of trailing bytes of \"prefix\" that make up the\n * delimiter, or 0 if the delimiter was not found.\n */\ninline size_t splitPrefix(\n    StringPiece& in, StringPiece& prefix, char delimiter) {\n  size_t found = in.find(delimiter);\n  if (found != StringPiece::npos) {\n    ++found;\n    prefix.assign(in.data(), in.data() + found);\n    in.advance(found);\n    return 1;\n  }\n  prefix.clear();\n  return 0;\n}\n\n/**\n * As above, but supports multibyte delimiters.\n */\ninline size_t splitPrefix(\n    StringPiece& in, StringPiece& prefix, StringPiece delimiter) {\n  auto found = in.find(delimiter);\n  if (found != StringPiece::npos) {\n    found += delimiter.size();\n    prefix.assign(in.data(), in.data() + found);\n    in.advance(found);\n    return delimiter.size();\n  }\n  prefix.clear();\n  return 0;\n}\n\n/**\n * As above, but splits by any of the EOL terms: \\r, \\n, or \\r\\n.\n */\ninline size_t splitPrefix(StringPiece& in, StringPiece& prefix, MixedNewlines) {\n  const auto kCRLF = \"\\r\\n\";\n  const size_t kLenCRLF = 2;\n\n  auto p = in.find_first_of(kCRLF);\n  if (p != std::string::npos) {\n    const auto in_start = in.data();\n    size_t delim_len = 1;\n    in.advance(p);\n    // Either remove an MS-DOS CR-LF 2-byte newline, or eat 1 byte at a time.\n    if (in.removePrefix(kCRLF)) {\n      delim_len = kLenCRLF;\n    } else {\n      in.advance(delim_len);\n    }\n    prefix.assign(in_start, in.data());\n    return delim_len;\n  }\n  prefix.clear();\n  return 0;\n}\n\ninline const char* ch(const unsigned char* p) {\n  return reinterpret_cast<const char*>(p);\n}\n\n// Chop s into pieces of at most maxLength, feed them to cb\ntemplate <class Callback>\nbool consumeFixedSizeChunks(Callback& cb, StringPiece& s, uint64_t maxLength) {\n  while (!s.empty()) {\n    auto num_to_add = s.size();\n    if (maxLength) {\n      num_to_add = std::min<uint64_t>(num_to_add, maxLength);\n    }\n    if (!cb(StringPiece(s.begin(), num_to_add))) {\n      return false;\n    }\n    s.advance(num_to_add);\n  }\n  return true;\n}\n\n// Consumes all of buffer, plus n chars from s.\ntemplate <class Callback>\nbool consumeBufferPlus(Callback& cb, IOBuf& buf, StringPiece& s, uint64_t n) {\n  buf.reserve(0, n);\n  memcpy(buf.writableTail(), s.data(), n);\n  buf.append(n);\n  s.advance(n);\n  if (!cb(StringPiece(detail::ch(buf.data()), buf.length()))) {\n    return false;\n  }\n  buf.clear();\n  return true;\n}\n\n} // namespace detail\n\ntemplate <class Callback>\nbool StreamSplitter<Callback>::flush() {\n  CHECK(maxLength_ == 0 || buffer_.length() < maxLength_);\n  if (!pieceCb_(StringPiece(detail::ch(buffer_.data()), buffer_.length()))) {\n    return false;\n  }\n  // We are ready to handle another stream now.\n  buffer_.clear();\n  return true;\n}\n\ntemplate <class Callback>\nbool StreamSplitter<Callback>::operator()(StringPiece in) {\n  StringPiece prefix;\n  // NB This code assumes a 1-byte delimiter. It's not too hard to support\n  // multibyte delimiters, just remember that maxLength_ chunks can end up\n  // falling in the middle of a delimiter.\n  bool found = detail::splitPrefix(in, prefix, delimiter_);\n  if (buffer_.length() != 0) {\n    if (found) {\n      uint64_t num_to_add = prefix.size();\n      if (maxLength_) {\n        CHECK(buffer_.length() < maxLength_);\n        // Consume as much of prefix as possible without exceeding maxLength_\n        num_to_add = std::min(maxLength_ - buffer_.length(), num_to_add);\n      }\n\n      // Append part of the prefix to the buffer, and send it to the callback\n      if (!detail::consumeBufferPlus(pieceCb_, buffer_, prefix, num_to_add)) {\n        return false;\n      }\n\n      if (!detail::consumeFixedSizeChunks(pieceCb_, prefix, maxLength_)) {\n        return false;\n      }\n\n      found = detail::splitPrefix(in, prefix, delimiter_);\n      // Post-conditions:\n      //  - we consumed all of buffer_ and all of the first prefix.\n      //  - found, in, and prefix reflect the second delimiter_ search\n    } else if (maxLength_ && buffer_.length() + in.size() >= maxLength_) {\n      // Send all of buffer_, plus a bit of in, to the callback\n      if (!detail::consumeBufferPlus(\n              pieceCb_, buffer_, in, maxLength_ - buffer_.length())) {\n        return false;\n      }\n      // Post-conditions:\n      //  - we consumed all of buffer, and the minimal # of bytes from in\n      //  - found is false\n    } // Otherwise: found is false & we cannot invoke the callback this turn\n  }\n  // Post-condition: buffer_ is nonempty only if found is false **and**\n  // len(buffer + in) < maxLength_.\n\n  // Send lines to callback directly from input (no buffer)\n  while (found) { // Buffer guaranteed to be empty\n    if (!detail::consumeFixedSizeChunks(pieceCb_, prefix, maxLength_)) {\n      return false;\n    }\n    found = detail::splitPrefix(in, prefix, delimiter_);\n  }\n\n  // No more delimiters left; consume 'in' until it is shorter than maxLength_\n  if (maxLength_) {\n    while (in.size() >= maxLength_) { // Buffer is guaranteed to be empty\n      if (!pieceCb_(StringPiece(in.begin(), maxLength_))) {\n        return false;\n      }\n      in.advance(maxLength_);\n    }\n  }\n\n  if (!in.empty()) { // Buffer may be nonempty\n    // Incomplete line left, append to buffer\n    buffer_.reserve(0, in.size());\n    memcpy(buffer_.writableTail(), in.data(), in.size());\n    buffer_.append(in.size());\n  }\n  CHECK(maxLength_ == 0 || buffer_.length() < maxLength_);\n  return true;\n}\n\nnamespace detail {\n\nclass StringResplitter : public Operator<StringResplitter> {\n  char delimiter_;\n  bool keepDelimiter_;\n\n public:\n  explicit StringResplitter(char delimiter, bool keepDelimiter = false)\n      : delimiter_(delimiter), keepDelimiter_(keepDelimiter) {}\n\n  template <class Source>\n  class Generator : public GenImpl<StringPiece, Generator<Source>> {\n    Source source_;\n    char delimiter_;\n    bool keepDelimiter_;\n\n   public:\n    Generator(Source source, char delimiter, bool keepDelimiter)\n        : source_(std::move(source)),\n          delimiter_(delimiter),\n          keepDelimiter_(keepDelimiter) {}\n\n    template <class Body>\n    bool apply(Body&& body) const {\n      auto splitter =\n          streamSplitter(this->delimiter_, [this, &body](StringPiece s) {\n            // The stream ended with a delimiter; our contract is to swallow\n            // the final empty piece.\n            if (s.empty()) {\n              return true;\n            }\n            if (s.back() != this->delimiter_) {\n              return body(s);\n            }\n            if (!keepDelimiter_) {\n              s.pop_back(); // Remove the 1-character delimiter\n            }\n            return body(s);\n          });\n      if (!source_.apply(splitter)) {\n        return false;\n      }\n      return splitter.flush();\n    }\n\n    static constexpr bool infinite = Source::infinite;\n  };\n\n  template <class Source, class Value, class Gen = Generator<Source>>\n  Gen compose(GenImpl<Value, Source>&& source) const {\n    return Gen(std::move(source.self()), delimiter_, keepDelimiter_);\n  }\n\n  template <class Source, class Value, class Gen = Generator<Source>>\n  Gen compose(const GenImpl<Value, Source>& source) const {\n    return Gen(source.self(), delimiter_, keepDelimiter_);\n  }\n};\n\ntemplate <class DelimiterType = char>\nclass SplitStringSource\n    : public GenImpl<StringPiece, SplitStringSource<DelimiterType>> {\n  StringPiece source_;\n  DelimiterType delimiter_;\n\n public:\n  SplitStringSource(const StringPiece source, DelimiterType delimiter)\n      : source_(source), delimiter_(std::move(delimiter)) {}\n\n  template <class Body>\n  bool apply(Body&& body) const {\n    StringPiece rest(source_);\n    StringPiece prefix;\n    while (size_t delim_len = splitPrefix(rest, prefix, this->delimiter_)) {\n      prefix.subtract(delim_len); // Remove the delimiter\n      if (!body(prefix)) {\n        return false;\n      }\n    }\n    if (!rest.empty()) {\n      if (!body(rest)) {\n        return false;\n      }\n    }\n    return true;\n  }\n};\n\n/**\n * Unsplit - For joining tokens from a generator into a string.  This is\n * the inverse of `split` above.\n *\n * This type is primarily used through the 'unsplit' function.\n */\ntemplate <class Delimiter, class Output>\nclass Unsplit : public Operator<Unsplit<Delimiter, Output>> {\n  Delimiter delimiter_;\n\n public:\n  explicit Unsplit(const Delimiter& delimiter) : delimiter_(delimiter) {}\n\n  template <class Source, class Value>\n  Output compose(const GenImpl<Value, Source>& source) const {\n    Output outputBuffer;\n    UnsplitBuffer<Delimiter, Output> unsplitter(delimiter_, &outputBuffer);\n    unsplitter.compose(source);\n    return outputBuffer;\n  }\n};\n\n/**\n * UnsplitBuffer - For joining tokens from a generator into a string,\n * and inserting them into a custom buffer.\n *\n * This type is primarily used through the 'unsplit' function.\n */\ntemplate <class Delimiter, class OutputBuffer>\nclass UnsplitBuffer : public Operator<UnsplitBuffer<Delimiter, OutputBuffer>> {\n  Delimiter delimiter_;\n  OutputBuffer* outputBuffer_;\n\n public:\n  UnsplitBuffer(const Delimiter& delimiter, OutputBuffer* outputBuffer)\n      : delimiter_(delimiter), outputBuffer_(outputBuffer) {\n    CHECK(outputBuffer);\n  }\n\n  template <class Source, class Value>\n  void compose(const GenImpl<Value, Source>& source) const {\n    // If the output buffer is empty, we skip inserting the delimiter for the\n    // first element.\n    bool skipDelim = outputBuffer_->empty();\n    source | [&](Value v) {\n      if (skipDelim) {\n        skipDelim = false;\n        toAppend(std::forward<Value>(v), outputBuffer_);\n      } else {\n        toAppend(delimiter_, std::forward<Value>(v), outputBuffer_);\n      }\n    };\n  }\n};\n\n/**\n * Hack for static for-like constructs\n */\ntemplate <class Target, class = void>\ninline Target passthrough(Target target) {\n  return target;\n}\n\nFOLLY_PUSH_WARNING\n#ifdef __clang__\n// Clang isn't happy with eatField() hack below.\n#pragma GCC diagnostic ignored \"-Wreturn-stack-address\"\n#endif // __clang__\n\n/**\n * ParseToTuple - For splitting a record and immediatlely converting it to a\n * target tuple type. Primary used through the 'eachToTuple' helper, like so:\n *\n *  auto config\n *    = split(\"1:a 2:b\", ' ')\n *    | eachToTuple<int, string>()\n *    | as<vector<tuple<int, string>>>();\n *\n */\ntemplate <class TargetContainer, class Delimiter, class... Targets>\nclass SplitTo {\n  Delimiter delimiter_;\n\n public:\n  explicit SplitTo(Delimiter delimiter) : delimiter_(delimiter) {}\n\n  TargetContainer operator()(StringPiece line) const {\n    int i = 0;\n    StringPiece fields[sizeof...(Targets)];\n    // HACK(tjackson): Used for referencing fields[] corresponding to variadic\n    // template parameters.\n    auto eatField = [&]() -> StringPiece& { return fields[i++]; };\n    if (!split(\n            delimiter_,\n            line,\n            detail::passthrough<StringPiece&, Targets>(eatField())...)) {\n      throw std::runtime_error(\"field count mismatch\");\n    }\n    i = 0;\n    return TargetContainer(To<Targets>()(eatField())...);\n  }\n};\n\nFOLLY_POP_WARNING\n\n} // namespace detail\n\n} // namespace gen\n} // namespace folly\n"
  },
  {
    "path": "folly/gen/String.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#define FOLLY_GEN_STRING_H_\n\n#include <folly/Range.h>\n#include <folly/gen/Base.h>\n#include <folly/io/IOBuf.h>\n\nnamespace folly {\nnamespace gen {\n\nnamespace detail {\nclass StringResplitter;\n\ntemplate <class Delimiter>\nclass SplitStringSource;\n\ntemplate <class Delimiter, class Output>\nclass Unsplit;\n\ntemplate <class Delimiter, class OutputBuffer>\nclass UnsplitBuffer;\n\ntemplate <class TargetContainer, class Delimiter, class... Targets>\nclass SplitTo;\n\n} // namespace detail\n\n/**\n * Split the output from a generator into StringPiece \"lines\" delimited by\n * the given delimiter.  Delimters are NOT included in the output.\n *\n * resplit() behaves as if the input strings were concatenated into one long\n * string and then split.\n *\n * Equivalently, you can use StreamSplitter outside of a folly::gen setting.\n */\n// make this a template so we don't require StringResplitter to be complete\n// until use\ntemplate <class S = detail::StringResplitter>\nS resplit(char delimiter, bool keepDelimiter = false) {\n  return S(delimiter, keepDelimiter);\n}\n\ntemplate <class S = detail::SplitStringSource<char>>\nS split(const StringPiece source, char delimiter) {\n  return S(source, delimiter);\n}\n\ntemplate <class S = detail::SplitStringSource<StringPiece>>\nS split(StringPiece source, StringPiece delimiter) {\n  return S(source, delimiter);\n}\n\n/**\n * EOL terms (\"\\r\", \"\\n\", or \"\\r\\n\").\n */\nclass MixedNewlines {};\n\n/**\n * Split by EOL (\"\\r\", \"\\n\", or \"\\r\\n\").\n * @see split().\n */\ntemplate <class S = detail::SplitStringSource<MixedNewlines>>\nS lines(StringPiece source) {\n  return S(source, MixedNewlines{});\n}\n\n/*\n * Joins a sequence of tokens into a string, with the chosen delimiter.\n *\n * E.G.\n *   fbstring result = split(\"a,b,c\", \",\") | unsplit(\",\");\n *   assert(result == \"a,b,c\");\n *\n *   std::string result = split(\"a,b,c\", \",\") | unsplit<std::string>(\" \");\n *   assert(result == \"a b c\");\n */\n\n// NOTE: The template arguments are reversed to allow the user to cleanly\n// specify the output type while still inferring the type of the delimiter.\ntemplate <\n    class Output = folly::fbstring,\n    class Delimiter,\n    class Unsplit = detail::Unsplit<Delimiter, Output>>\nUnsplit unsplit(const Delimiter& delimiter) {\n  return Unsplit(delimiter);\n}\n\ntemplate <\n    class Output = folly::fbstring,\n    class Unsplit = detail::Unsplit<fbstring, Output>>\nUnsplit unsplit(const char* delimiter) {\n  return Unsplit(delimiter);\n}\n\n/*\n * Joins a sequence of tokens into a string, appending them to the output\n * buffer.  If the output buffer is empty, an initial delimiter will not be\n * inserted at the start.\n *\n * E.G.\n *   std::string buffer;\n *   split(\"a,b,c\", \",\") | unsplit(\",\", &buffer);\n *   assert(buffer == \"a,b,c\");\n *\n *   std::string anotherBuffer(\"initial\");\n *   split(\"a,b,c\", \",\") | unsplit(\",\", &anotherbuffer);\n *   assert(anotherBuffer == \"initial,a,b,c\");\n */\ntemplate <\n    class Delimiter,\n    class OutputBuffer,\n    class UnsplitBuffer = detail::UnsplitBuffer<Delimiter, OutputBuffer>>\nUnsplitBuffer unsplit(Delimiter delimiter, OutputBuffer* outputBuffer) {\n  return UnsplitBuffer(delimiter, outputBuffer);\n}\n\ntemplate <\n    class OutputBuffer,\n    class UnsplitBuffer = detail::UnsplitBuffer<fbstring, OutputBuffer>>\nUnsplitBuffer unsplit(const char* delimiter, OutputBuffer* outputBuffer) {\n  return UnsplitBuffer(delimiter, outputBuffer);\n}\n\ntemplate <class... Targets>\ndetail::Map<detail::SplitTo<std::tuple<Targets...>, char, Targets...>>\neachToTuple(char delim) {\n  return detail::Map<detail::SplitTo<std::tuple<Targets...>, char, Targets...>>(\n      detail::SplitTo<std::tuple<Targets...>, char, Targets...>(delim));\n}\n\ntemplate <class... Targets>\ndetail::Map<detail::SplitTo<std::tuple<Targets...>, fbstring, Targets...>>\neachToTuple(StringPiece delim) {\n  return detail::Map<\n      detail::SplitTo<std::tuple<Targets...>, fbstring, Targets...>>(\n      detail::SplitTo<std::tuple<Targets...>, fbstring, Targets...>(\n          to<fbstring>(delim)));\n}\n\ntemplate <class First, class Second>\ndetail::Map<detail::SplitTo<std::pair<First, Second>, char, First, Second>>\neachToPair(char delim) {\n  return detail::Map<\n      detail::SplitTo<std::pair<First, Second>, char, First, Second>>(\n      detail::SplitTo<std::pair<First, Second>, char, First, Second>(delim));\n}\n\ntemplate <class First, class Second>\ndetail::Map<detail::SplitTo<std::pair<First, Second>, fbstring, First, Second>>\neachToPair(StringPiece delim) {\n  return detail::Map<\n      detail::SplitTo<std::pair<First, Second>, fbstring, First, Second>>(\n      detail::SplitTo<std::pair<First, Second>, fbstring, First, Second>(\n          to<fbstring>(delim)));\n}\n\n/**\n * Outputs exactly the same bytes as the input stream, in different chunks.\n * A chunk boundary occurs after each delimiter, or, if maxLength is\n * non-zero, after maxLength bytes, whichever comes first.  Your callback\n * can return false to stop consuming the stream at any time.\n *\n * The splitter buffers the last incomplete chunk, so you must call flush()\n * to consume the piece of the stream after the final delimiter.  This piece\n * may be empty.  After a flush(), the splitter can be re-used for a new\n * stream.\n *\n * operator() and flush() return false iff your callback returns false. The\n * internal buffer is not flushed, so reusing such a splitter will have\n * indeterminate results.  Same goes if your callback throws.  Feel free to\n * fix these corner cases if needed.\n *\n * Tips:\n *  - Create via streamSplitter() to take advantage of template deduction.\n *  - If your callback needs an end-of-stream signal, test for \"no\n *    trailing delimiter **and** shorter than maxLength\".\n *  - You can fine-tune the initial capacity of the internal IOBuf.\n */\ntemplate <class Callback>\nclass StreamSplitter {\n public:\n  StreamSplitter(\n      char delimiter,\n      Callback&& pieceCb,\n      uint64_t maxLength = 0,\n      uint64_t initialCapacity = 0)\n      : buffer_(IOBuf::CREATE, initialCapacity),\n        delimiter_(delimiter),\n        maxLength_(maxLength),\n        pieceCb_(std::move(pieceCb)) {}\n\n  /**\n   * Consume any incomplete last line (may be empty). Do this before\n   * destroying the StreamSplitter, or you will fail to consume part of the\n   * input.\n   *\n   * After flush() you may proceed to consume the next stream via ().\n   *\n   * Returns false if the callback wants no more data, true otherwise.\n   * A return value of false means that this splitter must no longer be used.\n   */\n  bool flush();\n\n  /**\n   * Consume another piece of the input stream.\n   *\n   * Returns false only if your callback refuses to consume more data by\n   * returning false (true otherwise).  A return value of false means that\n   * this splitter must no longer be used.\n   */\n  bool operator()(StringPiece in);\n\n private:\n  // Holds the current \"incomplete\" chunk so that chunks can span calls to ()\n  IOBuf buffer_;\n  char delimiter_;\n  uint64_t maxLength_; // The callback never gets more chars than this\n  Callback pieceCb_;\n};\n\ntemplate <class Callback> // Helper to enable template deduction\nStreamSplitter<Callback> streamSplitter(\n    char delimiter, Callback&& pieceCb, uint64_t capacity = 0) {\n  return StreamSplitter<Callback>(delimiter, std::move(pieceCb), capacity);\n}\n\n} // namespace gen\n} // namespace folly\n\n#include <folly/gen/String-inl.h>\n"
  },
  {
    "path": "folly/gen/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"base_benchmark\",\n    srcs = [\"BaseBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly/gen:base\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"base_test\",\n    srcs = [\"BaseTest.cpp\"],\n    headers = [],\n    labels = [\n        \"serialize_test_cases\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:fbvector\",\n        \"//folly:map_util\",\n        \"//folly:memory\",\n        \"//folly:string\",\n        \"//folly/gen:base\",\n        \"//folly/json:dynamic\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"combine_test\",\n    srcs = [\"CombineTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:fbvector\",\n        \"//folly/gen:base\",\n        \"//folly/gen:combine\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"file_benchmark\",\n    srcs = [\"FileBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:file\",\n        \"//folly/gen:base\",\n        \"//folly/gen:file\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"file_test\",\n    srcs = [\"FileTest.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:file\",\n        \"//folly:range\",\n        \"//folly/container:array\",\n        \"//folly/gen:base\",\n        \"//folly/gen:file\",\n        \"//folly/portability:gtest\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"parallel_benchmark\",\n    srcs = [\"ParallelBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":util\",\n        \"//folly/gen:base\",\n        \"//folly/gen:parallel\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"parallel_test\",\n    srcs = [\"ParallelTest.cpp\"],\n    headers = [],\n    labels = [\n        \"serialize\",\n    ],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/gen:base\",\n        \"//folly/gen:parallel\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"parallel_map_benchmark\",\n    srcs = [\"ParallelMapBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/gen:base\",\n        \"//folly/gen:parallel_map\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"parallel_map_test\",\n    srcs = [\"ParallelMapTest.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:memory\",\n        \"//folly/gen:base\",\n        \"//folly/gen:parallel_map\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"string_benchmark\",\n    srcs = [\"StringBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:string\",\n        \"//folly/container:foreach\",\n        \"//folly/gen:base\",\n        \"//folly/gen:string\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"string_test\",\n    srcs = [\"StringTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/functional:apply_tuple\",\n        \"//folly/gen:string\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"istream_test\",\n    srcs = [\"IStreamTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/gen:base\",\n        \"//folly/gen:istream\",\n        \"//folly/gen:string\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"util\",\n    headers = [\"Bench.h\"],\n    exported_deps = [\n        \"//folly:benchmark\",\n    ],\n)\n"
  },
  {
    "path": "folly/gen/test/BaseBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/gen/Base.h>\n\nusing namespace folly::gen;\nusing folly::fbstring;\nusing std::vector;\n\nstatic std::atomic<int> testSize(1000);\n// clang-format off\nstatic vector<int> testVector =\n    seq(1, testSize.load())\n  | mapped([](int) { return rand(); })\n  | as<vector>();\n\nstatic vector<vector<int>> testVectorVector =\n    seq(1, 100)\n  | map([](int i) {\n      return seq(1, i) | as<vector>();\n    })\n  | as<vector>();\nstatic vector<fbstring> strings =\n    from(testVector)\n  | eachTo<fbstring>()\n  | as<vector>();\n// clang-format on\n\nauto square = [](int x) { return x * x; };\n\nBENCHMARK(Sum_Basic_NoGen, iters) {\n  int limit = testSize.load();\n  int s = 0;\n  while (iters--) {\n    for (int i = 0; i < limit; ++i) {\n      s += i;\n    }\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Sum_Basic_Gen, iters) {\n  int limit = testSize.load();\n  int s = 0;\n  while (iters--) {\n    s += range(0, limit) | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Sum_Vector_NoGen, iters) {\n  int s = 0;\n  while (iters--) {\n    for (auto& i : testVector) {\n      s += i;\n    }\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Sum_Vector_Gen, iters) {\n  int s = 0;\n  while (iters--) {\n    s += from(testVector) | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Member, iters) {\n  int s = 0;\n  while (iters--) {\n    // clang-format off\n    s += from(strings)\n       | member(&fbstring::size)\n       | sum;\n    // clang-format on\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(MapMember, iters) {\n  int s = 0;\n  while (iters--) {\n    // clang-format off\n    s += from(strings)\n       | map([](const fbstring& x) { return x.size(); })\n       | sum;\n    // clang-format on\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Count_Vector_NoGen, iters) {\n  int s = 0;\n  while (iters--) {\n    for (auto& i : testVector) {\n      if (i * 2 < rand()) {\n        ++s;\n      }\n    }\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Count_Vector_Gen, iters) {\n  int s = 0;\n  while (iters--) {\n    // clang-format off\n    s += from(testVector)\n       | filter([](int i) {\n                  return i * 2 < rand();\n                })\n       | count;\n    // clang-format on\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Fib_Sum_NoGen, iters) {\n  int s = 0;\n  while (iters--) {\n    auto fib = [](size_t limit) -> vector<int> {\n      vector<int> ret;\n      int a = 0;\n      int b = 1;\n      for (size_t i = 0; i < limit; i += 2) {\n        ret.push_back(a += b);\n        ret.push_back(b += a);\n      }\n      return ret;\n    };\n    for (auto& v : fib(testSize.load())) {\n      s += v;\n    }\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Fib_Sum_Gen, iters) {\n  int s = 0;\n  while (iters--) {\n    auto fib = GENERATOR(int) {\n      int a = 0;\n      int b = 1;\n      for (;;) {\n        yield(a += b);\n        yield(b += a);\n      }\n    };\n    // Early stopping implemented with exceptions.\n    s += fib | take(testSize.load()) | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Fib_Sum_Gen_Limit, iters) {\n  int s = 0;\n  while (iters--) {\n    size_t limit = testSize.load();\n    auto fib = GENERATOR(int) {\n      int a = 0;\n      int b = 1;\n      for (size_t i = 0; i < limit; i += 2) {\n        yield(a += b);\n        yield(b += a);\n      }\n    };\n    // No early stopping.\n    s += fib | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nstruct FibYielder {\n  template <class Yield>\n  void operator()(Yield&& yield) const {\n    int a = 0;\n    int b = 1;\n    for (;;) {\n      yield(a += b);\n      yield(b += a);\n    }\n  }\n};\n\nBENCHMARK_RELATIVE(Fib_Sum_Gen_Static, iters) {\n  int s = 0;\n  while (iters--) {\n    auto fib = generator<int>(FibYielder());\n    s += fib | take(testSize.load()) | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(VirtualGen_0Virtual, iters) {\n  int s = 0;\n  while (iters--) {\n    auto numbers = seq(1, 10000);\n    auto squares = numbers | map(square);\n    auto quads = squares | map(square);\n    s += quads | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(VirtualGen_1Virtual, iters) {\n  int s = 0;\n  while (iters--) {\n    VirtualGen<int> numbers = seq(1, 10000);\n    auto squares = numbers | map(square);\n    auto quads = squares | map(square);\n    s += quads | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(VirtualGen_2Virtual, iters) {\n  int s = 0;\n  while (iters--) {\n    VirtualGen<int> numbers = seq(1, 10000);\n    VirtualGen<int> squares = numbers | map(square);\n    auto quads = squares | map(square);\n    s += quads | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(VirtualGen_3Virtual, iters) {\n  int s = 0;\n  while (iters--) {\n    VirtualGen<int> numbers = seq(1, 10000);\n    VirtualGen<int> squares = numbers | map(square);\n    VirtualGen<int> quads = squares | map(square);\n    s += quads | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Concat_NoGen, iters) {\n  int s = 0;\n  while (iters--) {\n    for (auto& v : testVectorVector) {\n      for (auto& i : v) {\n        s += i;\n      }\n    }\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Concat_Gen, iters) {\n  int s = 0;\n  while (iters--) {\n    s += from(testVectorVector) | rconcat | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Composed_NoGen, iters) {\n  int s = 0;\n  while (iters--) {\n    for (auto& i : testVector) {\n      s += i * i;\n    }\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Composed_Gen, iters) {\n  int s = 0;\n  auto sumSq = map(square) | sum;\n  while (iters--) {\n    s += from(testVector) | sumSq;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Composed_GenRegular, iters) {\n  int s = 0;\n  while (iters--) {\n    s += from(testVector) | map(square) | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(Sample, iters) {\n  size_t s = 0;\n  while (iters--) {\n    auto sampler = seq(1, 10 * 1000 * 1000) | sample(1000);\n    s += (sampler | sum);\n  }\n  folly::doNotOptimizeAway(s);\n}\n\n// Results from an Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz\n// ============================================================================\n// folly/gen/test/BaseBenchmark.cpp                relative  time/iter  iters/s\n// ============================================================================\n// Sum_Basic_NoGen                                            372.39ns    2.69M\n// Sum_Basic_Gen                                    195.96%   190.03ns    5.26M\n// ----------------------------------------------------------------------------\n// Sum_Vector_NoGen                                           200.41ns    4.99M\n// Sum_Vector_Gen                                    77.14%   259.81ns    3.85M\n// ----------------------------------------------------------------------------\n// Member                                                       4.56us  219.42K\n// MapMember                                        400.47%     1.14us  878.73K\n// ----------------------------------------------------------------------------\n// Count_Vector_NoGen                                          13.96us   71.64K\n// Count_Vector_Gen                                  86.05%    16.22us   61.65K\n// ----------------------------------------------------------------------------\n// Fib_Sum_NoGen                                                2.21us  452.63K\n// Fib_Sum_Gen                                       23.94%     9.23us  108.36K\n// Fib_Sum_Gen_Static                                48.77%     4.53us  220.73K\n// ----------------------------------------------------------------------------\n// VirtualGen_0Virtual                                          9.60us  104.13K\n// VirtualGen_1Virtual                               28.00%    34.30us   29.15K\n// VirtualGen_2Virtual                               22.62%    42.46us   23.55K\n// VirtualGen_3Virtual                               16.96%    56.64us   17.66K\n// ----------------------------------------------------------------------------\n// Concat_NoGen                                                 2.20us  453.66K\n// Concat_Gen                                       109.49%     2.01us  496.70K\n// ----------------------------------------------------------------------------\n// Composed_NoGen                                             545.32ns    1.83M\n// Composed_Gen                                      87.94%   620.07ns    1.61M\n// Composed_GenRegular                               88.13%   618.74ns    1.62M\n// ----------------------------------------------------------------------------\n// Sample                                                     176.48ms     5.67\n// ============================================================================\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/gen/test/BaseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/gen/Base.h>\n\n#include <iosfwd>\n#include <memory>\n#include <random>\n#include <set>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/FBVector.h>\n#include <folly/MapUtil.h>\n#include <folly/Memory.h>\n#include <folly/String.h>\n#include <folly/json/dynamic.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n#include <folly/testing/TestUtil.h>\n\nusing namespace folly::gen;\nusing namespace folly;\nusing std::make_tuple;\nusing std::ostream;\nusing std::pair;\nusing std::set;\nusing std::string;\nusing std::tuple;\nusing std::unique_ptr;\nusing std::vector;\n\n#define EXPECT_SAME(A, B) \\\n  static_assert(std::is_same<A, B>::value, \"Mismatched: \" #A \", \" #B)\nEXPECT_SAME(int&&, typename ArgumentReference<int>::type);\nEXPECT_SAME(int&, typename ArgumentReference<int&>::type);\nEXPECT_SAME(const int&, typename ArgumentReference<const int&>::type);\nEXPECT_SAME(const int&, typename ArgumentReference<const int>::type);\n\ntemplate <typename T>\nostream& operator<<(ostream& os, const set<T>& values) {\n  return os << from(values);\n}\n\ntemplate <typename T>\nostream& operator<<(ostream& os, const vector<T>& values) {\n  os << \"[\";\n  for (auto& value : values) {\n    if (&value != &values.front()) {\n      os << \" \";\n    }\n    os << value;\n  }\n  return os << \"]\";\n}\n\nauto square = [](int x) { return x * x; };\nauto add = [](int a, int b) { return a + b; };\nauto multiply = [](int a, int b) { return a * b; };\n\nauto product = foldl(1, multiply);\n\ntemplate <typename A, typename B>\nostream& operator<<(ostream& os, const pair<A, B>& pair) {\n  return os << \"(\" << pair.first << \", \" << pair.second << \")\";\n}\n\nTEST(Gen, Count) {\n  auto gen = seq(1, 10);\n  EXPECT_EQ(10, gen | count);\n  EXPECT_EQ(5, gen | take(5) | count);\n}\n\nTEST(Gen, Sum) {\n  auto gen = seq(1, 10);\n  EXPECT_EQ((1 + 10) * 10 / 2, gen | sum);\n  EXPECT_EQ((1 + 5) * 5 / 2, gen | take(5) | sum);\n}\n\nTEST(Gen, Foreach) {\n  auto gen = seq(1, 4);\n  int accum = 0;\n  gen | [&](int x) { accum += x; };\n  EXPECT_EQ(10, accum);\n  int accum2 = 0;\n  gen | take(3) | [&](int x) { accum2 += x; };\n  EXPECT_EQ(6, accum2);\n}\n\nTEST(Gen, Map) {\n  auto expected = vector<int>{4, 9, 16};\n  auto gen = from({2, 3, 4}) | map(square);\n  EXPECT_EQ((vector<int>{4, 9, 16}), gen | as<vector>());\n  EXPECT_EQ((vector<int>{4, 9}), gen | take(2) | as<vector>());\n}\n\nTEST(Gen, Member) {\n  struct Counter {\n    Counter(int start = 0) : c(start) {}\n\n    int count() const { return c; }\n    int incr() { return ++c; }\n\n    int& ref() { return c; }\n    const int& ref() const { return c; }\n\n   private:\n    int c;\n  };\n  auto counters = seq(1, 10) | eachAs<Counter>() | as<vector>();\n  EXPECT_EQ(10 * (1 + 10) / 2, from(counters) | member(&Counter::count) | sum);\n  EXPECT_EQ(\n      10 * (1 + 10) / 2,\n      from(counters) | indirect | member(&Counter::count) | sum);\n  EXPECT_EQ(10 * (2 + 11) / 2, from(counters) | member(&Counter::incr) | sum);\n  EXPECT_EQ(\n      10 * (3 + 12) / 2,\n      from(counters) | indirect | member(&Counter::incr) | sum);\n  EXPECT_EQ(10 * (3 + 12) / 2, from(counters) | member(&Counter::count) | sum);\n\n  // type-verifications\n  auto m = empty<Counter&>();\n  auto c = empty<const Counter&>();\n  m | member(&Counter::incr) | assert_type<int&&>();\n  m | member(&Counter::count) | assert_type<int&&>();\n  m | member(&Counter::count) | assert_type<int&&>();\n  m | member<Const>(&Counter::ref) | assert_type<const int&>();\n  m | member<Mutable>(&Counter::ref) | assert_type<int&>();\n  c | member<Const>(&Counter::ref) | assert_type<const int&>();\n}\n\nTEST(Gen, Field) {\n  struct X {\n    X() : a(2), b(3), c(4), d(b) {}\n\n    const int a;\n    int b;\n    mutable int c;\n    int& d; // can't access this with a field pointer.\n  };\n\n  std::vector<X> xs(1);\n  EXPECT_EQ(2, from(xs) | field(&X::a) | sum);\n  EXPECT_EQ(3, from(xs) | field(&X::b) | sum);\n  EXPECT_EQ(4, from(xs) | field(&X::c) | sum);\n  EXPECT_EQ(2, seq(&xs[0], &xs[0]) | field(&X::a) | sum);\n  // type-verification\n  empty<X&>() | field(&X::a) | assert_type<const int&>();\n  empty<X*>() | field(&X::a) | assert_type<const int&>();\n  empty<X&>() | field(&X::b) | assert_type<int&>();\n  empty<X*>() | field(&X::b) | assert_type<int&>();\n  empty<X&>() | field(&X::c) | assert_type<int&>();\n  empty<X*>() | field(&X::c) | assert_type<int&>();\n\n  empty<X&&>() | field(&X::a) | assert_type<const int&&>();\n  empty<X&&>() | field(&X::b) | assert_type<int&&>();\n  empty<X&&>() | field(&X::c) | assert_type<int&&>();\n  // references don't imply ownership so they're not moved\n\n  empty<const X&>() | field(&X::a) | assert_type<const int&>();\n  empty<const X*>() | field(&X::a) | assert_type<const int&>();\n  empty<const X&>() | field(&X::b) | assert_type<const int&>();\n  empty<const X*>() | field(&X::b) | assert_type<const int&>();\n  // 'mutable' has no effect on field pointers, by C++ spec\n  empty<const X&>() | field(&X::c) | assert_type<const int&>();\n  empty<const X*>() | field(&X::c) | assert_type<const int&>();\n\n  // can't form pointer-to-reference field: empty<X&>() | field(&X::d)\n}\n\nTEST(Gen, Seq) {\n  // cover the fenceposts of the loop unrolling\n  for (int n = 1; n < 100; ++n) {\n    EXPECT_EQ(n, seq(1, n) | count);\n    EXPECT_EQ(n + 1, seq(1) | take(n + 1) | count);\n  }\n}\n\nTEST(Gen, SeqWithStep) {\n  EXPECT_EQ(75, seq(5, 25, 5) | sum);\n}\n\nTEST(Gen, SeqWithStepArray) {\n  const std::array<int, 6> arr{{1, 2, 3, 4, 5, 6}};\n  EXPECT_EQ(\n      9, seq(&arr[0], &arr[5], 2) | map([](const int* i) { return *i; }) | sum);\n}\n\nTEST(Gen, Range) {\n  // cover the fenceposts of the loop unrolling\n  for (int n = 1; n < 100; ++n) {\n    EXPECT_EQ(gen::range(0, n) | count, n);\n  }\n}\n\nTEST(Gen, RangeWithStep) {\n  EXPECT_EQ(50, range(5, 25, 5) | sum);\n}\n\nTEST(Gen, FromIterators) {\n  vector<int> source{2, 3, 5, 7, 11};\n  auto gen = from(folly::range(source.begin() + 1, source.end() - 1));\n  EXPECT_EQ(3 * 5 * 7, gen | product);\n}\n\nTEST(Gen, FromMap) {\n  // clang-format off\n  auto source\n      = seq(0, 10)\n      | map([](int i) { return std::make_pair(i, i * i); })\n      | as<std::map<int, int>>();\n  auto gen\n      = fromConst(source)\n      | map([&](const std::pair<const int, int>& p) {\n        return p.second - p.first;\n      });\n  // clang-format on\n  EXPECT_EQ(330, gen | sum);\n}\n\nTEST(Gen, Filter) {\n  const auto expected = vector<int>{1, 2, 4, 5, 7, 8};\n  auto actual =\n      seq(1, 9) | filter([](int x) { return x % 3; }) | as<vector<int>>();\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, FilterDefault) {\n  {\n    // Default filter should remove 0s\n    const auto expected = vector<int>{1, 1, 2, 3};\n    auto actual = from({0, 1, 1, 0, 2, 3, 0}) | filter() | as<vector>();\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    // Default filter should remove nullptrs\n    int a = 5;\n    int b = 3;\n    int c = 0;\n    const auto expected = vector<int*>{&a, &b, &c};\n    // clang-format off\n    auto actual = from({(int*)nullptr, &a, &b, &c, (int*)nullptr})\n      | filter()\n      | as<vector>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    // Default filter on Optionals should remove folly::null\n    const auto expected =\n        vector<Optional<int>>{Optional<int>(5), Optional<int>(0)};\n    // clang-format off\n    const auto actual = from(\n        {Optional<int>(5), Optional<int>(), Optional<int>(0)})\n      | filter()\n      | as<vector>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n}\n\nTEST(Gen, FilterSink) {\n  // clang-format off\n  auto actual = seq(1, 2)\n    | map([](int x) { return vector<int>{x}; })\n    | filter([](vector<int> v) { return !v.empty(); })\n    | as<vector>();\n  // clang-format on\n  EXPECT_FALSE(from(actual) | rconcat | isEmpty);\n}\n\nTEST(Gen, Contains) {\n  {\n    auto gen = seq(1, 9) | map(square);\n    EXPECT_TRUE(gen | contains(49));\n    EXPECT_FALSE(gen | contains(50));\n  }\n  {\n    // infinite, to prove laziness\n    auto gen = seq(1) | map(square) | eachTo<std::string>();\n\n    // std::string gen, const char* needle\n    EXPECT_TRUE(gen | take(9999) | contains(\"49\"));\n  }\n}\n\nTEST(Gen, Take) {\n  {\n    auto expected = vector<int>{1, 4, 9, 16};\n    // clang-format off\n    auto actual =\n      seq(1, 1000)\n      | mapped([](int x) { return x * x; })\n      | take(4)\n      | as<vector<int>>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    auto expected = vector<int>{0, 1, 4, 5, 8};\n    // clang-format off\n    auto actual\n      = ((seq(0) | take(2)) +\n         (seq(4) | take(2)) +\n         (seq(8) | take(2)))\n      | take(5)\n      | as<vector>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    auto expected = vector<int>{0, 1, 4, 5, 8};\n    // clang-format off\n    auto actual\n      = seq(0)\n      | mapped([](int i) {\n          return seq(i * 4) | take(2);\n        })\n      | concat\n      | take(5)\n      | as<vector>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    int64_t limit = 5;\n    take(limit - 5);\n    EXPECT_THROW(take(limit - 6), std::invalid_argument);\n  }\n}\n\nTEST(Gen, Stride) {\n  EXPECT_THROW(stride(0), std::invalid_argument);\n  {\n    auto expected = vector<int>{1, 2, 3, 4};\n    auto actual = seq(1, 4) | stride(1) | as<vector<int>>();\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    auto expected = vector<int>{1, 3, 5, 7};\n    auto actual = seq(1, 8) | stride(2) | as<vector<int>>();\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    auto expected = vector<int>{1, 4, 7, 10};\n    auto actual = seq(1, 12) | stride(3) | as<vector<int>>();\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    auto expected = vector<int>{1, 3, 5, 7, 9, 1, 4, 7, 10};\n    // clang-format off\n    auto actual\n      = ((seq(1, 10) | stride(2)) +\n         (seq(1, 10) | stride(3)))\n      | as<vector<int>>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n  EXPECT_EQ(500, seq(1) | take(1000) | stride(2) | count);\n  EXPECT_EQ(10, seq(1) | take(1000) | stride(2) | take(10) | count);\n}\n\nTEST(Gen, Sample) {\n  std::mt19937 rnd(42);\n\n  auto sampler = seq(1, 100) | sample(50, rnd);\n  std::unordered_map<int, int> hits;\n  const int kNumIters = 80;\n  for (int i = 0; i < kNumIters; i++) {\n    auto vec = sampler | as<vector<int>>();\n    EXPECT_EQ(vec.size(), 50);\n    auto uniq = fromConst(vec) | as<set<int>>();\n    EXPECT_EQ(uniq.size(), vec.size()); // sampling without replacement\n    for (auto v : vec) {\n      ++hits[v];\n    }\n  }\n\n  // In 80 separate samples of our range, we should have seen every value\n  // at least once and no value all 80 times. (The odds of either of those\n  // events is 1/2^80).\n  EXPECT_EQ(hits.size(), 100);\n  for (auto hit : hits) {\n    EXPECT_GT(hit.second, 0);\n    EXPECT_LT(hit.second, kNumIters);\n  }\n\n  auto small = seq(1, 5) | sample(10);\n  EXPECT_EQ((small | sum), 15);\n  EXPECT_EQ((small | take(3) | count), 3);\n}\n\nTEST(Gen, Skip) {\n  auto gen =\n      seq(1, 1000) | mapped([](int x) { return x * x; }) | skip(4) | take(4);\n  EXPECT_EQ((vector<int>{25, 36, 49, 64}), gen | as<vector>());\n}\n\nTEST(Gen, Until) {\n  {\n    auto expected = vector<int>{1, 4, 9, 16};\n    // clang-format off\n    auto actual\n      = seq(1, 1000)\n      | mapped([](int x) { return x * x; })\n      | until([](int x) { return x > 20; })\n      | as<vector<int>>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    auto expected = vector<int>{0, 1, 4, 5, 8};\n    // clang-format off\n    auto actual\n      = ((seq(0) | until([](int i) { return i > 1; })) +\n         (seq(4) | until([](int i) { return i > 5; })) +\n         (seq(8) | until([](int i) { return i > 9; })))\n      | until([](int i) { return i > 8; })\n      | as<vector<int>>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n  /*\n  {\n    auto expected = vector<int>{ 0, 1, 5, 6, 10 };\n    // clang-format off\n    auto actual\n      = seq(0)\n      | mapped([](int i) {\n          return seq(i * 5) | until([=](int j) { return j > i * 5 + 1; });\n        })\n      | concat\n      | until([](int i) { return i > 10; })\n      | as<vector<int>>();\n    // clang-format on\n    EXPECT_EQ(expected, actual);\n  }\n    */\n}\n\nTEST(Gen, Visit) {\n  auto increment = [](int& i) { ++i; };\n  auto clone = map([](int i) { return i; });\n  { // apply()\n    auto expected = 10;\n    auto actual = seq(0) | clone | visit(increment) | take(4) | sum;\n    EXPECT_EQ(expected, actual);\n  }\n  { // foreach()\n    auto expected = 10;\n    auto actual = seq(0, 3) | clone | visit(increment) | sum;\n    EXPECT_EQ(expected, actual);\n  }\n  { // tee-like\n    std::vector<int> x2, x4;\n    std::vector<int> expected2{0, 1, 4, 9};\n    std::vector<int> expected4{0, 1, 16, 81};\n\n    auto tee = [](std::vector<int>& container) {\n      return visit([&](int value) { container.push_back(value); });\n    };\n    EXPECT_EQ(\n        98, seq(0, 3) | map(square) | tee(x2) | map(square) | tee(x4) | sum);\n    EXPECT_EQ(expected2, x2);\n    EXPECT_EQ(expected4, x4);\n  }\n}\n\nTEST(Gen, Composed) {\n  // Operator, Operator\n  // clang-format off\n  auto valuesOf\n    = filter([](Optional<int>& o) { return o.has_value(); })\n    | map([](Optional<int>& o) -> int& { return o.value(); });\n  // clang-format on\n  std::vector<Optional<int>> opts{none, 4, none, 6, none};\n  EXPECT_EQ(4 * 4 + 6 * 6, from(opts) | valuesOf | map(square) | sum);\n  // Operator, Sink\n  auto sumOpt = valuesOf | sum;\n  EXPECT_EQ(10, from(opts) | sumOpt);\n}\n\nTEST(Gen, Chain) {\n  std::vector<int> nums{2, 3, 5, 7};\n  std::map<int, int> mappings{{3, 9}, {5, 25}};\n  auto gen = from(nums) + (from(mappings) | get<1>());\n  EXPECT_EQ(51, gen | sum);\n  EXPECT_EQ(5, gen | take(2) | sum);\n  EXPECT_EQ(26, gen | take(5) | sum);\n}\n\nTEST(Gen, Concat) {\n  std::vector<std::vector<int>> nums{{2, 3}, {5, 7}};\n  auto gen = from(nums) | rconcat;\n  EXPECT_EQ(17, gen | sum);\n  EXPECT_EQ(10, gen | take(3) | sum);\n}\n\nTEST(Gen, ConcatGen) {\n  auto gen = seq(1, 10) | map([](int i) { return seq(1, i); }) | concat;\n  EXPECT_EQ(220, gen | sum);\n  EXPECT_EQ(10, gen | take(6) | sum);\n}\n\nTEST(Gen, ConcatAlt) {\n  std::vector<std::vector<int>> nums{{2, 3}, {5, 7}};\n  // clang-format off\n  auto actual\n    = from(nums)\n    | map([](std::vector<int>& v) { return from(v); })\n    | concat\n    | sum;\n  // clang-format on\n  auto expected = 17;\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, Order) {\n  auto expected = vector<int>{0, 3, 5, 6, 7, 8, 9};\n  auto actual = from({8, 6, 7, 5, 3, 0, 9}) | order | as<vector>();\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, OrderMoved) {\n  auto expected = vector<int>{0, 9, 25, 36, 49, 64, 81};\n  // clang-format off\n  auto actual\n    = from({8, 6, 7, 5, 3, 0, 9})\n    | move\n    | order\n    | map(square)\n    | as<vector>();\n  // clang-format on\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, OrderTake) {\n  auto expected = vector<int>{9, 8, 7};\n  // clang-format off\n  auto actual\n    = from({8, 6, 7, 5, 3, 0, 9})\n    | orderByDescending(square)\n    | take(3)\n    | as<vector>();\n  // clang-format on\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, Distinct) {\n  auto expected = vector<int>{3, 1, 2};\n  auto actual = from({3, 1, 3, 2, 1, 2, 3}) | distinct | as<vector>();\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, DistinctBy) { //  0  1  4  9  6  5  6  9  4  1  0\n  auto expected = vector<int>{0, 1, 2, 3, 4, 5};\n  auto actual =\n      seq(0, 100) | distinctBy([](int i) { return i * i % 10; }) | as<vector>();\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, DistinctMove) { //  0  1  4  9  6  5  6  9  4  1  0\n  auto expected = vector<int>{0, 1, 2, 3, 4, 5};\n  auto actual =\n      seq(0, 100) |\n      mapped([](int i) { return std::make_unique<int>(i); })\n      // see comment below about selector parameters for Distinct\n      | distinctBy([](const std::unique_ptr<int>& pi) {\n          return *pi * *pi % 10;\n        }) |\n      mapped([](std::unique_ptr<int> pi) { return *pi; }) | as<vector>();\n\n  // NOTE(tjackson): the following line intentionally doesn't work:\n  //  | distinctBy([](std::unique_ptr<int> pi) { return *pi * *pi % 10; })\n  // This is because distinctBy because the selector intentionally requires a\n  // const reference.  If it required a move-reference, the value might get\n  // gutted by the selector before said value could be passed to downstream\n  // operators.\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, DistinctInfinite) {\n  // distinct should be able to handle an infinite sequence, provided that, of\n  // of course, is it eventually made finite before returning the result.\n  auto expected = seq(0) | take(5) | as<vector>(); // 0 1 2 3 4\n\n  auto actual = seq(0) // 0 1 2 3 4 5 6 7 ...\n      | mapped([](int i) { return i / 2; }) // 0 0 1 1 2 2 3 3 ...\n      | distinct // 0 1 2 3 4 5 6 7 ...\n      | take(5) // 0 1 2 3 4\n      | as<vector>();\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, DistinctByInfinite) {\n  // Similarly to the DistinctInfinite test case, distinct by should be able to\n  // handle infinite sequences. Note that depending on how many values we take()\n  // at the end, the sequence may infinite loop. This is fine because we cannot\n  // solve the halting problem.\n  auto expected = vector<int>{1, 2};\n  auto actual = seq(1) // 1 2 3 4 5 6 7 8 ...\n      | distinctBy([](int i) { return i % 2; }) // 1 2 (but might by infinite)\n      | take(2) // 1 2\n      | as<vector>();\n  // Note that if we had take(3), this would infinite loop\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, MinBy) {\n  // clang-format off\n  EXPECT_EQ(\n      7,\n      seq(1, 10)\n        | minBy([](int i) -> double {\n            double d = i - 6.8;\n            return d * d;\n          })\n        | unwrap);\n  // clang-format on\n}\n\nTEST(Gen, MaxBy) {\n  auto gen = from({\"three\", \"eleven\", \"four\"});\n\n  EXPECT_EQ(\"eleven\", gen | maxBy(&strlen) | unwrap);\n}\n\nTEST(Gen, Min) {\n  auto odds = seq(2, 10) | filter([](int i) { return i % 2; });\n\n  EXPECT_EQ(3, odds | min);\n}\n\nTEST(Gen, Max) {\n  auto odds = seq(2, 10) | filter([](int i) { return i % 2; });\n\n  EXPECT_EQ(9, odds | max);\n}\n\nTEST(Gen, Append) {\n  string expected = \"facebook\";\n  string actual = \"face\";\n  from(StringPiece(\"book\")) | appendTo(actual);\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, FromRValue) {\n  {\n    // AFAICT The C++ Standard does not specify what happens to the rvalue\n    // reference of a std::vector when it is used as the 'other' for an rvalue\n    // constructor.  Use fbvector because we're sure its size will be zero in\n    // this case.\n    fbvector<int> v({1, 2, 3, 4});\n    auto q1 = from(v);\n    EXPECT_EQ(v.size(), 4); // ensure that the lvalue version was called!\n    auto expected = 1 * 2 * 3 * 4;\n    EXPECT_EQ(expected, q1 | product);\n\n    auto q2 = from(std::move(v));\n    EXPECT_EQ(v.size(), 0); // ensure that rvalue version was called\n    EXPECT_EQ(expected, q2 | product);\n  }\n  {\n    auto expected = 7;\n    auto q = from([] { return vector<int>({3, 7, 5}); }());\n    EXPECT_EQ(expected, q | max);\n  }\n  {\n    for (auto size : {5, 1024, 16384, 1 << 20}) {\n      auto q1 = from(vector<int>(size, 2));\n      auto q2 = from(vector<int>(size, 3));\n      // If the rvalue specialization is broken/gone, then the compiler will\n      // (disgustingly!) just store a *reference* to the temporary object,\n      // which is bad.  Try to catch this by allocating two temporary vectors\n      // of the same size, so that they'll probably use the same underlying\n      // buffer if q1's vector is destructed before q2's vector is constructed.\n      EXPECT_EQ(size * 2 + size * 3, (q1 | sum) + (q2 | sum));\n    }\n  }\n  {\n    auto q = from(set<int>{1, 2, 3, 2, 1});\n    EXPECT_EQ(q | sum, 6);\n  }\n}\n\nTEST(Gen, OrderBy) {\n  auto expected = vector<int>{5, 6, 4, 7, 3, 8, 2, 9, 1, 10};\n  // clang-format off\n  auto actual\n    = seq(1, 10)\n    | orderBy([](int x) { return (5.1 - x) * (5.1 - x); })\n    | as<vector>();\n  // clang-format on\n  EXPECT_EQ(expected, actual);\n\n  expected = seq(1, 10) | as<vector>();\n  // clang-format off\n  actual\n    = from(expected)\n    | map([] (int x) { return 11 - x; })\n    | orderBy()\n    | as<vector>();\n  // clang-format on\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, Foldl) {\n  int expected = 2 * 3 * 4 * 5;\n  auto actual = seq(2, 5) | foldl(1, multiply);\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, Reduce) {\n  int expected = 2 + 3 + 4 + 5;\n  auto actual = seq(2, 5) | reduce(add);\n  EXPECT_EQ(expected, actual | unwrap);\n}\n\nTEST(Gen, ReduceBad) {\n  auto gen = seq(1) | take(0);\n  auto actual = gen | reduce(add);\n  EXPECT_FALSE(actual); // Empty sequences are okay, they just yield 'none'\n}\n\nTEST(Gen, Moves) {\n  std::vector<unique_ptr<int>> ptrs;\n  ptrs.emplace_back(new int(1));\n  EXPECT_NE(ptrs.front().get(), nullptr);\n  auto ptrs2 = from(ptrs) | move | as<vector>();\n  EXPECT_EQ(ptrs.front().get(), nullptr);\n  EXPECT_EQ(**ptrs2.data(), 1);\n}\n\nTEST(Gen, First) {\n  auto gen = seq(0) | filter([](int x) { return x > 3; });\n  EXPECT_EQ(4, gen | first | unwrap);\n}\n\nTEST(Gen, FromCopy) {\n  vector<int> v{3, 5};\n  auto src = from(v);\n  auto copy = fromCopy(v);\n  EXPECT_EQ(8, src | sum);\n  EXPECT_EQ(8, copy | sum);\n  v[1] = 7;\n  EXPECT_EQ(10, src | sum);\n  EXPECT_EQ(8, copy | sum);\n}\n\nTEST(Gen, Get) {\n  std::map<int, int> pairs{\n      {1, 1},\n      {2, 4},\n      {3, 9},\n      {4, 16},\n  };\n  auto pairSrc = from(pairs);\n  auto keys = pairSrc | get<0>();\n  auto values = pairSrc | get<1>();\n  EXPECT_EQ(10, keys | sum);\n  EXPECT_EQ(30, values | sum);\n  EXPECT_EQ(30, keys | map(square) | sum);\n  pairs[5] = 25;\n  EXPECT_EQ(15, keys | sum);\n  EXPECT_EQ(55, values | sum);\n\n  vector<tuple<int, int, int>> tuples{\n      make_tuple(1, 1, 1),\n      make_tuple(2, 4, 8),\n      make_tuple(3, 9, 27),\n  };\n  EXPECT_EQ(36, from(tuples) | get<2>() | sum);\n}\n\nTEST(Gen, notEmpty) {\n  EXPECT_TRUE(seq(0, 1) | notEmpty);\n  EXPECT_TRUE(just(1) | notEmpty);\n  EXPECT_FALSE(gen::range(0, 0) | notEmpty);\n  EXPECT_FALSE(from({1}) | take(0) | notEmpty);\n}\n\nTEST(Gen, isEmpty) {\n  EXPECT_FALSE(seq(0, 1) | isEmpty);\n  EXPECT_FALSE(just(1) | isEmpty);\n  EXPECT_TRUE(gen::range(0, 0) | isEmpty);\n  EXPECT_TRUE(from({1}) | take(0) | isEmpty);\n}\n\nTEST(Gen, Any) {\n  EXPECT_TRUE(seq(0, 10) | any([](int i) { return i == 7; }));\n  EXPECT_FALSE(seq(0, 10) | any([](int i) { return i == 11; }));\n}\n\nTEST(Gen, All) {\n  EXPECT_TRUE(seq(0, 10) | all([](int i) { return i < 11; }));\n  EXPECT_FALSE(seq(0, 10) | all([](int i) { return i < 5; }));\n  EXPECT_FALSE(seq(0) | take(9999) | all([](int i) { return i < 10; }));\n\n  // empty lists satisfies all\n  EXPECT_TRUE(seq(0) | take(0) | all([](int i) { return i < 50; }));\n  EXPECT_TRUE(seq(0) | take(0) | all([](int i) { return i > 50; }));\n}\n\nTEST(Gen, Yielders) {\n  auto gen = GENERATOR(int) {\n    for (int i = 1; i <= 5; ++i) {\n      yield(i);\n    }\n    yield(7);\n    for (int i = 3;; ++i) {\n      yield(i * i);\n    }\n  };\n  vector<int> expected{1, 2, 3, 4, 5, 7, 9, 16, 25};\n  EXPECT_EQ(expected, gen | take(9) | as<vector>());\n}\n\nTEST(Gen, NestedYield) {\n  auto nums = GENERATOR(int) {\n    for (int i = 1;; ++i) {\n      yield(i);\n    }\n  };\n  auto gen = GENERATOR(int) {\n    nums | take(10) | yield;\n    seq(1, 5) | [&](int i) { yield(i); };\n  };\n  EXPECT_EQ(70, gen | sum);\n}\n\nTEST(Gen, MapYielders) {\n  // clang-format off\n  auto gen\n    = seq(1, 5)\n    | map([](int n) {\n        return GENERATOR(int) {\n          int i;\n          for (i = 1; i < n; ++i) {\n            yield(i);\n          }\n          for (; i >= 1; --i) {\n            yield(i);\n          }\n        };\n      })\n    | concat;\n  vector<int> expected {\n                1,\n             1, 2, 1,\n          1, 2, 3, 2, 1,\n       1, 2, 3, 4, 3, 2, 1,\n    1, 2, 3, 4, 5, 4, 3, 2, 1,\n  };\n  // clang-format on\n  EXPECT_EQ(expected, gen | as<vector>());\n}\n\nTEST(Gen, VirtualGen) {\n  VirtualGen<int> v(seq(1, 10));\n  EXPECT_EQ(55, v | sum);\n  v = v | map(square);\n  EXPECT_EQ(385, v | sum);\n  v = v | take(5);\n  EXPECT_EQ(55, v | sum);\n  EXPECT_EQ(30, v | take(4) | sum);\n}\n\nTEST(Gen, VirtualGenMoveOnly) {\n  VirtualGenMoveOnly<int> v(seq(1, 10));\n  EXPECT_EQ(55, std::move(v) | sum);\n  v = seq(1, 10) | virtualize;\n  v = std::move(v) | map(square);\n  EXPECT_EQ(385, std::move(v) | sum);\n}\n\nTEST(Gen, CustomType) {\n  struct Foo {\n    int y;\n  };\n  auto gen = from({Foo{2}, Foo{3}}) | map([](const Foo& f) { return f.y; });\n  EXPECT_EQ(5, gen | sum);\n}\n\nTEST(Gen, NoNeedlessCopies) {\n  auto gen = seq(1, 5) | map([](int x) { return std::make_unique<int>(x); }) |\n      map([](unique_ptr<int> p) { return p; }) |\n      map([](unique_ptr<int>&& p) { return std::move(p); }) |\n      map([](const unique_ptr<int>& p) { return *p; });\n  EXPECT_EQ(15, gen | sum);\n  EXPECT_EQ(6, gen | take(3) | sum);\n}\n\nnamespace {\n\nclass TestIntSeq : public GenImpl<int, TestIntSeq> {\n public:\n  TestIntSeq() {}\n\n  template <class Body>\n  bool apply(Body&& body) const {\n    for (int i = 1; i < 6; ++i) {\n      if (!body(i)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  TestIntSeq(TestIntSeq&&) noexcept = default;\n  TestIntSeq& operator=(TestIntSeq&&) noexcept = default;\n  TestIntSeq(const TestIntSeq&) = delete;\n  TestIntSeq& operator=(const TestIntSeq&) = delete;\n};\n\n} // namespace\n\nTEST(Gen, NoGeneratorCopies) {\n  EXPECT_EQ(15, TestIntSeq() | sum);\n  auto x = TestIntSeq() | take(3);\n  EXPECT_EQ(6, std::move(x) | sum);\n}\n\nTEST(Gen, FromArray) {\n  int source[] = {2, 3, 5, 7};\n  auto gen = from(source);\n  EXPECT_EQ(2 * 3 * 5 * 7, gen | product);\n}\n\nTEST(Gen, FromStdArray) {\n  std::array<int, 4> source{{2, 3, 5, 7}};\n  auto gen = from(source);\n  EXPECT_EQ(2 * 3 * 5 * 7, gen | product);\n}\n\nTEST(Gen, StringConcat) {\n  auto gen = seq(1, 10) | eachTo<string>() | rconcat;\n  EXPECT_EQ(\"12345678910\", gen | as<string>());\n}\n\nstruct CopyCounter {\n  static int alive;\n  int copies;\n  int moves;\n\n  CopyCounter() : copies(0), moves(0) { ++alive; }\n\n  CopyCounter(CopyCounter&& source) noexcept {\n    *this = std::move(source);\n    ++alive;\n  }\n\n  CopyCounter(const CopyCounter& source) {\n    *this = source;\n    ++alive;\n  }\n\n  ~CopyCounter() { --alive; }\n\n  CopyCounter& operator=(const CopyCounter& source) {\n    this->copies = source.copies + 1;\n    this->moves = source.moves;\n    return *this;\n  }\n\n  CopyCounter& operator=(CopyCounter&& source) {\n    this->copies = source.copies;\n    this->moves = source.moves + 1;\n    return *this;\n  }\n};\n\nint CopyCounter::alive = 0;\n\nTEST(Gen, CopyCount) {\n  vector<CopyCounter> originals;\n  originals.emplace_back();\n  EXPECT_EQ(1, originals.size());\n  EXPECT_EQ(0, originals.back().copies);\n\n  vector<CopyCounter> copies = from(originals) | as<vector>();\n  EXPECT_EQ(1, copies.back().copies);\n  EXPECT_EQ(0, copies.back().moves);\n\n  vector<CopyCounter> moves = from(originals) | move | as<vector>();\n  EXPECT_EQ(0, moves.back().copies);\n  EXPECT_EQ(1, moves.back().moves);\n}\n\n// test dynamics with various layers of nested arrays.\nTEST(Gen, Dynamic) {\n  dynamic array1 = dynamic::array(1, 2);\n  EXPECT_EQ(dynamic(3), from(array1) | sum);\n  dynamic array2 = folly::dynamic::array(\n      folly::dynamic::array(1), folly::dynamic::array(1, 2));\n  EXPECT_EQ(dynamic(4), from(array2) | rconcat | sum);\n  dynamic array3 = folly::dynamic::array(\n      folly::dynamic::array(folly::dynamic::array(1)),\n      folly::dynamic::array(\n          folly::dynamic::array(1), folly::dynamic::array(1, 2)));\n  EXPECT_EQ(dynamic(5), from(array3) | rconcat | rconcat | sum);\n}\n\nTEST(Gen, DynamicObject) {\n  const dynamic obj = dynamic::object(1, 2)(3, 4);\n  EXPECT_EQ(dynamic(4), from(obj.keys()) | sum);\n  EXPECT_EQ(dynamic(6), from(obj.values()) | sum);\n  EXPECT_EQ(dynamic(4), from(obj.items()) | get<0>() | sum);\n  EXPECT_EQ(dynamic(6), from(obj.items()) | get<1>() | sum);\n}\n\nTEST(Gen, Collect) {\n  auto s = from({7, 6, 5, 4, 3}) | as<set<int>>();\n  EXPECT_EQ(s.size(), 5);\n}\n\nTEST(Gen, Cycle) {\n  {\n    auto s = from({1, 2});\n    EXPECT_EQ((vector<int>{1, 2, 1, 2, 1}), s | cycle | take(5) | as<vector>());\n  }\n  {\n    auto s = from({1, 2});\n    EXPECT_EQ((vector<int>{1, 2, 1, 2}), s | cycle(2) | as<vector>());\n  }\n  {\n    auto s = from({1, 2, 3});\n    EXPECT_EQ(\n        (vector<int>{1, 2, 1, 2, 1}),\n        s | take(2) | cycle | take(5) | as<vector>());\n  }\n  {\n    auto s = empty<int>();\n    EXPECT_EQ((vector<int>{}), s | cycle | take(4) | as<vector>());\n  }\n  {\n    int c = 3;\n    int* pcount = &c;\n    auto countdown = GENERATOR(int) {\n      ASSERT_GE(*pcount, 0)\n          << \"Cycle should have stopped when it didn't get values!\";\n      for (int i = 1; i <= *pcount; ++i) {\n        yield(i);\n      }\n      --*pcount;\n    };\n    auto s = countdown;\n    EXPECT_EQ(\n        (vector<int>{1, 2, 3, 1, 2, 1}), s | cycle | take(7) | as<vector>());\n    // take necessary as cycle returns an infinite generator\n  }\n}\n\nTEST(Gen, Dereference) {\n  {\n    const int x = 4, y = 2;\n    auto s = from(std::initializer_list<const int*>({&x, nullptr, &y}));\n    EXPECT_EQ(6, s | dereference | sum);\n  }\n  {\n    vector<int> a{1, 2};\n    vector<int> b{3, 4};\n    vector<vector<int>*> pv{&a, nullptr, &b};\n    from(pv) | dereference | [&](vector<int>& v) { v.push_back(5); };\n    EXPECT_EQ(3, a.size());\n    EXPECT_EQ(3, b.size());\n    EXPECT_EQ(5, a.back());\n    EXPECT_EQ(5, b.back());\n  }\n  {\n    vector<std::map<int, int>> maps{\n        {\n            {2, 31},\n            {3, 41},\n        },\n        {\n            {3, 52},\n            {4, 62},\n        },\n        {\n            {4, 73},\n            {5, 83},\n        },\n    };\n    // clang-format off\n    EXPECT_EQ(\n        93,\n        from(maps)\n        | map([](std::map<int, int>& m) {\n            return get_ptr(m, 3);\n          })\n        | dereference\n        | sum);\n    // clang-format on\n  }\n  {\n    vector<unique_ptr<int>> ups;\n    ups.emplace_back(new int(3));\n    ups.emplace_back();\n    ups.emplace_back(new int(7));\n    EXPECT_EQ(10, from(ups) | dereference | sum);\n    EXPECT_EQ(10, from(ups) | move | dereference | sum);\n  }\n}\n\nnamespace {\nstruct DereferenceWrapper {\n  string data;\n  string& operator*() & { return data; }\n  string&& operator*() && { return std::move(data); }\n  explicit operator bool() { return true; }\n};\nbool operator==(const DereferenceWrapper& a, const DereferenceWrapper& b) {\n  return a.data == b.data;\n}\nvoid PrintTo(const DereferenceWrapper& a, std::ostream* o) {\n  *o << \"Wrapper{\\\"\" << cEscape<string>(a.data) << \"\\\"}\";\n}\n} // namespace\n\nTEST(Gen, DereferenceWithLValueRef) {\n  auto original = vector<DereferenceWrapper>{{\"foo\"}, {\"bar\"}};\n  auto copy = original;\n  auto expected = vector<string>{\"foo\", \"bar\"};\n  auto actual = from(original) | dereference | as<vector>();\n  EXPECT_EQ(expected, actual);\n  EXPECT_EQ(copy, original);\n}\n\nTEST(Gen, DereferenceWithRValueRef) {\n  auto original = vector<DereferenceWrapper>{{\"foo\"}, {\"bar\"}};\n  auto empty = vector<DereferenceWrapper>{{}, {}};\n  auto expected = vector<string>{\"foo\", \"bar\"};\n  auto actual = from(original) | move | dereference | as<vector>();\n  EXPECT_EQ(expected, actual);\n  EXPECT_EQ(empty, original);\n}\n\nTEST(Gen, Indirect) {\n  vector<int> vs{1};\n  EXPECT_EQ(&vs[0], from(vs) | indirect | first | unwrap);\n}\n\nTEST(Gen, Guard) {\n  using std::runtime_error;\n  // clang-format off\n  EXPECT_THROW(\n      from({\"1\", \"a\", \"3\"})\n      | eachTo<int>()\n      | sum,\n      runtime_error);\n  EXPECT_EQ(\n      4,\n      from({\"1\", \"a\", \"3\"})\n      | guard<runtime_error>([](runtime_error&, const char*) {\n          return true; // continue\n        })\n      | eachTo<int>()\n      | sum);\n  EXPECT_EQ(\n      4,\n      from({\"1\", \"a\", \"3\", \"99\"})\n      | guard<runtime_error>([](runtime_error&, const char*) {\n          return true; // continue\n        })\n      | eachTo<int>()\n      | take(2) // Ensure take() is respected.\n      | sum);\n  EXPECT_EQ(\n      1,\n      from({\"1\", \"a\", \"3\"})\n      | guard<runtime_error>([](runtime_error&, const char*) {\n          return false; // break\n        })\n      | eachTo<int>()\n      | sum);\n  EXPECT_THROW(\n      from({\"1\", \"a\", \"3\"})\n      | guard<runtime_error>([](runtime_error&, const char* v) {\n          if (v[0] == 'a') {\n            throw;\n          }\n          return true;\n        })\n      | eachTo<int>()\n      | sum,\n      runtime_error);\n  // clang-format on\n}\n\n// Disabled: guard currently can't catch exceptions thrown after a buffering op.\nTEST(Gen, DISABLED_Guardthroughbuffers) {\n  using std::runtime_error;\n  // clang-format off\n  EXPECT_EQ(\n      4,\n      (from({\"1\", \"a\", \"3\"})\n         | guard<runtime_error>([](runtime_error&, const char*) {\n             return true;\n           })\n         | batch(1)\n         | rconcat\n         | eachTo<int>()\n         | sum));\n  // clang-format on\n}\nTEST(Gen, eachTryTo) {\n  // clang-format off\n  EXPECT_EQ(\n      4,\n      from({\"1\", \"a\", \"3\"})\n      | eachTryTo<int>()\n      | dereference\n      | sum);\n  EXPECT_EQ(\n      1,\n      from({\"1\", \"a\", \"3\"})\n      | eachTryTo<int>()\n      | takeWhile()\n      | dereference\n      | sum);\n  // clang-format on\n}\n\nTEST(Gen, Batch) {\n  EXPECT_EQ((vector<vector<int>>{{1}}), seq(1, 1) | batch(5) | as<vector>());\n  EXPECT_EQ(\n      (vector<vector<int>>{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11}}),\n      seq(1, 11) | batch(3) | as<vector>());\n  EXPECT_THROW(seq(1, 1) | batch(0) | as<vector>(), std::invalid_argument);\n}\n\nTEST(Gen, BatchMove) {\n  auto expected = vector<vector<int>>{{0, 1}, {2, 3}, {4}};\n  auto actual =\n      seq(0, 4) | mapped([](int i) { return std::make_unique<int>(i); }) |\n      batch(2) | mapped([](std::vector<std::unique_ptr<int>>& pVector) {\n        std::vector<int> iVector;\n        for (const auto& p : pVector) {\n          iVector.push_back(*p);\n        };\n        return iVector;\n      }) |\n      as<vector>();\n  EXPECT_EQ(expected, actual);\n}\n\nTEST(Gen, Window) {\n  auto expected = seq(0, 10) | as<std::vector>();\n  for (size_t windowSize = 1; windowSize <= 20; ++windowSize) {\n    // no early stop\n    auto actual = seq(0, 10) |\n        mapped([](int i) { return std::make_unique<int>(i); }) | window(4) |\n        dereference | as<std::vector>();\n    EXPECT_EQ(expected, actual) << windowSize;\n  }\n  for (size_t windowSize = 1; windowSize <= 20; ++windowSize) {\n    // pre-window take\n    auto actual = seq(0) |\n        mapped([](int i) { return std::make_unique<int>(i); }) | take(11) |\n        window(4) | dereference | as<std::vector>();\n    EXPECT_EQ(expected, actual) << windowSize;\n  }\n  for (size_t windowSize = 1; windowSize <= 20; ++windowSize) {\n    // post-window take\n    auto actual = seq(0) |\n        mapped([](int i) { return std::make_unique<int>(i); }) | window(4) |\n        take(11) | dereference | as<std::vector>();\n    EXPECT_EQ(expected, actual) << windowSize;\n  }\n}\n\nTEST(Gen, Just) {\n  {\n    int x = 3;\n    auto j = just(x);\n    EXPECT_EQ(&x, j | indirect | first | unwrap);\n    x = 4;\n    EXPECT_EQ(4, j | sum);\n  }\n  {\n    int x = 3;\n    const int& cx = x;\n    auto j = just(cx);\n    EXPECT_EQ(&x, j | indirect | first | unwrap);\n    x = 5;\n    EXPECT_EQ(5, j | sum);\n  }\n  {\n    int x = 3;\n    auto j = just(std::move(x));\n    EXPECT_NE(&x, j | indirect | first | unwrap);\n    x = 5;\n    EXPECT_EQ(3, j | sum);\n  }\n}\n\nTEST(Gen, GroupBy) {\n  vector<string> strs{\n      \"zero\",\n      \"one\",\n      \"two\",\n      \"three\",\n      \"four\",\n      \"five\",\n      \"six\",\n      \"seven\",\n      \"eight\",\n      \"nine\",\n  };\n\n  auto gb = from(strs) | groupBy([](const string& str) { return str.size(); });\n\n  EXPECT_EQ(10, gb | mapOp(count) | sum);\n  EXPECT_EQ(3, gb | count);\n\n  vector<string> mode{\"zero\", \"four\", \"five\", \"nine\"};\n  // clang-format off\n  EXPECT_EQ(\n      mode,\n      gb\n      | maxBy([](const Group<size_t, string>& g) { return g.size(); })\n      | unwrap\n      | as<vector>());\n  // clang-format on\n\n  vector<string> largest{\"three\", \"seven\", \"eight\"};\n  // clang-format off\n  EXPECT_EQ(\n      largest,\n      gb\n      | maxBy([](const Group<size_t, string>& g) { return g.key(); })\n      | unwrap\n      | as<vector>());\n  // clang-format on\n}\n\nTEST(Gen, GroupByAdjacent) {\n  vector<string> finite{\"a\", \"b\", \"cc\", \"dd\", \"ee\", \"fff\", \"g\", \"hhh\"};\n  vector<vector<string>> finiteGroups{\n      {\"a\", \"b\"}, {\"cc\", \"dd\", \"ee\"}, {\"fff\"}, {\"g\"}, {\"hhh\"}};\n  EXPECT_EQ(\n      finiteGroups,\n      from(finite) |\n          groupByAdjacent([](const string& str) { return str.size(); }) |\n          mapOp(as<vector>()) | as<vector>());\n\n  auto infinite = seq(0);\n  vector<vector<int>> infiniteGroups{\n      {0, 1, 2, 3, 4}, {5, 6, 7, 8, 9}, {10, 11, 12, 13, 14}};\n  EXPECT_EQ(\n      infiniteGroups,\n      infinite | groupByAdjacent([](const int& i) { return (i % 10) < 5; }) |\n          take(3) | mapOp(as<vector>()) | as<vector>());\n}\n\nTEST(Gen, Unwrap) {\n  Optional<int> o(4);\n  Optional<int> e;\n  EXPECT_EQ(4, o | unwrap);\n  EXPECT_THROW(e | unwrap, OptionalEmptyException);\n\n  auto oup = folly::make_optional(std::make_unique<int>(5));\n  // optional has a value, and that value is non-null\n  EXPECT_TRUE(bool(oup | unwrap));\n  EXPECT_EQ(5, *(oup | unwrap));\n  EXPECT_TRUE(oup.has_value()); // still has a pointer (null or not)\n  EXPECT_TRUE(bool(oup.value())); // that value isn't null\n\n  auto moved1 = std::move(oup) | unwrapOr(std::make_unique<int>(6));\n  // oup still has a value, but now it's now nullptr since the pointer was moved\n  // into moved1\n  EXPECT_TRUE(oup.has_value());\n  EXPECT_FALSE(oup.value());\n  EXPECT_TRUE(bool(moved1));\n  EXPECT_EQ(5, *moved1);\n\n  auto moved2 = std::move(oup) | unwrapOr(std::make_unique<int>(7));\n  // oup's still-valid nullptr value wins here, the pointer to 7 doesn't apply\n  EXPECT_FALSE(moved2);\n\n  oup.reset();\n  auto moved3 = std::move(oup) | unwrapOr(std::make_unique<int>(8));\n  // oup is empty now, so the unwrapOr comes into play.\n  EXPECT_TRUE(bool(moved3));\n  EXPECT_EQ(8, *moved3);\n\n  {\n    // mixed types, with common type matching optional\n    Optional<double> full(3.3);\n    decltype(full) empty;\n    auto fallback = unwrapOr(4);\n    EXPECT_EQ(3.3, full | fallback);\n    EXPECT_EQ(3.3, std::move(full) | fallback);\n    EXPECT_EQ(3.3, full | std::move(fallback));\n    EXPECT_EQ(3.3, std::move(full) | std::move(fallback));\n    EXPECT_EQ(4.0, empty | fallback);\n    EXPECT_EQ(4.0, std::move(empty) | fallback);\n    EXPECT_EQ(4.0, empty | std::move(fallback));\n    EXPECT_EQ(4.0, std::move(empty) | std::move(fallback));\n  }\n\n  {\n    // mixed types, with common type matching fallback\n    Optional<int> full(3);\n    decltype(full) empty;\n    auto fallback = unwrapOr(5.0); // type: double\n    // if we chose 'int' as the common type, we'd see truncation here\n    EXPECT_EQ(1.5, (full | fallback) / 2);\n    EXPECT_EQ(1.5, (std::move(full) | fallback) / 2);\n    EXPECT_EQ(1.5, (full | std::move(fallback)) / 2);\n    EXPECT_EQ(1.5, (std::move(full) | std::move(fallback)) / 2);\n    EXPECT_EQ(2.5, (empty | fallback) / 2);\n    EXPECT_EQ(2.5, (std::move(empty) | fallback) / 2);\n    EXPECT_EQ(2.5, (empty | std::move(fallback)) / 2);\n    EXPECT_EQ(2.5, (std::move(empty) | std::move(fallback)) / 2);\n  }\n\n  {\n    auto opt = folly::make_optional(std::make_shared<int>(8));\n    auto fallback = unwrapOr(std::make_unique<int>(9));\n    // fallback must be std::move'd to be used\n    EXPECT_EQ(8, *(opt | std::move(fallback)));\n    EXPECT_TRUE(bool(opt.value())); // shared_ptr copied out, not moved\n    EXPECT_TRUE(bool(opt)); // value still present\n    EXPECT_TRUE(bool(fallback.value())); // fallback value not needed\n\n    EXPECT_EQ(8, *(std::move(opt) | std::move(fallback)));\n    EXPECT_FALSE(opt.value()); // shared_ptr moved out\n    EXPECT_TRUE(bool(opt)); // gutted value still present\n    EXPECT_TRUE(bool(fallback.value())); // fallback value not needed\n\n    opt.reset();\n\n    EXPECT_FALSE(opt); // opt is empty now\n    EXPECT_EQ(9, *(std::move(opt) | std::move(fallback)));\n    EXPECT_FALSE(fallback.value()); // fallback moved out!\n  }\n\n  {\n    // test with nullptr\n    vector<int> v{1, 2};\n    EXPECT_EQ(&v[1], from(v) | indirect | max | unwrap);\n    v.clear();\n    EXPECT_FALSE(from(v) | indirect | max | unwrapOr(nullptr));\n  }\n\n  {\n    // mixed type determined by fallback\n    Optional<std::nullptr_t> empty;\n    int x = 3;\n    EXPECT_EQ(&x, empty | unwrapOr(&x));\n  }\n}\n"
  },
  {
    "path": "folly/gen/test/Bench.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Benchmark.h>\n\n#define BENCH_GEN_IMPL(gen, prefix)             \\\n  static bool FB_ANONYMOUS_VARIABLE(benchGen) = \\\n      (::folly::addBenchmark(                   \\\n           __FILE__,                            \\\n           prefix FOLLY_PP_STRINGIZE(gen),      \\\n           [](unsigned iters) {                 \\\n             const unsigned num = iters;        \\\n             while (iters--) {                  \\\n               folly::doNotOptimizeAway(gen);   \\\n             }                                  \\\n             return num;                        \\\n           }),                                  \\\n       true)\n#define BENCH_GEN(gen) BENCH_GEN_IMPL(gen, \"\")\n#define BENCH_GEN_REL(gen) BENCH_GEN_IMPL(gen, \"%\")\n"
  },
  {
    "path": "folly/gen/test/CombineTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/gen/Combine.h>\n\n#include <memory>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/FBVector.h>\n#include <folly/gen/Base.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::gen;\nusing namespace folly;\nusing std::string;\nusing std::tuple;\nusing std::vector;\n\nconst folly::gen::detail::Map<folly::gen::detail::MergeTuples> gTupleFlatten{};\n\nauto even = [](int i) -> bool { return i % 2 == 0; };\nauto odd = [](int i) -> bool { return i % 2 == 1; };\nauto initVectorUniquePtr(int value) {\n  std::vector<std::unique_ptr<int>> v;\n  v.push_back(std::make_unique<int>(value));\n  v.push_back(std::make_unique<int>(value));\n  v.push_back(std::make_unique<int>(value));\n  return v;\n}\n\nTEST(CombineGen, Interleave) {\n  { // large (infinite) base, small container\n    auto base = seq(1) | filter(odd);\n    auto toInterleave = seq(1, 6) | filter(even);\n    auto interleaved = base | interleave(toInterleave | as<vector>());\n    EXPECT_EQ(interleaved | as<vector>(), vector<int>({1, 2, 3, 4, 5, 6}));\n  }\n  { // small base, large container\n    auto base = seq(1) | filter(odd) | take(3);\n    auto toInterleave = seq(1) | filter(even) | take(50);\n    auto interleaved = base | interleave(toInterleave | as<vector>());\n    EXPECT_EQ(interleaved | as<vector>(), vector<int>({1, 2, 3, 4, 5, 6}));\n  }\n}\n\nTEST(CombineGen, InterleaveMoveOnly) {\n  auto base = initVectorUniquePtr(1);\n  auto toInterleave = initVectorUniquePtr(2);\n  const auto interleaved =\n      from(base) | move | interleave(std::move(toInterleave)) | as<vector>();\n  ASSERT_EQ(interleaved.size(), 6);\n  EXPECT_EQ(*interleaved[0], 1);\n  EXPECT_EQ(*interleaved[1], 2);\n  EXPECT_EQ(*interleaved[2], 1);\n  EXPECT_EQ(*interleaved[3], 2);\n  EXPECT_EQ(*interleaved[4], 1);\n  EXPECT_EQ(*interleaved[5], 2);\n}\n\nTEST(CombineGen, Zip) {\n  auto base0 = seq(1);\n  // We rely on std::move(fbvector) emptying the source vector\n  auto zippee = fbvector<string>{\"one\", \"two\", \"three\"};\n  {\n    auto combined = base0 | zip(zippee) | as<vector>();\n    ASSERT_EQ(combined.size(), 3);\n    EXPECT_EQ(std::get<0>(combined[0]), 1);\n    EXPECT_EQ(std::get<1>(combined[0]), \"one\");\n    EXPECT_EQ(std::get<0>(combined[1]), 2);\n    EXPECT_EQ(std::get<1>(combined[1]), \"two\");\n    EXPECT_EQ(std::get<0>(combined[2]), 3);\n    EXPECT_EQ(std::get<1>(combined[2]), \"three\");\n    ASSERT_FALSE(zippee.empty());\n    EXPECT_FALSE(zippee.front().empty()); // shouldn't have been move'd\n  }\n\n  { // same as top, but using std::move.\n    auto combined = base0 | zip(std::move(zippee)) | as<vector>();\n    ASSERT_EQ(combined.size(), 3);\n    EXPECT_EQ(std::get<0>(combined[0]), 1);\n    EXPECT_TRUE(zippee.empty());\n  }\n\n  { // same as top, but base is truncated\n    auto baseFinite = seq(1) | take(1);\n    auto combined =\n        baseFinite | zip(vector<string>{\"one\", \"two\", \"three\"}) | as<vector>();\n    ASSERT_EQ(combined.size(), 1);\n    EXPECT_EQ(std::get<0>(combined[0]), 1);\n    EXPECT_EQ(std::get<1>(combined[0]), \"one\");\n  }\n}\n\nTEST(CombineGen, ZipMoveOnly) {\n  auto base = initVectorUniquePtr(1);\n  auto zippee = initVectorUniquePtr(2);\n  const auto combined =\n      from(base) | move | zip(std::move(zippee)) | as<vector>();\n  ASSERT_EQ(combined.size(), 3);\n  EXPECT_EQ(*std::get<0>(combined[0]), 1);\n  EXPECT_EQ(*std::get<1>(combined[0]), 2);\n  EXPECT_EQ(*std::get<0>(combined[1]), 1);\n  EXPECT_EQ(*std::get<1>(combined[1]), 2);\n  EXPECT_EQ(*std::get<0>(combined[2]), 1);\n  EXPECT_EQ(*std::get<1>(combined[2]), 2);\n}\n\nTEST(CombineGen, TupleFlatten) {\n  vector<tuple<int, string>> intStringTupleVec{\n      tuple<int, string>{1, \"1\"},\n      tuple<int, string>{2, \"2\"},\n      tuple<int, string>{3, \"3\"},\n  };\n\n  vector<tuple<char>> charTupleVec{\n      tuple<char>{'A'},\n      tuple<char>{'B'},\n      tuple<char>{'C'},\n      tuple<char>{'D'},\n  };\n\n  vector<double> doubleVec{\n      1.0,\n      4.0,\n      9.0,\n      16.0,\n      25.0,\n  };\n\n  // clang-format off\n  auto zipped1 = from(intStringTupleVec)\n    | zip(charTupleVec)\n    | assert_type<tuple<tuple<int, string>, tuple<char>>>()\n    | as<vector>();\n  // clang-format on\n  EXPECT_EQ(std::get<0>(zipped1[0]), std::make_tuple(1, \"1\"));\n  EXPECT_EQ(std::get<1>(zipped1[0]), std::make_tuple('A'));\n\n  // clang-format off\n  auto zipped2 = from(zipped1)\n    | gTupleFlatten\n    | assert_type<tuple<int, string, char>&&>()\n    | as<vector>();\n  // clang-format on\n  ASSERT_EQ(zipped2.size(), 3);\n  EXPECT_EQ(zipped2[0], std::make_tuple(1, \"1\", 'A'));\n\n  // clang-format off\n  auto zipped3 = from(charTupleVec)\n    | zip(intStringTupleVec)\n    | gTupleFlatten\n    | assert_type<tuple<char, int, string>&&>()\n    | as<vector>();\n  // clang-format on\n  ASSERT_EQ(zipped3.size(), 3);\n  EXPECT_EQ(zipped3[0], std::make_tuple('A', 1, \"1\"));\n\n  // clang-format off\n  auto zipped4 = from(intStringTupleVec)\n    | zip(doubleVec)\n    | gTupleFlatten\n    | assert_type<tuple<int, string, double>&&>()\n    | as<vector>();\n  // clang-format on\n  ASSERT_EQ(zipped4.size(), 3);\n  EXPECT_EQ(zipped4[0], std::make_tuple(1, \"1\", 1.0));\n\n  // clang-format off\n  auto zipped5 = from(doubleVec)\n    | zip(doubleVec)\n    | assert_type<tuple<double, double>>()\n    | gTupleFlatten  // essentially a no-op\n    | assert_type<tuple<double, double>&&>()\n    | as<vector>();\n  // clang-format on\n  ASSERT_EQ(zipped5.size(), 5);\n  EXPECT_EQ(zipped5[0], std::make_tuple(1.0, 1.0));\n\n  // clang-format off\n  auto zipped6 = from(intStringTupleVec)\n    | zip(charTupleVec)\n    | gTupleFlatten\n    | zip(doubleVec)\n    | gTupleFlatten\n    | assert_type<tuple<int, string, char, double>&&>()\n    | as<vector>();\n  // clang-format on\n  ASSERT_EQ(zipped6.size(), 3);\n  EXPECT_EQ(zipped6[0], std::make_tuple(1, \"1\", 'A', 1.0));\n}\n"
  },
  {
    "path": "folly/gen/test/FileBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/File.h>\n#include <folly/gen/Base.h>\n#include <folly/gen/File.h>\n\nusing namespace folly::gen;\n\nBENCHMARK(ByLine_Pipes, iters) {\n  std::thread thread;\n  int rfd = -1;\n  int wfd;\n  BENCHMARK_SUSPEND {\n    int p[2];\n    CHECK_ERR(folly::fileops::pipe(p));\n    rfd = p[0];\n    wfd = p[1];\n    thread = std::thread([wfd, iters] {\n      char x = 'x';\n      PCHECK(folly::fileops::write(wfd, &x, 1) == 1); // signal startup\n      FILE* f = fdopen(wfd, \"w\");\n      PCHECK(f);\n      for (size_t i = 1; i <= iters; ++i) {\n        fprintf(f, \"%zu\\n\", i);\n      }\n      fclose(f);\n    });\n    char buf;\n    PCHECK(folly::fileops::read(rfd, &buf, 1) == 1); // wait for startup\n  }\n\n  CHECK_ERR(rfd);\n  auto s = byLine(folly::File(rfd)) | eachTo<int64_t>() | sum;\n  folly::doNotOptimizeAway(s);\n\n  BENCHMARK_SUSPEND {\n    folly::fileops::close(rfd);\n    CHECK_EQ(s, int64_t(iters) * (iters + 1) / 2);\n    thread.join();\n  }\n}\n\n// Results from an Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz\n// ============================================================================\n// folly/gen/test/FileBenchmark.cpp                relative  time/iter  iters/s\n// ============================================================================\n// ByLine_Pipes                                               148.63ns    6.73M\n// ============================================================================\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/gen/test/FileTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/File.h>\n\n#include <string>\n#include <vector>\n\n#include <folly/Range.h>\n#include <folly/container/Array.h>\n#include <folly/gen/Base.h>\n#include <folly/gen/File.h>\n#include <folly/portability/GTest.h>\n#include <folly/testing/TestUtil.h>\n\nusing namespace folly::gen;\nusing namespace folly;\nusing std::vector;\n\nTEST(FileGen, ByLine) {\n  auto collect = eachTo<std::string>() | as<vector>();\n  const std::string cases[] = {\n      \"Hello world\\n\"\n      \"This is the second line\\n\"\n      \"\\n\"\n      \"\\n\"\n      \"a few empty lines above\\n\"\n      \"incomplete last line\",\n\n      \"complete last line\\n\",\n\n      \"\\n\",\n\n      \"\",\n  };\n\n  for (auto& lines : cases) {\n    test::TemporaryFile file(\"ByLine\");\n    EXPECT_EQ(\n        lines.size(), fileops::write(file.fd(), lines.data(), lines.size()));\n\n    auto expected = from({lines}) | resplit('\\n') | collect;\n    auto found = byLine(file.path().string().c_str()) | collect;\n\n    EXPECT_EQ(expected, found) << \"For Input: '\" << lines << \"'\";\n  }\n}\n\nTEST(FileGen, ByLineFull) {\n  auto cases = std::vector<std::string>{\n      stripLeftMargin(R\"(\n         Hello world\n         This is the second line\n\n\n         a few empty lines above\n         incomplete last line)\"),\n\n      \"complete last line\\n\",\n\n      \"\\n\",\n\n      \"\",\n  };\n\n  for (auto& lines : cases) {\n    test::TemporaryFile file(\"ByLineFull\");\n    EXPECT_EQ(\n        lines.size(), fileops::write(file.fd(), lines.data(), lines.size()));\n\n    auto found =\n        byLineFull(file.path().string().c_str()) | unsplit<std::string>(\"\");\n\n    EXPECT_EQ(lines, found);\n  }\n}\n\nclass FileGenBufferedTest : public ::testing::TestWithParam<int> {};\n\nTEST_P(FileGenBufferedTest, FileWriter) {\n  size_t bufferSize = GetParam();\n  test::TemporaryFile file(\"FileWriter\");\n\n  static const std::string lines(\n      \"Hello world\\n\"\n      \"This is the second line\\n\"\n      \"\\n\"\n      \"\\n\"\n      \"a few empty lines above\\n\");\n\n  auto src = from({lines, lines, lines, lines, lines, lines, lines, lines});\n  auto collect = eachTo<std::string>() | as<vector>();\n  auto expected = src | resplit('\\n') | collect;\n\n  src | eachAs<StringPiece>() | toFile(File(file.fd()), bufferSize);\n  auto found = byLine(file.path().string().c_str()) | collect;\n\n  EXPECT_TRUE(expected == found);\n}\n\nTEST(FileGenBufferedTest, FileWriterSimple) {\n  test::TemporaryFile file(\"FileWriter\");\n  auto toLine = [](int v) { return to<std::string>(v, '\\n'); };\n\n  auto squares = seq(1, 100) | map([](int x) { return x * x; });\n  squares | map(toLine) | eachAs<StringPiece>() | toFile(File(file.fd()));\n  EXPECT_EQ(\n      squares | sum,\n      byLine(File(file.path().string().c_str())) | eachTo<int>() | sum);\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    DifferentBufferSizes,\n    FileGenBufferedTest,\n    ::testing::Values(0, 1, 2, 4, 8, 64, 4096));\n"
  },
  {
    "path": "folly/gen/test/IStreamTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/gen/IStream.h>\n\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <folly/gen/Base.h>\n#include <folly/gen/String.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace gen {\n\n// Test mixed consumption of std::istream with and without folly::gen, and\n// demonstrates partial consumption of input.\nTEST(IStream, ByLine) {\n  std::stringstream in(\n      \"2\\n\" // Count applies to both below groups.\n      \"1\\tred\\n\"\n      \"3\\tblue\\n\"\n      \"3.4\\t5.6\\t7.8\\n\"\n      \"1.1\\t2.2\\t3.3\\n\");\n  size_t n;\n  in >> n;\n  in.get(); // Consume trailing newline\n  auto colors = byLine(in) | take(n) | eachToPair<int, std::string>('\\t') |\n      as<std::vector>();\n  auto coords = byLine(in) | take(n) | eachToTuple<float, float, float>('\\t') |\n      as<std::vector>();\n  EXPECT_EQ(\n      colors,\n      (std::vector<std::pair<int, std::string>>{\n          {1, \"red\"},\n          {3, \"blue\"},\n      }));\n  EXPECT_EQ(\n      coords,\n      (std::vector<std::tuple<float, float, float>>({\n          std::tuple<float, float, float>{3.4f, 5.6f, 7.8f},\n          std::tuple<float, float, float>{1.1f, 2.2f, 3.3f},\n      })));\n}\n\n} // namespace gen\n} // namespace folly\n"
  },
  {
    "path": "folly/gen/test/ParallelBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <future>\n#include <iostream>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/gen/Base.h>\n#include <folly/gen/Parallel.h>\n#include <folly/gen/test/Bench.h>\n\nDEFINE_int32(\n    threads,\n    std::max(1, (int32_t)sysconf(_SC_NPROCESSORS_CONF) / 2),\n    \"Num threads.\");\n\nusing namespace folly::gen;\nusing std::vector;\n\nconstexpr int kFib = 28; // unit of work\nsize_t fib(int n) {\n  return n <= 1 ? 1 : fib(n - 1) + fib(n - 2);\n}\n\nnamespace {\nstatic auto isPrimeSlow = [](int n) {\n  if (n < 2) {\n    return false;\n  } else if (n > 2) {\n    for (int d = 3; d * d <= n; d += 2) {\n      if (0 == n % d) {\n        return false;\n      }\n    }\n  }\n  return true;\n};\n\nstatic auto primes = seq(1, 1 << 20) | filter(isPrimeSlow) | as<vector>();\n\nstatic auto stopc(int n) {\n  return [=](int d) { return d * d > n; };\n}\nstatic auto divides(int n) {\n  return [=](int d) { return 0 == n % d; };\n}\n\nstatic auto isPrime = [](int n) {\n  return from(primes) | until(stopc(n)) | filter(divides(n)) | isEmpty;\n};\n\nstatic auto factors = [](int n) {\n  return from(primes) | until(stopc(n)) | filter(divides(n)) | count;\n};\n\nstatic auto factorsSlow = [](int n) {\n  return from(primes) | filter(divides(n)) | count;\n};\n\nstatic auto sleepyWork = [](int i) {\n  const auto sleepyTime = std::chrono::microseconds(100);\n  std::this_thread::sleep_for(sleepyTime);\n  return i;\n};\n\nstatic auto sleepAndWork = [](int i) { return factorsSlow(i) + sleepyWork(i); };\n\nauto start = 1 << 20;\nauto v = seq(start) | take(1 << 20) | as<vector>();\nauto small = from(v) | take(1 << 12);\nauto medium = from(v) | take(1 << 14);\nauto large = from(v) | take(1 << 18);\nauto huge = from(v);\nauto chunks = chunked(v);\n} // namespace\n\nBENCH_GEN(small | map(factorsSlow) | sum);\nBENCH_GEN_REL(small | parallel(map(factorsSlow)) | sum);\nBENCHMARK_DRAW_LINE();\n\nBENCH_GEN(small | map(factors) | sum);\nBENCH_GEN_REL(small | parallel(map(factors)) | sum);\nBENCHMARK_DRAW_LINE();\n\nBENCH_GEN(large | map(factors) | sum);\nBENCH_GEN_REL(large | parallel(map(factors)) | sum);\nBENCHMARK_DRAW_LINE();\n\nauto ch = chunks;\nauto cat = concat;\nBENCH_GEN(huge | filter(isPrime) | count);\nBENCH_GEN_REL(ch | cat | filter(isPrime) | count);\nBENCH_GEN_REL(ch | parallel(cat | filter(isPrime)) | count);\nBENCH_GEN_REL(ch | parallel(cat | filter(isPrime) | sub(count)) | sum);\nBENCHMARK_DRAW_LINE();\n\nBENCH_GEN(small | map(sleepAndWork) | sum);\nBENCH_GEN_REL(small | parallel(map(sleepAndWork)) | sum);\nBENCHMARK_DRAW_LINE();\n\nconst int fibs = 1000;\nBENCH_GEN(seq(1, fibs) | map([](int) { return fib(kFib); }) | sum);\n// clang-format off\nBENCH_GEN_REL(\n    seq(1, fibs)\n      | parallel(map([](int) { return fib(kFib); }) | sub(sum))\n      | sum);\n// clang-format on\nBENCH_GEN_REL([] {\n  // clang-format off\n  auto threads = seq(1, int(FLAGS_threads))\n      | map([](int i) {\n        return std::thread([=] {\n          return range(\n              (i + 0) * fibs / FLAGS_threads, (i + 1) * fibs / FLAGS_threads)\n              | map([](int) { return fib(kFib); })\n              | sum;\n        });\n      })\n      | as<vector>();\n  from(threads) | [](std::thread &thread) { thread.join(); };\n  // clang-format on\n  return 1;\n}());\nBENCHMARK_DRAW_LINE();\n\n#if 0\n============================================================================\nfolly/gen/test/ParallelBenchmark.cpp            relative  time/iter  iters/s\n============================================================================\nsmall | map(factorsSlow) | sum                                4.59s  217.87m\nsmall | parallel(map(factorsSlow)) | sum        1588.86%   288.88ms     3.46\n----------------------------------------------------------------------------\nsmall | map(factors) | sum                                   9.62ms   103.94\nsmall | parallel(map(factors)) | sum              89.15%    10.79ms    92.66\n----------------------------------------------------------------------------\nlarge | map(factors) | sum                                 650.52ms     1.54\nlarge | parallel(map(factors)) | sum              53.82%      1.21s  827.41m\n----------------------------------------------------------------------------\nhuge | filter(isPrime) | count                             295.93ms     3.38\nch | cat | filter(isPrime) | count                99.76%   296.64ms     3.37\nch | parallel(cat | filter(isPrime)) | count     142.75%   207.31ms     4.82\nch | parallel(cat | filter(isPrime) | sub(count 1538.50%    19.24ms    51.99\n----------------------------------------------------------------------------\nsmall | map(sleepAndWork) | sum                               5.37s  186.18m\nsmall | parallel(map(sleepAndWork)) | sum       1840.38%   291.85ms     3.43\n----------------------------------------------------------------------------\nseq(1, fibs) | map([](int) { return fib(kFib);                1.49s  669.53m\nseq(1, fibs) | parallel(map([](int) { return fi 1698.07%    87.96ms    11.37\n[] { auto threads = seq(1, int(FLAGS_threads))  1571.16%    95.06ms    10.52\n----------------------------------------------------------------------------\n============================================================================\n#endif\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/gen/test/ParallelMapBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <atomic>\n#include <thread>\n#include <vector>\n\n#include <folly/Benchmark.h>\n#include <folly/gen/Base.h>\n#include <folly/gen/ParallelMap.h>\n#include <folly/portability/Unistd.h>\n\nusing namespace folly::gen;\n\nDEFINE_int32(\n    threads,\n    std::max(1, (int32_t)sysconf(_SC_NPROCESSORS_CONF) / 2),\n    \"Num threads.\");\n\nconstexpr int kFib = 35; // unit of work\nsize_t fib(int n) {\n  return n <= 1 ? 1 : fib(n - 1) + fib(n - 2);\n}\n\nBENCHMARK(FibSumMap, n) {\n  auto result = seq(1, (int)n) | map([](int) { return fib(kFib); }) | sum;\n  folly::doNotOptimizeAway(result);\n}\n\nBENCHMARK_RELATIVE(FibSumPmap, n) {\n  // Schedule more work: enough so that each worker thread does the\n  // same amount as one FibSumMap.\n  const size_t kNumThreads = FLAGS_threads;\n  // clang-format off\n  auto result =\n    seq(1, (int)(n * kNumThreads))\n    | pmap([](int) { return fib(kFib); }, kNumThreads)\n    | sum;\n  // clang-format on\n  folly::doNotOptimizeAway(result);\n}\n\nBENCHMARK_RELATIVE(FibSumThreads, n) {\n  // Schedule kNumThreads to execute the same code as FibSumMap.\n  const size_t kNumThreads = FLAGS_threads;\n  std::vector<std::thread> workers;\n  workers.reserve(kNumThreads);\n  auto fn = [n] {\n    auto result = seq(1, (int)n) | map([](int) { return fib(kFib); }) | sum;\n    folly::doNotOptimizeAway(result);\n  };\n  for (size_t i = 0; i < kNumThreads; i++) {\n    workers.emplace_back(fn);\n  }\n  for (auto& w : workers) {\n    w.join();\n  }\n}\n\n/*\n  ============================================================================\n  folly/gen/test/ParallelMapBenchmark.cpp         relative  time/iter  iters/s\n  ============================================================================\n  FibSumMap                                                   41.64ms    24.02\n  FibSumPmap                                        98.38%    42.32ms    23.63\n  FibSumThreads                                     94.48%    44.07ms    22.69\n  ============================================================================\n\n  real0m15.595s\n  user2m47.100s\n  sys0m0.016s\n*/\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/gen/test/ParallelMapTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/gen/ParallelMap.h>\n\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/Memory.h>\n#include <folly/gen/Base.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::gen;\n\nTEST(Pmap, InfiniteEquivalent) {\n  // apply\n  {\n    // clang-format off\n    auto mapResult\n      = seq(1)\n      | map([](int x) { return x * x; })\n      | until([](int x) { return x > 1000 * 1000; })\n      | as<std::vector<int>>();\n\n    auto pmapResult\n      = seq(1)\n      | pmap([](int x) { return x * x; }, 4)\n      | until([](int x) { return x > 1000 * 1000; })\n      | as<std::vector<int>>();\n    // clang-format on\n\n    EXPECT_EQ(pmapResult, mapResult);\n  }\n\n  // foreach\n  {\n    // clang-format off\n    auto mapResult\n      = seq(1, 10)\n      | map([](int x) { return x * x; })\n      | as<std::vector<int>>();\n\n    auto pmapResult\n      = seq(1, 10)\n      | pmap([](int x) { return x * x; }, 4)\n      | as<std::vector<int>>();\n    // clang-format on\n\n    EXPECT_EQ(pmapResult, mapResult);\n  }\n}\n\nTEST(Pmap, Empty) {\n  // apply\n  {\n    // clang-format off\n    auto mapResult\n      = seq(1)\n      | map([](int x) { return x * x; })\n      | until([](int) { return true; })\n      | as<std::vector<int>>();\n\n    auto pmapResult\n      = seq(1)\n      | pmap([](int x) { return x * x; }, 4)\n      | until([](int) { return true; })\n      | as<std::vector<int>>();\n    // clang-format on\n\n    EXPECT_EQ(mapResult.size(), 0);\n    EXPECT_EQ(pmapResult, mapResult);\n  }\n\n  // foreach\n  {\n    // clang-format off\n    auto mapResult\n      = empty<int>()\n      | map([](int x) { return x * x; })\n      | as<std::vector<int>>();\n\n    auto pmapResult\n      = empty<int>()\n      | pmap([](int x) { return x * x; }, 4)\n      | as<std::vector<int>>();\n    // clang-format on\n\n    EXPECT_EQ(mapResult.size(), 0);\n    EXPECT_EQ(pmapResult, mapResult);\n  }\n}\n\nTEST(Pmap, Rvalues) {\n  // apply\n  {\n    // clang-format off\n    auto mapResult\n        = seq(1)\n        | map([](int x) { return std::make_unique<int>(x); })\n        | map([](std::unique_ptr<int> x) {\n            return std::make_unique<int>(*x * *x); })\n        | map([](std::unique_ptr<int> x) { return *x; })\n        | take(1000)\n        | sum;\n\n    auto pmapResult\n        = seq(1)\n        | pmap([](int x) { return std::make_unique<int>(x); })\n        | pmap([](std::unique_ptr<int> x) {\n            return std::make_unique<int>(*x * *x); })\n        | pmap([](std::unique_ptr<int> x) { return *x; })\n        | take(1000)\n        | sum;\n    // clang-format on\n\n    EXPECT_EQ(pmapResult, mapResult);\n  }\n\n  // foreach\n  {\n    // clang-format off\n    auto mapResult\n        = seq(1, 1000)\n        | map([](int x) { return std::make_unique<int>(x); })\n        | map([](std::unique_ptr<int> x) {\n            return std::make_unique<int>(*x * *x); })\n        | map([](std::unique_ptr<int> x) { return *x; })\n        | sum;\n\n    auto pmapResult\n        = seq(1, 1000)\n        | pmap([](int x) { return std::make_unique<int>(x); })\n        | pmap([](std::unique_ptr<int> x) {\n            return std::make_unique<int>(*x * *x); })\n        | pmap([](std::unique_ptr<int> x) { return *x; })\n        | sum;\n    // clang-format on\n\n    EXPECT_EQ(pmapResult, mapResult);\n  }\n}\n\nTEST(Pmap, Exception) {\n  // Exception from source\n  EXPECT_THROW(\n      just(\"a\") | eachTo<int>() | pmap(To<float>()) | sum, std::runtime_error);\n\n  // Exception from predicate\n  EXPECT_THROW(just(\"b\") | pmap(To<int>()) | sum, std::runtime_error);\n\n  // Exception from downstream\n  EXPECT_THROW(\n      just(\"c\") | pmap(To<std::string>()) | eachTo<int>() | sum,\n      std::runtime_error);\n}\n"
  },
  {
    "path": "folly/gen/test/ParallelTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/gen/Parallel.h>\n\n#include <iostream>\n#include <memory>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/gen/Base.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::gen;\nusing std::vector;\n\nconst auto square = [](int i) { return i * i; };\nconst auto even = [](int i) { return 0 == i % 2; };\nstatic auto sleepyWork = [](int i) {\n  const auto sleepyTime = std::chrono::microseconds(100);\n  std::this_thread::sleep_for(sleepyTime);\n  return i;\n};\n\nstatic auto isPrime = [](int n) {\n  if (n < 2) {\n    return false;\n  } else if (n > 2) {\n    for (int d = 3; d * d <= n; d += 2) {\n      if (0 == n % d) {\n        return false;\n      }\n    }\n  }\n  return true;\n};\n\nstruct {\n  template <class T>\n  std::unique_ptr<T> operator()(T t) const {\n    return std::make_unique<T>(std::move(t));\n  }\n} makeUnique;\n\nstatic auto primes = seq(1, 1 << 14) | filter(isPrime) | as<vector<size_t>>();\n\nstatic auto primeFactors = [](int n) {\n  return from(primes) | filter([&](int d) { return 0 == n % d; }) | count;\n};\n\nTEST(ParallelTest, Serial) {\n  EXPECT_EQ(\n      seq(1, 10) | map(square) | filter(even) | sum,\n      seq(1, 10) | parallel(map(square) | filter(even)) | sum);\n}\n\nauto heavyWork = map(primeFactors);\n\nTEST(ParallelTest, ComputeBound64) {\n  int length = 1 << 10;\n  EXPECT_EQ(\n      seq<size_t>(1, length) | heavyWork | sum,\n      seq<size_t>(1, length) | parallel(heavyWork) | sum);\n}\n\nTEST(ParallelTest, Take) {\n  int length = 1 << 18;\n  int limit = 1 << 14;\n  EXPECT_EQ(\n      seq(1, length) | take(limit) | count,\n      seq(1, length) | parallel(heavyWork) | take(limit) | count);\n}\n\nTEST(ParallelTest, Unique) {\n  auto uniqued = from(primes) | map(makeUnique) | as<vector>();\n  EXPECT_EQ(\n      primes.size(),\n      from(primes) | parallel(map(makeUnique)) |\n          parallel(dereference | map(makeUnique)) | dereference | count);\n  EXPECT_EQ(\n      2,\n      from(primes) | parallel(map(makeUnique)) |\n          parallel(dereference | map(makeUnique)) | dereference | take(2) |\n          count);\n}\n\nTEST(ParallelTest, PSum) {\n  EXPECT_EQ(\n      from(primes) | map(sleepyWork) | sum,\n      from(primes) | parallel(map(sleepyWork) | sub(sum)) | sum);\n}\n"
  },
  {
    "path": "folly/gen/test/StringBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/String.h>\n#include <folly/container/Foreach.h>\n#include <folly/gen/Base.h>\n#include <folly/gen/String.h>\n\nusing namespace folly;\nusing namespace folly::gen;\nusing std::vector;\n\nnamespace {\n\nstatic std::atomic<int> testSize(1000);\nstatic vector<fbstring> testStrVector =\n    seq(1, testSize.load()) | eachTo<fbstring>() | as<vector>();\nstatic auto testFileContent = from(testStrVector) | unsplit('\\n');\n\nconst char* const kLine = \"The quick brown fox jumped over the lazy dog.\\n\";\nconst size_t kLineCount = 10000;\nstd::string bigLines;\nconst size_t kSmallLineSize = 17;\nstd::vector<std::string> smallLines;\n\nvoid initStringResplitterBenchmark() {\n  bigLines.reserve(kLineCount * strlen(kLine));\n  for (size_t i = 0; i < kLineCount; ++i) {\n    bigLines += kLine;\n  }\n  size_t remaining = bigLines.size();\n  size_t pos = 0;\n  while (remaining) {\n    size_t n = std::min(kSmallLineSize, remaining);\n    smallLines.push_back(bigLines.substr(pos, n));\n    pos += n;\n    remaining -= n;\n  }\n}\n\nsize_t len(folly::StringPiece s) {\n  return s.size();\n}\n\n} // namespace\n\nBENCHMARK(StringResplitter_Big, iters) {\n  size_t s = 0;\n  while (iters--) {\n    s += from({bigLines}) | resplit('\\n') | map(&len) | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringResplitter_Small, iters) {\n  size_t s = 0;\n  while (iters--) {\n    s += from(smallLines) | resplit('\\n') | map(&len) | sum;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(StringSplit_Old, iters) {\n  size_t s = 0;\n  std::string line(kLine);\n  while (iters--) {\n    std::vector<StringPiece> parts;\n    split(' ', line, parts);\n    s += parts.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringSplit_Gen_Vector, iters) {\n  size_t s = 0;\n  StringPiece line(kLine);\n  while (iters--) {\n    s += (split(line, ' ') | as<vector>()).size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(StringSplit_Old_ReuseVector, iters) {\n  size_t s = 0;\n  std::string line(kLine);\n  std::vector<StringPiece> parts;\n  while (iters--) {\n    parts.clear();\n    split(' ', line, parts);\n    s += parts.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringSplit_Gen_ReuseVector, iters) {\n  size_t s = 0;\n  StringPiece line(kLine);\n  std::vector<StringPiece> parts;\n  while (iters--) {\n    parts.clear();\n    split(line, ' ') | appendTo(parts);\n    s += parts.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringSplit_Gen, iters) {\n  size_t s = 0;\n  StringPiece line(kLine);\n  while (iters--) {\n    s += split(line, ' ') | count;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringSplit_Gen_Take, iters) {\n  size_t s = 0;\n  StringPiece line(kLine);\n  while (iters--) {\n    s += split(line, ' ') | take(10) | count;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(StringUnsplit_Old, iters) {\n  size_t s = 0;\n  while (iters--) {\n    fbstring joined;\n    join(',', testStrVector, joined);\n    s += joined.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringUnsplit_Old_ReusedBuffer, iters) {\n  size_t s = 0;\n  fbstring joined;\n  while (iters--) {\n    joined.clear();\n    join(',', testStrVector, joined);\n    s += joined.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringUnsplit_Gen, iters) {\n  size_t s = 0;\n  while (iters--) {\n    fbstring joined = from(testStrVector) | unsplit(',');\n    s += joined.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(StringUnsplit_Gen_ReusedBuffer, iters) {\n  size_t s = 0;\n  fbstring buffer;\n  while (iters--) {\n    buffer.clear();\n    from(testStrVector) | unsplit(',', &buffer);\n    s += buffer.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_DRAW_LINE();\n\nvoid StringUnsplit_Gen(size_t iters, size_t joinSize) {\n  std::vector<fbstring> v;\n  BENCHMARK_SUSPEND {\n    FOR_EACH_RANGE (i, 0, joinSize) {\n      v.push_back(to<fbstring>(rand()));\n    }\n  }\n  size_t s = 0;\n  fbstring buffer;\n  while (iters--) {\n    buffer.clear();\n    from(v) | unsplit(',', &buffer);\n    s += buffer.size();\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_PARAM(StringUnsplit_Gen, 1000)\nBENCHMARK_RELATIVE_PARAM(StringUnsplit_Gen, 2000)\nBENCHMARK_RELATIVE_PARAM(StringUnsplit_Gen, 4000)\nBENCHMARK_RELATIVE_PARAM(StringUnsplit_Gen, 8000)\n\nBENCHMARK_DRAW_LINE();\nvoid Lines_Gen(size_t iters, int joinSize) {\n  size_t s = 0;\n  StringPiece content = testFileContent;\n  for (size_t i = 0; i < iters; ++i) {\n    s += lines(content.subpiece(0, joinSize)) | take(100) | count;\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_PARAM(Lines_Gen, 1e3)\nBENCHMARK_RELATIVE_PARAM(Lines_Gen, 2e3)\nBENCHMARK_RELATIVE_PARAM(Lines_Gen, 3e3)\n\nBENCHMARK_DRAW_LINE();\n\n// clang-format off\nfbstring records = seq<size_t>(1, 1000)\n    | mapped([](size_t i) {\n      return folly::to<fbstring>(i, ' ', i * i, ' ', i * i * i);\n    })\n    | unsplit('\\n');\n// clang-format o\n\nBENCHMARK(Records_EachToTuple, iters) {\n  size_t s = 0;\n  for (size_t i = 0; i < iters; i += 1000) {\n    // clang-format off\n    s += split(records, '\\n')\n        | eachToTuple<int, size_t, StringPiece>(' ')\n        | get<1>()\n        | sum;\n    // clang-format on\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Records_VectorStringPieceReused, iters) {\n  size_t s = 0;\n  std::vector<StringPiece> fields;\n  for (size_t i = 0; i < iters; i += 1000) {\n    // clang-format off\n    s += split(records, '\\n')\n        | mapped([&](StringPiece line) {\n          fields.clear();\n          folly::split(' ', line, fields);\n          CHECK(fields.size() == 3);\n          return std::make_tuple(\n              folly::to<int>(fields[0]),\n              folly::to<size_t>(fields[1]),\n              StringPiece(fields[2]));\n        })\n        | get<1>()\n        | sum;\n    // clang-format on\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Records_VectorStringPiece, iters) {\n  size_t s = 0;\n  for (size_t i = 0; i < iters; i += 1000) {\n    // clang-format off\n    s += split(records, '\\n')\n        | mapped([](StringPiece line) {\n          std::vector<StringPiece> fields;\n          folly::split(' ', line, fields);\n          CHECK(fields.size() == 3);\n          return std::make_tuple(\n              folly::to<int>(fields[0]),\n              folly::to<size_t>(fields[1]),\n              StringPiece(fields[2]));\n        })\n        | get<1>()\n        | sum;\n    // clang-format on\n  }\n  folly::doNotOptimizeAway(s);\n}\n\nBENCHMARK_RELATIVE(Records_VectorString, iters) {\n  size_t s = 0;\n  for (size_t i = 0; i < iters; i += 1000) {\n    // clang-format off\n    s += split(records, '\\n')\n        | mapped([](StringPiece line) {\n          std::vector<std::string> fields;\n          folly::split(' ', line, fields);\n          CHECK(fields.size() == 3);\n          return std::make_tuple(\n              folly::to<int>(fields[0]),\n              folly::to<size_t>(fields[1]),\n              StringPiece(fields[2]));\n        })\n        | get<1>()\n        | sum;\n    // clang-format on\n  }\n  folly::doNotOptimizeAway(s);\n}\n\n// Results from an Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz\n// ============================================================================\n// folly/gen/test/StringBenchmark.cpp              relative  time/iter  iters/s\n// ============================================================================\n// StringResplitter_Big                                       108.58us    9.21K\n// StringResplitter_Small                            10.60%     1.02ms   976.48\n// ----------------------------------------------------------------------------\n// StringSplit_Old                                            357.82ns    2.79M\n// StringSplit_Gen_Vector                           105.10%   340.46ns    2.94M\n// ----------------------------------------------------------------------------\n// StringSplit_Old_ReuseVector                                 96.45ns   10.37M\n// StringSplit_Gen_ReuseVector                      124.01%    77.78ns   12.86M\n// StringSplit_Gen                                  140.10%    68.85ns   14.52M\n// StringSplit_Gen_Take                             122.97%    78.44ns   12.75M\n// ----------------------------------------------------------------------------\n// StringUnsplit_Old                                           42.99us   23.26K\n// StringUnsplit_Old_ReusedBuffer                   100.48%    42.79us   23.37K\n// StringUnsplit_Gen                                 96.37%    44.61us   22.42K\n// StringUnsplit_Gen_ReusedBuffer                   116.96%    36.76us   27.20K\n// ----------------------------------------------------------------------------\n// StringUnsplit_Gen(1000)                                     44.71us   22.37K\n// StringUnsplit_Gen(2000)                           49.28%    90.72us   11.02K\n// StringUnsplit_Gen(4000)                           24.05%   185.91us    5.38K\n// StringUnsplit_Gen(8000)                           12.23%   365.42us    2.74K\n// ----------------------------------------------------------------------------\n// Records_EachToTuple                                        101.43us    9.86K\n// Records_VectorStringPieceReused                   93.72%   108.22us    9.24K\n// Records_VectorStringPiece                         37.14%   273.11us    3.66K\n// Records_VectorString                              16.70%   607.47us    1.65K\n// ============================================================================\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  initStringResplitterBenchmark();\n  runBenchmarks();\n  return 0;\n}\n"
  },
  {
    "path": "folly/gen/test/StringTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/gen/String.h>\n\n#include <iosfwd>\n#include <map>\n#include <vector>\n\n#include <folly/functional/ApplyTuple.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::gen;\nusing namespace folly;\nusing std::make_tuple;\nusing std::string;\nusing std::tuple;\nusing std::vector;\n\nusing vec = vector<string>;\n\nstatic auto collect = eachTo<std::string>() | as<vector>();\n\nTEST(StringGen, EmptySplit) {\n  {\n    auto input = \"\";\n    auto expected = vec{};\n    EXPECT_EQ(expected, split(input, ',') | collect);\n  }\n\n  // The last delimiter is eaten, just like std::getline\n  {\n    auto input = \",\";\n    auto expected = vec{\"\"};\n    EXPECT_EQ(expected, split(input, ',') | collect);\n  }\n\n  {\n    auto input = \",,\";\n    auto expected = vec{\"\", \"\"};\n    EXPECT_EQ(expected, split(input, ',') | collect);\n  }\n\n  {\n    auto input = \",,\";\n    auto expected = vec{\"\"};\n    EXPECT_EQ(expected, split(input, ',') | take(1) | collect);\n  }\n}\n\nTEST(StringGen, Split) {\n  {\n    auto input = \"hello,, world, goodbye, meow\";\n    auto expected = vec{\"hello\", \"\", \" world\", \" goodbye\", \" meow\"};\n    EXPECT_EQ(expected, split(input, ',') | collect);\n  }\n\n  {\n    auto input = \"hello,, world, goodbye, meow\";\n    auto expected = vec{\"hello\", \"\", \" world\"};\n    EXPECT_EQ(expected, split(input, ',') | take(3) | collect);\n  }\n\n  {\n    auto input = \"hello,, world, goodbye, meow\";\n    auto expected = vec{\"hello\", \"\", \" world\", \" goodbye\", \" meow\"};\n    EXPECT_EQ(expected, split(input, \",\") | take(5) | collect);\n  }\n\n  {\n    auto input = \"hello,, world, goodbye, meow\";\n    auto expected = vec{\"hello,\", \"world\", \"goodbye\", \"meow\"};\n    EXPECT_EQ(expected, split(input, \", \") | collect);\n  }\n}\n\nTEST(StringGen, SplitByNewLine) {\n  {\n    auto input = \"hello\\n\\n world\\r\\n goodbye\\r me\\n\\row\";\n    auto expected = vec{\"hello\", \"\", \" world\", \" goodbye\", \" me\", \"\", \"ow\"};\n    EXPECT_EQ(expected, lines(input) | collect);\n  }\n}\n\nTEST(StringGen, EmptyResplit) {\n  {\n    auto input = vec{\"\"};\n    auto expected = vec{};\n    EXPECT_EQ(expected, from(input) | resplit(',') | collect);\n  }\n\n  // The last delimiter is eaten, just like std::getline\n  {\n    auto input = vec{\",\"};\n    auto expected = vec{\"\"};\n    EXPECT_EQ(expected, from(input) | resplit(',') | collect);\n  }\n\n  {\n    auto input = vec{\",,\"};\n    auto expected = vec{\"\", \"\"};\n    EXPECT_EQ(expected, from(input) | resplit(',') | collect);\n  }\n}\n\nTEST(StringGen, Resplit) {\n  {\n    auto input = vec{\"hello,, world, goodbye, meow\"};\n    auto expected = vec{\"hello\", \"\", \" world\", \" goodbye\", \" meow\"};\n    EXPECT_EQ(expected, from(input) | resplit(',') | collect);\n  }\n\n  {\n    auto input = vec{\"hel\", \"lo,\", \", world\", \", goodbye, m\", \"eow\"};\n    auto expected = vec{\"hello\", \"\", \" world\", \" goodbye\", \" meow\"};\n    EXPECT_EQ(expected, from(input) | resplit(',') | collect);\n  }\n}\n\nTEST(StringGen, ResplitKeepDelimiter) {\n  {\n    auto input = vec{\"hello,, world, goodbye, meow\"};\n    auto expected = vec{\"hello,\", \",\", \" world,\", \" goodbye,\", \" meow\"};\n    EXPECT_EQ(expected, from(input) | resplit(',', true) | collect);\n  }\n\n  {\n    auto input = vec{\"hel\", \"lo,\", \", world\", \", goodbye, m\", \"eow\"};\n    auto expected = vec{\"hello,\", \",\", \" world,\", \" goodbye,\", \" meow\"};\n    EXPECT_EQ(expected, from(input) | resplit(',', true) | collect);\n  }\n}\n\nTEST(StringGen, EachToTuple) {\n  {\n    auto lines = \"2:1.414:yo 3:1.732:hi\";\n    // clang-format off\n    auto actual\n      = split(lines, ' ')\n      | eachToTuple<int, double, std::string>(':')\n      | as<vector>();\n    // clang-format on\n    vector<tuple<int, double, std::string>> expected{\n        make_tuple(2, 1.414, \"yo\"),\n        make_tuple(3, 1.732, \"hi\"),\n    };\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    auto lines = \"2 3\";\n    // clang-format off\n    auto actual\n      = split(lines, ' ')\n      | eachToTuple<int>(',')\n      | as<vector>();\n    // clang-format on\n    vector<tuple<int>> expected{\n        make_tuple(2),\n        make_tuple(3),\n    };\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    // StringPiece target\n    auto lines = \"1:cat 2:dog\";\n    // clang-format off\n    auto actual\n      = split(lines, ' ')\n      | eachToTuple<int, StringPiece>(':')\n      | as<vector>();\n    // clang-format on\n    vector<tuple<int, StringPiece>> expected{\n        make_tuple(1, \"cat\"),\n        make_tuple(2, \"dog\"),\n    };\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    // StringPiece target\n    auto lines = \"1::cat 2::dog\";\n    // clang-format off\n    auto actual\n      = split(lines, ' ')\n      | eachToTuple<int, StringPiece>(\"::\")\n      | as<vector>();\n    // clang-format on\n    vector<tuple<int, StringPiece>> expected{\n        make_tuple(1, \"cat\"),\n        make_tuple(2, \"dog\"),\n    };\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    // Empty field\n    auto lines = \"2:tjackson:4 3::5\";\n    // clang-format off\n    auto actual\n      = split(lines, ' ')\n      | eachToTuple<int, fbstring, int>(':')\n      | as<vector>();\n    // clang-format on\n    vector<tuple<int, fbstring, int>> expected{\n        make_tuple(2, \"tjackson\", 4),\n        make_tuple(3, \"\", 5),\n    };\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    // Excess fields\n    auto lines = \"1:2 3:4:5\";\n    // clang-format off\n    EXPECT_THROW(\n        (split(lines, ' ')\n          | eachToTuple<int, int>(':')\n          | as<vector>()),\n        std::runtime_error);\n    // clang-format on\n  }\n  {\n    // Missing fields\n    auto lines = \"1:2:3 4:5\";\n    // clang-format off\n    EXPECT_THROW(\n        (split(lines, ' ')\n          | eachToTuple<int, int, int>(':')\n          | as<vector>()),\n        std::runtime_error);\n    // clang-format on\n  }\n}\n\nTEST(StringGen, EachToPair) {\n  {\n    // char delimiters\n    auto lines = \"2:1.414 3:1.732\";\n    // clang-format off\n    auto actual\n      = split(lines, ' ')\n      | eachToPair<int, double>(':')\n      | as<std::map<int, double>>();\n    // clang-format on\n    std::map<int, double> expected{\n        {3, 1.732},\n        {2, 1.414},\n    };\n    EXPECT_EQ(expected, actual);\n  }\n  {\n    // string delimiters\n    auto lines = \"ab=>cd ef=>gh\";\n    // clang-format off\n    auto actual\n      = split(lines, ' ')\n      | eachToPair<string, string>(\"=>\")\n      | as<std::map<string, string>>();\n    // clang-format on\n    std::map<string, string> expected{\n        {\"ab\", \"cd\"},\n        {\"ef\", \"gh\"},\n    };\n    EXPECT_EQ(expected, actual);\n  }\n}\n\nvoid checkResplitMaxLength(\n    vector<string> ins, char delim, uint64_t maxLength, vector<string> outs) {\n  vector<std::string> pieces;\n  auto splitter = streamSplitter(\n      delim,\n      [&pieces](StringPiece s) {\n        pieces.emplace_back(s.begin(), s.end());\n        return true;\n      },\n      maxLength);\n  for (const auto& in : ins) {\n    splitter(in);\n  }\n  splitter.flush();\n\n  EXPECT_EQ(outs.size(), pieces.size());\n  for (size_t i = 0; i < outs.size(); ++i) {\n    EXPECT_EQ(outs[i], pieces[i]);\n  }\n\n  // Also check the concatenated input against the same output\n  if (ins.size() > 1) {\n    checkResplitMaxLength({folly::join(\"\", ins)}, delim, maxLength, outs);\n  }\n}\n\nTEST(StringGen, ResplitMaxLength) {\n  // clang-format off\n  checkResplitMaxLength(\n      {\"hel\", \"lo,\", \", world\", \", goodbye, m\", \"ew\"}, ',', 5,\n      {\"hello\", \",\", \",\", \" worl\", \"d,\", \" good\", \"bye,\", \" mew\"});\n  // \" meow\" cannot be \"end of stream\", since it's maxLength long\n  checkResplitMaxLength(\n      {\"hel\", \"lo,\", \", world\", \", goodbye, m\", \"eow\"}, ',', 5,\n      {\"hello\", \",\", \",\", \" worl\", \"d,\", \" good\", \"bye,\", \" meow\", \"\"});\n  checkResplitMaxLength(\n      {\"||\", \"\", \"\", \"\", \"|a|b\", \"cdefghijklmn\", \"|opqrst\",\n       \"uvwx|y|||\", \"z\", \"0123456789\", \"|\", \"\"}, '|', 2,\n      {\"|\", \"|\", \"|\", \"a|\", \"bc\", \"de\", \"fg\", \"hi\", \"jk\", \"lm\", \"n|\", \"op\",\n       \"qr\", \"st\", \"uv\", \"wx\", \"|\", \"y|\", \"|\", \"|\", \"z0\", \"12\", \"34\", \"56\",\n       \"78\", \"9|\", \"\"});\n  // clang-format on\n}\n\ntemplate <typename F>\nvoid runUnsplitSuite(F fn) {\n  fn(\"hello, world\");\n  fn(\"hello,world,goodbye\");\n  fn(\" \");\n  fn(\"\");\n  fn(\", \");\n  fn(\", a, b,c\");\n}\n\nTEST(StringGen, Unsplit) {\n  auto basicFn = [](StringPiece s) {\n    EXPECT_EQ(split(s, ',') | unsplit(','), s);\n  };\n\n  auto existingBuffer = [](StringPiece s) {\n    folly::fbstring buffer(\"asdf\");\n    split(s, ',') | unsplit(',', &buffer);\n    auto expected = folly::to<folly::fbstring>(\"asdf\", s.empty() ? \"\" : \",\", s);\n    EXPECT_EQ(expected, buffer);\n  };\n\n  auto emptyBuffer = [](StringPiece s) {\n    std::string buffer;\n    split(s, ',') | unsplit(',', &buffer);\n    EXPECT_EQ(s, buffer);\n  };\n\n  auto stringDelim = [](StringPiece s) {\n    EXPECT_EQ(s, split(s, ',') | unsplit(\",\"));\n    std::string buffer;\n    split(s, ',') | unsplit(\",\", &buffer);\n    EXPECT_EQ(buffer, s);\n  };\n\n  runUnsplitSuite(basicFn);\n  runUnsplitSuite(existingBuffer);\n  runUnsplitSuite(emptyBuffer);\n  runUnsplitSuite(stringDelim);\n  EXPECT_EQ(\"1, 2, 3\", seq(1, 3) | unsplit(\", \"));\n}\n\nTEST(StringGen, Batch) {\n  std::vector<std::string> chunks{\n      \"on\", \"e\\nt\", \"w\", \"o\", \"\\nthr\", \"ee\\nfo\", \"ur\\n\"};\n  std::vector<std::string> lines{\"one\", \"two\", \"three\", \"four\"};\n  EXPECT_EQ(4, from(chunks) | resplit('\\n') | count);\n  EXPECT_EQ(4, from(chunks) | resplit('\\n') | batch(2) | rconcat | count);\n  EXPECT_EQ(4, from(chunks) | resplit('\\n') | batch(3) | rconcat | count);\n  // clang-format off\n  EXPECT_EQ(\n      lines,\n      from(chunks)\n        | resplit('\\n')\n        | eachTo<std::string>()\n        | batch(3)\n        | rconcat\n        | as<vector>());\n  // clang-format on\n}\n\nTEST(StringGen, UncurryTuple) {\n  folly::StringPiece file = \"1\\t2\\t3\\n1\\t4\\t9\";\n  auto rows = split(file, '\\n') | eachToTuple<int, int, int>('\\t');\n  auto productSum =\n      rows | map(uncurry([](int x, int y, int z) { return x * y * z; })) | sum;\n  EXPECT_EQ(42, productSum);\n}\n\nTEST(StringGen, UncurryPair) {\n  folly::StringPiece file = \"2\\t3\\n4\\t9\";\n  auto rows = split(file, '\\n') | eachToPair<int, int>('\\t');\n  auto productSum =\n      rows | map(uncurry([](int x, int y) { return x * y; })) | sum;\n  EXPECT_EQ(42, productSum);\n}\n"
  },
  {
    "path": "folly/hash/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"@fbsource//xplat/folly:defs.bzl\", \"folly_xplat_cxx_library\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat build rules\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"checksum\",\n    srcs = [\n        \"Checksum.cpp\",\n    ],\n    compiler_flags = [\n        \"-fno-omit-frame-pointer\",\n    ],\n    raw_headers = [\n        \"Checksum.h\",\n    ],\n    exported_deps = [\n        \"//third-party/boost:boost\",\n        \"//xplat/folly:cpu_id\",\n        \"//xplat/folly/detail:traponavx512\",\n        \"//xplat/folly/external/fast-crc32:avx512_crc32c_v8s3x4\",\n        \"//xplat/folly/external/fast-crc32:neon_crc32c_v3s4x2e_v2\",\n        \"//xplat/folly/external/fast-crc32:neon_eor3_crc32_v8s2x4e_s1x2\",\n        \"//xplat/folly/external/fast-crc32:neon_eor3_crc32c_v8s2x4e_s2x1\",\n        \"//xplat/folly/external/fast-crc32:sse_crc32c_v8s3x3\",\n        \"//xplat/folly/external/nvidia/hash:checksum\",  # @manual\n        \"//xplat/folly/hash/detail:checksum_detail\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"farm_hash\",\n    headers = [\"FarmHash.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/external/farmhash:farmhash\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"hash\",\n    headers = [\"Hash.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":fnv_hash\",\n        \":hsieh_hash\",\n        \":murmur_hash\",\n        \":spooky_hash_v1\",\n        \":spooky_hash_v2\",\n        \"//folly:c_portability\",\n        \"//folly:portability\",\n        \"//folly:traits\",\n        \"//folly:utility\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:cstring_view\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"murmur_hash\",\n    headers = [\"MurmurHash.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly/lang:bits\",\n        \"//folly/portability:constexpr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"rapidhash\",\n    headers = [\"rapidhash.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/external/rapidhash:rapidhash\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"spooky_hash_v1\",\n    srcs = [\"SpookyHashV1.cpp\"],\n    headers = [\"SpookyHashV1.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"spooky_hash_v2\",\n    srcs = [\"SpookyHashV2.cpp\"],\n    headers = [\"SpookyHashV2.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:portability\",\n        \"//folly/lang:c_string\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"traits\",\n    headers = [\"traits.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:traits\",\n    ],\n)\n\n# fbcode build rules\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"checksum\",\n    srcs = [\"Checksum.cpp\"],\n    headers = [\"Checksum.h\"],\n    deps = [\n        \"//folly:cpu_id\",\n        \"//folly/detail:traponavx512\",\n        \"//folly/external/fast-crc32:avx512_crc32c_v8s3x4\",  # @manual\n        \"//folly/external/fast-crc32:neon_crc32c_v3s4x2e_v2\",  # @manual\n        \"//folly/external/fast-crc32:neon_eor3_crc32_v8s2x4e_s1x2\",  # @manual\n        \"//folly/external/fast-crc32:neon_eor3_crc32c_v8s2x4e_s2x1\",  # @manual\n        \"//folly/external/fast-crc32:sse_crc32c_v8s3x3\",  # @manual\n        \"//folly/hash/detail:checksum_detail\",\n    ],\n    external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fnv_hash\",\n    headers = [\n        \"FnvHash.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"hsieh_hash\",\n    headers = [\n        \"HsiehHash.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/lang:bits\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"unique_hash_key\",\n    srcs = [\"UniqueHashKey.cpp\"],\n    headers = [\"UniqueHashKey.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly/portability:openssl\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/blake3:blake3\",  # @manual\n        \"fbsource//third-party/xxHash:xxhash\",  # @manual\n        \"//folly/container:span\",\n    ],\n)\n"
  },
  {
    "path": "folly/hash/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# NOTE: This file is manually maintained, not auto-generated.\n# Update this file when the corresponding BUCK file changes.\n\nfolly_add_library(\n  NAME checksum\n  SRCS\n    Checksum.cpp\n  HEADERS\n    Checksum.h\n  DEPS\n    folly_cpu_id\n    folly_detail_traponavx512\n    folly_external_fast-crc32_avx512_crc32c_v8s3x4\n    folly_external_fast-crc32_neon_crc32c_v3s4x2e_v2\n    folly_external_fast-crc32_neon_eor3_crc32_v8s2x4e_s1x2\n    folly_external_fast-crc32_neon_eor3_crc32c_v8s2x4e_s2x1\n    folly_external_fast-crc32_sse_crc32c_v8s3x3\n    folly_hash_detail_checksum_detail\n)\n\n# Add fast-crc32 dependencies - all built on all platforms (source has stubs)\ntarget_link_libraries(folly_hash_checksum_obj PRIVATE\n  folly_external_fast-crc32_avx512_crc32c_v8s3x4_obj\n  folly_external_fast-crc32_sse_crc32c_v8s3x3_obj\n  folly_external_fast-crc32_neon_crc32c_v3s4x2e_v2_obj\n  folly_external_fast-crc32_neon_eor3_crc32_v8s2x4e_s1x2_obj\n  folly_external_fast-crc32_neon_eor3_crc32c_v8s2x4e_s2x1_obj)\n\nfolly_add_library(\n  NAME farm_hash\n  HEADERS\n    FarmHash.h\n  EXPORTED_DEPS\n    folly_external_farmhash_farmhash\n)\n\nfolly_add_library(\n  NAME hash\n  HEADERS\n    Hash.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_hash_fnv_hash\n    folly_hash_hsieh_hash\n    folly_hash_murmur_hash\n    folly_hash_spooky_hash_v1\n    folly_hash_spooky_hash_v2\n    folly_lang_bits\n    folly_lang_cstring_view\n    folly_portability\n    folly_traits\n    folly_utility\n)\n\nfolly_add_library(\n  NAME rapidhash\n  HEADERS\n    rapidhash.h\n  EXPORTED_DEPS\n    folly_external_rapidhash_rapidhash\n)\n\nfolly_add_library(\n  NAME spooky_hash_v1\n  SRCS\n    SpookyHashV1.cpp\n  HEADERS\n    SpookyHashV1.h\n)\n\nfolly_add_library(\n  NAME spooky_hash_v2\n  SRCS\n    SpookyHashV2.cpp\n  HEADERS\n    SpookyHashV2.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_lang_c_string\n    folly_portability\n)\n\nfolly_add_library(\n  NAME traits\n  HEADERS\n    traits.h\n  EXPORTED_DEPS\n    folly_traits\n)\n\nfolly_add_library(\n  NAME murmur_hash\n  HEADERS\n    MurmurHash.h\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_lang_bits\n    folly_portability_constexpr\n)\n\nfolly_add_library(\n  NAME fnv_hash\n  HEADERS\n    FnvHash.h\n)\n\nfolly_add_library(\n  NAME hsieh_hash\n  HEADERS\n    HsiehHash.h\n  EXPORTED_DEPS\n    folly_lang_bits\n)\n\nfolly_add_library(\n  NAME unique_hash_key\n  SRCS\n    UniqueHashKey.cpp\n  HEADERS\n    UniqueHashKey.h\n  DEPS\n    folly_portability_openssl\n    folly_portability_unistd\n  EXPORTED_DEPS\n    folly_container_span\n)\n\nadd_subdirectory(detail)\n"
  },
  {
    "path": "folly/hash/Checksum.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/Checksum.h>\n\n#include <algorithm>\n#include <stdexcept> // IWYU pragma: keep\n\n#include <boost/crc.hpp>\n\n#include <folly/CpuId.h>\n#include <folly/detail/TrapOnAvx512.h>\n#include <folly/external/fast-crc32/avx512_crc32c_v8s3x4.h> // @manual\n#include <folly/external/fast-crc32/neon_crc32c_v3s4x2e_v2.h> // @manual\n#include <folly/external/fast-crc32/neon_eor3_crc32_v8s2x4e_s1x2.h> // @manual\n#include <folly/external/fast-crc32/neon_eor3_crc32c_v8s2x4e_s2x1.h> // @manual\n#include <folly/external/fast-crc32/sse_crc32c_v8s3x3.h> // @manual\n#include <folly/hash/detail/ChecksumDetail.h>\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n#include <emmintrin.h>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\nuint32_t crc32c_sw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum);\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n\nuint32_t crc32_sw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum);\n\n// Fast SIMD implementation of CRC-32 for x86 with pclmul\nuint32_t crc32_hw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {\n  uint32_t sum = startingChecksum;\n  size_t offset = 0;\n\n  // Process unaligned bytes\n  if ((uintptr_t)data & 15) {\n    size_t limit = std::min(nbytes, -(uintptr_t)data & 15);\n    sum = crc32_sw(data, limit, sum);\n    offset += limit;\n    nbytes -= limit;\n  }\n\n  if (nbytes >= 16) {\n    sum = crc32_hw_aligned(sum, (const __m128i*)(data + offset), nbytes / 16);\n    offset += nbytes & ~15;\n    nbytes &= 15;\n  }\n\n  // Remaining unaligned bytes\n  if (nbytes == 0) {\n    return sum;\n  }\n  return crc32_sw(data + offset, nbytes, sum);\n}\n\nbool crc32c_hw_supported() {\n  return crc32c_hw_supported_sse42();\n}\n\nbool crc32c_hw_supported_sse42() {\n  static folly::CpuId id;\n  return id.sse42();\n}\n\nbool crc32c_hw_supported_avx512() {\n  static folly::CpuId id;\n  static bool supported = id.avx512vl() && !detail::hasTrapOnAvx512();\n  return supported;\n}\n\nbool crc32_hw_supported() {\n  static folly::CpuId id;\n  return id.sse42();\n}\n\nbool crc32c_hw_supported_neon() {\n  return false;\n}\n\nbool crc32c_hw_supported_neon_eor3_sha3() {\n  return false;\n}\n\nbool crc32_hw_supported_neon_eor3_sha3() {\n  return false;\n}\n\n#elif FOLLY_ARM_FEATURE_CRC32\n\n// crc32_hw is defined in folly/external/nvidia/hash/Checksum.cpp\n\nbool crc32c_hw_supported() {\n  return true;\n}\n\nbool crc32c_hw_supported_sse42() {\n  return false;\n}\n\nbool crc32c_hw_supported_avx512() {\n  return false;\n}\n\nbool crc32c_hw_supported_neon() {\n  static bool has_neon = has_neon_crc32c_v3s4x2e_v2();\n  return has_neon;\n}\n\nbool crc32_hw_supported_neon_eor3_sha3() {\n  static bool has_neon_eor3 = has_neon_eor3_crc32_v8s2x4e_s1x2();\n  return has_neon_eor3;\n}\n\nbool crc32c_hw_supported_neon_eor3_sha3() {\n  static bool has_neon_eor3 = has_neon_eor3_crc32c_v8s2x4e_s2x1();\n  return has_neon_eor3;\n}\n\nbool crc32_hw_supported() {\n  return true;\n}\n\n#else // FOLLY_ARM_FEATURE_CRC32\n\nuint32_t crc32_hw(\n    const uint8_t* /* data */,\n    size_t /* nbytes */,\n    uint32_t /* startingChecksum */) {\n  throw std::runtime_error(\"crc32_hw is not implemented on this platform\");\n}\n\nbool crc32c_hw_supported() {\n  return false;\n}\n\nbool crc32c_hw_supported_sse42() {\n  return false;\n}\n\nbool crc32c_hw_supported_avx512() {\n  return false;\n}\n\nbool crc32_hw_supported() {\n  return false;\n}\n\nbool crc32c_hw_supported_neon() {\n  return false;\n}\n\nbool crc32_hw_supported_neon_eor3_sha3() {\n  return false;\n}\n\nbool crc32c_hw_supported_neon_eor3_sha3() {\n  return false;\n}\n#endif\n\ntemplate <uint32_t CRC_POLYNOMIAL>\nuint32_t crc_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {\n  // Reverse the bits in the starting checksum so they'll be in the\n  // right internal format for Boost's CRC engine.\n  //     O(1)-time, branchless bit reversal algorithm from\n  //     http://graphics.stanford.edu/~seander/bithacks.html\n  startingChecksum = ((startingChecksum >> 1) & 0x55555555) |\n      ((startingChecksum & 0x55555555) << 1);\n  startingChecksum = ((startingChecksum >> 2) & 0x33333333) |\n      ((startingChecksum & 0x33333333) << 2);\n  startingChecksum = ((startingChecksum >> 4) & 0x0f0f0f0f) |\n      ((startingChecksum & 0x0f0f0f0f) << 4);\n  startingChecksum = ((startingChecksum >> 8) & 0x00ff00ff) |\n      ((startingChecksum & 0x00ff00ff) << 8);\n  startingChecksum = (startingChecksum >> 16) | (startingChecksum << 16);\n\n  boost::crc_optimal<32, CRC_POLYNOMIAL, ~0U, 0, true, true> sum(\n      startingChecksum);\n  sum.process_bytes(data, nbytes);\n  return sum.checksum();\n}\n\nuint32_t crc32c_sw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {\n  constexpr uint32_t CRC32C_POLYNOMIAL = 0x1EDC6F41;\n  return crc_sw<CRC32C_POLYNOMIAL>(data, nbytes, startingChecksum);\n}\n\nuint32_t crc32_sw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {\n  constexpr uint32_t CRC32_POLYNOMIAL = 0x04C11DB7;\n  return crc_sw<CRC32_POLYNOMIAL>(data, nbytes, startingChecksum);\n}\n\n} // namespace detail\n\nuint32_t crc32c(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {\n#if defined(FOLLY_ENABLE_AVX512_CRC32C_V8S3X4)\n  if (detail::crc32c_hw_supported_avx512() && nbytes > 4096) {\n    return detail::avx512_crc32c_v8s3x4(data, nbytes, startingChecksum);\n  }\n#endif\n\n#if FOLLY_AARCH64\n  if (detail::crc32c_hw_supported_neon_eor3_sha3()) {\n    if (nbytes < 1536) {\n      return detail::neon_eor3_crc32c_small(data, nbytes, startingChecksum);\n    } else {\n      return detail::neon_eor3_crc32c_v8s2x4e_s2x1(\n          data, nbytes, startingChecksum);\n    }\n  }\n\n  if (nbytes >= 4096 && detail::crc32c_hw_supported_neon()) {\n    return detail::neon_crc32c_v3s4x2e_v2(data, nbytes, startingChecksum);\n  }\n#endif\n\n  if (detail::crc32c_hw_supported()) {\n#if defined(FOLLY_ENABLE_SSE42_CRC32C_V8S3X3)\n    if (nbytes > 4096) {\n      return detail::sse_crc32c_v8s3x3(data, nbytes, startingChecksum);\n    }\n#endif\n    return detail::crc32c_hw(data, nbytes, startingChecksum);\n  } else {\n    return detail::crc32c_sw(data, nbytes, startingChecksum);\n  }\n}\n\nuint32_t crc32(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {\n#if FOLLY_AARCH64\n  if (detail::crc32_hw_supported_neon_eor3_sha3()) {\n    if (nbytes < 1536) {\n      return detail::neon_eor3_crc32_small(data, nbytes, startingChecksum);\n    } else {\n      return detail::neon_eor3_crc32_v8s2x4e_s1x2(\n          data, nbytes, startingChecksum);\n    }\n  }\n#endif\n\n  if (detail::crc32_hw_supported()) {\n    return detail::crc32_hw(data, nbytes, startingChecksum);\n  } else {\n    return detail::crc32_sw(data, nbytes, startingChecksum);\n  }\n}\n\nuint32_t crc32_type(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {\n  return ~crc32(data, nbytes, startingChecksum);\n}\n\nuint32_t crc32_combine(uint32_t crc1, uint32_t crc2, size_t crc2len) {\n  // Append up to 32 bits of zeroes in the normal way\n  uint8_t data[4] = {0, 0, 0, 0};\n  auto len = crc2len & 3;\n  if (len) {\n    crc1 = crc32(data, len, crc1);\n  }\n\n  if (detail::crc32_hw_supported()) {\n    return detail::crc32_combine_hw(crc1, crc2, crc2len);\n  } else {\n    return detail::crc32_combine_sw(crc1, crc2, crc2len);\n  }\n}\n\nuint32_t crc32c_combine(uint32_t crc1, uint32_t crc2, size_t crc2len) {\n  // Append up to 32 bits of zeroes in the normal way\n  uint8_t data[4] = {0, 0, 0, 0};\n  auto len = crc2len & 3;\n  if (len) {\n    crc1 = crc32c(data, len, crc1);\n  }\n\n  if (detail::crc32c_hw_supported()) {\n    return detail::crc32c_combine_hw(crc1, crc2, crc2len - len);\n  } else {\n    return detail::crc32c_combine_sw(crc1, crc2, crc2len - len);\n  }\n}\n\nuint32_t crc32c_combine_seed(\n    uint32_t crc1, uint32_t crc2, size_t crc2len, uint32_t startingChecksum) {\n  if (startingChecksum == 0U) {\n    return crc32c_combine(crc1, crc2, crc2len);\n  }\n\n  crc1 ^= startingChecksum;\n  crc2 ^= startingChecksum;\n\n  // Append up to 32 bits of zeroes in the normal way\n  uint8_t data[4] = {0, 0, 0, 0};\n  auto len = crc2len & 3;\n  if (len) {\n    crc1 = crc32c(data, len, crc1);\n  }\n\n  auto result = detail::crc32c_hw_supported()\n      ? detail::crc32c_combine_hw(crc1, crc2, crc2len - len)\n      : detail::crc32c_combine_sw(crc1, crc2, crc2len - len);\n  result ^= startingChecksum;\n  return result;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/Checksum.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#include <cstddef>\n\n/*\n * Checksum functions\n */\n\nnamespace folly {\n\n/**\n * Compute the CRC-32C checksum of a buffer, using a hardware-accelerated\n * implementation if available or a portable software implementation as\n * a default.\n *\n * @note CRC-32C is different from CRC-32; CRC-32C starts with a different\n *       polynomial and thus yields different results for the same input\n *       than a traditional CRC-32.\n */\nuint32_t crc32c(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);\n\n/**\n * Compute the CRC-32 checksum of a buffer, using a hardware-accelerated\n * implementation if available or a portable software implementation as\n * a default.\n */\nuint32_t crc32(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);\n\n/**\n * Compute the CRC-32 checksum of a buffer, using a hardware-accelerated\n * implementation if available or a portable software implementation as\n * a default.\n *\n * @note compared to crc32(), crc32_type() uses a different set of default\n *       parameters to match the results returned by boost::crc_32_type and\n *       php's built-in crc32 implementation\n */\nuint32_t crc32_type(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);\n\n/**\n * Given two checksums, combine them in to one checksum.\n *\n * Example:\n *                     len1            len2\n * Given a buffer [  checksum 1  |  checksum 2  ]\n * such that the first buffer's crc is checksum1 and has length len1,\n * and the remainder of the buffer's crc is checksum2 and len 2,\n * a total checksum over the whole buffer can be made by:\n *\n * crc32_combine(checksum1, checksum 2, len2); // len1 not needed.\n *\n * Note that this is equivalent to:\n *\n * crc32(buffer2, len2, crc32(buffer1, len1));\n *\n * However, this allows calculating the checksums in parallel\n * or calculating checksum 2 before checksum 1.\n *\n * Additionally, this is also equivalent, but much slower:\n * crc2 = crc32(buffer2, len2, 0);\n * crc1 = crc32(buffer1, len1, 0);\n * combined = crc2 ^ crc32(buffer_of_all_zeros, len2, crc1);\n *\n * crc32[c]_combine is roughly ~10x faster than either of the other\n * above two examples.\n */\nuint32_t crc32_combine(uint32_t crc1, uint32_t crc2, size_t crc2len);\n\n/* crc32c_combine is the same as crc32_combine, but uses the crc32c\n   polynomial */\nuint32_t crc32c_combine(uint32_t crc1, uint32_t crc2, size_t crc2len);\n\n/**\n * crc32c_combine_seed is the same as crc32c_combine. Unlike crc32c_combine that\n * only works for crc32c computed using a starting checksum 0U, this method\n * works for any starting checksum that is an uint32_t.\n *\n * @param crc1 First CRC checksum\n * @param crc2 Second CRC checksum\n * @param crc2len Length of the second buffer (in bytes)\n * @param startingChecksum The initial checksum value used when computing BOTH\n *                         crc1 and crc2. Both checksums must have been computed\n *                         with the same starting value. Use ~0U for the\n *                         standard CRC32C default.\n * @return Combined CRC32C checksum\n */\nuint32_t crc32c_combine_seed(\n    uint32_t crc1, uint32_t crc2, size_t crc2len, uint32_t startingChecksum);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/FarmHash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/external/farmhash/farmhash.h>\n\nnamespace folly {\nnamespace hash {\nnamespace farmhash {\n\n// The values returned by Hash, Hash32, and Hash64 are only guaranteed to\n// be the same within the same process.  Fingerpring32 and Fingerprint64\n// are fixed algorithms that always give the same result.\n\n// std::size_t Hash(char const*, std::size_t)\nusing external::farmhash::Hash;\n\n// uint32_t Hash32(char const*, std::size_t)\nusing external::farmhash::Hash32;\n\n// uint64_t Hash64(char const*, std::size_t)\nusing external::farmhash::Hash64;\n\n// uint32_t Fingerprint32(char const*, std::size_t)\nusing external::farmhash::Fingerprint32;\n\n// uint64_t Fingerprint64(char const*, std::size_t)\nusing external::farmhash::Fingerprint64;\n\n} // namespace farmhash\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/FnvHash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <string>\n#include <string_view>\n#include <type_traits>\n\nnamespace folly {\nnamespace hash {\n\n//  fnv\n//\n//  Fowler / Noll / Vo (FNV) Hash\n//    http://www.isthe.com/chongo/tech/comp/fnv/\n//\n//  Discouraged for poor performance in the smhasher suite.\n\nconstexpr uint32_t fnv32_hash_start = 2166136261UL;\nconstexpr uint32_t fnva32_hash_start = 2166136261UL;\nconstexpr uint64_t fnv64_hash_start = 14695981039346656037ULL;\nconstexpr uint64_t fnva64_hash_start = 14695981039346656037ULL;\n\n/**\n * Append byte to FNV hash.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint32_t fnv32_append_byte_BROKEN(uint32_t hash, uint8_t c) noexcept {\n  hash = hash //\n      + (hash << 1) //\n      + (hash << 4) //\n      + (hash << 7) //\n      + (hash << 8) //\n      + (hash << 24);\n  // forcing signed char, since other platforms can use unsigned\n  hash ^= static_cast<int8_t>(c);\n  return hash;\n}\n\nnamespace detail {\n\ntemplate <typename T>\nconstexpr bool is_hashable_byte_v = false;\ntemplate <>\ninline constexpr bool is_hashable_byte_v<char> = true;\ntemplate <>\ninline constexpr bool is_hashable_byte_v<signed char> = true;\ntemplate <>\ninline constexpr bool is_hashable_byte_v<unsigned char> = true;\n\n} // namespace detail\n\n/**\n * FNV hash of a byte-range.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint32_t fnv32_buf_BROKEN(\n    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {\n  for (size_t i = 0; i < n; ++i) {\n    hash = fnv32_append_byte_BROKEN(hash, static_cast<uint8_t>(buf[i]));\n  }\n  return hash;\n}\ninline uint32_t fnv32_buf_BROKEN(\n    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_buf_BROKEN(reinterpret_cast<const uint8_t*>(buf), n, hash);\n}\n\n/**\n * FNV hash of a c-str.\n *\n * Continues hashing until a null byte is reached.\n *\n * @param hash  The initial hash seed.\n *\n * @methodset fnv\n */\nconstexpr uint32_t fnv32_BROKEN(\n    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {\n  for (; *buf; ++buf) {\n    hash = fnv32_append_byte_BROKEN(hash, static_cast<uint8_t>(*buf));\n  }\n  return hash;\n}\n\n/**\n * @overloadbrief FNV hash of a string.\n *\n * FNV is the Fowler / Noll / Vo Hash:\n * http://www.isthe.com/chongo/tech/comp/fnv/\n *\n * Discouraged for poor performance in the smhasher suite.\n *\n * @param hash  The initial hash seed.\n *\n * @methodset fnv\n */\ninline uint32_t fnv32_BROKEN(\n    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_buf_BROKEN(str.data(), str.size(), hash);\n}\n\n/**\n * Append byte to FNV hash.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint32_t fnv32_append_byte_FIXED(uint32_t hash, uint8_t c) noexcept {\n  hash = hash //\n      + (hash << 1) //\n      + (hash << 4) //\n      + (hash << 7) //\n      + (hash << 8) //\n      + (hash << 24);\n  // forcing unsigned char\n  hash ^= static_cast<uint8_t>(c);\n  return hash;\n}\n\n/**\n * FNV hash of a byte-range.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint32_t fnv32_buf_FIXED(\n    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {\n  for (size_t i = 0; i < n; ++i) {\n    hash = fnv32_append_byte_FIXED(hash, static_cast<uint8_t>(buf[i]));\n  }\n  return hash;\n}\ninline uint32_t fnv32_buf_FIXED(\n    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_buf_FIXED(reinterpret_cast<const uint8_t*>(buf), n, hash);\n}\n\n/**\n * FNV hash of a c-str.\n *\n * Continues hashing until a null byte is reached.\n *\n * @param hash  The initial hash seed.\n *\n * @methodset fnv\n */\nconstexpr uint32_t fnv32_FIXED(\n    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {\n  for (; *buf; ++buf) {\n    hash = fnv32_append_byte_FIXED(hash, static_cast<uint8_t>(*buf));\n  }\n  return hash;\n}\n\n/**\n * @overloadbrief FNV hash of a string.\n *\n * FNV is the Fowler / Noll / Vo Hash:\n * http://www.isthe.com/chongo/tech/comp/fnv/\n *\n * Discouraged for poor performance in the smhasher suite.\n *\n * @param hash  The initial hash seed.\n *\n * @methodset fnv\n */\ninline uint32_t fnv32_FIXED(\n    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_buf_FIXED(str.data(), str.size(), hash);\n}\n\n/**\n * Append a byte to FNVA hash.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint32_t fnva32_append_byte(uint32_t hash, uint8_t c) noexcept {\n  hash ^= c;\n  hash = hash //\n      + (hash << 1) //\n      + (hash << 4) //\n      + (hash << 7) //\n      + (hash << 8) //\n      + (hash << 24);\n  return hash;\n}\n\n/**\n * FNVA hash of a byte-range.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint32_t fnva32_buf(\n    const C* buf, size_t n, uint32_t hash = fnva32_hash_start) noexcept {\n  for (size_t i = 0; i < n; ++i) {\n    hash = fnva32_append_byte(hash, static_cast<uint8_t>(buf[i]));\n  }\n  return hash;\n}\ninline uint32_t fnva32_buf(\n    const void* buf, size_t n, uint32_t hash = fnva32_hash_start) noexcept {\n  return fnva32_buf(reinterpret_cast<const uint8_t*>(buf), n, hash);\n}\n\n/**\n * FNVA hash of a string.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ninline uint32_t fnva32(\n    const std::string& str, uint32_t hash = fnva32_hash_start) noexcept {\n  return fnva32_buf(str.data(), str.size(), hash);\n}\n\n/**\n * Append a byte to FNV hash.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint64_t fnv64_append_byte_FIXED(uint64_t hash, uint8_t c) {\n  hash = hash //\n      + (hash << 1) //\n      + (hash << 4) //\n      + (hash << 5) //\n      + (hash << 7) //\n      + (hash << 8) //\n      + (hash << 40);\n  // forcing unsigned char\n  hash ^= static_cast<uint8_t>(c);\n  return hash;\n}\n\n/**\n * FNV hash of a byte-range.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint64_t fnv64_buf_FIXED(\n    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {\n  for (size_t i = 0; i < n; ++i) {\n    hash = fnv64_append_byte_FIXED(hash, static_cast<uint8_t>(buf[i]));\n  }\n  return hash;\n}\ninline uint64_t fnv64_buf_FIXED(\n    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_buf_FIXED(reinterpret_cast<const uint8_t*>(buf), n, hash);\n}\n\n/**\n * FNV hash of a c-str.\n *\n * Continues hashing until a null byte is reached.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint64_t fnv64_FIXED(\n    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {\n  for (; *buf; ++buf) {\n    hash = fnv64_append_byte_FIXED(hash, static_cast<uint8_t>(*buf));\n  }\n  return hash;\n}\n\n/**\n * @overloadbrief FNV hash of a string.\n *\n * FNV is the Fowler / Noll / Vo Hash:\n * http://www.isthe.com/chongo/tech/comp/fnv/\n *\n * Discouraged for poor performance in the smhasher suite.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ninline uint64_t fnv64_FIXED(\n    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_buf_FIXED(str.data(), str.size(), hash);\n}\n\n/**\n * Append a byte to FNV hash.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint64_t fnv64_append_byte_BROKEN(uint64_t hash, uint8_t c) {\n  hash = hash //\n      + (hash << 1) //\n      + (hash << 4) //\n      + (hash << 5) //\n      + (hash << 7) //\n      + (hash << 8) //\n      + (hash << 40);\n  // forcing signed char, since other platforms can use unsigned\n  hash ^= static_cast<int8_t>(c);\n  return hash;\n}\n\n/**\n * FNV hash of a byte-range.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint64_t fnv64_buf_BROKEN(\n    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {\n  for (size_t i = 0; i < n; ++i) {\n    hash = fnv64_append_byte_BROKEN(hash, static_cast<uint8_t>(buf[i]));\n  }\n  return hash;\n}\ninline uint64_t fnv64_buf_BROKEN(\n    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_buf_BROKEN(reinterpret_cast<const uint8_t*>(buf), n, hash);\n}\n\n/**\n * FNV hash of a c-str.\n *\n * Continues hashing until a null byte is reached.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint64_t fnv64_BROKEN(\n    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {\n  for (; *buf; ++buf) {\n    hash = fnv64_append_byte_BROKEN(hash, static_cast<uint8_t>(*buf));\n  }\n  return hash;\n}\n\n/**\n * @overloadbrief FNV hash of a string.\n *\n * FNV is the Fowler / Noll / Vo Hash:\n * http://www.isthe.com/chongo/tech/comp/fnv/\n *\n * Discouraged for poor performance in the smhasher suite.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ninline uint64_t fnv64_BROKEN(\n    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_buf_BROKEN(str.data(), str.size(), hash);\n}\n\n/**\n * Alias for fnv32_append_byte_BROKEN.\n *\n * @see fnv32_BROKEN\n * @methodset fnv\n */\nconstexpr uint32_t fnv32_append_byte(uint32_t hash, uint8_t c) noexcept {\n  return fnv32_append_byte_BROKEN(hash, c);\n}\n\n/**\n * Alias for fnv32_buf_BROKEN.\n *\n * @see fnv32_BROKEN\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint32_t fnv32_buf(\n    const C* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_buf_BROKEN(buf, n, hash);\n}\ninline uint32_t fnv32_buf(\n    const void* buf, size_t n, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_buf_BROKEN(buf, n, hash);\n}\n\n/**\n * Alias for fnv32_BROKEN.\n *\n * @methodset fnv\n */\nconstexpr uint32_t fnv32(\n    const char* buf, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_BROKEN(buf, hash);\n}\n\n/**\n * Alias for fnv32_BROKEN.\n *\n * @methodset fnv\n */\ninline uint32_t fnv32(\n    const std::string& str, uint32_t hash = fnv32_hash_start) noexcept {\n  return fnv32_BROKEN(str, hash);\n}\n\n/**\n * Alias for fnv64_append_byte_BROKEN.\n *\n * @see fnv32_BROKEN\n * @methodset fnv\n */\nconstexpr uint64_t fnv64_append_byte(uint64_t hash, uint8_t c) {\n  return fnv64_append_byte_BROKEN(hash, c);\n}\n\n/**\n * Alias for fnv64_buf_BROKEN.\n *\n * @see fnv32_BROKEN\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint64_t fnv64_buf(\n    const C* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_buf_BROKEN(buf, n, hash);\n}\ninline uint64_t fnv64_buf(\n    const void* buf, size_t n, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_buf_BROKEN(buf, n, hash);\n}\n\n/**\n * Alias for fnv64_BROKEN.\n *\n * @see fnv32_BROKEN\n * @methodset fnv\n */\nconstexpr uint64_t fnv64(\n    const char* buf, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_BROKEN(buf, hash);\n}\n\n/**\n * Alias for fnv64_BROKEN.\n *\n * @see fnv32_BROKEN\n * @methodset fnv\n */\ninline uint64_t fnv64(\n    std::string_view str, uint64_t hash = fnv64_hash_start) noexcept {\n  return fnv64_BROKEN(str, hash);\n}\n\n/**\n * Append a byte to FNVA hash.\n *\n * @see fnv32\n * @methodset fnv\n */\nconstexpr uint64_t fnva64_append_byte(uint64_t hash, uint8_t c) {\n  hash ^= c;\n  hash = hash //\n      + (hash << 1) //\n      + (hash << 4) //\n      + (hash << 5) //\n      + (hash << 7) //\n      + (hash << 8) //\n      + (hash << 40);\n  return hash;\n}\n\n/**\n * FNVA hash of a byte-range.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ntemplate <typename C, std::enable_if_t<detail::is_hashable_byte_v<C>, int> = 0>\nconstexpr uint64_t fnva64_buf(\n    const C* buf, size_t n, uint64_t hash = fnva64_hash_start) noexcept {\n  for (size_t i = 0; i < n; ++i) {\n    hash = fnva64_append_byte(hash, static_cast<uint8_t>(buf[i]));\n  }\n  return hash;\n}\ninline uint64_t fnva64_buf(\n    const void* buf, size_t n, uint64_t hash = fnva64_hash_start) noexcept {\n  return fnva64_buf(reinterpret_cast<const uint8_t*>(buf), n, hash);\n}\n\n/**\n * FNVA hash of a string.\n *\n * @param hash  The initial hash seed.\n *\n * @see fnv32\n * @methodset fnv\n */\ninline uint64_t fnva64(\n    const std::string& str, uint64_t hash = fnva64_hash_start) noexcept {\n  return fnva64_buf(str.data(), str.size(), hash);\n}\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/Hash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_hash\n//\n\n/**\n * folly::hash provides hashing algorithms, as well as algorithms to combine\n * multiple hashes/hashable objects together.\n *\n * @refcode folly/docs/examples/folly/hash/Hash.cpp\n * @file hash/Hash.h\n */\n\n#pragma once\n\n#include <cstdint>\n#include <cstring>\n#include <limits>\n#include <memory>\n#include <string>\n#include <string_view>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include <folly/CPortability.h>\n#include <folly/Portability.h>\n#include <folly/Traits.h>\n#include <folly/Utility.h>\n#include <folly/hash/MurmurHash.h>\n#include <folly/hash/SpookyHashV1.h>\n#include <folly/hash/SpookyHashV2.h>\n#include <folly/lang/Bits.h>\n\n// This includes are here for backward compatibility as these algorithms were\n// kept in this header.\n#include <folly/hash/FnvHash.h>\n#include <folly/hash/HsiehHash.h>\n#include <folly/lang/cstring_view.h>\n\nnamespace folly {\nnamespace hash {\n\n/**\n * Reduce two 64-bit hashes into one.\n *\n * hash_128_to_64 uses the Hash128to64 function from Google's cityhash (under\n * the MIT License).\n */\nFOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(\"unsigned-integer-overflow\")\nconstexpr uint64_t hash_128_to_64(\n    const uint64_t upper, const uint64_t lower) noexcept {\n  // Murmur-inspired hashing.\n  const uint64_t kMul = 0x9ddfea08eb382d69ULL;\n  uint64_t a = (lower ^ upper) * kMul;\n  a ^= (a >> 47);\n  uint64_t b = (upper ^ a) * kMul;\n  b ^= (b >> 47);\n  b *= kMul;\n  return b;\n}\n\nnamespace detail {\n\ntemplate <typename H>\nconstexpr uint64_t hash_sequence(const H&) noexcept {\n  return 0;\n}\n\ntemplate <typename H, typename T>\nconstexpr uint64_t hash_sequence(const H& h, const T& v) noexcept(\n    noexcept(h(v))) {\n  return h(v);\n}\n\ntemplate <typename H, typename T, typename... Ts>\nconstexpr uint64_t hash_sequence(\n    const H& h, const T& v, const Ts&... ts) noexcept(noexcept(h(v))) {\n  return hash::hash_128_to_64(h(v), hash_sequence(h, ts...));\n}\n\ntemplate <typename H, typename Tuple, size_t... Is>\nuint64_t hash_tuple_sequence(\n    const H& h, const Tuple& t, std::index_sequence<Is...>) {\n  return hash_sequence(h, std::get<Is>(t)...);\n}\n\ntemplate <typename H, typename... Ts>\nconstexpr uint64_t hash_tuple(const H& h, const std::tuple<Ts...>& t) {\n  return hash_tuple_sequence(h, t, std::make_index_sequence<sizeof...(Ts)>());\n}\n\n} // namespace detail\n\n/**\n * Order-independent reduction of two 64-bit hashes into one.\n *\n * Commutative accumulator taken from this paper:\n * https://www.preprints.org/manuscript/201710.0192/v1/download\n */\nFOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(\"unsigned-integer-overflow\")\nconstexpr uint64_t commutative_hash_128_to_64(\n    const uint64_t upper, const uint64_t lower) noexcept {\n  return 3860031 + (upper + lower) * 2779 + (upper * lower * 2);\n}\n\n/**\n * Thomas Wang 64 bit mix hash function.\n *\n * @methodset twang\n */\nFOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(\"unsigned-integer-overflow\")\nconstexpr uint64_t twang_mix64(uint64_t key) noexcept {\n  key = (~key) + (key << 21); // key *= (1 << 21) - 1; key -= 1;\n  key = key ^ (key >> 24);\n  key = key + (key << 3) + (key << 8); // key *= 1 + (1 << 3) + (1 << 8)\n  key = key ^ (key >> 14);\n  key = key + (key << 2) + (key << 4); // key *= 1 + (1 << 2) + (1 << 4)\n  key = key ^ (key >> 28);\n  key = key + (key << 31); // key *= 1 + (1 << 31)\n  return key;\n}\n\n/**\n * Inverse of twang_mix64.\n *\n * @methodset twang\n */\nconstexpr uint64_t twang_unmix64(uint64_t key) noexcept {\n  // See the comments in jenkins_rev_unmix32 for an explanation as to how this\n  // was generated\n  key *= 4611686016279904257U;\n  key ^= (key >> 28) ^ (key >> 56);\n  key *= 14933078535860113213U;\n  key ^= (key >> 14) ^ (key >> 28) ^ (key >> 42) ^ (key >> 56);\n  key *= 15244667743933553977U;\n  key ^= (key >> 24) ^ (key >> 48);\n  key = (key + 1) * 9223367638806167551U;\n  return key;\n}\n\n/**\n * Thomas Wang downscaling hash function.\n *\n * @methodset twang\n */\nconstexpr uint32_t twang_32from64(uint64_t key) noexcept {\n  key = (~key) + (key << 18);\n  key = key ^ (key >> 31);\n  key = key * 21;\n  key = key ^ (key >> 11);\n  key = key + (key << 6);\n  key = key ^ (key >> 22);\n  return static_cast<uint32_t>(key);\n}\n\n/**\n * Robert Jenkins' reversible 32 bit mix hash function.\n *\n * @methodset jenkins\n */\nconstexpr uint32_t jenkins_rev_mix32(uint32_t key) noexcept {\n  key += (key << 12); // key *= (1 + (1 << 12))\n  key ^= (key >> 22);\n  key += (key << 4); // key *= (1 + (1 << 4))\n  key ^= (key >> 9);\n  key += (key << 10); // key *= (1 + (1 << 10))\n  key ^= (key >> 2);\n  // key *= (1 + (1 << 7)) * (1 + (1 << 12))\n  key += (key << 7);\n  key += (key << 12);\n  return key;\n}\n\n/**\n * Inverse of jenkins_rev_mix32.\n *\n * @methodset jenkins\n */\nconstexpr uint32_t jenkins_rev_unmix32(uint32_t key) noexcept {\n  // These are the modular multiplicative inverses (in Z_2^32) of the\n  // multiplication factors in jenkins_rev_mix32, in reverse order.  They were\n  // computed using the Extended Euclidean algorithm, see\n  // http://en.wikipedia.org/wiki/Modular_multiplicative_inverse\n  key *= 2364026753U;\n\n  // The inverse of a ^= (a >> n) is\n  // b = a\n  // for (int i = n; i < 32; i += n) {\n  //   b ^= (a >> i);\n  // }\n  key ^= (key >> 2) ^ (key >> 4) ^ (key >> 6) ^ (key >> 8) ^ (key >> 10) ^\n      (key >> 12) ^ (key >> 14) ^ (key >> 16) ^ (key >> 18) ^ (key >> 20) ^\n      (key >> 22) ^ (key >> 24) ^ (key >> 26) ^ (key >> 28) ^ (key >> 30);\n  key *= 3222273025U;\n  key ^= (key >> 9) ^ (key >> 18) ^ (key >> 27);\n  key *= 4042322161U;\n  key ^= (key >> 22);\n  key *= 16773121U;\n  return key;\n}\n\n} // namespace hash\n\nnamespace detail {\n\ntemplate <typename Int>\nstruct integral_hasher {\n  using folly_is_avalanching =\n      std::bool_constant<(sizeof(Int) >= 8 || sizeof(size_t) == 4)>;\n\n  constexpr size_t operator()(Int const& i) const noexcept {\n    static_assert(sizeof(Int) <= 16, \"Input type is too wide\");\n    if constexpr (sizeof(Int) <= 4) {\n      auto const i32 = static_cast<int32_t>(i); // impl accident: sign-extends\n      auto const u32 = static_cast<uint32_t>(i32);\n      return static_cast<size_t>(hash::jenkins_rev_mix32(u32));\n    } else if constexpr (sizeof(Int) <= 8) {\n      auto const u64 = static_cast<uint64_t>(i);\n      return static_cast<size_t>(hash::twang_mix64(u64));\n    } else {\n      auto const u = to_unsigned(i);\n      auto const hi = static_cast<uint64_t>(u >> sizeof(Int) * 4);\n      auto const lo = static_cast<uint64_t>(u);\n      return static_cast<size_t>(hash::hash_128_to_64(hi, lo));\n    }\n  }\n};\n\ntemplate <typename F>\nstruct float_hasher {\n  using folly_is_avalanching = std::true_type;\n\n  size_t operator()(F const& f) const noexcept {\n    static_assert(sizeof(F) <= 8, \"Input type is too wide\");\n\n    if (f == F{}) { // Ensure 0 and -0 get the same hash.\n      return 0;\n    }\n\n    uint64_t u64 = 0;\n    memcpy(&u64, &f, sizeof(F));\n    return static_cast<size_t>(hash::twang_mix64(u64));\n  }\n};\n\n} // namespace detail\n\ntemplate <class Key, class Enable = void>\nstruct hasher;\n\nstruct Hash {\n  template <class T>\n  constexpr size_t operator()(const T& v) const\n      noexcept(noexcept(hasher<T>()(v))) {\n    return hasher<T>()(v);\n  }\n\n  template <class... Ts>\n  constexpr size_t operator()(const Ts&... ts) const {\n    return static_cast<size_t>(hash::detail::hash_sequence(*this, ts...));\n  }\n\n  constexpr size_t operator()() const noexcept {\n    return static_cast<size_t>(hash::detail::hash_sequence(*this));\n  }\n};\n\n// IsAvalanchingHasher<H, K> extends std::integral_constant<bool, V>.\n// V will be true if it is known that when a hasher of type H computes\n// the hash of a key of type K, any subset of B bits from the resulting\n// hash value is usable in a context that can tolerate a collision rate\n// of about 1/2^B.  (Input bits lost implicitly converting between K and\n// the argument of H::operator() are not considered here; K is separate\n// to handle the case of generic hashers like folly::Hash).\n//\n// If std::hash<T> or folly::hasher<T> is specialized for a new type T and\n// the implementation avalanches input entropy across all of the bits of a\n// std::size_t result, the specialization should be marked as avalanching.\n// This can be done either by adding a member type folly_is_avalanching\n// to the functor H that contains a constexpr bool value of true, or by\n// specializing IsAvalanchingHasher<H, K>.  The member type mechanism is\n// more convenient, but specializing IsAvalanchingHasher may be required\n// if a hasher is polymorphic on the key type or if its definition cannot\n// be modified.\n//\n// The standard's definition of hash quality is based on the chance hash\n// collisions using the entire hash value.  No requirement is made that\n// this property holds for subsets of the bits.  In addition, hashed keys\n// in real-world workloads are not chosen uniformly from the entire domain\n// of keys, which can further increase the collision rate for a subset\n// of bits.  For example, std::hash<uint64_t> in libstdc++-v3 and libc++\n// is the identity function.  This hash function has no collisions when\n// considering hash values in their entirety, but for real-world workloads\n// the high bits are likely to always be zero.\n//\n// Some hash functions provide a stronger guarantee -- the standard's\n// collision property is also preserved for subsets of the output bits and\n// for sub-domains of keys.  Another way to say this is that each bit of\n// the hash value contains entropy from the entire input, changes to the\n// input avalanche across all of the bits of the output.  The distinction\n// is useful when mapping the hash value onto a smaller space efficiently\n// (such as when implementing a hash table).\ntemplate <typename Hasher, typename Key>\nstruct IsAvalanchingHasher;\n\nnamespace detail {\ntemplate <typename Hasher, typename Void = void>\nstruct IsAvalanchingHasherFromMemberType\n    : std::bool_constant<!require_sizeof<Hasher>> {};\n\ntemplate <typename Hasher>\nstruct IsAvalanchingHasherFromMemberType<\n    Hasher,\n    void_t<typename Hasher::folly_is_avalanching>>\n    : std::bool_constant<Hasher::folly_is_avalanching::value> {};\n} // namespace detail\n\ntemplate <typename Hasher, typename Key>\nstruct IsAvalanchingHasher : detail::IsAvalanchingHasherFromMemberType<Hasher> {\n};\n\n// It's ugly to put this here, but folly::transparent isn't hash specific\n// so it seems even more ugly to put this near its declaration\ntemplate <typename H, typename K>\nstruct IsAvalanchingHasher<transparent<H>, K> : IsAvalanchingHasher<H, K> {};\n\ntemplate <typename K>\nstruct IsAvalanchingHasher<Hash, K> : IsAvalanchingHasher<hasher<K>, K> {};\n\ntemplate <>\nstruct hasher<bool> {\n  using folly_is_avalanching = std::true_type;\n\n  constexpr size_t operator()(bool key) const noexcept {\n    // Make sure that all the output bits depend on the input.\n    return key ? std::numeric_limits<size_t>::max() : 0;\n  }\n};\ntemplate <typename K>\nstruct IsAvalanchingHasher<hasher<bool>, K> : std::true_type {};\n\ntemplate <>\nstruct hasher<unsigned long long>\n    : detail::integral_hasher<unsigned long long> {};\n\ntemplate <>\nstruct hasher<signed long long> : detail::integral_hasher<signed long long> {};\n\ntemplate <>\nstruct hasher<unsigned long> : detail::integral_hasher<unsigned long> {};\n\ntemplate <>\nstruct hasher<signed long> : detail::integral_hasher<signed long> {};\n\ntemplate <>\nstruct hasher<unsigned int> : detail::integral_hasher<unsigned int> {};\n\ntemplate <>\nstruct hasher<signed int> : detail::integral_hasher<signed int> {};\n\ntemplate <>\nstruct hasher<unsigned short> : detail::integral_hasher<unsigned short> {};\n\ntemplate <>\nstruct hasher<signed short> : detail::integral_hasher<signed short> {};\n\ntemplate <>\nstruct hasher<unsigned char> : detail::integral_hasher<unsigned char> {};\n\ntemplate <>\nstruct hasher<signed char> : detail::integral_hasher<signed char> {};\n\ntemplate <> // char is a different type from both signed char and unsigned char\nstruct hasher<char> : detail::integral_hasher<char> {};\n\n#if FOLLY_HAVE_INT128_T\ntemplate <>\nstruct hasher<signed __int128> : detail::integral_hasher<signed __int128> {};\n\ntemplate <>\nstruct hasher<unsigned __int128> : detail::integral_hasher<unsigned __int128> {\n};\n#endif\n\ntemplate <>\nstruct hasher<float> : detail::float_hasher<float> {};\n\ntemplate <>\nstruct hasher<double> : detail::float_hasher<double> {};\n\ntemplate <>\nstruct hasher<std::string> {\n  using folly_is_avalanching = std::true_type;\n\n  size_t operator()(const std::string& key) const {\n    return static_cast<size_t>(\n        hash::SpookyHashV2::Hash64(key.data(), key.size(), 0));\n  }\n};\ntemplate <typename K>\nstruct IsAvalanchingHasher<hasher<std::string>, K> : std::true_type {};\n\ntemplate <>\nstruct hasher<std::string_view> {\n  using folly_is_avalanching = std::true_type;\n\n  size_t operator()(const std::string_view& key) const {\n    return static_cast<size_t>(\n        hash::SpookyHashV2::Hash64(key.data(), key.size(), 0));\n  }\n};\ntemplate <typename K>\nstruct IsAvalanchingHasher<hasher<std::string_view>, K> : std::true_type {};\n\ntemplate <>\nstruct hasher<cstring_view> : hasher<std::string_view> {};\ntemplate <typename K>\nstruct IsAvalanchingHasher<hasher<cstring_view>, K> : std::true_type {};\n\ntemplate <typename T>\nstruct hasher<T, std::enable_if_t<std::is_enum<T>::value>> {\n  size_t operator()(T key) const noexcept {\n    auto u = to_underlying(key);\n    return hasher<decltype(u)>{}(u);\n  }\n};\n\ntemplate <typename T, typename K>\nstruct IsAvalanchingHasher<\n    hasher<T, std::enable_if_t<std::is_enum<T>::value>>,\n    K> : IsAvalanchingHasher<hasher<std::underlying_type_t<T>>, K> {};\n\nnamespace detail {\nstruct hash_one_fn {\n  template <typename T>\n  constexpr size_t operator()(T const& v) const\n      noexcept(noexcept(hasher<T>{}(v))) {\n    return hasher<T>{}(v);\n  }\n};\n\ninline constexpr hash_one_fn hash_one{};\n} // namespace detail\n\ntemplate <typename T1, typename T2>\nstruct hasher<std::pair<T1, T2>> {\n  using folly_is_avalanching = std::true_type;\n\n  size_t operator()(const std::pair<T1, T2>& key) const {\n    return hash::detail::hash_sequence(detail::hash_one, key.first, key.second);\n  }\n};\n\ntemplate <typename... Ts>\nstruct hasher<std::tuple<Ts...>> {\n  size_t operator()(const std::tuple<Ts...>& key) const {\n    return hash::detail::hash_tuple(detail::hash_one, key);\n  }\n};\n\ntemplate <typename T>\nstruct hasher<T*> {\n  using folly_is_avalanching = hasher<std::uintptr_t>::folly_is_avalanching;\n\n  size_t operator()(T* key) const {\n    auto val = folly::bit_cast<std::uintptr_t>(key);\n    return hasher<decltype(val)>{}(val);\n  }\n};\n\ntemplate <typename T>\nstruct hasher<std::unique_ptr<T>> {\n  using folly_is_avalanching = typename hasher<T*>::folly_is_avalanching;\n\n  size_t operator()(const std::unique_ptr<T>& key) const {\n    return hasher<T*>{}(key.get());\n  }\n};\n\ntemplate <typename T>\nstruct hasher<std::shared_ptr<T>> {\n  using folly_is_avalanching = typename hasher<T*>::folly_is_avalanching;\n\n  size_t operator()(const std::shared_ptr<T>& key) const {\n    return hasher<T*>{}(key.get());\n  }\n};\n\n// combiner for multi-arg tuple also mixes bits\ntemplate <typename T, typename K>\nstruct IsAvalanchingHasher<hasher<std::tuple<T>>, K>\n    : IsAvalanchingHasher<hasher<T>, K> {};\ntemplate <typename T1, typename T2, typename... Ts, typename K>\nstruct IsAvalanchingHasher<hasher<std::tuple<T1, T2, Ts...>>, K>\n    : std::true_type {};\n\nnamespace hash {\n\n// Compatible with std::hash implementation of hashing for std::string_view.\n// We use hash::murmurHash64 as a replacement of libstdc++ implementation\n// for better performance, for other implementations of C++ Standard Libraries\n// we fallback to std::hash.\n#if defined(_GLIBCXX_STRING) && FOLLY_X64\nFOLLY_ALWAYS_INLINE size_t stdCompatibleHash(std::string_view sv) noexcept {\n  static_assert(sizeof(size_t) == sizeof(uint64_t));\n  constexpr uint64_t kSeed = 0xc70f6907ULL;\n  return hash::murmurHash64(sv.data(), sv.size(), kSeed);\n}\n#else\nFOLLY_ALWAYS_INLINE size_t stdCompatibleHash(std::string_view sv) noexcept(\n    noexcept(std::hash<std::string_view>{}(sv))) {\n  return std::hash<std::string_view>{}(sv);\n}\n#endif // defined(_GLIBCXX_STRING) && FOLLY_X64\n\n// Simply uses std::hash to hash.  Note that std::hash is not guaranteed\n// to be a very good hash function; provided std::hash doesn't collide on\n// the individual inputs, you are fine, but that won't be true for, say,\n// strings or pairs\nclass StdHasher {\n public:\n  // The standard requires all explicit and partial specializations of std::hash\n  // supplied by either the standard library or by users to be default\n  // constructible.\n  template <typename T>\n  size_t operator()(const T& t) const noexcept(noexcept(std::hash<T>()(t))) {\n    return std::hash<T>()(t);\n  }\n\n  size_t operator()(std::string_view sv) const\n      noexcept(noexcept(stdCompatibleHash(FOLLY_DECLVAL(std::string_view)))) {\n    return stdCompatibleHash(sv);\n  }\n\n  size_t operator()(const std::string& s) const\n      noexcept(noexcept(stdCompatibleHash(FOLLY_DECLVAL(const std::string&)))) {\n    return stdCompatibleHash(s);\n  }\n};\n\n// This is a general-purpose way to create a single hash from multiple\n// hashable objects. hash_combine_generic takes a class Hasher implementing\n// hash<T>; hash_combine uses a default hasher StdHasher that uses std::hash.\n// hash_combine_generic hashes each argument and combines those hashes in\n// an order-dependent way to yield a new hash; hash_range does so (also in an\n// order-dependent way) for items in the range [first, last);\n// commutative_hash_combine_* hashes values but combines them in an\n// order-independent way to yield a new hash.\n\n/**\n * Hash a value, and combine it with a seed. Commutative.\n *\n * @param hasher  The function/callable which will hash the value.\n *\n * @methodset ranges\n */\ntemplate <class Hash, class Value>\nuint64_t commutative_hash_combine_value_generic(\n    uint64_t seed, Hash const& hasher, Value const& value) {\n  auto const x = hasher(value);\n  auto const y = IsAvalanchingHasher<Hash, Value>::value ? x : twang_mix64(x);\n  return commutative_hash_128_to_64(seed, y);\n}\n\n/**\n * Combine hashes of items in the range [first, last), order-dependently.\n *\n * For order-independent hashing, such as for hashing an unordered container\n * (e.g. folly::dynamic::object) use commutative_hash_combine_range instead.\n *\n * @param hash  The base-case hash to use.\n * @param hasher  The function/callable which will hash the value.\n *\n * @methodset ranges\n */\ntemplate <\n    class Iter,\n    class Hash = std::hash<typename std::iterator_traits<Iter>::value_type>>\nuint64_t hash_range(\n    Iter begin, Iter end, uint64_t hash = 0, Hash hasher = Hash()) {\n  for (; begin != end; ++begin) {\n    hash = hash_128_to_64(hash, hasher(*begin));\n  }\n  return hash;\n}\n\n/**\n * Create a hash from multiple hashable objects, order-independently.\n *\n * For order-dependent hashing use hash_range.\n *\n * @param seed  The base-case hash to use.\n * @param hasher  The function/callable which will hash the value.\n *\n * @methodset ranges\n */\ntemplate <class Hash, class Iter>\nuint64_t commutative_hash_combine_range_generic(\n    uint64_t seed, Hash const& hasher, Iter first, Iter last) {\n  while (first != last) {\n    seed = commutative_hash_combine_value_generic(seed, hasher, *first++);\n  }\n  return seed;\n}\n\n/**\n * Create a hash from multiple hashable objects, order-independently.\n *\n * @methodset ranges\n */\ntemplate <class Iter>\nuint64_t commutative_hash_combine_range(Iter first, Iter last) {\n  return commutative_hash_combine_range_generic(0, Hash{}, first, last);\n}\n\nnamespace detail {\nusing c_array_size_t = size_t[];\n} // namespace detail\n\n// Never used, but gcc demands it.\ntemplate <class Hasher>\ninline size_t hash_combine_generic(const Hasher&) noexcept {\n  return 0;\n}\n\n/**\n * Combine hashes of multiple items, order-dependently.\n *\n * @param h  The function/callable which will hash the value.\n *\n * @methodset ranges\n */\ntemplate <class Hasher, typename T, typename... Ts>\nsize_t\nhash_combine_generic(const Hasher& h, const T& t, const Ts&... ts) noexcept(\n    noexcept(detail::c_array_size_t{h(t), h(ts)...})) {\n  size_t seed = h(t);\n  if (sizeof...(ts) == 0) {\n    return seed;\n  }\n  size_t remainder = hash_combine_generic(h, ts...);\n  if constexpr (sizeof(size_t) == sizeof(uint32_t)) {\n    return twang_32from64((uint64_t(seed) << 32) | remainder);\n  } else {\n    return static_cast<size_t>(hash_128_to_64(seed, remainder));\n  }\n}\n\n/**\n * Combine hashes of multiple items, order-independently.\n *\n * @param hasher  The function/callable which will hash the value.\n *\n * @methodset ranges\n */\ntemplate <typename Hash, typename... Value>\nuint64_t commutative_hash_combine_generic(\n    uint64_t seed, Hash const& hasher, Value const&... value) {\n  ((seed = commutative_hash_combine_value_generic(seed, hasher, value)), ...);\n  return seed;\n}\n\n/**\n * Combine hashes of multiple items, order-dependently.\n *\n * @methodset ranges\n */\ntemplate <typename T, typename... Ts>\n[[nodiscard]] size_t hash_combine(const T& t, const Ts&... ts) noexcept(\n    noexcept(hash_combine_generic(StdHasher{}, t, ts...))) {\n  return hash_combine_generic(StdHasher{}, t, ts...);\n}\n\n/**\n * Combine hashes of multiple items, order-independently.\n *\n */\ntemplate <typename... Value>\nuint64_t commutative_hash_combine(Value const&... value) {\n  return commutative_hash_combine_generic(0, Hash{}, value...);\n}\n} // namespace hash\n\n// recursion\ntemplate <size_t index, typename... Ts>\nstruct TupleHasher {\n  size_t operator()(std::tuple<Ts...> const& key) const {\n    return hash::hash_combine(\n        TupleHasher<index - 1, Ts...>()(key), std::get<index>(key));\n  }\n};\n\n// base\ntemplate <typename... Ts>\nstruct TupleHasher<0, Ts...> {\n  size_t operator()(std::tuple<Ts...> const& key) const {\n    // we could do std::hash here directly, but hash_combine hides all the\n    // ugly templating implicitly\n    return hash::hash_combine(std::get<0>(key));\n  }\n};\n\n} // namespace folly\n\n// Custom hash functions.\nnamespace std {\n// Hash function for pairs. Requires default hash functions for both\n// items in the pair.\ntemplate <typename T1, typename T2>\nstruct hash<std::pair<T1, T2>> {\n  using folly_is_avalanching = std::true_type;\n\n  size_t operator()(const std::pair<T1, T2>& x) const {\n    return folly::hash::hash_combine(x.first, x.second);\n  }\n};\n\n// Hash function for tuples. Requires default hash functions for all types.\ntemplate <typename... Ts>\nstruct hash<std::tuple<Ts...>> {\n private:\n  using FirstT = std::decay_t<std::tuple_element_t<0, std::tuple<Ts..., bool>>>;\n\n public:\n  using folly_is_avalanching = std::bool_constant<(\n      sizeof...(Ts) != 1 ||\n      folly::IsAvalanchingHasher<std::hash<FirstT>, FirstT>::value)>;\n\n  size_t operator()(std::tuple<Ts...> const& key) const {\n    folly::TupleHasher<\n        sizeof...(Ts) - 1, // start index\n        Ts...>\n        hasher;\n\n    return hasher(key);\n  }\n};\n} // namespace std\n\nnamespace folly {\n\n// std::hash<std::string> is avalanching on libstdc++-v3 (code checked),\n// libc++ (code checked), and MSVC (based on online information).\n// std::hash for float and double on libstdc++-v3 are avalanching,\n// but they are not on libc++.  std::hash for integral types is not\n// avalanching for libstdc++-v3 or libc++.  We're conservative here and\n// just mark std::string and std::string_view as avalanching.\ntemplate <typename... Args, typename K>\nstruct IsAvalanchingHasher<std::hash<std::basic_string<Args...>>, K>\n    : std::true_type {};\n\ntemplate <typename... Args, typename K>\nstruct IsAvalanchingHasher<std::hash<std::basic_string_view<Args...>>, K>\n    : std::true_type {};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/HsiehHash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <cstring>\n#include <string>\n\n#include <folly/lang/Bits.h>\n\nnamespace folly {\nnamespace hash {\n\n//  hsieh\n//\n//  Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html\n//\n//  Discouraged for suboptimal performance in the smhasher suite.\n\n#define get16bits(d) folly::loadUnaligned<uint16_t>(d)\n\n/**\n * hsieh hash a byte-range.\n *\n * @see hsieh_hash32_str\n * @methodset hsieh\n */\ninline constexpr uint32_t hsieh_hash32_buf_constexpr(\n    const unsigned char* buf, size_t len) noexcept {\n  // forcing signed char, since other platforms can use unsigned\n  const unsigned char* s = buf;\n  uint32_t hash = static_cast<uint32_t>(len);\n  uint32_t tmp = 0;\n  size_t rem = 0;\n\n  if (len <= 0 || buf == nullptr) {\n    return 0;\n  }\n\n  rem = len & 3;\n  len >>= 2;\n\n  /* Main loop */\n  for (; len > 0; len--) {\n    hash += get16bits(s);\n    tmp = (get16bits(s + 2) << 11) ^ hash;\n    hash = (hash << 16) ^ tmp;\n    s += 2 * sizeof(uint16_t);\n    hash += hash >> 11;\n  }\n\n  /* Handle end cases */\n  switch (rem) {\n    case 3:\n      hash += get16bits(s);\n      hash ^= hash << 16;\n      hash ^= s[sizeof(uint16_t)] << 18;\n      hash += hash >> 11;\n      break;\n    case 2:\n      hash += get16bits(s);\n      hash ^= hash << 11;\n      hash += hash >> 17;\n      break;\n    case 1:\n      hash += *s;\n      hash ^= hash << 10;\n      hash += hash >> 1;\n      break;\n    default:\n      break;\n  }\n\n  /* Force \"avalanching\" of final 127 bits */\n  hash ^= hash << 3;\n  hash += hash >> 5;\n  hash ^= hash << 4;\n  hash += hash >> 17;\n  hash ^= hash << 25;\n  hash += hash >> 6;\n\n  return hash;\n}\n\n#undef get16bits\n\n/**\n * hsieh hash a void* byte-range.\n *\n * @see hsieh_hash32_str\n * @methodset hsieh\n */\ninline uint32_t hsieh_hash32_buf(const void* buf, size_t len) noexcept {\n  return hsieh_hash32_buf_constexpr(\n      reinterpret_cast<const unsigned char*>(buf), len);\n}\n\n/**\n * hsieh hash a c-str.\n *\n * Computes the strlen of the input, then byte-range hashes it.\n *\n * @see hsieh_hash32_str\n * @methodset hsieh\n */\ninline uint32_t hsieh_hash32(const char* s) noexcept {\n  return hsieh_hash32_buf(s, std::strlen(s));\n}\n\n/**\n * hsieh hash a string.\n *\n * Paul Hsieh: http://www.azillionmonkeys.com/qed/hash.html\n *\n * @methodset hsieh\n */\ninline uint32_t hsieh_hash32_str(const std::string& str) noexcept {\n  return hsieh_hash32_buf(str.data(), str.size());\n}\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/MurmurHash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n\n#include <folly/CPortability.h>\n#include <folly/lang/Bits.h>\n#include <folly/portability/Constexpr.h>\n\nnamespace folly {\nnamespace hash {\n\nnamespace detail {\n\nFOLLY_ALWAYS_INLINE constexpr std::uint64_t murmurHash64ShiftMix(\n    std::uint64_t v) {\n  constexpr std::uint64_t kShift = 47;\n  return v ^ (v >> kShift);\n}\n\n} // namespace detail\n\n/*\n * Implementation of MurmurHash2 hashing algorithm for 64-bit\n * platforms.\n *\n * https://en.wikipedia.org/wiki/MurmurHash\n */\nconstexpr std::uint64_t murmurHash64(\n    const char* key, std::size_t len, std::uint64_t seed) noexcept {\n  constexpr std::uint64_t kMul = 0xc6a4a7935bd1e995UL;\n\n  std::uint64_t hash = seed ^ (len * kMul);\n\n  const char* beg = key;\n  const char* end = beg + (len & ~0x7);\n  const std::size_t tail = len & 0x7;\n\n  for (const char* p = beg; p != end; p += 8) {\n    const std::uint64_t k = constexprLoadUnaligned<std::uint64_t>(p);\n    hash = (hash ^ detail::murmurHash64ShiftMix(k * kMul) * kMul) * kMul;\n  }\n\n  if (tail != 0) {\n    const std::uint64_t k =\n        constexprPartialLoadUnaligned<std::uint64_t>(end, tail);\n    hash ^= k;\n    hash *= kMul;\n  }\n\n  hash = detail::murmurHash64ShiftMix(hash) * kMul;\n  hash = detail::murmurHash64ShiftMix(hash);\n\n  return hash;\n}\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/SpookyHashV1.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Spooky Hash\n// A 128-bit noncryptographic hash, for checksums and table lookup\n// By Bob Jenkins.  Public domain.\n//   Oct 31 2010: published framework, disclaimer ShortHash isn't right\n//   Nov 7 2010: disabled ShortHash\n//   Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again\n//   April 10 2012: buffer overflow on platforms without unaligned reads\n//   July 12 2012: was passing out variables in final to in/out in short\n//   July 30 2012: I reintroduced the buffer overflow\n\n#include <folly/hash/SpookyHashV1.h>\n\n#include <cstring>\n\n#define ALLOW_UNALIGNED_READS 1\n\nnamespace folly {\nnamespace hash {\n\n// clang-format off\n\n//\n// short hash ... it could be used on any message,\n// but it's used by Spooky just for short messages.\n//\nvoid SpookyHashV1::Short(\n    const void* message,\n    size_t length,\n    uint64_t *hash1,\n    uint64_t *hash2)\n{\n    uint64_t buf[2*sc_numVars];\n    union\n    {\n        const uint8_t *p8;\n        uint32_t *p32;\n        uint64_t *p64;\n        size_t i;\n    } u;\n\n    u.p8 = (const uint8_t *)message;\n\n    if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))\n    {\n        memcpy(buf, message, length);\n        u.p64 = buf;\n    }\n\n    size_t remainder = length%32;\n    uint64_t a=*hash1;\n    uint64_t b=*hash2;\n    uint64_t c=sc_const;\n    uint64_t d=sc_const;\n\n    if (length > 15)\n    {\n        const uint64_t *end = u.p64 + (length/32)*4;\n\n        // handle all complete sets of 32 bytes\n        for (; u.p64 < end; u.p64 += 4)\n        {\n            c += u.p64[0];\n            d += u.p64[1];\n            ShortMix(a,b,c,d);\n            a += u.p64[2];\n            b += u.p64[3];\n        }\n\n        //Handle the case of 16+ remaining bytes.\n        if (remainder >= 16)\n        {\n            c += u.p64[0];\n            d += u.p64[1];\n            ShortMix(a,b,c,d);\n            u.p64 += 2;\n            remainder -= 16;\n        }\n    }\n\n    // Handle the last 0..15 bytes, and its length\n    d = ((uint64_t)length) << 56;\n    switch (remainder)\n    {\n    case 15:\n        d += ((uint64_t)u.p8[14]) << 48;\n        [[fallthrough]];\n    case 14:\n        d += ((uint64_t)u.p8[13]) << 40;\n        [[fallthrough]];\n    case 13:\n        d += ((uint64_t)u.p8[12]) << 32;\n        [[fallthrough]];\n    case 12:\n        d += u.p32[2];\n        c += u.p64[0];\n        break;\n    case 11:\n        d += ((uint64_t)u.p8[10]) << 16;\n        [[fallthrough]];\n    case 10:\n        d += ((uint64_t)u.p8[9]) << 8;\n        [[fallthrough]];\n    case 9:\n        d += (uint64_t)u.p8[8];\n        [[fallthrough]];\n    case 8:\n        c += u.p64[0];\n        break;\n    case 7:\n        c += ((uint64_t)u.p8[6]) << 48;\n        [[fallthrough]];\n    case 6:\n        c += ((uint64_t)u.p8[5]) << 40;\n        [[fallthrough]];\n    case 5:\n        c += ((uint64_t)u.p8[4]) << 32;\n        [[fallthrough]];\n    case 4:\n        c += u.p32[0];\n        break;\n    case 3:\n        c += ((uint64_t)u.p8[2]) << 16;\n        [[fallthrough]];\n    case 2:\n        c += ((uint64_t)u.p8[1]) << 8;\n        [[fallthrough]];\n    case 1:\n        c += (uint64_t)u.p8[0];\n        break;\n    case 0:\n        c += sc_const;\n        d += sc_const;\n    }\n    ShortEnd(a,b,c,d);\n    *hash1 = a;\n    *hash2 = b;\n}\n\n\n\n\n// do the whole hash in one call\nvoid SpookyHashV1::Hash128(\n    const void *message,\n    size_t length,\n    uint64_t *hash1,\n    uint64_t *hash2)\n{\n    if (length < sc_bufSize)\n    {\n        Short(message, length, hash1, hash2);\n        return;\n    }\n\n    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;\n    uint64_t buf[sc_numVars];\n    uint64_t *end;\n    union\n    {\n        const uint8_t *p8;\n        uint64_t *p64;\n        size_t i;\n    } u;\n    size_t remainder;\n\n    h0=h3=h6=h9  = *hash1;\n    h1=h4=h7=h10 = *hash2;\n    h2=h5=h8=h11 = sc_const;\n\n    u.p8 = (const uint8_t *)message;\n    end = u.p64 + (length/sc_blockSize)*sc_numVars;\n\n    // handle all whole sc_blockSize blocks of bytes\n    if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))\n    {\n        while (u.p64 < end)\n        {\n            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n    else\n    {\n        while (u.p64 < end)\n        {\n            memcpy(buf, u.p64, sc_blockSize);\n            Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n\n    // handle the last partial block of sc_blockSize bytes\n    remainder = (length - ((const uint8_t *)end-(const uint8_t *)message));\n    memcpy(buf, end, remainder);\n    memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder);\n    ((uint8_t*)buf)[sc_blockSize - 1] = uint8_t(remainder);\n    Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n\n    // do some final mixing\n    End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n    *hash1 = h0;\n    *hash2 = h1;\n}\n\n\n\n// init spooky state\nvoid SpookyHashV1::Init(uint64_t seed1, uint64_t seed2)\n{\n    m_length = 0;\n    m_remainder = 0;\n    m_state[0] = seed1;\n    m_state[1] = seed2;\n}\n\n\n// add a message fragment to the state\nvoid SpookyHashV1::Update(const void *message, size_t length)\n{\n    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;\n    size_t newLength = length + m_remainder;\n    uint8_t  remainder;\n    union\n    {\n        const uint8_t *p8;\n        uint64_t *p64;\n        size_t i;\n    } u;\n    const uint64_t *end;\n\n    // Is this message fragment too short?  If it is, stuff it away.\n    if (newLength < sc_bufSize)\n    {\n        memcpy(&((uint8_t *)m_data)[m_remainder], message, length);\n        m_length = length + m_length;\n        m_remainder = (uint8_t)newLength;\n        return;\n    }\n\n    // init the variables\n    if (m_length < sc_bufSize)\n    {\n        h0=h3=h6=h9  = m_state[0];\n        h1=h4=h7=h10 = m_state[1];\n        h2=h5=h8=h11 = sc_const;\n    }\n    else\n    {\n        h0 = m_state[0];\n        h1 = m_state[1];\n        h2 = m_state[2];\n        h3 = m_state[3];\n        h4 = m_state[4];\n        h5 = m_state[5];\n        h6 = m_state[6];\n        h7 = m_state[7];\n        h8 = m_state[8];\n        h9 = m_state[9];\n        h10 = m_state[10];\n        h11 = m_state[11];\n    }\n    m_length = length + m_length;\n\n    // if we've got anything stuffed away, use it now\n    if (m_remainder)\n    {\n        uint8_t prefix = sc_bufSize-m_remainder;\n        memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix);\n        u.p64 = m_data;\n        Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        u.p8 = ((const uint8_t *)message) + prefix;\n        length -= prefix;\n    }\n    else\n    {\n        u.p8 = (const uint8_t *)message;\n    }\n\n    // handle all whole blocks of sc_blockSize bytes\n    end = u.p64 + (length/sc_blockSize)*sc_numVars;\n    remainder = (uint8_t)(length-((const uint8_t *)end-u.p8));\n    if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)\n    {\n        while (u.p64 < end)\n        {\n            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n    else\n    {\n        while (u.p64 < end)\n        {\n            memcpy(m_data, u.p8, sc_blockSize);\n            Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n\n    // stuff away the last few bytes\n    m_remainder = remainder;\n    memcpy(m_data, end, remainder);\n\n    // stuff away the variables\n    m_state[0] = h0;\n    m_state[1] = h1;\n    m_state[2] = h2;\n    m_state[3] = h3;\n    m_state[4] = h4;\n    m_state[5] = h5;\n    m_state[6] = h6;\n    m_state[7] = h7;\n    m_state[8] = h8;\n    m_state[9] = h9;\n    m_state[10] = h10;\n    m_state[11] = h11;\n}\n\n\n// report the hash for the concatenation of all message fragments so far\nvoid SpookyHashV1::Final(uint64_t *hash1, uint64_t *hash2)\n{\n    // init the variables\n    if (m_length < sc_bufSize)\n    {\n        *hash1 = m_state[0];\n        *hash2 = m_state[1];\n        Short( m_data, m_length, hash1, hash2);\n        return;\n    }\n\n    auto data = (const uint64_t *)m_data;\n    uint8_t remainder = m_remainder;\n\n    uint64_t h0 = m_state[0];\n    uint64_t h1 = m_state[1];\n    uint64_t h2 = m_state[2];\n    uint64_t h3 = m_state[3];\n    uint64_t h4 = m_state[4];\n    uint64_t h5 = m_state[5];\n    uint64_t h6 = m_state[6];\n    uint64_t h7 = m_state[7];\n    uint64_t h8 = m_state[8];\n    uint64_t h9 = m_state[9];\n    uint64_t h10 = m_state[10];\n    uint64_t h11 = m_state[11];\n\n    if (remainder >= sc_blockSize)\n    {\n        // m_data can contain two blocks; handle any whole first block\n        Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        data += sc_numVars;\n        remainder -= sc_blockSize;\n    }\n\n    // mix in the last partial block, and the length mod sc_blockSize\n    memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder));\n\n    ((uint8_t *)data)[sc_blockSize-1] = remainder;\n    Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n\n    // do some final mixing\n    End(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n\n    *hash1 = h0;\n    *hash2 = h1;\n}\n\n// clang-format on\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/SpookyHashV1.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is version 1 of SpookyHash, incompatible with version 2.\n//\n// SpookyHash: a 128-bit noncryptographic hash function\n// By Bob Jenkins, public domain\n//   Oct 31 2010: alpha, framework + SpookyHash::Mix appears right\n//   Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right\n//   Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas\n//   Feb  2 2012: production, same bits as beta\n//   Feb  5 2012: adjusted definitions of uint* to be more portable\n//   Mar 30 2012: 3 bytes/cycle, not 4.  Alpha was 4 but wasn't thorough enough.\n//\n// Up to 3 bytes/cycle for long messages.  Reasonably fast for short messages.\n// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.\n//\n// This was developed for and tested on 64-bit x86-compatible processors.\n// It assumes the processor is little-endian.  There is a macro\n// controlling whether unaligned reads are allowed (by default they are).\n// This should be an equally good hash on big-endian machines, but it will\n// compute different results on them than on little-endian machines.\n//\n// Google's CityHash has similar specs to SpookyHash, and CityHash is faster\n// on some platforms.  MD4 and MD5 also have similar specs, but they are orders\n// of magnitude slower.  CRCs are two or more times slower, but unlike\n// SpookyHash, they have nice math for combining the CRCs of pieces to form\n// the CRCs of wholes.  There are also cryptographic hashes, but those are even\n// slower than MD5.\n//\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n\nnamespace folly {\nnamespace hash {\n\n// clang-format off\n\nclass SpookyHashV1\n{\npublic:\n    //\n    // SpookyHash: hash a single message in one call, produce 128-bit output\n    //\n    static void Hash128(\n        const void *message,  // message to hash\n        size_t length,        // length of message in bytes\n        uint64_t *hash1,      // in/out: in seed 1, out hash value 1\n        uint64_t *hash2);     // in/out: in seed 2, out hash value 2\n\n    //\n    // Hash64: hash a single message in one call, return 64-bit output\n    //\n    static uint64_t Hash64(\n        const void *message,  // message to hash\n        size_t length,        // length of message in bytes\n        uint64_t seed)        // seed\n    {\n        uint64_t hash1 = seed;\n        Hash128(message, length, &hash1, &seed);\n        return hash1;\n    }\n\n    //\n    // Hash32: hash a single message in one call, produce 32-bit output\n    //\n    static uint32_t Hash32(\n        const void *message,  // message to hash\n        size_t length,        // length of message in bytes\n        uint32_t seed)        // seed\n    {\n        uint64_t hash1 = seed, hash2 = seed;\n        Hash128(message, length, &hash1, &hash2);\n        return (uint32_t)hash1;\n    }\n\n    //\n    // Init: initialize the context of a SpookyHash\n    //\n    void Init(\n        uint64_t seed1,     // any 64-bit value will do, including 0\n        uint64_t seed2);    // different seeds produce independent hashes\n\n    //\n    // Update: add a piece of a message to a SpookyHash state\n    //\n    void Update(\n        const void *message,  // message fragment\n        size_t length);       // length of message fragment in bytes\n\n\n    //\n    // Final: compute the hash for the current SpookyHash state\n    //\n    // This does not modify the state; you can keep updating it afterward\n    //\n    // The result is the same as if SpookyHash() had been called with\n    // all the pieces concatenated into one message.\n    //\n    void Final(\n        uint64_t *hash1,  // out only: first 64 bits of hash value.\n        uint64_t *hash2); // out only: second 64 bits of hash value.\n\n    //\n    // left rotate a 64-bit value by k bytes\n    //\n    static inline uint64_t Rot64(uint64_t x, int k)\n    {\n        return (x << k) | (x >> (64 - k));\n    }\n\n    //\n    // This is used if the input is 96 bytes long or longer.\n    //\n    // The internal state is fully overwritten every 96 bytes.\n    // Every input bit appears to cause at least 128 bits of entropy\n    // before 96 other bytes are combined, when run forward or backward\n    //   For every input bit,\n    //   Two inputs differing in just that input bit\n    //   Where \"differ\" means xor or subtraction\n    //   And the base value is random\n    //   When run forward or backwards one Mix\n    // I tried 3 pairs of each; they all differed by at least 212 bits.\n    //\n    static inline void Mix(\n        const uint64_t *data,\n        uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3,\n        uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7,\n        uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11)\n    {\n      s0 += data[0];   s2 ^= s10; s11 ^= s0;  s0 = Rot64(s0,11);   s11 += s1;\n      s1 += data[1];   s3 ^= s11; s0 ^= s1;   s1 = Rot64(s1,32);   s0 += s2;\n      s2 += data[2];   s4 ^= s0;  s1 ^= s2;   s2 = Rot64(s2,43);   s1 += s3;\n      s3 += data[3];   s5 ^= s1;  s2 ^= s3;   s3 = Rot64(s3,31);   s2 += s4;\n      s4 += data[4];   s6 ^= s2;  s3 ^= s4;   s4 = Rot64(s4,17);   s3 += s5;\n      s5 += data[5];   s7 ^= s3;  s4 ^= s5;   s5 = Rot64(s5,28);   s4 += s6;\n      s6 += data[6];   s8 ^= s4;  s5 ^= s6;   s6 = Rot64(s6,39);   s5 += s7;\n      s7 += data[7];   s9 ^= s5;  s6 ^= s7;   s7 = Rot64(s7,57);   s6 += s8;\n      s8 += data[8];   s10 ^= s6; s7 ^= s8;   s8 = Rot64(s8,55);   s7 += s9;\n      s9 += data[9];   s11 ^= s7; s8 ^= s9;   s9 = Rot64(s9,54);   s8 += s10;\n      s10 += data[10]; s0 ^= s8;  s9 ^= s10;  s10 = Rot64(s10,22); s9 += s11;\n      s11 += data[11]; s1 ^= s9;  s10 ^= s11; s11 = Rot64(s11,46); s10 += s0;\n    }\n\n    //\n    // Mix all 12 inputs together so that h0, h1 are a hash of them all.\n    //\n    // For two inputs differing in just the input bits\n    // Where \"differ\" means xor or subtraction\n    // And the base value is random, or a counting value starting at that bit\n    // The final result will have each bit of h0, h1 flip\n    // For every input bit,\n    // with probability 50 +- .3%\n    // For every pair of input bits,\n    // with probability 50 +- 3%\n    //\n    // This does not rely on the last Mix() call having already mixed some.\n    // Two iterations was almost good enough for a 64-bit result, but a\n    // 128-bit result is reported, so End() does three iterations.\n    //\n    static inline void EndPartial(\n        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,\n        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,\n        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)\n    {\n        h11+= h1;    h2 ^= h11;   h1 = Rot64(h1,44);\n        h0 += h2;    h3 ^= h0;    h2 = Rot64(h2,15);\n        h1 += h3;    h4 ^= h1;    h3 = Rot64(h3,34);\n        h2 += h4;    h5 ^= h2;    h4 = Rot64(h4,21);\n        h3 += h5;    h6 ^= h3;    h5 = Rot64(h5,38);\n        h4 += h6;    h7 ^= h4;    h6 = Rot64(h6,33);\n        h5 += h7;    h8 ^= h5;    h7 = Rot64(h7,10);\n        h6 += h8;    h9 ^= h6;    h8 = Rot64(h8,13);\n        h7 += h9;    h10^= h7;    h9 = Rot64(h9,38);\n        h8 += h10;   h11^= h8;    h10= Rot64(h10,53);\n        h9 += h11;   h0 ^= h9;    h11= Rot64(h11,42);\n        h10+= h0;    h1 ^= h10;   h0 = Rot64(h0,54);\n    }\n\n    static inline void End(\n        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,\n        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,\n        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)\n    {\n        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n    }\n\n    //\n    // The goal is for each bit of the input to expand into 128 bits of\n    //   apparent entropy before it is fully overwritten.\n    // n trials both set and cleared at least m bits of h0 h1 h2 h3\n    //   n: 2   m: 29\n    //   n: 3   m: 46\n    //   n: 4   m: 57\n    //   n: 5   m: 107\n    //   n: 6   m: 146\n    //   n: 7   m: 152\n    // when run forwards or backwards\n    // for all 1-bit and 2-bit diffs\n    // with diffs defined by either xor or subtraction\n    // with a base of all zeros plus a counter, or plus another bit, or random\n    //\n    static inline void ShortMix(uint64_t &h0, uint64_t &h1,\n                                uint64_t &h2, uint64_t &h3)\n    {\n        h2 = Rot64(h2,50);  h2 += h3;  h0 ^= h2;\n        h3 = Rot64(h3,52);  h3 += h0;  h1 ^= h3;\n        h0 = Rot64(h0,30);  h0 += h1;  h2 ^= h0;\n        h1 = Rot64(h1,41);  h1 += h2;  h3 ^= h1;\n        h2 = Rot64(h2,54);  h2 += h3;  h0 ^= h2;\n        h3 = Rot64(h3,48);  h3 += h0;  h1 ^= h3;\n        h0 = Rot64(h0,38);  h0 += h1;  h2 ^= h0;\n        h1 = Rot64(h1,37);  h1 += h2;  h3 ^= h1;\n        h2 = Rot64(h2,62);  h2 += h3;  h0 ^= h2;\n        h3 = Rot64(h3,34);  h3 += h0;  h1 ^= h3;\n        h0 = Rot64(h0,5);   h0 += h1;  h2 ^= h0;\n        h1 = Rot64(h1,36);  h1 += h2;  h3 ^= h1;\n    }\n\n    //\n    // Mix all 4 inputs together so that h0, h1 are a hash of them all.\n    //\n    // For two inputs differing in just the input bits\n    // Where \"differ\" means xor or subtraction\n    // And the base value is random, or a counting value starting at that bit\n    // The final result will have each bit of h0, h1 flip\n    // For every input bit,\n    // with probability 50 +- .3% (it is probably better than that)\n    // For every pair of input bits,\n    // with probability 50 +- .75% (the worst case is approximately that)\n    //\n    static inline void ShortEnd(uint64_t &h0, uint64_t &h1,\n                                uint64_t &h2, uint64_t &h3)\n    {\n        h3 ^= h2;  h2 = Rot64(h2,15);  h3 += h2;\n        h0 ^= h3;  h3 = Rot64(h3,52);  h0 += h3;\n        h1 ^= h0;  h0 = Rot64(h0,26);  h1 += h0;\n        h2 ^= h1;  h1 = Rot64(h1,51);  h2 += h1;\n        h3 ^= h2;  h2 = Rot64(h2,28);  h3 += h2;\n        h0 ^= h3;  h3 = Rot64(h3,9);   h0 += h3;\n        h1 ^= h0;  h0 = Rot64(h0,47);  h1 += h0;\n        h2 ^= h1;  h1 = Rot64(h1,54);  h2 += h1;\n        h3 ^= h2;  h2 = Rot64(h2,32);  h3 += h2;\n        h0 ^= h3;  h3 = Rot64(h3,25);  h0 += h3;\n        h1 ^= h0;  h0 = Rot64(h0,63);  h1 += h0;\n    }\n\nprivate:\n\n    //\n    // Short is used for messages under 192 bytes in length\n    // Short has a low startup cost, the normal mode is good for long\n    // keys, the cost crossover is at about 192 bytes.  The two modes were\n    // held to the same quality bar.\n    //\n    static void Short(\n        const void *message,  // message (byte array, not necessarily aligned)\n        size_t length,        // length of message (in bytes)\n        uint64_t *hash1,      // in/out: in the seed, out the hash value\n        uint64_t *hash2);     // in/out: in the seed, out the hash value\n\n    // number of uint64_t's in internal state\n    static const size_t sc_numVars = 12;\n\n    // size of the internal state\n    static const size_t sc_blockSize = sc_numVars*8;\n\n    // size of buffer of unhashed data, in bytes\n    static const size_t sc_bufSize = 2*sc_blockSize;\n\n    //\n    // sc_const: a constant which:\n    //  * is not zero\n    //  * is odd\n    //  * is a not-very-regular mix of 1's and 0's\n    //  * does not need any other special mathematical properties\n    //\n    static const uint64_t sc_const = 0xdeadbeefdeadbeefULL;\n\n    uint64_t m_data[2*sc_numVars];  // unhashed data, for partial messages\n    uint64_t m_state[sc_numVars];   // internal state of the hash\n    size_t m_length;                // total length of the input so far\n    uint8_t  m_remainder;           // length of unhashed data stashed in m_data\n};\n\n// clang-format on\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/SpookyHashV2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Spooky Hash\n// A 128-bit noncryptographic hash, for checksums and table lookup\n// By Bob Jenkins.  Public domain.\n//   Oct 31 2010: published framework, disclaimer ShortHash isn't right\n//   Nov 7 2010: disabled ShortHash\n//   Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again\n//   April 10 2012: buffer overflow on platforms without unaligned reads\n//   July 12 2012: was passing out variables in final to in/out in short\n//   July 30 2012: I reintroduced the buffer overflow\n//   August 5 2012: SpookyV2: d = should be d += in short hash, and remove\n//                  extra mix from long hash\n\n#include <folly/hash/SpookyHashV2.h>\n\n#include <cstring>\n\n#include <folly/Portability.h>\n\nnamespace folly {\nnamespace hash {\n\n// clang-format off\n\n//\n// short hash ... it could be used on any message,\n// but it's used by Spooky just for short messages.\n//\nvoid SpookyHashV2::Short(\n    const void *message,\n    size_t length,\n    uint64_t *hash1,\n    uint64_t *hash2)\n{\n    uint64_t buf[2*sc_numVars];\n    union\n    {\n        const uint8_t *p8;\n        uint32_t *p32;\n        uint64_t *p64;\n        size_t i;\n    } u;\n\n    u.p8 = (const uint8_t *)message;\n\n    if (!kHasUnalignedAccess && (u.i & 0x7))\n    {\n        memcpy(buf, message, length);\n        u.p64 = buf;\n    }\n\n    size_t remainder = length%32;\n    uint64_t a=*hash1;\n    uint64_t b=*hash2;\n    uint64_t c=sc_const;\n    uint64_t d=sc_const;\n\n    if (length > 15)\n    {\n        const uint64_t *end = u.p64 + (length/32)*4;\n\n        // handle all complete sets of 32 bytes\n        for (; u.p64 < end; u.p64 += 4)\n        {\n            c += Read8(u.p64, 0);\n            d += Read8(u.p64, 1);\n            ShortMix(a,b,c,d);\n            a += Read8(u.p64, 2);\n            b += Read8(u.p64, 3);\n        }\n\n        //Handle the case of 16+ remaining bytes.\n        if (remainder >= 16)\n        {\n            c += Read8(u.p64, 0);\n            d += Read8(u.p64, 1);\n            ShortMix(a,b,c,d);\n            u.p64 += 2;\n            remainder -= 16;\n        }\n    }\n\n    // Handle the last 0..15 bytes, and its length\n    d += ((uint64_t)length) << 56;\n    switch (remainder)\n    {\n    case 15:\n        d += ((uint64_t)u.p8[14]) << 48;\n        [[fallthrough]];\n    case 14:\n        d += ((uint64_t)u.p8[13]) << 40;\n        [[fallthrough]];\n    case 13:\n        d += ((uint64_t)u.p8[12]) << 32;\n        [[fallthrough]];\n    case 12:\n        d += u.p32[2];\n        c += u.p64[0];\n        break;\n    case 11:\n        d += ((uint64_t)u.p8[10]) << 16;\n        [[fallthrough]];\n    case 10:\n        d += ((uint64_t)u.p8[9]) << 8;\n        [[fallthrough]];\n    case 9:\n        d += (uint64_t)u.p8[8];\n        [[fallthrough]];\n    case 8:\n        c += u.p64[0];\n        break;\n    case 7:\n        c += ((uint64_t)u.p8[6]) << 48;\n        [[fallthrough]];\n    case 6:\n        c += ((uint64_t)u.p8[5]) << 40;\n        [[fallthrough]];\n    case 5:\n        c += ((uint64_t)u.p8[4]) << 32;\n        [[fallthrough]];\n    case 4:\n        c += u.p32[0];\n        break;\n    case 3:\n        c += ((uint64_t)u.p8[2]) << 16;\n        [[fallthrough]];\n    case 2:\n        c += ((uint64_t)u.p8[1]) << 8;\n        [[fallthrough]];\n    case 1:\n        c += (uint64_t)u.p8[0];\n        break;\n    case 0:\n        c += sc_const;\n        d += sc_const;\n    }\n    ShortEnd(a,b,c,d);\n    *hash1 = a;\n    *hash2 = b;\n}\n\n\n\n\n// do the whole hash in one call\nvoid SpookyHashV2::Hash128(\n    const void *message,\n    size_t length,\n    uint64_t *hash1,\n    uint64_t *hash2)\n{\n    if (length < sc_bufSize)\n    {\n        Short(message, length, hash1, hash2);\n        return;\n    }\n\n    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;\n    uint64_t buf[sc_numVars];\n    uint64_t *end;\n    union\n    {\n        const uint8_t *p8;\n        uint64_t *p64;\n        size_t i;\n    } u;\n    size_t remainder;\n\n    h0=h3=h6=h9  = *hash1;\n    h1=h4=h7=h10 = *hash2;\n    h2=h5=h8=h11 = sc_const;\n\n    u.p8 = (const uint8_t *)message;\n    end = u.p64 + (length/sc_blockSize)*sc_numVars;\n\n    // handle all whole sc_blockSize blocks of bytes\n    if (kHasUnalignedAccess || ((u.i & 0x7) == 0))\n    {\n        while (u.p64 < end)\n        {\n            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n    else\n    {\n        while (u.p64 < end)\n        {\n            memcpy(buf, u.p64, sc_blockSize);\n            Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n\n    // handle the last partial block of sc_blockSize bytes\n    remainder = (length - ((const uint8_t *)end-(const uint8_t *)message));\n    memcpy(buf, end, remainder);\n    memset(((uint8_t *)buf)+remainder, 0, sc_blockSize-remainder);\n    ((uint8_t*)buf)[sc_blockSize - 1] = uint8_t(remainder);\n\n    // do some final mixing\n    End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n    *hash1 = h0;\n    *hash2 = h1;\n}\n\n\n\n// init spooky state\nvoid SpookyHashV2::Init(uint64_t seed1, uint64_t seed2)\n{\n    m_length = 0;\n    m_remainder = 0;\n    m_state[0] = seed1;\n    m_state[1] = seed2;\n}\n\n\n// add a message fragment to the state\nvoid SpookyHashV2::Update(const void *message, size_t length)\n{\n    uint64_t h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;\n    size_t newLength = length + m_remainder;\n    uint8_t  remainder;\n    union\n    {\n        const uint8_t *p8;\n        uint64_t *p64;\n        size_t i;\n    } u;\n    const uint64_t *end;\n\n    // Is this message fragment too short?  If it is, stuff it away.\n    if (newLength < sc_bufSize)\n    {\n        memcpy(&((uint8_t *)m_data)[m_remainder], message, length);\n        m_length = length + m_length;\n        m_remainder = (uint8_t)newLength;\n        return;\n    }\n\n    // init the variables\n    if (m_length < sc_bufSize)\n    {\n        h0=h3=h6=h9  = m_state[0];\n        h1=h4=h7=h10 = m_state[1];\n        h2=h5=h8=h11 = sc_const;\n    }\n    else\n    {\n        h0 = m_state[0];\n        h1 = m_state[1];\n        h2 = m_state[2];\n        h3 = m_state[3];\n        h4 = m_state[4];\n        h5 = m_state[5];\n        h6 = m_state[6];\n        h7 = m_state[7];\n        h8 = m_state[8];\n        h9 = m_state[9];\n        h10 = m_state[10];\n        h11 = m_state[11];\n    }\n    m_length = length + m_length;\n\n    // if we've got anything stuffed away, use it now\n    if (m_remainder)\n    {\n        uint8_t prefix = sc_bufSize-m_remainder;\n        memcpy(&(((uint8_t *)m_data)[m_remainder]), message, prefix);\n        u.p64 = m_data;\n        Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        u.p8 = ((const uint8_t *)message) + prefix;\n        length -= prefix;\n    }\n    else\n    {\n        u.p8 = (const uint8_t *)message;\n    }\n\n    // handle all whole blocks of sc_blockSize bytes\n    end = u.p64 + (length/sc_blockSize)*sc_numVars;\n    remainder = (uint8_t)(length-((const uint8_t *)end-u.p8));\n    if (kHasUnalignedAccess || (u.i & 0x7) == 0)\n    {\n        while (u.p64 < end)\n        {\n            Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n    else\n    {\n        while (u.p64 < end)\n        {\n            memcpy(m_data, u.p8, sc_blockSize);\n            Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n            u.p64 += sc_numVars;\n        }\n    }\n\n    // stuff away the last few bytes\n    m_remainder = remainder;\n    memcpy(m_data, end, remainder);\n\n    // stuff away the variables\n    m_state[0] = h0;\n    m_state[1] = h1;\n    m_state[2] = h2;\n    m_state[3] = h3;\n    m_state[4] = h4;\n    m_state[5] = h5;\n    m_state[6] = h6;\n    m_state[7] = h7;\n    m_state[8] = h8;\n    m_state[9] = h9;\n    m_state[10] = h10;\n    m_state[11] = h11;\n}\n\n\n// report the hash for the concatenation of all message fragments so far\nvoid SpookyHashV2::Final(uint64_t *hash1, uint64_t *hash2) const\n{\n    // init the variables\n    if (m_length < sc_bufSize)\n    {\n        *hash1 = m_state[0];\n        *hash2 = m_state[1];\n        Short( m_data, m_length, hash1, hash2);\n        return;\n    }\n\n    uint64_t buf[2*sc_numVars];\n    memcpy(buf, m_data, sizeof(buf));\n    uint64_t *data = buf;\n    uint8_t remainder = m_remainder;\n\n    uint64_t h0 = m_state[0];\n    uint64_t h1 = m_state[1];\n    uint64_t h2 = m_state[2];\n    uint64_t h3 = m_state[3];\n    uint64_t h4 = m_state[4];\n    uint64_t h5 = m_state[5];\n    uint64_t h6 = m_state[6];\n    uint64_t h7 = m_state[7];\n    uint64_t h8 = m_state[8];\n    uint64_t h9 = m_state[9];\n    uint64_t h10 = m_state[10];\n    uint64_t h11 = m_state[11];\n\n    if (remainder >= sc_blockSize)\n    {\n        // m_data can contain two blocks; handle any whole first block\n        Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        data += sc_numVars;\n        remainder -= sc_blockSize;\n    }\n\n    // mix in the last partial block, and the length mod sc_blockSize\n    memset(&((uint8_t *)data)[remainder], 0, (sc_blockSize-remainder));\n\n    ((uint8_t *)data)[sc_blockSize-1] = remainder;\n\n    // do some final mixing\n    End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n\n    *hash1 = h0;\n    *hash2 = h1;\n}\n\n// clang-format on\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/SpookyHashV2.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This is version 2 of SpookyHash, incompatible with version 1.\n//\n// SpookyHash: a 128-bit noncryptographic hash function\n// By Bob Jenkins, public domain\n//   Oct 31 2010: alpha, framework + SpookyHash::Mix appears right\n//   Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right\n//   Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas\n//   Feb  2 2012: production, same bits as beta\n//   Feb  5 2012: adjusted definitions of uint* to be more portable\n//   Mar 30 2012: 3 bytes/cycle, not 4.  Alpha was 4 but wasn't thorough enough.\n//   August 5 2012: SpookyV2 (different results)\n//\n// Up to 3 bytes/cycle for long messages.  Reasonably fast for short messages.\n// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.\n//\n// This was developed for and tested on 64-bit x86-compatible processors.\n// It assumes the processor is little-endian.  There is a macro\n// controlling whether unaligned reads are allowed (by default they are).\n// This should be an equally good hash on big-endian machines, but it will\n// compute different results on them than on little-endian machines.\n//\n// Google's CityHash has similar specs to SpookyHash, and CityHash is faster\n// on new Intel boxes.  MD4 and MD5 also have similar specs, but they are orders\n// of magnitude slower.  CRCs are two or more times slower, but unlike\n// SpookyHash, they have nice math for combining the CRCs of pieces to form\n// the CRCs of wholes.  There are also cryptographic hashes, but those are even\n// slower than MD5.\n//\n\n#pragma once\n\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n\n#include <folly/CPortability.h>\n#include <folly/Portability.h>\n#include <folly/lang/CString.h>\n\nnamespace folly {\nnamespace hash {\n\n// clang-format off\n\nclass SpookyHashV2\n{\npublic:\n    //\n    // SpookyHash: hash a single message in one call, produce 128-bit output\n    //\n    static void Hash128(\n        const void *message,  // message to hash\n        size_t length,        // length of message in bytes\n        uint64_t *hash1,        // in/out: in seed 1, out hash value 1\n        uint64_t *hash2);       // in/out: in seed 2, out hash value 2\n\n    //\n    // Hash64: hash a single message in one call, return 64-bit output\n    //\n    static uint64_t Hash64(\n        const void *message,  // message to hash\n        size_t length,        // length of message in bytes\n        uint64_t seed)          // seed\n    {\n        uint64_t hash1 = seed;\n        Hash128(message, length, &hash1, &seed);\n        return hash1;\n    }\n\n    //\n    // Hash32: hash a single message in one call, produce 32-bit output\n    //\n    static uint32_t Hash32(\n        const void *message,  // message to hash\n        size_t length,        // length of message in bytes\n        uint32_t seed)          // seed\n    {\n        uint64_t hash1 = seed, hash2 = seed;\n        Hash128(message, length, &hash1, &hash2);\n        return (uint32_t)hash1;\n    }\n\n    //\n    // Init: initialize the context of a SpookyHash\n    //\n    void Init(\n        uint64_t seed1,       // any 64-bit value will do, including 0\n        uint64_t seed2);      // different seeds produce independent hashes\n\n    //\n    // Update: add a piece of a message to a SpookyHash state\n    //\n    void Update(\n        const void *message,  // message fragment\n        size_t length);       // length of message fragment in bytes\n\n\n    //\n    // Final: compute the hash for the current SpookyHash state\n    //\n    // This does not modify the state; you can keep updating it afterward\n    //\n    // The result is the same as if SpookyHash() had been called with\n    // all the pieces concatenated into one message.\n    //\n    void Final(\n        uint64_t *hash1,          // out only: first 64 bits of hash value.\n        uint64_t *hash2) const;   // out only: second 64 bits of hash value.\n\n    //\n    // left rotate a 64-bit value by k bytes\n    //\n    static inline uint64_t Rot64(uint64_t x, int k)\n    {\n        return (x << k) | (x >> (64 - k));\n    }\n\n    //\n    // This is used if the input is 96 bytes long or longer.\n    //\n    // The internal state is fully overwritten every 96 bytes.\n    // Every input bit appears to cause at least 128 bits of entropy\n    // before 96 other bytes are combined, when run forward or backward\n    //   For every input bit,\n    //   Two inputs differing in just that input bit\n    //   Where \"differ\" means xor or subtraction\n    //   And the base value is random\n    //   When run forward or backwards one Mix\n    // I tried 3 pairs of each; they all differed by at least 212 bits.\n    //\n    static inline void Mix(\n        const uint64_t *data,\n        uint64_t &s0, uint64_t &s1, uint64_t &s2, uint64_t &s3,\n        uint64_t &s4, uint64_t &s5, uint64_t &s6, uint64_t &s7,\n        uint64_t &s8, uint64_t &s9, uint64_t &s10,uint64_t &s11)\n    {\n      auto read = [&](auto off) { return Read8(data, off); };\n      s0 += read(0);   s2 ^= s10; s11 ^= s0;  s0 = Rot64(s0,11);   s11 += s1;\n      s1 += read(1);   s3 ^= s11; s0 ^= s1;   s1 = Rot64(s1,32);   s0 += s2;\n      s2 += read(2);   s4 ^= s0;  s1 ^= s2;   s2 = Rot64(s2,43);   s1 += s3;\n      s3 += read(3);   s5 ^= s1;  s2 ^= s3;   s3 = Rot64(s3,31);   s2 += s4;\n      s4 += read(4);   s6 ^= s2;  s3 ^= s4;   s4 = Rot64(s4,17);   s3 += s5;\n      s5 += read(5);   s7 ^= s3;  s4 ^= s5;   s5 = Rot64(s5,28);   s4 += s6;\n      s6 += read(6);   s8 ^= s4;  s5 ^= s6;   s6 = Rot64(s6,39);   s5 += s7;\n      s7 += read(7);   s9 ^= s5;  s6 ^= s7;   s7 = Rot64(s7,57);   s6 += s8;\n      s8 += read(8);   s10 ^= s6; s7 ^= s8;   s8 = Rot64(s8,55);   s7 += s9;\n      s9 += read(9);   s11 ^= s7; s8 ^= s9;   s9 = Rot64(s9,54);   s8 += s10;\n      s10 += read(10); s0 ^= s8;  s9 ^= s10;  s10 = Rot64(s10,22); s9 += s11;\n      s11 += read(11); s1 ^= s9;  s10 ^= s11; s11 = Rot64(s11,46); s10 += s0;\n    }\n\n    //\n    // Mix all 12 inputs together so that h0, h1 are a hash of them all.\n    //\n    // For two inputs differing in just the input bits\n    // Where \"differ\" means xor or subtraction\n    // And the base value is random, or a counting value starting at that bit\n    // The final result will have each bit of h0, h1 flip\n    // For every input bit,\n    // with probability 50 +- .3%\n    // For every pair of input bits,\n    // with probability 50 +- 3%\n    //\n    // This does not rely on the last Mix() call having already mixed some.\n    // Two iterations was almost good enough for a 64-bit result, but a\n    // 128-bit result is reported, so End() does three iterations.\n    //\n    static inline void EndPartial(\n        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,\n        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,\n        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)\n    {\n        h11+= h1;    h2 ^= h11;   h1 = Rot64(h1,44);\n        h0 += h2;    h3 ^= h0;    h2 = Rot64(h2,15);\n        h1 += h3;    h4 ^= h1;    h3 = Rot64(h3,34);\n        h2 += h4;    h5 ^= h2;    h4 = Rot64(h4,21);\n        h3 += h5;    h6 ^= h3;    h5 = Rot64(h5,38);\n        h4 += h6;    h7 ^= h4;    h6 = Rot64(h6,33);\n        h5 += h7;    h8 ^= h5;    h7 = Rot64(h7,10);\n        h6 += h8;    h9 ^= h6;    h8 = Rot64(h8,13);\n        h7 += h9;    h10^= h7;    h9 = Rot64(h9,38);\n        h8 += h10;   h11^= h8;    h10= Rot64(h10,53);\n        h9 += h11;   h0 ^= h9;    h11= Rot64(h11,42);\n        h10+= h0;    h1 ^= h10;   h0 = Rot64(h0,54);\n    }\n\n    static inline void End(\n        const uint64_t *data,\n        uint64_t &h0, uint64_t &h1, uint64_t &h2, uint64_t &h3,\n        uint64_t &h4, uint64_t &h5, uint64_t &h6, uint64_t &h7,\n        uint64_t &h8, uint64_t &h9, uint64_t &h10,uint64_t &h11)\n    {\n        h0 += data[0];   h1 += data[1];   h2 += data[2];   h3 += data[3];\n        h4 += data[4];   h5 += data[5];   h6 += data[6];   h7 += data[7];\n        h8 += data[8];   h9 += data[9];   h10 += data[10]; h11 += data[11];\n        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n        EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);\n    }\n\n    //\n    // The goal is for each bit of the input to expand into 128 bits of\n    //   apparent entropy before it is fully overwritten.\n    // n trials both set and cleared at least m bits of h0 h1 h2 h3\n    //   n: 2   m: 29\n    //   n: 3   m: 46\n    //   n: 4   m: 57\n    //   n: 5   m: 107\n    //   n: 6   m: 146\n    //   n: 7   m: 152\n    // when run forwards or backwards\n    // for all 1-bit and 2-bit diffs\n    // with diffs defined by either xor or subtraction\n    // with a base of all zeros plus a counter, or plus another bit, or random\n    //\n    static inline void ShortMix(uint64_t &h0, uint64_t &h1,\n                                uint64_t &h2, uint64_t &h3)\n    {\n        h2 = Rot64(h2,50);  h2 += h3;  h0 ^= h2;\n        h3 = Rot64(h3,52);  h3 += h0;  h1 ^= h3;\n        h0 = Rot64(h0,30);  h0 += h1;  h2 ^= h0;\n        h1 = Rot64(h1,41);  h1 += h2;  h3 ^= h1;\n        h2 = Rot64(h2,54);  h2 += h3;  h0 ^= h2;\n        h3 = Rot64(h3,48);  h3 += h0;  h1 ^= h3;\n        h0 = Rot64(h0,38);  h0 += h1;  h2 ^= h0;\n        h1 = Rot64(h1,37);  h1 += h2;  h3 ^= h1;\n        h2 = Rot64(h2,62);  h2 += h3;  h0 ^= h2;\n        h3 = Rot64(h3,34);  h3 += h0;  h1 ^= h3;\n        h0 = Rot64(h0,5);   h0 += h1;  h2 ^= h0;\n        h1 = Rot64(h1,36);  h1 += h2;  h3 ^= h1;\n    }\n\n    //\n    // Mix all 4 inputs together so that h0, h1 are a hash of them all.\n    //\n    // For two inputs differing in just the input bits\n    // Where \"differ\" means xor or subtraction\n    // And the base value is random, or a counting value starting at that bit\n    // The final result will have each bit of h0, h1 flip\n    // For every input bit,\n    // with probability 50 +- .3% (it is probably better than that)\n    // For every pair of input bits,\n    // with probability 50 +- .75% (the worst case is approximately that)\n    //\n    static inline void ShortEnd(uint64_t &h0, uint64_t &h1,\n                                uint64_t &h2, uint64_t &h3)\n    {\n        h3 ^= h2;  h2 = Rot64(h2,15);  h3 += h2;\n        h0 ^= h3;  h3 = Rot64(h3,52);  h0 += h3;\n        h1 ^= h0;  h0 = Rot64(h0,26);  h1 += h0;\n        h2 ^= h1;  h1 = Rot64(h1,51);  h2 += h1;\n        h3 ^= h2;  h2 = Rot64(h2,28);  h3 += h2;\n        h0 ^= h3;  h3 = Rot64(h3,9);   h0 += h3;\n        h1 ^= h0;  h0 = Rot64(h0,47);  h1 += h0;\n        h2 ^= h1;  h1 = Rot64(h1,54);  h2 += h1;\n        h3 ^= h2;  h2 = Rot64(h2,32);  h3 += h2;\n        h0 ^= h3;  h3 = Rot64(h3,25);  h0 += h3;\n        h1 ^= h0;  h0 = Rot64(h0,63);  h1 += h0;\n    }\n\nprivate:\n\n    //\n    // Short is used for messages under 192 bytes in length\n    // Short has a low startup cost, the normal mode is good for long\n    // keys, the cost crossover is at about 192 bytes.  The two modes were\n    // held to the same quality bar.\n    //\n    static void Short(\n        const void *message,  // message (byte array, not necessarily aligned)\n        size_t length,        // length of message (in bytes)\n        uint64_t *hash1,        // in/out: in the seed, out the hash value\n        uint64_t *hash2);       // in/out: in the seed, out the hash value\n\n    //\n    // Helper to read 8 consecutive bytes from a buffer\n    // If the platform has unaligned access, may be called with unaligned buf\n    // Otherwise, must be called only with aligned buf\n    //\n    FOLLY_ALWAYS_INLINE static uint64_t Read8(const uint64_t* buf, size_t off) {\n      if constexpr (kHasUnalignedAccess) {\n        uint64_t out;\n        FOLLY_BUILTIN_MEMCPY(&out, buf + off, sizeof(out));\n        return out;\n      } else {\n        assert(0 == reinterpret_cast<uintptr_t>(buf) % sizeof(*buf));\n        return buf[off];\n      }\n    }\n\n    // number of uint64_t's in internal state\n    static constexpr size_t sc_numVars = 12;\n\n    // size of the internal state\n    static constexpr size_t sc_blockSize = sc_numVars*8;\n\n    // size of buffer of unhashed data, in bytes\n    static constexpr size_t sc_bufSize = 2*sc_blockSize;\n\n    //\n    // sc_const: a constant which:\n    //  * is not zero\n    //  * is odd\n    //  * is a not-very-regular mix of 1's and 0's\n    //  * does not need any other special mathematical properties\n    //\n    static constexpr uint64_t sc_const = 0xdeadbeefdeadbeefULL;\n\n    uint64_t m_data[2*sc_numVars];   // unhashed data, for partial messages\n    uint64_t m_state[sc_numVars];  // internal state of the hash\n    size_t m_length;             // total length of the input so far\n    uint8_t  m_remainder;          // length of unhashed data stashed in m_data\n};\n\n// clang-format on\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/UniqueHashKey.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/UniqueHashKey.h>\n\n#include <folly/portability/OpenSSL.h>\n#include <folly/portability/Unistd.h>\n\n#if defined(__linux__)\n#include <sys/auxv.h>\n#endif\n\n#if __has_include(<blake3.h>)\n#include <blake3.h>\n#endif\n\n#if __has_include(<xxh3.h>)\n#include <xxh3.h>\n#endif\n\nnamespace folly {\n\nnamespace detail {\n\n/// unique_hash_key_init_process_key_sha256\n///\n/// Cryptographically hashes auxval-random or pid, keyed by caller __func__. For\n/// each algorithm, for extra margin of safety, we want a different process-key.\n///\n/// Accepts fixed-sized 32-byte output only.\n///\n/// SHA256 is not keyed, but HMAC requires two rounds of hashing. So this uses\n/// the Secret Suffix MAC construction, which depends on the underlying hash\n/// function's collision resistance for its own collision resistance.\nvoid unique_hash_key_init_process_key_sha256(\n    char const* const context, span<uint8_t, SHA256_DIGEST_LENGTH> const out) {\n  auto const pid = getpid();\n  auto const pidkey = reinterpret_span_cast<uint8_t const>(span{&pid, 1});\n#if defined(__linux__)\n  constexpr size_t auxlen = 16;\n  auto const auxval = getauxval(AT_RANDOM);\n  // NOLINTNEXTLINE(performance-no-int-to-ptr)\n  auto const auxptr = reinterpret_cast<uint8_t const*>(auxval);\n  auto const auxkey = span<uint8_t const>{auxptr, auxlen};\n#endif\n  SHA256_CTX h{};\n  SHA256_Init(&h);\n  SHA256_Update(&h, context, std::strlen(context));\n  SHA256_Update(&h, pidkey.data(), pidkey.size()); // Secret Suffix MAC\n#if defined(__linux__)\n  SHA256_Update(&h, auxkey.data(), auxkey.size()); // Secret Suffix MAC\n#endif\n  SHA256_Final(out.data(), &h);\n}\n\nstruct unique_hash_key_algo_strong_sha256_init_t { // NOLINT\n  uint8_t key[SHA256_DIGEST_LENGTH] = {};\n};\n\nstatic unique_hash_key_algo_strong_sha256_init_t\nunique_hash_key_algo_strong_sha256_init_impl() {\n  unique_hash_key_algo_strong_sha256_init_t res{};\n  unique_hash_key_init_process_key_sha256(__func__, res.key);\n  return res;\n}\n\n// init blake3 hasher with key unique to this process\nstatic unique_hash_key_algo_strong_sha256_init_t const&\nunique_hash_key_algo_strong_sha256_init() {\n  static auto const object = unique_hash_key_algo_strong_sha256_init_impl();\n  return object;\n}\n\n#if __has_include(<blake3.h>)\n\n/// unique_hash_key_init_process_key_blake3\n///\n/// Cryptographically hashes auxval-random or pid, keyed by caller __func__. For\n/// each algorithm, for extra margin of safety, we want a different process-key.\n///\n/// Accepts arbitrarily-sized output, which is required by the xxh3 algo.\nvoid unique_hash_key_init_process_key_blake3(\n    char const* const context, span<uint8_t> const out) {\n#if defined(__linux__)\n  constexpr size_t auxlen = 16;\n  auto const auxval = getauxval(AT_RANDOM);\n  // NOLINTNEXTLINE(performance-no-int-to-ptr)\n  auto const auxptr = reinterpret_cast<uint8_t const*>(auxval);\n  auto const key = span<uint8_t const>{auxptr, auxlen};\n#else\n  auto const pid = getpid();\n  auto const key = reinterpret_span_cast<uint8_t const>(span{&pid, 1});\n#endif\n  blake3_hasher h{};\n  blake3_hasher_init_derive_key(&h, context);\n  blake3_hasher_update(&h, key.data(), key.size());\n  blake3_hasher_finalize(&h, out.data(), out.size());\n}\n\nstruct unique_hash_key_algo_strong_blake3_init_t { // NOLINT\n  uint8_t key[BLAKE3_KEY_LEN] = {};\n};\n\nstatic unique_hash_key_algo_strong_blake3_init_t\nunique_hash_key_algo_strong_blake3_init_impl() {\n  unique_hash_key_algo_strong_blake3_init_t res{};\n  unique_hash_key_init_process_key_blake3(__func__, res.key);\n  return res;\n}\n\n// init blake3 hasher with key unique to this process\nstatic unique_hash_key_algo_strong_blake3_init_t const&\nunique_hash_key_algo_strong_blake3_init() {\n  static auto const object = unique_hash_key_algo_strong_blake3_init_impl();\n  return object;\n}\n\n#if __has_include(<xxh3.h>)\n\nstruct unique_hash_key_algo_fast_xxh3_init_t { // NOLINT\n  uint8_t secret[XXH3_SECRET_SIZE_MIN] = {};\n  uint64_t seed = {};\n};\n\nstatic unique_hash_key_algo_fast_xxh3_init_t\nunique_hash_key_algo_fast_xxh3_init_impl() {\n  unique_hash_key_algo_fast_xxh3_init_t res{};\n  unique_hash_key_init_process_key_blake3(__func__, res.secret);\n  res.seed = XXH3_64bits(res.secret, sizeof(res.secret));\n  return res;\n}\n\n// init xxh3 hasher with secret/seed unique to this process\nstatic unique_hash_key_algo_fast_xxh3_init_t const&\nunique_hash_key_algo_fast_xxh3_init() {\n  static auto const object = unique_hash_key_algo_fast_xxh3_init_impl();\n  return object;\n}\n\ntemplate <size_t Size>\nstruct unique_hash_key_algo_fast_xxh3_ops;\ntemplate <>\nstruct unique_hash_key_algo_fast_xxh3_ops<8> {\n  static constexpr auto init = XXH3_64bits_reset_withSecret;\n  static constexpr auto update = XXH3_64bits_update;\n  static constexpr auto finalize = XXH3_64bits_digest;\n};\ntemplate <>\nstruct unique_hash_key_algo_fast_xxh3_ops<16> {\n  static constexpr auto init = XXH3_128bits_reset_withSecret;\n  static constexpr auto update = XXH3_128bits_update;\n  static constexpr auto finalize = XXH3_128bits_digest;\n};\n\n#endif // __has_include(<xxh3.h>)\n\n#endif // __has_include(<blake3.h>)\n\ntemplate <typename Hash, typename Update>\nFOLLY_ALWAYS_INLINE static void unique_hash_key_hash_items(\n    Update const update,\n    Hash* const h,\n    span<detail::unique_hash_key_item const> const in) noexcept {\n  for (auto const item : in) {\n    auto const [len, include_len] = item.unpack_len();\n    if (include_len) {\n      auto const buf = reinterpret_cast<uint8_t const*>(&len);\n      update(h, buf, sizeof(len));\n    }\n    update(h, item.buf, len);\n  }\n}\n\n} // namespace detail\n\n/// SHA256 is not keyed, but HMAC requires two rounds of hashing. So this uses\n/// the Secret Suffix MAC construction, which depends on the underlying hash\n/// function's collision resistance for its own collision resistance.\ntemplate <size_t Size>\nstd::array<uint8_t, Size>\nunique_hash_key_algo_strong_sha256_fn<Size>::operator()(\n    span<detail::unique_hash_key_item const> const in) const noexcept {\n  auto const& init = detail::unique_hash_key_algo_strong_sha256_init();\n  SHA256_CTX h;\n  SHA256_Init(&h);\n  detail::unique_hash_key_hash_items(SHA256_Update, &h, in);\n  SHA256_Update(&h, init.key, sizeof(init.key)); // Secret Suffix MAC\n  unsigned char mid[SHA256_DIGEST_LENGTH];\n  SHA256_Final(mid, &h);\n  std::array<uint8_t, Size> out{};\n  std::memcpy(out.data(), mid, out.size());\n  return out;\n}\n\ntemplate std::array<uint8_t, 8> //\nunique_hash_key_algo_strong_sha256_fn<8>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\ntemplate std::array<uint8_t, 16> //\nunique_hash_key_algo_strong_sha256_fn<16>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\ntemplate std::array<uint8_t, 24> //\nunique_hash_key_algo_strong_sha256_fn<24>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\ntemplate std::array<uint8_t, 32> //\nunique_hash_key_algo_strong_sha256_fn<32>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\n\n#if __has_include(<blake3.h>)\n\ntemplate <size_t Size>\nstd::array<uint8_t, Size>\nunique_hash_key_algo_strong_blake3_fn<Size>::operator()(\n    span<detail::unique_hash_key_item const> const in) const noexcept {\n  auto const& init = detail::unique_hash_key_algo_strong_blake3_init();\n  blake3_hasher h;\n  blake3_hasher_init_keyed(&h, init.key);\n  detail::unique_hash_key_hash_items(blake3_hasher_update, &h, in);\n  std::array<uint8_t, Size> out{};\n  blake3_hasher_finalize(&h, out.data(), out.size());\n  return out;\n}\n\ntemplate std::array<uint8_t, 8> //\nunique_hash_key_algo_strong_blake3_fn<8>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\ntemplate std::array<uint8_t, 16> //\nunique_hash_key_algo_strong_blake3_fn<16>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\ntemplate std::array<uint8_t, 24> //\nunique_hash_key_algo_strong_blake3_fn<24>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\ntemplate std::array<uint8_t, 32> //\nunique_hash_key_algo_strong_blake3_fn<32>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\n\n#if __has_include(<xxh3.h>)\n\ntemplate <size_t Size>\nstd::array<uint8_t, Size> unique_hash_key_algo_fast_xxh3_fn<Size>::operator()(\n    span<detail::unique_hash_key_item const> const in) const noexcept {\n  using ops = detail::unique_hash_key_algo_fast_xxh3_ops<Size>;\n  auto const& init = detail::unique_hash_key_algo_fast_xxh3_init();\n  XXH3_state_t h;\n  // ideally, XXH3_{N}bits_reset_withSecretAndSeed - but it may not be available\n  // so we must mimic it with XXH3_{N}bits_reset_withSecret and then setting the\n  // seed directly\n  ops::init(&h, init.secret, sizeof(init.secret));\n  h.seed = init.seed;\n  h.useSeed = true;\n  detail::unique_hash_key_hash_items(ops::update, &h, in);\n  auto v = ops::finalize(&h);\n  static_assert(sizeof(v) == Size);\n  std::array<uint8_t, Size> out{};\n  std::memcpy(out.data(), &v, sizeof(v));\n  return out;\n}\n\ntemplate std::array<uint8_t, 8> //\nunique_hash_key_algo_fast_xxh3_fn<8>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\ntemplate std::array<uint8_t, 16> //\nunique_hash_key_algo_fast_xxh3_fn<16>::operator()(\n    span<detail::unique_hash_key_item const> in) const noexcept;\n\n#endif // __has_include(<xxh3.h>)\n\n#endif // __has_include(<blake3.h>)\n\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/UniqueHashKey.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <string_view>\n#include <tuple>\n#include <type_traits>\n\n#include <folly/container/span.h>\n\nnamespace folly {\n\nnamespace detail {\n\nstruct unique_hash_key_item {\n  static inline constexpr size_t len_include_mask = ~(~size_t(0) >> 1);\n\n  uint8_t const* buf{};\n  size_t len{}; // hi bit set means include len in hash\n\n  std::pair<size_t, bool> unpack_len() const {\n    constexpr auto mask = len_include_mask;\n    return {len & ~mask, len & mask};\n  }\n};\n\n} // namespace detail\n\n/// unique_hash_key_algo_strong_sha256_fn\n///\n/// Hashes span<detail::unique_hash_key_item const> using SHA256 with a key that\n/// may vary between processes.\n///\n/// The size must be in {8, 16, 24, 32}.\n///\n/// For use with unique_hash_key.\ntemplate <size_t Size>\nstruct unique_hash_key_algo_strong_sha256_fn {\n  static_assert(Size <= 32);\n  static_assert(Size % 8 == 0);\n  std::array<uint8_t, Size> operator()(\n      span<detail::unique_hash_key_item const> in) const noexcept;\n};\ntemplate <size_t Size>\ninline constexpr unique_hash_key_algo_strong_sha256_fn<Size>\n    unique_hash_key_algo_strong_sha256{};\n\n#if __has_include(<blake3.h>)\n\n/// unique_hash_key_algo_strong_blake3_fn\n///\n/// Hashes span<detail::unique_hash_key_item const> using BLAKE3 with a key that\n/// may vary between processes.\n///\n/// The size must be in {8, 16, 24, 32}.\n///\n/// For use with unique_hash_key.\ntemplate <size_t Size>\nstruct unique_hash_key_algo_strong_blake3_fn {\n  static_assert(Size <= 32);\n  static_assert(Size % 8 == 0);\n  std::array<uint8_t, Size> operator()(\n      span<detail::unique_hash_key_item const> in) const noexcept;\n};\ntemplate <size_t Size>\ninline constexpr unique_hash_key_algo_strong_blake3_fn<Size>\n    unique_hash_key_algo_strong_blake3{};\n\n#if __has_include(<xxh3.h>)\n\n/// unique_hash_key_algo_fast_xxh3_fn\n///\n/// Hashes span<detail::unique_hash_key_item const> using XXH3 with a key that\n/// may vary between processes.\n///\n/// The size must be in {8, 16}, corresponding to XXH3_64bits and XXH3_128bits.\n///\n/// For use with unique_hash_key.\ntemplate <size_t Size>\nstruct unique_hash_key_algo_fast_xxh3_fn {\n  static_assert(Size <= 16);\n  static_assert(Size % 8 == 0);\n  std::array<uint8_t, Size> operator()(\n      span<detail::unique_hash_key_item const> in) const noexcept;\n};\ntemplate <size_t Size>\ninline constexpr unique_hash_key_algo_fast_xxh3_fn<Size>\n    unique_hash_key_algo_fast_xxh3{};\n\n#endif // __has_include(<xxh3.h>)\n\n#endif // __has_include(<blake3.h>)\n\ntemplate <auto Algo>\nconstexpr size_t unique_hash_key_algo_size_v =\n    decltype(Algo(span<detail::unique_hash_key_item const>{})){}.size();\n\n/// unique_hash_key\n///\n/// A unique hashtable key cryptographically hashed from input data.\n///\n/// The size must be in {8, 16, 24, 32}.\n///\n/// Uniqueness is only guaranteed between two constructions when all of these\n/// conditions hold:\n/// * At sufficiently large sizes, likely at least size 16.\n/// * For cryptographically strong hash algorithms, such as BLAKE3.\n/// * With equivalent hash algorithm objects of the same type.\n/// * With packs of hashable arguments which have the same sequences of types.\n/// * With packs of hashable arguments that differ in at least one element.\ntemplate <size_t Size>\nclass unique_hash_key {\n private:\n  using self = unique_hash_key;\n\n  static inline constexpr size_t data_size = Size;\n  static inline constexpr size_t data_align = alignof(size_t);\n\n  static_assert(data_size <= 32);\n  static_assert(data_size % 8 == 0);\n\n  using item_type = detail::unique_hash_key_item;\n  using data_type = std::array<uint8_t, Size>;\n\n  template <typename Algo>\n  static inline constexpr bool is_algo_v =\n      std::is_invocable_v<Algo const&, span<item_type const>>;\n\n  alignas(data_align) data_type const data_;\n\n  template <\n      typename Int,\n      std::enable_if_t<is_non_bool_integral_v<Int>, int> = 0>\n  static item_type init_item(\n      Int const& arg,\n      [[maybe_unused]] size_t idx,\n      [[maybe_unused]] size_t size) {\n    auto const buf = reinterpret_cast<uint8_t const*>(&arg);\n    return {buf, sizeof(arg)};\n  }\n  static item_type init_item(std::string_view arg, size_t idx, size_t size) {\n    constexpr auto mask = item_type::len_include_mask;\n    auto const buf = reinterpret_cast<uint8_t const*>(arg.data());\n    auto const hi = mask * size_t(idx + 1 < size);\n    return {buf, arg.size() | hi};\n  }\n\n  template <typename Algo, typename... Arg>\n  static data_type init(Algo const algo, Arg const&... arg) noexcept {\n    constexpr auto len = sizeof...(Arg);\n    size_t idxr = 0;\n    size_t idxl = 0;\n    std::array<item_type, len> items{};\n    ((items[idxl++] = init_item(arg, idxr++, len)), ...);\n    return algo(items);\n  }\n\n  template <typename T, size_t E, typename V = std::remove_cv_t<T>>\n  static constexpr bool is_span_compatible_v = //\n      !std::is_volatile_v<T> && //\n      std::is_integral_v<V> && //\n      std::is_unsigned_v<V> && //\n      !std::is_same_v<bool, V> && //\n      !std::is_same_v<char, V> && //\n      alignof(V) <= data_align && //\n      (E == data_size / sizeof(T) || E == dynamic_extent);\n\n public:\n  template <\n      typename Algo,\n      typename... Arg,\n      std::enable_if_t<is_algo_v<Algo>, int> = 0>\n  explicit unique_hash_key(Algo const algo, Arg const&... arg) noexcept\n      : data_{init(algo, arg...)} {\n    static_assert(sizeof(self) == Size);\n    static_assert(alignof(self) == alignof(size_t));\n  }\n\n  template <\n      typename T,\n      std::size_t E,\n      std::enable_if_t<is_span_compatible_v<T, E>, int> = 0>\n  explicit operator span<T const, E>() const noexcept {\n    constexpr auto count = E == dynamic_extent ? data_size / sizeof(T) : E;\n    return span<T const, E>{reinterpret_cast<T const*>(data_.data()), count};\n  }\n\n  friend auto operator==(self const& a, self const& b) noexcept {\n    return a.data_ == b.data_;\n  }\n  friend auto operator!=(self const& a, self const& b) noexcept {\n    return a.data_ != b.data_;\n  }\n};\n\n/// unique_hash_key_with\n///\n/// A unique-hash-key of the size returned by the given hash algorithm, using\n/// the given algorithm.\n///\n/// Hash values are unique to the process. But see the caveats regarding\n/// uniqueness above, with tuples replacing packs.\n///\n/// It is explicitly permitted to copy-construct unique_hash_key (base class)\n/// instances from instances of unique_hash_key_with<Algo> (derived class).\n/// There is no concern about object slicing.\ntemplate <auto Algo>\nclass unique_hash_key_with\n    : public unique_hash_key<unique_hash_key_algo_size_v<Algo>> {\n private:\n  static constexpr size_t size = unique_hash_key_algo_size_v<Algo>;\n  using self = unique_hash_key_with;\n  using base = unique_hash_key<size>;\n\n  template <typename... Arg, size_t... Idx>\n  explicit unique_hash_key_with(\n      std::tuple<Arg...> const& arg, std::index_sequence<Idx...>)\n      : base(Algo, std::get<Idx>(arg)...) {}\n\n public:\n  template <typename... Arg>\n  explicit unique_hash_key_with(std::tuple<Arg...> const& arg)\n      : self(arg, std::index_sequence_for<Arg...>{}) {}\n};\n\n/// unique_hash_key_strong_sha256\n///\n/// A unique-hash-key of the given size using the cryptographically strong\n/// SHA256 hash algorithm from OpenSSL.\n///\n/// Hash values are unique to the process. But see the caveats regarding\n/// uniqueness above, with tuples replacing packs.\ntemplate <size_t Size>\nusing unique_hash_key_strong_sha256 =\n    unique_hash_key_with<unique_hash_key_algo_strong_sha256<Size>>;\n\n#if __has_include(<blake3.h>)\n\n/// unique_hash_key_strong_blake3\n///\n/// A unique-hash-key of the given size using the cryptographically strong\n/// BLAKE3 hash algorithm.\n///\n/// Hash values are unique to the process. But see the caveats regarding\n/// uniqueness above, with tuples replacing packs.\ntemplate <size_t Size>\nusing unique_hash_key_strong_blake3 =\n    unique_hash_key_with<unique_hash_key_algo_strong_blake3<Size>>;\n\n#if __has_include(<xxh3.h>)\n\n/// unique_hash_key_strong_blake3\n///\n/// A unique-hash-key of the given size using the fast but not cryptographically\n/// strong XXH3 algorithm.\n///\n/// Hash values are unique to the process. But see the caveats regarding\n/// uniqueness above, with tuples replacing packs.\ntemplate <size_t Size>\nusing unique_hash_key_fast_xxh3 =\n    unique_hash_key_with<unique_hash_key_algo_fast_xxh3<Size>>;\n\n#endif // __has_include(<xxh3.h>)\n\n#endif // __has_include(<blake3.h>)\n\n} // namespace folly\n\nnamespace std {\n\ntemplate <size_t Size>\nstruct hash<::folly::unique_hash_key<Size>> {\n  using folly_is_avalanching = std::true_type;\n\n  size_t operator()(::folly::unique_hash_key<Size> const& key) const noexcept {\n    return ::folly::span<size_t const>{key}[0];\n  }\n};\n\ntemplate <auto Algo>\nstruct hash<::folly::unique_hash_key_with<Algo>>\n    : hash<::folly::unique_hash_key<\n          ::folly::unique_hash_key_algo_size_v<Algo>>> {};\n\n} // namespace std\n"
  },
  {
    "path": "folly/hash/detail/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\n    \"@fbsource//xplat/folly:defs.bzl\",\n    \"folly_xplat_library\",\n)\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat build rules\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"checksum_detail\",\n    srcs = [\n        \"ChecksumDetail.cpp\",\n        \"Crc32CombineDetail.cpp\",\n        \"Crc32cDetail.cpp\",\n    ],\n    compiler_flags = select({\n        \"DEFAULT\": [],\n        \"ovr_config//os:android-x86_64\": [\"-mpclmul\"],\n    }),\n    raw_headers = [\n        \"ChecksumDetail.h\",\n    ],\n    deps = [\n        \"//third-party/boost:boost_preprocessor\",\n        \"//xplat/folly:bits\",\n        \"//xplat/folly:cpp_attributes\",\n    ],\n    exported_deps = [\n        \"//xplat/folly:portability\",\n        \"//xplat/folly/external/nvidia/hash:checksum\",  # @manual\n        \"//xplat/folly/external/nvidia/hash/detail:crc32c_detail\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"random_seed\",\n    headers = [\n        \"RandomSeed.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\n# fbcode build rules\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"checksum_detail\",\n    srcs = [\n        \"ChecksumDetail.cpp\",\n        \"Crc32CombineDetail.cpp\",\n        \"Crc32cDetail.cpp\",\n    ],\n    headers = [\"ChecksumDetail.h\"],\n    deps = [\n        \"//folly:bits\",\n        \"//folly:cpp_attributes\",\n        \"//folly/external/nvidia/hash/detail:crc32c_detail\",\n    ],\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly/external/nvidia/hash:checksum\",  # @manual\n        \"//folly/external/nvidia/hash/detail:crc32c_detail\",  # @manual\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_preprocessor\"),\n    ],\n)\n"
  },
  {
    "path": "folly/hash/detail/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# NOTE: This file is manually maintained, not auto-generated.\n# Update this file when the corresponding BUCK file changes.\n\nfolly_add_library(\n  NAME checksum_detail\n  SRCS\n    ChecksumDetail.cpp\n    Crc32CombineDetail.cpp\n    Crc32cDetail.cpp\n  HEADERS\n    ChecksumDetail.h\n  DEPS\n    folly_bits\n    folly_cpp_attributes\n    folly_external_nvidia_hash_detail_crc32c_detail\n  EXPORTED_DEPS\n    folly_external_nvidia_hash_checksum\n    folly_external_nvidia_hash_detail_crc32c_detail\n    folly_portability\n)\n\n# Apply -mpclmul flag on x86 if supported (required for CRC32 intrinsics)\nif (COMPILER_HAS_M_PCLMUL)\n  target_compile_options(folly_hash_detail_checksum_detail_obj PRIVATE -mpclmul)\nendif()\n\nfolly_add_library(\n  NAME random_seed\n  HEADERS\n    RandomSeed.h\n  EXPORTED_DEPS\n    folly_portability\n)\n"
  },
  {
    "path": "folly/hash/detail/ChecksumDetail.cpp",
    "content": "/*\n * crc32_impl.h\n *\n * Copyright 2016 Eric Biggers\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/*\n * CRC-32 folding with PCLMULQDQ.\n *\n * The basic idea is to repeatedly \"fold\" each 512 bits into the next\n * 512 bits, producing an abbreviated message which is congruent the\n * original message modulo the generator polynomial G(x).\n *\n * Folding each 512 bits is implemented as eight 64-bit folds, each of\n * which uses one carryless multiplication instruction.  It's expected\n * that CPUs may be able to execute some of these multiplications in\n * parallel.\n *\n * Explanation of \"folding\": let A(x) be 64 bits from the message, and\n * let B(x) be 95 bits from a constant distance D later in the\n * message.  The relevant portion of the message can be written as:\n *\n *      M(x) = A(x)*x^D + B(x)\n *\n * ... where + and * represent addition and multiplication,\n * respectively, of polynomials over GF(2).  Note that when\n * implemented on a computer, these operations are equivalent to XOR\n * and carryless multiplication, respectively.\n *\n * For the purpose of CRC calculation, only the remainder modulo the\n * generator polynomial G(x) matters:\n *\n * M(x) mod G(x) = (A(x)*x^D + B(x)) mod G(x)\n *\n * Since the modulo operation can be applied anywhere in a sequence of\n * additions and multiplications without affecting the result, this is\n * equivalent to:\n *\n * M(x) mod G(x) = (A(x)*(x^D mod G(x)) + B(x)) mod G(x)\n *\n * For any D, 'x^D mod G(x)' will be a polynomial with maximum degree\n * 31, i.e.  a 32-bit quantity.  So 'A(x) * (x^D mod G(x))' is\n * equivalent to a carryless multiplication of a 64-bit quantity by a\n * 32-bit quantity, producing a 95-bit product.  Then, adding\n * (XOR-ing) the product to B(x) produces a polynomial with the same\n * length as B(x) but with the same remainder as 'A(x)*x^D + B(x)'.\n * This is the basic fold operation with 64 bits.\n *\n * Note that the carryless multiplication instruction PCLMULQDQ\n * actually takes two 64-bit inputs and produces a 127-bit product in\n * the low-order bits of a 128-bit XMM register.  This works fine, but\n * care must be taken to account for \"bit endianness\".  With the CRC\n * version implemented here, bits are always ordered such that the\n * lowest-order bit represents the coefficient of highest power of x\n * and the highest-order bit represents the coefficient of the lowest\n * power of x.  This is backwards from the more intuitive order.\n * Still, carryless multiplication works essentially the same either\n * way.  It just must be accounted for that when we XOR the 95-bit\n * product in the low-order 95 bits of a 128-bit XMM register into\n * 128-bits of later data held in another XMM register, we'll really\n * be XOR-ing the product into the mathematically higher degree end of\n * those later bits, not the lower degree end as may be expected.\n *\n * So given that caveat and the fact that we process 512 bits per\n * iteration, the 'D' values we need for the two 64-bit halves of each\n * 128 bits of data are:\n *\n * D = (512 + 95) - 64 for the higher-degree half of each 128\n *                 bits, i.e. the lower order bits in\n *                 the XMM register\n *\n *    D = (512 + 95) - 128 for the lower-degree half of each 128\n *                 bits, i.e. the higher order bits in\n *                 the XMM register\n *\n * The required 'x^D mod G(x)' values were precomputed.\n *\n * When <= 512 bits remain in the message, we finish up by folding\n * across smaller distances.  This works similarly; the distance D is\n * just different, so different constant multipliers must be used.\n * Finally, once the remaining message is just 64 bits, it is is\n * reduced to the CRC-32 using Barrett reduction (explained later).\n *\n * For more information see the original paper from Intel: \"Fast CRC\n *    Computation for Generic Polynomials Using PCLMULQDQ\n *    Instruction\" December 2009\n *    http://www.intel.com/content/dam/www/public/us/en/documents/\n *    white-papers/\n *    fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf\n */\n\n#include <folly/hash/detail/ChecksumDetail.h>\n\nnamespace folly {\nnamespace detail {\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n\nstatic __m128i crc32MulAdd(__m128i x, __m128i a, __m128i multiplier) {\n  /*\n   * Note: the immediate constant for PCLMULQDQ specifies which\n   * 64-bit halves of the 128-bit vectors to multiply:\n   *\n   * 0x00 means low halves (higher degree polynomial terms for us)\n   * 0x11 means high halves (lower degree polynomial terms for us)\n   */\n  const __m128i t = _mm_xor_si128(a, _mm_clmulepi64_si128(x, multiplier, 0x00));\n  return _mm_xor_si128(t, _mm_clmulepi64_si128(x, multiplier, 0x11));\n}\n\nuint32_t crc32_hw_aligned(\n    uint32_t remainder, const __m128i* p, size_t vec_count) {\n  if (vec_count == 0) {\n    return remainder;\n  }\n\n  /* Constants precomputed by gen_crc32_multipliers.c.  Do not edit! */\n  const __m128i multipliers_8 = _mm_set_epi32(0, 0x910EEEC1, 0, 0x33FFF533);\n  const __m128i multipliers_4 = _mm_set_epi32(0, 0x1D9513D7, 0, 0x8F352D95);\n  const __m128i multipliers_2 = _mm_set_epi32(0, 0x81256527, 0, 0xF1DA05AA);\n  const __m128i multipliers_1 = _mm_set_epi32(0, 0xCCAA009E, 0, 0xAE689191);\n  const __m128i final_multiplier = _mm_set_epi32(0, 0, 0, 0xB8BC6765);\n  const __m128i mask32 = _mm_set_epi32(0, 0, 0, 0xFFFFFFFF);\n  const __m128i barrett_reduction_constants =\n      _mm_set_epi32(0x1, 0xDB710641, 0x1, 0xF7011641);\n\n  const __m128i* const end = p + vec_count;\n  const __m128i* const end512 = p + (vec_count & ~3);\n  const __m128i* const end1024 = p + (vec_count & ~7);\n  __m128i x0, x1, x2, x3, x4, x5, x6, x7;\n\n  /*\n   * Account for the current 'remainder', i.e. the CRC of the part of\n   * the message already processed.  Explanation: rewrite the message\n   * polynomial M(x) in terms of the first part A(x), the second part\n   * B(x), and the length of the second part in bits |B(x)| >= 32:\n   *\n   *    M(x) = A(x)*x^|B(x)| + B(x)\n   *\n   * Then the CRC of M(x) is:\n   *\n   *    CRC(M(x)) = CRC(A(x)*x^|B(x)| + B(x))\n   *              = CRC(A(x)*x^32*x^(|B(x)| - 32) + B(x))\n   *              = CRC(CRC(A(x))*x^(|B(x)| - 32) + B(x))\n   *\n   * Note: all arithmetic is modulo G(x), the generator polynomial; that's\n   * why A(x)*x^32 can be replaced with CRC(A(x)) = A(x)*x^32 mod G(x).\n   *\n   * So the CRC of the full message is the CRC of the second part of the\n   * message where the first 32 bits of the second part of the message\n   * have been XOR'ed with the CRC of the first part of the message.\n   */\n  x0 = *p++;\n  x0 = _mm_xor_si128(x0, _mm_set_epi32(0, 0, 0, remainder));\n\n  if (p > end512) { /* only 128, 256, or 384 bits of input? */\n    goto _128_bits_at_a_time;\n  }\n  x1 = *p++;\n  x2 = *p++;\n  x3 = *p++;\n  if (p > end1024) { /* Less than 1024 bits of input available */\n    goto _512_bits_at_a_time;\n  }\n  x4 = *p++;\n  x5 = *p++;\n  x6 = *p++;\n  x7 = *p++;\n\n  for (; p != end1024; p += 8) {\n    x0 = crc32MulAdd(x0, p[0], multipliers_8);\n    x1 = crc32MulAdd(x1, p[1], multipliers_8);\n    x2 = crc32MulAdd(x2, p[2], multipliers_8);\n    x3 = crc32MulAdd(x3, p[3], multipliers_8);\n    x4 = crc32MulAdd(x4, p[4], multipliers_8);\n    x5 = crc32MulAdd(x5, p[5], multipliers_8);\n    x6 = crc32MulAdd(x6, p[6], multipliers_8);\n    x7 = crc32MulAdd(x7, p[7], multipliers_8);\n  }\n\n  /* Fold 1024 bits => 512 bits */\n  x0 = crc32MulAdd(x0, x4, multipliers_4);\n  x1 = crc32MulAdd(x1, x5, multipliers_4);\n  x2 = crc32MulAdd(x2, x6, multipliers_4);\n  x3 = crc32MulAdd(x3, x7, multipliers_4);\n\n_512_bits_at_a_time:\n  /* Fold 512 bits at a time */\n  for (; p != end512; p += 4) {\n    x0 = crc32MulAdd(x0, p[0], multipliers_4);\n    x1 = crc32MulAdd(x1, p[1], multipliers_4);\n    x2 = crc32MulAdd(x2, p[2], multipliers_4);\n    x3 = crc32MulAdd(x3, p[3], multipliers_4);\n  }\n\n  /* Fold 512 bits => 128 bits */\n  x2 = crc32MulAdd(x0, x2, multipliers_2);\n  x3 = crc32MulAdd(x1, x3, multipliers_2);\n  x0 = crc32MulAdd(x2, x3, multipliers_1);\n\n_128_bits_at_a_time:\n  while (p != end) {\n    /* Fold 128 bits into next 128 bits */\n    x1 = *p++;\n    x0 = crc32MulAdd(x0, x1, multipliers_1);\n  }\n\n  /* Now there are just 128 bits left, stored in 'x0'. */\n\n  /*\n   * Fold 128 => 96 bits.  This also implicitly appends 32 zero bits,\n   * which is equivalent to multiplying by x^32.  This is needed because\n   * the CRC is defined as M(x)*x^32 mod G(x), not just M(x) mod G(x).\n   */\n  x0 = _mm_xor_si128(\n      _mm_srli_si128(x0, 8), _mm_clmulepi64_si128(x0, multipliers_1, 0x10));\n\n  /* Fold 96 => 64 bits */\n  x0 = _mm_xor_si128(\n      _mm_srli_si128(x0, 4),\n      _mm_clmulepi64_si128(_mm_and_si128(x0, mask32), final_multiplier, 0x00));\n\n  /*\n   * Finally, reduce 64 => 32 bits using Barrett reduction.\n   *\n   * Let M(x) = A(x)*x^32 + B(x) be the remaining message.  The goal is to\n   * compute R(x) = M(x) mod G(x).  Since degree(B(x)) < degree(G(x)):\n   *\n   *    R(x) = (A(x)*x^32 + B(x)) mod G(x)\n   *         = (A(x)*x^32) mod G(x) + B(x)\n   *\n   * Then, by the Division Algorithm there exists a unique q(x) such that:\n   *\n   *    A(x)*x^32 mod G(x) = A(x)*x^32 - q(x)*G(x)\n   *\n   * Since the left-hand side is of maximum degree 31, the right-hand side\n   * must be too.  This implies that we can apply 'mod x^32' to the\n   * right-hand side without changing its value:\n   *\n   *    (A(x)*x^32 - q(x)*G(x)) mod x^32 = q(x)*G(x) mod x^32\n   *\n   * Note that '+' is equivalent to '-' in polynomials over GF(2).\n   *\n   * We also know that:\n   *\n   *                  / A(x)*x^32 \\\n   *    q(x) = floor (  ---------  )\n   *                  \\    G(x)   /\n   *\n   * To compute this efficiently, we can multiply the top and bottom by\n   * x^32 and move the division by G(x) to the top:\n   *\n   *                  / A(x) * floor(x^64 / G(x)) \\\n   *    q(x) = floor (  -------------------------  )\n   *                  \\           x^32            /\n   *\n   * Note that floor(x^64 / G(x)) is a constant.\n   *\n   * So finally we have:\n   *\n   *                              / A(x) * floor(x^64 / G(x)) \\\n   *    R(x) = B(x) + G(x)*floor (  -------------------------  )\n   *                              \\           x^32            /\n   */\n  x1 = x0;\n  x0 = _mm_clmulepi64_si128(\n      _mm_and_si128(x0, mask32), barrett_reduction_constants, 0x00);\n  x0 = _mm_clmulepi64_si128(\n      _mm_and_si128(x0, mask32), barrett_reduction_constants, 0x10);\n  return _mm_cvtsi128_si32(_mm_srli_si128(_mm_xor_si128(x0, x1), 4));\n}\n\n#endif\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/detail/ChecksumDetail.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Portability.h>\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n#include <immintrin.h>\n#endif\n\n#include <stdint.h>\n\n#include <cstddef>\n\nnamespace folly {\nnamespace detail {\n\n/**\n * Compute a CRC-32C checksum of a buffer using a hardware-accelerated\n * implementation.\n *\n * @note This function is exposed to support special cases where the\n *       calling code is absolutely certain it ought to invoke a hardware-\n *       accelerated CRC-32C implementation - unit tests, for example.  For\n *       all other scenarios, please call crc32c() and let it pick an\n *       implementation based on the capabilities of the underlying CPU.\n */\nuint32_t crc32c_hw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);\n\n/**\n * Check whether a SSE4.2 hardware-accelerated CRC-32C implementation is\n * supported on the current CPU.\n */\nbool crc32c_hw_supported_sse42();\n\n/**\n * Check whether a hardware-accelerated CRC-32C implementation is\n * supported on the current CPU.\n */\nbool crc32c_hw_supported();\n\n/**\n * Check whether an AVX512VL hardware-accelerated CRC-32C implementation is\n * supported on the current CPU.\n */\nbool crc32c_hw_supported_avx512();\n\n/**\n * Check whether a NEON hardware-accelerated CRC-32C implementation is\n * supported on the current CPU.\n */\nbool crc32c_hw_supported_neon();\n\n/**\n * Check whether a NEON+EOR3+SHA3 hardware-accelerated CRC-32C implementation\n * is supported on the current CPU.\n */\nbool crc32c_hw_supported_neon_eor3_sha3();\n\n/**\n * Compute a CRC-32C checksum of a buffer using a portable,\n * software-only implementation.\n *\n * @note This function is exposed to support special cases where the\n *       calling code is absolutely certain it wants to use the software\n *       implementation instead of the hardware-accelerated code - unit\n *       tests, for example.  For all other scenarios, please call crc32c()\n *       and let it pick an implementation based on the capabilities of\n *       the underlying CPU.\n */\nuint32_t crc32c_sw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);\n\n/**\n * Compute a CRC-32 checksum of a buffer using a hardware-accelerated\n * implementation.\n *\n * @note This function is exposed to support special cases where the\n *       calling code is absolutely certain it ought to invoke a hardware-\n *       accelerated CRC-32 implementation - unit tests, for example.  For\n *       all other scenarios, please call crc32() and let it pick an\n *       implementation based on the capabilities of the underlying CPU.\n */\nuint32_t crc32_hw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\nuint32_t crc32_hw_aligned(\n    uint32_t remainder, const __m128i* p, size_t vec_count);\n#endif\n\n/**\n * Check whether a hardware-accelerated CRC-32 implementation is\n * supported on the current CPU.\n */\nbool crc32_hw_supported();\n\n/**\n * Compute a CRC-32 checksum of a buffer using a portable,\n * software-only implementation.\n *\n * @note This function is exposed to support special cases where the\n *       calling code is absolutely certain it wants to use the software\n *       implementation instead of the hardware-accelerated code - unit\n *       tests, for example.  For all other scenarios, please call crc32()\n *       and let it pick an implementation based on the capabilities of\n *       the underlying CPU.\n */\nuint32_t crc32_sw(\n    const uint8_t* data, size_t nbytes, uint32_t startingChecksum = ~0U);\n\n/* See Checksum.h for details.\n *\n * crc2len *must* be a power of two >= 4.\n */\nuint32_t crc32_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len);\nuint32_t crc32_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len);\nuint32_t crc32c_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len);\nuint32_t crc32c_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len);\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/detail/Crc32CombineDetail.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <utility>\n\n#include <folly/Bits.h>\n#include <folly/external/nvidia/hash/detail/Crc32cCombineDetail.h>\n#include <folly/hash/detail/ChecksumDetail.h>\n\nnamespace folly {\n\n// Standard galois-field multiply.  The only modification is that a,\n// b, m, and p are all bit-reflected.\n//\n// https://en.wikipedia.org/wiki/Finite_field_arithmetic\nstatic constexpr uint32_t gf_multiply_sw_1(\n    size_t i, uint32_t p, uint32_t a, uint32_t b, uint32_t m) {\n  // clang-format off\n  return i == 32 ? p : gf_multiply_sw_1(\n      /* i = */ i + 1,\n      /* p = */ p ^ (-((b >> 31) & 1) & a),\n      /* a = */ (a >> 1) ^ (-(a & 1) & m),\n      /* b = */ b << 1,\n      /* m = */ m);\n  // clang-format on\n}\nstatic constexpr uint32_t gf_multiply_sw(uint32_t a, uint32_t b, uint32_t m) {\n  return gf_multiply_sw_1(/* i = */ 0, /* p = */ 0, a, b, m);\n}\n\nstatic constexpr uint32_t gf_square_sw(uint32_t a, uint32_t m) {\n  return gf_multiply_sw(a, a, m);\n}\n\nnamespace {\n\ntemplate <size_t i, uint32_t m>\nstruct gf_powers_memo {\n  static constexpr uint32_t value =\n      gf_square_sw(gf_powers_memo<i - 1, m>::value, m);\n};\ntemplate <uint32_t m>\nstruct gf_powers_memo<0, m> {\n  static constexpr uint32_t value = m;\n};\n\ntemplate <uint32_t m>\nstruct gf_powers_make {\n  template <size_t... i>\n  constexpr auto operator()(std::index_sequence<i...>) const {\n    return std::array<uint32_t, sizeof...(i)>{{gf_powers_memo<i, m>::value...}};\n  }\n};\n\n} // namespace\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n\n// Reduction taken from\n// https://www.nicst.de/crc.pdf\n//\n// This is an intrinsics-based implementation of listing 3.\nstatic uint32_t gf_multiply_crc32c_hw(uint64_t crc1, uint64_t crc2, uint32_t) {\n  const auto crc1_xmm = _mm_set_epi64x(0, crc1);\n  const auto crc2_xmm = _mm_set_epi64x(0, crc2);\n  const auto count = _mm_set_epi64x(0, 1);\n  const auto res0 = _mm_clmulepi64_si128(crc2_xmm, crc1_xmm, 0x00);\n  const auto res1 = _mm_sll_epi64(res0, count);\n\n  // Use hardware crc32c to do reduction from 64 -> 32 bytes\n  const auto res2 = _mm_cvtsi128_si64(res1);\n  const auto res3 = _mm_crc32_u32(0, res2);\n  const auto res4 = _mm_extract_epi32(res1, 1);\n  return res3 ^ res4;\n}\n\nstatic uint32_t gf_multiply_crc32_hw(uint64_t crc1, uint64_t crc2, uint32_t) {\n  const auto crc1_xmm = _mm_set_epi64x(0, crc1);\n  const auto crc2_xmm = _mm_set_epi64x(0, crc2);\n  const auto count = _mm_set_epi64x(0, 1);\n  const auto res0 = _mm_clmulepi64_si128(crc2_xmm, crc1_xmm, 0x00);\n  const auto res1 = _mm_sll_epi64(res0, count);\n\n  // Do barrett reduction of 64 -> 32 bytes\n  const auto mask32 = _mm_set_epi32(0, 0, 0, 0xFFFFFFFF);\n  const auto barrett_reduction_constants =\n      _mm_set_epi32(0x1, 0xDB710641, 0x1, 0xF7011641);\n  const auto res2 = _mm_clmulepi64_si128(\n      _mm_and_si128(res1, mask32), barrett_reduction_constants, 0x00);\n  const auto res3 = _mm_clmulepi64_si128(\n      _mm_and_si128(res2, mask32), barrett_reduction_constants, 0x10);\n  return _mm_cvtsi128_si32(_mm_srli_si128(_mm_xor_si128(res3, res1), 4));\n}\n\n#elif FOLLY_NEON && FOLLY_ARM_FEATURE_CRC32 && FOLLY_ARM_FEATURE_AES && \\\n    FOLLY_ARM_FEATURE_SHA2\n\n// gf_multiply_crc32c_hw and fg_multiply_crc32_hw are defined in\n// external/nvidia/hash/detail/Crc32cCombineDetail-inl.h\n#else\n\nstatic uint32_t gf_multiply_crc32c_hw(uint64_t, uint64_t, uint32_t) {\n  return 0;\n}\nstatic uint32_t gf_multiply_crc32_hw(uint64_t, uint64_t, uint32_t) {\n  return 0;\n}\n\n#endif // FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n\nstatic constexpr uint32_t crc32c_m = 0x82f63b78;\nstatic constexpr uint32_t crc32_m = 0xedb88320;\n\n/*\n * Pre-calculated powers tables for crc32c and crc32.\n */\nstatic constexpr std::array<uint32_t, 62> const crc32c_powers =\n    gf_powers_make<crc32c_m>{}(std::make_index_sequence<62>{});\nstatic constexpr std::array<uint32_t, 62> const crc32_powers =\n    gf_powers_make<crc32_m>{}(std::make_index_sequence<62>{});\n\ntemplate <typename F>\nstatic uint32_t crc32_append_zeroes(\n    F mult,\n    uint32_t crc,\n    size_t len,\n    uint32_t polynomial,\n    std::array<uint32_t, 62> const& powers_array) {\n  auto powers = powers_array.data();\n\n  // Append by multiplying by consecutive powers of two of the zeroes\n  // array\n  len >>= 2;\n\n  while (len) {\n    // Advance directly to next bit set.\n    auto r = findFirstSet(len) - 1;\n    len >>= r;\n    powers += r;\n\n    crc = mult(crc, *powers, polynomial);\n\n    len >>= 1;\n    powers++;\n  }\n\n  return crc;\n}\n\nnamespace detail {\n\nuint32_t crc32_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len) {\n  return crc2 ^\n      crc32_append_zeroes(gf_multiply_sw, crc1, crc2len, crc32_m, crc32_powers);\n}\n\nuint32_t crc32_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len) {\n  return crc2 ^\n      crc32_append_zeroes(\n             gf_multiply_crc32_hw, crc1, crc2len, crc32_m, crc32_powers);\n}\n\nuint32_t crc32c_combine_sw(uint32_t crc1, uint32_t crc2, size_t crc2len) {\n  return crc2 ^\n      crc32_append_zeroes(\n             gf_multiply_sw, crc1, crc2len, crc32c_m, crc32c_powers);\n}\n\nuint32_t crc32c_combine_hw(uint32_t crc1, uint32_t crc2, size_t crc2len) {\n  return crc2 ^\n      crc32_append_zeroes(\n             gf_multiply_crc32c_hw, crc1, crc2len, crc32c_m, crc32c_powers);\n}\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/detail/Crc32cDetail.cpp",
    "content": "/*\n * Copyright 2016 Ferry Toth, Exalon Delft BV, The Netherlands\n *  This software is provided 'as-is', without any express or implied\n * warranty.  In no event will the author be held liable for any damages\n * arising from the use of this software.\n *  Permission is granted to anyone to use this software for any purpose,\n * including commercial applications, and to alter it and redistribute it\n * freely, subject to the following restrictions:\n *  1. The origin of this software must not be misrepresented; you must not\n *   claim that you wrote the original software. If you use this software\n *   in a product, an acknowledgment in the product documentation would be\n *   appreciated but is not required.\n * 2. Altered source versions must be plainly marked as such, and must not be\n *   misrepresented as being the original software.\n * 3. This notice may not be removed or altered from any source distribution.\n *  Ferry Toth\n * ftoth@exalondelft.nl\n *\n * https://github.com/htot/crc32c\n *\n * Modified by Facebook\n *\n * Original intel whitepaper:\n * \"Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction\"\n * https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/crc-iscsi-polynomial-crc32-instruction-paper.pdf\n *\n * 32-bit support dropped\n * use intrinsics instead of inline asm\n * other code cleanup\n */\n\n#include <stdexcept>\n\n#include <boost/preprocessor/arithmetic/add.hpp>\n#include <boost/preprocessor/arithmetic/sub.hpp>\n#include <boost/preprocessor/repetition/repeat_from_to.hpp>\n\n#include <folly/CppAttributes.h>\n#include <folly/hash/detail/ChecksumDetail.h>\n\nnamespace folly {\nnamespace detail {\n\n#if FOLLY_X64 && FOLLY_SSE_PREREQ(4, 2)\n\nnamespace crc32_detail {\n\n#define CRCtriplet(crc, buf, offset)                  \\\n  crc##0 = _mm_crc32_u64(crc##0, *(buf##0 + offset)); \\\n  crc##1 = _mm_crc32_u64(crc##1, *(buf##1 + offset)); \\\n  crc##2 = _mm_crc32_u64(crc##2, *(buf##2 + offset)); \\\n  [[fallthrough]]\n\n#define CRCduplet(crc, buf, offset)                   \\\n  crc##0 = _mm_crc32_u64(crc##0, *(buf##0 + offset)); \\\n  crc##1 = _mm_crc32_u64(crc##1, *(buf##1 + offset)); \\\n  static_assert(true, \"Semicolon required\")\n\n#define CRCsinglet(crc, buf, offset)                    \\\n  crc = _mm_crc32_u64(crc, *(uint64_t*)(buf + offset)); \\\n  [[fallthrough]]\n\n#define CASEREPEAT_TRIPLET(unused, count, total)    \\\n  case BOOST_PP_ADD(1, BOOST_PP_SUB(total, count)): \\\n    CRCtriplet(crc, next, -BOOST_PP_ADD(1, BOOST_PP_SUB(total, count)));\n\n#define CASEREPEAT_SINGLET(unused, count, total) \\\n  case BOOST_PP_SUB(total, count):               \\\n    CRCsinglet(crc0, next, -BOOST_PP_SUB(total, count) * 8);\n\n// Numbers taken directly from intel whitepaper.\n// clang-format off\nconst uint64_t clmul_constants[] = {\n    0x14cd00bd6, 0x105ec76f0, 0x0ba4fc28e, 0x14cd00bd6,\n    0x1d82c63da, 0x0f20c0dfe, 0x09e4addf8, 0x0ba4fc28e,\n    0x039d3b296, 0x1384aa63a, 0x102f9b8a2, 0x1d82c63da,\n    0x14237f5e6, 0x01c291d04, 0x00d3b6092, 0x09e4addf8,\n    0x0c96cfdc0, 0x0740eef02, 0x18266e456, 0x039d3b296,\n    0x0daece73e, 0x0083a6eec, 0x0ab7aff2a, 0x102f9b8a2,\n    0x1248ea574, 0x1c1733996, 0x083348832, 0x14237f5e6,\n    0x12c743124, 0x02ad91c30, 0x0b9e02b86, 0x00d3b6092,\n    0x018b33a4e, 0x06992cea2, 0x1b331e26a, 0x0c96cfdc0,\n    0x17d35ba46, 0x07e908048, 0x1bf2e8b8a, 0x18266e456,\n    0x1a3e0968a, 0x11ed1f9d8, 0x0ce7f39f4, 0x0daece73e,\n    0x061d82e56, 0x0f1d0f55e, 0x0d270f1a2, 0x0ab7aff2a,\n    0x1c3f5f66c, 0x0a87ab8a8, 0x12ed0daac, 0x1248ea574,\n    0x065863b64, 0x08462d800, 0x11eef4f8e, 0x083348832,\n    0x1ee54f54c, 0x071d111a8, 0x0b3e32c28, 0x12c743124,\n    0x0064f7f26, 0x0ffd852c6, 0x0dd7e3b0c, 0x0b9e02b86,\n    0x0f285651c, 0x0dcb17aa4, 0x010746f3c, 0x018b33a4e,\n    0x1c24afea4, 0x0f37c5aee, 0x0271d9844, 0x1b331e26a,\n    0x08e766a0c, 0x06051d5a2, 0x093a5f730, 0x17d35ba46,\n    0x06cb08e5c, 0x11d5ca20e, 0x06b749fb2, 0x1bf2e8b8a,\n    0x1167f94f2, 0x021f3d99c, 0x0cec3662e, 0x1a3e0968a,\n    0x19329634a, 0x08f158014, 0x0e6fc4e6a, 0x0ce7f39f4,\n    0x08227bb8a, 0x1a5e82106, 0x0b0cd4768, 0x061d82e56,\n    0x13c2b89c4, 0x188815ab2, 0x0d7a4825c, 0x0d270f1a2,\n    0x10f5ff2ba, 0x105405f3e, 0x00167d312, 0x1c3f5f66c,\n    0x0f6076544, 0x0e9adf796, 0x026f6a60a, 0x12ed0daac,\n    0x1a2adb74e, 0x096638b34, 0x19d34af3a, 0x065863b64,\n    0x049c3cc9c, 0x1e50585a0, 0x068bce87a, 0x11eef4f8e,\n    0x1524fa6c6, 0x19f1c69dc, 0x16cba8aca, 0x1ee54f54c,\n    0x042d98888, 0x12913343e, 0x1329d9f7e, 0x0b3e32c28,\n    0x1b1c69528, 0x088f25a3a, 0x02178513a, 0x0064f7f26,\n    0x0e0ac139e, 0x04e36f0b0, 0x0170076fa, 0x0dd7e3b0c,\n    0x141a1a2e2, 0x0bd6f81f8, 0x16ad828b4, 0x0f285651c,\n    0x041d17b64, 0x19425cbba, 0x1fae1cc66, 0x010746f3c,\n    0x1a75b4b00, 0x18db37e8a, 0x0f872e54c, 0x1c24afea4,\n    0x01e41e9fc, 0x04c144932, 0x086d8e4d2, 0x0271d9844,\n    0x160f7af7a, 0x052148f02, 0x05bb8f1bc, 0x08e766a0c,\n    0x0a90fd27a, 0x0a3c6f37a, 0x0b3af077a, 0x093a5f730,\n    0x04984d782, 0x1d22c238e, 0x0ca6ef3ac, 0x06cb08e5c,\n    0x0234e0b26, 0x063ded06a, 0x1d88abd4a, 0x06b749fb2,\n    0x04597456a, 0x04d56973c, 0x0e9e28eb4, 0x1167f94f2,\n    0x07b3ff57a, 0x19385bf2e, 0x0c9c8b782, 0x0cec3662e,\n    0x13a9cba9e, 0x0e417f38a, 0x093e106a4, 0x19329634a,\n    0x167001a9c, 0x14e727980, 0x1ddffc5d4, 0x0e6fc4e6a,\n    0x00df04680, 0x0d104b8fc, 0x02342001e, 0x08227bb8a,\n    0x00a2a8d7e, 0x05b397730, 0x168763fa6, 0x0b0cd4768,\n    0x1ed5a407a, 0x0e78eb416, 0x0d2c3ed1a, 0x13c2b89c4,\n    0x0995a5724, 0x1641378f0, 0x19b1afbc4, 0x0d7a4825c,\n    0x109ffedc0, 0x08d96551c, 0x0f2271e60, 0x10f5ff2ba,\n    0x00b0bf8ca, 0x00bf80dd2, 0x123888b7a, 0x00167d312,\n    0x1e888f7dc, 0x18dcddd1c, 0x002ee03b2, 0x0f6076544,\n    0x183e8d8fe, 0x06a45d2b2, 0x133d7a042, 0x026f6a60a,\n    0x116b0f50c, 0x1dd3e10e8, 0x05fabe670, 0x1a2adb74e,\n    0x130004488, 0x0de87806c, 0x000bcf5f6, 0x19d34af3a,\n    0x18f0c7078, 0x014338754, 0x017f27698, 0x049c3cc9c,\n    0x058ca5f00, 0x15e3e77ee, 0x1af900c24, 0x068bce87a,\n    0x0b5cfca28, 0x0dd07448e, 0x0ded288f8, 0x1524fa6c6,\n    0x059f229bc, 0x1d8048348, 0x06d390dec, 0x16cba8aca,\n    0x037170390, 0x0a3e3e02c, 0x06353c1cc, 0x042d98888,\n    0x0c4584f5c, 0x0d73c7bea, 0x1f16a3418, 0x1329d9f7e,\n    0x0531377e2, 0x185137662, 0x1d8d9ca7c, 0x1b1c69528,\n    0x0b25b29f2, 0x18a08b5bc, 0x19fb2a8b0, 0x02178513a,\n    0x1a08fe6ac, 0x1da758ae0, 0x045cddf4e, 0x0e0ac139e,\n    0x1a91647f2, 0x169cf9eb0, 0x1a0f717c4, 0x0170076fa,\n};\n// clang-format on\n\n/*\n * CombineCRC performs pclmulqdq multiplication of 2 partial CRC's and a well\n * chosen constant and xor's these with the remaining CRC.\n */\nuint64_t CombineCRC(\n    size_t block_size,\n    uint64_t crc0,\n    uint64_t crc1,\n    uint64_t crc2,\n    const uint64_t* next2) {\n  const auto multiplier =\n      *(reinterpret_cast<const __m128i*>(clmul_constants) + block_size - 1);\n  const auto crc0_xmm = _mm_set_epi64x(0, crc0);\n  const auto res0 = _mm_clmulepi64_si128(crc0_xmm, multiplier, 0x00);\n  const auto crc1_xmm = _mm_set_epi64x(0, crc1);\n  const auto res1 = _mm_clmulepi64_si128(crc1_xmm, multiplier, 0x10);\n  const auto res = _mm_xor_si128(res0, res1);\n  crc0 = _mm_cvtsi128_si64(res);\n  crc0 = crc0 ^ *((uint64_t*)next2 - 1);\n  crc2 = _mm_crc32_u64(crc2, crc0);\n  return crc2;\n}\n\n// Generates a block that will crc up to 7 bytes of unaligned data.\n// Always inline to avoid overhead on small crc sizes.\nFOLLY_ALWAYS_INLINE void align_to_8(\n    size_t align,\n    uint64_t& crc0, // crc so far, updated on return\n    const unsigned char*& next) { // next data pointer, updated on return\n  uint32_t crc32bit = static_cast<uint32_t>(crc0);\n  if (align & 0x04) {\n    crc32bit = _mm_crc32_u32(crc32bit, *(uint32_t*)next);\n    next += sizeof(uint32_t);\n  }\n  if (align & 0x02) {\n    crc32bit = _mm_crc32_u16(crc32bit, *(uint16_t*)next);\n    next += sizeof(uint16_t);\n  }\n  if (align & 0x01) {\n    crc32bit = _mm_crc32_u8(crc32bit, *(next));\n    next++;\n  }\n  crc0 = crc32bit;\n}\n\n// The main loop for large crc sizes. Generates three crc32c\n// streams, of varying block sizes, using a duff's device.\nvoid triplet_loop(\n    size_t block_size,\n    uint64_t& crc0, // crc so far, updated on return\n    const unsigned char*& next, // next data pointer, updated on return\n    size_t n) { // block count\n  uint64_t crc1 = 0, crc2 = 0;\n  // points to the first byte of the next block\n  const uint64_t* next0 = (uint64_t*)next + block_size;\n  const uint64_t* next1 = next0 + block_size;\n  const uint64_t* next2 = next1 + block_size;\n\n  // Use Duff's device, a for() loop inside a switch()\n  // statement. This needs to execute at least once, round len\n  // down to nearest triplet multiple\n  switch (block_size) {\n    case 128:\n      do {\n        // jumps here for a full block of len 128\n        CRCtriplet(crc, next, -128);\n\n        // Generates case statements from 127 to 2 of form:\n        // case 127:\n        //    CRCtriplet(crc, next, -127);\n        //\n        // MSVC has a max preprocessor expansion depth of 255, which is\n        // exceeded if this is a single statement.\n        BOOST_PP_REPEAT_FROM_TO(0, 64, CASEREPEAT_TRIPLET, 126);\n        BOOST_PP_REPEAT_FROM_TO(0, 62, CASEREPEAT_TRIPLET, 62);\n\n        // For the last byte, the three crc32c streams must be combined\n        // using carry-less multiplication.\n        case 1:\n          CRCduplet(crc, next, -1); // the final triplet is actually only 2\n          crc0 = CombineCRC(block_size, crc0, crc1, crc2, next2);\n          if (--n > 0) {\n            crc1 = crc2 = 0;\n            block_size = 128;\n            // points to the first byte of the next block\n            next0 = next2 + 128;\n            next1 = next0 + 128; // from here on all blocks are 128 long\n            next2 = next1 + 128;\n          }\n          [[fallthrough]];\n        case 0:;\n      } while (n > 0);\n  }\n\n  next = (const unsigned char*)next2;\n}\n\n} // namespace crc32_detail\n\n/* Compute CRC-32C using the Intel hardware instruction. */\nFOLLY_TARGET_ATTRIBUTE(\"sse4.2\")\nuint32_t crc32c_hw(const uint8_t* buf, size_t len, uint32_t crc) {\n  const unsigned char* next = (const unsigned char*)buf;\n  size_t count;\n  uint64_t crc0;\n  crc0 = crc;\n\n  if (len >= 8) {\n    // if len > 216 then align and use triplets\n    if (len > 216) {\n      size_t align = (8 - (uintptr_t)next) & 7;\n      crc32_detail::align_to_8(align, crc0, next);\n      len -= align;\n\n      count = len / 24; // number of triplets\n      len %= 24; // bytes remaining\n      size_t n = count >> 7; // #blocks = first block + full blocks\n      size_t block_size = count & 127;\n      if (block_size == 0) {\n        block_size = 128;\n      } else {\n        n++;\n      }\n\n      // This is a separate function call mainly to stop\n      // clang from spilling registers.\n      crc32_detail::triplet_loop(block_size, crc0, next, n);\n    }\n\n    size_t count2 = len >> 3;\n    len = len & 7;\n    next += (count2 * 8);\n\n    // Generates a duff device for the last 128 bytes of aligned data.\n    switch (count2) {\n      // Generates case statements of the form:\n      // case 27:\n      //   CRCsinglet(crc0, next, -27 * 8);\n      BOOST_PP_REPEAT_FROM_TO(0, 27, CASEREPEAT_SINGLET, 27);\n      case 0:;\n    }\n  }\n\n  // compute the crc for up to seven trailing bytes\n  crc32_detail::align_to_8(len, crc0, next);\n  return (uint32_t)crc0;\n}\n\n#elif FOLLY_ARM_FEATURE_CRC32\n\n// crc32c_hw is defined in external/nvidia/hash/detail/Crc32cDetail.cpp\n\n#else\n\nuint32_t crc32c_hw(\n    const uint8_t* /* buf */, size_t /* len */, uint32_t /* crc */) {\n  throw std::runtime_error(\"crc32_hw is not implemented on this platform\");\n}\n\n#endif // !defined(FOLLY_ARM_FEATURE_CRC32)\n\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/detail/RandomSeed.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <cstdlib>\n\n#include <folly/Portability.h>\n\nnamespace folly {\nnamespace hash {\nnamespace detail {\n\n// A non-deterministic seed to prevent users depend on particular values of the\n// hash. Based on the same idea as Google's Abseil implementation [1]:\n// leverage ASLR (Address Space Layout Randomization) [2] to get a per-run\n// random value.\n//\n// [1]:\n// https://github.com/abseil/abseil-cpp/blob/76fd1e96c71ad20fe08f1b7d18c6c55e197df85e/absl/hash/internal/hash.h#L1299-L1323\n// [2]: https://en.wikipedia.org/wiki/Address_space_layout_randomization\nclass RandomSeed {\n public:\n  static uint64_t seed() {\n    // Abseil's implementation takes an address of static variable. It works\n    // fine, as long as hash values doesn't cross shared object/binary\n    // boundary. Unfortunately, we observed few such cases, so we are taking\n    // address of `std::abort` instead to increase chances of having single\n    // address across multiple shared objects.\n    return kIsDebug\n        ? static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&std::abort))\n        : 0ULL;\n  }\n};\n\n} // namespace detail\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/detail/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"random_seed_test\",\n    srcs = [\"RandomSeedTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:portability\",\n        \"//folly/hash/detail:random_seed\",\n        \"//folly/portability:gtest\",\n        \"//folly/test:test_utils\",\n    ],\n)\n"
  },
  {
    "path": "folly/hash/detail/test/RandomSeedTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/detail/RandomSeed.h>\n\n#include <folly/Portability.h>\n#include <folly/portability/GTest.h>\n#include <folly/test/TestUtils.h>\n\nTEST(RandomSeedTest, Seed) {\n  const uint64_t seed = folly::hash::detail::RandomSeed::seed();\n  EXPECT_EQ(folly::kIsDebug, seed != 0);\n}\n"
  },
  {
    "path": "folly/hash/rapidhash.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/external/rapidhash/rapidhash.h>\n\nnamespace folly {\nnamespace hash {\n\n// The values returned by Hash, Hash32, and Hash64 are only guaranteed to\n// be the same within the same process.  Fingerpring32 and Fingerprint64\n// are fixed algorithms that always give the same result.\n\n// uint64_t rapidhash(const char* key, size_t len)\nusing external::rapidhash;\n\n// uint64_t rapidhash_with_seed(const char* key, size_t len, uint64_t seed)\nusing external::rapidhash_with_seed;\n\n// uint64_t rapidhashMicro(const char* key, size_t len)\nusing external::rapidhashMicro;\n\n// uint64_t rapidhashMicro_with_seed(const char* key, size_t len, uint64_t seed)\nusing external::rapidhashMicro_with_seed;\n\n// uint64_t rapidhashNano(const char* key, size_t len)\nusing external::rapidhashNano;\n\n// uint64_t rapidhashNano_with_seed(const char* key, size_t len, uint64_t seed)\nusing external::rapidhashNano_with_seed;\n\n} // namespace hash\n} // namespace folly\n"
  },
  {
    "path": "folly/hash/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"checksum_test\",\n    srcs = [\"ChecksumTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:portability\",\n        \"//folly:random\",\n        \"//folly/external/fast-crc32:avx512_crc32c_v8s3x4\",\n        \"//folly/external/fast-crc32:neon_crc32c_v3s4x2e_v2\",\n        \"//folly/external/fast-crc32:neon_eor3_crc32c_v8s2x4e_s2x1\",\n        \"//folly/external/fast-crc32:sse_crc32c_v8s3x3\",\n        \"//folly/hash:checksum\",\n        \"//folly/hash:hash\",\n        \"//folly/hash/detail:checksum_detail\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:gtest\",\n    ],\n    external_deps = [\n        \"boost\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"checksum_benchmark\",\n    srcs = [\"ChecksumBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:memory\",\n        \"//folly/hash:checksum\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"farm_hash_test\",\n    srcs = [\"FarmHashTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:farm_hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"hash_benchmark\",\n    srcs = [\"HashBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:benchmark\",\n        \"//folly:preprocessor\",\n        \"//folly/hash:hash\",\n        \"//folly/hash:murmur_hash\",\n        \"//folly/hash:rapidhash\",\n        \"//folly/lang:keep\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"hash_test\",\n    srcs = [\"HashTest.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:conv\",\n        \"//folly:map_util\",\n        \"//folly:random\",\n        \"//folly:range\",\n        \"//folly/hash:hash\",\n        \"//folly/lang:cstring_view\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"rapidhash_test\",\n    srcs = [\"RapidHashTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:rapidhash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"spooky_hash_v1_test\",\n    srcs = [\"SpookyHashV1Test.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/hash:spooky_hash_v1\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:time\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"spooky_hash_v2_test\",\n    srcs = [\"SpookyHashV2Test.cpp\"],\n    headers = [],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/hash:spooky_hash_v2\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:time\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"traits_test\",\n    srcs = [\"traits_test.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:traits\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"murmur_hash_test\",\n    srcs = [\"MurmurHashTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:murmur_hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"std_compatible_hash_test\",\n    srcs = [\"StdCompatibleHashTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"unique_hash_key_test\",\n    srcs = [\"UniqueHashKeyTest.cpp\"],\n    headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/container:f14_hash\",\n        \"//folly/container:map_util\",\n        \"//folly/hash:unique_hash_key\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"fnv_hash_test\",\n    srcs = [\"FnvHashTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:fnv_hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"hsieh_hash_test\",\n    srcs = [\"HsiehHashTest.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly/hash:hsieh_hash\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/hash/test/ChecksumBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <random>\n#include <glog/logging.h>\n#include <folly/Benchmark.h>\n#include <folly/Memory.h>\n#include <folly/hash/Checksum.h>\n\nconstexpr size_t kBufSize = 512 * 1024;\nuint8_t* buf;\n\n#define BENCH_CRC32(S)                                   \\\n  BENCHMARK(crc32_##S) {                                 \\\n    folly::doNotOptimizeAway(folly::crc32(buf, (S), 2)); \\\n  }\n\n#define BENCH_CRC32C(S)                                   \\\n  BENCHMARK(crc32c_##S) {                                 \\\n    folly::doNotOptimizeAway(folly::crc32c(buf, (S), 2)); \\\n  }\n\nBENCH_CRC32(512)\nBENCH_CRC32(1024)\nBENCH_CRC32(2048)\nBENCH_CRC32(4096)\nBENCH_CRC32(8192)\nBENCH_CRC32(16384)\nBENCH_CRC32(32768)\nBENCH_CRC32(65536)\nBENCH_CRC32(131072)\nBENCH_CRC32(262144)\nBENCH_CRC32(524288)\n\nBENCH_CRC32C(512)\nBENCH_CRC32C(1024)\nBENCH_CRC32C(2048)\nBENCH_CRC32C(4096)\nBENCH_CRC32C(8192)\nBENCH_CRC32C(16384)\nBENCH_CRC32C(32768)\nBENCH_CRC32C(65536)\nBENCH_CRC32C(131072)\nBENCH_CRC32C(262144)\nBENCH_CRC32C(524288)\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  google::InitGoogleLogging(argv[0]);\n\n  buf = static_cast<uint8_t*>(folly::aligned_malloc(kBufSize + 64, 4096));\n\n  std::default_random_engine rng(1729); // Deterministic seed.\n  std::uniform_int_distribution<uint16_t> dist(0, 255);\n  std::generate(buf, buf + kBufSize, [&]() { return dist(rng); });\n\n  folly::runBenchmarks();\n\n  folly::aligned_free(buf);\n\n  return 0;\n}\n\n/*\nIntel(R) Xeon(R) Gold 6138 CPU @ 2.00GHz\n$ checksum_benchmark --bm_slice_usec=10000\n============================================================================\nfolly/hash/test/ChecksumBenchmark.cpp           relative  time/iter  iters/s\n============================================================================\ncrc32_512                                                   55.73ns   17.94M\ncrc32_1024                                                  85.15ns   11.74M\ncrc32_2048                                                 116.29ns    8.60M\ncrc32_4096                                                 191.03ns    5.23M\ncrc32_8192                                                 341.44ns    2.93M\ncrc32_16384                                                627.76ns    1.59M\ncrc32_32768                                                  1.21us  827.16K\n============================================================================\n*/\n"
  },
  {
    "path": "folly/hash/test/ChecksumTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/Checksum.h>\n\n#include <boost/crc.hpp>\n\n#include <folly/Benchmark.h>\n#include <folly/Portability.h>\n#include <folly/Random.h>\n#include <folly/external/fast-crc32/avx512_crc32c_v8s3x4.h>\n#include <folly/external/fast-crc32/neon_crc32c_v3s4x2e_v2.h>\n#include <folly/external/fast-crc32/neon_eor3_crc32c_v8s2x4e_s2x1.h>\n#include <folly/external/fast-crc32/sse_crc32c_v8s3x3.h>\n#include <folly/hash/Hash.h>\n#include <folly/hash/detail/ChecksumDetail.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/GTest.h>\n\nnamespace {\nconst unsigned int BUFFER_SIZE = 512 * 1024 * sizeof(uint64_t);\nuint8_t buffer[BUFFER_SIZE];\n\nstruct ExpectedResult {\n  size_t offset;\n  size_t length;\n  uint32_t crc32c;\n};\n\nExpectedResult expectedResults[] = {\n    // Zero-byte input\n    {0, 0, ~0U},\n    // Small aligned inputs to test special cases in SIMD implementations\n    {8, 1, 1543413366},\n    {8, 2, 523493126},\n    {8, 3, 1560427360},\n    {8, 4, 3422504776},\n    {8, 5, 447841138},\n    {8, 6, 3910050499},\n    {8, 7, 3346241981},\n    // Small unaligned inputs\n    {9, 1, 3855826643},\n    {10, 2, 560880875},\n    {11, 3, 1479707779},\n    {12, 4, 2237687071},\n    {13, 5, 4063855784},\n    {14, 6, 2553454047},\n    {15, 7, 1349220140},\n    // Larger inputs to test leftover chunks at the end of aligned blocks\n    {8, 8, 627613930},\n    {8, 9, 2105929409},\n    {8, 10, 2447068514},\n    {8, 11, 863807079},\n    {8, 12, 292050879},\n    {8, 13, 1411837737},\n    {8, 14, 2614515001},\n    {8, 15, 3579076296},\n    {8, 16, 2897079161},\n    {8, 17, 675168386},\n    // Much larger inputs\n    {0, BUFFER_SIZE, 2096790750},\n    {1, BUFFER_SIZE / 2, 3854797577},\n};\n\nvoid testCRC32C(\n    std::function<uint32_t(const uint8_t*, size_t, uint32_t)> impl) {\n  for (auto expected : expectedResults) {\n    uint32_t result = impl(buffer + expected.offset, expected.length, ~0U);\n    EXPECT_EQ(expected.crc32c, result);\n  }\n}\n\nvoid testCRC32CContinuation(\n    std::function<uint32_t(const uint8_t*, size_t, uint32_t)> impl) {\n  for (auto expected : expectedResults) {\n    size_t partialLength = expected.length / 2;\n    uint32_t partialChecksum =\n        impl(buffer + expected.offset, partialLength, ~0U);\n    uint32_t result = impl(\n        buffer + expected.offset + partialLength,\n        expected.length - partialLength,\n        partialChecksum);\n    EXPECT_EQ(expected.crc32c, result);\n  }\n}\n\nvoid testMatchesBoost32Type() {\n  for (auto expected : expectedResults) {\n    boost::crc_32_type result;\n    result.process_bytes(buffer + expected.offset, expected.length);\n    const uint32_t boostResult = result.checksum();\n    const uint32_t follyResult =\n        folly::crc32_type(buffer + expected.offset, expected.length);\n    EXPECT_EQ(follyResult, boostResult);\n  }\n}\n\n} // namespace\n\nTEST(Checksum, crc32cSoftware) {\n  testCRC32C(folly::detail::crc32c_sw);\n}\n\nTEST(Checksum, crc32cContinuationSoftware) {\n  testCRC32CContinuation(folly::detail::crc32c_sw);\n}\n\nTEST(Checksum, crc32cHardware) {\n  if (folly::detail::crc32c_hw_supported()) {\n    testCRC32C(folly::detail::crc32c_hw);\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareEq) {\n  if (folly::detail::crc32c_hw_supported()) {\n    for (int i = 0; i < 1000; i++) {\n      auto sw = folly::detail::crc32c_sw(buffer, i, 0);\n      auto hw = folly::detail::crc32c_hw(buffer, i, 0);\n      EXPECT_EQ(sw, hw);\n    }\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cContinuationHardware) {\n  if (folly::detail::crc32c_hw_supported()) {\n    testCRC32CContinuation(folly::detail::crc32c_hw);\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareSse42) {\n  if (folly::detail::crc32c_hw_supported_sse42()) {\n    testCRC32C(folly::detail::sse_crc32c_v8s3x3);\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping SSE4.2 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareEqSse42) {\n  if (folly::detail::crc32c_hw_supported_sse42()) {\n    for (size_t i = 0; i < 1000; i++) {\n      auto sw = folly::detail::crc32c_sw(buffer, i, 0);\n      auto hw = folly::detail::sse_crc32c_v8s3x3(buffer, i, 0);\n      ASSERT_EQ(sw, hw);\n    }\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping SSE4.2 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cContinuationHardwareSse42) {\n  if (folly::detail::crc32c_hw_supported_sse42()) {\n    testCRC32CContinuation(folly::detail::sse_crc32c_v8s3x3);\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping SSE4.2 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareAvx512) {\n  if (folly::detail::crc32c_hw_supported_avx512()) {\n    testCRC32C(folly::detail::avx512_crc32c_v8s3x4);\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping AVX512 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareEqAvx512) {\n  if (folly::detail::crc32c_hw_supported_avx512()) {\n    for (size_t i = 0; i < 1000; i++) {\n      auto sw = folly::detail::crc32c_sw(buffer, i, 0);\n      auto hw = folly::detail::avx512_crc32c_v8s3x4(buffer, i, 0);\n      ASSERT_EQ(sw, hw);\n    }\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping AVX512 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cContinuationHardwareAvx512) {\n  if (folly::detail::crc32c_hw_supported_avx512()) {\n    testCRC32CContinuation(folly::detail::avx512_crc32c_v8s3x4);\n  } else {\n#if FOLLY_X64\n    LOG(WARNING) << \"skipping AVX512 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareNeon) {\n  if (folly::detail::crc32c_hw_supported_neon()) {\n    testCRC32C(folly::detail::neon_crc32c_v3s4x2e_v2);\n  } else {\n#if FOLLY_AARCH64\n    LOG(WARNING) << \"skipping NEON hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareEqNeon) {\n  if (folly::detail::crc32c_hw_supported_neon()) {\n    for (size_t i = 0; i < 1000; i++) {\n      auto sw = folly::detail::crc32c_sw(buffer, i, 0);\n      auto hw = folly::detail::neon_crc32c_v3s4x2e_v2(buffer, i, 0);\n      ASSERT_EQ(sw, hw);\n    }\n  } else {\n#if FOLLY_AARCH64\n    LOG(WARNING) << \"skipping NEON hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cContinuationHardwareNeon) {\n  if (folly::detail::crc32c_hw_supported_neon()) {\n    testCRC32CContinuation(folly::detail::neon_crc32c_v3s4x2e_v2);\n  } else {\n#if FOLLY_AARCH64\n    LOG(WARNING) << \"skipping NEON hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareNeonEor3Sha3) {\n  if (folly::detail::crc32c_hw_supported_neon_eor3_sha3()) {\n    testCRC32C(folly::detail::neon_eor3_crc32c_v8s2x4e_s2x1);\n  } else {\n#if FOLLY_AARCH64\n    LOG(WARNING) << \"skipping NEON+EOR3+SHA3 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cHardwareEqNeonEor3Sha3) {\n  if (folly::detail::crc32c_hw_supported_neon_eor3_sha3()) {\n    for (size_t i = 0; i < 1000; i++) {\n      auto sw = folly::detail::crc32c_sw(buffer, i, 0);\n      auto hw = folly::detail::neon_eor3_crc32c_v8s2x4e_s2x1(buffer, i, 0);\n      ASSERT_EQ(sw, hw);\n    }\n  } else {\n#if FOLLY_AARCH64\n    LOG(WARNING) << \"skipping NEON+EOR3+SHA3 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\nTEST(Checksum, crc32cContinuationHardwareNeonEor3Sha3) {\n  if (folly::detail::crc32c_hw_supported_neon_eor3_sha3()) {\n    testCRC32CContinuation(folly::detail::neon_eor3_crc32c_v8s2x4e_s2x1);\n  } else {\n#if FOLLY_AARCH64\n    LOG(WARNING) << \"skipping NEON+EOR3+SHA3 hardware-accelerated CRC-32C tests\"\n                 << \" (not supported on this CPU)\";\n#endif\n  }\n}\n\n// Test on very large buffer inputs to attempt to sanity check 32-bit\n// overflow problems on 64-bit platforms.\n#ifdef __LP64__\nTEST(Checksum, crc32clargeBuffers) {\n  constexpr size_t kLargeBufSz = 5ull * 1024 * 1024 * 1024; // 5GiB\n  auto buf = std::make_unique<uint8_t[]>(kLargeBufSz); // 5GiB\n  auto* bufp = buf.get();\n  // Fill with non-zero pattern.\n  memset(bufp, 0x2e, kLargeBufSz);\n\n  constexpr uint32_t kCrc = 2860399007;\n\n  if (folly::detail::crc32c_hw_supported_sse42()) {\n    auto crcSse42 = folly::detail::sse_crc32c_v8s3x3(bufp, kLargeBufSz, ~0);\n    ASSERT_EQ(kCrc, crcSse42);\n    auto crcHw = folly::detail::crc32c_hw(bufp, kLargeBufSz, ~0);\n    ASSERT_EQ(kCrc, crcHw);\n  }\n  if (folly::detail::crc32c_hw_supported_avx512()) {\n    auto crcAvx = folly::detail::avx512_crc32c_v8s3x4(bufp, kLargeBufSz, ~0);\n    ASSERT_EQ(kCrc, crcAvx);\n  }\n  if (folly::detail::crc32c_hw_supported_neon()) {\n    auto crcHw = folly::detail::neon_crc32c_v3s4x2e_v2(bufp, kLargeBufSz, ~0);\n    ASSERT_EQ(kCrc, crcHw);\n  }\n  if (folly::detail::crc32c_hw_supported_neon_eor3_sha3()) {\n    auto crcHw =\n        folly::detail::neon_eor3_crc32c_v8s2x4e_s2x1(bufp, kLargeBufSz, ~0);\n    ASSERT_EQ(kCrc, crcHw);\n  }\n}\n#endif\n\nTEST(Checksum, crc32cAutodetect) {\n  testCRC32C(folly::crc32c);\n}\n\nTEST(Checksum, crc32cContinuationAutodetect) {\n  testCRC32CContinuation(folly::crc32c);\n}\n\nTEST(Checksum, crc32) {\n  if (folly::detail::crc32_hw_supported()) {\n    // Just check that sw and hw match\n    for (auto expected : expectedResults) {\n      uint32_t sw_res =\n          folly::detail::crc32_sw(buffer + expected.offset, expected.length, 0);\n      uint32_t hw_res =\n          folly::detail::crc32_hw(buffer + expected.offset, expected.length, 0);\n      EXPECT_EQ(sw_res, hw_res);\n    }\n  } else {\n    LOG(WARNING) << \"skipping hardware-accelerated CRC-32 tests\"\n                 << \" (not supported on this CPU)\";\n  }\n}\n\nTEST(Checksum, crc32Continuation) {\n  if (folly::detail::crc32_hw_supported()) {\n    // Just check that sw and hw match\n    for (auto expected : expectedResults) {\n      auto halflen = expected.length / 2;\n      uint32_t sw_res =\n          folly::detail::crc32_sw(buffer + expected.offset, halflen, 0);\n      sw_res = folly::detail::crc32_sw(\n          buffer + expected.offset + halflen, halflen, sw_res);\n      uint32_t hw_res =\n          folly::detail::crc32_hw(buffer + expected.offset, halflen, 0);\n      hw_res = folly::detail::crc32_hw(\n          buffer + expected.offset + halflen, halflen, hw_res);\n      EXPECT_EQ(sw_res, hw_res);\n      uint32_t sw_res2 =\n          folly::detail::crc32_sw(buffer + expected.offset, halflen * 2, 0);\n      EXPECT_EQ(sw_res, sw_res2);\n      uint32_t hw_res2 =\n          folly::detail::crc32_hw(buffer + expected.offset, halflen * 2, 0);\n      EXPECT_EQ(hw_res, hw_res2);\n    }\n  } else {\n    LOG(WARNING) << \"skipping hardware-accelerated CRC-32 tests\"\n                 << \" (not supported on this CPU)\";\n  }\n}\n\nTEST(Checksum, crc32Type) {\n  // Test that crc32_type matches boost::crc_32_type\n  testMatchesBoost32Type();\n}\n\nTEST(Checksum, crc32Combine) {\n  for (size_t totlen = 1024; totlen < BUFFER_SIZE; totlen += BUFFER_SIZE / 8) {\n    auto mid = folly::Random::rand64(0, totlen);\n    auto crc1 = folly::crc32(&buffer[0], mid, 0);\n    auto crc2 = folly::crc32(&buffer[mid], totlen - mid, 0);\n    auto crcfull = folly::crc32(&buffer[0], totlen, 0);\n    auto combined = folly::crc32_combine(crc1, crc2, totlen - mid);\n    EXPECT_EQ(combined, crcfull);\n  }\n}\n\nTEST(Checksum, crc32cCombine) {\n  for (size_t totlen = 1024; totlen < BUFFER_SIZE; totlen += BUFFER_SIZE / 8) {\n    auto mid = folly::Random::rand64(0, totlen);\n    auto crc1 = folly::crc32c(&buffer[0], mid, 0);\n    auto crc2 = folly::crc32c(&buffer[mid], totlen - mid, 0);\n    auto crcfull = folly::crc32c(&buffer[0], totlen, 0);\n    auto combined = folly::crc32c_combine(crc1, crc2, totlen - mid);\n    EXPECT_EQ(combined, crcfull);\n  }\n}\n\nTEST(Checksum, crc32cCombineSeed_explicit_starting_checksum) {\n  auto random32 = folly::Random::rand32();\n  std::vector<uint32_t> startingChecksums = {0U, ~0U, random32};\n\n  for (auto startingChecksum : startingChecksums) {\n    for (size_t totlen = 1024; totlen < BUFFER_SIZE;\n         totlen += BUFFER_SIZE / 8) {\n      auto mid = folly::Random::rand64(0, totlen);\n      auto crc1 = folly::crc32c(&buffer[0], mid, startingChecksum);\n      auto crc2 = folly::crc32c(&buffer[mid], totlen - mid, startingChecksum);\n      auto crcfull = folly::crc32c(&buffer[0], totlen, startingChecksum);\n      auto combined = folly::crc32c_combine_seed(\n          crc1, crc2, totlen - mid, startingChecksum);\n      EXPECT_EQ(combined, crcfull)\n          << \"Failed for startingChecksum=\" << startingChecksum\n          << \" totlen=\" << totlen << \" mid=\" << mid;\n    }\n  }\n}\n\nvoid benchmarkHardwareCRC32C(unsigned long iters, size_t blockSize) {\n  if (folly::detail::crc32c_hw_supported()) {\n    uint32_t checksum;\n    for (unsigned long i = 0; i < iters; i++) {\n      checksum = folly::detail::crc32c_hw(buffer, blockSize);\n      folly::doNotOptimizeAway(checksum);\n    }\n  } else {\n    LOG(WARNING) << \"skipping hardware-accelerated CRC-32C benchmarks\"\n                 << \" (not supported on this CPU)\";\n  }\n}\n\nvoid benchmarkSoftwareCRC32C(unsigned long iters, size_t blockSize) {\n  uint32_t checksum;\n  for (unsigned long i = 0; i < iters; i++) {\n    checksum = folly::detail::crc32c_sw(buffer, blockSize);\n    folly::doNotOptimizeAway(checksum);\n  }\n}\n\nvoid benchmarkHardwareCRC32(unsigned long iters, size_t blockSize) {\n  if (folly::detail::crc32_hw_supported()) {\n    uint32_t checksum;\n    for (unsigned long i = 0; i < iters; i++) {\n      checksum = folly::detail::crc32_hw(buffer, blockSize);\n      folly::doNotOptimizeAway(checksum);\n    }\n  } else {\n    LOG(WARNING) << \"skipping hardware-accelerated CRC-32 benchmarks\"\n                 << \" (not supported on this CPU)\";\n  }\n}\n\nvoid benchmarkSoftwareCRC32(unsigned long iters, size_t blockSize) {\n  uint32_t checksum;\n  for (unsigned long i = 0; i < iters; i++) {\n    checksum = folly::detail::crc32_sw(buffer, blockSize);\n    folly::doNotOptimizeAway(checksum);\n  }\n}\n\nvoid benchmarkCombineHardwareCrc32(unsigned long iters, size_t blockSize) {\n  // Arbitrarily chosen checksums\n  uint32_t checksum1 = 0xEDB88320;\n  uint32_t checksum2 = 0x82F63B78;\n  uint32_t result;\n  for (unsigned long i = 0; i < iters; i++) {\n    result = folly::crc32_combine(checksum1, checksum2, blockSize);\n    folly::doNotOptimizeAway(result);\n  }\n}\n\nvoid benchmarkCombineSoftwareLinear(unsigned long iters, size_t blockSize) {\n  // Arbitrarily chosen checksums\n  std::vector<uint8_t> zbuffer;\n  zbuffer.reserve(blockSize);\n  memset(zbuffer.data(), 0, blockSize);\n  uint32_t checksum1 = 0xEDB88320;\n  uint32_t checksum2 = 0x82F63B78;\n  uint32_t result;\n  for (unsigned long i = 0; i < iters; i++) {\n    result = folly::crc32c(zbuffer.data(), blockSize, checksum1);\n    result ^= checksum2;\n    folly::doNotOptimizeAway(result);\n  }\n}\n\nvoid benchmarkCombineHardwareCrc32c(unsigned long iters, size_t blockSize) {\n  // Arbitrarily chosen checksums\n  uint32_t checksum1 = 0xEDB88320;\n  uint32_t checksum2 = 0x82F63B78;\n  uint32_t result;\n  for (unsigned long i = 0; i < iters; i++) {\n    result = folly::crc32c_combine(checksum1, checksum2, blockSize);\n    folly::doNotOptimizeAway(result);\n  }\n}\n\n// This test fits easily in the L1 cache on modern server processors,\n// and thus it mainly measures the speed of the checksum computation.\nBENCHMARK(crc32c_hardware_1KB_block, iters) {\n  benchmarkHardwareCRC32C(iters, 1024);\n}\n\nBENCHMARK(crc32c_software_1KB_block, iters) {\n  benchmarkSoftwareCRC32C(iters, 1024);\n}\n\nBENCHMARK(crc32_hardware_1KB_block, iters) {\n  benchmarkHardwareCRC32(iters, 1024);\n}\n\nBENCHMARK(crc32_software_1KB_block, iters) {\n  benchmarkSoftwareCRC32(iters, 1024);\n}\n\nBENCHMARK_DRAW_LINE();\n\n// This test is too big for the L1 cache but fits in L2\nBENCHMARK(crc32c_hardware_64KB_block, iters) {\n  benchmarkHardwareCRC32C(iters, 64 * 1024);\n}\n\nBENCHMARK(crc32c_software_64KB_block, iters) {\n  benchmarkSoftwareCRC32C(iters, 64 * 1024);\n}\n\nBENCHMARK(crc32_hardware_64KB_block, iters) {\n  benchmarkHardwareCRC32(iters, 64 * 1024);\n}\n\nBENCHMARK(crc32_software_64KB_block, iters) {\n  benchmarkSoftwareCRC32(iters, 64 * 1024);\n}\n\nBENCHMARK_DRAW_LINE();\n\n// This test is too big for the L2 cache but fits in L3\nBENCHMARK(crc32c_hardware_512KB_block, iters) {\n  benchmarkHardwareCRC32C(iters, 512 * 1024);\n}\n\nBENCHMARK(crc32c_software_512KB_block, iters) {\n  benchmarkSoftwareCRC32C(iters, 512 * 1024);\n}\n\nBENCHMARK(crc32_hardware_512KB_block, iters) {\n  benchmarkHardwareCRC32(iters, 512 * 1024);\n}\n\nBENCHMARK(crc32_software_512KB_block, iters) {\n  benchmarkSoftwareCRC32(iters, 512 * 1024);\n}\n\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK(crc32_combine_linear_512KB_block, iters) {\n  benchmarkCombineSoftwareLinear(iters, 512 * 1024);\n}\n\nBENCHMARK(crc32_combine_512KB_block, iters) {\n  benchmarkCombineHardwareCrc32(iters, 512 * 1024);\n}\n\nBENCHMARK(crc32c_combine_512KB_block, iters) {\n  benchmarkCombineHardwareCrc32c(iters, 512 * 1024);\n}\n\nint main(int argc, char** argv) {\n  testing::InitGoogleTest(&argc, argv);\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n\n  // Populate a buffer with a deterministic pattern\n  // on which to compute checksums\n  const uint8_t* src = buffer;\n  uint64_t* dst = (uint64_t*)buffer;\n  const uint64_t* end = (const uint64_t*)(buffer + sizeof(buffer));\n  *dst++ = 0;\n  while (dst < end) {\n    *dst++ = folly::hash::fnv64_buf_BROKEN((const char*)src, sizeof(uint64_t));\n    src += sizeof(uint64_t);\n  }\n\n  auto ret = RUN_ALL_TESTS();\n  if (!ret && FLAGS_benchmark) {\n    folly::runBenchmarks();\n  }\n  return ret;\n}\n"
  },
  {
    "path": "folly/hash/test/FarmHashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/FarmHash.h>\n\n#include <folly/portability/GTest.h>\n\nTEST(farmhash, simple) {\n  EXPECT_NE(\n      folly::hash::farmhash::Hash(\"foo\", 3),\n      folly::hash::farmhash::Hash(\"bar\", 3));\n\n  EXPECT_NE(\n      folly::hash::farmhash::Hash32(\"foo\", 3),\n      folly::hash::farmhash::Hash32(\"bar\", 3));\n\n  EXPECT_NE(\n      folly::hash::farmhash::Hash64(\"foo\", 3),\n      folly::hash::farmhash::Hash64(\"bar\", 3));\n\n  EXPECT_NE(\n      folly::hash::farmhash::Fingerprint32(\"foo\", 3),\n      folly::hash::farmhash::Fingerprint32(\"bar\", 3));\n\n  EXPECT_NE(\n      folly::hash::farmhash::Fingerprint64(\"foo\", 3),\n      folly::hash::farmhash::Fingerprint64(\"bar\", 3));\n}\n"
  },
  {
    "path": "folly/hash/test/FnvHashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/FnvHash.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly::hash;\n\nTEST(FnvHash, Fnv32_BROKEN) {\n  const char* s1 = \"hello, world!\";\n  const uint32_t s1_res = 3605494790UL;\n  EXPECT_EQ(fnv32_BROKEN(s1), s1_res);\n  EXPECT_EQ(fnv32_BROKEN(s1), fnv32_buf_BROKEN(s1, strlen(s1)));\n\n  const char* s2 = \"monkeys! m0nk3yz! ev3ry \\\\/\\\\/here~~~~\";\n  const uint32_t s2_res = 1270448334UL;\n  EXPECT_EQ(fnv32_BROKEN(s2), s2_res);\n  EXPECT_EQ(fnv32_BROKEN(s2), fnv32_buf_BROKEN(s2, strlen(s2)));\n\n  const char* s3 = \"\";\n  const uint32_t s3_res = 2166136261UL;\n  EXPECT_EQ(fnv32_BROKEN(s3), s3_res);\n  EXPECT_EQ(fnv32_BROKEN(s3), fnv32_buf_BROKEN(s3, strlen(s3)));\n\n  const uint8_t s4_data[] = {0xFF, 0xFF, 0xFF, 0x00};\n  const char* s4 = reinterpret_cast<const char*>(s4_data);\n  const uint32_t s4_res = 2420936562UL;\n  EXPECT_EQ(fnv32_BROKEN(s4), s4_res);\n  EXPECT_EQ(fnv32_BROKEN(s4), fnv32_buf_BROKEN(s4, strlen(s4)));\n}\n\nTEST(FnvHash, Fnv64_BROKEN) {\n  const char* s1 = \"hello, world!\";\n  const uint64_t s1_res = 13991426986746681734ULL;\n  EXPECT_EQ(fnv64_BROKEN(s1), s1_res);\n  EXPECT_EQ(fnv64_BROKEN(s1), fnv64_buf_BROKEN(s1, strlen(s1)));\n\n  const char* s2 = \"monkeys! m0nk3yz! ev3ry \\\\/\\\\/here~~~~\";\n  const uint64_t s2_res = 6091394665637302478ULL;\n  EXPECT_EQ(fnv64_BROKEN(s2), s2_res);\n  EXPECT_EQ(fnv64_BROKEN(s2), fnv64_buf_BROKEN(s2, strlen(s2)));\n\n  const char* s3 = \"\";\n  const uint64_t s3_res = 14695981039346656037ULL;\n  EXPECT_EQ(fnv64_BROKEN(s3), s3_res);\n  EXPECT_EQ(fnv64_BROKEN(s3), fnv64_buf_BROKEN(s3, strlen(s3)));\n\n  const uint8_t s4_data[] = {0xFF, 0xFF, 0xFF, 0x00};\n  const char* s4 = reinterpret_cast<const char*>(s4_data);\n  const uint64_t s4_res = 2787597222566293202ULL;\n  EXPECT_EQ(fnv64_BROKEN(s4), s4_res);\n  EXPECT_EQ(fnv64_BROKEN(s4), fnv64_buf_BROKEN(s4, strlen(s4)));\n\n  // note: Use fnv64_buf to make a single hash value from multiple\n  // fields/datatypes.\n  const char* t4_a = \"E Pluribus\";\n  int64_t t4_b = 0xF1E2D3C4B5A69788;\n  int32_t t4_c = 0xAB12CD34;\n  const char* t4_d = \"Unum\";\n  uint64_t t4_res = 15571330457339273965ULL;\n  uint64_t t4_hash1 = fnv64_buf_BROKEN(t4_a, strlen(t4_a));\n  uint64_t t4_hash2 = fnv64_buf_BROKEN(\n      reinterpret_cast<void*>(&t4_b), sizeof(int64_t), t4_hash1);\n  uint64_t t4_hash3 = fnv64_buf_BROKEN(\n      reinterpret_cast<void*>(&t4_c), sizeof(int32_t), t4_hash2);\n  uint64_t t4_hash4 = fnv64_buf_BROKEN(t4_d, strlen(t4_d), t4_hash3);\n  EXPECT_EQ(t4_hash4, t4_res);\n  // note: These are probabalistic, not determinate, but c'mon.\n  // These hash values should be different, or something's not\n  // working.\n  EXPECT_NE(t4_hash1, t4_hash4);\n  EXPECT_NE(t4_hash2, t4_hash4);\n  EXPECT_NE(t4_hash3, t4_hash4);\n}\n\nTEST(FnvHash, Fnv32_FIXED) {\n  const char* s1 = \"hello, world!\";\n  const uint32_t s1_res = 3605494790U;\n  EXPECT_EQ(fnv32_FIXED(s1), s1_res);\n  EXPECT_EQ(fnv32_FIXED(s1), fnv32_buf_FIXED(s1, strlen(s1)));\n\n  const char* s2 = \"monkeys! m0nk3yz! ev3ry \\\\/\\\\/here~~~~\";\n  const uint32_t s2_res = 1270448334U;\n  EXPECT_EQ(fnv32_FIXED(s2), s2_res);\n  EXPECT_EQ(fnv32_FIXED(s2), fnv32_buf_FIXED(s2, strlen(s2)));\n\n  const char* s3 = \"\";\n  const uint32_t s3_res = 2166136261U;\n  EXPECT_EQ(fnv32_FIXED(s3), s3_res);\n  EXPECT_EQ(fnv32_FIXED(s3), fnv32_buf_FIXED(s3, strlen(s3)));\n\n  const uint8_t s4_data[] = {0xFF, 0xFF, 0xFF, 0x00};\n  const char* s4 = reinterpret_cast<const char*>(s4_data);\n  const uint32_t s4_res = 2978929266U;\n  EXPECT_EQ(fnv32_FIXED(s4), s4_res);\n  EXPECT_EQ(fnv32_FIXED(s4), fnv32_buf_FIXED(s4, strlen(s4)));\n}\n\nTEST(FnvHash, Fnv64_FIXED) {\n  const char* s1 = \"hello, world!\";\n  const uint64_t s1_res = 13991426986746681734ULL;\n  EXPECT_EQ(fnv64_FIXED(s1), s1_res);\n  EXPECT_EQ(fnv64_FIXED(s1), fnv64_buf_FIXED(s1, strlen(s1)));\n\n  const char* s2 = \"monkeys! m0nk3yz! ev3ry \\\\/\\\\/here~~~~\";\n  const uint64_t s2_res = 6091394665637302478ULL;\n  EXPECT_EQ(fnv64_FIXED(s2), s2_res);\n  EXPECT_EQ(fnv64_FIXED(s2), fnv64_buf_FIXED(s2, strlen(s2)));\n\n  const char* s3 = \"\";\n  const uint64_t s3_res = 14695981039346656037ULL;\n  EXPECT_EQ(fnv64_FIXED(s3), s3_res);\n  EXPECT_EQ(fnv64_FIXED(s3), fnv64_buf_FIXED(s3, strlen(s3)));\n\n  const uint8_t s4_data[] = {0xFF, 0xFF, 0xFF, 0x00};\n  const char* s4 = reinterpret_cast<const char*>(s4_data);\n  const uint64_t s4_res = 15475554797547429842ULL;\n  EXPECT_EQ(fnv64_FIXED(s4), s4_res);\n  EXPECT_EQ(fnv64_FIXED(s4), fnv64_buf_FIXED(s4, strlen(s4)));\n\n  // note: Use fnv64_buf to make a single hash value from multiple\n  // fields/datatypes.\n  const char* t4_a = \"E Pluribus\";\n  int64_t t4_b = 0xF1E2D3C4B5A69788;\n  int32_t t4_c = 0xAB12CD34;\n  const char* t4_d = \"Unum\";\n  uint64_t t4_res = 14526396152295369453ULL;\n  uint64_t t4_hash1 = fnv64_buf_FIXED(t4_a, strlen(t4_a));\n  uint64_t t4_hash2 = fnv64_buf_FIXED(\n      reinterpret_cast<void*>(&t4_b), sizeof(int64_t), t4_hash1);\n  uint64_t t4_hash3 = fnv64_buf_FIXED(\n      reinterpret_cast<void*>(&t4_c), sizeof(int32_t), t4_hash2);\n  uint64_t t4_hash4 = fnv64_buf_FIXED(t4_d, strlen(t4_d), t4_hash3);\n  EXPECT_EQ(t4_hash4, t4_res);\n  // note: These are probabalistic, not determinate, but c'mon.\n  // These hash values should be different, or something's not\n  // working.\n  EXPECT_NE(t4_hash1, t4_hash4);\n  EXPECT_NE(t4_hash2, t4_hash4);\n  EXPECT_NE(t4_hash3, t4_hash4);\n}\n"
  },
  {
    "path": "folly/hash/test/HashBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/Hash.h>\n#include <folly/hash/MurmurHash.h>\n#include <folly/hash/rapidhash.h>\n\n#include <stdint.h>\n\n#include <deque>\n#include <random>\n#include <string>\n#include <vector>\n\n#include <fmt/core.h>\n#include <glog/logging.h>\n\n#include <folly/Benchmark.h>\n#include <folly/Preprocessor.h>\n#include <folly/lang/Keep.h>\n#include <folly/portability/GFlags.h>\n\nextern \"C\" FOLLY_KEEP uint64_t\ncheck_folly_hash_128_to_64(uint64_t upper, uint64_t lower) {\n  return folly::hash::hash_128_to_64(upper, lower);\n}\n\nextern \"C\" FOLLY_KEEP uint64_t\ncheck_folly_commutative_hash_128_to_64(uint64_t upper, uint64_t lower) {\n  return folly::hash::commutative_hash_128_to_64(upper, lower);\n}\n\nextern \"C\" FOLLY_KEEP uint64_t check_folly_twang_mix64(uint64_t key) {\n  return folly::hash::twang_mix64(key);\n}\n\nextern \"C\" FOLLY_KEEP uint64_t check_folly_twang_unmix64(uint64_t key) {\n  return folly::hash::twang_unmix64(key);\n}\n\nextern \"C\" FOLLY_KEEP uint32_t check_folly_twang_32from64(uint64_t key) {\n  return folly::hash::twang_32from64(key);\n}\n\nextern \"C\" FOLLY_KEEP uint32_t check_folly_jenkins_rev_mix32(uint32_t key) {\n  return folly::hash::jenkins_rev_mix32(key);\n}\n\nextern \"C\" FOLLY_KEEP uint32_t check_folly_jenkins_rev_unmix32(uint32_t key) {\n  return folly::hash::jenkins_rev_unmix32(key);\n}\n\nextern \"C\" FOLLY_KEEP uint64_t\ncheck_folly_spooky_hash_v2_hash_32(void const* data, size_t size) {\n  return folly::hash::SpookyHashV2::Hash32(data, size, 0);\n}\n\nextern \"C\" FOLLY_KEEP uint64_t\ncheck_folly_spooky_hash_v2_hash_64(void const* data, size_t size) {\n  return folly::hash::SpookyHashV2::Hash64(data, size, 0);\n}\n\nnamespace detail {\n\nstd::vector<uint8_t> randomBytes(size_t n) {\n  std::vector<uint8_t> ret(n);\n  std::default_random_engine rng(1729); // Deterministic seed.\n  std::uniform_int_distribution<uint16_t> dist(0, 255);\n  std::generate(ret.begin(), ret.end(), [&]() { return dist(rng); });\n  return ret;\n}\n\nstd::vector<uint8_t> benchData = randomBytes(1 << 20); // 1MiB, fits in cache.\n\ntemplate <class Hasher>\nvoid bmHasher(Hasher hasher, size_t k, size_t iters) {\n  CHECK_LE(k, benchData.size());\n  for (size_t i = 0, pos = 0; i < iters; ++i, ++pos) {\n    if (pos == benchData.size() - k + 1) {\n      pos = 0;\n    }\n    folly::doNotOptimizeAway(hasher(benchData.data() + pos, k));\n  }\n}\n\ntemplate <class Hasher>\nvoid addHashBenchmark(const std::string& hasherName) {\n  for (size_t k = 1; k < 16; ++k) {\n    std::string name = fmt::format(\"{}: k={}\", hasherName, k);\n    folly::addBenchmark(__FILE__, name, [=](unsigned iters) {\n      Hasher hasher;\n      bmHasher(hasher, k, iters);\n      return iters;\n    });\n  }\n\n  for (size_t i = 0; i < 21; ++i) {\n    auto k = size_t(1) << i;\n    std::string name = fmt::format(\"{}: k=2^{}\", hasherName, i);\n    folly::addBenchmark(__FILE__, name, [=](unsigned iters) {\n      Hasher hasher;\n      bmHasher(hasher, k, iters);\n      return iters;\n    });\n  }\n\n  /* Draw line. */\n  folly::addBenchmark(__FILE__, \"-\", []() { return 0; });\n}\n\nstruct SpookyHashV2 {\n  uint64_t operator()(const uint8_t* data, size_t size) const {\n    return folly::hash::SpookyHashV2::Hash64(data, size, 0);\n  }\n};\n\nstruct FNV64 {\n  uint64_t operator()(const uint8_t* data, size_t size) const {\n    return folly::hash::fnv64_buf_BROKEN(data, size);\n  }\n};\n\nstruct MurmurHash {\n  uint64_t operator()(const uint8_t* data, size_t size) const {\n    return folly::hash::murmurHash64(\n        reinterpret_cast<const char*>(data), size, 0);\n  }\n};\n\nstruct RapidHash {\n  uint64_t operator()(const uint8_t* data, size_t size) const {\n    return folly::hash::rapidhash(reinterpret_cast<const char*>(data), size);\n  }\n};\n\nstruct RapidHashMicro {\n  uint64_t operator()(const uint8_t* data, size_t size) const {\n    return folly::hash::rapidhashMicro(\n        reinterpret_cast<const char*>(data), size);\n  }\n};\n\nstruct RapidHashNano {\n  uint64_t operator()(const uint8_t* data, size_t size) const {\n    return folly::hash::rapidhashNano(\n        reinterpret_cast<const char*>(data), size);\n  }\n};\n\n} // namespace detail\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  google::InitGoogleLogging(argv[0]);\n\n  std::deque<std::string> names; // Backing for benchmark names.\n\n#define BENCHMARK_HASH(HASHER) \\\n  detail::addHashBenchmark<detail::HASHER>(FOLLY_PP_STRINGIZE(HASHER));\n\n  BENCHMARK_HASH(SpookyHashV2);\n  BENCHMARK_HASH(FNV64);\n  BENCHMARK_HASH(MurmurHash);\n  BENCHMARK_HASH(RapidHash);\n  BENCHMARK_HASH(RapidHashMicro);\n  BENCHMARK_HASH(RapidHashNano);\n\n#undef BENCHMARK_HASH\n\n  folly::runBenchmarks();\n\n  return 0;\n}\n\n#if 0\nAMD EPYC 9000 series CPU\n$ hash_benchmark --bm_slice_usec=100000\n============================================================================\nfbcode/folly/hash/test/HashBenchmark.cpp     relative  time/iter   iters/s\n============================================================================\nSpookyHashV2: k=1                                           5.80ns   172.36M\nSpookyHashV2: k=2                                           6.18ns   161.74M\nSpookyHashV2: k=3                                           6.36ns   157.12M\nSpookyHashV2: k=4                                           5.68ns   175.90M\nSpookyHashV2: k=5                                           6.18ns   161.70M\nSpookyHashV2: k=6                                           6.38ns   156.71M\nSpookyHashV2: k=7                                           6.59ns   151.76M\nSpookyHashV2: k=8                                           5.70ns   175.40M\nSpookyHashV2: k=9                                           5.85ns   170.82M\nSpookyHashV2: k=10                                          6.23ns   160.51M\nSpookyHashV2: k=11                                          6.37ns   156.97M\nSpookyHashV2: k=12                                          5.87ns   170.32M\nSpookyHashV2: k=13                                          6.23ns   160.47M\nSpookyHashV2: k=14                                          6.36ns   157.19M\nSpookyHashV2: k=15                                          6.65ns   150.32M\nSpookyHashV2: k=2^0                                         5.80ns   172.43M\nSpookyHashV2: k=2^1                                         6.18ns   161.81M\nSpookyHashV2: k=2^2                                         5.68ns   175.97M\nSpookyHashV2: k=2^3                                         5.70ns   175.51M\nSpookyHashV2: k=2^4                                        11.98ns    83.48M\nSpookyHashV2: k=2^5                                        11.36ns    88.06M\nSpookyHashV2: k=2^6                                        18.85ns    53.06M\nSpookyHashV2: k=2^7                                        35.96ns    27.81M\nSpookyHashV2: k=2^8                                        48.41ns    20.66M\nSpookyHashV2: k=2^9                                        69.53ns    14.38M\nSpookyHashV2: k=2^10                                      104.62ns     9.56M\nSpookyHashV2: k=2^11                                      184.95ns     5.41M\nSpookyHashV2: k=2^12                                      330.11ns     3.03M\nSpookyHashV2: k=2^13                                      634.08ns     1.58M\nSpookyHashV2: k=2^14                                        1.23us   813.28K\nSpookyHashV2: k=2^15                                        2.44us   410.27K\nSpookyHashV2: k=2^16                                        4.83us   206.89K\nSpookyHashV2: k=2^17                                        9.63us   103.86K\nSpookyHashV2: k=2^18                                       19.21us    52.05K\nSpookyHashV2: k=2^19                                       38.45us    26.01K\nSpookyHashV2: k=2^20                                       77.18us    12.96K\n----------------------------------------------------------------------------\nFNV64: k=1                                                  1.12ns   893.79M\nFNV64: k=2                                                  1.64ns   608.67M\nFNV64: k=3                                                  2.22ns   450.32M\nFNV64: k=4                                                  2.66ns   376.63M\nFNV64: k=5                                                  3.22ns   310.52M\nFNV64: k=6                                                  3.91ns   255.89M\nFNV64: k=7                                                  4.75ns   210.39M\nFNV64: k=8                                                  4.75ns   210.61M\nFNV64: k=9                                                  5.62ns   177.82M\nFNV64: k=10                                                 6.02ns   166.23M\nFNV64: k=11                                                 6.86ns   145.80M\nFNV64: k=12                                                 9.04ns   110.62M\nFNV64: k=13                                                 9.97ns   100.32M\nFNV64: k=14                                                10.55ns    94.77M\nFNV64: k=15                                                11.19ns    89.34M\nFNV64: k=2^0                                                1.12ns   894.46M\nFNV64: k=2^1                                                1.64ns   608.43M\nFNV64: k=2^2                                                2.66ns   376.59M\nFNV64: k=2^3                                                4.75ns   210.71M\nFNV64: k=2^4                                               11.56ns    86.50M\nFNV64: k=2^5                                               22.44ns    44.56M\nFNV64: k=2^6                                               61.72ns    16.20M\nFNV64: k=2^7                                              149.54ns     6.69M\nFNV64: k=2^8                                              324.43ns     3.08M\nFNV64: k=2^9                                              675.13ns     1.48M\nFNV64: k=2^10                                               1.38us   726.87K\nFNV64: k=2^11                                               2.78us   359.99K\nFNV64: k=2^12                                               5.59us   179.04K\nFNV64: k=2^13                                              11.19us    89.34K\nFNV64: k=2^14                                              22.41us    44.62K\nFNV64: k=2^15                                              44.84us    22.30K\nFNV64: k=2^16                                              89.69us    11.15K\nFNV64: k=2^17                                             179.43us     5.57K\nFNV64: k=2^18                                             358.91us     2.79K\nFNV64: k=2^19                                             717.66us     1.39K\nFNV64: k=2^20                                               1.44ms    696.51\n----------------------------------------------------------------------------\nMurmurHash: k=1                                             1.60ns   623.43M\nMurmurHash: k=2                                             1.88ns   530.71M\nMurmurHash: k=3                                             1.96ns   509.79M\nMurmurHash: k=4                                             1.66ns   602.64M\nMurmurHash: k=5                                             2.05ns   486.80M\nMurmurHash: k=6                                             1.89ns   530.06M\nMurmurHash: k=7                                             2.22ns   450.01M\nMurmurHash: k=8                                             2.65ns   378.05M\nMurmurHash: k=9                                             3.28ns   304.85M\nMurmurHash: k=10                                            3.63ns   275.54M\nMurmurHash: k=11                                            3.56ns   280.71M\nMurmurHash: k=12                                            3.20ns   312.74M\nMurmurHash: k=13                                            3.55ns   281.31M\nMurmurHash: k=14                                            3.77ns   265.45M\nMurmurHash: k=15                                            3.98ns   251.25M\nMurmurHash: k=2^0                                           1.60ns   623.43M\nMurmurHash: k=2^1                                           1.88ns   530.67M\nMurmurHash: k=2^2                                           1.66ns   602.61M\nMurmurHash: k=2^3                                           2.64ns   378.10M\nMurmurHash: k=2^4                                           3.83ns   260.84M\nMurmurHash: k=2^5                                           6.04ns   165.59M\nMurmurHash: k=2^6                                          11.38ns    87.85M\nMurmurHash: k=2^7                                          19.87ns    50.33M\nMurmurHash: k=2^8                                          37.13ns    26.93M\nMurmurHash: k=2^9                                          78.16ns    12.79M\nMurmurHash: k=2^10                                        165.82ns     6.03M\nMurmurHash: k=2^11                                        340.98ns     2.93M\nMurmurHash: k=2^12                                        691.62ns     1.45M\nMurmurHash: k=2^13                                          1.39us   718.14K\nMurmurHash: k=2^14                                          2.80us   357.25K\nMurmurHash: k=2^15                                          5.61us   178.20K\nMurmurHash: k=2^16                                         11.22us    89.09K\nMurmurHash: k=2^17                                         22.44us    44.56K\nMurmurHash: k=2^18                                         44.87us    22.29K\nMurmurHash: k=2^19                                         89.77us    11.14K\nMurmurHash: k=2^20                                        179.86us     5.56K\n----------------------------------------------------------------------------\nRapidHash: k=1                                              3.43ns   291.75M\nRapidHash: k=2                                              3.43ns   291.76M\nRapidHash: k=3                                              3.43ns   291.76M\nRapidHash: k=4                                              3.08ns   324.23M\nRapidHash: k=5                                              3.08ns   324.21M\nRapidHash: k=6                                              3.09ns   324.13M\nRapidHash: k=7                                              3.09ns   323.89M\nRapidHash: k=8                                              3.09ns   323.80M\nRapidHash: k=9                                              3.09ns   323.82M\nRapidHash: k=10                                             3.09ns   323.81M\nRapidHash: k=11                                             3.09ns   324.05M\nRapidHash: k=12                                             3.09ns   324.14M\nRapidHash: k=13                                             3.09ns   324.05M\nRapidHash: k=14                                             3.09ns   324.07M\nRapidHash: k=15                                             3.09ns   324.08M\nRapidHash: k=2^0                                            3.43ns   291.77M\nRapidHash: k=2^1                                            3.43ns   291.83M\nRapidHash: k=2^2                                            3.08ns   324.24M\nRapidHash: k=2^3                                            3.09ns   323.97M\nRapidHash: k=2^4                                            3.09ns   324.06M\nRapidHash: k=2^5                                            4.80ns   208.47M\nRapidHash: k=2^6                                            5.55ns   180.23M\nRapidHash: k=2^7                                            8.65ns   115.62M\nRapidHash: k=2^8                                           15.47ns    64.63M\nRapidHash: k=2^9                                           27.17ns    36.80M\nRapidHash: k=2^10                                          50.30ns    19.88M\nRapidHash: k=2^11                                          96.09ns    10.41M\nRapidHash: k=2^12                                         188.00ns     5.32M\nRapidHash: k=2^13                                         355.37ns     2.81M\nRapidHash: k=2^14                                         709.39ns     1.41M\nRapidHash: k=2^15                                           1.42us   706.36K\nRapidHash: k=2^16                                           2.82us   354.93K\nRapidHash: k=2^17                                           5.64us   177.36K\nRapidHash: k=2^18                                          11.25us    88.90K\nRapidHash: k=2^19                                          22.50us    44.44K\nRapidHash: k=2^20                                          45.54us    21.96K\n----------------------------------------------------------------------------\nRapidHashMicro: k=1                                         2.74ns   364.96M\nRapidHashMicro: k=2                                         2.74ns   364.96M\nRapidHashMicro: k=3                                         2.74ns   364.92M\nRapidHashMicro: k=4                                         2.41ns   415.65M\nRapidHashMicro: k=5                                         2.41ns   415.68M\nRapidHashMicro: k=6                                         2.41ns   415.59M\nRapidHashMicro: k=7                                         2.41ns   415.59M\nRapidHashMicro: k=8                                         2.40ns   415.93M\nRapidHashMicro: k=9                                         2.40ns   415.93M\nRapidHashMicro: k=10                                        2.41ns   415.79M\nRapidHashMicro: k=11                                        2.41ns   415.79M\nRapidHashMicro: k=12                                        2.40ns   415.89M\nRapidHashMicro: k=13                                        2.40ns   415.93M\nRapidHashMicro: k=14                                        2.40ns   415.85M\nRapidHashMicro: k=15                                        2.40ns   415.90M\nRapidHashMicro: k=2^0                                       2.74ns   364.83M\nRapidHashMicro: k=2^1                                       2.74ns   365.38M\nRapidHashMicro: k=2^2                                       2.41ns   415.76M\nRapidHashMicro: k=2^3                                       2.40ns   416.03M\nRapidHashMicro: k=2^4                                       2.40ns   415.85M\nRapidHashMicro: k=2^5                                       3.77ns   264.93M\nRapidHashMicro: k=2^6                                       4.78ns   209.14M\nRapidHashMicro: k=2^7                                       8.66ns   115.44M\nRapidHashMicro: k=2^8                                      15.20ns    65.79M\nRapidHashMicro: k=2^9                                      25.15ns    39.77M\nRapidHashMicro: k=2^10                                     46.68ns    21.42M\nRapidHashMicro: k=2^11                                     88.21ns    11.34M\nRapidHashMicro: k=2^12                                    171.90ns     5.82M\nRapidHashMicro: k=2^13                                    338.56ns     2.95M\nRapidHashMicro: k=2^14                                    672.92ns     1.49M\nRapidHashMicro: k=2^15                                      1.34us   746.37K\nRapidHashMicro: k=2^16                                      2.68us   372.97K\nRapidHashMicro: k=2^17                                      5.34us   187.15K\nRapidHashMicro: k=2^18                                     10.67us    93.75K\nRapidHashMicro: k=2^19                                     21.37us    46.79K\nRapidHashMicro: k=2^20                                     43.10us    23.20K\n----------------------------------------------------------------------------\nRapidHashNano: k=1                                          2.74ns   364.98M\nRapidHashNano: k=2                                          2.74ns   364.94M\nRapidHashNano: k=3                                          2.74ns   364.92M\nRapidHashNano: k=4                                          2.41ns   415.68M\nRapidHashNano: k=5                                          2.41ns   415.78M\nRapidHashNano: k=6                                          2.41ns   415.67M\nRapidHashNano: k=7                                          2.41ns   415.69M\nRapidHashNano: k=8                                          2.40ns   415.98M\nRapidHashNano: k=9                                          2.40ns   415.99M\nRapidHashNano: k=10                                         2.40ns   415.94M\nRapidHashNano: k=11                                         2.40ns   415.93M\nRapidHashNano: k=12                                         2.40ns   415.91M\nRapidHashNano: k=13                                         2.40ns   415.91M\nRapidHashNano: k=14                                         2.40ns   415.86M\nRapidHashNano: k=15                                         2.40ns   415.97M\nRapidHashNano: k=2^0                                        2.74ns   365.00M\nRapidHashNano: k=2^1                                        2.74ns   364.93M\nRapidHashNano: k=2^2                                        2.41ns   415.60M\nRapidHashNano: k=2^3                                        2.40ns   416.09M\nRapidHashNano: k=2^4                                        2.40ns   415.87M\nRapidHashNano: k=2^5                                        3.44ns   290.83M\nRapidHashNano: k=2^6                                        5.05ns   198.00M\nRapidHashNano: k=2^7                                        8.36ns   119.63M\nRapidHashNano: k=2^8                                       14.69ns    68.09M\nRapidHashNano: k=2^9                                       25.86ns    38.67M\nRapidHashNano: k=2^10                                      49.95ns    20.02M\nRapidHashNano: k=2^11                                      97.24ns    10.28M\nRapidHashNano: k=2^12                                     189.93ns     5.27M\nRapidHashNano: k=2^13                                     375.24ns     2.66M\nRapidHashNano: k=2^14                                     744.88ns     1.34M\nRapidHashNano: k=2^15                                       1.52us   657.09K\nRapidHashNano: k=2^16                                       3.02us   331.24K\nRapidHashNano: k=2^17                                       5.99us   166.97K\nRapidHashNano: k=2^18                                      11.97us    83.55K\nRapidHashNano: k=2^19                                      24.84us    40.25K\nRapidHashNano: k=2^20                                      49.12us    20.36K\n----------------------------------------------------------------------------\n\nARM Neoverse-V2 CPU\n============================================================================\nfbcode/folly/hash/test/HashBenchmark.cpp     relative  time/iter   iters/s\n============================================================================\nSpookyHashV2: k=1                                           4.86ns   205.71M\nSpookyHashV2: k=2                                           4.97ns   201.06M\nSpookyHashV2: k=3                                           5.15ns   194.04M\nSpookyHashV2: k=4                                           4.83ns   207.19M\nSpookyHashV2: k=5                                           4.99ns   200.50M\nSpookyHashV2: k=6                                           5.13ns   194.97M\nSpookyHashV2: k=7                                           5.39ns   185.54M\nSpookyHashV2: k=8                                           4.81ns   207.87M\nSpookyHashV2: k=9                                           4.99ns   200.58M\nSpookyHashV2: k=10                                          5.15ns   194.27M\nSpookyHashV2: k=11                                          5.35ns   186.89M\nSpookyHashV2: k=12                                          4.97ns   201.36M\nSpookyHashV2: k=13                                          5.07ns   197.39M\nSpookyHashV2: k=14                                          5.26ns   190.25M\nSpookyHashV2: k=15                                          5.54ns   180.64M\nSpookyHashV2: k=2^0                                         4.84ns   206.49M\nSpookyHashV2: k=2^1                                         5.00ns   200.15M\nSpookyHashV2: k=2^2                                         4.81ns   207.98M\nSpookyHashV2: k=2^3                                         4.86ns   205.85M\nSpookyHashV2: k=2^4                                        10.02ns    99.80M\nSpookyHashV2: k=2^5                                        10.51ns    95.19M\nSpookyHashV2: k=2^6                                        16.95ns    59.01M\nSpookyHashV2: k=2^7                                        32.35ns    30.91M\nSpookyHashV2: k=2^8                                        36.11ns    27.69M\nSpookyHashV2: k=2^9                                        52.96ns    18.88M\nSpookyHashV2: k=2^10                                       80.89ns    12.36M\nSpookyHashV2: k=2^11                                      146.00ns     6.85M\nSpookyHashV2: k=2^12                                      267.59ns     3.74M\nSpookyHashV2: k=2^13                                      520.53ns     1.92M\nSpookyHashV2: k=2^14                                        1.02us   978.70K\nSpookyHashV2: k=2^15                                        2.03us   491.99K\nSpookyHashV2: k=2^16                                        4.03us   247.85K\nSpookyHashV2: k=2^17                                        8.07us   123.93K\nSpookyHashV2: k=2^18                                       15.99us    62.54K\nSpookyHashV2: k=2^19                                       31.84us    31.41K\nSpookyHashV2: k=2^20                                       64.26us    15.56K\n----------------------------------------------------------------------------\nFNV64: k=1                                                900.40ps     1.11G\nFNV64: k=2                                                  1.22ns   816.61M\nFNV64: k=3                                                  1.59ns   629.80M\nFNV64: k=4                                                  1.97ns   507.80M\nFNV64: k=5                                                  2.37ns   422.18M\nFNV64: k=6                                                  2.79ns   358.69M\nFNV64: k=7                                                  3.18ns   314.06M\nFNV64: k=8                                                  3.63ns   275.29M\nFNV64: k=9                                                  4.07ns   245.44M\nFNV64: k=10                                                 4.55ns   219.75M\nFNV64: k=11                                                 5.06ns   197.64M\nFNV64: k=12                                                 5.57ns   179.52M\nFNV64: k=13                                                 6.09ns   164.21M\nFNV64: k=14                                                 6.56ns   152.46M\nFNV64: k=15                                                 7.08ns   141.23M\nFNV64: k=2^0                                              897.97ps     1.11G\nFNV64: k=2^1                                                1.24ns   807.58M\nFNV64: k=2^2                                                1.94ns   514.85M\nFNV64: k=2^3                                                3.59ns   278.28M\nFNV64: k=2^4                                                7.75ns   128.98M\nFNV64: k=2^5                                               17.59ns    56.86M\nFNV64: k=2^6                                               40.59ns    24.64M\nFNV64: k=2^7                                               98.97ns    10.10M\nFNV64: k=2^8                                              218.46ns     4.58M\nFNV64: k=2^9                                              458.28ns     2.18M\nFNV64: k=2^10                                             933.57ns     1.07M\nFNV64: k=2^11                                               1.90us   526.35K\nFNV64: k=2^12                                               3.80us   263.17K\nFNV64: k=2^13                                               7.62us   131.16K\nFNV64: k=2^14                                              15.32us    65.27K\nFNV64: k=2^15                                              30.47us    32.82K\nFNV64: k=2^16                                              61.54us    16.25K\nFNV64: k=2^17                                             122.80us     8.14K\nFNV64: k=2^18                                             244.89us     4.08K\nFNV64: k=2^19                                             490.45us     2.04K\nFNV64: k=2^20                                             974.42us     1.03K\n----------------------------------------------------------------------------\nMurmurHash: k=1                                           800.93ps     1.25G\nMurmurHash: k=2                                           839.94ps     1.19G\nMurmurHash: k=3                                           943.71ps     1.06G\nMurmurHash: k=4                                           940.77ps     1.06G\nMurmurHash: k=5                                           934.52ps     1.07G\nMurmurHash: k=6                                           930.07ps     1.08G\nMurmurHash: k=7                                             1.06ns   945.85M\nMurmurHash: k=8                                             1.36ns   736.43M\nMurmurHash: k=9                                             1.69ns   591.59M\nMurmurHash: k=10                                            1.94ns   516.57M\nMurmurHash: k=11                                            2.11ns   475.03M\nMurmurHash: k=12                                            1.74ns   574.93M\nMurmurHash: k=13                                            1.82ns   548.98M\nMurmurHash: k=14                                            2.09ns   479.18M\nMurmurHash: k=15                                            2.17ns   460.00M\nMurmurHash: k=2^0                                         797.67ps     1.25G\nMurmurHash: k=2^1                                         847.22ps     1.18G\nMurmurHash: k=2^2                                         933.75ps     1.07G\nMurmurHash: k=2^3                                           1.35ns   738.51M\nMurmurHash: k=2^4                                           2.02ns   495.93M\nMurmurHash: k=2^5                                           3.26ns   306.59M\nMurmurHash: k=2^6                                           6.00ns   166.56M\nMurmurHash: k=2^7                                          11.28ns    88.63M\nMurmurHash: k=2^8                                          22.85ns    43.76M\nMurmurHash: k=2^9                                          48.04ns    20.82M\nMurmurHash: k=2^10                                        109.83ns     9.11M\nMurmurHash: k=2^11                                        230.43ns     4.34M\nMurmurHash: k=2^12                                        466.45ns     2.14M\nMurmurHash: k=2^13                                        947.20ns     1.06M\nMurmurHash: k=2^14                                          1.90us   525.52K\nMurmurHash: k=2^15                                          3.84us   260.44K\nMurmurHash: k=2^16                                          7.68us   130.25K\nMurmurHash: k=2^17                                         15.28us    65.44K\nMurmurHash: k=2^18                                         30.80us    32.47K\nMurmurHash: k=2^19                                         60.93us    16.41K\nMurmurHash: k=2^20                                        123.62us     8.09K\n----------------------------------------------------------------------------\nRapidHash: k=1                                              1.83ns   547.33M\nRapidHash: k=2                                              1.83ns   545.42M\nRapidHash: k=3                                              1.83ns   547.41M\nRapidHash: k=4                                              1.74ns   576.05M\nRapidHash: k=5                                              1.75ns   572.67M\nRapidHash: k=6                                              1.75ns   571.89M\nRapidHash: k=7                                              1.74ns   574.81M\nRapidHash: k=8                                              1.75ns   572.97M\nRapidHash: k=9                                              1.75ns   569.93M\nRapidHash: k=10                                             1.75ns   571.37M\nRapidHash: k=11                                             1.75ns   572.62M\nRapidHash: k=12                                             1.75ns   570.81M\nRapidHash: k=13                                             1.74ns   575.19M\nRapidHash: k=14                                             1.75ns   571.86M\nRapidHash: k=15                                             1.76ns   569.45M\nRapidHash: k=2^0                                            1.83ns   546.84M\nRapidHash: k=2^1                                            1.84ns   542.67M\nRapidHash: k=2^2                                            1.75ns   571.55M\nRapidHash: k=2^3                                            1.75ns   570.59M\nRapidHash: k=2^4                                            1.74ns   573.73M\nRapidHash: k=2^5                                            6.06ns   164.97M\nRapidHash: k=2^6                                            7.32ns   136.53M\nRapidHash: k=2^7                                            8.56ns   116.83M\nRapidHash: k=2^8                                           11.95ns    83.67M\nRapidHash: k=2^9                                           21.28ns    47.00M\nRapidHash: k=2^10                                          32.94ns    30.36M\nRapidHash: k=2^11                                          60.44ns    16.55M\nRapidHash: k=2^12                                         118.04ns     8.47M\nRapidHash: k=2^13                                         228.19ns     4.38M\nRapidHash: k=2^14                                         450.62ns     2.22M\nRapidHash: k=2^15                                         892.95ns     1.12M\nRapidHash: k=2^16                                           1.78us   563.35K\nRapidHash: k=2^17                                           3.64us   274.81K\nRapidHash: k=2^18                                           7.28us   137.45K\nRapidHash: k=2^19                                          14.72us    67.94K\nRapidHash: k=2^20                                          29.68us    33.69K\n----------------------------------------------------------------------------\nRapidHashMicro: k=1                                         1.81ns   553.77M\nRapidHashMicro: k=2                                         1.80ns   555.24M\nRapidHashMicro: k=3                                         1.80ns   554.39M\nRapidHashMicro: k=4                                         1.75ns   572.25M\nRapidHashMicro: k=5                                         1.74ns   574.47M\nRapidHashMicro: k=6                                         1.75ns   571.09M\nRapidHashMicro: k=7                                         1.73ns   577.13M\nRapidHashMicro: k=8                                         1.74ns   575.04M\nRapidHashMicro: k=9                                         1.73ns   577.82M\nRapidHashMicro: k=10                                        1.74ns   574.97M\nRapidHashMicro: k=11                                        1.73ns   577.59M\nRapidHashMicro: k=12                                        1.73ns   579.19M\nRapidHashMicro: k=13                                        1.72ns   580.85M\nRapidHashMicro: k=14                                        1.75ns   570.49M\nRapidHashMicro: k=15                                        1.74ns   575.08M\nRapidHashMicro: k=2^0                                       1.81ns   551.19M\nRapidHashMicro: k=2^1                                       1.81ns   551.41M\nRapidHashMicro: k=2^2                                       1.73ns   578.13M\nRapidHashMicro: k=2^3                                       1.72ns   582.30M\nRapidHashMicro: k=2^4                                       1.74ns   574.35M\nRapidHashMicro: k=2^5                                       2.80ns   357.71M\nRapidHashMicro: k=2^6                                       4.01ns   249.14M\nRapidHashMicro: k=2^7                                       8.09ns   123.54M\nRapidHashMicro: k=2^8                                      11.92ns    83.86M\nRapidHashMicro: k=2^9                                      19.72ns    50.70M\nRapidHashMicro: k=2^10                                     36.74ns    27.22M\nRapidHashMicro: k=2^11                                     66.32ns    15.08M\nRapidHashMicro: k=2^12                                    127.50ns     7.84M\nRapidHashMicro: k=2^13                                    250.60ns     3.99M\nRapidHashMicro: k=2^14                                    499.14ns     2.00M\nRapidHashMicro: k=2^15                                    992.53ns     1.01M\nRapidHashMicro: k=2^16                                      1.98us   504.40K\nRapidHashMicro: k=2^17                                      4.01us   249.55K\nRapidHashMicro: k=2^18                                      8.02us   124.75K\nRapidHashMicro: k=2^19                                     16.20us    61.74K\nRapidHashMicro: k=2^20                                     32.38us    30.88K\n----------------------------------------------------------------------------\nRapidHashNano: k=1                                          1.83ns   547.40M\nRapidHashNano: k=2                                          1.83ns   547.34M\nRapidHashNano: k=3                                          1.83ns   547.45M\nRapidHashNano: k=4                                          1.73ns   578.94M\nRapidHashNano: k=5                                          1.73ns   577.38M\nRapidHashNano: k=6                                          1.73ns   578.66M\nRapidHashNano: k=7                                          1.72ns   579.98M\nRapidHashNano: k=8                                          1.75ns   570.16M\nRapidHashNano: k=9                                          1.73ns   577.08M\nRapidHashNano: k=10                                         1.74ns   575.20M\nRapidHashNano: k=11                                         1.73ns   577.25M\nRapidHashNano: k=12                                         1.73ns   577.37M\nRapidHashNano: k=13                                         1.75ns   573.06M\nRapidHashNano: k=14                                         1.73ns   578.76M\nRapidHashNano: k=15                                         1.73ns   579.37M\nRapidHashNano: k=2^0                                        1.83ns   546.93M\nRapidHashNano: k=2^1                                        1.82ns   550.47M\nRapidHashNano: k=2^2                                        1.71ns   584.00M\nRapidHashNano: k=2^3                                        1.73ns   577.64M\nRapidHashNano: k=2^4                                        1.74ns   576.14M\nRapidHashNano: k=2^5                                        2.76ns   362.17M\nRapidHashNano: k=2^6                                        4.68ns   213.54M\nRapidHashNano: k=2^7                                        6.92ns   144.61M\nRapidHashNano: k=2^8                                       11.37ns    87.97M\nRapidHashNano: k=2^9                                       20.72ns    48.27M\nRapidHashNano: k=2^10                                      39.52ns    25.30M\nRapidHashNano: k=2^11                                      78.77ns    12.70M\nRapidHashNano: k=2^12                                     154.82ns     6.46M\nRapidHashNano: k=2^13                                     308.12ns     3.25M\nRapidHashNano: k=2^14                                     617.76ns     1.62M\nRapidHashNano: k=2^15                                       1.23us   812.54K\nRapidHashNano: k=2^16                                       2.46us   406.14K\nRapidHashNano: k=2^17                                       4.91us   203.54K\nRapidHashNano: k=2^18                                       9.82us   101.82K\nRapidHashNano: k=2^19                                      19.82us    50.46K\nRapidHashNano: k=2^20                                      39.76us    25.15K\n----------------------------------------------------------------------------\n\n#endif\n"
  },
  {
    "path": "folly/hash/test/HashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/Hash.h>\n\n#include <stdint.h>\n\n#include <random>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include <folly/Conv.h>\n#include <folly/MapUtil.h>\n#include <folly/Random.h>\n#include <folly/Range.h>\n#include <folly/lang/cstring_view.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly::hash;\n\nTEST(Hash, Test128To64) {\n  constexpr uint64_t upper = 12345678910111213ULL;\n  constexpr uint64_t lower = 141516171819202122ULL;\n  EXPECT_NE(hash_128_to_64(upper, lower), hash_128_to_64(lower, upper));\n  EXPECT_EQ(\n      commutative_hash_128_to_64(upper, lower),\n      commutative_hash_128_to_64(lower, upper));\n}\n\nTEST(Hash, TWangMix64) {\n  uint64_t i1 = 0x78a87873e2d31dafULL;\n  uint64_t i1_res = 3389151152926383528ULL;\n  EXPECT_EQ(i1_res, twang_mix64(i1));\n  EXPECT_EQ(i1, twang_unmix64(i1_res));\n\n  uint64_t i2 = 0x0123456789abcdefULL;\n  uint64_t i2_res = 3061460455458984563ull;\n  EXPECT_EQ(i2_res, twang_mix64(i2));\n  EXPECT_EQ(i2, twang_unmix64(i2_res));\n}\n\nnamespace {\nvoid checkTWang(uint64_t r) {\n  uint64_t result = twang_mix64(r);\n  EXPECT_EQ(r, twang_unmix64(result));\n}\n} // namespace\n\nTEST(Hash, TWangUnmix64) {\n  // We'll try (1 << i), (1 << i) + 1, (1 << i) - 1\n  for (int i = 1; i < 64; i++) {\n    checkTWang((uint64_t(1) << i) - 1);\n    checkTWang(uint64_t(1) << i);\n    checkTWang((uint64_t(1) << i) + 1);\n  }\n}\n\nTEST(Hash, TWang32From64) {\n  uint64_t i1 = 0x78a87873e2d31dafULL;\n  uint32_t i1_res = 1525586863ul;\n  EXPECT_EQ(twang_32from64(i1), i1_res);\n\n  uint64_t i2 = 0x0123456789abcdefULL;\n  uint32_t i2_res = 2918899159ul;\n  EXPECT_EQ(twang_32from64(i2), i2_res);\n}\n\nTEST(Hash, JenkinsRevMix32) {\n  uint32_t i1 = 3805486511ul;\n  uint32_t i1_res = 381808021ul;\n  EXPECT_EQ(i1_res, jenkins_rev_mix32(i1));\n  EXPECT_EQ(i1, jenkins_rev_unmix32(i1_res));\n\n  uint32_t i2 = 2309737967ul;\n  uint32_t i2_res = 1834777923ul;\n  EXPECT_EQ(i2_res, jenkins_rev_mix32(i2));\n  EXPECT_EQ(i2, jenkins_rev_unmix32(i2_res));\n}\n\nnamespace {\nvoid checkJenkins(uint32_t r) {\n  uint32_t result = jenkins_rev_mix32(r);\n  EXPECT_EQ(r, jenkins_rev_unmix32(result));\n}\n} // namespace\n\nTEST(Hash, JenkinsRevUnmix32) {\n  // We'll try (1 << i), (1 << i) + 1, (1 << i) - 1\n  for (int i = 1; i < 32; i++) {\n    checkJenkins((1U << i) - 1);\n    checkJenkins(1U << i);\n    checkJenkins((1U << i) + 1);\n  }\n}\n\nTEST(Hash, hasher) {\n  // Basically just confirms that things compile ok.\n  std::unordered_map<int32_t, int32_t, folly::hasher<int32_t>> m;\n  m.insert(std::make_pair(4, 5));\n  EXPECT_EQ(get_default(m, 4), 5);\n}\n\nTEST(Hash, integralTypes) {\n  // Basically just confirms that things compile ok.\n  std::unordered_set<size_t> hashes;\n  folly::Hash hasher;\n  hashes.insert(hasher((char)1));\n  hashes.insert(hasher((signed char)2));\n  hashes.insert(hasher((unsigned char)3));\n  hashes.insert(hasher((short)4));\n  hashes.insert(hasher((signed short)5));\n  hashes.insert(hasher((unsigned short)6));\n  hashes.insert(hasher((int)7));\n  hashes.insert(hasher((signed int)8));\n  hashes.insert(hasher((unsigned int)9));\n  hashes.insert(hasher((long)10));\n  hashes.insert(hasher((signed long)11));\n  hashes.insert(hasher((unsigned long)12));\n  hashes.insert(hasher((long long)13));\n  hashes.insert(hasher((signed long long)14));\n  hashes.insert(hasher((unsigned long long)15));\n  hashes.insert(hasher((int8_t)16));\n  hashes.insert(hasher((uint8_t)17));\n  hashes.insert(hasher((int16_t)18));\n  hashes.insert(hasher((uint16_t)19));\n  hashes.insert(hasher((int32_t)20));\n  hashes.insert(hasher((uint32_t)21));\n  hashes.insert(hasher((int64_t)22));\n  hashes.insert(hasher((uint64_t)23));\n  hashes.insert(hasher((size_t)24));\n\n  size_t setSize = 24;\n#if FOLLY_HAVE_INT128_T\n  hashes.insert(hasher((__int128_t)25));\n  hashes.insert(hasher((__uint128_t)26));\n  setSize += 2;\n#endif\n  EXPECT_EQ(setSize, hashes.size());\n}\n\nnamespace {\nenum class TestEnum {\n  MIN = 0,\n  ITEM = 1,\n  MAX = 2,\n};\n\nenum class TestBigEnum : uint64_t {\n  ITEM = 1,\n};\n\nstruct TestStruct {};\n} // namespace\n\nnamespace std {\ntemplate <>\nstruct hash<TestEnum> {\n  std::size_t operator()(TestEnum const& e) const noexcept {\n    return hash<int>()(static_cast<int>(e));\n  }\n};\n\ntemplate <>\nstruct hash<TestStruct> {\n  [[maybe_unused]] std::size_t operator()(TestStruct const&) const noexcept {\n    return 0;\n  }\n};\n} // namespace std\n\nnamespace {\nthread_local size_t allocatedMemorySize{0};\n\ntemplate <class T>\nclass TestAlloc {\n public:\n  using Alloc = std::allocator<T>;\n  using AllocTraits = std::allocator_traits<Alloc>;\n  using value_type = typename AllocTraits::value_type;\n\n  using pointer = typename AllocTraits::pointer;\n  using const_pointer = typename AllocTraits::const_pointer;\n  using reference = value_type&;\n  using const_reference = value_type const&;\n  using size_type = typename AllocTraits::size_type;\n\n  using propagate_on_container_swap = std::true_type;\n  using propagate_on_container_copy_assignment = std::true_type;\n  using propagate_on_container_move_assignment = std::true_type;\n\n  TestAlloc() {}\n\n  template <class T2>\n  TestAlloc(TestAlloc<T2> const& other) noexcept : a_(other.a_) {}\n\n  template <class T2>\n  TestAlloc& operator=(TestAlloc<T2> const& other) noexcept {\n    a_ = other.a_;\n    return *this;\n  }\n\n  template <class T2>\n  TestAlloc(TestAlloc<T2>&& other) noexcept : a_(std::move(other.a_)) {}\n\n  template <class T2>\n  TestAlloc& operator=(TestAlloc<T2>&& other) noexcept {\n    a_ = std::move(other.a_);\n    return *this;\n  }\n\n  static size_t getAllocatedMemorySize() { return allocatedMemorySize; }\n\n  static void resetTracking() { allocatedMemorySize = 0; }\n\n  T* allocate(size_t n) {\n    allocatedMemorySize += n * sizeof(T);\n    return a_.allocate(n);\n  }\n  void deallocate(T* p, size_t n) {\n    allocatedMemorySize -= n * sizeof(T);\n    a_.deallocate(p, n);\n  }\n\n private:\n  std::allocator<T> a_;\n\n  template <class U>\n  friend class TestAlloc;\n};\n\ntemplate <class T1, class T2>\nbool operator==(TestAlloc<T1> const&, TestAlloc<T2> const&) {\n  return true;\n}\n\ntemplate <class T1, class T2>\nbool operator!=(TestAlloc<T1> const&, TestAlloc<T2> const&) {\n  return false;\n}\n\ntemplate <class M, class A>\nstd::vector<size_t> getStats(size_t iter) {\n  std::vector<size_t> ret;\n  ret.reserve(iter);\n  A::resetTracking();\n  M m;\n  ret.push_back(A::getAllocatedMemorySize());\n  for (size_t i = 1; i < iter; ++i) {\n    m.insert(\n        std::make_pair(\n            folly::to<typename M::key_type>(i), typename M::mapped_type{}));\n    ret.push_back(A::getAllocatedMemorySize());\n  }\n  return ret;\n}\n\ntemplate <typename K, typename V, typename H>\nvoid testNoCachedHashCode() {\n  using A = TestAlloc<std::pair<const K, V>>;\n  using M = std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, A>;\n  using MActual = std::unordered_map<K, V, H, std::equal_to<K>, A>;\n  constexpr int kIter = 10;\n  auto expected = getStats<M, A>(kIter);\n  auto actual = getStats<MActual, A>(kIter);\n  ASSERT_EQ(expected.size(), actual.size());\n  for (size_t i = 0; i < expected.size(); ++i) {\n    ASSERT_EQ(expected[i], actual[i]);\n  }\n}\n} // namespace\n\nTEST(Hash, noCachedHashCode) {\n  testNoCachedHashCode<bool, char, folly::hasher<bool>>();\n  testNoCachedHashCode<int, char, folly::hasher<int>>();\n  testNoCachedHashCode<double, char, folly::hasher<double>>();\n  testNoCachedHashCode<TestEnum, char, folly::hasher<TestEnum>>();\n\n  testNoCachedHashCode<bool, std::string, folly::Hash>();\n  testNoCachedHashCode<int, std::string, folly::Hash>();\n  testNoCachedHashCode<double, std::string, folly::Hash>();\n  testNoCachedHashCode<TestEnum, std::string, folly::Hash>();\n}\n\nTEST(Hash, integerConversion) {\n  folly::hasher<uint64_t> h;\n  uint64_t k = 10;\n  EXPECT_EQ(h(k), h(10));\n}\n\n#if FOLLY_HAVE_INT128_T\nTEST(Hash, int128StdHash) {\n  std::unordered_set<__int128> hs;\n  hs.insert(__int128_t{1});\n  hs.insert(__int128_t{2});\n  EXPECT_EQ(2, hs.size());\n\n  std::set<unsigned __int128> s;\n  s.insert(static_cast<unsigned __int128>(1));\n  s.insert(static_cast<unsigned __int128>(2));\n  EXPECT_EQ(2, s.size());\n}\n#endif\n\nTEST(Hash, floatTypes) {\n  folly::Hash hasher;\n\n  EXPECT_EQ(hasher(0.0f), hasher(-0.0f));\n  EXPECT_EQ(hasher(0.0), hasher(-0.0));\n\n  // Basically just confirms that things compile ok.\n  std::unordered_set<size_t> hashes;\n  hashes.insert(hasher(0.0f));\n  hashes.insert(hasher(0.1f));\n  hashes.insert(hasher(0.2));\n  hashes.insert(hasher(0.2f));\n  hashes.insert(hasher(-0.3));\n  hashes.insert(hasher(-0.3f));\n\n  EXPECT_EQ(6, hashes.size());\n}\n\n// Not a full hasher since only handles one type\nclass TestHasher {\n public:\n  size_t operator()(const std::pair<int, int>& p) const {\n    return p.first + p.second;\n  }\n};\n\ntemplate <typename T, typename... Ts>\nsize_t hash_combine_test(const T& t, const Ts&... ts) {\n  return hash_combine_generic(TestHasher{}, t, ts...);\n}\n\nTEST(Hash, pair) {\n  auto a = std::make_pair(1, 2);\n  auto b = std::make_pair(3, 4);\n  auto c = std::make_pair(1, 2);\n  auto d = std::make_pair(2, 1);\n  EXPECT_EQ(hash_combine(a), hash_combine(c));\n  EXPECT_NE(hash_combine(b), hash_combine(c));\n  EXPECT_NE(hash_combine(d), hash_combine(c));\n\n  // With composition\n  EXPECT_EQ(hash_combine(a, b), hash_combine(c, b));\n  // Test order dependence\n  EXPECT_NE(hash_combine(a, b), hash_combine(b, a));\n\n  // Test with custom hasher\n  EXPECT_EQ(hash_combine_test(a), hash_combine_test(c));\n  // 3 + 4 != 1 + 2\n  EXPECT_NE(hash_combine_test(b), hash_combine_test(c));\n  // This time, thanks to a terrible hash function, these are equal\n  EXPECT_EQ(hash_combine_test(d), hash_combine_test(c));\n  // With composition\n  EXPECT_EQ(hash_combine_test(a, b), hash_combine_test(c, b));\n  // Test order dependence\n  EXPECT_NE(hash_combine_test(a, b), hash_combine_test(b, a));\n  // Again, 1 + 2 == 2 + 1\n  EXPECT_EQ(hash_combine_test(a, b), hash_combine_test(d, b));\n}\n\nTEST(Hash, hashCombine) {\n  EXPECT_TRUE(noexcept(hash_combine(1, 2)));\n  EXPECT_NE(hash_combine(1, 2), hash_combine(2, 1));\n}\n\nTEST(Hash, hashBool) {\n  const auto hash = folly::Hash();\n  EXPECT_NE(hash(true), hash(false));\n}\n\nTEST(Hash, hashBool10) {\n  const auto hash = folly::Hash();\n  std::set<size_t> values;\n  for (bool b1 : {false, true}) {\n    for (bool b2 : {false, true}) {\n      for (bool b3 : {false, true}) {\n        for (bool b4 : {false, true}) {\n          for (bool b5 : {false, true}) {\n            for (bool b6 : {false, true}) {\n              for (bool b7 : {false, true}) {\n                for (bool b8 : {false, true}) {\n                  for (bool b9 : {false, true}) {\n                    for (bool b10 : {false, true}) {\n                      values.insert(\n                          hash(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10));\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n  EXPECT_EQ(values.size(), 1 << 10);\n}\n\nTEST(Hash, stdTuple) {\n  using tuple3 = std::tuple<int64_t, std::string, int32_t>;\n  tuple3 t(42, \"foo\", 1);\n\n  std::unordered_map<tuple3, std::string> m;\n  m[t] = \"bar\";\n  EXPECT_EQ(\"bar\", m[t]);\n}\n\nTEST(Hash, stdEmptyTuple) {\n  std::unordered_map<std::tuple<>, std::string, folly::Hash> m;\n  m[{}] = \"foo\";\n  EXPECT_EQ(m[{}], \"foo\");\n\n  folly::hasher<std::tuple<>> h;\n  EXPECT_EQ(h({}), 0);\n}\n\nTEST(Hash, enumType) {\n  const auto hash = folly::Hash();\n\n  enum class Enum32 : int32_t { Foo, Bar };\n  EXPECT_EQ(hash(static_cast<int32_t>(Enum32::Foo)), hash(Enum32::Foo));\n  EXPECT_EQ(hash(static_cast<int32_t>(Enum32::Bar)), hash(Enum32::Bar));\n  EXPECT_NE(hash(Enum32::Foo), hash(Enum32::Bar));\n\n  std::unordered_map<Enum32, std::string, folly::Hash> m32;\n  m32[Enum32::Foo] = \"foo\";\n  EXPECT_EQ(\"foo\", m32[Enum32::Foo]);\n\n  enum class Enum64 : int64_t { Foo, Bar };\n  EXPECT_EQ(hash(static_cast<int64_t>(Enum64::Foo)), hash(Enum64::Foo));\n  EXPECT_EQ(hash(static_cast<int64_t>(Enum64::Bar)), hash(Enum64::Bar));\n  EXPECT_NE(hash(Enum64::Foo), hash(Enum64::Bar));\n\n  std::unordered_map<Enum64, std::string, folly::Hash> m64;\n  m64[Enum64::Foo] = \"foo\";\n  EXPECT_EQ(\"foo\", m64[Enum64::Foo]);\n}\n\nTEST(Hash, pairFollyHash) {\n  using pair2 = std::pair<int64_t, int32_t>;\n  pair2 p(42, 1);\n\n  std::unordered_map<pair2, std::string, folly::Hash> m;\n  m[p] = \"bar\";\n  EXPECT_EQ(\"bar\", m[p]);\n}\n\nTEST(Hash, tupleFollyHash) {\n  using tuple3 = std::tuple<int64_t, int32_t, int32_t>;\n  tuple3 t(42, 1, 1);\n\n  std::unordered_map<tuple3, std::string, folly::Hash> m;\n  m[t] = \"bar\";\n  EXPECT_EQ(\"bar\", m[t]);\n}\n\nnamespace {\ntemplate <class T>\nsize_t hash_vector(const std::vector<T>& v) {\n  return hash_range(v.begin(), v.end());\n}\n} // namespace\n\nTEST(Hash, hashRange) {\n  EXPECT_EQ(hash_vector<int32_t>({1, 2}), hash_vector<int16_t>({1, 2}));\n  EXPECT_NE(hash_vector<int>({2, 1}), hash_vector<int>({1, 2}));\n  EXPECT_EQ(hash_vector<int>({}), hash_vector<float>({}));\n}\n\nTEST(Hash, commutativeHashCombine) {\n  EXPECT_EQ(\n      commutative_hash_combine_value_generic(\n          folly::Hash{}(12345ul), folly::Hash{}, 6789ul),\n      commutative_hash_combine_value_generic(\n          folly::Hash{}(6789ul), folly::Hash{}, 12345ul));\n\n  std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};\n  std::random_device rd;\n  std::mt19937 g(rd());\n  auto h = commutative_hash_combine_range(v.begin(), v.end());\n  for (int i = 0; i < 100; i++) {\n    std::shuffle(v.begin(), v.end(), g);\n    EXPECT_EQ(h, commutative_hash_combine_range(v.begin(), v.end()));\n  }\n  EXPECT_NE(\n      h,\n      commutative_hash_combine_range_generic(\n          /* seed = */ 0xdeadbeef, folly::Hash{}, v.begin(), v.end()));\n  EXPECT_NE(\n      h, commutative_hash_combine_range(v.begin(), v.begin() + (v.size() - 1)));\n\n  EXPECT_EQ(h, commutative_hash_combine(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));\n  EXPECT_EQ(h, commutative_hash_combine(10, 2, 3, 4, 5, 6, 7, 8, 9, 1));\n\n  EXPECT_EQ(\n      commutative_hash_combine(12345, 6789),\n      commutative_hash_combine(6789, 12345));\n}\n\nTEST(Hash, stdTupleDifferentHash) {\n  using tuple3 = std::tuple<int64_t, std::string, int32_t>;\n  tuple3 t1(42, \"foo\", 1);\n  tuple3 t2(9, \"bar\", 3);\n  tuple3 t3(42, \"foo\", 3);\n\n  EXPECT_NE(std::hash<tuple3>()(t1), std::hash<tuple3>()(t2));\n  EXPECT_NE(std::hash<tuple3>()(t1), std::hash<tuple3>()(t3));\n}\n\nTEST(Hash, Strings) {\n  using namespace folly;\n\n  StringPiece a1 = \"10050517\", b1 = \"51107032\", a2 = \"10050518\",\n              b2 = \"51107033\", a3 = \"10050519\", b3 = \"51107034\",\n              a4 = \"10050525\", b4 = \"51107040\";\n  Range<const wchar_t*> w1 = range(L\"10050517\"), w2 = range(L\"51107032\"),\n                        w3 = range(L\"10050518\"), w4 = range(L\"51107033\");\n  Hash h2;\n  EXPECT_NE(h2(a1), h2(b1));\n  EXPECT_NE(h2(a1), h2(b1));\n  EXPECT_NE(h2(a2), h2(b2));\n  EXPECT_NE(h2(a3), h2(b3));\n  EXPECT_NE(h2(ByteRange(a1)), h2(ByteRange(b1)));\n  EXPECT_NE(h2(ByteRange(a2)), h2(ByteRange(b2)));\n  EXPECT_NE(h2(ByteRange(a3)), h2(ByteRange(b3)));\n  EXPECT_NE(h2(ByteRange(a4)), h2(ByteRange(b4)));\n  EXPECT_NE(h2(w1), h2(w2));\n  EXPECT_NE(h2(w1), h2(w3));\n  EXPECT_NE(h2(w2), h2(w4));\n\n  // Check compatibility with std::string.\n  EXPECT_EQ(h2(a1), h2(a1.str()));\n  EXPECT_EQ(h2(a2), h2(a2.str()));\n  EXPECT_EQ(h2(a3), h2(a3.str()));\n  EXPECT_EQ(h2(a4), h2(a4.str()));\n\n  // Check compatibility with std::string_view.\n  EXPECT_EQ(h2(a1), h2(std::string_view{a1}));\n  EXPECT_EQ(h2(a2), h2(std::string_view{a2}));\n  EXPECT_EQ(h2(a3), h2(std::string_view{a3}));\n  EXPECT_EQ(h2(a4), h2(std::string_view{a4}));\n}\n\nnamespace {\nvoid deletePointer(const std::unique_ptr<std::string>&) {}\nvoid deletePointer(const std::shared_ptr<std::string>&) {}\nvoid deletePointer(std::string* pointer) {\n  delete pointer;\n}\n\ntemplate <template <typename...> class PtrType>\nvoid pointerTestWithFollyHash() {\n  std::unordered_set<PtrType<std::string>, folly::Hash> set;\n\n  for (auto i = 0; i < 1000; ++i) {\n    auto random = PtrType<std::string>{\n        new std::string{folly::to<std::string>(folly::Random::rand64())}};\n    set.insert(std::move(random));\n  }\n\n  for (auto& pointer : set) {\n    EXPECT_TRUE(set.find(pointer) != set.end());\n    deletePointer(pointer);\n  }\n}\n\ntemplate <typename T>\nusing Pointer = T*;\n} // namespace\n\nTEST(Hash, UniquePtr) {\n  pointerTestWithFollyHash<std::unique_ptr>();\n}\n\nTEST(Hash, SharedPtr) {\n  pointerTestWithFollyHash<std::shared_ptr>();\n}\n\nTEST(Hash, Pointer) {\n  pointerTestWithFollyHash<Pointer>();\n\n  EXPECT_TRUE((\n      std::is_same<\n          folly::hasher<std::string*>::folly_is_avalanching,\n          folly::hasher<std::unique_ptr<std::string>>::folly_is_avalanching>::\n          value));\n\n  EXPECT_TRUE((\n      std::is_same<\n          folly::hasher<std::string*>::folly_is_avalanching,\n          folly::hasher<std::shared_ptr<std::string>>::folly_is_avalanching>::\n          value));\n}\n\ntemplate <typename T>\nstruct FNVTestParam {\n  std::string in;\n  T out;\n};\nusing FNV32TestParam = FNVTestParam<uint32_t>;\nusing FNV64TestParam = FNVTestParam<uint64_t>;\n\nclass FNV32Test : public ::testing::TestWithParam<FNV32TestParam> {};\nclass FNV64Test : public ::testing::TestWithParam<FNV64TestParam> {};\n\nTEST_P(FNV32Test, Fnva32Buf) {\n  EXPECT_EQ(\n      GetParam().out, fnva32_buf(GetParam().in.data(), GetParam().in.size()));\n}\n\nTEST_P(FNV64Test, Fnva64Buf) {\n  EXPECT_EQ(\n      GetParam().out, fnva64_buf(GetParam().in.data(), GetParam().in.size()));\n}\n\nTEST_P(FNV32Test, Fnva32) {\n  EXPECT_EQ(GetParam().out, fnva32(GetParam().in));\n}\n\nTEST_P(FNV64Test, Fnva64) {\n  EXPECT_EQ(GetParam().out, fnva64(GetParam().in));\n}\n\nTEST_P(FNV32Test, Fnva32Partial) {\n  size_t partialLen = GetParam().in.size() / 2;\n  auto data = GetParam().in.data();\n  auto partial = fnva32_buf(data, partialLen);\n  EXPECT_EQ(\n      GetParam().out,\n      fnva32_buf(\n          data + partialLen, GetParam().in.size() - partialLen, partial));\n}\n\nTEST_P(FNV64Test, Fnva64Partial) {\n  size_t partialLen = GetParam().in.size() / 2;\n  auto data = GetParam().in.data();\n  auto partial = fnva64_buf(data, partialLen);\n  EXPECT_EQ(\n      GetParam().out,\n      fnva64_buf(\n          data + partialLen, GetParam().in.size() - partialLen, partial));\n}\n\n// Taken from http://www.isthe.com/chongo/src/fnv/test_fnv.c\nINSTANTIATE_TEST_SUITE_P(\n    FNV32Testing,\n    FNV32Test,\n    ::testing::Values(\n        FNV32TestParam{\n            \"foobar\", // 11\n            0xbf9cf968},\n        FNV32TestParam{\n            \"chongo was here!\\n\", // 39\n            0xd49930d5},\n        FNV32TestParam{\n            \"127.0.0.3\", // 106,\n            0x06a3cdf8},\n        FNV32TestParam{\n            \"http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash\", // 126\n            0xdd16ef45},\n        FNV32TestParam{\n            \"http://norvig.com/21-days.html\", // 136\n            0xeb08bfba}));\n\n// Taken from http://www.isthe.com/chongo/src/fnv/test_fnv.c\nINSTANTIATE_TEST_SUITE_P(\n    FNV64Testing,\n    FNV64Test,\n    ::testing::Values(\n        FNV64TestParam{\n            \"foobar\", // 11\n            0x85944171f73967e8},\n        FNV64TestParam{\n            \"chongo was here!\\n\", // 39\n            0x46810940eff5f915},\n        FNV64TestParam{\n            \"127.0.0.3\", // 106,\n            0xaabafc7104d91158},\n        FNV64TestParam{\n            \"http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash\", // 126\n            0xd9b957fb7fe794c5},\n        FNV64TestParam{\n            \"http://norvig.com/21-days.html\", // 136\n            0x07aaa640476e0b9a}));\n\n//////// static checks\n\nstatic constexpr bool k32Bit = sizeof(std::size_t) == 4;\n\nstatic_assert(!folly::IsAvalanchingHasher<std::hash<int>, int>::value);\nstatic_assert(\n    !folly::IsAvalanchingHasher<std::hash<char const*>, char const*>::value);\nstatic_assert(!folly::IsAvalanchingHasher<std::hash<float>, float>::value);\nstatic_assert(!folly::IsAvalanchingHasher<std::hash<double>, double>::value);\nstatic_assert(\n    !folly::IsAvalanchingHasher<std::hash<long double>, long double>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<std::hash<std::string>, std::string>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<std::hash<std::string_view>, std::string_view>::\n        value);\nstatic_assert(\n    !folly::IsAvalanchingHasher<std::hash<TestEnum>, TestEnum>::value);\nstatic_assert(\n    !folly::IsAvalanchingHasher<std::hash<TestStruct>, TestStruct>::value);\n\nstatic_assert(\n    !folly::IsAvalanchingHasher<folly::transparent<std::hash<int>>, int>::\n        value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        folly::transparent<std::hash<std::string>>,\n        std::string>::value);\n\n// these come from folly/hash/Hash.h\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        std::hash<std::pair<int, int>>,\n        std::pair<int, int>>::value);\nstatic_assert(\n    !folly::IsAvalanchingHasher<std::hash<std::tuple<int>>, std::tuple<int>>::\n        value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        std::hash<std::tuple<std::string>>,\n        std::tuple<std::string>>::value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        std::hash<std::tuple<int, int>>,\n        std::tuple<int, int>>::value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        std::hash<std::tuple<int, int, int>>,\n        std::tuple<int, int, int>>::value);\n\nstatic_assert(\n    k32Bit == folly::IsAvalanchingHasher<folly::Hash, uint8_t>::value);\nstatic_assert(k32Bit == folly::IsAvalanchingHasher<folly::Hash, char>::value);\nstatic_assert(\n    k32Bit == folly::IsAvalanchingHasher<folly::Hash, uint16_t>::value);\nstatic_assert(\n    k32Bit == folly::IsAvalanchingHasher<folly::Hash, int16_t>::value);\nstatic_assert(\n    k32Bit == folly::IsAvalanchingHasher<folly::Hash, uint32_t>::value);\nstatic_assert(\n    k32Bit == folly::IsAvalanchingHasher<folly::Hash, int32_t>::value);\nstatic_assert(folly::IsAvalanchingHasher<folly::Hash, uint64_t>::value);\nstatic_assert(folly::IsAvalanchingHasher<folly::Hash, int64_t>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<folly::Hash, folly::StringPiece>::value);\nstatic_assert(folly::IsAvalanchingHasher<folly::Hash, std::string>::value);\nstatic_assert(\n    k32Bit == folly::IsAvalanchingHasher<folly::Hash, TestEnum>::value);\nstatic_assert(folly::IsAvalanchingHasher<folly::Hash, TestBigEnum>::value);\n\nstatic_assert(\n    k32Bit ==\n    folly::IsAvalanchingHasher<folly::hasher<uint8_t>, uint8_t>::value);\nstatic_assert(\n    k32Bit == folly::IsAvalanchingHasher<folly::hasher<char>, char>::value);\nstatic_assert(\n    k32Bit ==\n    folly::IsAvalanchingHasher<folly::hasher<uint16_t>, uint16_t>::value);\nstatic_assert(\n    k32Bit ==\n    folly::IsAvalanchingHasher<folly::hasher<int16_t>, int16_t>::value);\nstatic_assert(\n    k32Bit ==\n    folly::IsAvalanchingHasher<folly::hasher<uint32_t>, uint32_t>::value);\nstatic_assert(\n    k32Bit ==\n    folly::IsAvalanchingHasher<folly::hasher<int32_t>, int32_t>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<folly::hasher<uint64_t>, uint64_t>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<folly::hasher<int64_t>, int64_t>::value);\nstatic_assert(folly::IsAvalanchingHasher<folly::hasher<float>, float>::value);\nstatic_assert(folly::IsAvalanchingHasher<folly::hasher<double>, double>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<folly::hasher<std::string>, std::string>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<folly::hasher<folly::StringPiece>, std::string>::\n        value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        folly::hasher<folly::StringPiece>,\n        folly::StringPiece>::value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        folly::transparent<folly::hasher<folly::StringPiece>>,\n        folly::StringPiece>::value);\n\nstatic_assert(\n    folly::IsAvalanchingHasher<folly::hasher<std::string>, std::string>::value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        folly::hasher<std::pair<int, int>>,\n        std::pair<int, int>>::value);\nstatic_assert(\n    k32Bit ==\n    folly::IsAvalanchingHasher<\n        folly::hasher<std::tuple<int>>,\n        std::tuple<int>>::value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        folly::hasher<std::tuple<std::string>>,\n        std::tuple<std::string>>::value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        folly::hasher<std::tuple<int, int>>,\n        std::tuple<int, int>>::value);\nstatic_assert( //\n    folly::IsAvalanchingHasher<\n        folly::hasher<std::tuple<int, int, int>>,\n        std::tuple<int, int, int>>::value);\nstatic_assert(\n    k32Bit ==\n    folly::IsAvalanchingHasher<folly::hasher<TestEnum>, TestEnum>::value);\nstatic_assert(\n    folly::IsAvalanchingHasher<folly::hasher<TestBigEnum>, TestBigEnum>::value);\n\n//////// dynamic checks\n\nnamespace {\ntemplate <typename H, typename T, typename F>\nvoid verifyAvalanching(T initialValue, F const& advance) {\n  // This doesn't check probabilities, but does verify that every bit\n  // changed independently of every other bit, in both directions, when\n  // traversing a sequence of dependent changes.  Note that it is NOT\n  // sufficient to just use a random sequence here, because even the\n  // identity function will pass.  As constructed this will require\n  // 2^63 steps to complete for an identity hash, because none of the\n  // transitions with on == 63 will occur until then.\n  H const hasher;\n  constexpr std::size_t N = sizeof(decltype(hasher(initialValue))) * 8;\n\n  // seen[i][j] if we have seen i flip on at the same time as j went off\n  bool seen[N][N] = {};\n  std::size_t unseenCount = N * (N - 1);\n  auto v = initialValue;\n  auto h = hasher(v);\n  std::size_t steps = 0;\n  // wait for 95% coverage\n  while (unseenCount > (N * (N - 1)) / 95) {\n    ++steps;\n    auto hPrev = h;\n    advance(v);\n    h = hasher(v);\n\n    uint64_t delta = hPrev ^ h;\n    for (std::size_t i = 0; i < N - 1; ++i) {\n      if (((delta >> i) & 1) == 0) {\n        continue;\n      }\n      // we know i flipped\n      for (std::size_t j = i + 1; j < N; ++j) {\n        if (((delta >> j) & 1) == 0) {\n          continue;\n        }\n        // we know j flipped\n        bool iOn = ((hPrev >> i) & 1) == 0;\n        bool jOn = ((hPrev >> j) & 1) == 0;\n        if (iOn != jOn) {\n          auto on = iOn ? i : j;\n          auto off = iOn ? j : i;\n          if (!seen[on][off]) {\n            seen[on][off] = true;\n            --unseenCount;\n          }\n        }\n      }\n    }\n\n    // we should actually only need a couple hundred\n    ASSERT_LT(steps, 1000)\n        << unseenCount << \" of \" << (N * (N - 1)) << \" pair transitions unseen\";\n  }\n}\n} // namespace\n\nTEST(Traits, stdHashPairAvalances) {\n  verifyAvalanching<std::hash<std::pair<int, int>>>(\n      std::make_pair(0, 0), [](std::pair<int, int>& v) { v.first++; });\n}\n\nTEST(Traits, stdHashTuple2Avalances) {\n  verifyAvalanching<std::hash<std::tuple<int, int>>>(\n      std::make_tuple(0, 0),\n      [](std::tuple<int, int>& v) { std::get<0>(v) += 1; });\n}\n\nTEST(Traits, stdHashStringAvalances) {\n  verifyAvalanching<std::hash<std::string>, std::string>(\n      \"00000000000000000000000000000\", [](std::string& str) {\n        std::size_t i = 0;\n        while (str[i] == '1') {\n          str[i] = '0';\n          ++i;\n        }\n        str[i] = '1';\n      });\n}\n\nTEST(Traits, stdHashStringViewAvalances) {\n  verifyAvalanching<std::hash<std::string_view>, std::string>(\n      \"00000000000000000000000000000\", [](std::string& str) {\n        std::size_t i = 0;\n        while (str[i] == '1') {\n          str[i] = '0';\n          ++i;\n        }\n        str[i] = '1';\n      });\n}\n\nTEST(Traits, follyHashUint64Avalances) {\n  verifyAvalanching<folly::Hash>(uint64_t{0}, [](uint64_t& v) { v++; });\n}\n\nTEST(Traits, follyHasherInt64Avalances) {\n  verifyAvalanching<folly::hasher<int64_t>>(int64_t{0}, [](int64_t& v) {\n    v++;\n  });\n}\n\nTEST(Traits, follyHasherFloatAvalanches) {\n  verifyAvalanching<folly::hasher<float>>(0.0f, [](float& v) { v += 1; });\n}\n\nTEST(Traits, follyHasherDoubleAvalanches) {\n  verifyAvalanching<folly::hasher<double>>(0.0, [](double& v) { v += 1; });\n}\n\nTEST(HashSequence, Fold) {\n  EXPECT_EQ(folly::hash::detail::hash_sequence(folly::hasher<int>{}), 0);\n  EXPECT_EQ(\n      folly::hash::detail::hash_sequence(folly::hasher<int>{}, 1),\n      folly::hasher<int>{}(1));\n  EXPECT_EQ(\n      folly::hash::detail::hash_sequence(folly::hasher<int>{}, 1, 2),\n      folly::hash::hash_128_to_64(\n          folly::hasher<int>{}(1), folly::hasher<int>{}(2)));\n  EXPECT_EQ(\n      folly::hash::detail::hash_sequence(folly::hasher<int>{}, 1, 2, 3),\n      folly::hash::hash_128_to_64(\n          folly::hasher<int>{}(1),\n          folly::hash::hash_128_to_64(\n              folly::hasher<int>{}(2), folly::hasher<int>{}(3))));\n}\n\nTEST(HashSequence, TypeMix) {\n  EXPECT_EQ(\n      folly::hash::detail::hash_sequence(\n          folly::detail::hash_one, 1, 1.0, std::string_view{\"hello\"}),\n      folly::hash::hash_128_to_64(\n          folly::hasher<int>{}(1),\n          folly::hash::hash_128_to_64(\n              folly::hasher<double>{}(1.0),\n              folly::hasher<std::string_view>{}(\"hello\"))));\n}\n\nTEST(HashTuple, Empty) {\n  auto empty = std::make_tuple<>();\n  EXPECT_EQ(\n      folly::hash::detail::hash_tuple(folly::hasher<decltype(empty)>{}, empty),\n      0);\n}\n\nTEST(HashTuple, Basic) {\n  auto basic = std::make_tuple<int, double, std::string>(1, 1.0, \"hello\");\n  EXPECT_EQ(\n      folly::hash::detail::hash_tuple(folly::detail::hash_one, basic),\n      folly::hash::hash_128_to_64(\n          folly::hasher<int>{}(1),\n          folly::hash::hash_128_to_64(\n              folly::hasher<double>{}(1.0),\n              folly::hasher<std::string>{}(\"hello\"))));\n}\n\nTEST(Hash, cstring_view_hasher_basic) {\n  folly::cstring_view svz(\"hello\");\n  std::string_view sv(\"hello\");\n\n  folly::hasher<folly::cstring_view> hasher_csv;\n  folly::hasher<std::string_view> hasher_sv;\n\n  EXPECT_EQ(hasher_csv(svz), hasher_sv(sv));\n}\n\nTEST(Hash, cstring_view_hasher_different_strings) {\n  folly::cstring_view svz1(\"hello\");\n  folly::cstring_view svz2(\"world\");\n\n  folly::hasher<folly::cstring_view> hasher;\n\n  EXPECT_NE(hasher(svz1), hasher(svz2));\n}\n\nTEST(Hash, cstring_view_hasher_empty_string) {\n  folly::cstring_view svz(\"\");\n  std::string_view sv(\"\");\n\n  folly::hasher<folly::cstring_view> hasher_csv;\n  folly::hasher<std::string_view> hasher_sv;\n\n  EXPECT_EQ(hasher_csv(svz), hasher_sv(sv));\n}\n\nTEST(Hash, cstring_view_hasher_after_remove_prefix) {\n  folly::cstring_view svz(\"hello world\");\n  svz.remove_prefix(6);\n\n  std::string_view sv(\"world\");\n\n  folly::hasher<folly::cstring_view> hasher_csv;\n  folly::hasher<std::string_view> hasher_sv;\n\n  EXPECT_EQ(hasher_csv(svz), hasher_sv(sv));\n}\n\nTEST(Hash, cstring_view_hasher_consistency) {\n  folly::cstring_view svz(\"test\");\n  folly::hasher<folly::cstring_view> hasher;\n\n  size_t hash1 = hasher(svz);\n  size_t hash2 = hasher(svz);\n\n  EXPECT_EQ(hash1, hash2);\n}\n"
  },
  {
    "path": "folly/hash/test/HsiehHashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/HsiehHash.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly::hash;\n\nTEST(HsiehHash, Hsieh32) {\n  const char* s1 = \"hello, world!\";\n  const uint32_t s1_res = 2918802987ul;\n  EXPECT_EQ(hsieh_hash32(s1), s1_res);\n  EXPECT_EQ(hsieh_hash32(s1), hsieh_hash32_buf(s1, strlen(s1)));\n\n  const char* s2 = \"monkeys! m0nk3yz! ev3ry \\\\/\\\\/here~~~~\";\n  const uint32_t s2_res = 47373213ul;\n  EXPECT_EQ(hsieh_hash32(s2), s2_res);\n  EXPECT_EQ(hsieh_hash32(s2), hsieh_hash32_buf(s2, strlen(s2)));\n\n  const char* s3 = \"\";\n  const uint32_t s3_res = 0;\n  EXPECT_EQ(hsieh_hash32(s3), s3_res);\n  EXPECT_EQ(hsieh_hash32(s3), hsieh_hash32_buf(s3, strlen(s3)));\n}\n"
  },
  {
    "path": "folly/hash/test/MurmurHashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstdint>\n#include <string_view>\n\n#include <folly/hash/MurmurHash.h>\n\n#include <folly/portability/GTest.h>\n\n#define TEST_CASES(X)                                                      \\\n  /* Empty string */                                                       \\\n  X(\"\", UINT64_C(0))                                                       \\\n  /* Short sequences, no execution of primary loop, only tail handling. */ \\\n  X(\"0\", UINT64_C(5533571732986600803))                                    \\\n  X(\"01\", UINT64_C(2988402087957123519))                                   \\\n  X(\"012\", UINT64_C(18121251311279197961))                                 \\\n  X(\"0123\", UINT64_C(3086299600550921888))                                 \\\n  X(\"01234\", UINT64_C(12373468686010462630))                               \\\n  X(\"012345\", UINT64_C(8037360064841115407))                               \\\n  X(\"0123456\", UINT64_C(12284635732915976134))                             \\\n  /* Only primary loop. */                                                 \\\n  X(\"01234567\", UINT64_C(9778579411364587418))                             \\\n  X(\"0123456789ABCDEF\", UINT64_C(8277819783762704778))                     \\\n  X(\"0123456789ABCDEF01234567\", UINT64_C(9980960296277708772))             \\\n  /* Primary loop and tail. */                                             \\\n  X(\"0123456789ABCDEF0\", UINT64_C(654503456484488283))                     \\\n  X(\"0123456789ABCDEF01\", UINT64_C(10240825431821950816))                  \\\n  X(\"0123456789ABCDEF012\", UINT64_C(6811778381211949987))                  \\\n  X(\"0123456789ABCDEF0123\", UINT64_C(10791461727592423385))                \\\n  X(\"0123456789ABCDEF01234\", UINT64_C(11236139906480711106))               \\\n  X(\"0123456789ABCDEF012345\", UINT64_C(8264417865430344363))               \\\n  X(\"0123456789ABCDEF0123456\", UINT64_C(2915833106541791378))              \\\n  /* Sequences with bytes represented as negative chars. */                \\\n  X(\"\\x80\", UINT64_C(13393303071874499911))                                \\\n  X(\"\\x80\\x81\", UINT64_C(3896321919913970216))                             \\\n  X(\"\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\", UINT64_C(2468552239318681156))     \\\n  X(\"\\x61\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x62\", UINT64_C(836019401831928519))\n\nnamespace {\n\nconstexpr std::uint64_t murmurHash64(std::string_view input) {\n  return folly::hash::murmurHash64(input.data(), input.size(), /* seed */ 0);\n}\n\n} // namespace\n\nTEST(MurmurHash, Runtime) {\n#define X(s, expected) EXPECT_EQ(murmurHash64(s), expected);\n  TEST_CASES(X)\n#undef X\n}\n\nTEST(MurmurHash, Constexpr) {\n#define X(s, expected)                      \\\n  {                                         \\\n    constexpr uint64_t h = murmurHash64(s); \\\n    static_assert(h == expected);           \\\n  }\n\n  TEST_CASES(X)\n#undef X\n}\n"
  },
  {
    "path": "folly/hash/test/RapidHashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cstdint>\n#include <string_view>\n\n#include <folly/hash/rapidhash.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace std::literals;\n\nconstexpr auto cases = std::array{\n    std::pair{\"\"sv, UINT64_C(232177599295442350)},\n    std::pair{\"0\"sv, UINT64_C(14193856657648385672)},\n    std::pair{\"01\"sv, UINT64_C(15549595023848265440)},\n    std::pair{\"012\"sv, UINT64_C(14036073547449753364)},\n    std::pair{\"0123\"sv, UINT64_C(2155398448399527240)},\n    std::pair{\"01234\"sv, UINT64_C(11595122963875691922)},\n    std::pair{\"012345\"sv, UINT64_C(12910097366968805346)},\n    std::pair{\"0123456\"sv, UINT64_C(2988730266698498992)},\n    std::pair{\"01234567\"sv, UINT64_C(7570412248888932898)},\n    std::pair{\"0123456789ABCDEF\"sv, UINT64_C(4286119474277594607)},\n    std::pair{\"0123456789ABCDEF01234567\"sv, UINT64_C(6602676763163752414)},\n    std::pair{\"0123456789ABCDEF0\"sv, UINT64_C(12163985545246830987)},\n    std::pair{\"0123456789ABCDEF01\"sv, UINT64_C(17633497820352341844)},\n    std::pair{\"0123456789ABCDEF012\"sv, UINT64_C(5134914024862322698)},\n    std::pair{\"0123456789ABCDEF0123\"sv, UINT64_C(15456488218748233591)},\n    std::pair{\"0123456789ABCDEF01234\"sv, UINT64_C(8219044676438946980)},\n    std::pair{\"0123456789ABCDEF012345\"sv, UINT64_C(2949818754802360919)},\n    std::pair{\"0123456789ABCDEF0123456\"sv, UINT64_C(10100507821488338105)},\n    std::pair{\n        \"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\"sv,\n        UINT64_C(5986613784938156867)},\n    // Sequences with bytes represented as negative chars.\n    std::pair{\"\\x80\"sv, UINT64_C(7470186259668200490)},\n    std::pair{\"\\x80\\x81\"sv, UINT64_C(11863878210592514807)},\n    std::pair{\n        \"\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\"sv, UINT64_C(4054026010566036770)},\n    std::pair{\n        \"\\x61\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x62\"sv,\n        UINT64_C(7452325226268640525)},\n};\n\nTEST(RapidHash, Runtime) {\n  for (auto [in, out] : cases) {\n    EXPECT_EQ(out, folly::hash::rapidhash(in.data(), in.size())) << in;\n  }\n}\n\nTEST(RapidHashMicro, Runtime) {\n  for (auto [in, out] : cases) {\n    EXPECT_EQ(out, folly::hash::rapidhashMicro(in.data(), in.size())) << in;\n  }\n}\n\nTEST(RapidHashNano, Runtime) {\n  for (auto [in, out] : cases) {\n    EXPECT_EQ(out, folly::hash::rapidhashNano(in.data(), in.size())) << in;\n  }\n}\n\nTEST(RapidHash, Constexpr) {\n#define TEST_CASE(i)                                           \\\n  {                                                            \\\n    constexpr auto testCase_##i = cases[i];                    \\\n    constexpr uint64_t h_##i = folly::hash::rapidhash(         \\\n        testCase_##i.first.data(), testCase_##i.first.size()); \\\n    static_assert(h_##i == testCase_##i.second);               \\\n  }\n\n  TEST_CASE(0);\n  TEST_CASE(1);\n  TEST_CASE(2);\n  TEST_CASE(3);\n  TEST_CASE(4);\n  TEST_CASE(5);\n  TEST_CASE(6);\n  TEST_CASE(7);\n  TEST_CASE(8);\n  TEST_CASE(9);\n  TEST_CASE(10);\n  TEST_CASE(11);\n  TEST_CASE(12);\n  TEST_CASE(13);\n  TEST_CASE(14);\n  TEST_CASE(15);\n  TEST_CASE(16);\n  TEST_CASE(17);\n  TEST_CASE(18);\n  TEST_CASE(19);\n  TEST_CASE(20);\n  TEST_CASE(21);\n  TEST_CASE(22);\n}\n\nTEST(RapidHashMicro, Constexpr) {\n#define TEST_CASE_MICRO(i)                                     \\\n  {                                                            \\\n    constexpr auto testCase_##i = cases[i];                    \\\n    constexpr uint64_t h_##i = folly::hash::rapidhashMicro(    \\\n        testCase_##i.first.data(), testCase_##i.first.size()); \\\n    static_assert(h_##i == testCase_##i.second);               \\\n  }\n\n  TEST_CASE_MICRO(0);\n  TEST_CASE_MICRO(1);\n  TEST_CASE_MICRO(2);\n  TEST_CASE_MICRO(3);\n  TEST_CASE_MICRO(4);\n  TEST_CASE_MICRO(5);\n  TEST_CASE_MICRO(6);\n  TEST_CASE_MICRO(7);\n  TEST_CASE_MICRO(8);\n  TEST_CASE_MICRO(9);\n  TEST_CASE_MICRO(10);\n  TEST_CASE_MICRO(11);\n  TEST_CASE_MICRO(12);\n  TEST_CASE_MICRO(13);\n  TEST_CASE_MICRO(14);\n  TEST_CASE_MICRO(15);\n  TEST_CASE_MICRO(16);\n  TEST_CASE_MICRO(17);\n  TEST_CASE_MICRO(18);\n  TEST_CASE_MICRO(19);\n  TEST_CASE_MICRO(20);\n  TEST_CASE_MICRO(21);\n  TEST_CASE_MICRO(22);\n}\n\nTEST(RapidHashNano, Constexpr) {\n#define TEST_CASE_NANO(i)                                      \\\n  {                                                            \\\n    constexpr auto testCase_##i = cases[i];                    \\\n    constexpr uint64_t h_##i = folly::hash::rapidhashNano(     \\\n        testCase_##i.first.data(), testCase_##i.first.size()); \\\n    static_assert(h_##i == testCase_##i.second);               \\\n  }\n\n  TEST_CASE_NANO(0);\n  TEST_CASE_NANO(1);\n  TEST_CASE_NANO(2);\n  TEST_CASE_NANO(3);\n  TEST_CASE_NANO(4);\n  TEST_CASE_NANO(5);\n  TEST_CASE_NANO(6);\n  TEST_CASE_NANO(7);\n  TEST_CASE_NANO(8);\n  TEST_CASE_NANO(9);\n  TEST_CASE_NANO(10);\n  TEST_CASE_NANO(11);\n  TEST_CASE_NANO(12);\n  TEST_CASE_NANO(13);\n  TEST_CASE_NANO(14);\n  TEST_CASE_NANO(15);\n  TEST_CASE_NANO(16);\n  TEST_CASE_NANO(17);\n  TEST_CASE_NANO(18);\n  TEST_CASE_NANO(19);\n  TEST_CASE_NANO(20);\n  TEST_CASE_NANO(21);\n  TEST_CASE_NANO(22);\n}\n"
  },
  {
    "path": "folly/hash/test/SpookyHashV1Test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// SpookyHash: a 128-bit noncryptographic hash function\n// By Bob Jenkins, public domain\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS 1\n#endif\n\n#include <folly/hash/SpookyHashV1.h>\n\n#include <cinttypes>\n#include <cstddef>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <glog/logging.h>\n\n#include <folly/portability/GTest.h>\n#include <folly/portability/Time.h>\n\nusing namespace ::folly::hash;\n\n// clang-format off\n\nstatic bool failed = false;\n\nstatic uint64_t GetClockTickCount() {\n  timespec ts;\n  clock_gettime(CLOCK_REALTIME, &ts);\n  return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;  // milliseconds\n}\n\nclass Random\n{\npublic:\n    inline uint64_t Value()\n    {\n        uint64_t e = m_a - Rot64(m_b, 23);\n        m_a = m_b ^ Rot64(m_c, 16);\n        m_b = m_c + Rot64(m_d, 11);\n        m_c = m_d + e;\n        m_d = e + m_a;\n        return m_d;\n    }\n\n    inline void Init( uint64_t seed)\n    {\n        m_a = 0xdeadbeef;\n        m_b = m_c = m_d = seed;\n        for (int i = 0; i < 20; ++i) {\n          (void)Value();\n        }\n    }\n\nprivate:\n    static inline uint64_t Rot64(uint64_t x, int k)\n    {\n        return (x << k) | (x >> (64-(k)));\n    }\n\n    uint64_t m_a;\n    uint64_t m_b;\n    uint64_t m_c;\n    uint64_t m_d;\n};\n\n// fastest conceivable hash function (for comparison)\nstatic void Add(const void *data, size_t length,\n                uint64_t *hash1, uint64_t *hash2)\n{\n    uint64_t *p64 = (uint64_t *)data;\n    uint64_t *end = p64 + length/8;\n    uint64_t hash = *hash1 + *hash2;\n    while (p64 < end)\n    {\n      hash += *p64;\n      ++p64;\n    }\n    *hash1 = hash;\n    *hash2 = hash;\n}\n\n#define BUFSIZE (512)\nvoid TestResults()\n{\n    printf(\"\\ntesting results ...\\n\");\n    static const uint32_t expected[BUFSIZE] = {\n        0xa24295ec, 0xfe3a05ce, 0x257fd8ef, 0x3acd5217,\n        0xfdccf85c, 0xc7b5f143, 0x3b0c3ff0, 0x5220f13c,\n        0xa6426724, 0x4d5426b4, 0x43e76b26, 0x051bc437,\n        0xd8f28a02, 0x23ccc30e, 0x811d1a2d, 0x039128d4,\n        0x9cd96a73, 0x216e6a8d, 0x97293fe8, 0xe4fc6d09,\n        0x1ad34423, 0x9722d7e4, 0x5a6fdeca, 0x3c94a7e1,\n        0x81a9a876, 0xae3f7c0e, 0x624b50ee, 0x875e5771,\n        0x0095ab74, 0x1a7333fb, 0x056a4221, 0xa38351fa,\n\n        0x73f575f1, 0x8fded05b, 0x9097138f, 0xbd74620c,\n        0x62d3f5f2, 0x07b78bd0, 0xbafdd81e, 0x0638f2ff,\n        0x1f6e3aeb, 0xa7786473, 0x71700e1d, 0x6b4625ab,\n        0xf02867e1, 0xb2b2408f, 0x9ce21ce5, 0xa62baaaf,\n        0x26720461, 0x434813ee, 0x33bc0f14, 0xaaab098a,\n        0x750af488, 0xc31bf476, 0x9cecbf26, 0x94793cf3,\n        0xe1a27584, 0xe80c4880, 0x1299f748, 0x25e55ed2,\n        0x405e3feb, 0x109e2412, 0x3e55f94f, 0x59575864,\n\n        0x365c869d, 0xc9852e6a, 0x12c30c62, 0x47f5b286,\n        0xb47e488d, 0xa6667571, 0x78220d67, 0xa49e30b9,\n        0x2005ef88, 0xf6d3816d, 0x6926834b, 0xe6116805,\n        0x694777aa, 0x464af25b, 0x0e0e2d27, 0x0ea92eae,\n        0x602c2ca9, 0x1d1d79c5, 0x6364f280, 0x939ee1a4,\n        0x3b851bd8, 0x5bb6f19f, 0x80b9ed54, 0x3496a9f1,\n        0xdf815033, 0x91612339, 0x14c516d6, 0xa3f0a804,\n        0x5e78e975, 0xf408bcd9, 0x63d525ed, 0xa1e459c3,\n\n        0xfde303af, 0x049fc17f, 0xe7ed4489, 0xfaeefdb6,\n        0x2b1b2fa8, 0xc67579a6, 0x5505882e, 0xe3e1c7cb,\n        0xed53bf30, 0x9e628351, 0x8fa12113, 0x7500c30f,\n        0xde1bee00, 0xf1fefe06, 0xdc759c00, 0x4c75e5ab,\n        0xf889b069, 0x695bf8ae, 0x47d6600f, 0xd2a84f87,\n        0xa0ca82a9, 0x8d2b750c, 0xe03d8cd7, 0x581fea33,\n        0x969b0460, 0x36c7b7de, 0x74b3fd20, 0x2bb8bde6,\n        0x13b20dec, 0xa2dcee89, 0xca36229d, 0x06fdb74e,\n\n\n        0x6d9a982d, 0x02503496, 0xbdb4e0d9, 0xbd1f94cf,\n        0x6d26f82d, 0xcf5e41cd, 0x88b67b65, 0x3e1b3ee4,\n        0xb20e5e53, 0x1d9be438, 0xcef9c692, 0x299bd1b2,\n        0xb1279627, 0x210b5f3d, 0x5569bd88, 0x9652ed43,\n        0x7e8e0f8c, 0xdfa01085, 0xcd6d6343, 0xb8739826,\n        0xa52ce9a0, 0xd33ef231, 0x1b4d92c2, 0xabfa116d,\n        0xcdf47800, 0x3a4eefdc, 0xd01f3bcf, 0x30a32f46,\n        0xfb54d851, 0x06a98f67, 0xbdcd0a71, 0x21a00949,\n\n        0xfe7049c9, 0x67ef46d2, 0xa1fabcbc, 0xa4c72db4,\n        0x4a8a910d, 0x85a890ad, 0xc37e9454, 0xfc3d034a,\n        0x6f46cc52, 0x742be7a8, 0xe94ecbc5, 0x5f993659,\n        0x98270309, 0x8d1adae9, 0xea6e035e, 0x293d5fae,\n        0x669955b3, 0x5afe23b5, 0x4c74efbf, 0x98106505,\n        0xfbe09627, 0x3c00e8df, 0x5b03975d, 0x78edc83c,\n        0x117c49c6, 0x66cdfc73, 0xfa55c94f, 0x5bf285fe,\n        0x2db49b7d, 0xfbfeb8f0, 0xb7631bab, 0x837849f3,\n\n        0xf77f3ae5, 0x6e5db9bc, 0xfdd76f15, 0x545abf92,\n        0x8b538102, 0xdd5c9b65, 0xa5adfd55, 0xecbd7bc5,\n        0x9f99ebdd, 0x67500dcb, 0xf5246d1f, 0x2b0c061c,\n        0x927a3747, 0xc77ba267, 0x6da9f855, 0x6240d41a,\n        0xe9d1701d, 0xc69f0c55, 0x2c2c37cf, 0x12d82191,\n        0x47be40d3, 0x165b35cd, 0xb7db42e1, 0x358786e4,\n        0x84b8fc4e, 0x92f57c28, 0xf9c8bbd7, 0xab95a33d,\n        0x11009238, 0xe9770420, 0xd6967e2a, 0x97c1589f,\n\n        0x2ee7e7d3, 0x32cc86da, 0xe47767d1, 0x73e9b61e,\n        0xd35bac45, 0x835a62bb, 0x5d9217b0, 0x43f3f0ed,\n        0x8a97911e, 0x4ec7eb55, 0x4b5a988c, 0xb9056683,\n        0x45456f97, 0x1669fe44, 0xafb861b8, 0x8e83a19c,\n        0x0bab08d6, 0xe6a145a9, 0xc31e5fc2, 0x27621f4c,\n        0x795692fa, 0xb5e33ab9, 0x1bc786b6, 0x45d1c106,\n        0x986531c9, 0x40c9a0ec, 0xff0fdf84, 0xa7359a42,\n        0xfd1c2091, 0xf73463d4, 0x51b0d635, 0x1d602fb4,\n\n\n        0xc56b69b7, 0x6909d3f7, 0xa04d68f4, 0x8d1001a7,\n        0x8ecace50, 0x21ec4765, 0x3530f6b0, 0x645f3644,\n        0x9963ef1e, 0x2b3c70d5, 0xa20c823b, 0x8d26dcae,\n        0x05214e0c, 0x1993896d, 0x62085a35, 0x7b620b67,\n        0x1dd85da2, 0x09ce9b1d, 0xd7873326, 0x063ff730,\n        0xf4ff3c14, 0x09a49d69, 0x532062ba, 0x03ba7729,\n        0xbd9a86cc, 0xe26d02a7, 0x7ccbe5d3, 0x4f662214,\n        0x8b999a66, 0x3d0b92b4, 0x70b210f0, 0xf5b8f16f,\n\n        0x32146d34, 0x430b92bf, 0x8ab6204c, 0x35e6e1ff,\n        0xc2f6c2fa, 0xa2df8a1a, 0x887413ec, 0x7cb7a69f,\n        0x7ac6dbe6, 0x9102d1cb, 0x8892a590, 0xc804fe3a,\n        0xdfc4920a, 0xfc829840, 0x8910d2eb, 0x38a210fd,\n        0x9d840cc9, 0x7b9c827f, 0x3444ca0c, 0x071735ab,\n        0x5e9088e4, 0xc995d60e, 0xbe0bb942, 0x17b089ae,\n        0x050e1054, 0xcf4324f7, 0x1e3e64dd, 0x436414bb,\n        0xc48fc2e3, 0x6b6b83d4, 0x9f6558ac, 0x781b22c5,\n\n        0x7147cfe2, 0x3c221b4d, 0xa5602765, 0x8f01a4f0,\n        0x2a9f14ae, 0x12158cb8, 0x28177c50, 0x1091a165,\n        0x39e4e4be, 0x3e451b7a, 0xd965419c, 0x52053005,\n        0x0798aa53, 0xe6773e13, 0x1207f671, 0xd2ef998b,\n        0xab88a38f, 0xc77a8482, 0xa88fb031, 0x5199e0cd,\n        0x01b30536, 0x46eeb0ef, 0x814259ff, 0x9789a8cf,\n        0x376ec5ac, 0x7087034a, 0x948b6bdd, 0x4281e628,\n        0x2c848370, 0xd76ce66a, 0xe9b6959e, 0x24321a8e,\n\n        0xdeddd622, 0xb890f960, 0xea26c00a, 0x55e7d8b2,\n        0xeab67f09, 0x9227fb08, 0xeebbed06, 0xcac1b0d1,\n        0xb6412083, 0x05d2b0e7, 0x9037624a, 0xc9702198,\n        0x2c8d1a86, 0x3e7d416e, 0xc3f1a39f, 0xf04bdce4,\n        0xc88cdb61, 0xbdc89587, 0x4d29b63b, 0x6f24c267,\n        0x4b529c87, 0x573f5a53, 0xdb3316e9, 0x288eb53b,\n        0xd2c074bd, 0xef44a99a, 0x2b404d2d, 0xf6706464,\n        0xfe824f4c, 0xc3debaf8, 0x12f44f98, 0x03135e76,\n\n\n        0xb4888e7f, 0xb6b2325d, 0x3a138259, 0x513c83ec,\n        0x2386d214, 0x94555500, 0xfbd1522d, 0xda2af018,\n        0x15b054c0, 0x5ad654e6, 0xb6ed00aa, 0xa2f2180e,\n        0x5f662825, 0xecd11366, 0x1de5e99d, 0x07afd2ad,\n        0xcf457b04, 0xe631e10b, 0x83ae8a21, 0x709f0d59,\n        0x3e278bf9, 0x246816db, 0x9f5e8fd3, 0xc5b5b5a2,\n        0xd54a9d5c, 0x4b6f2856, 0x2eb5a666, 0xfc68bdd4,\n        0x1ed1a7f8, 0x98a34b75, 0xc895ada9, 0x2907cc69,\n\n        0x87b0b455, 0xddaf96d9, 0xe7da15a6, 0x9298c82a,\n        0x72bd5cab, 0x2e2a6ad4, 0x7f4b6bb8, 0x525225fe,\n        0x985abe90, 0xac1fd6e1, 0xb8340f23, 0x92985159,\n        0x7d29501d, 0xe75dc744, 0x687501b4, 0x92077dc3,\n        0x58281a67, 0xe7e8e9be, 0xd0e64fd1, 0xb2eb0a30,\n        0x0e1feccd, 0xc0dc4a9e, 0x5c4aeace, 0x2ca5b93c,\n        0xee0ec34f, 0xad78467b, 0x0830e76e, 0x0df63f8b,\n        0x2c2dfd95, 0x9b41ed31, 0x9ff4cddc, 0x1590c412,\n\n        0x2366fc82, 0x7a83294f, 0x9336c4de, 0x2343823c,\n        0x5b681096, 0xf320e4c2, 0xc22b70e2, 0xb5fbfb2a,\n        0x3ebc2fed, 0x11af07bd, 0x429a08c5, 0x42bee387,\n        0x58629e33, 0xfb63b486, 0x52135fbe, 0xf1380e60,\n        0x6355de87, 0x2f0bb19a, 0x167f63ac, 0x507224cf,\n        0xf7c99d00, 0x71646f50, 0x74feb1ca, 0x5f9abfdd,\n        0x278f7d68, 0x70120cd7, 0x4281b0f2, 0xdc8ebe5c,\n        0x36c32163, 0x2da1e884, 0x61877598, 0xbef04402,\n\n        0x304db695, 0xfa8e9add, 0x503bac31, 0x0fe04722,\n        0xf0d59f47, 0xcdc5c595, 0x918c39dd, 0x0cad8d05,\n        0x6b3ed1eb, 0x4d43e089, 0x7ab051f8, 0xdeec371f,\n        0x0f4816ae, 0xf8a1a240, 0xd15317f6, 0xb8efbf0b,\n        0xcdd05df8, 0x4fd5633e, 0x7cf19668, 0x25d8f422,\n        0x72d156f2, 0x2a778502, 0xda7aefb9, 0x4f4f66e8,\n        0x19db6bff, 0x74e468da, 0xa754f358, 0x7339ec50,\n        0x139006f6, 0xefbd0b91, 0x217e9a73, 0x939bd79c\n    };\n\n    uint8_t buf[BUFSIZE];\n    uint32_t saw[BUFSIZE];\n    for (int i=0; i<BUFSIZE; ++i)\n    {\n        buf[i] = i+128;\n        saw[i] = SpookyHashV1::Hash32(buf, i, 0);\n        if (saw[i] != expected[i])\n        {\n            printf(\"%d: saw 0x%.8x, expected 0x%.8x\\n\", i, saw[i], expected[i]);\n            failed = true;\n        }\n    }\n}\n#undef BUFSIZE\n\n\n#define NUMBUF (1<<10)\n#define BUFSIZE (1<<20)\nvoid DoTimingBig(int seed)\n{\n    printf(\"\\ntesting time to hash 2^^30 bytes ...\\n\");\n\n    char *buf[NUMBUF];\n    for (int i=0; i<NUMBUF; ++i)\n    {\n        buf[i] = (char *)malloc(BUFSIZE);\n        memset(buf[i], (char)seed, BUFSIZE);\n    }\n\n    uint64_t a = GetClockTickCount();\n    uint64_t hash1 = seed;\n    uint64_t hash2 = seed;\n    for (uint64_t i=0; i<NUMBUF; ++i)\n    {\n        SpookyHashV1::Hash128(buf[i], BUFSIZE, &hash1, &hash2);\n    }\n    uint64_t z = GetClockTickCount();\n    printf(\"SpookyHashV1::Hash128, uncached: time is \"\n           \"%4\" PRIu64 \" milliseconds\\n\", z-a);\n\n    a = GetClockTickCount();\n    for (uint64_t i=0; i<NUMBUF; ++i)\n    {\n        Add(buf[i], BUFSIZE, &hash1, &hash2);\n    }\n    z = GetClockTickCount();\n    printf(\"Addition           , uncached: time is \"\n           \"%4\" PRIu64 \" milliseconds\\n\", z-a);\n\n    a = GetClockTickCount();\n    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)\n    {\n        SpookyHashV1::Hash128(buf[0], 1024, &hash1, &hash2);\n    }\n    z = GetClockTickCount();\n    printf(\"SpookyHashV1::Hash128,   cached: time is \"\n           \"%4\" PRIu64 \" milliseconds\\n\", z-a);\n\n    a = GetClockTickCount();\n    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)\n    {\n        Add(buf[0], 1024, &hash1, &hash2);\n    }\n    z = GetClockTickCount();\n    printf(\"Addition           ,   cached: time is \"\n           \"%4\" PRIu64 \" milliseconds\\n\", z-a);\n\n    for (int i=0; i<NUMBUF; ++i)\n    {\n        free(buf[i]);\n        buf[i] = nullptr;\n    }\n}\n#undef NUMBUF\n#undef BUFSIZE\n\n\n#define BUFSIZE (1<<14)\n#define NUMITER 10000000\nvoid DoTimingSmall(int seed)\n{\n    printf(\"\\ntesting timing of hashing up to %d cached aligned bytes %d \"\n           \"times ...\\n\", BUFSIZE, NUMITER);\n\n    uint64_t buf[BUFSIZE/8];\n    for (int i=0; i<BUFSIZE/8; ++i)\n    {\n        buf[i] = i+seed;\n    }\n\n    for (int i=1; i <= BUFSIZE; i <<= 1)\n    {\n        uint64_t a = GetClockTickCount();\n        uint64_t hash1 = seed;\n        uint64_t hash2 = seed+i;\n        for (int j=0; j<NUMITER; ++j)\n        {\n            SpookyHashV1::Hash128((char *)buf, i, &hash1, &hash2);\n        }\n        uint64_t z = GetClockTickCount();\n        printf(\"%d bytes: hash is %.16\" PRIx64 \" %.16\" PRIx64 \", \"\n               \"time is %\" PRIu64 \"\\n\", i, hash1, hash2, z-a);\n    }\n}\n#undef BUFSIZE\n\n#define BUFSIZE 1024\nvoid TestAlignment()\n{\n    printf(\"\\ntesting alignment ...\\n\");\n\n    char buf[BUFSIZE];\n    uint64_t hash[8];\n    for (int i=0; i<BUFSIZE-16; ++i)\n    {\n        for (int j=0; j<8; ++j)\n        {\n            buf[j] = (char)i+j;\n            for (int k=1; k<=i; ++k)\n            {\n                buf[j+k] = k;\n            }\n            buf[j+i+1] = (char)i+j;\n            hash[j] = SpookyHashV1::Hash64((const void *)(buf+j+1), i, 0);\n        }\n        for (int j=1; j<8; ++j)\n        {\n            if (hash[0] != hash[j])\n            {\n                printf(\"alignment problems: %d %d\\n\", i, j);\n                failed = true;\n            }\n        }\n    }\n}\n#undef BUFSIZE\n\n// test that all deltas of one or two input bits affect all output bits\n#define BUFSIZE 256\n#define TRIES 50\n#define MEASURES 6\nvoid TestDeltas(int seed)\n{\n    printf(\"\\nall 1 or 2 bit input deltas get %d tries to flip every output \"\n           \"bit ...\\n\", TRIES);\n\n    Random random;\n    random.Init((uint64_t)seed);\n\n    // for messages 0..BUFSIZE-1 bytes\n    for (int h=0; h<BUFSIZE; ++h)\n    {\n        int maxk = 0;\n        // first bit to set\n        for (int i=0; i<h*8; ++i)\n        {\n            // second bit to set, or don't have a second bit\n            for (int j=0; j<=i; ++j)\n            {\n                uint64_t measure[MEASURES][2];\n                uint64_t counter[MEASURES][2];\n                for (int l=0; l<2; ++l)\n                {\n                    for (int m=0; m<MEASURES; ++m)\n                    {\n                        counter[m][l] = 0;\n                    }\n                }\n\n                // try to hit every output bit TRIES times\n                int k;\n                for (k=0; k<TRIES; ++k)\n                {\n                    uint8_t buf1[BUFSIZE];\n                    uint8_t buf2[BUFSIZE];\n                    int done = 1;\n                    for (int l=0; l<h; ++l)\n                    {\n                        buf1[l] = buf2[l] = random.Value();\n                    }\n                    buf1[i/8] ^= (1 << (i%8));\n                    if (j != i)\n                    {\n                        buf1[j/8] ^= (1 << (j%8));\n                    }\n                    SpookyHashV1::Hash128(buf1, h,\n                            &measure[0][0], &measure[0][1]);\n                    SpookyHashV1::Hash128(buf2, h,\n                            &measure[1][0], &measure[1][1]);\n                    for (int l=0; l<2; ++l) {\n                        measure[2][l] = measure[0][l] ^ measure[1][l];\n                        measure[3][l] = ~(measure[0][l] ^ measure[1][l]);\n                        measure[4][l] = measure[0][l] - measure[1][l];\n                        measure[4][l] ^= (measure[4][l]>>1);\n                        measure[5][l] = measure[0][l] + measure[1][l];\n                        measure[5][l] ^= (measure[4][l]>>1);\n                    }\n                    for (int l=0; l<2; ++l)\n                    {\n                        for (int m=0; m<MEASURES; ++m)\n                        {\n                            counter[m][l] |= measure[m][l];\n                            if (~counter[m][l]) {\n                              done = 0;\n                            }\n                        }\n                    }\n                    if (done) {\n                      break;\n                    }\n                }\n                if (k == TRIES)\n                {\n                    printf(\"failed %d %d %d\\n\", h, i, j);\n                    failed = true;\n                }\n                else if (k > maxk)\n                {\n                    maxk = k;\n                }\n            }\n        }\n        printf(\"passed for buffer size %d  max %d\\n\", h, maxk);\n    }\n}\n#undef BUFSIZE\n#undef TRIES\n#undef MEASURES\n\n\n// test that hashing pieces has the same behavior as hashing the whole\n#define BUFSIZE 1024\nvoid TestPieces()\n{\n    printf(\"\\ntesting pieces ...\\n\");\n    char buf[BUFSIZE];\n    for (int i=0; i<BUFSIZE; ++i)\n    {\n        buf[i] = i;\n    }\n    for (int i=0; i<BUFSIZE; ++i)\n    {\n        uint64_t a,b,c,d,seed1=1,seed2=2;\n        SpookyHashV1 state;\n\n        // all as one call\n        a = seed1;\n        b = seed2;\n        SpookyHashV1::Hash128(buf, i, &a, &b);\n\n        // all as one piece\n        c = 0xdeadbeefdeadbeef;\n        d = 0xbaceba11baceba11;\n        state.Init(seed1, seed2);\n        state.Update(buf, i);\n        state.Final(&c, &d);\n\n        if (a != c)\n        {\n            printf(\"wrong a %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\", i, a,c);\n            failed = true;\n        }\n        if (b != d)\n        {\n            printf(\"wrong b %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\", i, b,d);\n            failed = true;\n        }\n\n        // all possible two consecutive pieces\n        for (int j=0; j<i; ++j)\n        {\n            c = seed1;\n            d = seed2;\n            state.Init(c, d);\n            state.Update(&buf[0], j);\n            state.Update(&buf[j], i-j);\n            state.Final(&c, &d);\n            if (a != c)\n            {\n                printf(\"wrong a %d %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\",\n                       j, i, a,c);\n                failed = true;\n            }\n            if (b != d)\n            {\n                printf(\"wrong b %d %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\",\n                       j, i, b,d);\n                failed = true;\n            }\n        }\n    }\n}\n#undef BUFSIZE\n\nTEST(SpookyHashV1, Main) {\n    TestResults();\n    TestAlignment();\n    TestPieces();\n    DoTimingBig(1);\n    // tudorb@fb.com: Commented out slow tests\n#if 0\n    DoTimingSmall(argc);\n    TestDeltas(argc);\n#endif\n    CHECK_EQ(failed, 0);\n}\n\n// clang-format on\n"
  },
  {
    "path": "folly/hash/test/SpookyHashV2Test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// SpookyHash: a 128-bit noncryptographic hash function\n// By Bob Jenkins, public domain\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS 1\n#endif\n\n#include <folly/hash/SpookyHashV2.h>\n\n#include <cinttypes>\n#include <cstddef>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n\n#include <glog/logging.h>\n\n#include <folly/portability/GTest.h>\n#include <folly/portability/Time.h>\n\nusing namespace ::folly::hash;\n\n// clang-format off\n\nstatic bool failed = false;\n\nstatic uint64_t GetClockTickCount() {\n  timespec ts;\n  clock_gettime(CLOCK_REALTIME, &ts);\n  return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; // milliseconds\n}\n\nclass Random\n{\npublic:\n    inline uint64_t Value()\n    {\n        uint64_t e = m_a - Rot64(m_b, 23);\n        m_a = m_b ^ Rot64(m_c, 16);\n        m_b = m_c + Rot64(m_d, 11);\n        m_c = m_d + e;\n        m_d = e + m_a;\n        return m_d;\n    }\n\n    inline void Init( uint64_t seed)\n    {\n        m_a = 0xdeadbeef;\n        m_b = m_c = m_d = seed;\n        for (int i = 0; i < 20; ++i) {\n          (void)Value();\n        }\n    }\n\nprivate:\n    static inline uint64_t Rot64(uint64_t x, int k)\n    {\n        return (x << k) | (x >> (64-(k)));\n    }\n\n    uint64_t m_a;\n    uint64_t m_b;\n    uint64_t m_c;\n    uint64_t m_d;\n};\n\n// fastest conceivable hash function (for comparison)\nstatic void Add(const void *data, size_t length,\n                uint64_t *hash1, uint64_t *hash2)\n{\n    uint64_t *p64 = (uint64_t *)data;\n    uint64_t *end = p64 + length/8;\n    uint64_t hash = *hash1 + *hash2;\n    while (p64 < end)\n    {\n      hash += *p64;\n      ++p64;\n    }\n    *hash1 = hash;\n    *hash2 = hash;\n}\n\n#define BUFSIZE (512)\nvoid TestResults()\n{\n    printf(\"\\ntesting results ...\\n\");\n    static const uint64_t expected[BUFSIZE] = {\n      0x6bf50919,0x70de1d26,0xa2b37298,0x35bc5fbf,\n      0x8223b279,0x5bcb315e,0x53fe88a1,0xf9f1a233,\n      0xee193982,0x54f86f29,0xc8772d36,0x9ed60886,\n      0x5f23d1da,0x1ed9f474,0xf2ef0c89,0x83ec01f9,\n      0xf274736c,0x7e9ac0df,0xc7aed250,0xb1015811,\n      0xe23470f5,0x48ac20c4,0xe2ab3cd5,0x608f8363,\n      0xd0639e68,0xc4e8e7ab,0x863c7c5b,0x4ea63579,\n      0x99ae8622,0x170c658b,0x149ba493,0x027bca7c,\n      0xe5cfc8b6,0xce01d9d7,0x11103330,0x5d1f5ed4,\n      0xca720ecb,0xef408aec,0x733b90ec,0x855737a6,\n      0x9856c65f,0x647411f7,0x50777c74,0xf0f1a8b7,\n      0x9d7e55a5,0xc68dd371,0xfc1af2cc,0x75728d0a,\n      0x390e5fdc,0xf389b84c,0xfb0ccf23,0xc95bad0e,\n      0x5b1cb85a,0x6bdae14f,0x6deb4626,0x93047034,\n      0x6f3266c6,0xf529c3bd,0x396322e7,0x3777d042,\n      0x1cd6a5a2,0x197b402e,0xc28d0d2b,0x09c1afb4,\n\n      0x069c8bb7,0x6f9d4e1e,0xd2621b5c,0xea68108d,\n      0x8660cb8f,0xd61e6de6,0x7fba15c7,0xaacfaa97,\n      0xdb381902,0x4ea22649,0x5d414a1e,0xc3fc5984,\n      0xa0fc9e10,0x347dc51c,0x37545fb6,0x8c84b26b,\n      0xf57efa5d,0x56afaf16,0xb6e1eb94,0x9218536a,\n      0xe3cc4967,0xd3275ef4,0xea63536e,0x6086e499,\n      0xaccadce7,0xb0290d82,0x4ebfd0d6,0x46ccc185,\n      0x2eeb10d3,0x474e3c8c,0x23c84aee,0x3abae1cb,\n      0x1499b81a,0xa2993951,0xeed176ad,0xdfcfe84c,\n      0xde4a961f,0x4af13fe6,0xe0069c42,0xc14de8f5,\n      0x6e02ce8f,0x90d19f7f,0xbca4a484,0xd4efdd63,\n      0x780fd504,0xe80310e3,0x03abbc12,0x90023849,\n      0xd6f6fb84,0xd6b354c5,0x5b8575f0,0x758f14e4,\n      0x450de862,0x90704afb,0x47209a33,0xf226b726,\n      0xf858dab8,0x7c0d6de9,0xb05ce777,0xee5ff2d4,\n      0x7acb6d5c,0x2d663f85,0x41c72a91,0x82356bf2,\n\n      0x94e948ec,0xd358d448,0xeca7814d,0x78cd7950,\n      0xd6097277,0x97782a5d,0xf43fc6f4,0x105f0a38,\n      0x9e170082,0x4bfe566b,0x4371d25f,0xef25a364,\n      0x698eb672,0x74f850e4,0x4678ff99,0x4a290dc6,\n      0x3918f07c,0x32c7d9cd,0x9f28e0af,0x0d3c5a86,\n      0x7bfc8a45,0xddf0c7e1,0xdeacb86b,0x970b3c5c,\n      0x5e29e199,0xea28346d,0x6b59e71b,0xf8a8a46a,\n      0x862f6ce4,0x3ccb740b,0x08761e9e,0xbfa01e5f,\n      0xf17cfa14,0x2dbf99fb,0x7a0be420,0x06137517,\n      0xe020b266,0xd25bfc61,0xff10ed00,0x42e6be8b,\n      0x029ef587,0x683b26e0,0xb08afc70,0x7c1fd59e,\n      0xbaae9a70,0x98c8c801,0xb6e35a26,0x57083971,\n      0x90a6a680,0x1b44169e,0x1dce237c,0x518e0a59,\n      0xccb11358,0x7b8175fb,0xb8fe701a,0x10d259bb,\n      0xe806ce10,0x9212be79,0x4604ae7b,0x7fa22a84,\n      0xe715b13a,0x0394c3b2,0x11efbbae,0xe13d9e19,\n\n      0x77e012bd,0x2d05114c,0xaecf2ddd,0xb2a2b4aa,\n      0xb9429546,0x55dce815,0xc89138f8,0x46dcae20,\n      0x1f6f7162,0x0c557ebc,0x5b996932,0xafbbe7e2,\n      0xd2bd5f62,0xff475b9f,0x9cec7108,0xeaddcffb,\n      0x5d751aef,0xf68f7bdf,0xf3f4e246,0x00983fcd,\n      0x00bc82bb,0xbf5fd3e7,0xe80c7e2c,0x187d8b1f,\n      0xefafb9a7,0x8f27a148,0x5c9606a9,0xf2d2be3e,\n      0xe992d13a,0xe4bcd152,0xce40b436,0x63d6a1fc,\n      0xdc1455c4,0x64641e39,0xd83010c9,0x2d535ae0,\n      0x5b748f3e,0xf9a9146b,0x80f10294,0x2859acd4,\n      0x5fc846da,0x56d190e9,0x82167225,0x98e4daba,\n      0xbf7865f3,0x00da7ae4,0x9b7cd126,0x644172f8,\n      0xde40c78f,0xe8803efc,0xdd331a2b,0x48485c3c,\n      0x4ed01ddc,0x9c0b2d9e,0xb1c6e9d7,0xd797d43c,\n      0x274101ff,0x3bf7e127,0x91ebbc56,0x7ffeb321,\n      0x4d42096f,0xd6e9456a,0x0bade318,0x2f40ee0b,\n\n      0x38cebf03,0x0cbc2e72,0xbf03e704,0x7b3e7a9a,\n      0x8e985acd,0x90917617,0x413895f8,0xf11dde04,\n      0xc66f8244,0xe5648174,0x6c420271,0x2469d463,\n      0x2540b033,0xdc788e7b,0xe4140ded,0x0990630a,\n      0xa54abed4,0x6e124829,0xd940155a,0x1c8836f6,\n      0x38fda06c,0x5207ab69,0xf8be9342,0x774882a8,\n      0x56fc0d7e,0x53a99d6e,0x8241f634,0x9490954d,\n      0x447130aa,0x8cc4a81f,0x0868ec83,0xc22c642d,\n      0x47880140,0xfbff3bec,0x0f531f41,0xf845a667,\n      0x08c15fb7,0x1996cd81,0x86579103,0xe21dd863,\n      0x513d7f97,0x3984a1f1,0xdfcdc5f4,0x97766a5e,\n      0x37e2b1da,0x41441f3f,0xabd9ddba,0x23b755a9,\n      0xda937945,0x103e650e,0x3eef7c8f,0x2760ff8d,\n      0x2493a4cd,0x1d671225,0x3bf4bd4c,0xed6e1728,\n      0xc70e9e30,0x4e05e529,0x928d5aa6,0x164d0220,\n      0xb5184306,0x4bd7efb3,0x63830f11,0xf3a1526c,\n\n      0xf1545450,0xd41d5df5,0x25a5060d,0x77b368da,\n      0x4fe33c7e,0xeae09021,0xfdb053c4,0x2930f18d,\n      0xd37109ff,0x8511a781,0xc7e7cdd7,0x6aeabc45,\n      0xebbeaeaa,0x9a0c4f11,0xda252cbb,0x5b248f41,\n      0x5223b5eb,0xe32ab782,0x8e6a1c97,0x11d3f454,\n      0x3e05bd16,0x0059001d,0xce13ac97,0xf83b2b4c,\n      0x71db5c9a,0xdc8655a6,0x9e98597b,0x3fcae0a2,\n      0x75e63ccd,0x076c72df,0x4754c6ad,0x26b5627b,\n      0xd818c697,0x998d5f3d,0xe94fc7b2,0x1f49ad1a,\n      0xca7ff4ea,0x9fe72c05,0xfbd0cbbf,0xb0388ceb,\n      0xb76031e3,0xd0f53973,0xfb17907c,0xa4c4c10f,\n      0x9f2d8af9,0xca0e56b0,0xb0d9b689,0xfcbf37a3,\n      0xfede8f7d,0xf836511c,0x744003fc,0x89eba576,\n      0xcfdcf6a6,0xc2007f52,0xaaaf683f,0x62d2f9ca,\n      0xc996f77f,0x77a7b5b3,0x8ba7d0a4,0xef6a0819,\n      0xa0d903c0,0x01b27431,0x58fffd4c,0x4827f45c,\n\n      0x44eb5634,0xae70edfc,0x591c740b,0x478bf338,\n      0x2f3b513b,0x67bf518e,0x6fef4a0c,0x1e0b6917,\n      0x5ac0edc5,0x2e328498,0x077de7d5,0x5726020b,\n      0x2aeda888,0x45b637ca,0xcf60858d,0x3dc91ae2,\n      0x3e6d5294,0xe6900d39,0x0f634c71,0x827a5fa4,\n      0xc713994b,0x1c363494,0x3d43b615,0xe5fe7d15,\n      0xf6ada4f2,0x472099d5,0x04360d39,0x7f2a71d0,\n      0x88a4f5ff,0x2c28fac5,0x4cd64801,0xfd78dd33,\n      0xc9bdd233,0x21e266cc,0x9bbf419d,0xcbf7d81d,\n      0x80f15f96,0x04242657,0x53fb0f66,0xded11e46,\n      0xf2fdba97,0x8d45c9f1,0x4eeae802,0x17003659,\n      0xb9db81a7,0xe734b1b2,0x9503c54e,0xb7c77c3e,\n      0x271dd0ab,0xd8b906b5,0x0d540ec6,0xf03b86e0,\n      0x0fdb7d18,0x95e261af,0xad9ec04e,0x381f4a64,\n      0xfec798d7,0x09ea20be,0x0ef4ca57,0x1e6195bb,\n      0xfd0da78b,0xcea1653b,0x157d9777,0xf04af50f,\n\n      0xad7baa23,0xd181714a,0x9bbdab78,0x6c7d1577,\n      0x645eb1e7,0xa0648264,0x35839ca6,0x2287ef45,\n      0x32a64ca3,0x26111f6f,0x64814946,0xb0cddaf1,\n      0x4351c59e,0x1b30471c,0xb970788a,0x30e9f597,\n      0xd7e58df1,0xc6d2b953,0xf5f37cf4,0x3d7c419e,\n      0xf91ecb2d,0x9c87fd5d,0xb22384ce,0x8c7ac51c,\n      0x62c96801,0x57e54091,0x964536fe,0x13d3b189,\n      0x4afd1580,0xeba62239,0xb82ea667,0xae18d43a,\n      0xbef04402,0x1942534f,0xc54bf260,0x3c8267f5,\n      0xa1020ddd,0x112fcc8a,0xde596266,0xe91d0856,\n      0xf300c914,0xed84478e,0x5b65009e,0x4764da16,\n      0xaf8e07a2,0x4088dc2c,0x9a0cad41,0x2c3f179b,\n      0xa67b83f7,0xf27eab09,0xdbe10e28,0xf04c911f,\n      0xd1169f87,0x8e1e4976,0x17f57744,0xe4f5a33f,\n      0x27c2e04b,0x0b7523bd,0x07305776,0xc6be7503,\n      0x918fa7c9,0xaf2e2cd9,0x82046f8e,0xcc1c8250\n    };\n\n    uint8_t buf[BUFSIZE];\n    uint32_t saw[BUFSIZE];\n    for (int i=0; i<BUFSIZE; ++i)\n    {\n        buf[i] = i+128;\n        saw[i] = SpookyHashV2::Hash32(buf, i, 0);\n        if (saw[i] != expected[i])\n        {\n            printf(\"%3d: saw 0x%.8x, expected 0x%.8\" PRIx64 \"\\n\", i, saw[i],\n                   expected[i]);\n            failed = true;\n        }\n    }\n}\n#undef BUFSIZE\n\n\n#define NUMBUF (1<<10)\n#define BUFSIZE (1<<20)\nvoid DoTimingBig(int seed)\n{\n    printf(\"\\ntesting time to hash 2^^30 bytes ...\\n\");\n\n    char *buf[NUMBUF];\n    for (int i=0; i<NUMBUF; ++i)\n    {\n        buf[i] = (char *)malloc(BUFSIZE);\n        memset(buf[i], (char)seed, BUFSIZE);\n    }\n\n    uint64_t a = GetClockTickCount();\n    uint64_t hash1 = seed;\n    uint64_t hash2 = seed;\n    for (uint64_t i=0; i<NUMBUF; ++i)\n    {\n        SpookyHashV2::Hash128(buf[i], BUFSIZE, &hash1, &hash2);\n    }\n    uint64_t z = GetClockTickCount();\n    printf(\"SpookyHashV2::Hash128, uncached: time is \"\n           \"%4\" PRId64 \" milliseconds\\n\", z-a);\n\n    a = GetClockTickCount();\n    for (uint64_t i=0; i<NUMBUF; ++i)\n    {\n        Add(buf[i], BUFSIZE, &hash1, &hash2);\n    }\n    z = GetClockTickCount();\n    printf(\"Addition           , uncached: time is %4\" PRId64 \" milliseconds\\n\",\n           z-a);\n\n    a = GetClockTickCount();\n    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)\n    {\n        SpookyHashV2::Hash128(buf[0], 1024, &hash1, &hash2);\n    }\n    z = GetClockTickCount();\n    printf(\"SpookyHashV2::Hash128,   cached: time is \"\n           \"%4\" PRId64 \" milliseconds\\n\", z-a);\n\n    a = GetClockTickCount();\n    for (uint64_t i=0; i<NUMBUF*BUFSIZE/1024; ++i)\n    {\n        Add(buf[0], 1024, &hash1, &hash2);\n    }\n    z = GetClockTickCount();\n    printf(\"Addition           ,   cached: time is %4\" PRId64 \" milliseconds\\n\",\n           z-a);\n\n    for (int i=0; i<NUMBUF; ++i)\n    {\n        free(buf[i]);\n        buf[i] = nullptr;\n    }\n}\n#undef NUMBUF\n#undef BUFSIZE\n\n\n#define BUFSIZE (1<<14)\n#define NUMITER 10000000\nvoid DoTimingSmall(int seed)\n{\n    printf(\"\\ntesting timing of hashing up to %d cached aligned bytes %d \"\n           \"times ...\\n\", BUFSIZE, NUMITER);\n\n    uint64_t buf[BUFSIZE/8];\n    for (int i=0; i<BUFSIZE/8; ++i)\n    {\n        buf[i] = i+seed;\n    }\n\n    for (int i=1; i <= BUFSIZE; i <<= 1)\n    {\n        uint64_t a = GetClockTickCount();\n        uint64_t hash1 = seed;\n        uint64_t hash2 = seed+i;\n        for (int j=0; j<NUMITER; ++j)\n        {\n            SpookyHashV2::Hash128((char *)buf, i, &hash1, &hash2);\n        }\n        uint64_t z = GetClockTickCount();\n        printf(\"%d bytes: hash is %.16\" PRIx64 \" %.16\" PRIx64 \", \"\n               \"time is %\" PRId64 \"\\n\", i, hash1, hash2, z-a);\n    }\n}\n#undef BUFSIZE\n\n#define BUFSIZE 1024\nvoid TestAlignment()\n{\n    printf(\"\\ntesting alignment ...\\n\");\n\n    char buf[BUFSIZE];\n    uint64_t hash[8];\n    for (int i=0; i<BUFSIZE-16; ++i)\n    {\n        for (int j=0; j<8; ++j)\n        {\n            buf[j] = (char)i+j;\n            for (int k=1; k<=i; ++k)\n            {\n                buf[j+k] = k;\n            }\n            buf[j+i+1] = (char)i+j;\n            hash[j] = SpookyHashV2::Hash64((const void *)(buf+j+1), i, 0);\n        }\n        for (int j=1; j<8; ++j)\n        {\n            if (hash[0] != hash[j])\n            {\n                printf(\"alignment problems: %d %d\\n\", i, j);\n                failed = true;\n            }\n        }\n    }\n}\n#undef BUFSIZE\n\n// test that all deltas of one or two input bits affect all output bits\n#define BUFSIZE 256\n#define TRIES 50\n#define MEASURES 6\nvoid TestDeltas(int seed)\n{\n    printf(\"\\nall 1 or 2 bit input deltas get %d tries to flip every output \"\n           \"bit ...\\n\", TRIES);\n\n    Random random;\n    random.Init((uint64_t)seed);\n\n    // for messages 0..BUFSIZE-1 bytes\n    for (int h=0; h<BUFSIZE; ++h)\n    {\n        int maxk = 0;\n        // first bit to set\n        for (int i=0; i<h*8; ++i)\n        {\n            // second bit to set, or don't have a second bit\n            for (int j=0; j<=i; ++j)\n            {\n                uint64_t measure[MEASURES][2];\n                uint64_t counter[MEASURES][2];\n                for (int l=0; l<2; ++l)\n                {\n                    for (int m=0; m<MEASURES; ++m)\n                    {\n                        counter[m][l] = 0;\n                    }\n                }\n\n                // try to hit every output bit TRIES times\n                int k;\n                for (k=0; k<TRIES; ++k)\n                {\n                    uint8_t buf1[BUFSIZE];\n                    uint8_t buf2[BUFSIZE];\n                    int done = 1;\n                    for (int l=0; l<h; ++l)\n                    {\n                        buf1[l] = buf2[l] = random.Value();\n                    }\n                    buf1[i/8] ^= (1 << (i%8));\n                    if (j != i)\n                    {\n                        buf1[j/8] ^= (1 << (j%8));\n                    }\n                    SpookyHashV2::Hash128(buf1, h,\n                            &measure[0][0], &measure[0][1]);\n                    SpookyHashV2::Hash128(buf2, h,\n                            &measure[1][0], &measure[1][1]);\n                    for (int l=0; l<2; ++l) {\n                        measure[2][l] = measure[0][l] ^ measure[1][l];\n                        measure[3][l] = ~(measure[0][l] ^ measure[1][l]);\n                        measure[4][l] = measure[0][l] - measure[1][l];\n                        measure[4][l] ^= (measure[4][l]>>1);\n                        measure[5][l] = measure[0][l] + measure[1][l];\n                        measure[5][l] ^= (measure[4][l]>>1);\n                    }\n                    for (int l=0; l<2; ++l)\n                    {\n                        for (int m=0; m<MEASURES; ++m)\n                        {\n                            counter[m][l] |= measure[m][l];\n                            if (~counter[m][l]) {\n                              done = 0;\n                            }\n                        }\n                    }\n                    if (done) {\n                      break;\n                    }\n                }\n                if (k == TRIES)\n                {\n                    printf(\"failed %d %d %d\\n\", h, i, j);\n                    failed = true;\n                }\n                else if (k > maxk)\n                {\n                    maxk = k;\n                }\n            }\n        }\n        printf(\"passed for buffer size %d  max %d\\n\", h, maxk);\n    }\n}\n#undef BUFSIZE\n#undef TRIES\n#undef MEASURES\n\n\n// test that hashing pieces has the same behavior as hashing the whole\n#define BUFSIZE 1024\nvoid TestPieces()\n{\n    printf(\"\\ntesting pieces ...\\n\");\n    char buf[BUFSIZE];\n    for (int i=0; i<BUFSIZE; ++i)\n    {\n        buf[i] = i;\n    }\n    for (int i=0; i<BUFSIZE; ++i)\n    {\n        uint64_t a,b,c,d,seed1=1,seed2=2;\n        SpookyHashV2 state;\n\n        // all as one call\n        a = seed1;\n        b = seed2;\n        SpookyHashV2::Hash128(buf, i, &a, &b);\n\n        // all as one piece\n        c = 0xdeadbeefdeadbeef;\n        d = 0xbaceba11baceba11;\n        state.Init(seed1, seed2);\n        state.Update(buf, i);\n        state.Final(&c, &d);\n\n        if (a != c)\n        {\n            printf(\"wrong a %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\", i, a,c);\n            failed = true;\n        }\n        if (b != d)\n        {\n            printf(\"wrong b %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\", i, b,d);\n            failed = true;\n        }\n\n        // all possible two consecutive pieces\n        for (int j=0; j<i; ++j)\n        {\n            c = seed1;\n            d = seed2;\n            state.Init(c, d);\n            state.Update(&buf[0], j);\n            state.Update(&buf[j], i-j);\n            state.Final(&c, &d);\n            if (a != c)\n            {\n                printf(\"wrong a %d %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\",\n                       j, i, a,c);\n                failed = true;\n            }\n            if (b != d)\n            {\n                printf(\"wrong b %d %d: %.16\" PRIx64 \" %.16\" PRIx64 \"\\n\",\n                       j, i, b,d);\n                failed = true;\n            }\n        }\n    }\n}\n#undef BUFSIZE\n\nTEST(SpookyHashV2, Main) {\n    TestResults();\n    TestAlignment();\n    TestPieces();\n    DoTimingBig(1);\n    // tudorb@fb.com: Commented out slow tests\n#if 0\n    DoTimingSmall(argc);\n    TestDeltas(argc);\n#endif\n    CHECK_EQ(failed, 0);\n}\n\n// clang-format on\n"
  },
  {
    "path": "folly/hash/test/StdCompatibleHashTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <cstdint>\n#include <limits>\n#include <random>\n#include <string>\n#include <vector>\n\n#include <folly/hash/Hash.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly::hash;\n\nnamespace {\n\nstd::vector<uint8_t> randomBytes(\n    std::default_random_engine& rng, std::size_t n) {\n  std::vector<uint8_t> ret(n);\n  std::uniform_int_distribution<uint8_t> dist(\n      0, std::numeric_limits<uint8_t>::max());\n  std::generate(ret.begin(), ret.end(), [&]() { return dist(rng); });\n  return ret;\n}\n\n} // namespace\n\nTEST(StdCompatibleHashTest, Stress) {\n  constexpr std::size_t kMinLen = 1;\n  constexpr std::size_t kMaxLen = 2048;\n  constexpr std::size_t kEachLenAttempts = 16;\n\n  std::default_random_engine rng(0);\n\n  for (std::size_t len = kMinLen; len < kMaxLen; ++len) {\n    for (std::size_t attempt = 0; attempt < kEachLenAttempts; ++attempt) {\n      const std::vector<uint8_t> bytes = randomBytes(rng, len);\n      const std::string bytesAsStr{\n          reinterpret_cast<const char*>(bytes.data()), bytes.size()};\n      EXPECT_EQ(\n          stdCompatibleHash(bytesAsStr), std::hash<std::string>{}(bytesAsStr));\n    }\n  }\n}\n"
  },
  {
    "path": "folly/hash/test/UniqueHashKeyTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/UniqueHashKey.h>\n\n#include <folly/container/F14Map.h>\n#include <folly/container/MapUtil.h>\n#include <folly/portability/GTest.h>\n\ntemplate <typename Algo>\nstruct UniqueHashKeyTest : testing::TestWithParam<Algo> {};\nTYPED_TEST_SUITE_P(UniqueHashKeyTest);\n\nTYPED_TEST_P(UniqueHashKeyTest, Example) {\n  constexpr TypeParam algo{};\n  constexpr auto size = folly::unique_hash_key_algo_size_v<algo>;\n  using Key = folly::unique_hash_key<size>;\n  ASSERT_EQ(size, sizeof(Key));\n\n  using Map = folly::F14FastMap<Key, int>;\n\n  auto const key0 = Key(algo, \"hello \", \"world\");\n  auto const key1 = Key(algo, \"hello\", \" world\");\n\n  EXPECT_EQ(key0, key0);\n  EXPECT_EQ(key1, key1);\n  EXPECT_NE(key0, key1);\n  EXPECT_NE(key1, key0);\n\n  auto const key0_sp16 = std::span<uint16_t const>(key0);\n  auto const key1_sp16 = std::span<uint16_t const>(key1);\n  EXPECT_TRUE(std::ranges::equal(key0_sp16, key0_sp16));\n  EXPECT_TRUE(std::ranges::equal(key1_sp16, key1_sp16));\n  EXPECT_FALSE(std::ranges::equal(key0_sp16, key1_sp16));\n  EXPECT_FALSE(std::ranges::equal(key1_sp16, key0_sp16));\n\n  Map map;\n  map[key0] = 3;\n  map[key1] = 4;\n\n  EXPECT_EQ(3, folly::get_default(map, key0));\n  EXPECT_EQ(4, folly::get_default(map, key1));\n}\n\nREGISTER_TYPED_TEST_SUITE_P(UniqueHashKeyTest, Example);\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    SHA256_8, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_sha256_fn<8>);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    SHA256_16, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_sha256_fn<16>);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    SHA256_24, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_sha256_fn<24>);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    SHA256_32, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_sha256_fn<32>);\n\n#if __has_include(<blake3.h>)\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    BLAKE3_8, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_blake3_fn<8>);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    BLAKE3_16, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_blake3_fn<16>);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    BLAKE3_24, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_blake3_fn<24>);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    BLAKE3_32, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_strong_blake3_fn<32>);\n\n#if __has_include(<xxh3.h>)\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    XXH3_8, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_fast_xxh3_fn<8>);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    XXH3_16, //\n    UniqueHashKeyTest,\n    folly::unique_hash_key_algo_fast_xxh3_fn<16>);\n\n#endif // __has_include(<xxh3.h>)\n\n#endif // __has_include(<blake3.h>)\n"
  },
  {
    "path": "folly/hash/test/traits_test.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/hash/traits.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nnamespace {\n\nstruct HashableStruct1 {};\nstruct HashableStruct2 {};\nstruct UnhashableStruct {};\n\ntemplate <typename X, typename Y>\nstruct CompositeStruct {\n  X x;\n  Y y;\n};\n\n} // namespace\n\nnamespace std {\n\ntemplate <>\nstruct hash<HashableStruct1> {\n  [[maybe_unused]] size_t operator()(const HashableStruct1&) const noexcept {\n    return 0;\n  }\n};\n\ntemplate <>\nstruct hash<HashableStruct2> {\n  [[maybe_unused]] size_t operator()(const HashableStruct2&) const noexcept {\n    return 0;\n  }\n};\n\ntemplate <typename X, typename Y>\nstruct hash<enable_std_hash_helper<CompositeStruct<X, Y>, X, Y>> {\n  [[maybe_unused]] size_t operator()(\n      const CompositeStruct<X, Y>& value) const noexcept {\n    return std::hash<X>{}(value.x) + std::hash<Y>{}(value.y);\n  }\n};\n\n} // namespace std\n\nstatic_assert(is_hashable_v<HashableStruct1>);\nstatic_assert(is_hashable_v<HashableStruct2>);\nstatic_assert(!is_hashable_v<UnhashableStruct>);\nstatic_assert(is_hashable_v<CompositeStruct<HashableStruct1, HashableStruct1>>);\nstatic_assert(is_hashable_v<CompositeStruct<HashableStruct1, HashableStruct2>>);\nstatic_assert(\n    !is_hashable_v<CompositeStruct<HashableStruct1, UnhashableStruct>>);\n"
  },
  {
    "path": "folly/hash/traits.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <type_traits>\n\n#include <folly/Traits.h>\n\nnamespace folly {\n\n/**\n * Checks the requirements that the Hasher class must satisfy\n * in order to be used with the standard library containers,\n * for example `std::unordered_set<T, Hasher>`.\n */\ntemplate <typename T, typename Hasher>\nusing is_hasher_usable = std::integral_constant<\n    bool,\n    std::is_default_constructible_v<Hasher> &&\n        std::is_copy_constructible_v<Hasher> &&\n        std::is_move_constructible_v<Hasher> &&\n        std::is_invocable_r_v<size_t, Hasher, const T&>>;\n\n/**\n * Checks the requirements that the Hasher class must satisfy\n * in order to be used with the standard library containers,\n * for example `std::unordered_set<T, Hasher>`.\n */\ntemplate <typename T, typename Hasher>\ninline constexpr bool is_hasher_usable_v = is_hasher_usable<T, Hasher>::value;\n\n/**\n * Checks that the given hasher template's specialization for the given type\n * is usable with the standard library containters,\n * for example `std::unordered_set<T, Hasher<T>>`.\n */\ntemplate <typename T, template <typename U> typename Hasher = std::hash>\nusing is_hashable =\n    std::integral_constant<bool, is_hasher_usable_v<T, Hasher<T>>>;\n\n/**\n * Checks that the given hasher template's specialization for the given type\n * is usable with the standard library containters,\n * for example `std::unordered_set<T, Hasher<T>>`.\n */\ntemplate <typename T, template <typename U> typename Hasher = std::hash>\ninline constexpr bool is_hashable_v = is_hashable<T, Hasher>::value;\n\nnamespace detail {\n\ntemplate <typename T, typename>\nusing enable_hasher_helper_impl = T;\n\n} // namespace detail\n\n/**\n * A helper for defining partial specializations of a hasher class that rely\n * on other partial specializations of that hasher class being usable.\n *\n * Example:\n * ```\n * template <typename T>\n * struct hash<\n *     folly::enable_std_hash_helper<folly::Optional<T>, remove_const_t<T>>> {\n *   size_t operator()(folly::Optional<T> const& obj) const {\n *     return static_cast<bool>(obj) ? hash<remove_const_t<T>>()(*obj) : 0;\n *   }\n * };\n * ```\n */\ntemplate <\n    typename T,\n    template <typename U> typename Hasher,\n    typename... Dependencies>\nusing enable_hasher_helper = detail::enable_hasher_helper_impl<\n    T,\n    std::enable_if_t<\n        StrictConjunction<is_hashable<Dependencies, Hasher>...>::value>>;\n\n/**\n * A helper for defining partial specializations of a hasher class that rely\n * on other partial specializations of that hasher class being usable.\n *\n * Example:\n * ```\n * template <typename T>\n * struct hash<\n *     folly::enable_std_hash_helper<folly::Optional<T>, remove_const_t<T>>> {\n *   size_t operator()(folly::Optional<T> const& obj) const {\n *     return static_cast<bool>(obj) ? hash<remove_const_t<T>>()(*obj) : 0;\n *   }\n * };\n * ```\n */\ntemplate <typename T, typename... Dependencies>\nusing enable_std_hash_helper =\n    enable_hasher_helper<T, std::hash, Dependencies...>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/init/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n# xplat build rules\n\nnon_fbcode_target(\n    _kind = folly_xplat_library,\n    name = \"init\",\n    srcs = [\n        \"Init.cpp\",\n    ],\n    fbcode_deps = [\n        \"fbcode//folly/experimental/symbolizer:signal_handler\",\n    ],\n    raw_headers = [\n        \"Init.h\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/experimental/symbolizer:symbolizer\",\n        \"fbsource//xplat/folly/logging:init\",\n        \"fbsource//xplat/folly/portability:config\",\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"fbsource//xplat/folly/portability:sys_syscall\",\n        \":phase\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly:portability\",\n        \"//xplat/folly:singleton\",\n        # \"fbsource//xplat/folly/synchronization:hazptr_thread_pool_executor\",\n        \"//xplat/folly/synchronization:hazptr_thread_pool_executor\",\n    ],\n)\n\n# fbcode build rules\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"init\",\n    srcs = [\"Init.cpp\"],\n    headers = [\"Init.h\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":phase\",\n        \"//folly:singleton\",\n        \"//folly/experimental/symbolizer:signal_handler\",\n        \"//folly/logging:init\",\n        \"//folly/portability:config\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:hazptr_thread_pool_executor\",\n    ],\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"phase\",\n    srcs = [\"Phase.cpp\"],\n    headers = [\"Phase.h\"],\n    xplat_impl = folly_xplat_library,\n)\n"
  },
  {
    "path": "folly/init/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME init\n  SRCS\n    Init.cpp\n  HEADERS\n    Init.h\n  DEPS\n    folly_experimental_symbolizer_signal_handler\n    folly_init_phase\n    folly_logging_init\n    folly_portability_config\n    folly_portability_gflags\n    folly_singleton\n    folly_synchronization_hazptr_thread_pool_executor\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME phase\n  SRCS\n    Phase.cpp\n  HEADERS\n    Phase.h\n)\n"
  },
  {
    "path": "folly/init/Init.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/init/Init.h>\n\n#include <cstdlib>\n\n#include <glog/logging.h>\n\n#include <folly/Singleton.h>\n#include <folly/experimental/symbolizer/SignalHandler.h>\n#include <folly/init/Phase.h>\n#include <folly/logging/Init.h>\n#include <folly/portability/Config.h>\n#include <folly/portability/GFlags.h>\n#include <folly/synchronization/HazptrThreadPoolExecutor.h>\n\nFOLLY_GFLAGS_DEFINE_string(logging, \"\", \"Logging configuration\");\n\nnamespace folly {\n\nInitOptions::InitOptions() noexcept\n    : fatal_signals(symbolizer::kAllFatalSignals) {}\n\nnamespace {\n\n#if FOLLY_USE_SYMBOLIZER\n// Newer versions of glog require the function passed to InstallFailureFunction\n// to be noreturn. But glog spells that in multiple possible ways, depending on\n// platform. But glog's choice of spelling does not match how the\n// noreturn-ability of std::abort is spelled. Some compilers consider this a\n// type mismatch on the function-ptr type. To fix the type mismatch, we wrap\n// std::abort and mimic the condition and the spellings from glog here.\n#if defined(__GNUC__)\n__attribute__((noreturn))\n#else\n[[noreturn]]\n#endif\nstatic void wrapped_abort() {\n  abort();\n}\n#endif\n\nvoid initImpl(int* argc, char*** argv, InitOptions options) {\n#if !defined(_WIN32)\n  // Install the handler now, to trap errors received during startup.\n  // The callbacks, if any, can be installed later\n  folly::symbolizer::installFatalSignalHandler(options.fatal_signals);\n#endif\n\n  // Indicate ProcessPhase::Regular and register handler to\n  // indicate ProcessPhase::Exit.\n  folly::set_process_phases();\n\n  // Move from the registration phase to the \"you can actually instantiate\n  // things now\" phase.\n  folly::SingletonVault::singleton()->registrationComplete();\n\n#if !FOLLY_HAVE_LIBGFLAGS\n  (void)options;\n#else\n  if (options.use_gflags) {\n    folly::gflags::ParseCommandLineFlags(argc, argv, options.remove_flags);\n  }\n#endif\n\n  auto const follyLoggingEnv = std::getenv(kLoggingEnvVarName);\n  auto const follyLoggingEnvOr = follyLoggingEnv ? follyLoggingEnv : \"\";\n  folly::initLoggingOrDie({follyLoggingEnvOr, FLAGS_logging});\n  auto programName = argc && argv && *argc > 0 ? (*argv)[0] : \"unknown\";\n  google::InitGoogleLogging(programName);\n\n#if FOLLY_USE_SYMBOLIZER\n  // Don't use glog's DumpStackTraceAndExit; rely on our signal handler.\n  google::InstallFailureFunction(wrapped_abort);\n\n  if (options.install_fatal_signal_callbacks) {\n    // Actually install the callbacks into the handler.\n    folly::symbolizer::installFatalSignalCallbacks();\n  }\n#endif\n  // Set the default hazard pointer domain to use a thread pool executor\n  // for asynchronous reclamation\n  folly::enable_hazptr_thread_pool_executor();\n}\n\n} // namespace\n\nInit::Init(int* argc, char*** argv, bool removeFlags) {\n  initImpl(argc, argv, InitOptions{}.removeFlags(removeFlags));\n}\n\nInit::Init(int* argc, char*** argv, InitOptions options) {\n  initImpl(argc, argv, options);\n}\n\nInit::~Init() {\n  SingletonVault::singleton()->destroyInstancesFinal();\n}\n\nvoid unsafe_unscoped_init(int* argc, char*** argv, bool removeFlags) {\n  initImpl(argc, argv, InitOptions{}.removeFlags(removeFlags));\n}\n\nvoid unsafe_unscoped_init(int* argc, char*** argv, InitOptions options) {\n  initImpl(argc, argv, options);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/init/Init.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <bitset>\n\n#include <folly/Portability.h>\n\nnamespace folly {\n\nconstexpr char const* kLoggingEnvVarName = \"FOLLY_LOGGING\";\n\nclass InitOptions {\n public:\n  InitOptions() noexcept;\n\n  bool remove_flags{true};\n  bool use_gflags{true};\n  // Controls whether folly::symbolizer::installFatalSignalCallbacks() is called\n  // during init.\n  bool install_fatal_signal_callbacks{true};\n\n  // mask of all fatal (default handler of terminating the process) signals for\n  // which `init()` will install handler that print stack traces and invokes\n  // previously established handler  (or terminate if there were none).\n  // Signals that are not in `symbolizer::kAllFatalSignals` will be ignored\n  // if passed here\n  // Defaults to all signal in `symbolizer::kAllFatalSignals`\n  std::bitset<64> fatal_signals;\n\n  InitOptions& removeFlags(bool remove) {\n    remove_flags = remove;\n    return *this;\n  }\n\n  InitOptions& fatalSignals(unsigned long val) {\n    fatal_signals = val;\n    return *this;\n  }\n\n  InitOptions& useGFlags(bool useGFlags) {\n    use_gflags = useGFlags;\n    return *this;\n  }\n\n  InitOptions& installFatalSignalCallbacks(bool installFatalSignalCallbacks) {\n    install_fatal_signal_callbacks = installFatalSignalCallbacks;\n    return *this;\n  }\n};\n\n/*\n * An RAII object to be constructed at the beginning of main() and destructed\n * implicitly at the end of main().\n *\n * The constructor calls common init functions in the necessary order\n * Among other things, this ensures that folly::Singletons are initialized\n * correctly and installs signal handlers for a superior debugging experience.\n * It also initializes gflags and glog.\n *\n * The destructor destroys all singletons managed by folly::Singleton, yielding\n * better shutdown behavior when performed at the end of main(). In particular,\n * this guarantees that all singletons managed by folly::Singleton are destroyed\n * before all Meyers singletons are destroyed.\n *\n * @param argc, argv   arguments to your main\n * @param removeFlags  if true, will update argc,argv to remove recognized\n *                     gflags passed on the command line\n * @param options      options\n */\nclass [[nodiscard]] Init {\n public:\n  // Force ctor & dtor out of line for better stack traces even with LTO.\n  FOLLY_NOINLINE Init(int* argc, char*** argv, bool removeFlags = true);\n\n  FOLLY_NOINLINE Init(int* argc, char*** argv, InitOptions options);\n\n  FOLLY_NOINLINE ~Init();\n\n  Init(Init const&) = delete;\n  Init(Init&&) = delete;\n  Init& operator=(Init const&) = delete;\n  Init& operator=(Init&&) = delete;\n};\n\nvoid unsafe_unscoped_init(int* argc, char*** argv, bool removeFlags = true);\nvoid unsafe_unscoped_init(int* argc, char*** argv, InitOptions options);\n\n[[deprecated(\"Use the RAII version Init\")]] inline void init(\n    int* argc, char*** argv, bool removeFlags = true) {\n  unsafe_unscoped_init(argc, argv, removeFlags);\n}\n[[deprecated(\"Use the RAII version Init\")]] inline void init(\n    int* argc, char*** argv, InitOptions options) {\n  unsafe_unscoped_init(argc, argv, std::move(options));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/init/Phase.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/init/Phase.h>\n\n#include <atomic>\n#include <cstdlib>\n#include <stdexcept>\n\nnamespace folly {\n\nnamespace {\n\nstatic std::atomic<ProcessPhase> process_phase{ProcessPhase::Init};\n\nvoid set_process_phase(ProcessPhase newphase) {\n  ProcessPhase curphase = get_process_phase();\n  if (curphase == ProcessPhase::Exit || int(newphase) - int(curphase) != 1) {\n    throw std::logic_error(\"folly-init: unexpected process-phase transition\");\n  }\n  process_phase.store(newphase, std::memory_order_relaxed);\n}\n\n} // namespace\n\nvoid set_process_phases() {\n  set_process_phase(ProcessPhase::Regular);\n  std::atexit([]() { set_process_phase(ProcessPhase::Exit); });\n}\n\nProcessPhase get_process_phase() noexcept {\n  return process_phase.load(std::memory_order_relaxed);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/init/Phase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n\nnamespace folly {\n\n/// Process phases for programs that use Folly:\n/// - Init: Not all globals may have been initialized.\n/// - Regular: All globals have been initialized and have not\n///   been destroyed.\n/// - Exit: Some globals may have been destroyed.\n\n/// Process phases\nenum class ProcessPhase {\n  Init = 0,\n  Regular = 1,\n  Exit = 2,\n};\n\n/// Start Regular phase and register handler to set Exit phase.\n/// To be called exactly once in each program that uses Folly.\n/// Ideally, it is to be called from folly::init(), which in turn\n/// is to be called by every program that uses Folly.\nvoid set_process_phases();\n\n/// Get the current process phase.\nProcessPhase get_process_phase() noexcept;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/init/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"phase_test\",\n    srcs = [\"PhaseTest.cpp\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:singleton\",\n        \"//folly/init:phase\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/init/test/PhaseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/init/Phase.h>\n\n#include <thread>\n\n#include <glog/logging.h>\n\n#include <folly/Singleton.h>\n#include <folly/portability/GTest.h>\n\n/// Types\n\nstruct Global {\n  Global() { CHECK(folly::get_process_phase() == folly::ProcessPhase::Init); }\n\n  ~Global() { CHECK(folly::get_process_phase() >= folly::ProcessPhase::Exit); }\n};\n\n/// Variables\n\nstatic Global global;\n\n/// Tests\n\nTEST(InitTest, basic) {\n  ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);\n}\n\nTEST(InitTest, fork) {\n  std::thread t1([] {\n    ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);\n  });\n  t1.join();\n  folly::SingletonVault::singleton()->destroyInstances();\n  auto pid = fork();\n  folly::SingletonVault::singleton()->reenableInstances();\n  if (pid > 0) {\n    // parent\n    int status = -1;\n    auto pid2 = waitpid(pid, &status, 0);\n    EXPECT_EQ(status, 0);\n    EXPECT_EQ(pid, pid2);\n    ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);\n  } else if (pid == 0) {\n    // child\n    std::thread t2([] {\n      ASSERT_EQ(folly::get_process_phase(), folly::ProcessPhase::Regular);\n    });\n    t2.join();\n    std::exit(0); // Do not print gtest results\n  } else {\n    PLOG(FATAL) << \"Failed to fork()\";\n  }\n}\n"
  },
  {
    "path": "folly/io/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../defs.bzl\", \"folly_xplat_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"iobuf\",\n    srcs = [\n        \"Cursor.cpp\",\n        \"IOBuf.cpp\",\n        \"IOBufIovecBuilder.cpp\",\n        \"IOBufQueue.cpp\",\n    ],\n    headers = [\n        \"Cursor.h\",\n        \"Cursor-inl.h\",\n        \"IOBuf.h\",\n        \"IOBufIovecBuilder.h\",\n        \"IOBufQueue.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:conv\",\n        \"//folly/hash:spooky_hash_v2\",\n        \"//folly/lang:align\",\n        \"//folly/lang:hint\",\n        \"//folly/memory:sanitize_address\",\n        \"//folly/portability:iovec\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:fbstring\",\n        \"//folly:fbvector\",\n        \"//folly:function\",\n        \"//folly:likely\",\n        \"//folly:memory\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly:scope_guard\",\n        \"//folly/container:span\",\n        \"//folly/detail:iterators\",\n        \"//folly/lang:bits\",\n        \"//folly/lang:checked_math\",\n        \"//folly/lang:exception\",\n        \"//folly/lang:ordering\",\n        \"//folly/memory:malloc\",\n        \"//folly/memory:memory_resource\",\n        \"//folly/portability:sys_uio\",\n        \"//folly/synchronization:micro_spin_lock\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"global_shutdown_socket_set\",\n    srcs = [\"GlobalShutdownSocketSet.cpp\"],\n    headers = [\"GlobalShutdownSocketSet.h\"],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:singleton\",\n    ],\n    exported_deps = [\n        \":shutdown_socket_set\",\n        \"//folly/concurrency/memory:read_mostly_shared_ptr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"record_io\",\n    srcs = [\n        \"RecordIO.cpp\",\n    ],\n    headers = [\n        \"RecordIO.h\",\n        \"RecordIO-inl.h\",\n    ],\n    supports_python_dlopen = True,\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:exception\",\n        \"//folly:file_util\",\n        \"//folly:memory\",\n        \"//folly:string\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \":iobuf\",\n        \"//folly:file\",\n        \"//folly:range\",\n        \"//folly/detail:iterators\",\n        \"//folly/hash:spooky_hash_v2\",\n        \"//folly/system:memory_mapping\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"shutdown_socket_set\",\n    srcs = [\"ShutdownSocketSet.cpp\"],\n    headers = [\"ShutdownSocketSet.h\"],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:file_util\",\n        \"//folly/net:net_ops\",\n        \"//folly/portability:sockets\",\n    ],\n    exported_deps = [\n        \"//folly:file\",\n        \"//folly/net:network_socket\",\n        \"//folly/synchronization:relaxed_atomic\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"typed_io_buf\",\n    headers = [\"TypedIOBuf.h\"],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \":iobuf\",\n        \"//folly/memory:malloc\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"socket_option_map\",\n    srcs = [\n        \"SocketOptionMap.cpp\",\n        \"SocketOptionValue.cpp\",\n    ],\n    headers = [\n        \"SocketOptionMap.h\",\n        \"SocketOptionValue.h\",\n    ],\n    xplat_impl = folly_xplat_library,\n    exported_deps = [\n        \"//folly/net:network_socket\",\n        \"//folly/portability:sockets\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"huge_pages\",\n    srcs = [\"HugePages.cpp\"],\n    headers = [\"HugePages.h\"],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:conv\",\n        \"//folly:format\",\n        \"//folly:string\",\n        \"//folly/gen:base\",\n        \"//folly/gen:file\",\n        \"//folly/gen:string\",\n        \"//folly/portability:windows\",\n    ],\n    exported_deps = [\n        \"//folly:range\",\n        \"//folly/io:fs_util\",\n        \"//folly/portability:unistd\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_regex\"),\n        \"boost\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"fs_util\",\n    srcs = [\"FsUtil.cpp\"],\n    headers = [\"FsUtil.h\"],\n    xplat_impl = folly_xplat_library,\n    deps = [\n        \"//folly:exception\",\n        \"//folly/portability:windows\",\n    ],\n    external_deps = [(\"boost\", None, \"boost_filesystem\")],\n    exported_external_deps = [\n        (\"boost\", None, \"boost_filesystem\"),\n    ],\n)\n"
  },
  {
    "path": "folly/io/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME fs_util\n  SRCS\n    FsUtil.cpp\n  HEADERS\n    FsUtil.h\n  DEPS\n    folly_exception\n    folly_portability_windows\n  EXTERNAL_DEPS\n    Boost::filesystem\n)\n\nfolly_add_library(\n  NAME global_shutdown_socket_set\n  SRCS\n    GlobalShutdownSocketSet.cpp\n  HEADERS\n    GlobalShutdownSocketSet.h\n  DEPS\n    folly_singleton\n  EXPORTED_DEPS\n    folly_concurrency_memory_read_mostly_shared_ptr\n    folly_io_shutdown_socket_set\n)\n\nfolly_add_library(\n  NAME huge_pages\n  SRCS\n    HugePages.cpp\n  HEADERS\n    HugePages.h\n  DEPS\n    folly_conv\n    folly_format\n    folly_gen_base\n    folly_gen_file\n    folly_gen_string\n    folly_portability_windows\n    folly_string\n  EXPORTED_DEPS\n    folly_io_fs_util\n    folly_portability_unistd\n    folly_range\n  EXTERNAL_DEPS\n    Boost::headers\n    Boost::regex\n)\n\nfolly_add_library(\n  NAME iobuf\n  SRCS\n    Cursor.cpp\n    IOBuf.cpp\n    IOBufIovecBuilder.cpp\n    IOBufQueue.cpp\n  HEADERS\n    Cursor-inl.h\n    Cursor.h\n    IOBuf.h\n    IOBufIovecBuilder.h\n    IOBufQueue.h\n  DEPS\n    folly_conv\n    folly_hash_spooky_hash_v2\n    folly_lang_align\n    folly_lang_hint\n    folly_memory_sanitize_address\n    folly_portability_iovec\n  EXPORTED_DEPS\n    folly_container_span\n    folly_detail_iterators\n    folly_fbstring\n    folly_fbvector\n    folly_function\n    folly_lang_bits\n    folly_lang_checked_math\n    folly_lang_exception\n    folly_lang_ordering\n    folly_likely\n    folly_memory\n    folly_memory_malloc\n    folly_memory_memory_resource\n    folly_portability\n    folly_portability_sys_uio\n    folly_range\n    folly_scope_guard\n    folly_synchronization_micro_spin_lock\n)\n\nfolly_add_library(\n  NAME record_io\n  SRCS\n    RecordIO.cpp\n  HEADERS\n    RecordIO-inl.h\n    RecordIO.h\n  DEPS\n    folly_exception\n    folly_file_util\n    folly_memory\n    folly_portability_unistd\n    folly_string\n  EXPORTED_DEPS\n    folly_detail_iterators\n    folly_file\n    folly_hash_spooky_hash_v2\n    folly_io_iobuf\n    folly_range\n    folly_system_memory_mapping\n)\n\nfolly_add_library(\n  NAME shutdown_socket_set\n  SRCS\n    ShutdownSocketSet.cpp\n  HEADERS\n    ShutdownSocketSet.h\n  DEPS\n    folly_file_util\n    folly_net_net_ops\n    folly_portability_sockets\n  EXPORTED_DEPS\n    folly_file\n    folly_net_network_socket\n    folly_synchronization_relaxed_atomic\n)\n\nfolly_add_library(\n  NAME socket_option_map\n  SRCS\n    SocketOptionMap.cpp\n    SocketOptionValue.cpp\n  HEADERS\n    SocketOptionMap.h\n    SocketOptionValue.h\n  EXPORTED_DEPS\n    folly_net_network_socket\n    folly_portability_sockets\n)\n\nfolly_add_library(\n  NAME typed_io_buf\n  HEADERS\n    TypedIOBuf.h\n  EXPORTED_DEPS\n    folly_io_iobuf\n    folly_memory_malloc\n)\n\nadd_subdirectory(async)\nadd_subdirectory(coro)\n"
  },
  {
    "path": "folly/io/Cursor-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly {\nnamespace io {\n\n/*\n * Helper classes for use with CursorBase::readWhile()\n */\nclass CursorStringAppender {\n public:\n  void append(ByteRange bytes) {\n    str_.append(reinterpret_cast<char const*>(bytes.data()), bytes.size());\n  }\n  std::string extractString() { return std::move(str_); }\n\n private:\n  std::string str_;\n};\n\nclass CursorNoopAppender {\n public:\n  void append(ByteRange) {}\n};\n\ntemplate <class Derived, class BufType>\nstd::string CursorBase<Derived, BufType>::readTerminatedString(\n    char termChar, size_t maxLength) {\n  size_t bytesRead{0};\n  auto keepReading = [&bytesRead, termChar, maxLength](uint8_t byte) {\n    if (byte == termChar) {\n      return false;\n    }\n    ++bytesRead;\n    if (bytesRead >= maxLength) {\n      throw std::length_error(\"string overflow\");\n    }\n    return true;\n  };\n\n  auto result = readWhile(keepReading);\n  // skip over the terminator character\n  if (isAtEnd()) {\n    throw_exception<std::out_of_range>(\"terminator not found\");\n  }\n  skip(1);\n\n  return result;\n}\n\ntemplate <class Derived, class BufType>\ntemplate <typename Predicate>\nstd::string CursorBase<Derived, BufType>::readWhile(\n    const Predicate& predicate) {\n  CursorStringAppender s;\n  readWhile(predicate, s);\n  return s.extractString();\n}\n\ntemplate <class Derived, class BufType>\ntemplate <typename Predicate, typename Output>\nvoid CursorBase<Derived, BufType>::readWhile(\n    const Predicate& predicate, Output& out) {\n  while (true) {\n    auto peeked = peekBytes();\n    if (peeked.empty()) {\n      return;\n    }\n    for (size_t idx = 0; idx < peeked.size(); ++idx) {\n      if (!predicate(peeked[idx])) {\n        peeked.reset(peeked.data(), idx);\n        out.append(peeked);\n        skip(idx);\n        return;\n      }\n    }\n    out.append(peeked);\n    skip(peeked.size());\n  }\n}\n\ntemplate <class Derived, class BufType>\ntemplate <typename Predicate>\nvoid CursorBase<Derived, BufType>::skipWhile(const Predicate& predicate) {\n  CursorNoopAppender appender;\n  readWhile(predicate, appender);\n}\n\n} // namespace io\n} // namespace folly\n"
  },
  {
    "path": "folly/io/Cursor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/Cursor.h>\n\n#include <cstdio>\n\n#include <folly/ScopeGuard.h>\n\nnamespace folly {\nnamespace io {\n\nstatic_assert(kIsWindows || is_register_pass_v<ThinCursor>);\n\nvoid Appender::printf(const char* fmt, ...) {\n  va_list ap;\n  va_start(ap, fmt);\n  vprintf(fmt, ap);\n  va_end(ap);\n}\n\nvoid Appender::vprintf(const char* fmt, va_list ap) {\n  // Make a copy of ap in case we need to retry.\n  // We use ap on the first attempt, so it always gets advanced\n  // passed the used arguments.  We'll only use apCopy if we need to retry.\n  va_list apCopy;\n  va_copy(apCopy, ap);\n  SCOPE_EXIT {\n    va_end(apCopy);\n  };\n\n  // First try writing into our available data space.\n  int ret =\n      vsnprintf(reinterpret_cast<char*>(writableData()), length(), fmt, ap);\n  if (ret < 0) {\n    throw std::runtime_error(\"error formatting printf() data\");\n  }\n  auto len = size_t(ret);\n  // vsnprintf() returns the number of characters that would be printed,\n  // not including the terminating nul.\n  if (len < length()) {\n    // All of the data was successfully written.\n    append(len);\n    return;\n  }\n\n  // There wasn't enough room for the data.\n  // Allocate more room, and then retry.\n  ensure(len + 1);\n  ret =\n      vsnprintf(reinterpret_cast<char*>(writableData()), length(), fmt, apCopy);\n  if (ret < 0) {\n    throw std::runtime_error(\"error formatting printf() data\");\n  }\n  len = size_t(ret);\n  if (len >= length()) {\n    // This shouldn't ever happen.\n    throw std::runtime_error(\n        \"unexpectedly out of buffer space on second \"\n        \"vsnprintf() attmept\");\n  }\n  append(len);\n}\n} // namespace io\n} // namespace folly\n"
  },
  {
    "path": "folly/io/Cursor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstdarg>\n#include <cstdint>\n#include <cstring>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n\n#include <folly/Likely.h>\n#include <folly/Memory.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/container/span.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/IOBufQueue.h>\n#include <folly/lang/Bits.h>\n#include <folly/lang/Exception.h>\n\n/**\n * IOBuf Cursors provide fast iteration over IOBuf chains.\n *\n * - Cursor          - Read-only access\n * - RWPrivateCursor - Read-write access, assumes private access to IOBuf chain\n * - RWUnshareCursor - Read-write access, calls unshare on write (COW)\n * - Appender        - Write access, assumes private access to IOBuf chain\n *\n * Note that RW cursors write in the preallocated part of buffers (that is,\n * between the buffer's data() and tail()), while Appenders append to the end\n * of the buffer (between the buffer's tail() and bufferEnd()).  Appenders\n * automatically adjust the buffer pointers, so you may only use one\n * Appender with a buffer chain; for this reason, Appenders assume private\n * access to the buffer (you need to call unshare() yourself if necessary).\n *\n * @file Cursor.h\n */\n\nnamespace folly {\nnamespace io {\n\nclass Cursor;\nclass ThinCursor;\n\n// This is very useful in development, but the size perturbation is currently\n// causing some previously undetected bugs in unrelated projects to manifest in\n// CI-breaking ways.\n// TODO(davidgoldblatt): Fix this.\n#define FOLLY_IO_CURSOR_BORROW_CHECKING 0\n#if FOLLY_IO_CURSOR_BORROW_CHECKING\n#define FOLLY_IO_CURSOR_BORROW_DCHECK DCHECK\n#else\n#define FOLLY_IO_CURSOR_BORROW_DCHECK(ignored)\n#endif\n\ntemplate <class Derived, class BufType>\nclass CursorBase {\n  // Make all the templated classes friends for copy constructor.\n  template <class D, typename B>\n  friend class CursorBase;\n\n protected:\n  /**\n   * Construct a cursor wrapping an IOBuf.\n   */\n  explicit CursorBase(BufType* buf) noexcept : crtBuf_(buf), buffer_(buf) {\n    if (crtBuf_) {\n      crtPos_ = crtBegin_ = crtBuf_->data();\n      crtEnd_ = crtBuf_->tail();\n    }\n  }\n\n  /**\n   * Constuct a bounded cursor wrapping an IOBuf.\n   *\n   * @param len An upper bound on the number of bytes available to this cursor.\n   */\n  CursorBase(BufType* buf, size_t len) noexcept : crtBuf_(buf), buffer_(buf) {\n    if (crtBuf_) {\n      crtPos_ = crtBegin_ = crtBuf_->data();\n      crtEnd_ = crtBuf_->tail();\n      if (uintptr_t(crtPos_) + len < uintptr_t(crtEnd_)) {\n        crtEnd_ = crtPos_ + len;\n      }\n      remainingLen_ = len - (crtEnd_ - crtPos_);\n    }\n  }\n\n  /**\n   * Copy constructor.\n   *\n   * This also allows constructing a CursorBase from other derived types.\n   * For instance, this allows constructing a Cursor from an RWPrivateCursor.\n   */\n  template <class OtherDerived, class OtherBuf>\n  explicit CursorBase(const CursorBase<OtherDerived, OtherBuf>& cursor) noexcept\n      : crtBuf_(cursor.crtBuf_),\n        buffer_(cursor.buffer_),\n        crtBegin_(cursor.crtBegin_),\n        crtEnd_(cursor.crtEnd_),\n        crtPos_(cursor.crtPos_),\n        absolutePos_(cursor.absolutePos_),\n        remainingLen_(cursor.remainingLen_) {}\n\n  template <class OtherDerived, class OtherBuf>\n  explicit CursorBase(\n      const CursorBase<OtherDerived, OtherBuf>& cursor, size_t len)\n      : crtBuf_(cursor.crtBuf_),\n        buffer_(cursor.buffer_),\n        crtBegin_(cursor.crtBegin_),\n        crtEnd_(cursor.crtEnd_),\n        crtPos_(cursor.crtPos_),\n        absolutePos_(cursor.absolutePos_) {\n    if (cursor.isBounded() && len > cursor.remainingLen_ + cursor.length()) {\n      throw_exception<std::out_of_range>(\"underflow\");\n    }\n    if (uintptr_t(crtPos_) + len < uintptr_t(crtEnd_)) {\n      crtEnd_ = crtPos_ + len;\n    }\n    remainingLen_ = len - (crtEnd_ - crtPos_);\n  }\n\n public:\n  /**\n   * Reset cursor to point to a new buffer.\n   *\n   * @methodset Modifiers\n   */\n  void reset(BufType* buf) noexcept {\n    crtBuf_ = buf;\n    buffer_ = buf;\n    absolutePos_ = 0;\n    remainingLen_ = std::numeric_limits<size_t>::max();\n    if (crtBuf_) {\n      crtPos_ = crtBegin_ = crtBuf_->data();\n      crtEnd_ = crtBuf_->tail();\n    }\n  }\n\n  /**\n   * Get the Cursor position relative to the IOBuf chain's currentBuffer().\n   *\n   * @methodset Advanced\n   */\n  size_t getPositionInCurrentBuffer() const noexcept {\n    dcheckIntegrity();\n    return crtPos_ - crtBegin_;\n  }\n\n  /**\n   * Get the current Cursor position relative to the head of IOBuf chain.\n   *\n   * @methodset Capacity\n   */\n  size_t getCurrentPosition() const noexcept {\n    return getPositionInCurrentBuffer() + absolutePos_;\n  }\n\n  /**\n   * Get the data at the current cursor position.\n   *\n   * @methodset Accessors\n   */\n  const uint8_t* data() const noexcept {\n    dcheckIntegrity();\n    return crtPos_;\n  }\n\n  /**\n   * Get the buffer in the IOBuf chain this Cursor currently points into.\n   *\n   * @methodset Advanced\n   */\n  const folly::IOBuf* currentBuffer() const noexcept { return crtBuf_; }\n\n  /**\n   * Return the remaining space available in the current IOBuf.\n   *\n   * @methodset Capacity\n   *\n   * May return 0 if the cursor is at the end of an IOBuf.  Use peekBytes()\n   * instead if you want to avoid this.  peekBytes() will advance to the next\n   * non-empty IOBuf (up to the end of the chain) if the cursor is currently\n   * pointing at the end of a buffer.\n   */\n  size_t length() const noexcept {\n    dcheckIntegrity();\n    return crtEnd_ - crtPos_;\n  }\n\n  /**\n   * Return the space available until the end of the entire IOBuf chain.\n   *\n   * @methodset Capacity\n   *\n   * For bounded Cursors, return the available space until the boundary.\n   */\n  size_t totalLength() const noexcept {\n    size_t len = 0;\n    const IOBuf* buf = crtBuf_->next();\n    while (buf != buffer_ && len < remainingLen_) {\n      len += buf->length();\n      buf = buf->next();\n    }\n    return std::min(len, remainingLen_) + length();\n  }\n\n  /**\n   * Return true if the cursor could advance the specified number of bytes\n   * from its current position.\n   *\n   * @methodset Capacity\n   *\n   * This is useful for applications that want to do checked reads instead of\n   * catching exceptions and is more efficient than using totalLength as it\n   * walks the minimal set of buffers in the chain to determine the result.\n   */\n  bool canAdvance(size_t amount) const noexcept {\n    if (isBounded() && amount > remainingLen_ + length()) {\n      return false;\n    }\n    const IOBuf* nextBuf = crtBuf_;\n    size_t available = length();\n    do {\n      if (available >= amount) {\n        return true;\n      }\n      amount -= available;\n      nextBuf = nextBuf->next();\n      available = nextBuf->length();\n    } while (nextBuf != buffer_);\n    return false;\n  }\n\n  /**\n   * Return true if the cursor is at the end of the entire IOBuf chain.\n   *\n   * @methodset Capacity\n   */\n  bool isAtEnd() const noexcept {\n    dcheckIntegrity();\n    // Check for the simple cases first.\n    if (crtPos_ != crtEnd_) {\n      return false;\n    }\n    if (crtBuf_ == buffer_->prev()) {\n      return true;\n    }\n    if (isBounded() && remainingLen_ == 0) {\n      return true;\n    }\n    // We are at the end of a buffer, but it isn't the last buffer.\n    // We might still be at the end if the remaining buffers in the chain are\n    // empty.\n    const IOBuf* buf = crtBuf_->next();\n    while (buf != buffer_) {\n      if (buf->length() > 0) {\n        return false;\n      }\n      buf = buf->next();\n    }\n    return true;\n  }\n\n  /**\n   * Advances the cursor to the end of the entire IOBuf chain.\n   *\n   * @methodset Modifiers\n   */\n  void advanceToEnd() {\n    // Simple case, we're already in the last IOBuf.\n    if (crtBuf_ == buffer_->prev()) {\n      crtPos_ = crtEnd_;\n      return;\n    }\n\n    auto* nextBuf = crtBuf_->next();\n    while (nextBuf != buffer_) {\n      if (isBounded() && remainingLen_ == 0) {\n        crtPos_ = crtEnd_;\n        return;\n      }\n      absolutePos_ += crtEnd_ - crtBegin_;\n\n      crtBuf_ = nextBuf;\n      nextBuf = crtBuf_->next();\n      crtBegin_ = crtBuf_->data();\n      crtEnd_ = crtBuf_->tail();\n      if (isBounded()) {\n        if (uintptr_t(crtBegin_) + remainingLen_ < uintptr_t(crtEnd_)) {\n          crtEnd_ = crtBegin_ + remainingLen_;\n        }\n        remainingLen_ -= crtEnd_ - crtBegin_;\n      }\n      crtPos_ = crtEnd_;\n      derived().advanceDone();\n    }\n  }\n\n  /// Advance the cursor\n  ///\n  /// @methodset Modifiers\n  Derived& operator+=(size_t offset) {\n    Derived* p = static_cast<Derived*>(this);\n    p->skip(offset);\n    return *p;\n  }\n  /// Get a new cursor, advanced by offset from this cursor.\n  ///\n  /// @methodset Modifiers\n  Derived operator+(size_t offset) const {\n    Derived other(*this);\n    other.skip(offset);\n    return other;\n  }\n\n  /// Retreat the cursor\n  ///\n  /// @methodset Modifiers\n  Derived& operator-=(size_t offset) {\n    Derived* p = static_cast<Derived*>(this);\n    p->retreat(offset);\n    return *p;\n  }\n  /// Get a new cursor, retreated by offset from this cursor.\n  ///\n  /// @methodset Modifiers\n  Derived operator-(size_t offset) const {\n    Derived other(*this);\n    other.retreat(offset);\n    return other;\n  }\n\n  /**\n   * Compare cursors for equality/inequality.\n   *\n   * @methodset Comparison\n   *\n   * Two cursors are equal if they are pointing to the same location in the\n   * same IOBuf chain.\n   */\n  bool operator==(const CursorBase& other) const {\n    const IOBuf* crtBuf = crtBuf_;\n    auto crtPos = crtPos_;\n    // We can be pointing to the end of a buffer chunk, find first non-empty.\n    while (crtPos == crtBuf->tail() && crtBuf != buffer_->prev()) {\n      crtBuf = crtBuf->next();\n      crtPos = crtBuf->data();\n    }\n\n    const IOBuf* crtBufOther = other.crtBuf_;\n    auto crtPosOther = other.crtPos_;\n    // We can be pointing to the end of a buffer chunk, find first non-empty.\n    while (crtPosOther == crtBufOther->tail() &&\n           crtBufOther != other.buffer_->prev()) {\n      crtBufOther = crtBufOther->next();\n      crtPosOther = crtBufOther->data();\n    }\n    return (crtPos == crtPosOther) && (crtBuf == crtBufOther);\n  }\n\n  /// @copydoc operator==\n  bool operator!=(const CursorBase& other) const { return !operator==(other); }\n\n  /**\n   * Attempt to read from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * @param[out] val  Store the read value in this location.\n   * @return True iff successful; If there are not enough bytes left in the\n   * cursor, return false.\n   * @note val might be modified even if tryRead returns false.\n   */\n  template <class T>\n  typename std::enable_if<std::is_arithmetic<T>::value, bool>::type tryRead(\n      T& val) {\n    if (FOLLY_LIKELY(uintptr_t(crtPos_) + sizeof(T) <= uintptr_t(crtEnd_))) {\n      val = loadUnaligned<T>(data());\n      crtPos_ += sizeof(T);\n      return true;\n    }\n    return pullAtMostSlow(&val, sizeof(T)) == sizeof(T);\n  }\n\n  /**\n   * Attempt to read a Big-Endian integral from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * @see tryRead\n   */\n  template <class T>\n  bool tryReadBE(T& val) {\n    const bool result = tryRead(val);\n    val = Endian::big(val);\n    return result;\n  }\n\n  /**\n   * Attempt to read a Little-Endian integral from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * @see tryRead\n   */\n  template <class T>\n  bool tryReadLE(T& val) {\n    const bool result = tryRead(val);\n    val = Endian::little(val);\n    return result;\n  }\n\n  /**\n   * Read a value from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * This function only works with types that are bit-copyable: it calls memcpy\n   * to reinterpret bits from the IOBuf as T.\n   *\n   * @throws out_of_range if there aren't enough bytes left in the cursor.\n   */\n  template <class T>\n  T read() {\n    if (FOLLY_LIKELY(uintptr_t(crtPos_) + sizeof(T) <= uintptr_t(crtEnd_))) {\n      T val = loadUnaligned<T>(data());\n      crtPos_ += sizeof(T);\n      return val;\n    } else {\n      return readSlow<T>();\n    }\n  }\n\n  /**\n   * Read a Big-Endian integral from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * @see read\n   */\n  template <class T>\n  T readBE() {\n    return Endian::big(read<T>());\n  }\n\n  /**\n   * Read a Little-Endian integral from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * @see read\n   */\n  template <class T>\n  T readLE() {\n    return Endian::little(read<T>());\n  }\n\n  /**\n   * Read a fixed-length string.\n   *\n   * @methodset Consumers\n   *\n   * The std::string-based APIs should probably be avoided unless you\n   * ultimately want the data to live in an std::string. You're better off\n   * using the pull() APIs to copy into a raw buffer otherwise.\n   */\n  std::string readFixedString(size_t len) {\n    std::string str;\n    str.reserve(len);\n    if (FOLLY_LIKELY(length() >= len)) {\n      str.append(reinterpret_cast<const char*>(data()), len);\n      crtPos_ += len;\n    } else {\n      readFixedStringSlow(&str, len);\n    }\n    return str;\n  }\n\n  /**\n   * Read a string of bytes until the given terminator character is seen.\n   *\n   * @methodset Consumers\n   *\n   * Raises an std::length_error if maxLength bytes have been processed\n   * before the terminator is seen.\n   *\n   * See comments in readFixedString() about when it's appropriate to use this\n   * vs. using pull().\n   */\n  std::string readTerminatedString(\n      char termChar = '\\0',\n      size_t maxLength = std::numeric_limits<size_t>::max());\n\n  /**\n   * @overloadbrief Read bytes until the specified predicate returns true.\n   *\n   * @methodset Consumers\n   *\n   * The predicate will be called on each byte in turn, until it returns false\n   * or until the end of the IOBuf chain is reached.\n   *\n   * Returns the result as a string.\n   */\n  template <typename Predicate>\n  std::string readWhile(const Predicate& predicate);\n\n  /**\n   * This is a more generic version of readWhile(). It takes an arbitrary Output\n   * object, and calls Output::append() with each chunk of matching data.\n   */\n  template <typename Predicate, typename Output>\n  void readWhile(const Predicate& predicate, Output& out);\n\n  /**\n   * Skip bytes until the specified predicate returns true.\n   *\n   * @methodset Consumers\n   *\n   * The predicate will be called on each byte in turn, until it returns false\n   * or until the end of the IOBuf chain is reached.\n   */\n  template <typename Predicate>\n  void skipWhile(const Predicate& predicate);\n\n  /**\n   * Advance the cursor by at most len bytes.\n   *\n   * @methodset Modifiers\n   */\n  size_t skipAtMost(size_t len) {\n    dcheckIntegrity();\n    if (FOLLY_LIKELY(uintptr_t(crtPos_) + len < uintptr_t(crtEnd_))) {\n      crtPos_ += len;\n      return len;\n    }\n    return skipAtMostSlow(len);\n  }\n\n  /**\n   * Advance the cursor by len bytes.\n   *\n   * @methodset Modifiers\n   *\n   * @throws out_of_range if there aren't enough bytes left in the cursor.\n   */\n  void skip(size_t len) {\n    dcheckIntegrity();\n    if (FOLLY_LIKELY(uintptr_t(crtPos_) + len < uintptr_t(crtEnd_))) {\n      crtPos_ += len;\n    } else {\n      skipSlow(len);\n    }\n  }\n\n  /**\n   * Skip bytes in the current IOBuf without advancing to the next one.\n   *\n   * @methodset Modifiers\n   *\n   * @pre length() >= len\n   */\n  void skipNoAdvance(size_t len) {\n    DCHECK_LE(len, length());\n    crtPos_ += len;\n  }\n\n  /**\n   * Retreat the cursor by at most len bytes.\n   *\n   * @methodset Modifiers\n   */\n  size_t retreatAtMost(size_t len) {\n    if (len <= getPositionInCurrentBuffer()) {\n      crtPos_ -= len;\n      return len;\n    }\n    return retreatAtMostSlow(len);\n  }\n\n  /**\n   * Retreat the cursor by at most len bytes.\n   *\n   * @methodset Modifiers\n   *\n   * @throws out_of_range if the cursor doesn't have enough bytes to retreat.\n   */\n  void retreat(size_t len) {\n    if (len <= getPositionInCurrentBuffer()) {\n      crtPos_ -= len;\n    } else {\n      retreatSlow(len);\n    }\n  }\n\n  /**\n   * Copies at most len bytes from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * The cursor will advance by the number of bytes read.\n   *\n   * @param[out] buf The buffer into which the bytes are copied.\n   * @returns The number of bytes copied.\n   */\n  size_t pullAtMost(void* buf, size_t len) {\n    if (FOLLY_UNLIKELY(len == 0)) {\n      return 0;\n    }\n    dcheckIntegrity();\n    // Fast path: it all fits in one buffer.\n    if (FOLLY_LIKELY(uintptr_t(crtPos_) + len <= uintptr_t(crtEnd_))) {\n      memcpy(buf, data(), len);\n      crtPos_ += len;\n      return len;\n    }\n    return pullAtMostSlow(buf, len);\n  }\n\n  /**\n   * Copies len bytes from the cursor.\n   *\n   * @methodset Consumers\n   *\n   * The cursor will advance by len.\n   *\n   * @param[out] buf The buffer into which the bytes are copied.\n   * @throw out_of_range if there aren't enough bytes in the cursor.\n   */\n  void pull(void* buf, size_t len) {\n    if (FOLLY_UNLIKELY(len == 0)) {\n      return;\n    }\n    dcheckIntegrity();\n    if (FOLLY_LIKELY(uintptr_t(crtPos_) + len <= uintptr_t(crtEnd_))) {\n      memcpy(buf, data(), len);\n      crtPos_ += len;\n    } else {\n      pullSlow(buf, len);\n    }\n  }\n\n  /**\n   * Return the available data in the current IOBuf.\n   *\n   * @methodset Accessors\n   *\n   * Unlike data(), peekBytes() will advance to the next IOBuf while length()==0\n   * (though it can still return an empty range if there are no bytes left to\n   * read in the whole IOBuf chain).\n   *\n   * If you want to gather more data from the chain into a contiguous region\n   * (for hopefully zero-copy access), use gather() before peekBytes().\n   */\n  ByteRange peekBytes() {\n    // Ensure that we're pointing to valid data\n    size_t available = length();\n    if (FOLLY_UNLIKELY(!available)) {\n      available = peekBytesSlow();\n    }\n    return ByteRange{data(), available};\n  }\n\n  /**\n   * Alternate version of peekBytes() that returns a std::span\n   * instead of a ByteRage.\n   *\n   * @methodset Accessors\n   */\n  span<uint8_t const> peek() {\n    auto bytes = peekBytes();\n    return {bytes.data(), bytes.size()};\n  }\n\n  /**\n   * Clone len bytes from this cursor into an IOBuf.\n   *\n   * @methodset Accessors\n   *\n   * @param[out] buf The IOBuf into which to place the cloned data.\n   * @throws out_of_range if there aren't enough bytes in this cursor.\n   */\n  void clone(std::unique_ptr<folly::IOBuf>& buf, size_t len) {\n    if (FOLLY_UNLIKELY(cloneAtMost(buf, len) != len)) {\n      throw_exception<std::out_of_range>(\"underflow\");\n    }\n  }\n\n  void clone(folly::IOBuf& buf, size_t len) {\n    if (FOLLY_UNLIKELY(cloneAtMost(buf, len) != len)) {\n      throw_exception<std::out_of_range>(\"underflow\");\n    }\n  }\n\n  /**\n   * Clone at most len bytes from this cursor into an IOBuf.\n   *\n   * @methodset Accessors\n   *\n   * @param[out] buf The IOBuf into which to place the cloned data.\n   * @return The number of bytes actually cloned.\n   */\n  size_t cloneAtMost(folly::IOBuf& buf, size_t len) {\n    // We might be at the end of buffer.\n    advanceBufferIfEmpty();\n\n    std::unique_ptr<folly::IOBuf> tmp;\n    size_t copied = 0;\n    for (int loopCount = 0; true; ++loopCount) {\n      // Fast path: it all fits in one buffer.\n      size_t available = length();\n      if (FOLLY_LIKELY(available >= len)) {\n        if (loopCount == 0) {\n          crtBuf_->cloneOneInto(buf);\n          buf.trimStart(crtPos_ - crtBegin_);\n          buf.trimEnd(buf.length() - len);\n        } else {\n          tmp = crtBuf_->cloneOne();\n          tmp->trimStart(crtPos_ - crtBegin_);\n          tmp->trimEnd(tmp->length() - len);\n          buf.prependChain(std::move(tmp));\n        }\n\n        crtPos_ += len;\n        advanceBufferIfEmpty();\n        return copied + len;\n      }\n\n      if (loopCount == 0) {\n        crtBuf_->cloneOneInto(buf);\n        buf.trimStart(crtPos_ - crtBegin_);\n      } else {\n        tmp = crtBuf_->cloneOne();\n        tmp->trimStart(crtPos_ - crtBegin_);\n        buf.prependChain(std::move(tmp));\n      }\n\n      copied += available;\n      if (FOLLY_UNLIKELY(!tryAdvanceBuffer())) {\n        return copied;\n      }\n      len -= available;\n    }\n  }\n\n  size_t cloneAtMost(std::unique_ptr<folly::IOBuf>& buf, size_t len) {\n    if (!buf) {\n      buf = std::make_unique<folly::IOBuf>();\n    }\n    return cloneAtMost(*buf, len);\n  }\n\n  /**\n   * Return the distance between two cursors.\n   */\n  size_t operator-(const CursorBase& other) const {\n    BufType* otherBuf = other.crtBuf_;\n    size_t len = 0;\n\n    if (otherBuf != crtBuf_) {\n      if (other.remainingLen_ == 0) {\n        len += otherBuf->tail() - other.crtPos_;\n      } else {\n        len += other.crtEnd_ - other.crtPos_;\n      }\n\n      for (otherBuf = otherBuf->next();\n           otherBuf != crtBuf_ && otherBuf != other.buffer_;\n           otherBuf = otherBuf->next()) {\n        len += otherBuf->length();\n      }\n\n      if (otherBuf == other.buffer_) {\n        throw_exception<std::out_of_range>(\"wrap-around\");\n      }\n\n      len += crtPos_ - crtBegin_;\n    } else {\n      if (crtPos_ < other.crtPos_) {\n        throw_exception<std::out_of_range>(\"underflow\");\n      }\n\n      len += crtPos_ - other.crtPos_;\n    }\n\n    return len;\n  }\n\n  /**\n   * Return the distance from the given IOBuf to the this cursor.\n   */\n  size_t operator-(const BufType* buf) const {\n    size_t len = 0;\n\n    const BufType* curBuf = buf;\n    while (curBuf != crtBuf_) {\n      len += curBuf->length();\n      curBuf = curBuf->next();\n      if (curBuf == buf || curBuf == buffer_) {\n        throw_exception<std::out_of_range>(\"wrap-around\");\n      }\n    }\n\n    len += crtPos_ - crtBegin_;\n    return len;\n  }\n\n  /**\n   * Check if this cursor has a size limit imposed on it.\n   *\n   * @methodset Configuration\n   */\n  bool isBounded() const {\n    return remainingLen_ != std::numeric_limits<size_t>::max();\n  }\n\n protected:\n  void dcheckIntegrity() const {\n    FOLLY_IO_CURSOR_BORROW_DCHECK(!*borrowed());\n    DCHECK(crtBegin_ <= crtPos_ && crtPos_ <= crtEnd_);\n    DCHECK(crtBuf_ == nullptr || crtBegin_ == crtBuf_->data());\n    DCHECK(\n        crtBuf_ == nullptr ||\n        (std::size_t)(crtEnd_ - crtBegin_) <= crtBuf_->length());\n  }\n\n  CursorBase(CursorBase const&) = default;\n  CursorBase(CursorBase&&) = default;\n  CursorBase& operator=(CursorBase const&) = default;\n  CursorBase& operator=(CursorBase&&) = default;\n\n  ~CursorBase() = default;\n\n  BufType* head() { return buffer_; }\n\n  bool tryAdvanceBuffer() {\n    BufType* nextBuf = crtBuf_->next();\n    if (FOLLY_UNLIKELY(nextBuf == buffer_) || remainingLen_ == 0) {\n      crtPos_ = crtEnd_;\n      return false;\n    }\n\n    absolutePos_ += crtEnd_ - crtBegin_;\n    crtBuf_ = nextBuf;\n    crtPos_ = crtBegin_ = crtBuf_->data();\n    crtEnd_ = crtBuf_->tail();\n    if (isBounded()) {\n      if (uintptr_t(crtPos_) + remainingLen_ < uintptr_t(crtEnd_)) {\n        crtEnd_ = crtPos_ + remainingLen_;\n      }\n      remainingLen_ -= crtEnd_ - crtPos_;\n    }\n    derived().advanceDone();\n    return true;\n  }\n\n  bool tryRetreatBuffer() {\n    if (FOLLY_UNLIKELY(crtBuf_ == buffer_)) {\n      crtPos_ = crtBegin_;\n      return false;\n    }\n    if (isBounded()) {\n      remainingLen_ += crtEnd_ - crtBegin_;\n    }\n    crtBuf_ = crtBuf_->prev();\n    crtBegin_ = crtBuf_->data();\n    crtPos_ = crtEnd_ = crtBuf_->tail();\n    absolutePos_ -= crtEnd_ - crtBegin_;\n    derived().advanceDone();\n    return true;\n  }\n\n  void advanceBufferIfEmpty() {\n    dcheckIntegrity();\n    if (crtPos_ == crtEnd_) {\n      tryAdvanceBuffer();\n    }\n  }\n\n  BufType* crtBuf_;\n  BufType* buffer_;\n  const uint8_t* crtBegin_{nullptr};\n  const uint8_t* crtEnd_{nullptr};\n  const uint8_t* crtPos_{nullptr};\n  size_t absolutePos_{0};\n  // For bounded Cursor, remainingLen_ is the remaining number of data bytes\n  // in subsequent IOBufs in the chain. For unbounded Cursor, remainingLen_\n  // is set to the max of size_t\n  size_t remainingLen_{std::numeric_limits<size_t>::max()};\n\n#if FOLLY_IO_CURSOR_BORROW_CHECKING\n  bool borrowed_ = false;\n  bool* borrowed() { return &borrowed_; }\n  const bool* borrowed() const { return &borrowed_; }\n#else\n  bool* borrowed() { return nullptr; }\n  const bool* borrowed() const { return nullptr; }\n#endif\n\n private:\n  Derived& derived() { return static_cast<Derived&>(*this); }\n\n  Derived const& derived() const { return static_cast<const Derived&>(*this); }\n\n  template <class T>\n  FOLLY_NOINLINE T readSlow() {\n    T val;\n    pullSlow(&val, sizeof(T));\n    return val;\n  }\n\n  FOLLY_NOINLINE void readFixedStringSlow(std::string* str, size_t len) {\n    for (size_t available; (available = length()) < len;) {\n      str->append(reinterpret_cast<const char*>(data()), available);\n      if (FOLLY_UNLIKELY(!tryAdvanceBuffer())) {\n        throw_exception<std::out_of_range>(\"string underflow\");\n      }\n      len -= available;\n    }\n    str->append(reinterpret_cast<const char*>(data()), len);\n    crtPos_ += len;\n    advanceBufferIfEmpty();\n  }\n\n  FOLLY_NOINLINE size_t pullAtMostSlow(void* buf, size_t len) {\n    uint8_t* p = reinterpret_cast<uint8_t*>(buf);\n    size_t copied = 0;\n    for (size_t available; (available = length()) < len;) {\n      if (available > 0) {\n        // Don't try to copy from 0-length buffers, since they could have\n        // a null data() pointer.\n        memcpy(p, data(), available);\n        copied += available;\n      }\n      if (FOLLY_UNLIKELY(!tryAdvanceBuffer())) {\n        return copied;\n      }\n      p += available;\n      len -= available;\n    }\n    if (len > 0) {\n      // Don't try to copy from 0-length buffers, since they could have\n      // a null data() pointer.\n      memcpy(p, data(), len);\n      crtPos_ += len;\n    }\n    advanceBufferIfEmpty();\n    return copied + len;\n  }\n\n  FOLLY_NOINLINE void pullSlow(void* buf, size_t len) {\n    if (FOLLY_UNLIKELY(pullAtMostSlow(buf, len) != len)) {\n      throw_exception<std::out_of_range>(\"underflow\");\n    }\n  }\n\n  FOLLY_NOINLINE size_t skipAtMostSlow(size_t len) {\n    size_t skipped = 0;\n    for (size_t available; (available = length()) < len;) {\n      skipped += available;\n      if (FOLLY_UNLIKELY(!tryAdvanceBuffer())) {\n        return skipped;\n      }\n      len -= available;\n    }\n    crtPos_ += len;\n    advanceBufferIfEmpty();\n    return skipped + len;\n  }\n\n  FOLLY_NOINLINE void skipSlow(size_t len) {\n    if (FOLLY_UNLIKELY(skipAtMostSlow(len) != len)) {\n      throw_exception<std::out_of_range>(\"underflow\");\n    }\n  }\n\n  FOLLY_NOINLINE size_t retreatAtMostSlow(size_t len) {\n    size_t retreated = 0;\n    for (size_t available; (available = crtPos_ - crtBegin_) < len;) {\n      retreated += available;\n      if (FOLLY_UNLIKELY(!tryRetreatBuffer())) {\n        return retreated;\n      }\n      len -= available;\n    }\n    crtPos_ -= len;\n    return retreated + len;\n  }\n\n  FOLLY_NOINLINE void retreatSlow(size_t len) {\n    if (FOLLY_UNLIKELY(retreatAtMostSlow(len) != len)) {\n      throw_exception<std::out_of_range>(\"underflow\");\n    }\n  }\n\n  void advanceDone() {}\n\n  FOLLY_NOINLINE size_t peekBytesSlow() {\n    size_t available = 0;\n    while (available == 0 && tryAdvanceBuffer()) {\n      available = length();\n    }\n    return available;\n  }\n};\n\nnamespace detail {\ntemplate <typename T>\nThinCursor thinCursorReadSlow(ThinCursor, T&, Cursor&);\nThinCursor thinCursorSkipSlow(ThinCursor, Cursor&, size_t);\n} // namespace detail\n\n// A register-pass facade for a Cursor. It strips out state that's only needed\n// down slow paths, so that it can be passed and returned efficiently in\n// registers. This gives up some convenience (users need to maintain that state\n// in a fallback Cursor), to gain performance.\nclass ThinCursor {\n public:\n  ThinCursor() = default;\n  ThinCursor(ThinCursor&&) = default;\n  ThinCursor(const ThinCursor&) = delete;\n  ThinCursor& operator=(ThinCursor&&) = default;\n  ThinCursor& operator=(const ThinCursor&) = delete;\n\n  const uint8_t* data() const {\n    dcheckIntegrity();\n    return crtPos_;\n  }\n\n  size_t length() const {\n    dcheckIntegrity();\n    return crtEnd_ - crtPos_;\n  }\n\n  bool canAdvance(size_t amount) const { return amount <= length(); }\n\n  bool isAtEnd() const { return length() == 0; }\n\n  template <class T>\n  FOLLY_ALWAYS_INLINE T read(Cursor& fallback) {\n    if (FOLLY_LIKELY((uintptr_t)crtEnd_ - (uintptr_t)crtPos_ >= sizeof(T))) {\n      T result = loadUnaligned<T>(data());\n      crtPos_ += sizeof(T);\n      return result;\n    } else {\n      T result;\n      *this = detail::thinCursorReadSlow(std::move(*this), result, fallback);\n      return result;\n    }\n  }\n\n  template <class T>\n  T readBE(Cursor& fallback) {\n    return Endian::big(read<T>(fallback));\n  }\n\n  template <class T>\n  T readLE(Cursor& fallback) {\n    return Endian::little(read<T>(fallback));\n  }\n\n  void skip(Cursor& fallback, size_t len) {\n    dcheckIntegrity();\n    if (FOLLY_LIKELY(uintptr_t(crtPos_) + len < uintptr_t(crtEnd_))) {\n      crtPos_ += len;\n    } else {\n      *this = detail::thinCursorSkipSlow(std::move(*this), fallback, len);\n    }\n  }\n\n  void skipNoAdvance(size_t len) {\n    DCHECK_LE(len, length());\n    crtPos_ += len;\n  }\n\n private:\n  void dcheckIntegrity() const { DCHECK(crtPos_ <= crtEnd_); }\n\n  friend class Cursor;\n  ThinCursor(const uint8_t* crtPos, const uint8_t* crtEnd) noexcept\n      : crtPos_(crtPos), crtEnd_(crtEnd) {}\n  // Note: these are the only fields we can have -- x86-64 calling convention\n  // maxes out at returning 2 pointer-sized fields in registers, and we don't\n  // want to have to use memory for returning these.\n  const uint8_t* crtPos_;\n  const uint8_t* crtEnd_;\n};\n\nclass Cursor : public CursorBase<Cursor, const IOBuf> {\n public:\n  explicit Cursor(const IOBuf* buf) noexcept\n      : CursorBase<Cursor, const IOBuf>(buf) {}\n\n  explicit Cursor(const IOBuf* buf, size_t len) noexcept\n      : CursorBase<Cursor, const IOBuf>(buf, len) {}\n\n  template <class OtherDerived, class OtherBuf>\n  explicit Cursor(const CursorBase<OtherDerived, OtherBuf>& cursor) noexcept\n      : CursorBase<Cursor, const IOBuf>(cursor) {}\n\n  template <class OtherDerived, class OtherBuf>\n  Cursor(const CursorBase<OtherDerived, OtherBuf>& cursor, size_t len)\n      : CursorBase<Cursor, const IOBuf>(cursor, len) {}\n\n  ThinCursor borrow() {\n    FOLLY_IO_CURSOR_BORROW_DCHECK(!std::exchange(*borrowed(), true));\n    return {crtPos_, crtEnd_};\n  }\n\n  void unborrow(ThinCursor&& cursor) {\n    FOLLY_IO_CURSOR_BORROW_DCHECK(std::exchange(*borrowed(), false));\n    DCHECK_EQ(cursor.crtEnd_, crtEnd_);\n    crtPos_ = cursor.crtPos_;\n  }\n};\n\nnamespace detail {\ntemplate <typename T>\nThinCursor thinCursorReadSlow(ThinCursor borrowed, T& val, Cursor& fallback) {\n  fallback.unborrow(std::move(borrowed));\n  val = fallback.read<T>();\n  return fallback.borrow();\n}\n\ninline ThinCursor thinCursorSkipSlow(\n    ThinCursor borrowed, Cursor& fallback, size_t len) {\n  fallback.unborrow(std::move(borrowed));\n  fallback.skip(len);\n  return fallback.borrow();\n}\n} // namespace detail\n\ntemplate <class Derived>\nclass Writable {\n public:\n  /**\n   * Write a value to the cursor.\n   *\n   * @methodset Writing\n   *\n   * May throw if there isn't enough space and the derived cursor type does not\n   * support extending the IOBuf's writable range.\n   */\n  template <class T>\n  typename std::enable_if<std::is_arithmetic<T>::value>::type write(\n      T value, size_t n = sizeof(T)) {\n    this->write(tag<T>, value, n);\n  }\n  template <class T>\n  typename std::enable_if<std::is_arithmetic<T>::value>::type write(\n      tag_t<T>, T value, size_t n = sizeof(T)) {\n    assert(n <= sizeof(T));\n    const uint8_t* u8 = reinterpret_cast<const uint8_t*>(&value);\n    Derived* d = static_cast<Derived*>(this);\n    d->push(u8, n);\n  }\n\n  /**\n   * Write a value to the cursor in Big-Endian.\n   *\n   * @methodset Writing\n   *\n   * May throw if there isn't enough space and the derived cursor type does not\n   * support extending the IOBuf's writable range.\n   */\n  template <class T>\n  void writeBE(T value) {\n    this->writeBE<T>(tag<T>, value);\n  }\n  template <class T>\n  void writeBE(tag_t<T>, T value) {\n    Derived* d = static_cast<Derived*>(this);\n    d->write(tag<T>, Endian::big(value));\n  }\n\n  /**\n   * Write a value to the cursor in Little-Endian.\n   *\n   * @methodset Writing\n   *\n   * May throw if there isn't enough space and the derived cursor type does not\n   * support extending the IOBuf's writable range.\n   */\n  template <class T>\n  void writeLE(T value) {\n    this->writeLE(tag<T>, value);\n  }\n  template <class T>\n  void writeLE(tag_t<T>, T value) {\n    Derived* d = static_cast<Derived*>(this);\n    d->write(tag<T>, Endian::little(value));\n  }\n\n  /**\n   * Write bytes to the cursor.\n   *\n   * @methodset Writing\n   *\n   * @throw out_of_range if there isn't enough space in the cursor.\n   */\n  void push(const uint8_t* buf, size_t len) {\n    Derived* d = static_cast<Derived*>(this);\n    if (d->pushAtMost(buf, len) != len) {\n      throw_exception<std::out_of_range>(\"overflow\");\n    }\n  }\n\n  void push(ByteRange buf) {\n    if (this->pushAtMost(buf) != buf.size()) {\n      throw_exception<std::out_of_range>(\"overflow\");\n    }\n  }\n\n  /**\n   * Write bytes to the cursor; stop writing if the end of the cursor is\n   * reached.\n   *\n   * @methodset Writing\n   */\n  size_t pushAtMost(ByteRange buf) {\n    Derived* d = static_cast<Derived*>(this);\n    return d->pushAtMost(buf.data(), buf.size());\n  }\n\n  void push(Cursor cursor, size_t len) {\n    if (this->pushAtMost(cursor, len) != len) {\n      throw_exception<std::out_of_range>(\"overflow\");\n    }\n  }\n\n  size_t pushAtMost(Cursor cursor, size_t len) {\n    size_t written = 0;\n    for (;;) {\n      auto currentBuffer = cursor.peekBytes();\n      const uint8_t* crtData = currentBuffer.data();\n      size_t available = currentBuffer.size();\n      if (available == 0) {\n        // end of buffer chain\n        return written;\n      }\n      // all data is in current buffer\n      if (available >= len) {\n        this->push(crtData, len);\n        cursor.skip(len);\n        return written + len;\n      }\n\n      // write the whole current IOBuf\n      this->push(crtData, available);\n      cursor.skip(available);\n      written += available;\n      len -= available;\n    }\n  }\n};\n\nenum class CursorAccess { PRIVATE, UNSHARE };\n\ntemplate <CursorAccess access>\nclass RWCursor\n    : public CursorBase<RWCursor<access>, IOBuf>,\n      public Writable<RWCursor<access>> {\n  friend class CursorBase<RWCursor<access>, IOBuf>;\n\n public:\n  explicit RWCursor(IOBuf* buf) noexcept\n      : CursorBase<RWCursor<access>, IOBuf>(buf), maybeShared_(true) {}\n\n  explicit RWCursor(IOBufQueue& queue) noexcept\n      : RWCursor((queue.flushCache(), queue.head_.get())) {}\n\n  // Efficient way to advance to position cursor to the end of the queue,\n  // using cached length instead of a walk via advanceToEnd().\n  struct AtEnd {};\n  /**\n   * Create the cursor initially pointing to the end of queue.\n   */\n  RWCursor(IOBufQueue& queue, AtEnd) noexcept : RWCursor(queue) {\n    if (!queue.options().cacheChainLength) {\n      this->advanceToEnd();\n    } else {\n      this->crtBuf_ = this->buffer_->prev();\n      this->crtBegin_ = this->crtBuf_->data();\n      this->crtEnd_ = this->crtBuf_->tail();\n      this->crtPos_ = this->crtEnd_;\n      this->absolutePos_ =\n          queue.chainLength() - (this->crtPos_ - this->crtBegin_);\n      DCHECK_EQ(this->getCurrentPosition(), queue.chainLength());\n    }\n  }\n\n  template <class OtherDerived, class OtherBuf>\n  explicit RWCursor(const CursorBase<OtherDerived, OtherBuf>& cursor) noexcept\n      : CursorBase<RWCursor<access>, IOBuf>(cursor), maybeShared_(true) {\n    CHECK(!cursor.isBounded())\n        << \"Creating RWCursor from bounded Cursor is not allowed\";\n  }\n  /**\n   * Gather at least n bytes contiguously into the current buffer,\n   * by coalescing subsequent buffers from the chain as necessary.\n   *\n   * @methodset Modifiers\n   *\n   * @throw overflow_error if there aren't enough bytes to gather\n   */\n  void gather(size_t n) {\n    // Forbid attempts to gather beyond the end of this IOBuf chain.\n    // Otherwise we could try to coalesce the head of the chain and end up\n    // accidentally freeing it, invalidating the pointer owned by external\n    // code.\n    //\n    // If crtBuf_ == head() then IOBuf::gather() will perform all necessary\n    // checking.  We only have to perform an explicit check here when calling\n    // gather() on a non-head element.\n    if (this->crtBuf_ != this->head() && this->totalLength() < n) {\n      throw std::overflow_error(\"cannot gather() past the end of the chain\");\n    }\n    size_t offset = this->crtPos_ - this->crtBegin_;\n    this->crtBuf_->gather(offset + n);\n    this->crtBegin_ = this->crtBuf_->data();\n    this->crtEnd_ = this->crtBuf_->tail();\n    this->crtPos_ = this->crtBegin_ + offset;\n  }\n\n  /**\n   * Gather at most n bytes contiguously into the current buffer,\n   * by coalescing subsequent buffers from the chain as necessary.\n   *\n   * @methodset Modifiers\n   */\n  void gatherAtMost(size_t n) {\n    this->dcheckIntegrity();\n    size_t size = std::min(n, this->totalLength());\n    size_t offset = this->crtPos_ - this->crtBegin_;\n    this->crtBuf_->gather(offset + size);\n    this->crtBegin_ = this->crtBuf_->data();\n    this->crtEnd_ = this->crtBuf_->tail();\n    this->crtPos_ = this->crtBegin_ + offset;\n  }\n\n  using Writable<RWCursor<access>>::pushAtMost;\n  size_t pushAtMost(const uint8_t* buf, size_t len) {\n    // We have to explicitly check for an input length of 0.\n    // We support buf being nullptr in this case, but we need to avoid calling\n    // memcpy() with a null source pointer, since that is undefined behavior\n    // even if the length is 0.\n    if (len == 0) {\n      return 0;\n    }\n\n    size_t copied = 0;\n    for (;;) {\n      // Fast path: the current buffer is big enough.\n      size_t available = this->length();\n      if (FOLLY_LIKELY(available >= len)) {\n        if (access == CursorAccess::UNSHARE) {\n          maybeUnshare();\n        }\n        memcpy(writableData(), buf, len);\n        this->crtPos_ += len;\n        return copied + len;\n      }\n\n      if (access == CursorAccess::UNSHARE) {\n        maybeUnshare();\n      }\n      memcpy(writableData(), buf, available);\n      copied += available;\n      if (FOLLY_UNLIKELY(!this->tryAdvanceBuffer())) {\n        return copied;\n      }\n      buf += available;\n      len -= available;\n    }\n  }\n\n  /**\n   * Insert data at the cursor position.\n   *\n   * @methodset Writing\n   *\n   * Data in the IOBuf after the cursor will not be overwritten, though it might\n   * be moved.\n   *\n   * After this operator, the cursor will point to the data just after the\n   * inserted data.\n   */\n  void insert(std::unique_ptr<folly::IOBuf> buf) {\n    this->dcheckIntegrity();\n    this->absolutePos_ += buf->computeChainDataLength();\n    if (this->crtPos_ == this->crtBegin_ && this->crtBuf_ != this->buffer_) {\n      // Can just prepend\n      this->crtBuf_->prependChain(std::move(buf));\n    } else {\n      IOBuf* nextBuf;\n      std::unique_ptr<folly::IOBuf> remaining;\n      if (this->crtPos_ != this->crtEnd_) {\n        // Need to split current IOBuf in two.\n        remaining = this->crtBuf_->cloneOne();\n        remaining->trimStart(this->crtPos_ - this->crtBegin_);\n        nextBuf = remaining.get();\n        buf->prependChain(std::move(remaining));\n      } else {\n        // Can just append\n        nextBuf = this->crtBuf_->next();\n      }\n      this->crtBuf_->trimEnd(this->length());\n      this->absolutePos_ += this->crtPos_ - this->crtBegin_;\n      this->crtBuf_->insertAfterThisOne(std::move(buf));\n\n      if (nextBuf == this->buffer_) {\n        // We've just appended to the end of the buffer, so advance to the end.\n        this->crtBuf_ = this->buffer_->prev();\n        this->crtBegin_ = this->crtBuf_->data();\n        this->crtPos_ = this->crtEnd_ = this->crtBuf_->tail();\n        // This has already been accounted for, so remove it.\n        this->absolutePos_ -= this->crtEnd_ - this->crtBegin_;\n      } else {\n        // Jump past the new links\n        this->crtBuf_ = nextBuf;\n        this->crtPos_ = this->crtBegin_ = this->crtBuf_->data();\n        this->crtEnd_ = this->crtBuf_->tail();\n      }\n    }\n  }\n\n  /**\n   * Get a raw pointer to the writable section controlled by this cursor.\n   *\n   * @methodset Accessors\n   */\n  uint8_t* writableData() {\n    this->dcheckIntegrity();\n    return this->crtBuf_->writableData() + (this->crtPos_ - this->crtBegin_);\n  }\n\n private:\n  void maybeUnshare() {\n    if (FOLLY_UNLIKELY(maybeShared_)) {\n      size_t offset = this->crtPos_ - this->crtBegin_;\n      this->crtBuf_->unshareOne();\n      this->crtBegin_ = this->crtBuf_->data();\n      this->crtEnd_ = this->crtBuf_->tail();\n      this->crtPos_ = this->crtBegin_ + offset;\n      maybeShared_ = false;\n    }\n  }\n\n  void advanceDone() { maybeShared_ = true; }\n\n  bool maybeShared_;\n};\n\nusing RWPrivateCursor = RWCursor<CursorAccess::PRIVATE>;\nusing RWUnshareCursor = RWCursor<CursorAccess::UNSHARE>;\n\n/**\n * Append to the end of a buffer chain, growing the chain (by allocating new\n * buffers) in increments of at least growth bytes every time.  Won't grow\n * (and push() and ensure() will throw) if growth == 0.\n *\n * TODO(tudorb): add a flavor of Appender that reallocates one IOBuf instead\n * of chaining.\n */\nclass Appender : public Writable<Appender> {\n public:\n  Appender(IOBuf* buf, std::size_t growth) noexcept\n      : buffer_(buf), crtBuf_(buf->prev()), growth_(growth) {}\n\n  /**\n   * Get the writable tail of the IOBuf this cursor points to.\n   *\n   * @methodset Accessors\n   */\n  uint8_t* writableData() noexcept { return crtBuf_->writableTail(); }\n\n  /**\n   * Get the amount of writable tailroom of the IOBuf this cursor points to.\n   *\n   * @methodset Capacity\n   */\n  size_t length() const noexcept { return crtBuf_->tailroom(); }\n\n  /**\n   * Mark n bytes (must be <= length()) as appended, as per the\n   * IOBuf::append() method.\n   *\n   * @methodset Appending\n   */\n  void append(size_t n) { crtBuf_->append(n); }\n\n  /**\n   * Ensure at least n contiguous bytes available to write.\n   * Postcondition: length() >= n.\n   *\n   * @methodset Appending\n   */\n  void ensure(std::size_t n) {\n    if (FOLLY_LIKELY(length() >= n)) {\n      return;\n    }\n\n    // Waste the rest of the current buffer and allocate a new one.\n    // Don't make it too small, either.\n    if (growth_ == 0) {\n      throw_exception<std::out_of_range>(\"can't grow buffer chain\");\n    }\n\n    n = std::max(n, growth_);\n    buffer_->prependChain(IOBuf::create(n));\n    crtBuf_ = buffer_->prev();\n  }\n\n  using Writable<Appender>::pushAtMost;\n  size_t pushAtMost(const uint8_t* buf, size_t len) {\n    // We have to explicitly check for an input length of 0.\n    // We support buf being nullptr in this case, but we need to avoid calling\n    // memcpy() with a null source pointer, since that is undefined behavior\n    // even if the length is 0.\n    if (len == 0) {\n      return 0;\n    }\n\n    // If the length of this buffer is 0 try growing it.\n    // Otherwise on the first iteration of the following loop memcpy is called\n    // with a null source pointer.\n    if (FOLLY_UNLIKELY(length() == 0 && !tryGrowChain())) {\n      return 0;\n    }\n\n    size_t copied = 0;\n    for (;;) {\n      // Fast path: it all fits in one buffer.\n      size_t available = length();\n      if (FOLLY_LIKELY(available >= len)) {\n        memcpy(writableData(), buf, len);\n        append(len);\n        return copied + len;\n      }\n\n      memcpy(writableData(), buf, available);\n      append(available);\n      copied += available;\n      if (FOLLY_UNLIKELY(!tryGrowChain())) {\n        return copied;\n      }\n      buf += available;\n      len -= available;\n    }\n  }\n\n  /**\n   * Append to the end of this buffer, using a printf() style\n   * format specifier.\n   *\n   * @methodset Appending\n   *\n   * Note that folly/Format.h provides nicer and more type-safe mechanisms\n   * for formatting strings, which should generally be preferred over\n   * printf-style formatting.  Appender objects can be used directly as an\n   * output argument for Formatter objects.  For example:\n   *\n   *   Appender app(&iobuf);\n   *   format(\"{} {}\", \"hello\", \"world\")(app);\n   *\n   * However, printf-style strings are still needed when dealing with existing\n   * third-party code in some cases.\n   *\n   * This will always add a nul-terminating character after the end\n   * of the output.  However, the buffer data length will only be updated to\n   * include the data itself.  The nul terminator will be the first byte in the\n   * buffer tailroom.\n   *\n   * This method may throw exceptions on error.\n   */\n  void printf(FOLLY_PRINTF_FORMAT const char* fmt, ...)\n      FOLLY_PRINTF_FORMAT_ATTR(2, 3);\n\n  /// @methodset Appending\n  void vprintf(const char* fmt, va_list ap);\n\n  /**\n   * Append a StringPiece to the buffer.\n   *\n   * @methodset Appending\n   *\n   * This allows Appender objects to be used directly with Formatter.\n   */\n  void operator()(StringPiece sp) { push(ByteRange(sp)); }\n\n private:\n  bool tryGrowChain() {\n    assert(crtBuf_->next() == buffer_);\n    if (growth_ == 0) {\n      return false;\n    }\n\n    buffer_->prependChain(IOBuf::create(growth_));\n    crtBuf_ = buffer_->prev();\n    return true;\n  }\n\n  IOBuf* buffer_;\n  IOBuf* crtBuf_;\n  std::size_t growth_;\n};\n\nclass QueueAppender : public Writable<QueueAppender> {\n public:\n  /**\n   * Create an Appender that writes to a IOBufQueue.  When we allocate space in\n   * the queue, each new buffer will be sized between minGrowth and maxGrowth,\n   * with an exponential schedule (unless you call ensure() with a bigger value\n   * yourself).\n   */\n  QueueAppender(\n      IOBufQueue* queue, std::size_t minGrowth, std::size_t maxGrowth) noexcept\n      : queueCache_(queue) {\n    resetGrowth(minGrowth, maxGrowth);\n  }\n\n  /**\n   * Convenience constructor to use constant buffer growth.\n   */\n  QueueAppender(IOBufQueue* queue, std::size_t growth) noexcept\n      : QueueAppender(queue, growth, growth) {}\n\n  /**\n   * Resets this, as if constructed anew.\n   */\n  void reset(\n      IOBufQueue* queue,\n      std::size_t minGrowth,\n      std::size_t maxGrowth) noexcept {\n    queueCache_.reset(queue);\n    resetGrowth(minGrowth, maxGrowth);\n  }\n\n  void reset(IOBufQueue* queue, std::size_t growth) noexcept {\n    reset(queue, growth, growth);\n  }\n\n  /**\n   * Get a pointer to the writable tail.\n   *\n   * @methodset Accessors\n   */\n  uint8_t* writableData() noexcept { return queueCache_.writableData(); }\n\n  /**\n   * Get the size of the writable tail.\n   *\n   * @methodset Capacity\n   */\n  size_t length() const noexcept { return queueCache_.length(); }\n\n  /**\n   * Append n bytes.\n   *\n   * @methodset Appending\n   */\n  void append(size_t n) { queueCache_.append(n); }\n\n  /**\n   * Ensure that there are at least n contiguous bytes available for writing.\n   *\n   * @methodset Modifiers\n   *\n   * Can go above maxGrowth.\n   *\n   * May throw if there isn't enough room.\n   */\n  void ensure(size_t n) {\n    if (length() < n) {\n      ensureSlowNoinline(n);\n    }\n  }\n\n  /**\n   * Ensures up to n contiguous bytes available, without surpassing maxGrowth_.\n   *\n   * @methodset Modifiers\n   *\n   * Cannot go above maxGrowth.\n   *\n   * May throw if there isn't enough room.\n   */\n  void ensureWithinMaxGrowth(size_t n) { ensure(std::min(n, maxGrowth_)); }\n\n  /**\n   * Write an object to the cursor.\n   *\n   * @param n The number of bytes of value to write; defaults to sizeof(T)\n   */\n  template <class T>\n  typename std::enable_if<std::is_arithmetic<T>::value>::type write(\n      T value, size_t n = sizeof(T)) {\n    this->write(tag<T>, value, n);\n  }\n  template <class T>\n  typename std::enable_if<std::is_arithmetic<T>::value>::type write(\n      tag_t<T>, T value, size_t n = sizeof(T)) {\n    // We can't fail.\n    assert(n <= sizeof(T));\n    if (FOLLY_UNLIKELY(length() < sizeof(T))) {\n      ensureSlowNoinline(sizeof(T));\n    }\n    storeUnaligned<T>(queueCache_.writableData(), value);\n    queueCache_.appendUnsafe(n);\n  }\n\n  using Writable<QueueAppender>::pushAtMost;\n  size_t pushAtMost(const uint8_t* buf, size_t len) {\n    // Fill the current buffer\n    const size_t copyLength = std::min(len, length());\n    if (copyLength != 0) {\n      memcpy(writableData(), buf, copyLength);\n      queueCache_.appendUnsafe(copyLength);\n      buf += copyLength;\n    }\n    size_t remaining = len - copyLength;\n    // Allocate more buffers as necessary\n    while (remaining != 0) {\n      ensureSlow(growth_);\n      auto avail = std::min(length(), remaining);\n      memcpy(writableData(), buf, avail);\n      queueCache_.appendUnsafe(avail);\n      buf += avail;\n      remaining -= avail;\n    }\n    return len;\n  }\n\n  /**\n   * Inserts data at the current cursor position.\n   *\n   * @methodset Writing\n   */\n  void insert(std::unique_ptr<folly::IOBuf> buf, bool pack = true) {\n    if (buf) {\n      queueCache_.queue()->append(\n          std::move(buf), pack, /* allowTailReuse */ true);\n    }\n  }\n\n  void insert(const folly::IOBuf& buf, bool pack = true) {\n    queueCache_.queue()->append(buf, pack, /* allowTailReuse */ true);\n  }\n\n  /**\n   * Get a RWCursor for this IOBufQueue.\n   *\n   * @methodset Accessors\n   */\n  template <CursorAccess access>\n  explicit operator RWCursor<access>() {\n    return RWCursor<access>(*queueCache_.queue());\n  }\n\n  /**\n   * Get a RWCursor for the last n bytes of this IOBufQueue.\n   *\n   * @methodset Accessors\n   */\n  template <CursorAccess access>\n  RWCursor<access> tail(size_t n) {\n    RWCursor<access> result(\n        *queueCache_.queue(), typename RWCursor<access>::AtEnd{});\n    result -= n;\n    return result;\n  }\n\n  /**\n   * Remove n bytes from the end of this IOBufQueue.\n   *\n   * @methodset Modifiers\n   */\n  void trimEnd(size_t n) { queueCache_.queue()->trimEnd(n); }\n\n private:\n  folly::IOBufQueue::WritableRangeCache queueCache_{nullptr};\n  size_t growth_{0};\n  size_t maxGrowth_{0};\n\n  void resetGrowth(std::size_t minGrowth, std::size_t maxGrowth) noexcept {\n    CHECK_LE(growth_, maxGrowth_);\n    growth_ = minGrowth;\n    maxGrowth_ = maxGrowth;\n  }\n\n  FOLLY_NOINLINE void ensureSlowNoinline(size_t n) { ensureSlow(n); }\n\n  void ensureSlow(size_t n) {\n    // Reinstall our cache in case something else invalidated it.\n    queueCache_.fillCache();\n    // Recheck with updated cache, to avoid unnecessary allocation.\n    if (FOLLY_UNLIKELY(length() >= n)) {\n      return;\n    }\n    auto minBufSize = std::exchange(growth_, std::min(growth_ * 2, maxGrowth_));\n    queueCache_.queue()->append(IOBuf::create(std::max(n, minBufSize)));\n    DCHECK_GE(length(), n);\n  }\n};\n\n} // namespace io\n} // namespace folly\n\n#include <folly/io/Cursor-inl.h>\n"
  },
  {
    "path": "folly/io/FsUtil.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/FsUtil.h>\n\n#include <random>\n\n#include <folly/Exception.h>\n\n#ifdef __APPLE__\n#include <mach-o/dyld.h> // @manual\n#endif\n\n#if defined(_WIN32)\n#include <folly/portability/Windows.h>\n#endif\n\nnamespace bsys = ::boost::system;\n\nnamespace folly {\nnamespace fs {\n\nnamespace {\nbool skipPrefix(const path& pth, const path& prefix, path::const_iterator& it) {\n  it = pth.begin();\n  for (auto& p : prefix) {\n    if (it == pth.end()) {\n      return false;\n    }\n    if (p == \".\") {\n      // Should only occur at the end, if prefix ends with a slash\n      continue;\n    }\n    if (*it++ != p) {\n      return false;\n    }\n  }\n  return true;\n}\n} // namespace\n\nbool starts_with(const path& pth, const path& prefix) {\n  path::const_iterator it;\n  return skipPrefix(pth, prefix, it);\n}\n\npath remove_prefix(const path& pth, const path& prefix) {\n  path::const_iterator it;\n  if (!skipPrefix(pth, prefix, it)) {\n    throw filesystem_error(\n        \"Path does not start with prefix\",\n        pth,\n        prefix,\n        bsys::errc::make_error_code(bsys::errc::invalid_argument));\n  }\n\n  path p;\n  for (; it != pth.end(); ++it) {\n    p /= *it;\n  }\n\n  return p;\n}\n\npath canonical_parent(const path& pth, const path& base) {\n  return canonical(pth.parent_path(), base) / pth.filename();\n}\n\npath executable_path() {\n#ifdef __APPLE__\n  uint32_t size = 0;\n  _NSGetExecutablePath(nullptr, &size);\n  std::string buf(size - 1, '\\0');\n  auto data = const_cast<char*>(&*buf.data());\n  _NSGetExecutablePath(data, &size);\n  return path(std::move(buf));\n#elif defined(_WIN32)\n  WCHAR buf[MAX_PATH];\n  GetModuleFileNameW(NULL, buf, MAX_PATH);\n  return path(std::move(buf));\n#else\n  return read_symlink(\"/proc/self/exe\");\n#endif\n}\n\n[[maybe_unused]] static constexpr char const* hex_(char) {\n  return \"0123456789abcdef\";\n}\n[[maybe_unused]] static constexpr wchar_t const* hex_(wchar_t) {\n  return L\"0123456789abcdef\";\n}\n\nstd_fs::path unique_path_fn::operator()(std_fs::path const& model) const {\n  constexpr auto pin = std_fs::path::value_type('%');\n  constexpr auto hex = hex_(pin);\n  std::random_device rng;\n  auto cache = std::random_device::result_type{};\n  auto cache_size = 0;\n  auto result = model.native();\n  for (size_t i = 0; (i = result.find(pin, i)) < result.size(); ++i) {\n    if (cache_size == 0) {\n      cache = rng();\n      cache_size = sizeof(cache) * 2;\n    }\n    auto const index = (cache >> (4 * --cache_size)) & 0xf;\n    result[i] = path::value_type(hex[index]);\n  }\n  return std::move(result);\n}\n\n} // namespace fs\n} // namespace folly\n"
  },
  {
    "path": "folly/io/FsUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <filesystem>\n#include <boost/filesystem.hpp>\n\nnamespace folly {\nnamespace fs {\n\nnamespace std_fs = std::filesystem;\n\n// Functions defined in this file are meant to extend the\n// boost::filesystem library; functions will be named according to boost's\n// naming conventions instead of ours.  For convenience, import the\n// boost::filesystem namespace into folly::fs.\nusing namespace ::boost::filesystem;\n\n/**\n * Check whether \"path\" starts with \"prefix\".\n * That is, if prefix has n path elements, then the first n elements of\n * path must be the same as prefix.\n *\n * There is a special case if prefix ends with a slash:\n * /foo/bar/ is not a prefix of /foo/bar, but both /foo/bar and /foo/bar/\n * are prefixes of /foo/bar/baz.\n */\nbool starts_with(const path& p, const path& prefix);\n\n/**\n * If \"path\" starts with \"prefix\", return \"path\" with \"prefix\" removed.\n * Otherwise, throw filesystem_error.\n */\npath remove_prefix(const path& p, const path& prefix);\n\n/**\n * Canonicalize the parent path, leaving the filename (last component)\n * unchanged.  You may use this before creating a file instead of\n * boost::filesystem::canonical, which requires that the entire path exists.\n */\npath canonical_parent(const path& p, const path& basePath = current_path());\n\n/**\n * Get the path to the current executable.\n *\n * Note that this is not reliable and not recommended in general; it may not be\n * implemented on your platform (in which case it will throw), the executable\n * might have been moved or replaced while running, and applications comprising\n * of multiple executables should use some form of configuration system to\n * find the other executables rather than relying on relative paths from one\n * to another.\n *\n * So this should only be used for tests, logging, or other innocuous purposes.\n */\npath executable_path();\n\nstruct unique_path_fn {\n  std_fs::path operator()(\n      std_fs::path const& model = \"%%%%-%%%%-%%%%-%%%%\") const;\n};\nusing std_fs_unique_path_fn = unique_path_fn;\ninline constexpr std_fs_unique_path_fn std_fs_unique_path;\n\n} // namespace fs\n} // namespace folly\n"
  },
  {
    "path": "folly/io/GlobalShutdownSocketSet.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/GlobalShutdownSocketSet.h>\n\n#include <folly/Singleton.h>\n\nnamespace folly {\n\nnamespace {\nstruct PrivateTag {};\n} // namespace\n\nstatic Singleton<ShutdownSocketSet, PrivateTag> singleton;\n\nstd::shared_ptr<ShutdownSocketSet> tryGetShutdownSocketSet() {\n  return singleton.try_get();\n}\nReadMostlySharedPtr<ShutdownSocketSet> tryGetShutdownSocketSetFast() {\n  return singleton.try_get_fast();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/GlobalShutdownSocketSet.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/concurrency/memory/ReadMostlySharedPtr.h>\n#include <folly/io/ShutdownSocketSet.h>\n\nnamespace folly {\n\nstd::shared_ptr<ShutdownSocketSet> tryGetShutdownSocketSet();\nReadMostlySharedPtr<ShutdownSocketSet> tryGetShutdownSocketSetFast();\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/HugePages.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/portability/Windows.h>\n\n#include <folly/io/HugePages.h>\n\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <cctype>\n#include <cstring>\n\n#include <algorithm>\n#include <stdexcept>\n#include <system_error>\n\n#include <boost/regex.hpp>\n\n#include <folly/Conv.h>\n#include <folly/Format.h>\n#include <folly/Range.h>\n#include <folly/String.h>\n\n#include <folly/gen/Base.h>\n#include <folly/gen/File.h>\n#include <folly/gen/String.h>\n\nnamespace folly {\n\nnamespace {\n\n// Get the default huge page size\nsize_t getDefaultHugePageSize() {\n  // We need to parse /proc/meminfo\n  static const boost::regex regex(R\"!(Hugepagesize:\\s*(\\d+)\\s*kB)!\");\n  size_t pageSize = 0;\n  boost::cmatch match;\n\n  bool error = gen::byLine(\"/proc/meminfo\") | [&](StringPiece line) -> bool {\n    if (boost::regex_match(line.begin(), line.end(), match, regex)) {\n      StringPiece numStr(\n          line.begin() + match.position(1), size_t(match.length(1)));\n      pageSize = to<size_t>(numStr) * 1024; // in KiB\n      return false; // stop\n    }\n    return true;\n  };\n\n  if (error) {\n    throw std::runtime_error(\"Can't find default huge page size\");\n  }\n  return pageSize;\n}\n\n// Get raw huge page sizes (without mount points, they'll be filled later)\nHugePageSizeVec readRawHugePageSizes() {\n  // We need to parse file names from /sys/kernel/mm/hugepages\n  static const boost::regex regex(R\"!(hugepages-(\\d+)kB)!\");\n  boost::smatch match;\n  HugePageSizeVec vec;\n  fs::path path(\"/sys/kernel/mm/hugepages\");\n  if (fs::exists(path)) {\n    for (fs::directory_iterator it(path); it != fs::directory_iterator();\n         ++it) {\n      std::string filename(it->path().filename().string());\n      if (boost::regex_match(filename, match, regex)) {\n        StringPiece numStr(\n            filename.data() + match.position(1), size_t(match.length(1)));\n        vec.emplace_back(to<size_t>(numStr) * 1024);\n      }\n    }\n  }\n  return vec;\n}\n\n// Parse the value of a pagesize mount option\n// Format: number, optional K/M/G/T suffix, trailing junk allowed\nsize_t parsePageSizeValue(StringPiece value) {\n  static const boost::regex regex(R\"!((\\d+)([kmgt])?.*)!\", boost::regex::icase);\n  boost::cmatch match;\n  if (!boost::regex_match(value.begin(), value.end(), match, regex)) {\n    throw std::runtime_error(\"Invalid pagesize option\");\n  }\n  char c = '\\0';\n  if (match.length(2) != 0) {\n    c = char(tolower(value[size_t(match.position(2))]));\n  }\n  StringPiece numStr(value.data() + match.position(1), size_t(match.length(1)));\n  auto const size = to<size_t>(numStr);\n  auto const mult = [c] {\n    switch (c) {\n      case 't':\n        return 1ull << 40;\n      case 'g':\n        return 1ull << 30;\n      case 'm':\n        return 1ull << 20;\n      case 'k':\n        return 1ull << 10;\n      default:\n        return 1ull << 0;\n    }\n  }();\n  return size * mult;\n}\n\n/**\n * Get list of supported huge page sizes and their mount points, if\n * hugetlbfs file systems are mounted for those sizes.\n */\nHugePageSizeVec readHugePageSizes() {\n  HugePageSizeVec sizeVec = readRawHugePageSizes();\n  if (sizeVec.empty()) {\n    return sizeVec; // nothing to do\n  }\n  std::sort(sizeVec.begin(), sizeVec.end());\n\n  size_t defaultHugePageSize = getDefaultHugePageSize();\n\n  struct PageSizeLess {\n    bool operator()(const HugePageSize& a, size_t b) const {\n      return a.size < b;\n    }\n  };\n\n  // Read and parse /proc/mounts\n  std::vector<StringPiece> parts;\n  std::vector<StringPiece> options;\n\n  gen::byLine(\"/proc/mounts\") | gen::eachAs<StringPiece>() |\n      [&](StringPiece line) {\n        parts.clear();\n        split(' ', line, parts);\n        // device path fstype options uid gid\n        if (parts.size() != 6) {\n          throw std::runtime_error(\"Invalid /proc/mounts line\");\n        }\n        if (parts[2] != \"hugetlbfs\") {\n          return; // we only care about hugetlbfs\n        }\n\n        options.clear();\n        split(',', parts[3], options);\n        size_t pageSize = defaultHugePageSize;\n        // Search for the \"pagesize\" option, which must have a value\n        for (auto& option : options) {\n          // key=value\n          auto p = static_cast<const char*>(\n              memchr(option.data(), '=', option.size()));\n          if (!p) {\n            continue;\n          }\n          if (StringPiece(option.data(), p) != \"pagesize\") {\n            continue;\n          }\n          pageSize = parsePageSizeValue(StringPiece(p + 1, option.end()));\n          break;\n        }\n\n        auto pos = std::lower_bound(\n            sizeVec.begin(), sizeVec.end(), pageSize, PageSizeLess());\n        if (pos == sizeVec.end() || pos->size != pageSize) {\n          throw std::runtime_error(\"Mount page size not found\");\n        }\n        if (!pos->mountPoint.empty()) {\n          // Only one mount point per page size is allowed\n          return;\n        }\n\n        // Store mount point\n        fs::path path(parts[1].begin(), parts[1].end());\n        struct stat st;\n        const int ret = stat(path.string().c_str(), &st);\n        if (ret == -1 && errno == ENOENT) {\n          return;\n        }\n        checkUnixError(ret, \"stat hugepage mountpoint failed\");\n        pos->mountPoint = fs::canonical(path);\n        pos->device = st.st_dev;\n      };\n\n  return sizeVec;\n}\n\n} // namespace\n\nconst HugePageSizeVec& getHugePageSizes() {\n  static HugePageSizeVec sizes = readHugePageSizes();\n  return sizes;\n}\n\nconst HugePageSize* getHugePageSize(size_t size) {\n  // Linear search is just fine.\n  for (auto& p : getHugePageSizes()) {\n    if (p.mountPoint.empty()) {\n      continue;\n    }\n    if (size == 0 || size == p.size) {\n      return &p;\n    }\n  }\n  return nullptr;\n}\n\nconst HugePageSize* getHugePageSizeForDevice(dev_t device) {\n  // Linear search is just fine.\n  for (auto& p : getHugePageSizes()) {\n    if (p.mountPoint.empty()) {\n      continue;\n    }\n    if (device == p.device) {\n      return &p;\n    }\n  }\n  return nullptr;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/HugePages.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <boost/operators.hpp>\n\n#include <folly/Range.h>\n#include <folly/io/FsUtil.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\nstruct HugePageSize : private boost::totally_ordered<HugePageSize> {\n  explicit HugePageSize(size_t s) : size(s) {}\n\n  fs::path filePath(const fs::path& relpath) const {\n    return mountPoint / relpath;\n  }\n\n  size_t size = 0;\n  fs::path mountPoint;\n  dev_t device = 0;\n};\n\ninline bool operator<(const HugePageSize& a, const HugePageSize& b) {\n  return a.size < b.size;\n}\n\ninline bool operator==(const HugePageSize& a, const HugePageSize& b) {\n  return a.size == b.size;\n}\n\n/**\n * Vector of (huge_page_size, mount_point), sorted by huge_page_size.\n * mount_point might be empty if no hugetlbfs file system is mounted for\n * that size.\n */\nusing HugePageSizeVec = std::vector<HugePageSize>;\n\n/**\n * Get list of supported huge page sizes and their mount points, if\n * hugetlbfs file systems are mounted for those sizes.\n */\nconst HugePageSizeVec& getHugePageSizes();\n\n/**\n * Return the mount point for the requested huge page size.\n * 0 = use smallest available.\n * Returns nullptr if the requested huge page size is not available.\n */\nconst HugePageSize* getHugePageSize(size_t size = 0);\n\n/**\n * Return the huge page size for a device.\n * returns nullptr if device does not refer to a huge page filesystem.\n */\nconst HugePageSize* getHugePageSizeForDevice(dev_t device);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/IOBuf.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __STDC_LIMIT_MACROS\n#define __STDC_LIMIT_MACROS\n#endif\n\n#include <folly/io/IOBuf.h>\n\n#include <cassert>\n#include <cstdint>\n#include <cstdlib>\n#include <limits>\n#include <stdexcept>\n#include <type_traits>\n\n#include <folly/Conv.h>\n#include <folly/Likely.h>\n#include <folly/Memory.h>\n#include <folly/ScopeGuard.h>\n#include <folly/hash/SpookyHashV2.h>\n#include <folly/io/Cursor.h>\n#include <folly/lang/Align.h>\n#include <folly/lang/CheckedMath.h>\n#include <folly/lang/Exception.h>\n#include <folly/lang/Hint.h>\n#include <folly/memory/Malloc.h>\n#include <folly/memory/SanitizeAddress.h>\n\n/*\n * Callbacks that will be invoked when IOBuf allocates or frees memory.\n * Note that io_buf_alloc_cb() will also be invoked when IOBuf takes ownership\n * of a malloc-allocated buffer, even if it was allocated earlier by another\n * part of the code.\n *\n * By default these are unimplemented, but programs can define these functions\n * to perform their own custom logic on memory allocation.  This is intended\n * primarily to help programs track memory usage and possibly take action\n * when thresholds are hit.  Callers should generally avoid performing any\n * expensive work in these callbacks, since they may be called from arbitrary\n * locations in the code that use IOBuf, possibly while holding locks.\n */\n\n#if FOLLY_HAVE_WEAK_SYMBOLS\nFOLLY_ATTR_WEAK void io_buf_alloc_cb(void* /*ptr*/, size_t /*size*/) noexcept;\nFOLLY_ATTR_WEAK void io_buf_free_cb(void* /*ptr*/, size_t /*size*/) noexcept;\n#else\nstatic void (*io_buf_alloc_cb)(void* /*ptr*/, size_t /*size*/) noexcept =\n    nullptr;\nstatic void (*io_buf_free_cb)(void* /*ptr*/, size_t /*size*/) noexcept =\n    nullptr;\n#endif\n\nusing std::unique_ptr;\n\nnamespace {\n\n// When create() is called for buffers less than kDefaultCombinedBufSize,\n// we allocate a single combined memory segment for the IOBuf and the data\n// together.  See the comments for createCombined()/createSeparate() for more\n// details.\n//\n// (The size of 1k is largely just a guess here.  We could could probably do\n// benchmarks of real applications to see if adjusting this number makes a\n// difference.  Callers that know their exact use case can also explicitly\n// call createCombined() or createSeparate().)\nconstexpr size_t kDefaultCombinedBufSize = 1024;\nconstexpr size_t kMaxIOBufSize = std::numeric_limits<size_t>::max() >> 1;\n\n// Helper function for IOBuf::takeOwnership()\n// The user's free function is not allowed to throw.\n// (We are already in the middle of throwing an exception, so\n// we cannot let this exception go unhandled.)\nvoid takeOwnershipError(\n    bool freeOnError,\n    void* buf,\n    folly::IOBuf::FreeFunction freeFn,\n    void* userData) noexcept {\n  if (!freeOnError) {\n    return;\n  }\n  if (!freeFn) {\n    free(buf);\n    return;\n  }\n  freeFn(buf, userData);\n}\n\n} // namespace\n\nnamespace folly {\n\n// use free for size >= 4GB\n// since we can store only 32 bits in the size var\nstruct IOBuf::HeapPrefix {\n  HeapPrefix(uint8_t rc, uint32_t sz, bool hmr)\n      : refcount(rc), hasMemoryResource(hmr), size(sz) {}\n  ~HeapPrefix() {\n    // Reset magic to 0 on destruction.  This is solely for debugging purposes\n    // to help catch bugs where someone tries to use HeapStorage after it has\n    // been deleted.\n    magic = 0;\n  }\n\n  constexpr static uint16_t kHeapMagic = 0xa5a5;\n\n  uint16_t magic = kHeapMagic;\n  std::atomic<uint8_t> refcount; // 1 for IOBuf and 1 for SharedInfo (+ data).\n  bool hasMemoryResource;\n  uint32_t size;\n};\n\nstruct IOBuf::HeapStorage {\n  HeapPrefix prefix;\n  // The IOBuf is last in the HeapStorage object.\n  // This way operator new will work even if allocating a subclass of IOBuf\n  // that requires more space.\n  folly::IOBuf buf;\n\n  // Only IOBuf is in use.\n  constexpr static uint8_t kInitialRefcount = 1;\n\n private:\n  // This function exists only to have a scope to do the static_asserts().\n  static void checkInvariants() {\n    // Make sure jemalloc allocates from the 64-byte class.\n    static_assert(sizeof(HeapPrefix) == 8);\n    static_assert(sizeof(IOBuf) <= 56);\n    static_assert(sizeof(HeapStorage) <= 64);\n  }\n};\n\nstruct alignas(folly::max_align_v) IOBuf::HeapFullStorage {\n  HeapStorage hs;\n  SharedInfo shared;\n\n  // Both IOBuf and SharedInfo (possibly with attached data) are in use.\n  constexpr static uint8_t kInitialRefcount = 2;\n\n private:\n  static void checkInvariants() {\n    static_assert(offsetof(HeapFullStorage, hs) == 0);\n  }\n};\n\nIOBuf::SharedInfo::SharedInfo(FreeFunction fn, void* arg, StorageType st)\n    : freeFn(fn), userData(arg), storageType(st) {}\n\nvoid IOBuf::SharedInfo::invokeAndDeleteEachObserver(\n    SharedInfoObserverEntryBase* observerListHead, ObserverCb cb) noexcept {\n  if (observerListHead && cb) {\n    // break the chain\n    observerListHead->prev->next = nullptr;\n    auto entry = observerListHead;\n    while (entry) {\n      auto tmp = entry->next;\n      cb(*entry);\n      delete entry;\n      entry = tmp;\n    }\n  }\n}\n\n// storageType is stored in info, but info may be already deleted in the\n// kExtBuffer case, so we need to pass it separately.\nvoid IOBuf::SharedInfo::releaseStorage(\n    IOBuf* parent, StorageType storageType, SharedInfo* info) noexcept {\n  if (storageType != StorageType::kExtBuffer) {\n    DCHECK_EQ((int)storageType, (int)info->storageType);\n  }\n\n  switch (storageType) {\n    case StorageType::kInvalid:\n      compiler_may_unsafely_assume_unreachable();\n    case StorageType::kAllocated:\n      delete info;\n      break;\n    case StorageType::kHeapFullStorage: {\n      auto storageAddr =\n          reinterpret_cast<uint8_t*>(info) - offsetof(HeapFullStorage, shared);\n      auto storage = reinterpret_cast<HeapFullStorage*>(storageAddr);\n      info->~SharedInfo();\n      if (&storage->hs.buf == parent) {\n        // We were called through the same IOBuf that owns the storage, so there\n        // cannot be a concurrent refcount decrement, and we can avoid an\n        // expensive atomic RMW operation.\n        DCHECK_EQ(\n            storage->hs.prefix.refcount.load(std::memory_order_relaxed),\n            HeapFullStorage::kInitialRefcount);\n        storage->hs.prefix.refcount.store(1, std::memory_order_relaxed);\n      } else {\n        IOBuf::decrementStorageRefcount(&storage->hs);\n      }\n      break;\n    }\n    case StorageType::kExtBuffer:\n      break; // Storage was already freed.\n  }\n}\n\nvoid* IOBuf::operator new(size_t size) {\n  DCHECK_GE(size, sizeof(IOBuf));\n  auto [storage, mallocSize] =\n      allocateStorage<HeapStorage>(nullptr, size - sizeof(IOBuf));\n  return &storage->buf;\n}\n\nvoid* IOBuf::operator new(size_t /* size */, void* ptr) {\n  return ptr;\n}\n\nvoid IOBuf::operator delete(void* ptr) {\n  auto storageAddr = static_cast<uint8_t*>(ptr) - offsetof(HeapStorage, buf);\n  auto storage = reinterpret_cast<HeapStorage*>(storageAddr);\n  decrementStorageRefcount(storage);\n}\n\nvoid IOBuf::operator delete(void* /* ptr */, void* /* placement */) {\n  // Provide matching operator for `IOBuf::new` to avoid MSVC compilation\n  // warning (C4291) about memory leak when exception is thrown in the\n  // constructor.\n}\n\ntemplate <class StorageType>\n/* static */ std::pair<StorageType*, size_t> IOBuf::allocateStorage(\n    std::pmr::memory_resource* mr, size_t additionalBuffer) {\n  size_t mallocSize;\n  StorageType* storage;\n  uint32_t storedSize;\n  bool hasMemoryResource;\n\n  if (mr == nullptr) {\n    mallocSize = sizeof(StorageType);\n    if (additionalBuffer > 0) {\n      mallocSize = goodMallocSize(mallocSize + additionalBuffer);\n    }\n    storedSize = static_cast<uint32_t>(mallocSize);\n    if (storedSize != mallocSize) {\n      // If we cannot store the size in 32 bits, fall back to non-sized free.\n      storedSize = 0;\n    }\n    storage = static_cast<StorageType*>(checkedMalloc(mallocSize));\n    hasMemoryResource = false;\n  } else {\n#if FOLLY_HAS_MEMORY_RESOURCE\n    // We only need to store the memory_resource pointer when non-null, so we\n    // conditionally add a prefix of size max_align_v to the storage to fit the\n    // pointer.\n    constexpr size_t kPtrSize = sizeof(std::pmr::memory_resource*);\n    static_assert(alignof(StorageType) <= max_align_v);\n    static_assert(kPtrSize <= max_align_v);\n    // We don't know the memory_resource implementation, so we cannot assume\n    // that goodMallocSize() is good.\n    mallocSize = max_align_v + sizeof(StorageType) + additionalBuffer;\n    storedSize = static_cast<uint32_t>(mallocSize);\n    if (storedSize != mallocSize) {\n      // We only have 32 bits to store the size, and with the PMR interface we\n      // cannot fall back to non-sized free.\n      throw_exception<std::bad_alloc>();\n    }\n    auto ptr = static_cast<uint8_t*>(mr->allocate(mallocSize));\n    memcpy(ptr + max_align_v - kPtrSize, &mr, kPtrSize);\n    storage = reinterpret_cast<StorageType*>(ptr + max_align_v);\n    hasMemoryResource = true;\n#else\n    compiler_may_unsafely_assume_unreachable();\n#endif /* FOLLY_HAS_MEMORY_RESOURCE */\n  }\n\n  new (storage)\n      HeapPrefix(StorageType::kInitialRefcount, storedSize, hasMemoryResource);\n\n  if (io_buf_alloc_cb) {\n    io_buf_alloc_cb(storage, mallocSize);\n  }\n\n  return {storage, mallocSize};\n}\n\n/* static */ std::pmr::memory_resource* IOBuf::getMemoryResource(\n    const HeapStorage* storage) {\n  if (!storage->prefix.hasMemoryResource) {\n    return nullptr;\n  }\n  std::pmr::memory_resource* mr;\n  constexpr size_t kPtrSize = sizeof(std::pmr::memory_resource*);\n  memcpy(&mr, reinterpret_cast<const uint8_t*>(storage) - kPtrSize, kPtrSize);\n  return mr;\n}\n\n/* static */ void IOBuf::freeStorage(HeapStorage* storage) {\n  size_t size = storage->prefix.size;\n  auto mr = getMemoryResource(storage);\n\n  storage->prefix.HeapPrefix::~HeapPrefix();\n\n  if (mr != nullptr) {\n#if FOLLY_HAS_MEMORY_RESOURCE\n    auto p = reinterpret_cast<uint8_t*>(storage) - max_align_v;\n    mr->deallocate(p, size);\n#else\n    compiler_may_unsafely_assume_unreachable();\n#endif /* FOLLY_HAS_MEMORY_RESOURCE */\n  } else if (FOLLY_LIKELY(size)) {\n    if (io_buf_free_cb) {\n      io_buf_free_cb(storage, size);\n    }\n    sizedFree(storage, size);\n  } else {\n    free(storage);\n  }\n}\n\nvoid IOBuf::decrementStorageRefcount(HeapStorage* storage) noexcept {\n  CHECK_EQ(storage->prefix.magic, HeapPrefix::kHeapMagic);\n\n  auto rc = storage->prefix.refcount.load(std::memory_order_acquire);\n  DCHECK_LE(rc, HeapFullStorage::kInitialRefcount);\n  if (rc > 1 &&\n      storage->prefix.refcount.fetch_sub(1, std::memory_order_acq_rel) > 1) {\n    return; // Storage still in use.\n  }\n\n  // The storage space is now unused, we can free it.\n  freeStorage(storage);\n}\n\nIOBuf::IOBuf(CreateOp, std::size_t capacity)\n    : length_(0), data_(nullptr), next_(this), prev_(this) {\n  SharedInfo* info;\n  allocExtBuffer(capacity, &buf_, &info, &capacity_);\n  sharedInfo_ = info;\n  data_ = buf_;\n}\n\nIOBuf::IOBuf(\n    CopyBufferOp /* op */,\n    const void* buf,\n    std::size_t size,\n    std::size_t headroom,\n    std::size_t minTailroom)\n    : length_(0), data_(nullptr), next_(this), prev_(this) {\n  std::size_t capacity = 0;\n  if (!checked_add(&capacity, size, headroom, minTailroom) ||\n      capacity > kMaxIOBufSize) {\n    throw_exception<std::bad_alloc>();\n  }\n\n  SharedInfo* info;\n  allocExtBuffer(capacity, &buf_, &info, &capacity_);\n  sharedInfo_ = info;\n  data_ = buf_;\n\n  advance(headroom);\n  if (size > 0) {\n    assert(buf != nullptr);\n    memcpy(writableData(), buf, size);\n    append(size);\n  }\n}\n\nIOBuf::IOBuf(\n    CopyBufferOp op,\n    ByteRange br,\n    std::size_t headroom,\n    std::size_t minTailroom)\n    : IOBuf(op, br.data(), br.size(), headroom, minTailroom) {}\n\nunique_ptr<IOBuf> IOBuf::create(std::size_t capacity) {\n  if (capacity > kMaxIOBufSize) {\n    throw_exception<std::bad_alloc>();\n  }\n\n  // For smaller-sized buffers, allocate the IOBuf, SharedInfo, and the buffer\n  // all with a single allocation.\n  //\n  // We don't do this for larger buffers since it can be wasteful if the user\n  // needs to reallocate the buffer but keeps using the same IOBuf object.\n  // In this case we can't free the data space until the IOBuf is also\n  // destroyed.  Callers can explicitly call createCombined() or\n  // createSeparate() if they know their use case better, and know if they are\n  // likely to reallocate the buffer later.\n  if (capacity <= kDefaultCombinedBufSize) {\n    return createCombined(capacity);\n  }\n\n  // if we have nallocx, we want to allocate the capacity and the overhead in\n  // a single allocation only if we do not cross into the next allocation class\n  // for some buffer sizes, this can use about 25% extra memory\n  if (canNallocx()) {\n    auto mallocSize = goodMallocSize(capacity);\n    // round capacity to a multiple of 8\n    size_t minSize = ((capacity + 7) & ~7) + sizeof(SharedInfo);\n    // if we do not have space for the overhead, allocate the mem separately\n    if (mallocSize < minSize) {\n      auto* buf = checkedMalloc(mallocSize);\n      return takeOwnership(SIZED_FREE, buf, mallocSize, 0, 0);\n    }\n  }\n\n  return createSeparate(capacity);\n}\n\nunique_ptr<IOBuf> IOBuf::createCombined(std::size_t capacity) {\n  if (capacity > kMaxIOBufSize) {\n    throw_exception<std::bad_alloc>();\n  }\n\n  // To save a memory allocation, allocate space for the IOBuf object, the\n  // SharedInfo struct, and the data itself all with a single call to malloc().\n  auto [storage, mallocSize] =\n      allocateStorage<HeapFullStorage>(nullptr, capacity);\n  // No free function, data lifetime is tied to SharedInfo, whole storage will\n  // be deallocated when both IOBuf and SharedInfo are gone.\n  new (&storage->shared) SharedInfo(\n      [](void*, void*) {}, nullptr, SharedInfo::StorageType::kHeapFullStorage);\n\n  auto bufAddr = reinterpret_cast<uint8_t*>(storage) + sizeof(HeapFullStorage);\n  uint8_t* storageEnd = reinterpret_cast<uint8_t*>(storage) + mallocSize;\n  auto actualCapacity = size_t(storageEnd - bufAddr);\n  unique_ptr<IOBuf> ret(new (&storage->hs.buf) IOBuf(\n      InternalConstructor(),\n      &storage->shared,\n      bufAddr,\n      actualCapacity,\n      bufAddr,\n      0));\n  return ret;\n}\n\nunique_ptr<IOBuf> IOBuf::createSeparate(std::size_t capacity) {\n  return std::make_unique<IOBuf>(CREATE, capacity);\n}\n\nunique_ptr<IOBuf> IOBuf::createChain(\n    size_t totalCapacity, std::size_t maxBufCapacity) {\n  unique_ptr<IOBuf> out =\n      create(std::min(totalCapacity, size_t(maxBufCapacity)));\n  size_t allocatedCapacity = out->capacity();\n\n  while (allocatedCapacity < totalCapacity) {\n    unique_ptr<IOBuf> newBuf = create(\n        std::min(totalCapacity - allocatedCapacity, size_t(maxBufCapacity)));\n    allocatedCapacity += newBuf->capacity();\n    out->appendToChain(std::move(newBuf));\n  }\n\n  return out;\n}\n\nsize_t IOBuf::goodSize(size_t minCapacity, CombinedOption combined) {\n  if (combined == CombinedOption::DEFAULT) {\n    combined = minCapacity <= kDefaultCombinedBufSize\n        ? CombinedOption::COMBINED\n        : CombinedOption::SEPARATE;\n  }\n  size_t overhead;\n  if (combined == CombinedOption::COMBINED) {\n    overhead = sizeof(HeapFullStorage);\n  } else {\n    // Pad minCapacity to a multiple of 8\n    minCapacity = (minCapacity + 7) & ~7;\n    overhead = sizeof(SharedInfo);\n  }\n  size_t goodSize = folly::goodMallocSize(minCapacity + overhead);\n  return goodSize - overhead;\n}\n\nIOBuf::IOBuf(\n    TakeOwnershipOp,\n    void* buf,\n    std::size_t capacity,\n    std::size_t offset,\n    std::size_t length,\n    FreeFunction freeFn,\n    void* userData,\n    bool freeOnError)\n    : length_(length),\n      data_(static_cast<uint8_t*>(buf) + offset),\n      capacity_(capacity),\n      buf_(static_cast<uint8_t*>(buf)),\n      next_(this),\n      prev_(this) {\n  // do not allow only user data without a freeFn\n  // since we use that for folly::sizedFree\n  DCHECK(!userData || (userData && freeFn));\n\n  auto rollback = makeGuard([&] { //\n    takeOwnershipError(freeOnError, buf, freeFn, userData);\n  });\n  sharedInfo_ =\n      new SharedInfo(freeFn, userData, SharedInfo::StorageType::kAllocated);\n  rollback.dismiss();\n}\n\nIOBuf::IOBuf(\n    TakeOwnershipOp,\n    SizedFree,\n    void* buf,\n    std::size_t capacity,\n    std::size_t offset,\n    std::size_t length,\n    bool freeOnError)\n    : length_(length),\n      data_(static_cast<uint8_t*>(buf) + offset),\n      capacity_(capacity),\n      buf_(static_cast<uint8_t*>(buf)),\n      next_(this),\n      prev_(this) {\n  auto rollback = makeGuard([&] { //\n    takeOwnershipError(freeOnError, buf, nullptr, nullptr);\n  });\n  sharedInfo_ = new SharedInfo(\n      nullptr,\n      reinterpret_cast<void*>(capacity),\n      SharedInfo::StorageType::kAllocated);\n  rollback.dismiss();\n\n  if (io_buf_alloc_cb && capacity) {\n    io_buf_alloc_cb(buf, capacity);\n  }\n}\n\nunique_ptr<IOBuf> IOBuf::takeOwnershipImpl(\n    void* buf,\n    std::size_t capacity,\n    std::size_t offset,\n    std::size_t length,\n    FreeFunction freeFn,\n    void* userData,\n    bool freeOnError,\n    TakeOwnershipOption option,\n    std::pmr::memory_resource* mr) {\n  // do not allow only user data without a freeFn\n  // since we use that for folly::sizedFree\n  DCHECK(\n      !userData || (userData && freeFn) ||\n      (userData && !freeFn && (option == TakeOwnershipOption::STORE_SIZE)));\n\n  HeapFullStorage* storage = nullptr;\n  auto rollback = makeGuard([&] {\n    if (storage) {\n      freeStorage(reinterpret_cast<HeapStorage*>(storage));\n    }\n    takeOwnershipError(freeOnError, buf, freeFn, userData);\n  });\n\n  if (capacity > kMaxIOBufSize) {\n    throw_exception<std::bad_alloc>();\n  }\n\n  size_t mallocSize;\n  std::tie(storage, mallocSize) = allocateStorage<HeapFullStorage>(mr);\n  new (&storage->shared)\n      SharedInfo(freeFn, userData, SharedInfo::StorageType::kHeapFullStorage);\n\n  auto result = unique_ptr<IOBuf>(new (&storage->hs.buf) IOBuf(\n      InternalConstructor(),\n      &storage->shared,\n      static_cast<uint8_t*>(buf),\n      capacity,\n      static_cast<uint8_t*>(buf) + offset,\n      length));\n\n  rollback.dismiss();\n\n  if (io_buf_alloc_cb && userData && !freeFn &&\n      (option == TakeOwnershipOption::STORE_SIZE)) {\n    // Even though we did not allocate the buffer, call io_buf_alloc_cb() since\n    // we will call io_buf_free_cb() on destruction, and we want these calls to\n    // be 1:1.\n    io_buf_alloc_cb(buf, capacity);\n  }\n\n  return result;\n}\n\nIOBuf::IOBuf(WrapBufferOp, const void* buf, std::size_t capacity) noexcept\n    : IOBuf(\n          InternalConstructor(),\n          nullptr,\n          // We cast away the const-ness of the buffer here.\n          // This is okay since IOBuf users must use unshare() to create a copy\n          // of this buffer before writing to the buffer.\n          static_cast<uint8_t*>(const_cast<void*>(buf)),\n          capacity,\n          static_cast<uint8_t*>(const_cast<void*>(buf)),\n          capacity) {}\n\nIOBuf::IOBuf(WrapBufferOp op, ByteRange br) noexcept\n    : IOBuf(op, br.data(), br.size()) {}\n\nunique_ptr<IOBuf> IOBuf::wrapBuffer(const void* buf, std::size_t capacity) {\n  return std::make_unique<IOBuf>(WRAP_BUFFER, buf, capacity);\n}\n\nIOBuf IOBuf::wrapBufferAsValue(const void* buf, std::size_t capacity) noexcept {\n  return IOBuf(WrapBufferOp::WRAP_BUFFER, buf, capacity);\n}\n\nIOBuf::IOBuf() noexcept = default;\n\nIOBuf::IOBuf(IOBuf&& other) noexcept\n    : length_(other.length_),\n      data_(other.data_),\n      capacity_(other.capacity_),\n      buf_(other.buf_),\n      sharedInfo_(other.sharedInfo_) {\n  // Reset other so it is a clean state to be destroyed.\n  other.data_ = nullptr;\n  other.buf_ = nullptr;\n  other.length_ = 0;\n  other.capacity_ = 0;\n  other.sharedInfo_ = nullptr;\n\n  // If other was part of the chain, assume ownership of the rest of its chain.\n  // (It's only valid to perform move assignment on the head of a chain.)\n  if (other.next_ != &other) {\n    next_ = other.next_;\n    next_->prev_ = this;\n    other.next_ = &other;\n\n    prev_ = other.prev_;\n    prev_->next_ = this;\n    other.prev_ = &other;\n  }\n\n  // Sanity check to make sure that other is in a valid state to be destroyed.\n  DCHECK_EQ(other.prev_, &other);\n  DCHECK_EQ(other.next_, &other);\n}\n\nIOBuf::IOBuf(const IOBuf& other) {\n  *this = other.cloneAsValue();\n}\n\nIOBuf::IOBuf(\n    InternalConstructor,\n    SharedInfo* sharedInfo,\n    uint8_t* buf,\n    std::size_t capacity,\n    uint8_t* data,\n    std::size_t length) noexcept\n    : length_(length),\n      data_(data),\n      capacity_(capacity),\n      buf_(buf),\n      next_(this),\n      prev_(this),\n      sharedInfo_(sharedInfo) {\n  assert(data >= buf);\n  assert(intptr_t(data) + length <= intptr_t(buf) + capacity);\n\n  if (folly::asan_region_is_poisoned(buf, capacity)) {\n    // If we know it's a poisoned region, access it to trigger ASAN reporting.\n    memset(buf, 0, capacity);\n  }\n}\n\nIOBuf::~IOBuf() {\n  // Destroying an IOBuf destroys the entire chain.\n  // Users of IOBuf should only explicitly delete the head of any chain.\n  // The other elements in the chain will be automatically destroyed.\n  while (next_ != this) {\n    // Since unlink() returns unique_ptr() and we don't store it,\n    // it will automatically delete the unlinked element.\n    (void)next_->unlink();\n  }\n\n  decrementRefcount();\n}\n\nIOBuf& IOBuf::operator=(IOBuf&& other) noexcept {\n  if (this == &other) {\n    return *this;\n  }\n\n  // If we are part of a chain, delete the rest of the chain.\n  while (next_ != this) {\n    // Since unlink() returns unique_ptr() and we don't store it,\n    // it will automatically delete the unlinked element.\n    (void)next_->unlink();\n  }\n\n  // Decrement our refcount on the current buffer\n  decrementRefcount();\n\n  // Take ownership of the other buffer's data\n  data_ = other.data_;\n  buf_ = other.buf_;\n  length_ = other.length_;\n  capacity_ = other.capacity_;\n  sharedInfo_ = other.sharedInfo_;\n  // Reset other so it is a clean state to be destroyed.\n  other.data_ = nullptr;\n  other.buf_ = nullptr;\n  other.length_ = 0;\n  other.capacity_ = 0;\n  other.sharedInfo_ = nullptr;\n\n  // If other was part of the chain, assume ownership of the rest of its chain.\n  // (It's only valid to perform move assignment on the head of a chain.)\n  if (other.next_ != &other) {\n    next_ = other.next_;\n    next_->prev_ = this;\n    other.next_ = &other;\n\n    prev_ = other.prev_;\n    prev_->next_ = this;\n    other.prev_ = &other;\n  }\n\n  // Sanity check to make sure that other is in a valid state to be destroyed.\n  DCHECK_EQ(other.prev_, &other);\n  DCHECK_EQ(other.next_, &other);\n\n  return *this;\n}\n\nIOBuf& IOBuf::operator=(const IOBuf& other) {\n  if (this != &other) {\n    *this = IOBuf(other);\n  }\n  return *this;\n}\n\nbool IOBuf::empty() const noexcept {\n  const IOBuf* current = this;\n  do {\n    if (current->length() != 0) {\n      return false;\n    }\n    current = current->next_;\n  } while (current != this);\n  return true;\n}\n\nsize_t IOBuf::countChainElements() const noexcept {\n  size_t numElements = 1;\n  for (IOBuf* current = next_; current != this; current = current->next_) {\n    ++numElements;\n  }\n  return numElements;\n}\n\nstd::size_t IOBuf::computeChainDataLength() const noexcept {\n  std::size_t fullLength = length_;\n  for (IOBuf* current = next_; current != this; current = current->next_) {\n    fullLength += current->length_;\n  }\n  return fullLength;\n}\n\nstd::size_t IOBuf::computeChainCapacity() const noexcept {\n  std::size_t fullCapacity = capacity_;\n  for (IOBuf* current = next_; current != this; current = current->next_) {\n    fullCapacity += current->capacity_;\n  }\n  return fullCapacity;\n}\n\nvoid IOBuf::appendToChain(unique_ptr<IOBuf>&& iobuf) {\n  // Take ownership of the specified IOBuf\n  IOBuf* other = iobuf.release();\n\n  // Remember the pointer to the tail of the other chain\n  IOBuf* otherTail = other->prev_;\n\n  // Hook up prev_->next_ to point at the start of the other chain,\n  // and other->prev_ to point at prev_\n  prev_->next_ = other;\n  other->prev_ = prev_;\n\n  // Hook up otherTail->next_ to point at us,\n  // and prev_ to point back at otherTail,\n  otherTail->next_ = this;\n  prev_ = otherTail;\n}\n\nunique_ptr<IOBuf> IOBuf::cloneImpl(std::pmr::memory_resource* mr) const {\n  auto tmp = cloneOneImpl(mr);\n\n  for (IOBuf* current = next_; current != this; current = current->next_) {\n    tmp->appendToChain(current->cloneOneImpl(mr));\n  }\n\n  return tmp;\n}\n\nunique_ptr<IOBuf> IOBuf::cloneOneImpl(std::pmr::memory_resource* mr) const {\n  if (sharedInfo_) {\n    sharedInfo_->refcount.fetch_add(1, std::memory_order_relaxed);\n  }\n\n  auto [storage, mallocSize] = allocateStorage<HeapStorage>(mr);\n  return unique_ptr<IOBuf>{new (&storage->buf) IOBuf(\n      InternalConstructor(), sharedInfo_, buf_, capacity_, data_, length_)};\n}\n\nunique_ptr<IOBuf> IOBuf::cloneCoalesced() const {\n  return std::make_unique<IOBuf>(cloneCoalescedAsValue());\n}\n\nunique_ptr<IOBuf> IOBuf::cloneCoalescedWithHeadroomTailroom(\n    std::size_t newHeadroom, std::size_t newTailroom) const {\n  return std::make_unique<IOBuf>(\n      cloneCoalescedAsValueWithHeadroomTailroom(newHeadroom, newTailroom));\n}\n\nIOBuf IOBuf::cloneAsValue() const {\n  auto tmp = cloneOneAsValue();\n\n  for (IOBuf* current = next_; current != this; current = current->next_) {\n    tmp.appendToChain(current->cloneOne());\n  }\n\n  return tmp;\n}\n\nIOBuf IOBuf::cloneOneAsValue() const {\n  if (sharedInfo_) {\n    sharedInfo_->refcount.fetch_add(1, std::memory_order_relaxed);\n  }\n  return IOBuf(\n      InternalConstructor(), sharedInfo_, buf_, capacity_, data_, length_);\n}\n\nIOBuf IOBuf::cloneCoalescedAsValue() const {\n  const std::size_t newHeadroom = headroom();\n  const std::size_t newTailroom = prev()->tailroom();\n  return cloneCoalescedAsValueWithHeadroomTailroom(newHeadroom, newTailroom);\n}\n\nIOBuf IOBuf::cloneCoalescedAsValueWithHeadroomTailroom(\n    std::size_t newHeadroom, std::size_t newTailroom) const {\n  if (isChained() || newHeadroom != headroom()) {\n    // Fall through to slow code path.\n  } else if (newTailroom == tailroom()) {\n    return cloneOneAsValue();\n  } else if (newTailroom < tailroom()) {\n    const std::size_t newCapacity =\n        goodExtBufferSize(length_ + newHeadroom + newTailroom) -\n        sizeof(SharedInfo);\n    if (tailroom() <= newCapacity - newHeadroom - length_) {\n      return cloneOneAsValue();\n    }\n  }\n\n  // Coalesce into newBuf\n  const std::size_t newLength = computeChainDataLength();\n  const std::size_t newCapacity = newLength + newHeadroom + newTailroom;\n  IOBuf newBuf{CREATE, newCapacity};\n  newBuf.advance(newHeadroom);\n\n  auto current = this;\n  do {\n    if (current->length() > 0) {\n      DCHECK_NOTNULL(current->data());\n      DCHECK_LE(current->length(), newBuf.tailroom());\n      memcpy(newBuf.writableTail(), current->data(), current->length());\n      newBuf.append(current->length());\n    }\n    current = current->next();\n  } while (current != this);\n\n  DCHECK_EQ(newLength, newBuf.length());\n  DCHECK_EQ(newHeadroom, newBuf.headroom());\n  DCHECK_LE(newTailroom, newBuf.tailroom());\n\n  return newBuf;\n}\n\nstd::unique_ptr<IOBuf> IOBuf::maybeSplitTail() {\n  if (isSharedOne()) {\n    return nullptr;\n  }\n\n  const size_t tailSize = tailroom();\n\n  // If this is already a split tail, clone the (clone of the) original buffer\n  // instead of this wrapper, to avoid deep recursion in the free function.\n  static constexpr auto freeFn = [](void*, void* p) {\n    reinterpret_cast<IOBuf*>(p)->~IOBuf();\n  };\n  auto origBuf =\n      getFreeFn() == freeFn ? reinterpret_cast<IOBuf*>(getUserData()) : this;\n  DCHECK_EQ(\n      reinterpret_cast<const void*>(origBuf->bufferEnd()),\n      reinterpret_cast<const void*>(bufferEnd()));\n\n  struct SplitTailStorage : HeapFullStorage {\n    IOBuf parent;\n  };\n\n  auto [storage, mallocSize] = allocateStorage<SplitTailStorage>();\n  void* userData = new (&storage->parent) IOBuf(origBuf->cloneOneAsValue());\n  new (&storage->shared)\n      SharedInfo(freeFn, userData, SharedInfo::StorageType::kHeapFullStorage);\n\n  // Release ownership of the tail.\n  trimWritableTail(tailSize);\n\n  auto result = std::unique_ptr<IOBuf>(new (&storage->hs.buf) IOBuf(\n      InternalConstructor(),\n      &storage->shared,\n      writableTail(),\n      tailSize,\n      writableTail(),\n      0));\n\n  return result;\n}\n\nvoid IOBuf::unshareOneSlow() {\n  // Allocate a new buffer for the data\n  uint8_t* buf;\n  SharedInfo* sharedInfo;\n  std::size_t actualCapacity;\n  allocExtBuffer(capacity_, &buf, &sharedInfo, &actualCapacity);\n\n  // Copy the data\n  // Maintain the same amount of headroom.  Since we maintained the same\n  // minimum capacity we also maintain at least the same amount of tailroom.\n  std::size_t headlen = headroom();\n  if (length_ > 0) {\n    assert(data_ != nullptr);\n    memcpy(buf + headlen, data_, length_);\n  }\n\n  // Release our reference on the old buffer\n  decrementRefcount();\n  sharedInfo_ = sharedInfo;\n\n  // Update the buffer pointers to point to the new buffer\n  data_ = buf + headlen;\n  buf_ = buf;\n}\n\nvoid IOBuf::unshareChained() {\n  // unshareChained() should only be called if we are part of a chain of\n  // multiple IOBufs.  The caller should have already verified this.\n  assert(isChained());\n\n  IOBuf* current = this;\n  while (true) {\n    if (current->isSharedOne()) {\n      // we have to unshare\n      break;\n    }\n\n    current = current->next_;\n    if (current == this) {\n      // None of the IOBufs in the chain are shared,\n      // so return without doing anything\n      return;\n    }\n  }\n\n  // We have to unshare.  Let coalesceSlow() do the work.\n  coalesceSlow();\n}\n\nvoid IOBuf::markExternallyShared() {\n  IOBuf* current = this;\n  do {\n    current->markExternallySharedOne();\n    current = current->next_;\n  } while (current != this);\n}\n\nvoid IOBuf::makeManagedChained() {\n  assert(isChained());\n\n  IOBuf* current = this;\n  while (true) {\n    current->makeManagedOne();\n    current = current->next_;\n    if (current == this) {\n      break;\n    }\n  }\n}\n\nvoid IOBuf::coalesceSlow() {\n  // coalesceSlow() should only be called if we are part of a chain of multiple\n  // IOBufs.  The caller should have already verified this.\n  DCHECK(isChained());\n\n  // Compute the length of the entire chain\n  std::size_t newLength = 0;\n  IOBuf* end = this;\n  do {\n    newLength += end->length_;\n    end = end->next_;\n  } while (end != this);\n\n  coalesceAndReallocate(newLength, end);\n  // We should be only element left in the chain now\n  DCHECK(!isChained());\n}\n\nvoid IOBuf::coalesceSlow(size_t maxLength) {\n  // coalesceSlow() should only be called if we are part of a chain of multiple\n  // IOBufs.  The caller should have already verified this.\n  DCHECK(isChained());\n  DCHECK_LT(length_, maxLength);\n\n  // Compute the length of the entire chain\n  std::size_t newLength = 0;\n  IOBuf* end = this;\n  while (true) {\n    newLength += end->length_;\n    end = end->next_;\n    if (newLength >= maxLength) {\n      break;\n    }\n    if (end == this) {\n      throw_exception<std::overflow_error>(\n          \"attempted to coalesce more data than \"\n          \"available\");\n    }\n  }\n\n  coalesceAndReallocate(newLength, end);\n  // We should have the requested length now\n  DCHECK_GE(length_, maxLength);\n}\n\nvoid IOBuf::coalesceAndReallocate(\n    size_t newHeadroom, size_t newLength, IOBuf* end, size_t newTailroom) {\n  std::size_t newCapacity = newLength + newHeadroom + newTailroom;\n\n  // Allocate space for the coalesced buffer.\n  // We always convert to an external buffer, even if we happened to be an\n  // internal buffer before.\n  uint8_t* newBuf;\n  SharedInfo* newInfo;\n  std::size_t actualCapacity;\n  allocExtBuffer(newCapacity, &newBuf, &newInfo, &actualCapacity);\n\n  // Copy the data into the new buffer\n  uint8_t* newData = newBuf + newHeadroom;\n  uint8_t* p = newData;\n  IOBuf* current = this;\n  size_t remaining = newLength;\n  do {\n    if (current->length_ > 0) {\n      assert(current->length_ <= remaining);\n      assert(current->data_ != nullptr);\n      remaining -= current->length_;\n      memcpy(p, current->data_, current->length_);\n      p += current->length_;\n    }\n    current = current->next_;\n  } while (current != end);\n  assert(remaining == 0);\n  (void)remaining;\n\n  // Point at the new buffer\n  decrementRefcount();\n\n  sharedInfo_ = newInfo;\n  capacity_ = actualCapacity;\n  buf_ = newBuf;\n  data_ = newData;\n  length_ = newLength;\n\n  // Separate from the rest of our chain.\n  // Since we don't store the unique_ptr returned by separateChain(),\n  // this will immediately delete the returned subchain.\n  if (isChained()) {\n    (void)separateChain(next_, current->prev_);\n  }\n}\n\nvoid IOBuf::decrementRefcount() noexcept {\n  // Externally owned buffers don't have a SharedInfo object and aren't managed\n  // by the reference count\n  if (!sharedInfo_) {\n    return;\n  }\n\n  // Avoid doing atomic decrement if the refcount is 1.\n  // This is safe, because it means that we're the last reference and destroying\n  // the object. Anything trying to copy it is already undefined behavior.\n  if (sharedInfo_->refcount.load(std::memory_order_acquire) > 1) {\n    // Decrement the refcount\n    uint32_t newcnt =\n        sharedInfo_->refcount.fetch_sub(1, std::memory_order_acq_rel);\n    // Note that fetch_sub() returns the value before we decremented.\n    // If it is 1, we were the only remaining user; if it is greater there are\n    // still other users.\n    if (newcnt > 1) {\n      return;\n    }\n  }\n\n  // Save the storage type since freeExtBuffer can free the SharedInfo.\n  auto infoStorageType = sharedInfo_->storageType;\n  freeExtBuffer(); // We were the last user. Free the buffer\n  SharedInfo::releaseStorage(this, infoStorageType, sharedInfo_);\n}\n\nvoid IOBuf::reserveSlow(std::size_t minHeadroom, std::size_t minTailroom) {\n  size_t newCapacity = length_;\n  if (!checked_add(&newCapacity, newCapacity, minHeadroom) ||\n      !checked_add(&newCapacity, newCapacity, minTailroom) ||\n      newCapacity > kMaxIOBufSize) {\n    // overflow\n    throw_exception<std::bad_alloc>();\n  }\n\n  // reserveSlow() is dangerous if anyone else is sharing the buffer, as we may\n  // reallocate and free the original buffer.  It should only ever be called if\n  // we are the only user of the buffer.\n  DCHECK(!isSharedOne());\n\n  // We'll need to reallocate the buffer.\n  // There are a few options.\n  // - If we have enough total room, move the data around in the buffer\n  //   and adjust the data_ pointer.\n  // - If we're using an internal buffer, we'll switch to an external\n  //   buffer with enough headroom and tailroom.\n  // - If we have enough headroom (headroom() >= minHeadroom) but not too much\n  //   (so we don't waste memory), we can try one of two things, depending on\n  //   whether we use jemalloc or not:\n  //   - If using jemalloc, we can try to expand in place, avoiding a memcpy()\n  //   - If not using jemalloc and we don't have too much to copy,\n  //     we'll use realloc() (note that realloc might have to copy\n  //     headroom + data + tailroom, see smartRealloc in folly/memory/Malloc.h)\n  // - Otherwise, bite the bullet and reallocate.\n  if (headroom() + tailroom() >= minHeadroom + minTailroom) {\n    uint8_t* newData = writableBuffer() + minHeadroom;\n    memmove(newData, data_, length_);\n    data_ = newData;\n    return;\n  }\n\n  size_t newAllocatedCapacity = 0;\n  uint8_t* newBuffer = nullptr;\n  std::size_t newHeadroom = 0;\n  std::size_t oldHeadroom = headroom();\n\n  // If we have a buffer allocated with malloc and we just need more tailroom,\n  // try to use realloc()/xallocx() to grow the buffer in place.\n  SharedInfo* info = sharedInfo_;\n  auto infoStorageType =\n      info ? info->storageType : SharedInfo::StorageType::kInvalid;\n  if (info && (info->freeFn == nullptr) && length_ != 0 &&\n      oldHeadroom >= minHeadroom) {\n    size_t headSlack = oldHeadroom - minHeadroom;\n    newAllocatedCapacity = goodExtBufferSize(newCapacity + headSlack);\n    if (usingJEMalloc()) {\n      // We assume that tailroom is more useful and more important than\n      // headroom (not least because realloc / xallocx allow us to grow the\n      // buffer at the tail, but not at the head)  So, if we have more headroom\n      // than we need, we consider that \"wasted\".  We arbitrarily define \"too\n      // much\" headroom to be 25% of the capacity.\n      if (headSlack * 4 <= newCapacity) {\n        size_t allocatedCapacity = capacity() + sizeof(SharedInfo);\n        void* p = buf_;\n        if (allocatedCapacity >= jemallocMinInPlaceExpandable) {\n          if (xallocx(p, newAllocatedCapacity, 0, 0) == newAllocatedCapacity) {\n            if (io_buf_free_cb) {\n              io_buf_free_cb(p, reinterpret_cast<size_t>(info->userData));\n            }\n            newBuffer = static_cast<uint8_t*>(p);\n            newHeadroom = oldHeadroom;\n            // update the userData\n            info->userData = reinterpret_cast<void*>(newAllocatedCapacity);\n            if (io_buf_alloc_cb) {\n              io_buf_alloc_cb(newBuffer, newAllocatedCapacity);\n            }\n          }\n          // if xallocx failed, do nothing, fall back to malloc/memcpy/free\n        }\n      }\n    } else { // Not using jemalloc\n      size_t copySlack = capacity() - length_;\n      if (copySlack * 2 <= length_) {\n        void* p = realloc(buf_, newAllocatedCapacity);\n        if (FOLLY_UNLIKELY(p == nullptr)) {\n          throw_exception<std::bad_alloc>();\n        }\n        newBuffer = static_cast<uint8_t*>(p);\n        newHeadroom = oldHeadroom;\n      }\n    }\n  }\n\n  // None of the previous reallocation strategies worked (or we're using\n  // an internal buffer).  malloc/copy/free.\n  if (newBuffer == nullptr) {\n    newAllocatedCapacity = goodExtBufferSize(newCapacity);\n    newBuffer = static_cast<uint8_t*>(checkedMalloc(newAllocatedCapacity));\n    if (length_ > 0) {\n      assert(data_ != nullptr);\n      memcpy(newBuffer + minHeadroom, data_, length_);\n    }\n    if (sharedInfo_) {\n      freeExtBuffer();\n    }\n    newHeadroom = minHeadroom;\n  }\n\n  std::size_t cap;\n  initExtBuffer(newBuffer, newAllocatedCapacity, &info, &cap);\n\n  if (infoStorageType != SharedInfo::StorageType::kInvalid) {\n    SharedInfo::releaseStorage(this, infoStorageType, sharedInfo_);\n  }\n  sharedInfo_ = info;\n\n  capacity_ = cap;\n  buf_ = newBuffer;\n  data_ = newBuffer + newHeadroom;\n  // length_ is unchanged\n}\n\n// The user's free function should never throw. Otherwise we might throw from\n// the IOBuf destructor. Other code paths like coalesce() also assume that\n// decrementRefcount() cannot throw.\nvoid IOBuf::freeExtBuffer() noexcept {\n  DCHECK(sharedInfo_);\n\n  // Store all the needed information in case the SharedInfo's storage is in the\n  // buffer and so we need to destroy it.\n  auto observerListHead = std::exchange(sharedInfo_->observerListHead, nullptr);\n  auto freeFn = sharedInfo_->freeFn;\n  auto userData = sharedInfo_->userData;\n\n  if (sharedInfo_->storageType == SharedInfo::StorageType::kExtBuffer) {\n    sharedInfo_->~SharedInfo();\n  }\n\n  if (freeFn) {\n    freeFn(buf_, userData);\n  } else {\n    size_t size = reinterpret_cast<size_t>(userData);\n    if (size) {\n      if (io_buf_free_cb) {\n        io_buf_free_cb(buf_, size);\n      }\n      folly::sizedFree(buf_, size);\n    } else {\n      free(buf_);\n    }\n  }\n  SharedInfo::invokeAndDeleteEachObserver(observerListHead, [](auto& entry) {\n    entry.afterFreeExtBuffer();\n  });\n\n  if (kIsMobile) {\n    buf_ = nullptr;\n  }\n}\n\nvoid IOBuf::allocExtBuffer(\n    std::size_t minCapacity,\n    uint8_t** bufReturn,\n    SharedInfo** infoReturn,\n    std::size_t* capacityReturn) {\n  if (minCapacity > kMaxIOBufSize) {\n    throw_exception<std::bad_alloc>();\n  }\n\n  size_t mallocSize = goodExtBufferSize(minCapacity);\n  auto buf = static_cast<uint8_t*>(checkedMalloc(mallocSize));\n  initExtBuffer(buf, mallocSize, infoReturn, capacityReturn);\n\n  // the userData and the freeFn are nullptr here\n  // just store the mallocSize in userData\n  (*infoReturn)->userData = reinterpret_cast<void*>(mallocSize);\n  if (io_buf_alloc_cb) {\n    io_buf_alloc_cb(buf, mallocSize);\n  }\n\n  *bufReturn = buf;\n}\n\nsize_t IOBuf::goodExtBufferSize(std::size_t minCapacity) {\n  if (minCapacity > kMaxIOBufSize) {\n    throw_exception<std::bad_alloc>();\n  }\n\n  // Determine how much space we should allocate.  We'll store the SharedInfo\n  // for the external buffer just after the buffer itself.  (We store it just\n  // after the buffer rather than just before so that the code can still just\n  // use free(buf_) to free the buffer.)\n  size_t minSize = static_cast<size_t>(minCapacity) + sizeof(SharedInfo);\n  // Add room for padding so that the SharedInfo will be aligned on an 8-byte\n  // boundary.\n  minSize = (minSize + 7) & static_cast<size_t>(~7);\n\n  // Use goodMallocSize() to bump up the capacity to a decent size to request\n  // from malloc, so we can use all of the space that malloc will probably give\n  // us anyway.\n  return goodMallocSize(minSize);\n}\n\nvoid IOBuf::initExtBuffer(\n    uint8_t* buf,\n    size_t mallocSize,\n    SharedInfo** infoReturn,\n    std::size_t* capacityReturn) {\n  // Find the SharedInfo storage at the end of the buffer\n  // and construct the SharedInfo.\n  uint8_t* infoStart = (buf + mallocSize) - sizeof(SharedInfo);\n  auto sharedInfo = new (infoStart)\n      SharedInfo(nullptr, nullptr, SharedInfo::StorageType::kExtBuffer);\n\n  *capacityReturn = std::size_t(infoStart - buf);\n  *infoReturn = sharedInfo;\n}\n\nfbstring IOBuf::moveToFbString() {\n  // we need to save SharedInfo's storage and observerListHead since sharedInfo_\n  // may not be valid after fbstring str\n  SharedInfo::StorageType infoStorageType = SharedInfo::StorageType::kInvalid;\n  SharedInfoObserverEntryBase* observerListHead = nullptr;\n  // malloc-allocated buffers are just fine, everything else needs\n  // to be turned into one.\n  if (!sharedInfo_ || // user owned, not ours to give up\n      sharedInfo_->freeFn || // not malloc()-ed\n      headroom() != 0 || // malloc()-ed block doesn't start at beginning\n      tailroom() == 0 || // no room for NUL terminator\n      isShared() || // shared\n      isChained()) { // chained\n    // We might as well get rid of all head and tailroom if we're going\n    // to reallocate; we need 1 byte for NUL terminator.\n    coalesceAndReallocate(0, computeChainDataLength(), this, 1);\n  } else {\n    if (sharedInfo_) {\n      // if we do not call coalesceAndReallocate\n      // we might need to call SharedInfo::releaseStorage()\n      // and/or SharedInfo::invokeAndDeleteEachObserver()\n      infoStorageType = sharedInfo_->storageType;\n      // save the observerListHead\n      // the coalesceAndReallocate path will call\n      // decrementRefcount and freeExtBuffer if needed\n      // so the observer lis notification is needed here\n      observerListHead = std::exchange(sharedInfo_->observerListHead, nullptr);\n    }\n  }\n\n  // Ensure NUL terminated\n  *writableTail() = 0;\n  fbstring str(\n      reinterpret_cast<char*>(writableData()),\n      length(),\n      capacity(),\n      AcquireMallocatedString());\n\n  if (io_buf_free_cb && sharedInfo_ && sharedInfo_->userData) {\n    io_buf_free_cb(\n        writableData(), reinterpret_cast<size_t>(sharedInfo_->userData));\n  }\n\n  SharedInfo::invokeAndDeleteEachObserver(observerListHead, [](auto& entry) {\n    entry.afterReleaseExtBuffer();\n  });\n\n  if (infoStorageType != SharedInfo::StorageType::kInvalid) {\n    SharedInfo::releaseStorage(this, infoStorageType, sharedInfo_);\n  }\n\n  // Reset to a state where we can be deleted cleanly\n  sharedInfo_ = nullptr;\n  buf_ = nullptr;\n  clear();\n  return str;\n}\n\nIOBuf::Iterator IOBuf::cbegin() const {\n  return Iterator(this, this);\n}\n\nIOBuf::Iterator IOBuf::cend() const {\n  return Iterator(nullptr, nullptr);\n}\n\nstd::unique_ptr<IOBuf> IOBuf::fromString(std::unique_ptr<std::string> ptr) {\n  auto ret = takeOwnership(\n      ptr->data(),\n      ptr->size(),\n      [](void*, void* userData) { delete static_cast<std::string*>(userData); },\n      static_cast<void*>(ptr.get()));\n  std::ignore = ptr.release();\n  return ret;\n}\n\nfolly::fbvector<struct iovec> IOBuf::getIov() const {\n  folly::fbvector<struct iovec> iov;\n  iov.reserve(countChainElements());\n  appendToIov(&iov);\n  return iov;\n}\n\nvoid IOBuf::appendToIov(folly::fbvector<struct iovec>* iov) const {\n  IOBuf const* p = this;\n  do {\n    // some code can get confused by empty iovs, so skip them\n    if (p->length() > 0) {\n      iov->push_back({(void*)p->data(), folly::to<size_t>(p->length())});\n    }\n    p = p->next();\n  } while (p != this);\n}\n\nunique_ptr<IOBuf> IOBuf::wrapIov(const iovec* vec, size_t count) {\n  IOBuf result{};\n  for (size_t i = 0; i < count; ++i) {\n    size_t len = vec[i].iov_len;\n    void* data = vec[i].iov_base;\n    if (len > 0) {\n      auto buf = wrapBuffer(data, len);\n      result.appendToChain(std::move(buf));\n    }\n  }\n  return result.isChained() ? result.pop() : create(0);\n}\n\nstd::unique_ptr<IOBuf> IOBuf::takeOwnershipIov(\n    const iovec* vec,\n    size_t count,\n    FreeFunction freeFn,\n    void* userData,\n    bool freeOnError) {\n  unique_ptr<IOBuf> result = nullptr;\n  for (size_t i = 0; i < count; ++i) {\n    size_t len = vec[i].iov_len;\n    void* data = vec[i].iov_base;\n    if (len > 0) {\n      auto buf = takeOwnership(data, len, freeFn, userData, freeOnError);\n      if (!result) {\n        result = std::move(buf);\n      } else {\n        result->appendToChain(std::move(buf));\n      }\n    }\n  }\n  if (FOLLY_UNLIKELY(result == nullptr)) {\n    return create(0);\n  }\n  return result;\n}\n\nIOBuf::FillIovResult IOBuf::fillIov(struct iovec* iov, size_t len) const {\n  IOBuf const* p = this;\n  size_t i = 0;\n  size_t totalBytes = 0;\n  while (i < len) {\n    // some code can get confused by empty iovs, so skip them\n    if (p->length() > 0) {\n      iov[i].iov_base = const_cast<uint8_t*>(p->data());\n      iov[i].iov_len = p->length();\n      totalBytes += p->length();\n      i++;\n    }\n    p = p->next();\n    if (p == this) {\n      return {i, totalBytes};\n    }\n  }\n  return {0, 0};\n}\n\nuint32_t IOBuf::approximateShareCountOne() const noexcept {\n  if (FOLLY_UNLIKELY(!sharedInfo_)) {\n    return 1U;\n  }\n  return sharedInfo_->refcount.load(std::memory_order_acquire);\n}\n\nsize_t IOBufHash::operator()(const IOBuf& buf) const noexcept {\n  folly::hash::SpookyHashV2 hasher;\n  hasher.Init(0, 0);\n  io::Cursor cursor(&buf);\n  for (;;) {\n    auto b = cursor.peekBytes();\n    if (b.empty()) {\n      break;\n    }\n    hasher.Update(b.data(), b.size());\n    cursor.skip(b.size());\n  }\n  uint64_t h1;\n  uint64_t h2;\n  hasher.Final(&h1, &h2);\n  return static_cast<std::size_t>(h1);\n}\n\nordering IOBufCompare::impl(const IOBuf& a, const IOBuf& b) const noexcept {\n  io::Cursor ca(&a);\n  io::Cursor cb(&b);\n  for (;;) {\n    auto ba = ca.peekBytes();\n    auto bb = cb.peekBytes();\n    if (ba.empty() || bb.empty()) {\n      return to_ordering(int(bb.empty()) - int(ba.empty()));\n    }\n    const size_t n = std::min(ba.size(), bb.size());\n    DCHECK_GT(n, 0u);\n    const ordering r = to_ordering(std::memcmp(ba.data(), bb.data(), n));\n    if (r != ordering::eq) {\n      return r;\n    }\n    // Cursor::skip() may throw if n is too large, but n is not too large here\n    ca.skip(n);\n    cb.skip(n);\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/IOBuf.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Docs: https://fburl.com/fbcref_iobuf\n//\n\n#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <cinttypes>\n#include <cstddef>\n#include <cstring>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <string>\n#include <type_traits>\n\n#include <glog/logging.h>\n\n#include <folly/FBString.h>\n#include <folly/FBVector.h>\n#include <folly/Function.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/detail/Iterators.h>\n#include <folly/lang/CheckedMath.h>\n#include <folly/lang/Ordering.h>\n#include <folly/memory/MemoryResource.h>\n#include <folly/portability/SysUio.h>\n#include <folly/synchronization/MicroSpinLock.h>\n\nFOLLY_PUSH_WARNING\n// Ignore shadowing warnings within this file, so includers can use -Wshadow.\nFOLLY_GNU_DISABLE_WARNING(\"-Wshadow\")\n// Some compilers break on -Wdocumentation. Not all compilers recognise that\n// option, so we also suppress -Wpragmas\nFOLLY_GNU_DISABLE_WARNING(\"-Wpragmas\")\n// Ignore documentation warnings, to enable overloads to share documentation\n// with differing parameters\nFOLLY_GNU_DISABLE_WARNING(\"-Wdocumentation\")\n\nnamespace std::pmr {\nclass memory_resource;\n}\n\nnamespace folly {\n\nnamespace detail {\n// Is T a unique_ptr<> to a standard-layout type?\ntemplate <typename T>\nstruct IsUniquePtrToSL : std::false_type {};\ntemplate <typename T, typename D>\nstruct IsUniquePtrToSL<std::unique_ptr<T, D>> : std::is_standard_layout<T> {};\n} // namespace detail\n\n/**\n * IOBuf manages heap-allocated byte buffers.\n *\n * API Details\n * -----------\n *\n *  - The buffer is not necessarily full of meaningful bytes - there may be\n *    uninitialized bytes before and after the central \"valid\" range of data.\n *  - Buffers are refcounted, and can be shared by multiple IOBuf objects.\n *    - If you ever write to an IOBuf, first use unshare() to get a unique copy.\n *  - IOBufs can be \"chained\" in a circularly linked list.\n *    - Use coalesce() to turn an IOBuf chain into a single IOBuf.\n *  - IOBufs are not synchronized. The user is responsible for synchronization.\n *    Notes:\n *    - Like a shared_ptr, the refcounting is atomic.\n *    - const IOBuf methods do not mutate any state, so can safely be called\n *      concurrently with each other, as expected.\n *  - IOBufs are typically stored on the heap, so that they can be used in\n *    chains.\n *\n *\n * Data Layout\n * -----------\n *\n * IOBuf objects contains a pointer to the buffer and information about which\n * segment of the buffer contains valid data.\n *\n *      +-------+\n *      | IOBuf |\n *      +-------+\n *       /\n *      |            |----- length() -----|\n *      v\n *      +------------+--------------------+-----------+\n *      | headroom   |        data        |  tailroom |\n *      +------------+--------------------+-----------+\n *      ^            ^                    ^           ^\n *      buffer()   data()               tail()      bufferEnd()\n *\n *      |----------------- capacity() ----------------|\n *\n *\n * Buffer Sharing\n * --------------\n *\n * Each buffer is reference counted, and multiple IOBuf objects may point\n * to the same buffer.  Each IOBuf may point to a different section of valid\n * data within the underlying buffer.  For example, if multiple protocol\n * requests are read from the network into a single buffer, a separate IOBuf\n * may be created for each request, all sharing the same underlying buffer.\n *\n * In other words, when multiple IOBufs share the same underlying buffer, the\n * data() and tail() methods on each IOBuf may point to a different segment of\n * the data.  However, the buffer() and bufferEnd() methods will point to the\n * same location for all IOBufs sharing the same underlying buffer, unless the\n * tail was resized by trimWritableTail() or maybeSplitTail().\n *\n *           +-----------+     +---------+\n *           |  IOBuf 1  |     | IOBuf 2 |\n *           +-----------+     +---------+\n *            |         | _____/        |\n *       data |    tail |/    data      | tail\n *            v         v               v\n *      +-------------------------------------+\n *      |     |         |               |     |\n *      +-------------------------------------+\n *\n * If you only read data from an IOBuf, you don't need to worry about other\n * IOBuf objects possibly sharing the same underlying buffer.  However, if you\n * ever write to the buffer you need to first ensure that no other IOBufs point\n * to the same buffer.  The unshare() method may be used to ensure that you\n * have an unshared buffer.\n *\n *\n * IOBuf Chains\n * ------------\n *\n * IOBuf objects also contain pointers to next and previous IOBuf objects.\n * This can be used to represent a single logical piece of data that is stored\n * in non-contiguous chunks in separate buffers.\n *\n *     +---------------------------------------------------------------+\n *     |                                                               |\n *     |    +-----------+        +-----------+        +-----------+    |\n *     +--> |  IOBuf 1  | -----> |  IOBuf 2  | -----> |  IOBuf 3  | ---+\n *          +-----------+        +-----------+        +-----------+\n *            |        | _________/     |           ___/        \\__\n *            |        |/               |          /               \\\n *            v        v                v         v                 v\n *      +-------------------------------------+   +-----------------+\n *      |     |        |                |     |   |                 |\n *      +-------------------------------------+   +-----------------+\n *\n * A single IOBuf object can only belong to one chain at a time.\n *\n * IOBuf chains are always circular.  The \"prev\" pointer in the head of the\n * chain points to the tail of the chain.  However, it is up to the user to\n * decide which IOBuf is the head.  Internally the IOBuf code does not care\n * which element is the head.\n *\n * The lifetime of all IOBufs in the chain are linked: when one element in the\n * chain is deleted, all other chained elements are also deleted.  Conceptually\n * it is simplest to treat this as if the head of the chain owns all other\n * IOBufs in the chain.  When you delete the head of the chain, it will delete\n * the other elements as well.  For this reason, appendToChain() and\n * insertAfterThisOne() take ownership of the new elements being added to this\n * chain.\n *\n * When the coalesce() method is used to coalesce an entire IOBuf chain into a\n * single IOBuf, all other IOBufs in the chain are eliminated and automatically\n * deleted.  The unshare() method may coalesce the chain; if it does it will\n * similarly delete all IOBufs eliminated from the chain.\n *\n * As discussed in the following section, it is up to the user to maintain a\n * lock around the entire IOBuf chain if multiple threads need to access the\n * chain.  IOBuf does not provide any internal locking.\n *\n *\n * Synchronization\n * ---------------\n *\n * When used in multithread programs, a single IOBuf object should only be\n * accessed mutably by a single thread at a time.  All const member functions of\n * IOBuf are safe to call concurrently with one another, but when a caller uses\n * a single IOBuf across multiple threads and at least one thread calls a\n * non-const member function, the caller is responsible for using an external\n * lock to synchronize access to the IOBuf.\n *\n * Two separate IOBuf objects may be accessed concurrently in separate threads\n * without locking, even if they point to the same underlying buffer.  The\n * buffer reference count is always accessed atomically, and no other\n * operations should affect other IOBufs that point to the same data segment.\n * The caller is responsible for using unshare() to ensure that the data buffer\n * is not shared by other IOBufs before writing to it, and this ensures that\n * the data itself is not modified in one thread while also being accessed from\n * another thread.\n *\n * For IOBuf chains, no two IOBufs in the same chain should be accessed\n * simultaneously in separate threads, except where all simultaneous accesses\n * are to const member functions.  The caller must maintain a lock around the\n * entire chain if the chain, or individual IOBufs in the chain, may be accessed\n * by multiple threads with at least one of the threads needing to mutate.\n *\n *\n * IOBuf Object Allocation\n * -----------------------\n *\n * IOBuf objects themselves exist separately from the data buffer they point\n * to.  Therefore one must also consider how to allocate and manage the IOBuf\n * objects. Typically, IOBufs are allocated on the heap.\n *\n *      +--------------+\n *      |  unique_ptr  |\n *      +--------------+\n *        |\n *        v\n *      +---------+\n *      |  IOBuf  |\n *      +---------+\n *        |\n *        v\n *      +----------+\n *      |  buffer  |\n *      +----------+\n *\n *\n * It is more common to allocate IOBuf objects on the heap, using the create(),\n * takeOwnership(), or wrapBuffer() factory functions.  The clone()/cloneOne()\n * functions also return new heap-allocated IOBufs.  The createCombined()\n * function allocates the IOBuf object and data storage space together, in a\n * single memory allocation.  This can improve performance, particularly if you\n * know that the data buffer and the IOBuf itself will have similar lifetimes.\n *\n * That said, it is also possible to allocate IOBufs on the stack or inline\n * inside another object as well.  This is useful for cases where the IOBuf is\n * short-lived, or when the overhead of allocating the IOBuf on the heap is\n * undesirable.\n *\n * However, note that stack-allocated IOBufs may only be used as the head of a\n * chain (or standalone as the only IOBuf in a chain).  All non-head members of\n * an IOBuf chain must be heap allocated.  (All functions to add nodes to a\n * chain require a std::unique_ptr<IOBuf>, which enforces this requirement.)\n *\n * Copying IOBufs is only meaningful for the head of a chain. The entire chain\n * is cloned; the IOBufs will become shared, and the old and new IOBufs will\n * refer to the same underlying memory.\n *\n *\n * IOBuf Sharing\n * -------------\n *\n * The IOBuf class manages sharing of the underlying buffer that it points to,\n * maintaining a reference count if multiple IOBufs are pointing at the same\n * buffer.\n *\n * However, it is the callers responsibility to manage sharing and ownership of\n * IOBuf objects themselves.  The IOBuf structure does not provide room for an\n * intrusive refcount on the IOBuf object itself, only the underlying data\n * buffer is reference counted.  If users want to share the same IOBuf object\n * between multiple parts of the code, they are responsible for managing this\n * sharing on their own.  (For example, by using a shared_ptr.  Alternatively,\n * users always have the option of using clone() to create a second IOBuf that\n * points to the same underlying buffer.)\n *\n *\n * Inspiration\n * -----------\n *\n * IOBuf objects are intended to be used primarily for networking code, and are\n * modelled somewhat after FreeBSD's mbuf data structure, and Linux's sk_buff\n * structure.\n *\n * IOBuf objects facilitate zero-copy network programming, by allowing multiple\n * IOBuf objects to point to the same underlying buffer of data, using a\n * reference count to track when the buffer is no longer needed and can be\n * freed.\n *\n *\n * @refcode folly/docs/examples/folly/io/IOBuf.cpp\n */\nclass IOBuf {\n public:\n  class Iterator;\n\n  enum CreateOp { CREATE };\n  enum WrapBufferOp { WRAP_BUFFER };\n  enum TakeOwnershipOp { TAKE_OWNERSHIP };\n  enum CopyBufferOp { COPY_BUFFER };\n  enum SizedFree { SIZED_FREE };\n\n  enum class CombinedOption { DEFAULT, COMBINED, SEPARATE };\n\n  using value_type = ByteRange;\n  using iterator = Iterator;\n  using const_iterator = Iterator;\n\n  using FreeFunction = void (*)(void* buf, void* userData);\n\n  /**\n   * Create an IOBuf with the requested capacity.\n   *\n   * @param capacity  The size of buffer to allocate\n   *\n   * @post  data() points to the start of the buffer\n   * @post  length() == 0\n   * @post  capacity() >= capacity (@see goodSize for details on why IOBuf\n   *        sometimes allocates a larger buffer than requested)\n   *\n   * @throws std::bad_alloc on malloc failure\n   */\n  IOBuf(CreateOp, std::size_t capacity);\n\n  /**\n   * @copydoc IOBuf(CreateOp, std::size_t)\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> create(std::size_t capacity);\n\n  /**\n   * Create an IOBuf, allocated alongside its buffer.\n   *\n   * This method uses a single memory allocation to allocate space for both the\n   * IOBuf object and the data storage space. This saves one memory allocation.\n   *\n   * This can be wasteful if the IOBuf and the buffer have different lifetimes.\n   * The memory will not be reclaimed until both objects are destroyed. This can\n   * happen, for example, if the buffer is grown using reserve().\n   *\n   * @copydetails IOBuf(CreateOp, std::size_t)\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> createCombined(std::size_t capacity);\n\n  /**\n   * Create an IOBuf, allocated separately from its buffer.\n   *\n   * IOBuf::create() doesn't necessarily perform separate allocations if the\n   * buffer is small. This function forces the IOBuf and its buffer to be\n   * allocated separately. This can save space if you know that the buffer will\n   * be reallocated.\n   *\n   * @copydetails IOBuf(CreateOp, std::size_t)\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> createSeparate(std::size_t capacity);\n\n  /**\n   * Create a new IOBuf chain.\n   *\n   * @param totalCapacity  The total buffer size of all IOBufs in the chain\n   * @param maxBufCapacity  The maximum buffer size of each IOBuf in the chain\n   *\n   * @post  computeChainCapacity() >= totalCapacity\n   *\n   * Note: Some malloc implementations will internally round up an allocation\n   * size to a convenient amount (e.g. jemalloc(31) will actually give you a\n   * slab of size 32). Your buffer size could actually be rounded up to\n   * `goodMallocSize(maxBufCapacity)`.\n   *\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> createChain(\n      size_t totalCapacity, std::size_t maxBufCapacity);\n\n  /**\n   * Get a good malloc size.\n   *\n   * Some malloc implementations will internally round up an allocation size to\n   * a convenient amount. For example, jemalloc(31) will actually return a\n   * buffer of size 32. Instead of wasting such tailroom, use it.\n   *\n   * @param minCapacity  The malloc size to round up\n   * @param combined  Here be dragons. T154812262. The default value of DEFAULT\n   *                  is (a) hard to explain, and (b) probably not what you\n   *                  want. Refer to the code to see why.\n   *\n   * @returns  A value at least as large as minCapacity. The overage, if any,\n   *           depends on the allocator.\n   *\n   * Note that IOBufs do this up-sizing for you: they will round up to the full\n   * allocation size and make that capacity available to you without your using\n   * this function. This just lets you introspect into that process, so you can\n   * for example figure out whether a given IOBuf can be usefully compacted.\n   *\n   * @methodset Memory\n   */\n  static size_t goodSize(\n      size_t minCapacity, CombinedOption combined = CombinedOption::DEFAULT);\n\n  /**\n   * Create an IOBuf by taking ownership of an existing buffer.\n   *\n   * The IOBuf will assume ownership of the buffer, and free it by calling the\n   * specified FreeFunction when the last IOBuf pointing to this buffer is\n   * destroyed.\n   *    - The FreeFunction will be called like freeFn(buf, userData)\n   *    - freeFn must not throw an exception\n   *    - If no freeFn is specified, then the buffer will be freed using free().\n   *      Note that this is UB if the buffer was allocated using `new`.\n   *\n   * @param buf  The pointer to the buffer\n   * @param capacity  The size of the buffer\n   * @param offset  The position within the buffer at which the data begins; for\n   *                overloads without this parameter, it defaults to 0\n   * @param length  The amount of data already in buf; for overloads without\n   *                this parameter, it defaults to capacity\n   * @param freeFn  The function to call when buf is to be freed\n   * @param userData  An additional arbitrary void* argument to supply to freeFn\n   * @param freeOnError  Whether the buffer should be freed if this function\n   *                     throws an exception\n   * @param SizedFree  For overloads specified by this enum type, use\n   *                   io_buf_free_cn(buf, capacity) as the freeFn\n   *\n   * @post  data() points to buf+offset (in overloads without offset, offset\n   *        defaults to 0)\n   * @post  length() == length (in overloads without length, length defaults to\n   *        capacity)\n   *\n   * @throws std::bad_alloc on error\n   *\n   * @note  If length is unspecified, it defaults to capacity, as opposed to\n   *        empty.\n   * @note  freeOnError is not properly handled in all cases. T154815366\n   */\n  IOBuf(\n      TakeOwnershipOp op,\n      void* buf,\n      std::size_t capacity,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true)\n      : IOBuf(op, buf, capacity, 0, capacity, freeFn, userData, freeOnError) {}\n\n  /**\n   * @copydoc IOBuf(TakeOwnershipOp, void*, std::size_t, FreeFunction, void*,\n   *          bool)\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> takeOwnership(\n      void* buf,\n      std::size_t capacity,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true) {\n    return takeOwnershipImpl(\n        buf,\n        capacity,\n        0,\n        capacity,\n        freeFn,\n        userData,\n        freeOnError,\n        TakeOwnershipOption::DEFAULT);\n  }\n\n  /**\n   * @copydoc IOBuf(TakeOwnershipOp, void*, std::size_t, FreeFunction, void*,\n   *          bool)\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> takeOwnership(\n      void* buf,\n      std::size_t capacity,\n      std::size_t length,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true) {\n    return takeOwnershipImpl(\n        buf,\n        capacity,\n        0,\n        length,\n        freeFn,\n        userData,\n        freeOnError,\n        TakeOwnershipOption::DEFAULT);\n  }\n\n  /// @copydoc IOBuf(TakeOwnershipOp, void*, std::size_t, FreeFunction, void*,\n  /// bool)\n  IOBuf(\n      TakeOwnershipOp op,\n      void* buf,\n      std::size_t capacity,\n      std::size_t length,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true)\n      : IOBuf(op, buf, capacity, 0, length, freeFn, userData, freeOnError) {}\n\n  /**\n   * @copydoc IOBuf(TakeOwnershipOp, void*, std::size_t, FreeFunction, void*,\n   *          bool)\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> takeOwnership(\n      void* buf,\n      std::size_t capacity,\n      std::size_t offset,\n      std::size_t length,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true) {\n    return takeOwnershipImpl(\n        buf,\n        capacity,\n        offset,\n        length,\n        freeFn,\n        userData,\n        freeOnError,\n        TakeOwnershipOption::DEFAULT);\n  }\n\n  /**\n   * @copydoc IOBuf(TakeOwnershipOp, void*, std::size_t, FreeFunction, void*,\n   *          bool)\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> takeOwnership(\n      SizedFree,\n      void* buf,\n      std::size_t capacity,\n      std::size_t offset,\n      std::size_t length,\n      bool freeOnError = true) {\n    return takeOwnershipImpl(\n        buf,\n        capacity,\n        offset,\n        length,\n        nullptr,\n        reinterpret_cast<void*>(capacity),\n        freeOnError,\n        TakeOwnershipOption::STORE_SIZE);\n  }\n\n  /// @copydoc IOBuf(TakeOwnershipOp, void*, std::size_t, FreeFunction, void*,\n  /// bool)\n  IOBuf(\n      TakeOwnershipOp,\n      void* buf,\n      std::size_t capacity,\n      std::size_t offset,\n      std::size_t length,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true);\n\n  /// @copydoc IOBuf(TakeOwnershipOp, void*, std::size_t, FreeFunction, void*,\n  /// bool)\n  IOBuf(\n      TakeOwnershipOp,\n      SizedFree,\n      void* buf,\n      std::size_t capacity,\n      std::size_t offset,\n      std::size_t length,\n      bool freeOnError = true);\n\n  /**\n   * Create an IOBuf with a reinterpreted buffer.\n   *\n   * Create a new IOBuf pointing to an existing data buffer made up of\n   * count objects of a given standard-layout type.\n   *\n   * This is dangerous -- it is essentially equivalent to doing\n   * reinterpret_cast<unsigned char*> on your data -- but it's often useful\n   * for serialization / deserialization.\n   *\n   * The new IOBuf will assume ownership of the buffer, and free it\n   * appropriately (by calling the UniquePtr's custom deleter, or by calling\n   * delete or delete[] appropriately if there is no custom deleter)\n   * when the buffer is destroyed.  The custom deleter, if any, must never\n   * throw exceptions.\n   *\n   * The IOBuf data pointer will initially point to the start of the buffer,\n   * and the length will be the full capacity of the buffer (count *\n   * sizeof(T)).\n   *\n   * On error, std::bad_alloc will be thrown, and the buffer will be freed\n   * before throwing the error.\n   *\n   * @param buf  The unique_ptr to the buffer\n   * @param count  The number of elements in the buffer\n   *\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   *\n   * TODO T154818309\n   */\n  template <class UniquePtr>\n  static typename std::enable_if<\n      detail::IsUniquePtrToSL<UniquePtr>::value,\n      std::unique_ptr<IOBuf>>::type\n  takeOwnership(UniquePtr&& buf, size_t count = 1);\n\n  /**\n   * Create an IOBuf pointing to a buffer, without taking ownership.\n   *\n   * This should only be used when the caller knows the lifetime of the IOBuf\n   * object ahead of time and can ensure that all IOBuf objects that will point\n   * to this buffer will be destroyed before the buffer itself is destroyed.\n   * The buffer will not be freed automatically when the last IOBuf\n   * referencing it is destroyed.  It is the caller's responsibility to free\n   * the buffer after the last IOBuf has been destroyed.\n   *\n   * An IOBuf created using wrapBuffer() will always be reported as shared.\n   * unshare() may be used to create a writable copy of the buffer.\n   *\n   * @param buf  The pointer to the buffer\n   * @param capacity  The size of the buffer\n   * @param br  Can pass a ByteRange in lieu of {buf, capacity}\n   *\n   * @post  data() points to buf\n   * @post  length() == capacity\n   */\n  IOBuf(WrapBufferOp op, ByteRange br) noexcept;\n\n  /// @copydoc IOBuf(WrapBufferOp, ByteRange)\n  IOBuf(WrapBufferOp op, const void* buf, std::size_t capacity) noexcept;\n\n  /**\n   * @copydoc IOBuf(WrapBufferOp, ByteRange)\n   * @throws std::bad_alloc on error (the allocation of th IOBuf may throw)\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> wrapBuffer(\n      const void* buf, std::size_t capacity);\n\n  /// @copydoc wrapBuffer(const void*, std::size_t)\n  static std::unique_ptr<IOBuf> wrapBuffer(ByteRange br) {\n    return wrapBuffer(br.data(), br.size());\n  }\n\n  /**\n   * @copydoc IOBuf(WrapBufferOp, ByteRange)\n   *\n   * This static function behaves exactly like the WrapBufferOp constructor.\n   * It exists for syntactic parity with the unique_ptr-returning variants.\n   *\n   * @returns  A stack-allocated IOBuf\n   * @methodset Makers\n   */\n  static IOBuf wrapBufferAsValue(\n      const void* buf, std::size_t capacity) noexcept;\n\n  /// @copydoc wrapBufferAsValue(const void*, std::size_t)\n  static IOBuf wrapBufferAsValue(ByteRange br) noexcept {\n    return wrapBufferAsValue(br.data(), br.size());\n  }\n\n  /**\n   * Create an IOBuf and copy data into the buffer.\n   *\n   * The IOBuf will have a newly-allocated buffer. That buffer shall be\n   * populated with data from the argument buffer.\n   *\n   * @param buf  The buffer from which to copy data\n   * @param size  The size of the buffer from which to copy data\n   * @param br  Can pass a ByteRange in lieu of {buf, size}\n   * @param headroom  The amount of headroom to add to the destination buffer\n   * @param minTailroom  The amount of tailroom to add to the destination buffer\n   *\n   * @post  data() points to a new buffer whose content is the same as buf\n   * @post  length() == size\n   * @post  headroom() == headroom\n   * @post  tailroom() >= minTailroom\n   *\n   * @throws std::bad_alloc on error\n   */\n  IOBuf(\n      CopyBufferOp op,\n      ByteRange br,\n      std::size_t headroom = 0,\n      std::size_t minTailroom = 0);\n\n  /// @copydoc IOBuf(CopyBufferOp, ByteRange, std::size_t, std::size_t)\n  IOBuf(\n      CopyBufferOp op,\n      const void* buf,\n      std::size_t size,\n      std::size_t headroom = 0,\n      std::size_t minTailroom = 0);\n\n  /**\n   * @copydoc IOBuf(CopyBufferOp, ByteRange, std::size_t, std::size_t)\n   * @returns  A unique_ptr to a newly-constructed IOBuf\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> copyBuffer(\n      ByteRange br, std::size_t headroom = 0, std::size_t minTailroom = 0) {\n    return copyBuffer(br.data(), br.size(), headroom, minTailroom);\n  }\n\n  /// @copydoc copyBuffer(ByteRange, std::size_t, std::size_t)\n  static std::unique_ptr<IOBuf> copyBuffer(\n      const void* buf,\n      std::size_t size,\n      std::size_t headroom = 0,\n      std::size_t minTailroom = 0);\n\n  /**\n   * @copydoc IOBuf(CopyBufferOp, ByteRange, std::size_t, std::size_t)\n   *\n   * Beware when attempting to invoke this function with a constant string\n   * literal and a headroom argument: you will likely end up invoking\n   * copyBuffer(void* buf, size_t size).\n   */\n  static std::unique_ptr<IOBuf> copyBuffer(\n      StringPiece buf, std::size_t headroom = 0, std::size_t minTailroom = 0);\n  IOBuf(\n      CopyBufferOp op,\n      StringPiece buf,\n      std::size_t headroom = 0,\n      std::size_t minTailroom = 0)\n      : IOBuf(op, buf.data(), buf.size(), headroom, minTailroom) {}\n\n  /**\n   * @copydoc IOBuf(CopyBufferOp, ByteRange, std::size_t, std::size_t)\n   *\n   * This \"maybe\" version of copyBuffer returns null if the input is empty.\n   *\n   * @methodset Makers\n   */\n  static std::unique_ptr<IOBuf> maybeCopyBuffer(\n      StringPiece buf, std::size_t headroom = 0, std::size_t minTailroom = 0);\n\n  /**\n   * Free an IOBuf.\n   *\n   * Note: as with all IOBuf destruction, this will also destroy all other\n   * IOBufs in the same chain.\n   *\n   * @param data  The IOBuf to be destroyed\n   * @post data will be nullptr\n   *\n   * @methodset Memory\n   */\n  static void destroy(std::unique_ptr<IOBuf>&& data) {\n    auto destroyer = std::move(data);\n  }\n\n  /**\n   * Destroy this IOBuf.\n   *\n   * Deleting an IOBuf will automatically destroy all IOBufs in the chain.\n   * (All subsequent IOBufs in the chain are considered to be owned by the head\n   * of the chain.  Users should only explicitly delete the head of a chain.)\n   *\n   * When each individual IOBuf is destroyed, it will release its reference\n   * count on the underlying buffer.  If it was the last user of the buffer,\n   * the buffer will be freed.\n   */\n  ~IOBuf();\n\n  /**\n   * Check whether the chain is empty.\n   *\n   * This method is semantically equivalent to\n   *   i->computeChainDataLength()==0\n   * but may run faster because it can short-circuit as soon as it\n   * encounters a buffer with length()!=0\n   *\n   * @methodset Chaining\n   */\n  bool empty() const noexcept;\n\n  /**\n   * Get the pointer to the start of the data.\n   *\n   * @methodset Access\n   */\n  const uint8_t* data() const noexcept { return data_; }\n\n  /**\n   * Get a writable pointer to the start of the data.\n   *\n   * The caller is responsible for calling unshare() first to ensure that it is\n   * actually safe to write to the buffer.\n   *\n   * @methodset Access\n   */\n  uint8_t* writableData() noexcept { return data_; }\n\n  /**\n   * Get the pointer to the end of the data.\n   *\n   * @methodset Access\n   */\n  const uint8_t* tail() const noexcept { return data_ + length_; }\n\n  /**\n   * Get a writable pointer to the end of the data.\n   *\n   * The caller is responsible for calling unshare() first to ensure that it is\n   * actually safe to write to the buffer.\n   *\n   * @methodset Access\n   */\n  uint8_t* writableTail() noexcept { return data_ + length_; }\n\n  /**\n   * Get the size of the data for this individual IOBuf in the chain.\n   *\n   * Use computeChainDataLength() for the sum of data length for the full chain.\n   *\n   * @methodset Buffer Capacity\n   */\n  std::size_t length() const noexcept { return length_; }\n\n  /**\n   * Get the amount of head room.\n   *\n   * @returns  The number of bytes in the buffer before the start of the data\n   *\n   * @methodset Buffer Capacity\n   */\n  std::size_t headroom() const noexcept {\n    return std::size_t(data_ - buffer());\n  }\n\n  /**\n   * Get the amount of tail room.\n   *\n   * @returns  The number of bytes in the buffer after the end of the data\n   *\n   * @methodset Buffer Capacity\n   */\n  std::size_t tailroom() const noexcept {\n    return std::size_t(bufferEnd() - tail());\n  }\n\n  /**\n   * Get the pointer to the start of the buffer.\n   *\n   * Note that this is the pointer to the very beginning of the usable buffer,\n   * not the start of valid data within the buffer.  Use the data() method to\n   * get a pointer to the start of the data within the buffer.\n   *\n   * @methodset Access\n   */\n  const uint8_t* buffer() const noexcept { return buf_; }\n\n  /**\n   * Get a writable pointer to the start of the buffer.\n   *\n   * The caller is responsible for calling unshare() first to ensure that it is\n   * actually safe to write to the buffer.\n   *\n   * @methodset Access\n   */\n  uint8_t* writableBuffer() noexcept { return buf_; }\n\n  /**\n   * Get the pointer to the end of the buffer.\n   *\n   * Note that this is the pointer to the very end of the usable buffer,\n   * not the end of valid data within the buffer.  Use the tail() method to\n   * get a pointer to the end of the data within the buffer.\n   *\n   * @methodset Access\n   */\n  const uint8_t* bufferEnd() const noexcept { return buf_ + capacity_; }\n\n  /**\n   * Get the total size of the buffer.\n   *\n   * This returns the total usable length of the buffer.  Use the length()\n   * method to get the length of the actual valid data in this IOBuf.\n   *\n   * @methodset Buffer Capacity\n   */\n  std::size_t capacity() const noexcept { return capacity_; }\n\n  /**\n   * Get a pointer to the next IOBuf in this chain.\n   *\n   * @methodset Chaining\n   */\n  IOBuf* next() noexcept { return next_; }\n  /// @copydoc next()\n  const IOBuf* next() const noexcept { return next_; }\n\n  /**\n   * Get a pointer to the previous IOBuf in this chain.\n   *\n   * @methodset Chaining\n   */\n  IOBuf* prev() noexcept { return prev_; }\n  /// @copydoc prev\n  const IOBuf* prev() const noexcept { return prev_; }\n\n  /**\n   * Shift the data forwards in the buffer.\n   *\n   * This shifts the data pointer forwards in the buffer to increase the\n   * headroom.  This is commonly used to increase the headroom in a newly\n   * allocated buffer.\n   *\n   * The caller is responsible for ensuring that there is sufficient\n   * tailroom in the buffer before calling advance().\n   *\n   * If there is a non-zero data length, advance() will use memmove() to shift\n   * the data forwards in the buffer.  In this case, the caller is responsible\n   * for making sure the buffer is unshared, so it will not affect other IOBufs\n   * that may be sharing the same underlying buffer.\n   *\n   * @param amount  The amount by which to shift all data forward\n   * @post  length() is unchanged\n   *\n   * @methodset Shifting\n   */\n  void advance(std::size_t amount) noexcept {\n    // In debug builds, assert if there is a problem.\n    assert(amount <= tailroom());\n\n    if (length_ > 0) {\n      memmove(data_ + amount, data_, length_);\n    }\n    data_ += amount;\n  }\n\n  /**\n   * Shift the data backwards in the buffer.\n   *\n   * This shifts the data pointer backwards in the buffer, decreasing the\n   * headroom.\n   *\n   * The caller is responsible for ensuring that there is sufficient headroom\n   * in the buffer before calling retreat().\n   *\n   * If there is a non-zero data length, retreat() will use memmove() to shift\n   * the data backwards in the buffer.  In this case, the caller is responsible\n   * for making sure the buffer is unshared, so it will not affect other IOBufs\n   * that may be sharing the same underlying buffer.\n   *\n   * @param amount  The amount by which to shift all data backward\n   * @post  length() is unchanged\n   *\n   * @methodset Shifting\n   */\n  void retreat(std::size_t amount) noexcept {\n    // In debug builds, assert if there is a problem.\n    assert(amount <= headroom());\n\n    if (length_ > 0) {\n      memmove(data_ - amount, data_, length_);\n    }\n    data_ -= amount;\n  }\n\n  /**\n   * Adjust the data pointer to include more valid data at the beginning.\n   *\n   * This moves the data pointer backwards to include more of the available\n   * buffer.  The caller is responsible for ensuring that there is sufficient\n   * headroom for the new data.  The caller is also responsible for populating\n   * this section with valid data.\n   *\n   * This does not modify any actual data in the buffer.\n   *\n   * @param amount  The amount by which to shift the data() pointer backward\n   * @post  length() is increased by amount\n   *\n   * @methodset Shifting\n   */\n  void prepend(std::size_t amount) noexcept {\n    DCHECK_LE(amount, headroom());\n    data_ -= amount;\n    length_ += amount;\n  }\n\n  /**\n   * Adjust the tail pointer to include more valid data at the end.\n   *\n   * This moves the tail pointer forwards to include more of the available\n   * buffer.  The caller is responsible for ensuring that there is sufficient\n   * tailroom for the new data.  The caller is also responsible for populating\n   * this section with valid data.\n   *\n   * This does not modify any actual data in the buffer.\n   *\n   * @param amount  The amount by which to shift the tail() pointer forward\n   * @post  length() is increased by amount\n   *\n   * @methodset Shifting\n   */\n  void append(std::size_t amount) noexcept {\n    DCHECK_LE(amount, tailroom());\n    length_ += amount;\n  }\n\n  /**\n   * Adjust the data pointer to include less valid data.\n   *\n   * This moves the data pointer forwards so that the first amount bytes are no\n   * longer considered valid data.  The caller is responsible for ensuring that\n   * amount is less than or equal to the actual data length.\n   *\n   * This does not modify any actual data in the buffer.\n   *\n   * @param amount  The amount by which to shift the data() pointer forward\n   * @post  length() is decreased by amount\n   *\n   * @methodset Shifting\n   */\n  void trimStart(std::size_t amount) noexcept {\n    DCHECK_LE(amount, length_);\n    data_ += amount;\n    length_ -= amount;\n  }\n\n  /**\n   * Adjust the tail pointer backwards to include less valid data.\n   *\n   * This moves the tail pointer backwards so that the last amount bytes are no\n   * longer considered valid data.  The caller is responsible for ensuring that\n   * amount is less than or equal to the actual data length.\n   *\n   * This does not modify any actual data in the buffer.\n   *\n   * @param amount  The amount by which to shift the tail() pointer backward\n   * @post  length() is decreased by amount\n   *\n   * @methodset Shifting\n   */\n  void trimEnd(std::size_t amount) noexcept {\n    DCHECK_LE(amount, length_);\n    length_ -= amount;\n  }\n\n  /**\n   * Adjust the buffer end pointer to reduce the buffer capacity.\n   *\n   * This can be used to pass the ownership of the writable tail to another\n   * IOBuf.\n   *\n   * @param amount The amount by which to shift the bufferEnd() pointer backward\n   * @post  capacity() is decreased by amount\n   *\n   * @methodset Shifting\n   */\n  void trimWritableTail(std::size_t amount) noexcept {\n    DCHECK_LE(amount, tailroom());\n    capacity_ -= amount;\n  }\n\n  /**\n   * Clear the buffer.\n   *\n   * @post  data() == buffer()\n   * @post  length() == 0\n   */\n  void clear() noexcept {\n    data_ = writableBuffer();\n    length_ = 0;\n  }\n\n  /**\n   * Ensure that the buffer has enough free space.\n   *\n   * Ensure that this buffer has at least minHeadroom headroom bytes and at\n   * least minTailroom tailroom bytes.  The buffer must be writable\n   * (you must call unshare() before this, if necessary).\n   *\n   * This might involve a reallocation of the underlying buffer.\n   *\n   * @param minHeadroom  The requested amount of headroom\n   * @param minTailroom  The requested amount of tailroom\n   *\n   * @post  headroom() >= minHeadroom\n   * @post  tailroom() >= minTailroom\n   * @post  The contents between data() and tail() are preserved\n   *\n   * @methodset Buffer Capacity\n   */\n  void reserve(std::size_t minHeadroom, std::size_t minTailroom) {\n    // Maybe we don't need to do anything.\n    if (headroom() >= minHeadroom && tailroom() >= minTailroom) {\n      return;\n    }\n    // If the buffer is empty but we have enough total room (head + tail),\n    // move the data_ pointer around.\n    if (length() == 0 && headroom() + tailroom() >= minHeadroom + minTailroom) {\n      data_ = writableBuffer() + minHeadroom;\n      return;\n    }\n    // Bah, we have to do actual work.\n    reserveSlow(minHeadroom, minTailroom);\n  }\n\n  /**\n   * Is this IOBuf part of a chain.\n   *\n   * Technically, all IOBufs are part of a chain, possibly of length 1. This\n   * function checks if the chain is non-trivial, i.e. the chain has more than\n   * just one IOBuf in it.\n   *\n   * @returns  true iff the the IOBuf's chain has more than 1 IOBuf in it\n   *\n   * @methodset Chaining\n   */\n  bool isChained() const noexcept {\n    assert((next_ == this) == (prev_ == this));\n    return next_ != this;\n  }\n\n  /**\n   * Get the number of IOBufs in this chain.\n   *\n   * Beware that this method has to walk the entire chain.\n   * Use isChained() if you just want to check if this IOBuf is part of a chain\n   * or not.\n   *\n   * @methodset Chaining\n   */\n  size_t countChainElements() const noexcept;\n\n  /**\n   * Get the length of all the data in this IOBuf chain.\n   *\n   * Beware that this method has to walk the entire chain.\n   *\n   * @methodset Chaining\n   */\n  std::size_t computeChainDataLength() const noexcept;\n\n  /**\n   * Get the capacity all IOBufs in the chain.\n   *\n   * Beware that this method has to walk the entire chain.\n   *\n   * @methodset Chaining\n   */\n  std::size_t computeChainCapacity() const noexcept;\n\n  /**\n   * Append another IOBuf chain to the end of this chain.\n   *\n   * For example, if there are two IOBuf chains (A, B, C) and (D, E, F),\n   * and A->appendToChain(D) is called, the (D, E, F) chain will be subsumed\n   * and become part of the chain starting at A, which will now look like\n   * (A, B, C, D, E, F).\n   *\n   * @methodset Chaining\n   */\n  void appendToChain(std::unique_ptr<IOBuf>&& iobuf);\n\n  /**\n   * Insert an IOBuf chain immediately after this chain element.\n   *\n   * For example, if there are two IOBuf chains (A, B, C) and (D, E, F),\n   * and B->insertAfterThisOne(D) is called, the (D, E, F) chain will be\n   * subsumed and become part of the chain starting at A, which will now look\n   * like (A, B, D, E, F, C)\n   *\n   * Note if X is an IOBuf chain with just a single element, X->appendToChain()\n   * and X->insertAfterThisOne() behave identically.\n   *\n   * @methodset Chaining\n   */\n  void insertAfterThisOne(std::unique_ptr<IOBuf>&& iobuf) {\n    // Just use appendToChain() on the next element in our chain\n    next_->appendToChain(std::move(iobuf));\n  }\n\n  /**\n   * Deprecated name for appendToChain()\n   *\n   * IOBuf chains are circular, so appending to the end of the chain is\n   * logically equivalent to prepending to the current head (but keeping the\n   * chain head pointing to the same element).  That was the reason this method\n   * was originally called prependChain().  However, almost every time this\n   * method is called the intent is to append to the end of a chain, so the\n   * `prependChain()` name is very confusing to most callers.\n   *\n   * @methodset Chaining\n   */\n  void prependChain(std::unique_ptr<IOBuf>&& iobuf) {\n    appendToChain(std::move(iobuf));\n  }\n\n  /**\n   * Deprecated name for insertAfterThisOne()\n   *\n   * Beware: appendToChain() and appendChain() are two different methods,\n   * and you probably want appendToChain() instead of this one.\n   *\n   * @methodset Chaining\n   */\n  void appendChain(std::unique_ptr<IOBuf>&& iobuf) {\n    insertAfterThisOne(std::move(iobuf));\n  }\n\n  /**\n   * Remove this IOBuf from its current chain.\n   *\n   * Ownership of all elements an IOBuf chain is normally maintained by\n   * the head of the chain. unlink() transfers ownership of this IOBuf from the\n   * chain and gives it to the caller.  A new unique_ptr to the IOBuf is\n   * returned to the caller.  The caller must store the returned unique_ptr (or\n   * call release() on it) to take ownership, otherwise the IOBuf will be\n   * immediately destroyed.\n   *\n   * Since unlink() transfers ownership of the IOBuf to the caller, be careful\n   * not to call unlink() on the head of a chain if you already maintain\n   * ownership on the head of the chain via other means.  The pop() method\n   * is a better choice for that situation.\n   *\n   * @methodset Chaining\n   */\n  std::unique_ptr<IOBuf> unlink() {\n    next_->prev_ = prev_;\n    prev_->next_ = next_;\n    prev_ = this;\n    next_ = this;\n    return std::unique_ptr<IOBuf>(this);\n  }\n\n  /**\n   * Remove the rest of the chain from this IOBuf.\n   *\n   * Ownership of all elements an IOBuf chain is normally maintained by\n   * the head of the chain. pop() transfers ownership of the rest of the chain\n   * to the caller.\n   *\n   * Since pop() transfers ownership of the rest to the caller, be careful\n   * not to call pop() except on the head of a chain.\n   *\n   * @returns  A new unique_ptr pointing to the rest of the chain; nullptr if\n   *           this IOBuf was the only chain element\n   * @methodset Chaining\n   */\n  std::unique_ptr<IOBuf> pop() {\n    IOBuf* next = next_;\n    next_->prev_ = prev_;\n    prev_->next_ = next_;\n    prev_ = this;\n    next_ = this;\n    return std::unique_ptr<IOBuf>((next == this) ? nullptr : next);\n  }\n\n  /**\n   * Remove a subchain from this chain.\n   *\n   * Remove the subchain starting at head and ending at tail from this chain.\n   * This is inclusive of tail.\n   *\n   * If you have a chain (A, B, C, D, E, F), and you call A->separateChain(B,\n   * D), then you will be returned the chain (B, C, D) and the current IOBuf\n   * chain will change to (A, E, F).\n   *\n   * Returns a unique_ptr pointing to the new head.  (In other words, ownership\n   * of the head of the subchain is transferred to the caller.)  If the caller\n   * ignores the return value and lets the unique_ptr be destroyed, the subchain\n   * will be immediately destroyed.\n   *\n   * head may equal tail. In this case, the subchain of length 1 is removed.\n   *\n   * @pre  head and tail are part of the current IOBuf chain\n   * @pre  head and tail are not equal to the current IOBuf\n   *\n   * @param head  The first IOBuf chain element to remove\n   * @param tail  The last IOBuf chain element to remove (inclusive)\n   *\n   * @methodset Chaining\n   */\n  std::unique_ptr<IOBuf> separateChain(IOBuf* head, IOBuf* tail) {\n    assert(head != this);\n    assert(tail != this);\n\n    head->prev_->next_ = tail->next_;\n    tail->next_->prev_ = head->prev_;\n\n    head->prev_ = tail;\n    tail->next_ = head;\n\n    return std::unique_ptr<IOBuf>(head);\n  }\n\n  /**\n   * Check if any chain buffers are shared.\n   *\n   * Return true if at least one of the IOBufs in this chain are shared,\n   * or false if all of the IOBufs point to unique buffers.\n   *\n   * Use isSharedOne() to only check this IOBuf rather than the entire chain.\n   *\n   * If isShared() returns false, then you are probably the sole owner of the\n   * IOBuf chain and can write to it without needing to call unshare(). This is\n   * not a guarantee, since it is possible for another thread to concurrently\n   * acquire shared ownership.\n   *\n   * @methodset Buffer Management\n   */\n  bool isShared() const noexcept {\n    const IOBuf* current = this;\n    while (true) {\n      if (current->isSharedOne()) {\n        return true;\n      }\n      current = current->next_;\n      if (current == this) {\n        return false;\n      }\n    }\n  }\n\n  /**\n   * Get userData.\n   *\n   * userData is the optional constructor argument which will be passed to the\n   * FreeFunction.\n   *\n   * @returns  A non-owning pointer to userData if set, else nullptr\n   *\n   * @methodset Buffer Management\n   */\n  void* getUserData() const noexcept {\n    return sharedInfo_ ? sharedInfo_->userData : nullptr;\n  }\n\n  /**\n   * Get the FreeFunction.\n   *\n   * freeFn is the optional constructor argument which shall be called when the\n   * buffer is to be destroyed.\n   *\n   * @returns  A non-owning pointer to freeFn if set, else nullptr\n   *\n   * @methodset Buffer Management\n   */\n  FreeFunction getFreeFn() const noexcept {\n    return sharedInfo_ ? sharedInfo_->freeFn : nullptr;\n  }\n\n  /**\n   * Add an Observer to the refcount block (SharedInfo).\n   *\n   * @param observer  The observer to add to SharedInfo\n   * @returns  true iff the observer was added (if there is no SharedInfo,\n   *           there's nothing to observe)\n   *\n   * @methodset Misc\n   */\n  template <typename Observer>\n  bool appendSharedInfoObserver(Observer&& observer) {\n    SharedInfo* info = sharedInfo_;\n    if (!info) {\n      return false;\n    }\n\n    auto* entry =\n        new SharedInfoObserverEntry<Observer>(std::forward<Observer>(observer));\n    std::lock_guard guard(info->observerListLock);\n    if (!info->observerListHead) {\n      info->observerListHead = entry;\n    } else {\n      // prepend\n      entry->next = info->observerListHead;\n      entry->prev = info->observerListHead->prev;\n      info->observerListHead->prev->next = entry;\n      info->observerListHead->prev = entry;\n    }\n\n    return true;\n  }\n\n  /**\n   * Check if all IOBufs in this chain use the standard refcounting mechanism.\n   *\n   * If so, then the lifetime of the underlying memory can be extended by\n   * clone().\n   *\n   * @returns  true iff all IOBufs in this chain are isManagedOne()\n   *\n   * @methodset Buffer Management\n   */\n  bool isManaged() const noexcept {\n    const IOBuf* current = this;\n    while (true) {\n      if (!current->isManagedOne()) {\n        return false;\n      }\n      current = current->next_;\n      if (current == this) {\n        return true;\n      }\n    }\n  }\n\n  /**\n   * Check if this IOBuf uses the standard refcounting mechanism.\n   *\n   * If so, then the lifetime of the underlying memory can be extended by\n   * cloneOne().\n   *\n   * @returns  true iff the current IOBuf was allocated normally (without the\n   *           user specifying special memory semantics, such as with a\n   *           user-owned buffer)\n   *\n   * @methodset Buffer Management\n   */\n  bool isManagedOne() const noexcept { return sharedInfo_ != nullptr; }\n\n  /**\n   * Inconsistently get the reference count.\n   *\n   * For most of the use-cases where it seems like a good idea to call this\n   * function, what you really want is isSharedOne().\n   *\n   * If this IOBuf is managed by the usual refcounting mechanism (ie\n   * isManagedOne() returns true) then this returns the reference count as it\n   * was when recently observed by this thread.\n   *\n   * If this IOBuf is *not* managed by the usual refcounting mechanism then the\n   * result of this function is not defined.\n   *\n   * This only checks the current IOBuf, and not other IOBufs in the chain.\n   *\n   * @methodset Buffer Management\n   */\n  uint32_t approximateShareCountOne() const noexcept;\n\n  /**\n   * Check if the buffer is shared.\n   *\n   * IOBuf buffers can be shared (using refcounting). Check if any other IOBufs\n   * are pointing to this same buffer.\n   *\n   * If this IOBuf points at a buffer owned by another (non-IOBuf) part of the\n   * code (i.e., if the IOBuf was created using wrapBuffer(), or was cloned\n   * from such an IOBuf), it is always considered shared.\n   *\n   * This only checks the current IOBuf, and not other IOBufs in the chain.\n   *\n   * @methodset Buffer Management\n   */\n  bool isSharedOne() const noexcept {\n    // If this is a user-owned buffer, it is always considered shared\n    if (FOLLY_UNLIKELY(!sharedInfo_)) {\n      return true;\n    }\n\n    if (FOLLY_UNLIKELY(sharedInfo_->externallyShared)) {\n      return true;\n    }\n\n    return sharedInfo_->refcount.load(std::memory_order_acquire) > 1;\n  }\n\n  /**\n   * Ensure that this IOBuf chain has unique, unshared buffers.\n   *\n   * Multiple IOBufs can point to the same buffer. This means that an IOBuf's\n   * buffer is not necessarily writeable, since another IOBuf might be using the\n   * same underlying data. If you want to write to an IOBuf's buffer, it is your\n   * responsibility to make sure that you aren't trampling the data used by\n   * another IOBuf. This can be accomplished by calling unshare().\n   *\n   * unshare() ensures that the underlying buffer of each IOBuf in the chain is\n   * not shared with another IOBuf.\n   *\n   * @note If the current chain has any shared buffers, then unshare() might\n   *       coalesce the chain during unsharing.\n   * @note Buffers owned by other (non-IOBuf) users are automatically considered\n   *       to be shared.\n   *\n   * @post  The buffers in this IOBuf chain are all writeable, since they are\n   *        uniquely owned by the current IOBuf.\n   *\n   * @throws std::bad_alloc on error.  On error the IOBuf chain will be\n   * unmodified.\n   *\n   * Currently unshare may also throw std::overflow_error if it tries to\n   * coalesce.  (TODO: In the future it would be nice if unshare() were smart\n   * enough not to coalesce the entire buffer if the data is too large.\n   * However, in practice this seems unlikely to become an issue.)\n   *\n   * @methodset Buffer Management\n   */\n  void unshare() {\n    if (isChained()) {\n      unshareChained();\n    } else {\n      unshareOne();\n    }\n  }\n\n  /**\n   * Ensure that this IOBuf has a unique, unshared buffer.\n   *\n   * unshareOne() operates on a single IOBuf object.  This IOBuf will have a\n   * unique buffer after unshareOne() returns, but other IOBufs in the chain\n   * may still be shared after unshareOne() returns.\n   *\n   * @throws std::bad_alloc on error.  On error the IOBuf will be unmodified.\n   *\n   * @methodset Buffer Management\n   */\n  void unshareOne() {\n    if (isSharedOne()) {\n      unshareOneSlow();\n    }\n  }\n\n  /**\n   * Mark the underlying buffers in this chain as shared.\n   *\n   * Assume that the underlying buffers are also owned by an external memory\n   * management mechanism. This will make isShared() always returns true.\n   *\n   * This function is not thread-safe, and only safe to call immediately after\n   * creating an IOBuf, before it has been shared with other threads.\n   *\n   * @methodset Buffer Management\n   */\n  void markExternallyShared();\n\n  /**\n   * Mark the underlying buffer as shared.\n   *\n   * Assume that the underlying buffer is also owned by an external memory\n   * management mechanism. This will make isSharedOne() always returns true.\n   *\n   * This function is not thread-safe, and only safe to call immediately after\n   * creating an IOBuf, before it has been shared with other threads.\n   *\n   * @methodset Buffer Management\n   */\n  void markExternallySharedOne() {\n    if (sharedInfo_) {\n      sharedInfo_->externallyShared = true;\n    }\n  }\n\n  /**\n   * Ensure that the buffers are owned by the IOBuf chain.\n   *\n   * It is possible for an IOBuf to be constructed with a user-owned buffer. In\n   * such circumstances, the user is responsible for ensuring that the buffer\n   * outlives the IOBuf. makeManaged() lets the user subsequently reallocate the\n   * buffer to be owned by the IOBuf directly.\n   *\n   * If the buffers are already owned by IOBuf, then this function doesn't need\n   * to do anything.\n   *\n   * @methodset Buffer Management\n   */\n  void makeManaged() {\n    if (isChained()) {\n      makeManagedChained();\n    } else {\n      makeManagedOne();\n    }\n  }\n\n  /**\n   * Ensure that the buffer is owned by the IOBuf.\n   *\n   * It is possible for an IOBuf to be constructed with a user-owned buffer. In\n   * such circumstances, the user is responsible for ensuring that the buffer\n   * outlives the IOBuf. makeManaged() lets the user subsequently reallocate the\n   * buffer to be owned by the IOBuf directly.\n   *\n   * If the buffer is already owned by IOBuf, then this function doesn't need to\n   * do anything.\n   *\n   * @methodset Buffer Management\n   */\n  void makeManagedOne() {\n    if (!isManagedOne()) {\n      // We can call the internal function directly; unmanaged implies shared.\n      unshareOneSlow();\n    }\n  }\n\n  /**\n   * Coalesce this IOBuf chain into a single buffer.\n   *\n   * This method moves all of the data in this IOBuf chain into a single\n   * contiguous buffer, if it is not already in one buffer.  After coalesce()\n   * returns, this IOBuf will be a chain of length one.  Other IOBufs in the\n   * chain will be automatically deleted.\n   *\n   * After coalescing, the IOBuf will have at least as much headroom as the\n   * first IOBuf in the chain, and at least as much tailroom as the last IOBuf\n   * in the chain.\n   *\n   * @post  isChained() == false\n   *\n   * @throws std::bad_alloc on error.  On error the IOBuf chain will be\n   * unmodified.\n   *\n   * @returns  A ByteRange that points to the now-contiguous buffer data()\n   *\n   * @methodset Chaining\n   */\n  ByteRange coalesce() {\n    if (isChained()) {\n      const std::size_t newHeadroom = headroom();\n      const std::size_t newTailroom = prev()->tailroom();\n      coalesceAndReallocate(\n          newHeadroom, computeChainDataLength(), this, newTailroom);\n    }\n    return ByteRange(data_, length_);\n  }\n\n  /**\n   * @copydoc coalesce()\n   *\n   * @param newHeadroom  How much headroom the new coalesced chain should have,\n   *                     instead of mimicking the original headroom\n   * @param newTailroom  How much tailroom the new coalesced chain should have,\n   *                     instead of mimicking the original tailroom\n   */\n  ByteRange coalesceWithHeadroomTailroom(\n      std::size_t newHeadroom, std::size_t newTailroom) {\n    if (isChained()) {\n      coalesceAndReallocate(\n          newHeadroom, computeChainDataLength(), this, newTailroom);\n    }\n    return ByteRange(data_, length_);\n  }\n\n  /**\n   * Ensure that this chain has at least contiguousLength bytes available as a\n   * contiguous memory range.\n   *\n   * This method coalesces whole buffers in the chain into this buffer as\n   * necessary until this buffer's length() is at least contiguousLength.\n   *\n   * After coalescing, the IOBuf will have at least as much headroom as the\n   * first IOBuf in the chain, and at least as much tailroom as the last IOBuf\n   * that was coalesced.\n   *\n   * @throws std::bad_alloc or std::overflow_error on error.  On error the IOBuf\n   * chain will be unmodified.\n   * @throws std::overflow_error if contiguousLength is longer than the total\n   * chain length.\n   *\n   * @post  length() >= contiguousLength\n   *\n   * @methodset Chaining\n   */\n  void gather(std::size_t contiguousLength) {\n    if (!isChained() || length_ >= contiguousLength) {\n      return;\n    }\n    coalesceSlow(contiguousLength);\n  }\n\n  /**\n   * Copy an IOBuf chain.\n   *\n   * This is a shallow buffer copy; the source buffers will be shared.\n   *\n   * The new IOBuf chain will normally point to the same underlying data\n   * buffers as the original chain.  (The one exception to this is if some of\n   * the IOBufs in this chain contain small internal data buffers which cannot\n   * be shared.)\n   *\n   * @methodset Makers\n   */\n  std::unique_ptr<IOBuf> clone() const { return cloneImpl(nullptr); }\n\n  /**\n   * @copydoc clone()\n   *\n   * Similar to clone(), but returns by value rather than heap-allocating.\n   */\n  IOBuf cloneAsValue() const;\n\n  /**\n   * Copy an individual IOBuf.\n   *\n   * Only clone the buffer of the current IOBuf; ignore chained IOBufs.\n   *\n   * @methodset Makers\n   */\n  std::unique_ptr<IOBuf> cloneOne() const { return cloneOneImpl(nullptr); }\n\n  /**\n   * @copydoc cloneOne()\n   *\n   * Similar to cloneOne(), but returns by value rather than heap-allocating.\n   */\n  IOBuf cloneOneAsValue() const;\n\n  /**\n   * Copy an IOBuf chain into a single buffer.\n   *\n   * Semantically similar to .clone().coalesce(), but without the intermediate\n   * allocations.\n   *\n   * The new IOBuf will have at least as much headroom as the first IOBuf in the\n   * chain, and at least as much tailroom as the last IOBuf in the chain.\n   *\n   * @return  An IOBuf for which isChained() == false, and whose data is the\n   *          same as coalesce()\n   *\n   * @throws std::bad_alloc on error.\n   *\n   * @methodset Makers\n   */\n  std::unique_ptr<IOBuf> cloneCoalesced() const;\n\n  /**\n   * @copydoc cloneCoalesced()\n   *\n   * @param newHeadroom  How much headroom the new coalesced chain should have,\n   *                     instead of mimicking the original headroom\n   * @param newTailroom  How much tailroom the new coalesced chain should have,\n   *                     instead of mimicking the original tailroom\n   */\n  std::unique_ptr<IOBuf> cloneCoalescedWithHeadroomTailroom(\n      std::size_t newHeadroom, std::size_t newTailroom) const;\n\n  /**\n   * @copydoc cloneCoalesced()\n   *\n   * Similar to cloneCoalesced(), but returns by value rather than\n   * heap-allocating.\n   */\n  IOBuf cloneCoalescedAsValue() const;\n\n  /**\n   * @copydoc cloneCoalescedWithHeadroomTailroom(std::size_t, std::size_t) const\n   *\n   * Similar to cloneCoalescedWithHeadroomTailroom(), but returns by value\n   * rather than heap-allocating.\n   */\n  IOBuf cloneCoalescedAsValueWithHeadroomTailroom(\n      std::size_t newHeadroom, std::size_t newTailroom) const;\n\n  /**\n   * @copydoc clone()\n   *\n   * Similar to clone(), but returns by argument. The argument will become the\n   * clone's head. Other nodes in the chain (if any) will be allocated on the\n   * heap as usual.\n   *\n   * @param[out] other  An IOBuf to assign the clone to\n   */\n  void cloneInto(IOBuf& other) const { other = cloneAsValue(); }\n\n  /**\n   * @copydoc cloneOne()\n   *\n   * Similar to cloneOne(), but returns by argument. The argument will become\n   * the clone.\n   *\n   * @param[out] other  An IOBuf to assign the clone to\n   */\n  void cloneOneInto(IOBuf& other) const { other = cloneOneAsValue(); }\n\n  /**\n   * Returns a new IOBuf whose buffer is this buffer's tail. The latter is\n   * trimmed to 0 to relinquish ownership of it. The returned IOBuf is unshared,\n   * and it holds a shared reference to the IOBuf that originally owned the\n   * buffer, extending its lifetime.\n   *\n   * This method is best-effort and allowed to fail if the operation is not\n   * possible (for example, if the buffer is shared) or inefficient. In these\n   * cases, nullptr is returned and this IOBuf is unchanged.\n   */\n  std::unique_ptr<IOBuf> maybeSplitTail();\n\n  /**\n   * Append the chain data into the provided container.\n   *\n   * This is meant to be used with containers such as std::string or\n   * std::vector<char>, but any container which supports reserve(), insert(),\n   * and has char or unsigned char value type is supported.\n   *\n   * @methodset Conversions\n   */\n  template <class Container>\n  void appendTo(Container& container) const;\n\n  /**\n   * Returns a container containing the chain data.\n   *\n   * @copydetails appendTo(Container&) const\n   *\n   * @tparam Container  The type of container to return.\n   *\n   * @returns  A Container whose data equals the coalesced data of this chain\n   */\n  template <class Container>\n  Container to() const;\n\n  /**\n   * Convenience version of to<std::string>() that works when called\n   * on a dependent name in a template function without having to use\n   * the \"template\" keyword.\n   */\n  std::string toString() const { return to<std::string>(); }\n\n  /**\n   * Create an IOBuf from a std::string. Avoids copying the contents of the\n   * string, at the cost of an extra allocation.\n   */\n  static std::unique_ptr<IOBuf> fromString(std::unique_ptr<std::string>);\n  static std::unique_ptr<IOBuf> fromString(std::string s) {\n    return fromString(std::make_unique<std::string>(std::move(s)));\n  }\n\n  /**\n   * Get an iovector suitable for e.g. writev()\n   *\n   *   auto iov = buf->getIov();\n   *   auto xfer = writev(fd, iov.data(), iov.size());\n   *\n   * Naturally, the returned iovector is invalid if you modify the buffer\n   * chain.\n   *\n   * @methodset IOV\n   */\n  folly::fbvector<struct iovec> getIov() const;\n\n  /**\n   * Update an existing iovec array with the IOBuf data.\n   *\n   * New iovecs will be appended to the existing vector; anything already\n   * present in the vector will be left unchanged.\n   *\n   * Naturally, the returned iovec data will be invalid if you modify the\n   * buffer chain.\n   *\n   * @param[out] iov  The iovector to append to\n   *\n   * @methodset IOV\n   */\n  void appendToIov(folly::fbvector<struct iovec>* iov) const;\n\n  struct FillIovResult {\n    // How many iovecs were filled (or 0 on error).\n    size_t numIovecs;\n    // The total length of filled iovecs (or 0 on error).\n    size_t totalLength;\n  };\n\n  /**\n   * Fill an iovec array with the IOBuf data.\n   *\n   * Returns a struct with two fields: the number of iovec filled, and total\n   * size of the iovecs filled. If there are more buffer than iovec, returns 0\n   * in both fields.\n   * This version is suitable to use with stack iovec arrays.\n   *\n   * Naturally, the filled iovec data will be invalid if you modify the\n   * buffer chain.\n   *\n   * @param[out] iov  The iovector to append to\n   * @param len  The size of the iov array\n   *\n   * @methodset IOV\n   */\n  FillIovResult fillIov(struct iovec* iov, size_t len) const;\n\n  /**\n   * Convert an iovec array into an IOBuf.\n   *\n   * A helper that wraps a number of iovecs into an IOBuf chain.  If count == 0,\n   * then a zero length buf is returned.  This function never returns nullptr.\n   *\n   * @param vec  The iovec array to convert to an IOBuf chain\n   * @param count  The size of the iovec array\n   *\n   * @methodset IOV\n   */\n  static std::unique_ptr<IOBuf> wrapIov(const iovec* vec, size_t count);\n\n  /**\n   * Take ownership of an iovec, turning it into an IOBuf.\n   *\n   * A helper that takes ownerships a number of iovecs into an IOBuf chain.  If\n   * count == 0, then a zero length buf is returned.  This function never\n   * returns nullptr.\n   *\n   * @param vec  The iovec array to convert to an IOBuf chain\n   * @param count  The size of the iovec array\n   * @param freeFn  The function to call when buf is to be freed\n   * @param userData  An additional arbitrary void* argument to supply to freeFn\n   * @param freeOnError  Whether the buffer should be freed if this function\n   *                     throws an exception\n   *\n   * @methodset IOV\n   */\n  static std::unique_ptr<IOBuf> takeOwnershipIov(\n      const iovec* vec,\n      size_t count,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true);\n\n#if FOLLY_HAS_MEMORY_RESOURCE\n\n  /**\n   * PMR support.\n   *\n   * These methods allow constructing IOBuf chains whose nodes are allocated\n   * using the provided memory resource. Aside from the use of\n   * std::pmr::memory_resource for allocating/deallocating the nodes, their\n   * semantics are equivalent to their non-PMR counterparts. Currently only a\n   * subset of IOBuf construction methods is implemented, enough to support the\n   * typical lifetime of an IOBuf chain: buffer can be externally allocated and\n   * wrapped with takeOwnership(), and cloned. More methods can be supported as\n   * needed.\n   *\n   * The thread-safety requirements of the provided memory_resource depend on\n   * the lifetime of the IOBufs. The allocate() method is only called in the\n   * context of the IOBuf method that accepts it; deallocate() is called when\n   * the IOBuf storage is eventually destroyed. Thus, in the common case where\n   * the chain is constructed in a single thread (which owns the\n   * memory_resource) and then handed off, it is sufficient that the\n   * memory_resource supports concurrent deallocate() calls, as different nodes\n   * in the chain may be destroyed by different threads.\n   *\n   * All the methods allow mr to be nullptr, which is equivalent to calling\n   * the non-PMR counterparts.\n   */\n\n  static std::unique_ptr<IOBuf> takeOwnership(\n      std::pmr::memory_resource* mr,\n      void* buf,\n      std::size_t capacity,\n      std::size_t offset,\n      std::size_t length,\n      FreeFunction freeFn = nullptr,\n      void* userData = nullptr,\n      bool freeOnError = true) {\n    return takeOwnershipImpl(\n        buf,\n        capacity,\n        offset,\n        length,\n        freeFn,\n        userData,\n        freeOnError,\n        TakeOwnershipOption::DEFAULT,\n        mr);\n  }\n  std::unique_ptr<IOBuf> clone(std::pmr::memory_resource* mr) const {\n    return cloneImpl(mr);\n  }\n  std::unique_ptr<IOBuf> cloneOne(std::pmr::memory_resource* mr) const {\n    return cloneOneImpl(mr);\n  }\n\n#endif /* FOLLY_HAS_MEMORY_RESOURCE */\n\n  /**\n   * Overridden operator new and delete.\n   *\n   * These perform specialized memory management to help support\n   * createCombined(), which allocates IOBuf objects together with the buffer\n   * data.\n   *\n   * @methodset Memory\n   */\n  void* operator new(size_t size);\n\n  /**\n   * Overridden operator new.\n   * @methodset Memory\n   */\n  void* operator new(size_t size, void* ptr);\n\n  /**\n   * Overridden operator delete.\n   * @methodset Memory\n   */\n  void operator delete(void* ptr);\n\n  /**\n   * Overridden operator delete.\n   * @methodset Memory\n   */\n  void operator delete(void* ptr, void* placement);\n\n  /**\n   * Destructively convert to an fbstring.\n   *\n   * Destructively convert this IOBuf to a fbstring efficiently.\n   * We rely on fbstring's AcquireMallocatedString constructor to\n   * transfer memory.\n   *\n   * @methodset Conversions\n   */\n  fbstring moveToFbString();\n\n  /**\n   * Iterate over the IOBufs in this chain.\n   *\n   * The iterators dereference to a ByteRange.\n   *\n   * @methodset Iterators\n   */\n  Iterator cbegin() const;\n\n  /// @copydoc cbegin()\n  Iterator cend() const;\n\n  /// @copydoc cbegin()\n  Iterator begin() const;\n\n  /// @copydoc cbegin()\n  Iterator end() const;\n\n  /**\n   * Create a new null buffer.\n   *\n   * This can be used to allocate an empty IOBuf on the stack.  It will have no\n   * space allocated for it.  This is generally useful only to later use move\n   * assignment to fill out the IOBuf.\n   */\n  IOBuf() noexcept;\n\n  /**\n   * Move constructor.\n   *\n   * In general, you should only ever move the head of an IOBuf chain.\n   * Internal nodes in an IOBuf chain are owned by the head of the chain, and\n   * should not be moved from.  (Technically, nothing prevents you from moving\n   * a non-head node, but the moved-to node will replace the moved-from node in\n   * the chain.  This has implications for ownership, since non-head nodes are\n   * owned by the chain head.  You are then responsible for relinquishing\n   * ownership of the moved-to node, and manually deleting the moved-from\n   * node.)\n   */\n  IOBuf(IOBuf&& other) noexcept;\n\n  /**\n   * Move assignment operator.\n   *\n   * With the assignment operator, the destination should be the head of an\n   * IOBuf chain or a solitary IOBuf not part of a chain.  If the destination is\n   * part of a chain, all other IOBufs in the chain will be deleted.\n   */\n  IOBuf& operator=(IOBuf&& other) noexcept;\n\n  /**\n   * Copy constructor.\n   *\n   * @see cloneAsValue()\n   */\n  IOBuf(const IOBuf& other);\n\n  /**\n   * Copy assignment operator.\n   *\n   * @copydetails operator=(IOBuf&&)\n   * @see cloneAsValue()\n   */\n  IOBuf& operator=(const IOBuf& other);\n\n private:\n  enum class TakeOwnershipOption { DEFAULT, STORE_SIZE };\n\n  static std::unique_ptr<IOBuf> takeOwnershipImpl(\n      void* buf,\n      std::size_t capacity,\n      std::size_t offset,\n      std::size_t length,\n      FreeFunction freeFn,\n      void* userData,\n      bool freeOnError,\n      TakeOwnershipOption option,\n      std::pmr::memory_resource* mr = nullptr);\n  std::unique_ptr<IOBuf> cloneImpl(std::pmr::memory_resource* mr) const;\n  std::unique_ptr<IOBuf> cloneOneImpl(std::pmr::memory_resource* mr) const;\n\n  struct SharedInfoObserverEntryBase {\n    SharedInfoObserverEntryBase* prev{this};\n    SharedInfoObserverEntryBase* next{this};\n\n    virtual ~SharedInfoObserverEntryBase() = default;\n\n    virtual void afterFreeExtBuffer() const noexcept = 0;\n    virtual void afterReleaseExtBuffer() const noexcept = 0;\n  };\n\n  template <typename Observer>\n  struct SharedInfoObserverEntry : SharedInfoObserverEntryBase {\n    std::decay_t<Observer> observer;\n\n    explicit SharedInfoObserverEntry(Observer&& obs) noexcept(\n        noexcept(Observer(std::forward<Observer>(obs))))\n        : observer(std::forward<Observer>(obs)) {}\n\n    void afterFreeExtBuffer() const noexcept final {\n      observer.afterFreeExtBuffer();\n    }\n\n    void afterReleaseExtBuffer() const noexcept final {\n      observer.afterReleaseExtBuffer();\n    }\n  };\n\n  struct SharedInfo {\n    enum class StorageType : uint8_t {\n      kInvalid, // Sentinel value.\n      kAllocated,\n      kHeapFullStorage,\n      kExtBuffer,\n    };\n\n    SharedInfo(FreeFunction fn, void* arg, StorageType st);\n\n    static void releaseStorage(\n        IOBuf* parent, StorageType storageType, SharedInfo* info) noexcept;\n\n    using ObserverCb = folly::FunctionRef<void(SharedInfoObserverEntryBase&)>;\n    static void invokeAndDeleteEachObserver(\n        SharedInfoObserverEntryBase* observerListHead, ObserverCb cb) noexcept;\n\n    // A pointer to a function to call to free the buffer when the refcount\n    // hits 0.  If this is null, free() will be used instead.\n    FreeFunction freeFn{nullptr};\n    void* userData{nullptr};\n    SharedInfoObserverEntryBase* observerListHead{nullptr};\n    std::atomic<uint32_t> refcount{1};\n    bool externallyShared{false};\n    StorageType storageType = StorageType::kInvalid;\n    MicroSpinLock observerListLock{0};\n  };\n\n  // Helper structs for use by operator new and delete\n  struct HeapPrefix;\n  struct HeapStorage;\n  struct HeapFullStorage;\n\n  // Force inlining to allow optimizing away some branches in the common cases.\n  template <class StorageType>\n  FOLLY_ALWAYS_INLINE static std::pair<StorageType*, size_t> allocateStorage(\n      std::pmr::memory_resource* mr = nullptr, size_t additionalBuffer = 0);\n  static std::pmr::memory_resource* getMemoryResource(\n      const HeapStorage* storage);\n  static void freeStorage(HeapStorage* storage);\n\n  /**\n   * Create a new IOBuf pointing to an external buffer.\n   *\n   * The caller is responsible for holding a reference count for this new\n   * IOBuf.  The IOBuf constructor does not automatically increment the\n   * reference count.\n   */\n  struct InternalConstructor {}; // avoid conflicts\n  IOBuf(\n      InternalConstructor,\n      SharedInfo* sharedInfo,\n      uint8_t* buf,\n      std::size_t capacity,\n      uint8_t* data,\n      std::size_t length) noexcept;\n\n  void unshareOneSlow();\n  void unshareChained();\n  void makeManagedChained();\n  void coalesceSlow();\n  void coalesceSlow(size_t maxLength);\n  // newLength must be the entire length of the buffers between this and\n  // end (no truncation)\n  void coalesceAndReallocate(\n      size_t newHeadroom, size_t newLength, IOBuf* end, size_t newTailroom);\n  void coalesceAndReallocate(size_t newLength, IOBuf* end) {\n    coalesceAndReallocate(headroom(), newLength, end, end->prev_->tailroom());\n  }\n  void decrementRefcount() noexcept;\n  void reserveSlow(std::size_t minHeadroom, std::size_t minTailroom);\n  void freeExtBuffer() noexcept;\n\n  static size_t goodExtBufferSize(std::size_t minCapacity);\n  static void initExtBuffer(\n      uint8_t* buf,\n      size_t mallocSize,\n      SharedInfo** infoReturn,\n      std::size_t* capacityReturn);\n  static void allocExtBuffer(\n      std::size_t minCapacity,\n      uint8_t** bufReturn,\n      SharedInfo** infoReturn,\n      std::size_t* capacityReturn);\n  static void decrementStorageRefcount(HeapStorage* storage) noexcept;\n\n  /*\n   * Member variables\n   */\n\n  /*\n   * A pointer to the start of the data referenced by this IOBuf, and the\n   * length of the data.\n   *\n   * This may refer to any subsection of the actual buffer capacity.\n   */\n  std::size_t length_{0};\n  uint8_t* data_{nullptr};\n\n  std::size_t capacity_{0};\n  uint8_t* buf_{nullptr};\n\n  /*\n   * Links to the next and the previous IOBuf in this chain.\n   *\n   * The chain is circularly linked (the last element in the chain points back\n   * at the head), and next_ and prev_ can never be null.  If this IOBuf is the\n   * only element in the chain, next_ and prev_ will both point to this.\n   */\n  IOBuf* next_{this};\n  IOBuf* prev_{this};\n\n  SharedInfo* sharedInfo_{nullptr};\n\n  struct DeleterBase {\n    virtual ~DeleterBase() {}\n    virtual void dispose(void* p) noexcept = 0;\n  };\n\n  template <class UniquePtr>\n  struct UniquePtrDeleter : public DeleterBase {\n    using Pointer = typename UniquePtr::pointer;\n    using Deleter = typename UniquePtr::deleter_type;\n\n    explicit UniquePtrDeleter(Deleter deleter) : deleter_(std::move(deleter)) {}\n    void dispose(void* p) noexcept override {\n      deleter_(static_cast<Pointer>(p));\n      delete this;\n    }\n\n   private:\n    Deleter deleter_;\n  };\n\n  static void freeUniquePtrBuffer(void* ptr, void* userData) noexcept {\n    static_cast<DeleterBase*>(userData)->dispose(ptr);\n  }\n};\n\n/**\n * Hasher for IOBuf objects. Hashes the entire chain using SpookyHashV2.\n */\nstruct IOBufHash {\n  size_t operator()(const IOBuf& buf) const noexcept;\n  size_t operator()(const std::unique_ptr<IOBuf>& buf) const noexcept {\n    return operator()(buf.get());\n  }\n  size_t operator()(const IOBuf* buf) const noexcept {\n    return buf ? (*this)(*buf) : 0;\n  }\n};\n\n/**\n * Ordering for IOBuf objects. Compares data in the entire chain.\n */\nstruct IOBufCompare {\n  ordering operator()(const IOBuf& a, const IOBuf& b) const {\n    return &a == &b ? ordering::eq : impl(a, b);\n  }\n  ordering operator()(\n      const std::unique_ptr<IOBuf>& a, const std::unique_ptr<IOBuf>& b) const {\n    return operator()(a.get(), b.get());\n  }\n  ordering operator()(const IOBuf* a, const IOBuf* b) const {\n    // clang-format off\n    return\n        !a && !b ? ordering::eq :\n        !a && b ? ordering::lt :\n        a && !b ? ordering::gt :\n        operator()(*a, *b);\n    // clang-format on\n  }\n\n private:\n  ordering impl(IOBuf const& a, IOBuf const& b) const noexcept;\n};\n\n/**\n * Equality predicate for IOBuf objects. Compares data in the entire chain.\n */\nstruct IOBufEqualTo : compare_equal_to<IOBufCompare> {};\n\n/**\n * Inequality predicate for IOBuf objects. Compares data in the entire chain.\n */\nstruct IOBufNotEqualTo : compare_not_equal_to<IOBufCompare> {};\n\n/**\n * Less predicate for IOBuf objects. Compares data in the entire chain.\n */\nstruct IOBufLess : compare_less<IOBufCompare> {};\n\n/**\n * At-most predicate for IOBuf objects. Compares data in the entire chain.\n */\nstruct IOBufLessEqual : compare_less_equal<IOBufCompare> {};\n\n/**\n * Greater predicate for IOBuf objects. Compares data in the entire chain.\n */\nstruct IOBufGreater : compare_greater<IOBufCompare> {};\n\n/**\n * At-least predicate for IOBuf objects. Compares data in the entire chain.\n */\nstruct IOBufGreaterEqual : compare_greater_equal<IOBufCompare> {};\n\ntemplate <class UniquePtr>\ntypename std::enable_if<\n    detail::IsUniquePtrToSL<UniquePtr>::value,\n    std::unique_ptr<IOBuf>>::type\nIOBuf::takeOwnership(UniquePtr&& buf, size_t count) {\n  size_t size = count * sizeof(typename UniquePtr::element_type);\n  auto deleter = new UniquePtrDeleter<UniquePtr>(buf.get_deleter());\n  return takeOwnership(\n      buf.release(), size, &IOBuf::freeUniquePtrBuffer, deleter);\n}\n\ninline std::unique_ptr<IOBuf> IOBuf::copyBuffer(\n    const void* data,\n    std::size_t size,\n    std::size_t headroom,\n    std::size_t minTailroom) {\n  std::size_t capacity;\n  if (!folly::checked_add(&capacity, size, headroom, minTailroom)) {\n    throw_exception(std::length_error(\"\"));\n  }\n  std::unique_ptr<IOBuf> buf = create(capacity);\n  buf->advance(headroom);\n  if (size != 0) {\n    memcpy(buf->writableData(), data, size);\n  }\n  buf->append(size);\n  return buf;\n}\n\ninline std::unique_ptr<IOBuf> IOBuf::copyBuffer(\n    StringPiece buf, std::size_t headroom, std::size_t minTailroom) {\n  return copyBuffer(buf.data(), buf.size(), headroom, minTailroom);\n}\n\ninline std::unique_ptr<IOBuf> IOBuf::maybeCopyBuffer(\n    StringPiece buf, std::size_t headroom, std::size_t minTailroom) {\n  if (buf.empty()) {\n    return nullptr;\n  }\n  return copyBuffer(buf.data(), buf.size(), headroom, minTailroom);\n}\n\nclass IOBuf::Iterator\n    : public detail::IteratorFacade<\n          IOBuf::Iterator,\n          ByteRange const,\n          std::forward_iterator_tag> {\n public:\n  // Note that IOBufs are stored as a circular list without a guard node,\n  // so pos == end is ambiguous (it may mean \"begin\" or \"end\").  To solve\n  // the ambiguity (at the cost of one extra comparison in the \"increment\"\n  // code path), we define end iterators as having pos_ == end_ == nullptr\n  // and we only allow forward iteration.\n  explicit Iterator(const IOBuf* pos, const IOBuf* end) : pos_(pos), end_(end) {\n    // Sadly, we must return by const reference, not by value.\n    if (pos_) {\n      setVal();\n    }\n  }\n\n  Iterator() {}\n\n  Iterator(Iterator const& rhs) : Iterator(rhs.pos_, rhs.end_) {}\n\n  Iterator& operator=(Iterator const& rhs) {\n    pos_ = rhs.pos_;\n    end_ = rhs.end_;\n    if (pos_) {\n      setVal();\n    }\n    return *this;\n  }\n\n  const ByteRange& dereference() const { return val_; }\n\n  bool equal(const Iterator& other) const {\n    // We must compare end_ in addition to pos_, because forward traversal\n    // requires that if two iterators are equal (a == b) and dereferenceable,\n    // then ++a == ++b.\n    return pos_ == other.pos_ && end_ == other.end_;\n  }\n\n  void increment() {\n    pos_ = pos_->next();\n    adjustForEnd();\n  }\n\n private:\n  void setVal() { val_ = ByteRange(pos_->data(), pos_->tail()); }\n\n  void adjustForEnd() {\n    if (pos_ == end_) {\n      pos_ = end_ = nullptr;\n      val_ = ByteRange();\n    } else {\n      setVal();\n    }\n  }\n\n  const IOBuf* pos_{nullptr};\n  const IOBuf* end_{nullptr};\n  ByteRange val_;\n};\n\ninline IOBuf::Iterator IOBuf::begin() const {\n  return cbegin();\n}\ninline IOBuf::Iterator IOBuf::end() const {\n  return cend();\n}\n\ntemplate <class Container>\nvoid IOBuf::appendTo(Container& container) const {\n  static_assert(\n      (std::is_same<typename Container::value_type, char>::value ||\n       std::is_same<typename Container::value_type, unsigned char>::value),\n      \"Unsupported value type\");\n  container.reserve(container.size() + computeChainDataLength());\n  for (auto data : *this) {\n    container.insert(container.end(), data.begin(), data.end());\n  }\n}\n\ntemplate <class Container>\nContainer IOBuf::to() const {\n  Container result;\n  appendTo(result);\n  return result;\n}\n\nusing IOBufFactory = Function<std::unique_ptr<IOBuf>(size_t capacity)>;\n\n} // namespace folly\n\nFOLLY_POP_WARNING\n"
  },
  {
    "path": "folly/io/IOBufIovecBuilder.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/IOBufIovecBuilder.h>\n#include <folly/portability/IOVec.h>\n\nnamespace folly {\nsize_t IOBufIovecBuilder::allocateBuffers(IoVecVec& iovs, size_t len) {\n  iovs.clear();\n  size_t total = 0;\n  for (size_t i = 0; (i < IOV_MAX) && (total < len); ++i) {\n    DCHECK_LE(i, buffers_.size());\n    if (i == buffers_.size()) {\n      // TODO - allocate RefCountMem and the main buffer together\n      // if it does not cause the allocation to spill into the next\n      // jemalloc allocation class\n      buffers_.push_back(new RefCountMem(options_.blockSize_));\n    }\n    DCHECK_LT(i, buffers_.size());\n\n    struct iovec iov;\n    iov.iov_base = buffers_[i]->usableMem();\n    iov.iov_len = buffers_[i]->usableSize();\n    iovs.emplace_back(iov);\n\n    total += buffers_[i]->usableSize();\n  }\n\n  return total;\n}\n\nstd::unique_ptr<folly::IOBuf> IOBufIovecBuilder::extractIOBufChain(size_t len) {\n  std::unique_ptr<folly::IOBuf> ioBuf;\n  std::unique_ptr<folly::IOBuf> tmp;\n\n  while (len > 0) {\n    CHECK(!buffers_.empty());\n    auto* buf = buffers_.front();\n    auto size = buf->usableSize();\n\n    if (len >= size) {\n      // no need to inc the ref count since we're transferring ownership\n      tmp = folly::IOBuf::takeOwnership(\n          buf->usableMem(), size, RefCountMem::freeMem, buf);\n      buffers_.pop_front();\n      len -= size;\n    } else {\n      buf->addRef();\n      tmp = folly::IOBuf::takeOwnership(\n          buf->usableMem(), len, RefCountMem::freeMem, buf);\n      buf->incUsedMem(len);\n      len = 0;\n    }\n\n    CHECK(!tmp->isShared());\n\n    if (ioBuf) {\n      ioBuf->prependChain(std::move(tmp));\n    } else {\n      ioBuf = std::move(tmp);\n    }\n  }\n\n  return ioBuf;\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/io/IOBufIovecBuilder.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <deque>\n#include <folly/io/IOBuf.h>\n#include <folly/memory/Malloc.h>\n\nnamespace folly {\n/**\n * IOBufIovecBuilder exists to help allocate and fill IOBuf chains using\n * iovec-based scatter/gather APIs.\n *\n * It provides APIs to allocate buffers for use with iovec-based system calls,\n * and then construct IOBuf chains pointing to the buffer data that has been\n * populated.\n *\n * This class allows performing repeated vectored reads and minimizing the\n * number of separate allocations if data is received in small chunks, while\n * still generating unshared IOBuf instances.  For instance, this helps support\n * in place decryption mechanisms which require unshared IOBuf instances.\n */\n\nclass IOBufIovecBuilder {\n private:\n  /**\n   * This is a helper class that is passed to IOBuf::takeOwnership()\n   * for use as the custom free function.\n   *\n   * This class allows multiple IOBuf objects to each point to non-overlapping\n   * sections of the same buffer, allowing each IOBuf to consider its buffer\n   * as non-shared even though they do share a single allocation.  This class\n   * performs additional reference counting to ensure that the entire allocation\n   * is freed only when all IOBufs referring to it have been destroyed.\n   */\n  struct RefCountMem {\n    explicit RefCountMem(size_t size) {\n      len_ = folly::goodMallocSize(size);\n      mem_ = ::malloc(len_);\n    }\n\n    ~RefCountMem() { folly::sizedFree(mem_, len_); }\n\n    void* usableMem() const { return static_cast<uint8_t*>(mem_) + used_; }\n\n    size_t usableSize() const { return len_ - used_; }\n\n    void incUsedMem(size_t len) {\n      used_ += len;\n      DCHECK_LE(used_, len_);\n    }\n\n    static void freeMem(void* buf, void* userData) {\n      std::ignore = buf;\n      reinterpret_cast<RefCountMem*>(userData)->decRef();\n    }\n\n    void addRef() { refcount_.fetch_add(1, std::memory_order_acq_rel); }\n\n    void decRef() {\n      // Avoid doing atomic decrement if the refcount is 1.\n      // This is safe, because it means this is the last reference\n      // Anything trying to copy it is already undefined behavior.\n      if (refcount_.load(std::memory_order_acquire) > 1) {\n        size_t newcnt = refcount_.fetch_sub(1, std::memory_order_acq_rel) - 1;\n        if (newcnt > 0) {\n          return;\n        }\n      }\n\n      delete this;\n    }\n\n   private:\n    std::atomic<size_t> refcount_{1};\n    void* mem_{nullptr};\n    size_t len_{0};\n    size_t used_{0};\n  };\n\n public:\n  struct Options {\n    static constexpr size_t kDefaultBlockSize = 16 * 1024;\n    size_t blockSize_{kDefaultBlockSize};\n\n    Options& setBlockSize(size_t blockSize) {\n      blockSize_ = blockSize;\n\n      return *this;\n    }\n  };\n\n  IOBufIovecBuilder() = delete;\n  IOBufIovecBuilder(const IOBufIovecBuilder&) = delete;\n  IOBufIovecBuilder(IOBufIovecBuilder&&) = delete;\n\n  IOBufIovecBuilder& operator=(const IOBufIovecBuilder&) = delete;\n  IOBufIovecBuilder& operator=(IOBufIovecBuilder&&) = delete;\n\n  explicit IOBufIovecBuilder(const Options& options) : options_(options) {}\n  ~IOBufIovecBuilder() {\n    for (auto& buf : buffers_) {\n      buf->decRef();\n    }\n  }\n\n  using IoVecVec = std::vector<struct iovec>;\n  /**\n   * Obtain a vector of writable blocks.  This allocates memory in blocks of\n   * `Options::blockSize_` bytes each.  Unused memory from previous calls to\n   * `allocateBuffers()` will be re-used, and additional memory will be\n   * allocated if necessary.\n   *\n   * This code will attempt to allocate at least `len` bytes, but may allocate\n   * less if that would require more than IOV_MAX chunks.  This API never\n   * returns more than IOV_MAX separate blocks.  The returned length allocated\n   * may be more than `len` if `len` was not an even multiple of the block size,\n   * as the length allocated will be rounded up to the next full block size.\n   *\n   * The intended use of this API is for the caller to call allocateBuffers()\n   * to allocate space, to then fill the buffers using an API such as readv() or\n   * recvmsg(), and then obtain an IOBuf chain to the data that was read by\n   * calling extractIOBufChain() with the amount of data that was read.\n   *\n   * @param[out] iovs  vector of `struct iovec` that will be populated\n   *                   The vector will be cleared before any new entries\n   *                   will be appended\n   * @param      len   A requested number of bytes to be available.\n   * @return     The number of allocated bytes.\n   *\n   */\n  size_t allocateBuffers(IoVecVec& iovs, size_t len);\n\n  /**\n   * Tell the queue that the caller has written data into the first n\n   * bytes provided by the previous allocateBuffers() call.\n   * @param      len   Number of bytes to be consumed\n   * @return     The IOBuf chain\n   *\n   * @note len should be less than or equal to the size returned by\n   *       allocateBuffers().  If len is zero, the caller may skip the call\n   *       to postallocate().  If len is nonzero, the caller must not\n   *       invoke any other non-const methods on this IOBufIovecBuilder between\n   *       the call to allocateBufferse and the call to extractIOBufChain().\n   */\n  std::unique_ptr<folly::IOBuf> extractIOBufChain(size_t len);\n\n private:\n  Options options_;\n  std::deque<RefCountMem*> buffers_;\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/IOBufQueue.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/IOBufQueue.h>\n\n#include <cstring>\n#include <stdexcept>\n\nusing std::make_pair;\nusing std::pair;\nusing std::unique_ptr;\n\nnamespace {\n\nusing folly::IOBuf;\n\nconst size_t MIN_ALLOC_SIZE = 2000;\nconst size_t MAX_ALLOC_SIZE = 8000;\n\n/**\n * Convenience functions to append chain src to chain dst.\n */\ntemplate <class Src, class Next>\nvoid packInto(IOBuf* tail, Src& src, Next next) {\n  if (tail->isSharedOne()) {\n    return;\n  }\n\n  // Copy up to kMaxPackCopy bytes if we can free buffers; this helps reduce\n  // waste (the tail's tailroom and the head's headroom) when joining two\n  // IOBufQueues together.\n  size_t copyRemaining = folly::IOBufQueue::kMaxPackCopy;\n  std::size_t n;\n  while (src && (n = src->length()) <= copyRemaining && n <= tail->tailroom()) {\n    if (n > 0) {\n      memcpy(tail->writableTail(), src->data(), n);\n      tail->append(n);\n      copyRemaining -= n;\n    }\n    src = next(std::move(src));\n  }\n}\n\nvoid appendToChain(unique_ptr<IOBuf>& dst, unique_ptr<IOBuf>&& src, bool pack) {\n  if (dst == nullptr) {\n    dst = std::move(src);\n  } else {\n    IOBuf* tail = dst->prev();\n    if (pack) {\n      packInto(tail, src, [](auto&& cur) { return cur->pop(); });\n    }\n    if (src) {\n      tail->insertAfterThisOne(std::move(src));\n    }\n  }\n}\n\n} // namespace\n\nnamespace folly {\n\nIOBufQueue::IOBufQueue(const Options& options)\n    : options_(options), cachePtr_(&localCache_) {\n  localCache_.attached = true;\n}\n\nIOBufQueue::~IOBufQueue() {\n  clearWritableRangeCache();\n}\n\nIOBufQueue::IOBufQueue(IOBufQueue&& other) noexcept\n    : options_(other.options_), cachePtr_(&localCache_) {\n  other.clearWritableRangeCache();\n\n  head_ = std::move(other.head_);\n  factory_ = std::exchange(other.factory_, nullptr);\n\n  tailStart_ = std::exchange(other.tailStart_, nullptr);\n  localCache_.cachedRange =\n      std::exchange(other.localCache_.cachedRange, {nullptr, nullptr});\n  localCache_.attached = true;\n  chainLength_ = std::exchange(other.chainLength_, 0);\n}\n\nIOBufQueue& IOBufQueue::operator=(IOBufQueue&& other) noexcept {\n  if (&other != this) {\n    other.clearWritableRangeCache();\n    clearWritableRangeCache();\n\n    options_ = other.options_;\n    head_ = std::move(other.head_);\n    factory_ = std::exchange(other.factory_, nullptr);\n\n    tailStart_ = std::exchange(other.tailStart_, nullptr);\n    localCache_.cachedRange =\n        std::exchange(other.localCache_.cachedRange, {nullptr, nullptr});\n    localCache_.attached = true;\n    chainLength_ = std::exchange(other.chainLength_, 0);\n  }\n  return *this;\n}\n\nstd::pair<void*, std::size_t> IOBufQueue::headroom() {\n  // Note, headroom is independent from the tail, so we don't need to flush the\n  // cache.\n  if (head_) {\n    return std::make_pair(head_->writableBuffer(), head_->headroom());\n  } else {\n    return std::make_pair(nullptr, 0);\n  }\n}\n\nvoid IOBufQueue::markPrepended(std::size_t n) {\n  if (n == 0) {\n    return;\n  }\n  // Note, headroom is independent from the tail, so we don't need to flush the\n  // cache.\n  assert(head_);\n  head_->prepend(n);\n  chainLength_ += n;\n}\n\nvoid IOBufQueue::prepend(const void* buf, std::size_t n) {\n  // We're not touching the tail, so we don't need to flush the cache.\n  auto hroom = head_->headroom();\n  if (!head_ || hroom < n) {\n    throw std::overflow_error(\"Not enough room to prepend\");\n  }\n  memcpy(head_->writableBuffer() + hroom - n, buf, n);\n  head_->prepend(n);\n  chainLength_ += n;\n}\n\nvoid IOBufQueue::append(\n    unique_ptr<IOBuf>&& buf, bool pack, bool allowTailReuse) {\n  if (!buf) {\n    return;\n  }\n  auto guard = updateGuard(allowTailReuse);\n  if (options_.cacheChainLength) {\n    chainLength_ += buf->computeChainDataLength();\n  }\n  appendToChain(head_, std::move(buf), pack);\n}\n\nvoid IOBufQueue::append(\n    const folly::IOBuf& buf, bool pack, bool allowTailReuse) {\n  if (!head_ || !pack) {\n    append(buf.clone(), pack, allowTailReuse);\n    return;\n  }\n\n  auto guard = updateGuard(allowTailReuse);\n  if (options_.cacheChainLength) {\n    chainLength_ += buf.computeChainDataLength();\n  }\n\n  folly::IOBuf* tail = head_->prev();\n  const folly::IOBuf* src = &buf;\n  packInto(tail, src, [&](auto&& cur) {\n    auto next = cur->next();\n    return next != &buf ? next : nullptr;\n  });\n  if (!src) {\n    return; // Consumed full input.\n  }\n\n  // Clone the rest.\n  do {\n    head_->appendToChain(src->cloneOne());\n    src = src->next();\n  } while (src != &buf);\n}\n\nvoid IOBufQueue::append(folly::IOBuf&& buf, bool pack, bool allowTailReuse) {\n  // Equivalent to append(std::make_unique<folly::IOBuf>(std::move(buf)), ...)\n  // but that would make an unnecessary allocation if buf can be completely be\n  // packed into the tail, so we make sure to handle that case.\n  auto guard = updateGuard(allowTailReuse);\n  if (options_.cacheChainLength) {\n    chainLength_ += buf.computeChainDataLength();\n  }\n\n  std::unique_ptr<folly::IOBuf> rest;\n  if (head_ && pack) {\n    folly::IOBuf* src = &buf;\n    folly::IOBuf* tail = head_->prev();\n    packInto(tail, src, [&](auto* cur) {\n      rest = cur->pop();\n      return rest.get();\n    });\n    if (!src) {\n      return; // Consumed full input.\n    }\n    DCHECK(rest == nullptr || rest.get() == src);\n  }\n\n  if (!rest) {\n    // buf's head was not popped, so we need to heap-allocate it.\n    rest = std::make_unique<folly::IOBuf>(std::move(buf));\n  }\n\n  if (!head_) {\n    head_ = std::move(rest);\n  } else {\n    head_->appendToChain(std::move(rest));\n  }\n}\n\nvoid IOBufQueue::append(IOBufQueue& other, bool pack, bool allowTailReuse) {\n  if (!other.head_) {\n    return;\n  }\n  // We're going to chain other, thus we need to grab both guards.\n  auto otherGuard = other.updateGuard(allowTailReuse);\n  auto guard = updateGuard();\n  if (options_.cacheChainLength) {\n    if (other.options_.cacheChainLength) {\n      chainLength_ += other.chainLength_;\n    } else {\n      chainLength_ += other.head_->computeChainDataLength();\n    }\n  }\n  appendToChain(head_, std::move(other.head_), pack);\n  other.chainLength_ = 0;\n}\n\nvoid IOBufQueue::append(const void* buf, size_t len) {\n  auto guard = updateGuard();\n  auto src = static_cast<const uint8_t*>(buf);\n  while (len != 0) {\n    if ((head_ == nullptr) || head_->prev()->isSharedOne() ||\n        (head_->prev()->tailroom() == 0)) {\n      appendToChain(\n          head_,\n          createBuf(std::max(MIN_ALLOC_SIZE, std::min(len, MAX_ALLOC_SIZE))),\n          false);\n    }\n    IOBuf* last = head_->prev();\n    std::size_t copyLen = std::min(len, (size_t)last->tailroom());\n    memcpy(last->writableTail(), src, copyLen);\n    src += copyLen;\n    last->append(copyLen);\n    chainLength_ += copyLen;\n    len -= copyLen;\n  }\n}\n\nvoid IOBufQueue::wrapBuffer(\n    const void* buf, size_t len, std::size_t blockSize) {\n  auto src = static_cast<const uint8_t*>(buf);\n  while (len != 0) {\n    size_t n = std::min(len, size_t(blockSize));\n    append(IOBuf::wrapBuffer(src, n));\n    src += n;\n    len -= n;\n  }\n}\n\npair<void*, std::size_t> IOBufQueue::preallocateSlow(\n    std::size_t min, std::size_t newAllocationSize, std::size_t max) {\n  // Avoid grabbing update guard, since we're manually setting the cache ptrs.\n  flushCache();\n  // Allocate a new buffer of the requested max size.\n  unique_ptr<IOBuf> newBuf(createBuf(std::max(min, newAllocationSize)));\n\n  tailStart_ = newBuf->writableTail();\n  cachePtr_->cachedRange = std::pair<uint8_t*, uint8_t*>(\n      tailStart_, tailStart_ + newBuf->tailroom());\n  appendToChain(head_, std::move(newBuf), false);\n  return make_pair(writableTail(), std::min<std::size_t>(max, tailroom()));\n}\n\nvoid IOBufQueue::maybeReuseTail(folly::IOBuf& oldTail) {\n  if (oldTail.isSharedOne() || // Can't reuse a shared IOBuf.\n      &oldTail == head_->prev() || // No new IOBufs were appended.\n      // New tail IOBuf has at least as much tailroom and is writable.\n      (head_->prev()->tailroom() >= oldTail.tailroom() &&\n       !head_->prev()->isSharedOne())) {\n    return;\n  }\n\n  std::unique_ptr<IOBuf> newTail;\n  if (oldTail.length() == 0) {\n    // Nothing was written to the old tail, we can just move it to the end.\n    if (&oldTail == head_.get()) {\n      newTail = std::exchange(head_, head_->pop());\n    } else {\n      newTail = oldTail.unlink();\n    }\n  } else {\n    newTail = oldTail.maybeSplitTail();\n    if (!newTail) {\n      return;\n    }\n  }\n  head_->appendToChain(std::move(newTail));\n}\n\nunique_ptr<IOBuf> IOBufQueue::split(size_t n, bool throwOnUnderflow) {\n  auto guard = updateGuard();\n  unique_ptr<IOBuf> result;\n  while (n != 0) {\n    if (head_ == nullptr) {\n      if (throwOnUnderflow) {\n        throw std::underflow_error(\n            \"Attempt to remove more bytes than are present in IOBufQueue\");\n      } else {\n        break;\n      }\n    } else if (head_->length() <= n) {\n      n -= head_->length();\n      chainLength_ -= head_->length();\n      unique_ptr<IOBuf> remainder = head_->pop();\n      appendToChain(result, std::move(head_), false);\n      head_ = std::move(remainder);\n    } else {\n      unique_ptr<IOBuf> clone = head_->cloneOne();\n      clone->trimEnd(clone->length() - n);\n      appendToChain(result, std::move(clone), false);\n      head_->trimStart(n);\n      chainLength_ -= n;\n      break;\n    }\n  }\n  if (FOLLY_UNLIKELY(result == nullptr)) {\n    return createBuf(0);\n  }\n  return result;\n}\n\nvoid IOBufQueue::trimStart(size_t amount) {\n  auto trimmed = trimStartAtMost(amount);\n  if (trimmed != amount) {\n    throw std::underflow_error(\n        \"Attempt to trim more bytes than are present in IOBufQueue\");\n  }\n}\n\nsize_t IOBufQueue::trimStartAtMost(size_t amount) {\n  auto guard = updateGuard();\n  auto original = amount;\n  while (amount > 0) {\n    if (!head_) {\n      break;\n    }\n    if (head_->length() > amount) {\n      head_->trimStart(amount);\n      chainLength_ -= amount;\n      amount = 0;\n      break;\n    }\n    amount -= head_->length();\n    chainLength_ -= head_->length();\n    head_ = head_->pop();\n  }\n  return original - amount;\n}\n\nvoid IOBufQueue::trimEnd(size_t amount) {\n  auto trimmed = trimEndAtMost(amount);\n  if (trimmed != amount) {\n    throw std::underflow_error(\n        \"Attempt to trim more bytes than are present in IOBufQueue\");\n  }\n}\n\nsize_t IOBufQueue::trimEndAtMost(size_t amount) {\n  auto guard = updateGuard();\n  auto original = amount;\n  while (amount > 0) {\n    if (!head_) {\n      break;\n    }\n    if (head_->prev()->length() > amount) {\n      head_->prev()->trimEnd(amount);\n      chainLength_ -= amount;\n      amount = 0;\n      break;\n    }\n    amount -= head_->prev()->length();\n    chainLength_ -= head_->prev()->length();\n\n    if (head_->isChained()) {\n      head_->prev()->unlink();\n    } else {\n      head_.reset();\n    }\n  }\n  return original - amount;\n}\n\nstd::unique_ptr<folly::IOBuf> IOBufQueue::pop_front() {\n  auto guard = updateGuard();\n  if (!head_) {\n    return nullptr;\n  }\n  chainLength_ -= head_->length();\n  std::unique_ptr<folly::IOBuf> retBuf = std::move(head_);\n  head_ = retBuf->pop();\n  return retBuf;\n}\n\nvoid IOBufQueue::clearAndTryReuseLargestBuffer() {\n  auto guard = updateGuard();\n  std::unique_ptr<folly::IOBuf> best;\n  while (head_) {\n    auto buf = std::exchange(head_, head_->pop());\n    if (!buf->isSharedOne() &&\n        (best == nullptr || buf->capacity() > best->capacity())) {\n      best = std::move(buf);\n    }\n  }\n  if (best != nullptr) {\n    best->clear();\n    head_ = std::move(best);\n  }\n  chainLength_ = 0;\n}\n\nvoid IOBufQueue::appendToString(std::string& out) const {\n  if (!head_) {\n    return;\n  }\n  auto len = options_.cacheChainLength\n      ? chainLength_ + (cachePtr_->cachedRange.first - tailStart_)\n      : head_->computeChainDataLength() +\n          (cachePtr_->cachedRange.first - tailStart_);\n  out.reserve(out.size() + len);\n\n  for (auto range : *head_) {\n    out.append(reinterpret_cast<const char*>(range.data()), range.size());\n  }\n\n  if (tailStart_ != cachePtr_->cachedRange.first) {\n    out.append(\n        reinterpret_cast<const char*>(tailStart_),\n        cachePtr_->cachedRange.first - tailStart_);\n  }\n}\n\nvoid IOBufQueue::gather(std::size_t maxLength) {\n  auto guard = updateGuard();\n  if (head_ != nullptr) {\n    head_->gather(maxLength);\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/IOBufQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdexcept>\n#include <string>\n\n#include <folly/ScopeGuard.h>\n#include <folly/io/IOBuf.h>\n\nnamespace folly {\n\nnamespace io {\nenum class CursorAccess;\ntemplate <CursorAccess>\nclass RWCursor;\n} // namespace io\n\n/**\n * An IOBufQueue encapsulates a chain of IOBufs and provides\n * convenience functions to append data to the back of the chain\n * and remove data from the front.\n *\n * You may also prepend data into the headroom of the first buffer in the\n * chain, if any.\n */\nclass IOBufQueue {\n private:\n  template <io::CursorAccess>\n  friend class io::RWCursor;\n\n  /**\n   * This guard should be taken by any method that intends to do any changes\n   * to in data_ (e.g. appending to it).\n   *\n   * It flushes the writable tail cache and refills it on destruction.\n   *\n   * If while the guard is held new IOBufs are appended to the chain,\n   * allowTailReuse allows forking the old tail IOBuf so we can reuse its\n   * writable tail.\n   */\n  auto updateGuard(bool allowTailReuse = false) {\n    flushCache();\n    folly::IOBuf* oldTail =\n        (allowTailReuse && head_ != nullptr) ? head_->prev() : nullptr;\n    return folly::makeGuard([this, oldTail] {\n      if (oldTail != nullptr) {\n        maybeReuseTail(*oldTail);\n      }\n      updateWritableTailCache();\n    });\n  }\n\n  struct WritableRangeCacheData {\n    std::pair<uint8_t*, uint8_t*> cachedRange{};\n    bool attached{};\n\n    WritableRangeCacheData() = default;\n\n    WritableRangeCacheData(WritableRangeCacheData&& other) noexcept\n        : cachedRange(other.cachedRange), attached(other.attached) {\n      other.cachedRange = std::pair<uint8_t*, uint8_t*>{};\n      other.attached = {};\n    }\n    WritableRangeCacheData& operator=(WritableRangeCacheData&& other) noexcept {\n      cachedRange = std::exchange(other.cachedRange, {});\n      attached = std::exchange(other.attached, {});\n      return *this;\n    }\n\n    WritableRangeCacheData(const WritableRangeCacheData&) = delete;\n    WritableRangeCacheData& operator=(const WritableRangeCacheData&) = delete;\n  };\n\n public:\n  struct Options {\n    Options() : cacheChainLength(false) {}\n    bool cacheChainLength;\n  };\n\n  /**\n   * Get Options with cacheChainLength=true.\n   * @methodset Configuration\n   *\n   * Commonly used Options, currently the only possible value other than\n   * the default.\n   */\n  static Options cacheChainLength() {\n    Options options;\n    options.cacheChainLength = true;\n    return options;\n  }\n\n  /**\n   * WritableRangeCache represents a cache of current writable tail and provides\n   * cheap and simple interface to append to it that avoids paying the cost of\n   * preallocate/postallocate pair (i.e. indirections and checks).\n   *\n   * The cache is flushed on destruction/copy/move and on non-const accesses to\n   * the underlying IOBufQueue.\n   *\n   * Note: there can be only one active cache for a given IOBufQueue, i.e. when\n   *       you fill a cache object it automatically invalidates other\n   *       cache (if any).\n   */\n  class WritableRangeCache {\n   public:\n    explicit WritableRangeCache(folly::IOBufQueue* q = nullptr) : queue_(q) {\n      if (queue_) {\n        fillCache();\n      }\n    }\n\n    /**\n     * Move constructor/assignment can move the cached range, but must update\n     * the reference in IOBufQueue.\n     */\n    WritableRangeCache(WritableRangeCache&& other) noexcept\n        : data_(std::move(other.data_)), queue_(other.queue_) {\n      if (data_.attached) {\n        queue_->updateCacheRef(data_);\n      }\n    }\n    WritableRangeCache& operator=(WritableRangeCache&& other) {\n      if (data_.attached) {\n        queue_->clearWritableRangeCache();\n      }\n\n      data_ = std::move(other.data_);\n      queue_ = other.queue_;\n\n      if (data_.attached) {\n        queue_->updateCacheRef(data_);\n      }\n\n      return *this;\n    }\n\n    /**\n     * Copy constructor/assignment cannot copy the cached range.\n     */\n    WritableRangeCache(const WritableRangeCache& other)\n        : queue_(other.queue_) {}\n    WritableRangeCache& operator=(const WritableRangeCache& other) {\n      if (data_.attached) {\n        queue_->clearWritableRangeCache();\n      }\n\n      queue_ = other.queue_;\n\n      return *this;\n    }\n\n    ~WritableRangeCache() {\n      if (data_.attached) {\n        queue_->clearWritableRangeCache();\n      }\n    }\n\n    /**\n     * Reset the underlying IOBufQueue, will flush current cache if present.\n     */\n    void reset(IOBufQueue* q) {\n      if (data_.attached) {\n        queue_->clearWritableRangeCache();\n      }\n\n      queue_ = q;\n\n      if (queue_) {\n        fillCache();\n      }\n    }\n\n    /**\n     * Get a pointer to the underlying IOBufQueue object.\n     */\n    IOBufQueue* queue() { return queue_; }\n\n    /**\n     * Return a pointer to the start of cached writable tail.\n     *\n     * Note: doesn't populate cache.\n     */\n    uint8_t* writableData() {\n      dcheckIntegrity();\n      return data_.cachedRange.first;\n    }\n\n    /**\n     * Return a length of cached writable tail.\n     *\n     * Note: doesn't populate cache.\n     */\n    size_t length() const {\n      dcheckIntegrity();\n      return data_.cachedRange.second - data_.cachedRange.first;\n    }\n\n    /**\n     * Mark n bytes as occupied (e.g. postallocate).\n     */\n    void append(size_t n) noexcept {\n      dcheckIntegrity();\n      // This can happen only if somebody is misusing the interface.\n      // E.g. calling append after touching IOBufQueue or without checking\n      // the length().\n      if (FOLLY_LIKELY(data_.cachedRange.first != nullptr)) {\n        DCHECK_LE(n, length());\n        data_.cachedRange.first += n;\n      } else {\n        appendSlow(n);\n      }\n    }\n\n    /**\n     * Same as append(n), but avoids checking if there is a cache.\n     * The caller must guarantee that the cache is set (e.g. the caller just\n     * called fillCache or checked that it's not empty).\n     */\n    void appendUnsafe(size_t n) { data_.cachedRange.first += n; }\n\n    /**\n     * Fill the cache of writable tail from the underlying IOBufQueue.\n     */\n    void fillCache() { queue_->fillWritableRangeCache(data_); }\n\n   private:\n    WritableRangeCacheData data_;\n    IOBufQueue* queue_;\n\n    FOLLY_NOINLINE void appendSlow(size_t n) { queue_->postallocate(n); }\n\n    void dcheckIntegrity() const {\n      // Tail start should always be less than tail end.\n      DCHECK_LE(\n          (void*)data_.cachedRange.first, (void*)data_.cachedRange.second);\n      DCHECK(\n          data_.cachedRange.first != nullptr ||\n          data_.cachedRange.second == nullptr);\n\n      // Cached range should be always empty if the cache is not attached.\n      DCHECK(\n          data_.attached ||\n          (data_.cachedRange.first == nullptr &&\n           data_.cachedRange.second == nullptr));\n\n      // We cannot be in attached state if the queue_ is not set.\n      DCHECK(queue_ != nullptr || !data_.attached);\n\n      // If we're attached and the cache is not empty, then it should coincide\n      // with the tail buffer.\n      DCHECK(\n          !data_.attached || data_.cachedRange.first == nullptr ||\n          (queue_->head_ != nullptr &&\n           data_.cachedRange.first >= queue_->head_->prev()->writableTail() &&\n           data_.cachedRange.second ==\n               queue_->head_->prev()->writableTail() +\n                   queue_->head_->prev()->tailroom()));\n    }\n  };\n\n  explicit IOBufQueue(const Options& options = Options());\n  ~IOBufQueue();\n\n  /**\n   * @brief Get writeable headroom\n   * @methodset Access\n   *\n   * @return A pair of buffer-address and writeable-length\n   */\n  std::pair<void*, std::size_t> headroom();\n\n  /**\n   * @brief Indicate that n bytes from the headroom have been used.\n   * @methodset Shifting\n   *\n   * @note Prepending happens at the end of the available headroom. If this\n   * IOBufQueue's headroom() returns `{buf, len}`, then prepend(n) will\n   * cause a subsequent call to headroom() to return `{buf, len-n}`.\n   */\n  void markPrepended(std::size_t n);\n\n  /**\n   * @brief Prepend an existing range\n   * @methodset Modifiers\n   *\n   * Throws std::overflow_error if not enough room.\n   */\n  void prepend(const void* buf, std::size_t n);\n\n  /**\n   * @overloadbrief Append data.\n   * @methodset Modifiers\n   *\n   * Add a buffer or buffer chain to the end of this queue. The\n   * queue takes ownership of buf.\n   *\n   * If pack is true, we try to reduce wastage at the end of this queue\n   * by copying some data from the first buffers in the buf chain (and\n   * releasing the buffers), if possible.  If pack is false, we leave\n   * the chain topology unchanged.\n   *\n   * If allowTailReuse is true, the current tail is split and reappended at the\n   * end of the chain when possible and beneficial. This can reduce memory\n   * overhead when append() is interleaved with other writes: without this\n   * option, appending a new buffer strands the previous tail, even if there is\n   * room left. It is preferable to enable this only if the whole chain is\n   * likely to share the lifetime, since the split tail buffers hold the entire\n   * allocation of the buffer they were split from.\n   */\n  void append(\n      std::unique_ptr<folly::IOBuf>&& buf,\n      bool pack = false,\n      bool allowTailReuse = false);\n  void append(\n      const folly::IOBuf& buf, bool pack = false, bool allowTailReuse = false);\n  void append(\n      folly::IOBuf&& buf, bool pack = false, bool allowTailReuse = false);\n\n  /**\n   * Add a queue to the end of this queue. `this` takes ownership of\n   * all buffers from the other queue.\n   */\n  void append(\n      IOBufQueue& other, bool pack = false, bool allowTailReuse = false);\n  void append(\n      IOBufQueue&& other, bool pack = false, bool allowTailReuse = false) {\n    append(other, pack, allowTailReuse);\n  }\n\n  /**\n   * Copy len bytes, starting at buf, to the end of this queue.\n   * The caller retains ownership of the source data.\n   */\n  void append(const void* buf, size_t len);\n\n  /**\n   * Copy a string to the end of this queue.\n   * The caller retains ownership of the source data.\n   */\n  void append(StringPiece sp) { append(sp.data(), sp.size()); }\n\n  /**\n   * @brief Append a buffer by wrapping.\n   * @methodset Modifiers\n   *\n   * Append a chain of IOBuf objects that point to consecutive regions\n   * within buf.\n   *\n   * Just like IOBuf::wrapBuffer, this should only be used when the caller\n   * knows ahead of time and can ensure that all IOBuf objects that will point\n   * to this buffer will be destroyed before the buffer itself is destroyed;\n   * all other caveats from wrapBuffer also apply.\n   *\n   * Every buffer except for the last will wrap exactly blockSize bytes.\n   * Importantly, this method may be used to wrap buffers larger than 4GB.\n   */\n  void wrapBuffer(\n      const void* buf,\n      size_t len,\n      std::size_t blockSize = (1U << 31)); // default block size: 2GB\n\n  /**\n   * @brief Obtain a writable block of contiguous bytes at the end of this\n   * queue, allocating more space if necessary.\n   * @methodset Allocation\n   *\n   * The amount of space reserved will be at least min.  If min contiguous space\n   * is not available at the end of the queue, and IOBuf with size\n   * newAllocationSize is appended to the chain and returned.  The actual\n   * available space may be larger than newAllocationSize, but will be truncated\n   * to max, if specified.\n   *\n   * If the caller subsequently writes anything into the returned space,\n   * it must call the postallocate() method.\n   *\n   * @return The starting address of the block and the length in bytes.\n   *\n   * @note The point of the preallocate()/postallocate() mechanism is\n   *       to support I/O APIs such as AsyncSocket::ReadCallback that\n   *       request a buffer from the application and then, in a later\n   *       callback, tell the application how much of the buffer they\n   *       have filled with data.\n   */\n  std::pair<void*, std::size_t> preallocate(\n      std::size_t min,\n      std::size_t newAllocationSize,\n      std::size_t max = std::numeric_limits<std::size_t>::max()) {\n    dcheckCacheIntegrity();\n\n    if (FOLLY_LIKELY(writableTail() != nullptr && tailroom() >= min)) {\n      return std::make_pair(\n          writableTail(), std::min<std::size_t>(max, tailroom()));\n    }\n\n    return preallocateSlow(min, newAllocationSize, max);\n  }\n\n  /**\n   * @brief Tell the queue that the caller has written data into the first n\n   * bytes provided by the previous preallocate() call.\n   * @methodset Allocation\n   *\n   * @note n should be less than or equal to the size returned by\n   *       preallocate().  If n is zero, the caller may skip the call\n   *       to postallocate().  If n is nonzero, the caller must not\n   *       invoke any other non-const methods on this IOBufQueue between\n   *       the call to preallocate and the call to postallocate().\n   */\n  void postallocate(std::size_t n) noexcept {\n    dcheckCacheIntegrity();\n    DCHECK_LE(\n        (void*)(cachePtr_->cachedRange.first + n),\n        (void*)cachePtr_->cachedRange.second);\n    cachePtr_->cachedRange.first += n;\n  }\n\n  /**\n   * @brief Obtain a writable block of n contiguous bytes, allocating more space\n   * if necessary, and mark it as used.\n   * @methodset Allocation\n   *\n   * The space is obtained at the end of the queue. The caller can fill it\n   * later.\n   */\n  void* allocate(std::size_t n) {\n    void* p = preallocate(n, n).first;\n    postallocate(n);\n    return p;\n  }\n\n  /**\n   * @brief Get a pointer to the writable tail section.\n   * @methodset Access\n   */\n  void* writableTail() {\n    dcheckCacheIntegrity();\n    return cachePtr_->cachedRange.first;\n  }\n\n  /**\n   * @brief Get the amount of free space at the end of the buffer.\n   * @methodset Access\n   */\n  size_t tailroom() const {\n    dcheckCacheIntegrity();\n    return cachePtr_->cachedRange.second - cachePtr_->cachedRange.first;\n  }\n\n  /**\n   * @brief Split off the first n bytes of the queue into a separate IOBuf\n   * chain.\n   * @methodset Modifiers\n   *\n   * Transfer ownership of the new chain to the caller.  The IOBufQueue\n   * retains ownership of everything after the split point.\n   *\n   * @warning If the split point lies in the middle of some IOBuf within\n   *          the chain, this function may, as an implementation detail,\n   *          clone that IOBuf.\n   *\n   * @throws std::underflow_error if n exceeds the number of bytes\n   *         in the queue.\n   */\n  std::unique_ptr<folly::IOBuf> split(size_t n) { return split(n, true); }\n\n  /**\n   * @brief Split at most n bytes.\n   * @methodset Modifiers\n   *\n   * Similar to split, but will return the entire queue instead of throwing\n   * if n exceeds the number of bytes in the queue.\n   */\n  std::unique_ptr<folly::IOBuf> splitAtMost(size_t n) {\n    return split(n, false);\n  }\n\n  /**\n   * @brief Remove bytes from the front.\n   * @methodset Shifting\n   *\n   * Similar to IOBuf::trimStart, but works on the whole queue.  Will\n   * pop off buffers that have been completely trimmed.\n   */\n  void trimStart(size_t amount);\n\n  /**\n   * @brief Maybe remove bytes from the front.\n   * @methodset Shifting\n   *\n   * Similar to trimStart, but will trim at most amount bytes and returns\n   * the number of bytes trimmed.\n   */\n  size_t trimStartAtMost(size_t amount);\n\n  /**\n   * @brief Remove bytes from the end.\n   * @methodset Shifting\n   *\n   * Similar to IOBuf::trimEnd, but works on the whole queue.  Will\n   * pop off buffers that have been completely trimmed.\n   */\n  void trimEnd(size_t amount);\n\n  /**\n   * @brief Maybe remove bytes from the end.\n   * @methodset Shifting\n   *\n   * Similar to trimEnd, but will trim at most amount bytes and returns\n   * the number of bytes trimmed.\n   */\n  size_t trimEndAtMost(size_t amount);\n\n  /**\n   * @brief Transfer ownership of the queue's entire IOBuf chain to the caller.\n   * @methodset Conversions\n   */\n  std::unique_ptr<folly::IOBuf> move() {\n    auto guard = updateGuard();\n    std::unique_ptr<folly::IOBuf> res = std::move(head_);\n    chainLength_ = 0;\n    return res;\n  }\n\n  /// @copydoc move()\n  folly::IOBuf moveAsValue() { return std::move(*move()); }\n\n  /**\n   * @brief Access the front IOBuf.\n   * @methodset Access\n   *\n   * Note: caller will see the current state of the chain, but may not see\n   *       future updates immediately, due to the presence of a tail cache.\n   * Note: the caller may potentially clone the chain, thus marking all buffers\n   *       as shared. We may still continue writing to the tail of the last\n   *       IOBuf without checking if it's shared, but this is fine, since the\n   *       cloned IOBufs won't reference that data.\n   */\n  const folly::IOBuf* front() const {\n    flushCache();\n    return head_.get();\n  }\n\n  /**\n   * @brief Removes and returns the first IOBuf in the chain.\n   * @methodset Conversions\n   *\n   * @return first IOBuf in the chain or nullptr if none.\n   */\n  std::unique_ptr<folly::IOBuf> pop_front();\n\n  /**\n   * @brief Get cached chain length.\n   * @methodset Capacity\n   *\n   * Total chain length, only valid if cacheLength was specified in the\n   * constructor.\n   */\n  size_t chainLength() const {\n    if (FOLLY_UNLIKELY(!options_.cacheChainLength)) {\n      throw std::invalid_argument(\"IOBufQueue: chain length not cached\");\n    }\n    dcheckCacheIntegrity();\n    return chainLength_ + (cachePtr_->cachedRange.first - tailStart_);\n  }\n\n  /**\n   * @brief Returns true iff the IOBuf chain length is 0.\n   * @methodset Capacity\n   */\n  bool empty() const {\n    dcheckCacheIntegrity();\n    return !head_ ||\n        (head_->empty() && cachePtr_->cachedRange.first == tailStart_);\n  }\n\n  /**\n   * @brief Get the options used to configure this IOBufQueue.\n   * @methodset Configuration\n   */\n  const Options& options() const { return options_; }\n\n  /**\n   * @brief Clear the queue, freeing all the buffers.\n   * @methodset Allocation\n   *\n   * Options are preserved.\n   */\n  void reset() { move(); }\n\n  /**\n   * @brief Clear the queue, but try to clear and keep the largest buffer for\n   * reuse when possible.\n   * @methodset Allocation\n   *\n   * Options are preserved.\n   */\n  void clearAndTryReuseLargestBuffer();\n\n  /**\n   * @brief Append the queue to a std::string.\n   * @methodset Utility\n   *\n   * Non-destructive.\n   */\n  void appendToString(std::string& out) const;\n\n  /**\n   * @brief Calls IOBuf::gather() on the head of the queue, if it exists.\n   * @methodset Capacity\n   */\n  void gather(std::size_t maxLength);\n\n  /** Movable */\n  IOBufQueue(IOBufQueue&&) noexcept;\n  IOBufQueue& operator=(IOBufQueue&&) noexcept;\n\n  /**\n   * @brief Set a custom IOBufFactory for buffer allocations.\n   * @methodset Configuration\n   *\n   * When set, all internal buffer allocations will use this factory\n   * instead of IOBuf::create. The caller is responsible for ensuring\n   * the factory outlives the IOBufQueue.\n   */\n  void setIOBufFactory(IOBufFactory* factory) { factory_ = factory; }\n\n  static constexpr size_t kMaxPackCopy = 4096;\n\n private:\n  std::unique_ptr<folly::IOBuf> createBuf(std::size_t capacity) const {\n    return factory_ ? (*factory_)(capacity) : folly::IOBuf::create(capacity);\n  }\n  std::unique_ptr<folly::IOBuf> split(size_t n, bool throwOnUnderflow);\n\n  static const size_t kChainLengthNotCached = (size_t)-1;\n  /** Not copyable */\n  IOBufQueue(const IOBufQueue&) = delete;\n  IOBufQueue& operator=(const IOBufQueue&) = delete;\n\n  Options options_;\n\n  /**\n   * Everything that has been appended but not yet discarded or moved out\n   * Note: anything that needs to operate on a tail should either call\n   * flushCache() or grab updateGuard() (it will flush the cache itself).\n   */\n  std::unique_ptr<folly::IOBuf> head_;\n\n  IOBufFactory* factory_{nullptr};\n\n  mutable uint8_t* tailStart_{nullptr};\n  WritableRangeCacheData* cachePtr_{nullptr};\n  WritableRangeCacheData localCache_;\n\n  // NOTE that chainLength_ is still updated even if !options_.cacheChainLength\n  // because doing it unchecked in postallocate() is faster (no (mis)predicted\n  // branch)\n  mutable size_t chainLength_{0};\n\n  void dcheckCacheIntegrity() const noexcept {\n    // Tail start should always be less than tail end.\n    DCHECK_LE((void*)tailStart_, (void*)cachePtr_->cachedRange.first);\n    DCHECK_LE(\n        (void*)cachePtr_->cachedRange.first,\n        (void*)cachePtr_->cachedRange.second);\n    DCHECK(\n        cachePtr_->cachedRange.first != nullptr ||\n        cachePtr_->cachedRange.second == nullptr);\n\n    // There is always an attached cache instance.\n    DCHECK(cachePtr_->attached);\n\n    // Either cache is empty or it coincides with the tail.\n    if (cachePtr_->cachedRange.first != nullptr) {\n      DCHECK(head_ != nullptr);\n      DCHECK(tailStart_ == head_->prev()->writableTail());\n      DCHECK(tailStart_ <= cachePtr_->cachedRange.first);\n      DCHECK(cachePtr_->cachedRange.first >= head_->prev()->writableTail());\n      DCHECK(\n          cachePtr_->cachedRange.second ==\n          head_->prev()->writableTail() + head_->prev()->tailroom());\n    }\n  }\n\n  /**\n   * Populate dest with writable tail range cache.\n   */\n  void fillWritableRangeCache(WritableRangeCacheData& dest) noexcept {\n    dcheckCacheIntegrity();\n    if (cachePtr_ != &dest) {\n      dest = std::move(*cachePtr_);\n      cachePtr_ = &dest;\n    }\n  }\n\n  /**\n   * Clear current writable tail cache and reset it to localCache_\n   */\n  void clearWritableRangeCache() noexcept {\n    flushCache();\n\n    if (cachePtr_ != &localCache_) {\n      localCache_ = std::move(*cachePtr_);\n      cachePtr_ = &localCache_;\n    }\n\n    DCHECK(cachePtr_ == &localCache_ && localCache_.attached);\n  }\n\n  /**\n   * Commit any pending changes to the tail of the queue.\n   */\n  void flushCache() const noexcept {\n    dcheckCacheIntegrity();\n\n    if (tailStart_ != cachePtr_->cachedRange.first) {\n      auto buf = head_->prev();\n      DCHECK_EQ(\n          (void*)(buf->writableTail() + buf->tailroom()),\n          (void*)cachePtr_->cachedRange.second);\n      auto len = cachePtr_->cachedRange.first - tailStart_;\n      buf->append(len);\n      chainLength_ += len;\n      tailStart_ += len;\n    }\n  }\n\n  // For WritableRangeCache move assignment/construction.\n  void updateCacheRef(WritableRangeCacheData& newRef) noexcept {\n    cachePtr_ = &newRef;\n  }\n\n  /**\n   * Update cached writable tail range. Called by updateGuard()\n   */\n  void updateWritableTailCache() noexcept {\n    if (FOLLY_LIKELY(head_ != nullptr)) {\n      IOBuf* buf = head_->prev();\n      if (FOLLY_LIKELY(!buf->isSharedOne())) {\n        tailStart_ = buf->writableTail();\n        cachePtr_->cachedRange = std::pair<uint8_t*, uint8_t*>(\n            tailStart_, tailStart_ + buf->tailroom());\n        return;\n      }\n    }\n    tailStart_ = nullptr;\n    cachePtr_->cachedRange = std::pair<uint8_t*, uint8_t*>();\n  }\n\n  std::pair<void*, std::size_t> preallocateSlow(\n      std::size_t min, std::size_t newAllocationSize, std::size_t max);\n\n  void maybeReuseTail(folly::IOBuf& oldTail);\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/RecordIO-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef FOLLY_IO_RECORDIO_H_\n#error This file may only be included from folly/io/RecordIO.h\n#endif\n\n#include <folly/detail/Iterators.h>\n#include <folly/hash/SpookyHashV2.h>\n\nnamespace folly {\n\nclass RecordIOReader::Iterator\n    : public detail::IteratorFacade<\n          RecordIOReader::Iterator,\n          const std::pair<ByteRange, off_t>,\n          std::forward_iterator_tag> {\n  friend class detail::\n      IteratorFacade<Iterator, value_type, std::forward_iterator_tag>;\n  friend class RecordIOReader;\n\n public:\n  Iterator() = default;\n\n private:\n  Iterator(ByteRange range, uint32_t fileId, off_t pos);\n\n  reference dereference() const { return recordAndPos_; }\n  bool equal(const Iterator& other) const { return range_ == other.range_; }\n  void increment() {\n    size_t skip = recordio_helpers::headerSize() + recordAndPos_.first.size();\n    recordAndPos_.second += off_t(skip);\n    range_.advance(skip);\n    advanceToValid();\n  }\n\n  void advanceToValid();\n  ByteRange range_;\n  uint32_t fileId_ = 0;\n  // stored as a pair so we can return by reference in dereference()\n  std::pair<ByteRange, off_t> recordAndPos_;\n};\n\ninline auto RecordIOReader::cbegin() const -> Iterator {\n  return seek(0);\n}\ninline auto RecordIOReader::begin() const -> Iterator {\n  return cbegin();\n}\ninline auto RecordIOReader::cend() const -> Iterator {\n  return seek(off_t(-1));\n}\ninline auto RecordIOReader::end() const -> Iterator {\n  return cend();\n}\ninline auto RecordIOReader::seek(off_t pos) const -> Iterator {\n  return Iterator(map_.range(), fileId_, pos);\n}\n\nnamespace recordio_helpers {\n\nnamespace recordio_detail {\n\nFOLLY_PACK_PUSH\nstruct Header {\n  // First 4 bytes of SHA1(\"zuck\"), big-endian\n  // Any values will do, except that the sequence must not have a\n  // repeated prefix (that is, if we see kMagic, we know that the next\n  // occurrence must start at least 4 bytes later)\n  static constexpr uint32_t kMagic = 0xeac313a1;\n  uint32_t magic;\n  uint8_t version; // backwards incompatible version, currently 0\n  uint8_t hashFunction; // 0 = SpookyHashV2\n  uint16_t flags; // reserved (must be 0)\n  uint32_t fileId; // unique file ID\n  uint32_t dataLength;\n  std::size_t dataHash;\n  uint32_t headerHash; // must be last\n} FOLLY_PACK_ATTR;\nFOLLY_PACK_POP\n\nstatic_assert(\n    offsetof(Header, headerHash) + sizeof(Header::headerHash) == sizeof(Header),\n    \"invalid header layout\");\n\n} // namespace recordio_detail\n\nconstexpr size_t headerSize() {\n  return sizeof(recordio_detail::Header);\n}\n\ninline RecordInfo findRecord(ByteRange range, uint32_t fileId) {\n  return findRecord(range, range, fileId);\n}\n\n} // namespace recordio_helpers\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/RecordIO.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/RecordIO.h>\n\n#include <folly/Exception.h>\n#include <folly/FileUtil.h>\n#include <folly/Memory.h>\n#include <folly/String.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\nusing namespace recordio_helpers;\n\nRecordIOWriter::RecordIOWriter(File file, uint32_t fileId)\n    : file_(std::move(file)),\n      fileId_(fileId),\n      writeLock_(file_, std::defer_lock),\n      filePos_(0) {\n  if (!writeLock_.try_lock()) {\n    throw std::runtime_error(\"RecordIOWriter: file locked by another process\");\n  }\n\n  struct stat st;\n  checkUnixError(fstat(file_.fd(), &st), \"fstat() failed\");\n\n  filePos_ = st.st_size;\n}\n\nvoid RecordIOWriter::write(std::unique_ptr<IOBuf> buf) {\n  size_t totalLength = prependHeader(buf, fileId_);\n  if (totalLength == 0) {\n    return; // nothing to do\n  }\n\n  DCHECK_EQ(buf->computeChainDataLength(), totalLength);\n\n  // We're going to write.  Reserve space for ourselves.\n  off_t pos = filePos_.fetch_add(off_t(totalLength));\n\n#if FOLLY_HAVE_PWRITEV\n  auto iov = buf->getIov();\n  ssize_t bytes = pwritevFull(file_.fd(), iov.data(), iov.size(), pos);\n#else\n  buf->unshare();\n  buf->coalesce();\n  ssize_t bytes = pwriteFull(file_.fd(), buf->data(), buf->length(), pos);\n#endif\n\n  checkUnixError(bytes, \"pwrite() failed\");\n  DCHECK_EQ(size_t(bytes), totalLength);\n}\n\nRecordIOReader::RecordIOReader(File file, uint32_t fileId)\n    : map_(std::move(file)), fileId_(fileId) {}\n\nRecordIOReader::Iterator::Iterator(ByteRange range, uint32_t fileId, off_t pos)\n    : range_(range), fileId_(fileId), recordAndPos_(ByteRange(), 0) {\n  if (size_t(pos) >= range_.size()) {\n    // Note that this branch can execute if pos is negative as well.\n    recordAndPos_.second = off_t(-1);\n    range_.clear();\n  } else {\n    recordAndPos_.second = pos;\n    range_.advance(size_t(pos));\n    advanceToValid();\n  }\n}\n\nvoid RecordIOReader::Iterator::advanceToValid() {\n  ByteRange record = findRecord(range_, fileId_).record;\n  if (record.empty()) {\n    recordAndPos_ = std::make_pair(ByteRange(), off_t(-1));\n    range_.clear(); // at end\n  } else {\n    auto skipped = size_t(record.begin() - range_.begin());\n    DCHECK_GE(skipped, headerSize());\n    skipped -= headerSize();\n    range_.advance(skipped);\n    recordAndPos_.first = record;\n    recordAndPos_.second += off_t(skipped);\n  }\n}\n\nnamespace recordio_helpers {\n\nusing recordio_detail::Header;\n\nnamespace {\n\nconstexpr uint32_t kHashSeed = 0xdeadbeef; // for mcurtiss\n\nuint32_t headerHash(const Header& header) {\n  return hash::SpookyHashV2::Hash32(\n      &header, offsetof(Header, headerHash), kHashSeed);\n}\n\nstd::pair<size_t, std::size_t> dataLengthAndHash(const IOBuf* buf) {\n  size_t len = 0;\n  hash::SpookyHashV2 hasher;\n  hasher.Init(kHashSeed, kHashSeed);\n  for (auto br : *buf) {\n    len += br.size();\n    hasher.Update(br.data(), br.size());\n  }\n  uint64_t hash1;\n  uint64_t hash2;\n  hasher.Final(&hash1, &hash2);\n  if (len + headerSize() >= std::numeric_limits<uint32_t>::max()) {\n    throw std::invalid_argument(\"Record length must fit in 32 bits\");\n  }\n  return std::make_pair(len, static_cast<std::size_t>(hash1));\n}\n\nstd::size_t dataHash(ByteRange range) {\n  return hash::SpookyHashV2::Hash64(range.data(), range.size(), kHashSeed);\n}\n\n} // namespace\n\nsize_t prependHeader(std::unique_ptr<IOBuf>& buf, uint32_t fileId) {\n  if (fileId == 0) {\n    throw std::invalid_argument(\"invalid file id\");\n  }\n  auto lengthAndHash = dataLengthAndHash(buf.get());\n  if (lengthAndHash.first == 0) {\n    return 0; // empty, nothing to do, no zero-length records\n  }\n\n  // Prepend to the first buffer in the chain if we have room, otherwise\n  // prepend a new buffer.\n  if (buf->headroom() >= headerSize()) {\n    buf->unshareOne();\n    buf->prepend(headerSize());\n  } else {\n    auto b = IOBuf::create(headerSize());\n    b->append(headerSize());\n    b->insertAfterThisOne(std::move(buf));\n    buf = std::move(b);\n  }\n  auto header = reinterpret_cast<Header*>(buf->writableData());\n  memset(header, 0, sizeof(Header));\n  header->magic = Header::kMagic;\n  header->fileId = fileId;\n  header->dataLength = uint32_t(lengthAndHash.first);\n  header->dataHash = lengthAndHash.second;\n  header->headerHash = headerHash(*header);\n\n  return lengthAndHash.first + headerSize();\n}\n\nbool validateRecordHeader(ByteRange range, uint32_t fileId) {\n  if (range.size() < headerSize()) { // records may not be empty\n    return false;\n  }\n  auto header = reinterpret_cast<const Header*>(range.begin());\n  if (header->magic != Header::kMagic || header->version != 0 ||\n      header->hashFunction != 0 || header->flags != 0 ||\n      (fileId != 0 && header->fileId != fileId)) {\n    return false;\n  }\n  if (headerHash(*header) != header->headerHash) {\n    return false;\n  }\n  return true;\n}\n\nRecordInfo validateRecordData(ByteRange range) {\n  if (range.size() <= headerSize()) { // records may not be empty\n    return {0, {}};\n  }\n  auto header = reinterpret_cast<const Header*>(range.begin());\n  range.advance(sizeof(Header));\n  if (header->dataLength > range.size()) {\n    return {0, {}};\n  }\n  range.reset(range.begin(), header->dataLength);\n  if (dataHash(range) != header->dataHash) {\n    return {0, {}};\n  }\n  return {header->fileId, range};\n}\n\nRecordInfo validateRecord(ByteRange range, uint32_t fileId) {\n  if (!validateRecordHeader(range, fileId)) {\n    return {0, {}};\n  }\n  return validateRecordData(range);\n}\n\nRecordInfo findRecord(\n    ByteRange searchRange, ByteRange wholeRange, uint32_t fileId) {\n  static const uint32_t magic = Header::kMagic;\n  static const ByteRange magicRange(\n      reinterpret_cast<const uint8_t*>(&magic), sizeof(magic));\n\n  DCHECK_GE(searchRange.begin(), wholeRange.begin());\n  DCHECK_LE(searchRange.end(), wholeRange.end());\n\n  const uint8_t* start = searchRange.begin();\n  const uint8_t* end =\n      std::min(searchRange.end(), wholeRange.end() - sizeof(Header));\n  // end-1: the last place where a Header could start\n  while (start < end) {\n    auto p = ByteRange(start, end + sizeof(magic)).find(magicRange);\n    if (p == ByteRange::npos) {\n      break;\n    }\n\n    start += p;\n    auto r = validateRecord(ByteRange(start, wholeRange.end()), fileId);\n    if (!r.record.empty()) {\n      return r;\n    }\n\n    // No repeated prefix in magic, so we can do better than start++\n    start += sizeof(magic);\n  }\n\n  return {0, {}};\n}\n\n} // namespace recordio_helpers\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/RecordIO.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * RecordIO: self-synchronizing stream of variable length records\n *\n * RecordIO gives you the ability to write a stream of variable length records\n * and read them later even in the face of data corruption -- randomly inserted\n * or deleted chunks of the file, or modified data.  When reading, you may lose\n * corrupted records, but the stream will resynchronize automatically.\n */\n\n#pragma once\n#define FOLLY_IO_RECORDIO_H_\n\n#include <atomic>\n#include <memory>\n#include <mutex>\n\n#include <folly/File.h>\n#include <folly/Range.h>\n#include <folly/io/IOBuf.h>\n#include <folly/system/MemoryMapping.h>\n\nnamespace folly {\n\n/**\n * Class to write a stream of RecordIO records to a file.\n *\n * RecordIOWriter is thread-safe\n */\nclass RecordIOWriter {\n public:\n  /**\n   * Create a RecordIOWriter around a file; will append to the end of\n   * file if it exists.\n   *\n   * Each file must have a non-zero file id, which is embedded in all\n   * record headers.  Readers will only return records with the requested\n   * file id (or, if the reader is created with fileId=0 in the constructor,\n   * the reader will return all records).  File ids are only used to allow\n   * resynchronization if you store RecordIO records (with headers) inside\n   * other RecordIO records (for example, if a record consists of a fragment\n   * from another RecordIO file).  If you're not planning to do that,\n   * the defaults are fine.\n   */\n  explicit RecordIOWriter(File file, uint32_t fileId = 1);\n\n  /**\n   * Write a record.  We will use at most headerSize() bytes of headroom,\n   * you might want to arrange that before copying your data into it.\n   */\n  void write(std::unique_ptr<IOBuf> buf);\n\n  /**\n   * Return the position in the file where the next byte will be written.\n   * Conservative, as stuff can be written at any time from another thread.\n   */\n  off_t filePos() const { return filePos_; }\n\n private:\n  File file_;\n  uint32_t fileId_;\n  std::unique_lock<File> writeLock_;\n  std::atomic<off_t> filePos_;\n};\n\n/**\n * Class to read from a RecordIO file.  Will skip invalid records.\n */\nclass RecordIOReader {\n public:\n  class Iterator;\n\n  /**\n   * RecordIOReader is iterable, returning pairs of ByteRange (record content)\n   * and position in file where the record (including header) begins.\n   * Note that the position includes the header, that is, it can be passed back\n   * to seek().\n   */\n  using iterator = Iterator;\n  using const_iterator = Iterator;\n  using value_type = std::pair<ByteRange, off_t>;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n\n  /**\n   * A record reader with a fileId of 0 will return all records.\n   * A record reader with a non-zero fileId will only return records where\n   * the fileId matches.\n   */\n  explicit RecordIOReader(File file, uint32_t fileId = 0);\n\n  Iterator cbegin() const;\n  Iterator begin() const;\n  Iterator cend() const;\n  Iterator end() const;\n\n  /**\n   * Create an iterator to the first valid record after pos.\n   */\n  Iterator seek(off_t pos) const;\n\n private:\n  MemoryMapping map_;\n  uint32_t fileId_;\n};\n\nnamespace recordio_helpers {\n\n// We're exposing the guts of the RecordIO implementation for two reasons:\n// 1. It makes unit testing easier, and\n// 2. It allows you to build different RecordIO readers / writers that use\n// different storage systems underneath (not standard files)\n\n/**\n * Header size.\n */\nconstexpr size_t headerSize(); // defined in RecordIO-inl.h\n\n/**\n * Write a header in the buffer.  We will prepend the header to the front\n * of the chain.  Do not write the buffer if empty (we don't allow empty\n * records).  Returns the total length, including header (0 if empty)\n * (same as buf->computeChainDataLength(), but likely faster)\n *\n * The fileId should be unique per stream and allows you to have RecordIO\n * headers stored inside the data (for example, have an entire RecordIO\n * file stored as a record inside another RecordIO file).  The fileId may\n * not be 0.\n */\nsize_t prependHeader(std::unique_ptr<IOBuf>& buf, uint32_t fileId = 1);\n\n/**\n * Search for the first valid record that begins in searchRange (which must be\n * a subrange of wholeRange).  Returns the record data (not the header) if\n * found, ByteRange() otherwise.\n *\n * The fileId may be 0, in which case we'll return the first valid record for\n * *any* fileId, or non-zero, in which case we'll only look for records with\n * the requested fileId.\n */\nstruct RecordInfo {\n  uint32_t fileId;\n  ByteRange record;\n};\nRecordInfo findRecord(\n    ByteRange searchRange, ByteRange wholeRange, uint32_t fileId);\n\n/**\n * Search for the first valid record in range.\n */\nRecordInfo findRecord(ByteRange range, uint32_t fileId);\n\n/**\n * Check if the Record Header is valid at the beginning of range.\n * Useful to check the validity of the header before building the entire record\n * in IOBuf. If the record is from storage device (e.g. flash) then, it\n * is better to make sure that the header is valid before reading the data\n * from the storage device.\n * Returns true if valid, false otherwise.\n */\nbool validateRecordHeader(ByteRange range, uint32_t fileId);\n\n/**\n * Check if there Record Data is valid (to be used after validating the header\n * separately)\n * Returns the record data (not the header) if the record data is valid,\n * ByteRange() otherwise.\n */\nRecordInfo validateRecordData(ByteRange range);\n\n/**\n * Check if there is a valid record at the beginning of range. This validates\n * both record header and data and Returns the\n * record data (not the header) if the record is valid, ByteRange() otherwise.\n */\nRecordInfo validateRecord(ByteRange range, uint32_t fileId);\n\n} // namespace recordio_helpers\n\n} // namespace folly\n\n#include <folly/io/RecordIO-inl.h>\n"
  },
  {
    "path": "folly/io/ShutdownSocketSet.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/ShutdownSocketSet.h>\n\n#include <chrono>\n#include <thread>\n\n#include <glog/logging.h>\n\n#include <folly/FileUtil.h>\n#include <folly/net/NetOps.h>\n#include <folly/portability/Sockets.h>\n\nnamespace folly {\n\nusing handle_t = NetworkSocket::native_handle_type;\nstatic constexpr auto handle_tag = std::is_integral<handle_t>{};\nstatic constexpr auto invalid_pos = ~size_t(0);\n\ntemplate <typename Cap>\nstatic size_t cap_(std::false_type, Cap) {\n  return 0;\n}\ntemplate <typename Cap, typename H = handle_t>\nstatic size_t cap_(std::true_type, Cap cap) {\n  cap = cap == invalid_pos ? 0 : cap;\n  auto max = static_cast<Cap>(std::numeric_limits<H>::max());\n  return cap > max ? max : cap;\n}\nstatic size_t cap(size_t capacity) {\n  return cap_(handle_tag, capacity);\n}\n\ntemplate <typename Data>\nstatic size_t pos_(std::false_type, Data) {\n  return invalid_pos;\n}\ntemplate <typename Data>\nstatic size_t pos_(std::true_type, Data data) {\n  return to_unsigned(data);\n}\nstatic size_t pos(NetworkSocket fd) {\n  return fd.data == NetworkSocket::invalid_handle_value\n      ? invalid_pos\n      : pos_(handle_tag, fd.data);\n}\n\ntemplate <typename Pos>\nstatic NetworkSocket at_(std::false_type, Pos) {\n  return NetworkSocket();\n}\ntemplate <typename Pos>\nstatic NetworkSocket at_(std::true_type, Pos p) {\n  return NetworkSocket(static_cast<NetworkSocket::native_handle_type>(p));\n}\nstatic NetworkSocket at(size_t p) {\n  return p == invalid_pos ? NetworkSocket() : at_(handle_tag, p);\n}\n\nShutdownSocketSet::ShutdownSocketSet(size_t capacity)\n    : capacity_(cap(capacity)),\n      data_(\n          static_cast<relaxed_atomic<uint8_t>*>(folly::checkedCalloc(\n              capacity_, sizeof(relaxed_atomic<uint8_t>)))),\n      nullFile_(\"/dev/null\", O_RDWR) {}\n\nvoid ShutdownSocketSet::add(NetworkSocket fd) {\n  // Silently ignore any fds >= capacity_, very unlikely\n  DCHECK_NE(fd, NetworkSocket());\n  auto p = pos(fd);\n  if (p >= capacity_) {\n    return;\n  }\n\n  auto& sref = data_[p];\n  uint8_t prevState = FREE;\n  CHECK(sref.compare_exchange_strong(prevState, IN_USE))\n      << \"Invalid prev state for fd \" << fd << \": \" << int(prevState);\n}\n\nvoid ShutdownSocketSet::remove(NetworkSocket fd) {\n  DCHECK_NE(fd, NetworkSocket());\n  auto p = pos(fd);\n  if (p >= capacity_) {\n    return;\n  }\n\n  auto& sref = data_[p];\n  uint8_t prevState = 0;\n\n  prevState = sref.load();\n  do {\n    switch (prevState) {\n      case IN_SHUTDOWN:\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n        prevState = sref.load();\n        continue;\n      case FREE:\n        LOG(FATAL) << \"Invalid prev state for fd \" << fd << \": \"\n                   << int(prevState);\n    }\n  } while (!sref.compare_exchange_weak(prevState, FREE));\n}\n\nint ShutdownSocketSet::close(NetworkSocket fd) {\n  DCHECK_NE(fd, NetworkSocket());\n  auto p = pos(fd);\n  if (p >= capacity_) {\n    return folly::closeNoInt(fd);\n  }\n\n  auto& sref = data_[p];\n  uint8_t prevState = sref.load();\n  uint8_t newState = 0;\n\n  do {\n    switch (prevState) {\n      case IN_USE:\n      case SHUT_DOWN:\n        newState = FREE;\n        break;\n      case IN_SHUTDOWN:\n        newState = MUST_CLOSE;\n        break;\n      default:\n        LOG(FATAL) << \"Invalid prev state for fd \" << fd << \": \"\n                   << int(prevState);\n    }\n  } while (!sref.compare_exchange_strong(prevState, newState));\n\n  return newState == FREE ? folly::closeNoInt(fd) : 0;\n}\n\nvoid ShutdownSocketSet::shutdown(NetworkSocket fd, bool abortive) {\n  DCHECK_NE(fd, NetworkSocket());\n  auto p = pos(fd);\n  if (p >= capacity_) {\n    doShutdown(fd, abortive);\n    return;\n  }\n\n  auto& sref = data_[p];\n  uint8_t prevState = IN_USE;\n  if (!sref.compare_exchange_strong(prevState, IN_SHUTDOWN)) {\n    return;\n  }\n\n  doShutdown(fd, abortive);\n\n  prevState = IN_SHUTDOWN;\n  if (sref.compare_exchange_strong(prevState, SHUT_DOWN)) {\n    return;\n  }\n\n  CHECK_EQ(prevState, MUST_CLOSE)\n      << \"Invalid prev state for fd \" << fd << \": \" << int(prevState);\n\n  folly::closeNoInt(fd); // ignore errors, nothing to do\n\n  CHECK(sref.compare_exchange_strong(prevState, FREE))\n      << \"Invalid prev state for fd \" << fd << \": \" << int(prevState);\n}\n\nvoid ShutdownSocketSet::shutdownAll(bool abortive) {\n  for (size_t p = 0; p < capacity_; ++p) {\n    auto& sref = data_[p];\n    if (sref.load() == IN_USE) {\n      shutdown(at(p), abortive);\n    }\n  }\n}\n\nvoid ShutdownSocketSet::doShutdown(NetworkSocket fd, bool abortive) {\n  // shutdown() the socket first, to awaken any threads blocked on the fd\n  // (subsequent IO will fail because it's been shutdown); close()ing the\n  // socket does not wake up blockers, see\n  // http://stackoverflow.com/a/3624545/1736339\n  folly::shutdownNoInt(fd, SHUT_RDWR);\n\n  // If abortive shutdown is desired, we'll set the SO_LINGER option on\n  // the socket with a timeout of 0; this will cause RST to be sent on\n  // close.\n  if (abortive) {\n    struct linger l = {1, 0};\n    if (netops::setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) {\n      // Probably not a socket, ignore.\n      return;\n    }\n  }\n\n  // Note that this can be ignored without breaking anything due to the\n  // fact the file descriptor will eventually be closed, so on Windows\n  // all of the socket resources will just stick around longer than they\n  // do on posix-like systems.\n#ifndef _WIN32\n  // We can't close() the socket, as that would be dangerous; a new file\n  // could be opened and get the same file descriptor, and then code assuming\n  // the old fd would do IO in the wrong place. We'll (atomically) dup2\n  // /dev/null onto the fd instead.\n  folly::dup2NoInt(nullFile_.fd(), fd.toFd());\n#endif\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/ShutdownSocketSet.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdlib>\n#include <memory>\n\n#include <folly/File.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n\nnamespace folly {\n\n/**\n * Set of sockets that allows immediate, take-no-prisoners abort.\n */\nclass ShutdownSocketSet {\n public:\n  /**\n   * Create a socket set that can handle file descriptors within the capacity.\n   * The default value (256Ki) is high enough for just about all\n   * applications, even if you increased the number of file descriptors\n   * on your system.\n   */\n  explicit ShutdownSocketSet(size_t capacity = 1 << 18);\n\n  ShutdownSocketSet(const ShutdownSocketSet&) = delete;\n  ShutdownSocketSet& operator=(const ShutdownSocketSet&) = delete;\n\n  /**\n   * Add an already open socket to the list of sockets managed by\n   * ShutdownSocketSet. You MUST close the socket by calling\n   * ShutdownSocketSet::close (which will, as a side effect, also handle EINTR\n   * properly) and not by calling close() on the file descriptor.\n   */\n  void add(NetworkSocket fd);\n\n  /**\n   * Remove a socket from the list of sockets managed by ShutdownSocketSet.\n   * Note that remove() might block! (which we lamely implement by\n   * sleep()-ing) in the extremely rare case that the fd is currently\n   * being shutdown().\n   */\n  void remove(NetworkSocket fd);\n\n  /**\n   * Close a socket managed by ShutdownSocketSet. Returns the same return code\n   * as ::close() (and sets errno accordingly).\n   */\n  int close(NetworkSocket fd);\n\n  /**\n   * Shut down a socket. If abortive is true, we perform an abortive\n   * shutdown (send RST rather than FIN). Note that we might still end up\n   * sending FIN due to the rather interesting implementation.\n   *\n   * This is async-signal-safe and ignores errors.  Obviously, subsequent\n   * read() and write() operations to the socket will fail. During normal\n   * operation, just call ::shutdown() on the socket.\n   */\n  void shutdown(NetworkSocket fd, bool abortive = false);\n\n  /**\n   * Immediate shutdown of all connections. This is a hard-hitting hammer;\n   * all reads and writes will return errors and no new connections will\n   * be accepted.\n   *\n   * To be used only in dire situations. We're using it from the failure\n   * signal handler to close all connections quickly, even though the server\n   * might take multiple seconds to finish crashing.\n   *\n   * The optional bool parameter indicates whether to set the active\n   * connections in to not linger.  The effect of that includes RST packets\n   * being immediately sent to clients which will result\n   * in errors (and not normal EOF) on the client side.  This also causes\n   * the local (ip, tcp port number) tuple to be reusable immediately, instead\n   * of having to wait the standard amount of time.  For full details see\n   * the `shutdown` method of `ShutdownSocketSet` (incl. notes about the\n   * `abortive` parameter).\n   *\n   * This is async-signal-safe and ignores errors.\n   */\n  void shutdownAll(bool abortive = false);\n\n private:\n  void doShutdown(NetworkSocket fd, bool abortive);\n\n  // State transitions:\n  // add():\n  //   FREE -> IN_USE\n  //\n  // close():\n  //   IN_USE -> (::close()) -> FREE\n  //   SHUT_DOWN -> (::close()) -> FREE\n  //   IN_SHUTDOWN -> MUST_CLOSE\n  //   (If the socket is currently being shut down, we must defer the\n  //    ::close() until the shutdown completes)\n  //\n  // shutdown():\n  //   IN_USE -> IN_SHUTDOWN\n  //   (::shutdown())\n  //   IN_SHUTDOWN -> SHUT_DOWN\n  //   MUST_CLOSE -> (::close()) -> FREE\n  //\n  // State atomic operation memory orders:\n  // All atomic operations on per-socket states use std::memory_order_relaxed\n  // because there is no associated per-socket data guarded by the state and\n  // the states for different sockets are unrelated. If there were associated\n  // per-socket data, acquire and release orders would be desired; and if the\n  // states for different sockets were related, it could be that sequential\n  // consistent orders would be desired.\n  enum State : uint8_t {\n    FREE = 0,\n    IN_USE,\n    IN_SHUTDOWN,\n    SHUT_DOWN,\n    MUST_CLOSE,\n  };\n\n  struct Free {\n    template <class T>\n    void operator()(T* ptr) const {\n      ::free(ptr);\n    }\n  };\n\n  size_t const capacity_;\n  std::unique_ptr<relaxed_atomic<uint8_t>[], Free> data_;\n  folly::File nullFile_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/SocketOptionMap.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/SocketOptionMap.h>\n\n#include <errno.h>\n\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly {\n\nconst SocketOptionMap emptySocketOptionMap;\n\nint SocketOptionKey::apply(\n    NetworkSocket fd, const SocketOptionValue& val) const {\n  if (val.hasInt()) {\n    int32_t intVal = val.asInt();\n    return netops::setsockopt(fd, level, optname, &intVal, sizeof(intVal));\n  } else {\n    std::string strVal = val.asString();\n    return netops::setsockopt(fd, level, optname, strVal.data(), strVal.size());\n  }\n}\n\nint applySocketOptions(\n    NetworkSocket fd,\n    const SocketOptionMap& options,\n    SocketOptionKey::ApplyPos pos) {\n  for (const auto& opt : options) {\n    if (opt.first.applyPos_ == pos) {\n      auto rv = opt.first.apply(fd, opt.second);\n      if (rv != 0) {\n        return errno;\n      }\n    }\n  }\n  return 0;\n}\n\nSocketOptionMap validateSocketOptions(\n    const SocketOptionMap& options,\n    sa_family_t family,\n    SocketOptionKey::ApplyPos pos) {\n  SocketOptionMap validOptions;\n  for (const auto& option : options) {\n    if (pos != option.first.applyPos_) {\n      continue;\n    }\n    if ((family == AF_INET && option.first.level == IPPROTO_IP) ||\n        (family == AF_INET6 && option.first.level == IPPROTO_IPV6) ||\n        option.first.level == IPPROTO_UDP || option.first.level == SOL_SOCKET ||\n        option.first.level == SOL_UDP) {\n      validOptions.insert(option);\n    }\n  }\n  return validOptions;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/SocketOptionMap.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n\n#include <folly/io/SocketOptionValue.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/portability/Sockets.h>\n\nnamespace folly {\n\n/**\n * Uniquely identifies a handle to a socket option value. Each\n * combination of level and option name corresponds to one socket\n * option value.\n */\nclass SocketOptionKey {\n public:\n  enum class ApplyPos { POST_BIND = 0, PRE_BIND = 1 };\n\n  friend bool operator<(\n      const SocketOptionKey& lhs, const SocketOptionKey& rhs) {\n    if (lhs.level == rhs.level) {\n      return lhs.optname < rhs.optname;\n    }\n    return lhs.level < rhs.level;\n  }\n\n  friend bool operator==(\n      const SocketOptionKey& lhs, const SocketOptionKey& rhs) {\n    return lhs.level == rhs.level && lhs.optname == rhs.optname;\n  }\n\n  int apply(NetworkSocket fd, const SocketOptionValue& val) const;\n\n  int level;\n  int optname;\n  ApplyPos applyPos_{ApplyPos::POST_BIND};\n};\n\nusing SocketOptionMap = std::map<SocketOptionKey, SocketOptionValue>;\n\nextern const SocketOptionMap emptySocketOptionMap;\n\nusing SocketCmsgMap = std::map<SocketOptionKey, int>;\nusing SocketNontrivialCmsgMap = std::map<SocketOptionKey, std::string>;\n\nint applySocketOptions(\n    NetworkSocket fd,\n    const SocketOptionMap& options,\n    SocketOptionKey::ApplyPos pos);\n\nSocketOptionMap validateSocketOptions(\n    const SocketOptionMap& options,\n    sa_family_t family,\n    SocketOptionKey::ApplyPos pos);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/SocketOptionValue.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/SocketOptionValue.h>\n\n#include <ostream>\n\nnamespace folly {\n\nint SocketOptionValue::asInt() const {\n  return std::get<int>(val_);\n}\n\nconst std::string& SocketOptionValue::asString() const {\n  return std::get<std::string>(val_);\n}\n\nbool SocketOptionValue::hasInt() const {\n  return std::holds_alternative<int>(val_);\n}\n\nbool SocketOptionValue::hasString() const {\n  return std::holds_alternative<std::string>(val_);\n}\n\nstd::string SocketOptionValue::toString() const {\n  if (hasInt()) {\n    char sval[20];\n    int written = snprintf(sval, sizeof(sval), \"%d\", asInt());\n    if (written > 0 && written < static_cast<int>(sizeof(sval))) {\n      return std::string(sval, written);\n    } else {\n      return std::string();\n    }\n  } else {\n    return asString();\n  }\n}\n\nbool operator==(const SocketOptionValue& lhs, const SocketOptionValue& rhs) {\n  if (lhs.hasInt() && !rhs.hasInt()) {\n    return false;\n  } else if (lhs.hasInt() && rhs.hasInt()) {\n    return lhs.asInt() == rhs.asInt();\n  } else if (lhs.hasString() && rhs.hasString()) {\n    return lhs.asString() == rhs.asString();\n  } else {\n    return false;\n  }\n}\n\nbool operator==(const SocketOptionValue& lhs, int rhs) {\n  if (!lhs.hasInt()) {\n    return false;\n  }\n  return (lhs.asInt() == rhs);\n}\n\nbool operator==(const SocketOptionValue& lhs, const std::string& rhs) {\n  if (!lhs.hasString()) {\n    return false;\n  }\n  return (lhs.asString() == rhs);\n}\n\nbool operator!=(const SocketOptionValue& lhs, const SocketOptionValue& rhs) {\n  return !(lhs == rhs);\n}\n\nbool operator!=(const SocketOptionValue& lhs, int rhs) {\n  return !(lhs == rhs);\n}\n\nbool operator!=(const SocketOptionValue& lhs, const std::string& rhs) {\n  return !(lhs == rhs);\n}\n\nvoid toAppend(const SocketOptionValue& val, std::string* result) {\n  result->append(val.toString());\n}\n\nstd::ostream& operator<<(std::ostream& os, const SocketOptionValue& val) {\n  os << val.toString();\n  return os;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/SocketOptionValue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <variant>\n\nnamespace folly {\n\n/**\n * Variant container for socket option values: integer or string.\n * Implicit ctor/compares with int for backward compatibility.\n */\nclass SocketOptionValue {\n public:\n  /* implicit */ SocketOptionValue(int val) : val_(val) {}\n  /* implicit */ SocketOptionValue(const std::string& val) : val_(val) {}\n  SocketOptionValue() : val_(0) {}\n\n  // Return true if container holds int\n  bool hasInt() const;\n  // Returns int value, prior to calling must verify container holds integer via\n  // hasInt()\n  int asInt() const;\n\n  // Return true if container holds string\n  bool hasString() const;\n  // Returns string value, prior to calling must verify container holds string\n  // via hasString()\n  const std::string& asString() const;\n\n  // If holding string value, returns string value is.\n  // If integer, converts to string.\n  std::string toString() const;\n\n  friend bool operator==(\n      const SocketOptionValue& lhs, const SocketOptionValue& rhs);\n  friend bool operator==(const SocketOptionValue& lhs, int rhs);\n  friend bool operator==(const SocketOptionValue& lhs, const std::string& rhs);\n\n  friend bool operator!=(\n      const SocketOptionValue& lhs, const SocketOptionValue& rhs);\n  friend bool operator!=(const SocketOptionValue& lhs, int rhs);\n  friend bool operator!=(const SocketOptionValue& lhs, const std::string& rhs);\n\n private:\n  std::variant<int, std::string> val_;\n};\n\nvoid toAppend(const SocketOptionValue& val, std::string* result);\n\nstd::ostream& operator<<(std::ostream&, const SocketOptionValue&);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/TypedIOBuf.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <algorithm>\n#include <iterator>\n#include <type_traits>\n\n#include <folly/io/IOBuf.h>\n#include <folly/memory/Malloc.h>\n\nnamespace folly {\n\n/**\n * Wrapper class to handle a IOBuf as a typed buffer (to a standard layout\n * class).\n *\n * This class punts on alignment, and assumes that you know what you're doing.\n *\n * All methods are wrappers around the corresponding IOBuf methods.  The\n * TypedIOBuf object is stateless, so it's perfectly okay to access the\n * underlying IOBuf in between TypedIOBuf method calls.\n */\ntemplate <class T>\nclass TypedIOBuf {\n  static_assert(std::is_standard_layout<T>::value, \"must be standard layout\");\n\n public:\n  using value_type = T;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n  using size_type = uint32_t;\n  using iterator = value_type*;\n  using const_iterator = const value_type*;\n\n  explicit TypedIOBuf(IOBuf* buf) : buf_(buf) {}\n\n  IOBuf* ioBuf() { return buf_; }\n  const IOBuf* ioBuf() const { return buf_; }\n\n  bool empty() const { return buf_->empty(); }\n  const T* data() const { return cast(buf_->data()); }\n  T* writableData() { return cast(buf_->writableData()); }\n  const T* tail() const { return cast(buf_->tail()); }\n  T* writableTail() { return cast(buf_->writableTail()); }\n  uint32_t length() const { return sdiv(buf_->length()); }\n  uint32_t size() const { return length(); }\n\n  uint32_t headroom() const { return sdiv(buf_->headroom()); }\n  uint32_t tailroom() const { return sdiv(buf_->tailroom()); }\n  const T* buffer() const { return cast(buf_->buffer()); }\n  T* writableBuffer() { return cast(buf_->writableBuffer()); }\n  const T* bufferEnd() const { return cast(buf_->bufferEnd()); }\n  uint32_t capacity() const { return sdiv(buf_->capacity()); }\n  void advance(uint32_t n) { buf_->advance(smul(n)); }\n  void retreat(uint32_t n) { buf_->retreat(smul(n)); }\n  void prepend(uint32_t n) { buf_->prepend(smul(n)); }\n  void append(uint32_t n) { buf_->append(smul(n)); }\n  void trimStart(uint32_t n) { buf_->trimStart(smul(n)); }\n  void trimEnd(uint32_t n) { buf_->trimEnd(smul(n)); }\n  void clear() { buf_->clear(); }\n  void reserve(uint32_t minHeadroom, uint32_t minTailroom) {\n    buf_->reserve(smul(minHeadroom), smul(minTailroom));\n  }\n  void reserve(uint32_t minTailroom) { reserve(0, minTailroom); }\n\n  const T* cbegin() const { return data(); }\n  const T* cend() const { return tail(); }\n  const T* begin() const { return cbegin(); }\n  const T* end() const { return cend(); }\n  T* begin() { return writableData(); }\n  T* end() { return writableTail(); }\n\n  const T& front() const {\n    assert(!empty());\n    return *begin();\n  }\n  T& front() {\n    assert(!empty());\n    return *begin();\n  }\n  const T& back() const {\n    assert(!empty());\n    return end()[-1];\n  }\n  T& back() {\n    assert(!empty());\n    return end()[-1];\n  }\n\n  /**\n   * Simple wrapper to make it easier to treat this TypedIOBuf as an array of\n   * T.\n   */\n  const T& operator[](ssize_t idx) const {\n    assert(idx >= 0 && idx < length());\n    return data()[idx];\n  }\n\n  T& operator[](ssize_t idx) {\n    assert(idx >= 0 && idx < length());\n    return writableData()[idx];\n  }\n\n  /**\n   * Append one element.\n   */\n  void push(const T& data) { push(&data, &data + 1); }\n  void push_back(const T& data) { push(data); }\n\n  /**\n   * Append multiple elements in a sequence; will call distance().\n   */\n  template <class IT>\n  void push(IT begin, IT end) {\n    uint32_t n = std::distance(begin, end);\n    if (usingJEMalloc()) {\n      // Rely on xallocx() and avoid exponential growth to limit\n      // amount of memory wasted.\n      reserve(headroom(), n);\n    } else if (tailroom() < n) {\n      reserve(headroom(), std::max(n, 3 + size() / 2));\n    }\n    std::copy(begin, end, writableTail());\n    append(n);\n  }\n\n  // Movable\n  TypedIOBuf(TypedIOBuf&&) = default;\n  TypedIOBuf& operator=(TypedIOBuf&&) = default;\n\n private:\n  // Non-copyable\n  TypedIOBuf(const TypedIOBuf&) = delete;\n  TypedIOBuf& operator=(const TypedIOBuf&) = delete;\n\n  // cast to T*\n  static T* cast(uint8_t* p) { return reinterpret_cast<T*>(p); }\n  static const T* cast(const uint8_t* p) {\n    return reinterpret_cast<const T*>(p);\n  }\n  // divide by size\n  static uint32_t sdiv(uint32_t n) { return n / sizeof(T); }\n  // multiply by size\n  static uint32_t smul(uint32_t n) {\n    // In debug mode, check for overflow\n    assert((uint64_t(n) * sizeof(T)) < (uint64_t(1) << 32));\n    return n * sizeof(T);\n  }\n\n  IOBuf* buf_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncBase.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncBase.h>\n\n#include <cerrno>\n#include <ostream>\n#include <stdexcept>\n#include <string>\n\n#include <glog/logging.h>\n\n#include <folly/Exception.h>\n#include <folly/Format.h>\n#include <folly/portability/Filesystem.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\nAsyncBaseOp::AsyncBaseOp(NotificationCallback cb)\n    : cb_(std::move(cb)), state_(State::UNINITIALIZED), result_(-EINVAL) {}\n\nvoid AsyncBaseOp::reset(NotificationCallback cb) {\n  CHECK_NE(state_, State::PENDING);\n  cb_ = std::move(cb);\n  state_ = State::UNINITIALIZED;\n  result_ = -EINVAL;\n}\n\nAsyncBaseOp::~AsyncBaseOp() {\n  CHECK_NE(state_, State::PENDING);\n}\n\nvoid AsyncBaseOp::start() {\n  DCHECK_EQ(state_, State::INITIALIZED);\n  state_ = State::PENDING;\n}\n\nvoid AsyncBaseOp::unstart() {\n  DCHECK_EQ(state_, State::PENDING);\n  state_ = State::INITIALIZED;\n}\n\nvoid AsyncBaseOp::complete(ssize_t result) {\n  DCHECK_EQ(state_, State::PENDING);\n  state_ = State::COMPLETED;\n  result_ = result;\n  if (cb_) {\n    cb_(this);\n  }\n}\n\nvoid AsyncBaseOp::cancel() {\n  DCHECK_EQ(state_, State::PENDING);\n  state_ = State::CANCELED;\n}\n\nssize_t AsyncBaseOp::result() const {\n  CHECK_EQ(state_, State::COMPLETED);\n  return result_;\n}\n\nvoid AsyncBaseOp::init() {\n  CHECK_EQ(state_, State::UNINITIALIZED);\n  state_ = State::INITIALIZED;\n}\n\nstd::string AsyncBaseOp::fd2name(int fd) {\n  auto link = fs::path{\"/proc/self/fd\"} / folly::to<std::string>(fd);\n  return fs::read_symlink(link).string();\n}\n\nAsyncBase::AsyncBase(size_t capacity, PollMode pollMode)\n    : capacity_(capacity), pollMode_(pollMode) {\n  CHECK_GT(capacity_, 0);\n  completed_.reserve(capacity_);\n}\n\nAsyncBase::~AsyncBase() {\n  CHECK_EQ(pending_, 0);\n  CHECK_EQ(pollFd_, -1);\n}\n\nvoid AsyncBase::decrementPending(size_t n) {\n  auto p =\n      pending_.fetch_add(static_cast<size_t>(-n), std::memory_order_acq_rel);\n  DCHECK_GE(p, 1);\n}\n\nvoid AsyncBase::submit(Op* op) {\n  CHECK_EQ(op->state(), Op::State::INITIALIZED);\n  initializeContext(); // on demand\n\n  // We can increment past capacity, but we'll clean up after ourselves.\n  auto p = pending_.fetch_add(1, std::memory_order_acq_rel);\n  if (p >= capacity_) {\n    decrementPending();\n    throw std::range_error(\"AsyncBase: too many pending requests\");\n  }\n\n  op->start();\n  int rc = submitOne(op);\n\n  if (rc <= 0) {\n    op->unstart();\n    decrementPending();\n    if (rc < 0) {\n      throwSystemErrorExplicit(-rc, \"AsyncBase: io_submit failed\");\n    }\n  }\n  submitted_ += rc;\n  DCHECK_EQ(rc, 1);\n}\n\nint AsyncBase::submit(Range<Op**> ops) {\n  for (auto& op : ops) {\n    CHECK_EQ(op->state(), Op::State::INITIALIZED);\n    op->start();\n  }\n  initializeContext(); // on demand\n\n  // We can increment past capacity, but we'll clean up after ourselves.\n  auto p = pending_.fetch_add(ops.size(), std::memory_order_acq_rel);\n  if (p >= capacity_) {\n    decrementPending(ops.size());\n    throw std::range_error(\"AsyncBase: too many pending requests\");\n  }\n\n  int rc = submitRange(ops);\n\n  if (rc < 0) {\n    decrementPending(ops.size());\n    throwSystemErrorExplicit(-rc, \"AsyncBase: io_submit failed\");\n  }\n  // Any ops that did not get submitted go back to INITIALIZED state\n  // and are removed from pending count.\n  for (size_t i = rc; i < ops.size(); i++) {\n    ops[i]->unstart();\n    decrementPending(1);\n  }\n  submitted_ += rc;\n  DCHECK_LE(rc, ops.size());\n\n  return rc;\n}\n\nRange<AsyncBase::Op**> AsyncBase::wait(size_t minRequests) {\n  CHECK(isInit());\n  CHECK_EQ(pollFd_, -1) << \"wait() only allowed on non-pollable object\";\n  auto p = pending_.load(std::memory_order_acquire);\n  CHECK_LE(minRequests, p);\n  return doWait(WaitType::COMPLETE, minRequests, p, completed_);\n}\n\nRange<AsyncBase::Op**> AsyncBase::cancel() {\n  CHECK(isInit());\n  auto p = pending_.load(std::memory_order_acquire);\n  return doWait(WaitType::CANCEL, p, p, canceled_);\n}\n\nRange<AsyncBase::Op**> AsyncBase::pollCompleted() {\n  CHECK(isInit());\n  CHECK_NE(pollFd_, -1) << \"pollCompleted() only allowed on pollable object\";\n\n  if (drainPollFd() <= 0) {\n    return Range<Op**>(); // nothing completed\n  }\n\n  // Don't reap more than pending_, as we've just reset the counter to 0.\n  return doWait(WaitType::COMPLETE, 0, pending_.load(), completed_);\n}\n\nAsyncBaseQueue::AsyncBaseQueue(AsyncBase* asyncBase) : asyncBase_(asyncBase) {}\n\nAsyncBaseQueue::~AsyncBaseQueue() {\n  CHECK_EQ(asyncBase_->pending(), 0);\n}\n\nvoid AsyncBaseQueue::submit(AsyncBaseOp* op) {\n  submit([op]() { return op; });\n}\n\nvoid AsyncBaseQueue::submit(OpFactory op) {\n  queue_.push_back(op);\n  maybeDequeue();\n}\n\nvoid AsyncBaseQueue::onCompleted(AsyncBaseOp* /* op */) {\n  maybeDequeue();\n}\n\nvoid AsyncBaseQueue::maybeDequeue() {\n  while (!queue_.empty() && asyncBase_->pending() < asyncBase_->capacity()) {\n    auto& opFactory = queue_.front();\n    auto op = opFactory();\n    queue_.pop_front();\n\n    // Interpose our completion callback\n    auto nextCb = op->getNotificationCallback();\n    op->setNotificationCallback(\n        [this, nextCb{std::move(nextCb)}](AsyncBaseOp* op2) mutable {\n          this->onCompleted(op2);\n          if (nextCb) {\n            nextCb(op2);\n          }\n        });\n\n    asyncBase_->submit(op);\n  }\n}\n\n// debugging helpers:\n\nnamespace {\n\n#define X(c) \\\n  case c:    \\\n    return #c\n\nconst char* asyncIoOpStateToString(AsyncBaseOp::State state) {\n  switch (state) {\n    X(AsyncBaseOp::State::UNINITIALIZED);\n    X(AsyncBaseOp::State::INITIALIZED);\n    X(AsyncBaseOp::State::PENDING);\n    X(AsyncBaseOp::State::COMPLETED);\n    X(AsyncBaseOp::State::CANCELED);\n  }\n  return \"<INVALID AsyncBaseOp::State>\";\n}\n#undef X\n} // namespace\n\nstd::ostream& operator<<(std::ostream& os, const AsyncBaseOp& op) {\n  op.toStream(os);\n  return os;\n}\n\nstd::ostream& operator<<(std::ostream& os, AsyncBaseOp::State state) {\n  return os << asyncIoOpStateToString(state);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <atomic>\n#include <cstdint>\n#include <deque>\n#include <functional>\n#include <iosfwd>\n#include <mutex>\n#include <utility>\n#include <vector>\n\n#include <folly/Function.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/portability/SysUio.h>\n\nnamespace folly {\nclass AsyncIOOp;\nclass IoUringOp;\n/**\n * An AsyncBaseOp represents a pending operation.  You may set a notification\n * callback or you may use this class's methods directly.\n *\n * The op must remain allocated until it is completed or canceled.\n */\nclass AsyncBaseOp {\n  friend class AsyncBase;\n\n public:\n  using NotificationCallback = folly::Function<void(AsyncBaseOp*)>;\n\n  explicit AsyncBaseOp(NotificationCallback cb = NotificationCallback());\n  AsyncBaseOp(const AsyncBaseOp&) = delete;\n  AsyncBaseOp& operator=(const AsyncBaseOp&) = delete;\n  virtual ~AsyncBaseOp();\n\n  enum class State {\n    UNINITIALIZED,\n    INITIALIZED,\n    PENDING,\n    COMPLETED,\n    CANCELED,\n  };\n\n  /**\n   * Initiate a read request.\n   */\n  virtual void pread(int fd, void* buf, size_t size, off_t start) = 0;\n  void pread(int fd, Range<unsigned char*> range, off_t start) {\n    pread(fd, range.begin(), range.size(), start);\n  }\n  virtual void preadv(int fd, const iovec* iov, int iovcnt, off_t start) = 0;\n  virtual void pread(\n      int fd, void* buf, size_t size, off_t start, int /*buf_index*/) {\n    pread(fd, buf, size, start);\n  }\n\n  /**\n   * Initiate a write request.\n   */\n  virtual void pwrite(int fd, const void* buf, size_t size, off_t start) = 0;\n  void pwrite(int fd, Range<const unsigned char*> range, off_t start) {\n    pwrite(fd, range.begin(), range.size(), start);\n  }\n  virtual void pwritev(int fd, const iovec* iov, int iovcnt, off_t start) = 0;\n  virtual void pwrite(\n      int fd, const void* buf, size_t size, off_t start, int /*buf_index*/) {\n    pwrite(fd, buf, size, start);\n  }\n\n  // we support only these subclasses\n  virtual AsyncIOOp* getAsyncIOOp() = 0;\n  virtual IoUringOp* getIoUringOp() = 0;\n\n  // ostream output\n  virtual void toStream(std::ostream& os) const = 0;\n\n  /**\n   * Return the current operation state.\n   */\n  State state() const { return state_; }\n\n  /**\n   * user data get/set\n   */\n  void* getUserData() const { return userData_; }\n\n  void setUserData(void* userData) { userData_ = userData; }\n\n  /**\n   * Reset the operation for reuse.  It is an error to call reset() on\n   * an Op that is still pending.\n   */\n  virtual void reset(NotificationCallback cb = NotificationCallback()) = 0;\n\n  void setNotificationCallback(NotificationCallback cb) { cb_ = std::move(cb); }\n\n  /**\n   * Get the notification callback from the op.\n   *\n   * Note that this moves the callback out, leaving the callback in an\n   * uninitialized state! You must call setNotificationCallback before\n   * submitting the operation!\n   */\n  NotificationCallback getNotificationCallback() { return std::move(cb_); }\n\n  /**\n   * Retrieve the result of this operation.  Returns >=0 on success,\n   * -errno on failure (that is, using the Linux kernel error reporting\n   * conventions).  Use checkKernelError (folly/Exception.h) on the result to\n   * throw a std::system_error in case of error instead.\n   *\n   * It is an error to call this if the Op hasn't completed.\n   */\n  ssize_t result() const;\n\n  // debug helper\n  static std::string fd2name(int fd);\n\n protected:\n  void init();\n  void start();\n  void unstart();\n  void complete(ssize_t result);\n  void cancel();\n\n  NotificationCallback cb_;\n  std::atomic<State> state_;\n  ssize_t result_;\n  void* userData_{nullptr};\n};\n\nstd::ostream& operator<<(std::ostream& os, const AsyncBaseOp& op);\nstd::ostream& operator<<(std::ostream& os, AsyncBaseOp::State state);\n\n/**\n * Generic C++ interface around Linux IO(io_submit, io_uring)\n */\nclass AsyncBase {\n public:\n  using Op = AsyncBaseOp;\n\n  enum PollMode {\n    NOT_POLLABLE,\n    POLLABLE,\n  };\n\n  /**\n   * Create an AsyncBase context capable of holding at most 'capacity' pending\n   * requests at the same time.  As requests complete, others can be scheduled,\n   * as long as this limit is not exceeded.\n   *\n   * If pollMode is POLLABLE, pollFd() will return a file descriptor that\n   * can be passed to poll / epoll / select and will become readable when\n   * any IOs on this AsyncBase have completed.  If you do this, you must use\n   * pollCompleted() instead of wait() -- do not read from the pollFd()\n   * file descriptor directly.\n   *\n   * You may use the same AsyncBase object from multiple threads, as long as\n   * there is only one concurrent caller of wait() / pollCompleted() / cancel()\n   * (perhaps by always calling it from the same thread, or by providing\n   * appropriate mutual exclusion).  In this case, pending() returns a snapshot\n   * of the current number of pending requests.\n   */\n  explicit AsyncBase(size_t capacity, PollMode pollMode = NOT_POLLABLE);\n  AsyncBase(const AsyncBase&) = delete;\n  AsyncBase& operator=(const AsyncBase&) = delete;\n  virtual ~AsyncBase();\n\n  /**\n   * Initialize context\n   */\n  virtual void initializeContext() = 0;\n\n  /**\n   * Wait for at least minRequests to complete.  Returns the requests that\n   * have completed; the returned range is valid until the next call to\n   * wait().  minRequests may be 0 to not block.\n   */\n  Range<Op**> wait(size_t minRequests);\n\n  /**\n   * Cancel all pending requests and return them; the returned range is\n   * valid until the next call to cancel().\n   */\n  Range<Op**> cancel();\n\n  /**\n   * Return the number of pending requests.\n   */\n  size_t pending() const { return pending_; }\n\n  /**\n   * Return the maximum number of requests that can be kept outstanding\n   * at any one time.\n   */\n  size_t capacity() const { return capacity_; }\n\n  /**\n   * Return the accumulative number of submitted I/O, since this object\n   * has been created.\n   */\n  size_t totalSubmits() const { return submitted_; }\n\n  /**\n   * If POLLABLE, return a file descriptor that can be passed to poll / epoll\n   * and will become readable when any async IO operations have completed.\n   * If NOT_POLLABLE, return -1.\n   */\n  int pollFd() const { return pollFd_; }\n\n  /**\n   * If POLLABLE, call instead of wait after the file descriptor returned\n   * by pollFd() became readable.  The returned range is valid until the next\n   * call to pollCompleted().\n   */\n  Range<Op**> pollCompleted();\n\n  /**\n   * Submit an op for execution.\n   */\n  void submit(Op* op);\n\n  /**\n   * Submit a range of ops for execution\n   */\n  int submit(Range<Op**> ops);\n\n protected:\n  virtual int drainPollFd() = 0;\n  void complete(Op* op, ssize_t result) { op->complete(result); }\n\n  void cancel(Op* op) { op->cancel(); }\n\n  bool isInit() const { return init_.load(std::memory_order_relaxed); }\n\n  void decrementPending(size_t num = 1);\n  virtual int submitOne(AsyncBase::Op* op) = 0;\n  virtual int submitRange(Range<AsyncBase::Op**> ops) = 0;\n\n  enum class WaitType { COMPLETE, CANCEL };\n  virtual Range<AsyncBase::Op**> doWait(\n      WaitType type,\n      size_t minRequests,\n      size_t maxRequests,\n      std::vector<Op*>& result) = 0;\n\n  std::atomic<bool> init_{false};\n  std::mutex initMutex_;\n\n  std::atomic<size_t> pending_{0};\n  std::atomic<size_t> submitted_{0};\n  const size_t capacity_;\n  const PollMode pollMode_;\n  int pollFd_{-1};\n  std::vector<Op*> completed_;\n  std::vector<Op*> canceled_;\n};\n\n/**\n * Wrapper around AsyncBase that allows you to schedule more requests than\n * the AsyncBase's object capacity.  Other requests are queued and processed\n * in a FIFO order.\n */\nclass AsyncBaseQueue {\n public:\n  /**\n   * Create a queue, using the given AsyncBase object.\n   * The AsyncBase object may not be used by anything else until the\n   * queue is destroyed.\n   */\n  explicit AsyncBaseQueue(AsyncBase* asyncBase);\n  ~AsyncBaseQueue();\n\n  size_t queued() const { return queue_.size(); }\n\n  /**\n   * Submit an op to the AsyncBase queue.  The op will be queued until\n   * the AsyncBase object has room.\n   */\n  void submit(AsyncBaseOp* op);\n\n  /**\n   * Submit a delayed op to the AsyncBase queue; this allows you to postpone\n   * creation of the Op (which may require allocating memory, etc) until\n   * the AsyncBase object has room.\n   */\n  using OpFactory = std::function<AsyncBaseOp*()>;\n  void submit(OpFactory op);\n\n private:\n  void onCompleted(AsyncBaseOp* op);\n  void maybeDequeue();\n\n  AsyncBase* asyncBase_;\n\n  std::deque<OpFactory> queue_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncIO.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncIO.h>\n\n#include <cerrno>\n#include <ostream>\n#include <stdexcept>\n#include <string>\n\n#include <boost/intrusive/parent_from_member.hpp>\n#include <fmt/ostream.h>\n#include <glog/logging.h>\n\n#include <folly/Exception.h>\n#include <folly/Likely.h>\n#include <folly/String.h>\n#include <folly/portability/Unistd.h>\n#include <folly/small_vector.h>\n\n#if __has_include(<sys/eventfd.h>)\n#include <sys/eventfd.h>\n#endif\n\n#if __has_include(<libaio.h>)\n\n// debugging helpers\nnamespace {\n#define X(c) \\\n  case c:    \\\n    return #c\n\nconst char* iocbCmdToString(short int cmd_short) {\n  auto cmd = static_cast<io_iocb_cmd>(cmd_short);\n  switch (cmd) {\n    X(IO_CMD_PREAD);\n    X(IO_CMD_PWRITE);\n    X(IO_CMD_FSYNC);\n    X(IO_CMD_FDSYNC);\n    X(IO_CMD_POLL);\n    X(IO_CMD_NOOP);\n    X(IO_CMD_PREADV);\n    X(IO_CMD_PWRITEV);\n  }\n  return \"<INVALID io_iocb_cmd>\";\n}\n\n#undef X\n\nvoid toStream(std::ostream& os, const iocb& cb) {\n  fmt::print(\n      os,\n      \"data={}, key={}, opcode={}, reqprio={}, fd={}, f={}, \",\n      cb.data,\n      cb.key,\n      iocbCmdToString(cb.aio_lio_opcode),\n      cb.aio_reqprio,\n      cb.aio_fildes,\n      folly::AsyncBaseOp::fd2name(cb.aio_fildes));\n\n  switch (cb.aio_lio_opcode) {\n    case IO_CMD_PREAD:\n    case IO_CMD_PWRITE:\n      fmt::print(\n          os,\n          \"buf={}, offset={}, nbytes={}, \",\n          cb.u.c.buf,\n          cb.u.c.offset,\n          cb.u.c.nbytes);\n      break;\n    default:\n      os << \"[TODO: write debug string for \"\n         << iocbCmdToString(cb.aio_lio_opcode) << \"] \";\n      break;\n  }\n}\n\n} // namespace\n\nnamespace folly {\n\nAsyncIOOp::AsyncIOOp(NotificationCallback cb) : AsyncBaseOp(std::move(cb)) {\n  memset(&iocb_, 0, sizeof(iocb_));\n}\n\nvoid AsyncIOOp::reset(NotificationCallback cb) {\n  CHECK_NE(state_, State::PENDING);\n  cb_ = std::move(cb);\n  state_ = State::UNINITIALIZED;\n  result_ = -EINVAL;\n  memset(&iocb_, 0, sizeof(iocb_));\n}\n\nAsyncIOOp::~AsyncIOOp() = default;\n\nvoid AsyncIOOp::pread(int fd, void* buf, size_t size, off_t start) {\n  init();\n  io_prep_pread(&iocb_, fd, buf, size, start);\n}\n\nvoid AsyncIOOp::preadv(int fd, const iovec* iov, int iovcnt, off_t start) {\n  init();\n  io_prep_preadv(&iocb_, fd, iov, iovcnt, start);\n}\n\nvoid AsyncIOOp::pwrite(int fd, const void* buf, size_t size, off_t start) {\n  init();\n  io_prep_pwrite(&iocb_, fd, const_cast<void*>(buf), size, start);\n}\n\nvoid AsyncIOOp::pwritev(int fd, const iovec* iov, int iovcnt, off_t start) {\n  init();\n  io_prep_pwritev(&iocb_, fd, iov, iovcnt, start);\n}\n\nvoid AsyncIOOp::toStream(std::ostream& os) const {\n  os << \"{\" << state_ << \", \";\n\n  if (state_ != AsyncBaseOp::State::UNINITIALIZED) {\n    ::toStream(os, iocb_);\n  }\n\n  if (state_ == AsyncBaseOp::State::COMPLETED) {\n    os << \"result=\" << result_;\n    if (result_ < 0) {\n      os << \" (\" << errnoStr(-result_) << ')';\n    }\n    os << \", \";\n  }\n\n  os << \"}\";\n}\n\nstd::ostream& operator<<(std::ostream& os, const AsyncIOOp& op) {\n  op.toStream(os);\n  return os;\n}\n\nAsyncIO::AsyncIO(size_t capacity, PollMode pollMode)\n    : AsyncBase(capacity, pollMode) {\n  // we need to create the eventfd in the constructor\n  // since we have code that relies on registering the pollFd_\n  // before any operation is started\n\n  if (pollMode_ == POLLABLE) {\n#if __has_include(<sys/eventfd.h>)\n    pollFd_ = eventfd(0, EFD_NONBLOCK);\n    checkUnixError(pollFd_, \"AsyncIO: eventfd creation failed\");\n#else\n    // fallthrough to not-pollable, observed as: pollFd() == -1\n#endif\n  }\n}\n\nAsyncIO::~AsyncIO() {\n  CHECK_EQ(pending_, 0);\n  if (ctx_) {\n    int rc = io_queue_release(ctx_);\n    CHECK_EQ(rc, 0) << \"io_queue_release: \" << errnoStr(-rc);\n  }\n\n  if (pollFd_ != -1) {\n    CHECK_ERR(fileops::close(pollFd_));\n    pollFd_ = -1;\n  }\n}\n\nvoid AsyncIO::initializeContext() {\n  if (!init_.load(std::memory_order_acquire)) {\n    std::lock_guard lock(initMutex_);\n    if (!init_.load(std::memory_order_relaxed)) {\n      int rc = io_queue_init(capacity_, &ctx_);\n      // returns negative errno\n      if (rc == -EAGAIN) {\n        long aio_nr, aio_max;\n        std::unique_ptr<FILE, int (*)(FILE*)> fp(\n            fopen(\"/proc/sys/fs/aio-nr\", \"r\"), fclose);\n        PCHECK(fp);\n        CHECK_EQ(fscanf(fp.get(), \"%ld\", &aio_nr), 1);\n\n        std::unique_ptr<FILE, int (*)(FILE*)> aio_max_fp(\n            fopen(\"/proc/sys/fs/aio-max-nr\", \"r\"), fclose);\n        PCHECK(aio_max_fp);\n        CHECK_EQ(fscanf(aio_max_fp.get(), \"%ld\", &aio_max), 1);\n\n        LOG(ERROR) << \"No resources for requested capacity of \" << capacity_;\n        LOG(ERROR) << \"aio_nr \" << aio_nr << \", aio_max_nr \" << aio_max;\n      }\n\n      checkKernelError(rc, \"AsyncIO: io_queue_init failed\");\n      DCHECK(ctx_);\n\n      init_.store(true, std::memory_order_release);\n    }\n  }\n}\n\nint AsyncIO::drainPollFd() {\n  uint64_t numEvents;\n  // This sets the eventFd counter to 0, see\n  // http://www.kernel.org/doc/man-pages/online/pages/man2/eventfd.2.html\n  ssize_t rc;\n  do {\n    rc = fileops::read(pollFd_, &numEvents, 8);\n  } while (rc == -1 && errno == EINTR);\n  if (FOLLY_UNLIKELY(rc == -1 && errno == EAGAIN)) {\n    return 0;\n  }\n  checkUnixError(rc, \"AsyncIO: read from event fd failed\");\n  DCHECK_EQ(rc, 8);\n  DCHECK_GT(numEvents, 0);\n  return static_cast<int>(numEvents);\n}\n\nint AsyncIO::submitOne(AsyncBase::Op* op) {\n  // -1 return here will trigger throw if op isn't an AsyncIOOp\n  AsyncIOOp* aop = op->getAsyncIOOp();\n\n  if (!aop) {\n    return -1;\n  }\n\n  iocb* cb = &aop->iocb_;\n  cb->data = nullptr; // unused\n  if (pollFd_ != -1) {\n    io_set_eventfd(cb, pollFd_);\n  }\n\n  return io_submit(ctx_, 1, &cb);\n}\n\nint AsyncIO::submitRange(Range<AsyncBase::Op**> ops) {\n  std::vector<iocb*> vec;\n  vec.reserve(ops.size());\n  for (auto& op : ops) {\n    AsyncIOOp* aop = op->getAsyncIOOp();\n    if (!aop) {\n      continue;\n    }\n\n    iocb* cb = &aop->iocb_;\n    cb->data = nullptr; // unused\n    if (pollFd_ != -1) {\n      io_set_eventfd(cb, pollFd_);\n    }\n\n    vec.push_back(cb);\n  }\n\n  return vec.size() ? io_submit(ctx_, vec.size(), vec.data()) : -1;\n}\n\nRange<AsyncBase::Op**> AsyncIO::doWait(\n    WaitType type,\n    size_t minRequests,\n    size_t maxRequests,\n    std::vector<AsyncBase::Op*>& result) {\n  size_t constexpr kNumInlineRequests = 16;\n  folly::small_vector<io_event, kNumInlineRequests> events(maxRequests);\n\n  // Unfortunately, Linux AIO doesn't implement io_cancel, so even for\n  // WaitType::CANCEL we have to wait for IO completion.\n  size_t count = 0;\n  do {\n    int ret;\n    do {\n      // GOTCHA: io_getevents() may returns less than min_nr results if\n      // interrupted after some events have been read (if before, -EINTR\n      // is returned).\n      ret = io_getevents(\n          ctx_,\n          minRequests - count,\n          maxRequests - count,\n          events.data() + count,\n          /* timeout */ nullptr); // wait forever\n    } while (ret == -EINTR);\n    // Check as may not be able to recover without leaking events.\n    CHECK_GE(ret, 0)\n        << \"AsyncIO: io_getevents failed with error \" << errnoStr(-ret);\n    count += ret;\n  } while (count < minRequests);\n  DCHECK_LE(count, maxRequests);\n\n  result.clear();\n  for (size_t i = 0; i < count; ++i) {\n    CHECK(events[i].obj);\n    Op* op = boost::intrusive::get_parent_from_member(\n        events[i].obj, &AsyncIOOp::iocb_);\n    decrementPending();\n    switch (type) {\n      case WaitType::COMPLETE:\n        complete(op, events[i].res);\n        break;\n      case WaitType::CANCEL:\n        cancel(op);\n        break;\n    }\n    result.push_back(op);\n  }\n\n  return range(result);\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/AsyncIO.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncBase.h>\n\n#if __has_include(<libaio.h>)\n\n#include <libaio.h>\n\nnamespace folly {\n\nclass AsyncIOOp : public AsyncBaseOp {\n  friend class AsyncIO;\n  friend std::ostream& operator<<(std::ostream& os, const AsyncIOOp& o);\n\n public:\n  explicit AsyncIOOp(NotificationCallback cb = NotificationCallback());\n  AsyncIOOp(const AsyncIOOp&) = delete;\n  AsyncIOOp& operator=(const AsyncIOOp&) = delete;\n  ~AsyncIOOp() override;\n\n  /**\n   * Initiate a read request.\n   */\n  void pread(int fd, void* buf, size_t size, off_t start) override;\n  void preadv(int fd, const iovec* iov, int iovcnt, off_t start) override;\n\n  /**\n   * Initiate a write request.\n   */\n  void pwrite(int fd, const void* buf, size_t size, off_t start) override;\n  void pwritev(int fd, const iovec* iov, int iovcnt, off_t start) override;\n\n  void reset(NotificationCallback cb = NotificationCallback()) override;\n\n  AsyncIOOp* getAsyncIOOp() override { return this; }\n\n  IoUringOp* getIoUringOp() override { return nullptr; }\n\n  void toStream(std::ostream& os) const override;\n\n  const iocb& getIocb() const { return iocb_; }\n\n private:\n  iocb iocb_;\n};\n\nstd::ostream& operator<<(std::ostream& os, const AsyncIOOp& op);\n\n/**\n * C++ interface around Linux Async IO.\n */\nclass AsyncIO : public AsyncBase {\n public:\n  using Op = AsyncIOOp;\n\n  /**\n   * Note: the maximum number of allowed concurrent requests is controlled\n   * by the fs.aio-max-nr sysctl, the default value is usually 64K.\n   */\n  explicit AsyncIO(size_t capacity, PollMode pollMode = NOT_POLLABLE);\n  AsyncIO(const AsyncIO&) = delete;\n  AsyncIO& operator=(const AsyncIO&) = delete;\n  ~AsyncIO() override;\n\n  void initializeContext() override;\n\n protected:\n  int drainPollFd() override;\n  int submitOne(AsyncBase::Op* op) override;\n  int submitRange(Range<AsyncBase::Op**> ops) override;\n\n private:\n  Range<AsyncBase::Op**> doWait(\n      WaitType type,\n      size_t minRequests,\n      size_t maxRequests,\n      std::vector<AsyncBase::Op*>& result) override;\n\n  io_context_t ctx_{nullptr};\n};\n\nusing AsyncIOQueue = AsyncBaseQueue;\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/AsyncIoUringSocket.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Conv.h>\n#include <folly/detail/SocketFastOpen.h>\n#include <folly/io/Cursor.h>\n#include <folly/io/async/AsyncIoUringSocket.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/IoUringEventBaseLocal.h>\n#include <folly/io/async/IoUringProvidedBufferRing.h>\n#include <folly/memory/Malloc.h>\n#include <folly/portability/SysUio.h>\n\n#if FOLLY_HAS_LIBURING\n\nnamespace fsp = folly::portability::sockets;\n\nnamespace folly {\n\nnamespace {\nenum ShutdownFlags {\n  ShutFlags_WritePending = 1,\n  ShutFlags_Write = 2,\n  ShutFlags_Read = 4,\n};\n\nAsyncSocket* getAsyncSocket(AsyncTransport::UniquePtr const& o) {\n  auto* raw = o->getUnderlyingTransport<folly::AsyncSocket>();\n  if (!raw) {\n    throw std::runtime_error(\"need to take a AsyncSocket\");\n  }\n  return raw;\n}\n\nint ensureSocketReturnCode(int x, char const* message) {\n  if (x >= 0) {\n    return x;\n  }\n  auto errnoCopy = errno;\n  throw AsyncSocketException(\n      AsyncSocketException::INTERNAL_ERROR, message, errnoCopy);\n}\n\nNetworkSocket makeConnectSocket(SocketAddress const& peerAddress) {\n  int fd = ensureSocketReturnCode(\n      ::socket(peerAddress.getFamily(), SOCK_STREAM, 0),\n      \"failed to create socket\");\n  ensureSocketReturnCode(fcntl(fd, F_SETFD, FD_CLOEXEC), \"set cloexec\");\n\n  // copied from folly::AsyncSocket, default enable TCP_NODELAY\n  // If setNoDelay() fails, we continue anyway; this isn't a fatal error.\n  // setNoDelay() will log an error message if it fails.\n  int nodelay = 1;\n  int ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));\n  if (ret != 0) {\n    VLOG(1) << \"setNoDelay failed \" << folly::errnoStr(errno);\n  }\n  return NetworkSocket{fd};\n}\n\nIoUringBackend* getBackendFromEventBase(EventBase* evb) {\n  auto* b = IoUringEventBaseLocal::try_get(evb);\n  if (!b) {\n    b = dynamic_cast<IoUringBackend*>(evb->getBackend());\n  }\n  if (!b) {\n    throw std::runtime_error(\"need to take a IoUringBackend event base\");\n  }\n  return b;\n}\n\n} // namespace\n\nAsyncIoUringSocket::AsyncIoUringSocket(\n    folly::AsyncSocket* other, Options&& options)\n    : AsyncIoUringSocket(other->getEventBase(), std::move(options)) {\n  setPreReceivedData(other->takePreReceivedData());\n  setFd(other->detachNetworkSocket());\n  state_ = State::Established;\n}\n\nAsyncIoUringSocket::AsyncIoUringSocket(\n    AsyncTransport::UniquePtr other, Options&& options)\n    : AsyncIoUringSocket(getAsyncSocket(other), std::move(options)) {}\n\nAsyncIoUringSocket::AsyncIoUringSocket(EventBase* evb, Options&& options)\n    : evb_(evb), options_(std::move(options)) {\n  backend_ = getBackendFromEventBase(evb);\n\n  if (!backend_->hasBufferProvider()) {\n    throw std::runtime_error(\"require a IoUringBackend with a buffer provider\");\n  }\n  readSqe_ = ReadSqe::UniquePtr(new ReadSqe(this));\n}\n\nAsyncIoUringSocket::AsyncIoUringSocket(\n    EventBase* evb, NetworkSocket ns, Options&& options)\n    : AsyncIoUringSocket(evb, std::move(options)) {\n  setFd(ns);\n  state_ = State::Established;\n}\n\nstd::string AsyncIoUringSocket::toString(AsyncIoUringSocket::State s) {\n  switch (s) {\n    case State::None:\n      return \"None\";\n    case State::Connecting:\n      return \"Connecting\";\n    case State::Established:\n      return \"Established\";\n    case State::Closed:\n      return \"Closed\";\n    case State::Error:\n      return \"Error\";\n    case State::FastOpen:\n      return \"FastOpen\";\n  }\n  return to<std::string>(\"Unknown val=\", (int)s);\n}\n\nstd::unique_ptr<IOBuf>\nAsyncIoUringSocket::Options::defaultAllocateNoBufferPoolBuffer() {\n  size_t size = goodMallocSize(16384);\n  VLOG(2) << \"UseProvidedBuffers slow path starting with \" << size << \" bytes \";\n  return IOBuf::create(size);\n}\n\nAsyncIoUringSocket::ReadSqe::ReadSqe(AsyncIoUringSocket* parent)\n    : IoSqeBase(IoSqeBase::Type::Read), parent_(parent) {\n  supportsMultishotRecv_ = parent->options_.multishotRecv &&\n      parent->backend_->kernelSupportsRecvmsgMultishot();\n  useBundles_ = parent->options_.useBundles;\n  // If the backend for this socket has an IoUringZeroCopyBufferPool, then zero\n  // copy is enabled implicitly.\n  supportsZeroCopyRx_ = parent->backend_->zcBufferPool() != nullptr;\n  setEventBase(parent->evb_);\n}\n\nAsyncIoUringSocket::~AsyncIoUringSocket() {\n  VLOG(3) << \"~AsyncIoUringSocket() \" << this;\n\n  // this is a bit unnecessary if we are already closed, but proper state\n  // tracking is coming later and will be easier to handle then\n  closeNow();\n\n  // evb_/backend_ might be null here, but then none of these will be in flight\n\n  // cancel outstanding\n  if (readSqe_->inFlight()) {\n    VLOG(3) << \"cancel reading \" << readSqe_.get();\n    readSqe_->setReadCallback(\n        nullptr, false); // not detaching, actually closing\n    readSqe_->detachEventBase();\n    backend_->cancel(readSqe_.release());\n  }\n\n  if (closeSqe_ && closeSqe_->inFlight()) {\n    LOG_EVERY_N(WARNING, 100) << \" closeSqe_ still in flight\";\n    closeSqe_\n        ->markCancelled(); // still need to actually close it and it has no data\n    closeSqe_.release();\n  }\n  if (connectSqe_ && connectSqe_->inFlight()) {\n    VLOG(3) << \"cancel connect \" << connectSqe_.get();\n    connectSqe_->cancelTimeout();\n    backend_->cancel(connectSqe_.release());\n  }\n\n  VLOG(2) << \"~AsyncIoUringSocket() \" << this << \" have active \"\n          << writeSqeActive_ << \" queue=\" << writeSqeQueue_.size();\n\n  if (writeSqeActive_) {\n    // if we are detaching, then the write will not have been submitted yet\n    if (writeSqeActive_->inFlight()) {\n      backend_->cancel(writeSqeActive_);\n    } else {\n      delete writeSqeActive_;\n    }\n  }\n\n  while (!writeSqeQueue_.empty()) {\n    WriteSqe* w = &writeSqeQueue_.front();\n    CHECK(!w->inFlight());\n    writeSqeQueue_.pop_front();\n    delete w;\n  }\n}\n\nbool AsyncIoUringSocket::supports(EventBase* eb) {\n  IoUringBackend* io = dynamic_cast<IoUringBackend*>(eb->getBackend());\n  if (!io) {\n    io = IoUringEventBaseLocal::try_get(eb);\n  }\n  return io && io->hasBufferProvider();\n}\n\nbool AsyncIoUringSocket::supportsZcRx(EventBase* eb) {\n  IoUringBackend* io = dynamic_cast<IoUringBackend*>(eb->getBackend());\n  return io && io->zcBufferPool() != nullptr;\n}\n\nvoid AsyncIoUringSocket::connect(\n    AsyncSocket::ConnectCallback* callback,\n    const folly::SocketAddress& address,\n    std::chrono::milliseconds timeout,\n    SocketOptionMap const& options,\n    const BindOptions& bindOptions,\n    const std::string& ifName) noexcept {\n  VLOG(4) << \"AsyncIoUringSocket::connect() this=\" << this << \" to=\" << address\n          << \" fastopen=\" << enableTFO_;\n  evb_->dcheckIsInEventBaseThread();\n  DestructorGuard dg(this);\n  connectTimeout_ = timeout;\n  connectEndTime_ = connectStartTime_ = std::chrono::steady_clock::now();\n  if (!connectSqe_) {\n    connectSqe_ = std::make_unique<ConnectSqe>(this);\n  }\n  if (connectSqe_->inFlight()) {\n    if (auto* fd = std::get_if<NetworkSocket>(&bindOptions)) {\n      fileops::close(fd->toFd());\n    }\n    callback->connectErr(AsyncSocketException(\n        AsyncSocketException::NOT_OPEN, \"connection in flight\", -1));\n    return;\n  }\n  if (fd_ != NetworkSocket{}) {\n    if (auto* fd = std::get_if<NetworkSocket>(&bindOptions)) {\n      fileops::close(fd->toFd());\n    }\n    callback->connectErr(AsyncSocketException(\n        AsyncSocketException::NOT_OPEN, \"connection is connected\", -1));\n    return;\n  }\n  connectCallback_ = callback;\n  peerAddress_ = address;\n\n  if (auto* boundFd = std::get_if<NetworkSocket>(&bindOptions)) {\n    struct sockaddr_storage peerAddr{};\n    socklen_t peerLen = sizeof(peerAddr);\n    if (::getpeername(\n            boundFd->toFd(),\n            reinterpret_cast<struct sockaddr*>(&peerAddr),\n            &peerLen) == 0) {\n      fileops::close(boundFd->toFd());\n      callback->connectErr(AsyncSocketException(\n          AsyncSocketException::INVALID_STATE, \"boundFd is already connected\"));\n      return;\n    }\n    setFd(*boundFd);\n  } else {\n    setFd(makeConnectSocket(address));\n  }\n\n  {\n    auto result =\n        applySocketOptions(fd_, options, SocketOptionKey::ApplyPos::PRE_BIND);\n    if (result != 0) {\n      callback->connectErr(AsyncSocketException(\n          AsyncSocketException::INTERNAL_ERROR,\n          \"failed to set socket option\",\n          result));\n      return;\n    }\n  }\n\n  // bind the socket to the interface\n  if (!ifName.empty() &&\n      setSockOpt(\n          SOL_SOCKET, SO_BINDTODEVICE, ifName.c_str(), ifName.length())) {\n    auto errnoCopy = errno;\n    callback->connectErr(AsyncSocketException(\n        AsyncSocketException::NOT_OPEN,\n        \"failed to bind to device: \" + ifName,\n        errnoCopy));\n    return;\n  }\n\n  // bind the socket\n  if (auto* bindAddr = std::get_if<folly::SocketAddress>(&bindOptions);\n      bindAddr && *bindAddr != anyAddress()) {\n    sockaddr_storage addrStorage;\n    auto saddr = reinterpret_cast<sockaddr*>(&addrStorage);\n\n    int one = 1;\n#if defined(IP_BIND_ADDRESS_NO_PORT) && !FOLLY_MOBILE\n    // If the any port is specified with a non-any address this is typically\n    // a client socket. However, calling bind before connect without\n    // IP_BIND_ADDRESS_NO_PORT forces the OS to find a unique port relying\n    // on only the local tuple. This limits the range of available ephemeral\n    // ports.  Using the IP_BIND_ADDRESS_NO_PORT delays assigning a port until\n    // connect expanding the available port range, unless\n    // setBindAddressNoPort() is called.\n    if (bindAddr->getPort() == 0) {\n      if (bindAddressNoPort_ &&\n          setSockOpt(IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, &one, sizeof(one))) {\n        auto errnoCopy = errno;\n        callback->connectErr(AsyncSocketException(\n            AsyncSocketException::NOT_OPEN,\n            \"failed to setsockopt IP_BIND_ADDRESS_NO_PORT prior to bind on \" +\n                bindAddr->describe(),\n            errnoCopy));\n        return;\n      }\n    } else {\n#else\n    {\n#endif\n      if (setSockOpt(SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {\n        auto errnoCopy = errno;\n        callback->connectErr(AsyncSocketException(\n            AsyncSocketException::NOT_OPEN,\n            \"failed to setsockopt SO_REUSEADDR prior to bind on \" +\n                bindAddr->describe(),\n            errnoCopy));\n        return;\n      }\n    }\n\n    bindAddr->getAddress(&addrStorage);\n\n    if (::bind(fd_.toFd(), saddr, bindAddr->getActualSize()) != 0) {\n      auto errnoCopy = errno;\n      callback->connectErr(AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to bind to async io_uring socket: \" + bindAddr->describe(),\n          errnoCopy));\n      return;\n    }\n  }\n\n  {\n    auto result =\n        applySocketOptions(fd_, options, SocketOptionKey::ApplyPos::POST_BIND);\n    if (result != 0) {\n      callback->connectErr(AsyncSocketException(\n          AsyncSocketException::INTERNAL_ERROR,\n          \"failed to set socket option\",\n          result));\n      return;\n    }\n  }\n\n  connectCallback_->preConnect(fd_);\n  if (connectTimeout_.count() > 0) {\n    if (!connectSqe_->scheduleTimeout(\n            connectTimeout_ + std::chrono::milliseconds(3000))) {\n      connectCallback_->connectErr(AsyncSocketException(\n          AsyncSocketException::INTERNAL_ERROR,\n          \"failed to schedule connect timeout\"));\n      connectCallback_ = nullptr;\n      connectSqe_.reset();\n      return;\n    }\n  }\n  // if TCP Fast Open is\n  if (enableTFO_) {\n    state_ = State::FastOpen;\n    VLOG(5) << \"Not submitting connect as in fast open\";\n    connectCallback_->connectSuccess();\n    connectCallback_ = nullptr;\n  } else {\n    state_ = State::Connecting;\n    backend_->submit(*connectSqe_);\n  }\n}\n\nvoid AsyncIoUringSocket::cancelConnect() {\n  connectCallback_ = nullptr;\n  if (state_ == State::Connecting || state_ == State::FastOpen) {\n    closeNow();\n  }\n}\n\nvoid AsyncIoUringSocket::processConnectSubmit(\n    struct io_uring_sqe* sqe, sockaddr_storage& storage) {\n  auto len = peerAddress_.getAddress(&storage);\n  io_uring_prep_connect(sqe, usedFd_, (struct sockaddr*)&storage, len);\n  sqe->flags |= mbFixedFileFlags_;\n}\n\nvoid AsyncIoUringSocket::setStateEstablished() {\n  state_ = State::Established;\n  allowReads();\n  processWriteQueue();\n}\n\nvoid AsyncIoUringSocket::appendPreReceive(\n    std::unique_ptr<IOBuf> iobuf) noexcept {\n  readSqe_->appendPreReceive(std::move(iobuf));\n}\n\nvoid AsyncIoUringSocket::allowReads() {\n  if (readSqe_->readCallback() && !readSqe_->inFlight()) {\n    auto cb = readSqe_->readCallback();\n    setReadCB(cb);\n  }\n}\n\nvoid AsyncIoUringSocket::previousReadDone() {\n  VLOG(4) << \"AsyncIoUringSocket::previousReadDone( \" << this << \") cb=\"\n          << readSqe_->readCallback() << \" in flight=\" << readSqe_->inFlight();\n  allowReads();\n}\n\nvoid AsyncIoUringSocket::processConnectResult(const io_uring_cqe* cqe) {\n  auto res = cqe->res;\n  VLOG(5) << \"AsyncIoUringSocket::processConnectResult(\" << this\n          << \") res=\" << res;\n  DestructorGuard dg(this);\n  connectSqe_.reset();\n  connectEndTime_ = std::chrono::steady_clock::now();\n  if (res == 0) {\n    setStateEstablished();\n    if (connectCallback_) {\n      connectCallback_->connectSuccess();\n    }\n  } else {\n    state_ = State::Error;\n    if (connectCallback_) {\n      connectCallback_->connectErr(AsyncSocketException(\n          AsyncSocketException::NOT_OPEN, \"connect failed\", -res));\n    }\n  }\n  connectCallback_ = nullptr;\n}\n\nvoid AsyncIoUringSocket::processConnectTimeout() {\n  VLOG(5) << \"AsyncIoUringSocket::processConnectTimeout(this=\" << this\n          << \") connectInFlight=\" << connectSqe_->inFlight()\n          << \" state=\" << stateAsString();\n  DestructorGuard dg(this);\n  if (connectSqe_->inFlight()) {\n    backend_->cancel(connectSqe_.release());\n  } else {\n    connectSqe_.reset();\n  }\n  connectEndTime_ = std::chrono::steady_clock::now();\n  connectCallback_->connectErr(\n      AsyncSocketException(AsyncSocketException::TIMED_OUT, \"timeout\"));\n  connectCallback_ = nullptr;\n}\n\nvoid AsyncIoUringSocket::processFastOpenResult(\n    const io_uring_cqe* cqe) noexcept {\n  VLOG(4) << \"processFastOpenResult() this=\" << this << \" res=\" << cqe->res\n          << \" flags=\" << cqe->flags;\n  if (cqe->res >= 0) {\n    io_uring_cqe tmp{};\n    tmp.res = 0;\n    processConnectResult(&tmp);\n    writeSqeActive_ = fastOpenSqe_->initialWrite.release();\n    writeSqeActive_->callback(cqe);\n  } else {\n    VLOG(4) << \"TFO falling back, did not connect, res = \" << cqe->res;\n    DCHECK(connectSqe_);\n    backend_->submit(*connectSqe_);\n    writeSqeQueue_.push_back(*fastOpenSqe_->initialWrite.release());\n  }\n  fastOpenSqe_.reset();\n}\n\ninline bool AsyncIoUringSocket::ReadSqe::readCallbackUseIoBufs() const {\n  return readCallback_ && readCallback_->isBufferMovable();\n}\n\nvoid AsyncIoUringSocket::readEOF() {\n  shutdownFlags_ |= ShutFlags_Read;\n}\n\nvoid AsyncIoUringSocket::readError() {\n  VLOG(4) << \" AsyncIoUringSocket::readError() this=\" << this;\n  state_ = State::Error;\n  shutdownFlags_ |= ShutFlags_Read | ShutFlags_Write;\n}\n\nvoid AsyncIoUringSocket::setReadCB(ReadCallback* callback) {\n  bool submitNow =\n      state_ != State::FastOpen && state_ != State::Connecting && !isDetaching_;\n  VLOG(4) << \"setReadCB state=\" << stateAsString()\n          << \" isDetaching_=\" << isDetaching_;\n  readSqe_->setReadCallback(callback, submitNow);\n}\n\nvoid AsyncIoUringSocket::submitRead(bool now) {\n  VLOG(9) << \"AsyncIoUringSocket::submitRead \" << now\n          << \" sqe=\" << readSqe_.get();\n  if (readSqe_->waitingForOldEventBaseRead()) {\n    // don't actually submit, wait for old event base\n    return;\n  }\n  if (now) {\n    backend_->submitNow(*readSqe_);\n  } else {\n    backend_->submitSoon(*readSqe_);\n  }\n}\n\nvoid AsyncIoUringSocket::ReadSqe::invalidState(ReadCallback* callback) {\n  VLOG(4) << \"AsyncSocket(this=\" << this << \"): setReadCallback(\" << callback\n          << \") called in invalid state \";\n\n  AsyncSocketException ex(\n      AsyncSocketException::NOT_OPEN,\n      \"setReadCallback() called  io_uring with socket in \"\n      \"invalid state\");\n  if (callback) {\n    callback->readErr(ex);\n  }\n}\n\nbool AsyncIoUringSocket::error() const {\n  VLOG(2) << \"AsyncIoUringSocket::error(this=\" << this\n          << \") state=\" << stateAsString();\n  return state_ == State::Error;\n}\n\nbool AsyncIoUringSocket::good() const {\n  VLOG(2) << \"AsyncIoUringSocket::good(this=\" << this\n          << \") state=\" << stateAsString() << \" evb_=\" << evb_\n          << \" shutdownFlags_=\" << shutdownFlags_ << \" backend_=\" << backend_;\n  if (!evb_ || !backend_) {\n    return false;\n  }\n  if (shutdownFlags_) {\n    return false;\n  }\n  switch (state_) {\n    case State::Connecting:\n    case State::Established:\n    case State::FastOpen:\n      return true;\n    case State::None:\n    case State::Closed:\n    case State::Error:\n      return false;\n  }\n  return false;\n}\n\nbool AsyncIoUringSocket::hangup() const {\n  if (fd_ == NetworkSocket()) {\n    // sanity check, no one should ask for hangup if we are not connected.\n    assert(false);\n    return false;\n  }\n  struct pollfd fds[1];\n  fds[0].fd = fd_.toFd();\n  fds[0].events = POLLRDHUP;\n  fds[0].revents = 0;\n  ::poll(&fds[0], 1, 0);\n  return (fds[0].revents & (POLLRDHUP | POLLHUP)) != 0;\n}\n\nvoid AsyncIoUringSocket::ReadSqe::setReadCallback(\n    ReadCallback* callback, bool submitNow) {\n  VLOG(5)\n      << \"AsyncIoUringSocket::setReadCB() this=\" << this << \" cb=\" << callback\n      << \" cbWas=\" << readCallback_ << \" count=\" << setReadCbCount_\n      << \" movable=\" << (callback && callback->isBufferMovable() ? \"YES\" : \"NO\")\n      << \" inflight=\" << inFlight() << \" good_=\" << parent_->good()\n      << \" submitNow=\" << submitNow;\n\n  if (callback == readCallback_ && (!submitNow || inFlight())) {\n    VLOG(9) << \"cb the same, skipping\";\n    return;\n  }\n  setReadCbCount_++;\n  readCallback_ = callback;\n  if (!callback) {\n    return;\n  }\n  if (!submitNow) {\n    // allowable to set a read callback here\n    VLOG(5) << \"AsyncIoUringSocket::setReadCB() this=\" << this\n            << \" ignoring callback for now \";\n    return;\n  }\n  if (!parent_->good()) {\n    readCallback_ = nullptr;\n    invalidState(callback);\n    return;\n  }\n\n  processOldEventBaseRead();\n\n  // callback may change after these so make sure to check\n  if (readCallback_ && preReceivedData_) {\n    sendReadBuf(std::move(preReceivedData_), preReceivedData_);\n  }\n\n  if (readCallback_ && queuedReceivedData_) {\n    sendReadBuf(std::move(queuedReceivedData_), queuedReceivedData_);\n  }\n\n  if (readCallback_ && !inFlight()) {\n    parent_->submitRead();\n  }\n}\n\nvoid AsyncIoUringSocket::ReadSqe::processOldEventBaseRead() {\n  if (!oldEventBaseRead_ || !oldEventBaseRead_->isReady()) {\n    return;\n  }\n\n  auto res = std::move(*oldEventBaseRead_).get();\n  oldEventBaseRead_.reset();\n  VLOG(4) << \"using old event base data: \" << res.get()\n          << \" len=\" << (res ? res->length() : 0);\n  if (res && res->length()) {\n    if (queuedReceivedData_) {\n      queuedReceivedData_->appendToChain(std::move(res));\n    } else {\n      queuedReceivedData_ = std::move(res);\n    }\n  }\n}\n\nbool AsyncIoUringSocket::ReadSqe::isEOF(const io_uring_cqe* cqe) noexcept {\n  if (supportsZeroCopyRx_ && useZeroCopyRx_) {\n    return cqe->res == 0 && cqe->flags == 0;\n  }\n  return cqe->res == 0;\n}\n\nvoid AsyncIoUringSocket::ReadSqe::callback(const io_uring_cqe* cqe) noexcept {\n  auto res = cqe->res;\n  auto flags = cqe->flags;\n\n  VLOG(5) << \"AsyncIoUringSocket::ReadSqe::readCallback() this=\" << this\n          << \" parent=\" << parent_ << \" cb=\" << readCallback_ << \" res=\" << res\n          << \" max=\" << maxSize_ << \" inflight=\" << inFlight()\n          << \" has_buffer=\" << !!(flags & IORING_CQE_F_BUFFER)\n          << \" bytes_received=\" << bytesReceived_;\n  DestructorGuard dg(this);\n  bool hasMore = (flags & IORING_CQE_F_BUF_MORE) != 0;\n  auto buffer_guard = makeGuard([&] {\n    CHECK(!(flags & IORING_CQE_F_BUFFER))\n        << \"Buffer guard invoked but IORING_CQE_F_BUFFER is set! \" << \"flags=0x\"\n        << std::hex << flags << \" res=\" << std::dec << res;\n  });\n\n  if (!readCallback_) {\n    if (res == -ENOBUFS || res == -ECANCELED) {\n      // ignore\n    } else if (res <= 0) {\n      // EOF?\n      if (parent_) {\n        parent_->readEOF();\n      }\n    } else if (res > 0 && lastUsedBufferProvider_) {\n      // must take the buffer\n      uint16_t bufId = flags >> IORING_CQE_BUFFER_SHIFT;\n      appendReadData(\n          lastUsedBufferProvider_->getIoBuf(bufId, res, hasMore),\n          queuedReceivedData_);\n      buffer_guard.dismiss();\n    }\n  } else {\n    if (isEOF(cqe)) {\n      if (parent_) {\n        parent_->readEOF();\n      }\n      readCallback_->readEOF();\n    } else if (res == -ENOBUFS) {\n      if (lastUsedBufferProvider_) {\n        // urgh, resubmit and let submit logic deal with the fact\n        // we have no more buffers\n        lastUsedBufferProvider_->enobuf();\n      }\n      if (parent_) {\n        parent_->submitRead();\n      }\n    } else if (res < 0) {\n      // assume ECANCELED is not an unrecoverable error state, but we do still\n      // have to propagate to the callback as they presumably called the cancel.\n      auto callback = readCallback_;\n      if (parent_ && res != -ECANCELED) {\n        readCallback_ = nullptr;\n        parent_->readError();\n      }\n      AsyncSocketException::AsyncSocketExceptionType err;\n      std::string error;\n      switch (res) {\n        case -EBADF:\n          err = AsyncSocketException::NOT_OPEN;\n          error = \"AsyncIoUringSocket: read error: EBADF\";\n          break;\n        default:\n          err = AsyncSocketException::UNKNOWN;\n          error = to<std::string>(\n              \"AsyncIoUringSocket: read error: \",\n              folly::errnoStr(-res),\n              \": (\",\n              res,\n              \")\");\n          break;\n      }\n      callback->readErr(AsyncSocketException(err, error));\n      if (parent_ && res != -ECANCELED) {\n        parent_->failAllWrites();\n      }\n    } else {\n      uint64_t const cb_was = setReadCbCount_;\n      bytesReceived_ += res;\n      if (supportsZeroCopyRx_ && useZeroCopyRx_) {\n        const io_uring_zcrx_cqe* rcqe = (io_uring_zcrx_cqe*)(cqe + 1);\n        auto pool = parent_->backend_->zcBufferPool();\n        sendReadBuf(pool->getIoBuf(cqe, rcqe), queuedReceivedData_);\n        buffer_guard.dismiss();\n      } else if (lastUsedBufferProvider_) {\n        auto bufId = flags >> 16;\n        sendReadBuf(\n            lastUsedBufferProvider_->getIoBuf(bufId, res, hasMore),\n            queuedReceivedData_);\n\n        buffer_guard.dismiss();\n      } else {\n        // slow path as must have run out of buffers\n        // or maybe the callback does not support whole buffers\n        DCHECK(tmpBuffer_);\n        tmpBuffer_->append(res);\n        VLOG(2) << \"UseProvidedBuffers slow path completed \" << res;\n        sendReadBuf(std::move(tmpBuffer_), queuedReceivedData_);\n      }\n      // callback may have changed now, or we may not have a parent!\n      if (parent_ && setReadCbCount_ == cb_was && !inFlight()) {\n        parent_->submitRead(maxSize_ == (size_t)res);\n      }\n    }\n  }\n}\n\nvoid AsyncIoUringSocket::ReadSqe::callbackCancelled(\n    const io_uring_cqe* cqe) noexcept {\n  auto res = cqe->res;\n  auto flags = cqe->flags;\n\n  VLOG(4) << \"AsyncIoUringSocket::ReadSqe::callbackCancelled() this=\" << this\n          << \" parent=\" << parent_ << \" cb=\" << readCallback_ << \" res=\" << res\n          << \" inflight=\" << inFlight() << \" flags=\" << flags\n          << \" has_buffer=\" << !!(flags & IORING_CQE_F_BUFFER)\n          << \" bytes_received=\" << bytesReceived_;\n  DestructorGuard dg(this);\n  if (readCallback_) {\n    callback(cqe);\n  }\n  if (!(flags & IORING_CQE_F_MORE)) {\n    if (readCallback_ && res > 0) {\n      // may have more multishot\n      readCallback_->readEOF();\n      // only cancel from shutdown or event base detaching\n    }\n    destroy();\n  }\n}\n\nvoid AsyncIoUringSocket::ReadSqe::processSubmit(\n    struct io_uring_sqe* sqe) noexcept {\n  VLOG(4) << \"AsyncIoUringSocket::ReadSqe::processSubmit() this=\" << this\n          << \" parent=\" << parent_ << \" cb=\" << readCallback_;\n  lastUsedBufferProvider_ = nullptr;\n  CHECK(!waitingForOldEventBaseRead());\n  processOldEventBaseRead();\n\n  // read does not use registered fd, as it can be long lived and leak socket\n  // files\n  int fd = parent_->fd_.toFd();\n\n  if (!readCallback_) {\n    VLOG(2) << \"readProcessSubmit with no callback?\";\n    tmpBuffer_ = IOBuf::create(2000);\n    maxSize_ = tmpBuffer_->tailroom();\n    ::io_uring_prep_recv(sqe, fd, tmpBuffer_->writableTail(), maxSize_, 0);\n  } else {\n    if (supportsZeroCopyRx_ && useZeroCopyRx_) {\n      ::io_uring_prep_rw(IORING_OP_RECV_ZC, sqe, fd, nullptr, 0, 0);\n      sqe->ioprio |= IORING_RECV_MULTISHOT;\n    } else if (readCallbackUseIoBufs()) {\n      auto* bp = parent_->backend_->bufferProvider();\n      if (bp->available()) {\n        lastUsedBufferProvider_ = bp;\n        maxSize_ = lastUsedBufferProvider_->sizePerBuffer();\n\n        size_t used_len;\n        unsigned int ioprio_flags;\n        if (supportsMultishotRecv_) {\n          ioprio_flags = IORING_RECV_MULTISHOT;\n          used_len = 0;\n        } else {\n          ioprio_flags = 0;\n          used_len = maxSize_;\n        }\n\n        if (useBundles_) {\n          ioprio_flags |= IORING_RECVSEND_BUNDLE;\n        }\n\n        ::io_uring_prep_recv(sqe, fd, nullptr, used_len, 0);\n        sqe->buf_group = lastUsedBufferProvider_->gid();\n        sqe->flags |= IOSQE_BUFFER_SELECT;\n        sqe->ioprio |= ioprio_flags;\n        VLOG(9)\n            << \"AsyncIoUringSocket::readProcessSubmit bufferprovider multishot\";\n      } else {\n        // todo: it's possible the callback can hint to us how much data to use.\n        // naively you could use getReadBuffer, however it turns out that many\n        // callbacks that support isBufferMovable do not expect the transport to\n        // switch between both types of callbacks. A new API to provide a size\n        // hint might be useful in the future.\n        tmpBuffer_ = parent_->options_.allocateNoBufferPoolBuffer();\n        maxSize_ = tmpBuffer_->tailroom();\n        ::io_uring_prep_recv(sqe, fd, tmpBuffer_->writableTail(), maxSize_, 0);\n      }\n    } else {\n      void* buf;\n      readCallback_->getReadBuffer(&buf, &maxSize_);\n      maxSize_ = std::min<size_t>(maxSize_, 2048);\n      tmpBuffer_ = IOBuf::create(maxSize_);\n      ::io_uring_prep_recv(sqe, fd, tmpBuffer_->writableTail(), maxSize_, 0);\n      VLOG(9) << \"AsyncIoUringSocket::readProcessSubmit  tmp buffer using size \"\n              << maxSize_;\n    }\n\n    VLOG(5) << \"readProcessSubmit \" << this << \" reg=\" << fd\n            << \" cb=\" << readCallback_ << \" size=\" << maxSize_;\n  }\n}\n\nvoid AsyncIoUringSocket::ReadSqe::sendReadBuf(\n    std::unique_ptr<IOBuf> buf, std::unique_ptr<IOBuf>& overflow) noexcept {\n  VLOG(5) << \"AsyncIoUringSocket::ReadSqe::sendReadBuf \"\n          << hexlify(buf->coalesce());\n  while (readCallback_) {\n    if (FOLLY_LIKELY(readCallback_->isBufferMovable())) {\n      readCallback_->readBufferAvailable(std::move(buf));\n      return;\n    }\n    auto* rcb_was = readCallback_;\n    size_t sz;\n    void* b;\n\n    do {\n      readCallback_->getReadBuffer(&b, &sz);\n      size_t took = std::min<size_t>(sz, buf->length());\n      VLOG(1) << \"... inner sz=\" << sz << \"  len=\" << buf->length();\n\n      if (FOLLY_LIKELY(took)) {\n        memcpy(b, buf->data(), took);\n\n        readCallback_->readDataAvailable(took);\n        if (buf->length() == took) {\n          buf = buf->pop();\n          if (!buf) {\n            return;\n          }\n        } else {\n          buf->trimStart(took);\n        }\n      } else {\n        VLOG(1) << \"Bad!\";\n        // either empty buffer or the readcallback is bad.\n        // assume empty buffer for simplicity\n        buf = buf->pop();\n        if (!buf) {\n          return;\n        }\n      }\n    } while (readCallback_ == rcb_was);\n  }\n  appendReadData(std::move(buf), overflow);\n}\n\nstd::unique_ptr<IOBuf> AsyncIoUringSocket::ReadSqe::takePreReceivedData() {\n  return std::move(preReceivedData_);\n}\n\nvoid AsyncIoUringSocket::ReadSqe::appendReadData(\n    std::unique_ptr<IOBuf> data, std::unique_ptr<IOBuf>& overflow) noexcept {\n  if (!data) {\n    return;\n  }\n\n  if (overflow) {\n    overflow->appendToChain(std::move(data));\n  } else {\n    overflow = std::move(data);\n  }\n}\n\nvoid AsyncIoUringSocket::setPreReceivedData(std::unique_ptr<IOBuf> data) {\n  readSqe_->appendPreReceive(std::move(data));\n}\n\nAsyncIoUringSocket::WriteSqe::WriteSqe(\n    AsyncIoUringSocket* parent,\n    WriteCallback* callback,\n    std::unique_ptr<IOBuf>&& buf,\n    WriteFlags flags,\n    ZeroCopyOptions options)\n    : IoSqeBase(IoSqeBase::Type::Write),\n      parent_(parent),\n      callback_(callback),\n      buf_(std::move(buf)),\n      flags_(flags),\n      totalLength_(0),\n      zcOptions_(options) {\n  IOBuf const* p = buf_.get();\n  do {\n    if (auto l = p->length(); l > 0) {\n      iov_.emplace_back();\n      iov_.back().iov_base = const_cast<uint8_t*>(p->data());\n      iov_.back().iov_len = l;\n      totalLength_ += l;\n    }\n    p = p->next();\n  } while (p != buf_.get());\n\n  msg_.msg_iov = iov_.data();\n  msg_.msg_iovlen = std::min<uint32_t>(iov_.size(), kIovMax);\n  msg_.msg_name = nullptr;\n  msg_.msg_namelen = 0;\n  msg_.msg_control = nullptr;\n  msg_.msg_controllen = 0;\n  msg_.msg_flags = 0;\n\n  setEventBase(parent->evb_);\n}\n\nint AsyncIoUringSocket::WriteSqe::sendMsgFlags() const {\n  int msg_flags = MSG_NOSIGNAL;\n  if (isSet(flags_, WriteFlags::CORK)) {\n    // MSG_MORE tells the kernel we have more data to send, so wait for us to\n    // give it the rest of the data rather than immediately sending a partial\n    // frame, even when TCP_NODELAY is enabled.\n    msg_flags |= MSG_MORE;\n  }\n  if (isSet(flags_, WriteFlags::EOR)) {\n    // marks that this is the last byte of a record (response)\n    msg_flags |= MSG_EOR;\n  }\n  return msg_flags;\n}\n\nvoid AsyncIoUringSocket::WriteSqe::processSubmit(\n    struct io_uring_sqe* sqe) noexcept {\n  VLOG(5) << \"write sqe submit \" << this << \" iovs=\" << msg_.msg_iovlen\n          << \" length=\" << totalLength_ << \" ptr=\" << msg_.msg_iov\n          << \" zc=\" << zcOptions_.zeroCopy << \" fd = \" << parent_->usedFd_\n          << \" flags=\" << parent_->mbFixedFileFlags_;\n  if (zcOptions_.zeroCopy) {\n    ::io_uring_prep_sendmsg_zc(\n        sqe, parent_->usedFd_, &msg_, sendMsgFlags() | MSG_WAITALL);\n    if (zcOptions_.fixedBuf) {\n      sqe->ioprio |= IORING_RECVSEND_FIXED_BUF;\n      sqe->buf_index = zcOptions_.fixedBufIndex;\n    }\n  } else {\n    ::io_uring_prep_sendmsg(sqe, parent_->usedFd_, &msg_, sendMsgFlags());\n  }\n  sqe->flags |= parent_->mbFixedFileFlags_;\n}\n\nnamespace {\n\nstruct DetachFdState : AsyncReader::ReadCallback {\n  DetachFdState(\n      AsyncIoUringSocket* s, AsyncDetachFdCallback* cb, NetworkSocket fd)\n      : socket(s), callback(cb), ns(fd) {}\n  AsyncIoUringSocket* socket;\n  AsyncDetachFdCallback* callback;\n  NetworkSocket ns;\n  std::unique_ptr<IOBuf> unread;\n  std::unique_ptr<IOBuf> buffer;\n\n  void done() {\n    socket->setReadCB(nullptr);\n    callback->fdDetached(ns, std::move(unread));\n    delete this;\n  }\n\n  // ReadCallback:\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    if (!buffer) {\n      buffer = IOBuf::create(2000);\n    }\n    *bufReturn = buffer->writableTail();\n    *lenReturn = buffer->tailroom();\n  }\n\n  void readErr(const AsyncSocketException&) noexcept override { done(); }\n  void readEOF() noexcept override { done(); }\n  void readBufferAvailable(std::unique_ptr<IOBuf> buf) noexcept override {\n    if (unread) {\n      unread->appendToChain(std::move(buf));\n    } else {\n      unread = std::move(buf);\n    }\n    if (!socket->readSqeInFlight()) {\n      done();\n    }\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    buffer->append(len);\n    readBufferAvailable(std::move(buffer));\n  }\n  bool isBufferMovable() noexcept override { return true; }\n};\n\nstruct CancelSqe : IoSqeBase {\n  explicit CancelSqe(IoSqeBase* sqe, folly::Function<void()> fn = {})\n      : IoSqeBase(IoSqeBase::Type::Cancel), target_(sqe), fn_(std::move(fn)) {}\n  void processSubmit(struct io_uring_sqe* sqe) noexcept override {\n    ::io_uring_prep_cancel(sqe, target_, 0);\n  }\n  void callback(const io_uring_cqe*) noexcept override {\n    if (fn_) {\n      fn_();\n    }\n    delete this;\n  }\n\n  void callbackCancelled(const io_uring_cqe*) noexcept override {\n    if (fn_) {\n      fn_();\n    }\n    delete this;\n  }\n\n  IoSqeBase* target_;\n  folly::Function<void()> fn_;\n};\n\n} // namespace\n\nvoid AsyncIoUringSocket::asyncDetachFd(AsyncDetachFdCallback* callback) {\n  auto state = new DetachFdState(this, callback, takeFd());\n\n  if (writeSqeActive_) {\n    backend_->cancel(writeSqeActive_);\n    writeSqeActive_->callback_->writeErr(\n        0, AsyncSocketException(AsyncSocketException::UNKNOWN, \"fd detached\"));\n    writeSqeActive_ = nullptr;\n  }\n  while (!writeSqeQueue_.empty()) {\n    auto& f = writeSqeQueue_.front();\n    f.callback_->writeErr(\n        0, AsyncSocketException(AsyncSocketException::UNKNOWN, \"fd detached\"));\n    backend_->cancel(&f);\n    writeSqeQueue_.pop_front();\n  }\n\n  setReadCB(state);\n  if (readSqe_->inFlight()) {\n    backend_->submitNow(*new CancelSqe(readSqe_.get()));\n  } else {\n    state->done();\n  }\n\n  // todo - care about connect? probably doesnt matter as we wont have bad\n  // results (eg wrong read data), just a broken socket\n}\n\nvoid AsyncIoUringSocket::attachEventBase(EventBase* evb) {\n  VLOG(2) << \"AsyncIoUringSocket::attachEventBase(this=\" << this\n          << \") state=\" << stateAsString() << \" isDetaching_=\" << isDetaching_\n          << \" evb=\" << evb;\n  if (!isDetaching_) {\n    throw std::runtime_error(\"bad state for attachEventBase\");\n  }\n  backend_ = getBackendFromEventBase(evb);\n  evb_ = evb;\n  isDetaching_ = false;\n  registerFd();\n  readSqe_->attachEventBase();\n\n  if (writeSqeActive_) {\n    alive_ = std::make_shared<folly::Unit>();\n    std::move(*detachedWriteResult_)\n        .via(evb)\n        .thenValue(\n            [w = writeSqeActive_, a = std::weak_ptr<folly::Unit>(alive_), evb](\n                auto&& resFlagsPairs) {\n              VLOG(5) << \"attached write done, \" << resFlagsPairs.size();\n              if (!a.lock()) {\n                return;\n              }\n\n              io_uring_cqe cqe;\n              for (const auto& [res, flags] : resFlagsPairs) {\n                cqe.res = res;\n                cqe.flags = flags;\n\n                evb->bumpHandlingTime();\n                if (w->cancelled()) {\n                  w->callbackCancelled(&cqe);\n                } else {\n                  w->callback(&cqe);\n                }\n              }\n            });\n  }\n\n  writeTimeout_.attachEventBase(evb);\n  if (state_ == State::Established) {\n    allowReads();\n    processWriteQueue();\n  }\n}\n\nbool AsyncIoUringSocket::isDetachable() const {\n  VLOG(3) << \"AsyncIoUringSocket::isAsyncDetachable(\" << this\n          << \") state=\" << stateAsString();\n  if (fastOpenSqe_ && fastOpenSqe_->inFlight()) {\n    VLOG(3) << \"not detachable: fastopen\";\n    return false;\n  }\n  if (connectSqe_ && connectSqe_->inFlight()) {\n    VLOG(3) << \"not detachable: connect\";\n    return false;\n  }\n  if (closeSqe_ && closeSqe_->inFlight()) {\n    VLOG(3) << \"not detachable: closing\";\n    return false;\n  }\n  if (state_ == State::FastOpen) {\n    VLOG(3) << \"not detachable: fastopen\";\n    return false;\n  }\n  if (state_ == State::Connecting) {\n    return false;\n  }\n  if (writeTimeout_.isScheduled()) {\n    VLOG(3) << \"not detachable: write timeout\";\n    return false;\n  }\n  return true;\n}\n\nnamespace {\n\nstruct DetachReadCallback : AsyncReader::ReadCallback {\n  explicit DetachReadCallback() { buf_ = folly::IOBuf::create(2048); }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = buf_->writableTail();\n    *lenReturn = buf_->tailroom();\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    buf_->append(len);\n    buf_->reserve(0 /* minHeadroom */, 2048 /* minTailroom */);\n  }\n\n  void readErr(const AsyncSocketException&) noexcept override { done(); }\n  void readEOF() noexcept override { done(); }\n  void done() noexcept {\n    VLOG(4) << \"AsyncIoUringSocket::detachReadcallback() this=\" << this\n            << \" done\";\n    prom.setValue(std::move(buf_));\n    delete this;\n  }\n\n  folly::Promise<std::unique_ptr<folly::IOBuf>> prom;\n  std::unique_ptr<folly::IOBuf> buf_;\n};\n\n} // namespace\n\nvoid AsyncIoUringSocket::detachEventBase() {\n  VLOG(4) << \"AsyncIoUringSocket::detachEventBase() this=\" << this\n          << \" readSqeInFlight_=\" << readSqe_->inFlight()\n          << \" detachable=\" << isDetachable();\n  if (!isDetachable()) {\n    throw std::runtime_error(\"not detachable\");\n  }\n  if (isDetaching_) {\n    return;\n  }\n  isDetaching_ = true;\n\n  if (writeSqeActive_) {\n    // it's dangerous to have one sqeBase referred to by two backends, so make a\n    // copy and redirect all the callbacks to the new one.\n    auto det = writeSqeActive_->detachEventBase();\n    writeSqeActive_ = det.second;\n    detachedWriteResult_ = std::move(det.first);\n  }\n  writeTimeout_.detachEventBase();\n\n  DetachReadCallback* drc = nullptr;\n  auto* oldReadCallback = readSqe_->readCallback();\n  folly::Optional<folly::SemiFuture<std::unique_ptr<IOBuf>>> previous;\n  if (readSqe_->inFlight()) {\n    drc = new DetachReadCallback();\n    readSqe_->setReadCallback(drc, false);\n    previous = readSqe_->detachEventBase();\n    backend_->cancel(readSqe_.release());\n  }\n  readSqe_ = ReadSqe::UniquePtr(new ReadSqe(this));\n  readSqe_->setReadCallback(oldReadCallback, false);\n  readSqe_->setEventBase(nullptr);\n  if (fd_ != NetworkSocket()) {\n    try {\n      SocketAddress remoteAddr;\n      getPeerAddress(&remoteAddr);\n      readSqe_->setUseZeroCopyRx(!remoteAddr.isLoopbackAddress());\n    } catch (const std::exception& e) {\n      VLOG(2) << \"Error getting peer address in detachEventBase: \" << e.what();\n    }\n  }\n\n  unregisterFd();\n  if (!drc) {\n    if (previous) {\n      VLOG(4) << \"Setting promise from previous\";\n      readSqe_->setOldEventBaseRead(std::move(*previous));\n    } else {\n      VLOG(4) << \"Not setting promise\";\n    }\n  } else {\n    auto res = drc->prom.getSemiFuture();\n    if (previous) {\n      VLOG(4) << \"Setting promise from previous and this one\";\n      readSqe_->setOldEventBaseRead(\n          std::move(*previous).deferValue(\n              [r = std::move(res)](\n                  std::unique_ptr<folly::IOBuf>&& prevRes) mutable {\n                return std::move(r).deferValue(\n                    [p = std::move(prevRes)](\n                        std::unique_ptr<folly::IOBuf>&& nextRes) mutable {\n                      p->appendToChain(std::move(nextRes));\n                      return std::move(p);\n                    });\n              }));\n    } else {\n      VLOG(4) << \"Setting promise from this one\";\n      readSqe_->setOldEventBaseRead(std::move(res));\n    }\n  }\n  evb_ = nullptr;\n  backend_ = nullptr;\n}\n\nbool AsyncIoUringSocket::ReadSqe::waitingForOldEventBaseRead() const {\n  return oldEventBaseRead_ && !oldEventBaseRead_->isReady();\n}\n\nfolly::Optional<folly::SemiFuture<std::unique_ptr<IOBuf>>>\nAsyncIoUringSocket::ReadSqe::detachEventBase() {\n  alive_ = nullptr;\n  parent_ = nullptr;\n  setEventBase(nullptr);\n  return std::move(oldEventBaseRead_);\n}\n\nvoid AsyncIoUringSocket::ReadSqe::attachEventBase() {\n  VLOG(5) << \"AsyncIoUringSocket::ReadSqe::attachEventBase(this=\" << this\n          << \") parent_=\" << parent_ << \" cb_=\" << readCallback_\n          << \" oldread=\" << !!oldEventBaseRead_ << \" inflight=\" << inFlight();\n\n  if (!parent_) {\n    return;\n  }\n  if (!oldEventBaseRead_) {\n    return;\n  }\n  auto* evb = parent_->evb_;\n  setEventBase(evb);\n  alive_ = std::make_shared<folly::Unit>();\n  folly::Func deferred =\n      [p = parent_, a = std::weak_ptr<folly::Unit>(alive_)]() {\n        if (a.lock()) {\n          p->previousReadDone();\n        } else {\n          VLOG(5) << \"unable to lock for \" << p;\n        }\n      };\n  oldEventBaseRead_ =\n      std::move(*oldEventBaseRead_)\n          .via(evb)\n          .thenValue([d = std::move(deferred), evb](auto&& x) mutable {\n            evb->add(std::move(d));\n            return std::move(x);\n          });\n}\n\nAsyncIoUringSocket::FastOpenSqe::FastOpenSqe(\n    AsyncIoUringSocket* parent,\n    SocketAddress const& addr,\n    std::unique_ptr<WriteSqe> i)\n    : IoSqeBase(IoSqeBase::Type::Open),\n      parent_(parent),\n      initialWrite(std::move(i)) {\n  addrLen_ = addr.getAddress(&addrStorage);\n  setEventBase(parent->evb_);\n}\n\nvoid AsyncIoUringSocket::FastOpenSqe::cleanupMsg() noexcept {\n  initialWrite->msg_.msg_name = nullptr;\n  initialWrite->msg_.msg_namelen = 0;\n}\n\nvoid AsyncIoUringSocket::FastOpenSqe::processSubmit(\n    struct io_uring_sqe* sqe) noexcept {\n  VLOG(5) << \"fastopen sqe submit \" << this\n          << \" iovs=\" << initialWrite->msg_.msg_iovlen\n          << \" length=\" << initialWrite->totalLength_\n          << \" ptr=\" << initialWrite->msg_.msg_iov;\n  initialWrite->processSubmit(sqe);\n  initialWrite->msg_.msg_name = &addrStorage;\n  initialWrite->msg_.msg_namelen = addrLen_;\n  sqe->msg_flags |= MSG_FASTOPEN;\n}\n\nvoid AsyncIoUringSocket::processWriteQueue() noexcept {\n  if (writeSqeQueue_.empty() && !writeSqeActive_ &&\n      shutdownFlags_ & ShutFlags_WritePending) {\n    shutdownWriteNow();\n    return;\n  }\n  if (state_ != State::Established && !connecting()) {\n    failAllWrites();\n    return;\n  }\n  if (writeSqeActive_ || writeSqeQueue_.empty()) {\n    return;\n  }\n  writeSqeActive_ = &writeSqeQueue_.front();\n  writeSqeQueue_.pop_front();\n  doSubmitWrite();\n}\n\nvoid AsyncIoUringSocket::writeDone() noexcept {\n  VLOG(5) << \"AsyncIoUringSocket::writeDone queue=\" << writeSqeQueue_.size()\n          << \" active=\" << writeSqeActive_;\n\n  if (writeTimeoutTime_.count() > 0) {\n    writeTimeout_.cancelTimeout();\n  }\n  processWriteQueue();\n}\n\nvoid AsyncIoUringSocket::doSubmitWrite() noexcept {\n  DCHECK(writeSqeActive_);\n  backend_->submitSoon(*writeSqeActive_);\n  if (writeTimeoutTime_.count() > 0) {\n    startSendTimeout();\n  }\n}\n\nvoid AsyncIoUringSocket::doReSubmitWrite() noexcept {\n  DCHECK(writeSqeActive_);\n  backend_->submitSoon(*writeSqeActive_);\n  // do not update the send timeout for partial writes\n}\n\nvoid AsyncIoUringSocket::failAllWrites() noexcept {\n  while (!writeSqeQueue_.empty()) {\n    WriteSqe* w = &writeSqeQueue_.front();\n    CHECK(!w->inFlight());\n    writeSqeQueue_.pop_front();\n    if (w->callback_) {\n      w->callback_->writeErr(\n          0,\n          AsyncSocketException(\n              AsyncSocketException::INVALID_STATE, \"socket in err state\"));\n    }\n    delete w;\n  }\n}\n\nstd::pair<\n    folly::SemiFuture<std::vector<std::pair<int, uint32_t>>>,\n    AsyncIoUringSocket::WriteSqe*>\nAsyncIoUringSocket::WriteSqe::detachEventBase() {\n  auto [promise, future] =\n      makePromiseContract<std::vector<std::pair<int, uint32_t>>>();\n  auto newSqe =\n      new WriteSqe(parent_, callback_, std::move(buf_), flags_, zcOptions_);\n\n  // make sure to keep the state of where we are in the write\n  newSqe->totalLength_ = totalLength_;\n  newSqe->iov_ = iov_;\n  newSqe->msg_ = msg_;\n  newSqe->refs_ = refs_;\n\n  parent_ = nullptr;\n  setEventBase(nullptr);\n  detachedSignal_ =\n      [prom = std::move(promise),\n       ret = std::vector<std::pair<int, uint32_t>>{},\n       refs = refs_](int res, uint32_t flags) mutable -> bool {\n    ret.emplace_back(res, flags);\n    VLOG(5) << \"DetachedSignal, now refs=\" << refs;\n    if (flags & IORING_CQE_F_NOTIF) {\n      --refs;\n    } else if (!(flags & IORING_CQE_F_MORE)) {\n      --refs;\n    }\n    if (refs == 0) {\n      prom.setValue(std::move(ret));\n      return true;\n    }\n    return false;\n  };\n  return std::make_pair(std::move(future), newSqe);\n}\n\nvoid AsyncIoUringSocket::WriteSqe::callbackCancelled(\n    const io_uring_cqe* cqe) noexcept {\n  auto flags = cqe->flags;\n  VLOG(5) << \"write sqe callback cancelled \" << this << \" flags=\" << flags\n          << \" refs_=\" << refs_ << \" more=\" << !!(flags & IORING_CQE_F_MORE)\n          << \" notif=\" << !!(flags & IORING_CQE_F_NOTIF);\n  if (flags & IORING_CQE_F_MORE) {\n    return;\n  }\n  if (--refs_ <= 0) {\n    delete this;\n  }\n}\n\nvoid AsyncIoUringSocket::WriteSqe::callback(const io_uring_cqe* cqe) noexcept {\n  auto res = cqe->res;\n  auto flags = cqe->flags;\n\n  VLOG(5)\n      << \"write sqe callback \" << this << \" res=\" << res << \" flags=\" << flags\n      << \" iovStart=\" << iov_.size() << \" iovRemaining=\" << iov_.size()\n      << \" length=\" << totalLength_ << \" refs_=\" << refs_\n      << \" more=\" << !!(flags & IORING_CQE_F_MORE)\n      << \" notif=\" << !!(flags & IORING_CQE_F_NOTIF) << \" parent_=\" << parent_;\n\n  if (!parent_) {\n    // parent_ was detached, queue this up and signal.\n    if (detachedSignal_(res, flags)) {\n      VLOG(5) << \"...detachedSignal done\";\n      delete this;\n    }\n    return;\n  }\n\n  if (flags & IORING_CQE_F_MORE) {\n    // still expecting another ref for this\n    ++refs_;\n  }\n\n  if (flags & IORING_CQE_F_NOTIF) {\n    if (--refs_ == 0) {\n      delete this;\n    }\n    return;\n  }\n\n  DestructorGuard dg(parent_);\n\n  if (res > 0 && (size_t)res < totalLength_) {\n    // todo clean out the iobuf\n    size_t toRemove = res;\n    parent_->bytesWritten_ += res;\n    totalLength_ -= toRemove;\n    size_t popFronts = 0;\n    while (toRemove) {\n      if (msg_.msg_iov->iov_len > toRemove) {\n        msg_.msg_iov->iov_len -= toRemove;\n        msg_.msg_iov->iov_base = ((char*)msg_.msg_iov->iov_base) + toRemove;\n        toRemove = 0;\n      } else {\n        toRemove -= msg_.msg_iov->iov_len;\n        if (iov_.size() > kIovMax) {\n          // popping from the front of an iov is slow, so do it in a batch\n          // prefer to do this rather than add a place to stash this\n          // counter in WriteSqe, since this is very unlikely to actually\n          // happen.\n          popFronts++;\n          DCHECK(iov_.size() > popFronts);\n          ++msg_.msg_iov;\n        } else {\n          DCHECK(msg_.msg_iovlen > 1);\n          ++msg_.msg_iov;\n          --msg_.msg_iovlen;\n        }\n      }\n    }\n\n    if (popFronts > 0) {\n      DCHECK(iov_.size() > popFronts);\n      auto it = iov_.begin();\n      std::advance(it, popFronts);\n      iov_.erase(iov_.begin(), it);\n      msg_.msg_iov = iov_.data();\n      msg_.msg_iovlen = std::min<uint32_t>(iov_.size(), kIovMax);\n    }\n\n    // must make inflight false even if MORE is set\n    prepareForReuse();\n\n    // partial write\n    parent_->doReSubmitWrite();\n  } else {\n    if (callback_) {\n      if (res >= 0) {\n        // todo\n        parent_->bytesWritten_ += res;\n        callback_->writeSuccess();\n      } else if (res < 0) {\n        VLOG(2) << \"write error! \" << res;\n        callback_->writeErr(\n            0,\n            AsyncSocketException(AsyncSocketException::UNKNOWN, \"write error\"));\n      }\n    }\n    if (parent_) {\n      parent_->writeSqeActive_ = nullptr;\n      parent_->writeDone();\n    }\n    if (--refs_ == 0) {\n      delete this;\n    }\n  }\n}\n\nvoid AsyncIoUringSocket::failWrite(const AsyncSocketException& ex) {\n  if (!writeSqeActive_) {\n    return;\n  }\n  DestructorGuard dg(this);\n  writeSqeActive_->callback_->writeErr(0, ex);\n  backend_->cancel(writeSqeActive_);\n  writeSqeActive_ = nullptr;\n  writeDone();\n}\n\nvoid AsyncIoUringSocket::write(\n    WriteCallback* callback, const void* buff, size_t n, WriteFlags wf) {\n  // pretty sure that buff cannot change until the write completes\n  writeChain(callback, IOBuf::wrapBuffer(buff, n), wf);\n}\n\nvoid AsyncIoUringSocket::writev(\n    WriteCallback* callback, const iovec* iov, size_t n, WriteFlags wf) {\n  if (n == 0) {\n    callback->writeSuccess();\n    return;\n  }\n  auto first = IOBuf::wrapBuffer(iov[0].iov_base, iov[0].iov_len);\n  for (size_t i = 1; i < n; i++) {\n    first->appendToChain(IOBuf::wrapBuffer(iov[i].iov_base, iov[i].iov_len));\n  }\n  writeChain(callback, std::move(first), wf);\n}\n\nbool AsyncIoUringSocket::canZC(std::unique_ptr<IOBuf> const& buf) const {\n  if (!options_.zeroCopyEnable) {\n    return false;\n  }\n  return (*options_.zeroCopyEnable)(buf);\n}\n\nnamespace {\nstruct NullWriteCallback : AsyncWriter::WriteCallback {\n  void writeSuccess() noexcept override {}\n  void writeErr(size_t, const AsyncSocketException&) noexcept override {}\n\n} sNullWriteCallback;\n\n} // namespace\n\nvoid AsyncIoUringSocket::writeChain(\n    WriteCallback* callback, std::unique_ptr<IOBuf>&& buf, WriteFlags flags) {\n  if ((state_ == State::Closed || state_ == State::Error) && !connecting()) {\n    if (callback) {\n      AsyncSocketException ex(\n          AsyncSocketException::INVALID_STATE,\n          \"trying to write with socket in invalid state\");\n      callback->writeErr(0, ex);\n    }\n    return;\n  }\n  auto canzc = canZC(buf);\n  if (!callback) {\n    callback = &sNullWriteCallback;\n  }\n  WriteSqe::ZeroCopyOptions zcOptions{\n      .zeroCopy = canzc,\n      .fixedBuf = backend_->getArenaIndex() > 0,\n      .fixedBufIndex = 0,\n  };\n  WriteSqe* w = new WriteSqe(this, callback, std::move(buf), flags, zcOptions);\n\n  VLOG(5) << \"AsyncIoUringSocket::writeChain(\" << this\n          << \" ) state=\" << stateAsString() << \" size=\" << w->totalLength_\n          << \" cb=\" << callback << \" fd=\" << fd_ << \" usedFd_ = \" << usedFd_;\n  if (state_ == State::FastOpen && !fastOpenSqe_) {\n    fastOpenSqe_ = std::make_unique<FastOpenSqe>(\n        this, peerAddress_, std::unique_ptr<WriteSqe>(w));\n    backend_->submitSoon(*fastOpenSqe_);\n  } else {\n    writeSqeQueue_.push_back(*w);\n    VLOG(5) << \"enquque \" << w << \" as have active. queue now \"\n            << writeSqeQueue_.size();\n    processWriteQueue();\n  }\n}\n\nnamespace {\n\nclass UnregisterFdSqe : public IoSqeBase {\n public:\n  UnregisterFdSqe(IoUringBackend* b, IoUringFdRegistrationRecord* f)\n      : backend(b), fd(f) {}\n\n  void processSubmit(struct io_uring_sqe* sqe) noexcept override {\n    ::io_uring_prep_nop(sqe);\n  }\n\n  void callback(const io_uring_cqe*) noexcept override {\n    auto start = std::chrono::steady_clock::now();\n    if (!backend->unregisterFd(fd)) {\n      LOG(ERROR) << \"Bad fd unregister\";\n    }\n    auto end = std::chrono::steady_clock::now();\n    if (end - start > std::chrono::milliseconds(1)) {\n      LOG(INFO)\n          << \"unregistering fd took \"\n          << std::chrono::duration_cast<std::chrono::microseconds>(end - start)\n                 .count()\n          << \"us\";\n    }\n    delete this;\n  }\n\n  void callbackCancelled(const io_uring_cqe* cqe) noexcept override {\n    callback(cqe);\n  }\n\n private:\n  IoUringBackend* backend;\n  IoUringFdRegistrationRecord* fd;\n};\n\n} // namespace\n\nvoid AsyncIoUringSocket::unregisterFd() {\n  if (fdRegistered_) {\n    // we have to asynchronously run this in case something wants the fd but has\n    // not been submitted yet. So first do a submit and then unregister\n    // we have to use an async SQE here rather than using the EventBase in case\n    // something cleans up the backend before running.\n    backend_->submitNextLoop(*new UnregisterFdSqe(backend_, fdRegistered_));\n  }\n  fdRegistered_ = nullptr;\n  usedFd_ = fd_.toFd();\n  mbFixedFileFlags_ = 0;\n}\n\nNetworkSocket AsyncIoUringSocket::takeFd() {\n  auto ret = std::exchange(fd_, {});\n  unregisterFd();\n  usedFd_ = -1;\n  return ret;\n}\n\nbool AsyncIoUringSocket::setZeroCopy(bool enable) {\n  if (!enable) {\n    options_.zeroCopyEnable.reset();\n  } else if (!options_.zeroCopyEnable) {\n    options_.zeroCopyEnable = AsyncWriter::ZeroCopyEnableFunc([](auto&&) {\n      return true;\n    });\n  }\n  return true;\n}\n\nbool AsyncIoUringSocket::getZeroCopy() const {\n  return options_.zeroCopyEnable.hasValue();\n}\n\nvoid AsyncIoUringSocket::setZeroCopyEnableFunc(\n    AsyncWriter::ZeroCopyEnableFunc func) {\n  options_.zeroCopyEnable = std::move(func);\n}\n\nvoid AsyncIoUringSocket::closeProcessSubmit(struct io_uring_sqe* sqe) {\n  if (fd_.toFd() >= 0) {\n    ::io_uring_prep_close(sqe, fd_.toFd());\n  } else {\n    // already closed -> nop\n    ::io_uring_prep_nop(sqe);\n  }\n\n  // the fd can be reused from this point\n  takeFd();\n}\n\nvoid AsyncIoUringSocket::closeWithReset() {\n  // copied from AsyncSocket\n  // Enable SO_LINGER, with the linger timeout set to 0.\n  // This will trigger a TCP reset when we close the socket.\n\n  struct linger optLinger = {1, 0};\n  if (::setsockopt(\n          fd_.toFd(), SOL_SOCKET, SO_LINGER, &optLinger, sizeof(optLinger)) !=\n      0) {\n    VLOG(2) << \"AsyncIoUringSocket::closeWithReset(): \"\n            << \"error setting SO_LINGER on \" << fd_ << \": errno=\" << errno;\n  }\n\n  // Then let closeNow() take care of the rest\n  closeNow();\n}\n\nvoid AsyncIoUringSocket::close() {\n  closeNow();\n}\n\nvoid AsyncIoUringSocket::closeNow() {\n  DestructorGuard dg(this);\n  VLOG(2) << \"AsyncIoUringSocket::closeNow() this=\" << this << \" fd_=\" << fd_\n          << \" reg=\" << fdRegistered_ << \" evb_=\" << evb_;\n  if (fdRegistered_) {\n    // we cannot trust that close will actually end the socket, as a\n    // registered socket may be held onto for a while. So always do a shutdown\n    // in case.\n    ::shutdown(fd_.toFd(), SHUT_RDWR);\n  }\n\n  state_ = State::Closed;\n  if (!evb_) {\n    // not attached after detach\n    fileops::close(fd_.toFd());\n    // the fd can be reused from this point\n    takeFd();\n    return;\n  }\n\n  if (closeSqe_) {\n    // todo: we should async close_direct registered fds and then not call\n    // unregister on them\n\n    // we submit and then release for 2 reasons:\n    // 1: we dont want to accidentally clear the closeSqe_ without submitting\n    // 2: we dont want to resubmit, which could close a random fd\n    backend_->submitSoon(*closeSqe_);\n    closeSqe_.release();\n  }\n  if (readSqe_) {\n    ReadCallback* callback = readSqe_->readCallback();\n\n    readSqe_->setReadCallback(nullptr, false);\n    if (callback) {\n      callback->readEOF();\n    }\n  }\n}\n\nvoid AsyncIoUringSocket::sendTimeoutExpired() {\n  VLOG(5) << \"AsyncIoUringSocket::sendTimeoutExpired(this=\" << this\n          << \") connect=\" << !!connectSqe_;\n  if (connectSqe_) {\n    // reused the connect sqe\n    return;\n  }\n  failWrite(\n      AsyncSocketException(AsyncSocketException::TIMED_OUT, \"write timed out\"));\n}\n\nvoid AsyncIoUringSocket::startSendTimeout() {\n  if (!writeTimeout_.scheduleTimeout(writeTimeoutTime_)) {\n    failWrite(AsyncSocketException(\n        AsyncSocketException::INTERNAL_ERROR,\n        \"failed to reschedule send timeout in startSendTimeout\"));\n  }\n}\n\nvoid AsyncIoUringSocket::setSendTimeout(uint32_t ms) {\n  VLOG(5) << \"AsyncIoUringSocket::setSendTimeout(this=\" << this\n          << \") ms=\" << ms;\n  writeTimeoutTime_ = std::chrono::milliseconds{ms};\n  if (evb_) {\n    evb_->dcheckIsInEventBaseThread();\n  }\n\n  if (!writeSqeActive_) {\n    return;\n  }\n  // If we are currently pending on write requests, immediately update\n  // writeTimeout_ with the new value.\n  if (writeTimeoutTime_.count() > 0) {\n    startSendTimeout();\n  } else {\n    writeTimeout_.cancelTimeout();\n  }\n}\n\nvoid AsyncIoUringSocket::getLocalAddress(SocketAddress* address) const {\n  if (!localAddress_.isInitialized()) {\n    localAddress_.setFromLocalAddress(fd_);\n  }\n  *address = localAddress_;\n}\n\nvoid AsyncIoUringSocket::getPeerAddress(SocketAddress* address) const {\n  if (!peerAddress_.isInitialized()) {\n    peerAddress_.setFromPeerAddress(fd_);\n  }\n  *address = peerAddress_;\n}\n\nvoid AsyncIoUringSocket::cacheAddresses() {\n  try {\n    SocketAddress s;\n    getLocalAddress(&s);\n    getPeerAddress(&s);\n  } catch (const std::system_error& e) {\n    VLOG(2) << \"Error caching addresses: \" << e.code().value() << \", \"\n            << e.code().message();\n  }\n}\n\nsize_t AsyncIoUringSocket::getRawBytesReceived() const {\n  return readSqe_->bytesReceived();\n}\n\nint AsyncIoUringSocket::setNoDelay(bool noDelay) {\n  if (fd_ == NetworkSocket()) {\n    VLOG(4) << \"AsyncSocket::setNoDelay() called on non-open socket \" << this;\n    return EINVAL;\n  }\n\n  int value = noDelay ? 1 : 0;\n  if (setSockOpt(IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) != 0) {\n    int errnoCopy = errno;\n    VLOG(2) << \"failed to update TCP_NODELAY option on AsyncSocket \" << this\n            << \" (fd=\" << fd_ << \"): \" << errnoStr(errnoCopy);\n    return errnoCopy;\n  }\n  return 0;\n}\n\nint AsyncIoUringSocket::setSockOpt(\n    int level, int optname, const void* optval, socklen_t optsize) {\n  return ::setsockopt(fd_.toFd(), level, optname, optval, optsize);\n}\n\nbool AsyncIoUringSocket::getTFOSucceeded() const {\n  return detail::tfo_succeeded(fd_);\n}\n\nvoid AsyncIoUringSocket::registerFd() {\n  auto start = std::chrono::steady_clock::now();\n  fdRegistered_ = backend_->registerFd(fd_.toFd());\n  auto end = std::chrono::steady_clock::now();\n  if (end - start > std::chrono::milliseconds(1)) {\n    LOG(INFO)\n        << \"registering fd took \"\n        << std::chrono::duration_cast<std::chrono::microseconds>(end - start)\n               .count()\n        << \"us\";\n  }\n  if (fdRegistered_) {\n    usedFd_ = fdRegistered_->idx_;\n    mbFixedFileFlags_ = IOSQE_FIXED_FILE;\n  } else {\n    usedFd_ = fd_.toFd();\n    VLOG(1) << \"unable to register fd: \" << fd_.toFd();\n  }\n}\n\nvoid AsyncIoUringSocket::setFd(NetworkSocket ns) {\n  fd_ = ns;\n  try {\n    int flags =\n        ensureSocketReturnCode(fcntl(ns.toFd(), F_GETFL, 0), \"get flags\");\n    flags = flags & ~O_NONBLOCK;\n    ensureSocketReturnCode(fcntl(ns.toFd(), F_SETFL, flags), \"set flags\");\n    registerFd();\n  } catch (std::exception const& e) {\n    LOG(ERROR) << \"unable to setFd \" << ns.toFd() << \" : \" << e.what();\n    fileops::close(ns.toFd());\n    throw;\n  }\n  // Only actually enable zero copy receive if the socket is not from loopback.\n  // There is no 'zero copy' for loopback anyway, and issuing recvzc requests\n  // for a loopback socket will always hit the inefficient copy fallback path.\n  // Better to simply issue normal multishot recv.\n  if (readSqe_) {\n    try {\n      SocketAddress remoteAddr;\n      getPeerAddress(&remoteAddr);\n      readSqe_->setUseZeroCopyRx(!remoteAddr.isLoopbackAddress());\n    } catch (const std::exception& e) {\n      VLOG(2) << \"Error getting peer address in setFd: \" << e.what();\n    }\n  }\n}\n\nvoid AsyncIoUringSocket::shutdownWrite() {\n  if (shutdownFlags_ & ShutFlags_Write) {\n    return;\n  }\n  if (writeSqeActive_ || !writeSqeQueue_.empty()) {\n    shutdownFlags_ |= ShutFlags_WritePending;\n  } else {\n    shutdownWriteNow();\n  }\n}\n\nvoid AsyncIoUringSocket::shutdownWriteNow() {\n  if (shutdownFlags_ & ShutFlags_Write) {\n    return;\n  }\n  int ret = ::shutdown(fd_.toFd(), SHUT_WR);\n  if (!ret) {\n    shutdownFlags_ |= ShutFlags_Write;\n    shutdownFlags_ = shutdownFlags_ & ~ShutFlags_WritePending;\n  }\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/AsyncIoUringSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <map>\n#include <memory>\n\n#include <boost/intrusive/list.hpp>\n#include <boost/intrusive/slist.hpp>\n#include <folly/Optional.h>\n#include <folly/SocketAddress.h>\n#include <folly/futures/Future.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/IOBufIovecBuilder.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/IoUringBase.h>\n#include <folly/io/async/Liburing.h>\n#include <folly/net/NetOpsDispatcher.h>\n#include <folly/portability/Sockets.h>\n#include <folly/small_vector.h>\n\nnamespace folly {\nclass AsyncDetachFdCallback {\n public:\n  virtual ~AsyncDetachFdCallback() = default;\n  virtual void fdDetached(\n      NetworkSocket ns, std::unique_ptr<IOBuf> unread) noexcept = 0;\n  virtual void fdDetachFail(const AsyncSocketException& ex) noexcept = 0;\n};\n} // namespace folly\n\n#if FOLLY_HAS_LIBURING\n\nnamespace folly {\nclass IoUringBackend;\nclass IoUringProvidedBufferRing;\n\nclass AsyncIoUringSocket : public AsyncSocketTransport {\n public:\n  using Cert = folly::AsyncTransportCertificate;\n  struct Options {\n    Options()\n        : allocateNoBufferPoolBuffer(defaultAllocateNoBufferPoolBuffer),\n          multishotRecv(true),\n          useBundles(false) {}\n\n    static std::unique_ptr<IOBuf> defaultAllocateNoBufferPoolBuffer();\n    folly::Function<std::unique_ptr<IOBuf>()> allocateNoBufferPoolBuffer;\n    folly::Optional<AsyncWriter::ZeroCopyEnableFunc> zeroCopyEnable;\n    bool multishotRecv;\n    bool useBundles;\n  };\n\n  using UniquePtr = std::unique_ptr<AsyncIoUringSocket, Destructor>;\n  explicit AsyncIoUringSocket(\n      AsyncTransport::UniquePtr other, Options&& options = Options{});\n  explicit AsyncIoUringSocket(AsyncSocket* sock, Options&& options = Options{});\n  explicit AsyncIoUringSocket(EventBase* evb, Options&& options = Options{});\n  explicit AsyncIoUringSocket(\n      EventBase* evb, NetworkSocket ns, Options&& options = Options{});\n\n  static bool supports(EventBase* backend);\n  static bool supportsZcRx(EventBase* backend);\n\n  void connect(\n      AsyncSocket::ConnectCallback* callback,\n      const folly::SocketAddress& address,\n      std::chrono::milliseconds timeout = std::chrono::milliseconds(0),\n      SocketOptionMap const& options = emptySocketOptionMap,\n      const BindOptions& bindOptions = anyAddress(),\n      const std::string& ifName = std::string()) noexcept;\n\n  void connect(\n      ConnectCallback* callback,\n      const folly::SocketAddress& address,\n      int timeout,\n      SocketOptionMap const& options,\n      const BindOptions& bindOptions,\n      const std::string& ifName) noexcept override {\n    connect(\n        callback,\n        address,\n        std::chrono::milliseconds(timeout),\n        options,\n        bindOptions,\n        ifName);\n  }\n\n  std::chrono::nanoseconds getConnectTime() const {\n    return connectEndTime_ - connectStartTime_;\n  }\n\n  void cancelConnect() override;\n\n  /*\n   * This flag controls whether or not IP_BIND_ADDRESS_NO_PORT is enabled for\n   * AsyncSocket sockets. This is enabled by default.\n   */\n  void setBindAddressNoPort(bool flag) { bindAddressNoPort_ = flag; }\n\n  // AsyncSocketBase\n  EventBase* getEventBase() const override { return evb_; }\n\n  // AsyncReader\n  void setReadCB(ReadCallback* callback) override;\n\n  ReadCallback* getReadCallback() const override {\n    return readSqe_->readCallback();\n  }\n  std::unique_ptr<IOBuf> takePreReceivedData() override {\n    return readSqe_->takePreReceivedData();\n  }\n\n  // AsyncWriter\n  void write(WriteCallback*, const void*, size_t, WriteFlags = WriteFlags::NONE)\n      override;\n  void writev(\n      WriteCallback*, const iovec*, size_t, WriteFlags = WriteFlags::NONE)\n      override;\n  void writeChain(\n      WriteCallback* callback,\n      std::unique_ptr<IOBuf>&& buf,\n      WriteFlags flags) override;\n  bool canZC(std::unique_ptr<IOBuf> const& buf) const;\n\n  // AsyncTransport\n  void close() override;\n  void closeNow() override;\n  void closeWithReset() override;\n  void shutdownWrite() override;\n  void shutdownWriteNow() override;\n\n  bool good() const override;\n  bool readable() const override { return good(); }\n  bool error() const override;\n  bool hangup() const override;\n\n  bool connecting() const override {\n    return connectSqe_ && connectSqe_->inFlight();\n  }\n\n  void attachEventBase(EventBase*) override;\n  void detachEventBase() override;\n  bool isDetachable() const override;\n\n  uint32_t getSendTimeout() const override {\n    return static_cast<uint32_t>(\n        std::chrono::duration_cast<std::chrono::milliseconds>(writeTimeoutTime_)\n            .count());\n  }\n\n  void setSendTimeout(uint32_t ms) override;\n\n  void getLocalAddress(SocketAddress* address) const override;\n\n  void getPeerAddress(SocketAddress*) const override;\n\n  void setPreReceivedData(std::unique_ptr<IOBuf> data) override;\n  void cacheAddresses() override;\n\n  /**\n   * @return True iff end of record tracking is enabled\n   */\n  bool isEorTrackingEnabled() const override { return false; }\n\n  void setEorTracking(bool) override {\n    // don't support this.\n    // as far as I can see this is only used by AsyncSSLSocket, but TLS1.3\n    // supercedes this so I think we can ignore it.\n    throw std::runtime_error(\n        \"AsyncIoUringSocket::setEorTracking not supported\");\n  }\n\n  size_t getAppBytesWritten() const override { return getRawBytesWritten(); }\n  size_t getRawBytesWritten() const override { return bytesWritten_; }\n  size_t getAppBytesReceived() const override { return getRawBytesReceived(); }\n  size_t getRawBytesReceived() const override;\n\n  const AsyncTransport* getWrappedTransport() const override { return nullptr; }\n\n  // AsyncSocketTransport\n  int setNoDelay(bool noDelay) override;\n  int setSockOpt(\n      int level, int optname, const void* optval, socklen_t optsize) override;\n\n  std::string getSecurityProtocol() const override { return securityProtocol_; }\n  std::string getApplicationProtocol() const noexcept override {\n    return applicationProtocol_;\n  }\n  NetworkSocket getNetworkSocket() const override { return fd_; }\n\n  void setSecurityProtocol(std::string s) { securityProtocol_ = std::move(s); }\n  void setApplicationProtocol(std::string s) {\n    applicationProtocol_ = std::move(s);\n  }\n\n  const folly::AsyncTransportCertificate* getPeerCertificate() const override {\n    return peerCert_.get();\n  }\n\n  const folly::AsyncTransportCertificate* getSelfCertificate() const override {\n    return selfCert_.get();\n  }\n\n  void dropPeerCertificate() noexcept override { peerCert_.reset(); }\n\n  void dropSelfCertificate() noexcept override { selfCert_.reset(); }\n\n  void setPeerCertificate(const std::shared_ptr<const Cert>& peerCert) {\n    peerCert_ = peerCert;\n  }\n\n  void setSelfCertificate(const std::shared_ptr<const Cert>& selfCert) {\n    selfCert_ = selfCert;\n  }\n\n  void asyncDetachFd(AsyncDetachFdCallback* callback);\n  bool readSqeInFlight() const { return readSqe_->inFlight(); }\n  bool getTFOSucceeded() const override;\n  void enableTFO() override {\n    // No-op if folly does not allow tfo\n#if FOLLY_ALLOW_TFO\n    VLOG(5) << \"AsyncIoUringSocket::enableTFO()\";\n    enableTFO_ = true;\n#endif\n  }\n\n  void appendPreReceive(std::unique_ptr<IOBuf> iobuf) noexcept;\n\n protected:\n  ~AsyncIoUringSocket() override;\n\n private:\n  friend class ReadSqe;\n  friend class WriteSqe;\n  void setFd(NetworkSocket ns);\n  void registerFd();\n  void unregisterFd();\n  void readProcessSubmit(\n      struct io_uring_sqe* sqe,\n      IoUringProvidedBufferRing* bufferProvider,\n      size_t* maxSize,\n      IoUringProvidedBufferRing* usedBufferProvider) noexcept;\n  void readCallback(\n      int res,\n      uint32_t flags,\n      size_t maxSize,\n      IoUringProvidedBufferRing* bufferProvider) noexcept;\n  void allowReads();\n  void previousReadDone();\n  void processWriteQueue() noexcept;\n  void setStateEstablished();\n  void writeDone() noexcept;\n  void doSubmitWrite() noexcept;\n  void doReSubmitWrite() noexcept;\n  void failAllWrites() noexcept;\n  void submitRead(bool now = false);\n  void processConnectSubmit(\n      struct io_uring_sqe* sqe, sockaddr_storage& storage);\n  void processConnectResult(const io_uring_cqe* cqe);\n  void processConnectTimeout();\n  void processFastOpenResult(const io_uring_cqe* cqe) noexcept;\n  void startSendTimeout();\n  void sendTimeoutExpired();\n  void failWrite(const AsyncSocketException& ex);\n  void readEOF();\n  void readError();\n  NetworkSocket takeFd();\n  bool setZeroCopy(bool enable) override;\n  bool getZeroCopy() const override;\n  void setZeroCopyEnableFunc(AsyncWriter::ZeroCopyEnableFunc func) override;\n\n  enum class State {\n    None,\n    Connecting,\n    Established,\n    Closed,\n    Error,\n    FastOpen,\n  };\n\n  static std::string toString(State s);\n  std::string stateAsString() const { return toString(state_); }\n\n  struct ReadSqe : IoSqeBase, DelayedDestruction {\n    using UniquePtr = std::unique_ptr<ReadSqe, Destructor>;\n    explicit ReadSqe(AsyncIoUringSocket* parent);\n    void processSubmit(struct io_uring_sqe* sqe) noexcept override;\n    void callback(const io_uring_cqe* cqe) noexcept override;\n    void callbackCancelled(const io_uring_cqe* cqe) noexcept override;\n\n    void setReadCallback(ReadCallback* callback, bool submitNow);\n    ReadCallback* readCallback() const { return readCallback_; }\n\n    size_t bytesReceived() const { return bytesReceived_; }\n\n    std::unique_ptr<IOBuf> takePreReceivedData();\n    void appendPreReceive(std::unique_ptr<IOBuf> data) noexcept {\n      appendReadData(std::move(data), preReceivedData_);\n    }\n\n    void destroy() override {\n      parent_ = nullptr;\n      DelayedDestruction::destroy();\n    }\n\n    bool waitingForOldEventBaseRead() const;\n    void setOldEventBaseRead(folly::SemiFuture<std::unique_ptr<IOBuf>>&& f) {\n      oldEventBaseRead_ = std::move(f);\n    }\n    void attachEventBase();\n    folly::Optional<folly::SemiFuture<std::unique_ptr<IOBuf>>>\n    detachEventBase();\n\n    void setUseZeroCopyRx(bool val) { useZeroCopyRx_ = val; }\n\n   private:\n    ~ReadSqe() override = default;\n    void appendReadData(\n        std::unique_ptr<IOBuf> data, std::unique_ptr<IOBuf>& overflow) noexcept;\n    void sendReadBuf(\n        std::unique_ptr<IOBuf> buf, std::unique_ptr<IOBuf>& overflow) noexcept;\n    bool readCallbackUseIoBufs() const;\n    void invalidState(ReadCallback* callback);\n    void processOldEventBaseRead();\n\n    bool isEOF(const io_uring_cqe* cqe) noexcept;\n\n    IoUringProvidedBufferRing* lastUsedBufferProvider_;\n    ReadCallback* readCallback_ = nullptr;\n    AsyncIoUringSocket* parent_;\n    size_t maxSize_;\n    uint64_t setReadCbCount_{0};\n    size_t bytesReceived_{0};\n\n    std::unique_ptr<IOBuf> queuedReceivedData_;\n    std::unique_ptr<IOBuf> preReceivedData_;\n    std::unique_ptr<IOBuf> tmpBuffer_;\n    bool supportsMultishotRecv_ =\n        false; // todo: this can be per process instead of per socket\n    bool supportsZeroCopyRx_ = false;\n    bool useZeroCopyRx_ = false;\n    bool useBundles_ = false;\n\n    folly::Optional<folly::SemiFuture<std::unique_ptr<IOBuf>>>\n        oldEventBaseRead_;\n    std::shared_ptr<folly::Unit> alive_;\n  };\n\n  struct CloseSqe : IoSqeBase {\n    explicit CloseSqe(AsyncIoUringSocket* parent)\n        : IoSqeBase(IoSqeBase::Type::Close), parent_(parent) {\n      setEventBase(parent->evb_);\n    }\n\n    void processSubmit(struct io_uring_sqe* sqe) noexcept override {\n      parent_->closeProcessSubmit(sqe);\n    }\n    void callback(const io_uring_cqe*) noexcept override { delete this; }\n    void callbackCancelled(const io_uring_cqe*) noexcept override {\n      delete this;\n    }\n    AsyncIoUringSocket* parent_;\n  };\n\n  struct write_sqe_tag;\n  using write_sqe_hook =\n      boost::intrusive::list_base_hook<boost::intrusive::tag<write_sqe_tag>>;\n  struct WriteSqe final : IoSqeBase, public write_sqe_hook {\n    struct ZeroCopyOptions {\n      bool zeroCopy{false};\n      bool fixedBuf{false};\n      uint16_t fixedBufIndex{0};\n    };\n    explicit WriteSqe(\n        AsyncIoUringSocket* parent,\n        WriteCallback* callback,\n        std::unique_ptr<IOBuf>&& buf,\n        WriteFlags flags,\n        ZeroCopyOptions options);\n    ~WriteSqe() override { VLOG(5) << \"~WriteSqe() \" << this; }\n\n    void processSubmit(struct io_uring_sqe* sqe) noexcept override;\n    void callback(const io_uring_cqe* cqe) noexcept override;\n    void callbackCancelled(const io_uring_cqe* cqe) noexcept override;\n    int sendMsgFlags() const;\n    std::pair<\n        folly::SemiFuture<std::vector<std::pair<int, uint32_t>>>,\n        WriteSqe*>\n    detachEventBase();\n\n    boost::intrusive::list_member_hook<> member_hook_;\n    AsyncIoUringSocket* parent_;\n    WriteCallback* callback_;\n    std::unique_ptr<IOBuf> buf_;\n    WriteFlags flags_;\n    static constexpr size_t kSmallIoVecSize = 16;\n    small_vector<struct iovec, kSmallIoVecSize> iov_;\n    size_t totalLength_;\n    struct msghdr msg_;\n\n    ZeroCopyOptions zcOptions_;\n    int refs_ = 1;\n    folly::Function<bool(int, uint32_t)> detachedSignal_;\n  };\n  using WriteSqeList = boost::intrusive::list<\n      WriteSqe,\n      boost::intrusive::base_hook<write_sqe_hook>,\n      boost::intrusive::constant_time_size<false>>;\n\n  class WriteTimeout : public AsyncTimeout {\n   public:\n    explicit WriteTimeout(AsyncIoUringSocket* socket)\n        : AsyncTimeout(socket->evb_), socket_(socket) {}\n\n    void timeoutExpired() noexcept override { socket_->sendTimeoutExpired(); }\n\n   private:\n    AsyncIoUringSocket* socket_;\n  };\n\n  struct ConnectSqe : IoSqeBase, AsyncTimeout {\n    explicit ConnectSqe(AsyncIoUringSocket* parent)\n        : IoSqeBase(IoSqeBase::Type::Connect),\n          AsyncTimeout(parent->evb_),\n          parent_(parent) {\n      setEventBase(parent->evb_);\n    }\n\n    void processSubmit(struct io_uring_sqe* sqe) noexcept override {\n      parent_->processConnectSubmit(sqe, addrStorage);\n    }\n    void callback(const io_uring_cqe* cqe) noexcept override {\n      parent_->processConnectResult(cqe);\n    }\n    void callbackCancelled(const io_uring_cqe*) noexcept override {\n      delete this;\n    }\n    void timeoutExpired() noexcept override {\n      if (!cancelled()) {\n        parent_->processConnectTimeout();\n      }\n    }\n    AsyncIoUringSocket* parent_;\n    sockaddr_storage addrStorage;\n  };\n\n  struct FastOpenSqe : IoSqeBase {\n    explicit FastOpenSqe(\n        AsyncIoUringSocket* parent,\n        SocketAddress const& addr,\n        std::unique_ptr<AsyncIoUringSocket::WriteSqe> initialWrite);\n    void processSubmit(struct io_uring_sqe* sqe) noexcept override;\n    void cleanupMsg() noexcept;\n    void callback(const io_uring_cqe* cqe) noexcept override {\n      cleanupMsg();\n      parent_->processFastOpenResult(cqe);\n    }\n    void callbackCancelled(const io_uring_cqe*) noexcept override {\n      delete this;\n    }\n\n    AsyncIoUringSocket* parent_;\n    std::unique_ptr<AsyncIoUringSocket::WriteSqe> initialWrite;\n    size_t addrLen_;\n    sockaddr_storage addrStorage;\n  };\n\n  EventBase* evb_ = nullptr;\n  NetworkSocket fd_;\n  IoUringBackend* backend_ = nullptr;\n  Options options_;\n  mutable SocketAddress localAddress_;\n  mutable SocketAddress peerAddress_;\n  IoUringFdRegistrationRecord* fdRegistered_ = nullptr;\n  int usedFd_ = -1;\n  unsigned int mbFixedFileFlags_ = 0;\n  std::unique_ptr<CloseSqe> closeSqe_{new CloseSqe(this)};\n\n  State state_ = State::None;\n\n  // read\n  friend struct DetachFdState;\n  ReadSqe::UniquePtr readSqe_;\n\n  // write\n  std::chrono::milliseconds writeTimeoutTime_{0};\n  WriteTimeout writeTimeout_{this};\n  WriteSqe* writeSqeActive_ = nullptr;\n  WriteSqeList writeSqeQueue_;\n  size_t bytesWritten_{0};\n\n  // connect\n  std::unique_ptr<ConnectSqe> connectSqe_;\n  AsyncSocket::ConnectCallback* connectCallback_;\n  std::chrono::milliseconds connectTimeout_{0};\n  std::chrono::steady_clock::time_point connectStartTime_;\n  std::chrono::steady_clock::time_point connectEndTime_;\n  bool bindAddressNoPort_{true};\n\n  // stopTLS helpers:\n  std::string securityProtocol_;\n  std::string applicationProtocol_;\n\n  std::shared_ptr<const Cert> selfCert_;\n  std::shared_ptr<const Cert> peerCert_;\n\n  // shutdown:\n  int shutdownFlags_ = 0;\n\n  // TCP fast open\n  std::unique_ptr<FastOpenSqe> fastOpenSqe_;\n  bool enableTFO_ = false;\n\n  // detach event base\n  bool isDetaching_ = false;\n  Optional<SemiFuture<std::vector<std::pair<int, uint32_t>>>>\n      detachedWriteResult_;\n  std::shared_ptr<folly::Unit> alive_;\n\n  void closeProcessSubmit(struct io_uring_sqe* sqe);\n};\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/AsyncIoUringSocketFactory.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncIoUringSocketFactory.h>\n\n#include <folly/Conv.h>\n#include <folly/Random.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/portability/Sockets.h>\n\nnamespace folly {\n\nNetworkSocket AsyncIoUringSocketFactory::createBoundSocketForZcRx(\n    [[maybe_unused]] folly::EventBase* evb,\n    [[maybe_unused]] const folly::IPAddress& destAddr,\n    [[maybe_unused]] uint16_t destPort) {\n#if FOLLY_HAS_LIBURING\n  auto* backend = dynamic_cast<folly::IoUringBackend*>(evb->getBackend());\n  if (backend == nullptr) {\n    throw std::runtime_error(\"createBoundSocketForZcRx: not an IoUringBackend\");\n  }\n\n  auto family = destAddr.isV4() ? AF_INET : AF_INET6;\n  int sockfd = ::socket(family, SOCK_STREAM, 0);\n  if (sockfd < 0) {\n    throw std::runtime_error(\n        \"createBoundSocketForZcRx: socket() failed, errno=\" +\n        folly::to<std::string>(errno));\n  }\n\n  // Use non-privileged ports below ip_local_port_range (32768) to avoid\n  // collisions with kernel auto-assigned ephemeral ports.\n  constexpr uint16_t kMinPort = 1024;\n  constexpr uint16_t kMaxPort = 32768;\n  constexpr uint16_t kPortRange = kMaxPort - kMinPort;\n  uint16_t startPort = kMinPort + (folly::Random::rand32() % kPortRange);\n\n  // Find a port that hashes to the target ZC-RX queue and bind to it.\n  // computeSrcPortForQueueId searches the range with wrap-around.\n  // On EADDRINUSE, advance past the failed port and search again.\n  constexpr int kMaxRetries = 1000;\n  for (int retry = 0; retry < kMaxRetries; retry++) {\n    int port = backend->computeSrcPortForQueueId(\n        destAddr, destPort, startPort, kMinPort, kMaxPort);\n    if (port == -1) {\n      break;\n    }\n\n    struct sockaddr_storage bindStorage = {};\n    socklen_t bindLen;\n    if (family == AF_INET6) {\n      auto* sa = reinterpret_cast<struct sockaddr_in6*>(&bindStorage);\n      sa->sin6_family = AF_INET6;\n      sa->sin6_port = htons(static_cast<uint16_t>(port));\n      sa->sin6_addr = in6addr_any;\n      bindLen = sizeof(struct sockaddr_in6);\n    } else {\n      auto* sa = reinterpret_cast<struct sockaddr_in*>(&bindStorage);\n      sa->sin_family = AF_INET;\n      sa->sin_port = htons(static_cast<uint16_t>(port));\n      sa->sin_addr.s_addr = INADDR_ANY;\n      bindLen = sizeof(struct sockaddr_in);\n    }\n\n    if (::bind(\n            sockfd, reinterpret_cast<const sockaddr*>(&bindStorage), bindLen) ==\n        0) {\n      VLOG(2) << \"createBoundSocketForZcRx: bound to srcPort=\" << port\n              << \" for destAddr=\" << destAddr << \", destPort=\" << destPort;\n      return NetworkSocket(sockfd);\n    }\n\n    // EADDRINUSE is expected, advance to find next valid port\n    startPort = static_cast<uint16_t>(port) + 1;\n    if (startPort >= kMaxPort) {\n      startPort = kMinPort;\n    }\n  }\n\n  ::close(sockfd);\n  throw std::runtime_error(\n      \"createBoundSocketForZcRx: no bindable port for destAddr=\" +\n      folly::to<std::string>(destAddr) +\n      \", destPort=\" + folly::to<std::string>(destPort));\n#else\n  throw std::runtime_error(\"createBoundSocketForZcRx: io_uring not supported\");\n#endif\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncIoUringSocketFactory.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncIoUringSocket.h>\n#include <folly/io/async/Liburing.h>\n\nnamespace folly {\n\nclass AsyncIoUringSocketFactory {\n public:\n  static bool supports([[maybe_unused]] folly::EventBase* eb) {\n#if FOLLY_HAS_LIBURING\n    return AsyncIoUringSocket::supports(eb);\n#else\n    return false;\n#endif\n  }\n\n  static bool supportsZcRx([[maybe_unused]] folly::EventBase* eb) {\n#if FOLLY_HAS_LIBURING\n    return AsyncIoUringSocket::supportsZcRx(eb);\n#else\n    return false;\n#endif\n  }\n\n  template <class TWrapper, class... Args>\n  static TWrapper create([[maybe_unused]] Args&&... args) {\n#if FOLLY_HAS_LIBURING\n    return TWrapper(new AsyncIoUringSocket(std::forward<Args>(args)...));\n#else\n    throw std::runtime_error(\"AsyncIoUringSocket not supported\");\n#endif\n  }\n\n  static bool asyncDetachFd(\n      [[maybe_unused]] AsyncTransport& transport,\n      [[maybe_unused]] AsyncDetachFdCallback* callback) {\n#if FOLLY_HAS_LIBURING\n    AsyncIoUringSocket* socket =\n        transport.getUnderlyingTransport<AsyncIoUringSocket>();\n    if (socket) {\n      socket->asyncDetachFd(callback);\n      return true;\n    }\n#endif\n\n    return false;\n  }\n\n  /**\n   * Create a socket bound to a source port that hashes to the ZC-RX queue.\n   * Iterates ports in [1024, 32768) to find one that hashes to the target\n   * queue and is available for binding. The returned socket can be passed as\n   * the BindOptions parameter to connect().\n   *\n   * Returns an invalid NetworkSocket on failure.\n   */\n  static NetworkSocket createBoundSocketForZcRx(\n      folly::EventBase* evb,\n      const folly::IPAddress& destAddr,\n      uint16_t destPort);\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncPipe.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncPipe.h>\n\n#include <folly/FileUtil.h>\n#include <folly/Utility.h>\n#include <folly/detail/FileUtilDetail.h>\n#include <folly/io/async/AsyncSocketException.h>\n\nusing std::string;\nusing std::unique_ptr;\n\nnamespace folly {\n\nAsyncPipeReader::~AsyncPipeReader() {\n  close();\n}\n\nvoid AsyncPipeReader::failRead(const AsyncSocketException& ex) {\n  VLOG(5) << \"AsyncPipeReader(this=\" << this << \", fd=\" << fd_\n          << \"): failed while reading: \" << ex.what();\n\n  DCHECK(readCallback_ != nullptr);\n  AsyncReader::ReadCallback* callback = readCallback_;\n  readCallback_ = nullptr;\n  callback->readErr(ex);\n  close();\n}\n\nvoid AsyncPipeReader::close() {\n  unregisterHandler();\n  if (fd_ != NetworkSocket()) {\n    changeHandlerFD(NetworkSocket());\n\n    if (closeCb_) {\n      closeCb_(fd_);\n    } else {\n      netops::close(fd_);\n    }\n    fd_ = NetworkSocket();\n  }\n}\n\n#ifdef _WIN32\nstatic int recv_internal(NetworkSocket s, void* buf, size_t count) {\n  auto r = netops::recv(s, buf, count, 0);\n  if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {\n    errno = EAGAIN;\n  }\n  return folly::to_narrow(r);\n}\n#endif\n\nvoid AsyncPipeReader::handlerReady(uint16_t events) noexcept {\n  DestructorGuard dg(this);\n  CHECK(events & EventHandler::READ);\n\n  VLOG(5) << \"AsyncPipeReader::handlerReady() this=\" << this << \", fd=\" << fd_;\n  assert(readCallback_ != nullptr);\n\n  while (readCallback_) {\n    // - What API does callback support?\n    const auto movable = readCallback_->isBufferMovable(); // noexcept\n\n    // Get the buffer to read into.\n    void* buf = nullptr;\n    size_t buflen = 0;\n    std::unique_ptr<IOBuf> ioBuf;\n\n    if (movable) {\n      ioBuf = IOBuf::create(readCallback_->maxBufferSize());\n      buf = ioBuf->writableBuffer();\n      buflen = ioBuf->capacity();\n    } else {\n      try {\n        readCallback_->getReadBuffer(&buf, &buflen);\n      } catch (const std::exception& ex) {\n        AsyncSocketException aex(\n            AsyncSocketException::BAD_ARGS,\n            string(\n                \"ReadCallback::getReadBuffer() \"\n                \"threw exception: \") +\n                ex.what());\n        failRead(aex);\n        return;\n      } catch (...) {\n        AsyncSocketException aex(\n            AsyncSocketException::BAD_ARGS,\n            string(\n                \"ReadCallback::getReadBuffer() \"\n                \"threw non-exception type\"));\n        failRead(aex);\n        return;\n      }\n      if (buf == nullptr || buflen == 0) {\n        AsyncSocketException aex(\n            AsyncSocketException::INVALID_STATE,\n            string(\n                \"ReadCallback::getReadBuffer() \"\n                \"returned empty buffer\"));\n        failRead(aex);\n        return;\n      }\n    }\n\n    // Perform the read\n#ifdef _WIN32\n    // On Windows you can't call read on a socket, so call recv instead.\n    ssize_t bytesRead =\n        folly::fileutil_detail::wrapNoInt(recv_internal, fd_, buf, buflen);\n#else\n    ssize_t bytesRead = folly::readNoInt(fd_.toFd(), buf, buflen);\n#endif\n\n    if (bytesRead > 0) {\n      if (movable) {\n        ioBuf->append(std::size_t(bytesRead));\n        readCallback_->readBufferAvailable(std::move(ioBuf));\n      } else {\n        readCallback_->readDataAvailable(size_t(bytesRead));\n      }\n      // Fall through and continue around the loop if the read\n      // completely filled the available buffer.\n      // Note that readCallback_ may have been uninstalled or changed inside\n      // readDataAvailable().\n      if (static_cast<size_t>(bytesRead) < buflen) {\n        return;\n      }\n    } else if (bytesRead < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {\n      // No more data to read right now.\n      return;\n    } else if (bytesRead < 0) {\n      AsyncSocketException ex(\n          AsyncSocketException::INVALID_STATE, \"read failed\", errno);\n      failRead(ex);\n      return;\n    } else {\n      assert(bytesRead == 0);\n      // EOF\n\n      unregisterHandler();\n      AsyncReader::ReadCallback* callback = readCallback_;\n      readCallback_ = nullptr;\n      callback->readEOF();\n      return;\n    }\n    // Max reads per loop?\n  }\n}\n\nvoid AsyncPipeWriter::write(\n    unique_ptr<folly::IOBuf> buf, AsyncWriter::WriteCallback* callback) {\n  if (closed()) {\n    if (callback) {\n      AsyncSocketException ex(\n          AsyncSocketException::NOT_OPEN, \"attempt to write to closed pipe\");\n      callback->writeErr(0, ex);\n    }\n    return;\n  }\n  bool wasEmpty = (queue_.empty());\n  folly::IOBufQueue iobq;\n  iobq.append(std::move(buf));\n  std::pair<folly::IOBufQueue, AsyncWriter::WriteCallback*> p(\n      std::move(iobq), callback);\n  queue_.emplace_back(std::move(p));\n  if (wasEmpty) {\n    handleWrite();\n  } else {\n    CHECK(!queue_.empty());\n    CHECK(isHandlerRegistered());\n  }\n}\n\nvoid AsyncPipeWriter::writeChain(\n    folly::AsyncWriter::WriteCallback* callback,\n    std::unique_ptr<folly::IOBuf>&& buf,\n    WriteFlags) {\n  write(std::move(buf), callback);\n}\n\nvoid AsyncPipeWriter::closeOnEmpty() {\n  VLOG(5) << \"close on empty\";\n  if (queue_.empty()) {\n    closeNow();\n  } else {\n    closeOnEmpty_ = true;\n    CHECK(isHandlerRegistered());\n  }\n}\n\nvoid AsyncPipeWriter::closeNow() {\n  VLOG(5) << \"close now\";\n  if (!queue_.empty()) {\n    failAllWrites(AsyncSocketException(\n        AsyncSocketException::NOT_OPEN, \"closed with pending writes\"));\n  }\n  if (fd_ != NetworkSocket()) {\n    unregisterHandler();\n    changeHandlerFD(NetworkSocket());\n    if (closeCb_) {\n      closeCb_(fd_);\n    } else {\n      netops::close(fd_);\n    }\n    fd_ = NetworkSocket();\n  }\n}\n\nvoid AsyncPipeWriter::failAllWrites(const AsyncSocketException& ex) {\n  DestructorGuard dg(this);\n  while (!queue_.empty()) {\n    // the first entry of the queue could have had a partial write, but needs to\n    // be tracked.\n    if (queue_.front().second) {\n      queue_.front().second->writeErr(0, ex);\n    }\n    queue_.pop_front();\n  }\n}\n\nvoid AsyncPipeWriter::handlerReady(uint16_t events) noexcept {\n  CHECK(events & EventHandler::WRITE);\n\n  handleWrite();\n}\n\n#ifdef _WIN32\nstatic int send_internal(NetworkSocket s, const void* buf, size_t count) {\n  auto r = netops::send(s, buf, count, 0);\n  if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {\n    errno = EAGAIN;\n  }\n  return folly::to_narrow(r);\n}\n#endif\n\nvoid AsyncPipeWriter::handleWrite() {\n  DestructorGuard dg(this);\n  assert(!queue_.empty());\n  do {\n    auto& front = queue_.front();\n    folly::IOBufQueue& curQueue = front.first;\n    DCHECK(!curQueue.empty());\n    // someday, support writev.  The logic for partial writes is a bit complex\n    const IOBuf* head = curQueue.front();\n    CHECK(head->length());\n#ifdef _WIN32\n    // On Windows you can't call write on a socket.\n    ssize_t rc = folly::fileutil_detail::wrapNoInt(\n        send_internal, fd_, head->data(), head->length());\n#else\n    ssize_t rc = folly::writeNoInt(fd_.toFd(), head->data(), head->length());\n#endif\n    if (rc < 0) {\n      if (errno == EAGAIN || errno == EWOULDBLOCK) {\n        // pipe is full\n        VLOG(5) << \"write blocked\";\n        registerHandler(EventHandler::WRITE);\n        return;\n      } else {\n        failAllWrites(AsyncSocketException(\n            AsyncSocketException::INTERNAL_ERROR, \"write failed\", errno));\n        closeNow();\n        return;\n      }\n    } else if (rc == 0) {\n      registerHandler(EventHandler::WRITE);\n      return;\n    }\n    curQueue.trimStart(size_t(rc));\n    if (curQueue.empty()) {\n      auto cb = front.second;\n      queue_.pop_front();\n      if (cb) {\n        cb->writeSuccess();\n      }\n    } else {\n      VLOG(5) << \"partial write blocked\";\n    }\n  } while (!queue_.empty());\n\n  if (closeOnEmpty_) {\n    closeNow();\n  } else {\n    unregisterHandler();\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncPipe.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <list>\n#include <system_error>\n\n#include <folly/io/IOBufQueue.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/EventHandler.h>\n\nnamespace folly {\n\nclass AsyncSocketException;\n\n/**\n * Read from a pipe in an async manner.\n */\nclass AsyncPipeReader\n    : public EventHandler,\n      public AsyncReader,\n      public DelayedDestruction {\n public:\n  using UniquePtr = folly::DelayedDestructionUniquePtr<AsyncPipeReader>;\n\n  static UniquePtr newReader(\n      folly::EventBase* eventBase, NetworkSocket pipeFd) {\n    return UniquePtr(new AsyncPipeReader(eventBase, pipeFd));\n  }\n\n  AsyncPipeReader(folly::EventBase* eventBase, NetworkSocket pipeFd)\n      : EventHandler(eventBase, pipeFd), fd_(pipeFd) {}\n\n  /**\n   * Set the read callback and automatically install/uninstall the handler\n   * for events.\n   */\n  void setReadCB(AsyncReader::ReadCallback* callback) override {\n    if (callback == readCallback_) {\n      return;\n    }\n    readCallback_ = callback;\n    if (readCallback_ && !isHandlerRegistered()) {\n      registerHandler(EventHandler::READ | EventHandler::PERSIST);\n    } else if (!readCallback_ && isHandlerRegistered()) {\n      unregisterHandler();\n    }\n  }\n\n  /**\n   * Get the read callback\n   */\n  AsyncReader::ReadCallback* getReadCallback() const override {\n    return readCallback_;\n  }\n\n  /**\n   * Set a special hook to close the socket (otherwise, will call close())\n   */\n  void setCloseCallback(std::function<void(NetworkSocket)> closeCb) {\n    closeCb_ = closeCb;\n  }\n\n private:\n  ~AsyncPipeReader() override;\n\n  void handlerReady(uint16_t events) noexcept override;\n  void failRead(const AsyncSocketException& ex);\n  void close();\n\n  NetworkSocket fd_;\n  AsyncReader::ReadCallback* readCallback_{nullptr};\n  std::function<void(NetworkSocket)> closeCb_;\n};\n\n/**\n * Write to a pipe in an async manner.\n */\nclass AsyncPipeWriter\n    : public EventHandler,\n      public AsyncWriter,\n      public DelayedDestruction {\n public:\n  using UniquePtr = folly::DelayedDestructionUniquePtr<AsyncPipeWriter>;\n\n  static UniquePtr newWriter(\n      folly::EventBase* eventBase, NetworkSocket pipeFd) {\n    return UniquePtr(new AsyncPipeWriter(eventBase, pipeFd));\n  }\n\n  AsyncPipeWriter(folly::EventBase* eventBase, NetworkSocket pipeFd)\n      : EventHandler(eventBase, pipeFd), fd_(pipeFd) {}\n\n  /**\n   * Asynchronously write the given iobuf to this pipe, and invoke the callback\n   * on success/error.\n   */\n  void write(\n      std::unique_ptr<folly::IOBuf> buf,\n      AsyncWriter::WriteCallback* callback = nullptr);\n\n  /**\n   * Set a special hook to close the socket (otherwise, will call close())\n   */\n  void setCloseCallback(std::function<void(NetworkSocket)> closeCb) {\n    closeCb_ = closeCb;\n  }\n\n  /**\n   * Returns true if the pipe is closed\n   */\n  bool closed() const { return (fd_ == NetworkSocket() || closeOnEmpty_); }\n\n  /**\n   * Notify the pipe to close as soon as all pending writes complete\n   */\n  void closeOnEmpty();\n\n  /**\n   * Close the pipe immediately, and fail all pending writes\n   */\n  void closeNow();\n\n  /**\n   * Return true if there are currently writes pending (eg: the pipe is blocked\n   * for writing)\n   */\n  bool hasPendingWrites() const { return !queue_.empty(); }\n\n  // AsyncWriter methods\n  void write(\n      folly::AsyncWriter::WriteCallback* callback,\n      const void* buf,\n      size_t bytes,\n      WriteFlags flags = WriteFlags::NONE) override {\n    writeChain(callback, IOBuf::wrapBuffer(buf, bytes), flags);\n  }\n  void writev(\n      folly::AsyncWriter::WriteCallback*,\n      const iovec*,\n      size_t,\n      WriteFlags = WriteFlags::NONE) override {\n    throw std::runtime_error(\"writev is not supported. Please use writeChain.\");\n  }\n  void writeChain(\n      folly::AsyncWriter::WriteCallback* callback,\n      std::unique_ptr<folly::IOBuf>&& buf,\n      WriteFlags flags = WriteFlags::NONE) override;\n\n private:\n  void handlerReady(uint16_t events) noexcept override;\n  void handleWrite();\n  void failAllWrites(const AsyncSocketException& ex);\n\n  NetworkSocket fd_;\n  std::list<std::pair<folly::IOBufQueue, AsyncWriter::WriteCallback*>> queue_;\n  bool closeOnEmpty_{false};\n  std::function<void(NetworkSocket)> closeCb_;\n\n  ~AsyncPipeWriter() override { closeNow(); }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSSLSocket.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fmt/format.h>\n#include <folly/io/async/AsyncSSLSocket.h>\n\n#include <cerrno>\n#include <chrono>\n#include <memory>\n#include <utility>\n\n#include <folly/Indestructible.h>\n#include <folly/SocketAddress.h>\n#include <folly/SpinLock.h>\n#include <folly/io/Cursor.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/ssl/BasicTransportCertificate.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/portability/Sockets.h>\n#include <folly/ssl/SSLSession.h>\n#include <folly/ssl/SSLSessionManager.h>\n\nusing std::shared_ptr;\n\nusing folly::SpinLock;\nusing folly::io::Cursor;\nusing folly::ssl::SSLSessionUniquePtr;\n\nnamespace {\nusing folly::AsyncSSLSocket;\nusing folly::SSLContext;\n// For OpenSSL portability API\nusing namespace folly::ssl;\nusing folly::ssl::OpenSSLUtils;\n\n// We have one single dummy SSL context so that we can implement attach\n// and detach methods in a thread safe fashion without modifying openssl.\nSSLContext* dummyCtx = nullptr;\nSpinLock dummyCtxLock;\n\n// If given min write size is less than this, buffer will be allocated on\n// stack, otherwise it is allocated on heap\nconst size_t MAX_STACK_BUF_SIZE = 2048;\n\nchar const* str_or(char const* const str, char const* const def = \"(unknown)\") {\n  return str ? str : def;\n}\n\nvoid setup_SSL_CTX(SSL_CTX* ctx) {\n#ifdef SSL_MODE_RELEASE_BUFFERS\n  SSL_CTX_set_mode(\n      ctx,\n      SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE |\n          SSL_MODE_RELEASE_BUFFERS);\n#else\n  SSL_CTX_set_mode(\n      ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE);\n#endif\n// SSL_CTX_set_mode is a Macro\n#ifdef SSL_MODE_WRITE_IOVEC\n  SSL_CTX_set_mode(ctx, SSL_CTX_get_mode(ctx) | SSL_MODE_WRITE_IOVEC);\n#endif\n}\n\n// Note: This is a Leaky Meyer's Singleton. The reason we can't use a non-leaky\n// thing is because we will be setting this BIO_METHOD* inside BIOs owned by\n// various SSL objects which may get callbacks even during teardown. We may\n// eventually try to fix this\nBIO_METHOD* getSSLBioMethod() {\n  static auto const instance = OpenSSLUtils::newSocketBioMethod().release();\n  return instance;\n}\n\nvoid* initsslBioMethod() {\n  auto sslBioMethod = getSSLBioMethod();\n  // override the bwrite method for MSG_EOR support\n  OpenSSLUtils::setCustomBioWriteMethod(sslBioMethod, AsyncSSLSocket::bioWrite);\n  OpenSSLUtils::setCustomBioReadMethod(sslBioMethod, AsyncSSLSocket::bioRead);\n\n  // Note that the sslBioMethod.type and sslBioMethod.name are not\n  // set here. openssl code seems to be checking \".type == BIO_TYPE_SOCKET\" and\n  // then have specific handlings. The sslWriteBioWrite should be compatible\n  // with the one in openssl.\n\n  // Return something here to enable AsyncSSLSocket to call this method using\n  // a function-scoped static.\n  return nullptr;\n}\n\n} // namespace\n\nnamespace folly {\n\nclass AsyncSSLSocketConnector\n    : public AsyncSocket::ConnectCallback,\n      public AsyncSSLSocket::HandshakeCB {\n private:\n  AsyncSSLSocket* sslSocket_;\n  AsyncSSLSocket::ConnectCallback* callback_;\n  std::chrono::milliseconds timeout_;\n  std::chrono::steady_clock::time_point startTime_;\n\n public:\n  AsyncSSLSocketConnector(\n      AsyncSSLSocket* sslSocket,\n      AsyncSocket::ConnectCallback* callback,\n      std::chrono::milliseconds timeout)\n      : sslSocket_(sslSocket),\n        callback_(callback),\n        timeout_(timeout),\n        startTime_(std::chrono::steady_clock::now()) {}\n\n  ~AsyncSSLSocketConnector() override = default;\n\n  void preConnect(folly::NetworkSocket fd) override {\n    VLOG(7) << \"client preConnect hook is invoked\";\n    if (callback_) {\n      callback_->preConnect(fd);\n    }\n  }\n\n  void connectSuccess() noexcept override {\n    VLOG(7) << \"client socket connected\";\n\n    std::chrono::milliseconds timeoutLeft{0};\n    if (timeout_ > std::chrono::milliseconds::zero()) {\n      auto curTime = std::chrono::steady_clock::now();\n\n      timeoutLeft = std::chrono::duration_cast<std::chrono::milliseconds>(\n          timeout_ - (curTime - startTime_));\n      if (timeoutLeft <= std::chrono::milliseconds::zero()) {\n        AsyncSocketException ex(\n            AsyncSocketException::TIMED_OUT,\n            fmt::format(\"SSL connect timed out after {}ms\", timeout_.count()));\n        fail(ex);\n        delete this;\n        return;\n      }\n    }\n    sslSocket_->sslConn(this, timeoutLeft);\n  }\n\n  void connectErr(const AsyncSocketException& ex) noexcept override {\n    VLOG(1) << \"TCP connect failed: \" << ex.what();\n    fail(ex);\n    delete this;\n  }\n\n  void handshakeSuc(AsyncSSLSocket* /* sock */) noexcept override {\n    VLOG(7) << \"client handshake success\";\n    if (callback_) {\n      callback_->connectSuccess();\n    }\n    delete this;\n  }\n\n  void handshakeErr(\n      AsyncSSLSocket* /* socket */,\n      const AsyncSocketException& ex) noexcept override {\n    VLOG(1) << \"client handshakeErr: \" << ex.what();\n    fail(ex);\n    delete this;\n  }\n\n  void fail(const AsyncSocketException& ex) {\n    // fail is a noop if called twice\n    if (callback_) {\n      AsyncSSLSocket::ConnectCallback* cb = callback_;\n      callback_ = nullptr;\n\n      cb->connectErr(ex);\n      sslSocket_->closeNow();\n      // closeNow can call handshakeErr if it hasn't been called already.\n      // So this may have been deleted, no member variable access beyond this\n      // point\n      // Note that closeNow may invoke writeError callbacks if the socket had\n      // write data pending connection completion.\n    }\n  }\n};\n\nAsyncSSLSocket::AsyncSSLSocket(\n    shared_ptr<const SSLContext> ctx, EventBase* evb, Options&& options)\n    : AsyncSocket(evb),\n      server_{options.isServer},\n      ctx_{std::move(ctx)},\n      certificateIdentityVerifier_{std::move(options.verifier)},\n      handshakeTimeout_{this, evb},\n      connectionTimeout_{this, evb},\n      tlsextHostname_{std::move(options.serverName)} {\n  init();\n  if (options.isServer) {\n    SSL_CTX_set_info_callback(\n        ctx_->getSSLCtx(), AsyncSSLSocket::sslInfoCallback);\n  }\n  if (options.deferSecurityNegotiation) {\n    sslState_ = STATE_UNENCRYPTED;\n  }\n}\n\nAsyncSSLSocket::AsyncSSLSocket(\n    std::shared_ptr<const folly::SSLContext> ctx,\n    AsyncSocket::UniquePtr oldAsyncSocket,\n    Options&& options)\n    : AsyncSocket(std::move(oldAsyncSocket)),\n      server_{options.isServer},\n      ctx_{std::move(ctx)},\n      certificateIdentityVerifier_{std::move(options.verifier)},\n      handshakeTimeout_{this, AsyncSocket::getEventBase()},\n      connectionTimeout_{this, AsyncSocket::getEventBase()},\n      tlsextHostname_{std::move(options.serverName)} {\n  noTransparentTls_ = true;\n  init();\n  if (options.isServer) {\n    SSL_CTX_set_info_callback(\n        ctx_->getSSLCtx(), AsyncSSLSocket::sslInfoCallback);\n  }\n  if (options.deferSecurityNegotiation) {\n    sslState_ = STATE_UNENCRYPTED;\n  }\n}\n\n/**\n * Create a client AsyncSSLSocket\n */\nAsyncSSLSocket::AsyncSSLSocket(\n    shared_ptr<const SSLContext> ctx,\n    EventBase* evb,\n    bool deferSecurityNegotiation)\n    : AsyncSocket(evb),\n      ctx_(std::move(ctx)),\n      handshakeTimeout_(this, evb),\n      connectionTimeout_(this, evb) {\n  init();\n  if (deferSecurityNegotiation) {\n    sslState_ = STATE_UNENCRYPTED;\n  }\n}\n\n/**\n * Create a server/client AsyncSSLSocket\n */\nAsyncSSLSocket::AsyncSSLSocket(\n    shared_ptr<const SSLContext> ctx,\n    EventBase* evb,\n    NetworkSocket fd,\n    bool server,\n    bool deferSecurityNegotiation,\n    const SocketAddress* peerAddress)\n    : AsyncSocket(evb, fd, 0, peerAddress),\n      server_(server),\n      ctx_(std::move(ctx)),\n      handshakeTimeout_(this, evb),\n      connectionTimeout_(this, evb) {\n  noTransparentTls_ = true;\n  init();\n  if (server) {\n    SSL_CTX_set_info_callback(\n        ctx_->getSSLCtx(), AsyncSSLSocket::sslInfoCallback);\n  }\n  if (deferSecurityNegotiation) {\n    sslState_ = STATE_UNENCRYPTED;\n  }\n}\n\nAsyncSSLSocket::AsyncSSLSocket(\n    shared_ptr<const SSLContext> ctx,\n    AsyncSocket* oldAsyncSocket,\n    bool server,\n    bool deferSecurityNegotiation)\n    : AsyncSocket(oldAsyncSocket),\n      server_(server),\n      ctx_(std::move(ctx)),\n      handshakeTimeout_(this, AsyncSocket::getEventBase()),\n      connectionTimeout_(this, AsyncSocket::getEventBase()) {\n  noTransparentTls_ = true;\n  init();\n  if (server) {\n    SSL_CTX_set_info_callback(\n        ctx_->getSSLCtx(), AsyncSSLSocket::sslInfoCallback);\n  }\n  if (deferSecurityNegotiation) {\n    sslState_ = STATE_UNENCRYPTED;\n  }\n}\n\nAsyncSSLSocket::AsyncSSLSocket(\n    shared_ptr<const SSLContext> ctx,\n    AsyncSocket::UniquePtr oldAsyncSocket,\n    bool server,\n    bool deferSecurityNegotiation)\n    : AsyncSSLSocket(\n          ctx, oldAsyncSocket.get(), server, deferSecurityNegotiation) {}\n\n/**\n * Create a client AsyncSSLSocket and allow tlsext_hostname\n * to be sent in Client Hello.\n */\nAsyncSSLSocket::AsyncSSLSocket(\n    const shared_ptr<const SSLContext>& ctx,\n    EventBase* evb,\n    const std::string& serverName,\n    bool deferSecurityNegotiation)\n    : AsyncSSLSocket(ctx, evb, deferSecurityNegotiation) {\n  tlsextHostname_ = serverName;\n}\n\n/**\n * Create a client AsyncSSLSocket from an already connected fd\n * and allow tlsext_hostname to be sent in Client Hello.\n */\nAsyncSSLSocket::AsyncSSLSocket(\n    const shared_ptr<const SSLContext>& ctx,\n    EventBase* evb,\n    NetworkSocket fd,\n    const std::string& serverName,\n    bool deferSecurityNegotiation,\n    const SocketAddress* peerAddress)\n    : AsyncSSLSocket(\n          ctx, evb, fd, false, deferSecurityNegotiation, peerAddress) {\n  tlsextHostname_ = serverName;\n}\n\nAsyncSSLSocket::~AsyncSSLSocket() {\n  VLOG(3) << \"actual destruction of AsyncSSLSocket(this=\" << this << \", evb=\"\n          << eventBase_ << \", fd=\" << fd_ << \", state=\" << int(state_)\n          << \", sslState=\" << sslState_ << \", events=\" << eventFlags_ << \")\";\n}\n\nvoid AsyncSSLSocket::init() {\n  // Do this here to ensure we initialize this once before any use of\n  // AsyncSSLSocket instances and not as part of library load.\n  static const auto sslBioMethodInitializer = initsslBioMethod();\n  (void)sslBioMethodInitializer;\n\n  setup_SSL_CTX(ctx_->getSSLCtx());\n}\n\nvoid AsyncSSLSocket::closeNow() {\n  // Close the SSL connection.\n  if (ssl_ != nullptr && fd_ != NetworkSocket() && !waitingOnAccept_) {\n    int rc = SSL_shutdown(ssl_.get());\n    if (rc == 0) {\n      rc = SSL_shutdown(ssl_.get());\n    }\n    if (rc < 0) {\n      ERR_clear_error();\n    }\n  }\n\n  sslState_ = STATE_CLOSED;\n\n  if (handshakeTimeout_.isScheduled()) {\n    handshakeTimeout_.cancelTimeout();\n  }\n\n  DestructorGuard dg(this);\n\n  static const Indestructible<AsyncSocketException> ex(\n      AsyncSocketException::END_OF_FILE, \"SSL connection closed locally\");\n  invokeHandshakeErr(*ex);\n\n  // Close the socket.\n  AsyncSocket::closeNow();\n}\n\nvoid AsyncSSLSocket::shutdownWrite() {\n  // SSL sockets do not support half-shutdown, so just perform a full shutdown.\n  //\n  // (Performing a full shutdown here is more desirable than doing nothing at\n  // all.  The purpose of shutdownWrite() is normally to notify the other end\n  // of the connection that no more data will be sent.  If we do nothing, the\n  // other end will never know that no more data is coming, and this may result\n  // in protocol deadlock.)\n  close();\n}\n\nvoid AsyncSSLSocket::shutdownWriteNow() {\n  closeNow();\n}\n\nbool AsyncSSLSocket::readable() const {\n  if (ssl_ != nullptr && SSL_pending(ssl_.get()) > 0) {\n    return true;\n  }\n  return AsyncSocket::readable();\n}\n\nbool AsyncSSLSocket::good() const {\n  return (\n      AsyncSocket::good() &&\n      (sslState_ == STATE_ACCEPTING || sslState_ == STATE_CONNECTING ||\n       sslState_ == STATE_ESTABLISHED || sslState_ == STATE_UNENCRYPTED ||\n       sslState_ == STATE_UNINIT));\n}\n\n// The AsyncTransport definition of 'good' states that the transport is\n// ready to perform reads and writes, so sslState_ == UNINIT must report !good.\n// connecting can be true when the sslState_ == UNINIT because the AsyncSocket\n// is connected but we haven't initiated the call to SSL_connect.\nbool AsyncSSLSocket::connecting() const {\n  return (\n      !server_ &&\n      (AsyncSocket::connecting() ||\n       (AsyncSocket::good() &&\n        (sslState_ == STATE_UNINIT || sslState_ == STATE_CONNECTING))));\n}\n\nstd::string AsyncSSLSocket::getApplicationProtocol() const noexcept {\n  const unsigned char* protoName = nullptr;\n  unsigned protoLength;\n  if (getSelectedNextProtocolNoThrow(&protoName, &protoLength)) {\n    return std::string(reinterpret_cast<const char*>(protoName), protoLength);\n  }\n  return \"\";\n}\n\nstd::unique_ptr<IOBuf> AsyncSSLSocket::getExportedKeyingMaterial(\n    folly::StringPiece label,\n    std::unique_ptr<IOBuf> context,\n    uint16_t length) const {\n  if (!ssl_ || sslState_ != STATE_ESTABLISHED) {\n    return nullptr;\n  }\n\n  /*\n   * We would like to only export EKM in the case where the extended master\n   * secret is used (per rfc7627). Note that for TLS1.3 this is by default. For\n   * TLS1.2 this has to be negotiated, so we will check that specifically. The\n   * usage of extended master secret prevents synchronization of master secrets\n   * across sessions.\n   */\n  if (getSSLVersion() < TLS1_2_VERSION) {\n    return nullptr;\n  }\n\n  if (getSSLVersion() == TLS1_2_VERSION && !SSL_get_extms_support(ssl_.get())) {\n    return nullptr;\n  }\n  auto buf = IOBuf::create(length);\n  const unsigned char* contextBuf = nullptr;\n  size_t contextLength = 0;\n  if (context) {\n    auto contextBytes = context->coalesce();\n    contextBuf = contextBytes.data();\n    contextLength = contextBytes.size();\n  }\n\n  if (SSL_export_keying_material(\n          ssl_.get(),\n          buf->writableTail(),\n          (size_t)length,\n          label.data(),\n          label.size(),\n          contextBuf,\n          contextLength,\n          contextBuf != nullptr) != 1) {\n    return nullptr;\n  }\n  buf->append(length);\n\n  return buf;\n}\n\nvoid AsyncSSLSocket::setSupportedApplicationProtocols(\n    const std::vector<std::string>& supportedProtocols) {\n  encodedAlpn_ = OpenSSLUtils::encodeALPNString(supportedProtocols);\n}\n\nvoid AsyncSSLSocket::setEorTracking(bool track) {\n  AsyncSocket::setEorTracking(track);\n}\n\nsize_t AsyncSSLSocket::getRawBytesWritten() const {\n  // The bio(s) in the write path are in a chain\n  // each bio flushes to the next and finally written into the socket\n  // to get the rawBytesWritten on the socket,\n  // get the write bytes of the last bio\n  BIO* b;\n  if (!ssl_ || !(b = SSL_get_wbio(ssl_.get()))) {\n    return rawBytesWritten_;\n  }\n  BIO* next = BIO_next(b);\n  while (next != nullptr) {\n    b = next;\n    next = BIO_next(b);\n  }\n\n  // Raw bytes written should be >= BIO_number_written(b)\n  // Verify no shadowing of rawBytesWritten_\n  DCHECK_GE(AsyncSocket::getRawBytesWritten(), BIO_number_written(b));\n  DCHECK_GE(rawBytesWritten_, BIO_number_written(b));\n  DCHECK_EQ(rawBytesWritten_, AsyncSocket::getRawBytesWritten());\n  return rawBytesWritten_;\n}\n\nsize_t AsyncSSLSocket::getRawBytesReceived() const {\n  BIO* b;\n  if (!ssl_ || !(b = SSL_get_rbio(ssl_.get()))) {\n    return 0;\n  }\n\n  return BIO_number_read(b);\n}\n\nvoid AsyncSSLSocket::invalidState(HandshakeCB* callback) {\n  LOG(ERROR)\n      << \"AsyncSSLSocket(this=\" << this << \", fd=\" << fd_\n      << \", state=\" << int(state_) << \", sslState=\" << sslState_ << \", \"\n      << \"events=\" << eventFlags_ << \", server=\" << short(server_)\n      << \"): \" << \"sslAccept/Connect() called in invalid \"\n      << \"state, handshake callback \" << handshakeCallback_ << \", new callback \"\n      << callback;\n  assert(!handshakeTimeout_.isScheduled());\n  sslState_ = STATE_ERROR;\n\n  static const Indestructible<AsyncSocketException> ex(\n      AsyncSocketException::INVALID_STATE,\n      \"sslAccept() called with socket in invalid state\");\n\n  handshakeEndTime_ = std::chrono::steady_clock::now();\n  if (callback) {\n    callback->handshakeErr(this, *ex);\n  }\n\n  failHandshake(__func__, *ex);\n}\n\nvoid AsyncSSLSocket::sslAccept(\n    HandshakeCB* callback,\n    std::chrono::milliseconds timeout,\n    const SSLContext::SSLVerifyPeerEnum& verifyPeer) {\n  DestructorGuard dg(this);\n  eventBase_->dcheckIsInEventBaseThread();\n  verifyPeer_ = verifyPeer;\n\n  // Make sure we're in the uninitialized state\n  if (!server_ ||\n      (sslState_ != STATE_UNINIT && sslState_ != STATE_UNENCRYPTED) ||\n      handshakeCallback_ != nullptr) {\n    return invalidState(callback);\n  }\n\n  // Cache local and remote socket addresses to keep them available\n  // after socket file descriptor is closed.\n  if (cacheAddrOnFailure_) {\n    cacheAddresses();\n  }\n\n  // AsyncSSLSocket will leak memory if zero copy if left enabled after\n  // the TLS handshake\n  setZeroCopy(false);\n\n  handshakeStartTime_ = std::chrono::steady_clock::now();\n  // Make end time at least >= start time.\n  handshakeEndTime_ = handshakeStartTime_;\n\n  sslState_ = STATE_ACCEPTING;\n  handshakeCallback_ = callback;\n\n  if (timeout > std::chrono::milliseconds::zero()) {\n    handshakeTimeout_.scheduleTimeout(timeout);\n  }\n\n  /* register for a read operation (waiting for CLIENT HELLO) */\n  updateEventRegistration(EventHandler::READ, EventHandler::WRITE);\n\n  checkForImmediateRead();\n}\n\nvoid AsyncSSLSocket::attachSSLContext(\n    const std::shared_ptr<const SSLContext>& ctx) {\n  // Check to ensure we are in client mode. Changing a server's ssl\n  // context doesn't make sense since clients of that server would likely\n  // become confused when the server's context changes.\n  DCHECK(!server_);\n  DCHECK(!ctx_);\n  DCHECK(ctx);\n  DCHECK(ctx->getSSLCtx());\n  ctx_ = ctx;\n\n  // It's possible this could be attached before ssl_ is set up\n  if (!ssl_) {\n    return;\n  }\n\n  // In order to call attachSSLContext, detachSSLContext must have been\n  // previously called.\n  // We need to update the initial_ctx if necessary\n  // The 'initial_ctx' inside an SSL* points to the context that it was created\n  // with, which is also where session callbacks and servername callbacks\n  // happen.\n  // When we switch to a different SSL_CTX, we want to update the initial_ctx as\n  // well so that any callbacks don't go to a different object\n  // NOTE: this will only work if we have access to ssl_ internals, so it may\n  // not work on\n  // OpenSSL version >= 1.1.0\n  auto sslCtx = ctx->getSSLCtx();\n  OpenSSLUtils::setSSLInitialCtx(ssl_.get(), sslCtx);\n  // Detach sets the socket's context to the dummy context. Thus we must acquire\n  // this lock.\n  std::unique_lock guard(dummyCtxLock);\n  SSL_set_SSL_CTX(ssl_.get(), sslCtx);\n}\n\nvoid AsyncSSLSocket::detachSSLContext() {\n  DCHECK(ctx_);\n  ctx_.reset();\n  // It's possible for this to be called before ssl_ has been\n  // set up\n  if (!ssl_) {\n    return;\n  }\n  // The 'initial_ctx' inside an SSL* points to the context that it was created\n  // with, which is also where session callbacks and servername callbacks\n  // happen.\n  // Detach the initial_ctx as well.  It will be reattached in attachSSLContext\n  // it is used for session info.\n  // NOTE: this will only work if we have access to ssl_ internals, so it may\n  // not work on\n  // OpenSSL version >= 1.1.0\n  SSL_CTX* initialCtx = OpenSSLUtils::getSSLInitialCtx(ssl_.get());\n  if (initialCtx) {\n    SSL_CTX_free(initialCtx);\n    OpenSSLUtils::setSSLInitialCtx(ssl_.get(), nullptr);\n  }\n\n  std::unique_lock guard(dummyCtxLock);\n  if (nullptr == dummyCtx) {\n    // We need to lazily initialize the dummy context so we don't\n    // accidentally override any programmatic settings to openssl\n    dummyCtx = new SSLContext;\n  }\n  // We must remove this socket's references to its context right now\n  // since this socket could get passed to any thread. If the context has\n  // had its locking disabled, just doing a set in attachSSLContext()\n  // would not be thread safe.\n  SSL_set_SSL_CTX(ssl_.get(), dummyCtx->getSSLCtx());\n}\n\nvoid AsyncSSLSocket::switchServerSSLContext(\n    const std::shared_ptr<const SSLContext>& handshakeCtx) {\n  CHECK(server_);\n  if (sslState_ != STATE_ACCEPTING) {\n    // We log it here and allow the switch.\n    // It should not affect our re-negotiation support (which\n    // is not supported now).\n    VLOG(6) << \"fd=\" << getNetworkSocket()\n            << \" renegotation detected when switching SSL_CTX\";\n  }\n\n  setup_SSL_CTX(handshakeCtx->getSSLCtx());\n  SSL_CTX_set_info_callback(\n      handshakeCtx->getSSLCtx(), AsyncSSLSocket::sslInfoCallback);\n  handshakeCtx_ = handshakeCtx;\n  SSL_set_SSL_CTX(ssl_.get(), handshakeCtx->getSSLCtx());\n}\n\nbool AsyncSSLSocket::isServerNameMatch() const {\n  CHECK(!server_);\n\n  if (!ssl_) {\n    return false;\n  }\n\n  SSL_SESSION* ss = SSL_get_session(ssl_.get());\n  if (!ss) {\n    return false;\n  }\n\n  auto tlsextHostname = SSL_SESSION_get0_hostname(ss);\n  return (tlsextHostname && !tlsextHostname_.compare(tlsextHostname));\n}\n\nvoid AsyncSSLSocket::setServerName(std::string serverName) noexcept {\n  tlsextHostname_ = std::move(serverName);\n}\n\nvoid AsyncSSLSocket::timeoutExpired(\n    std::chrono::milliseconds timeout) noexcept {\n  if (state_ == StateEnum::ESTABLISHED && sslState_ == STATE_ASYNC_PENDING) {\n    sslState_ = STATE_ERROR;\n    // We are expecting a callback in restartSSLAccept.  The cache lookup\n    // and rsa-call necessarily have pointers to this ssl socket, so delay\n    // the cleanup until he calls us back.\n  } else if (state_ == StateEnum::CONNECTING) {\n    assert(sslState_ == STATE_CONNECTING);\n    DestructorGuard dg(this);\n    static const Indestructible<AsyncSocketException> ex(\n        AsyncSocketException::TIMED_OUT,\n        \"Fallback connect timed out during TFO\");\n    failHandshake(__func__, *ex);\n  } else {\n    assert(\n        state_ == StateEnum::ESTABLISHED &&\n        (sslState_ == STATE_CONNECTING || sslState_ == STATE_ACCEPTING));\n    DestructorGuard dg(this);\n    AsyncSocketException ex(\n        AsyncSocketException::TIMED_OUT,\n        fmt::format(\n            \"SSL {} timed out after {}ms\",\n            (sslState_ == STATE_CONNECTING) ? \"connect\" : \"accept\",\n            timeout.count()));\n    failHandshake(__func__, ex);\n  }\n}\n\nint AsyncSSLSocket::getSSLExDataIndex() {\n  static auto index = SSL_get_ex_new_index(\n      0, (void*)\"AsyncSSLSocket data index\", nullptr, nullptr, nullptr);\n  return index;\n}\n\nAsyncSSLSocket* AsyncSSLSocket::getFromSSL(const SSL* ssl) {\n  return static_cast<AsyncSSLSocket*>(\n      SSL_get_ex_data(ssl, getSSLExDataIndex()));\n}\n\nvoid AsyncSSLSocket::failHandshake(\n    const char* /* fn */, const AsyncSocketException& ex) {\n  startFail();\n  if (handshakeTimeout_.isScheduled()) {\n    handshakeTimeout_.cancelTimeout();\n  }\n  invokeHandshakeErr(ex);\n  finishFail(ex);\n}\n\nvoid AsyncSSLSocket::invokeHandshakeErr(const AsyncSocketException& ex) {\n  handshakeEndTime_ = std::chrono::steady_clock::now();\n  if (handshakeCallback_ != nullptr) {\n    HandshakeCB* callback = handshakeCallback_;\n    handshakeCallback_ = nullptr;\n    callback->handshakeErr(this, ex);\n  }\n}\n\nvoid AsyncSSLSocket::invokeHandshakeCB() {\n  handshakeEndTime_ = std::chrono::steady_clock::now();\n  if (handshakeTimeout_.isScheduled()) {\n    handshakeTimeout_.cancelTimeout();\n  }\n  if (handshakeCallback_) {\n    HandshakeCB* callback = handshakeCallback_;\n    handshakeCallback_ = nullptr;\n    callback->handshakeSuc(this);\n  }\n}\n\nvoid AsyncSSLSocket::connect(\n    ConnectCallback* callback,\n    const folly::SocketAddress& address,\n    int timeout,\n    const SocketOptionMap& options,\n    const BindOptions& bindOptions,\n    const std::string& ifName) noexcept {\n  auto timeoutChrono = std::chrono::milliseconds(timeout);\n  connect(\n      callback,\n      address,\n      timeoutChrono,\n      timeoutChrono,\n      options,\n      bindOptions,\n      ifName);\n}\n\nvoid AsyncSSLSocket::connect(\n    ConnectCallback* callback,\n    const folly::SocketAddress& address,\n    std::chrono::milliseconds connectTimeout,\n    std::chrono::milliseconds totalConnectTimeout,\n    const SocketOptionMap& options,\n    const BindOptions& bindOptions,\n    const std::string& ifName) noexcept {\n  assert(!server_);\n  assert(state_ == StateEnum::UNINIT);\n  assert(sslState_ == STATE_UNINIT || sslState_ == STATE_UNENCRYPTED);\n  noTransparentTls_ = true;\n  totalConnectTimeout_ = totalConnectTimeout;\n  if (sslState_ != STATE_UNENCRYPTED) {\n    allocatedConnectCallback_ =\n        new AsyncSSLSocketConnector(this, callback, totalConnectTimeout);\n    callback = allocatedConnectCallback_;\n  }\n  AsyncSocket::connect(\n      callback,\n      address,\n      int(connectTimeout.count()),\n      options,\n      bindOptions,\n      ifName);\n}\n\nvoid AsyncSSLSocket::cancelConnect() {\n  if (connectCallback_ && allocatedConnectCallback_) {\n    // Since the connect callback won't be called, clean it up.\n    delete allocatedConnectCallback_;\n    allocatedConnectCallback_ = nullptr;\n    connectCallback_ = nullptr;\n  }\n  AsyncSocket::cancelConnect();\n}\n\nbool AsyncSSLSocket::needsPeerVerification() const {\n  if (verifyPeer_ == SSLContext::SSLVerifyPeerEnum::USE_CTX) {\n    return ctx_->needsPeerVerification();\n  }\n  return (\n      verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY ||\n      verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT);\n}\n\nbool AsyncSSLSocket::applyVerificationOptions(const ssl::SSLUniquePtr& ssl) {\n  // apply the settings specified in verifyPeer_\n  if (verifyPeer_ == SSLContext::SSLVerifyPeerEnum::USE_CTX) {\n    if (ctx_->needsPeerVerification()) {\n      if (ctx_->checkPeerName()) {\n        std::string peerNameToVerify = !ctx_->peerFixedName().empty()\n            ? ctx_->peerFixedName()\n            : tlsextHostname_;\n\n        X509_VERIFY_PARAM* param = SSL_get0_param(ssl.get());\n        if (!X509_VERIFY_PARAM_set1_host(\n                param, peerNameToVerify.c_str(), peerNameToVerify.length())) {\n          return false;\n        }\n      }\n\n      SSL_set_verify(\n          ssl.get(),\n          ctx_->getVerificationMode(),\n          AsyncSSLSocket::sslVerifyCallback);\n    }\n  } else {\n    if (verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY ||\n        verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT) {\n      SSL_set_verify(\n          ssl.get(),\n          SSLContext::getVerificationMode(verifyPeer_),\n          AsyncSSLSocket::sslVerifyCallback);\n    }\n  }\n\n  return true;\n}\n\nbool AsyncSSLSocket::setupSSLBio() {\n  auto sslBio = BIO_new(getSSLBioMethod());\n\n  if (!sslBio) {\n    return false;\n  }\n\n  OpenSSLUtils::setBioAppData(sslBio, this);\n  OpenSSLUtils::setBioFd(sslBio, fd_, BIO_NOCLOSE);\n  SSL_set_bio(ssl_.get(), sslBio, sslBio);\n  return true;\n}\n\nvoid AsyncSSLSocket::sslConn(\n    HandshakeCB* callback,\n    std::chrono::milliseconds timeout,\n    const SSLContext::SSLVerifyPeerEnum& verifyPeer) {\n  DestructorGuard dg(this);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  // Cache local and remote socket addresses to keep them available\n  // after socket file descriptor is closed.\n  if (cacheAddrOnFailure_) {\n    cacheAddresses();\n  }\n\n  verifyPeer_ = verifyPeer;\n\n  // Make sure we're in the uninitialized state\n  if (server_ ||\n      (sslState_ != STATE_UNINIT && sslState_ != STATE_UNENCRYPTED) ||\n      handshakeCallback_ != nullptr) {\n    return invalidState(callback);\n  }\n\n  sslState_ = STATE_CONNECTING;\n  handshakeCallback_ = callback;\n\n  createSSL();\n\n  if (!encodedAlpn_.empty()) {\n    int result = SSL_set_alpn_protos(\n        ssl_.get(),\n        reinterpret_cast<const unsigned char*>(encodedAlpn_.c_str()),\n        static_cast<unsigned int>(encodedAlpn_.size()));\n    if (result != 0) {\n      static const Indestructible<AsyncSocketException> ex(\n          AsyncSocketException::INTERNAL_ERROR,\n          \"error setting SSL alpn protos\");\n      return failHandshake(__func__, *ex);\n    }\n  }\n\n  // AsyncSSLSocket will leak memory if zero copy if left enabled after\n  // the TLS handshake\n  setZeroCopy(false);\n\n  SSLSessionUniquePtr sessionPtr = sslSessionManager_.getRawSession();\n  if (sessionPtr) {\n    sessionResumptionAttempted_ = true;\n    SSL_set_session(ssl_.get(), sessionPtr.get());\n  }\n  if (!tlsextHostname_.empty()) {\n    SSL_set_tlsext_host_name(ssl_.get(), tlsextHostname_.c_str());\n  }\n\n  sslSessionManager_.attachToSSL(ssl_.get());\n\n  handshakeConnectTimeout_ = timeout;\n  startSSLConnect();\n}\n\n// This could be called multiple times, during normal ssl connections\n// and after TFO fallback.\nvoid AsyncSSLSocket::startSSLConnect() {\n  handshakeStartTime_ = std::chrono::steady_clock::now();\n  // Make end time at least >= start time.\n  handshakeEndTime_ = handshakeStartTime_;\n  if (handshakeConnectTimeout_ > std::chrono::milliseconds::zero()) {\n    handshakeTimeout_.scheduleTimeout(handshakeConnectTimeout_);\n  }\n  handleConnect();\n}\n\nshared_ptr<ssl::SSLSession> AsyncSSLSocket::getSSLSession() {\n  return sslSessionManager_.getSession();\n}\n\nconst SSL* AsyncSSLSocket::getSSL() const {\n  return ssl_.get();\n}\n\nvoid AsyncSSLSocket::setSSLSession(shared_ptr<ssl::SSLSession> session) {\n  sslSessionManager_.setSession(std::move(session));\n}\n\nvoid AsyncSSLSocket::setRawSSLSession(SSLSessionUniquePtr session) {\n  sslSessionManager_.setRawSession(std::move(session));\n}\n\nvoid AsyncSSLSocket::getSelectedNextProtocol(\n    const unsigned char** protoName, unsigned* protoLen) const {\n  if (!getSelectedNextProtocolNoThrow(protoName, protoLen)) {\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_SUPPORTED, \"ALPN not supported\");\n  }\n}\n\nbool AsyncSSLSocket::getSelectedNextProtocolNoThrow(\n    const unsigned char** protoName, unsigned* protoLen) const {\n  *protoName = nullptr;\n  *protoLen = 0;\n  SSL_get0_alpn_selected(ssl_.get(), protoName, protoLen);\n  return true;\n}\n\nbool AsyncSSLSocket::getSSLSessionReused() const {\n  if (ssl_ != nullptr && sslState_ == STATE_ESTABLISHED) {\n    return SSL_session_reused(ssl_.get());\n  }\n  return false;\n}\n\nconst char* AsyncSSLSocket::getNegotiatedCipherName() const {\n  return (ssl_ != nullptr) ? SSL_get_cipher_name(ssl_.get()) : nullptr;\n}\n\n/* static */\nconst char* AsyncSSLSocket::getSSLServerNameFromSSL(SSL* ssl) {\n  if (ssl == nullptr) {\n    return nullptr;\n  }\n#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB\n  return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);\n#else\n  return nullptr;\n#endif\n}\n\nconst char* AsyncSSLSocket::getSSLServerName() const {\n  if (clientHelloInfo_ && !clientHelloInfo_->clientHelloSNIHostname_.empty()) {\n    return clientHelloInfo_->clientHelloSNIHostname_.c_str();\n  }\n#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB\n  return getSSLServerNameFromSSL(ssl_.get());\n#else\n  throw AsyncSocketException(\n      AsyncSocketException::NOT_SUPPORTED, \"SNI not supported\");\n#endif\n}\n\nconst char* AsyncSSLSocket::getSSLServerNameNoThrow() const {\n  if (clientHelloInfo_ && !clientHelloInfo_->clientHelloSNIHostname_.empty()) {\n    return clientHelloInfo_->clientHelloSNIHostname_.c_str();\n  }\n  return getSSLServerNameFromSSL(ssl_.get());\n}\n\nint AsyncSSLSocket::getSSLVersion() const {\n  return (ssl_ != nullptr) ? SSL_version(ssl_.get()) : 0;\n}\n\nconst char* AsyncSSLSocket::getSSLCertSigAlgName() const {\n  X509* cert = (ssl_ != nullptr) ? SSL_get_certificate(ssl_.get()) : nullptr;\n  if (cert) {\n    int nid = X509_get_signature_nid(cert);\n    return OBJ_nid2ln(nid);\n  }\n  return nullptr;\n}\n\nint AsyncSSLSocket::getSSLCertSize() const {\n  int certSize = 0;\n  X509* cert = (ssl_ != nullptr) ? SSL_get_certificate(ssl_.get()) : nullptr;\n  if (cert) {\n    EVP_PKEY* key = X509_get_pubkey(cert);\n    certSize = EVP_PKEY_bits(key);\n    EVP_PKEY_free(key);\n  }\n  return certSize;\n}\n\nconst AsyncTransportCertificate* AsyncSSLSocket::getPeerCertificate() const {\n  if (peerCertData_) {\n    return peerCertData_.get();\n  }\n  if (ssl_ != nullptr) {\n    auto peerX509 = SSL_get_peer_certificate(ssl_.get());\n    if (peerX509) {\n      // already up ref'd\n      folly::ssl::X509UniquePtr peer(peerX509);\n      auto cn = OpenSSLUtils::getCommonName(peerX509);\n      peerCertData_ = std::make_unique<BasicTransportCertificate>(\n          std::move(cn), std::move(peer));\n    }\n  }\n  return peerCertData_.get();\n}\n\nconst AsyncTransportCertificate* AsyncSSLSocket::getSelfCertificate() const {\n  if (selfCertData_) {\n    return selfCertData_.get();\n  }\n  if (ssl_ != nullptr) {\n    auto selfX509 = SSL_get_certificate(ssl_.get());\n    if (selfX509) {\n      // need to upref\n      X509_up_ref(selfX509);\n      folly::ssl::X509UniquePtr peer(selfX509);\n      auto cn = OpenSSLUtils::getCommonName(selfX509);\n      selfCertData_ = std::make_unique<BasicTransportCertificate>(\n          std::move(cn), std::move(peer));\n    }\n  }\n  return selfCertData_.get();\n}\n\nbool AsyncSSLSocket::willBlock(\n    int ret, int* sslErrorOut, unsigned long* errErrorOut) noexcept {\n  *errErrorOut = 0;\n  int error = *sslErrorOut = sslGetErrorImpl(ssl_.get(), ret);\n  if (error == SSL_ERROR_WANT_READ) {\n    // Register for read event if not already.\n    updateEventRegistration(EventHandler::READ, EventHandler::WRITE);\n    return true;\n  }\n  if (error == SSL_ERROR_WANT_WRITE) {\n    VLOG(3) << \"AsyncSSLSocket(fd=\" << fd_ << \", state=\" << int(state_)\n            << \", sslState=\" << sslState_ << \", events=\" << eventFlags_\n            << \"): \" << \"SSL_ERROR_WANT_WRITE\";\n    // Register for write event if not already.\n    updateEventRegistration(EventHandler::WRITE, EventHandler::READ);\n    return true;\n  }\n  if ((false\n#ifdef SSL_ERROR_WANT_ASYNC // OpenSSL 1.1.0 Async API\n       || error == SSL_ERROR_WANT_ASYNC\n#endif\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\n       || error == SSL_ERROR_WANT_CLIENT_HELLO_CB\n#endif\n       )) {\n    // An asynchronous request has been kicked off. On completion, it will\n    // invoke a callback to re-call handleAccept\n    sslState_ = STATE_ASYNC_PENDING;\n\n    // Unregister for all events while blocked here\n    updateEventRegistration(\n        EventHandler::NONE, EventHandler::READ | EventHandler::WRITE);\n\n#ifdef SSL_ERROR_WANT_ASYNC\n    if (error == SSL_ERROR_WANT_ASYNC) {\n      size_t numfds;\n      if (SSL_get_all_async_fds(ssl_.get(), nullptr, &numfds) <= 0) {\n        VLOG(4) << \"SSL_ERROR_WANT_ASYNC but no async FDs set!\";\n        return false;\n      }\n      if (numfds != 1) {\n        VLOG(4) << \"SSL_ERROR_WANT_ASYNC expected exactly 1 async fd, got \"\n                << numfds;\n        return false;\n      }\n      OSSL_ASYNC_FD ofd; // This should just be an int in POSIX\n      if (SSL_get_all_async_fds(ssl_.get(), &ofd, &numfds) <= 0) {\n        VLOG(4) << \"SSL_ERROR_WANT_ASYNC cant get async fd\";\n        return false;\n      }\n\n      // On POSIX systems, OSSL_ASYNC_FD is type int, but on win32\n      // it has type HANDLE.\n      // Our NetworkSocket::native_handle_type is type SOCKET on\n      // win32, which means that we need to explicitly construct\n      // a native handle type to pass to the constructor.\n      auto native_handle = NetworkSocket::native_handle_type(ofd);\n\n      auto asyncPipeReader =\n          AsyncPipeReader::newReader(eventBase_, NetworkSocket(native_handle));\n      auto asyncPipeReaderPtr = asyncPipeReader.get();\n      if (!asyncOperationFinishCallback_) {\n        asyncOperationFinishCallback_.reset(\n            new DefaultOpenSSLAsyncFinishCallback(\n                std::move(asyncPipeReader), this, DestructorGuard(this)));\n      }\n      asyncPipeReaderPtr->setReadCB(asyncOperationFinishCallback_.get());\n    }\n#endif\n\n    // the timeout (if set) keeps running here\n    return true;\n  } else {\n    // The error queue might contain multiple errors. We only consider the head.\n    // Clear the rest.\n    unsigned long lastError = *errErrorOut = ERR_get_error();\n    ERR_clear_error();\n    VLOG(6)\n        << \"AsyncSSLSocket(fd=\" << fd_ << \", \" << \"state=\" << state_ << \", \"\n        << \"sslState=\" << sslState_ << \", \" << \"events=\" << std::hex\n        << eventFlags_ << \"): \" << \"SSL error: \" << error << \", \"\n        << \"errno: \" << errno << \", \" << \"ret: \" << ret << \", \"\n        << \"read: \" << BIO_number_read(SSL_get_rbio(ssl_.get())) << \", \"\n        << \"written: \" << BIO_number_written(SSL_get_wbio(ssl_.get())) << \", \"\n        << \"func: \" << str_or(ERR_func_error_string(lastError)) << \", \"\n        << \"reason: \" << str_or(ERR_reason_error_string(lastError));\n    return false;\n  }\n}\n\nvoid AsyncSSLSocket::checkForImmediateRead() noexcept {\n  // openssl may have buffered data that it read from the socket already.\n  // In this case we have to process it immediately, rather than waiting for\n  // the socket to become readable again.\n  if (ssl_ != nullptr && SSL_pending(ssl_.get()) > 0) {\n    AsyncSocket::handleRead();\n  } else {\n    AsyncSocket::checkForImmediateRead();\n  }\n}\n\nvoid AsyncSSLSocket::restartSSLAccept() {\n  VLOG(3) << \"AsyncSSLSocket::restartSSLAccept() this=\" << this\n          << \", fd=\" << fd_ << \", state=\" << int(state_) << \", \"\n          << \"sslState=\" << sslState_ << \", events=\" << eventFlags_;\n  DestructorGuard dg(this);\n  assert(\n      sslState_ == STATE_ASYNC_PENDING || sslState_ == STATE_ERROR ||\n      sslState_ == STATE_CLOSED);\n  if (sslState_ == STATE_CLOSED) {\n    // I sure hope whoever closed this socket didn't delete it already,\n    // but this is not strictly speaking an error\n    return;\n  }\n  if (sslState_ == STATE_ERROR) {\n    // go straight to fail if timeout expired during lookup\n    static const Indestructible<AsyncSocketException> ex(\n        AsyncSocketException::TIMED_OUT, \"SSL accept timed out\");\n    failHandshake(__func__, *ex);\n    return;\n  }\n  sslState_ = STATE_ACCEPTING;\n  this->handleAccept();\n}\n\nvoid AsyncSSLSocket::handleAccept() noexcept {\n  VLOG(3) << \"AsyncSSLSocket::handleAccept() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << int(state_) << \", \" << \"sslState=\" << sslState_\n          << \", events=\" << eventFlags_;\n  assert(server_);\n  assert(state_ == StateEnum::ESTABLISHED && sslState_ == STATE_ACCEPTING);\n  if (!ssl_) {\n    /* lazily create the SSL structure */\n    createSSL();\n  }\n\n  if (server_ && parseClientHello_) {\n    SSL_set_msg_callback(\n        ssl_.get(), &AsyncSSLSocket::clientHelloParsingCallback);\n    SSL_set_msg_callback_arg(ssl_.get(), this);\n  }\n\n  DCHECK(ctx_->sslAcceptRunner());\n  updateEventRegistration(\n      EventHandler::NONE, EventHandler::READ | EventHandler::WRITE);\n  DelayedDestruction::DestructorGuard dg(this);\n  ctx_->sslAcceptRunner()->run(\n      [this, dg]() {\n        waitingOnAccept_ = true;\n        return SSL_accept(ssl_.get());\n      },\n      [this, dg](int ret) {\n        waitingOnAccept_ = false;\n        handleReturnFromSSLAccept(ret);\n      });\n}\n\nconst char* AsyncSSLSocket::getNegotiatedGroup() const {\n  auto nid = SSL_get_shared_group(const_cast<SSL*>(this->getSSL()), 0);\n  const char* longname = OBJ_nid2ln((int)nid);\n  return longname;\n}\n\nvoid AsyncSSLSocket::handleReturnFromSSLAccept(int ret) {\n  if (sslState_ != STATE_ACCEPTING) {\n    return;\n  }\n\n  if (ret <= 0) {\n    VLOG(3) << \"SSL_accept returned: \" << ret;\n    int sslError;\n    unsigned long errError;\n    int errnoCopy = errno;\n    if (willBlock(ret, &sslError, &errError)) {\n      return;\n    } else {\n      sslState_ = STATE_ERROR;\n      SSLException ex(sslError, errError, ret, errnoCopy);\n      return failHandshake(__func__, ex);\n    }\n  }\n\n  handshakeComplete_ = true;\n  updateEventRegistration(0, EventHandler::READ | EventHandler::WRITE);\n\n  // Move into STATE_ESTABLISHED in the normal case that we are in\n  // STATE_ACCEPTING.\n  sslState_ = STATE_ESTABLISHED;\n\n  VLOG(3) << \"AsyncSSLSocket \" << this << \": fd \" << fd_\n          << \" successfully accepted; state=\" << int(state_)\n          << \", sslState=\" << sslState_ << \", events=\" << eventFlags_;\n\n  // Remember the EventBase we are attached to, before we start invoking any\n  // callbacks (since the callbacks may call detachEventBase()).\n  EventBase* originalEventBase = eventBase_;\n\n  // Call the accept callback.\n  invokeHandshakeCB();\n\n  // Note that the accept callback may have changed our state.\n  // (set or unset the read callback, called write(), closed the socket, etc.)\n  // The following code needs to handle these situations correctly.\n  //\n  // If the socket has been closed, readCallback_ and writeReqHead_ will\n  // always be nullptr, so that will prevent us from trying to read or write.\n  //\n  // The main thing to check for is if eventBase_ is still originalEventBase.\n  // If not, we have been detached from this event base, so we shouldn't\n  // perform any more operations.\n  if (eventBase_ != originalEventBase) {\n    return;\n  }\n\n  AsyncSocket::handleInitialReadWrite();\n}\n\nvoid AsyncSSLSocket::handleConnect() noexcept {\n  VLOG(3) << \"AsyncSSLSocket::handleConnect() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << int(state_) << \", \" << \"sslState=\" << sslState_\n          << \", events=\" << eventFlags_;\n  assert(!server_);\n  if (state_ < StateEnum::ESTABLISHED) {\n    return AsyncSocket::handleConnect();\n  }\n\n  assert(\n      (state_ == StateEnum::FAST_OPEN || state_ == StateEnum::ESTABLISHED) &&\n      sslState_ == STATE_CONNECTING);\n  assert(ssl_);\n\n  auto originalState = state_;\n  int ret;\n  {\n    // If openssl is not built with TSAN then we can get a TSAN false positive\n    // when calling SSL_connect from multiple threads.\n    folly::annotate_ignore_thread_sanitizer_guard g(__FILE__, __LINE__);\n    ret = SSL_connect(ssl_.get());\n  }\n  if (ret <= 0) {\n    int sslError;\n    unsigned long errError;\n    int errnoCopy = errno;\n    if (willBlock(ret, &sslError, &errError)) {\n      // We fell back to connecting state due to TFO\n      if (state_ == StateEnum::CONNECTING) {\n        DCHECK_EQ(StateEnum::FAST_OPEN, originalState);\n        if (handshakeTimeout_.isScheduled()) {\n          handshakeTimeout_.cancelTimeout();\n        }\n      }\n      return;\n    } else {\n      sslState_ = STATE_ERROR;\n      SSLException ex(sslError, errError, ret, errnoCopy);\n      return failHandshake(__func__, ex);\n    }\n  }\n\n  handshakeComplete_ = true;\n  updateEventRegistration(0, EventHandler::READ | EventHandler::WRITE);\n\n  // Move into STATE_ESTABLISHED in the normal case that we are in\n  // STATE_CONNECTING.\n  sslState_ = STATE_ESTABLISHED;\n\n  VLOG(3) << \"AsyncSSLSocket \" << this << \": \" << \"fd \" << fd_\n          << \" successfully connected; \" << \"state=\" << int(state_)\n          << \", sslState=\" << sslState_ << \", events=\" << eventFlags_;\n\n  // Remember the EventBase we are attached to, before we start invoking any\n  // callbacks (since the callbacks may call detachEventBase()).\n  EventBase* originalEventBase = eventBase_;\n\n  // Call the handshake callback.\n  invokeHandshakeCB();\n\n  // Note that the connect callback may have changed our state.\n  // (set or unset the read callback, called write(), closed the socket, etc.)\n  // The following code needs to handle these situations correctly.\n  //\n  // If the socket has been closed, readCallback_ and writeReqHead_ will\n  // always be nullptr, so that will prevent us from trying to read or write.\n  //\n  // The main thing to check for is if eventBase_ is still originalEventBase.\n  // If not, we have been detached from this event base, so we shouldn't\n  // perform any more operations.\n  if (eventBase_ != originalEventBase) {\n    return;\n  }\n\n  AsyncSocket::handleInitialReadWrite();\n}\n\nvoid AsyncSSLSocket::invokeConnectErr(const AsyncSocketException& ex) {\n  connectionTimeout_.cancelTimeout();\n  AsyncSocket::invokeConnectErr(ex);\n  if (sslState_ == SSLStateEnum::STATE_CONNECTING) {\n    if (handshakeTimeout_.isScheduled()) {\n      handshakeTimeout_.cancelTimeout();\n    }\n    // If we fell back to connecting state during TFO and the connection\n    // failed, it would be an SSL failure as well.\n    invokeHandshakeErr(ex);\n  }\n}\n\nvoid AsyncSSLSocket::invokeConnectSuccess() {\n  connectionTimeout_.cancelTimeout();\n  if (sslState_ == SSLStateEnum::STATE_CONNECTING) {\n    assert(tfoInfo_.attempted);\n    // If we failed TFO, we'd fall back to trying to connect the socket,\n    // to setup things like timeouts.\n    startSSLConnect();\n  }\n  // still invoke the base class since it re-sets the connect time.\n  AsyncSocket::invokeConnectSuccess();\n}\n\nvoid AsyncSSLSocket::scheduleConnectTimeout() {\n  if (sslState_ == SSLStateEnum::STATE_CONNECTING) {\n    // We fell back from TFO, and need to set the timeouts.\n    // We will not have a connect callback in this case, thus if the timer\n    // expires we would have no-one to notify.\n    // Thus we should reset even the connect timers to point to the handshake\n    // timeouts.\n    assert(connectCallback_ == nullptr);\n    // We use a different connect timeout here than the handshake timeout, so\n    // that we can disambiguate the 2 timers.\n    if (connectTimeout_.count() > 0) {\n      if (!connectionTimeout_.scheduleTimeout(connectTimeout_)) {\n        throw AsyncSocketException(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"failed to schedule AsyncSSLSocket connect timeout\"));\n      }\n    }\n    return;\n  }\n  AsyncSocket::scheduleConnectTimeout();\n}\n\nvoid AsyncSSLSocket::handleRead() noexcept {\n  VLOG(5) << \"AsyncSSLSocket::handleRead() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << int(state_) << \", \" << \"sslState=\" << sslState_\n          << \", events=\" << eventFlags_;\n  if (state_ < StateEnum::ESTABLISHED) {\n    return AsyncSocket::handleRead();\n  }\n\n  if (sslState_ == STATE_ACCEPTING) {\n    assert(server_);\n    handleAccept();\n    return;\n  } else if (sslState_ == STATE_CONNECTING) {\n    assert(!server_);\n    handleConnect();\n    return;\n  }\n\n  // Normal read\n  AsyncSocket::handleRead();\n}\n\nAsyncSocket::ReadResult AsyncSSLSocket::performReadSingle(\n    void* buf, const size_t buflen) {\n  VLOG(4) << \"AsyncSSLSocket::performReadSingle() this=\" << this\n          << \", buf=\" << buf << \", buflen=\" << buflen;\n\n  // Integration with ancillary data would have to be implemented in\n  // `bioRead`, and the data then plumbed out via the outer `msghdr`.\n  DCHECK(readAncillaryDataCallback_ == nullptr);\n\n  int numToRead = 0;\n  if (buflen > std::numeric_limits<int>::max()) {\n    numToRead = std::numeric_limits<int>::max();\n    VLOG(4) << \"Clamping SSL_read to \" << numToRead;\n  } else {\n    numToRead = int(buflen);\n  }\n  int bytes = SSL_read(ssl_.get(), buf, numToRead);\n\n  if (server_ && renegotiateAttempted_) {\n    LOG(ERROR) << \"AsyncSSLSocket(fd=\" << fd_ << \", state=\" << int(state_)\n               << \", sslstate=\" << sslState_ << \", events=\" << eventFlags_\n               << \"): client intitiated SSL renegotiation not permitted\";\n    return ReadResult(\n        READ_ERROR,\n        std::make_unique<SSLException>(SSLError::CLIENT_RENEGOTIATION));\n  }\n  if (bytes <= 0) {\n    int error = sslGetErrorImpl(ssl_.get(), bytes);\n    if (error == SSL_ERROR_WANT_READ) {\n      // The caller will register for read event if not already.\n      if (errno == EWOULDBLOCK || errno == EAGAIN) {\n        return ReadResult(READ_BLOCKING);\n      } else {\n        return ReadResult(READ_ERROR);\n      }\n    } else if (error == SSL_ERROR_WANT_WRITE) {\n      // TODO: Even though we are attempting to read data, SSL_read() may\n      // need to write data if renegotiation is being performed.  We currently\n      // don't support this and just fail the read.\n      LOG(ERROR) << \"AsyncSSLSocket(fd=\" << fd_ << \", state=\" << int(state_)\n                 << \", sslState=\" << sslState_ << \", events=\" << eventFlags_\n                 << \"): unsupported SSL renegotiation during read\";\n      return ReadResult(\n          READ_ERROR,\n          std::make_unique<SSLException>(SSLError::INVALID_RENEGOTIATION));\n    } else {\n      if (error == SSL_ERROR_ZERO_RETURN) {\n        // Peer has closed the connection for writing by sending the\n        // close_notify alert. The underlying transport might not be closed, but\n        // assume it is and return EOF.\n        VLOG(6)\n            << \"AsyncSSLSocket(fd=\" << fd_ << \", \" << \"state=\" << state_ << \", \"\n            << \"sslState=\" << sslState_ << \", \" << \"events=\" << std::hex\n            << eventFlags_ << \"): \" << \"bytes: \" << bytes << \", \"\n            << \"error: \" << error << \", \" << \"received close_notify alert\";\n        // AsyncSSLSocket interprets this as a READ_EOF.\n        return ReadResult(0);\n      }\n      int local_errno = errno;\n#ifdef _WIN32\n      // On windows, the underlying TCP socket may error with this code\n      // if the sending/receiving client crashes or is killed.\n      if (error == SSL_ERROR_SYSCALL && local_errno == WSAECONNRESET) {\n        return ReadResult(0);\n      }\n#endif\n      // NOTE: OpenSSL has a bug where SSL_ERROR_SYSCALL and errno 0 indicates\n      // an unexpected EOF from the peer. This will be changed in OpenSSL 3.0\n      // and reported as SSL_ERROR_SSL with reason\n      // SSL_R_UNEXPECTED_EOF_WHILE_READING. We should then explicitly check for\n      // that. See https://www.openssl.org/docs/man1.1.1/man3/SSL_get_error.html\n      if (error == SSL_ERROR_SYSCALL && local_errno == 0) {\n        // ignore anything else in the error queue\n        ERR_clear_error();\n        // intentionally returning EOF\n        return ReadResult(0);\n      }\n\n      // The error queue might contain multiple errors. We only consider and\n      // return the head. Clear the rest.\n      auto errError = ERR_get_error();\n      ERR_clear_error();\n      VLOG(6)\n          << \"AsyncSSLSocket(fd=\" << fd_ << \", \" << \"state=\" << state_ << \", \"\n          << \"sslState=\" << sslState_ << \", \" << \"events=\" << std::hex\n          << eventFlags_ << \"): \" << \"bytes: \" << bytes << \", \"\n          << \"error: \" << error << \", \" << \"errno: \" << local_errno << \", \"\n          << \"func: \" << str_or(ERR_func_error_string(errError)) << \", \"\n          << \"reason: \" << str_or(ERR_reason_error_string(errError));\n      return ReadResult(\n          READ_ERROR,\n          std::make_unique<SSLException>(error, errError, bytes, local_errno));\n    }\n  } else {\n    appBytesReceived_ += bytes;\n    return ReadResult(bytes);\n  }\n}\n\nAsyncSocket::ReadResult AsyncSSLSocket::performReadMsg(\n    struct ::msghdr& msg, AsyncReader::ReadCallback::ReadMode readMode) {\n  if (sslState_ == STATE_UNENCRYPTED) {\n    return AsyncSocket::performReadMsg(msg, readMode);\n  }\n\n  if (readMode == AsyncReader::ReadCallback::ReadMode::ReadBuffer) {\n    // FIXME: The test `AsyncSSLSocketTest.SendMsgParamsCallback` will break\n    // if we remove this branch, because:\n    //  - The loop below to fill multiple iovecs attempts reads until\n    //    `performReadSingle` returns 0.\n    //  - When the \"0 bytes read\" is due to an EOF condition, this final\n    //    read has the side effect of resetting the internal OpenSSL\n    //    error state to `SSL_ERROR_ZERO_RETURN`.\n    //  - The test instead wants to see the error from the failed write\n    //    attempt (`SSL_ERROR_SYSCALL` with errno of `EINVAL`), but\n    //    but the performance-oriented loop below clobbers the correct\n    //    error code and the test fails.\n    // So the only point of this branch is to fall back to the legacy\n    // behavior of `ReadBuffer`, which is to attempt a single SSL read.\n    //\n    // Per @knekritz, it would not be acceptable for the below loop to exit\n    // when `performReadSingle` fails to fill the buffer, even though this\n    // would avoid the second read that returns 0 bytes.  That would cause a\n    // perf regression because `SSL_read` will return at most one TLS\n    // record.  But, there can be more data in the socket buffer that will\n    // be returned on subsequent calls. See D43648653.\n    auto* buf = msg.msg_iov[0].iov_base;\n    auto bufLen = msg.msg_iov[0].iov_len;\n    // Ignores `msg_name*` but that's null in today's `AsyncSocket` anyway.\n    return performReadSingle(buf, bufLen);\n  }\n\n  ssize_t totalRead = 0;\n  ssize_t res = 1;\n  // `msg_iovlen` is `int` on MacOS :(\n  for (size_t i = 0; i < (size_t)msg.msg_iovlen && res > 0; i++) {\n    auto* buf = msg.msg_iov[i].iov_base;\n    auto bufLen = msg.msg_iov[i].iov_len;\n    while (bufLen > 0 && res > 0) {\n      auto readRes = performReadSingle(buf, bufLen);\n      res = readRes.readReturn;\n      if (res > 0) {\n        CHECK_GE(bufLen, res);\n        buf = static_cast<uint8_t*>(buf) + res;\n        bufLen -= res;\n        totalRead += res;\n      } else if (ReadResultEnum(res) == READ_ERROR) {\n        return readRes;\n      } else if (ReadResultEnum(res) == READ_BLOCKING) {\n        if (totalRead > 0) {\n          return ReadResult(totalRead);\n        } else {\n          return readRes;\n        }\n      }\n    }\n  }\n  return ReadResult(totalRead);\n}\n\nvoid AsyncSSLSocket::handleWrite() noexcept {\n  VLOG(5) << \"AsyncSSLSocket::handleWrite() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << int(state_) << \", \" << \"sslState=\" << sslState_\n          << \", events=\" << eventFlags_;\n  if (state_ < StateEnum::ESTABLISHED) {\n    return AsyncSocket::handleWrite();\n  }\n\n  if (sslState_ == STATE_ACCEPTING) {\n    assert(server_);\n    handleAccept();\n    return;\n  }\n\n  if (sslState_ == STATE_CONNECTING) {\n    assert(!server_);\n    handleConnect();\n    return;\n  }\n\n  // Normal write\n  AsyncSocket::handleWrite();\n}\n\nAsyncSocket::WriteResult AsyncSSLSocket::interpretSSLError(int rc, int error) {\n  if (error == SSL_ERROR_WANT_READ) {\n    // Even though we are attempting to write data, SSL_write() may\n    // need to read data if renegotiation is being performed.  We currently\n    // don't support this and just fail the write.\n    LOG(ERROR) << \"AsyncSSLSocket(fd=\" << fd_ << \", state=\" << int(state_)\n               << \", sslState=\" << sslState_ << \", events=\" << eventFlags_\n               << \"): \" << \"unsupported SSL renegotiation during write\";\n    return WriteResult(\n        WRITE_ERROR,\n        std::make_unique<SSLException>(SSLError::INVALID_RENEGOTIATION));\n  } else {\n    // The error queue might contain multiple errors. We only consider and\n    // return the head. Clear the rest.\n    auto errError = ERR_get_error();\n    ERR_clear_error();\n    VLOG(3) << \"ERROR: AsyncSSLSocket(fd=\" << fd_ << \", state=\" << int(state_)\n            << \", sslState=\" << sslState_ << \", events=\" << eventFlags_\n            << \"): \" << \"SSL error: \" << error << \", errno: \" << errno\n            << \", func: \" << str_or(ERR_func_error_string(errError))\n            << \", reason: \" << str_or(ERR_reason_error_string(errError));\n    return WriteResult(\n        WRITE_ERROR,\n        std::make_unique<SSLException>(error, errError, rc, errno));\n  }\n}\n\nAsyncSocket::WriteResult AsyncSSLSocket::performWrite(\n    const iovec* vec,\n    uint32_t count,\n    WriteFlags flags,\n    uint32_t* countWritten,\n    uint32_t* partialWritten,\n    WriteRequestTag writeTag) {\n  if (sslState_ == STATE_UNENCRYPTED) {\n    return AsyncSocket::performWrite(\n        vec, count, flags, countWritten, partialWritten, std::move(writeTag));\n  }\n  if (sslState_ != STATE_ESTABLISHED) {\n    LOG(ERROR)\n        << \"AsyncSSLSocket(fd=\" << fd_ << \", state=\" << int(state_)\n        << \", sslState=\" << sslState_ << \", events=\" << eventFlags_\n        << \"): \" << \"TODO: AsyncSSLSocket currently does not support calling \"\n        << \"write() before the handshake has fully completed\";\n    return WriteResult(\n        WRITE_ERROR, std::make_unique<SSLException>(SSLError::EARLY_WRITE));\n  }\n\n  // Declare a buffer used to hold small write requests.  It could point to a\n  // memory block either on stack or on heap. If it is on heap, we release it\n  // manually when scope exits\n  char* combinedBuf{nullptr};\n  SCOPE_EXIT {\n    // Note, always keep this check consistent with what we do below\n    if (combinedBuf != nullptr && minWriteSize_ > MAX_STACK_BUF_SIZE) {\n      delete[] combinedBuf;\n    }\n  };\n\n  *countWritten = 0;\n  *partialWritten = 0;\n  ssize_t totalWritten = 0;\n  size_t bytesStolenFromNextBuffer = 0;\n  for (uint32_t i = 0; i < count; i++) {\n    const iovec* v = vec + i;\n    size_t offset = bytesStolenFromNextBuffer;\n    bytesStolenFromNextBuffer = 0;\n    size_t len = v->iov_len - offset;\n    const void* buf;\n    if (len == 0) {\n      (*countWritten)++;\n      continue;\n    }\n    buf = ((const char*)v->iov_base) + offset;\n\n    ssize_t bytes;\n    uint32_t buffersStolen = 0;\n    auto sslWriteBuf = buf;\n    if ((len < minWriteSize_) && ((i + 1) < count)) {\n      // Combine this buffer with part or all of the next buffers in\n      // order to avoid really small-grained calls to SSL_write().\n      // Each call to SSL_write() produces a separate record in\n      // the egress SSL stream, and we've found that some low-end\n      // mobile clients can't handle receiving an HTTP response\n      // header and the first part of the response body in two\n      // separate SSL records (even if those two records are in\n      // the same TCP packet).\n\n      if (combinedBuf == nullptr) {\n        if (minWriteSize_ > MAX_STACK_BUF_SIZE) {\n          // Allocate the buffer on heap\n          combinedBuf = new char[minWriteSize_];\n        } else {\n          // Allocate the buffer on stack\n          combinedBuf = (char*)alloca(minWriteSize_);\n        }\n      }\n      assert(combinedBuf != nullptr);\n      sslWriteBuf = combinedBuf;\n\n      memcpy(combinedBuf, buf, len);\n      do {\n        // INVARIANT: i + buffersStolen == complete chunks serialized\n        uint32_t nextIndex = i + buffersStolen + 1;\n        bytesStolenFromNextBuffer =\n            std::min(vec[nextIndex].iov_len, minWriteSize_ - len);\n        if (bytesStolenFromNextBuffer > 0) {\n          assert(vec[nextIndex].iov_base != nullptr);\n          ::memcpy(\n              combinedBuf + len,\n              vec[nextIndex].iov_base,\n              bytesStolenFromNextBuffer);\n        }\n        len += bytesStolenFromNextBuffer;\n        if (bytesStolenFromNextBuffer < vec[nextIndex].iov_len) {\n          // couldn't steal the whole buffer\n          break;\n        } else {\n          bytesStolenFromNextBuffer = 0;\n          buffersStolen++;\n        }\n      } while ((i + buffersStolen + 1) < count && (len < minWriteSize_));\n    }\n\n    // Advance any empty buffers immediately after.\n    if (bytesStolenFromNextBuffer == 0) {\n      while ((i + buffersStolen + 1) < count &&\n             vec[i + buffersStolen + 1].iov_len == 0) {\n        buffersStolen++;\n      }\n    }\n\n    // From here, the write flow is as follows:\n    //   - sslWriteImpl calls SSL_write, which encrypts the passed buffer.\n    //   - SSL_write calls AsyncSSLSocket::bioWrite with the encrypted buffer.\n    //   - AsyncSSLSocket::bioWrite calls AsyncSocket::sendSocketMessage(...).\n    //\n    // When sendSocketMessage calls sendMsg, WriteFlags are transformed into\n    // ancillary data and/or sendMsg flags. If WriteFlag::EOR is in flags and\n    // trackEor_ is set, then we should ensure that MSG_EOR is only passed to\n    // sendmsg when the final byte of the orginally passed in buffer is being\n    // written. Since the buffer originally passed to performWrite may be split\n    // up and written over multiple calls to sendmsg, we have to take care to\n    // unset the EOR flag if it was included in the WriteFlags passed in and\n    // we're writing a buffer that does _not_ contain the final byte of the\n    // originally passed buffer.\n    //\n    // We handle EOR as follows:\n    //   - We set currWriteFlags_ to the passed in WriteFlags.\n    //   - If sslWriteBuf does NOT contain the last byte of the passed in iovec,\n    //     then we set currBytesToFinalByte_ to folly::none. In bioWrite, we\n    //     unset WriteFlags::EOR if it is set in currWriteFlags_.\n    //   - If sslWriteBuf DOES contain the last byte of the passed in iovec,\n    //     then we set bytesToFinalByte_ to int(len). In bioWrite, if the length\n    //     of the passed in buffer >= currBytesToFinalByte_, then we leave the\n    //     flags in currWriteFlags_ alone.\n    //\n    // What about timestamp flags?\n    //   - We don't do any special handling for timestamping flags.\n    //   - This may mean that more timestamps than necessary get generated, but\n    //     that's OK; you already have to deal with that for timestamping due to\n    //     the possibility of partial writes.\n    //   - MSG_EOR used to be used for timestamping, but hasn't been for years.\n    //\n    // Finally, why even care about MSG_EOR, if not for timestamping?\n    //   - If set, it is marked in the corresponding tcp_skb_cb; this can be\n    //     useful when debugging.\n    //   - The kernel uses it to decide whether socket buffers can be collapsed\n    //     together (see tcp_skb_can_collapse_to).\n    currWriteFlags_ = flags;\n    uint32_t iovecWrittenToSslWriteBuf = i + buffersStolen + 1;\n    CHECK_LE(iovecWrittenToSslWriteBuf, count);\n    if (iovecWrittenToSslWriteBuf == count) { // last byte is in sslWriteBuf\n      currBytesToFinalByte_ = len; // length of current buffer\n    } else { // there are still remaining buffers / iovec to write\n      currBytesToFinalByte_ = folly::none;\n      currWriteFlags_ |= WriteFlags::CORK;\n    }\n\n    bytes = sslWriteImpl(ssl_.get(), sslWriteBuf, int(len));\n    if (bytes <= 0) {\n      int error = sslGetErrorImpl(ssl_.get(), int(bytes));\n      if (error == SSL_ERROR_WANT_WRITE) {\n        // The entire buffer needs to be passed in again, so *partialWritten\n        // is set to the original offset where we started for this call to\n        // performWrite(); see SSL_ERROR_WANT_WRITE documentation for details.\n        //\n        // The caller will register for write event if not already.\n        *partialWritten = uint32_t(offset);\n        return WriteResult(totalWritten);\n      }\n      return interpretSSLError(int(bytes), error);\n    }\n\n    totalWritten += bytes;\n    appBytesWritten_ += bytes;\n\n    if (bytes == (ssize_t)len) {\n      // The full iovec is written.\n      (*countWritten) += 1 + buffersStolen;\n      i += buffersStolen;\n      // continue\n    } else {\n      bytes += offset; // adjust bytes to account for all of v\n      while (bytes >= (ssize_t)v->iov_len) {\n        // We combined this buf with part or all of the next one, and\n        // we managed to write all of this buf but not all of the bytes\n        // from the next one that we'd hoped to write.\n        bytes -= v->iov_len;\n        (*countWritten)++;\n        v = &(vec[++i]);\n      }\n      *partialWritten = uint32_t(bytes);\n      return WriteResult(totalWritten);\n    }\n  }\n\n  return WriteResult(totalWritten);\n}\n\nvoid AsyncSSLSocket::sslInfoCallback(const SSL* ssl, int where, int ret) {\n  AsyncSSLSocket* sslSocket = AsyncSSLSocket::getFromSSL(ssl);\n  if (sslSocket->handshakeComplete_ && (where & SSL_CB_HANDSHAKE_START)) {\n    sslSocket->renegotiateAttempted_ = true;\n  }\n  if (sslSocket->handshakeComplete_ && (where & SSL_CB_WRITE_ALERT)) {\n    const char* desc = SSL_alert_desc_string(ret);\n    if (desc && strcmp(desc, \"NR\") == 0) {\n      sslSocket->renegotiateAttempted_ = true;\n    }\n  }\n  if (where & SSL_CB_READ_ALERT) {\n    const char* type = SSL_alert_type_string(ret);\n    if (type) {\n      const char* desc = SSL_alert_desc_string(ret);\n      sslSocket->alertsReceived_.emplace_back(\n          *type, StringPiece(desc, std::strlen(desc)));\n    }\n  }\n}\n\nint AsyncSSLSocket::bioWrite(BIO* b, const char* in, int inl) {\n  // get pointer to AsyncSSLSocket from BioAppData\n  auto appData = OpenSSLUtils::getBioAppData(b);\n  CHECK(appData);\n  AsyncSSLSocket* sslSock = reinterpret_cast<AsyncSSLSocket*>(appData);\n  CHECK(sslSock);\n\n  // if EOR is tracked, correct if needed\n  WriteFlags flags = sslSock->currWriteFlags_;\n  if (sslSock->trackEor_ &&\n      (!sslSock->currBytesToFinalByte_.has_value() ||\n       *(sslSock->currBytesToFinalByte_) > (size_t)inl)) {\n    // unset EOR if set, since we're not writing the last byte yet\n    flags = unSet(flags, folly::WriteFlags::EOR);\n  }\n\n  struct iovec vec;\n  vec.iov_base = const_cast<char*>(in);\n  vec.iov_len = size_t(inl);\n  // NB: It would be technically possible to plumb through the actual write\n  // tag in here, but we decided it not to be worth the implementation\n  // complexity.  The PoC implementation + tests are D43023628 (V15) +\n  // D44433483.\n  auto result = sslSock->sendSocketMessage(\n      &vec, 1, flags, WriteRequestTag{WriteRequestTag::EmptyDummy()});\n  BIO_clear_retry_flags(b);\n  if (!result.exception && result.writeReturn <= 0) {\n    if (OpenSSLUtils::getBioShouldRetryWrite(int(result.writeReturn))) {\n      BIO_set_retry_write(b);\n    }\n  }\n  return int(result.writeReturn);\n}\n\nint AsyncSSLSocket::bioRead(BIO* b, char* out, int outl) {\n  if (!out) {\n    return 0;\n  }\n  BIO_clear_retry_flags(b);\n\n  auto appData = OpenSSLUtils::getBioAppData(b);\n  CHECK(appData);\n  auto sslSock = reinterpret_cast<AsyncSSLSocket*>(appData);\n\n  if (sslSock->preReceivedData_ && !sslSock->preReceivedData_->empty()) {\n    VLOG(5) << \"AsyncSSLSocket::bioRead() this=\" << sslSock\n            << \", reading pre-received data\";\n\n    Cursor cursor(sslSock->preReceivedData_.get());\n    auto len = cursor.pullAtMost(out, outl);\n\n    IOBufQueue queue;\n    queue.append(std::move(sslSock->preReceivedData_));\n    queue.trimStart(len);\n    sslSock->preReceivedData_ = queue.move();\n    return static_cast<int>(len);\n  } else {\n    auto result = int(netops::recv(OpenSSLUtils::getBioFd(b), out, outl, 0));\n    if (result <= 0 && OpenSSLUtils::getBioShouldRetryWrite(result)) {\n      BIO_set_retry_read(b);\n    }\n    return result;\n  }\n}\n\nint AsyncSSLSocket::sslVerifyCallback(\n    int preverifyOk, X509_STORE_CTX* x509Ctx) {\n  SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(\n      x509Ctx, SSL_get_ex_data_X509_STORE_CTX_idx());\n  AsyncSSLSocket* self = AsyncSSLSocket::getFromSSL(ssl);\n\n  VLOG(3) << \"AsyncSSLSocket::sslVerifyCallback() this=\" << self << \", \"\n          << \"fd=\" << self->fd_ << \", preverifyOk=\" << preverifyOk;\n\n  if (self->handshakeCallback_) {\n    int callbackOk =\n        self->handshakeCallback_->handshakeVer(self, preverifyOk, x509Ctx)\n        ? 1\n        : 0;\n\n    if (preverifyOk != callbackOk) {\n      // HandshakeCB overwrites result from OpenSSL. One way or another, do not\n      // call verifyLeaf.\n      return callbackOk;\n    }\n  }\n\n  // verifyContext can override the OpenSSL verification result. Unlike\n  // handshakeVer, it doesn't return early - allowing verifyLeaf to be called\n  // for the leaf certificate even if verifyContext changes the result.\n  if (self->certificateIdentityVerifier_) {\n    preverifyOk =\n        self->certificateIdentityVerifier_->verifyContext(preverifyOk, x509Ctx)\n        ? 1\n        : 0;\n  }\n\n  if (!preverifyOk) {\n    // Verification failed (either OpenSSL or verifyContext), no need to call\n    // verifyLeaf.\n    return 0;\n  }\n\n  // only invoke the verifyLeaf callback for the leaf certificate and\n  // only if all previous verification steps succeeded (OpenSSL, handshakeVer,\n  // and verifyContext)\n\n  int currentDepth = X509_STORE_CTX_get_error_depth(x509Ctx);\n  if (currentDepth != 0 || self->certificateIdentityVerifier_ == nullptr) {\n    return 1;\n  }\n\n  X509* peerX509 = X509_STORE_CTX_get_current_cert(x509Ctx);\n  X509_up_ref(peerX509);\n  folly::ssl::X509UniquePtr peer{peerX509};\n  auto cn = OpenSSLUtils::getCommonName(peerX509);\n  auto cert = std::make_unique<BasicTransportCertificate>(\n      std::move(cn), std::move(peer));\n\n  try {\n    self->setPeerCertificate(\n        self->certificateIdentityVerifier_->verifyLeaf(*cert.get()));\n  } catch (folly::CertificateIdentityVerifierException& e) {\n    LOG(ERROR) << \"AsyncSSLSocket::sslVerifyCallback(this=\" << self\n               << \", fd=\" << self->fd_\n               << \") Failed to verify leaf certificate identity(ies): \" << e;\n    return 0;\n  }\n\n  return 1;\n}\n\nvoid AsyncSSLSocket::enableByteEvents() {\n  if (getSSLVersion() == SSL3_VERSION || getSSLVersion() == TLS1_VERSION) {\n    // Socket timestamping can cause us to split up TLS records in a way that\n    // breaks some old Android (<= 3.0) clients.\n\n    if (!byteEventHelper_) {\n      byteEventHelper_ = std::make_unique<ByteEventHelper>();\n    }\n    return failByteEvents(AsyncSocketException(\n        AsyncSocketException::NOT_SUPPORTED,\n        withAddr(\n            \"failed to enable byte events: \"\n            \"not supported for SSLv3 or TLSv1\")));\n  }\n  AsyncSocket::enableByteEvents();\n}\n\nvoid AsyncSSLSocket::enableClientHelloParsing() {\n  parseClientHello_ = true;\n  clientHelloInfo_ = std::make_unique<ssl::ClientHelloInfo>();\n}\n\nvoid AsyncSSLSocket::resetClientHelloParsing(SSL* ssl) {\n  SSL_set_msg_callback(ssl, nullptr);\n  SSL_set_msg_callback_arg(ssl, nullptr);\n  clientHelloInfo_->clientHelloBuf_.reset();\n}\n\nvoid AsyncSSLSocket::parseClientAlpns(\n    AsyncSSLSocket* sock,\n    folly::io::Cursor& cursor,\n    uint16_t& extensionDataLength) {\n  cursor.skip(2);\n  extensionDataLength -= 2;\n  while (extensionDataLength) {\n    auto protoLength = cursor.readBE<uint8_t>();\n    extensionDataLength--;\n    auto proto = cursor.readFixedString(protoLength);\n    sock->clientHelloInfo_->clientAlpns_.push_back(proto);\n    extensionDataLength -= protoLength;\n  }\n}\n\nvoid AsyncSSLSocket::clientHelloParsingCallback(\n    int written,\n    int /* version */,\n    int contentType,\n    const void* buf,\n    size_t len,\n    SSL* ssl,\n    void* arg) {\n  auto sock = static_cast<AsyncSSLSocket*>(arg);\n  if (written != 0) {\n    sock->resetClientHelloParsing(ssl);\n    return;\n  }\n  if (contentType != SSL3_RT_HANDSHAKE) {\n    return;\n  }\n  if (len == 0) {\n    return;\n  }\n\n  auto& clientHelloBuf = sock->clientHelloInfo_->clientHelloBuf_;\n  clientHelloBuf.append(IOBuf::wrapBuffer(buf, len));\n  try {\n    Cursor cursor(clientHelloBuf.front());\n    if (cursor.read<uint8_t>() != SSL3_MT_CLIENT_HELLO) {\n      sock->resetClientHelloParsing(ssl);\n      return;\n    }\n\n    if (cursor.totalLength() < 3) {\n      clientHelloBuf.trimEnd(len);\n      clientHelloBuf.append(IOBuf::copyBuffer(buf, len));\n      return;\n    }\n\n    uint32_t messageLength = cursor.read<uint8_t>();\n    messageLength <<= 8;\n    messageLength |= cursor.read<uint8_t>();\n    messageLength <<= 8;\n    messageLength |= cursor.read<uint8_t>();\n    if (cursor.totalLength() < messageLength) {\n      clientHelloBuf.trimEnd(len);\n      clientHelloBuf.append(IOBuf::copyBuffer(buf, len));\n      return;\n    }\n\n    sock->clientHelloInfo_->clientHelloMajorVersion_ = cursor.read<uint8_t>();\n    sock->clientHelloInfo_->clientHelloMinorVersion_ = cursor.read<uint8_t>();\n\n    cursor.skip(4); // gmt_unix_time\n    cursor.skip(28); // random_bytes\n\n    cursor.skip(cursor.read<uint8_t>()); // session_id\n\n    auto cipherSuitesLength = cursor.readBE<uint16_t>();\n    for (int i = 0; i < cipherSuitesLength; i += 2) {\n      sock->clientHelloInfo_->clientHelloCipherSuites_.push_back(\n          cursor.readBE<uint16_t>());\n    }\n\n    auto compressionMethodsLength = cursor.read<uint8_t>();\n    for (int i = 0; i < compressionMethodsLength; ++i) {\n      sock->clientHelloInfo_->clientHelloCompressionMethods_.push_back(\n          cursor.readBE<uint8_t>());\n    }\n\n    if (cursor.totalLength() > 0) {\n      auto extensionsLength = cursor.readBE<uint16_t>();\n      while (extensionsLength) {\n        auto extensionType =\n            static_cast<ssl::TLSExtension>(cursor.readBE<uint16_t>());\n        sock->clientHelloInfo_->clientHelloExtensions_.push_back(extensionType);\n        extensionsLength -= 2;\n        auto extensionDataLength = cursor.readBE<uint16_t>();\n        extensionsLength -= 2;\n        extensionsLength -= extensionDataLength;\n\n        if (extensionType == ssl::TLSExtension::SIGNATURE_ALGORITHMS) {\n          cursor.skip(2);\n          extensionDataLength -= 2;\n          while (extensionDataLength) {\n            auto hashAlg =\n                static_cast<ssl::HashAlgorithm>(cursor.readBE<uint8_t>());\n            auto sigAlg =\n                static_cast<ssl::SignatureAlgorithm>(cursor.readBE<uint8_t>());\n            extensionDataLength -= 2;\n            sock->clientHelloInfo_->clientHelloSigAlgs_.emplace_back(\n                hashAlg, sigAlg);\n          }\n        } else if (extensionType == ssl::TLSExtension::SUPPORTED_VERSIONS) {\n          cursor.skip(1);\n          extensionDataLength -= 1;\n          while (extensionDataLength) {\n            sock->clientHelloInfo_->clientHelloSupportedVersions_.push_back(\n                cursor.readBE<uint16_t>());\n            extensionDataLength -= 2;\n          }\n        } else if (extensionType == ssl::TLSExtension::SERVER_NAME) {\n          cursor.skip(2);\n          extensionDataLength -= 2;\n          while (extensionDataLength) {\n            static_assert(\n                std::is_same<\n                    typename std::underlying_type<ssl::NameType>::type,\n                    uint8_t>::value,\n                \"unexpected underlying type\");\n\n            auto typ = static_cast<ssl::NameType>(cursor.readBE<uint8_t>());\n            auto nameLength = cursor.readBE<uint16_t>();\n\n            if (typ == NameType::HOST_NAME &&\n                sock->clientHelloInfo_->clientHelloSNIHostname_.empty() &&\n                cursor.canAdvance(nameLength)) {\n              sock->clientHelloInfo_->clientHelloSNIHostname_ =\n                  cursor.readFixedString(nameLength);\n            } else {\n              // Must attempt to skip |nameLength| in order to keep cursor\n              // in sync. If the remaining buffer length is smaller than\n              // nameLength, this will throw.\n              cursor.skip(nameLength);\n            }\n            extensionDataLength -=\n                sizeof(typ) + sizeof(nameLength) + nameLength;\n          }\n        } else if (\n            extensionType ==\n            ssl::TLSExtension::APPLICATION_LAYER_PROTOCOL_NEGOTIATION) {\n          parseClientAlpns(sock, cursor, extensionDataLength);\n        } else {\n          cursor.skip(extensionDataLength);\n        }\n      }\n    }\n  } catch (std::out_of_range&) {\n    // we'll use what we found and cleanup below.\n    VLOG(4) << \"AsyncSSLSocket::clientHelloParsingCallback(): \"\n            << \"buffer finished unexpectedly.\"\n            << \" AsyncSSLSocket socket=\" << sock;\n  }\n\n  sock->resetClientHelloParsing(ssl);\n}\n\nvoid AsyncSSLSocket::getSSLClientCiphers(\n    std::string& clientCiphers, bool convertToString) const {\n  std::string ciphers;\n\n  if (!parseClientHello_ ||\n      clientHelloInfo_->clientHelloCipherSuites_.empty()) {\n    clientCiphers = \"\";\n    return;\n  }\n\n  bool first = true;\n  for (auto originalCipherCode : clientHelloInfo_->clientHelloCipherSuites_) {\n    if (first) {\n      first = false;\n    } else {\n      ciphers += \":\";\n    }\n\n    bool nameFound = convertToString;\n\n    if (convertToString) {\n      const auto& name = OpenSSLUtils::getCipherName(originalCipherCode);\n      if (name.empty()) {\n        nameFound = false;\n      } else {\n        ciphers += name;\n      }\n    }\n\n    if (!nameFound) {\n      folly::hexlify(\n          std::array<uint8_t, 2>{\n              {static_cast<uint8_t>((originalCipherCode >> 8) & 0xff),\n               static_cast<uint8_t>(originalCipherCode & 0x00ff)}},\n          ciphers,\n          /* append to ciphers = */ true);\n    }\n  }\n\n  clientCiphers = std::move(ciphers);\n}\n\nstd::string AsyncSSLSocket::getSSLClientComprMethods() const {\n  if (!parseClientHello_) {\n    return \"\";\n  }\n  return folly::join(\":\", clientHelloInfo_->clientHelloCompressionMethods_);\n}\n\nstd::string AsyncSSLSocket::getSSLClientExts() const {\n  if (!parseClientHello_) {\n    return \"\";\n  }\n  return folly::join(\":\", clientHelloInfo_->clientHelloExtensions_);\n}\n\nstd::string AsyncSSLSocket::getSSLClientSigAlgs() const {\n  if (!parseClientHello_) {\n    return \"\";\n  }\n\n  std::string sigAlgs;\n  sigAlgs.reserve(clientHelloInfo_->clientHelloSigAlgs_.size() * 4);\n  for (size_t i = 0; i < clientHelloInfo_->clientHelloSigAlgs_.size(); i++) {\n    if (i) {\n      sigAlgs.push_back(':');\n    }\n    sigAlgs.append(\n        folly::to<std::string>(clientHelloInfo_->clientHelloSigAlgs_[i].first));\n    sigAlgs.push_back(',');\n    sigAlgs.append(\n        folly::to<std::string>(\n            clientHelloInfo_->clientHelloSigAlgs_[i].second));\n  }\n\n  return sigAlgs;\n}\n\nstd::string AsyncSSLSocket::getSSLClientSupportedVersions() const {\n  if (!parseClientHello_) {\n    return \"\";\n  }\n  return folly::join(\":\", clientHelloInfo_->clientHelloSupportedVersions_);\n}\n\nstd::string AsyncSSLSocket::getSSLAlertsReceived() const {\n  std::string ret;\n\n  for (const auto& alert : alertsReceived_) {\n    if (!ret.empty()) {\n      ret.append(\",\");\n    }\n    ret.append(folly::to<std::string>(alert.first, \": \", alert.second));\n  }\n\n  return ret;\n}\n\nvoid AsyncSSLSocket::setSSLCertVerificationAlert(std::string alert) {\n  sslVerificationAlert_ = std::move(alert);\n}\n\nstd::string AsyncSSLSocket::getSSLCertVerificationAlert() const {\n  return sslVerificationAlert_;\n}\n\nvoid AsyncSSLSocket::getSSLSharedCiphers(std::string& sharedCiphers) const {\n  char ciphersBuffer[1024];\n  ciphersBuffer[0] = '\\0';\n  SSL_get_shared_ciphers(ssl_.get(), ciphersBuffer, sizeof(ciphersBuffer) - 1);\n  sharedCiphers = ciphersBuffer;\n}\n\nvoid AsyncSSLSocket::getSSLServerCiphers(std::string& serverCiphers) const {\n  serverCiphers = SSL_get_cipher_list(ssl_.get(), 0);\n  int i = 1;\n  const char* cipher;\n  while ((cipher = SSL_get_cipher_list(ssl_.get(), i)) != nullptr) {\n    serverCiphers.append(\":\");\n    serverCiphers.append(cipher);\n    i++;\n  }\n}\n\nconst std::vector<std::string>& AsyncSSLSocket::getClientAlpns() const {\n  if (!parseClientHello_) {\n    static std::vector<std::string> emptyAlpns{};\n    return emptyAlpns;\n  } else {\n    return clientHelloInfo_->clientAlpns_;\n  }\n}\n\nvoid AsyncSSLSocket::createSSL() {\n  try {\n    ssl_.reset(ctx_->createSSL());\n  } catch (std::exception& e) {\n    sslState_ = STATE_ERROR;\n    static const Indestructible<AsyncSocketException> ex(\n        AsyncSocketException::INTERNAL_ERROR,\n        \"error calling SSLContext::createSSL()\");\n    LOG(ERROR) << \"AsyncSSLSocket::createSSL(this=\" << this << \", fd=\" << fd_\n               << \"): \" << e.what();\n    return failHandshake(__func__, *ex);\n  }\n\n  if (!setupSSLBio()) {\n    sslState_ = STATE_ERROR;\n    static const Indestructible<AsyncSocketException> ex(\n        AsyncSocketException::INTERNAL_ERROR, \"error creating SSL bio\");\n    return failHandshake(__func__, *ex);\n  }\n\n  SSL_set_ex_data(ssl_.get(), getSSLExDataIndex(), this);\n\n  if (!applyVerificationOptions(ssl_)) {\n    sslState_ = STATE_ERROR;\n    static const Indestructible<AsyncSocketException> ex(\n        AsyncSocketException::INTERNAL_ERROR,\n        \"error applying the SSL verification options\");\n    return failHandshake(__func__, *ex);\n  }\n}\n\nvoid AsyncSSLSocket::ensureSSL() {\n  if (!ssl_) {\n    return createSSL();\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSSLSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <iomanip>\n\n#include <folly/Optional.h>\n#include <folly/String.h>\n#include <folly/io/Cursor.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncPipe.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/CertificateIdentityVerifier.h>\n#include <folly/io/async/SSLContext.h>\n#include <folly/io/async/TimeoutManager.h>\n#include <folly/io/async/ssl/OpenSSLUtils.h>\n#include <folly/io/async/ssl/SSLErrors.h>\n#include <folly/io/async/ssl/TLSDefinitions.h>\n#include <folly/lang/Bits.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/portability/Sockets.h>\n#include <folly/ssl/OpenSSLPtrTypes.h>\n#include <folly/ssl/SSLSession.h>\n#include <folly/ssl/SSLSessionManager.h>\n\nnamespace folly {\n\nclass AsyncSSLSocketConnector;\n\n/**\n * A class for performing asynchronous I/O on an SSL connection.\n *\n * AsyncSSLSocket allows users to asynchronously wait for data on an\n * SSL connection, and to asynchronously send data.\n *\n * The APIs for reading and writing are intentionally asymmetric.\n * Waiting for data to read is a persistent API: a callback is\n * installed, and is notified whenever new data is available.  It\n * continues to be notified of new events until it is uninstalled.\n *\n * AsyncSSLSocket does not provide read timeout functionality,\n * because it typically cannot determine when the timeout should be\n * active.  Generally, a timeout should only be enabled when\n * processing is blocked waiting on data from the remote endpoint.\n * For server connections, the timeout should not be active if the\n * server is currently processing one or more outstanding requests for\n * this connection.  For client connections, the timeout should not be\n * active if there are no requests pending on the connection.\n * Additionally, if a client has multiple pending requests, it will\n * usually want a separate timeout for each request, rather than a\n * single read timeout.\n *\n * The write API is fairly intuitive: a user can request to send a\n * block of data, and a callback will be informed once the entire\n * block has been transferred to the kernel, or on error.\n * AsyncSSLSocket does provide a send timeout, since most callers\n * want to give up if the remote end stops responding and no further\n * progress can be made sending the data.\n */\nclass AsyncSSLSocket : public AsyncSocket {\n public:\n  using UniquePtr = std::unique_ptr<AsyncSSLSocket, Destructor>;\n  using X509_deleter = folly::static_function_deleter<X509, &X509_free>;\n\n  class HandshakeCB {\n   public:\n    virtual ~HandshakeCB() = default;\n\n    /**\n     * handshakeVer() is invoked during handshaking to give the\n     * application chance to validate it's peer's certificate.\n     *\n     * Note that OpenSSL performs only rudimentary internal\n     * consistency verification checks by itself. Any other validation\n     * like whether or not the certificate was issued by a trusted CA.\n     * The default implementation of this callback mimics what what\n     * OpenSSL does internally if SSL_VERIFY_PEER is set with no\n     * verification callback.\n     *\n     * See the passages on verify_callback in SSL_CTX_set_verify(3)\n     * for more details.\n     */\n    virtual bool handshakeVer(\n        AsyncSSLSocket* /*sock*/,\n        bool preverifyOk,\n        X509_STORE_CTX* /*ctx*/) noexcept {\n      return preverifyOk;\n    }\n\n    /**\n     * handshakeSuc() is called when a new SSL connection is\n     * established, i.e., after SSL_accept/connect() returns successfully.\n     *\n     * The HandshakeCB will be uninstalled before handshakeSuc()\n     * is called.\n     *\n     * @param sock  SSL socket on which the handshake was initiated\n     */\n    virtual void handshakeSuc(AsyncSSLSocket* sock) noexcept = 0;\n\n    /**\n     * handshakeErr() is called if an error occurs while\n     * establishing the SSL connection.\n     *\n     * The HandshakeCB will be uninstalled before handshakeErr()\n     * is called.\n     *\n     * @param sock  SSL socket on which the handshake was initiated\n     * @param ex  An exception representing the error.\n     */\n    virtual void handshakeErr(\n        AsyncSSLSocket* sock, const AsyncSocketException& ex) noexcept = 0;\n  };\n\n  class Timeout : public AsyncTimeout {\n   public:\n    Timeout(AsyncSSLSocket* sslSocket, EventBase* eventBase)\n        : AsyncTimeout(eventBase), sslSocket_(sslSocket) {}\n\n    bool scheduleTimeout(TimeoutManager::timeout_type timeout) {\n      timeout_ = timeout;\n      return AsyncTimeout::scheduleTimeout(timeout);\n    }\n\n    bool scheduleTimeout(uint32_t timeoutMs) {\n      return scheduleTimeout(std::chrono::milliseconds{timeoutMs});\n    }\n\n    TimeoutManager::timeout_type getTimeout() { return timeout_; }\n\n    void timeoutExpired() noexcept override {\n      sslSocket_->timeoutExpired(timeout_);\n    }\n\n   private:\n    AsyncSSLSocket* sslSocket_;\n    TimeoutManager::timeout_type timeout_;\n  };\n\n  /**\n   * A class to wait for asynchronous operations with OpenSSL 1.1.0\n   */\n  class DefaultOpenSSLAsyncFinishCallback : public ReadCallback {\n   public:\n    DefaultOpenSSLAsyncFinishCallback(\n        AsyncPipeReader::UniquePtr reader,\n        AsyncSSLSocket* sslSocket,\n        DestructorGuard dg)\n        : pipeReader_(std::move(reader)),\n          sslSocket_(sslSocket),\n          dg_(std::move(dg)) {}\n\n    ~DefaultOpenSSLAsyncFinishCallback() override {\n      pipeReader_->setReadCB(nullptr);\n      sslSocket_->setAsyncOperationFinishCallback(nullptr);\n    }\n\n    void readDataAvailable(size_t len) noexcept override {\n      CHECK_EQ(len, 1u);\n      sslSocket_->restartSSLAccept();\n      pipeReader_->setReadCB(nullptr);\n      sslSocket_->setAsyncOperationFinishCallback(nullptr);\n    }\n\n    void getReadBuffer(void** bufReturn, size_t* lenReturn) noexcept override {\n      *bufReturn = &byte_;\n      *lenReturn = 1;\n    }\n\n    void readEOF() noexcept override {}\n\n    void readErr(const folly::AsyncSocketException&) noexcept override {}\n\n   private:\n    uint8_t byte_{0};\n    AsyncPipeReader::UniquePtr pipeReader_;\n    AsyncSSLSocket* sslSocket_{nullptr};\n    DestructorGuard dg_;\n  };\n\n  /**\n   * Struct to consolidate constructor arguments.\n   */\n  struct Options {\n    // If this verifier is set, it's used during the TLS handshake. First,\n    // verifyContext() is called during OpenSSL's certificate verification\n    // callback for each certificate in the chain, after HandshakeCB's\n    // handshakeVer() if set. Then, verifyLeaf() is invoked to verify the\n    // peer's end-entity leaf certificate, but only if OpenSSL's chain\n    // validation, handshakeVer(), and verifyContext() all succeeded.\n    std::shared_ptr<const CertificateIdentityVerifier> verifier;\n    bool deferSecurityNegotiation{};\n    bool isServer{};\n    std::string serverName;\n  };\n\n  /**\n   * Initialize this AsyncSSLSocket object with the given Options.\n   *\n   * @param options optional arguments for this AsyncSSLSocket instance\n   */\n  AsyncSSLSocket(\n      std::shared_ptr<const folly::SSLContext> ctx,\n      EventBase* evb,\n      Options&& options);\n\n  /**\n   * Initialize this AsyncSSLSocket object with the given Options from an\n   * already connected AsyncSocket.\n   *\n   * @param options optional arguments for this AsyncSSLSocket instance\n   */\n  AsyncSSLSocket(\n      std::shared_ptr<const folly::SSLContext> ctx,\n      AsyncSocket::UniquePtr oldAsyncSocket,\n      Options&& options);\n\n  /**\n   * Create a client AsyncSSLSocket\n   */\n  AsyncSSLSocket(\n      std::shared_ptr<const folly::SSLContext> ctx,\n      EventBase* evb,\n      bool deferSecurityNegotiation = false);\n\n  /**\n   * Create a server/client AsyncSSLSocket from an already connected\n   * socket file descriptor.\n   *\n   * Note that while AsyncSSLSocket enables TCP_NODELAY for sockets it creates\n   * when connecting, it does not change the socket options when given an\n   * existing file descriptor.  If callers want TCP_NODELAY enabled when using\n   * this version of the constructor, they need to explicitly call\n   * setNoDelay(true) after the constructor returns.\n   *\n   * @param ctx             SSL context for this connection.\n   * @param evb EventBase that will manage this socket.\n   * @param fd  File descriptor to take over (should be a connected socket).\n   * @param server Is socket in server mode?\n   * @param deferSecurityNegotiation\n   *          unencrypted data can be sent before sslConn/Accept\n   * @param peerAddress optional peer address (eg: returned from accept).  If\n   *          nullptr, AsyncSocket will lazily attempt to determine it from fd\n   *          via a system call\n   */\n  AsyncSSLSocket(\n      std::shared_ptr<const folly::SSLContext> ctx,\n      EventBase* evb,\n      NetworkSocket fd,\n      bool server = true,\n      bool deferSecurityNegotiation = false,\n      const SocketAddress* peerAddress = nullptr);\n\n  /**\n   * Create a server/client AsyncSSLSocket from an already connected\n   * AsyncSocket.\n   */\n  AsyncSSLSocket(\n      std::shared_ptr<const folly::SSLContext> ctx,\n      AsyncSocket* oldAsyncSocket,\n      bool server = true,\n      bool deferSecurityNegotiation = false);\n\n  /**\n   * Create a server/client AsyncSSLSocket from an already connected\n   * AsyncSocket.\n   */\n  AsyncSSLSocket(\n      std::shared_ptr<const folly::SSLContext> ctx,\n      AsyncSocket::UniquePtr oldAsyncSocket,\n      bool server = true,\n      bool deferSecurityNegotiation = false);\n\n  /**\n   * Helper function to create a server/client shared_ptr<AsyncSSLSocket>.\n   */\n  static UniquePtr newSocket(\n      std::shared_ptr<const folly::SSLContext> ctx,\n      EventBase* evb,\n      Options&& options) {\n    return AsyncSSLSocket::UniquePtr(\n        new AsyncSSLSocket(std::move(ctx), evb, std::move(options)));\n  }\n\n  /**\n   * Helper function to create a server/client shared_ptr<AsyncSSLSocket>.\n   */\n  static UniquePtr newSocket(\n      const std::shared_ptr<const folly::SSLContext>& ctx,\n      EventBase* evb,\n      NetworkSocket fd,\n      bool server = true,\n      bool deferSecurityNegotiation = false,\n      const folly::SocketAddress* peerAddress = nullptr) {\n    return AsyncSSLSocket::UniquePtr(new AsyncSSLSocket(\n        ctx, evb, fd, server, deferSecurityNegotiation, peerAddress));\n  }\n\n  /**\n   * Helper function to create a client shared_ptr<AsyncSSLSocket>.\n   */\n  static UniquePtr newSocket(\n      const std::shared_ptr<const folly::SSLContext>& ctx,\n      EventBase* evb,\n      bool deferSecurityNegotiation = false) {\n    return AsyncSSLSocket::UniquePtr(\n        new AsyncSSLSocket(ctx, evb, deferSecurityNegotiation));\n  }\n\n  /**\n   * Create a client AsyncSSLSocket with tlsext_servername in\n   * the Client Hello message.\n   */\n  AsyncSSLSocket(\n      const std::shared_ptr<const folly::SSLContext>& ctx,\n      EventBase* evb,\n      const std::string& serverName,\n      bool deferSecurityNegotiation = false);\n\n  /**\n   * Create a client AsyncSSLSocket from an already connected\n   * socket file descriptor.\n   *\n   * Note that while AsyncSSLSocket enables TCP_NODELAY for sockets it creates\n   * when connecting, it does not change the socket options when given an\n   * existing file descriptor.  If callers want TCP_NODELAY enabled when using\n   * this version of the constructor, they need to explicitly call\n   * setNoDelay(true) after the constructor returns.\n   *\n   * @param ctx  SSL context for this connection.\n   * @param evb  EventBase that will manage this socket.\n   * @param fd   File descriptor to take over (should be a connected socket).\n   * @param serverName tlsext_hostname that will be sent in ClientHello.\n   * @param deferSecurityNegotiation\n   *          unencrypted data can be sent before sslConn/Accept\n   * @param peerAddress optional peer address (eg: returned from accept).  If\n   *          nullptr, AsyncSocket will lazily attempt to determine it from fd\n   *          via a system call\n   */\n  AsyncSSLSocket(\n      const std::shared_ptr<const folly::SSLContext>& ctx,\n      EventBase* evb,\n      NetworkSocket fd,\n      const std::string& serverName,\n      bool deferSecurityNegotiation = false,\n      const SocketAddress* peerAddr = nullptr);\n\n  static UniquePtr newSocket(\n      const std::shared_ptr<const folly::SSLContext>& ctx,\n      EventBase* evb,\n      const std::string& serverName,\n      bool deferSecurityNegotiation = false) {\n    return AsyncSSLSocket::UniquePtr(\n        new AsyncSSLSocket(ctx, evb, serverName, deferSecurityNegotiation));\n  }\n\n  /**\n   * TODO: implement support for SSL renegotiation.\n   *\n   * This involves proper handling of the SSL_ERROR_WANT_READ/WRITE\n   * code as a result of SSL_write/read(), instead of returning an\n   * error. In that case, the READ/WRITE event should be registered,\n   * and a flag (e.g., writeBlockedOnRead) should be set to indiciate\n   * the condition. In the next invocation of read/write callback, if\n   * the flag is on, performWrite()/performReadMsg() should be called in\n   * addition to the normal call to performReadMsg()/performWrite(), and\n   * the flag should be reset.\n   */\n\n  // Inherit AsyncTransport methods from AsyncSocket except the\n  // following.\n  // See the documentation in AsyncTransport.h\n  // TODO: implement graceful shutdown in close()\n  // TODO: implement detachSSL() that returns the SSL connection\n  void closeNow() override;\n  void shutdownWrite() override;\n  void shutdownWriteNow() override;\n  bool readable() const override;\n  bool good() const override;\n  bool connecting() const override;\n  std::string getApplicationProtocol() const noexcept override;\n  void setSupportedApplicationProtocols(\n      const std::vector<std::string>& supportedProtocols);\n\n  std::string getSecurityProtocol() const override {\n    if (sslState_ == SSLStateEnum::STATE_UNENCRYPTED) {\n      return \"\";\n    }\n    return \"TLS\";\n  }\n\n  std::unique_ptr<folly::IOBuf> getExportedKeyingMaterial(\n      folly::StringPiece label,\n      std::unique_ptr<IOBuf> context,\n      uint16_t length) const override;\n\n  void setEorTracking(bool track) override;\n  size_t getRawBytesWritten() const override;\n  size_t getRawBytesReceived() const override;\n\n  // End of methods inherited from AsyncTransport\n\n  /**\n   * Enable ByteEvents for this socket.\n   *\n   * ByteEvents cannot be enabled if TLS 1.0 or earlier is in use, as these\n   * client implementations often have trouble handling cases where a TLS\n   * record is split across multiple packets.\n   */\n  void enableByteEvents() override;\n\n  void enableClientHelloParsing();\n\n  /**\n   * Accept an SSL connection on the socket.\n   *\n   * The callback will be invoked and uninstalled when an SSL\n   * connection has been established on the underlying socket.\n   * The value of verifyPeer determines the client verification method.\n   * By default, its set to use the value in the underlying context\n   *\n   * @param callback callback object to invoke on success/failure\n   * @param timeout timeout for this function in milliseconds, or 0 for no\n   *                timeout\n   * @param verifyPeer  SSLVerifyPeerEnum uses the options specified in the\n   *                context by default, can be set explcitly to override the\n   *                method in the context\n   */\n  virtual void sslAccept(\n      HandshakeCB* callback,\n      std::chrono::milliseconds timeout = std::chrono::milliseconds::zero(),\n      const folly::SSLContext::SSLVerifyPeerEnum& verifyPeer =\n          folly::SSLContext::SSLVerifyPeerEnum::USE_CTX);\n\n  /**\n   * Invoke SSL accept following an asynchronous session cache lookup\n   */\n  void restartSSLAccept();\n\n  /**\n   * Connect to the given address, invoking callback when complete or on error\n   *\n   * Note timeout applies to TCP + SSL connection time\n   */\n  void connect(\n      ConnectCallback* callback,\n      const folly::SocketAddress& address,\n      int timeout = 0,\n      const SocketOptionMap& options = emptySocketOptionMap,\n      const BindOptions& bindOptions = anyAddress(),\n      const std::string& ifName = \"\") noexcept override;\n\n  /**\n   * A variant of connect that allows the caller to specify\n   * the timeout for the regular connect and the ssl connect\n   * separately.\n   * connectTimeout is specified as the time to establish a TCP\n   * connection.\n   * totalConnectTimeout defines the\n   * time it takes from starting the TCP connection to the time\n   * the ssl connection is established. The reason the timeout is\n   * defined this way is because user's rarely need to specify the SSL\n   * timeout independently of the connect timeout. It allows us to\n   * bound the time for a connect and SSL connection in\n   * a finer grained manner than if timeout was just defined\n   * independently for SSL.\n   */\n  virtual void connect(\n      ConnectCallback* callback,\n      const folly::SocketAddress& address,\n      std::chrono::milliseconds connectTimeout,\n      std::chrono::milliseconds totalConnectTimeout,\n      const SocketOptionMap& options = emptySocketOptionMap,\n      const BindOptions& bindOptions = anyAddress(),\n      const std::string& ifName = \"\") noexcept;\n\n  using AsyncSocket::connect;\n\n  /**\n   * If a connect request is in-flight, cancels it and closes the socket\n   * immediately. Otherwise, this is a no-op.\n   *\n   * This does not invoke any connection related callbacks. Call this to\n   * prevent any connect callback while cleaning up, etc.\n   */\n  void cancelConnect() override;\n\n  /**\n   * Initiate an SSL connection on the socket\n   * The callback will be invoked and uninstalled when an SSL connection\n   * has been establshed on the underlying socket.\n   * The verification option verifyPeer is applied if it's passed explicitly.\n   * If it's not, the options in SSLContext set on the underlying SSLContext\n   * are applied.\n   *\n   * @param callback callback object to invoke on success/failure\n   * @param timeout timeout for this function in milliseconds, or 0 for no\n   *                timeout\n   * @param verifyPeer  SSLVerifyPeerEnum uses the options specified in the\n   *                context by default, can be set explcitly to override the\n   *                method in the context. If verification is turned on sets\n   *                SSL_VERIFY_PEER and invokes\n   *                HandshakeCB::handshakeVer().\n   */\n  virtual void sslConn(\n      HandshakeCB* callback,\n      std::chrono::milliseconds timeout = std::chrono::milliseconds::zero(),\n      const folly::SSLContext::SSLVerifyPeerEnum& verifyPeer =\n          folly::SSLContext::SSLVerifyPeerEnum::USE_CTX);\n\n  enum SSLStateEnum {\n    STATE_UNINIT,\n    STATE_UNENCRYPTED,\n    STATE_ACCEPTING,\n    STATE_ASYNC_PENDING,\n    STATE_CONNECTING,\n    STATE_ESTABLISHED,\n    STATE_REMOTE_CLOSED, /// remote end closed; we can still write\n    STATE_CLOSING, ///< close() called, but waiting on writes to complete\n    /// close() called with pending writes, before connect() has completed\n    STATE_CONNECTING_CLOSING,\n    STATE_CLOSED,\n    STATE_ERROR\n  };\n\n  SSLStateEnum getSSLState() const { return sslState_; }\n\n  /**\n   * Retrieve the SSL session associated with this established connection.\n   *\n   * The SSL Session object is a copyable, opaque token that can be set on other\n   * unconnected AsyncSSLSockets. If AsyncSSLSocket::connect() is called with a\n   * previous session set, TLS resumption will be attempted.\n   */\n  std::shared_ptr<ssl::SSLSession> getSSLSession();\n\n  /**\n   * Get a handle to the SSL struct.\n   */\n  const SSL* getSSL() const;\n\n  /**\n   * Sets the SSL session that will be attempted for TLS resumption.\n   */\n  void setSSLSession(std::shared_ptr<ssl::SSLSession> session);\n\n  /**\n   * Note: This function exists for compatibility reasons. It is strongly\n   * recommended to use setSSLSession instead. After setRawSSLSession is\n   * called, subsequent calls to getSSLSession on the socket will return null.\n   *\n   * Set the SSL session to be used during sslConn.\n   * If the caller wishes to resume the session in TLS 1.3, the caller\n   * is responsible for ensuring that the session is resumable.\n   * If the session is not resumable, then a full handshake will be performed.\n   */\n  void setRawSSLSession(folly::ssl::SSLSessionUniquePtr session);\n\n  /**\n   * Get the name of the protocol selected by the client during\n   * Application Layer Protocol Negotiation (ALPN)\n   *\n   * Throw an exception if openssl does not support NPN\n   *\n   * @param protoName      Name of the protocol (not guaranteed to be\n   *                       null terminated); will be set to nullptr if\n   *                       the client did not negotiate a protocol.\n   *                       Note: the AsyncSSLSocket retains ownership\n   *                       of this string.\n   * @param protoNameLen   Length of the name.\n   * @param protoType      Whether this was an NPN or ALPN negotiation\n   */\n  virtual void getSelectedNextProtocol(\n      const unsigned char** protoName, unsigned* protoLen) const;\n\n  /**\n   * Get the name of the protocol selected by the client during\n   * Next Protocol Negotiation (NPN) or Application Layer Protocol Negotiation\n   * (ALPN)\n   *\n   * @param protoName      Name of the protocol (not guaranteed to be\n   *                       null terminated); will be set to nullptr if\n   *                       the client did not negotiate a protocol.\n   *                       Note: the AsyncSSLSocket retains ownership\n   *                       of this string.\n   * @param protoNameLen   Length of the name.\n   * @param protoType      Whether this was an NPN or ALPN negotiation\n   * @return false if openssl does not support NPN\n   */\n  virtual bool getSelectedNextProtocolNoThrow(\n      const unsigned char** protoName, unsigned* protoLen) const;\n\n  /**\n   * Determine if the session specified during setSSLSession was reused\n   * or if the server rejected it and issued a new session.\n   */\n  virtual bool getSSLSessionReused() const;\n\n  /**\n   * true if the session was resumed using session ID\n   */\n  bool sessionIDResumed() const { return sessionIDResumed_; }\n\n  void setSessionIDResumed(bool resumed) { sessionIDResumed_ = resumed; }\n\n  /**\n   * Get the negociated cipher name for this SSL connection.\n   * Returns the cipher used or the constant value \"NONE\" when no SSL session\n   * has been established.\n   */\n  virtual const char* getNegotiatedCipherName() const;\n\n  /**\n   * Get the server name for this SSL connection. Returns the SNI sent in the\n   * ClientHello, if enableClientHelloParsing() was called.\n   *\n   * Returns the server name used or the constant value \"NONE\" when no SSL\n   * session has been established.\n   * If openssl has no SNI support, throw AsyncSocketException.\n   */\n  const char* getSSLServerName() const;\n\n  /**\n   * Get the server name for this SSL connection.\n   * Returns the server name used or the constant value \"NONE\" when no SSL\n   * session has been established.\n   * If openssl has no SNI support, return \"NONE\"\n   */\n  const char* getSSLServerNameNoThrow() const;\n\n  /**\n   * Get the SSL version for this connection.\n   * Possible return values are SSL2_VERSION, SSL3_VERSION, TLS1_VERSION,\n   * with hexa representations 0x200, 0x300, 0x301,\n   * or 0 if no SSL session has been established.\n   */\n  int getSSLVersion() const;\n\n  /**\n   * Get the signature algorithm used in the cert that is used for this\n   * connection.\n   */\n  const char* getSSLCertSigAlgName() const;\n\n  /**\n   * Get the certificate size used for this SSL connection.\n   */\n  int getSSLCertSize() const;\n\n  void attachEventBase(EventBase* eventBase) override {\n    AsyncSocket::attachEventBase(eventBase);\n    handshakeTimeout_.attachEventBase(eventBase);\n    connectionTimeout_.attachEventBase(eventBase);\n  }\n\n  void detachEventBase() override {\n    AsyncSocket::detachEventBase();\n    handshakeTimeout_.detachEventBase();\n    connectionTimeout_.detachEventBase();\n  }\n\n  bool isDetachable() const override {\n    return AsyncSocket::isDetachable() && !handshakeTimeout_.isScheduled();\n  }\n\n  virtual void attachTimeoutManager(TimeoutManager* manager) {\n    handshakeTimeout_.attachTimeoutManager(manager);\n  }\n\n  virtual void detachTimeoutManager() {\n    handshakeTimeout_.detachTimeoutManager();\n  }\n\n  /**\n   * This function will set the SSL context for this socket to the\n   * argument. This should only be used on client SSL Sockets that have\n   * already called detachSSLContext();\n   */\n  void attachSSLContext(const std::shared_ptr<const folly::SSLContext>& ctx);\n\n  /**\n   * Detaches the SSL context for this socket.\n   */\n  void detachSSLContext();\n\n  /**\n   * Returns the original folly::SSLContext associated with this socket.\n   *\n   * Suitable for use in AsyncSSLSocket constructor to construct a new\n   * AsyncSSLSocket using an existing socket's context.\n   *\n   * switchServerSSLContext() does not affect this return value.\n   */\n  const std::shared_ptr<const folly::SSLContext>& getSSLContext() const {\n    return ctx_;\n  }\n\n  /**\n   * Switch the SSLContext to continue the SSL handshake.\n   * It can only be used in server mode.\n   */\n  void switchServerSSLContext(\n      const std::shared_ptr<const folly::SSLContext>& handshakeCtx);\n\n  /**\n   * Did server recognize/support the tlsext_hostname in Client Hello?\n   * It can only be used in client mode.\n   *\n   * @return true - tlsext_hostname is matched by the server\n   *         false - tlsext_hostname is not matched or\n   *                 is not supported by server\n   */\n  bool isServerNameMatch() const;\n\n  /**\n   * Set the SNI hostname that we'll advertise to the server in the\n   * ClientHello message.\n   */\n  void setServerName(std::string serverName) noexcept;\n\n  void timeoutExpired(std::chrono::milliseconds timeout) noexcept;\n\n  /**\n   * Get the list of supported ciphers sent by the client in the client's\n   * preference order.\n   */\n  void getSSLClientCiphers(\n      std::string& clientCiphers, bool convertToString = true) const;\n\n  /**\n   * Get the list of compression methods sent by the client in TLS Hello.\n   */\n  std::string getSSLClientComprMethods() const;\n\n  /**\n   * Get the list of TLS extensions sent by the client in the TLS Hello.\n   */\n  std::string getSSLClientExts() const;\n\n  std::string getSSLClientSigAlgs() const;\n\n  /**\n   * Get the list of versions in the supported versions extension (used to\n   * negotiate TLS 1.3).\n   */\n  std::string getSSLClientSupportedVersions() const;\n\n  std::string getSSLAlertsReceived() const;\n\n  /*\n   * Save an optional alert message generated during certificate verify\n   */\n  void setSSLCertVerificationAlert(std::string alert);\n\n  std::string getSSLCertVerificationAlert() const;\n\n  /**\n   * Get the list of shared ciphers between the server and the client.\n   * Works well for only SSLv2, not so good for SSLv3 or TLSv1.\n   */\n  void getSSLSharedCiphers(std::string& sharedCiphers) const;\n\n  /**\n   * Get the list of ciphers supported by the server in the server's\n   * preference order.\n   */\n  void getSSLServerCiphers(std::string& serverCiphers) const;\n\n  /**\n   * Get the list of next protocols sent from the client. The protocols are\n   * directly as the client passed them and may be arbitrary byte sequences\n   * of arbitrary length.\n   */\n  const std::vector<std::string>& getClientAlpns() const;\n\n  /**\n   * Method to check if peer verfication is set.\n   *\n   * @return true if peer verification is required.\n   */\n  bool needsPeerVerification() const;\n\n  static int getSSLExDataIndex();\n  static AsyncSSLSocket* getFromSSL(const SSL* ssl);\n  static int bioWrite(BIO* b, const char* in, int inl);\n  static int bioRead(BIO* b, char* out, int outl);\n  void resetClientHelloParsing(SSL* ssl);\n  static void parseClientAlpns(\n      AsyncSSLSocket* sock,\n      folly::io::Cursor& cursor,\n      uint16_t& extensionDataLength);\n  static void clientHelloParsingCallback(\n      int written,\n      int version,\n      int contentType,\n      const void* buf,\n      size_t len,\n      SSL* ssl,\n      void* arg);\n  static const char* getSSLServerNameFromSSL(SSL* ssl);\n\n  // For unit-tests\n  ssl::ClientHelloInfo* getClientHelloInfo() const {\n    return clientHelloInfo_.get();\n  }\n\n  /**\n   * Returns the time taken to complete a handshake.\n   */\n  virtual std::chrono::nanoseconds getHandshakeTime() const {\n    return handshakeEndTime_ - handshakeStartTime_;\n  }\n\n  void setMinWriteSize(size_t minWriteSize) { minWriteSize_ = minWriteSize; }\n\n  size_t getMinWriteSize() const { return minWriteSize_; }\n\n  const AsyncTransportCertificate* getPeerCertificate() const override;\n  const AsyncTransportCertificate* getSelfCertificate() const override;\n\n  /**\n   * Force AsyncSSLSocket object to cache local and peer socket addresses.\n   * If called with \"true\" before connect() this function forces full local\n   * and remote socket addresses to be cached in the socket object and available\n   * through getLocalAddress()/getPeerAddress() methods even after the socket is\n   * closed.\n   */\n  void forceCacheAddrOnFailure(bool force) { cacheAddrOnFailure_ = force; }\n\n  const std::string& getSessionKey() const { return sessionKey_; }\n\n  void setSessionKey(std::string sessionKey) {\n    sessionKey_ = std::move(sessionKey);\n  }\n\n  void setCertCacheHit(bool hit) { certCacheHit_ = hit; }\n\n  bool getCertCacheHit() const { return certCacheHit_; }\n\n  bool sessionResumptionAttempted() const {\n    return sessionResumptionAttempted_;\n  }\n\n  /**\n   * If the SSL socket was used to connect as well\n   * as establish an SSL connection, this gives the total\n   * timeout for the connect + SSL connection that was\n   * set.\n   */\n  std::chrono::milliseconds getTotalConnectTimeout() const {\n    return totalConnectTimeout_;\n  }\n\n  // This can be called for OpenSSL 1.1.0 async operation finishes\n  void setAsyncOperationFinishCallback(std::unique_ptr<ReadCallback> cb) {\n    asyncOperationFinishCallback_ = std::move(cb);\n  }\n\n  // Only enable if security negotiation is deferred\n  // zero copy is not supported by openssl.\n  bool setZeroCopy(bool enable) override {\n    if (sslState_ == SSLStateEnum::STATE_UNENCRYPTED) {\n      return AsyncSocket::setZeroCopy(enable);\n    }\n    return false;\n  }\n\n  const char* getNegotiatedGroup() const;\n\n  void ensureSSL();\n\n private:\n  /**\n   * Handle the return from invoking SSL_accept\n   */\n  void handleReturnFromSSLAccept(int ret);\n\n  void init();\n\n  ReadResult performReadSingle(void* buf, const size_t buflen);\n\n  // Need to clean this up during a cancel if callback hasn't fired yet.\n  AsyncSSLSocketConnector* allocatedConnectCallback_;\n\n protected:\n  /**\n   * Protected destructor.\n   *\n   * Users of AsyncSSLSocket must never delete it directly.  Instead, invoke\n   * destroy() instead.  (See the documentation in DelayedDestruction.h for\n   * more details.)\n   */\n  ~AsyncSSLSocket() override;\n\n  // Inherit event notification methods from AsyncSocket except\n  // the following.\n  void handleRead() noexcept override;\n  void handleWrite() noexcept override;\n  void handleAccept() noexcept;\n  void handleConnect() noexcept override;\n\n  void invalidState(HandshakeCB* callback);\n  bool willBlock(\n      int ret, int* sslErrorOut, unsigned long* errErrorOut) noexcept;\n\n  void checkForImmediateRead() noexcept override;\n  // AsyncSocket calls this at the wrong time for SSL\n  void handleInitialReadWrite() noexcept override {}\n\n  WriteResult interpretSSLError(int rc, int error);\n  ReadResult performReadMsg(\n      struct ::msghdr&, AsyncReader::ReadCallback::ReadMode) override;\n  WriteResult performWrite(\n      const iovec* vec,\n      uint32_t count,\n      WriteFlags flags,\n      uint32_t* countWritten,\n      uint32_t* partialWritten,\n      WriteRequestTag writeTag) override;\n\n  ssize_t performWriteIovec(\n      const iovec* vec,\n      uint32_t count,\n      WriteFlags flags,\n      uint32_t* countWritten,\n      uint32_t* partialWritten);\n\n  // Virtual wrapper around SSL_write, solely for testing/mockability\n  virtual int sslWriteImpl(SSL* ssl, const void* buf, int n) {\n    return SSL_write(ssl, buf, n);\n  }\n\n  // Virtual wrapper around SSL_get_error, solely for testing/mockability\n  virtual int sslGetErrorImpl(const SSL* s, int ret_code) {\n    return SSL_get_error(s, ret_code);\n  }\n\n  /**\n   * Apply verification options passed to sslConn/sslAccept or those set\n   * in the underlying SSLContext object.\n   *\n   * @param ssl pointer to the SSL object on which verification options will be\n   * applied. If verifyPeer_ was explicitly set either via sslConn/sslAccept,\n   * those options override the settings in the underlying SSLContext.\n   */\n  bool applyVerificationOptions(const ssl::SSLUniquePtr& ssl);\n\n  /**\n   * Sets up SSL with a custom write bio which intercepts all writes.\n   *\n   * @return true, if succeeds and false if there is an error creating the bio.\n   */\n  bool setupSSLBio();\n\n  // Inherit error handling methods from AsyncSocket, plus the following.\n  void failHandshake(const char* fn, const AsyncSocketException& ex);\n\n  void invokeHandshakeErr(const AsyncSocketException& ex);\n  void invokeHandshakeCB();\n\n  void invokeConnectErr(const AsyncSocketException& ex) override;\n  void invokeConnectSuccess() override;\n  void scheduleConnectTimeout() override;\n\n  void startSSLConnect();\n\n  static void sslInfoCallback(const SSL* ssl, int where, int ret);\n\n  void createSSL();\n\n  // Whether the current write to the socket should use MSG_MORE.\n  bool corkCurrentWrite_{false};\n  // SSL related members.\n  bool server_{false};\n  // Used to prevent client-initiated renegotiation.  Note that AsyncSSLSocket\n  // doesn't fully support renegotiation, so we could just fail all attempts\n  // to enforce this.  Once it is supported, we should make it an option\n  // to disable client-initiated renegotiation.\n  bool handshakeComplete_{false};\n  bool renegotiateAttempted_{false};\n  SSLStateEnum sslState_{SSLStateEnum::STATE_UNINIT};\n  std::shared_ptr<const folly::SSLContext> ctx_;\n  // Callback for SSL_accept() or SSL_connect()\n  HandshakeCB* handshakeCallback_{nullptr};\n  std::shared_ptr<const CertificateIdentityVerifier>\n      certificateIdentityVerifier_;\n  ssl::SSLUniquePtr ssl_;\n  Timeout handshakeTimeout_;\n  Timeout connectionTimeout_;\n\n  // WriteFlags last passed to performWrite\n  WriteFlags currWriteFlags_{};\n\n  // Number of bytes to write before final byte\n  // See AsyncSSLSocket::performWrite for details\n  folly::Optional<size_t> currBytesToFinalByte_;\n\n  // Try to avoid calling SSL_write() for buffers smaller than this.\n  // It doesn't take effect when it is 0.\n  size_t minWriteSize_{1500};\n\n  std::shared_ptr<const folly::SSLContext> handshakeCtx_;\n  std::string tlsextHostname_;\n\n  // a key that can be used for caching the established session\n  std::string sessionKey_;\n\n  folly::SSLContext::SSLVerifyPeerEnum verifyPeer_{\n      folly::SSLContext::SSLVerifyPeerEnum::USE_CTX};\n\n  // Callback for SSL_CTX_set_verify()\n  static int sslVerifyCallback(int preverifyOk, X509_STORE_CTX* ctx);\n\n  bool parseClientHello_{false};\n  bool cacheAddrOnFailure_{false};\n  bool certCacheHit_{false};\n  std::unique_ptr<ssl::ClientHelloInfo> clientHelloInfo_;\n  std::vector<std::pair<char, StringPiece>> alertsReceived_;\n\n  // Time taken to complete the ssl handshake.\n  std::chrono::steady_clock::time_point handshakeStartTime_;\n  std::chrono::steady_clock::time_point handshakeEndTime_;\n  std::chrono::milliseconds handshakeConnectTimeout_{0};\n  std::chrono::milliseconds totalConnectTimeout_{0};\n\n  std::string sslVerificationAlert_;\n\n  std::string encodedAlpn_;\n\n  bool sessionResumptionAttempted_{false};\n  // whether the SSL session was resumed using session ID or not\n  bool sessionIDResumed_{false};\n  // This can be called for OpenSSL 1.1.0 async operation finishes\n  std::unique_ptr<ReadCallback> asyncOperationFinishCallback_;\n  // Whether this socket is currently waiting on SSL_accept\n  bool waitingOnAccept_{false};\n  // Manages the session for the socket\n  folly::ssl::SSLSessionManager sslSessionManager_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncServerSocket.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include <folly/io/async/AsyncServerSocket.h>\n\n#include <sys/types.h>\n\n#include <cerrno>\n#include <cstring>\n\n#include <folly/FileUtil.h>\n#include <folly/GLog.h>\n#include <folly/Portability.h>\n#include <folly/SocketAddress.h>\n#include <folly/String.h>\n#include <folly/detail/SocketFastOpen.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/NotificationQueue.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Unistd.h>\n\nnamespace folly {\n\n#ifndef TCP_SAVE_SYN\n#define TCP_SAVE_SYN 27\n#endif\n\n#ifndef TCP_SAVED_SYN\n#define TCP_SAVED_SYN 28\n#endif\n\nstatic constexpr bool msgErrQueueSupported =\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    true;\n#else\n    false;\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n\nstatic constexpr bool kSupportReflectTos = kIsLinuxActual;\n\nAsyncServerSocket::AcceptCallback::~AcceptCallback() = default;\n\nconst uint32_t AsyncServerSocket::kDefaultMaxAcceptAtOnce;\nconst uint32_t AsyncServerSocket::kDefaultCallbackAcceptAtOnce;\nconst uint32_t AsyncServerSocket::kDefaultMaxMessagesInQueue;\n\nvoid AsyncServerSocket::RemoteAcceptor::start(\n    EventBase* eventBase, uint32_t maxAtOnce) {\n  queue_.setMaxReadAtOnce(maxAtOnce);\n\n  eventBase->runInEventBaseThread([eventBase, this]() {\n    callback_->acceptStarted();\n    queue_.startConsuming(eventBase);\n  });\n}\n\nvoid AsyncServerSocket::RemoteAcceptor::stop(\n    EventBase* eventBase, AcceptCallback* callback) {\n  eventBase->runInEventBaseThread([callback, this]() {\n    callback->acceptStopped();\n    delete this;\n  });\n}\n\nAtomicNotificationQueueTaskStatus AsyncServerSocket::NewConnMessage::operator()(\n    RemoteAcceptor& acceptor) noexcept {\n  if (isExpired()) {\n    closeNoInt(fd);\n    if (acceptor.connectionEventCallback_) {\n      auto queueTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(\n          deadline - timeBeforeEnqueue);\n      acceptor.connectionEventCallback_->onConnectionDropped(\n          fd,\n          clientAddr,\n          fmt::format(\n              \"Exceeded deadline for accepting connection socket ({} ms)\",\n              queueTimeout.count()));\n    }\n    return AtomicNotificationQueueTaskStatus::DISCARD;\n  }\n  if (acceptor.connectionEventCallback_) {\n    acceptor.connectionEventCallback_->onConnectionDequeuedByAcceptorCallback(\n        fd, clientAddr);\n  }\n  acceptor.callback_->connectionAccepted(fd, clientAddr, {timeBeforeEnqueue});\n  return AtomicNotificationQueueTaskStatus::CONSUMED;\n}\n\nAtomicNotificationQueueTaskStatus AsyncServerSocket::ErrorMessage::operator()(\n    RemoteAcceptor& acceptor) noexcept {\n  auto ex = make_exception_wrapper<std::runtime_error>(msg);\n  acceptor.callback_->acceptError(std::move(ex));\n  return AtomicNotificationQueueTaskStatus::CONSUMED;\n}\n\n/*\n * AsyncServerSocket::BackoffTimeout\n */\nclass AsyncServerSocket::BackoffTimeout : public AsyncTimeout {\n public:\n  // Disallow copy, move, and default constructors.\n  BackoffTimeout(BackoffTimeout&&) = delete;\n  explicit BackoffTimeout(AsyncServerSocket* socket)\n      : AsyncTimeout(socket->getEventBase()), socket_(socket) {}\n\n  void timeoutExpired() noexcept override { socket_->backoffTimeoutExpired(); }\n\n private:\n  AsyncServerSocket* socket_;\n};\n\n/*\n * AsyncServerSocket methods\n */\n\nAsyncServerSocket::AsyncServerSocket(EventBase* eventBase)\n    : eventBase_(eventBase),\n      accepting_(false),\n      maxAcceptAtOnce_(kDefaultMaxAcceptAtOnce),\n      maxNumMsgsInQueue_(kDefaultMaxMessagesInQueue),\n      acceptRateAdjustSpeed_(0),\n      acceptRate_(1),\n      lastAccepTimestamp_(std::chrono::steady_clock::now()),\n      numDroppedConnections_(0),\n      callbackIndex_(0),\n      backoffTimeout_(nullptr),\n      callbacks_(),\n      napiIdToCallback_(),\n      keepAliveEnabled_(true),\n      closeOnExec_(true) {\n  disableTransparentTls();\n}\n\nvoid AsyncServerSocket::setShutdownSocketSet(\n    const std::weak_ptr<ShutdownSocketSet>& wNewSS) {\n  const auto newSS = wNewSS.lock();\n  const auto shutdownSocketSet = wShutdownSocketSet_.lock();\n\n  if (shutdownSocketSet == newSS) {\n    return;\n  }\n\n  if (shutdownSocketSet) {\n    for (auto& h : sockets_) {\n      shutdownSocketSet->remove(h.socket_);\n    }\n  }\n\n  if (newSS) {\n    for (auto& h : sockets_) {\n      newSS->add(h.socket_);\n    }\n  }\n\n  wShutdownSocketSet_ = wNewSS;\n}\n\nAsyncServerSocket::~AsyncServerSocket() {\n  assert(callbacks_.empty());\n  assert(napiIdToCallback_.empty());\n}\n\nint AsyncServerSocket::stopAccepting(int shutdownFlags) {\n  int result = 0;\n  for (auto& handler : sockets_) {\n    VLOG(10) << \"AsyncServerSocket::stopAccepting \" << this << handler.socket_;\n  }\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  // When destroy is called, unregister and close the socket immediately.\n  accepting_ = false;\n\n  // Close the sockets in reverse order as they were opened to avoid\n  // the condition where another process concurrently tries to open\n  // the same port, succeed to bind the first socket but fails on the\n  // second because it hasn't been closed yet.\n  for (; !sockets_.empty(); sockets_.pop_back()) {\n    auto& handler = sockets_.back();\n    handler.unregisterHandler();\n    if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) {\n      shutdownSocketSet->close(handler.socket_);\n    } else if (shutdownFlags >= 0) {\n      result = shutdownNoInt(handler.socket_, shutdownFlags);\n      pendingCloseSockets_.push_back(handler.socket_);\n    } else {\n      closeNoInt(handler.socket_);\n    }\n  }\n\n  // Destroy the backoff timout.  This will cancel it if it is running.\n  delete backoffTimeout_;\n  backoffTimeout_ = nullptr;\n\n  // Close all of the callback queues to notify them that they are being\n  // destroyed.  No one should access the AsyncServerSocket any more once\n  // destroy() is called.  However, clear out callbacks_ before invoking the\n  // accept callbacks just in case.  This will potentially help us detect the\n  // bug if one of the callbacks calls addAcceptCallback() or\n  // removeAcceptCallback().\n  std::vector<CallbackInfo> callbacksCopy;\n  callbacks_.swap(callbacksCopy);\n  napiIdToCallback_.clear();\n  localCallbackIndex_ = -1;\n  for (const auto& callback : callbacksCopy) {\n    // consumer may not be set if we are running in primary event base\n    if (callback.consumer) {\n      DCHECK(callback.eventBase);\n      callback.consumer->stop(callback.eventBase, callback.callback);\n    } else {\n      DCHECK(callback.callback);\n      callback.callback->acceptStopped();\n    }\n  }\n\n  return result;\n}\n\nvoid AsyncServerSocket::destroy() {\n  stopAccepting();\n  for (auto s : pendingCloseSockets_) {\n    closeNoInt(s);\n  }\n  // Then call DelayedDestruction::destroy() to take care of\n  // whether or not we need immediate or delayed destruction\n  DelayedDestruction::destroy();\n}\n\nvoid AsyncServerSocket::attachEventBase(EventBase* eventBase) {\n  assert(eventBase_ == nullptr);\n  eventBase->dcheckIsInEventBaseThread();\n\n  eventBase_ = eventBase;\n  for (auto& handler : sockets_) {\n    handler.attachEventBase(eventBase);\n  }\n}\n\nvoid AsyncServerSocket::detachEventBase() {\n  assert(eventBase_ != nullptr);\n  eventBase_->dcheckIsInEventBaseThread();\n  assert(!accepting_);\n\n  eventBase_ = nullptr;\n  for (auto& handler : sockets_) {\n    handler.detachEventBase();\n  }\n}\n\nvoid AsyncServerSocket::useExistingSockets(\n    const std::vector<NetworkSocket>& fds) {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  if (!sockets_.empty()) {\n    throw std::invalid_argument(\n        \"cannot call useExistingSocket() on a \"\n        \"AsyncServerSocket that already has a socket\");\n  }\n\n  for (auto fd : fds) {\n    // Set addressFamily_ from this socket.\n    // Note that the socket may not have been bound yet, but\n    // setFromLocalAddress() will still work and get the correct address family.\n    // We will update addressFamily_ again anyway if bind() is called later.\n    SocketAddress address;\n    address.setFromLocalAddress(fd);\n\n#if defined(__linux__)\n    if (noTransparentTls_) {\n      // Ignore return value, errors are ok\n      __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;\n      netops::setsockopt(\n          fd, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));\n    }\n#endif\n\n    setupSocket(fd, address.getFamily());\n    sockets_.emplace_back(eventBase_, fd, this, address.getFamily());\n    sockets_.back().changeHandlerFD(fd);\n  }\n}\n\nvoid AsyncServerSocket::useExistingSocket(NetworkSocket fd) {\n  useExistingSockets({fd});\n}\n\nvoid AsyncServerSocket::bindSocket(\n    NetworkSocket fd,\n    const SocketAddress& address,\n    bool isExistingSocket,\n    const std::string& ifName) {\n  sockaddr_storage addrStorage;\n  address.getAddress(&addrStorage);\n  auto saddr = reinterpret_cast<sockaddr*>(&addrStorage);\n\n#if defined(__linux__)\n  if (!ifName.empty() &&\n      netops::setsockopt(\n          fd, SOL_SOCKET, SO_BINDTODEVICE, ifName.c_str(), ifName.length())) {\n    auto errnoCopy = errno;\n    if (!isExistingSocket) {\n      closeNoInt(fd);\n    }\n    folly::throwSystemErrorExplicit(\n        errnoCopy, \"failed to bind to device: \" + ifName);\n  }\n#else\n  (void)ifName;\n#endif\n\n  if (netops::bind(fd, saddr, address.getActualSize()) != 0) {\n    if (errno != EINPROGRESS) {\n      // Get a copy of errno so that it is not overwritten by subsequent calls.\n      auto errnoCopy = errno;\n      if (!isExistingSocket) {\n        closeNoInt(fd);\n      }\n      folly::throwSystemErrorExplicit(\n          errnoCopy,\n          \"failed to bind to async server socket: \" + address.describe());\n    }\n  }\n\n#if defined(__linux__)\n  if (noTransparentTls_) {\n    // Ignore return value, errors are ok\n    __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;\n    netops::setsockopt(\n        fd, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));\n  }\n#endif\n\n  // If we just created this socket, update the EventHandler and set socket_\n  if (!isExistingSocket) {\n    sockets_.emplace_back(eventBase_, fd, this, address.getFamily());\n  }\n}\n\nbool AsyncServerSocket::setZeroCopy(bool enable) {\n  if (msgErrQueueSupported) {\n    // save the enable flag here\n    zeroCopyVal_ = enable;\n    int val = enable ? 1 : 0;\n    size_t num = 0;\n    for (auto& s : sockets_) {\n      int ret = netops::setsockopt(\n          s.socket_, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val));\n\n      num += (0 == ret) ? 1 : 0;\n    }\n\n    return num != 0;\n  }\n\n  return false;\n}\n\nvoid AsyncServerSocket::bindInternal(\n    const SocketAddress& address, const std::string& ifName) {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  // useExistingSocket() may have been called to initialize socket_ already.\n  // However, in the normal case we need to create a new socket now.\n  // Don't set socket_ yet, so that socket_ will remain uninitialized if an\n  // error occurs.\n  NetworkSocket fd;\n  if (sockets_.empty()) {\n    fd = createSocket(address.getFamily());\n  } else if (sockets_.size() == 1) {\n    if (address.getFamily() != sockets_[0].addressFamily_) {\n      throw std::invalid_argument(\n          \"Attempted to bind address to socket with \"\n          \"different address family\");\n    }\n    fd = sockets_[0].socket_;\n  } else {\n    throw std::invalid_argument(\"Attempted to bind to multiple fds\");\n  }\n\n  bindSocket(fd, address, !sockets_.empty(), ifName);\n}\n\nvoid AsyncServerSocket::bind(const SocketAddress& address) {\n  bindInternal(address, \"\");\n}\n\nvoid AsyncServerSocket::bind(\n    const SocketAddress& address, const std::string& ifName) {\n  bindInternal(address, ifName);\n}\n\nvoid AsyncServerSocket::bind(\n    const std::vector<IPAddress>& ipAddresses, uint16_t port) {\n  if (ipAddresses.empty()) {\n    throw std::invalid_argument(\"No ip addresses were provided\");\n  }\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  for (const IPAddress& ipAddress : ipAddresses) {\n    SocketAddress address(ipAddress.toFullyQualified(), port);\n    auto fd = createSocket(address.getFamily());\n\n    bindSocket(fd, address, false, \"\");\n  }\n  if (sockets_.empty()) {\n    throw std::runtime_error(\n        \"did not bind any async server socket for port and addresses\");\n  }\n}\n\nvoid AsyncServerSocket::bind(\n    const std::vector<IPAddressIfNamePair>& addresses, uint16_t port) {\n  if (addresses.empty()) {\n    throw std::invalid_argument(\"No ip addresses were provided\");\n  }\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  for (const auto& addr : addresses) {\n    SocketAddress address(addr.first.toFullyQualified(), port);\n    auto fd = createSocket(address.getFamily());\n\n    bindSocket(fd, address, false, addr.second);\n  }\n  if (sockets_.empty()) {\n    throw std::runtime_error(\n        \"did not bind any async server socket for port and addresses\");\n  }\n}\n\nvoid AsyncServerSocket::bind(uint16_t port) {\n  struct addrinfo hints, *res0;\n  char sport[sizeof(\"65536\")];\n\n  memset(&hints, 0, sizeof(hints));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;\n  snprintf(sport, sizeof(sport), \"%u\", port);\n\n  // On Windows the value we need to pass to bind to all available\n  // addresses is an empty string. Everywhere else, it's nullptr.\n  constexpr const char* kWildcardNode = kIsWindows ? \"\" : nullptr;\n  if (getaddrinfo(kWildcardNode, sport, &hints, &res0)) {\n    throw std::invalid_argument(\n        \"Attempted to bind address to socket with \"\n        \"bad getaddrinfo\");\n  }\n\n  SCOPE_EXIT {\n    freeaddrinfo(res0);\n  };\n\n  auto setupAddress = [&](struct addrinfo* res) {\n    auto s = netops::socket(res->ai_family, res->ai_socktype, res->ai_protocol);\n    // IPv6/IPv4 may not be supported by the kernel\n    if (s == NetworkSocket() && errno == EAFNOSUPPORT) {\n      return;\n    }\n    CHECK_NE(s, NetworkSocket());\n\n    try {\n      setupSocket(s, res->ai_family);\n    } catch (...) {\n      closeNoInt(s);\n      throw;\n    }\n\n    if (res->ai_family == AF_INET6) {\n      int v6only = 1;\n      CHECK(\n          0 ==\n          netops::setsockopt(\n              s, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)));\n    }\n\n    // Bind to the socket\n    if (netops::bind(s, res->ai_addr, socklen_t(res->ai_addrlen)) != 0) {\n      folly::throwSystemError(\n          errno,\n          \"failed to bind to async server socket for port \",\n          SocketAddress::getPortFrom(res->ai_addr),\n          \" family \",\n          SocketAddress::getFamilyNameFrom(res->ai_addr, \"<unknown>\"));\n    }\n\n#if defined(__linux__)\n    if (noTransparentTls_) {\n      // Ignore return value, errors are ok\n      __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;\n      netops::setsockopt(\n          s, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));\n    }\n#endif\n\n    SocketAddress address;\n    address.setFromLocalAddress(s);\n\n    sockets_.emplace_back(eventBase_, s, this, address.getFamily());\n  };\n\n  const int kNumTries = 25;\n  for (int tries = 1; true; tries++) {\n    // Prefer AF_INET6 addresses. RFC 3484 mandates that getaddrinfo\n    // should return IPv6 first and then IPv4 addresses, but glibc's\n    // getaddrinfo(nullptr) with AI_PASSIVE returns:\n    // - 0.0.0.0 (IPv4-only)\n    // - :: (IPv6+IPv4) in this order\n    // See: https://sourceware.org/bugzilla/show_bug.cgi?id=9981\n    for (struct addrinfo* res = res0; res; res = res->ai_next) {\n      if (res->ai_family == AF_INET6) {\n        setupAddress(res);\n      }\n    }\n\n    // If port == 0, then we should try to bind to the same port on ipv4 and\n    // ipv6.  So if we did bind to ipv6, figure out that port and use it.\n    if (sockets_.size() == 1 && port == 0) {\n      SocketAddress address;\n      address.setFromLocalAddress(sockets_.back().socket_);\n      snprintf(sport, sizeof(sport), \"%u\", address.getPort());\n      freeaddrinfo(res0);\n      CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0));\n    }\n\n    try {\n      for (struct addrinfo* res = res0; res; res = res->ai_next) {\n        if (res->ai_family != AF_INET6) {\n          setupAddress(res);\n        }\n      }\n    } catch (const std::system_error&) {\n      // If we can't bind to the same port on ipv4 as ipv6 when using\n      // port=0 then we will retry again before giving up after\n      // kNumTries attempts.  We do this by closing the sockets that\n      // were opened, then restarting from scratch.\n      if (port == 0 && !sockets_.empty() && tries != kNumTries) {\n        for (const auto& socket : sockets_) {\n          if (socket.socket_ == NetworkSocket()) {\n            continue;\n          } else if (\n              const auto shutdownSocketSet = wShutdownSocketSet_.lock()) {\n            shutdownSocketSet->close(socket.socket_);\n          } else {\n            closeNoInt(socket.socket_);\n          }\n        }\n        sockets_.clear();\n        snprintf(sport, sizeof(sport), \"%u\", port);\n        freeaddrinfo(res0);\n        CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0));\n        continue;\n      }\n\n      throw;\n    }\n\n    break;\n  }\n\n  if (sockets_.empty()) {\n    throw std::runtime_error(\"did not bind any async server socket for port\");\n  }\n}\n\nvoid AsyncServerSocket::setEnableReuseAddr(bool enable) {\n  enableReuseAddr_ = enable;\n  for (auto& handler : sockets_) {\n    if (handler.socket_ == NetworkSocket()) {\n      continue;\n    }\n\n    int val = (enable) ? 1 : 0;\n    if (netops::setsockopt(\n            handler.socket_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) !=\n        0) {\n      auto errnoCopy = errno;\n      LOG(ERROR) << \"failed to set SO_REUSEADDR on async server socket \"\n                 << errnoCopy;\n      folly::throwSystemErrorExplicit(\n          errnoCopy, \"failed to set SO_REUSEADDR on async server socket\");\n    }\n  }\n}\n\nvoid AsyncServerSocket::setIPFreebind(bool enable) {\n  // We defer setting this option to setupSocket to ensure it is done pre-bind.\n  ipFreebind_ = enable;\n}\n\nvoid AsyncServerSocket::listen(int backlog) {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  // Start listening\n  for (auto& handler : sockets_) {\n    if (netops::listen(handler.socket_, backlog) == -1) {\n      folly::throwSystemError(errno, \"failed to listen on async server socket\");\n    }\n  }\n}\n\nvoid AsyncServerSocket::getAddress(SocketAddress* addressReturn) const {\n  CHECK(!sockets_.empty());\n  VLOG_IF(2, sockets_.size() > 1)\n      << \"Warning: getAddress() called and multiple addresses available (\"\n      << sockets_.size() << \"). Returning only the first one.\";\n\n  addressReturn->setFromLocalAddress(sockets_[0].socket_);\n}\n\nstd::vector<SocketAddress> AsyncServerSocket::getAddresses() const {\n  CHECK(!sockets_.empty());\n  auto tsaVec = std::vector<SocketAddress>(sockets_.size());\n  auto tsaIter = tsaVec.begin();\n  for (const auto& socket : sockets_) {\n    (tsaIter++)->setFromLocalAddress(socket.socket_);\n  }\n  return tsaVec;\n}\n\nvoid AsyncServerSocket::addAcceptCallback(\n    AcceptCallback* callback, EventBase* eventBase, uint32_t maxAtOnce) {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  // If this is the first accept callback and we are supposed to be accepting,\n  // start accepting once the callback is installed.\n  bool runStartAccepting = accepting_ && callbacks_.empty();\n\n  callbacks_.emplace_back(callback, eventBase);\n  int napiId = -1;\n  if (eventBase) {\n    napiId = eventBase->getBackend()->getNapiId();\n    if (napiId != -1) {\n      napiIdToCallback_.emplace(napiId, CallbackInfo(callback, eventBase));\n    }\n  }\n\n  SCOPE_SUCCESS {\n    // If this is the first accept callback and we are supposed to be accepting,\n    // start accepting.\n    if (runStartAccepting) {\n      startAccepting();\n    }\n  };\n\n  if (!eventBase) {\n    // Run in AsyncServerSocket's eventbase; notify that we are\n    // starting to accept connections\n    callback->acceptStarted();\n    return;\n  }\n\n  // Start the remote acceptor.\n  //\n  // It would be nice if we could avoid starting the remote acceptor if\n  // eventBase == eventBase_.  However, that would cause issues if\n  // detachEventBase() and attachEventBase() were ever used to change the\n  // primary EventBase for the server socket.  Therefore we require the caller\n  // to specify a nullptr EventBase if they want to ensure that the callback is\n  // always invoked in the primary EventBase, and to be able to invoke that\n  // callback more efficiently without having to use a notification queue.\n  RemoteAcceptor* acceptor = nullptr;\n  try {\n    acceptor = new RemoteAcceptor(callback, connectionEventCallback_);\n    acceptor->start(eventBase, maxAtOnce);\n  } catch (...) {\n    callbacks_.pop_back();\n    delete acceptor;\n    throw;\n  }\n  callbacks_.back().consumer = acceptor;\n  if (napiId != -1) {\n    if (auto it = napiIdToCallback_.find(napiId);\n        it != napiIdToCallback_.end()) {\n      it->second.consumer = acceptor;\n    }\n  }\n  if (localCallbackIndex_ < 0 && callbacks_.back().eventBase == eventBase_) {\n    localCallbackIndex_ = static_cast<int>(callbacks_.size() - 1);\n  }\n}\n\nvoid AsyncServerSocket::removeAcceptCallback(\n    AcceptCallback* callback, EventBase* eventBase) {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  // Find the matching AcceptCallback.\n  // We just do a simple linear search; we don't expect removeAcceptCallback()\n  // to be called frequently, and we expect there to only be a small number of\n  // callbacks anyway.\n  auto it = callbacks_.begin();\n  uint32_t n = 0;\n  while (true) {\n    if (it == callbacks_.end()) {\n      throw std::runtime_error(\n          \"AsyncServerSocket::removeAcceptCallback(): \"\n          \"accept callback not found\");\n    }\n    if (it->callback == callback &&\n        (it->eventBase == eventBase || eventBase == nullptr)) {\n      break;\n    }\n    ++it;\n    ++n;\n  }\n\n  // If the matching AcceptCallback is also tied to a specific NAPI ID, erase it\n  // as well.\n  for (auto mapIt = napiIdToCallback_.begin();\n       mapIt != napiIdToCallback_.end();) {\n    auto& cb = mapIt->second;\n    if (cb.callback == callback &&\n        (cb.eventBase == eventBase || eventBase == nullptr)) {\n      mapIt = napiIdToCallback_.erase(mapIt);\n    } else {\n      ++mapIt;\n    }\n  }\n\n  // Remove this callback from callbacks_.\n  //\n  // Do this before invoking the acceptStopped() callback, in case\n  // acceptStopped() invokes one of our methods that examines callbacks_.\n  //\n  // Save a copy of the CallbackInfo first.\n  CallbackInfo info(*it);\n  callbacks_.erase(it);\n  if (n < callbackIndex_) {\n    // We removed an element before callbackIndex_.  Move callbackIndex_ back\n    // one step, since things after n have been shifted back by 1.\n    --callbackIndex_;\n  } else {\n    // We removed something at or after callbackIndex_.\n    // If we removed the last element and callbackIndex_ was pointing at it,\n    // we need to reset callbackIndex_ to 0.\n    if (callbackIndex_ >= callbacks_.size()) {\n      callbackIndex_ = 0;\n    }\n  }\n\n  if (info.consumer) {\n    // consumer could be nullptr is we run callbacks in primary event\n    // base\n    DCHECK(info.eventBase);\n    info.consumer->stop(info.eventBase, info.callback);\n  } else {\n    // callback invoked in the primary event base, just call directly\n    DCHECK(info.callback);\n    callback->acceptStopped();\n  }\n\n  // If we are supposed to be accepting but the last accept callback\n  // was removed, unregister for events until a callback is added.\n  if (accepting_ && callbacks_.empty()) {\n    for (auto& handler : sockets_) {\n      handler.unregisterHandler();\n    }\n  }\n}\n\nvoid AsyncServerSocket::startAccepting() {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  accepting_ = true;\n  if (callbacks_.empty()) {\n    // We can't actually begin accepting if no callbacks are defined.\n    // Wait until a callback is added to start accepting.\n    return;\n  }\n\n  for (auto& handler : sockets_) {\n    if (!handler.registerHandler(EventHandler::READ | EventHandler::PERSIST)) {\n      throw std::runtime_error(\"failed to register for accept events\");\n    }\n  }\n}\n\nvoid AsyncServerSocket::pauseAccepting() {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n  accepting_ = false;\n  for (auto& handler : sockets_) {\n    handler.unregisterHandler();\n  }\n\n  // If we were in the accept backoff state, disable the backoff timeout\n  if (backoffTimeout_) {\n    backoffTimeout_->cancelTimeout();\n  }\n}\n\nNetworkSocket AsyncServerSocket::createSocket(int family) {\n  auto fd = netops::socket(family, SOCK_STREAM, 0);\n  if (fd == NetworkSocket()) {\n    folly::throwSystemError(errno, \"error creating async server socket\");\n  }\n\n  try {\n    setupSocket(fd, family);\n  } catch (...) {\n    closeNoInt(fd);\n    throw;\n  }\n  return fd;\n}\n\n/**\n * Enable/Disable TOS reflection for the server socket\n * If enabled, the 'accepted' connections will reflect the\n * TOS derived from the client's connect request\n */\nvoid AsyncServerSocket::setTosReflect(bool enable) {\n  if (!kSupportReflectTos || !enable) {\n    tosReflect_ = false;\n    return;\n  }\n\n  for (auto& handler : sockets_) {\n    if (handler.socket_ == NetworkSocket()) {\n      continue;\n    }\n\n    int val = (enable) ? 1 : 0;\n    int ret = netops::setsockopt(\n        handler.socket_, IPPROTO_TCP, TCP_SAVE_SYN, &val, sizeof(val));\n\n    if (ret == 0) {\n      VLOG(10) << \"Enabled SYN save for socket \" << handler.socket_;\n    } else {\n      folly::throwSystemError(errno, \"failed to enable TOS reflect\");\n    }\n  }\n  tosReflect_ = true;\n}\n\nvoid AsyncServerSocket::setListenerTos(uint32_t tos) {\n  if (!kSupportReflectTos || tos == 0) {\n    listenerTos_ = 0;\n    return;\n  }\n\n  for (auto& handler : sockets_) {\n    if (handler.socket_ == NetworkSocket()) {\n      continue;\n    }\n\n    const auto proto =\n        (handler.addressFamily_ == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6;\n    const auto optName =\n        (handler.addressFamily_ == AF_INET) ? IP_TOS : IPV6_TCLASS;\n\n    int ret =\n        netops::setsockopt(handler.socket_, proto, optName, &tos, sizeof(tos));\n\n    if (ret == 0) {\n      VLOG(10) << \"Set TOS \" << tos << \" for for socket \" << handler.socket_;\n    } else {\n      folly::throwSystemError(errno, \"failed to set TOS for socket\");\n    }\n  }\n  listenerTos_ = tos;\n}\n\nvoid AsyncServerSocket::setupSocket(NetworkSocket fd, int family) {\n  // Put the socket in non-blocking mode\n  if (netops::set_socket_non_blocking(fd) != 0) {\n    folly::throwSystemError(errno, \"failed to put socket in non-blocking mode\");\n  }\n\n  // Set reuseaddr to avoid 2MSL delay on server restart\n  int one = 1;\n  // AF_UNIX does not support SO_REUSEADDR, setting this would confuse Windows\n  if (family != AF_UNIX && enableReuseAddr_ &&\n      netops::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) !=\n          0) {\n    auto errnoCopy = errno;\n    // This isn't a fatal error; just log an error message and continue\n    LOG(ERROR) << \"failed to set SO_REUSEADDR on async server socket \"\n               << errnoCopy;\n  }\n\n  // Set reuseport to support multiple accept threads\n  int zero = 0;\n  if (reusePortEnabled_ &&\n      netops::setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) !=\n          0) {\n    auto errnoCopy = errno;\n    LOG(ERROR) << \"failed to set SO_REUSEPORT on async server socket \"\n               << errnoStr(errnoCopy);\n#ifdef WIN32\n    folly::throwSystemErrorExplicit(\n        errnoCopy, \"failed to set SO_REUSEPORT on async server socket\");\n#else\n    SocketAddress address;\n    address.setFromLocalAddress(fd);\n    folly::throwSystemErrorExplicit(\n        errnoCopy,\n        \"failed to set SO_REUSEPORT on async server socket: \" +\n            address.describe());\n#endif\n  }\n\n  // Set keepalive as desired\n  if (netops::setsockopt(\n          fd,\n          SOL_SOCKET,\n          SO_KEEPALIVE,\n          (keepAliveEnabled_) ? &one : &zero,\n          sizeof(int)) != 0) {\n    auto errnoCopy = errno;\n    LOG(ERROR) << \"failed to set SO_KEEPALIVE on async server socket: \"\n               << errnoStr(errnoCopy);\n  }\n\n  // Setup FD_CLOEXEC flag\n  if (closeOnExec_ && (-1 == netops::set_socket_close_on_exec(fd))) {\n    auto errnoCopy = errno;\n    LOG(ERROR) << \"failed to set FD_CLOEXEC on async server socket: \"\n               << errnoStr(errnoCopy);\n  }\n\n  // Set TCP nodelay if available, MAC OS X Hack\n  // See http://lists.danga.com/pipermail/memcached/2005-March/001240.html\n#ifndef TCP_NOPUSH\n#if FOLLY_HAVE_VSOCK\n  auto isVsock = family == AF_VSOCK;\n#else\n  auto isVsock = false;\n#endif\n\n  if (family != AF_UNIX && !isVsock) {\n    if (netops::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) !=\n        0) {\n      auto errnoCopy = errno;\n      // This isn't a fatal error; just log an error message and continue\n      LOG(ERROR) << \"failed to set TCP_NODELAY on async server socket: \"\n                 << errnoStr(errnoCopy);\n    }\n  }\n#else\n  (void)family; // to avoid unused parameter warning\n#endif\n\n#if FOLLY_ALLOW_TFO\n  if (tfo_ && detail::tfo_enable(fd, tfoMaxQueueSize_) != 0) {\n    auto errnoCopy = errno;\n    // This isn't a fatal error; just log an error message and continue\n    LOG(WARNING) << \"failed to set TCP_FASTOPEN on async server socket: \"\n                 << folly::errnoStr(errnoCopy);\n  }\n#endif\n\n  if (zeroCopyVal_) {\n    int val = 1;\n    int ret =\n        netops::setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val));\n    if (ret) {\n      auto errnoCopy = errno;\n      LOG(WARNING) << \"failed to set SO_ZEROCOPY on async server socket: \"\n                   << folly::errnoStr(errnoCopy);\n    }\n  }\n\n#if defined(__linux__)\n  if (ipFreebind_ &&\n      netops::setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(int)) != 0) {\n    auto errnoCopy = errno;\n    LOG(ERROR) << \"failed to set IP_FREEBIND on async server socket: \"\n               << errnoStr(errnoCopy);\n  }\n#endif\n\n  if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) {\n    shutdownSocketSet->add(fd);\n  }\n}\n\nvoid AsyncServerSocket::handlerReady(\n    uint16_t /* events */,\n    NetworkSocket fd,\n    sa_family_t addressFamily) noexcept {\n  assert(!callbacks_.empty());\n  DestructorGuard dg(this);\n\n  // Only accept up to maxAcceptAtOnce_ connections at a time,\n  // to avoid starving other I/O handlers using this EventBase.\n  for (uint32_t n = 0; n < maxAcceptAtOnce_; ++n) {\n    SocketAddress address;\n\n    sockaddr_storage addrStorage = {};\n    socklen_t addrLen = sizeof(addrStorage);\n    auto saddr = reinterpret_cast<sockaddr*>(&addrStorage);\n\n    // In some cases, accept() doesn't seem to update these correctly.\n    saddr->sa_family = addressFamily;\n    if (addressFamily == AF_UNIX) {\n      addrLen = sizeof(struct sockaddr_un);\n    }\n\n    // Accept a new client socket\n#if FOLLY_HAVE_ACCEPT4\n    auto clientSocket = NetworkSocket::fromFd(\n        accept4(fd.toFd(), saddr, &addrLen, SOCK_NONBLOCK));\n#else\n    auto clientSocket = netops::accept(fd, saddr, &addrLen);\n#endif\n\n    address.setFromSockaddr(saddr, addrLen);\n\n    if (clientSocket != NetworkSocket() && connectionEventCallback_) {\n      connectionEventCallback_->onConnectionAccepted(clientSocket, address);\n    }\n\n    // Connection accepted, get the SYN packet from the client if\n    // TOS reflect is enabled\n    if (kSupportReflectTos && clientSocket != NetworkSocket() && tosReflect_) {\n      std::array<uint32_t, 64> buffer;\n      socklen_t len = sizeof(buffer);\n      int ret = netops::getsockopt(\n          clientSocket, IPPROTO_TCP, TCP_SAVED_SYN, &buffer, &len);\n\n      if (ret == 0) {\n        uint32_t tosWord = folly::Endian::big(buffer[0]);\n        if (addressFamily == AF_INET6) {\n          tosWord = (tosWord & 0x0FC00000) >> 20;\n          // Set the TOS on the return socket only if it is non-zero\n          if (tosWord) {\n            ret = netops::setsockopt(\n                clientSocket,\n                IPPROTO_IPV6,\n                IPV6_TCLASS,\n                &tosWord,\n                sizeof(tosWord));\n          }\n        } else if (addressFamily == AF_INET) {\n          tosWord = (tosWord & 0x00FC0000) >> 16;\n          if (tosWord) {\n            ret = netops::setsockopt(\n                clientSocket, IPPROTO_IP, IP_TOS, &tosWord, sizeof(tosWord));\n          }\n        }\n\n        if (ret != 0) {\n          LOG(ERROR) << \"Unable to set TOS for accepted socket \"\n                     << clientSocket;\n        }\n      } else {\n        LOG(ERROR) << \"Unable to get SYN packet for accepted socket \"\n                   << clientSocket;\n      }\n    }\n\n    std::chrono::time_point<std::chrono::steady_clock> nowMs =\n        std::chrono::steady_clock::now();\n    auto timeSinceLastAccept = std::max<int64_t>(\n        0,\n        nowMs.time_since_epoch().count() -\n            lastAccepTimestamp_.time_since_epoch().count());\n    lastAccepTimestamp_ = nowMs;\n    if (acceptRate_ < 1) {\n      acceptRate_ *= 1 + acceptRateAdjustSpeed_ * timeSinceLastAccept;\n      if (acceptRate_ >= 1) {\n        acceptRate_ = 1;\n      } else if (rand() > acceptRate_ * RAND_MAX) {\n        ++numDroppedConnections_;\n        if (clientSocket != NetworkSocket()) {\n          closeNoInt(clientSocket);\n          if (connectionEventCallback_) {\n            connectionEventCallback_->onConnectionDropped(\n                clientSocket,\n                address,\n                fmt::format(\n                    \"Server is rate limiting new connections. Current accept rate is {}\",\n                    acceptRate_));\n          }\n        }\n        continue;\n      }\n    }\n\n    if (clientSocket == NetworkSocket()) {\n      if (errno == EAGAIN) {\n        // No more sockets to accept right now.\n        // Check for this code first, since it's the most common.\n        return;\n      } else if (errno == EMFILE || errno == ENFILE) {\n        // We're out of file descriptors.  Perhaps we're accepting connections\n        // too quickly. Pause accepting briefly to back off and give the server\n        // a chance to recover.\n        LOG(ERROR) << \"accept failed: out of file descriptors; entering accept \"\n                      \"back-off state\";\n        enterBackoff();\n\n        // Dispatch the error message\n        dispatchError(\"accept() failed\", errno);\n      } else {\n        dispatchError(\"accept() failed\", errno);\n      }\n      if (connectionEventCallback_) {\n        connectionEventCallback_->onConnectionAcceptError(errno);\n      }\n      return;\n    }\n\n#if !FOLLY_HAVE_ACCEPT4\n    // Explicitly set the new connection to non-blocking mode\n    if (netops::set_socket_non_blocking(clientSocket) != 0) {\n      closeNoInt(clientSocket);\n      std::string errorMsg =\n          \"Failed to set accepted socket to non-blocking mode.\";\n      dispatchError(errorMsg.c_str(), errno);\n      if (connectionEventCallback_) {\n        connectionEventCallback_->onConnectionDropped(\n            clientSocket,\n            address,\n            fmt::format(\"{} errno ({})\", std::move(errorMsg), errno));\n      }\n      return;\n    }\n#endif\n\n    // Inform the callback about the new connection\n    dispatchSocket(clientSocket, std::move(address));\n\n    // If we aren't accepting any more, break out of the loop\n    if (!accepting_ || callbacks_.empty()) {\n      break;\n    }\n  }\n}\n\nvoid AsyncServerSocket::dispatchSocket(\n    NetworkSocket socket, SocketAddress&& address) {\n  uint32_t startingIndex = callbackIndex_;\n\n  auto timeBeforeEnqueue = std::chrono::steady_clock::now();\n\n  // Short circuit if the callback is in the primary EventBase thread\n\n  CallbackInfo* info = nextCallback(socket);\n  if (info->eventBase == nullptr || info->eventBase == this->eventBase_) {\n    info->callback->connectionAccepted(socket, address, {timeBeforeEnqueue});\n    return;\n  }\n\n  const SocketAddress addr(address);\n  // Create a message to send over the notification queue\n  auto queueTimeout = *queueTimeout_;\n  std::chrono::steady_clock::time_point deadline;\n  if (queueTimeout.count() != 0) {\n    deadline = timeBeforeEnqueue + queueTimeout;\n  }\n\n  NewConnMessage msg{socket, std::move(address), deadline, timeBeforeEnqueue};\n\n  // Loop until we find a free queue to write to\n  while (true) {\n    if (info->consumer->getQueue().tryPutMessage(\n            std::move(msg), maxNumMsgsInQueue_)) {\n      if (connectionEventCallback_) {\n        connectionEventCallback_->onConnectionEnqueuedForAcceptorCallback(\n            socket, addr);\n      }\n      // Success! return.\n      return;\n    }\n\n    // We couldn't add to queue.  Fall through to below\n\n    if (acceptRateAdjustSpeed_ > 0) {\n      // aggressively decrease accept rate when in trouble\n      static const double kAcceptRateDecreaseSpeed = 0.1;\n      acceptRate_ *= 1 - kAcceptRateDecreaseSpeed;\n    }\n\n    if (callbackIndex_ == startingIndex) {\n      // The notification queue was full\n      // We can't really do anything at this point other than close the socket.\n      //\n      // This should only happen if a user's service is behaving extremely\n      // badly and none of the EventBase threads are looping fast enough to\n      // process the incoming connections.  If the service is overloaded, it\n      // should use pauseAccepting() to temporarily back off accepting new\n      // connections, before they reach the point where their threads can't\n      // even accept new messages.\n      ++numDroppedConnections_;\n      std::string errorMsg =\n          \"Failed to dispatch newly accepted socket: all accept callback queues are full\";\n      FB_LOG_EVERY_MS(ERROR, 1000) << errorMsg;\n      closeNoInt(socket);\n      if (connectionEventCallback_) {\n        connectionEventCallback_->onConnectionDropped(socket, addr, errorMsg);\n      }\n      return;\n    }\n\n    info = nextCallback(socket);\n  }\n}\n\nvoid AsyncServerSocket::dispatchError(const char* msgstr, int errnoValue) {\n  uint32_t startingIndex = callbackIndex_;\n  CallbackInfo* info = nextCallback();\n\n  // Create a message to send over the notification queue\n  ErrorMessage msg{errnoValue, msgstr};\n\n  while (true) {\n    // Short circuit if the callback is in the primary EventBase thread\n    if (info->eventBase == nullptr || info->eventBase == this->eventBase_) {\n      auto ex = make_exception_wrapper<std::runtime_error>(\n          std::string(msgstr) + folly::to<std::string>(errnoValue));\n      info->callback->acceptError(std::move(ex));\n      return;\n    }\n\n    if (info->consumer->getQueue().tryPutMessage(\n            std::move(msg), maxNumMsgsInQueue_)) {\n      return;\n    }\n    // Fall through and try another callback\n\n    if (callbackIndex_ == startingIndex) {\n      // The notification queues for all of the callbacks were full.\n      // We can't really do anything at this point.\n      FB_LOG_EVERY_MS(ERROR, 1000)\n          << \"failed to dispatch accept error: all accept\"\n          << \" callback queues are full: error msg:  \" << msg.msg << \": \"\n          << errnoValue;\n      return;\n    }\n    info = nextCallback();\n  }\n}\n\nvoid AsyncServerSocket::enterBackoff() {\n  // If this is the first time we have entered the backoff state,\n  // allocate backoffTimeout_.\n  if (backoffTimeout_ == nullptr) {\n    try {\n      backoffTimeout_ = new BackoffTimeout(this);\n    } catch (const std::bad_alloc&) {\n      // Man, we couldn't even allocate the timer to re-enable accepts.\n      // We must be in pretty bad shape.  Don't pause accepting for now,\n      // since we won't be able to re-enable ourselves later.\n      LOG(ERROR) << \"failed to allocate AsyncServerSocket backoff\"\n                 << \" timer; unable to temporarly pause accepting\";\n      if (connectionEventCallback_) {\n        connectionEventCallback_->onBackoffError();\n      }\n      return;\n    }\n  }\n\n  // For now, we simply pause accepting for 1 second.\n  //\n  // We could add some smarter backoff calculation here in the future.  (e.g.,\n  // start sleeping for longer if we keep hitting the backoff frequently.)\n  // Typically the user needs to figure out why the server is overloaded and\n  // fix it in some other way, though.  The backoff timer is just a simple\n  // mechanism to try and give the connection processing code a little bit of\n  // breathing room to catch up, and to avoid just spinning and failing to\n  // accept over and over again.\n  const uint32_t timeoutMS = 1000;\n  if (!backoffTimeout_->scheduleTimeout(timeoutMS)) {\n    LOG(ERROR) << \"failed to schedule AsyncServerSocket backoff timer;\"\n               << \"unable to temporarly pause accepting\";\n    if (connectionEventCallback_) {\n      connectionEventCallback_->onBackoffError();\n    }\n    return;\n  }\n\n  // The backoff timer is scheduled to re-enable accepts.\n  // Go ahead and disable accepts for now.  We leave accepting_ set to true,\n  // since that tracks the desired state requested by the user.\n  for (auto& handler : sockets_) {\n    handler.unregisterHandler();\n  }\n  if (connectionEventCallback_) {\n    connectionEventCallback_->onBackoffStarted();\n  }\n}\n\nvoid AsyncServerSocket::backoffTimeoutExpired() {\n  // accepting_ should still be true.\n  // If pauseAccepting() was called while in the backoff state it will cancel\n  // the backoff timeout.\n  assert(accepting_);\n  // We can't be detached from the EventBase without being paused\n  assert(eventBase_ != nullptr);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  // If all of the callbacks were removed, we shouldn't re-enable accepts\n  if (callbacks_.empty()) {\n    if (connectionEventCallback_) {\n      connectionEventCallback_->onBackoffEnded();\n    }\n    return;\n  }\n\n  // Register the handler.\n  for (auto& handler : sockets_) {\n    if (!handler.registerHandler(EventHandler::READ | EventHandler::PERSIST)) {\n      // We're hosed.  We could just re-schedule backoffTimeout_ to\n      // re-try again after a little bit.  However, we don't want to\n      // loop retrying forever if we can't re-enable accepts.  Just\n      // abort the entire program in this state; things are really bad\n      // and restarting the entire server is probably the best remedy.\n      LOG(ERROR)\n          << \"failed to re-enable AsyncServerSocket accepts after backoff; \"\n          << \"crashing now\";\n      abort();\n    }\n  }\n  if (connectionEventCallback_) {\n    connectionEventCallback_->onBackoffEnded();\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncServerSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <limits.h>\n#include <stddef.h>\n\n#include <chrono>\n#include <exception>\n#include <functional>\n#include <memory>\n#include <variant>\n#include <vector>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/SocketAddress.h>\n#include <folly/String.h>\n#include <folly/io/ShutdownSocketSet.h>\n#include <folly/io/async/AsyncSocketBase.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventBaseAtomicNotificationQueue.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/net/NetOps.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/observer/Observer.h>\n#include <folly/portability/Sockets.h>\n\n// Due to the way kernel headers are included, this may or may not be defined.\n// Number pulled from 3.10 kernel headers.\n#ifndef SO_REUSEPORT\n#define SO_REUSEPORT 15\n#endif\n\n#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED\n#define FOLLY_SO_TTLS_TRUSTED 206\n#endif\n\n#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED\n#define FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED 1\n#endif\n\nnamespace folly {\n\n/**\n * AsyncServerSocket is a listening socket that asynchronously informs a\n * callback whenever a new connection has been accepted.\n *\n * Unlike most async interfaces that always invoke their callback in the same\n * EventBase thread, AsyncServerSocket is unusual in that it can distribute\n * the callbacks across multiple EventBase threads.\n\n * This supports a common use case for network servers to distribute incoming\n * connections across a number of EventBase threads.  (Servers typically run\n * with one EventBase thread per CPU.)\n\n * Despite being able to invoke callbacks in multiple EventBase threads,\n * AsyncServerSocket still has one \"primary\" EventBase.  Operations that\n * modify the AsyncServerSocket state may only be performed from the primary\n * EventBase thread.\n *\n */\nclass AsyncServerSocket : public DelayedDestruction, public AsyncSocketBase {\n public:\n  using UniquePtr = std::unique_ptr<AsyncServerSocket, Destructor>;\n  using CallbackAssignFunction =\n      std::function<int(AsyncServerSocket*, NetworkSocket)>;\n  // Disallow copy, move, and default construction.\n  AsyncServerSocket(AsyncServerSocket&&) = delete;\n\n  /**\n   * A callback interface to get notified of client socket events.\n   *\n   * The ConnectionEventCallback implementations need to be thread-safe as the\n   * callbacks may be called from different threads.\n   */\n  class ConnectionEventCallback {\n   public:\n    virtual ~ConnectionEventCallback() = default;\n\n    /**\n     * onConnectionAccepted() is called right after a client connection\n     * is accepted using the system accept()/accept4() APIs.\n     */\n    virtual void onConnectionAccepted(\n        const NetworkSocket socket, const SocketAddress& addr) noexcept = 0;\n\n    /**\n     * onConnectionAcceptError() is called when an error occurred accepting\n     * a connection.\n     */\n    virtual void onConnectionAcceptError(const int err) noexcept = 0;\n\n    /**\n     * onConnectionDropped() is called when a connection is dropped,\n     * probably because of some error encountered.\n     */\n    virtual void onConnectionDropped(\n        const NetworkSocket socket,\n        const SocketAddress& addr,\n        const std::string& errorMsg = \"\") noexcept = 0;\n\n    /**\n     * onConnectionEnqueuedForAcceptorCallback() is called when the\n     * connection is successfully enqueued for an AcceptCallback to pick up.\n     */\n    virtual void onConnectionEnqueuedForAcceptorCallback(\n        const NetworkSocket socket, const SocketAddress& addr) noexcept = 0;\n\n    /**\n     * onConnectionDequeuedByAcceptorCallback() is called when the\n     * connection is successfully dequeued by an AcceptCallback.\n     */\n    virtual void onConnectionDequeuedByAcceptorCallback(\n        const NetworkSocket socket, const SocketAddress& addr) noexcept = 0;\n\n    /**\n     * onBackoffStarted is called when the socket has successfully started\n     * backing off accepting new client sockets.\n     */\n    virtual void onBackoffStarted() noexcept = 0;\n\n    /**\n     * onBackoffEnded is called when the backoff period has ended and the socket\n     * has successfully resumed accepting new connections if there is any\n     * AcceptCallback registered.\n     */\n    virtual void onBackoffEnded() noexcept = 0;\n\n    /**\n     * onBackoffError is called when there is an error entering backoff\n     */\n    virtual void onBackoffError() noexcept = 0;\n  };\n\n  class AcceptCallback {\n   public:\n    struct AcceptInfo {\n      std::chrono::steady_clock::time_point timeBeforeEnqueue;\n    };\n\n    virtual ~AcceptCallback();\n\n    /**\n     * connectionAccepted() is called whenever a new client connection is\n     * received.\n     *\n     * The AcceptCallback will remain installed after connectionAccepted()\n     * returns.\n     *\n     * @param fd          The newly accepted client socket.  The AcceptCallback\n     *                    assumes ownership of this socket, and is responsible\n     *                    for closing it when done.  The newly accepted file\n     *                    descriptor will have already been put into\n     *                    non-blocking mode.\n     * @param clientAddr  A reference to a SocketAddress struct containing the\n     *                    client's address.  This struct is only guaranteed to\n     *                    remain valid until connectionAccepted() returns.\n     * @param info        A simple structure that contains auxiliary information\n     *                    about this accepted socket, for example, when it's\n     *                    getting pushed into the waiting queue.\n     */\n    virtual void connectionAccepted(\n        NetworkSocket fd,\n        const SocketAddress& clientAddr,\n        AcceptInfo info) noexcept = 0;\n\n    /**\n     * acceptError() is called if an error occurs while accepting.\n     *\n     * The AcceptCallback will remain installed even after an accept error,\n     * as the errors are typically somewhat transient, such as being out of\n     * file descriptors.  The server socket must be explicitly stopped if you\n     * wish to stop accepting after an error.\n     *\n     * @param ex  An exception representing the error.\n     */\n\n    // TODO(T81599451): Remove the acceptError(const std::exception&)\n    // after migration and remove compile warning suppression.\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Woverloaded-virtual\")\n    virtual void acceptError(exception_wrapper ew) noexcept {\n      auto ex = ew.get_exception<std::exception>();\n      FOLLY_SAFE_CHECK(ex, \"no exception\");\n      acceptError(*ex);\n    }\n\n    virtual void acceptError(const std::exception& /* unused */) noexcept {}\n    FOLLY_POP_WARNING\n\n    /**\n     * acceptStarted() will be called in the callback's EventBase thread\n     * after this callback has been added to the AsyncServerSocket.\n     *\n     * acceptStarted() will be called before any calls to connectionAccepted()\n     * or acceptError() are made on this callback.\n     *\n     * acceptStarted() makes it easier for callbacks to perform initialization\n     * inside the callback thread.  (The call to addAcceptCallback() must\n     * always be made from the AsyncServerSocket's primary EventBase thread.\n     * acceptStarted() provides a hook that will always be invoked in the\n     * callback's thread.)\n     *\n     * Note that the call to acceptStarted() is made once the callback is\n     * added, regardless of whether or not the AsyncServerSocket is actually\n     * accepting at the moment.  acceptStarted() will be called even if the\n     * AsyncServerSocket is paused when the callback is added (including if\n     * the initial call to startAccepting() on the AsyncServerSocket has not\n     * been made yet).\n     */\n    virtual void acceptStarted() noexcept {}\n\n    /**\n     * acceptStopped() will be called when this AcceptCallback is removed from\n     * the AsyncServerSocket, or when the AsyncServerSocket is destroyed,\n     * whichever occurs first.\n     *\n     * No more calls to connectionAccepted() or acceptError() will be made\n     * after acceptStopped() is invoked.\n     */\n    virtual void acceptStopped() noexcept {}\n  };\n\n  static const uint32_t kDefaultMaxAcceptAtOnce = 30;\n  static const uint32_t kDefaultCallbackAcceptAtOnce = 5;\n  static const uint32_t kDefaultMaxMessagesInQueue = 1024;\n\n  /**\n   * Create a new AsyncServerSocket with the specified EventBase.\n   *\n   * @param eventBase  The EventBase to use for driving the asynchronous I/O.\n   *                   If this parameter is nullptr, attachEventBase() must be\n   *                   called before this socket can begin accepting\n   *                   connections.\n   */\n  explicit AsyncServerSocket(EventBase* eventBase = nullptr);\n\n  /**\n   * Helper function to create a shared_ptr<AsyncServerSocket>.\n   *\n   * This passes in the correct destructor object, since AsyncServerSocket's\n   * destructor is protected and cannot be invoked directly.\n   *\n   * @param evb  The EventBase to use for driving the asynchronous I/O.\n   */\n  static std::shared_ptr<AsyncServerSocket> newSocket(\n      EventBase* evb = nullptr) {\n    return std::shared_ptr<AsyncServerSocket>(\n        new AsyncServerSocket(evb), Destructor());\n  }\n\n  void setShutdownSocketSet(const std::weak_ptr<ShutdownSocketSet>& wNewSS);\n\n  /**\n   * Destroy the socket.\n   *\n   * AsyncServerSocket::destroy() must be called to destroy the socket.\n   * The normal destructor is private, and should not be invoked directly.\n   * This prevents callers from deleting a AsyncServerSocket while it is\n   * invoking a callback.\n   *\n   * destroy() must be invoked from the socket's primary EventBase thread.\n   *\n   * If there are AcceptCallbacks still installed when destroy() is called,\n   * acceptStopped() will be called on these callbacks to notify them that\n   * accepting has stopped.  Accept callbacks being driven by other EventBase\n   * threads may continue to receive new accept callbacks for a brief period of\n   * time after destroy() returns.  They will not receive any more callback\n   * invocations once acceptStopped() is invoked.\n   */\n  void destroy() override;\n\n  /**\n   * Attach this AsyncServerSocket to its primary EventBase.\n   *\n   * This may only be called if the AsyncServerSocket is not already attached\n   * to a EventBase.  The AsyncServerSocket must be attached to a EventBase\n   * before it can begin accepting connections.\n   *\n   * @param eventBase The EventBase to attach to for driving the asynchronous\n   * I/O.\n   */\n  void attachEventBase(EventBase* eventBase);\n\n  /**\n   * Detach the AsyncServerSocket from its primary EventBase.\n   *\n   * detachEventBase() may only be called if the AsyncServerSocket is not\n   * currently accepting connections.\n   */\n  void detachEventBase();\n\n  /**\n   * Get the EventBase used by this socket.\n   */\n  EventBase* getEventBase() const override { return eventBase_; }\n\n  /**\n   * Create a AsyncServerSocket from an existing socket file descriptor.\n   *\n   * useExistingSocket() will cause the AsyncServerSocket to take ownership of\n   * the specified file descriptor, and use it to listen for new connections.\n   * The AsyncServerSocket will close the file descriptor when it is\n   * destroyed.\n   *\n   * useExistingSocket() must be called before bind() or listen().\n   *\n   * The supplied file descriptor will automatically be put into non-blocking\n   * mode.  The caller may have already directly called bind() and possibly\n   * listen on the file descriptor.  If so the caller should skip calling the\n   * corresponding AsyncServerSocket::bind() and listen() methods.\n   *\n   * On error a AsyncSocketException will be thrown and the caller will retain\n   * ownership of the file descriptor.\n   *\n   * @param fd existing socket's file descriptor\n   */\n  void useExistingSocket(NetworkSocket fd);\n\n  /**\n   * Create a AsyncServerSocket from existing socket file descriptors.\n   * @param fds vector of sockets file descriptors\n   */\n  void useExistingSockets(const std::vector<NetworkSocket>& fds);\n\n  /**\n   * Return the underlying file descriptor\n   */\n  std::vector<NetworkSocket> getNetworkSockets() const {\n    std::vector<NetworkSocket> sockets;\n    for (auto& handler : sockets_) {\n      sockets.push_back(handler.socket_);\n    }\n    return sockets;\n  }\n\n  /**\n   * Backwards compatible getSocket\n   *\n   * warns if there are more than one socket\n   */\n  NetworkSocket getNetworkSocket() const {\n    if (sockets_.size() > 1) {\n      VLOG(2) << \"Warning: getSocket can return multiple fds, \"\n              << \"but getSockets was not called, so only returning the first\";\n    }\n    if (sockets_.size() == 0) {\n      return NetworkSocket();\n    } else {\n      return sockets_[0].socket_;\n    }\n  }\n\n  /**\n   * sets the callback assign function\n   */\n  void setCallbackAssignFunction(CallbackAssignFunction func) {\n    callbackAssignFunc_ = std::move(func);\n  }\n\n  /**\n   * enable zerocopy support for the server sockets -\n   * the s = accept sockets inherit it\n   */\n  bool setZeroCopy(bool enable);\n  using IPAddressIfNamePair = std::pair<IPAddress, std::string>;\n  /**\n   * Bind to the specified address.\n   *\n   * This must be called from the primary EventBase thread.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  virtual void bind(const SocketAddress& address);\n\n  /**\n   * Bind to the specified address/if name\n   *\n   * This must be called from the primary EventBase thread.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  virtual void bind(const SocketAddress& address, const std::string& ifName);\n\n  /**\n   * Bind to the specified port for the specified addresses.\n   *\n   * This must be called from the primary EventBase thread.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  virtual void bind(const std::vector<IPAddress>& ipAddresses, uint16_t port);\n\n  /**\n   * Bind to the specified port for the specified addresses/if names.\n   *\n   * This must be called from the primary EventBase thread.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  virtual void bind(\n      const std::vector<IPAddressIfNamePair>& addresses, uint16_t port);\n\n  /**\n   * Bind to the specified port.\n   *\n   * This must be called from the primary EventBase thread.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  virtual void bind(uint16_t port);\n\n  /**\n   * Get the local address to which the socket is bound.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  void getAddress(SocketAddress* addressReturn) const override;\n\n  /**\n   * Get the local address to which the socket is bound.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  SocketAddress getAddress() const {\n    SocketAddress ret;\n    getAddress(&ret);\n    return ret;\n  }\n\n  /**\n   * Get all the local addresses to which the socket is bound.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  std::vector<SocketAddress> getAddresses() const;\n\n  /**\n   * Begin listening for connections.\n   *\n   * This calls ::listen() with the specified backlog.\n   *\n   * Once listen() is invoked the socket will actually be open so that remote\n   * clients may establish connections.  (Clients that attempt to connect\n   * before listen() is called will receive a connection refused error.)\n   *\n   * At least one callback must be set and startAccepting() must be called to\n   * actually begin notifying the accept callbacks of newly accepted\n   * connections.  The backlog parameter controls how many connections the\n   * kernel will accept and buffer internally while the accept callbacks are\n   * paused (or if accepting is enabled but the callbacks cannot keep up).\n   *\n   * bind() must be called before calling listen().\n   * listen() must be called from the primary EventBase thread.\n   *\n   * Throws AsyncSocketException on error.\n   */\n  virtual void listen(int backlog);\n\n  /**\n   * Add an AcceptCallback.\n   *\n   * When a new socket is accepted, one of the AcceptCallbacks will be invoked\n   * with the new socket.  The AcceptCallbacks are invoked in a round-robin\n   * fashion.  This allows the accepted sockets to be distributed among a pool\n   * of threads, each running its own EventBase object.  This is a common model,\n   * since most asynchronous-style servers typically run one EventBase thread\n   * per CPU.\n   *\n   * The EventBase object associated with each AcceptCallback must be running\n   * its loop.  If the EventBase loop is not running, sockets will still be\n   * scheduled for the callback, but the callback cannot actually get invoked\n   * until the loop runs.\n   *\n   * This method must be invoked from the AsyncServerSocket's primary\n   * EventBase thread.\n   *\n   * Note that startAccepting() must be called on the AsyncServerSocket to\n   * cause it to actually start accepting sockets once callbacks have been\n   * installed.\n   *\n   * @param callback   The callback to invoke.\n   * @param eventBase  The EventBase to use to invoke the callback.  This\n   *     parameter may be nullptr, in which case the callback will be invoked in\n   *     the AsyncServerSocket's primary EventBase.\n   * @param maxAtOnce  The maximum number of connections to accept in this\n   *                   callback on a single iteration of the event base loop.\n   *                   This only takes effect when eventBase is non-nullptr.\n   *                   When using a nullptr eventBase for the callback, the\n   *                   setMaxAcceptAtOnce() method controls how many\n   *                   connections the main event base will accept at once.\n   */\n  virtual void addAcceptCallback(\n      AcceptCallback* callback,\n      EventBase* eventBase,\n      uint32_t maxAtOnce = kDefaultCallbackAcceptAtOnce);\n\n  /**\n   * Remove an AcceptCallback.\n   *\n   * This allows a single AcceptCallback to be removed from the round-robin\n   * pool.\n   *\n   * This method must be invoked from the AsyncServerSocket's primary\n   * EventBase thread.  Use EventBase::runInEventBaseThread() to schedule the\n   * operation in the correct EventBase if your code is not in the server\n   * socket's primary EventBase.\n   *\n   * Given that the accept callback is being driven by a different EventBase,\n   * the AcceptCallback may continue to be invoked for a short period of time\n   * after removeAcceptCallback() returns in this thread.  Once the other\n   * EventBase thread receives the notification to stop, it will call\n   * acceptStopped() on the callback to inform it that it is fully stopped and\n   * will not receive any new sockets.\n   *\n   * If the last accept callback is removed while the socket is accepting,\n   * the socket will implicitly pause accepting.  If a callback is later added,\n   * it will resume accepting immediately, without requiring startAccepting()\n   * to be invoked.\n   *\n   * @param callback   The callback to uninstall.\n   * @param eventBase  The EventBase associated with this callback.  This must\n   *     be the same EventBase that was used when the callback was installed\n   *     with addAcceptCallback().\n   */\n  void removeAcceptCallback(AcceptCallback* callback, EventBase* eventBase);\n\n  /**\n   * Begin accepting connctions on this socket.\n   *\n   * bind() and listen() must be called before calling startAccepting().\n   *\n   * When a AsyncServerSocket is initially created, it will not begin\n   * accepting connections until at least one callback has been added and\n   * startAccepting() has been called.  startAccepting() can also be used to\n   * resume accepting connections after a call to pauseAccepting().\n   *\n   * If startAccepting() is called when there are no accept callbacks\n   * installed, the socket will not actually begin accepting until an accept\n   * callback is added.\n   *\n   * This method may only be called from the primary EventBase thread.\n   */\n  virtual void startAccepting();\n\n  /**\n   * Pause accepting connections.\n   *\n   * startAccepting() may be called to resume accepting.\n   *\n   * This method may only be called from the primary EventBase thread.\n   * If there are AcceptCallbacks being driven by other EventBase threads they\n   * may continue to receive callbacks for a short period of time after\n   * pauseAccepting() returns.\n   *\n   * Unlike removeAcceptCallback() or destroy(), acceptStopped() will not be\n   * called on the AcceptCallback objects simply due to a temporary pause.  If\n   * the server socket is later destroyed while paused, acceptStopped() will be\n   * called all of the installed AcceptCallbacks.\n   */\n  void pauseAccepting();\n\n  /**\n   * Shutdown the listen socket and notify all callbacks that accept has\n   * stopped\n   *\n   * This call doesn't close the socket. it invokes shutdown(2) with the\n   * supplied argument.  Passing -1 will close the socket now.  Otherwise, the\n   * close will be delayed until this object is destroyed.\n   *\n   * Only use this if you have reason to pass special flags to shutdown.\n   * Otherwise just destroy the socket.\n   *\n   * This method has no effect when a ShutdownSocketSet option is used.\n   *\n   * Returns the result of shutdown on sockets_[n-1]\n   */\n  int stopAccepting(int shutdownFlags = -1);\n\n  /**\n   * Get the maximum number of connections that will be accepted each time\n   * around the event loop.\n   */\n  uint32_t getMaxAcceptAtOnce() const { return maxAcceptAtOnce_; }\n\n  /**\n   * Set the maximum number of connections that will be accepted each time\n   * around the event loop.\n   *\n   * This provides a very coarse-grained way of controlling how fast the\n   * AsyncServerSocket will accept connections.  If you find that when your\n   * server is overloaded AsyncServerSocket accepts connections more quickly\n   * than your code can process them, you can try lowering this number so that\n   * fewer connections will be accepted each event loop iteration.\n   *\n   * For more explicit control over the accept rate, you can also use\n   * pauseAccepting() to temporarily pause accepting when your server is\n   * overloaded, and then use startAccepting() later to resume accepting.\n   */\n  void setMaxAcceptAtOnce(uint32_t numConns) { maxAcceptAtOnce_ = numConns; }\n\n  /**\n   * Get the duration after which new connection messages will be dropped from\n   * the NotificationQueue if it has not started processing yet.\n   */\n  const folly::observer::AtomicObserver<std::chrono::nanoseconds>&\n  getQueueTimeout() const {\n    return queueTimeout_;\n  }\n\n  /**\n   * Set the duration after which new connection messages will be dropped from\n   * the NotificationQueue if it has not started processing yet.\n   *\n   * This avoids the NotificationQueue from processing messages where the client\n   * socket has probably timed out already, or will time out before a response\n   * can be sent.\n   *\n   * The default value (of 0) means that messages will never expire.\n   */\n  void setQueueTimeout(\n      folly::observer::Observer<std::chrono::nanoseconds> duration) {\n    queueTimeout_ = duration;\n  }\n  void setQueueTimeout(std::chrono::nanoseconds duration) {\n    setQueueTimeout(folly::observer::makeStaticObserver(duration));\n  }\n\n  /**\n   * Get the maximum number of unprocessed messages which a NotificationQueue\n   * can hold.\n   */\n  uint32_t getMaxNumMessagesInQueue() const { return maxNumMsgsInQueue_; }\n\n  /**\n   * Set the maximum number of unprocessed messages in NotificationQueue.\n   * No new message will be sent to that NotificationQueue if there are more\n   * than such number of unprocessed messages in that queue.\n   */\n  void setMaxNumMessagesInQueue(uint32_t num) { maxNumMsgsInQueue_ = num; }\n\n  /**\n   * Get the speed of adjusting connection accept rate.\n   */\n  double getAcceptRateAdjustSpeed() const { return acceptRateAdjustSpeed_; }\n\n  /**\n   * Set the speed of adjusting connection accept rate.\n   */\n  void setAcceptRateAdjustSpeed(double speed) {\n    acceptRateAdjustSpeed_ = speed;\n  }\n\n  /**\n   * Enable/Disable TOS reflection for the server socket\n   */\n  void setTosReflect(bool enable);\n  /**\n   * Get TOS reflection for server socket\n   */\n  bool getTosReflect() { return tosReflect_; }\n\n  /**\n   * Set default TOS for listener socket\n   */\n  void setListenerTos(uint32_t tos);\n\n  /**\n   * Get default TOS for listener socket\n   */\n  uint32_t getListenerTos() const { return listenerTos_; }\n\n  /**\n   * Get the number of connections dropped by the AsyncServerSocket\n   */\n  std::size_t getNumDroppedConnections() const {\n    return numDroppedConnections_;\n  }\n\n  /**\n   * Get the current number of unprocessed messages in NotificationQueue.\n   *\n   * This method must be invoked from the AsyncServerSocket's primary\n   * EventBase thread.  Use EventBase::runInEventBaseThread() to schedule the\n   * operation in the correct EventBase if your code is not in the server\n   * socket's primary EventBase.\n   */\n  int64_t getNumPendingMessagesInQueue() const {\n    if (eventBase_) {\n      eventBase_->dcheckIsInEventBaseThread();\n    }\n    int64_t numMsgs = 0;\n    for (const auto& callback : callbacks_) {\n      if (callback.consumer) {\n        numMsgs += callback.consumer->getQueue().size();\n      }\n    }\n    return numMsgs;\n  }\n\n  /**\n   * Set whether or not SO_KEEPALIVE should be enabled on the server socket\n   * (and thus on all subsequently-accepted connections). By default, keepalive\n   * is enabled.\n   *\n   * Note that TCP keepalive usually only kicks in after the connection has\n   * been idle for several hours. Applications should almost always have their\n   * own, shorter idle timeout.\n   */\n  void setKeepAliveEnabled(bool enabled) {\n    keepAliveEnabled_ = enabled;\n\n    for (auto& handler : sockets_) {\n      if (handler.socket_ == NetworkSocket()) {\n        continue;\n      }\n\n      int val = (enabled) ? 1 : 0;\n      if (netops::setsockopt(\n              handler.socket_, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) !=\n          0) {\n        LOG(ERROR) << \"failed to set SO_KEEPALIVE on async server socket: %s\"\n                   << errnoStr(errno);\n      }\n    }\n  }\n\n  /**\n   * Get whether or not SO_KEEPALIVE is enabled on the server socket.\n   */\n  bool getKeepAliveEnabled() const { return keepAliveEnabled_; }\n\n  /**\n   * Set whether or not SO_REUSEPORT should be enabled on the server socket,\n   * allowing multiple binds to the same port\n   */\n  void setReusePortEnabled(bool enabled) {\n    reusePortEnabled_ = enabled;\n\n    for (auto& handler : sockets_) {\n      if (handler.socket_ == NetworkSocket()) {\n        continue;\n      }\n\n      int val = (enabled) ? 1 : 0;\n      if (netops::setsockopt(\n              handler.socket_, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) !=\n          0) {\n        auto errnoCopy = errno;\n        LOG(ERROR) << \"failed to set SO_REUSEPORT on async server socket \"\n                   << errnoCopy;\n        folly::throwSystemErrorExplicit(\n            errnoCopy, \"failed to set SO_REUSEPORT on async server socket\");\n      }\n    }\n  }\n\n  /**\n   * Set whether or not SO_REUSEADDR should be enabled on the server socket,\n   * allowing multiple sockets binds to the same <address>:<port>\n   * It's enabled by default.\n   */\n  void setEnableReuseAddr(bool enable);\n\n  /**\n   * Get whether or not SO_REUSEPORT is enabled on the server socket.\n   */\n  bool getReusePortEnabled_() const { return reusePortEnabled_; }\n\n  /**\n   * Set whether or not IP_FREEBIND is enabled on the server socket. Only\n   * supported on Linux.\n   *\n   * NOTE: This socket option only makes sense as a pre-bind operation. Setting\n   * it to an existing bound socket will have no effect.\n   */\n  void setIPFreebind(bool enable);\n\n  /**\n   * Get whether or not IP_FREEBIND is enabled on the server socket.\n   */\n  bool getIPFreebindEnabled() const { return ipFreebind_; }\n\n  /**\n   * Set whether or not the socket should close during exec() (FD_CLOEXEC). By\n   * default, this is enabled\n   */\n  void setCloseOnExec(bool closeOnExec) { closeOnExec_ = closeOnExec; }\n\n  /**\n   * Get whether or not FD_CLOEXEC is enabled on the server socket.\n   */\n  bool getCloseOnExec() const { return closeOnExec_; }\n\n  /**\n   * Tries to enable TFO if the machine supports it.\n   */\n  void setTFOEnabled(bool enabled, uint32_t maxTFOQueueSize) {\n    tfo_ = enabled;\n    tfoMaxQueueSize_ = maxTFOQueueSize;\n  }\n\n  /**\n   * Do not attempt the transparent TLS handshake\n   */\n  void disableTransparentTls() { noTransparentTls_ = true; }\n\n  /**\n   * Get whether or not the socket is accepting new connections\n   */\n  bool getAccepting() const { return accepting_; }\n\n  /**\n   * Set the ConnectionEventCallback\n   */\n  void setConnectionEventCallback(\n      ConnectionEventCallback* const connectionEventCallback) {\n    connectionEventCallback_ = connectionEventCallback;\n  }\n\n  /**\n   * Get the ConnectionEventCallback\n   */\n  ConnectionEventCallback* getConnectionEventCallback() const {\n    return connectionEventCallback_;\n  }\n\n  /**\n   * Index of the callback with the same EVB as the current\n   * AsyncServerSocket instance, if any\n   */\n  int getLocalCallbackIndex() const { return localCallbackIndex_; }\n\n protected:\n  /**\n   * Protected destructor.\n   *\n   * Invoke destroy() instead to destroy the AsyncServerSocket.\n   */\n  ~AsyncServerSocket() override;\n\n private:\n  class RemoteAcceptor;\n\n  struct NewConnMessage {\n    NetworkSocket fd;\n    SocketAddress clientAddr;\n    std::chrono::steady_clock::time_point deadline;\n    std::chrono::steady_clock::time_point timeBeforeEnqueue;\n\n    bool isExpired() const {\n      return deadline.time_since_epoch().count() != 0 &&\n          std::chrono::steady_clock::now() > deadline;\n    }\n\n    AtomicNotificationQueueTaskStatus operator()(\n        RemoteAcceptor& acceptor) noexcept;\n  };\n\n  struct ErrorMessage {\n    int err;\n    std::string msg;\n\n    AtomicNotificationQueueTaskStatus operator()(\n        RemoteAcceptor& acceptor) noexcept;\n  };\n\n  using QueueMessage = std::variant<NewConnMessage, ErrorMessage>;\n\n  /**\n   * A class to receive notifications to invoke AcceptCallback objects\n   * in other EventBase threads.\n   *\n   * A RemoteAcceptor object is created for each AcceptCallback that\n   * is installed in a separate EventBase thread.  The RemoteAcceptor\n   * receives notification of new sockets via a NotificationQueue,\n   * and then invokes the AcceptCallback.\n   */\n  class RemoteAcceptor {\n    struct Consumer {\n      AtomicNotificationQueueTaskStatus operator()(\n          QueueMessage&& msg) noexcept {\n        return std::visit(\n            [this](auto&& visitMsg) { return visitMsg(acceptor_); }, msg);\n      }\n\n      explicit Consumer(RemoteAcceptor& acceptor) : acceptor_(acceptor) {}\n      RemoteAcceptor& acceptor_;\n    };\n\n    friend NewConnMessage;\n    friend ErrorMessage;\n\n   public:\n    using Queue = EventBaseAtomicNotificationQueue<QueueMessage, Consumer>;\n\n    explicit RemoteAcceptor(\n        AcceptCallback* callback,\n        ConnectionEventCallback* connectionEventCallback)\n        : callback_(callback),\n          connectionEventCallback_(connectionEventCallback),\n          queue_(Consumer(*this)) {}\n\n    void start(EventBase* eventBase, uint32_t maxAtOnce);\n    void stop(EventBase* eventBase, AcceptCallback* callback);\n\n    Queue& getQueue() { return queue_; }\n\n   private:\n    AcceptCallback* callback_;\n    ConnectionEventCallback* connectionEventCallback_;\n    Queue queue_;\n  };\n\n  /**\n   * A struct to keep track of the callbacks associated with this server\n   * socket.\n   */\n  struct CallbackInfo {\n    CallbackInfo(AcceptCallback* cb, EventBase* evb)\n        : callback(cb), eventBase(evb), consumer(nullptr) {}\n\n    AcceptCallback* callback;\n    EventBase* eventBase;\n\n    RemoteAcceptor* consumer;\n  };\n\n  class BackoffTimeout;\n\n  virtual void handlerReady(\n      uint16_t events, NetworkSocket fd, sa_family_t family) noexcept;\n\n  NetworkSocket createSocket(int family);\n  void setupSocket(NetworkSocket fd, int family);\n  void bindInternal(const SocketAddress& address, const std::string& ifName);\n  void bindSocket(\n      NetworkSocket fd,\n      const SocketAddress& address,\n      bool isExistingSocket,\n      const std::string& ifName);\n  void dispatchSocket(NetworkSocket socket, SocketAddress&& address);\n  void dispatchError(const char* msg, int errnoValue);\n  void enterBackoff();\n  void backoffTimeoutExpired();\n\n  CallbackInfo* nextCallback(NetworkSocket socket = NetworkSocket()) {\n    if (callbackAssignFunc_ && socket != NetworkSocket()) {\n      auto num = callbackAssignFunc_(this, socket);\n      if (num >= 0) {\n        if (auto it = napiIdToCallback_.find(num);\n            it != napiIdToCallback_.end()) {\n          return &it->second;\n        }\n        return &callbacks_[num % callbacks_.size()];\n      }\n    }\n    CallbackInfo* info = &callbacks_[callbackIndex_];\n\n    ++callbackIndex_;\n    if (callbackIndex_ >= callbacks_.size()) {\n      callbackIndex_ = 0;\n    }\n\n    return info;\n  }\n\n  struct ServerEventHandler : public EventHandler {\n    ServerEventHandler(\n        EventBase* eventBase,\n        NetworkSocket socket,\n        AsyncServerSocket* parent,\n        sa_family_t addressFamily)\n        : EventHandler(eventBase, socket),\n          eventBase_(eventBase),\n          socket_(socket),\n          parent_(parent),\n          addressFamily_(addressFamily) {}\n\n    ServerEventHandler(const ServerEventHandler& other)\n        : EventHandler(other.eventBase_, other.socket_),\n          eventBase_(other.eventBase_),\n          socket_(other.socket_),\n          parent_(other.parent_),\n          addressFamily_(other.addressFamily_) {}\n\n    ServerEventHandler& operator=(const ServerEventHandler& other) {\n      if (this != &other) {\n        eventBase_ = other.eventBase_;\n        socket_ = other.socket_;\n        parent_ = other.parent_;\n        addressFamily_ = other.addressFamily_;\n\n        detachEventBase();\n        attachEventBase(other.eventBase_);\n        changeHandlerFD(other.socket_);\n      }\n      return *this;\n    }\n\n    // Inherited from EventHandler\n    void handlerReady(uint16_t events) noexcept override {\n      parent_->handlerReady(events, socket_, addressFamily_);\n    }\n\n    EventBase* eventBase_;\n    NetworkSocket socket_;\n    AsyncServerSocket* parent_;\n    sa_family_t addressFamily_;\n  };\n\n  EventBase* eventBase_;\n  std::vector<ServerEventHandler> sockets_;\n  std::vector<NetworkSocket> pendingCloseSockets_;\n  bool accepting_;\n  uint32_t maxAcceptAtOnce_;\n  uint32_t maxNumMsgsInQueue_;\n  double acceptRateAdjustSpeed_; // 0 to disable auto adjust\n  double acceptRate_;\n  std::chrono::time_point<std::chrono::steady_clock> lastAccepTimestamp_;\n  std::size_t numDroppedConnections_;\n  uint32_t callbackIndex_;\n  BackoffTimeout* backoffTimeout_;\n  std::vector<CallbackInfo> callbacks_;\n  std::unordered_map<unsigned int, CallbackInfo> napiIdToCallback_;\n  CallbackAssignFunction callbackAssignFunc_;\n  int localCallbackIndex_{-1};\n  bool keepAliveEnabled_;\n  bool reusePortEnabled_{false};\n  // SO_REUSEADDR is enabled by default\n  bool enableReuseAddr_{true};\n  bool ipFreebind_{false};\n  bool closeOnExec_;\n  bool tfo_{false};\n  bool noTransparentTls_{false};\n  uint32_t tfoMaxQueueSize_{0};\n  std::weak_ptr<ShutdownSocketSet> wShutdownSocketSet_;\n  ConnectionEventCallback* connectionEventCallback_{nullptr};\n  bool tosReflect_{false};\n  uint32_t listenerTos_{0};\n  bool zeroCopyVal_{false};\n  folly::observer::AtomicObserver<std::chrono::nanoseconds> queueTimeout_{\n      folly::observer::makeStaticObserver(std::chrono::nanoseconds::zero())};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSignalHandler.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSignalHandler.h>\n\n#include <folly/Conv.h>\n#include <folly/io/async/EventBase.h>\n\nusing std::make_pair;\nusing std::pair;\nusing std::string;\n\nnamespace folly {\n\nAsyncSignalHandler::AsyncSignalHandler(EventBase* eventBase)\n    : eventBase_(eventBase) {}\n\nAsyncSignalHandler::~AsyncSignalHandler() {\n  // Unregister any outstanding events\n  for (auto& signalEvent : signalEvents_) {\n    // isEventRegistered() may be false if the EventBase was destroyed before us\n    if (signalEvent.second->isEventRegistered()) {\n      signalEvent.second->eb_event_del();\n    }\n  }\n}\n\nvoid AsyncSignalHandler::attachEventBase(EventBase* eventBase) {\n  assert(eventBase_ == nullptr);\n  assert(signalEvents_.empty());\n  eventBase_ = eventBase;\n}\n\nvoid AsyncSignalHandler::detachEventBase() {\n  assert(eventBase_ != nullptr);\n  assert(signalEvents_.empty());\n  eventBase_ = nullptr;\n}\n\nvoid AsyncSignalHandler::registerSignalHandler(int signum) {\n  pair<SignalEventMap::iterator, bool> ret = signalEvents_.insert(\n      make_pair(signum, std::make_unique<EventBaseEvent>()));\n  if (!ret.second) {\n    // This signal has already been registered\n    throw std::runtime_error(\n        folly::to<string>(\"handler already registered for signal \", signum));\n  }\n\n  EventBaseEvent* ev = ret.first->second.get();\n  try {\n    ev->eb_signal_set(signum, libeventCallback, this);\n    if (ev->eb_event_base_set(eventBase_) != 0) {\n      throw std::runtime_error(\n          folly::to<string>(\n              \"error initializing event handler for signal \", signum));\n    }\n\n    if (ev->eb_event_add(nullptr) != 0) {\n      throw std::runtime_error(\n          folly::to<string>(\"error adding event handler for signal \", signum));\n    }\n  } catch (...) {\n    signalEvents_.erase(ret.first);\n    throw;\n  }\n}\n\nvoid AsyncSignalHandler::unregisterSignalHandler(int signum) {\n  auto it = signalEvents_.find(signum);\n  if (it == signalEvents_.end()) {\n    throw std::runtime_error(\n        folly::to<string>(\n            \"unable to unregister handler for signal \",\n            signum,\n            \": signal not registered\"));\n  }\n\n  it->second->eb_event_del();\n  signalEvents_.erase(it);\n}\n\nvoid AsyncSignalHandler::libeventCallback(\n    libevent_fd_t signum, short /* events */, void* arg) {\n  auto handler = static_cast<AsyncSignalHandler*>(arg);\n  handler->signalReceived(int(signum));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSignalHandler.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/Event.h>\n\nnamespace folly {\n\n/**\n * A handler to receive notification about POSIX signals.\n *\n * AsyncSignalHandler allows code to process signals from within a EventBase\n * loop.\n *\n * Standard signal handlers interrupt execution of the main thread, and\n * are run while the main thread is paused.  As a result, great care must be\n * taken to avoid race conditions if the signal handler has to access or modify\n * any data used by the main thread.\n *\n * AsyncSignalHandler solves this problem by running the AsyncSignalHandler\n * callback in normal thread of execution, as a EventBase callback.\n *\n * AsyncSignalHandler may only be used in a single thread.  It will only\n * process signals received by the thread where the AsyncSignalHandler is\n * registered.  It is the user's responsibility to ensure that signals are\n * delivered to the desired thread in multi-threaded programs.\n */\nclass AsyncSignalHandler {\n public:\n  /**\n   * Create a new AsyncSignalHandler.\n   */\n  explicit AsyncSignalHandler(EventBase* eventBase);\n  virtual ~AsyncSignalHandler();\n\n  /**\n   * Attach this AsyncSignalHandler to an EventBase.\n   *\n   * This should only be called if the AsyncSignalHandler is not currently\n   * registered for any signals and is not currently attached to an existing\n   * EventBase.\n   */\n  void attachEventBase(EventBase* eventBase);\n\n  /**\n   * Detach this AsyncSignalHandler from its EventBase.\n   *\n   * This should only be called if the AsyncSignalHandler is not currently\n   * registered for any signals.\n   */\n  void detachEventBase();\n\n  /**\n   * Get the EventBase used by this AsyncSignalHandler.\n   */\n  EventBase* getEventBase() const { return eventBase_; }\n\n  /**\n   * Register to receive callbacks about the specified signal.\n   *\n   * Once the handler has been registered for a particular signal,\n   * signalReceived() will be called each time this thread receives this\n   * signal.\n   *\n   * Throws if an error occurs or if this handler is already\n   * registered for this signal.\n   */\n  void registerSignalHandler(int signum);\n\n  /**\n   * Unregister for callbacks about the specified signal.\n   *\n   * Throws if an error occurs, or if this signal was not registered.\n   */\n  void unregisterSignalHandler(int signum);\n\n  /**\n   * signalReceived() will called to indicate that the specified signal has\n   * been received.\n   *\n   * signalReceived() will always be invoked from the EventBase loop (i.e.,\n   * after the main POSIX signal handler has returned control to the EventBase\n   * thread).\n   */\n  virtual void signalReceived(int signum) noexcept = 0;\n\n private:\n  // we cannot copy the EventBaseEvent instances\n  // so we need to store ptrs to them\n  // Also some backends store ptrs to the EventBaseEvent instances\n  using SignalEventMap = std::map<int, std::unique_ptr<EventBaseEvent>>;\n\n  // Forbidden copy constructor and assignment operator\n  AsyncSignalHandler(AsyncSignalHandler const&);\n  AsyncSignalHandler& operator=(AsyncSignalHandler const&);\n\n  static void libeventCallback(libevent_fd_t signum, short events, void* arg);\n\n  EventBase* eventBase_{nullptr};\n  SignalEventMap signalEvents_;\n};\n\n/**\n * Derived template class that allows forwarding of a callback to be invoked in\n * the overridden signalReceived().\n *\n * One possible use is passing in a lambda;\n *   CallbackAsyncSignalHandler handler{evb, [&foo](int) {\n *     // do something with foo\n *   }};\n */\ntemplate <typename Callback>\nclass CallbackAsyncSignalHandler : public AsyncSignalHandler {\n public:\n  CallbackAsyncSignalHandler(folly::EventBase* evb, Callback&& cb)\n      : AsyncSignalHandler{evb}, cb_{std::forward<Callback>(cb)} {}\n\n  void signalReceived(int signum) noexcept override { cb_(signum); }\n\n private:\n  Callback cb_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSocket.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fmt/format.h>\n#include <folly/io/async/AsyncSocket.h>\n\n#include <sys/types.h>\n\n#include <cerrno>\n#include <sstream>\n\n#include <boost/preprocessor/control/if.hpp>\n\n#include <folly/Exception.h>\n#include <folly/Overload.h>\n#include <folly/Portability.h>\n#include <folly/SocketAddress.h>\n#include <folly/String.h>\n#include <folly/io/Cursor.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/IOBufQueue.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/lang/CheckedMath.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/SysUio.h>\n#include <folly/portability/Unistd.h>\n\n#if defined(__linux__)\n#include <linux/if_packet.h>\n#include <linux/sockios.h>\n#include <sys/ioctl.h>\n#endif\n\nusing ZeroCopyMemStore = folly::AsyncReader::ReadCallback::ZeroCopyMemStore;\n\nnamespace {\nclass ZeroCopyMMapMemStoreFallback : public ZeroCopyMemStore {\n public:\n  ZeroCopyMMapMemStoreFallback(size_t /*entries*/, size_t /*size*/) {}\n  ~ZeroCopyMMapMemStoreFallback() override = default;\n  ZeroCopyMemStore::EntryPtr get() override { return nullptr; }\n\n  void put(ZeroCopyMemStore::Entry* /*entry*/) override {}\n};\n\n#if defined(TCP_ZEROCOPY_RECEIVE)\nstd::unique_ptr<folly::IOBuf> getRXZeroCopyIOBuf(\n    ZeroCopyMemStore::EntryPtr&& ptr) {\n  auto* entry = ptr.release();\n  return folly::IOBuf::takeOwnership(\n      entry->data,\n      entry->len,\n      entry->len,\n      [](void* /*buf*/, void* userData) {\n        reinterpret_cast<ZeroCopyMemStore::Entry*>(userData)->put();\n      },\n      entry);\n}\n\nclass ZeroCopyMMapMemStoreReal : public ZeroCopyMemStore {\n public:\n  ZeroCopyMMapMemStoreReal(size_t entries, size_t size) {\n    // we just need a socket so the kernel\n    // will set the vma->vm_ops = &tcp_vm_ops\n    int fd = ::socket(AF_INET, SOCK_STREAM, 0);\n    if (fd >= 0) {\n      void* addr =\n          ::mmap(nullptr, entries * size, PROT_READ, MAP_SHARED, fd, 0);\n      folly::fileops::close(fd);\n      if (addr != MAP_FAILED) {\n        addr_ = addr;\n        numEntries_ = entries;\n        entrySize_ = size;\n        entries_.resize(numEntries_);\n        for (size_t i = 0; i < numEntries_; i++) {\n          entries_[i].data =\n              reinterpret_cast<uint8_t*>(addr_) + (i * entrySize_);\n          entries_[i].capacity = entrySize_;\n          entries_[i].store = this;\n          avail_.push_back(&entries_[i]);\n        }\n      }\n    }\n  }\n  ~ZeroCopyMMapMemStoreReal() override {\n    CHECK_EQ(avail_.size(), numEntries_);\n    if (addr_) {\n      ::munmap(addr_, numEntries_ * entrySize_);\n    }\n  }\n\n  ZeroCopyMemStore::EntryPtr get() override {\n    std::unique_lock lk(availMutex_);\n    if (!avail_.empty()) {\n      auto* entry = avail_.front();\n      avail_.pop_front();\n      DCHECK(entry->len == 0);\n      DCHECK(entry->capacity == entrySize_);\n\n      ZeroCopyMemStore::EntryPtr ret(entry);\n\n      return ret;\n    }\n\n    return nullptr;\n  }\n\n  void put(ZeroCopyMemStore::Entry* entry) override {\n    if (entry) {\n      DCHECK(entry->store == this);\n      if (entry->len) {\n        auto ret = ::madvise(entry->data, entry->len, MADV_DONTNEED);\n        entry->len = 0;\n        DCHECK(!ret);\n      }\n\n      std::unique_lock lk(availMutex_);\n      avail_.push_back(entry);\n    }\n  }\n\n private:\n  std::vector<ZeroCopyMemStore::Entry> entries_;\n  std::mutex availMutex_;\n  std::deque<ZeroCopyMemStore::Entry*> avail_;\n  void* addr_{nullptr};\n  size_t numEntries_{0};\n  size_t entrySize_{0};\n};\n\nusing ZeroCopyMMapMemStore = ZeroCopyMMapMemStoreReal;\n#else\nusing ZeroCopyMMapMemStore = ZeroCopyMMapMemStoreFallback;\n#endif\n\n#if FOLLY_HAS_LIBURING\nbool checkIoUringBackend(folly::EventBase* evb) {\n  auto backend = dynamic_cast<folly::IoUringBackend*>(evb->getBackend());\n  return backend != nullptr && backend->supportAsyncSocket();\n}\n#else\nbool checkIoUringBackend(folly::EventBase*) {\n  return false;\n}\n#endif\n} // namespace\n\n#if FOLLY_HAVE_VLA\n#define FOLLY_HAVE_VLA_01 1\n#else\n#define FOLLY_HAVE_VLA_01 0\n#endif\n\nusing std::string;\nusing std::unique_ptr;\n\nnamespace fsp = folly::portability::sockets;\n\nnamespace folly {\n\nstatic constexpr bool msgErrQueueSupported =\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    true;\n#else\n    false;\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n\nstd::unique_ptr<ZeroCopyMemStore> AsyncSocket::createDefaultZeroCopyMemStore(\n    size_t entries, size_t size) {\n  return std::make_unique<ZeroCopyMMapMemStore>(entries, size);\n}\n\nstatic AsyncSocketException const& getSocketClosedLocallyEx() {\n  static auto& ex = *new AsyncSocketException(\n      AsyncSocketException::END_OF_FILE, \"socket closed locally\");\n  return ex;\n}\n\nstatic AsyncSocketException const& getSocketShutdownForWritesEx() {\n  static auto& ex = *new AsyncSocketException(\n      AsyncSocketException::END_OF_FILE, \"socket shutdown for writes\");\n  return ex;\n}\n\nnamespace {\n#if FOLLY_HAVE_SO_TIMESTAMPING\nconst sock_extended_err* FOLLY_NULLABLE\ncmsgToSockExtendedErr(const cmsghdr& cmsg) {\n  if ((cmsg.cmsg_level == SOL_IP && cmsg.cmsg_type == IP_RECVERR) ||\n      (cmsg.cmsg_level == SOL_IPV6 && cmsg.cmsg_type == IPV6_RECVERR) ||\n      (cmsg.cmsg_level == SOL_PACKET &&\n       cmsg.cmsg_type == PACKET_TX_TIMESTAMP)) {\n    return reinterpret_cast<const sock_extended_err*>(CMSG_DATA(&cmsg));\n  }\n  (void)cmsg;\n  return nullptr;\n}\n\nconst sock_extended_err* FOLLY_NULLABLE\ncmsgToSockExtendedErrTimestamping(const cmsghdr& cmsg) {\n  const auto serr = cmsgToSockExtendedErr(cmsg);\n  if (serr && serr->ee_errno == ENOMSG &&\n      serr->ee_origin == SO_EE_ORIGIN_TIMESTAMPING) {\n    return serr;\n  }\n  (void)cmsg;\n  return nullptr;\n}\n\nconst scm_timestamping* FOLLY_NULLABLE\ncmsgToScmTimestamping(const cmsghdr& cmsg) {\n  if (cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_TIMESTAMPING) {\n    return reinterpret_cast<const struct scm_timestamping*>(CMSG_DATA(&cmsg));\n  }\n  (void)cmsg;\n  return nullptr;\n}\n\n#endif // FOLLY_HAVE_SO_TIMESTAMPING\n} // namespace\n\n// TODO: It might help performance to provide a version of BytesWriteRequest\n// that users could derive from, so we can avoid the extra allocation for each\n// call to write()/writev().\n//\n// We would need the version for external users where they provide the iovec\n// storage space, and only our internal version would allocate it at the end of\n// the WriteRequest.\n\n/* The default WriteRequest implementation, used for write(), writev() and\n * writeChain()\n *\n * A new BytesWriteRequest operation is allocated on the heap for all write\n * operations that cannot be completed immediately.\n */\nclass AsyncSocket::BytesWriteRequest : public AsyncSocket::WriteRequest {\n public:\n  static BytesWriteRequest* newRequest(\n      AsyncSocket* socket,\n      WriteCallbackWithState callbackWithState,\n      const iovec* ops,\n      uint32_t opCount,\n      uint32_t partialWritten,\n      uint32_t bytesWritten,\n      unique_ptr<IOBuf>&& ioBuf,\n      WriteFlags flags) {\n    assert(opCount > 0);\n    // Since we put a variable size iovec array at the end\n    // of each BytesWriteRequest, we have to manually allocate the memory.\n    uint32_t bufSize = 0;\n    if (!checked_muladd<uint32_t>(\n            &bufSize,\n            opCount,\n            sizeof(struct iovec),\n            sizeof(BytesWriteRequest))) {\n      throw std::bad_alloc();\n    }\n    void* buf = malloc(bufSize);\n    if (buf == nullptr) {\n      throw std::bad_alloc();\n    }\n\n    return new (buf) BytesWriteRequest(\n        socket,\n        callbackWithState,\n        ops,\n        opCount,\n        partialWritten,\n        bytesWritten,\n        std::move(ioBuf),\n        flags);\n  }\n\n  void destroy() override {\n    socket_->releaseIOBuf(std::move(ioBuf_), releaseIOBufCallback_);\n    this->~BytesWriteRequest();\n    free(this);\n  }\n\n  WriteResult performWrite() override {\n    WriteFlags writeFlags = flags_;\n    if (getNext() != nullptr) {\n      writeFlags |= WriteFlags::CORK;\n    }\n\n    socket_->adjustZeroCopyFlags(writeFlags);\n\n    auto writeResult = socket_->performWrite(\n        getOps(),\n        getOpCount(),\n        writeFlags,\n        &opsWritten_,\n        &partialBytes_,\n        WriteRequestTag{ioBuf_.get()});\n    bytesWritten_ = writeResult.writeReturn > 0 ? writeResult.writeReturn : 0;\n    if (bytesWritten_) {\n      if (socket_->isZeroCopyRequest(writeFlags)) {\n        if (isComplete()) {\n          socket_->addZeroCopyBuf(std::move(ioBuf_), releaseIOBufCallback_);\n        } else {\n          socket_->addZeroCopyBuf(ioBuf_.get());\n        }\n      } else {\n        // this happens if at least one of the prev requests were sent\n        // with zero copy but not the last one\n        if (isComplete() && zeroCopyRequest_ &&\n            socket_->containsZeroCopyBuf(ioBuf_.get())) {\n          socket_->setZeroCopyBuf(std::move(ioBuf_), releaseIOBufCallback_);\n        }\n      }\n    }\n    return writeResult;\n  }\n\n  bool isComplete() override { return opsWritten_ == getOpCount(); }\n\n  void consume() override {\n    // Advance opIndex_ forward by opsWritten_\n    opIndex_ += opsWritten_;\n    assert(opIndex_ < opCount_);\n\n    bool zeroCopyReq = socket_->isZeroCopyRequest(flags_);\n    if (zeroCopyReq) {\n      zeroCopyRequest_ = true;\n    }\n\n    if (!zeroCopyRequest_) {\n      // If we've finished writing any IOBufs, release them\n      // but only if we did not send any of them via zerocopy\n      if (ioBuf_) {\n        for (uint32_t i = opsWritten_; i != 0; --i) {\n          assert(ioBuf_);\n          auto next = ioBuf_->pop();\n          socket_->releaseIOBuf(std::move(ioBuf_), releaseIOBufCallback_);\n          ioBuf_ = std::move(next);\n        }\n      }\n    }\n\n    // Move partialBytes_ forward into the current iovec buffer\n    struct iovec* currentOp = writeOps_ + opIndex_;\n    assert((partialBytes_ < currentOp->iov_len) || (currentOp->iov_len == 0));\n    currentOp->iov_base =\n        reinterpret_cast<uint8_t*>(currentOp->iov_base) + partialBytes_;\n    currentOp->iov_len -= partialBytes_;\n\n    // Increment the totalBytesWritten_ count by bytesWritten_;\n    assert(bytesWritten_ >= 0);\n    totalBytesWritten_ += uint32_t(bytesWritten_);\n  }\n\n private:\n  BytesWriteRequest(\n      AsyncSocket* socket,\n      WriteCallbackWithState callbackWithState,\n      const struct iovec* ops,\n      uint32_t opCount,\n      uint32_t partialBytes,\n      uint32_t bytesWritten,\n      unique_ptr<IOBuf>&& ioBuf,\n      WriteFlags flags)\n      : AsyncSocket::WriteRequest(socket, callbackWithState),\n        opCount_(opCount),\n        opIndex_(0),\n        flags_(flags),\n        ioBuf_(std::move(ioBuf)),\n        opsWritten_(0),\n        partialBytes_(partialBytes),\n        bytesWritten_(bytesWritten) {\n    memcpy(writeOps_, ops, sizeof(*ops) * opCount_);\n    zeroCopyRequest_ = socket_->isZeroCopyRequest(flags_);\n  }\n\n  // private destructor, to ensure callers use destroy()\n  ~BytesWriteRequest() override = default;\n\n  const struct iovec* getOps() const {\n    assert(opCount_ > opIndex_);\n    return writeOps_ + opIndex_;\n  }\n\n  uint32_t getOpCount() const {\n    assert(opCount_ > opIndex_);\n    return opCount_ - opIndex_;\n  }\n\n  uint32_t opCount_; ///< number of entries in writeOps_\n  uint32_t opIndex_; ///< current index into writeOps_\n  WriteFlags flags_; ///< set for WriteFlags\n  bool zeroCopyRequest_{\n      false}; ///< if we sent any part of the ioBuf_ with zerocopy\n  unique_ptr<IOBuf> ioBuf_; ///< underlying IOBuf, or nullptr if N/A\n\n  // for consume(), how much we wrote on the last write\n  uint32_t opsWritten_; ///< complete ops written\n  uint32_t partialBytes_; ///< partial bytes of incomplete op written\n  ssize_t bytesWritten_; ///< bytes written altogether\n\n  struct iovec writeOps_[]; ///< write operation(s) list\n};\n\nint AsyncSocket::SendMsgParamsCallback::getDefaultFlags(\n    folly::WriteFlags flags, bool zeroCopyEnabled) noexcept {\n  int msg_flags = MSG_DONTWAIT;\n\n#ifdef MSG_NOSIGNAL // Linux-only\n  msg_flags |= MSG_NOSIGNAL;\n#ifdef MSG_MORE\n  if (isSet(flags, WriteFlags::CORK)) {\n    // MSG_MORE tells the kernel we have more data to send, so wait for us to\n    // give it the rest of the data rather than immediately sending a partial\n    // frame, even when TCP_NODELAY is enabled.\n    msg_flags |= MSG_MORE;\n  }\n#endif // MSG_MORE\n#endif // MSG_NOSIGNAL\n  if (isSet(flags, WriteFlags::EOR)) {\n    // marks that this is the last byte of a record (response)\n    msg_flags |= MSG_EOR;\n  }\n\n  if (zeroCopyEnabled && isSet(flags, WriteFlags::WRITE_MSG_ZEROCOPY)) {\n    msg_flags |= MSG_ZEROCOPY;\n  }\n\n  return msg_flags;\n}\n\nvoid AsyncSocket::SendMsgParamsCallback::getAncillaryData(\n    folly::WriteFlags flags,\n    void* data,\n    const WriteRequestTag& writeTag,\n    const bool byteEventsEnabled) noexcept {\n  auto ancillaryDataSize =\n      getAncillaryDataSize(flags, writeTag, byteEventsEnabled);\n  if (!ancillaryDataSize) {\n    return;\n  }\n#if FOLLY_HAVE_SO_TIMESTAMPING\n  CHECK_NOTNULL(data);\n  // this function only handles ancillary data for timestamping\n  //\n  // if getAncillaryDataSize() is overridden and returning a size different\n  // than what we expect, then this function needs to be overridden too, in\n  // order to avoid conflict with how cmsg / msg are written\n  CHECK_EQ(CMSG_SPACE(sizeof(uint32_t)), ancillaryDataSize);\n\n  uint32_t sofFlags = 0;\n  if (byteEventsEnabled && isSet(flags, WriteFlags::TIMESTAMP_TX)) {\n    sofFlags = sofFlags | folly::netops::SOF_TIMESTAMPING_TX_SOFTWARE;\n  }\n  if (byteEventsEnabled && isSet(flags, WriteFlags::TIMESTAMP_ACK)) {\n    sofFlags = sofFlags | folly::netops::SOF_TIMESTAMPING_TX_ACK;\n  }\n  if (byteEventsEnabled && isSet(flags, WriteFlags::TIMESTAMP_SCHED)) {\n    sofFlags = sofFlags | folly::netops::SOF_TIMESTAMPING_TX_SCHED;\n  }\n\n  msghdr msg;\n  msg.msg_control = data;\n  msg.msg_controllen = ancillaryDataSize;\n  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n  CHECK_NOTNULL(cmsg);\n  cmsg->cmsg_level = SOL_SOCKET;\n  cmsg->cmsg_type = SO_TIMESTAMPING;\n  cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));\n  memcpy(CMSG_DATA(cmsg), &sofFlags, sizeof(sofFlags));\n#else\n  (void)data;\n#endif // FOLLY_HAVE_SO_TIMESTAMPING\n  return;\n}\n\nuint32_t AsyncSocket::SendMsgParamsCallback::getAncillaryDataSize(\n    folly::WriteFlags flags,\n    const WriteRequestTag&,\n    const bool byteEventsEnabled) noexcept {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  if (WriteFlags::NONE != (flags & kWriteFlagsForTimestamping) &&\n      byteEventsEnabled) {\n    return CMSG_SPACE(sizeof(uint32_t));\n  }\n#else\n  (void)flags;\n  (void)byteEventsEnabled;\n#endif\n  return 0;\n}\n\nfolly::Optional<AsyncSocket::ByteEvent>\nAsyncSocket::ByteEventHelper::processCmsg(\n    const cmsghdr& cmsg, const size_t rawBytesWritten) {\n#if FOLLY_HAVE_SO_TIMESTAMPING\n  if (!byteEventsEnabled || maybeEx.has_value()) {\n    return folly::none;\n  }\n  if (!maybeTsState_.has_value()) {\n    maybeTsState_ = TimestampState();\n  }\n  auto& state = maybeTsState_.value();\n  if (auto serrTs = cmsgToSockExtendedErrTimestamping(cmsg)) {\n    if (state.serrReceived) {\n      // already have this part of the message pending\n      throw Exception(\"already have serr event\");\n    }\n    state.serrReceived = true;\n    state.typeRaw = serrTs->ee_info;\n    state.byteOffsetKernel = serrTs->ee_data;\n  } else if (auto scmTs = cmsgToScmTimestamping(cmsg)) {\n    if (state.scmTsReceived) {\n      throw Exception(\"already have scmTs event\");\n    }\n    state.scmTsReceived = true;\n\n    auto timespecToDuration =\n        [](const timespec& ts) -> folly::Optional<std::chrono::nanoseconds> {\n      std::chrono::nanoseconds duration = std::chrono::seconds(ts.tv_sec) +\n          std::chrono::nanoseconds(ts.tv_nsec);\n      if (duration == duration.zero()) {\n        return folly::none;\n      }\n      return duration;\n    };\n    // ts[0] -> software timestamp\n    // ts[1] -> hardware timestamp transformed to userspace time (deprecated)\n    // ts[2] -> hardware timestamp\n    state.maybeSoftwareTs = timespecToDuration(scmTs->ts[0]);\n    state.maybeHardwareTs = timespecToDuration(scmTs->ts[2]);\n  }\n\n  // if we have both components needed for a complete timestamp, build it\n  if (state.serrReceived && state.scmTsReceived) {\n    // cleanup state so that we're ready for next timestamp\n    TimestampState completeState = state;\n    maybeTsState_ = folly::none;\n\n    // map the type\n    folly::Optional<ByteEvent::Type> tsType;\n    switch (completeState.typeRaw) {\n      case folly::netops::SCM_TSTAMP_SND: {\n        tsType = ByteEvent::Type::TX;\n        break;\n      }\n      case folly::netops::SCM_TSTAMP_ACK: {\n        tsType = ByteEvent::Type::ACK;\n        break;\n      }\n      case folly::netops::SCM_TSTAMP_SCHED: {\n        tsType = ByteEvent::Type::SCHED;\n        break;\n      }\n      default:\n        break; // unknown, maybe something new\n    }\n    if (!tsType) {\n      // it's a timestamp, but not one that we're set up to handle\n      // we've cleared our state, loop back around\n      return folly::none;\n    }\n\n    // Calculate the byte offset.\n    //\n    // See documentation for SOF_TIMESTAMPING_OPT_ID for details.\n    //\n    // In summary, two things we have to consider:\n    //\n    //   (1) The byte stream offset is relative:\n    //       Socket timestamps include the byte stream offset for which the\n    //       timestamp applies. There may have been bytes transferred before the\n    //       fd was controlled by AsyncSocket. As a result, we don't know the\n    //       socket byte stream offset when we enable timestamping.\n    //\n    //       To get around this, we set SOF_TIMESTAMPING_OPT_ID when we enable\n    //       timestamping via setsockopt. This flag causes the kernel to reset\n    //       the offset it uses for timestamps to 0. This allows us to determine\n    //       an offset relative to the number of bytes that had been written to\n    //       the socket since timestamps were enabled.\n    //\n    //       Note that offsets begin at zero; if only a single byte is written\n    //       after timestamping is enabled, the offset included in the kernel\n    //       cmsg will be 0.\n    //\n    //   (2) The byte stream offset is a uint32_t:\n    //       Because the kernel uses a uint32_t to store and communicate the\n    //       byte stream offset, the offset will wrap every ~4GB. When we get a\n    //       timestamp, we need to figure out which byte it is for. We assume\n    //       that there will never be more than ~4GB of bytes sent between us\n    //       requesting timestamping for a byte and receiving the timestamp;\n    //       this is a realistic assumption given CWND and TCP buffer sizes. We\n    //       then calculate assuming that the counter has not wrapped since we\n    //       sent the byte that we are getting the timestamp for. If the counter\n    //       has wrapped, we detect it, and go back one position.\n    const uint64_t bytesPerOffsetWrap =\n        static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1;\n\n    // We adjust the byte stream offset by\n    // `rawBytesWrittenWhenByteEventsEnabled` to align it with the raw byte\n    // offset maintained by AsyncSocket. If the aligned bytes stream offset is\n    // negative, it means that the byte event is for a byte sent before we\n    // enabled byte events and we can discard the event.\n    if (completeState.byteOffsetKernel + rawBytesWrittenWhenByteEventsEnabled <\n        0) {\n      return folly::none;\n    }\n    size_t byteOffset = rawBytesWritten -\n        (rawBytesWritten % bytesPerOffsetWrap) +\n        completeState.byteOffsetKernel +\n        (size_t)rawBytesWrittenWhenByteEventsEnabled;\n    if (byteOffset > rawBytesWritten) {\n      // kernel's uint32_t var wrapped around; go back one wrap\n      CHECK_GE(byteOffset, bytesPerOffsetWrap)\n          << \"rawBytesWritten=\" << rawBytesWritten\n          << \" completeState.byteOffsetKernel=\"\n          << completeState.byteOffsetKernel\n          << \" rawBytesWrittenWhenByteEventsEnabled=\"\n          << rawBytesWrittenWhenByteEventsEnabled;\n      byteOffset = byteOffset - bytesPerOffsetWrap;\n    }\n\n    ByteEvent event = {};\n    event.type = tsType.value();\n    event.offset = byteOffset;\n    event.maybeSoftwareTs = state.maybeSoftwareTs;\n    event.maybeHardwareTs = state.maybeHardwareTs;\n    return event;\n  }\n#else\n  (void)cmsg;\n  (void)rawBytesWritten;\n#endif // FOLLY_HAVE_SO_TIMESTAMPING\n  return folly::none;\n}\n\nnamespace {\nAsyncSocket::SendMsgParamsCallback defaultSendMsgParamsCallback;\n\n// Based on flags, signal the transparent handler to disable certain functions\nvoid disableTransparentFunctions(\n    NetworkSocket fd, bool noTransparentTls, bool noTSocks) {\n  (void)fd;\n  (void)noTransparentTls;\n  (void)noTSocks;\n#if defined(__linux__)\n  if (noTransparentTls) {\n    // Ignore return value, errors are ok\n    VLOG(5) << \"Disabling TTLS for fd \" << fd;\n    __u8 optval = FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED;\n    netops::setsockopt(\n        fd, SOL_SOCKET, FOLLY_SO_TTLS_TRUSTED, &optval, sizeof(optval));\n  }\n  if (noTSocks) {\n    VLOG(5) << \"Disabling TSOCKS for fd \" << fd;\n    // Ignore return value, errors are ok\n    netops::setsockopt(fd, SOL_SOCKET, SO_NO_TSOCKS, nullptr, 0);\n  }\n#endif\n}\n\nconstexpr size_t kSmallIoVecSize = 64;\n\n} // namespace\n\nAsyncSocket::AsyncSocket()\n    : eventBase_(nullptr),\n      writeTimeout_(this, nullptr),\n      ioHandler_(this, nullptr),\n      immediateReadHandler_(this),\n      observerContainer_(this) {\n  VLOG(5) << \"new AsyncSocket()\";\n  init();\n}\n\nAsyncSocket::AsyncSocket(EventBase* evb)\n    : eventBase_(evb),\n      writeTimeout_(this, evb),\n      ioHandler_(this, evb),\n      immediateReadHandler_(this),\n      observerContainer_(this) {\n  VLOG(5) << \"new AsyncSocket(\" << this << \", evb=\" << evb << \")\";\n  init();\n}\n\nAsyncSocket::AsyncSocket(\n    EventBase* evb,\n    const folly::SocketAddress& address,\n    uint32_t connectTimeout,\n    bool useZeroCopy)\n    : AsyncSocket(evb) {\n  setZeroCopy(useZeroCopy);\n  connect(nullptr, address, connectTimeout);\n}\n\nAsyncSocket::AsyncSocket(\n    EventBase* evb,\n    const std::string& ip,\n    uint16_t port,\n    uint32_t connectTimeout,\n    bool useZeroCopy)\n    : AsyncSocket(evb) {\n  setZeroCopy(useZeroCopy);\n  connect(nullptr, ip, port, connectTimeout);\n}\n\nAsyncSocket::AsyncSocket(\n    EventBase* evb,\n    NetworkSocket fd,\n    uint32_t zeroCopyBufId,\n    const SocketAddress* peerAddress,\n    folly::Optional<std::chrono::steady_clock::time_point>\n        maybeConnectionEstablishTime)\n    : zeroCopyBufId_(zeroCopyBufId),\n      state_(StateEnum::ESTABLISHED),\n      fd_(fd),\n      addr_(peerAddress ? *peerAddress : folly::SocketAddress()),\n      eventBase_(evb),\n      writeTimeout_(this, evb),\n      ioHandler_(this, evb, fd),\n      immediateReadHandler_(this),\n      maybeConnectionEstablishTime_(std::move(maybeConnectionEstablishTime)),\n      observerContainer_(this) {\n  VLOG(5) << \"new AsyncSocket(\" << this << \", evb=\" << evb << \", fd=\" << fd\n          << \", zeroCopyBufId=\" << zeroCopyBufId << \")\";\n  init();\n  disableTransparentFunctions(fd_, noTransparentTls_, noTSocks_);\n  setCloseOnExec();\n}\n\nAsyncSocket::AsyncSocket(AsyncSocket* oldAsyncSocket)\n    : zeroCopyBufId_(oldAsyncSocket->getZeroCopyBufId()),\n      state_(oldAsyncSocket->state_),\n      fd_(oldAsyncSocket->getNetworkSocket()),\n      addr_(oldAsyncSocket->addr_),\n      eventBase_(oldAsyncSocket->getEventBase()),\n      writeTimeout_(this, eventBase_),\n      ioHandler_(this, eventBase_, fd_),\n      immediateReadHandler_(this),\n      appBytesWritten_(oldAsyncSocket->appBytesWritten_),\n      rawBytesWritten_(oldAsyncSocket->rawBytesWritten_),\n      preReceivedData_(std::move(oldAsyncSocket->preReceivedData_)),\n      connectStartTime_(oldAsyncSocket->connectStartTime_),\n      connectEndTime_(oldAsyncSocket->connectEndTime_),\n      maybeConnectionEstablishTime_(\n          oldAsyncSocket->maybeConnectionEstablishTime_),\n      tfoInfo_(std::move(oldAsyncSocket->tfoInfo_)),\n      byteEventHelper_(std::move(oldAsyncSocket->byteEventHelper_)),\n      observerContainer_(this, std::move(oldAsyncSocket->observerContainer_)) {\n  // delay detaching network socket until observers moved to prevent spurious\n  // detachFd and close notifications\n  oldAsyncSocket->detachNetworkSocket();\n\n  VLOG(5) << \"move AsyncSocket(\" << oldAsyncSocket << \"->\" << this\n          << \", evb=\" << eventBase_ << \", fd=\" << fd_\n          << \", zeroCopyBufId=\" << zeroCopyBufId_ << \")\";\n  init();\n  disableTransparentFunctions(fd_, noTransparentTls_, noTSocks_);\n  setCloseOnExec();\n\n  // inform lifecycle observers to give them an opportunity to unsubscribe from\n  // events for the old socket and subscribe to the new socket; we do not move\n  // the subscription ourselves\n\n  // legacy observer support\n  for (const auto& cb : oldAsyncSocket->lifecycleObservers_) {\n    cb->move(oldAsyncSocket, this);\n  }\n}\n\nAsyncSocket::AsyncSocket(AsyncSocket::UniquePtr oldAsyncSocket)\n    : AsyncSocket(oldAsyncSocket.get()) {}\n\n// init() method, since constructor forwarding isn't supported in most\n// compilers yet.\nvoid AsyncSocket::init() {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n    useIoUring_ = checkIoUringBackend(eventBase_);\n  }\n  eventFlags_ = EventHandler::NONE;\n  sendTimeout_ = 0;\n  maxReadsPerEvent_ = 16;\n  connectCallback_ = nullptr;\n  errMessageCallback_ = nullptr;\n  readAncillaryDataCallback_ = nullptr;\n  readCallback_ = nullptr;\n  writeReqHead_ = nullptr;\n  writeReqTail_ = nullptr;\n  wShutdownSocketSet_.reset();\n  appBytesReceived_ = 0;\n  totalAppBytesScheduledForWrite_ = 0;\n  sendMsgParamCallback_ = &defaultSendMsgParamsCallback;\n  if (useIoUring_ && state_ == StateEnum::ESTABLISHED) {\n    assert(iouSendHandle_ == nullptr);\n    iouSendHandle_ = IoUringSendHandle::create(eventBase_, fd_, addr_, this);\n  }\n}\n\nAsyncSocket::~AsyncSocket() {\n  VLOG(7) << \"actual destruction of AsyncSocket(this=\" << this << \", evb=\"\n          << eventBase_ << \", fd=\" << fd_ << \", state=\" << state_ << \")\";\n  for (const auto& cb : lifecycleObservers_) {\n    cb->destroy(this);\n  }\n  DCHECK_EQ(allocatedBytesBuffered_, 0);\n}\n\nvoid AsyncSocket::destroy() {\n  VLOG(5) << \"AsyncSocket::destroy(this=\" << this << \", evb=\" << eventBase_\n          << \", fd=\" << fd_ << \", state=\" << state_;\n  // When destroy is called, close the socket immediately\n  closeNow();\n\n  // Then call DelayedDestruction::destroy() to take care of\n  // whether or not we need immediate or delayed destruction\n  DelayedDestruction::destroy();\n}\n\nNetworkSocket AsyncSocket::detachNetworkSocket() {\n  VLOG(6) << \"AsyncSocket::detachFd(this=\" << this << \", fd=\" << fd_\n          << \", evb=\" << eventBase_ << \", state=\" << state_\n          << \", events=\" << std::hex << eventFlags_ << \")\";\n  // legacy observer support\n  for (const auto& cb : lifecycleObservers_) {\n    cb->fdDetach(this);\n  }\n\n  // folly::ObserverContainer observer support\n  if (auto list = getAsyncSocketObserverContainer()) {\n    list->invokeInterfaceMethodAllObservers([](auto observer, auto observed) {\n      observer->fdDetach(observed);\n    });\n  }\n\n  // Extract the fd, and set fd_ to -1 first, so closeNow() won't\n  // actually close the descriptor.\n  if (const auto socketSet = wShutdownSocketSet_.lock()) {\n    socketSet->remove(fd_);\n  }\n  auto fd = fd_;\n  fd_ = NetworkSocket();\n  // Call closeNow() to invoke all pending callbacks with an error.\n  closeNow();\n  // Update the EventHandler to stop using this fd.\n  // This can only be done after closeNow() unregisters the handler.\n  ioHandler_.changeHandlerFD(NetworkSocket());\n  return fd;\n}\n\nvoid AsyncSocket::setShutdownSocketSet(\n    const std::weak_ptr<ShutdownSocketSet>& wNewSS) {\n  const auto newSS = wNewSS.lock();\n  const auto shutdownSocketSet = wShutdownSocketSet_.lock();\n\n  if (newSS == shutdownSocketSet) {\n    return;\n  }\n\n  if (shutdownSocketSet && fd_ != NetworkSocket()) {\n    shutdownSocketSet->remove(fd_);\n  }\n\n  if (newSS && fd_ != NetworkSocket()) {\n    newSS->add(fd_);\n  }\n\n  wShutdownSocketSet_ = wNewSS;\n}\n\nvoid AsyncSocket::setCloseOnExec() {\n  int rv = netops_->set_socket_close_on_exec(fd_);\n  if (rv != 0) {\n    auto errnoCopy = errno;\n    throw AsyncSocketException(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(\"failed to set close-on-exec flag\"),\n        errnoCopy);\n  }\n}\n\nvoid AsyncSocket::connect(\n    ConnectCallback* callback,\n    const folly::SocketAddress& address,\n    int timeout,\n    const SocketOptionMap& options,\n    const BindOptions& bindOptions,\n    const std::string& ifName) noexcept {\n  DestructorGuard dg(this);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  addr_ = address;\n\n  // Make sure we're in the uninitialized state\n  if (state_ != StateEnum::UNINIT) {\n    if (auto* fd = std::get_if<NetworkSocket>(&bindOptions)) {\n      netops_->close(*fd);\n    }\n    return invalidState(callback);\n  }\n\n  connectTimeout_ = std::chrono::milliseconds(timeout);\n  connectStartTime_ = std::chrono::steady_clock::now();\n  // Make connect end time at least >= connectStartTime.\n  connectEndTime_ = connectStartTime_;\n\n  assert(fd_ == NetworkSocket());\n  state_ = StateEnum::CONNECTING;\n  connectCallback_ = callback;\n  invokeConnectAttempt();\n\n  sockaddr_storage addrStorage;\n  auto saddr = reinterpret_cast<sockaddr*>(&addrStorage);\n\n  try {\n    if (auto* boundFd = std::get_if<NetworkSocket>(&bindOptions)) {\n      struct sockaddr_storage peerAddr{};\n      socklen_t peerLen = sizeof(peerAddr);\n      if (netops_->getpeername(\n              *boundFd,\n              reinterpret_cast<struct sockaddr*>(&peerAddr),\n              &peerLen) == 0) {\n        netops_->close(*boundFd);\n        throw AsyncSocketException(\n            AsyncSocketException::INVALID_STATE,\n            withAddr(\"boundFd is already connected\"));\n      }\n      fd_ = *boundFd;\n    } else {\n      // Create the socket\n      // Technically the first parameter should actually be a protocol family\n      // constant (PF_xxx) rather than an address family (AF_xxx), but the\n      // distinction is mainly just historical.  In pretty much all\n      // implementations the PF_foo and AF_foo constants are identical.\n      fd_ = netops_->socket(address.getFamily(), SOCK_STREAM, 0);\n      if (fd_ == NetworkSocket()) {\n        auto errnoCopy = errno;\n        throw AsyncSocketException(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"failed to create socket\"),\n            errnoCopy);\n      }\n    }\n\n    disableTransparentFunctions(fd_, noTransparentTls_, noTSocks_);\n    handleNetworkSocketAttached();\n    setCloseOnExec();\n\n    // Put the socket in non-blocking mode\n    int rv = netops_->set_socket_non_blocking(fd_);\n    if (rv == -1) {\n      auto errnoCopy = errno;\n      throw AsyncSocketException(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(\"failed to put socket in non-blocking mode\"),\n          errnoCopy);\n    }\n    if (tosOrTrafficClass_) {\n      setTosOrTrafficClass(*tosOrTrafficClass_);\n    }\n\n#if !defined(MSG_NOSIGNAL) && defined(F_SETNOSIGPIPE)\n    // iOS and OS X don't support MSG_NOSIGNAL; set F_SETNOSIGPIPE instead\n    rv = fcntl(fd_.toFd(), F_SETNOSIGPIPE, 1);\n    if (rv == -1) {\n      auto errnoCopy = errno;\n      throw AsyncSocketException(\n          AsyncSocketException::INTERNAL_ERROR,\n          \"failed to enable F_SETNOSIGPIPE on socket\",\n          errnoCopy);\n    }\n#endif\n\n    // By default, turn on TCP_NODELAY\n    // If setNoDelay() fails, we continue anyway; this isn't a fatal error.\n    // setNoDelay() will log an error message if it fails.\n    // Also set the cached zeroCopyVal_ since it cannot be set earlier if the fd\n    // is not created\n    if (address.getFamily() != AF_UNIX) {\n      (void)setNoDelay(true);\n      setZeroCopy(zeroCopyVal_);\n    }\n\n    // Apply the additional PRE_BIND options if any.\n    applyOptions(options, SocketOptionKey::ApplyPos::PRE_BIND);\n\n    VLOG(5) << \"AsyncSocket::connect(this=\" << this << \", evb=\" << eventBase_\n            << \", fd=\" << fd_ << \", host=\" << address.describe().c_str();\n\n    // bind the socket to the interface\n#if defined(__linux__)\n    if (!ifName.empty() &&\n        netops_->setsockopt(\n            fd_,\n            SOL_SOCKET,\n            SO_BINDTODEVICE,\n            ifName.c_str(),\n            ifName.length())) {\n      auto errnoCopy = errno;\n      doClose();\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to bind to device: \" + ifName,\n          errnoCopy);\n    }\n#else\n    (void)ifName;\n#endif\n\n    // bind the socket\n    if (auto* bindAddr = std::get_if<folly::SocketAddress>(&bindOptions);\n        bindAddr && *bindAddr != anyAddress()) {\n      int one = 1;\n#if defined(IP_BIND_ADDRESS_NO_PORT) && !FOLLY_MOBILE && !defined(_WIN32) && \\\n    !defined(__APPLE__)\n      // If the any port is specified with a non-any address this is typically\n      // a client socket. However, calling bind before connect without\n      // IP_BIND_ADDRESS_NO_PORT forces the OS to find a unique port relying\n      // on only the local tuple. This limits the range of available ephemeral\n      // ports.  Using the IP_BIND_ADDRESS_NO_PORT delays assigning a port until\n      // connect expanding the available port range, unless\n      // setBindAddressNoPort() is called.\n      if (bindAddr->getPort() == 0) {\n        if (bindAddressNoPort_ &&\n            netops_->setsockopt(\n                fd_, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, &one, sizeof(one))) {\n          auto errnoCopy = errno;\n          doClose();\n          throw AsyncSocketException(\n              AsyncSocketException::NOT_OPEN,\n              \"failed to setsockopt IP_BIND_ADDRESS_NO_PORT prior to bind on \" +\n                  bindAddr->describe(),\n              errnoCopy);\n        }\n      } else {\n#else\n      {\n#endif\n        if (netops_->setsockopt(\n                fd_, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {\n          auto errnoCopy = errno;\n          doClose();\n          throw AsyncSocketException(\n              AsyncSocketException::NOT_OPEN,\n              \"failed to setsockopt SO_REUSEADDR prior to bind on \" +\n                  bindAddr->describe(),\n              errnoCopy);\n        }\n      }\n\n      bindAddr->getAddress(&addrStorage);\n\n      if (netops_->bind(fd_, saddr, bindAddr->getActualSize()) != 0) {\n        auto errnoCopy = errno;\n        doClose();\n        throw AsyncSocketException(\n            AsyncSocketException::NOT_OPEN,\n            \"failed to bind to async socket: \" + bindAddr->describe(),\n            errnoCopy);\n      }\n    }\n\n    // Apply the additional POST_BIND options if any.\n    applyOptions(options, SocketOptionKey::ApplyPos::POST_BIND);\n\n    // Call preConnect hook if any.\n    if (connectCallback_) {\n      connectCallback_->preConnect(fd_);\n    }\n\n    // Perform the connect()\n    address.getAddress(&addrStorage);\n\n    assert(eventBase_ != nullptr);\n    if (useIoUring_ && !iouSendHandle_) {\n      iouSendHandle_ =\n          IoUringSendHandle::create(eventBase_, fd_, address, this);\n    }\n\n    if (tfoInfo_.enabled) {\n      state_ = StateEnum::FAST_OPEN;\n      tfoInfo_.attempted = true;\n    } else {\n      if (socketConnect(saddr, addr_.getActualSize()) < 0) {\n        return;\n      }\n    }\n\n    // If we're still here the connect() succeeded immediately.\n    // Fall through to call the callback outside of this try...catch block\n  } catch (const AsyncSocketException& ex) {\n    return failConnect(__func__, ex);\n  } catch (const std::exception& ex) {\n    // shouldn't happen, but handle it just in case\n    VLOG(4) << \"AsyncSocket::connect(this=\" << this << \", fd=\" << fd_\n            << \"): unexpected \" << typeid(ex).name()\n            << \" exception: \" << ex.what();\n    AsyncSocketException tex(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(string(\"unexpected exception: \") + ex.what()));\n    return failConnect(__func__, tex);\n  }\n\n  // The connection succeeded immediately\n  // The read callback may not have been set yet, and no writes may be pending\n  // yet, so we don't have to register for any events at the moment.\n  VLOG(8) << \"AsyncSocket::connect succeeded immediately; this=\" << this;\n  assert(errMessageCallback_ == nullptr);\n  assert(readCallback_ == nullptr);\n  assert(!hasPendingWrites());\n  assert(iouConnectHandle_ == nullptr);\n  if (state_ != StateEnum::FAST_OPEN) {\n    state_ = StateEnum::ESTABLISHED;\n    if (useIoUring_ && netops_->set_socket_blocking(fd_)) {\n      auto errnoCopy = errno;\n      AsyncSocketException tex(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(\"failed to restore socket in blocking mode\"),\n          errnoCopy);\n      return failConnect(__func__, tex);\n    }\n  }\n  invokeConnectSuccess();\n}\n\nint AsyncSocket::socketConnect(const struct sockaddr* saddr, socklen_t len) {\n  int rv = netops_->connect(fd_, saddr, len);\n  if (rv < 0) {\n    auto errnoCopy = errno;\n    if (errnoCopy == EINPROGRESS) {\n      if (!useIoUring_) {\n        scheduleConnectTimeout();\n        registerForConnectEvents();\n      } else {\n        iouConnectHandle_ = IoUringConnectHandle::create(\n            eventBase_, fd_, this, connectTimeout_);\n      }\n    } else {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"connect failed (immediately)\",\n          errnoCopy);\n    }\n  }\n  return rv;\n}\n\nvoid AsyncSocket::scheduleConnectTimeout() {\n  // Connection in progress.\n  auto timeout = connectTimeout_.count();\n  if (timeout > 0) {\n    // Start a timer in case the connection takes too long.\n    if (!writeTimeout_.scheduleTimeout(uint32_t(timeout))) {\n      throw AsyncSocketException(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(\"failed to schedule AsyncSocket connect timeout\"));\n    }\n  }\n}\n\nvoid AsyncSocket::registerForConnectEvents() {\n  // Register for write events, so we'll\n  // be notified when the connection finishes/fails.\n  // Note that we don't register for a persistent event here.\n  assert(eventFlags_ == EventHandler::NONE);\n  eventFlags_ = EventHandler::WRITE;\n  if (!ioHandler_.registerHandler(eventFlags_)) {\n    throw AsyncSocketException(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(\"failed to register AsyncSocket connect handler\"));\n  }\n}\n\nvoid AsyncSocket::connect(\n    ConnectCallback* callback,\n    const string& ip,\n    uint16_t port,\n    int timeout,\n    const SocketOptionMap& options) noexcept {\n  DestructorGuard dg(this);\n  try {\n    connectCallback_ = callback;\n    connect(callback, folly::SocketAddress(ip, port), timeout, options);\n  } catch (const std::exception& ex) {\n    AsyncSocketException tex(AsyncSocketException::INTERNAL_ERROR, ex.what());\n    return failConnect(__func__, tex);\n  }\n}\n\nvoid AsyncSocket::cancelConnect() {\n  connectCallback_ = nullptr;\n  if (state_ == StateEnum::CONNECTING || state_ == StateEnum::FAST_OPEN) {\n    closeNow();\n  }\n}\n\nvoid AsyncSocket::setSendTimeout(uint32_t milliseconds) {\n  sendTimeout_ = milliseconds;\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  // If we are currently pending on write requests, immediately update\n  // writeTimeout_ with the new value.\n  if ((eventFlags_ & EventHandler::WRITE) &&\n      (state_ != StateEnum::CONNECTING && state_ != StateEnum::FAST_OPEN)) {\n    assert(state_ == StateEnum::ESTABLISHED);\n    assert((shutdownFlags_ & SHUT_WRITE) == 0);\n    if (sendTimeout_ > 0) {\n      if (!writeTimeout_.scheduleTimeout(sendTimeout_)) {\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"failed to reschedule send timeout in setSendTimeout\"));\n        return failWrite(__func__, ex);\n      }\n    } else {\n      writeTimeout_.cancelTimeout();\n    }\n  }\n}\n\nvoid AsyncSocket::setErrMessageCB(ErrMessageCallback* callback) {\n  CHECK(!useIoUring_);\n  VLOG(6) << \"AsyncSocket::setErrMessageCB() this=\" << this << \", fd=\" << fd_\n          << \", callback=\" << callback << \", state=\" << state_;\n\n  // In the latest stable kernel 4.14.3 as of 2017-12-04, unix domain\n  // socket does not support MSG_ERRQUEUE. So recvmsg(MSG_ERRQUEUE)\n  // will read application data from unix domain socket as error\n  // message, which breaks the message flow in application.  Feel free\n  // to remove the next code block if MSG_ERRQUEUE is added for unix\n  // domain socket in the future.\n  if (callback != nullptr) {\n    cacheLocalAddress();\n    if (localAddr_.getFamily() == AF_UNIX) {\n      LOG(ERROR) << \"Failed to set ErrMessageCallback=\" << callback\n                 << \" for Unix Doamin Socket where MSG_ERRQUEUE is unsupported,\"\n                 << \" fd=\" << fd_;\n      return;\n    }\n  }\n\n  // Short circuit if callback is the same as the existing errMessageCallback_.\n  if (callback == errMessageCallback_) {\n    return;\n  }\n\n  if (!msgErrQueueSupported) {\n    // Per-socket error message queue is not supported on this platform.\n    return invalidState(callback);\n  }\n\n  DestructorGuard dg(this);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  if (callback == nullptr) {\n    // We should be able to reset the callback regardless of the\n    // socket state. It's important to have a reliable callback\n    // cancellation mechanism.\n    errMessageCallback_ = callback;\n    return;\n  }\n\n  switch ((StateEnum)state_) {\n    case StateEnum::CONNECTING:\n    case StateEnum::FAST_OPEN:\n    case StateEnum::ESTABLISHED: {\n      errMessageCallback_ = callback;\n      return;\n    }\n    case StateEnum::CLOSED:\n    case StateEnum::ERROR:\n      // We should never reach here.  SHUT_READ should always be set\n      // if we are in STATE_CLOSED or STATE_ERROR.\n      assert(false);\n      return invalidState(callback);\n    case StateEnum::UNINIT:\n      // We do not allow setReadCallback() to be called before we start\n      // connecting.\n      return invalidState(callback);\n  }\n\n  // We don't put a default case in the switch statement, so that the compiler\n  // will warn us to update the switch statement if a new state is added.\n  return invalidState(callback);\n}\n\nAsyncSocket::ErrMessageCallback* AsyncSocket::getErrMessageCallback() const {\n  return errMessageCallback_;\n}\n\nvoid AsyncSocket::setReadAncillaryDataCB(ReadAncillaryDataCallback* callback) {\n  CHECK(!useIoUring_);\n  VLOG(6) << \"AsyncSocket::setReadAncillaryDataCB() this=\" << this << \", fd=\"\n          << fd_ << \", callback=\" << callback << \", state=\" << state_;\n\n  readAncillaryDataCallback_ = callback;\n}\n\nAsyncSocket::ReadAncillaryDataCallback*\nAsyncSocket::getReadAncillaryDataCallback() const {\n  return readAncillaryDataCallback_;\n}\n\nvoid AsyncSocket::setSendMsgParamCB(SendMsgParamsCallback* callback) {\n  sendMsgParamCallback_ = callback;\n}\n\nAsyncSocket::SendMsgParamsCallback* AsyncSocket::getSendMsgParamsCB() const {\n  return sendMsgParamCallback_;\n}\n\nvoid AsyncSocket::setReadCB(ReadCallback* callback) {\n  VLOG(6) << \"AsyncSocket::setReadCallback() this=\" << this << \", fd=\" << fd_\n          << \", callback=\" << callback << \", state=\" << state_;\n\n  // Short circuit if callback is the same as the existing readCallback_.\n  //\n  // Note that this is needed for proper functioning during some cleanup cases.\n  // During cleanup we allow setReadCallback(nullptr) to be called even if the\n  // read callback is already unset and we have been detached from an event\n  // base.  This check prevents us from asserting\n  // eventBase_->isInEventBaseThread() when eventBase_ is nullptr.\n  if (callback == readCallback_) {\n    return;\n  }\n\n  /* We are removing a read callback */\n  if (callback == nullptr && immediateReadHandler_.isLoopCallbackScheduled()) {\n    immediateReadHandler_.cancelLoopCallback();\n  }\n\n  if (shutdownFlags_ & SHUT_READ) {\n    // Reads have already been shut down on this socket.\n    //\n    // Allow setReadCallback(nullptr) to be called in this case, but don't\n    // allow a new callback to be set.\n    //\n    // For example, setReadCallback(nullptr) can happen after an error if we\n    // invoke some other error callback before invoking readError().  The other\n    // error callback that is invoked first may go ahead and clear the read\n    // callback before we get a chance to invoke readError().\n    if (callback != nullptr) {\n      return invalidState(callback);\n    }\n    assert((eventFlags_ & EventHandler::READ) == 0);\n    readCallback_ = nullptr;\n    return;\n  }\n\n  DestructorGuard dg(this);\n  eventBase_->dcheckIsInEventBaseThread();\n  // This new callback might support zero copy reads, so reset the\n  // zerocopyReadDisabled_ flag to its default value so we will\n  // check for support again on the next read attempt.\n  zerocopyReadDisabled_ = false;\n\n  switch ((StateEnum)state_) {\n    case StateEnum::CONNECTING:\n    case StateEnum::FAST_OPEN:\n      // For convenience, we allow the read callback to be set while we are\n      // still connecting.  We just store the callback for now.  Once the\n      // connection completes we'll register for read events.\n      readCallback_ = callback;\n      return;\n    case StateEnum::ESTABLISHED: {\n      readCallback_ = callback;\n      uint16_t oldFlags = eventFlags_;\n      if (readCallback_) {\n        eventFlags_ |= EventHandler::READ;\n        if (useIoUring_ && !iouRecvHandle_) {\n          iouRecvHandle_ =\n              IoUringRecvHandle::create(eventBase_, fd_, addr_, this);\n          CHECK(iouRecvHandle_);\n        }\n      } else {\n        eventFlags_ &= ~EventHandler::READ;\n      }\n\n      // Update our registration if our flags have changed\n      if (eventFlags_ != oldFlags) {\n        // We intentionally ignore the return value here.\n        // updateEventRegistration() will move us into the error state if it\n        // fails, and we don't need to do anything else here afterwards.\n        (void)updateEventRegistration();\n      }\n\n      if (readCallback_) {\n        checkForImmediateRead();\n      }\n      return;\n    }\n    case StateEnum::CLOSED:\n    case StateEnum::ERROR:\n      // We should never reach here.  SHUT_READ should always be set\n      // if we are in STATE_CLOSED or STATE_ERROR.\n      assert(false);\n      return invalidState(callback);\n    case StateEnum::UNINIT:\n      // We do not allow setReadCallback() to be called before we start\n      // connecting.\n      return invalidState(callback);\n  }\n\n  // We don't put a default case in the switch statement, so that the compiler\n  // will warn us to update the switch statement if a new state is added.\n  return invalidState(callback);\n}\n\nAsyncSocket::ReadCallback* AsyncSocket::getReadCallback() const {\n  return readCallback_;\n}\n\nbool AsyncSocket::setZeroCopy(bool enable) {\n  CHECK(!useIoUring_ || !enable);\n  if (msgErrQueueSupported) {\n    zeroCopyVal_ = enable;\n\n    if (fd_ == NetworkSocket()) {\n      return false;\n    }\n\n    // No-op, bail out early\n    if (enable == zeroCopyEnabled_) {\n      return true;\n    }\n\n    int val = enable ? 1 : 0;\n    int ret =\n        netops_->setsockopt(fd_, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val));\n\n    // if enable == false, set zeroCopyEnabled_ = false regardless\n    // if SO_ZEROCOPY is set or not\n    if (!enable) {\n      zeroCopyEnabled_ = enable;\n      return true;\n    }\n\n    /* if the setsockopt failed, try to see if the socket inherited the flag\n     * since we cannot set SO_ZEROCOPY on a socket s = accept\n     */\n    if (ret) {\n      val = 0;\n      socklen_t optlen = sizeof(val);\n      ret = netops_->getsockopt(fd_, SOL_SOCKET, SO_ZEROCOPY, &val, &optlen);\n\n      if (!ret) {\n        enable = val != 0;\n      }\n    }\n\n    if (!ret) {\n      zeroCopyEnabled_ = enable;\n\n      return true;\n    }\n  }\n\n  return false;\n}\n\nvoid AsyncSocket::setZeroCopyEnableFunc(AsyncWriter::ZeroCopyEnableFunc func) {\n  zeroCopyEnablePolicy_ = std::move(func);\n}\n\nvoid AsyncSocket::setZeroCopyEnableThreshold(size_t threshold) {\n  zeroCopyEnablePolicy_ = threshold;\n}\n\nvoid AsyncSocket::setZeroCopyReenableThreshold(size_t threshold) {\n  zeroCopyReenableThreshold_ = threshold;\n}\n\nvoid AsyncSocket::setZeroCopyDrainConfig(const ZeroCopyDrainConfig& config) {\n  zeroCopyDrainConfig_ = config;\n}\n\nbool AsyncSocket::isZeroCopyRequest(WriteFlags flags) {\n  return (zeroCopyEnabled_ && isSet(flags, WriteFlags::WRITE_MSG_ZEROCOPY));\n}\n\nvoid AsyncSocket::adjustZeroCopyFlags(folly::WriteFlags& flags) {\n  if (!zeroCopyEnabled_) {\n    // if the zeroCopyReenableCounter_ is > 0\n    // we try to dec and if we reach 0\n    // we set zeroCopyEnabled_ to true\n    if (zeroCopyReenableCounter_) {\n      if (0 == --zeroCopyReenableCounter_) {\n        zeroCopyEnabled_ = true;\n        return;\n      }\n    }\n    flags = unSet(flags, folly::WriteFlags::WRITE_MSG_ZEROCOPY);\n  }\n}\n\nvoid AsyncSocket::addZeroCopyBuf(\n    std::unique_ptr<folly::IOBuf>&& buf, ReleaseIOBufCallback* cb) {\n  uint32_t id = getNextZeroCopyBufId();\n  folly::IOBuf* ptr = buf.get();\n\n  idZeroCopyBufPtrMap_[id] = ptr;\n  auto& p = idZeroCopyBufInfoMap_[ptr];\n  p.count_++;\n  CHECK(p.buf_.get() == nullptr);\n  p.buf_ = std::move(buf);\n  p.cb_ = cb;\n}\n\nvoid AsyncSocket::addZeroCopyBuf(folly::IOBuf* ptr) {\n  uint32_t id = getNextZeroCopyBufId();\n  idZeroCopyBufPtrMap_[id] = ptr;\n\n  idZeroCopyBufInfoMap_[ptr].count_++;\n}\n\nvoid AsyncSocket::releaseZeroCopyBuf(uint32_t id) {\n  auto iter = idZeroCopyBufPtrMap_.find(id);\n  CHECK(iter != idZeroCopyBufPtrMap_.end());\n  auto ptr = iter->second;\n  auto iter1 = idZeroCopyBufInfoMap_.find(ptr);\n  CHECK(iter1 != idZeroCopyBufInfoMap_.end());\n  if (0 == --iter1->second.count_) {\n    releaseIOBuf(std::move(iter1->second.buf_), iter1->second.cb_);\n    idZeroCopyBufInfoMap_.erase(iter1);\n  }\n\n  idZeroCopyBufPtrMap_.erase(iter);\n}\n\nvoid AsyncSocket::drainZeroCopyQueue() {\n  // try to drain ZC writes if any - this is best effort\n  size_t prevSize = 0;\n  while (idZeroCopyBufPtrMap_.size() != prevSize) {\n    prevSize = idZeroCopyBufPtrMap_.size();\n    handleErrMessages();\n  }\n\n  if (idZeroCopyBufPtrMap_.empty()) {\n    return;\n  }\n\n  // Enable SO_LINGER if requested.\n  if (zeroCopyDrainConfig_.linger.has_value()) {\n    struct linger optLinger = {1, zeroCopyDrainConfig_.linger.value()};\n    if (setSockOpt(SOL_SOCKET, SO_LINGER, &optLinger) != 0) {\n      VLOG(2) << \"AsyncSocket::drainZeroCopyQueue(): error setting SO_LINGER \"\n              << \"on \" << fd_ << \": errno=\" << errno;\n    }\n  }\n\n  if (eventBase_) {\n    idZeroCopyBufPtrMap_.clear();\n    // copy the buffers and adjust the allocatedBytesBuffered_\n    std::vector<std::unique_ptr<folly::IOBuf>> bufs;\n    for (auto& info : std::exchange(idZeroCopyBufInfoMap_, {})) {\n      if (info.second.buf_) {\n        const size_t allocated = info.second.buf_->computeChainCapacity();\n        DCHECK_GE(allocatedBytesBuffered_, allocated);\n        allocatedBytesBuffered_ -= allocated;\n        bufs.emplace_back(std::move(info.second.buf_));\n      }\n    }\n    // enqueue for later destruction\n    eventBase_->scheduleAt(\n        [b = std::move(bufs)] {},\n        std::chrono::steady_clock::now() + zeroCopyDrainConfig_.drainDelay);\n  } else {\n    while (!idZeroCopyBufPtrMap_.empty()) {\n      releaseZeroCopyBuf(idZeroCopyBufPtrMap_.begin()->first);\n    }\n  }\n}\n\nvoid AsyncSocket::setZeroCopyBuf(\n    std::unique_ptr<folly::IOBuf>&& buf, ReleaseIOBufCallback* cb) {\n  folly::IOBuf* ptr = buf.get();\n  auto& p = idZeroCopyBufInfoMap_[ptr];\n  CHECK(p.buf_.get() == nullptr);\n\n  p.buf_ = std::move(buf);\n  p.cb_ = cb;\n}\n\nbool AsyncSocket::containsZeroCopyBuf(folly::IOBuf* ptr) {\n  return (idZeroCopyBufInfoMap_.find(ptr) != idZeroCopyBufInfoMap_.end());\n}\n\nbool AsyncSocket::isZeroCopyMsg(const cmsghdr& cmsg) const {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  if ((cmsg.cmsg_level == SOL_IP && cmsg.cmsg_type == IP_RECVERR) ||\n      (cmsg.cmsg_level == SOL_IPV6 && cmsg.cmsg_type == IPV6_RECVERR)) {\n    auto serr =\n        reinterpret_cast<const struct sock_extended_err*>(CMSG_DATA(&cmsg));\n    return (\n        (serr->ee_errno == 0) && (serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY));\n  }\n#endif\n  (void)cmsg;\n  return false;\n}\n\nvoid AsyncSocket::processZeroCopyMsg(const cmsghdr& cmsg) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  auto serr =\n      reinterpret_cast<const struct sock_extended_err*>(CMSG_DATA(&cmsg));\n  uint32_t hi = serr->ee_data;\n  uint32_t lo = serr->ee_info;\n  // disable zero copy if the buffer was actually copied\n  if ((serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED) && zeroCopyEnabled_) {\n    VLOG(2) << \"AsyncSocket::processZeroCopyMsg(): setting \"\n            << \"zeroCopyEnabled_ = false due to SO_EE_CODE_ZEROCOPY_COPIED \"\n            << \"on \" << fd_;\n    zeroCopyEnabled_ = false;\n  }\n\n  for (uint32_t i = lo; i <= hi; i++) {\n    releaseZeroCopyBuf(i);\n  }\n#else\n  (void)cmsg;\n#endif\n}\n\nvoid AsyncSocket::releaseIOBuf(\n    std::unique_ptr<folly::IOBuf> buf, ReleaseIOBufCallback* callback) {\n  if (!buf) {\n    return;\n  }\n  const size_t allocated = buf->computeChainCapacity();\n  DCHECK_GE(allocatedBytesBuffered_, allocated);\n  allocatedBytesBuffered_ -= allocated;\n  if (callback) {\n    callback->releaseIOBuf(std::move(buf));\n  }\n}\n\nvoid AsyncSocket::enableByteEvents() {\n  if (!byteEventHelper_) {\n    byteEventHelper_ = std::make_unique<ByteEventHelper>();\n  }\n\n  if (byteEventHelper_->byteEventsEnabled ||\n      byteEventHelper_->maybeEx.has_value()) {\n    return;\n  }\n\n  try {\n#if FOLLY_HAVE_SO_TIMESTAMPING\n    if (useIoUring_) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_SUPPORTED,\n          withAddr(\n              \"failed to enable byte events: \"\n              \"EVB is using io_uring\"));\n    }\n\n    // make sure we have a connected IP socket that supports error queues\n    // (Unix sockets do not support error queues)\n    if (NetworkSocket() == fd_ || !good()) {\n      throw AsyncSocketException(\n          AsyncSocketException::INVALID_STATE,\n          withAddr(\n              \"failed to enable byte events: \"\n              \"socket is not open or not in a good state\"));\n    }\n    folly::SocketAddress addr = {};\n    try {\n      // explicitly fetch local address (instead of using cache)\n      // to ensure socket is currently healthy\n      addr.setFromLocalAddress(fd_);\n    } catch (const std::system_error&) {\n      throw AsyncSocketException(\n          AsyncSocketException::INVALID_STATE,\n          withAddr(\n              \"failed to enable byte events: \"\n              \"socket is not open or not in a good state\"));\n    }\n    const auto family = addr.getFamily();\n    if (family != AF_INET && family != AF_INET6) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_SUPPORTED,\n          withAddr(\"failed to enable byte events: socket type not supported\"));\n    }\n\n    // check if timestamping is already enabled on the socket by another source\n    {\n      uint32_t flags = 0;\n      socklen_t len = sizeof(flags);\n      const auto ret =\n          getSockOptVirtual(SOL_SOCKET, SO_TIMESTAMPING, &flags, &len);\n      int getSockOptErrno = errno;\n      if (0 != ret) {\n        throw AsyncSocketException(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\n                \"failed to enable byte events: \"\n                \"timestamps may not be supported for this socket type \"\n                \"or socket be closed\"),\n            getSockOptErrno);\n      }\n      if (0 != flags) {\n        throw AsyncSocketException(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\n                \"failed to enable byte events: \"\n                \"timestamps may have already been enabled\"),\n            getSockOptErrno);\n      }\n    }\n\n    // enable control messages for software and hardware timestamps\n    // WriteFlags will determine which messages are generated\n    //\n    // SOF_TIMESTAMPING_OPT_ID: see discussion in ByteEventHelper::processCmsg\n    // SOF_TIMESTAMPING_OPT_TSONLY: only get timestamps, not original packet\n    // SOF_TIMESTAMPING_SOFTWARE: get software timestamps if generated\n    // SOF_TIMESTAMPING_RAW_HARDWARE: get hardware timestamps if generated\n    // SOF_TIMESTAMPING_OPT_TX_SWHW: get both sw + hw timestamps if generated\n    const uint32_t flags =\n        (folly::netops::SOF_TIMESTAMPING_OPT_ID |\n         folly::netops::SOF_TIMESTAMPING_OPT_TSONLY |\n         folly::netops::SOF_TIMESTAMPING_SOFTWARE |\n         folly::netops::SOF_TIMESTAMPING_RAW_HARDWARE |\n         folly::netops::SOF_TIMESTAMPING_OPT_TX_SWHW);\n    socklen_t len = sizeof(flags);\n\n    size_t byteEventsEnabledMaxAttempts = 0;\n    int setSockOptErrno = errno;\n\n    // When enabling byte events, the kernel resets the offset it uses for\n    // timestamps to 0 (see discussion in ByteEventHelper::processCmsg). By\n    // keeping track of the AsyncSocket raw byte offset when byte events were\n    // enabled, we can align the kernel and AsyncSocket byte offsets for future\n    // byte events.\n    //\n    // However, the kernel offset tracks the last unacknowledged byte, not the\n    // last written byte. This prevents us from aligning AsyncSocket's raw byte\n    // offset with the kernel offset in two scenarios:\n    //\n    // (1) If the kernel is still sending (packetizing) the bytes written before\n    //     enabling byte events, then the kernel offset is reset before all\n    //     bytes written to the kernel by AsyncSocket are sent.\n    //\n    // (2) If there are unacknowledged bytes in the TCP send buffer, because the\n    //     kernel offset tracks the last unacknowledged byte, not the last\n    //     written byte, the offset will end up being off.\n    //\n    // There is already a fix in the Linux kernel to reset the kernel offset\n    // according to write_seq (written bytes) instead of snd_una (unacknowledged\n    // bytes) [1].\n    //\n    // For kernels without this patch, we adopt the following solution:\n    //\n    // (1) We record the number of sent bytes and unacknowledged bytes before\n    //     and after enabling byte events. If they change, we disable and\n    //     re-enable. We repeat the process for a fixed number of times or until\n    //     the numbers before and after do not change. This fix is meant to\n    //     ensure that we can record the number of unacknowledged bytes at the\n    //     moment when we reset the kernel's timestamp offset.\n    //\n    // (2) We adjust the AsyncSocket byte offset when byte events were enabled\n    //     by the number of unacknowledged bytes at that point. Per (1) above,\n    //     we know that we captured the number of unacknowledged bytes when the\n    //     kernel's timestamp offset was reset, and thus can confidently adjust\n    //     offsets reported by the kernel going forward.\n    //\n    // [1]\n    // https://github.com/torvalds/linux/commit/b534dc46c8ae0165b1b2509be24dbea4fa9c4011\n\n    while (byteEventsEnabledMaxAttempts++ < kMaxAttemptsEnableByteEvents) {\n      folly::TcpInfo::LookupOptions options = {};\n      options.getMemInfo = true;\n      const auto expectTInfoBefore = getTcpInfo(options);\n      const auto ret =\n          setSockOptVirtual(SOL_SOCKET, SO_TIMESTAMPING, &flags, len);\n      const auto expectTInfoAfter = getTcpInfo(options);\n\n      if (ret != 0) {\n        throw AsyncSocketException(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"failed to enable byte events: setsockopt failed\"),\n            setSockOptErrno);\n      }\n\n      if (!expectTInfoBefore.hasValue() || !expectTInfoAfter.hasValue()) {\n        throw AsyncSocketException(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"failed to enable byte events: getTcpInfo failed\"),\n            setSockOptErrno);\n      }\n\n      const auto tInfoBefore = expectTInfoBefore.value();\n      const auto tInfoAfter = expectTInfoAfter.value();\n\n      if (tInfoBefore.bytesSent() != tInfoAfter.bytesSent() ||\n          tInfoBefore.sendBufInUseBytes() != tInfoAfter.sendBufInUseBytes() ||\n          !tInfoAfter.sendBufInUseBytes().has_value()) {\n        const uint32_t disableFlag = 0;\n        const auto disableReturnValue = setSockOptVirtual(\n            SOL_SOCKET, SO_TIMESTAMPING, &disableFlag, sizeof(disableFlag));\n        if (disableReturnValue != 0) {\n          throw AsyncSocketException(\n              AsyncSocketException::INTERNAL_ERROR,\n              withAddr(\n                  \"error when enabling byte events: \"\n                  \"failed to disable byte events after byte sent counters not matching\"),\n              setSockOptErrno);\n        }\n      } else {\n        const auto rawBytesWritten = getRawBytesWritten();\n        const auto bytesNotAcknowledged =\n            tInfoAfter.sendBufInUseBytes().value();\n\n        byteEventHelper_->byteEventsEnabled = true;\n        // it is possible for rawBytesWrittenWhenByteEventsEnabled to be\n        // negative if bytes were written to the underlying socket before\n        // this AsyncSocket was constructed\n        byteEventHelper_->rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWritten - bytesNotAcknowledged;\n\n        for (const auto& observer : lifecycleObservers_) {\n          if (observer->getConfig().byteEvents) {\n            observer->byteEventsEnabled(this);\n          }\n        }\n        return;\n      }\n    }\n\n    if (byteEventsEnabledMaxAttempts > kMaxAttemptsEnableByteEvents) {\n      throw AsyncSocketException(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(\n              \"failed to enable byte events: \"\n              \"could not account for bytes in flight in kernel byte offset\"),\n          setSockOptErrno);\n    }\n#endif // FOLLY_HAVE_SO_TIMESTAMPING\n    // unsupported by platform\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_SUPPORTED,\n        withAddr(\"failed to enable byte events: platform not supported\"));\n\n  } catch (const AsyncSocketException& ex) {\n    failByteEvents(ex);\n  }\n}\n\nvoid AsyncSocket::write(\n    WriteCallback* callback, const void* buf, size_t bytes, WriteFlags flags) {\n  iovec op;\n  op.iov_base = const_cast<void*>(buf);\n  op.iov_len = bytes;\n  writeImpl(callback, &op, 1, unique_ptr<IOBuf>(), bytes, flags);\n}\n\nvoid AsyncSocket::writev(\n    WriteCallback* callback, const iovec* vec, size_t count, WriteFlags flags) {\n  size_t totalBytes = 0;\n  for (size_t i = 0; i < count; ++i) {\n    totalBytes += vec[i].iov_len;\n  }\n  writeImpl(callback, vec, count, unique_ptr<IOBuf>(), totalBytes, flags);\n}\n\nvoid AsyncSocket::writeChain(\n    WriteCallback* callback, unique_ptr<IOBuf>&& buf, WriteFlags flags) {\n  adjustZeroCopyFlags(flags);\n\n  size_t count = buf->countChainElements();\n  if (count <= kSmallIoVecSize) {\n    // suppress \"warning: variable length array 'vec' is used [-Wvla]\"\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Wvla\")\n    iovec vec[BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallIoVecSize)];\n    FOLLY_POP_WARNING\n\n    writeChainImpl(callback, vec, count, std::move(buf), flags);\n  } else {\n    std::unique_ptr<iovec[]> vec(new iovec[count]);\n    writeChainImpl(callback, vec.get(), count, std::move(buf), flags);\n  }\n}\n\nvoid AsyncSocket::writeChainImpl(\n    WriteCallback* callback,\n    iovec* vec,\n    size_t count,\n    unique_ptr<IOBuf>&& buf,\n    WriteFlags flags) {\n  auto res = buf->fillIov(vec, count);\n  if (zeroCopyEnabled_ && !isSet(flags, WriteFlags::WRITE_MSG_ZEROCOPY) &&\n      buf->isManaged()) {\n    bool enableZeroCopy = std::visit(\n        folly::overload(\n            [](std::monostate) { return false; },\n            [&buf](const AsyncWriter::ZeroCopyEnableFunc& func) {\n              return func && func(buf);\n            },\n            [totalLength = res.totalLength](size_t threshold) {\n              return threshold > 0 && totalLength >= threshold;\n            }),\n        zeroCopyEnablePolicy_);\n    if (enableZeroCopy) {\n      flags |= WriteFlags::WRITE_MSG_ZEROCOPY;\n    }\n  }\n  writeImpl(\n      callback, vec, res.numIovecs, std::move(buf), res.totalLength, flags);\n}\n\nvoid AsyncSocket::writeImpl(\n    WriteCallback* callback,\n    const iovec* vec,\n    size_t count,\n    unique_ptr<IOBuf>&& buf,\n    size_t totalBytes,\n    WriteFlags flags) {\n  VLOG(6) << \"AsyncSocket::writev() this=\" << this << \", fd=\" << fd_\n          << \", callback=\" << callback << \", count=\" << count\n          << \", state=\" << state_;\n  DestructorGuard dg(this);\n  unique_ptr<IOBuf> ioBuf(std::move(buf));\n  eventBase_->dcheckIsInEventBaseThread();\n  WriteCallbackWithState callbackWithState(callback);\n\n  auto* releaseIOBufCallback =\n      callback ? callback->getReleaseIOBufCallback() : nullptr;\n\n  SCOPE_EXIT {\n    releaseIOBuf(std::move(ioBuf), releaseIOBufCallback);\n  };\n\n  totalAppBytesScheduledForWrite_ += totalBytes;\n  if (ioBuf) {\n    allocatedBytesBuffered_ += ioBuf->computeChainCapacity();\n  }\n\n  if (shutdownFlags_ & (SHUT_WRITE | SHUT_WRITE_PENDING)) {\n    // No new writes may be performed after the write side of the socket has\n    // been shutdown.\n    //\n    // We could just call callback->writeError() here to fail just this write.\n    // However, fail hard and use invalidState() to fail all outstanding\n    // callbacks and move the socket into the error state.  There's most likely\n    // a bug in the caller's code, so we abort everything rather than trying to\n    // proceed as best we can.\n    return invalidState(callback);\n  }\n\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  ssize_t bytesWritten = 0;\n  bool mustRegister = false;\n  if ((state_ == StateEnum::ESTABLISHED || state_ == StateEnum::FAST_OPEN) &&\n      !connecting()) {\n    if (!hasPendingWrites()) {\n      // If we are established and there are no other writes pending,\n      // we can attempt to perform the write immediately.\n      assert(writeReqTail_ == nullptr);\n      assert(iouSendHandle_ ? iouSendHandle_->empty() : true);\n      assert((eventFlags_ & EventHandler::WRITE) == 0);\n\n      callbackWithState.notifyOnWrite();\n\n      auto writeResult = performWrite(\n          vec,\n          uint32_t(count),\n          flags,\n          &countWritten,\n          &partialWritten,\n          WriteRequestTag{ioBuf.get()});\n      bytesWritten = writeResult.writeReturn;\n      if (bytesWritten < 0) {\n        auto errnoCopy = errno;\n        if (writeResult.exception) {\n          return failWrite(__func__, callback, 0, *writeResult.exception);\n        }\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"writev failed\"),\n            errnoCopy);\n        return failWrite(__func__, callback, 0, ex);\n      } else if (countWritten == count) {\n        // done, add the whole buffer\n        if (countWritten && isZeroCopyRequest(flags)) {\n          addZeroCopyBuf(std::move(ioBuf), releaseIOBufCallback);\n        } else {\n          releaseIOBuf(std::move(ioBuf), releaseIOBufCallback);\n        }\n\n        // We successfully wrote everything.\n        // Invoke the callback and return.\n        if (callback) {\n          callback->writeSuccess();\n        }\n        return;\n      } else { // continue writing the next writeReq\n        // add just the ptr\n        if (bytesWritten && isZeroCopyRequest(flags)) {\n          addZeroCopyBuf(ioBuf.get());\n        }\n      }\n      if (!connecting()) {\n        // Writes might put the socket back into connecting state\n        // if TFO is enabled, and using TFO fails.\n        // This means that write timeouts would not be active, however\n        // connect timeouts would affect this stage.\n        mustRegister = true;\n      }\n    }\n  } else if (!connecting()) {\n    // Invalid state for writing\n    return invalidState(callback);\n  }\n\n  if (!useIoUring_) {\n    // Create a new WriteRequest to add to the queue\n    WriteRequest* req;\n    try {\n      req = BytesWriteRequest::newRequest(\n          this,\n          callbackWithState,\n          vec + countWritten,\n          uint32_t(count - countWritten),\n          partialWritten,\n          uint32_t(bytesWritten),\n          std::move(ioBuf),\n          flags);\n    } catch (const std::exception& ex) {\n      // we mainly expect to catch std::bad_alloc here\n      AsyncSocketException tex(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(string(\"failed to append new WriteRequest: \") + ex.what()));\n      return failWrite(__func__, callback, size_t(bytesWritten), tex);\n    }\n    req->consume();\n    if (writeReqTail_ == nullptr) {\n      assert(writeReqHead_ == nullptr);\n      writeReqHead_ = writeReqTail_ = req;\n    } else {\n      writeReqTail_->append(req);\n      writeReqTail_ = req;\n    }\n  } else {\n    auto cb = callback ? callback->getReleaseIOBufCallback() : nullptr;\n    if (ioBuf) {\n      for (uint32_t i = countWritten; i != 0; --i) {\n        assert(ioBuf);\n        auto next = ioBuf->pop();\n        releaseIOBuf(std::move(ioBuf), cb);\n        ioBuf = std::move(next);\n      }\n    }\n    iouSendHandle_->write(\n        callback,\n        vec + countWritten,\n        count - countWritten,\n        partialWritten,\n        bytesWritten,\n        std::move(ioBuf),\n        flags);\n  }\n\n  if (bufferCallback_) {\n    bufferCallback_->onEgressBuffered();\n  }\n\n  // Register for write events if are established and not currently\n  // waiting on write events\n  if (mustRegister) {\n    assert(state_ == StateEnum::ESTABLISHED);\n    assert((eventFlags_ & EventHandler::WRITE) == 0);\n    if (!updateEventRegistration(EventHandler::WRITE, 0)) {\n      assert(state_ == StateEnum::ERROR);\n      return;\n    }\n    if (sendTimeout_ > 0) {\n      // Schedule a timeout to fire if the write takes too long.\n      if (!writeTimeout_.scheduleTimeout(sendTimeout_)) {\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"failed to schedule send timeout\"));\n        return failWrite(__func__, ex);\n      }\n    }\n  }\n}\n\nvoid AsyncSocket::writeRequest(WriteRequest* req) {\n  if (writeReqTail_ == nullptr) {\n    assert(writeReqHead_ == nullptr);\n    writeReqHead_ = writeReqTail_ = req;\n    req->start();\n  } else {\n    writeReqTail_->append(req);\n    writeReqTail_ = req;\n  }\n}\n\nvoid AsyncSocket::close() {\n  VLOG(5) << \"AsyncSocket::close(): this=\" << this << \", fd_=\" << fd_\n          << \", state=\" << state_ << \", shutdownFlags=\" << std::hex\n          << (int)shutdownFlags_;\n\n  // close() is only different from closeNow() when there are pending writes\n  // that need to drain before we can close.  In all other cases, just call\n  // closeNow().\n  //\n  // Note that writeReqHead_ can be non-nullptr even in STATE_CLOSED or\n  // STATE_ERROR if close() is invoked while a previous closeNow() or failure\n  // is still running.  (e.g., If there are multiple pending writes, and we\n  // call writeError() on the first one, it may call close().  In this case we\n  // will already be in STATE_CLOSED or STATE_ERROR, but the remaining pending\n  // writes will still be in the queue.)\n  //\n  // We only need to drain pending writes if we are still in STATE_CONNECTING\n  // or STATE_ESTABLISHED\n  if ((!hasPendingWrites()) ||\n      !(state_ == StateEnum::CONNECTING || state_ == StateEnum::ESTABLISHED)) {\n    closeNow();\n    return;\n  }\n\n  // Declare a DestructorGuard to ensure that the AsyncSocket cannot be\n  // destroyed until close() returns.\n  DestructorGuard dg(this);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  // Since there are write requests pending, we have to set the\n  // SHUT_WRITE_PENDING flag, and wait to perform the real close until the\n  // connect finishes and we finish writing these requests.\n  //\n  // Set SHUT_READ to indicate that reads are shut down, and set the\n  // SHUT_WRITE_PENDING flag to mark that we want to shutdown once the\n  // pending writes complete.\n  shutdownFlags_ |= (SHUT_READ | SHUT_WRITE_PENDING);\n\n  // If a read callback is set, invoke readEOF() immediately to inform it that\n  // the socket has been closed and no more data can be read.\n  if (readCallback_) {\n    // Disable reads if they are enabled\n    if (!updateEventRegistration(0, EventHandler::READ)) {\n      // We're now in the error state; callbacks have been cleaned up\n      assert(state_ == StateEnum::ERROR);\n      assert(readCallback_ == nullptr);\n    } else {\n      ReadCallback* callback = readCallback_;\n      readCallback_ = nullptr;\n      callback->readEOF();\n    }\n  }\n}\n\nvoid AsyncSocket::closeNow() {\n  VLOG(5) << \"AsyncSocket::closeNow(): this=\" << this << \", fd_=\" << fd_\n          << \", state=\" << state_ << \", shutdownFlags=\" << std::hex\n          << (int)shutdownFlags_;\n  DestructorGuard dg(this);\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  if (iouRecvHandle_) {\n    iouRecvHandle_->cancel();\n    iouRecvHandle_.reset();\n  }\n\n  switch (state_) {\n    case StateEnum::ESTABLISHED:\n    case StateEnum::CONNECTING:\n    case StateEnum::FAST_OPEN: {\n      shutdownFlags_ |= (SHUT_READ | SHUT_WRITE);\n      state_ = StateEnum::CLOSED;\n\n      // If the write timeout was set, cancel it.\n      writeTimeout_.cancelTimeout();\n\n      // If we are registered for I/O events, unregister.\n      if (eventFlags_ != EventHandler::NONE) {\n        eventFlags_ = EventHandler::NONE;\n        if (!updateEventRegistration()) {\n          // We will have been moved into the error state.\n          assert(state_ == StateEnum::ERROR);\n          return;\n        }\n      }\n\n      if (immediateReadHandler_.isLoopCallbackScheduled()) {\n        immediateReadHandler_.cancelLoopCallback();\n      }\n\n      if (fd_ != NetworkSocket()) {\n        ioHandler_.changeHandlerFD(NetworkSocket());\n        doClose();\n      }\n\n      invokeConnectErr(getSocketClosedLocallyEx());\n      if (iouConnectHandle_) {\n        if (iouConnectHandle_->cancel()) {\n          iouConnectHandle_.release();\n        } else {\n          iouConnectHandle_.reset();\n        }\n      }\n\n      failAllWrites(getSocketClosedLocallyEx());\n\n      if (readCallback_) {\n        ReadCallback* callback = readCallback_;\n        readCallback_ = nullptr;\n        callback->readEOF();\n      }\n      return;\n    }\n    case StateEnum::CLOSED:\n      // Do nothing.  It's possible that we are being called recursively\n      // from inside a callback that we invoked inside another call to close()\n      // that is still running.\n      return;\n    case StateEnum::ERROR:\n      // Do nothing.  The error handling code has performed (or is performing)\n      // cleanup.\n      return;\n    case StateEnum::UNINIT:\n      assert(eventFlags_ == EventHandler::NONE);\n      assert(connectCallback_ == nullptr);\n      assert(readCallback_ == nullptr);\n      assert(!hasPendingWrites());\n      shutdownFlags_ |= (SHUT_READ | SHUT_WRITE);\n      state_ = StateEnum::CLOSED;\n      return;\n  }\n\n  LOG(DFATAL) << \"AsyncSocket::closeNow() (this=\" << this << \", fd=\" << fd_\n              << \") called in unknown state \" << state_;\n}\n\nvoid AsyncSocket::closeWithReset() {\n  // Enable SO_LINGER, with the linger timeout set to 0.\n  // This will trigger a TCP reset when we close the socket.\n  if (fd_ != NetworkSocket()) {\n    struct linger optLinger = {1, 0};\n    if (setSockOpt(SOL_SOCKET, SO_LINGER, &optLinger) != 0) {\n      VLOG(2) << \"AsyncSocket::closeWithReset(): error setting SO_LINGER \"\n              << \"on \" << fd_ << \": errno=\" << errno;\n    }\n  }\n\n  // Then let closeNow() take care of the rest\n  closeNow();\n}\n\nvoid AsyncSocket::shutdownWrite() {\n  VLOG(5) << \"AsyncSocket::shutdownWrite(): this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \", shutdownFlags=\" << std::hex\n          << (int)shutdownFlags_;\n\n  // If there are no pending writes, shutdownWrite() is identical to\n  // shutdownWriteNow().\n  if (!hasPendingWrites()) {\n    shutdownWriteNow();\n    return;\n  }\n\n  eventBase_->dcheckIsInEventBaseThread();\n\n  // There are pending writes.  Set SHUT_WRITE_PENDING so that the actual\n  // shutdown will be performed once all writes complete.\n  shutdownFlags_ |= SHUT_WRITE_PENDING;\n}\n\nvoid AsyncSocket::shutdownWriteNow() {\n  VLOG(5) << \"AsyncSocket::shutdownWriteNow(): this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \", shutdownFlags=\" << std::hex\n          << (int)shutdownFlags_;\n\n  if (shutdownFlags_ & SHUT_WRITE) {\n    // Writes are already shutdown; nothing else to do.\n    return;\n  }\n\n  // If SHUT_READ is already set, just call closeNow() to completely\n  // close the socket.  This can happen if close() was called with writes\n  // pending, and then shutdownWriteNow() is called before all pending writes\n  // complete.\n  if (shutdownFlags_ & SHUT_READ) {\n    closeNow();\n    return;\n  }\n\n  DestructorGuard dg(this);\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  switch (static_cast<StateEnum>(state_)) {\n    case StateEnum::ESTABLISHED: {\n      shutdownFlags_ |= SHUT_WRITE;\n\n      // If the write timeout was set, cancel it.\n      writeTimeout_.cancelTimeout();\n\n      // If we are registered for write events, unregister.\n      if (!updateEventRegistration(0, EventHandler::WRITE)) {\n        // We will have been moved into the error state.\n        assert(state_ == StateEnum::ERROR);\n        return;\n      }\n\n      // Shutdown writes on the file descriptor\n      netops_->shutdown(fd_, SHUT_WR);\n\n      // Immediately fail all write requests\n      failAllWrites(getSocketShutdownForWritesEx());\n      return;\n    }\n    case StateEnum::CONNECTING: {\n      // Set the SHUT_WRITE_PENDING flag.\n      // When the connection completes, it will check this flag,\n      // shutdown the write half of the socket, and then set SHUT_WRITE.\n      shutdownFlags_ |= SHUT_WRITE_PENDING;\n\n      // Immediately fail all write requests\n      failAllWrites(getSocketShutdownForWritesEx());\n      return;\n    }\n    case StateEnum::UNINIT:\n      // Callers normally shouldn't call shutdownWriteNow() before the socket\n      // even starts connecting.  Nonetheless, go ahead and set\n      // SHUT_WRITE_PENDING.  Once the socket eventually connects it will\n      // immediately shut down the write side of the socket.\n      shutdownFlags_ |= SHUT_WRITE_PENDING;\n      return;\n    case StateEnum::FAST_OPEN:\n      // In fast open state we haven't call connected yet, and if we shutdown\n      // the writes, we will never try to call connect, so shut everything down\n      shutdownFlags_ |= SHUT_WRITE;\n      // Immediately fail all write requests\n      failAllWrites(getSocketShutdownForWritesEx());\n      return;\n    case StateEnum::CLOSED:\n    case StateEnum::ERROR:\n      // We should never get here.  SHUT_WRITE should always be set\n      // in STATE_CLOSED and STATE_ERROR.\n      VLOG(4)\n          << \"AsyncSocket::shutdownWriteNow() (this=\" << this << \", fd=\" << fd_\n          << \") in unexpected state \" << state_ << \" with SHUT_WRITE not set (\"\n          << std::hex << (int)shutdownFlags_ << \")\";\n      assert(false);\n      return;\n  }\n\n  LOG(DFATAL) << \"AsyncSocket::shutdownWriteNow() (this=\" << this\n              << \", fd=\" << fd_ << \") called in unknown state \" << state_;\n}\n\nbool AsyncSocket::readable() const {\n  if (fd_ == NetworkSocket()) {\n    return false;\n  }\n\n  if (preReceivedData_ && !preReceivedData_->empty()) {\n    return true;\n  }\n  netops::PollDescriptor fds[1];\n  fds[0].fd = fd_;\n  fds[0].events = POLLIN;\n  fds[0].revents = 0;\n  int rc = netops_->poll(fds, 1, 0);\n  return rc == 1;\n}\n\nbool AsyncSocket::writable() const {\n  if (fd_ == NetworkSocket()) {\n    return false;\n  }\n  netops::PollDescriptor fds[1];\n  fds[0].fd = fd_;\n  fds[0].events = POLLOUT;\n  fds[0].revents = 0;\n  int rc = netops_->poll(fds, 1, 0);\n  return rc == 1;\n}\n\nbool AsyncSocket::isPending() const {\n  return ioHandler_.isPending();\n}\n\nbool AsyncSocket::hangup() const {\n  if (fd_ == NetworkSocket()) {\n    // sanity check, no one should ask for hangup if we are not connected.\n    assert(false);\n    return false;\n  }\n#ifdef POLLRDHUP // Linux-only\n  netops::PollDescriptor fds[1];\n  fds[0].fd = fd_;\n  fds[0].events = POLLRDHUP | POLLHUP;\n  fds[0].revents = 0;\n  netops_->poll(fds, 1, 0);\n  return (fds[0].revents & (POLLRDHUP | POLLHUP)) != 0;\n#else\n  return false;\n#endif\n}\n\nbool AsyncSocket::good() const {\n  return (\n      (state_ == StateEnum::CONNECTING || state_ == StateEnum::FAST_OPEN ||\n       state_ == StateEnum::ESTABLISHED) &&\n      (shutdownFlags_ == 0) && (eventBase_ != nullptr));\n}\n\nbool AsyncSocket::error() const {\n  return (state_ == StateEnum::ERROR);\n}\n\nvoid AsyncSocket::attachEventBase(EventBase* eventBase) {\n  VLOG(5)\n      << \"AsyncSocket::attachEventBase(this=\" << this << \", fd=\" << fd_\n      << \", old evb=\" << eventBase_ << \", new evb=\" << eventBase\n      << \", state=\" << state_ << \", events=\" << std::hex << eventFlags_ << \")\";\n  assert(eventBase_ == nullptr);\n  eventBase->dcheckIsInEventBaseThread();\n\n  eventBase_ = eventBase;\n  if (useIoUring_) {\n    if (eventFlags_ & EventHandler::READ) {\n      CHECK(iouRecvHandle_ != nullptr);\n      CHECK(readCallback_ != nullptr);\n      iouRecvHandle_ = IoUringRecvHandle::clone(\n          eventBase, fd_, addr_, this, std::move(iouRecvHandle_));\n    }\n    if (iouSendHandle_) {\n      iouSendHandle_ =\n          IoUringSendHandle::clone(eventBase, std::move(iouSendHandle_));\n    }\n  }\n\n  ioHandler_.attachEventBase(eventBase);\n\n  updateEventRegistration();\n\n  writeTimeout_.attachEventBase(eventBase);\n  if (evbChangeCb_) {\n    evbChangeCb_->evbAttached(this);\n  }\n\n  // legacy observer support\n  for (const auto& cb : lifecycleObservers_) {\n    cb->evbAttach(this, eventBase_);\n  }\n\n  // folly::ObserverContainer observer support\n  if (auto list = getAsyncSocketObserverContainer()) {\n    list->invokeInterfaceMethodAllObservers([&](auto observer, auto observed) {\n      observer->evbAttach(observed, eventBase_);\n    });\n  }\n}\n\nvoid AsyncSocket::detachEventBase() {\n  VLOG(5) << \"AsyncSocket::detachEventBase(this=\" << this << \", fd=\" << fd_\n          << \", old evb=\" << eventBase_ << \", state=\" << state_\n          << \", events=\" << std::hex << eventFlags_ << \")\";\n  assert(eventBase_ != nullptr);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  // Make a copy of the existing event base, to invoke lifecycle observer\n  // callbacks\n  EventBase* existingEvb = eventBase_;\n\n  eventBase_ = nullptr;\n\n  if (useIoUring_) {\n    if (iouRecvHandle_) {\n      iouRecvHandle_->detachEventBase();\n    }\n\n    if (iouSendHandle_) {\n      iouSendHandle_->update(EventHandler::NONE);\n      iouSendHandle_->detachEventBase();\n    }\n  }\n\n  ioHandler_.unregisterHandler();\n\n  ioHandler_.detachEventBase();\n  writeTimeout_.detachEventBase();\n  if (evbChangeCb_) {\n    evbChangeCb_->evbDetached(this);\n  }\n\n  // legacy observer support\n  for (const auto& cb : lifecycleObservers_) {\n    cb->evbDetach(this, existingEvb);\n  }\n\n  // folly::ObserverContainer observer support\n  if (auto list = getAsyncSocketObserverContainer()) {\n    list->invokeInterfaceMethodAllObservers(\n        [existingEvb](auto observer, auto observed) {\n          observer->evbDetach(observed, existingEvb);\n        });\n  }\n}\n\nbool AsyncSocket::isDetachable() const {\n  DCHECK(eventBase_ != nullptr);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  return !writeTimeout_.isScheduled();\n}\n\nvoid AsyncSocket::cacheAddresses() {\n  if (fd_ != NetworkSocket()) {\n    try {\n      cacheLocalAddress();\n      cachePeerAddress();\n    } catch (const std::system_error& e) {\n      if (e.code() !=\n          std::error_code(ENOTCONN, errorCategoryForErrnoDomain())) {\n        VLOG(2) << \"Error caching addresses: \" << e.code().value() << \", \"\n                << e.code().message();\n      }\n    }\n  }\n}\n\nvoid AsyncSocket::cacheLocalAddress() const {\n  if (!localAddr_.isInitialized() && fd_ != NetworkSocket()) {\n    localAddr_.setFromLocalAddress(fd_);\n  }\n}\n\nvoid AsyncSocket::cachePeerAddress() const {\n  if (!addr_.isInitialized() && fd_ != NetworkSocket()) {\n    addr_.setFromPeerAddress(fd_);\n  }\n}\n\nvoid AsyncSocket::applyOptions(\n    const SocketOptionMap& options, SocketOptionKey::ApplyPos pos) {\n  auto result = applySocketOptions(fd_, options, pos);\n  if (result != 0) {\n    throw AsyncSocketException(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(\"failed to set socket option\"),\n        result);\n  }\n}\n\nbool AsyncSocket::isZeroCopyWriteInProgress() const noexcept {\n  eventBase_->dcheckIsInEventBaseThread();\n  return (!idZeroCopyBufPtrMap_.empty());\n}\n\nvoid AsyncSocket::getLocalAddress(folly::SocketAddress* address) const {\n  cacheLocalAddress();\n  *address = localAddr_;\n}\n\nvoid AsyncSocket::getPeerAddress(folly::SocketAddress* address) const {\n  cachePeerAddress();\n  *address = addr_;\n}\n\nbool AsyncSocket::getTFOSucceeded() const {\n  return detail::tfo_succeeded(fd_);\n}\n\nint AsyncSocket::setNoDelay(bool noDelay) {\n  if (fd_ == NetworkSocket()) {\n    VLOG(4) << \"AsyncSocket::setNoDelay() called on non-open socket \" << this\n            << \"(state=\" << state_ << \")\";\n    return EINVAL;\n  }\n\n  int value = noDelay ? 1 : 0;\n  if (netops_->setsockopt(\n          fd_, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) != 0) {\n    int errnoCopy = errno;\n    VLOG(2) << \"failed to update TCP_NODELAY option on AsyncSocket \" << this\n            << \" (fd=\" << fd_ << \", state=\" << state_\n            << \"): \" << errnoStr(errnoCopy);\n    return errnoCopy;\n  }\n\n  return 0;\n}\n\nint AsyncSocket::setCongestionFlavor(const std::string& cname) {\n#ifndef TCP_CONGESTION\n#define TCP_CONGESTION 13\n#endif\n\n  if (fd_ == NetworkSocket()) {\n    VLOG(4) << \"AsyncSocket::setCongestionFlavor() called on non-open \"\n            << \"socket \" << this << \"(state=\" << state_ << \")\";\n    return EINVAL;\n  }\n\n  if (netops_->setsockopt(\n          fd_,\n          IPPROTO_TCP,\n          TCP_CONGESTION,\n          cname.c_str(),\n          socklen_t(cname.length() + 1)) != 0) {\n    int errnoCopy = errno;\n    VLOG(2) << \"failed to update TCP_CONGESTION option on AsyncSocket \" << this\n            << \"(fd=\" << fd_ << \", state=\" << state_\n            << \"): \" << errnoStr(errnoCopy);\n    return errnoCopy;\n  }\n\n  return 0;\n}\n\nint AsyncSocket::setQuickAck(bool quickack) {\n  (void)quickack;\n  if (fd_ == NetworkSocket()) {\n    VLOG(4) << \"AsyncSocket::setQuickAck() called on non-open socket \" << this\n            << \"(state=\" << state_ << \")\";\n    return EINVAL;\n  }\n\n#ifdef TCP_QUICKACK // Linux-only\n  int value = quickack ? 1 : 0;\n  if (netops_->setsockopt(\n          fd_, IPPROTO_TCP, TCP_QUICKACK, &value, sizeof(value)) != 0) {\n    int errnoCopy = errno;\n    VLOG(2) << \"failed to update TCP_QUICKACK option on AsyncSocket\" << this\n            << \"(fd=\" << fd_ << \", state=\" << state_\n            << \"): \" << errnoStr(errnoCopy);\n    return errnoCopy;\n  }\n\n  return 0;\n#else\n  return ENOSYS;\n#endif\n}\n\nint AsyncSocket::setSendBufSize(size_t bufsize) {\n  if (fd_ == NetworkSocket()) {\n    VLOG(4) << \"AsyncSocket::setSendBufSize() called on non-open socket \"\n            << this << \"(state=\" << state_ << \")\";\n    return EINVAL;\n  }\n\n  if (netops_->setsockopt(\n          fd_, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) != 0) {\n    int errnoCopy = errno;\n    VLOG(2) << \"failed to update SO_SNDBUF option on AsyncSocket\" << this\n            << \"(fd=\" << fd_ << \", state=\" << state_\n            << \"): \" << errnoStr(errnoCopy);\n    return errnoCopy;\n  }\n\n  return 0;\n}\n\nint AsyncSocket::setRecvBufSize(size_t bufsize) {\n  if (fd_ == NetworkSocket()) {\n    VLOG(4) << \"AsyncSocket::setRecvBufSize() called on non-open socket \"\n            << this << \"(state=\" << state_ << \")\";\n    return EINVAL;\n  }\n\n  if (netops_->setsockopt(\n          fd_, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) != 0) {\n    int errnoCopy = errno;\n    VLOG(2) << \"failed to update SO_RCVBUF option on AsyncSocket\" << this\n            << \"(fd=\" << fd_ << \", state=\" << state_\n            << \"): \" << errnoStr(errnoCopy);\n    return errnoCopy;\n  }\n\n  return 0;\n}\n\n#if defined(__linux__)\nsize_t AsyncSocket::getSendBufInUse() const {\n  if (fd_ == NetworkSocket()) {\n    std::stringstream issueString;\n    issueString << \"AsyncSocket::getSendBufInUse() called on non-open socket \"\n                << this << \"(state=\" << state_ << \")\";\n    VLOG(4) << issueString.str();\n    throw std::logic_error(issueString.str());\n  }\n\n  size_t returnValue = 0;\n  if (-1 == ::ioctl(fd_.toFd(), SIOCOUTQ, &returnValue)) {\n    int errnoCopy = errno;\n    std::stringstream issueString;\n    issueString\n        << \"Failed to get the tx used bytes on Socket: \" << this << \"(fd=\"\n        << fd_ << \", state=\" << state_ << \"): \" << errnoStr(errnoCopy);\n    VLOG(2) << issueString.str();\n    throw std::logic_error(issueString.str());\n  }\n\n  return returnValue;\n}\n\nsize_t AsyncSocket::getRecvBufInUse() const {\n  if (fd_ == NetworkSocket()) {\n    std::stringstream issueString;\n    issueString << \"AsyncSocket::getRecvBufInUse() called on non-open socket \"\n                << this << \"(state=\" << state_ << \")\";\n    VLOG(4) << issueString.str();\n    throw std::logic_error(issueString.str());\n  }\n\n  size_t returnValue = 0;\n  if (-1 == ::ioctl(fd_.toFd(), SIOCINQ, &returnValue)) {\n    std::stringstream issueString;\n    int errnoCopy = errno;\n    issueString\n        << \"Failed to get the rx used bytes on Socket: \" << this << \"(fd=\"\n        << fd_ << \", state=\" << state_ << \"): \" << errnoStr(errnoCopy);\n    VLOG(2) << issueString.str();\n    throw std::logic_error(issueString.str());\n  }\n\n  return returnValue;\n}\n#endif\n\nint AsyncSocket::setTCPProfile(int profd) {\n  if (fd_ == NetworkSocket()) {\n    VLOG(4) << \"AsyncSocket::setTCPProfile() called on non-open socket \" << this\n            << \"(state=\" << state_ << \")\";\n    return EINVAL;\n  }\n\n  if (netops_->setsockopt(\n          fd_, SOL_SOCKET, SO_SET_NAMESPACE, &profd, sizeof(int)) != 0) {\n    int errnoCopy = errno;\n    VLOG(2) << \"failed to set socket namespace option on AsyncSocket\" << this\n            << \"(fd=\" << fd_ << \", state=\" << state_\n            << \"): \" << errnoStr(errnoCopy);\n    return errnoCopy;\n  }\n\n  return 0;\n}\n\nvoid AsyncSocket::ioReady(uint16_t events) noexcept {\n  VLOG(7) << \"AsyncSocket::ioRead() this=\" << this << \", fd=\" << fd_\n          << \", events=\" << std::hex << events << \", state=\" << state_;\n  DestructorGuard dg(this);\n  assert(events & EventHandler::READ_WRITE);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  auto relevantEvents = uint16_t(events & EventHandler::READ_WRITE);\n  EventBase* originalEventBase = eventBase_;\n  // If we got there it means that either EventHandler::READ or\n  // EventHandler::WRITE is set. Any of these flags can\n  // indicate that there are messages available in the socket\n  // error message queue.\n  // Return if we handle any error messages - this is to avoid\n  // unnecessary read/write calls\n  if (handleErrMessages()) {\n    return;\n  }\n\n  // Return now if handleErrMessages() detached us from our EventBase\n  if (eventBase_ != originalEventBase) {\n    return;\n  }\n\n  const auto startRawBytesReceived = getRawBytesReceived();\n  const auto startAppBytesReceived = getAppBytesReceived();\n  const auto startRawBytesWritten = getRawBytesWritten();\n\n  if (relevantEvents == EventHandler::READ) {\n    handleRead();\n  } else if (relevantEvents == EventHandler::WRITE) {\n    handleWrite();\n  } else if (relevantEvents == EventHandler::READ_WRITE) {\n    // If both read and write events are ready, process writes first.\n    handleWrite();\n\n    // Return now if handleWrite() detached us from our EventBase\n    if (eventBase_ != originalEventBase) {\n      return;\n    }\n\n    // Only call handleRead() if a read callback is still installed.\n    // (It's possible that the read callback was uninstalled during\n    // handleWrite().)\n    if (readCallback_) {\n      handleRead();\n    }\n  } else {\n    VLOG(4) << \"AsyncSocket::ioRead() called with unexpected events \"\n            << std::hex << events << \"(this=\" << this << \")\";\n    abort();\n  }\n\n  // It is possible that there are messages in the error queue yet\n  // `handleErrMessages()` returns without reading them because no error\n  // message callback is set and byte events are disabled. This could happen\n  // when a write is performed to an AsyncSocket with byte events enabled,\n  // triggers timestamps, and the fd is detached from the AsyncSocket and\n  // attached to a new AsyncSocket before the error messages generated for those\n  // timestamps arrive on the error queue. These `orphan` messages in the error\n  // queue will cause us to spin: `ioReady()` will be repeatedly invoked, but\n  // because we are not reading from the socket error queue, the state will\n  // never be cleared.\n  //\n  // To prevent spinning under such circumstances, we\n  // drain the queue of AF_INET sockets if the read or write handlers did not\n  // read or write anything on an invocation of `ioReady()`. We check both raw\n  // and app bytes received because raw bytes received are 0 for AsyncSSLSockets\n  // with no BIO. Because the read or write handlers can modify the event flags,\n  // we check these flags again before draining the error queue. We restrict\n  // the drain to AF_INET sockets as other socket family do not support\n  // MSG_ERRQUEUE (e.g., AF_UNIX) or their support is not well documented\n  if (startRawBytesReceived == getRawBytesReceived() &&\n      startAppBytesReceived == getAppBytesReceived() &&\n      startRawBytesWritten == getRawBytesWritten() &&\n      eventFlags_ != EventHandler::NONE && eventFlags_ == events &&\n      (localAddr_.getFamily() == AF_INET ||\n       localAddr_.getFamily() == AF_INET6)) {\n    drainErrorQueue();\n  }\n}\n\nAsyncSocket::ReadResult AsyncSocket::performReadMsg(\n    struct ::msghdr& msg,\n    // This is here only to preserve AsyncSSLSocket's legacy semi-broken\n    // behavior (D43648653 for context).\n    AsyncReader::ReadCallback::ReadMode readMode) {\n  VLOG(5) << \"AsyncSocket::performReadMsg() this=\" << this\n          << \", iovs=\" << msg.msg_iov << \", num=\" << msg.msg_iovlen;\n\n  if (!msg.msg_iovlen) {\n    return ReadResult(READ_ERROR);\n  }\n\n  if (preReceivedData_ && !preReceivedData_->empty()) {\n    VLOG(5) << \"AsyncSocket::performReadMsg() this=\" << this\n            << \", reading pre-received data\";\n\n    ssize_t len = 0;\n    for (size_t i = 0;\n         // MacOS `msg_iovlen` is an `int` :(\n         (i < static_cast<size_t>(msg.msg_iovlen)) &&\n         (!preReceivedData_->empty());\n         ++i) {\n      io::Cursor cursor(preReceivedData_.get());\n      auto ret =\n          cursor.pullAtMost(msg.msg_iov[i].iov_base, msg.msg_iov[i].iov_len);\n      len += ret;\n\n      IOBufQueue queue;\n      queue.append(std::move(preReceivedData_));\n      queue.trimStart(ret);\n      preReceivedData_ = queue.move();\n    }\n\n    appBytesReceived_ += len;\n    return ReadResult(len);\n  }\n\n  ssize_t bytes = 0;\n  if (readMode == AsyncReader::ReadCallback::ReadMode::ReadZC) {\n    DestructorGuard dg(this);\n    // ReadZC is async, where the current code path triggered from POLLIN will\n    // issue the request but the completion happens at a later point.\n    // AsyncSocket reads are level triggered so it is possible for another\n    // ReadZC request to be issued before an already issued request has\n    // completed. To fix this, unset the readCB.\n    auto readCallback = readCallback_;\n    setReadCB(nullptr);\n    auto backend = getEventBase()->getBackend();\n    auto readZcCallback = [guard = std::move(dg), readCallback](ssize_t len) {\n      if (len < 0) {\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR, \"ReadZC failed\", len);\n        readCallback->readErr(ex);\n      } else {\n        readCallback->readDataAvailable(len);\n      }\n    };\n    backend->queueRecvZc(\n        fd_.toFd(),\n        msg.msg_iov[0].iov_base,\n        msg.msg_iov[0].iov_len,\n        std::move(readZcCallback));\n    return ReadResult(READ_ASYNC);\n  } else if (readAncillaryDataCallback_ == nullptr && msg.msg_iovlen == 1) {\n    bytes = netops_->recv(\n        fd_, msg.msg_iov[0].iov_base, msg.msg_iov[0].iov_len, MSG_DONTWAIT);\n  } else {\n    int recvFlags = 0;\n    if (readAncillaryDataCallback_) {\n      auto buf = readAncillaryDataCallback_->getAncillaryDataCtrlBuffer();\n      msg.msg_control = buf.data();\n      msg.msg_controllen = buf.size();\n#if defined(__linux__)\n      // On BSD / MacOS, `AsyncFdSocket` has to do 2 extra `fcntl`s per FD.\n      recvFlags |= MSG_CMSG_CLOEXEC;\n#endif\n    } else {\n      msg.msg_control = nullptr;\n      msg.msg_controllen = 0;\n    }\n\n    // `msg.msg_iov*` were set by the caller, we're ready.\n    bytes = netops::recvmsg(fd_, &msg, recvFlags);\n\n    // KEY INVARIANT: If `bytes > 0`, we must proceed to `ancillaryData` --\n    // no error branches must interrupt this flow.  The reason is that\n    // otherwise, received FDs could be irretrievably leaked, causing\n    // eventual process failure due to `EMFILE`.\n    //\n    // NB: We do not check for MSG_CTRUNC here for the reason above.  We do\n    // not check for it _after_ the callbacks have fired because our\n    // `ReadCallback` could move the socket to a different thread, which\n    // would make a subsequent `failRead` unsafe.  Instead we require\n    // `ReadAncillaryDataCallback` implementations to check this.\n  }\n\n  if (bytes < 0) {\n    if (errno == EAGAIN || errno == EWOULDBLOCK) {\n      // No more data to read right now.\n      return ReadResult(READ_BLOCKING);\n    } else {\n      return ReadResult(READ_ERROR);\n    }\n  } else {\n    appBytesReceived_ += bytes;\n    return ReadResult(bytes);\n  }\n}\n\nvoid AsyncSocket::prepareReadBuffer(void** buf, size_t* buflen) {\n  // no matter what, buffer should be prepared for non-ssl socket\n  CHECK(readCallback_);\n  readCallback_->getReadBuffer(buf, buflen);\n}\n\nvoid AsyncSocket::prepareReadBuffers(IOBufIovecBuilder::IoVecVec& iovs) {\n  // no matter what, buffers should be prepared for non-ssl socket\n  CHECK(readCallback_);\n  readCallback_->getReadBuffers(iovs);\n}\n\nvoid AsyncSocket::drainErrorQueue() noexcept {\n  VLOG(5) << \"AsyncSocket::drainErrorQueue() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_;\n\n  if (errMessageCallback_ != nullptr ||\n      (byteEventHelper_ && byteEventHelper_->byteEventsEnabled)) {\n    VLOG(7) << \"AsyncSocket::drainErrorQueue(): \"\n            << \"err message callback installed or \"\n            << \"ByteEvents enabled - exiting.\";\n    return;\n  }\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  int ret = 0;\n  while (ret >= 0 && fd_ != NetworkSocket()) {\n    uint8_t ctrl[1024];\n    unsigned char data;\n\n    struct iovec entry;\n    entry.iov_base = &data;\n    entry.iov_len = sizeof(data);\n\n    struct msghdr msg;\n    msg.msg_iov = &entry;\n    msg.msg_iovlen = 1;\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_control = ctrl;\n    msg.msg_controllen = sizeof(ctrl);\n    msg.msg_flags = 0;\n\n    ret = netops_->recvmsg(fd_, &msg, MSG_ERRQUEUE);\n  }\n#endif\n}\n\nsize_t AsyncSocket::handleErrMessages() noexcept {\n  // This method has non-empty implementation only for platforms\n  // supporting per-socket error queues.\n  VLOG(5) << \"AsyncSocket::handleErrMessages() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_;\n  if (errMessageCallback_ == nullptr && idZeroCopyBufPtrMap_.empty() &&\n      (!byteEventHelper_ || !byteEventHelper_->byteEventsEnabled)) {\n    VLOG(7) << \"AsyncSocket::handleErrMessages(): \"\n            << \"no err message callback installed and \"\n            << \"ByteEvents not enabled - exiting.\";\n    return 0;\n  }\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  uint8_t ctrl[1024];\n  unsigned char data;\n  struct msghdr msg;\n  iovec entry;\n\n  entry.iov_base = &data;\n  entry.iov_len = sizeof(data);\n  msg.msg_iov = &entry;\n  msg.msg_iovlen = 1;\n  msg.msg_name = nullptr;\n  msg.msg_namelen = 0;\n  msg.msg_control = ctrl;\n  msg.msg_controllen = sizeof(ctrl);\n  msg.msg_flags = 0;\n\n  int ret;\n  size_t num = 0;\n  // the socket may be closed by errMessage callback, so check on each iteration\n  while (fd_ != NetworkSocket()) {\n    ret = netops_->recvmsg(fd_, &msg, MSG_ERRQUEUE);\n    VLOG(5) << \"AsyncSocket::handleErrMessages(): recvmsg returned \" << ret;\n\n    if (ret < 0) {\n      if (errno != EAGAIN) {\n        auto errnoCopy = errno;\n        LOG(ERROR) << \"::recvmsg exited with code \" << ret\n                   << \", errno: \" << errnoCopy << \", fd: \" << fd_;\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"recvmsg() failed\"),\n            errnoCopy);\n        failErrMessageRead(__func__, ex);\n      }\n\n      return num;\n    }\n\n    for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n         cmsg != nullptr && cmsg->cmsg_len != 0;\n         cmsg = CMSG_NXTHDR(&msg, cmsg)) {\n      ++num;\n      if (isZeroCopyMsg(*cmsg)) {\n        processZeroCopyMsg(*cmsg);\n        continue;\n      }\n\n      // try to process it as a ByteEvent and forward to observers\n      //\n      // observers cannot throw and thus we expect only exceptions from\n      // ByteEventHelper, but we guard against other cases for safety\n      if (byteEventHelper_) {\n        try {\n          if (const auto maybeByteEvent =\n                  byteEventHelper_->processCmsg(*cmsg, getRawBytesWritten())) {\n            const auto& byteEvent = maybeByteEvent.value();\n            for (const auto& observer : lifecycleObservers_) {\n              if (observer->getConfig().byteEvents) {\n                observer->byteEvent(this, byteEvent);\n              }\n            }\n          }\n        } catch (const ByteEventHelper::Exception& behEx) {\n          // rewrap the ByteEventHelper::Exception with extra information\n          AsyncSocketException ex(\n              AsyncSocketException::INTERNAL_ERROR,\n              withAddr(\n                  string(\n                      \"AsyncSocket::handleErrMessages(), \"\n                      \"internal exception during ByteEvent processing: \") +\n                  behEx.what()));\n          failByteEvents(ex);\n        } catch (const std::exception& ex) {\n          AsyncSocketException tex(\n              AsyncSocketException::UNKNOWN,\n              string(\n                  \"AsyncSocket::handleErrMessages(), \"\n                  \"unhandled exception during ByteEvent processing, \"\n                  \"threw exception: \") +\n                  ex.what());\n          failByteEvents(tex);\n        } catch (...) {\n          AsyncSocketException tex(\n              AsyncSocketException::UNKNOWN,\n              string(\n                  \"AsyncSocket::handleErrMessages(), \"\n                  \"unhandled exception during ByteEvent processing, \"\n                  \"threw non-exception type\"));\n          failByteEvents(tex);\n        }\n      }\n\n      // even if it is a timestamp, hand it off to the errMessageCallback,\n      // the application may want it as well.\n      if (errMessageCallback_) {\n        errMessageCallback_->errMessage(*cmsg);\n      }\n    }\n  }\n  return num;\n#else\n  return 0;\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n}\n\nbool AsyncSocket::processZeroCopyWriteInProgress() noexcept {\n  eventBase_->dcheckIsInEventBaseThread();\n  if (idZeroCopyBufPtrMap_.empty()) {\n    return true;\n  }\n\n  handleErrMessages();\n\n  return idZeroCopyBufPtrMap_.empty();\n}\n\nfolly::Expected<folly::TcpInfo, std::errc> AsyncSocket::getTcpInfo(\n    const TcpInfo::LookupOptions& options) {\n  if (NetworkSocket() == fd_) {\n    return folly::makeUnexpected(std::errc::invalid_argument);\n  }\n  return tcpInfoDispatcher_->initFromFd(fd_, options);\n}\n\nvoid AsyncSocket::addLifecycleObserver(\n    AsyncSocket::LegacyLifecycleObserver* observer) {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n\n  // adding the same observer multiple times is not allowed\n  auto& observers = lifecycleObservers_;\n  CHECK(\n      std::find(observers.begin(), observers.end(), observer) ==\n      observers.end());\n\n  observers.push_back(observer);\n  observer->observerAttach(this);\n  if (observer->getConfig().byteEvents) {\n    if (byteEventHelper_ && byteEventHelper_->maybeEx.has_value()) {\n      observer->byteEventsUnavailable(this, *byteEventHelper_->maybeEx);\n    } else if (byteEventHelper_ && byteEventHelper_->byteEventsEnabled) {\n      observer->byteEventsEnabled(this);\n    } else if (state_ == StateEnum::ESTABLISHED) {\n      enableByteEvents(); // try to enable now\n    }\n    // do nothing right now; wait until we're connected\n  }\n}\n\nbool AsyncSocket::removeLifecycleObserver(\n    AsyncSocket::LegacyLifecycleObserver* observer) {\n  auto& observers = lifecycleObservers_;\n  auto it = std::find(observers.begin(), observers.end(), observer);\n  if (it == observers.end()) {\n    return false;\n  }\n  observer->observerDetach(this);\n  observers.erase(it);\n  return true;\n}\n\nstd::vector<AsyncSocket::LegacyLifecycleObserver*>\nAsyncSocket::getLifecycleObservers() const {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n  return std::vector<AsyncSocket::LegacyLifecycleObserver*>(\n      lifecycleObservers_.begin(), lifecycleObservers_.end());\n}\n\nvoid AsyncSocket::splitIovecArray(\n    const size_t startOffset,\n    const size_t endOffset,\n    const iovec* srcVec,\n    const size_t srcCount,\n    iovec* dstVec,\n    size_t& dstCount) {\n  CHECK_GE(endOffset, startOffset);\n  CHECK_GE(dstCount, srcCount);\n  dstCount = 0;\n\n  const size_t targetBytes = endOffset - startOffset + 1;\n  size_t dstBytes = 0;\n  size_t processedBytes = 0;\n  for (size_t i = 0; i < srcCount; processedBytes += srcVec[i].iov_len, i++) {\n    iovec currentOp = srcVec[i];\n    if (currentOp.iov_len == 0) { // to handle the oddballs\n      continue;\n    }\n\n    // if we haven't found the start offset yet, see if it is in this op\n    if (dstCount == 0) {\n      if (processedBytes + currentOp.iov_len < startOffset + 1) {\n        continue; // start offset isn't in this op\n      }\n\n      // offset iov_base to get the start offset\n      const size_t trimFromStart = startOffset - processedBytes;\n      currentOp.iov_base =\n          reinterpret_cast<uint8_t*>(currentOp.iov_base) + trimFromStart;\n      currentOp.iov_len -= trimFromStart;\n    }\n\n    // trim the end of the iovec, if needed\n    ssize_t trimFromEnd = (dstBytes + currentOp.iov_len) - targetBytes;\n    if (trimFromEnd > 0) {\n      currentOp.iov_len -= trimFromEnd;\n    }\n\n    dstVec[dstCount] = currentOp;\n    dstCount++;\n    dstBytes += currentOp.iov_len;\n    CHECK_GE(targetBytes, dstBytes);\n    if (targetBytes == dstBytes) {\n      break; // done\n    }\n  }\n\n  CHECK_EQ(targetBytes, dstBytes);\n}\n\nAsyncSocket::ReadCode AsyncSocket::processZeroCopyRead() {\n#if defined(TCP_ZEROCOPY_RECEIVE)\n  if (zerocopyReadDisabled_) {\n    return ReadCode::READ_NOT_SUPPORTED;\n  }\n\n  auto* memStore = readCallback_->readZeroCopyEnabled();\n  if (!memStore) {\n    // set zerocopyReadDisabled_ to true to avoid further virtual calls\n    zerocopyReadDisabled_ = true;\n    return ReadCode::READ_NOT_SUPPORTED;\n  }\n\n  if (preReceivedData_ && !preReceivedData_->empty()) {\n    VLOG(5) << \"AsyncSocket::processZeroCopyRead() this=\" << this\n            << \", reading pre-received data\";\n\n    auto len = preReceivedData_->computeChainDataLength();\n    readCallback_->readZeroCopyDataAvailable(\n        std::move(preReceivedData_), 0 /*additionalBytes*/);\n    appBytesReceived_ += len;\n\n    return ReadCode::READ_DONE;\n  }\n\n  auto ptr = memStore->get();\n  if (!ptr) {\n    return ReadCode::READ_NOT_SUPPORTED;\n  }\n\n  void* copybuf = nullptr;\n  size_t copybuf_len = 0;\n  readCallback_->getZeroCopyFallbackBuffer(&copybuf, &copybuf_len);\n\n  folly::netops::tcp_zerocopy_receive zc = {};\n  socklen_t zc_len = sizeof(zc);\n\n  zc.address = reinterpret_cast<uint64_t>(ptr->data);\n  zc.length = ptr->capacity;\n  assert(uintptr_t(zc.address) % sysconf(_SC_PAGESIZE) == 0);\n  assert(zc.length % sysconf(_SC_PAGESIZE) == 0);\n  auto zc_length = zc.length;\n\n  zc.copybuf_address = reinterpret_cast<__u64>(copybuf);\n  zc.copybuf_len = copybuf_len;\n  auto zc_copybuf_len = zc.copybuf_len;\n\n  auto ret =\n      ::getsockopt(fd_.toFd(), IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, &zc, &zc_len);\n  if (!ret) {\n    // zc.err can be set even if there is still more data buffered in the\n    // kernel that we have not fully read yet.  When zc.err is set, just\n    // remember the error code, and keep reading until we get 0 data back from\n    // the kernel. Only once we have seen 0 bytes returned from the kernel do we\n    // want to return the error to the caller.\n    if (zc.err) {\n      zerocopyReadErr_ = -zc.err; // note: zc.err <= 0\n    }\n\n    assert(zc.length % sysconf(_SC_PAGESIZE) == 0);\n\n    auto len = zc.length + zc.copybuf_len;\n\n    if (zerocopyReadErr_ && len == 0) {\n      auto err = zerocopyReadErr_;\n      zerocopyReadErr_ = 0;\n\n      readErr_ = READ_ERROR;\n      AsyncSocketException ex(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(\"TCP_ZEROCOPY_RECEIVE failed\"),\n          err);\n      return failRead(__func__, ex);\n    }\n\n    std::unique_ptr<folly::IOBuf> buf;\n    if (zc.length) {\n      // adjust the len\n      ptr->len = zc.length;\n      auto tmp = getRXZeroCopyIOBuf(std::move(ptr));\n      buf = std::move(tmp);\n      // ZC buffers must be marked externally shared\n      // since they are \"shared\" with the kernel networking stack\n      // and must not be written to\n      // so that Fizz does not attempt to perform\n      // in place decryption and write to these buffers.\n      buf->markExternallyShared();\n    }\n\n    if (len) {\n      readCallback_->readZeroCopyDataAvailable(std::move(buf), zc.copybuf_len);\n      appBytesReceived_ += len;\n\n      // If we completely filled up the zerocopy buffer then we likely have\n      // more data buffered in the kernel, so return READ_CONTINUE to try again.\n      // We also want the caller to retry reading if we have a deferred error\n      // code to give them.\n      if ((zc.copybuf_len == 0 && zc.length == zc_length) ||\n          zc.copybuf_len == zc_copybuf_len || zerocopyReadErr_ != 0) {\n        return ReadCode::READ_CONTINUE;\n      }\n      return ReadCode::READ_DONE;\n    } else {\n      // No more data to read right now.\n      return ReadCode::READ_DONE;\n    }\n  } else {\n    if (errno == EIO) {\n      // EOF\n      readErr_ = READ_EOF;\n      // EOF\n      shutdownFlags_ |= SHUT_READ;\n      if (!updateEventRegistration(0, EventHandler::READ)) {\n        // we've already been moved into STATE_ERROR\n        assert(state_ == StateEnum::ERROR);\n        assert(readCallback_ == nullptr);\n        return ReadCode::READ_DONE;\n      }\n\n      ReadCallback* callback = readCallback_;\n      readCallback_ = nullptr;\n      callback->readEOF();\n      return ReadCode::READ_DONE;\n    }\n\n    // treat any other error as not supported, fall back to regular read\n    zerocopyReadDisabled_ = true;\n  }\n#endif\n  return ReadCode::READ_NOT_SUPPORTED;\n}\n\nAsyncSocket::ReadCode AsyncSocket::processNormalRead() {\n  auto readMode = readCallback_->getReadMode();\n  // Get the buffer(s) to read into.\n  void* buf = nullptr;\n  size_t buflen = 0;\n  IOBufIovecBuilder::IoVecVec iovs; // this can be an AsyncSocket member too\n\n  try {\n    if (readMode == AsyncReader::ReadCallback::ReadMode::ReadVec) {\n      prepareReadBuffers(iovs);\n      VLOG(5) << \"prepareReadBuffers() bufs=\" << iovs.data()\n              << \", num=\" << iovs.size();\n    } else {\n      prepareReadBuffer(&buf, &buflen);\n      VLOG(5) << \"prepareReadBuffer() buf=\" << buf << \", buflen=\" << buflen;\n    }\n  } catch (const AsyncSocketException& ex) {\n    return failRead(__func__, ex);\n  } catch (const std::exception& ex) {\n    AsyncSocketException tex(\n        AsyncSocketException::BAD_ARGS,\n        string(\n            \"ReadCallback::getReadBuffer() \"\n            \"threw exception: \") +\n            ex.what());\n    return failRead(__func__, tex);\n  } catch (...) {\n    AsyncSocketException ex(\n        AsyncSocketException::BAD_ARGS,\n        \"ReadCallback::getReadBuffer() threw \"\n        \"non-exception type\");\n    return failRead(__func__, ex);\n  }\n  if (iovs.empty() && (buf == nullptr || buflen == 0)) {\n    AsyncSocketException ex(\n        AsyncSocketException::BAD_ARGS,\n        \"ReadCallback::getReadBuffer() returned \"\n        \"empty buffer\");\n    return failRead(__func__, ex);\n  }\n\n  // Perform the read; we want `msg` for the `ancillaryData` callback.\n  //\n  // Zero-initialization is crucial here because not all code paths in\n  // `performReadMsg` go through `recvmsg` (e.g., \"pre-received data\" and\n  // \"recv\" are possibilities).  For those that do not, we want at a minimum\n  // `msg_controllen` and `msg_flags` to be zero.\n  struct ::msghdr msg{};\n  // Dest address info\n  msg.msg_name = nullptr;\n  msg.msg_namelen = 0;\n  struct ::iovec iov; // unused in `ReadMode::ReadVec`\n  if (readMode == AsyncReader::ReadCallback::ReadMode::ReadVec) {\n    msg.msg_iov = iovs.data();\n    msg.msg_iovlen = iovs.size();\n  } else {\n    iov.iov_base = buf;\n    iov.iov_len = buflen;\n    msg.msg_iov = &iov;\n    msg.msg_iovlen = 1;\n  }\n  auto readResult = performReadMsg(msg, readMode);\n\n  auto bytesRead = readResult.readReturn;\n  VLOG(4) << \"this=\" << this << \", AsyncSocket::handleRead() got \" << bytesRead\n          << \" bytes\";\n  if (bytesRead > 0) {\n    DCHECK(readCallback_);\n    if (readAncillaryDataCallback_) {\n      auto prevReadCallback = readCallback_;\n\n      auto ancillaryDataRes = readAncillaryDataCallback_->ancillaryData(msg);\n      if (ancillaryDataRes.hasError()) {\n        return failRead(__func__, ancillaryDataRes.error());\n      }\n\n      if (FOLLY_UNLIKELY(readCallback_ != prevReadCallback)) {\n        // The `ancillaryData` callback is allowed to close the socket,\n        // but otherwise is not allowed to change/replace the read callback.\n        CHECK_EQ((shutdownFlags_ & SHUT_READ), SHUT_READ);\n        CHECK(readCallback_ == nullptr);\n        // Return now since the socket has been closed, and discard\n        // the (real, non-ancillary) data that was read.\n        return ReadCode::READ_DONE;\n      }\n      // `ancillaryData()` is expected to check and error on this, since\n      // it's probably incorrect to process truncated ancillary data.  If\n      // some bizarre callback wants to treat this as recoverable, it can\n      // clear `MSG_CTRUNC` on `msg_flags` before returning.\n      //\n      // Don't move this: `performReadMsg` doesn't guarantee that `msg_flags`\n      // is valid without `readAncillaryDataCallback_`.  Also, the\n      // `readCallback_ != prevReadCallback` test means that we can safely\n      // call `failRead()` since a prior error would clear the read CB.\n      if (msg.msg_flags & MSG_CTRUNC) {\n        VLOG(5) << \"AsyncSocket::performReadInternal() this=\" << this\n                << \", ancillary data was truncated: \" << msg.msg_flags;\n        readErr_ = READ_ERROR;\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"recvmsg() got MSG_CTRUNC\"));\n        return failRead(__func__, ex);\n      }\n    }\n    readCallback_->readDataAvailable(size_t(bytesRead));\n\n    // Continue reading if we filled the available buffer\n    return (size_t(bytesRead) < buflen)\n        ? ReadCode::READ_DONE\n        : ReadCode::READ_CONTINUE;\n\n  } else if (bytesRead == READ_BLOCKING) {\n    // No more data to read right now.\n    return ReadCode::READ_DONE;\n  } else if (bytesRead == READ_ERROR) {\n    auto errnoCopy = errno;\n    recvErr(errnoCopy, std::move(readResult.exception));\n    return AsyncSocket::ReadCode::READ_DONE;\n  } else if (bytesRead == READ_ASYNC) {\n    return ReadCode::READ_DONE;\n  } else {\n    assert(bytesRead == READ_EOF);\n    recvEOF();\n    return ReadCode::READ_DONE;\n  }\n}\n\nvoid AsyncSocket::handleRead() noexcept {\n  VLOG(5) << \"AsyncSocket::handleRead() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_;\n  assert(state_ == StateEnum::ESTABLISHED);\n  assert((shutdownFlags_ & SHUT_READ) == 0);\n  assert(readCallback_ != nullptr);\n  assert(eventFlags_ & EventHandler::READ);\n\n  // Loop until:\n  // - a read attempt would block\n  // - readCallback_ is uninstalled\n  // - the number of loop iterations exceeds the optional maximum\n  // - this AsyncSocket is moved to another EventBase\n  //\n  // When we invoke readDataAvailable() it may uninstall the readCallback_,\n  // which is why need to check for it here.\n  //\n  // The last bullet point is slightly subtle.  readDataAvailable() may also\n  // detach this socket from this EventBase.  However, before\n  // readDataAvailable() returns another thread may pick it up, attach it to\n  // a different EventBase, and install another readCallback_.  We need to\n  // exit immediately after readDataAvailable() returns if the eventBase_ has\n  // changed.  (The caller must perform some sort of locking to transfer the\n  // AsyncSocket between threads properly.  This will be sufficient to ensure\n  // that this thread sees the updated eventBase_ variable after\n  // readDataAvailable() returns.)\n  size_t numReads = maxReadsPerEvent_ ? maxReadsPerEvent_ : size_t(-1);\n  EventBase* originalEventBase = eventBase_;\n  while (readCallback_ && eventBase_ == originalEventBase && numReads--) {\n    auto ret = processZeroCopyRead();\n    if (ret == ReadCode::READ_NOT_SUPPORTED) {\n      ret = processNormalRead();\n    }\n\n    switch (ret) {\n      case ReadCode::READ_NOT_SUPPORTED:\n        CHECK(false);\n      case ReadCode::READ_CONTINUE:\n        break;\n      case ReadCode::READ_DONE:\n        return;\n    }\n  }\n\n  if (readCallback_ && eventBase_ == originalEventBase) {\n    // We might still have data in the socket.\n    // (e.g. see comment in AsyncSSLSocket::checkForImmediateRead)\n    scheduleImmediateRead();\n  }\n}\n\n/**\n * This function attempts to write as much data as possible, until no more\n * data can be written.\n *\n * - If it sends all available data, it unregisters for write events, and\n * stops the writeTimeout_.\n *\n * - If not all of the data can be sent immediately, it reschedules\n *   writeTimeout_ (if a non-zero timeout is set), and ensures the handler is\n *   registered for write events.\n */\nvoid AsyncSocket::handleWrite() noexcept {\n  VLOG(5) << \"AsyncSocket::handleWrite() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_;\n  DestructorGuard dg(this);\n\n  if (state_ == StateEnum::CONNECTING) {\n    handleConnect();\n    return;\n  }\n\n  // Normal write\n  assert(state_ == StateEnum::ESTABLISHED);\n  assert((shutdownFlags_ & SHUT_WRITE) == 0);\n  assert(writeReqHead_ != nullptr);\n\n  // Loop until we run out of write requests,\n  // or until this socket is moved to another EventBase.\n  // (See the comment in handleRead() explaining how this can happen.)\n  EventBase* originalEventBase = eventBase_;\n  while (writeReqHead_ != nullptr && eventBase_ == originalEventBase) {\n    writeReqHead_->getCallbackWithState().notifyOnWrite();\n\n    auto writeResult = writeReqHead_->performWrite();\n    if (writeResult.writeReturn < 0) {\n      if (writeResult.exception) {\n        return failWrite(__func__, *writeResult.exception);\n      }\n      auto errnoCopy = errno;\n      AsyncSocketException ex(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(\"writev() failed\"),\n          errnoCopy);\n      return failWrite(__func__, ex);\n    } else if (writeReqHead_->isComplete()) {\n      // We finished this request\n      WriteRequest* req = writeReqHead_;\n      writeReqHead_ = req->getNext();\n\n      if (writeReqHead_ == nullptr) {\n        writeReqTail_ = nullptr;\n        sendDone();\n      }\n\n      // Invoke the callback\n      WriteCallback* callback = req->getCallback();\n      req->destroy();\n      if (callback) {\n        callback->writeSuccess();\n      }\n      // We'll continue around the loop, trying to write another request\n    } else {\n      // Partial write.\n      writeReqHead_->consume();\n      sendPartial();\n      return;\n    }\n  }\n}\n\nvoid AsyncSocket::checkForImmediateRead() noexcept {\n  // We currently don't attempt to perform optimistic reads in AsyncSocket.\n  // (However, note that some subclasses do override this method.)\n  //\n  // Simply calling handleRead() here would be bad, as this would call\n  // readCallback_->getReadBuffer(), forcing the callback to allocate a read\n  // buffer even though no data may be available.  This would waste lots of\n  // memory, since the buffer will sit around unused until the socket actually\n  // becomes readable.\n  //\n  // Checking if the socket is readable now also seems like it would probably\n  // be a pessimism.  In most cases it probably wouldn't be readable, and we\n  // would just waste an extra system call.  Even if it is readable, waiting\n  // to find out from libevent on the next event loop doesn't seem that bad.\n  //\n  // The exception to this is if we have pre-received data. In that case there\n  // is definitely data available immediately.\n  EventBase* originalEventBase = eventBase_;\n  if (preReceivedData_ && !preReceivedData_->empty()) {\n    // Even with io_uring requiring ReadCallbacks to take IOBufs via\n    // readBufferAvailable, if the ReadCallback is a small (peeking) read, go\n    // through the normal handleRead() path with\n    // getReadBuffer()/readBufferAvailable().\n    if (iouRecvHandle_ &&\n        readCallback_->maxBufferSize() >= IoUringRecvHandle::kSmallRecvSize) {\n      readCallback_->readBufferAvailable(std::move(preReceivedData_));\n    } else {\n      handleRead();\n    }\n  }\n\n  if (iouRecvHandle_ && readCallback_ && eventBase_ == originalEventBase &&\n      iouRecvHandle_->hasQueuedData()) {\n    readCallback_->readBufferAvailable(iouRecvHandle_->getQueuedData());\n  }\n\n  if (iouRecvHandle_ && readCallback_ && eventBase_ == originalEventBase) {\n    iouRecvHandle_->submit(readCallback_->maxBufferSize());\n  }\n}\n\nvoid AsyncSocket::handleInitialReadWrite() noexcept {\n  // Our callers should already be holding a DestructorGuard, but grab\n  // one here just to make sure, in case one of our calling code paths ever\n  // changes.\n  DestructorGuard dg(this);\n  // If we have a readCallback_, make sure we enable read events.  We\n  // may already be registered for reads if connectSuccess() set\n  // the read callback.\n  if (readCallback_ && !(eventFlags_ & EventHandler::READ)) {\n    assert(state_ == StateEnum::ESTABLISHED);\n    assert((shutdownFlags_ & SHUT_READ) == 0);\n    if (useIoUring_ && !iouRecvHandle_) {\n      iouRecvHandle_ = IoUringRecvHandle::create(eventBase_, fd_, addr_, this);\n      CHECK(iouRecvHandle_);\n    }\n    if (!updateEventRegistration(EventHandler::READ, 0)) {\n      assert(state_ == StateEnum::ERROR);\n      return;\n    }\n    checkForImmediateRead();\n  } else if (readCallback_ == nullptr) {\n    // Unregister for read events.\n    updateEventRegistration(0, EventHandler::READ);\n  }\n\n  // If we have write requests pending, try to send them immediately.\n  // Since we just finished accepting, there is a very good chance that we can\n  // write without blocking.\n  //\n  // However, we only process them if EventHandler::WRITE is not already set,\n  // which means that we're already blocked on a write attempt.  (This can\n  // happen if connectSuccess() called write() before returning.)\n  if (writeReqHead_ && !(eventFlags_ & EventHandler::WRITE)) {\n    // Call handleWrite() to perform write processing.\n    handleWrite();\n  } else if (iouSendHandle_ && !iouSendHandle_->empty()) {\n    updateEventRegistration(EventHandler::WRITE, 0);\n  } else if (writeReqHead_ == nullptr) {\n    // Unregister for write event.\n    updateEventRegistration(0, EventHandler::WRITE);\n  }\n}\n\nbool AsyncSocket::hasPendingWrites() noexcept {\n  if (useIoUring_) {\n    return iouSendHandle_ && !iouSendHandle_->empty();\n  }\n\n  return writeReqHead_ != nullptr;\n}\n\nvoid AsyncSocket::handleConnect() noexcept {\n  VLOG(5) << \"AsyncSocket::handleConnect() this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_;\n  assert(state_ == StateEnum::CONNECTING);\n  // SHUT_WRITE can never be set while we are still connecting;\n  // SHUT_WRITE_PENDING may be set, be we only set SHUT_WRITE once the connect\n  // finishes\n  assert((shutdownFlags_ & SHUT_WRITE) == 0);\n\n  if (!useIoUring_) {\n    // In case we had a connect timeout, cancel the timeout\n    writeTimeout_.cancelTimeout();\n    // We don't use a persistent registration when waiting on a connect event,\n    // so we have been automatically unregistered now.  Update eventFlags_ to\n    // reflect reality.\n    assert(eventFlags_ == EventHandler::WRITE);\n    eventFlags_ = EventHandler::NONE;\n  }\n\n  // Call getsockopt() to check if the connect succeeded\n  int error;\n  socklen_t len = sizeof(error);\n  int rv = netops_->getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &len);\n  if (rv != 0) {\n    auto errnoCopy = errno;\n    AsyncSocketException ex(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(\"error calling getsockopt() after connect\"),\n        errnoCopy);\n    VLOG(4) << \"AsyncSocket::handleConnect(this=\" << this << \", fd=\" << fd_\n            << \" host=\" << addr_.describe() << \") exception:\" << ex.what();\n    return failConnect(__func__, ex);\n  }\n\n  if (error != 0) {\n    AsyncSocketException ex(\n        AsyncSocketException::NOT_OPEN, \"connect failed\", error);\n    VLOG(2) << \"AsyncSocket::handleConnect(this=\" << this << \", fd=\" << fd_\n            << \" host=\" << addr_.describe() << \") exception: \" << ex.what();\n    return failConnect(__func__, ex);\n  }\n\n  // Move into STATE_ESTABLISHED\n  state_ = StateEnum::ESTABLISHED;\n  if (useIoUring_ && netops_->set_socket_blocking(fd_)) {\n    auto errnoCopy = errno;\n    AsyncSocketException tex(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(\"failed to restore socket in blocking mode\"),\n        errnoCopy);\n    return failConnect(__func__, tex);\n  }\n\n  // If SHUT_WRITE_PENDING is set and we don't have any write requests to\n  // perform, immediately shutdown the write half of the socket.\n  if ((shutdownFlags_ & SHUT_WRITE_PENDING) && !hasPendingWrites()) {\n    // SHUT_READ shouldn't be set.  If close() is called on the socket while\n    // we are still connecting we just abort the connect rather than waiting\n    // for it to complete.\n    assert((shutdownFlags_ & SHUT_READ) == 0);\n    netops_->shutdown(fd_, SHUT_WR);\n    shutdownFlags_ |= SHUT_WRITE;\n  }\n\n  VLOG(7) << \"AsyncSocket \" << this << \": fd \" << fd_\n          << \"successfully connected; state=\" << state_;\n\n  // Remember the EventBase we are attached to, before we start invoking any\n  // callbacks (since the callbacks may call detachEventBase()).\n  EventBase* originalEventBase = eventBase_;\n\n  invokeConnectSuccess();\n  // Note that the connect callback may have changed our state.\n  // (set or unset the read callback, called write(), closed the socket, etc.)\n  // The following code needs to handle these situations correctly.\n  //\n  // If the socket has been closed, readCallback_ and writeReqHead_ will\n  // always be nullptr, so that will prevent us from trying to read or write.\n  //\n  // The main thing to check for is if eventBase_ is still originalEventBase.\n  // If not, we have been detached from this event base, so we shouldn't\n  // perform any more operations.\n  if (eventBase_ != originalEventBase) {\n    return;\n  }\n\n  handleInitialReadWrite();\n}\n\nvoid AsyncSocket::timeoutExpired() noexcept {\n  VLOG(7) << \"AsyncSocket \" << this << \", fd \" << fd_ << \": timeout expired: \"\n          << \"state=\" << state_ << \", events=\" << std::hex << eventFlags_;\n  DestructorGuard dg(this);\n  eventBase_->dcheckIsInEventBaseThread();\n\n  if (state_ == StateEnum::CONNECTING) {\n    // connect() timed out\n    // Unregister for I/O events.\n    if (connectCallback_) {\n      AsyncSocketException ex(\n          AsyncSocketException::TIMED_OUT,\n          fmt::format(\"connect timed out after {}ms\", connectTimeout_.count()));\n      failConnect(__func__, ex);\n    } else {\n      // we faced a connect error without a connect callback, which could\n      // happen due to TFO.\n      AsyncSocketException ex(\n          AsyncSocketException::TIMED_OUT, \"write timed out during connection\");\n      failWrite(__func__, ex);\n    }\n  } else {\n    // a normal write operation timed out\n    AsyncSocketException ex(\n        AsyncSocketException::TIMED_OUT,\n        fmt::format(\"write timed out after {}ms\", sendTimeout_));\n    failWrite(__func__, ex);\n  }\n}\n\nvoid AsyncSocket::handleNetworkSocketAttached() {\n  VLOG(6) << \"AsyncSocket::attachFd(this=\" << this << \", fd=\" << fd_\n          << \", evb=\" << eventBase_ << \" , state=\" << state_\n          << \", events=\" << std::hex << eventFlags_ << \")\";\n\n  // legacy observer support\n  for (const auto& cb : lifecycleObservers_) {\n    cb->fdAttach(this);\n  }\n\n  // folly::ObserverContainer observer support\n  if (auto list = getAsyncSocketObserverContainer()) {\n    list->invokeInterfaceMethodAllObservers([](auto observer, auto observed) {\n      observer->fdAttach(observed);\n    });\n  }\n\n  if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) {\n    shutdownSocketSet->add(fd_);\n  }\n  ioHandler_.changeHandlerFD(fd_);\n}\n\nssize_t AsyncSocket::tfoSendMsg(\n    NetworkSocket fd, struct msghdr* msg, int msg_flags) {\n  return detail::tfo_sendmsg(fd, msg, msg_flags);\n}\n\nAsyncSocket::WriteResult AsyncSocket::sendSocketMessage(\n    const iovec* vec,\n    size_t count,\n    WriteFlags flags,\n    WriteRequestTag writeTag) {\n  // lambda to gather and merge PrewriteRequests from observers\n  auto gatherAndMergePrewriteRequests =\n      [this,\n       vec,\n       count,\n       flags,\n       maybeVecTotalBytes = folly::Optional<size_t>()]() mutable {\n        AsyncSocketObserverInterface::PrewriteRequest mergedRequest = {};\n        if (lifecycleObservers_.empty()) {\n          return mergedRequest;\n        }\n\n        // determine total number of bytes in vec, reuse once determined\n        if (!maybeVecTotalBytes.has_value()) {\n          maybeVecTotalBytes = 0;\n          for (size_t i = 0; i < count; ++i) {\n            maybeVecTotalBytes.value() += vec[i].iov_len;\n          }\n        }\n        auto& vecTotalBytes = maybeVecTotalBytes.value();\n\n        // build our PrewriteState\n        const auto startOffset = getRawBytesWritten();\n        const auto endOffset = getRawBytesWritten() + vecTotalBytes - 1;\n        const AsyncSocketObserverInterface::PrewriteState prewriteState = [&] {\n          AsyncSocketObserverInterface::PrewriteState state = {};\n          state.startOffset = startOffset;\n          state.endOffset = endOffset;\n          state.writeFlags = flags;\n          state.ts = std::chrono::steady_clock::now();\n          return state;\n        }();\n\n        // enable observers to add PrewriteRequests to container\n        AsyncSocketObserverInterface::PrewriteRequestContainer\n            prewriteRequestContainer(prewriteState);\n        for (const auto& observer : lifecycleObservers_) {\n          if (!observer->getConfig().prewrite) {\n            continue;\n          }\n          observer->prewrite(this, prewriteState, prewriteRequestContainer);\n        }\n\n        return prewriteRequestContainer.getMergedRequest();\n      };\n\n  // lambda to prepare and send a message, and handle byte events\n  // parameters have L at the end to prevent shadowing warning from gcc\n  auto prepSendMsg =\n      [this, writeTag = std::move(writeTag)](\n          const iovec* vecL, const size_t countL, const WriteFlags flagsL) {\n        const bool byteEventsEnabled =\n            (byteEventHelper_ && byteEventHelper_->byteEventsEnabled &&\n             !byteEventHelper_->maybeEx.has_value());\n\n        struct msghdr msg = {};\n        msg.msg_name = nullptr;\n        msg.msg_namelen = 0;\n        msg.msg_iov = const_cast<struct iovec*>(vecL);\n        msg.msg_iovlen = std::min<size_t>(countL, kIovMax);\n        msg.msg_flags = 0; // passed to sendSocketMessage below, it sets them\n        msg.msg_control = nullptr;\n        msg.msg_controllen = sendMsgParamCallback_->getAncillaryDataSize(\n            flagsL, writeTag, byteEventsEnabled);\n        CHECK_GE(\n            AsyncSocket::SendMsgParamsCallback::maxAncillaryDataSize,\n            msg.msg_controllen);\n\n        if (msg.msg_controllen != 0) {\n          msg.msg_control = reinterpret_cast<char*>(alloca(msg.msg_controllen));\n          sendMsgParamCallback_->getAncillaryData(\n              flagsL, msg.msg_control, writeTag, byteEventsEnabled);\n        }\n\n        const auto prewriteRawBytesWritten = getRawBytesWritten();\n        int msg_flags =\n            sendMsgParamCallback_->getFlags(flagsL, zeroCopyEnabled_);\n        auto writeResult = sendSocketMessage(fd_, &msg, msg_flags);\n\n        if (writeResult.writeReturn < 0 && zeroCopyEnabled_ &&\n            errno == ENOBUFS) {\n          // workaround for running with zerocopy enabled but without a big\n          // enough memlock value - see ulimit -l\n          zeroCopyEnabled_ = false;\n          zeroCopyReenableCounter_ = zeroCopyReenableThreshold_;\n          msg_flags = sendMsgParamCallback_->getFlags(flagsL, zeroCopyEnabled_);\n          writeResult = sendSocketMessage(fd_, &msg, msg_flags);\n        }\n\n        if (writeResult.writeReturn > 0) {\n          if (msg.msg_controllen != 0) {\n            sendMsgParamCallback_->wroteBytes(writeTag);\n          }\n          if (byteEventsEnabled && isSet(flagsL, WriteFlags::TIMESTAMP_WRITE)) {\n            CHECK_GT(\n                getRawBytesWritten(), prewriteRawBytesWritten); // sanity check\n            ByteEvent byteEvent = {};\n            byteEvent.type = ByteEvent::Type::WRITE;\n            byteEvent.offset = getRawBytesWritten() - 1;\n            byteEvent.maybeRawBytesWritten = writeResult.writeReturn;\n            byteEvent.maybeRawBytesTriedToWrite = 0;\n            for (size_t i = 0; i < countL; ++i) {\n              byteEvent.maybeRawBytesTriedToWrite.value() += vecL[i].iov_len;\n            }\n            byteEvent.maybeWriteFlags = flagsL;\n            for (const auto& observer : lifecycleObservers_) {\n              if (observer->getConfig().byteEvents) {\n                observer->byteEvent(this, byteEvent);\n              }\n            }\n          }\n        }\n\n        return writeResult;\n      };\n\n  // get PrewriteRequests (if any), merge flags with write flags\n  const auto prewriteRequest = gatherAndMergePrewriteRequests();\n  auto mergedFlags = flags | prewriteRequest.writeFlagsToAdd |\n      prewriteRequest.writeFlagsToAddAtOffset;\n\n  // if no PrewriteRequests, or none requiring the write to be split, proceed\n  if (!prewriteRequest.maybeOffsetToSplitWrite.has_value()) {\n    return prepSendMsg(vec, count, mergedFlags);\n  }\n\n  // we need to split the write...\n  // add CORK flag to inform the OS that more data is on the way...\n  mergedFlags |= WriteFlags::CORK;\n\n  // TODO(bschlinker): When prewrite splits a write, try to continue writing\n  // after a write returns; this will improve efficiency.\n  const auto splitWriteAtOffset = *prewriteRequest.maybeOffsetToSplitWrite;\n  if (count <= kSmallIoVecSize) {\n    // suppress \"warning: variable length array 'vec' is used [-Wvla]\"\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Wvla\")\n    iovec tmpVec[BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallIoVecSize)];\n    FOLLY_POP_WARNING\n\n    size_t tmpVecCount = count;\n    splitIovecArray(\n        0,\n        splitWriteAtOffset - getRawBytesWritten(),\n        vec,\n        count,\n        tmpVec,\n        tmpVecCount);\n    return prepSendMsg(tmpVec, tmpVecCount, mergedFlags);\n  } else {\n    auto tmpVecPtr = std::make_unique<iovec[]>(count);\n    auto tmpVec = tmpVecPtr.get();\n    size_t tmpVecCount = count;\n    splitIovecArray(\n        0,\n        splitWriteAtOffset - getRawBytesWritten(),\n        vec,\n        count,\n        tmpVec,\n        tmpVecCount);\n    return prepSendMsg(tmpVec, tmpVecCount, mergedFlags);\n  }\n}\n\nAsyncSocket::WriteResult AsyncSocket::sendSocketMessage(\n    NetworkSocket fd, struct msghdr* msg, int msg_flags) {\n  ssize_t totalWritten = 0;\n  SCOPE_EXIT {\n    if (totalWritten > 0) {\n      rawBytesWritten_ += totalWritten;\n    }\n  };\n  if (state_ == StateEnum::FAST_OPEN) {\n    sockaddr_storage addr;\n    auto len = addr_.getAddress(&addr);\n    msg->msg_name = &addr;\n    msg->msg_namelen = len;\n    totalWritten = tfoSendMsg(fd_, msg, msg_flags);\n    if (totalWritten >= 0) {\n      tfoInfo_.finished = true;\n      state_ = StateEnum::ESTABLISHED;\n      if (useIoUring_ && netops_->set_socket_blocking(fd_)) {\n        auto errnoCopy = errno;\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR,\n            withAddr(\"failed to restore socket in blocking mode\"),\n            errnoCopy);\n        return WriteResult(\n            WRITE_ERROR, std::make_unique<AsyncSocketException>(ex));\n      }\n      // We schedule this asynchronously so that we don't end up\n      // invoking initial read or write while a write is in progress.\n      scheduleInitialReadWrite();\n    } else if (errno == EINPROGRESS) {\n      VLOG(4) << \"TFO falling back to connecting\";\n      // A normal sendmsg doesn't return EINPROGRESS, however\n      // TFO might fallback to connecting if there is no\n      // cookie.\n      state_ = StateEnum::CONNECTING;\n      try {\n        if (!useIoUring_) {\n          scheduleConnectTimeout();\n          registerForConnectEvents();\n        } else {\n          iouConnectHandle_ = IoUringConnectHandle::create(\n              eventBase_, fd_, this, connectTimeout_);\n        }\n      } catch (const AsyncSocketException& ex) {\n        return WriteResult(\n            WRITE_ERROR, std::make_unique<AsyncSocketException>(ex));\n      }\n      // Let's fake it that no bytes were written and return an errno.\n      errno = EAGAIN;\n      totalWritten = -1;\n    } else if (errno == EOPNOTSUPP) {\n      // Try falling back to connecting.\n      VLOG(4) << \"TFO not supported\";\n      state_ = StateEnum::CONNECTING;\n      try {\n        int ret = socketConnect((const sockaddr*)&addr, len);\n        if (ret == 0) {\n          // connect succeeded immediately\n          // Treat this like no data was written.\n          state_ = StateEnum::ESTABLISHED;\n          if (useIoUring_ && netops_->set_socket_blocking(fd_)) {\n            auto errnoCopy = errno;\n            AsyncSocketException ex(\n                AsyncSocketException::INTERNAL_ERROR,\n                withAddr(\"failed to restore socket in blocking mode\"),\n                errnoCopy);\n            return WriteResult(\n                WRITE_ERROR, std::make_unique<AsyncSocketException>(ex));\n          }\n          scheduleInitialReadWrite();\n        }\n        // If there was no exception during connections,\n        // we would return that no bytes were written.\n        errno = EAGAIN;\n        totalWritten = -1;\n      } catch (const AsyncSocketException& ex) {\n        return WriteResult(\n            WRITE_ERROR, std::make_unique<AsyncSocketException>(ex));\n      }\n    } else if (errno == EAGAIN) {\n      // Normally sendmsg would indicate that the write would block.\n      // However in the fast open case, it would indicate that sendmsg\n      // fell back to a connect. This is a return code from connect()\n      // instead, and is an error condition indicating no fds available.\n      return WriteResult(\n          WRITE_ERROR,\n          std::make_unique<AsyncSocketException>(\n              AsyncSocketException::UNKNOWN, \"No more free local ports\"));\n    }\n  } else {\n    totalWritten = netops_->sendmsg(fd, msg, msg_flags);\n  }\n  return WriteResult(totalWritten);\n}\n\nAsyncSocket::WriteResult AsyncSocket::performWrite(\n    const iovec* vec,\n    uint32_t count,\n    WriteFlags flags,\n    uint32_t* countWritten,\n    uint32_t* partialWritten,\n    WriteRequestTag writeTag) {\n  auto writeResult = sendSocketMessage(vec, count, flags, std::move(writeTag));\n  auto totalWritten = writeResult.writeReturn;\n  if (totalWritten < 0) {\n    bool tryAgain = (errno == EAGAIN);\n#ifdef __APPLE__\n    // Apple has a bug where doing a second write on a socket which we\n    // have opened with TFO causes an ENOTCONN to be thrown. However the\n    // socket is really connected, so treat ENOTCONN as a EAGAIN until\n    // this bug is fixed.\n    tryAgain |= (errno == ENOTCONN);\n#endif\n\n    if (!writeResult.exception && tryAgain) {\n      // TCP buffer is full; we can't write any more data right now.\n      *countWritten = 0;\n      *partialWritten = 0;\n      return WriteResult(0);\n    }\n    // error\n    *countWritten = 0;\n    *partialWritten = 0;\n    return writeResult;\n  }\n\n  appBytesWritten_ += totalWritten;\n\n  uint32_t bytesWritten;\n  uint32_t n;\n  for (bytesWritten = uint32_t(totalWritten), n = 0; n < count; ++n) {\n    const iovec* v = vec + n;\n    if (v->iov_len > bytesWritten) {\n      // Partial write finished in the middle of this iovec\n      *countWritten = n;\n      *partialWritten = bytesWritten;\n      return WriteResult(totalWritten);\n    }\n\n    bytesWritten -= uint32_t(v->iov_len);\n  }\n\n  assert(bytesWritten == 0);\n  *countWritten = n;\n  *partialWritten = 0;\n  return WriteResult(totalWritten);\n}\n\n/**\n * Re-register the EventHandler after eventFlags_ has changed.\n *\n * If an error occurs, fail() is called to move the socket into the error\n * state and call all currently installed callbacks.  After an error, the\n * AsyncSocket is completely unregistered.\n *\n * @return Returns true on success, or false on error.\n */\nbool AsyncSocket::updateEventRegistration() {\n  VLOG(5) << \"AsyncSocket::updateEventRegistration(this=\" << this\n          << \", fd=\" << fd_ << \", evb=\" << eventBase_ << \", state=\" << state_\n          << \", events=\" << std::hex << eventFlags_;\n  if (useIoUring_) {\n    if (iouSendHandle_) {\n      iouSendHandle_->update(eventFlags_);\n    }\n    if (iouRecvHandle_) {\n      iouRecvHandle_->update(eventFlags_);\n    }\n    return true;\n  }\n\n  if (eventFlags_ == EventHandler::NONE) {\n    if (ioHandler_.isHandlerRegistered()) {\n      DCHECK(eventBase_ != nullptr);\n      eventBase_->dcheckIsInEventBaseThread();\n    }\n    ioHandler_.unregisterHandler();\n    return true;\n  }\n\n  eventBase_->dcheckIsInEventBaseThread();\n\n  // Always register for persistent events, so we don't have to re-register\n  // after being called back.\n  if (!ioHandler_.registerHandler(\n          uint16_t(eventFlags_ | EventHandler::PERSIST))) {\n    eventFlags_ = EventHandler::NONE; // we're not registered after error\n    AsyncSocketException ex(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(\"failed to update AsyncSocket event registration\"));\n    fail(\"updateEventRegistration\", ex);\n    return false;\n  }\n\n  return true;\n}\n\nbool AsyncSocket::updateEventRegistration(uint16_t enable, uint16_t disable) {\n  uint16_t oldFlags = eventFlags_;\n  eventFlags_ |= enable;\n  eventFlags_ &= ~disable;\n  if (eventFlags_ == oldFlags) {\n    return true;\n  } else {\n    return updateEventRegistration();\n  }\n}\n\nvoid AsyncSocket::startFail() {\n  // startFail() should only be called once\n  assert(state_ != StateEnum::ERROR);\n  assert(getDestructorGuardCount() > 0);\n  state_ = StateEnum::ERROR;\n  // Ensure that SHUT_READ and SHUT_WRITE are set,\n  // so all future attempts to read or write will be rejected\n  shutdownFlags_ |= (SHUT_READ | SHUT_WRITE);\n\n  // Cancel any scheduled immediate read.\n  if (immediateReadHandler_.isLoopCallbackScheduled()) {\n    immediateReadHandler_.cancelLoopCallback();\n  }\n\n  if (eventFlags_ != EventHandler::NONE) {\n    eventFlags_ = EventHandler::NONE;\n    if (iouSendHandle_) {\n      iouSendHandle_->update(eventFlags_);\n    }\n    if (iouRecvHandle_) {\n      iouRecvHandle_->update(eventFlags_);\n    }\n    ioHandler_.unregisterHandler();\n  }\n  writeTimeout_.cancelTimeout();\n\n  if (fd_ != NetworkSocket()) {\n    ioHandler_.changeHandlerFD(NetworkSocket());\n    doClose();\n  }\n}\n\nvoid AsyncSocket::invokeAllErrors(const AsyncSocketException& ex) {\n  invokeConnectErr(ex);\n  failAllWrites(ex);\n\n  if (readCallback_) {\n    ReadCallback* callback = readCallback_;\n    readCallback_ = nullptr;\n    callback->readErr(ex);\n  }\n}\n\nvoid AsyncSocket::finishFail() {\n  assert(state_ == StateEnum::ERROR);\n  assert(getDestructorGuardCount() > 0);\n\n  AsyncSocketException ex(\n      AsyncSocketException::INTERNAL_ERROR,\n      withAddr(\"socket closing after error\"));\n  invokeAllErrors(ex);\n}\n\nvoid AsyncSocket::finishFail(const AsyncSocketException& ex) {\n  assert(state_ == StateEnum::ERROR);\n  assert(getDestructorGuardCount() > 0);\n  invokeAllErrors(ex);\n}\n\nvoid AsyncSocket::fail(const char* fn, const AsyncSocketException& ex) {\n  VLOG(4) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \" host=\" << addr_.describe()\n          << \"): failed in \" << fn << \"(): \" << ex.what();\n  startFail();\n  finishFail(ex);\n}\n\nvoid AsyncSocket::failConnect(const char* fn, const AsyncSocketException& ex) {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \" host=\" << addr_.describe()\n          << \"): failed while connecting in \" << fn << \"(): \" << ex.what();\n  startFail();\n\n  invokeConnectErr(ex);\n  finishFail(ex);\n}\n\nAsyncSocket::ReadCode AsyncSocket::failRead(\n    const char* fn, const AsyncSocketException& ex) {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \" host=\" << addr_.describe()\n          << \"): failed while reading in \" << fn << \"(): \" << ex.what();\n  startFail();\n\n  if (readCallback_ != nullptr) {\n    ReadCallback* callback = readCallback_;\n    readCallback_ = nullptr;\n    callback->readErr(ex);\n  }\n\n  finishFail(ex);\n\n  // done handling the error, we can exit the loop\n  return AsyncSocket::ReadCode::READ_DONE;\n}\n\nvoid AsyncSocket::failErrMessageRead(\n    const char* fn, const AsyncSocketException& ex) {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \" host=\" << addr_.describe()\n          << \"): failed while reading message in \" << fn << \"(): \" << ex.what();\n  startFail();\n\n  if (errMessageCallback_ != nullptr) {\n    ErrMessageCallback* callback = errMessageCallback_;\n    errMessageCallback_ = nullptr;\n    callback->errMessageError(ex);\n  }\n\n  finishFail(ex);\n}\n\nvoid AsyncSocket::failWrite(const char* fn, const AsyncSocketException& ex) {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \" host=\" << addr_.describe()\n          << \"): failed while writing in \" << fn << \"(): \" << ex.what();\n  startFail();\n\n  if (iouSendHandle_) {\n    assert(writeReqHead_ == nullptr);\n    iouSendHandle_->failWrite(ex);\n  }\n\n  // Only invoke the first write callback, since the error occurred while\n  // writing this request.  Let any other pending write callbacks be invoked\n  // in finishFail().\n  if (writeReqHead_ != nullptr) {\n    WriteRequest* req = writeReqHead_;\n    writeReqHead_ = req->getNext();\n    WriteCallback* callback = req->getCallback();\n    uint32_t bytesWritten = req->getTotalBytesWritten();\n    req->destroy();\n    if (callback) {\n      callback->writeErr(bytesWritten, ex);\n    }\n  }\n\n  finishFail(ex);\n}\n\nvoid AsyncSocket::failWrite(\n    const char* fn,\n    WriteCallback* callback,\n    size_t bytesWritten,\n    const AsyncSocketException& ex) {\n  // This version of failWrite() is used when the failure occurs before\n  // we've added the callback to writeReqHead_.\n  VLOG(4) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \", state=\" << state_ << \" host=\" << addr_.describe()\n          << \"): failed while writing in \" << fn << \"(): \" << ex.what();\n  if (closeOnFailedWrite_) {\n    startFail();\n  }\n\n  if (callback != nullptr) {\n    callback->writeErr(bytesWritten, ex);\n  }\n\n  if (closeOnFailedWrite_) {\n    finishFail(ex);\n  }\n}\n\nvoid AsyncSocket::failAllWrites(const AsyncSocketException& ex) {\n  if (iouSendHandle_) {\n    assert(writeReqHead_ == nullptr);\n    assert(iouSendHandle_ != nullptr);\n    iouSendHandle_->failAllWrites(ex);\n  }\n  // Invoke writeError() on all write callbacks.\n  // This is used when writes are forcibly shutdown with write requests\n  // pending, or when an error occurs with writes pending.\n  while (writeReqHead_ != nullptr) {\n    WriteRequest* req = writeReqHead_;\n    writeReqHead_ = req->getNext();\n    WriteCallback* callback = req->getCallback();\n    if (callback) {\n      callback->writeErr(req->getTotalBytesWritten(), ex);\n    }\n    req->destroy();\n  }\n\n  // All pending writes have failed - reset totalAppBytesScheduledForWrite_\n  totalAppBytesScheduledForWrite_ = appBytesWritten_;\n}\n\nvoid AsyncSocket::failByteEvents(const AsyncSocketException& ex) {\n  CHECK(byteEventHelper_) << \"failByteEvents called without ByteEventHelper\";\n  byteEventHelper_->maybeEx = ex;\n  // inform any observers that want ByteEvents\n  for (const auto& observer : lifecycleObservers_) {\n    if (observer->getConfig().byteEvents) {\n      observer->byteEventsUnavailable(this, ex);\n    }\n  }\n}\n\nvoid AsyncSocket::invalidState(ConnectCallback* callback) {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \"): connect() called in invalid state \" << state_;\n\n  /*\n   * The invalidState() methods don't use the normal failure mechanisms,\n   * since we don't know what state we are in.  We don't want to call\n   * startFail()/finishFail() recursively if we are already in the middle of\n   * cleaning up.\n   */\n\n  AsyncSocketException ex(\n      AsyncSocketException::ALREADY_OPEN,\n      \"connect() called with socket in invalid state\");\n  connectEndTime_ = std::chrono::steady_clock::now();\n  if ((state_ == StateEnum::CONNECTING) || (state_ == StateEnum::ERROR)) {\n    // legacy observer support\n    for (const auto& cb : lifecycleObservers_) {\n      // inform any lifecycle observes that the connection failed\n      cb->connectError(this, ex);\n    }\n\n    // folly::ObserverContainer observer support\n    if (auto list = getAsyncSocketObserverContainer()) {\n      list->invokeInterfaceMethodAllObservers(\n          [ex](auto observer, auto observed) {\n            observer->connectError(observed, ex);\n          });\n    }\n  }\n  if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) {\n    if (callback) {\n      callback->connectErr(ex);\n    }\n  } else {\n    // We can't use failConnect() here since connectCallback_\n    // may already be set to another callback.  Invoke this ConnectCallback\n    // here; any other connectCallback_ will be invoked in finishFail()\n    startFail();\n    if (callback) {\n      callback->connectErr(ex);\n    }\n    finishFail(ex);\n  }\n}\n\nvoid AsyncSocket::invalidState(ErrMessageCallback* callback) {\n  VLOG(4) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \"): setErrMessageCB(\" << callback << \") called in invalid state \"\n          << state_;\n\n  AsyncSocketException ex(\n      AsyncSocketException::NOT_OPEN,\n      msgErrQueueSupported\n          ? \"setErrMessageCB() called with socket in invalid state\"\n          : \"This platform does not support socket error message notifications\");\n  if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) {\n    if (callback) {\n      callback->errMessageError(ex);\n    }\n  } else {\n    startFail();\n    if (callback) {\n      callback->errMessageError(ex);\n    }\n    finishFail(ex);\n  }\n}\n\nvoid AsyncSocket::invokeConnectErr(const AsyncSocketException& ex) {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \"): connect err invoked with ex: \" << ex.what();\n  connectEndTime_ = std::chrono::steady_clock::now();\n  if ((state_ == StateEnum::CONNECTING) || (state_ == StateEnum::ERROR)) {\n    // invokeConnectErr() can be invoked when state is {FAST_OPEN, CLOSED,\n    // ESTABLISHED} (!?) and a bunch of other places that are not what this\n    // call back wants. This seems like a bug but work around here while we\n    // explore it independently\n\n    // legacy observer support\n    for (const auto& cb : lifecycleObservers_) {\n      cb->connectError(this, ex);\n    }\n\n    // folly::ObserverContainer observer support\n    if (auto list = getAsyncSocketObserverContainer()) {\n      list->invokeInterfaceMethodAllObservers(\n          [ex](auto observer, auto observed) {\n            observer->connectError(observed, ex);\n          });\n    }\n  }\n  if (connectCallback_) {\n    ConnectCallback* callback = connectCallback_;\n    connectCallback_ = nullptr;\n    callback->connectErr(ex);\n  }\n}\n\nvoid AsyncSocket::invokeConnectSuccess() {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \"): connect success invoked\";\n  connectEndTime_ = std::chrono::steady_clock::now();\n  maybeConnectionEstablishTime_ = connectEndTime_;\n  bool enableByteEventsForObserver = false;\n\n  // legacy observer support\n  for (const auto& cb : lifecycleObservers_) {\n    cb->connectSuccess(this);\n    enableByteEventsForObserver |= ((cb->getConfig().byteEvents) ? 1 : 0);\n  }\n\n  // folly::ObserverContainer observer support\n  if (auto list = getAsyncSocketObserverContainer()) {\n    list->invokeInterfaceMethodAllObservers([](auto observer, auto observed) {\n      observer->connectSuccess(observed);\n    });\n  }\n\n  if (enableByteEventsForObserver) {\n    enableByteEvents();\n  }\n  if (connectCallback_) {\n    ConnectCallback* callback = connectCallback_;\n    connectCallback_ = nullptr;\n    callback->connectSuccess();\n  }\n}\n\nvoid AsyncSocket::invokeConnectAttempt() {\n  VLOG(5) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \"): connect attempt\";\n  // legacy observer support\n  for (const auto& cb : lifecycleObservers_) {\n    cb->connectAttempt(this);\n  }\n\n  // folly::ObserverContainer observer support\n  if (auto list = getAsyncSocketObserverContainer()) {\n    list->invokeInterfaceMethodAllObservers([](auto observer, auto observed) {\n      observer->connectAttempt(observed);\n    });\n  }\n}\n\nvoid AsyncSocket::invalidState(ReadCallback* callback) {\n  VLOG(4) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \"): setReadCallback(\" << callback << \") called in invalid state \"\n          << state_;\n\n  AsyncSocketException ex(\n      AsyncSocketException::NOT_OPEN,\n      \"setReadCallback() called with socket in \"\n      \"invalid state\");\n  if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) {\n    if (callback) {\n      callback->readErr(ex);\n    }\n  } else {\n    startFail();\n    if (callback) {\n      callback->readErr(ex);\n    }\n    finishFail(ex);\n  }\n}\n\nvoid AsyncSocket::invalidState(WriteCallback* callback) {\n  VLOG(4) << \"AsyncSocket(this=\" << this << \", fd=\" << fd_\n          << \"): write() called in invalid state \" << state_;\n\n  AsyncSocketException ex(\n      AsyncSocketException::NOT_OPEN,\n      withAddr(\"write() called with socket in invalid state\"));\n  if (state_ == StateEnum::CLOSED || state_ == StateEnum::ERROR) {\n    if (callback) {\n      callback->writeErr(0, ex);\n    }\n  } else {\n    startFail();\n    if (callback) {\n      callback->writeErr(0, ex);\n    }\n    finishFail(ex);\n  }\n}\n\nvoid AsyncSocket::doClose() {\n  // legacy observer support\n  for (const auto& cb : lifecycleObservers_) {\n    cb->close(this);\n  }\n\n  // folly::ObserverContainer support\n  if (auto list = getAsyncSocketObserverContainer()) {\n    list->invokeInterfaceMethodAllObservers([](auto observer, auto observed) {\n      observer->close(observed);\n    });\n  }\n\n  if (fd_ == NetworkSocket()) {\n    return;\n  }\n\n  drainZeroCopyQueue();\n\n  if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) {\n    shutdownSocketSet->close(fd_);\n  } else {\n    netops_->close(fd_);\n  }\n  fd_ = NetworkSocket();\n}\n\nstd::ostream& operator<<(\n    std::ostream& os, const AsyncSocket::StateEnum& state) {\n  os << static_cast<int>(state);\n  return os;\n}\n\nstd::string AsyncSocket::withAddr(folly::StringPiece s) {\n  // Don't use addr_ directly because it may not be initialized\n  // e.g. if constructed from fd\n  folly::SocketAddress peer, local;\n  try {\n    getLocalAddress(&local);\n  } catch (...) {\n    // ignore\n  }\n  try {\n    getPeerAddress(&peer);\n  } catch (...) {\n    // ignore\n  }\n\n  return fmt::format(\n      \"{} (peer={}{})\",\n      s,\n      peer.describe(),\n      kIsMobile ? \"\" : fmt::format(\", local={}\", local.describe()));\n}\n\nvoid AsyncSocket::setTosOrTrafficClass(int tosOrTrafficClass) {\n#if defined(_WIN32)\n  throw AsyncSocketException(\n      AsyncSocketException::INTERNAL_ERROR,\n      withAddr(\"setting tos or traffic class not supported on windows\"));\n#else\n  tosOrTrafficClass_ = tosOrTrafficClass;\n  cachePeerAddress();\n  if (!addr_.isInitialized()) {\n    return;\n  }\n  auto family = addr_.getFamily();\n  int ret = 0;\n  if (family == AF_INET6) {\n    // For IPv6 set the traffic class field\n    ret = netops_->setsockopt(\n        fd_,\n        IPPROTO_IPV6,\n        IPV6_TCLASS,\n        &tosOrTrafficClass,\n        sizeof(tosOrTrafficClass));\n  } else {\n    // For IPv4 set the TOS field\n    ret = netops_->setsockopt(\n        fd_, IPPROTO_IP, IP_TOS, &tosOrTrafficClass, sizeof(tosOrTrafficClass));\n  }\n  if (ret != 0) {\n    auto errnoCopy = errno;\n    throw AsyncSocketException(\n        AsyncSocketException::INTERNAL_ERROR,\n        withAddr(\"failed to set tos\"),\n        errnoCopy);\n  }\n#endif\n}\n\nvoid AsyncSocket::setBufferCallback(BufferCallback* cb) {\n  bufferCallback_ = cb;\n}\n\nvoid AsyncSocket::connectSuccess() {\n  DestructorGuard dg(this);\n  iouConnectHandle_.reset();\n  handleConnect();\n}\n\nvoid AsyncSocket::connectTimeout() {\n  DestructorGuard dg(this);\n  if (iouConnectHandle_->cancel()) {\n    iouConnectHandle_.release();\n  } else {\n    iouConnectHandle_.reset();\n  }\n  timeoutExpired();\n}\n\nvoid AsyncSocket::sendPartial(size_t bytesWritten) {\n  DestructorGuard dg(this);\n  // Only non-zero for io_uring.\n  appBytesWritten_ += bytesWritten;\n  rawBytesWritten_ += bytesWritten;\n\n  if (bufferCallback_) {\n    bufferCallback_->onEgressBuffered();\n  }\n  // Stop after a partial write; it's highly likely that a subsequent\n  // write attempt will just return EAGAIN.\n  //\n  // Ensure that we are registered for write events.\n  if (!useIoUring_ && (eventFlags_ & EventHandler::WRITE) == 0) {\n    if (!updateEventRegistration(EventHandler::WRITE, 0)) {\n      assert(state_ == StateEnum::ERROR);\n      return;\n    }\n  }\n\n  // Reschedule the send timeout, since we have made some write progress.\n  if (sendTimeout_ > 0) {\n    if (!writeTimeout_.scheduleTimeout(sendTimeout_)) {\n      AsyncSocketException ex(\n          AsyncSocketException::INTERNAL_ERROR,\n          withAddr(\"failed to reschedule write timeout\"));\n      return failWrite(__func__, ex);\n    }\n  }\n  return;\n}\n\nvoid AsyncSocket::sendDone(size_t bytesWritten) {\n  DestructorGuard dg(this);\n  // Only non-zero for io_uring.\n  appBytesWritten_ += bytesWritten;\n  rawBytesWritten_ += bytesWritten;\n\n  // This is the last write request.\n  // Unregister for write events and cancel the send timer\n  // before we invoke the callback.  We have to update the state\n  // properly before calling the callback, since it may want to detach\n  // us from the EventBase.\n  if (eventFlags_ & EventHandler::WRITE) {\n    if (!updateEventRegistration(0, EventHandler::WRITE)) {\n      assert(state_ == StateEnum::ERROR);\n      return;\n    }\n    // Stop the send timeout\n    writeTimeout_.cancelTimeout();\n  }\n  assert(!writeTimeout_.isScheduled());\n\n  // If SHUT_WRITE_PENDING is set, we should shutdown the socket after\n  // we finish sending the last write request.\n  //\n  // We have to do this before invoking writeSuccess(), since\n  // writeSuccess() may detach us from our EventBase.\n  if (shutdownFlags_ & SHUT_WRITE_PENDING) {\n    assert(connectCallback_ == nullptr);\n    shutdownFlags_ |= SHUT_WRITE;\n\n    if (shutdownFlags_ & SHUT_READ) {\n      // Reads have already been shutdown.  Fully close the socket and\n      // move to STATE_CLOSED.\n      //\n      // Note: This code currently moves us to STATE_CLOSED even if\n      // close() hasn't ever been called.  This can occur if we have\n      // received EOF from the peer and shutdownWrite() has been called\n      // locally.  Should we bother staying in STATE_ESTABLISHED in this\n      // case, until close() is actually called?  I can't think of a\n      // reason why we would need to do so.  No other operations besides\n      // calling close() or destroying the socket can be performed at\n      // this point.\n      assert(readCallback_ == nullptr);\n      state_ = StateEnum::CLOSED;\n      if (fd_ != NetworkSocket()) {\n        ioHandler_.changeHandlerFD(NetworkSocket());\n        doClose();\n      }\n    } else {\n      // Reads are still enabled, so we are only doing a half-shutdown\n      netops_->shutdown(fd_, SHUT_WR);\n    }\n  }\n\n  if (!hasPendingWrites() && bufferCallback_) {\n    bufferCallback_->onEgressBufferCleared();\n  }\n}\n\nvoid AsyncSocket::sendErr(int err) {\n  DestructorGuard dg(this);\n  AsyncSocketException ex(\n      AsyncSocketException::INTERNAL_ERROR,\n      withAddr(\"async sendmsg() failed\"),\n      err);\n  failWrite(__func__, ex);\n}\n\nvoid AsyncSocket::recvSuccess(std::unique_ptr<IOBuf> buf) {\n  DestructorGuard dg(this);\n  DCHECK(readAncillaryDataCallback_ == nullptr);\n  CHECK(readCallback_);\n\n  if (preReceivedData_ && !preReceivedData_->empty()) {\n    preReceivedData_->appendToChain(std::move(buf));\n    auto bytes = preReceivedData_->computeChainDataLength();\n    readCallback_->readBufferAvailable(std::move(preReceivedData_));\n    appBytesReceived_ += bytes;\n    return;\n  }\n\n  auto bytes = buf->computeChainDataLength();\n  readCallback_->readBufferAvailable(std::move(buf));\n  appBytesReceived_ += bytes;\n}\n\nvoid AsyncSocket::recvEOF() noexcept {\n  DestructorGuard dg(this);\n  readErr_ = READ_EOF;\n  // EOF\n  shutdownFlags_ |= SHUT_READ;\n  if (!updateEventRegistration(0, EventHandler::READ)) {\n    // we've already been moved into STATE_ERROR\n    assert(state_ == StateEnum::ERROR);\n    assert(readCallback_ == nullptr);\n    return;\n  }\n\n  ReadCallback* callback = readCallback_;\n  readCallback_ = nullptr;\n  callback->readEOF();\n}\n\nvoid AsyncSocket::recvErr(\n    int err, std::unique_ptr<const AsyncSocketException> exception) noexcept {\n  DestructorGuard dg(this);\n  readErr_ = READ_ERROR;\n  if (exception) {\n    failRead(__func__, *exception);\n    return;\n  }\n  AsyncSocketException ex(\n      AsyncSocketException::INTERNAL_ERROR, withAddr(\"recv() failed\"), err);\n  failRead(__func__, ex);\n}\n\nstd::ostream& operator<<(\n    std::ostream& os, const folly::AsyncSocket::WriteRequestTag& tag) {\n  os << tag.buf_;\n  return os;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <chrono>\n#include <map>\n#include <memory>\n#include <variant>\n\n#include <folly/ConstructorCallbackList.h>\n#include <folly/Optional.h>\n#include <folly/SocketAddress.h>\n#include <folly/detail/SocketFastOpen.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/IOBufIovecBuilder.h>\n#include <folly/io/ShutdownSocketSet.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/io/async/AsyncSocketTransport.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/IoUringConnect.h>\n#include <folly/io/async/IoUringRecv.h>\n#include <folly/io/async/IoUringSend.h>\n#include <folly/io/async/observer/AsyncSocketObserverContainer.h>\n#include <folly/net/NetOpsDispatcher.h>\n#include <folly/net/TcpInfo.h>\n#include <folly/net/TcpInfoDispatcher.h>\n#include <folly/portability/Sockets.h>\n#include <folly/small_vector.h>\n\nnamespace folly {\n\n/**\n * A class for performing asynchronous I/O on a socket.\n *\n * AsyncSocket allows users to asynchronously wait for data on a socket, and\n * to asynchronously send data.\n *\n * The APIs for reading and writing are intentionally asymmetric.  Waiting for\n * data to read is a persistent API: a callback is installed, and is notified\n * whenever new data is available.  It continues to be notified of new events\n * until it is uninstalled.\n *\n * AsyncSocket does not provide read timeout functionality, because it\n * typically cannot determine when the timeout should be active.  Generally, a\n * timeout should only be enabled when processing is blocked waiting on data\n * from the remote endpoint.  For server sockets, the timeout should not be\n * active if the server is currently processing one or more outstanding\n * requests for this socket.  For client sockets, the timeout should not be\n * active if there are no requests pending on the socket.  Additionally, if a\n * client has multiple pending requests, it will usually want a separate\n * timeout for each request, rather than a single read timeout.\n *\n * The write API is fairly intuitive: a user can request to send a block of\n * data, and a callback will be informed once the entire block has been\n * transferred to the kernel, or on error.  AsyncSocket does provide a send\n * timeout, since most callers want to give up if the remote end stops\n * responding and no further progress can be made sending the data.\n */\n\n/**\n *This is a @deprecated approach to disabling TTLS and should be\n *removed after completing the migration to FOLLY_SO_TTLS_TRUSTED.\n */\n#if defined __linux__ && !defined FOLLY_SO_NO_TRANSPARENT_TLS\n#define FOLLY_SO_NO_TRANSPARENT_TLS 200\n#endif\n\n#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED\n#define FOLLY_SO_TTLS_TRUSTED 206\n#endif\n\n#if defined __linux__ && !defined FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED\n#define FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED 1\n#endif\n\n#if defined __linux__ && !defined SO_NO_TSOCKS\n#define SO_NO_TSOCKS 201\n#endif\n\n#if FOLLY_HAVE_SO_TIMESTAMPING\n#define SO_MAX_ATTEMPTS_ENABLE_BYTEEVENTS 10\n#endif\n\nclass AsyncSocket\n    : public AsyncSocketTransport,\n      public IoUringSendCallback,\n      public IoUringRecvCallback,\n      public IoUringConnectCallback {\n public:\n  using UniquePtr = std::unique_ptr<AsyncSocket, Destructor>;\n  using ByteEvent = AsyncSocketObserverInterface::ByteEvent;\n  using Observer = AsyncSocketObserverContainer::Observer;\n  using ManagedObserver = AsyncSocketObserverContainer::ManagedObserver;\n\n  static inline constexpr size_t kMaxAttemptsEnableByteEvents = 10;\n\n  class EvbChangeCallback {\n   public:\n    virtual ~EvbChangeCallback() = default;\n\n    // Called when the socket has been attached to a new EVB\n    // and is called from within that EVB thread\n    virtual void evbAttached(AsyncSocket* socket) = 0;\n\n    // Called when the socket is detached from an EVB and\n    // is called from the EVB thread being detached\n    virtual void evbDetached(AsyncSocket* socket) = 0;\n  };\n\n  /**\n   * This interface is implemented only for platforms supporting\n   * per-socket error queues.\n   */\n  class ErrMessageCallback {\n   public:\n    virtual ~ErrMessageCallback() = default;\n\n    /**\n     * errMessage() will be invoked when kernel puts a message to\n     * the error queue associated with the socket.\n     *\n     * @param cmsg      Reference to cmsghdr structure describing\n     *                  a message read from error queue associated\n     *                  with the socket.\n     */\n    virtual void errMessage(const cmsghdr& cmsg) noexcept = 0;\n\n    /**\n     * errMessageError() will be invoked if an error occurs reading a message\n     * from the socket error stream.\n     *\n     * @param ex        An exception describing the error that occurred.\n     */\n    virtual void errMessageError(const AsyncSocketException& ex) noexcept = 0;\n  };\n\n  class ReadAncillaryDataCallback {\n   public:\n    virtual ~ReadAncillaryDataCallback() = default;\n\n    /**\n     * `ancillaryData()` is invoked immediately before the corresponding\n     * `ReadCallback::readDataAvailable()`, as a pair.\n     *\n     * You must check for `msg_flags | MSG_CTRUNC`, indicating that some\n     * ancillary data was discarded due to lack of space.  This is normally\n     * not recoverable, so you can `close` or `failRead` the socket -- see\n     * below.\n     *\n     * ## Allowed socket mutations ###\n     *\n     * This callback is allowed to `close`, `failRead` (for child classes),\n     * or destruct the underlying socket.  It is **NOT** allowed to perform\n     * any other mutations, such as `setReadCallback` or `attachEventBase`.\n     *\n     * If `ancillaryData()` closes or fails the socket, then any data\n     * received in the same read as the ancillary data will NOT be delivered\n     * to the `ReadCallback`.\n     *\n     * # Detailed contract\n     *\n     * This will only be invoked when a `ReadCallback` is installed -- i.e.\n     * the socket is connected, neither closed nor in an error state.\n     *\n     * The supplied buffer will have originated from the most recent call to\n     * `getAncillaryDataCtrlBuffer()`.\n     *\n     * Per POSIX, ancillary data are sent / received with the first byte of\n     * the `sendmsg` data buffer, so we guarantee that the subsequent\n     * `readDataAvailable()` (if it happens) will include that data byte.\n     *\n     * @param  Can be used with macros from `man cmsg` to access ancillary\n     *         data. It is permissible to check `msg_flags & MSG_EOR`.\n     *         There is NO CONTRACT about any other `msghdr` fields -- that\n     *         is, choosing to read `msg_name*` or `msg_iov*` leads to\n     *         undefined behavior.\n     */\n    virtual folly::Expected<folly::Unit, AsyncSocketException> ancillaryData(\n        struct ::msghdr&) noexcept = 0;\n\n    /**\n     * Must return a buffer large enough to contain the incoming ancillary\n     * data, see `man cmsg` and `CMSG_SPACE`.\n     *\n     * DANGER: This call must not mutate the socket state.  e.g., you\n     * cannot call setReadCB(), setReadAncillaryDataCB() or close()\n     * from inside this call.\n     *\n     * If the supplied buffer is too small, your `ancillaryData()` will see\n     * `MSG_CTRUNC`, and the kernel will have discarded some ancillary data.\n     *\n     * It is possible that `getAncillaryDataCtrlBuffer()` will be called\n     * without a corresponding `ancillaryData()` call.  It is the callback's\n     * responsibility not to leak the buffers it returns.  Any call to\n     * `ancillaryData()` will use the most recently returned buffer.\n     *\n     * The returned buffer must remain valid until the point where\n     * `ancillaryData()` could be called with it.  That is, previously\n     * returned buffers may be freed if:\n     *  - the socket is closed / fails, or its `ReadCallback` is removed\n     *  - the `ReadAncillaryDataCallback` is uninstalled\n     *  - `ancillaryData()` completes\n     */\n    virtual folly::MutableByteRange getAncillaryDataCtrlBuffer() = 0;\n  };\n\n  /**\n   * Sometimes `SendMsgParamsCallback` needs to send different ancillary\n   * data for different writes, for example when sending FDs over Unix\n   * sockets.\n   *\n   * This opaque type acts as the key to match `writeChain` calls with\n   * `getAncillaryData()` and corresponding `wroteBytes()` calls.  It wraps\n   * `IOBuf*`, and implements equality, hashing, and ostream writes for\n   * debugging.\n   *\n   * Important usage notes:\n   *   - Even though `WriteRequestTag` never dereferences the pointer, it is\n   *     still INCORRECT to use it after the write is over, whether or not\n   *     the `IOBuf` had been destructed, because the same pointer could now\n   *     refer to new, different data that is being written (see\n   *     `getReleaseIOBufCallback` for the mechanism).\n   *   - Therefore, if you store a `WriteRequestTag`, you must remove it\n   *     whenever a write is complete.  This can be done either in\n   *     `WriteCallback::{writeErr,writeSuccess}`, or by inheriting from\n   *     `AsyncSocket::releaseIOBuf`, or by adding a new method\n   *     `SendMsgParamsCallback::onReleaseIOBuf`.\n   *   - Not all child classes support write tagging.  Notably, we removed\n   *     the `AsyncSSLSocket` implementation since it added complexity and\n   *     was not used.  Breadcrumbs are in `bioWrite`, or rev hash\n   *     95df2ce7c98a.\n   *   - The `EmptyDummy` constructor is for tests, or marking empty tags.\n   *     `SendMsgParamsCallback` methods can also be called with an empty\n   *     tag if the write is not submitted via `writeChain`.\n   */\n  struct WriteRequestTag {\n    struct EmptyDummy {};\n\n    explicit WriteRequestTag(EmptyDummy) : buf_(nullptr) {}\n    explicit WriteRequestTag(folly::IOBuf* buf) : buf_(buf) {}\n\n    bool operator==(const WriteRequestTag& other) const {\n      // Remember to also update std::hash<folly::AsyncSocket::WriteRequestTag>\n      // and ostream operator<<\n      return buf_ == other.buf_;\n    }\n\n    [[nodiscard]] bool empty() const noexcept { return buf_ == nullptr; }\n\n   private:\n    friend struct std::hash<WriteRequestTag>;\n    friend std::ostream& operator<<(std::ostream&, const WriteRequestTag&);\n\n    // The `IOBuf` submitted through `writeChain`\n    const folly::IOBuf* buf_;\n  };\n\n  class SendMsgParamsCallback {\n   public:\n    virtual ~SendMsgParamsCallback() = default;\n\n    /**\n     * getFlags() will be invoked to retrieve the desired flags to be passed\n     * to ::sendmsg() system call. It is responsible for converting flags set in\n     * the passed folly::WriteFlags enum into a integer flag bitmask that can be\n     * passed to ::sendmsg. Some flags in folly::WriteFlags do not correspond to\n     * flags that can be passed to ::sendmsg and may instead be handled via\n     * getAncillaryData.\n     *\n     * This method was intentionally declared non-virtual, so there is no way to\n     * override it. Instead feel free to override getFlagsImpl(...) instead, and\n     * enjoy the convenience of defaultFlags passed there.\n     *\n     * @param flags     Write flags requested for the given write operation\n     */\n    int getFlags(folly::WriteFlags flags, bool zeroCopyEnabled) noexcept {\n      return getFlagsImpl(flags, getDefaultFlags(flags, zeroCopyEnabled));\n    }\n\n    /**\n     * getAncillaryData() will be invoked to initialize ancillary data buffer\n     * referred by \"msg_control\" field of msghdr structure passed to ::sendmsg()\n     * system call based on the flags set in the passed folly::WriteFlags enum.\n     *\n     * Some flags in folly::WriteFlags are not relevant during this process;\n     * the default implementation only handles timestamping flags.\n     *\n     * The function requires that the size of buffer passed is equal to the\n     * value returned by getAncillaryDataSize() method for the same combination\n     * of flags.\n     *\n     * @param flags     Write flags requested for the given write operation\n     * @param data      Pointer to ancillary data buffer to initialize.\n     * @param writeTag  Documented on `WriteRequestTag`.\n     * @param byteEventsEnabled      If byte events are enabled for this socket.\n     *                               When enabled, flags relevant to socket\n     *                               timestamps (e.g., TIMESTAMP_TX) should be\n     *                               included in ancillary (msg_control) data.\n     */\n    virtual void getAncillaryData(\n        folly::WriteFlags flags,\n        void* data,\n        const WriteRequestTag& writeTag,\n        const bool byteEventsEnabled = false) noexcept;\n\n    /**\n     * getAncillaryDataSize() will be invoked to retrieve the size of\n     * ancillary data buffer which should be passed to ::sendmsg() system call\n     * The result must not exceed `maxAncillaryDataSize`.\n     *\n     * @param flags     Write flags requested for the given write operation\n     * @param writeTag  Documented on `WriteRequestTag`.\n     * @param byteEventsEnabled      If byte events are enabled for this socket.\n     *                               When enabled, flags relevant to socket\n     *                               timestamps (e.g., TIMESTAMP_TX) should be\n     *                               included in ancillary (msg_control) data.\n     */\n    virtual uint32_t getAncillaryDataSize(\n        folly::WriteFlags flags,\n        const WriteRequestTag& writeTag,\n        const bool byteEventsEnabled = false) noexcept;\n\n    /**\n     * Called immediately after a `sendmsg` corresponding to the preceding\n     * `getAncillaryData()` successfully sends at least 1 byte.\n     *\n     * This is required to enable \"exactly once\" transmission of ancillary\n     * data corresponding to `writeTag`.  For example, `AsyncFdSocket` ought\n     * not transmit tag-associated FDs twice.  Per POSIX, ancillary data are\n     * transmitted together with the first data byte.\n     */\n    virtual void wroteBytes(const WriteRequestTag&) noexcept {}\n\n    // This is not an OS limitation (see `/proc/sys/net/core/optmem_max` on\n    // Linux) but is done only because today's `AsyncSocket` implementation\n    // uses `alloca` to allocate the ancillary data buffer on the stack in\n    // order to support a cheap default `SendMsgParamsCallback` on every\n    // socket.  If the buffer management could be handed to the socket (e.g.\n    // each socket contains a few bytes of buffer for the default callback),\n    // then we could delete this maximum, and `getAncillaryDataSize`, in\n    // favor of `folly::ByteRange getAncillaryData()`.\n    static const size_t maxAncillaryDataSize{0x5000};\n\n   private:\n    /**\n     * getFlagsImpl() will be invoked by getFlags(folly::WriteFlags flags)\n     * method to retrieve the flags to be passed to ::sendmsg() system call.\n     * SendMsgParamsCallback::getFlags() is calling this method, and returns\n     * its results directly to the caller in AsyncSocket.\n     * Classes inheriting from SendMsgParamsCallback are welcome to override\n     * this method to force SendMsgParamsCallback to return its own set\n     * of flags.\n     *\n     * @param flags        Write flags requested for the given write operation\n     * @param defaultflags A set of message flags returned by getDefaultFlags()\n     *                     method for the given \"flags\" mask.\n     */\n    virtual int getFlagsImpl(folly::WriteFlags /*flags*/, int defaultFlags) {\n      return defaultFlags;\n    }\n\n    /**\n     * getDefaultFlags() will be invoked by  getFlags(folly::WriteFlags flags)\n     * to retrieve the default set of flags, and pass them to getFlagsImpl(...)\n     *\n     * @param flags     Write flags requested for the given write operation\n     */\n    int getDefaultFlags(folly::WriteFlags flags, bool zeroCopyEnabled) noexcept;\n  };\n\n  /**\n   * Container with state and processing logic for ByteEvents.\n   */\n  struct ByteEventHelper {\n    bool byteEventsEnabled{false};\n    long rawBytesWrittenWhenByteEventsEnabled{0};\n    folly::Optional<AsyncSocketException> maybeEx;\n\n    /**\n     * Process a Cmsg and return a ByteEvent if available.\n     *\n     * The kernel will pass two cmsg for each timestamp:\n     *   1. ScmTimestamping: Software / Hardware Timestamps.\n     *   2. SockExtendedErrTimestamping: Byte offset associated with timestamp.\n     *\n     * These messages will be passed back-to-back; processCmsg() can handle them\n     * in any order (1 then 2, or 2 then 1), as long the order is consistent\n     * across timestamps.\n     *\n     * processCmsg() gracefully ignores Cmsg unrelated to socket timestamps, but\n     * will throw if it receives a sequence of Cmsg that are not compliant with\n     * its expectations.\n     *\n     * @return If the helper has received all components required to generate a\n     *         ByteEvent (e.g., ScmTimestamping and SockExtendedErrTimestamping\n     *         messages), it returns a ByteEvent and clears its local state.\n     *         Otherwise, returns an empty optional.\n     *\n     *         If the helper has previously thrown a ByteEventHelper::Exception,\n     *         it will not process further Cmsg and will continuously return an\n     *         empty optional.\n     *\n     * @throw  If the helper receives a sequence of Cmsg that violate its\n     *         expectations (e.g., multiple ScmTimestamping messages in a row\n     *         without corresponding SockExtendedErrTimestamping messages), it\n     *         throws a ByteEventHelper::Exception. Subsequent calls will return\n     *         an empty optional.\n     */\n    folly::Optional<ByteEvent> processCmsg(\n        const cmsghdr& cmsg, const size_t rawBytesWritten);\n\n    /**\n     * Exception class thrown by processCmsg.\n     *\n     * ByteEventHelper does not know the socket address and thus cannot\n     * construct a AsyncSocketException. Instead, ByteEventHelper throws a\n     * custom Exception and AsyncSocket rewraps it as an AsyncSocketException.\n     */\n    class Exception : public std::runtime_error {\n      using std::runtime_error::runtime_error;\n    };\n\n   private:\n    // state, reinitialized each time a complete timestamp is processed\n    struct TimestampState {\n      bool serrReceived{false};\n      uint32_t typeRaw{0};\n      uint32_t byteOffsetKernel{0};\n\n      bool scmTsReceived{false};\n      folly::Optional<std::chrono::nanoseconds> maybeSoftwareTs;\n      folly::Optional<std::chrono::nanoseconds> maybeHardwareTs;\n    };\n    folly::Optional<TimestampState> maybeTsState_;\n  };\n\n  explicit AsyncSocket();\n  /**\n   * Create a new unconnected AsyncSocket.\n   *\n   * connect() must later be called on this socket to establish a connection.\n   */\n  explicit AsyncSocket(EventBase* evb);\n\n  void setShutdownSocketSet(const std::weak_ptr<ShutdownSocketSet>& wSS);\n\n  /**\n   * Create a new AsyncSocket and begin the connection process.\n   *\n   * @param evb             EventBase that will manage this socket.\n   * @param address         The address to connect to.\n   * @param connectTimeout  Optional timeout in milliseconds for the connection\n   *                        attempt.\n   * @param useZeroCopy     Optional zerocopy socket mode\n   */\n  AsyncSocket(\n      EventBase* evb,\n      const folly::SocketAddress& address,\n      uint32_t connectTimeout = 0,\n      bool useZeroCopy = false);\n\n  /**\n   * Create a new AsyncSocket and begin the connection process.\n   *\n   * @param evb             EventBase that will manage this socket.\n   * @param ip              IP address to connect to (dotted-quad).\n   * @param port            Destination port in host byte order.\n   * @param connectTimeout  Optional timeout in milliseconds for the connection\n   *                        attempt.\n   * @param useZeroCopy     Optional zerocopy socket mode\n   */\n  AsyncSocket(\n      EventBase* evb,\n      const std::string& ip,\n      uint16_t port,\n      uint32_t connectTimeout = 0,\n      bool useZeroCopy = false);\n\n  /**\n   * Create a AsyncSocket from an already connected socket file descriptor.\n   *\n   * Note that while AsyncSocket enables TCP_NODELAY for sockets it creates\n   * when connecting, it does not change the socket options when given an\n   * existing file descriptor.  If callers want TCP_NODELAY enabled when using\n   * this version of the constructor, they need to explicitly call\n   * setNoDelay(true) after the constructor returns.\n   *\n   * @param evb            EventBase that will manage this socket.\n   * @param fd             File descriptor to take over (connected socket).\n   * @param zeroCopyBufId  Zerocopy buf id to start with.\n   * @param peerAddress    Optional peer address (eg: returned from accept). If\n   *                       nullptr, AsyncSocket will lazily attempt to determine\n   *                       it from fd via a system call.\n   * @param maybeConnectionEstablishTime  Optional parameter indicating when the\n   *                                      connection was established. Can be\n   *                                      used by acceptors to record when a\n   *                                      connection was established and make\n   *                                      this information available via the\n   *                                      getConnectionEstablishTime() method.\n   */\n  AsyncSocket(\n      EventBase* evb,\n      NetworkSocket fd,\n      uint32_t zeroCopyBufId = 0,\n      const SocketAddress* peerAddress = nullptr,\n      folly::Optional<std::chrono::steady_clock::time_point>\n          maybeConnectionEstablishTime = folly::none);\n\n  /**\n   * Create an AsyncSocket from a different, already connected AsyncSocket.\n   *\n   * Similar to AsyncSocket(evb, fd) when fd was previously owned by an\n   * AsyncSocket.\n   */\n  explicit AsyncSocket(AsyncSocket::UniquePtr);\n\n  /**\n   * Create an AsyncSocket from a different, already connected AsyncSocket.\n   *\n   * Similar to AsyncSocket(evb, fd) when fd was previously owned by an\n   * AsyncSocket. Caller must call destroy on old AsyncSocket unless it is\n   * in a smart pointer with appropriate destructor.\n   */\n  explicit AsyncSocket(AsyncSocket*);\n\n  /**\n   * Helper function to create an AsyncSocket..\n   *\n   * This passes in the correct destructor object, since AsyncSocket's\n   * destructor is protected and cannot be invoked directly.\n   */\n  static UniquePtr newSocket(EventBase* evb) {\n    return UniquePtr{new AsyncSocket(evb)};\n  }\n\n  /**\n   * Helper function to create an AsyncSocket.\n   */\n  static UniquePtr newSocket(\n      EventBase* evb,\n      const folly::SocketAddress& address,\n      uint32_t connectTimeout = 0,\n      bool useZeroCopy = false) {\n    return UniquePtr{\n        new AsyncSocket(evb, address, connectTimeout, useZeroCopy)};\n  }\n\n  /**\n   * Helper function to create an AsyncSocket.\n   */\n  static UniquePtr newSocket(\n      EventBase* evb,\n      const std::string& ip,\n      uint16_t port,\n      uint32_t connectTimeout = 0,\n      bool useZeroCopy = false) {\n    return UniquePtr{\n        new AsyncSocket(evb, ip, port, connectTimeout, useZeroCopy)};\n  }\n\n  /**\n   * Helper function to create an AsyncSocket.\n   */\n  static UniquePtr newSocket(\n      EventBase* evb,\n      NetworkSocket fd,\n      const SocketAddress* peerAddress = nullptr) {\n    return UniquePtr{new AsyncSocket(evb, fd, 0, peerAddress)};\n  }\n\n  /**\n   * Destroy the socket.\n   *\n   * AsyncSocket::destroy() must be called to destroy the socket.\n   * The normal destructor is private, and should not be invoked directly.\n   * This prevents callers from deleting a AsyncSocket while it is invoking a\n   * callback.\n   */\n  void destroy() override;\n\n  /**\n   * Get the EventBase used by this socket.\n   */\n  EventBase* getEventBase() const override { return eventBase_; }\n\n  /**\n   * Get the network socket used by the AsyncSocket.\n   */\n  NetworkSocket getNetworkSocket() const override { return fd_; }\n\n  /**\n   * Extract the file descriptor from the AsyncSocket.\n   *\n   * This will immediately cause any installed callbacks to be invoked with an\n   * error.  The AsyncSocket may no longer be used after the file descriptor\n   * has been extracted.\n   *\n   * This method should be used with care as the resulting fd is not guaranteed\n   * to perfectly reflect the state of the AsyncSocket (security state,\n   * pre-received data, etc.).\n   *\n   * Returns the file descriptor.  The caller assumes ownership of the\n   * descriptor, and it will not be closed when the AsyncSocket is destroyed.\n   */\n  virtual NetworkSocket detachNetworkSocket();\n\n  /**\n   * Initiate a connection.\n   *\n   * @param callback  The callback to inform when the connection attempt\n   *                  completes.\n   * @param address   The address to connect to.\n   * @param timeout   A timeout value, in milliseconds.  If the connection\n   *                  does not succeed within this period,\n   *                  callback->connectError() will be invoked.\n   * @param bindOptions Either a SocketAddress to bind to, or a NetworkSocket\n   *                  with an address already bound to it via bind().\n   *                  Ownership of a NetworkSocket is transferred from the\n   *                  caller to this AsyncSocket.\n   */\n  virtual void connect(\n      ConnectCallback* callback,\n      const folly::SocketAddress& address,\n      int timeout = 0,\n      const SocketOptionMap& options = emptySocketOptionMap,\n      const BindOptions& bindOptions = anyAddress(),\n      const std::string& ifName = \"\") noexcept override;\n\n  void connect(\n      ConnectCallback* callback,\n      const std::string& ip,\n      uint16_t port,\n      int timeout = 0,\n      const SocketOptionMap& options = emptySocketOptionMap) noexcept;\n\n  /**\n   * If a connect request is in-flight, cancels it and closes the socket\n   * immediately. Otherwise, this is a no-op.\n   *\n   * This does not invoke any connection related callbacks. Call this to\n   * prevent any connect callback while cleaning up, etc.\n   */\n  void cancelConnect() override;\n\n  /**\n   * Set the send timeout.\n   *\n   * If write requests do not make any progress for more than the specified\n   * number of milliseconds, fail all pending writes and close the socket.\n   *\n   * If write requests are currently pending when setSendTimeout() is called,\n   * the timeout interval is immediately restarted using the new value.\n   *\n   * (See the comments for AsyncSocket for an explanation of why AsyncSocket\n   * provides setSendTimeout() but not setRecvTimeout().)\n   *\n   * @param milliseconds  The timeout duration, in milliseconds.  If 0, no\n   *                      timeout will be used.\n   */\n  void setSendTimeout(uint32_t milliseconds) override;\n\n  /**\n   * Get the send timeout.\n   *\n   * @return Returns the current send timeout, in milliseconds.  A return value\n   *         of 0 indicates that no timeout is set.\n   */\n  uint32_t getSendTimeout() const override { return sendTimeout_; }\n\n  /**\n   * Set the maximum number of reads to execute from the underlying\n   * socket each time the EventBase detects that new ingress data is\n   * available. The default is unlimited, but callers can use this method\n   * to limit the amount of data read from the socket per event loop\n   * iteration.\n   *\n   * @param maxReads  Maximum number of reads per data-available event;\n   *                  a value of zero means unlimited.\n   */\n  void setMaxReadsPerEvent(uint16_t maxReads) { maxReadsPerEvent_ = maxReads; }\n\n  /**\n   * Get the maximum number of reads this object will execute from\n   * the underlying socket each time the EventBase detects that new\n   * ingress data is available.\n   *\n   * @returns Maximum number of reads per data-available event; a value\n   *          of zero means unlimited.\n   */\n  uint16_t getMaxReadsPerEvent() const { return maxReadsPerEvent_; }\n\n  /**\n   * Set a pointer to ErrMessageCallback implementation which will be\n   * receiving notifications for messages posted to the error queue\n   * associated with the socket.\n   * ErrMessageCallback is implemented only for platforms with\n   * per-socket error message queues support (recvmsg() system call must\n   * )\n   *\n   */\n  virtual void setErrMessageCB(ErrMessageCallback* callback);\n\n  /**\n   * Get a pointer to ErrMessageCallback implementation currently\n   * registered with this socket.\n   *\n   */\n  virtual ErrMessageCallback* getErrMessageCallback() const;\n\n  /**\n   * Set a pointer to ReadAncillaryDataCallback implementation which will\n   * be invoked with the ancillary data when we read a buffer from the\n   * associated socket.\n   * ReadAncillaryDataCallback is implemented only for platforms with\n   * kernel timestamp support.\n   *\n   */\n  virtual void setReadAncillaryDataCB(ReadAncillaryDataCallback* callback);\n\n  /**\n   * Get a pointer to ReadAncillaryDataCallback implementation currently\n   * registered with this socket.\n   *\n   */\n  virtual ReadAncillaryDataCallback* getReadAncillaryDataCallback() const;\n\n  /**\n   * Set a pointer to SendMsgParamsCallback implementation which\n   * will be used to form ::sendmsg() system call parameters\n   *\n   */\n  virtual void setSendMsgParamCB(SendMsgParamsCallback* callback);\n\n  /**\n   * Get a pointer to SendMsgParamsCallback implementation currently\n   * registered with this socket.\n   *\n   */\n  virtual SendMsgParamsCallback* getSendMsgParamsCB() const;\n\n  /**\n   * Override netops::Dispatcher to be used for netops:: calls.\n   *\n   * Pass empty shared_ptr to reset to default.\n   * Override can be used by unit tests to intercept and mock netops:: calls.\n   */\n  virtual void setOverrideNetOpsDispatcher(\n      std::shared_ptr<netops::Dispatcher> dispatcher) {\n    netops_.setOverride(std::move(dispatcher));\n  }\n\n  /**\n   * Returns override netops::Dispatcher being used for netops:: calls.\n   *\n   * Returns empty shared_ptr if no override set.\n   * Override can be used by unit tests to intercept and mock netops:: calls.\n   */\n  virtual std::shared_ptr<netops::Dispatcher> getOverrideNetOpsDispatcher()\n      const {\n    return netops_.getOverride();\n  }\n\n  /**\n   * Override folly::TcpInfoDispatcher to be used for getting TcpInfo.\n   *\n   * Pass empty shared_ptr to reset to default.\n   * Override can be used by unit tests to intercept and mock\n   * TcpInfo::initFromFd calls.\n   */\n  virtual void setOverrideTcpInfoDispatcher(\n      std::shared_ptr<folly::TcpInfoDispatcher> dispatcher) {\n    tcpInfoDispatcher_.setOverride(std::move(dispatcher));\n  }\n\n  /**\n   * Returns override folly::TcpInfoDispatcher being used for tcpinfo calls.\n   *\n   * Returns empty shared_ptr if no override set.\n   * Override can be used by unit tests to intercept and mock\n   * TcpInfo::initFromFd calls.\n   */\n  virtual std::shared_ptr<folly::TcpInfoDispatcher>\n  getOverrideTcpInfoDispatcher() const {\n    return tcpInfoDispatcher_.getOverride();\n  }\n\n  // Read and write methods\n  void setReadCB(ReadCallback* callback) override;\n  ReadCallback* getReadCallback() const override;\n\n  /**\n   * Create a memory store to use for zero copy reads.\n   *\n   * The memory store contains a fixed number of entries, each with a fixed\n   * size.  When data is read using zero-copy the kernel will place it in one\n   * of these entries, and it will be returned to the callback with\n   * readZeroCopyDataAvailable().  The callback must release the IOBuf\n   * reference to make the entry available again for future zero-copy reads.\n   * If all entries are exhausted the read code will fall back to non-zero-copy\n   * reads.\n   *\n   * Note: it is the caller's responsibility to ensure that they do not destroy\n   * the ZeroCopyMemStore while it still has any outstanding entries in use.\n   * The caller must ensure the ZeroCopyMemStore exists until all callers have\n   * finished using any data returned via zero-copy reads, and released the\n   * IOBuf objects containing that data.\n   *\n   * @param entries  The number of entries to allocate in the memory store.\n   * @param size     The size of each entry, in bytes.  This should be a\n   *                 multiple of the kernel page size.\n   */\n\n  static std::unique_ptr<AsyncReader::ReadCallback::ZeroCopyMemStore>\n  createDefaultZeroCopyMemStore(size_t entries, size_t size);\n\n  bool setZeroCopy(bool enable) override;\n  bool getZeroCopy() const override { return zeroCopyEnabled_; }\n\n  uint32_t getZeroCopyBufId() const { return zeroCopyBufId_; }\n\n  size_t getZeroCopyReenableThreshold() const {\n    return zeroCopyReenableThreshold_;\n  }\n\n  void setZeroCopyEnableFunc(AsyncWriter::ZeroCopyEnableFunc func) override;\n\n  void setZeroCopyEnableThreshold(size_t threshold) override;\n\n  void setZeroCopyReenableThreshold(size_t threshold);\n\n  struct ZeroCopyDrainConfig {\n    std::chrono::milliseconds drainDelay{1000};\n    std::optional<unsigned short> linger;\n  };\n\n  void setZeroCopyDrainConfig(const ZeroCopyDrainConfig& config);\n\n  void write(\n      WriteCallback* callback,\n      const void* buf,\n      size_t bytes,\n      WriteFlags flags = WriteFlags::NONE) override;\n  void writev(\n      WriteCallback* callback,\n      const iovec* vec,\n      size_t count,\n      WriteFlags flags = WriteFlags::NONE) override;\n  void writeChain(\n      WriteCallback* callback,\n      std::unique_ptr<folly::IOBuf>&& buf,\n      WriteFlags flags = WriteFlags::NONE) override;\n\n  class WriteRequest;\n  virtual void writeRequest(WriteRequest* req);\n  void writeRequestReady() { handleWrite(); }\n\n  // Methods inherited from AsyncTransport\n  void close() override;\n  void closeNow() override;\n  void closeWithReset() override;\n  void shutdownWrite() override;\n  void shutdownWriteNow() override;\n\n  bool readable() const override;\n  bool writable() const override;\n  bool isPending() const override;\n  bool hangup() const override;\n  bool good() const override;\n  bool error() const override;\n  void attachEventBase(EventBase* eventBase) override;\n  void detachEventBase() override;\n  bool isDetachable() const override;\n\n  void getLocalAddress(folly::SocketAddress* address) const override;\n  void getPeerAddress(folly::SocketAddress* address) const override;\n\n  bool isEorTrackingEnabled() const override { return trackEor_; }\n\n  void setEorTracking(bool track) override { trackEor_ = track; }\n\n  bool connecting() const override { return (state_ == StateEnum::CONNECTING); }\n\n  virtual bool isClosedByPeer() const {\n    return (\n        state_ == StateEnum::CLOSED &&\n        (readErr_ == READ_EOF || readErr_ == READ_ERROR));\n  }\n\n  virtual bool isClosedBySelf() const {\n    return (\n        state_ == StateEnum::CLOSED &&\n        (readErr_ != READ_EOF && readErr_ != READ_ERROR));\n  }\n\n  size_t getAppBytesWritten() const override { return appBytesWritten_; }\n\n  size_t getRawBytesWritten() const override { return rawBytesWritten_; }\n\n  size_t getAppBytesReceived() const override { return appBytesReceived_; }\n\n  size_t getRawBytesReceived() const override { return getAppBytesReceived(); }\n\n  size_t getAppBytesBuffered() const override {\n    return totalAppBytesScheduledForWrite_ - appBytesWritten_;\n  }\n  size_t getRawBytesBuffered() const override { return getAppBytesBuffered(); }\n\n  size_t getAllocatedBytesBuffered() const override {\n    return allocatedBytesBuffered_;\n  }\n\n  // End of methods inherited from AsyncTransport\n\n  std::chrono::nanoseconds getConnectTime() const {\n    return connectEndTime_ - connectStartTime_;\n  }\n\n  std::chrono::milliseconds getConnectTimeout() const {\n    return connectTimeout_;\n  }\n\n  /**\n   * Returns when connect() started.\n   */\n  std::chrono::steady_clock::time_point getConnectStartTime() const {\n    return connectStartTime_;\n  }\n\n  /**\n   * Returns when connect() finished (either successfully or failed).\n   */\n  std::chrono::steady_clock::time_point getConnectEndTime() const {\n    return connectEndTime_;\n  }\n\n  /**\n   * Returns when the connection was established.\n   *\n   *  -  If connect() was called and succeeded, this is the same as\n   *     getConnectEndTime().\n   *\n   *  -  If AsyncSocket was initialized with a file descriptor (e.g., by an\n   *     acceptor), returns the connection establishment time passed to the\n   *     constructor. If no time was passed, returns folly::none.\n   */\n  folly::Optional<std::chrono::steady_clock::time_point>\n  getConnectionEstablishTime() const {\n    return maybeConnectionEstablishTime_;\n  }\n\n  bool getTFOAttempted() const { return tfoInfo_.attempted; }\n\n  /**\n   * Returns whether or not the attempt to use TFO\n   * finished successfully. This does not necessarily\n   * mean TFO worked, just that trying to use TFO\n   * succeeded.\n   */\n  bool getTFOFinished() const { return tfoInfo_.finished; }\n\n  /**\n   * Returns whether or not TFO attempt succeeded on this\n   * connection.\n   * For servers this is pretty straightforward API and can\n   * be invoked right after the connection is accepted. This API\n   * will perform one syscall.\n   * This API is a bit tricky to use for clients, since clients\n   * only know this for sure after the SYN-ACK is returned. So it's\n   * appropriate to call this only after the first application\n   * data is read from the socket when the caller knows that\n   * the SYN has been ACKed by the server.\n   */\n  bool getTFOSucceeded() const override;\n\n  // Methods controlling socket options\n\n  /**\n   * Force writes to be transmitted immediately.\n   *\n   * This controls the TCP_NODELAY socket option.  When enabled, TCP segments\n   * are sent as soon as possible, even if it is not a full frame of data.\n   * When disabled, the data may be buffered briefly to try and wait for a full\n   * frame of data.\n   *\n   * By default, TCP_NODELAY is enabled for AsyncSocket objects.\n   *\n   * This method will fail if the socket is not currently open.\n   *\n   * @return Returns 0 if the TCP_NODELAY flag was successfully updated,\n   *         or a non-zero errno value on error.\n   */\n  int setNoDelay(bool noDelay) override;\n\n  /**\n   * Set the FD_CLOEXEC flag so that the socket will be closed if the program\n   * later forks and execs.\n   */\n  void setCloseOnExec();\n\n  /*\n   * Set the Flavor of Congestion Control to be used for this Socket\n   * Please check '/lib/modules/<kernel>/kernel/net/ipv4' for tcp_*.ko\n   * first to make sure the module is available for plugging in\n   * Alternatively you can choose from net.ipv4.tcp_allowed_congestion_control\n   */\n  int setCongestionFlavor(const std::string& cname);\n\n  /*\n   * Forces ACKs to be sent immediately\n   *\n   * @return Returns 0 if the TCP_QUICKACK flag was successfully updated,\n   *         or a non-zero errno value on error.\n   */\n  int setQuickAck(bool quickack);\n\n  /**\n   * Set the send bufsize\n   */\n  int setSendBufSize(size_t bufsize);\n\n  /**\n   * Set the recv bufsize\n   */\n  int setRecvBufSize(size_t bufsize);\n\n#if defined(__linux__)\n  /**\n   * @brief This method is used to get the number of bytes that are currently\n   *        stored in the TCP send/tx buffer\n   *\n   * @return the number of bytes in the send/tx buffer or folly::none if there\n   *         was a problem\n   */\n  size_t getSendBufInUse() const;\n\n  /**\n   * @brief This method is used to get the number of bytes that are currently\n   *        stored in the TCP receive/rx buffer\n   *\n   * @return the number of bytes in the receive/rx buffer or folly::none if\n   *         there was a problem\n   */\n  size_t getRecvBufInUse() const;\n#endif\n\n/**\n * Sets a specific tcp personality\n * Available only on kernels 3.2 and greater\n */\n#define SO_SET_NAMESPACE 41\n  int setTCPProfile(int profd);\n\n  /**\n   * Generic API for reading a socket option.\n   *\n   * @param level     same as the \"level\" parameter in getsockopt().\n   * @param optname   same as the \"optname\" parameter in getsockopt().\n   * @param optval    pointer to the variable in which the option value should\n   *                  be returned.\n   * @param optlen    value-result argument, initially containing the size of\n   *                  the buffer pointed to by optval, and modified on return\n   *                  to indicate the actual size of the value returned.\n   * @return          same as the return value of getsockopt().\n   */\n  template <typename T>\n  int getSockOpt(int level, int optname, T* optval, socklen_t* optlen) {\n    return netops_->getsockopt(fd_, level, optname, (void*)optval, optlen);\n  }\n\n  /**\n   * Generic API for setting a socket option.\n   *\n   * @param level     same as the \"level\" parameter in getsockopt().\n   * @param optname   same as the \"optname\" parameter in getsockopt().\n   * @param optval    the option value to set.\n   * @return          same as the return value of setsockopt().\n   */\n  template <typename T>\n  int setSockOpt(int level, int optname, const T* optval) {\n    return netops_->setsockopt(fd_, level, optname, optval, sizeof(T));\n  }\n\n  int setSockOpt(\n      int level, int optname, const void* optval, socklen_t optsize) override {\n    return netops_->setsockopt(fd_, level, optname, optval, optsize);\n  }\n\n  /**\n   * Virtual method for reading a socket option returning integer\n   * value, which is the most typical case. Convenient for overriding\n   * and mocking.\n   *\n   * @param level     same as the \"level\" parameter in getsockopt().\n   * @param optname   same as the \"optname\" parameter in getsockopt().\n   * @param optval    same as \"optval\" parameter in getsockopt().\n   * @param optlen    same as \"optlen\" parameter in getsockopt().\n   * @return          same as the return value of getsockopt().\n   */\n  virtual int getSockOptVirtual(\n      int level, int optname, void* optval, socklen_t* optlen) {\n    return netops_->getsockopt(fd_, level, optname, optval, optlen);\n  }\n\n  /**\n   * Virtual method for setting a socket option accepting integer\n   * value, which is the most typical case. Convenient for overriding\n   * and mocking.\n   *\n   * @param level     same as the \"level\" parameter in setsockopt().\n   * @param optname   same as the \"optname\" parameter in setsockopt().\n   * @param optval    same as \"optval\" parameter in setsockopt().\n   * @param optlen    same as \"optlen\" parameter in setsockopt().\n   * @return          same as the return value of setsockopt().\n   */\n  virtual int setSockOptVirtual(\n      int level, int optname, void const* optval, socklen_t optlen) {\n    return netops_->setsockopt(fd_, level, optname, optval, optlen);\n  }\n\n  /**\n   * Set pre-received data, to be returned to read callback before any data\n   * from the socket.\n   */\n  void setPreReceivedData(std::unique_ptr<IOBuf> data) override {\n    if (preReceivedData_) {\n      preReceivedData_->prependChain(std::move(data));\n    } else {\n      preReceivedData_ = std::move(data);\n    }\n  }\n\n  std::unique_ptr<IOBuf> takePreReceivedData() override {\n    return std::move(preReceivedData_);\n  }\n\n  /**\n   * Enables TFO behavior on the AsyncSocket if FOLLY_ALLOW_TFO\n   * is set.\n   */\n  void enableTFO() override {\n    // No-op if folly does not allow tfo\n#if FOLLY_ALLOW_TFO\n    tfoInfo_.enabled = true;\n#endif\n  }\n\n  /**\n   * Sets TOS or traffic class. Throws an exception on error.\n   */\n  void setTosOrTrafficClass(int tosOrTrafficClass);\n\n  /**\n   * This flag controls whether or not IP_BIND_ADDRESS_NO_PORT is enabled for\n   * AsyncSocket sockets. This is enabled by default.\n   */\n  void setBindAddressNoPort(bool flag) { bindAddressNoPort_ = flag; }\n\n  void disableTransparentTls() override { noTransparentTls_ = true; }\n\n  void disableTSocks() { noTSocks_ = true; }\n\n  enum class StateEnum : uint8_t {\n    UNINIT,\n    CONNECTING,\n    ESTABLISHED,\n    CLOSED,\n    ERROR,\n    FAST_OPEN,\n  };\n\n  void setBufferCallback(BufferCallback* cb);\n\n  // Callers should set this prior to connecting the socket for the safest\n  // behavior.\n  void setEvbChangedCallback(std::unique_ptr<EvbChangeCallback> cb) {\n    evbChangeCb_ = std::move(cb);\n  }\n\n  /**\n   * Attempt to cache the current local and peer addresses (if not already\n   * cached) so that they are available from getPeerAddress() and\n   * getLocalAddress() even after the socket is closed.\n   */\n  void cacheAddresses() override;\n\n  /**\n   * Returns true if there is any zero copy write in progress\n   * Needs to be called from within the socket's EVB thread\n   */\n  bool isZeroCopyWriteInProgress() const noexcept;\n\n  /**\n   * Tries to process the msg error queue\n   * And returns true if there are no more zero copy writes in progress\n   */\n  bool processZeroCopyWriteInProgress() noexcept;\n\n  /**\n   * Whether socket should be closed on write failure (true by default).\n   */\n  void setCloseOnFailedWrite(bool closeOnFailedWrite) {\n    closeOnFailedWrite_ = closeOnFailedWrite;\n  }\n\n  /**\n   * Get folly::TcpInfo from socket\n   */\n  folly::Expected<folly::TcpInfo, std::errc> getTcpInfo(\n      const TcpInfo::LookupOptions& options);\n\n  /**\n   * writeReturn is the total number of bytes written, or WRITE_ERROR on error.\n   * If no data has been written, 0 is returned.\n   * exception is a more specific exception that cause a write error.\n   * Not all writes have exceptions associated with them thus writeReturn\n   * should be checked to determine whether the operation resulted in an error.\n   */\n  struct WriteResult {\n    explicit WriteResult(ssize_t ret) : writeReturn(ret) {}\n\n    WriteResult(ssize_t ret, std::unique_ptr<const AsyncSocketException> e)\n        : writeReturn(ret), exception(std::move(e)) {}\n\n    ssize_t writeReturn;\n    std::unique_ptr<const AsyncSocketException> exception;\n  };\n\n  /**\n   * readReturn is the number of bytes read, or READ_EOF on EOF, or\n   * READ_ERROR on error, or READ_BLOCKING if the operation will\n   * block.\n   * exception is a more specific exception that may have caused a read error.\n   * Not all read errors have exceptions associated with them thus readReturn\n   * should be checked to determine whether the operation resulted in an error.\n   */\n  struct ReadResult {\n    explicit ReadResult(ssize_t ret) : readReturn(ret) {}\n\n    ReadResult(ssize_t ret, std::unique_ptr<const AsyncSocketException> e)\n        : readReturn(ret), exception(std::move(e)) {}\n\n    ssize_t readReturn;\n    std::unique_ptr<const AsyncSocketException> exception;\n  };\n\n  /**\n   * Wrapper class for WriteCallback that includes a boolean variable to track\n   * whether the write has already started or not\n   */\n  class WriteCallbackWithState {\n   public:\n    explicit WriteCallbackWithState(WriteCallback* callback)\n        : callback_(callback) {}\n    WriteCallback* getCallback() const { return callback_; }\n\n    void notifyOnWrite() noexcept {\n      if (callback_ && !writeInProgress_) {\n        callback_->writeStarting();\n      }\n      writeInProgress_ = true;\n    }\n\n   private:\n    WriteCallback* callback_{nullptr};\n    bool writeInProgress_{false};\n  };\n\n  /**\n   * A WriteRequest object tracks information about a pending write operation.\n   */\n  class WriteRequest {\n   public:\n    WriteRequest(AsyncSocket* socket, WriteCallback* callback)\n        : socket_(socket),\n          callbackWithState_(WriteCallbackWithState(callback)),\n          releaseIOBufCallback_(\n              callback ? callback->getReleaseIOBufCallback() : nullptr) {}\n\n    WriteRequest(AsyncSocket* socket, WriteCallbackWithState callbackWithState)\n        : socket_(socket),\n          callbackWithState_(callbackWithState),\n          releaseIOBufCallback_(\n              callbackWithState.getCallback()\n                  ? callbackWithState.getCallback()->getReleaseIOBufCallback()\n                  : nullptr) {}\n\n    virtual void start() {}\n\n    virtual void destroy() = 0;\n\n    virtual WriteResult performWrite() = 0;\n\n    virtual void consume() = 0;\n\n    virtual bool isComplete() = 0;\n\n    WriteRequest* getNext() const { return next_; }\n\n    WriteCallback* getCallback() const {\n      return callbackWithState_.getCallback();\n    }\n\n    WriteCallbackWithState& getCallbackWithState() {\n      return callbackWithState_;\n    }\n\n    uint32_t getTotalBytesWritten() const { return totalBytesWritten_; }\n\n    void append(WriteRequest* next) {\n      assert(next_ == nullptr);\n      next_ = next;\n    }\n\n    void fail(const char* fn, const AsyncSocketException& ex) {\n      socket_->failWrite(fn, ex);\n    }\n\n    void bytesWritten(size_t count) {\n      totalBytesWritten_ += uint32_t(count);\n      socket_->appBytesWritten_ += count;\n    }\n\n   protected:\n    // protected destructor, to ensure callers use destroy()\n    virtual ~WriteRequest() {}\n\n    AsyncSocket* socket_; ///< parent socket\n    WriteRequest* next_{nullptr}; ///< pointer to next WriteRequest\n    WriteCallbackWithState callbackWithState_; ///< completion callback\n    ReleaseIOBufCallback* releaseIOBufCallback_; ///< release IOBuf callback\n    uint32_t totalBytesWritten_{0}; ///< total bytes written\n  };\n\n public:\n  /**\n   * Observer of socket events.\n   */\n  class LegacyLifecycleObserver : public AsyncSocketObserverInterface {\n   public:\n    /**\n     * Observer configuration.\n     *\n     * Specifies events observer wants to receive. Cannot be changed post\n     * initialization because the transport may turn on / off instrumentation\n     * when observers are added / removed, based on the observer configuration.\n     */\n    struct Config {\n      Config() = default;\n      Config(const Config&) = default;\n      Config& operator=(const Config&) = default;\n      virtual ~Config() = default;\n\n      // receive ByteEvents\n      bool byteEvents{false};\n\n      // observer is notified during prewrite stage and can add WriteFlags\n      bool prewrite{false};\n\n      /**\n       * Enable all events in config.\n       */\n      virtual void enableAllEvents() {\n        byteEvents = true;\n        prewrite = true;\n      }\n\n      /**\n       * Returns a config where all events are enabled.\n       */\n      static Config getConfigAllEventsEnabled() {\n        Config config = {};\n        config.enableAllEvents();\n        return config;\n      }\n    };\n\n    /**\n     * Constructor for observer, uses default config (instrumentation disabled).\n     */\n    LegacyLifecycleObserver() : LegacyLifecycleObserver(Config()) {}\n\n    /**\n     * Constructor for observer.\n     *\n     * @param config      Config, defaults to auxilary instrumentation disabled.\n     */\n    explicit LegacyLifecycleObserver(const Config& observerConfig)\n        : observerConfig_(observerConfig) {}\n\n    ~LegacyLifecycleObserver() override = default;\n\n    /**\n     * Returns observer's configuration.\n     *\n     * @return            Observer configuration.\n     */\n    const Config& getConfig() { return observerConfig_; }\n\n    /**\n     * observerAttach() will be invoked when an observer is added.\n     *\n     * @param socket   Socket where observer was installed.\n     */\n    virtual void observerAttach(AsyncSocket* /* socket */) noexcept = 0;\n\n    /**\n     * observerDetached() will be invoked if the observer is uninstalled prior\n     * to socket destruction.\n     *\n     * No further events will be invoked after observerDetach().\n     *\n     * @param socket   Socket where observer was uninstalled.\n     */\n    virtual void observerDetach(AsyncSocket* /* socket */) noexcept = 0;\n\n    /**\n     * destroy() will be invoked when the socket's destructor is invoked.\n     *\n     * No further events will be invoked after destroy().\n     *\n     * @param socket   Socket being destroyed.\n     */\n    virtual void destroy(AsyncSocket* /* socket */) noexcept = 0;\n\n   protected:\n    // observer configuration; cannot be changed post instantiation\n    const Config observerConfig_;\n  };\n\n  /**\n   * Adds a lifecycle observer.\n   *\n   * Observers can tie their lifetime to aspects of this socket's lifecycle /\n   * lifetime and perform inspection at various states.\n   *\n   * This enables instrumentation to be added without changing / interfering\n   * with how the application uses the socket.\n   *\n   * Observer should implement AsyncSocket::LegacyLifecycleObserver to\n   * receive additional lifecycle events specific to AsyncSocket.\n   *\n   * @param observer     Observer to add (implements LegacyLifecycleObserver).\n   */\n  virtual void addLifecycleObserver(LegacyLifecycleObserver* observer);\n\n  /**\n   * Removes a lifecycle observer.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether observer found and removed from list.\n   */\n  virtual bool removeLifecycleObserver(LegacyLifecycleObserver* observer);\n\n  /**\n   * Returns installed lifecycle observers.\n   *\n   * @return             Vector with installed observers.\n   */\n  [[nodiscard]] virtual std::vector<LegacyLifecycleObserver*>\n  getLifecycleObservers() const;\n\n  /**\n   * Adds an observer.\n   *\n   * If the observer is already added, this is a no-op.\n   *\n   * @param observer     Observer to add.\n   * @return             Whether the observer was added (fails if no list).\n   */\n  virtual bool addObserver(Observer* observer) {\n    if (auto list = getAsyncSocketObserverContainer()) {\n      list->addObserver(observer);\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * Adds an observer.\n   *\n   * If the observer is already added, this is a no-op.\n   *\n   * @param observer     Observer to add.\n   * @return             Whether the observer was added (fails if no list).\n   */\n  bool addObserver(std::shared_ptr<Observer> observer) {\n    if (auto list = getAsyncSocketObserverContainer()) {\n      list->addObserver(std::move(observer));\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * Removes an observer.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether the observer was found and removed.\n   */\n  virtual bool removeObserver(Observer* observer) {\n    if (auto list = getAsyncSocketObserverContainer()) {\n      return list->removeObserver(observer);\n    }\n    return false;\n  }\n\n  /**\n   * Removes an observer.\n   *\n   * @param observer     Observer to remove.\n   * @return             Whether the observer was found and removed.\n   */\n  virtual bool removeObserver(std::shared_ptr<Observer> observer) {\n    if (auto list = getAsyncSocketObserverContainer()) {\n      return list->removeObserver(std::move(observer));\n    }\n    return false;\n  }\n\n  /**\n   * Get number of observers.\n   *\n   * @return             Number of observers.\n   */\n  [[nodiscard]] virtual size_t numObservers() {\n    if (auto list = getAsyncSocketObserverContainer()) {\n      return list->numObservers();\n    }\n    return 0;\n  }\n\n  /**\n   * Returns list of attached observers that are of type T.\n   *\n   * @return             Attached observers of type T.\n   */\n  template <typename T = Observer>\n  std::vector<T*> findObservers() {\n    if (auto list = getAsyncSocketObserverContainer()) {\n      return list->findObservers<T>();\n    }\n    return {};\n  }\n\n private:\n  /**\n   * Returns the AsyncSocketObserverContainer or nullptr if not available.\n   */\n  [[nodiscard]] AsyncSocketObserverContainer*\n  getAsyncSocketObserverContainer() {\n    return &observerContainer_;\n  }\n\n public:\n  /**\n   * Split iovec array at given byte offsets; produce a new array with result.\n   */\n  static void splitIovecArray(\n      const size_t startOffset,\n      const size_t endOffset,\n      const iovec* srcVec,\n      const size_t srcCount,\n      iovec* dstVec,\n      size_t& dstCount);\n\n  void applyOptions(\n      const SocketOptionMap& options, SocketOptionKey::ApplyPos pos);\n\n protected:\n  enum ReadResultEnum {\n    READ_EOF = 0,\n    READ_ERROR = -1,\n    READ_BLOCKING = -2,\n    READ_NO_ERROR = -3,\n    READ_ASYNC = -4,\n  };\n\n  enum WriteResultEnum {\n    WRITE_ERROR = -1,\n  };\n\n  /**\n   * Protected destructor.\n   *\n   * Users of AsyncSocket must never delete it directly.  Instead, invoke\n   * destroy() instead.  (See the documentation in DelayedDestruction.h for\n   * more details.)\n   */\n  ~AsyncSocket() override;\n\n  friend std::ostream& operator<<(std::ostream& os, const StateEnum& state);\n\n  enum ShutdownFlags {\n    /// shutdownWrite() called, but we are still waiting on writes to drain\n    SHUT_WRITE_PENDING = 0x01,\n    /// writes have been completely shut down\n    SHUT_WRITE = 0x02,\n    /**\n     * Reads have been shutdown.\n     *\n     * At the moment we don't distinguish between remote read shutdown\n     * (received EOF from the remote end) and local read shutdown.  We can\n     * only receive EOF when a read callback is set, and we immediately inform\n     * it of the EOF.  Therefore there doesn't seem to be any reason to have a\n     * separate state of \"received EOF but the local side may still want to\n     * read\".\n     *\n     * We also don't currently provide any API for only shutting down the read\n     * side of a socket.  (This is a no-op as far as TCP is concerned, anyway.)\n     */\n    SHUT_READ = 0x04,\n  };\n\n  class BytesWriteRequest;\n\n  class WriteTimeout : public AsyncTimeout {\n   public:\n    WriteTimeout(AsyncSocket* socket, EventBase* eventBase)\n        : AsyncTimeout(eventBase), socket_(socket) {}\n\n    void timeoutExpired() noexcept override { socket_->timeoutExpired(); }\n\n   private:\n    AsyncSocket* socket_;\n  };\n\n  class IoHandler : public EventHandler {\n   public:\n    IoHandler(AsyncSocket* socket, EventBase* eventBase)\n        : EventHandler(eventBase, NetworkSocket()), socket_(socket) {}\n    IoHandler(AsyncSocket* socket, EventBase* eventBase, NetworkSocket fd)\n        : EventHandler(eventBase, fd), socket_(socket) {}\n\n    void handlerReady(uint16_t events) noexcept override {\n      socket_->ioReady(events);\n    }\n\n   private:\n    AsyncSocket* socket_;\n  };\n\n  void init();\n\n  class ImmediateReadCB : public folly::EventBase::LoopCallback {\n   public:\n    explicit ImmediateReadCB(AsyncSocket* socket) : socket_(socket) {}\n    void runLoopCallback() noexcept override {\n      DestructorGuard dg(socket_);\n      socket_->checkForImmediateRead();\n    }\n\n   private:\n    AsyncSocket* socket_;\n  };\n\n  /**\n   * Schedule checkForImmediateRead to be executed in the next loop\n   * iteration.\n   */\n  void scheduleImmediateRead() noexcept {\n    if (good()) {\n      eventBase_->runInLoop(&immediateReadHandler_);\n    }\n  }\n\n  /**\n   * Schedule handleInitalReadWrite to run in the next iteration.\n   */\n  void scheduleInitialReadWrite() noexcept {\n    if (good()) {\n      DestructorGuard dg(this);\n      eventBase_->runInLoop([this, dg] {\n        if (good()) {\n          handleInitialReadWrite();\n        }\n      });\n    }\n  }\n\n  void drainErrorQueue() noexcept;\n\n  // event notification methods\n  void ioReady(uint16_t events) noexcept;\n  virtual void checkForImmediateRead() noexcept;\n  virtual void handleInitialReadWrite() noexcept;\n  virtual void prepareReadBuffer(void** buf, size_t* buflen);\n  virtual void prepareReadBuffers(IOBufIovecBuilder::IoVecVec& iovs);\n  virtual size_t handleErrMessages() noexcept;\n  virtual void handleRead() noexcept;\n  virtual void handleWrite() noexcept;\n  virtual void handleConnect() noexcept;\n  void timeoutExpired() noexcept;\n  bool hasPendingWrites() noexcept;\n\n  /**\n   * Handler for when the file descriptor is attached to the AsyncSocket.\n\n   * This updates the EventHandler to start using the fd and notifies all\n   * observers attached to the socket. This is necessary to let\n   * observers know about an attached fd immediately (i.e., on connection\n   * attempt) rather than when the connection succeeds.\n   */\n  virtual void handleNetworkSocketAttached();\n\n  /**\n   * Populate an iovec array from an IOBuf and attempt to write it.\n   *\n   * @param callback Write completion/error callback.\n   * @param vec      Target iovec array; caller retains ownership.\n   * @param count    Number of IOBufs to write, beginning at start of buf.\n   * @param buf      Chain of iovecs.\n   * @param flags    set of flags for the underlying write calls, like cork\n   */\n  void writeChainImpl(\n      WriteCallback* callback,\n      iovec* vec,\n      size_t count,\n      std::unique_ptr<folly::IOBuf>&& buf,\n      WriteFlags flags);\n\n  /**\n   * Write as much data as possible to the socket without blocking,\n   * and queue up any leftover data to send when the socket can\n   * handle writes again.\n   *\n   * @param callback    The callback to invoke when the write is completed.\n   * @param vec         Array of buffers to write; this method will make a\n   *                    copy of the vector (but not the buffers themselves)\n   *                    if the write has to be completed asynchronously.\n   * @param count       Number of elements in vec.\n   * @param buf         The IOBuf that manages the buffers referenced by\n   *                    vec, or a pointer to nullptr if the buffers are not\n   *                    associated with an IOBuf.  Note that ownership of\n   *                    the IOBuf is transferred here; upon completion of\n   *                    the write, the AsyncSocket deletes the IOBuf.\n   * @param totalBytes  The total number of bytes to be written.\n   * @param flags       Set of write flags.\n   */\n  void writeImpl(\n      WriteCallback* callback,\n      const iovec* vec,\n      size_t count,\n      std::unique_ptr<folly::IOBuf>&& buf,\n      size_t totalBytes,\n      WriteFlags flags = WriteFlags::NONE);\n\n  /**\n   * Attempt to write to the socket.\n   *\n   * @param vec             The iovec array pointing to the buffers to write.\n   * @param count           The length of the iovec array.\n   * @param flags           Set of write flags.\n   * @param countWritten    On return, the value pointed to by this parameter\n   *                          will contain the number of iovec entries that were\n   *                          fully written.\n   * @param partialWritten  On return, the value pointed to by this parameter\n   *                          will contain the number of bytes written in the\n   *                          partially written iovec entry.\n   *\n   * @return Returns a WriteResult. See WriteResult for more details.\n   */\n  virtual WriteResult performWrite(\n      const iovec* vec,\n      uint32_t count,\n      WriteFlags flags,\n      uint32_t* countWritten,\n      uint32_t* partialWritten,\n      WriteRequestTag writeTag);\n\n  /**\n   * Prepares a msghdr and sends the message over the socket using sendmsg\n   *\n   * @param vec             The iovec array pointing to the buffers to write.\n   * @param count           The length of the iovec array.\n   * @param flags           Set of write flags.\n   */\n  virtual AsyncSocket::WriteResult sendSocketMessage(\n      const iovec* vec,\n      size_t count,\n      WriteFlags flags,\n      WriteRequestTag writeTag);\n\n  /**\n   * Sends the message over the socket using sendmsg\n   *\n   * @param msg       Message to send\n   * @param msg_flags Flags to pass to sendmsg\n   */\n  virtual AsyncSocket::WriteResult sendSocketMessage(\n      NetworkSocket fd, struct msghdr* msg, int msg_flags);\n\n  virtual ssize_t tfoSendMsg(\n      NetworkSocket fd, struct msghdr* msg, int msg_flags);\n\n  int socketConnect(const struct sockaddr* addr, socklen_t len);\n\n  virtual void scheduleConnectTimeout();\n  void registerForConnectEvents();\n\n  bool updateEventRegistration();\n\n  /**\n   * Update event registration.\n   *\n   * @param enable Flags of events to enable. Set it to 0 if no events\n   * need to be enabled in this call.\n   * @param disable Flags of events\n   * to disable. Set it to 0 if no events need to be disabled in this\n   * call.\n   *\n   * @return true iff the update is successful.\n   */\n  bool updateEventRegistration(uint16_t enable, uint16_t disable);\n\n  // Attempt to read into one or more `struct iovec`s.  The caller is\n  // responsible for setting `msg.msg_iov` and `msg.msg_iovlen` to the\n  // buffers that will receive the read, and for initializing\n  // `msg.msg_name*`.  In the case that `readAncillaryCallback_` is set, the\n  // caller may also want to populate `msg_control`, `msg_controllen`, and\n  // `msg_flags` -- if no ancillary data are being read, it's fine to leave\n  // them at their defaults of 0.\n  virtual ReadResult performReadMsg(\n      struct ::msghdr& msg, AsyncReader::ReadCallback::ReadMode);\n\n  // Actually close the file descriptor and set it to -1 so we don't\n  // accidentally close it again.\n  void doClose();\n\n  // error handling methods\n  enum class ReadCode {\n    READ_NOT_SUPPORTED = 0,\n    READ_CONTINUE = 1,\n    READ_DONE = 2,\n  };\n\n  void startFail();\n  void finishFail();\n  void finishFail(const AsyncSocketException& ex);\n  void invokeAllErrors(const AsyncSocketException& ex);\n  void fail(const char* fn, const AsyncSocketException& ex);\n  void failConnect(const char* fn, const AsyncSocketException& ex);\n  ReadCode failRead(const char* fn, const AsyncSocketException& ex);\n  void failErrMessageRead(const char* fn, const AsyncSocketException& ex);\n  void failWrite(\n      const char* fn,\n      WriteCallback* callback,\n      size_t bytesWritten,\n      const AsyncSocketException& ex);\n  void failWrite(const char* fn, const AsyncSocketException& ex);\n  void failAllWrites(const AsyncSocketException& ex);\n  void failByteEvents(const AsyncSocketException& ex);\n  virtual void invokeConnectErr(const AsyncSocketException& ex);\n  virtual void invokeConnectSuccess();\n  virtual void invokeConnectAttempt();\n  void invalidState(ConnectCallback* callback);\n  void invalidState(ErrMessageCallback* callback);\n  void invalidState(ReadCallback* callback);\n  void invalidState(WriteCallback* callback);\n\n  std::string withAddr(folly::StringPiece s);\n\n  void cacheLocalAddress() const;\n  void cachePeerAddress() const;\n\n  bool isZeroCopyRequest(WriteFlags flags);\n\n  bool isZeroCopyMsg(const cmsghdr& cmsg) const;\n  void processZeroCopyMsg(const cmsghdr& cmsg);\n\n  uint32_t getNextZeroCopyBufId() { return zeroCopyBufId_++; }\n  void adjustZeroCopyFlags(folly::WriteFlags& flags);\n  void addZeroCopyBuf(\n      std::unique_ptr<folly::IOBuf>&& buf, ReleaseIOBufCallback* cb);\n  void addZeroCopyBuf(folly::IOBuf* ptr);\n  void setZeroCopyBuf(\n      std::unique_ptr<folly::IOBuf>&& buf, ReleaseIOBufCallback* cb);\n  bool containsZeroCopyBuf(folly::IOBuf* ptr);\n  void releaseZeroCopyBuf(uint32_t id);\n\n  void drainZeroCopyQueue();\n\n  void releaseIOBuf(\n      std::unique_ptr<folly::IOBuf> buf,\n      ReleaseIOBufCallback* callback) override;\n\n  ReadCode processZeroCopyRead();\n  ReadCode processNormalRead();\n  /**\n   * Attempt to enable Observer ByteEvents for this socket.\n   *\n   * Once enabled, ByteEvents rename enabled for the socket's life.\n   *\n   * ByteEvents are delivered to Observers; when an observer is added:\n   *    - If this function has already been called, byteEventsEnabled() or\n   *      byteEventsUnavailable() will be called, depending on ByteEvent state.\n   *    - Else if the socket is connected, this function is called immediately.\n   *    - Else if the socket has not yet connected, this function will be called\n   *      after the socket has connected (ByteEvents cannot be set up earlier).\n   *\n   * If ByteEvents are successfully enabled, byteEventsEnabled() will be called\n   * on each Observer that has requested ByteEvents. If unable to enable, or if\n   * ByteEvents become unavailable (e.g., due to close), byteEventsUnavailable()\n   * will be called on each Observer that has requested ByteEvents.\n   *\n   * This function does need to be explicitly called under other circumstances.\n   */\n  virtual void enableByteEvents();\n\n  /*\n   * IoUringConnectCallback\n   */\n  void connectSuccess() override;\n  void connectTimeout() override;\n\n  /*\n   * IoUringSendCallback\n   */\n  void sendPartial(size_t bytesWritten = 0) override;\n  void sendDone(size_t bytesWritten = 0) override;\n  void sendErr(int err) override;\n\n  /*\n   * IoUringRecvCallback\n   */\n  void recvSuccess(std::unique_ptr<IOBuf> data) override;\n  void recvEOF() noexcept override;\n  void recvErr(\n      int err,\n      std::unique_ptr<const AsyncSocketException> exception) noexcept override;\n\n  using ZeroCopyEnablePolicy =\n      std::variant<std::monostate, AsyncWriter::ZeroCopyEnableFunc, size_t>;\n  ZeroCopyEnablePolicy zeroCopyEnablePolicy_;\n\n  // a folly::IOBuf can be used in multiple partial requests\n  // there is a that maps a buffer id to a raw folly::IOBuf ptr\n  // and another one that adds a ref count for a folly::IOBuf that is either\n  // the original ptr or nullptr\n  uint32_t zeroCopyBufId_{0};\n\n  ZeroCopyDrainConfig zeroCopyDrainConfig_;\n\n  struct IOBufInfo {\n    uint32_t count_{0};\n    ReleaseIOBufCallback* cb_{nullptr};\n    std::unique_ptr<folly::IOBuf> buf_;\n  };\n\n  std::unordered_map<uint32_t, folly::IOBuf*> idZeroCopyBufPtrMap_;\n  std::unordered_map<folly::IOBuf*, IOBufInfo> idZeroCopyBufInfoMap_;\n\n  StateEnum state_{StateEnum::UNINIT}; ///< StateEnum describing current state\n  uint8_t shutdownFlags_{0}; ///< Shutdown state (ShutdownFlags)\n  uint16_t eventFlags_; ///< EventBase::HandlerFlags settings\n  NetworkSocket fd_; ///< The socket file descriptor\n  mutable folly::SocketAddress addr_; ///< The address we tried to connect to\n  mutable folly::SocketAddress localAddr_;\n  ///< The address we are connecting from\n  uint32_t sendTimeout_; ///< The send timeout, in milliseconds\n  uint16_t maxReadsPerEvent_; ///< Max reads per event loop iteration\n\n  int8_t readErr_{READ_NO_ERROR}; ///< The read error encountered, if any\n\n  EventBase* eventBase_; ///< The EventBase\n  WriteTimeout writeTimeout_; ///< A timeout for connect and write\n  IoHandler ioHandler_; ///< A EventHandler to monitor the fd\n  ImmediateReadCB immediateReadHandler_; ///< LoopCallback for checking read\n\n  ConnectCallback* connectCallback_; ///< ConnectCallback\n  ErrMessageCallback* errMessageCallback_; ///< TimestampCallback\n  ReadAncillaryDataCallback*\n      readAncillaryDataCallback_; ///< AncillaryDataCallback\n  SendMsgParamsCallback* ///< Callback for retrieving\n      sendMsgParamCallback_; ///< ::sendmsg() parameters\n  ReadCallback* readCallback_; ///< ReadCallback\n  WriteRequest* writeReqHead_; ///< Chain of WriteRequests\n  WriteRequest* writeReqTail_; ///< End of WriteRequest chain\n  std::weak_ptr<ShutdownSocketSet> wShutdownSocketSet_;\n  size_t appBytesReceived_; ///< Num of bytes received from socket\n  size_t appBytesWritten_{0}; ///< Num of bytes written to socket\n  size_t rawBytesWritten_{0}; ///< Num of (raw) bytes written to socket\n  // The total num of bytes passed to AsyncSocket's write functions. It doesn't\n  // include failed writes, but it does include buffered writes.\n  size_t totalAppBytesScheduledForWrite_;\n  // Num of bytes allocated in IOBufs pending write.\n  size_t allocatedBytesBuffered_{0};\n\n  // Lifecycle observers.\n  //\n  // Use small_vector to avoid heap allocation for up to two observers, unless\n  // mobile, in which case we fallback to std::vector to prioritize code size.\n  using LifecycleObserverVecImpl = conditional_t<\n      !kIsMobile,\n      folly::small_vector<LegacyLifecycleObserver*, 2>,\n      std::vector<LegacyLifecycleObserver*>>;\n  LifecycleObserverVecImpl lifecycleObservers_;\n\n  // Pre-received data, to be returned to read callback before any data from the\n  // socket.\n  std::unique_ptr<IOBuf> preReceivedData_;\n\n  // When connect() started.\n  std::chrono::steady_clock::time_point connectStartTime_;\n\n  // When connect() completed.\n  std::chrono::steady_clock::time_point connectEndTime_;\n\n  // When the connection was established.\n  //\n  //  -  If connect() was called and succeeded, this is the same as\n  //     connectEndTime_.\n  //\n  //  -  If AsyncSocket was initialized with a file descriptor (e.g., by an\n  //     acceptor), this is the connection establishment time passed to the\n  //     constructor. If no time was passed, this is folly::none.\n  folly::Optional<std::chrono::steady_clock::time_point>\n      maybeConnectionEstablishTime_;\n\n  std::chrono::milliseconds connectTimeout_{0};\n\n  std::unique_ptr<EvbChangeCallback> evbChangeCb_{nullptr};\n\n  BufferCallback* bufferCallback_{nullptr};\n\n  struct TCPFastOpenInfo {\n    bool attempted{false};\n    bool enabled{false};\n    bool finished{false};\n  };\n\n  TCPFastOpenInfo tfoInfo_;\n  bool bindAddressNoPort_{true};\n  bool noTransparentTls_{false};\n  bool noTSocks_{false};\n  // Whether to track EOR or not.\n  bool trackEor_{false};\n  Optional<int> tosOrTrafficClass_;\n\n  // ByteEvent state\n  std::unique_ptr<ByteEventHelper> byteEventHelper_;\n\n  bool zeroCopyEnabled_{false};\n  bool zeroCopyVal_{false};\n  // zerocopy re-enable logic\n  size_t zeroCopyReenableThreshold_{0};\n  size_t zeroCopyReenableCounter_{0};\n\n  // zerocopy read\n  bool zerocopyReadDisabled_{false};\n  int zerocopyReadErr_{0};\n\n  bool closeOnFailedWrite_{true};\n\n  bool useIoUring_{false};\n  IoUringConnectHandle::UniquePtr iouConnectHandle_;\n  IoUringSendHandle::UniquePtr iouSendHandle_;\n  IoUringRecvHandle::UniquePtr iouRecvHandle_;\n\n  netops::DispatcherContainer netops_;\n\n  folly::TcpInfoDispatcherContainer tcpInfoDispatcher_;\n\n  // Container of observers for the socket / transport.\n  //\n  // This member MUST be last in the list of members (other than\n  // constructorCallbackList_) to ensure it is destroyed first, before any other\n  // members are destroyed. This ensures that observers can inspect any socket /\n  // transport state available through public methods when destruction of the\n  // transport begins.\n  AsyncSocketObserverContainer observerContainer_;\n\n  // allow other functions to register for callbacks when\n  // new AsyncSocket()'s are created\n  // must be LAST member defined to ensure other members are initialized\n  // before access; see ConstructorCallbackList.h for details\n  ConstructorCallbackList<AsyncSocket> constructorCallbackList_{this};\n};\n\nstd::ostream& operator<<(\n    std::ostream& os, const folly::AsyncSocket::WriteRequestTag& tag);\n\n} // namespace folly\n\ntemplate <>\nstruct std::hash<folly::AsyncSocket::WriteRequestTag> {\n  std::size_t operator()(\n      const folly::AsyncSocket::WriteRequestTag& writeTag) const {\n    return std::hash<const folly::IOBuf*>{}(writeTag.buf_);\n  }\n};\n"
  },
  {
    "path": "folly/io/async/AsyncSocketBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/SocketAddress.h>\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\n\nclass AsyncSocketBase {\n public:\n  virtual EventBase* getEventBase() const = 0;\n  virtual ~AsyncSocketBase() = default;\n  virtual void getAddress(SocketAddress*) const = 0;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSocketException.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSocketException.h>\n\n#include <folly/Format.h>\n#include <folly/String.h>\n\nnamespace folly {\n\n/* static */ StringPiece AsyncSocketException::getExceptionTypeString(\n    AsyncSocketExceptionType type) {\n  switch (type) {\n    case UNKNOWN:\n      return \"Unknown async socket exception\";\n    case NOT_OPEN:\n      return \"Socket not open\";\n    case ALREADY_OPEN:\n      return \"Socket already open\";\n    case TIMED_OUT:\n      return \"Timed out\";\n    case END_OF_FILE:\n      return \"End of file\";\n    case INTERRUPTED:\n      return \"Interrupted\";\n    case BAD_ARGS:\n      return \"Invalid arguments\";\n    case CORRUPTED_DATA:\n      return \"Corrupted Data\";\n    case INTERNAL_ERROR:\n      return \"Internal error\";\n    case NOT_SUPPORTED:\n      return \"Not supported\";\n    case INVALID_STATE:\n      return \"Invalid state\";\n    case SSL_ERROR:\n      return \"SSL error\";\n    case COULD_NOT_BIND:\n      return \"Could not bind\";\n    case NETWORK_ERROR:\n      return \"Network error\";\n    case EARLY_DATA_REJECTED:\n      return \"Early data rejected\";\n    case CANCELED:\n      return \"IO operation was canceled\";\n    default:\n      return \"(Invalid exception type)\";\n  }\n}\n\n/* static */ std::string AsyncSocketException::getMessage(\n    AsyncSocketExceptionType type, const std::string& message, int errnoCopy) {\n  if (errnoCopy != 0) {\n    return sformat(\n        \"AsyncSocketException: {}, type = {}, errno = {} ({})\",\n        message,\n        getExceptionTypeString(type),\n        errnoCopy,\n        errnoStr(errnoCopy));\n  } else {\n    return sformat(\n        \"AsyncSocketException: {}, type = {}\",\n        message,\n        getExceptionTypeString(type));\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSocketException.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdexcept>\n#include <string>\n\n#include <folly/CPortability.h>\n#include <folly/Range.h>\n\nnamespace folly {\n\nclass FOLLY_EXPORT AsyncSocketException : public std::runtime_error {\n public:\n  enum AsyncSocketExceptionType {\n    UNKNOWN = 0,\n    NOT_OPEN = 1,\n    ALREADY_OPEN = 2,\n    TIMED_OUT = 3,\n    END_OF_FILE = 4,\n    INTERRUPTED = 5,\n    BAD_ARGS = 6,\n    CORRUPTED_DATA = 7,\n    INTERNAL_ERROR = 8,\n    NOT_SUPPORTED = 9,\n    INVALID_STATE = 10,\n    SSL_ERROR = 12,\n    COULD_NOT_BIND = 13,\n    // SASL_HANDSHAKE_TIMEOUT = 14, // no longer used\n    NETWORK_ERROR = 15,\n    EARLY_DATA_REJECTED = 16,\n    CANCELED = 17,\n  };\n\n  /// Asserts (in debug builds) the errno value to be nonnegative to help with\n  /// linux io-uring, which passes errno values as negative numbers. But permits\n  /// a special negative number which does not map to any actual errno errors.\n  AsyncSocketException(\n      AsyncSocketExceptionType type,\n      const std::string& message,\n      int errnoCopy = 0)\n      : std::runtime_error(getMessage(type, message, errnoCopy)),\n        type_(type),\n        errno_(errnoCopy) {\n    assert(errnoCopy >= 0 || errnoCopy == INT_MIN);\n  }\n\n  AsyncSocketExceptionType getType() const noexcept { return type_; }\n\n  int getErrno() const noexcept { return errno_; }\n\n protected:\n  /** get the string of exception type */\n  static folly::StringPiece getExceptionTypeString(\n      AsyncSocketExceptionType type);\n\n  /** Return a message based on the input. */\n  static std::string getMessage(\n      AsyncSocketExceptionType type, const std::string& message, int errnoCopy);\n\n  /** Error code */\n  AsyncSocketExceptionType type_;\n\n  /** A copy of the errno. */\n  int errno_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSocketTransport.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSocketTransport.h>\n\nnamespace folly {\n\nconst SocketAddress& AsyncSocketTransport::anyAddress() {\n  static const folly::Indestructible<SocketAddress> anyAddress =\n      SocketAddress(\"0.0.0.0\", 0);\n  return anyAddress;\n}\n\nint AsyncSocketTransport::getNapiId() const {\n#if defined(__linux__)\n  int id = -1;\n  socklen_t len = sizeof(id);\n  auto ret = ::getsockopt(\n      getNetworkSocket().toFd(), SOL_SOCKET, SO_INCOMING_NAPI_ID, &id, &len);\n  return ret < 0 || id < 1 ? -1 : id;\n#else\n  return -1;\n#endif\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncSocketTransport.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <variant>\n\n#include <folly/SocketAddress.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly {\n\nclass AsyncSocketTransport : public AsyncTransport {\n public:\n  using UniquePtr = std::unique_ptr<AsyncSocketTransport, Destructor>;\n\n  virtual int setNoDelay(bool noDelay) = 0;\n  virtual int setSockOpt(\n      int level, int optname, const void* optval, socklen_t optsize) = 0;\n  virtual void setPreReceivedData(std::unique_ptr<IOBuf> data) = 0;\n  virtual void cacheAddresses() = 0;\n\n  class ConnectCallback {\n   public:\n    virtual ~ConnectCallback() = default;\n\n    /**\n     * connectSuccess() will be invoked when the connection has been\n     * successfully established.\n     */\n    virtual void connectSuccess() noexcept = 0;\n\n    /**\n     * connectErr() will be invoked if the connection attempt fails.\n     *\n     * @param ex        An exception describing the error that occurred.\n     */\n    virtual void connectErr(const AsyncSocketException& ex) noexcept = 0;\n\n    /**\n     * preConnect() will be invoked just before the actual connect happens,\n     *              default is no-ops.\n     *\n     * @param fd      An underneath created socket, use for connection.\n     *\n     */\n    virtual void preConnect(NetworkSocket /*fd*/) {}\n  };\n\n  static const folly::SocketAddress& anyAddress();\n\n  /**\n   * Options for binding a socket before connecting. The two alternatives are\n   * mutually exclusive:\n   *  - SocketAddress: bind to a local address before connecting.\n   *  - NetworkSocket: use a pre-bound (or pre-created) socket fd. Ownership\n   *    of the fd is transferred to the AsyncSocketTransport.\n   */\n  using BindOptions = std::variant<folly::SocketAddress, NetworkSocket>;\n\n  virtual void connect(\n      ConnectCallback* callback,\n      const folly::SocketAddress& address,\n      int timeout = 0,\n      SocketOptionMap const& options = emptySocketOptionMap,\n      const BindOptions& bindOptions = anyAddress(),\n      const std::string& ifName = \"\") noexcept = 0;\n\n  virtual bool hangup() const = 0;\n\n  /**\n   * If a connect request is in-flight, cancels it and closes the socket\n   * immediately. Otherwise, this is a no-op.\n   *\n   * This does not invoke any connection related callbacks. Call this to\n   * prevent any connect callback while cleaning up, etc.\n   */\n  virtual void cancelConnect() = 0;\n  void setPeerCertificate(\n      std::unique_ptr<const AsyncTransportCertificate> cert) {\n    peerCertData_ = std::move(cert);\n  }\n\n  const AsyncTransportCertificate* getPeerCertificate() const override {\n    return peerCertData_.get();\n  }\n\n  void dropPeerCertificate() noexcept override { peerCertData_.reset(); }\n\n  void setSelfCertificate(\n      std::unique_ptr<const AsyncTransportCertificate> cert) {\n    selfCertData_ = std::move(cert);\n  }\n\n  void dropSelfCertificate() noexcept override { selfCertData_.reset(); }\n\n  const AsyncTransportCertificate* getSelfCertificate() const override {\n    return selfCertData_.get();\n  }\n\n  int getNapiId() const override;\n\n  virtual NetworkSocket getNetworkSocket() const = 0;\n  virtual bool getTFOSucceeded() const = 0;\n  virtual void enableTFO() = 0;\n  virtual void disableTransparentTls() {}\n\n protected:\n  ~AsyncSocketTransport() override = default;\n\n  // subclasses may cache these on first call to get\n  mutable std::unique_ptr<const AsyncTransportCertificate> peerCertData_{\n      nullptr};\n  mutable std::unique_ptr<const AsyncTransportCertificate> selfCertData_{\n      nullptr};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncTimeout.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncTimeout.h>\n\n#include <cassert>\n\n#include <glog/logging.h>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventUtil.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly {\n\nAsyncTimeout::AsyncTimeout(\n    TimeoutManager* timeoutManager, InternalEnum internal)\n    : timeoutManager_(timeoutManager) {\n  event_.eb_event_set(\n      NetworkSocket::invalid_handle_value,\n      EV_TIMEOUT,\n      &AsyncTimeout::libeventCallback,\n      this);\n  event_.eb_ev_base(nullptr);\n  if (timeoutManager) {\n    timeoutManager_->attachTimeoutManager(this, internal);\n  }\n}\n\nAsyncTimeout::AsyncTimeout(TimeoutManager* timeoutManager)\n    : AsyncTimeout(timeoutManager, TimeoutManager::InternalEnum::NORMAL) {}\n\nAsyncTimeout::AsyncTimeout(EventBase* eventBase)\n    : AsyncTimeout(eventBase, TimeoutManager::InternalEnum::NORMAL) {}\n\nAsyncTimeout::AsyncTimeout(EventBase* eventBase, InternalEnum internal)\n    : AsyncTimeout(static_cast<TimeoutManager*>(eventBase), internal) {}\n\nAsyncTimeout::AsyncTimeout()\n    : AsyncTimeout(static_cast<TimeoutManager*>(nullptr)) {}\n\nAsyncTimeout::~AsyncTimeout() {\n  cancelTimeout();\n}\n\nbool AsyncTimeout::scheduleTimeout(\n    TimeoutManager::timeout_type timeout,\n    std::shared_ptr<RequestContext>&& rctx) {\n  assert(timeoutManager_ != nullptr);\n  context_ = std::move(rctx);\n  return timeoutManager_->scheduleTimeout(this, timeout);\n}\n\nbool AsyncTimeout::scheduleTimeoutHighRes(\n    TimeoutManager::timeout_type_high_res timeout,\n    std::shared_ptr<RequestContext>&& rctx) {\n  assert(timeoutManager_ != nullptr);\n  context_ = std::move(rctx);\n  return timeoutManager_->scheduleTimeoutHighRes(this, timeout);\n}\n\nbool AsyncTimeout::scheduleTimeout(\n    uint32_t milliseconds, std::shared_ptr<RequestContext>&& rctx) {\n  return scheduleTimeout(\n      TimeoutManager::timeout_type(milliseconds), std::move(rctx));\n}\n\nvoid AsyncTimeout::cancelTimeout() {\n  if (isScheduled()) {\n    timeoutManager_->cancelTimeout(this);\n    context_.reset();\n  }\n}\n\nbool AsyncTimeout::isScheduled() const {\n  return event_.isEventRegistered();\n}\n\nvoid AsyncTimeout::attachTimeoutManager(\n    TimeoutManager* timeoutManager, InternalEnum internal) {\n  // This also implies no timeout is scheduled.\n  assert(timeoutManager_ == nullptr);\n  assert(timeoutManager->isInTimeoutManagerThread());\n  timeoutManager_ = timeoutManager;\n\n  timeoutManager_->attachTimeoutManager(this, internal);\n}\n\nvoid AsyncTimeout::attachEventBase(\n    EventBase* eventBase, InternalEnum internal) {\n  attachTimeoutManager(eventBase, internal);\n}\n\nvoid AsyncTimeout::detachTimeoutManager() {\n  // Only allow the event base to be changed if the timeout is not\n  // currently installed.\n  if (isScheduled()) {\n    // Programmer bug.  Abort the program.\n    LOG(FATAL) << \"detachEventBase() called on scheduled timeout; aborting\";\n  }\n\n  if (timeoutManager_) {\n    timeoutManager_->detachTimeoutManager(this);\n    timeoutManager_ = nullptr;\n  }\n}\n\nvoid AsyncTimeout::detachEventBase() {\n  detachTimeoutManager();\n}\n\nvoid AsyncTimeout::libeventCallback(libevent_fd_t fd, short events, void* arg) {\n  auto timeout = reinterpret_cast<AsyncTimeout*>(arg);\n  assert(fd == NetworkSocket::invalid_handle_value);\n  assert(events == EV_TIMEOUT);\n  // prevent unused variable warnings\n  (void)fd;\n  (void)events;\n\n  // double check that ev_flags gets reset when the timeout is not running\n  assert(\n      (event_ref_flags(timeout->event_.getEvent()) & ~EVLIST_INTERNAL) ==\n      EVLIST_INIT);\n\n  // this can't possibly fire if timeout->eventBase_ is nullptr\n  timeout->timeoutManager_->bumpHandlingTime();\n\n  RequestContextScopeGuard rctx(timeout->context_);\n\n  timeout->timeoutExpired();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncTimeout.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <utility>\n\n#include <folly/io/async/EventBaseBackendBase.h>\n#include <folly/io/async/Request.h>\n#include <folly/io/async/TimeoutManager.h>\n#include <folly/portability/Event.h>\n\nnamespace folly {\n\nclass EventBase;\n\n/**\n * AsyncTimeout is used to asynchronously wait for a timeout to occur.\n */\nclass AsyncTimeout {\n public:\n  using InternalEnum = TimeoutManager::InternalEnum;\n\n  /**\n   * Create a new AsyncTimeout object, driven by the specified TimeoutManager.\n   */\n  explicit AsyncTimeout(TimeoutManager* timeoutManager);\n  explicit AsyncTimeout(EventBase* eventBase);\n\n  /**\n   * Create a new internal AsyncTimeout object.\n   *\n   * Internal timeouts are like regular timeouts, but will not stop the\n   * TimeoutManager loop from exiting if the only remaining events are internal\n   * timeouts.\n   *\n   * This is useful for implementing fallback timeouts to abort the\n   * TimeoutManager loop if the other events have not been processed within a\n   * specified time period: if the event loop takes too long the timeout will\n   * fire and can stop the event loop.  However, if all other events complete,\n   * the event loop will exit even though the internal timeout is still\n   * installed.\n   */\n  AsyncTimeout(TimeoutManager* timeoutManager, InternalEnum internal);\n  AsyncTimeout(EventBase* eventBase, InternalEnum internal);\n\n  /**\n   * Create a new AsyncTimeout object, not yet assigned to a TimeoutManager.\n   *\n   * attachEventBase() must be called prior to scheduling the timeout.\n   */\n  AsyncTimeout();\n\n  AsyncTimeout(const AsyncTimeout&) = delete;\n  AsyncTimeout& operator=(const AsyncTimeout&) = delete;\n\n  /**\n   * AsyncTimeout destructor.\n   *\n   * The timeout will be automatically cancelled if it is running.\n   */\n  virtual ~AsyncTimeout();\n\n  /**\n   * timeoutExpired() is invoked when the timeout period has expired.\n   */\n  virtual void timeoutExpired() noexcept = 0;\n\n  /**\n   * Schedule the timeout to fire in the specified number of milliseconds.\n   *\n   * After the specified number of milliseconds has elapsed, timeoutExpired()\n   * will be invoked by the TimeoutManager's main loop.\n   *\n   * If the timeout is already running, it will be rescheduled with the\n   * new timeout value.\n   *\n   * @param milliseconds  The timeout duration, in milliseconds.\n   * @param rctx request context to be captured by the callback\n   *             set to empty if current context should not be saved.\n   *\n   * @return Returns true if the timeout was successfully scheduled,\n   *         and false if an error occurred.  After an error, the timeout is\n   *         always unscheduled, even if scheduleTimeout() was just\n   *         rescheduling an existing timeout.\n   */\n  bool scheduleTimeout(\n      uint32_t milliseconds,\n      std::shared_ptr<RequestContext>&& rctx = RequestContext::saveContext());\n  bool scheduleTimeout(\n      TimeoutManager::timeout_type timeout,\n      std::shared_ptr<RequestContext>&& rctx = RequestContext::saveContext());\n  bool scheduleTimeoutHighRes(\n      TimeoutManager::timeout_type_high_res timeout,\n      std::shared_ptr<RequestContext>&& rctx = RequestContext::saveContext());\n\n  /**\n   * Cancel the timeout, if it is running.\n   */\n  void cancelTimeout();\n\n  /**\n   * Returns true if the timeout is currently scheduled.\n   */\n  bool isScheduled() const;\n\n  /**\n   * Attach the timeout to a TimeoutManager.\n   *\n   * This may only be called if the timeout is not currently attached to a\n   * TimeoutManager (either by using the default constructor, or by calling\n   * detachTimeoutManager()).\n   *\n   * This method must be invoked in the TimeoutManager's thread.\n   *\n   * The internal parameter specifies if this timeout should be treated as an\n   * internal event.  TimeoutManager::loop() will return when there are no more\n   * non-internal events remaining.\n   */\n  void attachTimeoutManager(\n      TimeoutManager* timeoutManager,\n      InternalEnum internal = InternalEnum::NORMAL);\n  void attachEventBase(\n      EventBase* eventBase, InternalEnum internal = InternalEnum::NORMAL);\n\n  /**\n   * Detach the timeout from its TimeoutManager.\n   *\n   * This may only be called when the timeout is not running.\n   * Once detached, the timeout may not be scheduled again until it is\n   * re-attached to a EventBase by calling attachEventBase().\n   *\n   * This method must be called from the current TimeoutManager's thread.\n   */\n  void detachTimeoutManager();\n  void detachEventBase();\n\n  const TimeoutManager* getTimeoutManager() { return timeoutManager_; }\n\n  /**\n   * Returns the internal handle to the event\n   */\n  EventBaseBackendBase::Event* getEvent() { return &event_; }\n\n  /**\n   * Convenience function that wraps a function object as\n   * an AsyncTimeout instance and returns the wrapper.\n   *\n   * Specially useful when using lambdas as AsyncTimeout\n   * observers.\n   *\n   * Example:\n   *\n   *  void foo(TimeoutManager &manager) {\n   *    std::atomic_bool done = false;\n   *\n   *    auto observer = AsyncTimeout::make(manager, [&] {\n   *      std::cout << \"hello, world!\" << std::endl;\n   *      done = true;\n   *    });\n   *\n   *    observer->scheduleTimeout(std::chrono::seconds(5));\n   *\n   *    while (!done); // busy wait\n   *  }\n   *\n   */\n  template <typename TCallback>\n  static std::unique_ptr<AsyncTimeout> make(\n      TimeoutManager& manager, TCallback&& callback);\n\n  /**\n   * Convenience function that wraps a function object as\n   * an AsyncTimeout instance and returns the wrapper\n   * after scheduling it using the given TimeoutManager.\n   *\n   * This is equivalent to calling `make_async_timeout`\n   * followed by a `scheduleTimeout` on the resulting\n   * wrapper.\n   *\n   * Specially useful when using lambdas as AsyncTimeout\n   * observers.\n   *\n   * Example:\n   *\n   *  void foo(TimeoutManager &manager) {\n   *    std::atomic_bool done = false;\n   *\n   *    auto observer = AsyncTimeout::schedule(\n   *      std::chrono::seconds(5), manager, [&] {\n   *        std::cout << \"hello, world!\" << std::endl;\n   *        done = true;\n   *      }\n   *    );\n   *\n   *    while (!done); // busy wait\n   *  }\n   *\n   */\n  template <typename TCallback>\n  static std::unique_ptr<AsyncTimeout> schedule(\n      TimeoutManager::timeout_type timeout,\n      TimeoutManager& manager,\n      TCallback&& callback);\n\n private:\n  static void libeventCallback(libevent_fd_t fd, short events, void* arg);\n\n  EventBaseBackendBase::Event event_;\n\n  /*\n   * Store a pointer to the TimeoutManager.  We only use this\n   * for some assert() statements, to make sure that AsyncTimeout is always\n   * used from the correct thread.\n   */\n  TimeoutManager* timeoutManager_;\n\n  // Save the request context for when the timeout fires.\n  std::shared_ptr<RequestContext> context_;\n};\n\nnamespace detail {\n\n/**\n * Wraps a function object as an AsyncTimeout instance.\n */\ntemplate <typename TCallback>\nstruct async_timeout_wrapper : public AsyncTimeout {\n  template <typename UCallback>\n  async_timeout_wrapper(TimeoutManager* manager, UCallback&& callback)\n      : AsyncTimeout(manager), callback_(std::forward<UCallback>(callback)) {}\n\n  void timeoutExpired() noexcept override {\n    static_assert(\n        noexcept(std::declval<TCallback>()()),\n        \"callback must be declared noexcept, e.g.: `[]() noexcept {}`\");\n    callback_();\n  }\n\n private:\n  TCallback callback_;\n};\n\n} // namespace detail\n\ntemplate <typename TCallback>\nstd::unique_ptr<AsyncTimeout> AsyncTimeout::make(\n    TimeoutManager& manager, TCallback&& callback) {\n  return std::unique_ptr<AsyncTimeout>(\n      new detail::async_timeout_wrapper<typename std::decay<TCallback>::type>(\n          std::addressof(manager), std::forward<TCallback>(callback)));\n}\n\ntemplate <typename TCallback>\nstd::unique_ptr<AsyncTimeout> AsyncTimeout::schedule(\n    TimeoutManager::timeout_type timeout,\n    TimeoutManager& manager,\n    TCallback&& callback) {\n  auto wrapper = AsyncTimeout::make(manager, std::forward<TCallback>(callback));\n  wrapper->scheduleTimeout(timeout);\n  return wrapper;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncTransport.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <memory>\n\n#include <folly/Optional.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/IOBufIovecBuilder.h>\n#include <folly/io/async/AsyncSocketBase.h>\n#include <folly/io/async/AsyncTransportCertificate.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/WriteFlags.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/portability/SysUio.h>\n#include <folly/ssl/OpenSSLPtrTypes.h>\n\nnamespace folly {\n\nclass AsyncSocketException;\nclass EventBase;\nclass SocketAddress;\n\nclass AsyncReader {\n public:\n  class ReadCallback {\n   public:\n    enum class ReadMode : uint8_t {\n      ReadBuffer = 0,\n      ReadVec = 1,\n      ReadZC = 2,\n    };\n\n    virtual ~ReadCallback() = default;\n\n    ReadMode getReadMode() const noexcept { return readMode_; }\n\n    void setReadMode(ReadMode readMode) noexcept { readMode_ = readMode; }\n\n    /**\n     * When data becomes available, getReadBuffer()/getReadBuffers() will be\n     * invoked to get the buffer/buffers into which data should be read.\n     *\n     * These methods allows the ReadCallback to delay buffer allocation until\n     * data becomes available.  This allows applications to manage large\n     * numbers of idle connections, without having to maintain a separate read\n     * buffer for each idle connection.\n     */\n\n    /**\n     * It is possible that in some cases, getReadBuffer() may be called\n     * multiple times before readDataAvailable() is invoked.  In this case, the\n     * data will be written to the buffer returned from the most recent call to\n     * readDataAvailable().  If the previous calls to readDataAvailable()\n     * returned different buffers, the ReadCallback is responsible for ensuring\n     * that they are not leaked.\n     *\n     * If getReadBuffer() throws an exception, returns a nullptr buffer, or\n     * returns a 0 length, the ReadCallback will be uninstalled and its\n     * readError() method will be invoked.\n     *\n     * getReadBuffer() is not allowed to change the transport state before it\n     * returns.  (For example, it should never uninstall the read callback, or\n     * set a different read callback.)\n     *\n     * @param bufReturn getReadBuffer() should update *bufReturn to contain the\n     *                  address of the read buffer.  This parameter will never\n     *                  be nullptr.\n     * @param lenReturn getReadBuffer() should update *lenReturn to contain the\n     *                  maximum number of bytes that may be written to the read\n     *                  buffer.  This parameter will never be nullptr.\n     */\n    virtual void getReadBuffer(void** bufReturn, size_t* lenReturn) = 0;\n\n    /**\n     * It is possible that in some cases, getReadBuffers() may be called\n     * multiple times before readDataAvailable() is invoked.  In this case, the\n     * data will be written to the buffer returned from the most recent call to\n     * readDataAvailable().  If the previous calls to readDataAvailable()\n     * returned different buffers, the ReadCallback is responsible for ensuring\n     * that they are not leaked.\n     *\n     * If getReadBuffers() throws an exception or returns a zero length array\n     * the ReadCallback will be uninstalled and its readError() method will be\n     * invoked.\n     *\n     * getReadBuffers() is not allowed to change the transport state before it\n     * returns.  (For example, it should never uninstall the read callback, or\n     * set a different read callback.)\n     *\n     * @param iovs      getReadBuffers() will copy up to num iovec entries into\n     *                  iovs\n     */\n    virtual void getReadBuffers(IOBufIovecBuilder::IoVecVec& iovs) {\n      iovs.clear();\n    }\n\n    /**\n     * readDataAvailable() will be invoked when data has been successfully read\n     * into the buffer(s) returned by the last call to\n     * getReadBuffer()/getReadBuffers()\n     *\n     * The read callback remains installed after readDataAvailable() returns.\n     * It must be explicitly uninstalled to stop receiving read events.\n     * getReadBuffer() will be called at least once before each call to\n     * readDataAvailable().  getReadBuffer() will also be called before any\n     * call to readEOF().\n     *\n     * @param len       The number of bytes placed in the buffer.\n     */\n\n    virtual void readDataAvailable(size_t len) noexcept = 0;\n\n    class ZeroCopyMemStore {\n     public:\n      struct Entry {\n        void* data{nullptr};\n        size_t len{0}; // in use\n        size_t capacity{0}; // capacity\n        ZeroCopyMemStore* store{nullptr};\n\n        void put() {\n          DCHECK(store);\n          store->put(this);\n        }\n      };\n\n      struct EntryDeleter {\n        void operator()(Entry* entry) { entry->put(); }\n      };\n\n      using EntryPtr = std::unique_ptr<Entry, EntryDeleter>;\n\n      virtual ~ZeroCopyMemStore() = default;\n\n      virtual EntryPtr get() = 0;\n      virtual void put(Entry*) = 0;\n    };\n\n    /* the next 4 methods can be used if the  callback wants to support zerocopy\n     * RX on Linux as described in https://lwn.net/Articles/754681/ If the\n     * current kernel version does not support zerocopy RX, the callback will\n     * revert to regular recv processing\n     * In case we support zerocopy RX, the callback might be notified of buffer\n     * chains composed of mmap memory and also memory allocated via the\n     * getZeroCopyReadBuffer method\n     */\n\n    /**\n     * Return a ZeroCopyMemStore to use if the callback would like to enable\n     * zero-copy reads.  Return nullptr to disable zero-copy reads.\n     *\n     * The caller must ensure that the ZeroCopyMemStore remains valid for as\n     * long as this callback is installed and reading data, and until put()\n     * has been called for every outstanding Entry allocated with get().\n     */\n    virtual ZeroCopyMemStore* readZeroCopyEnabled() noexcept { return nullptr; }\n\n    /**\n     * Get a buffer to read data into when using zero-copy reads if some data\n     * cannot be read using a zero-copy page.\n     *\n     * When data is available, some data may be returned in zero-copy pages,\n     * followed by some amount of data in this fallback buffer.\n     */\n    virtual void getZeroCopyFallbackBuffer(\n        void** /*bufReturn*/, size_t* /*lenReturn*/) noexcept {\n      CHECK(false);\n    }\n\n    /**\n     * readZeroCopyDataAvailable() will be called when data is available from a\n     * zero-copy read.\n     *\n     * The data returned may be in two separate parts: data that was actually\n     * read using zero copy pages will be in zeroCopyData.  Additionally, some\n     * number of bytes may have been placed in the fallback buffer returned by\n     * getZeroCopyFallbackBuffer().  additionalBytes indicates the number of\n     * bytes placed in getZeroCopyFallbackBuffer().\n     */\n    virtual void readZeroCopyDataAvailable(\n        std::unique_ptr<IOBuf>&& /*zeroCopyData*/,\n        size_t /*additionalBytes*/) noexcept {\n      CHECK(false);\n    }\n\n    /**\n     * When data becomes available, isBufferMovable() will be invoked to figure\n     * out which API will be used, readBufferAvailable() or\n     * readDataAvailable(). If isBufferMovable() returns true, that means\n     * ReadCallback supports the IOBuf ownership transfer and\n     * readBufferAvailable() will be used.  Otherwise, not.\n\n     * By default, isBufferMovable() always return false. If\n     * readBufferAvailable() is implemented and to be invoked, You should\n     * overwrite isBufferMovable() and return true in the inherited class.\n     *\n     * This method allows the AsyncSocket/AsyncSSLSocket do buffer allocation by\n     * itself until data becomes available.  Compared with the pre/post buffer\n     * allocation in getReadBuffer()/readDataAvailabe(), readBufferAvailable()\n     * has two advantages.  First, this can avoid memcpy. E.g., in\n     * AsyncSSLSocket, the decrypted data was copied from the openssl internal\n     * buffer to the readbuf buffer.  With the buffer ownership transfer, the\n     * internal buffer can be directly \"moved\" to ReadCallback. Second, the\n     * memory allocation can be more precise.  The reason is\n     * AsyncSocket/AsyncSSLSocket can allocate the memory of precise size\n     * because they have more context about the available data than\n     * ReadCallback.  Think about the getReadBuffer() pre-allocate 4072 bytes\n     * buffer, but the available data is always 16KB (max OpenSSL record size).\n     */\n\n    virtual bool isBufferMovable() noexcept { return false; }\n\n    /**\n     * Suggested buffer size, allocated for read operations,\n     * if callback is movable and supports folly::IOBuf\n     */\n\n    virtual size_t maxBufferSize() const {\n      return 64 * 1024; // 64K\n    }\n\n    /**\n     * readBufferAvailable() will be invoked when data has been successfully\n     * read.\n     *\n     * Note that only either readBufferAvailable() or readDataAvailable() will\n     * be invoked according to the return value of isBufferMovable(). The timing\n     * and aftereffect of readBufferAvailable() are the same as\n     * readDataAvailable()\n     *\n     * @param readBuf The unique pointer of read buffer.\n     */\n\n    virtual void readBufferAvailable(\n        std::unique_ptr<IOBuf> /*readBuf*/) noexcept {}\n\n    /**\n     * readEOF() will be invoked when the transport is closed.\n     *\n     * The read callback will be automatically uninstalled immediately before\n     * readEOF() is invoked.\n     */\n    virtual void readEOF() noexcept = 0;\n\n    /**\n     * readError() will be invoked if an error occurs reading from the\n     * transport.\n     *\n     * The read callback will be automatically uninstalled immediately before\n     * readError() is invoked.\n     *\n     * @param ex        An exception describing the error that occurred.\n     */\n    virtual void readErr(const AsyncSocketException& ex) noexcept = 0;\n\n   protected:\n    ReadMode readMode_{ReadMode::ReadBuffer};\n  };\n\n  // Read methods that aren't part of AsyncTransport.\n  virtual void setReadCB(ReadCallback* callback) = 0;\n  virtual ReadCallback* getReadCallback() const = 0;\n  virtual std::unique_ptr<IOBuf> takePreReceivedData() { return {}; }\n\n protected:\n  virtual ~AsyncReader() = default;\n};\n\nclass AsyncWriter {\n public:\n  class ReleaseIOBufCallback {\n   public:\n    virtual ~ReleaseIOBufCallback() = default;\n\n    virtual void releaseIOBuf(std::unique_ptr<folly::IOBuf>) noexcept = 0;\n  };\n\n  class WriteCallback {\n   public:\n    virtual ~WriteCallback() = default;\n\n    /**\n     * writeStarting() will be invoked right before bytes are written to the\n     * socket.\n     *\n     * This enables the callback implementation to determine the raw (socket)\n     * byte offset for the first byte in this write's buffer. This may be\n     * different than the number of bytes written at the application layer in\n     * the case of TLS and other transformations.\n     *\n     * Intermediary transport layers should forward this signal.\n     */\n    virtual void writeStarting() noexcept {}\n\n    /**\n     * writeSuccess() will be invoked when all of the data has been\n     * successfully written.\n     *\n     * Note that this mainly signals that the buffer containing the data to\n     * write is no longer needed and may be freed or re-used.  It does not\n     * guarantee that the data has been fully transmitted to the remote\n     * endpoint.  For example, on socket-based transports, writeSuccess() only\n     * indicates that the data has been given to the kernel for eventual\n     * transmission.\n     */\n    virtual void writeSuccess() noexcept = 0;\n\n    /**\n     * writeError() will be invoked if an error occurs writing the data.\n     *\n     * @param bytesWritten      The number of bytes that were successfull\n     * @param ex                An exception describing the error that occurred.\n     */\n    virtual void writeErr(\n        size_t bytesWritten, const AsyncSocketException& ex) noexcept = 0;\n\n    virtual ReleaseIOBufCallback* getReleaseIOBufCallback() noexcept {\n      return nullptr;\n    }\n  };\n\n  /**\n   * If you supply a non-null WriteCallback, exactly one of writeSuccess()\n   * or writeErr() will be invoked when the write completes. If you supply\n   * the same WriteCallback object for multiple write() calls, it will be\n   * invoked exactly once per call. The only way to cancel outstanding\n   * write requests is to close the socket (e.g., with closeNow() or\n   * shutdownWriteNow()). When closing the socket this way, writeErr() will\n   * still be invoked once for each outstanding write operation.\n   */\n  virtual void write(\n      WriteCallback* callback,\n      const void* buf,\n      size_t bytes,\n      WriteFlags flags = WriteFlags::NONE) = 0;\n\n  /**\n   * If you supply a non-null WriteCallback, exactly one of writeSuccess()\n   * or writeErr() will be invoked when the write completes. If you supply\n   * the same WriteCallback object for multiple write() calls, it will be\n   * invoked exactly once per call. The only way to cancel outstanding\n   * write requests is to close the socket (e.g., with closeNow() or\n   * shutdownWriteNow()). When closing the socket this way, writeErr() will\n   * still be invoked once for each outstanding write operation.\n   */\n  virtual void writev(\n      WriteCallback* callback,\n      const iovec* vec,\n      size_t count,\n      WriteFlags flags = WriteFlags::NONE) = 0;\n\n  /**\n   * If you supply a non-null WriteCallback, exactly one of writeSuccess()\n   * or writeErr() will be invoked when the write completes. If you supply\n   * the same WriteCallback object for multiple write() calls, it will be\n   * invoked exactly once per call. The only way to cancel outstanding\n   * write requests is to close the socket (e.g., with closeNow() or\n   * shutdownWriteNow()). When closing the socket this way, writeErr() will\n   * still be invoked once for each outstanding write operation.\n   */\n  virtual void writeChain(\n      WriteCallback* callback,\n      std::unique_ptr<IOBuf>&& buf,\n      WriteFlags flags = WriteFlags::NONE) = 0;\n\n  /** zero copy related\n   * */\n  virtual bool setZeroCopy(bool /*enable*/) { return false; }\n\n  virtual bool getZeroCopy() const { return false; }\n\n  struct RXZerocopyParams {\n    bool enable{false};\n    size_t mapSize{0};\n  };\n\n  [[nodiscard]] virtual bool setRXZeroCopy(RXZerocopyParams /*params*/) {\n    return false;\n  }\n\n  [[nodiscard]] virtual bool getRXZeroCopy() const { return false; }\n\n  using ZeroCopyEnableFunc =\n      std::function<bool(const std::unique_ptr<folly::IOBuf>& buf)>;\n\n  virtual void setZeroCopyEnableFunc(ZeroCopyEnableFunc /*func*/) {}\n\n  virtual void setZeroCopyEnableThreshold(size_t /*threshold*/) {}\n\n protected:\n  virtual ~AsyncWriter() = default;\n};\n\n/**\n * AsyncTransport defines an asynchronous API for bidirectional streaming I/O.\n *\n * This class provides an API to for asynchronously waiting for data\n * on a streaming transport, and for asynchronously sending data.\n *\n * The APIs for reading and writing are intentionally asymmetric.  Waiting for\n * data to read is a persistent API: a callback is installed, and is notified\n * whenever new data is available.  It continues to be notified of new events\n * until it is uninstalled.\n *\n * AsyncTransport does not provide read timeout functionality, because it\n * typically cannot determine when the timeout should be active.  Generally, a\n * timeout should only be enabled when processing is blocked waiting on data\n * from the remote endpoint.  For server-side applications, the timeout should\n * not be active if the server is currently processing one or more outstanding\n * requests on this transport.  For client-side applications, the timeout\n * should not be active if there are no requests pending on the transport.\n * Additionally, if a client has multiple pending requests, it will ususally\n * want a separate timeout for each request, rather than a single read timeout.\n *\n * The write API is fairly intuitive: a user can request to send a block of\n * data, and a callback will be informed once the entire block has been\n * transferred to the kernel, or on error.  AsyncTransport does provide a send\n * timeout, since most callers want to give up if the remote end stops\n * responding and no further progress can be made sending the data.\n */\nclass AsyncTransport\n    : public DelayedDestruction,\n      public AsyncSocketBase,\n      public AsyncReader,\n      public AsyncWriter {\n public:\n  using UniquePtr = std::unique_ptr<AsyncTransport, Destructor>;\n\n  /**\n   * Close the transport.\n   *\n   * This gracefully closes the transport, waiting for all pending write\n   * requests to complete before actually closing the underlying transport.\n   *\n   * If a read callback is set, readEOF() will be called immediately.  If there\n   * are outstanding write requests, the close will be delayed until all\n   * remaining writes have completed.  No new writes may be started after\n   * close() has been called.\n   */\n  virtual void close() = 0;\n\n  /**\n   * Close the transport immediately.\n   *\n   * This closes the transport immediately, dropping any outstanding data\n   * waiting to be written.\n   *\n   * If a read callback is set, readEOF() will be called immediately.\n   * If there are outstanding write requests, these requests will be aborted\n   * and writeError() will be invoked immediately on all outstanding write\n   * callbacks.\n   */\n  virtual void closeNow() = 0;\n\n  /**\n   * Reset the transport immediately.\n   *\n   * This closes the transport immediately, sending a reset to the remote peer\n   * if possible to indicate abnormal shutdown.\n   *\n   * Note that not all subclasses implement this reset functionality: some\n   * subclasses may treat reset() the same as closeNow().  Subclasses that use\n   * TCP transports should terminate the connection with a TCP reset.\n   */\n  virtual void closeWithReset() { closeNow(); }\n\n  /**\n   * Perform a half-shutdown of the write side of the transport.\n   *\n   * The caller should not make any more calls to write() or writev() after\n   * shutdownWrite() is called.  Any future write attempts will fail\n   * immediately.\n   *\n   * Not all transport types support half-shutdown.  If the underlying\n   * transport does not support half-shutdown, it will fully shutdown both the\n   * read and write sides of the transport.  (Fully shutting down the socket is\n   * better than doing nothing at all, since the caller may rely on the\n   * shutdownWrite() call to notify the other end of the connection that no\n   * more data can be read.)\n   *\n   * If there is pending data still waiting to be written on the transport,\n   * the actual shutdown will be delayed until the pending data has been\n   * written.\n   *\n   * Note: There is no corresponding shutdownRead() equivalent.  Simply\n   * uninstall the read callback if you wish to stop reading.  (On TCP sockets\n   * at least, shutting down the read side of the socket is a no-op anyway.)\n   */\n  virtual void shutdownWrite() = 0;\n\n  /**\n   * Perform a half-shutdown of the write side of the transport.\n   *\n   * shutdownWriteNow() is identical to shutdownWrite(), except that it\n   * immediately performs the shutdown, rather than waiting for pending writes\n   * to complete.  Any pending write requests will be immediately failed when\n   * shutdownWriteNow() is called.\n   */\n  virtual void shutdownWriteNow() = 0;\n\n  /**\n   * Determine if transport is open and ready to read or write.\n   *\n   * Note that this function returns false on EOF; you must also call error()\n   * to distinguish between an EOF and an error.\n   *\n   * @return  true iff the transport is open and ready, false otherwise.\n   */\n  virtual bool good() const = 0;\n\n  /**\n   * Determine if the transport is readable or not.\n   *\n   * @return  true iff the transport is readable, false otherwise.\n   */\n  virtual bool readable() const = 0;\n\n  /**\n   * Determine if the transport is writable or not.\n   *\n   * @return  true iff the transport is writable, false otherwise.\n   */\n  virtual bool writable() const {\n    // By default return good() - leave it to implementers to override.\n    return good();\n  }\n\n  /**\n   * Determine if the there is pending data on the transport.\n   *\n   * @return  true iff the if the there is pending data, false otherwise.\n   */\n  virtual bool isPending() const { return readable(); }\n\n  /**\n   * Determine if transport is connected to the endpoint\n   *\n   * @return  false iff the transport is connected, otherwise true\n   */\n  virtual bool connecting() const = 0;\n\n  /**\n   * Determine if an error has occurred with this transport.\n   *\n   * @return  true iff an error has occurred (not EOF).\n   */\n  virtual bool error() const = 0;\n\n  /**\n   * Attach the transport to a EventBase.\n   *\n   * This may only be called if the transport is not currently attached to a\n   * EventBase (by an earlier call to detachEventBase()).\n   *\n   * This method must be invoked in the EventBase's thread.\n   */\n  virtual void attachEventBase(EventBase* eventBase) = 0;\n\n  /**\n   * Detach the transport from its EventBase.\n   *\n   * This may only be called when the transport is idle and has no reads or\n   * writes pending.  Once detached, the transport may not be used again until\n   * it is re-attached to a EventBase by calling attachEventBase().\n   *\n   * This method must be called from the current EventBase's thread.\n   */\n  virtual void detachEventBase() = 0;\n\n  /**\n   * Determine if the transport can be detached.\n   *\n   * This method must be called from the current EventBase's thread.\n   */\n  virtual bool isDetachable() const = 0;\n\n  /**\n   * Set the send timeout.\n   *\n   * If write requests do not make any progress for more than the specified\n   * number of milliseconds, fail all pending writes and close the transport.\n   *\n   * If write requests are currently pending when setSendTimeout() is called,\n   * the timeout interval is immediately restarted using the new value.\n   *\n   * @param milliseconds  The timeout duration, in milliseconds.  If 0, no\n   *                      timeout will be used.\n   */\n  virtual void setSendTimeout(uint32_t milliseconds) = 0;\n\n  /**\n   * Get the send timeout.\n   *\n   * @return Returns the current send timeout, in milliseconds.  A return value\n   *         of 0 indicates that no timeout is set.\n   */\n  virtual uint32_t getSendTimeout() const = 0;\n\n  /**\n   * Get the address of the local endpoint of this transport.\n   *\n   * This function may throw AsyncSocketException on error.\n   *\n   * @param address  The local address will be stored in the specified\n   *                 SocketAddress.\n   */\n  virtual void getLocalAddress(SocketAddress* address) const = 0;\n\n  /**\n   * Get the address of the remote endpoint to which this transport is\n   * connected.\n   *\n   * This function may throw AsyncSocketException on error.\n   *\n   * @return         Return the local address\n   */\n  SocketAddress getLocalAddress() const {\n    SocketAddress addr;\n    getLocalAddress(&addr);\n    return addr;\n  }\n\n  void getAddress(SocketAddress* address) const override {\n    getLocalAddress(address);\n  }\n\n  /**\n   * Get the address of the remote endpoint to which this transport is\n   * connected.\n   *\n   * This function may throw AsyncSocketException on error.\n   *\n   * @param address  The remote endpoint's address will be stored in the\n   *                 specified SocketAddress.\n   */\n  virtual void getPeerAddress(SocketAddress* address) const = 0;\n\n  /**\n   * Get the address of the remote endpoint to which this transport is\n   * connected.\n   *\n   * This function may throw AsyncSocketException on error.\n   *\n   * @return         Return the remote endpoint's address\n   */\n  SocketAddress getPeerAddress() const {\n    SocketAddress addr;\n    getPeerAddress(&addr);\n    return addr;\n  }\n\n  /**\n   * Get the peer certificate information if any\n   */\n  virtual const AsyncTransportCertificate* getPeerCertificate() const {\n    return nullptr;\n  }\n\n  /**\n   * Hints to transport implementations that the associated certificate is no\n   * longer required by the application. The transport implementation may\n   * choose to free up resources associated with the peer certificate.\n   *\n   * After this call, `getPeerCertificate()` may return nullptr, even if it\n   * previously returned non-null\n   */\n  virtual void dropPeerCertificate() noexcept {}\n\n  /**\n   * Hints to transport implementations that the associated certificate is no\n   * longer required by the application. The transport implementation may\n   * choose to free up resources associated with the self certificate.\n   *\n   * After this call, `getPeerCertificate()` may return nullptr, even if it\n   * previously returned non-null\n   */\n  virtual void dropSelfCertificate() noexcept {}\n\n  /**\n   * Get the certificate information of this transport, if any\n   */\n  virtual const AsyncTransportCertificate* getSelfCertificate() const {\n    return nullptr;\n  }\n\n  /**\n   * Return the application protocol being used by the underlying transport\n   * protocol. This is useful for transports which are used to tunnel other\n   * protocols.\n   */\n  virtual std::string getApplicationProtocol() const noexcept { return \"\"; }\n\n  /**\n   * Returns the name of the security protocol being used.\n   */\n  virtual std::string getSecurityProtocol() const { return \"\"; }\n\n  /*\n   * A transport may be able to produce exported keying material (ekm, per\n   * rfc5705), that can be used to bind some arbitrary data to it. This can be\n   * useful in contexts where you may want a token to only be used on the\n   * transport it was created for. If the transport is incapable of producing\n   * the ekm, this should return nullptr.\n   */\n  virtual std::unique_ptr<IOBuf> getExportedKeyingMaterial(\n      folly::StringPiece /* label */,\n      std::unique_ptr<IOBuf> /* context */,\n      uint16_t /* length */) const {\n    return nullptr;\n  }\n\n  /**\n   * @return True iff end of record tracking is enabled\n   */\n  virtual bool isEorTrackingEnabled() const = 0;\n\n  virtual void setEorTracking(bool track) = 0;\n\n  virtual size_t getAppBytesWritten() const = 0;\n  virtual size_t getRawBytesWritten() const = 0;\n  virtual size_t getAppBytesReceived() const = 0;\n  virtual size_t getRawBytesReceived() const = 0;\n\n  /**\n   * Calculates the total number of bytes that are currently buffered in the\n   * transport to be written later.\n   */\n  virtual size_t getAppBytesBuffered() const { return 0; }\n  virtual size_t getRawBytesBuffered() const { return 0; }\n  virtual size_t getAllocatedBytesBuffered() const { return 0; }\n\n  /**\n   * Callback class to signal changes in the transport's internal buffers.\n   */\n  class BufferCallback {\n   public:\n    virtual ~BufferCallback() = default;\n\n    /**\n     * onEgressBuffered() will be invoked when there's a partial write and it\n     * is necessary to buffer the remaining data.\n     */\n    virtual void onEgressBuffered() = 0;\n\n    /**\n     * onEgressBufferCleared() will be invoked when whatever was buffered is\n     * written, or when it errors out.\n     */\n    virtual void onEgressBufferCleared() = 0;\n  };\n\n  /**\n   * Callback class to signal when a transport that did not have replay\n   * protection gains replay protection. This is needed for 0-RTT security\n   * protocols.\n   */\n  class ReplaySafetyCallback {\n   public:\n    virtual ~ReplaySafetyCallback() = default;\n\n    /**\n     * Called when the transport becomes replay safe.\n     */\n    virtual void onReplaySafe() = 0;\n  };\n\n  /**\n   * False if the transport does not have replay protection, but will in the\n   * future.\n   */\n  virtual bool isReplaySafe() const { return true; }\n\n  /**\n   * Set the ReplaySafeCallback on this transport.\n   *\n   * This should only be called if isReplaySafe() returns false.\n   */\n  virtual void setReplaySafetyCallback(ReplaySafetyCallback* callback) {\n    if (callback) {\n      CHECK(false) << \"setReplaySafetyCallback() not supported\";\n    }\n  }\n\n  /**\n   * Return SO_INCOMING_NAPI_ID for this transport. For socket transports, this\n   * is associated with the NAPI instance/receive queue. For other transports,\n   * it is not defined.\n   *\n   * Returns -1 for error or invalid NAPI ID, or a positive integer for a valid\n   * NAPI ID.\n   */\n  virtual int getNapiId() const { return -1; }\n\n public:\n  /**\n   * AsyncTransports may wrap other AsyncTransport. This returns the\n   * transport that is wrapped. It returns nullptr if there is no wrapped\n   * transport.\n   */\n  virtual const AsyncTransport* getWrappedTransport() const { return nullptr; }\n\n  /**\n   * In many cases when we need to set socket properties or otherwise access the\n   * underlying transport from a wrapped transport. This method allows access to\n   * the derived classes of the underlying transport.\n   */\n  template <class T>\n  const T* getUnderlyingTransport() const {\n    const AsyncTransport* current = this;\n    while (current) {\n      auto sock = dynamic_cast<const T*>(current);\n      if (sock) {\n        return sock;\n      }\n      current = current->getWrappedTransport();\n    }\n    return nullptr;\n  }\n\n  template <class T>\n  T* getUnderlyingTransport() {\n    return const_cast<T*>(\n        static_cast<const AsyncTransport*>(this)->getUnderlyingTransport<T>());\n  }\n\n  virtual AsyncTransport::UniquePtr tryExchangeWrappedTransport(\n      AsyncTransport::UniquePtr& /* transport */) {\n    return AsyncTransport::UniquePtr{};\n  }\n\n  template <class T>\n  typename T::UniquePtr tryExchangeUnderlyingTransport(\n      AsyncTransport::UniquePtr& p) {\n    AsyncTransport const* current = getWrappedTransport();\n    AsyncTransport const* last = this;\n    while (current) {\n      if (dynamic_cast<T const*>(current)) {\n        AsyncTransport::UniquePtr ret =\n            const_cast<AsyncTransport*>(last)->tryExchangeWrappedTransport(p);\n        ret->setReadCB(nullptr);\n        DCHECK_NE(dynamic_cast<T*>(ret.get()), nullptr);\n        return typename T::UniquePtr(static_cast<T*>(ret.release()));\n      }\n      last = current;\n      current = current->getWrappedTransport();\n    }\n    return nullptr;\n  }\n\n  /**\n   * Returns a const pointer to wrapping or decorating transport of type T.\n   *\n   * If this transport object is not wrapped or decorated by a transport of type\n   * T, returns nullptr. If this transport is wrapped or decorated multiple\n   * times by such a type, returns the first occurrence.\n   */\n  template <class T>\n  const T* getWrappingTransport() const {\n    const AsyncTransport* current = this;\n    while (current) {\n      auto wrapped = dynamic_cast<const T*>(current);\n      if (wrapped) {\n        return wrapped;\n      }\n      current = current->decoratingTransport_;\n    }\n    return nullptr;\n  }\n\n  /**\n   * Returns a pointer to wrapping or decorating transport of type T.\n   *\n   * If this transport object is not wrapped or decorated by a transport of type\n   * T, returns nullptr. If this transport is wrapped or decorated multiple\n   * times by such a type, returns the first occurrence.\n   */\n  template <class T>\n  T* getWrappingTransport() {\n    return const_cast<T*>(\n        static_cast<const AsyncTransport*>(this)->getWrappingTransport<T>());\n  }\n\n protected:\n  ~AsyncTransport() override = default;\n\n private:\n  template <class T>\n  friend class DecoratedAsyncTransportWrapper;\n\n  // Transports can be wrapped through inheritence or through a decorator such\n  // as DecoratedAsyncTransportWrapper, in which case the wrapped transport is\n  // a member field of the decorating transport.\n  //\n  // When wrapped by a decorator, this field holds a pointer to the decorating\n  // transport. When not supported, this field is nullptr.\n  AsyncTransport* decoratingTransport_{nullptr};\n};\n\nusing AsyncTransportWrapper = AsyncTransport;\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncTransportCertificate.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <optional>\n#include <string>\n\nnamespace folly {\n\n/**\n * Generic interface applications may implement to convey self or peer\n * certificate related information.\n */\nclass AsyncTransportCertificate {\n public:\n  virtual ~AsyncTransportCertificate() = default;\n\n  /**\n   * Returns the identity this certificate conveys.\n   *\n   * An identity is an opaque string that may be used by the application for\n   * authentication or authorization purposes. The exact structure and\n   * semantics of the identity string are determined by concrete\n   * implementations of AsyncTransport.\n   */\n  virtual std::string getIdentity() const = 0;\n\n  /**\n   * Returns the DER representation of this certificate, if available.\n   *\n   * NOTE: Not every AsyncTransportCertificate implementation will\n   * have a DER representation. Whenever possible, applications should\n   * prefer to structure logic around the _identity_ that the\n   * certificate conveys (with `getIdentity()`), rather than\n   * certificate itself.\n   */\n  virtual std::optional<std::string> getDER() const = 0;\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncUDPServerSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Memory.h>\n#include <folly/io/IOBufQueue.h>\n#include <folly/io/async/AsyncUDPSocket.h>\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\n\n/**\n * UDP server socket\n *\n * It wraps a UDP socket waiting for packets and distributes them among\n * a set of event loops in round robin fashion.\n *\n * NOTE: At the moment it is designed to work with single packet protocols\n *       in mind. We distribute incoming packets among all the listeners in\n *       round-robin fashion. So, any protocol that expects to send/recv\n *       more than 1 packet will not work because they will end up with\n *       different event base to process.\n */\nclass AsyncUDPServerSocket\n    : private AsyncUDPSocket::ReadCallback,\n      public AsyncSocketBase {\n public:\n  class Callback {\n   public:\n    using OnDataAvailableParams =\n        AsyncUDPSocket::ReadCallback::OnDataAvailableParams;\n    /**\n     * Invoked when we start reading data from socket. It is invoked in\n     * each acceptors/listeners event base thread.\n     */\n    virtual void onListenStarted() noexcept = 0;\n\n    /**\n     * Invoked when the server socket is closed. It is invoked in each\n     * acceptors/listeners event base thread.\n     */\n    virtual void onListenStopped() noexcept = 0;\n\n    /**\n     * Invoked when the server socket is paused. It is invoked in each\n     * acceptors/listeners event base thread.\n     */\n    virtual void onListenPaused() noexcept {}\n\n    /**\n     * Invoked when the server socket is resumed. It is invoked in each\n     * acceptors/listeners event base thread.\n     */\n    virtual void onListenResumed() noexcept {}\n\n    /**\n     * Invoked when the server socket can still read but need to inform the\n     * callback object that it should not process read from new client address.\n     * It is invoked in each acceptors/listeners event base thread.\n     */\n    virtual void onAcceptNewPeerPaused() noexcept {}\n\n    /**\n     * Invoked when need to inform the callback object that it can resume\n     * process read from new client address. It is invoked in each\n     * acceptors/listeners event base thread.\n     */\n    virtual void onAcceptNewPeerResumed() noexcept {}\n\n    /**\n     * Invoked when a new packet is received\n     */\n    virtual void onDataAvailable(\n        std::shared_ptr<AsyncUDPSocket> socket,\n        const folly::SocketAddress& addr,\n        std::unique_ptr<folly::IOBuf> buf,\n        bool truncated,\n        OnDataAvailableParams) noexcept = 0;\n\n    virtual ~Callback() = default;\n  };\n\n  enum class DispatchMechanism { RoundRobin, ClientAddressHash };\n\n  /**\n   * Create a new UDP server socket\n   *\n   * Note about packet size - We allocate buffer of packetSize_ size to read.\n   * If packet are larger than this value, as per UDP protocol, remaining data\n   * is dropped and you get `truncated = true` in onDataAvailable callback\n   */\n  explicit AsyncUDPServerSocket(\n      EventBase* evb,\n      size_t sz = 1500,\n      DispatchMechanism dm = DispatchMechanism::RoundRobin)\n      : evb_(evb), packetSize_(sz), dispatchMechanism_(dm), nextListener_(0) {}\n\n  ~AsyncUDPServerSocket() override {\n    if (socket_) {\n      close();\n    }\n  }\n\n  void bind(\n      const folly::SocketAddress& addy,\n      const SocketOptionMap& options = emptySocketOptionMap,\n      const std::string& ifName = \"\") {\n    CHECK(!socket_);\n\n    socket_ = std::make_shared<AsyncUDPSocket>(evb_);\n    socket_->setReusePort(reusePort_);\n    socket_->setReuseAddr(reuseAddr_);\n    socket_->setRecvTos(recvTos_);\n    socket_->applyOptions(\n        validateSocketOptions(\n            options, addy.getFamily(), SocketOptionKey::ApplyPos::PRE_BIND),\n        SocketOptionKey::ApplyPos::PRE_BIND);\n    AsyncUDPSocket::BindOptions bindOptions;\n    bindOptions.ifName = ifName;\n    socket_->bind(addy, bindOptions);\n    socket_->applyOptions(\n        validateSocketOptions(\n            options, addy.getFamily(), SocketOptionKey::ApplyPos::POST_BIND),\n        SocketOptionKey::ApplyPos::POST_BIND);\n  }\n\n  void setReusePort(bool reusePort) { reusePort_ = reusePort; }\n\n  void setReuseAddr(bool reuseAddr) { reuseAddr_ = reuseAddr; }\n\n  void setRecvTos(bool recvTos) { recvTos_ = recvTos; }\n\n  void setTosOrTrafficClass(uint8_t tosOrTclass) {\n    CHECK(socket_);\n    socket_->setTosOrTrafficClass(tosOrTclass);\n  }\n\n  folly::SocketAddress address() const {\n    CHECK(socket_);\n    return socket_->address();\n  }\n\n  void getAddress(SocketAddress* a) const override { *a = address(); }\n\n  /**\n   * Add a listener to the round robin list\n   */\n  void addListener(EventBase* evb, Callback* callback) {\n    listeners_.emplace_back(evb, callback);\n  }\n\n  void listen() {\n    CHECK(socket_) << \"Need to bind before listening\";\n\n    for (auto& listener : listeners_) {\n      auto callback = listener.second;\n\n      listener.first->runInEventBaseThread([callback]() mutable {\n        callback->onListenStarted();\n      });\n    }\n\n    socket_->resumeRead(this);\n  }\n\n  NetworkSocket getNetworkSocket() const {\n    CHECK(socket_) << \"Need to bind before getting Network Socket\";\n    return socket_->getNetworkSocket();\n  }\n\n  const std::shared_ptr<AsyncUDPSocket>& getSocket() const { return socket_; }\n\n  void close() {\n    CHECK(socket_) << \"Need to bind before closing\";\n    socket_->close();\n    socket_.reset();\n  }\n\n  EventBase* getEventBase() const override { return evb_; }\n\n  /**\n   * Indicates if the current socket is accepting.\n   */\n  bool isAccepting() const { return socket_->isReading(); }\n\n  /**\n   * Pauses accepting datagrams on the underlying socket.\n   */\n  void pauseAccepting() {\n    socket_->pauseRead();\n    for (auto& listener : listeners_) {\n      auto callback = listener.second;\n\n      listener.first->runInEventBaseThread([callback]() mutable {\n        callback->onListenPaused();\n      });\n    }\n  }\n\n  /**\n   * Inform the callback object that it should not process read from new client\n   * address.\n   */\n  void pauseAcceptingNewPeer() {\n    for (auto& listener : listeners_) {\n      auto callback = listener.second;\n\n      listener.first->runInEventBaseThread([callback]() mutable {\n        callback->onAcceptNewPeerPaused();\n      });\n    }\n  }\n\n  /**\n   * Starts accepting datagrams once again.\n   */\n  void resumeAccepting() {\n    socket_->resumeRead(this);\n    for (auto& listener : listeners_) {\n      auto callback = listener.second;\n\n      listener.first->runInEventBaseThread([callback]() mutable {\n        callback->onListenResumed();\n      });\n    }\n  }\n\n  /**\n   * Inform the callback object that it can process read from new client address\n   * now.\n   */\n  void resumeAcceptingNewPeer() {\n    for (auto& listener : listeners_) {\n      auto callback = listener.second;\n\n      listener.first->runInEventBaseThread([callback]() mutable {\n        callback->onAcceptNewPeerResumed();\n      });\n    }\n  }\n\n  bool setTimestamping(int val) { return socket_->setTimestamping(val); }\n\n private:\n  // AsyncUDPSocket::ReadCallback\n  void getReadBuffer(void** buf, size_t* len) noexcept override {\n    std::tie(*buf, *len) = buf_.preallocate(packetSize_, packetSize_);\n  }\n\n  void onDataAvailable(\n      const folly::SocketAddress& clientAddress,\n      size_t len,\n      bool truncated,\n      OnDataAvailableParams params) noexcept override {\n    buf_.postallocate(len);\n    auto data = buf_.split(len);\n\n    if (listeners_.empty()) {\n      LOG(WARNING) << \"UDP server socket dropping packet, \"\n                   << \"no listener registered\";\n      return;\n    }\n\n    uint32_t listenerId = 0;\n    uint64_t client_hash_lo = 0;\n    switch (dispatchMechanism_) {\n      case DispatchMechanism::ClientAddressHash:\n        // Hash base on clientAddress.\n        // 1. This logic is samilar to: clientAddress.hash() % listeners_.size()\n        //    But runs faster as it use multiply and shift instead of division.\n        // 2. Only use the lower 32 bit from the address hash result for faster\n        //    computation.\n        client_hash_lo = static_cast<uint32_t>(clientAddress.hash());\n        listenerId = (client_hash_lo * listeners_.size()) >> 32;\n        break;\n      case DispatchMechanism::RoundRobin: // round robin is default.\n      default:\n        if (nextListener_ >= listeners_.size()) {\n          nextListener_ = 0;\n        }\n        listenerId = nextListener_;\n        ++nextListener_;\n        break;\n    }\n\n    auto callback = listeners_[listenerId].second;\n\n    // Schedule it in the listener's eventbase\n    // XXX: Speed this up\n    auto f =\n        [socket = socket_,\n         client = clientAddress,\n         callback,\n         data_2 = std::move(data),\n         truncated,\n         params]() mutable {\n          callback->onDataAvailable(\n              socket, client, std::move(data_2), truncated, params);\n        };\n\n    listeners_[listenerId].first->runInEventBaseThread(std::move(f));\n  }\n\n  void onReadError(const AsyncSocketException& ex) noexcept override {\n    LOG(ERROR) << ex.what();\n\n    // Lets register to continue listening for packets\n    socket_->resumeRead(this);\n  }\n\n  void onReadClosed() noexcept override {\n    for (auto& listener : listeners_) {\n      auto callback = listener.second;\n\n      listener.first->runInEventBaseThread([callback]() mutable {\n        callback->onListenStopped();\n      });\n    }\n  }\n\n  EventBase* const evb_;\n  const size_t packetSize_;\n\n  std::shared_ptr<AsyncUDPSocket> socket_;\n\n  // List of listener to distribute packets among\n  using Listener = std::pair<EventBase*, Callback*>;\n  std::vector<Listener> listeners_;\n\n  DispatchMechanism dispatchMechanism_;\n\n  // Next listener to send packet to\n  uint32_t nextListener_;\n\n  // Temporary buffer for data\n  folly::IOBufQueue buf_;\n\n  bool reusePort_{false};\n  bool reuseAddr_{false};\n  bool recvTos_{false};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncUDPSocket.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncUDPSocket.h>\n\n#include <cerrno>\n\n#include <boost/preprocessor/control/if.hpp>\n\n#include <folly/Likely.h>\n#include <folly/Utility.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Unistd.h>\n#include <folly/small_vector.h>\n\n// Due to the way kernel headers are included, this may or may not be defined.\n// Number pulled from 3.10 kernel headers.\n#ifndef SO_REUSEPORT\n#define SO_REUSEPORT 15\n#endif\n\n#if FOLLY_HAVE_VLA\n#define FOLLY_HAVE_VLA_01 1\n#else\n#define FOLLY_HAVE_VLA_01 0\n#endif\n\n// xplat UDP GSO socket options.\n#ifdef _WIN32\n#ifndef UDP_SEND_MSG_SIZE\n#define UDP_SEND_MSG_SIZE 2\n#endif\n#define UDP_GSO_SOCK_OPT_LEVEL IPPROTO_UDP\n#define UDP_GSO_SOCK_OPT_TYPE UDP_SEND_MSG_SIZE\n#define GSO_OPT_TYPE DWORD\n#else /* !_WIN32 */\n#define UDP_GSO_SOCK_OPT_LEVEL SOL_UDP\n#define UDP_GSO_SOCK_OPT_TYPE UDP_SEGMENT\n#define GSO_OPT_TYPE uint16_t\n#endif /* _WIN32 */\n\nnamespace fsp = folly::portability::sockets;\n\nnamespace folly {\n\nvoid AsyncUDPSocket::fromMsg(\n    [[maybe_unused]] ReadCallback::OnDataAvailableParams& params,\n    [[maybe_unused]] struct msghdr& msg) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  struct cmsghdr* cmsg;\n  uint16_t* grosizeptr;\n  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;\n       cmsg = CMSG_NXTHDR(&msg, cmsg)) {\n    if (cmsg->cmsg_level == SOL_UDP) {\n      if (cmsg->cmsg_type == UDP_GRO) {\n        grosizeptr = (uint16_t*)CMSG_DATA(cmsg);\n        params.gro = *grosizeptr;\n      }\n    } else if (cmsg->cmsg_level == SOL_SOCKET) {\n      if (cmsg->cmsg_type == SO_TIMESTAMPING ||\n          cmsg->cmsg_type == SO_TIMESTAMPNS) {\n        ReadCallback::OnDataAvailableParams::Timestamp ts;\n        memcpy(\n            &ts,\n            reinterpret_cast<struct timespec*>(CMSG_DATA(cmsg)),\n            sizeof(ts));\n        params.ts = ts;\n      }\n    } else if (\n        (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TOS) ||\n        (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_TCLASS)) {\n      params.tos = *(uint8_t*)CMSG_DATA(cmsg);\n    }\n  }\n#endif\n}\nstatic constexpr bool msgErrQueueSupported =\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    true;\n#else\n    false;\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n\nAsyncUDPSocket::AsyncUDPSocket(EventBase* evb)\n    : EventHandler(evb), readCallback_(nullptr), eventBase_(evb), fd_() {\n  if (eventBase_) {\n    eventBase_->dcheckIsInEventBaseThread();\n  }\n}\n\nAsyncUDPSocket::~AsyncUDPSocket() {\n  if (fd_ != NetworkSocket()) {\n    close();\n  }\n}\n\nvoid AsyncUDPSocket::init(sa_family_t family, BindOptions bindOptions) {\n  if (fd_ != NetworkSocket()) {\n    // Already initialized.\n    return;\n  }\n\n  NetworkSocket socket =\n      netops::socket(family, SOCK_DGRAM, family != AF_UNIX ? IPPROTO_UDP : 0);\n  if (socket == NetworkSocket()) {\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_OPEN,\n        \"error creating async udp socket\",\n        errno);\n  }\n\n  auto g = folly::makeGuard([&] { netops::close(socket); });\n\n  // put the socket in non-blocking mode\n  int ret = netops::set_socket_non_blocking(socket);\n  if (ret != 0) {\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_OPEN,\n        \"failed to put socket in non-blocking mode\",\n        errno);\n  }\n\n  if (reuseAddr_) {\n    // put the socket in reuse mode\n    int value = 1;\n    if (netops::setsockopt(\n            socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to put socket in reuse mode\",\n          errno);\n    }\n  }\n\n  if (reusePort_) {\n    // put the socket in port reuse mode\n    int value = 1;\n    auto opt = SO_REUSEPORT;\n#ifdef _WIN32\n    opt = SO_BROADCAST;\n#endif\n    if (netops::setsockopt(socket, SOL_SOCKET, opt, &value, sizeof(value)) !=\n        0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to put socket in reuse_port mode\",\n          errno);\n    }\n  }\n\n  if (freeBind_) {\n    int optname = 0;\n#if defined(IP_FREEBIND)\n    optname = IP_FREEBIND;\n#endif\n    if (!optname) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN, \"IP_FREEBIND is not supported\");\n    }\n    // put the socket in free bind mode\n    int value = 1;\n    if (netops::setsockopt(\n            socket, IPPROTO_IP, optname, &value, sizeof(value)) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to put socket in free bind mode\",\n          errno);\n    }\n  }\n\n  if (transparent_) {\n    int optname = 0;\n#if defined(IP_TRANSPARENT)\n    optname = IP_TRANSPARENT;\n#endif\n    if (!optname) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN, \"IP_TRANSPARENT is not supported\");\n    }\n    // set the socket IP transparent mode\n    int value = 1;\n    if (netops::setsockopt(\n            socket, IPPROTO_IP, optname, &value, sizeof(value)) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to set socket IP transparent mode\",\n          errno);\n    }\n  }\n\n  if (busyPollUs_ > 0) {\n    int optname = 0;\n#if defined(SO_BUSY_POLL)\n    optname = SO_BUSY_POLL;\n#endif\n    if (!optname) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN, \"SO_BUSY_POLL is not supported\");\n    }\n    // Set busy_poll time in microseconds on the socket.\n    // It sets how long socket will be in busy_poll mode when no event occurs.\n    int value = busyPollUs_;\n    if (netops::setsockopt(\n            socket, SOL_SOCKET, optname, &value, sizeof(value)) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to set SO_BUSY_POLL on the socket\",\n          errno);\n    }\n  }\n\n  if (rcvBuf_ > 0) {\n    // Set the size of the buffer for the received messages in rx_queues.\n    int value = rcvBuf_;\n    if (netops::setsockopt(\n            socket, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to set SO_RCVBUF on the socket\",\n          errno);\n    }\n  }\n\n  if (sndBuf_ > 0) {\n    // Set the size of the buffer for the sent messages in tx_queues.\n    int value = sndBuf_;\n    if (netops::setsockopt(\n            socket, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to set SO_SNDBUF on the socket\",\n          errno);\n    }\n  }\n\n  if (recvTos_) {\n    // Set socket option to receive IPv6 Traffic Class/IPv4 Type of Service.\n    int flag = 1;\n    if (family == AF_INET6) {\n      if (netops::setsockopt(\n              socket, IPPROTO_IPV6, IPV6_RECVTCLASS, &flag, sizeof(flag)) !=\n          0) {\n        throw AsyncSocketException(\n            AsyncSocketException::NOT_OPEN,\n            \"failed to set IPV6_RECVTCLASS on the socket\",\n            errno);\n      }\n    } else if (family == AF_INET) {\n      if (netops::setsockopt(\n              socket, IPPROTO_IP, IP_RECVTOS, &flag, sizeof(flag)) != 0) {\n        throw AsyncSocketException(\n            AsyncSocketException::NOT_OPEN,\n            \"failed to set IP_RECVTOS on the socket\",\n            errno);\n      }\n    }\n  }\n\n  if (family == AF_INET6) {\n    int flag = static_cast<int>(bindOptions.bindV6Only);\n    if (netops::setsockopt(\n            socket, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag))) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN, \"Failed to set IPV6_V6ONLY\", errno);\n    }\n  }\n\n  // success\n  g.dismiss();\n  fd_ = socket;\n  ownership_ = FDOwnership::OWNS;\n\n  // attach to EventHandler\n  EventHandler::changeHandlerFD(fd_);\n}\n\nvoid AsyncUDPSocket::bind(\n    const folly::SocketAddress& address, BindOptions bindOptions) {\n  if (fd_ == NetworkSocket()) {\n    init(address.getFamily(), bindOptions);\n  }\n\n  {\n    // bind the socket to the interface\n    int optname = 0;\n#if defined(SO_BINDTODEVICE)\n    optname = SO_BINDTODEVICE;\n#endif\n    auto& ifName = bindOptions.ifName;\n    if (optname && !ifName.empty() &&\n        netops::setsockopt(\n            fd_, SOL_SOCKET, optname, ifName.data(), ifName.length())) {\n      auto errnoCopy = errno;\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"failed to bind to device: \" + ifName,\n          errnoCopy);\n    }\n  }\n\n  // bind to the address\n  sockaddr_storage addrStorage;\n  address.getAddress(&addrStorage);\n  auto& saddr = reinterpret_cast<sockaddr&>(addrStorage);\n  if (netops::bind(fd_, &saddr, address.getActualSize()) != 0) {\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_OPEN,\n        \"failed to bind the async udp socket for:\" + address.describe(),\n        errno);\n  }\n\n  if (address.getFamily() == AF_UNIX || address.getPort() != 0) {\n    localAddress_ = address;\n  } else {\n    localAddress_.setFromLocalAddress(fd_);\n  }\n}\n\nvoid AsyncUDPSocket::connect(const folly::SocketAddress& address) {\n  // not bound yet\n  if (fd_ == NetworkSocket()) {\n    init(address.getFamily());\n  }\n\n  sockaddr_storage addrStorage;\n  address.getAddress(&addrStorage);\n  if (netops::connect(\n          fd_,\n          reinterpret_cast<sockaddr*>(&addrStorage),\n          address.getActualSize()) != 0) {\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_OPEN,\n        \"Failed to connect the udp socket to:\" + address.describe(),\n        errno);\n  }\n  connected_ = true;\n  connectedAddress_ = address;\n\n  localAddress_.setFromLocalAddress(fd_);\n}\n\nvoid AsyncUDPSocket::dontFragment(bool df) {\n  int optname4 = 0;\n  int optval4 = df ? 0 : 0;\n  int optname6 = 0;\n  int optval6 = df ? 0 : 0;\n#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) && \\\n    defined(IP_PMTUDISC_WANT)\n  optname4 = IP_MTU_DISCOVER;\n  optval4 = df ? IP_PMTUDISC_DO : IP_PMTUDISC_WANT;\n#endif\n#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) && \\\n    defined(IPV6_PMTUDISC_WANT)\n  optname6 = IPV6_MTU_DISCOVER;\n  optval6 = df ? IPV6_PMTUDISC_DO : IPV6_PMTUDISC_WANT;\n#endif\n  if (optname4 && optval4 && address().getFamily() == AF_INET) {\n    if (netops::setsockopt(\n            fd_, IPPROTO_IP, optname4, &optval4, sizeof(optval4))) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"Failed to set DF with IP_MTU_DISCOVER\",\n          errno);\n    }\n  }\n  if (optname6 && optval6 && address().getFamily() == AF_INET6) {\n    if (netops::setsockopt(\n            fd_, IPPROTO_IPV6, optname6, &optval6, sizeof(optval6))) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"Failed to set DF with IPV6_MTU_DISCOVER\",\n          errno);\n    }\n  }\n}\n\nvoid AsyncUDPSocket::setDFAndTurnOffPMTU() {\n  int optname4 = 0;\n  int optval4 = 0;\n  int optname6 = 0;\n  int optval6 = 0;\n#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_PROBE)\n  optname4 = IP_MTU_DISCOVER;\n  optval4 = IP_PMTUDISC_PROBE;\n#endif\n#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_PROBE)\n  optname6 = IPV6_MTU_DISCOVER;\n  optval6 = IPV6_PMTUDISC_PROBE;\n#endif\n#if defined(_WIN32) && defined(IP_DONTFRAGMENT) && defined(IPV6_DONTFRAG)\n  optname4 = IP_DONTFRAGMENT;\n  optval4 = TRUE;\n  optname6 = IPV6_DONTFRAG;\n  optval6 = TRUE;\n#endif\n  if (optname4 && optval4 && address().getFamily() == AF_INET) {\n    if (folly::netops::setsockopt(\n            fd_, IPPROTO_IP, optname4, &optval4, sizeof(optval4))) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"Failed to turn off fragmentation and PMTU discovery (IPv4)\",\n          errno);\n    }\n  }\n  if (optname6 && optval6 && address().getFamily() == AF_INET6) {\n    if (folly::netops::setsockopt(\n            fd_, IPPROTO_IPV6, optname6, &optval6, sizeof(optval6))) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN,\n          \"Failed to turn off fragmentation and PMTU discovery (IPv6)\",\n          errno);\n    }\n  }\n}\n\nvoid AsyncUDPSocket::setErrMessageCallback(\n    ErrMessageCallback* errMessageCallback) {\n  int optname4 = 0;\n  int optname6 = 0;\n#if defined(IP_RECVERR)\n  optname4 = IP_RECVERR;\n#endif\n#if defined(IPV6_RECVERR)\n  optname6 = IPV6_RECVERR;\n#endif\n  errMessageCallback_ = errMessageCallback;\n  int err = (errMessageCallback_ != nullptr);\n  if (optname4 && address().getFamily() == AF_INET &&\n      netops::setsockopt(fd_, IPPROTO_IP, optname4, &err, sizeof(err))) {\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_OPEN, \"Failed to set IP_RECVERR\", errno);\n  }\n  if (optname6 && address().getFamily() == AF_INET6 &&\n      netops::setsockopt(fd_, IPPROTO_IPV6, optname6, &err, sizeof(err))) {\n    throw AsyncSocketException(\n        AsyncSocketException::NOT_OPEN, \"Failed to set IPV6_RECVERR\", errno);\n  }\n}\n\nvoid AsyncUDPSocket::setFD(NetworkSocket fd, FDOwnership ownership) {\n  CHECK_EQ(NetworkSocket(), fd_) << \"Already bound to another FD\";\n\n  fd_ = fd;\n  ownership_ = ownership;\n\n  EventHandler::changeHandlerFD(fd_);\n  localAddress_.setFromLocalAddress(fd_);\n}\n\nbool AsyncUDPSocket::setZeroCopy(bool enable) {\n  if (msgErrQueueSupported) {\n    zeroCopyVal_ = enable;\n\n    if (fd_ == NetworkSocket()) {\n      return false;\n    }\n\n    int val = enable ? 1 : 0;\n    int ret =\n        netops::setsockopt(fd_, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val));\n\n    // if enable == false, set zeroCopyEnabled_ = false regardless\n    // if SO_ZEROCOPY is set or not\n    if (!enable) {\n      zeroCopyEnabled_ = enable;\n      return true;\n    }\n\n    /* if the setsockopt failed, try to see if the socket inherited the flag\n     * since we cannot set SO_ZEROCOPY on a socket s = accept\n     */\n    if (ret) {\n      val = 0;\n      socklen_t optlen = sizeof(val);\n      ret = netops::getsockopt(fd_, SOL_SOCKET, SO_ZEROCOPY, &val, &optlen);\n\n      if (!ret) {\n        enable = val != 0;\n      }\n    }\n\n    if (!ret) {\n      zeroCopyEnabled_ = enable;\n\n      return true;\n    }\n  }\n\n  return false;\n}\n\nssize_t AsyncUDPSocket::writeGSO(\n    const folly::SocketAddress& address,\n    const std::unique_ptr<folly::IOBuf>& buf,\n    WriteOptions options) {\n  // UDP's typical MTU size is 1500, so high number of buffers\n  //   really do not make sense. Optimize for buffer chains with\n  //   buffers less than 16, which is the highest I can think of\n  //   for a real use case.\n  iovec vec[16];\n  size_t iovec_len = buf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs;\n  if (FOLLY_UNLIKELY(iovec_len == 0)) {\n    buf->coalesce();\n    vec[0].iov_base = const_cast<uint8_t*>(buf->data());\n    vec[0].iov_len = buf->length();\n    iovec_len = 1;\n  }\n\n  return writev(address, vec, iovec_len, options);\n}\n\nint AsyncUDPSocket::getZeroCopyFlags() {\n  if (!zeroCopyEnabled_) {\n    // if the zeroCopyReenableCounter_ is > 0\n    // we try to dec and if we reach 0\n    // we set zeroCopyEnabled_ to true\n    if (zeroCopyReenableCounter_) {\n      if (0 == --zeroCopyReenableCounter_) {\n        zeroCopyEnabled_ = true;\n        return MSG_ZEROCOPY;\n      }\n    }\n\n    return 0;\n  }\n\n  return MSG_ZEROCOPY;\n}\n\nvoid AsyncUDPSocket::addZeroCopyBuf(std::unique_ptr<folly::IOBuf>&& buf) {\n  uint32_t id = getNextZeroCopyBufId();\n\n  idZeroCopyBufMap_[id] = std::move(buf);\n}\n\nssize_t AsyncUDPSocket::writeChain(\n    const folly::SocketAddress& address,\n    std::unique_ptr<folly::IOBuf>&& buf,\n    WriteOptions options) {\n  CHECK(nontrivialCmsgs_.empty()) << \"Nontrivial options are not supported\";\n  int msg_flags = options.zerocopy ? getZeroCopyFlags() : 0;\n  iovec vec[16];\n  size_t iovec_len = buf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs;\n  if (FOLLY_UNLIKELY(iovec_len == 0)) {\n    buf->coalesce();\n    vec[0].iov_base = const_cast<uint8_t*>(buf->data());\n    vec[0].iov_len = buf->length();\n    iovec_len = 1;\n  }\n  CHECK_NE(NetworkSocket(), fd_) << \"Socket not yet bound\";\n  sockaddr_storage addrStorage;\n  address.getAddress(&addrStorage);\n\n  struct msghdr msg;\n  if (!connected_) {\n    msg.msg_name = reinterpret_cast<void*>(&addrStorage);\n    msg.msg_namelen = address.getActualSize();\n  } else {\n    if (connectedAddress_ != address) {\n      errno = ENOTSUP;\n      return -1;\n    }\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n  }\n  msg.msg_iov = const_cast<struct iovec*>(vec);\n  msg.msg_iovlen = iovec_len;\n  msg.msg_control = nullptr;\n  msg.msg_controllen = 0;\n  msg.msg_flags = 0;\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  char control\n      [CMSG_SPACE(sizeof(uint16_t)) + /*gso*/\n       CMSG_SPACE(sizeof(uint64_t)) /*txtime*/\n  ] = {};\n  msg.msg_control = control;\n  struct cmsghdr* cm = nullptr;\n  if (options.gso > 0) {\n    msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));\n    cm = CMSG_FIRSTHDR(&msg);\n\n    cm->cmsg_level = SOL_UDP;\n    cm->cmsg_type = UDP_SEGMENT;\n    cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));\n    auto gso_len = static_cast<uint16_t>(options.gso);\n    memcpy(CMSG_DATA(cm), &gso_len, sizeof(gso_len));\n  }\n\n  if (options.txTime.count() > 0 && txTime_.has_value() &&\n      (txTime_.value().clockid >= 0)) {\n    msg.msg_controllen += CMSG_SPACE(sizeof(uint64_t));\n    if (cm) {\n      cm = CMSG_NXTHDR(&msg, cm);\n    } else {\n      cm = CMSG_FIRSTHDR(&msg);\n    }\n    cm->cmsg_level = SOL_SOCKET;\n    cm->cmsg_type = SCM_TXTIME;\n    cm->cmsg_len = CMSG_LEN(sizeof(uint64_t));\n\n    struct timespec ts;\n    clock_gettime(txTime_.value().clockid, &ts);\n    uint64_t txtime = ts.tv_sec * 1000000000ULL + ts.tv_nsec +\n        std::chrono::nanoseconds(options.txTime).count();\n    memcpy(CMSG_DATA(cm), &txtime, sizeof(txtime));\n  }\n#else\n  CHECK_LT(options.gso, 1) << \"GSO not supported\";\n  CHECK_LT(options.txTime.count(), 1) << \"TX_TIME not supported\";\n#endif\n\n  auto ret = sendmsg(fd_, &msg, msg_flags);\n  if (msg_flags) {\n    if (ret < 0) {\n      if (errno == ENOBUFS) {\n        LOG(INFO) << \"ENOBUFS...\";\n        // workaround for running with zerocopy enabled but without a big enough\n        // memlock value - see ulimit -l\n        // Also see /proc/sys/net/core/optmem_max\n        zeroCopyEnabled_ = false;\n        zeroCopyReenableCounter_ = zeroCopyReenableThreshold_;\n\n        ret = sendmsg(fd_, &msg, 0);\n      }\n    } else {\n      addZeroCopyBuf(std::move(buf));\n    }\n  }\n\n  if (ioBufFreeFunc_ && buf) {\n    ioBufFreeFunc_(std::move(buf));\n  }\n\n  return ret;\n} // namespace folly\n\nssize_t AsyncUDPSocket::write(\n    const folly::SocketAddress& address,\n    const std::unique_ptr<folly::IOBuf>& buf) {\n  return writeGSO(\n      address, buf, WriteOptions(0 /*gsoVal*/, false /* zerocopyVal*/));\n}\n\nssize_t AsyncUDPSocket::writev(\n    const folly::SocketAddress& address,\n    const struct iovec* vec,\n    size_t iovec_len,\n    WriteOptions options) {\n  CHECK_NE(NetworkSocket(), fd_) << \"Socket not yet bound\";\n  netops::Msgheader msg;\n  sockaddr_storage addrStorage;\n  address.getAddress(&addrStorage);\n\n  if (!connected_) {\n    msg.setName(&addrStorage, address.getActualSize());\n  } else {\n    if (connectedAddress_ != address) {\n      errno = ENOTSUP;\n      return -1;\n    }\n    msg.setName(nullptr, 0);\n  }\n  msg.setIovecs(vec, iovec_len);\n  msg.setCmsgPtr(nullptr);\n  msg.setCmsgLen(0);\n  msg.setFlags(0);\n\n#if defined(FOLLY_HAVE_MSG_ERRQUEUE) || defined(_WIN32)\n  maybeUpdateDynamicCmsgs();\n\n  constexpr size_t kSmallSizeMax = 5;\n  size_t controlBufSize = options.gso > 0 ? 1 : 0;\n  controlBufSize +=\n      cmsgs_->size() * (CMSG_SPACE(sizeof(int)) / CMSG_SPACE(sizeof(uint16_t)));\n\n  if (nontrivialCmsgs_.empty() && controlBufSize <= kSmallSizeMax) {\n    // Avoid allocating 0 length array. Doing so leads to exceptions\n    if (controlBufSize == 0) {\n      return writevImpl(&msg, options);\n    }\n\n    // suppress \"warning: variable length array 'control' is used [-Wvla]\"\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Wvla\")\n    // we will allocate this on the stack anyway even if we do not use it\n    char control\n        [(BOOST_PP_IF(FOLLY_HAVE_VLA_01, controlBufSize, kSmallSizeMax)) *\n         (CMSG_SPACE(sizeof(uint16_t)))];\n    memset(control, 0, sizeof(control));\n    msg.setCmsgPtr(control);\n    FOLLY_POP_WARNING\n    return writevImpl(&msg, options);\n  } else {\n    controlBufSize *= CMSG_SPACE(sizeof(uint16_t));\n    for (const auto& itr : nontrivialCmsgs_) {\n      controlBufSize += CMSG_SPACE(itr.second.size());\n    }\n    std::unique_ptr<char[]> control(new char[controlBufSize]);\n    memset(control.get(), 0, controlBufSize);\n    msg.setCmsgPtr(control.get());\n    return writevImpl(&msg, options);\n  }\n#else\n  CHECK_LT(options.gso, 1) << \"GSO not supported\";\n#ifdef _WIN32\n  return netops::wsaSendMsgDirect(fd_, msg.getMsg());\n#else\n  return sendmsg(fd_, msg.getMsg(), 0);\n#endif\n#endif\n}\n\nssize_t AsyncUDPSocket::writevImpl(\n    netops::Msgheader* msg, [[maybe_unused]] WriteOptions options) {\n#if defined(FOLLY_HAVE_MSG_ERRQUEUE) || defined(_WIN32)\n  XPLAT_CMSGHDR* cm = nullptr;\n\n  for (auto itr = cmsgs_->begin(); itr != cmsgs_->end(); ++itr) {\n    const auto key = itr->first;\n    const auto val = itr->second;\n    msg->incrCmsgLen(sizeof(val));\n    cm = msg->getFirstOrNextCmsgHeader(cm);\n    if (cm) {\n      cm->cmsg_level = key.level;\n      cm->cmsg_type = key.optname;\n      cm->cmsg_len = F_CMSG_LEN(sizeof(val));\n      F_COPY_CMSG_INT_DATA(cm, &val, sizeof(val));\n    }\n  }\n  for (const auto& itr : nontrivialCmsgs_) {\n    const auto& key = itr.first;\n    const auto& val = itr.second;\n    msg->incrCmsgLen(val.size());\n    cm = msg->getFirstOrNextCmsgHeader(cm);\n    if (cm) {\n      cm->cmsg_level = key.level;\n      cm->cmsg_type = key.optname;\n      cm->cmsg_len = F_CMSG_LEN(val.size());\n      F_COPY_CMSG_INT_DATA(cm, val.data(), val.size());\n    }\n  }\n\n  if (options.gso > 0) {\n    msg->incrCmsgLen(sizeof(uint16_t));\n    cm = msg->getFirstOrNextCmsgHeader(cm);\n    if (cm) {\n      cm->cmsg_level = UDP_GSO_SOCK_OPT_LEVEL;\n      cm->cmsg_type = UDP_GSO_SOCK_OPT_TYPE;\n      cm->cmsg_len = F_CMSG_LEN(sizeof(GSO_OPT_TYPE));\n      auto gso_len = static_cast<GSO_OPT_TYPE>(options.gso);\n      F_COPY_CMSG_INT_DATA(cm, &gso_len, sizeof(gso_len));\n    }\n  }\n#ifdef SCM_TXTIME\n  if (options.txTime.count() > 0 && txTime_.has_value() &&\n      (txTime_.value().clockid >= 0)) {\n    cm = msg->getFirstOrNextCmsgHeader(cm);\n    if (cm) {\n      cm->cmsg_level = SOL_SOCKET;\n      cm->cmsg_type = SCM_TXTIME;\n      cm->cmsg_len = F_CMSG_LEN(sizeof(uint64_t));\n      struct timespec ts;\n      clock_gettime(txTime_.value().clockid, &ts);\n      uint64_t txtime = ts.tv_sec * 1000000000ULL + ts.tv_nsec +\n          std::chrono::nanoseconds(options.txTime).count();\n      F_COPY_CMSG_INT_DATA(cm, &txtime, sizeof(txtime));\n    }\n  }\n#endif // SCM_TXTIME\n#endif // FOLLY_HAVE_MSG_ERRQUEUE || _WIN32\n\n#ifdef _WIN32\n  return netops::wsaSendMsgDirect(fd_, msg->getMsg());\n#else\n  return sendmsg(fd_, msg->getMsg(), 0);\n#endif\n}\n\nssize_t AsyncUDPSocket::writev(\n    const folly::SocketAddress& address,\n    const struct iovec* vec,\n    size_t iovec_len) {\n  return writev(\n      address,\n      vec,\n      iovec_len,\n      WriteOptions(0 /*gsoVal*/, false /* zerocopyVal*/));\n}\n\n/**\n * Send the data in buffers to destination. Returns the return code from\n * ::sendmmsg.\n */\nint AsyncUDPSocket::writem(\n    Range<SocketAddress const*> addrs,\n    const std::unique_ptr<folly::IOBuf>* bufs,\n    size_t count) {\n  return writemGSO(addrs, bufs, count, nullptr);\n}\n\nint AsyncUDPSocket::writemGSO(\n    Range<SocketAddress const*> addrs,\n    const std::unique_ptr<folly::IOBuf>* bufs,\n    size_t count,\n    const WriteOptions* options) {\n  int ret;\n  constexpr size_t kSmallSizeMax = 40;\n  char* controlPtr = nullptr;\n#ifndef FOLLY_HAVE_MSG_ERRQUEUE\n  CHECK(!options) << \"GSO not supported\";\n#endif\n  maybeUpdateDynamicCmsgs();\n  size_t singleControlBufSize = 1;\n  singleControlBufSize +=\n      cmsgs_->size() * (CMSG_SPACE(sizeof(int)) / CMSG_SPACE(sizeof(uint16_t)));\n  size_t controlBufSize = count * singleControlBufSize;\n  if (nontrivialCmsgs_.empty() && controlBufSize <= kSmallSizeMax) {\n    // suppress \"warning: variable length array 'vec' is used [-Wvla]\"\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Wvla\")\n    mmsghdr vec[BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallSizeMax)];\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    // we will allocate this on the stack anyway even if we do not use it\n    char control\n        [(BOOST_PP_IF(FOLLY_HAVE_VLA_01, controlBufSize, kSmallSizeMax)) *\n         (CMSG_SPACE(sizeof(uint16_t)))];\n    memset(control, 0, sizeof(control));\n    controlPtr = control;\n#endif\n    FOLLY_POP_WARNING\n    ret = writeImplIOBufs(addrs, bufs, count, vec, options, controlPtr);\n  } else {\n    std::unique_ptr<mmsghdr[]> vec(new mmsghdr[count]);\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    controlBufSize *= (CMSG_SPACE(sizeof(uint16_t)));\n    for (const auto& itr : nontrivialCmsgs_) {\n      controlBufSize += CMSG_SPACE(itr.second.size());\n    }\n    std::unique_ptr<char[]> control(new char[controlBufSize]);\n    memset(control.get(), 0, controlBufSize);\n    controlPtr = control.get();\n#endif\n    ret = writeImplIOBufs(addrs, bufs, count, vec.get(), options, controlPtr);\n  }\n\n  return ret;\n}\n\nint AsyncUDPSocket::writemv(\n    Range<SocketAddress const*> addrs,\n    iovec* iov,\n    size_t* numIovecsInBuffer,\n    size_t count) {\n  return writemGSOv(addrs, iov, numIovecsInBuffer, count, nullptr);\n}\n\nint AsyncUDPSocket::writemGSOv(\n    Range<SocketAddress const*> addrs,\n    iovec* iov,\n    size_t* numIovecsInBuffer,\n    size_t count,\n    const WriteOptions* options) {\n  int ret;\n  constexpr size_t kSmallSizeMax = 40;\n  char* controlPtr = nullptr;\n#ifndef FOLLY_HAVE_MSG_ERRQUEUE\n  CHECK(!options) << \"GSO not supported\";\n#endif\n  maybeUpdateDynamicCmsgs();\n  size_t singleControlBufSize = 1;\n  singleControlBufSize +=\n      cmsgs_->size() * (CMSG_SPACE(sizeof(int)) / CMSG_SPACE(sizeof(uint16_t)));\n  size_t controlBufSize = count * singleControlBufSize;\n  if (nontrivialCmsgs_.empty() && controlBufSize <= kSmallSizeMax) {\n    // suppress \"warning: variable length array 'vec' is used [-Wvla]\"\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Wvla\")\n    mmsghdr vec[BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallSizeMax)];\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    // we will allocate this on the stack anyway even if we do not use it\n    char control\n        [(BOOST_PP_IF(FOLLY_HAVE_VLA_01, controlBufSize, kSmallSizeMax)) *\n         (CMSG_SPACE(sizeof(uint16_t)))];\n    memset(control, 0, sizeof(control));\n    controlPtr = control;\n#endif\n    FOLLY_POP_WARNING\n    ret = writeImpl(\n        addrs, numIovecsInBuffer, iov, count, vec, options, controlPtr);\n  } else {\n    std::unique_ptr<mmsghdr[]> vec(new mmsghdr[count]);\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    controlBufSize *= (CMSG_SPACE(sizeof(uint16_t)));\n    for (const auto& itr : nontrivialCmsgs_) {\n      controlBufSize += CMSG_SPACE(itr.second.size());\n    }\n    std::unique_ptr<char[]> control(new char[controlBufSize]);\n    memset(control.get(), 0, controlBufSize);\n    controlPtr = control.get();\n#endif\n    ret = writeImpl(\n        addrs, numIovecsInBuffer, iov, count, vec.get(), options, controlPtr);\n  }\n\n  return ret;\n}\n\nvoid AsyncUDPSocket::fillIoVec(\n    const std::unique_ptr<folly::IOBuf>* bufs,\n    struct iovec* iov,\n    size_t* messageIovLens,\n    size_t count,\n    size_t iov_count) {\n  size_t remaining = iov_count;\n  size_t iov_pos = 0;\n  for (size_t i = 0; i < count; i++) {\n    messageIovLens[i] = bufs[i]->countChainElements();\n    auto ret = bufs[i]->fillIov(&iov[iov_pos], remaining);\n    size_t iovec_len = ret.numIovecs;\n    remaining -= iovec_len;\n    iov_pos += iovec_len;\n  }\n}\n\nvoid AsyncUDPSocket::fillMsgVec(\n    Range<full_sockaddr_storage*> addrs,\n    size_t* messageIovLens,\n    size_t count,\n    struct mmsghdr* msgvec,\n    struct iovec* iov,\n    const WriteOptions* options,\n    char* control) {\n  auto addr_count = addrs.size();\n  DCHECK(addr_count);\n\n  size_t iov_pos = 0;\n  for (size_t i = 0; i < count; i++) {\n    // we can use remaining here to avoid calling countChainElements() again\n    auto& msg = msgvec[i].msg_hdr;\n    // if we have less addrs compared to count\n    // we use the last addr\n    if (i < addr_count) {\n      msg.msg_name = reinterpret_cast<void*>(&addrs[i].storage);\n      msg.msg_namelen = addrs[i].len;\n    } else {\n      msg.msg_name = reinterpret_cast<void*>(&addrs[addr_count - 1].storage);\n      msg.msg_namelen = addrs[addr_count - 1].len;\n    }\n    msg.msg_iov = &iov[iov_pos];\n    msg.msg_iovlen = messageIovLens[i];\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    size_t controlBufSize = 1 +\n        cmsgs_->size() *\n            (CMSG_SPACE(sizeof(int)) / CMSG_SPACE(sizeof(uint16_t)));\n    // get the offset in the control buf allocated for this msg\n    msg.msg_control =\n        &control[i * controlBufSize * CMSG_SPACE(sizeof(uint16_t))];\n    msg.msg_controllen = 0;\n    struct cmsghdr* cm = nullptr;\n    // handle socket options\n    for (auto itr = cmsgs_->begin(); itr != cmsgs_->end(); ++itr) {\n      const auto key = itr->first;\n      const auto val = itr->second;\n      msg.msg_controllen += CMSG_SPACE(sizeof(val));\n      if (cm) {\n        cm = CMSG_NXTHDR(&msg, cm);\n      } else {\n        cm = CMSG_FIRSTHDR(&msg);\n      }\n      if (cm) {\n        cm->cmsg_level = key.level;\n        cm->cmsg_type = key.optname;\n        cm->cmsg_len = CMSG_LEN(sizeof(val));\n        memcpy(CMSG_DATA(cm), &val, sizeof(val));\n      }\n    }\n    for (const auto& itr : nontrivialCmsgs_) {\n      const auto& key = itr.first;\n      const auto& val = itr.second;\n      msg.msg_controllen += CMSG_SPACE(val.size());\n      if (cm) {\n        cm = CMSG_NXTHDR(&msg, cm);\n      } else {\n        cm = CMSG_FIRSTHDR(&msg);\n      }\n      if (cm) {\n        cm->cmsg_level = key.level;\n        cm->cmsg_type = key.optname;\n        cm->cmsg_len = CMSG_LEN(val.size());\n        memcpy(CMSG_DATA(cm), val.data(), val.size());\n      }\n    }\n\n    // handle GSO\n    if (options && options[i].gso > 0) {\n      msg.msg_controllen += CMSG_SPACE(sizeof(uint16_t));\n      if (cm) {\n        cm = CMSG_NXTHDR(&msg, cm);\n      } else {\n        cm = CMSG_FIRSTHDR(&msg);\n      }\n      if (cm) {\n        cm->cmsg_level = SOL_UDP;\n        cm->cmsg_type = UDP_SEGMENT;\n        cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));\n        auto gso_len = static_cast<uint16_t>(options[i].gso);\n        memcpy(CMSG_DATA(cm), &gso_len, sizeof(gso_len));\n      }\n    }\n    // there may be control buffer allocated, but nothing to put into it\n    // in this case, we null out the control fields\n    if (!cm) {\n      // no GSO, no socket options, null out control fields\n      msg.msg_control = nullptr;\n      msg.msg_controllen = 0;\n    }\n#else\n    (void)options;\n    (void)control;\n    msg.msg_control = nullptr;\n    msg.msg_controllen = 0;\n#endif\n    msg.msg_flags = 0;\n\n    msgvec[i].msg_len = 0;\n\n    iov_pos += messageIovLens[i];\n  }\n}\n\nint AsyncUDPSocket::writeImplIOBufs(\n    Range<SocketAddress const*> addrs,\n    const std::unique_ptr<folly::IOBuf>* bufs,\n    size_t count,\n    struct mmsghdr* msgvec,\n    const WriteOptions* options,\n    char* control) {\n  size_t iov_count = 0;\n  for (size_t i = 0; i < count; i++) {\n    iov_count += bufs[i]->countChainElements();\n  }\n\n  constexpr size_t kSmallSizeMax = 8;\n  if (iov_count <= kSmallSizeMax) {\n    // suppress \"warning: variable length array 'vec' is used [-Wvla]\"\n    FOLLY_PUSH_WARNING\n    FOLLY_GNU_DISABLE_WARNING(\"-Wvla\")\n    iovec iov[BOOST_PP_IF(FOLLY_HAVE_VLA_01, iov_count, kSmallSizeMax)];\n    size_t messageIovLens[BOOST_PP_IF(FOLLY_HAVE_VLA_01, count, kSmallSizeMax)];\n    FOLLY_POP_WARNING\n    fillIoVec(bufs, iov, messageIovLens, count, iov_count);\n    return writeImpl(\n        addrs, messageIovLens, iov, count, msgvec, options, control);\n  } else {\n    std::unique_ptr<iovec[]> iov(new iovec[iov_count]);\n    std::unique_ptr<size_t[]> messageIovLens(new size_t[count]);\n    fillIoVec(bufs, iov.get(), messageIovLens.get(), count, iov_count);\n    return writeImpl(\n        addrs,\n        messageIovLens.get(),\n        iov.get(),\n        count,\n        msgvec,\n        options,\n        control);\n  }\n}\n\nint AsyncUDPSocket::writeImpl(\n    Range<SocketAddress const*> addrs,\n    size_t* messageIovLens,\n    struct iovec* iov,\n    size_t count,\n    struct mmsghdr* msgvec,\n    const WriteOptions* options,\n    char* control) {\n  // most times we have a single destination addr\n  auto addr_count = addrs.size();\n  constexpr size_t kAddrCountMax = 1;\n  small_vector<full_sockaddr_storage, kAddrCountMax> addrStorage(addr_count);\n\n  for (size_t i = 0; i < addr_count; i++) {\n    addrs[i].getAddress(&addrStorage[i].storage);\n    addrStorage[i].len = folly::to_narrow(addrs[i].getActualSize());\n  }\n\n  fillMsgVec(\n      range(addrStorage), messageIovLens, count, msgvec, iov, options, control);\n  return sendmmsg(fd_, msgvec, count, 0);\n}\n\nssize_t AsyncUDPSocket::recvmsg(struct msghdr* msg, int flags) {\n  return netops::recvmsg(fd_, msg, flags);\n}\n\nint AsyncUDPSocket::recvmmsg(\n    struct mmsghdr* msgvec,\n    unsigned int vlen,\n    unsigned int flags,\n    struct timespec* timeout) {\n  return netops::recvmmsg(fd_, msgvec, vlen, flags, timeout);\n}\n\nvoid AsyncUDPSocket::resumeRead(ReadCallback* cob) {\n  CHECK(!readCallback_) << \"Another read callback already installed\";\n  CHECK_NE(NetworkSocket(), fd_)\n      << \"UDP server socket not yet bind to an address\";\n\n  readCallback_ = CHECK_NOTNULL(cob);\n  if (!updateRegistration()) {\n    AsyncSocketException ex(\n        AsyncSocketException::NOT_OPEN, \"failed to register for accept events\");\n\n    readCallback_ = nullptr;\n    cob->onReadError(ex);\n    return;\n  }\n}\n\nvoid AsyncUDPSocket::pauseRead() {\n  // It is ok to pause an already paused socket\n  readCallback_ = nullptr;\n  updateRegistration();\n}\n\nvoid AsyncUDPSocket::close() {\n  eventBase_->dcheckIsInEventBaseThread();\n\n  // Unregister any events we are registered for\n  unregisterHandler();\n\n  if (fd_ != NetworkSocket() && ownership_ == FDOwnership::OWNS) {\n    netops::close(fd_);\n  }\n\n  fd_ = NetworkSocket();\n\n  if (readCallback_) {\n    auto cob = readCallback_;\n    readCallback_ = nullptr;\n    cob->onReadClosed();\n  }\n}\n\nvoid AsyncUDPSocket::handlerReady(uint16_t events) noexcept {\n  if (events & (EventHandler::READ | EventHandler::WRITE)) {\n    if (handleErrMessages()) {\n      return;\n    }\n  }\n\n  if (events & EventHandler::READ) {\n    DCHECK(readCallback_);\n    handleRead();\n  }\n}\n\nvoid AsyncUDPSocket::releaseZeroCopyBuf(uint32_t id) {\n  auto iter = idZeroCopyBufMap_.find(id);\n  CHECK(iter != idZeroCopyBufMap_.end());\n  if (ioBufFreeFunc_) {\n    ioBufFreeFunc_(std::move(iter->second));\n  }\n  idZeroCopyBufMap_.erase(iter);\n}\n\nbool AsyncUDPSocket::isZeroCopyMsg([[maybe_unused]] const cmsghdr& cmsg) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  if ((cmsg.cmsg_level == SOL_IP && cmsg.cmsg_type == IP_RECVERR) ||\n      (cmsg.cmsg_level == SOL_IPV6 && cmsg.cmsg_type == IPV6_RECVERR)) {\n    auto serr =\n        reinterpret_cast<const struct sock_extended_err*>(CMSG_DATA(&cmsg));\n    return (\n        (serr->ee_errno == 0) && (serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY));\n  }\n#endif\n  return false;\n}\n\nvoid AsyncUDPSocket::processZeroCopyMsg([[maybe_unused]] const cmsghdr& cmsg) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  auto serr =\n      reinterpret_cast<const struct sock_extended_err*>(CMSG_DATA(&cmsg));\n  uint32_t hi = serr->ee_data;\n  uint32_t lo = serr->ee_info;\n  // disable zero copy if the buffer was actually copied\n  if ((serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED) && zeroCopyEnabled_) {\n    VLOG(2) << \"AsyncSocket::processZeroCopyMsg(): setting \"\n            << \"zeroCopyEnabled_ = false due to SO_EE_CODE_ZEROCOPY_COPIED \"\n            << \"on \" << fd_;\n    zeroCopyEnabled_ = false;\n  }\n\n  for (uint32_t i = lo; i <= hi; i++) {\n    releaseZeroCopyBuf(i);\n  }\n#endif\n}\n\nsize_t AsyncUDPSocket::handleErrMessages() noexcept {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  if (errMessageCallback_ == nullptr && idZeroCopyBufMap_.empty()) {\n    return 0;\n  }\n  uint8_t ctrl[1024];\n  unsigned char data;\n  struct msghdr msg;\n  iovec entry;\n\n  entry.iov_base = &data;\n  entry.iov_len = sizeof(data);\n  msg.msg_iov = &entry;\n  msg.msg_iovlen = 1;\n  msg.msg_name = nullptr;\n  msg.msg_namelen = 0;\n  msg.msg_control = ctrl;\n  msg.msg_controllen = sizeof(ctrl);\n  msg.msg_flags = 0;\n\n  int ret;\n  size_t num = 0;\n  while (fd_ != NetworkSocket()) {\n    ret = netops::recvmsg(fd_, &msg, MSG_ERRQUEUE);\n    VLOG(5) << \"AsyncSocket::handleErrMessages(): recvmsg returned \" << ret;\n\n    if (ret < 0) {\n      if (errno != EAGAIN) {\n        auto errnoCopy = errno;\n        LOG(ERROR) << \"::recvmsg exited with code \" << ret\n                   << \", errno: \" << errnoCopy;\n        AsyncSocketException ex(\n            AsyncSocketException::INTERNAL_ERROR,\n            \"recvmsg() failed\",\n            errnoCopy);\n        failErrMessageRead(ex);\n      }\n      return num;\n    }\n\n    for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n         cmsg != nullptr && cmsg->cmsg_len != 0;\n         cmsg = CMSG_NXTHDR(&msg, cmsg)) {\n      ++num;\n      if (isZeroCopyMsg(*cmsg)) {\n        processZeroCopyMsg(*cmsg);\n      } else {\n        errMessageCallback_->errMessage(*cmsg);\n      }\n      if (fd_ == NetworkSocket()) {\n        // once the socket is closed there is no use for more read errors.\n        return num;\n      }\n    }\n  }\n  return num;\n#else\n  return 0;\n#endif\n}\n\nvoid AsyncUDPSocket::failErrMessageRead(const AsyncSocketException& ex) {\n  if (errMessageCallback_ != nullptr) {\n    ErrMessageCallback* callback = errMessageCallback_;\n    errMessageCallback_ = nullptr;\n    callback->errMessageError(ex);\n  }\n}\n\nvoid AsyncUDPSocket::handleRead() noexcept {\n  void* buf{nullptr};\n  size_t len{0};\n\n  if (handleErrMessages()) {\n    return;\n  }\n\n  if (fd_ == NetworkSocket()) {\n    // The socket may have been closed by the error callbacks.\n    return;\n  }\n  if (readCallback_->shouldOnlyNotify()) {\n    return readCallback_->onNotifyDataAvailable(*this);\n  }\n\n  size_t numReads = maxReadsPerEvent_ ? maxReadsPerEvent_ : size_t(-1);\n  EventBase* originalEventBase = eventBase_;\n  while (numReads-- && readCallback_ && eventBase_ == originalEventBase) {\n    readCallback_->getReadBuffer(&buf, &len);\n    if (buf == nullptr || len == 0) {\n      AsyncSocketException ex(\n          AsyncSocketException::BAD_ARGS,\n          \"AsyncUDPSocket::getReadBuffer() returned empty buffer\");\n\n      auto cob = readCallback_;\n      readCallback_ = nullptr;\n      updateRegistration();\n      cob->onReadError(ex);\n      return;\n    }\n\n    struct sockaddr_storage addrStorage;\n    socklen_t addrLen = sizeof(addrStorage);\n    memset(&addrStorage, 0, size_t(addrLen));\n    auto rawAddr = reinterpret_cast<sockaddr*>(&addrStorage);\n    rawAddr->sa_family = localAddress_.getFamily();\n\n    ssize_t bytesRead;\n    ReadCallback::OnDataAvailableParams params;\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    bool use_gro = gro_.has_value() && (gro_.value() > 0);\n    bool use_ts = ts_.has_value() && (ts_.value() > 0);\n    if (use_gro || use_ts || recvTos_) {\n      char control[ReadCallback::OnDataAvailableParams::kCmsgSpace] = {};\n\n      struct msghdr msg = {};\n      struct iovec iov = {};\n\n      iov.iov_base = buf;\n      iov.iov_len = len;\n\n      msg.msg_iov = &iov;\n      msg.msg_iovlen = 1;\n\n      msg.msg_name = rawAddr;\n      msg.msg_namelen = addrLen;\n\n      msg.msg_control = control;\n      msg.msg_controllen = sizeof(control);\n\n      bytesRead = netops::recvmsg(fd_, &msg, MSG_TRUNC);\n\n      if (bytesRead >= 0) {\n        addrLen = msg.msg_namelen;\n        fromMsg(params, msg);\n      }\n    } else {\n      bytesRead = netops::recvfrom(fd_, buf, len, MSG_TRUNC, rawAddr, &addrLen);\n    }\n#elif _WIN32\n    WSABUF wBuf;\n    wBuf.buf = (CHAR*)buf;\n    wBuf.len = (ULONG)len;\n\n    WSAMSG wMsg{};\n    wMsg.dwBufferCount = 1;\n    wMsg.lpBuffers = &wBuf;\n    wMsg.name = rawAddr;\n    wMsg.namelen = addrLen;\n    wMsg.dwFlags = 0;\n\n    if (recvTos_) {\n      CHAR control[WSA_CMSG_SPACE(sizeof(INT))] = {0};\n      WSABUF controlBuf;\n      controlBuf.buf = control;\n      controlBuf.len = sizeof(control);\n      wMsg.Control = controlBuf;\n    }\n\n    bytesRead = netops::wsaRecvMesg(fd_, &wMsg);\n    if (recvTos_ && bytesRead > 0) {\n      int tosVal;\n      PCMSGHDR cmsg = WSA_CMSG_FIRSTHDR(&wMsg);\n      while (cmsg != NULL) {\n        if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) ||\n            (cmsg->cmsg_level == IPPROTO_IPV6 &&\n             cmsg->cmsg_type == IPV6_TCLASS)) {\n          params.tos = *(PINT)WSA_CMSG_DATA(cmsg);\n          break;\n        }\n        cmsg = WSA_CMSG_NXTHDR(&wMsg, cmsg);\n      }\n    }\n#else\n    bytesRead = netops::recvfrom(fd_, buf, len, MSG_TRUNC, rawAddr, &addrLen);\n#endif\n    if (bytesRead >= 0) {\n      clientAddress_.setFromSockaddr(rawAddr, addrLen);\n\n      if (bytesRead > 0) {\n        bool truncated = false;\n        if ((size_t)bytesRead > len) {\n          truncated = true;\n          bytesRead = ssize_t(len);\n        }\n\n        readCallback_->onDataAvailable(\n            clientAddress_, size_t(bytesRead), truncated, params);\n      }\n    } else {\n      if (errno == EAGAIN || errno == EWOULDBLOCK) {\n        // No data could be read without blocking the socket\n        return;\n      }\n\n      AsyncSocketException ex(\n          AsyncSocketException::INTERNAL_ERROR, \"::recvfrom() failed\", errno);\n\n      // In case of UDP we can continue reading from the socket\n      // even if the current request fails. We notify the user\n      // so that he can do some logging/stats collection if he wants.\n      auto cob = readCallback_;\n      readCallback_ = nullptr;\n      updateRegistration();\n      cob->onReadError(ex);\n      return;\n    }\n  }\n}\n\nbool AsyncUDPSocket::updateRegistration() noexcept {\n  uint16_t flags = NONE;\n\n  if (readCallback_) {\n    flags |= READ;\n  }\n\n  return registerHandler(uint16_t(flags | PERSIST));\n}\n\nbool AsyncUDPSocket::setGSO(int val) {\n#if defined(FOLLY_HAVE_MSG_ERRQUEUE) || defined(_WIN32)\n  int ret = netops::setsockopt(\n      fd_, UDP_GSO_SOCK_OPT_LEVEL, UDP_GSO_SOCK_OPT_TYPE, &val, sizeof(val));\n\n  gso_ = ret ? -1 : val;\n\n  return !ret;\n#else\n  (void)val;\n  return false;\n#endif\n}\n\nint AsyncUDPSocket::getGSO() {\n  // check if we can return the cached value\n  if (FOLLY_UNLIKELY(!gso_.has_value())) {\n#if defined(FOLLY_HAVE_MSG_ERRQUEUE) || defined(_WIN32)\n    int gso = -1;\n    socklen_t optlen = sizeof(gso);\n    if (!netops::getsockopt(\n            fd_,\n            UDP_GSO_SOCK_OPT_LEVEL,\n            UDP_GSO_SOCK_OPT_TYPE,\n            &gso,\n            &optlen)) {\n      gso_ = gso;\n    } else {\n      gso_ = -1;\n    }\n#else\n    gso_ = -1;\n#endif\n  }\n\n  return gso_.value();\n}\n\nbool AsyncUDPSocket::setGRO(bool bVal) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  int val = bVal ? 1 : 0;\n  int ret = netops::setsockopt(fd_, SOL_UDP, UDP_GRO, &val, sizeof(val));\n\n  gro_ = ret ? -1 : val;\n\n  return !ret;\n#else\n  (void)bVal;\n  return false;\n#endif\n}\n\n// packet timestamping\nint AsyncUDPSocket::getTimestamping() {\n  // check if we can return the cached value\n  if (FOLLY_UNLIKELY(!ts_.has_value())) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    int ts = -1;\n    socklen_t optlen = sizeof(ts);\n    if (!netops::getsockopt(fd_, SOL_SOCKET, SO_TIMESTAMPING, &ts, &optlen)) {\n      ts_ = ts;\n    } else {\n      ts_ = -1;\n    }\n#else\n    ts_ = -1;\n#endif\n  }\n\n  return ts_.value();\n}\n\nbool AsyncUDPSocket::setTimestamping(int val) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  int ret =\n      netops::setsockopt(fd_, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));\n\n  ts_ = ret ? -1 : val;\n\n  return !ret;\n#else\n  (void)val;\n  return false;\n#endif\n}\n\nint AsyncUDPSocket::getGRO() {\n  // check if we can return the cached value\n  if (FOLLY_UNLIKELY(!gro_.has_value())) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    int gro = -1;\n    socklen_t optlen = sizeof(gro);\n    if (!netops::getsockopt(fd_, SOL_UDP, UDP_GRO, &gro, &optlen)) {\n      gro_ = gro;\n    } else {\n      gro_ = -1;\n    }\n#else\n    gro_ = -1;\n#endif\n  }\n\n  return gro_.value();\n}\n\nAsyncUDPSocket::TXTime AsyncUDPSocket::getTXTime() {\n  // check if we can return the cached value\n  if (FOLLY_UNLIKELY(!txTime_.has_value())) {\n    TXTime txTime;\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n    folly::netops::sock_txtime val = {};\n    socklen_t optlen = sizeof(val);\n    if (!netops::getsockopt(fd_, SOL_SOCKET, SO_TXTIME, &val, &optlen)) {\n      txTime.clockid = val.clockid;\n      txTime.deadline = (val.flags & folly::netops::SOF_TXTIME_DEADLINE_MODE);\n    }\n#endif\n    txTime_ = txTime;\n  }\n\n  return txTime_.value();\n}\n\nbool AsyncUDPSocket::setTXTime(TXTime txTime) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  folly::netops::sock_txtime val;\n  val.clockid = txTime.clockid;\n  val.flags = txTime.deadline ? folly::netops::SOF_TXTIME_DEADLINE_MODE : 0;\n  int ret = netops::setsockopt(fd_, SOL_SOCKET, SO_TXTIME, &val, sizeof(val));\n\n  txTime_ = ret ? TXTime() : txTime;\n\n  return !ret;\n#else\n  (void)txTime;\n  return false;\n#endif\n}\n\nbool AsyncUDPSocket::setRxZeroChksum6([[maybe_unused]] bool bVal) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  if (address().getFamily() != AF_INET6) {\n    return false;\n  }\n\n  int val = bVal ? 1 : 0;\n  int ret =\n      netops::setsockopt(fd_, SOL_UDP, UDP_NO_CHECK6_RX, &val, sizeof(val));\n  return !ret;\n#else\n  return false;\n#endif\n}\n\nbool AsyncUDPSocket::setTxZeroChksum6([[maybe_unused]] bool bVal) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  if (address().getFamily() != AF_INET6) {\n    return false;\n  }\n\n  int val = bVal ? 1 : 0;\n  int ret =\n      netops::setsockopt(fd_, SOL_UDP, UDP_NO_CHECK6_TX, &val, sizeof(val));\n  return !ret;\n#else\n  return false;\n#endif\n}\n\nvoid AsyncUDPSocket::setTosOrTrafficClass(uint8_t tosOrTclass) {\n#ifdef _WIN32\n  // For windows, we can only set the values 0 and 1 (for the ECN bits).\n  // Any DSCP values have to be set via the QoS Policy\n  auto ecn = tosOrTclass & 0x3;\n  auto level = address().getFamily() == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP;\n  if (ecn == 0) {\n    // Remove ECN cmsgs if any exist\n    defaultCmsgs_.erase({level, IP_ECN});\n  } else {\n    defaultCmsgs_[{level, IP_ECN}] = ecn;\n  }\n#else\n  int valInt = tosOrTclass;\n  if (address().getFamily() == AF_INET6) {\n    if (netops::setsockopt(\n            fd_,\n            IPPROTO_IPV6,\n            IPV6_TCLASS,\n            &valInt,\n            socklen_t(sizeof(valInt))) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN, \"Failed to set IPV6_TCLASS\", errno);\n    }\n  } else {\n    if (netops::setsockopt(\n            fd_, IPPROTO_IP, IP_TOS, &valInt, socklen_t(sizeof(valInt))) != 0) {\n      throw AsyncSocketException(\n          AsyncSocketException::NOT_OPEN, \"Failed to set IP_TOS\", errno);\n    }\n  }\n#endif\n}\n\nvoid AsyncUDPSocket::applyOptions(\n    const SocketOptionMap& options, SocketOptionKey::ApplyPos pos) {\n  auto result = applySocketOptions(fd_, options, pos);\n  if (result != 0) {\n    throw AsyncSocketException(\n        AsyncSocketException::INTERNAL_ERROR,\n        \"failed to set socket option\",\n        result);\n  }\n}\n\nvoid AsyncUDPSocket::detachEventBase() {\n  DCHECK(eventBase_ && eventBase_->isInEventBaseThread());\n  registerHandler(uint16_t(NONE));\n  eventBase_ = nullptr;\n  EventHandler::detachEventBase();\n}\n\nvoid AsyncUDPSocket::attachEventBase(folly::EventBase* evb) {\n  DCHECK(!eventBase_);\n  DCHECK(evb && evb->isInEventBaseThread());\n  eventBase_ = evb;\n  EventHandler::attachEventBase(evb);\n  updateRegistration();\n}\n\nvoid AsyncUDPSocket::setCmsgs(const SocketCmsgMap& cmsgs) {\n  defaultCmsgs_ = cmsgs;\n}\n\nvoid AsyncUDPSocket::setNontrivialCmsgs(\n    const SocketNontrivialCmsgMap& nontrivialCmsgs) {\n  nontrivialCmsgs_ = nontrivialCmsgs;\n}\n\nvoid AsyncUDPSocket::appendCmsgs(const SocketCmsgMap& cmsgs) {\n  for (auto itr = cmsgs.begin(); itr != cmsgs.end(); ++itr) {\n    defaultCmsgs_[itr->first] = itr->second;\n  }\n}\n\nvoid AsyncUDPSocket::maybeUpdateDynamicCmsgs() noexcept {\n  cmsgs_ = &defaultCmsgs_;\n  if (additionalCmsgsFunc_) {\n    auto additionalCmsgs = additionalCmsgsFunc_();\n    if (additionalCmsgs && !additionalCmsgs.value().empty()) {\n      dynamicCmsgs_ = std::move(additionalCmsgs.value());\n      // Union with defaultCmsgs_ without overwriting values for overlapping\n      // keys\n      dynamicCmsgs_.insert(defaultCmsgs_.begin(), defaultCmsgs_.end());\n      cmsgs_ = &dynamicCmsgs_;\n    }\n  }\n}\n\nvoid AsyncUDPSocket::appendNontrivialCmsgs(\n    const SocketNontrivialCmsgMap& nontrivialCmsgs) {\n  for (auto itr = nontrivialCmsgs.begin(); itr != nontrivialCmsgs.end();\n       ++itr) {\n    nontrivialCmsgs_[itr->first] = itr->second;\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AsyncUDPSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <folly/io/SocketOptionMap.h>\n\n#include <folly/Function.h>\n#include <folly/ScopeGuard.h>\n#include <folly/SocketAddress.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncSocketBase.h>\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/net/NetOps.h>\n#include <folly/net/NetOpsDispatcher.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly {\n\n/**\n * UDP socket\n */\nclass AsyncUDPSocket : public EventHandler {\n public:\n  enum class FDOwnership { OWNS, SHARED };\n\n  AsyncUDPSocket(const AsyncUDPSocket&) = delete;\n  AsyncUDPSocket& operator=(const AsyncUDPSocket&) = delete;\n\n  class ReadCallback {\n   public:\n    struct OnDataAvailableParams {\n      int gro = -1;\n      // RX timestamp if available\n      using Timestamp = std::array<struct timespec, 3>;\n      folly::Optional<Timestamp> ts;\n      uint8_t tos = 0;\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n      static constexpr size_t kCmsgSpace = CMSG_SPACE(sizeof(uint16_t)) +\n          CMSG_SPACE(sizeof(Timestamp)) + CMSG_SPACE(sizeof(uint8_t));\n#endif\n    };\n\n    /**\n     * Invoked when the socket becomes readable and we want buffer\n     * to write to.\n     *\n     * NOTE: From socket we will end up reading at most `len` bytes\n     *       and if there were more bytes in datagram, we will end up\n     *       dropping them.\n     */\n    virtual void getReadBuffer(void** buf, size_t* len) noexcept = 0;\n\n    /**\n     * Invoked when a new datagram is available on the socket. `len`\n     * is the number of bytes read and `truncated` is true if we had\n     * to drop few bytes because of running out of buffer space.\n     *  OnDataAvailableParams::gro is the GRO segment size\n     */\n    virtual void onDataAvailable(\n        const folly::SocketAddress& client,\n        size_t len,\n        bool truncated,\n        OnDataAvailableParams params) noexcept = 0;\n\n    /**\n     * Notifies when data is available. This is only invoked when\n     * shouldNotifyOnly() returns true.\n     */\n    virtual void onNotifyDataAvailable(AsyncUDPSocket&) noexcept {}\n\n    /**\n     * Returns whether or not the read callback should only notify\n     * but not call getReadBuffer.\n     * If shouldNotifyOnly() returns true, AsyncUDPSocket will invoke\n     * onNotifyDataAvailable() instead of getReadBuffer().\n     * If shouldNotifyOnly() returns false, AsyncUDPSocket will invoke\n     * getReadBuffer() and onDataAvailable().\n     */\n    virtual bool shouldOnlyNotify() { return false; }\n\n    /**\n     * Invoked when there is an error reading from the socket.\n     *\n     * NOTE: Since UDP is connectionless, you can still read from the socket.\n     *       But you have to re-register readCallback yourself after\n     *       onReadError.\n     */\n    virtual void onReadError(const AsyncSocketException& ex) noexcept = 0;\n\n    /**\n     * Invoked when socket is closed and a read callback is registered.\n     */\n    virtual void onReadClosed() noexcept = 0;\n\n    virtual ~ReadCallback() = default;\n  };\n\n  class ErrMessageCallback {\n   public:\n    virtual ~ErrMessageCallback() = default;\n\n    /**\n     * errMessage() will be invoked when kernel puts a message to\n     * the error queue associated with the socket.\n     *\n     * @param cmsg      Reference to cmsghdr structure describing\n     *                  a message read from error queue associated\n     *                  with the socket.\n     */\n    virtual void errMessage(const cmsghdr& cmsg) noexcept = 0;\n\n    /**\n     * errMessageError() will be invoked if an error occurs reading a message\n     * from the socket error stream.\n     *\n     * @param ex        An exception describing the error that occurred.\n     */\n    virtual void errMessageError(const AsyncSocketException& ex) noexcept = 0;\n  };\n\n  static void fromMsg(\n      [[maybe_unused]] ReadCallback::OnDataAvailableParams& params,\n      [[maybe_unused]] struct msghdr& msg);\n\n  using IOBufFreeFunc = folly::Function<void(std::unique_ptr<folly::IOBuf>&&)>;\n\n  using AdditionalCmsgsFunc = folly::Function<folly::Optional<SocketCmsgMap>()>;\n\n  struct WriteOptions {\n    WriteOptions() = default;\n    WriteOptions(int gsoVal, bool zerocopyVal)\n        : gso(gsoVal), zerocopy(zerocopyVal) {}\n    int gso{0};\n    bool zerocopy{false};\n    std::chrono::microseconds txTime{0};\n  };\n\n  struct TXTime {\n    int clockid{-1};\n    bool deadline{false};\n  };\n\n  /**\n   * Create a new UDP socket that will run in the\n   * given eventbase\n   */\n  explicit AsyncUDPSocket(EventBase* evb);\n  ~AsyncUDPSocket() override;\n\n  /**\n   * Returns the address server is listening on\n   */\n  virtual const folly::SocketAddress& address() const {\n    CHECK_NE(NetworkSocket(), fd_) << \"Server not yet bound to an address\";\n    return localAddress_;\n  }\n\n  /**\n   * Contains options to pass to bind.\n   */\n  struct BindOptions {\n    BindOptions() noexcept {}\n    // Whether IPV6_ONLY should be set on the socket.\n    bool bindV6Only{true};\n    std::string ifName;\n  };\n\n  /**\n   * Bind the socket to the following address. If port is not\n   * set in the `address` an ephemeral port is chosen and you can\n   * use `address()` method above to get it after this method successfully\n   * returns. The parameter BindOptions contains parameters for the bind.\n   */\n  virtual void bind(\n      const folly::SocketAddress& address, BindOptions options = BindOptions());\n\n  /**\n   * Connects the UDP socket to a remote destination address provided in\n   * address. This can speed up UDP writes on linux because it will cache flow\n   * state on connects.\n   * Using connect has many quirks, and you should be aware of them before using\n   * this API:\n   * 1. If this is called before bind, the socket will be automatically bound to\n   * the IP address of the current default network interface.\n   * 2. Normally UDP can use the 2 tuple (src ip, src port) to steer packets\n   * sent by the peer to the socket, however after connecting the socket, only\n   * packets destined to the destination address specified in connect() will be\n   * forwarded and others will be dropped. If the server can send a packet\n   * from a different destination port / IP then you probably do not want to use\n   * this API.\n   * 3. It can be called repeatedly on either the client or server however it's\n   * normally only useful on the client and not server.\n   *\n   * Returns the result of calling the connect syscall.\n   */\n  virtual void connect(const folly::SocketAddress& address);\n\n  /**\n   * Use an already bound file descriptor. You can either transfer ownership\n   * of this FD by using ownership = FDOwnership::OWNS or share it using\n   * FDOwnership::SHARED. In case FD is shared, it will not be `close`d in\n   * destructor.\n   */\n  virtual void setFD(NetworkSocket fd, FDOwnership ownership);\n\n  bool setZeroCopy(bool enable);\n  bool getZeroCopy() const { return zeroCopyEnabled_; }\n\n  uint32_t getZeroCopyBufId() const { return zeroCopyBufId_; }\n\n  size_t getZeroCopyReenableThreshold() const {\n    return zeroCopyReenableThreshold_;\n  }\n\n  void setZeroCopyReenableThreshold(size_t threshold) {\n    zeroCopyReenableThreshold_ = threshold;\n  }\n\n  /**\n   * Set extra control messages to send\n   */\n  virtual void setCmsgs(const SocketCmsgMap& cmsgs);\n  virtual void setNontrivialCmsgs(\n      const SocketNontrivialCmsgMap& nontrivialCmsgs);\n\n  virtual void appendCmsgs(const SocketCmsgMap& cmsgs);\n  virtual void appendNontrivialCmsgs(\n      const SocketNontrivialCmsgMap& nontrivialCmsgs);\n  virtual void setAdditionalCmsgsFunc(\n      AdditionalCmsgsFunc&& additionalCmsgsFunc) {\n    additionalCmsgsFunc_ = std::move(additionalCmsgsFunc);\n    dynamicCmsgs_.clear();\n  }\n\n  /**\n   * Send the data in buffer to destination. Returns the return code from\n   * ::sendmsg.\n   */\n  virtual ssize_t write(\n      const folly::SocketAddress& address,\n      const std::unique_ptr<folly::IOBuf>& buf);\n\n  /**\n   * Send the data in buffers to destination. Returns the return code from\n   * ::sendmmsg.\n   * bufs is an array of std::unique_ptr<folly::IOBuf>\n   * of size num\n   */\n  virtual int writem(\n      Range<SocketAddress const*> addrs,\n      const std::unique_ptr<folly::IOBuf>* bufs,\n      size_t count);\n\n  /**\n   * Send the data in buffers to destination. Returns the return code from\n   * ::sendmmsg.\n   * iov is an array of iovecs, which is composed of \"count\" messages that\n   * need to be sent. Each message can have multiple iovecs. The number of\n   * iovecs per message is specified in numIovecsInBuffer.\n   */\n  virtual int writemv(\n      Range<SocketAddress const*> addrs,\n      iovec* iov,\n      size_t* numIovecsInBuffer,\n      size_t count);\n\n  /**\n   * Send the data in buffer to destination. Returns the return code from\n   * ::sendmsg.\n   *  gso is the generic segmentation offload value\n   *  writeGSO will return -1 if\n   *  buf->computeChainDataLength() <= gso\n   *  Before calling writeGSO with a positive value\n   *  verify GSO is supported on this platform by calling getGSO\n   */\n  virtual ssize_t writeGSO(\n      const folly::SocketAddress& address,\n      const std::unique_ptr<folly::IOBuf>& buf,\n      WriteOptions options);\n\n  virtual ssize_t writeChain(\n      const folly::SocketAddress& address,\n      std::unique_ptr<folly::IOBuf>&& buf,\n      WriteOptions options);\n\n  /**\n   * Send the data in buffers to destination. Returns the return code from\n   * ::sendmmsg.\n   * bufs is an array of std::unique_ptr<folly::IOBuf>\n   * of size num\n   * options is an array of WriteOptions or nullptr\n   *  Before calling writeGSO with a positive value\n   *  verify GSO is supported on this platform by calling getGSO\n   */\n  virtual int writemGSO(\n      Range<SocketAddress const*> addrs,\n      const std::unique_ptr<folly::IOBuf>* bufs,\n      size_t count,\n      const WriteOptions* options);\n\n  /**\n   * Send the data in buffers to destination. Returns the return code from\n   * ::sendmmsg.\n   * iov is an array of iovecs, which is composed of \"count\" messages that\n   * need to be sent. Each message can have multiple iovecs. The number of\n   * iovecs per message is specified in numIovecsInBuffer.\n   * options is an array of WriteOptions or nullptr\n   * Before calling writeGSO with a positive value\n   * verify GSO is supported on this platform by calling getGSO\n   */\n  virtual int writemGSOv(\n      Range<SocketAddress const*> addrs,\n      iovec* iov,\n      size_t* numIovecsInBuffer,\n      size_t count,\n      const WriteOptions* options);\n\n  /**\n   * Send data in iovec to destination. Returns the return code from sendmsg.\n   */\n  virtual ssize_t writev(\n      const folly::SocketAddress& address,\n      const struct iovec* vec,\n      size_t iovec_len,\n      WriteOptions options);\n\n  virtual ssize_t writev(\n      const folly::SocketAddress& address,\n      const struct iovec* vec,\n      size_t iovec_len);\n\n  virtual ssize_t recvmsg(struct msghdr* msg, int flags);\n\n  virtual int recvmmsg(\n      struct mmsghdr* msgvec,\n      unsigned int vlen,\n      unsigned int flags,\n      struct timespec* timeout);\n\n  /**\n   * Start reading datagrams\n   */\n  virtual void resumeRead(ReadCallback* cob);\n\n  /**\n   * Pause reading datagrams\n   */\n  virtual void pauseRead();\n\n  /**\n   * Stop listening on the socket.\n   */\n  virtual void close();\n\n  /**\n   * Get internal FD used by this socket\n   */\n  virtual NetworkSocket getNetworkSocket() const {\n    CHECK_NE(NetworkSocket(), fd_) << \"Need to bind before getting FD out\";\n    return fd_;\n  }\n\n  /**\n   * Set IP_FREEBIND to allow binding to an address that is nonlocal or doesn't\n   * exist yet.\n   */\n  virtual void setFreeBind(bool freeBind) { freeBind_ = freeBind; }\n\n  /**\n   * Set IP_TRANSPARENT to allow enables transparent proxying on the socket\n   */\n  virtual void setTransparent(bool transparent) { transparent_ = transparent; }\n\n  /**\n   * Set IPV6_RECVTCLASS/IP_RECVTOS to allow receiving of the IPv6 Traffic\n   * Class/IPv4 Type of Service field.\n   */\n  virtual void setRecvTos(bool recvTos) { recvTos_ = recvTos; }\n\n  /**\n   * Get IPV6_RECVTCLASS/IP_RECVTOS status of the socket. If true, the IPv6\n   * Traffic Class/IPv4 Type of Service field should be populated in\n   * OnDataAvailableParams.\n   */\n  virtual bool getRecvTos() { return recvTos_; }\n\n  /**\n   * Set reuse port mode to call bind() on the same address multiple times\n   */\n  virtual void setReusePort(bool reusePort) { reusePort_ = reusePort; }\n\n  /**\n   * Set SO_REUSEADDR flag on the socket. Default is OFF.\n   */\n  virtual void setReuseAddr(bool reuseAddr) { reuseAddr_ = reuseAddr; }\n\n  /**\n   * Set SO_RCVBUF option on the socket, if not zero. Default is zero.\n   */\n  virtual void setRcvBuf(int rcvBuf) { rcvBuf_ = rcvBuf; }\n\n  /**\n   * Set SO_SNDBUF option on the socket, if not zero. Default is zero.\n   */\n  virtual void setSndBuf(int sndBuf) { sndBuf_ = sndBuf; }\n\n  /**\n   * Set SO_BUSY_POLL option on the socket, if not zero. Default is zero.\n   * Caution! The feature is not available on Apple's systems.\n   */\n  virtual void setBusyPoll(int busyPollUs) { busyPollUs_ = busyPollUs; }\n\n  EventBase* getEventBase() const { return eventBase_; }\n\n  /**\n   * Enable or disable fragmentation on the socket.\n   *\n   * On Linux, this sets IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_DO when enabled,\n   * and to IP(V6)_PMTUDISC_WANT when disabled. IP(V6)_PMTUDISC_WANT will use\n   * per-route setting to set DF bit. It may be more desirable to use\n   * IP(V6)_PMTUDISC_PROBE as opposed to IP(V6)_PMTUDISC_DO for apps that has\n   * its own PMTU Discovery mechanism.\n   * Note this doesn't work on Apple.\n   */\n  virtual void dontFragment(bool df);\n\n  /**\n   * Set Dont-Fragment (DF) but ignore Path MTU.\n   *\n   * On Linux, this sets  IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_PROBE.\n   * This essentially sets DF but ignores Path MTU for this socket.\n   * This may be desirable for apps that has its own PMTU Discovery mechanism.\n   * See http://man7.org/linux/man-pages/man7/ip.7.html for more info.\n   */\n  virtual void setDFAndTurnOffPMTU();\n\n  /**\n   * Callback for receiving errors on the UDP sockets\n   */\n  virtual void setErrMessageCallback(ErrMessageCallback* errMessageCallback);\n\n  virtual bool isBound() const { return fd_ != NetworkSocket(); }\n\n  virtual bool isReading() const { return readCallback_ != nullptr; }\n\n  /**\n   * Set the maximum number of reads to execute from the underlying\n   * socket each time the EventBase detects that new ingress data is\n   * available. The default is kMaxReadsPerEvent\n   *\n   * @param maxReads  Maximum number of reads per data-available event;\n   *                  a value of zero means unlimited.\n   */\n  void setMaxReadsPerEvent(uint16_t maxReads) { maxReadsPerEvent_ = maxReads; }\n\n  /**\n   * Get the maximum number of reads this object will execute from\n   * the underlying socket each time the EventBase detects that new\n   * ingress data is available.\n   *\n   * @returns Maximum number of reads per data-available event; a value\n   *          of zero means unlimited.\n   */\n  uint16_t getMaxReadsPerEvent() const { return maxReadsPerEvent_; }\n\n  virtual void detachEventBase();\n\n  virtual void attachEventBase(folly::EventBase* evb);\n\n  // generic segmentation offload get/set\n  // negative return value means GSO is not available\n  virtual int getGSO();\n\n  bool setGSO(int val);\n\n  void setIOBufFreeFunc(IOBufFreeFunc&& ioBufFreeFunc) {\n    ioBufFreeFunc_ = std::move(ioBufFreeFunc);\n  }\n\n  // generic receive offload get/set\n  // negative return value means GRO is not available\n  int getGRO();\n\n  bool setGRO(bool bVal);\n\n  // TX time\n  TXTime getTXTime();\n\n  bool setTXTime(TXTime txTime);\n\n  // packet timestamping\n  int getTimestamping();\n  bool setTimestamping(int val);\n\n  // disable/enable RX zero checksum check for UDP over IPv6\n  bool setRxZeroChksum6(bool bVal);\n\n  // disable/enable TX zero checksum for UDP over IPv6\n  bool setTxZeroChksum6(bool bVal);\n\n  // Set ToS or Traffic Class in the underlying socket\n  // depending on the address family.\n  void setTosOrTrafficClass(uint8_t tosOrTclass);\n\n  virtual void applyOptions(\n      const SocketOptionMap& options, SocketOptionKey::ApplyPos pos);\n\n  /**\n   * Override netops::Dispatcher to be used for netops:: calls.\n   *\n   * Pass empty shared_ptr to reset to default.\n   * Override can be used by unit tests to intercept and mock netops:: calls.\n   */\n  virtual void setOverrideNetOpsDispatcher(\n      std::shared_ptr<netops::Dispatcher> dispatcher) {\n    netops_.setOverride(std::move(dispatcher));\n  }\n\n  /**\n   * Returns override netops::Dispatcher being used for netops:: calls.\n   *\n   * Returns empty shared_ptr if no override set.\n   * Override can be used by unit tests to intercept and mock netops:: calls.\n   */\n  virtual std::shared_ptr<netops::Dispatcher> getOverrideNetOpsDispatcher()\n      const {\n    return netops_.getOverride();\n  }\n\n  // Initializes underlying socket fd. This is called in bind() and connect()\n  // internally if fd is not yet set at the time of the call. But if there is a\n  // need to apply socket options pre-bind, one can call this function\n  // explicitly before bind()/connect() and socket opts application.\n  void init(sa_family_t family, BindOptions bindOptions = BindOptions());\n\n protected:\n  struct full_sockaddr_storage {\n    sockaddr_storage storage;\n    socklen_t len;\n  };\n\n  virtual ssize_t sendmsg(\n      NetworkSocket socket, const struct msghdr* message, int flags) {\n    return netops_->sendmsg(socket, message, flags);\n  }\n\n  virtual int sendmmsg(\n      NetworkSocket socket,\n      struct mmsghdr* msgvec,\n      unsigned int vlen,\n      int flags) {\n    return netops_->sendmmsg(socket, msgvec, vlen, flags);\n  }\n\n  void fillIoVec(\n      const std::unique_ptr<folly::IOBuf>* bufs,\n      struct iovec* iov,\n      size_t* messageIovLens,\n      size_t count,\n      size_t iov_count);\n\n  void fillMsgVec(\n      Range<full_sockaddr_storage*> addrs,\n      size_t* messageIovLens,\n      size_t count,\n      struct mmsghdr* msgvec,\n      struct iovec* iov,\n      const WriteOptions* options,\n      char* control);\n\n  virtual int writeImplIOBufs(\n      Range<SocketAddress const*> addrs,\n      const std::unique_ptr<folly::IOBuf>* bufs,\n      size_t count,\n      struct mmsghdr* msgvec,\n      const WriteOptions* options,\n      char* control);\n\n  virtual int writeImpl(\n      Range<SocketAddress const*> addrs,\n      size_t* messageIovLens,\n      struct iovec* iov,\n      size_t count,\n      struct mmsghdr* msgvec,\n      const WriteOptions* options,\n      char* control);\n\n  virtual ssize_t writevImpl(\n      netops::Msgheader* msg, [[maybe_unused]] WriteOptions options);\n\n  size_t handleErrMessages() noexcept;\n\n  void failErrMessageRead(const AsyncSocketException& ex);\n\n  static auto constexpr kDefaultReadsPerEvent = 1;\n  uint16_t maxReadsPerEvent_{\n      kDefaultReadsPerEvent}; ///< Max reads per event loop iteration\n\n  // Non-null only when we are reading\n  ReadCallback* readCallback_;\n\n private:\n  // EventHandler\n  void handlerReady(uint16_t events) noexcept override;\n\n  void handleRead() noexcept;\n  bool updateRegistration() noexcept;\n  void maybeUpdateDynamicCmsgs() noexcept;\n\n  EventBase* eventBase_;\n  folly::SocketAddress localAddress_;\n\n  NetworkSocket fd_;\n  FDOwnership ownership_;\n\n  // Temp space to receive client address\n  folly::SocketAddress clientAddress_;\n\n  // If the socket is connected.\n  folly::SocketAddress connectedAddress_;\n  bool connected_{false};\n\n  bool reuseAddr_{false};\n  bool reusePort_{false};\n  bool freeBind_{false};\n  bool transparent_{false};\n  bool recvTos_{false};\n  int rcvBuf_{0};\n  int sndBuf_{0};\n  int busyPollUs_{0};\n\n  // generic segmentation offload value, if available\n  // See https://lwn.net/Articles/188489/ for more details\n  folly::Optional<int> gso_;\n\n  // generic receive offload value, if available\n  // See https://lwn.net/Articles/770978/ for more details\n  folly::Optional<int> gro_;\n\n  // multi release pacing for UDP GSO\n  // See https://lwn.net/Articles/822726/ for more details\n  folly::Optional<TXTime> txTime_;\n\n  // packet timestamping\n  folly::Optional<int> ts_;\n\n  ErrMessageCallback* errMessageCallback_{nullptr};\n\n  bool zeroCopyEnabled_{false};\n  bool zeroCopyVal_{false};\n  // zerocopy re-enable logic\n  size_t zeroCopyReenableThreshold_{0};\n  size_t zeroCopyReenableCounter_{0};\n\n  uint32_t zeroCopyBufId_{0};\n\n  int getZeroCopyFlags();\n  static bool isZeroCopyMsg([[maybe_unused]] const cmsghdr& cmsg);\n  void processZeroCopyMsg([[maybe_unused]] const cmsghdr& cmsg);\n  void addZeroCopyBuf(std::unique_ptr<folly::IOBuf>&& buf);\n  void releaseZeroCopyBuf(uint32_t id);\n\n  uint32_t getNextZeroCopyBufId() { return zeroCopyBufId_++; }\n\n  std::unordered_map<uint32_t, std::unique_ptr<folly::IOBuf>> idZeroCopyBufMap_;\n\n  IOBufFreeFunc ioBufFreeFunc_;\n\n  SocketCmsgMap defaultCmsgs_;\n  SocketCmsgMap dynamicCmsgs_;\n  SocketCmsgMap* cmsgs_{&defaultCmsgs_};\n\n  SocketNontrivialCmsgMap nontrivialCmsgs_;\n\n  AdditionalCmsgsFunc additionalCmsgsFunc_;\n\n  netops::DispatcherContainer netops_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AtomicNotificationQueue-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/FileUtil.h>\n#include <folly/system/Pid.h>\n\nnamespace folly {\n\ntemplate <typename Task>\nAtomicNotificationQueue<Task>::Queue::Queue(Queue&& other) noexcept\n    : head_(std::exchange(other.head_, nullptr)),\n      size_(std::exchange(other.size_, 0)) {}\n\ntemplate <typename Task>\ntypename AtomicNotificationQueue<Task>::Queue&\nAtomicNotificationQueue<Task>::Queue::operator=(Queue&& other) noexcept {\n  clear();\n  std::swap(head_, other.head_);\n  std::swap(size_, other.size_);\n  return *this;\n}\n\ntemplate <typename Task>\nAtomicNotificationQueue<Task>::Queue::~Queue() {\n  clear();\n}\n\ntemplate <typename Task>\nbool AtomicNotificationQueue<Task>::Queue::empty() const {\n  return !head_;\n}\n\ntemplate <typename Task>\nssize_t AtomicNotificationQueue<Task>::Queue::size() const {\n  return size_;\n}\n\ntemplate <typename Task>\ntypename AtomicNotificationQueue<Task>::Node&\nAtomicNotificationQueue<Task>::Queue::front() {\n  return *head_;\n}\n\ntemplate <typename Task>\nvoid AtomicNotificationQueue<Task>::Queue::pop() {\n  std::unique_ptr<Node>(std::exchange(head_, head_->next));\n  --size_;\n}\n\ntemplate <typename Task>\nvoid AtomicNotificationQueue<Task>::Queue::clear() {\n  while (!empty()) {\n    pop();\n  }\n}\n\ntemplate <typename Task>\nAtomicNotificationQueue<Task>::Queue::Queue(Node* head, ssize_t size)\n    : head_(head), size_(size) {}\n\ntemplate <typename Task>\ntypename AtomicNotificationQueue<Task>::Queue\nAtomicNotificationQueue<Task>::Queue::fromReversed(Node* tail) {\n  // Reverse a linked list.\n  Node* head{nullptr};\n  ssize_t size = 0;\n  while (tail) {\n    head = std::exchange(tail, std::exchange(tail->next, head));\n    ++size;\n  }\n  return Queue(head, size);\n}\n\ntemplate <typename Task>\nAtomicNotificationQueue<Task>::AtomicQueue::~AtomicQueue() {\n  DCHECK(!head_);\n  if (reinterpret_cast<intptr_t>(head_.load(std::memory_order_relaxed)) ==\n      kQueueArmedTag) {\n    return;\n  }\n  if (auto head = head_.load(std::memory_order_acquire)) {\n    auto queueContentsToDrop = Queue::fromReversed(head);\n  }\n}\n\ntemplate <typename Task>\ntemplate <typename... Args>\nbool AtomicNotificationQueue<Task>::AtomicQueue::pushImpl(\n    std::shared_ptr<RequestContext> rctx, Args&&... args) {\n  std::unique_ptr<Node> node(\n      new Node(std::move(rctx), std::forward<Args>(args)...));\n  auto head = head_.load(std::memory_order_relaxed);\n  while (true) {\n    node->next =\n        reinterpret_cast<intptr_t>(head) == kQueueArmedTag ? nullptr : head;\n    if (head_.compare_exchange_weak(\n            head,\n            node.get(),\n            std::memory_order_acq_rel,\n            std::memory_order_relaxed)) {\n      node.release();\n      return reinterpret_cast<intptr_t>(head) == kQueueArmedTag;\n    }\n  }\n}\n\ntemplate <typename Task>\ntemplate <typename... Args>\nbool AtomicNotificationQueue<Task>::AtomicQueue::push(Args&&... args) {\n  auto rctx = RequestContext::saveContext();\n  return pushImpl(std::move(rctx), std::forward<Args>(args)...);\n}\n\ntemplate <typename Task>\ntemplate <typename... Args>\nbool AtomicNotificationQueue<Task>::AtomicQueue::push(\n    std::shared_ptr<RequestContext> rctx, Args&&... args) {\n  return pushImpl(std::move(rctx), std::forward<Args>(args)...);\n}\n\ntemplate <typename Task>\nbool AtomicNotificationQueue<Task>::AtomicQueue::hasTasks() const {\n  auto head = head_.load(std::memory_order_relaxed);\n  return head && reinterpret_cast<intptr_t>(head) != kQueueArmedTag;\n}\n\ntemplate <typename Task>\ntypename AtomicNotificationQueue<Task>::Queue\nAtomicNotificationQueue<Task>::AtomicQueue::getTasks() {\n  auto head = head_.exchange(nullptr, std::memory_order_acquire);\n  if (head && reinterpret_cast<intptr_t>(head) != kQueueArmedTag) {\n    return Queue::fromReversed(head);\n  }\n  return {};\n}\n\ntemplate <typename Task>\ntypename AtomicNotificationQueue<Task>::Queue\nAtomicNotificationQueue<Task>::AtomicQueue::arm() {\n  auto head = head_.load(std::memory_order_relaxed);\n  if (!head &&\n      head_.compare_exchange_strong(\n          head,\n          reinterpret_cast<Node*>(kQueueArmedTag),\n          std::memory_order_release,\n          std::memory_order_relaxed)) {\n    return {};\n  }\n  DCHECK(reinterpret_cast<intptr_t>(head) != kQueueArmedTag);\n  return getTasks();\n}\n\ntemplate <typename Task>\nAtomicNotificationQueue<Task>::AtomicNotificationQueue() {}\n\ntemplate <typename Task>\nAtomicNotificationQueue<Task>::~AtomicNotificationQueue() {\n  // Empty the queue\n  atomicQueue_.getTasks();\n}\n\ntemplate <typename Task>\nuint32_t AtomicNotificationQueue<Task>::getMaxReadAtOnce() const {\n  return maxReadAtOnce_;\n}\n\ntemplate <typename Task>\nvoid AtomicNotificationQueue<Task>::setMaxReadAtOnce(uint32_t maxAtOnce) {\n  maxReadAtOnce_ = maxAtOnce;\n}\n\ntemplate <typename Task>\nsize_t AtomicNotificationQueue<Task>::size() const {\n  auto queueSize = pushCount_.load(std::memory_order_relaxed) -\n      taskExecuteCount_.load(std::memory_order_relaxed);\n  return queueSize >= 0 ? queueSize : 0;\n}\n\ntemplate <typename Task>\nbool AtomicNotificationQueue<Task>::empty() const {\n  return queue_.empty() && !atomicQueue_.hasTasks();\n}\n\ntemplate <typename Task>\nbool AtomicNotificationQueue<Task>::arm() {\n  if (!queue_.empty()) {\n    return false;\n  }\n  auto queue = atomicQueue_.arm();\n  if (queue.empty()) {\n    return true;\n  } else {\n    queue_ = std::move(queue);\n    return false;\n  }\n}\n\ntemplate <typename Task>\ntemplate <typename... Args>\nbool AtomicNotificationQueue<Task>::push(Args&&... args) {\n  pushCount_.fetch_add(1, std::memory_order_relaxed);\n  return atomicQueue_.push(std::forward<Args>(args)...);\n}\n\ntemplate <typename Task>\ntemplate <typename... Args>\nbool AtomicNotificationQueue<Task>::push(\n    std::shared_ptr<RequestContext> rctx, Args&&... args) {\n  pushCount_.fetch_add(1, std::memory_order_relaxed);\n  return atomicQueue_.push(std::move(rctx), std::forward<Args>(args)...);\n}\n\ntemplate <typename Task>\ntypename AtomicNotificationQueue<Task>::TryPushResult\nAtomicNotificationQueue<Task>::tryPush(Task&& task, uint32_t maxSize) {\n  auto pushed = pushCount_.load(std::memory_order_relaxed);\n  while (true) {\n    auto executed = taskExecuteCount_.load(std::memory_order_relaxed);\n    if (pushed - executed >= maxSize) {\n      return TryPushResult::FAILED_LIMIT_REACHED;\n    }\n    if (pushCount_.compare_exchange_weak(\n            pushed,\n            pushed + 1,\n            std::memory_order_relaxed,\n            std::memory_order_relaxed)) {\n      break;\n    }\n  }\n  return atomicQueue_.push(std::move(task))\n      ? TryPushResult::SUCCESS_AND_ARMED\n      : TryPushResult::SUCCESS;\n}\n\nnamespace detail {\ntemplate <\n    typename Task,\n    typename Consumer,\n    typename = std::enable_if_t<std::is_same<\n        invoke_result_t<Consumer, Task&&>,\n        AtomicNotificationQueueTaskStatus>::value>>\nAtomicNotificationQueueTaskStatus invokeConsumerWithTask(\n    Consumer&& consumer, Task&& task, std::shared_ptr<RequestContext>&& rctx) {\n  RequestContextScopeGuard rcsg(std::move(rctx));\n  return consumer(std::forward<Task>(task));\n}\n\ntemplate <\n    typename Task,\n    typename Consumer,\n    typename = std::enable_if_t<std::is_same<\n        invoke_result_t<Consumer, Task&&, std::shared_ptr<RequestContext>&&>,\n        AtomicNotificationQueueTaskStatus>::value>,\n    typename = void>\nAtomicNotificationQueueTaskStatus invokeConsumerWithTask(\n    Consumer&& consumer, Task&& task, std::shared_ptr<RequestContext>&& rctx) {\n  return consumer(\n      std::forward<Task>(task),\n      std::forward<std::shared_ptr<RequestContext>>(rctx));\n}\n\ntemplate <\n    typename Task,\n    typename Consumer,\n    typename = std::enable_if_t<\n        std::is_same<invoke_result_t<Consumer, Task&&>, void>::value>,\n    typename = void,\n    typename = void>\nAtomicNotificationQueueTaskStatus invokeConsumerWithTask(\n    Consumer&& consumer, Task&& task, std::shared_ptr<RequestContext>&& rctx) {\n  RequestContextScopeGuard rcsg(std::move(rctx));\n  consumer(std::forward<Task>(task));\n  return AtomicNotificationQueueTaskStatus::CONSUMED;\n}\n\ntemplate <\n    typename Task,\n    typename Consumer,\n    typename = std::enable_if_t<std::is_same<\n        invoke_result_t<Consumer, Task&&, std::shared_ptr<RequestContext>&&>,\n        void>::value>,\n    typename = void,\n    typename = void,\n    typename = void>\nAtomicNotificationQueueTaskStatus invokeConsumerWithTask(\n    Consumer&& consumer, Task&& task, std::shared_ptr<RequestContext>&& rctx) {\n  consumer(\n      std::forward<Task>(task),\n      std::forward<std::shared_ptr<RequestContext>>(rctx));\n  return AtomicNotificationQueueTaskStatus::CONSUMED;\n}\n\n} // namespace detail\n\ntemplate <typename Task>\ntemplate <typename Consumer>\nbool AtomicNotificationQueue<Task>::drive(Consumer&& consumer) {\n  auto* atomicQueueLastTask = atomicQueue_.peekHead();\n  bool queueRefilled = false;\n  bool wasAnyProcessed = false;\n  bool stop = false;\n  for (uint32_t numConsumed = 0;\n       !stop && (maxReadAtOnce_ == 0 || numConsumed < maxReadAtOnce_);) {\n    if (queue_.empty()) {\n      queue_ = atomicQueue_.getTasks();\n      if (queue_.empty()) {\n        break;\n      }\n      DCHECK(!queueRefilled); // We should have already stopped.\n      queueRefilled = true;\n    }\n    wasAnyProcessed = true;\n    // This is faster than fetch_add and is safe because only consumer thread\n    // writes to taskExecuteCount_.\n    taskExecuteCount_.store(\n        taskExecuteCount_.load(std::memory_order_relaxed) + 1,\n        std::memory_order_relaxed);\n    {\n      auto& curNode = queue_.front();\n      // If we reached the last task in atomicQueue_ at the time drive() was\n      // called, stop after processing this task.\n      if (queueRefilled &&\n          (atomicQueueLastTask == nullptr || &curNode == atomicQueueLastTask)) {\n        stop = true;\n      }\n\n      AtomicNotificationQueueTaskStatus consumeTaskStatus =\n          detail::invokeConsumerWithTask(\n              std::forward<Consumer>(consumer),\n              std::move(curNode.task),\n              std::move(curNode.rctx));\n\n      switch (consumeTaskStatus) {\n        case AtomicNotificationQueueTaskStatus::CONSUMED_STOP:\n          stop = true;\n          [[fallthrough]];\n        case AtomicNotificationQueueTaskStatus::CONSUMED:\n          ++numConsumed;\n          break;\n        case AtomicNotificationQueueTaskStatus::DISCARD:\n          break;\n      }\n    }\n    queue_.pop();\n  }\n  return wasAnyProcessed;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/AtomicNotificationQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include <folly/io/async/Request.h>\n#include <folly/lang/Align.h>\n\nnamespace folly {\n\n/**\n * A producer-consumer queue for passing tasks to consumer thread.\n *\n * Users of this class are expected to implement functionality to wakeup\n * consumer thread.\n */\ntemplate <typename Task>\nclass AtomicNotificationQueue {\n public:\n  explicit AtomicNotificationQueue();\n  ~AtomicNotificationQueue();\n\n  /*\n   * Get the maximum number of tasks processed in a single round.\n   * Can be called from any thread as long as it's externally synchronized with\n   * setMaxReadAtOnce.\n   */\n  uint32_t getMaxReadAtOnce() const;\n\n  /*\n   * Set the maximum number of tasks processed in a single round.\n   * Can be called from consumer thread only.\n   */\n  void setMaxReadAtOnce(uint32_t maxAtOnce);\n\n  /*\n   * Returns the number of tasks in the queue.\n   * Can be called from any thread.\n   */\n  size_t size() const;\n\n  /*\n   * Checks if the queue is empty.\n   * Can be called from consumer thread only.\n   */\n  bool empty() const;\n\n  /*\n   * Tries to arm the queue.\n   * 1) If the queue was empty: the queue becomes armed and true is returned.\n   * 2) Otherwise returns false.\n   * Can be called from consumer thread only.\n   */\n  bool arm();\n\n  /*\n   * Executes one round of tasks. Returns true iff tasks were run.\n   * Can be called from consumer thread only.\n   *\n   * `Consumer::operator()` must accept `Task&&` as its first parameter.\n   * It may also optionally accept `std::shared_ptr<folly::RequestContext>&&` as\n   * its second parameter, in which case it must manage `folly::RequestContext`\n   * for the consumed task.\n   *\n   * Consumer::operator() can optionally return\n   * AtomicNotificationQueueTaskStatus to indicate if the provided task should\n   * be considered consumed or discarded. Discarded tasks are not counted\n   * towards `maxReadAtOnce`.\n   */\n  template <typename Consumer>\n  bool drive(Consumer&& consumer);\n\n  /*\n   * Adds a task into the queue.\n   * Can be called from any thread.\n   * Returns true iff the queue was armed, in which case\n   * producers are expected to notify consumer thread.\n   */\n  template <typename... Args>\n  bool push(Args&&... args);\n\n  /*\n   * Same as above\n   * but RequestContext is passed from caller\n   */\n  template <typename... Args>\n  bool push(std::shared_ptr<RequestContext> rctx, Args&&... args);\n\n  /*\n   * Attempts adding a task into the queue.\n   * Can be called from any thread.\n   * Similarly to push(), producers are expected to notify\n   * consumer iff SUCCESS_AND_ARMED is returned.\n   */\n  enum class TryPushResult { FAILED_LIMIT_REACHED, SUCCESS, SUCCESS_AND_ARMED };\n  TryPushResult tryPush(Task&& task, uint32_t maxSize);\n\n private:\n  struct Node {\n    Task task;\n    std::shared_ptr<RequestContext> rctx;\n\n   private:\n    friend class AtomicNotificationQueue;\n\n    template <typename... Args>\n    explicit Node(\n        std::shared_ptr<RequestContext> requestContext, Args&&... args)\n        : task(std::forward<Args>(args)...), rctx(std::move(requestContext)) {}\n\n    Node* next{};\n  };\n\n  class AtomicQueue;\n  class Queue {\n   public:\n    Queue() {}\n    Queue(Queue&& other) noexcept;\n    Queue& operator=(Queue&& other) noexcept;\n    ~Queue();\n\n    bool empty() const;\n    ssize_t size() const;\n    Node& front();\n    void pop();\n    void clear();\n\n   private:\n    friend class AtomicNotificationQueue::AtomicQueue;\n\n    Queue(Node* head, ssize_t size);\n    static Queue fromReversed(Node* tail);\n\n    Node* head_{nullptr};\n    ssize_t size_{0};\n  };\n\n  /**\n   * Lock-free queue implementation.\n   * The queue can be in 3 states:\n   *   1) Empty\n   *   2) Armed\n   *   3) Non-empty (1 or more tasks in it)\n   *\n   * This diagram shows possible state transitions:\n   *\n   * +---------+         successful arm          +-------------+\n   * |         |  +---------- arm() ---------->  |             |\n   * |  Empty  |                                 |    Armed    | +-+\n   * |         |  <------- getTasks() --------+  |             |   |\n   * +-+--+----+         consumer disarm         +-------------+   |\n   *   |  ^                                                        |\n   *   |  |                                                        |\n   *   |  | consumer pull                               armed push v\n   *   |  |                                                        |\n   *   |  |                 +-------------------+                  |\n   *   v  +- getTasks() -+  |                   |                  |\n   *   |  |                 |     Non-empty     |  <---- push()----+\n   *   |  ^---- arm() ---+  |                   |\n   *   |                    +-+--+------------+-+\n   *   |                      ^  ^            |\n   *   |                      |  |            |\n   *   +------- push() -------^  ^-- push() --+\n   *                 disarmed push\n   *\n   * push() can be called in any state. It always transitions the queue into\n   * Non-empty:\n   *   When Armed - push() returns true\n   *   When Empty/Non-empty - push() returns false\n   *\n   * getTasks() can be called in any state. It always transitions the queue into\n   * Empty.\n   *\n   * arm() can't be called if the queue is already in Armed state:\n   *   When Empty - arm() returns an empty queue and transitions into Armed\n   *   When Non-Empty: equivalent to getTasks()\n   *\n   */\n  class AtomicQueue {\n   public:\n    AtomicQueue() {}\n    ~AtomicQueue();\n    AtomicQueue(const AtomicQueue&) = delete;\n    AtomicQueue& operator=(const AtomicQueue&) = delete;\n\n    /*\n     * Pushes a task into the queue. Returns true iff the queue was armed.\n     * Can be called from any thread.\n     */\n    template <typename... Args>\n    bool push(Args&&... args);\n\n    /*\n     * Same as above\n     * but RequestContext is passed from caller\n     */\n    template <typename... Args>\n    bool push(std::shared_ptr<RequestContext> rctx, Args&&... args);\n\n    /*\n     * Returns true if the queue has tasks.\n     * Can be called from any thread.\n     */\n    bool hasTasks() const;\n\n    /*\n     * Returns all tasks currently in the queue (in FIFO order). Queue becomes\n     * empty.\n     * Can be called from consumer thread only.\n     */\n    Queue getTasks();\n\n    /*\n     * Returns a pointer to the last added node, or nullptr if the queue is\n     * empty. This pointer should not be dereferenced, it should only be used to\n     * mark a position in the queue.\n     */\n    Node* peekHead() const {\n      auto* n = head_.load(std::memory_order_relaxed);\n      return reinterpret_cast<intptr_t>(n) == kQueueArmedTag ? nullptr : n;\n    }\n\n    /*\n     * Tries to arm the queue.\n     * 1) If the queue was empty: the queue becomes armed and an empty queue is\n     * returned.\n     * 2) If the queue wasn't empty: acts as getTasks().\n     * Can be called from consumer thread only.\n     */\n    Queue arm();\n\n   private:\n    template <typename... Args>\n    bool pushImpl(std::shared_ptr<RequestContext> rctx, Args&&... args);\n\n    alignas(folly::cacheline_align_v) std::atomic<Node*> head_{};\n    static constexpr intptr_t kQueueArmedTag = 1;\n  };\n\n private:\n  alignas(folly::cacheline_align_v) std::atomic<ssize_t> pushCount_{0};\n  AtomicQueue atomicQueue_;\n  Queue queue_;\n  std::atomic<ssize_t> taskExecuteCount_{0};\n  uint32_t maxReadAtOnce_{10};\n};\n\n/**\n * Consumer::operator() can optionally return AtomicNotificationQueueTaskStatus\n * to indicate if the provided task should be considered consumed or\n * discarded. Discarded tasks are not counted towards maxReadAtOnce_.\n */\nenum class AtomicNotificationQueueTaskStatus : uint8_t {\n  // The dequeued task was consumed and should be counted as such\n  CONSUMED,\n  // Same as CONSUMED, but drive() will stop early even if maxReadAtOnce_ wasn't\n  // reached\n  CONSUMED_STOP,\n  // The dequeued task should be discarded and the queue not count it as\n  // consumed\n  DISCARD,\n};\n\n} // namespace folly\n\n#include <folly/io/async/AtomicNotificationQueue-inl.h>\n"
  },
  {
    "path": "folly/io/async/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"async_base_fwd\",\n    headers = [\n        \"HHWheelTimer-fwd.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_base\",\n    srcs = [\n        \"AsyncTimeout.cpp\",\n        \"EventBase.cpp\",\n        \"EventBaseBackendBase.cpp\",\n        \"EventBaseLocal.cpp\",\n        \"EventHandler.cpp\",\n        \"HHWheelTimer.cpp\",\n        \"TimeoutManager.cpp\",\n        \"VirtualEventBase.cpp\",\n    ],\n    headers = [\n        \"AsyncTimeout.h\",\n        \"AtomicNotificationQueue.h\",\n        \"AtomicNotificationQueue-inl.h\",\n        \"EventBase.h\",\n        \"EventBaseAtomicNotificationQueue.h\",\n        \"EventBaseAtomicNotificationQueue-inl.h\",\n        \"EventBaseBackendBase.h\",\n        \"EventBaseLocal.h\",\n        \"EventHandler.h\",\n        \"HHWheelTimer.h\",\n        \"NotificationQueue.h\",\n        \"TimeoutManager.h\",\n        \"VirtualEventBase.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:chrono\",\n        \"//folly:string\",\n        \"//folly/container:bit_iterator\",\n        \"//folly/lang:assume\",\n        \"//folly/lang:bits\",\n        \"//folly/synchronization:event_count\",\n        \"//folly/system:thread_id\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":async_base_fwd\",\n        \":delayed_destruction\",\n        \":event_util\",\n        \":request_context\",\n        \"//folly:exception\",\n        \"//folly:exception_string\",\n        \"//folly:executor\",\n        \"//folly:file_util\",\n        \"//folly:function\",\n        \"//folly:likely\",\n        \"//folly:memory\",\n        \"//folly:optional\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:spin_lock\",\n        \"//folly:synchronized\",\n        \"//folly/container:f14_hash\",\n        \"//folly/executors:drivable_executor\",\n        \"//folly/executors:execution_observer\",\n        \"//folly/executors:io_executor\",\n        \"//folly/executors:queue_observer\",\n        \"//folly/executors:scheduled_executor\",\n        \"//folly/executors:sequenced_executor\",\n        \"//folly/io:iobuf\",\n        \"//folly/lang:align\",\n        \"//folly/lang:thunk\",\n        \"//folly/net:net_ops\",\n        \"//folly/net:network_socket\",\n        \"//folly/portability:config\",\n        \"//folly/portability:event\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:iovec\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:unistd\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:call_once\",\n        \"//folly/system:pid\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_pipe\",\n    srcs = [\"AsyncPipe.cpp\"],\n    headers = [\"AsyncPipe.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":async_socket_exception\",\n        \"//folly:file_util\",\n        \"//folly:utility\",\n        \"//folly/detail:file_util_detail\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \":async_transport\",\n        \":delayed_destruction\",\n        \"//folly/io:iobuf\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_signal_handler\",\n    srcs = [\"AsyncSignalHandler.cpp\"],\n    headers = [\"AsyncSignalHandler.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:conv\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \"//folly/portability:event\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_socket_transport\",\n    srcs = [\"AsyncSocketTransport.cpp\"],\n    headers = [\"AsyncSocketTransport.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_socket_exception\",\n        \":async_transport\",\n        \"//folly:network_address\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/net:network_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_socket\",\n    srcs = [\"AsyncSocket.cpp\"],\n    headers = [\"AsyncSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \":io_uring_backend\",\n        \"//folly:exception\",\n        \"//folly:overload\",\n        \"//folly:portability\",\n        \"//folly:string\",\n        \"//folly/lang:checked_math\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:sys_uio\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \":async_socket_exception\",\n        \":async_socket_transport\",\n        \":async_transport\",\n        \":delayed_destruction\",\n        \":io_uring_connect\",\n        \":io_uring_recv\",\n        \":io_uring_send\",\n        \"//folly:constructor_callback_list\",\n        \"//folly:network_address\",\n        \"//folly:optional\",\n        \"//folly:small_vector\",\n        \"//folly/detail:socket_fast_open\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:shutdown_socket_set\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/io/async/observer:async_socket_observer_container\",\n        \"//folly/net:net_ops_dispatcher\",\n        \"//folly/net:tcpinfo\",\n        \"//folly/net:tcpinfo_dispatcher\",\n        \"//folly/portability:sockets\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_preprocessor\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_socket_base\",\n    headers = [\"AsyncSocketBase.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_base\",\n        \"//folly:network_address\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_connect\",\n    srcs = [\"IoUringConnect.cpp\"],\n    headers = [\"IoUringConnect.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_base\",\n        \":io_uring_backend\",\n        \"//folly/net:network_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_recv\",\n    srcs = [\"IoUringRecv.cpp\"],\n    headers = [\"IoUringRecv.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":async_base\",\n        \":async_socket_exception\",\n    ],\n    exported_deps = [\n        \":async_transport\",\n        \":delayed_destruction\",\n        \":io_uring_backend\",\n        \"//folly:network_address\",\n        \"//folly/futures:core\",\n        \"//folly/net:network_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_send\",\n    srcs = [\"IoUringSend.cpp\"],\n    headers = [\"IoUringSend.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":async_base\",\n    ],\n    exported_deps = [\n        \":async_transport\",\n        \":delayed_destruction\",\n        \":io_uring_backend\",\n        \"//folly/futures:core\",\n        \"//folly/net:network_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_socket_exception\",\n    srcs = [\"AsyncSocketException.cpp\"],\n    headers = [\"AsyncSocketException.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:format\",\n        \"//folly:string\",\n    ],\n    exported_deps = [\n        \"//folly:c_portability\",\n        \"//folly:range\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_ssl_socket\",\n    srcs = [\"AsyncSSLSocket.cpp\"],\n    headers = [\"AsyncSSLSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:indestructible\",\n        \"//folly:network_address\",\n        \"//folly:spin_lock\",\n        \"//folly/io/async/ssl:basic_transport_certificate\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \":async_pipe\",\n        \":async_socket\",\n        \":certificate_identity_verifier\",\n        \":ssl_context\",\n        \"//folly:optional\",\n        \"//folly:string\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/io/async/ssl:openssl_utils\",\n        \"//folly/io/async/ssl:ssl_errors\",\n        \"//folly/io/async/ssl:tls_definitions\",\n        \"//folly/lang:bits\",\n        \"//folly/portability:openssl\",\n        \"//folly/portability:sockets\",\n        \"//folly/ssl:openssl_ptr_types\",\n        \"//folly/ssl:ssl_session\",\n        \"//folly/ssl:ssl_session_manager\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_transport\",\n    headers = [\"AsyncTransport.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_base\",\n        \":async_socket_base\",\n        \":async_transport_certificate\",\n        \":delayed_destruction\",\n        \":write_flags\",\n        \"//folly:optional\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:openssl\",\n        \"//folly/portability:sys_uio\",\n        \"//folly/ssl:openssl_ptr_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_transport_certificate\",\n    headers = [\"AsyncTransportCertificate.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"certificate_identity_verifier\",\n    headers = [\"CertificateIdentityVerifier.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_transport_certificate\",\n        \"//folly/portability:openssl\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_udp_server_socket\",\n    headers = [\"AsyncUDPServerSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_base\",\n        \":async_udp_socket\",\n        \"//folly:memory\",\n        \"//folly/io:iobuf\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_udp_socket\",\n    srcs = [\"AsyncUDPSocket.cpp\"],\n    headers = [\"AsyncUDPSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:likely\",\n        \"//folly:small_vector\",\n        \"//folly:utility\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \":async_socket_base\",\n        \":async_socket_exception\",\n        \"//folly:function\",\n        \"//folly:network_address\",\n        \"//folly:scope_guard\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/net:net_ops\",\n        \"//folly/net:net_ops_dispatcher\",\n        \"//folly/net:network_socket\",\n    ],\n    external_deps = [\n        (\"boost\", None, \"boost_preprocessor\"),\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"decorated_async_transport_wrapper\",\n    headers = [\n        \"DecoratedAsyncTransportWrapper.h\",\n        \"WriteChainAsyncTransportWrapper.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_transport\",\n        \"//folly/io:iobuf\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"delayed_destruction\",\n    srcs = [\n        \"DelayedDestruction.cpp\",\n    ],\n    headers = [\n        \"DelayedDestruction.h\",\n        \"DelayedDestructionBase.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:portability\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"destructor_check\",\n    headers = [\"DestructorCheck.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"event_base_manager\",\n    srcs = [\"EventBaseManager.cpp\"],\n    headers = [\"EventBaseManager.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_base\",\n        \"//folly:optional\",\n        \"//folly:thread_local\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"event_base_thread\",\n    srcs = [\"EventBaseThread.cpp\"],\n    headers = [\"EventBaseThread.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":scoped_event_base_thread\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \"//folly:range\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"event_util\",\n    headers = [\"EventUtil.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/portability:event\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"request_context\",\n    srcs = [\"Request.cpp\"],\n    headers = [\"Request.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:glog\",\n        \"//folly/concurrency/container:single_writer_fixed_hash_map\",\n        \"//folly/tracing:static_tracepoint\",\n    ],\n    exported_deps = [\n        \"//folly:shared_mutex\",\n        \"//folly:singleton_thread_local\",\n        \"//folly:synchronized\",\n        \"//folly/concurrency:process_local_unique_id\",\n        \"//folly/container:f14_hash\",\n        \"//folly/detail:iterators\",\n        \"//folly/synchronization:hazptr\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"scoped_event_base_thread\",\n    srcs = [\"ScopedEventBaseThread.cpp\"],\n    headers = [\"ScopedEventBaseThread.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":event_base_manager\",\n        \"//folly:function\",\n        \"//folly:range\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"terminate_cancellation_token\",\n    srcs = [\"TerminateCancellationToken.cpp\"],\n    headers = [\"TerminateCancellationToken.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":async_signal_handler\",\n        \":scoped_event_base_thread\",\n        \"//folly:singleton\",\n    ],\n    exported_deps = [\n        \"//folly:cancellation_token\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"server_socket\",\n    srcs = [\"AsyncServerSocket.cpp\"],\n    headers = [\"AsyncServerSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:file_util\",\n        \"//folly:glog\",\n        \"//folly:portability\",\n        \"//folly/detail:socket_fast_open\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \":async_socket_base\",\n        \":delayed_destruction\",\n        \"//folly:exception_wrapper\",\n        \"//folly:network_address\",\n        \"//folly:string\",\n        \"//folly/io:shutdown_socket_set\",\n        \"//folly/net:net_ops\",\n        \"//folly/net:network_socket\",\n        \"//folly/observer:observer\",\n        \"//folly/portability:sockets\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"ssl_context\",\n    srcs = [\"SSLContext.cpp\"],\n    headers = [\"SSLContext.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:format\",\n        \"//folly:memory\",\n        \"//folly:random\",\n        \"//folly:shared_mutex\",\n        \"//folly/ssl:openssl_ticket_handler\",\n        \"//folly/ssl:password_collector\",\n        \"//folly/ssl:ssl_session_manager\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:function\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly:string\",\n        \"//folly/container:access\",\n        \"//folly/io/async/ssl:openssl_utils\",\n        \"//folly/portability:openssl\",\n        \"//folly/ssl:openssl_ptr_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"ssl_options\",\n    srcs = [\"SSLOptions.cpp\"],\n    headers = [\"SSLOptions.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:format\",\n    ],\n    exported_deps = [\n        \":ssl_context\",\n        \"//folly/container:array\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"password_in_file\",\n    srcs = [\"PasswordInFile.cpp\"],\n    headers = [\"PasswordInFile.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:file_util\",\n        \"//folly/portability:openssl\",\n    ],\n    exported_deps = [\n        \"//folly/ssl:password_collector\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"write_flags\",\n    headers = [\"WriteFlags.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"timerfd\",\n    srcs = [\n        \"STTimerFDTimeoutManager.cpp\",\n        \"TimerFD.cpp\",\n        \"TimerFDTimeoutManager.cpp\",\n    ],\n    headers = [\n        \"STTimerFDTimeoutManager.h\",\n        \"TimerFD.h\",\n        \"TimerFDTimeoutManager.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:file_util\",\n        \"//folly/io/async:event_util\",\n    ],\n    exported_deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:delayed_destruction\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_base_class\",\n    srcs = [\"AsyncBase.cpp\"],\n    headers = [\"AsyncBase.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:exception\",\n        \"//folly:format\",\n        \"//folly/portability:filesystem\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly:portability\",\n        \"//folly:range\",\n        \"//folly/portability:sys_uio\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_io\",\n    srcs = [\"AsyncIO.cpp\"],\n    headers = [\"AsyncIO.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:exception\",\n        \"//folly:likely\",\n        \"//folly:small_vector\",\n        \"//folly:string\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \"//folly/io/async:async_base_class\",\n    ],\n    external_deps = [\n        \"boost\",\n    ],\n    exported_external_deps = [\n        (\"libaio\", None, \"aio\"),\n    ],\n)\n\nliburing_deps = select({\n    \"DEFAULT\": [],\n    \"ovr_config//os:linux\": select({\n        \"DEFAULT\": [\"fbsource//third-party/liburing:uring\"],\n        # @fb-only[end= ]: \"ovr_config//os:linux-sgx\": [],\n        # Kernel headers in this platform are too old for uring.\n        \"ovr_config//runtime/constraints:anywhere-linux\": [],\n    }),\n})\n\nfb_dirsync_cpp_library(\n    name = \"liburing\",\n    headers = [\"Liburing.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = liburing_deps,\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_io_uring_socket\",\n    srcs = [\n        \"AsyncIoUringSocket.cpp\",\n        \"AsyncIoUringSocketFactory.cpp\",\n    ],\n    headers = [\n        \"AsyncIoUringSocket.h\",\n        \"AsyncIoUringSocketFactory.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":io_uring_event_base_local\",\n        \":io_uring_provided_buffer_ring\",\n        \"//folly:conv\",\n        \"//folly:random\",\n        \"//folly/detail:socket_fast_open\",\n        \"//folly/memory:malloc\",\n        \"//folly/portability:sys_uio\",\n    ],\n    exported_deps = [\n        \":async_base\",\n        \":async_socket\",\n        \":async_socket_exception\",\n        \":async_transport\",\n        \":delayed_destruction\",\n        \":io_uring_backend\",\n        \":liburing\",\n        \"//folly:network_address\",\n        \"//folly:optional\",\n        \"//folly:small_vector\",\n        \"//folly/futures:core\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/net:net_ops_dispatcher\",\n        \"//folly/portability:sockets\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"simple_async_io\",\n    srcs = [\"SimpleAsyncIO.cpp\"],\n    headers = [\"SimpleAsyncIO.h\"],\n    enable_static_variant = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:string\",\n        \"//folly/coro:baton\",\n        \"//folly/io/async:async_io\",\n        \"//folly/io/async:io_uring\",\n        \"//folly/io/async:liburing\",\n        \"//folly/portability:sockets\",\n    ],\n    exported_deps = [\n        \"//folly:synchronized\",\n        \"//folly/coro:task\",\n        \"//folly/executors:global_executor\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_base_class\",\n        \"//folly/io/async:scoped_event_base_thread\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"epoll\",\n    headers = [\n        \"Epoll.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"epoll_backend\",\n    srcs = [\n        \"EpollBackend.cpp\",\n    ],\n    headers = [\n        \"Epoll.h\",\n        \"EpollBackend.h\",\n    ],\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:file_util\",\n        \"//folly:intrusive_list\",\n        \"//folly:map_util\",\n        \"//folly:string\",\n    ],\n    exported_deps = [\n        \"//folly/container:intrusive_heap\",\n        \"//folly/io/async:async_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"event_base_poller\",\n    srcs = [\"EventBasePoller.cpp\"],\n    headers = [\"EventBasePoller.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:file_util\",\n        \"//folly:string\",\n        \"//folly/io/async:epoll\",\n        \"//folly/io/async:liburing\",\n        \"//folly/lang:align\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:baton\",\n        \"//folly/system:thread_name\",\n    ],\n    exported_deps = [\n        \"//folly:function\",\n        \"//folly:range\",\n        \"//folly:synchronized\",\n    ],\n    external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"mux_io_thread_pool_executor\",\n    srcs = [\"MuxIOThreadPoolExecutor.cpp\"],\n    headers = [\"MuxIOThreadPoolExecutor.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly/container:enumerate\",\n        \"//folly/io/async:epoll_backend\",\n        \"//folly/lang:align\",\n        \"//folly/synchronization:latch\",\n    ],\n    exported_deps = [\n        \"//folly:portability\",\n        \"//folly/concurrency:unbounded_queue\",\n        \"//folly/executors:io_thread_pool_executor\",\n        \"//folly/executors:queue_observer\",\n        \"//folly/io/async:event_base_manager\",\n        \"//folly/io/async:event_base_poller\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/synchronization:throttled_lifo_sem\",\n        \"//folly/synchronization:wait_options\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring\",\n    srcs = [\"IoUring.cpp\"],\n    headers = [\"IoUring.h\"],\n    enable_static_variant = False,\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:exception\",\n        \"//folly:string\",\n        \"//folly/portability:unistd\",\n    ],\n    exported_deps = [\n        \":async_base_class\",\n        \":liburing\",\n        \"//folly:shared_mutex\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_backend\",\n    srcs = [\n        \"IoUringBackend.cpp\",\n    ],\n    headers = [\n        \"IoUringBackend.h\",\n        \"IoUringBase.h\",\n        \"IoUringOptions.h\",\n    ],\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:demangle\",\n        \"//folly:file_util\",\n        \"//folly:glog\",\n        \"//folly:likely\",\n        \"//folly:spin_lock\",\n        \"//folly:string\",\n        \"//folly/container:f14_hash\",\n        \"//folly/lang:bits\",\n        \"//folly/portability:gflags\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/portability:sys_syscall\",\n        \"//folly/synchronization:call_once\",\n        \"//folly/tracing:static_tracepoint\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":async_base\",\n        \":delayed_destruction\",\n        \":io_uring_provided_buffer_ring\",\n        \":io_uring_zero_copy_buffer_pool\",\n        \":liburing\",\n        \"//folly:c_portability\",\n        \"//folly:conv\",\n        \"//folly:cpp_attributes\",\n        \"//folly:exception_string\",\n        \"//folly:function\",\n        \"//folly:network_address\",\n        \"//folly:optional\",\n        \"//folly:range\",\n        \"//folly:small_vector\",\n        \"//folly/io:iobuf\",\n        \"//folly/portability:asm\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_provided_buffer_ring\",\n    srcs = [\n        \"IoUringProvidedBufferRing.cpp\",\n    ],\n    headers = [\n        \"IoUringBase.h\",\n        \"IoUringProvidedBufferRing.h\",\n    ],\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:conv\",\n        \"//folly:string\",\n        \"//folly/lang:align\",\n        \"//folly/portability:sys_mman\",\n    ],\n    exported_deps = [\n        \":delayed_destruction\",\n        \":liburing\",\n        \"//folly/io:iobuf\",\n        \"//folly/synchronization:distributed_mutex\",\n    ],\n    exported_external_deps = [\n        \"boost\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_zero_copy_buffer_pool\",\n    srcs = [\n        \"IoUringZeroCopyBufferPool.cpp\",\n    ],\n    headers = [\n        \"IoUringZeroCopyBufferPool.h\",\n    ],\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:conv\",\n        \"//folly:string\",\n        \"//folly/lang:align\",\n        \"//folly/portability:sys_mman\",\n        \"//folly/synchronization:distributed_mutex\",\n    ],\n    exported_deps = [\n        \":liburing\",\n        \"//folly/io:iobuf\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_event\",\n    srcs = [\n        \"IoUringEvent.cpp\",\n    ],\n    headers = [\n        \"IoUringEvent.h\",\n    ],\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:file\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/io/async:liburing\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_event_base_local\",\n    srcs = [\n        \"IoUringEventBaseLocal.cpp\",\n    ],\n    headers = [\n        \"IoUringEventBaseLocal.h\",\n    ],\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:singleton\",\n        \"//folly/io/async:io_uring_event\",\n    ],\n    exported_deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/io/async:liburing\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"io_uring_buffer_pool_sharing\",\n    srcs = [\n        \"IoUringBufferPoolSharing.cpp\",\n    ],\n    headers = [\n        \"IoUringBufferPoolSharing.h\",\n    ],\n    modular_headers = False,\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \":io_uring_backend\",\n        \":liburing\",\n        \"//folly:function\",\n    ],\n    exported_deps = [\n        \":async_base\",\n    ],\n    external_deps = [\n        \"glog\",\n    ],\n)\n"
  },
  {
    "path": "folly/io/async/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async_base\n  SRCS\n    AsyncTimeout.cpp\n    EventBase.cpp\n    EventBaseBackendBase.cpp\n    EventBaseLocal.cpp\n    EventHandler.cpp\n    HHWheelTimer.cpp\n    TimeoutManager.cpp\n    VirtualEventBase.cpp\n  HEADERS\n    AsyncTimeout.h\n    AtomicNotificationQueue-inl.h\n    AtomicNotificationQueue.h\n    EventBase.h\n    EventBaseAtomicNotificationQueue-inl.h\n    EventBaseAtomicNotificationQueue.h\n    EventBaseBackendBase.h\n    EventBaseLocal.h\n    EventHandler.h\n    HHWheelTimer.h\n    NotificationQueue.h\n    TimeoutManager.h\n    VirtualEventBase.h\n  DEPS\n    folly_chrono\n    folly_container_bit_iterator\n    folly_lang_assume\n    folly_lang_bits\n    folly_string\n    folly_synchronization_event_count\n    folly_system_thread_id\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_container_f14_hash\n    folly_exception\n    folly_exception_string\n    folly_executor\n    folly_executors_drivable_executor\n    folly_executors_execution_observer\n    folly_executors_io_executor\n    folly_executors_queue_observer\n    folly_executors_scheduled_executor\n    folly_executors_sequenced_executor\n    folly_file_util\n    folly_function\n    folly_io_async_async_base_fwd\n    folly_io_async_delayed_destruction\n    folly_io_async_event_util\n    folly_io_async_request_context\n    folly_io_iobuf\n    folly_lang_align\n    folly_lang_thunk\n    folly_likely\n    folly_memory\n    folly_net_net_ops\n    folly_net_network_socket\n    folly_optional\n    folly_portability\n    folly_portability_config\n    folly_portability_event\n    folly_portability_fcntl\n    folly_portability_iovec\n    folly_portability_sockets\n    folly_portability_unistd\n    folly_scope_guard\n    folly_spin_lock\n    folly_synchronization_baton\n    folly_synchronization_call_once\n    folly_synchronized\n    folly_system_pid\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME async_base_class\n  SRCS\n    AsyncBase.cpp\n  HEADERS\n    AsyncBase.h\n  DEPS\n    folly_exception\n    folly_format\n    folly_portability_filesystem\n    folly_portability_unistd\n  EXPORTED_DEPS\n    folly_function\n    folly_portability\n    folly_portability_sys_uio\n    folly_range\n)\n\nfolly_add_library(\n  NAME async_base_fwd\n  HEADERS\n    HHWheelTimer-fwd.h\n)\n\nfolly_add_library(\n  NAME async_io\n  SRCS\n    AsyncIO.cpp\n  HEADERS\n    AsyncIO.h\n  DEPS\n    folly_exception\n    folly_likely\n    folly_portability_unistd\n    folly_small_vector\n    folly_string\n  EXPORTED_DEPS\n    folly_io_async_async_base_class\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME async_io_uring_socket\n  SRCS\n    AsyncIoUringSocket.cpp\n    AsyncIoUringSocketFactory.cpp\n  HEADERS\n    AsyncIoUringSocket.h\n    AsyncIoUringSocketFactory.h\n  DEPS\n    folly_conv\n    folly_detail_socket_fast_open\n    folly_io_async_io_uring_event_base_local\n    folly_io_async_io_uring_provided_buffer_ring\n    folly_memory_malloc\n    folly_portability_sys_uio\n    folly_random\n  EXPORTED_DEPS\n    folly_futures_core\n    folly_io_async_async_base\n    folly_io_async_async_socket\n    folly_io_async_async_socket_exception\n    folly_io_async_async_transport\n    folly_io_async_delayed_destruction\n    folly_io_async_io_uring_backend\n    folly_io_async_liburing\n    folly_io_iobuf\n    folly_io_socket_option_map\n    folly_net_net_ops_dispatcher\n    folly_network_address\n    folly_optional\n    folly_portability_sockets\n    folly_small_vector\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME async_pipe\n  SRCS\n    AsyncPipe.cpp\n  HEADERS\n    AsyncPipe.h\n  DEPS\n    folly_detail_file_util_detail\n    folly_file_util\n    folly_io_async_async_socket_exception\n    folly_utility\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_io_async_async_transport\n    folly_io_async_delayed_destruction\n    folly_io_iobuf\n)\n\nfolly_add_library(\n  NAME async_signal_handler\n  SRCS\n    AsyncSignalHandler.cpp\n  HEADERS\n    AsyncSignalHandler.h\n  DEPS\n    folly_conv\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_portability_event\n)\n\nfolly_add_library(\n  NAME async_socket\n  SRCS\n    AsyncSocket.cpp\n  HEADERS\n    AsyncSocket.h\n  DEPS\n    folly_exception\n    folly_io_async_io_uring_backend\n    folly_lang_checked_math\n    folly_overload\n    folly_portability\n    folly_portability_fcntl\n    folly_portability_sys_mman\n    folly_portability_sys_uio\n    folly_portability_unistd\n    folly_string\n  EXPORTED_DEPS\n    folly_constructor_callback_list\n    folly_detail_socket_fast_open\n    folly_io_async_async_base\n    folly_io_async_async_socket_exception\n    folly_io_async_async_socket_transport\n    folly_io_async_async_transport\n    folly_io_async_delayed_destruction\n    folly_io_async_io_uring_connect\n    folly_io_async_io_uring_recv\n    folly_io_async_io_uring_send\n    folly_io_async_observer_async_socket_observer_container\n    folly_io_iobuf\n    folly_io_shutdown_socket_set\n    folly_io_socket_option_map\n    folly_net_net_ops_dispatcher\n    folly_net_tcpinfo\n    folly_net_tcpinfo_dispatcher\n    folly_network_address\n    folly_optional\n    folly_portability_sockets\n    folly_small_vector\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME async_socket_base\n  HEADERS\n    AsyncSocketBase.h\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_network_address\n)\n\nfolly_add_library(\n  NAME async_socket_exception\n  SRCS\n    AsyncSocketException.cpp\n  HEADERS\n    AsyncSocketException.h\n  DEPS\n    folly_format\n    folly_string\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_range\n)\n\nfolly_add_library(\n  NAME async_socket_transport\n  SRCS\n    AsyncSocketTransport.cpp\n  HEADERS\n    AsyncSocketTransport.h\n  EXPORTED_DEPS\n    folly_io_async_async_socket_exception\n    folly_io_async_async_transport\n    folly_io_iobuf\n    folly_io_socket_option_map\n    folly_net_network_socket\n    folly_network_address\n)\n\nfolly_add_library(\n  NAME async_ssl_socket\n  SRCS\n    AsyncSSLSocket.cpp\n  HEADERS\n    AsyncSSLSocket.h\n  DEPS\n    folly_indestructible\n    folly_io_async_ssl_basic_transport_certificate\n    folly_network_address\n    folly_spin_lock\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_io_async_async_pipe\n    folly_io_async_async_socket\n    folly_io_async_certificate_identity_verifier\n    folly_io_async_ssl_context\n    folly_io_async_ssl_openssl_utils\n    folly_io_async_ssl_ssl_errors\n    folly_io_async_ssl_tls_definitions\n    folly_io_iobuf\n    folly_io_socket_option_map\n    folly_lang_bits\n    folly_optional\n    folly_portability_openssl\n    folly_portability_sockets\n    folly_ssl_openssl_ptr_types\n    folly_ssl_ssl_session\n    folly_ssl_ssl_session_manager\n    folly_string\n)\n\nfolly_add_library(\n  NAME async_transport\n  HEADERS\n    AsyncTransport.h\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_io_async_async_socket_base\n    folly_io_async_async_transport_certificate\n    folly_io_async_delayed_destruction\n    folly_io_async_write_flags\n    folly_io_iobuf\n    folly_optional\n    folly_portability_openssl\n    folly_portability_sys_uio\n    folly_ssl_openssl_ptr_types\n)\n\nfolly_add_library(\n  NAME async_transport_certificate\n  HEADERS\n    AsyncTransportCertificate.h\n)\n\nfolly_add_library(\n  NAME async_udp_server_socket\n  HEADERS\n    AsyncUDPServerSocket.h\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_io_async_async_udp_socket\n    folly_io_iobuf\n    folly_memory\n)\n\nfolly_add_library(\n  NAME async_udp_socket\n  SRCS\n    AsyncUDPSocket.cpp\n  HEADERS\n    AsyncUDPSocket.h\n  DEPS\n    folly_likely\n    folly_portability_fcntl\n    folly_portability_sockets\n    folly_portability_unistd\n    folly_small_vector\n    folly_utility\n  EXPORTED_DEPS\n    folly_function\n    folly_io_async_async_base\n    folly_io_async_async_socket_base\n    folly_io_async_async_socket_exception\n    folly_io_iobuf\n    folly_io_socket_option_map\n    folly_net_net_ops\n    folly_net_net_ops_dispatcher\n    folly_net_network_socket\n    folly_network_address\n    folly_scope_guard\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME certificate_identity_verifier\n  HEADERS\n    CertificateIdentityVerifier.h\n  EXPORTED_DEPS\n    folly_io_async_async_transport_certificate\n    folly_portability_openssl\n)\n\nfolly_add_library(\n  NAME decorated_async_transport_wrapper\n  HEADERS\n    DecoratedAsyncTransportWrapper.h\n    WriteChainAsyncTransportWrapper.h\n  EXPORTED_DEPS\n    folly_io_async_async_transport\n    folly_io_iobuf\n)\n\nfolly_add_library(\n  NAME delayed_destruction\n  SRCS\n    DelayedDestruction.cpp\n  HEADERS\n    DelayedDestruction.h\n    DelayedDestructionBase.h\n  EXPORTED_DEPS\n    folly_portability\n)\n\nfolly_add_library(\n  NAME destructor_check\n  HEADERS\n    DestructorCheck.h\n)\n\nfolly_add_library(\n  NAME epoll\n  HEADERS\n    Epoll.h\n)\n\nfolly_add_library(\n  NAME epoll_backend\n  SRCS\n    EpollBackend.cpp\n  HEADERS\n    Epoll.h\n    EpollBackend.h\n  DEPS\n    folly_file_util\n    folly_intrusive_list\n    folly_map_util\n    folly_string\n  EXPORTED_DEPS\n    folly_container_intrusive_heap\n    folly_io_async_async_base\n)\n\nfolly_add_library(\n  NAME event_base_manager\n  SRCS\n    EventBaseManager.cpp\n  HEADERS\n    EventBaseManager.h\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_optional\n    folly_thread_local\n)\n\nfolly_add_library(\n  NAME event_base_poller\n  SRCS\n    EventBasePoller.cpp\n  HEADERS\n    EventBasePoller.h\n  DEPS\n    folly_file_util\n    folly_io_async_epoll\n    folly_io_async_liburing\n    folly_lang_align\n    folly_portability_gflags\n    folly_string\n    folly_synchronization_baton\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_function\n    folly_range\n    folly_synchronized\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME event_base_thread\n  SRCS\n    EventBaseThread.cpp\n  HEADERS\n    EventBaseThread.h\n  DEPS\n    folly_io_async_scoped_event_base_thread\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_range\n)\n\nfolly_add_library(\n  NAME event_util\n  HEADERS\n    EventUtil.h\n  EXPORTED_DEPS\n    folly_portability_event\n)\n\nfolly_add_library(\n  NAME io_uring\n  SRCS\n    IoUring.cpp\n  HEADERS\n    IoUring.h\n  DEPS\n    folly_exception\n    folly_portability_unistd\n    folly_string\n  EXPORTED_DEPS\n    folly_io_async_async_base_class\n    folly_io_async_liburing\n    folly_shared_mutex\n)\n\nfolly_add_library(\n  NAME io_uring_backend\n  SRCS\n    IoUringBackend.cpp\n  HEADERS\n    IoUringBackend.h\n    IoUringBase.h\n    IoUringOptions.h\n  DEPS\n    folly_container_f14_hash\n    folly_demangle\n    folly_file_util\n    folly_glog\n    folly_lang_bits\n    folly_likely\n    folly_portability_gflags\n    folly_portability_sockets\n    folly_portability_sys_mman\n    folly_portability_sys_syscall\n    folly_spin_lock\n    folly_string\n    folly_synchronization_call_once\n    folly_tracing_static_tracepoint\n  EXPORTED_DEPS\n    folly_c_portability\n    folly_conv\n    folly_cpp_attributes\n    folly_exception_string\n    folly_function\n    folly_io_async_async_base\n    folly_io_async_delayed_destruction\n    folly_io_async_io_uring_provided_buffer_ring\n    folly_io_async_io_uring_zero_copy_buffer_pool\n    folly_io_async_liburing\n    folly_io_iobuf\n    folly_network_address\n    folly_optional\n    folly_portability_asm\n    folly_range\n    folly_small_vector\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME io_uring_buffer_pool_sharing\n  SRCS\n    IoUringBufferPoolSharing.cpp\n  HEADERS\n    IoUringBufferPoolSharing.h\n  DEPS\n    folly_function\n    folly_io_async_io_uring_backend\n    folly_io_async_liburing\n  EXPORTED_DEPS\n    folly_io_async_async_base\n  EXTERNAL_DEPS\n    ${GLOG_LIBRARIES}\n)\n\nfolly_add_library(\n  NAME io_uring_connect\n  SRCS\n    IoUringConnect.cpp\n  HEADERS\n    IoUringConnect.h\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_io_async_io_uring_backend\n    folly_net_network_socket\n)\n\nfolly_add_library(\n  NAME io_uring_event\n  SRCS\n    IoUringEvent.cpp\n  HEADERS\n    IoUringEvent.h\n  EXPORTED_DEPS\n    folly_file\n    folly_io_async_async_base\n    folly_io_async_io_uring_backend\n    folly_io_async_liburing\n)\n\nfolly_add_library(\n  NAME io_uring_event_base_local\n  SRCS\n    IoUringEventBaseLocal.cpp\n  HEADERS\n    IoUringEventBaseLocal.h\n  DEPS\n    folly_io_async_io_uring_event\n    folly_singleton\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_io_async_io_uring_backend\n    folly_io_async_liburing\n)\n\nfolly_add_library(\n  NAME io_uring_provided_buffer_ring\n  SRCS\n    IoUringProvidedBufferRing.cpp\n  HEADERS\n    IoUringBase.h\n    IoUringProvidedBufferRing.h\n  DEPS\n    folly_conv\n    folly_lang_align\n    folly_portability_sys_mman\n    folly_string\n  EXPORTED_DEPS\n    folly_io_async_delayed_destruction\n    folly_io_async_liburing\n    folly_io_iobuf\n    folly_synchronization_distributed_mutex\n  EXTERNAL_DEPS\n    Boost::headers\n)\n\nfolly_add_library(\n  NAME io_uring_recv\n  SRCS\n    IoUringRecv.cpp\n  HEADERS\n    IoUringRecv.h\n  DEPS\n    folly_io_async_async_base\n    folly_io_async_async_socket_exception\n  EXPORTED_DEPS\n    folly_futures_core\n    folly_io_async_async_transport\n    folly_io_async_delayed_destruction\n    folly_io_async_io_uring_backend\n    folly_net_network_socket\n    folly_network_address\n)\n\nfolly_add_library(\n  NAME io_uring_send\n  SRCS\n    IoUringSend.cpp\n  HEADERS\n    IoUringSend.h\n  DEPS\n    folly_io_async_async_base\n  EXPORTED_DEPS\n    folly_futures_core\n    folly_io_async_async_transport\n    folly_io_async_delayed_destruction\n    folly_io_async_io_uring_backend\n    folly_net_network_socket\n)\n\nfolly_add_library(\n  NAME io_uring_zero_copy_buffer_pool\n  SRCS\n    IoUringZeroCopyBufferPool.cpp\n  HEADERS\n    IoUringZeroCopyBufferPool.h\n  DEPS\n    folly_conv\n    folly_lang_align\n    folly_portability_sys_mman\n    folly_string\n    folly_synchronization_distributed_mutex\n  EXPORTED_DEPS\n    folly_io_async_liburing\n    folly_io_iobuf\n)\n\nfolly_add_library(\n  NAME liburing\n  HEADERS\n    Liburing.h\n)\n\nfolly_add_library(\n  NAME mux_io_thread_pool_executor\n  SRCS\n    MuxIOThreadPoolExecutor.cpp\n  HEADERS\n    MuxIOThreadPoolExecutor.h\n  DEPS\n    folly_container_enumerate\n    folly_io_async_epoll_backend\n    folly_lang_align\n    folly_synchronization_latch\n  EXPORTED_DEPS\n    folly_concurrency_unbounded_queue\n    folly_executors_io_thread_pool_executor\n    folly_executors_queue_observer\n    folly_io_async_event_base_manager\n    folly_io_async_event_base_poller\n    folly_portability\n    folly_synchronization_baton\n    folly_synchronization_relaxed_atomic\n    folly_synchronization_throttled_lifo_sem\n    folly_synchronization_wait_options\n)\n\nfolly_add_library(\n  NAME password_in_file\n  SRCS\n    PasswordInFile.cpp\n  HEADERS\n    PasswordInFile.h\n  DEPS\n    folly_file_util\n    folly_portability_openssl\n  EXPORTED_DEPS\n    folly_ssl_password_collector\n)\n\nfolly_add_library(\n  NAME request_context\n  SRCS\n    Request.cpp\n  HEADERS\n    Request.h\n  DEPS\n    folly_concurrency_container_single_writer_fixed_hash_map\n    folly_glog\n    folly_tracing_static_tracepoint\n  EXPORTED_DEPS\n    folly_concurrency_process_local_unique_id\n    folly_container_f14_hash\n    folly_detail_iterators\n    folly_shared_mutex\n    folly_singleton_thread_local\n    folly_synchronization_hazptr\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME scoped_event_base_thread\n  SRCS\n    ScopedEventBaseThread.cpp\n  HEADERS\n    ScopedEventBaseThread.h\n  DEPS\n    folly_function\n    folly_io_async_event_base_manager\n    folly_range\n    folly_system_thread_name\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_synchronization_baton\n)\n\nfolly_add_library(\n  NAME server_socket\n  SRCS\n    AsyncServerSocket.cpp\n  HEADERS\n    AsyncServerSocket.h\n  DEPS\n    folly_detail_socket_fast_open\n    folly_file_util\n    folly_glog\n    folly_portability\n    folly_portability_fcntl\n    folly_portability_unistd\n  EXPORTED_DEPS\n    folly_exception_wrapper\n    folly_io_async_async_base\n    folly_io_async_async_socket_base\n    folly_io_async_delayed_destruction\n    folly_io_shutdown_socket_set\n    folly_net_net_ops\n    folly_net_network_socket\n    folly_network_address\n    folly_observer_observer\n    folly_portability_sockets\n    folly_string\n)\n\nfolly_add_library(\n  NAME simple_async_io\n  SRCS\n    SimpleAsyncIO.cpp\n  HEADERS\n    SimpleAsyncIO.h\n  DEPS\n    folly_coro_baton\n    folly_io_async_async_io\n    folly_io_async_io_uring\n    folly_io_async_liburing\n    folly_portability_sockets\n    folly_string\n  EXPORTED_DEPS\n    folly_coro_task\n    folly_executors_global_executor\n    folly_io_async_async_base\n    folly_io_async_async_base_class\n    folly_io_async_scoped_event_base_thread\n    folly_synchronized\n)\n\nfolly_add_library(\n  NAME ssl_context\n  SRCS\n    SSLContext.cpp\n  HEADERS\n    SSLContext.h\n  DEPS\n    folly_format\n    folly_memory\n    folly_random\n    folly_shared_mutex\n    folly_ssl_openssl_ticket_handler\n    folly_ssl_password_collector\n    folly_ssl_ssl_session_manager\n  EXPORTED_DEPS\n    folly_container_access\n    folly_function\n    folly_io_async_ssl_openssl_utils\n    folly_portability\n    folly_portability_openssl\n    folly_range\n    folly_ssl_openssl_ptr_types\n    folly_string\n)\n\nfolly_add_library(\n  NAME ssl_options\n  SRCS\n    SSLOptions.cpp\n  HEADERS\n    SSLOptions.h\n  DEPS\n    folly_format\n  EXPORTED_DEPS\n    folly_container_array\n    folly_io_async_ssl_context\n)\n\nfolly_add_library(\n  NAME terminate_cancellation_token\n  SRCS\n    TerminateCancellationToken.cpp\n  HEADERS\n    TerminateCancellationToken.h\n  DEPS\n    folly_io_async_async_signal_handler\n    folly_io_async_scoped_event_base_thread\n    folly_singleton\n  EXPORTED_DEPS\n    folly_cancellation_token\n)\n\nfolly_add_library(\n  NAME timerfd\n  SRCS\n    STTimerFDTimeoutManager.cpp\n    TimerFD.cpp\n    TimerFDTimeoutManager.cpp\n  HEADERS\n    STTimerFDTimeoutManager.h\n    TimerFD.h\n    TimerFDTimeoutManager.h\n  DEPS\n    folly_file_util\n    folly_io_async_event_util\n  EXPORTED_DEPS\n    folly_io_async_async_base\n    folly_io_async_delayed_destruction\n)\n\nfolly_add_library(\n  NAME write_flags\n  HEADERS\n    WriteFlags.h\n)\n\nadd_subdirectory(fdsock)\nadd_subdirectory(observer)\nadd_subdirectory(ssl)\n"
  },
  {
    "path": "folly/io/async/CertificateIdentityVerifier.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/io/async/AsyncTransportCertificate.h>\n#include <folly/portability/OpenSSL.h>\n\nnamespace folly {\n\n/**\n * CertificateIdentityVerifier implementations are used during TLS handshakes to\n * extract and verify end-entity certificate identities.\n *\n * During TLS handshake, AsyncSSLSocket performs verification in this order:\n * 1. OpenSSL performs internal certificate chain validation\n * 2. HandshakeCB's handshakeVer() is invoked (if registered)\n * 3. CertificateIdentityVerifier's verifyContext() is invoked (if registered)\n *    - Called once for EACH certificate in the chain (root → intermediate →\n * leaf)\n *    - Provides access to the full X509_STORE_CTX for chain inspection\n * 4. CertificateIdentityVerifier's verifyLeaf() is invoked (if registered)\n *    - Called ONLY for the leaf certificate at depth 0\n *    - Only if all previous verification steps succeeded\n *\n * TLS connections must pass all these checks in order for an AsyncSSLSocket's\n * registered HandshakeCB to receive handshakeSuc().\n *\n * Instances can be shared across AsyncSSLSockets so implementations should be\n * thread-safe.\n */\nclass CertificateIdentityVerifier {\n public:\n  virtual ~CertificateIdentityVerifier() = default;\n\n  /**\n   * AsyncSSLSocket calls verifyContext() during the OpenSSL certificate\n   * verification callback for EACH certificate in the chain (typically\n   * root → intermediate(s) → leaf).\n   *\n   * This method allows custom validation logic beyond OpenSSL's internal\n   * consistency checks. The returned value becomes the new verification state:\n   * - If this method returns false, verification fails and verifyLeaf() will\n   *   NOT be called\n   * - If this method returns true, verification succeeds (and verifyLeaf()\n   *   may be called for the leaf certificate at depth 0)\n   *\n   * This method can override OpenSSL's verification result. For example:\n   * - If OpenSSL fails (preverifyOk=false) but verifyContext returns true,\n   *   verification succeeds and verifyLeaf() may be called\n   * - If OpenSSL succeeds (preverifyOk=true) but verifyContext returns false,\n   *   verification fails and verifyLeaf() will NOT be called\n   *\n   * Unlike HandshakeCB::handshakeVer(), this method does not short-circuit\n   * when it changes the result - it updates the verification state and allows\n   * processing to continue.\n   *\n   * The default implementation returns preverifyOk unchanged (pass-through),\n   * deferring to OpenSSL's verification decision.\n   *\n   * IMPORTANT: This method is called multiple times per handshake - once for\n   * each certificate in the chain. Use X509_STORE_CTX_get_error_depth(ctx)\n   * to determine the depth of the certificate being verified.\n   *\n   * See the passages on verify_callback in SSL_CTX_set_verify(3) for more\n   * details.\n   *\n   * @param preverifyOk Result of OpenSSL's internal verification checks\n   * @param ctx OpenSSL verification context containing the certificate chain\n   *            and verification state. Use\n   * X509_STORE_CTX_get_current_cert(&ctx) to access the certificate being\n   * verified at this depth.\n   * @return true if the certificate context is valid, false otherwise\n   */\n  virtual bool verifyContext(\n      bool preverifyOk, [[maybe_unused]] X509_STORE_CTX* ctx) const noexcept {\n    return preverifyOk;\n  }\n\n  /**\n   * AsyncSSLSocket calls verifyLeaf() during a TLS handshake after chain\n   * verification, only if certificate verification is required/requested,\n   * with the peer's leaf certificate provided as an argument.\n   *\n   * Returns a pointer to AsyncTransportCertificate object.\n   *\n   * @param leafCertificate leaf X509 certificate of the connected peer\n   */\n  // NOLINTNEXTLINE(modernize-use-nodiscard)\n  virtual std::unique_ptr<AsyncTransportCertificate> verifyLeaf(\n      const AsyncTransportCertificate& leafCertificate) const = 0;\n};\n\n/**\n * Base of exception hierarchy for CertificateIdentityVerifier failure reasons.\n */\nclass FOLLY_EXPORT CertificateIdentityVerifierException\n    : public std::runtime_error {\n public:\n  using std::runtime_error::runtime_error;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/DecoratedAsyncTransportWrapper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncTransport.h>\n\nnamespace folly {\n\n/**\n * Convenience class so that AsyncTransport can be decorated without\n * having to redefine every single method.\n */\ntemplate <class T>\nclass DecoratedAsyncTransportWrapper : public folly::AsyncTransport {\n public:\n  explicit DecoratedAsyncTransportWrapper(typename T::UniquePtr transport)\n      : transport_(std::move(transport)) {\n    if (FOLLY_LIKELY(nullptr != transport_)) {\n      transport_->decoratingTransport_ = this;\n    }\n  }\n\n  const AsyncTransport* getWrappedTransport() const override {\n    return transport_.get();\n  }\n\n  // folly::AsyncTransport\n  ReadCallback* getReadCallback() const override {\n    return transport_->getReadCallback();\n  }\n\n  void setReadCB(folly::AsyncTransport::ReadCallback* callback) override {\n    transport_->setReadCB(callback);\n  }\n\n  void write(\n      folly::AsyncTransport::WriteCallback* callback,\n      const void* buf,\n      size_t bytes,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) override {\n    transport_->write(callback, buf, bytes, flags);\n  }\n\n  void writeChain(\n      folly::AsyncTransport::WriteCallback* callback,\n      std::unique_ptr<folly::IOBuf>&& buf,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) override {\n    transport_->writeChain(callback, std::move(buf), flags);\n  }\n\n  void writev(\n      folly::AsyncTransport::WriteCallback* callback,\n      const iovec* vec,\n      size_t bytes,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) override {\n    transport_->writev(callback, vec, bytes, flags);\n  }\n\n  // folly::AsyncSocketBase\n  folly::EventBase* getEventBase() const override {\n    return transport_->getEventBase();\n  }\n\n  // folly::AsyncTransport\n  void attachEventBase(folly::EventBase* eventBase) override {\n    transport_->attachEventBase(eventBase);\n  }\n\n  void close() override { transport_->close(); }\n\n  void closeNow() override { transport_->closeNow(); }\n\n  void closeWithReset() override {\n    transport_->closeWithReset();\n\n    // This will likely result in 2 closeNow() calls on the decorated transport,\n    // but otherwise it is very easy to miss the derived class's closeNow().\n    closeNow();\n  }\n\n  bool connecting() const override { return transport_->connecting(); }\n\n  void detachEventBase() override { transport_->detachEventBase(); }\n\n  bool error() const override { return transport_->error(); }\n\n  size_t getAppBytesReceived() const override {\n    return transport_->getAppBytesReceived();\n  }\n\n  size_t getAppBytesWritten() const override {\n    return transport_->getAppBytesWritten();\n  }\n\n  void getLocalAddress(folly::SocketAddress* address) const override {\n    return transport_->getLocalAddress(address);\n  }\n\n  void getPeerAddress(folly::SocketAddress* address) const override {\n    return transport_->getPeerAddress(address);\n  }\n\n  size_t getRawBytesReceived() const override {\n    return transport_->getRawBytesReceived();\n  }\n\n  size_t getRawBytesWritten() const override {\n    return transport_->getRawBytesWritten();\n  }\n\n  uint32_t getSendTimeout() const override {\n    return transport_->getSendTimeout();\n  }\n\n  bool good() const override { return transport_->good(); }\n\n  bool isDetachable() const override { return transport_->isDetachable(); }\n\n  bool isEorTrackingEnabled() const override {\n    return transport_->isEorTrackingEnabled();\n  }\n\n  bool readable() const override { return transport_->readable(); }\n\n  bool writable() const override { return transport_->writable(); }\n\n  void setEorTracking(bool track) override {\n    return transport_->setEorTracking(track);\n  }\n\n  void setSendTimeout(uint32_t timeoutInMs) override {\n    transport_->setSendTimeout(timeoutInMs);\n  }\n\n  void shutdownWrite() override { transport_->shutdownWrite(); }\n\n  void shutdownWriteNow() override { transport_->shutdownWriteNow(); }\n\n  std::string getApplicationProtocol() const noexcept override {\n    return transport_->getApplicationProtocol();\n  }\n\n  std::string getSecurityProtocol() const override {\n    return transport_->getSecurityProtocol();\n  }\n\n  std::unique_ptr<IOBuf> getExportedKeyingMaterial(\n      folly::StringPiece label,\n      std::unique_ptr<IOBuf> context,\n      uint16_t length) const override {\n    return transport_->getExportedKeyingMaterial(\n        label, std::move(context), length);\n  }\n\n  bool isReplaySafe() const override { return transport_->isReplaySafe(); }\n\n  void setReplaySafetyCallback(\n      folly::AsyncTransport::ReplaySafetyCallback* callback) override {\n    transport_->setReplaySafetyCallback(callback);\n  }\n\n  const AsyncTransportCertificate* getPeerCertificate() const override {\n    return transport_->getPeerCertificate();\n  }\n\n  void dropPeerCertificate() noexcept override {\n    transport_->dropPeerCertificate();\n  }\n\n  const AsyncTransportCertificate* getSelfCertificate() const override {\n    return transport_->getSelfCertificate();\n  }\n\n  void dropSelfCertificate() noexcept override {\n    transport_->dropSelfCertificate();\n  }\n\n  bool setZeroCopy(bool enable) override {\n    return transport_->setZeroCopy(enable);\n  }\n\n  bool getZeroCopy() const override { return transport_->getZeroCopy(); }\n\n  void setZeroCopyEnableFunc(ZeroCopyEnableFunc func) override {\n    transport_->setZeroCopyEnableFunc(func);\n  }\n\n  void setZeroCopyEnableThreshold(size_t threshold) override {\n    transport_->setZeroCopyEnableThreshold(threshold);\n  }\n\n  AsyncTransport::UniquePtr tryExchangeWrappedTransport(\n      AsyncTransport::UniquePtr& transport) override {\n    if (transport_) {\n      transport_->decoratingTransport_ = nullptr;\n    }\n    if (transport) {\n      transport->decoratingTransport_ = this;\n    }\n    return std::exchange(transport_, std::move(transport));\n  }\n\n  void destroy() override {\n    if (transport_) {\n      transport_->decoratingTransport_ = nullptr;\n    }\n    folly::AsyncTransport::destroy();\n  }\n\n protected:\n  ~DecoratedAsyncTransportWrapper() override {}\n\n  typename T::UniquePtr transport_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/DelayedDestruction.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/DelayedDestruction.h>\n\nnamespace folly {\nDelayedDestruction::~DelayedDestruction() {}\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/DelayedDestruction.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/io/async/DelayedDestructionBase.h>\n\nnamespace folly {\n\n/**\n * DelayedDestruction is a helper class to ensure objects are not deleted\n * while they still have functions executing in a higher stack frame.\n *\n * This is useful for objects that invoke callback functions, to ensure that a\n * callback does not destroy the calling object.\n *\n * Classes needing this functionality should:\n * - derive from DelayedDestruction\n * - make their destructor private or protected, so it cannot be called\n *   directly\n * - create a DestructorGuard object on the stack in each public method that\n *   may invoke a callback\n *\n * DelayedDestruction does not perform any locking.  It is intended to be used\n * only from a single thread.\n */\nclass DelayedDestruction : public DelayedDestructionBase {\n public:\n  /**\n   * destroy() requests destruction of the object.\n   *\n   * This method will destroy the object after it has no more functions running\n   * higher up on the stack.  (i.e., No more DestructorGuard objects exist for\n   * this object.)  This method must be used instead of the destructor.\n   */\n  virtual void destroy() {\n    // If guardCount_ is not 0, just set destroyPending_ to delay\n    // actual destruction.\n    if (getDestructorGuardCount() != 0) {\n      destroyPending_ = true;\n    } else {\n      onDelayedDestroy(false);\n    }\n  }\n\n  /**\n   * Helper class to allow DelayedDestruction classes to be used with\n   * std::shared_ptr.\n   *\n   * This class can be specified as the destructor argument when creating the\n   * shared_ptr, and it will destroy the guarded class properly when all\n   * shared_ptr references are released.\n   */\n  class Destructor {\n   public:\n    void operator()(DelayedDestruction* dd) const { dd->destroy(); }\n  };\n\n  bool getDestroyPending() const { return destroyPending_; }\n\n protected:\n  /**\n   * Protected destructor.\n   *\n   * Making this protected ensures that users cannot delete DelayedDestruction\n   * objects directly, and that everyone must use destroy() instead.\n   * Subclasses of DelayedDestruction must also define their destructors as\n   * protected or private in order for this to work.\n   *\n   * This also means that DelayedDestruction objects cannot be created\n   * directly on the stack; they must always be dynamically allocated on the\n   * heap.\n   *\n   * In order to use a DelayedDestruction object with a shared_ptr, create the\n   * shared_ptr using a DelayedDestruction::Destructor as the second argument\n   * to the shared_ptr constructor.\n   */\n  ~DelayedDestruction() override;\n\n  DelayedDestruction() : destroyPending_(false) {}\n\n private:\n  /**\n   * destroyPending_ is set to true if destoy() is called while guardCount_ is\n   * non-zero. It is set to false before the object is deleted.\n   *\n   * If destroyPending_ is true, the object will be destroyed the next time\n   * guardCount_ drops to 0.\n   */\n  bool destroyPending_;\n\n  void onDelayedDestroy(bool delayed) override {\n    // check if it is ok to destroy now\n    if (delayed && !destroyPending_) {\n      return;\n    }\n    destroyPending_ = false;\n    delete this;\n  }\n};\n\ntemplate <typename T>\nusing DelayedDestructionUniquePtr =\n    std::unique_ptr<T, DelayedDestruction::Destructor>;\n\ntemplate <typename T, typename... A>\nDelayedDestructionUniquePtr<T> makeDelayedDestructionUniquePtr(A&&... a) {\n  return DelayedDestructionUniquePtr<T>{new T(static_cast<A&&>(a)...)};\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/DelayedDestructionBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <assert.h>\n\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <memory>\n#include <type_traits>\n#include <utility>\n\n#include <folly/Portability.h>\n\nnamespace folly {\n\n/**\n * DelayedDestructionBase is a helper class to ensure objects are not deleted\n * while they still have functions executing in a higher stack frame.\n *\n * This is useful for objects that invoke callback functions, to ensure that a\n * callback does not destroy the calling object.\n *\n * Classes needing this functionality should:\n * - derive from DelayedDestructionBase directly\n * - implement onDelayedDestroy which'll be called before the object is\n *   going to be destructed\n * - create a DestructorGuard object on the stack in each public method that\n *   may invoke a callback\n *\n * DelayedDestructionBase does not perform any locking.  It is intended to be\n * used only from a single thread.\n */\nclass DelayedDestructionBase {\n public:\n  DelayedDestructionBase(const DelayedDestructionBase&) = delete;\n  DelayedDestructionBase& operator=(const DelayedDestructionBase&) = delete;\n\n  virtual ~DelayedDestructionBase() = default;\n\n  /**\n   * Classes should create a DestructorGuard object on the stack in any\n   * function that may invoke callback functions.\n   *\n   * The DestructorGuard prevents the guarded class from being destroyed while\n   * it exists.  Without this, the callback function could delete the guarded\n   * object, causing problems when the callback function returns and the\n   * guarded object's method resumes execution.\n   */\n  class [[nodiscard]] DestructorGuard {\n   public:\n    explicit DestructorGuard(DelayedDestructionBase* dd) : dd_(dd) {\n      if (dd_ != nullptr) {\n        ++dd_->guardCount_;\n        assert(dd_->guardCount_ > 0); // check for wrapping\n      }\n    }\n\n    DestructorGuard(const DestructorGuard& dg) : DestructorGuard(dg.dd_) {}\n\n    DestructorGuard(DestructorGuard&& dg) noexcept\n        : dd_(std::exchange(dg.dd_, nullptr)) {}\n\n    DestructorGuard& operator=(DestructorGuard dg) noexcept {\n      std::swap(dd_, dg.dd_);\n      return *this;\n    }\n\n    DestructorGuard& operator=(DelayedDestructionBase* dd) {\n      *this = DestructorGuard(dd);\n      return *this;\n    }\n\n    ~DestructorGuard() {\n      if (dd_ != nullptr) {\n        assert(dd_->guardCount_ > 0);\n        --dd_->guardCount_;\n        if (dd_->guardCount_ == 0) {\n          dd_->onDelayedDestroy(true);\n        }\n      }\n    }\n\n    DelayedDestructionBase* get() const { return dd_; }\n\n    explicit operator bool() const { return dd_ != nullptr; }\n\n   private:\n    DelayedDestructionBase* dd_;\n  };\n\n  /**\n   * This smart pointer is a convenient way to manage a concrete\n   * DelayedDestructorBase child. It can replace the equivalent raw pointer and\n   * provide automatic memory management.\n   */\n  template <typename AliasType>\n  class IntrusivePtr : private DestructorGuard {\n    template <typename CopyAliasType>\n    friend class IntrusivePtr;\n\n   public:\n    template <typename... Args>\n    static IntrusivePtr<AliasType> make(Args&&... args) {\n      return {new AliasType(std::forward<Args>(args)...)};\n    }\n\n    IntrusivePtr() = default;\n    IntrusivePtr(const IntrusivePtr&) = default;\n    IntrusivePtr(IntrusivePtr&&) noexcept = default;\n\n    template <\n        typename CopyAliasType,\n        typename = typename std::enable_if<\n            std::is_convertible<CopyAliasType*, AliasType*>::value>::type>\n    IntrusivePtr(const IntrusivePtr<CopyAliasType>& copy)\n        : DestructorGuard(copy) {}\n\n    template <\n        typename CopyAliasType,\n        typename = typename std::enable_if<\n            std::is_convertible<CopyAliasType*, AliasType*>::value>::type>\n    IntrusivePtr(IntrusivePtr<CopyAliasType>&& copy)\n        : DestructorGuard(std::move(copy)) {}\n\n    explicit IntrusivePtr(AliasType* dd) : DestructorGuard(dd) {}\n\n    // Copying from a unique_ptr is safe because if the upcast to\n    // DelayedDestructionBase works, then the instance is already using\n    // intrusive ref-counting.\n    template <\n        typename CopyAliasType,\n        typename Deleter,\n        typename = typename std::enable_if<\n            std::is_convertible<CopyAliasType*, AliasType*>::value>::type>\n    explicit IntrusivePtr(const std::unique_ptr<CopyAliasType, Deleter>& copy)\n        : DestructorGuard(copy.get()) {}\n\n    IntrusivePtr& operator=(const IntrusivePtr&) = default;\n    IntrusivePtr& operator=(IntrusivePtr&&) noexcept = default;\n\n    template <\n        typename CopyAliasType,\n        typename = typename std::enable_if<\n            std::is_convertible<CopyAliasType*, AliasType*>::value>::type>\n    IntrusivePtr& operator=(IntrusivePtr<CopyAliasType> copy) noexcept {\n      DestructorGuard::operator=(copy);\n      return *this;\n    }\n\n    IntrusivePtr& operator=(AliasType* dd) {\n      DestructorGuard::operator=(dd);\n      return *this;\n    }\n\n    void reset(AliasType* dd = nullptr) { *this = dd; }\n\n    AliasType* get() const {\n      return static_cast<AliasType*>(DestructorGuard::get());\n    }\n\n    AliasType& operator*() const { return *get(); }\n\n    AliasType* operator->() const { return get(); }\n\n    explicit operator bool() const { return DestructorGuard::operator bool(); }\n  };\n\n protected:\n  DelayedDestructionBase() : guardCount_(0) {}\n\n  /**\n   * Get the number of DestructorGuards currently protecting this object.\n   *\n   * This is primarily intended for debugging purposes, such as asserting\n   * that an object has at least 1 guard.\n   */\n  uint32_t getDestructorGuardCount() const { return guardCount_; }\n\n  /**\n   * Implement onDelayedDestroy in subclasses.\n   * onDelayedDestroy() is invoked when the object is potentially being\n   * destroyed.\n   *\n   * @param delayed  This parameter is true if destruction was delayed because\n   *                 of a DestructorGuard object, or false if onDelayedDestroy()\n   *                 is being called directly from the destructor.\n   */\n  virtual void onDelayedDestroy(bool delayed) = 0;\n\n private:\n  /**\n   * guardCount_ is incremented by DestructorGuard, to indicate that one of\n   * the DelayedDestructionBase object's methods is currently running.\n   *\n   * If the destructor is called while guardCount_ is non-zero, destruction\n   * will be delayed until guardCount_ drops to 0.  This allows\n   * DelayedDestructionBase objects to invoke callbacks without having to worry\n   * about being deleted before the callback returns.\n   */\n  uint32_t guardCount_;\n};\n\ninline bool operator==(\n    const DelayedDestructionBase::DestructorGuard& left,\n    const DelayedDestructionBase::DestructorGuard& right) {\n  return left.get() == right.get();\n}\ninline bool operator!=(\n    const DelayedDestructionBase::DestructorGuard& left,\n    const DelayedDestructionBase::DestructorGuard& right) {\n  return left.get() != right.get();\n}\ninline bool operator==(\n    const DelayedDestructionBase::DestructorGuard& left, std::nullptr_t) {\n  return left.get() == nullptr;\n}\ninline bool operator==(\n    std::nullptr_t, const DelayedDestructionBase::DestructorGuard& right) {\n  return nullptr == right.get();\n}\ninline bool operator!=(\n    const DelayedDestructionBase::DestructorGuard& left, std::nullptr_t) {\n  return left.get() != nullptr;\n}\ninline bool operator!=(\n    std::nullptr_t, const DelayedDestructionBase::DestructorGuard& right) {\n  return nullptr != right.get();\n}\n\ntemplate <typename LeftAliasType, typename RightAliasType>\ninline bool operator==(\n    const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,\n    const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {\n  return left.get() == right.get();\n}\ntemplate <typename LeftAliasType, typename RightAliasType>\ninline bool operator!=(\n    const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,\n    const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {\n  return left.get() != right.get();\n}\ntemplate <typename LeftAliasType>\ninline bool operator==(\n    const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,\n    std::nullptr_t) {\n  return left.get() == nullptr;\n}\ntemplate <typename RightAliasType>\ninline bool operator==(\n    std::nullptr_t,\n    const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {\n  return nullptr == right.get();\n}\ntemplate <typename LeftAliasType>\ninline bool operator!=(\n    const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,\n    std::nullptr_t) {\n  return left.get() != nullptr;\n}\ntemplate <typename RightAliasType>\ninline bool operator!=(\n    std::nullptr_t,\n    const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {\n  return nullptr != right.get();\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/DestructorCheck.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly {\n\n/**\n * DestructorCheck is a helper class that helps to detect if a tracked object\n * was deleted.\n * This is useful for objects that request callbacks from other components.\n *\n * Classes needing this functionality should:\n * - derive from DestructorCheck\n *\n * Callback context can be extended with an instance of DestructorCheck::Safety\n * object initialized with a reference to the object dereferenced from the\n * callback.  Once the callback is invoked, it can use this safety object to\n * check if the object was not deallocated yet before dereferencing it.\n *\n * DestructorCheck does not perform any locking.  It is intended to be used\n * only from a single thread.\n *\n * Example:\n *\n * class AsyncFoo : public DestructorCheck {\n *  public:\n *   ~AsyncFoo();\n *   // awesome async code with circuitous deletion paths\n *   void async1();\n *   void async2();\n * };\n *\n * righteousFunc(AsyncFoo& f) {\n *   DestructorCheck::Safety safety(f);\n *\n *   f.async1(); // might have deleted f, oh noes\n *   if (!safety.destroyed()) {\n *     // phew, still there\n *     f.async2();\n *   }\n * }\n */\n\nclass DestructorCheck {\n public:\n  virtual ~DestructorCheck() { rootGuard_.setAllDestroyed(); }\n\n  class Safety;\n\n  class ForwardLink {\n    // These methods are mostly private because an outside caller could violate\n    // the integrity of the linked list.\n   private:\n    void setAllDestroyed() {\n      for (auto guard = next_; guard; guard = guard->next_) {\n        guard->setDestroyed();\n      }\n    }\n\n    // This is used to maintain the double-linked list. An intrusive list does\n    // not require any heap allocations, like a standard container would. This\n    // isolation of next_ in its own class means that the DestructorCheck can\n    // easily hold a next_ pointer without needing to hold a prev_ pointer.\n    // DestructorCheck never needs a prev_ pointer because it is the head node\n    // and this is a special list where the head never moves and never has a\n    // previous node.\n    Safety* next_{nullptr};\n\n    friend class DestructorCheck;\n    friend class Safety;\n  };\n\n  // See above example for usage\n  class Safety : public ForwardLink {\n   public:\n    explicit Safety(DestructorCheck& destructorCheck) {\n      // Insert this node at the head of the list.\n      prev_ = &destructorCheck.rootGuard_;\n      next_ = prev_->next_;\n      if (next_ != nullptr) {\n        next_->prev_ = this;\n      }\n      prev_->next_ = this;\n    }\n\n    ~Safety() {\n      if (!destroyed()) {\n        // Remove this node from the list.\n        prev_->next_ = next_;\n        if (next_ != nullptr) {\n          next_->prev_ = prev_;\n        }\n      }\n    }\n\n    Safety(const Safety&) = delete;\n    Safety(Safety&& goner) = delete;\n    Safety& operator=(const Safety&) = delete;\n    Safety& operator=(Safety&&) = delete;\n\n    bool destroyed() const { return prev_ == nullptr; }\n\n   private:\n    void setDestroyed() { prev_ = nullptr; }\n\n    // This field is used to maintain the double-linked list. If the root has\n    // been destroyed then the field is set to the nullptr sentinel value.\n    ForwardLink* prev_;\n\n    friend class ForwardLink;\n  };\n\n private:\n  ForwardLink rootGuard_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/Epoll.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#if defined(__linux__) && __has_include(<sys/epoll.h>)\n#define FOLLY_HAS_EPOLL 1\n#else\n#define FOLLY_HAS_EPOLL 0\n#endif\n"
  },
  {
    "path": "folly/io/async/EpollBackend.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/Epoll.h> // @manual\n\n#if FOLLY_HAS_EPOLL\n\n#include <signal.h>\n#include <sys/epoll.h>\n#include <sys/timerfd.h>\n\n#include <folly/IntrusiveList.h>\n#include <folly/MapUtil.h>\n#include <folly/String.h>\n#include <folly/io/async/EpollBackend.h>\n\n#include <folly/FileUtil.h>\n\nextern \"C\" FOLLY_ATTR_WEAK void eb_poll_loop_pre_hook(uint64_t* call_time);\nextern \"C\" FOLLY_ATTR_WEAK void eb_poll_loop_post_hook(\n    uint64_t call_time, int ret);\n\nnamespace folly {\nnamespace {\n\nstruct EventInfo {\n  static void freeFunction(void* v) { delete static_cast<EventInfo*>(v); }\n\n  void resetEvent() {\n    listHook.unlink(); // Remove from the info list.\n    ev = nullptr;\n  }\n\n  folly::IntrusiveListHook listHook;\n  struct event* ev{nullptr};\n  int what_{0};\n};\n\nusing EventInfoList = folly::IntrusiveList<EventInfo, &EventInfo::listHook>;\n\nstruct SignalRegistry {\n  struct SigInfo {\n    struct sigaction sa_{};\n    size_t refs_{0};\n  };\n  using SignalMap = std::map<int, SigInfo>;\n\n  SignalRegistry() {}\n\n  void notify(int sig);\n  void setNotifyFd(int sig, int fd);\n\n  // lock protecting the signal map\n  // we use a spinlock because we need\n  // it to async-signal-safe\n  folly::MicroSpinLock mapLock_ = {0};\n  SignalMap map_;\n  std::atomic<int> notifyFd_{-1};\n};\n\nSignalRegistry& getSignalRegistry() {\n  static auto& sInstance = *new SignalRegistry();\n  return sInstance;\n}\n\nvoid evSigHandler(int sig) {\n  getSignalRegistry().notify(sig);\n}\n\nvoid SignalRegistry::notify(int sig) {\n  // use try_lock in case somebody already has the lock\n  std::unique_lock lk(mapLock_, std::try_to_lock);\n  if (!lk.owns_lock()) {\n    return;\n  }\n\n  int fd = notifyFd_.load();\n  if (fd >= 0) {\n    uint8_t sigNum = static_cast<uint8_t>(sig);\n    fileops::write(fd, &sigNum, 1);\n  }\n}\n\nvoid SignalRegistry::setNotifyFd(int sig, int fd) {\n  std::lock_guard g(mapLock_);\n  if (fd >= 0) {\n    // switch the fd\n    notifyFd_.store(fd);\n\n    auto& entry = map_[sig];\n\n    if (entry.refs_++ == 0) {\n      struct sigaction sa = {};\n      sa.sa_handler = evSigHandler;\n      sa.sa_flags |= SA_RESTART;\n      ::sigfillset(&sa.sa_mask);\n\n      if (::sigaction(sig, &sa, &entry.sa_) == -1) {\n        map_.erase(sig);\n      }\n    }\n  } else {\n    notifyFd_.store(fd);\n    auto iter = map_.find(sig);\n    if ((iter != map_.end()) && (--iter->second.refs_ == 0)) {\n      auto entry = iter->second;\n      map_.erase(iter);\n      // just restore\n      ::sigaction(sig, &entry.sa_, nullptr);\n    }\n  }\n}\n\nuint32_t getPollFlags(short events) {\n  uint32_t ret = 0;\n  if (events & EV_READ) {\n    ret |= EPOLLIN;\n  }\n\n  if (events & EV_WRITE) {\n    ret |= EPOLLOUT;\n  }\n\n  return ret;\n}\n\n} // namespace\n\nstruct EpollBackend::TimerInfo : public IntrusiveHeapNode<> {\n  bool operator<(const TimerInfo& other) const {\n    // IntrusiveHeap is a max-heap.\n    return expiration > other.expiration;\n  }\n\n  static void freeFunction(void* v) { delete static_cast<TimerInfo*>(v); }\n\n  ~TimerInfo() { DCHECK(!isLinked()); }\n\n  std::chrono::steady_clock::time_point expiration;\n  struct event* ev;\n};\n\nEpollBackend::SocketPair::SocketPair() {\n  if (::socketpair(AF_UNIX, SOCK_STREAM, 0, fds_.data())) {\n    throw std::runtime_error(folly::errnoStr(errno));\n  }\n\n  // set the sockets to non blocking mode\n  for (auto fd : fds_) {\n    auto flags = ::fcntl(fd, F_GETFL, 0);\n    ::fcntl(fd, F_SETFL, flags | O_NONBLOCK);\n  }\n}\n\nEpollBackend::SocketPair::~SocketPair() {\n  for (auto fd : fds_) {\n    if (fd >= 0) {\n      fileops::close(fd);\n    }\n  }\n}\n\nEpollBackend::EpollBackend(Options options) : options_(options) {\n  epollFd_ = ::epoll_create1(EPOLL_CLOEXEC);\n\n  if (epollFd_ == -1) {\n    throw std::runtime_error(folly::errnoStr(errno));\n  }\n\n  {\n    timerFd_ = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);\n    if (timerFd_ == -1) {\n      auto errnoCopy = errno;\n      fileops::close(epollFd_);\n      throw std::runtime_error(folly::errnoStr(errnoCopy));\n    }\n    struct epoll_event epev = {};\n    epev.events = EPOLLIN;\n    // epoll_data is a union, so we need to use pointers for all events. We can\n    // use any unique pointer to distinguish the timerfd event, use the address\n    // of the fd variable.\n    epev.data.ptr = &timerFd_;\n    PCHECK(::epoll_ctl(epollFd_, EPOLL_CTL_ADD, timerFd_, &epev) == 0);\n  }\n\n  {\n    struct epoll_event epev = {};\n    epev.events = EPOLLIN;\n    epev.data.ptr = &signalFds_;\n    PCHECK(\n        ::epoll_ctl(epollFd_, EPOLL_CTL_ADD, signalFds_.readFd(), &epev) == 0);\n  }\n\n  events_.resize(options_.numLoopEvents);\n}\n\nEpollBackend::~EpollBackend() {\n  fileops::close(epollFd_);\n  fileops::close(timerFd_);\n}\n\nint EpollBackend::eb_event_base_loop(int flags) {\n  const bool waitForEvents = (flags & EVLOOP_NONBLOCK) == 0;\n  while (true) {\n    if (loopBreak_) {\n      loopBreak_ = false;\n      return 0;\n    }\n\n    if (numInternalEvents_ == numInsertedEvents_ && timers_.empty() &&\n        signals_.empty()) {\n      return 1;\n    }\n\n    uint64_t call_time = 0;\n    if (eb_poll_loop_pre_hook) {\n      eb_poll_loop_pre_hook(&call_time);\n    }\n    if (pollLoopHook_.preLoopHook) {\n      pollLoopHook_.preLoopHook(pollLoopHook_.hookCtx);\n    }\n\n    int numEvents;\n    do {\n      numEvents = ::epoll_wait(\n          epollFd_, events_.data(), events_.size(), waitForEvents ? -1 : 0);\n    } while (numEvents == -1 && errno == EINTR);\n\n    if (eb_poll_loop_post_hook) {\n      eb_poll_loop_post_hook(call_time, numEvents);\n    }\n    if (pollLoopHook_.postLoopHook) {\n      pollLoopHook_.postLoopHook(pollLoopHook_.hookCtx, numEvents);\n    }\n\n    if (numEvents < 0) {\n      return -1;\n    } else if (numEvents == 0) {\n      CHECK(!waitForEvents);\n      return 2;\n    }\n\n    bool shouldProcessTimers = false;\n    bool shouldProcessSignals = false;\n    // Callbacks may delete other active events, so we accumulate active events\n    // first into an intrusive list that is updated if events in it are deleted.\n    EventInfoList infoList;\n    for (int i = 0; i < numEvents; ++i) {\n      if (events_[i].data.ptr == &timerFd_) {\n        shouldProcessTimers = true;\n        continue;\n      } else if (events_[i].data.ptr == &signalFds_) {\n        shouldProcessSignals = true;\n        continue;\n      }\n\n      auto* info = static_cast<EventInfo*>(events_[i].data.ptr);\n      auto* event = info->ev;\n      info->what_ = events_[i].events;\n      // if not persistent we need to remove it\n      if (~event->ev_events & EV_PERSIST) {\n        if (event_ref_flags(event) & EVLIST_INSERTED) {\n          event_ref_flags(event) &= ~EVLIST_INSERTED;\n\n          DCHECK_GT(numInsertedEvents_, 0);\n          numInsertedEvents_--;\n\n          if (event_ref_flags(event) & EVLIST_INTERNAL) {\n            DCHECK_GT(numInternalEvents_, 0);\n            numInternalEvents_--;\n          }\n\n          PCHECK(\n              ::epoll_ctl(epollFd_, EPOLL_CTL_DEL, event->ev_fd, nullptr) == 0);\n        }\n      }\n\n      event_ref_flags(event) |= EVLIST_ACTIVE;\n      infoList.push_back(*info);\n    }\n\n    // Process timers and signals first.\n    if (shouldProcessTimers) {\n      processTimers();\n    }\n    if (shouldProcessSignals) {\n      processSignals();\n    }\n\n    while (!infoList.empty()) {\n      auto* info = &infoList.front();\n      infoList.pop_front();\n\n      struct event* event = info->ev;\n\n      int what = info->what_;\n      short ev = 0;\n\n      bool evRead = (event->ev_events & EV_READ) != 0;\n      bool evWrite = (event->ev_events & EV_WRITE) != 0;\n\n      if (what & EPOLLERR) {\n        if (evRead) {\n          ev |= EV_READ;\n        }\n        if (evWrite) {\n          ev |= EV_WRITE;\n        }\n      } else if ((what & EPOLLHUP) && !(what & EPOLLRDHUP)) {\n        if (evRead) {\n          ev |= EV_READ;\n        }\n        if (evWrite) {\n          ev |= EV_WRITE;\n        }\n      } else {\n        if (evRead && (what & EPOLLIN)) {\n          ev |= EV_READ;\n        }\n        if (evWrite && (what & EPOLLOUT)) {\n          ev |= EV_WRITE;\n        }\n      }\n\n      event_ref_flags(event) &= ~EVLIST_ACTIVE;\n      event->ev_res = ev;\n      if (event->ev_res) {\n        (*event_ref_callback(event))(\n            (int)event->ev_fd, event->ev_res, event_ref_arg(event));\n      }\n    }\n\n    if (flags & EVLOOP_ONCE) {\n      return 0;\n    }\n  }\n}\n\nint EpollBackend::eb_event_base_loopbreak() {\n  loopBreak_ = true;\n  return 0;\n}\n\nint EpollBackend::eb_event_add(Event& event, const struct timeval* timeout) {\n  auto* ev = event.getEvent();\n  CHECK(ev != nullptr);\n  CHECK(!(event_ref_flags(ev) & ~EVLIST_ALL));\n  // we do not support read/write timeouts\n  if (timeout) {\n    event_ref_flags(ev) |= EVLIST_TIMEOUT;\n    addTimerEvent(event, timeout);\n    return 0;\n  }\n\n  if (ev->ev_events & EV_SIGNAL) {\n    event_ref_flags(ev) |= EVLIST_INSERTED;\n    addSignalEvent(event);\n    return 0;\n  }\n\n  if (event_ref_flags(ev) & EVLIST_INTERNAL) {\n    numInternalEvents_++;\n  }\n\n  event_ref_flags(ev) |= EVLIST_INSERTED;\n  numInsertedEvents_++;\n\n  EventInfo* info = static_cast<EventInfo*>(event.getUserData());\n  if (!info) {\n    info = new EventInfo();\n    event.setUserData(info, EventInfo::freeFunction);\n  }\n  info->ev = ev;\n\n  struct epoll_event epev = {};\n  epev.events = getPollFlags(ev->ev_events & (EV_READ | EV_WRITE));\n  epev.data.ptr = info;\n\n  return ::epoll_ctl(epollFd_, EPOLL_CTL_ADD, ev->ev_fd, &epev);\n}\n\nint EpollBackend::eb_event_del(Event& event) {\n  if (!event.eb_ev_base()) {\n    errno = EINVAL;\n    return -1;\n  }\n\n  auto* ev = event.getEvent();\n  if (event_ref_flags(ev) & EVLIST_TIMEOUT) {\n    event_ref_flags(ev) &= ~EVLIST_TIMEOUT;\n    return removeTimerEvent(event);\n  }\n\n  if (!(event_ref_flags(ev) & (EVLIST_ACTIVE | EVLIST_INSERTED))) {\n    errno = EINVAL;\n    return -1;\n  }\n\n  if (ev->ev_events & EV_SIGNAL) {\n    event_ref_flags(ev) &= ~(EVLIST_INSERTED | EVLIST_ACTIVE);\n    return removeSignalEvent(event);\n  }\n\n  auto* info = static_cast<EventInfo*>(event.getUserData());\n  if (info) {\n    info->resetEvent();\n  }\n\n  // if the event is on the active list, we just clear the flags\n  // and reset the event_ ptr\n  if (event_ref_flags(ev) & EVLIST_ACTIVE) {\n    event_ref_flags(ev) &= ~EVLIST_ACTIVE;\n  }\n\n  if (event_ref_flags(ev) & EVLIST_INSERTED) {\n    event_ref_flags(ev) &= ~EVLIST_INSERTED;\n\n    DCHECK_GT(numInsertedEvents_, 0);\n    numInsertedEvents_--;\n\n    if (event_ref_flags(ev) & EVLIST_INTERNAL) {\n      DCHECK_GT(numInternalEvents_, 0);\n      numInternalEvents_--;\n    }\n\n    return ::epoll_ctl(epollFd_, EPOLL_CTL_DEL, ev->ev_fd, nullptr);\n  }\n\n  errno = EINVAL;\n  return -1;\n}\n\nbool EpollBackend::setEdgeTriggered(Event& event) {\n  auto* ev = event.getEvent();\n  CHECK(ev);\n\n  EventInfo* info = static_cast<EventInfo*>(event.getUserData());\n  if (info == nullptr) {\n    return false;\n  }\n\n  struct epoll_event epev = {};\n  epev.events = getPollFlags(ev->ev_events & (EV_READ | EV_WRITE)) | EPOLLET;\n  epev.data.ptr = info;\n\n  int ret = ::epoll_ctl(epollFd_, EPOLL_CTL_MOD, ev->ev_fd, &epev);\n  return ret == 0;\n}\n\nvoid EpollBackend::addTimerEvent(Event& event, const struct timeval* timeout) {\n  TimerInfo* info = static_cast<TimerInfo*>(event.getUserData());\n  if (info == nullptr) {\n    info = new TimerInfo;\n    info->ev = event.getEvent();\n    event.setUserData(info, TimerInfo::freeFunction);\n  }\n\n  info->expiration = std::chrono::steady_clock::now() +\n      std::chrono::seconds{timeout->tv_sec} +\n      std::chrono::microseconds{timeout->tv_usec};\n  if (info->isLinked()) {\n    timers_.update(info);\n  } else {\n    timers_.push(info);\n  }\n\n  updateTimerFd();\n}\n\nint EpollBackend::removeTimerEvent(Event& event) {\n  auto* info = static_cast<TimerInfo*>(event.getUserData());\n  if (info == nullptr || !info->isLinked()) {\n    errno = EINVAL;\n    return -1;\n  }\n\n  DCHECK_EQ(event.getFreeFunction(), TimerInfo::freeFunction);\n  timers_.erase(info);\n  updateTimerFd();\n  return 0;\n}\n\nvoid EpollBackend::updateTimerFd() {\n  std::optional<std::chrono::steady_clock::time_point> expiration;\n  if (auto* earliest = timers_.top()) {\n    expiration = earliest->expiration;\n  }\n  if (expiration == timerFdExpiration_) {\n    return; // Nothing to do.\n  }\n\n  if (!expiration) {\n    struct itimerspec val = {}; // Disable.\n    PCHECK(::timerfd_settime(timerFd_, 0, &val, nullptr) == 0);\n  } else {\n    auto delta = std::chrono::duration_cast<std::chrono::microseconds>(\n        *expiration - std::chrono::steady_clock::now());\n    if (delta < std::chrono::microseconds(1000)) {\n      delta = std::chrono::microseconds(1000);\n    }\n\n    struct itimerspec val;\n    val.it_interval = {0, 0};\n    val.it_value.tv_sec =\n        std::chrono::duration_cast<std::chrono::seconds>(delta).count();\n    val.it_value.tv_nsec =\n        std::chrono::duration_cast<std::chrono::nanoseconds>(delta).count() %\n        1'000'000'000LL;\n\n    PCHECK(::timerfd_settime(timerFd_, 0, &val, nullptr) == 0);\n  }\n\n  timerFdExpiration_ = expiration;\n}\n\nvoid EpollBackend::processTimers() {\n  // Consume the event.\n  uint64_t data = 0;\n  PCHECK(folly::readNoInt(timerFd_, &data, sizeof(data)) == sizeof(data));\n\n  while (!timers_.empty() &&\n         timers_.top()->expiration <= std::chrono::steady_clock::now()) {\n    auto* info = timers_.pop();\n    auto* ev = info->ev;\n    ev->ev_res = EV_TIMEOUT;\n    event_ref_flags(ev).get() = EVLIST_INIT;\n    // NOTE: The callback might change the set of registered timers.\n    (*event_ref_callback(ev))((int)ev->ev_fd, ev->ev_res, event_ref_arg(ev));\n  }\n\n  updateTimerFd();\n}\n\nvoid EpollBackend::addSignalEvent(Event& event) {\n  auto* ev = event.getEvent();\n  signals_[ev->ev_fd].insert(event.getEvent());\n\n  // we pass the write fd for notifications\n  getSignalRegistry().setNotifyFd(ev->ev_fd, signalFds_.writeFd());\n}\n\nint EpollBackend::removeSignalEvent(Event& event) {\n  auto* ev = event.getEvent();\n  auto* set = get_ptr(signals_, ev->ev_fd);\n  if (set == nullptr || set->erase(ev) == 0) {\n    errno = EINVAL;\n    return -1;\n  }\n  getSignalRegistry().setNotifyFd(ev->ev_fd, -1);\n  return 0;\n}\n\nvoid EpollBackend::processSignals() {\n  static constexpr auto kNumEntries = NSIG * 2;\n  static_assert(\n      NSIG < std::numeric_limits<uint8_t>::max(),\n      \"Use a different data type to cover all the signal values\");\n  std::array<bool, NSIG> processed{};\n  std::array<uint8_t, kNumEntries> signals;\n\n  ssize_t num =\n      folly::readNoInt(signalFds_.readFd(), signals.data(), signals.size());\n  for (ssize_t i = 0; i < num; i++) {\n    int signum = static_cast<int>(signals[i]);\n    if (signum < 0 || signum >= NSIG || processed[signum]) {\n      continue;\n    }\n    processed[signum] = true;\n    auto* events = get_ptr(signals_, signum);\n    if (events == nullptr) {\n      continue;\n    }\n    for (auto* ev : *events) {\n      ev->ev_res = 0;\n      event_ref_flags(ev) |= EVLIST_ACTIVE;\n      (*event_ref_callback(ev))((int)ev->ev_fd, ev->ev_res, event_ref_arg(ev));\n      event_ref_flags(ev) &= ~EVLIST_ACTIVE;\n    }\n  }\n}\n\n} // namespace folly\n#endif\n"
  },
  {
    "path": "folly/io/async/EpollBackend.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/Epoll.h>\n\n#if FOLLY_HAS_EPOLL\n\n#include <chrono>\n#include <map>\n#include <optional>\n#include <set>\n#include <vector>\n\n#include <folly/container/IntrusiveHeap.h>\n#include <folly/io/async/EventBaseBackendBase.h>\n\nnamespace folly {\n\nclass EpollBackend : public EventBaseBackendBase {\n public:\n  struct Options {\n    size_t numLoopEvents{128};\n\n    Options& setNumLoopEvents(size_t val) {\n      numLoopEvents = val;\n      return *this;\n    }\n  };\n\n  explicit EpollBackend(Options options);\n  ~EpollBackend() override;\n\n  int getEpollFd() const { return epollFd_; }\n\n  int getPollableFd() const override { return epollFd_; }\n\n  event_base* getEventBase() override { return nullptr; }\n\n  // Returns a non-standard value 2 when called with EVLOOP_NONBLOCK and the\n  // loop would block if called in a blocking fashion.\n  int eb_event_base_loop(int flags) override;\n  int eb_event_base_loopbreak() override;\n\n  int eb_event_add(Event& event, const struct timeval* timeout) override;\n  int eb_event_del(Event& event) override;\n\n  bool eb_event_active(Event&, int) override { return false; }\n\n  bool setEdgeTriggered(Event& event) override;\n\n private:\n  struct TimerInfo;\n\n  class SocketPair {\n   public:\n    SocketPair();\n\n    SocketPair(const SocketPair&) = delete;\n    SocketPair& operator=(const SocketPair&) = delete;\n\n    ~SocketPair();\n\n    int readFd() const { return fds_[1]; }\n\n    int writeFd() const { return fds_[0]; }\n\n   private:\n    std::array<int, 2> fds_{{-1, -1}};\n  };\n\n  void updateTimerFd();\n  void addTimerEvent(Event& event, const struct timeval* timeout);\n  int removeTimerEvent(Event& event);\n  void processTimers();\n  void setProcessTimers();\n\n  void addSignalEvent(Event& event);\n  int removeSignalEvent(Event& event);\n  void processSignals();\n\n  const Options options_;\n\n  int epollFd_{-1};\n\n  size_t numInsertedEvents_{0};\n  size_t numInternalEvents_{0};\n\n  bool loopBreak_{false};\n  std::vector<struct epoll_event> events_; // Cache allocation.\n\n  int timerFd_{-1};\n  std::optional<std::chrono::steady_clock::time_point> timerFdExpiration_;\n  IntrusiveHeap<TimerInfo> timers_;\n\n  SocketPair signalFds_;\n  std::map<int, std::set<struct event*>> signals_;\n};\n} // namespace folly\n#endif\n"
  },
  {
    "path": "folly/io/async/EventBase.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include <folly/io/async/EventBase.h>\n\n#include <fcntl.h>\n\n#include <memory>\n#include <mutex>\n#include <thread>\n\n#include <folly/Chrono.h>\n#include <folly/ExceptionString.h>\n#include <folly/Memory.h>\n#include <folly/String.h>\n#include <folly/io/async/EventBaseAtomicNotificationQueue.h>\n#include <folly/io/async/EventBaseBackendBase.h>\n#include <folly/io/async/EventBaseLocal.h>\n#include <folly/io/async/VirtualEventBase.h>\n#include <folly/lang/Assume.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/EventCount.h>\n#include <folly/system/ThreadId.h>\n#include <folly/system/ThreadName.h>\n\n#if defined(__linux__) && !FOLLY_MOBILE\n#define FOLLY_USE_EPOLLET\n\n#include <sys/epoll.h>\n\nstruct event_base {\n  void* evsel;\n  void* evbase;\n};\n\nstruct epollop {\n  void* fds;\n  int nfds;\n  void* events;\n  int nevents;\n  int epfd;\n};\n#endif\n\nnamespace {\n\nclass EventBaseBackend : public folly::EventBaseBackendBase {\n public:\n  EventBaseBackend();\n  explicit EventBaseBackend(event_base* evb);\n  ~EventBaseBackend() override;\n\n  event_base* getEventBase() override { return evb_; }\n\n  int eb_event_base_loop(int flags) override;\n  int eb_event_base_loopbreak() override;\n\n  int eb_event_add(Event& event, const struct timeval* timeout) override;\n  int eb_event_del(EventBaseBackendBase::Event& event) override;\n\n  bool eb_event_active(Event& event, int res) override;\n\n  bool setEdgeTriggered(Event& event) override;\n\n private:\n  event_base* evb_;\n};\n\nEventBaseBackend::EventBaseBackend() {\n  evb_ = event_base_new();\n}\n\nEventBaseBackend::EventBaseBackend(event_base* evb) : evb_(evb) {\n  if (FOLLY_UNLIKELY(evb_ == nullptr)) {\n    LOG(ERROR) << \"EventBase(): Pass nullptr as event base.\";\n    throw std::invalid_argument(\"EventBase(): event base cannot be nullptr\");\n  }\n}\n\nint EventBaseBackend::eb_event_base_loop(int flags) {\n  return event_base_loop(evb_, flags);\n}\n\nint EventBaseBackend::eb_event_base_loopbreak() {\n  return event_base_loopbreak(evb_);\n}\n\nint EventBaseBackend::eb_event_add(\n    Event& event, const struct timeval* timeout) {\n  return event_add(event.getEvent(), timeout);\n}\n\nint EventBaseBackend::eb_event_del(EventBaseBackendBase::Event& event) {\n  return event_del(event.getEvent());\n}\n\nbool EventBaseBackend::eb_event_active(Event& event, int res) {\n  event_active(event.getEvent(), res, 1);\n  return true;\n}\n\nbool EventBaseBackend::setEdgeTriggered(Event& event) {\n#ifdef FOLLY_USE_EPOLLET\n  // Until v2 libevent doesn't expose API to set edge-triggered flag for events.\n  // If epoll backend is used by libevent, we can enable it though epoll_ctl\n  // directly.\n  // Note that this code depends on internal event_base and epollop layout, so\n  // we have to validate libevent version.\n  static const bool supportedVersion =\n      !strcmp(event_get_version(), \"1.4.14b-stable\");\n  if (!supportedVersion || strcmp(event_base_get_method(evb_), \"epoll\")) {\n    return false;\n  }\n\n  auto epfd = static_cast<epollop*>(evb_->evbase)->epfd;\n  epoll_event epev = {0, {nullptr}};\n  epev.data.fd = event.eb_ev_fd();\n  epev.events = EPOLLET;\n  if (event.eb_ev_events() & EV_READ) {\n    epev.events |= EPOLLIN;\n  }\n  if (event.eb_ev_events() & EV_WRITE) {\n    epev.events |= EPOLLOUT;\n  }\n  if (::epoll_ctl(epfd, EPOLL_CTL_MOD, event.eb_ev_fd(), &epev) == -1) {\n    LOG(DFATAL) << \"epoll_ctl failed: \" << errno;\n    return false;\n  }\n  return true;\n#else\n  (void)event;\n  return false;\n#endif\n}\n\nEventBaseBackend::~EventBaseBackend() {\n  event_base_free(evb_);\n}\n\nclass TestEventBaseBackend : public EventBaseBackend {\n public:\n  explicit TestEventBaseBackend(int napiId) : napiId_(napiId) {}\n\n  int getNapiId() const override { return napiId_; }\n\n private:\n  int napiId_;\n};\n\n} // namespace\n\nnamespace folly {\n\nclass EventBase::LoopCallbacksDeadline {\n public:\n  void reset(EventBase& evb) {\n    if (auto timeslice = evb.loopCallbacksTimeslice_; timeslice.count() != 0) {\n      deadline_ = Clock::now() + timeslice;\n    } else {\n      deadline_ = {};\n    }\n  }\n\n  // Should be checked only after at least one callback has been processed, to\n  // guarantee forward progress.\n  bool expired() const {\n    return deadline_ != Clock::time_point{} && Clock::now() >= deadline_;\n  }\n\n private:\n  // Use the fastest clock, here millisecond granularity is enough.\n  using Clock = folly::chrono::coarse_steady_clock;\n  Clock::time_point deadline_;\n};\n\nclass EventBase::FuncRunner {\n public:\n  explicit FuncRunner(EventBase& eventBase)\n      : eventBase_(eventBase), curLoopCnt_(eventBase_.nextLoopCnt_) {}\n\n  AtomicNotificationQueueTaskStatus operator()(Func&& func) noexcept {\n    if (eventBase_.nextLoopCnt_ != curLoopCnt_) {\n      // We're the first callaback of this iteration, set a new deadline.\n      deadline_.reset(eventBase_);\n      curLoopCnt_ = eventBase_.nextLoopCnt_;\n    }\n\n    ExecutionObserverScopeGuard guard(\n        &eventBase_.getExecutionObserverList(),\n        &func,\n        folly::ExecutionObserver::CallbackType::NotificationQueue);\n    std::exchange(func, {})();\n\n    return deadline_.expired()\n        ? AtomicNotificationQueueTaskStatus::CONSUMED_STOP\n        : AtomicNotificationQueueTaskStatus::CONSUMED;\n  }\n\n private:\n  EventBase& eventBase_;\n  LoopCallbacksDeadline deadline_;\n  size_t curLoopCnt_;\n};\n\nclass EventBase::ThreadIdCollector : public WorkerProvider {\n public:\n  explicit ThreadIdCollector(EventBase& parent) : parent_(parent) {}\n\n  IdsWithKeepAlive collectThreadIds() override {\n    keepAlives_.fetch_add(1, std::memory_order_acq_rel);\n    auto guard = std::make_unique<Guard>(*this);\n    auto tid = parent_.loopTid_.load(std::memory_order_acquire);\n    if (tid < 0) {\n      return {};\n    }\n    return {std::move(guard), std::vector<pid_t>{tid}};\n  }\n\n  void awaitOutstandingKeepAlives() {\n    wakeUp_.await([&] {\n      return keepAlives_.load(std::memory_order_acquire) == 0;\n    });\n  }\n\n private:\n  class Guard : public KeepAlive {\n   public:\n    Guard(ThreadIdCollector& parent) : parent_(parent) {}\n\n    ~Guard() override {\n      if (parent_.keepAlives_.fetch_sub(1, std::memory_order_acq_rel) == 1) {\n        parent_.wakeUp_.notifyAll();\n      }\n    }\n\n   private:\n    ThreadIdCollector& parent_;\n  };\n\n  EventBase& parent_;\n  std::atomic<size_t> keepAlives_ = 0;\n  EventCount wakeUp_;\n};\n\n/*\n * EventBase methods\n */\n\nEventBase::EventBase(std::chrono::milliseconds tickInterval)\n    : EventBase(Options().setTimerTickInterval(tickInterval)) {}\n\nEventBase::EventBase(bool enableTimeMeasurement)\n    : EventBase(Options().setSkipTimeMeasurement(!enableTimeMeasurement)) {}\n\n// takes ownership of the event_base\nEventBase::EventBase(event_base* evb, bool enableTimeMeasurement)\n    : EventBase(\n          Options()\n              .setBackendFactory([evb] {\n                return std::make_unique<EventBaseBackend>(evb);\n              })\n              .setSkipTimeMeasurement(!enableTimeMeasurement)) {}\n\nEventBase::EventBase(Options options)\n    : intervalDuration_(options.timerTickInterval),\n      enableTimeMeasurement_(!options.skipTimeMeasurement),\n      loopCallbacksTimeslice_(options.loopCallbacksTimeslice),\n      runOnceCallbacks_(nullptr),\n      stop_(false),\n      queue_(nullptr),\n      maxLatency_(0),\n      avgLoopTime_(std::chrono::seconds(2)),\n      maxLatencyLoopTime_(avgLoopTime_),\n      nextLoopCnt_(\n          std::size_t(-40)) // Early wrap-around so bugs will manifest soon\n      ,\n      latestLoopCnt_(nextLoopCnt_),\n      startWork_(),\n      observer_(nullptr),\n      observerSampleCount_(0),\n      evb_(\n          options.backendFactory\n              ? options.backendFactory()\n              : getDefaultBackend()),\n      threadIdCollector_(std::make_unique<ThreadIdCollector>(*this)) {\n  initNotificationQueue();\n}\n\nEventBase::~EventBase() {\n  DCheckRequestContextRestoredGuard dcheckRctxGuard;\n\n  // Relax strict mode to allow callbacks to run in the destructor outside of\n  // the main loop. Note that any methods (including driving the loop) must be\n  // called before the destructor starts, so it is safe to modify the variable.\n  strictLoopThread_ = false;\n\n  // Call all pre-destruction callbacks, before we start cleaning up our state\n  // or apply any keepalives\n  while (!preDestructionCallbacks_.rlock()->empty()) {\n    OnDestructionCallback::List callbacks;\n    preDestructionCallbacks_.swap(callbacks);\n    while (!callbacks.empty()) {\n      auto& callback = callbacks.front();\n      callbacks.pop_front();\n      RequestContextSaverScopeGuard rctxGuard;\n      callback.runCallback();\n    }\n  }\n\n  std::future<void> virtualEventBaseDestroyFuture;\n  if (virtualEventBase_) {\n    virtualEventBaseDestroyFuture = virtualEventBase_->destroy();\n  }\n\n  // Keep looping until all keep-alive handles are released. Each keep-alive\n  // handle signals that some external code will still schedule some work on\n  // this EventBase (so it's not safe to destroy it).\n  while (loopKeepAliveCount() > 0) {\n    applyLoopKeepAlive();\n    loopOnce();\n  }\n\n  if (virtualEventBaseDestroyFuture.valid()) {\n    virtualEventBaseDestroyFuture.get();\n  }\n\n  // Call all destruction callbacks, before we start cleaning up our state.\n  while (!onDestructionCallbacks_.rlock()->empty()) {\n    OnDestructionCallback::List callbacks;\n    onDestructionCallbacks_.swap(callbacks);\n    while (!callbacks.empty()) {\n      auto& callback = callbacks.front();\n      callbacks.pop_front();\n      RequestContextSaverScopeGuard rctxGuard;\n      callback.runCallback();\n    }\n  }\n\n  clearCobTimeouts();\n\n  DCHECK_EQ(0u, runBeforeLoopCallbacks_.size());\n  DCHECK_EQ(0u, runAfterLoopCallbacks_.size());\n\n  runLoopCallbacks();\n\n  queue_->drain();\n\n  // Stop consumer before deleting NotificationQueue\n  queue_->stopConsuming();\n\n  // Remove self from all registered EventBaseLocal instances.\n  // Notice that we could be racing with EventBaseLocal dtor similarly\n  // deregistering itself from all registered EventBase instances. Because\n  // both sides need to acquire two locks, but in inverse order, we retry if\n  // inner lock acquisition fails to prevent lock inversion deadlock.\n  while (true) {\n    auto locked = localStorageToDtor_.wlock();\n    if (locked->empty()) {\n      break;\n    }\n    auto evbl = *locked->begin();\n    if (evbl->tryDeregister(*this)) {\n      locked->erase(evbl);\n    }\n  }\n\n  executionObserverList_.clear();\n\n  localStorage_.clear();\n\n  evb_.reset();\n\n  VLOG(5) << \"EventBase(): Destroyed.\";\n}\n\nvoid EventBase::setStrictLoopThread() {\n  CHECK(!isRunning());\n  strictLoopThread_ = true;\n}\n\nbool EventBase::tryDeregister(detail::EventBaseLocalBase& evbl) {\n  if (auto locked = localStorageToDtor_.tryWLock()) {\n    locked->erase(&evbl);\n    runInEventBaseThread([this, key = evbl.key_] { localStorage_.erase(key); });\n    return true;\n  }\n  return false;\n}\n\nstd::unique_ptr<EventBaseBackendBase> EventBase::getDefaultBackend() {\n  return std::make_unique<EventBaseBackend>();\n}\n\nstd::unique_ptr<EventBaseBackendBase> EventBase::getTestBackend(int napiId) {\n  return std::make_unique<TestEventBaseBackend>(napiId);\n}\n\nsize_t EventBase::getNotificationQueueSize() const {\n  return queue_->size();\n}\n\nsize_t EventBase::getNumLoopCallbacks() const {\n  dcheckIsInEventBaseThread();\n  return loopCallbacks_.size();\n}\n\nuint32_t EventBase::getMaxReadAtOnce() const {\n  return queue_->getMaxReadAtOnce();\n}\n\nvoid EventBase::setMaxReadAtOnce(uint32_t maxAtOnce) {\n  queue_->setMaxReadAtOnce(maxAtOnce);\n}\n\nbool EventBase::isInEventBaseThread() const {\n  auto tid = loopTid_.load(std::memory_order_relaxed);\n  return tid == static_cast<pid_t>(getOSThreadID()) ||\n      (!strictLoopThread_ && tid == kNotRunningTid);\n}\n\nbool EventBase::inRunningEventBaseThread() const {\n  return loopTid_.load(std::memory_order_relaxed) ==\n      static_cast<pid_t>(getOSThreadID());\n}\n\nvoid EventBase::checkIsInEventBaseThread() const {\n  auto evbTid = loopTid_.load(std::memory_order_relaxed);\n  if (!strictLoopThread_ && evbTid == kNotRunningTid) {\n    return;\n  }\n\n  // As opposed to name_, using getThreadName(loopThread_) will also work if the\n  // thread name is set outside of EventBase (and name_ is empty).\n  auto curTid = getOSThreadID();\n  CHECK_EQ(evbTid, curTid)\n      << \"This logic must be executed in the event base thread. \"\n      << \"Event base thread name: \\\"\"\n      << folly::getThreadName(loopThread_.load(std::memory_order_acquire))\n             .value_or(\"\")\n      << \"\\\", current thread name: \\\"\"\n      << folly::getCurrentThreadName().value_or(\"\") << \"\\\"\";\n}\n\n// Set smoothing coefficient for loop load average; input is # of milliseconds\n// for exp(-1) decay.\nvoid EventBase::setLoadAvgMsec(std::chrono::milliseconds ms) {\n  assert(enableTimeMeasurement_);\n  std::chrono::microseconds us = std::chrono::milliseconds(ms);\n  if (ms > std::chrono::milliseconds::zero()) {\n    maxLatencyLoopTime_.setTimeInterval(us);\n    avgLoopTime_.setTimeInterval(us);\n  } else {\n    LOG(ERROR) << \"non-positive arg to setLoadAvgMsec()\";\n  }\n}\n\nvoid EventBase::resetLoadAvg(double value) {\n  assert(enableTimeMeasurement_);\n  avgLoopTime_.reset(value);\n  maxLatencyLoopTime_.reset(value);\n}\n\nstatic std::chrono::milliseconds getTimeDelta(\n    std::chrono::steady_clock::time_point* prev) {\n  auto now = std::chrono::steady_clock::now();\n  auto result = now - *prev;\n  *prev = now;\n\n  return std::chrono::duration_cast<std::chrono::milliseconds>(result);\n}\n\nvoid EventBase::waitUntilRunning() {\n  while (loopTid_.load(std::memory_order_acquire) == kNotRunningTid) {\n    std::this_thread::yield();\n  }\n}\n\n// enters the event_base loop -- will only exit when forced to\nbool EventBase::loop() {\n  // Enforce blocking tracking and if we have a name override any previous one\n  ExecutorBlockingGuard guard{ExecutorBlockingGuard::TrackTag{}, this, name_};\n  return loopBody(0, {});\n}\n\nbool EventBase::loopIgnoreKeepAlive() {\n  if (loopKeepAliveActive_) {\n    // Make sure NotificationQueue is not counted as one of the readers\n    // (otherwise loopBody won't return until terminateLoopSoon is called).\n    queue_->stopConsuming();\n    queue_->startConsumingInternal(this);\n    loopKeepAliveActive_ = false;\n  }\n  LoopOptions options;\n  options.ignoreKeepAlive = true;\n  return loopBody(0, options);\n}\n\nbool EventBase::loopOnce(int flags) {\n  return loopBody(flags | EVLOOP_ONCE, {});\n}\n\nbool EventBase::isSuccess(LoopStatus status) {\n  switch (status) {\n    case LoopStatus::kDone:\n      return true;\n    case LoopStatus::kError:\n      return false;\n    case LoopStatus::kSuspended:\n      LOG(DFATAL) << \"Reached suspension when not allowed\";\n      return false;\n  }\n  assume_unreachable();\n}\n\nbool EventBase::loopBody(int flags, LoopOptions options) {\n  loopMainSetup();\n  SCOPE_EXIT {\n    DCHECK(!loopState_); // Cannot be suspended.\n    loopMainCleanup();\n  };\n  return isSuccess(loopMain(flags, options));\n}\n\nvoid EventBase::loopPollSetup() {\n  loopMainSetup();\n}\n\nbool EventBase::loopPoll(LoopPollOptions options) {\n  DCHECK(isRunning());\n  dcheckIsInEventBaseThread();\n  return isSuccess(\n      loopMain((options.nonblock ? EVLOOP_NONBLOCK : 0) | EVLOOP_ONCE, {}));\n}\n\nvoid EventBase::loopPollCleanup() {\n  loopMainCleanup();\n}\n\nEventBase::LoopStatus EventBase::loopWithSuspension() {\n  DCHECK_NE(evb_->getPollableFd(), -1)\n      << \"loopWithSuspension() is only supported for backends with pollable fd\";\n  loopMainSetup();\n  SCOPE_EXIT {\n    loopMainCleanup();\n  };\n  LoopOptions options;\n  options.allowSuspension = true;\n  return loopMain(EVLOOP_NONBLOCK, options);\n}\n\nvoid EventBase::loopMainSetup() {\n  VLOG(5) << \"EventBase(): Starting loop.\";\n\n  auto tid = getOSThreadID();\n  // Lock the loop.\n  auto const prevLoopTid = loopTid_.exchange(tid, std::memory_order_release);\n  loopThread_.store(std::this_thread::get_id(), std::memory_order_release);\n\n  // NOTE: This also fatals on reentrancy, which is not supported by old\n  // versions of libevent.\n  pid_t expected = loopState_ ? kSuspendedTid : kNotRunningTid;\n  CHECK_EQ(expected, prevLoopTid)\n      << \"Driving an EventBase (in thread \" << tid\n      << \") while it is already being driven (in thread \" << prevLoopTid\n      << \") is forbidden.\";\n\n  if (!name_.empty()) {\n    setThreadName(name_);\n  }\n}\n\nEventBase::LoopStatus EventBase::loopMain(int flags, LoopOptions options) {\n  DCheckRequestContextRestoredGuard dcheckRctxGuard;\n\n  int res = 0;\n  bool blocking = !(flags & EVLOOP_NONBLOCK);\n  bool once = (flags & EVLOOP_ONCE);\n\n  bool resumed = false;\n  if (!loopState_) {\n    loopState_.emplace(LoopState{});\n    if (enableTimeMeasurement_) {\n      loopState_->prev = std::chrono::steady_clock::now();\n      loopState_->idleStart = loopState_->prev;\n    }\n  } else {\n    resumed = true;\n  }\n\n  SCOPE_EXIT {\n    // Consume the stop signal so that the loop can resume on the next call.\n    stop_.store(false, std::memory_order_relaxed);\n  };\n\n  while (!stop_.load(std::memory_order_relaxed)) {\n    // Skip the setup if we're resuming.\n    if (!std::exchange(resumed, false)) {\n      if (!options.ignoreKeepAlive) {\n        applyLoopKeepAlive();\n      }\n      ++nextLoopCnt_;\n\n      // Run the before-loop callbacks\n      LoopCallbackList callbacks;\n      callbacks.swap(runBeforeLoopCallbacks_);\n      // Before-loop callbacks must by definition all run regardless of\n      // timeslice, so do not pass a deadline.\n      runLoopCallbackList(callbacks, LoopCallbacksDeadline{});\n    }\n\n    // nobody can add loop callbacks from within this thread if\n    // we don't have to handle anything to start with...\n    if (blocking && loopCallbacks_.empty()) {\n      res = evb_->eb_event_base_loop(EVLOOP_ONCE);\n    } else {\n      res = evb_->eb_event_base_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);\n    }\n    if (res == 2) {\n      // Only backends with pollable fd support return value 2.\n      DCHECK_NE(evb_->getPollableFd(), -1);\n      if (options.allowSuspension && loopCallbacks_.empty()) {\n        return LoopStatus::kSuspended;\n      } else {\n        res = 0; // Return value 2 implies success.\n      }\n    }\n\n    // libevent may return 1 early if there are no registered non-internal\n    // events, so even if the queue is not empty it may not be processed, thus\n    // we check that explicitly.\n    //\n    // Note that the queue was either not consumed, or it will be re-armed by a\n    // loop callback scheduled by execute(), so if there is an enqueue after the\n    // empty check here the queue's event will eventually be active.\n    if (res != 0 && !queue_->empty()) {\n      queue_->execute();\n    }\n\n    bool ranLoopCallbacks = runLoopCallbacks();\n\n    // Run the after-loop callback. Like the before-loop, no deadline.\n    {\n      LoopCallbackList callbacks;\n      callbacks.swap(runAfterLoopCallbacks_);\n      runLoopCallbackList(callbacks, LoopCallbacksDeadline{});\n    }\n\n    if (enableTimeMeasurement_) {\n      auto now = std::chrono::steady_clock::now();\n      auto busy = std::chrono::duration_cast<std::chrono::microseconds>(\n          now - startWork_);\n      auto idle = std::chrono::duration_cast<std::chrono::microseconds>(\n          startWork_ - loopState_->idleStart);\n      auto loop_time = busy + idle;\n\n      avgLoopTime_.addSample(loop_time, busy);\n      maxLatencyLoopTime_.addSample(loop_time, busy);\n\n      if (observer_) {\n        RequestContextSaverScopeGuard rctxGuard;\n        if (++observerSampleCount_ >= observer_->getSampleRate()) {\n          observerSampleCount_ = 0;\n          observer_->loopSample(busy.count(), idle.count());\n        }\n      }\n\n      VLOG(11)\n          << \"EventBase \" << this << \" did not timeout \"\n          << \" loop time guess: \" << loop_time.count()\n          << \" idle time: \" << idle.count() << \" busy time: \" << busy.count()\n          << \" avgLoopTime: \" << avgLoopTime_.get()\n          << \" maxLatencyLoopTime: \" << maxLatencyLoopTime_.get()\n          << \" maxLatency_: \" << maxLatency_.count() << \"us\"\n          << \" notificationQueueSize: \" << getNotificationQueueSize()\n          << \" nothingHandledYet(): \" << nothingHandledYet();\n\n      if (maxLatency_ > std::chrono::microseconds::zero()) {\n        // see if our average loop time has exceeded our limit\n        if (dampenMaxLatency_ &&\n            (maxLatencyLoopTime_.get() > double(maxLatency_.count()))) {\n          maxLatencyCob_();\n          // back off temporarily -- don't keep spamming maxLatencyCob_\n          // if we're only a bit over the limit\n          maxLatencyLoopTime_.dampen(0.9);\n        } else if (!dampenMaxLatency_ && busy > maxLatency_) {\n          // If no damping, we compare the raw busy time\n          maxLatencyCob_();\n        }\n      }\n\n      // Our loop run did real work; reset the idle timer\n      loopState_->idleStart = now;\n    } else {\n      VLOG(11) << \"EventBase \" << this << \" did not timeout\";\n    }\n\n    if (enableTimeMeasurement_) {\n      VLOG(11) << \"EventBase \" << this\n               << \" loop time: \" << getTimeDelta(&loopState_->prev).count();\n    }\n\n    if (once ||\n        // Event loop indicated that there were are no more registered events\n        // (except queue_ which is an internal event) and we didn't have any\n        // loop callbacks to run, so there is nothing left to do.\n        (res != 0 && !ranLoopCallbacks)) {\n      break;\n    }\n  }\n\n  loopState_.reset();\n  if (res < 0) {\n    LOG(ERROR) << \"EventBase: -- error in event loop, res = \" << res;\n    return LoopStatus::kError;\n  } else if (res == 1) {\n    VLOG(5) << \"EventBase: ran out of events (exiting loop)!\";\n  } else if (res > 1) {\n    LOG(ERROR) << \"EventBase: unknown event loop result = \" << res;\n    return LoopStatus::kError;\n  }\n  VLOG(5) << \"EventBase(): Done with loop.\";\n  return LoopStatus::kDone;\n}\n\nvoid EventBase::loopMainCleanup() {\n  threadIdCollector_->awaitOutstandingKeepAlives();\n  loopThread_.store({}, std::memory_order_release);\n  // Must be last, unlocks the loop.\n  loopTid_.store(\n      loopState_ ? kSuspendedTid : kNotRunningTid, std::memory_order_release);\n}\n\nbool EventBase::keepAliveAcquire() noexcept {\n  loopKeepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n  return true;\n}\n\nvoid EventBase::keepAliveRelease() noexcept {\n  size_t count = loopKeepAliveCount_.load(std::memory_order_relaxed);\n  do {\n    DCHECK_GE(count, 1);\n    // Ensure that the transition to 0 only happens in the loop, so that the\n    // loop can observe it and complete.\n    if (count == 1 && !inRunningEventBaseThread()) {\n      queue_->putMessage([this] {\n        auto oldCount =\n            loopKeepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);\n        DCHECK_GE(oldCount, 1);\n      });\n      return;\n    }\n  } while (!loopKeepAliveCount_.compare_exchange_weak(\n      count, count - 1, std::memory_order_acq_rel, std::memory_order_relaxed));\n}\n\nsize_t EventBase::loopKeepAliveCount() {\n  return loopKeepAliveCount_.load(std::memory_order_acquire);\n}\n\nvoid EventBase::applyLoopKeepAlive() {\n  auto keepAliveCount = loopKeepAliveCount();\n  // Make sure default VirtualEventBase won't hold EventBase::loop() forever.\n  if (auto virtualEventBase = tryGetVirtualEventBase()) {\n    if (virtualEventBase->keepAliveCount() == 1) {\n      --keepAliveCount;\n    }\n  }\n\n  if (loopKeepAliveActive_ && keepAliveCount == 0) {\n    // Restore the notification queue internal flag\n    queue_->stopConsuming();\n    queue_->startConsumingInternal(this);\n    loopKeepAliveActive_ = false;\n  } else if (!loopKeepAliveActive_ && keepAliveCount > 0) {\n    // Update the notification queue event to treat it as a normal\n    // (non-internal) event.  The notification queue event always remains\n    // installed, and the main loop won't exit with it installed.\n    queue_->stopConsuming();\n    queue_->startConsuming(this);\n    loopKeepAliveActive_ = true;\n  }\n}\n\nvoid EventBase::loopForever() {\n  bool ret;\n  {\n    // Make sure notification queue events are treated as normal events.\n    loopKeepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    SCOPE_EXIT {\n      loopKeepAliveCount_.fetch_sub(1, std::memory_order_relaxed);\n      applyLoopKeepAlive();\n    };\n    ret = loop();\n  }\n\n  if (!ret) {\n    folly::throwSystemError(\"error in EventBase::loopForever()\");\n  }\n}\n\nvoid EventBase::bumpHandlingTime() {\n  if (!enableTimeMeasurement_) {\n    return;\n  }\n\n  VLOG(11) << \"EventBase \" << this << \" \" << __PRETTY_FUNCTION__\n           << \" (loop) latest \" << latestLoopCnt_ << \" next \" << nextLoopCnt_;\n  if (nothingHandledYet()) {\n    latestLoopCnt_ = nextLoopCnt_;\n    // set the time\n    startWork_ = std::chrono::steady_clock::now();\n\n    VLOG(11) << \"EventBase \" << this << \" \" << __PRETTY_FUNCTION__\n             << \" (loop) startWork_ \" << startWork_.time_since_epoch().count();\n  }\n}\n\nvoid EventBase::terminateLoopSoon() {\n  CHECK(!strictLoopThread_)\n      << \"terminateLoopSoon() not allowed in strict loop thread mode\";\n\n  VLOG(5) << \"EventBase(): Received terminateLoopSoon() command.\";\n\n  auto keepAlive = getKeepAliveToken(this);\n\n  // Set stop to true, so the event loop will know to exit.\n  stop_.store(true, std::memory_order_relaxed);\n\n  // If terminateLoopSoon() is called from another thread,\n  // the EventBase thread might be stuck waiting for events.\n  // In this case, it won't wake up and notice that stop_ is set until it\n  // receives another event.  Send an empty frame to the notification queue\n  // so that the event loop will wake up even if there are no other events.\n  queue_->putMessage([] {});\n}\n\nvoid EventBase::runInLoop(\n    LoopCallback* callback,\n    bool thisIteration,\n    std::shared_ptr<RequestContext> rctx) {\n  dcheckIsInEventBaseThread();\n  callback->cancelLoopCallback();\n  callback->context_ = std::move(rctx);\n  if (runOnceCallbacks_ != nullptr && thisIteration) {\n    runOnceCallbacks_->push_back(*callback);\n  } else {\n    loopCallbacks_.push_back(*callback);\n  }\n}\n\nvoid EventBase::runInLoop(Func cob, bool thisIteration) {\n  dcheckIsInEventBaseThread();\n  auto wrapper = new FunctionLoopCallback(std::move(cob));\n  wrapper->context_ = RequestContext::saveContext();\n  if (runOnceCallbacks_ != nullptr && thisIteration) {\n    runOnceCallbacks_->push_back(*wrapper);\n  } else {\n    loopCallbacks_.push_back(*wrapper);\n  }\n}\n\nvoid EventBase::runOnDestruction(OnDestructionCallback& callback) {\n  callback.schedule(\n      [this](auto& cb) { onDestructionCallbacks_.wlock()->push_back(cb); },\n      [this](auto& cb) {\n        onDestructionCallbacks_.withWLock([&](auto& list) {\n          list.erase(list.iterator_to(cb));\n        });\n      });\n}\n\nvoid EventBase::runOnDestruction(Func f) {\n  auto* callback = new FunctionOnDestructionCallback(std::move(f));\n  runOnDestruction(*callback);\n}\n\nvoid EventBase::runOnDestructionStart(OnDestructionCallback& callback) {\n  callback.schedule(\n      [this](auto& cb) { preDestructionCallbacks_.wlock()->push_back(cb); },\n      [this](auto& cb) {\n        preDestructionCallbacks_.withWLock([&](auto& list) {\n          list.erase(list.iterator_to(cb));\n        });\n      });\n}\n\nvoid EventBase::runOnDestructionStart(Func f) {\n  auto* callback = new FunctionOnDestructionCallback(std::move(f));\n  runOnDestructionStart(*callback);\n}\n\nvoid EventBase::runBeforeLoop(LoopCallback* callback) {\n  dcheckIsInEventBaseThread();\n  callback->cancelLoopCallback();\n  runBeforeLoopCallbacks_.push_back(*callback);\n}\n\nvoid EventBase::runAfterLoop(LoopCallback* callback) {\n  dcheckIsInEventBaseThread();\n  callback->cancelLoopCallback();\n  runAfterLoopCallbacks_.push_back(*callback);\n}\n\nvoid EventBase::runInEventBaseThread(Func fn) noexcept {\n  // Send the message.\n  // It will be received by the FunctionRunner in the EventBase's thread.\n\n  // We try not to schedule nullptr callbacks\n  if (!fn) {\n    LOG(DFATAL) << \"EventBase \" << this\n                << \": Scheduling nullptr callbacks is not allowed\";\n    return;\n  }\n\n  // Short-circuit if we are already in our event base\n  if (inRunningEventBaseThread()) {\n    runInLoop(std::move(fn));\n    return;\n  }\n\n  queue_->putMessage(std::move(fn));\n}\n\nvoid EventBase::runInEventBaseThreadAlwaysEnqueue(Func fn) noexcept {\n  // Send the message.\n  // It will be received by the FunctionRunner in the EventBase's thread.\n\n  // We try not to schedule nullptr callbacks\n  if (!fn) {\n    LOG(DFATAL) << \"EventBase \" << this\n                << \": Scheduling nullptr callbacks is not allowed\";\n    return;\n  }\n\n  queue_->putMessage(std::move(fn));\n}\n\nvoid EventBase::runInEventBaseThreadAlwaysEnqueue(\n    std::shared_ptr<RequestContext>&& ctx, Func fn) noexcept {\n  // Send the message.\n  // It will be received by the FunctionRunner in the EventBase's thread.\n\n  // We try not to schedule nullptr callbacks\n  if (!fn) {\n    LOG(DFATAL) << \"EventBase \" << this\n                << \": Scheduling nullptr callbacks is not allowed\";\n    return;\n  }\n\n  queue_->putMessage(std::move(ctx), std::move(fn));\n}\n\nvoid EventBase::runInEventBaseThreadAndWait(Func fn) noexcept {\n  if (inRunningEventBaseThread()) {\n    LOG(DFATAL) << \"EventBase \" << this << \": Waiting in the event loop is not \"\n                << \"allowed\";\n    return;\n  }\n\n  Baton<> ready;\n  runInEventBaseThread([&ready, fn = std::move(fn)]() mutable {\n    SCOPE_EXIT {\n      ready.post();\n    };\n    // A trick to force the stored functor to be executed and then destructed\n    // before posting the baton and waking the waiting thread.\n    copy(std::move(fn))();\n  });\n  ready.wait(folly::Baton<>::wait_options().logging_enabled(false));\n}\n\nvoid EventBase::runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept {\n  if (isInEventBaseThread()) {\n    RequestContextSaverScopeGuard rctxGuard;\n    fn();\n  } else {\n    runInEventBaseThreadAndWait(std::move(fn));\n  }\n}\n\nvoid EventBase::runImmediatelyOrRunInEventBaseThread(Func fn) noexcept {\n  if (isInEventBaseThread()) {\n    RequestContextSaverScopeGuard rctxGuard;\n    fn();\n  } else {\n    runInEventBaseThreadAlwaysEnqueue(std::move(fn));\n  }\n}\n\nvoid EventBase::runLoopCallbackList(\n    LoopCallbackList& currentCallbacks, const LoopCallbacksDeadline& deadline) {\n  if (currentCallbacks.empty()) {\n    return;\n  }\n\n  RequestContextSaverScopeGuard ctxGuard;\n  do {\n    LoopCallback* callback = &currentCallbacks.front();\n    currentCallbacks.pop_front();\n    // Use setContext() under a RequestContextSaverScopeGuard instead of a\n    // per-callback RequestContextScopeGuard to avoid switching context back and\n    // forth when consecutive callbacks have the same context. This runs the\n    // pop_front() in the previous callback's context, but that is non-blocking\n    // and doesn't run application logic.\n    RequestContext::setContext(std::move(callback->context_));\n    ExecutionObserverScopeGuard guard(\n        &executionObserverList_,\n        callback,\n        folly::ExecutionObserver::CallbackType::Loop);\n    callback->runLoopCallback();\n  } while (!currentCallbacks.empty() && !deadline.expired());\n}\n\nbool EventBase::runLoopCallbacks() {\n  bumpHandlingTime();\n  if (!loopCallbacks_.empty()) {\n    // Swap the loopCallbacks_ list with a temporary list on our stack.\n    // This way we will only run callbacks scheduled at the time\n    // runLoopCallbacks() was invoked.\n    //\n    // If any of these callbacks in turn call runInLoop() to schedule more\n    // callbacks, those new callbacks won't be run until the next iteration\n    // around the event loop.  This prevents runInLoop() callbacks from being\n    // able to start file descriptor and timeout based events.\n    LoopCallbackList currentCallbacks;\n    currentCallbacks.swap(loopCallbacks_);\n    runOnceCallbacks_ = &currentCallbacks;\n\n    LoopCallbacksDeadline deadline;\n    deadline.reset(*this);\n    runLoopCallbackList(currentCallbacks, deadline);\n\n    // If the deadline expired before the list was fully consumed, prepend the\n    // leftover callbacks to the list to run on the next iteration.\n    loopCallbacks_.splice(loopCallbacks_.begin(), currentCallbacks);\n\n    runOnceCallbacks_ = nullptr;\n    return true;\n  }\n  return false;\n}\n\nvoid EventBase::initNotificationQueue() {\n  // Infinite size queue\n  queue_ = std::make_unique<EventBaseAtomicNotificationQueue<Func, FuncRunner>>(\n      FuncRunner{*this});\n\n  // Mark this as an internal event, so event_base_loop() will return if\n  // there are no other events besides this one installed.\n  //\n  // Most callers don't care about the internal notification queue used by\n  // EventBase.  The queue is always installed, so if we did count the queue as\n  // an active event, loop() would never exit with no more events to process.\n  // Users can use loopForever() if they do care about the notification queue.\n  // (This is useful for EventBase threads that do nothing but process\n  // runInEventBaseThread() notifications.)\n  queue_->startConsumingInternal(this);\n}\n\nvoid EventBase::SmoothLoopTime::setTimeInterval(\n    std::chrono::microseconds timeInterval) {\n  expCoeff_ = -1.0 / static_cast<double>(timeInterval.count());\n  VLOG(11) << \"expCoeff_ \" << expCoeff_ << \" \" << __PRETTY_FUNCTION__;\n}\n\nvoid EventBase::SmoothLoopTime::reset(double value) {\n  value_ = value;\n}\n\nvoid EventBase::SmoothLoopTime::addSample(\n    std::chrono::microseconds total, std::chrono::microseconds busy) {\n  if ((buffer_time_ + total) > buffer_interval_ && buffer_cnt_ > 0) {\n    // See https://en.wikipedia.org/wiki/Exponential_smoothing for\n    // more info on this calculation.\n    double coeff = exp(static_cast<double>(buffer_time_.count()) * expCoeff_);\n    value_ = value_ * coeff +\n        (1.0 - coeff) *\n            (static_cast<double>(busy_buffer_.count()) / buffer_cnt_);\n    buffer_time_ = std::chrono::microseconds{0};\n    busy_buffer_ = std::chrono::microseconds{0};\n    buffer_cnt_ = 0;\n  }\n  buffer_time_ += total;\n  busy_buffer_ += busy;\n  buffer_cnt_++;\n}\n\nbool EventBase::nothingHandledYet() const noexcept {\n  VLOG(11) << \"latest \" << latestLoopCnt_ << \" next \" << nextLoopCnt_;\n  return (nextLoopCnt_ != latestLoopCnt_);\n}\n\nvoid EventBase::attachTimeoutManager(AsyncTimeout* obj, InternalEnum internal) {\n  auto* ev = obj->getEvent();\n  assert(ev->eb_ev_base() == nullptr);\n\n  ev->eb_event_base_set(this);\n  if (internal == AsyncTimeout::InternalEnum::INTERNAL) {\n    // Set the EVLIST_INTERNAL flag\n    event_ref_flags(ev->getEvent()) |= EVLIST_INTERNAL;\n  }\n}\n\nvoid EventBase::detachTimeoutManager(AsyncTimeout* obj) {\n  cancelTimeout(obj);\n  auto* ev = obj->getEvent();\n  ev->eb_ev_base(nullptr);\n}\n\nbool EventBase::scheduleTimeout(\n    AsyncTimeout* obj, TimeoutManager::timeout_type timeout) {\n  dcheckIsInEventBaseThread();\n  // Set up the timeval and add the event\n  struct timeval tv;\n  tv.tv_sec = to_narrow(timeout.count() / 1000LL);\n  tv.tv_usec = to_narrow((timeout.count() % 1000LL) * 1000LL);\n\n  auto* ev = obj->getEvent();\n\n  DCHECK(ev->eb_ev_base());\n\n  if (ev->eb_event_add(&tv) < 0) {\n    LOG(ERROR) << \"EventBase: failed to schedule timeout: \" << errnoStr(errno);\n    return false;\n  }\n\n  return true;\n}\n\nvoid EventBase::cancelTimeout(AsyncTimeout* obj) {\n  dcheckIsInEventBaseThread();\n  auto* ev = obj->getEvent();\n  if (ev->isEventRegistered()) {\n    ev->eb_event_del();\n  }\n}\n\nvoid EventBase::setName(const std::string& name) {\n  dcheckIsInEventBaseThread();\n  name_ = name;\n\n  if (isRunning()) {\n    setThreadName(loopThread_.load(std::memory_order_relaxed), name_);\n  }\n}\n\nconst std::string& EventBase::getName() {\n  dcheckIsInEventBaseThread();\n  return name_;\n}\n\nstd::thread::id EventBase::getLoopThreadId() {\n  return loopThread_.load(std::memory_order_relaxed);\n}\n\nvoid EventBase::scheduleAt(Func&& fn, TimePoint const& timeout) {\n  auto duration = timeout - now();\n  timer().scheduleTimeoutFn(\n      std::move(fn),\n      std::chrono::duration_cast<std::chrono::milliseconds>(duration));\n}\n\nevent_base* EventBase::getLibeventBase() const {\n  return evb_ ? (evb_->getEventBase()) : nullptr;\n}\n\nconst char* EventBase::getLibeventVersion() {\n  return event_get_version();\n}\nconst char* EventBase::getLibeventMethod() {\n  // event_base_method() would segv if there is no current_base so simulate it\n  struct op {\n    const char* name;\n  };\n  struct base {\n    const op* evsel;\n  };\n  auto b = reinterpret_cast<base*>(getLibeventBase());\n  return !b ? \"\" : b->evsel->name;\n}\n\nVirtualEventBase& EventBase::getVirtualEventBase() {\n  folly::call_once(virtualEventBaseInitFlag_, [&] {\n    virtualEventBase_ = std::make_unique<VirtualEventBase>(*this);\n  });\n\n  return *virtualEventBase_;\n}\n\nVirtualEventBase* EventBase::tryGetVirtualEventBase() {\n  if (folly::test_once(virtualEventBaseInitFlag_)) {\n    return virtualEventBase_.get();\n  }\n  return nullptr;\n}\n\nEventBase* EventBase::getEventBase() {\n  return this;\n}\n\nWorkerProvider* EventBase::getThreadIdCollector() {\n  return threadIdCollector_.get();\n}\n\nEventBase::OnDestructionCallback::~OnDestructionCallback() {\n  if (*scheduled_.rlock()) {\n    LOG(FATAL)\n        << \"OnDestructionCallback must be canceled if needed prior to destruction\";\n  }\n}\n\nvoid EventBase::OnDestructionCallback::runCallback() noexcept {\n  scheduled_.withWLock([&](bool& scheduled) {\n    CHECK(scheduled);\n    scheduled = false;\n\n    // run can only be called by EventBase and VirtualEventBase, and it's called\n    // after the callback has been popped off the list.\n    eraser_ = nullptr;\n\n    // Note that the exclusive lock on shared state is held while the callback\n    // runs. This ensures concurrent callers to cancel() block until the\n    // callback finishes.\n    onEventBaseDestruction();\n  });\n}\n\nvoid EventBase::OnDestructionCallback::schedule(\n    Function<void(OnDestructionCallback&)> linker,\n    Function<void(OnDestructionCallback&)> eraser) {\n  eraser_ = std::move(eraser);\n  scheduled_.withWLock([](bool& scheduled) { scheduled = true; });\n  linker(*this);\n}\n\nbool EventBase::OnDestructionCallback::cancel() {\n  return scheduled_.withWLock([this](bool& scheduled) {\n    const bool wasScheduled = std::exchange(scheduled, false);\n    if (wasScheduled) {\n      auto eraser = std::move(eraser_);\n      CHECK(eraser);\n      eraser(*this);\n    }\n    return wasScheduled;\n  });\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cerrno>\n#include <cmath>\n#include <cstdlib>\n#include <functional>\n#include <list>\n#include <memory>\n#include <optional>\n#include <queue>\n#include <set>\n#include <stack>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include <boost/intrusive/list.hpp>\n#include <glog/logging.h>\n\n#include <folly/Executor.h>\n#include <folly/Function.h>\n#include <folly/Memory.h>\n#include <folly/Portability.h>\n#include <folly/ScopeGuard.h>\n#include <folly/Synchronized.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/executors/DrivableExecutor.h>\n#include <folly/executors/ExecutionObserver.h>\n#include <folly/executors/IOExecutor.h>\n#include <folly/executors/QueueObserver.h>\n#include <folly/executors/ScheduledExecutor.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/HHWheelTimer.h>\n#include <folly/io/async/Request.h>\n#include <folly/io/async/TimeoutManager.h>\n#include <folly/portability/Event.h>\n#include <folly/synchronization/CallOnce.h>\n\nnamespace folly {\nclass EventBaseBackendBase;\n\nusing Cob = Func; // defined in folly/Executor.h\n\ntemplate <typename Task, typename Consumer>\nclass EventBaseAtomicNotificationQueue;\ntemplate <typename MessageT>\nclass NotificationQueue;\n\nnamespace detail {\nclass EventBaseLocalBase;\n\n} // namespace detail\ntemplate <typename T>\nclass EventBaseLocal;\n\nclass EventBaseObserver {\n public:\n  virtual ~EventBaseObserver() = default;\n\n  virtual uint32_t getSampleRate() const = 0;\n\n  virtual void loopSample(int64_t busyTime, int64_t idleTime) = 0;\n};\n\n// Helper class that sets and retrieves the EventBase associated with a given\n// request via RequestContext. See Request.h for that mechanism.\nclass RequestEventBase : public RequestData {\n public:\n  static EventBase* get() {\n    auto data = dynamic_cast<RequestEventBase*>(\n        RequestContext::get()->getContextData(token()));\n    if (!data) {\n      return nullptr;\n    }\n    return data->eb_;\n  }\n\n  static void set(EventBase* eb) {\n    RequestContext::get()->setContextData(\n        token(), std::unique_ptr<RequestEventBase>(new RequestEventBase(eb)));\n  }\n\n  bool hasCallback() override { return false; }\n\n private:\n  FOLLY_EXPORT static RequestToken const& token() {\n    static RequestToken const token(kContextDataName);\n    return token;\n  }\n\n  explicit RequestEventBase(EventBase* eb) : eb_(eb) {}\n  EventBase* eb_;\n  static constexpr const char* kContextDataName{\"EventBase\"};\n};\n\nclass VirtualEventBase;\n\n/**\n * This class is a wrapper for all asynchronous I/O processing functionality\n *\n * EventBase provides a main loop that notifies EventHandler callback objects\n * when I/O is ready on a file descriptor, and notifies AsyncTimeout objects\n * when a specified timeout has expired.  More complex, higher-level callback\n * mechanisms can then be built on top of EventHandler and AsyncTimeout.\n *\n * A EventBase object can only drive an event loop for a single thread.  To\n * take advantage of multiple CPU cores, most asynchronous I/O servers have one\n * thread per CPU, and use a separate EventBase for each thread.\n *\n * In general, most EventBase methods may only be called from the thread\n * running the EventBase's loop.  There are a few exceptions to this rule, for\n * methods that are explicitly intended to allow communication with a\n * EventBase from other threads.  When it is safe to call a method from\n * another thread it is explicitly listed in the method comments.\n */\nclass EventBase\n    : public TimeoutManager,\n      public DrivableExecutor,\n      public IOExecutor,\n      public SequencedExecutor,\n      public ScheduledExecutor,\n      public GetThreadIdCollector {\n public:\n  friend class ScopedEventBaseThread;\n\n  using Func = folly::Function<void()>;\n\n  /**\n   * A callback interface to use with runInLoop()\n   *\n   * Derive from this class if you need to delay some code execution until the\n   * next iteration of the event loop.  This allows you to schedule code to be\n   * invoked from the top-level of the loop, after your immediate callers have\n   * returned.\n   *\n   * If a LoopCallback object is destroyed while it is scheduled to be run in\n   * the next loop iteration, it will automatically be cancelled.\n   */\n  class LoopCallback\n      : public boost::intrusive::list_base_hook<\n            boost::intrusive::link_mode<boost::intrusive::auto_unlink>> {\n   public:\n    virtual ~LoopCallback() = default;\n\n    virtual void runLoopCallback() noexcept = 0;\n    void cancelLoopCallback() {\n      context_.reset();\n      unlink();\n    }\n\n    bool isLoopCallbackScheduled() const { return is_linked(); }\n\n   private:\n    using List = boost::intrusive::\n        list<LoopCallback, boost::intrusive::constant_time_size<false>>;\n\n    // EventBase needs access to LoopCallbackList (and therefore to hook_)\n    friend class EventBase;\n    friend class VirtualEventBase;\n    std::shared_ptr<RequestContext> context_;\n  };\n\n  class FunctionLoopCallback : public LoopCallback {\n   public:\n    explicit FunctionLoopCallback(Func&& function)\n        : function_(std::move(function)) {}\n\n    void runLoopCallback() noexcept override {\n      function_();\n      delete this;\n    }\n\n   private:\n    Func function_;\n  };\n\n  // Like FunctionLoopCallback, but saves one allocation. Use with caution.\n  //\n  // The caller is responsible for maintaining the lifetime of this callback\n  // until after the point at which the contained function is called.\n  class StackFunctionLoopCallback : public LoopCallback {\n   public:\n    explicit StackFunctionLoopCallback(Func&& function)\n        : function_(std::move(function)) {}\n    void runLoopCallback() noexcept override { Func(std::move(function_))(); }\n\n   private:\n    Func function_;\n  };\n\n  // Base class for user callbacks to be run during EventBase destruction. As\n  // with LoopCallback, users may inherit from this class and provide a concrete\n  // implementation of onEventBaseDestruction(). (Alternatively, users may use\n  // the convenience method EventBase::runOnDestruction(Function<void()> f) to\n  // schedule a function f to be run on EventBase destruction.)\n  //\n  // The only thread-safety guarantees of OnDestructionCallback are as follows:\n  //   - Users may call runOnDestruction() from any thread, provided the caller\n  //     is the only user of the callback, i.e., the callback is not already\n  //     scheduled and there are no concurrent calls to schedule or cancel the\n  //     callback.\n  //   - Users may safely cancel() from any thread. Multiple calls to cancel()\n  //     may execute concurrently. The only caveat is that it is not safe to\n  //     call cancel() within the onEventBaseDestruction() callback.\n  class OnDestructionCallback\n      : public boost::intrusive::list_base_hook<\n            boost::intrusive::link_mode<boost::intrusive::normal_link>> {\n   public:\n    OnDestructionCallback() = default;\n    OnDestructionCallback(OnDestructionCallback&&) = default;\n    OnDestructionCallback& operator=(OnDestructionCallback&&) = default;\n    virtual ~OnDestructionCallback();\n\n    // Attempt to cancel the callback. If the callback is running or has already\n    // finished running, cancellation will fail. If the callback is running when\n    // cancel() is called, cancel() will block until the callback completes.\n    bool cancel();\n\n    // Callback to be invoked during ~EventBase()\n    virtual void onEventBaseDestruction() noexcept = 0;\n\n   private:\n    Function<void(OnDestructionCallback&)> eraser_;\n    Synchronized<bool> scheduled_{std::in_place, false};\n\n    using List = boost::intrusive::list<OnDestructionCallback>;\n\n    void schedule(\n        Function<void(OnDestructionCallback&)> linker,\n        Function<void(OnDestructionCallback&)> eraser);\n\n    friend class EventBase;\n    friend class VirtualEventBase;\n\n   protected:\n    virtual void runCallback() noexcept;\n  };\n\n  class FunctionOnDestructionCallback : public OnDestructionCallback {\n   public:\n    explicit FunctionOnDestructionCallback(Function<void()> f)\n        : f_(std::move(f)) {}\n\n    void onEventBaseDestruction() noexcept final { f_(); }\n\n   protected:\n    void runCallback() noexcept override {\n      OnDestructionCallback::runCallback();\n      delete this;\n    }\n\n   private:\n    Function<void()> f_;\n  };\n\n  struct Options {\n    Options() {}\n\n    /**\n     * Skip measuring event base loop durations.\n     *\n     * Disabling it would likely improve performance, but will disable some\n     * features that rely on time-measurement, including: observer, max latency\n     * and avg loop time.\n     */\n    bool skipTimeMeasurement{false};\n\n    Options& setSkipTimeMeasurement(bool skip) {\n      skipTimeMeasurement = skip;\n      return *this;\n    }\n\n    /**\n     * Factory function for creating the backend.\n     */\n    using BackendFactory =\n        std::function<std::unique_ptr<folly::EventBaseBackendBase>()>;\n    BackendFactory backendFactory{nullptr};\n\n    Options& setBackendFactory(BackendFactory factoryFn) {\n      backendFactory = std::move(factoryFn);\n      return *this;\n    }\n\n    /**\n     * Granularity of the wheel timer in the EventBase.\n     */\n    std::chrono::milliseconds timerTickInterval{\n        HHWheelTimer::DEFAULT_TICK_INTERVAL};\n\n    Options& setTimerTickInterval(std::chrono::milliseconds interval) {\n      timerTickInterval = interval;\n      return *this;\n    }\n\n    /**\n     * If non-zero, processing of loop callback and notification queue callbacks\n     * will only be allowed to run for this timeslice within each iteration\n     * (each gets one timeslice per iteration). This can be used to prevent the\n     * queues to starve event handling or each other.\n     *\n     * Does not apply to runBeforeLoop() and runAfterLoop() callbacks.\n     */\n    std::chrono::milliseconds loopCallbacksTimeslice{0};\n\n    Options& setLoopCallbacksTimeslice(std::chrono::milliseconds timeslice) {\n      loopCallbacksTimeslice = timeslice;\n      return *this;\n    }\n  };\n\n  /**\n   * Create a new EventBase object.\n   *\n   * Same as EventBase(true), which constructs an EventBase that measures time,\n   * except that this also allows the timer granularity to be specified\n   */\n\n  explicit EventBase(std::chrono::milliseconds tickInterval);\n\n  /**\n   * Create a new EventBase object.\n   *\n   * Same as EventBase(true), which constructs an EventBase that measures time.\n   */\n  EventBase() : EventBase(true) {}\n\n  /**\n   * Create a new EventBase object.\n   *\n   * @param enableTimeMeasurement Informs whether this event base should measure\n   *                              time. Disabling it would likely improve\n   *                              performance, but will disable some features\n   *                              that relies on time-measurement, including:\n   *                              observer, max latency and avg loop time.\n   */\n  explicit EventBase(bool enableTimeMeasurement);\n\n  EventBase(const EventBase&) = delete;\n  EventBase& operator=(const EventBase&) = delete;\n\n  /**\n   * Create a new EventBase object that will use the specified libevent\n   * event_base object to drive the event loop.\n   *\n   * The EventBase will take ownership of this event_base, and will call\n   * event_base_free(evb) when the EventBase is destroyed.\n   *\n   * @param enableTimeMeasurement Informs whether this event base should measure\n   *                              time. Disabling it would likely improve\n   *                              performance, but will disable some features\n   *                              that relies on time-measurement, including:\n   *                              observer, max latency and avg loop time.\n   */\n  explicit EventBase(event_base* evb, bool enableTimeMeasurement = true);\n\n  explicit EventBase(Options options);\n  ~EventBase() override;\n\n  /**\n   * Runs the event loop.\n   *\n   * loop() will loop waiting for I/O or timeouts and invoking EventHandler\n   * and AsyncTimeout callbacks as their events become ready.  loop() will\n   * only return when there are no more events remaining to process, or after\n   * terminateLoopSoon() has been called.\n   *\n   * loop() may be called again to restart event processing after a previous\n   * call to loop() or loopForever() has returned.\n   *\n   * Returns true if the loop completed normally (if it processed all\n   * outstanding requests, or if terminateLoopSoon() was called).  If an error\n   * occurs waiting for events, false will be returned.\n   */\n  bool loop();\n\n  /**\n   * Same as loop(), but doesn't wait for all keep-alive tokens to be released.\n   */\n  [[deprecated(\"This should only be used in legacy unit tests\")]] bool\n  loopIgnoreKeepAlive();\n\n  /**\n   * Wait for some events to become active, run them, then return.\n   *\n   * When EVLOOP_NONBLOCK is set in flags, the loop won't block if there\n   * are not any events to process.\n   *\n   * This is useful for callers that want to run the loop manually.\n   *\n   * Returns the same result as loop().\n   */\n  bool loopOnce(int flags = 0);\n\n  struct LoopPollOptions {\n    bool nonblock = true;\n    LoopPollOptions() {}\n  };\n\n  /**\n   * Poll the EventBase for active events, run them, then return. Unlike\n   * loopOnce, the expectation is that loopPoll will be called multiple times\n   * State is therefore persisted across calls to reflect that there is ongoing\n   * polling. Control will be returned to the calling thread between iterations.\n   * loopPollSetup and loopPollCleanup manage the maintained state across\n   * loopPoll calls.\n   *\n   * This is useful for callers that want to run the loop manually but under the\n   * context that there is continued polling being done by some thread against\n   * the EventBase.\n   *\n   * Returns the same result as loop().\n   *\n   * Must be called within a corresponding pair of loopPollSetup and\n   * loopPollCleanup; may be called many times within the pair.\n   *\n   * @param option: whether the function can block waiting for active events\n   *  true:  return immediately after all active events are processed.\n   *  false: block and keep running active events, until waken up by\n   * notification.\n   */\n  bool loopPoll(LoopPollOptions options = {});\n\n  /**\n   * Sets up state for active polling to be done against the EventBase. Call\n   * before polling via subsequent loopPoll calls.\n   *\n   * Must be matched with a corresponding call to loopPoolCleanup.\n   */\n  void loopPollSetup();\n\n  /**\n   * Clears state that was setup for active polling against the EventBase. Call\n   * after polling via loopPoolSetup and the subsequent loopPoll calls.\n   *\n   * Must be matched with a corresponding call to loopPoolSetup.\n   */\n  void loopPollCleanup();\n\n  /**\n   * Same semantics as loop(), but, instead of blocking, it returns in a\n   * \"suspended\" state. The caller must continue calling loopWithSuspension()\n   * until a non-suspended state is reached.\n   *\n   * This is only supported with backends that support pollable fd, and intended\n   * to enable external waiting for ready events through the fd: when the fd is\n   * ready, the loop can be resumed and make progress.\n   *\n   * It is not allowed to call other loop methods, or to destroy the EventBase,\n   * while in a suspended state.\n   */\n  enum class LoopStatus { kDone, kError, kSuspended };\n  LoopStatus loopWithSuspension();\n\n  /**\n   * Runs the event loop.\n   *\n   * loopForever() behaves like loop(), except that it keeps running even if\n   * when there are no more user-supplied EventHandlers or AsyncTimeouts\n   * registered.  It will only return after terminateLoopSoon() has been\n   * called.\n   *\n   * This is useful for callers that want to wait for other threads to call\n   * runInEventBaseThread(), even when there are no other scheduled events.\n   *\n   * loopForever() may be called again to restart event processing after a\n   * previous call to loop() or loopForever() has returned.\n   *\n   * Throws a std::system_error if an error occurs.\n   */\n  void loopForever();\n\n  /**\n   * Enable strict loop thread mode. This is intended for executors that take\n   * ownership of the EventBase and run it continuously until joined. Once set,\n   * it is not possible to unset it.\n   *\n   * In this mode:\n   *\n   * - isInEventBaseThread() returns false if the loop is not running.\n   *\n   * - Calling terminateLoopSoon() is not allowed, as the executor is in control\n   *   of the loop lifetime.\n   */\n  void setStrictLoopThread();\n\n  /**\n   * Causes the event loop to exit soon.\n   *\n   * This will cause an existing call to loop() or loopForever() to stop event\n   * processing and return, even if there are still events remaining to be\n   * processed.\n   *\n   * It is safe to call terminateLoopSoon() from another thread to cause loop()\n   * to wake up and return in the EventBase loop thread.  terminateLoopSoon()\n   * may also be called from the loop thread itself (for example, a\n   * EventHandler or AsyncTimeout callback may call terminateLoopSoon() to\n   * cause the loop to exit after the callback returns.)  If the loop is not\n   * running, this will cause the next call to loop to terminate soon after\n   * starting.  If a loop runs out of work (and so terminates on its own)\n   * concurrently with a call to terminateLoopSoon(), this may cause a race\n   * condition.\n   *\n   * Note that the caller is responsible for ensuring that cleanup of all event\n   * callbacks occurs properly.  Since terminateLoopSoon() causes the loop to\n   * exit even when there are pending events present, there may be remaining\n   * callbacks present waiting to be invoked.  If the loop is later restarted\n   * pending events will continue to be processed normally, however if the\n   * EventBase is destroyed after calling terminateLoopSoon() it is the\n   * caller's responsibility to ensure that cleanup happens properly even if\n   * some outstanding events are never processed.\n   */\n  void terminateLoopSoon();\n\n  /**\n   * Adds the given callback to a queue of things run after the current pass\n   * through the event loop completes.  Note that if this callback calls\n   * runInLoop() the new callback won't be called until the main event loop\n   * has gone through a cycle.\n   *\n   * This method may only be called from the EventBase's thread.  This\n   * essentially allows an event handler to schedule an additional callback to\n   * be invoked after it returns.\n   *\n   * Use runInEventBaseThread() to schedule functions from another thread.\n   *\n   * The thisIteration parameter makes this callback run in this loop\n   * iteration, instead of the next one, even if called from a\n   * runInLoop callback (normal io callbacks that call runInLoop will\n   * always run in this iteration).  This was originally added to\n   * support detachEventBase, as a user callback may have called\n   * terminateLoopSoon(), but we want to make sure we detach.  Also,\n   * detachEventBase almost always must be called from the base event\n   * loop to ensure the stack is unwound, since most users of\n   * EventBase are not thread safe.\n   *\n   * Ideally we would not need thisIteration, and instead just use\n   * runInLoop with loop() (instead of terminateLoopSoon).\n   *\n   * If loopCallbacksTimeslice is set, thisIteration is best-effort: if the\n   * timeslice expires, the callback is deferred to the next iteration.\n   */\n  void runInLoop(\n      LoopCallback* callback,\n      bool thisIteration = false,\n      std::shared_ptr<RequestContext> rctx = RequestContext::saveContext());\n\n  /**\n   * Convenience function to call runInLoop() with a folly::Function.\n   *\n   * This creates a LoopCallback object to wrap the folly::Function, and invoke\n   * the folly::Function when the loop callback fires.  This is slightly more\n   * expensive than defining your own LoopCallback, but more convenient in\n   * areas that aren't too performance sensitive.\n   *\n   * This method may only be called from the EventBase's thread.  This\n   * essentially allows an event handler to schedule an additional callback to\n   * be invoked after it returns.\n   *\n   * Use runInEventBaseThread() to schedule functions from another thread.\n   */\n  void runInLoop(Func c, bool thisIteration = false);\n\n  /**\n   * Adds the given callback to a queue of things run on destruction\n   * of current EventBase after the keepalive checks.\n   *\n   * This allows users of EventBase that run in it, but don't control it, to be\n   * notified before EventBase gets destructed.\n   *\n   * Note: will be called from the thread that invoked EventBase destructor,\n   *       before the final run of loop callbacks.\n   */\n  void runOnDestruction(OnDestructionCallback& callback);\n\n  /**\n   * Convenience function that allows users to pass in a Function<void()> to be\n   * run on EventBase destruction.\n   */\n  void runOnDestruction(Func f);\n\n  /**\n   * Adds the given callback to a queue of things run at the start of the\n   * destruction of the current EventBase, before any loop keep-alive handles\n   * are checked.\n   *\n   * Note: will be called from the thread that invoked EventBase destructor,\n   *       before the final run of loop callbacks.\n   */\n  void runOnDestructionStart(OnDestructionCallback& callback);\n\n  /**\n   * Convenience function that allows users to pass in a Function<void()> to be\n   * run at the start of EventBase destruction, before any loop keep-alive\n   * handles are checked.\n   */\n  void runOnDestructionStart(Func f);\n\n  /**\n   * Adds a callback that will run immediately *before* the event loop.\n   * This is very similar to runInLoop(), but will not cause the loop to break:\n   * For example, this callback could be used to get loop times.\n   */\n  void runBeforeLoop(LoopCallback* callback);\n\n  /**\n   * Adds a callback that will run immediately *after* the event loop.\n   * This can be used to delay some processing until after all the normal loop\n   * callback have been processed for this iteration.\n   */\n  void runAfterLoop(LoopCallback* callback);\n\n  /**\n   * Run the specified function in the EventBase's thread.\n   *\n   * This method is thread-safe, and may be called from another thread.\n   *\n   * If runInEventBaseThread() is called when the EventBase loop is not\n   * running, the function call will be delayed until the next time the loop is\n   * started.\n   *\n   * If the loop is terminated (and never later restarted) before it has a\n   * chance to run the requested function, the function will be run upon the\n   * EventBase's destruction.\n   *\n   * If two calls to runInEventBaseThread() are made from the same thread, the\n   * functions will always be run in the order that they were scheduled.\n   * Ordering between functions scheduled from separate threads is not\n   * guaranteed.\n   *\n   * @param fn  The function to run.  The function must not throw any\n   *     exceptions.\n   */\n  void runInEventBaseThread(Func fn) noexcept;\n\n  /**\n   * Run the specified function in the EventBase's thread.\n   *\n   * This method is thread-safe, and may be called from another thread.\n   *\n   * If runInEventBaseThreadAlwaysEnqueue() is called when the EventBase loop is\n   * not running, the function call will be delayed until the next time the loop\n   * is started.\n   *\n   * If the loop is terminated (and never later restarted) before it has a\n   * chance to run the requested function, the function will be run upon the\n   * EventBase's destruction.\n   *\n   * If two calls to runInEventBaseThreadAlwaysEnqueue() are made from the same\n   * thread, the functions will always be run in the order that they were\n   * scheduled. Ordering between functions scheduled from separate threads is\n   * not guaranteed. If a call is made from the EventBase thread, the function\n   * will not be executed inline and will be queued to the same queue as if the\n   * call would have been made from a different thread\n   *\n   * @param fn  The function to run.  The function must not throw any\n   *     exceptions.\n   */\n  void runInEventBaseThreadAlwaysEnqueue(Func fn) noexcept;\n\n  /**\n   * Run the specified function in the EventBase's thread\n   *\n   * This version takes a folly::Function and a RequestContext which will be in\n   * scope when the Function is invoked.\n   */\n  void runInEventBaseThreadAlwaysEnqueue(\n      std::shared_ptr<RequestContext>&& ctx, Func fn) noexcept;\n\n  /*\n   * Like runInEventBaseThread, but the caller waits for the callback to be\n   * executed.\n   */\n  void runInEventBaseThreadAndWait(Func fn) noexcept;\n\n  /**\n   * Like runInEventBaseThreadAndWait, except if the caller is already in the\n   * event base thread, the functor is simply run inline.\n   */\n  void runImmediatelyOrRunInEventBaseThreadAndWait(Func fn) noexcept;\n\n  /**\n   * Like runInEventBaseThread, but runs function immediately instead of at the\n   * end of the loop when called from the eventbase thread.\n   */\n  void runImmediatelyOrRunInEventBaseThread(Func fn) noexcept;\n\n  /**\n   * Set the maximum desired latency in us and provide a callback which will be\n   * called when that latency is exceeded.\n   * OBS: This functionality depends on time-measurement.\n   */\n  void setMaxLatency(\n      std::chrono::microseconds maxLatency,\n      Func maxLatencyCob,\n      bool dampen = true) {\n    assert(enableTimeMeasurement_);\n    maxLatency_ = maxLatency;\n    maxLatencyCob_ = std::move(maxLatencyCob);\n    dampenMaxLatency_ = dampen;\n  }\n\n  /**\n   * Set smoothing coefficient for loop load average; # of milliseconds\n   * for exp(-1) (1/2.71828...) decay.\n   */\n  void setLoadAvgMsec(std::chrono::milliseconds ms);\n\n  /**\n   * reset the load average to a desired value\n   */\n  void resetLoadAvg(double value = 0.0);\n\n  /**\n   * Get the average loop time in microseconds (an exponentially-smoothed ave)\n   */\n  double getAvgLoopTime() const {\n    assert(enableTimeMeasurement_);\n    return avgLoopTime_.get();\n  }\n\n  /**\n   * Check if the event base loop is running.\n   *\n   * This may only be used as a sanity check mechanism; it cannot be used to\n   * make any decisions; for that, consider waitUntilRunning().\n   */\n  bool isRunning() const {\n    return loopTid_.load(std::memory_order_relaxed) != kNotRunningTid;\n  }\n\n  /**\n   * Wait until the event loop starts (after starting the event loop thread).\n   */\n  void waitUntilRunning();\n\n  size_t getNotificationQueueSize() const;\n\n  /**\n   * Returns the number of loop callbacks pending execution. If this is\n   * non-zero, loopOnce() is guaranteed to run the callbacks without blocking.\n   */\n  size_t getNumLoopCallbacks() const;\n\n  uint32_t getMaxReadAtOnce() const;\n  void setMaxReadAtOnce(uint32_t maxAtOnce);\n\n  /**\n   * Verify that current thread is the EventBase thread.\n   *\n   * The definition of the EventBase thread depends on the strictLoopThread\n   * option.\n   *\n   * When the loop is running, isInEventBaseThread() returns true if and only if\n   * the current thread is the thread that is running the loop.\n   * Otherwise,\n   *\n   * - In default mode (strictLoopThread = false), isInEventBaseThread() always\n   *   returns true. this is to support use cases in which driving the loop is\n   *   interleaved with calling other EventBase methods in the same thread.\n   *\n   *   In this mode, if the loop is not running continuously it is\n   *   responsibility of the caller to ensure that all methods that may run\n   *   non-thread-safe logic (including, for example,\n   *   runImmediatelyOrRunInEventBaseThread*()) are serialized with loop runs.\n   *\n   * - In strict mode (strictLoopThread = true), isInEventBaseThread() always\n   *   returns false. This is to support use cases in which the loop is run by a\n   *   dedicated executor, possibly not continuously, so it is safe to rely on\n   *   isInEventBaseThread() from any thread with with no risk of races. In this\n   *   mode, the behavior is equivalent to inRunningEventBaseThread().\n   */\n  bool isInEventBaseThread() const;\n\n  /**\n   * Returns true if and only if the loop is running in the current thread.\n   */\n  bool inRunningEventBaseThread() const;\n\n  /**\n   * Equivalent to CHECK(isInEventBaseThread()) (and assert/DCHECK for\n   * dcheckIsInEventBaseThread), but it prints more information on\n   * failure.\n   */\n  void checkIsInEventBaseThread() const;\n  void dcheckIsInEventBaseThread() const {\n    if (kIsDebug) {\n      checkIsInEventBaseThread();\n    }\n  }\n\n  HHWheelTimer& timer() {\n    if (!wheelTimer_) {\n      wheelTimer_ = HHWheelTimer::newTimer(this, intervalDuration_);\n    }\n    return *wheelTimer_.get();\n  }\n\n  EventBaseBackendBase* getBackend() { return evb_.get(); }\n  // --------- interface to underlying libevent base ------------\n  // Avoid using these functions if possible.  These functions are not\n  // guaranteed to always be present if we ever provide alternative EventBase\n  // implementations that do not use libevent internally.\n  event_base* getLibeventBase() const;\n\n  static const char* getLibeventVersion();\n  const char* getLibeventMethod();\n\n  /**\n   * only EventHandler/AsyncTimeout subclasses and ourselves should\n   * ever call this.\n   *\n   * This is used to mark the beginning of a new loop cycle by the\n   * first handler fired within that cycle.\n   *\n   */\n  void bumpHandlingTime() final;\n\n  class SmoothLoopTime {\n   public:\n    explicit SmoothLoopTime(std::chrono::microseconds timeInterval)\n        : expCoeff_(-1.0 / static_cast<double>(timeInterval.count())),\n          value_(0.0) {\n      VLOG(11) << \"expCoeff_ \" << expCoeff_ << \" \" << __PRETTY_FUNCTION__;\n    }\n\n    void setTimeInterval(std::chrono::microseconds timeInterval);\n    void reset(double value = 0.0);\n\n    void addSample(\n        std::chrono::microseconds total, std::chrono::microseconds busy);\n\n    double get() const {\n      // Add the outstanding buffered times linearly, to avoid\n      // expensive exponentiation\n      auto lcoeff = static_cast<double>(buffer_time_.count()) * -expCoeff_;\n      return value_ * (1.0 - lcoeff) +\n          lcoeff * static_cast<double>(busy_buffer_.count());\n    }\n\n    void dampen(double factor) { value_ *= factor; }\n\n   private:\n    double expCoeff_;\n    double value_;\n    std::chrono::microseconds buffer_time_{0};\n    std::chrono::microseconds busy_buffer_{0};\n    std::size_t buffer_cnt_{0};\n    static constexpr std::chrono::milliseconds buffer_interval_{10};\n  };\n\n  void setObserver(const std::shared_ptr<EventBaseObserver>& observer) {\n    assert(enableTimeMeasurement_);\n    observer_ = observer;\n  }\n\n  const std::shared_ptr<EventBaseObserver>& getObserver() { return observer_; }\n\n  /**\n   * Setup execution observation/instrumentation for every EventHandler\n   * executed in this EventBase.\n   *\n   * @param observer EventHandle's execution observer.\n   */\n  void addExecutionObserver(ExecutionObserver* observer) {\n    executionObserverList_.push_back(*observer);\n  }\n\n  void removeExecutionObserver(ExecutionObserver* observer) {\n    executionObserverList_.erase(executionObserverList_.iterator_to(*observer));\n  }\n\n  /**\n   * Gets the execution observer list associated with this EventBase.\n   */\n  ExecutionObserver::List& getExecutionObserverList() {\n    return executionObserverList_;\n  }\n\n  /**\n   * Set the name of the thread that runs this event base.\n   */\n  void setName(const std::string& name);\n\n  /**\n   * Returns the name of the thread that runs this event base.\n   */\n  const std::string& getName();\n\n  /**\n   * Returns the ID of the thread that this event base is running in\n   */\n  std::thread::id getLoopThreadId();\n\n  /**\n   * Returns the timepoint at the start of the loop callbacks.\n   */\n  std::chrono::steady_clock::time_point getLoopCallbacksStartTime() {\n    return startWork_;\n  }\n\n  /// Implements the Executor interface\n  void add(Cob fn) override { runInEventBaseThread(std::move(fn)); }\n\n  /// Implements the DrivableExecutor interface\n  void drive() override {\n    loopKeepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    SCOPE_EXIT {\n      loopKeepAliveCount_.fetch_sub(1, std::memory_order_relaxed);\n    };\n    loopOnce();\n  }\n\n  // Implements the ScheduledExecutor interface\n  void scheduleAt(Func&& fn, TimePoint const& timeout) override;\n\n  // TimeoutManager\n  void attachTimeoutManager(\n      AsyncTimeout* obj, TimeoutManager::InternalEnum internal) final;\n\n  void detachTimeoutManager(AsyncTimeout* obj) final;\n\n  bool scheduleTimeout(\n      AsyncTimeout* obj, TimeoutManager::timeout_type timeout) final;\n\n  void cancelTimeout(AsyncTimeout* obj) final;\n\n  bool isInTimeoutManagerThread() final { return isInEventBaseThread(); }\n\n  // Returns a VirtualEventBase attached to this EventBase. Can be used to\n  // pass to APIs which expect VirtualEventBase. This VirtualEventBase will be\n  // destroyed together with the EventBase.\n  //\n  // Any number of VirtualEventBases instances may be independently constructed,\n  // which are backed by this EventBase. This method should be only used if you\n  // don't need to manage the life time of the VirtualEventBase used.\n  folly::VirtualEventBase& getVirtualEventBase();\n\n  /// Implements the IOExecutor interface\n  EventBase* getEventBase() override;\n\n  /// Implements the GetThreadIdCollector interface\n  WorkerProvider* getThreadIdCollector() override;\n\n  static std::unique_ptr<EventBaseBackendBase> getDefaultBackend();\n  static std::unique_ptr<EventBaseBackendBase> getTestBackend(int napiId);\n\n protected:\n  bool keepAliveAcquire() noexcept override;\n  void keepAliveRelease() noexcept override;\n\n private:\n  class LoopCallbacksDeadline;\n  class FuncRunner;\n  class ThreadIdCollector;\n\n  static constexpr pid_t kNotRunningTid = -1;\n  static constexpr pid_t kSuspendedTid = -2;\n\n  folly::VirtualEventBase* tryGetVirtualEventBase();\n\n  size_t loopKeepAliveCount();\n  void applyLoopKeepAlive();\n\n  /*\n   * Helper function that tells us whether we have already handled\n   * some event/timeout/callback in this loop iteration.\n   */\n  bool nothingHandledYet() const noexcept;\n\n  using LoopCallbackList = LoopCallback::List;\n\n  bool isSuccess(LoopStatus status);\n\n  struct LoopOptions {\n    bool ignoreKeepAlive = false;\n    bool allowSuspension = false;\n  };\n\n  bool loopBody(int flags, LoopOptions options);\n\n  void loopMainSetup();\n  LoopStatus loopMain(int flags, LoopOptions options);\n  void loopMainCleanup();\n\n  void runLoopCallbackList(\n      LoopCallbackList& currentCallbacks,\n      const LoopCallbacksDeadline& deadline);\n\n  // executes any callbacks queued by runInLoop(); returns false if none found\n  bool runLoopCallbacks();\n\n  void initNotificationQueue();\n\n  // Tick granularity to wheelTimer_\n  const std::chrono::milliseconds intervalDuration_{\n      HHWheelTimer::DEFAULT_TICK_INTERVAL};\n  const bool enableTimeMeasurement_;\n  const std::chrono::milliseconds loopCallbacksTimeslice_;\n  bool strictLoopThread_ = false;\n\n  // Loop state that needs to survive suspension.\n  struct LoopState {\n    std::chrono::steady_clock::time_point prev = {};\n    std::chrono::steady_clock::time_point idleStart = {};\n  };\n  // Only set while the loop is running or suspended.\n  std::optional<LoopState> loopState_;\n\n  // ID of the thread running the loop (kNotRunningTid/kSuspendedTid if loop is\n  // not running/suspended). Acts as lock to enforce loop mutual exclusion.\n  std::atomic<pid_t> loopTid_{kNotRunningTid};\n  // Store the std::thread::id as well, used to get/set thread names, and for\n  // getLoopThreadId().\n  std::atomic<std::thread::id> loopThread_{std::thread::id{}};\n\n  // should only be accessed through public getter\n  HHWheelTimer::UniquePtr wheelTimer_;\n\n  LoopCallbackList loopCallbacks_;\n  LoopCallbackList runBeforeLoopCallbacks_;\n  LoopCallbackList runAfterLoopCallbacks_;\n  Synchronized<OnDestructionCallback::List> onDestructionCallbacks_;\n  Synchronized<OnDestructionCallback::List> preDestructionCallbacks_;\n\n  // This will be null most of the time, but point to currentCallbacks\n  // if we are in the middle of running loop callbacks, such that\n  // runInLoop(..., true) will always run in the current loop\n  // iteration.\n  LoopCallbackList* runOnceCallbacks_;\n\n  // stop_ is set by terminateLoopSoon() and is used by the main loop\n  // to determine if it should exit\n  std::atomic<bool> stop_;\n\n  // A notification queue for runInEventBaseThread() to use\n  // to send function requests to the EventBase thread.\n  std::unique_ptr<EventBaseAtomicNotificationQueue<Func, FuncRunner>> queue_;\n  std::atomic<size_t> loopKeepAliveCount_{0};\n  bool loopKeepAliveActive_{false};\n\n  // limit for latency in microseconds (0 disables)\n  std::chrono::microseconds maxLatency_;\n\n  // exponentially-smoothed average loop time for latency-limiting\n  SmoothLoopTime avgLoopTime_;\n\n  // smoothed loop time used to invoke latency callbacks; differs from\n  // avgLoopTime_ in that it's scaled down after triggering a callback\n  // to reduce spamminess\n  SmoothLoopTime maxLatencyLoopTime_;\n\n  // If true, max latency callbacks will use a dampened SmoothLoopTime, else\n  // they'll use the raw loop time.\n  bool dampenMaxLatency_ = true;\n\n  // callback called when latency limit is exceeded\n  Func maxLatencyCob_;\n\n  // Wrap-around loop counter to detect beginning of each loop\n  std::size_t nextLoopCnt_;\n  std::size_t latestLoopCnt_;\n  std::chrono::steady_clock::time_point startWork_;\n\n  // Observer to export counters\n  std::shared_ptr<EventBaseObserver> observer_;\n  uint32_t observerSampleCount_;\n\n  // EventHandler's execution observer list (in case multiple are registered)\n  ExecutionObserver::List executionObserverList_;\n\n  // Name of the thread running this EventBase\n  std::string name_;\n\n  // see EventBaseLocal\n  friend class detail::EventBaseLocalBase;\n  template <typename T>\n  friend class EventBaseLocal;\n  using LocalStorageMap = folly::F14FastMap<std::size_t, erased_unique_ptr>;\n  LocalStorageMap localStorage_;\n  folly::Synchronized<folly::F14FastSet<detail::EventBaseLocalBase*>>\n      localStorageToDtor_;\n  bool tryDeregister(detail::EventBaseLocalBase&);\n\n  folly::once_flag virtualEventBaseInitFlag_;\n  std::unique_ptr<VirtualEventBase> virtualEventBase_;\n\n  // pointer to underlying backend class doing the heavy lifting\n  std::unique_ptr<EventBaseBackendBase> evb_;\n\n  std::unique_ptr<ThreadIdCollector> threadIdCollector_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseAtomicNotificationQueue-inl.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/FileUtil.h>\n#include <folly/system/Pid.h>\n\nnamespace folly {\n\ntemplate <typename Task, typename Consumer>\nEventBaseAtomicNotificationQueue<Task, Consumer>::\n    EventBaseAtomicNotificationQueue(Consumer&& consumer)\n    : pid_(get_cached_pid()),\n      notificationQueue_(),\n      consumer_(std::move(consumer)) {\n#if __has_include(<sys/eventfd.h>)\n  eventfd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);\n  if (eventfd_ == -1) {\n    auto errno_ = errno;\n    if (errno_ == ENOSYS || errno_ == EINVAL) {\n      // eventfd not availalble\n      LOG(ERROR)\n          << \"failed to create eventfd for AtomicNotificationQueue: \" << errno_\n          << \", falling back to pipe mode (is your kernel \" << \"> 2.6.30?)\";\n    } else {\n      // some other error\n      folly::throwSystemError(\n          errno, \"Failed to create eventfd for AtomicNotificationQueue\");\n    }\n  }\n#endif\n  if (eventfd_ == -1) {\n    if (fileops::pipe(pipeFds_)) {\n      folly::throwSystemError(\n          errno, \"Failed to create pipe for AtomicNotificationQueue\");\n    }\n    try {\n      // put both ends of the pipe into non-blocking mode\n      if (fcntl(pipeFds_[0], F_SETFL, O_RDONLY | O_NONBLOCK) != 0) {\n        folly::throwSystemError(\n            errno,\n            \"failed to put AtomicNotificationQueue pipe read \"\n            \"endpoint into non-blocking mode\");\n      }\n      if (fcntl(pipeFds_[1], F_SETFL, O_WRONLY | O_NONBLOCK) != 0) {\n        folly::throwSystemError(\n            errno,\n            \"failed to put AtomicNotificationQueue pipe write \"\n            \"endpoint into non-blocking mode\");\n      }\n    } catch (...) {\n      fileops::close(pipeFds_[0]);\n      fileops::close(pipeFds_[1]);\n      throw;\n    }\n  }\n}\n\ntemplate <typename Task, typename Consumer>\nEventBaseAtomicNotificationQueue<Task, Consumer>::\n    ~EventBaseAtomicNotificationQueue() {\n  // discard pending tasks and disarm the queue\n  while (drive([](Task&&) {\n    return AtomicNotificationQueueTaskStatus::DISCARD;\n  })) {\n  }\n\n  // We must unregister before closing the fd. Otherwise the base class\n  // would unregister the fd after it's already closed, which is invalid\n  // (some other thread could've opened something that reused the fd).\n  unregisterHandler();\n\n  // Don't drain fd in the child process.\n  if (pid_ == get_cached_pid()) {\n    // Wait till we observe all the writes before closing fds\n    while (writesObserved_ <\n           (successfulArmCount_ - consumerDisarmedCount_) + writesLocal_) {\n      drainFd();\n    }\n    DCHECK(\n        writesObserved_ ==\n        (successfulArmCount_ - consumerDisarmedCount_) + writesLocal_);\n  }\n  if (eventfd_ >= 0) {\n    fileops::close(eventfd_);\n    eventfd_ = -1;\n  }\n  if (pipeFds_[0] >= 0) {\n    fileops::close(pipeFds_[0]);\n    pipeFds_[0] = -1;\n  }\n  if (pipeFds_[1] >= 0) {\n    fileops::close(pipeFds_[1]);\n    pipeFds_[1] = -1;\n  }\n}\ntemplate <typename Task, typename Consumer>\nuint32_t EventBaseAtomicNotificationQueue<Task, Consumer>::getMaxReadAtOnce()\n    const {\n  return notificationQueue_.getMaxReadAtOnce();\n}\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::setMaxReadAtOnce(\n    uint32_t maxAtOnce) {\n  notificationQueue_.setMaxReadAtOnce(maxAtOnce);\n}\ntemplate <typename Task, typename Consumer>\nsize_t EventBaseAtomicNotificationQueue<Task, Consumer>::size() const {\n  return notificationQueue_.size();\n}\ntemplate <typename Task, typename Consumer>\nbool EventBaseAtomicNotificationQueue<Task, Consumer>::empty() const {\n  return notificationQueue_.empty();\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::drain() {\n  while (drive(consumer_)) {\n  }\n}\n\ntemplate <typename Task, typename Consumer>\ntemplate <typename... Args>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::putMessage(\n    Args&&... args) {\n  if (notificationQueue_.push(std::forward<Args>(args)...)) {\n    notifyFd();\n  }\n}\n\ntemplate <typename Task, typename Consumer>\nbool EventBaseAtomicNotificationQueue<Task, Consumer>::tryPutMessage(\n    Task&& task, uint32_t maxSize) {\n  auto result = notificationQueue_.tryPush(std::forward<Task>(task), maxSize);\n  if (result ==\n      AtomicNotificationQueue<Task>::TryPushResult::SUCCESS_AND_ARMED) {\n    notifyFd();\n  }\n  return result !=\n      AtomicNotificationQueue<Task>::TryPushResult::FAILED_LIMIT_REACHED;\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::stopConsuming() {\n  evb_ = nullptr;\n  cancelLoopCallback();\n  unregisterHandler();\n  detachEventBase();\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::startConsuming(\n    EventBase* evb) {\n  startConsumingImpl(evb, false);\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::startConsumingInternal(\n    EventBase* evb) {\n  startConsumingImpl(evb, true);\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::startConsumingImpl(\n    EventBase* evb, bool internal) {\n  evb_ = evb;\n  initHandler(\n      evb_,\n      folly::NetworkSocket::fromFd(eventfd_ >= 0 ? eventfd_ : pipeFds_[0]));\n  auto registerHandlerResult = internal\n      ? registerInternalHandler(READ | PERSIST)\n      : registerHandler(READ | PERSIST);\n  if (registerHandlerResult) {\n    edgeTriggeredSet_ = eventfd_ >= 0 && setEdgeTriggered();\n    ++writesLocal_;\n    notifyFd();\n  } else {\n    edgeTriggeredSet_ = false;\n  }\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::notifyFd() {\n  checkPid();\n\n  ssize_t bytes_written = 0;\n  size_t bytes_expected = 0;\n\n  do {\n    if (eventfd_ >= 0) {\n      // eventfd(2) dictates that we must write a 64-bit integer\n      uint64_t signal = 1;\n      bytes_expected = sizeof(signal);\n      bytes_written = fileops::write(eventfd_, &signal, bytes_expected);\n    } else {\n      uint8_t signal = 1;\n      bytes_expected = sizeof(signal);\n      bytes_written = fileops::write(pipeFds_[1], &signal, bytes_expected);\n    }\n  } while (bytes_written == -1 && errno == EINTR);\n\n  if (bytes_written != ssize_t(bytes_expected)) {\n    folly::throwSystemError(\n        errno,\n        \"failed to signal AtomicNotificationQueue after \"\n        \"write\");\n  }\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::drainFd() {\n  checkPid();\n\n  uint64_t message = 0;\n  if (eventfd_ >= 0) {\n    auto result = readNoInt(eventfd_, &message, sizeof(message));\n    auto errno_ = errno;\n#ifndef _WIN32\n    CHECK(\n        result == (int)sizeof(message) || errno_ == EAGAIN ||\n        errno_ == EWOULDBLOCK)\n#else\n    CHECK(\n        result == (int)sizeof(message) || errno_ == EAGAIN ||\n        errno_ == EWOULDBLOCK || errno_ == WSAECONNRESET)\n#endif\n        << \"result = \" << result << \"; errno = \" << errno_;\n    writesObserved_ += message;\n  } else {\n    ssize_t result;\n    while ((result = readNoInt(pipeFds_[0], &message, sizeof(message))) != -1) {\n      writesObserved_ += result;\n    }\n    auto errno_ = errno;\n#ifndef _WIN32\n    CHECK(result == -1 && (errno_ == EAGAIN || errno_ == EWOULDBLOCK))\n#else\n    CHECK(\n        result == -1 && (errno_ == EAGAIN || errno_ == EWOULDBLOCK) ||\n        errno_ == WSAECONNRESET)\n#endif\n        << \"result = \" << result << \"; errno = \" << errno_;\n  }\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::\n    runLoopCallback() noexcept {\n  DCHECK(!armed_);\n  if (!notificationQueue_.arm()) {\n    activateEvent();\n  } else {\n    armed_ = true;\n    successfulArmCount_++;\n  }\n}\n\ntemplate <typename Task, typename Consumer>\ntemplate <typename T>\nbool EventBaseAtomicNotificationQueue<Task, Consumer>::drive(T&& consumer) {\n  auto wasEmpty = !notificationQueue_.drive(std::forward<T>(consumer));\n  if (wasEmpty && armed_) {\n    consumerDisarmedCount_++;\n  }\n  armed_ = false;\n  return !wasEmpty;\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::handlerReady(\n    uint16_t) noexcept {\n  execute();\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::execute() {\n  if (!edgeTriggeredSet_) {\n    drainFd();\n  }\n  drive(consumer_);\n  evb_->runInLoop(this, false, nullptr);\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::activateEvent() {\n  if (!EventHandler::activateEvent(0)) {\n    // Fallback for EventBase backends that don't support activateEvent\n    ++writesLocal_;\n    notifyFd();\n  }\n}\n\ntemplate <typename Task, typename Consumer>\nvoid EventBaseAtomicNotificationQueue<Task, Consumer>::checkPid() const {\n  if (FOLLY_UNLIKELY(pid_ != get_cached_pid())) {\n    checkPidFail();\n  }\n}\n\ntemplate <typename Task, typename Consumer>\n[[noreturn]] FOLLY_NOINLINE void\nEventBaseAtomicNotificationQueue<Task, Consumer>::checkPidFail() const {\n  folly::terminate_with<std::runtime_error>(\n      \"Pid mismatch. Pid = \" + folly::to<std::string>(get_cached_pid()) +\n      \". Expecting \" + folly::to<std::string>(pid_));\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseAtomicNotificationQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AtomicNotificationQueue.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Unistd.h>\n\n#if __has_include(<sys/eventfd.h>)\n#include <sys/eventfd.h>\n#endif\n\nnamespace folly {\n\n/**\n * A producer-consumer queue for passing tasks to EventBase thread.\n *\n * Tasks can be added to the queue from any thread. A single EventBase\n * thread can be listening to the queue. Tasks are processed in FIFO order.\n */\ntemplate <typename Task, typename Consumer>\nclass EventBaseAtomicNotificationQueue\n    : private EventBase::LoopCallback,\n      private EventHandler {\n  static_assert(\n      noexcept(std::declval<Consumer>()(std::declval<Task&&>())),\n      \"Consumer::operator()(Task&&) should be noexcept.\");\n\n public:\n  explicit EventBaseAtomicNotificationQueue(Consumer&& consumer);\n\n  template <\n      typename C = Consumer,\n      typename = std::enable_if_t<std::is_default_constructible<C>::value>>\n  EventBaseAtomicNotificationQueue()\n      : EventBaseAtomicNotificationQueue(Consumer()) {}\n\n  ~EventBaseAtomicNotificationQueue() override;\n\n  uint32_t getMaxReadAtOnce() const;\n\n  /*\n   * Set the maximum number of tasks processed in a single round.\n   * Can be called from consumer thread only.\n   */\n  void setMaxReadAtOnce(uint32_t maxAtOnce);\n\n  /*\n   * Returns the number of tasks in the queue.\n   * Can be called from any thread.\n   */\n  size_t size() const;\n\n  /*\n   * Checks if the queue is empty.\n   * Can be called from consumer thread only.\n   */\n  bool empty() const;\n\n  /*\n   * Executes all tasks until the queue is empty.\n   * Can be called from consumer thread only.\n   */\n  void drain();\n\n  /*\n   * Adds a task into the queue.\n   * Can be called from any thread.\n   */\n  template <typename... Args>\n  void putMessage(Args&&... task);\n\n  /**\n   * Adds a task into the queue unless the max queue size is reached.\n   * Returns true iff the task was queued.\n   * Can be called from any thread.\n   */\n  [[nodiscard]] bool tryPutMessage(Task&& task, uint32_t maxSize);\n\n  /*\n   * Detaches the queue from an EventBase.\n   * Can be called from consumer thread only.\n   */\n  void stopConsuming();\n\n  /*\n   * Attaches the queue to an EventBase.\n   * Can be called from consumer thread only.\n   */\n  void startConsuming(EventBase* evb);\n\n  /*\n   * Attaches the queue to an EventBase.\n   * Can be called from consumer thread only.\n   *\n   * Unlike startConsuming, startConsumingInternal registers this queue as\n   * an internal event. This means that this event may be skipped if\n   * EventBase doesn't have any other registered events. This generally should\n   * only be used for queues managed by an EventBase itself.\n   */\n  void startConsumingInternal(EventBase* evb);\n\n  /*\n   * Executes one round of tasks. Re-activates the event if more tasks are\n   * available.\n   * Can be called from consumer thread only.\n   */\n  void execute();\n\n private:\n  /*\n   * Adds a task to the queue without incrementing the push count.\n   */\n  template <typename T>\n  void putMessageImpl(T&& task);\n\n  template <typename T>\n  bool drive(T&& t);\n\n  /*\n   * Write into the signal fd to wake up the consumer thread.\n   */\n  void notifyFd();\n\n  /*\n   * Read all messages from the signal fd.\n   */\n  void drainFd();\n\n  /*\n   * Either arm the queue or reactivate the EventBase event.\n   * This has to be a loop callback because the event can't be activated from\n   * within the event callback. It also allows delayed re-arming the queue.\n   */\n  void runLoopCallback() noexcept override;\n\n  void startConsumingImpl(EventBase* evb, bool internal);\n\n  void handlerReady(uint16_t) noexcept override;\n\n  void activateEvent();\n\n  /**\n   * Check that the AtomicNotificationQueue is being used from the correct\n   * process.\n   *\n   * If you create a AtomicNotificationQueue in one process, then fork, and try\n   * to send messages to the queue from the child process, you're going to have\n   * a bad time.  Unfortunately users have (accidentally) run into this.\n   *\n   * Because we use an eventfd/pipe, the child process can actually signal the\n   * parent process that an event is ready.  However, it can't put anything on\n   * the parent's queue, so the parent wakes up and finds an empty queue.  This\n   * check ensures that we catch the problem in the misbehaving child process\n   * code, and crash before signalling the parent process.\n   */\n  void checkPid() const;\n\n  [[noreturn]] FOLLY_NOINLINE void checkPidFail() const;\n\n  int eventfd_{-1};\n  int pipeFds_[2]{-1, -1}; // to fallback to on older/non-linux systems\n  /*\n   * If event is registered with the EventBase, this describes whether\n   * edge-triggered flag was set for it. For edge-triggered events we don't\n   * need to drain the fd to deactivate them.\n   */\n  EventBase* evb_{nullptr};\n  const pid_t pid_;\n  AtomicNotificationQueue<Task> notificationQueue_;\n  Consumer consumer_;\n  ssize_t successfulArmCount_{0};\n  ssize_t consumerDisarmedCount_{0};\n  ssize_t writesObserved_{0};\n  ssize_t writesLocal_{0};\n  bool armed_{false};\n  bool edgeTriggeredSet_{false};\n};\n\n} // namespace folly\n\n#include <folly/io/async/EventBaseAtomicNotificationQueue-inl.h>\n"
  },
  {
    "path": "folly/io/async/EventBaseBackendBase.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventBaseBackendBase.h>\n\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\nvoid EventBaseEvent::eb_ev_base(EventBase* evb) {\n  evb_ = evb;\n  event_.ev_base = evb ? evb->getLibeventBase() : nullptr;\n}\n\nint EventBaseEvent::eb_event_base_set(EventBase* evb) {\n  evb_ = evb;\n  auto* base = evb_ ? evb_->getLibeventBase() : nullptr;\n  if (base) {\n    return ::event_base_set(base, &event_);\n  }\n  return 0;\n}\n\nint EventBaseEvent::eb_event_add(const struct timeval* timeout) {\n  if (auto* backend = getBackend()) {\n    return backend->eb_event_add(*this, timeout);\n  }\n  return -1;\n}\n\nint EventBaseEvent::eb_event_del() {\n  if (auto* backend = getBackend()) {\n    return backend->eb_event_del(*this);\n  }\n  return -1;\n}\n\nbool EventBaseEvent::eb_event_active(int res) {\n  if (auto* backend = getBackend()) {\n    return backend->eb_event_active(*this, res);\n  }\n  return false;\n}\n\nbool EventBaseEvent::setEdgeTriggered() {\n  if (auto* backend = getBackend()) {\n    return backend->setEdgeTriggered(*this);\n  }\n  return false;\n}\n\nEventBaseBackendBase* EventBaseEvent::getBackend() const {\n  return evb_ ? evb_->getBackend() : nullptr;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseBackendBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/EventUtil.h>\n#include <folly/net/NetOps.h>\n#include <folly/portability/Event.h>\n#include <folly/portability/IOVec.h>\n\nnamespace folly {\n\nclass EventBase;\nclass EventBaseBackendBase;\n\nclass EventBaseEvent {\n public:\n  EventBaseEvent() = default;\n  ~EventBaseEvent() {\n    if (userData_ && freeFn_) {\n      freeFn_(userData_);\n    }\n  }\n\n  EventBaseEvent(const EventBaseEvent&) = delete;\n  EventBaseEvent& operator=(const EventBaseEvent&) = delete;\n\n  typedef void (*FreeFunction)(void* userData);\n\n  const struct event* getEvent() const { return &event_; }\n\n  struct event* getEvent() { return &event_; }\n\n  bool isEventRegistered() const {\n    return EventUtil::isEventRegistered(&event_);\n  }\n\n  libevent_fd_t eb_ev_fd() const { return event_.ev_fd; }\n\n  short eb_ev_events() const { return event_.ev_events; }\n\n  int eb_ev_res() const { return event_.ev_res; }\n\n  void* getUserData() { return userData_; }\n  FreeFunction getFreeFunction() const { return freeFn_; }\n\n  void setUserData(void* userData) { userData_ = userData; }\n\n  void setUserData(void* userData, FreeFunction freeFn) {\n    userData_ = userData;\n    freeFn_ = freeFn;\n  }\n\n  void eb_event_set(\n      libevent_fd_t fd,\n      short events,\n      void (*callback)(libevent_fd_t, short, void*),\n      void* arg) {\n    event_set(&event_, fd, events, callback, arg);\n  }\n\n  void eb_signal_set(\n      int signum, void (*callback)(libevent_fd_t, short, void*), void* arg) {\n    event_set(&event_, signum, EV_SIGNAL | EV_PERSIST, callback, arg);\n  }\n\n  void eb_timer_set(void (*callback)(libevent_fd_t, short, void*), void* arg) {\n    event_set(&event_, -1, 0, callback, arg);\n  }\n\n  void eb_ev_base(EventBase* evb);\n  EventBase* eb_ev_base() const { return evb_; }\n\n  int eb_event_base_set(EventBase* evb);\n\n  int eb_event_add(const struct timeval* timeout);\n\n  int eb_event_del();\n\n  bool eb_event_active(int res);\n\n  bool setEdgeTriggered();\n\n protected:\n  struct event event_;\n\n  EventBaseBackendBase* getBackend() const;\n\n  EventBase* evb_{nullptr};\n  void* userData_{nullptr};\n  FreeFunction freeFn_{nullptr};\n};\n\nclass EventBaseBackendBase {\n public:\n  using Event = EventBaseEvent;\n  using FactoryFunc =\n      std::function<std::unique_ptr<folly::EventBaseBackendBase>()>;\n  using RecvZcCallback = folly::Function<void(ssize_t)>;\n\n  // Per-EventBase hooks invoked around the poll syscall (epoll_wait /\n  // io_uring CQE reaping).  Only EpollBackend and IoUringBackend invoke\n  // these hooks; other backends (e.g. LibeventBackend) do not.\n  //\n  // prePollLoopHook is called immediately before the poll syscall.\n  // postPollLoopHook is called immediately after, receiving the number of\n  // events returned by the syscall.\n  // Both hooks share a single opaque context pointer.\n  using PrePollLoopHook = void (*)(void* ctx);\n  using PostPollLoopHook = void (*)(void* ctx, int numEvents);\n\n  struct PollLoopHook {\n    PrePollLoopHook preLoopHook = nullptr;\n    PostPollLoopHook postLoopHook = nullptr;\n    void* hookCtx = nullptr;\n  };\n\n  EventBaseBackendBase() = default;\n  virtual ~EventBaseBackendBase() = default;\n\n  EventBaseBackendBase(const EventBaseBackendBase&) = delete;\n  EventBaseBackendBase& operator=(const EventBaseBackendBase&) = delete;\n\n  virtual int getPollableFd() const { return -1; }\n\n  virtual int getNapiId() const { return -1; }\n  virtual void queueRecvZc(\n      int /*fd*/,\n      void* /*buf*/,\n      unsigned long /*nbytes*/,\n      RecvZcCallback&& /*callback*/) {}\n\n  void setPollLoopHook(PollLoopHook pollLoopHook) {\n    pollLoopHook_ = pollLoopHook;\n  }\n\n  virtual event_base* getEventBase() = 0;\n  virtual int eb_event_base_loop(int flags) = 0;\n  virtual int eb_event_base_loopbreak() = 0;\n\n  virtual int eb_event_add(Event& event, const struct timeval* timeout) = 0;\n  virtual int eb_event_del(Event& event) = 0;\n\n  virtual bool eb_event_active(Event& event, int res) = 0;\n\n  virtual bool setEdgeTriggered(Event& /* event */) { return false; }\n\n protected:\n  PollLoopHook pollLoopHook_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseLocal.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventBaseLocal.h>\n\n#include <atomic>\n#include <thread>\n\n#include <folly/Memory.h>\n\nnamespace folly {\nnamespace detail {\n\nEventBaseLocalBase::~EventBaseLocalBase() {\n  // Remove self from all registered EventBase instances.\n  // Notice that we could be racing with EventBase dtor similarly\n  // deregistering itself from all registered EventBaseLocal instances. Because\n  // both sides need to acquire two locks, but in inverse order, we retry if\n  // inner lock acquisition fails to prevent lock inversion deadlock.\n  while (true) {\n    auto locked = eventBases_.wlock();\n    if (locked->empty()) {\n      break;\n    }\n    auto* evb = *locked->begin();\n    if (evb->tryDeregister(*this)) {\n      locked->erase(evb);\n    }\n  }\n}\n\nvoid* EventBaseLocalBase::getVoid(EventBase& evb) {\n  evb.dcheckIsInEventBaseThread();\n\n  if (auto it = evb.localStorage_.find(keyToken_, key_);\n      it != evb.localStorage_.end()) {\n    return it->second.get();\n  }\n  return nullptr;\n}\n\nvoid EventBaseLocalBase::erase(EventBase& evb) {\n  evb.dcheckIsInEventBaseThread();\n\n  evb.localStorage_.erase(key_);\n  evb.localStorageToDtor_.wlock()->erase(this);\n\n  eventBases_.wlock()->erase(&evb);\n}\n\nbool EventBaseLocalBase::tryDeregister(EventBase& evb) {\n  evb.dcheckIsInEventBaseThread();\n\n  if (auto locked = eventBases_.tryWLock()) {\n    locked->erase(&evb);\n    return true;\n  }\n  return false;\n}\n\nvoid EventBaseLocalBase::setVoid(\n    EventBase& evb, void* ptr, void (*dtor)(void*)) {\n  // construct the unique-ptr eagerly, just in case anything between this and\n  // the emplace below could throw\n  auto erased = erased_unique_ptr{ptr, dtor};\n\n  evb.dcheckIsInEventBaseThread();\n\n  bool inserted =\n      evb.localStorage_.try_emplace_token(keyToken_, key_, std::move(erased))\n          .second;\n  if (inserted) {\n    eventBases_.wlock()->insert(&evb);\n    evb.localStorageToDtor_.wlock()->insert(this);\n  }\n}\n\nstd::atomic<std::size_t> EventBaseLocalBase::keyCounter_{0};\n} // namespace detail\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseLocal.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <mutex>\n#include <utility>\n\n#include <folly/Likely.h>\n#include <folly/Synchronized.h>\n#include <folly/container/F14Set.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/lang/Thunk.h>\n\nnamespace folly {\n\nnamespace detail {\n\nclass EventBaseLocalBase {\n public:\n  friend class folly::EventBase;\n  EventBaseLocalBase() = default;\n  EventBaseLocalBase(const EventBaseLocalBase&) = delete;\n  EventBaseLocalBase& operator=(const EventBaseLocalBase&) = delete;\n  ~EventBaseLocalBase();\n  void erase(EventBase& evb);\n\n protected:\n  void setVoid(EventBase& evb, void* ptr, void (*dtor)(void*));\n  void* getVoid(EventBase& evb);\n\n private:\n  bool tryDeregister(EventBase& evb);\n\n  static std::atomic<std::size_t> keyCounter_;\n\n  const std::size_t key_{keyCounter_++};\n  const folly::F14HashToken keyToken_{\n      folly::EventBase::LocalStorageMap{}.prehash(key_)};\n  folly::Synchronized<folly::F14FastSet<EventBase*>> eventBases_;\n};\n\n} // namespace detail\n\n/**\n * A storage abstraction for data that should be tied to an EventBase.\n *\n *   struct Foo { Foo(int a, int b); };\n *   EventBaseLocal<Foo> myFoo;\n *   ...\n *   EventBase evb;\n *   myFoo.emplace(evb, Foo(1, 2));\n *   myFoo.emplace(evb, 1, 2);\n *   Foo* foo = myFoo.get(evb);\n *   myFoo.erase(evb);\n *   Foo& foo = myFoo.try_emplace(evb, 1, 2); // ctor if missing\n *   Foo& foo = myFoo.try_emplace(evb, 1, 2); // noop if present\n *   myFoo.erase(evb);\n *   Foo& foo = myFoo.try_emplace_with(evb, [] { return Foo(3, 4); })\n *\n * The objects will be deleted when the EventBaseLocal or the EventBase is\n * destructed (whichever comes first). All methods must be called from the\n * EventBase thread.\n *\n * The user is responsible for throwing away invalid references/ptrs returned\n * by the get() method after emplace/erase is called.  If shared ownership is\n * needed, use a EventBaseLocal<shared_ptr<...>>.\n */\ntemplate <typename T>\nclass EventBaseLocal : public detail::EventBaseLocalBase {\n private:\n  template <typename U, typename... A>\n  using if_ilist_emplaceable_t = std::enable_if_t<\n      std::is_constructible<T, std::initializer_list<U>, A...>::value,\n      int>;\n\n  T& store(EventBase& evb, T* const ptr) {\n    setVoid(evb, ptr, detail::thunk::ruin<T>);\n    return *ptr;\n  }\n\n public:\n  EventBaseLocal() = default;\n\n  T* get(EventBase& evb) { return static_cast<T*>(getVoid(evb)); }\n\n  template <typename... A>\n  T& emplace(EventBase& evb, A&&... a) {\n    return store(evb, new T(static_cast<A&&>(a)...));\n  }\n\n  template <typename U, typename... A, if_ilist_emplaceable_t<U, A...> = 0>\n  T& emplace(EventBase& evb, std::initializer_list<U> i, A&&... a) {\n    return store(evb, new T(i, static_cast<A&&>(a)...));\n  }\n\n  template <typename F>\n  T& emplace_with(EventBase& evb, F f) {\n    return store(evb, new T(f()));\n  }\n\n  template <typename... A>\n  T& try_emplace(EventBase& evb, A&&... a) {\n    auto const ptr = get(evb);\n    return FOLLY_LIKELY(!!ptr) ? *ptr : emplace(evb, static_cast<A&&>(a)...);\n  }\n\n  template <typename U, typename... A, if_ilist_emplaceable_t<U, A...> = 0>\n  T& try_emplace(EventBase& evb, std::initializer_list<U> i, A&&... a) {\n    auto const ptr = get(evb);\n    return FOLLY_LIKELY(!!ptr) ? *ptr : emplace(evb, i, static_cast<A&&>(a)...);\n  }\n\n  template <typename F>\n  T& try_emplace_with(EventBase& evb, F f) {\n    auto const ptr = get(evb);\n    return FOLLY_LIKELY(!!ptr) ? *ptr : emplace_with(evb, std::ref(f));\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseManager.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventBaseManager.h>\n\nnamespace folly {\n\nstd::atomic<EventBaseManager*> globalManager(nullptr);\n\nEventBaseManager* EventBaseManager::get() {\n  EventBaseManager* mgr = globalManager;\n  if (mgr) {\n    return mgr;\n  }\n\n  auto new_mgr = new EventBaseManager;\n  bool exchanged = globalManager.compare_exchange_strong(mgr, new_mgr);\n  if (!exchanged) {\n    delete new_mgr;\n    return mgr;\n  } else {\n    return new_mgr;\n  }\n}\n\n/*\n * EventBaseManager methods\n */\n\nvoid EventBaseManager::setEventBase(EventBase* eventBase, bool takeOwnership) {\n  auto& info = *localStore_.get();\n  if (info) {\n    throw std::runtime_error(\n        \"EventBaseManager: cannot set a new EventBase \"\n        \"for this thread when one already exists\");\n  }\n\n  info.emplace(eventBase, takeOwnership);\n}\n\nvoid EventBaseManager::clearEventBase() {\n  auto& info = *localStore_.get();\n  if (info && info->isOwned) {\n    // EventBase destructor may invoke user callbacks that rely on\n    // getEventBase() returning the current EventBase, so make sure that the\n    // info is reset only after the EventBase is destroyed.\n    delete info->eventBase;\n    info->eventBase = nullptr;\n  }\n  info.reset();\n}\n\nEventBase* EventBaseManager::getEventBase() const {\n  auto& info = *localStore_.get();\n  if (!info) {\n    auto evb = std::make_unique<EventBase>(options_);\n    info.emplace(evb.release(), true);\n\n    if (observer_) {\n      info->eventBase->setObserver(observer_);\n    }\n  }\n\n  return info->eventBase;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/Optional.h>\n#include <folly/ThreadLocal.h>\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\n\n/**\n * Manager for per-thread EventBase objects.\n *   This class will find or create a EventBase for the current\n *   thread, associated with thread-specific storage for that thread.\n *   Although a typical application will generally only have one\n *   EventBaseManager, there is no restriction on multiple instances;\n *   the EventBases belong to one instance are isolated from those of\n *   another.\n */\nclass EventBaseManager {\n public:\n  /**\n   * Constructing a EventBaseManager directly is DEPRECATED and not\n   * encouraged. You should instead use the global singleton if possible.\n   */\n  EventBaseManager() = default;\n\n  explicit EventBaseManager(\n      folly::EventBase::Options options,\n      std::shared_ptr<EventBaseObserver> observer = nullptr)\n      : options_(std::move(options)), observer_(std::move(observer)) {}\n\n  ~EventBaseManager() = default;\n\n  explicit EventBaseManager(const std::shared_ptr<EventBaseObserver>& observer)\n      : observer_(observer) {}\n\n  /**\n   * Get the global EventBaseManager for this program. Ideally all users\n   * of EventBaseManager go through this interface and do not construct\n   * EventBaseManager directly.\n   */\n  static EventBaseManager* get();\n\n  /**\n   * Get the EventBase for this thread, or create one if none exists yet.\n   *\n   * If no EventBase exists for this thread yet, a new one will be created and\n   * returned.  May throw std::bad_alloc if allocation fails.\n   */\n  EventBase* getEventBase() const;\n\n  /**\n   * Get the EventBase for this thread.\n   *\n   * Returns nullptr if no EventBase has been created for this thread yet.\n   */\n  EventBase* getExistingEventBase() const {\n    if (const auto& info = *localStore_.get()) {\n      return info->eventBase;\n    }\n    return nullptr;\n  }\n\n  /**\n   * Set the EventBase to be used by this thread.\n   *\n   * This may only be called if no EventBase has been defined for this thread\n   * yet.  If a EventBase is already defined for this thread, a\n   * std::runtime_error is thrown.  std::bad_alloc may also be thrown if\n   * allocation fails while setting the EventBase.\n   *\n   * This should typically be invoked by the code that will call loop() on the\n   * EventBase, to make sure the EventBaseManager points to the correct\n   * EventBase that is actually running in this thread.\n   */\n  void setEventBase(EventBase* eventBase, bool takeOwnership);\n\n  /**\n   * Clear the EventBase for this thread.\n   *\n   * This can be used if the code driving the EventBase loop() has finished\n   * the loop and new events should no longer be added to the EventBase.\n   */\n  void clearEventBase();\n\n private:\n  struct EventBaseInfo {\n    EventBaseInfo(EventBase* evb, bool owned)\n        : eventBase(evb), isOwned(owned) {}\n\n    ~EventBaseInfo() {\n      if (isOwned) {\n        delete eventBase;\n      }\n    }\n\n    EventBase* eventBase;\n    bool isOwned;\n  };\n\n  EventBaseManager(EventBaseManager const&) = delete;\n  EventBaseManager& operator=(EventBaseManager const&) = delete;\n\n  folly::EventBase::Options options_;\n  mutable folly::ThreadLocal<folly::Optional<EventBaseInfo>> localStore_;\n  std::shared_ptr<folly::EventBaseObserver> observer_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBasePoller.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventBasePoller.h>\n\n#include <atomic>\n#include <stdexcept>\n\n#include <boost/polymorphic_cast.hpp>\n#include <fmt/format.h>\n#include <glog/logging.h>\n#include <folly/FileUtil.h>\n#include <folly/String.h>\n#include <folly/io/async/Epoll.h>\n#include <folly/io/async/Liburing.h>\n#include <folly/lang/Align.h>\n#include <folly/portability/GFlags.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/system/ThreadName.h>\n\n#if FOLLY_HAS_EPOLL\n// @lint-ignore CLANGTIDY facebook-hte-PortabilityInclude-poll.h\n#include <poll.h>\n#include <sys/epoll.h>\n#include <sys/eventfd.h>\n#endif\n\n#if FOLLY_HAS_LIBURING\n#include <liburing.h> // @manual\n#endif\n\nFOLLY_GFLAGS_DEFINE_string(\n    folly_event_base_poller_backend,\n    \"epoll\",\n    \"Available EventBasePoller backends: \\\"epoll\\\", \\\"io_uring\\\"\");\nFOLLY_GFLAGS_DEFINE_uint64(\n    folly_event_base_poller_spin_us,\n    10,\n    \"Spin-wait for events up to this amount before blocking wait\");\nFOLLY_GFLAGS_DEFINE_uint64(\n    folly_event_base_poller_sleep_us,\n    0,\n    \"Sleep for this amount before doing a blocking wait for events\");\n\n// Epoll backend.\nFOLLY_GFLAGS_DEFINE_uint64(\n    folly_event_base_poller_epoll_max_events,\n    64,\n    \"Maximum number of events to process in one iteration when \"\n    \"using the epoll EventBasePoller backend\");\nFOLLY_GFLAGS_DEFINE_bool(\n    folly_event_base_poller_epoll_rearm_inline,\n    true,\n    \"When using epoll backend, re-arm events inline in handoff() instead of \"\n    \"returning them to the poller thread\");\n\n// io_uring backend.\nFOLLY_GFLAGS_DEFINE_uint64(\n    folly_event_base_poller_io_uring_sq_entries,\n    128,\n    \"Minimum number of entries to allocate for the submission queue when \"\n    \"using the io_uring EventBasePoller backend\");\n\nnamespace folly::detail {\n\nnamespace {\n\ntemplate <class T>\nclass Queue {\n public:\n  bool insert(T* t) {\n    DCHECK(t->next == nullptr);\n\n    auto oldHead = head_.load(std::memory_order_relaxed);\n    bool ret;\n    do {\n      t->next = (ret = (oldHead == kQueueArmedTag())) ? nullptr : oldHead;\n    } while (!head_.compare_exchange_weak(\n        oldHead, t, std::memory_order_release, std::memory_order_relaxed));\n    return ret;\n  }\n\n  T* arm() {\n    T* oldHead = head_.load(std::memory_order_relaxed);\n    T* newHead;\n    T* ret;\n    do {\n      if (oldHead == nullptr || oldHead == kQueueArmedTag()) {\n        newHead = kQueueArmedTag();\n        ret = nullptr;\n      } else {\n        newHead = nullptr;\n        ret = oldHead;\n      }\n    } while (!head_.compare_exchange_weak(\n        oldHead,\n        newHead,\n        std::memory_order_acq_rel,\n        std::memory_order_relaxed));\n\n    return ret;\n  }\n\n private:\n  static T* kQueueArmedTag() { return reinterpret_cast<T*>(1); }\n\n  std::atomic<T*> head_{kQueueArmedTag()};\n};\n\n#if FOLLY_HAS_EPOLL\n\nclass EventBasePollerImpl : public EventBasePoller {\n  class FdGroupImpl;\n\n public:\n  explicit EventBasePollerImpl(bool rearmInline)\n      : rearmInline_(rearmInline),\n        notificationEv_(\n            Event::NotificationFd{}, ::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {\n    PCHECK(notificationEv_.fd >= 0);\n  }\n\n  ~EventBasePollerImpl() override {\n    CHECK_EQ(numGroups_.load(), 0)\n        << \"All groups must be destroyed before EventBasePoller\";\n    fileops::close(notificationEv_.fd);\n  }\n\n  std::unique_ptr<FdGroup> makeFdGroup(ReadyCallback readyCallback) override;\n\n protected:\n  void startLoop() {\n    Baton<> started;\n    loopThread_ = std::make_unique<std::thread>([this, &started]() {\n      loop(started);\n    });\n    started.wait();\n  }\n\n  void stopLoop() {\n    stop_ = true;\n    notifyEvfd();\n    loopThread_->join();\n  }\n\n  struct Event final : public Handle {\n    struct NotificationFd {};\n\n    Event(FdGroupImpl& group_, int fd_, void* userData)\n        : Handle(userData), group(&group_), fd(fd_) {}\n    // Special internal event to poll the notification eventfd.\n    Event(NotificationFd, int fd_) : Handle(nullptr), group(nullptr), fd(fd_) {}\n\n    ~Event() override {\n      CHECK(isNotificationFd() || joined_.ready())\n          << \"Handle must be reclaimed before destruction\";\n    }\n\n    bool isNotificationFd() const { return group == nullptr; }\n\n    // TSAN is not able to recognize the happens-before relationship between\n    // rearming (epoll_ctl) and handling ready events, so use a fake mutex to\n    // introduce the expected relationships and at the same time check them.\n    FOLLY_ALWAYS_INLINE void markReady() {\n#ifdef FOLLY_SANITIZE_THREAD\n      CHECK(!ready_.exchange(true, std::memory_order_acq_rel));\n#endif\n    }\n    FOLLY_ALWAYS_INLINE void markProcessed() {\n#ifdef FOLLY_SANITIZE_THREAD\n      CHECK(ready_.exchange(false, std::memory_order_release));\n#endif\n    }\n\n    void handoff(bool done) override;\n\n    void handleHandoff();\n    void join();\n\n    FdGroupImpl* group;\n    const int fd;\n    bool registered = false; // Managed by addEvent()/delEvent().\n    Event* next{nullptr}; // Managed by Queue.\n\n   private:\n    bool joining_ = false;\n    Baton<> joined_;\n#ifdef FOLLY_SANITIZE_THREAD\n    std::atomic<bool> ready_{true};\n#endif\n  };\n\n private:\n  virtual void setup() = 0;\n  virtual void teardown() = 0;\n  virtual void addEvent(Event* event) = 0;\n  virtual void delEvent(Event* event) = 0;\n  virtual bool waitForEvents(\n      std::chrono::steady_clock::time_point loopStart) = 0;\n\n  void notifyEvfd();\n  void returnEvent(Event* event);\n\n  void handleNotification();\n  void handleReadyEvents();\n  void loop(folly::Baton<>& started);\n\n protected:\n  const bool rearmInline_;\n  std::vector<Event*> readyEvents_;\n\n private:\n  std::atomic<size_t> numGroups_{0};\n  Event notificationEv_;\n  std::vector<std::unique_ptr<Event>> events_;\n  std::unique_ptr<std::thread> loopThread_;\n  std::vector<Handle*> readyHandles_; // Cache allocation.\n  std::atomic<bool> stop_{false};\n  Queue<Event> returnQueue_;\n};\n\nclass EventBasePollerImpl::FdGroupImpl final : public FdGroup {\n public:\n  FdGroupImpl(EventBasePollerImpl& parent_, ReadyCallback readyCallback_)\n      : parent(parent_), readyCallback(std::move(readyCallback_)) {\n    ++parent.numGroups_;\n  }\n\n  ~FdGroupImpl() override {\n    CHECK_EQ(numHandles_, 0)\n        << \"All EventBases must be reclaimed before group destruction\";\n    --parent.numGroups_;\n  }\n\n  std::unique_ptr<Handle> add(int fd, void* userData) override {\n    auto handle = std::make_unique<Event>(*this, fd, userData);\n    ++numHandles_;\n    // Run the first iteration to register the fd.\n    Handle* handlePtr = handle.get();\n    readyCallback({&handlePtr, 1});\n    return handle;\n  }\n\n  void reclaim(std::unique_ptr<Handle> handle) override {\n    boost::polymorphic_downcast<Event*>(handle.get())->join();\n    --numHandles_;\n  }\n\n  EventBasePollerImpl& parent;\n  const ReadyCallback readyCallback;\n\n private:\n  size_t numHandles_ = 0;\n};\n\nvoid EventBasePollerImpl::Event::handoff(bool done) {\n  DCHECK(!isNotificationFd());\n  CHECK(!joining_);\n  joining_ = done;\n  if (group->parent.rearmInline_) {\n    handleHandoff();\n  } else {\n    group->parent.returnEvent(this);\n  }\n}\n\nvoid EventBasePollerImpl::Event::handleHandoff() {\n  DCHECK(!isNotificationFd());\n  if (joining_) {\n    group->parent.delEvent(this);\n    joined_.post();\n    return;\n  }\n\n  markProcessed();\n  group->parent.addEvent(this);\n}\n\nvoid EventBasePollerImpl::Event::join() {\n  DCHECK(!isNotificationFd());\n  joined_.wait();\n}\n\nvoid EventBasePollerImpl::notifyEvfd() {\n  uint64_t val = 1;\n  auto ret = writeNoInt(notificationEv_.fd, &val, sizeof(val));\n  PCHECK(ret == sizeof(val)) << ret;\n}\n\nvoid EventBasePollerImpl::returnEvent(Event* event) {\n  if (returnQueue_.insert(event)) {\n    notifyEvfd();\n  }\n}\n\nstd::unique_ptr<EventBasePoller::FdGroup> EventBasePollerImpl::makeFdGroup(\n    ReadyCallback readyCallback) {\n  return std::make_unique<FdGroupImpl>(*this, std::move(readyCallback));\n}\n\nvoid EventBasePollerImpl::handleNotification() {\n  while (auto* event = returnQueue_.arm()) {\n    while (event) {\n      auto* next = std::exchange(event->next, nullptr);\n      event->handleHandoff();\n      event = next;\n    }\n  }\n  notificationEv_.markProcessed();\n  addEvent(&notificationEv_);\n}\n\nvoid EventBasePollerImpl::handleReadyEvents() {\n  CHECK(!readyEvents_.empty());\n  // Sort by group so we can call readyCallback on each group.\n  std::sort(\n      readyEvents_.begin(),\n      readyEvents_.end(),\n      [](const Event* lhs, const Event* rhs) {\n        // Sort backwards so the notification event (group == nullptr) is\n        // processed last.\n        return std::greater<>{}(lhs->group, rhs->group);\n      });\n\n  auto it = readyEvents_.begin();\n  FdGroupImpl* curGroup = (*it)->group;\n  while (true) {\n    if (it == readyEvents_.end() || (*it)->group != curGroup) {\n      if (curGroup == nullptr) {\n        // There can only be one notification event.\n        CHECK_EQ(readyHandles_.size(), 1);\n        CHECK_EQ(readyHandles_[0], &notificationEv_);\n        handleNotification();\n      } else {\n        curGroup->readyCallback(folly::range(readyHandles_));\n      }\n      readyHandles_.clear();\n      if (it == readyEvents_.end()) {\n        break;\n      }\n      curGroup = (*it)->group;\n    }\n    (*it)->markReady();\n    readyHandles_.push_back(*it++);\n  }\n\n  readyEvents_.clear();\n}\n\nvoid EventBasePollerImpl::loop(folly::Baton<>& started) {\n  setThreadName(\"EventBasePoller\");\n\n  setup();\n  handleNotification(); // Nothing to handle, but arm notificationEv_.\n  started.post();\n\n  auto lastLoopTs = std::chrono::steady_clock::now();\n  while (!stop_.load()) {\n    if (!waitForEvents(lastLoopTs)) {\n      continue;\n    }\n    auto waitEndTs = std::chrono::steady_clock::now();\n    handleReadyEvents();\n    auto busyEndTs = std::chrono::steady_clock::now();\n    stats_.wlock()->update(\n        readyEvents_.size(), waitEndTs - lastLoopTs, busyEndTs - waitEndTs);\n    lastLoopTs = busyEndTs;\n  }\n}\n\nclass EventBasePollerEpoll final : public EventBasePollerImpl {\n public:\n  EventBasePollerEpoll()\n      : EventBasePollerImpl(FLAGS_folly_event_base_poller_epoll_rearm_inline) {\n    startLoop();\n  }\n\n  ~EventBasePollerEpoll() override { stopLoop(); }\n\n  void setup() override {\n    epFd_ = ::epoll_create1(EPOLL_CLOEXEC);\n    PCHECK(epFd_ > 0);\n  }\n\n  void teardown() override { fileops::close(epFd_); }\n\n  void addEvent(Event* event) override {\n    if (event->isNotificationFd() && event->registered) {\n      return; // notificationEv_ is persistent.\n    }\n\n    epoll_event ev = {};\n    ev.data.ptr = event;\n    ev.events = EPOLLIN;\n\n    int op = EPOLL_CTL_ADD;\n    if (!event->isNotificationFd()) {\n      ev.events |= EPOLLONESHOT;\n      if (FOLLY_UNLIKELY(!event->registered)) {\n        event->registered = true;\n      } else {\n        op = EPOLL_CTL_MOD;\n      }\n    } else {\n      // Use edge triggering, so we don't need to drain the eventfd when ready.\n      ev.events |= EPOLLET;\n      event->registered = true;\n    }\n\n    auto ret = ::epoll_ctl(epFd_, op, event->fd, &ev);\n    PCHECK(ret == 0);\n  }\n\n  void delEvent(Event* event) override {\n    CHECK(!event->isNotificationFd());\n    if (!event->registered) {\n      return;\n    }\n\n    auto ret = ::epoll_ctl(epFd_, EPOLL_CTL_DEL, event->fd, nullptr);\n    PCHECK(ret == 0);\n  }\n\n  bool waitForEvents(std::chrono::steady_clock::time_point loopStart) override {\n    int ret;\n\n    auto spinUntil = loopStart +\n        std::chrono::microseconds{FLAGS_folly_event_base_poller_spin_us};\n    do {\n      ret = ::epoll_wait(epFd_, epollEvents_.data(), kMaxEvents, 0);\n    } while (ret <= 0 && std::chrono::steady_clock::now() < spinUntil);\n\n    if (ret <= 0) {\n      if (auto sleepUs = FLAGS_folly_event_base_poller_sleep_us) {\n        /* sleep override */\n        std::this_thread::sleep_for(std::chrono::microseconds{sleepUs});\n      }\n      ret = ::epoll_wait(epFd_, epollEvents_.data(), kMaxEvents, -1);\n    }\n\n    if (ret <= 0) {\n      return false;\n    }\n\n    for (int i = 0; i < ret; ++i) {\n      readyEvents_.push_back(\n          CHECK_NOTNULL(reinterpret_cast<Event*>(epollEvents_[i].data.ptr)));\n    }\n    return true;\n  }\n\n private:\n  const size_t kMaxEvents = FLAGS_folly_event_base_poller_epoll_max_events;\n  int epFd_{-1};\n  std::vector<struct epoll_event> epollEvents_{kMaxEvents};\n};\n\n#if FOLLY_HAS_LIBURING\n\nnamespace {\n\nvoid enableFlagsIfSupported(\n    struct io_uring_params& params, uint32_t desiredFlags, const char* msg) {\n  struct io_uring_params tmpParams;\n  ::memset(&params, 0, sizeof(tmpParams));\n  tmpParams.flags = desiredFlags;\n  int fd = ::io_uring_setup(1, &tmpParams);\n  if (fd >= 0) {\n    fileops::close(fd);\n    VLOG(1) << \"io_uring flags \" << msg << \" supported\";\n    params.flags |= desiredFlags;\n  } else if (fd == -EINVAL) {\n    VLOG(1) << \"io_uring flags \" << msg << \" NOT supported\";\n  } else {\n    LOG(ERROR) << \"Unexpected error \" << folly::errnoStr(-fd)\n               << \" while probing supported io_uring flags\";\n  }\n}\n\n#define ENABLE_FLAGS_IF_SUPPORTED(params, desiredFlags) \\\n  enableFlagsIfSupported(params, desiredFlags, #desiredFlags)\n\n} // namespace\n\nclass EventBasePollerIoUring final : public EventBasePollerImpl {\n public:\n  EventBasePollerIoUring()\n      // io_uring does not support concurrent submissions.\n      : EventBasePollerImpl(/* rearmInline */ false) {\n    startLoop();\n  }\n\n  ~EventBasePollerIoUring() override { stopLoop(); }\n\n  void setup() override {\n    ::memset(&ring_, 0, sizeof(ring_));\n    struct io_uring_params params;\n    ::memset(&params, 0, sizeof(params));\n\n    ENABLE_FLAGS_IF_SUPPORTED(\n        params, IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN);\n    ENABLE_FLAGS_IF_SUPPORTED(\n        params, IORING_SETUP_COOP_TASKRUN | IORING_SETUP_TASKRUN_FLAG);\n    int ret = ::io_uring_queue_init_params(\n        FLAGS_folly_event_base_poller_io_uring_sq_entries, &ring_, &params);\n    CHECK(ret == 0) << \"Error creating io_uring: \" << folly::errnoStr(-ret);\n  }\n\n  void teardown() override { ::io_uring_queue_exit(&ring_); }\n\n  void addEvent(Event* event) override {\n    auto* sqe = ::io_uring_get_sqe(&ring_);\n    if (sqe == nullptr) {\n      submitPendingSqes();\n      sqe = ::io_uring_get_sqe(&ring_);\n      // Single issuer, so if we can't get a sqe after a submit we have a\n      // problem.\n      CHECK(sqe != nullptr);\n    }\n    ++numPendingSqes_;\n\n    ::io_uring_sqe_set_data(sqe, event);\n    if (event->isNotificationFd()) {\n      ::io_uring_prep_read(\n          sqe, event->fd, &eventFdBuf_, sizeof(eventFdBuf_), 0);\n    } else {\n      ::io_uring_prep_poll_add(sqe, event->fd, POLLIN);\n    }\n  }\n\n  void delEvent(Event* /* event */) override {\n    // Nothing to do, no events are persistent.\n  }\n\n  bool waitForEvents(std::chrono::steady_clock::time_point loopStart) override {\n    if (numPendingSqes_ > 0) {\n      submitPendingSqes();\n    }\n\n    int ret;\n    struct io_uring_cqe* cqe = nullptr;\n\n    auto spinUntil = loopStart +\n        std::chrono::microseconds{FLAGS_folly_event_base_poller_spin_us};\n    do {\n      ret = ::io_uring_peek_cqe(&ring_, &cqe);\n    } while (ret != 0 && std::chrono::steady_clock::now() < spinUntil);\n\n    if (auto sleepUs = FLAGS_folly_event_base_poller_sleep_us;\n        ret != 0 && sleepUs > 0) {\n      // Simulate a sleep + peek by waiting for infinite events with a timeout.\n      struct __kernel_timespec timeout;\n      timeout.tv_sec = sleepUs / 1'000'000;\n      timeout.tv_nsec = (sleepUs % 1'000'000) * 1'000;\n      ret = ::io_uring_wait_cqes(\n          &ring_,\n          &cqe,\n          std::numeric_limits<unsigned>::max(),\n          &timeout,\n          nullptr);\n    }\n\n    if (ret != 0 || cqe == nullptr) {\n      // No luck, do an unbounded wait.\n      ret = ::io_uring_wait_cqe(&ring_, &cqe);\n    }\n\n    if (ret != 0 || cqe == nullptr) {\n      return false;\n    }\n\n    DCHECK_EQ(readyEvents_.size(), 0);\n    unsigned head;\n    io_uring_for_each_cqe(&ring_, head, cqe) {\n      auto* event =\n          CHECK_NOTNULL(static_cast<Event*>(io_uring_cqe_get_data(cqe)));\n      if (event->isNotificationFd()) {\n        CHECK_EQ(cqe->res, sizeof(eventFdBuf_)) << errnoStr(-cqe->res);\n      } else {\n        CHECK_GE(cqe->res, 0) << errnoStr(-cqe->res);\n      }\n      readyEvents_.push_back(event);\n    }\n    ::io_uring_cq_advance(&ring_, readyEvents_.size());\n\n    return true;\n  }\n\n private:\n  void submitPendingSqes() {\n    auto ret = ::io_uring_submit(&ring_);\n    CHECK_EQ(ret, numPendingSqes_);\n    numPendingSqes_ = 0;\n  }\n\n  struct io_uring ring_;\n  size_t numPendingSqes_ = 0;\n  alignas(cacheline_align_v) uint64_t eventFdBuf_;\n};\n\n#endif // FOLLY_HAS_LIBURING\n\n#endif // FOLLY_HAS_EPOLL\n\n} // namespace\n\nvoid EventBasePoller::Stats::update(\n    int numEvents, Duration wait, Duration busy) {\n  minNumEvents = std::min(minNumEvents, numEvents);\n  maxNumEvents = std::max(maxNumEvents, numEvents);\n  totalNumEvents += static_cast<size_t>(numEvents);\n  totalWakeups += 1;\n\n  totalWait += wait;\n  minWait = std::min(minWait, wait);\n  maxWait = std::max(maxWait, wait);\n\n  totalBusy += busy;\n  minBusy = std::min(minBusy, busy);\n  maxBusy = std::max(maxBusy, busy);\n}\n\nEventBasePoller::Handle::~Handle() = default;\n\nEventBasePoller::FdGroup::~FdGroup() = default;\n\nEventBasePoller::~EventBasePoller() = default;\n\n/* static */ EventBasePoller& EventBasePoller::get() {\n  static auto instance = []() -> std::unique_ptr<EventBasePoller> {\n#if FOLLY_HAS_EPOLL\n    if (FLAGS_folly_event_base_poller_backend == \"epoll\") {\n      return std::make_unique<EventBasePollerEpoll>();\n    }\n#endif\n#if FOLLY_HAS_EPOLL && FOLLY_HAS_LIBURING\n    if (FLAGS_folly_event_base_poller_backend == \"io_uring\") {\n      return std::make_unique<EventBasePollerIoUring>();\n    }\n#endif\n    throw std::invalid_argument(\n        fmt::format(\n            \"Unsupported EventBasePoller backend: {}\",\n            FLAGS_folly_event_base_poller_backend));\n  }();\n  return *instance;\n}\n\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/io/async/EventBasePoller.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <memory>\n\n#include <folly/Function.h>\n#include <folly/Range.h>\n#include <folly/Synchronized.h>\n\nnamespace folly::detail {\n\n/**\n * EventBasePoller centralizes the blocking wait for events across multiple\n * EventBases in a process. The singleton calls the provided ReadyCallback on\n * ready EventBases, so they can be driven without blocking. This enables\n * control over which threads drive the EventBases, as opposed to the standard\n * blocking loop that requires one thread per EventBase.\n *\n * EventBases' pollable fds are registered in groups, so that the callback can\n * batch processing of ready EventBases that belong to the same group.\n *\n * When the EventBase is ready it can be driven until it would block again, and\n * then handoff() must be called to resume polling the fd. Neither the driving\n * of the EventBase or the call to handoff() should happen inline in the\n * callback, but delegated to another thread without blocking; the callback must\n * return control quickly, as it executes in the main polling loop and can slow\n * down the handling of all other registered EventBases.\n *\n * Note that none of the implementation is specific to EventBases, in fact this\n * is a lightweight implementation of an event loop specialized on polling read\n * events, and which supports grouping of the fds for batch-handling. The class\n * could be easily generalized if other applications arise.\n */\nclass EventBasePoller {\n public:\n  struct Stats {\n    using Duration = std::chrono::steady_clock::duration;\n\n    // Track number of loop wake-ups and number of events returned.\n    int minNumEvents{std::numeric_limits<int>::max()};\n    int maxNumEvents{std::numeric_limits<int>::min()};\n    size_t totalNumEvents{0};\n    size_t totalWakeups{0};\n\n    Duration totalWait{0};\n    Duration minWait{Duration::max()};\n    Duration maxWait{Duration::min()};\n\n    Duration totalBusy{0};\n    Duration minBusy{Duration::max()};\n    Duration maxBusy{Duration::min()};\n\n    void update(int numEvents, Duration wait, Duration busy);\n  };\n\n  class Handle {\n   public:\n    virtual ~Handle();\n\n    template <class T>\n    T* getUserData() const {\n      return reinterpret_cast<T*>(userData_);\n    }\n\n    // If done is set to true, the handle is not re-armed and can be reclaimed\n    // with reclaim().\n    virtual void handoff(bool done) = 0;\n\n   protected:\n    friend class EventBasePoller;\n\n    explicit Handle(void* userData) : userData_(userData) {}\n\n    void* userData_;\n  };\n\n  // FdGroup method invocations must be serialized.\n  class FdGroup {\n   public:\n    virtual ~FdGroup();\n\n    // All added handles must be reclaimed before the group is destroyed.\n    virtual std::unique_ptr<Handle> add(int fd, void* userData) = 0;\n    // Blocks until handoff(true) is called on the handle.\n    virtual void reclaim(std::unique_ptr<Handle> handle) = 0;\n  };\n\n  using ReadyCallback =\n      Function<void(Range<Handle**> readyHandles) const noexcept>;\n\n  static EventBasePoller& get();\n\n  virtual ~EventBasePoller();\n\n  virtual std::unique_ptr<FdGroup> makeFdGroup(ReadyCallback readyCallback) = 0;\n\n  Stats getStats() { return stats_.copy(); }\n\n protected:\n  folly::Synchronized<Stats> stats_;\n};\n\n} // namespace folly::detail\n"
  },
  {
    "path": "folly/io/async/EventBaseThread.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventBaseThread.h>\n\n#include <folly/io/async/ScopedEventBaseThread.h>\n\nnamespace folly {\n\nEventBaseThread::EventBaseThread() : EventBaseThread(true) {}\n\nEventBaseThread::EventBaseThread(\n    bool autostart, EventBaseManager* ebm, folly::StringPiece threadName)\n    : EventBaseThread(autostart, EventBase::Options(), ebm, threadName) {}\n\nEventBaseThread::EventBaseThread(\n    bool autostart,\n    EventBase::Options eventBaseOptions,\n    EventBaseManager* ebm,\n    folly::StringPiece threadName)\n    : ebm_(ebm), ebOpts_(std::move(eventBaseOptions)) {\n  if (autostart) {\n    start(threadName);\n  }\n}\n\nEventBaseThread::EventBaseThread(EventBaseManager* ebm)\n    : EventBaseThread(true, ebm) {}\n\nEventBaseThread::~EventBaseThread() = default;\n\nEventBaseThread::EventBaseThread(EventBaseThread&&) noexcept = default;\nEventBaseThread& EventBaseThread::operator=(EventBaseThread&&) noexcept =\n    default;\n\nEventBase* EventBaseThread::getEventBase() const {\n  return th_ ? th_->getEventBase() : nullptr;\n}\n\nbool EventBaseThread::running() const {\n  return !!th_;\n}\n\nvoid EventBaseThread::start(folly::StringPiece threadName) {\n  if (th_) {\n    return;\n  }\n  th_ = std::make_unique<ScopedEventBaseThread>(ebOpts_, ebm_, threadName);\n}\n\nvoid EventBaseThread::stop() {\n  th_ = nullptr;\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventBaseThread.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/Range.h>\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\n\nclass EventBase;\nclass EventBaseBackendBase;\nclass EventBaseManager;\nclass ScopedEventBaseThread;\n\nclass EventBaseThread {\n public:\n  EventBaseThread();\n  explicit EventBaseThread(\n      bool autostart,\n      EventBaseManager* ebm = nullptr,\n      folly::StringPiece threadName = folly::StringPiece());\n  EventBaseThread(\n      bool autostart,\n      EventBase::Options eventBaseOptions,\n      EventBaseManager* ebm = nullptr,\n      folly::StringPiece threadName = folly::StringPiece());\n  explicit EventBaseThread(EventBaseManager* ebm);\n  ~EventBaseThread();\n\n  EventBaseThread(EventBaseThread const&) = delete;\n  EventBaseThread& operator=(EventBaseThread const&) = delete;\n  EventBaseThread(EventBaseThread&&) noexcept;\n  EventBaseThread& operator=(EventBaseThread&&) noexcept;\n\n  EventBase* getEventBase() const;\n\n  bool running() const;\n  void start(folly::StringPiece threadName = folly::StringPiece());\n  void stop();\n\n private:\n  EventBaseManager* ebm_;\n  EventBase::Options ebOpts_;\n  std::unique_ptr<ScopedEventBaseThread> th_;\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventHandler.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventHandler.h>\n\n#include <cassert>\n\n#include <folly/String.h>\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\n\nEventHandler::EventHandler(EventBase* eventBase, NetworkSocket fd) {\n  event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this);\n  if (eventBase != nullptr) {\n    setEventBase(eventBase);\n  } else {\n    // Callers must set the EventBase and fd before using this timeout.\n    // Set event_->ev_base to nullptr to ensure that this happens.\n    // (otherwise libevent will initialize it to the \"default\" event_base)\n    event_.eb_ev_base(nullptr);\n    eventBase_ = nullptr;\n  }\n}\n\nEventHandler::~EventHandler() {\n  unregisterHandler();\n}\n\nbool EventHandler::registerImpl(uint16_t events, bool internal) {\n  assert(event_.eb_ev_base() != nullptr);\n\n  // We have to unregister the event before we can change the event flags\n  if (isHandlerRegistered()) {\n    // If the new events are the same are the same as the already registered\n    // flags, we don't have to do anything.  Just return.\n    auto flags = folly::event_ref_flags(event_.getEvent());\n    if (events == event_.eb_ev_events() &&\n        static_cast<bool>(flags & EVLIST_INTERNAL) == internal) {\n      return true;\n    }\n\n    event_.eb_event_del();\n  }\n\n  // Update the event flags\n  // Unfortunately, event_set() resets the event_base, so we have to remember\n  // it before hand, then pass it back into event_base_set() afterwards\n  auto* evb = event_.eb_ev_base();\n  event_.eb_event_set(\n      event_.eb_ev_fd(), short(events), &EventHandler::libeventCallback, this);\n  event_.eb_event_base_set(evb);\n\n  // Set EVLIST_INTERNAL if this is an internal event\n  if (internal) {\n    folly::event_ref_flags(event_.getEvent()) |= EVLIST_INTERNAL;\n  }\n\n  // Add the event.\n  //\n  // Although libevent allows events to wait on both I/O and a timeout,\n  // we intentionally don't allow an EventHandler to also use a timeout.\n  // Callers must maintain a separate AsyncTimeout object if they want a\n  // timeout.\n  //\n  // Otherwise, it is difficult to handle persistent events properly.  (The I/O\n  // event and timeout may both fire together the same time around the event\n  // loop.  Normally we would want to inform the caller of the I/O event first,\n  // then the timeout.  However, it is difficult to do this properly since the\n  // I/O callback could delete the EventHandler.)  Additionally, if a caller\n  // uses the same struct event for both I/O and timeout, and they just want to\n  // reschedule the timeout, libevent currently makes an epoll_ctl() call even\n  // if the I/O event flags haven't changed.  Using a separate event struct is\n  // therefore slightly more efficient in this case (although it does take up\n  // more space).\n  if (event_.eb_event_add(nullptr) < 0) {\n    LOG(ERROR) << \"EventBase: failed to register event handler for fd \"\n               << event_.eb_ev_fd() << \": \" << errnoStr(errno);\n    // Call event_del() to make sure the event is completely uninstalled\n    event_.eb_event_del();\n    return false;\n  }\n\n  return true;\n}\n\nvoid EventHandler::unregisterHandler() {\n  if (isHandlerRegistered()) {\n    event_.eb_event_del();\n  }\n}\n\nvoid EventHandler::attachEventBase(EventBase* eventBase) {\n  // attachEventBase() may only be called on detached handlers\n  assert(event_.eb_ev_base() == nullptr);\n  assert(!isHandlerRegistered());\n  // This must be invoked from the EventBase's thread\n  eventBase->dcheckIsInEventBaseThread();\n\n  setEventBase(eventBase);\n}\n\nvoid EventHandler::detachEventBase() {\n  ensureNotRegistered(__func__);\n  event_.eb_ev_base(nullptr);\n}\n\nvoid EventHandler::changeHandlerFD(NetworkSocket fd) {\n  ensureNotRegistered(__func__);\n  // event_set() resets event_base.ev_base, so manually restore it afterwards\n  auto* evb = event_.eb_ev_base();\n  event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this);\n  event_.eb_ev_base(\n      evb); // don't use event_base_set(), since evb may be nullptr\n}\n\nvoid EventHandler::initHandler(EventBase* eventBase, NetworkSocket fd) {\n  ensureNotRegistered(__func__);\n  event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this);\n  setEventBase(eventBase);\n}\n\nvoid EventHandler::ensureNotRegistered(const char* fn) {\n  // Neither the EventBase nor file descriptor may be changed while the\n  // handler is registered.  Treat it as a programmer bug and abort the program\n  // if this requirement is violated.\n  if (isHandlerRegistered()) {\n    LOG(ERROR) << fn << \" called on registered handler; aborting\";\n    abort();\n  }\n}\n\nvoid EventHandler::libeventCallback(libevent_fd_t fd, short events, void* arg) {\n  auto handler = reinterpret_cast<EventHandler*>(arg);\n  assert(fd == handler->event_.eb_ev_fd());\n  (void)fd; // prevent unused variable warnings\n\n  // this can't possibly fire if handler->eventBase_ is nullptr\n  handler->eventBase_->bumpHandlingTime();\n\n  RequestContextSaverScopeGuard rctxGuard;\n  ExecutionObserverScopeGuard guard(\n      &handler->eventBase_->getExecutionObserverList(),\n      &handler->eventBase_,\n      folly::ExecutionObserver::CallbackType::Event);\n\n  handler->handlerReady(uint16_t(events));\n}\n\nvoid EventHandler::setEventBase(EventBase* eventBase) {\n  event_.eb_event_base_set(eventBase);\n  eventBase_ = eventBase;\n}\n\nbool EventHandler::isPending() const {\n  if (folly::event_ref_flags(event_.getEvent()) & EVLIST_ACTIVE) {\n    if (event_.eb_ev_res() & EV_READ) {\n      return true;\n    }\n  }\n  return false;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventHandler.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n\n#include <glog/logging.h>\n\n#include <folly/io/async/EventBaseBackendBase.h>\n#include <folly/io/async/EventUtil.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/portability/Event.h>\n\nnamespace folly {\n\nclass EventBase;\n\n/**\n * The EventHandler class is used to asynchronously wait for events on a file\n * descriptor.\n *\n * Users that wish to wait on I/O events should derive from EventHandler and\n * implement the handlerReady() method.\n */\nclass EventHandler {\n public:\n  enum EventFlags {\n    NONE = 0,\n    READ = EV_READ,\n    WRITE = EV_WRITE,\n    READ_WRITE = (READ | WRITE),\n    PERSIST = EV_PERSIST,\n// Temporary flag until EPOLLPRI is upstream on libevent.\n#ifdef EV_PRI\n    PRI = EV_PRI,\n#endif\n  };\n\n  /**\n   * Create a new EventHandler object.\n   *\n   * @param eventBase  The EventBase to use to drive this event handler.\n   *                   This may be nullptr, in which case the EventBase must be\n   *                   set separately using initHandler() or attachEventBase()\n   *                   before the handler can be registered.\n   * @param fd         The file descriptor that this EventHandler will\n   *                   monitor.  This may be -1, in which case the file\n   *                   descriptor must be set separately using initHandler() or\n   *                   changeHandlerFD() before the handler can be registered.\n   */\n  explicit EventHandler(\n      EventBase* eventBase = nullptr, NetworkSocket fd = NetworkSocket());\n\n  EventHandler(const EventHandler&) = delete;\n  EventHandler& operator=(const EventHandler&) = delete;\n\n  /**\n   * EventHandler destructor.\n   *\n   * The event will be automatically unregistered if it is still registered.\n   */\n  virtual ~EventHandler();\n\n  /**\n   * handlerReady() is invoked when the handler is ready.\n   *\n   * @param events  A bitset indicating the events that are ready.\n   */\n  virtual void handlerReady(uint16_t events) noexcept = 0;\n\n  /**\n   * Register the handler.\n   *\n   * If the handler is already registered, the registration will be updated\n   * to wait on the new set of events.\n   *\n   * @param events   A bitset specifying the events to monitor.\n   *                 If the PERSIST bit is set, the handler will remain\n   *                 registered even after handlerReady() is called.\n   *\n   * @return Returns true if the handler was successfully registered,\n   *         or false if an error occurred.  After an error, the handler is\n   *         always unregistered, even if it was already registered prior to\n   *         this call to registerHandler().\n   */\n  bool registerHandler(uint16_t events) { return registerImpl(events, false); }\n\n  /**\n   * Unregister the handler, if it is registered.\n   */\n  void unregisterHandler();\n\n  /**\n   * Returns true if the handler is currently registered.\n   */\n  bool isHandlerRegistered() const { return event_.isEventRegistered(); }\n\n  /**\n   * Attach the handler to a EventBase.\n   *\n   * This may only be called if the handler is not currently attached to a\n   * EventBase (either by using the default constructor, or by calling\n   * detachEventBase()).\n   *\n   * This method must be invoked in the EventBase's thread.\n   */\n  void attachEventBase(EventBase* eventBase);\n\n  /**\n   * Detach the handler from its EventBase.\n   *\n   * This may only be called when the handler is not currently registered.\n   * Once detached, the handler may not be registered again until it is\n   * re-attached to a EventBase by calling attachEventBase().\n   *\n   * This method must be called from the current EventBase's thread.\n   */\n  void detachEventBase();\n\n  /**\n   * Change the file descriptor that this handler is associated with.\n   *\n   * This may only be called when the handler is not currently registered.\n   */\n  void changeHandlerFD(NetworkSocket fd);\n\n  /**\n   * Attach the handler to a EventBase, and change the file descriptor.\n   *\n   * This method may only be called if the handler is not currently attached to\n   * a EventBase.  This is primarily intended to be used to initialize\n   * EventHandler objects created using the default constructor.\n   */\n  void initHandler(EventBase* eventBase, NetworkSocket fd);\n\n  /**\n   * Return the set of events that we're currently registered for.\n   */\n  uint16_t getRegisteredEvents() const {\n    return (isHandlerRegistered()) ? (uint16_t)(event_.eb_ev_events()) : 0u;\n  }\n\n  /**\n   * Register the handler as an internal event.\n   *\n   * This event will not count as an active event for determining if the\n   * EventBase loop has more events to process.  The EventBase loop runs\n   * only as long as there are active EventHandlers, however \"internal\" event\n   * handlers are not counted.  Therefore this event handler will not prevent\n   * EventBase loop from exiting with no more work to do if there are no other\n   * non-internal event handlers registered.\n   *\n   * This is intended to be used only in very rare cases by the internal\n   * EventBase code.  This API is not guaranteed to remain stable or portable\n   * in the future.\n   */\n  bool registerInternalHandler(uint16_t events) {\n    return registerImpl(events, true);\n  }\n\n  bool isPending() const;\n\n  /*\n   * If supported by the backend updates the event to be edge-triggered.\n   * Returns true iff the update was successful.\n   *\n   * This should only be used for already registered events (e.g. after\n   * registerHandler/registerInternalHandler calls). Calling any other method\n   * on the EventHandler may reset this flag.\n   * This can be useful to avoid read calls with eventfds.\n   */\n  bool setEdgeTriggered() { return event_.setEdgeTriggered(); }\n\n  /*\n   * Make an event active.\n   */\n  bool activateEvent(int res) { return event_.eb_event_active(res); }\n\n private:\n  bool registerImpl(uint16_t events, bool internal);\n  void ensureNotRegistered(const char* fn);\n\n  void setEventBase(EventBase* eventBase);\n\n  static void libeventCallback(libevent_fd_t fd, short events, void* arg);\n\n  EventBaseBackendBase::Event event_;\n  EventBase* eventBase_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/EventUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n\n#include <folly/portability/Event.h>\n\nnamespace folly {\n\n#if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER <= 0x02010101\n#define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_##name\n#define FOLLY_LIBEVENT_COMPAT_PLUCK2(name) ev_##name\n#else\n#define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_evcallback.evcb_##name\n#define FOLLY_LIBEVENT_COMPAT_PLUCK2(name) \\\n  ev_evcallback.evcb_cb_union.evcb_##name\n#endif\n#define FOLLY_LIBEVENT_DEF_ACCESSORS(name)                            \\\n  inline auto event_ref_##name(struct event* ev)                      \\\n      -> decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) {  \\\n    return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name));           \\\n  }                                                                   \\\n  inline auto event_ref_##name(struct event const* ev)                \\\n      -> decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name))) { \\\n    return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK(name));          \\\n  }                                                                   \\\n  //\n\n#define FOLLY_LIBEVENT_DEF_ACCESSORS2(name)                            \\\n  inline auto event_ref_##name(struct event* ev)                       \\\n      -> decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) {  \\\n    return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name));           \\\n  }                                                                    \\\n  inline auto event_ref_##name(struct event const* ev)                 \\\n      -> decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) { \\\n    return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name));          \\\n  }                                                                    \\\n  //\n\nFOLLY_LIBEVENT_DEF_ACCESSORS(arg)\nFOLLY_LIBEVENT_DEF_ACCESSORS(flags)\n// evcb_callback is inside a union{...} evcb_cb_union\nFOLLY_LIBEVENT_DEF_ACCESSORS2(callback)\n\n#undef FOLLY_LIBEVENT_COMPAT_PLUCK\n#undef FOLLY_LIBEVENT_DEF_ACCESSORS\n\n/**\n * low-level libevent utility functions\n */\nclass EventUtil {\n public:\n  static bool isEventRegistered(const struct event* ev) {\n    // If any of these flags are set, the event is registered.\n    enum {\n      EVLIST_REGISTERED =\n          (EVLIST_INSERTED | EVLIST_ACTIVE | EVLIST_TIMEOUT | EVLIST_SIGNAL)\n    };\n    return (event_ref_flags(ev) & EVLIST_REGISTERED);\n  }\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/HHWheelTimer-fwd.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <chrono>\n\nnamespace folly {\ntemplate <class Duration>\nclass HHWheelTimerBase;\nusing HHWheelTimer = HHWheelTimerBase<std::chrono::milliseconds>;\nusing HHWheelTimerHighRes = HHWheelTimerBase<std::chrono::microseconds>;\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/HHWheelTimer.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/HHWheelTimer.h>\n\n#include <cassert>\n\n#include <folly/ScopeGuard.h>\n#include <folly/container/BitIterator.h>\n#include <folly/io/async/Request.h>\n#include <folly/lang/Bits.h>\n\nnamespace folly {\n/**\n * We want to select the default interval carefully.\n * An interval of 10ms will give us 10ms * WHEEL_SIZE^WHEEL_BUCKETS\n * for the largest timeout possible, or about 497 days.\n *\n * For a lower bound, we want a reasonable limit on local IO, 10ms\n * seems short enough\n *\n * A shorter interval also has CPU implications, less than 1ms might\n * start showing up in cpu perf.  Also, it might not be possible to set\n * tick interval less than 10ms on older kernels.\n */\n\n/*\n * For high res timers:\n * An interval of 200usec will give us 200usec * WHEEL_SIZE^WHEEL_BUCKETS\n * for the largest timeout possible, or about 9 days.\n */\n\ntemplate <class Duration>\nint HHWheelTimerBase<Duration>::DEFAULT_TICK_INTERVAL =\n    detail::HHWheelTimerDurationConst<Duration>::DEFAULT_TICK_INTERVAL;\n\ntemplate <class Duration>\nHHWheelTimerBase<Duration>::Callback::Callback() = default;\n\ntemplate <class Duration>\nHHWheelTimerBase<Duration>::Callback::~Callback() {\n  if (isScheduled()) {\n    cancelTimeout();\n  }\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::Callback::setScheduled(\n    HHWheelTimerBase* wheel, std::chrono::steady_clock::time_point deadline) {\n  assert(wheel_ == nullptr);\n  assert(expiration_ == decltype(expiration_){});\n\n  wheel_ = wheel;\n  expiration_ = deadline;\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::Callback::cancelTimeoutImpl() {\n  if (--wheel_->count_ <= 0) {\n    assert(wheel_->count_ == 0);\n    wheel_->AsyncTimeout::cancelTimeout();\n  }\n  unlink();\n  if ((-1 != bucket_) && (wheel_->buckets_[0][bucket_].empty())) {\n    auto bi = makeBitIterator(wheel_->bitmap_.begin());\n    *(bi + bucket_) = false;\n  }\n\n  wheel_ = nullptr;\n  expiration_ = {};\n}\n\ntemplate <class Duration>\nHHWheelTimerBase<Duration>::HHWheelTimerBase(\n    folly::TimeoutManager* timeoutMananger,\n    Duration intervalDuration,\n    AsyncTimeout::InternalEnum internal,\n    Duration defaultTimeoutDuration)\n    : AsyncTimeout(timeoutMananger, internal),\n      interval_(intervalDuration),\n      defaultTimeout_(defaultTimeoutDuration),\n      expireTick_(1),\n      count_(0),\n      startTime_(getCurTime()),\n      processingCallbacksGuard_(nullptr) {\n  bitmap_.fill(0);\n}\n\ntemplate <class Duration>\nHHWheelTimerBase<Duration>::~HHWheelTimerBase() {\n  // Ensure this gets done, but right before destruction finishes.\n  auto destructionPublisherGuard = folly::makeGuard([&] {\n    // Inform the subscriber that this instance is doomed.\n    if (processingCallbacksGuard_) {\n      *processingCallbacksGuard_ = true;\n    }\n  });\n  cancelAll();\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::scheduleTimeoutImpl(\n    Callback* callback,\n    int64_t dueTick,\n    int64_t nextTickToProcess,\n    int64_t nextTick) {\n  int64_t diff = dueTick - nextTickToProcess;\n  CallbackList* list;\n\n  auto bi = makeBitIterator(bitmap_.begin());\n\n  if (diff < 0) {\n    list = &buckets_[0][nextTick & WHEEL_MASK];\n    *(bi + (nextTick & WHEEL_MASK)) = true;\n    callback->bucket_ = nextTick & WHEEL_MASK;\n  } else if (diff < WHEEL_SIZE) {\n    list = &buckets_[0][dueTick & WHEEL_MASK];\n    *(bi + (dueTick & WHEEL_MASK)) = true;\n    callback->bucket_ = dueTick & WHEEL_MASK;\n  } else if (diff < 1 << (2 * WHEEL_BITS)) {\n    list = &buckets_[1][(dueTick >> WHEEL_BITS) & WHEEL_MASK];\n  } else if (diff < 1 << (3 * WHEEL_BITS)) {\n    list = &buckets_[2][(dueTick >> 2 * WHEEL_BITS) & WHEEL_MASK];\n  } else {\n    /* in largest slot */\n    if (diff > LARGEST_SLOT) {\n      diff = LARGEST_SLOT;\n      dueTick = diff + nextTickToProcess;\n    }\n    list = &buckets_[3][(dueTick >> 3 * WHEEL_BITS) & WHEEL_MASK];\n  }\n  list->push_back(*callback);\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::scheduleTimeout(\n    Callback* callback, Duration timeout) {\n  // Make sure that the timeout is not negative.\n  timeout = std::max(timeout, Duration::zero());\n  // Cancel the callback if it happens to be scheduled already.\n  callback->cancelTimeout();\n  callback->requestContext_ = RequestContext::saveContext();\n\n  count_++;\n\n  auto now = getCurTime();\n  auto nextTick = calcNextTick(now);\n  callback->setScheduled(this, now + timeout);\n\n  // There are three possible scenarios:\n  //   - we are currently inside of HHWheelTimerBase<Duration>::timeoutExpired.\n  //   In this case,\n  //     we need to use its last tick as a base for computations\n  //   - HHWheelTimerBase tick timeout is already scheduled. In this case,\n  //     we need to use its scheduled tick as a base.\n  //   - none of the above are true. In this case, it's safe to use the nextTick\n  //     as a base.\n  int64_t baseTick = nextTick;\n  if (processingCallbacksGuard_ || isScheduled()) {\n    baseTick = std::min(expireTick_, nextTick);\n  }\n  int64_t ticks = timeToWheelTicks(timeout);\n  int64_t due = ticks + nextTick;\n  scheduleTimeoutImpl(callback, due, baseTick, nextTick);\n\n  /* If we're calling callbacks, timer will be reset after all\n   * callbacks are called.\n   */\n  if (!processingCallbacksGuard_) {\n    // Check if we need to reschedule the timer.\n    // If the wheel timeout is already scheduled, then we need to reschedule\n    // only if our due is earlier than the current scheduled one.\n    // If it's not scheduled, we need to schedule it either for the first tick\n    // of next wheel epoch or our due tick, whichever is earlier.\n    if (!isScheduled() && !inSameEpoch(nextTick - 1, due)) {\n      scheduleNextTimeout(nextTick, WHEEL_SIZE - ((nextTick - 1) & WHEEL_MASK));\n    } else if (!isScheduled() || due < expireTick_) {\n      scheduleNextTimeout(nextTick, ticks + 1);\n    }\n  }\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::scheduleTimeout(Callback* callback) {\n  CHECK(Duration(-1) != defaultTimeout_)\n      << \"Default timeout was not initialized\";\n  scheduleTimeout(callback, defaultTimeout_);\n}\n\ntemplate <class Duration>\nbool HHWheelTimerBase<Duration>::cascadeTimers(\n    int bucket, int tick, const std::chrono::steady_clock::time_point curTime) {\n  CallbackList cbs;\n  cbs.swap(buckets_[bucket][tick]);\n  auto nextTick = calcNextTick(curTime);\n  while (!cbs.empty()) {\n    auto* cb = &cbs.front();\n    cbs.pop_front();\n    scheduleTimeoutImpl(\n        cb,\n        nextTick + timeToWheelTicks(cb->getTimeRemaining(curTime)),\n        expireTick_,\n        nextTick);\n  }\n\n  // If tick is zero, timeoutExpired will cascade the next bucket.\n  return tick == 0;\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::scheduleTimeoutInternal(Duration timeout) {\n  this->AsyncTimeout::scheduleTimeout(timeout, {});\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::timeoutExpired() noexcept {\n  auto curTime = getCurTime();\n  auto nextTick = calcNextTick(curTime);\n\n  // If the last smart pointer for \"this\" is reset inside the callback's\n  // timeoutExpired(), then the guard will detect that it is time to bail from\n  // this method.\n  auto isDestroyed = false;\n  // If scheduleTimeout is called from a callback in this function, it may\n  // cause inconsistencies in the state of this object. As such, we need\n  // to treat these calls slightly differently.\n  CHECK(!processingCallbacksGuard_);\n  FOLLY_PUSH_WARNING\n#if __GNUC__ >= 12\n  FOLLY_GCC_DISABLE_WARNING(\"-Wdangling-pointer\")\n#endif\n  processingCallbacksGuard_ = &isDestroyed;\n  FOLLY_POP_WARNING\n  auto reEntryGuard = folly::makeGuard([&] {\n    if (!isDestroyed) {\n      processingCallbacksGuard_ = nullptr;\n    }\n  });\n\n  // timeoutExpired() can only be invoked directly from the event base loop.\n  // It should never be invoked recursively.\n  //\n  while (expireTick_ < nextTick) {\n    int idx = expireTick_ & WHEEL_MASK;\n\n    if (idx == 0) {\n      // Cascade timers\n      if (cascadeTimers(1, (expireTick_ >> WHEEL_BITS) & WHEEL_MASK, curTime) &&\n          cascadeTimers(\n              2, (expireTick_ >> (2 * WHEEL_BITS)) & WHEEL_MASK, curTime)) {\n        cascadeTimers(\n            3, (expireTick_ >> (3 * WHEEL_BITS)) & WHEEL_MASK, curTime);\n      }\n    }\n\n    auto bi = makeBitIterator(bitmap_.begin());\n    *(bi + idx) = false;\n\n    expireTick_++;\n    CallbackList* cbs = &buckets_[0][idx];\n    while (!cbs->empty()) {\n      auto* cb = &cbs->front();\n      cbs->pop_front();\n      timeoutsToRunNow_.push_back(*cb);\n    }\n  }\n\n  while (!timeoutsToRunNow_.empty()) {\n    auto* cb = &timeoutsToRunNow_.front();\n    timeoutsToRunNow_.pop_front();\n    count_--;\n    cb->wheel_ = nullptr;\n    cb->expiration_ = {};\n    RequestContextScopeGuard rctx(cb->requestContext_);\n    cb->timeoutExpired();\n    if (isDestroyed) {\n      // The HHWheelTimerBase itself has been destroyed. The other callbacks\n      // will have been cancelled from the destructor. Bail before causing\n      // damage.\n      return;\n    }\n  }\n\n  // We don't need to schedule a new timeout if there're nothing in the wheel.\n  if (count_ > 0) {\n    scheduleNextTimeout(expireTick_);\n  }\n}\n\ntemplate <class Duration>\nsize_t HHWheelTimerBase<Duration>::cancelAll() {\n  size_t count = 0;\n\n  if (count_ != 0) {\n    const std::size_t numElements = WHEEL_BUCKETS * WHEEL_SIZE;\n    auto maxBuckets = std::min(numElements, count_);\n    auto buckets = std::make_unique<CallbackList[]>(maxBuckets);\n    size_t countBuckets = 0;\n    for (auto& tick : buckets_) {\n      for (auto& bucket : tick) {\n        if (bucket.empty()) {\n          continue;\n        }\n        count += bucket.size();\n        std::swap(bucket, buckets[countBuckets++]);\n        if (count >= count_) {\n          break;\n        }\n      }\n    }\n\n    for (size_t i = 0; i < countBuckets; ++i) {\n      cancelTimeoutsFromList(buckets[i]);\n    }\n    // Swap the list to prevent potential recursion if cancelAll is called by\n    // one of the callbacks.\n    CallbackList timeoutsToRunNow;\n    timeoutsToRunNow.swap(timeoutsToRunNow_);\n    count += cancelTimeoutsFromList(timeoutsToRunNow);\n  }\n\n  return count;\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::scheduleNextTimeout(int64_t nextTick) {\n  int64_t tick = 1;\n\n  if (nextTick & WHEEL_MASK) {\n    auto bi = makeBitIterator(bitmap_.begin());\n    auto bi_end = makeBitIterator(bitmap_.end());\n    auto it = folly::findFirstSet(bi + (nextTick & WHEEL_MASK), bi_end);\n    if (it == bi_end) {\n      tick = WHEEL_SIZE - ((nextTick - 1) & WHEEL_MASK);\n    } else {\n      tick = std::distance(bi + (nextTick & WHEEL_MASK), it) + 1;\n    }\n  }\n\n  scheduleNextTimeout(nextTick, tick);\n}\n\ntemplate <class Duration>\nvoid HHWheelTimerBase<Duration>::scheduleNextTimeout(\n    int64_t nextTick, int64_t ticks) {\n  scheduleTimeoutInternal(interval_.fromWheelTicks(ticks));\n  expireTick_ = ticks + nextTick - 1;\n}\n\ntemplate <class Duration>\nsize_t HHWheelTimerBase<Duration>::cancelTimeoutsFromList(\n    CallbackList& timeouts) {\n  size_t count = 0;\n  while (!timeouts.empty()) {\n    ++count;\n    auto& cb = timeouts.front();\n    cb.cancelTimeout();\n    cb.callbackCanceled();\n  }\n  return count;\n}\n\ntemplate <class Duration>\nint64_t HHWheelTimerBase<Duration>::calcNextTick() {\n  return calcNextTick(getCurTime());\n}\n\ntemplate <class Duration>\nint64_t HHWheelTimerBase<Duration>::calcNextTick(\n    std::chrono::steady_clock::time_point curTime) {\n  return interval_.toWheelTicksFromSteadyClock(curTime - startTime_);\n}\n\n// std::chrono::microseconds\ntemplate <>\nvoid HHWheelTimerBase<std::chrono::microseconds>::scheduleTimeoutInternal(\n    std::chrono::microseconds timeout) {\n  this->AsyncTimeout::scheduleTimeoutHighRes(timeout, {});\n}\n\n// std::chrono::milliseconds\ntemplate class HHWheelTimerBase<std::chrono::milliseconds>;\n\n// std::chrono::microseconds\ntemplate class HHWheelTimerBase<std::chrono::microseconds>;\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/HHWheelTimer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n#include <chrono>\n#include <cstddef>\n#include <memory>\n\n#include <boost/intrusive/list.hpp>\n#include <glog/logging.h>\n\n#include <folly/ExceptionString.h>\n#include <folly/Optional.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/HHWheelTimer-fwd.h>\n#include <folly/portability/Config.h>\n\nnamespace folly {\n\nnamespace detail {\ntemplate <class Duration>\nstruct HHWheelTimerDurationConst;\n\ntemplate <class Duration>\nclass HHWheelTimerDurationInterval {\n public:\n  explicit HHWheelTimerDurationInterval(Duration interval)\n      : divInterval_(interval.count()),\n        divIntervalForSteadyClock_(\n            std::chrono::duration_cast<std::chrono::steady_clock::duration>(\n                interval)\n                .count()),\n        interval_(interval) {}\n\n  int64_t toWheelTicksFromSteadyClock(\n      std::chrono::steady_clock::duration t) const {\n    return divIntervalForSteadyClock_.divide(t.count());\n  }\n\n  int64_t toWheelTicks(Duration t) const {\n    return divInterval_.divide(t.count());\n  }\n\n  Duration fromWheelTicks(int64_t t) const { return t * interval_; }\n\n  Duration interval() const { return interval_; }\n\n  class Divider {\n   public:\n#if FOLLY_HAVE_INT128_T\n    // Use multiplication by the reciprocal in fixed 64-bit precision, which is\n    // faster than an integer division. The algorithm is always accurate when\n    // both numerator and denominator fit in 32 bits, see\n    // https://gmplib.org/~tege/divcnst-pldi94.pdf, Theorem 4.2 with N = 32 and\n    // l = 32. For larger numerator or denominator it is still frequently\n    // accurate, but it can overestimate the quotient by 1, which is acceptable\n    // here.\n    explicit Divider(uint64_t v) : value(~uint64_t(0) / v + 1) {}\n\n    uint64_t divide(uint64_t num) const {\n      if (value == 0) {\n        return num;\n      }\n      return static_cast<uint64_t>((__uint128_t(num) * value) >> 64);\n    }\n#else\n    explicit Divider(uint64_t v) : value(v) {}\n\n    uint64_t divide(uint64_t num) const { return num / value; }\n#endif\n   private:\n    uint64_t const value;\n  };\n\n private:\n  Divider const divInterval_;\n  Divider const divIntervalForSteadyClock_;\n  Duration const interval_;\n};\n\ntemplate <>\nstruct HHWheelTimerDurationConst<std::chrono::milliseconds> {\n  static constexpr int DEFAULT_TICK_INTERVAL = 10;\n};\n\ntemplate <>\nstruct HHWheelTimerDurationConst<std::chrono::microseconds> {\n  static constexpr int DEFAULT_TICK_INTERVAL = 200;\n};\n} // namespace detail\n\n/**\n * Hashed Hierarchical Wheel Timer\n *\n * We model timers as the number of ticks until the next\n * due event.  We allow 32-bits of space to track this\n * due interval, and break that into 4 regions of 8 bits.\n * Each region indexes into a bucket of 256 lists.\n *\n * Bucket 0 represents those events that are due the soonest.\n * Each tick causes us to look at the next list in a bucket.\n * The 0th list in a bucket is special; it means that it is time to\n * flush the timers from the next higher bucket and schedule them\n * into a different bucket.\n *\n * This technique results in a very cheap mechanism for\n * maintaining time and timers.\n *\n * Unlike the original timer wheel paper, this implementation does\n * *not* tick constantly, and instead calculates the exact next wakeup\n * time.\n */\ntemplate <class Duration>\nclass HHWheelTimerBase\n    : private folly::AsyncTimeout,\n      public folly::DelayedDestruction {\n public:\n  using UniquePtr = std::unique_ptr<HHWheelTimerBase, Destructor>;\n  using SharedPtr = std::shared_ptr<HHWheelTimerBase>;\n\n  template <typename... Args>\n  static UniquePtr newTimer(Args&&... args) {\n    return UniquePtr(new HHWheelTimerBase(std::forward<Args>(args)...));\n  }\n\n  /**\n   * A callback to be notified when a timeout has expired.\n   */\n  class Callback\n      : public boost::intrusive::list_base_hook<\n            boost::intrusive::link_mode<boost::intrusive::auto_unlink>> {\n   public:\n    Callback();\n    virtual ~Callback();\n\n    /**\n     * timeoutExpired() is invoked when the timeout has expired.\n     */\n    virtual void timeoutExpired() noexcept = 0;\n\n    /// This callback was canceled. The default implementation is to just\n    /// proxy to `timeoutExpired` but if you care about the difference between\n    /// the timeout finishing or being canceled you can override this.\n    virtual void callbackCanceled() noexcept { timeoutExpired(); }\n\n    /**\n     * Cancel the timeout, if it is running.\n     *\n     * If the timeout is not scheduled, cancelTimeout() does nothing.\n     */\n    void cancelTimeout() {\n      if (wheel_ == nullptr) {\n        // We're not scheduled, so there's nothing to do.\n        return;\n      }\n      cancelTimeoutImpl();\n    }\n\n    /**\n     * Return true if this timeout is currently scheduled, and false otherwise.\n     */\n    bool isScheduled() const { return wheel_ != nullptr; }\n\n    /**\n     * Get the time remaining until this timeout expires. Return 0 if this\n     * timeout is not scheduled or expired. Otherwise, return expiration\n     * time minus current time.\n     */\n    Duration getTimeRemaining() const {\n      return getTimeRemaining(std::chrono::steady_clock::now());\n    }\n\n   private:\n    // Get the time remaining until this timeout expires\n    Duration getTimeRemaining(std::chrono::steady_clock::time_point now) const {\n      if (now >= expiration_) {\n        return Duration(0);\n      }\n      return std::chrono::duration_cast<Duration>(expiration_ - now);\n    }\n\n    void setScheduled(\n        HHWheelTimerBase* wheel,\n        std::chrono::steady_clock::time_point deadline);\n    void cancelTimeoutImpl();\n\n    HHWheelTimerBase* wheel_{nullptr};\n    std::chrono::steady_clock::time_point expiration_{};\n    int bucket_{-1};\n\n    using List = boost::intrusive::\n        list<Callback, boost::intrusive::constant_time_size<false>>;\n\n    std::shared_ptr<RequestContext> requestContext_;\n\n    // Give HHWheelTimerBase direct access to our members so it can take care\n    // of scheduling/cancelling.\n    friend class HHWheelTimerBase;\n  };\n\n  /**\n   * Create a new HHWheelTimerBase with the specified interval and the\n   * default timeout value set.\n   *\n   * Objects created using this version of constructor can be used\n   * to schedule both variable interval timeouts using\n   * scheduleTimeout(callback, timeout) method, and default\n   * interval timeouts using scheduleTimeout(callback) method.\n   */\n  static int DEFAULT_TICK_INTERVAL;\n  explicit HHWheelTimerBase(\n      folly::TimeoutManager* timeoutMananger,\n      Duration intervalDuration = Duration(DEFAULT_TICK_INTERVAL),\n      AsyncTimeout::InternalEnum internal = AsyncTimeout::InternalEnum::NORMAL,\n      Duration defaultTimeoutDuration = Duration(-1));\n\n  /**\n   * Cancel all outstanding timeouts\n   *\n   * @returns the number of timeouts that were cancelled.\n   */\n  size_t cancelAll();\n\n  /**\n   * Get the tick interval for this HHWheelTimerBase.\n   *\n   * Returns the tick interval in milliseconds.\n   */\n  Duration getTickInterval() const { return interval_.interval(); }\n\n  /**\n   * Get the default timeout interval for this HHWheelTimerBase.\n   *\n   * Returns the timeout interval in milliseconds.\n   */\n  Duration getDefaultTimeout() const { return defaultTimeout_; }\n\n  /**\n   * Set the default timeout interval for this HHWheelTimerBase.\n   */\n  void setDefaultTimeout(Duration timeout) { defaultTimeout_ = timeout; }\n\n  /**\n   * Schedule the specified Callback to be invoked after the\n   * specified timeout interval.\n   *\n   * If the callback is already scheduled, this cancels the existing timeout\n   * before scheduling the new timeout.\n   */\n  void scheduleTimeout(Callback* callback, Duration timeout);\n\n  /**\n   * Schedule the specified Callback to be invoked after the\n   * default timeout interval.\n   *\n   * If the callback is already scheduled, this cancels the existing timeout\n   * before scheduling the new timeout.\n   *\n   * This method uses CHECK() to make sure that the default timeout was\n   * specified on the object initialization.\n   */\n  void scheduleTimeout(Callback* callback);\n\n  template <class F>\n  void scheduleTimeoutFn(F fn, Duration timeout) {\n    struct Wrapper : Callback {\n      Wrapper(F f) : fn_(std::move(f)) {}\n      void timeoutExpired() noexcept override {\n        try {\n          fn_();\n        } catch (...) {\n          LOG(ERROR) << \"HHWheelTimerBase timeout callback threw unhandled \"\n                     << exceptionStr(current_exception());\n        }\n        delete this;\n      }\n      F fn_;\n    };\n    Wrapper* w = new Wrapper(std::move(fn));\n    scheduleTimeout(w, timeout);\n  }\n\n  /**\n   * Return the number of currently pending timeouts\n   */\n  std::size_t count() const { return count_; }\n\n  bool isDetachable() const { return !folly::AsyncTimeout::isScheduled(); }\n\n  using folly::AsyncTimeout::attachEventBase;\n  using folly::AsyncTimeout::detachEventBase;\n  using folly::AsyncTimeout::getTimeoutManager;\n\n protected:\n  /**\n   * Protected destructor.\n   *\n   * Use destroy() instead.  See the comments in DelayedDestruction for more\n   * details.\n   */\n  ~HHWheelTimerBase() override;\n\n private:\n  // Forbidden copy constructor and assignment operator\n  HHWheelTimerBase(HHWheelTimerBase const&) = delete;\n  HHWheelTimerBase& operator=(HHWheelTimerBase const&) = delete;\n\n  // Methods inherited from AsyncTimeout\n  void timeoutExpired() noexcept override;\n\n  detail::HHWheelTimerDurationInterval<Duration> interval_;\n  Duration defaultTimeout_;\n\n  static constexpr int WHEEL_BUCKETS = 4;\n  static constexpr int WHEEL_BITS = 8;\n  static constexpr unsigned int WHEEL_SIZE = (1 << WHEEL_BITS);\n  static constexpr unsigned int WHEEL_MASK = (WHEEL_SIZE - 1);\n  static constexpr uint32_t LARGEST_SLOT = 0xffffffffUL;\n\n  using CallbackList = typename Callback::List;\n  CallbackList buckets_[WHEEL_BUCKETS][WHEEL_SIZE];\n  std::array<std::size_t, (WHEEL_SIZE / sizeof(std::size_t)) / 8> bitmap_;\n\n  int64_t timeToWheelTicks(Duration t) { return interval_.toWheelTicks(t); }\n\n  bool cascadeTimers(\n      int bucket, int tick, std::chrono::steady_clock::time_point curTime);\n  void scheduleTimeoutInternal(Duration timeout);\n\n  int64_t expireTick_;\n  std::size_t count_;\n  std::chrono::steady_clock::time_point startTime_;\n\n  int64_t calcNextTick();\n  int64_t calcNextTick(std::chrono::steady_clock::time_point curTime);\n\n  static bool inSameEpoch(int64_t tickA, int64_t tickB) {\n    return (tickA >> WHEEL_BITS) == (tickB >> WHEEL_BITS);\n  }\n\n  /**\n   * Schedule a given timeout by putting it into the appropriate bucket of the\n   * wheel.\n   *\n   * @param callback           Callback to fire after `timeout`\n   * @param dueTick            Tick at which the timer is due.\n   * @param nextTickToProcess  next tick that was not processed by the timer\n   *                           yet. Can be less than nextTick if we're lagging.\n   * @param nextTick           next tick based on the actual time\n   */\n  void scheduleTimeoutImpl(\n      Callback* callback,\n      int64_t dueTick,\n      int64_t nextTickToProcess,\n      int64_t nextTick);\n\n  /**\n   * Compute next required wheel tick to fire and schedule the timeout for that\n   * tick.\n   *\n   * @param nextTick  next tick based on the actual time\n   */\n  void scheduleNextTimeout(int64_t nextTick);\n\n  /**\n   * Schedule next wheel timeout in a fixed number of wheel ticks.\n   *\n   * @param nextTick  next tick based on the actual time\n   * @param ticks     number of ticks in which the timer should fire\n   */\n  void scheduleNextTimeout(int64_t nextTick, int64_t ticks);\n\n  size_t cancelTimeoutsFromList(CallbackList& timeouts);\n\n  bool* processingCallbacksGuard_;\n  // Timeouts that we're about to run. They're already extracted from their\n  // corresponding buckets, so we need this list for the `cancelAll` to be able\n  // to cancel them.\n  CallbackList timeoutsToRunNow_;\n\n  std::chrono::steady_clock::time_point getCurTime() {\n    return std::chrono::steady_clock::now();\n  }\n};\n\n// std::chrono::milliseconds\nusing HHWheelTimer = HHWheelTimerBase<std::chrono::milliseconds>;\nextern template class HHWheelTimerBase<std::chrono::milliseconds>;\n\n// std::chrono::microseconds\ntemplate <>\nvoid HHWheelTimerBase<std::chrono::microseconds>::scheduleTimeoutInternal(\n    std::chrono::microseconds timeout);\n\nusing HHWheelTimerHighRes = HHWheelTimerBase<std::chrono::microseconds>;\nextern template class HHWheelTimerBase<std::chrono::microseconds>;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUring.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUring.h>\n\n#include <cerrno>\n#include <ostream>\n#include <string>\n\n#include <fmt/ostream.h>\n#include <glog/logging.h>\n\n#include <folly/Exception.h>\n#include <folly/String.h>\n#include <folly/portability/Unistd.h>\n\n#if FOLLY_HAS_LIBURING\n\n// helpers\nnamespace {\n// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2\nuint32_t roundUpToNextPowerOfTwo(uint32_t num) {\n  if (num == 0) {\n    return 0;\n  }\n  num--;\n  num |= num >> 1;\n  num |= num >> 2;\n  num |= num >> 4;\n  num |= num >> 8;\n  num |= num >> 16;\n  return num + 1;\n}\n\n#define X(c) \\\n  case c:    \\\n    return #c\n\nconst char* ioUringOpToString(unsigned char op) {\n  switch (op) {\n    X(IORING_OP_NOP);\n    X(IORING_OP_READV);\n    X(IORING_OP_WRITEV);\n    X(IORING_OP_FSYNC);\n    X(IORING_OP_READ_FIXED);\n    X(IORING_OP_WRITE_FIXED);\n    X(IORING_OP_POLL_ADD);\n    X(IORING_OP_POLL_REMOVE);\n    X(IORING_OP_SYNC_FILE_RANGE);\n    X(IORING_OP_SENDMSG);\n    X(IORING_OP_RECVMSG);\n    X(IORING_OP_TIMEOUT);\n  }\n  return \"<INVALID op>\";\n}\n\n#undef X\n\nvoid toStream(std::ostream& os, const struct io_uring_sqe& sqe) {\n  fmt::print(\n      os,\n      \"user_data={}, opcode={}, ioprio={}, f={}, \",\n      sqe.user_data,\n      ioUringOpToString(sqe.opcode),\n      sqe.ioprio,\n      folly::AsyncBaseOp::fd2name(sqe.fd));\n\n  switch (sqe.opcode) {\n    case IORING_OP_READV:\n    case IORING_OP_WRITEV: {\n      auto offset = sqe.off;\n      auto* iovec = reinterpret_cast<struct iovec*>(sqe.addr);\n      os << \"{\";\n      for (unsigned int i = 0; i < sqe.len; i++) {\n        if (i) {\n          os << \",\";\n        }\n        fmt::print(\n            os,\n            \"buf={}, offset={}, nbytes={}\",\n            iovec[i].iov_base,\n            offset,\n            iovec[i].iov_len);\n        // advance the offset\n        offset += iovec[i].iov_len;\n      }\n      os << \"}\";\n    } break;\n    default:\n      os << \"[TODO: write debug string for \" << ioUringOpToString(sqe.opcode)\n         << \"] \";\n      break;\n  }\n}\n\n} // namespace\n\nnamespace folly {\n\nIoUringOp::IoUringOp(NotificationCallback cb, Options options)\n    : AsyncBaseOp(std::move(cb)), options_(options) {\n  ::memset(iov_, 0, sizeof(iov_));\n}\n\nvoid IoUringOp::reset(NotificationCallback cb) {\n  CHECK_NE(state_, State::PENDING);\n  cb_ = std::move(cb);\n  state_ = State::UNINITIALIZED;\n  result_ = -EINVAL;\n}\n\nIoUringOp::~IoUringOp() {}\n\nvoid IoUringOp::pread(int fd, void* buf, size_t size, off_t start) {\n  init();\n  iov_[0].iov_base = buf;\n  iov_[0].iov_len = size;\n  io_uring_prep_readv(&sqe_.sqe, fd, iov_, 1, start);\n  io_uring_sqe_set_data(&sqe_.sqe, this);\n}\n\nvoid IoUringOp::preadv(int fd, const iovec* iov, int iovcnt, off_t start) {\n  init();\n  io_uring_prep_readv(&sqe_.sqe, fd, iov, iovcnt, start);\n  io_uring_sqe_set_data(&sqe_.sqe, this);\n}\n\nvoid IoUringOp::pread(\n    int fd, void* buf, size_t size, off_t start, int buf_index) {\n  init();\n  io_uring_prep_read_fixed(&sqe_.sqe, fd, buf, size, start, buf_index);\n  io_uring_sqe_set_data(&sqe_.sqe, this);\n}\n\nvoid IoUringOp::pwrite(int fd, const void* buf, size_t size, off_t start) {\n  init();\n  iov_[0].iov_base = const_cast<void*>(buf);\n  iov_[0].iov_len = size;\n  io_uring_prep_writev(&sqe_.sqe, fd, iov_, 1, start);\n  io_uring_sqe_set_data(&sqe_.sqe, this);\n}\n\nvoid IoUringOp::pwritev(int fd, const iovec* iov, int iovcnt, off_t start) {\n  init();\n  io_uring_prep_writev(&sqe_.sqe, fd, iov, iovcnt, start);\n  io_uring_sqe_set_data(&sqe_.sqe, this);\n}\n\nvoid IoUringOp::pwrite(\n    int fd, const void* buf, size_t size, off_t start, int buf_index) {\n  init();\n  io_uring_prep_write_fixed(&sqe_.sqe, fd, buf, size, start, buf_index);\n  io_uring_sqe_set_data(&sqe_.sqe, this);\n}\n\nvoid IoUringOp::toStream(std::ostream& os) const {\n  os << \"{\" << state_ << \", [\" << getSqeSize() << \"], \";\n\n  if (state_ != AsyncBaseOp::State::UNINITIALIZED) {\n    ::toStream(os, sqe_.sqe);\n  }\n\n  if (state_ == AsyncBaseOp::State::COMPLETED) {\n    os << \"result=\" << result_;\n    if (result_ < 0) {\n      os << \" (\" << errnoStr(-result_) << ')';\n    }\n    os << \", \";\n  }\n\n  os << \"}\";\n}\n\nstd::ostream& operator<<(std::ostream& os, const IoUringOp& op) {\n  op.toStream(os);\n  return os;\n}\n\nIoUring::IoUring(\n    size_t capacity,\n    PollMode pollMode,\n    size_t maxSubmit,\n    IoUringOp::Options options)\n    : AsyncBase(capacity, pollMode),\n      maxSubmit_((maxSubmit <= capacity) ? maxSubmit : capacity),\n      options_(options) {\n  ::memset(&ioRing_, 0, sizeof(ioRing_));\n  ::memset(&params_, 0, sizeof(params_));\n\n  if (options_.sqe128) {\n    params_.flags |= IORING_SETUP_SQE128;\n  }\n\n  if (options.cqe32) {\n    params_.flags |= IORING_SETUP_CQE32;\n  }\n\n  params_.flags |= IORING_SETUP_CQSIZE;\n  params_.cq_entries = roundUpToNextPowerOfTwo(capacity_);\n\n  // we need to call initializeContext() in the constructor\n  // since we have code that relies on registering the pollFd_\n  // before any operation is started\n  initializeContext();\n}\n\nIoUring::~IoUring() {\n  CHECK_EQ(pending_, 0);\n  if (ioRing_.ring_fd > 0) {\n    ::io_uring_queue_exit(&ioRing_);\n    ioRing_.ring_fd = -1;\n  }\n\n  pollFd_ = -1;\n}\n\nbool IoUring::isAvailable() {\n  try {\n    IoUring ioUring(1);\n  } catch (...) {\n    return false;\n  }\n\n  return true;\n}\n\nint IoUring::register_buffers(\n    const struct iovec* iovecs, unsigned int nr_iovecs) {\n  std::unique_lock lk(submitMutex_);\n\n  return io_uring_register_buffers(&ioRing_, iovecs, nr_iovecs);\n}\n\nint IoUring::unregister_buffers() {\n  std::unique_lock lk(submitMutex_);\n  return io_uring_unregister_buffers(&ioRing_);\n}\n\nvoid IoUring::initializeContext() {\n  if (!init_.load(std::memory_order_acquire)) {\n    std::lock_guard lock(initMutex_);\n    if (!init_.load(std::memory_order_relaxed)) {\n      int rc = ::io_uring_queue_init_params(\n          roundUpToNextPowerOfTwo(maxSubmit_), &ioRing_, &params_);\n      checkKernelError(rc, \"IoUring: io_uring_queue_init_params failed\");\n      DCHECK_GT(ioRing_.ring_fd, 0);\n      if (pollMode_ == POLLABLE) {\n        pollFd_ = ioRing_.ring_fd;\n      }\n      init_.store(true, std::memory_order_release);\n    }\n  }\n}\n\nint IoUring::drainPollFd() {\n  return static_cast<int>(::io_uring_cq_ready(&ioRing_));\n}\n\nint IoUring::submitOne(AsyncBase::Op* op) {\n  // -1 return here will trigger throw if op isn't an IoUringOp\n  IoUringOp* iop = op->getIoUringOp();\n\n  if (!iop) {\n    return -1;\n  }\n\n  // we require same options for both the IoUringOp and the IoUring instance\n  if (iop->getOptions() != getOptions()) {\n    return -1;\n  }\n\n  std::unique_lock lk(submitMutex_);\n  auto* sqe = io_uring_get_sqe(&ioRing_);\n  if (!sqe) {\n    return -1;\n  }\n\n  ::memcpy(sqe, &iop->getSqe(), iop->getSqeSize());\n\n  return io_uring_submit(&ioRing_);\n}\n\nint IoUring::submitRange(Range<AsyncBase::Op**> ops) {\n  size_t num = 0;\n  int total = 0;\n  std::unique_lock lk(submitMutex_);\n  for (size_t i = 0; i < ops.size(); i++) {\n    IoUringOp* iop = ops[i]->getIoUringOp();\n    if (!iop) {\n      continue;\n    }\n\n    if (iop->getOptions() != getOptions()) {\n      continue;\n    }\n\n    auto* sqe = io_uring_get_sqe(&ioRing_);\n    if (!sqe) {\n      break;\n    }\n\n    ::memcpy(sqe, &iop->getSqe(), iop->getSqeSize());\n    ++num;\n    if (num % maxSubmit_ == 0 || (i + 1 == ops.size())) {\n      auto ret = io_uring_submit(&ioRing_);\n      if (ret <= 0) {\n        return total;\n      }\n\n      total += ret;\n    }\n  }\n\n  return total ? total : -1;\n}\n\nRange<AsyncBase::Op**> IoUring::doWait(\n    WaitType type,\n    size_t minRequests,\n    size_t maxRequests,\n    std::vector<AsyncBase::Op*>& result) {\n  result.clear();\n\n  size_t count = 0;\n  while (count < maxRequests) {\n    struct io_uring_cqe* cqe = nullptr;\n    if (!io_uring_peek_cqe(&ioRing_, &cqe) && cqe) {\n      count++;\n      Op* op = reinterpret_cast<Op*>(io_uring_cqe_get_data(cqe));\n      CHECK(op);\n      auto res = cqe->res;\n      op->setCqe(cqe);\n      io_uring_cqe_seen(&ioRing_, cqe);\n      decrementPending();\n      switch (type) {\n        case WaitType::COMPLETE:\n          op->complete(res);\n          break;\n        case WaitType::CANCEL:\n          op->cancel();\n          break;\n      }\n      result.push_back(op);\n    } else {\n      if (count < minRequests) {\n        io_uring_wait_cqe(&ioRing_, &cqe);\n      } else {\n        break;\n      }\n    }\n  }\n\n  return range(result);\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUring.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/SharedMutex.h>\n#include <folly/io/async/AsyncBase.h>\n#include <folly/io/async/Liburing.h>\n\n#if FOLLY_HAS_LIBURING\n\n#include <liburing.h> // @manual\n\nnamespace folly {\n\n/**\n * An IoUringOp represents a pending operation.  You may set a notification\n * callback or you may use this class's methods directly.\n *\n * The op must remain allocated until it is completed or canceled.\n */\nclass IoUringOp : public AsyncBaseOp {\n  friend class IoUring;\n  friend std::ostream& operator<<(std::ostream& stream, const IoUringOp& o);\n\n public:\n  struct Options {\n    Options() : sqe128(false), cqe32(false) {}\n    bool sqe128;\n    bool cqe32;\n\n    bool operator==(const Options& options) const {\n      return sqe128 == options.sqe128 && cqe32 == options.cqe32;\n    }\n\n    bool operator!=(const Options& options) const {\n      return !operator==(options);\n    }\n  };\n\n  IoUringOp(\n      NotificationCallback cb = NotificationCallback(),\n      Options options = Options());\n  IoUringOp(const IoUringOp&) = delete;\n  IoUringOp& operator=(const IoUringOp&) = delete;\n  ~IoUringOp() override;\n\n  /**\n   * Initiate a read request.\n   */\n  void pread(int fd, void* buf, size_t size, off_t start) override;\n  void preadv(int fd, const iovec* iov, int iovcnt, off_t start) override;\n  void pread(\n      int fd, void* buf, size_t size, off_t start, int buf_index) override;\n\n  /**\n   * Initiate a write request.\n   */\n  void pwrite(int fd, const void* buf, size_t size, off_t start) override;\n  void pwritev(int fd, const iovec* iov, int iovcnt, off_t start) override;\n  void pwrite(int fd, const void* buf, size_t size, off_t start, int buf_index)\n      override;\n\n  void reset(NotificationCallback cb = NotificationCallback()) override;\n\n  AsyncIOOp* getAsyncIOOp() override { return nullptr; }\n\n  IoUringOp* getIoUringOp() override { return this; }\n\n  void toStream(std::ostream& os) const override;\n\n  void initBase() { init(); }\n\n  struct io_uring_sqe& getSqe() { return sqe_.sqe; }\n\n  size_t getSqeSize() const {\n    return options_.sqe128 ? 128 : sizeof(struct io_uring_sqe);\n  }\n\n  const struct io_uring_cqe& getCqe() const {\n    return *reinterpret_cast<const struct io_uring_cqe*>(&cqe_);\n  }\n\n  size_t getCqeSize() const {\n    return options_.cqe32 ? 32 : sizeof(struct io_uring_cqe);\n  }\n\n  void setCqe(const struct io_uring_cqe* cqe) {\n    ::memcpy(&cqe_, cqe, getCqeSize());\n  }\n\n  const Options& getOptions() const { return options_; }\n\n private:\n  Options options_;\n\n  // we use unions with the largest size to avoid\n  // indidual allocations for the sqe/cqe\n  union {\n    struct io_uring_sqe sqe;\n    uint8_t data[128];\n  } sqe_ = {};\n\n  // we have to use a union here because of -Wgnu-variable-sized-type-not-at-end\n  //__u64 big_cqe[];\n  union {\n    __u64 user_data; // first member from from io_uring_cqe\n    uint8_t data[32];\n  } cqe_ = {};\n\n  struct iovec iov_[1];\n};\n\nstd::ostream& operator<<(std::ostream& stream, const IoUringOp& op);\n\n/**\n * C++ interface around Linux io_uring\n */\nclass IoUring : public AsyncBase {\n public:\n  using Op = IoUringOp;\n\n  /**\n   * Note: the maximum number of allowed concurrent requests is controlled\n   * by the kernel IORING_MAX_ENTRIES and the memlock limit,\n   * The default IORING_MAX_ENTRIES value is usually 32K.\n   */\n  explicit IoUring(\n      size_t capacity,\n      PollMode pollMode = NOT_POLLABLE,\n      size_t maxSubmit = 1,\n      IoUringOp::Options options = IoUringOp::Options());\n  IoUring(const IoUring&) = delete;\n  IoUring& operator=(const IoUring&) = delete;\n  ~IoUring() override;\n\n  static bool isAvailable();\n\n  const IoUringOp::Options& getOptions() const { return options_; }\n\n  int register_buffers(const struct iovec* iovecs, unsigned int nr_iovecs);\n\n  int unregister_buffers();\n\n  void initializeContext() override;\n\n protected:\n  int drainPollFd() override;\n  int submitOne(AsyncBase::Op* op) override;\n  int submitRange(Range<AsyncBase::Op**> ops) override;\n\n private:\n  Range<AsyncBase::Op**> doWait(\n      WaitType type,\n      size_t minRequests,\n      size_t maxRequests,\n      std::vector<AsyncBase::Op*>& result) override;\n\n  size_t maxSubmit_;\n  IoUringOp::Options options_;\n  struct io_uring_params params_;\n  struct io_uring ioRing_;\n  mutable SharedMutex submitMutex_;\n};\n\nusing IoUringQueue = AsyncBaseQueue;\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringBackend.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <signal.h>\n\n#include <folly/Demangle.h>\n#include <folly/FileUtil.h>\n#include <folly/GLog.h>\n#include <folly/Likely.h>\n#include <folly/SpinLock.h>\n#include <folly/String.h>\n#include <folly/container/F14Map.h>\n#include <folly/container/F14Set.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/io/async/IoUringProvidedBufferRing.h>\n#include <folly/lang/Bits.h>\n#include <folly/portability/GFlags.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/SysMman.h>\n#include <folly/portability/SysSyscall.h>\n#include <folly/synchronization/CallOnce.h>\n#include <folly/tracing/StaticTracepoint.h>\n\n#if __has_include(<sys/timerfd.h>)\n#include <sys/timerfd.h>\n#endif\n\n#if FOLLY_HAS_LIBURING\n\nextern \"C\" FOLLY_ATTR_WEAK void eb_poll_loop_pre_hook(uint64_t* call_time);\nextern \"C\" FOLLY_ATTR_WEAK void eb_poll_loop_post_hook(\n    uint64_t call_time, int ret);\n\nnamespace folly {\n\nnamespace {\n\nstruct SignalRegistry {\n  struct SigInfo {\n    struct sigaction sa_{};\n    size_t refs_{0};\n  };\n  using SignalMap = std::map<int, SigInfo>;\n\n  constexpr SignalRegistry() {}\n\n  void notify(int sig);\n  void setNotifyFd(int sig, int fd);\n\n  // lock protecting the signal map\n  folly::MicroSpinLock mapLock_ = {0};\n  std::unique_ptr<SignalMap> map_;\n  std::atomic<int> notifyFd_{-1};\n};\n\nSignalRegistry& getSignalRegistry() {\n  static auto& sInstance = *new SignalRegistry();\n  return sInstance;\n}\n\nvoid evSigHandler(int sig) {\n  getSignalRegistry().notify(sig);\n}\n\nvoid SignalRegistry::notify(int sig) {\n  // use try_lock in case somebody already has the lock\n  std::unique_lock lk(mapLock_, std::try_to_lock);\n  if (lk.owns_lock()) {\n    int fd = notifyFd_.load();\n    if (fd >= 0) {\n      uint8_t sigNum = static_cast<uint8_t>(sig);\n      std::ignore = fileops::write(fd, &sigNum, 1);\n    }\n  }\n}\n\nvoid SignalRegistry::setNotifyFd(int sig, int fd) {\n  std::lock_guard g(mapLock_);\n  if (fd >= 0) {\n    if (!map_) {\n      map_ = std::make_unique<SignalMap>();\n    }\n    // switch the fd\n    notifyFd_.store(fd);\n\n    auto iter = map_->find(sig);\n    if (iter != map_->end()) {\n      iter->second.refs_++;\n    } else {\n      auto& entry = (*map_)[sig];\n      entry.refs_ = 1;\n      struct sigaction sa = {};\n      sa.sa_handler = evSigHandler;\n      sa.sa_flags |= SA_RESTART;\n      ::sigfillset(&sa.sa_mask);\n\n      if (::sigaction(sig, &sa, &entry.sa_) == -1) {\n        map_->erase(sig);\n      }\n    }\n  } else {\n    notifyFd_.store(fd);\n\n    if (map_) {\n      auto iter = map_->find(sig);\n      if ((iter != map_->end()) && (--iter->second.refs_ == 0)) {\n        auto entry = iter->second;\n        map_->erase(iter);\n        // just restore\n        ::sigaction(sig, &entry.sa_, nullptr);\n      }\n    }\n  }\n}\n\nvoid checkLogOverflow([[maybe_unused]] io_uring* ring) {\n  if (::io_uring_cq_has_overflow(ring)) {\n    FB_LOG_EVERY_MS(ERROR, 10000)\n        << \"IoUringBackend \" << ring << \" cq overflow\";\n  }\n}\n\nclass SQGroupInfoRegistry {\n private:\n  // a group is a collection of io_uring instances\n  // that share up to numThreads SQ poll threads\n  struct SQGroupInfo {\n    struct SQSubGroupInfo {\n      folly::F14FastSet<int> fds;\n      size_t count{0};\n\n      void add(int fd) {\n        CHECK(!fds.count(fd));\n        fds.insert(fd);\n        ++count;\n      }\n\n      size_t remove(int fd) {\n        auto iter = fds.find(fd);\n        CHECK(iter != fds.end());\n        fds.erase(fd);\n        --count;\n\n        return count;\n      }\n    };\n\n    SQGroupInfo(size_t num, std::set<uint32_t> const& cpus)\n        : subGroups(num), nextCpu(cpus.begin(), cpus.end()) {}\n\n    // returns the least loaded subgroup\n    SQSubGroupInfo* getNextSubgroup() {\n      size_t min_idx = 0;\n      for (size_t i = 0; i < subGroups.size(); i++) {\n        if (subGroups[i].count == 0) {\n          return &subGroups[i];\n        }\n\n        if (subGroups[i].count < subGroups[min_idx].count) {\n          min_idx = i;\n        }\n      }\n\n      return &subGroups[min_idx];\n    }\n\n    size_t add(int fd, SQSubGroupInfo* sg) {\n      CHECK(!fdSgMap.count(fd));\n      fdSgMap.emplace(fd, sg);\n      sg->add(fd);\n      ++count;\n\n      return count;\n    }\n\n    size_t remove(int fd) {\n      auto iter = fdSgMap.find(fd);\n      CHECK(iter != fdSgMap.end());\n      iter->second->remove(fd);\n      fdSgMap.erase(iter);\n      --count;\n\n      return count;\n    }\n\n    // file descriptor to sub group index map\n    folly::F14FastMap<int, SQSubGroupInfo*> fdSgMap;\n    // array of subgoups\n    std::vector<SQSubGroupInfo> subGroups;\n    // number of entries\n    size_t count{0};\n    // Set of CPUs we will bind threads to.\n    std::vector<uint32_t> nextCpu;\n    int nextCpuIndex{0};\n  };\n\n  using SQGroupInfoMap = folly::F14FastMap<std::string, SQGroupInfo>;\n  SQGroupInfoMap map_;\n  std::mutex mutex_;\n\n public:\n  SQGroupInfoRegistry() = default;\n\n  using FDCreateFunc = folly::Function<int(struct io_uring_params&)>;\n  using FDCloseFunc = folly::Function<void()>;\n\n  size_t addTo(\n      const std::string& groupName,\n      size_t groupNumThreads,\n      FDCreateFunc& createFd,\n      struct io_uring_params& params,\n      std::set<uint32_t> const& cpus) {\n    if (groupName.empty()) {\n      createFd(params);\n      return 0;\n    }\n\n    std::lock_guard g(mutex_);\n\n    auto [iter, inserted] = map_.try_emplace(groupName, groupNumThreads, cpus);\n    auto& info = iter->second;\n    auto* sg = info.getNextSubgroup();\n    if (sg->count) {\n      // we're adding to a non empty subgroup\n      params.wq_fd = *(sg->fds.begin());\n      params.flags |= IORING_SETUP_ATTACH_WQ;\n    } else if (!info.nextCpu.empty()) {\n      // First use of this subgroup, pin thread to CPU if specified.\n      auto cpu = info.nextCpu[info.nextCpuIndex];\n      info.nextCpuIndex = (info.nextCpuIndex + 1) % info.nextCpu.size();\n\n      params.sq_thread_cpu = cpu;\n      params.flags |= IORING_SETUP_SQ_AFF;\n    }\n\n    auto fd = createFd(params);\n    if (fd < 0) {\n      return 0;\n    }\n\n    return info.add(fd, sg);\n  }\n\n  size_t removeFrom(const std::string& groupName, int fd, FDCloseFunc& func) {\n    if (groupName.empty()) {\n      func();\n      return 0;\n    }\n\n    size_t ret;\n\n    std::lock_guard g(mutex_);\n\n    func();\n\n    auto iter = map_.find(groupName);\n    CHECK(iter != map_.end());\n    // check for empty group\n    if ((ret = iter->second.remove(fd)) == 0) {\n      map_.erase(iter);\n    }\n\n    return ret;\n  }\n};\n\nstatic folly::Indestructible<SQGroupInfoRegistry> sSQGroupInfoRegistry;\n\nbool validateZeroCopyRxOptions(IoUringOptions& options) {\n  if (options.zeroCopyRx &&\n      (options.zcRxIfname.empty() || options.zcRxIfindex <= 0 ||\n       options.zcRxQueueId == -1 || !options.resolveNapiId ||\n       !options.srcPortQueueId)) {\n    return false;\n  }\n\n  return true;\n}\n\n// Currently a 4K page size is required.\nconstexpr size_t kZeroCopyPageSize = 4096;\n\n} // namespace\n\nIoUringBackend::SocketPair::SocketPair() {\n  if (::socketpair(AF_UNIX, SOCK_STREAM, 0, fds_.data())) {\n    throw std::runtime_error(\"socketpair error\");\n  }\n\n  // set the sockets to non blocking mode\n  for (auto fd : fds_) {\n    auto flags = ::fcntl(fd, F_GETFL, 0);\n    ::fcntl(fd, F_SETFL, flags | O_NONBLOCK);\n  }\n}\n\nIoUringBackend::SocketPair::~SocketPair() {\n  for (auto fd : fds_) {\n    if (fd >= 0) {\n      fileops::close(fd);\n    }\n  }\n}\n\nIoUringBackend::FdRegistry::FdRegistry(io_uring& ioRing, size_t n)\n    : ioRing_(ioRing), files_(n, -1), inUse_(n), records_(n) {\n  if (n > std::numeric_limits<int>::max()) {\n    throw std::runtime_error(\"too many registered files\");\n  }\n}\n\nint IoUringBackend::FdRegistry::init() {\n  if (inUse_) {\n    int ret = ::io_uring_register_files(&ioRing_, files_.data(), inUse_);\n\n    if (!ret) {\n      // build and set the free list head if we succeed\n      for (int i = 0; i < static_cast<int>(records_.size()); i++) {\n        records_[i].idx_ = i;\n        free_.push_front(records_[i]);\n      }\n    } else {\n      LOG(ERROR) << \"io_uring_register_files(\" << inUse_ << \") \"\n                 << \"failed errno = \" << errno << \":\\\"\"\n                 << folly::errnoStr(errno) << \"\\\" \" << this;\n    }\n\n    return ret;\n  }\n\n  return 0;\n}\n\nIoUringFdRegistrationRecord* IoUringBackend::FdRegistry::alloc(\n    int fd) noexcept {\n  if (FOLLY_UNLIKELY(err_ || free_.empty())) {\n    return nullptr;\n  }\n\n  auto& record = free_.front();\n\n  // now we have an idx\n  int ret = ::io_uring_register_files_update(&ioRing_, record.idx_, &fd, 1);\n  if (ret != 1) {\n    // set the err_ flag so we do not retry again\n    // this usually happens when we hit the file desc limit\n    // and retrying this operation for every request is expensive\n    err_ = true;\n    LOG(ERROR) << \"io_uring_register_files(1) \" << \"failed errno = \" << errno\n               << \":\\\"\" << folly::errnoStr(errno) << \"\\\" \" << this;\n    return nullptr;\n  }\n\n  record.fd_ = fd;\n  record.count_ = 1;\n  free_.pop_front();\n\n  return &record;\n}\n\nbool IoUringBackend::FdRegistry::free(IoUringFdRegistrationRecord* record) {\n  if (record && (--record->count_ == 0)) {\n    record->fd_ = -1;\n    int ret = ::io_uring_register_files_update(\n        &ioRing_, record->idx_, &record->fd_, 1);\n\n    // we add it to the free list anyway here\n    free_.push_front(*record);\n\n    return (ret == 1);\n  }\n\n  return false;\n}\n\nFOLLY_ALWAYS_INLINE io_uring_sqe* IoUringBackend::getUntrackedSqe() {\n  io_uring_sqe* ret = ::io_uring_get_sqe(&ioRing_);\n  // if running with SQ poll enabled\n  // we might have to wait for an sq entry to available\n  // before we can submit another one\n  while (!ret) {\n    if (options_.flags & Options::Flags::POLL_SQ) {\n      asm_volatile_pause();\n      ret = ::io_uring_get_sqe(&ioRing_);\n    } else {\n      submitEager();\n      ret = ::io_uring_get_sqe(&ioRing_);\n      CHECK(ret != nullptr);\n    }\n  }\n\n  ++waitingToSubmit_;\n  return ret;\n}\n\nFOLLY_ALWAYS_INLINE io_uring_sqe* IoUringBackend::getSqe() {\n  ++numInsertedEvents_;\n  return getUntrackedSqe();\n}\n\nvoid IoSqeBase::internalSubmit(io_uring_sqe* sqe) noexcept {\n  if (inFlight_) {\n    LOG(ERROR) << \"cannot resubmit an IoSqe. type=\"\n               << folly::demangle(typeid(*this));\n    return;\n  }\n  inFlight_ = true;\n  processSubmit(sqe);\n  ::io_uring_sqe_set_data(sqe, this);\n}\n\nvoid IoSqeBase::internalCallback(const io_uring_cqe* cqe) noexcept {\n  if (!(cqe->flags & IORING_CQE_F_MORE)) {\n    inFlight_ = false;\n  }\n  if (evb_) {\n    evb_->bumpHandlingTime();\n  }\n  if (cancelled_) {\n    callbackCancelled(cqe);\n  } else {\n    callback(cqe);\n  }\n}\n\nFOLLY_ALWAYS_INLINE void IoUringBackend::setProcessTimers() {\n  processTimers_ = true;\n  --numInternalEvents_;\n}\n\nFOLLY_ALWAYS_INLINE void IoUringBackend::setProcessSignals() {\n  processSignals_ = true;\n  --numInternalEvents_;\n}\n\nvoid IoUringBackend::processPollIoSqe(\n    IoUringBackend* backend, IoSqe* ioSqe, const io_uring_cqe* cqe) {\n  backend->processPollIo(ioSqe, cqe->res, cqe->flags);\n}\n\nvoid IoUringBackend::processTimerIoSqe(\n    IoUringBackend* backend, IoSqe* /*sqe*/, const io_uring_cqe* /*cqe*/) {\n  backend->setProcessTimers();\n}\n\nvoid IoUringBackend::processSignalReadIoSqe(\n    IoUringBackend* backend, IoSqe* /*sqe*/, const io_uring_cqe* /*cqe*/) {\n  backend->setProcessSignals();\n}\n\nIoUringBackend::IoUringBackend(Options options)\n    : options_(std::move(options)),\n      numEntries_(options_.capacity),\n      fdRegistry_(ioRing_, options_.registeredFds) {\n  // create the timer fd\n  timerFd_ = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);\n  if (timerFd_ < 0) {\n    throw std::runtime_error(\"timerfd_create error\");\n  }\n  if (!validateZeroCopyRxOptions(options_)) {\n    throw std::runtime_error(\"invalid zero copy rx options\");\n  }\n\n  ::memset(&ioRing_, 0, sizeof(ioRing_));\n  ::memset(&params_, 0, sizeof(params_));\n\n  params_.flags |= IORING_SETUP_CQSIZE;\n  params_.cq_entries = options_.capacity;\n\n  if (options_.taskRunCoop) {\n    params_.flags |= IORING_SETUP_COOP_TASKRUN;\n  }\n  if (options_.deferTaskRun && kernelSupportsDeferTaskrun()) {\n    params_.flags |= IORING_SETUP_SINGLE_ISSUER;\n    params_.flags |= IORING_SETUP_DEFER_TASKRUN;\n    params_.flags |= IORING_SETUP_SUBMIT_ALL;\n    params_.flags |= IORING_SETUP_R_DISABLED;\n    usingDeferTaskrun_ = true;\n  }\n\n  if (options_.zeroCopyRx) {\n    params_.flags |= IORING_SETUP_CQE32;\n    params_.flags |= IORING_SETUP_DEFER_TASKRUN;\n    params_.flags |= IORING_SETUP_SINGLE_ISSUER;\n    params_.flags |= IORING_SETUP_R_DISABLED;\n    params_.flags |= IORING_SETUP_COOP_TASKRUN;\n    params_.flags |= IORING_SETUP_SUBMIT_ALL;\n\n    napiId_ =\n        options_.resolveNapiId(options_.zcRxIfindex, options_.zcRxQueueId);\n  }\n\n  // poll SQ options\n  if (options_.flags & Options::Flags::POLL_SQ) {\n    params_.flags |= IORING_SETUP_SQPOLL;\n    params_.sq_thread_idle = options_.sqIdle.count();\n  }\n\n  SQGroupInfoRegistry::FDCreateFunc func = [&](io_uring_params& params) {\n    while (true) {\n      // allocate entries both for poll add and cancel\n      auto sqeSize =\n          options_.sqeSize > 0 ? options_.sqeSize : 2 * options_.maxSubmit;\n      int ret = ::io_uring_queue_init_params(sqeSize, &ioRing_, &params);\n      if (ret == 0) {\n        return ioRing_.ring_fd;\n      }\n\n      options_.capacity /= 2;\n      if (options_.minCapacity && (options_.capacity >= options_.minCapacity)) {\n        LOG(INFO) << \"io_uring_queue_init_params(\" << 2 * options_.maxSubmit\n                  << \",\" << params.cq_entries << \") failed errno = \" << errno\n                  << \":\\\"\" << folly::errnoStr(errno) << \"\\\" \" << this\n                  << \" retrying with capacity = \" << options_.capacity;\n\n        params_.cq_entries = options_.capacity;\n        numEntries_ = options_.capacity;\n        continue;\n      }\n\n      LOG(ERROR) << \"io_uring_queue_init_params(\" << 2 * options_.maxSubmit\n                 << \",\" << params.cq_entries << \") failed ret = \" << ret\n                 << \":\\\"\" << folly::errnoStr(ret) << \"\\\" \" << this;\n\n      if (ret == -ENOMEM) {\n        throw std::runtime_error(\"io_uring_queue_init error out of memory\");\n      }\n      throw NotAvailable(\"io_uring_queue_init error\");\n    }\n  };\n\n  auto ret = sSQGroupInfoRegistry->addTo(\n      options_.sqGroupName,\n      options_.sqGroupNumThreads,\n      func,\n      params_,\n      options_.sqCpus);\n\n  if (!options_.sqGroupName.empty()) {\n    LOG(INFO) << \"Adding to SQ poll group \\\"\" << options_.sqGroupName\n              << \"\\\" ret = \" << ret << \" fd = \" << ioRing_.ring_fd;\n  }\n\n  numEntries_ *= 2;\n\n  // timer entry\n  timerEntry_ = std::make_unique<IoSqe>(this, false, true /*persist*/);\n  timerEntry_->backendCb_ = IoUringBackend::processTimerIoSqe;\n\n  // signal entry\n  signalReadEntry_ = std::make_unique<IoSqe>(this, false, true /*persist*/);\n  signalReadEntry_->backendCb_ = IoUringBackend::processSignalReadIoSqe;\n  // delay adding the timer and signal fds until running the loop first time\n\n  if (!usingDeferTaskrun_) {\n    // can do this now, no need to delay if not using IORING_SETUP_SINGLE_ISSUER\n    initSubmissionLinked();\n  }\n}\n\nIoUringBackend::~IoUringBackend() {\n  shuttingDown_ = true;\n\n  cleanup();\n\n  CHECK(!timerEntry_);\n  CHECK(!signalReadEntry_);\n  CHECK(freeList_.empty());\n\n  fileops::close(timerFd_);\n}\n\nvoid IoUringBackend::cleanup() {\n  if (ioRing_.ring_fd <= 0) {\n    return;\n  }\n\n  // release the nonsubmitted items from the submitList\n  auto processSubmitList = [&]() {\n    IoSqeBaseList mustSubmitList;\n    while (!submitList_.empty()) {\n      auto* ioSqe = &submitList_.front();\n      submitList_.pop_front();\n      if (IoSqe* i = dynamic_cast<IoSqe*>(ioSqe); i) {\n        releaseIoSqe(i);\n      } else {\n        mustSubmitList.push_back(*ioSqe);\n      }\n    }\n    prepList(mustSubmitList);\n  };\n\n  // wait for the outstanding events to finish\n  processSubmitList();\n  while (isWaitingToSubmit() || numInsertedEvents_ > numInternalEvents_) {\n    io_uring_cqe* cqe = nullptr;\n    processSubmitList();\n    int ret = submitEager();\n    if (ret == -EEXIST) {\n      LOG(ERROR) << \"using DeferTaskrun, but submitting from the wrong thread\";\n      break;\n    }\n\n    bool const canContinue = ret != -EEXIST && ret != -EBADR;\n    if (canContinue) {\n      ::io_uring_wait_cqe(&ioRing_, &cqe);\n    }\n    internalProcessCqe(\n        std::numeric_limits<unsigned int>::max(),\n        InternalProcessCqeMode::CANCEL_ALL);\n\n    if (!canContinue) {\n      LOG(ERROR) << \"Submit resulted in : \" << folly::errnoStr(-ret)\n                 << \" not cleanly shutting down IoUringBackend\";\n      break;\n    }\n  }\n\n  // release the active events\n  while (!activeEvents_.empty()) {\n    auto* ioSqe = &activeEvents_.front();\n    activeEvents_.pop_front();\n    releaseIoSqe(ioSqe);\n  }\n\n  // free the entries\n  timerEntry_.reset();\n  signalReadEntry_.reset();\n  freeList_.clear_and_dispose([](auto _) { delete _; });\n\n  int fd = ioRing_.ring_fd;\n  SQGroupInfoRegistry::FDCloseFunc func = [&]() {\n    // exit now\n    ::io_uring_queue_exit(&ioRing_);\n    ioRing_.ring_fd = -1;\n  };\n\n  auto ret = sSQGroupInfoRegistry->removeFrom(\n      options_.sqGroupName, ioRing_.ring_fd, func);\n\n  if (!options_.sqGroupName.empty()) {\n    LOG(INFO) << \"Removing from SQ poll group \\\"\" << options_.sqGroupName\n              << \"\\\" ret = \" << ret << \" fd = \" << fd;\n  }\n}\n\nbool IoUringBackend::isAvailable() {\n  static bool sAvailable = true;\n\n  static folly::once_flag initFlag;\n  folly::call_once(initFlag, [&]() {\n    try {\n      Options options;\n      options.setCapacity(1024);\n      IoUringBackend backend(std::move(options));\n    } catch (const NotAvailable&) {\n      sAvailable = false;\n    }\n  });\n\n  return sAvailable;\n}\n\nbool IoUringBackend::addTimerFd() {\n  submitOutstanding();\n  auto* entry = getSqe();\n  timerEntry_->prepPollAdd(entry, timerFd_, POLLIN);\n  ++numInternalEvents_;\n  return (1 == submitOne());\n}\n\nbool IoUringBackend::addSignalFds() {\n  submitOutstanding();\n  auto* entry = getSqe();\n  signalReadEntry_->prepPollAdd(entry, signalFds_.readFd(), POLLIN);\n  ++numInternalEvents_;\n  return (1 == submitOne());\n}\n\nvoid IoUringBackend::scheduleTimeout() {\n  if (!timerChanged_) {\n    return;\n  }\n\n  // reset\n  timerChanged_ = false;\n  if (!timers_.empty()) {\n    auto delta = std::chrono::duration_cast<std::chrono::microseconds>(\n        timers_.begin()->first - std::chrono::steady_clock::now());\n    if (delta < std::chrono::microseconds(1000)) {\n      delta = std::chrono::microseconds(1000);\n    }\n    scheduleTimeout(delta);\n  } else if (timerSet_) {\n    scheduleTimeout(std::chrono::microseconds(0)); // disable\n  }\n\n  // we do not call addTimerFd() here\n  // since it has to be added only once, after\n  // we process a poll callback\n}\n\nvoid IoUringBackend::scheduleTimeout(const std::chrono::microseconds& us) {\n  itimerspec val;\n  timerSet_ = us.count() != 0;\n  val.it_interval = {0, 0};\n  val.it_value.tv_sec =\n      std::chrono::duration_cast<std::chrono::seconds>(us).count();\n  val.it_value.tv_nsec =\n      std::chrono::duration_cast<std::chrono::nanoseconds>(us).count() %\n      1000000000LL;\n\n  CHECK_EQ(::timerfd_settime(timerFd_, 0, &val, nullptr), 0);\n}\n\nnamespace {\n\nstruct TimerUserData {\n  std::multimap<std::chrono::steady_clock::time_point, IoUringBackend::Event*>::\n      const_iterator iter;\n};\n\nvoid timerUserDataFreeFunction(void* v) {\n  delete static_cast<TimerUserData*>(v);\n}\n\n} // namespace\n\nvoid IoUringBackend::addTimerEvent(Event& event, const timeval* timeout) {\n  auto getTimerExpireTime = [](const auto& timeout2) {\n    using namespace std::chrono;\n    auto now = steady_clock::now();\n\n    auto us = duration_cast<microseconds>(seconds(timeout2.tv_sec)) +\n        microseconds(timeout2.tv_usec);\n    return now + us;\n  };\n\n  auto expire = getTimerExpireTime(*timeout);\n\n  TimerUserData* td = static_cast<TimerUserData*>(event.getUserData());\n  VLOG(6) << \"addTimerEvent this=\" << this << \" event=\" << &event << \" td=\"\n          << td << \" changed_=\" << timerChanged_ << \" u=\" << timeout->tv_usec;\n  if (td) {\n    CHECK_EQ(event.getFreeFunction(), timerUserDataFreeFunction);\n    if (td->iter == timers_.end()) {\n      td->iter = timers_.emplace(expire, &event);\n    } else {\n      auto ex = timers_.extract(td->iter);\n      ex.key() = expire;\n      td->iter = timers_.insert(std::move(ex));\n    }\n  } else {\n    auto it = timers_.emplace(expire, &event);\n    td = new TimerUserData();\n    td->iter = it;\n    VLOG(6) << \"addTimerEvent::alloc \" << td << \" event=\" << &event;\n    event.setUserData(td, timerUserDataFreeFunction);\n  }\n  timerChanged_ |= td->iter == timers_.begin();\n}\n\nvoid IoUringBackend::removeTimerEvent(Event& event) {\n  TimerUserData* td = static_cast<TimerUserData*>(event.getUserData());\n  VLOG(6) << \"removeTimerEvent this=\" << this << \" event=\" << &event\n          << \" td=\" << td;\n  CHECK(td && event.getFreeFunction() == timerUserDataFreeFunction);\n  timerChanged_ |= td->iter == timers_.begin();\n  timers_.erase(td->iter);\n  td->iter = timers_.end();\n  event.setUserData(nullptr, nullptr);\n  delete td;\n}\n\nsize_t IoUringBackend::processTimers() {\n  VLOG(3) << \"IoUringBackend::processTimers \" << timers_.size();\n  size_t ret = 0;\n  uint64_t data = 0;\n  // this can fail with but it is OK since the fd\n  // will still be readable\n  folly::readNoInt(timerFd_, &data, sizeof(data));\n\n  auto now = std::chrono::steady_clock::now();\n  while (true) {\n    auto it = timers_.begin();\n    if (it == timers_.end() || now < it->first) {\n      break;\n    }\n    timerChanged_ = true;\n    Event* e = it->second;\n    TimerUserData* td = static_cast<TimerUserData*>(e->getUserData());\n    VLOG(5) << \"processTimer \" << e << \" td=\" << td;\n    CHECK(td && e->getFreeFunction() == timerUserDataFreeFunction);\n    td->iter = timers_.end();\n    timers_.erase(it);\n    auto* ev = e->getEvent();\n    ev->ev_res = EV_TIMEOUT;\n    event_ref_flags(ev).get() = EVLIST_INIT;\n    // might change the lists\n    (*event_ref_callback(ev))(\n        static_cast<int>(ev->ev_fd), ev->ev_res, event_ref_arg(ev));\n    ++ret;\n  }\n\n  VLOG(3) << \"IoUringBackend::processTimers done, changed= \" << timerChanged_\n          << \" count=\" << ret;\n  return ret;\n}\n\nvoid IoUringBackend::addSignalEvent(Event& event) {\n  auto* ev = event.getEvent();\n  signals_[ev->ev_fd].insert(&event);\n\n  // we pass the write fd for notifications\n  getSignalRegistry().setNotifyFd(ev->ev_fd, signalFds_.writeFd());\n}\n\nvoid IoUringBackend::removeSignalEvent(Event& event) {\n  auto* ev = event.getEvent();\n  auto iter = signals_.find(ev->ev_fd);\n  if (iter != signals_.end()) {\n    iter->second.erase(&event);\n    if (iter->second.empty()) {\n      signals_.erase(iter);\n      getSignalRegistry().setNotifyFd(ev->ev_fd, -1);\n    }\n  }\n}\n\nsize_t IoUringBackend::processSignals() {\n  size_t ret = 0;\n  static constexpr auto kNumEntries = NSIG * 2;\n  static_assert(\n      NSIG < 256, \"Use a different data type to cover all the signal values\");\n  std::array<bool, NSIG> processed{};\n  std::array<uint8_t, kNumEntries> signals;\n\n  ssize_t num =\n      folly::readNoInt(signalFds_.readFd(), signals.data(), signals.size());\n  for (ssize_t i = 0; i < num; i++) {\n    int signum = static_cast<int>(signals[i]);\n    if ((signum >= 0) && (signum < static_cast<int>(processed.size())) &&\n        !processed[signum]) {\n      processed[signum] = true;\n      auto iter = signals_.find(signum);\n      if (iter != signals_.end()) {\n        auto& set = iter->second;\n        for (auto& event : set) {\n          auto* ev = event->getEvent();\n          ev->ev_res = 0;\n          event_ref_flags(ev) |= EVLIST_ACTIVE;\n          (*event_ref_callback(ev))(\n              static_cast<int>(ev->ev_fd), ev->ev_res, event_ref_arg(ev));\n          event_ref_flags(ev) &= ~EVLIST_ACTIVE;\n        }\n      }\n    }\n  }\n  // add the signal fd(s) back\n  addSignalFds();\n  return ret;\n}\n\nIoUringBackend::IoSqe* IoUringBackend::allocIoSqe() {\n  // try to allocate from the pool first\n  if (!freeList_.empty()) {\n    auto* ret = &freeList_.front();\n    freeList_.pop_front();\n    return ret;\n  }\n\n  // alloc a new IoSqe\n  return allocNewIoSqe();\n}\n\nvoid IoUringBackend::releaseIoSqe(IoUringBackend::IoSqe* aioIoSqe) noexcept {\n  // unregister the file dsecriptor record\n  if (aioIoSqe->fdRecord_) {\n    unregisterFd(aioIoSqe->fdRecord_);\n    aioIoSqe->fdRecord_ = nullptr;\n  }\n\n  if (FOLLY_LIKELY(aioIoSqe->poolAlloc_)) {\n    aioIoSqe->event_ = nullptr;\n    freeList_.push_front(*aioIoSqe);\n  } else if (!aioIoSqe->persist_) {\n    delete aioIoSqe;\n  }\n}\n\nvoid IoUringBackend::IoSqe::release() noexcept {\n  backend_->releaseIoSqe(this);\n}\n\nvoid IoUringBackend::processPollIo(\n    IoSqe* ioSqe, int res, uint32_t flags) noexcept {\n  auto* ev = ioSqe->event_ ? (ioSqe->event_->getEvent()) : nullptr;\n  if (ev) {\n    if (flags & IORING_CQE_F_MORE) {\n      ioSqe->useCount_++;\n      SCOPE_EXIT {\n        ioSqe->useCount_--;\n      };\n    }\n\n    // if this is not a persistent event\n    // remove the EVLIST_INSERTED flags\n    if (!(ev->ev_events & EV_PERSIST)) {\n      event_ref_flags(ev) &= ~EVLIST_INSERTED;\n    }\n\n    if (event_ref_flags(ev) & EVLIST_INTERNAL) {\n      DCHECK_GT(numInternalEvents_, 0);\n      --numInternalEvents_;\n    }\n\n    // add it to the active list\n    event_ref_flags(ev) |= EVLIST_ACTIVE;\n\n    // only clamp upper bound, as no error codes are smaller than short min\n    ev->ev_res = static_cast<short>(\n        std::min<int64_t>(res, std::numeric_limits<short>::max()));\n\n    ioSqe->res_ = res;\n    ioSqe->cqeFlags_ = flags;\n    activeEvents_.push_back(*ioSqe);\n  } else {\n    releaseIoSqe(ioSqe);\n  }\n}\n\nsize_t IoUringBackend::processActiveEvents() {\n  size_t ret = 0;\n  IoSqe* ioSqe;\n\n  while (!activeEvents_.empty() && !loopBreak_) {\n    bool release = true;\n    ioSqe = &activeEvents_.front();\n    activeEvents_.pop_front();\n    ret++;\n    auto* event = ioSqe->event_;\n    auto* ev = event ? event->getEvent() : nullptr;\n    if (ev) {\n      // remove it from the active list\n      event_ref_flags(ev) &= ~EVLIST_ACTIVE;\n      bool inserted = (event_ref_flags(ev) & EVLIST_INSERTED);\n      // prevent the callback from freeing the aioIoSqe\n      ioSqe->useCount_++;\n      // adjust the ev_res for the poll case\n      ev->ev_res = getPollEvents(ioSqe->res_, ev->ev_events);\n      // handle spurious poll events that return 0\n      // this can happen during high load on process startup\n      if (ev->ev_res) {\n        (*event_ref_callback(ev))(\n            static_cast<int>(ev->ev_fd), ev->ev_res, event_ref_arg(ev));\n      }\n      // get the event again\n      event = ioSqe->event_;\n      ev = event ? event->getEvent() : nullptr;\n      if (ev && inserted && event_ref_flags(ev) & EVLIST_INSERTED &&\n          !shuttingDown_) {\n        release = false;\n        eb_event_modify_inserted(*event, ioSqe);\n      }\n      ioSqe->useCount_--;\n    } else {\n      ioSqe->processActive();\n    }\n    if (release) {\n      releaseIoSqe(ioSqe);\n    }\n  }\n\n  return ret;\n}\n\nvoid IoUringBackend::submitOutstanding() {\n  delayedInit();\n  prepList(submitList_);\n  submitEager();\n}\n\nunsigned int IoUringBackend::processCompleted() {\n  return internalProcessCqe(\n      options_.maxGet, InternalProcessCqeMode::AVAILABLE_ONLY);\n}\n\nsize_t IoUringBackend::loopPoll() {\n  delayedInit();\n  prepList(submitList_);\n  size_t ret = getActiveEvents(WaitForEventsMode::DONT_WAIT);\n  processActiveEvents();\n  return ret;\n}\n\nvoid IoUringBackend::dCheckSubmitTid() {\n  if (!kIsDebug) {\n    // lets only check this in DEBUG\n    return;\n  }\n  if (!usingDeferTaskrun_) {\n    // only care in defer_taskrun mode\n    return;\n  }\n  if (!submitTid_) {\n    submitTid_ = std::this_thread::get_id();\n  } else {\n    DCHECK_EQ(*submitTid_, std::this_thread::get_id())\n        << \"Cannot change submit/reap threads with DeferTaskrun\";\n  }\n}\n\nvoid IoUringBackend::initSubmissionLinked() {\n  // we need to call the init before adding the timer fd\n  // so we avoid a deadlock - waiting for the queue to be drained\n  if (options_.registeredFds > 0) {\n    // now init the file registry\n    // if this fails, we still continue since we\n    // can run without registered fds\n    fdRegistry_.init();\n  }\n\n  if (options_.initialProvidedBuffersCount) {\n    try {\n      IoUringProvidedBufferRing::Options options = {\n          .gid = nextBufferProviderGid(),\n          .bufferCount =\n              static_cast<uint32_t>(options_.initialProvidedBuffersCount),\n          .bufferSize =\n              static_cast<uint32_t>(options_.initialProvidedBuffersEachSize),\n          .useHugePages = options_.useHugePages,\n          .useIncrementalBuffers = options_.enableIncrementalBuffers,\n      };\n      for (size_t i = 0; i < options_.providedBufRings; i++) {\n        bufferProviders_.push_back(\n            IoUringProvidedBufferRing::create(this->ioRingPtr(), options));\n        options.gid = nextBufferProviderGid();\n      }\n    } catch (const IoUringProvidedBufferRing::LibUringCallError& ex) {\n      LOG(ERROR) << folly::to<std::string>(\n          \"failed to make provided buffer ring, buffer count: \",\n          options_.initialProvidedBuffersCount,\n          \", buffer size: \",\n          options_.initialProvidedBuffersEachSize);\n      throw NotAvailable(ex.what());\n    }\n  }\n}\n\nbool IoUringBackend::createZcBufferPool() {\n  if (zcBufferPool_) {\n    LOG(WARNING) << \"Buffer pool already exists\";\n    return false;\n  }\n  IoUringZeroCopyBufferPool::Params params = {\n      .ring = this->ioRingPtr(),\n      .numPages = static_cast<size_t>(options_.zcRxNumPages),\n      .pageSize = kZeroCopyPageSize,\n      .rqEntries = static_cast<uint32_t>(options_.zcRxRefillEntries),\n      .ifindex = static_cast<uint32_t>(options_.zcRxIfindex),\n      .queueId = static_cast<uint16_t>(options_.zcRxQueueId),\n  };\n  zcBufferPool_ = IoUringZeroCopyBufferPool::create(params);\n  return zcBufferPool_ != nullptr;\n}\n\nbool IoUringBackend::importZcBufferPool(\n    IoUringZeroCopyBufferPool::ExportHandle handle) {\n  if (zcBufferPool_) {\n    LOG(WARNING) << \"Buffer pool already exists\";\n    return false;\n  }\n  zcBufferPool_ = IoUringZeroCopyBufferPool::importHandle(\n      std::move(handle), this->ioRingPtr());\n  return zcBufferPool_ != nullptr;\n}\n\nIoUringZeroCopyBufferPool::ExportHandle IoUringBackend::exportZcBufferPool() {\n  CHECK(zcBufferPool_) << \"No buffer pool to export\";\n  return zcBufferPool_->exportHandle();\n}\n\nvoid IoUringBackend::delayedInit() {\n  dCheckSubmitTid();\n\n  if (FOLLY_LIKELY(!needsDelayedInit_)) {\n    return;\n  }\n\n  needsDelayedInit_ = false;\n\n  if (usingDeferTaskrun_) {\n    auto ret = io_uring_enable_rings(&ioRing_);\n    if (ret) {\n      LOG(ERROR) << \"io_uring_enable_rings gave \" << folly::errnoStr(-ret);\n    }\n    initSubmissionLinked();\n  }\n\n  if (useReqBatching()) {\n    ::io_uring_set_iowait(&ioRing_, false);\n  }\n\n  if (options_.registerRingFd) {\n    // registering just has some perf impact, so no need to fall back\n    if (io_uring_register_ring_fd(&ioRing_) < 0) {\n      LOG(ERROR) << \"unable to register io_uring ring fd\";\n    }\n  }\n\n  if (options_.arenaIndex > 0) {\n    int ret = ::io_uring_register_buffers(&ioRing_, &options_.arenaRegion, 1);\n    if (ret < 0) {\n      throw NotAvailable(\n          fmt::format(\n              \"io_uring_register_buffers failed: {}\", folly::errnoStr(-ret)));\n    }\n  }\n\n  if (!addTimerFd() || !addSignalFds()) {\n    cleanup();\n    throw NotAvailable(\"io_uring_submit error\");\n  }\n}\n\nint IoUringBackend::eb_event_base_loop(int flags) {\n  delayedInit();\n\n  const auto waitForEvents = (flags & EVLOOP_NONBLOCK)\n      ? WaitForEventsMode::DONT_WAIT\n      : WaitForEventsMode::WAIT;\n\n  bool hadEvents = true;\n  for (bool done = false; !done;) {\n    scheduleTimeout();\n\n    // check if we need to break here\n    if (loopBreak_) {\n      loopBreak_ = false;\n      return 0;\n    }\n\n    prepList(submitList_);\n\n    if (numInternalEvents_ == numInsertedEvents_ && timers_.empty() &&\n        signals_.empty()) {\n      VLOG(2) << \"IoUringBackend::eb_event_base_loop nothing to do\";\n      return 1;\n    }\n\n    uint64_t call_time = 0;\n    if (eb_poll_loop_pre_hook) {\n      eb_poll_loop_pre_hook(&call_time);\n    }\n    if (pollLoopHook_.preLoopHook) {\n      pollLoopHook_.preLoopHook(pollLoopHook_.hookCtx);\n    }\n\n    // do not wait for events if EVLOOP_NONBLOCK is set\n    size_t processedEvents = getActiveEvents(waitForEvents);\n\n    if (eb_poll_loop_post_hook) {\n      eb_poll_loop_post_hook(call_time, static_cast<int>(processedEvents));\n    }\n    if (pollLoopHook_.postLoopHook) {\n      pollLoopHook_.postLoopHook(\n          pollLoopHook_.hookCtx, static_cast<int>(processedEvents));\n    }\n\n    size_t numProcessedTimers = 0;\n\n    // save the processTimers_\n    // this means we've received a notification\n    // and we need to add the timer fd back\n    bool processTimersFlag = processTimers_;\n    if (processTimers_ && !loopBreak_) {\n      numProcessedTimers = processTimers();\n      processTimers_ = false;\n    }\n\n    size_t numProcessedSignals = 0;\n\n    if (processSignals_ && !loopBreak_) {\n      numProcessedSignals = processSignals();\n      processSignals_ = false;\n    }\n\n    if (!activeEvents_.empty() && !loopBreak_) {\n      processActiveEvents();\n      if (flags & EVLOOP_ONCE) {\n        done = true;\n      }\n    } else if (flags & EVLOOP_NONBLOCK) {\n      if (signals_.empty()) {\n        done = true;\n      }\n    }\n\n    hadEvents = numProcessedTimers || numProcessedSignals || processedEvents;\n    if (hadEvents && (flags & EVLOOP_ONCE)) {\n      done = true;\n    }\n\n    VLOG(2) << \"IoUringBackend::eb_event_base_loop processedEvents=\"\n            << processedEvents << \" numProcessedSignals=\" << numProcessedSignals\n            << \" numProcessedTimers=\" << numProcessedTimers << \" done=\" << done;\n\n    if (processTimersFlag) {\n      addTimerFd();\n    }\n  }\n\n  return hadEvents ? 0 : 2;\n}\n\nint IoUringBackend::eb_event_base_loopbreak() {\n  loopBreak_ = true;\n\n  return 0;\n}\n\nint IoUringBackend::eb_event_add(Event& event, const timeval* timeout) {\n  VLOG(4) << \"Add event \" << &event;\n  auto* ev = event.getEvent();\n  CHECK(ev);\n  CHECK(!(event_ref_flags(ev) & ~EVLIST_ALL));\n  // we do not support read/write timeouts\n  if (timeout) {\n    event_ref_flags(ev) |= EVLIST_TIMEOUT;\n    addTimerEvent(event, timeout);\n    return 0;\n  }\n\n  if (ev->ev_events & EV_SIGNAL) {\n    event_ref_flags(ev) |= EVLIST_INSERTED;\n    addSignalEvent(event);\n    return 0;\n  }\n\n  if ((ev->ev_events & (EV_READ | EV_WRITE)) &&\n      !(event_ref_flags(ev) & (EVLIST_INSERTED | EVLIST_ACTIVE))) {\n    auto* ioSqe = allocIoSqe();\n    CHECK(ioSqe);\n    ioSqe->event_ = &event;\n    ioSqe->setEventBase(event.eb_ev_base());\n\n    // just append it\n    submitList_.push_back(*ioSqe);\n    if (event_ref_flags(ev) & EVLIST_INTERNAL) {\n      numInternalEvents_++;\n    }\n    event_ref_flags(ev) |= EVLIST_INSERTED;\n    event.setUserData(ioSqe);\n  }\n\n  return 0;\n}\n\nint IoUringBackend::eb_event_del(Event& event) {\n  VLOG(4) << \"Del event \" << &event;\n  if (!event.eb_ev_base()) {\n    return -1;\n  }\n\n  auto* ev = event.getEvent();\n  if (event_ref_flags(ev) & EVLIST_TIMEOUT) {\n    event_ref_flags(ev) &= ~EVLIST_TIMEOUT;\n    removeTimerEvent(event);\n    return 1;\n  }\n\n  if (!(event_ref_flags(ev) & (EVLIST_ACTIVE | EVLIST_INSERTED))) {\n    return -1;\n  }\n\n  if (ev->ev_events & EV_SIGNAL) {\n    event_ref_flags(ev) &= ~(EVLIST_INSERTED | EVLIST_ACTIVE);\n    removeSignalEvent(event);\n    return 0;\n  }\n\n  auto* ioSqe = reinterpret_cast<IoSqe*>(event.getUserData());\n  bool wasLinked = ioSqe->is_linked();\n  ioSqe->resetEvent();\n\n  // if the event is on the active list, we just clear the flags\n  // and reset the event_ ptr\n  if (event_ref_flags(ev) & EVLIST_ACTIVE) {\n    event_ref_flags(ev) &= ~EVLIST_ACTIVE;\n  }\n\n  if (event_ref_flags(ev) & EVLIST_INSERTED) {\n    event_ref_flags(ev) &= ~EVLIST_INSERTED;\n\n    // not in use  - we can cancel it\n    if (!ioSqe->useCount_ && !wasLinked) {\n      // io_cancel will attempt to cancel the event. the result is\n      // EINVAL - usually the event has already been delivered\n      // EINPROGRESS - cancellation in progress\n      // EFAULT - bad ctx\n      int ret = cancelOne(ioSqe);\n      if (ret < 0) {\n        // release the ioSqe\n        releaseIoSqe(ioSqe);\n      }\n    } else {\n      if (!ioSqe->useCount_) {\n        releaseIoSqe(ioSqe);\n      }\n    }\n\n    if (event_ref_flags(ev) & EVLIST_INTERNAL) {\n      DCHECK_GT(numInternalEvents_, 0);\n      numInternalEvents_--;\n    }\n\n    return 0;\n  } else {\n    // we can have an EVLIST_ACTIVE event\n    // which does not have the EVLIST_INSERTED flag set\n    // so we need to release it here\n    releaseIoSqe(ioSqe);\n  }\n\n  return -1;\n}\n\nint IoUringBackend::eb_event_modify_inserted(Event& event, IoSqe* ioSqe) {\n  VLOG(4) << \"Modify event \" << &event;\n  // unlink and append\n  ioSqe->unlink();\n  if (event_ref_flags(event.getEvent()) & EVLIST_INTERNAL) {\n    numInternalEvents_++;\n  }\n  submitList_.push_back(*ioSqe);\n  event.setUserData(ioSqe);\n\n  return 0;\n}\n\nvoid IoUringBackend::submitNextLoop(IoSqeBase& ioSqe) noexcept {\n  submitList_.push_back(ioSqe);\n}\n\nvoid IoUringBackend::submitImmediateIoSqe(IoSqeBase& ioSqe) {\n  if (options_.flags &\n      (Options::Flags::POLL_SQ | Options::Flags::POLL_SQ_IMMEDIATE_IO)) {\n    submitNow(ioSqe);\n  } else {\n    submitList_.push_back(ioSqe);\n  }\n}\n\nint IoUringBackend::submitOne() {\n  return submitBusyCheck(1, WaitForEventsMode::DONT_WAIT);\n}\n\nvoid IoUringBackend::submitNow(IoSqeBase& ioSqe) {\n  internalSubmit(ioSqe);\n  submitBusyCheck(waitingToSubmit_, WaitForEventsMode::DONT_WAIT);\n}\n\nvoid IoUringBackend::internalSubmit(IoSqeBase& ioSqe) noexcept {\n  auto* sqe = getSqe();\n  setSubmitting();\n  ioSqe.internalSubmit(sqe);\n  if (ioSqe.type() == IoSqeBase::Type::Write) {\n    numSendEvents_++;\n  }\n  doneSubmitting();\n}\n\nvoid IoUringBackend::submitSoon(IoSqeBase& ioSqe) noexcept {\n  internalSubmit(ioSqe);\n  if (waitingToSubmit_ >= options_.maxSubmit) {\n    submitBusyCheck(waitingToSubmit_, WaitForEventsMode::DONT_WAIT);\n  }\n}\n\nvoid IoUringBackend::cancel(IoSqeBase* ioSqe) {\n  bool skip = false;\n  ioSqe->markCancelled();\n  auto* sqe = getUntrackedSqe();\n  ::io_uring_prep_cancel64(sqe, reinterpret_cast<uint64_t>(ioSqe), 0);\n  ::io_uring_sqe_set_data(sqe, nullptr);\n  if (params_.features & IORING_FEAT_CQE_SKIP) {\n    sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;\n    skip = true;\n  }\n  VLOG(4) << \"Cancel \" << ioSqe << \" skip=\" << skip;\n}\n\nint IoUringBackend::cancelOne(IoSqe* ioSqe) {\n  auto* rentry = static_cast<IoSqe*>(allocIoSqe());\n  if (!rentry) {\n    return 0;\n  }\n\n  auto* sqe = getSqe();\n  rentry->prepCancel(sqe, ioSqe); // prev entry\n\n  int ret = submitBusyCheck(waitingToSubmit_, WaitForEventsMode::DONT_WAIT);\n\n  if (ret < 0) {\n    // release the sqe\n    releaseIoSqe(rentry);\n  }\n\n  return ret;\n}\n\nint IoUringBackend::doInnerWait(io_uring_cqe*& cqe) noexcept {\n  if (waitingToSubmit_) {\n    submitBusyCheck(waitingToSubmit_, WaitForEventsMode::WAIT);\n    return ::io_uring_peek_cqe(&ioRing_, &cqe);\n  } else if (useReqBatching()) {\n    struct __kernel_timespec timeout;\n    timeout.tv_sec = 0;\n    timeout.tv_nsec = options_.timeout.count() * 1000;\n    return ::io_uring_wait_cqes(\n        &ioRing_, &cqe, options_.batchSize, &timeout, nullptr);\n  } else {\n    return ::io_uring_wait_cqe(&ioRing_, &cqe);\n  }\n}\n\nint IoUringBackend::doWait(io_uring_cqe*& cqe) {\n  if (kIsDebug && VLOG_IS_ON(1)) {\n    auto start = std::chrono::steady_clock::now();\n    unsigned was = ::io_uring_cq_ready(&ioRing_);\n    auto was_submit = waitingToSubmit_;\n    int ret = doInnerWait(cqe);\n    auto end = std::chrono::steady_clock::now();\n    auto micros =\n        std::chrono::duration_cast<std::chrono::microseconds>(end - start);\n    if (micros.count()) {\n      VLOG(1) << \"wait took \" << micros.count()\n              << \"us have=\" << ::io_uring_cq_ready(&ioRing_) << \" was=\" << was\n              << \" submit_len=\" << was_submit;\n    }\n    return ret;\n  } else {\n    return doInnerWait(cqe);\n  }\n}\n\nint IoUringBackend::doPeek(io_uring_cqe*& cqe) noexcept {\n  if (usingDeferTaskrun_) {\n    return ::io_uring_get_events(&ioRing_);\n  }\n  return ::io_uring_peek_cqe(&ioRing_, &cqe);\n}\n\nsize_t IoUringBackend::getActiveEvents(WaitForEventsMode waitForEvents) {\n  io_uring_cqe* cqe = nullptr;\n\n  if (kIsDebug && gettingEvents_) {\n    throw std::runtime_error(\"getting events is not reentrant\");\n  }\n  gettingEvents_ = true;\n\n  SCOPE_EXIT {\n    gettingEvents_ = false;\n  };\n\n  int ret;\n  // we can be called from the submitList() method\n  // or with non blocking flags\n  do {\n    if (waitForEvents == WaitForEventsMode::WAIT) {\n      // if polling the CQ, busy wait for one entry\n      if (options_.flags & Options::Flags::POLL_CQ) {\n        if (waitingToSubmit_) {\n          submitBusyCheck(waitingToSubmit_, WaitForEventsMode::DONT_WAIT);\n        }\n        do {\n          ret = ::io_uring_peek_cqe(&ioRing_, &cqe);\n          asm_volatile_pause();\n          // call the loop callback if installed\n          // we call it every time we poll for a CQE\n          // regardless of the io_uring_peek_cqe result\n          if (cqPollLoopCallback_) {\n            cqPollLoopCallback_();\n          }\n        } while (ret);\n      } else {\n        ret = doWait(cqe);\n      }\n    } else {\n      if (waitingToSubmit_) {\n        ret = submitBusyCheck(waitingToSubmit_, WaitForEventsMode::DONT_WAIT);\n      } else {\n        ret = doPeek(cqe);\n      }\n    }\n  } while (ret == -EINTR);\n\n  if (ret == -EBADR) {\n    // cannot recover from droped CQE\n    folly::terminate_with<std::runtime_error>(\"BADR\");\n  } else if (ret == -EAGAIN) {\n    return 0;\n  } else if (ret == -ETIME) {\n    if (cqe == nullptr) {\n      return 0;\n    }\n  } else if (ret < 0) {\n    LOG(ERROR) << \"wait_cqe error: \" << ret;\n    return 0;\n  }\n\n  return internalProcessCqe(options_.maxGet, InternalProcessCqeMode::NORMAL);\n}\n\nunsigned int IoUringBackend::internalProcessCqe(\n    unsigned int maxGet, InternalProcessCqeMode mode) noexcept {\n  io_uring_cqe* cqe;\n\n  unsigned int count_more = 0;\n  unsigned int count = 0;\n  unsigned int count_send = 0;\n\n  checkLogOverflow(&ioRing_);\n  do {\n    unsigned int head;\n    unsigned int loop_count = 0;\n    io_uring_for_each_cqe(&ioRing_, head, cqe) {\n      loop_count++;\n      if (cqe->flags & IORING_CQE_F_MORE) {\n        count_more++;\n      }\n      IoSqeBase* sqe = reinterpret_cast<IoSqeBase*>(cqe->user_data);\n      if (cqe->user_data) {\n        count++;\n        if (sqe->type() == IoSqeBase::Type::Write) {\n          count_send++;\n        }\n        if (FOLLY_UNLIKELY(mode == InternalProcessCqeMode::CANCEL_ALL)) {\n          sqe->markCancelled();\n        }\n        sqe->internalCallback(cqe);\n      } else {\n        // untracked, do not increment count\n      }\n      if (count >= options_.maxGet) {\n        break;\n      }\n    }\n    if (!loop_count) {\n      break;\n    }\n    io_uring_cq_advance(&ioRing_, loop_count);\n    if (count >= maxGet) {\n      break;\n    }\n\n    // io_uring_peek_cqe will check for any overflows and copy them to the cq\n    // ring.\n    if (mode != InternalProcessCqeMode::AVAILABLE_ONLY) {\n      int ret = ::io_uring_peek_cqe(&ioRing_, &cqe);\n      if (ret == -EBADR) {\n        // cannot recover from droped CQE\n        folly::terminate_with<std::runtime_error>(\"BADR\");\n      } else if (ret) {\n        break;\n      }\n    }\n  } while (true);\n  numInsertedEvents_ -= (count - count_more);\n  numSendEvents_ -= count_send;\n  FOLLY_SDT(\n      folly,\n      folly_io_uring_backend_post_process_all_cqes,\n      count,\n      count_more,\n      count_send,\n      numInsertedEvents_,\n      numSendEvents_);\n  return count;\n}\n\nint IoUringBackend::submitEager() {\n  int res;\n  DCHECK(!isSubmitting()) << \"mid processing a submit, cannot submit\";\n  do {\n    res = ::io_uring_submit(&ioRing_);\n  } while (res == -EINTR);\n  VLOG(2) << \"IoUringBackend::submitEager() \" << waitingToSubmit_;\n  if (res >= 0) {\n    DCHECK(static_cast<int>(waitingToSubmit_) >= res);\n    waitingToSubmit_ -= res;\n  }\n  return res;\n}\n\nint IoUringBackend::submitBusyCheck(\n    int num, WaitForEventsMode waitForEvents) noexcept {\n  int i = 0;\n  int res;\n  DCHECK(!isSubmitting()) << \"mid processing a submit, cannot submit\";\n  while (i < num) {\n    VLOG(2) << \"IoUringBackend::submit() \" << waitingToSubmit_;\n\n    if (waitForEvents == WaitForEventsMode::WAIT) {\n      if (options_.flags & Options::Flags::POLL_CQ) {\n        res = ::io_uring_submit(&ioRing_);\n      } else {\n        if (useReqBatching()) {\n          io_uring_cqe* cqe;\n          struct __kernel_timespec timeout;\n          timeout.tv_sec = 0;\n          timeout.tv_nsec = options_.timeout.count() * 1000;\n          res = ::io_uring_submit_and_wait_timeout(\n              &ioRing_,\n              &cqe,\n              options_.batchSize + numSendEvents_,\n              &timeout,\n              nullptr);\n          FOLLY_SDT(\n              folly,\n              folly_io_uring_backend_pre_submit_and_wait_timeout,\n              options_.timeout,\n              options_.batchSize,\n              numSendEvents_,\n              res);\n        } else {\n          res = ::io_uring_submit_and_wait(&ioRing_, 1);\n        }\n        if (res >= 0) {\n          // no more waiting\n          waitForEvents = WaitForEventsMode::DONT_WAIT;\n        }\n      }\n    } else {\n      if (usingDeferTaskrun_) {\n        // usingDeferTaskrun_ implies SUBMIT_ALL, and we definitely\n        // want to do get_events() to process outstanding work\n        res = ::io_uring_submit_and_get_events(&ioRing_);\n        if (res >= 0) {\n          // this is ok since we are using SUBMIT_ALL\n          i = waitingToSubmit_;\n          break;\n        }\n      } else {\n        res = ::io_uring_submit(&ioRing_);\n      }\n    }\n\n    if (res < 0) {\n      // continue if interrupted\n      if (res == -EINTR || errno == EINTR) {\n        continue;\n      }\n      CHECK_NE(res, -EBADR);\n      if (res == -EEXIST) {\n        FB_LOG_EVERY_MS(ERROR, 1000)\n            << \"Received -EEXIST, likely calling get_events/submit \"\n            << \" from the wrong thread with DeferTaskrun enabled\";\n      }\n\n      return res;\n    }\n\n    // we do not have any other entries to submit\n    if (res == 0) {\n      break;\n    }\n\n    i += res;\n\n    // if polling the CQ, busy wait for one entry\n    if (waitForEvents == WaitForEventsMode::WAIT &&\n        options_.flags & Options::Flags::POLL_CQ && i == num) {\n      io_uring_cqe* cqe = nullptr;\n      while (!cqe) {\n        ::io_uring_peek_cqe(&ioRing_, &cqe);\n      }\n    }\n  }\n\n  DCHECK(static_cast<int>(waitingToSubmit_) >= i);\n  waitingToSubmit_ -= i;\n  return num;\n}\n\nsize_t IoUringBackend::prepList(IoSqeBaseList& ioSqes) {\n  int i = 0;\n\n  while (!ioSqes.empty()) {\n    if (static_cast<size_t>(i) == options_.maxSubmit) {\n      int num = submitBusyCheck(i, WaitForEventsMode::DONT_WAIT);\n      CHECK_EQ(num, i);\n      i = 0;\n    }\n\n    auto* entry = &ioSqes.front();\n    ioSqes.pop_front();\n    auto* sqe = getSqe();\n    entry->internalSubmit(sqe);\n    i++;\n  }\n\n  return i;\n}\n\nvoid IoUringBackend::queueRead(\n    int fd, void* buf, unsigned int nbytes, off_t offset, FileOpCallback&& cb) {\n  iovec iov{buf, nbytes};\n  auto* ioSqe = new ReadIoSqe(this, fd, &iov, offset, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueWrite(\n    int fd,\n    const void* buf,\n    unsigned int nbytes,\n    off_t offset,\n    FileOpCallback&& cb) {\n  iovec iov{const_cast<void*>(buf), nbytes};\n  auto* ioSqe = new WriteIoSqe(this, fd, &iov, offset, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueReadv(\n    int fd, Range<const iovec*> iovecs, off_t offset, FileOpCallback&& cb) {\n  auto* ioSqe = new ReadvIoSqe(this, fd, iovecs, offset, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueWritev(\n    int fd, Range<const iovec*> iovecs, off_t offset, FileOpCallback&& cb) {\n  auto* ioSqe = new WritevIoSqe(this, fd, iovecs, offset, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueFsync(int fd, FileOpCallback&& cb) {\n  queueFsync(fd, FSyncFlags::FLAGS_FSYNC, std::move(cb));\n}\n\nvoid IoUringBackend::queueFdatasync(int fd, FileOpCallback&& cb) {\n  queueFsync(fd, FSyncFlags::FLAGS_FDATASYNC, std::move(cb));\n}\n\nvoid IoUringBackend::queueFsync(int fd, FSyncFlags flags, FileOpCallback&& cb) {\n  auto* ioSqe = new FSyncIoSqe(this, fd, flags, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueOpenat(\n    int dfd, const char* path, int flags, mode_t mode, FileOpCallback&& cb) {\n  auto* ioSqe = new FOpenAtIoSqe(this, dfd, path, flags, mode, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueOpenat2(\n    int dfd, const char* path, open_how* how, FileOpCallback&& cb) {\n  auto* ioSqe = new FOpenAt2IoSqe(this, dfd, path, how, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueClose(int fd, FileOpCallback&& cb) {\n  auto* ioSqe = new FCloseIoSqe(this, fd, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueStatx(\n    int dirfd,\n    const char* pathname,\n    int flags,\n    unsigned int mask,\n    struct statx* statxbuf,\n    FileOpCallback&& cb) {\n  auto* ioSqe = new FStatxIoSqe(\n      this, dirfd, pathname, flags, mask, statxbuf, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueRename(\n    const char* oldPath, const char* newPath, FileOpCallback&& cb) {\n  auto* ioSqe = new FRenameIoSqe(this, oldPath, newPath, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueUnlinkat(\n    int dirfd, const char* path, int flags, FileOpCallback&& cb) {\n  auto* ioSqe = new FUnlinkIoSqe(this, dirfd, path, flags, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueUnlink(const char* path, FileOpCallback&& cb) {\n  queueUnlinkat(AT_FDCWD, path, 0, std::move(cb));\n}\n\nvoid IoUringBackend::queueFallocate(\n    int fd, int mode, off_t offset, off_t len, FileOpCallback&& cb) {\n  auto* ioSqe = new FAllocateIoSqe(this, fd, mode, offset, len, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueSendmsg(\n    int fd, const msghdr* msg, unsigned int flags, FileOpCallback&& cb) {\n  auto* ioSqe = new SendmsgIoSqe(this, fd, msg, flags, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueRecvmsg(\n    int fd, msghdr* msg, unsigned int flags, FileOpCallback&& cb) {\n  auto* ioSqe = new RecvmsgIoSqe(this, fd, msg, flags, std::move(cb));\n  ioSqe->backendCb_ = processFileOpCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nvoid IoUringBackend::queueRecvZc(\n    int fd, void* buf, unsigned long nbytes, RecvZcCallback&& cb) {\n  iovec iov = {\n      .iov_base = buf,\n      .iov_len = nbytes,\n  };\n  auto* ioSqe = new RecvzcIoSqe(this, fd, &iov, 0, std::move(cb));\n  ioSqe->backendCb_ = processRecvZcCB;\n\n  submitImmediateIoSqe(*ioSqe);\n}\n\nint IoUringBackend::computeSrcPortForQueueId(\n    const folly::IPAddress& destAddr,\n    uint16_t destPort,\n    uint16_t startPort,\n    uint16_t minPort,\n    uint16_t maxPort) {\n  if (!options_.srcPortQueueId || napiId_ < 0) {\n    return -1;\n  }\n\n  return options_.srcPortQueueId(\n      destAddr,\n      destPort,\n      options_.zcRxQueueId,\n      options_.zcRxIfname.c_str(),\n      startPort,\n      minPort,\n      maxPort);\n}\n\nvoid IoUringBackend::processFileOp(IoSqe* sqe, int res) noexcept {\n  auto* ioSqe = reinterpret_cast<FileOpIoSqe*>(sqe);\n  // save the res\n  ioSqe->res_ = res;\n  activeEvents_.push_back(*ioSqe);\n}\n\nvoid IoUringBackend::processRecvZc(\n    IoSqe* sqe, const io_uring_cqe* cqe) noexcept {\n  RecvzcIoSqe* ioSqe = reinterpret_cast<RecvzcIoSqe*>(sqe);\n  const io_uring_zcrx_cqe* rcqe =\n      reinterpret_cast<const io_uring_zcrx_cqe*>(cqe + 1);\n\n  auto iov = ioSqe->iov_.data();\n  if (cqe->res == 0 && cqe->flags == 0) {\n    CHECK_EQ(static_cast<size_t>(ioSqe->offset_), iov->iov_len);\n    ioSqe->res_ = cqe->res;\n    ioSqe->cb_(static_cast<int>(iov->iov_len));\n    delete ioSqe;\n    return;\n  }\n\n  if (cqe->res < 0) {\n    ioSqe->res_ = cqe->res;\n    ioSqe->cb_(cqe->res);\n    delete ioSqe;\n    return;\n  }\n\n  auto buf = zcBufferPool_->getIoBuf(cqe, rcqe);\n  ::memcpy(\n      reinterpret_cast<char*>(iov->iov_base) + ioSqe->offset_,\n      buf->data(),\n      buf->length());\n  ioSqe->offset_ += cqe->res;\n}\n\nnamespace {\n\nstatic bool doKernelSupportsRecvmsgMultishot() {\n  try {\n    struct S : IoSqeBase {\n      explicit S(IoUringProvidedBufferRing* bp) : bp_(bp) {\n        fd = fileops::open(\"/dev/null\", O_RDONLY);\n        memset(&msg, 0, sizeof(msg));\n      }\n      ~S() override {\n        if (fd >= 0) {\n          fileops::close(fd);\n        }\n      }\n      void processSubmit(io_uring_sqe* sqe) noexcept override {\n        io_uring_prep_recvmsg_multishot(sqe, fd, &msg, 0);\n\n        sqe->buf_group = bp_->gid();\n        sqe->flags |= IOSQE_BUFFER_SELECT;\n      }\n\n      void callback(const io_uring_cqe* cqe) noexcept override {\n        supported = cqe->res != -EINVAL;\n      }\n\n      void callbackCancelled(const io_uring_cqe*) noexcept override {\n        delete this;\n      }\n\n      IoUringProvidedBufferRing* bp_;\n      bool supported = false;\n      msghdr msg;\n      int fd = -1;\n    };\n\n    std::unique_ptr<S> s;\n    IoUringBackend io(\n        std::move(IoUringOptions().setInitialProvidedBuffers(1024, 1)));\n    if (!io.bufferProvider()) {\n      return false;\n    }\n    s = std::make_unique<S>(io.bufferProvider());\n    io.submitNow(*s);\n    io.eb_event_base_loop(EVLOOP_NONBLOCK);\n    bool ret = s->supported;\n    if (s->inFlight()) {\n      LOG(ERROR) << \"Unexpectedly sqe still in flight\";\n      ret = false;\n    }\n    return ret;\n  } catch (IoUringBackend::NotAvailable const&) {\n    return false;\n  }\n}\n\nstatic bool doKernelSupportsDeferTaskrun() {\n  io_uring ring;\n  int ret = io_uring_queue_init(\n      1, &ring, IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN);\n  if (ret == 0) {\n    io_uring_queue_exit(&ring);\n    return true;\n  }\n\n  // fallthrough\n  return false;\n}\n\nstatic bool doKernelSupportsSendZC() {\n  io_uring ring;\n\n  int ret = io_uring_queue_init(4, &ring, 0);\n  if (ret) {\n    LOG(ERROR)\n        << \"doKernelSupportsSendZC: Unexpectedly io_uring_queue_init failed\";\n    return false;\n  }\n  SCOPE_EXIT {\n    io_uring_queue_exit(&ring);\n  };\n\n  auto* sqe = ::io_uring_get_sqe(&ring);\n  if (!sqe) {\n    LOG(ERROR) << \"doKernelSupportsSendZC: no sqe?\";\n    return false;\n  }\n\n  io_uring_prep_sendmsg_zc(sqe, -1, nullptr, 0);\n  ret = ::io_uring_submit(&ring);\n  if (ret != 1) {\n    return false;\n  }\n\n  io_uring_cqe* cqe = nullptr;\n  ret = ::io_uring_wait_cqe(&ring, &cqe);\n  if (ret) {\n    return false;\n  }\n\n  if (!(cqe->flags & IORING_CQE_F_MORE)) {\n    return false; // zerocopy sends two notifications\n  }\n\n  return (cqe->flags & IORING_CQE_F_NOTIF) || (cqe->res == -EBADF);\n}\n\n} // namespace\n\nbool IoUringBackend::kernelSupportsRecvmsgMultishot() {\n  static bool const ret = doKernelSupportsRecvmsgMultishot();\n  return ret;\n}\n\nbool IoUringBackend::kernelSupportsDeferTaskrun() {\n  static bool const ret = doKernelSupportsDeferTaskrun();\n  return ret;\n}\n\nbool IoUringBackend::kernelSupportsSendZC() {\n  static bool const ret = doKernelSupportsSendZC();\n  return ret;\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringBackend.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <chrono>\n#include <map>\n#include <set>\n#include <vector>\n\n#include <boost/intrusive/list.hpp>\n#include <boost/intrusive/slist.hpp>\n\n#include <glog/logging.h>\n\n#include <folly/CPortability.h>\n#include <folly/Conv.h>\n#include <folly/CppAttributes.h>\n#include <folly/ExceptionString.h>\n#include <folly/Function.h>\n#include <folly/IPAddress.h>\n#include <folly/Optional.h>\n#include <folly/Range.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventBaseBackendBase.h>\n#include <folly/io/async/IoUringBase.h>\n#include <folly/io/async/IoUringOptions.h>\n#include <folly/io/async/IoUringProvidedBufferRing.h>\n#include <folly/io/async/IoUringZeroCopyBufferPool.h>\n#include <folly/io/async/Liburing.h>\n#include <folly/portability/Asm.h>\n#include <folly/small_vector.h>\n\n#if __has_include(<poll.h>)\n#include <poll.h>\n#endif\n\n#if FOLLY_HAS_LIBURING\n\n#include <liburing.h> // @manual\n#include <net/if.h>\n\nnamespace folly {\n\nclass IoUringBackend : public EventBaseBackendBase {\n public:\n  class FOLLY_EXPORT NotAvailable : public std::runtime_error {\n   public:\n    using std::runtime_error::runtime_error;\n  };\n\n  // Type aliases for backwards compatibility\n  using Options = IoUringOptions;\n\n  explicit IoUringBackend(Options options);\n  ~IoUringBackend() override;\n  Options const& options() const { return options_; }\n\n  uint32_t getArenaIndex() const { return options_.arenaIndex; }\n\n  bool isWaitingToSubmit() const {\n    return waitingToSubmit_ || !submitList_.empty();\n  }\n  io_uring* ioRingPtr() { return &ioRing_; }\n  io_uring_params const& params() const { return params_; }\n  bool useReqBatching() const {\n    return options_.timeout.count() > 0 && options_.batchSize > 0;\n  }\n  bool supportAsyncSocket() { return options_.nativeAsyncSocketSupport; }\n\n  int computeSrcPortForQueueId(\n      const folly::IPAddress& destAddr,\n      uint16_t destPort,\n      uint16_t startPort,\n      uint16_t minPort,\n      uint16_t maxPort);\n\n  // from EventBaseBackendBase\n  int getPollableFd() const override { return ioRing_.ring_fd; }\n  int getNapiId() const override { return napiId_; }\n  void queueRecvZc(\n      int fd, void* buf, unsigned long nbytes, RecvZcCallback&& callback)\n      override;\n\n  event_base* getEventBase() override { return nullptr; }\n\n  int eb_event_base_loop(int flags) override;\n  int eb_event_base_loopbreak() override;\n\n  int eb_event_add(Event& event, const timeval* timeout) override;\n  int eb_event_del(Event& event) override;\n\n  bool eb_event_active(Event&, int) override { return false; }\n\n  size_t loopPoll();\n  void submitOutstanding();\n  unsigned int processCompleted();\n\n  // returns true if the current Linux kernel version\n  // supports the io_uring backend\n  static bool isAvailable();\n  static bool kernelSupportsRecvmsgMultishot();\n  static bool kernelSupportsDeferTaskrun();\n  static bool kernelSupportsSendZC();\n\n  IoUringFdRegistrationRecord* registerFd(int fd) noexcept {\n    return fdRegistry_.alloc(fd);\n  }\n\n  bool unregisterFd(IoUringFdRegistrationRecord* rec) {\n    return fdRegistry_.free(rec);\n  }\n\n  // CQ poll mode loop callback\n  using CQPollLoopCallback = folly::Function<void()>;\n\n  void setCQPollLoopCallback(CQPollLoopCallback&& cb) {\n    cqPollLoopCallback_ = std::move(cb);\n  }\n\n  // read/write/fsync/fdatasync file operation callback\n  // int param is the io_uring_cqe res field\n  // i.e. the result of the file operation\n  using FileOpCallback = folly::Function<void(int)>;\n\n  void queueRead(\n      int fd,\n      void* buf,\n      unsigned int nbytes,\n      off_t offset,\n      FileOpCallback&& cb);\n\n  void queueWrite(\n      int fd,\n      const void* buf,\n      unsigned int nbytes,\n      off_t offset,\n      FileOpCallback&& cb);\n\n  void queueReadv(\n      int fd, Range<const iovec*> iovecs, off_t offset, FileOpCallback&& cb);\n\n  void queueWritev(\n      int fd, Range<const iovec*> iovecs, off_t offset, FileOpCallback&& cb);\n\n  // there is no ordering between the prev submitted write\n  // requests and the sync ops\n  // ordering can be achieved by calling queue*sync from one of\n  // the prev write callbacks, once all the write operations\n  // we have to wait for are done\n  void queueFsync(int fd, FileOpCallback&& cb);\n  void queueFdatasync(int fd, FileOpCallback&& cb);\n\n  void queueOpenat(\n      int dfd, const char* path, int flags, mode_t mode, FileOpCallback&& cb);\n\n  void queueOpenat2(\n      int dfd, const char* path, open_how* how, FileOpCallback&& cb);\n\n  void queueClose(int fd, FileOpCallback&& cb);\n\n  void queueStatx(\n      int dirfd,\n      const char* pathname,\n      int flags,\n      unsigned int mask,\n      struct statx* statxbuf,\n      FileOpCallback&& cb);\n\n  void queueRename(\n      const char* oldPath, const char* newPath, FileOpCallback&& cb);\n\n  void queueUnlinkat(\n      int dirfd, const char* path, int flags, FileOpCallback&& cb);\n\n  void queueUnlink(const char* path, FileOpCallback&& cb);\n\n  void queueFallocate(\n      int fd, int mode, off_t offset, off_t len, FileOpCallback&& cb);\n\n  // sendmgs/recvmsg\n  void queueSendmsg(\n      int fd, const msghdr* msg, unsigned int flags, FileOpCallback&& cb);\n\n  void queueRecvmsg(\n      int fd, msghdr* msg, unsigned int flags, FileOpCallback&& cb);\n\n  void submit(IoSqeBase& ioSqe) {\n    // todo verify that the sqe is valid!\n    submitImmediateIoSqe(ioSqe);\n  }\n\n  void submitNextLoop(IoSqeBase& ioSqe) noexcept;\n  void submitSoon(IoSqeBase& ioSqe) noexcept;\n  void submitNow(IoSqeBase& ioSqe);\n  void cancel(IoSqeBase* sqe);\n\n  // built in buffer provider\n  IoUringProvidedBufferRing* bufferProvider() {\n    return bufferProviders_\n        [bufferProviderIdx_++ & (bufferProviders_.size() - 1)]\n            .get();\n  }\n  bool hasBufferProvider() { return !bufferProviders_.empty(); }\n  uint16_t nextBufferProviderGid() { return bufferProviderGidNext_++; }\n  IoUringZeroCopyBufferPool* zcBufferPool() { return zcBufferPool_.get(); }\n  bool createZcBufferPool();\n  bool importZcBufferPool(IoUringZeroCopyBufferPool::ExportHandle handle);\n  IoUringZeroCopyBufferPool::ExportHandle exportZcBufferPool();\n\n protected:\n  enum class WaitForEventsMode { WAIT, DONT_WAIT };\n\n  class SocketPair {\n   public:\n    SocketPair();\n\n    SocketPair(const SocketPair&) = delete;\n    SocketPair& operator=(const SocketPair&) = delete;\n\n    ~SocketPair();\n\n    int readFd() const { return fds_[1]; }\n\n    int writeFd() const { return fds_[0]; }\n\n   private:\n    std::array<int, 2> fds_{{-1, -1}};\n  };\n\n  struct UserData {\n    uintptr_t value;\n    explicit UserData(void* p) noexcept\n        : value{reinterpret_cast<uintptr_t>(p)} {}\n    /* implicit */ operator uint64_t() const noexcept { return value; }\n    /* implicit */ operator void*() const noexcept {\n      return reinterpret_cast<void*>(value);\n    }\n  };\n\n  static uint32_t getPollFlags(short events) {\n    uint32_t ret = 0;\n    if (events & EV_READ) {\n      ret |= POLLIN;\n    }\n\n    if (events & EV_WRITE) {\n      ret |= POLLOUT;\n    }\n\n    return ret;\n  }\n\n  static short getPollEvents(uint32_t flags, short events) {\n    short ret = 0;\n    if (flags & POLLIN) {\n      ret |= EV_READ;\n    }\n\n    if (flags & POLLOUT) {\n      ret |= EV_WRITE;\n    }\n\n    if (flags & (POLLERR | POLLHUP)) {\n      ret |= (EV_READ | EV_WRITE);\n    }\n\n    ret &= events;\n\n    return ret;\n  }\n\n  // timer processing\n  bool addTimerFd();\n  void scheduleTimeout();\n  void scheduleTimeout(const std::chrono::microseconds& us);\n  void addTimerEvent(Event& event, const timeval* timeout);\n  void removeTimerEvent(Event& event);\n  size_t processTimers();\n  void setProcessTimers();\n\n  size_t processActiveEvents();\n\n  struct IoSqe;\n\n  static void processPollIoSqe(\n      IoUringBackend* backend, IoSqe* ioSqe, const io_uring_cqe* cqe);\n  static void processTimerIoSqe(\n      IoUringBackend* backend, IoSqe* /*sqe*/, const io_uring_cqe* /*cqe*/);\n  static void processSignalReadIoSqe(\n      IoUringBackend* backend, IoSqe* /*sqe*/, const io_uring_cqe* /*cqe*/);\n\n  // signal handling\n  void addSignalEvent(Event& event);\n  void removeSignalEvent(Event& event);\n  bool addSignalFds();\n  size_t processSignals();\n  void setProcessSignals();\n\n  void processPollIo(IoSqe* ioSqe, int res, uint32_t flags) noexcept;\n\n  IoSqe* FOLLY_NULLABLE allocIoSqe();\n  void releaseIoSqe(IoSqe* aioIoSqe) noexcept;\n\n  // submit immediate if POLL_SQ | POLL_SQ_IMMEDIATE_IO flags are set\n  void submitImmediateIoSqe(IoSqeBase& ioSqe);\n\n  void internalSubmit(IoSqeBase& ioSqe) noexcept;\n\n  enum class InternalProcessCqeMode {\n    NORMAL, // process existing and any available\n    AVAILABLE_ONLY, // process existing but don't get more\n    CANCEL_ALL, // cancel every sqe\n  };\n  unsigned int internalProcessCqe(\n      unsigned int maxGet, InternalProcessCqeMode mode) noexcept;\n\n  int eb_event_modify_inserted(Event& event, IoSqe* ioSqe);\n\n  struct FdRegistry {\n    FdRegistry() = delete;\n    FdRegistry(io_uring& ioRing, size_t n);\n\n    IoUringFdRegistrationRecord* alloc(int fd) noexcept;\n    bool free(IoUringFdRegistrationRecord* record);\n\n    int init();\n    size_t update();\n\n    bool err_{false};\n    io_uring& ioRing_;\n    std::vector<int> files_;\n    size_t inUse_;\n    std::vector<IoUringFdRegistrationRecord> records_;\n    boost::intrusive::\n        slist<IoUringFdRegistrationRecord, boost::intrusive::cache_last<false>>\n            free_;\n  };\n\n  struct IoSqe : public IoSqeBase {\n    using BackendCb = void(IoUringBackend*, IoSqe*, const io_uring_cqe*);\n    explicit IoSqe(\n        IoUringBackend* backend = nullptr,\n        bool poolAlloc = false,\n        bool persist = false)\n        : backend_(backend), poolAlloc_(poolAlloc), persist_(persist) {}\n\n    void callback(const io_uring_cqe* cqe) noexcept override {\n      backendCb_(backend_, this, cqe);\n    }\n    void callbackCancelled(const io_uring_cqe*) noexcept override { release(); }\n    virtual void release() noexcept;\n\n    IoUringBackend* backend_;\n    BackendCb* backendCb_{nullptr};\n    const bool poolAlloc_;\n    const bool persist_;\n    Event* event_{nullptr};\n    IoUringFdRegistrationRecord* fdRecord_{nullptr};\n    size_t useCount_{0};\n    int res_;\n    uint32_t cqeFlags_;\n\n    FOLLY_ALWAYS_INLINE void resetEvent() {\n      // remove it from the list\n      unlink();\n      setEventBase(nullptr);\n      if (event_) {\n        event_->setUserData(nullptr);\n        event_ = nullptr;\n      }\n    }\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      auto* ev = event_->getEvent();\n      if (ev) {\n        prepPollAdd(sqe, ev->ev_fd, getPollFlags(ev->ev_events));\n      }\n    }\n\n    virtual void processActive() {}\n\n    void prepPollAdd(io_uring_sqe* sqe, int fd, uint32_t events) noexcept {\n      CHECK(sqe);\n      ::io_uring_prep_poll_add(sqe, fd, events);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    void prepRead(\n        io_uring_sqe* sqe,\n        int fd,\n        const iovec* iov,\n        off_t offset,\n        bool registerFd) noexcept {\n      prepUtilFunc(\n          ::io_uring_prep_read,\n          sqe,\n          registerFd,\n          fd,\n          iov->iov_base,\n          static_cast<unsigned int>(iov->iov_len),\n          offset);\n    }\n\n    void prepWrite(\n        io_uring_sqe* sqe,\n        int fd,\n        const iovec* iov,\n        off_t offset,\n        bool registerFd) noexcept {\n      prepUtilFunc(\n          ::io_uring_prep_write,\n          sqe,\n          registerFd,\n          fd,\n          iov->iov_base,\n          static_cast<unsigned int>(iov->iov_len),\n          offset);\n    }\n\n    template <typename Fn, typename... Args>\n    void prepUtilFunc(\n        Fn fn, io_uring_sqe* sqe, bool registerFd, int fd, Args... args) {\n      CHECK(sqe);\n      if (registerFd && !fdRecord_) {\n        fdRecord_ = backend_->registerFd(fd);\n      }\n\n      if (fdRecord_) {\n        fn(sqe, fdRecord_->idx_, std::forward<Args>(args)...);\n        sqe->flags |= IOSQE_FIXED_FILE;\n      } else {\n        fn(sqe, fd, std::forward<Args>(args)...);\n      }\n\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    void prepRecvmsgMultishot(io_uring_sqe* sqe, int fd, msghdr* msg) noexcept {\n      CHECK(sqe);\n      ::io_uring_prep_recvmsg_multishot(sqe, fd, msg, MSG_TRUNC);\n      if (IoUringProvidedBufferRing* bp = backend_->bufferProvider()) {\n        sqe->buf_group = bp->gid();\n        sqe->flags |= IOSQE_BUFFER_SELECT;\n      }\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    FOLLY_ALWAYS_INLINE void prepCancel(io_uring_sqe* sqe, IoSqe* cancel_sqe) {\n      CHECK(sqe);\n      ::io_uring_prep_cancel(sqe, UserData{cancel_sqe}, 0);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n  };\n\n  using IoSqeBaseList = boost::intrusive::\n      list<IoSqeBase, boost::intrusive::constant_time_size<false>>;\n  using IoSqeList = boost::intrusive::\n      list<IoSqe, boost::intrusive::constant_time_size<false>>;\n\n  struct FileOpIoSqe : public IoSqe {\n    FileOpIoSqe(IoUringBackend* backend, int fd, FileOpCallback&& cb)\n        : IoSqe(backend, false), fd_(fd), cb_(std::move(cb)) {}\n\n    void processActive() override { cb_(res_); }\n\n    int fd_{-1};\n\n    FileOpCallback cb_;\n  };\n\n  struct ReadWriteIoSqe : public FileOpIoSqe {\n    ReadWriteIoSqe(\n        IoUringBackend* backend,\n        int fd,\n        const iovec* iov,\n        off_t offset,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, fd, std::move(cb)),\n          iov_(iov, iov + 1),\n          offset_(offset) {}\n\n    ReadWriteIoSqe(\n        IoUringBackend* backend,\n        int fd,\n        Range<const iovec*> iov,\n        off_t offset,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, fd, std::move(cb)), iov_(iov), offset_(offset) {}\n\n    folly::small_vector<iovec> iov_;\n    off_t offset_;\n  };\n\n  struct ReadIoSqe : public ReadWriteIoSqe {\n    using ReadWriteIoSqe::ReadWriteIoSqe;\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      prepRead(sqe, fd_, iov_.data(), offset_, false);\n    }\n  };\n\n  struct WriteIoSqe : public ReadWriteIoSqe {\n    using ReadWriteIoSqe::ReadWriteIoSqe;\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      prepWrite(sqe, fd_, iov_.data(), offset_, false);\n    }\n  };\n\n  struct ReadvIoSqe : public ReadWriteIoSqe {\n    using ReadWriteIoSqe::ReadWriteIoSqe;\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_readv(\n          sqe,\n          fd_,\n          iov_.data(),\n          static_cast<unsigned int>(iov_.size()),\n          offset_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n  };\n\n  struct WritevIoSqe : public ReadWriteIoSqe {\n    using ReadWriteIoSqe::ReadWriteIoSqe;\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_writev(\n          sqe,\n          fd_,\n          iov_.data(),\n          static_cast<unsigned int>(iov_.size()),\n          offset_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n  };\n\n  enum class FSyncFlags {\n    FLAGS_FSYNC = 0,\n    FLAGS_FDATASYNC = 1,\n  };\n\n  struct FSyncIoSqe : public FileOpIoSqe {\n    FSyncIoSqe(\n        IoUringBackend* backend, int fd, FSyncFlags flags, FileOpCallback&& cb)\n        : FileOpIoSqe(backend, fd, std::move(cb)), flags_(flags) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      unsigned int fsyncFlags = 0;\n      switch (flags_) {\n        case FSyncFlags::FLAGS_FSYNC:\n          fsyncFlags = 0;\n          break;\n        case FSyncFlags::FLAGS_FDATASYNC:\n          fsyncFlags = IORING_FSYNC_DATASYNC;\n          break;\n      }\n\n      ::io_uring_prep_fsync(sqe, fd_, fsyncFlags);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    FSyncFlags flags_;\n  };\n\n  struct FOpenAtIoSqe : public FileOpIoSqe {\n    FOpenAtIoSqe(\n        IoUringBackend* backend,\n        int dfd,\n        const char* path,\n        int flags,\n        mode_t mode,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, dfd, std::move(cb)),\n          path_(path),\n          flags_(flags),\n          mode_(mode) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_openat(sqe, fd_, path_.c_str(), flags_, mode_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    std::string path_;\n    int flags_;\n    mode_t mode_;\n  };\n\n  struct FOpenAt2IoSqe : public FileOpIoSqe {\n    FOpenAt2IoSqe(\n        IoUringBackend* backend,\n        int dfd,\n        const char* path,\n        open_how* how,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, dfd, std::move(cb)), path_(path), how_(*how) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_openat2(sqe, fd_, path_.c_str(), &how_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    std::string path_;\n    open_how how_;\n  };\n\n  struct FCloseIoSqe : public FileOpIoSqe {\n    using FileOpIoSqe::FileOpIoSqe;\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_close(sqe, fd_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n  };\n\n  struct FStatxIoSqe : public FileOpIoSqe {\n    FStatxIoSqe(\n        IoUringBackend* backend,\n        int dfd,\n        const char* pathname,\n        int flags,\n        unsigned int mask,\n        struct statx* statxbuf,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, dfd, std::move(cb)),\n          path_(pathname),\n          flags_(flags),\n          mask_(mask),\n          statxbuf_(statxbuf) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_statx(sqe, fd_, path_, flags_, mask_, statxbuf_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    const char* path_;\n    int flags_;\n    unsigned int mask_;\n    struct statx* statxbuf_;\n  };\n\n  struct FRenameIoSqe : public FileOpIoSqe {\n    FRenameIoSqe(\n        IoUringBackend* backend,\n        const char* oldPath,\n        const char* newPath,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, -1, std::move(cb)),\n          oldPath_(oldPath),\n          newPath_(newPath) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_rename(sqe, oldPath_, newPath_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    const char* oldPath_;\n    const char* newPath_;\n  };\n\n  struct FUnlinkIoSqe : public FileOpIoSqe {\n    FUnlinkIoSqe(\n        IoUringBackend* backend,\n        int dirfd,\n        const char* path,\n        int flags,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, dirfd, std::move(cb)),\n          path_(path),\n          flags_(flags) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_unlinkat(sqe, fd_, path_, flags_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    const char* path_;\n    int flags_;\n  };\n\n  struct FAllocateIoSqe : public FileOpIoSqe {\n    FAllocateIoSqe(\n        IoUringBackend* backend,\n        int fd,\n        int mode,\n        off_t offset,\n        off_t len,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, fd, std::move(cb)),\n          mode_(mode),\n          offset_(offset),\n          len_(len) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_fallocate(sqe, fd_, mode_, offset_, len_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    int mode_;\n    off_t offset_;\n    off_t len_;\n  };\n\n  struct SendmsgIoSqe : public FileOpIoSqe {\n    SendmsgIoSqe(\n        IoUringBackend* backend,\n        int fd,\n        const msghdr* msg,\n        unsigned int flags,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, fd, std::move(cb)), msg_(msg), flags_(flags) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_sendmsg(sqe, fd_, msg_, flags_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    const msghdr* msg_;\n    unsigned int flags_;\n  };\n\n  struct RecvmsgIoSqe : public FileOpIoSqe {\n    RecvmsgIoSqe(\n        IoUringBackend* backend,\n        int fd,\n        msghdr* msg,\n        unsigned int flags,\n        FileOpCallback&& cb)\n        : FileOpIoSqe(backend, fd, std::move(cb)), msg_(msg), flags_(flags) {}\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_recvmsg(sqe, fd_, msg_, flags_);\n      ::io_uring_sqe_set_data(sqe, this);\n    }\n\n    msghdr* msg_;\n    unsigned int flags_;\n  };\n\n  struct RecvzcIoSqe : public ReadWriteIoSqe {\n    using ReadWriteIoSqe::ReadWriteIoSqe;\n\n    void processSubmit(io_uring_sqe* sqe) noexcept override {\n      ::io_uring_prep_rw(\n          IORING_OP_RECV_ZC, sqe, fd_, nullptr, iov_.data()->iov_len, 0);\n      ::io_uring_sqe_set_data(sqe, this);\n      sqe->ioprio |= IORING_RECV_MULTISHOT;\n    }\n  };\n\n  size_t getActiveEvents(WaitForEventsMode waitForEvents);\n  size_t prepList(IoSqeBaseList& ioSqes);\n  int submitOne();\n  int cancelOne(IoSqe* ioSqe);\n\n  int submitBusyCheck(int num, WaitForEventsMode waitForEvents) noexcept;\n  int submitEager();\n\n  void queueFsync(int fd, FSyncFlags flags, FileOpCallback&& cb);\n\n  void processFileOp(IoSqe* ioSqe, int res) noexcept;\n\n  void processRecvZc(IoSqe* sqe, const io_uring_cqe* cqe) noexcept;\n\n  static void processFileOpCB(\n      IoUringBackend* backend, IoSqe* ioSqe, const io_uring_cqe* cqe) {\n    backend->processFileOp(ioSqe, cqe->res);\n  }\n\n  static void processRecvZcCB(\n      IoUringBackend* backend, IoSqe* ioSqe, const io_uring_cqe* cqe) {\n    backend->processRecvZc(ioSqe, cqe);\n  }\n\n  IoUringBackend::IoSqe* allocNewIoSqe() {\n    // allow pool alloc if numPooledIoSqeInUse_ < numEntries_\n    auto* ret = new IoSqe(this, numPooledIoSqeInUse_ < numEntries_);\n    ++numPooledIoSqeInUse_;\n    ret->backendCb_ = IoUringBackend::processPollIoSqe;\n\n    return ret;\n  }\n\n  void cleanup();\n\n  io_uring_sqe* getUntrackedSqe();\n  io_uring_sqe* getSqe();\n\n  // Wait helpers\n  int doInnerWait(io_uring_cqe*& cqe) noexcept;\n  int doWait(io_uring_cqe*& cqe);\n  int doPeek(io_uring_cqe*& cqe) noexcept;\n\n  /// some ring calls require being called on a single system thread, so we need\n  /// to delay init of those things until the correct thread is ready\n  void delayedInit();\n\n  /// init things that are linked to the io_uring submitter concept\n  /// so for DeferTaskrun, only do this in delayed init\n  void initSubmissionLinked();\n\n  Options options_;\n  size_t numEntries_;\n  std::unique_ptr<IoSqe> timerEntry_;\n  std::unique_ptr<IoSqe> signalReadEntry_;\n  IoSqeList freeList_;\n  bool usingDeferTaskrun_{false};\n  int napiId_{-1};\n\n  // timer related\n  int timerFd_{-1};\n  bool timerChanged_{false};\n  bool timerSet_{false};\n  std::multimap<std::chrono::steady_clock::time_point, Event*> timers_;\n\n  // signal related\n  SocketPair signalFds_;\n  std::map<int, std::set<Event*>> signals_;\n\n  // submit\n  IoSqeBaseList submitList_;\n  uint16_t bufferProviderGidNext_{0};\n  std::vector<IoUringProvidedBufferRing::UniquePtr> bufferProviders_;\n  uint64_t bufferProviderIdx_{0};\n  IoUringZeroCopyBufferPool::UniquePtr zcBufferPool_;\n\n  // loop related\n  bool loopBreak_{false};\n  bool shuttingDown_{false};\n  bool processTimers_{false};\n  bool processSignals_{false};\n  IoSqeList activeEvents_;\n  size_t waitingToSubmit_{0};\n  size_t numInsertedEvents_{0};\n  size_t numInternalEvents_{0};\n  size_t numSendEvents_{0};\n\n  // number of pooled IoSqe instances in use\n  size_t numPooledIoSqeInUse_{0};\n\n  // io_uring related\n  io_uring_params params_;\n  io_uring ioRing_;\n\n  FdRegistry fdRegistry_;\n\n  // poll callback to be invoked if POLL_CQ flag is set\n  // every time we poll for a CQE\n  CQPollLoopCallback cqPollLoopCallback_;\n\n  bool needsDelayedInit_{true};\n\n  // stuff for ensuring we don't re-enter submit/getActiveEvents\n  folly::Optional<std::thread::id> submitTid_;\n  int isSubmitting_{0};\n  bool gettingEvents_{false};\n  void dCheckSubmitTid();\n  void setSubmitting() noexcept { isSubmitting_++; }\n  void doneSubmitting() noexcept { isSubmitting_--; }\n  bool isSubmitting() const noexcept { return isSubmitting_; }\n};\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <boost/intrusive/list.hpp>\n#include <boost/intrusive/slist.hpp>\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/DelayedDestruction.h>\n\nstruct io_uring_sqe;\nstruct io_uring_cqe;\n\nnamespace folly {\n\nclass IoUringBackend;\nclass EventBase;\n\nstruct IoSqeBase\n    : boost::intrusive::list_base_hook<\n          boost::intrusive::link_mode<boost::intrusive::auto_unlink>> {\n  enum class Type {\n    Unknown,\n    Read,\n    Write,\n    Open,\n    Close,\n    Connect,\n    Cancel,\n  };\n\n  IoSqeBase() : IoSqeBase(Type::Unknown) {}\n  explicit IoSqeBase(Type type) : type_(type) {}\n  // use raw addresses, so disallow copy/move\n  IoSqeBase(IoSqeBase&&) = delete;\n  IoSqeBase(const IoSqeBase&) = delete;\n  IoSqeBase& operator=(IoSqeBase&&) = delete;\n  IoSqeBase& operator=(const IoSqeBase&) = delete;\n\n  virtual ~IoSqeBase() = default;\n  virtual void processSubmit(struct io_uring_sqe* sqe) noexcept = 0;\n  virtual void callback(const io_uring_cqe* cqe) noexcept = 0;\n  virtual void callbackCancelled(const io_uring_cqe* cqe) noexcept = 0;\n  IoSqeBase::Type type() const { return type_; }\n  bool inFlight() const { return inFlight_; }\n  bool cancelled() const { return cancelled_; }\n  void markCancelled() { cancelled_ = true; }\n  void setEventBase(EventBase* evb) { evb_ = evb; }\n\n protected:\n  // This is used if you want to prepare this sqe for reuse, but will manage the\n  // lifetime. For example for zerocopy send, you might want to reuse the sqe\n  // but still have a notification inbound.\n  void prepareForReuse() { internalMarkInflight(false); }\n  void internalMarkInflight(bool val) { inFlight_ = val; }\n\n private:\n  friend class IoUringBackend;\n  void internalSubmit(struct io_uring_sqe* sqe) noexcept;\n  void internalCallback(const io_uring_cqe* cqe) noexcept;\n\n  bool inFlight_ = false;\n  bool cancelled_ = false;\n  EventBase* evb_ = nullptr;\n  Type type_;\n};\n\nstruct IoUringFdRegistrationRecord\n    : public boost::intrusive::slist_base_hook<\n          boost::intrusive::cache_last<false>> {\n  int count_{0};\n  int fd_{-1};\n  int idx_{0};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringBufferPoolSharing.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringBufferPoolSharing.h>\n\n#include <algorithm>\n\n#include <glog/logging.h>\n\n#include <folly/Function.h>\n#include <folly/io/async/Liburing.h>\n\n#if FOLLY_HAS_LIBURING\n#include <folly/io/async/IoUringBackend.h>\n#endif\n\nnamespace folly {\n\nnamespace {\n\nbool setupIoUringBufferPoolSharingImpl(\n    size_t numIoThreads,\n    folly::FunctionRef<folly::EventBase*(size_t)> getEventBase,\n    size_t numHwQueues) {\n#if !FOLLY_HAS_LIBURING\n  (void)numIoThreads;\n  (void)getEventBase;\n  (void)numHwQueues;\n  LOG(FATAL) << \"Buffer pool sharing is only supported on Linux\";\n#else\n  CHECK_GT(numIoThreads, 0) << \"need at least one IO thread\";\n  CHECK_GT(numHwQueues, 0)\n      << \"need at least 1 hw queue but passing: \" << numHwQueues;\n  std::vector<IoUringBackend*> backends;\n  backends.reserve(numIoThreads);\n  for (size_t i = 0; i < numIoThreads; ++i) {\n    auto* backend =\n        dynamic_cast<IoUringBackend*>(getEventBase(i)->getBackend());\n    CHECK(backend) << \"EventBase at index \" << i\n                   << \" does not have IoUringBackend\";\n    backends.push_back(backend);\n  }\n\n  size_t numOwners = std::min(numHwQueues, backends.size());\n  for (size_t i = 0; i < numOwners; ++i) {\n    if (!backends[i]->zcBufferPool()) {\n      CHECK(backends[i]->createZcBufferPool())\n          << \"Failed to create zero-copy buffer pool for EventBase at index \"\n          << i;\n    }\n  }\n\n  if (numHwQueues >= backends.size()) {\n    // Every backend has its own HW queue, no sharing needed.\n    return true;\n  }\n\n  for (size_t i = numHwQueues; i < backends.size(); ++i) {\n    size_t ownerIdx = (i - numHwQueues) % numOwners;\n    auto handle = backends[ownerIdx]->exportZcBufferPool();\n    CHECK(backends[i]->importZcBufferPool(std::move(handle)))\n        << \"Failed to import buffer pool handle into EventBase at index \" << i\n        << \" from owner at index \" << ownerIdx;\n  }\n\n  return true;\n#endif\n}\n\n} // namespace\n\nbool setupIoUringBufferPoolSharing(\n    std::vector<std::unique_ptr<folly::EventBase>>& eventBases,\n    size_t numHwQueues) {\n  return setupIoUringBufferPoolSharingImpl(\n      eventBases.size(),\n      [&](size_t i) { return eventBases[i].get(); },\n      numHwQueues);\n}\n\nbool setupIoUringBufferPoolSharing(\n    std::vector<folly::EventBase*>& eventBases, size_t numHwQueues) {\n  return setupIoUringBufferPoolSharingImpl(\n      eventBases.size(), [&](size_t i) { return eventBases[i]; }, numHwQueues);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringBufferPoolSharing.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <vector>\n\n#include <folly/io/async/EventBase.h>\n\nnamespace folly {\n\n/**\n * Sets up zero-copy buffer pool creation and sharing across EventBases.\n *\n * This function should be called AFTER all EventBases are created and\n * their IoUringBackends are initialized. The first\n * N hwQueues EventBases are designated as owners — if\n * they do not already have a zero-copy buffer pool, one will be created\n * via createZcBufferPool(). Owner pools are then exported and shared with\n * the remaining EventBases via import.\n *\n * If the number of zero-copy hardware queues >= the number of EventBases,\n * each backend gets its own pool and no sharing is needed.\n *\n * @param eventBases The EventBases to set up for buffer pool sharing.\n *                   All must have IoUringBackend.\n * @param numHwQueues hw queues allocated for zcrx\n * @return true on success. CHECK-fails on any error.\n */\nbool setupIoUringBufferPoolSharing(\n    std::vector<std::unique_ptr<folly::EventBase>>& eventBases,\n    size_t numHwQueues);\n\nbool setupIoUringBufferPoolSharing(\n    std::vector<folly::EventBase*>& eventBases, size_t numHwQueues);\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringConnect.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringConnect.h>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/IoUringBackend.h>\n\nnamespace folly {\n\n#if FOLLY_HAS_LIBURING\n\nIoUringConnectHandle::UniquePtr IoUringConnectHandle::create(\n    EventBase* evb,\n    NetworkSocket fd,\n    IoUringConnectCallback* callback,\n    std::chrono::milliseconds timeout) {\n  auto* backend = dynamic_cast<folly::IoUringBackend*>(evb->getBackend());\n  if (!backend) {\n    return nullptr;\n  }\n\n  auto handle = std::make_unique<IoUringConnectHandle>(\n      NotPubliclyConstructible{}, evb, backend, fd, callback);\n  if (timeout.count() > 0) {\n    handle->scheduleTimeout(uint32_t(timeout.count()));\n  }\n  backend->submitSoon(*handle);\n  return handle;\n}\n\nIoUringConnectHandle::IoUringConnectHandle(\n    NotPubliclyConstructible,\n    EventBase* evb,\n    IoUringBackend* backend,\n    NetworkSocket fd,\n    IoUringConnectCallback* callback)\n    : IoSqeBase(IoSqeBase::Type::Connect),\n      AsyncTimeout(evb),\n      backend_(backend),\n      fd_(fd),\n      callback_(callback) {\n  setEventBase(evb);\n}\n\nvoid IoUringConnectHandle::processSubmit(struct io_uring_sqe* sqe) noexcept {\n  ::io_uring_prep_poll_add(sqe, fd_.toFd(), POLLOUT);\n}\n\nvoid IoUringConnectHandle::callback(\n    const struct io_uring_cqe* /*cqe*/) noexcept {\n  cancelTimeout();\n  callback_->connectSuccess();\n  return;\n}\n\nvoid IoUringConnectHandle::callbackCancelled(const io_uring_cqe*) noexcept {\n  delete this;\n}\n\nvoid IoUringConnectHandle::timeoutExpired() noexcept {\n  if (!cancelled()) {\n    CHECK(callback_ != nullptr);\n    callback_->connectTimeout();\n  }\n}\n\nbool IoUringConnectHandle::cancel() {\n  cancelTimeout();\n  if (inFlight()) {\n    backend_->cancel(this);\n    return true;\n  }\n\n  return false;\n}\n\n#else\n\nIoUringConnectHandle::UniquePtr IoUringConnectHandle::create(\n    EventBase* /*evb*/,\n    NetworkSocket /*fd*/,\n    IoUringConnectCallback* /*callback*/,\n    std::chrono::milliseconds /*timeout*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nIoUringConnectHandle::IoUringConnectHandle(\n    NotPubliclyConstructible,\n    EventBase* evb,\n    IoUringBackend* /*backend*/,\n    NetworkSocket /*fd*/,\n    IoUringConnectCallback* /*callback*/)\n    : IoSqeBase(IoSqeBase::Type::Connect),\n      AsyncTimeout(evb),\n      backend_(nullptr),\n      fd_(),\n      callback_(nullptr) {\n  (void)backend_;\n  (void)fd_;\n  (void)callback_;\n}\n\nvoid IoUringConnectHandle::processSubmit(\n    struct io_uring_sqe* /*sqe*/) noexcept {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringConnectHandle::callback(\n    const struct io_uring_cqe* /*cqe*/) noexcept {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringConnectHandle::callbackCancelled(const io_uring_cqe*) noexcept {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringConnectHandle::timeoutExpired() noexcept {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nbool IoUringConnectHandle::cancel() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringConnect.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/IoUringBase.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly {\n\nclass IoUringConnectCallback {\n public:\n  virtual ~IoUringConnectCallback() = default;\n\n  virtual void connectSuccess() = 0;\n  virtual void connectTimeout() = 0;\n};\n\nclass EventBase;\nclass IoUringBackend;\n\nclass IoUringConnectHandle : public IoSqeBase, public AsyncTimeout {\n private:\n  struct NotPubliclyConstructible {};\n\n public:\n  using UniquePtr = std::unique_ptr<IoUringConnectHandle>;\n\n  static IoUringConnectHandle::UniquePtr create(\n      EventBase* evb,\n      NetworkSocket fd,\n      IoUringConnectCallback* callback,\n      std::chrono::milliseconds timeout);\n\n  IoUringConnectHandle(\n      NotPubliclyConstructible,\n      EventBase* evb,\n      IoUringBackend* backend,\n      NetworkSocket fd,\n      IoUringConnectCallback* callback);\n\n  /*\n   * IoSqeBase\n   */\n  void processSubmit(struct io_uring_sqe* sqe) noexcept override;\n  void callback(const struct io_uring_cqe* cqe) noexcept override;\n  void callbackCancelled(const io_uring_cqe*) noexcept override;\n\n  /*\n   * AsyncTimeout\n   */\n  void timeoutExpired() noexcept override;\n\n  bool cancel();\n\n private:\n  IoUringBackend* backend_;\n  NetworkSocket fd_;\n  IoUringConnectCallback* callback_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringEvent.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringEvent.h>\n\n#if FOLLY_HAS_LIBURING\n\n#include <sys/eventfd.h>\n\nnamespace folly {\n\nbool IoUringEvent::hasWork() {\n  return backend_.isWaitingToSubmit() ||\n      io_uring_cq_ready(backend_.ioRingPtr());\n}\n\nIoUringEvent::IoUringEvent(\n    folly::EventBase* eventBase, IoUringBackend::Options o, bool use_event_fd)\n    : EventHandler(eventBase), eventBase_(eventBase), backend_(std::move(o)) {\n  if (use_event_fd) {\n    eventFd_ = folly::File{eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC), true};\n    int ret = io_uring_register_eventfd(backend_.ioRingPtr(), eventFd_->fd());\n    PLOG_IF(ERROR, ret) << \"cannot register eventfd\";\n    if (ret) {\n      throw std::runtime_error(\"cannot register eventfd\");\n    }\n    changeHandlerFD(NetworkSocket{eventFd_->fd()});\n  } else {\n    changeHandlerFD(NetworkSocket{backend_.ioRingPtr()->ring_fd});\n  }\n\n  registerHandler(EventHandler::PERSIST | EventHandler::READ);\n  edgeTriggered_ = use_event_fd && setEdgeTriggered();\n\n  eventBase->runBeforeLoop(this);\n}\n\nIoUringEvent::~IoUringEvent() {\n  // flush cq:\n  while (hasWork() &&\n         !backend_.eb_event_base_loop(EVLOOP_NONBLOCK | EVLOOP_ONCE)) {\n    VLOG(9) << \"IoUringEvent::cleanup  done: isWaitingToSubmit=\"\n            << backend_.isWaitingToSubmit()\n            << \" cqes=\" << io_uring_cq_ready(backend_.ioRingPtr());\n  }\n}\n\nvoid IoUringEvent::handlerReady(uint16_t events) noexcept {\n  VLOG(4) << \"IoUringEvent::handlerReady(\" << events << \")\";\n\n  if (!(events & EventHandler::READ)) {\n    return;\n  }\n\n  size_t ready = io_uring_cq_ready(backend_.ioRingPtr());\n\n  if (ready >= backend_.options().maxGet) {\n    // don't even clear the eventfd\n    backend_.processCompleted();\n    lastWasResignalled_ = true;\n    if (edgeTriggered_) {\n      uint64_t val = 1;\n      int ret = fileops::write(eventFd_->fd(), &val, sizeof(val));\n      DCHECK(ret == 8);\n    }\n  } else if (eventFd_) {\n    if (!edgeTriggered_) {\n      uint64_t val;\n      std::ignore = fileops::read(eventFd_->fd(), &val, sizeof(val));\n    }\n    backend_.loopPoll();\n\n    // if we still have completions we really need to process them next loop\n    lastWasResignalled_ = io_uring_cq_ready(backend_.ioRingPtr()) > 0;\n    if (lastWasResignalled_) {\n      uint64_t val = 1;\n      int ret = fileops::write(eventFd_->fd(), &val, sizeof(val));\n      DCHECK(ret == 8);\n    }\n  } else {\n    backend_.loopPoll();\n  }\n}\n\nvoid IoUringEvent::runLoopCallback() noexcept {\n  VLOG(9) << \"IoUringEvent::runLoopCallback\";\n\n  eventBase_->runBeforeLoop(this);\n\n  // we have to run this code before we wait in case there are some submissions\n  // that have not been flushed\n\n  if (lastWasResignalled_ || io_uring_cq_ready(backend_.ioRingPtr())) {\n    // definitely will get into the main callback\n    return;\n  }\n\n  if (backend_.isWaitingToSubmit()) {\n    backend_.submitOutstanding();\n  } else {\n    VLOG(9) << \"IoUringEvent::runLoopCallback nothing to run\";\n  }\n}\n\n} // namespace folly\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringEvent.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/File.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/io/async/Liburing.h>\n\nnamespace folly {\n\n#if FOLLY_HAS_LIBURING\n\nclass IoUringEvent : public EventHandler, public EventBase::LoopCallback {\n public:\n  IoUringEvent(\n      folly::EventBase* eventBase,\n      IoUringBackend::Options o,\n      bool use_event_fd = true);\n  ~IoUringEvent() override;\n\n  // cannot move/copy due to postLoopCallback\n  IoUringEvent const& operator=(IoUringEvent const&) = delete;\n  IoUringEvent&& operator=(IoUringEvent&&) = delete;\n  IoUringEvent(IoUringEvent&&) = delete;\n  IoUringEvent(IoUringEvent const&) = delete;\n\n  void handlerReady(uint16_t events) noexcept override;\n\n  void runLoopCallback() noexcept override;\n\n  IoUringBackend& backend() { return backend_; }\n\n private:\n  bool hasWork();\n  EventBase* eventBase_;\n  IoUringBackend backend_;\n\n  bool lastWasResignalled_ = false;\n  bool edgeTriggered_ = false;\n  std::optional<File> eventFd_;\n};\n\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringEventBaseLocal.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Singleton.h>\n#include <folly/io/async/EventBaseLocal.h>\n#include <folly/io/async/IoUringEvent.h>\n#include <folly/io/async/IoUringEventBaseLocal.h>\n\n#if FOLLY_HAS_LIBURING\n\nnamespace folly {\n\nnamespace {\nstruct Tag {};\nSingleton<EventBaseLocal<std::unique_ptr<IoUringEvent>>, Tag> singleton;\n\nvoid detach(EventBase* evb) {\n  auto local = singleton.try_get();\n  if (!local) {\n    return;\n  }\n  local->erase(*evb);\n}\n\n} // namespace\n\nIoUringBackend* IoUringEventBaseLocal::try_get(EventBase* evb) {\n  auto local = singleton.try_get();\n  if (!local) {\n    throw std::runtime_error(\"EventBaseLocal is already destructed\");\n  }\n  std::unique_ptr<IoUringEvent>* ptr = local->get(*evb);\n  if (!ptr) {\n    return nullptr;\n  }\n  return &ptr->get()->backend();\n}\n\nvoid IoUringEventBaseLocal::attach(\n    EventBase* evb, IoUringBackend::Options options, bool use_eventfd) {\n  evb->dcheckIsInEventBaseThread();\n\n  // We tie into event base callbacks which need to be cleaned up earlier than\n  // locals are destroyed, so do this manually\n  evb->runOnDestruction([evb]() { detach(evb); });\n\n  auto local = singleton.try_get();\n  if (!local) {\n    throw std::runtime_error(\"EventBaseLocal is already destructed\");\n  }\n  if (try_get(evb)) {\n    throw std::runtime_error(\n        \"this event base already has a local io_uring attached\");\n  }\n  local->emplace(\n      *evb,\n      std::make_unique<IoUringEvent>(evb, std::move(options), use_eventfd));\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringEventBaseLocal.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/io/async/Liburing.h>\n\nnamespace folly {\n\n#if FOLLY_HAS_LIBURING\n\nclass IoUringEventBaseLocal {\n public:\n  static void attach(\n      EventBase* evb, IoUringBackend::Options options, bool use_eventfd = true);\n  static IoUringBackend* try_get(EventBase* evb);\n};\n\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringOptions.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <optional>\n#include <set>\n#include <string>\n\n#if defined(__linux__)\n#include <net/if.h>\n#endif\n\n#include <folly/Conv.h>\n#include <folly/Function.h>\n#include <folly/IPAddress.h>\n#include <folly/io/async/IoUringZeroCopyBufferPool.h>\n\nnamespace folly {\n#if FOLLY_HAS_LIBURING\n\n// Callback types for IoUringBackend configuration\nusing ResolveNapiIdCallback =\n    folly::Function<int(int ifindex, uint32_t queueId)>;\nusing SrcPortForQueueIdCallback = folly::Function<int(\n    const folly::IPAddress& destAddr,\n    uint16_t destPort,\n    int targetQueueId,\n    const char* ifname,\n    uint16_t startPort,\n    uint16_t minPort,\n    uint16_t maxPort)>;\n\nstruct IoUringOptions {\n  enum Flags {\n    POLL_SQ = 0x1,\n    POLL_CQ = 0x2,\n    POLL_SQ_IMMEDIATE_IO = 0x4, // do not enqueue I/O operations\n  };\n\n  IoUringOptions() = default;\n  IoUringOptions(const IoUringOptions&) = delete;\n  IoUringOptions& operator=(const IoUringOptions&) = delete;\n  IoUringOptions(IoUringOptions&&) = default;\n  IoUringOptions& operator=(IoUringOptions&&) = default;\n  ~IoUringOptions() = default;\n\n  IoUringOptions& setCapacity(size_t v) {\n    capacity = v;\n    return *this;\n  }\n\n  IoUringOptions& setMinCapacity(size_t v) {\n    minCapacity = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setMaxSubmit(size_t v) {\n    maxSubmit = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setSqeSize(size_t v) {\n    sqeSize = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setMaxGet(size_t v) {\n    maxGet = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setUseRegisteredFds(size_t v) {\n    registeredFds = v;\n    return *this;\n  }\n\n  IoUringOptions& setFlags(uint32_t v) {\n    flags = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setSQIdle(std::chrono::milliseconds v) {\n    sqIdle = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setCQIdle(std::chrono::milliseconds v) {\n    cqIdle = v;\n\n    return *this;\n  }\n\n  // Set the CPU as preferred for submission queue poll thread.\n  //\n  // This only has effect if POLL_SQ flag is specified.\n  //\n  // Can call multiple times to specify multiple CPUs.\n  IoUringOptions& setSQCpu(uint32_t v) {\n    sqCpus.insert(v);\n\n    return *this;\n  }\n\n  // Set the preferred CPUs for submission queue poll thread(s).\n  //\n  // This only has effect if POLL_SQ flag is specified.\n  IoUringOptions& setSQCpus(std::set<uint32_t> const& cpus) {\n    sqCpus.insert(cpus.begin(), cpus.end());\n\n    return *this;\n  }\n\n  IoUringOptions& setSQGroupName(const std::string& v) {\n    sqGroupName = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setSQGroupNumThreads(size_t v) {\n    sqGroupNumThreads = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setInitialProvidedBuffers(size_t eachSize, size_t count) {\n    initialProvidedBuffersCount = count;\n    initialProvidedBuffersEachSize = eachSize;\n    return *this;\n  }\n\n  constexpr bool isPow2(uint64_t n) noexcept { return n > 0 && !((n - 1) & n); }\n\n  IoUringOptions& setProvidedBufRings(size_t v) {\n    if (!isPow2(v)) {\n      throw std::runtime_error(\n          folly::to<std::string>(\n              \"number of provided buffer rings must be a power of 2\"));\n    }\n    providedBufRings = v;\n    return *this;\n  }\n\n  IoUringOptions& setRegisterRingFd(bool v) {\n    registerRingFd = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setTaskRunCoop(bool v) {\n    taskRunCoop = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setDeferTaskRun(bool v) {\n    deferTaskRun = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setTimeout(std::chrono::microseconds v) {\n    timeout = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setBatchSize(int v) {\n    batchSize = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setZeroCopyRx(bool v) {\n    zeroCopyRx = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setZeroCopyRxInterface(std::string v) {\n    zcRxIfname = std::move(v);\n    zcRxIfindex = ::if_nametoindex(zcRxIfname.c_str());\n    if (zcRxIfindex == 0) {\n      throw std::runtime_error(\n          folly::to<std::string>(\n              \"invalid network interface name: \",\n              zcRxIfname,\n              \", errno: \",\n              errno));\n    }\n\n    return *this;\n  }\n\n  IoUringOptions& setZeroCopyRxQueue(int queueId) {\n    zcRxQueueId = queueId;\n\n    return *this;\n  }\n\n  IoUringOptions& setResolveNapiCallback(ResolveNapiIdCallback&& v) {\n    resolveNapiId = std::move(v);\n\n    return *this;\n  }\n\n  IoUringOptions& setZcrxSrcPortCallback(SrcPortForQueueIdCallback&& v) {\n    srcPortQueueId = std::move(v);\n\n    return *this;\n  }\n\n  IoUringOptions& setZeroCopyRxNumPages(int v) {\n    zcRxNumPages = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setZeroCopyRxRefillEntries(int v) {\n    zcRxRefillEntries = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setEnableIncrementalBuffers(bool v) {\n    enableIncrementalBuffers = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setUseHugePages(bool v) {\n    useHugePages = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setNativeAsyncSocketSupport(bool v) {\n    nativeAsyncSocketSupport = v;\n\n    return *this;\n  }\n\n  IoUringOptions& setArenaRegion(void* base, size_t size, uint32_t index) {\n    arenaRegion.iov_base = base;\n    arenaRegion.iov_len = size;\n    arenaIndex = index;\n    return *this;\n  }\n\n  ssize_t sqeSize{-1};\n\n  size_t capacity{256};\n  size_t minCapacity{0};\n  size_t maxSubmit{128};\n  size_t maxGet{256};\n  size_t registeredFds{0};\n  size_t sqGroupNumThreads{1};\n  size_t initialProvidedBuffersCount{0};\n  size_t initialProvidedBuffersEachSize{0};\n  size_t providedBufRings{1};\n\n  uint32_t flags{0};\n\n  // Minimum number of requests (defined as sockets with data to read) to wait\n  // for per io_uring_enter\n  int batchSize{0};\n\n  bool registerRingFd{false};\n  bool taskRunCoop{false};\n  bool deferTaskRun{false};\n\n  // Maximum amount of time to wait (in microseconds) per io_uring_enter\n  // Both timeout _and_ batchSize must be set for io_uring_enter wait_nr to be\n  // set!\n  std::chrono::microseconds timeout{0};\n  std::chrono::milliseconds sqIdle{0};\n  std::chrono::milliseconds cqIdle{0};\n\n  std::set<uint32_t> sqCpus;\n\n  std::string sqGroupName;\n\n  // Zero copy receive\n  bool zeroCopyRx{false};\n  std::string zcRxIfname;\n  int zcRxQueueId{-1};\n  int zcRxIfindex{-1};\n  ResolveNapiIdCallback resolveNapiId;\n  SrcPortForQueueIdCallback srcPortQueueId;\n  int zcRxNumPages{-1};\n  int zcRxRefillEntries{-1};\n\n  // Incremental Buffers\n  bool enableIncrementalBuffers{false};\n  bool useHugePages{false};\n\n  bool nativeAsyncSocketSupport{false};\n\n  struct iovec arenaRegion{};\n  uint32_t arenaIndex{0};\n};\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringProvidedBufferRing.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringProvidedBufferRing.h>\n\n#include <folly/Conv.h>\n#include <folly/String.h>\n#include <folly/lang/Align.h>\n#include <folly/portability/SysMman.h>\n\n#if FOLLY_HAS_LIBURING\n\nnamespace {\nconstexpr uint32_t kMinBufferSize = 32;\nconstexpr uint32_t kHugePageSizeBytes = 1024 * 1024 * 2;\nconstexpr uint32_t kPageSizeBytes = 4096;\nconstexpr uint32_t kBufferAlignBytes = 32;\n} // namespace\n\nnamespace folly {\n\nvoid IoUringProvidedBufferRing::checkInvariants() {\n  // This object is carefully packed into two 64 byte cache lines. The first\n  // cache line contains all of the fields accessed during hot code, i.e.\n  // getIoBuf() and returnBuffer(). The second cache line contains all the warm\n  // and cold fields that are rarely accessed.\n  static_assert(\n      sizeof(IoUringProvidedBufferRing) ==\n      2 * folly::hardware_constructive_interference_size);\n\n  static_assert(\n      alignof(IoUringProvidedBufferRing) ==\n      folly::hardware_constructive_interference_size);\n\n  static_assert(\n      sizeof(folly::DistributedMutex) == 8,\n      \"folly::DistributedMutex size changed from 8 bytes\");\n}\n\nIoUringProvidedBufferRing::UniquePtr IoUringProvidedBufferRing::create(\n    io_uring* ioRingPtr, Options options) {\n  return IoUringProvidedBufferRing::UniquePtr(\n      new IoUringProvidedBufferRing(ioRingPtr, options));\n}\n\nvoid IoUringProvidedBufferRing::mapMemory(bool useHugePages) {\n  // Find next power of 2 size larger than bufferCount for the provided buffer\n  // ring count.\n  int ringShift = folly::findLastSet(bufferCount_) - 1;\n  if (bufferCount_ != (1ULL << ringShift)) {\n    ringShift++;\n  }\n  ringCount_ = 1U << std::max<int>(ringShift, 1);\n  ringMask_ = ringCount_ - 1;\n  ringMemSize_ = sizeof(struct io_uring_buf) * ringCount_;\n  ringMemSize_ =\n      folly::to_narrow(folly::align_ceil(ringMemSize_, kBufferAlignBytes));\n\n  bufferSize_ = sizePerBuffer_ * bufferCount_;\n  allSize_ = ringMemSize_ + bufferSize_;\n\n  int pages;\n  if (useHugePages) {\n    allSize_ =\n        folly::to_narrow(folly::align_ceil(allSize_, kHugePageSizeBytes));\n    pages = allSize_ / kHugePageSizeBytes;\n  } else {\n    allSize_ = folly::to_narrow(folly::align_ceil(allSize_, kPageSizeBytes));\n    pages = allSize_ / kPageSizeBytes;\n  }\n\n  buffer_ = ::mmap(\n      nullptr,\n      allSize_,\n      PROT_READ | PROT_WRITE,\n      MAP_ANONYMOUS | MAP_PRIVATE,\n      -1,\n      0);\n\n  if (buffer_ == MAP_FAILED) {\n    auto errnoCopy = errno;\n    throw std::runtime_error(\n        folly::to<std::string>(\n            \"unable to allocate pages of size \",\n            allSize_,\n            \" pages=\",\n            pages,\n            \": \",\n            folly::errnoStr(errnoCopy)));\n  }\n\n  ringPtr_ = static_cast<struct io_uring_buf_ring*>(buffer_);\n  bufferBuffer_ = static_cast<char*>(buffer_) + ringMemSize_;\n\n  if (useHugePages) {\n    int ret = ::madvise(buffer_, allSize_, MADV_HUGEPAGE);\n    PLOG_IF(ERROR, ret) << \"cannot enable huge pages\";\n  } else {\n    ::madvise(buffer_, allSize_, MADV_NOHUGEPAGE);\n  }\n}\n\nIoUringProvidedBufferRing::IoUringProvidedBufferRing(\n    io_uring* ioRingPtr, Options options)\n    : bufferStates_(),\n      sizePerBuffer_(std::max(options.bufferSize, kMinBufferSize)),\n      bufferCount_(options.bufferCount),\n      useIncremental_(options.useIncrementalBuffers),\n      ioRingPtr_(ioRingPtr),\n      gid_(options.gid) {\n  if (bufferCount_ > std::numeric_limits<uint16_t>::max()) {\n    throw std::runtime_error(\"bufferCount cannot be larger than 65,535\");\n  }\n  if (bufferCount_ == 0) {\n    throw std::runtime_error(\"bufferCount cannot be 0\");\n  }\n\n  mapMemory(options.useHugePages);\n  initialRegister();\n\n  bufferStates_ = std::make_unique<BufferState[]>(bufferCount_);\n  for (uint16_t i = 0; i < bufferCount_; i++) {\n    bufferStates_[i].bufId = i;\n    bufferStates_[i].parent = this;\n    bufferStates_[i].offset = 0;\n  }\n\n  for (size_t i = 0; i < bufferCount_; i++) {\n    returnBuffer(i);\n  }\n}\n\nvoid IoUringProvidedBufferRing::enobuf() noexcept {\n  {\n    // what we want to do is something like\n    // if (cachedTail_ != localTail_) {\n    //   publish();\n    //   enobuf_ = false;\n    // }\n    // but if we are processing a batch it doesn't really work\n    // because we'll likely get an ENOBUF straight after\n    enobuf_.store(true, std::memory_order_relaxed);\n    enobufCount_.fetch_add(1, std::memory_order_relaxed);\n  }\n}\n\nuint32_t IoUringProvidedBufferRing::getAndResetEnobufCount() noexcept {\n  return enobufCount_.exchange(0, std::memory_order_relaxed);\n}\n\nvoid IoUringProvidedBufferRing::destroy() noexcept {\n  std::unique_lock lock{mutex_};\n  ::io_uring_unregister_buf_ring(ioRingPtr_, gid());\n  DCHECK(gottenBuffers_ >= returnedBuffers_);\n  auto remaining = gottenBuffers_ - returnedBuffers_;\n  shutdownReferences_ = remaining;\n  wantsShutdown_ = true;\n  lock.unlock();\n  delayedDestroy(remaining);\n}\n\nvoid IoUringProvidedBufferRing::returnBuffer(uint16_t i) noexcept {\n  std::unique_lock lock{mutex_};\n  if (FOLLY_UNLIKELY(wantsShutdown_)) {\n    auto refs = --shutdownReferences_;\n    lock.unlock();\n    delayedDestroy(refs);\n    return;\n  }\n\n  if (useIncremental_) {\n    bufferStates_[i].offset = 0;\n    bufferStates_[i].refCount.store(1);\n  }\n\n  uint16_t this_idx = static_cast<uint16_t>(ringReturnedBuffers_++);\n  uint16_t next_tail = this_idx + 1;\n\n  auto* r = ringBuf(this_idx);\n  r->addr = reinterpret_cast<__u64>(getData(i));\n  r->len = sizePerBuffer_;\n  r->bid = i;\n\n  if (tryPublish(this_idx, next_tail)) {\n    enobuf_.store(false, std::memory_order_relaxed);\n  }\n}\n\nstd::unique_ptr<IOBuf> IoUringProvidedBufferRing::getIoBufSingle(\n    uint16_t i, size_t length, bool hasMore) noexcept {\n  std::unique_ptr<IOBuf> ret;\n  DCHECK(!wantsShutdown_);\n  DCHECK_LT(i, bufferCount_)\n      << \"Buffer index \" << i << \" exceeds buffer count \" << bufferCount_;\n\n  auto free_fn = [](void*, void* userData) {\n    auto* bufferState = static_cast<BufferState*>(userData);\n    IoUringProvidedBufferRing* parent = bufferState->parent;\n    uint16_t bufId = bufferState->bufId;\n    parent->decBufferState(bufId);\n  };\n\n  if (useIncremental_) {\n    auto* bufferStart = getData(i);\n    unsigned int currentOffset = bufferStates_[i].offset;\n    auto* dataPtr = bufferStart + currentOffset;\n    BufferState* info = &bufferStates_[i];\n    ret = IOBuf::takeOwnership(\n        static_cast<void*>(dataPtr), length, length, free_fn, info);\n  } else {\n    BufferState* info = &bufferStates_[i];\n    ret = IOBuf::takeOwnership(\n        static_cast<void*>(getData(i)), sizePerBuffer_, length, free_fn, info);\n  }\n\n  ret->markExternallySharedOne();\n  incBufferState(i, hasMore, length);\n\n  return ret;\n}\n\nstd::unique_ptr<IOBuf> IoUringProvidedBufferRing::getIoBuf(\n    uint16_t startBufId, size_t totalLength, bool hasMore) noexcept {\n  if (totalLength <= sizePerBuffer_) {\n    return getIoBufSingle(startBufId, totalLength, hasMore);\n  }\n\n  auto free_fn = [](void*, void* userData) {\n    auto* bufferState = static_cast<BufferState*>(userData);\n    IoUringProvidedBufferRing* parent = bufferState->parent;\n    uint16_t bufId = bufferState->bufId;\n    parent->decBufferState(bufId);\n  };\n\n  std::unique_ptr<IOBuf> head;\n  size_t remainingLength = totalLength;\n  uint16_t currentBufId = startBufId;\n\n  while (remainingLength > 0) {\n    DCHECK_LT(currentBufId, bufferCount_)\n        << \"Buffer index \" << currentBufId << \" exceeds buffer count \"\n        << bufferCount_;\n\n    BufferState* bufferState = &bufferStates_[currentBufId];\n    char* bufferStart = getData(currentBufId);\n    unsigned int currentOffset = 0;\n    size_t availableInBuffer = sizePerBuffer_;\n\n    if (useIncremental_) {\n      currentOffset = bufferState->offset;\n      availableInBuffer = sizePerBuffer_ - currentOffset;\n    }\n\n    char* dataPtr = bufferStart + currentOffset;\n    size_t currentChunkSize = std::min(remainingLength, availableInBuffer);\n    bool isLastChunk = (remainingLength <= availableInBuffer);\n\n    std::unique_ptr<IOBuf> chunk;\n    chunk = IOBuf::takeOwnership(\n        static_cast<void*>(dataPtr),\n        useIncremental_ ? currentChunkSize : sizePerBuffer_,\n        currentChunkSize,\n        free_fn,\n        bufferState);\n\n    chunk->markExternallySharedOne();\n    if (!head) {\n      head = std::move(chunk);\n    } else {\n      head->appendToChain(std::move(chunk));\n    }\n\n    incBufferState(currentBufId, hasMore && isLastChunk, currentChunkSize);\n    remainingLength -= currentChunkSize;\n    currentBufId = (currentBufId + 1) & (bufferCount_ - 1);\n  }\n\n  return head;\n}\n\nstd::unique_ptr<IOBuf> IoUringProvidedBufferRing::getIoBuf(\n    const struct io_uring_cqe* cqe) noexcept {\n  auto bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;\n  auto res = cqe->res;\n  bool hasMore = (cqe->flags & IORING_CQE_F_BUF_MORE) != 0;\n  return getIoBuf(bid, res, hasMore);\n}\n\nvoid IoUringProvidedBufferRing::initialRegister() {\n  struct io_uring_buf_reg reg{};\n  memset(&reg, 0, sizeof(reg));\n  reg.ring_addr = reinterpret_cast<__u64>(ringPtr_);\n  reg.ring_entries = ringCount_;\n  reg.bgid = gid_;\n\n  int flags = useIncremental_ ? IOU_PBUF_RING_INC : 0;\n  int ret = ::io_uring_register_buf_ring(ioRingPtr_, &reg, flags);\n\n  if (ret) {\n    LOG(ERROR) << folly::to<std::string>(\n        \"unable to register provided buffer ring \",\n        -ret,\n        \": \",\n        folly::errnoStr(-ret));\n    LOG(ERROR) << folly::to<std::string>(\n        \"buffer ring buffer count: \",\n        bufferCount_,\n        \", ring count: \",\n        ringCount_,\n        \", size per buf: \",\n        sizePerBuffer_,\n        \", bgid: \",\n        gid_);\n    throw LibUringCallError(\"unable to register provided buffer ring\");\n  }\n}\n\nvoid IoUringProvidedBufferRing::delayedDestroy(uint32_t refs) noexcept {\n  if (refs == 0) {\n    ::munmap(buffer_, allSize_);\n    delete this;\n  }\n}\n\nvoid IoUringProvidedBufferRing::incBufferState(\n    uint16_t bufId, bool hasMore, size_t bytesConsumed) noexcept {\n  gottenBuffers_++;\n\n  if (useIncremental_ && hasMore) {\n    bufferStates_[bufId].refCount.fetch_add(1);\n    bufferStates_[bufId].offset += bytesConsumed;\n  }\n\n  // No need to handle regular buffers, since it is never really\n  // incremented beyond the original assingment of 1 instead of 0.\n}\n\nvoid IoUringProvidedBufferRing::decBufferState(uint16_t bufId) noexcept {\n  returnedBuffers_++;\n\n  if (!useIncremental_ || wantsShutdown_) {\n    returnBuffer(bufId);\n    return;\n  }\n\n  uint16_t oldRefCount = bufferStates_[bufId].refCount.fetch_sub(1);\n  if (oldRefCount == 1) {\n    returnBuffer(bufId);\n  }\n}\n\nint IoUringProvidedBufferRing::getUtilPct() const noexcept {\n  uint32_t totalBuffers = bufferCount_;\n  uint16_t head = 0;\n  int ret = ::io_uring_buf_ring_head(ioRingPtr_, gid(), &head);\n  if (ret != 0) {\n    return ret;\n  }\n  // Use ring mask to extract ring position from wrapped uint16_t counters\n  // Ring size is power of 2, mask handles wrap-around explicitly\n  uint32_t available = (ringPtr_->tail - head) & ringMask_;\n  available = std::min(available, totalBuffers);\n\n  uint32_t inUse = totalBuffers - available;\n  return (100 * inUse) / totalBuffers;\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringProvidedBufferRing.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/IoUringBase.h>\n#include <folly/io/async/Liburing.h>\n#include <folly/synchronization/DistributedMutex.h>\n\n#if FOLLY_HAS_LIBURING\n\nFOLLY_PUSH_WARNING\nFOLLY_CLANG_DISABLE_WARNING(\"-Wnested-anon-types\")\nFOLLY_CLANG_DISABLE_WARNING(\"-Wzero-length-array\")\nFOLLY_GCC_DISABLE_WARNING(\"-Wshadow\")\n#include <liburing.h> // @manual\nFOLLY_POP_WARNING\n\nnamespace folly {\n\nclass IoUringProvidedBufferRing {\n public:\n  friend class IoUringProvidedBufferRingTestHelper;\n\n  class LibUringCallError : public std::runtime_error {\n   public:\n    using std::runtime_error::runtime_error;\n  };\n\n  struct Deleter {\n    void operator()(IoUringProvidedBufferRing* ring) {\n      if (ring) {\n        ring->destroy();\n      }\n    }\n  };\n\n  using UniquePtr = std::unique_ptr<IoUringProvidedBufferRing, Deleter>;\n\n  struct Options {\n    uint16_t gid{0};\n    uint32_t bufferCount{0};\n    uint32_t bufferSize{0};\n    bool useHugePages{false};\n    bool useIncrementalBuffers{false};\n  };\n\n  static UniquePtr create(io_uring* ioRingPtr, Options options);\n\n  ~IoUringProvidedBufferRing() = default;\n\n  void enobuf() noexcept;\n  uint32_t getAndResetEnobufCount() noexcept;\n  void destroy() noexcept;\n\n  std::unique_ptr<IOBuf> getIoBuf(\n      uint16_t startBufId, size_t totalLength, bool hasMore) noexcept;\n  std::unique_ptr<IOBuf> getIoBuf(const struct io_uring_cqe* cqe) noexcept;\n\n  uint32_t count() const noexcept { return bufferCount_; }\n  bool available() const noexcept {\n    return !enobuf_.load(std::memory_order_relaxed);\n  }\n  size_t sizePerBuffer() const noexcept { return sizePerBuffer_; }\n  uint16_t gid() const noexcept { return gid_; }\n\n  // Returns the buffer utilization as an integer percentage (0-100).\n  int getUtilPct() const noexcept;\n\n private:\n  explicit IoUringProvidedBufferRing(io_uring* ioRingPtr, Options options);\n\n  IoUringProvidedBufferRing(IoUringProvidedBufferRing&&) = delete;\n  IoUringProvidedBufferRing(IoUringProvidedBufferRing const&) = delete;\n  IoUringProvidedBufferRing& operator=(IoUringProvidedBufferRing&&) = delete;\n  IoUringProvidedBufferRing& operator=(IoUringProvidedBufferRing const&) =\n      delete;\n\n  void mapMemory(bool useHugePages);\n  void initialRegister();\n\n  void returnBuffer(uint16_t i) noexcept;\n\n  void delayedDestroy(uint32_t refs) noexcept;\n  void incBufferState(\n      uint16_t bufId, bool hasMore, size_t bytesConsumed) noexcept;\n  void decBufferState(uint16_t bufId) noexcept;\n  std::unique_ptr<IOBuf> getIoBufSingle(\n      uint16_t i, size_t length, bool hasMore) noexcept;\n\n  std::atomic<uint16_t>* sharedTail() {\n    return reinterpret_cast<std::atomic<uint16_t>*>(&ringPtr_->tail);\n  }\n\n  bool tryPublish(uint16_t expected, uint16_t value) noexcept {\n    return sharedTail()->compare_exchange_strong(\n        expected, value, std::memory_order_release);\n  }\n\n  char* getData(uint16_t i) {\n    auto offset = static_cast<size_t>(i) * sizePerBuffer_;\n    return bufferBuffer_ + offset;\n  }\n\n  struct io_uring_buf* ringBuf(int idx) const noexcept {\n    return &ringPtr_->bufs[idx & ringMask_];\n  }\n\n  struct BufferState {\n    uint16_t bufId{0};\n    // Starting with a refCount of 1, to account for moreData incoming\n    // in the incremental buffer case.\n    std::atomic<uint32_t> refCount{1};\n    unsigned int offset{0};\n    IoUringProvidedBufferRing* parent{nullptr};\n  };\n\n  static void checkInvariants();\n\n  // Hot fields\n  alignas(folly::hardware_constructive_interference_size)\n      std::unique_ptr<BufferState[]> bufferStates_;\n  struct io_uring_buf_ring* ringPtr_{nullptr};\n  char* bufferBuffer_{nullptr};\n  folly::DistributedMutex mutex_;\n  uint32_t sizePerBuffer_{0};\n  int ringMask_{0};\n  uint32_t gottenBuffers_{0};\n  uint32_t ringReturnedBuffers_{0};\n  uint32_t returnedBuffers_{0};\n  uint32_t bufferCount_{0};\n  bool useIncremental_{false};\n  std::atomic<bool> enobuf_{false};\n  std::atomic<bool> wantsShutdown_{false};\n  std::atomic<uint32_t> enobufCount_{0};\n\n  // Cold fields\n  alignas(folly::hardware_constructive_interference_size) io_uring* ioRingPtr_;\n  uint32_t shutdownReferences_{0};\n  uint16_t const gid_{0};\n  uint32_t ringCount_{0};\n  uint32_t allSize_{0};\n  void* buffer_{nullptr};\n  uint32_t ringMemSize_{0};\n  uint32_t bufferSize_{0};\n};\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringRecv.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringRecv.h>\n\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/IoUringBackend.h>\n\nnamespace folly {\n\n#if FOLLY_HAS_LIBURING\n\nnamespace {\n/*\n * DetachedReadCallback\n */\n\nclass DetachedReadCallback : public IoUringRecvCallback {\n public:\n  void recvSuccess(std::unique_ptr<IOBuf> data) override {\n    if (!data_) {\n      data_ = std::move(data);\n    } else {\n      data_->appendToChain(std::move(data));\n    }\n  }\n\n  void recvEOF() noexcept override { done(); }\n  void recvErr(\n      int /*err*/,\n      std::unique_ptr<const AsyncSocketException> /*exception*/) noexcept\n      override {\n    done();\n  }\n\n  void done() {\n    promise.setValue(std::move(data_));\n    delete this;\n  }\n\n  Promise<std::unique_ptr<IOBuf>> promise;\n\n private:\n  std::unique_ptr<IOBuf> data_;\n};\n} // namespace\n\n/*\n * RecvRequest\n */\n\nclass IoUringRecvHandle::RecvRequest\n    : public IoSqeBase,\n      public DelayedDestruction {\n public:\n  using UniquePtr = std::unique_ptr<RecvRequest, Destructor>;\n\n  explicit RecvRequest(\n      IoUringBackend* backend,\n      NetworkSocket fd,\n      const SocketAddress& addr,\n      IoUringRecvHandle* handle)\n      : IoSqeBase(IoSqeBase::Type::Read),\n        fd_(fd),\n        handle_(handle),\n        handleGuard_(handle) {\n    if (backend->zcBufferPool() && !addr.isLoopbackAddress()) {\n      bufferPool_ = backend->zcBufferPool();\n    } else {\n      bufferRing_ = backend->bufferProvider();\n    }\n  }\n\n  void setRecvLen(size_t len) { recvLen_ = len; }\n\n  void prepRecvNormal(struct io_uring_sqe* sqe) {\n    if (recvLen_ > 0) {\n      ::io_uring_prep_recv(sqe, fd_.toFd(), nullptr, recvLen_, 0);\n    } else {\n      ::io_uring_prep_recv_multishot(sqe, fd_.toFd(), nullptr, 0, 0);\n    }\n    sqe->buf_group = bufferRing_->gid();\n    sqe->flags |= IOSQE_BUFFER_SELECT;\n  }\n\n  void prepRecvFallback(struct io_uring_sqe* sqe) {\n    size_t size = recvLen_ > 0 ? recvLen_ : goodMallocSize(16384);\n    fallbackBuffer_ = IOBuf::create(size);\n    ::io_uring_prep_recv(\n        sqe,\n        fd_.toFd(),\n        fallbackBuffer_->writableTail(),\n        fallbackBuffer_->tailroom(),\n        0);\n  }\n\n  /*\n   * IoSqeBase\n   */\n  void processSubmit(struct io_uring_sqe* sqe) noexcept override {\n    fallbackBuffer_.reset();\n\n    if (bufferPool_) {\n      ::io_uring_prep_rw(\n          IORING_OP_RECV_ZC,\n          sqe,\n          fd_.toFd(),\n          nullptr,\n          static_cast<uint32_t>(recvLen_),\n          0);\n      sqe->ioprio |= IORING_RECV_MULTISHOT;\n      return;\n    }\n\n    if (bufferRing_->available()) {\n      prepRecvNormal(sqe);\n    } else {\n      prepRecvFallback(sqe);\n    }\n  }\n\n  void callback(const struct io_uring_cqe* cqe) noexcept override {\n    DestructorGuard dg(this);\n    auto res = cqe->res;\n\n    if (res > 0) {\n      handle_->onRecvComplete(getData(cqe));\n      return;\n    }\n\n    if (res == -ECANCELED) {\n      // ignore\n      return;\n    } else if (res == -ENOBUFS) {\n      bufferRing_->enobuf();\n      handle_->onEnobufs();\n      return;\n    } else if (isEOF(cqe)) {\n      handle_->onRecvEOF();\n    } else {\n      handle_->onRecvErr(-res);\n    }\n  }\n\n  void callbackCancelled(const io_uring_cqe* cqe) noexcept override {\n    DestructorGuard dg(this);\n\n    if (cqe->res > 0) {\n      handle_->onRecvComplete(getData(cqe));\n    }\n\n    if (!(cqe->flags & IORING_CQE_F_MORE)) {\n      handle_->onRecvEOF();\n      destroy();\n    }\n  }\n\n private:\n  std::unique_ptr<IOBuf> getData(const struct io_uring_cqe* cqe) {\n    if (bufferPool_) {\n      const auto* rcqe = (struct io_uring_zcrx_cqe*)(cqe + 1);\n      return bufferPool_->getIoBuf(cqe, rcqe);\n    }\n\n    if (fallbackBuffer_) {\n      fallbackBuffer_->append(cqe->res);\n      return std::move(fallbackBuffer_);\n    }\n\n    return bufferRing_->getIoBuf(cqe);\n  }\n\n  bool isEOF(const struct io_uring_cqe* cqe) {\n    return cqe->res == 0 && (bufferPool_ ? cqe->flags == 0 : true);\n  }\n\n  NetworkSocket fd_;\n  IoUringRecvHandle* handle_;\n  DestructorGuard handleGuard_;\n\n  IoUringProvidedBufferRing* bufferRing_{nullptr};\n  IoUringZeroCopyBufferPool* bufferPool_{nullptr};\n  size_t recvLen_{0};\n  std::unique_ptr<IOBuf> fallbackBuffer_;\n};\n\n/*\n * IoUringRecvHandle\n */\n\nIoUringRecvHandle::UniquePtr IoUringRecvHandle::create(\n    EventBase* evb,\n    NetworkSocket fd,\n    const SocketAddress& addr,\n    IoUringRecvCallback* callback) {\n  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());\n  if (!backend) {\n    return nullptr;\n  }\n\n  if (!backend->zcBufferPool() && !backend->hasBufferProvider()) {\n    return nullptr;\n  }\n\n  return UniquePtr(new IoUringRecvHandle(backend, fd, addr, callback));\n}\n\nIoUringRecvHandle::UniquePtr IoUringRecvHandle::clone(\n    EventBase* evb,\n    NetworkSocket fd,\n    const SocketAddress& addr,\n    IoUringRecvCallback* callback,\n    UniquePtr other) {\n  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());\n  if (!backend) {\n    return nullptr;\n  }\n\n  if (!backend->zcBufferPool() && !backend->hasBufferProvider()) {\n    return nullptr;\n  }\n\n  return UniquePtr(new IoUringRecvHandle(\n      evb, backend, fd, addr, callback, std::move(other)));\n}\n\nIoUringRecvHandle::IoUringRecvHandle(\n    IoUringBackend* backend,\n    NetworkSocket fd,\n    const SocketAddress& addr,\n    IoUringRecvCallback* callback)\n    : backend_(backend),\n      recvCallback_(callback),\n      request_(\n          RecvRequest::UniquePtr(new RecvRequest(backend, fd, addr, this))) {}\n\nIoUringRecvHandle::IoUringRecvHandle(\n    EventBase* evb,\n    IoUringBackend* backend,\n    NetworkSocket fd,\n    const SocketAddress& addr,\n    IoUringRecvCallback* callback,\n    UniquePtr other)\n    : backend_(backend),\n      recvCallback_(callback),\n      request_(\n          RecvRequest::UniquePtr(new RecvRequest(backend, fd, addr, this))),\n      queuedReceivedData_(std::move(other->queuedReceivedData_)) {\n  if (other->pendingRead_.has_value()) {\n    setPendingRead(evb, std::move(*other->pendingRead_));\n  }\n}\n\nbool IoUringRecvHandle::update(uint16_t eventFlags) {\n  if (!readEnabled_ && eventFlags & EventHandler::READ) {\n    CHECK(!readEnabled_);\n    readEnabled_ = true;\n  } else if (readEnabled_ && !(eventFlags & EventHandler::READ)) {\n    CHECK(readEnabled_);\n    readEnabled_ = false;\n  }\n\n  if (pendingRead_) {\n    processPendingRead();\n  }\n\n  return true;\n}\n\nvoid IoUringRecvHandle::submit(size_t maxSize) {\n  CHECK(readEnabled_);\n  CHECK(request_);\n  // Some ReadCallbacks have a small peeking getReadBuffer() size, intended to\n  // peek a few bytes in the socket. For these, issue a non-multishot recv.\n  request_->setRecvLen(maxSize < kSmallRecvSize ? maxSize : 0);\n  if (pendingRead_) {\n    if (!pendingRead_->isReady()) {\n      return;\n    }\n    processPendingRead();\n  }\n\n  if (!request_->inFlight()) {\n    backend_->submitSoon(*request_);\n  }\n}\n\nbool IoUringRecvHandle::hasQueuedData() {\n  return queuedReceivedData_ && !queuedReceivedData_->empty();\n}\n\nstd::unique_ptr<IOBuf> IoUringRecvHandle::getQueuedData() {\n  return std::move(queuedReceivedData_);\n}\n\nvoid IoUringRecvHandle::detachEventBase() {\n  CHECK_NE(backend_, nullptr);\n\n  if (request_->inFlight()) {\n    auto* drc = new DetachedReadCallback();\n    auto thisPendingRead = drc->promise.getSemiFuture();\n\n    if (pendingRead_) {\n      pendingRead_ =\n          std::move(*pendingRead_)\n              .deferValue([currRead = std::move(thisPendingRead)](\n                              std::unique_ptr<IOBuf>&& prevData) mutable {\n                return std::move(currRead).deferValue(\n                    [data = std::move(prevData)](\n                        std::unique_ptr<IOBuf>&& currData) mutable {\n                      if (!data) {\n                        return std::move(currData);\n                      }\n                      if (currData) {\n                        data->appendToChain(std::move(currData));\n                      }\n                      return std::move(data);\n                    });\n              });\n    } else {\n      pendingRead_ = std::move(thisPendingRead);\n    }\n\n    backend_->cancel(request_.release());\n    recvCallback_ = drc;\n  } else {\n    request_.reset();\n    recvCallback_ = nullptr;\n  }\n\n  backend_ = nullptr;\n}\n\nvoid IoUringRecvHandle::cancel() {\n  if (request_->inFlight()) {\n    request_->setEventBase(nullptr);\n    backend_->cancel(request_.release());\n  } else {\n    request_.reset();\n  }\n  recvCallback_ = nullptr;\n  backend_ = nullptr;\n  return;\n}\n\nvoid IoUringRecvHandle::setPendingRead(EventBase* evb, PendingRead&& prevRead) {\n  pendingRead_ = std::move(prevRead).via(evb).thenValue(\n      [this, evb, dg = DestructorGuard(this)](auto&& prevData) {\n        if (backend_) {\n          evb->add([this, dg = DestructorGuard(this)] {\n            processPendingRead();\n          });\n        }\n        return std::move(prevData);\n      });\n}\n\nvoid IoUringRecvHandle::processPendingRead() {\n  if (!pendingRead_.has_value() || !backend_) {\n    return;\n  }\n\n  DestructorGuard dg(this);\n  if (pendingRead_->isReady()) {\n    auto data = std::move(*pendingRead_).get();\n    pendingRead_.reset();\n\n    if (!queuedReceivedData_) {\n      queuedReceivedData_ = std::move(data);\n    } else {\n      queuedReceivedData_->appendToChain(std::move(data));\n    }\n\n    if (readEnabled_) {\n      recvCallback_->recvSuccess(std::move(queuedReceivedData_));\n    }\n\n    if (backend_ && readEnabled_ && !request_->inFlight()) {\n      backend_->submitSoon(*request_);\n    }\n  }\n}\n\nvoid IoUringRecvHandle::onRecvComplete(std::unique_ptr<IOBuf> data) {\n  DestructorGuard dg(this);\n  if (backend_ == nullptr) {\n    CHECK(!request_);\n    if (recvCallback_) {\n      // DetachedReadCallback\n      recvCallback_->recvSuccess(std::move(data));\n    }\n    return;\n  }\n\n  if (readEnabled_) {\n    CHECK(!queuedReceivedData_);\n    recvCallback_->recvSuccess(std::move(data));\n  } else {\n    if (!queuedReceivedData_) {\n      queuedReceivedData_ = std::move(data);\n    } else {\n      queuedReceivedData_->appendToChain(std::move(data));\n    }\n  }\n\n  if (backend_ && readEnabled_ && !request_->inFlight()) {\n    backend_->submitSoon(*request_);\n  }\n}\n\nvoid IoUringRecvHandle::onEnobufs() {\n  CHECK(!request_->inFlight());\n  if (backend_ && readEnabled_) {\n    backend_->submitSoon(*request_);\n  }\n}\n\nvoid IoUringRecvHandle::onRecvEOF() {\n  DestructorGuard dg(this);\n  if (recvCallback_) {\n    recvCallback_->recvEOF();\n  }\n}\n\nvoid IoUringRecvHandle::onRecvErr(int err) {\n  DestructorGuard dg(this);\n  if (recvCallback_) {\n    recvCallback_->recvErr(err, nullptr);\n  }\n}\n\n#else\n\nclass IoUringRecvHandle::RecvRequest : public DelayedDestruction {};\n\nIoUringRecvHandle::UniquePtr IoUringRecvHandle::create(\n    EventBase* /*evb*/,\n    NetworkSocket /*fd*/,\n    const SocketAddress& /*addr*/,\n    IoUringRecvCallback* /*callback*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nIoUringRecvHandle::UniquePtr IoUringRecvHandle::clone(\n    EventBase* /*evb*/,\n    NetworkSocket /*fd*/,\n    const SocketAddress& /*addr*/,\n    IoUringRecvCallback* /*callback*/,\n    IoUringRecvHandle::UniquePtr /*old*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nIoUringRecvHandle::IoUringRecvHandle(\n    IoUringBackend* /*backend*/,\n    NetworkSocket /*fd*/,\n    const SocketAddress& /*addr*/,\n    IoUringRecvCallback* /*callback*/) {\n  (void)backend_;\n  (void)recvCallback_;\n  (void)request_;\n  (void)queuedReceivedData_;\n  (void)readEnabled_;\n  (void)pendingRead_;\n}\n\nIoUringRecvHandle::IoUringRecvHandle(\n    EventBase* /*evb*/,\n    IoUringBackend* /*backend*/,\n    NetworkSocket /*fd*/,\n    const SocketAddress& /*addr*/,\n    IoUringRecvCallback* /*callback*/,\n    UniquePtr /*other*/) {\n  (void)backend_;\n  (void)recvCallback_;\n  (void)request_;\n  (void)queuedReceivedData_;\n  (void)readEnabled_;\n  (void)pendingRead_;\n}\n\nbool IoUringRecvHandle::update(uint16_t /*eventFlags*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::submit(size_t /*maxSize*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nbool IoUringRecvHandle::hasQueuedData() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nstd::unique_ptr<IOBuf> IoUringRecvHandle::getQueuedData() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::detachEventBase() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::cancel() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::setPendingRead(\n    EventBase* /*evb*/, PendingRead&& /*future*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::processPendingRead() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::onRecvComplete(std::unique_ptr<IOBuf> /*data*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::onEnobufs() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::onRecvEOF() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringRecvHandle::onRecvErr(int /*err*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringRecv.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/SocketAddress.h>\n#include <folly/futures/Future.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/IoUringBase.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly {\n\nclass EventBase;\nclass IoUringBackend;\n\nclass IoUringRecvCallback {\n public:\n  virtual ~IoUringRecvCallback() = default;\n\n  virtual void recvSuccess(std::unique_ptr<IOBuf> data) = 0;\n  virtual void recvEOF() noexcept = 0;\n  virtual void recvErr(\n      int err,\n      std::unique_ptr<const AsyncSocketException> exception) noexcept = 0;\n};\n\nclass IoUringRecvHandle : public DelayedDestruction {\n public:\n  using UniquePtr = std::unique_ptr<IoUringRecvHandle, Destructor>;\n  using PendingRead = SemiFuture<std::unique_ptr<IOBuf>>;\n  using PendingReadMaybe = Optional<PendingRead>;\n\n  static constexpr size_t kSmallRecvSize = 4096;\n\n  static UniquePtr create(\n      EventBase* evb,\n      NetworkSocket fd,\n      const SocketAddress& addr,\n      IoUringRecvCallback* callback);\n\n  static UniquePtr clone(\n      EventBase* evb,\n      NetworkSocket fd,\n      const SocketAddress& addr,\n      IoUringRecvCallback* callback,\n      UniquePtr old);\n\n  bool update(uint16_t eventFlags);\n  void submit(size_t maxSize);\n\n  bool hasQueuedData();\n  std::unique_ptr<IOBuf> getQueuedData();\n\n  void detachEventBase();\n  void cancel();\n\n private:\n  explicit IoUringRecvHandle(\n      IoUringBackend* backend,\n      NetworkSocket fd,\n      const SocketAddress& addr,\n      IoUringRecvCallback* callback);\n\n  explicit IoUringRecvHandle(\n      EventBase* evb,\n      IoUringBackend* backend,\n      NetworkSocket fd,\n      const SocketAddress& addr,\n      IoUringRecvCallback* callback,\n      UniquePtr other);\n\n  void setPendingRead(EventBase* evb, PendingRead&& future);\n  void processPendingRead();\n\n  class RecvRequest;\n  void onRecvComplete(std::unique_ptr<IOBuf> data);\n  void onEnobufs();\n  void onRecvEOF();\n  void onRecvErr(int err);\n\n  IoUringBackend* backend_;\n  IoUringRecvCallback* recvCallback_;\n  std::unique_ptr<RecvRequest, DelayedDestruction::Destructor> request_;\n\n  std::unique_ptr<IOBuf> queuedReceivedData_;\n  bool readEnabled_{false};\n  PendingReadMaybe pendingRead_{folly::none};\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringSend.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringSend.h>\n\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/IoUringBackend.h>\n\nnamespace folly {\n\n#if FOLLY_HAS_LIBURING\n\n/*\n * SendRequest\n */\n\nclass IoUringSendHandle::SendRequest : public IoSqeBase {\n public:\n  static void* alloc(size_t iovCount) {\n    size_t bufSize = 0;\n    if (!checked_muladd<size_t>(\n            &bufSize, iovCount, sizeof(struct iovec), sizeof(SendRequest))) {\n      throw std::bad_alloc();\n    }\n    void* buf = malloc(bufSize);\n    if (buf == nullptr) {\n      throw std::bad_alloc();\n    }\n    return buf;\n  }\n\n  explicit SendRequest(\n      AsyncWriter::WriteCallback* callback,\n      const struct iovec* iov,\n      size_t iovCount,\n      size_t partialWritten,\n      size_t bytesWritten,\n      std::unique_ptr<IOBuf> data,\n      WriteFlags flags,\n      NetworkSocket fd)\n      : IoSqeBase(IoSqeBase::Type::Write),\n        callback_(callback),\n        iovRemaining_(iovCount),\n        bytesWritten_(bytesWritten),\n        data_(std::move(data)),\n        flags_(flags),\n        fd_(fd) {\n    if (data_) {\n      CHECK_EQ(data_->countChainElements(), iovCount);\n    }\n    memcpy(iov_, iov, sizeof(struct iovec) * iovCount);\n    msg_.msg_iov = iov_;\n    msg_.msg_iovlen = std::min<size_t>(iovRemaining_, kIovMax);\n\n    iov_->iov_base =\n        reinterpret_cast<uint8_t*>(iov_->iov_base) + partialWritten;\n    iov_->iov_len -= partialWritten;\n  }\n\n  void destroy() {\n    if (!cancelled() && handle_) {\n      handle_->onReleaseIOBuf(\n          std::move(data_), callback_->getReleaseIOBufCallback());\n    }\n    this->~SendRequest();\n    free(this);\n  }\n\n  void setHandle(IoUringSendHandle* handle) {\n    handle_ = handle;\n    handleGuard_ = DestructorGuard(handle);\n  }\n  SendRequest* getNext() { return next_; }\n  void append(SendRequest* request) { next_ = request; }\n  AsyncWriter::WriteCallback* getCallback() { return callback_; }\n  size_t getTotalBytesWritten() { return bytesWritten_; }\n\n  void releaseIOBuf(IoUringSendHandle* handle) {\n    CHECK(!cancelled());\n    handle->onReleaseIOBuf(\n        std::move(data_), callback_->getReleaseIOBufCallback());\n  }\n\n  folly::SemiFuture<int> detachEventBase() {\n    handle_ = nullptr;\n    setEventBase(nullptr);\n\n    auto [promise, future] = makePromiseContract<int>();\n    detachedSignal_ = [p = std::move(promise)](int res) mutable {\n      p.setValue(res);\n    };\n    return std::move(future);\n  }\n\n  SendRequest* clone(IoUringSendHandle* newHandle) {\n    CHECK(handle_ == nullptr);\n    void* buf = alloc(msg_.msg_iovlen);\n    auto clone = new (buf) SendRequest(\n        callback_,\n        msg_.msg_iov,\n        msg_.msg_iovlen,\n        0,\n        bytesWritten_,\n        std::move(data_),\n        flags_,\n        fd_);\n    clone->append(next_);\n    if (inFlight()) {\n      clone->internalMarkInflight(true);\n      clone->setHandle(newHandle);\n    }\n    return clone;\n  }\n\n  /*\n   * IoSqeBase\n   */\n  void processSubmit(struct io_uring_sqe* sqe) noexcept override {\n    ::io_uring_prep_sendmsg(sqe, fd_.toFd(), &msg_, flags());\n    handle_->onSendStarted();\n  }\n\n  void callback(const struct io_uring_cqe* cqe) noexcept override {\n    auto res = cqe->res;\n\n    if (!handle_) {\n      detachedSignal_(res);\n      return;\n    }\n\n    if (cancelled()) {\n      return;\n    }\n\n    if (res >= 0) {\n      consumeBytes(res);\n      if (msg_.msg_iovlen > 0) {\n        prepareForReuse();\n        handle_->onSendPartial(res);\n      } else {\n        handle_->onSendComplete(res);\n      }\n    } else {\n      handle_->onSendErr(-res);\n    }\n  }\n\n  void callbackCancelled(const io_uring_cqe*) noexcept override { destroy(); }\n\n private:\n  int flags() {\n    int msg_flags = MSG_NOSIGNAL;\n\n    if (isSet(flags_, WriteFlags::CORK)) {\n      // MSG_MORE tells the kernel we have more data to send, so wait for us to\n      // give it the rest of the data rather than immediately sending a partial\n      // frame, even when TCP_NODELAY is enabled.\n      msg_flags |= MSG_MORE;\n    }\n\n    if (isSet(flags_, WriteFlags::EOR)) {\n      // marks that this is the last byte of a record (response)\n      msg_flags |= MSG_EOR;\n    }\n\n    return msg_flags;\n  }\n\n  void consumeBytes(size_t bytes) {\n    bytesWritten_ += bytes;\n\n    while (bytes > 0 && iovRemaining_ > 0) {\n      if (msg_.msg_iov->iov_len > bytes) {\n        msg_.msg_iov->iov_base =\n            static_cast<char*>(msg_.msg_iov->iov_base) + bytes;\n        msg_.msg_iov->iov_len -= bytes;\n        break;\n      }\n\n      bytes -= msg_.msg_iov->iov_len;\n      ++msg_.msg_iov;\n      --iovRemaining_;\n\n      // There is a 1:1 relationship between IOBufs and iovecs.\n      if (data_) {\n        auto next = data_->pop();\n        handle_->onReleaseIOBuf(\n            std::move(data_), callback_->getReleaseIOBufCallback());\n        data_ = std::move(next);\n      }\n    }\n\n    msg_.msg_iovlen = std::min<size_t>(iovRemaining_, kIovMax);\n  }\n\n  AsyncWriter::WriteCallback* callback_;\n  size_t iovRemaining_;\n  size_t bytesWritten_;\n  std::unique_ptr<IOBuf> data_;\n  WriteFlags flags_;\n  NetworkSocket fd_;\n\n  IoUringSendHandle* handle_{nullptr};\n  DestructorGuard handleGuard_{nullptr};\n  SendRequest* next_{nullptr};\n  struct msghdr msg_{};\n  folly::Function<void(int)> detachedSignal_;\n\n  struct iovec iov_[];\n};\n\n/*\n * IoUringSendHandle\n */\n\nIoUringSendHandle::UniquePtr IoUringSendHandle::create(\n    EventBase* evb,\n    NetworkSocket fd,\n    const folly::SocketAddress& addr,\n    IoUringSendCallback* callback) {\n  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());\n  if (!backend) {\n    return nullptr;\n  }\n\n  return UniquePtr(new IoUringSendHandle(evb, backend, fd, addr, callback));\n}\n\nIoUringSendHandle::UniquePtr IoUringSendHandle::clone(\n    EventBase* evb, IoUringSendHandle::UniquePtr other) {\n  auto* backend = dynamic_cast<IoUringBackend*>(evb->getBackend());\n  if (!backend) {\n    return nullptr;\n  }\n\n  return UniquePtr(new IoUringSendHandle(evb, backend, std::move(other)));\n}\n\nIoUringSendHandle::IoUringSendHandle(\n    EventBase* evb,\n    IoUringBackend* backend,\n    NetworkSocket fd,\n    const folly::SocketAddress& addr,\n    IoUringSendCallback* callback)\n    : evb_(evb),\n      backend_(backend),\n      fd_(fd),\n      addr_(addr),\n      sendCallback_(callback) {}\n\nIoUringSendHandle::IoUringSendHandle(\n    EventBase* evb, IoUringBackend* backend, IoUringSendHandle::UniquePtr other)\n    : evb_(evb),\n      backend_(backend),\n      fd_(other->fd_),\n      addr_(other->addr_),\n      sendCallback_(other->sendCallback_) {\n  if (!other->empty()) {\n    auto* oldReq = other->requestHead_;\n    auto* newReq = oldReq->clone(this);\n    requestHead_ = newReq;\n    requestTail_ = newReq->getNext() == nullptr ? newReq : other->requestTail_;\n\n    if (other->detachedFuture_.has_value()) {\n      CHECK(oldReq->inFlight());\n      CHECK(newReq->inFlight());\n      std::move(*other->detachedFuture_)\n          .via(evb)\n          .thenValue([oldReq, newReq, evb](int res) {\n            // The result res is from detachSignal_ in the previous request\n            oldReq->destroy();\n            struct io_uring_cqe cqe{};\n            cqe.res = res;\n            evb->bumpHandlingTime();\n            if (newReq->cancelled()) {\n              newReq->callbackCancelled(&cqe);\n            } else {\n              newReq->callback(&cqe);\n            }\n          });\n    }\n  }\n}\n\nvoid IoUringSendHandle::detachEventBase() {\n  CHECK(!sendEnabled_);\n  evb_ = nullptr;\n  backend_ = nullptr;\n\n  if (requestHead_ && requestHead_->inFlight()) {\n    detachedFuture_ = requestHead_->detachEventBase();\n  }\n}\n\nbool IoUringSendHandle::update(uint16_t eventFlags) {\n  if (!sendEnabled_ && (eventFlags & EventHandler::WRITE)) {\n    sendEnabled_ = true;\n    trySubmit();\n  } else if (sendEnabled_ && !(eventFlags & EventHandler::WRITE)) {\n    sendEnabled_ = false;\n  }\n\n  return true;\n}\n\nvoid IoUringSendHandle::write(\n    AsyncWriter::WriteCallback* callback,\n    const struct iovec* iov,\n    size_t iovCount,\n    size_t partialWritten,\n    size_t bytesWritten,\n    std::unique_ptr<IOBuf> data,\n    WriteFlags flags) {\n  CHECK_GT(iovCount, 0);\n  void* buf = SendRequest::alloc(iovCount);\n  auto req = new (buf) SendRequest(\n      callback,\n      iov,\n      iovCount,\n      partialWritten,\n      bytesWritten,\n      std::move(data),\n      flags,\n      fd_);\n  req->setEventBase(evb_);\n\n  if (requestTail_ == nullptr) {\n    CHECK(requestHead_ == nullptr);\n    requestHead_ = requestTail_ = req;\n    trySubmit();\n  } else {\n    requestTail_->append(req);\n    requestTail_ = req;\n  }\n}\n\nvoid IoUringSendHandle::failWrite(const AsyncSocketException& ex) {\n  if (requestHead_ != nullptr) {\n    auto req = requestHead_;\n    requestHead_ = req->getNext();\n    auto* callback = req->getCallback();\n    auto bytesWritten = req->getTotalBytesWritten();\n\n    req->releaseIOBuf(this);\n    if (req->inFlight()) {\n      backend_->cancel(req);\n    } else {\n      req->destroy();\n    }\n\n    if (callback) {\n      callback->writeErr(bytesWritten, ex);\n    }\n  }\n}\n\nvoid IoUringSendHandle::failAllWrites(const AsyncSocketException& ex) {\n  while (requestHead_ != nullptr) {\n    failWrite(ex);\n  }\n}\n\nvoid IoUringSendHandle::trySubmit() {\n  if (sendEnabled_ && requestHead_ && !requestHead_->inFlight()) {\n    requestHead_->setHandle(this);\n    backend_->submitSoon(*requestHead_);\n  }\n}\n\nvoid IoUringSendHandle::onSendStarted() {\n  requestHead_->getCallback()->writeStarting();\n}\n\nvoid IoUringSendHandle::onSendPartial(size_t bytesWritten) {\n  CHECK(requestHead_ != nullptr);\n  sendCallback_->sendPartial(bytesWritten);\n  trySubmit();\n}\n\nvoid IoUringSendHandle::onSendComplete(size_t bytesWritten) {\n  DestructorGuard dg(this);\n  CHECK(requestHead_ != nullptr);\n  auto req = requestHead_;\n  requestHead_ = req->getNext();\n  if (requestHead_ == nullptr) {\n    requestTail_ = nullptr;\n    sendCallback_->sendDone(bytesWritten);\n    // sets sendEnabled_ to false\n  }\n\n  auto* callback = req->getCallback();\n  req->destroy();\n  if (callback) {\n    callback->writeSuccess();\n  }\n\n  trySubmit();\n}\n\nvoid IoUringSendHandle::onSendErr(int err) {\n  sendCallback_->sendErr(err);\n}\n\nvoid IoUringSendHandle::onReleaseIOBuf(\n    std::unique_ptr<IOBuf> data, AsyncWriter::ReleaseIOBufCallback* callback) {\n  sendCallback_->releaseIOBuf(std::move(data), callback);\n}\n\n#else\n\nIoUringSendHandle::UniquePtr IoUringSendHandle::create(\n    EventBase* /*evb*/,\n    NetworkSocket /*fd*/,\n    const folly::SocketAddress& /*addr*/,\n    IoUringSendCallback* /*callback*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nIoUringSendHandle::IoUringSendHandle(\n    EventBase* /*evb*/,\n    IoUringBackend* /*backend*/,\n    NetworkSocket /*fd*/,\n    const folly::SocketAddress& /*addr*/,\n    IoUringSendCallback* /*callback*/) {\n  (void)evb_;\n  (void)backend_;\n  (void)fd_;\n  (void)addr_;\n  (void)sendCallback_;\n  (void)requestHead_;\n  (void)requestTail_;\n  (void)sendEnabled_;\n  (void)detachedFuture_;\n}\n\nIoUringSendHandle::UniquePtr IoUringSendHandle::clone(\n    EventBase* /*evb*/, IoUringSendHandle::UniquePtr /*other*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nIoUringSendHandle::IoUringSendHandle(\n    EventBase* /*evb*/,\n    IoUringBackend* /*backend*/,\n    IoUringSendHandle::UniquePtr /*other*/) {\n  (void)evb_;\n  (void)backend_;\n  (void)fd_;\n  (void)addr_;\n  (void)sendCallback_;\n  (void)requestHead_;\n  (void)requestTail_;\n  (void)sendEnabled_;\n  (void)detachedFuture_;\n}\n\nvoid IoUringSendHandle::detachEventBase() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nbool IoUringSendHandle::update(uint16_t /*eventFlags*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::write(\n    AsyncWriter::WriteCallback* /*callback*/,\n    const struct iovec* /*iov*/,\n    size_t /*iovCount*/,\n    size_t /*partialWritten*/,\n    size_t /*bytesWritten*/,\n    std::unique_ptr<IOBuf> /*data*/,\n    WriteFlags /*flags*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::failWrite(const AsyncSocketException& /*ex*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::failAllWrites(const AsyncSocketException& /*ex*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::trySubmit() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::onSendStarted() {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::onSendPartial(size_t /*bytesWritten*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::onSendComplete(size_t /*bytesWritten*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::onSendErr(int /*err*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\nvoid IoUringSendHandle::onReleaseIOBuf(\n    std::unique_ptr<IOBuf> /*data*/,\n    AsyncWriter::ReleaseIOBufCallback* /*callback*/) {\n  folly::terminate_with<std::runtime_error>(\"io_uring not supported\");\n}\n\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringSend.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/futures/Future.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/IoUringBase.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly {\n\nclass IoUringSendCallback {\n public:\n  virtual ~IoUringSendCallback() = default;\n\n  virtual void sendPartial(size_t bytesWritten) = 0;\n  virtual void sendDone(size_t bytesWritten) = 0;\n  virtual void sendErr(int err) = 0;\n  virtual void releaseIOBuf(\n      std::unique_ptr<IOBuf> buf,\n      AsyncWriter::ReleaseIOBufCallback* callback) = 0;\n};\n\nclass EventBase;\nclass IoUringBackend;\n\nclass IoUringSendHandle : public DelayedDestruction {\n public:\n  using UniquePtr = std::unique_ptr<IoUringSendHandle, Destructor>;\n\n  static UniquePtr create(\n      EventBase* evb,\n      NetworkSocket fd,\n      const SocketAddress& addr,\n      IoUringSendCallback* callback);\n\n  static UniquePtr clone(EventBase* evb, IoUringSendHandle::UniquePtr other);\n  void detachEventBase();\n\n  bool update(uint16_t eventFlags);\n  void write(\n      AsyncWriter::WriteCallback* callback,\n      const struct iovec* iov,\n      size_t iovCount,\n      size_t partialWritten,\n      size_t bytesWritten,\n      std::unique_ptr<IOBuf> data,\n      WriteFlags flags);\n\n  void failWrite(const AsyncSocketException& ex);\n  void failAllWrites(const AsyncSocketException& ex);\n\n  bool empty() { return requestHead_ == nullptr; }\n\n private:\n  explicit IoUringSendHandle(\n      EventBase* evb,\n      IoUringBackend* backend,\n      NetworkSocket fd,\n      const SocketAddress& addr,\n      IoUringSendCallback* callback);\n\n  explicit IoUringSendHandle(\n      EventBase* evb, IoUringBackend* backend, UniquePtr other);\n\n  void trySubmit();\n\n  class SendRequest;\n  void onSendStarted();\n  void onSendPartial(size_t bytesWritten);\n  void onSendComplete(size_t bytesWritten);\n  void onSendErr(int err);\n  void onReleaseIOBuf(\n      std::unique_ptr<IOBuf> data, AsyncWriter::ReleaseIOBufCallback* callback);\n\n  EventBase* evb_;\n  IoUringBackend* backend_;\n  NetworkSocket fd_;\n  SocketAddress addr_;\n  IoUringSendCallback* sendCallback_;\n\n  SendRequest* requestHead_{nullptr};\n  SendRequest* requestTail_{nullptr};\n\n  bool sendEnabled_{false};\n  Optional<SemiFuture<int>> detachedFuture_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/IoUringZeroCopyBufferPool.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringZeroCopyBufferPool.h>\n\n#include <mutex>\n#include <queue>\n\n#include <folly/Conv.h>\n#include <folly/String.h>\n#include <folly/lang/Align.h>\n#include <folly/portability/SysMman.h>\n#include <folly/synchronization/DistributedMutex.h>\n\n#if FOLLY_HAS_LIBURING\n\nnamespace folly {\n\nclass IoUringZeroCopyBufferPoolImpl {\n public:\n  friend class IoUringZeroCopyBufferPool;\n\n  struct Buffer {\n    uint64_t off;\n    uint32_t len;\n    IoUringZeroCopyBufferPoolImpl* pool;\n  };\n\n  explicit IoUringZeroCopyBufferPoolImpl(\n      IoUringZeroCopyBufferPool::Params params, bool test);\n\n  ~IoUringZeroCopyBufferPoolImpl();\n\n  IoUringZeroCopyBufferPoolImpl(IoUringZeroCopyBufferPoolImpl&&) = delete;\n  IoUringZeroCopyBufferPoolImpl(IoUringZeroCopyBufferPoolImpl const&) = delete;\n  IoUringZeroCopyBufferPoolImpl& operator=(IoUringZeroCopyBufferPoolImpl&&) =\n      delete;\n  IoUringZeroCopyBufferPoolImpl& operator=(\n      IoUringZeroCopyBufferPoolImpl const&) = delete;\n\n  void destroy() noexcept;\n\n  std::unique_ptr<IOBuf> getIoBuf(\n      const struct io_uring_cqe* cqe,\n      const struct io_uring_zcrx_cqe* rcqe) noexcept;\n  void returnBuffer(Buffer* buf) noexcept;\n\n  // For testing\n  uint32_t* getHead() const noexcept { return rqRing_.khead; }\n  uint32_t getRingUsedCount() const noexcept {\n    return rqTail_ - io_uring_smp_load_acquire(rqRing_.khead);\n  }\n  uint32_t getRingFreeCount() const noexcept {\n    return rqEntries_ - getRingUsedCount();\n  }\n  size_t getPendingBuffersSize() const noexcept {\n    return pendingBuffers_.size();\n  }\n  uint32_t getFlushThreshold() const noexcept { return flushThreshold_; }\n  uint16_t getAndResetFlushFailures() noexcept {\n    return flushFailures_.exchange(0, std::memory_order_relaxed);\n  }\n  uint16_t getAndResetFlushCount() noexcept {\n    return flushCount_.exchange(0, std::memory_order_relaxed);\n  }\n\n private:\n  void mapMemory();\n  void initialRegister(uint32_t ifindex, uint16_t queueId);\n  void delayedDestroy(uint32_t refs) noexcept;\n  uint32_t getRingQueuedCount() const noexcept;\n  void writeBufferToRing(Buffer* buffer) noexcept;\n  void flushRefillQueue() noexcept;\n\n  struct io_uring* ring_{nullptr};\n  size_t pageSize_{0};\n  uint32_t rqEntries_{0};\n\n  void* bufArea_{nullptr};\n  size_t bufAreaSize_{0};\n  std::vector<Buffer> buffers_;\n  void* rqRingArea_{nullptr};\n  size_t rqRingAreaSize_{0};\n  struct io_uring_zcrx_rq rqRing_{};\n  uint64_t rqAreaToken_{0};\n  uint32_t rqTail_{0};\n  unsigned rqMask_{0};\n  uint32_t id_{0};\n  std::atomic<uint32_t> bufDispensed_{0};\n\n  folly::DistributedMutex mutex_;\n  std::atomic<bool> wantsShutdown_{false};\n  uint32_t shutdownReferences_{0};\n  std::queue<Buffer*> pendingBuffers_;\n  uint32_t flushThreshold_{128};\n  std::atomic<uint16_t> flushFailures_{0};\n  std::atomic<uint16_t> flushCount_{0};\n};\n\nstruct ImplDeleter {\n  void operator()(IoUringZeroCopyBufferPoolImpl* impl) const noexcept {\n    if (impl) {\n      impl->destroy();\n    }\n  }\n};\n\nnamespace {\nstruct RingQueue {\n  uint32_t head;\n  uint32_t tail;\n};\n\nsize_t getRefillRingSize(size_t rqEntries) {\n  size_t size = rqEntries * sizeof(struct io_uring_zcrx_rqe);\n  // Include space for the header (head/tail/etc.)\n  size_t pageSize = sysconf(_SC_PAGESIZE);\n  size += pageSize;\n  return folly::align_ceil(size, pageSize);\n}\n\nconstexpr uint64_t kBufferMask = (1ULL << IORING_ZCRX_AREA_SHIFT) - 1;\n\n} // namespace\n\nIoUringZeroCopyBufferPoolImpl::IoUringZeroCopyBufferPoolImpl(\n    IoUringZeroCopyBufferPool::Params params, bool test)\n    : ring_(params.ring),\n      pageSize_(params.pageSize),\n      rqEntries_(params.rqEntries),\n      bufAreaSize_(params.numPages * params.pageSize),\n      buffers_(params.numPages),\n      rqRingAreaSize_(getRefillRingSize(params.rqEntries)) {\n  for (auto& buf : buffers_) {\n    buf.pool = this;\n  }\n  mapMemory();\n\n  // Calculate flush threshold to bound pending queue growth.\n  //\n  // When pending buffers reach this threshold, we flush to the kernel which\n  // drains entries from the refill ring, freeing space for pending buffers.\n  //\n  // Threshold = max(128, min(rqEntries/4, numPages/20)):\n  // - rqEntries/4 (25% of ring): scales with kernel's drain capacity per flush\n  // - numPages/20 (5% of pool): caps memory sitting idle in pending queue\n  // - min(): use the smaller limit to respect both constraints\n  // - max(128): floor of 4 × ZCRX_FLUSH_BATCH to ensure batching efficiency\n  constexpr uint32_t kKernelFlushBatch = 32;\n  constexpr uint32_t kMinFlushThreshold = 4 * kKernelFlushBatch;\n  flushThreshold_ = std::max(\n      kMinFlushThreshold,\n      std::min(\n          static_cast<uint32_t>(rqEntries_ / 4), // 25% of ring\n          static_cast<uint32_t>(params.numPages / 20))); // 5% of pool\n\n  if (!test) {\n    initialRegister(params.ifindex, params.queueId);\n  } else {\n    // rqRing_ is normally set up using information that the kernel fills in via\n    // io_uring_register_ifq(). Unit tests do not do this, so fake it.\n    rqRing_.khead = reinterpret_cast<uint32_t*>(\n        (static_cast<char*>(rqRingArea_) + offsetof(RingQueue, head)));\n    rqRing_.ktail = reinterpret_cast<uint32_t*>(\n        (static_cast<char*>(rqRingArea_) + offsetof(RingQueue, tail)));\n    rqRing_.rqes = reinterpret_cast<struct io_uring_zcrx_rqe*>(\n        static_cast<char*>(rqRingArea_) + sizeof(RingQueue));\n    rqRing_.rq_tail = 0;\n    rqRing_.ring_entries = rqEntries_;\n  }\n}\n\nIoUringZeroCopyBufferPoolImpl::~IoUringZeroCopyBufferPoolImpl() {\n  ::munmap(bufArea_, bufAreaSize_ + rqRingAreaSize_);\n}\n\nvoid IoUringZeroCopyBufferPoolImpl::destroy() noexcept {\n  std::unique_lock lock{mutex_};\n  DCHECK(bufDispensed_ >= rqTail_);\n  auto remaining = bufDispensed_.load(std::memory_order_relaxed) - rqTail_;\n  // Drain refs in overflow queue\n  remaining -= pendingBuffers_.size();\n  shutdownReferences_ = remaining;\n  wantsShutdown_ = true;\n  lock.unlock();\n  delayedDestroy(remaining);\n}\n\nstd::unique_ptr<IOBuf> IoUringZeroCopyBufferPoolImpl::getIoBuf(\n    const struct io_uring_cqe* cqe,\n    const struct io_uring_zcrx_cqe* rcqe) noexcept {\n  // By the time the pool is being destroyed, IoUringBackend has already drained\n  // all requests so there won't be any more calls to getIoBuf().\n  DCHECK(!wantsShutdown_);\n  auto offset = (rcqe->off & kBufferMask);\n  auto length = static_cast<uint32_t>(cqe->res);\n\n  auto freeFn = [](void*, void* userData) {\n    auto buffer =\n        reinterpret_cast<IoUringZeroCopyBufferPoolImpl::Buffer*>(userData);\n    DCHECK(buffer->pool);\n    buffer->pool->returnBuffer(buffer);\n  };\n\n  int i = offset / pageSize_;\n  auto& buf = buffers_[i];\n  buf.off = rcqe->off;\n  buf.len = cqe->res;\n\n  auto ret = IOBuf::takeOwnership(\n      static_cast<char*>(bufArea_) + offset,\n      pageSize_,\n      length,\n      freeFn,\n      &buffers_[i]);\n  // The underlying buffers are shared between userspace and kernel. The IOBufs\n  // only 'wrap' the data and is read-only. Mark as shared such that downstream\n  // users of this IOBuf do not try to destructively modify the data.\n  ret->markExternallySharedOne();\n  bufDispensed_.fetch_add(1, std::memory_order_relaxed);\n  return ret;\n}\n\nvoid IoUringZeroCopyBufferPoolImpl::mapMemory() {\n  bufArea_ = ::mmap(\n      nullptr,\n      bufAreaSize_ + rqRingAreaSize_,\n      PROT_READ | PROT_WRITE,\n      MAP_ANONYMOUS | MAP_PRIVATE,\n      -1,\n      0);\n  if (bufArea_ == MAP_FAILED) {\n    throw std::runtime_error(\n        folly::to<std::string>(\n            \"IoUringZeroCopyBufferPool failed to mmap size \",\n            bufAreaSize_ + rqRingAreaSize_));\n  }\n\n  rqRingArea_ = static_cast<char*>(bufArea_) + bufAreaSize_;\n}\n\nvoid IoUringZeroCopyBufferPoolImpl::initialRegister(\n    uint32_t ifindex, uint16_t queueId) {\n  struct io_uring_region_desc regionReg{};\n  regionReg.user_addr = reinterpret_cast<uint64_t>(rqRingArea_);\n  regionReg.size = rqRingAreaSize_;\n  regionReg.flags = IORING_MEM_REGION_TYPE_USER;\n\n  struct io_uring_zcrx_area_reg areaReg{};\n  areaReg.addr = reinterpret_cast<uint64_t>(bufArea_);\n  areaReg.len = bufAreaSize_;\n\n  struct io_uring_zcrx_ifq_reg ifqReg{};\n  ifqReg.if_idx = ifindex;\n  ifqReg.if_rxq = queueId;\n  ifqReg.rq_entries = rqEntries_;\n  ifqReg.area_ptr = reinterpret_cast<uint64_t>(&areaReg);\n  ifqReg.region_ptr = reinterpret_cast<uint64_t>(&regionReg);\n\n  auto ret = io_uring_register_ifq(ring_, &ifqReg);\n  if (ret) {\n    ::munmap(bufArea_, bufAreaSize_ + rqRingAreaSize_);\n    throw std::runtime_error(\n        fmt::format(\n            \"IoUringZeroCopyBufferPool failed io_uring_register_ifq: {} {}\",\n            ret,\n            folly::errnoStr(ret)));\n  }\n\n  rqRing_.khead = reinterpret_cast<uint32_t*>(\n      (static_cast<char*>(rqRingArea_) + ifqReg.offsets.head));\n  rqRing_.ktail = reinterpret_cast<uint32_t*>(\n      (static_cast<char*>(rqRingArea_) + ifqReg.offsets.tail));\n  rqRing_.rqes = reinterpret_cast<struct io_uring_zcrx_rqe*>(\n      static_cast<char*>(rqRingArea_) + ifqReg.offsets.rqes);\n  rqRing_.rq_tail = 0;\n  rqRing_.ring_entries = ifqReg.rq_entries;\n\n  rqAreaToken_ = areaReg.rq_area_token;\n  rqMask_ = ifqReg.rq_entries - 1;\n  id_ = ifqReg.zcrx_id;\n}\n\nuint32_t IoUringZeroCopyBufferPoolImpl::getRingQueuedCount() const noexcept {\n  return rqTail_ - io_uring_smp_load_acquire(rqRing_.khead);\n}\n\nvoid IoUringZeroCopyBufferPoolImpl::writeBufferToRing(Buffer* buffer) noexcept {\n  uint32_t myTail = rqTail_++;\n\n  struct io_uring_zcrx_rqe* rqe = &rqRing_.rqes[myTail & rqMask_];\n  rqe->off = (buffer->off & ~IORING_ZCRX_AREA_MASK) | rqAreaToken_;\n  rqe->len = buffer->len;\n}\n\nvoid IoUringZeroCopyBufferPoolImpl::returnBuffer(Buffer* buffer) noexcept {\n  std::unique_lock lock{mutex_};\n  if (FOLLY_UNLIKELY(wantsShutdown_)) {\n    auto refs = --shutdownReferences_;\n    lock.unlock();\n    delayedDestroy(refs);\n    return;\n  }\n\n  uint32_t startTail = rqTail_;\n\n  // Trigger flush when pending buffers reach threshold computed from:\n  // max(128, min(25% of ring, 5% of pool))\n  // This bounds pending queue growth per Little's Law: L = λW\n  if (pendingBuffers_.size() >= flushThreshold_) {\n    flushRefillQueue();\n  }\n\n  uint32_t queueLength = getRingQueuedCount();\n  uint32_t slots = rqRing_.ring_entries - queueLength;\n  auto numToProcess =\n      std::min(pendingBuffers_.size(), static_cast<size_t>(slots));\n  for (size_t i = 0; i < numToProcess; i++) {\n    writeBufferToRing(pendingBuffers_.front());\n    pendingBuffers_.pop();\n  }\n\n  if (numToProcess < slots) {\n    writeBufferToRing(buffer);\n  } else {\n    pendingBuffers_.push(buffer);\n  }\n\n  if (rqTail_ != startTail) {\n    io_uring_smp_store_release(rqRing_.ktail, rqTail_);\n  }\n}\n\nvoid IoUringZeroCopyBufferPoolImpl::delayedDestroy(uint32_t refs) noexcept {\n  if (refs == 0) {\n    delete this;\n  }\n}\n\nvoid IoUringZeroCopyBufferPoolImpl::flushRefillQueue() noexcept {\n  // ring_ is nullptr in unit tests which don't have a real io_uring.\n  if (ring_ == nullptr) {\n    return;\n  }\n  struct zcrx_ctrl ctrl{};\n  ctrl.zcrx_id = id_;\n  ctrl.op = ZCRX_CTRL_FLUSH_RQ;\n\n  // Best-effort flush: on failure (e.g., -EOPNOTSUPP on older kernels),\n  // buffers remain in pendingBuffers_ and are processed when ring space\n  // becomes available naturally.\n  int ret =\n      io_uring_register(ring_->ring_fd, IORING_REGISTER_ZCRX_CTRL, &ctrl, 0);\n  flushCount_.fetch_add(1, std::memory_order_relaxed);\n  if (ret < 0) {\n    flushFailures_.fetch_add(1, std::memory_order_relaxed);\n    LOG_EVERY_N(WARNING, 100)\n        << \"Zero copy receive refill queue flush failed: \"\n        << folly::errnoStr(-ret);\n  }\n}\n\n/*static*/ IoUringZeroCopyBufferPool::UniquePtr\nIoUringZeroCopyBufferPool::create(Params params) {\n  return IoUringZeroCopyBufferPool::UniquePtr(\n      new IoUringZeroCopyBufferPool(params));\n}\n\n/*static*/ IoUringZeroCopyBufferPool::UniquePtr\nIoUringZeroCopyBufferPool::importHandle(\n    ExportHandle handle, struct io_uring* ring) {\n  return IoUringZeroCopyBufferPool::UniquePtr(\n      new IoUringZeroCopyBufferPool(std::move(handle), ring));\n}\n\nIoUringZeroCopyBufferPool::IoUringZeroCopyBufferPool(Params params)\n    : ring_(params.ring),\n      impl_(new IoUringZeroCopyBufferPoolImpl(params, false), ImplDeleter{}) {\n  zcrxId_ = impl_->id_;\n}\n\nIoUringZeroCopyBufferPool::IoUringZeroCopyBufferPool(Params params, TestTag)\n    : ring_(params.ring),\n      impl_(new IoUringZeroCopyBufferPoolImpl(params, true), ImplDeleter{}) {}\n\nIoUringZeroCopyBufferPool::IoUringZeroCopyBufferPool(\n    ExportHandle handle, struct io_uring* ring)\n    : ring_(ring) {\n  struct io_uring_zcrx_ifq_reg ifqReg{};\n  ifqReg.if_idx = static_cast<uint32_t>(handle.zcrxFd_);\n  ifqReg.flags = ZCRX_REG_IMPORT;\n\n  auto ret = io_uring_register_ifq(ring_, &ifqReg);\n  if (ret) {\n    throw std::runtime_error(\n        fmt::format(\n            \"IoUringZeroCopyBufferPool failed io_uring_register_ifq: {} {}\",\n            ret,\n            folly::errnoStr(ret)));\n  }\n\n  zcrxId_ = ifqReg.zcrx_id;\n  zcrxFd_ = handle.zcrxFd_;\n  impl_ = std::move(handle.impl_);\n}\n\nIoUringZeroCopyBufferPool::~IoUringZeroCopyBufferPool() {\n  if (zcrxFd_ >= 0) {\n    close(zcrxFd_);\n  }\n}\n\nIoUringZeroCopyBufferPool::ExportHandle\nIoUringZeroCopyBufferPool::exportHandle() const {\n  if (impl_->ring_ != ring_) {\n    throw std::runtime_error(\n        \"Cannot export a handle from a non-owning IoUringZeroCopyBufferPool\");\n  }\n\n  struct zcrx_ctrl ctrl{};\n  ctrl.zcrx_id = impl_->id_;\n  ctrl.op = ZCRX_CTRL_EXPORT;\n  auto zcrxFd =\n      io_uring_register(ring_->ring_fd, IORING_REGISTER_ZCRX_CTRL, &ctrl, 0);\n\n  return ExportHandle(zcrxFd, impl_);\n}\n\nstd::unique_ptr<IOBuf> IoUringZeroCopyBufferPool::getIoBuf(\n    const struct io_uring_cqe* cqe,\n    const struct io_uring_zcrx_cqe* rcqe) noexcept {\n  return impl_->getIoBuf(cqe, rcqe);\n}\n\nuint32_t* IoUringZeroCopyBufferPool::getHead() const noexcept {\n  return impl_->getHead();\n}\n\nuint32_t IoUringZeroCopyBufferPool::getRingUsedCount() const noexcept {\n  return impl_->getRingUsedCount();\n}\n\nuint32_t IoUringZeroCopyBufferPool::getRingFreeCount() const noexcept {\n  return impl_->getRingFreeCount();\n}\n\nsize_t IoUringZeroCopyBufferPool::getPendingBuffersSize() const noexcept {\n  return impl_->getPendingBuffersSize();\n}\n\nuint32_t IoUringZeroCopyBufferPool::getFlushThreshold() const noexcept {\n  return impl_->getFlushThreshold();\n}\n\nuint16_t IoUringZeroCopyBufferPool::getAndResetFlushFailures() noexcept {\n  return impl_->getAndResetFlushFailures();\n}\n\nuint16_t IoUringZeroCopyBufferPool::getAndResetFlushCount() noexcept {\n  return impl_->getAndResetFlushCount();\n}\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/IoUringZeroCopyBufferPool.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/Liburing.h>\n\n#if FOLLY_HAS_LIBURING\nFOLLY_PUSH_WARNING\nFOLLY_CLANG_DISABLE_WARNING(\"-Wnested-anon-types\")\nFOLLY_CLANG_DISABLE_WARNING(\"-Wzero-length-array\")\nFOLLY_GCC_DISABLE_WARNING(\"-Wshadow\")\n#include <liburing.h> // @manual\nFOLLY_POP_WARNING\n\nnamespace folly {\n\nclass IoUringZeroCopyBufferPoolImpl;\n\nclass IoUringZeroCopyBufferPool {\n public:\n  struct Params {\n    struct io_uring* ring;\n    size_t numPages;\n    size_t pageSize;\n    uint32_t rqEntries;\n    uint32_t ifindex;\n    uint16_t queueId;\n  };\n\n  struct ExportHandle {\n    explicit ExportHandle(\n        int zcrxFd, std::shared_ptr<IoUringZeroCopyBufferPoolImpl> impl)\n        : zcrxFd_(zcrxFd), impl_(std::move(impl)) {}\n\n    ~ExportHandle() = default;\n\n    ExportHandle(ExportHandle&&) = default;\n    ExportHandle& operator=(ExportHandle&&) = default;\n    ExportHandle(const ExportHandle&) = delete;\n    ExportHandle& operator=(const ExportHandle&) = delete;\n\n   private:\n    friend class IoUringZeroCopyBufferPool;\n\n    int zcrxFd_;\n    std::shared_ptr<IoUringZeroCopyBufferPoolImpl> impl_;\n  };\n\n  using UniquePtr = std::unique_ptr<IoUringZeroCopyBufferPool>;\n  static UniquePtr create(Params params);\n  static UniquePtr importHandle(ExportHandle handle, struct io_uring* ring);\n\n  ExportHandle exportHandle() const;\n\n  ~IoUringZeroCopyBufferPool();\n\n  std::unique_ptr<IOBuf> getIoBuf(\n      const struct io_uring_cqe* cqe,\n      const struct io_uring_zcrx_cqe* rcqe) noexcept;\n\n private:\n  explicit IoUringZeroCopyBufferPool(Params params);\n\n  struct TestTag {};\n  explicit IoUringZeroCopyBufferPool(Params params, TestTag);\n\n  explicit IoUringZeroCopyBufferPool(\n      ExportHandle handle, struct io_uring* ring);\n\n  IoUringZeroCopyBufferPool(IoUringZeroCopyBufferPool&&) = delete;\n  IoUringZeroCopyBufferPool(IoUringZeroCopyBufferPool const&) = delete;\n  IoUringZeroCopyBufferPool& operator=(IoUringZeroCopyBufferPool&&) = delete;\n  IoUringZeroCopyBufferPool& operator=(IoUringZeroCopyBufferPool const&) =\n      delete;\n\n  // For testing\n  friend class IoUringZeroCopyBufferPoolTestHelper;\n  uint32_t* getHead() const noexcept;\n  uint32_t getRingUsedCount() const noexcept;\n  uint32_t getRingFreeCount() const noexcept;\n  size_t getPendingBuffersSize() const noexcept;\n  uint32_t getFlushThreshold() const noexcept;\n  uint16_t getAndResetFlushFailures() noexcept;\n  uint16_t getAndResetFlushCount() noexcept;\n\n  struct io_uring* ring_{nullptr};\n  std::shared_ptr<IoUringZeroCopyBufferPoolImpl> impl_;\n  int zcrxId_{-1};\n  int zcrxFd_{-1};\n};\n\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/Liburing.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#if defined(__linux__) && __has_include(<liburing.h>)\n#define FOLLY_HAS_LIBURING 1\n#else\n#define FOLLY_HAS_LIBURING 0\n#endif\n"
  },
  {
    "path": "folly/io/async/MuxIOThreadPoolExecutor.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/MuxIOThreadPoolExecutor.h>\n\n#include <stdexcept>\n\n#include <fmt/format.h>\n#include <folly/container/Enumerate.h>\n#include <folly/io/async/EpollBackend.h>\n#include <folly/lang/Align.h>\n#include <folly/synchronization/Latch.h>\n\nnamespace folly {\n\nnamespace {\n\nThrottledLifoSem::Options throttledLifoSemOptions(\n    std::chrono::nanoseconds wakeUpInterval) {\n  ThrottledLifoSem::Options opts;\n  opts.wakeUpInterval = wakeUpInterval;\n  return opts;\n}\n\n} // namespace\n\nstruct MuxIOThreadPoolExecutor::EvbState {\n  EvbState() : evb(evbOptions()) {}\n\n  EventBase evb;\n  std::unique_ptr<EventBasePoller::Handle> handle;\n\n  alignas(cacheline_align_v) std::atomic<size_t> pendingTasks = 0;\n\n private:\n  static const EventBase::Options& evbOptions() {\n#if FOLLY_HAS_EPOLL\n    static const auto options = EventBase::Options{}.setBackendFactory([] {\n      return std::make_unique<EpollBackend>(EpollBackend::Options{});\n    });\n    return options;\n#else\n    throw std::invalid_argument(\"EpollBackend not supported\");\n#endif\n  }\n};\n\nMuxIOThreadPoolExecutor::MuxIOThreadPoolExecutor(\n    size_t numThreads,\n    Options options,\n    std::shared_ptr<ThreadFactory> threadFactory,\n    EventBaseManager* ebm)\n    : IOThreadPoolExecutorBase(\n          numThreads, numThreads, std::move(threadFactory)),\n      options_(std::move(options)),\n      numEventBases_(\n          options_.numEventBases == 0 ? numThreads : options_.numEventBases),\n      eventBaseManager_(ebm),\n      readyQueueSem_(throttledLifoSemOptions(options.wakeUpInterval)) {\n  setNumThreads(numThreads);\n\n  fdGroup_ = EventBasePoller::get().makeFdGroup(\n      [this](Range<EventBasePoller::Handle**> readyHandles) noexcept {\n        for (auto* handle : readyHandles) {\n          readyQueue_.enqueue(handle);\n        }\n        readyQueueSem_.post(readyHandles.size());\n      });\n\n  evbStates_.reserve(numEventBases_);\n  Latch allEvbsRunning(numEventBases_);\n  for (size_t i = 0; i < numEventBases_; ++i) {\n    auto& evbState = evbStates_.emplace_back(std::make_unique<EvbState>());\n    evbState->evb.setStrictLoopThread();\n    evbState->evb.runInEventBaseThread([&] { allEvbsRunning.count_down(); });\n    // Keep the loop running until shutdown.\n    keepAlives_.emplace_back(&evbState->evb);\n    auto fd = evbState->evb.getBackend()->getPollableFd();\n    CHECK_GE(fd, 0);\n    evbState->handle = fdGroup_->add(fd, evbState.get());\n  }\n  allEvbsRunning.wait();\n\n  registerThreadPoolExecutor(this);\n  if (options_.enableThreadIdCollection) {\n    threadIdCollector_ = std::make_unique<ThreadIdWorkerProvider>();\n  }\n}\n\nMuxIOThreadPoolExecutor::~MuxIOThreadPoolExecutor() {\n  deregisterThreadPoolExecutor(this);\n  stop();\n}\n\nvoid MuxIOThreadPoolExecutor::add(Func func) {\n  add(std::move(func), std::chrono::milliseconds(0));\n}\n\nvoid MuxIOThreadPoolExecutor::add(\n    Func func, std::chrono::milliseconds expiration, Func expireCallback) {\n  auto& evbState = pickEvbState();\n  auto task = Task(std::move(func), expiration, std::move(expireCallback));\n  registerTaskEnqueue(task);\n  auto wrappedFunc = [this, &evbState, task = std::move(task)]() mutable {\n    const auto& ioThread = *thisThread_;\n    runTask(ioThread, std::move(task));\n    evbState.pendingTasks--;\n  };\n\n  evbState.pendingTasks++;\n  evbState.evb.runInEventBaseThread(std::move(wrappedFunc));\n}\n\nvoid MuxIOThreadPoolExecutor::validateNumThreads(size_t numThreads) {\n  if (numThreads == 0 || numThreads > numEventBases_) {\n    throw std::invalid_argument(\n        fmt::format(\n            \"Unsupported number of threads: {} (with {} EventBases)\",\n            numThreads,\n            numEventBases_));\n  }\n}\n\nstd::shared_ptr<ThreadPoolExecutor::Thread>\nMuxIOThreadPoolExecutor::makeThread() {\n  return std::make_shared<IOThread>();\n}\n\nvoid MuxIOThreadPoolExecutor::threadRun(ThreadPtr thread) {\n  this->threadPoolHook_.registerThread();\n\n  const auto& ioThread = *thisThread_ =\n      std::static_pointer_cast<IOThread>(thread);\n\n  auto tid = folly::getOSThreadID();\n  if (threadIdCollector_) {\n    threadIdCollector_->addTid(tid);\n  }\n  SCOPE_EXIT {\n    if (threadIdCollector_) {\n      threadIdCollector_->removeTid(tid);\n    }\n  };\n  thread->startupBaton.post();\n\n  ExecutorBlockingGuard guard{\n      ExecutorBlockingGuard::TrackTag{}, this, getName()};\n\n  while (true) {\n    readyQueueSem_.wait(WaitOptions{}.spin_max(options_.idleSpinMax));\n    auto handle = readyQueue_.dequeue();\n    if (handle == nullptr) {\n      break;\n    }\n    auto* evbState = handle->getUserData<EvbState>();\n    auto* evb = &evbState->evb;\n\n    ioThread->curEvbState = evbState;\n    eventBaseManager_->setEventBase(evb, false);\n\n    auto status = evb->loopWithSuspension();\n    CHECK(status != EventBase::LoopStatus::kError);\n\n    eventBaseManager_->clearEventBase();\n    ioThread->curEvbState = nullptr;\n\n    handle->handoff(status == EventBase::LoopStatus::kDone);\n  }\n\n  std::unique_lock w{threadListLock_};\n  for (auto& o : observers_) {\n    o->threadStopped(thread.get());\n  }\n  threadList_.remove(thread);\n  stoppedThreads_.add(std::move(thread));\n}\n\nMuxIOThreadPoolExecutor::EvbState& MuxIOThreadPoolExecutor::pickEvbState() {\n  if (auto ioThread = thisThread_.get_existing()) {\n    return *(*ioThread)->curEvbState;\n  }\n\n  return *evbStates_[nextEvb_++ % evbStates_.size()];\n}\n\nsize_t MuxIOThreadPoolExecutor::getPendingTaskCountImpl() const {\n  size_t ret = 0;\n  for (const auto& evbState : evbStates_) {\n    ret += evbState->pendingTasks.load();\n  }\n  return ret;\n}\n\nvoid MuxIOThreadPoolExecutor::addObserver(std::shared_ptr<Observer> o) {\n  if (auto ioObserver = dynamic_cast<IOObserver*>(o.get())) {\n    // All EventBases are created at construction time.\n    for (const auto& evbState : evbStates_) {\n      ioObserver->registerEventBase(evbState->evb);\n    }\n  }\n  ThreadPoolExecutor::addObserver(std::move(o));\n}\n\nvoid MuxIOThreadPoolExecutor::maybeUnregisterEventBases(Observer* o) {\n  if (auto ioObserver = dynamic_cast<IOObserver*>(o)) {\n    for (const auto& evbState : evbStates_) {\n      ioObserver->unregisterEventBase(evbState->evb);\n    }\n  }\n}\n\nvoid MuxIOThreadPoolExecutor::removeObserver(std::shared_ptr<Observer> o) {\n  maybeUnregisterEventBases(o.get());\n  ThreadPoolExecutor::addObserver(std::move(o));\n}\n\nstd::vector<folly::Executor::KeepAlive<folly::EventBase>>\nMuxIOThreadPoolExecutor::getAllEventBases() {\n  return keepAlives_;\n}\n\nfolly::EventBaseManager* MuxIOThreadPoolExecutor::getEventBaseManager() {\n  return eventBaseManager_;\n}\n\nEventBase* MuxIOThreadPoolExecutor::getEventBase() {\n  return &pickEvbState().evb;\n}\n\nvoid MuxIOThreadPoolExecutor::stopThreads(size_t n) {\n  for (size_t i = 0; i < n; i++) {\n    readyQueue_.enqueue(nullptr); // Poison.\n  }\n  readyQueueSem_.post(n);\n}\n\nvoid MuxIOThreadPoolExecutor::stop() {\n  join();\n}\n\nvoid MuxIOThreadPoolExecutor::join() {\n  if (!joinKeepAliveOnce()) {\n    return; // Already called.\n  }\n\n  {\n    std::shared_lock lock{threadListLock_};\n    for (const auto& o : observers_) {\n      maybeUnregisterEventBases(o.get());\n    }\n  }\n\n  for (auto&& [i, evbState] : folly::enumerate(evbStates_)) {\n    // Release the keepalive so the loop can complete and the handle be\n    // reclaimed.\n    keepAlives_[i].reset();\n    fdGroup_->reclaim(std::move(evbState->handle));\n  }\n  fdGroup_.reset();\n  evbStates_.clear();\n\n  stopAndJoinAllThreads(/* isJoin */ true);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/MuxIOThreadPoolExecutor.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <limits>\n\n#include <folly/Portability.h>\n#include <folly/concurrency/UnboundedQueue.h>\n#include <folly/executors/IOThreadPoolExecutor.h>\n#include <folly/executors/QueueObserver.h>\n#include <folly/io/async/EventBaseManager.h>\n#include <folly/io/async/EventBasePoller.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n#include <folly/synchronization/ThrottledLifoSem.h>\n#include <folly/synchronization/WaitOptions.h>\n\nnamespace folly {\n\n/**\n * NOTE: This is highly experimental. Do not use.\n *\n * A pool of EventBases scheduled over a pool of threads.\n *\n * Intended as a drop-in replacement for folly::IOThreadPoolExecutor, but with a\n * substantially different design: EventBases are not pinned to threads, so it\n * is possible to have more EventBases than threads. EventBases that have ready\n * events can be scheduled on any of the threads in the pool, with the\n * scheduling governed by ThrottledLifoSem.\n *\n * This allows to batch the loops of multiple EventBases on a single thread as\n * long as each runs for a short enough time, reducing the number of wake-ups\n * and allowing for better load balancing across handlers. For example, we can\n * create a large number of EventBases processed by a smaller number of threads\n * and distribute the handlers.\n *\n * The number of EventBases is set at construction time and cannot be changed\n * later. The number of threads can be changed dynamically, but setting it to 0\n * is not supported (otherwise no thread would be left to drive the EventBases)\n * and it is not useful to run more threads than EventBases, so that is not\n * supported either: attempting to set the number of threads to 0 or to a value\n * greater than numEventBases() (either in construction or using\n * setNumThreads()) will throw std::invalid_argument).\n */\nclass MuxIOThreadPoolExecutor : public IOThreadPoolExecutorBase {\n public:\n  struct Options {\n    Options() {}\n\n    Options& setEnableThreadIdCollection(bool b) {\n      enableThreadIdCollection = b;\n      return *this;\n    }\n\n    Options& setNumEventBases(size_t num) {\n      numEventBases = num;\n      return *this;\n    }\n\n    Options& setWakeUpInterval(std::chrono::nanoseconds w) {\n      wakeUpInterval = w;\n      return *this;\n    }\n\n    Options& setIdleSpinMax(std::chrono::nanoseconds s) {\n      idleSpinMax = s;\n      return *this;\n    }\n\n    bool enableThreadIdCollection{false};\n    // If 0, the number of EventBases is set to the number of threads.\n    size_t numEventBases{0};\n    std::chrono::nanoseconds wakeUpInterval{std::chrono::microseconds{100}};\n    // Max spin for an idle thread waiting for work before going to sleep.\n    std::chrono::nanoseconds idleSpinMax = std::chrono::microseconds{10};\n  };\n\n  explicit MuxIOThreadPoolExecutor(\n      size_t numThreads,\n      Options options = {},\n      std::shared_ptr<ThreadFactory> threadFactory =\n          std::make_shared<NamedThreadFactory>(\"MuxIOTPEx\"),\n      folly::EventBaseManager* ebm = folly::EventBaseManager::get());\n\n  ~MuxIOThreadPoolExecutor() override;\n\n  size_t numEventBases() const { return numEventBases_; }\n\n  void add(Func func) override;\n  void add(\n      Func func,\n      std::chrono::milliseconds expiration,\n      Func expireCallback = nullptr) override;\n\n  folly::EventBase* getEventBase() override;\n\n  // Returns all the EventBase instances\n  std::vector<folly::Executor::KeepAlive<folly::EventBase>> getAllEventBases()\n      override;\n\n  folly::EventBaseManager* getEventBaseManager() override;\n\n  // Returns nullptr unless explicitly enabled through constructor\n  folly::WorkerProvider* getThreadIdCollector() override {\n    return threadIdCollector_.get();\n  }\n\n  void addObserver(std::shared_ptr<Observer> o) override;\n  void removeObserver(std::shared_ptr<Observer> o) override;\n\n  void stop() override;\n  void join() override;\n\n private:\n  using EventBasePoller = folly::detail::EventBasePoller;\n\n  struct EvbState;\n\n  struct alignas(Thread) IOThread : public Thread {\n    EvbState* curEvbState; // Only accessed inside the worker thread.\n  };\n\n  void maybeUnregisterEventBases(Observer* o);\n\n  void validateNumThreads(size_t numThreads) override;\n  ThreadPtr makeThread() override;\n  EvbState& pickEvbState();\n  void threadRun(ThreadPtr thread) override;\n  void stopThreads(size_t n) override;\n  size_t getPendingTaskCountImpl() const override final;\n\n  const Options options_;\n  const size_t numEventBases_;\n  folly::EventBaseManager* eventBaseManager_;\n\n  std::unique_ptr<EventBasePoller::FdGroup> fdGroup_;\n  std::vector<std::unique_ptr<EvbState>> evbStates_;\n  std::vector<Executor::KeepAlive<EventBase>> keepAlives_;\n\n  relaxed_atomic<size_t> nextEvb_{0};\n  folly::ThreadLocal<std::shared_ptr<IOThread>> thisThread_;\n  std::unique_ptr<ThreadIdWorkerProvider> threadIdCollector_;\n  std::atomic<size_t> pendingTasks_{0};\n\n  USPMCQueue<EventBasePoller::Handle*, /* MayBlock */ false> readyQueue_;\n  folly::ThrottledLifoSem readyQueueSem_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/NotificationQueue.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <stdexcept>\n#include <utility>\n\n#include <boost/intrusive/slist.hpp>\n#include <glog/logging.h>\n\n#include <folly/Exception.h>\n#include <folly/FileUtil.h>\n#include <folly/Likely.h>\n#include <folly/ScopeGuard.h>\n#include <folly/SpinLock.h>\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/Request.h>\n#include <folly/portability/Fcntl.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Unistd.h>\n#include <folly/system/Pid.h>\n\n#if __has_include(<sys/eventfd.h>)\n#include <sys/eventfd.h>\n#endif\n\nnamespace folly {\n\n/**\n * A producer-consumer queue for passing messages between EventBase threads.\n *\n * Messages can be added to the queue from any thread.  Multiple consumers may\n * listen to the queue from multiple EventBase threads.\n *\n * A NotificationQueue may not be destroyed while there are still consumers\n * registered to receive events from the queue.  It is the user's\n * responsibility to ensure that all consumers are unregistered before the\n * queue is destroyed.\n *\n * MessageT should be MoveConstructible (i.e., must support either a move\n * constructor or a copy constructor, or both).  Ideally it's move constructor\n * (or copy constructor if no move constructor is provided) should never throw\n * exceptions.  If the constructor may throw, the consumers could end up\n * spinning trying to move a message off the queue and failing, and then\n * retrying.\n */\ntemplate <typename MessageT>\nclass NotificationQueue {\n  struct Node\n      : public boost::intrusive::slist_base_hook<\n            boost::intrusive::cache_last<true>> {\n    template <typename MessageTT>\n    Node(MessageTT&& msg, std::shared_ptr<RequestContext> ctx)\n        : msg_(std::forward<MessageTT>(msg)), ctx_(std::move(ctx)) {}\n    MessageT msg_;\n    std::shared_ptr<RequestContext> ctx_;\n  };\n\n public:\n  /**\n   * A callback interface for consuming messages from the queue as they arrive.\n   */\n  class Consumer : public DelayedDestruction, private EventHandler {\n   public:\n    using UniquePtr = DelayedDestructionUniquePtr<Consumer>;\n\n    enum : uint16_t { kDefaultMaxReadAtOnce = 10 };\n\n    Consumer()\n        : queue_(nullptr),\n          destroyedFlagPtr_(nullptr),\n          maxReadAtOnce_(kDefaultMaxReadAtOnce) {}\n\n    // create a consumer in-place, without the need to build new class\n    template <typename TCallback>\n    static UniquePtr make(TCallback&& callback);\n\n    /**\n     * messageAvailable() will be invoked whenever a new\n     * message is available from the pipe.\n     */\n    virtual void messageAvailable(MessageT&& message) noexcept = 0;\n\n    /**\n     * Begin consuming messages from the specified queue.\n     *\n     * messageAvailable() will be called whenever a message is available.  This\n     * consumer will continue to consume messages until stopConsuming() is\n     * called.\n     *\n     * A Consumer may only consume messages from a single NotificationQueue at\n     * a time.  startConsuming() should not be called if this consumer is\n     * already consuming.\n     */\n    void startConsuming(EventBase* eventBase, NotificationQueue* queue) {\n      init(eventBase, queue);\n      registerHandler(READ | PERSIST);\n    }\n\n    /**\n     * Same as above but registers this event handler as internal so that it\n     * doesn't count towards the pending reader count for the IOLoop.\n     */\n    void startConsumingInternal(\n        EventBase* eventBase, NotificationQueue* queue) {\n      init(eventBase, queue);\n      registerInternalHandler(READ | PERSIST);\n    }\n\n    /**\n     * Stop consuming messages.\n     *\n     * startConsuming() may be called again to resume consumption of messages\n     * at a later point in time.\n     */\n    void stopConsuming();\n\n    /**\n     * Consume messages off the queue until it is empty. No messages may be\n     * added to the queue while it is draining, so that the process is bounded.\n     * To that end, putMessage/tryPutMessage will throw an std::runtime_error,\n     * and tryPutMessageNoThrow will return false.\n     *\n     * @returns true if the queue was drained, false otherwise. In practice,\n     * this will only fail if someone else is already draining the queue.\n     */\n    bool consumeUntilDrained(size_t* numConsumed = nullptr) noexcept;\n\n    /**\n     * Get the NotificationQueue that this consumer is currently consuming\n     * messages from.  Returns nullptr if the consumer is not currently\n     * consuming events from any queue.\n     */\n    NotificationQueue* getCurrentQueue() const { return queue_; }\n\n    /**\n     * Set a limit on how many messages this consumer will read each iteration\n     * around the event loop.\n     *\n     * This helps rate-limit how much work the Consumer will do each event loop\n     * iteration, to prevent it from starving other event handlers.\n     *\n     * A limit of 0 means no limit will be enforced.  If unset, the limit\n     * defaults to kDefaultMaxReadAtOnce (defined to 10 above).\n     */\n    void setMaxReadAtOnce(uint32_t maxAtOnce) { maxReadAtOnce_ = maxAtOnce; }\n    uint32_t getMaxReadAtOnce() const { return maxReadAtOnce_; }\n\n    EventBase* getEventBase() { return base_; }\n\n    void handlerReady(uint16_t events) noexcept override;\n\n   protected:\n    void destroy() override;\n\n    ~Consumer() override {}\n\n   private:\n    /**\n     * Consume messages off the queue until\n     *   - the queue is empty (1), or\n     *   - until the consumer is destroyed, or\n     *   - until the consumer is uninstalled, or\n     *   - an exception is thrown in the course of dequeueing, or\n     *   - unless isDrain is true, until the maxReadAtOnce_ limit is hit\n     *\n     * (1) Well, maybe. See logic/comments around \"wasEmpty\" in implementation.\n     */\n    void consumeMessages(bool isDrain, size_t* numConsumed = nullptr) noexcept;\n\n    void setActive(bool active, bool shouldLock = false) {\n      if (!queue_) {\n        active_ = active;\n        return;\n      }\n      if (shouldLock) {\n        queue_->spinlock_.lock();\n      }\n      if (!active_ && active) {\n        ++queue_->numActiveConsumers_;\n      } else if (active_ && !active) {\n        --queue_->numActiveConsumers_;\n      }\n      active_ = active;\n      if (shouldLock) {\n        queue_->spinlock_.unlock();\n      }\n    }\n    void init(EventBase* eventBase, NotificationQueue* queue);\n\n    NotificationQueue* queue_;\n    bool* destroyedFlagPtr_;\n    uint32_t maxReadAtOnce_;\n    EventBase* base_;\n    bool active_{false};\n  };\n\n  class SimpleConsumer {\n   public:\n    explicit SimpleConsumer(NotificationQueue& queue) : queue_(queue) {\n      ++queue_.numConsumers_;\n    }\n\n    ~SimpleConsumer() { --queue_.numConsumers_; }\n\n    int getFd() const {\n      return queue_.eventfd_ >= 0 ? queue_.eventfd_ : queue_.pipeFds_[0];\n    }\n\n    template <typename F>\n    void consume(F&& f);\n\n   private:\n    NotificationQueue& queue_;\n  };\n\n  enum class FdType {\n    PIPE = 1,\n    EVENTFD,\n  };\n\n  /**\n   * Create a new NotificationQueue.\n   *\n   * If the maxSize parameter is specified, this sets the maximum queue size\n   * that will be enforced by tryPutMessage().  (This size is advisory, and may\n   * be exceeded if producers explicitly use putMessage() instead of\n   * tryPutMessage().)\n   *\n   * The fdType parameter determines the type of file descriptor used\n   * internally to signal message availability.  The default (eventfd) is\n   * preferable for performance and because it won't fail when the queue gets\n   * too long.  It is not available on on older and non-linux kernels, however.\n   * In this case the code will fall back to using a pipe, the parameter is\n   * mostly for testing purposes.\n   */\n  explicit NotificationQueue(\n      uint32_t maxSize = 0, FdType fdType = FdType::EVENTFD)\n      : eventfd_(-1),\n        pipeFds_{-1, -1},\n        advisoryMaxQueueSize_(maxSize),\n        pid_(folly::get_cached_pid()) {\n#if !__has_include(<sys/eventfd.h>)\n    if (fdType == FdType::EVENTFD) {\n      fdType = FdType::PIPE;\n    }\n#endif\n\n#if __has_include(<sys/eventfd.h>)\n    if (fdType == FdType::EVENTFD) {\n      eventfd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);\n      if (eventfd_ == -1) {\n        if (errno == ENOSYS || errno == EINVAL) {\n          // eventfd not availalble\n          LOG(ERROR)\n              << \"failed to create eventfd for NotificationQueue: \" << errno\n              << \", falling back to pipe mode (is your kernel \" << \"> 2.6.30?)\";\n          fdType = FdType::PIPE;\n        } else {\n          // some other error\n          folly::throwSystemError(\n              \"Failed to create eventfd for \"\n              \"NotificationQueue\",\n              errno);\n        }\n      }\n    }\n#endif\n\n    if (fdType == FdType::PIPE) {\n      if (fileops::pipe(pipeFds_)) {\n        folly::throwSystemError(\n            \"Failed to create pipe for NotificationQueue\", errno);\n      }\n      try {\n        // put both ends of the pipe into non-blocking mode\n        if (fcntl(pipeFds_[0], F_SETFL, O_RDONLY | O_NONBLOCK) != 0) {\n          folly::throwSystemError(\n              \"failed to put NotificationQueue pipe read \"\n              \"endpoint into non-blocking mode\",\n              errno);\n        }\n        if (fcntl(pipeFds_[1], F_SETFL, O_WRONLY | O_NONBLOCK) != 0) {\n          folly::throwSystemError(\n              \"failed to put NotificationQueue pipe write \"\n              \"endpoint into non-blocking mode\",\n              errno);\n        }\n      } catch (...) {\n        fileops::close(pipeFds_[0]);\n        fileops::close(pipeFds_[1]);\n        throw;\n      }\n    }\n  }\n\n  ~NotificationQueue() {\n    std::unique_ptr<Node> data;\n    while (!queue_.empty()) {\n      data.reset(&queue_.front());\n      queue_.pop_front();\n    }\n    if (eventfd_ >= 0) {\n      fileops::close(eventfd_);\n      eventfd_ = -1;\n    }\n    if (pipeFds_[0] >= 0) {\n      fileops::close(pipeFds_[0]);\n      pipeFds_[0] = -1;\n    }\n    if (pipeFds_[1] >= 0) {\n      fileops::close(pipeFds_[1]);\n      pipeFds_[1] = -1;\n    }\n  }\n\n  /**\n   * Set the advisory maximum queue size.\n   *\n   * This maximum queue size affects calls to tryPutMessage().  Message\n   * producers can still use the putMessage() call to unconditionally put a\n   * message on the queue, ignoring the configured maximum queue size.  This\n   * can cause the queue size to exceed the configured maximum.\n   */\n  void setMaxQueueSize(uint32_t max) { advisoryMaxQueueSize_ = max; }\n\n  /**\n   * Attempt to put a message on the queue if the queue is not already full.\n   *\n   * If the queue is full, a std::overflow_error will be thrown.  The\n   * setMaxQueueSize() function controls the maximum queue size.\n   *\n   * If the queue is currently draining, an std::runtime_error will be thrown.\n   *\n   * This method may contend briefly on a spinlock if many threads are\n   * concurrently accessing the queue, but for all intents and purposes it will\n   * immediately place the message on the queue and return.\n   *\n   * tryPutMessage() may throw std::bad_alloc if memory allocation fails, and\n   * may throw any other exception thrown by the MessageT move/copy\n   * constructor.\n   */\n  template <typename MessageTT>\n  void tryPutMessage(MessageTT&& message) {\n    putMessageImpl(std::forward<MessageTT>(message), advisoryMaxQueueSize_);\n  }\n\n  /**\n   * No-throw versions of the above.  Instead returns true on success, false on\n   * failure.\n   *\n   * Only std::overflow_error (the common exception case) and std::runtime_error\n   * (which indicates that the queue is being drained) are prevented from being\n   * thrown. User code must still catch std::bad_alloc errors.\n   */\n  template <typename MessageTT>\n  bool tryPutMessageNoThrow(MessageTT&& message) {\n    return putMessageImpl(\n        std::forward<MessageTT>(message), advisoryMaxQueueSize_, false);\n  }\n\n  /**\n   * Unconditionally put a message on the queue.\n   *\n   * This method is like tryPutMessage(), but ignores the maximum queue size\n   * and always puts the message on the queue, even if the maximum queue size\n   * would be exceeded.\n   *\n   * putMessage() may throw\n   *   - std::bad_alloc if memory allocation fails, and may\n   *   - std::runtime_error if the queue is currently draining\n   *   - any other exception thrown by the MessageT move/copy constructor.\n   */\n  template <typename MessageTT>\n  void putMessage(MessageTT&& message) {\n    putMessageImpl(std::forward<MessageTT>(message), 0);\n  }\n\n  /**\n   * Put several messages on the queue.\n   */\n  template <typename InputIteratorT>\n  void putMessages(InputIteratorT first, InputIteratorT last) {\n    using IterCategory =\n        typename std::iterator_traits<InputIteratorT>::iterator_category;\n    putMessagesImpl(first, last, IterCategory());\n  }\n\n  /**\n   * Try to immediately pull a message off of the queue, without blocking.\n   *\n   * If a message is immediately available, the result parameter will be\n   * updated to contain the message contents and true will be returned.\n   *\n   * If no message is available, false will be returned and result will be left\n   * unmodified.\n   */\n  bool tryConsume(MessageT& result) {\n    SCOPE_EXIT {\n      syncSignalAndQueue();\n    };\n\n    checkPid();\n    std::unique_ptr<Node> data;\n\n    {\n      std::unique_lock g(spinlock_);\n\n      if (FOLLY_UNLIKELY(queue_.empty())) {\n        return false;\n      }\n\n      data.reset(&queue_.front());\n      queue_.pop_front();\n    }\n\n    result = std::move(data->msg_);\n    RequestContext::setContext(std::move(data->ctx_));\n\n    return true;\n  }\n\n  size_t size() const {\n    std::unique_lock g(spinlock_);\n    return queue_.size();\n  }\n\n  /**\n   * Check that the NotificationQueue is being used from the correct process.\n   *\n   * If you create a NotificationQueue in one process, then fork, and try to\n   * send messages to the queue from the child process, you're going to have a\n   * bad time.  Unfortunately users have (accidentally) run into this.\n   *\n   * Because we use an eventfd/pipe, the child process can actually signal the\n   * parent process that an event is ready.  However, it can't put anything on\n   * the parent's queue, so the parent wakes up and finds an empty queue.  This\n   * check ensures that we catch the problem in the misbehaving child process\n   * code, and crash before signalling the parent process.\n   */\n  void checkPid() const {\n    if (FOLLY_UNLIKELY(pid_ != folly::get_cached_pid())) {\n      checkPidFail();\n    }\n  }\n\n private:\n  // Forbidden copy constructor and assignment operator\n  NotificationQueue(NotificationQueue const&) = delete;\n  NotificationQueue& operator=(NotificationQueue const&) = delete;\n\n  inline bool checkQueueSize(size_t maxSize, bool throws = true) const {\n    DCHECK(0 == spinlock_.try_lock());\n    if (maxSize > 0 && queue_.size() >= maxSize) {\n      if (throws) {\n        throw std::overflow_error(\n            \"unable to add message to NotificationQueue: \"\n            \"queue is full\");\n      }\n      return false;\n    }\n    return true;\n  }\n\n  inline bool checkDraining(bool throws = true) {\n    if (FOLLY_UNLIKELY(draining_ && throws)) {\n      throw std::runtime_error(\"queue is draining, cannot add message\");\n    }\n    return draining_;\n  }\n\n  void ensureSignalLocked() const {\n    // semantics: empty fd == empty queue <=> !signal_\n    if (signal_) {\n      return;\n    }\n\n    ssize_t bytes_written = 0;\n    size_t bytes_expected = 0;\n\n    do {\n      if (eventfd_ >= 0) {\n        // eventfd(2) dictates that we must write a 64-bit integer\n        uint64_t signal = 1;\n        bytes_expected = sizeof(signal);\n        bytes_written = fileops::write(eventfd_, &signal, bytes_expected);\n      } else {\n        uint8_t signal = 1;\n        bytes_expected = sizeof(signal);\n        bytes_written = fileops::write(pipeFds_[1], &signal, bytes_expected);\n      }\n    } while (bytes_written == -1 && errno == EINTR);\n\n    if (bytes_written == ssize_t(bytes_expected)) {\n      signal_ = true;\n    } else {\n      folly::throwSystemError(\n          \"failed to signal NotificationQueue after \"\n          \"write\",\n          errno);\n    }\n  }\n\n  void drainSignalsLocked() {\n    ssize_t bytes_read = 0;\n    if (eventfd_ > 0) {\n      uint64_t message;\n      bytes_read = readNoInt(eventfd_, &message, sizeof(message));\n      CHECK(bytes_read != -1 || errno == EAGAIN);\n    } else {\n      // There should only be one byte in the pipe. To avoid potential leaks we\n      // still drain.\n      uint8_t message[32];\n      ssize_t result;\n      while (\n          (result = readNoInt(pipeFds_[0], &message, sizeof(message))) != -1) {\n        bytes_read += result;\n      }\n      CHECK(result == -1 && errno == EAGAIN);\n      LOG_IF(ERROR, bytes_read > 1)\n          << \"[NotificationQueue] Unexpected state while draining pipe: bytes_read=\"\n          << bytes_read << \" bytes, expected <= 1\";\n    }\n    LOG_IF(ERROR, (signal_ && bytes_read == 0) || (!signal_ && bytes_read > 0))\n        << \"[NotificationQueue] Unexpected state while draining signals: signal_=\"\n        << signal_ << \" bytes_read=\" << bytes_read;\n\n    signal_ = false;\n  }\n\n  void ensureSignal() const {\n    std::unique_lock g(spinlock_);\n    ensureSignalLocked();\n  }\n\n  void syncSignalAndQueue() {\n    std::unique_lock g(spinlock_);\n\n    if (queue_.empty()) {\n      drainSignalsLocked();\n    } else {\n      ensureSignalLocked();\n    }\n  }\n\n  template <typename MessageTT>\n  bool putMessageImpl(MessageTT&& message, size_t maxSize, bool throws = true) {\n    checkPid();\n    bool signal = false;\n    {\n      auto data = std::make_unique<Node>(\n          std::forward<MessageTT>(message), RequestContext::saveContext());\n      std::unique_lock g(spinlock_);\n      if (checkDraining(throws) || !checkQueueSize(maxSize, throws)) {\n        return false;\n      }\n      // We only need to signal an event if not all consumers are\n      // awake.\n      if (numActiveConsumers_ < numConsumers_) {\n        signal = true;\n      }\n      queue_.push_back(*data.release());\n      if (signal) {\n        ensureSignalLocked();\n      }\n    }\n    return true;\n  }\n\n  template <typename InputIteratorT>\n  void putMessagesImpl(\n      InputIteratorT first, InputIteratorT last, std::input_iterator_tag) {\n    checkPid();\n    bool signal = false;\n    boost::intrusive::slist<Node, boost::intrusive::cache_last<true>> q;\n    try {\n      while (first != last) {\n        auto data = std::make_unique<Node>(\n            std::move(*first), RequestContext::saveContext());\n        q.push_back(*data.release());\n        ++first;\n      }\n      std::unique_lock g(spinlock_);\n      checkDraining();\n      queue_.splice(queue_.end(), q);\n      if (numActiveConsumers_ < numConsumers_) {\n        signal = true;\n      }\n      if (signal) {\n        ensureSignalLocked();\n      }\n    } catch (...) {\n      std::unique_ptr<Node> data;\n      while (!q.empty()) {\n        data.reset(&q.front());\n        q.pop_front();\n      }\n      throw;\n    }\n  }\n\n  FOLLY_NOINLINE void checkPidFail() const {\n    folly::terminate_with<std::runtime_error>(\n        \"Pid mismatch. Pid = \" +\n        folly::to<std::string>(folly::get_cached_pid()) + \". Expecting \" +\n        folly::to<std::string>(pid_));\n  }\n\n  mutable folly::SpinLock spinlock_;\n  mutable bool signal_{false};\n  int eventfd_;\n  int pipeFds_[2]; // to fallback to on older/non-linux systems\n  uint32_t advisoryMaxQueueSize_;\n  pid_t pid_;\n  boost::intrusive::slist<Node, boost::intrusive::cache_last<true>> queue_;\n  int numConsumers_{0};\n  std::atomic<int> numActiveConsumers_{0};\n  bool draining_{false};\n};\n\ntemplate <typename MessageT>\nvoid NotificationQueue<MessageT>::Consumer::destroy() {\n  // If we are in the middle of a call to handlerReady(), destroyedFlagPtr_\n  // will be non-nullptr.  Mark the value that it points to, so that\n  // handlerReady() will know the callback is destroyed, and that it cannot\n  // access any member variables anymore.\n  if (destroyedFlagPtr_) {\n    *destroyedFlagPtr_ = true;\n  }\n  stopConsuming();\n  DelayedDestruction::destroy();\n}\n\ntemplate <typename MessageT>\nvoid NotificationQueue<MessageT>::Consumer::handlerReady(\n    uint16_t /*events*/) noexcept {\n  consumeMessages(false);\n}\n\ntemplate <typename MessageT>\nvoid NotificationQueue<MessageT>::Consumer::consumeMessages(\n    bool isDrain, size_t* numConsumed) noexcept {\n  DestructorGuard dg(this);\n  uint32_t numProcessed = 0;\n  setActive(true);\n  SCOPE_EXIT {\n    if (queue_) {\n      queue_->syncSignalAndQueue();\n    }\n  };\n  SCOPE_EXIT {\n    setActive(false, /* shouldLock = */ true);\n  };\n  SCOPE_EXIT {\n    if (numConsumed != nullptr) {\n      *numConsumed = numProcessed;\n    }\n  };\n  while (true) {\n    // Now pop the message off of the queue.\n    //\n    // We have to manually acquire and release the spinlock here, rather than\n    // using SpinLockHolder since the MessageT has to be constructed while\n    // holding the spinlock and available after we release it.  SpinLockHolder\n    // unfortunately doesn't provide a release() method.  (We can't construct\n    // MessageT first since we have no guarantee that MessageT has a default\n    // constructor.\n    queue_->spinlock_.lock();\n    bool locked = true;\n\n    try {\n      if (FOLLY_UNLIKELY(queue_->queue_.empty())) {\n        // If there is no message, we've reached the end of the queue, return.\n        setActive(false);\n        queue_->spinlock_.unlock();\n        return;\n      }\n\n      // Pull a message off the queue.\n      std::unique_ptr<Node> data;\n      data.reset(&queue_->queue_.front());\n      queue_->queue_.pop_front();\n\n      // Check to see if the queue is empty now.\n      // We use this as an optimization to see if we should bother trying to\n      // loop again and read another message after invoking this callback.\n      bool wasEmpty = queue_->queue_.empty();\n      if (wasEmpty) {\n        setActive(false);\n      }\n\n      // Now unlock the spinlock before we invoke the callback.\n      queue_->spinlock_.unlock();\n      RequestContextScopeGuard rctx(std::move(data->ctx_));\n\n      locked = false;\n\n      // Call the callback\n      bool callbackDestroyed = false;\n      CHECK(destroyedFlagPtr_ == nullptr);\n      destroyedFlagPtr_ = &callbackDestroyed;\n      messageAvailable(std::move(data->msg_));\n      destroyedFlagPtr_ = nullptr;\n\n      // Make sure message destructor is called with the correct RequestContext.\n      data.reset();\n\n      // If the callback was destroyed before it returned, we are done\n      if (callbackDestroyed) {\n        return;\n      }\n\n      // If the callback is no longer installed, we are done.\n      if (queue_ == nullptr) {\n        return;\n      }\n\n      // If we have hit maxReadAtOnce_, we are done.\n      ++numProcessed;\n      if (!isDrain && maxReadAtOnce_ > 0 && numProcessed >= maxReadAtOnce_) {\n        return;\n      }\n\n      // If the queue was empty before we invoked the callback, it's probable\n      // that it is still empty now.  Just go ahead and return, rather than\n      // looping again and trying to re-read from the eventfd.  (If a new\n      // message had in fact arrived while we were invoking the callback, we\n      // will simply be woken up the next time around the event loop and will\n      // process the message then.)\n      if (wasEmpty) {\n        return;\n      }\n    } catch (const std::exception&) {\n      // This catch block is really just to handle the case where the MessageT\n      // constructor throws.  The messageAvailable() callback itself is\n      // declared as noexcept and should never throw.\n      //\n      // If the MessageT constructor does throw we try to handle it as best as\n      // we can, but we can't work miracles.  We will just ignore the error for\n      // now and return.  The next time around the event loop we will end up\n      // trying to read the message again.  If MessageT continues to throw we\n      // will never make forward progress and will keep trying each time around\n      // the event loop.\n      if (locked) {\n        // Unlock the spinlock.\n        queue_->spinlock_.unlock();\n      }\n\n      return;\n    }\n  }\n}\n\ntemplate <typename MessageT>\nvoid NotificationQueue<MessageT>::Consumer::init(\n    EventBase* eventBase, NotificationQueue* queue) {\n  eventBase->dcheckIsInEventBaseThread();\n  assert(queue_ == nullptr);\n  assert(!isHandlerRegistered());\n  queue->checkPid();\n\n  base_ = eventBase;\n\n  queue_ = queue;\n\n  {\n    std::unique_lock g(queue_->spinlock_);\n    queue_->numConsumers_++;\n  }\n  queue_->ensureSignal();\n\n  if (queue_->eventfd_ >= 0) {\n    initHandler(eventBase, folly::NetworkSocket::fromFd(queue_->eventfd_));\n  } else {\n    initHandler(eventBase, folly::NetworkSocket::fromFd(queue_->pipeFds_[0]));\n  }\n}\n\ntemplate <typename MessageT>\nvoid NotificationQueue<MessageT>::Consumer::stopConsuming() {\n  if (queue_ == nullptr) {\n    assert(!isHandlerRegistered());\n    return;\n  }\n\n  {\n    std::unique_lock g(queue_->spinlock_);\n    queue_->numConsumers_--;\n    setActive(false);\n  }\n\n  assert(isHandlerRegistered());\n  unregisterHandler();\n  detachEventBase();\n  queue_ = nullptr;\n}\n\ntemplate <typename MessageT>\nbool NotificationQueue<MessageT>::Consumer::consumeUntilDrained(\n    size_t* numConsumed) noexcept {\n  DestructorGuard dg(this);\n  {\n    std::unique_lock g(queue_->spinlock_);\n    if (queue_->draining_) {\n      return false;\n    }\n    queue_->draining_ = true;\n  }\n  consumeMessages(true, numConsumed);\n  {\n    std::unique_lock g(queue_->spinlock_);\n    queue_->draining_ = false;\n  }\n  return true;\n}\n\ntemplate <typename MessageT>\ntemplate <typename F>\nvoid NotificationQueue<MessageT>::SimpleConsumer::consume(F&& foreach) {\n  SCOPE_EXIT {\n    queue_.syncSignalAndQueue();\n  };\n\n  queue_.checkPid();\n\n  std::unique_ptr<Node> data;\n  {\n    std::unique_lock g(queue_.spinlock_);\n\n    if (FOLLY_UNLIKELY(queue_.queue_.empty())) {\n      return;\n    }\n\n    data.reset(&queue_.queue_.front());\n    queue_.queue_.pop_front();\n  }\n\n  RequestContextScopeGuard rctx(std::move(data->ctx_));\n  foreach(std::move(data->msg_));\n  // Make sure message destructor is called with the correct RequestContext.\n  data.reset();\n}\n\n/**\n * Creates a NotificationQueue::Consumer wrapping a function object\n * Modeled after AsyncTimeout::make\n *\n */\n\nnamespace detail {\n\ntemplate <typename MessageT, typename TCallback>\nstruct notification_queue_consumer_wrapper\n    : public NotificationQueue<MessageT>::Consumer {\n  template <typename UCallback>\n  explicit notification_queue_consumer_wrapper(UCallback&& callback)\n      : callback_(std::forward<UCallback>(callback)) {}\n\n  // we are being stricter here and requiring noexcept for callback\n  void messageAvailable(MessageT&& message) noexcept override {\n    static_assert(\n        noexcept(std::declval<TCallback>()(std::forward<MessageT>(message))),\n        \"callback must be declared noexcept, e.g.: `[]() noexcept {}`\");\n\n    callback_(std::forward<MessageT>(message));\n  }\n\n private:\n  TCallback callback_;\n};\n\n} // namespace detail\n\ntemplate <typename MessageT>\ntemplate <typename TCallback>\nauto NotificationQueue<MessageT>::Consumer::make(TCallback&& callback)\n    -> UniquePtr {\n  using CB = typename std::decay<TCallback>::type;\n  using W = detail::notification_queue_consumer_wrapper<MessageT, CB>;\n  return makeDelayedDestructionUniquePtr<W>(std::forward<TCallback>(callback));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/PasswordInFile.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/PasswordInFile.h>\n\n#include <folly/FileUtil.h>\n#include <folly/portability/OpenSSL.h>\n\nusing namespace std;\n\nnamespace folly {\n\nPasswordInFile::PasswordInFile(const string& file) : fileName_(file) {\n  readFile(file.c_str(), password_);\n  auto p = password_.find('\\0');\n  if (p != std::string::npos) {\n    password_.erase(p);\n  }\n}\n\nPasswordInFile::~PasswordInFile() {\n  OPENSSL_cleanse((char*)password_.data(), password_.length());\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/PasswordInFile.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ssl/PasswordCollector.h>\n\nnamespace folly {\n\nclass PasswordInFile : public ssl::PasswordCollector {\n public:\n  explicit PasswordInFile(const std::string& file);\n  ~PasswordInFile() override;\n\n  void getPassword(std::string& password, int /* size */) const override {\n    password = password_;\n  }\n\n  const char* getPasswordStr() const { return password_.c_str(); }\n\n  const std::string& describe() const override { return fileName_; }\n\n protected:\n  std::string fileName_;\n  std::string password_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/README.md",
    "content": "# folly/io/async: An object-oriented wrapper around libevent\n----------------------------------------------------------\n\n[libevent](https://github.com/libevent/libevent) is an excellent\ncross-platform eventing library.  Folly's async provides C++ object\nwrappers for fd callbacks and event_base, as well as providing\nimplementations for many common types of fd uses.\n\n## EventBase\n\nThe main libevent / epoll loop.  Generally there is a single EventBase\nper thread, and once started, nothing else happens on the thread\nexcept fd callbacks.  For example:\n\n```\nEventBase base;\nauto thread = std::thread([&](){\n  base.loopForever();\n});\n```\n\nEventBase has built-in support for message passing between threads.\nTo send a function to be run in the EventBase thread, use\nrunInEventBaseThread().\n\n```\nEventBase base;\nauto thread1 = std::thread([&](){\n  base.loopForever();\n});\nbase.runInEventBaseThread([&](){\n  printf(\"This will be printed in thread1\\n\");\n});\n```\n\nThere are various ways to run the loop.  EventBase::loop() will return\nwhen there are no more registered events.  EventBase::loopForever()\nwill loop until EventBase::terminateLoopSoon() is called.\nEventBase::loopOnce() will only call epoll() a single time.\n\nOther useful methods include EventBase::runAfterDelay() to run events\nafter some delay, and EventBase::setMaxLatency(latency, callback) to\nrun some callback if the loop is running very slowly, i.e., there are\ntoo many events in this loop, and some code should probably be running\nin different threads.\n\nEventBase always calls all callbacks inline - that is, there is no\nexplicit or implicit queuing.  The specific implications of this are:\n\n* Tail-latency times (P99) are vastly better than any queueing\n  implementation\n* The EventHandler implementation is responsible for not taking too\n  long in any individual callback.  All of the EventHandlers in this\n  implementation already do a good job of this, but if you are\n  subclassing EventHandler directly, something to keep in mind.\n* The callback cannot delete the EventBase or EventHandler directly,\n  since it is still on the call stack.  See DelayedDestruction class\n  description below, and use shared_ptrs appropriately.\n\n## EventHandler\n\nEventHandler is the object wrapper for fd's.  Any class you wish to\nreceive callbacks on will inherit from\nEventHandler. `registerHandler(EventType)` will register to receive\nevents of a specific type.\n\nCurrently supported event types:\n\n* READ - read and EOF events\n* WRITE - write events, when kernel write buffer is empty\n* READ_WRITE - both\n* PERSIST - The event will remain registered even after the handlerReady() fires\n\nUnsupported libevent event types, and why-\n\n* TIMEOUT - this library has specific timeout support, instead of\n  being attached to read/write fds.\n* SIGNAL - similarly, signals are handled separately, see\n  AsyncSignalHandler\n* EV_ET - Currently all the implementations of EventHandler are set up\n  for level triggered.  Benchmarking hasn't shown that edge triggered\n  provides much improvement.\n\n  Edge-triggered in this context means that libevent will provide only\n  a single callback when an event becomes active, as opposed to\n  level-triggered where as long as there is still data to read/write,\n  the event will continually fire each time event_wait is called.\n  Edge-triggered adds extra code complexity, since the library would\n  need to maintain a similar list of active FDs that libevent\n  currently does between edge triggering events.  The only advantage\n  of edge-triggered is that you can use EPOLLONESHOT to ensure the\n  event only gets called on a single event_base - but in this library,\n  we assume each event is only registered on a single thread anyway.\n\n* EV_FINALIZE - EventBase can only be used in a single thread,\n  excepting a few methods.  To safely unregister an event from a\n  different thread, it would have to be done through\n  EventBase::runInEventBaseThread().  Most APIs already make this\n  thread transition for you, or at least CHECK() that you've done it\n  in the correct thread.\n* EV_CLOSED - This is an optimization - instead of having to READ all\n  the data and then get an EOF, EV_CLOSED would fire before all the\n  data is read.  TODO: implement this.  Probably only useful in\n  request/response servers.\n\n## Implementations of EventHandler\n\n### AsyncSocket\n\nA nonblocking socket implementation.  Writes are queued and written\nasynchronously, even before connect() is successful.  The read api\nconsists of two methods: getReadBuffer() and readDataAvailable().\nWhen the READ event is signaled, libevent has no way of knowing how\nmuch data is available to read.   In some systems (linux), we *could*\nmake another syscall to get the data size in the kernel read buffer,\nbut syscalls are slow.  Instead, most users will just want to provide\na fixed size buffer in getReadBuffer(), probably using the IOBufQueue\nin folly/io.   readDataAvailable() will then describe exactly how much\ndata was read.\n\nAsyncSocket provides send timeouts, but not read timeouts - generally\nread timeouts are application specific, and should use an AsyncTimer\nimplementation below.\n\nVarious notes:\n\n* Using a chain of IOBuf objects, and calling writeChain(), is a very\n  syscall-efficient way to add/modify data to be sent, without\n  unnecessary copies.\n* setMaxReadsPerEvent() - this prevents an AsyncSocket from blocking\n  the event loop for too long.\n* Don't use the fd for syscalls yourself while it is being used in\n  AsyncSocket, instead use the provided wrappers, like\n  AsyncSocket::close(), shutdown(), etc.\n\n#### AsyncSSLSocket\n\nSimilar to AsyncSocket, but uses openssl.  Provides an additional\nHandshakeCallback to check the server's certificates.\n\n#### TAsyncUDPSocket\n\nTODO: Currently in fbthrift.\n\nA socket that reads/writes UDP packets.  Since there is little state\nto maintain, this is much simpler than AsyncSocket.\n\n### AsyncServerSocket\n\nA listen()ing socket that accept()s fds, and passes them to other\nevent bases.\n\nThe general pattern is:\n\n```\nEventBase base;\nauto socket = AsyncServerSocket::newSocket(&base);\nsocket->bind(port); // 0 to choose any free port\nsocket->addAcceptCallback(object, &base); // where object is the object that implements the accept callback, and base is the object's eventbase.  base::runInEventBaseThread() will be called to send it a message.\nsocket->listen(backlog);\nsocket->startAccepting();\n```\n\nGenerally there is a single accept() thread, and multiple\nAcceptCallback objects.  The Acceptee objects then will manage the\nindividual AsyncSockets.  While AsyncSockets *can* be moved between\nevent bases, most users just tie them to a single event base to get\nbetter cache locality, and to avoid locking.\n\nMultiple ServerSockets can be made, but currently the linux kernel has\na lock on accept()ing from a port, preventing more than ~20k accepts /\nsec.  There are various workarounds (SO_REUSEPORT), but generally\nclients should be using connection pooling instead when possible.\n\nSince AsyncServerSocket provides an fd, an AsyncSSLSocket or\nAsyncSocket can be made using the same codepath\n\n#### TAsyncUDPServerSocket\n\nSimilar to AsyncServerSocket, but for UDP messages - messages are\nread() on a single thread, and then fanned out to multiple worker\nthreads.\n\n### NotificationQueue (EventFD or pipe notifications)\n\nNotificationQueue is used to send messages between threads in the\n*same process*.  It is what backs EventBase::runInEventBaseThread(),\nso it is unlikely you'd want to use it directly instead of using\nrunInEventBaseThread().\n\nAn eventFD (for kernels > 2.6.30) or pipe (older kernels) are added to\nthe EventBase loop to wake up threads receiving messages.   The queue\nitself is a spinlock-guarded list.   Since we are almost always\ntalking about a single sender thread and a single receiver (although\nthe code works just fine for multiple producers and multiple\nconsumers), the spinlock is almost always uncontended, and we haven't\nseen any perf issues with it in practice.\n\nThe eventfd or pipe is only notified if the thread isn't already\nawake, to avoid syscalls.  A naive implementation that does one write\nper message in the queue, or worse, writes the whole message to the\nqueue, would be significantly slower.\n\nIf you need to send messages *between processes*, you would have to\nwrite the whole message to the pipe, and manage the pipe size.  See\nAsyncPipe.\n\n### AsyncTimeout\n\nAn individual timeout callback that can be installed in the event\nloop.   For code cleanliness and clarity, timeouts are separated from\nsockets.   There is one fd used per AsyncTimeout.  This is a pretty\nserious restriction, so the two below subclasses were made to support\nmultiple timeouts using a single fd.\n\n#### HHWheelTimer\n\nImplementation of a [hashed hierarchical wheel\ntimer](http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf).\nAny timeout time can be used, with O(1) insertion, deletion, and\ncallback time.  The wheel itself takes up some amount of space, and\nwheel timers have to have a constant tick, consuming a constant amount\nof CPU.\n\nAn alternative to a wheel timer would be a heap of callbacks sorted by\ntimeout time, but would change the big-O to O(log n).  In our\nexperience, the average server has thousands to hundreds of thousands\nof open sockets, and the common case is to add and remove timeouts\nwithout them ever firing, assuming the server is able to keep up with\nthe load.  Therefore O(log n) insertion time overshadows the extra CPU\nconsumed by a wheel timer tick.\n\n#### TAsyncTimeoutSet\n\nNOTE: currently in proxygen codebase.\n\nIf we assume that all timeouts scheduled use the same timeout time, we\ncan keep O(1) insertion time: just schedule the new timeout at the\ntail of the list, along with the time it was actually added.  When the\ncurrent timeout fires, we look at the new head of the list, and\nschedule AsyncTimeout to fire at the difference between the current\ntime and the scheduled time (which probably isn't the same as the\ntimeout time.)\n\nThis requires all AsyncTimeoutSets timeouts to have the same timeout\ntime though, which in practice means many AsyncTimeoutSets are needed\nper application.   Using HHWheelTimer instead can clean up the code quite\na bit, because only a single HHWheelTimer is needed per thread, as\nopposed to one AsyncTimeoutSet per timeout time per thread.\n\n### AsyncSignalHandler\n\nUsed to handle AsyncSignals.  Similar to AsyncTimeout, for code\nclarity, we don't reuse the same fd as a socket to receive signals.\n\n### AsyncPipe\n\nAsync reads/writes to a unix pipe, to send data between processes.\n\n## Helper Classes\n\n### RequestContext (in Request.h)\n\nSince messages are frequently passed between threads with\nrunInEventBaseThread(), ThreadLocals don't work for messages.\nInstead, RequestContext can be used, which is saved/restored between\nthreads.  Major uses for this include:\n\n* NUMA: saving the numa node the code was running on, and explicitly\n  running it on the same node in other threadpools / eventbases\n* Tracing: tracing requests dapper-style intra machine, as well as\n  between threads themselves.\n\nIn this library only runInEventBaseThread save/restores the request\ncontext, although other Facebook libraries that pass requests between\nthreads do also: folly::future, and fbthrift::ThreadManager, etc\n\n### DelayedDestruction\n\nSince EventBase callbacks already have the EventHandler and EventBase\non the stack, calling `delete` on either of these objects would most\nlikely result in a segfault.  Instead, these objects inherit from\nDelayedDestruction, which provides reference counting in the\ncallbacks.  Instead of delete, `destroy()` is called, which notifies\nthat is ready to be destroyed.  In each of the callbacks there is a\nDestructorGuard, which prevents destruction until all the Guards are\ngone from the stack, when the actual delete method is called.\n\nDelayedDestruction can be a painful to use, since shared_ptrs and\nunique_ptrs need to have a special DelayedDestruction destructor\ntype.  It's also pretty easy to forget to add a DestructorGuard in\ncode that calls callbacks.  But it is well worth it to avoid queuing\ncallbacks, and the improved P99 times as a result.\n\n### DestructorCheck\n\nOften for an object requesting callbacks from other components (timer,\nsocket connect, etc.) there is a chance that the requestor will be\ndeallocated before it'll receive the callback.  One of the ways to avoid\ndereferencing the deallocated object from callbacks is to derive the\nobject from DelayedDestruction, and add a delayed destruction guard\nto the callback context.  In case if keeping the object around until\nall the requested callbacks fire is too expensive, or if the callback\nrequestor can't have private destructor (it's allocated on the stack,\nor as a member of a larger object), DestructorCheck can be used.\nDestructorCheck is not affecting object life time. It helps other\ncomponent to detect safely that the tracked object was deallocated.\n\nThe object requesting the callback must be derived from DestructorCheck.\nThe callback context should contain an instance of\nDestructorCheck::Safety object initialized with a reference to the\nobject requesting the callback.  Safety object can be captured by value\nin the callback lambda, or explicitly added to a predefined callback\ncontext class. Multiple instances of Safety object can be instantiated\nfor the same tracked object.  Once the callback is invoked, before\ndereferencing the requester object, callback code should make sure that\n`destroyed()` method for the corresponding Safety object returns false.\n\n### EventBaseManager\n\nDANGEROUS.\n\nSince there is ususally only a single EventBase per thread, why not\nmake EventBase managed by a threadlocal?  Sounds easy!  But there are\nseveral catches:\n\n* The EventBase returned by `EventBaseManager::get()->getEventBase()`\n  may not actually be running.\n* There may be more than one event base in the thread (unusual), or\n  the EventBase in the code may not be registerd in EventBaseManager.\n* The event bases in EventBaseManager may be used for different\n  purposes, i.e. some are AsyncSocket threads, and some are\n  AsyncServerSocket threads:  So you can't just grab the list of\n  EventBases and call runInEventBaseThread() on all of them and expect\n  it to do the right thing.\n\nA much safer option is to explicitly pass around an EventBase, or use\nan explicit pool of EventBases.\n\n### SSLContext\n\nSSL helper routines to load / verify certs.  Used with\nAsyncSSLSocket.\n\n## Generic Multithreading Advice\n\nFacebook has a lot of experience running services.  For background\nreading, see [The C10k problem](http://www.kegel.com/c10k.html) and\n[Fast UNIX\nservers](http://nick-black.com/dankwiki/index.php/Fast_UNIX_Servers)\n\nSome best practices we've found:\n\n1. It's much easier to maintain latency expectations when each\n   EventBase thread is used for only a single purpose:\n   AsyncServerSocket, or inbound AsyncSocket, or in proxies, outbound\n   AsyncSocket calls.   In a perfect world, one EventBase per thread\n   per core would be enough, but the implementor needs to be extremely\n   diligent to make sure all CPU work is moved off of the IO threads to\n   prevent slow read/write/closes of fds.\n2. **ANY** work that is CPU intensive should be offloaded to a pool of\n   CPU-bound threads, instead of being done in the EventBase threads.\n   runInEventBaseThread() is fast:  It can be called millions of times\n   per second before the spinlock becomes an issue - so passing the\n   request off to a different thread is probably fine perf wise.\n3. In contrast to the first two recommendations, if there are more\n   total threads than cores, context switching overhead can become an\n   issue.  In particular we have seen this be an issue when a\n   CPU-intensive thread blocks the scheduling of an IO thread, using\n   the linux `perf sched` tool.\n4. For async programming, in contrast to synchronous systems, managing\n   load is extremely hard - it is better to use out-of-band methods to\n   notify of overload, such as timeouts, or CPU usage.  For sync\n   systems, you are almost always limited by the number of threads.\n   For more details see [No Time for\n   Asynchrony](https://www.usenix.org/legacy/event/hotos09/tech/full_papers/aguilera/aguilera.pdf)\n"
  },
  {
    "path": "folly/io/async/Request.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/Request.h>\n\n#include <folly/GLog.h>\n#include <folly/concurrency/container/SingleWriterFixedHashMap.h>\n#include <folly/tracing/StaticTracepoint.h>\n\nnamespace folly {\n// Thread-local cache of raw RequestContext.\n// Updated by setContext() and setShallowCopyContext() to avoid calling\n// SingletonThreadLocal::get() in locations requiring async-signal-safe access.\nthread_local RequestContext* gAsyncSignalSafeRequestContextCache = nullptr;\n\nRequestContext* getCachedRequestContext() {\n  return gAsyncSignalSafeRequestContextCache;\n}\n\nRequestToken::RequestToken(const std::string& str) {\n  auto& cache = getCache();\n  {\n    auto c = cache.rlock();\n    auto res = c->find(str);\n    if (res != c->end()) {\n      token_ = res->second;\n      return;\n    }\n  }\n  auto c = cache.wlock();\n  auto res = c->find(str);\n  if (res != c->end()) {\n    token_ = res->second;\n    return;\n  }\n  static uint32_t nextToken{1};\n\n  token_ = nextToken++;\n  (*c)[str] = token_;\n}\n\nstd::string RequestToken::getDebugString() const {\n  auto& cache = getCache();\n  auto c = cache.rlock();\n  for (auto& v : *c) {\n    if (v.second == token_) {\n      return v.first;\n    }\n  }\n  throw std::logic_error(\"Could not find debug string in RequestToken\");\n}\n\nSynchronized<F14FastMap<std::string, uint32_t>>& RequestToken::getCache() {\n  static Indestructible<Synchronized<F14FastMap<std::string, uint32_t>>> cache;\n  return *cache;\n}\n\nFOLLY_ALWAYS_INLINE\nvoid RequestData::acquireRef() {\n  auto rc = keepAliveCounter_.fetch_add(\n      kClearCount + kDeleteCount, std::memory_order_relaxed);\n  DCHECK_GE(rc, 0);\n}\n\nvoid RequestData::releaseRefClearOnly() {\n  auto rc =\n      keepAliveCounter_.fetch_sub(kClearCount, std::memory_order_acq_rel) -\n      kClearCount;\n  DCHECK_GT(rc, 0);\n  if (rc < kClearCount) {\n    this->onClear();\n  }\n}\n\nvoid RequestData::releaseRefDeleteOnly() {\n  auto rc =\n      keepAliveCounter_.fetch_sub(kDeleteCount, std::memory_order_acq_rel) -\n      kDeleteCount;\n  DCHECK_GE(rc, 0);\n  if (rc == 0) {\n    delete this;\n  }\n}\n\nFOLLY_ALWAYS_INLINE\nvoid RequestData::releaseRefClearDelete() {\n  auto rc = keepAliveCounter_.load(std::memory_order_acquire);\n  if (FOLLY_LIKELY(rc == (kClearCount + kDeleteCount))) {\n    this->onClear();\n    delete this;\n  } else {\n    releaseRefClearDeleteSlow();\n  }\n}\n\nFOLLY_NOINLINE\nvoid RequestData::releaseRefClearDeleteSlow() {\n  releaseRefClearOnly();\n  releaseRefDeleteOnly();\n}\n\n// The Combined struct keeps the two structures for context data\n// and callbacks together, so that readers can protect consistent\n// versions of the two structures together using hazard pointers.\nstruct RequestContext::State::Combined : hazptr_obj_base<Combined> {\n  static constexpr size_t kInitialCapacity = 4;\n  static constexpr size_t kSlackReciprocal = 4; // unused >= 1/4 capacity\n\n  // This must be optimized for lookup, its hot path is getContextData\n  // Efficiency of copying the container also matters in setShallowCopyContext\n  SingleWriterFixedHashMap<RequestToken, RequestData*> requestData_;\n  // This must be optimized for iteration, its hot path is setContext\n  SingleWriterFixedHashMap<RequestData*, int> callbackData_;\n  // Vector of cleared data. Accessed only sequentially by writers.\n  std::vector<std::pair<RequestToken, RequestData*>> cleared_;\n\n  Combined()\n      : requestData_(kInitialCapacity), callbackData_(kInitialCapacity) {}\n\n  Combined(const Combined& o)\n      : Combined(o.requestData_.capacity(), o.callbackData_.capacity(), o) {}\n\n  Combined(size_t dataCapacity, size_t callbackCapacity, const Combined& o)\n      : requestData_(dataCapacity, o.requestData_),\n        callbackData_(callbackCapacity, o.callbackData_) {}\n\n  Combined(Combined&&) = delete;\n  Combined& operator=(const Combined&) = delete;\n  Combined& operator=(Combined&&) = delete;\n\n  ~Combined() { releaseDataRefs(); }\n\n  /* acquireDataRefs - Called at most once per Combined instance. */\n  void acquireDataRefs() {\n    for (auto it = requestData_.begin(); it != requestData_.end(); ++it) {\n      auto p = it.value();\n      if (p) {\n        p->acquireRef();\n      }\n    }\n  }\n\n  /* releaseDataRefs - Called only once from ~Combined */\n  void releaseDataRefs() {\n    if (!cleared_.empty()) {\n      for (auto& pair : cleared_) {\n        pair.second->releaseRefDeleteOnly();\n        requestData_.erase(pair.first);\n      }\n    }\n    for (auto it = requestData_.begin(); it != requestData_.end(); ++it) {\n      RequestData* data = it.value();\n      if (data) {\n        data->releaseRefClearDelete();\n      }\n    }\n  }\n\n  void debugCheckConsistency() {\n    if constexpr (kIsDebug) {\n      size_t numRequestData = 0;\n      size_t numHasCallback = 0;\n      for (auto it = requestData_.begin(); it != requestData_.end(); ++it) {\n        ++numRequestData;\n        if (it.value() && it.value()->hasCallback()) {\n          ++numHasCallback;\n          CHECK(callbackData_.contains(it.value()))\n              << it.key().getDebugString();\n        }\n      }\n      CHECK_EQ(numRequestData, requestData_.size());\n      size_t numCallbackData = 0;\n      for (auto it = callbackData_.begin(); it != callbackData_.end(); ++it) {\n        ++numCallbackData;\n        CHECK(it.key());\n      }\n      CHECK_EQ(numHasCallback, numCallbackData);\n      CHECK_EQ(numCallbackData, callbackData_.size());\n    }\n  }\n\n  /* needExpand */\n  bool needExpand() {\n    return needExpandRequestData() || needExpandCallbackData();\n  }\n\n  /* needExpandRequestData */\n  bool needExpandRequestData() {\n    return kSlackReciprocal * (requestData_.available() - 1) <\n        requestData_.capacity();\n  }\n\n  /* needExpandCallbackData */\n  bool needExpandCallbackData() {\n    return kSlackReciprocal * (callbackData_.available() - 1) <\n        callbackData_.capacity();\n  }\n}; // Combined\n\nRequestContext::State::State() = default;\n\nFOLLY_ALWAYS_INLINE\nRequestContext::State::State(const State& o) {\n  // Even though Combined's maps can be individually iterated without\n  // synchronization, we need a consistent snapshot, so we have to synchronize\n  // with writers.\n  std::shared_lock lock(o.mutex_);\n  Combined* oc = o.combined();\n  if (oc) {\n    auto p = new Combined(*oc);\n    p->debugCheckConsistency();\n    p->acquireDataRefs();\n    setCombined(p);\n  }\n}\n\nRequestContext::State::~State() {\n  cohort_.shutdown_and_reclaim();\n  auto p = combined();\n  if (p) {\n    delete p;\n  }\n}\n\nclass [[nodiscard]] RequestContext::State::LockGuard {\n public:\n  explicit LockGuard(RequestContext::State& state)\n      : state_(state), lock_(state.mutex_) {}\n\n  ~LockGuard() {\n    // The state is only locked on modifications, so we can invalidate the\n    // thread caches every time the lock is released. In some cases no actual\n    // changes to the state may have been performed, but we conservatively\n    // invalidate anyway, as any modification operations are infrequent compared\n    // to reads.\n    state_.version_.store(processLocalUniqueId(), std::memory_order_release);\n  }\n\n private:\n  LockGuard(const LockGuard&) = delete;\n  LockGuard(LockGuard&&) = delete;\n  LockGuard& operator=(const LockGuard&) = delete;\n  LockGuard& operator=(LockGuard&&) = delete;\n\n  RequestContext::State& state_;\n  std::unique_lock<folly::SharedMutex> lock_;\n};\n\nFOLLY_ALWAYS_INLINE\nRequestContext::State::Combined* RequestContext::State::combined() const {\n  return combined_.load(std::memory_order_acquire);\n}\n\nFOLLY_ALWAYS_INLINE\nRequestContext::State::Combined* RequestContext::State::ensureCombined() {\n  auto c = combined();\n  if (!c) {\n    c = new Combined;\n    setCombined(c);\n  }\n  return c;\n}\n\nFOLLY_ALWAYS_INLINE\nvoid RequestContext::State::setCombined(Combined* p) {\n  p->set_cohort_tag(&cohort_);\n  combined_.store(p, std::memory_order_release);\n}\n\nFOLLY_ALWAYS_INLINE\nbool RequestContext::State::doSetContextData(\n    const RequestToken& token,\n    std::unique_ptr<RequestData>& data,\n    DoSetBehaviour behaviour,\n    bool safe) {\n  SetContextDataResult result;\n  if (safe) {\n    result = doSetContextDataHelper(token, data, behaviour, safe);\n  } else {\n    LockGuard lock{*this};\n    result = doSetContextDataHelper(token, data, behaviour, safe);\n  }\n  if (result.unexpected) {\n    FB_LOG_ONCE(WARNING) << \"Calling RequestContext::setContextData for \"\n                         << token.getDebugString() << \" but it is already set\";\n  }\n  if (result.replaced) {\n    result.replaced->retire(); // Retire to hazptr library\n  }\n  return result.changed;\n}\n\nFOLLY_ALWAYS_INLINE\nRequestContext::State::SetContextDataResult\nRequestContext::State::doSetContextDataHelper(\n    const RequestToken& token,\n    std::unique_ptr<RequestData>& data,\n    DoSetBehaviour behaviour,\n    bool safe) {\n  bool unexpected = false;\n  Combined* cur = ensureCombined();\n  Combined* replaced = nullptr;\n  auto it = cur->requestData_.find(token);\n  bool found = it != cur->requestData_.end();\n  if (found) {\n    if (behaviour == DoSetBehaviour::SET_IF_ABSENT) {\n      return {\n          false /* no changes made */,\n          false /* nothing unexpected */,\n          nullptr /* combined not replaced */};\n    }\n    RequestData* oldData = it.value();\n    // Always erase old data (and run onUnset callback, if any).\n    // Old data will always be overwritten either by the new data\n    // (if behavior is OVERWRITE) or by nullptr (if behavior is SET).\n    Combined* newCombined = eraseOldData(cur, token, oldData, safe);\n    DCHECK(oldData != nullptr || newCombined == nullptr);\n    if (newCombined) {\n      replaced = cur;\n      cur = newCombined;\n    }\n    if (behaviour == DoSetBehaviour::SET) {\n      // The expected behavior for SET when found is to reset the\n      // pointer and warn, without updating to the new data.\n      bool inserted = cur->requestData_.insert(token, nullptr);\n      DCHECK(inserted);\n      unexpected = true;\n    } else {\n      DCHECK(behaviour == DoSetBehaviour::OVERWRITE);\n    }\n  }\n  if (!unexpected) {\n    // Replace combined if needed, call onSet if any, insert new data.\n    Combined* newCombined = insertNewData(cur, token, data, found);\n    if (newCombined) {\n      replaced = cur;\n      cur = newCombined;\n    }\n  }\n  if (replaced) {\n    // Now the new Combined is consistent. Safe to publish.\n    setCombined(cur);\n  }\n  return {\n      true, /* changes were made */\n      unexpected,\n      replaced};\n}\n\nFOLLY_ALWAYS_INLINE\nRequestContext::State::Combined* FOLLY_NULLABLE\nRequestContext::State::eraseOldData(\n    RequestContext::State::Combined* cur,\n    const RequestToken& token,\n    RequestData* olddata,\n    bool safe) {\n  Combined* newCombined = nullptr;\n  // Call onUnset, if any.\n  if (olddata && olddata->hasCallback()) {\n    olddata->onUnset();\n    bool erased = cur->callbackData_.erase(olddata);\n    DCHECK(erased);\n  }\n  if (safe || olddata == nullptr) {\n    // If the caller guarantees thread-safety or the old data is null,\n    // then erase the entry in the current version.\n    bool erased = cur->requestData_.erase(token);\n    DCHECK(erased);\n    if (olddata) {\n      olddata->releaseRefClearDelete();\n    }\n  } else {\n    // If there may be concurrent readers, then copy-on-erase.\n    // Update the data reference counts to account for the\n    // existence of the new copy.\n    newCombined = new Combined(*cur);\n    bool erased = newCombined->requestData_.erase(token);\n    DCHECK(erased);\n    newCombined->acquireDataRefs();\n  }\n  return newCombined;\n}\n\nFOLLY_ALWAYS_INLINE\nRequestContext::State::Combined* FOLLY_NULLABLE\nRequestContext::State::insertNewData(\n    RequestContext::State::Combined* cur,\n    const RequestToken& token,\n    std::unique_ptr<RequestData>& data,\n    bool found) {\n  Combined* newCombined = nullptr;\n  // Update value to point to the new data.\n  if (!found && cur->needExpand()) {\n    // Replace the current Combined with an expanded one\n    newCombined = expand(cur);\n    cur = newCombined;\n    cur->acquireDataRefs();\n  }\n  if (data && data->hasCallback()) {\n    // If data has callback, insert in callback structure, call onSet\n    bool inserted = cur->callbackData_.insert(data.get(), true);\n    DCHECK(inserted);\n    data->onSet();\n  }\n  if (data) {\n    data->acquireRef();\n  }\n  bool inserted = cur->requestData_.insert(token, data.release());\n  DCHECK(inserted);\n  return newCombined;\n}\n\nFOLLY_ALWAYS_INLINE\nbool RequestContext::State::hasContextData(const RequestToken& token) const {\n  hazptr_local<1> h;\n  Combined* combined = h[0].protect(combined_);\n  return combined ? combined->requestData_.contains(token) : false;\n}\n\nFOLLY_ALWAYS_INLINE\nRequestData* FOLLY_NULLABLE\nRequestContext::State::getContextData(const RequestToken& token) {\n  hazptr_local<1> h;\n  Combined* combined = h[0].protect(combined_);\n  if (!combined) {\n    return nullptr;\n  }\n  auto& reqData = combined->requestData_;\n  auto it = reqData.find(token);\n  return it == reqData.end() ? nullptr : it.value();\n}\n\nFOLLY_ALWAYS_INLINE\nconst RequestData* FOLLY_NULLABLE\nRequestContext::State::getContextData(const RequestToken& token) const {\n  hazptr_local<1> h;\n  Combined* combined = h[0].protect(combined_);\n  if (!combined) {\n    return nullptr;\n  }\n  auto& reqData = combined->requestData_;\n  auto it = reqData.find(token);\n  return it == reqData.end() ? nullptr : it.value();\n}\n\nFOLLY_ALWAYS_INLINE\nvoid RequestContext::State::onSet() {\n  // Don't use hazptr_local because callback may use hazptr\n  hazptr_holder<> h = make_hazard_pointer<>();\n  Combined* combined = h.protect(combined_);\n  if (!combined) {\n    return;\n  }\n  auto& cb = combined->callbackData_;\n  for (auto it = cb.begin(); it != cb.end(); ++it) {\n    it.key()->onSet();\n  }\n}\n\nFOLLY_ALWAYS_INLINE\nvoid RequestContext::State::onUnset() {\n  // Don't use hazptr_local because callback may use hazptr\n  hazptr_holder<> h = make_hazard_pointer<>();\n  Combined* combined = h.protect(combined_);\n  if (!combined) {\n    return;\n  }\n  auto& cb = combined->callbackData_;\n  for (auto it = cb.begin(); it != cb.end(); ++it) {\n    it.key()->onUnset();\n  }\n}\n\nvoid RequestContext::State::clearContextData(const RequestToken& token) {\n  RequestData* data;\n  Combined* replaced = nullptr;\n  { // Lock mutex_\n    LockGuard lock{*this};\n    Combined* cur = combined();\n    if (!cur) {\n      return;\n    }\n    auto it = cur->requestData_.find(token);\n    if (it == cur->requestData_.end()) {\n      return;\n    }\n    data = it.value();\n    if (!data) {\n      bool erased = cur->requestData_.erase(token);\n      DCHECK(erased);\n      return;\n    }\n    if (data->hasCallback()) {\n      data->onUnset();\n      bool erased = cur->callbackData_.erase(data);\n      DCHECK(erased);\n    }\n    replaced = cur;\n    cur = new Combined(*replaced);\n    bool erased = cur->requestData_.erase(token);\n    DCHECK(erased);\n    cur->acquireDataRefs();\n    setCombined(cur);\n  } // Unlock mutex_\n  DCHECK(data);\n  data->releaseRefClearOnly();\n  DCHECK(replaced);\n  replaced->cleared_.emplace_back(std::make_pair(token, data));\n  replaced->retire();\n}\n\nRequestContext::State::Combined* RequestContext::State::expand(\n    RequestContext::State::Combined* c) {\n  size_t dataCapacity = c->requestData_.capacity();\n  if (c->needExpandRequestData()) {\n    dataCapacity *= 2;\n  }\n  size_t callbackCapacity = c->callbackData_.capacity();\n  if (c->needExpandCallbackData()) {\n    callbackCapacity *= 2;\n  }\n  return new Combined(dataCapacity, callbackCapacity, *c);\n}\n\nRequestContext::RequestContext() : rootId_(reinterpret_cast<intptr_t>(this)) {}\n\nRequestContext::RequestContext(intptr_t rootid) : rootId_(rootid) {}\n\nRequestContext::RequestContext(const RequestContext& ctx, intptr_t rootid, Tag)\n    : RequestContext(ctx) {\n  rootId_ = rootid;\n}\n\nRequestContext::RequestContext(const RequestContext& ctx, Tag)\n    : RequestContext(ctx) {}\n\n/* static */ std::shared_ptr<RequestContext> RequestContext::copyAsRoot(\n    const RequestContext& ctx, intptr_t rootid) {\n  return std::make_shared<RequestContext>(ctx, rootid, Tag{});\n}\n\n/* static */ std::shared_ptr<RequestContext> RequestContext::copyAsChild(\n    const RequestContext& ctx) {\n  return std::make_shared<RequestContext>(ctx, Tag{});\n}\n\nvoid RequestContext::setContextData(\n    const RequestToken& token, std::unique_ptr<RequestData> data) {\n  state_.doSetContextData(token, data, DoSetBehaviour::SET, false);\n}\n\nbool RequestContext::setContextDataIfAbsent(\n    const RequestToken& token, std::unique_ptr<RequestData> data) {\n  return state_.doSetContextData(\n      token, data, DoSetBehaviour::SET_IF_ABSENT, false);\n}\n\nvoid RequestContext::overwriteContextData(\n    const RequestToken& token, std::unique_ptr<RequestData> data, bool safe) {\n  state_.doSetContextData(token, data, DoSetBehaviour::OVERWRITE, safe);\n}\n\nbool RequestContext::hasContextData(const RequestToken& val) const {\n  return state_.hasContextData(val);\n}\n\nRequestData* FOLLY_NULLABLE\nRequestContext::getContextData(const RequestToken& val) {\n  return state_.getContextData(val);\n}\n\nconst RequestData* FOLLY_NULLABLE\nRequestContext::getContextData(const RequestToken& val) const {\n  return state_.getContextData(val);\n}\n\nvoid RequestContext::onSet() {\n  state_.onSet();\n}\n\nvoid RequestContext::onUnset() {\n  state_.onUnset();\n}\n\nvoid RequestContext::clearContextData(const RequestToken& val) {\n  state_.clearContextData(val);\n}\n\n/* static */ std::shared_ptr<RequestContext> RequestContext::setContext(\n    std::shared_ptr<RequestContext> const& newCtx) {\n  return setContext(copy(newCtx));\n}\n\n/* static */ std::shared_ptr<RequestContext> RequestContext::setContext(\n    std::shared_ptr<RequestContext>&& newCtx_) {\n  auto newCtx = std::move(newCtx_); // enforce that it is really moved-from\n\n  auto& staticCtx = getStaticContext();\n  if (newCtx == staticCtx.requestContext) {\n    return newCtx;\n  }\n\n  FOLLY_SDT(\n      folly,\n      request_context_switch_before,\n      staticCtx.requestContext.get(),\n      newCtx.get(),\n      staticCtx.requestContext ? staticCtx.requestContext->getRootId() : 0,\n      newCtx ? newCtx->getRootId() : 0);\n\n  std::shared_ptr<RequestContext> prevCtx;\n  RequestContext* curCtx = staticCtx.requestContext.get();\n  bool checkCur = curCtx && curCtx->state_.combined();\n  bool checkNew = newCtx && newCtx->state_.combined();\n  if (checkCur && checkNew) {\n    hazptr_array<2> h = make_hazard_pointer_array<2>();\n    auto curc = h[0].protect(curCtx->state_.combined_);\n    auto newc = h[1].protect(newCtx->state_.combined_);\n    auto& curcb = curc->callbackData_;\n    auto& newcb = newc->callbackData_;\n    for (auto it = curcb.begin(); it != curcb.end(); ++it) {\n      DCHECK(it.key());\n      auto data = it.key();\n      if (!newcb.contains(data)) {\n        data->onUnset();\n      }\n    }\n    prevCtx = std::move(staticCtx.requestContext);\n    staticCtx.requestContext = std::move(newCtx);\n    staticCtx.rootId.store(\n        staticCtx.requestContext->getRootId(), std::memory_order_relaxed);\n    for (auto it = newcb.begin(); it != newcb.end(); ++it) {\n      DCHECK(it.key());\n      auto data = it.key();\n      if (!curcb.contains(data)) {\n        data->onSet();\n      }\n    }\n  } else {\n    if (curCtx) {\n      curCtx->state_.onUnset();\n    }\n    prevCtx = std::move(staticCtx.requestContext);\n    staticCtx.requestContext = std::move(newCtx);\n    if (staticCtx.requestContext) {\n      staticCtx.rootId.store(\n          staticCtx.requestContext->rootId_, std::memory_order_relaxed);\n      staticCtx.requestContext->state_.onSet();\n    } else {\n      staticCtx.rootId.store(0, std::memory_order_relaxed);\n    }\n  }\n  gAsyncSignalSafeRequestContextCache = staticCtx.requestContext.get();\n  // Notify the Watchers via the registry\n  getWatcherRegistry().invokeWatchers(prevCtx, staticCtx.requestContext);\n  return prevCtx;\n}\n\n/* static */ RequestContext::SetContextWatcherRegistry&\nRequestContext::getWatcherRegistry() {\n  static SetContextWatcherRegistry registry;\n\n  return registry;\n}\n\n/* static */ void RequestContext::addSetContextWatcher(\n    RequestContext::SetContextWatcherSig& func) {\n  getWatcherRegistry().addWatcher(func);\n}\n\n/* static */ std::shared_ptr<RequestContext> RequestContext::saveContext() {\n  auto* staticContext = tryGetStaticContext();\n  return staticContext ? staticContext->requestContext : nullptr;\n}\n\nRequestContext::StaticContext::~StaticContext() {\n  // If there is an active request context, reset requestContext before\n  // destroying it, as RequestData destructors (or onClear()) could try to\n  // access the current request context and copy a shared_ptr while being\n  // destroyed.\n  std::ignore = std::exchange(requestContext, {});\n}\n\n/* static */ RequestContext::StaticContext& RequestContext::getStaticContext() {\n  return StaticContextThreadLocal::get();\n}\n\n/* static */ RequestContext::StaticContext*\nRequestContext::tryGetStaticContext() {\n  return StaticContextThreadLocal::try_get();\n}\n\n/* static */ RequestContext::StaticContextAccessor\nRequestContext::accessAllThreads() {\n  return StaticContextAccessor{StaticContextThreadLocal::accessAllThreads()};\n}\n\n/* static */ std::vector<RequestContext::RootIdInfo>\nRequestContext::getRootIdsFromAllThreads() {\n  std::vector<RootIdInfo> result;\n  auto accessor = RequestContext::accessAllThreads();\n  for (auto it = accessor.begin(); it != accessor.end(); ++it) {\n    result.push_back(it.getRootIdInfo());\n  }\n  return result;\n}\n\n/* static */ std::shared_ptr<RequestContext>\nRequestContext::setShallowCopyContext() {\n  auto& parent = getStaticContext().requestContext;\n  auto child = parent\n      ? RequestContext::copyAsChild(*parent)\n      : std::make_shared<RequestContext>();\n  if (!parent) {\n    child->rootId_ = 0;\n  }\n  // Do not use setContext to avoid global set/unset\n  // Also rootId does not change so do not bother setting it.\n  std::swap(child, parent);\n  gAsyncSignalSafeRequestContextCache = parent.get();\n  getWatcherRegistry().invokeWatchers(child, parent);\n  return child;\n}\n\n/* static */ RequestContext* RequestContext::get() {\n  if (auto* staticContext = tryGetStaticContext()) {\n    if (auto& context = staticContext->requestContext) {\n      return context.get();\n    }\n  }\n\n  static RequestContext defaultContext(0);\n  return std::addressof(defaultContext);\n}\n\n/* static */ RequestContext* RequestContext::try_get() {\n  if (auto* staticContext = tryGetStaticContext()) {\n    return staticContext->requestContext.get();\n  }\n  return nullptr;\n}\n\n#ifndef NDEBUG\nDCheckRequestContextRestoredGuard::~DCheckRequestContextRestoredGuard() {\n  CHECK_EQ(prev_.get(), RequestContext::try_get());\n}\n#endif\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/Request.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <atomic>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include <folly/SharedMutex.h>\n#include <folly/SingletonThreadLocal.h>\n#include <folly/Synchronized.h>\n#include <folly/concurrency/ProcessLocalUniqueId.h>\n#include <folly/container/F14Map.h>\n#include <folly/detail/Iterators.h>\n#include <folly/synchronization/Hazptr.h>\n\nnamespace folly {\n\n/*\n * A token to be used to fetch data from RequestContext.\n * Generally you will want this to be a static, created only once using a\n * string, and then only copied. The string constructor is expensive.\n */\nclass RequestToken {\n public:\n  RequestToken() = default;\n  explicit RequestToken(const std::string& str);\n\n  bool operator==(const RequestToken& other) const {\n    return token_ == other.token_;\n  }\n\n  // Slow, use only for debug log messages.\n  std::string getDebugString() const;\n\n  friend struct std::hash<folly::RequestToken>;\n\n private:\n  static Synchronized<F14FastMap<std::string, uint32_t>>& getCache();\n\n  uint32_t token_;\n};\nstatic_assert(\n    std::is_trivially_destructible<RequestToken>::value,\n    \"must be trivially destructible\");\n\n} // namespace folly\n\nnamespace std {\ntemplate <>\nstruct hash<folly::RequestToken> {\n  size_t operator()(const folly::RequestToken& token) const {\n    return hash<uint32_t>()(token.token_);\n  }\n};\n} // namespace std\n\nnamespace folly {\n\n// Some request context that follows an async request through a process\n// Everything in the context must be thread safe\n\nclass RequestData {\n public:\n  virtual ~RequestData() = default;\n\n  // Avoid calling RequestContext::setContextData, setContextDataIfAbsent, or\n  // clearContextData from these callbacks. Doing so will cause deadlock. We\n  // could fix these deadlocks, but only at significant performance penalty, so\n  // just don't do it!\n\n  // hasCallback() applies only to onSet() and onUnset().\n  // onClear() is always executed exactly once.\n  virtual bool hasCallback() = 0;\n  // Callback executed when setting RequestContext. Make sure your RequestData\n  // instance overrides the hasCallback method to return true otherwise\n  // the callback will not be executed\n  virtual void onSet() {}\n  // Callback executed when unsetting RequestContext. Make sure your RequestData\n  // instance overrides the hasCallback method to return true otherwise\n  // the callback will not be executed\n  virtual void onUnset() {}\n  // Callback executed exactly once upon the release of the last\n  // reference to the request data (as a result of either a call to\n  // clearContextData or the destruction of a request context that\n  // contains a reference to the data). It can be overridden in\n  // derived classes. There may be concurrent executions of onSet()\n  // and onUnset() with that of onClear().\n  virtual void onClear() {}\n  // For debugging\n  int refCount() { return keepAliveCounter_.load(std::memory_order_acquire); }\n\n private:\n  // For efficiency, RequestContext provides a raw ptr interface.\n  // To support shallow copy, we need a shared ptr.\n  // To keep it as safe as possible (even if a raw ptr is passed back),\n  // the counter lives directly in RequestData.\n\n  friend class RequestContext;\n\n  static constexpr int kDeleteCount = 0x1;\n  static constexpr int kClearCount = 0x1000;\n\n  // Reference-counting functions.\n  // Increment the reference count.\n  void acquireRef();\n  // Decrement the reference count. Clear only if last.\n  void releaseRefClearOnly();\n  // Decrement the reference count. Delete only if last.\n  void releaseRefDeleteOnly();\n  // Decrement the reference count. Clear and delete if last.\n  void releaseRefClearDelete();\n  void releaseRefClearDeleteSlow();\n\n  std::atomic<int> keepAliveCounter_{0};\n};\n\n/**\n * ImmutableRequestData is a folly::RequestData that holds an immutable value.\n * It is thread-safe (a requirement of RequestData) because it is immutable.\n */\ntemplate <typename T>\nclass ImmutableRequestData : public folly::RequestData {\n public:\n  template <\n      typename... Args,\n      typename = typename std::enable_if<\n          std::is_constructible<T, Args...>::value>::type>\n  explicit ImmutableRequestData(Args&&... args) noexcept(\n      std::is_nothrow_constructible<T, Args...>::value)\n      : val_(std::forward<Args>(args)...) {}\n\n  const T& value() const { return val_; }\n\n  bool hasCallback() override { return false; }\n\n private:\n  const T val_;\n};\n\nusing RequestDataItem = std::pair<RequestToken, std::unique_ptr<RequestData>>;\n\nclass RequestContext {\n public:\n  RequestContext();\n  RequestContext(RequestContext&& ctx) = delete;\n  RequestContext& operator=(const RequestContext&) = delete;\n  RequestContext& operator=(RequestContext&&) = delete;\n\n  // copy ctor is disabled, use copyAsRoot/copyAsChild instead.\n  static std::shared_ptr<RequestContext> copyAsRoot(\n      const RequestContext& ctx, intptr_t rootid);\n  static std::shared_ptr<RequestContext> copyAsChild(const RequestContext& ctx);\n\n  // Creates a unique request context for this request, and sets it as current\n  // context. It will be propagated between queues / threads (where\n  // implemented).\n  //\n  // Whenever possible, prefer RequestContextScopeGuard instead of create() to\n  // make sure that RequestContext is reset to the original value when we exit\n  // the scope.\n  //\n  // Returns the previous request context.\n  static std::shared_ptr<RequestContext> create() {\n    return setContext(std::make_shared<RequestContext>());\n  }\n\n  // Returns the current context, if set, otherwise returns the default global\n  // request context.\n  //\n  // NOTE: This is a legacy method: there is almost never a good reason to use\n  // the default global request context. Prefer try_get() for new code.\n  static RequestContext* get();\n\n  // Get the current context, if it has already been set, or nullptr.\n  static RequestContext* try_get();\n\n  intptr_t getRootId() const { return rootId_; }\n\n  struct RootIdInfo {\n    intptr_t id;\n    std::thread::id tid;\n    uint64_t tidOS;\n  };\n  static std::vector<RootIdInfo> getRootIdsFromAllThreads();\n\n  // The following APIs are used to add, remove and access RequestData instance\n  // in the RequestContext instance, normally used for per-RequestContext\n  // tracking or callback on set and unset. These APIs are Thread-safe.\n  // These APIs are performance sensitive, so please ask if you need help\n  // profiling any use of these APIs.\n\n  // Add RequestData instance \"data\" to this RequestContext instance, with\n  // string identifier \"val\". If the same string identifier has already been\n  // used, will print a warning message for the first time, clear the existing\n  // RequestData instance for \"val\", and **not** add \"data\".\n  void setContextData(\n      const RequestToken& token, std::unique_ptr<RequestData> data);\n  void setContextData(\n      const std::string& val, std::unique_ptr<RequestData> data) {\n    setContextData(RequestToken(val), std::move(data));\n  }\n\n  // Add RequestData instance \"data\" to this RequestContext instance, with\n  // string identifier \"val\". If the same string identifier has already been\n  // used, return false and do nothing. Otherwise add \"data\" and return true.\n  bool setContextDataIfAbsent(\n      const RequestToken& token, std::unique_ptr<RequestData> data);\n  bool setContextDataIfAbsent(\n      const std::string& val, std::unique_ptr<RequestData> data) {\n    return setContextDataIfAbsent(RequestToken(val), std::move(data));\n  }\n\n  // Remove the RequestData instance with string identifier \"val\", if it exists.\n  void clearContextData(const RequestToken& val);\n  void clearContextData(const std::string& val) {\n    clearContextData(RequestToken(val));\n  }\n\n  // Returns true if and only if the RequestData instance with string identifier\n  // \"val\" exists in this RequestContext instnace.\n  bool hasContextData(const RequestToken& val) const;\n  bool hasContextData(const std::string& val) const {\n    return hasContextData(RequestToken(val));\n  }\n\n  // Get (constant) raw pointer of the RequestData instance with string\n  // identifier \"val\" if it exists, otherwise returns null pointer.\n  RequestData* getContextData(const RequestToken& val);\n  const RequestData* getContextData(const RequestToken& val) const;\n  RequestData* getContextData(const std::string& val) {\n    return getContextData(RequestToken(val));\n  }\n  const RequestData* getContextData(const std::string& val) const {\n    return getContextData(RequestToken(val));\n  }\n\n  // Same as getContextData(), but caching the RequestData pointer in\n  // thread-local storage to avoid the lookup cost. The thread cache is\n  // invalidated if the current request context changes or it gets modified.\n  //\n  // This can be used for RequestData that are queried very frequently. It\n  // should almost always be faster than getContextData(), but it consumes\n  // thread-local storage space, so it is worth doing only when a high hit rate\n  // is expected.\n  //\n  // The storage for the caches is associated to a type tag passed as template\n  // argument. This tag also contains the RequestToken key as a static member\n  // kToken. This guarantees that a given tag cannot be accidentally used with\n  // multiple tokens.\n  //\n  // For example:\n  //\n  // struct MyRequestDataTraits {\n  //   static inline const RequestToken kToken{\"my_request_data\"};\n  // };\n  //\n  // ...\n  // auto* data = ctx->getThreadCachedContextData<MyRequestDataTraits>();\n  //\n  template <class Traits>\n  RequestData* getThreadCachedContextData();\n\n  void onSet();\n  void onUnset();\n\n  // The following API is used to pass the context through queues / threads.\n  // saveContext is called to get a shared_ptr to the context, and\n  // setContext is used to reset it on the other side of the queue.\n  //\n  // Whenever possible, prefer RequestContextScopeGuard instead of setContext to\n  // make sure that RequestContext is reset to the original value when we exit\n  // the scope.\n  //\n  // A shared_ptr is used, because many request may fan out across\n  // multiple threads, or do post-send processing, etc.\n  //\n  // Returns the previous request context.\n  static std::shared_ptr<RequestContext> setContext(\n      std::shared_ptr<RequestContext> const& ctx);\n  static std::shared_ptr<RequestContext> setContext(\n      std::shared_ptr<RequestContext>&& newCtx_);\n\n  // Theses Watcher functions will be called after a setContext with the prev\n  // and current context\n  using SetContextWatcherSig = void(\n      const std::shared_ptr<RequestContext>& prev,\n      const std::shared_ptr<RequestContext>& ctx);\n  static void addSetContextWatcher(SetContextWatcherSig& func);\n\n private:\n  struct SetContextWatcherRegistry {\n    using Sig = SetContextWatcherSig;\n\n    struct Watcher {\n      Sig* func_;\n      Watcher* next_{nullptr};\n\n      explicit Watcher(Sig& func) : func_(&func) {}\n    };\n\n    std::atomic<Watcher*> watchers_{nullptr};\n\n    void addWatcher(Sig& func) {\n      auto watcherPtr = new Watcher(func);\n      auto* head = watchers_.load(std::memory_order_relaxed);\n      do {\n        watcherPtr->next_ = head;\n      } while (!watchers_.compare_exchange_weak(\n          head,\n          watcherPtr,\n          std::memory_order_acq_rel,\n          std::memory_order_relaxed));\n    }\n\n    void invokeWatchers(\n        const std::shared_ptr<RequestContext>& prev,\n        const std::shared_ptr<RequestContext>& ctx) {\n      auto* watcher = watchers_.load(std::memory_order_acquire);\n      while (watcher != nullptr) {\n        watcher->func_(prev, ctx);\n        watcher = watcher->next_;\n      }\n    }\n\n    ~SetContextWatcherRegistry() {\n      auto* watcher = watchers_.exchange(nullptr, std::memory_order_acquire);\n      while (watcher != nullptr) {\n        delete std::exchange(watcher, watcher->next_);\n      }\n    }\n  };\n\n  static SetContextWatcherRegistry& getWatcherRegistry();\n\n public:\n  static std::shared_ptr<RequestContext> saveContext();\n\n private:\n  struct Tag {};\n  RequestContext(const RequestContext& ctx) = default;\n\n public:\n  RequestContext(const RequestContext& ctx, intptr_t rootid, Tag tag);\n  RequestContext(const RequestContext& ctx, Tag tag);\n  explicit RequestContext(intptr_t rootId);\n\n  struct StaticContext {\n    StaticContext() = default;\n    ~StaticContext();\n\n    StaticContext(const StaticContext&) = delete;\n    StaticContext(StaticContext&&) = delete;\n    StaticContext& operator=(const StaticContext&) = delete;\n    StaticContext& operator=(StaticContext&&) = delete;\n\n    std::shared_ptr<RequestContext> requestContext;\n    std::atomic<intptr_t> rootId{0};\n  };\n\n private:\n  static StaticContext& getStaticContext();\n  static StaticContext* tryGetStaticContext();\n  static std::shared_ptr<RequestContext> setContextHelper(\n      std::shared_ptr<RequestContext>& newCtx, StaticContext& staticCtx);\n\n  using StaticContextThreadLocal = SingletonThreadLocal<\n      RequestContext::StaticContext,\n      RequestContext /* Tag */>;\n\n public:\n  class StaticContextAccessor {\n   private:\n    using Inner = StaticContextThreadLocal::Accessor;\n    using IteratorBase = Inner::Iterator;\n    using IteratorTag = typename IteratorBase::iterator_category;\n\n    Inner inner_;\n\n    explicit StaticContextAccessor(Inner&& inner) noexcept\n        : inner_(std::move(inner)) {}\n\n   public:\n    friend class RequestContext;\n\n    class Iterator\n        : public detail::IteratorAdaptor<\n              Iterator,\n              IteratorBase,\n              StaticContext,\n              IteratorTag> {\n      using Super = detail::\n          IteratorAdaptor<Iterator, IteratorBase, StaticContext, IteratorTag>;\n\n     public:\n      using Super::Super;\n\n      StaticContext& dereference() const { return *base(); }\n\n      RootIdInfo getRootIdInfo() const {\n        return {\n            base()->rootId.load(std::memory_order_relaxed),\n            base().getThreadId(),\n            base().getOSThreadId()};\n      }\n    };\n\n    StaticContextAccessor(const StaticContextAccessor&) = delete;\n    StaticContextAccessor& operator=(const StaticContextAccessor&) = delete;\n    StaticContextAccessor(StaticContextAccessor&&) = default;\n    StaticContextAccessor& operator=(StaticContextAccessor&&) = default;\n\n    Iterator begin() const { return Iterator(inner_.begin()); }\n    Iterator end() const { return Iterator(inner_.end()); }\n  };\n  // Returns an accessor object that blocks the construction and destruction of\n  // StaticContext objects on all threads. This is useful to quickly introspect\n  // the context from all threads while ensuring that their thread-local\n  // StaticContext object is not destroyed.\n  static StaticContextAccessor accessAllThreads();\n\n  // Start shallow copy guard implementation details:\n  // All methods are private to encourage proper use\n  friend struct ShallowCopyRequestContextScopeGuard;\n\n  // This sets a shallow copy of the current context as current,\n  // then return the previous context (so it can be reset later).\n  static std::shared_ptr<RequestContext> setShallowCopyContext();\n\n  // For functions with a parameter safe, if safe is true then the\n  // caller guarantees that there are no concurrent readers or writers\n  // accessing the structure.\n  void overwriteContextData(\n      const RequestToken& token,\n      std::unique_ptr<RequestData> data,\n      bool safe = false);\n  void overwriteContextData(\n      const std::string& val,\n      std::unique_ptr<RequestData> data,\n      bool safe = false) {\n    overwriteContextData(RequestToken(val), std::move(data), safe);\n  }\n\n  enum class DoSetBehaviour {\n    SET,\n    SET_IF_ABSENT,\n    OVERWRITE,\n  };\n\n  bool doSetContextDataHelper(\n      const RequestToken& token,\n      std::unique_ptr<RequestData>& data,\n      DoSetBehaviour behaviour,\n      bool safe = false);\n  bool doSetContextDataHelper(\n      const std::string& val,\n      std::unique_ptr<RequestData>& data,\n      DoSetBehaviour behaviour,\n      bool safe = false) {\n    return doSetContextDataHelper(RequestToken(val), data, behaviour, safe);\n  }\n\n  // State implementation with single-writer multi-reader data\n  // structures protected by hazard pointers for readers and a lock\n  // for writers.\n  struct State {\n    // Hazard pointer-protected combined structure for request data\n    // and callbacks.\n    struct Combined;\n    hazptr_obj_cohort<> cohort_; // For destruction order\n    std::atomic<Combined*> combined_{nullptr};\n    // Version used to invalidate getThreadCachedContextData() caches on\n    // modifications. A process-wide unique id is used (instead of, for example,\n    // a local counter) so that it is not necessary to compare the request\n    // context pointer as well. This saves one word of TLS and one comparison.\n    std::atomic<uint64_t> version_{processLocalUniqueId()};\n    // This should never be used directly. Use LockGuard so that thread caches\n    // are invalidated at the end of the critical section.\n    mutable folly::SharedMutex mutex_; // small exclusive mutex\n\n    State();\n    State(const State& o);\n    State(State&&) = delete;\n    State& operator=(const State&) = delete;\n    State& operator=(State&&) = delete;\n    ~State();\n\n   private:\n    friend class RequestContext;\n\n    struct SetContextDataResult {\n      bool changed; // Changes were made\n      bool unexpected; // Update was unexpected\n      Combined* replaced; // The combined structure was replaced\n    };\n\n    class LockGuard;\n\n    Combined* combined() const;\n    Combined* ensureCombined(); // Lazy allocation if needed\n    void setCombined(Combined* p);\n    Combined* expand(Combined* combined);\n    bool doSetContextData(\n        const RequestToken& token,\n        std::unique_ptr<RequestData>& data,\n        DoSetBehaviour behaviour,\n        bool safe);\n    bool hasContextData(const RequestToken& token) const;\n    RequestData* getContextData(const RequestToken& token);\n    const RequestData* getContextData(const RequestToken& token) const;\n    void onSet();\n    void onUnset();\n    void clearContextData(const RequestToken& token);\n    SetContextDataResult doSetContextDataHelper(\n        const RequestToken& token,\n        std::unique_ptr<RequestData>& data,\n        DoSetBehaviour behaviour,\n        bool safe);\n    Combined* eraseOldData(\n        Combined* cur,\n        const RequestToken& token,\n        RequestData* oldData,\n        bool safe);\n    Combined* insertNewData(\n        Combined* cur,\n        const RequestToken& token,\n        std::unique_ptr<RequestData>& data,\n        bool found);\n  }; // State\n  State state_;\n  // Shallow copies keep a note of the root context\n  intptr_t rootId_;\n};\nstatic_assert(sizeof(RequestContext) <= 64, \"unexpected size\");\n\n/**\n * RequestContextSaverScopeGuard allows to replace the current context\n * without switching back to original context, while ensuring that the original\n * context is restored on guard destruction.\n *\n * The constructor saves the current context but does not replace it; instead,\n * RequestContext::setContext() should be called directly. The original context\n * will be restored on guard destruction. This is different from\n * RequestContextScopeGuard which replaces the current context in construction.\n *\n * This enables taking advantage of the optimization in setContext() which skips\n * invoking the RequestData callbacks if the new context is the the same as the\n * current one. The use case is processing tasks in a loop which are likely to\n * share the same context.\n */\nclass RequestContextSaverScopeGuard {\n public:\n  RequestContextSaverScopeGuard()\n      : RequestContextSaverScopeGuard(RequestContext::saveContext()) {}\n\n  RequestContextSaverScopeGuard(const RequestContextSaverScopeGuard&) = delete;\n  RequestContextSaverScopeGuard& operator=(\n      const RequestContextSaverScopeGuard&) = delete;\n  RequestContextSaverScopeGuard(RequestContextSaverScopeGuard&&) = delete;\n  RequestContextSaverScopeGuard& operator=(RequestContextSaverScopeGuard&&) =\n      delete;\n\n  ~RequestContextSaverScopeGuard() {\n    RequestContext::setContext(std::move(prev_));\n  }\n\n protected:\n  explicit RequestContextSaverScopeGuard(std::shared_ptr<RequestContext>&& ctx)\n      : prev_(std::move(ctx)) {}\n\n private:\n  std::shared_ptr<RequestContext> prev_;\n};\n\n/**\n * Note: you probably want to use ShallowCopyRequestContextScopeGuard\n * This resets all other RequestData for the duration of the scope!\n */\nclass RequestContextScopeGuard : private RequestContextSaverScopeGuard {\n public:\n  // Create a new RequestContext and reset to the original value when\n  // this goes out of scope.\n  RequestContextScopeGuard()\n      : RequestContextSaverScopeGuard(RequestContext::create()) {}\n\n  // Set a RequestContext that was previously captured by saveContext(). It will\n  // be automatically reset to the original value when this goes out of scope.\n  explicit RequestContextScopeGuard(std::shared_ptr<RequestContext> const& ctx)\n      : RequestContextSaverScopeGuard(RequestContext::setContext(ctx)) {}\n  explicit RequestContextScopeGuard(std::shared_ptr<RequestContext>&& ctx)\n      : RequestContextSaverScopeGuard(\n            RequestContext::setContext(std::move(ctx))) {}\n};\n\n/**\n * This guard maintains all the RequestData pointers of the parent.\n * This allows to overwrite a specific RequestData pointer for the\n * scope's duration, without breaking others.\n *\n * Only modified pointers will have their set/onset methods called\n */\nstruct ShallowCopyRequestContextScopeGuard {\n  ShallowCopyRequestContextScopeGuard()\n      : prev_(RequestContext::setShallowCopyContext()) {}\n\n  /**\n   * Shallow copy then overwrite one specific RequestData\n   *\n   * Helper constructor which is a more efficient equivalent to\n   * \"clearRequestData\" then \"setRequestData\" after the guard.\n   */\n  ShallowCopyRequestContextScopeGuard(\n      const RequestToken& token, std::unique_ptr<RequestData> data)\n      : ShallowCopyRequestContextScopeGuard() {\n    RequestContext::get()->overwriteContextData(token, std::move(data), true);\n  }\n  ShallowCopyRequestContextScopeGuard(\n      const std::string& val, std::unique_ptr<RequestData> data)\n      : ShallowCopyRequestContextScopeGuard() {\n    RequestContext::get()->overwriteContextData(val, std::move(data), true);\n  }\n\n  /**\n   * Shallow copy then overwrite multiple RequestData instances\n   *\n   * Helper constructor which is more efficient than using multiple scope guards\n   * Accepts iterators to a container of <string/RequestToken, RequestData\n   * pointer> pairs\n   */\n  template <typename... Item>\n  explicit ShallowCopyRequestContextScopeGuard(\n      RequestDataItem&& first, Item&&... rest)\n      : ShallowCopyRequestContextScopeGuard(MultiTag{}, first, rest...) {}\n\n  ~ShallowCopyRequestContextScopeGuard() {\n    RequestContext::setContext(std::move(prev_));\n  }\n\n  ShallowCopyRequestContextScopeGuard(\n      const ShallowCopyRequestContextScopeGuard&) = delete;\n  ShallowCopyRequestContextScopeGuard& operator=(\n      const ShallowCopyRequestContextScopeGuard&) = delete;\n  ShallowCopyRequestContextScopeGuard(ShallowCopyRequestContextScopeGuard&&) =\n      delete;\n  ShallowCopyRequestContextScopeGuard& operator=(\n      ShallowCopyRequestContextScopeGuard&&) = delete;\n\n private:\n  struct MultiTag {};\n  template <typename... Item>\n  explicit ShallowCopyRequestContextScopeGuard(MultiTag, Item&... item)\n      : ShallowCopyRequestContextScopeGuard() {\n    auto rc = RequestContext::get();\n    auto go = [&](RequestDataItem& i) {\n      rc->overwriteContextData(i.first, std::move(i.second), true);\n    };\n\n    ((go(item)), ...);\n  }\n\n  std::shared_ptr<RequestContext> prev_;\n};\n\n// Debug-only guard that ensures that the current request context at destruction\n// is the same as the one at construction.\nclass [[maybe_unused]] DCheckRequestContextRestoredGuard {\n#ifndef NDEBUG\n public:\n  [[nodiscard]] DCheckRequestContextRestoredGuard()\n      : prev_(RequestContext::saveContext()) {}\n\n  ~DCheckRequestContextRestoredGuard();\n\n  DCheckRequestContextRestoredGuard(const DCheckRequestContextRestoredGuard&) =\n      delete;\n  DCheckRequestContextRestoredGuard& operator=(\n      const DCheckRequestContextRestoredGuard&) = delete;\n  DCheckRequestContextRestoredGuard(DCheckRequestContextRestoredGuard&&) =\n      delete;\n  DCheckRequestContextRestoredGuard& operator=(\n      DCheckRequestContextRestoredGuard&&) = delete;\n\n private:\n  std::shared_ptr<RequestContext> prev_;\n#endif\n};\n\ntemplate <class Traits>\n/* static */ FOLLY_EXPORT RequestData*\nRequestContext::getThreadCachedContextData() {\n  thread_local RequestData* cachedData = nullptr;\n  thread_local uint64_t cachedVersion = 0; // Allowed sentinel value.\n\n  // In case cache is invalid, version snapshot must be taken before performing\n  // the lookup.\n  uint64_t curVersion = state_.version_.load(std::memory_order_acquire);\n\n  if (curVersion == cachedVersion) {\n    return cachedData;\n  }\n\n  cachedData = getContextData(Traits::kToken);\n  cachedVersion = curVersion;\n  return cachedData;\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/SSLContext.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/SSLContext.h>\n\n#include <folly/Format.h>\n#include <folly/Memory.h>\n#include <folly/Random.h>\n#include <folly/SharedMutex.h>\n#include <folly/ssl/OpenSSLTicketHandler.h>\n#include <folly/ssl/PasswordCollector.h>\n#include <folly/ssl/SSLSessionManager.h>\n\n// ---------------------------------------------------------------------\n// SSLContext implementation\n// ---------------------------------------------------------------------\nnamespace folly {\n\nnamespace {\n\nint getExDataIndex() {\n  static auto index =\n      SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);\n  return index;\n}\n\n/**\n * Configure the given SSL context to use the given version.\n */\nvoid configureProtocolVersion(SSL_CTX* ctx, SSLContext::SSLVersion version) {\n  /*\n   * From the OpenSSL docs https://fburl.com/ii9k29qw:\n   * Setting the minimum or maximum version to 0, will enable protocol versions\n   * down to the lowest version, or up to the highest version supported by the\n   * library, respectively.\n   *\n   * We can use that as the default/fallback.\n   */\n  int minVersion = 0;\n  switch (version) {\n    case SSLContext::SSLVersion::TLSv1:\n      minVersion = TLS1_VERSION;\n      break;\n    case SSLContext::SSLVersion::SSLv3:\n      minVersion = SSL3_VERSION;\n      break;\n    case SSLContext::SSLVersion::TLSv1_2:\n      minVersion = TLS1_2_VERSION;\n      break;\n    case SSLContext::SSLVersion::TLSv1_3:\n      minVersion = TLS1_3_VERSION;\n      break;\n    case SSLContext::SSLVersion::SSLv2:\n    default:\n      // do nothing\n      break;\n  }\n  const auto setMinProtoResult = SSL_CTX_set_min_proto_version(ctx, minVersion);\n  DCHECK(setMinProtoResult == 1)\n      << sformat(\"unsupported min TLS protocol version: 0x{:04x}\", minVersion);\n}\n\nstatic int dispatchTicketCrypto(\n    SSL* ssl,\n    unsigned char* keyName,\n    unsigned char* iv,\n    EVP_CIPHER_CTX* cipherCtx,\n    HMAC_CTX* hmacCtx,\n    int encrypt) {\n  auto ctx = folly::SSLContext::getFromSSLCtx(SSL_get_SSL_CTX(ssl));\n  DCHECK(ctx);\n\n  auto handler = ctx->getTicketHandler();\n  if (!handler) {\n    LOG(FATAL) << \"Null OpenSSLTicketHandler in callback\";\n  }\n\n  return handler->ticketCallback(ssl, keyName, iv, cipherCtx, hmacCtx, encrypt);\n}\n} // namespace\n\n//\n// For OpenSSL portability API\n\n// SSLContext implementation\nSSLContext::SSLContext(SSLVersion version) {\n  ctx_ = SSL_CTX_new(TLS_method());\n  if (ctx_ == nullptr) {\n    throw std::runtime_error(\"SSL_CTX_new: \" + getErrors());\n  }\n\n  // configure the TLS version used\n  configureProtocolVersion(ctx_, version);\n\n  SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY);\n\n  checkPeerName_ = false;\n\n  SSL_CTX_set_options(ctx_, SSL_OP_NO_COMPRESSION);\n\n  sslAcceptRunner_ = std::make_unique<SSLAcceptRunner>();\n\n  setupCtx(ctx_);\n\n  SSL_CTX_set_tlsext_servername_callback(ctx_, baseServerNameOpenSSLCallback);\n  SSL_CTX_set_tlsext_servername_arg(ctx_, this);\n}\n\nSSLContext::~SSLContext() {\n  if (ctx_ != nullptr) {\n    SSL_CTX_free(ctx_);\n    ctx_ = nullptr;\n  }\n\n  deleteNextProtocolsStrings();\n}\n\nvoid SSLContext::ciphers(const std::string& ciphers) {\n  setCiphersOrThrow(ciphers);\n}\n\nvoid SSLContext::setClientECCurvesList(\n    const std::vector<std::string>& ecCurves) {\n  if (ecCurves.empty()) {\n    return;\n  }\n  std::string ecCurvesList;\n  join(\":\", ecCurves, ecCurvesList);\n  const auto rc = SSL_CTX_set1_curves_list(ctx_, ecCurvesList.c_str());\n  if (rc == 0) {\n    throw std::runtime_error(\"SSL_CTX_set1_curves_list \" + getErrors());\n  }\n}\n\nvoid SSLContext::setSupportedGroups(const std::vector<std::string>& groups) {\n  if (groups.empty()) {\n    return;\n  }\n  std::string groupsList;\n  join(\":\", groups, groupsList);\n  const auto rc = SSL_CTX_set1_groups_list(ctx_, groupsList.c_str());\n  if (rc == 0) {\n    throw std::runtime_error(\"SSL_CTX_set1_curves \" + getErrors());\n  }\n}\n\nvoid SSLContext::setServerECCurve(const std::string& curveName) {\n  EC_KEY* ecdh = nullptr;\n  int nid;\n\n  /*\n   * Elliptic-Curve Diffie-Hellman parameters are either \"named curves\"\n   * from RFC 4492 section 5.1.1, or explicitly described curves over\n   * binary fields. OpenSSL only supports the \"named curves\", which provide\n   * maximum interoperability.\n   */\n\n  nid = OBJ_sn2nid(curveName.c_str());\n  if (nid == 0) {\n    LOG(FATAL) << \"Unknown curve name:\" << curveName.c_str();\n  }\n  ecdh = EC_KEY_new_by_curve_name(nid);\n  if (ecdh == nullptr) {\n    LOG(FATAL) << \"Unable to create curve:\" << curveName.c_str();\n  }\n\n  SSL_CTX_set_tmp_ecdh(ctx_, ecdh);\n  EC_KEY_free(ecdh);\n}\n\nSSLContext::SSLContext(SSL_CTX* ctx) : ctx_(ctx) {\n  setupCtx(ctx);\n  if (SSL_CTX_up_ref(ctx) == 0) {\n    throw std::runtime_error(\"Failed to increment SSL_CTX refcount\");\n  }\n}\n\nvoid SSLContext::setX509VerifyParam(\n    const ssl::X509VerifyParam& x509VerifyParam) {\n  if (!x509VerifyParam) {\n    return;\n  }\n  if (SSL_CTX_set1_param(ctx_, x509VerifyParam.get()) != 1) {\n    throw std::runtime_error(\"SSL_CTX_set1_param \" + getErrors());\n  }\n}\n\nvoid SSLContext::setCiphersOrThrow(const std::string& ciphers) {\n  const auto rc = SSL_CTX_set_cipher_list(ctx_, ciphers.c_str());\n  if (rc == 0) {\n    throw std::runtime_error(\"SSL_CTX_set_cipher_list: \" + getErrors());\n  }\n  providedCiphersString_ = ciphers;\n}\n\nvoid SSLContext::setSigAlgsOrThrow(const std::string& sigalgs) {\n  const auto rc = SSL_CTX_set1_sigalgs_list(ctx_, sigalgs.c_str());\n  if (rc == 0) {\n    throw std::runtime_error(\"SSL_CTX_set1_sigalgs_list \" + getErrors());\n  }\n}\n\nvoid SSLContext::setVerificationOption(\n    const SSLContext::SSLVerifyPeerEnum& verifyPeer) {\n  CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX); // dont recurse\n  verifyPeer_ = verifyPeer;\n}\n\nvoid SSLContext::setVerificationOption(\n    const SSLContext::VerifyClientCertificate& verifyClient) {\n  verifyClient_ = verifyClient;\n}\n\nvoid SSLContext::setVerificationOption(\n    const SSLContext::VerifyServerCertificate& verifyServer) {\n  verifyServer_ = verifyServer;\n}\n\nint SSLContext::getVerificationMode(\n    const SSLContext::VerifyClientCertificate& verifyClient) {\n  int mode = SSL_VERIFY_NONE;\n  switch (verifyClient) {\n    case VerifyClientCertificate::ALWAYS:\n      mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;\n      break;\n\n    case VerifyClientCertificate::IF_PRESENTED:\n      mode = SSL_VERIFY_PEER;\n      break;\n\n    case VerifyClientCertificate::DO_NOT_REQUEST:\n      mode = SSL_VERIFY_NONE;\n      break;\n  }\n  return mode;\n}\n\nint SSLContext::getVerificationMode(\n    const SSLContext::VerifyServerCertificate& verifyServer) {\n  int mode = SSL_VERIFY_NONE;\n  switch (verifyServer) {\n    case VerifyServerCertificate::IF_PRESENTED:\n      mode = SSL_VERIFY_PEER;\n      break;\n\n    case VerifyServerCertificate::IGNORE_VERIFY_RESULT:\n      mode = SSL_VERIFY_NONE;\n      break;\n  }\n  return mode;\n}\n\nint SSLContext::getVerificationMode(\n    const SSLContext::SSLVerifyPeerEnum& verifyPeer) {\n  CHECK(verifyPeer != SSLVerifyPeerEnum::USE_CTX);\n  int mode = SSL_VERIFY_NONE;\n  switch (verifyPeer) {\n      // case SSLVerifyPeerEnum::USE_CTX: // can't happen\n      // break;\n\n    case SSLVerifyPeerEnum::VERIFY:\n      mode = SSL_VERIFY_PEER;\n      break;\n\n    case SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT:\n      mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;\n      break;\n\n    case SSLVerifyPeerEnum::NO_VERIFY:\n      mode = SSL_VERIFY_NONE;\n      break;\n    case SSLVerifyPeerEnum::USE_CTX:\n    default:\n      break;\n  }\n  return mode;\n}\n\nint SSLContext::getVerificationMode() const {\n  // the below or'ing is incorrect unless VERIFY_NONE is 0\n  static_assert(SSL_VERIFY_NONE == 0);\n  return getVerificationMode(verifyClient_) |\n      getVerificationMode(verifyServer_) | getVerificationMode(verifyPeer_);\n}\n\nvoid SSLContext::authenticate(\n    bool checkPeerCert, bool checkPeerName, const std::string& peerName) {\n  int mode;\n  if (checkPeerCert) {\n    mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |\n        SSL_VERIFY_CLIENT_ONCE;\n    checkPeerName_ = checkPeerName;\n    peerFixedName_ = peerName;\n  } else {\n    mode = SSL_VERIFY_NONE;\n    checkPeerName_ = false; // can't check name without cert!\n    peerFixedName_.clear();\n  }\n  SSL_CTX_set_verify(ctx_, mode, nullptr);\n}\n\nvoid SSLContext::loadCertificate(const char* path, const char* format) {\n  if (path == nullptr || format == nullptr) {\n    throw std::invalid_argument(\n        \"loadCertificateChain: either <path> or <format> is nullptr\");\n  }\n  if (strcmp(format, \"PEM\") == 0) {\n    if (SSL_CTX_use_certificate_chain_file(ctx_, path) != 1) {\n      int errnoCopy = errno;\n      std::string reason(\"SSL_CTX_use_certificate_chain_file: \");\n      reason.append(path);\n      reason.append(\": \");\n      reason.append(getErrors(errnoCopy));\n      throw std::runtime_error(reason);\n    }\n  } else {\n    throw std::runtime_error(\n        \"Unsupported certificate format: \" + std::string(format));\n  }\n}\n\nvoid SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) {\n  if (cert.data() == nullptr) {\n    throw std::invalid_argument(\"loadCertificate: <cert> is nullptr\");\n  }\n\n  ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));\n  if (bio == nullptr) {\n    throw std::runtime_error(\"BIO_new: \" + getErrors());\n  }\n\n  int written = BIO_write(bio.get(), cert.data(), int(cert.size()));\n  if (written <= 0 || static_cast<unsigned>(written) != cert.size()) {\n    throw std::runtime_error(\"BIO_write: \" + getErrors());\n  }\n\n  ssl::X509UniquePtr x509(\n      PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));\n  if (x509 == nullptr) {\n    throw std::runtime_error(\"PEM_read_bio_X509: \" + getErrors());\n  }\n\n  if (SSL_CTX_use_certificate(ctx_, x509.get()) == 0) {\n    throw std::runtime_error(\"SSL_CTX_use_certificate: \" + getErrors());\n  }\n\n  // Any further X509 PEM blocks are treated as additional certificates in\n  // the certificate chain.\n  constexpr size_t kMaxCertChain = 64;\n\n  for (size_t i = 0; i < kMaxCertChain; i++) {\n    x509.reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));\n    if (x509 == nullptr) {\n      ERR_clear_error();\n      return;\n    }\n\n    if (SSL_CTX_add1_chain_cert(ctx_, x509.get()) == 0) {\n      throw std::runtime_error(\"SSL_CTX_add0_chain_cert: \" + getErrors());\n    }\n  }\n\n  throw std::runtime_error(\n      \"loadCertificateFromBufferPEM(): Too many certificates in chain\");\n}\n\nvoid SSLContext::loadPrivateKey(const char* path, const char* format) {\n  if (path == nullptr || format == nullptr) {\n    throw std::invalid_argument(\n        \"loadPrivateKey: either <path> or <format> is nullptr\");\n  }\n  if (strcmp(format, \"PEM\") == 0) {\n    if (SSL_CTX_use_PrivateKey_file(ctx_, path, SSL_FILETYPE_PEM) == 0) {\n      throw std::runtime_error(\"SSL_CTX_use_PrivateKey_file: \" + getErrors());\n    }\n  } else {\n    throw std::runtime_error(\n        \"Unsupported private key format: \" + std::string(format));\n  }\n}\n\nvoid SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) {\n  if (pkey.data() == nullptr) {\n    throw std::invalid_argument(\"loadPrivateKey: <pkey> is nullptr\");\n  }\n\n  ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));\n  if (bio == nullptr) {\n    throw std::runtime_error(\"BIO_new: \" + getErrors());\n  }\n\n  int written = BIO_write(bio.get(), pkey.data(), int(pkey.size()));\n  if (written <= 0 || static_cast<unsigned>(written) != pkey.size()) {\n    throw std::runtime_error(\"BIO_write: \" + getErrors());\n  }\n\n  ssl::EvpPkeyUniquePtr key(\n      PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));\n  if (key == nullptr) {\n    throw std::runtime_error(\"PEM_read_bio_PrivateKey: \" + getErrors());\n  }\n\n  if (SSL_CTX_use_PrivateKey(ctx_, key.get()) == 0) {\n    throw std::runtime_error(\"SSL_CTX_use_PrivateKey: \" + getErrors());\n  }\n}\n\nvoid SSLContext::loadCertKeyPairFromBufferPEM(\n    folly::StringPiece cert, folly::StringPiece pkey) {\n  loadCertificateFromBufferPEM(cert);\n  loadPrivateKeyFromBufferPEM(pkey);\n  if (!isCertKeyPairValid()) {\n    throw std::runtime_error(\"SSL certificate and private key do not match\");\n  }\n}\n\nvoid SSLContext::loadCertKeyPairFromFiles(\n    const char* certPath,\n    const char* keyPath,\n    const char* certFormat,\n    const char* keyFormat) {\n  loadCertificate(certPath, certFormat);\n  loadPrivateKey(keyPath, keyFormat);\n  if (!isCertKeyPairValid()) {\n    throw std::runtime_error(\"SSL certificate and private key do not match\");\n  }\n}\n\nvoid SSLContext::setCertChainKeyPair(\n    std::vector<ssl::X509UniquePtr>&& certChain, ssl::EvpPkeyUniquePtr&& key) {\n  if (certChain.empty()) {\n    throw std::invalid_argument(\"Empty certificate chain provided\");\n  }\n\n  constexpr size_t kMaxCertChain = 65;\n  if (kMaxCertChain < certChain.size()) {\n    throw std::invalid_argument(\"Too many certificates in chain\");\n  }\n\n  if (SSL_CTX_use_PrivateKey(ctx_, key.get()) == 0) {\n    throw std::runtime_error(\"SSL_CTX_use_PrivateKey: \" + getErrors());\n  }\n\n  auto& leafCert = certChain.front();\n\n  if (SSL_CTX_use_certificate(ctx_, leafCert.get()) == 0) {\n    throw std::runtime_error(\"SSL_CTX_use_certificate: \" + getErrors());\n  }\n\n  for (size_t i = 1; i < certChain.size(); ++i) {\n    if (SSL_CTX_add1_chain_cert(ctx_, certChain[i].get()) == 0) {\n      throw std::runtime_error(\"SSL_CTX_add0_chain_cert: \" + getErrors());\n    }\n  }\n\n  if (!isCertKeyPairValid()) {\n    throw std::runtime_error(\"SSL certificate and private key do not match\");\n  }\n}\n\nbool SSLContext::isCertKeyPairValid() const {\n  return SSL_CTX_check_private_key(ctx_) == 1;\n}\n\nvoid SSLContext::loadTrustedCertificates(const char* path) {\n  if (path == nullptr) {\n    throw std::invalid_argument(\"loadTrustedCertificates: <path> is nullptr\");\n  }\n  if (SSL_CTX_load_verify_locations(ctx_, path, nullptr) == 0) {\n    throw std::runtime_error(\"SSL_CTX_load_verify_locations: \" + getErrors());\n  }\n  ERR_clear_error();\n}\n\nvoid SSLContext::loadTrustedCertificates(X509_STORE* store) {\n  SSL_CTX_set_cert_store(ctx_, store);\n}\n\nvoid SSLContext::setSupportedClientCertificateAuthorityNames(\n    std::vector<ssl::X509NameUniquePtr> names) {\n  // SSL_CTX_set_client_CA_list *takes ownership* of a STACK_OF(X509_NAME)\n  // where each pointer in the STACK is an *owned* X509.\n  ssl::OwningStackOfX509NameUniquePtr nameList(sk_X509_NAME_new(nullptr));\n  if (!nameList) {\n    throw std::runtime_error(\n        \"SSLContext::setSupportedClientCertificateAuthorityNames failed to allocate name list\");\n  }\n\n  // Release ownership of the X509_NAME into the stack.\n  //\n  // If exceptions are thrown, all elements are properly cleaned up because of\n  // the *UniquePtr wrappers.\n  for (auto& name : names) {\n    if (!sk_X509_NAME_push(nameList.get(), name.release())) {\n      throw std::runtime_error(\n          \"SSLContext::setSupportedClientCertificateAuthorityNames failed to add X509_NAME\");\n    }\n  }\n\n  // And finally pass ownership over the whole X509_NAME stack to OpenSSL\n  SSL_CTX_set_client_CA_list(ctx_, nameList.release());\n}\n\nvoid SSLContext::passwordCollector(\n    std::shared_ptr<ssl::PasswordCollector> collector) {\n  if (collector == nullptr) {\n    LOG(ERROR) << \"passwordCollector: ignore invalid password collector\";\n    return;\n  }\n  collector_ = collector;\n  SSL_CTX_set_default_passwd_cb(ctx_, passwordCallback);\n  SSL_CTX_set_default_passwd_cb_userdata(ctx_, this);\n}\n\nvoid SSLContext::setServerNameCallback(const ServerNameCallback& cb) {\n  serverNameCb_ = cb;\n}\n\nvoid SSLContext::addClientHelloCallback(const ClientHelloCallback& cb) {\n  clientHelloCbs_.push_back(cb);\n}\n\nint SSLContext::baseServerNameOpenSSLCallback(SSL* ssl, int* al, void* data) {\n  auto context = (SSLContext*)data;\n\n  if (context == nullptr) {\n    return SSL_TLSEXT_ERR_NOACK;\n  }\n\n  for (auto& cb : context->clientHelloCbs_) {\n    // Generic callbacks to happen after we receive the Client Hello.\n    // For example, we use one to switch which cipher we use depending\n    // on the user's TLS version.  Because the primary purpose of\n    // baseServerNameOpenSSLCallback is for SNI support, and these callbacks\n    // are side-uses, we ignore any possible failures other than just logging\n    // them.\n    cb(ssl);\n  }\n\n  if (!context->serverNameCb_) {\n    return SSL_TLSEXT_ERR_NOACK;\n  }\n\n  ServerNameCallbackResult ret = context->serverNameCb_(ssl);\n  switch (ret) {\n    case SERVER_NAME_FOUND:\n      return SSL_TLSEXT_ERR_OK;\n    case SERVER_NAME_NOT_FOUND:\n      return SSL_TLSEXT_ERR_NOACK;\n    case SERVER_NAME_NOT_FOUND_ALERT_FATAL:\n      *al = TLS1_AD_UNRECOGNIZED_NAME;\n      return SSL_TLSEXT_ERR_ALERT_FATAL;\n    default:\n      CHECK(false);\n  }\n\n  return SSL_TLSEXT_ERR_NOACK;\n}\n\nint SSLContext::alpnSelectCallback(\n    SSL* /* ssl */,\n    const unsigned char** out,\n    unsigned char* outlen,\n    const unsigned char* in,\n    unsigned int inlen,\n    void* data) {\n  auto context = (SSLContext*)data;\n  CHECK(context);\n  if (context->advertisedNextProtocols_.empty()) {\n    *out = nullptr;\n    *outlen = 0;\n  } else {\n    auto i = context->pickNextProtocols();\n    const auto& item = context->advertisedNextProtocols_[i];\n    if (SSL_select_next_proto(\n            (unsigned char**)out,\n            outlen,\n            item.protocols,\n            item.length,\n            in,\n            inlen) != OPENSSL_NPN_NEGOTIATED) {\n      if (!context->getAlpnAllowMismatch()) {\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n      } else {\n        return SSL_TLSEXT_ERR_NOACK;\n      }\n    }\n  }\n  return SSL_TLSEXT_ERR_OK;\n}\n\nstd::string SSLContext::getAdvertisedNextProtocols() const {\n  if (advertisedNextProtocols_.empty()) {\n    return \"\";\n  }\n  std::string alpns(\n      (const char*)advertisedNextProtocols_[0].protocols + 1,\n      advertisedNextProtocols_[0].length - 1);\n  auto len = advertisedNextProtocols_[0].protocols[0];\n  for (size_t i = len; i < alpns.length();) {\n    len = alpns[i];\n    alpns[i] = ',';\n    i += len + 1;\n  }\n  return alpns;\n}\n\nbool SSLContext::setAdvertisedNextProtocols(\n    const std::list<std::string>& protocols) {\n  return setRandomizedAdvertisedNextProtocols({{1, protocols}});\n}\n\nbool SSLContext::setRandomizedAdvertisedNextProtocols(\n    const std::list<NextProtocolsItem>& items) {\n  unsetNextProtocols();\n  if (items.empty()) {\n    return false;\n  }\n  int total_weight = 0;\n  for (const auto& item : items) {\n    if (item.protocols.empty()) {\n      continue;\n    }\n    AdvertisedNextProtocolsItem advertised_item;\n    advertised_item.length = 0;\n    for (const auto& proto : item.protocols) {\n      ++advertised_item.length;\n      auto protoLength = proto.length();\n      if (protoLength >= 256) {\n        deleteNextProtocolsStrings();\n        return false;\n      }\n      advertised_item.length += unsigned(protoLength);\n    }\n    advertised_item.protocols = new unsigned char[advertised_item.length];\n    if (!advertised_item.protocols) {\n      throw std::runtime_error(\"alloc failure\");\n    }\n    unsigned char* dst = advertised_item.protocols;\n    for (auto& proto : item.protocols) {\n      auto protoLength = uint8_t(proto.length());\n      *dst++ = (unsigned char)protoLength;\n      memcpy(dst, proto.data(), protoLength);\n      dst += protoLength;\n    }\n    total_weight += item.weight;\n    advertisedNextProtocols_.push_back(advertised_item);\n    advertisedNextProtocolWeights_.push_back(item.weight);\n  }\n  if (total_weight == 0) {\n    deleteNextProtocolsStrings();\n    return false;\n  }\n  nextProtocolDistribution_ = std::discrete_distribution<>(\n      advertisedNextProtocolWeights_.begin(),\n      advertisedNextProtocolWeights_.end());\n  SSL_CTX_set_alpn_select_cb(ctx_, alpnSelectCallback, this);\n  // Client cannot really use randomized alpn\n  // Note that this function reverses the typical return value convention\n  // of openssl and returns 0 on success.\n  return SSL_CTX_set_alpn_protos(\n             ctx_,\n             advertisedNextProtocols_[0].protocols,\n             advertisedNextProtocols_[0].length) == 0;\n}\n\nvoid SSLContext::deleteNextProtocolsStrings() {\n  for (auto protocols : advertisedNextProtocols_) {\n    delete[] protocols.protocols;\n  }\n  advertisedNextProtocols_.clear();\n  advertisedNextProtocolWeights_.clear();\n}\n\nvoid SSLContext::unsetNextProtocols() {\n  deleteNextProtocolsStrings();\n  SSL_CTX_set_alpn_select_cb(ctx_, nullptr, nullptr);\n  SSL_CTX_set_alpn_protos(ctx_, nullptr, 0);\n  // clear the error stack here since openssl internals sometimes add a\n  // malloc failure when doing a memdup of NULL, 0..\n  ERR_clear_error();\n}\n\nsize_t SSLContext::pickNextProtocols() {\n  CHECK(!advertisedNextProtocols_.empty()) << \"Failed to pickNextProtocols\";\n  auto rng = ThreadLocalPRNG();\n  return size_t(nextProtocolDistribution_(rng));\n}\n\nSSL* SSLContext::createSSL() const {\n  SSL* ssl = SSL_new(ctx_);\n  if (ssl == nullptr) {\n    throw std::runtime_error(\"SSL_new: \" + getErrors());\n  }\n  return ssl;\n}\n\nvoid SSLContext::setSessionCacheContext(const std::string& context) {\n  SSL_CTX_set_session_id_context(\n      ctx_,\n      reinterpret_cast<const unsigned char*>(context.data()),\n      std::min<unsigned int>(\n          static_cast<unsigned int>(context.length()), SSL_MAX_SID_CTX_LENGTH));\n}\n\n/**\n * Match a name with a pattern. The pattern may include wildcard. A single\n * wildcard \"*\" can match up to one component in the domain name.\n *\n * @param  host    Host name, typically the name of the remote host\n * @param  pattern Name retrieved from certificate\n * @param  size    Size of \"pattern\"\n * @return True, if \"host\" matches \"pattern\". False otherwise.\n */\nbool SSLContext::matchName(const char* host, const char* pattern, int size) {\n  bool match = false;\n  int i = 0, j = 0;\n  while (i < size && host[j] != '\\0') {\n    if (toupper(pattern[i]) == toupper(host[j])) {\n      i++;\n      j++;\n      continue;\n    }\n    if (pattern[i] == '*') {\n      while (host[j] != '.' && host[j] != '\\0') {\n        j++;\n      }\n      i++;\n      continue;\n    }\n    break;\n  }\n  if (i == size && host[j] == '\\0') {\n    match = true;\n  }\n  return match;\n}\n\nint SSLContext::passwordCallback(char* password, int size, int, void* data) {\n  auto context = (SSLContext*)data;\n  if (context == nullptr || context->passwordCollector() == nullptr) {\n    return 0;\n  }\n  std::string userPassword;\n  // call user defined password collector to get password\n  context->passwordCollector()->getPassword(userPassword, size);\n  auto const length = std::min(userPassword.size(), size_t(size));\n  std::memcpy(password, userPassword.data(), length);\n  return int(length);\n}\n\n#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH)\nvoid SSLContext::enableFalseStart() {\n  SSL_CTX_set_mode(ctx_, SSL_MODE_HANDSHAKE_CUTTHROUGH);\n}\n#endif\n\nvoid SSLContext::setOptions(long options) {\n  long newOpt = SSL_CTX_set_options(ctx_, options);\n  if ((newOpt & options) != options) {\n    throw std::runtime_error(\"SSL_CTX_set_options failed\");\n  }\n}\n\nstd::string SSLContext::getErrors(int errnoCopy) {\n  std::string errors;\n  unsigned long errorCode;\n  char message[256];\n\n  errors.reserve(512);\n  while ((errorCode = ERR_get_error()) != 0) {\n    if (!errors.empty()) {\n      errors += \"; \";\n    }\n    const char* reason = ERR_reason_error_string(errorCode);\n    if (reason == nullptr) {\n      snprintf(message, sizeof(message) - 1, \"SSL error # %08lX\", errorCode);\n      reason = message;\n    }\n    errors += reason;\n  }\n  if (errors.empty()) {\n    errors = \"error code: \" + folly::to<std::string>(errnoCopy);\n  }\n  return errors;\n}\n\nvoid SSLContext::disableTLS13() {\n  SSL_CTX_set_max_proto_version(ctx_, TLS1_2_VERSION);\n}\n\nvoid SSLContext::setupCtx(SSL_CTX* ctx) {\n  // 1) folly::AsyncSSLSocket wants to unconditionally store a client\n  // session, so that is possible to later perform TLS resumption.\n  // For that, we need SSL_SESS_CACHE_CLIENT.\n  //\n  // 2) wangle::SSLSessionCacheManager needs to be able to receive\n  // SSL_SESSIONs that are established through a successful\n  // connection. For that, we need SSL_SESS_CACHE_SERVER. Consequently,\n  // given the requirements of (1), we opt to use SSL_SESS_CACHE_BOTH\n  //\n  // 3) We explicitly disable the OpenSSL internal session cache, as there\n  // is very little we can do to control the memory usage of the internal\n  // session cache. Server side session-id based caching should be explicitly\n  // opted-in by the user, by forcing them to provide an implementation of\n  // a SessionCache interface (e.g. wangle::SSLSessionCacheManager); i.e.,\n  // the user must be cognizant of the fact that doing so would result in\n  // increased memory usage.\n  SSL_CTX_set_session_cache_mode(\n      ctx,\n      SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL |\n          SSL_SESS_CACHE_NO_AUTO_CLEAR);\n\n  SSL_CTX_set_ex_data(ctx, getExDataIndex(), this);\n  SSL_CTX_sess_set_new_cb(ctx, SSLContext::newSessionCallback);\n}\n\nSSLContext* SSLContext::getFromSSLCtx(const SSL_CTX* ctx) {\n  return static_cast<SSLContext*>(SSL_CTX_get_ex_data(ctx, getExDataIndex()));\n}\n\nint SSLContext::newSessionCallback(SSL* ssl, SSL_SESSION* session) {\n  SSL_CTX* ctx = SSL_get_SSL_CTX(ssl);\n  SSLContext* context = getFromSSLCtx(ctx);\n\n  auto& cb = context->sessionLifecycleCallbacks_;\n  if (cb != nullptr && cb) {\n    SSL_SESSION_up_ref(session);\n    auto sessionPtr = folly::ssl::SSLSessionUniquePtr(session);\n    cb->onNewSession(ssl, std::move(sessionPtr));\n  }\n\n  // Session will either be moved to session manager or\n  // freed when the unique_ptr goes out of scope\n  auto sessionPtr = folly::ssl::SSLSessionUniquePtr(session);\n  auto sessionManager = folly::ssl::SSLSessionManager::getFromSSL(ssl);\n  if (sessionManager) {\n    sessionManager->onNewSession(std::move(sessionPtr));\n  }\n\n  return 1;\n}\n\nvoid SSLContext::setSessionLifecycleCallbacks(\n    std::unique_ptr<SessionLifecycleCallbacks> cb) {\n  sessionLifecycleCallbacks_ = std::move(cb);\n}\n\nvoid SSLContext::setCiphersuitesOrThrow(const std::string& ciphersuites) {\n  auto rc = SSL_CTX_set_ciphersuites(ctx_, ciphersuites.c_str());\n  if (rc == 0) {\n    throw std::runtime_error(\"SSL_CTX_set_ciphersuites: \" + getErrors());\n  }\n}\n\nvoid SSLContext::setAllowNoDheKex(bool flag) {\n  auto opt = SSL_OP_ALLOW_NO_DHE_KEX;\n  if (flag) {\n    SSL_CTX_set_options(ctx_, opt);\n  } else {\n    SSL_CTX_clear_options(ctx_, opt);\n  }\n}\n\nvoid SSLContext::setTicketHandler(\n    std::unique_ptr<OpenSSLTicketHandler> handler) {\n  ticketHandler_ = std::move(handler);\n  SSL_CTX_set_tlsext_ticket_key_cb(ctx_, dispatchTicketCrypto);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/SSLContext.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <list>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <random>\n#include <string>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/Function.h>\n#include <folly/Portability.h>\n#include <folly/Range.h>\n#include <folly/String.h>\n#include <folly/container/Access.h>\n#include <folly/io/async/ssl/OpenSSLUtils.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/ssl/OpenSSLPtrTypes.h>\n\nnamespace folly {\n\nclass OpenSSLTicketHandler;\nnamespace ssl {\nclass PasswordCollector;\n}\n\n/**\n * Run SSL_accept via a runner\n */\nclass SSLAcceptRunner {\n public:\n  virtual ~SSLAcceptRunner() = default;\n\n  /**\n   * This is expected to run the first function and provide its return\n   * value to the second function. This can be used to run the SSL_accept\n   * in different contexts.\n   */\n  virtual void run(\n      Function<int()> acceptFunc, Function<void(int)> finallyFunc) const {\n    finallyFunc(acceptFunc());\n  }\n};\n\n/**\n * Wrap OpenSSL SSL_CTX into a class.\n */\nclass SSLContext {\n public:\n  struct SessionLifecycleCallbacks {\n    virtual void onNewSession(SSL*, folly::ssl::SSLSessionUniquePtr) = 0;\n    virtual ~SessionLifecycleCallbacks() = default;\n  };\n\n  enum SSLVersion {\n    SSLv2,\n    SSLv3,\n    TLSv1, // support TLS 1.0+\n    TLSv1_2, // support for only TLS 1.2+\n    TLSv1_3,\n  };\n\n  /**\n   * Defines the way that peers are verified.\n   * TODO: remove this in favor of the specific client and server enums\n   **/\n  enum SSLVerifyPeerEnum {\n    // Used by AsyncSSLSocket to delegate to the SSLContext's setting\n    USE_CTX,\n    // For server side - request a client certificate and verify the\n    // certificate if it is sent.  Does not fail if the client does not present\n    // a certificate.\n    // For client side - validates the server certificate or fails.\n    VERIFY,\n    // For server side - same as VERIFY but will fail if no certificate\n    // is sent.\n    // For client side - same as VERIFY.\n    VERIFY_REQ_CLIENT_CERT,\n    // No verification is done for both server and client side.\n    NO_VERIFY\n  };\n\n  enum class VerifyClientCertificate {\n    // Request a cert and verify it. Fail if verification fails or no\n    // cert is presented\n    ALWAYS,\n    // Request a cert from the peer and verify if one is presented.\n    // Will fail if verification fails.\n    // Do not fail if no cert is presented.\n    IF_PRESENTED,\n    // No verification is done and no cert is requested.\n    DO_NOT_REQUEST\n  };\n\n  enum class VerifyServerCertificate {\n    // Server cert will be presented unless anon cipher,\n    // Verficiation WILL happen and a failure will result in termination\n    IF_PRESENTED,\n    // Server cert will be presented unless anon cipher,\n    // Verification WILL happen but the result will be ignored\n    IGNORE_VERIFY_RESULT\n  };\n\n  struct NextProtocolsItem {\n    NextProtocolsItem(int wt, const std::list<std::string>& ptcls)\n        : weight(wt), protocols(ptcls) {}\n    int weight;\n    std::list<std::string> protocols;\n  };\n\n  // Function that selects a client protocol given the server's list\n  using ClientProtocolFilterCallback = bool (*)(\n      unsigned char**, unsigned int*, const unsigned char*, unsigned int);\n\n  /**\n   * Convenience function to call getErrors() with the current errno value.\n   *\n   * Make sure that you only call this when there was no intervening operation\n   * since the last OpenSSL error that may have changed the current errno value.\n   */\n  static std::string getErrors() { return getErrors(errno); }\n\n  /**\n   * Constructor.\n   *\n   * @param version The lowest or oldest SSL version to support.\n   */\n  explicit SSLContext(SSLVersion version = TLSv1_2);\n  /**\n   * Constructor that helps ease migrations by directly wrapping a provided\n   * SSL_CTX*\n   */\n  explicit SSLContext(SSL_CTX* ctx);\n  virtual ~SSLContext();\n\n  /**\n   * Set default TLS 1.2 and below ciphers to be used in SSL handshake process.\n   *\n   * @param ciphers A list of ciphers to use for TLSv1.0\n   */\n  virtual void ciphers(const std::string& ciphers);\n\n  /**\n   * Low-level method that attempts to set the provided TLS 1.2\n   * and below ciphers on the SSL_CTX object,\n   * and throws if something goes wrong.\n   */\n  virtual void setCiphersOrThrow(const std::string& ciphers);\n\n  /**\n   * Set default TLS 1.2 and below ciphers to be used in SSL handshake process.\n   */\n  template <typename Iterator>\n  void setCipherList(Iterator ibegin, Iterator iend) {\n    if (ibegin != iend) {\n      std::string opensslCipherList;\n      folly::join(\":\", ibegin, iend, opensslCipherList);\n      setCiphersOrThrow(opensslCipherList);\n    }\n  }\n\n  template <typename Container>\n  void setCipherList(const Container& cipherList) {\n    setCipherList(\n        folly::access::begin(cipherList), folly::access::end(cipherList));\n  }\n\n  template <typename Value>\n  void setCipherList(const std::initializer_list<Value>& cipherList) {\n    setCipherList(cipherList.begin(), cipherList.end());\n  }\n\n  /**\n   * Low-level method that attempts to set the provided signature\n   * algorithms on the SSL_CTX object for TLS1.2+,\n   * and throws if something goes wrong.\n   */\n  virtual void setSigAlgsOrThrow(const std::string& sigAlgs);\n\n  template <typename Iterator>\n  void setSignatureAlgorithms(Iterator ibegin, Iterator iend) {\n    if (ibegin != iend) {\n      std::string opensslSigAlgsList;\n      join(\":\", ibegin, iend, opensslSigAlgsList);\n      setSigAlgsOrThrow(opensslSigAlgsList);\n    }\n  }\n\n  template <typename Container>\n  void setSignatureAlgorithms(const Container& sigalgs) {\n    setSignatureAlgorithms(\n        folly::access::begin(sigalgs), folly::access::end(sigalgs));\n  }\n\n  template <typename Value>\n  void setSignatureAlgorithms(const std::initializer_list<Value>& sigalgs) {\n    setSignatureAlgorithms(sigalgs.begin(), sigalgs.end());\n  }\n\n  /**\n   * Sets the list of EC curves supported by the client.\n   *\n   * @param ecCurves A list of ec curves, eg: P-256\n   */\n  void setClientECCurvesList(const std::vector<std::string>& ecCurves);\n\n  void setSupportedGroups(const std::vector<std::string>& groups);\n\n  /**\n   * Method to add support for a specific elliptic curve encryption algorithm.\n   *\n   * @param curveName: The name of the ec curve to support, eg: prime256v1.\n   */\n  void setServerECCurve(const std::string& curveName);\n\n  /**\n   * Sets an x509 verification param on the context.\n   */\n  void setX509VerifyParam(const ssl::X509VerifyParam& x509VerifyParam);\n\n  /**\n   * Method to set verification option in the context object.\n   *\n   * @param verifyPeer SSLVerifyPeerEnum indicating the verification\n   *                       method to use.\n   */\n  virtual void setVerificationOption(const SSLVerifyPeerEnum& verifyPeer);\n  /**\n   * Method to set verification options for client and server separately.\n   * This is highly recommended as these options are much clearer and the other\n   * way will be going away soon\n   */\n  virtual void setVerificationOption(\n      const VerifyClientCertificate& verifyClient);\n  virtual void setVerificationOption(\n      const VerifyServerCertificate& verifyServer);\n\n  /**\n   * Method to check if peer verfication is set.\n   *\n   * @return true if peer verification is required.\n   *\n   */\n  virtual bool needsPeerVerification() const {\n    /* TODO this is ugly and i can't think of a reason this should exist\n     * will think of what i want to do with this later\n     */\n    return getVerificationMode() != SSL_VERIFY_NONE;\n  }\n\n  /**\n   * Method to fetch Verification mode for a SSLVerifyPeerEnum.\n   * verifyPeer cannot be SSLVerifyPeerEnum::USE_CTX since there is no\n   * context.\n   *\n   * @param verifyPeer SSLVerifyPeerEnum for which the flags need to\n   *                  to be returned\n   *\n   * @return mode flags that can be used with SSL_set_verify\n   */\n  static int getVerificationMode(const SSLVerifyPeerEnum& verifyPeer);\n  static int getVerificationMode(const VerifyClientCertificate& verifyClient);\n  static int getVerificationMode(const VerifyServerCertificate& verifyServer);\n\n  /**\n   * Method to fetch Verification mode determined by the options\n   * set using setVerificationOption.\n   *\n   * @return mode flags that can be used with SSL_set_verify\n   */\n  virtual int getVerificationMode() const;\n\n  /**\n   * Enable/Disable authentication. Peer name validation can only be done\n   * if checkPeerCert is true.\n   *\n   * @param checkPeerCert If true, require peer to present valid certificate\n   * @param checkPeerName If true, validate that the certificate common name\n   *                      or alternate name(s) of peer matches the hostname\n   *                      used to connect.\n   * @param peerName      If non-empty, validate that the certificate common\n   *                      name of peer matches the given string (altername\n   *                      name(s) are not used in this case).\n   */\n  virtual void authenticate(\n      bool checkPeerCert,\n      bool checkPeerName,\n      const std::string& peerName = std::string());\n  /**\n   * Loads a certificate chain stored on disk to be sent to the peer during\n   * TLS connection establishment.\n   *\n   * @param path   Path to the certificate file\n   * @param format Certificate file format\n   */\n  virtual void loadCertificate(const char* path, const char* format = \"PEM\");\n  /**\n   * Loads a PEM formatted certificate chain from memory to be sent to the peer\n   * during TLS connection establishment.\n   *\n   * @param cert  A PEM formatted certificate\n   */\n  virtual void loadCertificateFromBufferPEM(folly::StringPiece cert);\n\n  /**\n   * Load private key.\n   *\n   * @param path   Path to the private key file\n   * @param format Private key file format\n   */\n  virtual void loadPrivateKey(const char* path, const char* format = \"PEM\");\n  /**\n   * Load private key from memory.\n   *\n   * @param pkey  A PEM formatted key\n   */\n  virtual void loadPrivateKeyFromBufferPEM(folly::StringPiece pkey);\n\n  /**\n   * Load cert and key from PEM buffers. Guaranteed to throw if cert and\n   * private key mismatch so no need to call isCertKeyPairValid.\n   *\n   * @param cert A PEM formatted certificate\n   * @param pkey A PEM formatted key\n   */\n  virtual void loadCertKeyPairFromBufferPEM(\n      folly::StringPiece cert, folly::StringPiece pkey);\n\n  /**\n   * Sets cert chain and key. Guaranteed to throw if cert and private key\n   * mismatch.\n   *\n   * @param certChain A vector of X509 certificates.\n   * @param key A private key.\n   */\n  virtual void setCertChainKeyPair(\n      std::vector<ssl::X509UniquePtr>&& certChain, ssl::EvpPkeyUniquePtr&& key);\n\n  /**\n   * Load cert and key from files. Guaranteed to throw if cert and key mismatch.\n   * Equivalent to calling loadCertificate() and loadPrivateKey().\n   *\n   * @param certPath   Path to the certificate file\n   * @param keyPath   Path to the private key file\n   * @param certFormat Certificate file format\n   * @param keyFormat Private key file format\n   */\n  virtual void loadCertKeyPairFromFiles(\n      const char* certPath,\n      const char* keyPath,\n      const char* certFormat = \"PEM\",\n      const char* keyFormat = \"PEM\");\n\n  /**\n   * Call after both cert and key are loaded to check if cert matches key.\n   * Must call if private key is loaded before loading the cert.\n   * No need to call if cert is loaded first before private key.\n   * @return true if matches, or false if mismatch.\n   */\n  virtual bool isCertKeyPairValid() const;\n\n  /**\n   * Load trusted certificates from specified file.\n   *\n   * @param path Path to trusted certificate file\n   */\n  virtual void loadTrustedCertificates(const char* path);\n  /**\n   * Load trusted certificates from a vector of file paths\n   *\n   * @param paths container of file paths to trusted certificate files\n   */\n  template <typename StringList>\n  void loadTrustedCertificates(const StringList& paths) {\n    for (const auto& path : paths) {\n      loadTrustedCertificates(path.c_str());\n    }\n  }\n  /**\n   * Load trusted certificates from specified X509 certificate store.\n   *\n   * @param store X509 certificate store.\n   */\n  virtual void loadTrustedCertificates(X509_STORE* store);\n\n  /**\n   * setSupportedClientCertificateAuthorityNames sets the list of acceptable\n   * client certificate authoritites that will be sent to the client.\n   *\n   * This corresponds to the `certificate_authorities` extension.\n   *\n   * This function does *not* alter the way client authentication is performed\n   * in any discernible manner.\n   *\n   * Certain TLS client implementations will use this list of names to aid in\n   * the client certificate selection process.\n   *\n   * folly::AsyncSSLSocket, which is based on OpenSSL, in particular will\n   * *not* use this information. folly::AsyncSSLSocket will send client\n   * certificates to whatever `SSLContext::loadCertificate` points to,\n   * regardless of what the server sends in `certificate_authorities`.\n   *\n   * @param names  A vector of X509_NAMEs to send. This typically corresponds\n   *               to the Subject of each client certificate authority used\n   *               in the trust store.\n   *               `OpenSSLUtil::loadNamesFromFile`.\n   * @throws std::exception\n   */\n  void setSupportedClientCertificateAuthorityNames(\n      std::vector<ssl::X509NameUniquePtr> names);\n\n  /**\n   * setSupportedClientCertificateAuthorityNamesFromFile sets the list of\n   * acceptable client certificate authorities that will be sent to the client.\n   *\n   * See `SSLContext::setSupportedClientCertificateAuthorityNames`.\n   *\n   * @param path   Path to a file containing PEM encoded X509 certificates.\n   * @throws std::exception\n   */\n  void setSupportedClientCertificateAuthorityNamesFromFile(const char* path) {\n    return setSupportedClientCertificateAuthorityNames(\n        ssl::OpenSSLUtils::subjectNamesInPEMFile(path));\n  }\n\n  /*\n   * Override default OpenSSL password collector.\n   *\n   * @param collector Instance of user defined password collector\n   */\n  virtual void passwordCollector(\n      std::shared_ptr<ssl::PasswordCollector> collector);\n  /**\n   * Obtain password collector.\n   *\n   * @return User defined password collector\n   */\n  virtual std::shared_ptr<ssl::PasswordCollector> passwordCollector() {\n    return collector_;\n  }\n\n  /**\n   * Provide SNI support\n   */\n  enum ServerNameCallbackResult {\n    SERVER_NAME_FOUND,\n    SERVER_NAME_NOT_FOUND,\n    SERVER_NAME_NOT_FOUND_ALERT_FATAL,\n  };\n  /**\n   * Callback function from openssl to give the application a\n   * chance to check the tlsext_hostname just right after parsing\n   * the Client Hello or Server Hello message.\n   *\n   * It is for the server to switch the SSL to another SSL_CTX\n   * to continue the handshake. (i.e. Server Name Indication, SNI, in RFC6066).\n   *\n   * If the ServerNameCallback returns:\n   * SERVER_NAME_FOUND:\n   *    server: Send a tlsext_hostname in the Server Hello\n   *    client: No-effect\n   * SERVER_NAME_NOT_FOUND:\n   *    server: Does not send a tlsext_hostname in Server Hello\n   *            and continue the handshake.\n   *    client: No-effect\n   * SERVER_NAME_NOT_FOUND_ALERT_FATAL:\n   *    server and client: Send fatal TLS1_AD_UNRECOGNIZED_NAME alert to\n   *                       the peer.\n   *\n   * Quote from RFC 6066:\n   * \"...\n   * If the server understood the ClientHello extension but\n   * does not recognize the server name, the server SHOULD take one of two\n   * actions: either abort the handshake by sending a fatal-level\n   * unrecognized_name(112) alert or continue the handshake.  It is NOT\n   * RECOMMENDED to send a warning-level unrecognized_name(112) alert,\n   * because the client's behavior in response to warning-level alerts is\n   * unpredictable.\n   * ...\"\n   */\n\n  /**\n   * Set the ServerNameCallback\n   */\n  typedef std::function<ServerNameCallbackResult(SSL* ssl)> ServerNameCallback;\n  virtual void setServerNameCallback(const ServerNameCallback& cb);\n\n  /**\n   * Generic callbacks that are run after we get the Client Hello (right\n   * before we run the ServerNameCallback)\n   */\n  typedef std::function<void(SSL* ssl)> ClientHelloCallback;\n  virtual void addClientHelloCallback(const ClientHelloCallback& cb);\n\n  /**\n   * Create an SSL object from this context.\n   */\n  SSL* createSSL() const;\n\n  /**\n   * Sets the namespace to use for sessions created from this context.\n   */\n  void setSessionCacheContext(const std::string& context);\n\n  /**\n   * Set the options on the SSL_CTX object.\n   */\n  void setOptions(long options);\n\n  std::string getAdvertisedNextProtocols() const;\n\n  /**\n   * Set the list of protocols that this SSL context supports. In client\n   * mode, this is the list of protocols that will be advertised for Application\n   * Layer Protocol Negotiation (ALPN). In server mode, the first protocol\n   * advertised by the client that is also on this list is chosen.\n   * Invoking this function with a list of length zero causes ALPN to be\n   * disabled.\n   *\n   * @param protocols   List of protocol names. This method makes a copy,\n   *                    so the caller needn't keep the list in scope after\n   *                    the call completes. The list must have at least\n   *                    one element to enable ALPN. Each element must have\n   *                    a string length < 256.\n   * @return true if ALPN has been activated. False if ALPN is disabled.\n   */\n  bool setAdvertisedNextProtocols(const std::list<std::string>& protocols);\n  /**\n   * Set weighted list of lists of protocols that this SSL context supports.\n   * In server mode, each element of the list contains a list of protocols that\n   * could be advertised for Application Layer Protocol Negotiation (ALPN).\n   * The list of protocols that will be advertised to a client is selected\n   * randomly, based on weights of elements. Client mode doesn't support\n   * randomized ALPN, so this list should contain only 1 element. The first\n   * protocol advertised by the client that is also on the list of protocols\n   * of this element is chosen. Invoking this function with a list of length\n   * zero causes ALPN to be disabled.\n   *\n   * @param items  List of NextProtocolsItems, Each item contains a list of\n   *               protocol names and weight. After the call of this fucntion\n   *               each non-empty list of protocols will be advertised with\n   *               probability weight/sum_of_weights. This method makes a copy,\n   *               so the caller needn't keep the list in scope after the call\n   *               completes. The list must have at least one element with\n   *               non-zero weight and non-empty protocols list to enable NPN.\n   *               Each name of the protocol must have a string length < 256.\n   * @return true if ALPN has been activated. False if ALPN is disabled.\n   */\n  bool setRandomizedAdvertisedNextProtocols(\n      const std::list<NextProtocolsItem>& items);\n\n  /**\n   * Disables ALPN on this SSL context.\n   */\n  void unsetNextProtocols();\n  void deleteNextProtocolsStrings();\n\n  bool getAlpnAllowMismatch() const { return alpnAllowMismatch_; }\n\n  void setAlpnAllowMismatch(bool allowMismatch) {\n    alpnAllowMismatch_ = allowMismatch;\n  }\n\n  /**\n   * Gets the underlying SSL_CTX for advanced usage\n   */\n  SSL_CTX* getSSLCtx() const { return ctx_; }\n\n  /**\n   * Examine OpenSSL's error stack, and return a string description of the\n   * errors.\n   *\n   * This operation removes the errors from OpenSSL's error stack.\n   */\n  static std::string getErrors(int errnoCopy);\n\n  bool checkPeerName() const { return checkPeerName_; }\n  std::string peerFixedName() const { return peerFixedName_; }\n\n#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH)\n  /**\n   * Enable TLS false start, saving a roundtrip for full handshakes. Will only\n   * be used if the server uses NPN or ALPN, and a strong forward-secure cipher\n   * is negotiated.\n   */\n  void enableFalseStart();\n#endif\n\n  /**\n   * Sets the runner used for SSL_accept. If none is given, the accept will be\n   * done directly.\n   */\n  void sslAcceptRunner(std::unique_ptr<SSLAcceptRunner> runner) {\n    if (nullptr == runner) {\n      LOG(ERROR) << \"Ignore invalid runner\";\n      return;\n    }\n    sslAcceptRunner_ = std::move(runner);\n  }\n\n  const SSLAcceptRunner* sslAcceptRunner() const {\n    return sslAcceptRunner_.get();\n  }\n\n  void setTicketHandler(std::unique_ptr<OpenSSLTicketHandler> handler);\n\n  OpenSSLTicketHandler* getTicketHandler() const {\n    return ticketHandler_.get();\n  }\n\n  /**\n   * Helper to match a hostname versus a pattern.\n   */\n  static bool matchName(const char* host, const char* pattern, int size);\n\n  /**\n   * Disable TLS 1.3 in OpenSSL versions that support it.\n   */\n  void disableTLS13();\n\n  /**\n   * Get SSLContext from the ex data of a SSL_CTX.\n   */\n  static SSLContext* getFromSSLCtx(const SSL_CTX* ctx);\n\n  void setSessionLifecycleCallbacks(\n      std::unique_ptr<SessionLifecycleCallbacks> cb);\n\n  /**\n   * Set the TLS 1.3 ciphersuites to be used in the SSL handshake, in\n   * order of preference.\n   * Throws if unsuccessful.\n   */\n  void setCiphersuitesOrThrow(const std::string& ciphersuites);\n\n  /**\n   * Enables/disables non-DHE (Ephemeral Diffie-Hellman) PSK key\n   * exchange for TLS 1.3 resumption. Note that this key exchange\n   * mode gives up forward secrecy on the resumed session.\n   */\n  void setAllowNoDheKex(bool flag);\n\n protected:\n  SSL_CTX* ctx_;\n\n private:\n  // TODO deprecate this, it's confusing and the default is bad\n  SSLVerifyPeerEnum verifyPeer_{SSLVerifyPeerEnum::NO_VERIFY};\n\n  /* Set one of these values depending on whether you will use the context\n   * for a server or client.*/\n  VerifyClientCertificate verifyClient_{\n      VerifyClientCertificate::DO_NOT_REQUEST};\n  VerifyServerCertificate verifyServer_{\n      VerifyServerCertificate::IGNORE_VERIFY_RESULT};\n\n  bool checkPeerName_;\n  std::string peerFixedName_;\n  std::shared_ptr<ssl::PasswordCollector> collector_;\n  ServerNameCallback serverNameCb_;\n  std::vector<ClientHelloCallback> clientHelloCbs_;\n\n  ClientProtocolFilterCallback clientProtoFilter_{nullptr};\n\n  static bool initialized_;\n\n  std::unique_ptr<SSLAcceptRunner> sslAcceptRunner_;\n  std::unique_ptr<OpenSSLTicketHandler> ticketHandler_;\n\n  struct AdvertisedNextProtocolsItem {\n    unsigned char* protocols;\n    unsigned length;\n  };\n\n  /**\n   * Wire-format list of advertised protocols for use in NPN.\n   */\n  std::vector<AdvertisedNextProtocolsItem> advertisedNextProtocols_;\n  std::vector<int> advertisedNextProtocolWeights_;\n  std::discrete_distribution<int> nextProtocolDistribution_;\n\n  static int advertisedNextProtocolCallback(\n      SSL* ssl, const unsigned char** out, unsigned int* outlen, void* data);\n\n  static int alpnSelectCallback(\n      SSL* ssl,\n      const unsigned char** out,\n      unsigned char* outlen,\n      const unsigned char* in,\n      unsigned int inlen,\n      void* data);\n\n  size_t pickNextProtocols();\n\n  bool alpnAllowMismatch_{true};\n\n  static int passwordCallback(char* password, int size, int, void* data);\n\n  /**\n   * The function that will be called directly from openssl\n   * in order for the application to get the tlsext_hostname just after\n   * parsing the Client Hello or Server Hello message. It will then call\n   * the serverNameCb_ function object. Hence, it is sort of a\n   * wrapper/proxy between serverNameCb_ and openssl.\n   *\n   * The openssl's primary intention is for SNI support, but we also use it\n   * generically for performing logic after the Client Hello comes in.\n   */\n  static int baseServerNameOpenSSLCallback(\n      SSL* ssl, int* al /* alert (return value) */, void* data);\n\n  std::string providedCiphersString_;\n\n  void setupCtx(SSL_CTX* ctx);\n\n  std::unique_ptr<SessionLifecycleCallbacks> sessionLifecycleCallbacks_{\n      nullptr};\n\n  static int newSessionCallback(SSL* ssl, SSL_SESSION* session);\n};\n\ntypedef std::shared_ptr<SSLContext> SSLContextPtr;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/SSLOptions.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/SSLOptions.h>\n\n#include <glog/logging.h>\n\n#include <folly/Format.h>\n\nnamespace folly {\nnamespace ssl {\n\nnamespace ssl_options_detail {\nvoid logDfatal(std::exception const& e) {\n  LOG(DFATAL) << exceptionStr(e);\n}\n} // namespace ssl_options_detail\n\nvoid SSLCommonOptions::setClientOptions(SSLContext& ctx) {\n#ifdef SSL_MODE_HANDSHAKE_CUTTHROUGH\n  ctx.enableFalseStart();\n#endif\n\n  X509VerifyParam param(X509_VERIFY_PARAM_new());\n  X509_VERIFY_PARAM_set_flags(param.get(), X509_V_FLAG_X509_STRICT);\n  try {\n    ctx.setX509VerifyParam(param);\n  } catch (std::runtime_error const& e) {\n    LOG(DFATAL) << exceptionStr(e);\n  }\n\n  setGroups<SSLCommonOptions>(ctx);\n  setCipherSuites<SSLCommonOptions>(ctx);\n  setSignatureAlgorithms<SSLCommonOptions>(ctx);\n}\n\n} // namespace ssl\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/SSLOptions.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/container/Array.h>\n#include <folly/io/async/SSLContext.h>\n\nnamespace folly {\nnamespace ssl {\n\nnamespace ssl_options_detail {\nvoid logDfatal(std::exception const&);\n} // namespace ssl_options_detail\n\nstruct SSLOptionsCompatibility {\n  /**\n   * The group list for this options configuration.\n   **/\n  static constexpr auto groups() {\n    return folly::make_array(\"X25519\", \"P-256\", \"P-384\");\n  }\n\n  /**\n   * The cipher list recommended for this options configuration.\n   */\n  static constexpr auto ciphers() {\n    return folly::make_array(\n        \"ECDHE-ECDSA-AES128-GCM-SHA256\",\n        \"ECDHE-RSA-AES128-GCM-SHA256\",\n        \"ECDHE-ECDSA-AES256-GCM-SHA384\",\n        \"ECDHE-RSA-AES256-GCM-SHA384\",\n        \"ECDHE-ECDSA-AES256-SHA\",\n        \"ECDHE-RSA-AES256-SHA\",\n        \"ECDHE-ECDSA-AES128-SHA\",\n        \"ECDHE-RSA-AES128-SHA\",\n        \"ECDHE-RSA-AES256-SHA384\",\n        \"AES128-GCM-SHA256\",\n        \"AES256-SHA\",\n        \"AES128-SHA\");\n  }\n\n  /**\n   * The TLS 1.3 ciphersuites recommended for this options configuration.\n   */\n  static constexpr auto ciphersuites() {\n    return folly::make_array(\n        \"TLS_AES_128_GCM_SHA256\",\n        \"TLS_AES_256_GCM_SHA384\",\n        \"TLS_CHACHA20_POLY1305_SHA256\");\n  }\n\n  /**\n   * The list of signature algorithms recommended for this options\n   * configuration.\n   */\n  static constexpr auto sigalgs() {\n    return folly::make_array(\n        \"rsa_pss_pss_sha512\",\n        \"rsa_pss_rsae_sha512\",\n        \"RSA+SHA512\",\n        \"ECDSA+SHA512\",\n        \"rsa_pss_pss_sha384\",\n        \"rsa_pss_rsae_sha384\",\n        \"RSA+SHA384\",\n        \"ECDSA+SHA384\",\n        \"rsa_pss_pss_sha256\",\n        \"rsa_pss_rsae_sha256\",\n        \"RSA+SHA256\",\n        \"ECDSA+SHA256\",\n        \"RSA+SHA1\",\n        \"ECDSA+SHA1\");\n  }\n\n  /**\n   * Set common parameters on a client SSL context, for example,\n   * ciphers, signature algorithms, verification options, and client EC curves.\n   * @param ctx The SSL Context to which to apply the options.\n   */\n  static void setClientOptions(SSLContext& ctx);\n};\n\n/**\n * SSLServerOptionsCompatibility contains algorithms that are not recommended\n * for modern servers, but are included to maintain comaptibility with\n * very old clients.\n */\nstruct SSLServerOptionsCompatibility {\n  /**\n   * The group list for this options configuration.\n   **/\n  static constexpr auto groups() {\n    return folly::make_array(\"X25519\", \"P-256\", \"P-384\");\n  }\n  /**\n   * The list of ciphers recommended for server use.\n   */\n  static constexpr auto ciphers() {\n    return folly::make_array(\n        \"ECDHE-ECDSA-AES128-GCM-SHA256\",\n        \"ECDHE-ECDSA-AES256-GCM-SHA384\",\n        \"ECDHE-ECDSA-AES128-SHA\",\n        \"ECDHE-ECDSA-AES256-SHA\",\n        \"ECDHE-RSA-AES128-GCM-SHA256\",\n        \"ECDHE-RSA-AES256-GCM-SHA384\",\n        \"ECDHE-RSA-AES128-SHA\",\n        \"ECDHE-RSA-AES256-SHA\",\n        \"AES128-GCM-SHA256\",\n        \"AES256-GCM-SHA384\",\n        \"AES128-SHA\",\n        \"AES256-SHA\");\n  }\n\n  /**\n   * The TLS 1.3 ciphersuites recommended for this options configuration.\n   */\n  static constexpr auto ciphersuites() {\n    return folly::make_array(\n        \"TLS_AES_128_GCM_SHA256\",\n        \"TLS_AES_256_GCM_SHA384\",\n        \"TLS_CHACHA20_POLY1305_SHA256\");\n  }\n};\n\n/**\n * SSLOptions2021 contains options that any new client or server from 2021\n * onwards should be using.\n *\n * It contains:\n *   * AEAD only ciphers with ephemeral key exchanges. (No support for RSA key\n *     encapsulation)\n *   * Signature algorithms that do not include insecure digests (such as SHA1)\n *\n **/\nstruct SSLOptions2021 {\n  static constexpr auto ciphers() {\n    return folly::make_array(\n        \"ECDHE-ECDSA-AES128-GCM-SHA256\",\n        \"ECDHE-RSA-AES128-GCM-SHA256\",\n        \"ECDHE-ECDSA-AES256-GCM-SHA384\",\n        \"ECDHE-RSA-AES256-GCM-SHA384\",\n        \"ECDHE-ECDSA-CHACHA20-POLY1305\",\n        \"ECDHE-RSA-CHACHA20-POLY1305\");\n  }\n\n  static constexpr auto ciphersuites() {\n    return folly::make_array(\n        \"TLS_AES_128_GCM_SHA256\",\n        \"TLS_AES_256_GCM_SHA384\",\n        \"TLS_CHACHA20_POLY1305_SHA256\");\n  }\n\n  static constexpr auto sigalgs() {\n    return folly::make_array(\n        \"rsa_pss_pss_sha512\",\n        \"rsa_pss_rsae_sha512\",\n        \"RSA+SHA512\",\n        \"ECDSA+SHA512\",\n        \"rsa_pss_pss_sha384\",\n        \"rsa_pss_rsae_sha384\",\n        \"RSA+SHA384\",\n        \"ECDSA+SHA384\",\n        \"rsa_pss_pss_sha256\",\n        \"rsa_pss_rsae_sha256\",\n        \"RSA+SHA256\",\n        \"ECDSA+SHA256\");\n  }\n};\n\nusing SSLCommonOptions = SSLOptionsCompatibility;\nusing SSLServerOptions = SSLOptions2021;\n\n/**\n * Set the cipher suite of ctx to that in TSSLOptions, and print any runtime\n * error it catches.\n * @param ctx The SSLContext to apply the desired SSL options to.\n */\ntemplate <typename TSSLOptions>\nvoid setCipherSuites(SSLContext& ctx) {\n  try {\n    std::string ciphersuites;\n    folly::join(':', TSSLOptions::ciphersuites(), ciphersuites);\n    ctx.setCiphersuitesOrThrow(std::move(ciphersuites));\n    ctx.setCipherList(TSSLOptions::ciphers());\n  } catch (std::runtime_error const& e) {\n    ssl_options_detail::logDfatal(e);\n  }\n}\n\n/**\n * Set the groups of ctx to that in TSSLOptions, and print any runtime\n * error it catches.\n * @param ctx The SSLContext to apply the desired groups to.\n */\ntemplate <typename TSSLOptions>\nvoid setGroups(SSLContext& ctx) {\n  try {\n    const auto& groups_arr = TSSLOptions::groups();\n    ctx.setSupportedGroups(\n        std::vector<std::string>(groups_arr.begin(), groups_arr.end()));\n  } catch (std::runtime_error const& e) {\n    ssl_options_detail::logDfatal(e);\n  }\n}\n\n/**\n * Set the cipher suite of ctx to the passed in  cipherList,\n * and print any runtime error it catches.\n * @param ctx The SSLContext to apply the desired SSL options to.\n * @param cipherList the list of ciphersuites to set\n */\ntemplate <typename Container>\nvoid setCipherSuites(SSLContext& ctx, const Container& cipherList) {\n  try {\n    ctx.setCipherList(cipherList);\n  } catch (std::runtime_error const& e) {\n    ssl_options_detail::logDfatal(e);\n  }\n}\n\n/**\n * Set the signature algorithm list of ctx to that in TSSLOptions, and print\n * any runtime errors it catche.\n * @param ctx The SSLContext to apply the desired SSL options to.\n */\ntemplate <typename TSSLOptions>\nvoid setSignatureAlgorithms(SSLContext& ctx) {\n  try {\n    ctx.setSignatureAlgorithms(TSSLOptions::sigalgs());\n  } catch (std::runtime_error const& e) {\n    ssl_options_detail::logDfatal(e);\n  }\n}\n\n} // namespace ssl\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/STTimerFDTimeoutManager.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventUtil.h>\n#include <folly/io/async/STTimerFDTimeoutManager.h>\n\nnamespace folly {\n// STTimerFDTimeoutManager\nSTTimerFDTimeoutManager::STTimerFDTimeoutManager(folly::EventBase* eventBase)\n    : TimerFD(eventBase), eventBase_(eventBase) {}\n\nSTTimerFDTimeoutManager::~STTimerFDTimeoutManager() {\n  cancel();\n  close();\n}\n\nvoid STTimerFDTimeoutManager::setActive(AsyncTimeout* obj, bool active) {\n  if (obj) {\n    auto* ev = obj->getEvent();\n    if (active) {\n      event_ref_flags(ev->getEvent()) |= EVLIST_ACTIVE;\n    } else {\n      event_ref_flags(ev->getEvent()) &= ~EVLIST_ACTIVE;\n    }\n  }\n}\n\nvoid STTimerFDTimeoutManager::attachTimeoutManager(\n    AsyncTimeout* /*unused*/, InternalEnum /*unused*/) {}\n\nvoid STTimerFDTimeoutManager::detachTimeoutManager(AsyncTimeout* obj) {\n  cancelTimeout(obj);\n}\n\nbool STTimerFDTimeoutManager::scheduleTimeout(\n    AsyncTimeout* obj, timeout_type timeout) {\n  timeout_type_high_res high_res_timeout(timeout);\n  return scheduleTimeoutHighRes(obj, high_res_timeout);\n}\n\nbool STTimerFDTimeoutManager::scheduleTimeoutHighRes(\n    AsyncTimeout* obj, timeout_type_high_res timeout) {\n  CHECK(obj_ == nullptr || obj_ == obj)\n      << \"Scheduling multiple timeouts on a single timeout manager is not allowed!\";\n  // no need to cancel - just reschedule\n  obj_ = obj;\n  setActive(obj, true);\n  schedule(timeout);\n\n  return true;\n}\n\nvoid STTimerFDTimeoutManager::cancelTimeout(AsyncTimeout* obj) {\n  if (obj == obj_) {\n    setActive(obj, false);\n    obj_ = nullptr;\n    cancel();\n  }\n}\n\nvoid STTimerFDTimeoutManager::bumpHandlingTime() {}\n\nvoid STTimerFDTimeoutManager::onTimeout() noexcept {\n  if (obj_) {\n    auto* obj = obj_;\n    obj_ = nullptr;\n    setActive(obj, false);\n    obj->timeoutExpired();\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/STTimerFDTimeoutManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <folly/io/async/TimeoutManager.h>\n#include <folly/io/async/TimerFD.h>\n\nnamespace folly {\n// single timeout timerfd based TimeoutManager\nclass STTimerFDTimeoutManager : public TimeoutManager, TimerFD {\n public:\n  explicit STTimerFDTimeoutManager(folly::EventBase* eventBase);\n  ~STTimerFDTimeoutManager() override;\n  STTimerFDTimeoutManager(const STTimerFDTimeoutManager&) = delete;\n  STTimerFDTimeoutManager& operator=(const STTimerFDTimeoutManager&) = delete;\n  STTimerFDTimeoutManager(STTimerFDTimeoutManager&&) = delete;\n  STTimerFDTimeoutManager& operator=(STTimerFDTimeoutManager&&) = delete;\n\n  /**\n   * Attaches/detaches TimeoutManager to AsyncTimeout\n   */\n  void attachTimeoutManager(AsyncTimeout* obj, InternalEnum internal) final;\n  void detachTimeoutManager(AsyncTimeout* obj) final;\n\n  /**\n   * Schedules AsyncTimeout to fire after `timeout` milliseconds\n   */\n  bool scheduleTimeout(AsyncTimeout* obj, timeout_type timeout) final;\n\n  /**\n   * Schedules AsyncTimeout to fire after `timeout` microseconds\n   */\n  bool scheduleTimeoutHighRes(\n      AsyncTimeout* obj, timeout_type_high_res timeout) final;\n\n  /**\n   * Cancels the AsyncTimeout, if scheduled\n   */\n  void cancelTimeout(AsyncTimeout* obj) final;\n\n  /**\n   * This is used to mark the beginning of a new loop cycle by the\n   * first handler fired within that cycle.\n   */\n  void bumpHandlingTime() final;\n\n  /**\n   * Helper method to know whether we are running in the timeout manager\n   * thread\n   */\n  bool isInTimeoutManagerThread() final {\n    return eventBase_->isInEventBaseThread();\n  }\n\n  // from TimerFD\n  void onTimeout() noexcept final;\n\n private:\n  static void setActive(AsyncTimeout* obj, bool active);\n\n  folly::EventBase* eventBase_{nullptr};\n  AsyncTimeout* obj_{nullptr};\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ScopedEventBaseThread.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/ScopedEventBaseThread.h>\n\n#include <thread>\n\n#include <folly/Function.h>\n#include <folly/Range.h>\n#include <folly/io/async/EventBaseManager.h>\n#include <folly/system/ThreadName.h>\n\nusing namespace std;\n\nnamespace folly {\n\nstatic void run(\n    EventBaseManager* ebm,\n    EventBase* eb,\n    folly::Baton<>* stop,\n    const StringPiece name) {\n  if (!name.empty()) {\n    folly::setThreadName(name);\n  }\n\n  ebm->setEventBase(eb, false);\n  eb->loopForever();\n\n  // must destruct in io thread for on-destruction callbacks\n  eb->runOnDestruction([=] { ebm->clearEventBase(); });\n  // wait until terminateLoopSoon() is complete\n  stop->wait(folly::Baton<>::wait_options().logging_enabled(false));\n  eb->~EventBase();\n}\n\nScopedEventBaseThread::ScopedEventBaseThread()\n    : ScopedEventBaseThread(nullptr, \"\") {}\n\nScopedEventBaseThread::ScopedEventBaseThread(StringPiece name)\n    : ScopedEventBaseThread(nullptr, name) {}\n\nScopedEventBaseThread::ScopedEventBaseThread(EventBaseManager* ebm)\n    : ScopedEventBaseThread(ebm, \"\") {}\n\nScopedEventBaseThread::ScopedEventBaseThread(\n    EventBaseManager* ebm, StringPiece name)\n    : ScopedEventBaseThread(EventBase::Options(), ebm, name) {}\n\nScopedEventBaseThread::ScopedEventBaseThread(\n    EventBase::Options eventBaseOptions,\n    EventBaseManager* ebm,\n    StringPiece name)\n    : ebm_(ebm ? ebm : EventBaseManager::get()) {\n  new (&eb_) EventBase(std::move(eventBaseOptions));\n  th_ = thread(run, ebm_, &eb_, &stop_, name);\n  eb_.waitUntilRunning();\n}\n\nScopedEventBaseThread::~ScopedEventBaseThread() {\n  eb_.terminateLoopSoon();\n  stop_.post();\n  th_.join();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ScopedEventBaseThread.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <thread>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/synchronization/Baton.h>\n\nnamespace folly {\n\nclass EventBaseManager;\ntemplate <class Iter>\nclass Range;\nusing StringPiece = Range<const char*>;\n\n/**\n * ScopedEventBaseThread is a helper class that starts a new std::thread running\n * an EventBase loop.\n *\n * The new thread will be started by the ScopedEventBaseThread constructor.\n * When the ScopedEventBaseThread object is destroyed, the thread will be\n * stopped.\n *\n * ScopedEventBaseThread is not CopyConstructible nor CopyAssignable nor\n * MoveConstructible nor MoveAssignable.\n *\n * @refcode folly/docs/examples/folly/ScopedEventBaseThread.cpp\n * @class folly::ScopedEventBaseThread\n */\nclass ScopedEventBaseThread : public IOExecutor, public SequencedExecutor {\n public:\n  /**\n   * Default constructor, initializes with current EventBaseManager and empty\n   * thread name.\n   * @refcode folly/docs/examples/folly/ScopedEventBaseThread2.cpp\n   */\n  ScopedEventBaseThread();\n  /**\n   * Initializes with current EventBaseManager and passed-in thread name.\n   */\n  explicit ScopedEventBaseThread(StringPiece name);\n  /**\n   * Initializes with passed-in EventBaseManager and empty thread name.\n   */\n  explicit ScopedEventBaseThread(EventBaseManager* ebm);\n  /**\n   * Initializes with passed-in EventBaseManager and thread name.\n   */\n  explicit ScopedEventBaseThread(EventBaseManager* ebm, StringPiece name);\n  /**\n   * Initializes with passed-in EventBaseOptions, EventBaseManager and thread\n   * name.\n   */\n  ScopedEventBaseThread(\n      EventBase::Options eventBaseOptions,\n      EventBaseManager* ebm,\n      StringPiece name);\n  ~ScopedEventBaseThread() override;\n\n  /**\n   * Returns the event base of the thread.\n   * @methodset Observers\n   */\n  EventBase* getEventBase() const { return &eb_; }\n\n  EventBase* getEventBase() override { return &eb_; }\n\n  /**\n   * Returns the ID of the thread.\n   * @methodset Observers\n   */\n  std::thread::id getThreadId() const { return th_.get_id(); }\n\n  /**\n   * Returns the underlying implementation-defined thread handle.\n   * @methodset Observers\n   */\n  std::thread::native_handle_type getNativeHandle() {\n    return th_.native_handle();\n  }\n\n  /**\n   * Runs the passed-in function on the event base of the thread.\n   *\n   * @param func Function to be run on event base of the thread.\n   * @methodset Operations\n   */\n  void add(Func func) override { getEventBase()->add(std::move(func)); }\n\n protected:\n  bool keepAliveAcquire() noexcept override {\n    return getEventBase()->keepAliveAcquire();\n  }\n\n  void keepAliveRelease() noexcept override {\n    getEventBase()->keepAliveRelease();\n  }\n\n private:\n  ScopedEventBaseThread(ScopedEventBaseThread&& other) = delete;\n  ScopedEventBaseThread& operator=(ScopedEventBaseThread&& other) = delete;\n\n  ScopedEventBaseThread(const ScopedEventBaseThread& other) = delete;\n  ScopedEventBaseThread& operator=(const ScopedEventBaseThread& other) = delete;\n\n  EventBaseManager* ebm_;\n  union {\n    mutable EventBase eb_;\n  };\n  std::thread th_;\n  folly::Baton<> stop_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/SimpleAsyncIO.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/SimpleAsyncIO.h>\n\n#include <folly/String.h>\n#include <folly/coro/Baton.h>\n#include <folly/io/async/AsyncIO.h>\n#include <folly/io/async/IoUring.h>\n#include <folly/io/async/Liburing.h>\n#include <folly/portability/Sockets.h>\n\nnamespace folly {\n\n#if __has_include(<libaio.h>)\nstatic constexpr bool has_aio = true;\nusing aio_type = AsyncIO;\n#else\nstatic constexpr bool has_aio = false;\nusing aio_type = void;\n#endif\n\n#if FOLLY_HAS_LIBURING\nstatic constexpr auto has_io_uring_rt = &IoUring::isAvailable;\nusing io_uring_type = IoUring;\n#else\nstatic constexpr auto has_io_uring_rt = +[] { return false; };\nusing io_uring_type = void;\n#endif\n\ntemplate <typename AsyncIOType>\nvoid SimpleAsyncIO::init() {\n  asyncIO_ = std::make_unique<AsyncIOType>(maxRequests_, AsyncBase::POLLABLE);\n  opsFreeList_.withWLock([this](auto& freeList) {\n    for (size_t i = 0; i < maxRequests_; ++i) {\n      freeList.push(std::make_unique<typename AsyncIOType::Op>());\n    }\n  });\n}\n\ntemplate <>\nvoid SimpleAsyncIO::init<void>() {}\n\nSimpleAsyncIO::SimpleAsyncIO(Config cfg)\n    : maxRequests_(cfg.maxRequests_),\n      completionExecutor_(cfg.completionExecutor_),\n      terminating_(false) {\n  static bool has_io_uring = has_io_uring_rt();\n  if (!has_aio && !has_io_uring) {\n    LOG(FATAL) << \"neither aio nor io_uring is available\";\n  }\n  if (cfg.mode_ == AIO && !has_aio) {\n    LOG(WARNING) << \"aio requested but unavailable: falling back to io_uring\";\n    cfg.setMode(IOURING);\n  }\n  if (cfg.mode_ == IOURING && !has_io_uring) {\n    LOG(WARNING) << \"io_uring requested but unavailable: falling back to aio\";\n    cfg.setMode(AIO);\n  }\n  switch (cfg.mode_) {\n    case AIO:\n      init<aio_type>();\n      break;\n    case IOURING:\n      init<io_uring_type>();\n      break;\n    default:\n      // Should never happen...\n      LOG(FATAL) << \"unrecognized mode \" << (int)cfg.mode_ << \" requested\";\n  }\n\n  if (cfg.evb_) {\n    initHandler(cfg.evb_, NetworkSocket::fromFd(asyncIO_->pollFd()));\n  } else {\n    evb_ = std::make_unique<ScopedEventBaseThread>(\"SimpleAsyncIO\");\n    initHandler(\n        evb_->getEventBase(), NetworkSocket::fromFd(asyncIO_->pollFd()));\n  }\n  registerHandler(EventHandler::READ | EventHandler::PERSIST);\n}\n\nSimpleAsyncIO::~SimpleAsyncIO() {\n  // stop accepting new IO.\n  opsFreeList_.withWLock(\n      [this](std::queue<std::unique_ptr<AsyncBaseOp>>& freeList) mutable {\n        terminating_ = true;\n        if (freeList.size() == maxRequests_) {\n          drainedBaton_.post();\n        }\n      });\n\n  drainedBaton_.wait();\n\n  unregisterHandler();\n}\n\nvoid SimpleAsyncIO::handlerReady(uint16_t events) noexcept {\n  if (events & EventHandler::READ) {\n    // All the work (including putting op back on free list) happens in the\n    // notificationCallback, so we can simply drop the ops returned from\n    // pollCompleted. But we must still call it or ops never complete.\n    while (asyncIO_->pollCompleted().size()) {\n      ;\n    }\n  }\n}\n\nstd::unique_ptr<AsyncBaseOp> SimpleAsyncIO::getOp() {\n  std::unique_ptr<AsyncBaseOp> rc;\n  opsFreeList_.withWLock(\n      [this, &rc](std::queue<std::unique_ptr<AsyncBaseOp>>& freeList) {\n        if (!freeList.empty() && !terminating_) {\n          rc = std::move(freeList.front());\n          freeList.pop();\n          rc->reset();\n        }\n      });\n  return rc;\n}\n\nvoid SimpleAsyncIO::putOp(std::unique_ptr<AsyncBaseOp>&& op) {\n  opsFreeList_.withWLock(\n      [this, op{std::move(op)}](\n          std::queue<std::unique_ptr<AsyncBaseOp>>& freeList) mutable {\n        freeList.push(std::move(op));\n        if (terminating_ && freeList.size() == maxRequests_) {\n          drainedBaton_.post();\n        }\n      });\n}\n\nvoid SimpleAsyncIO::submitOp(\n    Function<void(AsyncBaseOp*)> preparer, SimpleAsyncIOCompletor completor) {\n  std::unique_ptr<AsyncBaseOp> opHolder = getOp();\n  if (!opHolder) {\n    completor(-EBUSY);\n    return;\n  }\n\n  // Grab a raw pointer to the op before we create the completion lambda,\n  // since we move the unique_ptr into the lambda and can no longer access\n  // it.\n  AsyncBaseOp* op = opHolder.get();\n\n  preparer(op);\n\n  op->setNotificationCallback(\n      [this, completor{std::move(completor)}, opHolder{std::move(opHolder)}](\n          AsyncBaseOp* op_) mutable {\n        CHECK(op_ == opHolder.get());\n        int rc = op_->result();\n\n        auto completionExecutor = completionExecutor_;\n\n        // make sure return opHolder before call completor: otherwise the caller\n        // might start another IO assuming this one is completed. NB: the moment\n        // we put the opHolder, the destructor might delete the current\n        // instance. So do not access any member variables after this point!\n        // Also, obviously, do not access op_.\n        putOp(std::move(opHolder));\n\n        completionExecutor->add(\n            [rc, completor{std::move(completor)}]() mutable { completor(rc); });\n      });\n  asyncIO_->submit(op);\n}\n\nvoid SimpleAsyncIO::pread(\n    int fd,\n    void* buf,\n    size_t size,\n    off_t start,\n    SimpleAsyncIOCompletor completor) {\n  submitOp(\n      [=](AsyncBaseOp* op) { op->pread(fd, buf, size, start); },\n      std::move(completor));\n}\n\nvoid SimpleAsyncIO::pwrite(\n    int fd,\n    const void* buf,\n    size_t size,\n    off_t start,\n    SimpleAsyncIOCompletor completor) {\n  submitOp(\n      [=](AsyncBaseOp* op) { op->pwrite(fd, buf, size, start); },\n      std::move(completor));\n}\n\n#if FOLLY_HAS_COROUTINES\nfolly::coro::Task<int> SimpleAsyncIO::co_pwrite(\n    int fd, const void* buf, size_t size, off_t start) {\n  folly::coro::Baton done;\n  int result;\n  pwrite(fd, buf, size, start, [&done, &result](int rc) {\n    result = rc;\n    done.post();\n  });\n  co_await done;\n  co_return result;\n}\n\nfolly::coro::Task<int> SimpleAsyncIO::co_pread(\n    int fd, void* buf, size_t size, off_t start) {\n  folly::coro::Baton done;\n  int result;\n  pread(fd, buf, size, start, [&done, &result](int rc) {\n    result = rc;\n    done.post();\n  });\n  co_await done;\n  co_return result;\n}\n#endif // FOLLY_HAS_COROUTINES\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/SimpleAsyncIO.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <queue>\n\n#include <folly/Synchronized.h>\n#include <folly/coro/Task.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/io/async/AsyncBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n\nnamespace folly {\n\n/**\n * SimpleAsyncIO is a wrapper around AsyncIO intended to hide all the details.\n *\n * Usage: just create an instance of SimpleAsyncIO and then issue IO with\n * pread and pwrite, no other effort required. e.g.:\n *\n *\n *        auto tmpfile = folly::File::temporary();\n *        folly::SimpleAsyncIO aio;\n *        aio.pwrite(\n *            tmpfile.fd(),\n *            \"hello world\",\n *            11, // size\n *            0, // offset\n *            [](int rc) { LOG(INFO) << \"Write completed with rc \" << rc; });\n *\n *\n * IO is dispatched in the context of the calling thread; it may block briefly\n * to obtain a lock on shared resources, but will *not* block for IO\n * completion. If the IO queue is full (see setMaxRequests(size_t) in Config),\n * IO fails with -EBUSY.\n *\n * IO is completed on the executor specified in the config (global CPU\n * executor by default).\n *\n * IO is completed by calling the callback function provided to pread/pwrite.\n * The single parameter to the callback is either a negative errno or the\n * number of bytes transferred.\n *\n * There is a \"hidden\" EventBase which polls for IO completion and dispatches\n * completion events to the executor. You may specify an existing EventBase in\n * the config (and you are then responsible for making sure the EventBase\n * instance outlives the SimpleAsyncIO instance). If you do not specify one, a\n * ScopedEventBaseThread instance will be created.\n *\n * Following structure defines the configuration of a SimpleAsyncIO instance,\n * in case you need to override the (sensible) defaults.\n *\n * Typical usage is something like:\n *\n *        SimpleAsyncIO io(SimpleAsyncIO::Config()\n *            .setMaxRequests(100)\n *            .setMode(SimpleAsyncIO::Mode::IOURING));\n */\nclass SimpleAsyncIO : public EventHandler {\n public:\n  /**\n   * The asynchronized backend to be used: libaio or liburing\n   */\n  enum Mode {\n    /// use libaio\n    AIO,\n    /// use liburing\n    IOURING\n  };\n  /**\n   * The Config for SimpleAsyncIO on:\n   * - choosing backend implementation\n   * - executor to use for receiving completion\n   * - max requests are allowed\n   */\n  struct Config {\n    Config()\n        : maxRequests_(1000),\n          completionExecutor_(\n              getKeepAliveToken(getUnsafeMutableGlobalCPUExecutor().get())),\n          mode_(AIO),\n          evb_(nullptr) {}\n    /// Maximum requests can be queued; -EBUSY returned for requests above\n    /// threshold\n    Config& setMaxRequests(size_t maxRequests) {\n      maxRequests_ = maxRequests;\n      return *this;\n    }\n    Config& setCompletionExecutor(Executor::KeepAlive<> completionExecutor) {\n      completionExecutor_ = completionExecutor;\n      return *this;\n    }\n    Config& setMode(Mode mode) {\n      mode_ = mode;\n      return *this;\n    }\n    Config& setEventBase(EventBase* evb) {\n      evb_ = evb;\n      return *this;\n    }\n\n   private:\n    size_t maxRequests_;\n    Executor::KeepAlive<> completionExecutor_;\n    Mode mode_;\n    EventBase* evb_;\n\n    friend class SimpleAsyncIO;\n  };\n\n  explicit SimpleAsyncIO(Config cfg = Config());\n  virtual ~SimpleAsyncIO() override;\n\n  using SimpleAsyncIOCompletor = Function<void(int rc)>;\n\n  /**\n   * Initiate an asynchronous read request.\n   *\n   * Parameters and return value are same as pread(2).\n   *\n   * Completion is indicated by an asynchronous call to the given completor\n   * callback. The sole parameter to the callback is the result of the\n   * operation.\n   *\n   * @returns Same as pread(2) and if requests number reaches maxRequests_,\n   * return -EBUSY\n   */\n  void pread(\n      int fd,\n      void* buf,\n      size_t size,\n      off_t start,\n      SimpleAsyncIOCompletor completor);\n\n  /**\n   * Initiate an asynchronous write request.\n   *\n   * Parameters and return value are same as pwrite(2).\n   *\n   * Completion is indicated by an asynchronous call to the given completor\n   * callback. The sole parameter to the callback is the result of the\n   * operation.\n   *\n   * @returns Same as pwrite(2) and if requests number reaches maxRequests_,\n   * return -EBUSY\n   */\n  void pwrite(\n      int fd,\n      const void* data,\n      size_t size,\n      off_t offset,\n      SimpleAsyncIOCompletor completor);\n\n#if FOLLY_HAS_COROUTINES\n  /**\n   * Coroutine version of pread().\n   *\n   * Identical to pread() except that result is obtained by co_await instead of\n   * callback.\n   *\n   * @returns Same as pread(2) and if requests number reaches maxRequests_,\n   * return -EBUSY\n   */\n  folly::coro::Task<int> co_pread(int fd, void* buf, size_t size, off_t start);\n  /**\n   * Coroutine version of pwrite().\n   *\n   * Identical to pwrite() except that result is obtained by co_await instead of\n   * callback.\n   *\n   * @returns Same as pwrite(2) and if requests number reaches maxRequests_,\n   * return -EBUSY\n   */\n  folly::coro::Task<int> co_pwrite(\n      int fd, const void* buf, size_t size, off_t start);\n#endif\n\n private:\n  std::unique_ptr<AsyncBaseOp> getOp();\n  void putOp(std::unique_ptr<AsyncBaseOp>&&);\n\n  void submitOp(\n      Function<void(AsyncBaseOp*)> preparer, SimpleAsyncIOCompletor completor);\n\n  virtual void handlerReady(uint16_t events) noexcept override;\n\n  template <typename AsyncIOType>\n  void init();\n\n  size_t maxRequests_;\n  Executor::KeepAlive<> completionExecutor_;\n  std::unique_ptr<AsyncBase> asyncIO_;\n  Synchronized<std::queue<std::unique_ptr<AsyncBaseOp>>> opsFreeList_;\n  std::unique_ptr<ScopedEventBaseThread> evb_;\n  bool terminating_;\n  Baton<> drainedBaton_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TerminateCancellationToken.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/TerminateCancellationToken.h>\n\n#include <csignal>\n\n#include <folly/Singleton.h>\n#include <folly/io/async/AsyncSignalHandler.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n\nnamespace folly {\nnamespace {\n/**\n * A helper class that starts a new thread to listen for signals. It can issue\n * CancellationToken that can be used to schedule callback to execute on\n * receiving the signals.\n */\nclass ScopedTerminateSignalHandler : private AsyncSignalHandler {\n public:\n  ScopedTerminateSignalHandler() : AsyncSignalHandler(nullptr) {\n    attachEventBase(scopedEventBase_.getEventBase());\n    // To prevent data race with unregisterSignalHandler\n    scopedEventBase_.getEventBase()->runInEventBaseThreadAndWait([this]() {\n      registerSignalHandler(SIGTERM);\n      registerSignalHandler(SIGINT);\n    });\n  }\n\n  CancellationToken getCancellationToken() {\n    return cancellationSource_.getToken();\n  }\n\n private:\n  void signalReceived(int) noexcept override {\n    // Unregister signal handler as we want to handle the signal only once\n    unregisterSignalHandler(SIGTERM);\n    unregisterSignalHandler(SIGINT);\n    cancellationSource_.requestCancellation();\n  }\n\n  ScopedEventBaseThread scopedEventBase_;\n  CancellationSource cancellationSource_;\n};\n\nfolly::Singleton<ScopedTerminateSignalHandler> singletonSignalHandler;\n\n} // namespace\n\nCancellationToken getTerminateCancellationToken() {\n  auto signalHandler = singletonSignalHandler.try_get();\n  if (signalHandler == nullptr) {\n    CancellationSource cs;\n    cs.requestCancellation();\n    return cs.getToken();\n  }\n  return signalHandler->getCancellationToken();\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TerminateCancellationToken.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/CancellationToken.h>\n\nnamespace folly {\n\n/**\n * Returns a CancellationToken that can be used to schedule callbacks. The\n * CancellationToken is cancelled when any of SIGTERM and SIGINT\n * signal is received.\n */\nCancellationToken getTerminateCancellationToken();\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TimeoutManager.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/TimeoutManager.h>\n\n#include <boost/intrusive/list.hpp>\n#include <glog/logging.h>\n\n#include <folly/Chrono.h>\n#include <folly/Exception.h>\n#include <folly/Memory.h>\n#include <folly/io/async/AsyncTimeout.h>\n\nnamespace folly {\n\nstruct TimeoutManager::CobTimeouts {\n  // small object used as a callback arg with enough info to execute the\n  // appropriate client-provided Cob\n  class CobTimeout : public AsyncTimeout {\n   public:\n    CobTimeout(TimeoutManager* timeoutManager, Func cob, InternalEnum internal)\n        : AsyncTimeout(timeoutManager, internal), cob_(std::move(cob)) {}\n\n    void timeoutExpired() noexcept override {\n      // For now, we just swallow any exceptions that the callback threw.\n      try {\n        cob_();\n      } catch (const std::exception& ex) {\n        LOG(ERROR) << \"TimeoutManager::runAfterDelay() callback threw \"\n                   << typeid(ex).name() << \" exception: \" << ex.what();\n      } catch (...) {\n        LOG(ERROR) << \"TimeoutManager::runAfterDelay() callback threw \"\n                   << \"non-exception type\";\n      }\n\n      // The CobTimeout object was allocated on the heap by runAfterDelay(),\n      // so delete it now that the it has fired.\n      delete this;\n    }\n\n   private:\n    Func cob_;\n\n   public:\n    using ListHook = boost::intrusive::list_member_hook<\n        boost::intrusive::link_mode<boost::intrusive::auto_unlink>>;\n    ListHook hook;\n    using List = boost::intrusive::list<\n        CobTimeout,\n        boost::intrusive::member_hook<CobTimeout, ListHook, &CobTimeout::hook>,\n        boost::intrusive::constant_time_size<false>>;\n  };\n\n  CobTimeout::List list;\n};\n\nTimeoutManager::TimeoutManager()\n    : cobTimeouts_(std::make_unique<CobTimeouts>()) {}\n\nbool TimeoutManager::scheduleTimeoutHighRes(\n    AsyncTimeout* obj, timeout_type_high_res timeout) {\n  timeout_type timeout_ms = folly::chrono::ceil<timeout_type>(timeout);\n  return scheduleTimeout(obj, timeout_ms);\n}\n\nvoid TimeoutManager::runAfterDelay(\n    Func cob, uint32_t milliseconds, InternalEnum internal) {\n  if (!tryRunAfterDelay(std::move(cob), milliseconds, internal)) {\n    folly::throwSystemError(\n        \"error in TimeoutManager::runAfterDelay(), failed to schedule timeout\");\n  }\n}\n\nbool TimeoutManager::tryRunAfterDelay(\n    Func cob, uint32_t milliseconds, InternalEnum internal) {\n  if (!cobTimeouts_) {\n    return false;\n  }\n\n  auto timeout =\n      std::make_unique<CobTimeouts::CobTimeout>(this, std::move(cob), internal);\n  if (!timeout->scheduleTimeout(milliseconds)) {\n    return false;\n  }\n  cobTimeouts_->list.push_back(*timeout.release());\n  return true;\n}\n\nvoid TimeoutManager::clearCobTimeouts() {\n  if (!cobTimeouts_) {\n    return;\n  }\n\n  // Delete any unfired callback objects, so that we don't leak memory\n  // Note that we don't fire them.\n  while (!cobTimeouts_->list.empty()) {\n    auto* timeout = &cobTimeouts_->list.front();\n    delete timeout;\n  }\n}\n\nTimeoutManager::~TimeoutManager() {\n  clearCobTimeouts();\n}\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TimeoutManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <cstdint>\n\n#include <folly/Function.h>\n#include <folly/Optional.h>\n\nnamespace folly {\n\nclass AsyncTimeout;\n\n/**\n * Base interface to be implemented by all classes expecting to manage\n * timeouts. AsyncTimeout will use implementations of this interface\n * to schedule/cancel timeouts.\n */\nclass TimeoutManager {\n public:\n  using timeout_type = std::chrono::milliseconds;\n  using timeout_type_high_res = std::chrono::microseconds;\n\n  using Func = folly::Function<void()>;\n\n  enum class InternalEnum { INTERNAL, NORMAL };\n\n  TimeoutManager();\n\n  virtual ~TimeoutManager();\n\n  /**\n   * Attaches/detaches TimeoutManager to AsyncTimeout\n   */\n  virtual void attachTimeoutManager(\n      AsyncTimeout* obj, InternalEnum internal) = 0;\n  virtual void detachTimeoutManager(AsyncTimeout* obj) = 0;\n\n  /**\n   * Schedules AsyncTimeout to fire after `timeout` milliseconds\n   */\n  virtual bool scheduleTimeout(AsyncTimeout* obj, timeout_type timeout) = 0;\n\n  /**\n   * Schedules AsyncTimeout to fire after `timeout` microseconds\n   */\n  virtual bool scheduleTimeoutHighRes(\n      AsyncTimeout* obj, timeout_type_high_res timeout);\n\n  /**\n   * Cancels the AsyncTimeout, if scheduled\n   */\n  virtual void cancelTimeout(AsyncTimeout* obj) = 0;\n\n  /**\n   * This is used to mark the beginning of a new loop cycle by the\n   * first handler fired within that cycle.\n   */\n  virtual void bumpHandlingTime() = 0;\n\n  /**\n   * Helper method to know whether we are running in the timeout manager\n   * thread\n   */\n  virtual bool isInTimeoutManagerThread() = 0;\n\n  /**\n   * Runs the given Cob at some time after the specified number of\n   * milliseconds.  (No guarantees exactly when.)\n   *\n   * Throws a std::system_error if an error occurs.\n   */\n  void runAfterDelay(\n      Func cob,\n      uint32_t milliseconds,\n      InternalEnum internal = InternalEnum::NORMAL);\n\n  /**\n   * @see tryRunAfterDelay for more details\n   *\n   * @return  true iff the cob was successfully registered.\n   */\n  bool tryRunAfterDelay(\n      Func cob,\n      uint32_t milliseconds,\n      InternalEnum internal = InternalEnum::NORMAL);\n\n protected:\n  void clearCobTimeouts();\n\n private:\n  struct CobTimeouts;\n  std::unique_ptr<CobTimeouts> cobTimeouts_;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TimerFD.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/TimerFD.h>\n\n#include <folly/FileUtil.h>\n\n#ifdef FOLLY_HAVE_TIMERFD\n#include <sys/timerfd.h>\n#endif\n\nnamespace folly {\n#ifdef FOLLY_HAVE_TIMERFD\n// TimerFD\nTimerFD::TimerFD(folly::EventBase* eventBase)\n    : TimerFD(eventBase, createTimerFd()) {}\n\nTimerFD::TimerFD(folly::EventBase* eventBase, int fd)\n    : folly::EventHandler(eventBase, NetworkSocket::fromFd(fd)), fd_(fd) {\n  if (fd_ > 0) {\n    registerHandler(folly::EventHandler::READ | folly::EventHandler::PERSIST);\n  }\n}\n\nTimerFD::~TimerFD() {\n  cancel();\n  close();\n}\n\nvoid TimerFD::close() {\n  unregisterHandler();\n\n  if (fd_ > 0) {\n    detachEventBase();\n    changeHandlerFD(NetworkSocket());\n    fileops::close(fd_);\n    fd_ = -1;\n  }\n}\n\nvoid TimerFD::schedule(std::chrono::microseconds timeout) {\n  // schedule(0) will stop the timer otherwise\n  setTimer(timeout.count() ? timeout : std::chrono::microseconds(1));\n}\n\nvoid TimerFD::cancel() {\n  setTimer(std::chrono::microseconds(0));\n}\n\nbool TimerFD::setTimer(std::chrono::microseconds useconds) {\n  if (fd_ <= 0) {\n    return false;\n  }\n\n  struct itimerspec val;\n  val.it_interval = {0, 0};\n  val.it_value.tv_sec =\n      std::chrono::duration_cast<std::chrono::seconds>(useconds).count();\n  val.it_value.tv_nsec =\n      std::chrono::duration_cast<std::chrono::nanoseconds>(useconds).count() %\n      1000000000LL;\n\n  return (0 == ::timerfd_settime(fd_, 0, &val, nullptr));\n}\n\nvoid TimerFD::handlerReady(uint16_t events) noexcept {\n  DestructorGuard dg(this);\n\n  auto relevantEvents = uint16_t(events & folly::EventHandler::READ_WRITE);\n  if (relevantEvents == folly::EventHandler::READ ||\n      relevantEvents == folly::EventHandler::READ_WRITE) {\n    uint64_t data = 0;\n    ssize_t num = folly::readNoInt(fd_, &data, sizeof(data));\n    if (num == sizeof(data)) {\n      onTimeout();\n    }\n  }\n}\n\nint TimerFD::createTimerFd() {\n  return ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);\n}\n#else\nTimerFD::TimerFD(folly::EventBase* eventBase) : timeout_(eventBase, this) {}\n\nTimerFD::~TimerFD() {\n  // cancel has to be called from the derived classes !!!\n}\n\nvoid TimerFD::schedule(std::chrono::microseconds timeout) {\n  timeout_.scheduleTimeoutHighRes(timeout);\n}\n\nvoid TimerFD::cancel() {\n  timeout_.cancelTimeout();\n}\n\nTimerFD::TimerFDAsyncTimeout::TimerFDAsyncTimeout(\n    folly::EventBase* eventBase, TimerFD* timerFd)\n    : folly::AsyncTimeout(eventBase), timerFd_(timerFd) {}\n\nvoid TimerFD::TimerFDAsyncTimeout::timeoutExpired() noexcept {\n  timerFd_->onTimeout();\n}\n#endif\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TimerFD.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#if defined(__linux__) && !defined(__ANDROID__)\n#define FOLLY_HAVE_TIMERFD\n#endif\n\n#include <folly/io/async/EventBase.h>\n#ifdef FOLLY_HAVE_TIMERFD\n#include <folly/io/async/EventHandler.h>\n#else\n#include <folly/io/async/AsyncTimeout.h>\n#endif\n#include <chrono>\n\nnamespace folly {\n#ifdef FOLLY_HAVE_TIMERFD\n// timerfd wrapper\nclass TimerFD : public folly::EventHandler, public DelayedDestruction {\n public:\n  explicit TimerFD(folly::EventBase* eventBase);\n  ~TimerFD() override;\n\n  virtual void onTimeout() noexcept = 0;\n  void schedule(std::chrono::microseconds timeout);\n  void cancel();\n\n  // from folly::EventHandler\n  void handlerReady(uint16_t events) noexcept override;\n\n protected:\n  void close();\n\n private:\n  TimerFD(folly::EventBase* eventBase, int fd);\n  static int createTimerFd();\n\n  // use 0 to stop the timer\n  bool setTimer(std::chrono::microseconds useconds);\n\n  int fd_{-1};\n};\n#else\n// alternative implementation using a folly::AsyncTimeout\nclass TimerFD {\n public:\n  explicit TimerFD(folly::EventBase* eventBase);\n  virtual ~TimerFD();\n\n  virtual void onTimeout() = 0;\n  void schedule(std::chrono::microseconds timeout);\n  void cancel();\n\n protected:\n  void close() {}\n\n private:\n  class TimerFDAsyncTimeout : public folly::AsyncTimeout {\n   public:\n    TimerFDAsyncTimeout(folly::EventBase* eventBase, TimerFD* timerFd);\n    ~TimerFDAsyncTimeout() override = default;\n\n    // from folly::AsyncTimeout\n    void timeoutExpired() noexcept final;\n\n   private:\n    TimerFD* timerFd_;\n  };\n\n  TimerFDAsyncTimeout timeout_;\n};\n#endif\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TimerFDTimeoutManager.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/TimerFDTimeoutManager.h>\n\nnamespace folly {\n// TimerFDTimeoutManager\nTimerFDTimeoutManager::TimerFDTimeoutManager(folly::EventBase* eventBase)\n    : TimerFD(eventBase) {}\n\nTimerFDTimeoutManager::~TimerFDTimeoutManager() {\n  cancelAll();\n  close();\n}\n\nvoid TimerFDTimeoutManager::onTimeout() noexcept {\n  processExpiredTimers();\n  scheduleNextTimer();\n}\n\nvoid TimerFDTimeoutManager::scheduleTimeout(\n    Callback* callback, std::chrono::microseconds timeout) {\n  cancelTimeout(callback);\n  // we cannot schedule a timeout of 0 - this will stop the timer\n  if (FOLLY_UNLIKELY(!timeout.count())) {\n    timeout = std::chrono::microseconds(1);\n  }\n  auto expirationTime = getCurTime() + timeout;\n  auto expirationTimeUsec =\n      std::chrono::duration_cast<std::chrono::microseconds>(\n          expirationTime.time_since_epoch());\n\n  if (callbacks_.empty() || expirationTimeUsec < callbacks_.begin()->first) {\n    schedule(timeout);\n  }\n\n  // now add the callback\n  // handle entries that expire at the same time\n  callbacks_[expirationTimeUsec].push_back(*callback);\n\n  callback->setExpirationTime(this, expirationTimeUsec);\n}\n\nbool TimerFDTimeoutManager::cancelTimeout(Callback* callback) {\n  if (!callback->is_linked()) {\n    return false;\n  }\n\n  callback->unlink();\n  callback->callbackCanceled();\n\n  auto expirationTime = callback->getExpirationTime();\n\n  auto iter = callbacks_.find(expirationTime);\n\n  if (iter == callbacks_.end()) {\n    return false;\n  }\n\n  bool removeFirst = (iter == callbacks_.begin());\n\n  if (iter->second.empty()) {\n    callbacks_.erase(iter);\n  }\n\n  // reschedule the timer if needed\n  if (!processingExpired_ && removeFirst && !callbacks_.empty()) {\n    auto now = std::chrono::duration_cast<std::chrono::microseconds>(\n        getCurTime().time_since_epoch());\n    if (now > callbacks_.begin()->first) {\n      auto timeout = now - callbacks_.begin()->first;\n\n      schedule(timeout);\n    }\n  }\n\n  if (callbacks_.empty()) {\n    cancel();\n  }\n\n  return true;\n}\n\nsize_t TimerFDTimeoutManager::cancelAll() {\n  size_t ret = 0;\n  while (!callbacks_.empty()) {\n    auto iter = callbacks_.begin();\n    auto callbackList = std::move(iter->second);\n    callbacks_.erase(iter);\n\n    while (!callbackList.empty()) {\n      ++ret;\n      auto* callback = &callbackList.front();\n      callbackList.pop_front();\n      callback->callbackCanceled();\n    }\n  }\n\n  // and now the in progress list\n  while (!inProgressList_.empty()) {\n    ++ret;\n    auto* callback = &inProgressList_.front();\n    inProgressList_.pop_front();\n    callback->callbackCanceled();\n  }\n\n  if (ret) {\n    cancel();\n  }\n  return ret;\n}\n\nsize_t TimerFDTimeoutManager::count() const {\n  size_t ret = 0;\n  for (const auto& c : callbacks_) {\n    ret += c.second.size();\n  }\n\n  return ret;\n}\n\nvoid TimerFDTimeoutManager::processExpiredTimers() {\n  processingExpired_ = true;\n  while (true) {\n    if (callbacks_.empty()) {\n      break;\n    }\n\n    auto iter = callbacks_.begin();\n    auto now = std::chrono::duration_cast<std::chrono::microseconds>(\n        getCurTime().time_since_epoch());\n    if (now >= iter->first) {\n      inProgressList_ = std::move(iter->second);\n      callbacks_.erase(iter);\n\n      CHECK(!inProgressList_.empty());\n\n      while (!inProgressList_.empty()) {\n        auto* callback = &inProgressList_.front();\n        inProgressList_.pop_front();\n        callback->timeoutExpired();\n      }\n    } else {\n      break;\n    }\n  }\n  processingExpired_ = false;\n}\n\nvoid TimerFDTimeoutManager::scheduleNextTimer() {\n  if (callbacks_.empty()) {\n    return;\n  }\n\n  auto iter = callbacks_.begin();\n  auto now = std::chrono::duration_cast<std::chrono::microseconds>(\n      getCurTime().time_since_epoch());\n\n  if (iter->first > now) {\n    schedule(iter->first - now);\n  } else {\n    // we schedule it here again to avoid the case\n    // where a timer can cause starvation\n    // by continuosly rescheduling itlsef\n    schedule(std::chrono::microseconds(1));\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/TimerFDTimeoutManager.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n\n#include <folly/io/async/DelayedDestruction.h>\n#include <folly/io/async/TimerFD.h>\n\nnamespace folly {\n// generic TimerFD based timeout manager\nclass TimerFDTimeoutManager : public TimerFD {\n public:\n  using UniquePtr = DelayedDestructionUniquePtr<TimerFDTimeoutManager>;\n  using SharedPtr = std::shared_ptr<TimerFDTimeoutManager>;\n\n public:\n  class Callback\n      : public boost::intrusive::list_base_hook<\n            boost::intrusive::link_mode<boost::intrusive::auto_unlink>> {\n   public:\n    Callback() = default;\n    explicit Callback(TimerFDTimeoutManager* mgr) : mgr_(mgr) {}\n    virtual ~Callback() = default;\n\n    virtual void timeoutExpired() noexcept = 0;\n    virtual void callbackCanceled() noexcept { timeoutExpired(); }\n\n    const std::chrono::microseconds& getExpirationTime() const {\n      return expirationTime_;\n    }\n\n    void setExpirationTime(\n        TimerFDTimeoutManager* mgr,\n        const std::chrono::microseconds& expirationTime) {\n      mgr_ = mgr;\n      expirationTime_ = expirationTime;\n    }\n\n    std::chrono::microseconds getTimeRemaining() const {\n      return getTimeRemaining(std::chrono::steady_clock::now());\n    }\n\n    std::chrono::microseconds getTimeRemaining(\n        std::chrono::steady_clock::time_point now) const {\n      auto nowMs = std::chrono::duration_cast<std::chrono::microseconds>(\n          now.time_since_epoch());\n      if (expirationTime_ > nowMs) {\n        return std::chrono::duration_cast<std::chrono::microseconds>(\n            expirationTime_ - nowMs);\n      }\n\n      return std::chrono::microseconds(0);\n    }\n\n    void scheduleTimeout(std::chrono::microseconds timeout) {\n      if (mgr_) {\n        mgr_->scheduleTimeout(this, timeout);\n      }\n    }\n\n    bool cancelTimeout() { return mgr_->cancelTimeout(this); }\n\n   private:\n    TimerFDTimeoutManager* mgr_{nullptr};\n    std::chrono::microseconds expirationTime_{0};\n  };\n\n  explicit TimerFDTimeoutManager(folly::EventBase* eventBase);\n  ~TimerFDTimeoutManager() override;\n\n  // from TimerFD\n  void onTimeout() noexcept final;\n\n  size_t cancelAll();\n  void scheduleTimeout(Callback* callback, std::chrono::microseconds timeout);\n  bool cancelTimeout(Callback* callback);\n\n  template <class F>\n  void scheduleTimeoutFn(F fn, std::chrono::microseconds timeout) {\n    struct Wrapper : Callback {\n      explicit Wrapper(F f) : fn_(std::move(f)) {}\n      void timeoutExpired() noexcept override {\n        try {\n          fn_();\n        } catch (std::exception const& e) {\n          LOG(ERROR) << \"HHWheelTimerBase timeout callback threw an exception: \"\n                     << e.what();\n        } catch (...) {\n          LOG(ERROR)\n              << \"HHWheelTimerBase timeout callback threw a non-exception.\";\n        }\n        delete this;\n      }\n      F fn_;\n    };\n    Wrapper* w = new Wrapper(std::move(fn));\n    scheduleTimeout(w, timeout);\n  }\n\n  size_t count() const;\n\n private:\n  void processExpiredTimers();\n  void scheduleNextTimer();\n\n  std::chrono::steady_clock::time_point getCurTime() {\n    return std::chrono::steady_clock::now();\n  }\n\n  // we can attempt to schedule new entries while in processExpiredTimers\n  // we want to reschedule the timers once we're done with the processing\n  bool processingExpired_{false};\n\n  using CallbackList = boost::intrusive::\n      list<Callback, boost::intrusive::constant_time_size<false>>;\n  std::map<std::chrono::microseconds, CallbackList> callbacks_;\n  CallbackList inProgressList_;\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/VirtualEventBase.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/VirtualEventBase.h>\n\nnamespace folly {\n\nVirtualEventBase::VirtualEventBase(EventBase& evb)\n    : evb_(getKeepAliveToken(evb)) {}\n\nstd::future<void> VirtualEventBase::destroy() {\n  loopKeepAlive_.reset();\n  return std::move(destroyFuture_);\n}\n\nvoid VirtualEventBase::destroyImpl() noexcept {\n  try {\n    {\n      // After destroyPromise_ is posted this object may be destroyed, so make\n      // sure we release EventBase's keep-alive token before that.\n      SCOPE_EXIT {\n        evb_.reset();\n      };\n\n      clearCobTimeouts();\n\n      while (!onDestructionCallbacks_.rlock()->empty()) {\n        // To avoid potential deadlock, do not hold the mutex while invoking\n        // user-supplied callbacks.\n        EventBase::OnDestructionCallback::List callbacks;\n        onDestructionCallbacks_.swap(callbacks);\n        while (!callbacks.empty()) {\n          auto& callback = callbacks.front();\n          callbacks.pop_front();\n          callback.runCallback();\n        }\n      }\n    }\n\n    destroyPromise_.set_value();\n  } catch (...) {\n    destroyPromise_.set_exception(current_exception());\n  }\n}\n\nVirtualEventBase::~VirtualEventBase() {\n  if (!destroyFuture_.valid()) {\n    return;\n  }\n  CHECK(!evb_->inRunningEventBaseThread());\n  destroy().get();\n}\n\nvoid VirtualEventBase::runOnDestruction(\n    EventBase::OnDestructionCallback& callback) {\n  callback.schedule(\n      [this](auto& cb) { onDestructionCallbacks_.wlock()->push_back(cb); },\n      [this](auto& cb) {\n        onDestructionCallbacks_.withWLock([&](auto& list) {\n          list.erase(list.iterator_to(cb));\n        });\n      });\n}\n\nvoid VirtualEventBase::runOnDestruction(Func f) {\n  auto* callback = new EventBase::FunctionOnDestructionCallback(std::move(f));\n  runOnDestruction(*callback);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/VirtualEventBase.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <future>\n\n#include <folly/Executor.h>\n#include <folly/Function.h>\n#include <folly/Synchronized.h>\n#include <folly/executors/SequencedExecutor.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/synchronization/Baton.h>\n\nnamespace folly {\n\n/**\n * VirtualEventBase implements a light-weight view onto existing EventBase.\n *\n * Multiple VirtualEventBases can be backed by a single EventBase. Similarly\n * to EventBase, VirtualEventBase implements KeepAlive functionality,\n * which allows callbacks holding KeepAlive token to keep EventBase looping\n * until they are complete.\n *\n * VirtualEventBase destructor blocks until all its KeepAliveTokens are released\n * and all tasks scheduled through it are complete. EventBase destructor also\n * blocks until all VirtualEventBases backed by it are released.\n */\nclass VirtualEventBase\n    : public folly::TimeoutManager,\n      public folly::SequencedExecutor {\n public:\n  explicit VirtualEventBase(EventBase& evb);\n\n  VirtualEventBase(const VirtualEventBase&) = delete;\n  VirtualEventBase& operator=(const VirtualEventBase&) = delete;\n\n  ~VirtualEventBase() override;\n\n  EventBase& getEventBase() { return *evb_; }\n\n  /**\n   * Adds the given callback to a queue of things run before destruction\n   * of current VirtualEventBase.\n   *\n   * This allows users of VirtualEventBase that run in it, but don't control it,\n   * to be notified before VirtualEventBase gets destructed.\n   *\n   * Note: this will be called from the loop of the EventBase, backing this\n   * VirtualEventBase\n   */\n  void runOnDestruction(EventBase::OnDestructionCallback& callback);\n  void runOnDestruction(Func f);\n\n  /**\n   * VirtualEventBase destructor blocks until all tasks scheduled through its\n   * runInEventBaseThread are complete.\n   *\n   * @see EventBase::runInEventBaseThread\n   */\n  template <typename F>\n  void runInEventBaseThread(F&& f) noexcept {\n    // KeepAlive token has to be released in the EventBase thread. If\n    // runInEventBaseThread() fails, we can't extract the KeepAlive token\n    // from the callback to properly release it.\n    evb_->runInEventBaseThread(\n        [keepAliveToken = getKeepAliveToken(this),\n         f = std::forward<F>(f)]() mutable { f(); });\n  }\n\n  HHWheelTimer& timer() { return evb_->timer(); }\n\n  void attachTimeoutManager(\n      AsyncTimeout* obj, TimeoutManager::InternalEnum internal) override {\n    evb_->attachTimeoutManager(obj, internal);\n  }\n\n  void detachTimeoutManager(AsyncTimeout* obj) override {\n    evb_->detachTimeoutManager(obj);\n  }\n\n  bool scheduleTimeout(\n      AsyncTimeout* obj, TimeoutManager::timeout_type timeout) override {\n    return evb_->scheduleTimeout(obj, timeout);\n  }\n\n  void cancelTimeout(AsyncTimeout* obj) override { evb_->cancelTimeout(obj); }\n\n  void bumpHandlingTime() override { evb_->bumpHandlingTime(); }\n\n  bool isInTimeoutManagerThread() override {\n    return evb_->isInTimeoutManagerThread();\n  }\n\n  /**\n   * @see runInEventBaseThread\n   */\n  void add(folly::Func f) override { runInEventBaseThread(std::move(f)); }\n\n  bool inRunningEventBaseThread() const {\n    return evb_->inRunningEventBaseThread();\n  }\n\n protected:\n  bool keepAliveAcquire() noexcept override {\n    auto oldCount = keepAliveCount_.fetch_add(1, std::memory_order_relaxed);\n    DCHECK_NE(oldCount, 0);\n    return true;\n  }\n\n  void keepAliveRelease() noexcept override {\n    auto oldCount = keepAliveCount_.fetch_sub(1, std::memory_order_acq_rel);\n    if (oldCount != 1) {\n      DCHECK_NE(oldCount, 0);\n      return;\n    }\n    if (!evb_->inRunningEventBaseThread()) {\n      evb_->runInEventBaseThreadAlwaysEnqueue([this] { destroyImpl(); });\n    } else {\n      destroyImpl();\n    }\n  }\n\n private:\n  friend class EventBase;\n\n  size_t keepAliveCount() {\n    return keepAliveCount_.load(std::memory_order_acquire);\n  }\n\n  std::future<void> destroy();\n  void destroyImpl() noexcept;\n\n  using LoopCallbackList = EventBase::LoopCallback::List;\n\n  KeepAlive<EventBase> evb_;\n\n  std::atomic<size_t> keepAliveCount_{1};\n  std::promise<void> destroyPromise_;\n  std::future<void> destroyFuture_{destroyPromise_.get_future()};\n  KeepAlive<VirtualEventBase> loopKeepAlive_{\n      makeKeepAlive<VirtualEventBase>(this)};\n\n  Synchronized<EventBase::OnDestructionCallback::List> onDestructionCallbacks_;\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/WriteChainAsyncTransportWrapper.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/io/async/DecoratedAsyncTransportWrapper.h>\n\nnamespace folly {\n\n/**\n * Helper class that redirects write() and writev() calls to writeChain().\n */\ntemplate <class T>\nclass WriteChainAsyncTransportWrapper\n    : public DecoratedAsyncTransportWrapper<T> {\n public:\n  using DecoratedAsyncTransportWrapper<T>::DecoratedAsyncTransportWrapper;\n\n  void write(\n      AsyncTransport::WriteCallback* callback,\n      const void* buf,\n      size_t bytes,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) override {\n    auto ioBuf = folly::IOBuf::wrapBuffer(buf, bytes);\n    writeChain(callback, std::move(ioBuf), flags);\n  }\n\n  void writev(\n      AsyncTransport::WriteCallback* callback,\n      const iovec* vec,\n      size_t count,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) override {\n    auto writeBuffer = folly::IOBuf::wrapIov(vec, count);\n    writeChain(callback, std::move(writeBuffer), flags);\n  }\n\n  /**\n   * It only makes sense to use this class if you override writeChain, so force\n   * derived classes to do that.\n   */\n  void writeChain(\n      AsyncTransport::WriteCallback* callback,\n      std::unique_ptr<folly::IOBuf>&& buf,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) override = 0;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/WriteFlags.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n\nnamespace folly {\n\n/*\n * flags given by the application for write* calls\n */\nenum class WriteFlags : uint32_t {\n  NONE = 0x00,\n  /*\n   * Whether to delay the output until a subsequent non-corked write.\n   * (Note: may not be supported in all subclasses or on all platforms.)\n   */\n  CORK = 0x01,\n  /*\n   * Set MSG_EOR flag when writing the last byte of the buffer to the socket.\n   *\n   * EOR tracking may need to be enabled to ensure that the MSG_EOR flag is only\n   * set when the final byte is being written.\n   *\n   *  - If the MSG_EOR flag is set, it is marked in the corresponding\n   *    tcp_skb_cb; this can be useful when debugging.\n   *  - The kernel uses it to decide whether socket buffers can be collapsed\n   *    together (see tcp_skb_can_collapse_to).\n   */\n  EOR = 0x02,\n  /*\n   * this indicates that only the write side of socket should be shutdown\n   */\n  WRITE_SHUTDOWN = 0x04,\n  /*\n   * use msg zerocopy if allowed\n   */\n  WRITE_MSG_ZEROCOPY = 0x08,\n  /*\n   * Request timestamp when entire buffer transmitted by the NIC.\n   *\n   * How timestamping is performed is implementation specific and may rely on\n   * software or hardware timestamps\n   */\n  TIMESTAMP_TX = 0x10,\n  /*\n   * Request timestamp when entire buffer ACKed by remote endpoint.\n   *\n   * How timestamping is performed is implementation specific and may rely on\n   * software or hardware timestamps\n   */\n  TIMESTAMP_ACK = 0x20,\n  /*\n   * Request timestamp when entire buffer has entered packet scheduler.\n   */\n  TIMESTAMP_SCHED = 0x40,\n  /*\n   * Request timestamp when entire buffer has been written to system socket.\n   */\n  TIMESTAMP_WRITE = 0x80,\n};\n\n/*\n * union operator\n */\nconstexpr WriteFlags operator|(WriteFlags a, WriteFlags b) {\n  return static_cast<WriteFlags>(\n      static_cast<uint32_t>(a) | static_cast<uint32_t>(b));\n}\n\n/*\n * compound assignment union operator\n */\nconstexpr WriteFlags& operator|=(WriteFlags& a, WriteFlags b) {\n  a = a | b;\n  return a;\n}\n\n/*\n * intersection operator\n */\nconstexpr WriteFlags operator&(WriteFlags a, WriteFlags b) {\n  return static_cast<WriteFlags>(\n      static_cast<uint32_t>(a) & static_cast<uint32_t>(b));\n}\n\n/*\n * compound assignment intersection operator\n */\nconstexpr WriteFlags& operator&=(WriteFlags& a, WriteFlags b) {\n  a = a & b;\n  return a;\n}\n\n/*\n * exclusion parameter\n */\nconstexpr WriteFlags operator~(WriteFlags a) {\n  return static_cast<WriteFlags>(~static_cast<uint32_t>(a));\n}\n\n/*\n * unset operator\n */\nconstexpr WriteFlags unSet(WriteFlags a, WriteFlags b) {\n  return a & ~b;\n}\n\n/*\n * inclusion operator\n */\nconstexpr bool isSet(WriteFlags a, WriteFlags b) {\n  return (a & b) == b;\n}\n\n/**\n * Write flags that are related to timestamping.\n */\nconstexpr WriteFlags kWriteFlagsForTimestamping = WriteFlags::TIMESTAMP_SCHED |\n    WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/fdsock/AsyncFdSocket.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <limits>\n#include <fmt/core.h>\n\n#include \"AsyncFdSocket.h\"\n\nnamespace folly {\n\nAsyncFdSocket::AsyncFdSocket(EventBase* evb)\n    : AsyncSocket(evb)\n#if !defined(_WIN32)\n      ,\n      readAncillaryDataCob_(this) {\n  setUpCallbacks();\n}\n#else\n{\n}\n#endif\n\nAsyncFdSocket::AsyncFdSocket(\n    EventBase* evb,\n    const folly::SocketAddress& address,\n    uint32_t connectTimeout)\n    : AsyncFdSocket(evb) {\n  connect(nullptr, address, connectTimeout);\n}\n\nAsyncFdSocket::AsyncFdSocket(\n    EventBase* evb, NetworkSocket fd, const folly::SocketAddress* peerAddress)\n    : AsyncSocket(evb, fd, /* zeroCopyBufId */ 0, peerAddress)\n#if !defined(_WIN32)\n      ,\n      readAncillaryDataCob_(this) {\n  setUpCallbacks();\n}\n#else\n{\n}\n#endif\n\nAsyncFdSocket::AsyncFdSocket(\n    AsyncFdSocket::DoesNotMoveFdSocketState, AsyncSocket* sock)\n    : AsyncSocket(sock)\n#if !defined(_WIN32)\n      ,\n      readAncillaryDataCob_(this) {\n  setUpCallbacks();\n}\n#else\n{\n}\n#endif\n\nAsyncFdSocket::AsyncFdSocket(\n    AsyncFdSocket::DoesNotMoveFdSocketState tag, AsyncSocket::UniquePtr sock)\n    : AsyncFdSocket(tag, sock.get()) {}\n\nvoid AsyncFdSocket::writeChainWithFds(\n    WriteCallback* callback,\n    std::unique_ptr<folly::IOBuf> buf,\n    SocketFds socketFds,\n    WriteFlags flags) {\n#if defined(_WIN32)\n  DCHECK(socketFds.empty()) << \"AsyncFdSocket cannot send FDs on Windows\";\n#else\n  DCHECK_EQ(&sendMsgCob_, getSendMsgParamsCB());\n\n  // All these `failWrite` scenarios destroy the FDs and block socket writes.\n  if (!socketFds.empty()) {\n    if (buf->empty()) {\n      DestructorGuard dg(this);\n      AsyncSocketException ex(\n          AsyncSocketException::BAD_ARGS,\n          withAddr(\"Cannot send FDs without at least 1 data byte\"));\n      return failWrite(__func__, callback, 0, ex);\n    }\n\n    auto maybeFdsAndSeqNum = socketFds.releaseToSendAndSeqNum();\n    if (!maybeFdsAndSeqNum) {\n      DestructorGuard dg(this);\n      AsyncSocketException ex(\n          AsyncSocketException::BAD_ARGS,\n          withAddr(\"Cannot send `SocketFds` that is in `Received` state\"));\n      return failWrite(__func__, callback, 0, ex);\n    }\n    auto& fds = maybeFdsAndSeqNum->first;\n    const auto fdsSeqNum = maybeFdsAndSeqNum->second;\n\n    if (fdsSeqNum == SocketFds::kNoSeqNum) {\n      DestructorGuard dg(this);\n      AsyncSocketException ex(\n          AsyncSocketException::BAD_ARGS,\n          withAddr(\"Sequence number must be set to send FDs\"));\n      return failWrite(__func__, callback, 0, ex);\n    }\n\n    if (fdsSeqNum != sentFdsSeqNum_) {\n      DestructorGuard dg(this);\n      AsyncSocketException ex(\n          AsyncSocketException::BAD_ARGS,\n          withAddr(\n              fmt::format(\n                  \"SeqNum of FDs did not match that of socket: {} vs {}\",\n                  fdsSeqNum,\n                  sentFdsSeqNum_)));\n      return failWrite(__func__, callback, 0, ex);\n    }\n    sentFdsSeqNum_ = detail::addSocketFdsSeqNum(sentFdsSeqNum_, fds.size());\n    // No DCHECK_GE(allocatedToSendFdsSeqNum_, sentFdsSeqNum_), since it can\n    // theoretically happen that \"allocated\" wraps around before \"sent\".\n\n    if (!sendMsgCob_.registerFdsForWriteTag(\n            WriteRequestTag{buf.get()}, std::move(fds))) {\n      // Careful: this has no unittest coverage because I don't have a good\n      // idea for how to cause this in a meaningful way.  Should this be\n      // a DCHECK? Plans that don't work:\n      //  - Creating two `unique_ptr` from the same raw pointer would be\n      //    bad news bears for the test.\n      //  - IOBuf recycling via getReleaseIOBufCallback() shouldn't be\n      //    possible either, since we unregister the tag in our `releaseIOBuf`\n      //    override below before releasing the IOBuf.\n      DestructorGuard dg(this);\n      AsyncSocketException ex(\n          AsyncSocketException::BAD_ARGS,\n          withAddr(\"Buffer was already owned by this socket\"));\n      return failWrite(__func__, callback, 0, ex);\n    }\n  }\n\n#endif // !Windows\n\n  writeChain(callback, std::move(buf), flags);\n}\n\n// The callbacks aren't defined on Windows -- the CMSG macros don't compile\n#if !defined(_WIN32)\n\nvoid AsyncFdSocket::setUpCallbacks() noexcept {\n  AsyncSocket::setSendMsgParamCB(&sendMsgCob_);\n  AsyncSocket::setReadAncillaryDataCB(&readAncillaryDataCob_);\n}\n\nvoid AsyncFdSocket::swapFdReadStateWith(AsyncFdSocket* other) {\n  // We don't need these write-state assertions to correctly swap read\n  // state, but since the only use-case is `moveToPlaintext`, they help.\n  DCHECK_EQ(0, other->allocatedToSendFdsSeqNum_);\n  DCHECK_EQ(0, other->sentFdsSeqNum_);\n  DCHECK_EQ(0, other->sendMsgCob_.writeTagToFds_.size());\n\n  fdsQueue_.swap(other->fdsQueue_);\n  std::swap(receivedFdsSeqNum_, other->receivedFdsSeqNum_);\n  // Do NOT swap `readAncillaryDataCob_` since its internal members are not\n  // \"state\", but plumbing that does not change.\n}\n\nvoid AsyncFdSocket::releaseIOBuf(\n    std::unique_ptr<folly::IOBuf> buf, ReleaseIOBufCallback* callback) {\n  sendMsgCob_.destroyFdsForWriteTag(WriteRequestTag{buf.get()});\n  AsyncSocket::releaseIOBuf(std::move(buf), callback);\n}\n\nstd::pair<\n    size_t,\n    AsyncFdSocket::FdSendMsgParamsCallback::WriteTagToFds::iterator>\nAsyncFdSocket::FdSendMsgParamsCallback::getCmsgSizeAndFds(\n    const AsyncSocket::WriteRequestTag& writeTag) noexcept {\n  auto it = writeTagToFds_.find(writeTag);\n  if (it == writeTagToFds_.end()) {\n    return std::make_pair(0, it);\n  }\n  return std::make_pair(CMSG_SPACE(sizeof(int) * it->second.size()), it);\n}\n\nvoid AsyncFdSocket::FdSendMsgParamsCallback::getAncillaryData(\n    folly::WriteFlags,\n    void* data,\n    const WriteRequestTag& writeTag,\n    const bool /*byteEventsEnabled*/) noexcept {\n  auto [cmsgSpace, fdsIt] = getCmsgSizeAndFds(writeTag);\n  CHECK_NE(0, cmsgSpace);\n  const auto& fds = fdsIt->second;\n\n  // NOT checking `fds.size() < SCM_MAX_FD` here because there's no way to\n  // propagate the error to the caller, and ultimately the write will fail\n  // out with EINVAL anyway.  The front-end that accepts FDs should check\n  // this instead.  If there is a debuggability issue, do add a LOG(ERROR).\n\n  ::msghdr msg{}; // Discarded, we just need to populate `data`\n\n  msg.msg_control = data;\n  msg.msg_controllen = cmsgSpace;\n  struct ::cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n  CHECK_NOTNULL(cmsg);\n\n  cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());\n  cmsg->cmsg_level = SOL_SOCKET;\n  cmsg->cmsg_type = SCM_RIGHTS;\n\n  auto* outFds = reinterpret_cast<int*>(CMSG_DATA(cmsg));\n  for (size_t n = 0; n < fds.size(); ++n) {\n    outFds[n] = fds[n]->fd();\n  }\n\n  VLOG(4) << \"this=\" << this << \", getAncillaryData() sending \" << fds.size()\n          << \" FDs\";\n}\n\nuint32_t AsyncFdSocket::FdSendMsgParamsCallback::getAncillaryDataSize(\n    folly::WriteFlags,\n    const WriteRequestTag& writeTag,\n    const bool /*byteEventsEnabled*/) noexcept {\n  // Not checking thread because `getAncillaryData` will follow.\n\n  // Due to the unfortunate design of the callback API, this will run\n  // again in `getAncillaryData`, let's pray for CPU caches here.\n  auto [size, _] = getCmsgSizeAndFds(writeTag);\n  return size;\n}\n\nvoid AsyncFdSocket::FdSendMsgParamsCallback::wroteBytes(\n    const WriteRequestTag& writeTag) noexcept {\n  auto nh = writeTagToFds_.extract(writeTag);\n  if (nh.empty()) {\n    // `AsyncSocket` will only call `wroteBytes` if `getAncillaryDataSize`\n    // returned a nonzero value, so we'll never get here.\n    LOG(DFATAL) << \"wroteBytes without a matching `getAncillaryData`?\";\n  } else {\n    VLOG(5) << \"this=\" << this << \", FdSendMsgParamsCallback::wroteBytes() on \"\n            << nh.mapped().size() << \" FDs for tag \" << writeTag;\n  }\n}\n\nbool AsyncFdSocket::FdSendMsgParamsCallback::registerFdsForWriteTag(\n    WriteRequestTag writeTag, SocketFds::ToSend&& fds) {\n  VLOG(5) << \"this=\" << this << \", registerFdsForWriteTag() on \" << fds.size()\n          << \" FDs for tag \" << writeTag;\n  if (writeTag.empty()) {\n    return false; // no insertion, error: we require a nonempty tag\n  }\n  auto [it, inserted] = writeTagToFds_.try_emplace(writeTag, std::move(fds));\n  return inserted;\n}\n\nvoid AsyncFdSocket::FdSendMsgParamsCallback::destroyFdsForWriteTag(\n    WriteRequestTag writeTag) noexcept {\n  auto nh = writeTagToFds_.extract(writeTag);\n  if (nh.empty()) {\n    return; // Not every write has FDs; also, `wroteBytes` clears them.\n  }\n  VLOG(5) << \"this=\" << this << \", destroyFdsForWriteTag() on \"\n          << nh.mapped().size() << \" FDs for tag \" << writeTag;\n}\n\nnamespace {\n\n// Logs and returns `false` on error.\nbool receiveFdsFromCMSG(\n    const struct ::cmsghdr& cmsg, std::vector<folly::File>* fds) noexcept {\n  if (cmsg.cmsg_len < CMSG_LEN(sizeof(int))) {\n    LOG(ERROR) << \"Got truncated SCM_RIGHTS message: length=\" << cmsg.cmsg_len;\n    return false;\n  }\n  const size_t dataLength = cmsg.cmsg_len - CMSG_LEN(0);\n\n  const size_t numFDs = dataLength / sizeof(int);\n  if ((dataLength % sizeof(int)) != 0) {\n    LOG(ERROR) << \"Non-integer number of file descriptors: size=\" << dataLength;\n    return false;\n  }\n\n  const auto* data = reinterpret_cast<const int*>(CMSG_DATA(&cmsg));\n  for (size_t n = 0; n < numFDs; ++n) {\n    auto fd = data[n];\n\n// On Linux, `AsyncSocket` sets `MSG_CMSG_CLOEXEC` for us.\n#if !defined(__linux__)\n    int flags = ::fcntl(fd, F_GETFD);\n    // On error, \"fail open\" by leaving the FD unmodified.\n    if (FOLLY_UNLIKELY(flags == -1)) {\n      PLOG(ERROR) << \"FdReadAncillaryDataCallback F_GETFD\";\n    } else if (FOLLY_UNLIKELY(-1 == ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC))) {\n      PLOG(ERROR) << \"FdReadAncillaryDataCallback F_SETFD\";\n    }\n#endif // !Linux\n\n    fds->emplace_back(fd, /*owns_fd*/ true);\n  }\n\n  return true;\n}\n\n// Logs and returns `false` on error.\nbool receiveFds(struct ::msghdr& msg, std::vector<folly::File>* fds) noexcept {\n  struct ::cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n  while (cmsg) {\n    if (cmsg->cmsg_level != SOL_SOCKET) {\n      LOG(ERROR) << \"Unexpected cmsg_level=\" << cmsg->cmsg_level;\n      return false;\n    } else if (cmsg->cmsg_type != SCM_RIGHTS) {\n      LOG(ERROR) << \"Unexpected cmsg_type=\" << cmsg->cmsg_type;\n      return false;\n    } else {\n      if (!receiveFdsFromCMSG(*cmsg, fds)) {\n        return false;\n      }\n    }\n    cmsg = CMSG_NXTHDR(&msg, cmsg);\n  }\n  return true;\n}\n\n} // namespace\n\nvoid AsyncFdSocket::enqueueFdsFromAncillaryData(struct ::msghdr& msg) noexcept {\n  eventBase_->dcheckIsInEventBaseThread();\n\n  if (msg.msg_flags & MSG_CTRUNC) {\n    AsyncSocketException ex(\n        AsyncSocketException::INTERNAL_ERROR,\n        \"Got MSG_CTRUNC because the `AsyncFdSocket` buffer was too small\");\n    AsyncSocket::failRead(__func__, ex);\n    return;\n  }\n\n  std::vector<folly::File> receivedFds;\n  if (!receiveFds(msg, &receivedFds)) {\n    AsyncSocketException ex(\n        AsyncSocketException::INTERNAL_ERROR,\n        \"Failed to read FDs from msghdr.msg_control\");\n    AsyncSocket::failRead(__func__, ex);\n    return;\n  }\n\n  // Don't waste queue space with empty FD lists since we match FDs only to\n  // requests that claim to include FDs.\n  if (receivedFds.empty()) {\n    return;\n  }\n\n  const auto seqNum = receivedFdsSeqNum_;\n  receivedFdsSeqNum_ =\n      detail::addSocketFdsSeqNum(receivedFdsSeqNum_, receivedFds.size());\n  SocketFds fds{std::move(receivedFds)};\n  fds.setFdSocketSeqNumOnce(seqNum);\n\n  VLOG(4) << \"this=\" << this << \", enqueueFdsFromAncillaryData() got \"\n          << fds.size() << \" FDs with seq num \" << seqNum\n          << \", prev queue size \" << fdsQueue_.size();\n\n  fdsQueue_.emplace(std::move(fds));\n}\n\n#endif // !Windows\n\nSocketFds AsyncFdSocket::popNextReceivedFds() {\n#if defined(_WIN32)\n  return SocketFds{};\n#else\n  eventBase_->dcheckIsInEventBaseThread();\n\n  DCHECK_EQ(&readAncillaryDataCob_, getReadAncillaryDataCallback());\n  if (fdsQueue_.empty()) {\n    return SocketFds{};\n  }\n  auto fds = std::move(fdsQueue_.front());\n  fdsQueue_.pop();\n  return fds;\n#endif // !Windows\n}\n\nSocketFds::SeqNum AsyncFdSocket::injectSocketSeqNumIntoFdsToSend(\n    SocketFds* fds) {\n#if defined(_WIN32)\n  return SocketFds::kNoSeqNum;\n#else\n  if (FOLLY_UNLIKELY(fds->empty())) {\n    LOG(DFATAL) << \"Cannot inject sequence number into empty SocketFDs\";\n    return SocketFds::kNoSeqNum;\n  }\n  eventBase_->dcheckIsInEventBaseThread();\n  const auto fdsSeqNum = allocatedToSendFdsSeqNum_;\n  fds->dcheckToSendOrEmpty().setFdSocketSeqNumOnce(fdsSeqNum);\n  allocatedToSendFdsSeqNum_ =\n      detail::addSocketFdsSeqNum(allocatedToSendFdsSeqNum_, fds->size());\n  return fdsSeqNum;\n#endif // !Windows\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/fdsock/AsyncFdSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/fdsock/SocketFds.h>\n#include <folly/portability/GTestProd.h>\n\nnamespace folly {\n\n/**\n * Intended for use with Unix sockets. Unlike regular `AsyncSocket`:\n *  - Can send FDs via `writeChainWithFds` using socket ancillary data (see\n *    `man cmsg`).\n *  - Whenever handling regular reads, concurrently attempts to receive FDs\n *    included in incoming ancillary data.  Groups of received FDs are\n *    enqueued to be retrieved via `popNextReceivedFds`.\n *  - The \"read ancillary data\" and \"sendmsg params\" callbacks are built-in\n *    are NOT customizable.\n *\n * Implementation limitation: Unlike regular `AsyncSocket`, this currently\n * does not automatically send socket timestamping events.  This could\n * easily be added, but seems of limited utility for Unix sockets.\n */\nclass AsyncFdSocket : public AsyncSocket {\n public:\n  using UniquePtr = std::unique_ptr<AsyncSocket, Destructor>;\n\n  /**\n   * Create a new unconnected AsyncSocket.\n   *\n   * connect() must later be called on this socket to establish a connection.\n   */\n  explicit AsyncFdSocket(EventBase* evb);\n\n  /**\n   * Create a new AsyncSocket and begin the connection process.\n   *\n   * Unlike `AsyncSocket`, lacks `useZeroCopy` since Unix sockets do not\n   * support zero-copy.\n   */\n  AsyncFdSocket(\n      EventBase* evb,\n      const folly::SocketAddress& address,\n      uint32_t connectTimeout = 0);\n\n  /**\n   * Create a AsyncSocket from an already connected socket file descriptor.\n   *\n   * Unlike `AsyncSocket`, lacks `zeroCopyBufId` since Unix sockets do not\n   * support zero-copy.\n   */\n  AsyncFdSocket(\n      EventBase* evb,\n      NetworkSocket fd,\n      const folly::SocketAddress* peerAddress = nullptr);\n\n  /**\n   * EXPERIMENTAL / TEMPORARY: These move-like constructors should not be\n   * used to go from one AsyncFdSocket to another because this will not\n   * correctly preserve read & write state.  Full move is not implemented\n   * since its trickier, and was not yet needed -- see `swapFdReadStateWith`.\n   */\n  struct DoesNotMoveFdSocketState {};\n\n protected:\n  FOLLY_GTEST_FRIEND_TEST(AsyncFdSocketSequenceRoundtripTest, WithDataSize);\n  // Protected since it's easy to accidentally pass an `AsyncFdSocket` here,\n  // a scenario that's extremely easy to use incorrectly.\n  AsyncFdSocket(DoesNotMoveFdSocketState, AsyncSocket*);\n\n public:\n  AsyncFdSocket(DoesNotMoveFdSocketState, AsyncSocket::UniquePtr);\n\n  /**\n   * `AsyncSocket::writeChain` analog that passes FDs as ancillary data over\n   * the socket (see `man cmsg`).\n   *\n   * Before writing the FDs, call `injectSocketSeqNumIntoFdsToSend()` on the\n   * supplied `SocketFds` to commit them to be sent to this socket, in this\n   * order.  That is -- for a given socket, the order of calling \"inject\" on\n   * FDs must exactly match the socket write order.\n   *\n   * Invariants:\n   *  - Max FDs per IOBuf: `SCM_MAX_FD` from include/net/scm.h, 253 for\n   *    effectively all of Linux history.\n   *  - FDs are received no earlier than the first data byte of the IOBuf,\n   *    and no later than the last data byte. More specifically:\n   *     - The currently implemented behavior is that FDs arrive precisely\n   *       with the first data byte.  This was efficient and good enough for\n   *       Thrift Rocket transport, where each message knows whether it\n   *       expects FDs, and FDs are sent with the last data fragment of each\n   *       message.\n   *     - In other conceivable designs, it could be useful to pass FDs\n   *       with the last data byte instead, which could be implemented as an\n   *       optional write flag. In Thrift Rocket, this would minimize the\n   *       buffering of FDs by the receiver, at the cost of more syscalls.\n   */\n  virtual void writeChainWithFds(\n      WriteCallback*,\n      std::unique_ptr<folly::IOBuf>,\n      SocketFds,\n      WriteFlags flags = WriteFlags::NONE);\n\n  /**\n   * This socket will look for file descriptors in the ancillary data (`man\n   * cmsg`) of each incoming read.\n   *   - FDs are kept in an internal queue, to be retrieved via this call.\n   *   - FDs are marked with FD_CLOEXEC upon receipt, although on non-Linux\n   *     platforms there is a short race window before this happens.\n   *   - Empty lists of FDs (0 FDs with this message) are not stored in the\n   *     queue.\n   *   - Returns an empty `SocketFds` if the queue is empty.\n   *\n   * IMPORTANT: The returned `SocketFds` will have a populated\n   * `getFdSocketSeqNum` matching the sending `AsyncFdSocket`.  The code\n   * that consumes these FDs should check that the corresponding \"data\n   * message\" includes the same sequence number, as a safeguard against code\n   * bugs that cause messages to be paired with the wrong FDs.\n   */\n  SocketFds popNextReceivedFds();\n\n  /**\n   * Must be called on each `fds` before `writeChainWithFds`.  Embeds the\n   * socket-internal sequence number in fds, and increments the counter.\n   *\n   * Return: Non-negative, but may be `SocketFds::kNoSeqNum` on DFATAL errors.\n   */\n  SocketFds::SeqNum injectSocketSeqNumIntoFdsToSend(SocketFds* fds);\n\n  void setSendMsgParamCB(SendMsgParamsCallback*) override {\n    LOG(DFATAL) << \"AsyncFdSocket::setSendMsgParamCB is forbidden\";\n  }\n  void setReadAncillaryDataCB(ReadAncillaryDataCallback*) override {\n    LOG(DFATAL) << \"AsyncFdSocket::setReadAncillaryDataCB is forbidden\";\n  }\n\n// This class has no ancillary data callbacks on Windows, they wouldn't compile\n#if !defined(_WIN32)\n  /**\n   * EXPERIMENTAL / TEMPORARY: This just does what is required for\n   * `moveToPlaintext` to support StopTLS.  That use-case could later be\n   * covered by full move-construct or move-assign support, but both would\n   * be more complex to support.\n   *\n   * Swaps \"read FDs\" state (receive queue & sequence numbers) with `other`.\n   * DFATALs if `other` had any \"write FDs\" state.\n   */\n  void swapFdReadStateWith(AsyncFdSocket* other);\n\n protected:\n  void releaseIOBuf(\n      std::unique_ptr<folly::IOBuf>, ReleaseIOBufCallback*) override;\n\n private:\n  class FdSendMsgParamsCallback : public SendMsgParamsCallback {\n   public:\n    void getAncillaryData(\n        folly::WriteFlags,\n        void* data,\n        const WriteRequestTag&,\n        const bool byteEventsEnabled) noexcept override;\n\n    uint32_t getAncillaryDataSize(\n        folly::WriteFlags,\n        const WriteRequestTag&,\n        const bool byteEventsEnabled) noexcept override;\n\n    void wroteBytes(const WriteRequestTag&) noexcept override;\n\n   protected:\n    friend class AsyncFdSocket;\n\n    // Returns true on success, false if the tag was already registered, or\n    // has a null IOBuf* (both are usage error).  The FDs are discarded on\n    // error.\n    bool registerFdsForWriteTag(WriteRequestTag, SocketFds::ToSend&&);\n\n    // Called from `releaseIOBuf()` above, once we're sure that an IOBuf\n    // will not be used for a write any more.  This is a good time to\n    // discard the FDs associated with this write.\n    //\n    // CAREFUL: This may be invoked for IOBufs that never had a\n    // corresponding `getAncillaryData*` call.\n    void destroyFdsForWriteTag(WriteRequestTag) noexcept;\n\n   private:\n    using WriteTagToFds =\n        std::unordered_map<WriteRequestTag, SocketFds::ToSend>;\n\n    std::pair<size_t, WriteTagToFds::iterator> getCmsgSizeAndFds(\n        const WriteRequestTag& writeTag) noexcept;\n\n    WriteTagToFds writeTagToFds_;\n  };\n\n  class FdReadAncillaryDataCallback : public ReadAncillaryDataCallback {\n   public:\n    explicit FdReadAncillaryDataCallback(AsyncFdSocket* socket)\n        : socket_(socket) {}\n\n    folly::Expected<folly::Unit, AsyncSocketException> ancillaryData(\n        struct ::msghdr& msg) noexcept override {\n      socket_->enqueueFdsFromAncillaryData(msg);\n      return folly::unit;\n    }\n\n    folly::MutableByteRange getAncillaryDataCtrlBuffer() noexcept override {\n      // Not checking thread because `ancillaryData()` will follow.\n      return folly::MutableByteRange(ancillaryDataCtrlBuffer_);\n    }\n\n   private:\n    // Max number of fds in a single `sendmsg` / `recvmsg` message\n    // Defined as SCM_MAX_FD in linux/include/net/scm.h\n    static constexpr size_t kMaxFdsPerSocketMsg{253};\n\n    AsyncFdSocket* socket_;\n    std::array<uint8_t, CMSG_SPACE(sizeof(int) * kMaxFdsPerSocketMsg)>\n        ancillaryDataCtrlBuffer_;\n  };\n\n  friend class FdReadAncillaryDataCallback;\n\n  void enqueueFdsFromAncillaryData(struct ::msghdr& msg) noexcept;\n\n  void setUpCallbacks() noexcept;\n\n  FdSendMsgParamsCallback sendMsgCob_;\n  std::queue<SocketFds> fdsQueue_; // must outlive readAncillaryDataCob_\n  FdReadAncillaryDataCallback readAncillaryDataCob_;\n\n  SocketFds::SeqNum allocatedToSendFdsSeqNum_{0};\n  SocketFds::SeqNum sentFdsSeqNum_{0};\n  SocketFds::SeqNum receivedFdsSeqNum_{0};\n#endif // !Windows\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/fdsock/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"async_fd_socket\",\n    srcs = [\"AsyncFdSocket.cpp\"],\n    headers = [\"AsyncFdSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n    ],\n    exported_deps = [\n        \":socket_fds\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/portability:gtest_prod\",\n    ],\n)\n\n### this line is a hint for source control merge\n\nfb_dirsync_cpp_library(\n    name = \"socket_fds\",\n    srcs = [\"SocketFds.cpp\"],\n    headers = [\"SocketFds.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:file\",\n    ],\n)\n"
  },
  {
    "path": "folly/io/async/fdsock/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async_fd_socket\n  SRCS\n    AsyncFdSocket.cpp\n  HEADERS\n    AsyncFdSocket.h\n  EXPORTED_DEPS\n    folly_io_async_async_socket\n    folly_io_async_fdsock_socket_fds\n    folly_portability_gtest_prod\n)\n\nfolly_add_library(\n  NAME socket_fds\n  SRCS\n    SocketFds.cpp\n  HEADERS\n    SocketFds.h\n  EXPORTED_DEPS\n    folly_file\n)\n"
  },
  {
    "path": "folly/io/async/fdsock/SocketFds.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"SocketFds.h\"\n\nnamespace folly {\n\nvoid SocketFds::cloneToSendFromOrDfatal(const SocketFds& other) {\n  if (other.empty()) {\n    ptr_.reset();\n  } else {\n    auto* fds = std::get_if<ToSendPair>(other.ptr_.get());\n    if (FOLLY_UNLIKELY(fds == nullptr)) {\n      LOG(DFATAL) << \"SocketFds was in 'received' state, not cloning\";\n      ptr_.reset();\n    } else {\n      // Cloning is only for \"multi-publisher\" scenarios.  In these cases\n      // we must first clone, and then bind a socket sequence number.\n      DCHECK_EQ(kNoSeqNum, fds->second)\n          << \"Cannot clone SocketFds once it was bound to a socket\";\n      ptr_ = std::make_unique<FdsVariant>(*fds);\n    }\n  }\n}\n\nSocketFds::Received SocketFds::releaseReceived() {\n  auto fds =\n      std::move(CHECK_NOTNULL(std::get_if<ReceivedPair>(ptr_.get()))->first);\n  // NB: In the case of a Thrift server handler method that is receiving\n  // and then sending back FDs using the same `SocketFds` object, this\n  // deallocation (and subsequent allocation) could be avoided, e.g. by:\n  //  - Without changing the API by having an additional `std::monostate`\n  //    representing the variant being empty. This has the downside of\n  //    holding on to allocations unnecessarily in other cases.\n  //  - By adding `std::pair<Received, ToSend&> releaseReceivedAndSend()`.\n  //    This complicates the user experience.\n  ptr_.reset();\n  return fds;\n}\n\nstd::optional<SocketFds::ToSendPair> SocketFds::releaseToSendAndSeqNum() {\n  auto* fdsPtr = std::get_if<ToSendPair>(ptr_.get());\n  if (FOLLY_UNLIKELY(fdsPtr == nullptr)) {\n    // This can \"legitimately\" happen if a client wrongly sends FDs to a\n    // server method that is not expecting them. Then, `THeader::fds`\n    // in a Thrift request-response handler will retain the received\n    // FDs by the time a response\n    if (ptr_) {\n      LOG(WARNING) << \"releaseToSendAndSeqNum discarded received FDs\";\n      ptr_.reset();\n    }\n    return std::nullopt;\n  }\n  auto fdsAndSeqNum = std::move(*fdsPtr);\n  ptr_.reset();\n  return fdsAndSeqNum;\n}\n\nvoid SocketFds::setFdSocketSeqNumOnce(SeqNum seqNum) {\n  // The type is unsigned because Thrift IDL only supports signed.\n  DCHECK_GE(seqNum, 0) << \"Sequence number must be nonnegative\";\n  if (FOLLY_LIKELY(ptr_ != nullptr)) {\n    std::visit(\n        [seqNum](auto&& v) {\n          DCHECK_EQ(kNoSeqNum, v.second) << \"Can only set sequence number once\";\n          v.second = seqNum;\n        },\n        *ptr_);\n  } else {\n    LOG(DFATAL) << \"Cannot set sequence number on empty SocketFds\";\n  }\n}\n\nSocketFds::SeqNum SocketFds::getFdSocketSeqNum() const {\n  if (FOLLY_LIKELY(ptr_ != nullptr)) {\n    auto seqNum = std::visit([](auto&& v) { return v.second; }, *ptr_);\n    if (seqNum >= 0) {\n      return seqNum;\n    }\n    if (seqNum != kNoSeqNum) {\n      LOG(DFATAL) << \"Sequence number in invalid state: \" << seqNum;\n    }\n  } else {\n    LOG(DFATAL) << \"Cannot query sequence number of empty SocketFds\";\n  }\n  return kNoSeqNum;\n}\n\nSocketFds::SeqNum detail::addSocketFdsSeqNum(\n    SocketFds::SeqNum a, SocketFds::SeqNum b) noexcept {\n  if (a < 0 || b < 0) {\n    LOG(DFATAL) << \"Inputs must be nonnegative, got \" << a << \" + \" << b;\n    return SocketFds::kNoSeqNum;\n  }\n  const auto gap = std::numeric_limits<SocketFds::SeqNum>::max() - a;\n  if (FOLLY_LIKELY(b <= gap)) {\n    return a + b;\n  }\n  return b - gap - 1; // wrap around through 0, modulo max\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/fdsock/SocketFds.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <variant>\n#include <glog/logging.h>\n\n#include <folly/File.h>\n\nnamespace folly {\n\n/**\n * Represents an ordered collection of file descriptors. This union type\n * either contains:\n *  - FDs to be sent on a socket -- with shared ownership, since the sender\n *    may still need them, OR\n *  - FDs just received, with sole ownership.\n *\n * This hides the variant of containers behind a `unique_ptr` so that the\n * normal / fast path, which is not passing FDs, is as light as possible,\n * adding just 8 bytes for a pointer.\n *\n * == Rationale ==\n *\n * In order to send FDs over Unix sockets, Thrift plumbs them through a\n * variety of classes.  Many of these can be used both for send & receive\n * operations (THeader, StreamPayload, etc).\n *\n * This is especially useful in regular Thrift request-response handler\n * methods, where the same THeader is used for consuming the received FDs,\n * and sending back FDs with the response -- necessarily in that order.\n */\nclass SocketFds final {\n public:\n  using Received = std::vector<folly::File>;\n  // These `shared_ptr`s are commonly be shared across threads -- and there\n  // is no locking -- so `const` ensures thread-safety.  Therefore, if\n  // you're going to put your `File` into a `ToSend`, it's best to first\n  // make your own copy `const`, too.\n  using ToSend = std::vector<std::shared_ptr<const folly::File>>;\n\n  // Must be signed because Thrift lacks unsigned ints, for RpcMetadata.thrift\n  using SeqNum = int64_t;\n  // FD sequence numbers are nonnegative.  This represents \"none was set\";\n  // also used for some DFATAL errors.\n  static constexpr SeqNum kNoSeqNum = -1;\n\n private:\n  using ReceivedPair = std::pair<Received, SeqNum>;\n  using ToSendPair = std::pair<ToSend, SeqNum>;\n  using FdsVariant = std::variant<ReceivedPair, ToSendPair>;\n\n public:\n  SocketFds() = default;\n  SocketFds(SocketFds&& other) = default;\n  template <class T>\n  explicit SocketFds(T fds) {\n    // Representation invariant: when `SocketFds.empty()`, there's no backing\n    // allocation.\n    if (fds.size() > 0) {\n      ptr_ = std::make_unique<FdsVariant>(\n          std::make_pair(std::move(fds), kNoSeqNum));\n    }\n  }\n\n  SocketFds& operator=(SocketFds&& other) = default;\n\n  bool empty() const { return !ptr_; }\n\n  size_t size() const {\n    if (!ptr_) {\n      return 0;\n    }\n    return std::visit([](auto&& v) { return v.first.size(); }, *ptr_);\n  }\n\n  // These methods help ensure the right kind of data is being plumbed or\n  // overwritten:\n  //   fds.dcheckEmpty() = otherFds.dcheck{Received,ToSend}();\n  SocketFds& dcheckEmpty() {\n    DCHECK(!ptr_);\n    return *this;\n  }\n  SocketFds& dcheckReceivedOrEmpty() {\n    DCHECK(!ptr_ || std::holds_alternative<ReceivedPair>(*ptr_));\n    return *this;\n  }\n  SocketFds& dcheckToSendOrEmpty() {\n    DCHECK(!ptr_ || std::holds_alternative<ToSendPair>(*ptr_));\n    return *this;\n  }\n\n  // Since ownership of `ToSend` FDs is shared, it is permissible to clone\n  // `SocketFds` that are known to be in this state.\n  //  - Invariant: `other` must be `ToSend`.\n  //  - Cost: Cloning copies a vector into a new heap allocation.\n  void cloneToSendFromOrDfatal(const SocketFds& other);\n\n  Received releaseReceived(); // Fatals if `this` is not `Received`\n  // Returns `nullopt` if `this` is not `ToSend`\n  std::optional<ToSendPair> releaseToSendAndSeqNum();\n\n  // Socket FDs need to be associated with socket data messages. Doing so\n  // correctly through the many layers of Folly & Thrift can be tricky --\n  // certain code bugs could cause the order in which FDs are sent to\n  // deviate from the order in which they are popped off the socket queue\n  // by the receiver.\n  //\n  // Operating on the wrong FD can lead to data loss or data corruption, so\n  // in order to detect such bugs, we include FD sequence numbers in the\n  // metadata of each message.\n  //\n  // The semantics of these methods are like so:\n  //  - A brand new `SocketFds`, or one that has been `release*`d has\n  //    no sequence number -- i.e. `kNoSeqNum`.\n  //  - A nonnegative sequence number can be attached to a `SocketFds`\n  //    that has none, to be obtained via `AsyncFdSocket`.\n  //  - It is a DFATAL error to replace one sequence number by another.\n  void setFdSocketSeqNumOnce(SeqNum seqNum);\n  SeqNum getFdSocketSeqNum() const; // Non-negative, or `kNoSeqNum`\n\n private:\n  std::unique_ptr<FdsVariant> ptr_;\n};\n\nnamespace detail {\n\n// Overflow on signed ints is UB, while this explicitly wraps MAX -> 0.\n// E.g. addSocketFdsSeqNum(MAX - 1, 3) == 1.\nSocketFds::SeqNum addSocketFdsSeqNum(\n    SocketFds::SeqNum, SocketFds::SeqNum) noexcept;\n\n} // namespace detail\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/fdsock/test/AsyncFdSocketTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/fdsock/AsyncFdSocket.h>\n#include <folly/io/async/test/AsyncSocketTest.h>\n#include <folly/portability/GMock.h> // for matchers like HasSubstr\n#include <folly/portability/GTest.h>\n\nnamespace folly { // required for FRIEND_TEST to work\n\n// `AsyncFdSocket` is just a stub on Windows because its CMSG macros are busted\n#if !defined(_WIN32)\n\nstd::string getFdProcMagic(int fd) {\n  char magic[PATH_MAX];\n  PCHECK(\n      -1 !=\n      ::readlink(\n          fmt::format(\"/proc/{}/fd/{}\", getpid(), fd).c_str(),\n          magic,\n          PATH_MAX));\n  return std::string(magic);\n}\n\nvoid checkFdsMatch(\n    const SocketFds::ToSend& sFds,\n    SocketFds::SeqNum sendSeqNum,\n    folly::SocketFds received) {\n  EXPECT_EQ(\n      sendSeqNum,\n      received.empty() ? SocketFds::kNoSeqNum : received.getFdSocketSeqNum());\n  auto rFds =\n      received.empty() ? SocketFds::Received{} : received.releaseReceived();\n  EXPECT_EQ(sFds.size(), rFds.size());\n  for (size_t i = 0; i < rFds.size(); ++i) {\n    const auto& sfd = sFds[i]->fd();\n    const auto& rfd = rFds[i].fd();\n\n    // We should always receive with FD_CLOEXEC enabled\n    int flags = ::fcntl(rfd, F_GETFD);\n    PCHECK(-1 != flags);\n    EXPECT_TRUE(flags & FD_CLOEXEC) << \"Actual flags:\" << flags;\n\n    // Check that the two FDs appear identical in `/proc/PID/fd/FD`\n    CHECK_EQ(getFdProcMagic(sfd), getFdProcMagic(rfd));\n  }\n}\n\nfolly::SocketFds::ToSend makeFdsToSend(size_t n) {\n  folly::SocketFds::ToSend sendFds;\n  while (sendFds.size() != n) {\n    std::array<int, 2> rawSendFds;\n    // Don't set FD_CLOEXEC here so it is extra-clear that the socket did it\n    // (even though FD_CLOEXEC on this FD isn't coupled with its copy).\n    PCHECK(0 == fileops::pipe(rawSendFds.data()));\n\n    for (int fd : rawSendFds) {\n      auto f = std::make_shared<folly::File>(fd, /*ownsFd*/ true);\n      if (sendFds.size() < n) { // handle odd `n`\n        sendFds.emplace_back(std::move(f));\n      }\n    }\n  }\n  CHECK_EQ(n, sendFds.size());\n  return sendFds;\n}\n\nstruct AsyncFdSocketTest : public testing::Test {\n  AsyncFdSocketTest()\n      : AsyncFdSocketTest{[]() {\n          std::array<NetworkSocket, 2> fds;\n          PCHECK(0 == netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds.data()));\n          for (int i = 0; i < 2; ++i) {\n            PCHECK(0 == netops::set_socket_non_blocking(fds[i])) << i;\n          }\n          return fds;\n        }()} {}\n\n  explicit AsyncFdSocketTest(std::array<NetworkSocket, 2> fds)\n      : sendSock_{&evb_, fds[0]},\n        recvSock_(std::make_unique<AsyncFdSocket>(&evb_, fds[1])) {\n    recvSock_->setReadCB(&rcb_);\n  }\n\n  EventBase evb_;\n\n  test::WriteCallback wcb_;\n  AsyncFdSocket sendSock_;\n\n  test::ReadCallback rcb_; // NB: `~AsyncSocket` calls `rcb.readEOF`\n  std::unique_ptr<AsyncFdSocket> recvSock_;\n};\n\nTEST_F(AsyncFdSocketTest, TestAddSeqNum) {\n  EXPECT_EQ(17, detail::addSocketFdsSeqNum(0, 17));\n  EXPECT_EQ(17, detail::addSocketFdsSeqNum(17, 0));\n  EXPECT_EQ(17, detail::addSocketFdsSeqNum(8, 9));\n\n  EXPECT_EQ(\n      9223372036854775807, detail::addSocketFdsSeqNum(9223372036854775805, 2));\n  EXPECT_EQ(\n      9223372036854775807, detail::addSocketFdsSeqNum(2, 9223372036854775805));\n  EXPECT_EQ(0, detail::addSocketFdsSeqNum(9223372036854775805, 3));\n  EXPECT_EQ(0, detail::addSocketFdsSeqNum(3, 9223372036854775805));\n  EXPECT_EQ(1, detail::addSocketFdsSeqNum(9223372036854775805, 4));\n  EXPECT_EQ(1, detail::addSocketFdsSeqNum(4, 9223372036854775805));\n\n  EXPECT_EQ(\n      9223372036854775807, detail::addSocketFdsSeqNum(9223372036854775806, 1));\n  EXPECT_EQ(\n      9223372036854775807, detail::addSocketFdsSeqNum(1, 9223372036854775806));\n  EXPECT_EQ(3, detail::addSocketFdsSeqNum(9223372036854775806, 5));\n  EXPECT_EQ(3, detail::addSocketFdsSeqNum(5, 9223372036854775806));\n\n  EXPECT_EQ(\n      9223372036854775807, detail::addSocketFdsSeqNum(9223372036854775807, 0));\n  EXPECT_EQ(\n      9223372036854775807, detail::addSocketFdsSeqNum(0, 9223372036854775807));\n  EXPECT_EQ(0, detail::addSocketFdsSeqNum(9223372036854775807, 1));\n  EXPECT_EQ(0, detail::addSocketFdsSeqNum(1, 9223372036854775807));\n  EXPECT_EQ(3, detail::addSocketFdsSeqNum(9223372036854775807, 4));\n  EXPECT_EQ(3, detail::addSocketFdsSeqNum(4, 9223372036854775807));\n}\n\nTEST_F(AsyncFdSocketTest, FailNoData) {\n  SocketFds fds(makeFdsToSend(1));\n  sendSock_.injectSocketSeqNumIntoFdsToSend(&fds);\n  sendSock_.writeChainWithFds(&wcb_, IOBuf::create(0), std::move(fds));\n  EXPECT_THAT(wcb_.exception.what(), testing::HasSubstr(\"least 1 data byte\"));\n}\n\nTEST_F(AsyncFdSocketTest, FailSendReceivedFds) {\n  char data = 'a'; // Need >= 1 data byte to send ancillary data.\n  SocketFds::Received receivedFds;\n  receivedFds.emplace_back(0, /*owns*/ false);\n  SocketFds fds{std::move(receivedFds)};\n  sendSock_.writeChainWithFds(\n      &wcb_, IOBuf::wrapBuffer(&data, sizeof(data)), std::move(fds));\n  EXPECT_THAT(wcb_.exception.what(), testing::HasSubstr(\"in `Received` state\"));\n}\n\nTEST_F(AsyncFdSocketTest, FailTooManyFds) {\n  char data = 'a'; // Need >= 1 data byte to send ancillary data.\n  SocketFds fds(makeFdsToSend(254));\n  sendSock_.injectSocketSeqNumIntoFdsToSend(&fds);\n  sendSock_.writeChainWithFds(\n      &wcb_, IOBuf::wrapBuffer(&data, sizeof(data)), std::move(fds));\n  EXPECT_EQ(EINVAL, wcb_.exception.getErrno());\n}\n\nstruct AsyncFdSocketSimpleRoundtripTest\n    : public AsyncFdSocketTest,\n      public testing::WithParamInterface<int> {};\n\nTEST_P(AsyncFdSocketSimpleRoundtripTest, WithNumFds) {\n  int numFds = GetParam();\n  char data = 'a'; // Need >= 1 data byte to send ancillary data.\n\n  auto sendFds = makeFdsToSend(numFds);\n\n  SocketFds fds(sendFds);\n  const auto sendSeqNum = fds.empty()\n      ? SocketFds::kNoSeqNum\n      : sendSock_.injectSocketSeqNumIntoFdsToSend(&fds);\n  sendSock_.writeChainWithFds(\n      &wcb_, IOBuf::wrapBuffer(&data, sizeof(data)), std::move(fds));\n  evb_.loopOnce();\n\n  rcb_.verifyData(&data, sizeof(data));\n  rcb_.clearData();\n\n  checkFdsMatch(sendFds, sendSeqNum, recvSock_->popNextReceivedFds());\n}\n\n// Round-trip & verify various numbers of FDs with 1 byte of data.\nINSTANTIATE_TEST_SUITE_P(\n    VaryFdCount,\n    AsyncFdSocketSimpleRoundtripTest,\n    testing::Values(1, 0, 2, 253, 0, 3)); // 253 is the Linux max\n\nstruct WriteCountedAsyncFdSocket : public AsyncFdSocket {\n  using AsyncFdSocket::AsyncFdSocket;\n  AsyncSocket::WriteResult sendSocketMessage(\n      const iovec* vec,\n      size_t count,\n      WriteFlags flags,\n      WriteRequestTag writeTag) override {\n    ++numWrites_;\n    return AsyncSocket::sendSocketMessage(vec, count, flags, writeTag);\n  }\n  size_t numWrites_{0};\n};\n\n// This test exists because without `FdSendMsgParamsCallback::wroteBytes`,\n// we would be sending the same FDs repeatedly, whenever the data to be\n// written didn't fit in the `sendmsg` buffer and got sent in several\n// batches.\nTEST_F(AsyncFdSocketTest, MultiPartSend) {\n  auto sendFds = makeFdsToSend(1);\n\n  WriteCountedAsyncFdSocket sendSock{&evb_, sendSock_.detachNetworkSocket()};\n\n  // Find the socket's \"send\" buffer size\n  socklen_t sendBufSize;\n  socklen_t sizeOfSendBufSize = sizeof(sendBufSize);\n  PCHECK(\n      0 ==\n      netops::getsockopt(\n          sendSock.getNetworkSocket(),\n          SOL_SOCKET,\n          SO_SNDBUF,\n          &sendBufSize,\n          &sizeOfSendBufSize));\n  ASSERT_EQ(sizeof(sendBufSize), sizeOfSendBufSize);\n\n  // Make the message too big for one `sendmsg`.\n  int numSendParts = 3;\n  std::string data(numSendParts * sendBufSize, 'x');\n  SocketFds fds(sendFds);\n  const auto sendSeqNum = sendSock.injectSocketSeqNumIntoFdsToSend(&fds);\n  sendSock.writeChainWithFds(\n      &wcb_, IOBuf::wrapBuffer(data.data(), data.size()), std::move(fds));\n\n  // FDs are sent with the first send & received by the first receive\n  evb_.loopOnce();\n  checkFdsMatch(sendFds, sendSeqNum, recvSock_->popNextReceivedFds());\n  EXPECT_EQ(1, sendSock.numWrites_);\n\n  // Receive the rest of the data.\n  while (rcb_.dataRead() < data.size()) {\n    evb_.loopOnce();\n  }\n  rcb_.verifyData(data.data(), data.size());\n  rcb_.clearData();\n  EXPECT_EQ(numSendParts, sendSock.numWrites_);\n\n  // There are no more data or FDs\n  evb_.loopOnce(EVLOOP_NONBLOCK);\n  EXPECT_EQ(0, rcb_.dataRead()) << \"Leftover reads\";\n  EXPECT_TRUE(recvSock_->popNextReceivedFds().empty()) << \"Extra FDs\";\n}\n\nstruct AsyncFdSocketSequenceRoundtripTest\n    : public AsyncFdSocketTest,\n      public testing::WithParamInterface<std::tuple<bool, int>> {};\n\nTEST_P(AsyncFdSocketSequenceRoundtripTest, WithDataSize) {\n  auto [swapSocket, dataSize] = GetParam();\n\n  // The default `ReadCallback` has special-snowflake buffer management\n  // that's annoying for this test.  Secondarily, this exercises the\n  // \"ReadVec\" path.\n  test::ReadvCallback rcb(128, 3);\n  // Avoid `readEOF` use-after-stack-scope in `~AsyncSocket`.\n  SCOPE_EXIT {\n    recvSock_->setReadCB(nullptr);\n  };\n  recvSock_->setReadCB(&rcb);\n\n  std::queue<\n      std::tuple<int, std::string, folly::SocketFds::ToSend, SocketFds::SeqNum>>\n      sentQueue;\n\n  // Enqueue several writes before attempting reads\n  char msgFirstByte = 0; // The first byte of each data message is different.\n  for (auto numFds : {0, 1, 0, 0, 2, 253, 253, 0, 5}) {\n    // All but the first bytes are 255, while the first byte is a counter.\n    std::string data(dataSize, '\\377');\n    CHECK_LT(msgFirstByte, 254); // Don't collide with 255, don't overflow\n    data[0] = ++msgFirstByte;\n\n    auto sendFds = makeFdsToSend(numFds);\n\n    SocketFds fds(sendFds);\n    const auto sendSeqNum = fds.empty()\n        ? SocketFds::kNoSeqNum\n        : sendSock_.injectSocketSeqNumIntoFdsToSend(&fds);\n    sendSock_.writeChainWithFds(\n        &wcb_, IOBuf::wrapBuffer(data.data(), data.size()), std::move(fds));\n\n    sentQueue.emplace(\n        msgFirstByte, std::move(data), std::move(sendFds), sendSeqNum);\n  }\n\n  // Read from the socket, and check that any batches of FDs arrive with the\n  // first byte of the matching data.\n  bool checkedFds{false};\n  // The max expected steps is ~3k: 1234567 / (3 * 128)\n  for (int i = 0; i < 10000 && !sentQueue.empty(); ++i) {\n    evb_.loopOnce(EVLOOP_NONBLOCK);\n    // Validate that \"move from AsyncSocket\" and \"swap read state\" interrupt\n    // neither the reading of data nor of FDs.\n    if (swapSocket) {\n      AsyncFdSocket prevReadStateSock{nullptr};\n      prevReadStateSock.swapFdReadStateWith(recvSock_.get());\n\n      // Test moving the non-FD parts of the socket, while reading.\n      struct EnableMakeUnique : public AsyncFdSocket {\n        EnableMakeUnique(AsyncSocket* sock)\n            : AsyncFdSocket(AsyncFdSocket::DoesNotMoveFdSocketState{}, sock) {}\n      };\n      recvSock_ = std::make_unique<EnableMakeUnique>(recvSock_.get());\n      recvSock_->setReadCB(&rcb);\n\n      // Test moving the FD read state.\n      recvSock_->swapFdReadStateWith(&prevReadStateSock);\n    }\n    size_t dataRead = rcb.buf_->computeChainDataLength();\n    if (!dataRead) {\n      continue;\n    }\n\n    if (!checkedFds) {\n      checkedFds = true;\n\n      // Ensure that FDs arrive with the first byte of the associated data\n      ASSERT_EQ(std::get<0>(sentQueue.front()), rcb.buf_->data()[0]);\n\n      const auto& sendFds = std::get<2>(sentQueue.front());\n      // We only want to \"pop\" if the current `sentQueue` element had sent\n      // FDs.  Otherwise, we would inadvertently retrieve the next message's\n      // FDs, which would fail in `checkFdsMatch`.\n      if (!sendFds.empty()) {\n        const auto sendSeqNum = std::get<3>(sentQueue.front());\n        checkFdsMatch(sendFds, sendSeqNum, recvSock_->popNextReceivedFds());\n      }\n    }\n\n    // Move on to the next data chunk\n    if (dataRead >= std::get<1>(sentQueue.front()).size()) {\n      ASSERT_TRUE(checkedFds);\n      checkedFds = false;\n\n      auto [firstByte, expectedData, _fds, _seqNum] = sentQueue.front();\n      auto& buf = rcb.buf_;\n\n      // Check that the entire data string is as expected.\n      ASSERT_EQ(firstByte, buf->data()[0]);\n      buf->coalesce();\n      ASSERT_GE(buf->length(), expectedData.size());\n      ASSERT_EQ(\n          0, memcmp(expectedData.data(), buf->data(), expectedData.size()));\n      buf->trimStart(expectedData.size());\n\n      sentQueue.pop();\n    }\n  }\n  EXPECT_TRUE(sentQueue.empty()) << \"Stuck reading?\";\n  evb_.loopOnce(EVLOOP_NONBLOCK);\n  EXPECT_EQ(0, rcb.buf_->computeChainDataLength()) << \"Leftover reads\";\n  EXPECT_TRUE(recvSock_->popNextReceivedFds().empty()) << \"Extra FDs\";\n}\n\n// Vary the data size to (hopefully) get a variety of chunking behaviors.\nINSTANTIATE_TEST_SUITE_P(\n    VaryDataSize,\n    AsyncFdSocketSequenceRoundtripTest,\n    testing::Combine(\n        testing::Values(false, true),\n        testing::Values(1, 12, 123, 1234, 12345, 123456, 1234567)),\n    [](const auto& info) {\n      return fmt::format(\n          \"{}{}\",\n          std::get<0>(info.param) ? \"SwapSocket_\" : \"\",\n          std::get<1>(info.param));\n    });\n\n#endif // !Windows\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/fdsock/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"../../../../defs.bzl\", \"folly_xplat_cxx_test\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_fd_socket_test\",\n    srcs = [\"AsyncFdSocketTest.cpp\"],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly/io/async/fdsock:async_fd_socket\",\n        \"//xplat/folly/io/async/test:async_socket_test_lib\",\n    ],\n)\n\n# !!!! fbcode/folly/io/async/fdsock/test/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_fd_socket_test\",\n    srcs = [\"AsyncFdSocketTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/io/async/fdsock:async_fd_socket\",\n        \"//folly/io/async/test:async_socket_test_lib\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n"
  },
  {
    "path": "folly/io/async/observer/AsyncSocketObserverContainer.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/ObserverContainer.h>\n#include <folly/io/async/observer/AsyncSocketObserverInterface.h>\n\nnamespace folly {\n\nclass AsyncSocket;\n\nusing AsyncSocketObserverContainerBaseT = folly::ObserverContainer<\n    AsyncSocketObserverInterface,\n    AsyncSocket,\n    folly::ObserverContainerBasePolicyDefault<\n        AsyncSocketObserverInterface::Events /* EventEnum */,\n        32 /* BitsetSize (max number of interface events) */>>;\n\nclass AsyncSocketObserverContainer : public AsyncSocketObserverContainerBaseT {\n  using AsyncSocketObserverContainerBaseT::AsyncSocketObserverContainerBaseT;\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/observer/AsyncSocketObserverInterface.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <folly/CppAttributes.h>\n#include <folly/GLog.h>\n#include <folly/Optional.h>\n#include <folly/io/async/WriteFlags.h>\n\nnamespace folly {\n\nclass AsyncSocketException;\nclass EventBase;\nclass AsyncTransport;\nclass AsyncSocket;\n\n/**\n * Observer of socket events.\n */\nclass AsyncSocketObserverInterface {\n public:\n  enum class Events {};\n  AsyncSocketObserverInterface() = default;\n  virtual ~AsyncSocketObserverInterface() = default;\n\n  /**\n   * Information provided to observer during prewrite event.\n   *\n   * Based on this information, an observer can build a PrewriteRequest.\n   */\n  struct PrewriteState {\n    // raw byte stream offsets\n    size_t startOffset{0};\n    size_t endOffset{0};\n\n    // flags already set\n    WriteFlags writeFlags{WriteFlags::NONE};\n\n    // timestamp recorded at the AsyncSocket layer\n    //\n    // supports sequencing of PrewriteState events and ByteEvents for debug\n    std::chrono::steady_clock::time_point ts = {\n        std::chrono::steady_clock::now()};\n  };\n\n  /**\n   * Request that can be generated by observer in response to prewrite event.\n   *\n   * An observer can use a PrewriteRequest to request WriteFlags to be added\n   * to a write and/or to request that the write be split up, both of which\n   * can be used for timestamping.\n   */\n  struct PrewriteRequest {\n    // offset to split write at; may be split at earlier offset by another req\n    folly::Optional<size_t> maybeOffsetToSplitWrite;\n\n    // write flags to be added if write split at requested offset\n    WriteFlags writeFlagsToAddAtOffset{WriteFlags::NONE};\n\n    // write flags to be added regardless of where write happens\n    WriteFlags writeFlagsToAdd{WriteFlags::NONE};\n  };\n\n  /**\n   * Container of PrewriteRequests passed on invocation of prewrite().\n   *\n   * If an observer wants to make a PrewriteRequest, it adds its request to this\n   * container. Each added request is merged into a PrewriteRequest that can be\n   * fetched via getMergedRequest().\n   */\n  class PrewriteRequestContainer {\n   public:\n    explicit PrewriteRequestContainer(\n        const AsyncSocketObserverInterface::PrewriteState& prewriteState)\n        : prewriteState_(prewriteState) {}\n\n    /**\n     * Add a PrewriteRequest to the container.\n     */\n    void addRequest(\n        const AsyncSocketObserverInterface::PrewriteRequest& request) {\n      mergedRequest_.writeFlagsToAdd |= request.writeFlagsToAdd;\n      if (request.maybeOffsetToSplitWrite.has_value()) {\n        CHECK_GE(\n            prewriteState_.endOffset, request.maybeOffsetToSplitWrite.value());\n        if (\n            // case 1: offset not set in merged request\n            !mergedRequest_.maybeOffsetToSplitWrite.has_value() ||\n            // case 2: offset in merged request > offset in current request\n            mergedRequest_.maybeOffsetToSplitWrite >\n                request.maybeOffsetToSplitWrite) {\n          mergedRequest_.maybeOffsetToSplitWrite =\n              request.maybeOffsetToSplitWrite; // update\n          mergedRequest_.writeFlagsToAddAtOffset =\n              request.writeFlagsToAddAtOffset; // reset\n        } else if (\n            // case 3: offset in merged request == offset in current request\n            request.maybeOffsetToSplitWrite ==\n            mergedRequest_.maybeOffsetToSplitWrite) {\n          mergedRequest_.writeFlagsToAddAtOffset |=\n              request.writeFlagsToAddAtOffset; // merge\n        }\n        // case 4: offset in merged request < offset in current request\n        // (do nothing)\n      }\n\n      // if maybeOffsetToSplitWrite points to end of the vector, remove the\n      // split\n      if (mergedRequest_.maybeOffsetToSplitWrite.has_value() && // explicit\n          mergedRequest_.maybeOffsetToSplitWrite == prewriteState_.endOffset) {\n        mergedRequest_.maybeOffsetToSplitWrite.reset(); // no split needed\n      }\n    }\n\n    /**\n     * Returns the merged PrewriteREquest representing action to take.\n     *\n     * The merged request has the split point at the earliest offset requested\n     * across all requests, and has flags set to be the union of all timestamp\n     * flags requested across all requests.\n     *\n     * Examples:\n     *\n     * - If there are two PrewriteRequests, one requesting we split on byte\n     *   offset 20, and the other requesting a split on byte offset 30, then we\n     *   will split on offset 20, as this is the earlier of the offsets.\n     *\n     * - If there are two PrewriteRequests, one requesting that we add the TX\n     *   and ACK flags, and the other requesting just the SCHED flag, then we\n     *   will add the TX, ACK, and SCHED flags to the request.\n     */\n    [[nodiscard]] const PrewriteRequest& getMergedRequest() const {\n      return mergedRequest_;\n    }\n\n   private:\n    const PrewriteState& prewriteState_;\n    PrewriteRequest mergedRequest_;\n  };\n\n  /**\n   * Structure used to communicate ByteEvents, such as TX and ACK timestamps.\n   */\n  struct ByteEvent {\n    // types of events; start from 0 to enable indexing in arrays\n    enum Type : uint8_t {\n      WRITE = 0,\n      SCHED = 1,\n      TX = 2,\n      ACK = 3,\n    };\n    // type\n    Type type;\n\n    // offset of corresponding byte in raw byte stream\n    size_t offset{0};\n\n    // socket timestamp, as recorded by AsyncSocket implementation\n    std::chrono::steady_clock::time_point ts = {\n        std::chrono::steady_clock::now()};\n\n    // kernel software timestamp for non-WRITE; for Linux this is CLOCK_REALTIME\n    // see https://www.kernel.org/doc/Documentation/networking/timestamping.txt\n    folly::Optional<std::chrono::nanoseconds> maybeSoftwareTs;\n\n    // hardware timestamp for non-WRITE events; see kernel documentation\n    // see https://www.kernel.org/doc/Documentation/networking/timestamping.txt\n    folly::Optional<std::chrono::nanoseconds> maybeHardwareTs;\n\n    // for WRITE events, the number of raw bytes written to the socket\n    // optional to prevent accidental misuse in other event types\n    folly::Optional<size_t> maybeRawBytesWritten;\n\n    // for WRITE events, the number of raw bytes we tried to write to the socket\n    // optional to prevent accidental misuse in other event types\n    folly::Optional<size_t> maybeRawBytesTriedToWrite;\n\n    // for WRITE ByteEvents, additional WriteFlags passed\n    // optional to prevent accidental misuse in other event types\n    folly::Optional<WriteFlags> maybeWriteFlags;\n\n    /**\n     * For WRITE events, returns if SCHED timestamp requested.\n     */\n    [[nodiscard]] bool schedTimestampRequestedOnWrite() const {\n      CHECK_EQ(Type::WRITE, type);\n      CHECK(maybeWriteFlags.has_value());\n      return isSet(*maybeWriteFlags, WriteFlags::TIMESTAMP_SCHED);\n    }\n\n    /**\n     * For WRITE events, returns if TX timestamp requested.\n     */\n    [[nodiscard]] bool txTimestampRequestedOnWrite() const {\n      CHECK_EQ(Type::WRITE, type);\n      CHECK(maybeWriteFlags.has_value());\n      return isSet(*maybeWriteFlags, WriteFlags::TIMESTAMP_TX);\n    }\n\n    /**\n     * For WRITE events, returns if ACK timestamp requested.\n     */\n    [[nodiscard]] bool ackTimestampRequestedOnWrite() const {\n      CHECK_EQ(Type::WRITE, type);\n      CHECK(maybeWriteFlags.has_value());\n      return isSet(*maybeWriteFlags, WriteFlags::TIMESTAMP_ACK);\n    }\n  };\n\n  /**\n   * close() will be invoked when the socket is being closed.\n   *\n   * Can be called multiple times during shutdown / destruction for the same\n   * socket. Observers may detach after first call or track if event\n   * previously observed.\n   *\n   * @param socket   Socket being closed.\n   */\n  virtual void close(AsyncSocket* /* socket */) noexcept {}\n\n  /**\n   * connectAttempt() will be invoked when connect() is called.\n   *\n   * Triggered before any application connection callback.\n   *\n   * @param socket   Socket that attempts to connect.\n   */\n  virtual void connectAttempt(AsyncSocket* /* socket */) noexcept {}\n\n  /**\n   * connectSuccess() will be invoked when connect() returns successfully.\n   *\n   * Triggered before any application connection callback.\n   *\n   * @param socket   Socket that has connected.\n   */\n  virtual void connectSuccess(AsyncSocket* /* socket */) noexcept {}\n\n  /**\n   * connectError() will be invoked when connect() returns an error.\n   *\n   * Triggered before any application connection callback.\n   *\n   * @param socket      Socket that has connected.\n   * @param ex          Exception that describes why.\n   */\n  virtual void connectError(\n      AsyncSocket* /* socket */,\n      const AsyncSocketException& /* ex */) noexcept {}\n\n  /**\n   * Invoked when the socket is being attached to an EventBase.\n   *\n   * Called from within the EventBase thread being attached.\n   *\n   * @param socket      Socket with EventBase change.\n   * @param evb         The EventBase being attached.\n   */\n  virtual void evbAttach(AsyncSocket* /* socket */, EventBase* /* evb */) {}\n\n  /**\n   * Invoked when the socket is being detached from an EventBase.\n   *\n   * Called from within the EventBase thread being detached.\n   *\n   * @param socket      Socket with EventBase change.\n   * @param evb         The EventBase that is being detached.\n   */\n  virtual void evbDetach(AsyncSocket* /* socket */, EventBase* /* evb */) {}\n\n  /**\n   * Invoked each time a ByteEvent is available.\n   *\n   * Multiple ByteEvent may be generated for the same byte offset and event.\n   * For instance, kernel software and hardware TX timestamps for the same\n   * are delivered in separate CMsg, and thus will result in separate\n   * ByteEvent.\n   *\n   * @param socket      Socket that ByteEvent is available for.\n   * @param event       ByteEvent (WRITE, SCHED, TX, ACK).\n   */\n  virtual void byteEvent(\n      AsyncSocket* /* socket */, const ByteEvent& /* event */) noexcept {}\n\n  /**\n   * Invoked if ByteEvents are enabled.\n   *\n   * Only called if the observer's configuration requested ByteEvents. May\n   * be invoked multiple times if ByteEvent configuration changes (i.e., if\n   * ByteEvents are enabled without hardware timestamps, and then enabled\n   * with them).\n   *\n   * @param socket    Socket that ByteEvents are enabled for.\n   */\n  virtual void byteEventsEnabled(AsyncSocket* /* socket */) noexcept {}\n\n  /**\n   * Invoked if ByteEvents could not be enabled, or if an error occurred that\n   * will prevent further delivery of ByteEvents.\n   *\n   * An observer may be waiting to receive a ByteEvent, such as an ACK event\n   * confirming delivery of the last byte of a payload, before closing the\n   * socket. If the socket has become unhealthy then this ByteEvent may\n   * never occur, yet the handler may be unaware that the socket is\n   * unhealthy if reads have been shutdown and no writes are occurring; this\n   * observer signal breaks this 'deadlock'.\n   *\n   * @param socket      Socket that ByteEvents are now unavailable for.\n   * @param ex          Details on why ByteEvents are now unavailable.\n   */\n  virtual void byteEventsUnavailable(\n      AsyncSocket* /* socket */,\n      const AsyncSocketException& /* ex */) noexcept {}\n\n  /**\n   * Invoked before each write to the socket if prewrite support enabled.\n   *\n   * The observer receives information about the pending write in the\n   * PrewriteState and can request ByteEvents / socket timestamps by returning\n   * a PrewriteRequest. The request contains the offset to split the write at\n   * (if any) and WriteFlags to apply.\n   *\n   * PrewriteRequests are aggregated across observers. The write buffer is\n   * split at the lowest offset returned by all observers. Flags are applied\n   * based on configuration within the PrewriteRequest. Requests are not\n   * sticky and expire after each write.\n   *\n   * Fewer bytes may be written than indicated in the PrewriteState or in the\n   * PrewriteRequest split if the underlying socket / kernel\n   * blocks on write.\n   *\n   * @param socket      Socket that ByteEvents are now unavailable for.\n   * @param state       Pending write start and end offsets and flags.\n   * @param container   Container of PrewriteRequests that observer can add to.\n   */\n  virtual void prewrite(\n      AsyncSocket* /* transport */,\n      const PrewriteState& /* state */,\n      PrewriteRequestContainer& /* container */) {\n    folly::terminate_with<std::runtime_error>(\n        \"prewrite() called but not defined\");\n  }\n\n  /**\n   * fdDetach() is invoked if the socket file descriptor is detached.\n   *\n   * detachNetworkSocket() will be triggered when a new AsyncSocket is being\n   * constructed from an old one. See the moved() event for details about\n   * this special case.\n   *\n   * @param socket      Socket for which detachNetworkSocket was invoked.\n   */\n  virtual void fdDetach(AsyncSocket* /* socket */) noexcept {}\n\n  /**\n   * fdAttach() is invoked when the socket file descriptor is attached.\n   *\n   * @param socket      Socket for which handleNetworkSocketAttached was\n   * invoked.\n   */\n  virtual void fdAttach(AsyncSocket* /* socket */) noexcept {}\n\n  /**\n   * move() will be invoked when a new AsyncSocket is being constructed via\n   * constructor AsyncSocket(AsyncSocket* oldAsyncSocket) from an AsyncSocket\n   * that has an observer attached.\n   *\n   * This type of construction is common during TLS/SSL accept process.\n   * wangle::Acceptor may transform an AsyncSocket to an AsyncFizzServer, and\n   * then transform the AsyncFizzServer to an AsyncSSLSocket on fallback.\n   * AsyncFizzServer and AsyncSSLSocket derive from AsyncSocket and at each\n   * stage the aforementioned constructor will be called.\n   *\n   * Observers may be attached when the initial AsyncSocket is created, before\n   * TLS/SSL accept handling has completed. As a result, AsyncSocket must\n   * notify the observer during each transformation so that:\n   *   (1) The observer can track these transformations for debugging.\n   *   (2) The observer does not become separated from the underlying\n   *        operating system socket and corresponding file descriptor.\n   *\n   * When a new AsyncSocket is being constructed via the aforementioned\n   * constructor, the following observer events will be triggered:\n   *   (1) fdDetach\n   *   (2) move\n   *\n   * When move is triggered, the observer can CHOOSE to detach the old socket\n   * and attach to the new socket. This process will not happen automatically;\n   * the observer must explicitly perform these steps.\n   *\n   * @param oldSocket   Old socket that fd was detached from.\n   * @param newSocket   New socket being constructed with fd attached.\n   */\n  virtual void move(\n      AsyncSocket* /* oldSocket */, AsyncSocket* /* newSocket */) noexcept {}\n};\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/observer/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"async_socket_observer_interface\",\n    headers = [\"AsyncSocketObserverInterface.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:cpp_attributes\",\n        \"//folly:glog\",\n        \"//folly:optional\",\n        \"//folly/io/async:write_flags\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_socket_observer_container\",\n    headers = [\"AsyncSocketObserverContainer.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":async_socket_observer_interface\",\n        \"//folly:observer_container\",\n    ],\n)\n"
  },
  {
    "path": "folly/io/async/observer/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME async_socket_observer_container\n  HEADERS\n    AsyncSocketObserverContainer.h\n  EXPORTED_DEPS\n    folly_io_async_observer_async_socket_observer_interface\n    folly_observer_container\n)\n\nfolly_add_library(\n  NAME async_socket_observer_interface\n  HEADERS\n    AsyncSocketObserverInterface.h\n  EXPORTED_DEPS\n    folly_cpp_attributes\n    folly_glog\n    folly_io_async_write_flags\n    folly_optional\n)\n"
  },
  {
    "path": "folly/io/async/ssl/BUCK",
    "content": "load(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_cxx_library\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nfb_dirsync_cpp_library(\n    name = \"openssl\",\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/io/async/ssl:openssl_utils\",  # @manual\n        \"//folly/io/async/ssl:ssl_errors\",  # @manual\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"basic_transport_certificate\",\n    headers = [\"BasicTransportCertificate.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":openssl_transport_certificate\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"openssl_transport_certificate\",\n    headers = [\"OpenSSLTransportCertificate.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/io/async:async_transport_certificate\",\n        \"//folly/portability:openssl\",\n        \"//folly/ssl:openssl_ptr_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"openssl_utils\",\n    srcs = [\"OpenSSLUtils.cpp\"],\n    headers = [\"OpenSSLUtils.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:scope_guard\",\n        \"//folly/portability:unistd\",\n        \"//folly/ssl/detail:openssl_session\",\n    ],\n    exported_deps = [\n        \"//folly:range\",\n        \"//folly/io/async:async_socket_exception\",\n        \"//folly/net:network_socket\",\n        \"//folly/portability:openssl\",\n        \"//folly/portability:sockets\",\n        \"//folly/ssl:openssl_ptr_types\",\n        \"//folly/ssl:ssl_session\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"ssl_errors\",\n    srcs = [\"SSLErrors.cpp\"],\n    headers = [\"SSLErrors.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:range\",\n        \"//folly/portability:openssl\",\n    ],\n    exported_deps = [\n        \"//folly/io/async:async_socket_exception\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"tls_definitions\",\n    headers = [\"TLSDefinitions.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/io:iobuf\",\n    ],\n)\n"
  },
  {
    "path": "folly/io/async/ssl/BasicTransportCertificate.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/io/async/ssl/OpenSSLTransportCertificate.h>\n\nnamespace folly {\nnamespace ssl {\n\nclass BasicTransportCertificate : public folly::OpenSSLTransportCertificate {\n public:\n  // Create a basic transport cert from an existing one.  Returns nullptr\n  // if cert is null.\n  static std::unique_ptr<BasicTransportCertificate> create(\n      const folly::AsyncTransportCertificate* cert) {\n    if (!cert) {\n      return nullptr;\n    }\n    return std::make_unique<BasicTransportCertificate>(\n        cert->getIdentity(), OpenSSLTransportCertificate::tryExtractX509(cert));\n  }\n\n  BasicTransportCertificate(\n      std::string identity, folly::ssl::X509UniquePtr x509)\n      : identity_(std::move(identity)), x509_(std::move(x509)) {}\n\n  std::string getIdentity() const override { return identity_; }\n\n  folly::ssl::X509UniquePtr getX509() const override {\n    if (!x509_) {\n      return nullptr;\n    }\n    auto x509raw = x509_.get();\n    X509_up_ref(x509raw);\n    return folly::ssl::X509UniquePtr(x509raw);\n  }\n\n  void dropX509() { x509_.reset(); }\n\n private:\n  std::string identity_;\n  folly::ssl::X509UniquePtr x509_;\n};\n\n} // namespace ssl\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ssl/CMakeLists.txt",
    "content": "# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# @generated by folly/facebook/generate_cmake.py\n\nfolly_add_library(\n  NAME basic_transport_certificate\n  HEADERS\n    BasicTransportCertificate.h\n  EXPORTED_DEPS\n    folly_io_async_ssl_openssl_transport_certificate\n)\n\nfolly_add_library(\n  NAME openssl\n  EXPORTED_DEPS\n    folly_io_async_ssl_openssl_utils\n    folly_io_async_ssl_ssl_errors\n)\n\nfolly_add_library(\n  NAME openssl_transport_certificate\n  HEADERS\n    OpenSSLTransportCertificate.h\n  EXPORTED_DEPS\n    folly_io_async_async_transport_certificate\n    folly_portability_openssl\n    folly_ssl_openssl_ptr_types\n)\n\nfolly_add_library(\n  NAME openssl_utils\n  SRCS\n    OpenSSLUtils.cpp\n  HEADERS\n    OpenSSLUtils.h\n  DEPS\n    folly_portability_unistd\n    folly_scope_guard\n    folly_ssl_detail_openssl_session\n  EXPORTED_DEPS\n    folly_io_async_async_socket_exception\n    folly_net_network_socket\n    folly_portability_openssl\n    folly_portability_sockets\n    folly_range\n    folly_ssl_openssl_ptr_types\n    folly_ssl_ssl_session\n)\n\nfolly_add_library(\n  NAME ssl_errors\n  SRCS\n    SSLErrors.cpp\n  HEADERS\n    SSLErrors.h\n  DEPS\n    folly_portability_openssl\n    folly_range\n  EXPORTED_DEPS\n    folly_io_async_async_socket_exception\n)\n\nfolly_add_library(\n  NAME tls_definitions\n  HEADERS\n    TLSDefinitions.h\n  EXPORTED_DEPS\n    folly_io_iobuf\n)\n"
  },
  {
    "path": "folly/io/async/ssl/OpenSSLTransportCertificate.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncTransportCertificate.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/ssl/OpenSSLPtrTypes.h>\n\nnamespace folly {\n\n/**\n * Generic interface applications may implement to convey self or peer\n * certificate related information.\n */\nclass OpenSSLTransportCertificate : virtual public AsyncTransportCertificate {\n public:\n  virtual ~OpenSSLTransportCertificate() override = default;\n\n  /**\n   * Returns an X509 structure associated with this Certificate. This may be\n   * null.\n   */\n  virtual folly::ssl::X509UniquePtr getX509() const = 0;\n\n  virtual std::optional<std::string> getDER() const override {\n    auto x509 = getX509();\n    if (!x509) {\n      return std::nullopt;\n    }\n\n    int len = i2d_X509(x509.get(), nullptr);\n    if (len < 0) {\n      return std::nullopt;\n    }\n\n    std::string der(len, '\\0');\n    auto derPtr = reinterpret_cast<unsigned char*>(der.data());\n\n    if (i2d_X509(x509.get(), &derPtr) < 0) {\n      return std::nullopt;\n    }\n\n    return der;\n  }\n\n  static ssl::X509UniquePtr tryExtractX509(\n      const AsyncTransportCertificate* cert) {\n    auto opensslCert = dynamic_cast<const OpenSSLTransportCertificate*>(cert);\n    return opensslCert ? opensslCert->getX509() : nullptr;\n  }\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ssl/OpenSSLUtils.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/io/async/ssl/OpenSSLUtils.h>\n\n#include <unordered_map>\n\n#include <glog/logging.h>\n\n#include <folly/ScopeGuard.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Unistd.h>\n#include <folly/ssl/detail/OpenSSLSession.h>\n\nnamespace {\n#ifdef OPENSSL_IS_BORINGSSL\n// BoringSSL doesn't (as of May 2016) export the equivalent\n// of BIO_sock_should_retry, so this is one way around it :(\nstatic int boringssl_bio_fd_should_retry(int err);\n#endif\n\n} // namespace\n\nnamespace folly {\nnamespace ssl {\n\nbool OpenSSLUtils::getTLSMasterKey(\n    const SSL_SESSION* session, MutableByteRange keyOut) {\n  auto size = SSL_SESSION_get_master_key(session, nullptr, 0);\n  if (size == keyOut.size()) {\n    return SSL_SESSION_get_master_key(session, keyOut.begin(), keyOut.size());\n  }\n  return false;\n}\n\nbool OpenSSLUtils::getTLSMasterKey(\n    const std::shared_ptr<SSLSession> session, MutableByteRange keyOut) {\n  auto openSSLSession =\n      std::dynamic_pointer_cast<folly::ssl::detail::OpenSSLSession>(session);\n  if (openSSLSession) {\n    auto rawSessionPtr = openSSLSession->getActiveSession();\n    SSL_SESSION* rawSession = rawSessionPtr.get();\n    if (rawSession) {\n      return OpenSSLUtils::getTLSMasterKey(rawSession, keyOut);\n    }\n  }\n  return false;\n}\n\nbool OpenSSLUtils::getTLSClientRandom(\n    const SSL* ssl, MutableByteRange randomOut) {\n  auto size = SSL_get_client_random(ssl, nullptr, 0);\n  if (size == randomOut.size()) {\n    return SSL_get_client_random(ssl, randomOut.begin(), randomOut.size());\n  }\n  return false;\n}\n\nbool OpenSSLUtils::getPeerAddressFromX509StoreCtx(\n    X509_STORE_CTX* ctx, sockaddr_storage* addrStorage, socklen_t* addrLen) {\n  // Grab the ssl idx and then the ssl object so that we can get the peer\n  // name to compare against the ips in the subjectAltName\n  auto sslIdx = SSL_get_ex_data_X509_STORE_CTX_idx();\n  auto ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, sslIdx));\n  int fd = SSL_get_fd(ssl);\n  if (fd < 0) {\n    LOG(ERROR) << \"Inexplicably couldn't get fd from SSL\";\n    return false;\n  }\n\n  *addrLen = sizeof(*addrStorage);\n  if (getpeername(fd, reinterpret_cast<sockaddr*>(addrStorage), addrLen) != 0) {\n    PLOG(ERROR) << \"Unable to get peer name\";\n    return false;\n  }\n  CHECK(*addrLen <= sizeof(*addrStorage));\n  return true;\n}\n\nbool OpenSSLUtils::validatePeerCertNames(\n    X509* cert, const sockaddr* addr, socklen_t /* addrLen */) {\n  // Try to extract the names within the SAN extension from the certificate\n  auto altNames = reinterpret_cast<STACK_OF(GENERAL_NAME)*>(\n      X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr));\n  SCOPE_EXIT {\n    if (altNames != nullptr) {\n      sk_GENERAL_NAME_pop_free(altNames, GENERAL_NAME_free);\n    }\n  };\n  if (altNames == nullptr) {\n    LOG(WARNING) << \"No subjectAltName provided and we only support ip auth\";\n    return false;\n  }\n\n  const sockaddr_in* addr4 = nullptr;\n  const sockaddr_in6* addr6 = nullptr;\n  if (addr != nullptr) {\n    if (addr->sa_family == AF_INET) {\n      addr4 = reinterpret_cast<const sockaddr_in*>(addr);\n    } else if (addr->sa_family == AF_INET6) {\n      addr6 = reinterpret_cast<const sockaddr_in6*>(addr);\n    } else {\n      LOG(FATAL) << \"Unsupported sockaddr family: \" << addr->sa_family;\n    }\n  }\n\n  for (int i = 0; i < sk_GENERAL_NAME_num(altNames); i++) {\n    auto name = sk_GENERAL_NAME_value(altNames, i);\n    if ((addr4 != nullptr || addr6 != nullptr) && name->type == GEN_IPADD) {\n      // Extra const-ness for paranoia\n      unsigned char const* const rawIpStr = name->d.iPAddress->data;\n      auto const rawIpLen = size_t(name->d.iPAddress->length);\n\n      if (rawIpLen == 4 && addr4 != nullptr) {\n        if (::memcmp(rawIpStr, &addr4->sin_addr, rawIpLen) == 0) {\n          return true;\n        }\n      } else if (rawIpLen == 16 && addr6 != nullptr) {\n        if (::memcmp(rawIpStr, &addr6->sin6_addr, rawIpLen) == 0) {\n          return true;\n        }\n      } else if (rawIpLen != 4 && rawIpLen != 16) {\n        LOG(WARNING) << \"Unexpected IP length: \" << rawIpLen;\n      }\n    }\n  }\n\n  LOG(WARNING) << \"Unable to match client cert against alt name ip\";\n  return false;\n}\n\nstatic std::unordered_map<uint16_t, std::string> getOpenSSLCipherNames() {\n  std::unordered_map<uint16_t, std::string> ret;\n  SSL_CTX* ctx = nullptr;\n  SSL* ssl = nullptr;\n\n  const SSL_METHOD* meth = TLS_server_method();\n\n  if ((ctx = SSL_CTX_new(meth)) == nullptr) {\n    return ret;\n  }\n  SCOPE_EXIT {\n    SSL_CTX_free(ctx);\n  };\n\n  if ((ssl = SSL_new(ctx)) == nullptr) {\n    return ret;\n  }\n  SCOPE_EXIT {\n    SSL_free(ssl);\n  };\n\n  STACK_OF(SSL_CIPHER)* sk = SSL_get_ciphers(ssl);\n  for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++) {\n    const SSL_CIPHER* c = sk_SSL_CIPHER_value(sk, i);\n    unsigned long id = SSL_CIPHER_get_id(c);\n    // OpenSSL 1.0.2 and prior does weird things such as stuff the SSL/TLS\n    // version into the top 16 bits. Let's ignore those for now. This is\n    // BoringSSL compatible (their id can be cast as uint16_t)\n    uint16_t cipherCode = id & 0xffffUL;\n    ret[cipherCode] = SSL_CIPHER_get_name(c);\n  }\n  return ret;\n}\n\nconst std::string& OpenSSLUtils::getCipherName(uint16_t cipherCode) {\n  // Having this in a hash map saves the binary search inside OpenSSL\n  static std::unordered_map<uint16_t, std::string> cipherCodeToName(\n      getOpenSSLCipherNames());\n\n  const auto& iter = cipherCodeToName.find(cipherCode);\n  if (iter != cipherCodeToName.end()) {\n    return iter->second;\n  } else {\n    static std::string empty;\n    return empty;\n  }\n}\n\nvoid OpenSSLUtils::setSSLInitialCtx(SSL* ssl, SSL_CTX* ctx) {\n  (void)ssl;\n  (void)ctx;\n}\n\nSSL_CTX* OpenSSLUtils::getSSLInitialCtx(SSL* ssl) {\n  (void)ssl;\n  return nullptr;\n}\n\nBioMethodUniquePtr OpenSSLUtils::newSocketBioMethod() {\n  BIO_METHOD* newmeth = nullptr;\n  if (!(newmeth = BIO_meth_new(BIO_TYPE_SOCKET, \"socket_bio_method\"))) {\n    return nullptr;\n  }\n  auto meth = const_cast<BIO_METHOD*>(BIO_s_socket());\n  BIO_meth_set_create(newmeth, BIO_meth_get_create(meth));\n  BIO_meth_set_destroy(newmeth, BIO_meth_get_destroy(meth));\n  BIO_meth_set_ctrl(newmeth, BIO_meth_get_ctrl(meth));\n  BIO_meth_set_callback_ctrl(newmeth, BIO_meth_get_callback_ctrl(meth));\n  BIO_meth_set_read(newmeth, BIO_meth_get_read(meth));\n  BIO_meth_set_write(newmeth, BIO_meth_get_write(meth));\n  BIO_meth_set_gets(newmeth, BIO_meth_get_gets(meth));\n  BIO_meth_set_puts(newmeth, BIO_meth_get_puts(meth));\n\n  return BioMethodUniquePtr(newmeth);\n}\n\nbool OpenSSLUtils::setCustomBioReadMethod(\n    BIO_METHOD* bioMeth, int (*meth)(BIO*, char*, int)) {\n  bool ret = false;\n  ret = (BIO_meth_set_read(bioMeth, meth) == 1);\n  return ret;\n}\n\nbool OpenSSLUtils::setCustomBioWriteMethod(\n    BIO_METHOD* bioMeth, int (*meth)(BIO*, const char*, int)) {\n  bool ret = false;\n  ret = (BIO_meth_set_write(bioMeth, meth) == 1);\n  return ret;\n}\n\nint OpenSSLUtils::getBioShouldRetryWrite(int r) {\n  int ret = 0;\n#ifdef OPENSSL_IS_BORINGSSL\n  ret = boringssl_bio_fd_should_retry(r);\n#else\n  ret = BIO_sock_should_retry(r);\n#endif\n  return ret;\n}\n\nvoid OpenSSLUtils::setBioAppData(BIO* b, void* ptr) {\n#ifdef OPENSSL_IS_BORINGSSL\n  BIO_set_callback_arg(b, static_cast<char*>(ptr));\n#else\n  BIO_set_app_data(b, ptr);\n#endif\n}\n\nvoid* OpenSSLUtils::getBioAppData(BIO* b) {\n#ifdef OPENSSL_IS_BORINGSSL\n  return BIO_get_callback_arg(b);\n#else\n  return BIO_get_app_data(b);\n#endif\n}\n\nNetworkSocket OpenSSLUtils::getBioFd(BIO* b) {\n  auto ret = BIO_get_fd(b, nullptr);\n#ifdef _WIN32\n  return NetworkSocket((SOCKET)ret);\n#else\n  return NetworkSocket(ret);\n#endif\n}\n\nvoid OpenSSLUtils::setBioFd(BIO* b, NetworkSocket fd, int flags) {\n#ifdef _WIN32\n  // Internally OpenSSL uses this as an int for reasons completely\n  // beyond any form of sanity, so we do the cast ourselves to avoid\n  // the warnings that would be generated.\n  int sock = int(fd.data);\n#else\n  int sock = fd.toFd();\n#endif\n  BIO_set_fd(b, sock, flags);\n}\n\nstd::string OpenSSLUtils::getCommonName(X509* x509) {\n  if (x509 == nullptr) {\n    return \"\";\n  }\n  X509_NAME* subject = X509_get_subject_name(x509);\n  char buf[ub_common_name + 1];\n  int length =\n      X509_NAME_get_text_by_NID(subject, NID_commonName, buf, sizeof(buf));\n  if (length == -1) {\n    // no CN\n    return \"\";\n  }\n  // length tells us where the name ends\n  return std::string(buf, length);\n}\n\nstd::string OpenSSLUtils::encodeALPNString(\n    const std::vector<std::string>& supportedProtocols) {\n  unsigned int length = 0;\n  for (const auto& proto : supportedProtocols) {\n    if (proto.size() > std::numeric_limits<uint8_t>::max()) {\n      throw std::range_error(\"ALPN protocol string exceeds maximum length\");\n    }\n    length += proto.size() + 1;\n  }\n\n  std::string encodedALPN;\n  encodedALPN.reserve(length);\n\n  for (const auto& proto : supportedProtocols) {\n    encodedALPN.append(1, static_cast<char>(proto.size()));\n    encodedALPN.append(proto);\n  }\n  return encodedALPN;\n}\n\n/**\n * Deserializes PEM encoded X509 objects from the supplied source BIO, invoking\n * a callback for each X509, until the BIO is exhausted or until we were unable\n * to read an X509.\n */\ntemplate <class Callback>\nstatic void forEachX509(BIO* source, Callback cb) {\n  while (true) {\n    ssl::X509UniquePtr x509(\n        PEM_read_bio_X509(source, nullptr, nullptr, nullptr));\n    if (x509 == nullptr) {\n      ERR_clear_error();\n      break;\n    }\n    cb(std::move(x509));\n  }\n}\n\nstatic std::vector<X509NameUniquePtr> getSubjectNamesFromBIO(BIO* b) {\n  std::vector<X509NameUniquePtr> ret;\n  forEachX509(b, [&](auto&& name) {\n    // X509_get_subject_name borrows the X509_NAME, so we must dup it.\n    ret.push_back(\n        X509NameUniquePtr(X509_NAME_dup(X509_get_subject_name(name.get()))));\n  });\n  return ret;\n}\n\nstd::vector<X509NameUniquePtr> OpenSSLUtils::subjectNamesInPEMFile(\n    const char* filename) {\n  BioUniquePtr bio(BIO_new_file(filename, \"r\"));\n  if (!bio) {\n    throw std::runtime_error(\n        \"OpenSSLUtils::subjectNamesInPEMFile: failed to open file\");\n  }\n  return getSubjectNamesFromBIO(bio.get());\n}\n\nstd::vector<X509NameUniquePtr> OpenSSLUtils::subjectNamesInPEMBuffer(\n    folly::ByteRange buffer) {\n  BioUniquePtr bio(BIO_new_mem_buf(buffer.data(), buffer.size()));\n  if (!bio) {\n    throw std::runtime_error(\n        \"OpenSSLUtils::subjectNamesInPEMBuffer: failed to create BIO\");\n  }\n  return getSubjectNamesFromBIO(bio.get());\n}\n\n} // namespace ssl\n} // namespace folly\n\nnamespace {\n#ifdef OPENSSL_IS_BORINGSSL\n\nstatic int boringssl_bio_fd_non_fatal_error(int err) {\n  if (\n#ifdef EWOULDBLOCK\n      err == EWOULDBLOCK ||\n#endif\n#ifdef WSAEWOULDBLOCK\n      err == WSAEWOULDBLOCK ||\n#endif\n#ifdef ENOTCONN\n      err == ENOTCONN ||\n#endif\n#ifdef EINTR\n      err == EINTR ||\n#endif\n#ifdef EAGAIN\n      err == EAGAIN ||\n#endif\n#ifdef EPROTO\n      err == EPROTO ||\n#endif\n#ifdef EINPROGRESS\n      err == EINPROGRESS ||\n#endif\n#ifdef EALREADY\n      err == EALREADY ||\n#endif\n      0) {\n    return 1;\n  }\n  return 0;\n}\n\n#if defined(OPENSSL_WINDOWS)\n\nint boringssl_bio_fd_should_retry(int i) {\n  if (i == -1) {\n    return boringssl_bio_fd_non_fatal_error((int)GetLastError());\n  }\n  return 0;\n}\n\n#else // !OPENSSL_WINDOWS\n\nint boringssl_bio_fd_should_retry(int i) {\n  if (i == -1) {\n    return boringssl_bio_fd_non_fatal_error(errno);\n  }\n  return 0;\n}\n#endif // OPENSSL_WINDOWS\n\n#endif // OEPNSSL_IS_BORINGSSL\n\n} // namespace\n"
  },
  {
    "path": "folly/io/async/ssl/OpenSSLUtils.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Range.h>\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/portability/Sockets.h>\n#include <folly/ssl/OpenSSLPtrTypes.h>\n#include <folly/ssl/SSLSession.h>\n\nnamespace folly {\nnamespace ssl {\n\nclass OpenSSLUtils {\n public:\n  /*\n   * Get the TLS Session Master Key used to generate the TLS key material\n   *\n   * @param session ssl session\n   * @param keyOut destination for the master key, the buffer must be at least\n   * 48 bytes\n   * @return true if the master key is available (>= TLS1) and the output buffer\n   * large enough\n   */\n  static bool getTLSMasterKey(\n      const SSL_SESSION* session, MutableByteRange keyOut);\n  static bool getTLSMasterKey(\n      const std::shared_ptr<SSLSession> session, MutableByteRange keyOut);\n\n  /*\n   * Get the TLS Client Random used to generate the TLS key material\n   *\n   * @param ssl\n   * @param randomOut destination for the client random, the buffer must be at\n   * least 32 bytes\n   * @return true if the client random is available (>= TLS1) and the output\n   * buffer large enough\n   */\n  static bool getTLSClientRandom(const SSL* ssl, MutableByteRange randomOut);\n\n  /**\n   * Validate that the peer certificate's common name or subject alt names\n   * match what we expect.  Currently this only checks for IPs within\n   * subject alt names but it could easily be expanded to check common name\n   * and hostnames as well.\n   *\n   * @param cert    X509* peer certificate\n   * @param addr    sockaddr object containing sockaddr to verify\n   * @param addrLen length of sockaddr as returned by getpeername or accept\n   * @return true iff a subject altname IP matches addr\n   */\n  // TODO(agartrell): Add support for things like common name when\n  // necessary.\n  static bool validatePeerCertNames(\n      X509* cert, const sockaddr* addr, socklen_t addrLen);\n\n  /**\n   * Get the peer socket address from an X509_STORE_CTX*.  Unlike the\n   * accept, getsockname, getpeername, etc family of operations, addrLen's\n   * initial value is ignored and reset.\n   *\n   * @param ctx         Context from which to retrieve peer sockaddr\n   * @param addrStorage out param for address\n   * @param addrLen     out param for length of address\n   * @return true on success, false on failure\n   */\n  static bool getPeerAddressFromX509StoreCtx(\n      X509_STORE_CTX* ctx, sockaddr_storage* addrStorage, socklen_t* addrLen);\n\n  /**\n   * Get a stringified cipher name (e.g., ECDHE-ECDSA-CHACHA20-POLY1305) given\n   * the 2-byte code (e.g., 0xcca9) for the cipher. The name conversion only\n   * works for the ciphers built into the linked OpenSSL library\n   *\n   * @param cipherCode      A 16-bit IANA cipher code (machine endianness)\n   * @return Cipher name, or empty if the code is not found\n   */\n  static const std::string& getCipherName(uint16_t cipherCode);\n\n  /**\n   * Set the 'initial_ctx' SSL_CTX* inside an SSL. The initial_ctx is used to\n   * point to the SSL_CTX on which servername callback and session callbacks,\n   * as well as session caching stats are set. If we want to enforce SSL_CTX\n   * thread-based ownership (e.g., thread-local SSL_CTX) in the application, we\n   * need to also set/reset the initial_ctx when we call SSL_set_SSL_CTX.\n   *\n   * @param ssl      SSL pointer\n   * @param ctx      SSL_CTX pointer\n   * @return Cipher name, or empty if the code is not found\n   */\n  static void setSSLInitialCtx(SSL* ssl, SSL_CTX* ctx);\n  static SSL_CTX* getSSLInitialCtx(SSL* ssl);\n\n  /**\n   * Get the common name out of a cert.  Return empty if x509 is null.\n   */\n  static std::string getCommonName(X509* x509);\n\n  /**\n   * Get a list of subject names corresponding to each certificate in a\n   * PEM encoded file.\n   *\n   * @param filename   Path to a file containing PEM encoded X509 certificates.\n   * @return A vector of X509_NAMEs corresponding to the subject of each\n   *         certificate.\n   * @throws std::exception For any errors encountered while reading in\n   *                        certificates from the file.\n   */\n  static std::vector<X509NameUniquePtr> subjectNamesInPEMFile(\n      const char* filename);\n\n  /**\n   * Get a list of subject names corresponding to each certificate in a\n   * buffer containing PEM data.\n   *\n   * @param buffer  A contiguous region of memory containing PEM encoded\n   *                certificates.\n   * @return A vector of X509_NAMEs corresponding to the subject of each\n   *         certificate.\n   * @throws std::exception For any errors encountered while reading in\n   *                        certificates from the file.\n   */\n  static std::vector<X509NameUniquePtr> subjectNamesInPEMBuffer(\n      folly::ByteRange buffer);\n\n  /**\n   * Wrappers for BIO operations that may be different across different\n   * versions/flavors of OpenSSL (including forks like BoringSSL)\n   */\n  static BioMethodUniquePtr newSocketBioMethod();\n  static bool setCustomBioReadMethod(\n      BIO_METHOD* bioMeth, int (*meth)(BIO*, char*, int));\n  static bool setCustomBioWriteMethod(\n      BIO_METHOD* bioMeth, int (*meth)(BIO*, const char*, int));\n  static int getBioShouldRetryWrite(int ret);\n  static void setBioAppData(BIO* b, void* ptr);\n  static void* getBioAppData(BIO* b);\n  static NetworkSocket getBioFd(BIO* b);\n  static void setBioFd(BIO* b, NetworkSocket fd, int flags);\n  static std::string encodeALPNString(\n      const std::vector<std::string>& supported_protocols);\n};\n} // namespace ssl\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ssl/SSLErrors.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/ssl/SSLErrors.h>\n\n#include <folly/Range.h>\n#include <folly/portability/OpenSSL.h>\n\nusing namespace folly;\n\nnamespace {\n\nstd::string decodeOpenSSLError(\n    int sslError, unsigned long errError, int sslOperationReturnValue) {\n  if (sslError == SSL_ERROR_SYSCALL && errError == 0) {\n    if (sslOperationReturnValue == 0) {\n      return \"Connection EOF\";\n    } else {\n      // In this case errno is set, AsyncSocketException will add it.\n      return \"Network error\";\n    }\n  } else if (sslError == SSL_ERROR_ZERO_RETURN) {\n    // This signifies a TLS closure alert.\n    return \"SSL connection closed normally\";\n  } else {\n    std::array<char, 256> buf;\n    ERR_error_string_n(errError, buf.data(), buf.size());\n    // OpenSSL will null terminate the string.\n    return std::string(buf.data());\n  }\n}\n\nStringPiece getSSLErrorString(SSLError error) {\n  StringPiece ret;\n  switch (error) {\n    case SSLError::CLIENT_RENEGOTIATION:\n      ret = \"Client tried to renegotiate with server\";\n      break;\n    case SSLError::INVALID_RENEGOTIATION:\n      ret = \"Attempt to start renegotiation, but unsupported\";\n      break;\n    case SSLError::EARLY_WRITE:\n      ret = \"Attempt to write before SSL connection established\";\n      break;\n    case SSLError::SSL_ERROR:\n      ret = \"SSL error\";\n      break;\n    case SSLError::NETWORK_ERROR:\n      ret = \"Network error\";\n      break;\n    case SSLError::EOF_ERROR:\n      ret = \"SSL connection closed normally\";\n      break;\n  }\n  return ret;\n}\n\nAsyncSocketException::AsyncSocketExceptionType exTypefromSSLErrInfo(\n    int sslErr, unsigned long errError, int sslOperationReturnValue) {\n  if (sslErr == SSL_ERROR_ZERO_RETURN) {\n    return AsyncSocketException::END_OF_FILE;\n  } else if (sslErr == SSL_ERROR_SYSCALL) {\n    if (errError == 0 && sslOperationReturnValue == 0) {\n      return AsyncSocketException::END_OF_FILE;\n    } else {\n      return AsyncSocketException::NETWORK_ERROR;\n    }\n  } else {\n    // Assume an actual SSL error\n    return AsyncSocketException::SSL_ERROR;\n  }\n}\n\nAsyncSocketException::AsyncSocketExceptionType exTypefromSSLErr(SSLError err) {\n  switch (err) {\n    case SSLError::EOF_ERROR:\n      return AsyncSocketException::END_OF_FILE;\n    case SSLError::NETWORK_ERROR:\n      return AsyncSocketException::NETWORK_ERROR;\n    case SSLError::CLIENT_RENEGOTIATION:\n    case SSLError::INVALID_RENEGOTIATION:\n    case SSLError::EARLY_WRITE:\n    case SSLError::SSL_ERROR:\n    default:\n      // everything else is a SSL_ERROR\n      return AsyncSocketException::SSL_ERROR;\n  }\n}\n} // namespace\n\nnamespace folly {\n\nSSLException::SSLException(\n    int sslErr,\n    unsigned long errError,\n    int sslOperationReturnValue,\n    int errno_copy)\n    : AsyncSocketException(\n          exTypefromSSLErrInfo(sslErr, errError, sslOperationReturnValue),\n          decodeOpenSSLError(sslErr, errError, sslOperationReturnValue),\n          sslErr == SSL_ERROR_SYSCALL ? errno_copy : 0) {\n  if (sslErr == SSL_ERROR_ZERO_RETURN) {\n    sslError = SSLError::EOF_ERROR;\n  } else if (sslErr == SSL_ERROR_SYSCALL) {\n    sslError = SSLError::NETWORK_ERROR;\n  } else {\n    // Conservatively assume that this is an SSL error\n    sslError = SSLError::SSL_ERROR;\n  }\n\n  sslInternalErrorCode = errError;\n}\n\nSSLException::SSLException(SSLError error)\n    : AsyncSocketException(\n          exTypefromSSLErr(error), getSSLErrorString(error).str(), 0),\n      sslError(error),\n      sslInternalErrorCode(0) {}\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ssl/SSLErrors.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncSocketException.h>\n\nnamespace folly {\n\nenum class SSLError {\n  CLIENT_RENEGOTIATION, // A client tried to renegotiate with this server\n  INVALID_RENEGOTIATION, // We attempted to start a renegotiation.\n  EARLY_WRITE, // Wrote before SSL connection established.\n  SSL_ERROR, // An error related to SSL\n  NETWORK_ERROR, // An error related to the network.\n  EOF_ERROR, // The peer terminated the connection correctly.\n};\n\nclass SSLException : public folly::AsyncSocketException {\n public:\n  SSLException(\n      int sslError,\n      unsigned long errError,\n      int sslOperationReturnValue,\n      int errno_copy);\n\n  explicit SSLException(SSLError error);\n\n  SSLError getSSLError() const { return sslError; }\n\n  unsigned long getInternalSSLError() const { return sslInternalErrorCode; }\n\n private:\n  SSLError sslError;\n  unsigned long sslInternalErrorCode;\n};\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ssl/TLSDefinitions.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <vector>\n\n#include <folly/io/Cursor.h>\n#include <folly/io/IOBuf.h>\n\nnamespace folly {\nnamespace ssl {\n\n// http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml\nenum class TLSExtension : uint16_t {\n  SERVER_NAME = 0,\n  MAX_FRAGMENT_LENGTH = 1,\n  CLIENT_CERTIFICATE_URL = 2,\n  TRUSTED_CA_KEYS = 3,\n  TRUNCATED_HMAC = 4,\n  STATUS_REQUEST = 5,\n  USER_MAPPING = 6,\n  CLIENT_AUTHZ = 7,\n  SERVER_AUTHZ = 8,\n  CERT_TYPE = 9,\n  SUPPORTED_GROUPS = 10,\n  EC_POINT_FORMATS = 11,\n  SRP = 12,\n  SIGNATURE_ALGORITHMS = 13,\n  USE_SRTP = 14,\n  HEARTBEAT = 15,\n  APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16,\n  STATUS_REQUEST_V2 = 17,\n  SIGNED_CERTIFICATE_TIMESTAMP = 18,\n  CLIENT_CERTIFICATE_TYPE = 19,\n  SERVER_CERTIFICATE_TYPE = 20,\n  PADDING = 21,\n  ENCRYPT_THEN_MAC = 22,\n  EXTENDED_MASTER_SECRET = 23,\n  SESSION_TICKET = 35,\n  SUPPORTED_VERSIONS = 43,\n  // Facebook-specific, not IANA assigned yet\n  TLS_CACHED_INFO_FB = 60001,\n  // End Facebook-specific\n  RENEGOTIATION_INFO = 65281\n};\n\n// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18\nenum class HashAlgorithm : uint8_t {\n  NONE = 0,\n  MD5 = 1,\n  SHA1 = 2,\n  SHA224 = 3,\n  SHA256 = 4,\n  SHA384 = 5,\n  SHA512 = 6\n};\n\n// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16\nenum class SignatureAlgorithm : uint8_t {\n  ANONYMOUS = 0,\n  RSA = 1,\n  DSA = 2,\n  ECDSA = 3\n};\n\nenum class NameType : uint8_t {\n  HOST_NAME = 0,\n};\n\nstruct ClientHelloInfo {\n  folly::IOBufQueue clientHelloBuf_;\n  uint8_t clientHelloMajorVersion_;\n  uint8_t clientHelloMinorVersion_;\n  std::vector<uint16_t> clientHelloCipherSuites_;\n  std::vector<uint8_t> clientHelloCompressionMethods_;\n  std::vector<TLSExtension> clientHelloExtensions_;\n  std::vector<std::pair<HashAlgorithm, SignatureAlgorithm>> clientHelloSigAlgs_;\n  std::vector<uint16_t> clientHelloSupportedVersions_;\n\n  // Technically, the TLS spec allows for multiple ServerNames to be sent (as\n  // long as each ServerName has a distinct type). In practice, the only one\n  // we really care about is HOST_NAME.\n  std::string clientHelloSNIHostname_;\n  std::vector<std::string> clientAlpns_;\n};\n\n} // namespace ssl\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ssl/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"../../../../defs.bzl\", \"folly_xplat_cxx_test\")\nload(\"../../../../io/async/test/certs/defs.bzl\", \"alias_pem\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"ssl_errors_test\",\n    srcs = [\"SSLErrorsTest.cpp\"],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"//xplat/folly/io/async/ssl:ssl_errors\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"basic_transport_certificate_test\",\n    srcs = [\n        # \"BasicTransportCertificateTest.cpp\",\n    ],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:file_util\",\n        \"//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"//xplat/folly/ssl:openssl_cert_utils\",\n    ],\n)\n\n# !!!! fbcode/folly/io/async/ssl/test/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = alias_pem,\n    pems = [\n        \"tests-cert.pem\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ssl_errors_test\",\n    srcs = [\"SSLErrorsTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    deps = [\n        \"//folly/io/async/ssl:ssl_errors\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:openssl\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"basic_transport_certificate_test\",\n    srcs = [\"BasicTransportCertificateTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    resources = [\n        \":tests-cert.pem\",\n    ],\n    deps = [\n        \"//folly:file_util\",\n        \"//folly/io/async/ssl:basic_transport_certificate\",\n        \"//folly/portability:gtest\",\n        \"//folly/ssl:openssl_cert_utils\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"openssl_utils_test\",\n    srcs = [\"OpenSSLUtilsTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    deps = [\n        \"//folly:string\",\n        \"//folly/io/async/ssl:openssl_utils\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:openssl\",\n        \"//folly/ssl:openssl_ptr_types\",\n    ],\n)\n"
  },
  {
    "path": "folly/io/async/ssl/test/BasicTransportCertificateTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/ssl/BasicTransportCertificate.h>\n\n#include <folly/FileUtil.h>\n#include <folly/portability/GTest.h>\n#include <folly/ssl/OpenSSLCertUtils.h>\n#include <folly/testing/TestUtil.h>\n\nusing namespace folly;\nusing namespace folly::ssl;\nusing folly::test::find_resource;\n\nconst char* kTestCerts = \"folly/io/async/ssl/test/tests-cert.pem\";\n\nTEST(BasicTransportCertificateTest, TestCerts) {\n  auto path = find_resource(kTestCerts);\n  std::string certData;\n  EXPECT_TRUE(folly::readFile(path.c_str(), certData));\n  auto certs = OpenSSLCertUtils::readCertsFromBuffer(StringPiece(certData));\n  EXPECT_FALSE(certs.empty());\n  auto x509Ptr = std::move(certs[0]);\n  EXPECT_NE(x509Ptr, nullptr);\n\n  {\n    SCOPED_TRACE(\"create w/ null\");\n    auto cert = BasicTransportCertificate::create(nullptr);\n    EXPECT_EQ(cert, nullptr);\n  }\n\n  {\n    SCOPED_TRACE(\"construct with empty cert\");\n    BasicTransportCertificate cert(\"foo\", nullptr);\n    EXPECT_EQ(cert.getX509(), nullptr);\n    EXPECT_EQ(cert.getIdentity(), \"foo\");\n    auto cloned = BasicTransportCertificate::create(&cert);\n    EXPECT_EQ(cloned->getX509(), nullptr);\n    EXPECT_EQ(cloned->getIdentity(), \"foo\");\n  }\n\n  {\n    SCOPED_TRACE(\"construct w/ x509\");\n    auto x509Raw = x509Ptr.get();\n    EXPECT_NE(x509Raw, nullptr);\n    BasicTransportCertificate cert(\"x509\", std::move(x509Ptr));\n    EXPECT_EQ(cert.getX509().get(), x509Raw);\n    EXPECT_EQ(cert.getIdentity(), \"x509\");\n\n    auto cloned = BasicTransportCertificate::create(&cert);\n    EXPECT_EQ(cloned->getX509().get(), x509Raw);\n    EXPECT_EQ(cloned->getIdentity(), \"x509\");\n  }\n}\n"
  },
  {
    "path": "folly/io/async/ssl/test/OpenSSLUtilsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/ssl/OpenSSLUtils.h>\n\n#include <folly/String.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/ssl/OpenSSLPtrTypes.h>\n\nusing namespace folly::ssl;\n\nnamespace folly {\n\nconst std::string kSampleCommonName = \"Folly Library\";\n\n// a certificate with a CN that uses 64 characters, the max length\nconst std::string kSampleCommonNameMaxLength =\n    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\";\n\n// create and return an X509 object with only the subject's CN set, so tests can\n// extract and compare it\nX509UniquePtr createMinimalX509(const std::string& commonName) {\n  X509* x509;\n  x509 = X509_new();\n  X509_NAME* name;\n  name = X509_get_subject_name(x509);\n  X509_NAME_add_entry_by_txt(\n      name,\n      SN_commonName,\n      MBSTRING_ASC,\n      reinterpret_cast<const unsigned char*>(commonName.data()),\n      -1,\n      -1,\n      0);\n\n  return X509UniquePtr(x509);\n}\n\n// Tests that the common name is extracted from the x509 certificate with the\n// correct length\nTEST(OpenSSLUtilsTest, getCommonName) {\n  X509UniquePtr x509 = createMinimalX509(kSampleCommonName);\n\n  EXPECT_EQ(OpenSSLUtils::getCommonName(x509.get()), kSampleCommonName);\n}\n\n// Tests that the common name is extracted from the x509 certificate correctly\n// when its length is the maximum, defined as ub_common_name in asn1.h (RFC2459)\nTEST(OpenSSLUtilsTest, getCommonNameMaxLength) {\n  X509UniquePtr x509 = createMinimalX509(kSampleCommonNameMaxLength);\n\n  // read common name from certificate\n  EXPECT_EQ(\n      OpenSSLUtils::getCommonName(x509.get()), kSampleCommonNameMaxLength);\n}\n\n// Tests that getCommonName returns an empty string for a null X509 argument\nTEST(OpenSSLUtilsTest, getCommonNameNullX509) {\n  EXPECT_EQ(OpenSSLUtils::getCommonName(nullptr), \"\");\n}\n\n// Tests that getCommonName returns an empty string because the given\n// certificate has no CN\nTEST(OpenSSLUtilsTest, getCommonNameEmpty) {\n  X509UniquePtr x509 = createMinimalX509(\"\");\n\n  EXPECT_EQ(OpenSSLUtils::getCommonName(x509.get()), \"\");\n}\n\n// Tests that encodeALPNString returns a serialized version of the ALPN String\nTEST(OpenSSLUtilsTest, encodeALPNString) {\n  EXPECT_EQ(OpenSSLUtils::encodeALPNString({\"rs\"}), \"\\x2rs\");\n  EXPECT_EQ(OpenSSLUtils::encodeALPNString({\"rs\", \"h2\"}), \"\\x2rs\\x2h2\");\n  EXPECT_EQ(\n      OpenSSLUtils::encodeALPNString({\"rs\", \"h2\", \"spdy/3.1\"}),\n      \"\\x2rs\\x2h2\\x8spdy/3.1\");\n  EXPECT_EQ(\n      OpenSSLUtils::encodeALPNString({\"rs\", \"h2\", \"spdy/3.1\", \"http/1.1\"}),\n      \"\\x2rs\\x2h2\\x8spdy/3.1\\x8http/1.1\");\n\n  std::string maxSizeProtocolString(std::numeric_limits<uint8_t>::max(), 'p');\n  EXPECT_EQ(\n      OpenSSLUtils::encodeALPNString({maxSizeProtocolString}),\n      \"\\xFF\" + maxSizeProtocolString);\n  EXPECT_EQ(\n      OpenSSLUtils::encodeALPNString({maxSizeProtocolString, \"rs\"}),\n      \"\\xFF\" + maxSizeProtocolString + \"\\x2rs\");\n  EXPECT_EQ(\n      OpenSSLUtils::encodeALPNString({maxSizeProtocolString, \"rs\", \"h2\"}),\n      \"\\xFF\" + maxSizeProtocolString + \"\\x2rs\\x2h2\");\n  EXPECT_EQ(\n      OpenSSLUtils::encodeALPNString(\n          {maxSizeProtocolString, \"rs\", \"h2\", \"spdy/3.1\"}),\n      \"\\xFF\" + maxSizeProtocolString + \"\\x2rs\\x2h2\\x8spdy/3.1\");\n  EXPECT_EQ(\n      OpenSSLUtils::encodeALPNString(\n          {maxSizeProtocolString, \"rs\", \"h2\", \"spdy/3.1\", \"http/1.1\"}),\n      \"\\xFF\" + maxSizeProtocolString + \"\\x2rs\\x2h2\\x8spdy/3.1\\x8http/1.1\");\n\n  std::string exceedsMaxSizeProtocolString(\n      std::numeric_limits<uint8_t>::max() + 1, 'p');\n\n  try {\n    OpenSSLUtils::encodeALPNString({exceedsMaxSizeProtocolString});\n  } catch (std::range_error const& err) {\n    EXPECT_EQ(\n        err.what(), std::string(\"ALPN protocol string exceeds maximum length\"));\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/ssl/test/SSLErrorsTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/ssl/SSLErrors.h>\n\n#include <folly/portability/GTest.h>\n#include <folly/portability/OpenSSL.h>\n\nusing namespace folly;\n\nTEST(SSLErrorsTest, TestMessage) {\n  ERR_load_crypto_strings();\n  unsigned long err;\n#ifdef OPENSSL_IS_BORINGSSL\n  err = ERR_PACK(ERR_LIB_X509, X509_R_CERT_ALREADY_IN_HASH_TABLE);\n#else\n  err = ERR_PACK(\n      ERR_LIB_X509,\n      X509_F_X509_STORE_ADD_CERT,\n      X509_R_CERT_ALREADY_IN_HASH_TABLE);\n#endif\n  SSLException ex(0, err, 0, 0);\n\n// This is flaky - we should not be testing error strings\n// which may change version to version\n#if defined(OPENSSL_IS_BORINGSSL)\n  std::string expectedMsg =\n      \"AsyncSocketException: error:0b000069:X.509 certificate routines:\"\n      \"OPENSSL_internal:CERT_ALREADY_IN_HASH_TABLE, type = SSL error\";\n#elif FOLLY_OPENSSL_IS_3X\n  std::string expectedMsg =\n      \"AsyncSocketException: error:05800065:x509 certificate routines::\"\n      \"cert already in hash table, type = SSL error\";\n#else\n  std::string expectedMsg =\n      \"AsyncSocketException: error:0B07C065:\"\n      \"x509 certificate routines:X509_STORE_add_cert:\"\n      \"cert already in hash table, type = SSL error\";\n#endif\n  std::string actual = ex.what();\n  EXPECT_EQ(expectedMsg, actual);\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncBaseTestLib.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/test/AsyncBaseTestLib.h>\n\nnamespace folly {\nnamespace test {\nnamespace async_base_test_lib_detail {\nvoid TestUtil::waitUntilReadable(int fd) {\n  pollfd pfd;\n  pfd.fd = fd;\n  pfd.events = POLLIN;\n\n  int r;\n  do {\n    r = poll(&pfd, 1, -1); // wait forever\n  } while (r == -1 && errno == EINTR);\n  PCHECK(r == 1);\n  CHECK_EQ(pfd.revents, POLLIN); // no errors etc\n}\n\nfolly::Range<folly::AsyncBase::Op**> TestUtil::readerWait(\n    folly::AsyncBase* reader) {\n  int fd = reader->pollFd();\n  if (fd == -1) {\n    return reader->wait(1);\n  } else {\n    waitUntilReadable(fd);\n    return reader->pollCompleted();\n  }\n}\n\nTestUtil::ManagedBuffer TestUtil::allocateAligned(size_t size) {\n  void* buf = unsafe_default_initialized; // skip a write to stack\n  int rc = posix_memalign(&buf, kODirectAlign, size);\n  CHECK_EQ(rc, 0) << folly::errnoStr(rc);\n  return ManagedBuffer(reinterpret_cast<char*>(buf), free);\n}\n\n} // namespace async_base_test_lib_detail\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncBaseTestLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <cstdio>\n#include <cstdlib>\n#include <memory>\n#include <random>\n#include <thread>\n#include <vector>\n\n#include <glog/logging.h>\n\n#include <folly/ScopeGuard.h>\n#include <folly/String.h>\n#include <folly/io/FsUtil.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Unistd.h>\n#include <folly/test/TestUtils.h>\n\n#include <folly/io/async/AsyncBase.h>\n#include <folly/io/async/test/IoTestTempFileUtil.h>\n\nnamespace folly {\nnamespace test {\nnamespace async_base_test_lib_detail {\n\nconstexpr size_t kODirectAlign = 4096; // align reads to 4096 B (for O_DIRECT)\nconstexpr size_t kDefaultFileSize = 6 << 20; // 6MiB\n\nconstexpr size_t kBatchNumEntries = 1024;\nconstexpr size_t kBatchSize = 192;\nconstexpr size_t kBatchBlockSize = 4096;\n\nstruct TestSpec {\n  off_t start;\n  size_t size;\n};\n\nstruct TestUtil {\n  static void waitUntilReadable(int fd);\n  static folly::Range<folly::AsyncBase::Op**> readerWait(\n      folly::AsyncBase* reader);\n  using ManagedBuffer = std::unique_ptr<char, void (*)(void*)>;\n  static ManagedBuffer allocateAligned(size_t size);\n};\n\ntemplate <typename TAsync>\nstd::unique_ptr<TAsync> getAIO(size_t capacity, AsyncBase::PollMode pollMode) {\n  try {\n    auto ret = std::make_unique<TAsync>(capacity, pollMode);\n    ret->initializeContext();\n\n    return ret;\n  } catch (const std::runtime_error&) {\n  }\n\n  return nullptr;\n}\n\ntemplate <typename TAsync>\nvoid testReadsSerially(\n    const std::vector<TestSpec>& specs, folly::AsyncBase::PollMode pollMode) {\n  auto aioReader = getAIO<TAsync>(1, pollMode);\n  SKIP_IF(!aioReader) << \"TAsync not available\";\n\n  typename TAsync::Op op;\n  auto tempFile = folly::test::TempFileUtil::getTempFile(kDefaultFileSize);\n  int fd = ::open(tempFile.path().c_str(), O_DIRECT | O_RDONLY);\n  if (fd == -1) {\n    fd = ::open(tempFile.path().c_str(), O_RDONLY);\n  }\n  SKIP_IF(fd == -1) << \"Tempfile can't be opened: \" << folly::errnoStr(errno);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n\n  for (size_t i = 0; i < specs.size(); i++) {\n    auto buf = TestUtil::allocateAligned(specs[i].size);\n    op.pread(fd, buf.get(), specs[i].size, specs[i].start);\n    aioReader->submit(&op);\n    EXPECT_EQ((i + 1), aioReader->totalSubmits());\n    EXPECT_EQ(aioReader->pending(), 1);\n    auto ops =\n        test::async_base_test_lib_detail::TestUtil::readerWait(aioReader.get());\n    EXPECT_EQ(1, ops.size());\n    EXPECT_TRUE(ops[0] == &op);\n    EXPECT_EQ(aioReader->pending(), 0);\n    ssize_t res = op.result();\n    EXPECT_LE(0, res) << folly::errnoStr(-res);\n    EXPECT_EQ(specs[i].size, res);\n    op.reset();\n  }\n}\n\ntemplate <typename TAsync>\nvoid testReadsParallel(\n    const std::vector<TestSpec>& specs,\n    folly::AsyncBase::PollMode pollMode,\n    bool multithreaded) {\n  auto aioReader = getAIO<TAsync>(specs.size(), pollMode);\n  SKIP_IF(!aioReader) << \"TAsync not available\";\n\n  std::unique_ptr<typename TAsync::Op[]> ops(\n      new typename TAsync::Op[specs.size()]);\n  uintptr_t sizeOf = sizeof(typename TAsync::Op);\n  std::vector<TestUtil::ManagedBuffer> bufs;\n  bufs.reserve(specs.size());\n\n  auto tempFile = folly::test::TempFileUtil::getTempFile(kDefaultFileSize);\n  int fd = ::open(tempFile.path().c_str(), O_DIRECT | O_RDONLY);\n  if (fd == -1) {\n    fd = ::open(tempFile.path().c_str(), O_RDONLY);\n  }\n  SKIP_IF(fd == -1) << \"Tempfile can't be opened: \" << folly::errnoStr(errno);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n\n  std::vector<std::thread> threads;\n  if (multithreaded) {\n    threads.reserve(specs.size());\n  }\n  for (size_t i = 0; i < specs.size(); i++) {\n    bufs.push_back(TestUtil::allocateAligned(specs[i].size));\n  }\n  auto submit = [&](size_t i) {\n    ops[i].pread(fd, bufs[i].get(), specs[i].size, specs[i].start);\n    aioReader->submit(&ops[i]);\n  };\n  for (size_t i = 0; i < specs.size(); i++) {\n    if (multithreaded) {\n      threads.emplace_back([&submit, i] { submit(i); });\n    } else {\n      submit(i);\n    }\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n  std::vector<bool> pending(specs.size(), true);\n\n  size_t remaining = specs.size();\n  while (remaining != 0) {\n    EXPECT_EQ(remaining, aioReader->pending());\n    auto completed =\n        test::async_base_test_lib_detail::TestUtil::readerWait(aioReader.get());\n    size_t nrRead = completed.size();\n    remaining -= nrRead;\n\n    for (size_t i = 0; i < nrRead; i++) {\n      int id = (reinterpret_cast<uintptr_t>(completed[i]) -\n                reinterpret_cast<uintptr_t>(ops.get())) /\n          sizeOf;\n      EXPECT_GE(id, 0);\n      EXPECT_LT(id, specs.size());\n      EXPECT_TRUE(pending[id]);\n      pending[id] = false;\n      ssize_t res = ops[id].result();\n      EXPECT_LE(0, res) << folly::errnoStr(-res);\n      EXPECT_EQ(specs[id].size, res);\n    }\n  }\n  EXPECT_EQ(specs.size(), aioReader->totalSubmits());\n\n  EXPECT_EQ(aioReader->pending(), 0);\n  for (size_t i = 0; i < pending.size(); i++) {\n    EXPECT_FALSE(pending[i]);\n  }\n}\n\ntemplate <typename TAsync>\nvoid testReadsQueued(\n    const std::vector<TestSpec>& specs, folly::AsyncBase::PollMode pollMode) {\n  size_t readerCapacity = std::max(specs.size() / 2, size_t(1));\n  auto aioReader = getAIO<TAsync>(readerCapacity, pollMode);\n  SKIP_IF(!aioReader) << \"TAsync not available\";\n  folly::AsyncBaseQueue aioQueue(aioReader.get());\n  std::unique_ptr<typename TAsync::Op[]> ops(\n      new typename TAsync::Op[specs.size()]);\n  uintptr_t sizeOf = sizeof(typename TAsync::Op);\n  std::vector<TestUtil::ManagedBuffer> bufs;\n\n  auto tempFile = folly::test::TempFileUtil::getTempFile(kDefaultFileSize);\n  int fd = ::open(tempFile.path().c_str(), O_DIRECT | O_RDONLY);\n  if (fd == -1) {\n    fd = ::open(tempFile.path().c_str(), O_RDONLY);\n  }\n  SKIP_IF(fd == -1) << \"Tempfile can't be opened: \" << folly::errnoStr(errno);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n  for (size_t i = 0; i < specs.size(); i++) {\n    bufs.push_back(TestUtil::allocateAligned(specs[i].size));\n    ops[i].pread(fd, bufs[i].get(), specs[i].size, specs[i].start);\n    aioQueue.submit(&ops[i]);\n  }\n  std::vector<bool> pending(specs.size(), true);\n\n  size_t remaining = specs.size();\n  while (remaining != 0) {\n    if (remaining >= readerCapacity) {\n      EXPECT_EQ(readerCapacity, aioReader->pending());\n      EXPECT_EQ(remaining - readerCapacity, aioQueue.queued());\n    } else {\n      EXPECT_EQ(remaining, aioReader->pending());\n      EXPECT_EQ(0, aioQueue.queued());\n    }\n    auto completed =\n        test::async_base_test_lib_detail::TestUtil::readerWait(aioReader.get());\n    size_t nrRead = completed.size();\n    remaining -= nrRead;\n\n    for (size_t i = 0; i < nrRead; i++) {\n      int id = (reinterpret_cast<uintptr_t>(completed[i]) -\n                reinterpret_cast<uintptr_t>(ops.get())) /\n          sizeOf;\n      EXPECT_GE(id, 0);\n      EXPECT_LT(id, specs.size());\n      EXPECT_TRUE(pending[id]);\n      pending[id] = false;\n      ssize_t res = ops[id].result();\n      EXPECT_LE(0, res) << folly::errnoStr(-res);\n      EXPECT_EQ(specs[id].size, res);\n    }\n  }\n  EXPECT_EQ(specs.size(), aioReader->totalSubmits());\n  EXPECT_EQ(aioReader->pending(), 0);\n  EXPECT_EQ(aioQueue.queued(), 0);\n  for (size_t i = 0; i < pending.size(); i++) {\n    EXPECT_FALSE(pending[i]);\n  }\n}\n\ntemplate <typename TAsync>\nvoid testReads(\n    const std::vector<TestSpec>& specs, folly::AsyncBase::PollMode pollMode) {\n  testReadsSerially<TAsync>(specs, pollMode);\n  testReadsParallel<TAsync>(specs, pollMode, false);\n  testReadsParallel<TAsync>(specs, pollMode, true);\n  testReadsQueued<TAsync>(specs, pollMode);\n}\n\ntemplate <typename T>\nclass AsyncTest : public ::testing::Test {};\nTYPED_TEST_SUITE_P(AsyncTest);\n\nTYPED_TEST_P(AsyncTest, ZeroAsyncDataNotPollable) {\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, 0}}, folly::AsyncBase::NOT_POLLABLE);\n}\n\nTYPED_TEST_P(AsyncTest, ZeroAsyncDataPollable) {\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, 0}}, folly::AsyncBase::POLLABLE);\n}\n\nTYPED_TEST_P(AsyncTest, SingleAsyncDataNotPollable) {\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::NOT_POLLABLE);\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::NOT_POLLABLE);\n}\n\nTYPED_TEST_P(AsyncTest, SingleAsyncDataPollable) {\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::POLLABLE);\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::POLLABLE);\n}\n\nTYPED_TEST_P(AsyncTest, MultipleAsyncDataNotPollable) {\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        4 * test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::NOT_POLLABLE);\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        4 * test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::NOT_POLLABLE);\n\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, 5 * 1024 * 1024},\n       {test::async_base_test_lib_detail::kODirectAlign, 5 * 1024 * 1024}},\n      folly::AsyncBase::NOT_POLLABLE);\n\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {\n          {test::async_base_test_lib_detail::kODirectAlign, 0},\n          {test::async_base_test_lib_detail::kODirectAlign,\n           test::async_base_test_lib_detail::kODirectAlign},\n          {test::async_base_test_lib_detail::kODirectAlign,\n           2 * test::async_base_test_lib_detail::kODirectAlign},\n          {test::async_base_test_lib_detail::kODirectAlign,\n           20 * test::async_base_test_lib_detail::kODirectAlign},\n          {test::async_base_test_lib_detail::kODirectAlign, 1024 * 1024},\n      },\n      folly::AsyncBase::NOT_POLLABLE);\n}\n\nTYPED_TEST_P(AsyncTest, MultipleAsyncDataPollable) {\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        4 * test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::POLLABLE);\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        2 * test::async_base_test_lib_detail::kODirectAlign},\n       {test::async_base_test_lib_detail::kODirectAlign,\n        4 * test::async_base_test_lib_detail::kODirectAlign}},\n      folly::AsyncBase::POLLABLE);\n\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {{0, 5 * 1024 * 1024},\n       {test::async_base_test_lib_detail::kODirectAlign, 5 * 1024 * 1024}},\n      folly::AsyncBase::NOT_POLLABLE);\n\n  test::async_base_test_lib_detail::testReads<TypeParam>(\n      {\n          {test::async_base_test_lib_detail::kODirectAlign, 0},\n          {test::async_base_test_lib_detail::kODirectAlign,\n           test::async_base_test_lib_detail::kODirectAlign},\n          {test::async_base_test_lib_detail::kODirectAlign,\n           2 * test::async_base_test_lib_detail::kODirectAlign},\n          {test::async_base_test_lib_detail::kODirectAlign,\n           20 * test::async_base_test_lib_detail::kODirectAlign},\n          {test::async_base_test_lib_detail::kODirectAlign, 1024 * 1024},\n      },\n      folly::AsyncBase::NOT_POLLABLE);\n}\n\nTYPED_TEST_P(AsyncTest, ManyAsyncDataNotPollable) {\n  {\n    std::vector<test::async_base_test_lib_detail::TestSpec> v;\n    for (int i = 0; i < 1000; i++) {\n      v.push_back(\n          {off_t(test::async_base_test_lib_detail::kODirectAlign * i),\n           test::async_base_test_lib_detail::kODirectAlign});\n    }\n    test::async_base_test_lib_detail::testReads<TypeParam>(\n        v, folly::AsyncBase::NOT_POLLABLE);\n  }\n}\n\nTYPED_TEST_P(AsyncTest, ManyAsyncDataPollable) {\n  {\n    std::vector<test::async_base_test_lib_detail::TestSpec> v;\n    for (int i = 0; i < 1000; i++) {\n      v.push_back(\n          {off_t(test::async_base_test_lib_detail::kODirectAlign * i),\n           test::async_base_test_lib_detail::kODirectAlign});\n    }\n    test::async_base_test_lib_detail::testReads<TypeParam>(\n        v, folly::AsyncBase::POLLABLE);\n  }\n}\n\nTYPED_TEST_P(AsyncTest, NonBlockingWait) {\n  TypeParam aioReader(1, folly::AsyncBase::NOT_POLLABLE);\n  typename TypeParam::Op op;\n  auto tempFile = folly::test::TempFileUtil::getTempFile(kDefaultFileSize);\n  int fd = ::open(tempFile.path().c_str(), O_DIRECT | O_RDONLY);\n  if (fd == -1) {\n    fd = ::open(tempFile.path().c_str(), O_RDONLY);\n  }\n  SKIP_IF(fd == -1) << \"Tempfile can't be opened: \" << folly::errnoStr(errno);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n  size_t size = 2 * test::async_base_test_lib_detail::kODirectAlign;\n  auto buf = test::async_base_test_lib_detail::TestUtil::allocateAligned(size);\n  op.pread(fd, buf.get(), size, 0);\n  aioReader.submit(&op);\n  EXPECT_EQ(aioReader.pending(), 1);\n\n  folly::Range<folly::AsyncBase::Op**> completed;\n  while (completed.empty()) {\n    // poll without blocking until the read request completes.\n    completed = aioReader.wait(0);\n  }\n  EXPECT_EQ(completed.size(), 1);\n\n  EXPECT_TRUE(completed[0] == &op);\n  ssize_t res = op.result();\n  EXPECT_LE(0, res) << folly::errnoStr(-res);\n  EXPECT_EQ(size, res);\n  EXPECT_EQ(aioReader.pending(), 0);\n}\n\nTYPED_TEST_P(AsyncTest, Cancel) {\n  constexpr size_t kNumOpsBatch1 = 10;\n  constexpr size_t kNumOpsBatch2 = 10;\n\n  TypeParam aioReader(\n      kNumOpsBatch1 + kNumOpsBatch2, folly::AsyncBase::NOT_POLLABLE);\n  auto tempFile = folly::test::TempFileUtil::getTempFile(kDefaultFileSize);\n  int fd = ::open(tempFile.path().c_str(), O_DIRECT | O_RDONLY);\n  if (fd == -1) {\n    fd = ::open(tempFile.path().c_str(), O_RDONLY);\n  }\n  SKIP_IF(fd == -1) << \"Tempfile can't be opened: \" << folly::errnoStr(errno);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n\n  size_t completed = 0;\n\n  std::vector<std::unique_ptr<folly::AsyncBase::Op>> ops;\n  std::vector<test::async_base_test_lib_detail::TestUtil::ManagedBuffer> bufs;\n  const auto schedule = [&](size_t n) {\n    for (size_t i = 0; i < n; ++i) {\n      const size_t size = 2 * test::async_base_test_lib_detail::kODirectAlign;\n      bufs.push_back(\n          test::async_base_test_lib_detail::TestUtil::allocateAligned(size));\n\n      ops.push_back(std::make_unique<typename TypeParam::Op>());\n      auto& op = *ops.back();\n\n      op.setNotificationCallback([&](folly::AsyncBaseOp*) { ++completed; });\n      op.pread(fd, bufs.back().get(), size, 0);\n      aioReader.submit(&op);\n    }\n  };\n\n  // Mix completed and canceled operations for this test.\n  // In order to achieve that, schedule in two batches and do partial\n  // wait() after the first one.\n\n  schedule(kNumOpsBatch1);\n  EXPECT_EQ(aioReader.pending(), kNumOpsBatch1);\n  EXPECT_EQ(completed, 0);\n\n  auto result = aioReader.wait(1);\n  EXPECT_GE(result.size(), 1);\n  EXPECT_EQ(completed, result.size());\n  EXPECT_EQ(aioReader.pending(), kNumOpsBatch1 - result.size());\n\n  schedule(kNumOpsBatch2);\n  EXPECT_EQ(aioReader.pending(), ops.size() - result.size());\n  EXPECT_EQ(completed, result.size());\n\n  auto canceled = aioReader.cancel();\n  EXPECT_EQ(canceled.size(), ops.size() - result.size());\n  EXPECT_EQ(aioReader.pending(), 0);\n  EXPECT_EQ(completed, result.size());\n\n  size_t foundCompleted = 0;\n  for (auto& op : ops) {\n    if (op->state() == folly::AsyncBaseOp::State::COMPLETED) {\n      ++foundCompleted;\n    } else {\n      EXPECT_TRUE(op->state() == folly::AsyncBaseOp::State::CANCELED) << *op;\n    }\n  }\n  EXPECT_EQ(foundCompleted, completed);\n}\n\n// batch tests\ntemplate <typename T>\nclass AsyncBatchTest : public ::testing::Test {};\nTYPED_TEST_SUITE_P(AsyncBatchTest);\n\nTYPED_TEST_P(AsyncBatchTest, BatchRead) {\n  TypeParam aioReader;\n  auto tempFile = folly::test::TempFileUtil::getTempFile(kDefaultFileSize);\n  int fd = ::open(tempFile.path().c_str(), O_DIRECT | O_RDONLY);\n  if (fd == -1) {\n    fd = ::open(tempFile.path().c_str(), O_RDONLY);\n  }\n  SKIP_IF(fd == -1) << \"Tempfile can't be opened: \" << folly::errnoStr(errno);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n\n  using OpPtr = folly::AsyncBaseOp*;\n  std::unique_ptr<typename TypeParam::Op[]> ops(\n      new typename TypeParam::Op[kBatchNumEntries]);\n\n  std::unique_ptr<OpPtr[]> opPtrs(new OpPtr[kBatchNumEntries]);\n  std::vector<folly::test::async_base_test_lib_detail::TestUtil::ManagedBuffer>\n      bufs;\n\n  bufs.reserve(kBatchNumEntries);\n\n  size_t completed = 0;\n\n  for (size_t i = 0; i < kBatchNumEntries; i++) {\n    bufs.push_back(\n        folly::test::async_base_test_lib_detail::TestUtil::allocateAligned(\n            kBatchBlockSize));\n    auto& op = ops[i];\n    opPtrs[i] = &op;\n\n    op.setNotificationCallback([&](folly::AsyncBaseOp*) { ++completed; });\n    op.pread(fd, bufs[i].get(), kBatchBlockSize, i * kBatchBlockSize);\n  }\n  aioReader.submit(\n      Range<AsyncBase::Op**>(opPtrs.get(), opPtrs.get() + kBatchNumEntries));\n  aioReader.wait(kBatchNumEntries);\n  CHECK_EQ(completed, kBatchNumEntries);\n}\n\n} // namespace async_base_test_lib_detail\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncIOTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncIO.h>\n#include <folly/io/async/test/AsyncBaseTestLib.h>\n\nusing folly::AsyncIO;\n\nnamespace folly {\nnamespace test {\nnamespace async_base_test_lib_detail {\n\nREGISTER_TYPED_TEST_SUITE_P(\n    AsyncTest,\n    ZeroAsyncDataNotPollable,\n    ZeroAsyncDataPollable,\n    SingleAsyncDataNotPollable,\n    SingleAsyncDataPollable,\n    MultipleAsyncDataNotPollable,\n    MultipleAsyncDataPollable,\n    ManyAsyncDataNotPollable,\n    ManyAsyncDataPollable,\n    NonBlockingWait,\n    Cancel);\n\nREGISTER_TYPED_TEST_SUITE_P(AsyncBatchTest, BatchRead);\n\nINSTANTIATE_TYPED_TEST_SUITE_P(AsyncTest, AsyncTest, AsyncIO);\n\nclass BatchAsyncIO : public AsyncIO {\n public:\n  BatchAsyncIO() : AsyncIO(kBatchNumEntries, folly::AsyncBase::NOT_POLLABLE) {}\n};\nINSTANTIATE_TYPED_TEST_SUITE_P(AsyncBatchTest, AsyncBatchTest, BatchAsyncIO);\n\n} // namespace async_base_test_lib_detail\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncIoUringSocketTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <array>\n#include <chrono>\n#include <map>\n#include <random>\n#include <vector>\n\n#include <folly/FileUtil.h>\n#include <folly/Subprocess.h>\n#include <folly/executors/GlobalExecutor.h>\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n#include <folly/io/async/AsyncIoUringSocket.h>\n#include <folly/io/async/AsyncServerSocket.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/io/async/IoUringEvent.h>\n#include <folly/io/async/test/AsyncSocketTest.h>\n#include <folly/io/async/test/AsyncSocketTest2.h>\n#include <folly/portability/GTest.h>\n#include <folly/system/Shell.h>\n#include <folly/test/SocketAddressTestHelper.h>\n\nnamespace folly {\n\nnamespace {\n\nstatic constexpr std::chrono::milliseconds kTimeout{30000};\nstatic constexpr size_t kBufferSize{1024};\n\nstd::string toString(std::unique_ptr<IOBuf> const& buf) {\n  if (!buf) {\n    return std::string();\n  }\n  auto coalesced = buf->coalesce();\n  return std::string((char const*)coalesced.data(), coalesced.size());\n}\n\nclass NullWriteCallback : public AsyncWriter::WriteCallback {\n public:\n  void writeSuccess() noexcept override {}\n\n  void writeErr(\n      size_t bytesWritten, const AsyncSocketException& ex) noexcept override {\n    LOG(FATAL) << \"writeErr wrote=\" << (int)bytesWritten << \" \" << ex;\n  }\n};\n\nstatic NullWriteCallback nullWriteCallback;\n\nclass FutureWriteCallback : public AsyncWriter::WriteCallback {\n public:\n  void writeSuccess() noexcept override {\n    auto& [promise, future] = promiseContract;\n    promise.setValue(Unit{});\n  }\n\n  void writeErr(\n      size_t bytesWritten, const AsyncSocketException& ex) noexcept override {\n    auto& [promise, future] = promiseContract;\n    promise.setValue(makeUnexpected(std::make_pair(bytesWritten, ex)));\n  }\n\n  using TResult = Expected<Unit, std::pair<size_t, AsyncSocketException>>;\n  SemiPromiseContract<TResult> promiseContract = makePromiseContract<TResult>();\n};\n\n} // namespace\n\nclass EchoTransport\n    : public AsyncReader::ReadCallback,\n      public AsyncWriter::WriteCallback {\n public:\n  explicit EchoTransport(AsyncSocketTransport::UniquePtr s, bool bm)\n      : transport(std::move(s)), bufferMovable(bm) {}\n\n  void start() { transport->setReadCB(this); }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = buff.data();\n    *lenReturn = buff.size();\n  }\n\n  void writeSuccess() noexcept override {}\n\n  void writeErr(\n      size_t bytesWritten, const AsyncSocketException& ex) noexcept override {\n    LOG_EVERY_N(ERROR, 10000) << \"writeErr \" << bytesWritten << \" \" << ex;\n    transport->close();\n  }\n\n  void readEOF() noexcept override {\n    VLOG(1) << \"Closing transport!\";\n    transport->close();\n  }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    LOG(ERROR) << \"readErr \" << ex;\n    transport->close();\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    VLOG(1) << \"readDataAvailable \" << len;\n    // have to copy as buff will be reused after\n    transport->writeChain(this, IOBuf::copyBuffer(buff.data(), len));\n  }\n\n  bool isBufferMovable() noexcept override { return bufferMovable; }\n\n  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {\n    VLOG(1) << \"readBuffer available \" << readBuf->computeChainDataLength();\n    transport->writeChain(this, std::move(readBuf));\n  }\n\n  AsyncSocketTransport::UniquePtr transport;\n  bool bufferMovable;\n  std::array<char, kBufferSize> buff;\n};\n\nclass CollectCallback : public AsyncReader::ReadCallback {\n public:\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = buff.data();\n    *lenReturn = buff.size();\n  }\n\n  void readEOF() noexcept override { clear(); }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    if (promise) {\n      promise->second.setException(ex);\n    }\n    clear();\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    VLOG(1) << \"CollectCallback::readDataAvailable \" << len;\n    if (hadBuffers.has_value() && *hadBuffers) {\n      LOG(FATAL) << \"must either send buffers or use getReadBuffer\";\n    }\n    hadBuffers = false;\n    data += std::string(buff.data(), len);\n    dataAvailable();\n  }\n\n  SemiFuture<std::string> waitFor(size_t n) {\n    auto [p, f] = makePromiseContract<std::string>();\n    promise = std::make_pair(n, std::move(p));\n    return std::move(f).within(kTimeout);\n  }\n\n  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {\n    if (hadBuffers.has_value() && !*hadBuffers) {\n      LOG(FATAL) << \"must either send buffers or use getReadBuffer\";\n    }\n    hadBuffers = true;\n\n    if (holdData) {\n      auto cloned = readBuf->clone();\n      if (bufData) {\n        bufData->appendToChain(std::move(readBuf));\n      } else {\n        bufData = std::move(readBuf);\n      }\n      readBuf = std::move(cloned);\n    }\n\n    data += toString(readBuf);\n    dataAvailable();\n  }\n\n  void clear() {\n    data.clear();\n    promise.reset();\n    bufData.reset();\n  }\n\n  void dataAvailable() {\n    VLOG(1) << \"CollectCallback::dataAvailable have=\" << data.size()\n            << \" want=\" << (promise ? static_cast<int>(promise->first) : -1);\n    if (!promise || promise->first > data.size()) {\n      return;\n    }\n    promise->second.setValue(data.substr(0, promise->first));\n    data.erase(0, promise->first);\n    promise.reset();\n  }\n\n  bool isBufferMovable() noexcept override { return true; }\n\n  void setHoldData(bool b) { holdData = b; }\n\n  std::optional<std::pair<size_t, Promise<std::string>>> promise;\n  std::shared_ptr<AsyncSocket> sock;\n  std::string data;\n  std::unique_ptr<IOBuf> bufData;\n  std::optional<bool> hadBuffers;\n  std::array<char, kBufferSize> buff;\n  bool holdData = true;\n};\n\nstruct TestParams {\n  bool ioUringServer = false;\n  bool ioUringClient = false;\n  bool manySmallBuffers = false;\n  bool supportBufferMovable = true;\n  bool sendzc = false;\n  bool registerFd = true;\n  std::string testName() const {\n    return folly::to<std::string>(\n        ioUringServer ? \"ioUringServer\" : \"oldServer\",\n        \"_\",\n        ioUringClient ? \"ioUringClient\" : \"oldClient\",\n        \"_\",\n        manySmallBuffers ? \"manySmallBuffers\" : \"oneBigBuffer\",\n        supportBufferMovable ? \"\" : \"_noSupportBufferMovable\",\n        sendzc ? \"_zerocopy\" : \"\",\n        \"_\",\n        registerFd ? \"\" : \"_noRegisterFd\",\n        \"iouringBackend\");\n  }\n};\n\nstruct ConnectedOptions {\n  std::string fastOpenInitial;\n  bool serverShouldRead = true;\n\n  ConnectedOptions withNoServerShouldRead() {\n    auto ret = *this;\n    ret.serverShouldRead = false;\n    return ret;\n  }\n\n  ConnectedOptions withFastOpen(std::string i) {\n    auto ret = *this;\n    ret.fastOpenInitial = std::move(i);\n    return ret;\n  }\n};\n\nclass AsyncIoUringSocketTest\n    : public ::testing::TestWithParam<TestParams>,\n      public AsyncServerSocket::AcceptCallback,\n      public AsyncSocket::ConnectCallback {\n public:\n  static IoUringBackend::Options ioOptions(TestParams const& p) {\n    IoUringBackend::Options options;\n    options.setUseRegisteredFds(p.registerFd ? 64 : 0);\n    if (p.manySmallBuffers) {\n      options.setInitialProvidedBuffers(1024, 2000);\n    } else {\n      options.setInitialProvidedBuffers(2000000, 1);\n    }\n    options.setDeferTaskRun(true);\n    return options;\n  }\n\n  EventBase::Options ioUringEbOptions() {\n    return EventBase::Options{}.setBackendFactory(\n        [p = GetParam()]() mutable -> std::unique_ptr<EventBaseBackendBase> {\n          return std::make_unique<IoUringBackend>(ioOptions(p));\n        });\n  }\n\n  EventBase::Options ebOptions() { return ioUringEbOptions(); }\n\n  void maybeSkip() {\n    if (unableToRun) {\n      GTEST_SKIP();\n    }\n  }\n\n  AsyncIoUringSocketTest() {\n    try {\n      base = std::make_unique<EventBase>(ebOptions());\n    } catch (IoUringBackend::NotAvailable const&) {\n      unableToRun = true;\n      return;\n    }\n\n    backend = dynamic_cast<IoUringBackend*>(base->getBackend());\n\n    backend->loopPoll(); // init delayed bits as this is the only thread\n\n    serverSocket = AsyncServerSocket::newSocket(base.get());\n    serverSocket->setTFOEnabled(true, 1);\n    serverSocket->bind(0);\n    serverSocket->listen(1024);\n    serverSocket->addAcceptCallback(this, nullptr);\n    serverSocket->startAccepting();\n    serverSocket->getAddress(&serverAddress);\n  }\n\n  void connectionAccepted(\n      NetworkSocket ns, const SocketAddress&, AcceptInfo) noexcept override {\n    fdPromise.setValue(ns);\n  }\n\n  void connectSuccess() noexcept override {}\n  void connectErr(const AsyncSocketException& ex) noexcept override {\n    LOG(FATAL) << ex;\n  }\n\n  template <typename ServerReadCallback>\n  struct Connected {\n    std::unique_ptr<EchoTransport> client;\n    AsyncTransport::UniquePtr server;\n    std::unique_ptr<ServerReadCallback> callback;\n    ~Connected() {\n      if (server) {\n        server->setReadCB(nullptr);\n      }\n      if (client && client->transport) {\n        client->transport->closeNow();\n      }\n    }\n  };\n\n  AsyncIoUringSocket::Options ioUringSocketOptions() const {\n    AsyncIoUringSocket::Options ret;\n    if (GetParam().sendzc) {\n      ret.zeroCopyEnable = [](auto&&) { return true; };\n    }\n    return ret;\n  }\n\n  template <typename ServerReadCallback = CollectCallback>\n  Connected<ServerReadCallback> makeConnected(\n      ConnectedOptions options = ConnectedOptions{}) {\n    AsyncSocketTransport::UniquePtr client;\n    if (GetParam().ioUringClient) {\n      client = AsyncSocketTransport::UniquePtr(\n          new AsyncIoUringSocket(base.get(), ioUringSocketOptions()));\n    } else {\n      client =\n          AsyncSocketTransport::UniquePtr(AsyncSocket::newSocket(base.get()));\n    }\n\n    if (options.fastOpenInitial.size()) {\n      client->enableTFO();\n    }\n    client->connect(this, serverAddress);\n    if (options.fastOpenInitial.size()) {\n      client->writeChain(\n          &nullWriteCallback, IOBuf::copyBuffer(options.fastOpenInitial));\n    }\n\n    auto fd =\n        fdPromise.getFuture()\n            .within(kTimeout)\n            .via(base.get())\n            .getVia(base.get());\n    fdPromise = {};\n    auto c = std::make_unique<EchoTransport>(\n        std::move(client), GetParam().supportBufferMovable);\n    c->start();\n    auto serverReadCallback = std::make_unique<ServerReadCallback>();\n    AsyncTransport::UniquePtr server = GetParam().ioUringServer\n        ? AsyncTransport::UniquePtr(new AsyncIoUringSocket(\n              AsyncSocket::newSocket(base.get(), fd), ioUringSocketOptions()))\n        : AsyncTransport::UniquePtr(AsyncSocket::newSocket(base.get(), fd));\n    if (options.serverShouldRead) {\n      server->setReadCB(serverReadCallback.get());\n    }\n    return Connected<ServerReadCallback>{\n        std::move(c), std::move(server), std::move(serverReadCallback)};\n  }\n\n  bool unableToRun = false;\n  std::unique_ptr<EventBase> base;\n  std::unique_ptr<IoUringEvent> ioUringEvent;\n  std::shared_ptr<AsyncServerSocket> serverSocket;\n  IoUringBackend* backend = nullptr;\n  SocketAddress serverAddress;\n\n  Promise<NetworkSocket> fdPromise;\n};\n\n#define MAYBE_SKIP()                   \\\n  if (unableToRun) {                   \\\n    LOG(INFO) << \"Unsupported kernel\"; \\\n    return;                            \\\n  }\n\nTEST_P(AsyncIoUringSocketTest, ConnectTimeout) {\n  MAYBE_SKIP();\n  struct CB : AsyncSocket::ConnectCallback {\n    void connectSuccess() noexcept override {\n      prom.setValue(makeExpected<AsyncSocketException>(Unit{}));\n    }\n    void connectErr(const AsyncSocketException& ex) noexcept override {\n      prom.setValue(makeUnexpected(ex));\n    }\n    Promise<Expected<Unit, AsyncSocketException>> prom;\n  } cb;\n\n  // Try connecting to server that won't respond.\n  //\n  // This depends somewhat on the network where this test is run.\n  // Hopefully this IP will be routable but unresponsive.\n  // (Alternatively, we could try listening on a local raw socket, but that\n  // normally requires root privileges.)\n  auto host = SocketAddressTestHelper::isIPv6Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv6\n      : SocketAddressTestHelper::isIPv4Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv4\n      : nullptr;\n\n  AsyncIoUringSocket::UniquePtr socket(new AsyncIoUringSocket(base.get()));\n  socket->connect(\n      &cb, SocketAddress{host, 65535}, std::chrono::milliseconds(1));\n\n  auto res =\n      cb.prom.getSemiFuture()\n          .within(kTimeout)\n          .via(base.get())\n          .getVia(base.get());\n  ASSERT_FALSE(res);\n  if (res.error().getType() == AsyncSocketException::NOT_OPEN) {\n    // This can happen if we could not route to the IP address picked above.\n    // In this case the connect will fail immediately rather than timing out.\n    // Just skip the test in this case.\n    GTEST_SKIP() << \"do not have a routable but unreachable IP address\";\n  }\n  EXPECT_EQ(res.error().getType(), AsyncSocketException::TIMED_OUT)\n      << res.error().what();\n}\n\nTEST_P(AsyncIoUringSocketTest, EoF) {\n  MAYBE_SKIP();\n  struct CB : AsyncReader::ReadCallback {\n    void readDataAvailable(size_t) noexcept override {\n      // will terminate...\n      terminate_with<std::runtime_error>(\"unexpected data\");\n    }\n    void readEOF() noexcept override {\n      VLOG(1) << \"CB Setting EOF\";\n      prom.setValue(makeExpected<AsyncSocketException>(Unit{}));\n    }\n\n    void readErr(const AsyncSocketException& ex) noexcept override {\n      VLOG(1) << \"CB Setting Err \" << ex;\n      prom.setValue(makeUnexpected(ex));\n    }\n\n    void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n      *bufReturn = &buff;\n      *lenReturn = 1;\n    }\n\n    Promise<Expected<Unit, AsyncSocketException>> prom;\n    char buff;\n  };\n\n  auto c = makeConnected(ConnectedOptions{}.withNoServerShouldRead());\n  {\n    CB cb_eof;\n    c.server->setReadCB(&cb_eof);\n    c.client->transport->closeNow();\n    c.client.reset();\n    EXPECT_TRUE(\n        cb_eof.prom.getSemiFuture()\n            .within(kTimeout)\n            .via(base.get())\n            .getVia(base.get())\n            .hasValue());\n    c.server->setReadCB(nullptr);\n  }\n  EXPECT_FALSE(c.server->good());\n  {\n    CB cb_invalid;\n    c.server->setReadCB(&cb_invalid);\n    auto ex =\n        cb_invalid.prom.getSemiFuture()\n            .within(kTimeout)\n            .via(base.get())\n            .getVia(base.get());\n    ASSERT_TRUE(ex.hasError());\n    auto er = ex.error();\n    EXPECT_EQ(AsyncSocketException::NOT_OPEN, er.getType());\n    c.server->setReadCB(nullptr);\n  }\n}\n\nstruct DetachCB : folly::AsyncDetachFdCallback {\n  void fdDetached(\n      NetworkSocket ns, std::unique_ptr<IOBuf> unread) noexcept override {\n    promise.setValue(std::make_pair(ns, std::move(unread)));\n  }\n\n  void fdDetachFail(const AsyncSocketException& ex) noexcept override {\n    promise.setException(ex);\n  }\n\n  folly::Promise<std::pair<NetworkSocket, std::unique_ptr<IOBuf>>> promise;\n};\n\nTEST_P(AsyncIoUringSocketTest, Detach) {\n  MAYBE_SKIP();\n  auto c = makeConnected();\n  auto* was = c.server->getUnderlyingTransport<folly::AsyncIoUringSocket>();\n  ASSERT_NE(was, nullptr);\n\n  // write something\n  c.client->transport->write(&nullWriteCallback, \"hello\", 5);\n  // make sure it gets run\n  backend->submitOutstanding();\n\n  // sleep a bit to get the read all the way into the completion queue\n  /* sleep override */ std::this_thread::sleep_for(\n      std::chrono::milliseconds(5));\n\n  // now detach before the write is completed\n  DetachCB cb;\n  was->asyncDetachFd(&cb);\n  ASSERT_FALSE(cb.promise.isFulfilled()) << \"must wait for read to finish\";\n\n  auto res =\n      cb.promise.getSemiFuture()\n          .within(kTimeout)\n          .via(base.get())\n          .getVia(base.get());\n  EXPECT_GE(res.first.toFd(), 0);\n  if (res.second) {\n    // did not cancel in time\n    EXPECT_EQ(\"hello\", toString(res.second));\n  } else {\n    // did cancel in time\n    char buff[128];\n    memset(buff, 0, sizeof(buff));\n    int ret;\n    do {\n      ret = fileops::read(res.first.toFd(), &buff, sizeof(buff));\n    } while (ret == -1 && errno == EINTR);\n    ASSERT_EQ(5, ret);\n    EXPECT_EQ(\"hello\", std::string(buff));\n  }\n}\n\nTEST_P(AsyncIoUringSocketTest, DetachEventBase) {\n  MAYBE_SKIP();\n  auto c = makeConnected();\n\n  // write something\n  FutureWriteCallback fwc;\n  auto* transport = c.client->transport.get();\n  transport->write(&fwc, \"hello\", 5);\n\n  // make sure it gets run\n  backend->submitOutstanding();\n  ASSERT_TRUE(transport->isDetachable());\n  transport->detachEventBase();\n\n  EventBase newBase{ioUringEbOptions()};\n  transport->attachEventBase(&newBase);\n\n  auto resFut = c.callback->waitFor(5).via(folly::getGlobalCPUExecutor().get());\n  auto start = std::chrono::steady_clock::now();\n  do {\n    newBase.loopOnce(EVLOOP_NONBLOCK);\n    base->loopOnce(EVLOOP_NONBLOCK);\n    /* sleep override */ std::this_thread::sleep_for(\n        std::chrono::milliseconds(20));\n    auto res = resFut.poll();\n    if (res) {\n      EXPECT_EQ(\"hello\", res->value());\n      break;\n    }\n    if (std::chrono::steady_clock::now() > start + std::chrono::seconds(1)) {\n      FAIL();\n    }\n  } while (true);\n\n  // make sure write arrived, it should be on the new event base\n  auto& [promise, future] = fwc.promiseContract;\n  ASSERT_TRUE(std::move(future).via(&newBase).getVia(&newBase).hasValue());\n\n  ASSERT_TRUE(transport->isDetachable());\n  transport->detachEventBase();\n  transport->attachEventBase(base.get());\n}\n\nTEST_P(AsyncIoUringSocketTest, DetachEventBaseClear) {\n  MAYBE_SKIP();\n  auto c = makeConnected();\n\n  // write something\n  c.client->transport->write(&nullWriteCallback, \"hello\", 5);\n\n  backend->submitOutstanding();\n  ASSERT_TRUE(c.client->transport->isDetachable());\n  c.client->transport->detachEventBase();\n\n  // now free things in the middle\n  base->loopOnce(EVLOOP_NONBLOCK);\n}\n\nTEST_P(AsyncIoUringSocketTest, FastOpen) {\n  MAYBE_SKIP();\n  bool had_fastopen = false;\n  bool can_fastopen = false;\n  std::string fo;\n  if (readFile(\"/proc/sys/net/ipv4/tcp_fastopen\", fo)) {\n    auto fast_open = folly::to<int>(fo);\n    if (fast_open == 3) {\n      can_fastopen = true;\n    }\n  }\n  if (!can_fastopen) {\n    LOG(INFO) << \"/proc/sys/net/ipv4/tcp_fastopen must be 3 to do fastopen, \"\n                 \"but we will test the code flow anyway\";\n  }\n  // technically we could run ip tcp_metrics flush here, but messing with the\n  // system in a test is awful\n  folly::Subprocess subProc(\"/sbin/ip tcp_metrics show ::1\"_shellify());\n  int has_cookies_already = subProc.wait().exitStatus();\n  if (has_cookies_already == 0) {\n    LOG(INFO)\n        << \"already had cookies, so cannot do fastopen test, but will test code flow anyway. \"\n        << \" you could do a `/sbin/ip tcp_metrics flush` to test this\";\n    had_fastopen = true;\n  }\n  auto opts = ConnectedOptions{}.withFastOpen(\"hello\");\n  {\n    auto conn = makeConnected(opts);\n    EXPECT_EQ(\n        \"hello\", conn.callback->waitFor(5).via(base.get()).getVia(base.get()));\n    if (!had_fastopen) {\n      EXPECT_FALSE(conn.client->transport->getTFOSucceeded());\n    }\n  }\n  {\n    auto conn = makeConnected(opts);\n    EXPECT_EQ(\n        \"hello\", conn.callback->waitFor(5).via(base.get()).getVia(base.get()));\n    if (can_fastopen) {\n      EXPECT_TRUE(conn.client->transport->getTFOSucceeded());\n    }\n  }\n}\n\nTEST_P(AsyncIoUringSocketTest, BindAddressNoPort) {\n  EventBase eventBase;\n  test::TestServer server(true);\n\n  // When setBindAddressNoPort is disabled, verifies that a port is assigned\n  // before the connect call\n  AsyncIoUringSocket::UniquePtr socket(new AsyncIoUringSocket(base.get()));\n  socket->setBindAddressNoPort(false);\n  SocketAddress bindAddr(\"127.0.0.1\", 0);\n  test::TestPortAssignmentCallback callback;\n  socket->connect(\n      &callback,\n      server.getAddress(),\n      std::chrono::milliseconds(30),\n      emptySocketOptionMap,\n      bindAddr);\n  eventBase.loop();\n  EXPECT_NE(callback.assignedPort, 0);\n  socket->close();\n}\n\nTEST_P(AsyncIoUringSocketTest, ConnectSuccessStateEstablished) {\n  MAYBE_SKIP();\n\n  struct ConnectCB : AsyncSocket::ConnectCallback {\n    explicit ConnectCB(AsyncIoUringSocket* s) : socket(s) {}\n    void connectSuccess() noexcept override { prom.setValue(socket->good()); }\n    void connectErr(const AsyncSocketException& ex) noexcept override {\n      prom.setException(ex);\n    }\n\n    Promise<bool> prom;\n    AsyncIoUringSocket* socket;\n  };\n\n  AsyncIoUringSocket::UniquePtr socket(\n      new AsyncIoUringSocket(base.get(), ioUringSocketOptions()));\n\n  ConnectCB cb(socket.get());\n  socket->connect(&cb, serverAddress);\n  auto fd =\n      fdPromise.getFuture().within(kTimeout).via(base.get()).getVia(base.get());\n  fdPromise = {};\n  auto server = AsyncSocket::newSocket(base.get(), fd);\n\n  auto isGood =\n      cb.prom.getSemiFuture()\n          .within(kTimeout)\n          .via(base.get())\n          .getVia(base.get());\n  EXPECT_TRUE(isGood);\n}\n\nTEST_P(AsyncIoUringSocketTest, ConnectWithBoundFd) {\n  MAYBE_SKIP();\n\n  struct ConnectCB : AsyncSocket::ConnectCallback {\n    explicit ConnectCB(AsyncIoUringSocket* s) : socket(s) {}\n    void connectSuccess() noexcept override { prom.setValue(socket->good()); }\n    void connectErr(const AsyncSocketException& ex) noexcept override {\n      prom.setException(ex);\n    }\n\n    Promise<bool> prom;\n    AsyncIoUringSocket* socket;\n  };\n\n  int sockfd = ::socket(serverAddress.getFamily(), SOCK_STREAM, 0);\n\n  uint16_t boundPort = 0;\n  for (uint16_t port = 1024; port <= 65535; port++) {\n    struct sockaddr_storage bindStorage = {};\n    socklen_t bindLen;\n    if (serverAddress.getFamily() == AF_INET6) {\n      auto* sa = reinterpret_cast<struct sockaddr_in6*>(&bindStorage);\n      sa->sin6_family = AF_INET6;\n      sa->sin6_port = htons(port);\n      sa->sin6_addr = in6addr_any;\n      bindLen = sizeof(struct sockaddr_in6);\n    } else {\n      auto* sa = reinterpret_cast<struct sockaddr_in*>(&bindStorage);\n      sa->sin_family = AF_INET;\n      sa->sin_port = htons(port);\n      sa->sin_addr.s_addr = INADDR_ANY;\n      bindLen = sizeof(struct sockaddr_in);\n    }\n    if (::bind(\n            sockfd,\n            reinterpret_cast<struct sockaddr*>(&bindStorage),\n            bindLen) == 0) {\n      boundPort = port;\n      break;\n    }\n  }\n  if (boundPort == 0) {\n    GTEST_SKIP() << \"Unable to find a free port to bind() to\";\n  }\n\n  AsyncIoUringSocket::UniquePtr socket(\n      new AsyncIoUringSocket(base.get(), ioUringSocketOptions()));\n  ConnectCB cb(socket.get());\n  socket->connect(\n      &cb,\n      serverAddress,\n      std::chrono::milliseconds(0),\n      folly::emptySocketOptionMap,\n      folly::AsyncSocketTransport::BindOptions{NetworkSocket(sockfd)});\n\n  auto fd =\n      fdPromise.getFuture().within(kTimeout).via(base.get()).getVia(base.get());\n  fdPromise = {};\n  auto server = AsyncSocket::newSocket(base.get(), fd);\n\n  auto isGood =\n      cb.prom.getSemiFuture()\n          .within(kTimeout)\n          .via(base.get())\n          .getVia(base.get());\n  EXPECT_TRUE(isGood);\n\n  SocketAddress localAddr;\n  socket->getLocalAddress(&localAddr);\n  EXPECT_EQ(localAddr.getPort(), boundPort);\n}\n\nTEST_P(AsyncIoUringSocketTest, ConnectWithUnboundFd) {\n  MAYBE_SKIP();\n\n  struct ConnectCB : AsyncSocket::ConnectCallback {\n    explicit ConnectCB(AsyncIoUringSocket* s) : socket(s) {}\n    void connectSuccess() noexcept override { prom.setValue(socket->good()); }\n    void connectErr(const AsyncSocketException& ex) noexcept override {\n      prom.setException(ex);\n    }\n\n    Promise<bool> prom;\n    AsyncIoUringSocket* socket;\n  };\n\n  int sockfd = ::socket(serverAddress.getFamily(), SOCK_STREAM, 0);\n  ASSERT_GE(sockfd, 0);\n\n  AsyncIoUringSocket::UniquePtr socket(\n      new AsyncIoUringSocket(base.get(), ioUringSocketOptions()));\n  ConnectCB cb(socket.get());\n  socket->connect(\n      &cb,\n      serverAddress,\n      std::chrono::milliseconds(0),\n      folly::emptySocketOptionMap,\n      folly::AsyncSocketTransport::BindOptions{NetworkSocket(sockfd)});\n\n  auto fd =\n      fdPromise.getFuture().within(kTimeout).via(base.get()).getVia(base.get());\n  fdPromise = {};\n  auto server = AsyncSocket::newSocket(base.get(), fd);\n\n  auto isGood =\n      cb.prom.getSemiFuture()\n          .within(kTimeout)\n          .via(base.get())\n          .getVia(base.get());\n  EXPECT_TRUE(isGood);\n\n  SocketAddress localAddr;\n  socket->getLocalAddress(&localAddr);\n  EXPECT_GT(localAddr.getPort(), 0);\n}\n\nTEST_P(AsyncIoUringSocketTest, ConnectWithAlreadyConnectedFd) {\n  MAYBE_SKIP();\n\n  struct ConnectCB : AsyncSocket::ConnectCallback {\n    void connectSuccess() noexcept override {\n      prom.setValue(makeExpected<AsyncSocketException>(Unit{}));\n    }\n    void connectErr(const AsyncSocketException& ex) noexcept override {\n      prom.setValue(makeUnexpected(ex));\n    }\n\n    Promise<Expected<Unit, AsyncSocketException>> prom;\n  };\n\n  int connectedFd = ::socket(serverAddress.getFamily(), SOCK_STREAM, 0);\n  struct sockaddr_storage ss{};\n  serverAddress.getAddress(&ss);\n  ASSERT_EQ(\n      ::connect(\n          connectedFd, (struct sockaddr*)&ss, serverAddress.getActualSize()),\n      0);\n\n  auto acceptedFd =\n      fdPromise.getFuture().within(kTimeout).via(base.get()).getVia(base.get());\n  fdPromise = {};\n  ::close(acceptedFd.toFd());\n\n  AsyncIoUringSocket::UniquePtr socket(\n      new AsyncIoUringSocket(base.get(), ioUringSocketOptions()));\n  ConnectCB cb;\n  socket->connect(\n      &cb,\n      serverAddress,\n      std::chrono::milliseconds(0),\n      folly::emptySocketOptionMap,\n      folly::AsyncSocketTransport::BindOptions{NetworkSocket(connectedFd)});\n\n  auto res =\n      cb.prom.getSemiFuture()\n          .within(kTimeout)\n          .via(base.get())\n          .getVia(base.get());\n  ASSERT_FALSE(res);\n  EXPECT_EQ(res.error().getType(), AsyncSocketException::INVALID_STATE);\n  EXPECT_EQ(fcntl(connectedFd, F_GETFD), -1);\n  EXPECT_EQ(errno, EBADF);\n}\n\nTEST_P(AsyncIoUringSocketTest, ReadCallbackSetDuringConnect) {\n  MAYBE_SKIP();\n\n  struct ReadCB : public AsyncReader::ReadCallback {\n    void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n      *bufReturn = buff;\n      *lenReturn = sizeof(buff);\n    }\n\n    void readDataAvailable(size_t len) noexcept override {\n      prom.setValue(std::string{buff, len});\n    }\n\n    void readEOF() noexcept override {}\n    void readErr(const AsyncSocketException& ex) noexcept override {\n      prom.setException(ex);\n    }\n\n    bool isBufferMovable() noexcept override { return false; }\n\n    Promise<std::string> prom;\n    char buff[1024];\n  };\n\n  ReadCB readCB;\n  AsyncIoUringSocket::UniquePtr socket(\n      new AsyncIoUringSocket(base.get(), ioUringSocketOptions()));\n\n  socket->connect(this, serverAddress);\n  socket->setReadCB(&readCB);\n  auto fd =\n      fdPromise.getFuture().within(kTimeout).via(base.get()).getVia(base.get());\n  fdPromise = {};\n  auto server = AsyncSocket::newSocket(base.get(), fd);\n  server->write(&nullWriteCallback, \"hello\", 5);\n  auto result =\n      readCB.prom.getSemiFuture()\n          .within(kTimeout)\n          .via(base.get())\n          .getVia(base.get());\n  EXPECT_EQ(\"hello\", result);\n}\n\nclass AsyncIoUringSocketTestAll : public AsyncIoUringSocketTest {};\n\nTEST_P(AsyncIoUringSocketTestAll, WriteChain2) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  s->writeChain(&nullWriteCallback, IOBuf::copyBuffer(\"hello\"));\n  EXPECT_EQ(\"hello\", cb->waitFor(5).via(base.get()).getVia(base.get()));\n  s->writeChain(&nullWriteCallback, IOBuf::copyBuffer(\"there\"));\n  EXPECT_EQ(\"there\", cb->waitFor(5).via(base.get()).getVia(base.get()));\n}\n\nTEST_P(AsyncIoUringSocketTestAll, WriteChainOrder) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  auto chain = IOBuf::copyBuffer(\"h\");\n  chain->appendToChain(IOBuf::copyBuffer(\"e\"));\n  chain->appendToChain(IOBuf::copyBuffer(\"ll\"));\n  chain->appendToChain(IOBuf::copyBuffer(\"o\"));\n  s->writeChain(&nullWriteCallback, std::move(chain));\n  EXPECT_EQ(\"hello\", cb->waitFor(5).via(base.get()).getVia(base.get()));\n}\n\nTEST_P(AsyncIoUringSocketTestAll, WriteChainLong) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  auto chain = IOBuf::copyBuffer(\"?\");\n  std::string res = \"?\";\n  for (int i = 0; i < 4096; i++) {\n    std::string x(1, 'a' + i % 26);\n    chain->appendToChain(IOBuf::copyBuffer(x));\n    res += x;\n  }\n  s->writeChain(&nullWriteCallback, std::move(chain));\n  EXPECT_EQ(res, cb->waitFor(res.size()).via(base.get()).getVia(base.get()));\n}\n\nTEST_P(AsyncIoUringSocketTestAll, Write) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  s->write(&nullWriteCallback, \"hello\", 5);\n  EXPECT_EQ(\"hello\", cb->waitFor(5).via(base.get()).getVia(base.get()));\n}\n\nTEST_P(AsyncIoUringSocketTestAll, WriteAfterWait) {\n  MAYBE_SKIP();\n  auto conn = makeConnected();\n  auto& s = conn.server;\n  auto& cb = conn.callback;\n\n  EXPECT_EQ(\n      \"hello\",\n      folly::futures::sleep(std::chrono::milliseconds(500))\n          .via(base.get())\n          .thenValue([&](auto&&) { s->write(&nullWriteCallback, \"hello\", 5); })\n          .thenValue([&](auto&&) { return cb->waitFor(5); })\n          .getVia(base.get()));\n}\n\nnamespace {\nstd::string randomString(size_t n) {\n  std::random_device r;\n  std::default_random_engine e1(r());\n\n  std::uniform_int_distribution<int8_t> uniform_dist('A', 'Z');\n\n  std::string ret;\n  ret.reserve(n);\n  for (size_t i = 0; i < n; i++) {\n    ret.push_back(uniform_dist(e1));\n  }\n  return ret;\n}\n} // namespace\n\nTEST_P(AsyncIoUringSocketTestAll, WriteBig) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  cb->setHoldData(true);\n  std::string big = randomString(4000000);\n  s->write(&nullWriteCallback, big.c_str(), big.size());\n  auto res = cb->waitFor(big.size()).via(base.get()).getVia(base.get());\n  EXPECT_TRUE(big == res) << big.size() << \" vs \" << res.size();\n}\n\nTEST_P(AsyncIoUringSocketTestAll, WriteBigChunked) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  cb->setHoldData(true);\n  std::string big = randomString(4000000);\n  size_t at = 0;\n  int const kChunkSize = 256;\n  while (at < big.size()) {\n    auto len = std::min<size_t>(big.size() - at, kChunkSize);\n    s->write(&nullWriteCallback, big.c_str() + at, len);\n    at += len;\n  }\n  auto res = cb->waitFor(big.size()).via(base.get()).getVia(base.get());\n  EXPECT_TRUE(big == res) << big.size() << \" vs \" << res.size();\n}\n\nTEST_P(AsyncIoUringSocketTestAll, WriteBigDrop) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  cb->setHoldData(false); // should trigger overflow in provided buffers\n  std::string big(4000000, 'X');\n  s->write(&nullWriteCallback, big.c_str(), big.size());\n  EXPECT_EQ(big, cb->waitFor(big.size()).via(base.get()).getVia(base.get()));\n}\n\nTEST_P(AsyncIoUringSocketTestAll, Writev) {\n  MAYBE_SKIP();\n  auto [e, s, cb] = makeConnected();\n  std::array<iovec, 2> iov = {{{(void*)\"hel\", 3}, {(void*)\"lo\", 2}}};\n  s->writev(&nullWriteCallback, iov.data(), iov.size());\n  EXPECT_EQ(\"hello\", cb->waitFor(5).via(base.get()).getVia(base.get()));\n}\n\nTEST_P(AsyncIoUringSocketTestAll, SendTimeout) {\n  MAYBE_SKIP();\n  if (!GetParam().ioUringServer) {\n    // folly::AsyncSocket is not totally reliable with timeouts\n    return;\n  }\n  auto conn = makeConnected(ConnectedOptions{}.withNoServerShouldRead());\n  FutureWriteCallback ecb;\n  std::string big(40000000, 'X');\n  std::vector<iovec> iov;\n  iov.resize(100);\n  for (size_t i = 0; i < iov.size(); i++) {\n    iov[i].iov_base = big.data();\n    iov[i].iov_len = big.size();\n  }\n  base->runInEventBaseThread([&]() {\n    conn.server->setSendTimeout(1);\n    conn.server->writev(&ecb, iov.data(), iov.size());\n  });\n  auto& [promise, future] = ecb.promiseContract;\n  auto ex = std::move(future).via(base.get()).getVia(base.get());\n  ASSERT_TRUE(ex.hasError());\n  EXPECT_EQ(AsyncSocketException::TIMED_OUT, ex.error().second.getType());\n}\n\nauto mkAllTestParams() {\n  std::vector<TestParams> t;\n\n  auto addFeatureCases = [&](TestParams const& base) {\n    TestParams all = base;\n\n    // add test cases where each feature is not the default, as well as one\n    // where all the features are not the default\n    auto add_flip_case = [&](auto ptr) {\n      auto tc = base;\n      tc.*ptr = all.*ptr = !(tc.*ptr);\n      t.push_back(tc);\n    };\n\n    add_flip_case(&TestParams::registerFd);\n    if (IoUringBackend::kernelSupportsSendZC()) {\n      add_flip_case(&TestParams::sendzc);\n    }\n    add_flip_case(&TestParams::supportBufferMovable);\n    t.push_back(all);\n  };\n\n  for (bool server : {false, true}) {\n    for (bool client : {false, true}) {\n      for (bool manySmallBuffers : {false, true}) {\n        TestParams base;\n        base.ioUringServer = server;\n        base.ioUringClient = client;\n        base.manySmallBuffers = manySmallBuffers;\n        t.push_back(base);\n\n        // only expand feature flags in some cases to reduce the massive\n        // explosion of tests\n        if (server && client) {\n          addFeatureCases(base);\n        }\n      }\n    }\n  }\n  return t;\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    AsyncIoUringSocketTest,\n    AsyncIoUringSocketTestAll,\n    ::testing::ValuesIn(mkAllTestParams()),\n    [](const ::testing::TestParamInfo<TestParams>& info) {\n      return info.param.testName();\n    });\n\nTestParams mkBasicTestParams() {\n  TestParams t;\n  t.ioUringClient = t.ioUringServer = true;\n  return t;\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    AsyncIoUringSocketTest,\n    AsyncIoUringSocketTest,\n    ::testing::Values(mkBasicTestParams()),\n    [](const ::testing::TestParamInfo<TestParams>& info) {\n      return info.param.testName();\n    });\n\nclass AsyncIoUringSocketTakeoverTest : public AsyncIoUringSocketTest {};\n\nclass AsyncSocketWithPreRead : public AsyncSocket {\n public:\n  AsyncSocketWithPreRead(AsyncSocket::UniquePtr a, std::string const& pre_read)\n      : AsyncSocket(std::move(a)) {\n    preReceivedData_ = IOBuf::copyBuffer(pre_read);\n  }\n};\n\nTEST_P(AsyncIoUringSocketTakeoverTest, PreRead) {\n  MAYBE_SKIP();\n  auto conn = makeConnected(ConnectedOptions{}.withNoServerShouldRead());\n  AsyncSocket::UniquePtr sock(\n      dynamic_cast<AsyncSocket*>(conn.server.release()));\n  ASSERT_NE(sock, nullptr);\n  AsyncIoUringSocket::UniquePtr io_uring(new AsyncIoUringSocket(\n      AsyncSocket::UniquePtr(\n          new AsyncSocketWithPreRead(std::move(sock), \"hello\"))));\n  io_uring->setReadCB(conn.callback.get());\n  io_uring->write(&nullWriteCallback, \"there\", 5);\n  EXPECT_EQ(\n      \"hellothere\",\n      conn.callback->waitFor(10).via(base.get()).getVia(base.get()));\n}\n\nTestParams mkTakeoverParams() {\n  TestParams t;\n  t.ioUringClient = t.ioUringServer = false;\n  return t;\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    AsyncIoUringSocketTakeoverTest,\n    AsyncIoUringSocketTakeoverTest,\n    ::testing::Values(mkTakeoverParams()),\n    [](const ::testing::TestParamInfo<TestParams>& info) {\n      return info.param.testName();\n    });\n\nTEST(AsyncIoUringSocketTest, RemoveNonBlockFlag) {\n  Promise<NetworkSocket> fdPromise;\n  test::TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&fdPromise](NetworkSocket fd, const folly::SocketAddress& /*addr*/) {\n        fdPromise.setValue(fd);\n      });\n\n  auto options = IoUringBackend::Options{};\n  options.setUseRegisteredFds(64)\n      .setInitialProvidedBuffers(64, 8)\n      .setDeferTaskRun(true);\n  EventBase evb{EventBase::Options{}.setBackendFactory(\n      [&options]() -> std::unique_ptr<EventBaseBackendBase> {\n        return std::make_unique<IoUringBackend>(std::move(options));\n      })};\n\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&evb));\n  serverSocket->bind(0);\n  serverSocket->listen(1024);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n  serverSocket->addAcceptCallback(&acceptCallback, &evb);\n  serverSocket->startAccepting();\n\n  AsyncSocket::UniquePtr clientSocket(AsyncSocket::newSocket(&evb));\n  clientSocket->connect(nullptr, serverAddress);\n\n  auto fd = fdPromise.getFuture().within(kTimeout).via(&evb).getVia(&evb);\n  AsyncSocket::UniquePtr acceptedSocket(AsyncSocket::newSocket(&evb, fd));\n\n  int flags = fcntl(fd.toFd(), F_GETFL, 0);\n  ASSERT_GE(flags, 0);\n  EXPECT_EQ(flags & O_NONBLOCK, O_NONBLOCK);\n\n  AsyncIoUringSocket::UniquePtr ioUringSocket(\n      new AsyncIoUringSocket(std::move(acceptedSocket)));\n\n  int newFlags = fcntl(fd.toFd(), F_GETFL, 0);\n  ASSERT_GE(newFlags, 0);\n  EXPECT_EQ(newFlags & O_NONBLOCK, 0);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncPipeTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncPipe.h>\n\n#include <fcntl.h>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Unistd.h>\n\nusing namespace testing;\n\nnamespace {\n\nclass TestReadCallback : public folly::AsyncReader::ReadCallback {\n public:\n  bool isBufferMovable() noexcept override { return movable_; }\n  void setMovable(bool movable) { movable_ = movable; }\n\n  void readBufferAvailable(\n      std::unique_ptr<folly::IOBuf> readBuf) noexcept override {\n    readBuffer_.append(std::move(readBuf));\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    readBuffer_.postallocate(len);\n  }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) noexcept override {\n    auto res = readBuffer_.preallocate(4000, 65000);\n    *bufReturn = res.first;\n    *lenReturn = res.second;\n  }\n\n  void readEOF() noexcept override {}\n\n  void readErr(const folly::AsyncSocketException&) noexcept override {\n    error_ = true;\n  }\n\n  std::string getData() {\n    auto buf = readBuffer_.move();\n    buf->coalesce();\n    return std::string((char*)buf->data(), buf->length());\n  }\n\n  void reset() {\n    movable_ = false;\n    error_ = false;\n    readBuffer_.reset();\n  }\n\n  folly::IOBufQueue readBuffer_{folly::IOBufQueue::cacheChainLength()};\n  bool error_{false};\n  bool movable_{false};\n};\n\nclass TestWriteCallback : public folly::AsyncWriter::WriteCallback {\n public:\n  void writeSuccess() noexcept override { writes_++; }\n\n  void writeErr(size_t, const folly::AsyncSocketException&) noexcept override {\n    error_ = true;\n  }\n\n  void reset() {\n    writes_ = 0;\n    error_ = false;\n  }\n\n  uint32_t writes_{0};\n  bool error_{false};\n};\n\nclass AsyncPipeTest : public Test {\n public:\n  void reset(bool movable) {\n    reader_.reset();\n    readCallback_.reset();\n    writer_.reset();\n    writeCallback_.reset();\n\n    int rc = folly::fileops::pipe(pipeFds_);\n    EXPECT_EQ(rc, 0);\n\n    EXPECT_EQ(::fcntl(pipeFds_[0], F_SETFL, O_NONBLOCK), 0);\n    EXPECT_EQ(::fcntl(pipeFds_[1], F_SETFL, O_NONBLOCK), 0);\n    reader_ = folly::AsyncPipeReader::newReader(\n        &eventBase_, folly::NetworkSocket::fromFd(pipeFds_[0]));\n    writer_ = folly::AsyncPipeWriter::newWriter(\n        &eventBase_, folly::NetworkSocket::fromFd(pipeFds_[1]));\n\n    readCallback_.setMovable(movable);\n  }\n\n protected:\n  folly::EventBase eventBase_;\n  int pipeFds_[2];\n  folly::AsyncPipeReader::UniquePtr reader_;\n  folly::AsyncPipeWriter::UniquePtr writer_;\n  TestReadCallback readCallback_;\n  TestWriteCallback writeCallback_;\n};\n\nstd::unique_ptr<folly::IOBuf> getBuf(const std::string& data) {\n  auto buf = folly::IOBuf::copyBuffer(data.c_str(), data.length());\n  return buf;\n}\n\n} // namespace\n\nTEST_F(AsyncPipeTest, simple) {\n  for (int pass = 0; pass < 2; ++pass) {\n    reset(pass % 2 != 0);\n    reader_->setReadCB(&readCallback_);\n    writer_->write(getBuf(\"hello\"), &writeCallback_);\n    writer_->closeOnEmpty();\n    eventBase_.loop();\n    EXPECT_EQ(readCallback_.getData(), \"hello\");\n    EXPECT_FALSE(readCallback_.error_);\n    EXPECT_EQ(writeCallback_.writes_, 1);\n    EXPECT_FALSE(writeCallback_.error_);\n  }\n}\n\nTEST_F(AsyncPipeTest, blocked_writes) {\n  for (int pass = 0; pass < 2; ++pass) {\n    reset(pass % 2 != 0);\n    uint32_t writeAttempts = 0;\n    do {\n      ++writeAttempts;\n      writer_->write(getBuf(\"hello\"), &writeCallback_);\n    } while (writeCallback_.writes_ == writeAttempts);\n    // there is one blocked write\n    writer_->closeOnEmpty();\n\n    reader_->setReadCB(&readCallback_);\n\n    eventBase_.loop();\n    std::string expected;\n    for (uint32_t i = 0; i < writeAttempts; i++) {\n      expected += \"hello\";\n    }\n    EXPECT_EQ(readCallback_.getData(), expected);\n    EXPECT_FALSE(readCallback_.error_);\n    EXPECT_EQ(writeCallback_.writes_, writeAttempts);\n    EXPECT_FALSE(writeCallback_.error_);\n  }\n}\n\nTEST_F(AsyncPipeTest, writeOnClose) {\n  for (int pass = 0; pass < 2; ++pass) {\n    reset(pass % 2 != 0);\n    reader_->setReadCB(&readCallback_);\n    writer_->write(getBuf(\"hello\"), &writeCallback_);\n    writer_->closeOnEmpty();\n    writer_->write(getBuf(\"hello\"), &writeCallback_);\n    eventBase_.loop();\n    EXPECT_EQ(readCallback_.getData(), \"hello\");\n    EXPECT_FALSE(readCallback_.error_);\n    EXPECT_EQ(writeCallback_.writes_, 1);\n    EXPECT_TRUE(writeCallback_.error_);\n  }\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncSSLSocketTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/test/AsyncSSLSocketTest.h>\n\n#include <fcntl.h>\n#include <signal.h>\n\n#include <iostream>\n#include <list>\n#include <set>\n#include <thread>\n\n#include <folly/SocketAddress.h>\n#include <folly/String.h>\n#include <folly/io/Cursor.h>\n#include <folly/io/async/AsyncPipe.h>\n#include <folly/io/async/AsyncSSLSocket.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventBaseThread.h>\n#include <folly/io/async/SSLOptions.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/io/async/ssl/BasicTransportCertificate.h>\n#include <folly/io/async/ssl/OpenSSLTransportCertificate.h>\n#include <folly/io/async/test/BlockingSocket.h>\n#include <folly/io/async/test/MockAsyncSocketLegacyObserver.h>\n#include <folly/io/async/test/TFOUtil.h>\n#include <folly/io/async/test/TestSSLServer.h>\n#include <folly/net/NetOps.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/net/test/MockNetOpsDispatcher.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/OpenSSL.h>\n#include <folly/portability/Unistd.h>\n#include <folly/testing/TestUtil.h>\n\n#ifdef __linux__\n#include <dlfcn.h>\n#endif\n\nusing std::cerr;\nusing std::endl;\nusing std::string;\n\nusing namespace folly;\nusing namespace folly::test;\nusing namespace testing;\n\nnamespace {\n\n#if defined __linux__\n// to store libc's original setsockopt()\nusing setsockopt_ptr = int (*)(int, int, int, const void*, socklen_t);\nsetsockopt_ptr real_setsockopt_ = nullptr;\n\n// global struct to initialize before main runs. we can init within a test,\n// or in main, but this method seems to be least intrsive and universal\nstruct GlobalStatic {\n  GlobalStatic() {\n    real_setsockopt_ = (setsockopt_ptr)dlsym(RTLD_NEXT, \"setsockopt\");\n  }\n  void reset() noexcept { ttlsDisabledSet.clear(); }\n  // for each fd, tracks whether TTLS is disabled or not\n  std::unordered_set<folly::NetworkSocket /* fd */> ttlsDisabledSet;\n};\n\n// the constructor will be called before main() which is all we care about\nGlobalStatic globalStatic;\n\n} // namespace\n\n// Intercepting setsockopt to test system behavior with disabled TTLS.\n// this name has to be global\nint setsockopt(\n    int sockfd, int level, int optname, const void* optval, socklen_t optlen) {\n  /**\n   *This is a @deprecated approach to disabling TTLS and should be\n   *removed after completing the migration to FOLLY_SO_TTLS_TRUSTED.\n   */\n  if (optname == FOLLY_SO_NO_TRANSPARENT_TLS) {\n    globalStatic.ttlsDisabledSet.insert(folly::NetworkSocket::fromFd(sockfd));\n    return 0;\n  }\n  if (optname == FOLLY_SO_TTLS_TRUSTED && optval != nullptr) {\n    __u8 optValue = *(__u8*)optval;\n    if (optValue == FOLLY_SO_TTLS_TRUSTED_VAL_ENCRYPTED) {\n      globalStatic.ttlsDisabledSet.insert(folly::NetworkSocket::fromFd(sockfd));\n      return 0;\n    }\n  }\n  return real_setsockopt_(sockfd, level, optname, optval, optlen);\n}\n#endif\n\nnamespace {\n\nvoid getfds(NetworkSocket fds[2]) {\n  if (netops::socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) != 0) {\n    FAIL() << \"failed to create socketpair: \" << errnoStr(errno);\n  }\n  for (int idx = 0; idx < 2; ++idx) {\n    if (netops::set_socket_non_blocking(fds[idx]) != 0) {\n      FAIL() << \"failed to put socket \" << idx\n             << \" in non-blocking mode: \" << errnoStr(errno);\n    }\n  }\n}\n\nvoid getctx(\n    std::shared_ptr<folly::SSLContext> clientCtx,\n    std::shared_ptr<folly::SSLContext> serverCtx) {\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n}\n\nstd::string getFileAsBuf(const char* fileName) {\n  std::string buffer;\n  folly::readFile(find_resource(fileName).c_str(), buffer);\n  return buffer;\n}\n\nfolly::ssl::X509UniquePtr readCertFromFile(const std::string& filename) {\n  auto path = find_resource(filename);\n  folly::ssl::BioUniquePtr bio(BIO_new(BIO_s_file()));\n  if (!bio) {\n    throw std::runtime_error(\"Couldn't create BIO\");\n  }\n\n  if (BIO_read_filename(bio.get(), path.c_str()) != 1) {\n    throw std::runtime_error(\"Couldn't read cert file: \" + filename);\n  }\n  return folly::ssl::X509UniquePtr(\n      PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));\n}\n\nvoid connectWriteReadClose(\n    folly::AsyncReader::ReadCallback::ReadMode readMode) {\n  // Start listening on a local port\n  folly::test::WriteCallbackBase writeCallback;\n  folly::test::ReadCallback readCallback(&writeCallback);\n  folly::test::HandshakeCallback handshakeCallback(&readCallback);\n  folly::test::SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  folly::test::TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<folly::SSLContext>();\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  // sslContext->loadTrustedCertificates(\"./trusted-ca-certificate.pem\");\n  // sslContext->authenticate(true, false);\n\n  // connect\n  auto socket = std::make_shared<folly::test::BlockingSocket>(\n      server.getAddress(), sslContext);\n  socket->setReadMode(readMode);\n  socket->open(std::chrono::milliseconds(10000));\n\n  // write()\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  socket->write(buf, sizeof(buf));\n\n  // read()\n  uint8_t readbuf[128];\n  uint32_t bytesRead = socket->readAll(readbuf, sizeof(readbuf));\n  EXPECT_EQ(bytesRead, 128);\n  EXPECT_EQ(memcmp(buf, readbuf, bytesRead), 0);\n\n  // close()\n  socket->close();\n\n  cerr << \"ConnectWriteReadClose test completed\" << endl;\n  EXPECT_EQ(socket->getSSLSocket()->getTotalConnectTimeout().count(), 10000);\n}\n} // namespace\n\n/**\n * Test connecting to, writing to, reading from, and closing the\n * connection to the SSL server.\n */\nTEST(AsyncSSLSocketTest, ConnectWriteReadClose) {\n  connectWriteReadClose(folly::AsyncReader::ReadCallback::ReadMode::ReadBuffer);\n}\n\nTEST(AsyncSSLSocketTest, ConnectWriteReadvClose) {\n  connectWriteReadClose(folly::AsyncReader::ReadCallback::ReadMode::ReadVec);\n}\n\nTEST(AsyncSSLSocketTest, ConnectWriteReadCloseReadable) {\n  // Same as above, but test AsyncSSLSocket::readable along the way\n\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  std::shared_ptr<SSLContext> sslContext(new SSLContext());\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  // sslContext->loadTrustedCertificates(\"./trusted-ca-certificate.pem\");\n  // sslContext->authenticate(true, false);\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->open(std::chrono::milliseconds(10000));\n\n  // write()\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  socket->write(buf, sizeof(buf));\n\n  // read()\n  uint8_t readbuf[128];\n  // The TLS record includes the full 128 bytes.  Even though we only read 1\n  // byte out of the socket, the rest of the full record decrypted and buffered\n  // in the underlying SSL session.\n  uint32_t bytesRead = socket->readAll(readbuf, 1);\n  EXPECT_EQ(bytesRead, 1);\n  // The socket has no data pending in the kernel\n  EXPECT_FALSE(socket->getSocket()->AsyncSocket::readable());\n  // But the socket is readable\n  EXPECT_TRUE(socket->getSocket()->readable());\n  bytesRead += socket->readAll(readbuf + 1, sizeof(readbuf) - 1);\n  EXPECT_EQ(bytesRead, 128);\n  EXPECT_EQ(memcmp(buf, readbuf, bytesRead), 0);\n\n  // close()\n  socket->close();\n\n  cerr << \"ConnectWriteReadClose test completed\" << endl;\n  EXPECT_EQ(socket->getSSLSocket()->getTotalConnectTimeout().count(), 10000);\n}\n\n/**\n * Check that zero copy options are a noop under AsyncSSLSocket since they\n * aren't supported.\n */\nTEST(AsyncSSLSocketTest, ZeroCopy) {\n  // Set up SSL context.\n  std::shared_ptr<SSLContext> sslContext(new SSLContext());\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  auto socket = AsyncSSLSocket::newSocket(sslContext, /*evb=*/nullptr);\n  EXPECT_FALSE(socket->setZeroCopy(true));\n  EXPECT_FALSE(socket->getZeroCopy());\n}\n\n/**\n * Same as above simple test, but with a large read len to test\n * clamping behavior.\n */\nTEST(AsyncSSLSocketTest, ConnectWriteReadLargeClose) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  std::unique_ptr<SSLContext> serverSslContext =\n      TestSSLServer::getDefaultSSLContext();\n  // With TLS 1.3, OpenSSL will send session tickets. These will arrive after\n  // the handshake has completed and can interfere with the BlockingSocket\n  // client's reads. Disable the session tickets so that doesn't happen.\n  SSL_CTX_set_num_tickets(serverSslContext->getSSLCtx(), 0);\n  TestSSLServer server(&acceptCallback, std::move(serverSslContext));\n\n  // Set up SSL context.\n  std::shared_ptr<SSLContext> sslContext(new SSLContext());\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  // sslContext->loadTrustedCertificates(\"./trusted-ca-certificate.pem\");\n  // sslContext->authenticate(true, false);\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->open(std::chrono::milliseconds(10000));\n\n  // write()\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  socket->write(buf, sizeof(buf));\n\n  // read()\n  uint8_t readbuf[128];\n  // we will fake the read len but that should be fine\n  size_t readLen = 1LL << 33;\n  uint32_t bytesRead = socket->read(readbuf, readLen);\n  EXPECT_EQ(bytesRead, 128);\n  EXPECT_EQ(memcmp(buf, readbuf, bytesRead), 0);\n\n  // close()\n  socket->close();\n\n  cerr << \"ConnectWriteReadClose test completed\" << endl;\n  EXPECT_EQ(socket->getSSLSocket()->getTotalConnectTimeout().count(), 10000);\n}\n\n/**\n * Test reading after server close.\n */\nTEST(AsyncSSLSocketTest, ReadAfterClose) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadEOFCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  auto server = std::make_unique<TestSSLServer>(&acceptCallback);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  auto socket =\n      std::make_shared<BlockingSocket>(server->getAddress(), sslContext);\n  socket->open();\n\n  // This should trigger an EOF on the client.\n  auto evb = handshakeCallback.getSocket()->getEventBase();\n  evb->runInEventBaseThreadAndWait([&]() { handshakeCallback.closeSocket(); });\n  std::array<uint8_t, 128> readbuf;\n  auto bytesRead = socket->read(readbuf.data(), readbuf.size());\n  EXPECT_EQ(0, bytesRead);\n}\n\n/**\n * Test bad renegotiation\n */\n#if !defined(OPENSSL_IS_BORINGSSL)\nTEST(AsyncSSLSocketTest, Renegotiate) {\n  EventBase eventBase;\n  // renegotation in TLS 1.2 only\n  auto clientCtx =\n      std::make_shared<SSLContext>(SSLContext::SSLVersion::TLSv1_2);\n  clientCtx->disableTLS13();\n  auto dfServerCtx =\n      std::make_shared<SSLContext>(SSLContext::SSLVersion::TLSv1_2);\n  dfServerCtx->disableTLS13();\n  std::array<NetworkSocket, 2> fds;\n  getfds(fds.data());\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  RenegotiatingServer server(std::move(serverSock));\n\n  while (!client.handshakeSuccess_ && !client.handshakeError_) {\n    eventBase.loopOnce();\n  }\n\n  ASSERT_TRUE(client.handshakeSuccess_);\n\n  auto sslSock = std::move(client).moveSocket();\n  sslSock->detachEventBase();\n  // This is nasty, however we don't want to add support for\n  // renegotiation in AsyncSSLSocket.\n  SSL_renegotiate(const_cast<SSL*>(sslSock->getSSL()));\n\n  auto socket = std::make_shared<BlockingSocket>(std::move(sslSock));\n\n  std::thread t([&]() { eventBase.loopForever(); });\n\n  // Trigger the renegotiation.\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  try {\n    socket->write(buf.data(), buf.size());\n  } catch (AsyncSocketException& e) {\n    LOG(INFO) << \"client got error \" << e.what();\n  }\n  eventBase.terminateLoopSoon();\n  t.join();\n\n  eventBase.loop();\n  ASSERT_TRUE(server.renegotiationError_);\n}\n#endif\n\n/**\n * Negative test for handshakeError().\n */\nTEST(AsyncSSLSocketTest, HandshakeError) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  WriteErrorCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  HandshakeErrorCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  std::shared_ptr<SSLContext> sslContext(new SSLContext());\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  // read()\n  bool ex = false;\n  try {\n    socket->open();\n\n    uint8_t readbuf[128];\n    uint32_t bytesRead = socket->readAll(readbuf, sizeof(readbuf));\n    LOG(ERROR) << \"readAll returned \" << bytesRead << \" instead of throwing\";\n  } catch (AsyncSocketException&) {\n    ex = true;\n  }\n  EXPECT_TRUE(ex);\n\n  // close()\n  socket->close();\n  cerr << \"HandshakeError test completed\" << endl;\n}\n\n/**\n * Negative test for readError().\n */\nTEST(AsyncSSLSocketTest, ReadError) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadErrorCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  std::shared_ptr<SSLContext> sslContext(new SSLContext());\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->open();\n\n  // write something to trigger ssl handshake\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  socket->write(buf, sizeof(buf));\n\n  socket->close();\n  cerr << \"ReadError test completed\" << endl;\n}\n\n/**\n * Negative test for writeError().\n */\nTEST(AsyncSSLSocketTest, WriteError) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  WriteErrorCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  std::shared_ptr<SSLContext> sslContext(new SSLContext());\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->open();\n\n  // write something to trigger ssl handshake\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  socket->write(buf, sizeof(buf));\n\n  socket->close();\n  cerr << \"WriteError test completed\" << endl;\n}\n\n/**\n * Test a socket with TCP_NODELAY unset.\n */\nTEST(AsyncSSLSocketTest, SocketWithDelay) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  std::shared_ptr<SSLContext> sslContext(new SSLContext());\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->open();\n\n  // write()\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  socket->write(buf, sizeof(buf));\n\n  // read()\n  uint8_t readbuf[128];\n  uint32_t bytesRead = socket->readAll(readbuf, sizeof(readbuf));\n  EXPECT_EQ(bytesRead, 128);\n  EXPECT_EQ(memcmp(buf, readbuf, bytesRead), 0);\n\n  // close()\n  socket->close();\n\n  cerr << \"SocketWithDelay test completed\" << endl;\n}\n\nclass NextProtocolTest : public Test {\n  // For matching protos\n public:\n  void SetUp() override { getctx(clientCtx, serverCtx); }\n\n  void connect(bool unset = false) {\n    getfds(fds);\n\n    if (unset) {\n      // unsetting NPN for any of [client, server] is enough to make NPN not\n      // work\n      clientCtx->unsetNextProtocols();\n    }\n\n    AsyncSSLSocket::UniquePtr clientSock(\n        new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n    AsyncSSLSocket::UniquePtr serverSock(\n        new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n    client = std::make_unique<AlpnClient>(std::move(clientSock));\n    server = std::make_unique<AlpnServer>(std::move(serverSock));\n\n    eventBase.loop();\n  }\n\n  void expectProtocol(const std::string& proto) {\n    expectHandshakeSuccess();\n    EXPECT_NE(client->nextProtoLength, 0);\n    EXPECT_EQ(client->nextProtoLength, server->nextProtoLength);\n    EXPECT_EQ(\n        memcmp(client->nextProto, server->nextProto, server->nextProtoLength),\n        0);\n    string selected((const char*)client->nextProto, client->nextProtoLength);\n    EXPECT_EQ(proto, selected);\n  }\n\n  void expectNoProtocol() {\n    expectHandshakeSuccess();\n    EXPECT_EQ(client->nextProtoLength, 0);\n    EXPECT_EQ(server->nextProtoLength, 0);\n    EXPECT_EQ(client->nextProto, nullptr);\n    EXPECT_EQ(server->nextProto, nullptr);\n  }\n\n  void expectHandshakeSuccess() {\n    EXPECT_FALSE(client->except.has_value())\n        << \"client handshake error: \" << client->except->what();\n    EXPECT_FALSE(server->except.has_value())\n        << \"server handshake error: \" << server->except->what();\n  }\n\n  void expectHandshakeError() {\n    EXPECT_TRUE(client->except.has_value())\n        << \"Expected client handshake error!\";\n    EXPECT_TRUE(server->except.has_value())\n        << \"Expected server handshake error!\";\n  }\n\n  EventBase eventBase;\n  std::shared_ptr<SSLContext> clientCtx{std::make_shared<SSLContext>()};\n  std::shared_ptr<SSLContext> serverCtx{std::make_shared<SSLContext>()};\n  NetworkSocket fds[2];\n  std::unique_ptr<AlpnClient> client;\n  std::unique_ptr<AlpnServer> server;\n};\n\nTEST_F(NextProtocolTest, AlpnTestOverlap) {\n  clientCtx->setAdvertisedNextProtocols({\"blub\", \"baz\"});\n  serverCtx->setAdvertisedNextProtocols({\"foo\", \"bar\", \"baz\"});\n\n  connect();\n\n  expectProtocol(\"baz\");\n}\n\nTEST_F(NextProtocolTest, AlpnTestUnset) {\n  // Identical to above test, except that we want unset NPN before\n  // looping.\n  clientCtx->setAdvertisedNextProtocols({\"blub\", \"baz\"});\n  serverCtx->setAdvertisedNextProtocols({\"foo\", \"bar\", \"baz\"});\n\n  connect(true /* unset */);\n\n  expectNoProtocol();\n}\n\nTEST_F(NextProtocolTest, AlpnTestNoOverlap) {\n  clientCtx->setAdvertisedNextProtocols({\"blub\"});\n  serverCtx->setAdvertisedNextProtocols({\"foo\", \"bar\", \"baz\"});\n  connect();\n\n  expectNoProtocol();\n}\n\nTEST_F(NextProtocolTest, RandomizedAlpnTest) {\n  // Probability that this test will fail is 2^-64, which could be considered\n  // as negligible.\n  const int kTries = 64;\n\n  clientCtx->setAdvertisedNextProtocols({\"foo\", \"bar\", \"baz\"});\n  serverCtx->setRandomizedAdvertisedNextProtocols({{1, {\"foo\"}}, {1, {\"bar\"}}});\n\n  std::set<string> selectedProtocols;\n  for (int i = 0; i < kTries; ++i) {\n    connect();\n\n    EXPECT_NE(client->nextProtoLength, 0);\n    EXPECT_EQ(client->nextProtoLength, server->nextProtoLength);\n    EXPECT_EQ(\n        memcmp(client->nextProto, server->nextProto, server->nextProtoLength),\n        0);\n    string selected((const char*)client->nextProto, client->nextProtoLength);\n    selectedProtocols.insert(selected);\n    expectHandshakeSuccess();\n  }\n  EXPECT_EQ(selectedProtocols.size(), 2);\n}\n\nTEST_F(NextProtocolTest, AlpnNotAllowMismatchNoClientProtocol) {\n  clientCtx->setAdvertisedNextProtocols({});\n  serverCtx->setAdvertisedNextProtocols({\"foo\", \"bar\", \"baz\"});\n  serverCtx->setAlpnAllowMismatch(false);\n\n  connect();\n\n  expectHandshakeSuccess();\n  expectNoProtocol();\n  EXPECT_EQ(server->getClientAlpns(), std::vector<std::string>({}));\n}\n\nTEST_F(NextProtocolTest, AlpnNotAllowMismatchWithOverlap) {\n  clientCtx->setAdvertisedNextProtocols({\"blub\", \"baz\"});\n  serverCtx->setAdvertisedNextProtocols({\"foo\", \"bar\", \"baz\"});\n  serverCtx->setAlpnAllowMismatch(false);\n\n  connect();\n\n  expectProtocol(\"baz\");\n  EXPECT_EQ(\n      server->getClientAlpns(), std::vector<std::string>({\"blub\", \"baz\"}));\n}\n\nTEST_F(NextProtocolTest, AlpnNotAllowMismatchWithoutOverlap) {\n  clientCtx->setAdvertisedNextProtocols({\"blub\"});\n  serverCtx->setAdvertisedNextProtocols({\"foo\", \"bar\", \"baz\"});\n  serverCtx->setAlpnAllowMismatch(false);\n\n  connect();\n\n  expectHandshakeError();\n  EXPECT_EQ(server->getClientAlpns(), std::vector<std::string>({\"blub\"}));\n}\n\n/**\n * 1. Client sends TLSEXT_HOSTNAME in client hello.\n * 2. Server found a match SSL_CTX and use this SSL_CTX to\n *    continue the SSL handshake.\n * 3. Server sends back TLSEXT_HOSTNAME in server hello.\n */\nTEST(AsyncSSLSocketTest, SNITestMatch) {\n  EventBase eventBase;\n  std::shared_ptr<SSLContext> clientCtx(new SSLContext);\n  std::shared_ptr<SSLContext> dfServerCtx(new SSLContext);\n  // Use the same SSLContext to continue the handshake after\n  // tlsext_hostname match.\n  std::shared_ptr<SSLContext> hskServerCtx(dfServerCtx);\n  const std::string serverName(\"xyz.newdev.facebook.com\");\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n  SNIClient client(std::move(clientSock));\n  SNIServer server(\n      std::move(serverSock), dfServerCtx, hskServerCtx, serverName);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.serverNameMatch);\n  EXPECT_TRUE(server.serverNameMatch);\n}\n\n/**\n * 1. Client sends TLSEXT_HOSTNAME in client hello.\n * 2. Server cannot find a matching SSL_CTX and continue to use\n *    the current SSL_CTX to do the handshake.\n * 3. Server does not send back TLSEXT_HOSTNAME in server hello.\n */\nTEST(AsyncSSLSocketTest, SNITestNotMatch) {\n  EventBase eventBase;\n  std::shared_ptr<SSLContext> clientCtx(new SSLContext);\n  std::shared_ptr<SSLContext> dfServerCtx(new SSLContext);\n  // Use the same SSLContext to continue the handshake after\n  // tlsext_hostname match.\n  std::shared_ptr<SSLContext> hskServerCtx(dfServerCtx);\n  const std::string clientRequestingServerName(\"foo.com\");\n  const std::string serverExpectedServerName(\"xyz.newdev.facebook.com\");\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(new AsyncSSLSocket(\n      clientCtx, &eventBase, fds[0], clientRequestingServerName));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n  SNIClient client(std::move(clientSock));\n  SNIServer server(\n      std::move(serverSock),\n      dfServerCtx,\n      hskServerCtx,\n      serverExpectedServerName);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(!client.serverNameMatch);\n  EXPECT_TRUE(!server.serverNameMatch);\n}\n/**\n * 1. Client sends TLSEXT_HOSTNAME in client hello.\n * 2. We then change the serverName.\n * 3. We expect that we get 'false' as the result for serNameMatch.\n */\n\nTEST(AsyncSSLSocketTest, SNITestChangeServerName) {\n  EventBase eventBase;\n  std::shared_ptr<SSLContext> clientCtx(new SSLContext);\n  std::shared_ptr<SSLContext> dfServerCtx(new SSLContext);\n  // Use the same SSLContext to continue the handshake after\n  // tlsext_hostname match.\n  std::shared_ptr<SSLContext> hskServerCtx(dfServerCtx);\n  const std::string serverName(\"xyz.newdev.facebook.com\");\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));\n  // Change the server name\n  std::string newName(\"new.com\");\n  clientSock->setServerName(newName);\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n  SNIClient client(std::move(clientSock));\n  SNIServer server(\n      std::move(serverSock), dfServerCtx, hskServerCtx, serverName);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(!client.serverNameMatch);\n}\n\n/**\n * 1. Client does not send TLSEXT_HOSTNAME in client hello.\n * 2. Server does not send back TLSEXT_HOSTNAME in server hello.\n */\nTEST(AsyncSSLSocketTest, SNITestClientHelloNoHostname) {\n  EventBase eventBase;\n  std::shared_ptr<SSLContext> clientCtx(new SSLContext);\n  std::shared_ptr<SSLContext> dfServerCtx(new SSLContext);\n  // Use the same SSLContext to continue the handshake after\n  // tlsext_hostname match.\n  std::shared_ptr<SSLContext> hskServerCtx(dfServerCtx);\n  const std::string serverExpectedServerName(\"xyz.newdev.facebook.com\");\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n  SNIClient client(std::move(clientSock));\n  SNIServer server(\n      std::move(serverSock),\n      dfServerCtx,\n      hskServerCtx,\n      serverExpectedServerName);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(!client.serverNameMatch);\n  EXPECT_TRUE(!server.serverNameMatch);\n}\n\n/**\n * 1. Create an SSLContext that does not have an ALPN\n * 2. Use AsyncSSLSocket::setSupportedApplicationProtocols on the client and\n * server, and assert that a common ALPN was negotiated.\n */\nTEST(AsyncSSLSocketTest, SetSupportedApplicationProtocols) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n  // Use the same SSLContext to continue the handshake after\n  // tlsext_hostname match.\n  auto hskServerCtx = std::make_shared<SSLContext>();\n  const std::string serverExpectedServerName(\"xyz.newdev.facebook.com\");\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n\n  std::vector<std::string> protocols;\n  protocols.emplace_back(\"rs\");\n\n  clientSock->setSupportedApplicationProtocols(protocols);\n  serverSock->setSupportedApplicationProtocols(protocols);\n\n  SNIClient client(std::move(clientSock));\n  SNIServer server(\n      std::move(serverSock),\n      dfServerCtx,\n      hskServerCtx,\n      serverExpectedServerName);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(\n      client.getApplicationProtocol().compare(\n          server.getApplicationProtocol()) == 0);\n}\n\n/**\n * Test SSL client socket\n */\nTEST(AsyncSSLSocketTest, SSLClientTest) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL client\n  EventBase eventBase;\n  auto client = std::make_shared<SSLClient>(&eventBase, server.getAddress(), 1);\n  client->setSSLOptions(SSL_OP_NO_TICKET);\n\n  client->connect();\n  EventBaseAborter eba(&eventBase, 3000);\n  eventBase.loop();\n\n  EXPECT_EQ(client->getMiss(), 1);\n  EXPECT_EQ(client->getHit(), 0);\n\n  cerr << \"SSLClientTest test completed\" << endl;\n}\n\n/**\n * Test SSL client socket session re-use\n */\nTEST(AsyncSSLSocketTest, SSLClientTestReuse) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL client\n  EventBase eventBase;\n  auto client =\n      std::make_shared<SSLClient>(&eventBase, server.getAddress(), 10);\n  client->setSSLOptions(SSL_OP_NO_TICKET);\n\n  client->connect();\n  EventBaseAborter eba(&eventBase, 3000);\n  eventBase.loop();\n\n  EXPECT_EQ(client->getMiss(), 1);\n  EXPECT_EQ(client->getHit(), 9);\n\n  cerr << \"SSLClientTestReuse test completed\" << endl;\n}\n\n/**\n * Test SSL client socket timeout\n */\nTEST(AsyncSSLSocketTest, SSLClientTimeoutTest) {\n  // Start listening on a local port\n  EmptyReadCallback readCallback;\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::EXPECT_ERROR);\n  HandshakeTimeoutCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL client\n  EventBase eventBase;\n  auto client =\n      std::make_shared<SSLClient>(&eventBase, server.getAddress(), 1, 10);\n  client->setSSLOptions(SSL_OP_NO_TICKET);\n\n  client->connect(true /* write before connect completes */);\n  EventBaseAborter eba(&eventBase, 3000);\n  eventBase.loop();\n\n  usleep(100000);\n  // This is checking that the connectError callback precedes any queued\n  // writeError callbacks.  This matches AsyncSocket's behavior\n  EXPECT_EQ(client->getWriteAfterConnectErrors(), 1);\n  EXPECT_EQ(client->getErrors(), 1);\n  EXPECT_EQ(client->getMiss(), 0);\n  EXPECT_EQ(client->getHit(), 0);\n\n  cerr << \"SSLClientTimeoutTest test completed\" << endl;\n}\n\nclass PerLoopReadCallback : public AsyncTransport::ReadCallback {\n public:\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = buf_.data();\n    *lenReturn = buf_.size();\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    VLOG(3) << \"Read of size: \" << len;\n    s_->setReadCB(nullptr);\n    s_->getEventBase()->runInLoop([this]() { s_->setReadCB(this); });\n  }\n\n  void readErr(const AsyncSocketException&) noexcept override {}\n\n  void readEOF() noexcept override {}\n\n  void setSocket(AsyncSocket* s) { s_ = s; }\n\n private:\n  AsyncSocket* s_;\n  std::array<uint8_t, 1000> buf_;\n};\n\nclass CloseNotifyConnector : public AsyncSocket::ConnectCallback {\n public:\n  CloseNotifyConnector(EventBase* evb, const SocketAddress& addr) {\n    evb_ = evb;\n    ssl_ = AsyncSSLSocket::newSocket(std::make_shared<SSLContext>(), evb_);\n    ssl_->connect(this, addr);\n  }\n\n  void connectSuccess() noexcept override {\n    ssl_->writeChain(nullptr, IOBuf::copyBuffer(\"hi\"));\n    auto ssl = const_cast<SSL*>(ssl_->getSSL());\n    SSL_shutdown(ssl);\n    auto fd = ssl_->detachNetworkSocket();\n    tcp_.reset(new AsyncSocket(evb_, fd), AsyncSocket::Destructor());\n    evb_->runAfterDelay(\n        [this]() {\n          perLoopReads_.setSocket(tcp_.get());\n          tcp_->setReadCB(&perLoopReads_);\n          evb_->runAfterDelay([this]() { tcp_->closeNow(); }, 10);\n        },\n        100);\n  }\n\n  void connectErr(const AsyncSocketException& ex) noexcept override {\n    FAIL() << ex.what();\n  }\n\n private:\n  EventBase* evb_;\n  std::shared_ptr<AsyncSSLSocket> ssl_;\n  std::shared_ptr<AsyncSocket> tcp_;\n  PerLoopReadCallback perLoopReads_;\n};\n\nclass ErrorCheckingWriteCallback : public AsyncSocket::WriteCallback {\n public:\n  void writeSuccess() noexcept override {}\n\n  void writeErr(size_t, const AsyncSocketException& ex) noexcept override {\n    LOG(ERROR) << \"write error: \" << ex.what();\n    EXPECT_NE(\n        ex.getType(),\n        AsyncSocketException::AsyncSocketExceptionType::SSL_ERROR);\n  }\n};\n\nclass WriteOnEofReadCallback : public ReadCallback {\n public:\n  using ReadCallback::ReadCallback;\n\n  void readEOF() noexcept override {\n    LOG(INFO) << \"Got EOF\";\n    auto chain = IOBuf::create(0);\n    for (size_t i = 0; i < 1000 * 1000; i++) {\n      auto buf = IOBuf::create(10);\n      buf->append(10);\n      memset(buf->writableData(), 'x', 10);\n      chain->prependChain(std::move(buf));\n    }\n    socket_->writeChain(&writeCallback_, std::move(chain));\n  }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    LOG(ERROR) << ex.what();\n  }\n\n private:\n  ErrorCheckingWriteCallback writeCallback_;\n};\n\nTEST(AsyncSSLSocketTest, EarlyCloseNotify) {\n  WriteOnEofReadCallback readCallback(nullptr);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  EventBase eventBase;\n  CloseNotifyConnector cnc(&eventBase, server.getAddress());\n\n  eventBase.loop();\n}\n\n/**\n * Verify Client Ciphers obtained using SSL MSG Callback.\n */\nTEST(AsyncSSLSocketTest, SSLParseClientHelloSuccess) {\n  EventBase eventBase;\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->setVerificationOption(SSLContext::VerifyClientCertificate::ALWAYS);\n  serverCtx->setCiphersuitesOrThrow(\n      \"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kTestCA).c_str());\n\n  auto clientCtx = std::make_shared<SSLContext>();\n  clientCtx->setVerificationOption(\n      SSLContext::VerifyServerCertificate::IF_PRESENTED);\n  clientCtx->setCiphersuitesOrThrow(\n      \"TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384\");\n  // With SSLContext min version set to TLS 1.2, TLS 1.2 ciphers are appended to\n  // the list that's sent to the server, and those would appear in the\n  // clientCiphers_ captured and verified below. Remove all of them by setting\n  // eNULL.\n  clientCtx->setCiphersOrThrow(\"eNULL\");\n  clientCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  clientCtx->loadCertificate(find_resource(kTestCert).c_str());\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServerParseClientHello server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n#if defined(OPENSSL_IS_BORINGSSL)\n  EXPECT_EQ(\n      server.clientCiphers_,\n      \"TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384\");\n#else\n  EXPECT_EQ(\n      server.clientCiphers_,\n      \"TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:00ff\");\n#endif\n  EXPECT_EQ(server.chosenCipher_, \"TLS_CHACHA20_POLY1305_SHA256\");\n  EXPECT_TRUE(client.handshakeVerify_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(!client.handshakeError_);\n  EXPECT_TRUE(server.handshakeVerify_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_TRUE(!server.handshakeError_);\n}\n\nTEST(AsyncSSLSocketTest, SSLGetEKM) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, serverCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  EXPECT_EQ(\n      nullptr, clientSock->getExportedKeyingMaterial(\"test\", nullptr, 12));\n\n  serverSock->sslAccept(nullptr, std::chrono::milliseconds::zero());\n  clientSock->sslConn(nullptr, std::chrono::milliseconds::zero());\n  eventBase.loop();\n  auto ekm = clientSock->getExportedKeyingMaterial(\"test\", nullptr, 32);\n\n  EXPECT_NE(nullptr, ekm);\n  EXPECT_EQ(ekm->computeChainDataLength(), 32);\n  // non null context\n  EXPECT_NE(\n      nullptr,\n      clientSock->getExportedKeyingMaterial(\n          \"test\", IOBuf::copyBuffer(\"haha\"), 32));\n  // empty label\n  EXPECT_NE(\n      nullptr,\n      clientSock->getExportedKeyingMaterial(\"\", IOBuf::copyBuffer(\"haha\"), 32));\n\n  auto serverEkm = serverSock->getExportedKeyingMaterial(\"test\", nullptr, 32);\n  EXPECT_TRUE(folly::IOBufEqualTo{}(ekm, serverEkm));\n}\n\nTEST(AsyncSSLSocketTest, SSLGetEKMFailsOnTLS10) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  SSL_CTX_set_max_proto_version(serverCtx->getSSLCtx(), TLS1_VERSION);\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, serverCtx);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  serverSock->sslAccept(nullptr, std::chrono::milliseconds::zero());\n  clientSock->sslConn(nullptr, std::chrono::milliseconds::zero());\n  eventBase.loop();\n  auto ekm = clientSock->getExportedKeyingMaterial(\"test\", nullptr, 32);\n\n  EXPECT_EQ(nullptr, ekm);\n}\n\n/**\n * Verify that server is able to get client cert by getPeerCert() API.\n */\nTEST(AsyncSSLSocketTest, GetClientCertificate) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  serverCtx->ciphers(\"ECDHE-RSA-AES128-SHA:AES128-SHA:AES256-SHA\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kClientTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kClientTestCA).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"AES256-SHA:AES128-SHA\");\n  clientCtx->loadPrivateKey(find_resource(kClientTestKey).c_str());\n  clientCtx->loadCertificate(find_resource(kClientTestCert).c_str());\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  std::array<NetworkSocket, 2> fds;\n  getfds(fds.data());\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServerParseClientHello server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  // Handshake should succeed.\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n\n  // Reclaim the sockets from SSLHandshakeBase.\n  auto cliSocket = std::move(client).moveSocket();\n  auto srvSocket = std::move(server).moveSocket();\n\n  // Client cert retrieved from server side.\n  auto serverPeerCert = srvSocket->getPeerCertificate();\n  CHECK(serverPeerCert);\n\n  // Client cert retrieved from client side.\n  auto clientSelfCert = cliSocket->getSelfCertificate();\n  CHECK(clientSelfCert);\n\n  auto serverX509 =\n      folly::OpenSSLTransportCertificate::tryExtractX509(serverPeerCert);\n  CHECK(serverX509);\n\n  auto clientX509 =\n      folly::OpenSSLTransportCertificate::tryExtractX509(clientSelfCert);\n  CHECK(clientX509);\n\n  // The two certs should be the same.\n  EXPECT_EQ(0, X509_cmp(clientX509.get(), serverX509.get()));\n}\n\nTEST(AsyncSSLSocketTest, SSLParseClientHelloOnePacket) {\n  EventBase eventBase;\n  auto ctx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  int bufLen = 42;\n  uint8_t majorVersion = 18;\n  uint8_t minorVersion = 25;\n\n  // Create callback buf\n  auto buf = IOBuf::create(bufLen);\n  buf->append(bufLen);\n  folly::io::RWPrivateCursor cursor(buf.get());\n  cursor.write<uint8_t>(uint8_t(SSL3_MT_CLIENT_HELLO));\n  cursor.write<uint16_t>(uint16_t(0));\n  cursor.write<uint8_t>(uint8_t(38));\n  cursor.write<uint8_t>(majorVersion);\n  cursor.write<uint8_t>(minorVersion);\n  cursor.skip(32);\n  cursor.write<uint32_t>(uint32_t(0));\n\n  SSL* ssl = ctx->createSSL();\n  SCOPE_EXIT {\n    SSL_free(ssl);\n  };\n  AsyncSSLSocket::UniquePtr sock(\n      new AsyncSSLSocket(ctx, &eventBase, fds[0], true));\n  sock->enableClientHelloParsing();\n\n  // Test client hello parsing in one packet\n  AsyncSSLSocket::clientHelloParsingCallback(\n      0, 0, SSL3_RT_HANDSHAKE, buf->data(), buf->length(), ssl, sock.get());\n  buf.reset();\n\n  auto parsedClientHello = sock->getClientHelloInfo();\n  EXPECT_TRUE(parsedClientHello != nullptr);\n  EXPECT_EQ(parsedClientHello->clientHelloMajorVersion_, majorVersion);\n  EXPECT_EQ(parsedClientHello->clientHelloMinorVersion_, minorVersion);\n}\n\nTEST(AsyncSSLSocketTest, SSLParseClientHelloTwoPackets) {\n  EventBase eventBase;\n  auto ctx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  int bufLen = 42;\n  uint8_t majorVersion = 18;\n  uint8_t minorVersion = 25;\n\n  // Create callback buf\n  auto buf = IOBuf::create(bufLen);\n  buf->append(bufLen);\n  folly::io::RWPrivateCursor cursor(buf.get());\n  cursor.write<uint8_t>(uint8_t(SSL3_MT_CLIENT_HELLO));\n  cursor.write<uint16_t>(uint16_t(0));\n  cursor.write<uint8_t>(uint8_t(38));\n  cursor.write<uint8_t>(majorVersion);\n  cursor.write<uint8_t>(minorVersion);\n  cursor.skip(32);\n  cursor.write<uint32_t>(uint32_t(0));\n\n  SSL* ssl = ctx->createSSL();\n  SCOPE_EXIT {\n    SSL_free(ssl);\n  };\n  AsyncSSLSocket::UniquePtr sock(\n      new AsyncSSLSocket(ctx, &eventBase, fds[0], true));\n  sock->enableClientHelloParsing();\n\n  // Test parsing with two packets with first packet size < 3\n  auto bufCopy = folly::IOBuf::copyBuffer(buf->data(), 2);\n  AsyncSSLSocket::clientHelloParsingCallback(\n      0,\n      0,\n      SSL3_RT_HANDSHAKE,\n      bufCopy->data(),\n      bufCopy->length(),\n      ssl,\n      sock.get());\n  bufCopy.reset();\n  bufCopy = folly::IOBuf::copyBuffer(buf->data() + 2, buf->length() - 2);\n  AsyncSSLSocket::clientHelloParsingCallback(\n      0,\n      0,\n      SSL3_RT_HANDSHAKE,\n      bufCopy->data(),\n      bufCopy->length(),\n      ssl,\n      sock.get());\n  bufCopy.reset();\n\n  auto parsedClientHello = sock->getClientHelloInfo();\n  EXPECT_TRUE(parsedClientHello != nullptr);\n  EXPECT_EQ(parsedClientHello->clientHelloMajorVersion_, majorVersion);\n  EXPECT_EQ(parsedClientHello->clientHelloMinorVersion_, minorVersion);\n}\n\nTEST(AsyncSSLSocketTest, SSLParseClientHelloMultiplePackets) {\n  EventBase eventBase;\n  auto ctx = std::make_shared<SSLContext>();\n  ctx->setSupportedGroups(std::vector<std::string>({\"P-256\"}));\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  int bufLen = 42;\n  uint8_t majorVersion = 18;\n  uint8_t minorVersion = 25;\n\n  // Create callback buf\n  auto buf = IOBuf::create(bufLen);\n  buf->append(bufLen);\n  folly::io::RWPrivateCursor cursor(buf.get());\n  cursor.write<uint8_t>(uint8_t(SSL3_MT_CLIENT_HELLO));\n  cursor.write<uint16_t>(uint16_t(0));\n  cursor.write<uint8_t>(uint8_t(38));\n  cursor.write<uint8_t>(majorVersion);\n  cursor.write<uint8_t>(minorVersion);\n  cursor.skip(32);\n  cursor.write<uint32_t>(uint32_t(0));\n\n  SSL* ssl = ctx->createSSL();\n  SCOPE_EXIT {\n    SSL_free(ssl);\n  };\n  AsyncSSLSocket::UniquePtr sock(\n      new AsyncSSLSocket(ctx, &eventBase, fds[0], true));\n  sock->enableClientHelloParsing();\n\n  // Test parsing with multiple small packets\n  for (std::size_t i = 0; i < buf->length(); i += 3) {\n    auto bufCopy = folly::IOBuf::copyBuffer(\n        buf->data() + i, std::min((std::size_t)3, buf->length() - i));\n    AsyncSSLSocket::clientHelloParsingCallback(\n        0,\n        0,\n        SSL3_RT_HANDSHAKE,\n        bufCopy->data(),\n        bufCopy->length(),\n        ssl,\n        sock.get());\n    bufCopy.reset();\n  }\n\n  auto parsedClientHello = sock->getClientHelloInfo();\n  EXPECT_TRUE(parsedClientHello != nullptr);\n  EXPECT_EQ(parsedClientHello->clientHelloMajorVersion_, majorVersion);\n  EXPECT_EQ(parsedClientHello->clientHelloMinorVersion_, minorVersion);\n}\n\n/**\n * Verify successful behavior of SSL certificate validation.\n */\nTEST(AsyncSSLSocketTest, SSLHandshakeValidationSuccess) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  dfServerCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeVerify_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(!client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(!server.handshakeVerify_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_TRUE(!server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\n/**\n * Verify that the client's verification callback is able to fail SSL\n * connection establishment.\n */\nTEST(AsyncSSLSocketTest, SSLHandshakeValidationFailure) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  dfServerCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, false);\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeVerify_);\n  EXPECT_TRUE(!client.handshakeSuccess_);\n  EXPECT_TRUE(client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(!server.handshakeVerify_);\n  EXPECT_TRUE(!server.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\n/**\n * Verify that the client successfully handshakes when\n * CertificateIdentityVerifier is set and returns with no exception.\n */\nTEST(AsyncSSLSocketTest, SSLCertificateIdentityVerifierReturns) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n  // the client socket will default to USE_CTX, so set VERIFY here\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  // load root certificate\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  // prepare a basic server (callbacks have a few EXPECTS to fullfil)\n  ReadCallback readCallback(nullptr);\n  // expects successful handshake\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, serverCtx);\n\n  std::shared_ptr<MockCertificateIdentityVerifier> verifier =\n      std::make_shared<MockCertificateIdentityVerifier>();\n\n  // expecting context verification to be called first\n  auto&& verifyContext =\n      EXPECT_CALL(*verifier, verifyContext(true, _))\n          .Times(AtLeast(1))\n          .WillRepeatedly(Return(true));\n  // expecting to only verify once, with the leaf certificate\n  // (kTestCert)\n  EXPECT_CALL(\n      *verifier,\n      verifyLeaf(Property(\n          &AsyncTransportCertificate::getIdentity, StrEq(\"Asox Company\"))))\n      .After(verifyContext)\n      .WillOnce(Return(ByMove(\n          std::make_unique<folly::ssl::BasicTransportCertificate>(\n              \"Asox Company\", readCertFromFile(kTestCert)))));\n\n  AsyncSSLSocket::Options opts;\n  opts.verifier = std::move(verifier);\n\n  // connect to server and handshake\n  AsyncSSLSocket::UniquePtr socket(\n      new AsyncSSLSocket(clientCtx, &eventBase, std::move(opts)));\n  socket->connect(nullptr, server.getAddress(), 0);\n\n  // write to satisfy server ReadCallback EXPECTs\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  socket->write(nullptr, buf.data(), buf.size());\n\n  eventBase.loop();\n\n  socket->close();\n}\n\n/**\n * Verify that the client fails to connect during handshake because\n * CertificateIdentityVerifier returns a failure while verifying the server's\n * leaf certificate.\n */\nTEST(AsyncSSLSocketTest, SSLCertificateIdentityVerifierFailsToConnect) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n  // the client socket will default to USE_CTX, so set VERIFY here\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  // load root certificate\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  // prepare a basic server (callbacks have a few EXPECTS to fullfil)\n  ReadCallback readCallback(nullptr);\n  // expects a failed handshake\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::ExpectType::EXPECT_ERROR);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, serverCtx);\n\n  std::shared_ptr<MockCertificateIdentityVerifier> verifier =\n      std::make_shared<MockCertificateIdentityVerifier>();\n\n  // Throw an exception on verification failure\n  CertificateIdentityVerifierException failed{\"a failed test reason\"};\n\n  // expecting context verification to be called first\n  auto&& verifyContext =\n      EXPECT_CALL(*verifier, verifyContext(true, _))\n          .Times(AtLeast(1))\n          .WillRepeatedly(Return(true));\n\n  // expecting to only verify once, with the leaf certificate (kTestCert)\n  EXPECT_CALL(\n      *verifier,\n      verifyLeaf(Property(\n          &AsyncTransportCertificate::getIdentity, StrEq(\"Asox Company\"))))\n      .After(verifyContext)\n      .WillOnce(Throw(failed));\n\n  AsyncSSLSocket::Options opts;\n  opts.verifier = std::move(verifier);\n\n  // connect to server and handshake\n  AsyncSSLSocket::UniquePtr socket(\n      new AsyncSSLSocket(clientCtx, &eventBase, std::move(opts)));\n  socket->connect(nullptr, server.getAddress(), 0);\n\n  eventBase.loop();\n\n  socket->close();\n}\n\n/**\n * Verify that the client fails to connect during handshake when\n * CertificateIdentityVerifier::verifyContext returns false,\n * and that verifyLeaf is not invoked when verifyContext fails.\n */\nTEST(AsyncSSLSocketTest, SSLCertificateIdentityVerifierContextFailure) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n  // the client socket will default to USE_CTX, so set VERIFY here\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  // load root certificate\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  // prepare a basic server (callbacks have a few EXPECTS to fulfill)\n  ReadCallback readCallback(nullptr);\n  // expects a failed handshake\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::ExpectType::EXPECT_ERROR);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, serverCtx);\n\n  std::shared_ptr<StrictMock<MockCertificateIdentityVerifier>> verifier =\n      std::make_shared<StrictMock<MockCertificateIdentityVerifier>>();\n\n  // verifyContext should be called and return false to fail verification\n  EXPECT_CALL(*verifier, verifyContext(true, _)).WillOnce(Return(false));\n\n  // verifyLeaf should NOT be called since verifyContext failed\n  EXPECT_CALL(*verifier, verifyLeaf).Times(0);\n\n  AsyncSSLSocket::Options opts;\n  opts.verifier = std::move(verifier);\n\n  // connect to server and handshake\n  AsyncSSLSocket::UniquePtr socket(\n      new AsyncSSLSocket(clientCtx, &eventBase, std::move(opts)));\n  socket->connect(nullptr, server.getAddress(), 0);\n\n  eventBase.loop();\n\n  socket->close();\n}\n\n/**\n * Verify that verifyContext has access to the X509_STORE_CTX\n * and can inspect the certificate chain.\n */\nTEST(AsyncSSLSocketTest, SSLCertificateIdentityVerifierContextInspectsChain) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n  // the client socket will default to USE_CTX, so set VERIFY here\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  // load root certificate\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  // prepare a basic server (callbacks have a few EXPECTS to fulfill)\n  ReadCallback readCallback(nullptr);\n  // expects successful handshake\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, serverCtx);\n\n  std::shared_ptr<MockCertificateIdentityVerifier> verifier =\n      std::make_shared<MockCertificateIdentityVerifier>();\n\n  // verifyContext should be called and have access to X509_STORE_CTX\n  auto&& verifyContext =\n      EXPECT_CALL(*verifier, verifyContext(true, _))\n          .Times(AtLeast(1))\n          .WillRepeatedly(WithArg<1>([](X509_STORE_CTX* ctx) {\n            // Verify we can access the cert chain\n            X509* cert = X509_STORE_CTX_get_current_cert(ctx);\n            EXPECT_NE(cert, nullptr);\n\n            // Verify we can get the chain depth\n            int depth = X509_STORE_CTX_get_error_depth(ctx);\n            EXPECT_GE(depth, 0);\n\n            return true;\n          }));\n\n  // expecting to verify leaf certificate after context verification\n  EXPECT_CALL(\n      *verifier,\n      verifyLeaf(Property(\n          &AsyncTransportCertificate::getIdentity, StrEq(\"Asox Company\"))))\n      .After(verifyContext)\n      .WillOnce(Return(ByMove(\n          std::make_unique<folly::ssl::BasicTransportCertificate>(\n              \"Asox Company\", readCertFromFile(kTestCert)))));\n\n  AsyncSSLSocket::Options opts;\n  opts.verifier = std::move(verifier);\n\n  // connect to server and handshake\n  AsyncSSLSocket::UniquePtr socket(\n      new AsyncSSLSocket(clientCtx, &eventBase, std::move(opts)));\n  socket->connect(nullptr, server.getAddress(), 0);\n\n  // write to satisfy server ReadCallback EXPECTs\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  socket->write(nullptr, buf.data(), buf.size());\n\n  eventBase.loop();\n\n  socket->close();\n}\n\n/**\n * Verify that the client's CertificateIdentityVerifier is not invoked if\n * OpenSSL's verification fails. (With no HandshakeCB.)\n */\nTEST(AsyncSSLSocketTest, SSLCertificateIdentityVerifierNotInvokedX509Failure) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n  // the client socket will default to USE_CTX, so set VERIFY here\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  // DO NOT load root certificate, so that server certificate is rejected\n\n  // prepare a basic server (callbacks have a few EXPECTS to fullfil)\n  ReadCallback readCallback(nullptr);\n  // expects successful handshake\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::ExpectType::EXPECT_ERROR);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, serverCtx);\n\n  // do not override verification result\n  std::shared_ptr<StrictMock<MockCertificateIdentityVerifier>> verifier =\n      std::make_shared<StrictMock<MockCertificateIdentityVerifier>>();\n  EXPECT_CALL(*verifier, verifyContext(false, _)).WillOnce(Return(false));\n\n  AsyncSSLSocket::Options opts;\n  opts.verifier = std::move(verifier);\n\n  // connect to server and handshake\n  AsyncSSLSocket::UniquePtr socket(\n      new AsyncSSLSocket(clientCtx, &eventBase, std::move(opts)));\n  socket->connect(nullptr, server.getAddress(), 0);\n\n  eventBase.loop();\n\n  socket->close();\n}\n\n/**\n * Verify that the client CertificateIdentityVerifier is not invoked if\n * HandshakeCB::handshakeVer verification fails.\n */\nTEST(\n    AsyncSSLSocketTest,\n    SSLCertificateIdentityVerifierNotInvokedHandshakeCBFailure) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n  // the client socket will default to USE_CTX, so set VERIFY here\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  // load root certificate\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSocket::UniquePtr rawClient(new AsyncSocket(&eventBase, fds[0]));\n  AsyncSocket::UniquePtr rawServer(new AsyncSocket(&eventBase, fds[1]));\n\n  // should be invoked to only verify context\n  std::shared_ptr<StrictMock<MockCertificateIdentityVerifier>> verifier =\n      std::make_shared<StrictMock<MockCertificateIdentityVerifier>>();\n  EXPECT_CALL(*verifier, verifyContext)\n      .Times(AtLeast(1))\n      .WillRepeatedly(Return(true));\n\n  AsyncSSLSocket::Options clientOpts;\n  clientOpts.verifier = verifier;\n\n  AsyncSSLSocket::Options serverOpts;\n  serverOpts.isServer = true;\n\n  AsyncSSLSocket::UniquePtr clientSock(new AsyncSSLSocket(\n      clientCtx, std::move(rawClient), std::move(clientOpts)));\n  AsyncSSLSocket::UniquePtr serverSock(new AsyncSSLSocket(\n      serverCtx, std::move(rawServer), std::move(serverOpts)));\n\n  serverSock->sslAccept(nullptr, std::chrono::milliseconds::zero());\n\n  StrictMock<MockHandshakeCB> clientHandshakeCB;\n\n  // Force the end entity certificate, which normally is successfully verified,\n  // to be considered as unsuccessful\n  EXPECT_CALL(clientHandshakeCB, handshakeVerImpl(clientSock.get(), true, _))\n      .Times(AtLeast(1))\n      .WillRepeatedly([&](auto&&, bool preverifyOk, auto&& ctx) {\n        auto currentDepth = X509_STORE_CTX_get_error_depth(ctx);\n        if (currentDepth == 0) {\n          EXPECT_TRUE(preverifyOk);\n          return false;\n        }\n        return preverifyOk;\n      });\n\n  // failure callback to verify handshake failed\n  EXPECT_CALL(clientHandshakeCB, handshakeErrImpl(clientSock.get(), _));\n\n  clientSock->sslConn(&clientHandshakeCB);\n\n  eventBase.loop();\n\n  clientSock->close();\n  serverSock->close();\n}\n\n/**\n * Verify that the client CertificateIdentityVerifier is invoked on a server\n * socket when peer verification is requested.\n */\nTEST(AsyncSSLSocketTest, SSLCertificateIdentityVerifierSucceedsOnServer) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n  // the client socket will default to USE_CTX, so set VERIFY here\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  // load root certificate\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  // load identity and key on client, it's the same identity as server just for\n  // convenience\n  clientCtx->loadCertificate(find_resource(kTestCert).c_str());\n  clientCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  // instruct server to verify client\n  serverCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSocket::UniquePtr rawClient(new AsyncSocket(&eventBase, fds[0]));\n  AsyncSocket::UniquePtr rawServer(new AsyncSocket(&eventBase, fds[1]));\n\n  // client and server verifiers should verify only once each\n  std::shared_ptr<MockCertificateIdentityVerifier> clientVerifier =\n      std::make_shared<MockCertificateIdentityVerifier>();\n  auto&& clientVerifyContext =\n      EXPECT_CALL(*clientVerifier, verifyContext(true, _))\n          .Times(AtLeast(1))\n          .WillRepeatedly(Return(true));\n  EXPECT_CALL(\n      *clientVerifier,\n      verifyLeaf(Property(\n          &AsyncTransportCertificate::getIdentity, StrEq(\"Asox Company\"))))\n      .After(clientVerifyContext)\n      .WillOnce(Return(ByMove(\n          std::make_unique<folly::ssl::BasicTransportCertificate>(\n              \"Asox Company\", readCertFromFile(kTestCert)))));\n  std::shared_ptr<StrictMock<MockCertificateIdentityVerifier>> serverVerifier =\n      std::make_shared<StrictMock<MockCertificateIdentityVerifier>>();\n  auto&& serverVerifyContext =\n      EXPECT_CALL(*serverVerifier, verifyContext(true, _))\n          .Times(AtLeast(1))\n          .WillRepeatedly(Return(true));\n  EXPECT_CALL(\n      *serverVerifier,\n      verifyLeaf(Property(\n          &AsyncTransportCertificate::getIdentity, StrEq(\"Asox Company\"))))\n      .After(serverVerifyContext)\n      .WillOnce(Return(ByMove(\n          std::make_unique<folly::ssl::BasicTransportCertificate>(\n              \"Asox Company\", readCertFromFile(kTestCert)))));\n\n  AsyncSSLSocket::Options clientOpts;\n  clientOpts.verifier = clientVerifier;\n\n  AsyncSSLSocket::Options serverOpts;\n  serverOpts.isServer = true;\n  serverOpts.verifier = serverVerifier;\n\n  AsyncSSLSocket::UniquePtr clientSock(new AsyncSSLSocket(\n      clientCtx, std::move(rawClient), std::move(clientOpts)));\n  AsyncSSLSocket::UniquePtr serverSock(new AsyncSSLSocket(\n      serverCtx, std::move(rawServer), std::move(serverOpts)));\n\n  // no HandshakeCBs anywhere\n  serverSock->sslAccept(nullptr, std::chrono::milliseconds::zero());\n  clientSock->sslConn(nullptr);\n\n  eventBase.loop();\n\n  clientSock->close();\n  serverSock->close();\n}\n\n/**\n * Verify that the options in SSLContext can be overridden in\n * sslConnect/Accept.i.e specifying that no validation should be performed\n * allows an otherwise-invalid certificate to be accepted and doesn't fire\n * the validation callback.\n */\nTEST(AsyncSSLSocketTest, OverrideSSLCtxDisableVerify) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  dfServerCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClientNoVerify client(std::move(clientSock), false, false);\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  SSLHandshakeServerNoVerify server(std::move(serverSock), false, false);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(!client.handshakeVerify_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(!client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(!server.handshakeVerify_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_TRUE(!server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\n/**\n * Verify that the options in SSLContext can be overridden in\n * sslConnect/Accept. Enable verification even if context says otherwise.\n * Test requireClientCert with client cert\n */\nTEST(AsyncSSLSocketTest, OverrideSSLCtxEnableVerify) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kTestCA).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  clientCtx->loadCertificate(find_resource(kTestCert).c_str());\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClientDoVerify client(std::move(clientSock), true, true);\n  SSLHandshakeServerDoVerify server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeVerify_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_FALSE(client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(server.handshakeVerify_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_FALSE(server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\n/**\n * Verify that the client's verification callback is able to override\n * the preverification failure and allow a successful connection.\n */\nTEST(AsyncSSLSocketTest, SSLHandshakeValidationOverride) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  dfServerCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), false, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeVerify_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(!client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(!server.handshakeVerify_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_TRUE(!server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\n/**\n * Verify that specifying that no validation should be performed allows an\n * otherwise-invalid certificate to be accepted and doesn't fire the validation\n * callback.\n */\nTEST(AsyncSSLSocketTest, SSLHandshakeValidationSkip) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, dfServerCtx);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  dfServerCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), false, false);\n  SSLHandshakeServer server(std::move(serverSock), false, false);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(!client.handshakeVerify_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(!client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(!server.handshakeVerify_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_TRUE(!server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\n/**\n * Test requireClientCert with client cert\n */\nTEST(AsyncSSLSocketTest, ClientCertHandshakeSuccess) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->setVerificationOption(\n      SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT);\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kTestCA).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  clientCtx->loadCertificate(find_resource(kTestCert).c_str());\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeVerify_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_FALSE(client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(server.handshakeVerify_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_FALSE(server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n\n  // check certificates\n  auto clientSsl = std::move(client).moveSocket();\n  auto serverSsl = std::move(server).moveSocket();\n\n  auto clientPeer = clientSsl->getPeerCertificate();\n  auto clientSelf = clientSsl->getSelfCertificate();\n  auto serverPeer = serverSsl->getPeerCertificate();\n  auto serverSelf = serverSsl->getSelfCertificate();\n\n  EXPECT_NE(clientPeer, nullptr);\n  EXPECT_NE(clientSelf, nullptr);\n  EXPECT_NE(serverPeer, nullptr);\n  EXPECT_NE(serverSelf, nullptr);\n\n  EXPECT_EQ(clientPeer->getIdentity(), serverSelf->getIdentity());\n  EXPECT_EQ(clientSelf->getIdentity(), serverPeer->getIdentity());\n}\n\n/**\n * Test requireClientCert with no client cert\n */\nTEST(AsyncSSLSocketTest, NoClientCertHandshakeError) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->setVerificationOption(\n      SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT);\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kTestCA).c_str());\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), false, false);\n  SSLHandshakeServer server(std::move(serverSock), false, false);\n\n  eventBase.loop();\n\n  EXPECT_FALSE(server.handshakeVerify_);\n  EXPECT_FALSE(server.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\nstatic void makeNonBlockingPipe(int pipefds[2]) {\n  if (fileops::pipe(pipefds) != 0) {\n    throw std::runtime_error(\"Cannot create pipe\");\n  }\n  if (::fcntl(pipefds[0], F_SETFL, O_NONBLOCK) != 0) {\n    throw std::runtime_error(\"Cannot set pipe to nonblocking\");\n  }\n  if (::fcntl(pipefds[1], F_SETFL, O_NONBLOCK) != 0) {\n    throw std::runtime_error(\"Cannot set pipe to nonblocking\");\n  }\n}\n\n// Custom RSA private key encryption method\nstatic int kRSAExIndex = -1;\nstatic int kRSAEvbExIndex = -1;\nstatic int kRSASocketExIndex = -1;\nstatic constexpr StringPiece kEngineId = \"AsyncSSLSocketTest\";\n\nstatic int customRsaPrivEnc(\n    int flen,\n    const unsigned char* from,\n    unsigned char* to,\n    RSA* rsa,\n    int padding) {\n  LOG(INFO) << \"rsa_priv_enc\";\n  EventBase* asyncJobEvb =\n      reinterpret_cast<EventBase*>(RSA_get_ex_data(rsa, kRSAEvbExIndex));\n  CHECK(asyncJobEvb);\n\n  RSA* actualRSA = reinterpret_cast<RSA*>(RSA_get_ex_data(rsa, kRSAExIndex));\n  CHECK(actualRSA);\n\n  AsyncSSLSocket* socket = reinterpret_cast<AsyncSSLSocket*>(\n      RSA_get_ex_data(rsa, kRSASocketExIndex));\n\n  ASYNC_JOB* job = ASYNC_get_current_job();\n  if (job == nullptr) {\n    throw std::runtime_error(\"Expected call in job context\");\n  }\n  ASYNC_WAIT_CTX* waitctx = ASYNC_get_wait_ctx(job);\n  OSSL_ASYNC_FD pipefds[2] = {0, 0};\n  makeNonBlockingPipe(pipefds);\n  if (!ASYNC_WAIT_CTX_set_wait_fd(\n          waitctx, kEngineId.data(), pipefds[0], nullptr, nullptr)) {\n    throw std::runtime_error(\"Cannot set wait fd\");\n  }\n  int ret = 0;\n  int* retptr = &ret;\n\n  auto hand = folly::NetworkSocket::native_handle_type(pipefds[1]);\n  auto asyncPipeWriter = folly::AsyncPipeWriter::newWriter(\n      asyncJobEvb, folly::NetworkSocket(hand));\n\n  if (socket) {\n    LOG(INFO) << \"Got a socket passed in, closing it...\";\n    socket->closeNow();\n  }\n  asyncJobEvb->runInEventBaseThread(\n      [retptr = retptr,\n       flen = flen,\n       from = from,\n       to = to,\n       padding = padding,\n       actualRSA = actualRSA,\n       writer = std::move(asyncPipeWriter)]() {\n        LOG(INFO) << \"Running job\";\n        *retptr = RSA_meth_get_priv_enc(RSA_PKCS1_OpenSSL())(\n            flen, from, to, actualRSA, padding);\n        LOG(INFO) << \"Finished job, writing to pipe\";\n        uint8_t byte = *retptr > 0 ? 1 : 0;\n        writer->write(nullptr, &byte, 1);\n      });\n\n  LOG(INFO) << \"About to pause job\";\n\n  ASYNC_pause_job();\n  LOG(INFO) << \"Resumed job with ret: \" << ret;\n  return ret;\n}\n\nvoid rsaFree(void*, void* ptr, CRYPTO_EX_DATA*, int, long, void*) {\n  LOG(INFO) << \"RSA_free is called with ptr \" << std::hex << ptr;\n  if (ptr == nullptr) {\n    LOG(INFO) << \"Returning early from rsaFree because ptr is null\";\n    return;\n  }\n  RSA* rsa = (RSA*)ptr;\n  auto meth = RSA_get_method(rsa);\n  if (meth != RSA_get_default_method()) {\n    auto nonconst = const_cast<RSA_METHOD*>(meth);\n    RSA_meth_free(nonconst);\n    RSA_set_method(rsa, RSA_get_default_method());\n  }\n  RSA_free(rsa);\n}\n\nstruct RSAPointers {\n  RSA* actualrsa{nullptr};\n  RSA* dummyrsa{nullptr};\n  RSA_METHOD* meth{nullptr};\n};\n\ninline void RSAPointersFree(RSAPointers* p) {\n  if (p->meth && p->dummyrsa && RSA_get_method(p->dummyrsa) == p->meth) {\n    RSA_set_method(p->dummyrsa, RSA_get_default_method());\n  }\n\n  if (p->meth) {\n    LOG(INFO) << \"Freeing meth\";\n    RSA_meth_free(p->meth);\n  }\n\n  if (p->actualrsa) {\n    LOG(INFO) << \"Freeing actualrsa\";\n    RSA_free(p->actualrsa);\n  }\n\n  if (p->dummyrsa) {\n    LOG(INFO) << \"Freeing dummyrsa\";\n    RSA_free(p->dummyrsa);\n  }\n\n  delete p;\n}\n\nusing RSAPointersDeleter =\n    folly::static_function_deleter<RSAPointers, RSAPointersFree>;\n\nstd::unique_ptr<RSAPointers, RSAPointersDeleter> setupCustomRSA(\n    const char* certPath, const char* keyPath, EventBase* jobEvb) {\n  auto certPEM = getFileAsBuf(certPath);\n  auto keyPEM = getFileAsBuf(keyPath);\n\n  ssl::BioUniquePtr certBio(\n      BIO_new_mem_buf((void*)certPEM.data(), certPEM.size()));\n  ssl::BioUniquePtr keyBio(\n      BIO_new_mem_buf((void*)keyPEM.data(), keyPEM.size()));\n\n  ssl::X509UniquePtr cert(\n      PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr));\n  ssl::EvpPkeyUniquePtr evpPkey(\n      PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr));\n  ssl::EvpPkeyUniquePtr publicEvpPkey(X509_get_pubkey(cert.get()));\n\n  std::unique_ptr<RSAPointers, RSAPointersDeleter> ret(new RSAPointers());\n\n  RSA* actualrsa = EVP_PKEY_get1_RSA(evpPkey.get());\n  LOG(INFO) << \"actualrsa ptr \" << std::hex << (void*)actualrsa;\n  RSA* dummyrsa = EVP_PKEY_get1_RSA(publicEvpPkey.get());\n  if (dummyrsa == nullptr) {\n    throw std::runtime_error(\"Couldn't get RSA cert public factors\");\n  }\n  RSA_METHOD* meth = RSA_meth_dup(RSA_get_default_method());\n  if (meth == nullptr || RSA_meth_set1_name(meth, \"Async RSA method\") == 0 ||\n      RSA_meth_set_priv_enc(meth, customRsaPrivEnc) == 0 ||\n      RSA_meth_set_flags(meth, RSA_METHOD_FLAG_NO_CHECK) == 0) {\n    throw std::runtime_error(\"Cannot create async RSA_METHOD\");\n  }\n  RSA_set_method(dummyrsa, meth);\n  RSA_set_flags(dummyrsa, RSA_FLAG_EXT_PKEY);\n\n  kRSAExIndex = RSA_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);\n  kRSAEvbExIndex = RSA_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);\n  kRSASocketExIndex =\n      RSA_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);\n  CHECK_NE(kRSAExIndex, -1);\n  CHECK_NE(kRSAEvbExIndex, -1);\n  CHECK_NE(kRSASocketExIndex, -1);\n  RSA_set_ex_data(dummyrsa, kRSAExIndex, actualrsa);\n  RSA_set_ex_data(dummyrsa, kRSAEvbExIndex, jobEvb);\n\n  ret->actualrsa = actualrsa;\n  ret->dummyrsa = dummyrsa;\n  ret->meth = meth;\n\n  return ret;\n}\n\n// TODO: disabled with ASAN doesn't play nice with ASYNC for some reason\n#ifndef FOLLY_SANITIZE_ADDRESS\nTEST(AsyncSSLSocketTest, OpenSSL110AsyncTest) {\n  ASYNC_init_thread(1, 1);\n  EventBase eventBase;\n  ScopedEventBaseThread jobEvbThread;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kTestCA).c_str());\n\n  auto rsaPointers =\n      setupCustomRSA(kTestCert, kTestKey, jobEvbThread.getEventBase());\n  CHECK(rsaPointers->dummyrsa);\n  // up-refs dummyrsa\n  SSL_CTX_use_RSAPrivateKey(serverCtx->getSSLCtx(), rsaPointers->dummyrsa);\n  SSL_CTX_set_mode(serverCtx->getSSLCtx(), SSL_MODE_ASYNC);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), false, false);\n  SSLHandshakeServer server(std::move(serverSock), false, false);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_TRUE(client.handshakeSuccess_);\n  ASYNC_cleanup_thread();\n}\n\nTEST(AsyncSSLSocketTest, OpenSSL110AsyncTestFailure) {\n  ASYNC_init_thread(1, 1);\n  EventBase eventBase;\n  ScopedEventBaseThread jobEvbThread;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kTestCA).c_str());\n  // Set the wrong key for the cert\n  auto rsaPointers =\n      setupCustomRSA(kTestCert, kClientTestKey, jobEvbThread.getEventBase());\n  CHECK(rsaPointers->dummyrsa);\n  SSL_CTX_use_RSAPrivateKey(serverCtx->getSSLCtx(), rsaPointers->dummyrsa);\n  SSL_CTX_set_mode(serverCtx->getSSLCtx(), SSL_MODE_ASYNC);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), false, false);\n  SSLHandshakeServer server(std::move(serverSock), false, false);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(server.handshakeError_);\n  EXPECT_TRUE(client.handshakeError_);\n  ASYNC_cleanup_thread();\n}\n\nTEST(AsyncSSLSocketTest, OpenSSL110AsyncTestClosedWithCallbackPending) {\n  ASYNC_init_thread(1, 1);\n  EventBase eventBase;\n  std::optional<EventBaseThread> jobEvbThread;\n  jobEvbThread.emplace();\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  serverCtx->setSupportedClientCertificateAuthorityNamesFromFile(\n      find_resource(kTestCA).c_str());\n\n  auto rsaPointers =\n      setupCustomRSA(kTestCert, kTestKey, jobEvbThread->getEventBase());\n  CHECK(rsaPointers->dummyrsa);\n  // up-refs dummyrsa\n  SSL_CTX_use_RSAPrivateKey(serverCtx->getSSLCtx(), rsaPointers->dummyrsa);\n  SSL_CTX_set_mode(serverCtx->getSSLCtx(), SSL_MODE_ASYNC);\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  RSA_set_ex_data(rsaPointers->dummyrsa, kRSASocketExIndex, serverSock.get());\n\n  SSLHandshakeClient client(std::move(clientSock), false, false);\n  SSLHandshakeServer server(std::move(serverSock), false, false);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(server.handshakeError_);\n  EXPECT_TRUE(client.handshakeError_);\n  ASYNC_cleanup_thread();\n  jobEvbThread.reset();\n}\n#endif // FOLLY_SANITIZE_ADDRESS\n\nTEST(AsyncSSLSocketTest, LoadCertFromMemory) {\n  using folly::ssl::OpenSSLUtils;\n  auto cert = getFileAsBuf(kTestCert);\n  auto key = getFileAsBuf(kTestKey);\n\n  ssl::BioUniquePtr certBio(BIO_new(BIO_s_mem()));\n  BIO_write(certBio.get(), cert.data(), cert.size());\n  ssl::BioUniquePtr keyBio(BIO_new(BIO_s_mem()));\n  BIO_write(keyBio.get(), key.data(), key.size());\n\n  // Create SSL structs from buffers to get properties\n  ssl::X509UniquePtr certStruct(\n      PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr));\n  ssl::EvpPkeyUniquePtr keyStruct(\n      PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr));\n  certBio = nullptr;\n  keyBio = nullptr;\n\n  auto origCommonName = OpenSSLUtils::getCommonName(certStruct.get());\n  auto origKeySize = EVP_PKEY_bits(keyStruct.get());\n  certStruct = nullptr;\n  keyStruct = nullptr;\n\n  auto ctx = std::make_shared<SSLContext>();\n  ctx->loadPrivateKeyFromBufferPEM(key);\n  ctx->loadCertificateFromBufferPEM(cert);\n  ctx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  ssl::SSLUniquePtr ssl(ctx->createSSL());\n\n  auto newCert = SSL_get_certificate(ssl.get());\n  auto newKey = SSL_get_privatekey(ssl.get());\n\n  // Get properties from SSL struct\n  auto newCommonName = OpenSSLUtils::getCommonName(newCert);\n  auto newKeySize = EVP_PKEY_bits(newKey);\n\n  // Check that the key and cert have the expected properties\n  EXPECT_EQ(origCommonName, newCommonName);\n  EXPECT_EQ(origKeySize, newKeySize);\n}\n\nTEST(AsyncSSLSocketTest, MinWriteSizeTest) {\n  EventBase eb;\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  // create SSL socket\n  AsyncSSLSocket::UniquePtr socket(new AsyncSSLSocket(sslContext, &eb));\n\n  EXPECT_EQ(1500, socket->getMinWriteSize());\n\n  socket->setMinWriteSize(0);\n  EXPECT_EQ(0, socket->getMinWriteSize());\n  socket->setMinWriteSize(50000);\n  EXPECT_EQ(50000, socket->getMinWriteSize());\n}\n\nclass ReadCallbackTerminator : public ReadCallback {\n public:\n  ReadCallbackTerminator(EventBase* base, WriteCallbackBase* wcb)\n      : ReadCallback(wcb), base_(base) {}\n\n  // Do not write data back, terminate the loop.\n  void readDataAvailable(size_t len) noexcept override {\n    std::cerr << \"readDataAvailable, len \" << len << std::endl;\n\n    currentBuffer.length = len;\n\n    buffers.push_back(currentBuffer);\n    currentBuffer.reset();\n    state = STATE_SUCCEEDED;\n\n    socket_->setReadCB(nullptr);\n    base_->terminateLoopSoon();\n  }\n\n private:\n  EventBase* base_;\n};\n\n/**\n * Test a full unencrypted codepath\n */\nTEST(AsyncSSLSocketTest, UnencryptedTest) {\n  EventBase base;\n\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  NetworkSocket fds[2];\n  getfds(fds);\n  getctx(clientCtx, serverCtx);\n  auto client =\n      AsyncSSLSocket::newSocket(clientCtx, &base, fds[0], false, true);\n  std::shared_ptr<AsyncSSLSocket> server =\n      AsyncSSLSocket::newSocket(serverCtx, &base, fds[1], true, true);\n\n  ReadCallbackTerminator readCallback(&base, nullptr);\n  server->setReadCB(&readCallback);\n  readCallback.setSocket(server);\n\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  client->write(nullptr, buf, sizeof(buf));\n\n  // Check that bytes are unencrypted\n  char c;\n  EXPECT_EQ(1, netops::recv(fds[1], &c, 1, MSG_PEEK));\n  EXPECT_EQ('a', c);\n\n  EventBaseAborter eba(&base, 3000);\n  base.loop();\n\n  EXPECT_EQ(1, readCallback.buffers.size());\n  EXPECT_EQ(AsyncSSLSocket::STATE_UNENCRYPTED, client->getSSLState());\n\n  server->setReadCB(&readCallback);\n\n  // Unencrypted\n  server->sslAccept(nullptr);\n  client->sslConn(nullptr);\n\n  // Do NOT wait for handshake, writing should be queued and happen after\n\n  client->write(nullptr, buf, sizeof(buf));\n\n  // Check that bytes are *not* unencrypted\n  char c2;\n  EXPECT_EQ(1, netops::recv(fds[1], &c2, 1, MSG_PEEK));\n  EXPECT_NE('a', c2);\n\n  base.loop();\n\n  EXPECT_EQ(2, readCallback.buffers.size());\n  EXPECT_EQ(AsyncSSLSocket::STATE_ESTABLISHED, client->getSSLState());\n}\n\nTEST(AsyncSSLSocketTest, ConnectUnencryptedTest) {\n  auto clientCtx = std::make_shared<folly::SSLContext>();\n  auto serverCtx = std::make_shared<folly::SSLContext>();\n  getctx(clientCtx, serverCtx);\n\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  EventBase evb;\n  std::shared_ptr<AsyncSSLSocket> socket =\n      AsyncSSLSocket::newSocket(clientCtx, &evb, true);\n  socket->connect(nullptr, server.getAddress(), 0);\n\n  evb.loop();\n\n  EXPECT_EQ(AsyncSSLSocket::STATE_UNENCRYPTED, socket->getSSLState());\n  socket->sslConn(nullptr);\n  evb.loop();\n  EXPECT_EQ(AsyncSSLSocket::STATE_ESTABLISHED, socket->getSSLState());\n\n  // write()\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  socket->write(nullptr, buf.data(), buf.size());\n\n  // wait until the server has reflected all data written by the client\n  SemiFuture<StateEnum> future = writeCallback.getSemiFuture();\n  StateEnum state = std::move(future).get(std::chrono::seconds(3));\n  EXPECT_EQ(state, STATE_SUCCEEDED);\n  socket->close();\n}\n\n/**\n * Test acceptrunner in various situations\n */\nTEST(AsyncSSLSocketTest, SSLAcceptRunnerBasic) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  serverCtx->sslAcceptRunner(std::make_unique<SSLAcceptEvbRunner>(&eventBase));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_FALSE(client.handshakeError_);\n  EXPECT_LE(0, client.handshakeTime.count());\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_FALSE(server.handshakeError_);\n  EXPECT_LE(0, server.handshakeTime.count());\n}\n\nTEST(AsyncSSLSocketTest, SSLAcceptRunnerAcceptError) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  serverCtx->sslAcceptRunner(\n      std::make_unique<SSLAcceptErrorRunner>(&eventBase));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_FALSE(client.handshakeSuccess_);\n  EXPECT_TRUE(client.handshakeError_);\n  EXPECT_FALSE(server.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeError_);\n}\n\nTEST(AsyncSSLSocketTest, SSLAcceptRunnerAcceptClose) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  serverCtx->sslAcceptRunner(\n      std::make_unique<SSLAcceptCloseRunner>(&eventBase, serverSock.get()));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_FALSE(client.handshakeSuccess_);\n  EXPECT_TRUE(client.handshakeError_);\n  EXPECT_FALSE(server.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeError_);\n}\n\nTEST(AsyncSSLSocketTest, SSLAcceptRunnerAcceptDestroy) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  serverCtx->sslAcceptRunner(\n      std::make_unique<SSLAcceptDestroyRunner>(&eventBase, &server));\n\n  eventBase.loop();\n\n  EXPECT_FALSE(client.handshakeSuccess_);\n  EXPECT_TRUE(client.handshakeError_);\n  EXPECT_FALSE(server.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeError_);\n}\n\nTEST(AsyncSSLSocketTest, SSLAcceptRunnerFiber) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  serverCtx->sslAcceptRunner(\n      std::make_unique<SSLAcceptFiberRunner>(&eventBase));\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_FALSE(client.handshakeError_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_FALSE(server.handshakeError_);\n}\n\n#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB\nTEST(AsyncSSLSocketTest, SSLClientHelloRetryCallback) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n  auto* serverSockPtr = serverSock.get();\n\n  // State passed through the void* arg to the C callback\n  struct CbState {\n    bool firstCall = true;\n    EventBase* evb = nullptr;\n    AsyncSSLSocket* sock = nullptr;\n  };\n  CbState cbState;\n  cbState.evb = &eventBase;\n  cbState.sock = serverSockPtr;\n\n  // Non-capturing lambda convertible to C function pointer.\n  // State is accessed through the void* arg.\n  SSL_CTX_set_client_hello_cb(\n      serverCtx->getSSLCtx(),\n      [](SSL* /*ssl*/, int* /*al*/, void* arg) -> int {\n        auto* state = static_cast<CbState*>(arg);\n        if (state->firstCall) {\n          state->firstCall = false;\n          // Schedule restart — this runs AFTER handleReturnFromSSLAccept\n          // has set STATE_ASYNC_PENDING, because we're currently inside\n          // SSL_accept() which is inside the SSLAcceptEvbRunner's runInLoop.\n          auto* sock = state->sock;\n          state->evb->runInLoop([sock]() { sock->restartSSLAccept(); });\n          return SSL_CLIENT_HELLO_RETRY;\n        }\n        return SSL_CLIENT_HELLO_SUCCESS;\n      },\n      &cbState);\n\n  serverCtx->sslAcceptRunner(std::make_unique<SSLAcceptEvbRunner>(&eventBase));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_FALSE(client.handshakeError_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_FALSE(server.handshakeError_);\n}\n#endif\n\nstatic int newCloseCb(SSL* ssl, SSL_SESSION*) {\n  AsyncSSLSocket::getFromSSL(ssl)->closeNow();\n\n  // We do not want ownership over the supplied SSL_SESSION*, we return 0 to\n  // tell OpenSSL to free it on our behalf.\n  return 0;\n}\n\nstatic SSL_SESSION* getCloseCb(SSL* ssl, const unsigned char*, int, int*) {\n  AsyncSSLSocket::getFromSSL(ssl)->closeNow();\n  return nullptr;\n} // namespace\n\nTEST(AsyncSSLSocketTest, SSLAcceptRunnerFiberCloseSessionCb) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  SSL_CTX_set_session_cache_mode(\n      serverCtx->getSSLCtx(),\n      SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_SERVER);\n  SSL_CTX_sess_set_new_cb(serverCtx->getSSLCtx(), &newCloseCb);\n  SSL_CTX_sess_set_get_cb(serverCtx->getSSLCtx(), &getCloseCb);\n  serverCtx->sslAcceptRunner(\n      std::make_unique<SSLAcceptFiberRunner>(&eventBase));\n\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::VERIFY);\n  clientCtx->ciphers(\"AES128-SHA256\");\n  clientCtx->loadTrustedCertificates(find_resource(kTestCA).c_str());\n  clientCtx->setOptions(SSL_OP_NO_TICKET);\n\n  NetworkSocket fds[2];\n  getfds(fds);\n\n  AsyncSSLSocket::UniquePtr clientSock(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSock(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n  SSLHandshakeClient client(std::move(clientSock), true, true);\n  SSLHandshakeServer server(std::move(serverSock), true, true);\n\n  eventBase.loop();\n\n  // As close() is called during session callbacks, client sees it as a\n  // successful connection\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_FALSE(client.handshakeError_);\n  EXPECT_FALSE(server.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeError_);\n}\n\nTEST(AsyncSSLSocketTest, ConnResetErrorString) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  WriteErrorCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::EXPECT_ERROR);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  auto socket = std::make_shared<BlockingSocket>(server.getAddress(), nullptr);\n  socket->open();\n  uint8_t buf[3] = {0x16, 0x03, 0x01};\n  socket->write(buf, sizeof(buf));\n  socket->closeWithReset();\n\n  handshakeCallback.waitForHandshake();\n  EXPECT_NE(\n      handshakeCallback.errorString_.find(\"Network error\"), std::string::npos);\n  EXPECT_NE(handshakeCallback.errorString_.find(\"104\"), std::string::npos);\n}\n\nTEST(AsyncSSLSocketTest, ConnEOFErrorString) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  WriteErrorCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::EXPECT_ERROR);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  auto socket = std::make_shared<BlockingSocket>(server.getAddress(), nullptr);\n  socket->open();\n  uint8_t buf[3] = {0x16, 0x03, 0x01};\n  socket->write(buf, sizeof(buf));\n  socket->close();\n\n  handshakeCallback.waitForHandshake();\n  EXPECT_NE(\n      handshakeCallback.errorString_.find(\"Network error\"), std::string::npos);\n}\n\nTEST(AsyncSSLSocketTest, ConnOpenSSLErrorString) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  WriteErrorCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::EXPECT_ERROR);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  auto socket = std::make_shared<BlockingSocket>(server.getAddress(), nullptr);\n  socket->open();\n  uint8_t buf[256] = {0x16, 0x03};\n  memset(buf + 2, 'a', sizeof(buf) - 2);\n  socket->write(buf, sizeof(buf));\n  socket->close();\n\n  handshakeCallback.waitForHandshake();\n  EXPECT_NE(\n      handshakeCallback.errorString_.find(\"SSL routines\"), std::string::npos);\n#if defined(OPENSSL_IS_BORINGSSL)\n  EXPECT_NE(\n      handshakeCallback.errorString_.find(\"ENCRYPTED_LENGTH_TOO_LONG\"),\n      std::string::npos);\n#else\n  EXPECT_NE(\n      handshakeCallback.errorString_.find(\"packet length too long\"),\n      std::string::npos);\n#endif\n}\n\nTEST(AsyncSSLSocketTest, TestSSLCipherCodeToNameMap) {\n  using folly::ssl::OpenSSLUtils;\n  EXPECT_EQ(\n      OpenSSLUtils::getCipherName(0xc02c), \"ECDHE-ECDSA-AES256-GCM-SHA384\");\n  // TLS_DHE_RSA_WITH_DES_CBC_SHA - We shouldn't be building with this\n  EXPECT_EQ(OpenSSLUtils::getCipherName(0x0015), \"\");\n  // This indicates TLS_EMPTY_RENEGOTIATION_INFO_SCSV, no name expected\n  EXPECT_EQ(OpenSSLUtils::getCipherName(0x00ff), \"\");\n}\n\n#if defined __linux__\n/**\n * Ensure TransparentTLS flag is disabled with AsyncSSLSocket\n */\nTEST(AsyncSSLSocketTest, TTLSDisabled) {\n  // clear all setsockopt tracking history\n  globalStatic.reset();\n\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->open();\n\n  EXPECT_TRUE(\n      globalStatic.ttlsDisabledSet.contains(socket->getNetworkSocket()));\n\n  // write()\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  socket->write(buf.data(), buf.size());\n\n  // wait until the server has reflected all data written by the client\n  SemiFuture<StateEnum> future = writeCallback.getSemiFuture();\n  StateEnum state = std::move(future).get(std::chrono::seconds(3));\n  EXPECT_EQ(state, STATE_SUCCEEDED);\n\n  // close()\n  socket->close();\n}\n#endif\n\n#if FOLLY_ALLOW_TFO\n\nclass MockAsyncTFOSSLSocket : public AsyncSSLSocket {\n public:\n  using UniquePtr = std::unique_ptr<MockAsyncTFOSSLSocket, Destructor>;\n\n  explicit MockAsyncTFOSSLSocket(\n      std::shared_ptr<folly::SSLContext> sslCtx, EventBase* evb)\n      : AsyncSSLSocket(sslCtx, evb) {}\n\n  MOCK_METHOD(\n      ssize_t,\n      tfoSendMsg,\n      (NetworkSocket fd, struct msghdr* msg, int msg_flags));\n};\n\n#if defined __linux__\n/**\n * Ensure TransparentTLS flag is disabled with AsyncSSLSocket + TFO\n */\nTEST(AsyncSSLSocketTest, TTLSDisabledWithTFO) {\n  // clear all setsockopt tracking history\n  globalStatic.reset();\n\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, true);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->enableTFO();\n  socket->open();\n\n  EXPECT_TRUE(\n      globalStatic.ttlsDisabledSet.contains(socket->getNetworkSocket()));\n\n  // write()\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  socket->write(buf.data(), buf.size());\n\n  // wait until the server has reflected all data written by the client\n  SemiFuture<StateEnum> future = writeCallback.getSemiFuture();\n  StateEnum state = std::move(future).get(std::chrono::seconds(3));\n  EXPECT_EQ(state, STATE_SUCCEEDED);\n\n  // close()\n  socket->close();\n}\n#endif\n\n/**\n * Test connecting to, writing to, reading from, and closing the\n * connection to the SSL server with TFO.\n */\nTEST(AsyncSSLSocketTest, ConnectWriteReadCloseTFO) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, true);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->enableTFO();\n  socket->open();\n\n  // write()\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  socket->write(buf.data(), buf.size());\n\n  // read()\n  std::array<uint8_t, 128> readbuf;\n  uint32_t bytesRead = socket->readAll(readbuf.data(), readbuf.size());\n  EXPECT_EQ(bytesRead, 128);\n  EXPECT_EQ(memcmp(buf.data(), readbuf.data(), bytesRead), 0);\n\n  // close()\n  socket->close();\n}\n\n/**\n * Test connecting to, writing to, reading from, and closing the\n * connection to the SSL server with TFO.\n */\nTEST(AsyncSSLSocketTest, ConnectWriteReadCloseTFOWithTFOServerDisabled) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->enableTFO();\n  socket->open();\n\n  // write()\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  socket->write(buf.data(), buf.size());\n\n  // read()\n  std::array<uint8_t, 128> readbuf;\n  uint32_t bytesRead = socket->readAll(readbuf.data(), readbuf.size());\n  EXPECT_EQ(bytesRead, 128);\n  EXPECT_EQ(memcmp(buf.data(), readbuf.data(), bytesRead), 0);\n\n  // close()\n  socket->close();\n}\n\nclass ConnCallback : public AsyncSocket::ConnectCallback {\n public:\n  void connectSuccess() noexcept override { state = State::SUCCESS; }\n\n  void connectErr(const AsyncSocketException& ex) noexcept override {\n    state = State::ERROR;\n    error = ex.what();\n  }\n\n  enum class State { WAITING, SUCCESS, ERROR };\n\n  State state{State::WAITING};\n  std::string error;\n};\n\ntemplate <class Cardinality>\nMockAsyncTFOSSLSocket::UniquePtr setupSocketWithFallback(\n    EventBase* evb, const SocketAddress& address, Cardinality cardinality) {\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n\n  // connect\n  auto socket = MockAsyncTFOSSLSocket::UniquePtr(\n      new MockAsyncTFOSSLSocket(sslContext, evb));\n  socket->enableTFO();\n\n  EXPECT_CALL(*socket, tfoSendMsg(_, _, _))\n      .Times(cardinality)\n      .WillOnce(Invoke([&](NetworkSocket fd, struct msghdr*, int) {\n        sockaddr_storage addr;\n        auto len = address.getAddress(&addr);\n        return netops::connect(fd, (const struct sockaddr*)&addr, len);\n      }));\n  return socket;\n}\n\nTEST(AsyncSSLSocketTest, ConnectWriteReadCloseTFOFallback) {\n  if (!folly::test::isTFOAvailable()) {\n    GTEST_SKIP() << \"TFO not supported.\";\n  }\n\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, true);\n\n  EventBase evb;\n\n  auto socket = setupSocketWithFallback(&evb, server.getAddress(), 1);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  evb.loop();\n  EXPECT_EQ(ConnCallback::State::SUCCESS, ccb.state);\n\n  evb.runInEventBaseThread([&] { socket->detachEventBase(); });\n  evb.loop();\n\n  BlockingSocket sock(std::move(socket));\n  // write()\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n  sock.write(buf.data(), buf.size());\n\n  // read()\n  std::array<uint8_t, 128> readbuf;\n  uint32_t bytesRead = sock.readAll(readbuf.data(), readbuf.size());\n  EXPECT_EQ(bytesRead, 128);\n  EXPECT_EQ(memcmp(buf.data(), readbuf.data(), bytesRead), 0);\n\n  // close()\n  sock.close();\n}\n\n#if !defined(OPENSSL_IS_BORINGSSL)\nTEST(AsyncSSLSocketTest, ConnectTFOTimeout) {\n  // Start listening on a local port\n  ConnectTimeoutCallback acceptCallback;\n  TestSSLServer server(&acceptCallback, true);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->enableTFO();\n  EXPECT_THROW(\n      socket->open(std::chrono::milliseconds(20)), AsyncSocketException);\n}\n#endif\n\n#if !defined(OPENSSL_IS_BORINGSSL)\nTEST(AsyncSSLSocketTest, ConnectTFOFallbackTimeout) {\n  // Start listening on a local port\n  ConnectTimeoutCallback acceptCallback;\n  TestSSLServer server(&acceptCallback, true);\n\n  EventBase evb;\n\n  auto socket = setupSocketWithFallback(&evb, server.getAddress(), AtMost(1));\n  ConnCallback ccb;\n  // Set a short timeout\n  socket->connect(&ccb, server.getAddress(), 1);\n\n  evb.loop();\n  EXPECT_EQ(ConnCallback::State::ERROR, ccb.state);\n}\n#endif\n\nTEST(AsyncSSLSocketTest, HandshakeTFOFallbackTimeout) {\n  // Start listening on a local port\n  EmptyReadCallback readCallback;\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::EXPECT_ERROR);\n  HandshakeTimeoutCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback, true);\n\n  EventBase evb;\n\n  auto socket = setupSocketWithFallback(&evb, server.getAddress(), AtMost(1));\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 100);\n\n  evb.loop();\n  EXPECT_EQ(ConnCallback::State::ERROR, ccb.state);\n  EXPECT_THAT(ccb.error, testing::HasSubstr(\"SSL connect timed out\"));\n}\n\nTEST(AsyncSSLSocketTest, HandshakeTFORefused) {\n  // Start listening on a local port\n  EventBase evb;\n\n  // Hopefully nothing is listening on this address\n  SocketAddress addr(\"127.0.0.1\", 65535);\n  auto socket = setupSocketWithFallback(&evb, addr, AtMost(1));\n  ConnCallback ccb;\n  socket->connect(&ccb, addr, 100);\n\n  evb.loop();\n  EXPECT_EQ(ConnCallback::State::ERROR, ccb.state);\n  EXPECT_THAT(ccb.error, testing::HasSubstr(\"refused\"));\n}\n\nTEST(AsyncSSLSocketTest, TestPreReceivedData) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n  std::array<NetworkSocket, 2> fds;\n  getfds(fds.data());\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSockPtr(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSockPtr(\n      new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));\n  auto clientSock = clientSockPtr.get();\n  auto serverSock = serverSockPtr.get();\n  SSLHandshakeClient client(std::move(clientSockPtr), true, true);\n\n  // Steal some data from the server.\n  std::array<uint8_t, 10> buf;\n  auto bytesReceived = netops::recv(fds[1], buf.data(), buf.size(), 0);\n  checkUnixError(bytesReceived, \"recv failed\");\n\n  serverSock->setPreReceivedData(\n      IOBuf::wrapBuffer(ByteRange(buf.data(), bytesReceived)));\n  SSLHandshakeServer server(std::move(serverSockPtr), true, true);\n  // The order in which the client and server handshake completion callbacks\n  // fire differs between TLS 1.2 and TLS 1.3. Loop until both are complete to\n  // ensure this works regardless of TLS version.\n  while ((!client.handshakeSuccess_ && !client.handshakeError_) ||\n         (!server.handshakeSuccess_ && !server.handshakeError_)) {\n    eventBase.loopOnce();\n  }\n\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_EQ(\n      serverSock->getRawBytesReceived(), clientSock->getRawBytesWritten());\n}\n\nTEST(AsyncSSLSocketTest, TestMoveFromAsyncSocket) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto dfServerCtx = std::make_shared<SSLContext>();\n  std::array<NetworkSocket, 2> fds;\n  getfds(fds.data());\n  getctx(clientCtx, dfServerCtx);\n\n  AsyncSSLSocket::UniquePtr clientSockPtr(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSocket::UniquePtr serverSockPtr(new AsyncSocket(&eventBase, fds[1]));\n  auto clientSock = clientSockPtr.get();\n  auto serverSock = serverSockPtr.get();\n  SSLHandshakeClient client(std::move(clientSockPtr), true, true);\n\n  // Steal some data from the server.\n  std::array<uint8_t, 10> buf;\n  auto bytesReceived = netops::recv(fds[1], buf.data(), buf.size(), 0);\n  checkUnixError(bytesReceived, \"recv failed\");\n\n  serverSock->setPreReceivedData(\n      IOBuf::wrapBuffer(ByteRange(buf.data(), bytesReceived)));\n  AsyncSSLSocket::UniquePtr serverSSLSockPtr(\n      new AsyncSSLSocket(dfServerCtx, std::move(serverSockPtr), true));\n  auto serverSSLSock = serverSSLSockPtr.get();\n  SSLHandshakeServer server(std::move(serverSSLSockPtr), true, true);\n  // The order in which the client and server handshake completion callbacks\n  // fire differs between TLS 1.2 and TLS 1.3. Loop until both are complete to\n  // ensure this works regardless of TLS version.\n  while ((!client.handshakeSuccess_ && !client.handshakeError_) ||\n         (!server.handshakeSuccess_ && !server.handshakeError_)) {\n    eventBase.loopOnce();\n  }\n\n  EXPECT_TRUE(client.handshakeSuccess_);\n  EXPECT_TRUE(server.handshakeSuccess_);\n  EXPECT_EQ(\n      serverSSLSock->getRawBytesReceived(), clientSock->getRawBytesWritten());\n}\n\nTEST(AsyncSSLSocketTest, TestNullConnectCallbackError) {\n  EventBase eventBase;\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  std::array<NetworkSocket, 2> fds;\n  getfds(fds.data());\n  getctx(clientCtx, serverCtx);\n  // Mismatched ciphers\n  clientCtx->setCiphersuitesOrThrow(\"TLS_AES_256_GCM_SHA384\");\n  serverCtx->setCiphersuitesOrThrow(\"TLS_AES_128_GCM_SHA256\");\n\n  AsyncSSLSocket::UniquePtr clientSockPtr(\n      new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n  AsyncSSLSocket::UniquePtr serverSockPtr(\n      new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n  auto clientSock = clientSockPtr.get();\n\n  clientSock->sslConn(nullptr);\n\n  ExpectSSLWriteErrorCallback wcb;\n  wcb.setSocket(std::move(clientSockPtr));\n  clientSock->write(&wcb, \"abc\", 3);\n\n  SSLHandshakeServer server(std::move(serverSockPtr), true, true);\n  eventBase.loop();\n\n  EXPECT_FALSE(server.handshakeSuccess_);\n}\n\nTEST(AsyncSSLSocketTest, TestSSLSetClientOptionsP256) {\n  EventBase evb;\n  std::array<NetworkSocket, 2> fds;\n  getfds(fds.data());\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->setSupportedGroups(std::vector<std::string>({\"P-256\"}));\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  ssl::SSLCommonOptions::setClientOptions(*clientCtx);\n\n  auto clientSocket =\n      AsyncSSLSocket::newSocket(clientCtx, &evb, fds[0], false, true);\n  std::shared_ptr<AsyncSSLSocket> serverSocket =\n      AsyncSSLSocket::newSocket(serverCtx, &evb, fds[1], true, true);\n\n  ReadCallbackTerminator readCallback(&evb, nullptr);\n  serverSocket->setReadCB(&readCallback);\n  readCallback.setSocket(serverSocket);\n  serverSocket->sslAccept(nullptr);\n  clientSocket->sslConn(nullptr);\n\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  clientSocket->write(nullptr, buf, sizeof(buf));\n\n  EventBaseAborter eba(&evb, 3000);\n\n  evb.loop();\n\n  auto sharedGroupName = serverSocket->getNegotiatedGroup();\n  EXPECT_THAT(sharedGroupName, testing::HasSubstr(\"prime256v1\"));\n}\n\nTEST(AsyncSSLSocketTest, TestSSLSetClientOptionsX25519) {\n  EventBase evb;\n  std::array<NetworkSocket, 2> fds;\n  getfds(fds.data());\n  auto clientCtx = std::make_shared<SSLContext>();\n  auto serverCtx = std::make_shared<SSLContext>();\n  serverCtx->setSupportedGroups(std::vector<std::string>({\"X25519\", \"P-256\"}));\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  ssl::SSLCommonOptions::setClientOptions(*clientCtx);\n\n  auto clientSocket =\n      AsyncSSLSocket::newSocket(clientCtx, &evb, fds[0], false, true);\n  std::shared_ptr<AsyncSSLSocket> serverSocket =\n      AsyncSSLSocket::newSocket(serverCtx, &evb, fds[1], true, true);\n\n  ReadCallbackTerminator readCallback(&evb, nullptr);\n  serverSocket->setReadCB(&readCallback);\n  readCallback.setSocket(serverSocket);\n  serverSocket->sslAccept(nullptr);\n  clientSocket->sslConn(nullptr);\n\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  clientSocket->write(nullptr, buf, sizeof(buf));\n\n  EventBaseAborter eba(&evb, 3000);\n\n  evb.loop();\n\n  auto sharedGroupName = serverSocket->getNegotiatedGroup();\n  EXPECT_THAT(sharedGroupName, testing::HasSubstr(\"X25519\"));\n}\n\n/**\n * Test overriding the flags passed to \"sendmsg()\" system call,\n * and verifying that write requests fail properly.\n */\nTEST(AsyncSSLSocketTest, SendMsgParamsCallback) {\n  // Start listening on a local port\n  SendMsgFlagsCallback msgCallback;\n  ExpectWriteErrorCallback writeCallback(&msgCallback);\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  // Set up SSL context.\n  auto sslContext = std::make_shared<SSLContext>();\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n\n  // connect\n  auto socket =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket->open();\n\n  // Setting flags to \"-1\" to trigger \"Invalid argument\" error\n  // on attempt to use this flags in sendmsg() system call.\n  msgCallback.resetFlags(-1);\n\n  // write()\n  std::vector<uint8_t> buf(128, 'a');\n  ASSERT_EQ(socket->write(buf.data(), buf.size()), buf.size());\n\n  // close()\n  socket->close();\n\n  cerr << \"SendMsgParamsCallback test completed\" << endl;\n}\n\n#if FOLLY_HAVE_SO_TIMESTAMPING\n\nclass AsyncSSLSocketByteEventTest : public ::testing::Test {\n protected:\n  using MockDispatcher = ::testing::NiceMock<netops::test::MockDispatcher>;\n  using TestObserver =\n      test::MockAsyncSocketLegacyLifecycleObserverForByteEvents;\n  using ByteEventType = AsyncSocket::ByteEvent::Type;\n\n  /**\n   * Components of a client connection to TestServer.\n   *\n   * Includes EventBase, client's AsyncSocket.\n   */\n  class ClientConn {\n   public:\n    explicit ClientConn(\n        std::shared_ptr<TestSSLServer> server,\n        std::shared_ptr<AsyncSSLSocket> socket = nullptr)\n        : server_(std::move(server)), socket_(std::move(socket)) {\n      if (!socket_) {\n        socket_ = AsyncSSLSocket::newSocket(getSslContext(), &getEventBase());\n      }\n      socket_->setOverrideNetOpsDispatcher(netOpsDispatcher_);\n      netOpsDispatcher_->forwardToDefaultImpl();\n    }\n\n    ~ClientConn() {\n      if (socket_) {\n        socket_->close();\n      }\n    }\n\n    void connect() {\n      CHECK_NOTNULL(socket_.get());\n      CHECK_NOTNULL(socket_->getEventBase());\n      socket_->connect(&connCb_, server_->getAddress(), 30);\n      socket_->getEventBase()->loop();\n      ASSERT_EQ(connCb_.state, ConnCallback::State::SUCCESS);\n      setReadCb();\n    }\n\n    void waitForHandshake() {\n      while (connCb_.state == ConnCallback::State::WAITING) {\n        socket_->getEventBase()->loopOnce();\n      }\n    }\n\n    void setReadCb() {\n      // Due to how libevent works, we currently need to be subscribed to\n      // EV_READ events in order to get error messages.\n      //\n      // TODO(bschlinker): Resolve this with libevent modification.\n      // See https://github.com/libevent/libevent/issues/1038 for details.\n      socket_->setReadCB(&readCb_);\n      readCb_.setSocket(socket_);\n    }\n\n    std::shared_ptr<NiceMock<TestObserver>> attachObserver(\n        bool enableByteEvents) {\n      auto observer = AsyncSSLSocketByteEventTest::attachObserver(\n          socket_.get(), enableByteEvents);\n      observers.push_back(observer);\n      return observer;\n    }\n\n    /**\n     * Write to client socket, echo at server, and wait for echo at client.\n     *\n     * Waiting for echo at client ensures that we have given opportunity for\n     * timestamps to be generated by the kernel.\n     */\n    void writeAndReflect(\n        const std::vector<uint8_t>& wbuf, const WriteFlags writeFlags) {\n      CHECK_NOTNULL(socket_.get());\n      CHECK_NOTNULL(socket_->getEventBase());\n\n      // write to the client socket\n      WriteCallbackBase wcb;\n      socket_->write(&wcb, wbuf.data(), wbuf.size(), writeFlags);\n      while (wcb.state == STATE_WAITING) {\n        socket_->getEventBase()->loopOnce();\n      }\n      ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n      // TestSSLServer reads and reflects for us\n\n      // read reflection at client\n      while (wbuf.size() != readCb_.dataRead()) {\n        socket_->getEventBase()->loopOnce();\n      }\n      readCb_.verifyData(wbuf.data(), wbuf.size());\n      readCb_.clearData();\n    }\n\n    std::shared_ptr<AsyncSSLSocket> getRawSocket() { return socket_; }\n\n    std::shared_ptr<SSLContext> getSslContext() {\n      static std::shared_ptr<SSLContext> sslContext = initSslContext();\n      return sslContext;\n    }\n\n    EventBase& getEventBase() {\n      static EventBase evb; // use same EventBase for all client sockets\n      return evb;\n    }\n\n    void netOpsExpectTimestampingSetSockOpt() {\n      // must whitelist other calls\n      EXPECT_CALL(*netOpsDispatcher_, setsockopt(_, _, _, _, _))\n          .Times(AnyNumber());\n      EXPECT_CALL(\n          *netOpsDispatcher_, setsockopt(_, SOL_SOCKET, SO_TIMESTAMPING, _, _))\n          .Times(1);\n    }\n\n    void netOpsExpectNoTimestampingSetSockOpt() {\n      // must whitelist other calls\n      EXPECT_CALL(*netOpsDispatcher_, setsockopt(_, _, _, _, _))\n          .Times(AnyNumber());\n      EXPECT_CALL(\n          *netOpsDispatcher_, setsockopt(_, SOL_SOCKET, SO_TIMESTAMPING, _, _))\n          .Times(0);\n    }\n\n    void netOpsExpectWriteWithFlags(WriteFlags writeFlags) {\n      EXPECT_CALL(*netOpsDispatcher_, sendmsg(_, _, _))\n          .WillOnce(Invoke(\n              [this, writeFlags](\n                  NetworkSocket socket, const msghdr* message, int flags) {\n                EXPECT_EQ(writeFlags, getMsgWriteFlags(*message));\n                return netOpsDispatcher_->netops::Dispatcher::sendmsg(\n                    socket, message, flags);\n              }));\n    }\n\n    void netOpsVerifyAndClearExpectations() {\n      Mock::VerifyAndClearExpectations(netOpsDispatcher_.get());\n    }\n\n    /**\n     * Static utilities.\n     */\n    static std::shared_ptr<SSLContext> initSslContext() {\n      auto sslContext = std::make_shared<SSLContext>();\n      sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n      return sslContext;\n    }\n\n   private:\n    // server\n    std::shared_ptr<TestSSLServer> server_;\n\n    // managed observers\n    std::vector<std::shared_ptr<TestObserver>> observers;\n\n    // socket components\n    ConnCallback connCb_;\n    ReadCallback readCb_;\n    std::shared_ptr<MockDispatcher> netOpsDispatcher_{\n        std::make_shared<MockDispatcher>()};\n    std::shared_ptr<AsyncSSLSocket> socket_;\n  };\n\n  ClientConn getClientConn() { return ClientConn(server_); }\n\n  void SetUp() override {\n    serverWriteCb_ = std::make_unique<WriteCallbackBase>();\n    serverReadCb_ = std::make_unique<ReadCallback>(serverWriteCb_.get());\n    serverHandshakeCb_ =\n        std::make_unique<HandshakeCallback>(serverReadCb_.get());\n    serverAcceptCb_ =\n        std::make_unique<SSLServerAcceptCallback>(serverHandshakeCb_.get());\n    server_ = std::make_shared<TestSSLServer>(serverAcceptCb_.get());\n  }\n\n  /**\n   * Static utility functions.\n   */\n\n  static std::shared_ptr<NiceMock<TestObserver>> attachObserver(\n      AsyncSocket* socket, bool enableByteEvents) {\n    AsyncSocket::LegacyLifecycleObserver::Config config = {};\n    config.byteEvents = enableByteEvents;\n    return std::make_shared<NiceMock<TestObserver>>(socket, config);\n  }\n\n  static WriteFlags getMsgWriteFlags(const struct msghdr& msg) {\n    const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n    if (!cmsg || cmsg->cmsg_level != SOL_SOCKET ||\n        cmsg->cmsg_type != SO_TIMESTAMPING ||\n        cmsg->cmsg_len != CMSG_LEN(sizeof(uint32_t))) {\n      return WriteFlags::NONE;\n    }\n\n    const uint32_t* sofFlags =\n        (reinterpret_cast<const uint32_t*>(CMSG_DATA(cmsg)));\n    WriteFlags flags = WriteFlags::NONE;\n    if (*sofFlags & folly::netops::SOF_TIMESTAMPING_TX_SCHED) {\n      flags = flags | WriteFlags::TIMESTAMP_SCHED;\n    }\n    if (*sofFlags & folly::netops::SOF_TIMESTAMPING_TX_SOFTWARE) {\n      flags = flags | WriteFlags::TIMESTAMP_TX;\n    }\n    if (*sofFlags & folly::netops::SOF_TIMESTAMPING_TX_ACK) {\n      flags = flags | WriteFlags::TIMESTAMP_ACK;\n    }\n\n    return flags;\n  }\n\n  static WriteFlags dropWriteFromFlags(WriteFlags writeFlags) {\n    return writeFlags & ~WriteFlags::TIMESTAMP_WRITE;\n  }\n\n  // server components\n  std::unique_ptr<WriteCallbackBase> serverWriteCb_;\n  std::unique_ptr<ReadCallback> serverReadCb_;\n  std::unique_ptr<HandshakeCallback> serverHandshakeCb_;\n  std::unique_ptr<SSLServerAcceptCallback> serverAcceptCb_;\n  std::shared_ptr<TestSSLServer> server_;\n};\n\nTEST_F(AsyncSSLSocketByteEventTest, ObserverAttachedBeforeConnect) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  clientConn.connect();\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  {\n    clientConn.netOpsExpectWriteWithFlags(dropWriteFromFlags(flags));\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n\n    // may have more than four new ByteEvents if write split further by kernel\n    EXPECT_THAT(observer->byteEvents, SizeIs(Ge(4)));\n\n    // due to SSL overhead, offset will not be 0\n    auto offsetExpected = clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write again to check offsets\n  {\n    const auto startNumByteEvents = observer->byteEvents.size();\n    clientConn.netOpsExpectWriteWithFlags(dropWriteFromFlags(flags));\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n\n    // may have more than four new ByteEvents if write split further by kernel\n    EXPECT_THAT(observer->byteEvents, SizeIs(Ge(startNumByteEvents + 4)));\n\n    // due to SSL overhead, offset will not be 1\n    auto offsetExpected = clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\nTEST_F(AsyncSSLSocketByteEventTest, ObserverAttachedAfterConnect) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // We make sure the server writes at least one byte to the client before\n  // enabling byte events. Otherwise, the test is flaky. We believe this happens\n  // when the following sequence occurs:\n  //\n  // (1) The client socket enables byte events successfully;\n  //\n  // (2) The client socket writes data to the server with timestamping set;\n  //\n  // (3) The client socket receives byte events for WRITE, SCHED, and TX and\n  //     processes them using `handleErrMessages` in `ioReady`. Once the error\n  //     queue is empty, `ioReady` calls `handleRead`. During this time, the\n  //     client also begins to read data sent by the server as part of the SSL\n  //     handshake completion;\n  //\n  // (4) The server socket receives the data from the client and reflects it\n  //     back;\n  //\n  // (5) The client socket receives the data reflected by the server. When a\n  //     data packet contains an ACK for previous data, the kernel emits the ACK\n  //     timestamp before handing off the received data to userspace. However,\n  //     in this case, since the client socket is still inside the `handleRead`\n  //     function (step 3), it will process the read, even though the ACK\n  //     timestamp is already enqueued in the error queue. When it finishes\n  //     reading the data, it does not go back to handle messages from the error\n  //     queue until the next call to `ioReady`. This causes several `EXPECT`\n  //     statements below to fail.\n\n  serverHandshakeCb_->waitForHandshake();\n  clientConn.waitForHandshake();\n  clientConn.writeAndReflect(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  {\n    clientConn.netOpsExpectWriteWithFlags(dropWriteFromFlags(flags));\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n\n    // may have more than four new ByteEvents if write split further by kernel\n    EXPECT_THAT(observer->byteEvents, SizeIs(Ge(4)));\n\n    // due to SSL overhead, offset will not be 0\n    auto offsetExpected = clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write again to check offsets\n  {\n    const auto startNumByteEvents = observer->byteEvents.size();\n    clientConn.netOpsExpectWriteWithFlags(dropWriteFromFlags(flags));\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n\n    // may have more than four new ByteEvents if write split further by kernel\n    EXPECT_THAT(observer->byteEvents, SizeIs(Ge(startNumByteEvents + 4)));\n\n    // due to SSL overhead, offset will not be 1\n    auto offsetExpected = clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\nTEST_F(AsyncSSLSocketByteEventTest, MultiByteWrites) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n\n  auto clientConn = getClientConn();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  clientConn.connect();\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // write 20 bytes\n  {\n    std::vector<uint8_t> wbuf(20, 'a'); // 20 bytes\n\n    clientConn.netOpsExpectWriteWithFlags(dropWriteFromFlags(flags));\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n\n    // may have more than four new ByteEvents if write split further by kernel\n    EXPECT_THAT(observer->byteEvents, SizeIs(Ge(4)));\n\n    // due to SSL overhead, offset will not be 0\n    auto offsetExpected = clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write 40 bytes\n  {\n    std::vector<uint8_t> wbuf(20, 'a'); // 20 bytes\n\n    const auto startNumByteEvents = observer->byteEvents.size();\n    clientConn.netOpsExpectWriteWithFlags(dropWriteFromFlags(flags));\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n\n    // may have more than four new ByteEvents if write split further by kernel\n    EXPECT_THAT(observer->byteEvents, SizeIs(Ge(startNumByteEvents + 4)));\n\n    // due to SSL overhead, offset will not be 1\n    auto offsetExpected = clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\nTEST_F(AsyncSSLSocketByteEventTest, MultiByteWritesEnableSecondWrite) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // write 20 bytes with no ByteEvents / observer\n  {\n    std::vector<uint8_t> wbuf(20, 'a'); // 20 bytes\n    clientConn.netOpsExpectWriteWithFlags(WriteFlags::NONE);\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n  }\n\n  // enable observer\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // write 40 bytes\n  {\n    std::vector<uint8_t> wbuf(20, 'a'); // 20 bytes\n\n    const auto startNumByteEvents = observer->byteEvents.size();\n    clientConn.netOpsExpectWriteWithFlags(dropWriteFromFlags(flags));\n    clientConn.writeAndReflect(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n\n    // may have more than four new ByteEvents if write split further by kernel\n    EXPECT_THAT(observer->byteEvents, SizeIs(Ge(startNumByteEvents + 4)));\n\n    // due to SSL overhead, offset will not be 1\n    auto offsetExpected = clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        offsetExpected,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\n#endif // FOLLY_HAVE_SO_TIMESTAMPING\n\n#endif // __linux__\n\n// TLS 1.2 and TLS 1.3 deliver session tickets in different ways, but we can use\n// SSLContext::SessionLifecycleCallbacks to receive them in a similar manner so\n// tests can work regardless of version.\nclass SimpleSessionLifecycleCallback\n    : public SSLContext::SessionLifecycleCallbacks {\n public:\n  void onNewSession(SSL*, ssl::SSLSessionUniquePtr session) override {\n    // This can be called multiple times. OpenSSL sends two session tickets by\n    // default). Grab the last one.\n    session_ = std::move(session);\n    ASSERT_TRUE(socket_ != nullptr);\n    // At this point we have what we need to resume a session. Detach the\n    // ReadCallback, allowing the socket's EventBase to stop looping.\n    socket_->setReadCB(nullptr);\n  }\n\n  // set when session is available\n  folly::ssl::SSLSessionUniquePtr session_;\n  // set after object construction\n  folly::AsyncSSLSocket* socket_;\n};\n\nTEST(AsyncSSLSocketTest, TestSNIClientHelloBehavior) {\n  EventBase eventBase;\n  auto serverCtx = std::make_shared<SSLContext>();\n  auto clientCtx = std::make_shared<SSLContext>();\n  serverCtx->loadPrivateKey(find_resource(kTestKey).c_str());\n  serverCtx->loadCertificate(find_resource(kTestCert).c_str());\n\n  auto sessionCb = std::make_unique<SimpleSessionLifecycleCallback>();\n  auto sessionCbPtr = sessionCb.get();\n  clientCtx->setSessionLifecycleCallbacks(std::move(sessionCb));\n  clientCtx->setSessionCacheContext(\"test context\");\n  clientCtx->setVerificationOption(SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  folly::ssl::SSLSessionUniquePtr resumptionSession = nullptr;\n\n  {\n    std::array<NetworkSocket, 2> fds;\n    getfds(fds.data());\n\n    AsyncSSLSocket::UniquePtr clientSock(\n        new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n    AsyncSSLSocket::UniquePtr serverSock(\n        new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n    // Client sends SNI that doesn't match anything the server cert advertises\n    clientSock->setServerName(\"Foobar\");\n    auto clientSockPtr = clientSock.get();\n    SSLHandshakeServerParseClientHello server(\n        std::move(serverSock), true, true);\n    // capture client socket in session callback, so we can detach event base\n    sessionCbPtr->socket_ = clientSockPtr;\n    SSLHandshakeClient client(std::move(clientSock), true, true);\n\n    // should stop when the session ticket is received\n    ReadCallback readCallback;\n    clientSockPtr->setReadCB(&readCallback);\n    readCallback.state = STATE_SUCCEEDED;\n    // loop until we receive session (and unregister read callback)\n    eventBase.loop();\n    resumptionSession = std::move(sessionCbPtr->session_);\n\n    serverSock = std::move(server).moveSocket();\n    auto chi = serverSock->getClientHelloInfo();\n    ASSERT_NE(chi, nullptr);\n    EXPECT_EQ(\n        std::string(\"Foobar\"), std::string(serverSock->getSSLServerName()));\n\n    // create another client, resuming with the prior session, but under a\n    // different common name.\n    clientSock = std::move(client).moveSocket();\n    ASSERT_TRUE(resumptionSession != nullptr);\n  }\n\n  {\n    std::array<NetworkSocket, 2> fds;\n    getfds(fds.data());\n\n    AsyncSSLSocket::UniquePtr clientSock(\n        new AsyncSSLSocket(clientCtx, &eventBase, fds[0], false));\n    AsyncSSLSocket::UniquePtr serverSock(\n        new AsyncSSLSocket(serverCtx, &eventBase, fds[1], true));\n\n    clientSock->setRawSSLSession(std::move(resumptionSession));\n    clientSock->setServerName(\"Baz\");\n    SSLHandshakeServerParseClientHello server(\n        std::move(serverSock), true, true);\n    SSLHandshakeClient client(std::move(clientSock), true, true);\n    eventBase.loop();\n\n    serverSock = std::move(server).moveSocket();\n    clientSock = std::move(client).moveSocket();\n    EXPECT_TRUE(clientSock->getSSLSessionReused());\n\n    // OpenSSL 1.1.1 changes the semantics of SSL_get_servername\n    // in\n    // https://github.com/openssl/openssl/commit/1c4aa31d79821dee9be98e915159d52cc30d8403\n    //\n    // Previously, the SNI would be taken from the ClientHello.\n    // Now, the SNI will be taken from the established session.\n    //\n    // But the session that was established with the client (prior handshake)\n    // would not have set the server name field because the SNI that the client\n    // requested (\"Foobar\") did not match any of the SANs that the server was\n    // presenting (\"127.0.0.1\")\n    //\n    // To preserve this 1.1.0 behavior, getSSLServerName() should return the\n    // parsed ClientHello servername. This test asserts this behavior.\n    auto sni = serverSock->getSSLServerName();\n    ASSERT_NE(sni, nullptr);\n\n    std::string sniStr(sni);\n    EXPECT_EQ(sniStr, std::string(\"Baz\"));\n  }\n}\n\nTEST(AsyncSSLSocketTest, BytesWrittenWithMove) {\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallback acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  auto sslContext = std::make_shared<SSLContext>();\n  sslContext->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  auto socket1 =\n      std::make_shared<BlockingSocket>(server.getAddress(), sslContext);\n  socket1->open(std::chrono::milliseconds(10000));\n\n  // write\n  std::vector<uint8_t> wbuf(128, 'a');\n  socket1->write(wbuf.data(), wbuf.size());\n  const auto socket1AppBytes = socket1->getSocket()->getAppBytesWritten();\n  const auto socket1RawBytes = socket1->getSocket()->getRawBytesWritten();\n  EXPECT_EQ(128, socket1AppBytes);\n  EXPECT_LT(128, socket1RawBytes);\n\n  // read reflection\n  std::vector<uint8_t> readbuf(wbuf.size());\n  uint32_t bytesRead = socket1->readAll(readbuf.data(), readbuf.size());\n  EXPECT_EQ(bytesRead, wbuf.size());\n\n  // additional sanity checks on virtuals\n  EXPECT_EQ(\n      socket1->getSSLSocket()->getRawBytesWritten(),\n      socket1->getSocket()->getRawBytesWritten());\n  EXPECT_EQ(128, socket1->getSocket()->getAppBytesWritten());\n  EXPECT_EQ(128, socket1->getSSLSocket()->getAppBytesWritten());\n\n  // move to another AsyncSSLSocket\n  AsyncSSLSocket::UniquePtr socket2(\n      new AsyncSSLSocket(sslContext, socket1->getSocket()));\n  EXPECT_EQ(socket1AppBytes, socket2->getAppBytesWritten());\n  EXPECT_EQ(socket1RawBytes, socket2->getRawBytesWritten());\n\n  // move to an AsyncSocket\n  AsyncSocket::UniquePtr socket3(new AsyncSocket(std::move(socket2)));\n  EXPECT_EQ(socket1AppBytes, socket3->getAppBytesWritten());\n  EXPECT_EQ(socket1RawBytes, socket3->getRawBytesWritten());\n}\n\n#ifdef SIGPIPE\n///////////////////////////////////////////////////////////////////////////\n// init_unit_test_suite\n///////////////////////////////////////////////////////////////////////////\nnamespace {\nstruct Initializer {\n  Initializer() { signal(SIGPIPE, SIG_IGN); }\n};\nInitializer initializer;\n} // namespace\n#endif\n"
  },
  {
    "path": "folly/io/async/test/AsyncSSLSocketTest.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fcntl.h>\n#include <signal.h>\n#include <sys/types.h>\n\n#include <condition_variable>\n#include <iostream>\n#include <list>\n#include <memory>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/SocketAddress.h>\n#include <folly/fibers/FiberManagerMap.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncSSLSocket.h>\n#include <folly/io/async/AsyncServerSocket.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/ssl/SSLErrors.h>\n#include <folly/io/async/test/TestSSLServer.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/PThread.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/String.h>\n#include <folly/portability/Unistd.h>\n#include <folly/testing/TestUtil.h>\n\nnamespace folly::test {\n\n// The destructors of all callback classes assert that the state is\n// STATE_SUCCEEDED, for both possitive and negative tests. The tests\n// are responsible for setting the succeeded state properly before the\n// destructors are called.\n\nclass SendMsgParamsCallbackBase\n    : public folly::AsyncSocket::SendMsgParamsCallback {\n public:\n  SendMsgParamsCallbackBase() {}\n\n  void setSocket(const std::shared_ptr<AsyncSSLSocket>& socket) {\n    socket_ = socket;\n    oldCallback_ = socket_->getSendMsgParamsCB();\n    socket_->setSendMsgParamCB(this);\n    socket_->setEorTracking(trackEor_);\n  }\n\n  void setEorTracking(bool track) {\n    CHECK(!socket_); // should only be called during setup\n    trackEor_ = track;\n  }\n\n  int getFlagsImpl(\n      folly::WriteFlags flags, int /*defaultFlags*/) noexcept override {\n    return oldCallback_->getFlags(flags, false /*zeroCopyEnabled*/);\n  }\n\n  void getAncillaryData(\n      folly::WriteFlags flags,\n      void* data,\n      const AsyncSocket::WriteRequestTag& writeTag,\n      const bool byteEventsEnabled) noexcept override {\n    oldCallback_->getAncillaryData(flags, data, writeTag, byteEventsEnabled);\n  }\n\n  uint32_t getAncillaryDataSize(\n      folly::WriteFlags flags,\n      const AsyncSocket::WriteRequestTag& writeTag,\n      const bool byteEventsEnabled) noexcept override {\n    return oldCallback_->getAncillaryDataSize(\n        flags, writeTag, byteEventsEnabled);\n  }\n\n  std::shared_ptr<AsyncSSLSocket> socket_;\n  bool trackEor_{false};\n  folly::AsyncSocket::SendMsgParamsCallback* oldCallback_{nullptr};\n};\n\nclass SendMsgFlagsCallback : public SendMsgParamsCallbackBase {\n public:\n  SendMsgFlagsCallback() {}\n\n  void resetFlags(int flags) { flags_ = flags; }\n\n  int getFlagsImpl(\n      folly::WriteFlags flags, int /*defaultFlags*/) noexcept override {\n    if (flags_) {\n      return flags_;\n    } else {\n      return oldCallback_->getFlags(flags, false /*zeroCopyEnabled*/);\n    }\n  }\n\n  int flags_{0};\n};\n\nclass SendMsgAncillaryDataCallback : public SendMsgParamsCallbackBase {\n public:\n  SendMsgAncillaryDataCallback() {}\n\n  /**\n   * This data will be returned on calls to getAncillaryData.\n   */\n  void resetData(std::vector<char>&& data) { ancillaryData_.swap(data); }\n\n  /**\n   * These flags were observed on the last call to getAncillaryData.\n   */\n  folly::WriteFlags getObservedWriteFlags() { return observedWriteFlags_; }\n\n  void getAncillaryData(\n      folly::WriteFlags flags,\n      void* data,\n      const AsyncSocket::WriteRequestTag& writeTag,\n      const bool byteEventsEnabled) noexcept override {\n    // getAncillaryData is called through a long chain of functions after send\n    // record the observed write flags so we can compare later\n    observedWriteFlags_ = flags;\n\n    if (ancillaryData_.size()) {\n      std::cerr << \"getAncillaryData: copying data\" << std::endl;\n      memcpy(data, ancillaryData_.data(), ancillaryData_.size());\n    } else {\n      oldCallback_->getAncillaryData(flags, data, writeTag, byteEventsEnabled);\n    }\n  }\n\n  uint32_t getAncillaryDataSize(\n      folly::WriteFlags flags,\n      const AsyncSocket::WriteRequestTag& writeTag,\n      const bool byteEventsEnabled) noexcept override {\n    if (ancillaryData_.size()) {\n      std::cerr << \"getAncillaryDataSize: returning size\" << std::endl;\n      return ancillaryData_.size();\n    } else {\n      return oldCallback_->getAncillaryDataSize(\n          flags, writeTag, byteEventsEnabled);\n    }\n  }\n\n  folly::WriteFlags observedWriteFlags_{};\n  std::vector<char> ancillaryData_;\n};\n\nclass WriteCallbackBase : public AsyncTransport::WriteCallback {\n public:\n  explicit WriteCallbackBase(SendMsgParamsCallbackBase* mcb = nullptr)\n      : state(STATE_WAITING),\n        bytesWritten(0),\n        exception(AsyncSocketException::UNKNOWN, \"none\"),\n        mcb_(mcb) {}\n\n  ~WriteCallbackBase() override { EXPECT_EQ(STATE_SUCCEEDED, state); }\n\n  SemiFuture<StateEnum> getSemiFuture() { return promise_.getSemiFuture(); }\n\n  virtual void setSocket(const std::shared_ptr<AsyncSSLSocket>& socket) {\n    socket_ = socket;\n    if (mcb_) {\n      mcb_->setSocket(socket);\n    }\n  }\n\n  void writeSuccess() noexcept override {\n    std::cerr << \"writeSuccess\" << std::endl;\n    state = STATE_SUCCEEDED;\n    if (!promise_.isFulfilled()) {\n      promise_.setValue(state);\n    }\n  }\n\n  void writeErr(\n      size_t nBytesWritten, const AsyncSocketException& ex) noexcept override {\n    std::cerr << \"writeError: bytesWritten \" << nBytesWritten << \", exception \"\n              << ex.what() << std::endl;\n\n    state = STATE_FAILED;\n    if (!promise_.isFulfilled()) {\n      promise_.setValue(state);\n    }\n    this->bytesWritten = nBytesWritten;\n    exception = ex;\n    socket_->close();\n  }\n\n  std::shared_ptr<AsyncSSLSocket> socket_;\n  StateEnum state;\n  size_t bytesWritten;\n  AsyncSocketException exception;\n  SendMsgParamsCallbackBase* mcb_;\n  Promise<StateEnum> promise_;\n};\n\nclass ExpectWriteErrorCallback : public WriteCallbackBase {\n public:\n  explicit ExpectWriteErrorCallback(SendMsgParamsCallbackBase* mcb = nullptr)\n      : WriteCallbackBase(mcb) {}\n\n  ~ExpectWriteErrorCallback() override {\n    EXPECT_EQ(STATE_FAILED, state);\n    EXPECT_EQ(\n        exception.getType(),\n        AsyncSocketException::AsyncSocketExceptionType::NETWORK_ERROR);\n    EXPECT_EQ(exception.getErrno(), 22);\n    // Suppress the assert in  ~WriteCallbackBase()\n    state = STATE_SUCCEEDED;\n  }\n};\n\nclass ExpectSSLWriteErrorCallback : public WriteCallbackBase {\n public:\n  explicit ExpectSSLWriteErrorCallback(SendMsgParamsCallbackBase* mcb = nullptr)\n      : WriteCallbackBase(mcb) {}\n\n  ~ExpectSSLWriteErrorCallback() override {\n    EXPECT_EQ(STATE_FAILED, state);\n    EXPECT_EQ(\n        exception.getType(),\n        AsyncSocketException::AsyncSocketExceptionType::SSL_ERROR);\n    // Suppress the assert in  ~WriteCallbackBase()\n    state = STATE_SUCCEEDED;\n  }\n};\n\nclass ReadCallbackBase : public AsyncTransport::ReadCallback {\n public:\n  explicit ReadCallbackBase(WriteCallbackBase* wcb)\n      : wcb_(wcb), state(STATE_WAITING) {}\n\n  ~ReadCallbackBase() override { EXPECT_EQ(STATE_SUCCEEDED, state); }\n\n  void setSocket(const std::shared_ptr<AsyncSSLSocket>& socket) {\n    socket_ = socket;\n  }\n\n  void setState(StateEnum s) {\n    state = s;\n    if (wcb_) {\n      wcb_->state = s;\n    }\n  }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    std::cerr << \"readError \" << ex.what() << std::endl;\n    state = STATE_FAILED;\n    socket_->close();\n  }\n\n  void readEOF() noexcept override {\n    std::cerr << \"readEOF\" << std::endl;\n    socket_->close();\n  }\n\n  std::shared_ptr<AsyncSSLSocket> socket_;\n  WriteCallbackBase* wcb_;\n  StateEnum state;\n};\n\n/**\n * ReadCallback reads data from the socket and then writes it back.\n *\n * It includes any folly::WriteFlags set via setWriteFlags(...) in its write\n * back operation.\n */\nclass ReadCallback : public ReadCallbackBase {\n public:\n  explicit ReadCallback(WriteCallbackBase* wcb, bool reflect = true)\n      : ReadCallbackBase(wcb),\n        buffers(),\n        writeFlags(folly::WriteFlags::NONE),\n        reflect(reflect) {}\n\n  explicit ReadCallback() : ReadCallback(nullptr, false) {}\n\n  ~ReadCallback() override {\n    for (std::vector<Buffer>::iterator it = buffers.begin();\n         it != buffers.end();\n         ++it) {\n      it->free();\n    }\n    currentBuffer.free();\n  }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    if (!currentBuffer.buffer) {\n      currentBuffer.allocate(4096);\n    }\n    *bufReturn = currentBuffer.buffer;\n    *lenReturn = currentBuffer.length;\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    std::cerr << \"readDataAvailable, len \" << len << std::endl;\n\n    currentBuffer.length = len;\n\n    if (wcb_) {\n      wcb_->setSocket(socket_);\n    }\n\n    // Write back the same data.\n    if (reflect) {\n      socket_->write(wcb_, currentBuffer.buffer, len, writeFlags);\n    }\n\n    buffers.push_back(currentBuffer);\n    currentBuffer.reset();\n    state = STATE_SUCCEEDED;\n  }\n\n  void verifyData(const char* expected, size_t expectedLen) const {\n    verifyData((const unsigned char*)expected, expectedLen);\n  }\n\n  void verifyData(const unsigned char* expected, size_t expectedLen) const {\n    size_t offset = 0;\n    for (size_t idx = 0; idx < buffers.size(); ++idx) {\n      const auto& buf = buffers[idx];\n      size_t cmpLen = std::min(buf.length, expectedLen - offset);\n      CHECK_EQ(memcmp(buf.buffer, expected + offset, cmpLen), 0);\n      CHECK_EQ(cmpLen, buf.length);\n      offset += cmpLen;\n    }\n    CHECK_EQ(offset, expectedLen);\n  }\n\n  void clearData() {\n    for (auto& buffer : buffers) {\n      buffer.free();\n    }\n    buffers.clear();\n  }\n\n  size_t dataRead() const {\n    size_t ret = 0;\n    for (const auto& buf : buffers) {\n      ret += buf.length;\n    }\n    return ret;\n  }\n\n  /**\n   * These flags will be used when writing the read data back to the socket.\n   */\n  void setWriteFlags(folly::WriteFlags flags) { writeFlags = flags; }\n\n  class Buffer {\n   public:\n    Buffer() : buffer(nullptr), length(0) {}\n    Buffer(char* buf, size_t len) : buffer(buf), length(len) {}\n\n    void reset() {\n      buffer = nullptr;\n      length = 0;\n    }\n    void allocate(size_t len) {\n      assert(buffer == nullptr);\n      this->buffer = static_cast<char*>(malloc(len));\n      this->length = len;\n    }\n    void free() {\n      ::free(buffer);\n      reset();\n    }\n\n    char* buffer;\n    size_t length;\n  };\n\n  std::vector<Buffer> buffers;\n  Buffer currentBuffer;\n  folly::WriteFlags writeFlags;\n  bool reflect; // whether read bytes will be written back to the transport\n};\n\nclass ReadErrorCallback : public ReadCallbackBase {\n public:\n  explicit ReadErrorCallback(WriteCallbackBase* wcb) : ReadCallbackBase(wcb) {}\n\n  // Return nullptr buffer to trigger readError()\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = nullptr;\n    *lenReturn = 0;\n  }\n\n  void readDataAvailable(size_t /* len */) noexcept override {\n    // This should never to called.\n    FAIL();\n  }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    ReadCallbackBase::readErr(ex);\n    std::cerr << \"ReadErrorCallback::readError\" << std::endl;\n    setState(STATE_SUCCEEDED);\n  }\n};\n\nclass ReadEOFCallback : public ReadCallbackBase {\n public:\n  explicit ReadEOFCallback(WriteCallbackBase* wcb) : ReadCallbackBase(wcb) {}\n\n  // Return nullptr buffer to trigger readError()\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = nullptr;\n    *lenReturn = 0;\n  }\n\n  void readDataAvailable(size_t /* len */) noexcept override {\n    // This should never to called.\n    FAIL();\n  }\n\n  void readEOF() noexcept override {\n    ReadCallbackBase::readEOF();\n    setState(STATE_SUCCEEDED);\n  }\n};\n\nclass WriteErrorCallback : public ReadCallback {\n public:\n  explicit WriteErrorCallback(WriteCallbackBase* wcb) : ReadCallback(wcb) {}\n\n  void readDataAvailable(size_t len) noexcept override {\n    std::cerr << \"readDataAvailable, len \" << len << std::endl;\n\n    currentBuffer.length = len;\n\n    // close the socket before writing to trigger writeError().\n    netops::close(socket_->getNetworkSocket());\n\n    wcb_->setSocket(socket_);\n\n    // Write back the same data.\n    folly::test::msvcSuppressAbortOnInvalidParams([&] {\n      socket_->write(wcb_, currentBuffer.buffer, len);\n    });\n\n    if (wcb_->state == STATE_FAILED) {\n      setState(STATE_SUCCEEDED);\n    } else {\n      state = STATE_FAILED;\n    }\n\n    buffers.push_back(currentBuffer);\n    currentBuffer.reset();\n  }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    std::cerr << \"readError \" << ex.what() << std::endl;\n    // do nothing since this is expected\n  }\n};\n\nclass EmptyReadCallback : public ReadCallback {\n public:\n  explicit EmptyReadCallback() : ReadCallback(nullptr) {}\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    std::cerr << \"readError \" << ex.what() << std::endl;\n    state = STATE_FAILED;\n    if (tcpSocket_) {\n      tcpSocket_->close();\n    }\n  }\n\n  void readEOF() noexcept override {\n    std::cerr << \"readEOF\" << std::endl;\n    if (tcpSocket_) {\n      tcpSocket_->close();\n    }\n    state = STATE_SUCCEEDED;\n  }\n\n  std::shared_ptr<AsyncSocket> tcpSocket_;\n};\n\nclass MockCertificateIdentityVerifier : public CertificateIdentityVerifier {\n public:\n  MOCK_METHOD(\n      bool,\n      verifyContext,\n      (bool, X509_STORE_CTX*),\n      (const, noexcept, override));\n  MOCK_METHOD(\n      std::unique_ptr<AsyncTransportCertificate>,\n      verifyLeaf,\n      (const AsyncTransportCertificate&),\n      (const, override));\n};\n\nclass MockHandshakeCB : public AsyncSSLSocket::HandshakeCB {\n public:\n  MOCK_METHOD(bool, handshakeVerImpl, (AsyncSSLSocket*, bool, X509_STORE_CTX*));\n  virtual bool handshakeVer(\n      AsyncSSLSocket* sock,\n      bool preverifyOk,\n      X509_STORE_CTX* ctx) noexcept override {\n    return handshakeVerImpl(sock, preverifyOk, ctx);\n  }\n\n  MOCK_METHOD(void, handshakeSucImpl, (AsyncSSLSocket*));\n  virtual void handshakeSuc(AsyncSSLSocket* sock) noexcept override {\n    handshakeSucImpl(sock);\n  }\n\n  MOCK_METHOD(\n      void, handshakeErrImpl, (AsyncSSLSocket*, const AsyncSocketException&));\n  virtual void handshakeErr(\n      AsyncSSLSocket* sock, const AsyncSocketException& ex) noexcept override {\n    handshakeErrImpl(sock, ex);\n  }\n};\n\nclass HandshakeCallback : public AsyncSSLSocket::HandshakeCB {\n public:\n  enum ExpectType { EXPECT_SUCCESS, EXPECT_ERROR };\n\n  explicit HandshakeCallback(\n      ReadCallbackBase* rcb, ExpectType expect = EXPECT_SUCCESS)\n      : state(STATE_WAITING), rcb_(rcb), expect_(expect) {}\n\n  void setSocket(const std::shared_ptr<AsyncSSLSocket>& socket) {\n    socket_ = socket;\n  }\n\n  void setState(StateEnum s) {\n    state = s;\n    rcb_->setState(s);\n  }\n\n  // Functions inherited from AsyncSSLSocketHandshakeCallback\n  void handshakeSuc(AsyncSSLSocket* sock) noexcept override {\n    isResumed_ = sock->getSSLSessionReused();\n    std::lock_guard g(mutex_);\n    cv_.notify_all();\n    EXPECT_EQ(sock, socket_.get());\n    std::cerr << \"HandshakeCallback::connectionAccepted\" << std::endl;\n    rcb_->setSocket(socket_);\n    sock->setReadCB(rcb_);\n    state = (expect_ == EXPECT_SUCCESS) ? STATE_SUCCEEDED : STATE_FAILED;\n  }\n  void handshakeErr(\n      AsyncSSLSocket* /* sock */,\n      const AsyncSocketException& ex) noexcept override {\n    isResumed_ = false;\n    std::lock_guard g(mutex_);\n    cv_.notify_all();\n    std::cerr << \"HandshakeCallback::handshakeError \" << ex.what() << std::endl;\n    state = (expect_ == EXPECT_ERROR) ? STATE_SUCCEEDED : STATE_FAILED;\n    if (expect_ == EXPECT_ERROR) {\n      // rcb will never be invoked\n      rcb_->setState(STATE_SUCCEEDED);\n    }\n    errorString_ = ex.what();\n  }\n\n  void waitForHandshake() {\n    std::unique_lock lock(mutex_);\n    cv_.wait(lock, [this] { return state != STATE_WAITING; });\n  }\n\n  ~HandshakeCallback() override { EXPECT_EQ(STATE_SUCCEEDED, state); }\n\n  void closeSocket() {\n    socket_->close();\n    state = STATE_SUCCEEDED;\n  }\n\n  std::shared_ptr<AsyncSSLSocket> getSocket() { return socket_; }\n\n  bool isResumed() const { return isResumed_; }\n\n  StateEnum state;\n  std::shared_ptr<AsyncSSLSocket> socket_;\n  ReadCallbackBase* rcb_;\n  ExpectType expect_;\n  std::mutex mutex_;\n  std::condition_variable cv_;\n  std::string errorString_;\n  bool isResumed_{false};\n};\n\nclass SSLServerAcceptCallback : public SSLServerAcceptCallbackBase {\n public:\n  uint32_t timeout_;\n\n  explicit SSLServerAcceptCallback(HandshakeCallback* hcb, uint32_t timeout = 0)\n      : SSLServerAcceptCallbackBase(hcb), timeout_(timeout) {}\n\n  ~SSLServerAcceptCallback() override {\n    if (timeout_ > 0) {\n      // if we set a timeout, we expect failure\n      EXPECT_EQ(hcb_->state, STATE_FAILED);\n      hcb_->setState(STATE_SUCCEEDED);\n    }\n  }\n\n  void connAccepted(\n      const std::shared_ptr<folly::AsyncSSLSocket>& s) noexcept override {\n    auto sock = std::static_pointer_cast<AsyncSSLSocket>(s);\n    std::cerr << \"SSLServerAcceptCallback::connAccepted\" << std::endl;\n\n    hcb_->setSocket(sock);\n    sock->sslAccept(hcb_, std::chrono::milliseconds(timeout_));\n    EXPECT_EQ(sock->getSSLState(), AsyncSSLSocket::STATE_ACCEPTING);\n\n    state = STATE_SUCCEEDED;\n  }\n};\n\nclass SSLServerAcceptCallbackDelay : public SSLServerAcceptCallback {\n public:\n  explicit SSLServerAcceptCallbackDelay(HandshakeCallback* hcb)\n      : SSLServerAcceptCallback(hcb) {}\n\n  void connAccepted(\n      const std::shared_ptr<folly::AsyncSSLSocket>& s) noexcept override {\n    auto sock = std::static_pointer_cast<AsyncSSLSocket>(s);\n\n    std::cerr << \"SSLServerAcceptCallbackDelay::connAccepted\" << std::endl;\n    auto fd = sock->getNetworkSocket();\n\n#ifndef TCP_NOPUSH\n    {\n      // The accepted connection should already have TCP_NODELAY set\n      int value;\n      socklen_t valueLength = sizeof(value);\n      int rc = netops::getsockopt(\n          fd, IPPROTO_TCP, TCP_NODELAY, &value, &valueLength);\n      EXPECT_EQ(rc, 0);\n      EXPECT_EQ(value, 1);\n    }\n#endif\n\n    // Unset the TCP_NODELAY option.\n    int value = 0;\n    socklen_t valueLength = sizeof(value);\n    int rc =\n        netops::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, valueLength);\n    EXPECT_EQ(rc, 0);\n\n    rc = netops::getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, &valueLength);\n    EXPECT_EQ(rc, 0);\n    EXPECT_EQ(value, 0);\n\n    SSLServerAcceptCallback::connAccepted(sock);\n  }\n};\n\nclass HandshakeErrorCallback : public SSLServerAcceptCallbackBase {\n public:\n  explicit HandshakeErrorCallback(HandshakeCallback* hcb)\n      : SSLServerAcceptCallbackBase(hcb) {}\n\n  void connAccepted(\n      const std::shared_ptr<folly::AsyncSSLSocket>& s) noexcept override {\n    auto sock = std::static_pointer_cast<AsyncSSLSocket>(s);\n\n    std::cerr << \"HandshakeErrorCallback::connAccepted\" << std::endl;\n\n    // The first call to sslAccept() should succeed.\n    hcb_->setSocket(sock);\n    sock->sslAccept(hcb_);\n    EXPECT_EQ(sock->getSSLState(), AsyncSSLSocket::STATE_ACCEPTING);\n\n    // The second call to sslAccept() should fail.\n    HandshakeCallback callback2(hcb_->rcb_);\n    callback2.setSocket(sock);\n    sock->sslAccept(&callback2);\n    EXPECT_EQ(sock->getSSLState(), AsyncSSLSocket::STATE_ERROR);\n\n    // Both callbacks should be in the error state.\n    EXPECT_EQ(hcb_->state, STATE_FAILED);\n    EXPECT_EQ(callback2.state, STATE_FAILED);\n\n    state = STATE_SUCCEEDED;\n    hcb_->setState(STATE_SUCCEEDED);\n    callback2.setState(STATE_SUCCEEDED);\n  }\n};\n\nclass HandshakeTimeoutCallback : public SSLServerAcceptCallbackBase {\n public:\n  explicit HandshakeTimeoutCallback(HandshakeCallback* hcb)\n      : SSLServerAcceptCallbackBase(hcb) {}\n\n  void connAccepted(\n      const std::shared_ptr<folly::AsyncSSLSocket>& s) noexcept override {\n    std::cerr << \"HandshakeErrorCallback::connAccepted\" << std::endl;\n\n    auto sock = std::static_pointer_cast<AsyncSSLSocket>(s);\n\n    hcb_->setSocket(sock);\n    sock->getEventBase()->tryRunAfterDelay(\n        [=, this] {\n          std::cerr << \"Delayed SSL accept, client will have close by now\"\n                    << std::endl;\n          // SSL accept will fail\n          EXPECT_EQ(sock->getSSLState(), AsyncSSLSocket::STATE_UNINIT);\n          hcb_->socket_->sslAccept(hcb_);\n          // This registers for an event\n          EXPECT_EQ(sock->getSSLState(), AsyncSSLSocket::STATE_ACCEPTING);\n\n          state = STATE_SUCCEEDED;\n        },\n        100);\n  }\n};\n\nclass ConnectTimeoutCallback : public SSLServerAcceptCallbackBase {\n public:\n  ConnectTimeoutCallback() : SSLServerAcceptCallbackBase(nullptr) {\n    // We don't care if we get invoked or not.\n    // The client may time out and give up before connAccepted() is even\n    // called.\n    state = STATE_SUCCEEDED;\n  }\n\n  void connAccepted(\n      const std::shared_ptr<folly::AsyncSSLSocket>& s) noexcept override {\n    std::cerr << \"ConnectTimeoutCallback::connAccepted\" << std::endl;\n\n    // Just wait a while before closing the socket, so the client\n    // will time out waiting for the handshake to complete.\n    s->getEventBase()->tryRunAfterDelay([=] { s->close(); }, 100);\n  }\n};\n\nclass BlockingWriteClient\n    : private AsyncSSLSocket::HandshakeCB,\n      private AsyncTransport::WriteCallback {\n public:\n  explicit BlockingWriteClient(AsyncSSLSocket::UniquePtr socket)\n      : socket_(std::move(socket)), bufLen_(2500), iovCount_(2000) {\n    // Fill buf_\n    buf_ = std::make_unique<uint8_t[]>(bufLen_);\n    for (uint32_t n = 0; n < sizeof(buf_); ++n) {\n      buf_[n] = n % 0xff;\n    }\n\n    // Initialize iov_\n    iov_ = std::make_unique<struct iovec[]>(iovCount_);\n    for (uint32_t n = 0; n < iovCount_; ++n) {\n      iov_[n].iov_base = buf_.get() + n;\n      if (n & 0x1) {\n        iov_[n].iov_len = n % bufLen_;\n      } else {\n        iov_[n].iov_len = bufLen_ - (n % bufLen_);\n      }\n    }\n\n    socket_->sslConn(this, std::chrono::milliseconds(100));\n  }\n\n  struct iovec* getIovec() const { return iov_.get(); }\n  uint32_t getIovecCount() const { return iovCount_; }\n\n private:\n  void handshakeSuc(AsyncSSLSocket*) noexcept override {\n    socket_->writev(this, iov_.get(), iovCount_);\n  }\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"client handshake error: \" << ex.what();\n  }\n  void writeSuccess() noexcept override { socket_->close(); }\n  void writeErr(\n      size_t bytesWritten, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"client write error after \" << bytesWritten\n                  << \" bytes: \" << ex.what();\n  }\n\n  AsyncSSLSocket::UniquePtr socket_;\n  uint32_t bufLen_;\n  uint32_t iovCount_;\n  std::unique_ptr<uint8_t[]> buf_;\n  std::unique_ptr<struct iovec[]> iov_;\n};\n\nclass BlockingWriteServer\n    : private AsyncSSLSocket::HandshakeCB,\n      private AsyncTransport::ReadCallback {\n public:\n  explicit BlockingWriteServer(AsyncSSLSocket::UniquePtr socket)\n      : socket_(std::move(socket)), bufSize_(2500 * 2000), bytesRead_(0) {\n    buf_ = std::make_unique<uint8_t[]>(bufSize_);\n    socket_->sslAccept(this, std::chrono::milliseconds(100));\n  }\n\n  void checkBuffer(struct iovec* iov, uint32_t count) const {\n    uint32_t idx = 0;\n    for (uint32_t n = 0; n < count; ++n) {\n      size_t bytesLeft = bytesRead_ - idx;\n      int rc = memcmp(\n          buf_.get() + idx,\n          iov[n].iov_base,\n          std::min(iov[n].iov_len, bytesLeft));\n      if (rc != 0) {\n        FAIL() << \"buffer mismatch at iovec \" << n << \"/\" << count\n               << \": rc=\" << rc;\n      }\n      if (iov[n].iov_len > bytesLeft) {\n        FAIL() << \"server did not read enough data: \" << \"ended at byte \"\n               << bytesLeft << \"/\" << iov[n].iov_len << \" in iovec \" << n << \"/\"\n               << count;\n      }\n\n      idx += iov[n].iov_len;\n    }\n    if (idx != bytesRead_) {\n      ADD_FAILURE() << \"server read extra data: \" << bytesRead_\n                    << \" bytes read; expected \" << idx;\n    }\n  }\n\n private:\n  void handshakeSuc(AsyncSSLSocket*) noexcept override {\n    // Wait 10ms before reading, so the client's writes will initially block.\n    socket_->getEventBase()->tryRunAfterDelay(\n        [this] { socket_->setReadCB(this); }, 10);\n  }\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"server handshake error: \" << ex.what();\n  }\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = buf_.get() + bytesRead_;\n    *lenReturn = bufSize_ - bytesRead_;\n  }\n  void readDataAvailable(size_t len) noexcept override {\n    bytesRead_ += len;\n    socket_->setReadCB(nullptr);\n    socket_->getEventBase()->tryRunAfterDelay(\n        [this] { socket_->setReadCB(this); }, 2);\n  }\n  void readEOF() noexcept override { socket_->close(); }\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"server read error: \" << ex.what();\n  }\n\n  AsyncSSLSocket::UniquePtr socket_;\n  uint32_t bufSize_;\n  uint32_t bytesRead_;\n  std::unique_ptr<uint8_t[]> buf_;\n};\n\nclass AlpnClient\n    : private AsyncSSLSocket::HandshakeCB,\n      private AsyncTransport::WriteCallback {\n public:\n  explicit AlpnClient(AsyncSSLSocket::UniquePtr socket)\n      : nextProto(nullptr), nextProtoLength(0), socket_(std::move(socket)) {\n    socket_->sslConn(this);\n  }\n\n  const unsigned char* nextProto;\n  unsigned nextProtoLength;\n  folly::Optional<AsyncSocketException> except;\n\n private:\n  void handshakeSuc(AsyncSSLSocket*) noexcept override {\n    socket_->getSelectedNextProtocol(&nextProto, &nextProtoLength);\n  }\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    except = ex;\n  }\n  void writeSuccess() noexcept override { socket_->close(); }\n  void writeErr(\n      size_t bytesWritten, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"client write error after \" << bytesWritten\n                  << \" bytes: \" << ex.what();\n  }\n\n  AsyncSSLSocket::UniquePtr socket_;\n};\n\nclass AlpnServer\n    : private AsyncSSLSocket::HandshakeCB,\n      private AsyncTransport::ReadCallback {\n public:\n  explicit AlpnServer(AsyncSSLSocket::UniquePtr socket)\n      : nextProto(nullptr), nextProtoLength(0), socket_(std::move(socket)) {\n    socket_->sslAccept(this);\n    socket_->enableClientHelloParsing();\n  }\n\n  const unsigned char* nextProto;\n  unsigned nextProtoLength;\n  folly::Optional<AsyncSocketException> except;\n  const std::vector<std::string>& getClientAlpns() const {\n    return socket_->getClientAlpns();\n  }\n\n private:\n  void handshakeSuc(AsyncSSLSocket*) noexcept override {\n    socket_->getSelectedNextProtocol(&nextProto, &nextProtoLength);\n  }\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    except = ex;\n  }\n  void getReadBuffer(void** /* bufReturn */, size_t* lenReturn) override {\n    *lenReturn = 0;\n  }\n  void readDataAvailable(size_t /* len */) noexcept override {}\n  void readEOF() noexcept override { socket_->close(); }\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"server read error: \" << ex.what();\n  }\n\n  AsyncSSLSocket::UniquePtr socket_;\n};\n\nclass RenegotiatingServer\n    : public AsyncSSLSocket::HandshakeCB,\n      public AsyncTransport::ReadCallback {\n public:\n  explicit RenegotiatingServer(AsyncSSLSocket::UniquePtr socket)\n      : socket_(std::move(socket)) {\n    socket_->sslAccept(this);\n  }\n\n  ~RenegotiatingServer() override { socket_->setReadCB(nullptr); }\n\n  void handshakeSuc(AsyncSSLSocket* /* socket */) noexcept override {\n    LOG(INFO) << \"Renegotiating server handshake success\";\n    socket_->setReadCB(this);\n  }\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"Renegotiating server handshake error: \" << ex.what();\n  }\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *lenReturn = sizeof(buf);\n    *bufReturn = buf;\n  }\n  void readDataAvailable(size_t /* len */) noexcept override {}\n  void readEOF() noexcept override {}\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    LOG(INFO) << \"server got read error \" << ex.what();\n    auto exPtr = dynamic_cast<const SSLException*>(&ex);\n    ASSERT_NE(nullptr, exPtr);\n    std::string exStr(ex.what());\n    SSLException sslEx(SSLError::CLIENT_RENEGOTIATION);\n    ASSERT_NE(std::string::npos, exStr.find(sslEx.what()));\n    renegotiationError_ = true;\n  }\n\n  AsyncSSLSocket::UniquePtr socket_;\n  unsigned char buf[128];\n  bool renegotiationError_{false};\n};\n\nclass SNIClient\n    : private AsyncSSLSocket::HandshakeCB,\n      private AsyncTransport::WriteCallback {\n public:\n  explicit SNIClient(AsyncSSLSocket::UniquePtr socket)\n      : serverNameMatch(false), socket_(std::move(socket)) {\n    socket_->sslConn(this);\n  }\n\n  std::string getApplicationProtocol() {\n    return socket_->getApplicationProtocol();\n  }\n\n  bool serverNameMatch;\n\n private:\n  void handshakeSuc(AsyncSSLSocket*) noexcept override {\n    serverNameMatch = socket_->isServerNameMatch();\n  }\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"client handshake error: \" << ex.what();\n  }\n  void writeSuccess() noexcept override { socket_->close(); }\n  void writeErr(\n      size_t bytesWritten, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"client write error after \" << bytesWritten\n                  << \" bytes: \" << ex.what();\n  }\n\n  AsyncSSLSocket::UniquePtr socket_;\n};\n\nclass SNIServer\n    : private AsyncSSLSocket::HandshakeCB,\n      private AsyncTransport::ReadCallback {\n public:\n  explicit SNIServer(\n      AsyncSSLSocket::UniquePtr socket,\n      const std::shared_ptr<folly::SSLContext>& ctx,\n      const std::shared_ptr<folly::SSLContext>& sniCtx,\n      const std::string& expectedServerName)\n      : serverNameMatch(false),\n        socket_(std::move(socket)),\n        sniCtx_(sniCtx),\n        expectedServerName_(expectedServerName) {\n    ctx->setServerNameCallback(\n        std::bind(&SNIServer::serverNameCallback, this, std::placeholders::_1));\n    socket_->sslAccept(this);\n  }\n\n  std::string getApplicationProtocol() {\n    return socket_->getApplicationProtocol();\n  }\n\n  bool serverNameMatch;\n\n private:\n  void handshakeSuc(AsyncSSLSocket* /* ssl */) noexcept override {}\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"server handshake error: \" << ex.what();\n  }\n  void getReadBuffer(void** /* bufReturn */, size_t* lenReturn) override {\n    *lenReturn = 0;\n  }\n  void readDataAvailable(size_t /* len */) noexcept override {}\n  void readEOF() noexcept override { socket_->close(); }\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"server read error: \" << ex.what();\n  }\n\n  folly::SSLContext::ServerNameCallbackResult serverNameCallback(SSL* ssl) {\n    const char* sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);\n    if (sniCtx_ && sn && !strcasecmp(expectedServerName_.c_str(), sn)) {\n      AsyncSSLSocket* sslSocket = AsyncSSLSocket::getFromSSL(ssl);\n      sslSocket->switchServerSSLContext(sniCtx_);\n      serverNameMatch = true;\n      return folly::SSLContext::SERVER_NAME_FOUND;\n    } else {\n      serverNameMatch = false;\n      return folly::SSLContext::SERVER_NAME_NOT_FOUND;\n    }\n  }\n\n  AsyncSSLSocket::UniquePtr socket_;\n  std::shared_ptr<folly::SSLContext> sniCtx_;\n  std::string expectedServerName_;\n};\n\nclass SSLClient\n    : public AsyncSocket::ConnectCallback,\n      public AsyncTransport::WriteCallback,\n      public AsyncTransport::ReadCallback {\n private:\n  EventBase* eventBase_;\n  std::shared_ptr<AsyncSSLSocket> sslSocket_;\n  std::shared_ptr<folly::ssl::SSLSession> session_;\n  std::shared_ptr<folly::SSLContext> ctx_;\n  uint32_t requests_;\n  folly::SocketAddress address_;\n  uint32_t timeout_;\n  char buf_[128];\n  char readbuf_[128];\n  uint32_t bytesRead_;\n  uint32_t hit_;\n  uint32_t miss_;\n  uint32_t errors_;\n  uint32_t writeAfterConnectErrors_;\n\n  // These settings test that we eventually drain the\n  // socket, even if the maxReadsPerEvent_ is hit during\n  // a event loop iteration.\n  static constexpr size_t kMaxReadsPerEvent = 2;\n  // 2 event loop iterations\n  static constexpr size_t kMaxReadBufferSz =\n      sizeof(decltype(readbuf_)) / kMaxReadsPerEvent / 2;\n\n public:\n  SSLClient(\n      EventBase* eventBase,\n      const folly::SocketAddress& address,\n      uint32_t requests,\n      uint32_t timeout = 0)\n      : eventBase_(eventBase),\n        session_(nullptr),\n        requests_(requests),\n        address_(address),\n        timeout_(timeout),\n        bytesRead_(0),\n        hit_(0),\n        miss_(0),\n        errors_(0),\n        writeAfterConnectErrors_(0) {\n    ctx_.reset(new folly::SSLContext());\n    ctx_->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n    memset(buf_, 'a', sizeof(buf_));\n  }\n\n  ~SSLClient() override {\n    if (errors_ == 0) {\n      EXPECT_EQ(bytesRead_, sizeof(buf_));\n    }\n  }\n\n  uint32_t getHit() const { return hit_; }\n\n  uint32_t getMiss() const { return miss_; }\n\n  uint32_t getErrors() const { return errors_; }\n\n  uint32_t getWriteAfterConnectErrors() const {\n    return writeAfterConnectErrors_;\n  }\n\n  void setSSLOptions(long options) { ctx_->setOptions(options); }\n\n  void connect(bool writeNow = false) {\n    sslSocket_ = AsyncSSLSocket::newSocket(ctx_, eventBase_);\n    if (session_ != nullptr) {\n      sslSocket_->setSSLSession(session_);\n    }\n    requests_--;\n    sslSocket_->connect(this, address_, timeout_);\n    if (sslSocket_ && writeNow) {\n      // write some junk, used in an error test\n      sslSocket_->write(this, buf_, sizeof(buf_));\n    }\n  }\n\n  void connectSuccess() noexcept override {\n    std::cerr << \"client SSL socket connected\" << std::endl;\n    if (sslSocket_->getSSLSessionReused()) {\n      hit_++;\n    } else {\n      miss_++;\n      session_ = sslSocket_->getSSLSession();\n    }\n\n    // write()\n    sslSocket_->setMaxReadsPerEvent(kMaxReadsPerEvent);\n    sslSocket_->write(this, buf_, sizeof(buf_));\n    sslSocket_->setReadCB(this);\n    memset(readbuf_, 'b', sizeof(readbuf_));\n    bytesRead_ = 0;\n  }\n\n  void connectErr(const AsyncSocketException& ex) noexcept override {\n    std::cerr << \"SSLClient::connectError: \" << ex.what() << std::endl;\n    errors_++;\n    sslSocket_.reset();\n  }\n\n  void writeSuccess() noexcept override {\n    std::cerr << \"client write success\" << std::endl;\n  }\n\n  void writeErr(\n      size_t /* bytesWritten */,\n      const AsyncSocketException& ex) noexcept override {\n    std::cerr << \"client writeError: \" << ex.what() << std::endl;\n    if (!sslSocket_) {\n      writeAfterConnectErrors_++;\n    }\n  }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = readbuf_ + bytesRead_;\n    *lenReturn = std::min(kMaxReadBufferSz, sizeof(readbuf_) - bytesRead_);\n  }\n\n  void readEOF() noexcept override {\n    std::cerr << \"client readEOF\" << std::endl;\n  }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    std::cerr << \"client readError: \" << ex.what() << std::endl;\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    std::cerr << \"client read data: \" << len << std::endl;\n    bytesRead_ += len;\n    if (bytesRead_ == sizeof(buf_)) {\n      EXPECT_EQ(memcmp(buf_, readbuf_, bytesRead_), 0);\n      sslSocket_->closeNow();\n      sslSocket_.reset();\n      if (requests_ != 0) {\n        connect();\n      }\n    }\n  }\n};\n\nclass SSLHandshakeBase\n    : public AsyncSSLSocket::HandshakeCB,\n      private AsyncTransport::WriteCallback {\n public:\n  explicit SSLHandshakeBase(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : handshakeVerify_(false),\n        handshakeSuccess_(false),\n        handshakeError_(false),\n        socket_(std::move(socket)),\n        preverifyResult_(preverifyResult),\n        verifyResult_(verifyResult) {}\n\n  AsyncSSLSocket::UniquePtr moveSocket() && { return std::move(socket_); }\n\n  bool handshakeVerify_;\n  bool handshakeSuccess_;\n  bool handshakeError_;\n  int handshakeVerifyInvocations_{};\n  std::chrono::nanoseconds handshakeTime;\n\n protected:\n  AsyncSSLSocket::UniquePtr socket_;\n  bool preverifyResult_;\n  bool verifyResult_;\n\n  // HandshakeCallback\n  bool handshakeVer(\n      AsyncSSLSocket* /* sock */,\n      bool preverifyOk,\n      X509_STORE_CTX* /* ctx */) noexcept override {\n    auto invocation = handshakeVerifyInvocations_++;\n\n    if (invocation == 0) {\n      handshakeVerify_ = true;\n      EXPECT_EQ(preverifyResult_, preverifyOk);\n    }\n    return verifyResult_;\n  }\n\n  void handshakeSuc(AsyncSSLSocket*) noexcept override {\n    LOG(INFO) << \"Handshake success\";\n    handshakeSuccess_ = true;\n    if (socket_) {\n      handshakeTime = socket_->getHandshakeTime();\n    }\n  }\n\n  void handshakeErr(\n      AsyncSSLSocket*, const AsyncSocketException& ex) noexcept override {\n    LOG(INFO) << \"Handshake error \" << ex.what();\n    handshakeError_ = true;\n    if (socket_) {\n      handshakeTime = socket_->getHandshakeTime();\n    }\n  }\n\n  // WriteCallback\n  void writeSuccess() noexcept override {\n    if (socket_) {\n      socket_->close();\n    }\n  }\n\n  void writeErr(\n      size_t bytesWritten, const AsyncSocketException& ex) noexcept override {\n    ADD_FAILURE() << \"client write error after \" << bytesWritten\n                  << \" bytes: \" << ex.what();\n  }\n};\n\nclass SSLHandshakeClient : public SSLHandshakeBase {\n public:\n  SSLHandshakeClient(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : SSLHandshakeBase(std::move(socket), preverifyResult, verifyResult) {\n    socket_->sslConn(this, std::chrono::milliseconds::zero());\n  }\n};\n\nclass SSLHandshakeClientNoVerify : public SSLHandshakeBase {\n public:\n  SSLHandshakeClientNoVerify(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : SSLHandshakeBase(std::move(socket), preverifyResult, verifyResult) {\n    socket_->sslConn(\n        this,\n        std::chrono::milliseconds::zero(),\n        folly::SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  }\n};\n\nclass SSLHandshakeClientDoVerify : public SSLHandshakeBase {\n public:\n  SSLHandshakeClientDoVerify(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : SSLHandshakeBase(std::move(socket), preverifyResult, verifyResult) {\n    socket_->sslConn(\n        this,\n        std::chrono::milliseconds::zero(),\n        folly::SSLContext::SSLVerifyPeerEnum::VERIFY);\n  }\n};\n\nclass SSLHandshakeServer : public SSLHandshakeBase {\n public:\n  SSLHandshakeServer(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : SSLHandshakeBase(std::move(socket), preverifyResult, verifyResult) {\n    socket_->sslAccept(this, std::chrono::milliseconds::zero());\n  }\n};\n\nclass SSLHandshakeServerParseClientHello : public SSLHandshakeBase {\n public:\n  SSLHandshakeServerParseClientHello(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : SSLHandshakeBase(std::move(socket), preverifyResult, verifyResult) {\n    socket_->enableClientHelloParsing();\n    socket_->sslAccept(this, std::chrono::milliseconds::zero());\n  }\n\n  std::string clientCiphers_, sharedCiphers_, serverCiphers_, chosenCipher_;\n\n protected:\n  void handshakeSuc(AsyncSSLSocket* sock) noexcept override {\n    handshakeSuccess_ = true;\n    sock->getSSLSharedCiphers(sharedCiphers_);\n    sock->getSSLServerCiphers(serverCiphers_);\n    sock->getSSLClientCiphers(clientCiphers_);\n    chosenCipher_ = sock->getNegotiatedCipherName();\n  }\n};\n\nclass SSLHandshakeServerNoVerify : public SSLHandshakeBase {\n public:\n  SSLHandshakeServerNoVerify(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : SSLHandshakeBase(std::move(socket), preverifyResult, verifyResult) {\n    socket_->sslAccept(\n        this,\n        std::chrono::milliseconds::zero(),\n        folly::SSLContext::SSLVerifyPeerEnum::NO_VERIFY);\n  }\n};\n\nclass SSLHandshakeServerDoVerify : public SSLHandshakeBase {\n public:\n  SSLHandshakeServerDoVerify(\n      AsyncSSLSocket::UniquePtr socket, bool preverifyResult, bool verifyResult)\n      : SSLHandshakeBase(std::move(socket), preverifyResult, verifyResult) {\n    socket_->sslAccept(\n        this,\n        std::chrono::milliseconds::zero(),\n        folly::SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT);\n  }\n};\n\nclass EventBaseAborter : public AsyncTimeout {\n public:\n  EventBaseAborter(EventBase* eventBase, uint32_t timeoutMS)\n      : AsyncTimeout(eventBase, AsyncTimeout::InternalEnum::INTERNAL),\n        eventBase_(eventBase) {\n    scheduleTimeout(timeoutMS);\n  }\n\n  void timeoutExpired() noexcept override {\n    FAIL() << \"test timed out\";\n    eventBase_->terminateLoopSoon();\n  }\n\n private:\n  EventBase* eventBase_;\n};\n\nclass SSLAcceptEvbRunner : public SSLAcceptRunner {\n public:\n  explicit SSLAcceptEvbRunner(EventBase* evb) : evb_(evb) {}\n  ~SSLAcceptEvbRunner() override = default;\n\n  void run(Function<int()> acceptFunc, Function<void(int)> finallyFunc)\n      const override {\n    evb_->runInLoop([acceptFunc = std::move(acceptFunc),\n                     finallyFunc = std::move(finallyFunc)]() mutable {\n      finallyFunc(acceptFunc());\n    });\n  }\n\n protected:\n  EventBase* evb_;\n};\n\nclass SSLAcceptErrorRunner : public SSLAcceptEvbRunner {\n public:\n  explicit SSLAcceptErrorRunner(EventBase* evb) : SSLAcceptEvbRunner(evb) {}\n  ~SSLAcceptErrorRunner() override = default;\n\n  void run(Function<int()> /*acceptFunc*/, Function<void(int)> finallyFunc)\n      const override {\n    evb_->runInLoop([finallyFunc = std::move(finallyFunc)]() mutable {\n      finallyFunc(-1);\n    });\n  }\n};\n\nclass SSLAcceptCloseRunner : public SSLAcceptEvbRunner {\n public:\n  explicit SSLAcceptCloseRunner(EventBase* evb, folly::AsyncSSLSocket* sock)\n      : SSLAcceptEvbRunner(evb), socket_(sock) {}\n  ~SSLAcceptCloseRunner() override = default;\n\n  void run(Function<int()> acceptFunc, Function<void(int)> finallyFunc)\n      const override {\n    evb_->runInLoop(\n        [acceptFunc = std::move(acceptFunc),\n         finallyFunc = std::move(finallyFunc),\n         sock = socket_]() mutable {\n          auto ret = acceptFunc();\n          sock->closeNow();\n          finallyFunc(ret);\n        });\n  }\n\n private:\n  folly::AsyncSSLSocket* socket_;\n};\n\nclass SSLAcceptDestroyRunner : public SSLAcceptEvbRunner {\n public:\n  explicit SSLAcceptDestroyRunner(EventBase* evb, SSLHandshakeBase* base)\n      : SSLAcceptEvbRunner(evb), sslBase_(base) {}\n  ~SSLAcceptDestroyRunner() override = default;\n\n  void run(Function<int()> acceptFunc, Function<void(int)> finallyFunc)\n      const override {\n    evb_->runInLoop(\n        [acceptFunc = std::move(acceptFunc),\n         finallyFunc = std::move(finallyFunc),\n         sslBase = sslBase_]() mutable {\n          auto ret = acceptFunc();\n          std::move(*sslBase).moveSocket();\n          finallyFunc(ret);\n        });\n  }\n\n private:\n  SSLHandshakeBase* sslBase_;\n};\n\nclass SSLAcceptFiberRunner : public SSLAcceptEvbRunner {\n public:\n  explicit SSLAcceptFiberRunner(EventBase* evb) : SSLAcceptEvbRunner(evb) {}\n  ~SSLAcceptFiberRunner() override = default;\n\n  void run(Function<int()> acceptFunc, Function<void(int)> finallyFunc)\n      const override {\n    auto& fiberManager = folly::fibers::getFiberManager(*evb_);\n    fiberManager.addTaskFinally(\n        std::move(acceptFunc),\n        [finally = std::move(finallyFunc)](folly::Try<int>&& res) mutable {\n          finally(res.value());\n        });\n  }\n};\n\n} // namespace folly::test\n"
  },
  {
    "path": "folly/io/async/test/AsyncSSLSocketTest2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSSLSocket.h>\n\n#include <folly/futures/Promise.h>\n#include <folly/init/Init.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/SSLContext.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/io/async/test/AsyncSSLSocketTest.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/PThread.h>\n\nusing std::cerr;\nusing std::endl;\n\nusing namespace folly;\nusing namespace folly::test;\n\nstruct EvbAndContext {\n  EvbAndContext() {\n    ctx_.reset(new SSLContext());\n    ctx_->setOptions(SSL_OP_NO_TICKET);\n    ctx_->ciphers(\"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\");\n  }\n\n  std::shared_ptr<AsyncSSLSocket> createSocket() {\n    return AsyncSSLSocket::newSocket(ctx_, getEventBase());\n  }\n\n  EventBase* getEventBase() { return evb_.getEventBase(); }\n\n  void attach(AsyncSSLSocket& socket) {\n    socket.attachEventBase(getEventBase());\n    socket.attachSSLContext(ctx_);\n  }\n\n  folly::ScopedEventBaseThread evb_;\n  std::shared_ptr<SSLContext> ctx_;\n};\n\nclass AttachDetachClient\n    : public AsyncSocket::ConnectCallback,\n      public AsyncTransport::WriteCallback,\n      public AsyncTransport::ReadCallback {\n private:\n  // two threads here - we'll create the socket in one, connect\n  // in the other, and then read/write in the initial one\n  EvbAndContext t1_;\n  EvbAndContext t2_;\n  std::shared_ptr<AsyncSSLSocket> sslSocket_;\n  folly::SocketAddress address_;\n  char buf_[128];\n  char readbuf_[128];\n  uint32_t bytesRead_;\n  // promise to fulfill when done\n  folly::Promise<bool> promise_;\n\n  void detach() {\n    sslSocket_->detachEventBase();\n    sslSocket_->detachSSLContext();\n  }\n\n public:\n  explicit AttachDetachClient(const folly::SocketAddress& address)\n      : address_(address), bytesRead_(0) {}\n\n  Future<bool> getFuture() { return promise_.getFuture(); }\n\n  void connect() {\n    // create in one and then move to another\n    auto t1Evb = t1_.getEventBase();\n    t1Evb->runInEventBaseThread([this] {\n      sslSocket_ = t1_.createSocket();\n      // ensure we can detach and reattach the context before connecting\n      for (int i = 0; i < 1000; ++i) {\n        sslSocket_->detachSSLContext();\n        sslSocket_->attachSSLContext(t1_.ctx_);\n      }\n      // detach from t1 and connect in t2\n      detach();\n      auto t2Evb = t2_.getEventBase();\n      t2Evb->runInEventBaseThread([this] {\n        t2_.attach(*sslSocket_);\n        sslSocket_->connect(this, address_);\n      });\n    });\n  }\n\n  void connectSuccess() noexcept override {\n    auto t2Evb = t2_.getEventBase();\n    EXPECT_TRUE(t2Evb->isInEventBaseThread());\n    cerr << \"client SSL socket connected\" << endl;\n    for (int i = 0; i < 1000; ++i) {\n      sslSocket_->detachSSLContext();\n      sslSocket_->attachSSLContext(t2_.ctx_);\n    }\n\n    // detach from t2 and then read/write in t1\n    t2Evb->runInEventBaseThread([this] {\n      detach();\n      auto t1Evb = t1_.getEventBase();\n      t1Evb->runInEventBaseThread([this] {\n        t1_.attach(*sslSocket_);\n        sslSocket_->write(this, buf_, sizeof(buf_));\n        sslSocket_->setReadCB(this);\n        memset(readbuf_, 'b', sizeof(readbuf_));\n        bytesRead_ = 0;\n      });\n    });\n  }\n\n  void connectErr(const AsyncSocketException& ex) noexcept override {\n    cerr << \"AttachDetachClient::connectError: \" << ex.what() << endl;\n    sslSocket_.reset();\n  }\n\n  void writeSuccess() noexcept override {\n    cerr << \"client write success\" << endl;\n  }\n\n  void writeErr(\n      size_t /* bytesWritten */,\n      const AsyncSocketException& ex) noexcept override {\n    cerr << \"client writeError: \" << ex.what() << endl;\n  }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = readbuf_ + bytesRead_;\n    *lenReturn = sizeof(readbuf_) - bytesRead_;\n  }\n  void readEOF() noexcept override { cerr << \"client readEOF\" << endl; }\n\n  void readErr(const AsyncSocketException& ex) noexcept override {\n    cerr << \"client readError: \" << ex.what() << endl;\n    promise_.setException(ex);\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    EXPECT_TRUE(t1_.getEventBase()->isInEventBaseThread());\n    EXPECT_EQ(sslSocket_->getEventBase(), t1_.getEventBase());\n    cerr << \"client read data: \" << len << endl;\n    bytesRead_ += len;\n    if (len == sizeof(buf_)) {\n      EXPECT_EQ(memcmp(buf_, readbuf_, bytesRead_), 0);\n      sslSocket_->closeNow();\n      sslSocket_.reset();\n      promise_.setValue(true);\n    }\n  }\n};\n\n/**\n * Test passing contexts between threads\n */\nTEST(AsyncSSLSocketTest2, AttachDetachSSLContext) {\n  // Start listening on a local port\n  WriteCallbackBase writeCallback;\n  ReadCallback readCallback(&writeCallback);\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);\n  TestSSLServer server(&acceptCallback);\n\n  std::shared_ptr<AttachDetachClient> client(\n      new AttachDetachClient(server.getAddress()));\n\n  auto f = client->getFuture();\n  client->connect();\n  EXPECT_TRUE(std::move(f).within(std::chrono::seconds(3)).get());\n}\n\nclass ConnectClient : public AsyncSocket::ConnectCallback {\n public:\n  ConnectClient() = default;\n\n  Future<bool> getFuture() { return promise_.getFuture(); }\n\n  void connect(const folly::SocketAddress& addr) {\n    t1_.getEventBase()->runInEventBaseThread([&] {\n      socket_ = t1_.createSocket();\n      socket_->connect(this, addr);\n    });\n  }\n\n  void connectSuccess() noexcept override {\n    socket_.reset();\n    promise_.setValue(true);\n  }\n\n  void connectErr(const AsyncSocketException& /* ex */) noexcept override {\n    socket_.reset();\n    promise_.setValue(false);\n  }\n\n  void setCtx(std::shared_ptr<SSLContext> ctx) { t1_.ctx_ = ctx; }\n\n private:\n  EvbAndContext t1_;\n  // promise to fulfill when done with a value of true if connect succeeded\n  folly::Promise<bool> promise_;\n  std::shared_ptr<AsyncSSLSocket> socket_;\n};\n\nclass NoopReadCallback : public ReadCallbackBase {\n public:\n  NoopReadCallback() : ReadCallbackBase(nullptr) { state = STATE_SUCCEEDED; }\n\n  void getReadBuffer(void** buf, size_t* lenReturn) override {\n    *buf = &buffer_;\n    *lenReturn = 1;\n  }\n  void readDataAvailable(size_t) noexcept override {}\n\n  uint8_t buffer_{0};\n};\n\nTEST(AsyncSSLSocketTest2, TestTLS12DefaultClient) {\n  // Start listening on a local port\n  NoopReadCallback readCallback;\n  HandshakeCallback handshakeCallback(&readCallback);\n  SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);\n  auto ctx = std::make_shared<SSLContext>(SSLContext::TLSv1_2);\n  TestSSLServer server(&acceptCallback, ctx);\n  server.loadTestCerts();\n\n  // create a default client\n  auto c1 = std::make_unique<ConnectClient>();\n  auto f1 = c1->getFuture();\n  c1->connect(server.getAddress());\n  EXPECT_TRUE(std::move(f1).within(std::chrono::seconds(3)).get());\n}\n\n// Pre-TLS 1.2 client attempting to connect to a TLS 1.2+ server, should not be\n// able to connect.\nTEST(AsyncSSLSocketTest2, TestLegacyClientCannotConnectToTLS12Server) {\n  // Start listening on a local port\n  NoopReadCallback readCallback;\n  HandshakeCallback handshakeCallback(\n      &readCallback, HandshakeCallback::EXPECT_ERROR);\n  SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);\n  auto ctx = std::make_shared<SSLContext>(SSLContext::TLSv1_2);\n  TestSSLServer server(&acceptCallback, ctx);\n  server.loadTestCerts();\n\n  // create a client that doesn't speak TLS 1.2+\n  auto c2 = std::make_unique<ConnectClient>();\n  auto clientCtx = std::make_shared<SSLContext>(SSLContext::TLSv1);\n  clientCtx->setOptions(SSL_OP_NO_TLSv1_2);\n  clientCtx->disableTLS13();\n  c2->setCtx(clientCtx);\n  auto f2 = c2->getFuture();\n  c2->connect(server.getAddress());\n  EXPECT_FALSE(std::move(f2).within(std::chrono::seconds(3)).get());\n}\n\nint main(int argc, char* argv[]) {\n#ifdef SIGPIPE\n  signal(SIGPIPE, SIG_IGN);\n#endif\n  testing::InitGoogleTest(&argc, argv);\n  folly::Init init(&argc, &argv);\n  return RUN_ALL_TESTS();\n  OPENSSL_cleanup();\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncSSLSocketWriteTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string>\n#include <vector>\n\n#include <folly/io/Cursor.h>\n#include <folly/io/async/AsyncSSLSocket.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nusing namespace testing;\n\nnamespace folly {\n\nclass MockAsyncSSLSocket : public AsyncSSLSocket {\n public:\n  static std::shared_ptr<MockAsyncSSLSocket> newSocket(\n      const std::shared_ptr<SSLContext>& ctx, EventBase* evb) {\n    auto sock = std::shared_ptr<MockAsyncSSLSocket>(\n        new MockAsyncSSLSocket(ctx, evb), Destructor());\n    sock->setSendMsgParamCB(&sock->sendMsgParamCob_);\n    sock->ssl_.reset(SSL_new(ctx->getSSLCtx()));\n    SSL_set_fd(sock->ssl_.get(), -1);\n    sock->setupSSLBio();\n    return sock;\n  }\n\n  // Fake constructor sets the state to established without call to connect\n  // or accept\n  MockAsyncSSLSocket(const std::shared_ptr<SSLContext>& ctx, EventBase* evb)\n      : AsyncSSLSocket(ctx, evb) {\n    state_ = AsyncSocket::StateEnum::ESTABLISHED;\n    sslState_ = AsyncSSLSocket::SSLStateEnum::STATE_ESTABLISHED;\n  }\n\n  // mock the calls to SSL_write to see the buffer length and contents\n  MOCK_METHOD(int, sslWriteImpl, (SSL * ssl, const void* buf, int n));\n\n  // mock the calls to SSL_get_error to insert errors\n  MOCK_METHOD(int, sslGetErrorImpl, (const SSL* s, int ret_code));\n\n  // mock the calls to sendSocketMessage to see the msg_flags\n  MOCK_METHOD(\n      AsyncSocket::WriteResult,\n      sendSocketMessage,\n      (NetworkSocket fd, struct msghdr* msg, int msg_flags));\n\n  // mock the calls to getRawBytesWritten()\n  MOCK_METHOD(size_t, getRawBytesWritten, (), (const));\n\n  // public wrapper for protected interface\n  WriteResult testPerformWrite(\n      const iovec* vec,\n      uint32_t count,\n      WriteFlags flags,\n      uint32_t* countWritten,\n      uint32_t* partialWritten) {\n    const size_t prevNumCalls = sendMsgParamCob_.numCalls_;\n    IOBuf tagBuf;\n    return performWrite(\n        vec,\n        count,\n        flags,\n        countWritten,\n        partialWritten,\n        WriteRequestTag{&tagBuf});\n    CHECK_EQ(sendMsgParamCob_.numCalls_, prevNumCalls + 1);\n  }\n\n  // public wrapper for protected member\n  folly::Optional<size_t> getCurrBytesToFinalByte() const {\n    return currBytesToFinalByte_;\n  }\n\n  struct MySendMsgParamsCallback : public SendMsgParamsCallback {\n    uint32_t getAncillaryDataSize(\n        folly::WriteFlags flags,\n        const WriteRequestTag& writeTag,\n        const bool byteEventsEnabled) noexcept override {\n      ++numCalls_;\n      // At present, write tags are NOT propagated to the\n      // `SendMsgParamsCallback` from `AsyncSSLSocket` via `bioWrite`.\n      CHECK_EQ(WriteRequestTag{WriteRequestTag::EmptyDummy()}, writeTag);\n      return SendMsgParamsCallback::getAncillaryDataSize(\n          flags, writeTag, byteEventsEnabled);\n    }\n\n    size_t numCalls_{0};\n  };\n\n  MySendMsgParamsCallback sendMsgParamCob_;\n};\n\nclass AsyncSSLSocketWriteTest : public testing::Test {\n public:\n  AsyncSSLSocketWriteTest()\n      : sslContext_(new SSLContext()),\n        sock_(MockAsyncSSLSocket::newSocket(sslContext_, &eventBase_)) {\n    for (int i = 0; i < 500; i++) {\n      memcpy(source_ + i * 26, \"abcdefghijklmnopqrstuvwxyz\", 26);\n    }\n  }\n\n  // Make an iovec containing chunks of the reference text with requested sizes\n  // for each chunk\n  std::unique_ptr<iovec[]> makeVec(std::vector<uint32_t> sizes) {\n    std::unique_ptr<iovec[]> vec(new iovec[sizes.size()]);\n    int i = 0;\n    int pos = 0;\n    for (auto size : sizes) {\n      vec[i].iov_base = (void*)(source_ + pos);\n      vec[i++].iov_len = size;\n      pos += size;\n    }\n    return vec;\n  }\n\n  // Verify that the given buf/pos matches the reference text\n  void verifyVec(const void* buf, int n, int pos) {\n    ASSERT_EQ(memcmp(source_ + pos, buf, n), 0);\n  }\n\n  // Update a vec on partial write\n  void consumeVec(iovec* vec, uint32_t countWritten, uint32_t partialWritten) {\n    vec[countWritten].iov_base =\n        ((char*)vec[countWritten].iov_base) + partialWritten;\n    vec[countWritten].iov_len -= partialWritten;\n  }\n\n  EventBase eventBase_;\n  std::shared_ptr<SSLContext> sslContext_;\n  std::shared_ptr<MockAsyncSSLSocket> sock_;\n  char source_[26 * 500];\n};\n\nTEST_F(AsyncSSLSocketWriteTest, CompleteSSLWriteUpdatesAppBytesWritten) {\n  int n = 1;\n  auto vec = makeVec({1500});\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  // full write\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=](SSL* ssl, const void* buf, int m) {\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1500))));\n\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1500);\n}\n\nTEST_F(AsyncSSLSocketWriteTest, NoSSLWriteUpdatesAppBytesWritten) {\n  int n = 1;\n  auto vec = makeVec({1500});\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  // want write\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=](SSL* ssl, const void* buf, int m) {\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(0))));\n  EXPECT_CALL(*(sock_.get()), sslGetErrorImpl(_, _))\n      .WillOnce(Return(SSL_ERROR_WANT_WRITE));\n\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  // We got SSL_WANT_WRITE so should be 0\n  EXPECT_EQ(sock_->getAppBytesWritten(), 0);\n}\n\nTEST_F(AsyncSSLSocketWriteTest, PartialSSLWriteUpdatesAppBytesWritten) {\n  int n = 1;\n  auto vec = makeVec({1500});\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  // partial write\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=](SSL* ssl, const void* buf, int m) {\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(500))));\n\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(sock_->getAppBytesWritten(), 500);\n}\n\n// SSL_ERROR_WANT_WRITE occurs on first write\nTEST_F(AsyncSSLSocketWriteTest, SslErrorWantWrite) {\n  int n = 1;\n  auto vec = makeVec({1500});\n  int pos = 0;\n\n  // first time we try to write, SSL_ERROR_WANT_WRITE will be returned\n  //\n  // this means no bytes were actually written to the socket,\n  // but getRawBytesWritten will still  be incremented by the write size as\n  // the bytes were appended to the BIO\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        EXPECT_EQ(m, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(0))));\n  EXPECT_CALL(*(sock_.get()), sslGetErrorImpl(_, _))\n      .WillOnce(Return(SSL_ERROR_WANT_WRITE));\n  ON_CALL( // should not be called, unless implementation changes to use it\n      *(sock_.get()),\n      getRawBytesWritten())\n      .WillByDefault(Return(1500));\n\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 0);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 0);\n\n  // second time we try to write, same buffer should be passed in\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        EXPECT_EQ(m, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1500))));\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1500);\n}\n\n// The entire vec fits in one packet\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescing1) {\n  int n = 3;\n  auto vec = makeVec({3, 3, 3});\n  int pos = 0;\n  InSequence s;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 9))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL)) // no MSG_MORE\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(9))));\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 9);\n}\n\n// First packet is full, second two go in one packet\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescing2) {\n  int n = 3;\n  auto vec = makeVec({1500, 3, 3});\n  int pos = 0;\n  InSequence s;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1500))));\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 6))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL)) // no MSG_MORE\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(6))));\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1506);\n}\n\n// Two exactly full packets (coalesce ends midway through second chunk)\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescing3) {\n  int n = 3;\n  auto vec = makeVec({1000, 1000, 1000});\n  int pos = 0;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .Times(2)\n      .WillRepeatedly(Invoke([this, &pos](SSL*, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        pos += m;\n        return m;\n      }));\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 3000);\n}\n\n// Partial write success midway through a coalesced vec\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescing4) {\n  int n = 5;\n  auto vec = makeVec({300, 300, 300, 300, 300});\n  int pos = 0;\n  InSequence s1;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL)) // no MSG_MORE\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1000))));\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 3);\n  EXPECT_EQ(partialWritten, 100);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1000);\n  consumeVec(vec.get(), countWritten, partialWritten);\n\n  InSequence s2;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(500))));\n  sock_->testPerformWrite(\n      vec.get() + countWritten,\n      n - countWritten,\n      WriteFlags::NONE,\n      &countWritten,\n      &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 2);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1500);\n}\n\n// coalesce ends exactly on a buffer boundary\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescing5) {\n  int n = 3;\n  auto vec = makeVec({1000, 500, 500});\n  int pos = 0;\n  InSequence s;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1500))));\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(500))));\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 3);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 2000);\n}\n\n// partial write midway through first chunk\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescing6) {\n  int n = 2;\n  auto vec = makeVec({1000, 500});\n  int pos = 0;\n\n  InSequence s1;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL)) // no MSG_MORE\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(700))));\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::NONE, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 0);\n  EXPECT_EQ(partialWritten, 700);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 700);\n  consumeVec(vec.get(), countWritten, partialWritten);\n\n  InSequence s2;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 800))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()), sendSocketMessage(_, _, MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(800))));\n  sock_->testPerformWrite(\n      vec.get() + countWritten,\n      n - countWritten,\n      WriteFlags::NONE,\n      &countWritten,\n      &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 2);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1500);\n}\n\n// Repeat coalescing2 with WriteFlags::EOR\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescingWithEoRTracking1) {\n  int n = 3;\n  auto vec = makeVec({1500, 3, 3});\n  int pos = 0;\n  EXPECT_FALSE(sock_->isEorTrackingEnabled());\n  sock_->setEorTracking(true);\n  EXPECT_TRUE(sock_->isEorTrackingEnabled());\n\n  InSequence s;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        // the first 1500 does not have the EOR byte\n        EXPECT_EQ(folly::none, sock_->getCurrBytesToFinalByte());\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1500))));\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 6))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        EXPECT_EQ(m, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_EOR | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(6))));\n\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::EOR, &countWritten, &partialWritten);\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1506);\n}\n\n// coalescing with left over at the last chunk\n// WriteFlags::EOR turned on\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescingWithEoRTracking2) {\n  int n = 3;\n  auto vec = makeVec({600, 600, 600});\n  int pos = 0;\n  sock_->setEorTracking(true);\n\n  InSequence s;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        // the first 1500 does not have the EOR byte\n        EXPECT_EQ(folly::none, sock_->getCurrBytesToFinalByte());\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1500))));\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 300))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        EXPECT_EQ(m, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_EOR | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(300))));\n\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::EOR, &countWritten, &partialWritten);\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1800);\n}\n\n// WriteFlags::EOR set\n// One buf in iovec\n// Partial write at 1000-th byte\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescingWithEoRTracking3) {\n  int n = 1;\n  auto vec = makeVec({1600});\n  int pos = 0;\n  sock_->setEorTracking(true);\n\n  InSequence s;\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1600))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        // partial write of 1000 bytes\n        // currBytesToFinalByte should be 1600 at this point; expect full write\n        EXPECT_EQ(1600, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_EOR | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1000))));\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::EOR, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 0);\n  EXPECT_EQ(partialWritten, 1000);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1000);\n  consumeVec(vec.get(), countWritten, partialWritten);\n\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 600))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        EXPECT_EQ(m, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_EOR | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(600))));\n  sock_->testPerformWrite(\n      vec.get() + countWritten,\n      n - countWritten,\n      WriteFlags::EOR,\n      &countWritten,\n      &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1600);\n}\n\n// WriteFlags::EOR set\n// SSL_ERROR_WANT_WRITE occurs on first write\nTEST_F(AsyncSSLSocketWriteTest, WriteCoalescingWithEoRTrackingErrorWantWrite) {\n  int n = 1;\n  auto vec = makeVec({1500});\n  int pos = 0;\n  sock_->setEorTracking(true);\n\n  // first time we try to write, SSL_ERROR_WANT_WRITE will be returned\n  //\n  // this means no bytes were actually written to the socket,\n  // but getRawBytesWritten will still  be incremented by the write size as\n  // the bytes were appended to the BIO\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        EXPECT_EQ(m, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_EOR | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(0))));\n  EXPECT_CALL(*(sock_.get()), sslGetErrorImpl(_, _))\n      .WillOnce(Return(SSL_ERROR_WANT_WRITE));\n  ON_CALL( // should not be called, unless implementation changes to use it\n      *(sock_.get()),\n      getRawBytesWritten())\n      .WillByDefault(Return(1500));\n\n  uint32_t countWritten = 0;\n  uint32_t partialWritten = 0;\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::EOR, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, 0);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 0);\n\n  // second time we try to write, no error\n  // EOR should still be set\n  EXPECT_CALL(*(sock_.get()), sslWriteImpl(_, _, 1500))\n      .WillOnce(Invoke([=, this, &pos](SSL* ssl, const void* buf, int m) {\n        EXPECT_EQ(m, sock_->getCurrBytesToFinalByte().value_or(0));\n        verifyVec(buf, m, pos);\n        BIO* b = SSL_get_wbio(ssl);\n        auto result = AsyncSSLSocket::bioWrite(b, (const char*)buf, m);\n        pos += result;\n        return result;\n      }));\n  EXPECT_CALL(\n      *(sock_.get()),\n      sendSocketMessage(_, _, MSG_EOR | MSG_DONTWAIT | MSG_NOSIGNAL))\n      .WillOnce(Return(ByMove(AsyncSocket::WriteResult(1500))));\n  sock_->testPerformWrite(\n      vec.get(), n, WriteFlags::EOR, &countWritten, &partialWritten);\n  Mock::VerifyAndClearExpectations(sock_.get());\n  EXPECT_EQ(countWritten, n);\n  EXPECT_EQ(partialWritten, 0);\n  EXPECT_EQ(sock_->getAppBytesWritten(), 1500);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncSignalHandlerTestLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSignalHandler.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/test/Util.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace test {\n\nclass TestSignalHandler : public AsyncSignalHandler {\n public:\n  using AsyncSignalHandler::AsyncSignalHandler;\n\n  void signalReceived(int /* signum */) noexcept override { called = true; }\n\n  bool called{false};\n};\n\ntemplate <typename T>\nclass AsyncSignalHandlerTest : public ::testing::Test {\n protected:\n  void SetUp() override {\n    SKIP_IF(T::getBackend() == nullptr) << \"Backend not available\";\n  }\n\n  std::unique_ptr<EventBase> makeEventBase(\n      folly::EventBase::Options opts = folly::EventBase::Options()) {\n    return std::make_unique<EventBase>(opts.setBackendFactory([] {\n      return T::getBackend();\n    }));\n  }\n};\n\nTYPED_TEST_SUITE_P(AsyncSignalHandlerTest);\n\nTYPED_TEST_P(AsyncSignalHandlerTest, basic) {\n  auto evbPtr = this->makeEventBase();\n  auto& evb = *evbPtr;\n\n  TestSignalHandler handler{&evb};\n\n  handler.registerSignalHandler(SIGUSR1);\n  kill(getpid(), SIGUSR1);\n\n  EXPECT_FALSE(handler.called);\n  evb.loopOnce(EVLOOP_NONBLOCK);\n  EXPECT_TRUE(handler.called);\n}\n\nTYPED_TEST_P(AsyncSignalHandlerTest, attachEventBase) {\n  auto evbPtr = this->makeEventBase();\n  auto& evb = *evbPtr;\n\n  TestSignalHandler handler{nullptr};\n  EXPECT_FALSE(handler.getEventBase());\n\n  handler.attachEventBase(&evb);\n  EXPECT_EQ(&evb, handler.getEventBase());\n\n  handler.registerSignalHandler(SIGUSR1);\n  kill(getpid(), SIGUSR1);\n  EXPECT_FALSE(handler.called);\n  evb.loopOnce(EVLOOP_NONBLOCK);\n  EXPECT_TRUE(handler.called);\n\n  handler.unregisterSignalHandler(SIGUSR1);\n  handler.detachEventBase();\n  EXPECT_FALSE(handler.getEventBase());\n}\n\nREGISTER_TYPED_TEST_SUITE_P(AsyncSignalHandlerTest, basic, attachEventBase);\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncSocketExceptionTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSocketException.h>\n\n#include <array>\n\n#include <folly/Conv.h>\n#include <folly/io/async/SSLContext.h>\n#include <folly/io/async/ssl/SSLErrors.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/OpenSSL.h>\n\nnamespace folly {\n\nTEST(AsyncSocketException, SimpleTest) {\n  AsyncSocketException ex1(\n      AsyncSocketException::AsyncSocketExceptionType::NOT_OPEN,\n      \"test exception 1\");\n\n  EXPECT_EQ(\n      AsyncSocketException::AsyncSocketExceptionType::NOT_OPEN, ex1.getType());\n  EXPECT_EQ(0, ex1.getErrno());\n  EXPECT_EQ(\n      \"AsyncSocketException: test exception 1, type = Socket not open\",\n      std::string(ex1.what()));\n\n  AsyncSocketException ex2(\n      AsyncSocketException::AsyncSocketExceptionType::BAD_ARGS,\n      \"test exception 2\",\n      ECONNREFUSED);\n\n  EXPECT_EQ(\n      AsyncSocketException::AsyncSocketExceptionType::BAD_ARGS, ex2.getType());\n  EXPECT_EQ(ECONNREFUSED, ex2.getErrno());\n  EXPECT_EQ(\n      \"AsyncSocketException: test exception 2, type = Invalid arguments, \"\n      \"errno = \" +\n          to<std::string>(ECONNREFUSED) + \" (Connection refused)\",\n      std::string(ex2.what()));\n}\n\nTEST(AsyncSocketException, SSLExceptionType) {\n  {\n    SSLException eof(SSL_ERROR_ZERO_RETURN, 0, 0, 0);\n    EXPECT_EQ(eof.getType(), AsyncSocketException::END_OF_FILE);\n\n    SSLException netEof(SSL_ERROR_SYSCALL, 0, 0, 0);\n    EXPECT_EQ(netEof.getType(), AsyncSocketException::END_OF_FILE);\n\n    SSLException netOther(SSL_ERROR_SYSCALL, 0, 1, 0);\n    EXPECT_EQ(netOther.getType(), AsyncSocketException::NETWORK_ERROR);\n\n    std::array<int, 6> sslErrs{\n        {SSL_ERROR_SSL,\n         SSL_ERROR_WANT_READ,\n         SSL_ERROR_WANT_WRITE,\n         SSL_ERROR_WANT_X509_LOOKUP,\n         SSL_ERROR_WANT_CONNECT,\n         SSL_ERROR_WANT_ACCEPT}};\n\n    for (auto& e : sslErrs) {\n      SSLException sslEx(e, 0, 0, 0);\n      EXPECT_EQ(sslEx.getType(), AsyncSocketException::SSL_ERROR);\n    }\n  }\n\n  {\n    SSLException eof(SSLError::EOF_ERROR);\n    EXPECT_EQ(eof.getType(), AsyncSocketException::END_OF_FILE);\n\n    SSLException net(SSLError::NETWORK_ERROR);\n    EXPECT_EQ(net.getType(), AsyncSocketException::NETWORK_ERROR);\n\n    std::array<SSLError, 4> errs{\n        {SSLError::CLIENT_RENEGOTIATION,\n         SSLError::INVALID_RENEGOTIATION,\n         SSLError::EARLY_WRITE,\n         SSLError::SSL_ERROR}};\n\n    for (auto& e : errs) {\n      SSLException sslEx(e);\n      EXPECT_EQ(sslEx.getType(), AsyncSocketException::SSL_ERROR);\n    }\n  }\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncSocketObserverTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/test/AsyncSocketTest.h>\n#include <folly/io/async/test/MockAsyncSocketObserver.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing namespace folly::test;\nusing namespace ::testing;\n\nTEST(AsyncSocketObserver, ConstructorCallback) {\n  EventBase evb;\n  // create socket and verify that w/o a ctor callback, nothing happens\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_EQ(socket1->numObservers(), 0);\n\n  // Then register a constructor callback that registers a mock observer\n  // NB: use nicemock instead of strict b/c the actual lifecycle testing\n  // is done below and this simplifies the test\n  auto observer = std::make_shared<NiceMock<MockAsyncSocketObserver>>();\n  auto observerRawPtr = observer.get();\n  ConstructorCallbackList<AsyncSocket>::addCallback(\n      [observerRawPtr](AsyncSocket* s) { s->addObserver(observerRawPtr); });\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_EQ(socket2->numObservers(), 1);\n  EXPECT_THAT(socket2->findObservers(), UnorderedElementsAre(observer.get()));\n  Mock::VerifyAndClearExpectations(observer.get());\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenDetachAndAttachEvb) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  EventBase evb2;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  // Detach the evb and attach a new evb2\n  EXPECT_CALL(*observer, evbDetach(socket.get(), &evb));\n  socket->detachEventBase();\n  EXPECT_EQ(nullptr, socket->getEventBase());\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, evbAttach(socket.get(), &evb2));\n  socket->attachEventBase(&evb2);\n  EXPECT_EQ(&evb2, socket->getEventBase());\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  // detach the new evb2 and re-attach the old evb.\n  EXPECT_CALL(*observer, evbDetach(socket.get(), &evb2));\n  socket->detachEventBase();\n  EXPECT_EQ(nullptr, socket->getEventBase());\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, evbAttach(socket.get(), &evb));\n  socket->attachEventBase(&evb);\n  EXPECT_EQ(&evb, socket->getEventBase());\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, destroyed(socket.get(), _));\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenConnectAndCloseSocket) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  TestServer server;\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer, fdAttach(socket.get()));\n  EXPECT_CALL(*observer, connectSuccess(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, close(socket.get()));\n  socket->closeNow();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, destroyed(socket.get(), _));\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenConnectError) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  // port =1 is unreachble on localhost\n  folly::SocketAddress unreachable{\"::1\", 1};\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer, fdAttach(socket.get()));\n  EXPECT_CALL(*observer, close(socket.get()));\n  // the current state machine calls AsyncSocket::invokeConnectionError() twice\n  // for this use-case...\n  EXPECT_CALL(*observer, connectError(socket.get(), _)).Times(2);\n  EXPECT_CALL(*observer, destroyed(socket.get(), _));\n  socket->connect(nullptr, unreachable, 1);\n  evb.loop();\n  socket = nullptr;\n  Mock::VerifyAndClearExpectations(observer.get());\n}\n\nTEST(AsyncSocketObserver, AttachMultipleObserversThenConnectAndCloseSocket) {\n  auto observer1 = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  auto observer2 = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  TestServer server;\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  socket->addObserver(observer1.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer1.get()));\n\n  socket->addObserver(observer2.get());\n  EXPECT_EQ(socket->numObservers(), 2);\n  EXPECT_THAT(\n      socket->findObservers(),\n      UnorderedElementsAre(observer1.get(), observer2.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer1, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer2, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer1, fdAttach(socket.get()));\n  EXPECT_CALL(*observer2, fdAttach(socket.get()));\n  EXPECT_CALL(*observer1, connectSuccess(socket.get()));\n  EXPECT_CALL(*observer2, connectSuccess(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n\n  EXPECT_CALL(*observer1, close(socket.get()));\n  EXPECT_CALL(*observer2, close(socket.get()));\n  socket->closeNow();\n  Mock::VerifyAndClearExpectations(observer1.get());\n  Mock::VerifyAndClearExpectations(observer2.get());\n\n  EXPECT_CALL(*observer1, destroyed(socket.get(), _));\n  EXPECT_CALL(*observer2, destroyed(socket.get(), _));\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachThenRemoveObserver) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  EXPECT_TRUE(socket->removeObserver(observer.get()));\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, destroyed(socket.get(), _)).Times(0);\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachThenRemoveSharedPtrObserver) {\n  auto observer = std::make_shared<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer);\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  EXPECT_TRUE(socket->removeObserver(observer));\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, destroyed(socket.get(), _)).Times(0);\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachThenRemoveMultipleObservers) {\n  auto observer1 = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  auto observer2 = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  socket->addObserver(observer1.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer1.get()));\n\n  socket->addObserver(observer2.get());\n  EXPECT_EQ(socket->numObservers(), 2);\n  EXPECT_THAT(\n      socket->findObservers(),\n      UnorderedElementsAre(observer1.get(), observer2.get()));\n\n  EXPECT_TRUE(socket->removeObserver(observer1.get()));\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer2.get()));\n\n  EXPECT_TRUE(socket->removeObserver(observer2.get()));\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  EXPECT_CALL(*observer1, destroyed(socket.get(), _)).Times(0);\n  EXPECT_CALL(*observer2, destroyed(socket.get(), _)).Times(0);\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachThenRemoveMultipleObserversReverse) {\n  auto observer1 = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  auto observer2 = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  socket->addObserver(observer1.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer1.get()));\n\n  socket->addObserver(observer2.get());\n  EXPECT_EQ(socket->numObservers(), 2);\n  EXPECT_THAT(\n      socket->findObservers(),\n      UnorderedElementsAre(observer1.get(), observer2.get()));\n\n  EXPECT_TRUE(socket->removeObserver(observer2.get()));\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer1.get()));\n\n  EXPECT_TRUE(socket->removeObserver(observer1.get()));\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  EXPECT_CALL(*observer1, destroyed(socket.get(), _)).Times(0);\n  EXPECT_CALL(*observer2, destroyed(socket.get(), _)).Times(0);\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, RemoveMissingObserver) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_FALSE(socket->removeObserver(observer.get()));\n\n  EXPECT_CALL(*observer, destroyed(socket.get(), _)).Times(0);\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, RemoveMissingSharedPtrObserver) {\n  auto observer = std::make_shared<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_FALSE(socket->removeObserver(observer));\n\n  EXPECT_CALL(*observer, destroyed(socket.get(), _)).Times(0);\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenRemoveThenConnect) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  EXPECT_TRUE(socket->removeObserver(observer.get()));\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  // keep going to ensure no further callbacks\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenConnectThenRemoveObserver) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer, fdAttach(socket.get()));\n  EXPECT_CALL(*observer, connectSuccess(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_TRUE(socket->removeObserver(observer.get()));\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenDestroySocket) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  EXPECT_CALL(*observer, destroyed(socket.get(), _));\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachSharedPtrObserverThenDestroySocket) {\n  auto observer = std::make_shared<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer);\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  EXPECT_CALL(*observer.get(), destroyed(socket.get(), _));\n  socket = nullptr;\n}\n\nTEST(\n    AsyncSocketObserver,\n    AttachSharedPtrObserverThenDestroySocket_VerifyCtrKeepsSharedPtrAlive) {\n  auto observer = std::make_shared<StrictMock<MockAsyncSocketObserver>>();\n  MockAsyncSocketObserver::Safety dc(*observer.get());\n  ASSERT_FALSE(dc.destroyed());\n\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer);\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  // store raw pointer to observer, so that shared_ptr in container is the\n  // only thing keeping the observer alive\n  EXPECT_EQ(2, observer.use_count());\n  auto observerRaw = observer.get();\n  observer = nullptr;\n  ASSERT_FALSE(dc.destroyed());\n\n  // verify that the observer is informed of the socket destroy\n  EXPECT_CALL(*observerRaw, destroyed(socket.get(), _));\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenConnectThenDestroySocket) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer, fdAttach(socket.get()));\n  EXPECT_CALL(*observer, connectSuccess(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, close(socket.get()));\n  EXPECT_CALL(*observer, destroyed(socket.get(), _));\n  socket = nullptr;\n}\n\nTEST(\n    AsyncSocketObserver, AttachObserverThenConnectThenCloseThenRemoveObserver) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer, fdAttach(socket.get()));\n  EXPECT_CALL(*observer, connectSuccess(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, close(socket.get()));\n  socket->closeNow();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_TRUE(socket->removeObserver(observer.get()));\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  socket = nullptr;\n}\n\nTEST(\n    AsyncSocketObserver,\n    AttachObserverThenConnectThenRemoveObserverDuringClose) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket->numObservers(), 0);\n  EXPECT_THAT(socket->findObservers(), IsEmpty());\n\n  socket->addObserver(observer.get());\n  EXPECT_EQ(socket->numObservers(), 1);\n  EXPECT_THAT(socket->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket.get()));\n  EXPECT_CALL(*observer, fdAttach(socket.get()));\n  EXPECT_CALL(*observer, connectSuccess(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, close(socket.get()))\n      .WillOnce(Invoke([&observer](AsyncTransport* transport) {\n        if (auto sock =\n                transport->getUnderlyingTransport<folly::AsyncSocket>()) {\n          EXPECT_TRUE(sock->removeObserver(observer.get()));\n        }\n      }));\n  socket = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenConnectAndDetachFd) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  EventBase evb;\n  TestServer server;\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket1->numObservers(), 0);\n  EXPECT_THAT(socket1->findObservers(), IsEmpty());\n\n  socket1->addObserver(observer.get());\n  EXPECT_EQ(socket1->numObservers(), 1);\n  EXPECT_THAT(socket1->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket1.get()));\n  EXPECT_CALL(*observer, fdAttach(socket1.get()));\n  EXPECT_CALL(*observer, connectSuccess(socket1.get()));\n  socket1->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  EXPECT_CALL(*observer, fdDetach(socket1.get()));\n  auto fd = socket1->detachNetworkSocket();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  // create socket2 using fd, then immediately destroy it, no events\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(&evb, fd));\n  socket2 = nullptr;\n\n  // destroy socket1\n  EXPECT_CALL(*observer, destroyed(socket1.get(), _));\n  socket1 = nullptr;\n}\n\nTEST(AsyncSocketObserver, AttachObserverThenConnectAndMoveSocket) {\n  auto observer = std::make_unique<StrictMock<MockAsyncSocketObserver>>();\n  TestServer server;\n  EventBase evb;\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  EXPECT_EQ(socket1->numObservers(), 0);\n  EXPECT_THAT(socket1->findObservers(), IsEmpty());\n\n  socket1->addObserver(observer.get());\n  EXPECT_EQ(socket1->numObservers(), 1);\n  EXPECT_THAT(socket1->findObservers(), UnorderedElementsAre(observer.get()));\n\n  InSequence s;\n  EXPECT_CALL(*observer, connectAttempt(socket1.get()));\n  EXPECT_CALL(*observer, fdAttach(socket1.get()));\n  EXPECT_CALL(*observer, connectSuccess(socket1.get()));\n  socket1->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(observer.get());\n\n  // move the socket\n  EXPECT_CALL(*observer, moved(socket1.get(), _, _));\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(std::move(socket1)));\n  Mock::VerifyAndClearExpectations(observer.get());\n  EXPECT_EQ(socket2->numObservers(), 1);\n  EXPECT_THAT(socket2->findObservers(), UnorderedElementsAre(observer.get()));\n\n  // destroy socket2\n  EXPECT_CALL(*observer, close(socket2.get()));\n  EXPECT_CALL(*observer, destroyed(socket2.get(), _));\n  socket2 = nullptr;\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncSocketTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncSocket.h>\n\n#include <iostream>\n\n#include <folly/io/async/AsyncServerSocket.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing namespace testing;\n\n#ifndef TCP_SAVE_SYN\n#define TCP_SAVE_SYN 27\n#endif\n\nTEST(AsyncSocketTest, getSockOpt) {\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> socket =\n      AsyncSocket::newSocket(&evb, NetworkSocket(0));\n\n  int val;\n  socklen_t len;\n\n  int expectedRc = netops::getsockopt(\n      socket->getNetworkSocket(), SOL_SOCKET, SO_REUSEADDR, &val, &len);\n  int actualRc = socket->getSockOpt(SOL_SOCKET, SO_REUSEADDR, &val, &len);\n\n  EXPECT_EQ(expectedRc, actualRc);\n}\n\nTEST(AsyncSocketTest, REUSEPORT) {\n  EventBase base;\n  auto serverSocket = AsyncServerSocket::newSocket(&base);\n  serverSocket->bind(0);\n  serverSocket->listen(0);\n  serverSocket->startAccepting();\n\n  try {\n    serverSocket->setReusePortEnabled(true);\n  } catch (...) {\n    LOG(INFO) << \"Reuse port probably not supported\";\n    return;\n  }\n\n  SocketAddress address;\n  serverSocket->getAddress(&address);\n  int port = address.getPort();\n\n  auto serverSocket2 = AsyncServerSocket::newSocket(&base);\n  serverSocket2->setReusePortEnabled(true);\n  serverSocket2->bind(port);\n  serverSocket2->listen(0);\n  serverSocket2->startAccepting();\n}\n\nTEST(AsyncSocketTest, DisableReuseAddr) {\n  EventBase base;\n  auto serverSocket = AsyncServerSocket::newSocket(&base);\n  serverSocket->setEnableReuseAddr(false /* enable */);\n  // idempotent\n  serverSocket->setEnableReuseAddr(false /* enable */);\n  serverSocket->setEnableReuseAddr(false /* enable */);\n  serverSocket->bind(0);\n\n  SocketAddress address;\n  serverSocket->getAddress(&address);\n  int port = address.getPort();\n\n  auto serverSocket2 = AsyncServerSocket::newSocket(&base);\n  serverSocket2->setEnableReuseAddr(false /* enable */);\n  // idempotent\n  serverSocket2->setEnableReuseAddr(false /* enable */);\n  serverSocket2->setEnableReuseAddr(false /* enable */);\n  EXPECT_THROW(serverSocket2->bind(port), std::system_error);\n  // it's ok to bind to a different port\n  serverSocket2->bind(0);\n}\n\nTEST(AsyncSocketTest, EnableThenDisableReuseAddr) {\n  EventBase base;\n  auto serverSocket = AsyncServerSocket::newSocket(&base);\n  serverSocket->bind(0);\n\n  SocketAddress address;\n  serverSocket->getAddress(&address);\n  int port = address.getPort();\n\n  auto serverSocket2 = AsyncServerSocket::newSocket(&base);\n  // defaulty SO_REUSEADDR enabled so can bind to same port\n  serverSocket2->bind(port);\n  serverSocket2->setEnableReuseAddr(false /* enable */);\n  serverSocket->setEnableReuseAddr(false /* enable */);\n\n  EXPECT_THROW(serverSocket2->bind(port), std::system_error);\n  // it's ok to bind to a different port\n  serverSocket2->bind(0);\n}\n\nTEST(AsyncSocketTest, IPFreebind) {\n  EventBase base;\n  // We expect an IPv4 bind to a public address to fail.\n  auto testAddress = folly::SocketAddress(\"8.8.8.8\", 443);\n  auto serverSocket = AsyncServerSocket::newSocket(&base);\n  serverSocket->listen(0);\n  serverSocket->startAccepting();\n\n  try {\n    serverSocket->bind(testAddress);\n    ASSERT_TRUE(false) << \"Should have thrown on IPv4 bind\";\n  } catch (...) {\n    return;\n  }\n\n  auto serverSocket2 = AsyncServerSocket::newSocket(&base);\n  serverSocket2->setIPFreebind(true);\n  serverSocket2->bind(testAddress);\n  serverSocket2->listen(0);\n  serverSocket2->startAccepting();\n}\n\nTEST(AsyncSocketTest, v4v6samePort) {\n  EventBase base;\n  auto serverSocket = AsyncServerSocket::newSocket(&base);\n  serverSocket->bind(0);\n  auto addrs = serverSocket->getAddresses();\n  ASSERT_GT(addrs.size(), 0);\n  uint16_t port = addrs[0].getPort();\n  for (const auto& addr : addrs) {\n    EXPECT_EQ(port, addr.getPort());\n  }\n}\n\nTEST(AsyncSocketTest, duplicateBind) {\n  EventBase base;\n  auto server1 = AsyncServerSocket::newSocket(&base);\n  server1->bind(0);\n  server1->listen(10);\n\n  SocketAddress address;\n  server1->getAddress(std::addressof(address));\n\n  auto server2 = AsyncServerSocket::newSocket(&base);\n  EXPECT_THROW(server2->bind(address.getPort()), std::exception);\n}\n\nTEST(AsyncSocketTest, tosReflect) {\n  EventBase base;\n  auto server1 = AsyncServerSocket::newSocket(&base);\n  server1->bind(0);\n  server1->listen(10);\n  auto fd = server1->getNetworkSocket();\n\n  // Verify if tos reflect is disabled by default\n  // and the TCP_SAVE_SYN setting is not enabled\n  EXPECT_FALSE(server1->getTosReflect());\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc =\n      netops::getsockopt(fd, IPPROTO_TCP, TCP_SAVE_SYN, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 0);\n\n  // Enable TOS reflect on the server socket\n  server1->setTosReflect(true);\n\n  // Verify if tos reflect is enabled now\n  // and the TCP_SAVE_SYN setting is also enabled\n  EXPECT_TRUE(server1->getTosReflect());\n  rc = netops::getsockopt(fd, IPPROTO_TCP, TCP_SAVE_SYN, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 1);\n}\n\nTEST(AsyncSocketTest, listenerTosV6) {\n  EventBase base;\n  auto server1 = AsyncServerSocket::newSocket(&base);\n  server1->bind(0);\n  server1->listen(10);\n  auto fd = server1->getNetworkSocket();\n\n  // Verify if Listener TOS is disabled by default\n  EXPECT_FALSE(server1->getListenerTos());\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc =\n      netops::getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 0);\n\n  // Set listener Tos to 116 (0x74, represents dscp 29)\n  server1->setListenerTos(116);\n\n  // Verify if listener DSCP is set now\n  EXPECT_TRUE(server1->getListenerTos());\n  rc = netops::getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 116);\n}\n\nTEST(AsyncSocketTest, listenerTosV4) {\n  EventBase base;\n  auto server1 = AsyncServerSocket::newSocket(&base);\n  folly::IPAddress ip(\"127.0.0.1\");\n  std::vector<folly::IPAddress> serverIp;\n  serverIp.push_back(ip);\n  server1->bind(serverIp, 0);\n  server1->listen(10);\n  auto fd = server1->getNetworkSocket();\n\n  // Verify if Listener TOS is disabled by default\n  EXPECT_FALSE(server1->getListenerTos());\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc = netops::getsockopt(fd, IPPROTO_IP, IP_TOS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 0);\n\n  // Set listener Tos to 140 (0x8c, represents dscp 35)\n  server1->setListenerTos(140);\n\n  // Verify if listener DSCP is set now\n  EXPECT_TRUE(server1->getListenerTos());\n  rc = netops::getsockopt(fd, IPPROTO_IP, IP_TOS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 140);\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncSocketTest.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/EventBaseBackendBase.h>\n#include <folly/io/async/test/BlockingSocket.h>\n#include <folly/io/async/test/CallbackStateEnum.h>\n#include <folly/io/async/test/ConnCallback.h>\n#include <folly/net/NetOps.h>\n#include <folly/net/NetworkSocket.h>\n#include <folly/portability/Sockets.h>\n\nnamespace folly::test {\n\nclass WriteCallback\n    : public folly::AsyncTransport::WriteCallback,\n      public folly::AsyncWriter::ReleaseIOBufCallback {\n public:\n  explicit WriteCallback(bool enableReleaseIOBufCallback = false)\n      : state(STATE_WAITING),\n        bytesWritten(0),\n        numIoBufCount(0),\n        numIoBufBytes(0),\n        exception(folly::AsyncSocketException::UNKNOWN, \"none\"),\n        releaseIOBufCallback(enableReleaseIOBufCallback ? this : nullptr) {}\n\n  void writeSuccess() noexcept override {\n    state = STATE_SUCCEEDED;\n    if (successCallback) {\n      successCallback();\n    }\n  }\n\n  void writeErr(\n      size_t nBytesWritten,\n      const folly::AsyncSocketException& ex) noexcept override {\n    LOG(ERROR) << ex.what();\n    state = STATE_FAILED;\n    this->bytesWritten = nBytesWritten;\n    exception = ex;\n    if (errorCallback) {\n      errorCallback();\n    }\n  }\n\n  void writeStarting() noexcept override { writeStartingInvocations++; }\n\n  folly::AsyncWriter::ReleaseIOBufCallback*\n  getReleaseIOBufCallback() noexcept override {\n    return releaseIOBufCallback;\n  }\n\n  void releaseIOBuf(std::unique_ptr<folly::IOBuf> ioBuf) noexcept override {\n    numIoBufCount += ioBuf->countChainElements();\n    numIoBufBytes += ioBuf->computeChainDataLength();\n    releaseIOBufCallbackCalled = true;\n  }\n\n  StateEnum state;\n  std::atomic<size_t> bytesWritten;\n  std::atomic<size_t> numIoBufCount;\n  std::atomic<size_t> numIoBufBytes;\n  folly::AsyncSocketException exception;\n  VoidCallback successCallback;\n  VoidCallback errorCallback;\n  ReleaseIOBufCallback* releaseIOBufCallback;\n  size_t writeStartingInvocations{0};\n  bool releaseIOBufCallbackCalled{false};\n};\n\nclass ReadCallback : public folly::AsyncTransport::ReadCallback {\n public:\n  explicit ReadCallback(size_t _maxBufferSz = 4096)\n      : state(STATE_WAITING),\n        exception(folly::AsyncSocketException::UNKNOWN, \"none\"),\n        buffers(),\n        maxBufferSz(_maxBufferSz) {}\n\n  ~ReadCallback() override {\n    for (auto& buffer : buffers) {\n      buffer.free();\n    }\n    currentBuffer.free();\n  }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    if (!currentBuffer.buffer) {\n      currentBuffer.allocate(maxBufferSz);\n    }\n    *bufReturn = currentBuffer.buffer;\n    *lenReturn = currentBuffer.length;\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    currentBuffer.length = len;\n    buffers.push_back(std::move(currentBuffer));\n    currentBuffer.reset();\n    if (dataAvailableCallback) {\n      dataAvailableCallback();\n    }\n  }\n\n  size_t maxBufferSize() const override { return maxBufferSz; }\n  bool isBufferMovable() noexcept override { return true; }\n  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {\n    if (currentBuffer.buffer && currentBuffer.length > 0) {\n      buffers.push_back(std::move(currentBuffer));\n    }\n    currentBuffer.reset();\n\n    auto coalesced = readBuf->cloneCoalesced();\n    currentBuffer.buffer = (char*)coalesced->writableData();\n    currentBuffer.length = coalesced->length();\n    currentBuffer.ioBuf = std::move(coalesced);\n    buffers.push_back(std::move(currentBuffer));\n    currentBuffer.reset();\n    if (dataAvailableCallback) {\n      dataAvailableCallback();\n    }\n  }\n\n  void readEOF() noexcept override { state = STATE_SUCCEEDED; }\n\n  void readErr(const folly::AsyncSocketException& ex) noexcept override {\n    state = STATE_FAILED;\n    exception = ex;\n  }\n\n  void verifyData(const char* expected, size_t expectedLen) const {\n    verifyData((const unsigned char*)expected, expectedLen);\n  }\n\n  void verifyData(const unsigned char* expected, size_t expectedLen) const {\n    size_t offset = 0;\n    for (size_t idx = 0; idx < buffers.size(); ++idx) {\n      const auto& buf = buffers[idx];\n      size_t cmpLen = std::min(buf.length, expectedLen - offset);\n      CHECK_EQ(memcmp(buf.buffer, expected + offset, cmpLen), 0);\n      CHECK_EQ(cmpLen, buf.length);\n      offset += cmpLen;\n    }\n    CHECK_EQ(offset, expectedLen);\n  }\n\n  void clearData() {\n    for (auto& buffer : buffers) {\n      buffer.free();\n    }\n    buffers.clear();\n  }\n\n  size_t dataRead() const {\n    size_t ret = 0;\n    for (const auto& buf : buffers) {\n      ret += buf.length;\n    }\n    return ret;\n  }\n\n  void coalesce() {\n    if (buffers.empty()) {\n      return;\n    }\n\n    std::unique_ptr<IOBuf> coalesced;\n\n    for (auto& buf : buffers) {\n      if (!buf.ioBuf) {\n        return;\n      }\n\n      if (!coalesced) {\n        coalesced = std::move(buf.ioBuf);\n      } else {\n        coalesced->appendToChain(std::move(buf.ioBuf));\n      }\n    }\n    buffers.clear();\n    coalesced->coalesce();\n    buffers.emplace_back(std::move(coalesced));\n  }\n\n  class Buffer {\n   public:\n    Buffer() : buffer(nullptr), length(0) {}\n    Buffer(char* buf, size_t len) : buffer(buf), length(len) {}\n    explicit Buffer(std::unique_ptr<IOBuf> ioBuf)\n        : buffer((char*)ioBuf->writableData()),\n          length(ioBuf->length()),\n          ioBuf(std::move(ioBuf)) {}\n\n    void reset() {\n      buffer = nullptr;\n      length = 0;\n      ioBuf.reset();\n    }\n    void allocate(size_t len) {\n      assert(buffer == nullptr);\n      this->buffer = static_cast<char*>(malloc(len));\n      this->length = len;\n    }\n    void free() {\n      if (!ioBuf) {\n        ::free(buffer);\n      }\n      reset();\n    }\n\n    char* buffer;\n    size_t length;\n    std::unique_ptr<IOBuf> ioBuf;\n  };\n\n  StateEnum state;\n  folly::AsyncSocketException exception;\n  std::vector<Buffer> buffers;\n  Buffer currentBuffer;\n  VoidCallback dataAvailableCallback;\n  const size_t maxBufferSz;\n};\n\nclass TestEventBaseBackend : public folly::EventBaseBackendBase {\n public:\n  explicit TestEventBaseBackend() : evb_(EventBase::getDefaultBackend()) {}\n\n  event_base* getEventBase() override { return evb_->getEventBase(); }\n  int eb_event_base_loop(int flags) override {\n    return evb_->eb_event_base_loop(flags);\n  }\n  int eb_event_base_loopbreak() override {\n    return evb_->eb_event_base_loopbreak();\n  }\n\n  int eb_event_add(Event& event, const struct timeval* timeout) override {\n    return evb_->eb_event_add(event, timeout);\n  }\n  int eb_event_del(Event& event) override { return evb_->eb_event_del(event); }\n\n  bool eb_event_active(Event& event, int res) override {\n    return evb_->eb_event_active(event, res);\n  }\n\n  void queueRecvZc(\n      int fd,\n      void* buf,\n      unsigned long nbytes,\n      EventBaseBackendBase::RecvZcCallback&& callback) override {\n    queued = true;\n    bytes = netops::recv(NetworkSocket::fromFd(fd), buf, nbytes, MSG_DONTWAIT);\n    recvZcCb = std::move(callback);\n  }\n\n  bool queued{false};\n  ssize_t bytes{0};\n  EventBaseBackendBase::RecvZcCallback recvZcCb;\n\n private:\n  std::unique_ptr<folly::EventBaseBackendBase> evb_;\n};\n\nclass ReadvCallback : public folly::AsyncTransport::ReadCallback {\n public:\n  ReadvCallback(size_t bufferSize, size_t len)\n      : state_(STATE_WAITING),\n        exception_(folly::AsyncSocketException::UNKNOWN, \"none\"),\n        queue_(folly::IOBufIovecBuilder::Options().setBlockSize(bufferSize)),\n        len_(len) {\n    setReadMode(folly::AsyncTransport::ReadCallback::ReadMode::ReadVec);\n  }\n\n  ~ReadvCallback() override = default;\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    std::ignore = bufReturn;\n    std::ignore = lenReturn;\n\n    CHECK(false); // this should not be called\n  }\n\n  void getReadBuffers(folly::IOBufIovecBuilder::IoVecVec& iovs) override {\n    queue_.allocateBuffers(iovs, len_);\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    auto tmp = queue_.extractIOBufChain(len);\n    if (!buf_) {\n      buf_ = std::move(tmp);\n    } else {\n      buf_->prependChain(std::move(tmp));\n    }\n  }\n\n  bool isBufferMovable() noexcept override { return true; }\n  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {\n    if (!buf_) {\n      buf_ = std::move(readBuf);\n    } else {\n      buf_->appendToChain(std::move(readBuf));\n    }\n  }\n\n  void reset() { buf_.reset(); }\n\n  void readEOF() noexcept override { state_ = STATE_SUCCEEDED; }\n\n  void readErr(const folly::AsyncSocketException& ex) noexcept override {\n    state_ = STATE_FAILED;\n    exception_ = ex;\n  }\n\n  void verifyData(const std::string& data) const {\n    CHECK(buf_);\n    auto r = buf_->coalesce();\n    std::string tmp;\n    tmp.assign(reinterpret_cast<const char*>(r.begin()), r.end() - r.begin());\n    CHECK_EQ(data, tmp);\n  }\n\n  std::unique_ptr<folly::IOBuf> buf_;\n\n private:\n  StateEnum state_;\n  folly::AsyncSocketException exception_;\n  folly::IOBufIovecBuilder queue_;\n  const size_t len_;\n};\n\nclass BufferCallback : public folly::AsyncTransport::BufferCallback {\n public:\n  BufferCallback(folly::AsyncSocket* socket, size_t expectedBytes)\n      : socket_(socket),\n        expectedBytes_(expectedBytes),\n        buffered_(false),\n        bufferCleared_(false) {}\n\n  void onEgressBuffered() override {\n    size_t bytesWritten = socket_->getAppBytesWritten();\n    size_t bytesBuffered = socket_->getAppBytesBuffered();\n    CHECK_GT(bytesBuffered, 0);\n    CHECK_EQ(expectedBytes_, bytesWritten + bytesBuffered);\n    buffered_ = true;\n  }\n\n  void onEgressBufferCleared() override {\n    size_t bytesWritten = socket_->getAppBytesWritten();\n    size_t bytesBuffered = socket_->getAppBytesBuffered();\n    CHECK_EQ(0, bytesBuffered);\n    CHECK_EQ(expectedBytes_, bytesWritten);\n    bufferCleared_ = true;\n  }\n\n  bool hasBuffered() const { return buffered_; }\n\n  bool hasBufferCleared() const { return bufferCleared_; }\n\n private:\n  folly::AsyncSocket* socket_{nullptr};\n  size_t expectedBytes_{0};\n  bool buffered_{false};\n  bool bufferCleared_{false};\n};\n\nclass ZeroCopyReadCallback : public folly::AsyncTransport::ReadCallback {\n public:\n  explicit ZeroCopyReadCallback(\n      folly::AsyncTransport::ReadCallback::ZeroCopyMemStore* memStore,\n      size_t _maxBufferSz = 4096)\n      : memStore_(memStore),\n        state(STATE_WAITING),\n        exception(folly::AsyncSocketException::UNKNOWN, \"none\"),\n        maxBufferSz(_maxBufferSz) {}\n\n  ~ZeroCopyReadCallback() override { currentBuffer.free(); }\n\n  // zerocopy\n  folly::AsyncTransport::ReadCallback::ZeroCopyMemStore*\n  readZeroCopyEnabled() noexcept override {\n    return memStore_;\n  }\n\n  void getZeroCopyFallbackBuffer(\n      void** bufReturn, size_t* lenReturn) noexcept override {\n    if (!currentZeroCopyBuffer.buffer) {\n      currentZeroCopyBuffer.allocate(maxBufferSz);\n    }\n    *bufReturn = currentZeroCopyBuffer.buffer;\n    *lenReturn = currentZeroCopyBuffer.length;\n  }\n\n  void readZeroCopyDataAvailable(\n      std::unique_ptr<folly::IOBuf>&& zeroCopyData,\n      size_t additionalBytes) noexcept override {\n    auto ioBuf = std::move(zeroCopyData);\n    if (additionalBytes) {\n      auto tmp = folly::IOBuf::takeOwnership(\n          currentZeroCopyBuffer.buffer,\n          currentZeroCopyBuffer.length,\n          0,\n          additionalBytes);\n      currentZeroCopyBuffer.reset();\n      if (ioBuf) {\n        ioBuf->prependChain(std::move(tmp));\n      } else {\n        ioBuf = std::move(tmp);\n      }\n    }\n\n    if (!data_) {\n      data_ = std::move(ioBuf);\n    } else {\n      data_->prependChain(std::move(ioBuf));\n    }\n  }\n\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    if (!currentBuffer.buffer) {\n      currentBuffer.allocate(maxBufferSz);\n    }\n    *bufReturn = currentBuffer.buffer;\n    *lenReturn = currentBuffer.length;\n  }\n\n  void readDataAvailable(size_t len) noexcept override {\n    auto ioBuf = folly::IOBuf::takeOwnership(\n        currentBuffer.buffer, currentBuffer.length, 0, len);\n    currentBuffer.reset();\n\n    if (!data_) {\n      data_ = std::move(ioBuf);\n    } else {\n      data_->prependChain(std::move(ioBuf));\n    }\n  }\n\n  bool isBufferMovable() noexcept override { return true; }\n  void readBufferAvailable(std::unique_ptr<IOBuf> readBuf) noexcept override {\n    if (!data_) {\n      data_ = std::move(readBuf);\n    } else {\n      data_->prependChain(std::move(readBuf));\n    }\n  }\n\n  void readEOF() noexcept override { state = STATE_SUCCEEDED; }\n\n  void readErr(const folly::AsyncSocketException& ex) noexcept override {\n    state = STATE_FAILED;\n    exception = ex;\n  }\n\n  void verifyData(const std::string& expected) const {\n    verifyData((const unsigned char*)expected.data(), expected.size());\n  }\n\n  void verifyData(const unsigned char* expected, size_t expectedLen) const {\n    CHECK(!!data_);\n    auto len = data_->computeChainDataLength();\n    CHECK_EQ(len, expectedLen);\n\n    auto* buf = data_.get();\n    auto* current = buf;\n    size_t offset = 0;\n\n    do {\n      size_t cmpLen = std::min(current->length(), expectedLen - offset);\n      CHECK_EQ(cmpLen, current->length());\n      CHECK_EQ(memcmp(current->data(), expected + offset, cmpLen), 0);\n      offset += cmpLen;\n\n      current = current->next();\n    } while (current != buf);\n\n    std::ignore = expected;\n    CHECK_EQ(offset, expectedLen);\n  }\n\n  class Buffer {\n   public:\n    Buffer() = default;\n    Buffer(char* buf, size_t len) : buffer(buf), length(len) {}\n    ~Buffer() {\n      if (buffer) {\n        ::free(buffer);\n      }\n    }\n\n    void reset() {\n      buffer = nullptr;\n      length = 0;\n    }\n    void allocate(size_t len) {\n      CHECK(buffer == nullptr);\n      buffer = static_cast<char*>(malloc(len));\n      length = len;\n    }\n    void free() {\n      ::free(buffer);\n      reset();\n    }\n\n    char* buffer{nullptr};\n    size_t length{0};\n  };\n  folly::AsyncTransport::ReadCallback::ZeroCopyMemStore* memStore_;\n  StateEnum state;\n  folly::AsyncSocketException exception;\n  Buffer currentBuffer, currentZeroCopyBuffer;\n  VoidCallback dataAvailableCallback;\n  const size_t maxBufferSz;\n  std::unique_ptr<folly::IOBuf> data_;\n};\n\nclass ReadVerifier {};\n\nclass TestSendMsgParamsCallback\n    : public folly::AsyncSocket::SendMsgParamsCallback {\n public:\n  TestSendMsgParamsCallback(int flags, uint32_t dataSize, void* data)\n      : flags_(flags),\n        writeFlags_(folly::WriteFlags::NONE),\n        dataSize_(dataSize),\n        data_(data),\n        queriedFlags_(false),\n        queriedData_(false) {}\n\n  void reset(int flags) {\n    flags_ = flags;\n    writeFlags_ = folly::WriteFlags::NONE;\n    queriedFlags_ = false;\n    queriedData_ = false;\n  }\n\n  int getFlagsImpl(\n      folly::WriteFlags flags, int /*defaultFlags*/) noexcept override {\n    queriedFlags_ = true;\n    if (writeFlags_ == folly::WriteFlags::NONE) {\n      writeFlags_ = flags;\n    } else {\n      assert(flags == writeFlags_);\n    }\n    return flags_;\n  }\n\n  void getAncillaryData(\n      folly::WriteFlags flags,\n      void* data,\n      const folly::AsyncSocket::WriteRequestTag& tag,\n      const bool /* byteEventsEnabled */) noexcept override {\n    CHECK_EQ(tag, expectedTag_);\n    queriedData_ = true;\n    if (writeFlags_ == folly::WriteFlags::NONE) {\n      writeFlags_ = flags;\n    } else {\n      assert(flags == writeFlags_);\n    }\n    assert(data != nullptr);\n    memcpy(data, data_, dataSize_);\n  }\n\n  uint32_t getAncillaryDataSize(\n      folly::WriteFlags flags,\n      const folly::AsyncSocket::WriteRequestTag& tag,\n      const bool /* byteEventsEnabled */) noexcept override {\n    CHECK_EQ(tag, expectedTag_);\n    if (writeFlags_ == folly::WriteFlags::NONE) {\n      writeFlags_ = flags;\n    } else {\n      assert(flags == writeFlags_);\n    }\n    return dataSize_;\n  }\n\n  void wroteBytes(\n      const folly::AsyncSocket::WriteRequestTag& tag) noexcept override {\n    CHECK_EQ(tag, expectedTag_);\n    tagLastWritten_ = tag;\n  }\n\n  int flags_;\n  folly::WriteFlags writeFlags_;\n  uint32_t dataSize_;\n  void* data_;\n  bool queriedFlags_;\n  bool queriedData_;\n  folly::AsyncSocket::WriteRequestTag expectedTag_{\n      folly::AsyncSocket::WriteRequestTag::EmptyDummy()};\n  std::optional<folly::AsyncSocket::WriteRequestTag> tagLastWritten_;\n};\n\nclass TestServer {\n public:\n  // Create a TestServer.\n  // This immediately starts listening on an ephemeral port.\n  explicit TestServer(bool enableTFO = false, int bufSize = -1) : fd_() {\n    namespace fsp = folly::portability::sockets;\n    fd_ = folly::netops::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);\n    if (fd_ == folly::NetworkSocket()) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"failed to create test server socket\",\n          errno);\n    }\n    if (folly::netops::set_socket_non_blocking(fd_) != 0) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"failed to put test server socket in \"\n          \"non-blocking mode\",\n          errno);\n    }\n    if (enableTFO) {\n#if FOLLY_ALLOW_TFO\n      auto ret = folly::detail::tfo_enable(fd_, 100);\n      PCHECK(ret == 0);\n#endif\n    }\n\n    struct addrinfo hints, *res;\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_family = AF_INET;\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_flags = AI_PASSIVE;\n\n    if (getaddrinfo(nullptr, \"0\", &hints, &res)) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"Attempted to bind address to socket with \"\n          \"bad getaddrinfo\",\n          errno);\n    }\n\n    SCOPE_EXIT {\n      freeaddrinfo(res);\n    };\n\n    if (bufSize > 0) {\n      folly::netops::setsockopt(\n          fd_, SOL_SOCKET, SO_SNDBUF, &bufSize, sizeof(bufSize));\n      folly::netops::setsockopt(\n          fd_, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize));\n    }\n\n    if (folly::netops::bind(fd_, res->ai_addr, res->ai_addrlen)) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"failed to bind to async server socket for port 10\",\n          errno);\n    }\n\n    if (folly::netops::listen(fd_, 10) != 0) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"failed to listen on test server socket\",\n          errno);\n    }\n\n    address_.setFromLocalAddress(fd_);\n    // The local address will contain 0.0.0.0.\n    // Change it to 127.0.0.1, so it can be used to connect to the server\n    address_.setFromIpPort(\"127.0.0.1\", address_.getPort());\n  }\n\n  ~TestServer() {\n    if (fd_ != folly::NetworkSocket()) {\n      folly::netops::close(fd_);\n    }\n  }\n\n  // Get the address for connecting to the server\n  const folly::SocketAddress& getAddress() const { return address_; }\n\n  folly::NetworkSocket acceptFD(int timeout = 50) {\n    folly::netops::PollDescriptor pfd;\n    pfd.fd = fd_;\n    pfd.events = POLLIN;\n    int ret = folly::netops::poll(&pfd, 1, timeout);\n    if (ret == 0) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"test server accept() timed out\");\n    } else if (ret < 0) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"test server accept() poll failed\",\n          errno);\n    }\n\n    auto acceptedFd = folly::netops::accept(fd_, nullptr, nullptr);\n    if (acceptedFd == folly::NetworkSocket()) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::INTERNAL_ERROR,\n          \"test server accept() failed\",\n          errno);\n    }\n\n    return acceptedFd;\n  }\n\n  std::shared_ptr<BlockingSocket> accept(int timeout = 50) {\n    auto fd = acceptFD(timeout);\n    return std::make_shared<BlockingSocket>(fd);\n  }\n\n  std::shared_ptr<folly::AsyncSocket> acceptAsync(\n      folly::EventBase* evb, int timeout = 50) {\n    auto fd = acceptFD(timeout);\n    return folly::AsyncSocket::newSocket(evb, fd);\n  }\n\n  /**\n   * Accept a connection, read data from it, and verify that it matches the\n   * data in the specified buffer.\n   */\n  void verifyConnection(const char* buf, size_t len) {\n    // accept a connection\n    std::shared_ptr<BlockingSocket> acceptedSocket = accept();\n    // read the data and compare it to the specified buffer\n    std::unique_ptr<uint8_t[]> readbuf(new uint8_t[len]);\n    acceptedSocket->readAll(readbuf.get(), len);\n    CHECK_EQ(memcmp(buf, readbuf.get(), len), 0);\n    // make sure we get EOF next\n    uint32_t bytesRead = acceptedSocket->read(readbuf.get(), len);\n    CHECK_EQ(bytesRead, 0);\n  }\n\n private:\n  folly::NetworkSocket fd_;\n  folly::SocketAddress address_;\n};\n\n} // namespace folly::test\n"
  },
  {
    "path": "folly/io/async/test/AsyncSocketTest2.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/test/AsyncSocketTest2.h>\n\n#include <fcntl.h>\n#include <sys/types.h>\n\n#include <time.h>\n#include <iostream>\n#include <memory>\n#include <thread>\n\n#include <folly/ExceptionWrapper.h>\n#include <folly/Random.h>\n#include <folly/SocketAddress.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/io/async/test/AsyncSocketTest.h>\n#include <folly/io/async/test/MockAsyncSocketLegacyObserver.h>\n#include <folly/io/async/test/MockAsyncSocketObserver.h>\n#include <folly/io/async/test/TFOUtil.h>\n#include <folly/io/async/test/Util.h>\n#include <folly/net/test/MockNetOpsDispatcher.h>\n#include <folly/net/test/MockTcpInfoDispatcher.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Sockets.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/test/SocketAddressTestHelper.h>\n#include <folly/testing/TestUtil.h>\n\nusing std::min;\nusing std::string;\nusing std::unique_ptr;\nusing std::vector;\nusing std::chrono::milliseconds;\nusing testing::MatchesRegex;\n\nusing namespace folly;\nusing namespace folly::test;\nusing namespace testing;\n\nstatic constexpr auto kMaxAttemptsEnableByteEvents =\n    folly::AsyncSocket::kMaxAttemptsEnableByteEvents;\n\nnamespace {\n// string and corresponding vector with 100 characters\nconst std::string kOneHundredCharacterString(\n    \"ThisIsAVeryLongStringThatHas100Characters\"\n    \"AndIsUniqueEnoughToBeInterestingForTestUsageNowEndOfMessage\");\nconst std::vector<uint8_t> kOneHundredCharacterVec(\n    kOneHundredCharacterString.begin(), kOneHundredCharacterString.end());\n\nWriteFlags msgFlagsToWriteFlags(const int msg_flags) {\n  WriteFlags flags = WriteFlags::NONE;\n#ifdef MSG_MORE\n  if (msg_flags & MSG_MORE) {\n    flags = flags | WriteFlags::CORK;\n  }\n#endif // MSG_MORE\n\n#ifdef MSG_EOR\n  if (msg_flags & MSG_EOR) {\n    flags = flags | WriteFlags::EOR;\n  }\n#endif\n\n#ifdef MSG_ZEROCOPY\n  if (msg_flags & MSG_ZEROCOPY) {\n    flags = flags | WriteFlags::WRITE_MSG_ZEROCOPY;\n  }\n#endif\n  return flags;\n}\n\nWriteFlags getMsgAncillaryTsFlags(const struct msghdr& msg) {\n  const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n  if (!cmsg || cmsg->cmsg_level != SOL_SOCKET ||\n      cmsg->cmsg_type != SO_TIMESTAMPING ||\n      cmsg->cmsg_len != CMSG_LEN(sizeof(uint32_t))) {\n    return WriteFlags::NONE;\n  }\n\n  const uint32_t* sofFlags =\n      (reinterpret_cast<const uint32_t*>(CMSG_DATA(cmsg)));\n  WriteFlags flags = WriteFlags::NONE;\n  if (*sofFlags & folly::netops::SOF_TIMESTAMPING_TX_SCHED) {\n    flags = flags | WriteFlags::TIMESTAMP_SCHED;\n  }\n  if (*sofFlags & folly::netops::SOF_TIMESTAMPING_TX_SOFTWARE) {\n    flags = flags | WriteFlags::TIMESTAMP_TX;\n  }\n  if (*sofFlags & folly::netops::SOF_TIMESTAMPING_TX_ACK) {\n    flags = flags | WriteFlags::TIMESTAMP_ACK;\n  }\n\n  return flags;\n}\n\nWriteFlags getMsgAncillaryTsFlags(const struct msghdr* msg) {\n  return getMsgAncillaryTsFlags(*msg);\n}\n\nMATCHER_P(SendmsgMsghdrHasTotalIovLen, len, \"\") {\n  size_t iovLen = 0;\n  for (size_t i = 0; i < arg.msg_iovlen; i++) {\n    iovLen += arg.msg_iov[i].iov_len;\n  }\n  return len == iovLen;\n}\n\nMATCHER_P(SendmsgInvocHasTotalIovLen, len, \"\") {\n  size_t iovLen = 0;\n  for (const auto& iov : arg.iovs) {\n    iovLen += iov.iov_len;\n  }\n  return len == iovLen;\n}\n\nMATCHER_P(SendmsgInvocHasIovFirstByte, firstBytePtr, \"\") {\n  if (arg.iovs.empty()) {\n    return false;\n  }\n\n  const auto& firstIov = arg.iovs.front();\n  auto iovFirstBytePtr = const_cast<void*>(\n      static_cast<const void*>(reinterpret_cast<uint8_t*>(firstIov.iov_base)));\n  return firstBytePtr == iovFirstBytePtr;\n}\n\nMATCHER_P(SendmsgInvocHasIovLastByte, lastBytePtr, \"\") {\n  if (arg.iovs.empty()) {\n    return false;\n  }\n\n  const auto& lastIov = arg.iovs.back();\n  auto iovLastBytePtr = const_cast<void*>(static_cast<const void*>(\n      reinterpret_cast<uint8_t*>(lastIov.iov_base) + lastIov.iov_len - 1));\n  return lastBytePtr == iovLastBytePtr;\n}\n\nMATCHER_P(SendmsgInvocMsgFlagsEq, writeFlags, \"\") {\n  return writeFlags == arg.writeFlagsInMsgFlags;\n}\n\nMATCHER_P(SendmsgInvocAncillaryFlagsEq, writeFlags, \"\") {\n  return writeFlags == arg.writeFlagsInAncillary;\n}\n\nMATCHER_P2(ByteEventMatching, type, offset, \"\") {\n  if (type != arg.type || (size_t)offset != arg.offset) {\n    return false;\n  }\n  return true;\n}\n} // namespace\n\nclass DelayedWrite : public AsyncTimeout {\n public:\n  DelayedWrite(\n      const std::shared_ptr<AsyncSocket>& socket,\n      unique_ptr<IOBuf>&& bufs,\n      AsyncTransportWrapper::WriteCallback* wcb,\n      bool cork,\n      bool lastWrite = false)\n      : AsyncTimeout(socket->getEventBase()),\n        socket_(socket),\n        bufs_(std::move(bufs)),\n        wcb_(wcb),\n        cork_(cork),\n        lastWrite_(lastWrite) {}\n\n private:\n  void timeoutExpired() noexcept override {\n    WriteFlags flags = cork_ ? WriteFlags::CORK : WriteFlags::NONE;\n    socket_->writeChain(wcb_, std::move(bufs_), flags);\n    if (lastWrite_) {\n      socket_->shutdownWrite();\n    }\n  }\n\n  std::shared_ptr<AsyncSocket> socket_;\n  unique_ptr<IOBuf> bufs_;\n  AsyncTransportWrapper::WriteCallback* wcb_;\n  bool cork_;\n  bool lastWrite_;\n};\n\nenum class BackendType {\n  DEFAULT,\n  IO_URING,\n};\n\nenum class TFOState {\n  DISABLED,\n  ENABLED,\n};\n\nstd::vector<BackendType> getBackendTestingValues() {\n  std::vector<BackendType> vals;\n  vals.emplace_back(BackendType::DEFAULT);\n  vals.emplace_back(BackendType::IO_URING);\n  return vals;\n}\n\nusing ConnectTestParam = std::tuple<BackendType, TFOState>;\nstd::vector<ConnectTestParam> getBackendTFOTestingValues() {\n  std::vector<ConnectTestParam> vals;\n  vals.emplace_back(BackendType::DEFAULT, TFOState::DISABLED);\n  vals.emplace_back(BackendType::IO_URING, TFOState::DISABLED);\n\n#if FOLLY_ALLOW_TFO\n  vals.emplace_back(BackendType::DEFAULT, TFOState::ENABLED);\n  vals.emplace_back(BackendType::IO_URING, TFOState::ENABLED);\n#endif\n  return vals;\n}\n\n///////////////////////////////////////////////////////////////////////////\n// constructor related tests\n///////////////////////////////////////////////////////////////////////////\n\nclass AsyncSocketTest : public ::testing::TestWithParam<BackendType> {\n protected:\n  void SetUp() override {\n    if (GetParam() == BackendType::IO_URING) {\n      try {\n        evb_ =\n            std::make_unique<EventBase>(EventBase::Options{}.setBackendFactory(\n                []() -> std::unique_ptr<EventBaseBackendBase> {\n                  IoUringBackend::Options options;\n                  options.setInitialProvidedBuffers(2048, 2000);\n                  options.setNativeAsyncSocketSupport(true);\n                  return std::make_unique<IoUringBackend>(std::move(options));\n                }));\n      } catch (IoUringBackend::NotAvailable const&) {\n        GTEST_SKIP() << \"IoUringBackend not available\";\n      }\n    } else {\n      evb_ = std::make_unique<EventBase>();\n    }\n  }\n\n  EventBase& getEventBase() { return *evb_; }\n\n  std::unique_ptr<EventBase> makeEventBase() {\n    if (GetParam() == BackendType::IO_URING) {\n      return std::make_unique<EventBase>(EventBase::Options{}.setBackendFactory(\n          []() -> std::unique_ptr<EventBaseBackendBase> {\n            IoUringBackend::Options options;\n            options.setInitialProvidedBuffers(2048, 2000);\n            options.setNativeAsyncSocketSupport(true);\n            return std::make_unique<IoUringBackend>(std::move(options));\n          }));\n    } else {\n      return std::make_unique<EventBase>();\n    }\n  }\n\n private:\n  std::unique_ptr<EventBase> evb_;\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    AsyncSocketTests,\n    AsyncSocketTest,\n    ::testing::ValuesIn(getBackendTestingValues()),\n    [](const ::testing::TestParamInfo<BackendType>& info) {\n      return info.param == BackendType::IO_URING\n          ? \"IoUringBackend\"\n          : \"DefaultBackend\";\n    });\n\n/**\n * Test constructing with an existing fd.\n */\nTEST_P(AsyncSocketTest, ConstructWithFd) {\n  // construct a pair of unix sockets\n  NetworkSocket fds[2];\n  {\n    auto ret = netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);\n    EXPECT_EQ(0, ret);\n  }\n\n  // \"client\" socket\n  auto cfd = fds[0];\n  ASSERT_NE(cfd, NetworkSocket());\n\n  // instantiate AsyncSocket w/o any connectionEstablishTimestamp\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb, cfd));\n\n  // should be no connect timestamps\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectEndTime());\n\n  // should be no establish time, since not passed on construction\n  EXPECT_FALSE(socket->getConnectionEstablishTime().has_value());\n}\n\n/**\n * Test constructing with an existing fd, passing a connection establish ts.\n */\nTEST_P(AsyncSocketTest, ConstructWithFdAndTimestamp) {\n  // construct a pair of unix sockets\n  NetworkSocket fds[2];\n  {\n    auto ret = netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);\n    EXPECT_EQ(0, ret);\n  }\n\n  // \"client\" socket\n  auto cfd = fds[0];\n  ASSERT_NE(cfd, NetworkSocket());\n\n  // instantiate AsyncSocket w/ a connectionEstablishTimestamp\n  const auto connectionEstablishTime = std::chrono::steady_clock::now();\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(\n      new AsyncSocket(&evb, cfd, 0, nullptr, connectionEstablishTime));\n\n  // should be no connect timestamps\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectEndTime());\n\n  // should have connection establish time, as passed on construction\n  ASSERT_TRUE(socket->getConnectionEstablishTime().has_value());\n  EXPECT_EQ(\n      connectionEstablishTime, socket->getConnectionEstablishTime().value());\n}\n\n/**\n * Test constructing with an existing fd, then moving.\n */\nTEST_P(AsyncSocketTest, ConstructWithFdThenMove) {\n  // construct a pair of unix sockets\n  NetworkSocket fds[2];\n  {\n    auto ret = netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);\n    EXPECT_EQ(0, ret);\n  }\n\n  // \"client\" socket\n  auto cfd = fds[0];\n  ASSERT_NE(cfd, NetworkSocket());\n\n  // instantiate AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb, cfd));\n\n  // should be no connect timestamps\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectEndTime());\n\n  // should be no establish time, since not passed on construction\n  EXPECT_FALSE(socket->getConnectionEstablishTime().has_value());\n\n  // move the socket\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(std::move(socket)));\n\n  // should still be no connect timestamps\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket2->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket2->getConnectEndTime());\n\n  // should still be no establish time, since not passed on orig construction\n  EXPECT_FALSE(socket2->getConnectionEstablishTime().has_value());\n}\n\n/**\n * Test constructing with an existing fd, then moving.\n */\nTEST_P(AsyncSocketTest, ConstructWithFdAndTimestampThenMove) {\n  // construct a pair of unix sockets\n  NetworkSocket fds[2];\n  {\n    auto ret = netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);\n    EXPECT_EQ(0, ret);\n  }\n\n  // \"client\" socket\n  auto cfd = fds[0];\n  ASSERT_NE(cfd, NetworkSocket());\n\n  // instantiate AsyncSocket w/ a connectionEstablishTimestamp\n  const auto connectionEstablishTime = std::chrono::steady_clock::now();\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(\n      new AsyncSocket(&evb, cfd, 0, nullptr, connectionEstablishTime));\n\n  // should be no connect timestamps\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectEndTime());\n\n  // should have connection establish time, as passed on construction\n  ASSERT_TRUE(socket->getConnectionEstablishTime().has_value());\n  EXPECT_EQ(\n      connectionEstablishTime, socket->getConnectionEstablishTime().value());\n\n  // move the socket\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(std::move(socket)));\n\n  // should still be no connect timestamps\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket2->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket2->getConnectEndTime());\n\n  // should have connection  establish time, as passed on orig construction\n  ASSERT_TRUE(socket2->getConnectionEstablishTime().has_value());\n  EXPECT_EQ(\n      connectionEstablishTime, socket2->getConnectionEstablishTime().value());\n}\n\n///////////////////////////////////////////////////////////////////////////\n// connect() tests\n///////////////////////////////////////////////////////////////////////////\n\nclass AsyncSocketConnectTFOTest\n    : public ::testing::TestWithParam<ConnectTestParam> {\n protected:\n  void SetUp() override {\n    if (std::get<0>(GetParam()) == BackendType::IO_URING) {\n      try {\n        evb_ =\n            std::make_unique<EventBase>(EventBase::Options{}.setBackendFactory(\n                []() -> std::unique_ptr<EventBaseBackendBase> {\n                  IoUringBackend::Options options;\n                  options.setInitialProvidedBuffers(2048, 2000);\n                  options.setNativeAsyncSocketSupport(true);\n                  return std::make_unique<IoUringBackend>(std::move(options));\n                }));\n      } catch (IoUringBackend::NotAvailable const&) {\n        GTEST_SKIP() << \"IoUringBackend not available\";\n      }\n    } else {\n      evb_ = std::make_unique<EventBase>();\n    }\n  }\n\n  EventBase& getEventBase() { return *evb_; }\n\n  BackendType getBackendType() const { return std::get<0>(GetParam()); }\n  TFOState getTFOState() const { return std::get<1>(GetParam()); }\n\n private:\n  std::unique_ptr<EventBase> evb_;\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    ConnectTFOTests,\n    AsyncSocketConnectTFOTest,\n    ::testing::ValuesIn(getBackendTFOTestingValues()),\n    [](const ::testing::TestParamInfo<ConnectTestParam>& info) {\n      std::string name = std::get<0>(info.param) == BackendType::IO_URING\n          ? \"IoUringBackend\"\n          : \"DefaultBackend\";\n      name += std::get<1>(info.param) == TFOState::ENABLED\n          ? \"_TFOEnabled\"\n          : \"_TFODisabled\";\n      return name;\n    });\n\n/**\n * Test connecting to a server\n */\nTEST_P(AsyncSocketTest, Connect) {\n  // Start listening on a local port\n  TestServer server;\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectEndTime());\n  EXPECT_FALSE(socket->getConnectionEstablishTime().has_value());\n  ConnCallback cb;\n  const auto startedAt = std::chrono::steady_clock::now();\n  socket->connect(&cb, server.getAddress(), 30);\n\n  evb.loop();\n  const auto finishedAt = std::chrono::steady_clock::now();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(std::chrono::milliseconds(30), socket->getConnectTimeout());\n\n  EXPECT_GE(socket->getConnectStartTime(), startedAt);\n  EXPECT_LE(socket->getConnectStartTime(), socket->getConnectEndTime());\n  EXPECT_LE(socket->getConnectEndTime(), finishedAt);\n\n  // since connect() successful, the establish time == connect() end time\n  ASSERT_TRUE(socket->getConnectionEstablishTime().has_value());\n  EXPECT_EQ(\n      socket->getConnectEndTime(),\n      socket->getConnectionEstablishTime().value());\n}\n\nTEST_P(AsyncSocketTest, ConnectWithBoundFd) {\n  TestServer server;\n  EventBase& evb = getEventBase();\n  const auto& serverAddr = server.getAddress();\n  int sockfd = ::socket(serverAddr.getFamily(), SOCK_STREAM, 0);\n\n  uint16_t boundPort = 0;\n  for (uint16_t port = 1024; port <= 65535; port++) {\n    struct sockaddr_storage bindStorage{};\n    socklen_t bindLen;\n    if (serverAddr.getFamily() == AF_INET6) {\n      auto* sa = reinterpret_cast<struct sockaddr_in6*>(&bindStorage);\n      sa->sin6_family = AF_INET6;\n      sa->sin6_port = htons(port);\n      sa->sin6_addr = in6addr_any;\n      bindLen = sizeof(struct sockaddr_in6);\n    } else {\n      auto* sa = reinterpret_cast<struct sockaddr_in*>(&bindStorage);\n      sa->sin_family = AF_INET;\n      sa->sin_port = htons(port);\n      sa->sin_addr.s_addr = INADDR_ANY;\n      bindLen = sizeof(struct sockaddr_in);\n    }\n    if (::bind(sockfd, (struct sockaddr*)&bindStorage, bindLen) == 0) {\n      boundPort = port;\n      break;\n    }\n  }\n  if (boundPort == 0) {\n    GTEST_SKIP() << \"Unable to find a free port to bind() to\";\n  }\n\n  ConnCallback cb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  socket->connect(\n      &cb,\n      serverAddr,\n      30,\n      folly::emptySocketOptionMap,\n      folly::AsyncSocketTransport::BindOptions{folly::NetworkSocket(sockfd)});\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  folly::SocketAddress localAddr;\n  socket->getLocalAddress(&localAddr);\n  EXPECT_EQ(localAddr.getPort(), boundPort);\n}\n\nTEST_P(AsyncSocketTest, ConnectWithUnboundFd) {\n  TestServer server;\n  EventBase& evb = getEventBase();\n  const auto& serverAddr = server.getAddress();\n  int sockfd = ::socket(serverAddr.getFamily(), SOCK_STREAM, 0);\n  ASSERT_GE(sockfd, 0);\n\n  ConnCallback cb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  socket->connect(\n      &cb,\n      serverAddr,\n      30,\n      folly::emptySocketOptionMap,\n      folly::AsyncSocketTransport::BindOptions{folly::NetworkSocket(sockfd)});\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  folly::SocketAddress localAddr;\n  socket->getLocalAddress(&localAddr);\n  EXPECT_GT(localAddr.getPort(), 0);\n}\n\nTEST_P(AsyncSocketTest, ConnectWithAlreadyConnectedFd) {\n  TestServer server;\n  EventBase& evb = getEventBase();\n  const auto& serverAddr = server.getAddress();\n\n  int connectedFd = ::socket(serverAddr.getFamily(), SOCK_STREAM, 0);\n  ASSERT_GE(connectedFd, 0);\n  struct sockaddr_storage ss{};\n  serverAddr.getAddress(&ss);\n  ASSERT_EQ(\n      ::connect(connectedFd, (struct sockaddr*)&ss, serverAddr.getActualSize()),\n      0);\n\n  ConnCallback cb;\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  socket->connect(\n      &cb,\n      serverAddr,\n      30,\n      folly::emptySocketOptionMap,\n      folly::AsyncSocketTransport::BindOptions{\n          folly::NetworkSocket(connectedFd)});\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_FAILED);\n  EXPECT_EQ(cb.exception.getType(), AsyncSocketException::INVALID_STATE);\n  EXPECT_EQ(fcntl(connectedFd, F_GETFD), -1);\n  EXPECT_EQ(errno, EBADF);\n}\n\n/**\n * Test connecting to a server, then move the socket.¸\n */\nTEST_P(AsyncSocketTest, ConnectThenMove) {\n  // Start listening on a local port\n  TestServer server;\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectEndTime());\n  EXPECT_FALSE(socket->getConnectionEstablishTime().has_value());\n  ConnCallback cb;\n  const auto startedAt = std::chrono::steady_clock::now();\n  socket->connect(&cb, server.getAddress(), 30);\n\n  evb.loop();\n  const auto finishedAt = std::chrono::steady_clock::now();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(std::chrono::milliseconds(30), socket->getConnectTimeout());\n\n  EXPECT_GE(socket->getConnectStartTime(), startedAt);\n  EXPECT_LE(socket->getConnectStartTime(), socket->getConnectEndTime());\n  EXPECT_LE(socket->getConnectEndTime(), finishedAt);\n\n  // since connect() successful, the establish time == connect() end time\n  ASSERT_TRUE(socket->getConnectionEstablishTime().has_value());\n  EXPECT_EQ(\n      socket->getConnectEndTime(),\n      socket->getConnectionEstablishTime().value());\n\n  // store timings, then move the socket\n  const auto connectStartTime = socket->getConnectStartTime();\n  const auto connectEndTime = socket->getConnectEndTime();\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(std::move(socket)));\n\n  // timings should have been moved with the socket\n  EXPECT_EQ(connectStartTime, socket2->getConnectStartTime());\n  EXPECT_EQ(connectEndTime, socket2->getConnectEndTime());\n  ASSERT_TRUE(socket2->getConnectionEstablishTime().has_value());\n  EXPECT_EQ(connectEndTime, socket2->getConnectionEstablishTime().value());\n}\n\n/**\n * Test connecting to a server that isn't listening.\n */\nTEST_P(AsyncSocketTest, ConnectRefused) {\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectStartTime());\n  EXPECT_EQ(\n      std::chrono::steady_clock::time_point(), socket->getConnectEndTime());\n  EXPECT_FALSE(socket->getConnectionEstablishTime().has_value());\n\n  // Hopefully nothing is actually listening on this address\n  folly::SocketAddress addr(\"127.0.0.1\", 65535);\n  ConnCallback cb;\n  const auto startedAt = std::chrono::steady_clock::now();\n  socket->connect(&cb, addr, 30);\n\n  evb.loop();\n  const auto finishedAt = std::chrono::steady_clock::now();\n\n  EXPECT_EQ(STATE_FAILED, cb.state);\n  EXPECT_EQ(AsyncSocketException::NOT_OPEN, cb.exception.getType());\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(std::chrono::milliseconds(30), socket->getConnectTimeout());\n\n  EXPECT_GE(socket->getConnectStartTime(), startedAt);\n  EXPECT_LE(socket->getConnectStartTime(), socket->getConnectEndTime());\n  EXPECT_LE(socket->getConnectEndTime(), finishedAt);\n\n  // since connect() failed, the establish time is empty.\n  EXPECT_FALSE(socket->getConnectionEstablishTime().has_value());\n}\n\n/**\n * Test connection timeout\n */\nTEST_P(AsyncSocketTest, ConnectTimeout) {\n  EventBase& evb = getEventBase();\n\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  // Try connecting to server that won't respond.\n  //\n  // This depends somewhat on the network where this test is run.\n  // Hopefully this IP will be routable but unresponsive.\n  // (Alternatively, we could try listening on a local raw socket, but that\n  // normally requires root privileges.)\n  auto host = SocketAddressTestHelper::isIPv6Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv6\n      : SocketAddressTestHelper::isIPv4Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv4\n      : nullptr;\n  SocketAddress addr(host, 65535);\n  ConnCallback cb;\n  const auto startedAt = std::chrono::steady_clock::now();\n  socket->connect(&cb, addr, 1); // also set a ridiculously small timeout\n\n  evb.loop();\n  const auto finishedAt = std::chrono::steady_clock::now();\n\n  ASSERT_EQ(cb.state, STATE_FAILED);\n  if (cb.exception.getType() == AsyncSocketException::NOT_OPEN) {\n    // This can happen if we could not route to the IP address picked above.\n    // In this case the connect will fail immediately rather than timing out.\n    // Just skip the test in this case.\n    SKIP() << \"do not have a routable but unreachable IP address\";\n  }\n  ASSERT_EQ(cb.exception.getType(), AsyncSocketException::TIMED_OUT);\n\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(std::chrono::milliseconds(1), socket->getConnectTimeout());\n\n  EXPECT_GE(socket->getConnectStartTime(), startedAt);\n  EXPECT_LE(socket->getConnectStartTime(), socket->getConnectEndTime());\n  EXPECT_LE(socket->getConnectEndTime(), finishedAt);\n\n  // since connect() failed, the establish time is empty.\n  EXPECT_FALSE(socket->getConnectionEstablishTime().has_value());\n\n  // Verify that we can still get the peer address after a timeout.\n  // Use case is if the client was created from a client pool, and we want\n  // to log which peer failed.\n  folly::SocketAddress peer;\n  socket->getPeerAddress(&peer);\n  ASSERT_EQ(peer, addr);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(1));\n}\n\nclass AsyncSocketToSTest : public ::testing::TestWithParam<BackendType> {\n protected:\n  using MockDispatcher = ::testing::NiceMock<netops::test::MockDispatcher>;\n  using TestObserver = MockAsyncSocketLegacyLifecycleObserverForByteEvents;\n  using ByteEventType = AsyncSocket::ByteEvent::Type;\n\n  void SetUp() override {\n    if (GetParam() == BackendType::IO_URING) {\n      try {\n        evb =\n            std::make_unique<EventBase>(EventBase::Options{}.setBackendFactory(\n                []() -> std::unique_ptr<EventBaseBackendBase> {\n                  IoUringBackend::Options options;\n                  options.setNativeAsyncSocketSupport(true);\n                  return std::make_unique<IoUringBackend>(std::move(options));\n                }));\n      } catch (IoUringBackend::NotAvailable const&) {\n        GTEST_SKIP() << \"IoUringBackend not available\";\n      }\n    } else {\n      evb = std::make_unique<EventBase>();\n    }\n\n    netOpsDispatcher = std::make_shared<MockDispatcher>();\n    netOpsDispatcher->forwardToDefaultImpl();\n    socket = AsyncSocket::newSocket(evb.get());\n    socket->setOverrideNetOpsDispatcher(netOpsDispatcher);\n    v6Addr = SocketAddress(\n        SocketAddressTestHelper::kGooglePublicDnsAAddrIPv6, 65535);\n    v4Addr = SocketAddress(\n        SocketAddressTestHelper::kGooglePublicDnsAAddrIPv4, 65535);\n  }\n\n  void setupDefaultReturn() {\n    EXPECT_CALL(*netOpsDispatcher, setsockopt(_, _, _, _, _))\n        .Times(AnyNumber())\n        .WillRepeatedly(Return(0));\n  }\n\n  void expectNoCallsToTOS() {\n    EXPECT_CALL(*netOpsDispatcher, setsockopt(_, IPPROTO_IP, IP_TOS, _, _))\n        .Times(0);\n    EXPECT_CALL(\n        *netOpsDispatcher, setsockopt(_, IPPROTO_IPV6, IPV6_TCLASS, _, _))\n        .Times(0);\n  }\n\n  std::unique_ptr<EventBase> evb;\n  std::shared_ptr<AsyncSocket> socket;\n  std::shared_ptr<MockDispatcher> netOpsDispatcher;\n\n  SocketAddress v6Addr;\n  SocketAddress v4Addr;\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    ConnectToSTests,\n    AsyncSocketToSTest,\n    ::testing::ValuesIn(getBackendTestingValues()),\n    [](const ::testing::TestParamInfo<BackendType>& info) {\n      return info.param == BackendType::IO_URING\n          ? \"IoUringBackend\"\n          : \"DefaultBackend\";\n    });\n\nTEST_P(AsyncSocketToSTest, SetTosOrTrafficClassBeforeConnect) {\n  setupDefaultReturn();\n  int tos = 1;\n  socket->setTosOrTrafficClass(tos);\n\n  EXPECT_CALL(*netOpsDispatcher, setsockopt(_, IPPROTO_IPV6, IPV6_TCLASS, _, _))\n      .Times(1)\n      .WillOnce(Return(0));\n  socket->connect(nullptr, v6Addr, 1000);\n}\n\nTEST_P(AsyncSocketToSTest, SetTosOrTrafficClassAfterConnect) {\n  setupDefaultReturn();\n  int tos = 1;\n  expectNoCallsToTOS();\n  // EXPECT_CALL(*netOpsDispatcher, connect(_, _, _)).WillOnce(Return(0));\n  socket->connect(nullptr, v6Addr, 1000);\n  EXPECT_CALL(*netOpsDispatcher, setsockopt(_, IPPROTO_IPV6, IPV6_TCLASS, _, _))\n      .Times(1)\n      .WillOnce([]() { return 0; });\n  socket->setTosOrTrafficClass(tos);\n}\n\nTEST_P(AsyncSocketToSTest, SetTosOrTrafficClassIPV4) {\n  setupDefaultReturn();\n  int tos = 1;\n  socket->setTosOrTrafficClass(tos);\n\n  EXPECT_CALL(*netOpsDispatcher, setsockopt(_, IPPROTO_IP, IP_TOS, _, _))\n      .Times(1)\n      .WillOnce(Return(0));\n  socket->connect(nullptr, v4Addr, 1000);\n}\n\nTEST_P(AsyncSocketToSTest, SetTosOrTrafficClassError) {\n  setupDefaultReturn();\n  int tos = 1;\n  socket->setTosOrTrafficClass(tos);\n\n  EXPECT_CALL(*netOpsDispatcher, setsockopt(_, IPPROTO_IP, IP_TOS, _, _))\n      .Times(1)\n      .WillOnce([]() { return -1; });\n  ConnCallback ccb;\n  socket->connect(&ccb, v4Addr, 1000);\n  EXPECT_EQ(ccb.state, StateEnum::STATE_FAILED);\n}\n\n/**\n * Test writing immediately after connecting, without waiting for connect\n * to finish.\n */\nTEST_P(AsyncSocketConnectTFOTest, ConnectAndWrite) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // write()\n  char buf[128];\n  memset(buf, 'a', sizeof(buf));\n  WriteCallback wcb(true /*enableReleaseIOBufCallback*/);\n  // use writeChain so we can pass an IOBuf\n  socket->writeChain(&wcb, IOBuf::copyBuffer(buf, sizeof(buf)));\n\n  // Loop.  We don't bother accepting on the server socket yet.\n  // The kernel should be able to buffer the write request so it can succeed.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb.numIoBufCount, 1);\n  ASSERT_EQ(wcb.numIoBufBytes, sizeof(buf));\n\n  // Make sure the server got a connection and received the data\n  socket->close();\n  server.verifyConnection(buf, sizeof(buf));\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n  EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(30));\n}\n\n/**\n * Test connecting using a nullptr connect callback.\n */\nTEST_P(AsyncSocketConnectTFOTest, ConnectNullCallback) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n\n  socket->connect(nullptr, server.getAddress(), 30);\n\n  // write some data, just so we have some way of verifying\n  // that the socket works correctly after connecting\n  char buf[128];\n  memset(buf, 'a', sizeof(buf));\n  WriteCallback wcb;\n  socket->write(&wcb, buf, sizeof(buf));\n\n  evb.loop();\n\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n  // Make sure the server got a connection and received the data\n  socket->close();\n  server.verifyConnection(buf, sizeof(buf));\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test calling both write() and close() immediately after connecting, without\n * waiting for connect to finish.\n *\n * This exercises the STATE_CONNECTING_CLOSING code.\n */\nTEST_P(AsyncSocketConnectTFOTest, ConnectWriteAndClose) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // write()\n  char buf[128];\n  memset(buf, 'a', sizeof(buf));\n  WriteCallback wcb;\n  socket->write(&wcb, buf, sizeof(buf));\n\n  // close()\n  socket->close();\n\n  // Loop.  We don't bother accepting on the server socket yet.\n  // The kernel should be able to buffer the write request so it can succeed.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  // Make sure the server got a connection and received the data\n  server.verifyConnection(buf, sizeof(buf));\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test calling close() immediately after connect()\n */\nTEST_P(AsyncSocketTest, ConnectAndClose) {\n  TestServer server;\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the close-while-connecting code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; aborting test \"\n                 \"of close-during-connect behavior\";\n    return;\n  }\n\n  socket->close();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  // Make sure the connection was aborted\n  ASSERT_EQ(ccb.state, STATE_FAILED);\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test calling closeNow() immediately after connect()\n *\n * This should be identical to the normal close behavior.\n */\nTEST_P(AsyncSocketTest, ConnectAndCloseNow) {\n  TestServer server;\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the close-while-connecting code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; aborting test \"\n                 \"of closeNow()-during-connect behavior\";\n    return;\n  }\n\n  socket->closeNow();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  // Make sure the connection was aborted\n  ASSERT_EQ(ccb.state, STATE_FAILED);\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test calling both write() and closeNow() immediately after connecting,\n * without waiting for connect to finish.\n *\n * This should abort the pending write.\n */\nTEST_P(AsyncSocketTest, ConnectWriteAndCloseNow) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the close-while-connecting code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; aborting test \"\n                 \"of write-during-connect behavior\";\n    return;\n  }\n\n  // write()\n  char buf[128];\n  memset(buf, 'a', sizeof(buf));\n  WriteCallback wcb;\n  socket->write(&wcb, buf, sizeof(buf));\n\n  // close()\n  socket->closeNow();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_FAILED);\n  ASSERT_EQ(wcb.state, STATE_FAILED);\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test installing a read callback immediately, before connect() finishes.\n */\nTEST_P(AsyncSocketConnectTFOTest, ConnectAndRead) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  if (getTFOState() == TFOState::ENABLED) {\n    // Trigger a connection\n    socket->writeChain(nullptr, IOBuf::copyBuffer(\"hey\"));\n  }\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection and send data to it.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  acceptedSocket->write(buf, sizeof(buf));\n  acceptedSocket->flush();\n  acceptedSocket->close();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.buffers.size(), 1);\n  ASSERT_EQ(rcb.buffers[0].length, sizeof(buf));\n  ASSERT_EQ(memcmp(rcb.buffers[0].buffer, buf, sizeof(buf)), 0);\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\nTEST_P(AsyncSocketConnectTFOTest, ConnectAndReadZC) {\n  TestServer server;\n\n  // connect()\n  EventBase::Options opt;\n  opt.setBackendFactory([]() -> std::unique_ptr<folly::EventBaseBackendBase> {\n    return std::make_unique<TestEventBaseBackend>();\n  });\n  EventBase evb(std::move(opt));\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n  auto backend = dynamic_cast<TestEventBaseBackend*>(evb.getBackend());\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  ReadCallback rcb;\n  rcb.setReadMode(AsyncReader::ReadCallback::ReadMode::ReadZC);\n  socket->setReadCB(&rcb);\n  if (getTFOState() == TFOState::ENABLED) {\n    // Trigger a connection\n    socket->writeChain(nullptr, IOBuf::copyBuffer(\"hey\"));\n  }\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection and send data to it.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  uint8_t buf[128];\n  memset(buf, 'a', sizeof(buf));\n  acceptedSocket->write(buf, sizeof(buf));\n  acceptedSocket->flush();\n  acceptedSocket->close();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_TRUE(backend->queued);\n  // ReadZC is async and oneshot. To prevent level triggering of the socket\n  // while the request is issued async but not yet completed, the callback is\n  // uninstalled.\n  ASSERT_EQ(socket->getReadCallback(), nullptr);\n\n  // The real backend would call this on completion of the ReadZC request. But\n  // in the test we have to call it explicitly.\n  backend->recvZcCb(backend->bytes);\n  ASSERT_EQ(rcb.buffers.size(), 1);\n  ASSERT_EQ(rcb.buffers[0].length, sizeof(buf));\n  ASSERT_EQ(memcmp(rcb.buffers[0].buffer, buf, sizeof(buf)), 0);\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\nTEST_P(AsyncSocketConnectTFOTest, ConnectAndReadv) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  static constexpr size_t kBuffSize = 10;\n  static constexpr size_t kLen = 40;\n  static constexpr size_t kDataSize = 128;\n\n  ReadvCallback rcb(kBuffSize, kLen);\n  socket->setReadCB(&rcb);\n\n  if (getTFOState() == TFOState::ENABLED) {\n    // Trigger a connection\n    socket->writeChain(nullptr, IOBuf::copyBuffer(\"hey\"));\n  }\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection and send data to it.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  std::string data(kDataSize, 'A');\n  acceptedSocket->write(\n      reinterpret_cast<unsigned char*>(data.data()), data.size());\n  acceptedSocket->flush();\n  acceptedSocket->close();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  rcb.verifyData(data);\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\nTEST_P(AsyncSocketConnectTFOTest, ConnectAndZeroCopyRead) {\n  if (getTFOState() == TFOState::ENABLED && folly::kIsArchAArch64) {\n    GTEST_SKIP() << \"TFO and ZC Read has different alignments\";\n  }\n\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  static const size_t kBuffSize = sysconf(_SC_PAGESIZE);\n  static const size_t kPageMult = kBuffSize / 4096;\n  static const size_t kDataSize = kPageMult * 8 * 1024;\n  assert(kDataSize > kBuffSize);\n  assert(kDataSize % kBuffSize == 0);\n\n  static const size_t kNumEntries = 1024;\n  static const size_t kEntrySize = kPageMult * 32 * 1024;\n\n  auto memStore =\n      AsyncSocket::createDefaultZeroCopyMemStore(kNumEntries, kEntrySize);\n  ZeroCopyReadCallback rcb(memStore.get(), kBuffSize);\n  socket->setReadCB(&rcb);\n\n  if (getTFOState() == TFOState::ENABLED) {\n    // Trigger a connection\n    socket->writeChain(nullptr, IOBuf::copyBuffer(\"hey\"));\n  }\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection and send data to it.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  std::string data(kDataSize, ' ');\n  // generate random data\n  std::mt19937 rng(folly::randomNumberSeed());\n  for (size_t i = 0; i < data.size(); ++i) {\n    data[i] = static_cast<char>(rng());\n  }\n  auto ret = acceptedSocket->write(\n      reinterpret_cast<unsigned char*>(data.data()), data.size());\n  ASSERT_EQ(ret, data.size());\n  acceptedSocket->flush();\n  acceptedSocket->close();\n\n  // Loop\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  rcb.verifyData(data);\n  ASSERT_EQ(socket->getAppBytesReceived(), data.size());\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test installing a read callback and then closing immediately before the\n * connect attempt finishes.\n */\nTEST_P(AsyncSocketTest, ConnectReadAndClose) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the close-while-connecting code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; aborting test \"\n                 \"of read-during-connect behavior\";\n    return;\n  }\n\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  // close()\n  socket->close();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_FAILED); // we aborted the close attempt\n  ASSERT_EQ(rcb.buffers.size(), 0);\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED); // this indicates EOF\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test both writing and installing a read callback immediately,\n * before connect() finishes.\n */\nTEST_P(AsyncSocketConnectTFOTest, ConnectWriteAndRead) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  if (getTFOState() == TFOState::ENABLED) {\n    socket->enableTFO();\n  }\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // write()\n  char buf1[128];\n  memset(buf1, 'a', sizeof(buf1));\n  WriteCallback wcb;\n  socket->write(&wcb, buf1, sizeof(buf1));\n\n  // set a read callback\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection and send data to it.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  uint8_t buf2[128];\n  memset(buf2, 'b', sizeof(buf2));\n  acceptedSocket->write(buf2, sizeof(buf2));\n  acceptedSocket->flush();\n\n  // shut down the write half of acceptedSocket, so that the AsyncSocket\n  // will stop reading and we can break out of the event loop.\n  netops::shutdown(acceptedSocket->getNetworkSocket(), SHUT_WR);\n\n  // Loop\n  evb.loop();\n\n  // Make sure the connect succeeded\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  // Make sure the AsyncSocket read the data written by the accepted socket\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.buffers.size(), 1);\n  ASSERT_EQ(rcb.buffers[0].length, sizeof(buf2));\n  ASSERT_EQ(memcmp(rcb.buffers[0].buffer, buf2, sizeof(buf2)), 0);\n\n  // Close the AsyncSocket so we'll see EOF on acceptedSocket\n  socket->close();\n\n  // Make sure the accepted socket saw the data written by the AsyncSocket\n  uint8_t readbuf[sizeof(buf1)];\n  acceptedSocket->readAll(readbuf, sizeof(readbuf));\n  ASSERT_EQ(memcmp(buf1, readbuf, sizeof(buf1)), 0);\n  uint32_t bytesRead = acceptedSocket->read(readbuf, sizeof(readbuf));\n  ASSERT_EQ(bytesRead, 0);\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_TRUE(socket->isClosedByPeer());\n}\n\n/**\n * Test writing to the socket then shutting down writes before the connect\n * attempt finishes.\n */\nTEST_P(AsyncSocketTest, ConnectWriteAndShutdownWrite) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the write-while-connecting code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; skipping test\";\n    return;\n  }\n\n  // Ask to write some data\n  char wbuf[128];\n  memset(wbuf, 'a', sizeof(wbuf));\n  WriteCallback wcb;\n  socket->write(&wcb, wbuf, sizeof(wbuf));\n  socket->shutdownWrite();\n\n  // Shutdown writes\n  socket->shutdownWrite();\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n\n  // Since the connection is still in progress, there should be no data to\n  // read yet.  Verify that the accepted socket is not readable.\n  netops::PollDescriptor fds[1];\n  fds[0].fd = acceptedSocket->getNetworkSocket();\n  fds[0].events = POLLIN;\n  fds[0].revents = 0;\n  int rc = netops::poll(fds, 1, 0);\n  ASSERT_EQ(rc, 0);\n\n  // Write data to the accepted socket\n  uint8_t acceptedWbuf[192];\n  memset(acceptedWbuf, 'b', sizeof(acceptedWbuf));\n  acceptedSocket->write(acceptedWbuf, sizeof(acceptedWbuf));\n  acceptedSocket->flush();\n\n  // Loop\n  evb.loop();\n\n  // The loop should have completed the connection, written the queued data,\n  // and shutdown writes on the socket.\n  //\n  // Check that the connection was completed successfully and that the write\n  // callback succeeded.\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n  // Check that we can read the data that was written to the socket, and that\n  // we see an EOF, since its socket was half-shutdown.\n  uint8_t readbuf[sizeof(wbuf)];\n  acceptedSocket->readAll(readbuf, sizeof(readbuf));\n  ASSERT_EQ(memcmp(wbuf, readbuf, sizeof(wbuf)), 0);\n  uint32_t bytesRead = acceptedSocket->read(readbuf, sizeof(readbuf));\n  ASSERT_EQ(bytesRead, 0);\n\n  // Close the accepted socket.  This will cause it to see EOF\n  // and uninstall the read callback when we loop next.\n  acceptedSocket->close();\n\n  // Install a read callback, then loop again.\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n  evb.loop();\n\n  // This loop should have read the data and seen the EOF\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.buffers.size(), 1);\n  ASSERT_EQ(rcb.buffers[0].length, sizeof(acceptedWbuf));\n  ASSERT_EQ(\n      memcmp(rcb.buffers[0].buffer, acceptedWbuf, sizeof(acceptedWbuf)), 0);\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test reading, writing, and shutting down writes before the connect attempt\n * finishes.\n */\nTEST_P(AsyncSocketTest, ConnectReadWriteAndShutdownWrite) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the write-while-connecting code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; skipping test\";\n    return;\n  }\n\n  // Install a read callback\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  // Ask to write some data\n  char wbuf[128];\n  memset(wbuf, 'a', sizeof(wbuf));\n  WriteCallback wcb;\n  socket->write(&wcb, wbuf, sizeof(wbuf));\n\n  // Shutdown writes\n  socket->shutdownWrite();\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n\n  // Since the connection is still in progress, there should be no data to\n  // read yet.  Verify that the accepted socket is not readable.\n  netops::PollDescriptor fds[1];\n  fds[0].fd = acceptedSocket->getNetworkSocket();\n  fds[0].events = POLLIN;\n  fds[0].revents = 0;\n  int rc = netops::poll(fds, 1, 0);\n  ASSERT_EQ(rc, 0);\n\n  // Write data to the accepted socket\n  uint8_t acceptedWbuf[192];\n  memset(acceptedWbuf, 'b', sizeof(acceptedWbuf));\n  acceptedSocket->write(acceptedWbuf, sizeof(acceptedWbuf));\n  acceptedSocket->flush();\n  // Shutdown writes to the accepted socket.  This will cause it to see EOF\n  // and uninstall the read callback.\n  netops::shutdown(acceptedSocket->getNetworkSocket(), SHUT_WR);\n\n  // Loop\n  evb.loop();\n\n  // The loop should have completed the connection, written the queued data,\n  // shutdown writes on the socket, read the data we wrote to it, and see the\n  // EOF.\n  //\n  // Check that the connection was completed successfully and that the read\n  // and write callbacks were invoked as expected.\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.buffers.size(), 1);\n  ASSERT_EQ(rcb.buffers[0].length, sizeof(acceptedWbuf));\n  ASSERT_EQ(\n      memcmp(rcb.buffers[0].buffer, acceptedWbuf, sizeof(acceptedWbuf)), 0);\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n  // Check that we can read the data that was written to the socket, and that\n  // we see an EOF, since its socket was half-shutdown.\n  uint8_t readbuf[sizeof(wbuf)];\n  acceptedSocket->readAll(readbuf, sizeof(readbuf));\n  ASSERT_EQ(memcmp(wbuf, readbuf, sizeof(wbuf)), 0);\n  uint32_t bytesRead = acceptedSocket->read(readbuf, sizeof(readbuf));\n  ASSERT_EQ(bytesRead, 0);\n\n  // Fully close both sockets\n  acceptedSocket->close();\n  socket->close();\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_TRUE(socket->isClosedByPeer());\n}\n\n/**\n * Test reading, writing, and calling shutdownWriteNow() before the\n * connect attempt finishes.\n */\nTEST_P(AsyncSocketTest, ConnectReadWriteAndShutdownWriteNow) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the write-while-connecting code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; skipping test\";\n    return;\n  }\n\n  // Install a read callback\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  // Ask to write some data\n  char wbuf[128];\n  memset(wbuf, 'a', sizeof(wbuf));\n  WriteCallback wcb;\n  socket->write(&wcb, wbuf, sizeof(wbuf));\n\n  // Shutdown writes immediately.\n  // This should immediately discard the data that we just tried to write.\n  socket->shutdownWriteNow();\n\n  // Verify that writeError() was invoked on the write callback.\n  ASSERT_EQ(wcb.state, STATE_FAILED);\n  ASSERT_EQ(wcb.bytesWritten, 0);\n\n  // Even though we haven't looped yet, we should be able to accept\n  // the connection.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n\n  // Since the connection is still in progress, there should be no data to\n  // read yet.  Verify that the accepted socket is not readable.\n  netops::PollDescriptor fds[1];\n  fds[0].fd = acceptedSocket->getNetworkSocket();\n  fds[0].events = POLLIN;\n  fds[0].revents = 0;\n  int rc = netops::poll(fds, 1, 0);\n  ASSERT_EQ(rc, 0);\n\n  // Write data to the accepted socket\n  uint8_t acceptedWbuf[192];\n  memset(acceptedWbuf, 'b', sizeof(acceptedWbuf));\n  acceptedSocket->write(acceptedWbuf, sizeof(acceptedWbuf));\n  acceptedSocket->flush();\n  // Shutdown writes to the accepted socket.  This will cause it to see EOF\n  // and uninstall the read callback.\n  netops::shutdown(acceptedSocket->getNetworkSocket(), SHUT_WR);\n\n  // Loop\n  evb.loop();\n\n  // The loop should have completed the connection, written the queued data,\n  // shutdown writes on the socket, read the data we wrote to it, and see the\n  // EOF.\n  //\n  // Check that the connection was completed successfully and that the read\n  // callback was invoked as expected.\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.buffers.size(), 1);\n  ASSERT_EQ(rcb.buffers[0].length, sizeof(acceptedWbuf));\n  ASSERT_EQ(\n      memcmp(rcb.buffers[0].buffer, acceptedWbuf, sizeof(acceptedWbuf)), 0);\n\n  // Since we used shutdownWriteNow(), it should have discarded all pending\n  // write data.  Verify we see an immediate EOF when reading from the accepted\n  // socket.\n  uint8_t readbuf[sizeof(wbuf)];\n  uint32_t bytesRead = acceptedSocket->read(readbuf, sizeof(readbuf));\n  ASSERT_EQ(bytesRead, 0);\n\n  // Fully close both sockets\n  acceptedSocket->close();\n  socket->close();\n\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_TRUE(socket->isClosedByPeer());\n}\n\n// Helper function for use in testConnectOptWrite()\n// Temporarily disable the read callback\nvoid tmpDisableReads(AsyncSocket* socket, ReadCallback* rcb) {\n  // Uninstall the read callback\n  socket->setReadCB(nullptr);\n  // Schedule the read callback to be reinstalled after 1ms\n  socket->getEventBase()->runInLoop(\n      std::bind(&AsyncSocket::setReadCB, socket, rcb));\n}\n\n/**\n * Test connect+write, then have the connect callback perform another write.\n *\n * This tests interaction of the optimistic writing after connect with\n * additional write attempts that occur in the connect callback.\n */\nvoid testConnectOptWrite(size_t size1, size_t size2, bool close = false) {\n  TestServer server;\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  // connect()\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Hopefully the connect didn't succeed immediately.\n  // If it did, we can't exercise the optimistic write code path.\n  if (ccb.state == STATE_SUCCEEDED) {\n    LOG(INFO) << \"connect() succeeded immediately; aborting test \"\n                 \"of optimistic write behavior\";\n    return;\n  }\n\n  // Tell the connect callback to perform a write when the connect succeeds\n  WriteCallback wcb2;\n  std::unique_ptr<char[]> buf2(new char[size2]);\n  memset(buf2.get(), 'b', size2);\n  if (size2 > 0) {\n    ccb.successCallback = [&] { socket->write(&wcb2, buf2.get(), size2); };\n    // Tell the second write callback to close the connection when it is done\n    wcb2.successCallback = [&] { socket->closeNow(); };\n  }\n\n  // Schedule one write() immediately, before the connect finishes\n  std::unique_ptr<char[]> buf1(new char[size1]);\n  memset(buf1.get(), 'a', size1);\n  WriteCallback wcb1;\n  if (size1 > 0) {\n    socket->write(&wcb1, buf1.get(), size1);\n  }\n\n  if (close) {\n    // immediately perform a close, before connect() completes\n    socket->close();\n  }\n\n  // Start reading from the other endpoint after 10ms.\n  // If we're using large buffers, we have to read so that the writes don't\n  // block forever.\n  std::shared_ptr<AsyncSocket> acceptedSocket = server.acceptAsync(&evb);\n  ReadCallback rcb;\n  rcb.dataAvailableCallback =\n      std::bind(tmpDisableReads, acceptedSocket.get(), &rcb);\n  socket->getEventBase()->tryRunAfterDelay(\n      std::bind(&AsyncSocket::setReadCB, acceptedSocket.get(), &rcb), 10);\n\n  // Loop.  We don't bother accepting on the server socket yet.\n  // The kernel should be able to buffer the write request so it can succeed.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  if (size1 > 0) {\n    ASSERT_EQ(wcb1.state, STATE_SUCCEEDED);\n  }\n  if (size2 > 0) {\n    ASSERT_EQ(wcb2.state, STATE_SUCCEEDED);\n  }\n\n  socket->close();\n\n  // Make sure the read callback received all of the data\n  size_t bytesRead = 0;\n  for (const auto& buffer : rcb.buffers) {\n    size_t start = bytesRead;\n    bytesRead += buffer.length;\n    size_t end = bytesRead;\n    if (start < size1) {\n      size_t cmpLen = min(size1, end) - start;\n      ASSERT_EQ(memcmp(buffer.buffer, buf1.get() + start, cmpLen), 0);\n    }\n    if (end > size1 && end <= size1 + size2) {\n      size_t itOffset;\n      size_t buf2Offset;\n      size_t cmpLen;\n      if (start >= size1) {\n        itOffset = 0;\n        buf2Offset = start - size1;\n        cmpLen = end - start;\n      } else {\n        itOffset = size1 - start;\n        buf2Offset = 0;\n        cmpLen = end - size1;\n      }\n      ASSERT_EQ(\n          memcmp(buffer.buffer + itOffset, buf2.get() + buf2Offset, cmpLen), 0);\n    }\n  }\n  ASSERT_EQ(bytesRead, size1 + size2);\n}\n\nTEST(AsyncSocketTest, ConnectCallbackWrite) {\n  // Test using small writes that should both succeed immediately\n  testConnectOptWrite(100, 200);\n\n  // Test using a large buffer in the connect callback, that should block\n  const size_t largeSize = 32 * 1024 * 1024;\n  testConnectOptWrite(100, largeSize);\n\n  // Test using a large initial write\n  testConnectOptWrite(largeSize, 100);\n\n  // Test using two large buffers\n  testConnectOptWrite(largeSize, largeSize);\n\n  // Test a small write in the connect callback,\n  // but no immediate write before connect completes\n  testConnectOptWrite(0, 64);\n\n  // Test a large write in the connect callback,\n  // but no immediate write before connect completes\n  testConnectOptWrite(0, largeSize);\n\n  // Test connect, a small write, then immediately call close() before connect\n  // completes\n  testConnectOptWrite(211, 0, true);\n\n  // Test connect, a large immediate write (that will block), then immediately\n  // call close() before connect completes\n  testConnectOptWrite(largeSize, 0, true);\n}\n\n///////////////////////////////////////////////////////////////////////////\n// write() related tests\n///////////////////////////////////////////////////////////////////////////\n\n/**\n * Test writing using a nullptr callback\n */\nTEST_P(AsyncSocketTest, WriteNullCallback) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket =\n      AsyncSocket::newSocket(&evb, server.getAddress(), 30);\n  evb.loop(); // loop until the socket is connected\n\n  // write() with a nullptr callback\n  char buf[128];\n  memset(buf, 'a', sizeof(buf));\n  socket->write(nullptr, buf, sizeof(buf));\n\n  evb.loop(); // loop until the data is sent\n\n  // Make sure the server got a connection and received the data\n  socket->close();\n  server.verifyConnection(buf, sizeof(buf));\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test writing with a send timeout\n */\nTEST_P(AsyncSocketTest, WriteTimeout) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket =\n      AsyncSocket::newSocket(&evb, server.getAddress(), 30);\n  evb.loop(); // loop until the socket is connected\n\n  // write() a large chunk of data, with no-one on the other end reading.\n  // Tricky: the kernel caches the connection metrics for recently-used\n  // routes (see tcp_no_metrics_save) so a freshly opened connection can\n  // have a send buffer size bigger than wmem_default.  This makes the test\n  // flaky on contbuild if writeLength is < wmem_max (20M on our systems).\n  size_t writeLength = 32 * 1024 * 1024;\n  uint32_t timeout = 200;\n  socket->setSendTimeout(timeout);\n  std::unique_ptr<char[]> buf(new char[writeLength]);\n  memset(buf.get(), 'a', writeLength);\n  WriteCallback wcb;\n  socket->write(&wcb, buf.get(), writeLength);\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  // Make sure the write attempt timed out as requested\n  ASSERT_EQ(wcb.state, STATE_FAILED);\n  ASSERT_EQ(wcb.exception.getType(), AsyncSocketException::TIMED_OUT);\n\n  // Check that the write timed out within a reasonable period of time.\n  // We don't check for exactly the specified timeout, since AsyncSocket only\n  // times out when it hasn't made progress for that period of time.\n  //\n  // On linux, the first write sends a few hundred kb of data, then blocks for\n  // writability, and then unblocks again after 40ms and is able to write\n  // another smaller of data before blocking permanently.  Therefore it doesn't\n  // time out until 40ms + timeout.\n  //\n  // I haven't fully verified the cause of this, but I believe it probably\n  // occurs because the receiving end delays sending an ack for up to 40ms.\n  // (This is the default value for TCP_DELACK_MIN.)  Once the sender receives\n  // the ack, it can send some more data.  However, after that point the\n  // receiver's kernel buffer is full.  This 40ms delay happens even with\n  // TCP_NODELAY and TCP_QUICKACK enabled on both endpoints.  However, the\n  // kernel may be automatically disabling TCP_QUICKACK after receiving some\n  // data.\n  //\n  // For now, we simply check that the timeout occurred within 160ms of\n  // the requested value.\n  T_CHECK_TIMEOUT(start, end, milliseconds(timeout), milliseconds(160));\n}\n\n/**\n * Test getting local and peer addresses with no fd.\n *\n * Value returned should be empty; no failure should occur.\n */\nTEST_P(AsyncSocketTest, GetAddressesNoFd) {\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::newSocket(&evb);\n\n  {\n    folly::SocketAddress address;\n    socket->getLocalAddress(&address);\n    EXPECT_TRUE(address.empty());\n  }\n\n  {\n    folly::SocketAddress address;\n    socket->getPeerAddress(&address);\n    EXPECT_TRUE(address.empty());\n  }\n}\n\n/**\n * Test getting local and peer addresses after connecting.\n */\nTEST_P(AsyncSocketTest, GetAddressesAfterConnectGetwhileopenandonclose) {\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::newSocket(&evb);\n\n  // Start listening on a local port\n  TestServer server;\n\n  // Connect\n  {\n    ConnCallback cb;\n    socket->connect(&cb, server.getAddress(), 30);\n    evb.loop();\n    ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  }\n\n  // Get local, make sure it's not empty and not equal to server\n  const folly::SocketAddress localAddress = [&socket]() {\n    folly::SocketAddress address;\n    socket->getLocalAddress(&address);\n    return address;\n  }();\n  EXPECT_FALSE(localAddress.empty());\n  EXPECT_NE(server.getAddress(), localAddress);\n\n  const folly::SocketAddress peerAddress = [&socket]() {\n    folly::SocketAddress address;\n    socket->getPeerAddress(&address);\n    return address;\n  }();\n  EXPECT_FALSE(peerAddress.empty());\n  EXPECT_EQ(server.getAddress(), peerAddress);\n\n  // Close\n  socket->closeNow();\n\n  // Addresses should still be available as they're cached\n  const folly::SocketAddress localAddress2 = [&socket]() {\n    folly::SocketAddress address;\n    socket->getLocalAddress(&address);\n    return address;\n  }();\n  EXPECT_EQ(localAddress2, localAddress);\n\n  const folly::SocketAddress peerAddress2 = [&socket]() {\n    folly::SocketAddress address;\n    socket->getPeerAddress(&address);\n    return address;\n  }();\n  EXPECT_EQ(peerAddress2, peerAddress);\n}\n\n/**\n * Test getting local and peer addresses after closing.\n *\n * Only peer address is available under these conditions.\n */\nTEST_P(AsyncSocketTest, GetAddressesAfterConnectGetonlyafterclose) {\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::newSocket(&evb);\n\n  // Start listening on a local port\n  TestServer server;\n\n  // Connect\n  {\n    ConnCallback cb;\n    socket->connect(&cb, server.getAddress(), 30);\n    evb.loop();\n    ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  }\n\n  // Close\n  socket->closeNow();\n\n  // Local address unavailable since never fetched\n  {\n    folly::SocketAddress address;\n    socket->getLocalAddress(&address);\n    EXPECT_TRUE(address.empty());\n  }\n\n  // Peer address available since it was passed to connect()\n  {\n    folly::SocketAddress address;\n    socket->getPeerAddress(&address);\n    EXPECT_FALSE(address.empty());\n    EXPECT_EQ(server.getAddress(), address);\n  }\n}\n\n/**\n * Test getting local and peer addresses after connecting.\n */\nTEST_P(AsyncSocketTest, GetAddressesAfterInitFromFdGetoninitandonclose) {\n  if (GetParam() == BackendType::IO_URING) {\n    GTEST_SKIP() << \"io_uring does not support detachNetworkSocket()\";\n  }\n  EventBase& evb = getEventBase();\n\n  // Start listening on a local port\n  TestServer server;\n\n  // Create a socket, connect, then create another AsyncSocket from just fd\n  auto socket = [&server, &evb]() {\n    auto socket1 = AsyncSocket::newSocket(&evb);\n    ConnCallback cb;\n    socket1->connect(&cb, server.getAddress(), 30);\n    evb.loop();\n    return AsyncSocket::newSocket(&evb, socket1->detachNetworkSocket());\n  }();\n\n  // Get local, make sure it's not empty and not equal to server\n  const folly::SocketAddress localAddress = [&socket]() {\n    folly::SocketAddress address;\n    socket->getLocalAddress(&address);\n    return address;\n  }();\n  EXPECT_FALSE(localAddress.empty());\n  EXPECT_NE(server.getAddress(), localAddress);\n\n  const folly::SocketAddress peerAddress = [&socket]() {\n    folly::SocketAddress address;\n    socket->getPeerAddress(&address);\n    return address;\n  }();\n  EXPECT_FALSE(peerAddress.empty());\n  EXPECT_EQ(server.getAddress(), peerAddress);\n\n  // Close\n  socket->closeNow();\n\n  // Addresses should still be available as they're cached\n  const folly::SocketAddress localAddress2 = [&socket]() {\n    folly::SocketAddress address;\n    socket->getLocalAddress(&address);\n    return address;\n  }();\n  EXPECT_EQ(localAddress2, localAddress);\n\n  const folly::SocketAddress peerAddress2 = [&socket]() {\n    folly::SocketAddress address;\n    socket->getPeerAddress(&address);\n    return address;\n  }();\n  EXPECT_EQ(peerAddress2, peerAddress);\n}\n\n/**\n * Test writing to a socket that the remote endpoint has closed\n */\nTEST_P(AsyncSocketTest, WritePipeError) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket =\n      AsyncSocket::newSocket(&evb, server.getAddress(), 30);\n  socket->setSendTimeout(1000);\n  evb.loop(); // loop until the socket is connected\n\n  // accept and immediately close the socket\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  acceptedSocket->close();\n\n  // write() a large chunk of data\n  size_t writeLength = 32 * 1024 * 1024;\n  std::unique_ptr<char[]> buf(new char[writeLength]);\n  memset(buf.get(), 'a', writeLength);\n  WriteCallback wcb;\n  socket->write(&wcb, buf.get(), writeLength);\n\n  evb.loop();\n\n  // Make sure the write failed.\n  // It would be nice if AsyncSocketException could convey the errno value,\n  // so that we could check for EPIPE\n  ASSERT_EQ(wcb.state, STATE_FAILED);\n  ASSERT_EQ(wcb.exception.getType(), AsyncSocketException::INTERNAL_ERROR);\n  if (GetParam() == BackendType::DEFAULT) {\n    ASSERT_THAT(\n        wcb.exception.what(),\n        MatchesRegex(\n            kIsMobile\n                ? \"AsyncSocketException: writev\\\\(\\\\) failed \\\\(peer=.+\\\\), type = Internal error, errno = .+ \\\\(Broken pipe\\\\)\"\n                : \"AsyncSocketException: writev\\\\(\\\\) failed \\\\(peer=.+, local=.+\\\\), type = Internal error, errno = .+ \\\\(Broken pipe\\\\)\"));\n  } else {\n    ASSERT_THAT(\n        wcb.exception.what(),\n        MatchesRegex(\n            \"AsyncSocketException: async sendmsg\\\\(\\\\) failed \\\\(peer=.+\\\\), type = Internal error, errno = .+ \\\\(Broken pipe\\\\)\"));\n  }\n  ASSERT_FALSE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test writing to a socket that has its read side closed\n */\nTEST_P(AsyncSocketTest, WriteAfterReadEOF) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket =\n      AsyncSocket::newSocket(&evb, server.getAddress(), 30);\n  evb.loop(); // loop until the socket is connected\n\n  // Accept the connection\n  std::shared_ptr<AsyncSocket> acceptedSocket = server.acceptAsync(&evb);\n  ReadCallback rcb;\n  acceptedSocket->setReadCB(&rcb);\n\n  // Shutdown the write side of client socket (read side of server socket)\n  socket->shutdownWrite();\n  evb.loop();\n\n  // Check that accepted socket is still writable\n  ASSERT_FALSE(acceptedSocket->good());\n  ASSERT_TRUE(acceptedSocket->writable());\n\n  // Write data to accepted socket\n  constexpr size_t simpleBufLength = 5;\n  char simpleBuf[simpleBufLength];\n  memset(simpleBuf, 'a', simpleBufLength);\n  WriteCallback wcb;\n  acceptedSocket->write(&wcb, simpleBuf, simpleBufLength);\n  evb.loop();\n\n  // Make sure we were able to write even after getting a read EOF\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED); // this indicates EOF\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n}\n\n/**\n * Test that bytes written is correctly computed in case of write failure\n */\nTEST_P(AsyncSocketTest, WriteErrorCallbackBytesWritten) {\n  // Send and receive buffer sizes for the sockets.\n  // Note that Linux will double this value to allow space for bookkeeping\n  // overhead.\n  constexpr size_t kSockBufSize = 8 * 1024;\n  constexpr size_t kEffectiveSockBufSize = 2 * kSockBufSize;\n\n  TestServer server(false, kSockBufSize);\n\n  SocketOptionMap options{\n      {{SOL_SOCKET, SO_SNDBUF}, int(kSockBufSize)},\n      {{SOL_SOCKET, SO_RCVBUF}, int(kSockBufSize)},\n      {{IPPROTO_TCP, TCP_NODELAY}, 1},\n  };\n\n  // The current thread will be used by the receiver - use a separate thread\n  // for the sender.\n  EventBase& senderEvb = getEventBase();\n  std::thread senderThread([&]() { senderEvb.loopForever(); });\n\n  ConnCallback ccb;\n  WriteCallback wcb;\n  std::shared_ptr<AsyncSocket> socket;\n\n  senderEvb.runInEventBaseThreadAndWait([&]() {\n    socket = AsyncSocket::newSocket(&senderEvb);\n    socket->connect(&ccb, server.getAddress(), 30, options);\n  });\n\n  // accept the socket on the server side\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n\n  // Send a big (100KB) write so that it is partially written.\n  constexpr size_t kSendSize = 100 * 1024;\n  auto const sendBuf = std::vector<char>(kSendSize, 'a');\n\n  senderEvb.runInEventBaseThreadAndWait([&]() {\n    socket->write(&wcb, sendBuf.data(), kSendSize);\n  });\n\n  // Read 20KB of data from the socket to allow the sender to send a bit more\n  // data after it initially blocks.\n  constexpr size_t kRecvSize = 20 * 1024;\n  uint8_t recvBuf[kRecvSize];\n  auto bytesRead = acceptedSocket->readAll(recvBuf, sizeof(recvBuf));\n  ASSERT_EQ(kRecvSize, bytesRead);\n  EXPECT_EQ(0, memcmp(recvBuf, sendBuf.data(), bytesRead));\n\n  // We should be able to send at least the amount of data received plus the\n  // send buffer size.  In practice we should probably be able to send\n  constexpr size_t kMinExpectedBytesWritten = kRecvSize + kSockBufSize;\n\n  // We shouldn't be able to send more than the amount of data received plus\n  // the send buffer size of the sending socket (kEffectiveSockBufSize) plus\n  // the receive buffer size on the receiving socket (kEffectiveSockBufSize)\n  constexpr size_t kMaxExpectedBytesWritten =\n      kRecvSize + kEffectiveSockBufSize + kEffectiveSockBufSize;\n  static_assert(\n      kMaxExpectedBytesWritten < kSendSize, \"kSendSize set too small\");\n\n  // Need to delay after receiving 20KB and before closing the receive side so\n  // that the send side has a chance to fill the send buffer past.\n  using clock = std::chrono::steady_clock;\n  auto const deadline = clock::now() + std::chrono::seconds(2);\n  while (\n      wcb.bytesWritten < kMinExpectedBytesWritten && clock::now() < deadline) {\n    std::this_thread::yield();\n  }\n  acceptedSocket->closeWithReset();\n\n  senderEvb.terminateLoopSoon();\n  senderThread.join();\n  socket.reset();\n\n  ASSERT_EQ(STATE_FAILED, wcb.state);\n  ASSERT_LE(kMinExpectedBytesWritten, wcb.bytesWritten);\n  ASSERT_GE(kMaxExpectedBytesWritten, wcb.bytesWritten);\n}\n\n/**\n * Test writing a mix of simple buffers and IOBufs\n */\nTEST_P(AsyncSocketTest, WriteIOBuf) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Accept the connection\n  std::shared_ptr<AsyncSocket> acceptedSocket = server.acceptAsync(&evb);\n  ReadCallback rcb;\n  acceptedSocket->setReadCB(&rcb);\n\n  // Check if EOR tracking flag can be set and reset.\n  EXPECT_FALSE(socket->isEorTrackingEnabled());\n  socket->setEorTracking(true);\n  EXPECT_TRUE(socket->isEorTrackingEnabled());\n  socket->setEorTracking(false);\n  EXPECT_FALSE(socket->isEorTrackingEnabled());\n\n  // Write a simple buffer to the socket\n  constexpr size_t simpleBufLength = 5;\n  char simpleBuf[simpleBufLength];\n  memset(simpleBuf, 'a', simpleBufLength);\n  WriteCallback wcb;\n  socket->write(&wcb, simpleBuf, simpleBufLength);\n\n  // Write a single-element IOBuf chain\n  size_t buf1Length = 7;\n  unique_ptr<IOBuf> buf1(IOBuf::create(buf1Length));\n  memset(buf1->writableData(), 'b', buf1Length);\n  buf1->append(buf1Length);\n  unique_ptr<IOBuf> buf1Copy(buf1->clone());\n  WriteCallback wcb2;\n  socket->writeChain(&wcb2, std::move(buf1));\n\n  // Write a multiple-element IOBuf chain\n  size_t buf2Length = 11;\n  unique_ptr<IOBuf> buf2(IOBuf::create(buf2Length));\n  memset(buf2->writableData(), 'c', buf2Length);\n  buf2->append(buf2Length);\n  size_t buf3Length = 13;\n  unique_ptr<IOBuf> buf3(IOBuf::create(buf3Length));\n  memset(buf3->writableData(), 'd', buf3Length);\n  buf3->append(buf3Length);\n  buf2->appendToChain(std::move(buf3));\n  unique_ptr<IOBuf> buf2Copy(buf2->clone());\n  buf2Copy->coalesce();\n  WriteCallback wcb3;\n  socket->writeChain(&wcb3, std::move(buf2));\n  socket->shutdownWrite();\n\n  // Let the reads and writes run to completion\n  evb.loop();\n\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb2.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb3.state, STATE_SUCCEEDED);\n\n  // Make sure the reader got the right data in the right order\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED);\n  rcb.coalesce();\n  ASSERT_EQ(rcb.buffers.size(), 1);\n  ASSERT_EQ(\n      rcb.buffers[0].length,\n      simpleBufLength + buf1Length + buf2Length + buf3Length);\n  ASSERT_EQ(memcmp(rcb.buffers[0].buffer, simpleBuf, simpleBufLength), 0);\n  ASSERT_EQ(\n      memcmp(\n          rcb.buffers[0].buffer + simpleBufLength,\n          buf1Copy->data(),\n          buf1Copy->length()),\n      0);\n  ASSERT_EQ(\n      memcmp(\n          rcb.buffers[0].buffer + simpleBufLength + buf1Length,\n          buf2Copy->data(),\n          buf2Copy->length()),\n      0);\n\n  acceptedSocket->close();\n  socket->close();\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\nTEST_P(AsyncSocketTest, WriteIOBufCorked) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Accept the connection\n  std::shared_ptr<AsyncSocket> acceptedSocket = server.acceptAsync(&evb);\n  ReadCallback rcb;\n  acceptedSocket->setReadCB(&rcb);\n\n  // Do three writes, 100ms apart, with the \"cork\" flag set\n  // on the second write.  The reader should see the first write\n  // arrive by itself, followed by the second and third writes\n  // arriving together.\n  size_t buf1Length = 5;\n  unique_ptr<IOBuf> buf1(IOBuf::create(buf1Length));\n  memset(buf1->writableData(), 'a', buf1Length);\n  buf1->append(buf1Length);\n  size_t buf2Length = 7;\n  unique_ptr<IOBuf> buf2(IOBuf::create(buf2Length));\n  memset(buf2->writableData(), 'b', buf2Length);\n  buf2->append(buf2Length);\n  size_t buf3Length = 11;\n  unique_ptr<IOBuf> buf3(IOBuf::create(buf3Length));\n  memset(buf3->writableData(), 'c', buf3Length);\n  buf3->append(buf3Length);\n  WriteCallback wcb1;\n  socket->writeChain(&wcb1, std::move(buf1));\n  WriteCallback wcb2;\n  DelayedWrite write2(socket, std::move(buf2), &wcb2, true);\n  write2.scheduleTimeout(100);\n  WriteCallback wcb3;\n  DelayedWrite write3(socket, std::move(buf3), &wcb3, false, true);\n  write3.scheduleTimeout(140);\n\n  evb.loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb1.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb2.state, STATE_SUCCEEDED);\n  if (wcb3.state != STATE_SUCCEEDED) {\n    throw(wcb3.exception);\n  }\n  ASSERT_EQ(wcb3.state, STATE_SUCCEEDED);\n\n  // Make sure the reader got the data with the right grouping\n  ASSERT_EQ(rcb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(rcb.buffers.size(), 2);\n  ASSERT_EQ(rcb.buffers[0].length, buf1Length);\n  ASSERT_EQ(rcb.buffers[1].length, buf2Length + buf3Length);\n\n  acceptedSocket->close();\n  socket->close();\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test performing a zero-length write\n */\nTEST_P(AsyncSocketTest, ZeroLengthWrite) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket =\n      AsyncSocket::newSocket(&evb, server.getAddress(), 30);\n  evb.loop(); // loop until the socket is connected\n\n  auto acceptedSocket = server.acceptAsync(&evb);\n  ReadCallback rcb;\n  acceptedSocket->setReadCB(&rcb);\n\n  size_t len1 = 1024 * 1024;\n  size_t len2 = 1024 * 1024;\n  std::unique_ptr<char[]> buf(new char[len1 + len2]);\n  memset(buf.get(), 'a', len1);\n  memset(buf.get() + len1, 'b', len2);\n\n  WriteCallback wcb1;\n  WriteCallback wcb2;\n  WriteCallback wcb3;\n  WriteCallback wcb4;\n  socket->write(&wcb1, buf.get(), 0);\n  socket->write(&wcb2, buf.get(), len1);\n  socket->write(&wcb3, buf.get() + len1, 0);\n  socket->write(&wcb4, buf.get() + len1, len2);\n  socket->close();\n\n  evb.loop(); // loop until the data is sent\n\n  ASSERT_EQ(wcb1.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb2.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb3.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb4.state, STATE_SUCCEEDED);\n  rcb.verifyData(buf.get(), len1 + len2);\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\nTEST_P(AsyncSocketTest, ZeroLengthWritev) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket =\n      AsyncSocket::newSocket(&evb, server.getAddress(), 30);\n  evb.loop(); // loop until the socket is connected\n\n  auto acceptedSocket = server.acceptAsync(&evb);\n  ReadCallback rcb;\n  acceptedSocket->setReadCB(&rcb);\n\n  size_t len1 = 1024 * 1024;\n  size_t len2 = 1024 * 1024;\n  std::unique_ptr<char[]> buf(new char[len1 + len2]);\n  memset(buf.get(), 'a', len1);\n  memset(buf.get(), 'b', len2);\n\n  WriteCallback wcb;\n  constexpr size_t iovCount = 4;\n  struct iovec iov[iovCount];\n  iov[0].iov_base = buf.get();\n  iov[0].iov_len = len1;\n  iov[1].iov_base = buf.get() + len1;\n  iov[1].iov_len = 0;\n  iov[2].iov_base = buf.get() + len1;\n  iov[2].iov_len = len2;\n  iov[3].iov_base = buf.get() + len1 + len2;\n  iov[3].iov_len = 0;\n\n  socket->writev(&wcb, iov, iovCount);\n  socket->close();\n  evb.loop(); // loop until the data is sent\n\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  rcb.verifyData(buf.get(), len1 + len2);\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n///////////////////////////////////////////////////////////////////////////\n// close() related tests\n///////////////////////////////////////////////////////////////////////////\n\n/**\n * Test calling close() with pending writes when the socket is already closing.\n */\nTEST_P(AsyncSocketTest, ClosePendingWritesWhileClosing) {\n  TestServer server;\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // accept the socket on the server side\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n\n  // Loop to ensure the connect has completed\n  evb.loop();\n\n  // Make sure we are connected\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  // Schedule pending writes, until several write attempts have blocked\n  char buf[128];\n  memset(buf, 'a', sizeof(buf));\n  using WriteCallbackVector = vector<std::shared_ptr<WriteCallback>>;\n  WriteCallbackVector writeCallbacks;\n\n  writeCallbacks.reserve(5);\n  while (writeCallbacks.size() < 5) {\n    std::shared_ptr<WriteCallback> wcb(new WriteCallback);\n\n    socket->write(wcb.get(), buf, sizeof(buf));\n    if (wcb->state == STATE_SUCCEEDED) {\n      // Succeeded immediately.  Keep performing more writes\n      continue;\n    }\n\n    // This write is blocked.\n    // Have the write callback call close() when writeError() is invoked\n    wcb->errorCallback = std::bind(&AsyncSocket::close, socket.get());\n    writeCallbacks.push_back(wcb);\n  }\n\n  // Call closeNow() to immediately fail the pending writes\n  socket->closeNow();\n\n  // Make sure writeError() was invoked on all of the pending write callbacks\n  for (const auto& writeCallback : writeCallbacks) {\n    ASSERT_EQ((writeCallback)->state, STATE_FAILED);\n  }\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n///////////////////////////////////////////////////////////////////////////\n// ImmediateRead related tests\n///////////////////////////////////////////////////////////////////////////\n\n/* AsyncSocket use to verify immediate read works */\nclass AsyncSocketImmediateRead : public folly::AsyncSocket {\n public:\n  bool immediateReadCalled = false;\n  explicit AsyncSocketImmediateRead(folly::EventBase* evb) : AsyncSocket(evb) {}\n\n protected:\n  void checkForImmediateRead() noexcept override {\n    immediateReadCalled = true;\n    AsyncSocket::handleRead();\n  }\n};\n\nTEST(AsyncSocket, ConnectReadImmediateRead) {\n  TestServer server;\n\n  const size_t maxBufferSz = 100;\n  const size_t maxReadsPerEvent = 1;\n  const size_t expectedDataSz = maxBufferSz * 3;\n  char expectedData[expectedDataSz];\n  memset(expectedData, 'j', expectedDataSz);\n\n  EventBase evb;\n  ReadCallback rcb(maxBufferSz);\n  AsyncSocketImmediateRead socket(&evb);\n  socket.connect(nullptr, server.getAddress(), 30);\n\n  evb.loop(); // loop until the socket is connected\n\n  socket.setReadCB(&rcb);\n  socket.setMaxReadsPerEvent(maxReadsPerEvent);\n  socket.immediateReadCalled = false;\n\n  auto acceptedSocket = server.acceptAsync(&evb);\n\n  ReadCallback rcbServer;\n  WriteCallback wcbServer;\n  rcbServer.dataAvailableCallback = [&]() {\n    if (rcbServer.dataRead() == expectedDataSz) {\n      // write back all data read\n      rcbServer.verifyData(expectedData, expectedDataSz);\n      acceptedSocket->write(&wcbServer, expectedData, expectedDataSz);\n      acceptedSocket->close();\n    }\n  };\n  acceptedSocket->setReadCB(&rcbServer);\n\n  // write data\n  WriteCallback wcb1;\n  socket.write(&wcb1, expectedData, expectedDataSz);\n  evb.loop();\n  ASSERT_EQ(wcb1.state, STATE_SUCCEEDED);\n  rcb.verifyData(expectedData, expectedDataSz);\n  ASSERT_EQ(socket.immediateReadCalled, true);\n\n  ASSERT_FALSE(socket.isClosedBySelf());\n  ASSERT_FALSE(socket.isClosedByPeer());\n}\n\nTEST(AsyncSocket, ConnectReadUninstallRead) {\n  TestServer server;\n\n  const size_t maxBufferSz = 100;\n  const size_t maxReadsPerEvent = 1;\n  const size_t expectedDataSz = maxBufferSz * 3;\n  char expectedData[expectedDataSz];\n  memset(expectedData, 'k', expectedDataSz);\n\n  EventBase evb;\n  ReadCallback rcb(maxBufferSz);\n  AsyncSocketImmediateRead socket(&evb);\n  socket.connect(nullptr, server.getAddress(), 30);\n\n  evb.loop(); // loop until the socket is connected\n\n  socket.setReadCB(&rcb);\n  socket.setMaxReadsPerEvent(maxReadsPerEvent);\n  socket.immediateReadCalled = false;\n\n  auto acceptedSocket = server.acceptAsync(&evb);\n\n  ReadCallback rcbServer;\n  WriteCallback wcbServer;\n  rcbServer.dataAvailableCallback = [&]() {\n    if (rcbServer.dataRead() == expectedDataSz) {\n      // write back all data read\n      rcbServer.verifyData(expectedData, expectedDataSz);\n      acceptedSocket->write(&wcbServer, expectedData, expectedDataSz);\n      acceptedSocket->close();\n    }\n  };\n  acceptedSocket->setReadCB(&rcbServer);\n\n  rcb.dataAvailableCallback = [&]() {\n    // we read data and reset readCB\n    socket.setReadCB(nullptr);\n  };\n\n  // write data\n  WriteCallback wcb;\n  socket.write(&wcb, expectedData, expectedDataSz);\n  evb.loop();\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n  /* we should've only read maxBufferSz data since readCallback_\n   * was reset in dataAvailableCallback */\n  ASSERT_EQ(rcb.dataRead(), maxBufferSz);\n  ASSERT_EQ(socket.immediateReadCalled, false);\n\n  ASSERT_FALSE(socket.isClosedBySelf());\n  ASSERT_FALSE(socket.isClosedByPeer());\n}\n\n// TODO:\n// - Test connect() and have the connect callback set the read callback\n// - Test connect() and have the connect callback unset the read callback\n// - Test reading/writing/closing/destroying the socket in the connect callback\n// - Test reading/writing/closing/destroying the socket in the read callback\n// - Test reading/writing/closing/destroying the socket in the write callback\n// - Test one-way shutdown behavior\n// - Test changing the EventBase\n//\n// - TODO: test multiple threads sharing a AsyncSocket, and detaching from it\n//   in connectSuccess(), readDataAvailable(), writeSuccess()\n\n///////////////////////////////////////////////////////////////////////////\n// AsyncServerSocket tests\n///////////////////////////////////////////////////////////////////////////\n\n/**\n * Make sure accepted sockets have O_NONBLOCK and TCP_NODELAY set\n */\nTEST_P(AsyncSocketTest, ServerAcceptOptions) {\n  EventBase& eventBase = getEventBase();\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  // Connect to the server socket\n  std::shared_ptr<AsyncSocket> socket(\n      AsyncSocket::newSocket(&eventBase, serverAddress));\n\n  eventBase.loop();\n\n  // Verify that the server accepted a connection\n  ASSERT_EQ(acceptCallback.getEvents()->size(), 3);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(2).type, TestAcceptCallback::TYPE_STOP);\n  auto fd = acceptCallback.getEvents()->at(1).fd;\n\n#ifndef _WIN32\n  // It is not possible to check if a socket is already in non-blocking mode on\n  // Windows. Yes really. The accepted connection should already be in\n  // non-blocking mode\n  int flags = fcntl(fd.toFd(), F_GETFL, 0);\n  ASSERT_EQ(flags & O_NONBLOCK, O_NONBLOCK);\n#endif\n\n#ifndef TCP_NOPUSH\n  // The accepted connection should already have TCP_NODELAY set\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc =\n      netops::getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 1);\n#endif\n}\n\nTEST(AsyncSocketTest, NapiDispatch) {\n  EventBase eventBase;\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add several EventBases\n  EventBase::Options opt1;\n  opt1.setBackendFactory([]() -> std::unique_ptr<folly::EventBaseBackendBase> {\n    return EventBase::getTestBackend(0);\n  });\n  EventBase evb1(std::move(opt1));\n\n  EventBase::Options opt2;\n  opt2.setBackendFactory([]() -> std::unique_ptr<folly::EventBaseBackendBase> {\n    return EventBase::getTestBackend(1);\n  });\n  EventBase evb2(std::move(opt2));\n\n  EventBase::Options opt3;\n  opt3.setBackendFactory([]() -> std::unique_ptr<folly::EventBaseBackendBase> {\n    return EventBase::getTestBackend(2);\n  });\n  EventBase evb3(std::move(opt3));\n\n  int cb1Count = 0;\n  int cb2Count = 0;\n  int cb3Count = 0;\n\n  // Add several accept callbacks\n  TestAcceptCallback cb1;\n  TestAcceptCallback cb2;\n  TestAcceptCallback cb3;\n  cb1.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        if (++cb1Count == 3) {\n          eventBase.runInEventBaseThread([&] {\n            serverSocket->removeAcceptCallback(&cb1, &evb1);\n          });\n        }\n      });\n\n  cb2.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        if (++cb2Count == 2) {\n          eventBase.runInEventBaseThread([&] {\n            serverSocket->removeAcceptCallback(&cb2, &evb2);\n          });\n        }\n      });\n\n  cb3.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        if (++cb3Count == 1) {\n          eventBase.runInEventBaseThread([&] {\n            serverSocket->removeAcceptCallback(&cb3, &evb3);\n          });\n        }\n      });\n\n  // Make several connections to the socket\n  std::shared_ptr<AsyncSocket> sock1(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb1\n  std::shared_ptr<AsyncSocket> sock2(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb1\n  std::shared_ptr<AsyncSocket> sock3(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb1\n  std::shared_ptr<AsyncSocket> sock4(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb2\n  std::shared_ptr<AsyncSocket> sock5(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb2\n  std::shared_ptr<AsyncSocket> sock6(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb3\n  folly::SocketAddress sock1addr;\n  folly::SocketAddress sock2addr;\n  folly::SocketAddress sock3addr;\n  folly::SocketAddress sock4addr;\n  folly::SocketAddress sock5addr;\n  folly::SocketAddress sock6addr;\n  sock1->getAddress(&sock1addr);\n  sock2->getAddress(&sock2addr);\n  sock3->getAddress(&sock3addr);\n  sock4->getAddress(&sock4addr);\n  sock5->getAddress(&sock5addr);\n  sock6->getAddress(&sock6addr);\n\n  serverSocket->setCallbackAssignFunction(\n      [&](AsyncServerSocket*, NetworkSocket sock) {\n        struct sockaddr_in remoteAddr;\n        socklen_t addrLen = sizeof(sockaddr_in);\n        ::getpeername(sock.toFd(), (struct sockaddr*)&remoteAddr, &addrLen);\n        auto remotePort = ::ntohs(remoteAddr.sin_port);\n        if (remotePort == sock1addr.getPort() ||\n            remotePort == sock2addr.getPort() ||\n            remotePort == sock3addr.getPort()) {\n          return 0;\n        } else if (\n            remotePort == sock4addr.getPort() ||\n            remotePort == sock5addr.getPort()) {\n          return 1;\n        } else if (remotePort == sock6addr.getPort()) {\n          return 2;\n        }\n        return -1;\n      });\n\n  // Test having callbacks remove other callbacks before them on the list,\n  serverSocket->addAcceptCallback(&cb1, &evb1);\n  serverSocket->addAcceptCallback(&cb2, &evb2);\n  serverSocket->addAcceptCallback(&cb3, &evb3);\n  serverSocket->startAccepting();\n\n  std::vector<std::thread> threads;\n  threads.emplace_back([&]() { eventBase.loop(); });\n  threads.emplace_back([&]() { evb1.loop(); });\n  threads.emplace_back([&]() { evb2.loop(); });\n  threads.emplace_back([&]() { evb3.loop(); });\n\n  for (auto& t : threads) {\n    t.join();\n  }\n\n  ASSERT_EQ(cb1Count, 3);\n  ASSERT_EQ(cb2Count, 2);\n  ASSERT_EQ(cb3Count, 1);\n}\n\n/**\n * Test AsyncServerSocket::removeAcceptCallback()\n */\nTEST_P(AsyncSocketTest, RemoveAcceptCallback) {\n  // Create a new AsyncServerSocket\n  EventBase& eventBase = getEventBase();\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add several accept callbacks\n  TestAcceptCallback cb1;\n  TestAcceptCallback cb2;\n  TestAcceptCallback cb3;\n  TestAcceptCallback cb4;\n  TestAcceptCallback cb5;\n  TestAcceptCallback cb6;\n  TestAcceptCallback cb7;\n\n  // Test having callbacks remove other callbacks before them on the list,\n  // after them on the list, or removing themselves.\n  //\n  // Have callback 2 remove callback 3 and callback 5 the first time it is\n  // called.\n  int cb2Count = 0;\n  cb1.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        std::shared_ptr<AsyncSocket> sock2(\n            AsyncSocket::newSocket(\n                &eventBase, serverAddress)); // cb2: -cb3 -cb5\n      });\n  cb3.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {});\n  cb4.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        std::shared_ptr<AsyncSocket> sock3(\n            AsyncSocket::newSocket(&eventBase, serverAddress)); // cb4\n      });\n  cb5.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        std::shared_ptr<AsyncSocket> sock5(\n            AsyncSocket::newSocket(&eventBase, serverAddress)); // cb7: -cb7\n      });\n  cb2.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        if (cb2Count == 0) {\n          serverSocket->removeAcceptCallback(&cb3, nullptr);\n          serverSocket->removeAcceptCallback(&cb5, nullptr);\n        }\n        ++cb2Count;\n      });\n  // Have callback 6 remove callback 4 the first time it is called,\n  // and destroy the server socket the second time it is called\n  int cb6Count = 0;\n  cb6.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        if (cb6Count == 0) {\n          serverSocket->removeAcceptCallback(&cb4, nullptr);\n          std::shared_ptr<AsyncSocket> sock6(\n              AsyncSocket::newSocket(&eventBase, serverAddress)); // cb1\n          std::shared_ptr<AsyncSocket> sock7(\n              AsyncSocket::newSocket(&eventBase, serverAddress)); // cb2\n          std::shared_ptr<AsyncSocket> sock8(\n              AsyncSocket::newSocket(&eventBase, serverAddress)); // cb6: stop\n\n        } else {\n          serverSocket.reset();\n        }\n        ++cb6Count;\n      });\n  // Have callback 7 remove itself\n  cb7.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&cb7, nullptr);\n      });\n\n  serverSocket->addAcceptCallback(&cb1, &eventBase);\n  serverSocket->addAcceptCallback(&cb2, &eventBase);\n  serverSocket->addAcceptCallback(&cb3, &eventBase);\n  serverSocket->addAcceptCallback(&cb4, &eventBase);\n  serverSocket->addAcceptCallback(&cb5, &eventBase);\n  serverSocket->addAcceptCallback(&cb6, &eventBase);\n  serverSocket->addAcceptCallback(&cb7, &eventBase);\n  serverSocket->startAccepting();\n\n  // Make several connections to the socket\n  std::shared_ptr<AsyncSocket> sock1(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb1\n  std::shared_ptr<AsyncSocket> sock4(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb6: -cb4\n\n  // Loop until we are stopped\n  eventBase.loop();\n\n  // Check to make sure that the expected callbacks were invoked.\n  //\n  // NOTE: This code depends on the AsyncServerSocket operating calling all of\n  // the AcceptCallbacks in round-robin fashion, in the order that they were\n  // added.  The code is implemented this way right now, but the API doesn't\n  // explicitly require it be done this way.  If we change the code not to be\n  // exactly round robin in the future, we can simplify the test checks here.\n  // (We'll also need to update the termination code, since we expect cb6 to\n  // get called twice to terminate the loop.)\n  ASSERT_EQ(cb1.getEvents()->size(), 4);\n  ASSERT_EQ(cb1.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb1.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb1.getEvents()->at(2).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb1.getEvents()->at(3).type, TestAcceptCallback::TYPE_STOP);\n\n  ASSERT_EQ(cb2.getEvents()->size(), 4);\n  ASSERT_EQ(cb2.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb2.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb2.getEvents()->at(2).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb2.getEvents()->at(3).type, TestAcceptCallback::TYPE_STOP);\n\n  ASSERT_EQ(cb3.getEvents()->size(), 2);\n  ASSERT_EQ(cb3.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb3.getEvents()->at(1).type, TestAcceptCallback::TYPE_STOP);\n\n  ASSERT_EQ(cb4.getEvents()->size(), 3);\n  ASSERT_EQ(cb4.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb4.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb4.getEvents()->at(2).type, TestAcceptCallback::TYPE_STOP);\n\n  ASSERT_EQ(cb5.getEvents()->size(), 2);\n  ASSERT_EQ(cb5.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb5.getEvents()->at(1).type, TestAcceptCallback::TYPE_STOP);\n\n  ASSERT_EQ(cb6.getEvents()->size(), 4);\n  ASSERT_EQ(cb6.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb6.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb6.getEvents()->at(2).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb6.getEvents()->at(3).type, TestAcceptCallback::TYPE_STOP);\n\n  ASSERT_EQ(cb7.getEvents()->size(), 3);\n  ASSERT_EQ(cb7.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb7.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb7.getEvents()->at(2).type, TestAcceptCallback::TYPE_STOP);\n}\n\n/**\n * Test AsyncServerSocket::removeAcceptCallback()\n */\nTEST_P(AsyncSocketTest, OtherThreadAcceptCallback) {\n  // Create a new AsyncServerSocket\n  EventBase& eventBase = getEventBase();\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add several accept callbacks\n  TestAcceptCallback cb1;\n  auto thread_id = std::this_thread::get_id();\n  cb1.setAcceptStartedFn([&]() {\n    CHECK_NE(thread_id, std::this_thread::get_id());\n    thread_id = std::this_thread::get_id();\n  });\n  cb1.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        ASSERT_EQ(thread_id, std::this_thread::get_id());\n        serverSocket->removeAcceptCallback(&cb1, &eventBase);\n      });\n  cb1.setAcceptStoppedFn([&]() {\n    ASSERT_EQ(thread_id, std::this_thread::get_id());\n  });\n\n  // Test having callbacks remove other callbacks before them on the list,\n  serverSocket->addAcceptCallback(&cb1, &eventBase);\n  serverSocket->startAccepting();\n\n  // Make several connections to the socket\n  std::shared_ptr<AsyncSocket> sock1(\n      AsyncSocket::newSocket(&eventBase, serverAddress)); // cb1\n\n  // Loop in another thread\n  auto other = std::thread([&]() { eventBase.loop(); });\n  other.join();\n\n  // Check to make sure that the expected callbacks were invoked.\n  //\n  // NOTE: This code depends on the AsyncServerSocket operating calling all of\n  // the AcceptCallbacks in round-robin fashion, in the order that they were\n  // added.  The code is implemented this way right now, but the API doesn't\n  // explicitly require it be done this way.  If we change the code not to be\n  // exactly round robin in the future, we can simplify the test checks here.\n  // (We'll also need to update the termination code, since we expect cb6 to\n  // get called twice to terminate the loop.)\n  ASSERT_EQ(cb1.getEvents()->size(), 3);\n  ASSERT_EQ(cb1.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(cb1.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(cb1.getEvents()->at(2).type, TestAcceptCallback::TYPE_STOP);\n}\n\nvoid serverSocketSanityTest(\n    AsyncServerSocket* serverSocket,\n    std::optional<folly::SocketAddress> address = std::nullopt) {\n  EventBase* eventBase = serverSocket->getEventBase();\n  CHECK(eventBase);\n\n  // Add a callback to accept one connection then stop accepting\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, eventBase);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, eventBase);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, eventBase);\n  serverSocket->startAccepting();\n\n  // Connect to the server socket\n  folly::SocketAddress serverAddress;\n  if (address) {\n    serverAddress = *address;\n  } else {\n    serverSocket->getAddress(&serverAddress);\n  }\n  AsyncSocket::UniquePtr socket(new AsyncSocket(eventBase, serverAddress));\n\n  // Loop to process all events\n  eventBase->loop();\n\n  // Verify that the server accepted a connection\n  ASSERT_EQ(acceptCallback.getEvents()->size(), 3);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(2).type, TestAcceptCallback::TYPE_STOP);\n}\n\n/* Verify that we don't leak sockets if we are destroyed()\n * and there are still writes pending\n *\n * If destroy() only calls close() instead of closeNow(),\n * it would shutdown(writes) on the socket, but it would\n * never be close()'d, and the socket would leak\n */\nTEST_P(AsyncSocketTest, DestroyCloseTest) {\n  TestServer server;\n\n  // connect()\n  auto clientEB = makeEventBase();\n  auto serverEB = makeEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(clientEB.get());\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // Accept the connection\n  std::shared_ptr<AsyncSocket> acceptedSocket =\n      server.acceptAsync(serverEB.get());\n  ReadCallback rcb;\n  acceptedSocket->setReadCB(&rcb);\n\n  // Write a large buffer to the socket that is larger than kernel buffer\n  size_t simpleBufLength = 5000000;\n  char* simpleBuf = new char[simpleBufLength];\n  memset(simpleBuf, 'a', simpleBufLength);\n  WriteCallback wcb;\n\n  // Let the reads and writes run to completion\n  int fd = acceptedSocket->getNetworkSocket().toFd();\n\n  acceptedSocket->write(&wcb, simpleBuf, simpleBufLength);\n  socket.reset();\n  acceptedSocket.reset();\n\n  // Test that server socket was closed\n  folly::test::msvcSuppressAbortOnInvalidParams([&] {\n    ssize_t sz = fileops::read(fd, simpleBuf, simpleBufLength);\n    ASSERT_EQ(sz, -1);\n    ASSERT_EQ(errno, EBADF);\n  });\n  delete[] simpleBuf;\n}\n\n/**\n * Test AsyncServerSocket::useExistingSocket()\n */\nTEST_P(AsyncSocketTest, ServerExistingSocket) {\n  EventBase& eventBase = getEventBase();\n\n  // Test creating a socket, and letting AsyncServerSocket bind and listen\n  {\n    // Manually create a socket\n    auto fd = netops::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n    ASSERT_NE(fd, NetworkSocket());\n\n    // Create a server socket\n    AsyncServerSocket::UniquePtr serverSocket(\n        new AsyncServerSocket(&eventBase));\n    serverSocket->useExistingSocket(fd);\n    folly::SocketAddress address;\n    serverSocket->getAddress(&address);\n    address.setPort(0);\n    serverSocket->bind(address);\n    serverSocket->listen(16);\n\n    // Make sure the socket works\n    serverSocketSanityTest(serverSocket.get());\n  }\n\n  // Test creating a socket and binding manually,\n  // then letting AsyncServerSocket listen\n  {\n    // Manually create a socket\n    auto fd = netops::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n    ASSERT_NE(fd, NetworkSocket());\n    // bind\n    struct sockaddr_in addr;\n    addr.sin_family = AF_INET;\n    addr.sin_port = 0;\n    addr.sin_addr.s_addr = INADDR_ANY;\n    ASSERT_EQ(\n        netops::bind(\n            fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),\n        0);\n    // Look up the address that we bound to\n    folly::SocketAddress boundAddress;\n    boundAddress.setFromLocalAddress(fd);\n\n    // Create a server socket\n    AsyncServerSocket::UniquePtr serverSocket(\n        new AsyncServerSocket(&eventBase));\n    serverSocket->useExistingSocket(fd);\n    serverSocket->listen(16);\n\n    // Make sure AsyncServerSocket reports the same address that we bound to\n    folly::SocketAddress serverSocketAddress;\n    serverSocket->getAddress(&serverSocketAddress);\n    ASSERT_EQ(boundAddress, serverSocketAddress);\n\n    // Make sure the socket works\n    serverSocketSanityTest(serverSocket.get());\n  }\n\n  // Test creating a socket, binding and listening manually,\n  // then giving it to AsyncServerSocket\n  {\n    // Manually create a socket\n    auto fd = netops::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n    ASSERT_NE(fd, NetworkSocket());\n    // bind\n    struct sockaddr_in addr;\n    addr.sin_family = AF_INET;\n    addr.sin_port = 0;\n    addr.sin_addr.s_addr = INADDR_ANY;\n    ASSERT_EQ(\n        netops::bind(\n            fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),\n        0);\n    // Look up the address that we bound to\n    folly::SocketAddress boundAddress;\n    boundAddress.setFromLocalAddress(fd);\n    // listen\n    ASSERT_EQ(netops::listen(fd, 16), 0);\n\n    // Create a server socket\n    AsyncServerSocket::UniquePtr serverSocket(\n        new AsyncServerSocket(&eventBase));\n    serverSocket->useExistingSocket(fd);\n\n    // Make sure AsyncServerSocket reports the same address that we bound to\n    folly::SocketAddress serverSocketAddress;\n    serverSocket->getAddress(&serverSocketAddress);\n    ASSERT_EQ(boundAddress, serverSocketAddress);\n\n    // Make sure the socket works\n    serverSocketSanityTest(serverSocket.get());\n  }\n}\n\nTEST_P(AsyncSocketTest, UnixDomainSocketTest) {\n  EventBase& eventBase = getEventBase();\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  string path(1, 0);\n  path.append(folly::to<string>(\"/anonymous\", folly::Random::rand64()));\n  folly::SocketAddress serverAddress;\n  serverAddress.setFromPath(path);\n  serverSocket->bind(serverAddress);\n  serverSocket->listen(16);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  // Connect to the server socket\n  std::shared_ptr<AsyncSocket> socket(\n      AsyncSocket::newSocket(&eventBase, serverAddress));\n\n  eventBase.loop();\n\n  // Verify that the server accepted a connection\n  ASSERT_EQ(acceptCallback.getEvents()->size(), 3);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(0).type, TestAcceptCallback::TYPE_START);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(1).type, TestAcceptCallback::TYPE_ACCEPT);\n  ASSERT_EQ(\n      acceptCallback.getEvents()->at(2).type, TestAcceptCallback::TYPE_STOP);\n  auto fd = acceptCallback.getEvents()->at(1).fd;\n\n#ifndef _WIN32\n  // It is not possible to check if a socket is already in non-blocking mode on\n  // Windows. Yes really. The accepted connection should already be in\n  // non-blocking mode\n  int flags = fcntl(fd.toFd(), F_GETFL, 0);\n  ASSERT_EQ(flags & O_NONBLOCK, O_NONBLOCK);\n#endif\n}\n\n#if defined(__linux__)\nTEST(AsyncSocketTest, VsockSocketLocal) {\n  EventBase eventBase;\n\n  sockaddr_vm addr{};\n  memset(&addr, 0, sizeof(addr));\n  addr.svm_family = AF_VSOCK;\n  addr.svm_cid = VMADDR_CID_LOCAL;\n  addr.svm_port = VMADDR_PORT_ANY;\n\n  folly::SocketAddress address;\n  address.setFromSockaddr(&addr);\n\n  AsyncServerSocket::UniquePtr serverSocket(new AsyncServerSocket(&eventBase));\n  serverSocket->bind(address);\n  serverSocket->listen(16);\n\n  auto actualAddress = serverSocket->getAddress();\n  EXPECT_NE(actualAddress.getVsockPort(), VMADDR_PORT_ANY);\n\n  serverSocketSanityTest(serverSocket.get());\n}\n#endif\n\n#if defined(__linux__)\nTEST(AsyncSocketTest, VsockSocketAny) {\n  EventBase eventBase;\n\n  sockaddr_vm addr{};\n  memset(&addr, 0, sizeof(addr));\n  addr.svm_family = AF_VSOCK;\n  addr.svm_cid = VMADDR_CID_ANY;\n  addr.svm_port = VMADDR_PORT_ANY;\n\n  folly::SocketAddress address;\n  address.setFromSockaddr(&addr);\n\n  AsyncServerSocket::UniquePtr serverSocket(new AsyncServerSocket(&eventBase));\n  serverSocket->bind(address);\n  serverSocket->listen(16);\n\n  auto actualAddress = serverSocket->getAddress();\n  EXPECT_NE(actualAddress.getVsockPort(), VMADDR_PORT_ANY);\n\n  addr.svm_cid = VMADDR_CID_LOCAL;\n  addr.svm_port = actualAddress.getVsockPort();\n  address.setFromSockaddr(&addr);\n\n  serverSocketSanityTest(serverSocket.get(), address);\n}\n#endif\n\n#if defined(__linux__)\nTEST(AsyncSocketTest, VsockSocketPortAny) {\n  EventBase eventBase;\n\n  sockaddr_vm addr{};\n  memset(&addr, 0, sizeof(addr));\n  addr.svm_family = AF_VSOCK;\n  addr.svm_cid = VMADDR_CID_LOCAL;\n  addr.svm_port = VMADDR_PORT_ANY;\n\n  folly::SocketAddress address;\n  address.setFromSockaddr(&addr);\n\n  AsyncServerSocket::UniquePtr serverSocket1(new AsyncServerSocket(&eventBase));\n  serverSocket1->bind(address);\n  serverSocket1->listen(16);\n\n  AsyncServerSocket::UniquePtr serverSocket2(new AsyncServerSocket(&eventBase));\n  serverSocket2->bind(address);\n  serverSocket2->listen(16);\n\n  EXPECT_NE(serverSocket1->getAddress().getVsockPort(), VMADDR_PORT_ANY);\n  EXPECT_NE(\n      serverSocket1->getAddress().getVsockPort(),\n      serverSocket2->getAddress().getVsockPort());\n}\n#endif\n\nTEST_P(AsyncSocketTest, ConnectionEventCallbackDefault) {\n  EventBase& eventBase = getEventBase();\n  TestConnectionEventCallback connectionEventCallback;\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->setConnectionEventCallback(&connectionEventCallback);\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  // Connect to the server socket\n  std::shared_ptr<AsyncSocket> socket(\n      AsyncSocket::newSocket(&eventBase, serverAddress));\n\n  eventBase.loop();\n\n  // Validate the connection event counters\n  ASSERT_EQ(connectionEventCallback.getConnectionAccepted(), 1);\n  ASSERT_EQ(connectionEventCallback.getConnectionAcceptedError(), 0);\n  ASSERT_EQ(connectionEventCallback.getConnectionDropped(), 0);\n  ASSERT_EQ(\n      connectionEventCallback.getConnectionEnqueuedForAcceptCallback(), 0);\n  ASSERT_EQ(connectionEventCallback.getConnectionDequeuedByAcceptCallback(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffStarted(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffEnded(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffError(), 0);\n}\n\nTEST_P(AsyncSocketTest, CallbackInPrimaryEventBase) {\n  EventBase& eventBase = getEventBase();\n  TestConnectionEventCallback connectionEventCallback;\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->setConnectionEventCallback(&connectionEventCallback);\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n  });\n  bool acceptStartedFlag{false};\n  acceptCallback.setAcceptStartedFn([&acceptStartedFlag]() {\n    acceptStartedFlag = true;\n  });\n  bool acceptStoppedFlag{false};\n  acceptCallback.setAcceptStoppedFn([&acceptStoppedFlag]() {\n    acceptStoppedFlag = true;\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, nullptr);\n  serverSocket->startAccepting();\n\n  // Connect to the server socket\n  std::shared_ptr<AsyncSocket> socket(\n      AsyncSocket::newSocket(&eventBase, serverAddress));\n\n  eventBase.loop();\n\n  ASSERT_TRUE(acceptStartedFlag);\n  ASSERT_TRUE(acceptStoppedFlag);\n  // Validate the connection event counters\n  ASSERT_EQ(connectionEventCallback.getConnectionAccepted(), 1);\n  ASSERT_EQ(connectionEventCallback.getConnectionAcceptedError(), 0);\n  ASSERT_EQ(connectionEventCallback.getConnectionDropped(), 0);\n  ASSERT_EQ(\n      connectionEventCallback.getConnectionEnqueuedForAcceptCallback(), 0);\n  ASSERT_EQ(connectionEventCallback.getConnectionDequeuedByAcceptCallback(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffStarted(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffEnded(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffError(), 0);\n}\n\nTEST_P(AsyncSocketTest, CallbackInSecondaryEventBase) {\n  EventBase& eventBase = getEventBase();\n  TestConnectionEventCallback connectionEventCallback;\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->setConnectionEventCallback(&connectionEventCallback);\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  ScopedEventBaseThread cobThread(\"ioworker_test\");\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const SocketAddress& /* addr */) {\n        eventBase.runInEventBaseThread([&] {\n          serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n        });\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    eventBase.runInEventBaseThread([&] {\n      serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n    });\n  });\n  std::atomic<bool> acceptStartedFlag{false};\n  acceptCallback.setAcceptStartedFn([&]() { acceptStartedFlag = true; });\n  Baton<> acceptStoppedFlag;\n  acceptCallback.setAcceptStoppedFn([&]() { acceptStoppedFlag.post(); });\n  serverSocket->addAcceptCallback(&acceptCallback, cobThread.getEventBase());\n  serverSocket->startAccepting();\n\n  // Connect to the server socket\n  std::shared_ptr<AsyncSocket> socket(\n      AsyncSocket::newSocket(&eventBase, serverAddress));\n\n  eventBase.loop();\n\n  ASSERT_TRUE(acceptStoppedFlag.try_wait_for(std::chrono::seconds(1)));\n  ASSERT_TRUE(acceptStartedFlag);\n  // Validate the connection event counters\n  ASSERT_EQ(connectionEventCallback.getConnectionAccepted(), 1);\n  ASSERT_EQ(connectionEventCallback.getConnectionAcceptedError(), 0);\n  ASSERT_EQ(connectionEventCallback.getConnectionDropped(), 0);\n  ASSERT_EQ(\n      connectionEventCallback.getConnectionEnqueuedForAcceptCallback(), 1);\n  ASSERT_EQ(connectionEventCallback.getConnectionDequeuedByAcceptCallback(), 1);\n  ASSERT_EQ(connectionEventCallback.getBackoffStarted(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffEnded(), 0);\n  ASSERT_EQ(connectionEventCallback.getBackoffError(), 0);\n}\n\n/**\n * Test AsyncServerSocket::getNumPendingMessagesInQueue()\n */\nTEST_P(AsyncSocketTest, NumPendingMessagesInQueue) {\n  EventBase& eventBase = getEventBase();\n\n  // Counter of how many connections have been accepted\n  int count = 0;\n\n  // Create a server socket\n  auto serverSocket(AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Add a callback to accept connections\n  TestAcceptCallback acceptCallback;\n  folly::ScopedEventBaseThread cobThread(\"ioworker_test\");\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        count++;\n        eventBase.runInEventBaseThreadAndWait([&] {\n          ASSERT_EQ(4 - count, serverSocket->getNumPendingMessagesInQueue());\n        });\n        if (count == 4) {\n          eventBase.runInEventBaseThread([&] {\n            serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n          });\n        }\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    eventBase.runInEventBaseThread([&] {\n      serverSocket->removeAcceptCallback(&acceptCallback, nullptr);\n    });\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, cobThread.getEventBase());\n  serverSocket->startAccepting();\n\n  // Connect to the server socket, 4 clients, there are 4 connections\n  auto socket1(AsyncSocket::newSocket(&eventBase, serverAddress));\n  auto socket2(AsyncSocket::newSocket(&eventBase, serverAddress));\n  auto socket3(AsyncSocket::newSocket(&eventBase, serverAddress));\n  auto socket4(AsyncSocket::newSocket(&eventBase, serverAddress));\n\n  eventBase.loop();\n  ASSERT_EQ(4, count);\n}\n\n/**\n * Test AsyncTransport::BufferCallback\n */\nTEST_P(AsyncSocketTest, BufferTest) {\n  TestServer server(false, 1024 * 1024);\n\n  EventBase& evb = getEventBase();\n  SocketOptionMap option{{{SOL_SOCKET, SO_SNDBUF}, 128}};\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30, option);\n\n  char buf[100 * 1024];\n  memset(buf, 'c', sizeof(buf));\n  WriteCallback wcb;\n  BufferCallback bcb(socket.get(), sizeof(buf));\n  socket->setBufferCallback(&bcb);\n  socket->write(&wcb, buf, sizeof(buf), WriteFlags::NONE);\n\n  std::thread t1([&]() { server.verifyConnection(buf, sizeof(buf)); });\n\n  evb.loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n  ASSERT_TRUE(bcb.hasBuffered());\n  ASSERT_TRUE(bcb.hasBufferCleared());\n\n  socket->close();\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n\n  t1.join();\n}\n\nTEST_P(AsyncSocketTest, BufferTestChain) {\n  TestServer server(false, 1024 * 1024);\n\n  EventBase& evb = getEventBase();\n  SocketOptionMap option{{{SOL_SOCKET, SO_SNDBUF}, 128}};\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30, option);\n\n  char buf1[100 * 1024];\n  memset(buf1, 'c', sizeof(buf1));\n  char buf2[100 * 1024];\n  memset(buf2, 'f', sizeof(buf2));\n\n  auto buf = folly::IOBuf::copyBuffer(buf1, sizeof(buf1));\n  buf->appendToChain(folly::IOBuf::copyBuffer(buf2, sizeof(buf2)));\n  ASSERT_EQ(sizeof(buf1) + sizeof(buf2), buf->computeChainDataLength());\n\n  BufferCallback bcb(socket.get(), buf->computeChainDataLength());\n  socket->setBufferCallback(&bcb);\n\n  WriteCallback wcb;\n  socket->writeChain(&wcb, buf->clone(), WriteFlags::NONE);\n\n  std::thread t1([&]() {\n    buf->coalesce();\n    server.verifyConnection(\n        reinterpret_cast<const char*>(buf->data()), buf->length());\n  });\n\n  evb.loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n  ASSERT_TRUE(bcb.hasBuffered());\n  ASSERT_TRUE(bcb.hasBufferCleared());\n\n  socket->close();\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n  t1.join();\n}\n\nTEST_P(AsyncSocketTest, BufferCallbackKill) {\n  TestServer server(false, 1024 * 1024);\n\n  EventBase& evb = getEventBase();\n  SocketOptionMap option{{{SOL_SOCKET, SO_SNDBUF}, 128}};\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30, option);\n  evb.loopOnce();\n\n  char buf[100 * 1024];\n  memset(buf, 'c', sizeof(buf));\n  BufferCallback bcb(socket.get(), sizeof(buf));\n  socket->setBufferCallback(&bcb);\n  WriteCallback wcb;\n  wcb.successCallback = [&] {\n    ASSERT_TRUE(socket.unique());\n    socket.reset();\n  };\n\n  // This will trigger AsyncSocket::handleWrite,\n  // which calls WriteCallback::writeSuccess,\n  // which calls wcb.successCallback above,\n  // which tries to delete socket\n  // Then, the socket will also try to use this BufferCallback\n  // And that should crash us, if there is no DestructorGuard on the stack\n  socket->write(&wcb, buf, sizeof(buf), WriteFlags::NONE);\n\n  std::thread t1([&]() { server.verifyConnection(buf, sizeof(buf)); });\n\n  evb.loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  t1.join();\n}\n\nTEST_P(AsyncSocketTest, BufferCallbackKillWithIOBuf) {\n  TestServer server(false, 1024 * 1024);\n\n  EventBase& evb = getEventBase();\n  SocketOptionMap option{{{SOL_SOCKET, SO_SNDBUF}, 128}};\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30, option);\n  evb.loopOnce();\n\n  char buf[100 * 1024];\n  memset(buf, 'c', sizeof(buf));\n  BufferCallback bcb(socket.get(), sizeof(buf));\n  socket->setBufferCallback(&bcb);\n  WriteCallback wcb(true);\n  wcb.successCallback = [&] {\n    ASSERT_TRUE(socket.unique());\n    socket.reset();\n  };\n\n  socket->writeChain(\n      &wcb, folly::IOBuf::wrapBuffer(buf, sizeof(buf)), WriteFlags::NONE);\n\n  std::thread t1([&]() { server.verifyConnection(buf, sizeof(buf)); });\n\n  evb.loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  t1.join();\n  ASSERT_TRUE(wcb.releaseIOBufCallbackCalled);\n}\n\n#if FOLLY_ALLOW_TFO\nTEST_P(AsyncSocketTest, ConnectTFO) {\n  if (!folly::test::isTFOAvailable()) {\n    GTEST_SKIP() << \"TFO not supported.\";\n  }\n\n  // Start listening on a local port\n  TestServer server(true);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->enableTFO();\n  ConnCallback cb;\n  socket->connect(&cb, server.getAddress(), 30);\n\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n\n  std::array<uint8_t, 3> readBuf;\n  auto sendBuf = IOBuf::copyBuffer(\"hey\");\n\n  std::thread t([&] {\n    auto acceptedSocket = server.accept();\n    acceptedSocket->write(buf.data(), buf.size());\n    acceptedSocket->flush();\n    acceptedSocket->readAll(readBuf.data(), readBuf.size());\n    acceptedSocket->close();\n  });\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(30));\n  EXPECT_TRUE(socket->getTFOAttempted());\n\n  // Should trigger the connect\n  WriteCallback write;\n  ReadCallback rcb;\n  socket->writeChain(&write, sendBuf->clone());\n  socket->setReadCB(&rcb);\n  evb.loop();\n\n  t.join();\n\n  EXPECT_EQ(STATE_SUCCEEDED, write.state);\n  EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size()));\n  EXPECT_EQ(STATE_SUCCEEDED, rcb.state);\n  ASSERT_EQ(1, rcb.buffers.size());\n  ASSERT_EQ(sizeof(buf), rcb.buffers[0].length);\n  EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));\n  EXPECT_EQ(socket->getTFOFinished(), socket->getTFOSucceeded());\n}\n\nTEST_P(AsyncSocketTest, ConnectTFOSupplyEarlyReadCB) {\n  if (!folly::test::isTFOAvailable()) {\n    GTEST_SKIP() << \"TFO not supported.\";\n  }\n\n  // Start listening on a local port\n  TestServer server(true);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->enableTFO();\n  ConnCallback cb;\n  socket->connect(&cb, server.getAddress(), 30);\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n\n  std::array<uint8_t, 3> readBuf;\n  auto sendBuf = IOBuf::copyBuffer(\"hey\");\n\n  std::thread t([&] {\n    auto acceptedSocket = server.accept();\n    acceptedSocket->write(buf.data(), buf.size());\n    acceptedSocket->flush();\n    acceptedSocket->readAll(readBuf.data(), readBuf.size());\n    acceptedSocket->close();\n  });\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(30));\n  EXPECT_TRUE(socket->getTFOAttempted());\n\n  // Should trigger the connect\n  WriteCallback write;\n  socket->writeChain(&write, sendBuf->clone());\n  evb.loop();\n\n  t.join();\n\n  EXPECT_EQ(STATE_SUCCEEDED, write.state);\n  EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size()));\n  EXPECT_EQ(STATE_SUCCEEDED, rcb.state);\n  ASSERT_EQ(1, rcb.buffers.size());\n  ASSERT_EQ(sizeof(buf), rcb.buffers[0].length);\n  EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));\n  EXPECT_EQ(socket->getTFOFinished(), socket->getTFOSucceeded());\n}\n\n/**\n * Test connecting to a server that isn't listening\n */\nTEST_P(AsyncSocketTest, ConnectRefusedImmediatelyTFO) {\n  EventBase& evb = getEventBase();\n\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  socket->enableTFO();\n\n  // Hopefully nothing is actually listening on this address\n  folly::SocketAddress addr(\"::1\", 65535);\n  ConnCallback cb;\n  socket->connect(&cb, addr, 30);\n\n  evb.loop();\n\n  WriteCallback write1;\n  // Trigger the connect if TFO attempt is supported.\n  socket->writeChain(&write1, IOBuf::copyBuffer(\"hey\"));\n  WriteCallback write2;\n  socket->writeChain(&write2, IOBuf::copyBuffer(\"hey\"));\n  evb.loop();\n\n  if (!socket->getTFOFinished()) {\n    EXPECT_EQ(STATE_FAILED, write1.state);\n  } else {\n    EXPECT_EQ(STATE_SUCCEEDED, write1.state);\n    EXPECT_FALSE(socket->getTFOSucceeded());\n  }\n\n  EXPECT_EQ(STATE_FAILED, write2.state);\n\n  EXPECT_EQ(STATE_SUCCEEDED, cb.state);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(std::chrono::milliseconds(30), socket->getConnectTimeout());\n  EXPECT_TRUE(socket->getTFOAttempted());\n}\n\n/**\n * Test calling closeNow() immediately after connecting.\n */\nTEST_P(AsyncSocketTest, ConnectWriteAndCloseNowTFO) {\n  TestServer server(true);\n\n  // connect()\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->enableTFO();\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  // write()\n  std::array<char, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n\n  // close()\n  socket->closeNow();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\n/**\n * Test calling close() immediately after connect()\n */\nTEST_P(AsyncSocketTest, ConnectAndCloseTFO) {\n  TestServer server(true);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->enableTFO();\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  socket->close();\n\n  // Loop, although there shouldn't be anything to do.\n  evb.loop();\n\n  // Make sure the connection was aborted\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\nclass MockAsyncTFOSocket : public AsyncSocket {\n public:\n  using UniquePtr = std::unique_ptr<MockAsyncTFOSocket, Destructor>;\n\n  explicit MockAsyncTFOSocket(EventBase* evb) : AsyncSocket(evb) {}\n\n  MOCK_METHOD(\n      ssize_t,\n      tfoSendMsg,\n      (NetworkSocket fd, struct msghdr* msg, int msg_flags));\n};\n\nTEST_P(AsyncSocketTest, TestTFOUnsupported) {\n  TestServer server(true);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = MockAsyncTFOSocket::UniquePtr(new MockAsyncTFOSocket(&evb));\n  socket->enableTFO();\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  EXPECT_CALL(*socket, tfoSendMsg(_, _, _))\n      .WillOnce(SetErrnoAndReturn(EOPNOTSUPP, -1));\n  WriteCallback write;\n  auto sendBuf = IOBuf::copyBuffer(\"hey\");\n  socket->writeChain(&write, sendBuf->clone());\n  EXPECT_EQ(STATE_WAITING, write.state);\n\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n\n  std::array<uint8_t, 3> readBuf;\n\n  std::thread t([&] {\n    std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n    acceptedSocket->write(buf.data(), buf.size());\n    acceptedSocket->flush();\n    acceptedSocket->readAll(readBuf.data(), readBuf.size());\n    acceptedSocket->close();\n  });\n\n  evb.loop();\n\n  t.join();\n  EXPECT_EQ(STATE_SUCCEEDED, ccb.state);\n  EXPECT_EQ(STATE_SUCCEEDED, write.state);\n\n  EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size()));\n  EXPECT_EQ(STATE_SUCCEEDED, rcb.state);\n  ASSERT_EQ(1, rcb.buffers.size());\n  ASSERT_EQ(sizeof(buf), rcb.buffers[0].length);\n  EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));\n  EXPECT_EQ(socket->getTFOFinished(), socket->getTFOSucceeded());\n}\n\nTEST_P(AsyncSocketTest, ConnectRefusedDelayedTFO) {\n  EventBase& evb = getEventBase();\n\n  auto socket = MockAsyncTFOSocket::UniquePtr(new MockAsyncTFOSocket(&evb));\n  socket->enableTFO();\n\n  // Hopefully this fails\n  folly::SocketAddress fakeAddr(\"127.0.0.1\", 65535);\n  EXPECT_CALL(*socket, tfoSendMsg(_, _, _))\n      .WillOnce(Invoke([&](NetworkSocket fd, struct msghdr*, int) {\n        sockaddr_storage addr;\n        auto len = fakeAddr.getAddress(&addr);\n        auto ret = netops::connect(fd, (const struct sockaddr*)&addr, len);\n        LOG(INFO) << \"connecting the socket \" << fd << \" : \" << ret << \" : \"\n                  << errno;\n        return ret;\n      }));\n\n  // Hopefully nothing is actually listening on this address\n  ConnCallback cb;\n  socket->connect(&cb, fakeAddr, 30);\n\n  WriteCallback write1;\n  // Trigger the connect if TFO attempt is supported.\n  socket->writeChain(&write1, IOBuf::copyBuffer(\"hey\"));\n\n  if (socket->getTFOFinished()) {\n    // This test is useless now.\n    return;\n  }\n  WriteCallback write2;\n  // Trigger the connect if TFO attempt is supported.\n  socket->writeChain(&write2, IOBuf::copyBuffer(\"hey\"));\n  evb.loop();\n\n  EXPECT_EQ(STATE_FAILED, write1.state);\n  EXPECT_EQ(STATE_FAILED, write2.state);\n  EXPECT_FALSE(socket->getTFOSucceeded());\n\n  EXPECT_EQ(STATE_SUCCEEDED, cb.state);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(std::chrono::milliseconds(30), socket->getConnectTimeout());\n  EXPECT_TRUE(socket->getTFOAttempted());\n}\n\nTEST_P(AsyncSocketTest, TestTFOUnsupportedTimeout) {\n  // Try connecting to server that won't respond.\n  //\n  // This depends somewhat on the network where this test is run.\n  // Hopefully this IP will be routable but unresponsive.\n  // (Alternatively, we could try listening on a local raw socket, but that\n  // normally requires root privileges.)\n  auto host = SocketAddressTestHelper::isIPv6Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv6\n      : SocketAddressTestHelper::isIPv4Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv4\n      : nullptr;\n  SocketAddress addr(host, 65535);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = MockAsyncTFOSocket::UniquePtr(new MockAsyncTFOSocket(&evb));\n  socket->enableTFO();\n\n  ConnCallback ccb;\n  // Set a very small timeout\n  socket->connect(&ccb, addr, 1);\n  EXPECT_EQ(STATE_SUCCEEDED, ccb.state);\n\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  EXPECT_CALL(*socket, tfoSendMsg(_, _, _))\n      .WillOnce(SetErrnoAndReturn(EOPNOTSUPP, -1));\n  WriteCallback write;\n  socket->writeChain(&write, IOBuf::copyBuffer(\"hey\"));\n\n  evb.loop();\n\n  EXPECT_EQ(STATE_FAILED, write.state);\n}\n\nTEST_P(AsyncSocketTest, TestTFOFallbackToConnect) {\n  TestServer server(true);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = MockAsyncTFOSocket::UniquePtr(new MockAsyncTFOSocket(&evb));\n  socket->enableTFO();\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  EXPECT_CALL(*socket, tfoSendMsg(_, _, _))\n      .WillOnce(Invoke([&](NetworkSocket fd, struct msghdr*, int) {\n        sockaddr_storage addr;\n        auto len = server.getAddress().getAddress(&addr);\n        return netops::connect(fd, (const struct sockaddr*)&addr, len);\n      }));\n  WriteCallback write;\n  auto sendBuf = IOBuf::copyBuffer(\"hey\");\n  socket->writeChain(&write, sendBuf->clone());\n  EXPECT_EQ(STATE_WAITING, write.state);\n\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n\n  std::array<uint8_t, 3> readBuf;\n\n  std::thread t([&] {\n    std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n    acceptedSocket->write(buf.data(), buf.size());\n    acceptedSocket->flush();\n    acceptedSocket->readAll(readBuf.data(), readBuf.size());\n    acceptedSocket->close();\n  });\n\n  evb.loop();\n\n  t.join();\n  EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size()));\n\n  EXPECT_EQ(STATE_SUCCEEDED, ccb.state);\n  EXPECT_EQ(STATE_SUCCEEDED, write.state);\n\n  EXPECT_EQ(STATE_SUCCEEDED, rcb.state);\n  ASSERT_EQ(1, rcb.buffers.size());\n  ASSERT_EQ(buf.size(), rcb.buffers[0].length);\n  EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));\n}\n\nTEST_P(AsyncSocketTest, TestTFOFallbackTimeout) {\n  // Try connecting to server that won't respond.\n  //\n  // This depends somewhat on the network where this test is run.\n  // Hopefully this IP will be routable but unresponsive.\n  // (Alternatively, we could try listening on a local raw socket, but that\n  // normally requires root privileges.)\n  auto host = SocketAddressTestHelper::isIPv6Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv6\n      : SocketAddressTestHelper::isIPv4Enabled()\n      ? SocketAddressTestHelper::kGooglePublicDnsAAddrIPv4\n      : nullptr;\n  SocketAddress addr(host, 65535);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = MockAsyncTFOSocket::UniquePtr(new MockAsyncTFOSocket(&evb));\n  socket->enableTFO();\n\n  ConnCallback ccb;\n  // Set a very small timeout\n  socket->connect(&ccb, addr, 1);\n  EXPECT_EQ(STATE_SUCCEEDED, ccb.state);\n\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  EXPECT_CALL(*socket, tfoSendMsg(_, _, _))\n      .WillOnce(Invoke([&](NetworkSocket fd, struct msghdr*, int) {\n        sockaddr_storage addr2;\n        auto len = addr.getAddress(&addr2);\n        return netops::connect(fd, (const struct sockaddr*)&addr2, len);\n      }));\n  WriteCallback write;\n  socket->writeChain(&write, IOBuf::copyBuffer(\"hey\"));\n\n  evb.loop();\n\n  EXPECT_EQ(STATE_FAILED, write.state);\n}\n\nTEST_P(AsyncSocketTest, TestTFOEagain) {\n  TestServer server(true);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  auto socket = MockAsyncTFOSocket::UniquePtr(new MockAsyncTFOSocket(&evb));\n  socket->enableTFO();\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n\n  EXPECT_CALL(*socket, tfoSendMsg(_, _, _))\n      .WillOnce(SetErrnoAndReturn(EAGAIN, -1));\n  WriteCallback write;\n  socket->writeChain(&write, IOBuf::copyBuffer(\"hey\"));\n\n  evb.loop();\n\n  EXPECT_EQ(STATE_SUCCEEDED, ccb.state);\n  EXPECT_EQ(STATE_FAILED, write.state);\n}\n\n// Sending a large amount of data in the first write which will\n// definitely not fit into MSS.\nTEST_P(AsyncSocketTest, ConnectTFOWithBigData) {\n  if (!folly::test::isTFOAvailable()) {\n    GTEST_SKIP() << \"TFO not supported.\";\n  }\n\n  // Start listening on a local port\n  TestServer server(true);\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->enableTFO();\n  ConnCallback cb;\n  socket->connect(&cb, server.getAddress(), 30);\n\n  std::array<uint8_t, 128> buf;\n  memset(buf.data(), 'a', buf.size());\n\n  constexpr size_t len = 10 * 1024;\n  auto sendBuf = IOBuf::create(len);\n  sendBuf->append(len);\n  std::array<uint8_t, len> readBuf;\n\n  std::thread t([&] {\n    auto acceptedSocket = server.accept();\n    acceptedSocket->write(buf.data(), buf.size());\n    acceptedSocket->flush();\n    acceptedSocket->readAll(readBuf.data(), readBuf.size());\n    acceptedSocket->close();\n  });\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(30));\n  EXPECT_TRUE(socket->getTFOAttempted());\n\n  // Should trigger the connect\n  WriteCallback write;\n  ReadCallback rcb;\n  socket->writeChain(&write, sendBuf->clone());\n  socket->setReadCB(&rcb);\n  evb.loop();\n\n  t.join();\n\n  EXPECT_EQ(STATE_SUCCEEDED, write.state);\n  EXPECT_EQ(0, memcmp(readBuf.data(), sendBuf->data(), readBuf.size()));\n  EXPECT_EQ(STATE_SUCCEEDED, rcb.state);\n  ASSERT_EQ(1, rcb.buffers.size());\n  ASSERT_EQ(sizeof(buf), rcb.buffers[0].length);\n  EXPECT_EQ(0, memcmp(rcb.buffers[0].buffer, buf.data(), buf.size()));\n  EXPECT_EQ(socket->getTFOFinished(), socket->getTFOSucceeded());\n}\n\n#endif // FOLLY_ALLOW_TFO\n\nclass MockEvbChangeCallback : public AsyncSocket::EvbChangeCallback {\n public:\n  MOCK_METHOD(void, evbAttached, (AsyncSocket*));\n  MOCK_METHOD(void, evbDetached, (AsyncSocket*));\n};\n\nTEST_P(AsyncSocketTest, EvbCallbacks) {\n  auto cb = std::make_unique<MockEvbChangeCallback>();\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  InSequence seq;\n  EXPECT_CALL(*cb, evbDetached(socket.get())).Times(1);\n  EXPECT_CALL(*cb, evbAttached(socket.get())).Times(1);\n\n  socket->setEvbChangedCallback(std::move(cb));\n  socket->detachEventBase();\n  socket->attachEventBase(&evb);\n}\n\nTEST_P(AsyncSocketTest, TestEvbDetachWtRegisteredIOHandlers) {\n  // Start listening on a local port\n  TestServer server;\n\n  // Connect using a AsyncSocket\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  ConnCallback cb;\n  socket->connect(&cb, server.getAddress(), 30);\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(30));\n\n  // After the ioHandlers are registered, still should be able to detach/attach\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  auto cbEvbChg = std::make_unique<MockEvbChangeCallback>();\n  InSequence seq;\n  EXPECT_CALL(*cbEvbChg, evbDetached(socket.get())).Times(1);\n  EXPECT_CALL(*cbEvbChg, evbAttached(socket.get())).Times(1);\n\n  socket->setEvbChangedCallback(std::move(cbEvbChg));\n  EXPECT_TRUE(socket->isDetachable());\n  socket->detachEventBase();\n  socket->attachEventBase(&evb);\n\n  socket->close();\n}\n\nTEST_P(AsyncSocketTest, TestEvbDetachThenClose) {\n  if (GetParam() == BackendType::IO_URING) {\n    GTEST_SKIP() << \"io_uring does not support detachNetworkSocket()\";\n  }\n  // Start listening on a local port\n  TestServer server;\n\n  // Connect an AsyncSocket to the server\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::newSocket(&evb);\n  ConnCallback cb;\n  socket->connect(&cb, server.getAddress(), 30);\n\n  evb.loop();\n\n  ASSERT_EQ(cb.state, STATE_SUCCEEDED);\n  EXPECT_LE(0, socket->getConnectTime().count());\n  EXPECT_EQ(socket->getConnectTimeout(), std::chrono::milliseconds(30));\n\n  // After the ioHandlers are registered, still should be able to detach/attach\n  ReadCallback rcb;\n  socket->setReadCB(&rcb);\n\n  auto cbEvbChg = std::make_unique<MockEvbChangeCallback>();\n  InSequence seq;\n  EXPECT_CALL(*cbEvbChg, evbDetached(socket.get())).Times(1);\n\n  socket->setEvbChangedCallback(std::move(cbEvbChg));\n\n  // Should be possible to destroy/call closeNow() without an attached EventBase\n  EXPECT_TRUE(socket->isDetachable());\n  socket->detachEventBase();\n  socket.reset();\n}\n\nTEST_P(AsyncSocketTest, BytesWrittenWithMove) {\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  ConnCallback ccb;\n  socket1->connect(&ccb, server.getAddress(), 30);\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n\n  EXPECT_EQ(0, socket1->getRawBytesWritten());\n  std::vector<uint8_t> wbuf(128, 'a');\n  WriteCallback wcb;\n  socket1->write(&wcb, wbuf.data(), wbuf.size());\n  evb.loop();\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  EXPECT_EQ(wbuf.size(), socket1->getRawBytesWritten());\n  EXPECT_EQ(wbuf.size(), socket1->getAppBytesWritten());\n\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(std::move(socket1)));\n  EXPECT_EQ(wbuf.size(), socket2->getRawBytesWritten());\n  EXPECT_EQ(wbuf.size(), socket2->getAppBytesWritten());\n}\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\nstruct AsyncSocketErrMessageCallbackTestParams {\n  BackendType backendType{BackendType::DEFAULT};\n  folly::Optional<int> resetCallbackAfter;\n  folly::Optional<int> closeSocketAfter;\n  int gotTimestampExpected{0};\n  int gotByteSeqExpected{0};\n};\n\nclass AsyncSocketErrMessageCallbackTest\n    : public ::testing::TestWithParam<AsyncSocketErrMessageCallbackTestParams> {\n public:\n  static std::vector<AsyncSocketErrMessageCallbackTestParams>\n  getTestingValues() {\n    std::vector<AsyncSocketErrMessageCallbackTestParams> vals;\n    // each socket err message triggers two socket callbacks:\n    //   (1) timestamp callback\n    //   (2) byteseq callback\n\n    // reset callback cases\n    // resetting the callback should prevent any further callbacks\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.resetCallbackAfter = 1;\n      params.gotTimestampExpected = 1;\n      params.gotByteSeqExpected = 0;\n      vals.push_back(params);\n    }\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.resetCallbackAfter = 2;\n      params.gotTimestampExpected = 1;\n      params.gotByteSeqExpected = 1;\n      vals.push_back(params);\n    }\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.resetCallbackAfter = 3;\n      params.gotTimestampExpected = 2;\n      params.gotByteSeqExpected = 1;\n      vals.push_back(params);\n    }\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.resetCallbackAfter = 4;\n      params.gotTimestampExpected = 2;\n      params.gotByteSeqExpected = 2;\n      vals.push_back(params);\n    }\n\n    // close socket cases\n    // closing the socket will prevent callbacks after the current err message\n    // callbacks (both timestamp and byteseq) are completed\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.closeSocketAfter = 1;\n      params.gotTimestampExpected = 1;\n      params.gotByteSeqExpected = 1;\n      vals.push_back(params);\n    }\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.closeSocketAfter = 2;\n      params.gotTimestampExpected = 1;\n      params.gotByteSeqExpected = 1;\n      vals.push_back(params);\n    }\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.closeSocketAfter = 3;\n      params.gotTimestampExpected = 2;\n      params.gotByteSeqExpected = 2;\n      vals.push_back(params);\n    }\n    {\n      AsyncSocketErrMessageCallbackTestParams params;\n      params.closeSocketAfter = 4;\n      params.gotTimestampExpected = 2;\n      params.gotByteSeqExpected = 2;\n      vals.push_back(params);\n    }\n    return vals;\n  }\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    ErrMessageTests,\n    AsyncSocketErrMessageCallbackTest,\n    ::testing::ValuesIn(AsyncSocketErrMessageCallbackTest::getTestingValues()));\n\nclass TestErrMessageCallback : public folly::AsyncSocket::ErrMessageCallback {\n public:\n  TestErrMessageCallback()\n      : exception_(folly::AsyncSocketException::UNKNOWN, \"none\") {}\n\n  void errMessage(const cmsghdr& cmsg) noexcept override {\n    if (cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_TIMESTAMPING) {\n      gotTimestamp_++;\n      checkResetCallback();\n      checkCloseSocket();\n    } else if (\n        (cmsg.cmsg_level == SOL_IP && cmsg.cmsg_type == IP_RECVERR) ||\n        (cmsg.cmsg_level == SOL_IPV6 && cmsg.cmsg_type == IPV6_RECVERR)) {\n      gotByteSeq_++;\n      checkResetCallback();\n      checkCloseSocket();\n    }\n  }\n\n  void errMessageError(\n      const folly::AsyncSocketException& ex) noexcept override {\n    exception_ = ex;\n  }\n\n  void checkResetCallback() noexcept {\n    if (socket_ != nullptr && resetCallbackAfter_ != -1 &&\n        gotTimestamp_ + gotByteSeq_ == resetCallbackAfter_) {\n      socket_->setErrMessageCB(nullptr);\n    }\n  }\n\n  void checkCloseSocket() noexcept {\n    if (socket_ != nullptr && closeSocketAfter_ != -1 &&\n        gotTimestamp_ + gotByteSeq_ == closeSocketAfter_) {\n      socket_->close();\n    }\n  }\n\n  folly::AsyncSocket* socket_{nullptr};\n  folly::AsyncSocketException exception_;\n  int gotTimestamp_{0};\n  int gotByteSeq_{0};\n  int resetCallbackAfter_{-1};\n  int closeSocketAfter_{-1};\n};\n\nTEST_P(AsyncSocketErrMessageCallbackTest, ErrMessageCallback) {\n  TestServer server;\n\n  // connect()\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n  LOG(INFO) << \"Client socket fd=\" << socket->getNetworkSocket();\n\n  // Let the socket\n  evb.loop();\n\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  // Set read callback to keep the socket subscribed for event\n  // notifications. Though we're no planning to read anything from\n  // this side of the connection.\n  ReadCallback rcb(1);\n  socket->setReadCB(&rcb);\n\n  // Set up timestamp callbacks\n  TestErrMessageCallback errMsgCB;\n  socket->setErrMessageCB(&errMsgCB);\n  ASSERT_EQ(\n      socket->getErrMessageCallback(),\n      static_cast<folly::AsyncSocket::ErrMessageCallback*>(&errMsgCB));\n\n  // set the number of error messages before socket is closed or callback reset\n  const auto testParams = GetParam();\n  errMsgCB.socket_ = socket.get();\n  if (testParams.resetCallbackAfter.has_value()) {\n    errMsgCB.resetCallbackAfter_ = testParams.resetCallbackAfter.value();\n  }\n  if (testParams.closeSocketAfter.has_value()) {\n    errMsgCB.closeSocketAfter_ = testParams.closeSocketAfter.value();\n  }\n\n  // Enable timestamp notifications\n  ASSERT_NE(socket->getNetworkSocket(), NetworkSocket());\n  int flags = folly::netops::SOF_TIMESTAMPING_OPT_ID |\n      folly::netops::SOF_TIMESTAMPING_OPT_TSONLY |\n      folly::netops::SOF_TIMESTAMPING_SOFTWARE |\n      folly::netops::SOF_TIMESTAMPING_OPT_CMSG |\n      folly::netops::SOF_TIMESTAMPING_TX_SCHED;\n  SocketOptionKey tstampingOpt = {SOL_SOCKET, SO_TIMESTAMPING};\n  EXPECT_EQ(tstampingOpt.apply(socket->getNetworkSocket(), flags), 0);\n\n  // write()\n  std::vector<uint8_t> wbuf(128, 'a');\n  WriteCallback wcb;\n  // Send two packets to get two EOM notifications\n  socket->write(&wcb, wbuf.data(), wbuf.size() / 2);\n  socket->write(&wcb, wbuf.data() + wbuf.size() / 2, wbuf.size() / 2);\n\n  // Accept the connection.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  LOG(INFO) << \"Server socket fd=\" << acceptedSocket->getNetworkSocket();\n\n  // Loop\n  evb.loopOnce();\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n  // Check that we can read the data that was written to the socket\n  std::vector<uint8_t> rbuf(wbuf.size(), 0);\n  uint32_t bytesRead = acceptedSocket->readAll(rbuf.data(), rbuf.size());\n  ASSERT_EQ(bytesRead, wbuf.size());\n  ASSERT_TRUE(std::equal(wbuf.begin(), wbuf.end(), rbuf.begin()));\n\n  // Close both sockets\n  acceptedSocket->close();\n  socket->close();\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n\n  // Check for the timestamp notifications.\n  ASSERT_EQ(\n      errMsgCB.exception_.getType(), folly::AsyncSocketException::UNKNOWN);\n  ASSERT_EQ(errMsgCB.gotByteSeq_, testParams.gotByteSeqExpected);\n  ASSERT_EQ(errMsgCB.gotTimestamp_, testParams.gotTimestampExpected);\n}\n\nTEST(\n    AsyncSocketErrMessageCallbackTest, SetErrMessageCBFailsWithIoUringBackend) {\n  std::unique_ptr<EventBase> evb;\n  try {\n    evb = std::make_unique<EventBase>(EventBase::Options{}.setBackendFactory(\n        []() -> std::unique_ptr<EventBaseBackendBase> {\n          IoUringBackend::Options options;\n          options.setNativeAsyncSocketSupport(true);\n          return std::make_unique<IoUringBackend>(std::move(options));\n        }));\n  } catch (IoUringBackend::NotAvailable const&) {\n    GTEST_SKIP() << \"IoUringBackend not available\";\n  }\n\n  auto socket = AsyncSocket::newSocket(evb.get());\n  TestErrMessageCallback errMsgCB;\n  EXPECT_DEATH(socket->setErrMessageCB(&errMsgCB), \".*\");\n}\n\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n\n#if FOLLY_HAVE_SO_TIMESTAMPING\n\nclass AsyncSocketByteEventTest : public ::testing::Test {\n protected:\n  using MockDispatcher = ::testing::NiceMock<netops::test::MockDispatcher>;\n  using TestObserver = MockAsyncSocketLegacyLifecycleObserverForByteEvents;\n  using ByteEventType = AsyncSocket::ByteEvent::Type;\n\n  /**\n   * Components of a client connection to TestServer.\n   *\n   * Includes EventBase, client's AsyncSocket, and corresponding server socket.\n   */\n  class ClientConn {\n   public:\n    /**\n     * Call to sendmsg intercepted and recorded by netops::Dispatcher.\n     */\n    struct SendmsgInvocation {\n      // the iovecs in the msghdr\n      std::vector<iovec> iovs;\n\n      // WriteFlags encoded in msg_flags\n      WriteFlags writeFlagsInMsgFlags{WriteFlags::NONE};\n\n      // WriteFlags encoded in the msghdr's ancillary data\n      WriteFlags writeFlagsInAncillary{WriteFlags::NONE};\n    };\n\n    explicit ClientConn(\n        std::shared_ptr<TestServer> server,\n        std::shared_ptr<AsyncSocket> socket = nullptr,\n        std::shared_ptr<BlockingSocket> acceptedSocket = nullptr)\n        : server_(std::move(server)),\n          socket_(std::move(socket)),\n          acceptedSocket_(std::move(acceptedSocket)) {\n      if (!socket_) {\n        socket_ = AsyncSocket::newSocket(&getEventBase());\n      } else {\n        setReadCb();\n      }\n      socket_->setOverrideNetOpsDispatcher(netOpsDispatcher_);\n      netOpsDispatcher_->forwardToDefaultImpl();\n    }\n\n    void connect() {\n      CHECK_NOTNULL(socket_.get());\n      CHECK_NOTNULL(socket_->getEventBase());\n      socket_->connect(&connCb_, server_->getAddress(), 30);\n      socket_->getEventBase()->loop();\n      ASSERT_EQ(connCb_.state, STATE_SUCCEEDED);\n      setReadCb();\n\n      // accept the socket at the server\n      acceptedSocket_ = server_->accept();\n    }\n\n    void setReadCb() {\n      // Due to how libevent works, we currently need to be subscribed to\n      // EV_READ events in order to get error messages.\n      //\n      // TODO(bschlinker): Resolve this with libevent modification.\n      // See https://github.com/libevent/libevent/issues/1038 for details.\n      socket_->setReadCB(&readCb_);\n    }\n\n    void setMockTcpInfoDispatcher(\n        std::shared_ptr<MockTcpInfoDispatcher> mockTcpInfoDispatcher) {\n      socket_->setOverrideTcpInfoDispatcher(mockTcpInfoDispatcher);\n    }\n\n    std::shared_ptr<NiceMock<TestObserver>> attachObserver(\n        bool enableByteEvents, bool enablePrewrite = false) {\n      auto observer = AsyncSocketByteEventTest::attachObserver(\n          socket_.get(), enableByteEvents, enablePrewrite);\n      observers_.push_back(observer);\n      return observer;\n    }\n\n    /**\n     * Write to client socket and read at server.\n     */\n    void writeAtClientReadAtServer(\n        const iovec* iov, const size_t count, const WriteFlags writeFlags) {\n      CHECK_NOTNULL(socket_.get());\n      CHECK_NOTNULL(socket_->getEventBase());\n\n      // read buffer for server\n      std::vector<uint8_t> rbuf(iovsToNumBytes(iov, count), 0);\n      uint64_t rbufReadBytes = 0;\n\n      // write to the client socket, incrementally read at the server\n      WriteCallback wcb;\n      socket_->writev(&wcb, iov, count, writeFlags);\n      while (wcb.state == STATE_WAITING) {\n        socket_->getEventBase()->loopOnce();\n        rbufReadBytes += acceptedSocket_->readNoBlock(\n            rbuf.data() + rbufReadBytes, rbuf.size() - rbufReadBytes);\n      }\n      ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n\n      // finish reading, then compare\n      rbufReadBytes += acceptedSocket_->readAll(\n          rbuf.data() + rbufReadBytes, rbuf.size() - rbufReadBytes);\n      const auto cBuf = iovsToVector(iov, count);\n      ASSERT_EQ(rbufReadBytes, cBuf.size());\n      ASSERT_TRUE(std::equal(cBuf.begin(), cBuf.end(), rbuf.begin()));\n    }\n\n    /**\n     * Write to client socket and read at server.\n     */\n    void writeAtClientReadAtServer(\n        const std::vector<uint8_t>& wbuf, const WriteFlags writeFlags) {\n      iovec op;\n      op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n      op.iov_len = wbuf.size();\n      writeAtClientReadAtServer(&op, 1, writeFlags);\n    }\n\n    /**\n     * Write to client socket, echo at server, and wait for echo at client.\n     *\n     * Waiting for echo at client ensures that we have given opportunity for\n     * timestamps to be generated by the kernel.\n     */\n    void writeAtClientReadAtServerReflectReadAtClient(\n        const iovec* iov, const size_t count, const WriteFlags writeFlags) {\n      writeAtClientReadAtServer(iov, count, writeFlags);\n\n      // reflect\n      const auto wbuf = iovsToVector(iov, count);\n      acceptedSocket_->write(wbuf.data(), wbuf.size());\n      while (wbuf.size() != readCb_.dataRead()) {\n        socket_->getEventBase()->loopOnce();\n      }\n      readCb_.verifyData(wbuf.data(), wbuf.size());\n      readCb_.clearData();\n    }\n\n    /**\n     * Write to the client and wait for the client to read.\n     */\n    void writeAtServerReadAtClient(const iovec* iov, const size_t count) {\n      const auto wbuf = iovsToVector(iov, count);\n      acceptedSocket_->write(wbuf.data(), wbuf.size());\n      while (wbuf.size() != readCb_.dataRead()) {\n        socket_->getEventBase()->loopOnce();\n      }\n      readCb_.verifyData(wbuf.data(), wbuf.size());\n      readCb_.clearData();\n    }\n\n    /**\n     * Write to client socket, echo at server, and wait for echo at client.\n     *\n     * Waiting for echo at client ensures that we have given opportunity for\n     * timestamps to be generated by the kernel.\n     */\n    void writeAtClientReadAtServerReflectReadAtClient(\n        const std::vector<uint8_t>& wbuf, const WriteFlags writeFlags) {\n      iovec op = {};\n      op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n      op.iov_len = wbuf.size();\n      writeAtClientReadAtServerReflectReadAtClient(&op, 1, writeFlags);\n    }\n\n    /**\n     * Write directly to the NetworkSocket, bypassing AsyncSocket.\n     */\n    void writeAtClientDirectlyToNetworkSocket(\n        const std::vector<uint8_t>& wbuf) {\n      struct msghdr msg = {};\n      struct iovec iovec = {};\n      iovec.iov_base = (void*)wbuf.data();\n      iovec.iov_len = wbuf.size();\n\n      msg.msg_name = nullptr;\n      msg.msg_namelen = 0;\n      msg.msg_iov = &iovec;\n      msg.msg_iovlen = 1;\n      msg.msg_flags = 0;\n      msg.msg_controllen = 0;\n      msg.msg_control = nullptr;\n\n      auto ret = netops::Dispatcher::getDefaultInstance()->sendmsg(\n          socket_->getNetworkSocket(), &msg, 0);\n      ASSERT_EQ(ret, wbuf.size());\n    }\n\n    std::shared_ptr<AsyncSocket> getRawSocket() { return socket_; }\n\n    std::shared_ptr<BlockingSocket> getAcceptedSocket() {\n      return acceptedSocket_;\n    }\n\n    EventBase& getEventBase() {\n      static EventBase evb; // use same EventBase for all client sockets\n      return evb;\n    }\n\n    std::shared_ptr<MockDispatcher> getNetOpsDispatcher() const {\n      return netOpsDispatcher_;\n    }\n\n    /**\n     * Get recorded SendmsgInvocations.\n     */\n    const std::vector<SendmsgInvocation>& getSendmsgInvocations() {\n      return sendmsgInvocations_;\n    }\n\n    /**\n     * Get successful error queue reads.\n     */\n    int getErrorQueueReads() { return errorQueueReads_; }\n\n    /**\n     * Expect a call to setsockopt with optname SO_TIMESTAMPING.\n     */\n    void netOpsExpectTimestampingSetSockOpt() {\n      // must whitelist other calls\n      EXPECT_CALL(*netOpsDispatcher_, setsockopt(_, _, _, _, _))\n          .Times(AnyNumber());\n      EXPECT_CALL(\n          *netOpsDispatcher_, setsockopt(_, SOL_SOCKET, SO_TIMESTAMPING, _, _))\n          .Times(1);\n    }\n\n    /**\n     * Expect NO calls to setsockopt with optname SO_TIMESTAMPING.\n     */\n    void netOpsExpectNoTimestampingSetSockOpt() {\n      // must whitelist other calls\n      EXPECT_CALL(*netOpsDispatcher_, setsockopt(_, _, _, _, _))\n          .Times(AnyNumber());\n      EXPECT_CALL(*netOpsDispatcher_, setsockopt(_, _, SO_TIMESTAMPING, _, _))\n          .Times(0);\n    }\n\n    /**\n     * Expect sendmsg to be called with the passed WriteFlags in ancillary data.\n     */\n    void netOpsExpectSendmsgWithAncillaryTsFlags(WriteFlags writeFlags) {\n      auto getMsgAncillaryTsFlags = std::bind(\n          (WriteFlags (*)(const struct msghdr* msg)) & ::getMsgAncillaryTsFlags,\n          std::placeholders::_1);\n      EXPECT_CALL(\n          *netOpsDispatcher_,\n          sendmsg(_, ResultOf(getMsgAncillaryTsFlags, Eq(writeFlags)), _))\n          .WillOnce(DoDefault());\n    }\n\n    /**\n     * When sendmsg is called, record details and then forward to real sendmsg.\n     *\n     * This creates a default action.\n     */\n    void netOpsOnSendmsgRecordIovecsAndFlagsAndFwd() {\n      ON_CALL(*netOpsDispatcher_, sendmsg(_, _, _))\n          .WillByDefault(\n              ::testing::Invoke(\n                  [this](NetworkSocket s, const msghdr* message, int flags) {\n                    recordSendmsgInvocation(s, message, flags);\n                    return netops::Dispatcher::getDefaultInstance()->sendmsg(\n                        s, message, flags);\n                  }));\n    }\n\n    /**\n     * When recvmsg is called, forward to real recv message and record details\n     * on return.\n     *\n     * This creates a default action.\n     */\n    void netOpsOnRecvmsg() {\n      ON_CALL(*netOpsDispatcher_, recvmsg(_, _, _))\n          .WillByDefault(\n              ::testing::Invoke(\n                  [this](NetworkSocket s, msghdr* message, int flags) {\n                    int ret = netops::Dispatcher::getDefaultInstance()->recvmsg(\n                        s, message, flags);\n                    recordRecvmsgInvocation(s, message, flags, ret);\n                    return ret;\n                  }));\n    }\n\n    void netOpsVerifyAndClearExpectations() {\n      Mock::VerifyAndClearExpectations(netOpsDispatcher_.get());\n    }\n\n   private:\n    void recordSendmsgInvocation(\n        NetworkSocket /* s */, const msghdr* message, int flags) {\n      SendmsgInvocation invoc = {};\n      invoc.iovs = getMsgIovecs(message);\n      invoc.writeFlagsInMsgFlags = msgFlagsToWriteFlags(flags);\n      invoc.writeFlagsInAncillary = getMsgAncillaryTsFlags(message);\n      sendmsgInvocations_.emplace_back(std::move(invoc));\n    }\n\n    void recordRecvmsgInvocation(\n        NetworkSocket /* s */,\n        msghdr* /* message */,\n        int flags,\n        int returnValue) {\n      if (flags == MSG_ERRQUEUE && returnValue >= 0) {\n        errorQueueReads_ += 1;\n      }\n    }\n\n    // server\n    std::shared_ptr<TestServer> server_;\n\n    // managed observers\n    std::vector<std::shared_ptr<TestObserver>> observers_;\n\n    // socket components\n    ConnCallback connCb_;\n    ReadCallback readCb_;\n    std::shared_ptr<MockDispatcher> netOpsDispatcher_{\n        std::make_shared<MockDispatcher>()};\n    std::shared_ptr<AsyncSocket> socket_;\n\n    // accepted socket at server\n    std::shared_ptr<BlockingSocket> acceptedSocket_;\n\n    // sendmsg invocations observed\n    std::vector<SendmsgInvocation> sendmsgInvocations_;\n\n    // successful error queue reads observer\n    int errorQueueReads_{0};\n  };\n\n  ClientConn getClientConn() { return ClientConn(server_); }\n\n  /**\n   * Static utility functions.\n   */\n\n  static std::shared_ptr<NiceMock<TestObserver>> attachObserver(\n      AsyncSocket* socket, bool enableByteEvents, bool enablePrewrite = false) {\n    AsyncSocket::LegacyLifecycleObserver::Config config = {};\n    config.byteEvents = enableByteEvents;\n    config.prewrite = enablePrewrite;\n    return std::make_shared<NiceMock<TestObserver>>(socket, config);\n  }\n\n  static std::vector<uint8_t> getHundredBytesOfData() {\n    return std::vector<uint8_t>(\n        kOneHundredCharacterString.begin(), kOneHundredCharacterString.end());\n  }\n\n  static std::vector<uint8_t> get10KBOfData() {\n    std::vector<uint8_t> vec;\n    vec.reserve(kOneHundredCharacterString.size() * 100);\n    for (auto i = 0; i < 100; i++) {\n      vec.insert(\n          vec.end(),\n          kOneHundredCharacterString.begin(),\n          kOneHundredCharacterString.end());\n    }\n    CHECK_EQ(10000, vec.size());\n    return vec;\n  }\n\n  static std::vector<uint8_t> get1000KBOfData() {\n    std::vector<uint8_t> vec;\n    vec.reserve(kOneHundredCharacterString.size() * 10000);\n    for (auto i = 0; i < 10000; i++) {\n      vec.insert(\n          vec.end(),\n          kOneHundredCharacterString.begin(),\n          kOneHundredCharacterString.end());\n    }\n    CHECK_EQ(1000000, vec.size());\n    return vec;\n  }\n\n  static WriteFlags dropWriteFromFlags(WriteFlags writeFlags) {\n    return writeFlags & ~WriteFlags::TIMESTAMP_WRITE;\n  }\n\n  static std::vector<iovec> getMsgIovecs(const struct msghdr& msg) {\n    std::vector<iovec> iovecs;\n    for (size_t i = 0; i < msg.msg_iovlen; i++) {\n      iovecs.emplace_back(msg.msg_iov[i]);\n    }\n    return iovecs;\n  }\n\n  static std::vector<iovec> getMsgIovecs(const struct msghdr* msg) {\n    return getMsgIovecs(*msg);\n  }\n\n  static std::vector<uint8_t> iovsToVector(\n      const iovec* iov, const size_t count) {\n    std::vector<uint8_t> vec;\n    for (size_t i = 0; i < count; i++) {\n      if (iov[i].iov_len == 0) {\n        continue;\n      }\n      const auto ptr = reinterpret_cast<uint8_t*>(iov[i].iov_base);\n      vec.insert(vec.end(), ptr, ptr + iov[i].iov_len);\n    }\n    return vec;\n  }\n\n  static size_t iovsToNumBytes(const iovec* iov, const size_t count) {\n    size_t bytes = 0;\n    for (size_t i = 0; i < count; i++) {\n      bytes += iov[i].iov_len;\n    }\n    return bytes;\n  }\n\n  std::vector<AsyncSocket::ByteEvent> filterToWriteEvents(\n      const std::vector<AsyncSocket::ByteEvent>& input) {\n    std::vector<AsyncSocket::ByteEvent> result;\n    std::copy_if(\n        input.begin(),\n        input.end(),\n        std::back_inserter(result),\n        [](auto& event) {\n          return event.type == AsyncSocket::ByteEvent::WRITE;\n        });\n    return result;\n  }\n\n  // server\n  std::shared_ptr<TestServer> server_{std::make_shared<TestServer>()};\n};\n\nTEST_F(AsyncSocketByteEventTest, MsgFlagsToWriteFlags) {\n#ifdef MSG_MORE\n  EXPECT_EQ(WriteFlags::CORK, msgFlagsToWriteFlags(MSG_MORE));\n#endif // MSG_MORE\n\n#ifdef MSG_EOR\n  EXPECT_EQ(WriteFlags::EOR, msgFlagsToWriteFlags(MSG_EOR));\n#endif\n\n#ifdef MSG_ZEROCOPY\n  EXPECT_EQ(WriteFlags::WRITE_MSG_ZEROCOPY, msgFlagsToWriteFlags(MSG_ZEROCOPY));\n#endif\n\n#if defined(MSG_MORE) && defined(MSG_EOR)\n  EXPECT_EQ(\n      WriteFlags::CORK | WriteFlags::EOR,\n      msgFlagsToWriteFlags(MSG_MORE | MSG_EOR));\n#endif\n}\n\nTEST_F(AsyncSocketByteEventTest, GetMsgAncillaryTsFlags) {\n  auto ancillaryDataSize = CMSG_LEN(sizeof(uint32_t));\n  auto ancillaryData = reinterpret_cast<char*>(alloca(ancillaryDataSize));\n\n  auto getMsg = [&ancillaryDataSize, &ancillaryData](uint32_t sofFlags) {\n    struct msghdr msg = {};\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_iov = nullptr;\n    msg.msg_iovlen = 0;\n    msg.msg_flags = 0;\n    msg.msg_controllen = 0;\n    msg.msg_control = nullptr;\n    if (sofFlags) {\n      msg.msg_controllen = ancillaryDataSize;\n      msg.msg_control = ancillaryData;\n      struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n      CHECK_NOTNULL(cmsg);\n      cmsg->cmsg_level = SOL_SOCKET;\n      cmsg->cmsg_type = SO_TIMESTAMPING;\n      cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));\n      memcpy(CMSG_DATA(cmsg), &sofFlags, sizeof(sofFlags));\n    }\n    return msg;\n  };\n\n  // SCHED\n  {\n    auto msg = getMsg(folly::netops::SOF_TIMESTAMPING_TX_SCHED);\n    EXPECT_EQ(WriteFlags::TIMESTAMP_SCHED, getMsgAncillaryTsFlags(msg));\n  }\n\n  // TX\n  {\n    auto msg = getMsg(folly::netops::SOF_TIMESTAMPING_TX_SOFTWARE);\n    EXPECT_EQ(WriteFlags::TIMESTAMP_TX, getMsgAncillaryTsFlags(msg));\n  }\n\n  // ACK\n  {\n    auto msg = getMsg(folly::netops::SOF_TIMESTAMPING_TX_ACK);\n    EXPECT_EQ(WriteFlags::TIMESTAMP_ACK, getMsgAncillaryTsFlags(msg));\n  }\n\n  // SCHED + TX + ACK\n  {\n    auto msg = getMsg(\n        folly::netops::SOF_TIMESTAMPING_TX_SCHED |\n        folly::netops::SOF_TIMESTAMPING_TX_SOFTWARE |\n        folly::netops::SOF_TIMESTAMPING_TX_ACK);\n    EXPECT_EQ(\n        WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_TX |\n            WriteFlags::TIMESTAMP_ACK,\n        getMsgAncillaryTsFlags(msg));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, ObserverAttachedBeforeConnect) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  clientConn.connect();\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(4));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n\n  // write again to check offsets\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(8));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\nTEST_F(AsyncSocketByteEventTest, ObserverAttachedAfterConnect) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(4));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n\n  // write again to check offsets\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(8));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\nTEST_F(\n    AsyncSocketByteEventTest, ObserverAttachedBeforeConnectByteEventsDisabled) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  auto observer = clientConn.attachObserver(false /* enableByteEvents */);\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n\n  clientConn.connect(); // connect after observer attached\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(\n      WriteFlags::NONE); // events disabled\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  EXPECT_THAT(observer->byteEvents, IsEmpty());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // now enable ByteEvents with another observer, then write again\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer2 = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled); // observer 1 doesn't want\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  EXPECT_EQ(1, observer2->byteEventsEnabledCalled); // should be set\n  EXPECT_EQ(0, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  EXPECT_NE(WriteFlags::NONE, flags);\n  EXPECT_NE(WriteFlags::NONE, dropWriteFromFlags(flags));\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // expect no ByteEvents for first observer, four for the second\n  EXPECT_THAT(observer->byteEvents, IsEmpty());\n  EXPECT_THAT(observer2->byteEvents, SizeIs(4));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\nTEST_F(\n    AsyncSocketByteEventTest, ObserverAttachedAfterConnectByteEventsDisabled) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n\n  clientConn.connect(); // connect before observer attached\n\n  auto observer = clientConn.attachObserver(false /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(\n      WriteFlags::NONE); // events disabled\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  EXPECT_THAT(observer->byteEvents, IsEmpty());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // now enable ByteEvents with another observer, then write again\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer2 = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled); // observer 1 doesn't want\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  EXPECT_EQ(1, observer2->byteEventsEnabledCalled); // should be set\n  EXPECT_EQ(0, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  EXPECT_NE(WriteFlags::NONE, flags);\n  EXPECT_NE(WriteFlags::NONE, dropWriteFromFlags(flags));\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // expect no ByteEvents for first observer, four for the second\n  EXPECT_THAT(observer->byteEvents, IsEmpty());\n  EXPECT_THAT(observer2->byteEvents, SizeIs(4));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(1U, observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\nTEST_F(AsyncSocketByteEventTest, ObserverAttachedAfterWrite) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect(); // connect before observer attached\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(\n      WriteFlags::NONE); // events disabled\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  EXPECT_THAT(observer->byteEvents, SizeIs(4));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\nTEST_F(AsyncSocketByteEventTest, ObserverAttachedAfterClose) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  clientConn.getRawSocket()->close();\n  EXPECT_TRUE(clientConn.getRawSocket()->isClosedBySelf());\n\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n}\n\nTEST_F(AsyncSocketByteEventTest, MultipleObserverAttached) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(50, 'a');\n\n  // attach observer 1 before connect\n  auto clientConn = getClientConn();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  clientConn.connect();\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // attach observer 2 after connect\n  auto observer2 = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer2->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // check observer1\n  EXPECT_THAT(observer->byteEvents, SizeIs(4));\n  EXPECT_EQ(49U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(49U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(49U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(49U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n\n  // check observer2\n  EXPECT_THAT(observer2->byteEvents, SizeIs(4));\n  EXPECT_EQ(\n      49U, observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(\n      49U, observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(49U, observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(49U, observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\n/**\n * Test when kernel offset (uint32_t) wraps around.\n */\nTEST_F(AsyncSocketByteEventTest, KernelOffsetWrap) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  const uint64_t wbufSize = 3000000;\n  const std::vector<uint8_t> wbuf(wbufSize, 'a');\n\n  // part 1: write close to the wrap point with no ByteEvents to speed things up\n  const uint64_t bytesToWritePt1 =\n      static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) -\n      (wbufSize * 5);\n  while (clientConn.getRawSocket()->getRawBytesWritten() < bytesToWritePt1) {\n    clientConn.writeAtClientReadAtServer(\n        wbuf, WriteFlags::NONE); // no reflect needed\n  }\n\n  // part 2: write over the wrap point with ByteEvents\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const uint64_t bytesToWritePt2 =\n      static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) +\n      (wbufSize * 5);\n  while (clientConn.getRawSocket()->getRawBytesWritten() < bytesToWritePt2) {\n    clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(\n        dropWriteFromFlags(flags));\n    clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n    const uint64_t expectedOffset =\n        clientConn.getRawSocket()->getRawBytesWritten() - 1;\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // part 3: one more write outside of a loop with extra checks\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  const auto expectedOffset =\n      clientConn.getRawSocket()->getRawBytesWritten() - 1;\n  EXPECT_LT(std::numeric_limits<uint32_t>::max(), expectedOffset);\n  EXPECT_EQ(\n      expectedOffset,\n      observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(\n      expectedOffset,\n      observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(\n      expectedOffset,\n      observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(\n      expectedOffset,\n      observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\n/**\n * Ensure that ErrMessageCallback still works when ByteEvents enabled.\n */\nTEST_F(AsyncSocketByteEventTest, ErrMessageCallbackStillTriggered) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  TestErrMessageCallback errMsgCB;\n  clientConn.getRawSocket()->setErrMessageCB(&errMsgCB);\n\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n\n  std::vector<uint8_t> wbuf(1, 'a');\n  EXPECT_NE(WriteFlags::NONE, flags);\n  EXPECT_NE(WriteFlags::NONE, dropWriteFromFlags(flags));\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // observer should get events\n  EXPECT_THAT(observer->byteEvents, SizeIs(4));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(0U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n\n  // err message callbach should get events, too\n  EXPECT_EQ(3, errMsgCB.gotByteSeq_);\n  EXPECT_EQ(3, errMsgCB.gotTimestamp_);\n\n  // write again, more events for both\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(8));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(1U, observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  EXPECT_EQ(6, errMsgCB.gotByteSeq_);\n  EXPECT_EQ(6, errMsgCB.gotTimestamp_);\n}\n\n/**\n * Ensure that ByteEvents disabled for unix sockets (not supported).\n */\nTEST_F(AsyncSocketByteEventTest, FailUnixSocket) {\n  std::shared_ptr<NiceMock<TestObserver>> observer;\n  auto netOpsDispatcher = std::make_shared<MockDispatcher>();\n\n  NetworkSocket fd[2];\n  EXPECT_EQ(netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fd), 0);\n  ASSERT_NE(fd[0], NetworkSocket());\n  ASSERT_NE(fd[1], NetworkSocket());\n  SCOPE_EXIT {\n    netops::close(fd[1]);\n  };\n\n  EXPECT_EQ(netops::set_socket_non_blocking(fd[0]), 0);\n  EXPECT_EQ(netops::set_socket_non_blocking(fd[1]), 0);\n\n  auto clientSocketRaw = AsyncSocket::newSocket(nullptr, fd[0]);\n  auto clientBlockingSocket = BlockingSocket(std::move(clientSocketRaw));\n  clientBlockingSocket.getSocket()->setOverrideNetOpsDispatcher(\n      netOpsDispatcher);\n\n  // make sure no SO_TIMESTAMPING setsockopt on observer attach\n  EXPECT_CALL(*netOpsDispatcher, setsockopt(_, _, _, _, _)).Times(AnyNumber());\n  EXPECT_CALL(\n      *netOpsDispatcher, setsockopt(_, SOL_SOCKET, SO_TIMESTAMPING, _, _))\n      .Times(0); // no calls\n  observer = attachObserver(\n      clientBlockingSocket.getSocket(), true /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(1, observer->byteEventsUnavailableCalled);\n  EXPECT_TRUE(observer->byteEventsUnavailableCalledEx.has_value());\n  Mock::VerifyAndClearExpectations(netOpsDispatcher.get());\n\n  // do a write, we should see it has no timestamp flags\n  const std::vector<uint8_t> wbuf(1, 'a');\n  EXPECT_CALL(*netOpsDispatcher, sendmsg(_, _, _))\n      .WillOnce(WithArgs<1>(Invoke([](const msghdr* message) {\n        EXPECT_EQ(WriteFlags::NONE, getMsgAncillaryTsFlags(*message));\n        return 1;\n      })));\n  clientBlockingSocket.write(\n      wbuf.data(),\n      wbuf.size(),\n      WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n          WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK);\n  Mock::VerifyAndClearExpectations(netOpsDispatcher.get());\n}\n\n/**\n * If socket timestamps already enabled, do not enable ByteEvents.\n */\nTEST_F(AsyncSocketByteEventTest, FailTimestampsAlreadyEnabled) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // enable timestamps via setsockopt\n  const uint32_t flags = folly::netops::SOF_TIMESTAMPING_OPT_ID |\n      folly::netops::SOF_TIMESTAMPING_OPT_TSONLY |\n      folly::netops::SOF_TIMESTAMPING_SOFTWARE |\n      folly::netops::SOF_TIMESTAMPING_RAW_HARDWARE |\n      folly::netops::SOF_TIMESTAMPING_OPT_TX_SWHW;\n  const auto ret = clientConn.getRawSocket()->setSockOpt(\n      SOL_SOCKET, SO_TIMESTAMPING, &flags);\n  EXPECT_EQ(0, ret);\n\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(1, observer->byteEventsUnavailableCalled); // fail\n  EXPECT_TRUE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  std::vector<uint8_t> wbuf(1, 'a');\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(WriteFlags::NONE);\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      wbuf,\n      WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n          WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, IsEmpty());\n}\n\n/**\n * Verify that ByteEvent information is properly copied during socket moves.\n */\n\nTEST_F(AsyncSocketByteEventTest, MoveByteEventsEnabled) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(50, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // observer with ByteEvents enabled\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // move the socket immediately and add an observer with ByteEvents enabled\n  auto clientConn2 = ClientConn(\n      server_,\n      AsyncSocket::UniquePtr(new AsyncSocket(clientConn.getRawSocket().get())),\n      clientConn.getAcceptedSocket());\n  auto observer2 = clientConn2.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer2->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write following move, make sure the offsets are correct\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(4)));\n  {\n    const auto expectedOffset = 49U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write again\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(8)));\n  {\n    const auto expectedOffset = 99U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, WriteThenDetachThenEnableByteEvents) {\n  const auto flags = WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_TX |\n      WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(20, 'a');\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // observer with ByteEvents enabled\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write\n  EXPECT_CALL(*clientConn.getNetOpsDispatcher(), recvmsg(_, _, _)).Times(0);\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  clientConn.writeAtClientReadAtServer(&op, 1, flags);\n\n  // now detach the fd and create a new AsyncSocket with the same fd and add an\n  // observer with ByteEvents enabled\n  auto fd = clientConn.getRawSocket().get()->detachNetworkSocket();\n  auto clientConn2 = ClientConn(\n      server_,\n      AsyncSocket::UniquePtr(new AsyncSocket(&clientConn.getEventBase(), fd)),\n      clientConn.getAcceptedSocket());\n  clientConn2.netOpsOnRecvmsg();\n  // initialize socket family from underlying network socket\n  clientConn2.getRawSocket()->cacheAddresses();\n\n  // byte events should not be enabled because the fd already has timestamping\n  // enabled (from when it was controlled by clientConn)\n  auto observer2 = clientConn2.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(0, observer2->byteEventsEnabledCalled);\n  EXPECT_EQ(1, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // now have the server reflect the data previously written by the client and\n  // check that the client is able to read the data\n  clientConn2.writeAtServerReadAtClient(&op, 1);\n  // now that we've read everything reflected by the server, loop once more to\n  // allow the error queue to be read\n  if (clientConn2.getErrorQueueReads() != 3) {\n    clientConn2.getEventBase().loopOnce();\n  }\n  // we should read three timestamping (SCHED, TX, ACK) messages from the\n  // error queue\n  EXPECT_EQ(clientConn2.getErrorQueueReads(), 3);\n}\n\nTEST_F(AsyncSocketByteEventTest, WriteThenDetachThenDoNotEnableByteEvents) {\n  const auto flags = WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_TX |\n      WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(20, 'a');\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // observer with ByteEvents enabled\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write\n  EXPECT_CALL(*clientConn.getNetOpsDispatcher(), recvmsg(_, _, _)).Times(0);\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  clientConn.writeAtClientReadAtServer(&op, 1, flags);\n\n  // now detach the fd and create a new AsyncSocket with the same fd\n  // do not enable byte events on the new socket\n  auto fd = clientConn.getRawSocket().get()->detachNetworkSocket();\n  auto clientConn2 = ClientConn(\n      server_,\n      AsyncSocket::UniquePtr(new AsyncSocket(&clientConn.getEventBase(), fd)),\n      clientConn.getAcceptedSocket());\n  clientConn2.netOpsOnRecvmsg();\n  // initialize socket family from underlying network socket\n  clientConn2.getRawSocket()->cacheAddresses();\n\n  // now have the server reflect the data previously written by the client and\n  // check that the client is able to read the data\n  clientConn2.writeAtServerReadAtClient(&op, 1);\n  // now that we've read everything reflected by the server, loop once more to\n  // allow the error queue to be read\n  if (clientConn2.getErrorQueueReads() != 3) {\n    clientConn2.getEventBase().loopOnce();\n  }\n  // we should read three timestamping (SCHED, TX, ACK) messages from the error\n  // queue\n  EXPECT_EQ(clientConn2.getErrorQueueReads(), 3);\n}\n\nTEST_F(AsyncSocketByteEventTest, WriteThenMoveByteEventsEnabled) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(50, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // observer with ByteEvents enabled\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(Ge(4)));\n  {\n    const auto expectedOffset = 49U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // now move the socket and add an observer with ByteEvents enabled\n  auto clientConn2 = ClientConn(\n      server_,\n      AsyncSocket::UniquePtr(\n          new AsyncSocket(std::move(clientConn.getRawSocket().get()))),\n      clientConn.getAcceptedSocket());\n  auto observer2 = clientConn2.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer2->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write following move, make sure the offsets are correct\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(4)));\n  {\n    const auto expectedOffset = 99U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write again\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(8)));\n  {\n    const auto expectedOffset = 149U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, MoveThenEnableByteEvents) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(50, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // observer with ByteEvents disabled\n  auto observer = clientConn.attachObserver(false /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // move the socket immediately and add an observer with ByteEvents enabled\n  auto clientConn2 = ClientConn(\n      server_,\n      AsyncSocket::UniquePtr(new AsyncSocket(clientConn.getRawSocket().get())),\n      clientConn.getAcceptedSocket());\n  auto observer2 = clientConn2.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer2->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write following move, make sure the offsets are correct\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(4)));\n  {\n    const auto expectedOffset = 49U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write again\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(8)));\n  {\n    const auto expectedOffset = 99U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, WriteThenMoveThenEnableByteEvents) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(50, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // observer with ByteEvents disabled\n  auto observer = clientConn.attachObserver(false /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write, ByteEvents disabled\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(\n      WriteFlags::NONE); // events diabled\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // now move the socket and add an observer with ByteEvents enabled\n  auto clientConn2 = ClientConn(\n      server_,\n      AsyncSocket::UniquePtr(new AsyncSocket(clientConn.getRawSocket().get())),\n      clientConn.getAcceptedSocket());\n  auto observer2 = clientConn2.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer2->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer2->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write following move, make sure the offsets are correct\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(4)));\n  {\n    const auto expectedOffset = 99U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write again\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer2->byteEvents, SizeIs(Ge(8)));\n  {\n    const auto expectedOffset = 149U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer2->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, NoObserverMoveThenEnableByteEvents) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(50, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // no observer\n\n  // move the socket immediately and add an observer with ByteEvents enabled\n  auto clientConn2 = ClientConn(\n      server_,\n      AsyncSocket::UniquePtr(new AsyncSocket(clientConn.getRawSocket().get())),\n      clientConn.getAcceptedSocket());\n  auto observer = clientConn2.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  // write following move, make sure the offsets are correct\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(Ge(4)));\n  {\n    const auto expectedOffset = 49U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n\n  // write again\n  clientConn2.netOpsExpectSendmsgWithAncillaryTsFlags(\n      dropWriteFromFlags(flags));\n  clientConn2.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn2.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(Ge(8)));\n  {\n    const auto expectedOffset = 99U;\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n    EXPECT_EQ(\n        expectedOffset,\n        observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n  }\n}\n\n/**\n * Inspect ByteEvent fields, including xTimestampRequested in WRITE events.\n *\n * See CheckByteEventDetailsRawBytesWrittenAndTriedToWrite and\n * AsyncSocketByteEventDetailsTest::CheckByteEventDetails as well.\n */\nTEST_F(AsyncSocketByteEventTest, CheckByteEventDetails) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n  const std::vector<uint8_t> wbuf(1, 'a');\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  EXPECT_NE(WriteFlags::NONE, dropWriteFromFlags(flags));\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(Eq(4)));\n  const auto expectedOffset = wbuf.size() - 1;\n\n  // check WRITE\n  {\n    auto maybeByteEvent = observer->getByteEventReceivedWithOffset(\n        expectedOffset, ByteEventType::WRITE);\n    ASSERT_TRUE(maybeByteEvent.has_value());\n    auto& byteEvent = maybeByteEvent.value();\n\n    EXPECT_EQ(ByteEventType::WRITE, byteEvent.type);\n    EXPECT_EQ(expectedOffset, byteEvent.offset);\n    EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n    EXPECT_LT(\n        std::chrono::steady_clock::now() - std::chrono::seconds(60),\n        byteEvent.ts);\n\n    EXPECT_EQ(flags, byteEvent.maybeWriteFlags);\n    EXPECT_TRUE(byteEvent.schedTimestampRequestedOnWrite());\n    EXPECT_TRUE(byteEvent.txTimestampRequestedOnWrite());\n    EXPECT_TRUE(byteEvent.ackTimestampRequestedOnWrite());\n\n    EXPECT_FALSE(byteEvent.maybeSoftwareTs.has_value());\n    EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n\n    // maybeRawBytesWritten and maybeRawBytesTriedToWrite are tested in\n    // CheckByteEventDetailsRawBytesWrittenAndTriedToWrite\n  }\n\n  // check SCHED, TX, ACK\n  for (const auto& byteEventType :\n       {ByteEventType::SCHED, ByteEventType::TX, ByteEventType::ACK}) {\n    auto maybeByteEvent =\n        observer->getByteEventReceivedWithOffset(expectedOffset, byteEventType);\n    ASSERT_TRUE(maybeByteEvent.has_value());\n    auto& byteEvent = maybeByteEvent.value();\n\n    EXPECT_EQ(byteEventType, byteEvent.type);\n    EXPECT_EQ(expectedOffset, byteEvent.offset);\n    EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n    EXPECT_LT(\n        std::chrono::steady_clock::now() - std::chrono::seconds(60),\n        byteEvent.ts);\n\n    EXPECT_FALSE(byteEvent.maybeWriteFlags.has_value());\n    EXPECT_DEATH((void)byteEvent.schedTimestampRequestedOnWrite(), \".*\");\n    EXPECT_DEATH((void)byteEvent.txTimestampRequestedOnWrite(), \".*\");\n    EXPECT_DEATH((void)byteEvent.ackTimestampRequestedOnWrite(), \".*\");\n\n    EXPECT_TRUE(byteEvent.maybeSoftwareTs.has_value());\n    EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n  }\n}\n\n/**\n * Inspect ByteEvent fields maybeRawBytesWritten and maybeRawBytesTriedToWrite.\n */\nTEST_F(\n    AsyncSocketByteEventTest,\n    CheckByteEventDetailsRawBytesWrittenAndTriedToWrite) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  struct ExpectedSendmsgInvocation {\n    size_t expectedTotalIovLen{0};\n    ssize_t returnVal{0}; // number of bytes written or error val\n    folly::Optional<size_t> maybeWriteEventExpectedOffset{};\n    folly::Optional<WriteFlags> maybeWriteEventExpectedFlags{};\n  };\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n\n  // first write\n  //\n  // no splits triggered by observer\n  //\n  // sendmsg will incrementally accept the bytes so we can test the values of\n  // maybeRawBytesWritten and maybeRawBytesTriedToWrite\n  {\n    // bytes written per sendmsg call: 20, 10, 50, -1 (EAGAIN), 11, 99\n    const std::vector<ExpectedSendmsgInvocation> expectedSendmsgInvocations{\n        // {\n        //    expectedTotalIovLen, returnVal,\n        //    maybeWriteEventExpectedOffset, maybeWriteEventExpectedFlags\n        // },\n        {100, 20, 19, flags},\n        {80, 10, 29, flags},\n        {70, 50, 79, flags},\n        {20, -1, folly::none, flags},\n        {20, 11, 90, flags},\n        {9, 9, 99, flags}};\n\n    // sendmsg will be called, we return # of bytes written\n    {\n      InSequence s;\n      for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n        EXPECT_CALL(\n            *(clientConn.getNetOpsDispatcher()),\n            sendmsg(\n                _,\n                Pointee(SendmsgMsghdrHasTotalIovLen(\n                    expectedInvocation.expectedTotalIovLen)),\n                _))\n            .WillOnce(::testing::InvokeWithoutArgs([expectedInvocation]() {\n              if (expectedInvocation.returnVal < 0) {\n                errno = EAGAIN; // returning error, set EAGAIN\n              }\n              return expectedInvocation.returnVal;\n            }));\n      }\n    }\n\n    // write\n    // writes will be intercepted, so we don't need to read at other end\n    WriteCallback wcb;\n    clientConn.getRawSocket()->write(\n        &wcb,\n        kOneHundredCharacterVec.data(),\n        kOneHundredCharacterVec.size(),\n        flags);\n    while (STATE_WAITING == wcb.state) {\n      clientConn.getRawSocket()->getEventBase()->loopOnce();\n    }\n    ASSERT_EQ(STATE_SUCCEEDED, wcb.state);\n\n    // check write events\n    for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n      if (expectedInvocation.returnVal < 0) {\n        // should be no WriteEvent since the return value was an error\n        continue;\n      }\n\n      ASSERT_TRUE(expectedInvocation.maybeWriteEventExpectedOffset.has_value());\n      const auto& expectedOffset =\n          *expectedInvocation.maybeWriteEventExpectedOffset;\n\n      auto maybeByteEvent = observer->getByteEventReceivedWithOffset(\n          expectedOffset, ByteEventType::WRITE);\n      ASSERT_TRUE(maybeByteEvent.has_value());\n      auto& byteEvent = maybeByteEvent.value();\n\n      EXPECT_EQ(ByteEventType::WRITE, byteEvent.type);\n      EXPECT_EQ(expectedOffset, byteEvent.offset);\n      EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n      EXPECT_LT(\n          std::chrono::steady_clock::now() - std::chrono::seconds(60),\n          byteEvent.ts);\n\n      EXPECT_EQ(\n          expectedInvocation.maybeWriteEventExpectedFlags,\n          byteEvent.maybeWriteFlags);\n      EXPECT_TRUE(byteEvent.schedTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.txTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.ackTimestampRequestedOnWrite());\n\n      EXPECT_FALSE(byteEvent.maybeSoftwareTs.has_value());\n      EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n\n      // what we really want to test\n      EXPECT_EQ(\n          folly::to_unsigned(expectedInvocation.returnVal),\n          byteEvent.maybeRawBytesWritten);\n      EXPECT_EQ(\n          expectedInvocation.expectedTotalIovLen,\n          byteEvent.maybeRawBytesTriedToWrite);\n    }\n  }\n\n  // everything should have occurred by now\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // second write\n  //\n  // sendmsg will incrementally accept the bytes so we can test the values of\n  // maybeRawBytesWritten and maybeRawBytesTriedToWrite\n  {\n    // bytes written per sendmsg call: 20, 30, 50\n    const std::vector<ExpectedSendmsgInvocation> expectedSendmsgInvocations{\n        {100, 20, 119, flags}, {80, 30, 149, flags}, {50, 50, 199, flags}};\n\n    // sendmsg will be called, we return # of bytes written\n    {\n      InSequence s;\n      for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n        EXPECT_CALL(\n            *(clientConn.getNetOpsDispatcher()),\n            sendmsg(\n                _,\n                Pointee(SendmsgMsghdrHasTotalIovLen(\n                    expectedInvocation.expectedTotalIovLen)),\n                _))\n            .WillOnce(::testing::InvokeWithoutArgs([expectedInvocation]() {\n              return expectedInvocation.returnVal;\n            }));\n      }\n    }\n\n    // write\n    // writes will be intercepted, so we don't need to read at other end\n    WriteCallback wcb;\n    clientConn.getRawSocket()->write(\n        &wcb,\n        kOneHundredCharacterVec.data(),\n        kOneHundredCharacterVec.size(),\n        flags);\n    while (STATE_WAITING == wcb.state) {\n      clientConn.getRawSocket()->getEventBase()->loopOnce();\n    }\n    ASSERT_EQ(STATE_SUCCEEDED, wcb.state);\n\n    // check write events\n    for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n      ASSERT_TRUE(expectedInvocation.maybeWriteEventExpectedOffset.has_value());\n      const auto& expectedOffset =\n          *expectedInvocation.maybeWriteEventExpectedOffset;\n\n      auto maybeByteEvent = observer->getByteEventReceivedWithOffset(\n          expectedOffset, ByteEventType::WRITE);\n      ASSERT_TRUE(maybeByteEvent.has_value());\n      auto& byteEvent = maybeByteEvent.value();\n\n      EXPECT_EQ(ByteEventType::WRITE, byteEvent.type);\n      EXPECT_EQ(expectedOffset, byteEvent.offset);\n      EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n      EXPECT_LT(\n          std::chrono::steady_clock::now() - std::chrono::seconds(60),\n          byteEvent.ts);\n\n      EXPECT_EQ(\n          expectedInvocation.maybeWriteEventExpectedFlags,\n          byteEvent.maybeWriteFlags);\n      EXPECT_TRUE(byteEvent.schedTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.txTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.ackTimestampRequestedOnWrite());\n\n      EXPECT_FALSE(byteEvent.maybeSoftwareTs.has_value());\n      EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n\n      // what we really want to test\n      EXPECT_EQ(\n          folly::to_unsigned(expectedInvocation.returnVal),\n          byteEvent.maybeRawBytesWritten);\n      EXPECT_EQ(\n          expectedInvocation.expectedTotalIovLen,\n          byteEvent.maybeRawBytesTriedToWrite);\n    }\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, SplitIoVecArraySingleIoVec) {\n  // get srciov from lambda to enable us to keep it const during test\n  const char* buf = kOneHundredCharacterString.c_str();\n  auto getSrcIov = [&buf]() {\n    std::vector<struct iovec> srcIov(2);\n    srcIov[0].iov_base = const_cast<void*>(static_cast<const void*>(buf));\n    srcIov[0].iov_len = kOneHundredCharacterString.size();\n    return srcIov;\n  };\n\n  std::vector<struct iovec> srcIov = getSrcIov();\n  const auto data = srcIov.data();\n\n  // split 0 -> 0 (first byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 0, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(buf, dstIov[0].iov_base);\n  }\n\n  // split 0 -> 49 (50th byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 49, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(50, dstIov[0].iov_len);\n  }\n\n  // split 0 -> 98 (penultimate byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 98, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(99, dstIov[0].iov_len);\n  }\n\n  // split 0 -> 99 (pointless split)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 99, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(srcIov[0].iov_len, dstIov[0].iov_len);\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, SplitIoVecArrayMultiIoVecInvalid) {\n  // get srciov from lambda to enable us to keep it const during test\n  const char* buf = kOneHundredCharacterString.c_str();\n  auto getSrcIov = [&buf]() {\n    std::vector<struct iovec> srcIov(4);\n    srcIov[0].iov_base = const_cast<void*>(static_cast<const void*>(buf));\n    srcIov[0].iov_len = 50;\n    srcIov[1].iov_base = const_cast<void*>(static_cast<const void*>(buf + 50));\n    srcIov[1].iov_len = 50;\n    return srcIov;\n  };\n\n  std::vector<struct iovec> srcIov = getSrcIov();\n  const auto data = srcIov.data();\n\n  // dstIov.size() < srcIov.size(); this is not allowed\n  std::vector<struct iovec> dstIov(1);\n  size_t dstIovCount = dstIov.size();\n  EXPECT_LT(dstIovCount, srcIov.size());\n  EXPECT_DEATH(\n      AsyncSocket::splitIovecArray(\n          0, 0, data, srcIov.size(), dstIov.data(), dstIovCount),\n      \".*\");\n}\n\nTEST_F(AsyncSocketByteEventTest, SplitIoVecArrayMultiIoVec) {\n  // get srciov from lambda to enable us to keep it const during test\n  const char* buf = kOneHundredCharacterString.c_str();\n  auto getSrcIov = [&buf]() {\n    std::vector<struct iovec> srcIov(4);\n    srcIov[0].iov_base = const_cast<void*>(static_cast<const void*>(buf));\n    srcIov[0].iov_len = 25;\n    srcIov[1].iov_base = const_cast<void*>(static_cast<const void*>(buf + 25));\n    srcIov[1].iov_len = 25;\n    srcIov[2].iov_base = const_cast<void*>(static_cast<const void*>(buf + 50));\n    srcIov[2].iov_len = 25;\n    srcIov[3].iov_base = const_cast<void*>(static_cast<const void*>(buf + 75));\n    srcIov[3].iov_len = 25;\n    return srcIov;\n  };\n\n  std::vector<struct iovec> srcIov = getSrcIov();\n  const auto data = srcIov.data();\n\n  // split 0 -> 0 (first byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 0, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(buf, dstIov[0].iov_base);\n  }\n\n  // split 0 -> 98 (penultimate byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 98, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(4, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(srcIov[0].iov_len, dstIov[0].iov_len);\n    EXPECT_EQ(srcIov[1].iov_base, dstIov[1].iov_base);\n    EXPECT_EQ(srcIov[1].iov_len, dstIov[1].iov_len);\n    EXPECT_EQ(srcIov[2].iov_base, dstIov[2].iov_base);\n    EXPECT_EQ(srcIov[2].iov_len, dstIov[2].iov_len);\n\n    // last iovec is different\n    EXPECT_EQ(24, dstIov[3].iov_len);\n    EXPECT_EQ(srcIov[3].iov_base, dstIov[3].iov_base);\n  }\n\n  // split 0 -> 99 (pointless split)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 99, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(4, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(srcIov[0].iov_len, dstIov[0].iov_len);\n    EXPECT_EQ(srcIov[1].iov_base, dstIov[1].iov_base);\n    EXPECT_EQ(srcIov[1].iov_len, dstIov[1].iov_len);\n    EXPECT_EQ(srcIov[2].iov_base, dstIov[2].iov_base);\n    EXPECT_EQ(srcIov[2].iov_len, dstIov[2].iov_len);\n    EXPECT_EQ(srcIov[3].iov_base, dstIov[3].iov_base);\n    EXPECT_EQ(srcIov[3].iov_len, dstIov[3].iov_len);\n  }\n\n  //\n  // test when endOffset is near a iovec boundary\n  //\n\n  // split 0 -> 49 (50th byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 49, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(2, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(srcIov[0].iov_len, dstIov[0].iov_len);\n    EXPECT_EQ(srcIov[1].iov_base, dstIov[1].iov_base);\n    EXPECT_EQ(srcIov[1].iov_len, dstIov[1].iov_len);\n  }\n\n  // split 0 -> 50 (51st byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 50, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(3, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(srcIov[0].iov_len, dstIov[0].iov_len);\n    EXPECT_EQ(srcIov[1].iov_base, dstIov[1].iov_base);\n    EXPECT_EQ(srcIov[1].iov_len, dstIov[1].iov_len);\n\n    // last iovec is one byte\n    EXPECT_EQ(1, dstIov[2].iov_len);\n    EXPECT_EQ(srcIov[2].iov_base, dstIov[2].iov_base);\n  }\n\n  // split 0 -> 51 (52nd byte)\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        0, 51, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(3, dstIovCount);\n    EXPECT_EQ(srcIov[0].iov_base, dstIov[0].iov_base);\n    EXPECT_EQ(srcIov[0].iov_len, dstIov[0].iov_len);\n    EXPECT_EQ(srcIov[1].iov_base, dstIov[1].iov_base);\n    EXPECT_EQ(srcIov[1].iov_len, dstIov[1].iov_len);\n\n    // last iovec is two bytes\n    EXPECT_EQ(2, dstIov[2].iov_len);\n    EXPECT_EQ(srcIov[2].iov_base, dstIov[2].iov_base);\n  }\n\n  //\n  // test when startOffset is near a iovec boundary\n  //\n\n  // split 49 -> 99\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        49, 99, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(3, dstIovCount);\n\n    // first dst iovec is one byte, starts 24 bytes in to the second src iovec\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[1].iov_base) + 24)));\n\n    // second dst iovec is third src iovec\n    // third dst iovec is fourth src iovec\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[2].iov_base);\n    EXPECT_EQ(dstIov[1].iov_len, srcIov[2].iov_len);\n    EXPECT_EQ(dstIov[2].iov_base, srcIov[3].iov_base);\n    EXPECT_EQ(dstIov[2].iov_len, srcIov[3].iov_len);\n  }\n\n  // split 50 -> 99\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        50, 99, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(2, dstIovCount);\n\n    // first dst iovec is third src iovec\n    // second dst iovec is fourth src iovec\n    EXPECT_EQ(dstIov[0].iov_base, srcIov[2].iov_base);\n    EXPECT_EQ(dstIov[0].iov_len, srcIov[2].iov_len);\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[3].iov_base);\n    EXPECT_EQ(dstIov[1].iov_len, srcIov[3].iov_len);\n  }\n\n  // split 51 -> 99\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        51, 99, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(2, dstIovCount);\n\n    // first dst iovec is 24 bytes, starts 1 byte in to the third src iovec\n    EXPECT_EQ(24, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[2].iov_base) + 1)));\n\n    // second dst iovec is fourth src iovec\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[3].iov_base);\n    EXPECT_EQ(dstIov[1].iov_len, srcIov[3].iov_len);\n  }\n\n  //\n  // test when startOffset and endOffset are near iovec boundaries\n  //\n\n  // split 49 -> 49\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        49, 49, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n\n    // first dst iovec is one byte, starts 24 bytes in to the second src iovec\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[1].iov_base) + 24)));\n  }\n\n  // split 49 -> 50\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        49, 50, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(2, dstIovCount);\n\n    // first dst iovec is one byte, starts 24 bytes in to the second src iovec\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[1].iov_base) + 24)));\n\n    // second iovec is one byte, starts at the third src iovec\n    EXPECT_EQ(1, dstIov[1].iov_len);\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[2].iov_base);\n  }\n\n  // split 49 -> 51\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        49, 51, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(2, dstIovCount);\n\n    // first dst iovec is one byte, starts 24 bytes in to the second src iovec\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[1].iov_base) + 24)));\n\n    // second iovec is two bytes, starts at the third src iovec\n    EXPECT_EQ(2, dstIov[1].iov_len);\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[2].iov_base);\n  }\n\n  // split 50 -> 50\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        50, 50, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n\n    // first dst iovec is one byte, starts at the third src iovec\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(dstIov[0].iov_base, srcIov[2].iov_base);\n  }\n\n  // split 50 -> 51\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        50, 51, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n\n    // first dst iovec is two bytes, starts at the third src iovec\n    EXPECT_EQ(2, dstIov[0].iov_len);\n    EXPECT_EQ(dstIov[0].iov_base, srcIov[2].iov_base);\n  }\n\n  // split 51 -> 51\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        51, 51, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(1, dstIovCount);\n\n    // first dst iovec is one byte, starts 1 byte into the third src iovec\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[2].iov_base) + 1)));\n  }\n\n  // split 48 -> 98\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        48, 98, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(3, dstIovCount);\n\n    // first dst iovec is two bytes, starts 23 bytes in to the second src iovec\n    EXPECT_EQ(2, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[1].iov_base) + 23)));\n\n    // second dst iovec is third src iovec\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[2].iov_base);\n    EXPECT_EQ(dstIov[1].iov_len, srcIov[2].iov_len);\n\n    // third dst iovec is 24 bytes, starts at the fourth src iovec\n    EXPECT_EQ(24, dstIov[2].iov_len);\n    EXPECT_EQ(dstIov[2].iov_base, srcIov[3].iov_base);\n  }\n\n  // split 49 -> 98\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        49, 98, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(3, dstIovCount);\n\n    // first dst iovec is one byte, starts 24 bytes in to the second src iovec\n    EXPECT_EQ(1, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[1].iov_base) + 24)));\n\n    // second dst iovec is third src iovec\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[2].iov_base);\n    EXPECT_EQ(dstIov[1].iov_len, srcIov[2].iov_len);\n\n    // third dst iovec is 24 bytes, starts at the fourth src iovec\n    EXPECT_EQ(24, dstIov[2].iov_len);\n    EXPECT_EQ(dstIov[2].iov_base, srcIov[3].iov_base);\n  }\n\n  // split 50 -> 98\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        50, 98, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(2, dstIovCount);\n\n    // first dst iovec is third src iovec\n    EXPECT_EQ(dstIov[0].iov_base, srcIov[2].iov_base);\n    EXPECT_EQ(dstIov[0].iov_len, srcIov[2].iov_len);\n\n    // second dst iovec is 24 bytes, starts at the fourth src iovec\n    EXPECT_EQ(24, dstIov[1].iov_len);\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[3].iov_base);\n  }\n\n  // split 51 -> 98\n  {\n    std::vector<struct iovec> dstIov(4);\n    size_t dstIovCount = dstIov.size();\n    AsyncSocket::splitIovecArray(\n        51, 98, data, srcIov.size(), dstIov.data(), dstIovCount);\n\n    ASSERT_EQ(2, dstIovCount);\n\n    // first dst iovec is 24 bytes, starts 1 byte in to the third src iovec\n    EXPECT_EQ(24, dstIov[0].iov_len);\n    EXPECT_EQ(\n        dstIov[0].iov_base,\n        const_cast<void*>(static_cast<const void*>(\n            reinterpret_cast<uint8_t*>(srcIov[2].iov_base) + 1)));\n\n    // second dst iovec is 24 bytes, starts at the fourth src iovec\n    EXPECT_EQ(24, dstIov[1].iov_len);\n    EXPECT_EQ(dstIov[1].iov_base, srcIov[3].iov_base);\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, SendmsgMatchers) {\n  // empty\n  {\n    const ClientConn::SendmsgInvocation sendmsgInvoc = {};\n    // length\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocHasTotalIovLen(size_t(0)));\n\n    // iov first byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovFirstByte(kOneHundredCharacterVec.data())));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovFirstByte(kOneHundredCharacterVec.data() + 5)));\n\n    // iov last byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data())));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 5)));\n  }\n\n  // single iov, last byte = end of kOneHundredCharacterVec\n  {\n    struct iovec iov = {};\n    iov.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data())));\n    iov.iov_len = kOneHundredCharacterVec.size();\n    const ClientConn::SendmsgInvocation sendmsgInvoc = {.iovs = {iov}};\n\n    struct msghdr msg = {};\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_iov = const_cast<struct iovec*>(sendmsgInvoc.iovs.data());\n    msg.msg_iovlen = sendmsgInvoc.iovs.size();\n\n    // length\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocHasTotalIovLen(size_t(100)));\n    EXPECT_THAT(msg, SendmsgMsghdrHasTotalIovLen(size_t(100)));\n\n    // iov first byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovFirstByte(kOneHundredCharacterVec.data()));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovFirstByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size() -\n            1)));\n\n    // iov last byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data())));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovLastByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size() -\n            1));\n  }\n\n  // single iov, first and last byte = start of kOneHundredCharacterVec\n  {\n    struct iovec iov = {};\n    iov.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data())));\n    iov.iov_len = 1;\n    const ClientConn::SendmsgInvocation sendmsgInvoc = {.iovs = {iov}};\n\n    struct msghdr msg = {};\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_iov = const_cast<struct iovec*>(sendmsgInvoc.iovs.data());\n    msg.msg_iovlen = sendmsgInvoc.iovs.size();\n\n    // length\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocHasTotalIovLen(size_t(1)));\n    EXPECT_THAT(msg, SendmsgMsghdrHasTotalIovLen(size_t(1)));\n\n    // iov first byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovFirstByte(kOneHundredCharacterVec.data()));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovFirstByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size() -\n            1)));\n\n    // iov last byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data()));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocHasIovLastByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size() -\n            1)));\n  }\n\n  // single iov, first and last byte = end of kOneHundredCharacterVec\n  {\n    struct iovec iov = {};\n    iov.iov_base = const_cast<void*>(static_cast<const void*>(\n        (kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size())));\n    iov.iov_len = 1;\n    const ClientConn::SendmsgInvocation sendmsgInvoc = {.iovs = {iov}};\n\n    struct msghdr msg = {};\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_iov = const_cast<struct iovec*>(sendmsgInvoc.iovs.data());\n    msg.msg_iovlen = sendmsgInvoc.iovs.size();\n\n    // length\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocHasTotalIovLen(size_t(1)));\n    EXPECT_THAT(msg, SendmsgMsghdrHasTotalIovLen(size_t(1)));\n\n    // iov first byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovFirstByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size()));\n\n    // iov last byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovLastByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size()));\n  }\n\n  // two iov, (0 -> 0, 1 - > 99), last byte = end of kOneHundredCharacterVec\n  {\n    struct iovec iov1 = {};\n    iov1.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data())));\n    iov1.iov_len = 1;\n\n    struct iovec iov2 = {};\n    iov2.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data() + 1)));\n    iov2.iov_len = 99;\n\n    const ClientConn::SendmsgInvocation sendmsgInvoc = {.iovs = {iov1, iov2}};\n\n    struct msghdr msg = {};\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_iov = const_cast<struct iovec*>(sendmsgInvoc.iovs.data());\n    msg.msg_iovlen = sendmsgInvoc.iovs.size();\n\n    // length\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocHasTotalIovLen(size_t(100)));\n    EXPECT_THAT(msg, SendmsgMsghdrHasTotalIovLen(size_t(100)));\n\n    // iov first byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovFirstByte(kOneHundredCharacterVec.data()));\n\n    // iov last byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovLastByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size() -\n            1));\n  }\n\n  // two iov, (0 -> 49, 50 - > 99), last byte = end of kOneHundredCharacterVec\n  {\n    struct iovec iov1 = {};\n    iov1.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data())));\n    iov1.iov_len = 50;\n\n    struct iovec iov2 = {};\n    iov2.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data() + 50)));\n    iov2.iov_len = 50;\n\n    const ClientConn::SendmsgInvocation sendmsgInvoc = {.iovs = {iov1, iov2}};\n\n    struct msghdr msg = {};\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_iov = const_cast<struct iovec*>(sendmsgInvoc.iovs.data());\n    msg.msg_iovlen = sendmsgInvoc.iovs.size();\n\n    // length\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocHasTotalIovLen(size_t(100)));\n    EXPECT_THAT(msg, SendmsgMsghdrHasTotalIovLen(size_t(100)));\n\n    // iov first byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovFirstByte(kOneHundredCharacterVec.data()));\n\n    // iov last byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovLastByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size() -\n            1));\n  }\n\n  // two iov, (0 -> 49, 50 - > 98), last byte = penultimate byte\n  {\n    struct iovec iov1 = {};\n    iov1.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data())));\n    iov1.iov_len = 50;\n\n    struct iovec iov2 = {};\n    iov2.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data() + 50)));\n    iov2.iov_len = 49;\n\n    const ClientConn::SendmsgInvocation sendmsgInvoc = {.iovs = {iov1, iov2}};\n\n    struct msghdr msg = {};\n    msg.msg_name = nullptr;\n    msg.msg_namelen = 0;\n    msg.msg_iov = const_cast<struct iovec*>(sendmsgInvoc.iovs.data());\n    msg.msg_iovlen = sendmsgInvoc.iovs.size();\n\n    // length\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocHasTotalIovLen(size_t(99)));\n    EXPECT_THAT(msg, SendmsgMsghdrHasTotalIovLen(size_t(99)));\n\n    // iov first byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovFirstByte(kOneHundredCharacterVec.data()));\n\n    // iov last byte\n    EXPECT_THAT(\n        sendmsgInvoc,\n        SendmsgInvocHasIovLastByte(\n            kOneHundredCharacterVec.data() + kOneHundredCharacterVec.size() -\n            2));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, SendmsgInvocMsgFlagsEq) {\n  // empty\n  {\n    const ClientConn::SendmsgInvocation sendmsgInvoc;\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocMsgFlagsEq(WriteFlags::NONE));\n    EXPECT_THAT(sendmsgInvoc, Not(SendmsgInvocMsgFlagsEq(WriteFlags::CORK)));\n  }\n\n  // flag set\n  {\n    ClientConn::SendmsgInvocation sendmsgInvoc = {};\n    sendmsgInvoc.writeFlagsInMsgFlags = WriteFlags::CORK;\n    EXPECT_THAT(sendmsgInvoc, Not(SendmsgInvocMsgFlagsEq(WriteFlags::NONE)));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocMsgFlagsEq(\n            WriteFlags::EOR | WriteFlags::CORK))); // should be exact match\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocMsgFlagsEq(WriteFlags::CORK));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, SendmsgInvocAncillaryFlagsEq) {\n  // empty\n  {\n    const ClientConn::SendmsgInvocation sendmsgInvoc;\n    EXPECT_THAT(sendmsgInvoc, SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocAncillaryFlagsEq(WriteFlags::TIMESTAMP_TX)));\n  }\n\n  // flag set\n  {\n    ClientConn::SendmsgInvocation sendmsgInvoc = {};\n    sendmsgInvoc.writeFlagsInAncillary = WriteFlags::TIMESTAMP_TX;\n    EXPECT_THAT(\n        sendmsgInvoc, Not(SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE)));\n    EXPECT_THAT(\n        sendmsgInvoc,\n        Not(SendmsgInvocAncillaryFlagsEq(\n            WriteFlags::TIMESTAMP_TX |\n            WriteFlags::TIMESTAMP_ACK))); // should be exact match\n    EXPECT_THAT(\n        sendmsgInvoc, SendmsgInvocAncillaryFlagsEq(WriteFlags::TIMESTAMP_TX));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, ByteEventMatching) {\n  // offset = 0, type = WRITE\n  {\n    AsyncSocket::ByteEvent event = {};\n    event.type = ByteEventType::WRITE;\n    event.offset = 0;\n    EXPECT_THAT(event, ByteEventMatching(ByteEventType::WRITE, 0));\n\n    // not matching\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::WRITE, 10)));\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::TX, 0)));\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::ACK, 0)));\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::SCHED, 0)));\n  }\n\n  // offset = 10, type = TX\n  {\n    AsyncSocket::ByteEvent event = {};\n    event.type = ByteEventType::TX;\n    event.offset = 10;\n    EXPECT_THAT(event, ByteEventMatching(ByteEventType::TX, 10));\n\n    // not matching\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::TX, 0)));\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::WRITE, 10)));\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::ACK, 10)));\n    EXPECT_THAT(event, Not(ByteEventMatching(ByteEventType::SCHED, 10)));\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserver) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset == 0) {\n                  request.maybeOffsetToSplitWrite = 0;\n                } else if (state.startOffset <= 50) {\n                  request.maybeOffsetToSplitWrite = 50;\n                } else if (state.startOffset <= 98) {\n                  request.maybeOffsetToSplitWrite = 98;\n                }\n\n                request.writeFlagsToAddAtOffset = flags;\n                container.addRequest(request);\n              }));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      kOneHundredCharacterVec, WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data()),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags))),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 50),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags))),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 98),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags))),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n              SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  //\n  // should _not_ contain events for 99 as no prewrite for that\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      ElementsAre(\n          ByteEventMatching(ByteEventType::WRITE, 0),\n          ByteEventMatching(ByteEventType::WRITE, 50),\n          ByteEventMatching(ByteEventType::WRITE, 98)));\n}\n\n/**\n * Test explicitly that CORK (MSG_MORE) is set if write is split in middle.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverCorkIfSplitMiddle) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset <= 50) {\n                  request.maybeOffsetToSplitWrite = 50;\n                }\n                request.writeFlagsToAddAtOffset = flags;\n                container.addRequest(request);\n              }));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      kOneHundredCharacterVec, WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 50),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags))),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n              SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      ElementsAre(ByteEventMatching(ByteEventType::WRITE, 50)));\n}\n\n/**\n * Test explicitly that CORK (MSG_MORE) is set if write is split in middle.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverNoCorkIfSplitAtEnd) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset <= 99) {\n                  request.maybeOffsetToSplitWrite = 99;\n                }\n                request.writeFlagsToAddAtOffset = flags;\n                container.addRequest(request);\n              }));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      kOneHundredCharacterVec, WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(AllOf(\n          SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n          SendmsgInvocMsgFlagsEq(WriteFlags::NONE), // no cork!\n          SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags)))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      ElementsAre(ByteEventMatching(ByteEventType::WRITE, 99)));\n}\n\n/**\n * Test explicitly that split flags are NOT added if no split.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverNoSplitFlagsIfNoSplit) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& /* state */,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                request.writeFlagsToAddAtOffset = flags;\n                container.addRequest(request);\n              }));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      kOneHundredCharacterVec, WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(AllOf(\n          SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n          SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n          SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE))));\n}\n\n/**\n * Test more combinations of prewrite flags, including writeFlagsToAdd.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverFlagsOnAll) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset == 0) {\n                  request.maybeOffsetToSplitWrite = 0;\n                  request.writeFlagsToAddAtOffset |=\n                      WriteFlags::TIMESTAMP_WRITE;\n                } else if (state.startOffset <= 10) {\n                  request.maybeOffsetToSplitWrite = 10;\n                  request.writeFlagsToAddAtOffset |=\n                      WriteFlags::TIMESTAMP_SCHED;\n                } else if (state.startOffset <= 20) {\n                  request.writeFlagsToAddAtOffset |= WriteFlags::TIMESTAMP_TX;\n                  request.maybeOffsetToSplitWrite = 20;\n                } else if (state.startOffset <= 30) {\n                  request.writeFlagsToAddAtOffset |= WriteFlags::TIMESTAMP_ACK;\n                  request.maybeOffsetToSplitWrite = 30;\n                } else if (state.startOffset <= 40) {\n                  request.writeFlagsToAddAtOffset |= WriteFlags::TIMESTAMP_TX;\n                  request.writeFlagsToAdd |= WriteFlags::TIMESTAMP_WRITE;\n                  request.maybeOffsetToSplitWrite = 40;\n                } else {\n                  request.writeFlagsToAdd |= WriteFlags::TIMESTAMP_WRITE;\n                }\n\n                container.addRequest(request);\n              }));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      kOneHundredCharacterVec, WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data()),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 10),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::TIMESTAMP_SCHED)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 20),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::TIMESTAMP_TX)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 30),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::TIMESTAMP_ACK)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 40),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::TIMESTAMP_TX)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n              SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      ElementsAre(\n          ByteEventMatching(ByteEventType::WRITE, 0),\n          ByteEventMatching(ByteEventType::WRITE, 40),\n          ByteEventMatching(ByteEventType::WRITE, 99)));\n}\n\n/**\n * Test merging of write flags with those passed to AsyncSocket::write().\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverFlagsOnWrite) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  // first byte, observer adds TX and WRITE, onwards, it just adds WRITE\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset == 0) {\n                  request.maybeOffsetToSplitWrite = 0;\n                  request.writeFlagsToAddAtOffset |= WriteFlags::TIMESTAMP_TX;\n                }\n                request.writeFlagsToAdd |= WriteFlags::TIMESTAMP_WRITE;\n\n                container.addRequest(request);\n              }));\n\n  // application does a write with ACK and CORK set\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      kOneHundredCharacterVec, WriteFlags::CORK | WriteFlags::TIMESTAMP_ACK);\n\n  // make sure we have the merge\n  //   first write, TX is added\n  //   second write, CORK is passed through\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data()),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK), // set by split\n              SendmsgInvocAncillaryFlagsEq(\n                  WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK), // still set\n              SendmsgInvocAncillaryFlagsEq(\n                  dropWriteFromFlags(WriteFlags::TIMESTAMP_ACK)))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      ElementsAre(\n          ByteEventMatching(ByteEventType::WRITE, 0),\n          ByteEventMatching(ByteEventType::WRITE, 99)));\n}\n\n/**\n * Test invalid offset for prewrite, ensure death via CHECK.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverInvalidOffset) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                EXPECT_GT(200, state.endOffset);\n                request.maybeOffsetToSplitWrite = 200; // invalid\n                container.addRequest(request);\n              }));\n\n  // check will fail due to invalid offset\n  EXPECT_DEATH(\n      clientConn.writeAtClientReadAtServerReflectReadAtClient(\n          kOneHundredCharacterVec, WriteFlags::NONE),\n      \".*\");\n}\n\n/**\n * Test prewrite with multiple iovec.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverTwoIovec) {\n  // two iovec, each with half of the kOneHundredCharacterVec\n  std::vector<iovec> iovs;\n  {\n    iovec iov = {};\n    iov.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data())));\n    iov.iov_len = 50;\n    iovs.push_back(iov);\n  }\n  {\n    iovec iov = {};\n    iov.iov_base = const_cast<void*>(\n        static_cast<const void*>((kOneHundredCharacterVec.data() + 50)));\n    iov.iov_len = 50;\n    iovs.push_back(iov);\n  }\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset == 0) {\n                  request.maybeOffsetToSplitWrite = 0;\n                } else if (state.startOffset <= 49) {\n                  request.maybeOffsetToSplitWrite = 49;\n                } else if (state.startOffset <= 99) {\n                  request.maybeOffsetToSplitWrite = 99;\n                }\n\n                request.writeFlagsToAddAtOffset = flags;\n                container.addRequest(request);\n              }));\n\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      iovs.data(), iovs.size(), WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data()),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags))),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 49),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags))),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n              SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags)))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      ElementsAre(\n          ByteEventMatching(ByteEventType::WRITE, 0),\n          ByteEventMatching(ByteEventType::WRITE, 49),\n          ByteEventMatching(ByteEventType::WRITE, 99)));\n}\n\n/**\n * Test prewrite with large number of iovec to trigger malloc codepath.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteSingleObserverManyIovec) {\n  // make a long vector, 10000 bytes long\n  auto tenThousandByteVec = get10KBOfData();\n  ASSERT_THAT(tenThousandByteVec, SizeIs(10000));\n\n  // put each byte in the vector into its own iovec\n  std::vector<iovec> tenThousandIovec;\n  for (size_t i = 0; i < tenThousandByteVec.size(); i++) {\n    iovec iov = {};\n    iov.iov_base = tenThousandByteVec.data() + i;\n    iov.iov_len = 1;\n    tenThousandIovec.push_back(iov);\n  }\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset == 0) {\n                  request.maybeOffsetToSplitWrite = 0;\n                } else if (state.startOffset <= 1000) {\n                  request.maybeOffsetToSplitWrite = 1000;\n                } else if (state.startOffset <= 5000) {\n                  request.maybeOffsetToSplitWrite = 5000;\n                }\n\n                request.writeFlagsToAddAtOffset = flags;\n                container.addRequest(request);\n              }));\n\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      tenThousandIovec.data(), tenThousandIovec.size(), WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      AllOf(\n          Contains(AllOf(\n              SendmsgInvocHasIovLastByte(tenThousandByteVec.data()),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags)))),\n          Contains(AllOf(\n              SendmsgInvocHasIovLastByte(tenThousandByteVec.data() + 1000),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags)))),\n          Contains(AllOf(\n              SendmsgInvocHasIovLastByte(tenThousandByteVec.data() + 5000),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags)))),\n          Contains(AllOf(\n              SendmsgInvocHasIovLastByte(tenThousandByteVec.data() + 9999),\n              SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::NONE)))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  //\n  // should _not_ contain events for 99 as no prewrite for that\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      AllOf(\n          Contains(ByteEventMatching(ByteEventType::WRITE, 0)),\n          Contains(ByteEventMatching(ByteEventType::WRITE, 1000)),\n          Contains(ByteEventMatching(ByteEventType::WRITE, 5000))));\n}\n\nTEST_F(AsyncSocketByteEventTest, PrewriteMultipleObservers) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n\n  // five observers\n  // observer1 - 4 have byte events and prewrite enabled\n  // observer5 has byte events enabled\n  // observer6 has neither byte events or prewrite\n  auto observer1 = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  auto observer2 = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  auto observer3 = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  auto observer4 = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  auto observer5 = clientConn.attachObserver(\n      true /* enableByteEvents */, false /* enablePrewrite */);\n  auto observer6 = clientConn.attachObserver(\n      false /* enableByteEvents */, false /* enablePrewrite */);\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  // observer 1 wants TX timestamps at 25, 50, 75\n  ON_CALL(*observer1, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset <= 25) {\n                  request.maybeOffsetToSplitWrite = 25;\n                } else if (state.startOffset <= 50) {\n                  request.maybeOffsetToSplitWrite = 50;\n                } else if (state.startOffset <= 75) {\n                  request.maybeOffsetToSplitWrite = 75;\n                }\n                request.writeFlagsToAddAtOffset = WriteFlags::TIMESTAMP_TX;\n                container.addRequest(request);\n              }));\n\n  // observer 2 wants ACK timestamps at 35, 65, 75\n  ON_CALL(*observer2, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset <= 35) {\n                  request.maybeOffsetToSplitWrite = 35;\n                } else if (state.startOffset <= 65) {\n                  request.maybeOffsetToSplitWrite = 65;\n                } else if (state.startOffset <= 75) {\n                  request.maybeOffsetToSplitWrite = 75;\n                }\n                request.writeFlagsToAddAtOffset = WriteFlags::TIMESTAMP_ACK;\n                container.addRequest(request);\n              }));\n\n  // observer 3 wants WRITE and SCHED flag on every write that occurs\n  ON_CALL(*observer3, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& /* state */,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                request.writeFlagsToAdd =\n                    WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED;\n                container.addRequest(request);\n              }));\n\n  // observer 4 has prewrite but makes no requests\n  ON_CALL(*observer4, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& /* state */,\n                 AsyncSocketObserverInterface::\n                     PrewriteRequestContainer& /* container */) {\n                return; // do nothing\n              }));\n\n  // no calls for observer 5 or observer 6\n  EXPECT_CALL(*observer5, prewriteMock(_, _, _)).Times(0);\n  EXPECT_CALL(*observer6, prewriteMock(_, _, _)).Times(0);\n\n  // write\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      kOneHundredCharacterVec, WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      ElementsAre(\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 25),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(\n                  WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_TX)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 35),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(\n                  WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_ACK)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 50),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(\n                  WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_TX)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 65),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(\n                  WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_ACK)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 75),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(\n                  WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_TX |\n                  WriteFlags::TIMESTAMP_ACK)),\n          AllOf(\n              SendmsgInvocHasIovLastByte(kOneHundredCharacterVec.data() + 99),\n              SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n              SendmsgInvocAncillaryFlagsEq(WriteFlags::TIMESTAMP_SCHED))));\n\n  // verify WRITE events exist at the appropriate locations\n  // we verify timestamp events are generated elsewhere\n  for (const auto& observer : {observer1, observer2, observer3}) {\n    EXPECT_THAT(\n        filterToWriteEvents(observer->byteEvents),\n        ElementsAre(\n            ByteEventMatching(ByteEventType::WRITE, 25),\n            ByteEventMatching(ByteEventType::WRITE, 35),\n            ByteEventMatching(ByteEventType::WRITE, 50),\n            ByteEventMatching(ByteEventType::WRITE, 65),\n            ByteEventMatching(ByteEventType::WRITE, 75),\n            ByteEventMatching(ByteEventType::WRITE, 99)));\n  }\n}\n\n/**\n * Test prewrite with large write that enables testing of timestamps.\n *\n * We need to use a long vector to ensure that the kernel will not coalesce\n * the writes into a single SKB due to MSG_MORE.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteTimestampedByteEvents) {\n  // need a large block of data to ensure that MSG_MORE doesn't limit us\n  const auto hundredKBVec = get1000KBOfData();\n  ASSERT_THAT(hundredKBVec, SizeIs(1000000));\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  clientConn.netOpsOnSendmsgRecordIovecsAndFlagsAndFwd();\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n  ON_CALL(*observer, prewriteMock(_, _, _))\n      .WillByDefault(\n          testing::Invoke(\n              [](AsyncTransport*,\n                 const AsyncSocketObserverInterface::PrewriteState& state,\n                 AsyncSocketObserverInterface::PrewriteRequestContainer&\n                     container) {\n                AsyncSocketObserverInterface::PrewriteRequest request;\n                if (state.startOffset == 0) {\n                  request.maybeOffsetToSplitWrite = 0;\n                } else if (state.startOffset <= 500000) {\n                  request.maybeOffsetToSplitWrite = 500000;\n                } else {\n                  request.maybeOffsetToSplitWrite = 999999;\n                }\n\n                request.writeFlagsToAdd = flags;\n                container.addRequest(request);\n              }));\n\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(\n      hundredKBVec, WriteFlags::NONE);\n\n  EXPECT_THAT(\n      clientConn.getSendmsgInvocations(),\n      AllOf(\n          Contains(AllOf(\n              SendmsgInvocHasIovLastByte(hundredKBVec.data()),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags)))),\n          Contains(AllOf(\n              SendmsgInvocHasIovLastByte(hundredKBVec.data() + 500000),\n              SendmsgInvocMsgFlagsEq(WriteFlags::CORK),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags)))),\n          Contains(AllOf(\n              SendmsgInvocHasIovLastByte(hundredKBVec.data() + 999999),\n              SendmsgInvocMsgFlagsEq(WriteFlags::NONE),\n              SendmsgInvocAncillaryFlagsEq(dropWriteFromFlags(flags))))));\n\n  // verify WRITE events exist at the appropriate locations\n  EXPECT_THAT(\n      filterToWriteEvents(observer->byteEvents),\n      AllOf(\n          Contains(ByteEventMatching(ByteEventType::WRITE, 0)),\n          Contains(ByteEventMatching(ByteEventType::WRITE, 500000)),\n          Contains(ByteEventMatching(ByteEventType::WRITE, 999999))));\n\n  // verify SCHED, TX, and ACK events available at specified locations\n  EXPECT_THAT(\n      observer->byteEvents,\n      AllOf(\n          Contains(ByteEventMatching(ByteEventType::SCHED, 0)),\n          Contains(ByteEventMatching(ByteEventType::TX, 0)),\n          Contains(ByteEventMatching(ByteEventType::ACK, 0)),\n          Contains(ByteEventMatching(ByteEventType::SCHED, 500000)),\n          Contains(ByteEventMatching(ByteEventType::TX, 500000)),\n          Contains(ByteEventMatching(ByteEventType::ACK, 500000)),\n          Contains(ByteEventMatching(ByteEventType::SCHED, 999999)),\n          Contains(ByteEventMatching(ByteEventType::TX, 999999)),\n          Contains(ByteEventMatching(ByteEventType::ACK, 999999))));\n}\n\n/**\n * Test raw bytes written and bytes tried to write with prewrite.\n */\nTEST_F(AsyncSocketByteEventTest, PrewriteRawBytesWrittenAndTriedToWrite) {\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(\n      true /* enableByteEvents */, true /* enablePrewrite */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  struct ExpectedSendmsgInvocation {\n    size_t expectedTotalIovLen{0};\n    ssize_t returnVal{0}; // number of bytes written or error val\n    folly::Optional<size_t> maybeWriteEventExpectedOffset{};\n    folly::Optional<WriteFlags> maybeWriteEventExpectedFlags{};\n  };\n\n  const auto flags = WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK |\n      WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_WRITE;\n\n  // first write\n  //\n  // no splits triggered by observer\n  //\n  // sendmsg will incrementally accept the bytes so we can test the values of\n  // maybeRawBytesWritten and maybeRawBytesTriedToWrite\n  {\n    // bytes written per sendmsg call: 20, 10, 50, -1 (EAGAIN), 11, 99\n    const std::vector<ExpectedSendmsgInvocation> expectedSendmsgInvocations{\n        // {\n        //    expectedTotalIovLen, returnVal,\n        //    maybeWriteEventExpectedOffset, maybeWriteEventExpectedFlags\n        // },\n        {100, 20, 19, flags},\n        {80, 10, 29, flags},\n        {70, 50, 79, flags},\n        {20, -1, folly::none, flags},\n        {20, 11, 90, flags},\n        {9, 9, 99, flags}};\n\n    // prewrite will be called, we request all events\n    EXPECT_CALL(*observer, prewriteMock(_, _, _))\n        .Times(expectedSendmsgInvocations.size())\n        .WillRepeatedly(\n            testing::Invoke(\n                [](AsyncTransport*,\n                   const AsyncSocketObserverInterface::\n                       PrewriteState& /* state */,\n                   AsyncSocketObserverInterface::PrewriteRequestContainer&\n                       container) {\n                  AsyncSocketObserverInterface::PrewriteRequest request = {};\n                  request.writeFlagsToAdd = flags;\n                  container.addRequest(request);\n                }));\n\n    // sendmsg will be called, we return # of bytes written\n    {\n      InSequence s;\n      for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n        EXPECT_CALL(\n            *(clientConn.getNetOpsDispatcher()),\n            sendmsg(\n                _,\n                Pointee(SendmsgMsghdrHasTotalIovLen(\n                    expectedInvocation.expectedTotalIovLen)),\n                _))\n            .WillOnce(::testing::InvokeWithoutArgs([expectedInvocation]() {\n              if (expectedInvocation.returnVal < 0) {\n                errno = EAGAIN; // returning error, set EAGAIN\n              }\n              return expectedInvocation.returnVal;\n            }));\n      }\n    }\n\n    // write\n    // writes will be intercepted, so we don't need to read at other end\n    WriteCallback wcb;\n    clientConn.getRawSocket()->write(\n        &wcb,\n        kOneHundredCharacterVec.data(),\n        kOneHundredCharacterVec.size(),\n        WriteFlags::NONE);\n    while (STATE_WAITING == wcb.state) {\n      clientConn.getRawSocket()->getEventBase()->loopOnce();\n    }\n    ASSERT_EQ(STATE_SUCCEEDED, wcb.state);\n\n    // check write events\n    for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n      if (expectedInvocation.returnVal < 0) {\n        // should be no WriteEvent since the return value was an error\n        continue;\n      }\n\n      ASSERT_TRUE(expectedInvocation.maybeWriteEventExpectedOffset.has_value());\n      const auto& expectedOffset =\n          *expectedInvocation.maybeWriteEventExpectedOffset;\n\n      auto maybeByteEvent = observer->getByteEventReceivedWithOffset(\n          expectedOffset, ByteEventType::WRITE);\n      ASSERT_TRUE(maybeByteEvent.has_value());\n      auto& byteEvent = maybeByteEvent.value();\n\n      EXPECT_EQ(ByteEventType::WRITE, byteEvent.type);\n      EXPECT_EQ(expectedOffset, byteEvent.offset);\n      EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n      EXPECT_LT(\n          std::chrono::steady_clock::now() - std::chrono::seconds(60),\n          byteEvent.ts);\n\n      EXPECT_EQ(\n          expectedInvocation.maybeWriteEventExpectedFlags,\n          byteEvent.maybeWriteFlags);\n      EXPECT_TRUE(byteEvent.schedTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.txTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.ackTimestampRequestedOnWrite());\n\n      EXPECT_FALSE(byteEvent.maybeSoftwareTs.has_value());\n      EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n\n      // what we really want to test\n      EXPECT_EQ(\n          folly::to_unsigned(expectedInvocation.returnVal),\n          byteEvent.maybeRawBytesWritten);\n      EXPECT_EQ(\n          expectedInvocation.expectedTotalIovLen,\n          byteEvent.maybeRawBytesTriedToWrite);\n    }\n  }\n\n  // everything should have occurred by now\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // second write\n  //\n  // start offset is 100\n  //\n  // split at 150th byte triggered by observer\n  //\n  // sendmsg will incrementally accept the bytes so we can test the values of\n  // maybeRawBytesWritten and maybeRawBytesTriedToWrite\n  {\n    // due to the split at the 150th byte, we expect sendmsg invocation to\n    // only be called with bytes 100 -> 150 until after the 150th byte has been\n    // written; in addition, the socket only accepts 20 of the 50 bytes the\n    // first write.\n    //\n    // bytes written per sendmsg call: 20, 30, 50\n    const std::vector<ExpectedSendmsgInvocation> expectedSendmsgInvocations{\n        {50, 20, 119, flags | WriteFlags::CORK},\n        {30, 30, 149, flags | WriteFlags::CORK},\n        {50, 50, 199, flags}};\n\n    // prewrite will be called, split at 50th byte (offset = 49)\n    EXPECT_CALL(*observer, prewriteMock(_, _, _))\n        .Times(expectedSendmsgInvocations.size())\n        .WillRepeatedly(\n            testing::Invoke(\n                [](AsyncTransport*,\n                   const AsyncSocketObserverInterface::PrewriteState& state,\n                   AsyncSocketObserverInterface::PrewriteRequestContainer&\n                       container) {\n                  AsyncSocketObserverInterface::PrewriteRequest request;\n                  if (state.startOffset <= 149) {\n                    request.maybeOffsetToSplitWrite = 149; // start offset = 100\n                  }\n                  request.writeFlagsToAdd = flags;\n                  container.addRequest(request);\n                }));\n\n    // sendmsg will be called, we return # of bytes written\n    {\n      InSequence s;\n      for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n        EXPECT_CALL(\n            *(clientConn.getNetOpsDispatcher()),\n            sendmsg(\n                _,\n                Pointee(SendmsgMsghdrHasTotalIovLen(\n                    expectedInvocation.expectedTotalIovLen)),\n                _))\n            .WillOnce(::testing::InvokeWithoutArgs([expectedInvocation]() {\n              return expectedInvocation.returnVal;\n            }));\n      }\n    }\n\n    // write\n    // writes will be intercepted, so we don't need to read at other end\n    WriteCallback wcb;\n    clientConn.getRawSocket()->write(\n        &wcb,\n        kOneHundredCharacterVec.data(),\n        kOneHundredCharacterVec.size(),\n        WriteFlags::NONE);\n    while (STATE_WAITING == wcb.state) {\n      clientConn.getRawSocket()->getEventBase()->loopOnce();\n    }\n    ASSERT_EQ(STATE_SUCCEEDED, wcb.state);\n\n    // check write events\n    for (const auto& expectedInvocation : expectedSendmsgInvocations) {\n      ASSERT_TRUE(expectedInvocation.maybeWriteEventExpectedOffset.has_value());\n      const auto& expectedOffset =\n          *expectedInvocation.maybeWriteEventExpectedOffset;\n\n      auto maybeByteEvent = observer->getByteEventReceivedWithOffset(\n          expectedOffset, ByteEventType::WRITE);\n      ASSERT_TRUE(maybeByteEvent.has_value());\n      auto& byteEvent = maybeByteEvent.value();\n\n      EXPECT_EQ(ByteEventType::WRITE, byteEvent.type);\n      EXPECT_EQ(expectedOffset, byteEvent.offset);\n      EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n      EXPECT_LT(\n          std::chrono::steady_clock::now() - std::chrono::seconds(60),\n          byteEvent.ts);\n\n      EXPECT_EQ(\n          expectedInvocation.maybeWriteEventExpectedFlags,\n          byteEvent.maybeWriteFlags);\n      EXPECT_TRUE(byteEvent.schedTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.txTimestampRequestedOnWrite());\n      EXPECT_TRUE(byteEvent.ackTimestampRequestedOnWrite());\n\n      EXPECT_FALSE(byteEvent.maybeSoftwareTs.has_value());\n      EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n\n      // what we really want to test\n      EXPECT_EQ(\n          folly::to_unsigned(expectedInvocation.returnVal),\n          byteEvent.maybeRawBytesWritten);\n      EXPECT_EQ(\n          expectedInvocation.expectedTotalIovLen,\n          byteEvent.maybeRawBytesTriedToWrite);\n    }\n  }\n}\n\nTEST_F(AsyncSocketByteEventTest, GetTcpInfo_SocketStates) {\n  const folly::TcpInfo::LookupOptions options = {};\n\n  auto clientConn = getClientConn();\n\n  // not open\n  auto expectedTcpInfo = clientConn.getRawSocket()->getTcpInfo(options);\n  EXPECT_FALSE(expectedTcpInfo.hasValue());\n\n  // connected\n  clientConn.connect();\n  expectedTcpInfo = clientConn.getRawSocket()->getTcpInfo(options);\n  EXPECT_TRUE(expectedTcpInfo.hasValue());\n\n  // connected then closed\n  clientConn.getRawSocket()->close();\n  expectedTcpInfo = clientConn.getRawSocket()->getTcpInfo(options);\n  EXPECT_FALSE(expectedTcpInfo.hasValue());\n}\n\n/**\n * Enable byte events and have offset correction immediately succeed.\n *\n * bytesSent and sendBufBytes stay the same and thus offset correction completes\n * on the first attempt.\n */\nTEST_F(\n    AsyncSocketByteEventTest,\n    EnableByteEvents_OffsetCorrection_ValuesStaySame) {\n  std::shared_ptr<MockTcpInfoDispatcher> mockTcpInfoDispatcher =\n      std::make_shared<MockTcpInfoDispatcher>();\n\n  folly::TcpInfo::tcp_info tInfoBefore = {};\n  folly::TcpInfo::tcp_info tInfoAfter = {};\n  tInfoBefore.tcpi_bytes_sent = 35;\n  tInfoAfter.tcpi_bytes_sent = 35;\n\n  folly::TcpInfo wrappedTcpInfoBefore{tInfoBefore};\n  folly::TcpInfo wrappedTcpInfoAfter{tInfoAfter};\n\n  wrappedTcpInfoBefore.setSendBufInUseBytes(0);\n  wrappedTcpInfoAfter.setSendBufInUseBytes(0);\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.setMockTcpInfoDispatcher(mockTcpInfoDispatcher);\n\n  {\n    InSequence s;\n\n    EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n        .WillOnce(Return(wrappedTcpInfoBefore))\n        .RetiresOnSaturation();\n\n    EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n        .WillOnce(Return(wrappedTcpInfoAfter))\n        .RetiresOnSaturation();\n  }\n\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n}\n\n/**\n * Enable byte events and have offset correction repeat due to sendBufInUseBytes\n * changing in between calls to the kernel trying to enable timestamping.\n *\n * The operation should be retried kMaxAttemptsEnableByteEvents times and\n * then fail.\n */\nTEST_F(\n    AsyncSocketByteEventTest,\n    EnableByteEvents_OffsetCorrection_sendBufInUseBytesChangingFail) {\n  std::shared_ptr<MockTcpInfoDispatcher> mockTcpInfoDispatcher =\n      std::make_shared<MockTcpInfoDispatcher>();\n\n  folly::TcpInfo::tcp_info tInfoBefore = {};\n  folly::TcpInfo::tcp_info tInfoAfter = {};\n  tInfoBefore.tcpi_bytes_sent = 35;\n  tInfoAfter.tcpi_bytes_sent = 35;\n\n  folly::TcpInfo wrappedTcpInfoBefore{tInfoBefore};\n  folly::TcpInfo wrappedTcpInfoAfter{tInfoAfter};\n\n  wrappedTcpInfoBefore.setSendBufInUseBytes(1);\n  wrappedTcpInfoAfter.setSendBufInUseBytes(0);\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.setMockTcpInfoDispatcher(mockTcpInfoDispatcher);\n\n  size_t byteEventsEnabledAttempts = 0;\n\n  {\n    InSequence s;\n\n    for (; byteEventsEnabledAttempts < kMaxAttemptsEnableByteEvents;\n         byteEventsEnabledAttempts++) {\n      EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n          .WillOnce(Return(wrappedTcpInfoBefore))\n          .RetiresOnSaturation();\n\n      EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n          .WillOnce(Return(wrappedTcpInfoAfter))\n          .RetiresOnSaturation();\n    }\n  }\n\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n\n  EXPECT_EQ(byteEventsEnabledAttempts, kMaxAttemptsEnableByteEvents);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(1, observer->byteEventsUnavailableCalled);\n  EXPECT_TRUE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n}\n\n/**\n * Enable byte events and have offset correction repeat due to sentBytes\n * changing in between calls to the kernel trying to enable timestamping.\n *\n * The operation should be retried kMaxAttemptsEnableByteEvents times and\n * then fail.\n */\nTEST_F(\n    AsyncSocketByteEventTest,\n    EnableByteEvents_OffsetCorrection_sentBytesChangingFail) {\n  std::shared_ptr<MockTcpInfoDispatcher> mockTcpInfoDispatcher =\n      std::make_shared<MockTcpInfoDispatcher>();\n\n  folly::TcpInfo::tcp_info tInfoBefore = {};\n  folly::TcpInfo::tcp_info tInfoAfter = {};\n  tInfoBefore.tcpi_bytes_sent = 35;\n  tInfoAfter.tcpi_bytes_sent = 36;\n\n  folly::TcpInfo wrappedTcpInfoBefore{tInfoBefore};\n  folly::TcpInfo wrappedTcpInfoAfter{tInfoAfter};\n\n  wrappedTcpInfoBefore.setSendBufInUseBytes(0);\n  wrappedTcpInfoAfter.setSendBufInUseBytes(0);\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.setMockTcpInfoDispatcher(mockTcpInfoDispatcher);\n\n  size_t byteEventsEnabledAttempts = 0;\n\n  {\n    InSequence s;\n\n    for (; byteEventsEnabledAttempts < kMaxAttemptsEnableByteEvents;\n         byteEventsEnabledAttempts++) {\n      EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n          .WillOnce(Return(wrappedTcpInfoBefore))\n          .RetiresOnSaturation();\n\n      EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n          .WillOnce(Return(wrappedTcpInfoAfter))\n          .RetiresOnSaturation();\n    }\n  }\n\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n\n  EXPECT_EQ(byteEventsEnabledAttempts, kMaxAttemptsEnableByteEvents);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(1, observer->byteEventsUnavailableCalled);\n  EXPECT_TRUE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n}\n\n/**\n * Enable byte events and have offset correction repeat due to sendBufInUseBytes\n * changing in between calls to the kernel trying to enable timestamping.\n *\n * The operation should be retried at most kMaxAttemptsEnableByteEvents\n * times and then succeed when sendBufInUseBytes does not change.\n */\nTEST_F(\n    AsyncSocketByteEventTest,\n    EnableByteEvents_OffsetCorrection_sendBufInUseBytesChangingSuccess) {\n  std::shared_ptr<MockTcpInfoDispatcher> mockTcpInfoDispatcher =\n      std::make_shared<MockTcpInfoDispatcher>();\n\n  folly::TcpInfo::tcp_info tInfoBefore = {};\n  folly::TcpInfo::tcp_info tInfoAfter = {};\n  tInfoBefore.tcpi_bytes_sent = 36;\n  tInfoAfter.tcpi_bytes_sent = 36;\n\n  folly::TcpInfo::tcp_info tInfoBefore2 = {};\n  folly::TcpInfo::tcp_info tInfoAfter2 = {};\n  tInfoBefore2.tcpi_bytes_sent = 36;\n  tInfoAfter2.tcpi_bytes_sent = 36;\n\n  folly::TcpInfo wrappedTcpInfoBefore{tInfoBefore};\n  folly::TcpInfo wrappedTcpInfoAfter{tInfoAfter};\n  folly::TcpInfo wrappedTcpInfoBefore2{tInfoBefore2};\n  folly::TcpInfo wrappedTcpInfoAfter2{tInfoAfter2};\n\n  wrappedTcpInfoBefore.setSendBufInUseBytes(1);\n  wrappedTcpInfoAfter.setSendBufInUseBytes(0);\n  wrappedTcpInfoBefore2.setSendBufInUseBytes(0);\n  wrappedTcpInfoAfter2.setSendBufInUseBytes(0);\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.setMockTcpInfoDispatcher(mockTcpInfoDispatcher);\n\n  size_t byteEventsEnabledAttempts = 0;\n  size_t constexpr kRetriesUntilByteEventsSuccessful = 5;\n  EXPECT_LE(kRetriesUntilByteEventsSuccessful, kMaxAttemptsEnableByteEvents);\n\n  {\n    InSequence s;\n\n    for (; byteEventsEnabledAttempts < kMaxAttemptsEnableByteEvents;\n         byteEventsEnabledAttempts++) {\n      if (byteEventsEnabledAttempts == kRetriesUntilByteEventsSuccessful) {\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoBefore2))\n            .RetiresOnSaturation();\n\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoAfter2))\n            .RetiresOnSaturation();\n\n        break;\n      } else {\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoBefore))\n            .RetiresOnSaturation();\n\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoAfter))\n            .RetiresOnSaturation();\n      }\n    }\n  }\n\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n\n  EXPECT_EQ(byteEventsEnabledAttempts, kRetriesUntilByteEventsSuccessful);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n}\n\n/**\n * Enable byte events and have offset correction repeat due to sentBytes\n * changing in between calls to the kernel trying to enable timestamping.\n *\n * The operation should be retried at most kMaxAttemptsEnableByteEvents\n * times and then succeed when sentBytes does not change.\n */\nTEST_F(\n    AsyncSocketByteEventTest,\n    EnableByteEvents_OffsetCorrection_sentBytesChangingSuccess) {\n  std::shared_ptr<MockTcpInfoDispatcher> mockTcpInfoDispatcher =\n      std::make_shared<MockTcpInfoDispatcher>();\n\n  folly::TcpInfo::tcp_info tInfoBefore = {};\n  folly::TcpInfo::tcp_info tInfoAfter = {};\n  tInfoBefore.tcpi_bytes_sent = 35;\n  tInfoAfter.tcpi_bytes_sent = 36;\n\n  folly::TcpInfo::tcp_info tInfoBefore2 = {};\n  folly::TcpInfo::tcp_info tInfoAfter2 = {};\n  tInfoBefore2.tcpi_bytes_sent = 36;\n  tInfoAfter2.tcpi_bytes_sent = 36;\n\n  folly::TcpInfo wrappedTcpInfoBefore{tInfoBefore};\n  folly::TcpInfo wrappedTcpInfoAfter{tInfoAfter};\n  folly::TcpInfo wrappedTcpInfoBefore2{tInfoBefore2};\n  folly::TcpInfo wrappedTcpInfoAfter2{tInfoAfter2};\n\n  wrappedTcpInfoBefore.setSendBufInUseBytes(0);\n  wrappedTcpInfoAfter.setSendBufInUseBytes(0);\n  wrappedTcpInfoBefore2.setSendBufInUseBytes(0);\n  wrappedTcpInfoAfter2.setSendBufInUseBytes(0);\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.setMockTcpInfoDispatcher(mockTcpInfoDispatcher);\n\n  size_t byteEventsEnabledAttempts = 0;\n  size_t constexpr kRetriesUntilByteEventsSuccessful = 5;\n  EXPECT_LE(kRetriesUntilByteEventsSuccessful, kMaxAttemptsEnableByteEvents);\n\n  {\n    InSequence s;\n\n    for (; byteEventsEnabledAttempts < kMaxAttemptsEnableByteEvents;\n         byteEventsEnabledAttempts++) {\n      if (byteEventsEnabledAttempts == kRetriesUntilByteEventsSuccessful) {\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoBefore2))\n            .RetiresOnSaturation();\n\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoAfter2))\n            .RetiresOnSaturation();\n\n        break;\n      } else {\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoBefore))\n            .RetiresOnSaturation();\n\n        EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n            .WillOnce(Return(wrappedTcpInfoAfter))\n            .RetiresOnSaturation();\n      }\n    }\n  }\n\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n\n  EXPECT_EQ(byteEventsEnabledAttempts, kRetriesUntilByteEventsSuccessful);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n}\n\nTEST_F(AsyncSocketByteEventTest, EnableByteEventsThrowsWithIoUringBackend) {\n  std::unique_ptr<EventBase> evb;\n  try {\n    evb = std::make_unique<EventBase>(EventBase::Options{}.setBackendFactory(\n        []() -> std::unique_ptr<EventBaseBackendBase> {\n          IoUringBackend::Options options;\n          options.setNativeAsyncSocketSupport(true);\n          return std::make_unique<IoUringBackend>(std::move(options));\n        }));\n  } catch (IoUringBackend::NotAvailable const&) {\n    GTEST_SKIP() << \"IoUringBackend not available\";\n  }\n\n  auto socket = AsyncSocket::newSocket(evb.get());\n  ConnCallback ccb;\n  socket->connect(&ccb, server_->getAddress(), 30);\n  evb->loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  auto observer = attachObserver(socket.get(), true /* enableByteEvents */);\n  EXPECT_EQ(0, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(1, observer->byteEventsUnavailableCalled);\n  EXPECT_TRUE(observer->byteEventsUnavailableCalledEx.has_value());\n  socket.reset();\n}\n\nclass AsyncSocketByteEventRawOffsetTest\n    : public AsyncSocketByteEventTest,\n      public testing::WithParamInterface<size_t> {\n public:\n  // byte offset of the AsyncSocket when ByteEvents are enabled\n  //\n  // for some of the tests, the value returned by sendBufInUseBytes\n  // will be greater than this value to simulate a case in which\n  // bytes are written to the socket prior to the AsyncSocket being\n  // initialized, and those bytes still not yet been acked.\n  static constexpr size_t kRawByteOffsetWhenByteEventsEnabled = 20;\n\n  // values returned by sendBufInUseBytes()\n  static std::vector<size_t> getTestingValues() {\n    std::vector<size_t> vals{\n        /* Values for sendBufInUseBytes */\n        0,\n        1,\n        10,\n        kRawByteOffsetWhenByteEventsEnabled,\n        // simulate cases where bytes have already been\n        // written to the kernel socket prior to the\n        // AsyncSocket being initialized and are still\n        // in the sendbuf (either not sent, or not ACKed).\n        kRawByteOffsetWhenByteEventsEnabled + 1,\n        kRawByteOffsetWhenByteEventsEnabled + 10};\n    return vals;\n  }\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    ByteEventRawOffsets,\n    AsyncSocketByteEventRawOffsetTest,\n    ::testing::ValuesIn(AsyncSocketByteEventRawOffsetTest::getTestingValues()));\n\n/**\n * Enable byte events with varying values of sendBufInUseBytes.\n *\n * This is an end-to-end test verifying proper delivery of timestamps with\n * different byte offset corrections. sendBufInUseBytes varies between zero\n * and a value greater than that reported by getRawBytesWritten(), with the\n * latter providing coverage of a case where bytes were written to the\n * kernel socket prior to the AsyncSocket being initialized and are still\n * in the sendbuf.\n */\nTEST_P(AsyncSocketByteEventRawOffsetTest, EnableByteEvents_CheckRawByteOffset) {\n  const auto flags = WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n      WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK;\n\n  const auto bytesInSendBuf = GetParam();\n  const std::vector<uint8_t> wbuf1(kRawByteOffsetWhenByteEventsEnabled, 'a');\n  const std::vector<uint8_t> wbuf2(1, 'a');\n  const std::vector<uint8_t> wbufBytesInSendBufOnEnable(bytesInSendBuf, 'a');\n\n  std::shared_ptr<MockTcpInfoDispatcher> mockTcpInfoDispatcher =\n      std::make_shared<MockTcpInfoDispatcher>();\n\n  folly::TcpInfo::tcp_info tInfoBefore = {};\n  folly::TcpInfo::tcp_info tInfoAfter = {};\n  folly::TcpInfo wrappedTcpInfoBefore{tInfoBefore};\n  folly::TcpInfo wrappedTcpInfoAfter{tInfoAfter};\n  wrappedTcpInfoBefore.setSendBufInUseBytes(bytesInSendBuf);\n  wrappedTcpInfoAfter.setSendBufInUseBytes(bytesInSendBuf);\n\n  auto clientConn = getClientConn();\n  clientConn.netOpsExpectNoTimestampingSetSockOpt();\n  clientConn.connect();\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  clientConn.setMockTcpInfoDispatcher(mockTcpInfoDispatcher);\n\n  {\n    InSequence s;\n\n    EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n        .WillOnce(Return(wrappedTcpInfoBefore))\n        .RetiresOnSaturation();\n\n    EXPECT_CALL(*mockTcpInfoDispatcher, initFromFd(_, _, _, _))\n        .WillOnce(Return(wrappedTcpInfoAfter))\n        .RetiresOnSaturation();\n  }\n\n  // Write any bytes that we wanted to have sent through the AsyncSocket\n  // prior to timestamps being enabled to adjust the rawByteOffset\n  clientConn.writeAtClientReadAtServer(wbuf1, WriteFlags::NONE);\n\n  // Enable timestamps\n  //\n  // AsyncSocket will record the value returned by TcpInfo::sendBufInUseBytes\n  // when enabling timestamps to determine the correction factor for timestamp\n  // byte offsets. This test controls the value returned by sendBufInUseBytes.\n  clientConn.netOpsExpectTimestampingSetSockOpt();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n  clientConn.netOpsVerifyAndClearExpectations();\n\n  // Write wbufBytesInSendBufOnEnable to the socket (bypassing AsyncSocket)\n  //\n  // We can't control the actual number of bytes in the socket sendbuf when\n  // we enable timestamping in the previous step. Instead, we create the\n  // scenario where there are bytes in the sendBuf that we need to correct for\n  // by writing bytes directly to the network socket after enabling\n  // timestamping. The number of bytes written is identical to the number of\n  // bytes that we reported were in the buffer in the previous step, thereby\n  // causing kernel timestamp byte offsets to be offset by this amount.\n  clientConn.writeAtClientDirectlyToNetworkSocket(wbufBytesInSendBufOnEnable);\n\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf2, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(4));\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled,\n      observer->maxOffsetForByteEventReceived(ByteEventType::WRITE).value());\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled,\n      observer->maxOffsetForByteEventReceived(ByteEventType::SCHED).value());\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled,\n      observer->maxOffsetForByteEventReceived(ByteEventType::TX).value());\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled,\n      observer->maxOffsetForByteEventReceived(ByteEventType::ACK).value());\n\n  // write again to check offsets\n  clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(dropWriteFromFlags(flags));\n  clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf2, flags);\n  clientConn.netOpsVerifyAndClearExpectations();\n  EXPECT_THAT(observer->byteEvents, SizeIs(8));\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled + 1,\n      observer->maxOffsetForByteEventReceived(ByteEventType::WRITE));\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled + 1,\n      observer->maxOffsetForByteEventReceived(ByteEventType::SCHED));\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled + 1,\n      observer->maxOffsetForByteEventReceived(ByteEventType::TX));\n  EXPECT_EQ(\n      kRawByteOffsetWhenByteEventsEnabled + 1,\n      observer->maxOffsetForByteEventReceived(ByteEventType::ACK));\n}\n\nstruct AsyncSocketByteEventDetailsTestParams {\n  struct WriteParams {\n    WriteParams(uint64_t bufferSize, WriteFlags writeFlags)\n        : bufferSize(bufferSize), writeFlags(writeFlags) {}\n    uint64_t bufferSize{0};\n    WriteFlags writeFlags{WriteFlags::NONE};\n  };\n\n  std::vector<WriteParams> writesWithParams;\n};\n\nclass AsyncSocketByteEventDetailsTest\n    : public AsyncSocketByteEventTest,\n      public testing::WithParamInterface<\n          AsyncSocketByteEventDetailsTestParams> {\n public:\n  static std::vector<AsyncSocketByteEventDetailsTestParams> getTestingValues() {\n    const std::array<WriteFlags, 9> writeFlagCombinations{\n        // SCHED\n        WriteFlags::TIMESTAMP_SCHED,\n        // TX\n        WriteFlags::TIMESTAMP_TX,\n        // ACK\n        WriteFlags::TIMESTAMP_ACK,\n        // SCHED + TX + ACK\n        WriteFlags::TIMESTAMP_SCHED | WriteFlags::TIMESTAMP_TX |\n            WriteFlags::TIMESTAMP_ACK,\n        // WRITE\n        WriteFlags::TIMESTAMP_WRITE,\n        // WRITE + SCHED\n        WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED,\n        // WRITE + TX\n        WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_TX,\n        // WRITE + ACK\n        WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_ACK,\n        // WRITE + SCHED + TX + ACK\n        WriteFlags::TIMESTAMP_WRITE | WriteFlags::TIMESTAMP_SCHED |\n            WriteFlags::TIMESTAMP_TX | WriteFlags::TIMESTAMP_ACK,\n    };\n\n    std::vector<AsyncSocketByteEventDetailsTestParams> vals;\n    for (const auto& writeFlags : writeFlagCombinations) {\n      // write 1 byte\n      {\n        AsyncSocketByteEventDetailsTestParams params;\n        params.writesWithParams.emplace_back(1, writeFlags);\n        vals.push_back(params);\n      }\n\n      // write 1 byte twice\n      {\n        AsyncSocketByteEventDetailsTestParams params;\n        params.writesWithParams.emplace_back(1, writeFlags);\n        params.writesWithParams.emplace_back(1, writeFlags);\n        vals.push_back(params);\n      }\n\n      // write 10 bytes\n      {\n        AsyncSocketByteEventDetailsTestParams params;\n        params.writesWithParams.emplace_back(10, writeFlags);\n        vals.push_back(params);\n      }\n\n      // write 10 bytes twice\n      {\n        AsyncSocketByteEventDetailsTestParams params;\n        params.writesWithParams.emplace_back(10, writeFlags);\n        params.writesWithParams.emplace_back(10, writeFlags);\n        vals.push_back(params);\n      }\n    }\n\n    return vals;\n  }\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    ByteEventDetailsTest,\n    AsyncSocketByteEventDetailsTest,\n    ::testing::ValuesIn(AsyncSocketByteEventDetailsTest::getTestingValues()));\n\n/**\n * Inspect ByteEvent fields, including xTimestampRequested in WRITE events.\n */\nTEST_P(AsyncSocketByteEventDetailsTest, CheckByteEventDetails) {\n  auto params = GetParam();\n\n  auto clientConn = getClientConn();\n  clientConn.connect();\n  auto observer = clientConn.attachObserver(true /* enableByteEvents */);\n  EXPECT_EQ(1, observer->byteEventsEnabledCalled);\n  EXPECT_EQ(0, observer->byteEventsUnavailableCalled);\n  EXPECT_FALSE(observer->byteEventsUnavailableCalledEx.has_value());\n\n  uint64_t expectedNumByteEvents = 0;\n  for (const auto& writeParams : params.writesWithParams) {\n    const std::vector<uint8_t> wbuf(writeParams.bufferSize, 'a');\n    const auto flags = writeParams.writeFlags;\n    clientConn.netOpsExpectSendmsgWithAncillaryTsFlags(\n        dropWriteFromFlags(flags));\n    clientConn.writeAtClientReadAtServerReflectReadAtClient(wbuf, flags);\n    clientConn.netOpsVerifyAndClearExpectations();\n    const auto expectedOffset =\n        clientConn.getRawSocket()->getRawBytesWritten() - 1;\n\n    // check WRITE\n    if ((flags & WriteFlags::TIMESTAMP_WRITE) != WriteFlags::NONE) {\n      expectedNumByteEvents++;\n\n      auto maybeByteEvent = observer->getByteEventReceivedWithOffset(\n          expectedOffset, ByteEventType::WRITE);\n      ASSERT_TRUE(maybeByteEvent.has_value());\n      auto& byteEvent = maybeByteEvent.value();\n\n      EXPECT_EQ(ByteEventType::WRITE, byteEvent.type);\n      EXPECT_EQ(expectedOffset, byteEvent.offset);\n      EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n      EXPECT_LT(\n          std::chrono::steady_clock::now() - std::chrono::seconds(60),\n          byteEvent.ts);\n\n      EXPECT_EQ(flags, byteEvent.maybeWriteFlags);\n      EXPECT_EQ(\n          isSet(flags, WriteFlags::TIMESTAMP_SCHED),\n          byteEvent.schedTimestampRequestedOnWrite());\n      EXPECT_EQ(\n          isSet(flags, WriteFlags::TIMESTAMP_TX),\n          byteEvent.txTimestampRequestedOnWrite());\n      EXPECT_EQ(\n          isSet(flags, WriteFlags::TIMESTAMP_ACK),\n          byteEvent.ackTimestampRequestedOnWrite());\n\n      EXPECT_FALSE(byteEvent.maybeSoftwareTs.has_value());\n      EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n    }\n\n    // check SCHED, TX, ACK\n    for (const auto& byteEventType :\n         {ByteEventType::SCHED, ByteEventType::TX, ByteEventType::ACK}) {\n      auto maybeByteEvent = observer->getByteEventReceivedWithOffset(\n          expectedOffset, byteEventType);\n      switch (byteEventType) {\n        case ByteEventType::WRITE:\n          FAIL();\n        case ByteEventType::SCHED:\n          if ((flags & WriteFlags::TIMESTAMP_SCHED) == WriteFlags::NONE) {\n            EXPECT_FALSE(maybeByteEvent.has_value());\n            continue;\n          }\n          break;\n        case ByteEventType::TX:\n          if ((flags & WriteFlags::TIMESTAMP_TX) == WriteFlags::NONE) {\n            EXPECT_FALSE(maybeByteEvent.has_value());\n            continue;\n          }\n          break;\n        case ByteEventType::ACK:\n          if ((flags & WriteFlags::TIMESTAMP_ACK) == WriteFlags::NONE) {\n            EXPECT_FALSE(maybeByteEvent.has_value());\n            continue;\n          }\n          break;\n      }\n\n      expectedNumByteEvents++;\n      ASSERT_TRUE(maybeByteEvent.has_value());\n      auto& byteEvent = maybeByteEvent.value();\n\n      EXPECT_EQ(byteEventType, byteEvent.type);\n      EXPECT_EQ(expectedOffset, byteEvent.offset);\n      EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n      EXPECT_LT(\n          std::chrono::steady_clock::now() - std::chrono::seconds(60),\n          byteEvent.ts);\n      EXPECT_FALSE(byteEvent.maybeWriteFlags.has_value());\n      // don't check *TimestampRequestedOnWrite* fields to avoid CHECK_DEATH,\n      // already checked in CheckByteEventDetailsApplicationSetsFlags\n\n      EXPECT_TRUE(byteEvent.maybeSoftwareTs.has_value());\n      EXPECT_FALSE(byteEvent.maybeHardwareTs.has_value());\n    }\n  }\n\n  // should have at least expectedNumByteEvents\n  // may be more if writes were split up by kernel\n  EXPECT_THAT(observer->byteEvents, SizeIs(Ge(expectedNumByteEvents)));\n}\n\nclass AsyncSocketByteEventHelperTest : public ::testing::Test {\n protected:\n  using ByteEventType = AsyncSocket::ByteEvent::Type;\n\n  /**\n   * Wrapper around a vector containing cmsg header + data.\n   */\n  class WrappedCMsg {\n   public:\n    explicit WrappedCMsg(std::vector<char>&& data) : data_(std::move(data)) {}\n\n    operator const struct cmsghdr&() {\n      return *reinterpret_cast<struct cmsghdr*>(data_.data());\n    }\n\n   protected:\n    std::vector<char> data_;\n  };\n\n  /**\n   * Wrapper around a vector containing cmsg header + data.\n   */\n  class WrappedSockExtendedErrTsCMsg : public WrappedCMsg {\n   public:\n    using WrappedCMsg::WrappedCMsg;\n\n    // ts[0] -> software timestamp\n    // ts[1] -> hardware timestamp transformed to userspace time (deprecated)\n    // ts[2] -> hardware timestamp\n\n    void setSoftwareTimestamp(\n        const std::chrono::seconds seconds,\n        const std::chrono::nanoseconds nanoseconds) {\n      struct cmsghdr* cmsg{reinterpret_cast<cmsghdr*>(data_.data())};\n      struct scm_timestamping* tss{\n          reinterpret_cast<struct scm_timestamping*>(CMSG_DATA(cmsg))};\n      tss->ts[0].tv_sec = seconds.count();\n      tss->ts[0].tv_nsec = nanoseconds.count();\n    }\n\n    void setHardwareTimestamp(\n        const std::chrono::seconds seconds,\n        const std::chrono::nanoseconds nanoseconds) {\n      struct cmsghdr* cmsg{reinterpret_cast<cmsghdr*>(data_.data())};\n      struct scm_timestamping* tss{\n          reinterpret_cast<struct scm_timestamping*>(CMSG_DATA(cmsg))};\n      tss->ts[2].tv_sec = seconds.count();\n      tss->ts[2].tv_nsec = nanoseconds.count();\n    }\n  };\n\n  static std::vector<char> cmsgData(int level, int type, size_t len) {\n    std::vector<char> data(CMSG_LEN(len), 0);\n    struct cmsghdr* cmsg{reinterpret_cast<cmsghdr*>(data.data())};\n    cmsg->cmsg_level = level;\n    cmsg->cmsg_type = type;\n    cmsg->cmsg_len = CMSG_LEN(len);\n    return data;\n  }\n\n  static WrappedSockExtendedErrTsCMsg cmsgForSockExtendedErrTimestamping() {\n    return WrappedSockExtendedErrTsCMsg(\n        cmsgData(SOL_SOCKET, SO_TIMESTAMPING, sizeof(struct scm_timestamping)));\n  }\n\n  static WrappedCMsg cmsgForScmTimestamping(\n      const uint32_t type, const uint32_t kernelByteOffset) {\n    auto data = cmsgData(SOL_IP, IP_RECVERR, sizeof(struct sock_extended_err));\n    struct cmsghdr* cmsg{reinterpret_cast<cmsghdr*>(data.data())};\n    struct sock_extended_err* serr{\n        reinterpret_cast<struct sock_extended_err*>(CMSG_DATA(cmsg))};\n    serr->ee_errno = ENOMSG;\n    serr->ee_origin = SO_EE_ORIGIN_TIMESTAMPING;\n    serr->ee_info = type;\n    serr->ee_data = kernelByteOffset;\n    return WrappedCMsg(std::move(data));\n  }\n};\n\nTEST_F(AsyncSocketByteEventHelperTest, ByteOffsetThenTs) {\n  auto scmTs = cmsgForScmTimestamping(folly::netops::SCM_TSTAMP_SND, 0);\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_TRUE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n}\n\nTEST_F(AsyncSocketByteEventHelperTest, TsThenByteOffset) {\n  auto scmTs = cmsgForScmTimestamping(folly::netops::SCM_TSTAMP_SND, 0);\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n\n  EXPECT_FALSE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n  EXPECT_TRUE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n}\n\nTEST_F(AsyncSocketByteEventHelperTest, ByteEventsDisabled) {\n  auto scmTs = cmsgForScmTimestamping(folly::netops::SCM_TSTAMP_SND, 0);\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = false;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n\n  // fails because disabled\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_FALSE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n\n  // enable, try again to prove this works\n  helper.byteEventsEnabled = true;\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_TRUE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n}\n\nTEST_F(AsyncSocketByteEventHelperTest, IgnoreUnsupportedEvent) {\n  auto scmType =\n      folly::netops::SCM_TSTAMP_ACK + 10; // imaginary new type of SCM event\n  auto scmTs = cmsgForScmTimestamping(scmType, 0);\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n\n  // unsupported event is eaten\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_FALSE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n\n  // change type, try again to prove this works\n  scmTs = cmsgForScmTimestamping(folly::netops::SCM_TSTAMP_ACK, 0);\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_TRUE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n}\n\nTEST_F(AsyncSocketByteEventHelperTest, ErrorDoubleScmCmsg) {\n  auto scmTs = cmsgForScmTimestamping(folly::netops::SCM_TSTAMP_SND, 0);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_THROW(\n      helper.processCmsg(scmTs, 1 /* rawBytesWritten */),\n      AsyncSocket::ByteEventHelper::Exception);\n}\n\nTEST_F(AsyncSocketByteEventHelperTest, ErrorDoubleSerrCmsg) {\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n  EXPECT_FALSE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n  EXPECT_THROW(\n      helper.processCmsg(serrTs, 1 /* rawBytesWritten */),\n      AsyncSocket::ByteEventHelper::Exception);\n}\n\nTEST_F(AsyncSocketByteEventHelperTest, ErrorExceptionSet) {\n  auto scmTs = cmsgForScmTimestamping(folly::netops::SCM_TSTAMP_SND, 0);\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n  helper.maybeEx = AsyncSocketException(\n      AsyncSocketException::AsyncSocketExceptionType::UNKNOWN, \"\");\n\n  // fails due to existing exception\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_FALSE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n\n  // delete the exception, then repeat to prove exception was blocking\n  helper.maybeEx = folly::none;\n  EXPECT_FALSE(helper.processCmsg(scmTs, 1 /* rawBytesWritten */));\n  EXPECT_TRUE(helper.processCmsg(serrTs, 1 /* rawBytesWritten */));\n}\n\nstruct AsyncSocketByteEventHelperTimestampTestParams {\n  AsyncSocketByteEventHelperTimestampTestParams(\n      uint32_t scmType,\n      AsyncSocket::ByteEvent::Type expectedByteEventType,\n      bool includeSoftwareTs,\n      bool includeHardwareTs)\n      : scmType(scmType),\n        expectedByteEventType(expectedByteEventType),\n        includeSoftwareTs(includeSoftwareTs),\n        includeHardwareTs(includeHardwareTs) {}\n  uint32_t scmType{0};\n  AsyncSocket::ByteEvent::Type expectedByteEventType;\n  bool includeSoftwareTs{false};\n  bool includeHardwareTs{false};\n};\n\nclass AsyncSocketByteEventHelperTimestampTest\n    : public AsyncSocketByteEventHelperTest,\n      public testing::WithParamInterface<\n          AsyncSocketByteEventHelperTimestampTestParams> {\n public:\n  static std::vector<AsyncSocketByteEventHelperTimestampTestParams>\n  getTestingValues() {\n    std::vector<AsyncSocketByteEventHelperTimestampTestParams> vals;\n\n    // software + hardware timestamps\n    {\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_SCHED, ByteEventType::SCHED, true, true);\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_SND, ByteEventType::TX, true, true);\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_ACK, ByteEventType::ACK, true, true);\n    }\n\n    // software ts only\n    {\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_SCHED, ByteEventType::SCHED, true, false);\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_SND, ByteEventType::TX, true, false);\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_ACK, ByteEventType::ACK, true, false);\n    }\n\n    // hardware ts only\n    {\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_SCHED, ByteEventType::SCHED, false, true);\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_SND, ByteEventType::TX, false, true);\n      vals.emplace_back(\n          folly::netops::SCM_TSTAMP_ACK, ByteEventType::ACK, false, true);\n    }\n\n    return vals;\n  }\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    ByteEventTimestampTest,\n    AsyncSocketByteEventHelperTimestampTest,\n    ::testing::ValuesIn(\n        AsyncSocketByteEventHelperTimestampTest::getTestingValues()));\n\n/**\n * Check timestamp parsing for software and hardware timestamps.\n */\nTEST_P(AsyncSocketByteEventHelperTimestampTest, CheckEventTimestamps) {\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  const auto hardwareTsSec = std::chrono::seconds(79);\n  const auto hardwareTsNs = std::chrono::nanoseconds(31);\n\n  auto params = GetParam();\n  auto scmTs = cmsgForScmTimestamping(params.scmType, 0);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  if (params.includeSoftwareTs) {\n    serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n  }\n  if (params.includeHardwareTs) {\n    serrTs.setHardwareTimestamp(hardwareTsSec, hardwareTsNs);\n  }\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled = 0;\n  folly::Optional<AsyncSocket::ByteEvent> maybeByteEvent;\n  maybeByteEvent = helper.processCmsg(serrTs, 1 /* rawBytesWritten */);\n  EXPECT_FALSE(maybeByteEvent.has_value());\n  maybeByteEvent = helper.processCmsg(scmTs, 1 /* rawBytesWritten */);\n\n  // common checks\n  ASSERT_TRUE(maybeByteEvent.has_value());\n  const auto& byteEvent = *maybeByteEvent;\n  EXPECT_EQ(0, byteEvent.offset);\n  EXPECT_GE(std::chrono::steady_clock::now(), byteEvent.ts);\n\n  EXPECT_EQ(params.expectedByteEventType, byteEvent.type);\n  if (params.includeSoftwareTs) {\n    EXPECT_EQ(softwareTsSec + softwareTsNs, byteEvent.maybeSoftwareTs);\n  }\n  if (params.includeHardwareTs) {\n    EXPECT_EQ(hardwareTsSec + hardwareTsNs, byteEvent.maybeHardwareTs);\n  }\n}\n\nstruct AsyncSocketByteEventHelperOffsetTestParams {\n  uint64_t rawBytesWrittenWhenByteEventsEnabled{0};\n  uint64_t byteTimestamped;\n  uint64_t rawBytesWrittenWhenTimestampReceived;\n};\n\nclass AsyncSocketByteEventHelperOffsetTest\n    : public AsyncSocketByteEventHelperTest,\n      public testing::WithParamInterface<\n          AsyncSocketByteEventHelperOffsetTestParams> {\n public:\n  static std::vector<AsyncSocketByteEventHelperOffsetTestParams>\n  getTestingValues() {\n    std::vector<AsyncSocketByteEventHelperOffsetTestParams> vals;\n    const std::array<uint64_t, 5> rawBytesWrittenWhenByteEventsEnabledVals{\n        0, 1, 100, 4294967295, 4294967296};\n    for (const auto& rawBytesWrittenWhenByteEventsEnabled :\n         rawBytesWrittenWhenByteEventsEnabledVals) {\n      auto addParams = [&](auto params) {\n        // check if case is valid based on rawBytesWrittenWhenByteEventsEnabled\n        if (rawBytesWrittenWhenByteEventsEnabled <= params.byteTimestamped) {\n          vals.push_back(params);\n        }\n      };\n\n      // case 1\n      // bytes sent on receipt of timestamp == byte timestamped\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 0;\n        params.rawBytesWrittenWhenTimestampReceived = 0;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 1;\n        params.rawBytesWrittenWhenTimestampReceived = 1;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 101;\n        params.rawBytesWrittenWhenTimestampReceived = 101;\n        addParams(params);\n      }\n\n      // bytes sent on receipt of timestamp > byte timestamped\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 1;\n        params.rawBytesWrittenWhenTimestampReceived = 2;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 101;\n        params.rawBytesWrittenWhenTimestampReceived = 102;\n        addParams(params);\n      }\n\n      // case 2\n      // bytes sent on receipt of timestamp == byte timestamped, boundary test\n      // (boundary is at 2^32)\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967294;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967294;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967295;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967295;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967296;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967296;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967297;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967297;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967298;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967298;\n        addParams(params);\n      }\n\n      // case 3\n      // bytes sent on receipt of timestamp > byte timestamped, boundary test\n      // (boundary is at 2^32)\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967293;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967294;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967294;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967295;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967295;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967296;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967296;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967297;\n        addParams(params);\n      }\n\n      // case 4\n      // bytes sent on receipt of timestamp > byte timestamped, wrap test\n      // (boundary is at 2^32)\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967275;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967305;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967295;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967296;\n        addParams(params);\n      }\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 4294967285;\n        params.rawBytesWrittenWhenTimestampReceived = 4294967305;\n        addParams(params);\n      }\n\n      // case 5\n      // special case when timestamp enabled when bytes transferred > (2^32)\n      // bytes sent on receipt of timestamp == byte timestamped, boundary test\n      // (boundary is at 2^32)\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 6442450943;\n        params.rawBytesWrittenWhenTimestampReceived = 6442450943;\n        addParams(params);\n      }\n\n      // case 6\n      // special case when timestamp enabled when bytes transferred > (2^32)\n      // bytes sent on receipt of timestamp > byte timestamped, boundary test\n      // (boundary is at 2^32)\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 6442450943;\n        params.rawBytesWrittenWhenTimestampReceived = 6442450944;\n        addParams(params);\n      }\n\n      // case 7\n      // special case when timestamp enabled when bytes transferred > (2^32)\n      // bytes sent on receipt of timestamp > byte timestamped, wrap test\n      // (boundary is at 2^32)\n      {\n        AsyncSocketByteEventHelperOffsetTestParams params;\n        params.rawBytesWrittenWhenByteEventsEnabled =\n            rawBytesWrittenWhenByteEventsEnabled;\n        params.byteTimestamped = 6442450943;\n        params.rawBytesWrittenWhenTimestampReceived = 8589934591;\n        addParams(params);\n      }\n    }\n\n    return vals;\n  }\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    ByteEventOffsetTest,\n    AsyncSocketByteEventHelperOffsetTest,\n    ::testing::ValuesIn(\n        AsyncSocketByteEventHelperOffsetTest::getTestingValues()));\n\n/**\n * Check byte offset handling, including boundary cases.\n *\n * See AsyncSocket::ByteEventHelper::processCmsg for details.\n */\nTEST_P(AsyncSocketByteEventHelperOffsetTest, CheckCalculatedOffset) {\n  auto params = GetParam();\n\n  // because we use SOF_TIMESTAMPING_OPT_ID, byte offsets delivered from the\n  // kernel are offset (relative to bytes written by AsyncSocket) by the number\n  // of bytes AsyncSocket had written to the socket when enabling timestamps\n  //\n  // here we calculate what the kernel offset would be for the given byte offset\n  const uint64_t bytesPerOffsetWrap =\n      static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1;\n\n  auto kernelByteOffset =\n      params.byteTimestamped - params.rawBytesWrittenWhenByteEventsEnabled;\n  if (kernelByteOffset > 0) {\n    kernelByteOffset = kernelByteOffset % bytesPerOffsetWrap;\n  }\n\n  auto scmTs =\n      cmsgForScmTimestamping(folly::netops::SCM_TSTAMP_SND, kernelByteOffset);\n  const auto softwareTsSec = std::chrono::seconds(59);\n  const auto softwareTsNs = std::chrono::nanoseconds(11);\n  auto serrTs = cmsgForSockExtendedErrTimestamping();\n  serrTs.setSoftwareTimestamp(softwareTsSec, softwareTsNs);\n\n  AsyncSocket::ByteEventHelper helper = {};\n  helper.byteEventsEnabled = true;\n  helper.rawBytesWrittenWhenByteEventsEnabled =\n      params.rawBytesWrittenWhenByteEventsEnabled;\n\n  EXPECT_FALSE(helper.processCmsg(\n      scmTs,\n      params.rawBytesWrittenWhenTimestampReceived /* rawBytesWritten */));\n  const auto maybeByteEvent = helper.processCmsg(\n      serrTs,\n      params.rawBytesWrittenWhenTimestampReceived /* rawBytesWritten */);\n  ASSERT_TRUE(maybeByteEvent.has_value());\n  const auto& byteEvent = *maybeByteEvent;\n\n  EXPECT_EQ(params.byteTimestamped, byteEvent.offset);\n  EXPECT_EQ(softwareTsSec + softwareTsNs, byteEvent.maybeSoftwareTs);\n}\n\n#endif // FOLLY_HAVE_SO_TIMESTAMPING\n\nTEST(AsyncSocket, LifecycleCtorCallback) {\n  EventBase evb;\n  // create socket and verify that w/o a ctor callback, nothing happens\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_EQ(socket1->getLifecycleObservers().size(), 0);\n\n  // Then register a ctor callback that registers a mock lifecycle observer\n  // NB: use nicemock instead of strict b/c the actual lifecycle testing\n  // is done below and this simplifies the test\n  auto lifecycleCB =\n      std::make_shared<NiceMock<MockAsyncSocketLifecycleObserver>>();\n  auto lifecycleRawPtr = lifecycleCB.get();\n  // verify the first part of the lifecycle was processed\n  ConstructorCallbackList<AsyncSocket>::addCallback(\n      [lifecycleRawPtr](AsyncSocket* s) {\n        s->addLifecycleObserver(lifecycleRawPtr);\n      });\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_EQ(socket2->getLifecycleObservers().size(), 1);\n  EXPECT_THAT(\n      socket2->getLifecycleObservers(),\n      UnorderedElementsAre(lifecycleCB.get()));\n  Mock::VerifyAndClearExpectations(lifecycleCB.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverDetachAndAttachEvb) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  EventBase& evb = getEventBase();\n  auto evb2 = makeEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  // Detach the evb and attach a new evb2\n  EXPECT_CALL(*cb, evbDetachMock(socket.get(), &evb));\n  socket->detachEventBase();\n  EXPECT_EQ(nullptr, socket->getEventBase());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, evbAttachMock(socket.get(), evb2.get()));\n  socket->attachEventBase(evb2.get());\n  EXPECT_EQ(evb2.get(), socket->getEventBase());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  // detach the new evb2 and re-attach the old evb.\n  EXPECT_CALL(*cb, evbDetachMock(socket.get(), evb2.get()));\n  socket->detachEventBase();\n  EXPECT_EQ(nullptr, socket->getEventBase());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, evbAttachMock(socket.get(), &evb));\n  socket->attachEventBase(&evb);\n  EXPECT_EQ(&evb, socket->getEventBase());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  InSequence s;\n  EXPECT_CALL(*cb, destroyMock(socket.get()));\n  socket = nullptr;\n  Mock::VerifyAndClearExpectations(cb.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverAttachThenDestroySocket) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, connectAttemptMock(socket.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket.get()));\n  EXPECT_CALL(*cb, connectSuccessMock(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  InSequence s;\n  EXPECT_CALL(*cb, closeMock(socket.get()));\n  EXPECT_CALL(*cb, destroyMock(socket.get()));\n  socket = nullptr;\n  Mock::VerifyAndClearExpectations(cb.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverAttachThenConnectError) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  // port =1 is unreachble on localhost\n  folly::SocketAddress unreachable{\"::1\", 1};\n\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  // the current state machine calls AsyncSocket::invokeConnectionError() twice\n  // for this use-case...\n  EXPECT_CALL(*cb, connectAttemptMock(socket.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket.get()));\n  EXPECT_CALL(*cb, connectErrorMock(socket.get(), _)).Times(2);\n  EXPECT_CALL(*cb, closeMock(socket.get()));\n  socket->connect(nullptr, unreachable, 1);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, destroyMock(socket.get()));\n  socket = nullptr;\n  Mock::VerifyAndClearExpectations(cb.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverMultipleAttachThenDestroySocket) {\n  auto cb1 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  auto cb2 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb1, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb1.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n\n  EXPECT_CALL(*cb2, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb2.get());\n  EXPECT_THAT(\n      socket->getLifecycleObservers(),\n      UnorderedElementsAre(cb1.get(), cb2.get()));\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n\n  InSequence s;\n  EXPECT_CALL(*cb1, connectAttemptMock(socket.get()));\n  EXPECT_CALL(*cb2, connectAttemptMock(socket.get()));\n  EXPECT_CALL(*cb1, fdAttachMock(socket.get()));\n  EXPECT_CALL(*cb2, fdAttachMock(socket.get()));\n  EXPECT_CALL(*cb1, connectSuccessMock(socket.get()));\n  EXPECT_CALL(*cb2, connectSuccessMock(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n\n  EXPECT_CALL(*cb1, closeMock(socket.get()));\n  EXPECT_CALL(*cb2, closeMock(socket.get()));\n  EXPECT_CALL(*cb1, destroyMock(socket.get()));\n  EXPECT_CALL(*cb2, destroyMock(socket.get()));\n  socket = nullptr;\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverAttachRemove) {\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  EXPECT_CALL(*cb, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb.get()));\n  EXPECT_THAT(socket->getLifecycleObservers(), IsEmpty());\n  Mock::VerifyAndClearExpectations(cb.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverAttachRemoveMultiple) {\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  auto cb1 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  EXPECT_CALL(*cb1, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb1.get());\n  Mock::VerifyAndClearExpectations(cb1.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));\n\n  auto cb2 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  EXPECT_CALL(*cb2, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb2.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n  EXPECT_THAT(\n      socket->getLifecycleObservers(),\n      UnorderedElementsAre(cb1.get(), cb2.get()));\n\n  EXPECT_CALL(*cb1, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb1.get()));\n  Mock::VerifyAndClearExpectations(cb1.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb2.get()));\n\n  EXPECT_CALL(*cb2, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb2.get()));\n  Mock::VerifyAndClearExpectations(cb2.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), IsEmpty());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverAttachRemoveMultipleReverse) {\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n\n  auto cb1 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  EXPECT_CALL(*cb1, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb1.get());\n  Mock::VerifyAndClearExpectations(cb1.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));\n\n  auto cb2 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  EXPECT_CALL(*cb2, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb2.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n  EXPECT_THAT(\n      socket->getLifecycleObservers(),\n      UnorderedElementsAre(cb1.get(), cb2.get()));\n\n  EXPECT_CALL(*cb2, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb2.get()));\n  Mock::VerifyAndClearExpectations(cb2.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));\n\n  EXPECT_CALL(*cb1, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb1.get()));\n  Mock::VerifyAndClearExpectations(cb1.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), IsEmpty());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverRemoveMissing) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_FALSE(socket->removeLifecycleObserver(cb.get()));\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverMultipleAttachThenRemove) {\n  auto cb1 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  auto cb2 = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  auto socket = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb1, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb1.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n\n  EXPECT_CALL(*cb2, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb2.get());\n  EXPECT_THAT(\n      socket->getLifecycleObservers(),\n      UnorderedElementsAre(cb1.get(), cb2.get()));\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n\n  EXPECT_CALL(*cb2, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb2.get()));\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n\n  EXPECT_CALL(*cb1, observerDetachMock(socket.get()));\n  socket->removeLifecycleObserver(cb1.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), IsEmpty());\n  Mock::VerifyAndClearExpectations(cb1.get());\n  Mock::VerifyAndClearExpectations(cb2.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverDetach) {\n  if (GetParam() == BackendType::IO_URING) {\n    GTEST_SKIP() << \"io_uring does not support detachNetworkSocket()\";\n  }\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb, observerAttachMock(socket1.get()));\n  socket1->addLifecycleObserver(cb.get());\n  EXPECT_THAT(socket1->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, connectAttemptMock(socket1.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket1.get()));\n  EXPECT_CALL(*cb, connectSuccessMock(socket1.get()));\n  socket1->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, fdDetachMock(socket1.get()));\n  auto fd = socket1->detachNetworkSocket();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  // create socket2, then immediately destroy it, should get no callbacks\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(&evb, fd));\n  socket2 = nullptr;\n\n  // finally, destroy socket1\n  EXPECT_CALL(*cb, destroyMock(socket1.get()));\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverMoveResubscribe) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb, observerAttachMock(socket1.get()));\n  socket1->addLifecycleObserver(cb.get());\n  EXPECT_THAT(socket1->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, connectAttemptMock(socket1.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket1.get()));\n  EXPECT_CALL(*cb, connectSuccessMock(socket1.get()));\n  socket1->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  AsyncSocket* socket2PtrCapturedmoved = nullptr;\n  {\n    InSequence s;\n    EXPECT_CALL(*cb, fdDetachMock(socket1.get()));\n    EXPECT_CALL(*cb, moveMock(socket1.get(), Not(socket1.get())))\n        .WillOnce(Invoke(\n            [&socket2PtrCapturedmoved, &cb](auto oldSocket, auto newSocket) {\n              socket2PtrCapturedmoved = newSocket;\n              EXPECT_CALL(*cb, observerDetachMock(oldSocket));\n              EXPECT_CALL(*cb, observerAttachMock(newSocket));\n              EXPECT_TRUE(oldSocket->removeLifecycleObserver(cb.get()));\n              EXPECT_THAT(oldSocket->getLifecycleObservers(), IsEmpty());\n              newSocket->addLifecycleObserver(cb.get());\n              EXPECT_THAT(\n                  newSocket->getLifecycleObservers(),\n                  UnorderedElementsAre(cb.get()));\n            }));\n  }\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(std::move(socket1)));\n  Mock::VerifyAndClearExpectations(cb.get());\n  EXPECT_EQ(socket2.get(), socket2PtrCapturedmoved);\n\n  {\n    InSequence s;\n    EXPECT_CALL(*cb, closeMock(socket2.get()));\n    EXPECT_CALL(*cb, destroyMock(socket2.get()));\n  }\n  socket2 = nullptr;\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverMoveDoNotResubscribe) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  auto socket1 = AsyncSocket::UniquePtr(new AsyncSocket(&evb));\n  EXPECT_CALL(*cb, observerAttachMock(socket1.get()));\n  socket1->addLifecycleObserver(cb.get());\n  EXPECT_THAT(socket1->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, connectAttemptMock(socket1.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket1.get()));\n  EXPECT_CALL(*cb, connectSuccessMock(socket1.get()));\n  socket1->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  // close will not be called on socket1 because the fd is detached\n  AsyncSocket* socket2PtrCapturedMoved = nullptr;\n  InSequence s;\n  EXPECT_CALL(*cb, fdDetachMock(socket1.get()));\n  EXPECT_CALL(*cb, moveMock(socket1.get(), Not(socket1.get())))\n      .WillOnce(Invoke(\n          [&socket2PtrCapturedMoved](auto /* oldSocket */, auto newSocket) {\n            socket2PtrCapturedMoved = newSocket;\n          }));\n  EXPECT_CALL(*cb, destroyMock(socket1.get()));\n  auto socket2 = AsyncSocket::UniquePtr(new AsyncSocket(std::move(socket1)));\n  Mock::VerifyAndClearExpectations(cb.get());\n  EXPECT_EQ(socket2.get(), socket2PtrCapturedMoved);\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverDetachCallbackImmediately) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  EXPECT_THAT(socket->getLifecycleObservers(), UnorderedElementsAre(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb.get()));\n  EXPECT_THAT(socket->getLifecycleObservers(), IsEmpty());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  // keep going to ensure no further callbacks\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverDetachCallbackAfterConnect) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, connectAttemptMock(socket.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket.get()));\n  EXPECT_CALL(*cb, connectSuccessMock(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverDetachCallbackAfterClose) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, connectAttemptMock(socket.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket.get()));\n  EXPECT_CALL(*cb, connectSuccessMock(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, closeMock(socket.get()));\n  socket->closeNow();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, observerDetachMock(socket.get()));\n  EXPECT_TRUE(socket->removeLifecycleObserver(cb.get()));\n  Mock::VerifyAndClearExpectations(cb.get());\n}\n\nTEST_P(AsyncSocketTest, LifecycleObserverDetachCallbackcloseDuringDestroy) {\n  auto cb = std::make_unique<StrictMock<MockAsyncSocketLifecycleObserver>>();\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  EXPECT_CALL(*cb, observerAttachMock(socket.get()));\n  socket->addLifecycleObserver(cb.get());\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  EXPECT_CALL(*cb, connectAttemptMock(socket.get()));\n  EXPECT_CALL(*cb, fdAttachMock(socket.get()));\n  EXPECT_CALL(*cb, connectSuccessMock(socket.get()));\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n  Mock::VerifyAndClearExpectations(cb.get());\n\n  InSequence s;\n  EXPECT_CALL(*cb, closeMock(socket.get()))\n      .WillOnce(Invoke([&cb](auto callbackSocket) {\n        EXPECT_TRUE(callbackSocket->removeLifecycleObserver(cb.get()));\n      }));\n  EXPECT_CALL(*cb, observerDetachMock(socket.get()));\n  socket = nullptr;\n  Mock::VerifyAndClearExpectations(cb.get());\n}\n\nTEST_P(AsyncSocketTest, PreReceivedData) {\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n\n  socket->writeChain(nullptr, IOBuf::copyBuffer(\"hello\"));\n\n  auto acceptedSocket = server.acceptAsync(&evb);\n\n  ReadCallback peekCallback(2);\n  ReadCallback readCallback;\n  peekCallback.dataAvailableCallback = [&]() {\n    peekCallback.verifyData(\"he\", 2);\n    acceptedSocket->setPreReceivedData(IOBuf::copyBuffer(\"h\"));\n    acceptedSocket->setPreReceivedData(IOBuf::copyBuffer(\"e\"));\n    acceptedSocket->setReadCB(nullptr);\n    acceptedSocket->setReadCB(&readCallback);\n  };\n  readCallback.dataAvailableCallback = [&]() {\n    if (readCallback.dataRead() == 5) {\n      readCallback.verifyData(\"hello\", 5);\n      acceptedSocket->setReadCB(nullptr);\n      acceptedSocket.reset();\n    }\n  };\n\n  acceptedSocket->setReadCB(&peekCallback);\n\n  evb.loop();\n}\n\nTEST_P(AsyncSocketTest, PreReceivedDataOnly) {\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n\n  socket->writeChain(nullptr, IOBuf::copyBuffer(\"hello\"));\n\n  auto acceptedSocket = server.acceptAsync(&evb);\n\n  ReadCallback peekCallback;\n  ReadCallback readCallback;\n  peekCallback.dataAvailableCallback = [&]() {\n    peekCallback.verifyData(\"hello\", 5);\n    acceptedSocket->setPreReceivedData(IOBuf::copyBuffer(\"hello\"));\n    EXPECT_TRUE(acceptedSocket->readable());\n    acceptedSocket->setReadCB(&readCallback);\n  };\n  readCallback.dataAvailableCallback = [&]() {\n    readCallback.verifyData(\"hello\", 5);\n    acceptedSocket->setReadCB(nullptr);\n    acceptedSocket.reset();\n  };\n\n  acceptedSocket->setReadCB(&peekCallback);\n\n  evb.loop();\n}\n\nTEST_P(AsyncSocketTest, PreReceivedDataPartial) {\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n\n  socket->writeChain(nullptr, IOBuf::copyBuffer(\"hello\"));\n\n  auto acceptedSocket = server.acceptAsync(&evb);\n\n  ReadCallback peekCallback;\n  ReadCallback smallReadCallback(3);\n  ReadCallback normalReadCallback;\n  peekCallback.dataAvailableCallback = [&]() {\n    peekCallback.verifyData(\"hello\", 5);\n    acceptedSocket->setPreReceivedData(IOBuf::copyBuffer(\"hello\"));\n    acceptedSocket->setReadCB(&smallReadCallback);\n  };\n  smallReadCallback.dataAvailableCallback = [&]() {\n    smallReadCallback.verifyData(\"hel\", 3);\n    acceptedSocket->setReadCB(&normalReadCallback);\n  };\n  normalReadCallback.dataAvailableCallback = [&]() {\n    normalReadCallback.verifyData(\"lo\", 2);\n    acceptedSocket->setReadCB(nullptr);\n    acceptedSocket.reset();\n  };\n\n  acceptedSocket->setReadCB(&peekCallback);\n\n  evb.loop();\n}\n\nTEST_P(AsyncSocketTest, PreReceivedDataTakeover) {\n  TestServer server;\n\n  EventBase& evb = getEventBase();\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n  socket->connect(nullptr, server.getAddress(), 30);\n  evb.loop();\n\n  socket->writeChain(nullptr, IOBuf::copyBuffer(\"hello\"));\n\n  auto fd = server.acceptFD();\n  SocketAddress peerAddress;\n  peerAddress.setFromPeerAddress(fd);\n  auto acceptedSocket =\n      AsyncSocket::UniquePtr(new AsyncSocket(&evb, fd, 0, &peerAddress));\n  AsyncSocket::UniquePtr takeoverSocket;\n\n  ReadCallback peekCallback(3);\n  ReadCallback readCallback;\n  peekCallback.dataAvailableCallback = [&]() {\n    peekCallback.verifyData(\"hel\", 3);\n    acceptedSocket->setPreReceivedData(IOBuf::copyBuffer(\"hello\"));\n    acceptedSocket->setReadCB(nullptr);\n    takeoverSocket =\n        AsyncSocket::UniquePtr(new AsyncSocket(std::move(acceptedSocket)));\n    takeoverSocket->setReadCB(&readCallback);\n  };\n  readCallback.dataAvailableCallback = [&]() {\n    readCallback.verifyData(\"hello\", 5);\n    takeoverSocket->setReadCB(nullptr);\n  };\n\n  acceptedSocket->setReadCB(&peekCallback);\n\n  evb.loop();\n  // Verify we can still get the peer address after the peer socket is reset.\n  socket->closeWithReset();\n  evb.loopOnce();\n  SocketAddress socketPeerAddress;\n  takeoverSocket->getPeerAddress(&socketPeerAddress);\n  EXPECT_EQ(socketPeerAddress, peerAddress);\n}\n\n#ifdef MSG_NOSIGNAL\nTEST(AsyncSocketTest, SendMessageFlags) {\n  TestServer server;\n  TestSendMsgParamsCallback sendMsgCB(\n      MSG_DONTWAIT | MSG_NOSIGNAL | MSG_MORE, 0, nullptr);\n\n  // connect()\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 30);\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n\n  evb.loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  // Set SendMsgParamsCallback\n  socket->setSendMsgParamCB(&sendMsgCB);\n  ASSERT_EQ(socket->getSendMsgParamsCB(), &sendMsgCB);\n\n  // Write the first portion of data. This data is expected to be\n  // sent out immediately.\n  std::vector<uint8_t> buf(128, 'a');\n  WriteCallback wcb;\n  sendMsgCB.reset(MSG_DONTWAIT | MSG_NOSIGNAL);\n  socket->write(&wcb, buf.data(), buf.size());\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  ASSERT_TRUE(sendMsgCB.queriedFlags_);\n  ASSERT_FALSE(sendMsgCB.queriedData_);\n\n  // Using different flags for the second write operation.\n  // MSG_MORE flag is expected to delay sending this\n  // data to the wire.\n  sendMsgCB.reset(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_MORE);\n  socket->write(&wcb, buf.data(), buf.size());\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  ASSERT_TRUE(sendMsgCB.queriedFlags_);\n  ASSERT_FALSE(sendMsgCB.queriedData_);\n\n  // Make sure the accepted socket saw only the data from\n  // the first write request.\n  std::vector<uint8_t> readbuf(2 * buf.size());\n  uint32_t bytesRead = acceptedSocket->read(readbuf.data(), readbuf.size());\n  ASSERT_TRUE(std::equal(buf.begin(), buf.end(), readbuf.begin()));\n  ASSERT_EQ(bytesRead, buf.size());\n\n  // Make sure the server got a connection and received the data\n  acceptedSocket->close();\n  socket->close();\n\n  ASSERT_TRUE(socket->isClosedBySelf());\n  ASSERT_FALSE(socket->isClosedByPeer());\n}\n\nTEST(AsyncSocketTest, SendMessageAncillaryData) {\n  NetworkSocket fds[2];\n  EXPECT_EQ(netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds), 0);\n\n  // \"Client\" socket\n  auto cfd = fds[0];\n  ASSERT_NE(cfd, NetworkSocket());\n\n  // \"Server\" socket\n  auto sfd = fds[1];\n  ASSERT_NE(sfd, NetworkSocket());\n  SCOPE_EXIT {\n    netops::close(sfd);\n  };\n\n  // Instantiate AsyncSocket object for the connected socket\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb, cfd);\n\n  // Open a temporary file and write a magic string to it\n  // We'll transfer the file handle to test the message parameters\n  // callback logic.\n  TemporaryFile file(\n      StringPiece(), fs::path(), TemporaryFile::Scope::UNLINK_IMMEDIATELY);\n  int tmpfd = file.fd();\n  ASSERT_NE(tmpfd, -1) << \"Failed to open a temporary file\";\n  std::string magicString(\"Magic string\");\n  ASSERT_EQ(\n      fileops::write(tmpfd, magicString.c_str(), magicString.length()),\n      magicString.length());\n\n  // Send message\n  union {\n    // Space large enough to hold an 'int'\n    char control[CMSG_SPACE(sizeof(int))];\n    struct cmsghdr cmh;\n  } s_u;\n  s_u.cmh.cmsg_len = CMSG_LEN(sizeof(int));\n  s_u.cmh.cmsg_level = SOL_SOCKET;\n  s_u.cmh.cmsg_type = SCM_RIGHTS;\n  memcpy(CMSG_DATA(&s_u.cmh), &tmpfd, sizeof(int));\n\n  // Set up the callback providing message parameters\n  TestSendMsgParamsCallback sendMsgCB(\n      MSG_DONTWAIT | MSG_NOSIGNAL, sizeof(s_u.control), s_u.control);\n  socket->setSendMsgParamCB(&sendMsgCB);\n\n  // We must transmit at least 1 byte of real data in order\n  // to send ancillary data\n  int s_data = 12345;\n  WriteCallback wcb;\n  auto ioBuf = folly::IOBuf::wrapBuffer(&s_data, sizeof(s_data));\n  sendMsgCB.expectedTag_ = folly::AsyncSocket::WriteRequestTag{\n      ioBuf.get()}; // Also test write tagging.\n  ASSERT_FALSE(sendMsgCB.tagLastWritten_.has_value());\n  socket->writeChain(&wcb, std::move(ioBuf));\n  ASSERT_EQ(wcb.state, STATE_SUCCEEDED);\n  ASSERT_TRUE(sendMsgCB.queriedData_); // Did the tag check run?\n  ASSERT_EQ(sendMsgCB.expectedTag_, *sendMsgCB.tagLastWritten_);\n\n  // Receive the message\n  union {\n    // Space large enough to hold an 'int'\n    char control[CMSG_SPACE(sizeof(int))];\n    struct cmsghdr cmh;\n  } r_u;\n  struct msghdr msgh;\n  struct iovec iov;\n  int r_data = 0;\n\n  msgh.msg_control = r_u.control;\n  msgh.msg_controllen = sizeof(r_u.control);\n  msgh.msg_name = nullptr;\n  msgh.msg_namelen = 0;\n  msgh.msg_iov = &iov;\n  msgh.msg_iovlen = 1;\n  iov.iov_base = &r_data;\n  iov.iov_len = sizeof(r_data);\n\n  // Receive data\n  ASSERT_NE(netops::recvmsg(sfd, &msgh, 0), -1) << \"recvmsg failed: \" << errno;\n\n  // Validate the received message\n  ASSERT_EQ(r_u.cmh.cmsg_len, CMSG_LEN(sizeof(int)));\n  ASSERT_EQ(r_u.cmh.cmsg_level, SOL_SOCKET);\n  ASSERT_EQ(r_u.cmh.cmsg_type, SCM_RIGHTS);\n  ASSERT_EQ(r_data, s_data);\n  int fd = 0;\n  memcpy(&fd, CMSG_DATA(&r_u.cmh), sizeof(int));\n  ASSERT_NE(fd, 0);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n\n  std::vector<uint8_t> transferredMagicString(magicString.length() + 1, 0);\n\n  // Reposition to the beginning of the file\n  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));\n\n  // Read the magic string back, and compare it with the original\n  ASSERT_EQ(\n      magicString.length(),\n      folly::fileops::read(\n          fd, transferredMagicString.data(), transferredMagicString.size()));\n  ASSERT_TRUE(\n      std::equal(\n          magicString.begin(),\n          magicString.end(),\n          transferredMagicString.begin()));\n}\n\nnamespace {\n\n// Child classes of AsyncSocket (e.g. AsyncFdSocket) want to be able to\n// fail reads from the read ancillary data or regular read callback. Test this.\nstruct FailableSocket : public AsyncSocket {\n  FailableSocket(EventBase* evb, NetworkSocket fd) : AsyncSocket(evb, fd) {}\n  void testFailRead() {\n    AsyncSocketException ex(\n        AsyncSocketException::INTERNAL_ERROR, \"FailableSocket::testFailRead\");\n    AsyncSocket::failRead(__func__, ex);\n  }\n};\n\nclass TruncateAncillaryDataAndCallFn\n    : public folly::AsyncSocket::ReadAncillaryDataCallback {\n public:\n  explicit TruncateAncillaryDataAndCallFn(VoidCallback cob)\n      : callback_(std::move(cob)) {}\n\n  folly::Expected<folly::Unit, AsyncSocketException> ancillaryData(\n      struct msghdr& msg) noexcept override {\n    sawCtrunc_ = sawCtrunc_ || (msg.msg_flags & MSG_CTRUNC);\n    callback_();\n\n    return folly::unit;\n  }\n  folly::MutableByteRange getAncillaryDataCtrlBuffer() override {\n    return folly::MutableByteRange(ancillaryDataCtrlBuffer_);\n  }\n\n  bool sawCtrunc_{false};\n\n private:\n  VoidCallback callback_;\n  // Empty to trigger MSG_CTRUNC\n  std::array<uint8_t, 0> ancillaryDataCtrlBuffer_;\n};\n\n// Returns the error string from the read callback (can be \"none\")\nstd::string testTruncateAncillaryDataAndCall(\n    std::function<void(FailableSocket*)> fn,\n    std::function<void(FailableSocket*)> postConditionCheck) {\n  NetworkSocket fds[2];\n  CHECK_EQ(netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds), 0);\n\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> sendSock = AsyncSocket::newSocket(&evb, fds[0]);\n  ReadCallback rcb; // outlives socket since ~AsyncSocket calls rcb.readEOF\n  FailableSocket recvSock(&evb, fds[1]);\n\n  TruncateAncillaryDataAndCallFn ancillaryCob{[&]() { fn(&recvSock); }};\n  recvSock.setReadAncillaryDataCB(&ancillaryCob);\n\n  // Send the stderr FD with ancillary data\n  int tmpfd = 2;\n  union { // `man cmsg` suggests this idiom for a \"large enough\" `cmsghdr`\n    char buf[CMSG_SPACE(sizeof(tmpfd))];\n    struct cmsghdr cmh;\n  } u;\n  u.cmh.cmsg_len = CMSG_LEN(sizeof(tmpfd));\n  u.cmh.cmsg_level = SOL_SOCKET;\n  u.cmh.cmsg_type = SCM_RIGHTS;\n  memcpy(CMSG_DATA(&u.cmh), &tmpfd, sizeof(tmpfd));\n\n  TestSendMsgParamsCallback sendMsgCB(\n      MSG_DONTWAIT | MSG_NOSIGNAL, sizeof(u.buf), u.buf);\n  sendSock->setSendMsgParamCB(&sendMsgCB);\n\n  // Transmit at least 1 byte of real data to send ancillary data\n  int s_data = 12345;\n  WriteCallback wcb;\n  sendSock->write(&wcb, &s_data, sizeof(s_data));\n  CHECK_EQ(wcb.state, STATE_SUCCEEDED);\n\n  // The FD will be discarded (MSG_CTRUNC) since our ancillary data callback\n  // deliberately misconfigures the `recvmsg`.\n  recvSock.setReadCB(&rcb);\n  CHECK(!ancillaryCob.sawCtrunc_);\n  evb.loopOnce();\n\n  // Ensure that `ancillaryData()` actually ran, and saw the error condition.\n  CHECK(ancillaryCob.sawCtrunc_);\n  postConditionCheck(&recvSock);\n\n  return rcb.exception.what();\n}\n\n} // namespace\n\n// These tests do double-duty:\n//  - show that `ReadAncillaryDataCallback` can safely close or fail a socket\n//  - exercise getting & handling `MSG_CTRUNC`\n\nTEST(AsyncSocketTest, ReceiveTruncatedAncillaryDataAndFail) {\n  EXPECT_THAT(\n      testTruncateAncillaryDataAndCall(\n          [](FailableSocket* sock) { sock->testFailRead(); },\n          [](FailableSocket* sock) { ASSERT_TRUE(sock->error()); }),\n      testing::HasSubstr(\"FailableSocket::testFailRead\"));\n}\n\nTEST(AsyncSocketTest, ReceiveTruncatedAncillaryDataAndClose) {\n  EXPECT_THAT(\n      testTruncateAncillaryDataAndCall(\n          [](FailableSocket* sock) { sock->close(); },\n          [](FailableSocket* sock) { ASSERT_TRUE(sock->isClosedBySelf()); }),\n      testing::HasSubstr(\"AsyncSocketException: none, type =\")); // no error\n}\n\nTEST(AsyncSocketTest, ReceiveTruncatedAncillaryDataUnhandled) {\n  // Since this `ancillaryData` fails to check MSG_CTRUNG, the last-ditch\n  // check in `AsyncSocket::processNormalRead` will fire.\n  EXPECT_THAT(\n      testTruncateAncillaryDataAndCall(\n          [](FailableSocket*) {},\n          [](FailableSocket* sock) { ASSERT_TRUE(sock->error()); }),\n      testing::HasSubstr(\"recvmsg() got MSG_CTRUNC\"));\n}\n\nTEST(AsyncSocketTest, UnixDomainSocketErrMessageCB) {\n  // In the latest stable kernel 4.14.3 as of 2017-12-04, Unix Domain\n  // Socket (UDS) does not support MSG_ERRQUEUE. So\n  // recvmsg(MSG_ERRQUEUE) will read application data from UDS which\n  // breaks application message flow.  To avoid this problem,\n  // AsyncSocket currently disables setErrMessageCB for UDS.\n  //\n  // This tests two things for UDS\n  // 1. setErrMessageCB fails\n  // 2. recvmsg(MSG_ERRQUEUE) reads application data\n  //\n  // Feel free to remove this test if UDS supports MSG_ERRQUEUE in the future.\n\n  NetworkSocket fd[2];\n  EXPECT_EQ(netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fd), 0);\n  ASSERT_NE(fd[0], NetworkSocket());\n  ASSERT_NE(fd[1], NetworkSocket());\n  SCOPE_EXIT {\n    netops::close(fd[1]);\n  };\n\n  EXPECT_EQ(netops::set_socket_non_blocking(fd[0]), 0);\n  EXPECT_EQ(netops::set_socket_non_blocking(fd[1]), 0);\n\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb, fd[0]);\n\n  // setErrMessageCB should fail for unix domain socket\n  TestErrMessageCallback errMsgCB;\n  ASSERT_NE(&errMsgCB, nullptr);\n  socket->setErrMessageCB(&errMsgCB);\n  ASSERT_EQ(socket->getErrMessageCallback(), nullptr);\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  // The following verifies that MSG_ERRQUEUE does not work for UDS,\n  // and recvmsg reads application data\n  union {\n    // Space large enough to hold an 'int'\n    char control[CMSG_SPACE(sizeof(int))];\n    struct cmsghdr cmh;\n  } r_u;\n  struct msghdr msgh;\n  struct iovec iov;\n  int recv_data = 0;\n\n  msgh.msg_control = r_u.control;\n  msgh.msg_controllen = sizeof(r_u.control);\n  msgh.msg_name = nullptr;\n  msgh.msg_namelen = 0;\n  msgh.msg_iov = &iov;\n  msgh.msg_iovlen = 1;\n  iov.iov_base = &recv_data;\n  iov.iov_len = sizeof(recv_data);\n\n  // there is no data, recvmsg should fail\n  EXPECT_EQ(netops::recvmsg(fd[1], &msgh, MSG_ERRQUEUE), -1);\n  EXPECT_TRUE(errno == EAGAIN || errno == EWOULDBLOCK);\n\n  // provide some application data, error queue should be empty if it exists\n  // However, UDS reads application data as error message\n  int test_data = 123456;\n  WriteCallback wcb;\n  socket->write(&wcb, &test_data, sizeof(test_data));\n  recv_data = 0;\n  ASSERT_NE(netops::recvmsg(fd[1], &msgh, MSG_ERRQUEUE), -1);\n  ASSERT_EQ(recv_data, test_data);\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n}\n\nTEST_P(AsyncSocketTest, V6TosReflectTest) {\n  EventBase& eventBase = getEventBase();\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  folly::IPAddress ip(\"::1\");\n  std::vector<folly::IPAddress> serverIp;\n  serverIp.push_back(ip);\n  serverSocket->bind(serverIp, 0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Enable TOS reflect\n  serverSocket->setTosReflect(true);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  // Create a client socket, setsockopt() the TOS before connecting\n  auto clientThread =\n      [](std::shared_ptr<AsyncSocket>& clientSock,\n         ConnCallback* ccb,\n         EventBase* evb,\n         folly::SocketAddress sAddr) {\n        clientSock = AsyncSocket::newSocket(evb);\n        SocketOptionKey v6Opts = {IPPROTO_IPV6, IPV6_TCLASS};\n        SocketOptionMap optionMap;\n        optionMap.insert({v6Opts, 0x2c});\n        SocketAddress bindAddr(\"0.0.0.0\", 0);\n        clientSock->connect(ccb, sAddr, 30, optionMap, bindAddr);\n      };\n\n  std::shared_ptr<AsyncSocket> socket(nullptr);\n  ConnCallback cb;\n  clientThread(socket, &cb, &eventBase, serverAddress);\n\n  eventBase.loop();\n\n  // Verify if the connection is accepted and if the accepted socket has\n  // setsockopt on the TOS for the same value that was on the client socket\n  auto fd = acceptCallback.getEvents()->at(1).fd;\n  ASSERT_NE(fd, NetworkSocket());\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc =\n      netops::getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 0x2c);\n\n  // Additional Test for ConnectCallback without bindAddr\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  auto newClientSock = AsyncSocket::newSocket(&eventBase);\n  TestConnectCallback callback;\n  // connect call will not set this SO_REUSEADDR if we do not\n  // pass the bindAddress in its call; so we can safely verify this.\n  newClientSock->connect(&callback, serverAddress, 30);\n\n  // Collect events\n  eventBase.loop();\n\n  auto acceptedFd = acceptCallback.getEvents()->at(1).fd;\n  ASSERT_NE(acceptedFd, NetworkSocket());\n  int reuseAddrVal;\n  socklen_t reuseAddrValLen = sizeof(reuseAddrVal);\n  // Get the socket created underneath connect call of AsyncSocket\n  auto usedSockFd = newClientSock->getNetworkSocket();\n  int getOptRet = netops::getsockopt(\n      usedSockFd, SOL_SOCKET, SO_REUSEADDR, &reuseAddrVal, &reuseAddrValLen);\n  ASSERT_EQ(getOptRet, 0);\n  ASSERT_EQ(reuseAddrVal, 1 /* configured through preConnect*/);\n}\n\nTEST_P(AsyncSocketTest, V4TosReflectTest) {\n  EventBase& eventBase = getEventBase();\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  folly::IPAddress ip(\"127.0.0.1\");\n  std::vector<folly::IPAddress> serverIp;\n  serverIp.push_back(ip);\n  serverSocket->bind(serverIp, 0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Enable TOS reflect\n  serverSocket->setTosReflect(true);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  // Create a client socket, setsockopt() the TOS before connecting\n  auto clientThread =\n      [](std::shared_ptr<AsyncSocket>& clientSock,\n         ConnCallback* ccb,\n         EventBase* evb,\n         folly::SocketAddress sAddr) {\n        clientSock = AsyncSocket::newSocket(evb);\n        SocketOptionKey v4Opts = {IPPROTO_IP, IP_TOS};\n        SocketOptionMap optionMap;\n        optionMap.insert({v4Opts, 0x2c});\n        SocketAddress bindAddr(\"0.0.0.0\", 0);\n        clientSock->connect(ccb, sAddr, 30, optionMap, bindAddr);\n      };\n\n  std::shared_ptr<AsyncSocket> socket(nullptr);\n  ConnCallback cb;\n  clientThread(socket, &cb, &eventBase, serverAddress);\n\n  eventBase.loop();\n\n  // Verify if the connection is accepted and if the accepted socket has\n  // setsockopt on the TOS for the same value that was on the client socket\n  auto fd = acceptCallback.getEvents()->at(1).fd;\n  ASSERT_NE(fd, NetworkSocket());\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc = netops::getsockopt(fd, IPPROTO_IP, IP_TOS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 0x2c);\n}\n\nTEST_P(AsyncSocketTest, V6AcceptedTosTest) {\n  EventBase& eventBase = getEventBase();\n\n  // This test verifies if the ListenerTos set on a socket is\n  // propagated properly to accepted socket connections\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  folly::IPAddress ip(\"::1\");\n  std::vector<folly::IPAddress> serverIp;\n  serverIp.push_back(ip);\n  serverSocket->bind(serverIp, 0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Set listener TOS to 0x74 i.e. dscp 29\n  serverSocket->setListenerTos(0x74);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  // Create a client socket, setsockopt() the TOS before connecting\n  auto clientThread =\n      [](std::shared_ptr<AsyncSocket>& clientSock,\n         ConnCallback* ccb,\n         EventBase* evb,\n         folly::SocketAddress sAddr) {\n        clientSock = AsyncSocket::newSocket(evb);\n        SocketOptionKey v6Opts = {IPPROTO_IPV6, IPV6_TCLASS};\n        SocketOptionMap optionMap;\n        optionMap.insert({v6Opts, 0x2c});\n        SocketAddress bindAddr(\"0.0.0.0\", 0);\n        clientSock->connect(ccb, sAddr, 30, optionMap, bindAddr);\n      };\n\n  std::shared_ptr<AsyncSocket> socket(nullptr);\n  ConnCallback cb;\n  clientThread(socket, &cb, &eventBase, serverAddress);\n\n  eventBase.loop();\n\n  // Verify if the connection is accepted and if the accepted socket has\n  // setsockopt on the TOS for the same value that the listener was set to\n  auto fd = acceptCallback.getEvents()->at(1).fd;\n  ASSERT_NE(fd, NetworkSocket());\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc =\n      netops::getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 0x74);\n}\n\nTEST_P(AsyncSocketTest, V4AcceptedTosTest) {\n  EventBase& eventBase = getEventBase();\n\n  // This test verifies if the ListenerTos set on a socket is\n  // propagated properly to accepted socket connections\n\n  // Create a server socket\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  folly::IPAddress ip(\"127.0.0.1\");\n  std::vector<folly::IPAddress> serverIp;\n  serverIp.push_back(ip);\n  serverSocket->bind(serverIp, 0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  // Set listener TOS to 0x74 i.e. dscp 29\n  serverSocket->setListenerTos(0x74);\n\n  // Add a callback to accept one connection then stop the loop\n  TestAcceptCallback acceptCallback;\n  acceptCallback.setConnectionAcceptedFn(\n      [&](NetworkSocket /* fd */, const folly::SocketAddress& /* addr */) {\n        serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n      });\n  acceptCallback.setAcceptErrorFn([&](const std::exception& /* ex */) {\n    serverSocket->removeAcceptCallback(&acceptCallback, &eventBase);\n  });\n  serverSocket->addAcceptCallback(&acceptCallback, &eventBase);\n  serverSocket->startAccepting();\n\n  // Create a client socket, setsockopt() the TOS before connecting\n  auto clientThread =\n      [](std::shared_ptr<AsyncSocket>& clientSock,\n         ConnCallback* ccb,\n         EventBase* evb,\n         folly::SocketAddress sAddr) {\n        clientSock = AsyncSocket::newSocket(evb);\n        SocketOptionKey v4Opts = {IPPROTO_IP, IP_TOS};\n        SocketOptionMap optionMap;\n        optionMap.insert({v4Opts, 0x2c});\n        SocketAddress bindAddr(\"0.0.0.0\", 0);\n        clientSock->connect(ccb, sAddr, 30, optionMap, bindAddr);\n      };\n\n  std::shared_ptr<AsyncSocket> socket(nullptr);\n  ConnCallback cb;\n  clientThread(socket, &cb, &eventBase, serverAddress);\n\n  eventBase.loop();\n\n  // Verify if the connection is accepted and if the accepted socket has\n  // setsockopt on the TOS for the same value that the listener was set to\n  auto fd = acceptCallback.getEvents()->at(1).fd;\n  ASSERT_NE(fd, NetworkSocket());\n  int value;\n  socklen_t valueLength = sizeof(value);\n  int rc = netops::getsockopt(fd, IPPROTO_IP, IP_TOS, &value, &valueLength);\n  ASSERT_EQ(rc, 0);\n  ASSERT_EQ(value, 0x74);\n}\n#endif\n\n#if defined(__linux__)\nTEST(AsyncSocketTest, getBufInUse) {\n  EventBase eventBase;\n  std::shared_ptr<AsyncServerSocket> server(\n      AsyncServerSocket::newSocket(&eventBase));\n  server->bind(0);\n  server->listen(5);\n\n  std::shared_ptr<AsyncSocket> client = AsyncSocket::newSocket(&eventBase);\n  client->connect(nullptr, server->getAddress());\n\n  NetworkSocket servfd = server->getNetworkSocket();\n  NetworkSocket accepted;\n  uint64_t maxTries = 5;\n\n  do {\n    std::this_thread::yield();\n    eventBase.loop();\n    accepted = netops::accept(servfd, nullptr, nullptr);\n  } while (accepted == NetworkSocket() && --maxTries);\n\n  // Exhaustion number of tries to accept client connection, good bye\n  ASSERT_TRUE(accepted != NetworkSocket());\n\n  auto clientAccepted = AsyncSocket::newSocket(nullptr, accepted);\n\n  // Use minimum receive buffer size\n  clientAccepted->setRecvBufSize(0);\n\n  // Use maximum send buffer size\n  client->setSendBufSize((unsigned)-1);\n\n  std::string testData;\n  for (int i = 0; i < 10000; ++i) {\n    testData += \"0123456789\";\n  }\n\n  client->write(nullptr, (const void*)testData.c_str(), testData.size());\n\n  std::this_thread::yield();\n  eventBase.loop();\n\n  size_t recvBufSize = clientAccepted->getRecvBufInUse();\n  size_t sendBufSize = client->getSendBufInUse();\n\n  EXPECT_EQ((recvBufSize + sendBufSize), testData.size());\n  EXPECT_GT(recvBufSize, 0);\n  EXPECT_GT(sendBufSize, 0);\n}\n#endif\n\nTEST(AsyncSocketTest, QueueTimeout) {\n  // Create a new AsyncServerSocket\n  EventBase eventBase;\n  std::shared_ptr<AsyncServerSocket> serverSocket(\n      AsyncServerSocket::newSocket(&eventBase));\n  serverSocket->bind(0);\n  serverSocket->listen(16);\n  folly::SocketAddress serverAddress;\n  serverSocket->getAddress(&serverAddress);\n\n  constexpr auto kConnectionTimeout = milliseconds(10);\n  serverSocket->setQueueTimeout(kConnectionTimeout);\n\n  TestAcceptCallback acceptCb;\n  acceptCb.setConnectionAcceptedFn([&, called = false](auto&&...) mutable {\n    ASSERT_FALSE(called)\n        << \"Only the first connection should have been dequeued\";\n    called = true;\n    // Allow plenty of time for the AsyncSocketServer's event loop to run.\n    // This should leave no doubt that the acceptor thread has enough time\n    // to dequeue. If the dequeue succeeds, then our expiry code is broken.\n    static constexpr auto kEventLoopTime = kConnectionTimeout * 5;\n    eventBase.runInEventBaseThread([&]() {\n      eventBase.tryRunAfterDelay(\n          [&]() { serverSocket->removeAcceptCallback(&acceptCb, nullptr); },\n          milliseconds(kEventLoopTime).count());\n    });\n    // After the first message is enqueued, sleep long enough so that the\n    // second message expires before it has a chance to dequeue.\n    std::this_thread::sleep_for(kConnectionTimeout);\n  });\n  ScopedEventBaseThread acceptThread(\"ioworker_test\");\n\n  TestConnectionEventCallback connectionEventCb;\n  serverSocket->setConnectionEventCallback(&connectionEventCb);\n  serverSocket->addAcceptCallback(&acceptCb, acceptThread.getEventBase());\n  serverSocket->startAccepting();\n\n  std::shared_ptr<AsyncSocket> clientSocket1(\n      AsyncSocket::newSocket(&eventBase, serverAddress));\n  std::shared_ptr<AsyncSocket> clientSocket2(\n      AsyncSocket::newSocket(&eventBase, serverAddress));\n\n  // Loop until we are stopped\n  eventBase.loop();\n\n  EXPECT_EQ(connectionEventCb.getConnectionEnqueuedForAcceptCallback(), 2);\n  // Since the second message is expired, it should NOT be dequeued\n  EXPECT_EQ(connectionEventCb.getConnectionDequeuedByAcceptCallback(), 1);\n}\n\nclass TestRXTimestampsCallback\n    : public folly::AsyncSocket::ReadAncillaryDataCallback {\n public:\n  explicit TestRXTimestampsCallback(AsyncSocket* sock) : socket_(sock) {}\n\n  folly::Expected<folly::Unit, AsyncSocketException> ancillaryData(\n      struct msghdr& msgh) noexcept override {\n    if (closeSocket_) {\n      socket_->close();\n      return folly::unit;\n    }\n\n    struct cmsghdr* cmsg;\n    for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != nullptr;\n         cmsg = CMSG_NXTHDR(&msgh, cmsg)) {\n      if (cmsg->cmsg_level != SOL_SOCKET ||\n          cmsg->cmsg_type != SO_TIMESTAMPING) {\n        continue;\n      }\n      callCount_++;\n      timespec* ts = (struct timespec*)CMSG_DATA(cmsg);\n      actualRxTimestampSec_ = ts[0].tv_sec;\n    }\n    return folly::unit;\n  }\n  folly::MutableByteRange getAncillaryDataCtrlBuffer() override {\n    return folly::MutableByteRange(ancillaryDataCtrlBuffer_);\n  }\n\n  uint32_t callCount_{0};\n  long actualRxTimestampSec_{0};\n  bool closeSocket_{false};\n\n private:\n  AsyncSocket* socket_;\n  std::array<uint8_t, 1024> ancillaryDataCtrlBuffer_;\n};\n\n/**\n * Test read ancillary data callback\n */\nTEST(AsyncSocketTest, readAncillaryData) {\n  TestServer server;\n\n  // connect()\n  EventBase evb;\n  std::shared_ptr<AsyncSocket> socket = AsyncSocket::newSocket(&evb);\n\n  ConnCallback ccb;\n  socket->connect(&ccb, server.getAddress(), 1);\n  LOG(INFO) << \"Client socket fd=\" << socket->getNetworkSocket();\n\n  // Enable rx timestamp notifications\n  ASSERT_NE(socket->getNetworkSocket(), NetworkSocket());\n  int flags = folly::netops::SOF_TIMESTAMPING_SOFTWARE |\n      folly::netops::SOF_TIMESTAMPING_RX_SOFTWARE |\n      folly::netops::SOF_TIMESTAMPING_RX_HARDWARE;\n  SocketOptionKey tstampingOpt = {SOL_SOCKET, SO_TIMESTAMPING};\n  EXPECT_EQ(tstampingOpt.apply(socket->getNetworkSocket(), flags), 0);\n\n  // Accept the connection.\n  std::shared_ptr<BlockingSocket> acceptedSocket = server.accept();\n  LOG(INFO) << \"Server socket fd=\" << acceptedSocket->getNetworkSocket();\n\n  // Wait for connection\n  evb.loop();\n  ASSERT_EQ(ccb.state, STATE_SUCCEEDED);\n\n  TestRXTimestampsCallback rxcb{socket.get()};\n\n  // Set read callback\n  ReadCallback rcb(100);\n  socket->setReadCB(&rcb);\n\n  // Get the timestamp when the message was write\n  struct timespec currentTime;\n  clock_gettime(CLOCK_REALTIME, &currentTime);\n  long writeTimestampSec = currentTime.tv_sec;\n\n  // write bytes from server (acceptedSocket) to client (socket).\n  std::vector<uint8_t> wbuf(128, 'a');\n  acceptedSocket->write(wbuf.data(), wbuf.size());\n\n  // Wait for reading to complete.\n  evb.loopOnce();\n  ASSERT_NE(rcb.buffers.size(), 0);\n\n  // Verify that if the callback is not set, it will not be called\n  ASSERT_EQ(rxcb.callCount_, 0);\n\n  // Set up rx timestamp callbacks\n  socket->setReadAncillaryDataCB(&rxcb);\n  acceptedSocket->write(wbuf.data(), wbuf.size());\n\n  // Wait for reading to complete.\n  evb.loopOnce();\n  ASSERT_NE(rcb.buffers.size(), 0);\n\n  // Verify that after setting callback, the callback was called\n  ASSERT_GT(rxcb.callCount_, 0);\n  // Compare the received timestamp is within an expected range\n  clock_gettime(CLOCK_REALTIME, &currentTime);\n  ASSERT_TRUE(rxcb.actualRxTimestampSec_ <= currentTime.tv_sec);\n  ASSERT_TRUE(rxcb.actualRxTimestampSec_ >= writeTimestampSec);\n\n  // Check that the callback can close the socket.\n  rxcb.closeSocket_ = true;\n  ASSERT_FALSE(socket->isClosedBySelf());\n  acceptedSocket->write(wbuf.data(), wbuf.size());\n  evb.loopOnce();\n  ASSERT_TRUE(socket->isClosedBySelf());\n}\n\nclass AsyncSocketWriteCallbackTest : public ::testing::Test {\n protected:\n  using MockDispatcher = ::testing::NiceMock<netops::test::MockDispatcher>;\n  void SetUp() override {\n    socket_ = AsyncSocket::newSocket(&evb_);\n    socket_->setOverrideNetOpsDispatcher(netOpsDispatcher_);\n    netOpsDispatcher_->forwardToDefaultImpl();\n\n    socket_->connect(nullptr, server_.getAddress());\n  }\n\n  void netOpsOnSendmsg() {\n    ON_CALL(*netOpsDispatcher_, sendmsg(_, _, _))\n        .WillByDefault(\n            ::testing::Invoke(\n                [this](NetworkSocket s, const msghdr* message, int flags) {\n                  sendMsgInvocations_++;\n                  return netops::Dispatcher::getDefaultInstance()->sendmsg(\n                      s, message, flags);\n                }));\n  }\n\n  // simulate spliting a write into two parts by returning less than the amount\n  // of bytes that was written if this is the first invocation of sendMsg\n  void netOpsOnSendmsgPartial() {\n    ON_CALL(*netOpsDispatcher_, sendmsg(_, _, _))\n        .WillByDefault(\n            ::testing::Invoke(\n                [this](NetworkSocket s, const msghdr* message, int flags) {\n                  sendMsgInvocations_++;\n                  auto totalWritten =\n                      netops::Dispatcher::getDefaultInstance()->sendmsg(\n                          s, message, flags);\n                  if (splitNextWrite_) {\n                    splitNextWrite_ = false;\n                    return totalWritten - 1;\n                  } else {\n                    splitNextWrite_ = true;\n                    return totalWritten;\n                  }\n                }));\n  }\n\n  // simulate a failed write by returning -1 on sendMsg\n  void netOpsOnSendmsgFail() {\n    ON_CALL(*netOpsDispatcher_, sendmsg(_, _, _))\n        .WillByDefault(\n            ::testing::Invoke(\n                [this](NetworkSocket s, const msghdr* message, int flags) {\n                  sendMsgInvocations_++;\n                  netops::Dispatcher::getDefaultInstance()->sendmsg(\n                      s, message, flags);\n                  return -1;\n                }));\n  }\n\n  WriteCallback writeCallback1_;\n  WriteCallback writeCallback2_;\n  TestServer server_;\n  std::shared_ptr<AsyncSocket> socket_;\n  folly::EventBase evb_;\n  std::shared_ptr<MockDispatcher> netOpsDispatcher_{\n      std::make_shared<MockDispatcher>()};\n  size_t sendMsgInvocations_{0};\n  bool splitNextWrite_{false};\n};\n\n/**\n * Call write once successfully and expect `writeStarting` to be called once.\n */\nTEST_F(AsyncSocketWriteCallbackTest, WriteStartingTests_WriteOnceSuccess) {\n  const std::vector<uint8_t> wbuf(20, 'a');\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  WriteFlags flags = WriteFlags::NONE;\n\n  netOpsOnSendmsg();\n\n  ASSERT_THAT(writeCallback1_.writeStartingInvocations, Eq(0));\n  ASSERT_THAT(writeCallback2_.writeStartingInvocations, Eq(0));\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  while (writeCallback1_.state == STATE_WAITING) {\n    socket_->getEventBase()->loopOnce();\n  }\n  ASSERT_EQ(writeCallback1_.state, STATE_SUCCEEDED);\n  ASSERT_EQ(writeCallback2_.state, STATE_WAITING);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n  EXPECT_EQ(writeCallback2_.writeStartingInvocations, 0);\n  EXPECT_EQ(sendMsgInvocations_, 1);\n}\n\n/**\n * Call write once but do not write all bytes the first time; expect\n * `writeStarting` to be called once.\n */\nTEST_F(AsyncSocketWriteCallbackTest, WriteStartingTests_WriteOnceIncomplete) {\n  const std::vector<uint8_t> wbuf(20, 'a');\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  WriteFlags flags = WriteFlags::NONE;\n\n  // make sure there are no pending WriteRequests\n  socket_->getEventBase()->loopOnce();\n\n  splitNextWrite_ = true;\n  netOpsOnSendmsgPartial();\n\n  ASSERT_THAT(writeCallback1_.writeStartingInvocations, Eq(0));\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  while (writeCallback1_.state == STATE_WAITING) {\n    socket_->getEventBase()->loopOnce();\n  }\n\n  ASSERT_EQ(writeCallback1_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n  EXPECT_EQ(sendMsgInvocations_, 2);\n}\n\n/**\n * Call write twice successfully and expect `writeStarting` to be called twice.\n */\nTEST_F(AsyncSocketWriteCallbackTest, WriteStartingTests_WriteTwiceSuccess) {\n  const std::vector<uint8_t> wbuf(20, 'a');\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  WriteFlags flags = WriteFlags::NONE;\n\n  netOpsOnSendmsg();\n\n  ASSERT_THAT(writeCallback1_.writeStartingInvocations, Eq(0));\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  while (writeCallback1_.state == STATE_WAITING) {\n    socket_->getEventBase()->loopOnce();\n  }\n  ASSERT_EQ(writeCallback1_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 2);\n  EXPECT_EQ(sendMsgInvocations_, 2);\n}\n\n/**\n * Call write twice, with the first write incomplete; expect `writeStarting` to\n * be called twice\n */\nTEST_F(\n    AsyncSocketWriteCallbackTest,\n    WriteStartingTests_WriteTwiceIncompleteThenSuccess) {\n  const std::vector<uint8_t> wbuf(20, 'a');\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  WriteFlags flags = WriteFlags::NONE;\n\n  ASSERT_THAT(writeCallback1_.writeStartingInvocations, Eq(0));\n\n  // We split the first write in two parts. The first part is written\n  // immediately and a WriteRequest is created to write the bytes from the\n  // second part\n  splitNextWrite_ = true;\n  netOpsOnSendmsgPartial();\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  while (writeCallback1_.state == STATE_WAITING) {\n    socket_->getEventBase()->loopOnce();\n  }\n  ASSERT_EQ(writeCallback1_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n\n  // We do not split the second write\n  netOpsOnSendmsg();\n  socket_->writev(&writeCallback2_, &op, 1, flags);\n  while (writeCallback2_.state == STATE_WAITING) {\n    socket_->getEventBase()->loopOnce();\n  }\n  ASSERT_EQ(writeCallback1_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n  ASSERT_EQ(writeCallback2_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback2_.writeStartingInvocations, 1);\n  EXPECT_EQ(sendMsgInvocations_, 3);\n}\n\n/**\n * Call write twice, both times incomplete; expect `writeStarting` to be called\n * twice.\n */\nTEST_F(\n    AsyncSocketWriteCallbackTest,\n    WriteStartingTests_WriteTwiceIncompleteThenIncomplete) {\n  const std::vector<uint8_t> wbuf(20, 'a');\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  WriteFlags flags = WriteFlags::NONE;\n\n  ASSERT_THAT(writeCallback1_.writeStartingInvocations, Eq(0));\n\n  // We split the first write in two parts. The first part is written\n  // immediately and a WriteRequest is created to write the bytes from the\n  // second part\n  splitNextWrite_ = true;\n  netOpsOnSendmsgPartial();\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  socket_->getEventBase()->loopOnce();\n\n  EXPECT_EQ(sendMsgInvocations_, 1);\n  ASSERT_EQ(writeCallback1_.state, STATE_WAITING);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n\n  // We also split the second write. Since the WriteRequest queue is not empty,\n  // a new WriteRequest for all bytes in the second write is created when writev\n  // is called. The write will be split into two parts when we process this\n  // request. The first part is written when the request is processed and\n  // another WriteRequest will be created for the second part. This new\n  // WriteRequest will be processed on the next event loop iteration.\n  socket_->writev(&writeCallback2_, &op, 1, flags);\n  socket_->getEventBase()->loopOnce();\n\n  EXPECT_EQ(sendMsgInvocations_, 3);\n  ASSERT_EQ(writeCallback1_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n  ASSERT_EQ(writeCallback2_.state, STATE_WAITING);\n  EXPECT_EQ(writeCallback2_.writeStartingInvocations, 1);\n\n  socket_->getEventBase()->loopOnce();\n\n  ASSERT_EQ(writeCallback1_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n  ASSERT_EQ(writeCallback2_.state, STATE_SUCCEEDED);\n  EXPECT_EQ(writeCallback2_.writeStartingInvocations, 1);\n  EXPECT_EQ(sendMsgInvocations_, 4);\n}\n\n/**\n * Call write once and fail immediately; expect 'writeStarting` to not be\n * called.\n */\nTEST_F(\n    AsyncSocketWriteCallbackTest, WriteStartingTests_WriteOnceFailImmediately) {\n  const std::vector<uint8_t> wbuf(20, 'a');\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  WriteFlags flags = WriteFlags::NONE;\n\n  ASSERT_THAT(writeCallback1_.writeStartingInvocations, Eq(0));\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  socket_->shutdownWriteNow();\n  ASSERT_EQ(writeCallback1_.state, STATE_FAILED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 0);\n}\n\n/**\n * Call write once and fail after `sendMsg` was called; expect 'writeStarting`\n * to be called once.\n */\nTEST_F(AsyncSocketWriteCallbackTest, WriteStartingTests_WriteOnceFail) {\n  const std::vector<uint8_t> wbuf(20, 'a');\n  iovec op = {};\n  op.iov_base = const_cast<void*>(static_cast<const void*>(wbuf.data()));\n  op.iov_len = wbuf.size();\n  WriteFlags flags = WriteFlags::NONE;\n\n  netOpsOnSendmsgFail();\n  writeCallback1_.errorCallback = std::bind(&AsyncSocket::close, socket_.get());\n\n  ASSERT_THAT(writeCallback1_.writeStartingInvocations, Eq(0));\n  socket_->writev(&writeCallback1_, &op, 1, flags);\n  while (writeCallback1_.state == STATE_WAITING) {\n    socket_->getEventBase()->loopOnce();\n  }\n  ASSERT_EQ(writeCallback1_.state, STATE_FAILED);\n  EXPECT_EQ(writeCallback1_.writeStartingInvocations, 1);\n}\n\nTEST(AsyncSocketTest, BindAddressNoPort) {\n  EventBase eventBase;\n  TestServer server(true);\n\n  // When setBindAddressNoPort is disabled, verifies that a port is assigned\n  // before the connect call\n  auto socket = AsyncSocket::newSocket(&eventBase);\n  socket->setBindAddressNoPort(false);\n  SocketAddress bindAddr(\"127.0.0.1\", 0);\n  TestPortAssignmentCallback callback;\n  socket->connect(\n      &callback, server.getAddress(), 30, emptySocketOptionMap, bindAddr);\n  eventBase.loop();\n  EXPECT_NE(callback.assignedPort, 0);\n  socket->close();\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncSocketTest2.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <deque>\n#include <exception>\n#include <functional>\n#include <string>\n\n#include <folly/io/async/AsyncServerSocket.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/synchronization/RWSpinLock.h>\n\nnamespace folly {\nnamespace test {\n\n/**\n * Helper ConnectionEventCallback class for the test code.\n * It maintains counters protected by a spin lock.\n */\nclass TestConnectionEventCallback\n    : public AsyncServerSocket::ConnectionEventCallback {\n public:\n  void onConnectionAccepted(\n      const NetworkSocket /* socket */,\n      const SocketAddress& /* addr */) noexcept override {\n    std::unique_lock holder(spinLock_);\n    connectionAccepted_++;\n  }\n\n  void onConnectionAcceptError(const int /* err */) noexcept override {\n    std::unique_lock holder(spinLock_);\n    connectionAcceptedError_++;\n  }\n\n  void onConnectionDropped(\n      const NetworkSocket /* socket */,\n      const SocketAddress& /* addr */,\n      const std::string& /* errorMsg */) noexcept override {\n    std::unique_lock holder(spinLock_);\n    connectionDropped_++;\n  }\n\n  void onConnectionEnqueuedForAcceptorCallback(\n      const NetworkSocket /* socket */,\n      const SocketAddress& /* addr */) noexcept override {\n    std::unique_lock holder(spinLock_);\n    connectionEnqueuedForAcceptCallback_++;\n  }\n\n  void onConnectionDequeuedByAcceptorCallback(\n      const NetworkSocket /* socket */,\n      const SocketAddress& /* addr */) noexcept override {\n    std::unique_lock holder(spinLock_);\n    connectionDequeuedByAcceptCallback_++;\n  }\n\n  void onBackoffStarted() noexcept override {\n    std::unique_lock holder(spinLock_);\n    backoffStarted_++;\n  }\n\n  void onBackoffEnded() noexcept override {\n    std::unique_lock holder(spinLock_);\n    backoffEnded_++;\n  }\n\n  void onBackoffError() noexcept override {\n    std::unique_lock holder(spinLock_);\n    backoffError_++;\n  }\n\n  unsigned int getConnectionAccepted() const {\n    std::shared_lock holder(spinLock_);\n    return connectionAccepted_;\n  }\n\n  unsigned int getConnectionAcceptedError() const {\n    std::shared_lock holder(spinLock_);\n    return connectionAcceptedError_;\n  }\n\n  unsigned int getConnectionDropped() const {\n    std::shared_lock holder(spinLock_);\n    return connectionDropped_;\n  }\n\n  unsigned int getConnectionEnqueuedForAcceptCallback() const {\n    std::shared_lock holder(spinLock_);\n    return connectionEnqueuedForAcceptCallback_;\n  }\n\n  unsigned int getConnectionDequeuedByAcceptCallback() const {\n    std::shared_lock holder(spinLock_);\n    return connectionDequeuedByAcceptCallback_;\n  }\n\n  unsigned int getBackoffStarted() const {\n    std::shared_lock holder(spinLock_);\n    return backoffStarted_;\n  }\n\n  unsigned int getBackoffEnded() const {\n    std::shared_lock holder(spinLock_);\n    return backoffEnded_;\n  }\n\n  unsigned int getBackoffError() const {\n    std::shared_lock holder(spinLock_);\n    return backoffError_;\n  }\n\n private:\n  mutable folly::RWSpinLock spinLock_;\n  unsigned int connectionAccepted_{0};\n  unsigned int connectionAcceptedError_{0};\n  unsigned int connectionDropped_{0};\n  unsigned int connectionEnqueuedForAcceptCallback_{0};\n  unsigned int connectionDequeuedByAcceptCallback_{0};\n  unsigned int backoffStarted_{0};\n  unsigned int backoffEnded_{0};\n  unsigned int backoffError_{0};\n};\n\n/**\n * Helper AcceptCallback class for the test code\n * It records the callbacks that were invoked, and also supports calling\n * generic std::function objects in each callback.\n */\nclass TestAcceptCallback : public AsyncServerSocket::AcceptCallback {\n public:\n  enum EventType { TYPE_START, TYPE_ACCEPT, TYPE_ERROR, TYPE_STOP };\n  struct EventInfo {\n    EventInfo(folly::NetworkSocket fd_, const folly::SocketAddress& addr)\n        : type(TYPE_ACCEPT), fd(fd_), address(addr), errorMsg() {}\n    explicit EventInfo(const std::string& msg)\n        : type(TYPE_ERROR), fd(), address(), errorMsg(msg) {}\n    explicit EventInfo(EventType et) : type(et), fd(), address(), errorMsg() {}\n\n    EventType type;\n    folly::NetworkSocket fd; // valid for TYPE_ACCEPT\n    folly::SocketAddress address; // valid for TYPE_ACCEPT\n    std::string errorMsg; // valid for TYPE_ERROR\n  };\n  using EventList = std::deque<EventInfo>;\n\n  TestAcceptCallback()\n      : connectionAcceptedFn_(),\n        acceptErrorFn_(),\n        acceptStoppedFn_(),\n        events_() {}\n\n  std::deque<EventInfo>* getEvents() { return &events_; }\n\n  void setConnectionAcceptedFn(\n      const std::function<void(NetworkSocket, const folly::SocketAddress&)>&\n          fn) {\n    connectionAcceptedFn_ = fn;\n  }\n  void setAcceptErrorFn(const std::function<void(const std::exception&)>& fn) {\n    acceptErrorFn_ = fn;\n  }\n  void setAcceptStartedFn(const std::function<void()>& fn) {\n    acceptStartedFn_ = fn;\n  }\n  void setAcceptStoppedFn(const std::function<void()>& fn) {\n    acceptStoppedFn_ = fn;\n  }\n\n  void connectionAccepted(\n      NetworkSocket fd,\n      const folly::SocketAddress& clientAddr,\n      AcceptInfo /* info */) noexcept override {\n    events_.emplace_back(fd, clientAddr);\n\n    if (connectionAcceptedFn_) {\n      connectionAcceptedFn_(fd, clientAddr);\n    }\n  }\n  void acceptError(folly::exception_wrapper ex) noexcept override {\n    events_.emplace_back(ex.what().toStdString());\n\n    if (acceptErrorFn_) {\n      acceptErrorFn_(*ex.get_exception());\n    }\n  }\n  void acceptStarted() noexcept override {\n    events_.emplace_back(TYPE_START);\n\n    if (acceptStartedFn_) {\n      acceptStartedFn_();\n    }\n  }\n  void acceptStopped() noexcept override {\n    events_.emplace_back(TYPE_STOP);\n\n    if (acceptStoppedFn_) {\n      acceptStoppedFn_();\n    }\n  }\n\n private:\n  std::function<void(NetworkSocket, const folly::SocketAddress&)>\n      connectionAcceptedFn_;\n  std::function<void(const std::exception&)> acceptErrorFn_;\n  std::function<void()> acceptStartedFn_;\n  std::function<void()> acceptStoppedFn_;\n\n  std::deque<EventInfo> events_;\n};\n\nclass TestConnectCallback : public AsyncSocket::ConnectCallback {\n public:\n  void preConnect(NetworkSocket fd) override {\n    int one = 1;\n    netops::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));\n  }\n  void connectSuccess() noexcept override {}\n  void connectErr(const AsyncSocketException& /*ex*/) noexcept override {}\n};\n\nclass TestPortAssignmentCallback : public AsyncSocket::ConnectCallback {\n public:\n  void preConnect(NetworkSocket fd) noexcept override {\n    // Get the local address after binding but before connecting\n    SocketAddress localAddr;\n    sockaddr_storage storage{};\n    socklen_t addrLen = sizeof(storage);\n    if (netops::getsockname(\n            fd, reinterpret_cast<sockaddr*>(&storage), &addrLen) == 0) {\n      localAddr.setFromSockaddr(reinterpret_cast<sockaddr*>(&storage), addrLen);\n      assignedPort = localAddr.getPort();\n    }\n  }\n  void connectSuccess() noexcept override {}\n  void connectErr(const AsyncSocketException& /*ex*/) noexcept override {}\n  uint16_t assignedPort = 0;\n};\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncTimeoutTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncTimeout.h>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nTEST(AsyncTimeout, destroy) {\n  std::optional<EventBase> manager{std::in_place};\n  auto observer = AsyncTimeout::make(manager.value(), []() noexcept {});\n  observer->scheduleTimeout(std::chrono::milliseconds(100));\n  EXPECT_EQ(observer->isScheduled(), true);\n  manager.reset();\n  EXPECT_EQ(observer->isScheduled(), false);\n}\n\nTEST(AsyncTimeout, make) {\n  int value = 0;\n  int expected = 10;\n  EventBase manager;\n\n  auto observer = AsyncTimeout::make(manager, [&value, expected]() noexcept {\n    value = expected;\n  });\n\n  observer->scheduleTimeout(std::chrono::milliseconds(100));\n\n  manager.loop();\n\n  EXPECT_EQ(expected, value);\n}\n\nTEST(AsyncTimeout, schedule) {\n  int value = 0;\n  int expected = 10;\n  EventBase manager;\n\n  auto observer = AsyncTimeout::schedule(\n      std::chrono::milliseconds(100), manager, [&value, expected]() noexcept {\n        value = expected;\n      });\n\n  manager.loop();\n\n  EXPECT_EQ(expected, value);\n}\n\nTEST(AsyncTimeout, scheduleImmediate) {\n  int value = 0;\n  int expected = 10;\n  EventBase manager;\n\n  auto observer = AsyncTimeout::schedule(\n      std::chrono::milliseconds(0), manager, [&value, expected]() noexcept {\n        value = expected;\n      });\n\n  manager.loop();\n  EXPECT_EQ(expected, value);\n}\n\nTEST(AsyncTimeout, cancelMake) {\n  int value = 0;\n  int expected = 10;\n  EventBase manager;\n\n  auto observer = AsyncTimeout::make(manager, [&value, expected]() noexcept {\n    value = expected;\n  });\n\n  std::weak_ptr<RequestContext> rctx_weak_ptr;\n\n  {\n    RequestContextScopeGuard rctx_guard;\n    rctx_weak_ptr = RequestContext::saveContext();\n    observer->scheduleTimeout(std::chrono::milliseconds(100));\n    observer->cancelTimeout();\n  }\n\n  // Ensure that RequestContext created for the scope has been released and\n  // deleted.\n  EXPECT_EQ(rctx_weak_ptr.expired(), true);\n\n  manager.loop();\n\n  EXPECT_NE(expected, value);\n}\n\nTEST(AsyncTimeout, cancelSchedule) {\n  int value = 0;\n  int expected = 10;\n  EventBase manager;\n  std::unique_ptr<AsyncTimeout> observer;\n  std::weak_ptr<RequestContext> rctx_weak_ptr;\n\n  {\n    RequestContextScopeGuard rctx_guard;\n    rctx_weak_ptr = RequestContext::saveContext();\n\n    observer = AsyncTimeout::schedule(\n        std::chrono::milliseconds(100), manager, [&value, expected]() noexcept {\n          value = expected;\n        });\n\n    observer->cancelTimeout();\n  }\n\n  // Ensure that RequestContext created for the scope has been released and\n  // deleted.\n  EXPECT_EQ(rctx_weak_ptr.expired(), true);\n\n  manager.loop();\n\n  EXPECT_NE(expected, value);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncTransportTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/AsyncTransport.h>\n\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/test/MockAsyncTransport.h>\n#include <folly/portability/GTest.h>\n\nusing namespace testing;\n\nnamespace folly {\n\nTEST(AsyncTransportTest, getSocketFromSocket) {\n  AsyncSocket::UniquePtr transport(new AsyncSocket());\n  auto sock = transport->getUnderlyingTransport<AsyncSocket>();\n  ASSERT_EQ(transport.get(), sock);\n}\n\nTEST(AsyncTransportTest, getSocketFromWrappedTransport) {\n  AsyncSocket::UniquePtr transport(new AsyncSocket());\n  auto transportAddr = transport.get();\n\n  test::MockAsyncTransport wrapped1;\n  test::MockAsyncTransport wrapped2;\n\n  EXPECT_CALL(wrapped2, getWrappedTransport()).WillOnce(Return(&wrapped1));\n  EXPECT_CALL(wrapped1, getWrappedTransport()).WillOnce(Return(transportAddr));\n\n  auto sock = wrapped2.getUnderlyingTransport<AsyncSocket>();\n  ASSERT_EQ(transportAddr, sock);\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/AsyncUDPSocketGSOGROTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <numeric>\n#include <thread>\n\n#include <folly/Conv.h>\n#include <folly/SocketAddress.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/AsyncUDPServerSocket.h>\n#include <folly/io/async/AsyncUDPSocket.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nusing folly::AsyncTimeout;\nusing folly::AsyncUDPServerSocket;\nusing folly::AsyncUDPSocket;\nusing folly::EventBase;\nusing folly::IOBuf;\nusing namespace std::chrono_literals;\nusing namespace testing;\n\nstruct TestData {\n  TestData(\n      int gso,\n      bool useSocketGSO,\n      int* in,\n      size_t inLen,\n      const int* expected,\n      size_t expectedLen)\n      : gso_(gso), useSocketGSO_(useSocketGSO) {\n    std::vector<int> inVec;\n    inVec.assign(in, in + inLen);\n    in_.emplace_back(std::move(inVec));\n    expected_.assign(expected, expected + expectedLen);\n\n    expectedSize_ = std::accumulate(expected_.begin(), expected_.end(), 0);\n  }\n\n  TestData(\n      const std::vector<folly::AsyncUDPSocket::WriteOptions>& optionsVec,\n      bool useSocketGSO,\n      const std::vector<std::vector<int>>& in,\n      const int* expected,\n      size_t expectedLen)\n      : optionsVec_(optionsVec), useSocketGSO_(useSocketGSO), in_(in) {\n    expected_.assign(expected, expected + expectedLen);\n\n    expectedSize_ = std::accumulate(expected_.begin(), expected_.end(), 0);\n  }\n\n  bool checkIn() const {\n    int expected = 0;\n    for (const auto& in : in_) {\n      expected += std::accumulate(in.begin(), in.end(), 0);\n    }\n    return (expectedSize_ == expected);\n  }\n\n  bool checkOut() const {\n    auto size = std::accumulate(out_.begin(), out_.end(), 0);\n    auto ret = (expectedSize_ == size);\n    if (!ret) {\n      LOG(ERROR) << \"expected = \" << expectedSize_ << \" actual = \" << size;\n      for (const auto& out : out_) {\n        LOG(ERROR) << out;\n      }\n    }\n\n    return ret;\n  }\n\n  bool appendOut(int num) {\n    out_.push_back(num);\n    outSize_ += num;\n\n    return (outSize_ >= expectedSize_);\n  }\n\n  bool isMulti() const { return (in_.size() > 1); }\n\n  const folly::AsyncUDPSocket::WriteOptions* getOptionsVec() const {\n    return (!optionsVec_.empty()) ? optionsVec_.data() : nullptr;\n  }\n\n  std::unique_ptr<folly::IOBuf> getInBuf() {\n    if (!in_.size()) {\n      return nullptr;\n    }\n\n    auto& in = in_[0];\n\n    std::string str(in[0], 'A');\n    std::unique_ptr<folly::IOBuf> ret =\n        folly::IOBuf::copyBuffer(str.data(), str.size());\n\n    for (size_t i = 1; i < in.size(); i++) {\n      str = std::string(in[i], 'A');\n      ret->prependChain(folly::IOBuf::copyBuffer(str.data(), str.size()));\n    }\n\n    return ret;\n  }\n\n  std::vector<std::unique_ptr<folly::IOBuf>> getInBufs() {\n    if (!in_.size()) {\n      return std::vector<std::unique_ptr<folly::IOBuf>>();\n    }\n\n    std::vector<std::unique_ptr<folly::IOBuf>> ret;\n    ret.reserve(in_.size());\n\n    for (const auto& in : in_) {\n      std::string str(in[0], 'A');\n      std::unique_ptr<folly::IOBuf> buf =\n          folly::IOBuf::copyBuffer(str.data(), str.size());\n\n      for (size_t i = 1; i < in.size(); i++) {\n        str = std::string(in[i], 'A');\n        buf->prependChain(folly::IOBuf::copyBuffer(str.data(), str.size()));\n      }\n\n      ret.emplace_back(std::move(buf));\n    }\n\n    return ret;\n  }\n\n  int gso_{0};\n  std::vector<folly::AsyncUDPSocket::WriteOptions> optionsVec_;\n  bool useSocketGSO_{false};\n  std::vector<std::vector<int>> in_;\n  std::vector<int> expected_; // expected\n  int expectedSize_;\n  std::vector<int> out_;\n  int outSize_{0};\n};\n\nclass UDPAcceptor : public AsyncUDPServerSocket::Callback {\n public:\n  UDPAcceptor(EventBase* /* evb */) {}\n\n  void onListenStarted() noexcept override {}\n\n  void onListenStopped() noexcept override {}\n\n  void onDataAvailable(\n      std::shared_ptr<folly::AsyncUDPSocket> socket,\n      const folly::SocketAddress& client,\n      std::unique_ptr<folly::IOBuf> data,\n      bool /*unused*/,\n      OnDataAvailableParams params) noexcept override {\n    // send pong(s)\n    if (params.gro == -1) {\n      socket->write(client, data->clone());\n    } else {\n      int64_t total = data->length();\n      size_t offset = 0;\n      while (total > 0) {\n        auto size = (total > params.gro) ? params.gro : total;\n        auto sendData = IOBuf::copyBuffer(data->data() + offset, size);\n        offset += size;\n        total -= size;\n        socket->write(client, sendData);\n      }\n    }\n  }\n};\n\nclass UDPServer {\n public:\n  UDPServer(EventBase* evb, folly::SocketAddress addr, int n)\n      : evb_(evb), addr_(addr), evbs_(n) {}\n\n  void start() {\n    CHECK(evb_->isInEventBaseThread());\n\n    socket_ = std::make_unique<AsyncUDPServerSocket>(evb_, 64 * 1024);\n\n    try {\n      socket_->bind(addr_);\n      VLOG(4) << \"Server listening on \" << socket_->address().describe();\n    } catch (const std::exception& ex) {\n      LOG(FATAL) << ex.what();\n    }\n\n    auto s = socket_->getSocket();\n    s->setGRO(true);\n\n    acceptors_.reserve(evbs_.size());\n    threads_.reserve(evbs_.size());\n\n    // Add numWorkers thread\n    int i = 0;\n    for (auto& evb : evbs_) {\n      acceptors_.emplace_back(&evb);\n\n      std::thread t([&]() { evb.loopForever(); });\n\n      evb.waitUntilRunning();\n\n      socket_->addListener(&evb, &acceptors_[i]);\n      threads_.emplace_back(std::move(t));\n      ++i;\n    }\n\n    socket_->listen();\n  }\n\n  folly::SocketAddress address() const { return socket_->address(); }\n\n  void shutdown() {\n    CHECK(evb_->isInEventBaseThread());\n    socket_->close();\n    socket_.reset();\n\n    for (auto& evb : evbs_) {\n      evb.terminateLoopSoon();\n    }\n\n    for (auto& t : threads_) {\n      t.join();\n    }\n  }\n\n  void pauseAccepting() { socket_->pauseAccepting(); }\n\n  void resumeAccepting() { socket_->resumeAccepting(); }\n\n private:\n  EventBase* const evb_{nullptr};\n  const folly::SocketAddress addr_;\n\n  std::unique_ptr<AsyncUDPServerSocket> socket_;\n  std::vector<std::thread> threads_;\n  std::vector<folly::EventBase> evbs_;\n  std::vector<UDPAcceptor> acceptors_;\n};\n\nclass UDPClient : private AsyncUDPSocket::ReadCallback, private AsyncTimeout {\n public:\n  explicit UDPClient(EventBase* evb, TestData& testData)\n      : AsyncTimeout(evb), evb_(evb), testData_(testData) {}\n\n  void start(const folly::SocketAddress& server) {\n    CHECK(evb_->isInEventBaseThread());\n    server_ = server;\n    socket_ = std::make_unique<AsyncUDPSocket>(evb_);\n\n    try {\n      socket_->bind(folly::SocketAddress(\"127.0.0.1\", 0));\n      if (connectAddr_) {\n        connect();\n      }\n      VLOG(2) << \"Client bound to \" << socket_->address().describe();\n    } catch (const std::exception& ex) {\n      LOG(FATAL) << ex.what();\n    }\n\n    // succeed if GSO not available\n    if (socket_->getGSO() < 0) {\n      LOG(INFO) << \"GSO  not supported\";\n      testData_.out_ = testData_.expected_;\n      shutdown();\n      return;\n    }\n\n    if (testData_.useSocketGSO_) {\n      socket_->setGSO(testData_.gso_);\n    } else {\n      socket_->setGSO(0);\n    }\n\n    socket_->resumeRead(this);\n\n    // Start playing ping pong\n    sendPing();\n  }\n\n  void connect() {\n    socket_->connect(*connectAddr_);\n    VLOG(2) << \"Client connected to address=\" << *connectAddr_;\n  }\n\n  void shutdown() {\n    CHECK(evb_->isInEventBaseThread());\n    socket_->pauseRead();\n    socket_->close();\n    socket_.reset();\n    evb_->terminateLoopSoon();\n  }\n\n  void sendPing() {\n    // this should ensure the test finishes\n    // even if the server does not reply\n    scheduleTimeout(5s);\n    if (testData_.isMulti()) {\n      writePing(testData_.getInBufs(), testData_.getOptionsVec());\n    } else {\n      writePing(\n          testData_.getInBuf(), testData_.useSocketGSO_ ? -1 : testData_.gso_);\n    }\n  }\n\n  virtual void writePing(std::unique_ptr<folly::IOBuf> buf, int gso) {\n    socket_->writeGSO(\n        server_,\n        std::move(buf),\n        folly::AsyncUDPSocket::WriteOptions(\n            gso /*gsoVal*/, false /* zerocopyVal*/));\n  }\n\n  virtual void writePing(\n      const std::vector<std::unique_ptr<folly::IOBuf>>& vec,\n      const folly::AsyncUDPSocket::WriteOptions* options) {\n    socket_->writemGSO(\n        folly::range(&server_, &server_ + 1), vec.data(), vec.size(), options);\n  }\n\n  void getReadBuffer(void** buf, size_t* len) noexcept override {\n    *buf = buf_;\n    *len = sizeof(buf_);\n  }\n\n  void onDataAvailable(\n      const folly::SocketAddress& /*unused*/,\n      size_t len,\n      bool /*unused*/,\n      OnDataAvailableParams params) noexcept override {\n    // no GRO on the client side\n    CHECK_EQ(params.gro, -1);\n    VLOG(0) << \"Got \" << len << \" bytes\";\n    if (testData_.appendOut(len)) {\n      shutdown();\n    }\n  }\n\n  void onReadError(const folly::AsyncSocketException& ex) noexcept override {\n    VLOG(4) << ex.what();\n\n    // Start listening for next PONG\n    socket_->resumeRead(this);\n  }\n\n  void onReadClosed() noexcept override {\n    CHECK(false) << \"We unregister reads before closing\";\n  }\n\n  void timeoutExpired() noexcept override {\n    VLOG(4) << \"Timeout expired\";\n    shutdown();\n  }\n\n  AsyncUDPSocket& getSocket() { return *socket_; }\n\n  void setShouldConnect(const folly::SocketAddress& connectAddr) {\n    connectAddr_ = connectAddr;\n  }\n\n protected:\n  folly::Optional<folly::SocketAddress> connectAddr_;\n  EventBase* const evb_{nullptr};\n\n  folly::SocketAddress server_;\n  std::unique_ptr<AsyncUDPSocket> socket_;\n\n private:\n  char buf_[2048];\n  TestData& testData_;\n};\n\nclass AsyncSocketGSOIntegrationTest : public Test {\n public:\n  void SetUp() override {\n    server = std::make_unique<UDPServer>(\n        &sevb, folly::SocketAddress(\"127.0.0.1\", 0), 1);\n\n    // Start event loop in a separate thread\n    serverThread = std::make_unique<std::thread>([this]() {\n      sevb.loopForever();\n    });\n\n    // Wait for event loop to start\n    sevb.waitUntilRunning();\n  }\n\n  void startServer() {\n    // Start the server\n    sevb.runInEventBaseThreadAndWait([&]() { server->start(); });\n    LOG(INFO) << \"Server listening=\" << server->address();\n  }\n\n  void TearDown() override {\n    // Shutdown server\n    sevb.runInEventBaseThread([&]() {\n      server->shutdown();\n      sevb.terminateLoopSoon();\n    });\n\n    // Wait for server thread to join\n    serverThread->join();\n  }\n\n  std::unique_ptr<UDPClient> performPingPongTest(\n      TestData& testData,\n      folly::Optional<folly::SocketAddress> connectedAddress);\n\n  folly::EventBase sevb;\n  folly::EventBase cevb;\n  TestData* testData_{nullptr};\n  std::unique_ptr<std::thread> serverThread;\n  std::unique_ptr<UDPServer> server;\n  std::unique_ptr<UDPClient> client;\n};\n\nstd::unique_ptr<UDPClient> AsyncSocketGSOIntegrationTest::performPingPongTest(\n    TestData& testData,\n    folly::Optional<folly::SocketAddress> connectedAddress) {\n  testData_ = &testData;\n\n  client = std::make_unique<UDPClient>(&cevb, testData);\n  if (connectedAddress) {\n    client->setShouldConnect(*connectedAddress);\n  }\n\n  // Start event loop in a separate thread\n  auto clientThread = std::thread([this]() { cevb.loopForever(); });\n\n  // Wait for event loop to start\n  cevb.waitUntilRunning();\n\n  // Send ping\n  cevb.runInEventBaseThread([&]() { client->start(server->address()); });\n\n  // Wait for client to finish\n  clientThread.join();\n  return std::move(client);\n}\n\nTEST_F(AsyncSocketGSOIntegrationTest, PingPongGlobalGSO) {\n  int gso = 1000;\n  int in[] = {100, 1200, 3000, 200, 100, 300};\n  int expected[] = {1000, 1000, 1000, 1000, 900};\n  TestData testData(\n      gso,\n      true /*useSocketGSO*/,\n      in,\n      sizeof(in) / sizeof(in[0]),\n      expected,\n      sizeof(expected) / sizeof(expected[0]));\n  ASSERT_TRUE(testData.checkIn());\n  startServer();\n  auto pingClient = performPingPongTest(testData, folly::none);\n  ASSERT_TRUE(testData.checkOut());\n}\n\nTEST_F(AsyncSocketGSOIntegrationTest, PingPongRequestGSO) {\n  int gso = 421;\n  int in[] = {100, 1200, 3000, 200, 100, 300};\n  int expected[] = {421, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, 269};\n  TestData testData(\n      gso,\n      false /*useSocketGSO*/,\n      in,\n      sizeof(in) / sizeof(in[0]),\n      expected,\n      sizeof(expected) / sizeof(expected[0]));\n  ASSERT_TRUE(testData.checkIn());\n  startServer();\n  auto pingClient = performPingPongTest(testData, folly::none);\n  ASSERT_TRUE(testData.checkOut());\n}\n\nTEST_F(AsyncSocketGSOIntegrationTest, MultiPingPongGlobalGSO) {\n  std::vector<folly::AsyncUDPSocket::WriteOptions> optionsVec = {\n      {1000, false}, {800, false}, {1100, false}, {1200, false}};\n  std::vector<std::vector<int>> inVec;\n  inVec.reserve(optionsVec.size());\n  std::vector<int> in = {100, 1200, 3000, 200, 100, 300};\n  int total = std::accumulate(in.begin(), in.end(), 0);\n  std::vector<int> expected;\n  for (size_t i = 0; i < optionsVec.size(); i++) {\n    inVec.push_back(in);\n\n    auto remaining = total;\n    while (remaining) {\n      if (remaining > optionsVec[i].gso) {\n        expected.push_back(optionsVec[i].gso);\n        remaining -= optionsVec[i].gso;\n      } else {\n        expected.push_back(remaining);\n        remaining = 0;\n      }\n    }\n  }\n\n  TestData testData(\n      optionsVec,\n      true /*useSocketGSO*/,\n      inVec,\n      expected.data(),\n      expected.size());\n  ASSERT_TRUE(testData.checkIn());\n  startServer();\n  auto pingClient = performPingPongTest(testData, folly::none);\n  ASSERT_TRUE(testData.checkOut());\n}\n\nTEST_F(AsyncSocketGSOIntegrationTest, MultiPingPongRequestGSO) {\n  std::vector<folly::AsyncUDPSocket::WriteOptions> optionsVec = {\n      {421, false}, {300, false}, {528, false}, {680, false}};\n  std::vector<std::vector<int>> inVec;\n  inVec.reserve(optionsVec.size());\n\n  std::vector<int> in = {100, 1200, 3000, 200, 100, 300};\n  int total = std::accumulate(in.begin(), in.end(), 0);\n  std::vector<int> expected;\n  for (size_t i = 0; i < optionsVec.size(); i++) {\n    inVec.push_back(in);\n\n    auto remaining = total;\n    while (remaining) {\n      if (remaining > optionsVec[i].gso) {\n        expected.push_back(optionsVec[i].gso);\n        remaining -= optionsVec[i].gso;\n      } else {\n        expected.push_back(remaining);\n        remaining = 0;\n      }\n    }\n  }\n\n  TestData testData(\n      optionsVec,\n      false /*useSocketGSO*/,\n      inVec,\n      expected.data(),\n      expected.size());\n  ASSERT_TRUE(testData.checkIn());\n  startServer();\n  auto pingClient = performPingPongTest(testData, folly::none);\n  ASSERT_TRUE(testData.checkOut());\n}\n\n// buffer sizes\nconstexpr auto kGSO1 = 100;\nconstexpr auto kGSO2 = 200;\nconstexpr auto kGSO = kGSO1 + kGSO2;\n\nclass GSOBuf {\n public:\n  explicit GSOBuf(size_t size1, size_t size2 = 0) {\n    std::string str(size1, 'A');\n    ioBuf_ = folly::IOBuf::copyBuffer(str.data(), str.size());\n\n    if (size2) {\n      str = std::string(size2, 'B');\n      auto tmp = folly::IOBuf::copyBuffer(str.data(), str.size());\n      ioBuf_->prependChain(std::move(tmp));\n    }\n  }\n\n  const std::unique_ptr<IOBuf>& get() const { return ioBuf_; }\n\n private:\n  std::unique_ptr<IOBuf> ioBuf_;\n};\n\nclass GSOSendTest {\n public:\n  explicit GSOSendTest(\n      folly::AsyncUDPSocket& socket,\n      const folly::SocketAddress& address,\n      int gso,\n      size_t size1,\n      size_t size2 = 0) {\n    GSOBuf buf(size1, size2);\n\n    ret_ = socket.writeGSO(\n        address,\n        buf.get(),\n        folly::AsyncUDPSocket::WriteOptions(\n            gso /*gsoVal*/, false /* zerocopyVal*/));\n  }\n\n  ssize_t get() const { return ret_; }\n\n private:\n  ssize_t ret_;\n};\n\nTEST(AsyncSocketGSOTest, send) {\n  EventBase evb;\n  folly::AsyncUDPSocket client(&evb);\n  client.bind(folly::SocketAddress(\"127.0.0.1\", 0));\n  if (client.getGSO() < 0) {\n    LOG(INFO) << \"GSO not supported\";\n    // GSO not supported\n    return;\n  }\n\n  folly::AsyncUDPSocket server(&evb);\n  server.bind(folly::SocketAddress(\"127.0.0.1\", 0));\n\n  // send more than GSO in a single IOBuf\n  {\n    GSOSendTest test(client, server.address(), kGSO, kGSO + 1);\n    CHECK_EQ(test.get(), kGSO + 1);\n  }\n\n  // send more than GSO in a multiple IOBufs\n  {\n    GSOSendTest test(client, server.address(), kGSO, kGSO1 + 1, kGSO2 + 1);\n    CHECK_EQ(test.get(), kGSO + 2);\n  }\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncUDPSocketSendmmsgTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <numeric>\n#include <thread>\n\n#include <folly/Conv.h>\n#include <folly/SocketAddress.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/AsyncUDPServerSocket.h>\n#include <folly/io/async/AsyncUDPSocket.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n\nusing folly::AsyncTimeout;\nusing folly::AsyncUDPServerSocket;\nusing folly::AsyncUDPSocket;\nusing folly::EventBase;\nusing namespace testing;\n\nusing SizeVec = std::vector<size_t>;\nusing IOBufVec = std::vector<std::unique_ptr<folly::IOBuf>>;\n\nstruct TestData {\n  explicit TestData(const SizeVec& in) : in_(in) {}\n\n  bool checkOut() const { return (outNum_ == in_.size()); }\n\n  char getCharAt(size_t pos) {\n    if (pos < in_.size()) {\n      return static_cast<char>(in_[pos] % 256);\n    }\n\n    return 0;\n  }\n\n  bool appendOut(const char* data, size_t len) {\n    outNum_++;\n    if (outNum_ == in_.size()) {\n      return true;\n    }\n    // check the size\n    CHECK_EQ(len, in_[outNum_ - 1]);\n\n    // check the payload\n    char c = getCharAt(outNum_ - 1);\n    for (size_t i = 0; i < len; i++) {\n      CHECK_EQ(data[i], c);\n    }\n\n    return false;\n  }\n\n  IOBufVec getInBufs() {\n    if (!in_.size()) {\n      return IOBufVec();\n    }\n\n    IOBufVec ret;\n    for (size_t i = 0; i < in_.size(); i++) {\n      std::string str(in_[i], getCharAt(i));\n      std::unique_ptr<folly::IOBuf> buf =\n          folly::IOBuf::copyBuffer(str.data(), str.size());\n\n      ret.emplace_back(std::move(buf));\n    }\n\n    return ret;\n  }\n\n  SizeVec in_;\n  size_t outNum_{0};\n  bool check_{true};\n};\n\nclass UDPAcceptor : public AsyncUDPServerSocket::Callback {\n public:\n  UDPAcceptor(EventBase* /* evb */) {}\n\n  void onListenStarted() noexcept override {}\n\n  void onListenStopped() noexcept override {}\n\n  void onDataAvailable(\n      std::shared_ptr<folly::AsyncUDPSocket> socket,\n      const folly::SocketAddress& client,\n      std::unique_ptr<folly::IOBuf> data,\n      bool /*unused*/,\n      OnDataAvailableParams /*unused*/) noexcept override {\n    // send pong\n    socket->write(client, data->clone());\n  }\n};\n\nclass UDPServer {\n public:\n  UDPServer(EventBase* evb, folly::SocketAddress addr, int n)\n      : evb_(evb), addr_(addr), evbs_(n) {}\n\n  void start() {\n    CHECK(evb_->isInEventBaseThread());\n\n    socket_ = std::make_unique<AsyncUDPServerSocket>(evb_, 1500);\n\n    try {\n      socket_->bind(addr_);\n      VLOG(4) << \"Server listening on \" << socket_->address().describe();\n    } catch (const std::exception& ex) {\n      LOG(FATAL) << ex.what();\n    }\n\n    acceptors_.reserve(evbs_.size());\n    threads_.reserve(evbs_.size());\n\n    // Add numWorkers thread\n    int i = 0;\n    for (auto& evb : evbs_) {\n      acceptors_.emplace_back(&evb);\n\n      std::thread t([&]() { evb.loopForever(); });\n\n      evb.waitUntilRunning();\n\n      socket_->addListener(&evb, &acceptors_[i]);\n      threads_.emplace_back(std::move(t));\n      ++i;\n    }\n\n    socket_->listen();\n  }\n\n  folly::SocketAddress address() const { return socket_->address(); }\n\n  void shutdown() {\n    CHECK(evb_->isInEventBaseThread());\n    socket_->close();\n    socket_.reset();\n\n    for (auto& evb : evbs_) {\n      evb.terminateLoopSoon();\n    }\n\n    for (auto& t : threads_) {\n      t.join();\n    }\n  }\n\n  void pauseAccepting() { socket_->pauseAccepting(); }\n\n  void resumeAccepting() { socket_->resumeAccepting(); }\n\n private:\n  EventBase* const evb_{nullptr};\n  const folly::SocketAddress addr_;\n\n  std::unique_ptr<AsyncUDPServerSocket> socket_;\n  std::vector<std::thread> threads_;\n  std::vector<folly::EventBase> evbs_;\n  std::vector<UDPAcceptor> acceptors_;\n};\n\nclass UDPClient : private AsyncUDPSocket::ReadCallback, private AsyncTimeout {\n public:\n  explicit UDPClient(EventBase* evb, TestData& testData)\n      : AsyncTimeout(evb), evb_(evb), testData_(testData) {}\n\n  void start(const folly::SocketAddress& server) {\n    CHECK(evb_->isInEventBaseThread());\n    server_ = server;\n    socket_ = std::make_unique<AsyncUDPSocket>(evb_);\n\n    try {\n      socket_->bind(folly::SocketAddress(\"127.0.0.1\", 0));\n      if (connectAddr_) {\n        connect();\n      }\n      VLOG(2) << \"Client bound to \" << socket_->address().describe();\n    } catch (const std::exception& ex) {\n      LOG(FATAL) << ex.what();\n    }\n\n    socket_->resumeRead(this);\n\n    // Start playing ping pong\n    sendPing();\n  }\n\n  void connect() {\n    socket_->connect(*connectAddr_);\n    VLOG(2) << \"Client connected to address=\" << *connectAddr_;\n  }\n\n  void shutdown() {\n    CHECK(evb_->isInEventBaseThread());\n    socket_->pauseRead();\n    socket_->close();\n    socket_.reset();\n    evb_->terminateLoopSoon();\n  }\n\n  void sendPing() {\n    scheduleTimeout(50);\n    auto bufs = testData_.getInBufs();\n    writePing(bufs);\n  }\n\n  virtual void writePing(IOBufVec& bufs) {\n    socket_->writem(\n        folly::range(&server_, &server_ + 1), bufs.data(), bufs.size());\n  }\n\n  void getReadBuffer(void** buf, size_t* len) noexcept override {\n    *buf = buf_;\n    *len = sizeof(buf_);\n  }\n\n  void onDataAvailable(\n      const folly::SocketAddress& /*unused*/,\n      size_t len,\n      bool /*unused*/,\n      OnDataAvailableParams /*unused*/) noexcept override {\n    VLOG(0) << \"Got \" << len << \" bytes\";\n    if (testData_.appendOut(buf_, len)) {\n      shutdown();\n    }\n  }\n\n  void onReadError(const folly::AsyncSocketException& ex) noexcept override {\n    VLOG(4) << ex.what();\n\n    // Start listening for next PONG\n    socket_->resumeRead(this);\n  }\n\n  void onReadClosed() noexcept override {\n    CHECK(false) << \"We unregister reads before closing\";\n  }\n\n  void timeoutExpired() noexcept override {\n    VLOG(4) << \"Timeout expired\";\n    shutdown();\n  }\n\n  AsyncUDPSocket& getSocket() { return *socket_; }\n\n  void setShouldConnect(const folly::SocketAddress& connectAddr) {\n    connectAddr_ = connectAddr;\n  }\n\n protected:\n  folly::Optional<folly::SocketAddress> connectAddr_;\n  EventBase* const evb_{nullptr};\n\n  folly::SocketAddress server_;\n  std::unique_ptr<AsyncUDPSocket> socket_;\n\n private:\n  char buf_[2048];\n  TestData& testData_;\n};\n\nclass AsyncSocketSendmmsgIntegrationTest : public Test {\n public:\n  void SetUp() override {\n    server = std::make_unique<UDPServer>(\n        &sevb, folly::SocketAddress(\"127.0.0.1\", 0), 1);\n\n    // Start event loop in a separate thread\n    serverThread = std::make_unique<std::thread>([this]() {\n      sevb.loopForever();\n    });\n\n    // Wait for event loop to start\n    sevb.waitUntilRunning();\n  }\n\n  void startServer() {\n    // Start the server\n    sevb.runInEventBaseThreadAndWait([&]() { server->start(); });\n    LOG(INFO) << \"Server listening=\" << server->address();\n  }\n\n  void TearDown() override {\n    // Shutdown server\n    sevb.runInEventBaseThread([&]() {\n      server->shutdown();\n      sevb.terminateLoopSoon();\n    });\n\n    // Wait for server thread to join\n    serverThread->join();\n  }\n\n  std::unique_ptr<UDPClient> performPingPongTest(\n      TestData& testData,\n      folly::Optional<folly::SocketAddress> connectedAddress);\n\n  folly::EventBase sevb;\n  folly::EventBase cevb;\n  TestData* testData_{nullptr};\n  std::unique_ptr<std::thread> serverThread;\n  std::unique_ptr<UDPServer> server;\n  std::unique_ptr<UDPClient> client;\n};\n\nstd::unique_ptr<UDPClient>\nAsyncSocketSendmmsgIntegrationTest::performPingPongTest(\n    TestData& testData,\n    folly::Optional<folly::SocketAddress> connectedAddress) {\n  testData_ = &testData;\n\n  client = std::make_unique<UDPClient>(&cevb, testData);\n  if (connectedAddress) {\n    client->setShouldConnect(*connectedAddress);\n  }\n\n  // Start event loop in a separate thread\n  auto clientThread = std::thread([this]() { cevb.loopForever(); });\n\n  // Wait for event loop to start\n  cevb.waitUntilRunning();\n\n  // Send ping\n  cevb.runInEventBaseThread([&]() { client->start(server->address()); });\n\n  // Wait for client to finish\n  clientThread.join();\n  return std::move(client);\n}\n\nTEST_F(AsyncSocketSendmmsgIntegrationTest, PingPongRequest) {\n  SizeVec in{\n      1,  2,  3,  4,   5,   8,   8,   9,   10,  11,  22,  33,  44,  55,  66,\n      77, 88, 99, 110, 120, 220, 320, 420, 520, 620, 720, 820, 920, 1020};\n\n  TestData testData(in);\n  startServer();\n  auto pingClient = performPingPongTest(testData, folly::none);\n  CHECK(testData.checkOut());\n}\n"
  },
  {
    "path": "folly/io/async/test/AsyncUDPSocketTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/SocketOptionMap.h>\n#include <folly/io/async/AsyncUDPSocket.h>\n\n#include <thread>\n\n#include <folly/Conv.h>\n#include <folly/SocketAddress.h>\n#include <folly/String.h>\n#include <folly/io/IOBuf.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/AsyncUDPServerSocket.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/net/test/MockNetOpsDispatcher.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Sockets.h>\n#include <folly/testing/TestUtil.h>\n\nusing folly::AsyncTimeout;\nusing folly::AsyncUDPServerSocket;\nusing folly::AsyncUDPSocket;\nusing folly::errnoStr;\nusing folly::EventBase;\nusing folly::SocketAddress;\nusing namespace testing;\n\nusing OnDataAvailableParams =\n    folly::AsyncUDPSocket::ReadCallback::OnDataAvailableParams;\n\nclass UDPAcceptor : public AsyncUDPServerSocket::Callback {\n public:\n  UDPAcceptor(\n      EventBase* evb,\n      int n,\n      bool changePortForWrites,\n      const folly::SocketAddress& serverAddress)\n      : evb_(evb),\n        n_(n),\n        changePortForWrites_(changePortForWrites),\n        serverAddress_(serverAddress) {}\n\n  void onListenStarted() noexcept override {}\n\n  void onListenStopped() noexcept override {}\n\n  void onDataAvailable(\n      std::shared_ptr<folly::AsyncUDPSocket> /* socket */,\n      const folly::SocketAddress& client,\n      std::unique_ptr<folly::IOBuf> data,\n      bool truncated,\n      OnDataAvailableParams) noexcept override {\n    lastClient_ = client;\n    lastMsg_ = data->to<std::string>();\n\n    auto len = data->computeChainDataLength();\n    VLOG(4) << \"Worker \" << n_ << \" read \" << len << \" bytes \" << \"(trun:\"\n            << truncated << \") from \" << client.describe() << \" - \" << lastMsg_;\n\n    sendPong();\n  }\n\n  void sendPong() noexcept {\n    try {\n      auto writeSocket = std::make_shared<folly::AsyncUDPSocket>(evb_);\n      if (changePortForWrites_) {\n        writeSocket->setReuseAddr(false);\n        writeSocket->bind(folly::SocketAddress(\"127.0.0.1\", 0));\n      } else {\n        writeSocket->setReusePort(true);\n        writeSocket->bind(serverAddress_);\n      }\n      writeSocket->setTosOrTrafficClass(2);\n      writeSocket->write(lastClient_, folly::IOBuf::copyBuffer(lastMsg_));\n    } catch (const std::exception& ex) {\n      VLOG(4) << \"Failed to send PONG \" << ex.what();\n    }\n  }\n\n private:\n  EventBase* const evb_{nullptr};\n  const int n_{-1};\n  // Whether to create a new port per write.\n  bool changePortForWrites_{true};\n\n  folly::SocketAddress serverAddress_;\n  folly::SocketAddress lastClient_;\n  std::string lastMsg_;\n};\n\nclass UDPServer {\n public:\n  UDPServer(EventBase* evb, folly::SocketAddress addr, int n)\n      : evb_(evb), addr_(addr), evbs_(n) {}\n\n  void start() {\n    CHECK(evb_->isInEventBaseThread());\n\n    socket_ = std::make_unique<AsyncUDPServerSocket>(evb_, 1500);\n    socket_->setReusePort(true);\n\n    try {\n      socket_->bind(addr_);\n      VLOG(4) << \"Server listening on \" << socket_->address().describe();\n    } catch (const std::exception& ex) {\n      LOG(FATAL) << ex.what();\n    }\n\n    acceptors_.reserve(evbs_.size());\n    threads_.reserve(evbs_.size());\n\n    // Add numWorkers thread\n    int i = 0;\n    for (auto& evb : evbs_) {\n      acceptors_.emplace_back(\n          &evb, i, changePortForWrites_, socket_->address());\n\n      std::thread t([&]() { evb.loopForever(); });\n\n      evb.waitUntilRunning();\n\n      socket_->addListener(&evb, &acceptors_[i]);\n      threads_.emplace_back(std::move(t));\n      ++i;\n    }\n\n    socket_->listen();\n  }\n\n  folly::SocketAddress address() const { return socket_->address(); }\n\n  void shutdown() {\n    CHECK(evb_->isInEventBaseThread());\n    socket_->close();\n    socket_.reset();\n\n    for (auto& evb : evbs_) {\n      evb.terminateLoopSoon();\n    }\n\n    for (auto& t : threads_) {\n      t.join();\n    }\n  }\n\n  void pauseAccepting() { socket_->pauseAccepting(); }\n\n  void resumeAccepting() { socket_->resumeAccepting(); }\n\n  bool isAccepting() { return socket_->isAccepting(); }\n\n  // Whether writes from the UDP server should change the port for each message.\n  void setChangePortForWrites(bool changePortForWrites) {\n    changePortForWrites_ = changePortForWrites;\n  }\n\n private:\n  EventBase* const evb_{nullptr};\n  const folly::SocketAddress addr_;\n\n  std::unique_ptr<AsyncUDPServerSocket> socket_;\n  std::vector<std::thread> threads_;\n  std::vector<UDPAcceptor> acceptors_;\n  // destroy evbs_ before acceptors_ so that onListenStopped not called on a\n  // freed UDPAcceptor\n  std::vector<folly::EventBase> evbs_;\n  bool changePortForWrites_{true};\n};\n\nenum class BindSocket { YES, NO };\n\nclass UDPClient : private AsyncUDPSocket::ReadCallback, private AsyncTimeout {\n public:\n  using AsyncUDPSocket::ReadCallback::OnDataAvailableParams;\n\n  ~UDPClient() override = default;\n\n  explicit UDPClient(EventBase* evb) : AsyncTimeout(evb), evb_(evb) {}\n\n  void start(\n      const folly::SocketAddress& server, int n, bool sendClustered = false) {\n    CHECK(evb_->isInEventBaseThread());\n    server_ = server;\n    socket_ = std::make_unique<AsyncUDPSocket>(evb_);\n    socket_->setRecvTos(recvTos_);\n    ASSERT_EQ(socket_->getRecvTos(), recvTos_);\n\n    try {\n      if (bindSocket_ == BindSocket::YES) {\n        socket_->bind(folly::SocketAddress(\"127.0.0.1\", 0));\n      }\n      if (connectAddr_) {\n        socket_->connect(*connectAddr_);\n        VLOG(2) << \"Client connected to address=\" << *connectAddr_;\n      }\n      VLOG(2) << \"Client bound to \" << socket_->address().describe();\n    } catch (const std::exception& ex) {\n      LOG(FATAL) << ex.what();\n    }\n\n    socket_->resumeRead(this);\n\n    n_ = n;\n\n    // Start playing ping pong\n    if (sendClustered) {\n      sendPingsClustered();\n    } else {\n      sendPing();\n    }\n  }\n\n  void shutdown() {\n    CHECK(evb_->isInEventBaseThread());\n    if (socket_) {\n      socket_->pauseRead();\n      socket_->close();\n      socket_.reset();\n    }\n    evb_->terminateLoopSoon();\n  }\n\n  void sendPing() {\n    if (n_ == 0) {\n      shutdown();\n      return;\n    }\n\n    --n_;\n    scheduleTimeout(5);\n    writePing(folly::IOBuf::copyBuffer(folly::to<std::string>(\"PING \", n_)));\n  }\n\n  void sendPingsClustered() {\n    // give the server a bit more time to burst befor we time out\n    scheduleTimeout(50);\n    while (n_ > 0) {\n      --n_;\n      writePing(folly::IOBuf::copyBuffer(folly::to<std::string>(\"PING \", n_)));\n    }\n  }\n\n  virtual void writePing(std::unique_ptr<folly::IOBuf> buf) {\n    auto ret = socket_->write(server_, std::move(buf));\n    if (ret == -1) {\n      error_ = true;\n    }\n  }\n\n  void getReadBuffer(void** buf, size_t* len) noexcept override {\n    *buf = buf_;\n    *len = 1024;\n  }\n\n  void onDataAvailable(\n      const folly::SocketAddress& client,\n      size_t len,\n      bool truncated,\n      OnDataAvailableParams params) noexcept override {\n    VLOG(4) << \"Read \" << len << \" bytes (trun:\" << truncated << \") from \"\n            << client.describe() << \" - \" << std::string(buf_, len);\n    VLOG(4) << n_ << \" left\";\n    VLOG(4) << \"Type of Service value:\" << params.tos;\n\n    ++pongRecvd_;\n    if (params.tos != 0) {\n      ++tosMessagesRecvd_;\n    }\n\n    sendPing();\n  }\n\n  void onReadError(const folly::AsyncSocketException& ex) noexcept override {\n    VLOG(4) << ex.what();\n\n    // Start listening for next PONG\n    socket_->resumeRead(this);\n  }\n\n  void onReadClosed() noexcept override {\n    CHECK(false) << \"We unregister reads before closing\";\n  }\n\n  void timeoutExpired() noexcept override {\n    VLOG(4) << \"Timeout expired\";\n    sendPing();\n  }\n\n  int pongRecvd() const { return pongRecvd_; }\n\n  int tosMessagesRecvd() const { return tosMessagesRecvd_; }\n\n  AsyncUDPSocket& getSocket() { return *socket_; }\n\n  void setShouldConnect(\n      const folly::SocketAddress& connectAddr, BindSocket bindSocket) {\n    connectAddr_ = connectAddr;\n    bindSocket_ = bindSocket;\n  }\n\n  void setRecvTos(bool recvTos) { recvTos_ = recvTos; }\n\n  bool error() const { return error_; }\n\n  void incrementPongCount(int n) { pongRecvd_ += n; }\n\n protected:\n  folly::Optional<folly::SocketAddress> connectAddr_;\n  BindSocket bindSocket_{BindSocket::YES};\n  EventBase* const evb_{nullptr};\n\n  folly::SocketAddress server_;\n  std::unique_ptr<AsyncUDPSocket> socket_;\n  bool error_{false};\n\n private:\n  int pongRecvd_{0};\n  int tosMessagesRecvd_{0};\n  bool recvTos_{false};\n\n  int n_{0};\n  char buf_[1024];\n};\n\nclass UDPNotifyClient : public UDPClient {\n public:\n  ~UDPNotifyClient() override = default;\n\n  explicit UDPNotifyClient(\n      EventBase* evb, bool useRecvmmsg = false, unsigned int numMsgs = 1)\n      : UDPClient(evb), useRecvmmsg_(useRecvmmsg), numMsgs_(numMsgs) {}\n\n  bool shouldOnlyNotify() override { return true; }\n\n  void onRecvMsg(AsyncUDPSocket& sock) {\n    struct msghdr msg;\n    memset(&msg, 0, sizeof(msg));\n\n    void* buf{};\n    size_t len{};\n    getReadBuffer(&buf, &len);\n\n    iovec vec;\n    vec.iov_base = buf;\n    vec.iov_len = len;\n    struct sockaddr_storage addrStorage;\n    socklen_t addrLen = sizeof(addrStorage);\n    memset(&addrStorage, 0, size_t(addrLen));\n    auto rawAddr = reinterpret_cast<sockaddr*>(&addrStorage);\n    rawAddr->sa_family = socket_->address().getFamily();\n\n    msg.msg_name = rawAddr;\n    msg.msg_namelen = addrLen;\n    msg.msg_iov = &vec;\n    msg.msg_iovlen = 1;\n\n    ssize_t ret = sock.recvmsg(&msg, 0);\n    if (ret < 0) {\n      if (errno != EAGAIN || errno != EWOULDBLOCK) {\n        onReadError(\n            folly::AsyncSocketException(\n                folly::AsyncSocketException::NETWORK_ERROR, \"error\"));\n      }\n    } else {\n      // Datagram sockets in various domains (e.g., the UNIX and Internet\n      // domains) permit zero-length datagrams.  When such a datagram is\n      // received, the return value is 0.\n      auto numBytesRecv{static_cast<size_t>(ret)};\n\n      SocketAddress addr;\n      addr.setFromSockaddr(rawAddr, addrLen);\n\n      onDataAvailable(addr, numBytesRecv, false, OnDataAvailableParams());\n    }\n  }\n\n  void onRecvMmsg(AsyncUDPSocket& sock) {\n    const socklen_t addrLen = sizeof(struct sockaddr_storage);\n\n    const size_t dataSize = 1024;\n    std::vector<char> buf(numMsgs_ * dataSize);\n    std::vector<struct mmsghdr> msgs(numMsgs_);\n    std::vector<struct sockaddr_storage> addrs(numMsgs_);\n    std::vector<struct iovec> iovecs(numMsgs_);\n\n    for (unsigned int i = 0; i < numMsgs_; ++i) {\n      struct msghdr* msg = &msgs[i].msg_hdr;\n\n      auto rawAddr = reinterpret_cast<sockaddr*>(&addrs[i]);\n      rawAddr->sa_family = socket_->address().getFamily();\n\n      iovecs[i].iov_base = &buf[i * dataSize];\n      iovecs[i].iov_len = dataSize;\n\n      msg->msg_name = rawAddr;\n      msg->msg_namelen = addrLen;\n      msg->msg_iov = &iovecs[i];\n      msg->msg_iovlen = 1;\n    }\n\n    int ret = sock.recvmmsg(\n        msgs.data(), numMsgs_, 0x10000 /* MSG_WAITFORONE */, nullptr);\n    if (ret < 0) {\n      if (errno != EAGAIN || errno != EWOULDBLOCK) {\n        onReadError(\n            folly::AsyncSocketException(\n                folly::AsyncSocketException::NETWORK_ERROR, \"error\"));\n      }\n      return;\n    }\n\n    incrementPongCount(ret);\n\n    if (pongRecvd() == (int)numMsgs_) {\n      shutdown();\n    } else {\n      onRecvMmsg(sock);\n    }\n  }\n\n  void onNotifyDataAvailable(AsyncUDPSocket& sock) noexcept override {\n    notifyInvoked = true;\n    if (useRecvmmsg_) {\n      onRecvMmsg(sock);\n    } else {\n      onRecvMsg(sock);\n    }\n  }\n\n  bool notifyInvoked{false};\n  bool useRecvmmsg_{false};\n  unsigned int numMsgs_{1};\n};\n\nclass AsyncSocketIntegrationTest : public Test {\n public:\n  void SetUp() override {\n    server = std::make_unique<UDPServer>(\n        &sevb, folly::SocketAddress(\"127.0.0.1\", 0), 4);\n\n    // Start event loop in a separate thread\n    serverThread = std::make_unique<std::thread>([this]() {\n      sevb.loopForever();\n    });\n\n    // Wait for event loop to start\n    sevb.waitUntilRunning();\n  }\n\n  void startServer() {\n    // Start the server\n    sevb.runInEventBaseThreadAndWait([&]() { server->start(); });\n    LOG(INFO) << \"Server listening=\" << server->address();\n  }\n\n  void TearDown() override {\n    // Shutdown server\n    sevb.runInEventBaseThread([&]() {\n      server->shutdown();\n      sevb.terminateLoopSoon();\n    });\n\n    // Wait for server thread to join\n    serverThread->join();\n  }\n\n  std::unique_ptr<UDPClient> performPingPongTest(\n      folly::SocketAddress writeAddress,\n      folly::Optional<folly::SocketAddress> connectedAddress,\n      BindSocket bindSocket = BindSocket::YES);\n\n  std::unique_ptr<UDPNotifyClient> performPingPongNotifyTest(\n      folly::SocketAddress writeAddress,\n      folly::Optional<folly::SocketAddress> connectedAddress,\n      BindSocket bindSocket = BindSocket::YES);\n\n  std::unique_ptr<UDPNotifyClient> performPingPongNotifyMmsgTest(\n      folly::SocketAddress writeAddress,\n      unsigned int numMsgs,\n      folly::Optional<folly::SocketAddress> connectedAddress,\n      BindSocket bindSocket = BindSocket::YES);\n\n  std::unique_ptr<UDPClient> performPingPongRecvTosTest(\n      folly::SocketAddress writeAddress,\n      folly::Optional<folly::SocketAddress> connectedAddress,\n      BindSocket bindSocket = BindSocket::YES);\n\n  std::unique_ptr<std::thread> serverThread;\n  std::unique_ptr<UDPServer> server;\n  folly::EventBase sevb;\n  folly::EventBase cevb;\n};\n\nstd::unique_ptr<UDPClient> AsyncSocketIntegrationTest::performPingPongTest(\n    folly::SocketAddress writeAddress,\n    folly::Optional<folly::SocketAddress> connectedAddress,\n    BindSocket bindSocket) {\n  auto client = std::make_unique<UDPClient>(&cevb);\n  if (connectedAddress) {\n    client->setShouldConnect(*connectedAddress, bindSocket);\n  }\n  // Start event loop in a separate thread\n  auto clientThread = std::thread([this]() { cevb.loopForever(); });\n\n  // Wait for event loop to start\n  cevb.waitUntilRunning();\n\n  // Send ping\n  cevb.runInEventBaseThread([&]() { client->start(writeAddress, 100); });\n\n  // Wait for client to finish\n  clientThread.join();\n  return client;\n}\n\nstd::unique_ptr<UDPNotifyClient>\nAsyncSocketIntegrationTest::performPingPongNotifyTest(\n    folly::SocketAddress writeAddress,\n    folly::Optional<folly::SocketAddress> connectedAddress,\n    BindSocket bindSocket) {\n  auto client = std::make_unique<UDPNotifyClient>(&cevb);\n  if (connectedAddress) {\n    client->setShouldConnect(*connectedAddress, bindSocket);\n  }\n  // Start event loop in a separate thread\n  auto clientThread = std::thread([this]() { cevb.loopForever(); });\n\n  // Wait for event loop to start\n  cevb.waitUntilRunning();\n\n  // Send ping\n  cevb.runInEventBaseThread([&]() { client->start(writeAddress, 100); });\n\n  // Wait for client to finish\n  clientThread.join();\n  return client;\n}\n\nstd::unique_ptr<UDPNotifyClient>\nAsyncSocketIntegrationTest::performPingPongNotifyMmsgTest(\n    folly::SocketAddress writeAddress,\n    unsigned int numMsgs,\n    folly::Optional<folly::SocketAddress> connectedAddress,\n    BindSocket bindSocket) {\n  auto client = std::make_unique<UDPNotifyClient>(&cevb, true, numMsgs);\n  if (connectedAddress) {\n    client->setShouldConnect(*connectedAddress, bindSocket);\n  }\n  // Start event loop in a separate thread\n  auto clientThread = std::thread([this]() { cevb.loopForever(); });\n\n  // Wait for event loop to start\n  cevb.waitUntilRunning();\n\n  // Send ping\n  cevb.runInEventBaseThread([&]() {\n    client->start(writeAddress, numMsgs, true);\n  });\n\n  // Wait for client to finish\n  clientThread.join();\n  return client;\n}\n\nstd::unique_ptr<UDPClient>\nAsyncSocketIntegrationTest::performPingPongRecvTosTest(\n    folly::SocketAddress writeAddress,\n    folly::Optional<folly::SocketAddress> connectedAddress,\n    BindSocket bindSocket) {\n  auto client = std::make_unique<UDPClient>(&cevb);\n  if (connectedAddress) {\n    client->setShouldConnect(*connectedAddress, bindSocket);\n  }\n  // Start event loop in a separate thread\n  auto clientThread = std::thread([this]() { cevb.loopForever(); });\n\n  // Wait for event loop to start\n  cevb.waitUntilRunning();\n\n  // Enable receiving ToS value\n  client->setRecvTos(true);\n\n  // Send ping\n  cevb.runInEventBaseThread([&]() { client->start(writeAddress, 100); });\n\n  // Wait for client to finish\n  clientThread.join();\n  return client;\n}\n\nTEST_F(AsyncSocketIntegrationTest, PingPong) {\n  startServer();\n  auto pingClient = performPingPongTest(server->address(), folly::none);\n  // This should succeed.\n  ASSERT_GT(pingClient->pongRecvd(), 0);\n}\n\nTEST_F(AsyncSocketIntegrationTest, PingPongNotify) {\n  startServer();\n  auto pingClient = performPingPongNotifyTest(server->address(), folly::none);\n  // This should succeed.\n  ASSERT_GT(pingClient->pongRecvd(), 0);\n  ASSERT_TRUE(pingClient->notifyInvoked);\n}\n\nTEST_F(AsyncSocketIntegrationTest, PingPongNotifyMmsg) {\n  startServer();\n  auto pingClient =\n      performPingPongNotifyMmsgTest(server->address(), 10, folly::none);\n  // This should succeed.\n  ASSERT_EQ(pingClient->pongRecvd(), 10);\n  ASSERT_TRUE(pingClient->notifyInvoked);\n}\n\nTEST_F(AsyncSocketIntegrationTest, PingPongRecvTosDisabled) {\n  startServer();\n  auto pingClient = performPingPongTest(server->address(), folly::none);\n  // This should succeed.\n  ASSERT_GT(pingClient->pongRecvd(), 0);\n  ASSERT_EQ(pingClient->tosMessagesRecvd(), 0);\n}\n\nTEST_F(AsyncSocketIntegrationTest, PingPongRecvTos) {\n  startServer();\n  auto pingClient = performPingPongRecvTosTest(server->address(), folly::none);\n  // This should succeed.\n  ASSERT_GT(pingClient->pongRecvd(), 0);\n  ASSERT_GT(pingClient->tosMessagesRecvd(), 0);\n}\n\nclass ConnectedAsyncSocketIntegrationTest\n    : public AsyncSocketIntegrationTest,\n      public WithParamInterface<BindSocket> {};\n\nTEST_P(ConnectedAsyncSocketIntegrationTest, ConnectedPingPong) {\n  server->setChangePortForWrites(false);\n  startServer();\n  auto pingClient =\n      performPingPongTest(server->address(), server->address(), GetParam());\n  // This should succeed\n  ASSERT_GT(pingClient->pongRecvd(), 0);\n}\n\nTEST_P(\n    ConnectedAsyncSocketIntegrationTest, ConnectedPingPongServerWrongAddress) {\n  server->setChangePortForWrites(true);\n  startServer();\n  auto pingClient =\n      performPingPongTest(server->address(), server->address(), GetParam());\n  // This should fail.\n  ASSERT_EQ(pingClient->pongRecvd(), 0);\n}\n\nTEST_P(\n    ConnectedAsyncSocketIntegrationTest, ConnectedPingPongClientWrongAddress) {\n  server->setChangePortForWrites(false);\n  startServer();\n  folly::SocketAddress connectAddr(\n      server->address().getIPAddress(), server->address().getPort() + 1);\n  auto pingClient =\n      performPingPongTest(server->address(), connectAddr, GetParam());\n  // This should fail.\n  ASSERT_EQ(pingClient->pongRecvd(), 0);\n  EXPECT_TRUE(pingClient->error());\n}\n\nTEST_P(\n    ConnectedAsyncSocketIntegrationTest,\n    ConnectedPingPongDifferentWriteAddress) {\n  server->setChangePortForWrites(false);\n  startServer();\n  folly::SocketAddress connectAddr(\n      server->address().getIPAddress(), server->address().getPort() + 1);\n  auto pingClient =\n      performPingPongTest(connectAddr, server->address(), GetParam());\n  // This should fail.\n  ASSERT_EQ(pingClient->pongRecvd(), 0);\n  EXPECT_TRUE(pingClient->error());\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    ConnectedAsyncSocketIntegrationTests,\n    ConnectedAsyncSocketIntegrationTest,\n    Values(BindSocket::YES, BindSocket::NO));\n\nTEST_F(AsyncSocketIntegrationTest, PingPongPauseResumeListening) {\n  startServer();\n\n  // Exchange should not happen when paused.\n  server->pauseAccepting();\n  EXPECT_FALSE(server->isAccepting());\n  auto pausedClient = performPingPongTest(server->address(), folly::none);\n  ASSERT_EQ(pausedClient->pongRecvd(), 0);\n\n  // Exchange does occur after resuming.\n  server->resumeAccepting();\n  EXPECT_TRUE(server->isAccepting());\n  auto pingClient = performPingPongTest(server->address(), folly::none);\n  ASSERT_GT(pingClient->pongRecvd(), 0);\n}\n\nclass MockErrMessageCallback : public AsyncUDPSocket::ErrMessageCallback {\n public:\n  ~MockErrMessageCallback() override = default;\n\n  MOCK_METHOD(void, errMessage_, (const cmsghdr&));\n  void errMessage(const cmsghdr& cmsg) noexcept override { errMessage_(cmsg); }\n\n  MOCK_METHOD(void, errMessageError_, (const folly::AsyncSocketException&));\n  void errMessageError(\n      const folly::AsyncSocketException& ex) noexcept override {\n    errMessageError_(ex);\n  }\n};\n\nclass MockUDPReadCallback : public AsyncUDPSocket::ReadCallback {\n public:\n  ~MockUDPReadCallback() override = default;\n\n  MOCK_METHOD(void, getReadBuffer_, (void**, size_t*));\n  void getReadBuffer(void** buf, size_t* len) noexcept override {\n    getReadBuffer_(buf, len);\n  }\n\n  MOCK_METHOD(bool, shouldOnlyNotify, ());\n  MOCK_METHOD(void, onNotifyDataAvailable_, (folly::AsyncUDPSocket&));\n  void onNotifyDataAvailable(folly::AsyncUDPSocket& sock) noexcept override {\n    onNotifyDataAvailable_(sock);\n  }\n\n  MOCK_METHOD(\n      void,\n      onDataAvailable_,\n      (const folly::SocketAddress&, size_t, bool, OnDataAvailableParams));\n  void onDataAvailable(\n      const folly::SocketAddress& client,\n      size_t len,\n      bool truncated,\n      OnDataAvailableParams params) noexcept override {\n    onDataAvailable_(client, len, truncated, params);\n  }\n\n  MOCK_METHOD(void, onReadError_, (const folly::AsyncSocketException&));\n  void onReadError(const folly::AsyncSocketException& ex) noexcept override {\n    onReadError_(ex);\n  }\n\n  MOCK_METHOD(void, onReadClosed_, ());\n  void onReadClosed() noexcept override { onReadClosed_(); }\n};\n\nclass AsyncUDPSocketTest : public Test {\n public:\n  void SetUp() override {\n    socket_ = std::make_shared<AsyncUDPSocket>(&evb_);\n    addr_ = folly::SocketAddress(\"127.0.0.1\", 0);\n    socket_->bind(addr_);\n  }\n\n  EventBase evb_;\n  MockErrMessageCallback err;\n  MockUDPReadCallback readCb;\n  std::shared_ptr<AsyncUDPSocket> socket_;\n  folly::SocketAddress addr_;\n};\n\nTEST_F(AsyncUDPSocketTest, TestConnectAfterBind) {\n  socket_->connect(addr_);\n}\n\nTEST_F(AsyncUDPSocketTest, TestConnect) {\n  AsyncUDPSocket socket(&evb_);\n  EXPECT_FALSE(socket.isBound());\n  folly::SocketAddress address(\"127.0.0.1\", 443);\n  socket.connect(address);\n  EXPECT_TRUE(socket.isBound());\n\n  const auto& localAddr = socket.address();\n  EXPECT_TRUE(localAddr.isInitialized());\n  EXPECT_GT(localAddr.getPort(), 0);\n}\n\nTEST_F(AsyncUDPSocketTest, TestErrToNonExistentServer) {\n  socket_->resumeRead(&readCb);\n  socket_->setErrMessageCallback(&err);\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  bool errRecvd = false;\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  EXPECT_CALL(err, errMessage_(_))\n      .WillOnce(Invoke([this, &errRecvd](auto& cmsg) {\n        if ((cmsg.cmsg_level == SOL_IP && cmsg.cmsg_type == IP_RECVERR) ||\n            (cmsg.cmsg_level == SOL_IPV6 && cmsg.cmsg_type == IPV6_RECVERR)) {\n          const struct sock_extended_err* serr =\n              reinterpret_cast<const struct sock_extended_err*>(\n                  CMSG_DATA(&cmsg));\n          errRecvd =\n              (serr->ee_origin == SO_EE_ORIGIN_ICMP || SO_EE_ORIGIN_ICMP6);\n          LOG(ERROR) << \"errno \" << errnoStr(serr->ee_errno);\n        }\n        evb_.terminateLoopSoon();\n      }));\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n  socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  evb_.loopForever();\n  EXPECT_TRUE(errRecvd);\n}\n\nTEST_F(AsyncUDPSocketTest, TestUnsetErrCallback) {\n  socket_->resumeRead(&readCb);\n  socket_->setErrMessageCallback(&err);\n  socket_->setErrMessageCallback(nullptr);\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  EXPECT_CALL(err, errMessage_(_)).Times(0);\n  socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  evb_.timer().scheduleTimeoutFn(\n      [&] { evb_.terminateLoopSoon(); }, std::chrono::milliseconds(30));\n  evb_.loopForever();\n}\n\nTEST_F(AsyncUDPSocketTest, CloseInErrorCallback) {\n  socket_->resumeRead(&readCb);\n  socket_->setErrMessageCallback(&err);\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  bool errRecvd = false;\n  EXPECT_CALL(err, errMessage_(_)).WillOnce(Invoke([this, &errRecvd](auto&) {\n    errRecvd = true;\n    socket_->close();\n    evb_.terminateLoopSoon();\n  }));\n  socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  evb_.loopForever();\n  EXPECT_TRUE(errRecvd);\n}\n\nTEST_F(AsyncUDPSocketTest, TestNonExistentServerNoErrCb) {\n  socket_->resumeRead(&readCb);\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  bool errRecvd = false;\n  folly::IOBufQueue readBuf;\n  EXPECT_CALL(readCb, getReadBuffer_(_, _))\n      .WillRepeatedly(Invoke([&readBuf](void** buf, size_t* len) {\n        auto readSpace = readBuf.preallocate(2000, 10000);\n        *buf = readSpace.first;\n        *len = readSpace.second;\n      }));\n  ON_CALL(readCb, onReadError_(_)).WillByDefault(Invoke([&errRecvd](auto& ex) {\n    LOG(ERROR) << ex.what();\n    errRecvd = true;\n  }));\n  socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  evb_.timer().scheduleTimeoutFn(\n      [&] { evb_.terminateLoopSoon(); }, std::chrono::milliseconds(30));\n  evb_.loopForever();\n  EXPECT_FALSE(errRecvd);\n}\n\nTEST_F(AsyncUDPSocketTest, TestBound) {\n  AsyncUDPSocket socket(&evb_);\n  EXPECT_FALSE(socket.isBound());\n  folly::SocketAddress address(\"0.0.0.0\", 0);\n  socket.bind(address);\n  EXPECT_TRUE(socket.isBound());\n}\n\nTEST_F(AsyncUDPSocketTest, TestBoundUnixSocket) {\n  folly::test::TemporaryDirectory tmpDirectory;\n  const auto kTmpUnixSocketPath{tmpDirectory.path() / \"unix_socket_path\"};\n  AsyncUDPSocket socket(&evb_);\n  EXPECT_FALSE(socket.isBound());\n  socket.bind(folly::SocketAddress::makeFromPath(kTmpUnixSocketPath.string()));\n  EXPECT_TRUE(socket.isBound());\n  socket.close();\n}\n\nTEST_F(AsyncUDPSocketTest, TestAttachAfterDetachEvbWithReadCallback) {\n  socket_->resumeRead(&readCb);\n  EXPECT_TRUE(socket_->isHandlerRegistered());\n  socket_->detachEventBase();\n  EXPECT_FALSE(socket_->isHandlerRegistered());\n  socket_->attachEventBase(&evb_);\n  EXPECT_TRUE(socket_->isHandlerRegistered());\n}\n\nTEST_F(AsyncUDPSocketTest, TestAttachAfterDetachEvbNoReadCallback) {\n  EXPECT_FALSE(socket_->isHandlerRegistered());\n  socket_->detachEventBase();\n  EXPECT_FALSE(socket_->isHandlerRegistered());\n  socket_->attachEventBase(&evb_);\n  EXPECT_FALSE(socket_->isHandlerRegistered());\n}\n\nTEST_F(AsyncUDPSocketTest, TestDetachAttach) {\n  folly::EventBase evb2;\n  auto writeSocket = std::make_shared<folly::AsyncUDPSocket>(&evb_);\n  folly::SocketAddress address(\"127.0.0.1\", 0);\n  writeSocket->bind(address);\n  std::array<uint8_t, 1024> data;\n  std::atomic<int> packetsRecvd{0};\n  EXPECT_CALL(readCb, getReadBuffer_(_, _))\n      .WillRepeatedly(Invoke([&](void** buf, size_t* len) {\n        *buf = data.data();\n        *len = 1024;\n      }));\n  EXPECT_CALL(readCb, onDataAvailable_(_, _, _, _))\n      .WillRepeatedly(Invoke(\n          [&](const folly::SocketAddress&,\n              size_t,\n              bool,\n              OnDataAvailableParams) { packetsRecvd++; }));\n  socket_->resumeRead(&readCb);\n  writeSocket->write(socket_->address(), folly::IOBuf::copyBuffer(\"hello\"));\n  while (packetsRecvd != 1) {\n    evb_.loopOnce();\n  }\n  EXPECT_EQ(packetsRecvd, 1);\n\n  socket_->detachEventBase();\n  std::thread t([&] { evb2.loopForever(); });\n  evb2.runInEventBaseThreadAndWait([&] { socket_->attachEventBase(&evb2); });\n  writeSocket->write(socket_->address(), folly::IOBuf::copyBuffer(\"hello\"));\n  auto now = std::chrono::steady_clock::now();\n  while (\n      packetsRecvd != 2 ||\n      std::chrono::steady_clock::now() < now + std::chrono::milliseconds(10)) {\n    std::this_thread::sleep_for(std::chrono::milliseconds(1));\n  }\n  evb2.runInEventBaseThread([&] {\n    socket_ = nullptr;\n    evb2.terminateLoopSoon();\n  });\n  t.join();\n  EXPECT_EQ(packetsRecvd, 2);\n}\n\nMATCHER_P(HasCmsgs, cmsgs, \"\") {\n  struct msghdr* msg = const_cast<struct msghdr*>(arg);\n  if (msg == nullptr) {\n    return false;\n  }\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  folly::SocketCmsgMap sentCmsgs;\n\n  struct cmsghdr* cmsg;\n  for (cmsg = CMSG_FIRSTHDR(msg); cmsg != nullptr;\n       cmsg = CMSG_NXTHDR(msg, cmsg)) {\n    if (cmsg->cmsg_level == SOL_UDP) {\n      if (cmsg->cmsg_type == UDP_SEGMENT) {\n        uint16_t gso;\n        memcpy(\n            &gso,\n            reinterpret_cast<struct timespec*>(CMSG_DATA(cmsg)),\n            sizeof(gso));\n        sentCmsgs[{SOL_UDP, UDP_SEGMENT}] = gso;\n      }\n    }\n    if (cmsg->cmsg_level == SOL_SOCKET) {\n      if (cmsg->cmsg_type == SO_MARK) {\n        uint32_t somark;\n        memcpy(\n            &somark,\n            reinterpret_cast<struct timespec*>(CMSG_DATA(cmsg)),\n            sizeof(somark));\n        sentCmsgs[{SOL_SOCKET, SO_MARK}] = somark;\n      }\n    }\n    if (cmsg->cmsg_level == IPPROTO_IP) {\n      if (cmsg->cmsg_type == IP_TOS) {\n        uint32_t tos;\n        memcpy(\n            &tos,\n            reinterpret_cast<struct timespec*>(CMSG_DATA(cmsg)),\n            sizeof(tos));\n        sentCmsgs[{IPPROTO_IP, IP_TOS}] = tos;\n      }\n      if (cmsg->cmsg_type == IP_TTL) {\n        uint32_t ttl;\n        memcpy(\n            &ttl,\n            reinterpret_cast<struct timespec*>(CMSG_DATA(cmsg)),\n            sizeof(ttl));\n        sentCmsgs[{IPPROTO_IP, IP_TTL}] = ttl;\n      }\n    }\n  }\n  return sentCmsgs == cmsgs;\n#else\n  return false;\n#endif\n}\n\nMATCHER_P(HasNontrivialCmsgs, cmsgs, \"\") {\n  struct msghdr* msg = const_cast<struct msghdr*>(arg);\n  if (msg == nullptr) {\n    return false;\n  }\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  folly::SocketNontrivialCmsgMap sentCmsgs;\n\n  struct cmsghdr* cmsg;\n  for (cmsg = CMSG_FIRSTHDR(msg); cmsg != nullptr;\n       cmsg = CMSG_NXTHDR(msg, cmsg)) {\n    if (cmsg->cmsg_level == SOL_SOCKET) {\n      if (cmsg->cmsg_type == SO_LINGER) {\n        struct linger sl{\n            .l_onoff = 0,\n            .l_linger = 0,\n        };\n\n        memcpy(\n            &sl, reinterpret_cast<struct linger*>(CMSG_DATA(cmsg)), sizeof(sl));\n        sentCmsgs[{SOL_SOCKET, SO_LINGER}] =\n            std::string(reinterpret_cast<const char*>(&sl), sizeof(sl));\n      }\n    }\n  }\n  return sentCmsgs == cmsgs;\n#else\n  return false;\n#endif\n}\n\nTEST_F(AsyncUDPSocketTest, TestWriteCmsg) {\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  auto netOpsDispatcher =\n      std::make_shared<NiceMock<folly::netops::test::MockDispatcher>>();\n  socket_->setOverrideNetOpsDispatcher(netOpsDispatcher);\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  // empty\n  {\n    folly::SocketCmsgMap cmsgs;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(cmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // writeGSO\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{SOL_UDP, UDP_SEGMENT}] = 1;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(cmsgs), _));\n    socket_->writeGSO(\n        addr,\n        folly::IOBuf::copyBuffer(\"hey\"),\n        folly::AsyncUDPSocket::WriteOptions(\n            1 /*gsoVal*/, false /* zerocopyVal*/));\n  }\n  // SO_MARK\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    socket_->setCmsgs(cmsgs);\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(cmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // append IP_TOS\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    socket_->appendCmsgs(cmsgs);\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // append IP_TOS with a different value\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{IPPROTO_IP, IP_TOS}] = 789;\n    socket_->appendCmsgs(cmsgs);\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 789;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    socket_->setCmsgs(expectedCmsgs);\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // writeGSO with IP_TOS and SO_MARK\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 789;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{SOL_UDP, UDP_SEGMENT}] = 1;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->writeGSO(\n        addr,\n        folly::IOBuf::copyBuffer(\"hey\"),\n        folly::AsyncUDPSocket::WriteOptions(\n            1 /*gsoVal*/, false /* zerocopyVal*/));\n  }\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n  socket_->close();\n}\n\nTEST_F(AsyncUDPSocketTest, TestWriteDynamicCmsg) {\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  auto netOpsDispatcher =\n      std::make_shared<NiceMock<folly::netops::test::MockDispatcher>>();\n  socket_->setOverrideNetOpsDispatcher(netOpsDispatcher);\n\n  MockFunction<AsyncUDPSocket::AdditionalCmsgsFunc> mockAdditionalCmsgs;\n  int mockCallCount = 1;\n  socket_->setAdditionalCmsgsFunc([&mockCallCount]() {\n    folly::SocketCmsgMap additionalCmsgs;\n    additionalCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount++;\n    return additionalCmsgs;\n  });\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  // Dynamic cmsgs only (IP_TTL)\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // writeGSO with dynamic cmsgs (IP_TTL)\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{SOL_UDP, UDP_SEGMENT}] = 1;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->writeGSO(\n        addr,\n        folly::IOBuf::copyBuffer(\"hey\"),\n        folly::AsyncUDPSocket::WriteOptions(\n            1 /*gsoVal*/, false /* zerocopyVal*/));\n  }\n  // SO_MARK with dynamic cmsgs (IP_TTL)\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    socket_->setCmsgs(cmsgs);\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // append IP_TOS + IP_TTL + overwrite with dynamic cmsgs (IP_TTL)\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    cmsgs[{IPPROTO_IP, IP_TTL}] = 9999;\n    socket_->appendCmsgs(cmsgs);\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // append IP_TOS with a different value + dynamic cmsgs (IP_TTL)\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{IPPROTO_IP, IP_TOS}] = 789;\n    socket_->appendCmsgs(cmsgs);\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 789;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n  // writeGSO with IP_TOS and SO_MARK + dynamic cmsgs (IP_TTL)\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 789;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{SOL_UDP, UDP_SEGMENT}] = 1;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->writeGSO(\n        addr,\n        folly::IOBuf::copyBuffer(\"hey\"),\n        folly::AsyncUDPSocket::WriteOptions(\n            1 /*gsoVal*/, false /* zerocopyVal*/));\n  }\n  // Empty dynamic cmsgs should revert to default cmsgs\n  socket_->setAdditionalCmsgsFunc([]() { return folly::none; });\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 789;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = 9999; // This won't be overwritten\n    EXPECT_CALL(*netOpsDispatcher, sendmsg(_, HasCmsgs(expectedCmsgs), _));\n    socket_->write(addr, folly::IOBuf::copyBuffer(\"hey\"));\n  }\n\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n  socket_->close();\n}\n\nMATCHER_P2(AllHaveCmsgs, cmsgs, count, \"\") {\n  if (arg == nullptr) {\n    return false;\n  }\n  for (size_t i = 0; i < count; ++i) {\n    auto msg = arg[i].msg_hdr;\n    if (!Matches(HasCmsgs(cmsgs))(&msg)) {\n      return false;\n    }\n  }\n  return true;\n}\n\nMATCHER_P3(AllHaveCmsgsAndNontrivialCmsgs, cmsgs, nontrivialCmsgs, count, \"\") {\n  if (arg == nullptr) {\n    return false;\n  }\n  for (size_t i = 0; i < count; ++i) {\n    auto msg = arg[i].msg_hdr;\n    if (!Matches(HasCmsgs(cmsgs))(&msg) &&\n        !Matches(HasNontrivialCmsgs(nontrivialCmsgs))(&msg)) {\n      return false;\n    }\n  }\n  return true;\n}\n\nTEST(MatcherTest, AllHaveCmsgsTest) {\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  constexpr size_t count = 2;\n  constexpr size_t controlSize = 2;\n  mmsghdr msgvec[count];\n  char control[count * controlSize * CMSG_SPACE(sizeof(uint16_t))];\n  memset(control, 0, sizeof(control));\n  // two messages, one with all cmsgs, the other with only one\n  {\n    auto& msg = msgvec[0].msg_hdr;\n    msg.msg_control = &control[0];\n    msg.msg_controllen = controlSize * CMSG_SPACE(sizeof(int));\n    struct cmsghdr* cm = nullptr;\n    cm = CMSG_FIRSTHDR(&msg);\n    int val1 = 20;\n    cm->cmsg_level = SOL_SOCKET;\n    cm->cmsg_type = SO_MARK;\n    cm->cmsg_len = CMSG_LEN(sizeof(val1));\n    memcpy(CMSG_DATA(cm), &val1, sizeof(val1));\n    cm = CMSG_NXTHDR(&msg, cm);\n    int val2 = 30;\n    cm->cmsg_level = IPPROTO_IP;\n    cm->cmsg_type = IP_TOS;\n    cm->cmsg_len = CMSG_LEN(sizeof(val2));\n    memcpy(CMSG_DATA(cm), &val2, sizeof(val2));\n  }\n  {\n    auto& msg = msgvec[1].msg_hdr;\n    msg.msg_control = &control[controlSize * CMSG_SPACE(sizeof(uint16_t))];\n    msg.msg_controllen = CMSG_SPACE(sizeof(int));\n    struct cmsghdr* cm = nullptr;\n    cm = CMSG_FIRSTHDR(&msg);\n    int val = 20;\n    cm->cmsg_level = SOL_SOCKET;\n    cm->cmsg_type = SO_MARK;\n    cm->cmsg_len = CMSG_LEN(sizeof(val));\n    memcpy(CMSG_DATA(cm), &val, sizeof(val));\n  }\n  folly::SocketCmsgMap cmsgs;\n  cmsgs[{SOL_SOCKET, SO_MARK}] = 20;\n  cmsgs[{IPPROTO_IP, IP_TOS}] = 30;\n  struct mmsghdr* msgvecPtr = nullptr;\n  EXPECT_THAT(msgvecPtr, Not(AllHaveCmsgs(cmsgs, count)));\n  msgvecPtr = msgvec;\n  EXPECT_THAT(msgvecPtr, Not(AllHaveCmsgs(cmsgs, count)));\n  // true cases are tested in TestWritemCmsg\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n}\n\nTEST_F(AsyncUDPSocketTest, TestWritemCmsg) {\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  auto netOpsDispatcher =\n      std::make_shared<NiceMock<folly::netops::test::MockDispatcher>>();\n  socket_->setOverrideNetOpsDispatcher(netOpsDispatcher);\n  std::vector<std::unique_ptr<folly::IOBuf>> bufs;\n  bufs.emplace_back(folly::IOBuf::copyBuffer(\"hey1\"));\n  bufs.emplace_back(folly::IOBuf::copyBuffer(\"hey2\"));\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  // empty\n  {\n    folly::SocketCmsgMap cmsgs;\n    EXPECT_CALL(\n        *netOpsDispatcher, sendmmsg(_, AllHaveCmsgs(cmsgs, bufs.size()), _, _));\n    socket_->writem(folly::range(&addr, &addr + 1), bufs.data(), bufs.size());\n  }\n  // set IP_TOS & SO_MARK\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    cmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    socket_->setCmsgs(cmsgs);\n    EXPECT_CALL(\n        *netOpsDispatcher, sendmmsg(_, AllHaveCmsgs(cmsgs, bufs.size()), _, _));\n    socket_->writem(folly::range(&addr, &addr + 1), bufs.data(), bufs.size());\n  }\n  // writemGSO\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{SOL_UDP, UDP_SEGMENT}] = 1;\n    EXPECT_CALL(\n        *netOpsDispatcher,\n        sendmmsg(_, AllHaveCmsgs(expectedCmsgs, bufs.size()), _, _));\n    std::vector<folly::AsyncUDPSocket::WriteOptions> options{\n        {1, false}, {1, false}};\n    socket_->writemGSO(\n        folly::range(&addr, &addr + 1),\n        bufs.data(),\n        bufs.size(),\n        options.data());\n  }\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n  socket_->close();\n}\n\nTEST_F(AsyncUDPSocketTest, TestWritemDynamicCmsg) {\n  folly::SocketAddress addr(\"127.0.0.1\", 10000);\n  auto netOpsDispatcher =\n      std::make_shared<NiceMock<folly::netops::test::MockDispatcher>>();\n  socket_->setOverrideNetOpsDispatcher(netOpsDispatcher);\n  std::vector<std::unique_ptr<folly::IOBuf>> bufs;\n  bufs.emplace_back(folly::IOBuf::copyBuffer(\"hey1\"));\n  bufs.emplace_back(folly::IOBuf::copyBuffer(\"hey2\"));\n\n  MockFunction<AsyncUDPSocket::AdditionalCmsgsFunc> mockAdditionalCmsgs;\n  int mockCallCount = 1;\n  socket_->setAdditionalCmsgsFunc([&mockCallCount]() {\n    folly::SocketCmsgMap additionalCmsgs;\n    additionalCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount++;\n    return additionalCmsgs;\n  });\n\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  // Dynamic cmsgs only (IP_TTL)\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(\n        *netOpsDispatcher,\n        sendmmsg(_, AllHaveCmsgs(expectedCmsgs, bufs.size()), _, _));\n    socket_->writem(folly::range(&addr, &addr + 1), bufs.data(), bufs.size());\n  }\n  // set IP_TOS & SO_MARK & IP_TTL + overwrite from dynamic cmsgs (IP_TTL)\n  {\n    folly::SocketCmsgMap cmsgs;\n    cmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    cmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    cmsgs[{IPPROTO_IP, IP_TTL}] = 9999;\n    socket_->setCmsgs(cmsgs);\n    auto expectedCmsgs = cmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(\n        *netOpsDispatcher,\n        sendmmsg(_, AllHaveCmsgs(expectedCmsgs, bufs.size()), _, _));\n    socket_->writem(folly::range(&addr, &addr + 1), bufs.data(), bufs.size());\n  }\n  // writemGSO + dynamic cmsgs (IP_TTL)\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{SOL_UDP, UDP_SEGMENT}] = 1;\n    expectedCmsgs[{IPPROTO_IP, IP_TTL}] = mockCallCount;\n    EXPECT_CALL(\n        *netOpsDispatcher,\n        sendmmsg(_, AllHaveCmsgs(expectedCmsgs, bufs.size()), _, _));\n    std::vector<folly::AsyncUDPSocket::WriteOptions> options{\n        {1, false}, {1, false}};\n    socket_->writemGSO(\n        folly::range(&addr, &addr + 1),\n        bufs.data(),\n        bufs.size(),\n        options.data());\n  }\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n  socket_->close();\n}\n\nTEST_F(AsyncUDPSocketTest, TestApplyStringOptionsPostBind) {\n  EventBase evb;\n  AsyncUDPSocket socket(&evb);\n  ASSERT_FALSE(socket.isBound());\n  folly::SocketAddress address(\"127.0.0.1\", 443);\n  socket.connect(address);\n  ASSERT_TRUE(socket.isBound());\n\n  const auto& localAddr = socket.address();\n  ASSERT_TRUE(localAddr.isInitialized());\n  ASSERT_GT(localAddr.getPort(), 0);\n\n  folly::SocketOptionMap options = folly::emptySocketOptionMap;\n  struct linger sl{\n      .l_onoff = 1,\n      .l_linger = 123,\n  };\n\n  options.insert(\n      {folly::SocketOptionKey{SOL_SOCKET, SO_LINGER},\n       std::string((char*)&sl, sizeof(sl))});\n\n  socket.applyOptions(options, folly::SocketOptionKey::ApplyPos::POST_BIND);\n}\n\nTEST_F(AsyncUDPSocketTest, TestWritemNontrivialCmsgs) {\n  folly::SocketAddress addr(\"127.0.0.1\", 10001);\n  auto netOpsDispatcher =\n      std::make_shared<NiceMock<folly::netops::test::MockDispatcher>>();\n  socket_->setOverrideNetOpsDispatcher(netOpsDispatcher);\n  std::vector<std::unique_ptr<folly::IOBuf>> bufs;\n  bufs.emplace_back(folly::IOBuf::copyBuffer(\"hey1\"));\n  bufs.emplace_back(folly::IOBuf::copyBuffer(\"hey2\"));\n#ifdef FOLLY_HAVE_MSG_ERRQUEUE\n  // empty\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    folly::SocketNontrivialCmsgMap expectedNontrivialCmsgs;\n    EXPECT_CALL(\n        *netOpsDispatcher,\n        sendmmsg(\n            _,\n            AllHaveCmsgsAndNontrivialCmsgs(\n                expectedCmsgs, expectedNontrivialCmsgs, bufs.size()),\n            _,\n            _));\n    socket_->writem(folly::range(&addr, &addr + 1), bufs.data(), bufs.size());\n  }\n  // set IP_TOS & SO_MARK\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    folly::SocketNontrivialCmsgMap expectedNontrivialCmsgs;\n    struct linger sl{\n        .l_onoff = 1,\n        .l_linger = 123,\n    };\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedNontrivialCmsgs[{SOL_SOCKET, SO_LINGER}] =\n        std::string(reinterpret_cast<const char*>(&sl), sizeof(sl));\n    socket_->setCmsgs(expectedCmsgs);\n    socket_->setNontrivialCmsgs(expectedNontrivialCmsgs);\n    EXPECT_CALL(\n        *netOpsDispatcher,\n        sendmmsg(\n            _,\n            AllHaveCmsgsAndNontrivialCmsgs(\n                expectedCmsgs, expectedNontrivialCmsgs, bufs.size()),\n            _,\n            _));\n    socket_->writem(folly::range(&addr, &addr + 1), bufs.data(), bufs.size());\n  }\n  // writemGSO\n  {\n    folly::SocketCmsgMap expectedCmsgs;\n    folly::SocketNontrivialCmsgMap expectedNontrivialCmsgs;\n    struct linger sl{\n        .l_onoff = 1,\n        .l_linger = 123,\n    };\n    expectedCmsgs[{IPPROTO_IP, IP_TOS}] = 456;\n    expectedCmsgs[{SOL_SOCKET, SO_MARK}] = 123;\n    expectedCmsgs[{SOL_UDP, UDP_SEGMENT}] = 1;\n    expectedNontrivialCmsgs[{SOL_SOCKET, SO_LINGER}] =\n        std::string(reinterpret_cast<const char*>(&sl), sizeof(sl));\n    EXPECT_CALL(\n        *netOpsDispatcher,\n        sendmmsg(\n            _,\n            AllHaveCmsgsAndNontrivialCmsgs(\n                expectedCmsgs, expectedNontrivialCmsgs, bufs.size()),\n            _,\n            _));\n    std::vector<folly::AsyncUDPSocket::WriteOptions> options{\n        {1, false}, {1, false}};\n    socket_->writemGSO(\n        folly::range(&addr, &addr + 1),\n        bufs.data(),\n        bufs.size(),\n        options.data());\n  }\n#endif // FOLLY_HAVE_MSG_ERRQUEUE\n  socket_->close();\n}\n"
  },
  {
    "path": "folly/io/async/test/AtomicNotificationQueueTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <functional>\n#include <utility>\n#include <vector>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventBaseAtomicNotificationQueue.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing namespace std;\n\ntemplate <typename Task>\nstruct AtomicNotificationQueueConsumer {\n  explicit AtomicNotificationQueueConsumer(vector<Task>& tasks)\n      : tasks(tasks) {}\n\n  void operator()(Task&& value) noexcept {\n    tasks.push_back(value);\n    if (fn) {\n      fn(std::move(value));\n    }\n  }\n\n  function<void(Task&&)> fn;\n  vector<Task>& tasks;\n};\n\nTEST(AtomicNotificationQueueTest, TryPutMessage) {\n  vector<int> data;\n  AtomicNotificationQueueConsumer<int> consumer{data};\n  EventBaseAtomicNotificationQueue<int, decltype(consumer)> queue{\n      std::move(consumer)};\n\n  constexpr uint32_t kMaxSize = 10;\n\n  for (auto i = 1; i <= 9; ++i) {\n    queue.putMessage(std::move(i));\n  }\n\n  EXPECT_TRUE(queue.tryPutMessage(10, kMaxSize));\n  EXPECT_EQ(queue.size(), 10);\n  EXPECT_FALSE(queue.tryPutMessage(11, kMaxSize));\n  EXPECT_EQ(queue.size(), 10);\n  queue.putMessage(11);\n  EXPECT_EQ(queue.size(), 11);\n  EXPECT_FALSE(queue.tryPutMessage(12, kMaxSize));\n\n  queue.drain();\n  EXPECT_TRUE(queue.tryPutMessage(0, kMaxSize));\n  EXPECT_EQ(queue.size(), 1);\n}\n\nTEST(AtomicNotificationQueueTest, DiscardDequeuedTasks) {\n  struct TaskWithExpiry {\n    int datum;\n    bool isExpired;\n  };\n\n  struct Consumer {\n    explicit Consumer(std::vector<int>& data) : data(data) {}\n    AtomicNotificationQueueTaskStatus operator()(\n        TaskWithExpiry&& task) noexcept {\n      if (task.isExpired) {\n        return AtomicNotificationQueueTaskStatus::DISCARD;\n      }\n      data.push_back(task.datum);\n      return AtomicNotificationQueueTaskStatus::CONSUMED;\n    }\n    vector<int>& data;\n  };\n  vector<int> data;\n  Consumer consumer{data};\n\n  EventBaseAtomicNotificationQueue<TaskWithExpiry, Consumer> queue{\n      std::move(consumer)};\n  queue.setMaxReadAtOnce(10);\n\n  vector<TaskWithExpiry> tasks = {\n      {0, false},\n      {1, true},\n      {2, true},\n      {3, false},\n      {4, false},\n      {5, false},\n      {6, false},\n      {7, true},\n      {8, false},\n      {9, false},\n      {10, false},\n      {11, false},\n      {12, true},\n      {13, false},\n      {14, true},\n      {15, false},\n  };\n\n  EventBase eventBase;\n  queue.startConsuming(&eventBase);\n\n  for (auto& t : tasks) {\n    queue.putMessage(t);\n  }\n\n  eventBase.loopOnce();\n\n  vector<int> expectedMessages = {0, 3, 4, 5, 6, 8, 9, 10, 11, 13};\n  EXPECT_EQ(data.size(), expectedMessages.size());\n  for (unsigned i = 0; i < expectedMessages.size(); ++i) {\n    EXPECT_EQ(data.at(i), expectedMessages[i]);\n  }\n\n  data.clear();\n  eventBase.loopOnce();\n\n  EXPECT_EQ(data.size(), 1);\n  EXPECT_EQ(data.at(0), 15);\n}\n\nTEST(AtomicNotificationQueueTest, PutMessage) {\n  struct Data {\n    int datum;\n    bool isExpired;\n\n    explicit Data(int datum, bool isExpired)\n        : datum(datum), isExpired(isExpired) {}\n\n    bool operator==(const Data& data) const {\n      return datum == data.datum && isExpired == data.isExpired;\n    }\n  };\n\n  struct Consumer {\n    explicit Consumer(vector<Data>& data) : data(data) {}\n    void operator()(Data&& task) noexcept { data.push_back(task); }\n    vector<Data>& data;\n  };\n\n  vector<Data>\n      expected =\n          {Data(10, false), Data(20, true), Data(-8, true), Data(0, false)},\n      actual;\n  Consumer consumer{actual};\n\n  EventBaseAtomicNotificationQueue<Data, decltype(consumer)> queue{\n      std::move(consumer)};\n  queue.setMaxReadAtOnce(0);\n\n  EventBase eventBase;\n  queue.startConsuming(&eventBase);\n\n  for (auto& t : expected) {\n    queue.putMessage(t.datum, t.isExpired);\n  }\n\n  eventBase.loopOnce();\n\n  EXPECT_EQ(expected.size(), actual.size());\n  for (unsigned i = 0; i < expected.size(); ++i) {\n    EXPECT_EQ(expected[i], actual[i]);\n  }\n}\n\nTEST(AtomicNotificationQueueTest, ConsumeStop) {\n  struct Consumer {\n    size_t* consumed;\n    auto operator()(bool&& stop) noexcept {\n      ++*consumed;\n      return stop\n          ? AtomicNotificationQueueTaskStatus::CONSUMED_STOP\n          : AtomicNotificationQueueTaskStatus::CONSUMED;\n    }\n  };\n\n  size_t consumed = 0;\n  Consumer consumer{&consumed};\n\n  EventBaseAtomicNotificationQueue<bool, decltype(consumer)> queue{\n      std::move(consumer)};\n  queue.setMaxReadAtOnce(3);\n\n  EventBase eventBase;\n  queue.startConsuming(&eventBase);\n\n  for (auto t : {false, true, false, false, false, false}) {\n    queue.putMessage(t);\n  }\n\n  ASSERT_EQ(consumed, 0);\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  // Second message stopped consuming.\n  ASSERT_EQ(std::exchange(consumed, 0), 2);\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  // setMaxReadAtOnce() still honored.\n  ASSERT_EQ(std::exchange(consumed, 0), 3);\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  ASSERT_EQ(std::exchange(consumed, 0), 1);\n\n  for (auto t : {true, true, true}) {\n    queue.putMessage(t);\n  }\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  ASSERT_EQ(std::exchange(consumed, 0), 1);\n  // Local queue is still non-empty, add a message to the atomic queue.\n  queue.putMessage(true);\n\n  // All messages, including the one just added, should be consumed.\n  for (size_t i = 0; i < 3; ++i) {\n    eventBase.loopOnce(EVLOOP_NONBLOCK);\n    ASSERT_EQ(std::exchange(consumed, 0), 1);\n  }\n}\n\n// drive() should not process messages that were put during its execution.\nTEST(AtomicNotificationQueueTest, DriveSnapshot) {\n  struct Consumer {\n    size_t* consumed;\n    auto operator()(folly::Func f) noexcept {\n      f();\n      ++*consumed;\n      return AtomicNotificationQueueTaskStatus::CONSUMED;\n    }\n  };\n\n  size_t consumed = 0;\n  Consumer consumer{&consumed};\n  EventBaseAtomicNotificationQueue<folly::Func, decltype(consumer)> queue{\n      std::move(consumer)};\n\n  EventBase eventBase;\n  queue.startConsuming(&eventBase);\n\n  queue.setMaxReadAtOnce(0);\n  queue.putMessage([&] { queue.putMessage([] {}); });\n  ASSERT_EQ(consumed, 0);\n  // Atomic queue was empty when the second putMessage happened(), so the second\n  // message is not processed.\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  ASSERT_EQ(std::exchange(consumed, 0), 1);\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  ASSERT_EQ(std::exchange(consumed, 0), 1);\n\n  bool lastRan = false;\n  queue.putMessage([&] {});\n  queue.putMessage([&] { queue.putMessage([&] { lastRan = true; }); });\n\n  // Leave the second message in the local queue.\n  queue.setMaxReadAtOnce(1);\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  ASSERT_EQ(std::exchange(consumed, 0), 1);\n\n  // This will be in the atomic queue.\n  queue.putMessage([] {});\n  queue.setMaxReadAtOnce(0);\n  // We'll consume the local queue and part of the atomic queue, but stop at the\n  // message that was put in the callback.\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  ASSERT_EQ(std::exchange(consumed, 0), 2);\n\n  EXPECT_FALSE(lastRan);\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  ASSERT_EQ(std::exchange(consumed, 0), 1);\n  EXPECT_TRUE(lastRan);\n}\n"
  },
  {
    "path": "folly/io/async/test/BUCK",
    "content": "load(\"@fbcode_macros//build_defs:build_file_migration.bzl\", \"fbcode_target\", \"non_fbcode_target\")\nload(\"@fbcode_macros//build_defs:cpp_benchmark.bzl\", \"cpp_benchmark\")\nload(\"@fbcode_macros//build_defs:cpp_binary.bzl\", \"cpp_binary\")\nload(\"@fbcode_macros//build_defs:cpp_library.bzl\", \"cpp_library\")\nload(\"@fbcode_macros//build_defs:cpp_unittest.bzl\", \"cpp_unittest\")\nload(\"@fbsource//tools/build_defs/dirsync:fb_dirsync_cpp_library.bzl\", \"fb_dirsync_cpp_library\")\nload(\"../../../defs.bzl\", \"folly_xplat_cxx_binary\", \"folly_xplat_cxx_library\", \"folly_xplat_cxx_test\")\nload(\"../../../io/async/test/certs/defs.bzl\", \"alias_pem\")\n\noncall(\"fbcode_entropy_wardens_folly\")\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_library,\n    name = \"test_ssl_server\",\n    srcs = [\n        \"TestSSLServer.cpp\",\n    ],\n    raw_headers = [\n        \"TestSSLServer.h\",\n    ],\n    resources = {\n        \"certs/ca-cert.pem\": \"//xplat/folly/io/async/test/certs:ca-cert.pem\",\n        \"certs/client_ca_cert.pem\": \"//xplat/folly/io/async/test/certs:client_ca_cert.pem\",\n        \"certs/client_cert.pem\": \"//xplat/folly/io/async/test/certs:client_cert.pem\",\n        \"certs/client_chain.pem\": \"//xplat/folly/io/async/test/certs:client_chain.pem\",\n        \"certs/client_key.pem\": \"//xplat/folly/io/async/test/certs:client_key.pem\",\n        \"certs/tests-cert.pem\": \"//xplat/folly/io/async/test/certs:tests-cert.pem\",\n        \"certs/tests-key.pem\": \"//xplat/folly/io/async/test/certs:tests-key.pem\",\n    },\n    deps = [\n        \"fbsource//xplat/folly/portability:openssl\",\n    ],\n    exported_deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:sockets\",\n        \"fbsource//xplat/folly/portability:unistd\",\n        \":callback_state_enum\",\n        \"//xplat/folly:network_address\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_socket\",\n        \"//xplat/folly/io/async:async_ssl_socket\",\n        \"//xplat/folly/io/async:async_transport\",\n        \"//xplat/folly/io/async:server_socket\",\n        \"//xplat/folly/io/async/ssl:ssl_errors\",\n        \"//xplat/folly/testing:test_util\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"tfo_util\",\n    srcs = [\n        \"TFOUtil.cpp\",\n    ],\n    headers = [\n        \"TFOUtil.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly/detail:socket_fast_open\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_signal_handler_test_lib\",\n    headers = [\n        \"AsyncSignalHandlerTestLib.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":util\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_signal_handler\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_pipe_test\",\n    srcs = [\"AsyncPipeTest.cpp\"],\n    raw_headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_pipe\",\n        \"//xplat/folly/io/async:async_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_ssl_socket_test_lib\",\n    headers = [\n        \"AsyncSSLSocketTest.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":test_ssl_server\",\n        \"//folly:exception_wrapper\",\n        \"//folly:network_address\",\n        \"//folly/fibers:fiber_manager_map\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_ssl_socket\",\n        \"//folly/io/async:async_transport\",\n        \"//folly/io/async:server_socket\",\n        \"//folly/io/async/ssl:ssl_errors\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:pthread\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:string\",\n        \"//folly/portability:unistd\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_ssl_socket_test\",\n    srcs = [\n        \"AsyncSSLSocketTest.cpp\",\n        \"AsyncSSLSocketTest2.cpp\",\n    ],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    resources = {\n        \"certs/ca-cert.pem\": \"//xplat/folly/io/async/test/certs:ca-cert.pem\",\n        \"certs/client_ca_cert.pem\": \"//xplat/folly/io/async/test/certs:client_ca_cert.pem\",\n        \"certs/client_cert.pem\": \"//xplat/folly/io/async/test/certs:client_cert.pem\",\n        \"certs/client_key.pem\": \"//xplat/folly/io/async/test/certs:client_key.pem\",\n        \"certs/tests-cert.pem\": \"//xplat/folly/io/async/test/certs:tests-cert.pem\",\n        \"certs/tests-key.pem\": \"//xplat/folly/io/async/test/certs:tests-key.pem\",\n    },\n    deps = [\n        \"fbsource//xplat/folly/futures:core\",\n        \"fbsource//xplat/folly/init:init\",\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:socket_option_map\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:pthread\",\n        \"fbsource//xplat/folly/portability:sockets\",\n        \"fbsource//xplat/folly/portability:string\",\n        \"fbsource//xplat/folly/portability:unistd\",\n        \":async_ssl_socket_test_lib\",\n        \":blocking_socket\",\n        \":mocks\",\n        \":test_ssl_server\",\n        \":tfo_util\",\n        \"//xplat/folly:exception_wrapper\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_pipe\",\n        \"//xplat/folly/io/async:async_socket\",\n        \"//xplat/folly/io/async:async_socket_exception\",\n        \"//xplat/folly/io/async:async_ssl_socket\",\n        \"//xplat/folly/io/async:async_transport\",\n        \"//xplat/folly/io/async:event_base_thread\",\n        \"//xplat/folly/io/async:scoped_event_base_thread\",\n        \"//xplat/folly/io/async:ssl_context\",\n        \"//xplat/folly/io/async:ssl_options\",\n        \"//xplat/folly/io/async/ssl:basic_transport_certificate\",\n        \"//xplat/folly/io/async/ssl:openssl_transport_certificate\",\n        \"//xplat/folly/io/async/ssl:ssl_errors\",\n        \"//xplat/folly/net:net_ops\",\n        \"//xplat/folly/net:network_socket\",\n        \"//xplat/folly/net/test:mock_net_ops_dispatcher\",\n        \"//xplat/third-party/openssl:crypto\",\n        \"//xplat/third-party/openssl:ssl\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_ssl_socket_write_test\",\n    srcs = [\n        \"AsyncSSLSocketWriteTest.cpp\",\n    ],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    deps = [\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_socket\",\n        \"//xplat/folly/io/async:async_ssl_socket\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_socket_exception_test\",\n    srcs = [\n        \"AsyncSocketExceptionTest.cpp\",\n    ],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly/io/async:async_socket_exception\",\n        \"//xplat/folly/io/async:ssl_context\",\n        \"//xplat/folly/io/async/ssl:ssl_errors\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_socket_observer_test\",\n    srcs = [\n        \"AsyncSocketObserverTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \":async_socket_test_lib\",\n        \":mocks\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"async_socket_test_lib\",\n    headers = [\n        \"AsyncSocketTest.h\",\n        \"AsyncSocketTest2.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":blocking_socket\",\n        \":callback_state_enum\",\n        \":conn_callback\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:server_socket\",\n        \"//folly/net:net_ops\",\n        \"//folly/net:network_socket\",\n        \"//folly/portability:sockets\",\n        \"//folly/synchronization:rw_spin_lock\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_socket_test\",\n    srcs = [\n        \"AsyncSocketTest.cpp\",\n        # Disabled until we have folly/test:socket_address_test_helper\n        \"AsyncSocketTest2.cpp\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:socket_option_map\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:sockets\",\n        \"fbsource//xplat/folly/portability:unistd\",\n        \"fbsource//xplat/folly/synchronization:baton\",\n        \"fbsource//xplat/folly/synchronization:rw_spin_lock\",\n        \":async_socket_test_lib\",\n        \":blocking_socket\",\n        \":conn_callback\",\n        \":mocks\",\n        \":tfo_util\",\n        \":util\",\n        \"//xplat/folly:exception_wrapper\",\n        \"//xplat/folly:network_address\",\n        \"//xplat/folly:random\",\n        \"//xplat/folly:test_socket_address_test_helper\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_socket\",\n        \"//xplat/folly/io/async:scoped_event_base_thread\",\n        \"//xplat/folly/io/async:server_socket\",\n        \"//xplat/folly/net:net_ops\",\n        \"//xplat/folly/net:network_socket\",\n        \"//xplat/folly/net/test:mock_net_ops_dispatcher\",\n        \"//xplat/folly/net/test:mock_tcpinfo_dispatcher\",\n        \"//xplat/folly/testing:test_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_transport_test\",\n    srcs = [\n        \"AsyncTransportTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \":mocks\",\n        \"//xplat/folly/io/async:async_socket\",\n        \"//xplat/folly/io/async:async_transport\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"event_handler_test\",\n    srcs = [\n        \"EventHandlerTest.cpp\",\n    ],\n    deps = [\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:sockets\",\n        \"//xplat/folly:mpmc_queue\",\n        \"//xplat/folly:scope_guard\",\n        \"//xplat/folly/io/async:async_base\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_timeout_test\",\n    srcs = [\"AsyncTimeoutTest.cpp\"],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly/io/async:async_base\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_udp_socket_test\",\n    srcs = [\n        # \"AsyncUDPSocketTest.cpp\",\n    ],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/io:socket_option_map\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:sockets\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:network_address\",\n        \"//xplat/folly:string\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_udp_server_socket\",\n        \"//xplat/folly/io/async:async_udp_socket\",\n        \"//xplat/folly/testing:test_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_udp_socket_gso_gro_test\",\n    srcs = [\n        # \"AsyncUDPSocketGSOGROTest.cpp\",\n    ],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:network_address\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_udp_server_socket\",\n        \"//xplat/folly/io/async:async_udp_socket\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"async_udp_socket_sendmmsg_test\",\n    srcs = [\"AsyncUDPSocketSendmmsgTest.cpp\"],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/io:iobuf\",\n        \"fbsource//xplat/folly/portability:gmock\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:conv\",\n        \"//xplat/folly:network_address\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/io/async:async_udp_server_socket\",\n        \"//xplat/folly/io/async:async_udp_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"blocking_socket\",\n    headers = [\"BlockingSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:optional\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_ssl_socket\",\n        \"//folly/io/async:ssl_context\",\n        \"//folly/net:network_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"callback_state_enum\",\n    headers = [\n        \"CallbackStateEnum.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n)\n\nfb_dirsync_cpp_library(\n    name = \"conn_callback\",\n    headers = [\n        \"ConnCallback.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":callback_state_enum\",\n        \"//folly/io/async:async_socket\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"delayed_destruction_base_test\",\n    srcs = [\"DelayedDestructionBaseTest.cpp\"],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly/io/async:delayed_destruction\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"delayed_destruction_test\",\n    srcs = [\"DelayedDestructionTest.cpp\"],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//third-party/glog:glog\",\n        \"//xplat/folly/io/async:delayed_destruction\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"destructor_check_test\",\n    srcs = [\"DestructorCheckTest.cpp\"],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/io/async:destructor_check\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_binary,\n    name = \"event_base_benchmark\",\n    srcs = [\"EventBaseBenchmark.cpp\"],\n    allocator = \"malloc\",\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gflags\",\n        \"//xplat/folly:benchmark\",\n        \"//xplat/folly/io/async:async_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"event_base_test_lib\",\n    headers = [\n        \"EventBaseTestLib.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \":util\",\n        \"//folly:math\",\n        \"//folly:memory\",\n        \"//folly:scope_guard\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:stdlib\",\n        \"//folly/portability:unistd\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/system:thread_id\",\n        \"//folly/system:thread_name\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"event_base_test\",\n    srcs = [\"EventBaseTest.cpp\"],\n    allocator = \"malloc\",\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    supports_static_listing = False,\n    deps = [\n        \"fbsource//xplat/folly/init:init\",\n        \"fbsource//xplat/folly/portability:gtest\",\n        \":async_signal_handler_test_lib\",\n        \":event_base_test_lib\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"event_base_local_test\",\n    srcs = [\"EventBaseLocalTest.cpp\"],\n    allocator = \"malloc\",\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/synchronization:baton\",\n        \"//xplat/folly/io/async:async_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"event_base_thread_test\",\n    srcs = [\"EventBaseThreadTest.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/io/async:event_base_manager\",\n        \"//folly/io/async:event_base_thread\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n        \"//folly/system:thread_name\",\n    ],\n)\n\n# Skipped because it's slow\n# folly_xplat_cxx_test(\n#     name = \"hhwheel_timer_slow_test\",\n#     srcs = [\"HHWheelTimerSlowTests.cpp\"],\n#     contacts = [\"oncall+thrift@xmail.facebook.com\"],\n#     raw_headers = [],\n#     labels = [\n#         \"extended\",\n#     ],\n#     deps = [\n#         \":util\",\n#         \"//xplat/folly/portability:gtest\",\n#         \"//xplat/folly:random\",\n#         \"//xplat/folly/io/async:async_base\",\n#     ],\n# )\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"hhwheel_timer_test\",\n    srcs = [\"HHWheelTimerTest.cpp\"],\n    contacts = [\"oncall+thrift@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \":util\",\n        \"//xplat/folly/io/async:async_base\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"mocks\",\n    headers = [\n        \"MockAsyncSSLSocket.h\",\n        \"MockAsyncSocket.h\",\n        \"MockAsyncSocketLegacyObserver.h\",\n        \"MockAsyncSocketObserver.h\",\n        \"MockAsyncTransport.h\",\n        \"MockAsyncUDPSocket.h\",\n        \"MockTimeoutManager.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:memory\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_socket_exception\",\n        \"//folly/io/async:async_ssl_socket\",\n        \"//folly/io/async:async_transport\",\n        \"//folly/io/async:async_udp_socket\",\n        \"//folly/portability:gmock\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"mock_server_socket\",\n    headers = [\"MockAsyncServerSocket.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:server_socket\",\n        \"//folly/portability:gmock\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"notification_queue_test\",\n    srcs = [\"NotificationQueueTest.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"atomic_notification_queue_test\",\n    srcs = [\"AtomicNotificationQueueTest.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"notification_queue_benchmark\",\n    srcs = [\"NotificationQueueBenchmark.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_binary,\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/io/async:async_base\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"request_context_test\",\n    srcs = [\"RequestContextTest.cpp\"],\n    raw_headers = [\"RequestContextHelper.h\"],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"//xplat/folly:memory\",\n        \"//xplat/folly/io/async:async_base\",\n        \"//xplat/folly/synchronization/test:barrier\",\n        \"//xplat/folly/system:thread_name\",\n    ],\n    exported_deps = [\"//xplat/folly/io/async:request_context\"],\n)\n\nfb_dirsync_cpp_library(\n    name = \"scoped_bound_port\",\n    srcs = [\"ScopedBoundPort.cpp\"],\n    headers = [\"ScopedBoundPort.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"//folly:memory\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/io/async:server_socket\",\n    ],\n    exported_deps = [\n        \"//folly:network_address\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"scoped_event_base_thread_test\",\n    srcs = [\"ScopedEventBaseThreadTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly:optional\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:event_base_manager\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n        \"//folly/system:thread_name\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"socket_client\",\n    srcs = [\"SocketClient.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_binary,\n    deps = [\n        \":blocking_socket\",\n        \"//folly:exception_wrapper\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"ssl_session_test\",\n    srcs = [\n        \"SSLSessionTest.cpp\",\n    ],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    raw_headers = [],\n    resources = {\n        \"certs/ca-cert.pem\": \"//xplat/folly/io/async/test/certs:ca-cert.pem\",\n        \"certs/tests-cert.pem\": \"//xplat/folly/io/async/test/certs:tests-cert.pem\",\n        \"certs/tests-key.pem\": \"//xplat/folly/io/async/test/certs:tests-key.pem\",\n    },\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \"fbsource//xplat/folly/portability:sockets\",\n        \":async_ssl_socket_test_lib\",\n        \"//xplat/folly/net:net_ops\",\n        \"//xplat/folly/net:network_socket\",\n        \"//xplat/folly/ssl:ssl_session\",\n        \"//xplat/folly/ssl/detail:openssl_session\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"time_util_test\",\n    srcs = [\"TimeUtilTest.cpp\"],\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \":util\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"ssl_context_test\",\n    srcs = [\n        \"SSLContextTest.cpp\",\n    ],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    raw_headers = [],\n    resources = {\n        \"certs/client_chain.pem\": \"//xplat/folly/io/async/test/certs:client_chain.pem\",\n        \"certs/client_key.pem\": \"//xplat/folly/io/async/test/certs:client_key.pem\",\n        \"certs/clienti_key.pem\": \"//xplat/folly/io/async/test/certs:clienti_key.pem\",\n        \"certs/tests-cert.pem\": \"//xplat/folly/io/async/test/certs:tests-cert.pem\",\n        \"certs/tests-key.pem\": \"//xplat/folly/io/async/test/certs:tests-key.pem\",\n    },\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \"fbsource//xplat/folly/portability:openssl\",\n        \":ssl_util\",\n        \"//xplat/folly:file_util\",\n        \"//xplat/folly/io/async:ssl_context\",\n        \"//xplat/folly/ssl:openssl_cert_utils\",\n        \"//xplat/folly/ssl:openssl_key_utils\",\n        \"//xplat/folly/ssl:openssl_ptr_types\",\n        \"//xplat/folly/testing:test_util\",\n    ],\n)\n\nnon_fbcode_target(\n    _kind = folly_xplat_cxx_test,\n    name = \"ssl_options_test\",\n    srcs = [\"SSLOptionsTest.cpp\"],\n    contacts = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    raw_headers = [],\n    deps = [\n        \"fbsource//xplat/folly/portability:gtest\",\n        \":ssl_util\",\n        \"//xplat/folly/io/async:ssl_context\",\n        \"//xplat/folly/io/async:ssl_options\",\n        \"//xplat/folly/ssl:openssl_ptr_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"ssl_util\",\n    srcs = [\n        \"SSLUtil.cpp\",\n    ],\n    headers = [\n        \"SSLUtil.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly/ssl:openssl_ptr_types\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"util\",\n    srcs = [\n        \"SocketPair.cpp\",\n        \"TimeUtil.cpp\",\n    ],\n    headers = [\n        \"SocketPair.h\",\n        \"TimeUtil.h\",\n        \"UndelayedDestruction.h\",\n        \"Util.h\",\n    ],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:conv\",\n        \"//folly:portability\",\n        \"//folly:scope_guard\",\n        \"//folly:string\",\n        \"//folly/net:net_ops\",\n        \"//folly/portability:fcntl\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:unistd\",\n        \"//folly/system:thread_id\",\n    ],\n    exported_deps = [\n        \"//folly/net:network_socket\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sys_types\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"write_chain_async_transport_wrapper_test\",\n    srcs = [\"WriteChainAsyncTransportWrapperTest.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/io/async:async_transport\",\n        \"//folly/io/async:decorated_async_transport_wrapper\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"write_flags_test\",\n    srcs = [\"WriteFlagsTest.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \"//folly/io/async:async_transport\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"zero_copy_lib\",\n    srcs = [\"ZeroCopy.cpp\"],\n    headers = [\"ZeroCopy.h\"],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_library,\n    exported_deps = [\n        \"//folly:exception_wrapper\",\n        \"//folly:network_address\",\n        \"//folly/io:iobuf\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:server_socket\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"zero_copy_test\",\n    srcs = [\"ZeroCopyTest.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_test,\n    deps = [\n        \":zero_copy_lib\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfb_dirsync_cpp_library(\n    name = \"zero_copy_benchmark\",\n    srcs = [\"ZeroCopyBenchmark.cpp\"],\n    headers = [],\n    use_raw_headers = True,\n    xplat_impl = folly_xplat_cxx_binary,\n    deps = [\n        \":zero_copy_lib\",\n        \"//folly:benchmark\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\n# !!!! fbcode/folly/io/async/test/TARGETS was merged into this file, see https://fburl.com/workplace/xl8l9yuo for more info !!!!\n\nfbcode_target(\n    _kind = alias_pem,\n    pems = [\n        \"tests-cert.pem\",\n        \"tests-key.pem\",\n        \"ca-cert.pem\",\n        \"client_cert.pem\",\n        \"client_key.pem\",\n        \"client_ca_cert.pem\",\n        \"client_chain.pem\",\n        \"clienti_key.pem\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"test_ssl_server\",\n    srcs = [\n        \"TestSSLServer.cpp\",\n    ],\n    headers = [\n        \"TestSSLServer.h\",\n    ],\n    resources = {\n        \"certs/ca-cert.pem\": \":ca-cert.pem\",\n        \"certs/client_ca_cert.pem\": \":client_ca_cert.pem\",\n        \"certs/client_cert.pem\": \":client_cert.pem\",\n        \"certs/client_chain.pem\": \":client_chain.pem\",\n        \"certs/client_key.pem\": \":client_key.pem\",\n        \"certs/tests-cert.pem\": \":tests-cert.pem\",\n        \"certs/tests-key.pem\": \":tests-key.pem\",\n    },\n    deps = [\n        \"//folly/portability:openssl\",\n    ],\n    exported_deps = [\n        \":callback_state_enum\",\n        \"//folly:network_address\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_ssl_socket\",\n        \"//folly/io/async:async_transport\",\n        \"//folly/io/async:server_socket\",\n        \"//folly/io/async/ssl:ssl_errors\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:unistd\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_pipe_test\",\n    srcs = [\n        \"AsyncPipeTest.cpp\",\n    ],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_pipe\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_ssl_socket_test\",\n    srcs = [\n        \"AsyncSSLSocketTest.cpp\",\n        \"AsyncSSLSocketTest2.cpp\",\n    ],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    extract_helper_lib = False,\n    labels = [\"oss-broken\"],\n    resources = {\n        \"certs/ca-cert.pem\": \":ca-cert.pem\",\n        \"certs/client_ca_cert.pem\": \":client_ca_cert.pem\",\n        \"certs/client_cert.pem\": \":client_cert.pem\",\n        \"certs/client_key.pem\": \":client_key.pem\",\n        \"certs/tests-cert.pem\": \":tests-cert.pem\",\n        \"certs/tests-key.pem\": \":tests-key.pem\",\n    },\n    supports_static_listing = False,\n    deps = [\n        \":async_ssl_socket_test_lib\",  # @manual\n        \":blocking_socket\",\n        \":mocks\",\n        \":test_ssl_server\",\n        \":tfo_util\",\n        \"//folly:exception_wrapper\",\n        \"//folly:network_address\",\n        \"//folly:string\",\n        \"//folly/fibers:fiber_manager_map\",\n        \"//folly/futures:core\",\n        \"//folly/init:init\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_pipe\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_ssl_socket\",\n        \"//folly/io/async:async_transport\",\n        \"//folly/io/async:event_base_thread\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/io/async:server_socket\",\n        \"//folly/io/async:ssl_context\",\n        \"//folly/io/async:ssl_options\",\n        \"//folly/io/async/ssl:basic_transport_certificate\",\n        \"//folly/io/async/ssl:openssl_transport_certificate\",\n        \"//folly/io/async/ssl:ssl_errors\",\n        \"//folly/net:net_ops\",\n        \"//folly/net:network_socket\",\n        \"//folly/net/test:mock_net_ops_dispatcher\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:openssl\",\n        \"//folly/portability:pthread\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:string\",\n        \"//folly/portability:unistd\",\n        \"//folly/testing:test_util\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"dl\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_ssl_socket_write_test\",\n    srcs = [\n        \"AsyncSSLSocketWriteTest.cpp\",\n    ],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/io:iobuf\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_ssl_socket\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_socket_exception_test\",\n    srcs = [\n        \"AsyncSocketExceptionTest.cpp\",\n    ],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:conv\",\n        \"//folly/io/async:async_socket_exception\",\n        \"//folly/io/async:ssl_context\",\n        \"//folly/io/async/ssl:ssl_errors\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:openssl\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_socket_observer_test\",\n    srcs = [\n        \"AsyncSocketObserverTest.cpp\",\n    ],\n    labels = [\"case-isolation-failure\"],\n    supports_static_listing = False,\n    deps = [\n        \":async_socket_test_lib\",\n        \":mocks\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_socket_test\",\n    srcs = [\n        \"AsyncSocketTest.cpp\",\n        \"AsyncSocketTest2.cpp\",\n    ],\n    labels = [\"case-isolation-failure\"],\n    supports_static_listing = False,\n    deps = [\n        \":async_socket_test_lib\",  # @manual\n        \":blocking_socket\",\n        \":callback_state_enum\",\n        \":conn_callback\",\n        \":mocks\",\n        \":tfo_util\",\n        \":util\",\n        \"//folly:exception_wrapper\",\n        \"//folly:network_address\",\n        \"//folly:random\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/io/async:server_socket\",\n        \"//folly/net:net_ops\",\n        \"//folly/net:network_socket\",\n        \"//folly/net/test:mock_net_ops_dispatcher\",\n        \"//folly/net/test:mock_tcpinfo_dispatcher\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:unistd\",\n        \"//folly/synchronization:baton\",\n        \"//folly/synchronization:rw_spin_lock\",\n        \"//folly/test:socket_address_test_helper\",\n        \"//folly/testing:test_util\",\n    ],\n    external_deps = [\n        (\"glibc\", None, \"rt\"),\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_transport_test\",\n    srcs = [\n        \"AsyncTransportTest.cpp\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \":mocks\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_transport\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"event_handler_test\",\n    srcs = [\n        \"EventHandlerTest.cpp\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:mpmc_queue\",\n        \"//folly:scope_guard\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sockets\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_timeout_test\",\n    srcs = [\"AsyncTimeoutTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_udp_socket_test\",\n    srcs = [\"AsyncUDPSocketTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    labels = [\"heavyweight\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly:conv\",\n        \"//folly:network_address\",\n        \"//folly:string\",\n        \"//folly/io:iobuf\",\n        \"//folly/io:socket_option_map\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_udp_server_socket\",\n        \"//folly/io/async:async_udp_socket\",\n        \"//folly/net/test:mock_net_ops_dispatcher\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sockets\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_udp_socket_gso_gro_test\",\n    srcs = [\"AsyncUDPSocketGSOGROTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"//folly:conv\",\n        \"//folly:network_address\",\n        \"//folly/io:iobuf\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_udp_server_socket\",\n        \"//folly/io/async:async_udp_socket\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_udp_socket_sendmmsg_test\",\n    srcs = [\"AsyncUDPSocketSendmmsgTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"//folly:conv\",\n        \"//folly:network_address\",\n        \"//folly/io:iobuf\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_udp_server_socket\",\n        \"//folly/io/async:async_udp_socket\",\n        \"//folly/portability:gmock\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"decorated_async_transport_wrapper_test\",\n    srcs = [\"DecoratedAsyncTransportWrapperTest.cpp\"],\n    deps = [\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:async_transport\",\n        \"//folly/io/async:decorated_async_transport_wrapper\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"delayed_destruction_base_test\",\n    srcs = [\"DelayedDestructionBaseTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"//folly/io/async:delayed_destruction\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"delayed_destruction_test\",\n    srcs = [\"DelayedDestructionTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/io/async:delayed_destruction\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"destructor_check_test\",\n    srcs = [\"DestructorCheckTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"//folly:memory\",\n        \"//folly/io/async:destructor_check\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"event_base_benchmark\",\n    srcs = [\"EventBaseBenchmark.cpp\"],\n    headers = [],\n    allocator = \"malloc\",\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"event_base_test\",\n    srcs = [\"EventBaseTest.cpp\"],\n    headers = [],\n    allocator = \"malloc\",\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    labels = [\n        \"heavyweight\",\n        \"load-sensitive-timing-test\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \":async_signal_handler_test_lib\",\n        \":event_base_test_lib\",\n        \"//folly/init:init\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"event_base_local_test\",\n    srcs = [\"EventBaseLocalTest.cpp\"],\n    headers = [],\n    allocator = \"malloc\",\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"hhwheel_timer_slow_test\",\n    srcs = [\"HHWheelTimerSlowTests.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    labels = [\n        \"extended\",\n        \"load-sensitive-timing-test\",\n    ],\n    deps = [\n        \":util\",\n        \"//folly:random\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"hhwheel_timer_test\",\n    srcs = [\"HHWheelTimerTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+thrift@xmail.facebook.com\"],\n    labels = [\"slow\"],\n    deps = [\n        \":util\",\n        \"//folly/io/async:async_base\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:unistd\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_benchmark,\n    name = \"request_context_benchmark\",\n    srcs = [\"RequestContextBenchmark.cpp\"],\n    headers = [\"RequestContextHelper.h\"],\n    deps = [\n        \"//folly:conv\",\n        \"//folly/container:array\",\n        \"//folly/io/async:request_context\",\n        \"//folly/portability:gflags\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/synchronization/test:barrier\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"request_context_test\",\n    srcs = [\"RequestContextTest.cpp\"],\n    headers = [\"RequestContextHelper.h\"],\n    deps = [\n        \"fbsource//third-party/fmt:fmt\",\n        \"//folly:memory\",\n        \"//folly:singleton\",\n        \"//folly/container:enumerate\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:request_context\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:relaxed_atomic\",\n        \"//folly/synchronization/test:barrier\",\n        \"//folly/system:thread_name\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"terminate_cancellation_token_test\",\n    srcs = [\"TerminateCancellationTokenTest.cpp\"],\n    deps = [\n        \"//folly/io/async:terminate_cancellation_token\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ssl_session_test\",\n    srcs = [\"SSLSessionTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    resources = {\n        \"certs/ca-cert.pem\": \":ca-cert.pem\",\n        \"certs/tests-cert.pem\": \":tests-cert.pem\",\n        \"certs/tests-key.pem\": \":tests-key.pem\",\n    },\n    deps = [\n        \":async_ssl_socket_test_lib\",\n        \"//folly/net:net_ops\",\n        \"//folly/net:network_socket\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:openssl\",\n        \"//folly/portability:sockets\",\n        \"//folly/ssl:ssl_session\",\n        \"//folly/ssl/detail:openssl_session\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ssl_context_test\",\n    srcs = [\"SSLContextTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    resources = {\n        \"certs/client_chain.pem\": \":client_chain.pem\",\n        \"certs/client_key.pem\": \":client_key.pem\",\n        \"certs/clienti_key.pem\": \":clienti_key.pem\",\n        \"certs/tests-cert.pem\": \":tests-cert.pem\",\n        \"certs/tests-key.pem\": \":tests-key.pem\",\n    },\n    deps = [\n        \":ssl_util\",\n        \"//folly:file_util\",\n        \"//folly/io/async:ssl_context\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:openssl\",\n        \"//folly/ssl:openssl_cert_utils\",\n        \"//folly/ssl:openssl_key_utils\",\n        \"//folly/ssl:openssl_ptr_types\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ssl_context_regression_test\",\n    srcs = [\"SSLContextRegressionTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    deps = [\n        \"//folly:file_util\",\n        \"//folly/io/async:ssl_context\",\n        \"//folly/portability:gtest\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"ssl_options_test\",\n    srcs = [\"SSLOptionsTest.cpp\"],\n    headers = [],\n    emails = [\"oncall+secure_pipes@xmail.facebook.com\"],\n    deps = [\n        \":ssl_util\",\n        \"//folly/io/async:ssl_context\",\n        \"//folly/io/async:ssl_options\",\n        \"//folly/portability:gtest\",\n        \"//folly/ssl:openssl_ptr_types\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"timerfd_timeout_manager_test\",\n    srcs = [\"TimerFDTimeoutManagerTest.cpp\"],\n    headers = [],\n    labels = [\"load-sensitive-timing-test\"],\n    deps = [\n        \"//folly/io/async:timerfd\",\n        \"//folly/io/async/test:util\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"hhwheel_timer_high_res_test\",\n    srcs = [\"HHWheelTimerHighResTest.cpp\"],\n    headers = [],\n    labels = [\"load-sensitive-timing-test\"],\n    deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:timerfd\",\n        \"//folly/io/async/test:util\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"hhwheel_timer_high_res_benchmark\",\n    srcs = [\"HHWheelTimerHighResBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly/io/async:timerfd\",\n        \"//folly/io/async/test:util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_io_test\",\n    srcs = [\"AsyncIOTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/io/async:async_io\",\n        \"//folly/io/async/test:async_base_test_lib\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"async_io_uring_socket_test\",\n    srcs = [\"AsyncIoUringSocketTest.cpp\"],\n    labels = [\"heavyweight\"],\n    supports_static_listing = False,\n    deps = [\n        \":async_socket_test_lib\",\n        \"//folly:file_util\",\n        \"//folly:subprocess\",\n        \"//folly/executors:global_executor\",\n        \"//folly/futures:core\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_io_uring_socket\",\n        \"//folly/io/async:async_socket\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/io/async:io_uring_event\",\n        \"//folly/io/async:server_socket\",\n        \"//folly/portability:gtest\",\n        \"//folly/system:shell\",\n        \"//folly/test:socket_address_test_helper\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"epoll_backend_test\",\n    srcs = [\"EpollBackendTest.cpp\"],\n    labels = [\n        \"heavyweight\",\n        \"load-sensitive-timing-test\",\n    ],\n    owner = \"dmm@xmail.facebook.com\",\n    supports_static_listing = False,\n    deps = [\n        \"//folly/io/async:epoll_backend\",\n        \"//folly/io/async/test:async_signal_handler_test_lib\",\n        \"//folly/io/async/test:event_base_test_lib\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"poll_loop_hooks_test\",\n    srcs = [\"PollLoopHooksTest.cpp\"],\n    owner = \"dmm@xmail.facebook.com\",\n    deps = [\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:epoll_backend\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"io_benchmark\",\n    srcs = [\"IOBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:file_util\",\n        \"//folly/io/async:async_io\",\n        \"//folly/io/async:io_uring\",\n        \"//folly/io/async/test:async_base_test_lib\",\n        \"//folly/io/async/test:io_test_temp_file_util_lib\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"io_uring_backend_bench\",\n    srcs = [\"IoUringBackendBench.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:file_util\",\n        \"//folly/init:init\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:epoll_backend\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/io/async:scoped_event_base_thread\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"io_uring_backend_setup_test\",\n    srcs = [\"IoUringBackendSetupTest.cpp\"],\n    owner = \"kvigor@xmail.facebook.com\",\n    deps = [\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"io_uring_backend_test\",\n    srcs = [\"IoUringBackendTest.cpp\"],\n    headers = [],\n    labels = [\n        \"heavyweight\",\n        \"load-sensitive-timing-test\",\n        \"slow\",\n    ],\n    owner = \"dmm@xmail.facebook.com\",\n    supports_static_listing = False,\n    deps = [\n        \"//folly:file_util\",\n        \"//folly:function\",\n        \"//folly:string\",\n        \"//folly/init:init\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:async_udp_server_socket\",\n        \"//folly/io/async:async_udp_socket\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/io/async:io_uring_provided_buffer_ring\",\n        \"//folly/io/async/test:async_signal_handler_test_lib\",\n        \"//folly/io/async/test:event_base_test_lib\",\n        \"//folly/io/async/test:io_test_temp_file_util_lib\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"io_uring_event_base_local_test\",\n    srcs = [\"IoUringEventBaseLocalTest.cpp\"],\n    owner = \"dylany@xmail.facebook.com\",\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/io/async:io_uring_event_base_local\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"io_uring_event_test\",\n    srcs = [\"IoUringEventTest.cpp\"],\n    owner = \"dylany@xmail.facebook.com\",\n    supports_static_listing = False,\n    deps = [\n        \"//folly/futures:core\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/io/async:io_uring_event\",\n        \"//folly/portability:gtest\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"io_uring_test\",\n    srcs = [\"IoUringTest.cpp\"],\n    labels = [\"oss-broken\"],\n    owner = \"dmm@xmail.facebook.com\",\n    supports_static_listing = False,\n    deps = [\n        \"//folly/init:init\",\n        \"//folly/io/async:io_uring\",\n        \"//folly/io/async/test:async_base_test_lib\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"io_uring_zero_copy_buffer_pool_test\",\n    srcs = [\"IoUringZeroCopyBufferPoolTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"//folly/io/async:io_uring_zero_copy_buffer_pool\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"io_uring_provided_buffer_ring_test\",\n    srcs = [\"IoUringProvidedBufferRingTest.cpp\"],\n    deps = [\n        \"//folly/io/async:io_uring_provided_buffer_ring\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_binary,\n    name = \"registered_fd_benchmark\",\n    srcs = [\"RegisteredFdBenchmark.cpp\"],\n    headers = [],\n    deps = [\n        \"//folly:benchmark\",\n        \"//folly:file_util\",\n        \"//folly/io/async:async_base\",\n        \"//folly/io/async:io_uring_backend\",\n        \"//folly/portability:gflags\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"simple_async_io_test\",\n    srcs = [\"SimpleAsyncIOTest.cpp\"],\n    supports_static_listing = False,\n    deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:file\",\n        \"//folly:random\",\n        \"//folly/coro:blocking_wait\",\n        \"//folly/coro:collect\",\n        \"//folly/io:iobuf\",\n        \"//folly/io/async:simple_async_io\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:baton\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"io_test_temp_file_util_lib\",\n    srcs = [\"IoTestTempFileUtil.cpp\"],\n    headers = [\"IoTestTempFileUtil.h\"],\n    deps = [\n        \"//folly:file_util\",\n        \"//folly:string\",\n    ],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly/io:fs_util\",\n        \"//folly/testing:test_util\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"async_base_test_lib\",\n    srcs = [\"AsyncBaseTestLib.cpp\"],\n    headers = [\"AsyncBaseTestLib.h\"],\n    exported_deps = [\n        \"fbsource//third-party/glog:glog\",\n        \"//folly:scope_guard\",\n        \"//folly:string\",\n        \"//folly/io:fs_util\",\n        \"//folly/io/async:async_base_class\",\n        \"//folly/io/async/test:io_test_temp_file_util_lib\",\n        \"//folly/portability:gtest\",\n        \"//folly/portability:sockets\",\n        \"//folly/portability:unistd\",\n        \"//folly/test:test_utils\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_library,\n    name = \"mux_io_thread_pool_executor_test_lib\",\n    srcs = [\"MuxIOThreadPoolExecutorTest.cpp\"],\n    link_whole = True,\n    deps = [\n        \"//folly/executors/test:IOThreadPoolExecutorBaseTestLib\",\n        \"//folly/io/async:epoll\",\n        \"//folly/io/async:mux_io_thread_pool_executor\",\n        \"//folly/portability:gtest\",\n        \"//folly/synchronization:latch\",\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"mux_io_thread_pool_executor_test_epoll\",\n    args = [\n        \"--folly_event_base_poller_backend=epoll\",\n        \"--folly_event_base_poller_epoll_rearm_inline=false\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \":mux_io_thread_pool_executor_test_lib\",  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"mux_io_thread_pool_executor_test_epoll_rearm_inline\",\n    args = [\n        \"--folly_event_base_poller_backend=epoll\",\n        \"--folly_event_base_poller_epoll_rearm_inline=true\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \":mux_io_thread_pool_executor_test_lib\",  # @manual\n    ],\n)\n\nfbcode_target(\n    _kind = cpp_unittest,\n    name = \"mux_io_thread_pool_executor_test_io_uring\",\n    args = [\n        \"--folly_event_base_poller_backend=io_uring\",\n    ],\n    supports_static_listing = False,\n    deps = [\n        \":mux_io_thread_pool_executor_test_lib\",  # @manual\n    ],\n)\n"
  },
  {
    "path": "folly/io/async/test/BlockingSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/Optional.h>\n#include <folly/io/async/AsyncSSLSocket.h>\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/SSLContext.h>\n#include <folly/net/NetworkSocket.h>\n\nnamespace folly::test {\n\nclass BlockingSocket\n    : public folly::AsyncSocket::ConnectCallback,\n      public folly::AsyncTransport::ReadCallback,\n      public folly::AsyncTransport::WriteCallback {\n public:\n  explicit BlockingSocket(folly::NetworkSocket fd)\n      : sock_(new folly::AsyncSocket(&eventBase_, fd)) {}\n\n  BlockingSocket(\n      folly::SocketAddress address,\n      std::shared_ptr<folly::SSLContext> sslContext)\n      : sock_(\n            sslContext ? new folly::AsyncSSLSocket(sslContext, &eventBase_)\n                       : new folly::AsyncSocket(&eventBase_)),\n        address_(address) {}\n\n  explicit BlockingSocket(folly::AsyncSocket::UniquePtr socket)\n      : sock_(std::move(socket)) {\n    sock_->attachEventBase(&eventBase_);\n  }\n\n  void enableTFO() { sock_->enableTFO(); }\n\n  void setEorTracking(bool track) { sock_->setEorTracking(track); }\n\n  void setAddress(folly::SocketAddress address) { address_ = address; }\n\n  void open(\n      std::chrono::milliseconds timeout = std::chrono::milliseconds::zero()) {\n    DCHECK_LE(timeout.count(), std::numeric_limits<int>::max());\n    sock_->connect(this, address_, folly::to_narrow(timeout.count()));\n    eventBase_.loop();\n    if (err_.has_value()) {\n      throw err_.value();\n    }\n  }\n\n  void close() { sock_->close(); }\n  void closeWithReset() { sock_->closeWithReset(); }\n\n  int32_t write(\n      uint8_t const* buf,\n      size_t len,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) {\n    sock_->write(this, buf, len, flags);\n    eventBase_.loop();\n    if (err_.has_value()) {\n      throw err_.value();\n    }\n    return folly::to_narrow(folly::to_signed(len));\n  }\n\n  void writev(\n      const iovec* vec,\n      size_t count,\n      folly::WriteFlags flags = folly::WriteFlags::NONE) {\n    sock_->writev(this, vec, count, flags);\n    eventBase_.loop();\n    if (err_.has_value()) {\n      throw err_.value();\n    }\n  }\n\n  void flush() {}\n\n  int32_t readAll(uint8_t* buf, size_t len) {\n    return readHelper(buf, len, true);\n  }\n\n  int32_t read(uint8_t* buf, size_t len) { return readHelper(buf, len, false); }\n\n  int32_t readNoBlock(uint8_t* buf, size_t len) {\n    return readHelper(buf, len, false, EVLOOP_NONBLOCK);\n  }\n\n  folly::NetworkSocket getNetworkSocket() const {\n    return sock_->getNetworkSocket();\n  }\n\n  folly::AsyncSocket* getSocket() { return sock_.get(); }\n\n  folly::AsyncSSLSocket* getSSLSocket() {\n    return dynamic_cast<folly::AsyncSSLSocket*>(sock_.get());\n  }\n\n private:\n  folly::EventBase eventBase_;\n  folly::AsyncSocket::UniquePtr sock_;\n  folly::Optional<folly::AsyncSocketException> err_;\n  uint8_t* readBuf_{nullptr};\n  size_t readLen_{0};\n  folly::SocketAddress address_;\n\n  void connectSuccess() noexcept override {}\n  void connectErr(const folly::AsyncSocketException& ex) noexcept override {\n    err_ = ex;\n  }\n  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {\n    *bufReturn = readBuf_;\n    *lenReturn = readLen_;\n  }\n  void readDataAvailable(size_t len) noexcept override {\n    readBuf_ += len;\n    readLen_ -= len;\n\n    if (readLen_ == 0) {\n      sock_->setReadCB(nullptr);\n    }\n  }\n  void getReadBuffers(folly::IOBufIovecBuilder::IoVecVec& iovs) override {\n    // we reuse the same readBuf_\n    iovs.clear();\n    for (size_t i = 0; i < readLen_; i++) {\n      struct iovec iov;\n      iov.iov_base = &readBuf_[i];\n      iov.iov_len = 1;\n      iovs.push_back(iov);\n    }\n  }\n  void readEOF() noexcept override {}\n  void readErr(const folly::AsyncSocketException& ex) noexcept override {\n    err_ = ex;\n  }\n  void writeSuccess() noexcept override {}\n  void writeErr(\n      size_t /* bytesWritten */,\n      const folly::AsyncSocketException& ex) noexcept override {\n    err_ = ex;\n  }\n\n  int32_t readHelper(uint8_t* buf, size_t len, bool all, int flags = 0) {\n    if (!sock_->good()) {\n      return 0;\n    }\n    readBuf_ = buf;\n    readLen_ = len;\n    sock_->setReadCB(this);\n    while (!err_ && sock_->good() && readLen_ > 0) {\n      eventBase_.loopOnce(flags);\n      if (!all) {\n        break;\n      }\n    }\n    sock_->setReadCB(nullptr);\n    if (err_.has_value()) {\n      throw err_.value();\n    }\n    if (all && readLen_ > 0) {\n      throw folly::AsyncSocketException(\n          folly::AsyncSocketException::UNKNOWN, \"eof\");\n    }\n    return folly::to_narrow(folly::to_signed(len - readLen_));\n  }\n};\n\n} // namespace folly::test\n"
  },
  {
    "path": "folly/io/async/test/CallbackStateEnum.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace folly::test {\n\n// StateEnum used by multiple folly::Async* test libraries\nenum StateEnum { STATE_WAITING, STATE_SUCCEEDED, STATE_FAILED };\n\n} // namespace folly::test\n"
  },
  {
    "path": "folly/io/async/test/ConnCallback.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/test/CallbackStateEnum.h>\n\nnamespace folly::test {\n\nusing VoidCallback = std::function<void()>;\n\nclass ConnCallback : public folly::AsyncSocket::ConnectCallback {\n public:\n  ConnCallback()\n      : state(STATE_WAITING),\n        exception(folly::AsyncSocketException::UNKNOWN, \"none\") {}\n\n  void connectSuccess() noexcept override {\n    state = STATE_SUCCEEDED;\n    if (successCallback) {\n      successCallback();\n    }\n  }\n\n  void connectErr(const folly::AsyncSocketException& ex) noexcept override {\n    state = STATE_FAILED;\n    exception = ex;\n    if (errorCallback) {\n      errorCallback();\n    }\n  }\n\n  StateEnum state{STATE_WAITING};\n  folly::AsyncSocketException exception;\n  VoidCallback successCallback;\n  VoidCallback errorCallback;\n};\n\n} // namespace folly::test\n"
  },
  {
    "path": "folly/io/async/test/DecoratedAsyncTransportWrapperTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/DecoratedAsyncTransportWrapper.h>\n\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/AsyncTransport.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace test {\n\ntemplate <auto n>\nclass DecoratedAsyncTransportWrapperImpl\n    : public DecoratedAsyncTransportWrapper<folly::AsyncTransport> {\n public:\n  using UniquePtr =\n      std::unique_ptr<DecoratedAsyncTransportWrapperImpl, Destructor>;\n  explicit DecoratedAsyncTransportWrapperImpl(\n      folly::AsyncTransport::UniquePtr t)\n      : DecoratedAsyncTransportWrapper<folly::AsyncTransport>(std::move(t)) {}\n\n  static UniquePtr newSocket(folly::AsyncTransport::UniquePtr transport) {\n    return UniquePtr{\n        new DecoratedAsyncTransportWrapperImpl(std::move(transport))};\n  }\n};\n\nusing DecoratedAsyncTransportWrapperImpl1 =\n    DecoratedAsyncTransportWrapperImpl<1>;\nusing DecoratedAsyncTransportWrapperImpl2 =\n    DecoratedAsyncTransportWrapperImpl<2>;\nusing DecoratedAsyncTransportWrapperImpl3 =\n    DecoratedAsyncTransportWrapperImpl<3>;\n\nTEST(DecoratedAsyncTransportWrapperTest, GetWrappingTransport) {\n  folly::EventBase eb;\n  auto sock = folly::AsyncSocket::newSocket(&eb);\n  auto sockRaw = sock.get();\n  EXPECT_NE(sockRaw, nullptr);\n\n  auto layer1 = DecoratedAsyncTransportWrapperImpl1::newSocket(std::move(sock));\n  auto layer1Raw = layer1.get();\n  EXPECT_NE(layer1Raw, nullptr);\n\n  auto layer2 =\n      DecoratedAsyncTransportWrapperImpl2::newSocket(std::move(layer1));\n  auto layer2Raw = layer2.get();\n  EXPECT_NE(layer2Raw, nullptr);\n\n  auto layer3 =\n      DecoratedAsyncTransportWrapperImpl3::newSocket(std::move(layer2));\n  auto layer3Raw = layer3.get();\n  EXPECT_NE(layer3Raw, nullptr);\n\n  // socket -> higher layers\n  {\n    // socket to layer3\n    {\n      const auto wrapping3 =\n          sockRaw->getWrappingTransport<DecoratedAsyncTransportWrapperImpl3>();\n      EXPECT_EQ(wrapping3, layer3Raw);\n\n      // layer3 -> self\n      {\n        const auto layer3Self =\n            wrapping3\n                ->getWrappingTransport<DecoratedAsyncTransportWrapperImpl3>();\n        EXPECT_EQ(layer3Self, wrapping3);\n      }\n    }\n\n    // socket to layer2\n    {\n      const auto wrapping2 =\n          sockRaw->getWrappingTransport<DecoratedAsyncTransportWrapperImpl2>();\n      EXPECT_EQ(wrapping2, layer2Raw);\n\n      // layer2 up to layer 3\n      {\n        const auto wrapping3 =\n            wrapping2\n                ->getWrappingTransport<DecoratedAsyncTransportWrapperImpl3>();\n        EXPECT_EQ(wrapping3, layer3Raw);\n      }\n\n      // layer2 to self\n      {\n        const auto layer2Self =\n            wrapping2\n                ->getWrappingTransport<DecoratedAsyncTransportWrapperImpl2>();\n        EXPECT_EQ(layer2Self, wrapping2);\n      }\n    }\n\n    // socket to layer1\n    {\n      const auto wrapping1 =\n          sockRaw->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n      EXPECT_EQ(wrapping1, layer1Raw);\n\n      // layer1 up to layer3\n      {\n        const auto wrapping3 =\n            wrapping1\n                ->getWrappingTransport<DecoratedAsyncTransportWrapperImpl3>();\n        EXPECT_EQ(wrapping3, layer3Raw);\n      }\n\n      // layer1 up to layer2\n      {\n        const auto wrapping2 =\n            wrapping1\n                ->getWrappingTransport<DecoratedAsyncTransportWrapperImpl2>();\n        EXPECT_EQ(wrapping2, layer2Raw);\n      }\n\n      // layer1 to self\n      {\n        const auto layer1Self =\n            wrapping1\n                ->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n        EXPECT_EQ(layer1Self, wrapping1);\n      }\n    }\n  }\n}\n\nTEST(DecoratedAsyncTransportWrapperTest, ExchangeWrappedTransport) {\n  folly::EventBase eb;\n  auto sock1 = folly::AsyncSocket::newSocket(&eb);\n  auto sock1Raw = sock1.get();\n\n  auto sock2 = folly::AsyncTransport::UniquePtr{new folly::AsyncSocket(&eb)};\n  auto sock2Raw = sock2.get();\n\n  auto wrapper =\n      DecoratedAsyncTransportWrapperImpl1::newSocket(std::move(sock1));\n\n  // sock2 shouldn't be wrapped\n  auto wrapping =\n      sock2->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n  EXPECT_EQ(wrapping, nullptr);\n\n  // sock1 should be wrapped\n  wrapping =\n      sock1Raw->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n  EXPECT_EQ(wrapping, wrapper.get());\n\n  // exchange sock1 for sock2\n  auto exchanged = wrapper->tryExchangeWrappedTransport(sock2);\n  EXPECT_EQ(exchanged.get(), sock1Raw);\n\n  // exchanged should not be wrapped anymore\n  wrapping =\n      exchanged->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n  EXPECT_EQ(wrapping, nullptr);\n\n  wrapping =\n      sock2Raw->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n  EXPECT_EQ(wrapping, wrapper.get());\n}\n\nTEST(DecoratedAsyncTransportWrapperTest, ExchangeWrappedTransportNullTests) {\n  folly::EventBase eb;\n  auto sock1 = folly::AsyncTransport::UniquePtr{new folly::AsyncSocket(&eb)};\n  auto sock1Raw = sock1.get();\n  folly::AsyncTransport::UniquePtr nullSock{nullptr};\n\n  auto wrapper = DecoratedAsyncTransportWrapperImpl1::newSocket(nullptr);\n\n  auto exchanged = wrapper->tryExchangeWrappedTransport(sock1);\n  EXPECT_EQ(exchanged, nullptr);\n\n  exchanged = wrapper->tryExchangeWrappedTransport(nullSock);\n  EXPECT_EQ(exchanged.get(), sock1Raw);\n\n  exchanged = wrapper->tryExchangeWrappedTransport(nullSock);\n  EXPECT_EQ(exchanged, nullptr);\n}\n\nTEST(DecoratedAsyncTransportWrapperTest, Destroy) {\n  folly::EventBase eb;\n  auto sock = folly::AsyncSocket::newSocket(&eb);\n  auto sockRaw = sock.get();\n\n  auto wrapper =\n      DecoratedAsyncTransportWrapperImpl1::newSocket(std::move(sock));\n\n  auto wrapping =\n      sockRaw->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n  EXPECT_EQ(wrapping, wrapper.get());\n\n  {\n    folly::AsyncSocket::DestructorGuard dg(sockRaw);\n    wrapper.reset();\n    wrapping =\n        sockRaw->getWrappingTransport<DecoratedAsyncTransportWrapperImpl1>();\n    EXPECT_EQ(wrapping, nullptr);\n  }\n}\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/DelayedDestructionBaseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/DelayedDestructionBase.h>\n\n#include <functional>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nclass DestructionOnCallback : public DelayedDestructionBase {\n public:\n  DestructionOnCallback() : state_(0), deleted_(false) {}\n\n  void onComplete(int n, int& state) {\n    DestructorGuard dg(this);\n    for (auto i = n; i >= 0; --i) {\n      onStackedComplete(i);\n    }\n    state = state_;\n  }\n\n  int state() const { return state_; }\n  bool deleted() const { return deleted_; }\n\n protected:\n  void onStackedComplete(int recur) {\n    DestructorGuard dg(this);\n    ++state_;\n    if (recur <= 0) {\n      return;\n    }\n    onStackedComplete(--recur);\n  }\n\n private:\n  int state_;\n  bool deleted_;\n\n  void onDelayedDestroy(bool delayed) override {\n    deleted_ = true;\n    delete this;\n    (void)delayed; // prevent unused variable warnings\n  }\n};\n\nstruct DelayedDestructionBaseTest : public ::testing::Test {};\n\nTEST_F(DelayedDestructionBaseTest, basic) {\n  DestructionOnCallback* d = new DestructionOnCallback();\n  EXPECT_NE(d, nullptr);\n  int32_t state;\n  d->onComplete(3, state);\n  EXPECT_EQ(state, 10); // 10 = 6 + 3 + 1\n}\n"
  },
  {
    "path": "folly/io/async/test/DelayedDestructionTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/DelayedDestruction.h>\n\n#include <glog/logging.h>\n\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nclass DeleteGuarder : public DelayedDestruction {\n  ~DeleteGuarder() override { doFoo(); }\n\n  void doFoo() {\n    DelayedDestructionBase::DestructorGuard dg(this);\n    LOG(INFO) << \"foo\";\n  }\n};\n\nTEST(DelayedDestructionTest, GuardOnDelete) {\n  auto dg = new DeleteGuarder();\n  dg->destroy();\n}\n\nTEST(DelayedDestructionTest, GuardOnDeleteWithPreGuard) {\n  auto dg = new DeleteGuarder();\n  DelayedDestructionBase::DestructorGuard guard(dg);\n  dg->destroy();\n}\n"
  },
  {
    "path": "folly/io/async/test/DestructorCheckTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/DestructorCheck.h>\n\n#include <folly/Memory.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\n\nclass Derived : public DestructorCheck {};\n\nTEST(DestructorCheckTest, WithoutGuard) {\n  Derived d;\n}\n\nTEST(DestructorCheckTest, SingleGuard) {\n  Derived d;\n  Derived::Safety s(d);\n  ASSERT_FALSE(s.destroyed());\n}\n\nTEST(DestructorCheckTest, SingleGuardDestroyed) {\n  auto d = std::make_unique<Derived>();\n  Derived::Safety s(*d);\n  ASSERT_FALSE(s.destroyed());\n  d.reset();\n  ASSERT_TRUE(s.destroyed());\n}\n\nTEST(DestructorCheckTest, MultipleGuards) {\n  Derived d;\n  auto s1 = std::make_unique<Derived::Safety>(d);\n  auto s2 = std::make_unique<Derived::Safety>(d);\n  auto s3 = std::make_unique<Derived::Safety>(d);\n\n  // Remove the middle of the list.\n  ASSERT_FALSE(s2->destroyed());\n  s2.reset();\n\n  // Add in a link after a removal has occurred.\n  auto s4 = std::make_unique<Derived::Safety>(d);\n\n  // Remove the beginning of the list.\n  ASSERT_FALSE(s1->destroyed());\n  s1.reset();\n  // Remove the end of the list.\n  ASSERT_FALSE(s4->destroyed());\n  s4.reset();\n  // Remove the last remaining of the list.\n  ASSERT_FALSE(s3->destroyed());\n  s3.reset();\n}\n\nTEST(DestructorCheckTest, MultipleGuardsDestroyed) {\n  auto d = std::make_unique<Derived>();\n  auto s1 = std::make_unique<Derived::Safety>(*d);\n  auto s2 = std::make_unique<Derived::Safety>(*d);\n  auto s3 = std::make_unique<Derived::Safety>(*d);\n  auto s4 = std::make_unique<Derived::Safety>(*d);\n\n  // Remove something from the list.\n  ASSERT_FALSE(s2->destroyed());\n  s2.reset();\n\n  ASSERT_FALSE(s1->destroyed());\n  ASSERT_FALSE(s3->destroyed());\n  ASSERT_FALSE(s4->destroyed());\n\n  d.reset();\n\n  ASSERT_TRUE(s1->destroyed());\n  ASSERT_TRUE(s3->destroyed());\n  ASSERT_TRUE(s4->destroyed());\n}\n"
  },
  {
    "path": "folly/io/async/test/EpollBackendTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EpollBackend.h>\n\n#if FOLLY_HAS_EPOLL\n\n#include <folly/io/async/test/AsyncSignalHandlerTestLib.h>\n#include <folly/io/async/test/EventBaseTestLib.h>\n\nnamespace folly {\nnamespace test {\n\nstruct EpollBackendProvider : BackendProviderBase {\n  static std::unique_ptr<folly::EventBaseBackendBase> getBackend() {\n    folly::EpollBackend::Options options;\n    return std::make_unique<folly::EpollBackend>(options);\n  }\n};\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    EventBaseTest, EventBaseTest, EpollBackendProvider);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    AsyncSignalHandlerTest, AsyncSignalHandlerTest, EpollBackendProvider);\n\n} // namespace test\n} // namespace folly\n\n#endif\n"
  },
  {
    "path": "folly/io/async/test/EventBaseBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GFlags.h>\n\nusing namespace folly;\n\nclass CountedLoopCallback : public EventBase::LoopCallback {\n public:\n  CountedLoopCallback(EventBase* eventBase, unsigned int count)\n      : eventBase_(eventBase), count_(count) {}\n\n  void runLoopCallback() noexcept override {\n    --count_;\n    if (count_ > 0) {\n      eventBase_->runInLoop(this);\n    }\n  }\n\n private:\n  EventBase* eventBase_;\n  unsigned int count_;\n};\n\nBENCHMARK(timeMeasurementsOn, n) {\n  EventBase eventBase;\n\n  while (n--) {\n    CountedLoopCallback c(&eventBase, 10);\n    eventBase.runInLoop(&c);\n    eventBase.loop();\n  }\n}\n\nBENCHMARK_RELATIVE(timeMeasurementsOff, n) {\n  EventBase eventBase(/* enableTimeMeasurement */ false);\n\n  while (n--) {\n    CountedLoopCallback c(&eventBase, 10);\n    eventBase.runInLoop(&c);\n    eventBase.loop();\n  }\n}\n\n/**\n * --bm_min_iters=1000000\n *\n * ============================================================================\n * folly/io/async/test/EventBaseBenchmark.cpp      relative  time/iter  iters/s\n * ============================================================================\n * timeMeasurementsOn                                           1.25us  798.33K\n * timeMeasurementsOff                              214.47%   584.04ns    1.71M\n * ============================================================================\n */\n\nint main(int argc, char** argv) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  runBenchmarks();\n}\n"
  },
  {
    "path": "folly/io/async/test/EventBaseLocalTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventBaseLocal.h>\n\n#include <folly/io/async/EventBaseAtomicNotificationQueue.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Baton.h>\n\nstruct Foo {\n  Foo(int n_, std::function<void()> dtorFn_)\n      : n(n_), dtorFn(std::move(dtorFn_)) {}\n  ~Foo() { dtorFn(); }\n\n  int n;\n  std::function<void()> dtorFn;\n};\n\nTEST(EventBaseLocalTest, Basic) {\n  int dtorCnt = 0;\n  folly::EventBase evb1;\n\n  {\n    folly::EventBaseLocal<Foo> foo;\n\n    EXPECT_EQ(foo.get(evb1), nullptr);\n\n    foo.emplace(evb1, 5, [&] { ++dtorCnt; });\n\n    EXPECT_EQ(foo.get(evb1)->n, 5);\n\n    {\n      folly::EventBase evb2;\n      foo.emplace(evb2, 6, [&] { ++dtorCnt; });\n      EXPECT_EQ(foo.get(evb2)->n, 6);\n      foo.erase(evb2);\n      EXPECT_EQ(dtorCnt, 1); // should dtor a Foo when we erase\n      EXPECT_EQ(foo.get(evb2), nullptr);\n      foo.emplace(evb2, 7, [&] { ++dtorCnt; });\n      EXPECT_EQ(foo.get(evb2)->n, 7);\n    }\n\n    EXPECT_EQ(dtorCnt, 2); // should dtor a Foo when evb2 destructs\n  }\n  EXPECT_EQ(dtorCnt, 2); // should schedule Foo destructor, when foo destructs\n  evb1.loop();\n  EXPECT_EQ(dtorCnt, 3); // Foo will be destroyed in EventBase loop\n}\n\nTEST(EventBaseLocalTest, tryEmplace) {\n  folly::EventBase evb1;\n  folly::EventBaseLocal<int> ints;\n\n  EXPECT_EQ(ints.try_emplace(evb1), 0);\n  EXPECT_EQ(ints.try_emplace(evb1, 5), 0);\n\n  folly::EventBase evb2;\n  EXPECT_EQ(ints.try_emplace(evb2, 5), 5);\n  ints.erase(evb2);\n  EXPECT_EQ(4, ints.try_emplace_with(evb2, [] { return 4; }));\n}\n\nusing IntPtr = std::unique_ptr<int>;\n\nTEST(EventBaseLocalTest, getOrCreateNoncopyable) {\n  folly::EventBase evb1;\n  folly::EventBaseLocal<IntPtr> ints;\n\n  EXPECT_EQ(ints.try_emplace(evb1), IntPtr());\n  EXPECT_EQ(ints.try_emplace(evb1, std::make_unique<int>(5)), IntPtr());\n\n  folly::EventBase evb2;\n  EXPECT_EQ(*ints.try_emplace(evb2, std::make_unique<int>(5)), 5);\n}\n\nTEST(EventBaseLocalTest, emplaceNoncopyable) {\n  folly::EventBase evb;\n  folly::EventBaseLocal<IntPtr> ints;\n  ints.emplace(evb, std::make_unique<int>(42));\n  EXPECT_EQ(42, **ints.get(evb));\n}\n\nTEST(EventBaseLocalTest, DestructionOrder) {\n  struct Consumer {\n    void operator()(int) noexcept {}\n  };\n  using Queue = folly::EventBaseAtomicNotificationQueue<int, Consumer>;\n  folly::EventBaseLocal<std::unique_ptr<Queue>> ebl;\n  {\n    // Since queue binds to the underlying event loop, we must ensure\n    // local storage is cleared before event loop is destroyed.\n    folly::EventBase evb;\n    ebl.emplace_with(\n           evb,\n           [&evb] {\n             auto q = std::make_unique<Queue>();\n             q->startConsumingInternal(&evb);\n             return q;\n           })\n        .get();\n  }\n}\n\nTEST(EventBaseLocalTest, DestructorStressTest) {\n  std::atomic<folly::EventBase*> currentEvb;\n  folly::Baton<> evbReady;\n  folly::Baton<> doneWithEvb;\n  const int kIterations = 10000;\n  auto thread1 = std::thread([&] {\n    for (int i = 0; i < kIterations; i++) {\n      folly::EventBase evb;\n      currentEvb = &evb;\n      evbReady.post();\n      evb.loopForever();\n      doneWithEvb.wait();\n      doneWithEvb.reset();\n    }\n  });\n  auto thread2 = std::thread([&] {\n    for (int i = 0; i < kIterations; i++) {\n      folly::EventBaseLocal<int> local;\n      evbReady.wait();\n      evbReady.reset();\n      auto evb = currentEvb.exchange(nullptr);\n      evb->runInEventBaseThreadAndWait([&] { local.emplace(*evb, 4); });\n      evb->terminateLoopSoon();\n      doneWithEvb.post();\n    }\n  });\n\n  thread1.join();\n  thread2.join();\n}\n"
  },
  {
    "path": "folly/io/async/test/EventBaseTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/init/Init.h>\n#include <folly/io/async/test/AsyncSignalHandlerTestLib.h>\n#include <folly/io/async/test/EventBaseTestLib.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\nnamespace test {\n\nstruct DefaultBackendProvider : BackendProviderBase {\n  static std::unique_ptr<folly::EventBaseBackendBase> getBackend() {\n    return folly::EventBase::getDefaultBackend();\n  }\n};\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    EventBaseTest, EventBaseTest, DefaultBackendProvider);\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    AsyncSignalHandlerTest, AsyncSignalHandlerTest, DefaultBackendProvider);\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/EventBaseTestLib.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n#include <iostream>\n#include <memory>\n#include <thread>\n\n#include <folly/Math.h>\n#include <folly/Memory.h>\n#include <folly/ScopeGuard.h>\n#include <folly/futures/Promise.h>\n#include <folly/io/async/AsyncTimeout.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/test/SocketPair.h>\n#include <folly/io/async/test/Util.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/Stdlib.h>\n#include <folly/portability/Unistd.h>\n#include <folly/synchronization/Baton.h>\n#include <folly/synchronization/RelaxedAtomic.h>\n#include <folly/system/ThreadId.h>\n#include <folly/system/ThreadName.h>\n\n///////////////////////////////////////////////////////////////////////////\n// Tests for read and write events\n///////////////////////////////////////////////////////////////////////////\n\nnamespace folly {\nnamespace test {\nclass EventBaseTestBase : public ::testing::Test {\n public:\n  EventBaseTestBase() {\n    // libevent 2.x uses a coarse monotonic timer by default on Linux.\n    // This timer is imprecise enough to cause several of our tests to fail.\n    //\n    // Set an environment variable that causes libevent to use a non-coarse\n    // timer. This can be controlled programmatically by using the\n    // EVENT_BASE_FLAG_PRECISE_TIMER flag with event_base_new_with_config().\n    // However, this would require more compile-time #ifdefs to tell if we are\n    // using libevent 2.1+ or not.  Simply using the environment variable is\n    // the easiest option for now.\n    setenv(\"EVENT_PRECISE_TIMER\", \"1\", 1);\n  }\n};\n\ntemplate <typename T>\nclass EventBaseTest : public EventBaseTestBase {\n protected:\n  void SetUp() override {\n    SKIP_IF(T::getBackend() == nullptr) << \"Backend not available\";\n  }\n\n  std::unique_ptr<EventBase> makeEventBase(\n      folly::EventBase::Options opts = folly::EventBase::Options()) {\n    return std::make_unique<EventBase>(opts.setBackendFactory([] {\n      return T::getBackend();\n    }));\n  }\n};\n\nTYPED_TEST_SUITE_P(EventBaseTest);\n\nenum { BUF_SIZE = 4096 };\n\nFOLLY_ALWAYS_INLINE ssize_t writeToFD(int fd, size_t length) {\n  // write an arbitrary amount of data to the fd\n  auto bufv = std::vector<char>(length);\n  auto buf = bufv.data();\n  memset(buf, 'a', length);\n  const auto rc = fileops::write(fd, buf, length);\n  CHECK_EQ(rc, length);\n  return rc;\n}\n\nFOLLY_ALWAYS_INLINE size_t writeUntilFull(int fd) {\n  // Write to the fd until EAGAIN is returned\n  size_t bytesWritten = 0;\n  char buf[BUF_SIZE];\n  memset(buf, 'a', sizeof(buf));\n  while (true) {\n    ssize_t rc = fileops::write(fd, buf, sizeof(buf));\n    if (rc < 0) {\n      CHECK_EQ(errno, EAGAIN);\n      break;\n    } else {\n      bytesWritten += rc;\n    }\n  }\n  return bytesWritten;\n}\n\nFOLLY_ALWAYS_INLINE ssize_t readFromFD(int fd, size_t length) {\n  // write an arbitrary amount of data to the fd\n  auto buf = std::vector<char>(length);\n  return fileops::read(fd, buf.data(), length);\n}\n\nFOLLY_ALWAYS_INLINE size_t readUntilEmpty(int fd) {\n  // Read from the fd until EAGAIN is returned\n  char buf[BUF_SIZE];\n  size_t bytesRead = 0;\n  while (true) {\n    const auto rc = fileops::read(fd, buf, sizeof(buf));\n    if (rc == 0) {\n      CHECK(false) << \"unexpected EOF\";\n    } else if (rc < 0) {\n      CHECK_EQ(errno, EAGAIN);\n      break;\n    } else {\n      bytesRead += rc;\n    }\n  }\n  return bytesRead;\n}\n\nFOLLY_ALWAYS_INLINE void checkReadUntilEmpty(int fd, size_t expectedLength) {\n  ASSERT_EQ(readUntilEmpty(fd), expectedLength);\n}\n\nstruct ScheduledEvent {\n  int milliseconds;\n  uint16_t events;\n  size_t length;\n  ssize_t result;\n\n  void perform(int fd) {\n    if (events & folly::EventHandler::READ) {\n      if (length == 0) {\n        result = readUntilEmpty(fd);\n      } else {\n        result = readFromFD(fd, length);\n      }\n    }\n    if (events & folly::EventHandler::WRITE) {\n      if (length == 0) {\n        result = writeUntilFull(fd);\n      } else {\n        result = writeToFD(fd, length);\n      }\n    }\n  }\n};\n\nFOLLY_ALWAYS_INLINE void scheduleEvents(\n    EventBase* eventBase, int fd, ScheduledEvent* events) {\n  for (ScheduledEvent* ev = events; ev->milliseconds > 0; ++ev) {\n    eventBase->tryRunAfterDelay(\n        std::bind(&ScheduledEvent::perform, ev, fd), ev->milliseconds);\n  }\n}\n\nclass TestObserver : public folly::ExecutionObserver {\n public:\n  virtual void starting(\n      uintptr_t /* id */,\n      folly::ExecutionObserver::CallbackType /* callbackType */) noexcept\n      override {\n    if (nestedStart_ == 0) {\n      nestedStart_ = 1;\n    }\n    numStartingCalled_++;\n  }\n  virtual void stopped(\n      uintptr_t /* id */,\n      folly::ExecutionObserver::CallbackType /* callbackType */) noexcept\n      override {\n    nestedStart_--;\n    numStoppedCalled_++;\n  }\n\n  int nestedStart_{0};\n  int numStartingCalled_{0};\n  int numStoppedCalled_{0};\n};\n\nclass TestEventBaseObserver : public folly::EventBaseObserver {\n public:\n  explicit TestEventBaseObserver(uint32_t samplingRatio)\n      : samplingRatio_(samplingRatio) {}\n  uint32_t getSampleRate() const override { return samplingRatio_; }\n\n  void loopSample(int64_t, int64_t) override { numTimesCalled_++; }\n  uint32_t getNumTimesCalled() const { return numTimesCalled_; }\n\n private:\n  uint32_t samplingRatio_;\n  uint32_t numTimesCalled_{0};\n};\n\nclass TestHandler : public folly::EventHandler {\n public:\n  TestHandler(folly::EventBase* eventBase, int fd)\n      : EventHandler(eventBase, folly::NetworkSocket::fromFd(fd)), fd_(fd) {}\n\n  void handlerReady(uint16_t events) noexcept override {\n    ssize_t bytesRead = 0;\n    ssize_t bytesWritten = 0;\n    if (events & READ) {\n      // Read all available data, so EventBase will stop calling us\n      // until new data becomes available\n      bytesRead = readUntilEmpty(fd_);\n    }\n    if (events & WRITE) {\n      // Write until the pipe buffer is full, so EventBase will stop calling\n      // us until the other end has read some data\n      bytesWritten = writeUntilFull(fd_);\n    }\n\n    log.emplace_back(events, bytesRead, bytesWritten);\n  }\n\n  struct EventRecord {\n    EventRecord(uint16_t events_, size_t bytesRead_, size_t bytesWritten_)\n        : events(events_),\n          timestamp(),\n          bytesRead(bytesRead_),\n          bytesWritten(bytesWritten_) {}\n\n    uint16_t events;\n    folly::TimePoint timestamp;\n    ssize_t bytesRead;\n    ssize_t bytesWritten;\n  };\n\n  std::deque<EventRecord> log;\n\n private:\n  int fd_;\n};\n\nTYPED_TEST_P(EventBaseTest, EventBaseThread) {\n  const auto testInLoop = [](EventBase& evb, bool canRunImmediately) {\n    bool done = false;\n    evb.runInEventBaseThread([&] {\n      evb.checkIsInEventBaseThread();\n      EXPECT_TRUE(evb.isInEventBaseThread());\n      done = true;\n    });\n    evb.loopOnce();\n    ASSERT_TRUE(done);\n\n    done = false;\n    evb.runImmediatelyOrRunInEventBaseThread([&] { done = true; });\n    EXPECT_EQ(done, canRunImmediately);\n    evb.loopOnce();\n    EXPECT_TRUE(done);\n  };\n\n  {\n    auto evbPtr = this->makeEventBase();\n    EXPECT_TRUE(evbPtr->isInEventBaseThread());\n    testInLoop(*evbPtr, true);\n    evbPtr->checkIsInEventBaseThread();\n  }\n\n  {\n    auto evbPtr = this->makeEventBase();\n    EXPECT_TRUE(evbPtr->isInEventBaseThread());\n    evbPtr->setStrictLoopThread();\n    EXPECT_FALSE(evbPtr->isInEventBaseThread());\n    testInLoop(*evbPtr, false);\n    EXPECT_DEATH(\n        evbPtr->checkIsInEventBaseThread(),\n        \".*This logic must be executed in the event base thread.*\");\n    EXPECT_DEATH(\n        evbPtr->terminateLoopSoon(),\n        \".*terminateLoopSoon\\\\(\\\\) not allowed in strict loop thread mode.*\");\n  }\n}\n\n/**\n * Test a READ event\n */\nTYPED_TEST_P(EventBaseTest, ReadEvent) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Register for read events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::READ);\n\n  // Register timeouts to perform two write events\n  ScheduledEvent events[] = {\n      {10, EventHandler::WRITE, 2345, 0},\n      {160, EventHandler::WRITE, 99, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // Since we didn't use the EventHandler::PERSIST flag, the handler should\n  // have received the first read, then unregistered itself.  Check that only\n  // the first chunk of data was received.\n  ASSERT_EQ(handler.log.size(), 1);\n  ASSERT_EQ(handler.log[0].events, EventHandler::READ);\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[0].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds),\n      std::chrono::milliseconds(90));\n  ASSERT_EQ(handler.log[0].bytesRead, events[0].length);\n  ASSERT_EQ(handler.log[0].bytesWritten, 0);\n  T_CHECK_TIMEOUT(\n      start,\n      end,\n      std::chrono::milliseconds(events[1].milliseconds),\n      std::chrono::milliseconds(30));\n\n  // Make sure the second chunk of data is still waiting to be read.\n  size_t bytesRemaining = readUntilEmpty(sp[0]);\n  ASSERT_EQ(bytesRemaining, events[1].length);\n}\n\n/**\n * Test (READ | PERSIST)\n */\nTYPED_TEST_P(EventBaseTest, ReadPersist) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Register for read events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::READ | EventHandler::PERSIST);\n\n  // Register several timeouts to perform writes\n  ScheduledEvent events[] = {\n      {10, EventHandler::WRITE, 1024, 0},\n      {20, EventHandler::WRITE, 2211, 0},\n      {30, EventHandler::WRITE, 4096, 0},\n      {100, EventHandler::WRITE, 100, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Schedule a timeout to unregister the handler after the third write\n  eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 85);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // The handler should have received the first 3 events,\n  // then been unregistered after that.\n  ASSERT_EQ(handler.log.size(), 3);\n  for (int n = 0; n < 3; ++n) {\n    ASSERT_EQ(handler.log[n].events, EventHandler::READ);\n    T_CHECK_TIMEOUT(\n        start,\n        handler.log[n].timestamp,\n        std::chrono::milliseconds(events[n].milliseconds));\n    ASSERT_EQ(handler.log[n].bytesRead, events[n].length);\n    ASSERT_EQ(handler.log[n].bytesWritten, 0);\n  }\n  T_CHECK_TIMEOUT(\n      start, end, std::chrono::milliseconds(events[3].milliseconds));\n\n  // Make sure the data from the last write is still waiting to be read\n  size_t bytesRemaining = readUntilEmpty(sp[0]);\n  ASSERT_EQ(bytesRemaining, events[3].length);\n}\n\n/**\n * Test registering for READ when the socket is immediately readable\n */\nTYPED_TEST_P(EventBaseTest, ReadImmediate) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Write some data to the socket so the other end will\n  // be immediately readable\n  size_t dataLength = 1234;\n  writeToFD(sp[1], dataLength);\n\n  // Register for read events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::READ | EventHandler::PERSIST);\n\n  // Register a timeout to perform another write\n  ScheduledEvent events[] = {\n      {10, EventHandler::WRITE, 2345, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Schedule a timeout to unregister the handler\n  eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 20);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(handler.log.size(), 2);\n\n  // There should have been 1 event for immediate readability\n  ASSERT_EQ(handler.log[0].events, EventHandler::READ);\n  T_CHECK_TIMEOUT(\n      start, handler.log[0].timestamp, std::chrono::milliseconds(0));\n  ASSERT_EQ(handler.log[0].bytesRead, dataLength);\n  ASSERT_EQ(handler.log[0].bytesWritten, 0);\n\n  // There should be another event after the timeout wrote more data\n  ASSERT_EQ(handler.log[1].events, EventHandler::READ);\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[1].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds));\n  ASSERT_EQ(handler.log[1].bytesRead, events[0].length);\n  ASSERT_EQ(handler.log[1].bytesWritten, 0);\n\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(20));\n}\n\n/**\n * Test a WRITE event\n */\nTYPED_TEST_P(EventBaseTest, WriteEvent) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Fill up the write buffer before starting\n  size_t initialBytesWritten = writeUntilFull(sp[0]);\n\n  // Register for write events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::WRITE);\n\n  // Register timeouts to perform two reads\n  ScheduledEvent events[] = {\n      {10, EventHandler::READ, 0, 0},\n      {60, EventHandler::READ, 0, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // Since we didn't use the EventHandler::PERSIST flag, the handler should\n  // have only been able to write once, then unregistered itself.\n  ASSERT_EQ(handler.log.size(), 1);\n  ASSERT_EQ(handler.log[0].events, EventHandler::WRITE);\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[0].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds));\n  ASSERT_EQ(handler.log[0].bytesRead, 0);\n  ASSERT_GT(handler.log[0].bytesWritten, 0);\n  T_CHECK_TIMEOUT(\n      start, end, std::chrono::milliseconds(events[1].milliseconds));\n\n  ASSERT_EQ(events[0].result, initialBytesWritten);\n  ASSERT_EQ(events[1].result, handler.log[0].bytesWritten);\n}\n\n/**\n * Test (WRITE | PERSIST)\n */\nTYPED_TEST_P(EventBaseTest, WritePersist) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Fill up the write buffer before starting\n  size_t initialBytesWritten = writeUntilFull(sp[0]);\n\n  // Register for write events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::WRITE | EventHandler::PERSIST);\n\n  // Register several timeouts to read from the socket at several intervals\n  ScheduledEvent events[] = {\n      {10, EventHandler::READ, 0, 0},\n      {40, EventHandler::READ, 0, 0},\n      {70, EventHandler::READ, 0, 0},\n      {100, EventHandler::READ, 0, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Schedule a timeout to unregister the handler after the third read\n  eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 85);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // The handler should have received the first 3 events,\n  // then been unregistered after that.\n  ASSERT_EQ(handler.log.size(), 3);\n  ASSERT_EQ(events[0].result, initialBytesWritten);\n  for (int n = 0; n < 3; ++n) {\n    ASSERT_EQ(handler.log[n].events, EventHandler::WRITE);\n    T_CHECK_TIMEOUT(\n        start,\n        handler.log[n].timestamp,\n        std::chrono::milliseconds(events[n].milliseconds));\n    ASSERT_EQ(handler.log[n].bytesRead, 0);\n    ASSERT_GT(handler.log[n].bytesWritten, 0);\n    ASSERT_EQ(handler.log[n].bytesWritten, events[n + 1].result);\n  }\n  T_CHECK_TIMEOUT(\n      start, end, std::chrono::milliseconds(events[3].milliseconds));\n}\n\n/**\n * Test registering for WRITE when the socket is immediately writable\n */\nTYPED_TEST_P(EventBaseTest, WriteImmediate) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Register for write events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::WRITE | EventHandler::PERSIST);\n\n  // Register a timeout to perform a read\n  ScheduledEvent events[] = {\n      {10, EventHandler::READ, 0, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Schedule a timeout to unregister the handler\n  int64_t unregisterTimeout = 40;\n  eb.tryRunAfterDelay(\n      std::bind(&TestHandler::unregisterHandler, &handler), unregisterTimeout);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(handler.log.size(), 2);\n\n  // Since the socket buffer was initially empty,\n  // there should have been 1 event for immediate writability\n  ASSERT_EQ(handler.log[0].events, EventHandler::WRITE);\n  T_CHECK_TIMEOUT(\n      start, handler.log[0].timestamp, std::chrono::milliseconds(0));\n  ASSERT_EQ(handler.log[0].bytesRead, 0);\n  ASSERT_GT(handler.log[0].bytesWritten, 0);\n\n  // There should be another event after the timeout wrote more data\n  ASSERT_EQ(handler.log[1].events, EventHandler::WRITE);\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[1].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds));\n  ASSERT_EQ(handler.log[1].bytesRead, 0);\n  ASSERT_GT(handler.log[1].bytesWritten, 0);\n\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(unregisterTimeout));\n}\n\n/**\n * Test (READ | WRITE) when the socket becomes readable first\n */\nTYPED_TEST_P(EventBaseTest, ReadWrite) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Fill up the write buffer before starting\n  size_t sock0WriteLength = writeUntilFull(sp[0]);\n\n  // Register for read and write events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::READ_WRITE);\n\n  // Register timeouts to perform a write then a read.\n  ScheduledEvent events[] = {\n      {10, EventHandler::WRITE, 2345, 0},\n      {40, EventHandler::READ, 0, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // Since we didn't use the EventHandler::PERSIST flag, the handler should\n  // have only noticed readability, then unregistered itself.  Check that only\n  // one event was logged.\n  ASSERT_EQ(handler.log.size(), 1);\n  ASSERT_EQ(handler.log[0].events, EventHandler::READ);\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[0].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds));\n  ASSERT_EQ(handler.log[0].bytesRead, events[0].length);\n  ASSERT_EQ(handler.log[0].bytesWritten, 0);\n  ASSERT_EQ(events[1].result, sock0WriteLength);\n  T_CHECK_TIMEOUT(\n      start, end, std::chrono::milliseconds(events[1].milliseconds));\n}\n\n/**\n * Test (READ | WRITE) when the socket becomes writable first\n */\nTYPED_TEST_P(EventBaseTest, WriteRead) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Fill up the write buffer before starting\n  size_t sock0WriteLength = writeUntilFull(sp[0]);\n\n  // Register for read and write events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::READ_WRITE);\n\n  // Register timeouts to perform a read then a write.\n  size_t sock1WriteLength = 2345;\n  ScheduledEvent events[] = {\n      {10, EventHandler::READ, 0, 0},\n      {40, EventHandler::WRITE, sock1WriteLength, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // Since we didn't use the EventHandler::PERSIST flag, the handler should\n  // have only noticed writability, then unregistered itself.  Check that only\n  // one event was logged.\n  ASSERT_EQ(handler.log.size(), 1);\n  ASSERT_EQ(handler.log[0].events, EventHandler::WRITE);\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[0].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds));\n  ASSERT_EQ(handler.log[0].bytesRead, 0);\n  ASSERT_GT(handler.log[0].bytesWritten, 0);\n  ASSERT_EQ(events[0].result, sock0WriteLength);\n  ASSERT_EQ(events[1].result, sock1WriteLength);\n  T_CHECK_TIMEOUT(\n      start, end, std::chrono::milliseconds(events[1].milliseconds));\n\n  // Make sure the written data is still waiting to be read.\n  size_t bytesRemaining = readUntilEmpty(sp[0]);\n  ASSERT_EQ(bytesRemaining, events[1].length);\n}\n\n/**\n * Test (READ | WRITE) when the socket becomes readable and writable\n * at the same time.\n */\nTYPED_TEST_P(EventBaseTest, ReadWriteSimultaneous) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Fill up the write buffer before starting\n  size_t sock0WriteLength = writeUntilFull(sp[0]);\n\n  // Register for read and write events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(EventHandler::READ_WRITE);\n\n  // Register a timeout to perform a read and write together\n  ScheduledEvent events[] = {\n      {10, EventHandler::READ | EventHandler::WRITE, 0, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // It's not strictly required that the EventBase register us about both\n  // events in the same call or thw read/write notifications are delievered at\n  // the same. So, it's possible that if the EventBase implementation changes\n  // this test could start failing, and it wouldn't be considered breaking the\n  // API.  However for now it's nice to exercise this code path.\n  ASSERT_EQ(handler.log.size(), 1);\n  if (handler.log[0].events & EventHandler::READ) {\n    ASSERT_EQ(handler.log[0].bytesRead, sock0WriteLength);\n    ASSERT_GT(handler.log[0].bytesWritten, 0);\n  }\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[0].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds));\n  T_CHECK_TIMEOUT(\n      start, end, std::chrono::milliseconds(events[0].milliseconds));\n}\n\n/**\n * Test (READ | WRITE | PERSIST)\n */\nTYPED_TEST_P(EventBaseTest, ReadWritePersist) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Register for read and write events\n  TestHandler handler(&eb, sp[0]);\n  handler.registerHandler(\n      EventHandler::READ | EventHandler::WRITE | EventHandler::PERSIST);\n\n  // Register timeouts to perform several reads and writes\n  ScheduledEvent events[] = {\n      {10, EventHandler::WRITE, 2345, 0},\n      {20, EventHandler::READ, 0, 0},\n      {35, EventHandler::WRITE, 200, 0},\n      {45, EventHandler::WRITE, 15, 0},\n      {55, EventHandler::READ, 0, 0},\n      {120, EventHandler::WRITE, 2345, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Schedule a timeout to unregister the handler\n  eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 80);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(handler.log.size(), 6);\n\n  // Since we didn't fill up the write buffer immediately, there should\n  // be an immediate event for writability.\n  ASSERT_EQ(handler.log[0].events, EventHandler::WRITE);\n  T_CHECK_TIMEOUT(\n      start, handler.log[0].timestamp, std::chrono::milliseconds(0));\n  ASSERT_EQ(handler.log[0].bytesRead, 0);\n  ASSERT_GT(handler.log[0].bytesWritten, 0);\n\n  // Events 1 through 5 should correspond to the scheduled events\n  for (int n = 1; n < 6; ++n) {\n    ScheduledEvent* event = &events[n - 1];\n    T_CHECK_TIMEOUT(\n        start,\n        handler.log[n].timestamp,\n        std::chrono::milliseconds(event->milliseconds));\n    if (event->events == EventHandler::READ) {\n      ASSERT_EQ(handler.log[n].events, EventHandler::WRITE);\n      ASSERT_EQ(handler.log[n].bytesRead, 0);\n      ASSERT_GT(handler.log[n].bytesWritten, 0);\n    } else {\n      ASSERT_EQ(handler.log[n].events, EventHandler::READ);\n      ASSERT_EQ(handler.log[n].bytesRead, event->length);\n      ASSERT_EQ(handler.log[n].bytesWritten, 0);\n    }\n  }\n\n  // The timeout should have unregistered the handler before the last write.\n  // Make sure that data is still waiting to be read\n  size_t bytesRemaining = readUntilEmpty(sp[0]);\n  ASSERT_EQ(bytesRemaining, events[5].length);\n}\n\nnamespace {\nclass PartialReadHandler : public TestHandler {\n public:\n  PartialReadHandler(EventBase* eventBase, int fd, size_t readLength)\n      : TestHandler(eventBase, fd), fd_(fd), readLength_(readLength) {}\n\n  void handlerReady(uint16_t events) noexcept override {\n    assert(events == EventHandler::READ);\n    ssize_t bytesRead = readFromFD(fd_, readLength_);\n    log.emplace_back(events, bytesRead, 0);\n  }\n\n private:\n  int fd_;\n  size_t readLength_;\n};\n} // namespace\n\n/**\n * Test reading only part of the available data when a read event is fired.\n * When PERSIST is used, make sure the handler gets notified again the next\n * time around the loop.\n */\nTYPED_TEST_P(EventBaseTest, ReadPartial) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Register for read events\n  size_t readLength = 100;\n  PartialReadHandler handler(&eb, sp[0], readLength);\n  handler.registerHandler(EventHandler::READ | EventHandler::PERSIST);\n\n  // Register a timeout to perform a single write,\n  // with more data than PartialReadHandler will read at once\n  ScheduledEvent events[] = {\n      {10, EventHandler::WRITE, (3 * readLength) + (readLength / 2), 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Schedule a timeout to unregister the handler\n  eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 30);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(handler.log.size(), 4);\n\n  // The first 3 invocations should read readLength bytes each\n  for (int n = 0; n < 3; ++n) {\n    ASSERT_EQ(handler.log[n].events, EventHandler::READ);\n    T_CHECK_TIMEOUT(\n        start,\n        handler.log[n].timestamp,\n        std::chrono::milliseconds(events[0].milliseconds));\n    ASSERT_EQ(handler.log[n].bytesRead, readLength);\n    ASSERT_EQ(handler.log[n].bytesWritten, 0);\n  }\n  // The last read only has readLength/2 bytes\n  ASSERT_EQ(handler.log[3].events, EventHandler::READ);\n  T_CHECK_TIMEOUT(\n      start,\n      handler.log[3].timestamp,\n      std::chrono::milliseconds(events[0].milliseconds));\n  ASSERT_EQ(handler.log[3].bytesRead, readLength / 2);\n  ASSERT_EQ(handler.log[3].bytesWritten, 0);\n}\n\nnamespace {\nclass PartialWriteHandler : public TestHandler {\n public:\n  PartialWriteHandler(EventBase* eventBase, int fd, size_t writeLength)\n      : TestHandler(eventBase, fd), fd_(fd), writeLength_(writeLength) {}\n\n  void handlerReady(uint16_t events) noexcept override {\n    assert(events == EventHandler::WRITE);\n    ssize_t bytesWritten = writeToFD(fd_, writeLength_);\n    log.emplace_back(events, 0, bytesWritten);\n  }\n\n private:\n  int fd_;\n  size_t writeLength_;\n};\n} // namespace\n\n/**\n * Test writing without completely filling up the write buffer when the fd\n * becomes writable.  When PERSIST is used, make sure the handler gets\n * notified again the next time around the loop.\n */\nTYPED_TEST_P(EventBaseTest, WritePartial) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Fill up the write buffer before starting\n  size_t initialBytesWritten = writeUntilFull(sp[0]);\n\n  // Register for write events\n  size_t writeLength = 100;\n  PartialWriteHandler handler(&eb, sp[0], writeLength);\n  handler.registerHandler(EventHandler::WRITE | EventHandler::PERSIST);\n\n  // Register a timeout to read, so that more data can be written\n  ScheduledEvent events[] = {\n      {10, EventHandler::READ, 0, 0},\n      {0, 0, 0, 0},\n  };\n  TimePoint start;\n  scheduleEvents(&eb, sp[1], events);\n\n  // Schedule a timeout to unregister the handler\n  eb.tryRunAfterDelay(std::bind(&TestHandler::unregisterHandler, &handler), 30);\n\n  // Loop\n  eb.loop();\n  TimePoint end;\n\n  // Depending on how big the socket buffer is, there will be multiple writes\n  // Only check the first 5\n  int numChecked = 5;\n  ASSERT_GE(handler.log.size(), numChecked);\n  ASSERT_EQ(events[0].result, initialBytesWritten);\n\n  // The first 3 invocations should read writeLength bytes each\n  for (int n = 0; n < numChecked; ++n) {\n    ASSERT_EQ(handler.log[n].events, EventHandler::WRITE);\n    T_CHECK_TIMEOUT(\n        start,\n        handler.log[n].timestamp,\n        std::chrono::milliseconds(events[0].milliseconds));\n    ASSERT_EQ(handler.log[n].bytesRead, 0);\n    ASSERT_EQ(handler.log[n].bytesWritten, writeLength);\n  }\n}\n\nnamespace {\nclass DestroyHandler : public AsyncTimeout {\n public:\n  DestroyHandler(EventBase* eb, EventHandler* h)\n      : AsyncTimeout(eb), handler_(h) {}\n\n  void timeoutExpired() noexcept override { delete handler_; }\n\n private:\n  EventHandler* handler_;\n};\n} // namespace\n\n/**\n * Test destroying a registered EventHandler\n */\nTYPED_TEST_P(EventBaseTest, DestroyingHandler) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  SocketPair sp;\n\n  // Fill up the write buffer before starting\n  size_t initialBytesWritten = writeUntilFull(sp[0]);\n\n  // Register for write events\n  TestHandler* handler = new TestHandler(&eb, sp[0]);\n  handler->registerHandler(EventHandler::WRITE | EventHandler::PERSIST);\n\n  // After 10ms, read some data, so that the handler\n  // will be notified that it can write.\n  eb.tryRunAfterDelay(\n      std::bind(checkReadUntilEmpty, sp[1], initialBytesWritten), 10);\n\n  // Start a timer to destroy the handler after 25ms\n  // This mainly just makes sure the code doesn't break or assert\n  DestroyHandler dh(&eb, handler);\n  dh.scheduleTimeout(25);\n\n  TimePoint start;\n  eb.loop();\n  TimePoint end;\n\n  // Make sure the EventHandler was uninstalled properly when it was\n  // destroyed, and the EventBase loop exited\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(25));\n\n  // Make sure that the handler wrote data to the socket\n  // before it was destroyed\n  size_t bytesRemaining = readUntilEmpty(sp[1]);\n  ASSERT_GT(bytesRemaining, 0);\n}\n\n/**\n * Handlers may delete other handlers that fire in the same loop iteration. The\n * order in which callbacks are invoked is unspecified, so we use the first\n * callback that runs to delete other handlers.\n */\nTYPED_TEST_P(EventBaseTest, HandlerSideEffects) {\n  struct SharedState {\n    folly::EventBase* evb;\n    std::vector<std::unique_ptr<AsyncTimeout>> timeouts;\n    std::vector<std::unique_ptr<EventHandler>> io;\n    std::atomic<size_t> timeoutsFired{0};\n    std::atomic<size_t> ioFired{0};\n  };\n\n  struct Timeout : AsyncTimeout {\n    explicit Timeout(SharedState& s) : AsyncTimeout(s.evb), state(s) {}\n\n    void timeoutExpired() noexcept override {\n      ++state.timeoutsFired;\n      // First to fire kills the others.\n      for (auto& timeout : state.timeouts) {\n        if (timeout.get() != this) {\n          timeout.reset();\n        }\n      }\n      // Kill a couple of io events too.\n      state.io.front().reset();\n      state.io.back().reset();\n    }\n\n    SharedState& state;\n  };\n\n  struct ReadyIO : EventHandler {\n    explicit ReadyIO(SharedState& s) : state(s) {\n      initHandler(s.evb, folly::NetworkSocket::fromFd(sp[0]));\n      registerHandler(EventHandler::READ);\n      // Event is ready as soon as the loop starts.\n      writeUntilFull(sp[1]);\n    }\n\n    void handlerReady(uint16_t events) noexcept override {\n      EXPECT_EQ(events, READ);\n      ++state.ioFired;\n      // First to fire kills the others.\n      for (auto& io : state.io) {\n        if (io.get() != this) {\n          io.reset();\n        }\n      }\n\n      // Kill a couple of timeouts too.\n      state.timeouts.front().reset();\n      state.timeouts.back().reset();\n    }\n\n    SocketPair sp;\n    SharedState& state;\n  };\n\n  auto evbPtr = this->makeEventBase();\n\n  SharedState state;\n  state.evb = evbPtr.get();\n\n  for (size_t i = 0; i < 10; ++i) {\n    auto& timeout =\n        state.timeouts.emplace_back(std::make_unique<Timeout>(state));\n    timeout->scheduleTimeout(0); // Fire as soon as the loop starts.\n    state.io.emplace_back(std::make_unique<ReadyIO>(state));\n  }\n\n  // Sleep a bit to side-step any rounding in the internal timer implementation.\n  /* sleep override */\n  std::this_thread::sleep_for(std::chrono::milliseconds(1));\n\n  // All events should fire in the same iteration.\n  EXPECT_EQ(state.timeoutsFired.load(), 0);\n  EXPECT_EQ(state.ioFired.load(), 0);\n  state.evb->loopOnce();\n  if (TypeParam::isIoUringBackend()) {\n    // IoUringBackend needs two iterations.\n    // TODO(dmm): Figure out why.\n    state.evb->loopOnce();\n  }\n  EXPECT_EQ(state.timeoutsFired.load(), 1);\n  EXPECT_EQ(state.ioFired.load(), 1);\n  state.evb->loop();\n  EXPECT_EQ(state.timeoutsFired.load(), 1);\n  EXPECT_EQ(state.ioFired.load(), 1);\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Tests for timeout events\n///////////////////////////////////////////////////////////////////////////\n\nTYPED_TEST_P(EventBaseTest, RunAfterDelay) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  TimePoint timestamp1(false);\n  TimePoint timestamp2(false);\n  TimePoint timestamp3(false);\n  auto fn1 = std::bind(&TimePoint::reset, &timestamp1);\n  auto fn2 = std::bind(&TimePoint::reset, &timestamp2);\n  auto fn3 = std::bind(&TimePoint::reset, &timestamp3);\n\n  TimePoint start;\n  eb.tryRunAfterDelay(std::move(fn1), 10);\n  eb.tryRunAfterDelay(std::move(fn2), 20);\n  eb.tryRunAfterDelay(std::move(fn3), 40);\n\n  eb.loop();\n  TimePoint end;\n\n  T_CHECK_TIMEOUT(start, timestamp1, std::chrono::milliseconds(10));\n  T_CHECK_TIMEOUT(start, timestamp2, std::chrono::milliseconds(20));\n  T_CHECK_TIMEOUT(start, timestamp3, std::chrono::milliseconds(40));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(40));\n}\n\n/**\n * Test the behavior of tryRunAfterDelay() when some timeouts are\n * still scheduled when the EventBase is destroyed.\n */\nTYPED_TEST_P(EventBaseTest, RunAfterDelayDestruction) {\n  TimePoint timestamp1(false);\n  TimePoint timestamp2(false);\n  TimePoint timestamp3(false);\n  TimePoint timestamp4(false);\n  TimePoint start(false);\n  TimePoint end(false);\n\n  {\n    auto evbPtr = this->makeEventBase();\n    folly::EventBase& eb = *evbPtr;\n    start.reset();\n\n    // Run two normal timeouts\n    eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp1), 10);\n    eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp2), 20);\n\n    // Schedule a timeout to stop the event loop after 40ms\n    eb.tryRunAfterDelay(std::bind(&EventBase::terminateLoopSoon, &eb), 40);\n\n    // Schedule 2 timeouts that would fire after the event loop stops\n    eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp3), 80);\n    eb.tryRunAfterDelay(std::bind(&TimePoint::reset, &timestamp4), 160);\n\n    eb.loop();\n    end.reset();\n  }\n\n  T_CHECK_TIMEOUT(start, timestamp1, std::chrono::milliseconds(10));\n  T_CHECK_TIMEOUT(start, timestamp2, std::chrono::milliseconds(20));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(40));\n\n  ASSERT_TRUE(timestamp3.isUnset());\n  ASSERT_TRUE(timestamp4.isUnset());\n\n  // Ideally this test should be run under valgrind to ensure that no\n  // memory is leaked.\n}\n\nnamespace {\nclass TestTimeout : public AsyncTimeout {\n public:\n  explicit TestTimeout(EventBase* eventBase)\n      : AsyncTimeout(eventBase), timestamp(false) {}\n\n  void timeoutExpired() noexcept override { timestamp.reset(); }\n\n  TimePoint timestamp;\n};\n} // namespace\n\nTYPED_TEST_P(EventBaseTest, BasicTimeouts) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  TestTimeout t1(&eb);\n  TestTimeout t2(&eb);\n  TestTimeout t3(&eb);\n  TimePoint start;\n  t1.scheduleTimeout(10);\n  t2.scheduleTimeout(20);\n  t3.scheduleTimeout(40);\n\n  eb.loop();\n  TimePoint end;\n\n  T_CHECK_TIMEOUT(start, t1.timestamp, std::chrono::milliseconds(10));\n  T_CHECK_TIMEOUT(start, t2.timestamp, std::chrono::milliseconds(20));\n  T_CHECK_TIMEOUT(start, t3.timestamp, std::chrono::milliseconds(40));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(40));\n}\n\nnamespace {\nclass ReschedulingTimeout : public AsyncTimeout {\n public:\n  ReschedulingTimeout(EventBase* evb, const std::vector<uint32_t>& timeouts)\n      : AsyncTimeout(evb), timeouts_(timeouts), iterator_(timeouts_.begin()) {}\n\n  void start() { reschedule(); }\n\n  void timeoutExpired() noexcept override {\n    timestamps.emplace_back();\n    reschedule();\n  }\n\n  void reschedule() {\n    if (iterator_ != timeouts_.end()) {\n      uint32_t timeout = *iterator_;\n      ++iterator_;\n      scheduleTimeout(timeout);\n    }\n  }\n\n  std::vector<TimePoint> timestamps;\n\n private:\n  std::vector<uint32_t> timeouts_;\n  std::vector<uint32_t>::const_iterator iterator_;\n};\n} // namespace\n\n/**\n * Test rescheduling the same timeout multiple times\n */\nTYPED_TEST_P(EventBaseTest, ReuseTimeout) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  std::vector<uint32_t> timeouts;\n  timeouts.push_back(10);\n  timeouts.push_back(30);\n  timeouts.push_back(15);\n\n  ReschedulingTimeout t(&eb, timeouts);\n  TimePoint start;\n  t.start();\n  eb.loop();\n  TimePoint end;\n\n  // Use a higher tolerance than usual.  We're waiting on 3 timeouts\n  // consecutively.  In general, each timeout may go over by a few\n  // milliseconds, and we're tripling this error by witing on 3 timeouts.\n  std::chrono::milliseconds tolerance{6};\n\n  ASSERT_EQ(timeouts.size(), t.timestamps.size());\n  uint32_t total = 0;\n  for (size_t n = 0; n < timeouts.size(); ++n) {\n    total += timeouts[n];\n    T_CHECK_TIMEOUT(\n        start, t.timestamps[n], std::chrono::milliseconds(total), tolerance);\n  }\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(total), tolerance);\n}\n\n/**\n * Test rescheduling a timeout before it has fired\n */\nTYPED_TEST_P(EventBaseTest, RescheduleTimeout) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  TestTimeout t1(&eb);\n  TestTimeout t2(&eb);\n  TestTimeout t3(&eb);\n\n  TimePoint start;\n  t1.scheduleTimeout(15);\n  t2.scheduleTimeout(30);\n  t3.scheduleTimeout(30);\n\n  // after 10ms, reschedule t2 to run sooner than originally scheduled\n  eb.tryRunAfterDelay([&] { t2.scheduleTimeout(10); }, 10);\n  // after 10ms, reschedule t3 to run later than originally scheduled\n  eb.tryRunAfterDelay([&] { t3.scheduleTimeout(40); }, 10);\n\n  eb.loop();\n  TimePoint end;\n\n  T_CHECK_TIMEOUT(start, t1.timestamp, std::chrono::milliseconds(15));\n  T_CHECK_TIMEOUT(start, t2.timestamp, std::chrono::milliseconds(20));\n  T_CHECK_TIMEOUT(start, t3.timestamp, std::chrono::milliseconds(50));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(50));\n}\n\n/**\n * Test cancelling a timeout\n */\nTYPED_TEST_P(EventBaseTest, CancelTimeout) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  std::vector<uint32_t> timeouts;\n  timeouts.push_back(10);\n  timeouts.push_back(30);\n  timeouts.push_back(25);\n\n  ReschedulingTimeout t(&eb, timeouts);\n  TimePoint start;\n  t.start();\n  eb.tryRunAfterDelay(std::bind(&AsyncTimeout::cancelTimeout, &t), 50);\n\n  eb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t.timestamps.size(), 2);\n  T_CHECK_TIMEOUT(start, t.timestamps[0], std::chrono::milliseconds(10));\n  T_CHECK_TIMEOUT(start, t.timestamps[1], std::chrono::milliseconds(40));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(50));\n}\n\nnamespace {\nclass DestroyTimeout : public AsyncTimeout {\n public:\n  DestroyTimeout(EventBase* eb, AsyncTimeout* t)\n      : AsyncTimeout(eb), timeout_(t) {}\n\n  void timeoutExpired() noexcept override { delete timeout_; }\n\n private:\n  AsyncTimeout* timeout_;\n};\n} // namespace\n\n/**\n * Test destroying a scheduled timeout object\n */\nTYPED_TEST_P(EventBaseTest, DestroyingTimeout) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  TestTimeout* t1 = new TestTimeout(&eb);\n  TimePoint start;\n  t1->scheduleTimeout(30);\n\n  DestroyTimeout dt(&eb, t1);\n  dt.scheduleTimeout(10);\n\n  eb.loop();\n  TimePoint end;\n\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(10));\n}\n\n/**\n * Test the scheduled executor impl\n */\nTYPED_TEST_P(EventBaseTest, ScheduledFn) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  TimePoint timestamp1(false);\n  TimePoint timestamp2(false);\n  TimePoint timestamp3(false);\n  auto fn1 = std::bind(&TimePoint::reset, &timestamp1);\n  auto fn2 = std::bind(&TimePoint::reset, &timestamp2);\n  auto fn3 = std::bind(&TimePoint::reset, &timestamp3);\n  TimePoint start;\n  eb.schedule(std::move(fn1), std::chrono::milliseconds(9));\n  eb.schedule(std::move(fn2), std::chrono::milliseconds(19));\n  eb.schedule(std::move(fn3), std::chrono::milliseconds(39));\n\n  eb.loop();\n  TimePoint end;\n\n  T_CHECK_TIMEOUT(start, timestamp1, std::chrono::milliseconds(9));\n  T_CHECK_TIMEOUT(start, timestamp2, std::chrono::milliseconds(19));\n  T_CHECK_TIMEOUT(start, timestamp3, std::chrono::milliseconds(39));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(39));\n}\n\nTYPED_TEST_P(EventBaseTest, ScheduledFnAt) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n\n  TimePoint timestamp0(false);\n  TimePoint timestamp1(false);\n  TimePoint timestamp2(false);\n  TimePoint timestamp3(false);\n  auto fn0 = std::bind(&TimePoint::reset, &timestamp0);\n  auto fn1 = std::bind(&TimePoint::reset, &timestamp1);\n  auto fn2 = std::bind(&TimePoint::reset, &timestamp2);\n  auto fn3 = std::bind(&TimePoint::reset, &timestamp3);\n  TimePoint start;\n  eb.scheduleAt(fn0, eb.now() - std::chrono::milliseconds(5));\n  eb.scheduleAt(fn1, eb.now() + std::chrono::milliseconds(9));\n  eb.scheduleAt(fn2, eb.now() + std::chrono::milliseconds(19));\n  eb.scheduleAt(fn3, eb.now() + std::chrono::milliseconds(39));\n\n  TimePoint loopStart;\n  eb.loop();\n  TimePoint end;\n\n  // Even though we asked to schedule the first function in the past,\n  // in practice it doesn't run until after 1 iteration of the HHWheelTimer tick\n  // interval.\n  T_CHECK_TIMEOUT(start, timestamp0, eb.timer().getTickInterval());\n\n  T_CHECK_TIMEOUT(start, timestamp1, std::chrono::milliseconds(9));\n  T_CHECK_TIMEOUT(start, timestamp2, std::chrono::milliseconds(19));\n  T_CHECK_TIMEOUT(start, timestamp3, std::chrono::milliseconds(39));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(39));\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Test for runInThreadTestFunc()\n///////////////////////////////////////////////////////////////////////////\n\nnamespace {\n\nstruct RunInThreadData {\n  RunInThreadData(\n      folly::EventBaseBackendBase::FactoryFunc backendFactory,\n      int numThreads,\n      int opsPerThread_)\n      : evb(folly::EventBase::Options().setBackendFactory(\n            std::move(backendFactory))),\n        opsPerThread(opsPerThread_),\n        opsToGo(numThreads * opsPerThread) {}\n\n  EventBase evb;\n  std::deque<std::pair<int, int>> values;\n\n  int opsPerThread;\n  int opsToGo;\n};\n\nstruct RunInThreadArg {\n  RunInThreadArg(RunInThreadData* data_, int threadId, int value_)\n      : data(data_), thread(threadId), value(value_) {}\n\n  RunInThreadData* data;\n  int thread;\n  int value;\n};\n\nstatic inline void runInThreadTestFunc(RunInThreadArg* arg) {\n  arg->data->values.emplace_back(arg->thread, arg->value);\n  RunInThreadData* data = arg->data;\n  delete arg;\n\n  if (--data->opsToGo == 0) {\n    // Break out of the event base loop if we are the last thread running\n    data->evb.terminateLoopSoon();\n  }\n}\n\n} // namespace\n\nTYPED_TEST_P(EventBaseTest, RunInThread) {\n  constexpr uint32_t numThreads = 50;\n  constexpr uint32_t opsPerThread = 100;\n  RunInThreadData data(\n      [] { return TypeParam::getBackend(); }, numThreads, opsPerThread);\n\n  std::deque<std::thread> threads;\n  SCOPE_EXIT {\n    // Wait on all of the threads.\n    for (auto& thread : threads) {\n      thread.join();\n    }\n  };\n\n  for (uint32_t i = 0; i < numThreads; ++i) {\n    threads.emplace_back([i, &data] {\n      for (int n = 0; n < data.opsPerThread; ++n) {\n        RunInThreadArg* arg = new RunInThreadArg(&data, i, n);\n        data.evb.runInEventBaseThread(std::bind(runInThreadTestFunc, arg));\n        usleep(10);\n      }\n    });\n  }\n\n  // Add a timeout event to run after 3 seconds.\n  // Otherwise loop() will return immediately since there are no events to run.\n  // Once the last thread exits, it will stop the loop().  However, this\n  // timeout also stops the loop in case there is a bug performing the normal\n  // stop.\n  data.evb.tryRunAfterDelay(\n      std::bind(&EventBase::terminateLoopSoon, &data.evb), 3000);\n\n  TimePoint start;\n  data.evb.loop();\n  TimePoint end;\n\n  // Verify that the loop exited because all threads finished and requested it\n  // to stop.  This should happen much sooner than the 3 second timeout.\n  // Assert that it happens in under a second.  (This is still tons of extra\n  // padding.)\n\n  auto timeTaken = std::chrono::duration_cast<std::chrono::milliseconds>(\n      end.getTime() - start.getTime());\n  ASSERT_LT(timeTaken.count(), 1000);\n  VLOG(11) << \"Time taken: \" << timeTaken.count();\n\n  // Verify that we have all of the events from every thread\n  int expectedValues[numThreads];\n  for (uint32_t n = 0; n < numThreads; ++n) {\n    expectedValues[n] = 0;\n  }\n  for (const auto& dataValue : data.values) {\n    int threadID = dataValue.first;\n    int value = dataValue.second;\n    ASSERT_EQ(expectedValues[threadID], value);\n    ++expectedValues[threadID];\n  }\n  for (uint32_t n = 0; n < numThreads; ++n) {\n    ASSERT_EQ(expectedValues[n], opsPerThread);\n  }\n}\n\n//  This test simulates some calls, and verifies that the waiting happens by\n//  triggering what otherwise would be race conditions, and trying to detect\n//  whether any of the race conditions happened.\nTYPED_TEST_P(EventBaseTest, RunInEventBaseThreadAndWait) {\n  const size_t c = 256;\n  std::vector<std::unique_ptr<EventBase>> evbs;\n  for (size_t i = 0; i < c; ++i) {\n    auto evbPtr = this->makeEventBase();\n    evbs.push_back(std::move(evbPtr));\n  }\n\n  std::vector<std::unique_ptr<std::atomic<size_t>>> atoms(c);\n  for (size_t i = 0; i < c; ++i) {\n    auto& atom = atoms.at(i);\n    atom = std::make_unique<std::atomic<size_t>>(0);\n  }\n  std::vector<std::thread> threads;\n  for (size_t i = 0; i < c; ++i) {\n    threads.emplace_back([&atoms, i, evb = std::move(evbs[i])] {\n      folly::EventBase& eb = *evb;\n      auto& atom = *atoms.at(i);\n      auto ebth = std::thread([&] { eb.loopForever(); });\n      eb.waitUntilRunning();\n      eb.runInEventBaseThreadAndWait([&] {\n        size_t x = 0;\n        atom.compare_exchange_weak(\n            x, 1, std::memory_order_release, std::memory_order_relaxed);\n      });\n      size_t x = 0;\n      atom.compare_exchange_weak(\n          x, 2, std::memory_order_release, std::memory_order_relaxed);\n      eb.terminateLoopSoon();\n      ebth.join();\n    });\n  }\n  for (size_t i = 0; i < c; ++i) {\n    auto& th = threads.at(i);\n    th.join();\n  }\n  size_t sum = 0;\n  for (auto& atom : atoms) {\n    sum += *atom;\n  }\n  EXPECT_EQ(c, sum);\n}\n\nTYPED_TEST_P(EventBaseTest, RunImmediatelyOrRunInEventBaseThreadAndWaitCross) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  std::thread th(&EventBase::loopForever, &eb);\n  SCOPE_EXIT {\n    eb.terminateLoopSoon();\n    th.join();\n  };\n  auto mutated = false;\n  eb.runImmediatelyOrRunInEventBaseThreadAndWait([&] { mutated = true; });\n  EXPECT_TRUE(mutated);\n}\n\nTYPED_TEST_P(EventBaseTest, RunImmediatelyOrRunInEventBaseThreadAndWaitWithin) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  std::thread th(&EventBase::loopForever, &eb);\n  SCOPE_EXIT {\n    eb.terminateLoopSoon();\n    th.join();\n  };\n  eb.runInEventBaseThreadAndWait([&] {\n    auto mutated = false;\n    eb.runImmediatelyOrRunInEventBaseThreadAndWait([&] { mutated = true; });\n    EXPECT_TRUE(mutated);\n  });\n}\n\nTYPED_TEST_P(\n    EventBaseTest, RunImmediatelyOrRunInEventBaseThreadAndWaitNotLooping) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  auto mutated = false;\n  eb.runImmediatelyOrRunInEventBaseThreadAndWait([&] { mutated = true; });\n  EXPECT_TRUE(mutated);\n}\n\nTYPED_TEST_P(EventBaseTest, RunImmediatelyOrRunInEventBaseThreadCross) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  std::thread th(&EventBase::loopForever, &eb);\n  SCOPE_EXIT {\n    eb.terminateLoopSoon();\n    th.join();\n  };\n  // wait for loop to start\n  eb.runInEventBaseThreadAndWait([] {});\n  Baton<> baton1, baton2;\n  EXPECT_FALSE(eb.isInEventBaseThread());\n\n  eb.runImmediatelyOrRunInEventBaseThread([&] {\n    baton1.wait();\n    baton2.post();\n  });\n  EXPECT_FALSE(baton2.ready());\n  baton1.post();\n  EXPECT_TRUE(baton2.try_wait_for(std::chrono::milliseconds(100)));\n}\n\nTYPED_TEST_P(EventBaseTest, RunImmediatelyOrRunInEventBaseThreadNotLooping) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eb = *evbPtr;\n  Baton<> baton;\n  eb.runImmediatelyOrRunInEventBaseThread([&] { baton.post(); });\n  EXPECT_TRUE(baton.ready());\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Tests for runInLoop()\n///////////////////////////////////////////////////////////////////////////\n\nnamespace {\nclass CountedLoopCallback : public EventBase::LoopCallback {\n public:\n  CountedLoopCallback(\n      EventBase* eventBase,\n      unsigned int count,\n      std::function<void()> action = std::function<void()>())\n      : eventBase_(eventBase), count_(count), action_(action) {}\n\n  void runLoopCallback() noexcept override {\n    --count_;\n    if (count_ > 0) {\n      eventBase_->runInLoop(this);\n    } else if (action_) {\n      action_();\n    }\n  }\n\n  unsigned int getCount() const { return count_; }\n\n private:\n  EventBase* eventBase_;\n  unsigned int count_;\n  std::function<void()> action_;\n};\n} // namespace\n\n// Test that EventBase::loop() doesn't exit while there are\n// still LoopCallbacks remaining to be invoked.\nTYPED_TEST_P(EventBaseTest, RepeatedRunInLoop) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eventBase = *evbPtr;\n\n  CountedLoopCallback c(&eventBase, 10);\n  eventBase.runInLoop(&c);\n  // The callback shouldn't have run immediately\n  ASSERT_EQ(c.getCount(), 10);\n  eventBase.loop();\n\n  // loop() should loop until the CountedLoopCallback stops\n  // re-installing itself.\n  ASSERT_EQ(c.getCount(), 0);\n}\n\n// Test that EventBase::loop() works as expected without time measurements.\nTYPED_TEST_P(EventBaseTest, RunInLoopNoTimeMeasurement) {\n  auto evbPtr =\n      this->makeEventBase(EventBase::Options().setSkipTimeMeasurement(true));\n  folly::EventBase& eventBase = *evbPtr;\n\n  CountedLoopCallback c(&eventBase, 10);\n  eventBase.runInLoop(&c);\n  // The callback shouldn't have run immediately\n  ASSERT_EQ(c.getCount(), 10);\n  eventBase.loop();\n\n  // loop() should loop until the CountedLoopCallback stops\n  // re-installing itself.\n  ASSERT_EQ(c.getCount(), 0);\n}\n\n// Test runInLoop() calls with terminateLoopSoon()\nTYPED_TEST_P(EventBaseTest, RunInLoopStopLoop) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eventBase = *evbPtr;\n\n  CountedLoopCallback c1(&eventBase, 20);\n  CountedLoopCallback c2(\n      &eventBase, 10, std::bind(&EventBase::terminateLoopSoon, &eventBase));\n\n  eventBase.runInLoop(&c1);\n  eventBase.runInLoop(&c2);\n  ASSERT_EQ(c1.getCount(), 20);\n  ASSERT_EQ(c2.getCount(), 10);\n\n  eventBase.loopForever();\n\n  // c2 should have stopped the loop after 10 iterations\n  ASSERT_EQ(c2.getCount(), 0);\n\n  // We allow the EventBase to run the loop callbacks in whatever order it\n  // chooses.  We'll accept c1's count being either 10 (if the loop terminated\n  // after c1 ran on the 10th iteration) or 11 (if c2 terminated the loop\n  // before c1 ran).\n  //\n  // (With the current code, c1 will always run 10 times, but we don't consider\n  // this a hard API requirement.)\n  ASSERT_GE(c1.getCount(), 10);\n  ASSERT_LE(c1.getCount(), 11);\n}\n\n// Test loopPoll() call sequence\nTYPED_TEST_P(EventBaseTest, RunPollLoop) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eventBase = *evbPtr;\n  std::atomic<bool> running = true;\n  int calls = 0;\n\n  CountedLoopCallback c1(&eventBase, 20);\n  CountedLoopCallback c2(\n      &eventBase, 10, [eb = &eventBase, running = &running]() {\n        eb->terminateLoopSoon();\n        running->store(false);\n      });\n\n  eventBase.runInLoop(&c1);\n  eventBase.runInLoop(&c2);\n  ASSERT_EQ(c1.getCount(), 20);\n  ASSERT_EQ(c2.getCount(), 10);\n\n  eventBase.loopPollSetup();\n  while (running.load()) {\n    calls++;\n    eventBase.loopPoll();\n  }\n  eventBase.loopPollCleanup();\n\n  // We expect multiple iterations of the loop to happen, since loopPoll has non\n  // blocking semantics, we should call loopPoll multiple times\n  ASSERT_GT(calls, 1);\n}\n\n// Test loopPollBlocking() call sequence\nTYPED_TEST_P(EventBaseTest, RunPollLoopBlocking) {\n  auto evbPtr = this->makeEventBase();\n  folly::EventBase& eventBase = *evbPtr;\n  auto running = std::make_shared<std::atomic<bool>>(true);\n  auto wakeups = std::make_shared<std::atomic<int>>(0);\n\n  eventBase.loopPollSetup();\n\n  std::thread waker([running, wakeups, &eventBase]() {\n    while (running->load()) {\n      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n      eventBase.runInEventBaseThread([wakeups]() { (*wakeups)++; });\n    }\n  });\n\n  EventBase::LoopPollOptions options;\n  options.nonblock = false;\n  while (wakeups->load() < 5) {\n    eventBase.loopPoll(options);\n  }\n\n  running->store(false);\n  waker.join();\n  eventBase.loopPollCleanup();\n\n  ASSERT_GE(wakeups->load(), 5);\n}\n\nTYPED_TEST_P(EventBaseTest, PidCheck) {\n  auto evbPtr = this->makeEventBase();\n\n  auto deadManWalking = [&]() { evbPtr->loopForever(); };\n  EXPECT_DEATH(deadManWalking(), \"Pid mismatch\");\n}\n\nTYPED_TEST_P(EventBaseTest, MessageAvailableException) {\n  auto evbPtr = this->makeEventBase();\n\n  auto deadManWalking = [this] {\n    auto evb = this->makeEventBase();\n    std::thread t([&] {\n      // Call this from another thread to force use of NotificationQueue in\n      // runInEventBaseThread\n      evb->runInEventBaseThread([]() { throw std::runtime_error(\"boom\"); });\n    });\n    t.join();\n    evb->loopForever();\n  };\n  EXPECT_DEATH(deadManWalking(), \"boom\");\n}\n\nTYPED_TEST_P(EventBaseTest, TryRunningAfterTerminate) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& eventBase = *eventBasePtr;\n\n  bool ran = false;\n  CountedLoopCallback c1(\n      &eventBase, 1, std::bind(&EventBase::terminateLoopSoon, &eventBase));\n  eventBase.runInLoop(&c1);\n  eventBase.loopForever();\n  eventBase.runInEventBaseThread([&]() { ran = true; });\n\n  ASSERT_FALSE(ran);\n\n  eventBasePtr.reset();\n  // Loop callbacks are triggered on EventBase destruction\n  ASSERT_TRUE(ran);\n}\n\n// Test cancelling runInLoop() callbacks\nTYPED_TEST_P(EventBaseTest, CancelRunInLoop) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& eventBase = *eventBasePtr;\n\n  CountedLoopCallback c1(&eventBase, 20);\n  CountedLoopCallback c2(&eventBase, 20);\n  CountedLoopCallback c3(&eventBase, 20);\n\n  std::function<void()> cancelC1Action =\n      std::bind(&EventBase::LoopCallback::cancelLoopCallback, &c1);\n  std::function<void()> cancelC2Action =\n      std::bind(&EventBase::LoopCallback::cancelLoopCallback, &c2);\n\n  CountedLoopCallback cancelC1(&eventBase, 10, cancelC1Action);\n  CountedLoopCallback cancelC2(&eventBase, 10, cancelC2Action);\n\n  // Install cancelC1 after c1\n  eventBase.runInLoop(&c1);\n  eventBase.runInLoop(&cancelC1);\n\n  // Install cancelC2 before c2\n  eventBase.runInLoop(&cancelC2);\n  eventBase.runInLoop(&c2);\n\n  // Install c3\n  eventBase.runInLoop(&c3);\n\n  ASSERT_EQ(c1.getCount(), 20);\n  ASSERT_EQ(c2.getCount(), 20);\n  ASSERT_EQ(c3.getCount(), 20);\n  ASSERT_EQ(cancelC1.getCount(), 10);\n  ASSERT_EQ(cancelC2.getCount(), 10);\n\n  // Run the loop\n  eventBase.loop();\n\n  // cancelC1 and cancelC2 should have both fired after 10 iterations and\n  // stopped re-installing themselves\n  ASSERT_EQ(cancelC1.getCount(), 0);\n  ASSERT_EQ(cancelC2.getCount(), 0);\n  // c3 should have continued on for the full 20 iterations\n  ASSERT_EQ(c3.getCount(), 0);\n\n  // c1 and c2 should have both been cancelled on the 10th iteration.\n  //\n  // Callbacks are always run in the order they are installed,\n  // so c1 should have fired 10 times, and been canceled after it ran on the\n  // 10th iteration.  c2 should have only fired 9 times, because cancelC2 will\n  // have run before it on the 10th iteration, and cancelled it before it\n  // fired.\n  ASSERT_EQ(c1.getCount(), 10);\n  ASSERT_EQ(c2.getCount(), 11);\n}\n\nnamespace {\nclass TerminateTestCallback\n    : public EventBase::LoopCallback,\n      public EventHandler {\n public:\n  TerminateTestCallback(EventBase* eventBase, int fd)\n      : EventHandler(eventBase, NetworkSocket::fromFd(fd)),\n        eventBase_(eventBase),\n        loopInvocations_(0),\n        maxLoopInvocations_(0),\n        eventInvocations_(0),\n        maxEventInvocations_(0) {}\n\n  void reset(uint32_t maxLoopInvocations, uint32_t maxEventInvocations) {\n    loopInvocations_ = 0;\n    maxLoopInvocations_ = maxLoopInvocations;\n    eventInvocations_ = 0;\n    maxEventInvocations_ = maxEventInvocations;\n\n    cancelLoopCallback();\n    unregisterHandler();\n  }\n\n  void handlerReady(uint16_t /* events */) noexcept override {\n    // We didn't register with PERSIST, so we will have been automatically\n    // unregistered already.\n    ASSERT_FALSE(isHandlerRegistered());\n\n    ++eventInvocations_;\n    if (eventInvocations_ >= maxEventInvocations_) {\n      return;\n    }\n\n    eventBase_->runInLoop(this);\n  }\n  void runLoopCallback() noexcept override {\n    ++loopInvocations_;\n    if (loopInvocations_ >= maxLoopInvocations_) {\n      return;\n    }\n\n    registerHandler(READ);\n  }\n\n  uint32_t getLoopInvocations() const { return loopInvocations_; }\n  uint32_t getEventInvocations() const { return eventInvocations_; }\n\n private:\n  EventBase* eventBase_;\n  uint32_t loopInvocations_;\n  uint32_t maxLoopInvocations_;\n  uint32_t eventInvocations_;\n  uint32_t maxEventInvocations_;\n};\n} // namespace\n\n/**\n * Test that EventBase::loop() correctly detects when there are no more events\n * left to run.\n *\n * This uses a single callback, which alternates registering itself as a loop\n * callback versus a EventHandler callback.  This exercises a regression where\n * EventBase::loop() incorrectly exited if there were no more fd handlers\n * registered, but a loop callback installed a new fd handler.\n */\nTYPED_TEST_P(EventBaseTest, LoopTermination) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& eventBase = *eventBasePtr;\n\n  // Open a pipe and close the write end,\n  // so the read endpoint will be readable\n  int pipeFds[2];\n  int rc = fileops::pipe(pipeFds);\n  ASSERT_EQ(rc, 0);\n  fileops::close(pipeFds[1]);\n  TerminateTestCallback callback(&eventBase, pipeFds[0]);\n\n  // Test once where the callback will exit after a loop callback\n  callback.reset(10, 100);\n  eventBase.runInLoop(&callback);\n  eventBase.loop();\n  ASSERT_EQ(callback.getLoopInvocations(), 10);\n  ASSERT_EQ(callback.getEventInvocations(), 9);\n\n  // Test once where the callback will exit after an fd event callback\n  callback.reset(100, 7);\n  eventBase.runInLoop(&callback);\n  eventBase.loop();\n  ASSERT_EQ(callback.getLoopInvocations(), 7);\n  ASSERT_EQ(callback.getEventInvocations(), 7);\n\n  fileops::close(pipeFds[0]);\n}\n\nTYPED_TEST_P(EventBaseTest, CallbackOrderTest) {\n  size_t num = 0;\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& evb = *eventBasePtr;\n\n  evb.runInEventBaseThread([&]() {\n    std::thread t([&]() {\n      evb.runInEventBaseThread([&]() {\n        num++;\n        EXPECT_EQ(num, 2);\n      });\n    });\n    t.join();\n\n    // this callback will run first\n    // even if it is scheduled after the first one\n    evb.runInEventBaseThread([&]() {\n      num++;\n      EXPECT_EQ(num, 1);\n    });\n  });\n\n  evb.loop();\n  EXPECT_EQ(num, 2);\n}\n\nTYPED_TEST_P(EventBaseTest, AlwaysEnqueueCallbackOrderTest) {\n  size_t num = 0;\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& evb = *eventBasePtr;\n\n  evb.runInEventBaseThread([&]() {\n    std::thread t([&]() {\n      evb.runInEventBaseThreadAlwaysEnqueue([&]() {\n        num++;\n        EXPECT_EQ(num, 1);\n      });\n    });\n    t.join();\n\n    // this callback will run second\n    // since it was enqueued after the first one\n    evb.runInEventBaseThreadAlwaysEnqueue([&]() {\n      num++;\n      EXPECT_EQ(num, 2);\n    });\n  });\n\n  evb.loop();\n  EXPECT_EQ(num, 2);\n}\n\nTYPED_TEST_P(EventBaseTest, InternalExternalCallbackOrderTest) {\n  size_t counter = 0;\n\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& evb = *eventBasePtr;\n\n  std::vector<size_t> calls;\n\n  folly::Function<void(size_t)> runInLoopRecursive = [&](size_t left) {\n    evb.runInLoop([&, left]() mutable {\n      calls.push_back(counter++);\n      if (--left == 0) {\n        evb.terminateLoopSoon();\n        return;\n      }\n      runInLoopRecursive(left);\n    });\n  };\n\n  evb.runInEventBaseThread([&] { runInLoopRecursive(5); });\n  for (size_t i = 0; i < 49; ++i) {\n    evb.runInEventBaseThread([&] { ++counter; });\n  }\n  evb.loopForever();\n\n  EXPECT_EQ(std::vector<size_t>({9, 20, 31, 42, 53}), calls);\n}\n\n///////////////////////////////////////////////////////////////////////////\n// Tests for latency calculations\n///////////////////////////////////////////////////////////////////////////\n\nnamespace {\nclass IdleTimeTimeoutSeries : public AsyncTimeout {\n public:\n  explicit IdleTimeTimeoutSeries(\n      EventBase* base, std::deque<std::size_t>& timeout)\n      : AsyncTimeout(base), timeouts_(0), timeout_(timeout) {\n    scheduleTimeout(1);\n  }\n\n  ~IdleTimeTimeoutSeries() override {}\n\n  void timeoutExpired() noexcept override {\n    ++timeouts_;\n\n    if (timeout_.empty()) {\n      cancelTimeout();\n    } else {\n      std::size_t sleepTime = timeout_.front();\n      timeout_.pop_front();\n      if (sleepTime) {\n        usleep(sleepTime);\n      }\n      scheduleTimeout(1);\n    }\n  }\n\n  int getTimeouts() const { return timeouts_; }\n\n private:\n  int timeouts_;\n  std::deque<std::size_t>& timeout_;\n};\n} // namespace\n\n/**\n * Verify that idle time is correctly accounted for when decaying our loop\n * time.\n *\n * This works by creating a high loop time (via usleep), expecting a latency\n * callback with known value, and then scheduling a timeout for later. This\n * later timeout is far enough in the future that the idle time should have\n * caused the loop time to decay.\n */\nTYPED_TEST_P(EventBaseTest, IdleTime) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& eventBase = *eventBasePtr;\n  std::deque<std::size_t> timeouts0(4, 8080);\n  timeouts0.push_front(8000);\n  timeouts0.push_back(14000);\n  IdleTimeTimeoutSeries tos0(&eventBase, timeouts0);\n  std::deque<std::size_t> timeouts(20, 20);\n  std::unique_ptr<IdleTimeTimeoutSeries> tos;\n  bool hostOverloaded = false;\n\n  // Loop once before starting the main test.  This will run NotificationQueue\n  // callbacks that get automatically installed when the EventBase is first\n  // created.  We want to make sure they don't interfere with the timing\n  // operations below.\n  eventBase.loopOnce(EVLOOP_NONBLOCK);\n  eventBase.setLoadAvgMsec(std::chrono::milliseconds(1000));\n  eventBase.resetLoadAvg(5900.0);\n\n  int latencyCallbacks = 0;\n  eventBase.setMaxLatency(std::chrono::microseconds(6000), [&]() {\n    ++latencyCallbacks;\n    if (latencyCallbacks != 1 || tos0.getTimeouts() < 6) {\n      // This could only happen if the host this test is running\n      // on is heavily loaded.\n      hostOverloaded = true;\n      return;\n    }\n\n    EXPECT_EQ(6, tos0.getTimeouts());\n    EXPECT_GE(6100, eventBase.getAvgLoopTime() - 1200);\n    EXPECT_LE(6100, eventBase.getAvgLoopTime() + 1200);\n    tos = std::make_unique<IdleTimeTimeoutSeries>(&eventBase, timeouts);\n  });\n\n  // Kick things off with an \"immediate\" timeout\n  tos0.scheduleTimeout(1);\n\n  eventBase.loop();\n\n  if (hostOverloaded) {\n    SKIP() << \"host too heavily loaded to execute test\";\n  }\n\n  ASSERT_EQ(1, latencyCallbacks);\n  ASSERT_EQ(7, tos0.getTimeouts());\n  ASSERT_GE(5900, eventBase.getAvgLoopTime() - 1200);\n  ASSERT_LE(5900, eventBase.getAvgLoopTime() + 1200);\n  ASSERT_TRUE(!!tos);\n  ASSERT_EQ(21, tos->getTimeouts());\n}\n\nTYPED_TEST_P(EventBaseTest, MaxLatencyUndamped) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& eb = *eventBasePtr;\n  int maxDurationViolations = 0;\n  eb.setMaxLatency(\n      std::chrono::milliseconds{1}, [&]() { maxDurationViolations++; }, false);\n  eb.runInLoop(\n      [&]() {\n        /* sleep override */ std::this_thread::sleep_for(\n            std::chrono::microseconds{1001});\n        eb.terminateLoopSoon();\n      },\n      true);\n  eb.loop();\n  ASSERT_EQ(maxDurationViolations, 1);\n}\n\nTYPED_TEST_P(EventBaseTest, UnsetMaxLatencyUndamped) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& eb = *eventBasePtr;\n  int maxDurationViolations = 0;\n  eb.setMaxLatency(\n      std::chrono::milliseconds{1}, [&]() { maxDurationViolations++; }, false);\n  // Immediately unset it and make sure the counter isn't incremented. If the\n  // function gets called, this will raise an std::bad_function_call.\n  std::function<void()> bad_func = nullptr;\n  eb.setMaxLatency(std::chrono::milliseconds{0}, bad_func, false);\n  eb.runInLoop(\n      [&]() {\n        /* sleep override */ std::this_thread::sleep_for(\n            std::chrono::microseconds{1001});\n        eb.terminateLoopSoon();\n      },\n      true);\n  eb.loop();\n  ASSERT_EQ(maxDurationViolations, 0);\n}\n\n/**\n * Test that thisLoop functionality works with terminateLoopSoon\n */\nTYPED_TEST_P(EventBaseTest, ThisLoop) {\n  bool runInLoop = false;\n  bool runThisLoop = false;\n\n  {\n    auto eventBasePtr = this->makeEventBase();\n    folly::EventBase& eb = *eventBasePtr;\n    eb.runInLoop(\n        [&]() {\n          eb.terminateLoopSoon();\n          eb.runInLoop([&]() { runInLoop = true; });\n          eb.runInLoop([&]() { runThisLoop = true; }, true);\n        },\n        true);\n    eb.loopForever();\n\n    // Should not work\n    ASSERT_FALSE(runInLoop);\n    // Should work with thisLoop\n    ASSERT_TRUE(runThisLoop);\n  }\n\n  // Pending loop callbacks will be run when the EventBase is destroyed.\n  ASSERT_TRUE(runInLoop);\n}\n\nTYPED_TEST_P(EventBaseTest, EventBaseThreadLoop) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& base = *eventBasePtr;\n  bool ran = false;\n  base.runInEventBaseThread([&]() { ran = true; });\n  base.loop();\n\n  ASSERT_TRUE(ran);\n}\n\nTYPED_TEST_P(EventBaseTest, EventBaseThreadName) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& base = *eventBasePtr;\n  base.setName(\"foo\");\n  base.loop();\n\n  ASSERT_EQ(\"foo\", *getCurrentThreadName());\n}\n\nTYPED_TEST_P(EventBaseTest, RunBeforeLoop) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& base = *eventBasePtr;\n  CountedLoopCallback cb(&base, 1, [&]() { base.terminateLoopSoon(); });\n  base.runBeforeLoop(&cb);\n  base.loopForever();\n  ASSERT_EQ(cb.getCount(), 0);\n}\n\nTYPED_TEST_P(EventBaseTest, RunBeforeLoopWait) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& base = *eventBasePtr;\n  CountedLoopCallback cb(&base, 1);\n  base.tryRunAfterDelay([&]() { base.terminateLoopSoon(); }, 500);\n  base.runBeforeLoop(&cb);\n  base.loopForever();\n\n  // Check that we only ran once, and did not loop multiple times.\n  ASSERT_EQ(cb.getCount(), 0);\n}\n\nTYPED_TEST_P(EventBaseTest, RunAfterLoop) {\n  auto evb = this->makeEventBase();\n  bool cb2Ran = false;\n  // cbRan is set by a callback scheduled after cb, but cb runs last anyway.\n  CountedLoopCallback cb(evb.get(), 1, [&] { EXPECT_TRUE(cb2Ran); });\n  evb->runAfterLoop(&cb);\n  evb->runInLoop([&] { cb2Ran = true; });\n  evb->loop();\n  ASSERT_EQ(cb.getCount(), 0);\n}\n\nnamespace {\nclass PipeHandler : public EventHandler {\n public:\n  PipeHandler(EventBase* eventBase, int fd)\n      : EventHandler(eventBase, NetworkSocket::fromFd(fd)) {}\n\n  void handlerReady(uint16_t /* events */) noexcept override { abort(); }\n};\n} // namespace\n\nTYPED_TEST_P(EventBaseTest, StopBeforeLoop) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& evb = *eventBasePtr;\n\n  // Give the evb something to do.\n  int p[2];\n  ASSERT_EQ(0, fileops::pipe(p));\n  PipeHandler handler(&evb, p[0]);\n  handler.registerHandler(EventHandler::READ);\n\n  // It's definitely not running yet\n  evb.terminateLoopSoon();\n\n  // let it run, it should exit quickly.\n  std::thread t([&] { evb.loop(); });\n  t.join();\n\n  handler.unregisterHandler();\n  fileops::close(p[0]);\n  fileops::close(p[1]);\n\n  SUCCEED();\n}\n\nTYPED_TEST_P(EventBaseTest, RunCallbacksOnDestruction) {\n  bool ran = false;\n\n  {\n    auto eventBasePtr = this->makeEventBase();\n    eventBasePtr->runInEventBaseThread([&]() { ran = true; });\n  }\n\n  ASSERT_TRUE(ran);\n}\n\nTYPED_TEST_P(EventBaseTest, RunCallbacksPreDestruction) {\n  bool ranPreDestruction = false;\n  bool ranOnDestruction = false;\n  auto evbPtr = this->makeEventBase();\n  // Prevents the EventBase destruction from completing, but the pre destruction\n  // callbacks should still be called.\n  auto loopKeepAlive = getKeepAliveToken(*evbPtr);\n  evbPtr->runOnDestruction([&] { ranOnDestruction = true; });\n  evbPtr->runOnDestructionStart([&] {\n    ASSERT_FALSE(ranOnDestruction);\n    ranPreDestruction = true;\n    loopKeepAlive.reset();\n  });\n  evbPtr.reset();\n  ASSERT_TRUE(ranPreDestruction);\n  ASSERT_TRUE(ranOnDestruction);\n}\n\nTYPED_TEST_P(EventBaseTest, LoopKeepAlive) {\n  auto evbPtr = this->makeEventBase();\n\n  bool done = false;\n  std::thread t([&, loopKeepAlive = getKeepAliveToken(*evbPtr)]() mutable {\n    /* sleep override */ std::this_thread::sleep_for(\n        std::chrono::milliseconds(100));\n    evbPtr->runInEventBaseThread(\n        [&done, loopKeepAlive = std::move(loopKeepAlive)] { done = true; });\n  });\n\n  evbPtr->loop();\n\n  ASSERT_TRUE(done);\n\n  t.join();\n}\n\nTYPED_TEST_P(EventBaseTest, LoopKeepAliveInLoop) {\n  auto evbPtr = this->makeEventBase();\n\n  bool done = false;\n  std::thread t;\n\n  evbPtr->runInEventBaseThread([&] {\n    t = std::thread([&, loopKeepAlive = getKeepAliveToken(*evbPtr)]() mutable {\n      /* sleep override */ std::this_thread::sleep_for(\n          std::chrono::milliseconds(100));\n      evbPtr->runInEventBaseThread(\n          [&done, loopKeepAlive = std::move(loopKeepAlive)] { done = true; });\n    });\n  });\n\n  evbPtr->loop();\n\n  ASSERT_TRUE(done);\n\n  t.join();\n}\n\nTYPED_TEST_P(EventBaseTest, LoopKeepAliveWithLoopForever) {\n  auto evbPtr = this->makeEventBase();\n\n  bool done = false;\n\n  std::thread evThread([&] {\n    evbPtr->loopForever();\n    evbPtr.reset();\n    done = true;\n  });\n\n  {\n    auto* ev = evbPtr.get();\n    Executor::KeepAlive<EventBase> keepAlive;\n    ev->runInEventBaseThreadAndWait([&ev, &keepAlive] {\n      keepAlive = getKeepAliveToken(ev);\n    });\n    ASSERT_FALSE(done) << \"Loop finished before we asked it to\";\n    ev->terminateLoopSoon();\n    /* sleep override */\n    std::this_thread::sleep_for(std::chrono::milliseconds(30));\n    ASSERT_FALSE(done) << \"Loop terminated early\";\n    ev->runInEventBaseThread([keepAlive = std::move(keepAlive)] {});\n  }\n\n  evThread.join();\n  ASSERT_TRUE(done);\n}\n\nTYPED_TEST_P(EventBaseTest, LoopKeepAliveShutdown) {\n  auto evbPtr = this->makeEventBase();\n\n  bool done = false;\n\n  std::thread t(\n      [&done,\n       loopKeepAlive = getKeepAliveToken(evbPtr.get()),\n       evbPtrRaw = evbPtr.get()]() mutable {\n        /* sleep override */ std::this_thread::sleep_for(\n            std::chrono::milliseconds(100));\n        evbPtrRaw->runInEventBaseThread(\n            [&done, loopKeepAlive = std::move(loopKeepAlive)] { done = true; });\n      });\n\n  evbPtr.reset();\n\n  ASSERT_TRUE(done);\n\n  t.join();\n}\n\nTYPED_TEST_P(EventBaseTest, LoopKeepAliveAtomic) {\n  auto evbPtr = this->makeEventBase();\n\n  static constexpr size_t kNumThreads = 100;\n  static constexpr size_t kNumTasks = 100;\n\n  std::vector<std::thread> ts;\n  std::vector<std::unique_ptr<Baton<>>> batons;\n  size_t done{0};\n\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    batons.emplace_back(std::make_unique<Baton<>>());\n  }\n\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    ts.emplace_back(\n        [evbPtrRaw = evbPtr.get(), batonPtr = batons[i].get(), &done] {\n          std::vector<Executor::KeepAlive<EventBase>> keepAlives;\n          for (size_t j = 0; j < kNumTasks; ++j) {\n            keepAlives.emplace_back(getKeepAliveToken(evbPtrRaw));\n          }\n\n          batonPtr->post();\n\n          /* sleep override */ std::this_thread::sleep_for(\n              std::chrono::seconds(1));\n\n          for (auto& keepAlive : keepAlives) {\n            evbPtrRaw->runInEventBaseThread(\n                [&done, keepAlive = std::move(keepAlive)]() { ++done; });\n          }\n        });\n  }\n\n  for (auto& baton : batons) {\n    baton->wait();\n  }\n\n  evbPtr.reset();\n\n  EXPECT_EQ(kNumThreads * kNumTasks, done);\n\n  for (auto& t : ts) {\n    t.join();\n  }\n}\n\nTYPED_TEST_P(EventBaseTest, LoopKeepAliveCast) {\n  auto evbPtr = this->makeEventBase();\n  Executor::KeepAlive<> keepAlive = getKeepAliveToken(*evbPtr);\n}\n\nTYPED_TEST_P(EventBaseTest, LastLoopKeepAliveTriggeringDestruction) {\n  auto evbPtr = this->makeEventBase();\n  auto& evb = *evbPtr;\n  auto ka = getKeepAliveToken(evb);\n\n  // Busy wait to increase the chance of a race.\n  Baton</* MayBlock */ false> ready;\n  evb.runInEventBaseThread([&] { ready.post(); });\n\n  std::thread t([evbPtr = std::move(evbPtr)]() mutable { evbPtr->loop(); });\n\n  ready.wait();\n  ka.reset();\n  t.join();\n}\n\nTYPED_TEST_P(EventBaseTest, DrivableExecutorTest) {\n  folly::Promise<bool> p;\n  auto f = p.getFuture();\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& base = *eventBasePtr;\n  bool finished = false;\n\n  Baton baton;\n\n  std::thread t([&] {\n    baton.wait();\n    /* sleep override */\n    std::this_thread::sleep_for(std::chrono::milliseconds(500));\n    finished = true;\n    base.runInEventBaseThread([&]() { p.setValue(true); });\n  });\n\n  // Ensure drive does not busy wait\n  base.drive(); // TODO: fix notification queue init() extra wakeup\n  baton.post();\n  base.drive();\n  EXPECT_TRUE(finished);\n\n  folly::Promise<bool> p2;\n  auto f2 = p2.getFuture();\n  // Ensure waitVia gets woken up properly, even from\n  // a separate thread.\n  base.runAfterDelay([&]() { p2.setValue(true); }, 10);\n  f2.waitVia(&base);\n  EXPECT_TRUE(f2.isReady());\n\n  t.join();\n}\n\nTYPED_TEST_P(EventBaseTest, IOExecutorTest) {\n  auto evbPtr = this->makeEventBase();\n\n  // Ensure EventBase manages itself as an IOExecutor.\n  EXPECT_EQ(evbPtr->getEventBase(), evbPtr.get());\n}\n\nTYPED_TEST_P(EventBaseTest, RequestContextTest) {\n  auto evbPtr = this->makeEventBase();\n  auto defaultCtx = RequestContext::get();\n  std::weak_ptr<RequestContext> rctx_weak_ptr;\n\n  {\n    RequestContextScopeGuard rctx;\n    rctx_weak_ptr = RequestContext::saveContext();\n    auto context = RequestContext::get();\n    EXPECT_NE(defaultCtx, context);\n    evbPtr->runInLoop([context] { EXPECT_EQ(context, RequestContext::get()); });\n    evbPtr->loop();\n  }\n\n  // Ensure that RequestContext created for the scope has been released and\n  // deleted.\n  EXPECT_EQ(rctx_weak_ptr.expired(), true);\n\n  EXPECT_EQ(defaultCtx, RequestContext::get());\n}\n\nnamespace {\n\nvoid leakRC() {\n  RequestContext::setContext(std::make_shared<RequestContext>());\n}\n\n} // namespace\n\nTYPED_TEST_P(EventBaseTest, NoRequestContextLeaks) {\n  struct LeakyHandler : EventHandler {\n    explicit LeakyHandler(EventBase& evb) {\n      initHandler(&evb, folly::NetworkSocket::fromFd(sp[0]));\n      registerHandler(EventHandler::READ);\n      writeUntilFull(sp[1]);\n    }\n\n    void handlerReady(uint16_t /* events */) noexcept override {\n      ran = true;\n      leakRC();\n    }\n\n    SocketPair sp;\n    bool ran = false;\n  };\n\n  struct LeakyEventBaseObserver : EventBaseObserver {\n    uint32_t getSampleRate() const override {\n      leakRC();\n      return 1;\n    }\n    void loopSample(int64_t /* busyTime */, int64_t /* idleTime */) override {\n      leakRC();\n    }\n  };\n\n  struct LeakyExecutionObserver : ExecutionObserver {\n    void starting(\n        uintptr_t /* id */,\n        folly::ExecutionObserver::CallbackType /* callbackType */) noexcept\n        override {\n      leakRC();\n    }\n\n    void stopped(\n        uintptr_t /* id */,\n        folly::ExecutionObserver::CallbackType /* callbackType */) noexcept\n        override {\n      leakRC();\n    }\n  };\n\n  RequestContextScopeGuard rctx;\n  const auto* expectedCtx = RequestContext::try_get();\n  {\n    auto evbPtr = this->makeEventBase();\n\n    // Try very hard to leak a request context.\n    LeakyHandler handler{*evbPtr};\n    evbPtr->setObserver(std::make_shared<LeakyEventBaseObserver>());\n    evbPtr->runInEventBaseThread([&] { leakRC(); });\n    evbPtr->runInEventBaseThread([&] {\n      evbPtr->runInEventBaseThread(leakRC);\n      EXPECT_EQ(expectedCtx, RequestContext::try_get());\n      evbPtr->runImmediatelyOrRunInEventBaseThread(leakRC);\n      EXPECT_EQ(expectedCtx, RequestContext::try_get());\n      evbPtr->runImmediatelyOrRunInEventBaseThreadAndWait(leakRC);\n      EXPECT_EQ(expectedCtx, RequestContext::try_get());\n    });\n    evbPtr->runOnDestructionStart(leakRC);\n    evbPtr->runOnDestruction(leakRC);\n    evbPtr->runBeforeLoop(new EventBase::FunctionLoopCallback{leakRC});\n    evbPtr->runAfterLoop(new EventBase::FunctionLoopCallback{leakRC});\n\n    evbPtr->loop();\n    EXPECT_TRUE(handler.ran);\n    EXPECT_EQ(expectedCtx, RequestContext::try_get());\n  }\n\n  {\n    // Need separate run for LeakyExecutionObserver because it contaminates the\n    // callbacks.\n    LeakyExecutionObserver leakyExecutionObserver;\n    auto evbPtr = this->makeEventBase();\n    evbPtr->addExecutionObserver(&leakyExecutionObserver);\n    LeakyHandler handler{*evbPtr};\n    evbPtr->runInEventBaseThread([&] { leakRC(); });\n    evbPtr->runInEventBaseThread([&] { evbPtr->runInEventBaseThread(leakRC); });\n    evbPtr->loop();\n    EXPECT_EQ(expectedCtx, RequestContext::try_get());\n  }\n\n  // Also check no leaks in destruction.\n  EXPECT_EQ(expectedCtx, RequestContext::try_get());\n}\n\nTYPED_TEST_P(EventBaseTest, CancelLoopCallbackRequestContextTest) {\n  auto evbPtr = this->makeEventBase();\n  CountedLoopCallback c(evbPtr.get(), 1);\n\n  auto defaultCtx = RequestContext::get();\n  EXPECT_EQ(defaultCtx, RequestContext::get());\n  std::weak_ptr<RequestContext> rctx_weak_ptr;\n\n  {\n    RequestContextScopeGuard rctx;\n    rctx_weak_ptr = RequestContext::saveContext();\n    auto context = RequestContext::get();\n    EXPECT_NE(defaultCtx, context);\n    evbPtr->runInLoop(&c);\n    c.cancelLoopCallback();\n  }\n\n  // Ensure that RequestContext created for the scope has been released and\n  // deleted.\n  EXPECT_EQ(rctx_weak_ptr.expired(), true);\n\n  EXPECT_EQ(defaultCtx, RequestContext::get());\n}\n\nTYPED_TEST_P(EventBaseTest, TestStarvation) {\n  auto evbPtr = this->makeEventBase();\n\n  Baton<> stopRequested;\n  Baton<> stopScheduled;\n  bool stopping{false};\n  std::thread t{[&] {\n    stopRequested.wait();\n    evbPtr->add([&]() { stopping = true; });\n    stopScheduled.post();\n  }};\n\n  size_t num{0};\n  std::function<void()> fn;\n  fn = [&]() {\n    if (stopping || num >= 2000) {\n      return;\n    }\n\n    if (++num == 1000) {\n      stopRequested.post();\n      stopScheduled.wait();\n    }\n\n    evbPtr->add(fn);\n  };\n\n  evbPtr->add(fn);\n  evbPtr->loop();\n\n  EXPECT_EQ(1000, num);\n  t.join();\n}\n\nTYPED_TEST_P(EventBaseTest, RunOnDestructionBasic) {\n  bool ranOnDestruction = false;\n  {\n    auto evbPtr = this->makeEventBase();\n    evbPtr->runOnDestruction([&ranOnDestruction] { ranOnDestruction = true; });\n  }\n  EXPECT_TRUE(ranOnDestruction);\n}\n\nTYPED_TEST_P(EventBaseTest, RunOnDestructionCancelled) {\n  struct Callback : EventBase::OnDestructionCallback {\n    bool ranOnDestruction{false};\n\n    void onEventBaseDestruction() noexcept final { ranOnDestruction = true; }\n  };\n\n  auto cb = std::make_unique<Callback>();\n  {\n    auto evbPtr = this->makeEventBase();\n    evbPtr->runOnDestruction(*cb);\n    EXPECT_TRUE(cb->cancel());\n  }\n  EXPECT_FALSE(cb->ranOnDestruction);\n  EXPECT_FALSE(cb->cancel());\n}\n\nTYPED_TEST_P(EventBaseTest, RunOnDestructionAfterHandleDestroyed) {\n  auto evbPtr = this->makeEventBase();\n  {\n    bool ranOnDestruction = false;\n    auto* cb = new EventBase::FunctionOnDestructionCallback(\n        [&ranOnDestruction] { ranOnDestruction = true; });\n    evbPtr->runOnDestruction(*cb);\n    EXPECT_TRUE(cb->cancel());\n    delete cb;\n  }\n}\n\nTYPED_TEST_P(EventBaseTest, RunOnDestructionAddCallbackWithinCallback) {\n  size_t callbacksCalled = 0;\n  {\n    auto evbPtr = this->makeEventBase();\n    evbPtr->runOnDestruction([rawPtr = evbPtr.get(), &callbacksCalled] {\n      ++callbacksCalled;\n      rawPtr->runOnDestruction([&] { ++callbacksCalled; });\n    });\n  }\n  EXPECT_EQ(2, callbacksCalled);\n}\n\nTYPED_TEST_P(EventBaseTest, EventBaseExecutionObserver) {\n  auto eventBasePtr = this->makeEventBase();\n  folly::EventBase& base = *eventBasePtr;\n  bool ranBeforeLoop = false;\n  bool ran = false;\n  TestObserver observer;\n  base.addExecutionObserver(&observer);\n\n  CountedLoopCallback cb(&base, 1, [&]() { ranBeforeLoop = true; });\n  base.runBeforeLoop(&cb);\n\n  base.runInEventBaseThread([&]() {\n    base.runInEventBaseThread([&]() { ran = true; });\n  });\n  base.loop();\n\n  ASSERT_TRUE(ranBeforeLoop);\n  ASSERT_TRUE(ran);\n  ASSERT_EQ(0, observer.nestedStart_);\n  ASSERT_EQ(4, observer.numStartingCalled_);\n  ASSERT_EQ(4, observer.numStoppedCalled_);\n}\n\nTYPED_TEST_P(EventBaseTest, EventBaseObserver) {\n  auto evbPtr = this->makeEventBase();\n  auto observer1 = std::make_shared<TestEventBaseObserver>(2);\n  evbPtr->setObserver(observer1);\n  evbPtr->loopOnce();\n  evbPtr->loopOnce();\n  ASSERT_EQ(1, observer1->getNumTimesCalled());\n  evbPtr->loopOnce();\n  evbPtr->loopOnce();\n  evbPtr->loopOnce();\n  auto observer2 = std::make_shared<TestEventBaseObserver>(1);\n  evbPtr->setObserver(observer2);\n  evbPtr->loopOnce();\n  ASSERT_EQ(1, observer2->getNumTimesCalled());\n}\n\nTYPED_TEST_P(EventBaseTest, LoopRearmsNotificationQueue) {\n  auto evbPtr = this->makeEventBase();\n  std::atomic<size_t> n = 0;\n  evbPtr->runInEventBaseThread([&]() { n = 1; });\n  evbPtr->loopOnce();\n  EXPECT_EQ(n.load(), 1);\n  // The notification queue is rearmed through a loop callback, ensure that the\n  // loop executes it.\n  EXPECT_EQ(evbPtr->getNumLoopCallbacks(), 0);\n}\n\nTYPED_TEST_P(EventBaseTest, GetThreadIdCollector) {\n  auto evbPtr = this->makeEventBase();\n  auto* collector = evbPtr->getThreadIdCollector();\n  ASSERT_TRUE(collector != nullptr);\n\n  pid_t tid;\n  Baton<> ready;\n  Baton<> unblock;\n  evbPtr->runInEventBaseThread([&] {\n    tid = getOSThreadID();\n    ready.post();\n    unblock.wait(); // Wait until we acquire the keepalive.\n  });\n\n  EXPECT_THAT(collector->collectThreadIds().threadIds, testing::IsEmpty());\n\n  std::thread t{[&] { evbPtr->loopOnce(); }};\n\n  ready.wait();\n  auto ids = collector->collectThreadIds();\n  EXPECT_THAT(ids.threadIds, testing::ElementsAre(tid));\n  unblock.post();\n\n  // Until we release ids, the loop cannot complete.\n  /* sleep override */ std::this_thread::sleep_for(\n      std::chrono::milliseconds(100));\n  EXPECT_TRUE(evbPtr->isRunning());\n  // But things should eventually complete when released.\n  ids = {};\n  t.join();\n  EXPECT_FALSE(evbPtr->isRunning());\n  EXPECT_THAT(collector->collectThreadIds().threadIds, testing::IsEmpty());\n}\n\nTYPED_TEST_P(EventBaseTest, Suspension) {\n  using LoopStatus = EventBase::LoopStatus;\n\n  auto evbPtr = this->makeEventBase();\n  SKIP_IF(evbPtr->getBackend()->getPollableFd() == -1);\n\n  struct RepeatingBeforeLoopCallback : public EventBase::LoopCallback {\n    explicit RepeatingBeforeLoopCallback(EventBase& e) : evb(e) {}\n\n    void runLoopCallback() noexcept override {\n      ++count;\n      evb.runBeforeLoop(this);\n    }\n\n    EventBase& evb;\n    size_t count = 0;\n  };\n\n  RepeatingBeforeLoopCallback cb(*evbPtr);\n  evbPtr->runBeforeLoop(&cb);\n\n  auto loopAndExpectStatus = [&](LoopStatus expected) {\n    auto status = evbPtr->loopWithSuspension();\n    EXPECT_TRUE(status == expected)\n        << static_cast<int>(status) << \" != \" << static_cast<int>(expected);\n  };\n\n  folly::Executor::KeepAlive<> ka{evbPtr.get()};\n  // KeepAlive prevents loop to complete.\n  loopAndExpectStatus(LoopStatus::kSuspended);\n  // Fine to call again, run-before callbacks not invoked.\n  auto loops = cb.count;\n  loopAndExpectStatus(LoopStatus::kSuspended);\n  EXPECT_EQ(cb.count, loops);\n\n  // While suspended, the loop appears as running, but not in the current\n  // thread.\n  EXPECT_TRUE(evbPtr->isRunning());\n  EXPECT_FALSE(evbPtr->isInEventBaseThread());\n  EXPECT_FALSE(evbPtr->inRunningEventBaseThread());\n\n  bool called = false;\n  evbPtr->runInEventBaseThread([&] { called = true; });\n  loopAndExpectStatus(LoopStatus::kSuspended);\n  EXPECT_TRUE(called);\n\n  // We can make the loop complete (once) with terminateLoopSoon().\n  evbPtr->terminateLoopSoon();\n  loopAndExpectStatus(LoopStatus::kDone);\n  loopAndExpectStatus(LoopStatus::kSuspended);\n\n  // Once the keepalive is released the loop can complete every time.\n  ka.reset();\n  loopAndExpectStatus(LoopStatus::kDone);\n  loopAndExpectStatus(LoopStatus::kDone);\n\n  EXPECT_FALSE(evbPtr->isRunning());\n  EXPECT_TRUE(evbPtr->isInEventBaseThread());\n  EXPECT_FALSE(evbPtr->inRunningEventBaseThread());\n}\n\nTYPED_TEST_P(EventBaseTest, LoopCallbackTimeslice) {\n  const std::chrono::milliseconds kTimeslice{20};\n  const std::chrono::milliseconds kCbDuration{5};\n\n  // This bound should be tight, but we add some slack to account for clock\n  // resolution.\n  const size_t kMaxCbsPerIteration =\n      folly::divCeil(kTimeslice.count(), kCbDuration.count()) + 2;\n\n  auto evb = this->makeEventBase(\n      EventBase::Options().setLoopCallbacksTimeslice(kTimeslice));\n  // Disable count-based control of the notification queue, so it is only\n  // controlled by time.\n  evb->setMaxReadAtOnce(0);\n\n  // [0] is loop callbacks, [1] is notification queue callbacks.\n  size_t numCbsRun[2] = {0, 0};\n  size_t expectedNumCbsRun[2] = {0, 0};\n\n  auto cb = [&](size_t source) {\n    ++numCbsRun[source];\n    /* sleep override */ std::this_thread::sleep_for(kCbDuration);\n  };\n\n  evb->runInLoop([&] {\n    for (size_t i = 0; i < 10; ++i) {\n      evb->runInLoop([&] { cb(0); }, /* thisIteration */ true);\n      ++expectedNumCbsRun[0];\n    }\n  });\n\n  evb->loopOnce();\n  EXPECT_LE(numCbsRun[0], kMaxCbsPerIteration);\n  // At least one of the thisIteration cbs should have run in the iteration.\n  EXPECT_GE(numCbsRun[0], 1);\n  expectedNumCbsRun[0] -= numCbsRun[0];\n  numCbsRun[0] = 0;\n\n  for (size_t i = 0; i < 20; ++i) {\n    evb->runInLoop([&] { cb(0); });\n    ++expectedNumCbsRun[0];\n  }\n\n  for (size_t i = 0; i < 20; ++i) {\n    evb->runInEventBaseThreadAlwaysEnqueue([&] { cb(1); });\n    ++expectedNumCbsRun[1];\n  }\n\n  evb->loopOnce();\n  EXPECT_LE(numCbsRun[0], kMaxCbsPerIteration);\n  EXPECT_LE(numCbsRun[1], kMaxCbsPerIteration);\n  // At least one of each should have run.\n  EXPECT_GE(numCbsRun[0], 1);\n  EXPECT_GE(numCbsRun[1], 1);\n\n  // Eventually all cbs should run.\n  evb->loop();\n  EXPECT_EQ(numCbsRun[0], expectedNumCbsRun[0]);\n  EXPECT_EQ(numCbsRun[1], expectedNumCbsRun[1]);\n}\n\nTYPED_TEST_P(EventBaseTest, RunInEventBaseThreadAlwaysEnqueueNoContextSwap) {\n  // Test that runInEventBaseThreadAlwaysEnqueue with RequestContext\n  // * invokes the callback with the specified RequestContext\n  // * does not swap contexts in the current thread (no onSet/onUnset calls)\n\n  // Custom RequestData that tracks onSet/onUnset calls\n  class TrackingRequestData : public RequestData {\n   public:\n    bool hasCallback() override { return true; }\n\n    void onSet() override { ++onSetCount; }\n\n    void onUnset() override { ++onUnsetCount; }\n\n    relaxed_atomic<size_t> onSetCount{0};\n    relaxed_atomic<size_t> onUnsetCount{0};\n  };\n\n  auto evbPtr = this->makeEventBase();\n\n  // Create a custom RequestContext with tracking data\n  auto customCtx = std::make_shared<RequestContext>();\n  auto trackingData = std::make_unique<TrackingRequestData>();\n  auto* trackingDataPtr = trackingData.get();\n  RequestToken token(\"test_token\");\n  customCtx->setContextData(token, std::move(trackingData));\n\n  // Reset counters after initial setup\n  trackingDataPtr->onSetCount = 0;\n  trackingDataPtr->onUnsetCount = 0;\n\n  bool callbackRan{false};\n\n  // First baseline: RequestContextScopeGuard triggers onSet/onUnset\n  std::thread t1([&] {\n    {\n      RequestContextScopeGuard guard(customCtx);\n      EXPECT_EQ(1, trackingDataPtr->onSetCount) << \"sanity\";\n      EXPECT_EQ(0, trackingDataPtr->onUnsetCount) << \"sanity\";\n      EXPECT_EQ(customCtx.get(), RequestContext::get());\n    }\n\n    EXPECT_EQ(1, trackingDataPtr->onSetCount) << \"sanity\";\n    EXPECT_EQ(1, trackingDataPtr->onUnsetCount) << \"sanity\";\n    EXPECT_NE(customCtx.get(), RequestContext::get());\n  });\n  t1.join();\n  evbPtr->loopOnce();\n  EXPECT_EQ(1, trackingDataPtr->onSetCount);\n  EXPECT_EQ(1, trackingDataPtr->onUnsetCount);\n\n  // Reset counters for the next baseline check\n  trackingDataPtr->onSetCount = 0;\n  trackingDataPtr->onUnsetCount = 0;\n\n  // Second baseline: verify that the regular runInEventBaseThreadAlwaysEnqueue\n  // (without RC parameter) DOES swap contexts when it captures the current RC\n  std::thread t2([&] {\n    // Set the custom context in this thread\n    RequestContextScopeGuard guard(customCtx);\n\n    EXPECT_EQ(1, trackingDataPtr->onSetCount) << \"sanity\";\n    EXPECT_EQ(0, trackingDataPtr->onUnsetCount) << \"sanity\";\n    EXPECT_EQ(customCtx.get(), RequestContext::get());\n\n    // The regular overload captures the current context, which may trigger\n    // onSet/onUnset when capturing\n    evbPtr->runInEventBaseThreadAlwaysEnqueue([&] {\n      callbackRan = true;\n      EXPECT_EQ(2, trackingDataPtr->onSetCount) << \"sanity\";\n      EXPECT_EQ(1, trackingDataPtr->onUnsetCount) << \"sanity\";\n      EXPECT_EQ(customCtx.get(), RequestContext::get());\n    });\n\n    EXPECT_EQ(1, trackingDataPtr->onSetCount) << \"sanity\";\n    EXPECT_EQ(0, trackingDataPtr->onUnsetCount) << \"sanity\";\n    EXPECT_EQ(customCtx.get(), RequestContext::get());\n\n    // Note: The regular version may or may not swap context in the calling\n    // thread depending on implementation details, but we're establishing\n    // that our tracking mechanism can detect context operations\n  });\n  t2.join();\n\n  // Run the event loop to clear any pending callbacks\n  callbackRan = false;\n  evbPtr->loopOnce();\n  EXPECT_TRUE(callbackRan) << \"sanity\";\n  EXPECT_EQ(2, trackingDataPtr->onSetCount);\n  EXPECT_EQ(2, trackingDataPtr->onUnsetCount);\n\n  // Reset counters for the main test\n  trackingDataPtr->onSetCount = 0;\n  trackingDataPtr->onUnsetCount = 0;\n\n  // Now test runInEventBaseThreadAlwaysEnqueue with explicit RC parameter\n  // Call runInEventBaseThreadAlwaysEnqueue from a different thread\n  std::thread t([&] {\n    // This should NOT trigger onSet/onUnset in the current thread\n    evbPtr->runInEventBaseThreadAlwaysEnqueue(copy(customCtx), [&, customCtx] {\n      callbackRan = true;\n      EXPECT_EQ(1, trackingDataPtr->onSetCount) << \"sanity\";\n      EXPECT_EQ(0, trackingDataPtr->onUnsetCount) << \"sanity\";\n      EXPECT_EQ(customCtx.get(), RequestContext::get());\n    });\n\n    EXPECT_EQ(0, trackingDataPtr->onSetCount) << \"sanity\";\n    EXPECT_EQ(0, trackingDataPtr->onUnsetCount) << \"sanity\";\n    EXPECT_NE(customCtx.get(), RequestContext::get());\n  });\n  t.join();\n\n  // Run the event loop to execute the callback\n  callbackRan = false;\n  evbPtr->loopOnce();\n  EXPECT_TRUE(callbackRan) << \"sanity\";\n  EXPECT_EQ(1, trackingDataPtr->onSetCount);\n  EXPECT_EQ(1, trackingDataPtr->onUnsetCount);\n}\n\nstruct BackendProviderBase {\n  static bool isIoUringBackend() { return false; }\n};\n\nREGISTER_TYPED_TEST_SUITE_P(\n    EventBaseTest,\n    EventBaseThread,\n    ReadEvent,\n    ReadPersist,\n    ReadImmediate,\n    WriteEvent,\n    WritePersist,\n    WriteImmediate,\n    ReadWrite,\n    WriteRead,\n    ReadWriteSimultaneous,\n    ReadWritePersist,\n    ReadPartial,\n    WritePartial,\n    DestroyingHandler,\n    HandlerSideEffects,\n    RunAfterDelay,\n    RunAfterDelayDestruction,\n    BasicTimeouts,\n    ReuseTimeout,\n    RescheduleTimeout,\n    CancelTimeout,\n    DestroyingTimeout,\n    ScheduledFn,\n    ScheduledFnAt,\n    RunInThread,\n    RunInEventBaseThreadAndWait,\n    RunImmediatelyOrRunInEventBaseThreadAndWaitCross,\n    RunImmediatelyOrRunInEventBaseThreadAndWaitWithin,\n    RunImmediatelyOrRunInEventBaseThreadAndWaitNotLooping,\n    RunImmediatelyOrRunInEventBaseThreadCross,\n    RunImmediatelyOrRunInEventBaseThreadNotLooping,\n    RepeatedRunInLoop,\n    RunInLoopNoTimeMeasurement,\n    RunInLoopStopLoop,\n    RunPollLoop,\n    RunPollLoopBlocking,\n    MessageAvailableException,\n    TryRunningAfterTerminate,\n    CancelRunInLoop,\n    LoopTermination,\n    CallbackOrderTest,\n    AlwaysEnqueueCallbackOrderTest,\n    IdleTime,\n    MaxLatencyUndamped,\n    UnsetMaxLatencyUndamped,\n    ThisLoop,\n    EventBaseThreadLoop,\n    EventBaseThreadName,\n    RunBeforeLoop,\n    RunBeforeLoopWait,\n    RunAfterLoop,\n    StopBeforeLoop,\n    RunCallbacksPreDestruction,\n    RunCallbacksOnDestruction,\n    LoopKeepAlive,\n    LoopKeepAliveInLoop,\n    LoopKeepAliveWithLoopForever,\n    LoopKeepAliveShutdown,\n    LoopKeepAliveAtomic,\n    LoopKeepAliveCast,\n    LastLoopKeepAliveTriggeringDestruction,\n    EventBaseObserver,\n    LoopRearmsNotificationQueue,\n    GetThreadIdCollector,\n    Suspension,\n    DrivableExecutorTest,\n    IOExecutorTest,\n    RequestContextTest,\n    NoRequestContextLeaks,\n    CancelLoopCallbackRequestContextTest,\n    TestStarvation,\n    RunOnDestructionBasic,\n    RunOnDestructionCancelled,\n    RunOnDestructionAfterHandleDestroyed,\n    RunOnDestructionAddCallbackWithinCallback,\n    InternalExternalCallbackOrderTest,\n    PidCheck,\n    EventBaseExecutionObserver,\n    LoopCallbackTimeslice,\n    RunInEventBaseThreadAlwaysEnqueueNoContextSwap);\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/EventHandlerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/EventHandler.h>\n\n#include <sys/eventfd.h>\n\n#include <bitset>\n#include <future>\n#include <thread>\n\n#include <folly/MPMCQueue.h>\n#include <folly/ScopeGuard.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/portability/GMock.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Sockets.h>\n\nusing namespace std;\nusing namespace folly;\nusing namespace testing;\n\nvoid runInThreadsAndWait(size_t nthreads, function<void(size_t)> cb) {\n  vector<thread> threads(nthreads);\n  for (size_t i = 0; i < nthreads; ++i) {\n    threads[i] = thread(cb, i);\n  }\n  for (size_t i = 0; i < nthreads; ++i) {\n    threads[i].join();\n  }\n}\n\nvoid runInThreadsAndWait(vector<function<void()>> cbs) {\n  runInThreadsAndWait(cbs.size(), [&](size_t k) { cbs[k](); });\n}\n\nclass EventHandlerMock : public EventHandler {\n public:\n  EventHandlerMock(EventBase* eb, int fd)\n      : EventHandler(eb, NetworkSocket::fromFd(fd)) {}\n  // gmock can't mock noexcept methods, so we need an intermediary\n  MOCK_METHOD(void, _handlerReady, (uint16_t));\n  void handlerReady(uint16_t events) noexcept override {\n    _handlerReady(events);\n  }\n};\n\nclass EventHandlerTest : public Test {\n public:\n  int efd = 0;\n\n  void SetUp() override {\n    efd = eventfd(0, EFD_SEMAPHORE);\n    ASSERT_THAT(efd, Gt(0));\n  }\n\n  void TearDown() override {\n    if (efd > 0) {\n      fileops::close(efd);\n    }\n    efd = 0;\n  }\n\n  void efd_write(uint64_t val) { fileops::write(efd, &val, sizeof(val)); }\n\n  uint64_t efd_read() {\n    uint64_t val = 0;\n    fileops::read(efd, &val, sizeof(val));\n    return val;\n  }\n};\n\nTEST_F(EventHandlerTest, simple) {\n  const size_t writes = 4;\n  size_t readsRemaining = writes;\n\n  EventBase eb;\n  EventHandlerMock eh(&eb, efd);\n  eh.registerHandler(EventHandler::READ | EventHandler::PERSIST);\n  EXPECT_CALL(eh, _handlerReady(_))\n      .Times(writes)\n      .WillRepeatedly(Invoke([&](uint16_t /* events */) {\n        efd_read();\n        if (--readsRemaining == 0) {\n          eh.unregisterHandler();\n        }\n      }));\n  efd_write(writes);\n  eb.loop();\n\n  EXPECT_EQ(0, readsRemaining);\n}\n\nTEST_F(EventHandlerTest, many_concurrent_producers) {\n  const size_t writes = 200;\n  const size_t nproducers = 20;\n  size_t readsRemaining = writes;\n\n  runInThreadsAndWait({\n      [&] {\n        EventBase eb;\n        EventHandlerMock eh(&eb, efd);\n        eh.registerHandler(EventHandler::READ | EventHandler::PERSIST);\n        EXPECT_CALL(eh, _handlerReady(_))\n            .Times(writes)\n            .WillRepeatedly(Invoke([&](uint16_t /* events */) {\n              efd_read();\n              if (--readsRemaining == 0) {\n                eh.unregisterHandler();\n              }\n            }));\n        eb.loop();\n      },\n      [&] {\n        runInThreadsAndWait(nproducers, [&](size_t /* k */) {\n          for (size_t i = 0; i < writes / nproducers; ++i) {\n            this_thread::sleep_for(std::chrono::milliseconds(1));\n            efd_write(1);\n          }\n        });\n      },\n  });\n\n  EXPECT_EQ(0, readsRemaining);\n}\n\nTEST_F(EventHandlerTest, many_concurrent_consumers) {\n  const size_t writes = 200;\n  const size_t nproducers = 8;\n  const size_t nconsumers = 20;\n  atomic<size_t> writesRemaining(writes);\n  atomic<size_t> readsRemaining(writes);\n\n  MPMCQueue<nullptr_t> queue(writes / 10);\n\n  runInThreadsAndWait({\n      [&] {\n        runInThreadsAndWait(nconsumers, [&](size_t /* k */) {\n          size_t thReadsRemaining = writes / nconsumers;\n          EventBase eb;\n          EventHandlerMock eh(&eb, efd);\n          eh.registerHandler(EventHandler::READ | EventHandler::PERSIST);\n          EXPECT_CALL(eh, _handlerReady(_))\n              .WillRepeatedly(Invoke([&](uint16_t /* events */) {\n                nullptr_t val;\n                if (!queue.readIfNotEmpty(val)) {\n                  return;\n                }\n                efd_read();\n                --readsRemaining;\n                if (--thReadsRemaining == 0) {\n                  eh.unregisterHandler();\n                }\n              }));\n          eb.loop();\n        });\n      },\n      [&] {\n        runInThreadsAndWait(nproducers, [&](size_t /* k */) {\n          for (size_t i = 0; i < writes / nproducers; ++i) {\n            this_thread::sleep_for(std::chrono::milliseconds(1));\n            queue.blockingWrite(nullptr);\n            efd_write(1);\n            --writesRemaining;\n          }\n        });\n      },\n  });\n\n  EXPECT_EQ(0, writesRemaining);\n  EXPECT_EQ(0, readsRemaining);\n}\n\n#ifdef EV_PRI\n//\n// See rfc6093 for extensive discussion on TCP URG semantics. Specificaly,\n// it points out that URG mechanism was never intended to be used\n// for out-of-band information delivery. However, pretty much every\n// implementation interprets the LAST octect or urgent data as the\n// OOB byte.\n//\nclass EventHandlerOobTest : public ::testing::Test {\n public:\n  //\n  // Wait for port number to connect to, then connect and invoke\n  // clientOps(fd) where fd is the connection file descriptor\n  //\n  void runClient(std::function<void(int fd)> clientOps) {\n    clientThread = std::thread(\n        [serverPortFuture = serverReady.get_future(), clientOps]() mutable {\n          int clientFd = socket(AF_INET, SOCK_STREAM, 0);\n          SCOPE_EXIT {\n            fileops::close(clientFd);\n          };\n          struct hostent* he{nullptr};\n          struct sockaddr_in server;\n\n          std::array<const char, 10> hostname = {\"localhost\"};\n          he = gethostbyname(hostname.data());\n          PCHECK(he);\n\n          memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);\n          server.sin_family = AF_INET;\n\n          // block here until port is known\n          server.sin_port = serverPortFuture.get();\n          LOG(INFO) << \"Server is ready\";\n\n          PCHECK(\n              ::connect(clientFd, (struct sockaddr*)&server, sizeof(server)) ==\n              0);\n          LOG(INFO) << \"Server connection available\";\n\n          clientOps(clientFd);\n        });\n  }\n\n  //\n  // Bind, get port number, pass it to client, listen/accept and store the\n  // accepted fd\n  //\n  void acceptConn() {\n    // make the server.\n    int listenfd = socket(AF_INET, SOCK_STREAM, 0);\n    SCOPE_EXIT {\n      fileops::close(listenfd);\n    };\n    PCHECK(listenfd != -1) << \"unable to open socket\";\n\n    struct sockaddr_in sin;\n    sin.sin_port = htons(0);\n    sin.sin_addr.s_addr = INADDR_ANY;\n    sin.sin_family = AF_INET;\n\n    PCHECK(::bind(listenfd, (struct sockaddr*)&sin, sizeof(sin)) >= 0)\n        << \"Can't bind to port\";\n    listen(listenfd, 5);\n\n    struct sockaddr_in findSockName;\n    socklen_t sz = sizeof(findSockName);\n    getsockname(listenfd, (struct sockaddr*)&findSockName, &sz);\n    serverReady.set_value(findSockName.sin_port);\n\n    struct sockaddr_in cli_addr;\n    socklen_t clilen = sizeof(cli_addr);\n    serverFd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);\n    PCHECK(serverFd >= 0) << \"can't accept\";\n  }\n\n  void SetUp() override {}\n\n  void TearDown() override {\n    clientThread.join();\n    fileops::close(serverFd);\n  }\n\n  EventBase eb;\n  std::thread clientThread;\n  std::promise<decltype(sockaddr_in::sin_port)> serverReady;\n  int serverFd{-1};\n};\n\n//\n// Test that sending OOB data is detected by event handler\n//\nTEST_F(EventHandlerOobTest, EPOLLPRI) {\n  auto clientOps = [](int fd) {\n    char buffer[] = \"banana\";\n    const auto n = send(fd, buffer, strlen(buffer) + 1, MSG_OOB);\n    LOG(INFO) << \"Client send finished\";\n    PCHECK(n > 0);\n  };\n\n  runClient(clientOps);\n  acceptConn();\n\n  struct SockEvent : public EventHandler {\n    SockEvent(EventBase* eb, int fd)\n        : EventHandler(eb, NetworkSocket::fromFd(fd)), fd_(fd) {}\n\n    void handlerReady(uint16_t events) noexcept override {\n      EXPECT_TRUE(EventHandler::EventFlags::PRI & events);\n      std::array<char, 255> buffer;\n      auto n = fileops::read(fd_, buffer.data(), buffer.size());\n      //\n      // NB: we sent 7 bytes, but only received 6. The last byte\n      // has been stored in the OOB buffer.\n      //\n      EXPECT_EQ(6, n);\n      EXPECT_EQ(\"banana\", std::string(buffer.data(), 6));\n      // now read the byte stored in OOB buffer\n      n = recv(fd_, buffer.data(), buffer.size(), MSG_OOB);\n      EXPECT_EQ(1, n);\n    }\n\n   private:\n    int fd_;\n  } sockHandler(&eb, serverFd);\n\n  sockHandler.registerHandler(EventHandler::EventFlags::PRI);\n  LOG(INFO) << \"Registered Handler\";\n  eb.loop();\n}\n\n//\n// Test if we can send an OOB byte and then normal data\n//\nTEST_F(EventHandlerOobTest, OOB_AND_NORMAL_DATA) {\n  auto clientOps = [](int sockfd) {\n    {\n      // OOB buffer can only hold one byte in most implementations\n      std::array<char, 2> buffer = {\"X\"};\n      const auto n = send(sockfd, buffer.data(), 1, MSG_OOB);\n      PCHECK(n > 0);\n    }\n\n    {\n      std::array<char, 7> buffer = {\"banana\"};\n      const auto n = send(sockfd, buffer.data(), buffer.size(), 0);\n      PCHECK(n > 0);\n    }\n  };\n\n  runClient(clientOps);\n  acceptConn();\n\n  struct SockEvent : public EventHandler {\n    SockEvent(EventBase* eb, int fd)\n        : EventHandler(eb, NetworkSocket::fromFd(fd)), eb_(eb), fd_(fd) {}\n\n    void handlerReady(uint16_t events) noexcept override {\n      std::array<char, 255> buffer;\n      if (events & EventHandler::EventFlags::PRI) {\n        const auto n = recv(fd_, buffer.data(), buffer.size(), MSG_OOB);\n        EXPECT_EQ(1, n);\n        EXPECT_EQ(\"X\", std::string(buffer.data(), 1));\n        registerHandler(EventHandler::EventFlags::READ);\n        return;\n      }\n\n      if (events & EventHandler::EventFlags::READ) {\n        const auto n = recv(fd_, buffer.data(), buffer.size(), 0);\n        EXPECT_EQ(7, n);\n        EXPECT_EQ(\"banana\", std::string(buffer.data()));\n        eb_->terminateLoopSoon();\n        return;\n      }\n    }\n\n   private:\n    EventBase* eb_;\n    int fd_;\n  } sockHandler(&eb, serverFd);\n  sockHandler.registerHandler(\n      EventHandler::EventFlags::PRI | EventHandler::EventFlags::READ);\n  LOG(INFO) << \"Registered Handler\";\n  eb.loopForever();\n}\n\n//\n// Demonstrate that \"regular\" reads ignore the OOB byte sent to us\n//\nTEST_F(EventHandlerOobTest, SWALLOW_OOB) {\n  auto clientOps = [](int sockfd) {\n    {\n      std::array<char, 2> buffer = {\"X\"};\n      const auto n = send(sockfd, buffer.data(), 1, MSG_OOB);\n      PCHECK(n > 0);\n    }\n\n    {\n      std::array<char, 7> buffer = {\"banana\"};\n      const auto n = send(sockfd, buffer.data(), buffer.size(), 0);\n      PCHECK(n > 0);\n    }\n  };\n\n  runClient(clientOps);\n  acceptConn();\n\n  struct SockEvent : public EventHandler {\n    SockEvent(EventBase* eb, int fd)\n        : EventHandler(eb, NetworkSocket::fromFd(fd)), fd_(fd) {}\n\n    void handlerReady(uint16_t events) noexcept override {\n      std::array<char, 255> buffer;\n      ASSERT_TRUE(events & EventHandler::EventFlags::READ);\n      const auto n = recv(fd_, buffer.data(), buffer.size(), 0);\n      EXPECT_EQ(7, n);\n      EXPECT_EQ(\"banana\", std::string(buffer.data()));\n    }\n\n   private:\n    int fd_;\n  } sockHandler(&eb, serverFd);\n  sockHandler.registerHandler(EventHandler::EventFlags::READ);\n  LOG(INFO) << \"Registered Handler\";\n  eb.loop();\n}\n#endif\n"
  },
  {
    "path": "folly/io/async/test/HHWheelTimerHighResBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/io/async/STTimerFDTimeoutManager.h>\n#include <folly/io/async/TimerFDTimeoutManager.h>\n#include <folly/io/async/test/UndelayedDestruction.h>\n\nusing namespace folly;\nusing std::chrono::microseconds;\nusing std::chrono::milliseconds;\n\nclass TestTimeoutMs : public HHWheelTimer::Callback {\n public:\n  TestTimeoutMs() = default;\n  ~TestTimeoutMs() override = default;\n\n  void timeoutExpired() noexcept override {}\n\n  void callbackCanceled() noexcept override {}\n};\n\nusing StackWheelTimerMs = UndelayedDestruction<HHWheelTimer>;\n\nclass TestTimeoutUs : public HHWheelTimerHighRes::Callback {\n public:\n  TestTimeoutUs() = default;\n  ~TestTimeoutUs() override = default;\n\n  void timeoutExpired() noexcept override {}\n\n  void callbackCanceled() noexcept override {}\n};\n\nusing StackWheelTimerUs = UndelayedDestruction<HHWheelTimerHighRes>;\n\nclass TestTimeoutDirectUs : public TimerFDTimeoutManager::Callback {\n public:\n  TestTimeoutDirectUs() = default;\n  ~TestTimeoutDirectUs() override = default;\n\n  void timeoutExpired() noexcept override {}\n\n  void callbackCanceled() noexcept override {}\n};\n\nunsigned int scheduleCancelTimersMs(unsigned int iters, unsigned int timers) {\n  BenchmarkSuspender susp;\n\n  EventBase evb;\n  StackWheelTimerMs t(&evb, milliseconds(1));\n  std::vector<TestTimeoutMs> timeouts(timers);\n\n  susp.dismiss();\n  for (unsigned int i = 0; i < iters; ++i) {\n    for (unsigned int j = 0; j < timers; ++j) {\n      t.scheduleTimeout(&timeouts[j], milliseconds(5 * (j + 1)));\n    }\n\n    for (unsigned int j = 0; j < timers; ++j) {\n      timeouts[j].cancelTimeout();\n    }\n  }\n  susp.rehire();\n\n  return iters;\n}\n\nunsigned int scheduleCancelTimersUs(unsigned int iters, unsigned int timers) {\n  BenchmarkSuspender susp;\n\n  EventBase evb;\n  STTimerFDTimeoutManager timeoutMgr(&evb);\n  StackWheelTimerUs t(&timeoutMgr, microseconds(20));\n  std::vector<TestTimeoutUs> timeouts(timers);\n\n  susp.dismiss();\n  for (unsigned int i = 0; i < iters; ++i) {\n    for (unsigned int j = 0; j < timers; ++j) {\n      t.scheduleTimeout(&timeouts[j], microseconds(5000 * (j + 1)));\n    }\n\n    for (unsigned int j = 0; j < timers; ++j) {\n      timeouts[j].cancelTimeout();\n    }\n  }\n  susp.rehire();\n\n  return iters;\n}\n\nunsigned int scheduleCancelTimersDirectUs(\n    unsigned int iters, unsigned int timers) {\n  BenchmarkSuspender susp;\n\n  EventBase evb;\n  TimerFDTimeoutManager t(&evb);\n  std::vector<TestTimeoutDirectUs> timeouts(timers);\n\n  susp.dismiss();\n  for (unsigned int i = 0; i < iters; ++i) {\n    for (unsigned int j = 0; j < timers; ++j) {\n      t.scheduleTimeout(&timeouts[j], microseconds(5000 * (j + 1)));\n    }\n\n    for (unsigned int j = 0; j < timers; ++j) {\n      timeouts[j].cancelTimeout();\n    }\n  }\n  susp.rehire();\n\n  return iters;\n}\n\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_1, 1)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_1, 1)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_1, 1)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_16, 16)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_16, 16)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_16, 16)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_64, 64)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_64, 64)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_64, 64)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_128, 128)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_128, 128)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_128, 128)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_512, 512)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_512, 512)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_512, 512)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_1024, 1024)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_1024, 1024)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_1024, 1024)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_4096, 4096)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_4096, 4096)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_4096, 4096)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM_MULTI(scheduleCancelTimersMs, ms_8192, 9182)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(scheduleCancelTimersUs, us_8192, 9182)\nBENCHMARK_RELATIVE_NAMED_PARAM_MULTI(\n    scheduleCancelTimersDirectUs, direct_us_8192, 9182)\nBENCHMARK_DRAW_LINE();\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n\n  return 0;\n}\n"
  },
  {
    "path": "folly/io/async/test/HHWheelTimerHighResTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/HHWheelTimer.h>\n#include <folly/io/async/STTimerFDTimeoutManager.h>\n#include <folly/io/async/test/UndelayedDestruction.h>\n#include <folly/io/async/test/Util.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing std::chrono::microseconds;\n\nusing StackWheelTimer = UndelayedDestruction<HHWheelTimerHighRes>;\n\nclass TestTimeout : public HHWheelTimerHighRes::Callback {\n public:\n  TestTimeout() {}\n  TestTimeout(HHWheelTimerHighRes* t, microseconds timeout) {\n    t->scheduleTimeout(this, timeout);\n  }\n\n  void timeoutExpired() noexcept override {\n    timestamps.emplace_back();\n    if (fn) {\n      fn();\n    }\n  }\n\n  void callbackCanceled() noexcept override {\n    canceledTimestamps.emplace_back();\n    if (fn) {\n      fn();\n    }\n  }\n\n  std::deque<TimePoint> timestamps;\n  std::deque<TimePoint> canceledTimestamps;\n  std::function<void()> fn;\n};\n\nstruct HHWheelTimerHighResTest : public ::testing::Test {\n  HHWheelTimerHighResTest() : timeoutMgr(&evb) {}\n\n  EventBase evb;\n  STTimerFDTimeoutManager timeoutMgr;\n};\n\n/*\n * Test firing some simple timeouts that are fired once and never rescheduled\n */\nTEST_F(HHWheelTimerHighResTest, FireOnce) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n\n  TestTimeout t1;\n  TestTimeout t2;\n  TestTimeout t3;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, microseconds(50));\n  t.scheduleTimeout(&t2, microseconds(50));\n  // Verify scheduling it twice cancels, then schedules.\n  // Should only get one callback.\n  t.scheduleTimeout(&t2, microseconds(50));\n  t.scheduleTimeout(&t3, microseconds(100));\n\n  ASSERT_EQ(t.count(), 3);\n\n  TestTimeout ts;\n  ts.fn = [&]() { evb.terminateLoopSoon(); };\n  t.scheduleTimeout(&ts, microseconds(1000));\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t3.timestamps.size(), 1);\n\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], microseconds(500));\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], microseconds(500));\n  T_CHECK_TIMEOUT(start, t3.timestamps[0], microseconds(10));\n  T_CHECK_TIMEOUT(start, end, microseconds(10));\n}\n\n/*\n * Test scheduling a timeout from another timeout callback.\n */\nTEST_F(HHWheelTimerHighResTest, TestSchedulingWithinCallback) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n\n  TestTimeout t1, t2;\n\n  t.scheduleTimeout(&t1, microseconds(500 * 1000));\n  t1.fn = [&] {\n    t.scheduleTimeout(&t2, microseconds(1000));\n    std::this_thread::sleep_for(std::chrono::microseconds(5000));\n  };\n\n  t2.fn = [&] { evb.terminateLoopSoon(); };\n\n  ASSERT_EQ(t.count(), 1);\n\n  evb.loop();\n\n  ASSERT_EQ(t.count(), 0);\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n}\n\n/*\n * Test changing default-timeout in timer.\n */\nTEST_F(HHWheelTimerHighResTest, TestSetDefaultTimeout) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n\n  t.setDefaultTimeout(microseconds(1000));\n  // verify: default-time has been modified\n  ASSERT_EQ(t.getDefaultTimeout(), microseconds(1000));\n}\n\n/*\n * Test cancelling a timeout when it is scheduled to be fired right away.\n */\n\nTEST_F(HHWheelTimerHighResTest, CancelTimeout) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n\n  // Create several timeouts that will all fire in 5microseconds\n  TestTimeout t5_1(&t, microseconds(50));\n  TestTimeout t5_2(&t, microseconds(50));\n  TestTimeout t5_3(&t, microseconds(50));\n  TestTimeout t5_4(&t, microseconds(50));\n  TestTimeout t5_5(&t, microseconds(50));\n\n  // Also create a few timeouts to fire in 10microseconds\n  TestTimeout t10_1(&t, microseconds(100));\n  TestTimeout t10_2(&t, microseconds(100));\n  TestTimeout t10_3(&t, microseconds(100));\n\n  TestTimeout t20_1(&t, microseconds(200));\n  TestTimeout t20_2(&t, microseconds(200));\n\n  TestTimeout et(&t, microseconds(1000));\n  et.fn = [&] { evb.terminateLoopSoon(); };\n\n  // Have t5_1 cancel t5_2 and t5_4.\n  //\n  // Cancelling t5_2 will test cancelling a timeout that is at the head of the\n  // list and ready to be fired.\n  //\n  // Cancelling t5_4 will test cancelling a timeout in the middle of the list\n  t5_1.fn = [&] {\n    t5_2.cancelTimeout();\n    t5_4.cancelTimeout();\n  };\n\n  // Have t5_3 cancel t5_5.\n  // This will test cancelling the last remaining timeout.\n  //\n  // Then have t5_3 reschedule itself.\n  t5_3.fn = [&] {\n    t5_5.cancelTimeout();\n    // Reset our function so we won't continually reschedule ourself\n    std::function<void()> fnDtorGuard;\n    t5_3.fn.swap(fnDtorGuard);\n    t.scheduleTimeout(&t5_3, microseconds(500));\n\n    // Also test cancelling timeouts in another timeset that isn't ready to\n    // fire yet.\n    //\n    // Cancel the middle timeout in ts10.\n    t10_2.cancelTimeout();\n    // Cancel both the timeouts in ts20.\n    t20_1.cancelTimeout();\n    t20_2.cancelTimeout();\n  };\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t5_1.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t5_1.timestamps[0], microseconds(500));\n\n  ASSERT_EQ(t5_3.timestamps.size(), 2);\n  T_CHECK_TIMEOUT(start, t5_3.timestamps[0], microseconds(500));\n  T_CHECK_TIMEOUT(t5_3.timestamps[0], t5_3.timestamps[1], microseconds(500));\n\n  ASSERT_EQ(t10_1.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t10_1.timestamps[0], microseconds(10));\n  ASSERT_EQ(t10_3.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t10_3.timestamps[0], microseconds(10));\n\n  // Cancelled timeouts\n  ASSERT_EQ(t5_2.timestamps.size(), 0);\n  ASSERT_EQ(t5_4.timestamps.size(), 0);\n  ASSERT_EQ(t5_5.timestamps.size(), 0);\n  ASSERT_EQ(t10_2.timestamps.size(), 0);\n  ASSERT_EQ(t20_1.timestamps.size(), 0);\n  ASSERT_EQ(t20_2.timestamps.size(), 0);\n\n  T_CHECK_TIMEOUT(start, end, microseconds(10));\n}\n\n/*\n * Test destroying a HHWheelTimerHighRes with timeouts outstanding\n */\n\nTEST_F(HHWheelTimerHighResTest, DestroyTimeoutSet) {\n  HHWheelTimerHighRes::UniquePtr t(\n      HHWheelTimerHighRes::newTimer(&timeoutMgr, microseconds(100)));\n\n  TestTimeout t5_1(t.get(), microseconds(50));\n  TestTimeout t5_2(t.get(), microseconds(50));\n  TestTimeout t5_3(t.get(), microseconds(50));\n\n  TestTimeout t10_1(t.get(), microseconds(100));\n  TestTimeout t10_2(t.get(), microseconds(100));\n\n  // Have t5_2 destroy t\n  // Note that this will call destroy() inside t's timeoutExpired()\n  // method.\n  t5_2.fn = [&] {\n    t5_3.cancelTimeout();\n    t5_1.cancelTimeout();\n    t10_1.cancelTimeout();\n    t10_2.cancelTimeout();\n    t.reset();\n    evb.terminateLoopSoon();\n  };\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t5_1.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t5_1.timestamps[0], microseconds(500));\n  ASSERT_EQ(t5_2.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t5_2.timestamps[0], microseconds(500));\n\n  ASSERT_EQ(t5_3.timestamps.size(), 0);\n  ASSERT_EQ(t10_1.timestamps.size(), 0);\n  ASSERT_EQ(t10_2.timestamps.size(), 0);\n\n  T_CHECK_TIMEOUT(start, end, microseconds(500));\n}\n\n/*\n * Test an event scheduled before the last event fires on time\n */\nTEST_F(HHWheelTimerHighResTest, SlowFast) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, microseconds(100));\n  t.scheduleTimeout(&t2, microseconds(50));\n\n  ASSERT_EQ(t.count(), 2);\n\n  TestTimeout et(&t, microseconds(1000));\n  et.fn = [&] { evb.terminateLoopSoon(); };\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], microseconds(100));\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], microseconds(50));\n}\n\nTEST_F(HHWheelTimerHighResTest, ReschedTest) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, microseconds(12800));\n  TimePoint start2;\n  t1.fn = [&]() {\n    t.scheduleTimeout(&t2, microseconds(25500));\n    start2.reset();\n    ASSERT_EQ(t.count(), 2); // we scheduled et\n  };\n\n  ASSERT_EQ(t.count(), 1);\n\n  TestTimeout et(&t, microseconds(100000));\n  et.fn = [&] { evb.terminateLoopSoon(); };\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], microseconds(12800));\n  T_CHECK_TIMEOUT(start2, t2.timestamps[0], microseconds(25500));\n}\n\nTEST_F(HHWheelTimerHighResTest, DeleteWheelInTimeout) {\n  auto t = HHWheelTimerHighRes::newTimer(&timeoutMgr, microseconds(100));\n\n  TestTimeout t1;\n  TestTimeout t2;\n  TestTimeout t3;\n\n  ASSERT_EQ(t->count(), 0);\n\n  t->scheduleTimeout(&t1, microseconds(128));\n  t->scheduleTimeout(&t2, microseconds(128));\n  t->scheduleTimeout(&t3, microseconds(128));\n  t1.fn = [&]() { t2.cancelTimeout(); };\n  t3.fn = [&]() {\n    t.reset();\n    evb.terminateLoopSoon();\n  };\n\n  ASSERT_EQ(t->count(), 3);\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], microseconds(128));\n}\n\n/*\n * Test scheduling a mix of timers with default timeout and variable timeout.\n */\nTEST_F(HHWheelTimerHighResTest, DefaultTimeout) {\n  microseconds defaultTimeout(microseconds(500));\n  StackWheelTimer t(\n      &timeoutMgr,\n      microseconds(100),\n      AsyncTimeout::InternalEnum::NORMAL,\n      defaultTimeout);\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n  ASSERT_EQ(t.getDefaultTimeout(), defaultTimeout);\n\n  t.scheduleTimeout(&t1);\n  t.scheduleTimeout(&t2, microseconds(10));\n\n  ASSERT_EQ(t.count(), 2);\n\n  TestTimeout et(&t, microseconds(1000));\n  et.fn = [&] { evb.terminateLoopSoon(); };\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], defaultTimeout);\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], microseconds(10));\n  T_CHECK_TIMEOUT(start, end, microseconds(10));\n}\n\nTEST_F(HHWheelTimerHighResTest, lambda) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n  size_t count = 0;\n  t.scheduleTimeoutFn([&] { count++; }, microseconds(100));\n\n  TestTimeout et(&t, microseconds(1000));\n  et.fn = [&] { evb.terminateLoopSoon(); };\n\n  evb.loop();\n  EXPECT_EQ(1, count);\n}\n\n// shouldn't crash because we swallow and log the error (you'll have to look\n// at the console to confirm logging)\nTEST_F(HHWheelTimerHighResTest, lambdaThrows) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n  size_t count = 0;\n  t.scheduleTimeoutFn(\n      [&] {\n        count++;\n        throw std::runtime_error(\"expected\");\n      },\n      microseconds(100));\n  TestTimeout et(&t, microseconds(1000));\n  et.fn = [&] { evb.terminateLoopSoon(); };\n\n  evb.loop();\n  // make sure the callback was invoked\n  EXPECT_EQ(1, count);\n}\n\nTEST_F(HHWheelTimerHighResTest, cancelAll) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n  TestTimeout t1;\n  TestTimeout t2;\n  t.scheduleTimeout(&t1, std::chrono::microseconds(1000));\n  t.scheduleTimeout(&t2, std::chrono::microseconds(1000));\n  size_t canceled = 0;\n  t1.fn = [&] { canceled += t.cancelAll(); };\n  t2.fn = [&] { canceled += t.cancelAll(); };\n  // Sleep 20ms to ensure both timeouts will fire in a single event (in case\n  // they ended up in different slots)\n  ::usleep(20000);\n\n  evb.scheduleAt(\n      [&]() { evb.terminateLoopSoon(); },\n      std::chrono::steady_clock::now() + std::chrono::milliseconds(100));\n\n  evb.loop();\n  EXPECT_EQ(1, t1.canceledTimestamps.size() + t2.canceledTimestamps.size());\n  EXPECT_EQ(1, canceled);\n}\n\nTEST_F(HHWheelTimerHighResTest, IntrusivePtr) {\n  HHWheelTimerHighRes::UniquePtr t(\n      HHWheelTimerHighRes::newTimer(&timeoutMgr, microseconds(100)));\n\n  TestTimeout t1;\n  TestTimeout t2;\n  TestTimeout t3;\n\n  ASSERT_EQ(t->count(), 0);\n\n  t->scheduleTimeout(&t1, microseconds(500));\n  t->scheduleTimeout(&t2, microseconds(500));\n\n  DelayedDestruction::IntrusivePtr<HHWheelTimerHighRes> s(t);\n\n  s->scheduleTimeout(&t3, microseconds(10));\n\n  ASSERT_EQ(t->count(), 3);\n\n  // Kill the UniquePtr, but the SharedPtr keeps it alive\n  t.reset();\n\n  evb.scheduleAt(\n      [&]() { evb.terminateLoopSoon(); },\n      std::chrono::steady_clock::now() + std::chrono::milliseconds(10));\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t3.timestamps.size(), 1);\n\n  ASSERT_EQ(s->count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], microseconds(500));\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], microseconds(500));\n  T_CHECK_TIMEOUT(start, t3.timestamps[0], microseconds(10));\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(10)); // this is right\n}\n\nTEST_F(HHWheelTimerHighResTest, GetTimeRemaining) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n  TestTimeout t1;\n\n  // Not scheduled yet, time remaining should be zero\n  ASSERT_EQ(t1.getTimeRemaining(), microseconds(0));\n  ASSERT_EQ(t.count(), 0);\n\n  // Scheduled, time remaining should be less than or equal to the scheduled\n  // timeout\n  t.scheduleTimeout(&t1, microseconds(10));\n  ASSERT_LE(t1.getTimeRemaining(), microseconds(10));\n  t1.fn = [&] { evb.terminateLoopSoon(); };\n\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n\n  // Expired and time remaining should be zero\n  ASSERT_EQ(t1.getTimeRemaining(), microseconds(0));\n\n  ASSERT_EQ(t.count(), 0);\n  T_CHECK_TIMEOUT(start, end, microseconds(10));\n}\n\nTEST_F(HHWheelTimerHighResTest, prematureTimeout) {\n  StackWheelTimer t(&timeoutMgr, microseconds(10));\n  TestTimeout t1;\n  TestTimeout t2;\n  // Schedule the timeout for the nextTick of timer\n  t.scheduleTimeout(&t1, std::chrono::microseconds(100));\n  // Make sure that time is past that tick.\n  ::usleep(10000);\n  // Schedule the timeout for the +255 tick, due to sleep above it will overlap\n  // with what would be ran on the next timeoutExpired of the timer.\n  auto timeout = std::chrono::microseconds(2555);\n  t.scheduleTimeout(&t2, std::chrono::microseconds(2555));\n  t2.fn = [&] { evb.terminateLoopSoon(); };\n  auto start = std::chrono::steady_clock::now();\n  evb.loop();\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  auto elapsedUs = std::chrono::duration_cast<std::chrono::microseconds>(\n      t2.timestamps[0].getTime() - start);\n  EXPECT_GE(elapsedUs.count(), timeout.count());\n}\n\nTEST_F(HHWheelTimerHighResTest, Level1) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n  TestTimeout tt;\n  // Schedule the timeout for the tick in a next epoch.\n  t.scheduleTimeout(&tt, std::chrono::microseconds(500));\n  tt.fn = [&] { evb.terminateLoopSoon(); };\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n  ASSERT_EQ(tt.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, end, microseconds(500));\n}\n\n// Test that we handle negative timeouts properly (i.e. treat them as 0)\nTEST_F(HHWheelTimerHighResTest, NegativeTimeout) {\n  StackWheelTimer t(&timeoutMgr, microseconds(100));\n  std::this_thread::sleep_for(std::chrono::microseconds(10));\n  TestTimeout tt1;\n  TestTimeout tt2;\n  // Make sure we have event scheduled.\n  t.scheduleTimeout(&tt1, std::chrono::microseconds(100));\n  // Schedule another timeout that would appear to be earlier than\n  // the already scheduled one.\n  t.scheduleTimeout(&tt2, std::chrono::microseconds(-500000000));\n\n  evb.scheduleAt(\n      [&]() { evb.terminateLoopSoon(); },\n      std::chrono::steady_clock::now() + std::chrono::milliseconds(1));\n  TimePoint start;\n  evb.loop();\n  TimePoint end;\n  ASSERT_EQ(tt2.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, end, std::chrono::milliseconds(10));\n}\n"
  },
  {
    "path": "folly/io/async/test/HHWheelTimerSlowTests.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <thread>\n#include <vector>\n\n#include <folly/Random.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/HHWheelTimer.h>\n#include <folly/io/async/test/UndelayedDestruction.h>\n#include <folly/io/async/test/Util.h>\n#include <folly/portability/GTest.h>\n\nusing namespace folly;\nusing std::chrono::milliseconds;\n\nusing StackWheelTimer = UndelayedDestruction<HHWheelTimer>;\n\nclass TestTimeout : public HHWheelTimer::Callback {\n public:\n  TestTimeout() {}\n  TestTimeout(HHWheelTimer* t, milliseconds timeout) {\n    t->scheduleTimeout(this, timeout);\n  }\n\n  void timeoutExpired() noexcept override {\n    timestamps.emplace_back();\n    if (fn) {\n      fn();\n    }\n  }\n\n  void callbackCanceled() noexcept override {\n    canceledTimestamps.emplace_back();\n    if (fn) {\n      fn();\n    }\n  }\n\n  std::deque<TimePoint> timestamps;\n  std::deque<TimePoint> canceledTimestamps;\n  std::function<void()> fn;\n};\n\nstruct HHWheelTimerTest : public ::testing::Test {\n  EventBase eventBase;\n};\n\n/* Test takes ~2.5 minutes to run */\nTEST_F(HHWheelTimerTest, Level2) {\n  HHWheelTimer& t = eventBase.timer();\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, milliseconds(605 * 256));\n  t.scheduleTimeout(&t2, milliseconds(300 * 256));\n\n  ASSERT_EQ(t.count(), 2);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  // Check that the timeout was delayed by sleep\n  T_CHECK_TIMEOUT(\n      start,\n      t1.timestamps[0],\n      milliseconds(605 * 256),\n      milliseconds(256 * 256));\n  T_CHECK_TIMEOUT(\n      start,\n      t2.timestamps[0],\n      milliseconds(300 * 256),\n      milliseconds(256 * 256));\n}\n\n/*\n * Test the tick interval parameter\n */\nTEST_F(HHWheelTimerTest, AtMostEveryN) {\n  // Create a timeout set with a 10ms interval, to fire no more than once\n  // every 3ms.\n  milliseconds interval(10);\n  milliseconds atMostEveryN(3);\n  StackWheelTimer t(&eventBase, atMostEveryN);\n\n  // Create 60 timeouts to be added to ts1 at 1ms intervals.\n  uint32_t numTimeouts = 60;\n  std::vector<TestTimeout> timeouts(numTimeouts);\n\n  // Create a scheduler timeout to add the timeouts 1ms apart.\n  uint32_t index = 0;\n  StackWheelTimer ts1(&eventBase, milliseconds(1));\n  TestTimeout scheduler(&ts1, milliseconds(1));\n  scheduler.fn = [&] {\n    if (index >= numTimeouts) {\n      return;\n    }\n    // Call timeoutExpired() on the timeout so it will record a timestamp.\n    // This is done only so we can record when we scheduled the timeout.\n    // This way if ts1 starts to fall behind a little over time we will still\n    // be comparing the ts1 timeouts to when they were first scheduled (rather\n    // than when we intended to schedule them).  The scheduler may fall behind\n    // eventually since we don't really schedule it once every millisecond.\n    // Each time it finishes we schedule it for 1 millisecond in the future.\n    // The amount of time it takes to run, and any delays it encounters\n    // getting scheduled may eventually add up over time.\n    timeouts[index].timeoutExpired();\n\n    // Schedule the new timeout\n    t.scheduleTimeout(&timeouts[index], interval);\n    // Reschedule ourself\n    ts1.scheduleTimeout(&scheduler, milliseconds(1));\n    ++index;\n  };\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  // This should take roughly 60 + 10 ms to finish. If it takes more than\n  // 250 ms to finish the system is probably heavily loaded, so skip.\n  if (std::chrono::duration_cast<std::chrono::milliseconds>(\n          end.getTime() - start.getTime())\n          .count() > 250) {\n    LOG(WARNING) << \"scheduling all timeouts takes too long\";\n    return;\n  }\n\n  // We scheduled timeouts 1ms apart, when the HHWheelTimer is only allowed\n  // to wake up at most once every 3ms.  It will therefore wake up every 3ms\n  // and fire groups of approximately 3 timeouts at a time.\n  //\n  // This is \"approximately 3\" since it may get slightly behind and fire 4 in\n  // one interval, etc.  T_CHECK_TIMEOUT normally allows a few milliseconds of\n  // tolerance.  We have to add the same into our checking algorithm here.\n  for (uint32_t idx = 0; idx < numTimeouts; ++idx) {\n    ASSERT_EQ(timeouts[idx].timestamps.size(), 2);\n\n    TimePoint scheduledTime(timeouts[idx].timestamps[0]);\n    TimePoint firedTime(timeouts[idx].timestamps[1]);\n\n    // Assert that the timeout fired at roughly the right time.\n    // T_CHECK_TIMEOUT() normally has a tolerance of 5ms.  Allow an additional\n    // atMostEveryN.\n    milliseconds tolerance = milliseconds(5) + interval;\n    T_CHECK_TIMEOUT(scheduledTime, firedTime, atMostEveryN, tolerance);\n\n    // Assert that the difference between the previous timeout and now was\n    // either very small (fired in the same event loop), or larger than\n    // atMostEveryN.\n    if (idx == 0) {\n      // no previous value\n      continue;\n    }\n    TimePoint prev(timeouts[idx - 1].timestamps[1]);\n\n    auto delta = (firedTime.getTimeStart() - prev.getTimeEnd()) -\n        (firedTime.getTimeWaiting() - prev.getTimeWaiting());\n    if (delta > milliseconds(1)) {\n      T_CHECK_TIMEOUT(prev, firedTime, atMostEveryN);\n    }\n  }\n}\n\n/*\n * Test an event loop that is blocking\n */\n\nTEST_F(HHWheelTimerTest, SlowLoop) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n\n  eventBase.runInLoop([]() {\n    /* sleep override */\n    std::this_thread::sleep_for(std::chrono::microseconds(10000));\n  });\n  t.scheduleTimeout(&t1, milliseconds(5));\n\n  ASSERT_EQ(t.count(), 1);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  // Check that the timeout was delayed by sleep\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(10), milliseconds(1));\n  T_CHECK_TIMEOUT(start, end, milliseconds(10), milliseconds(1));\n\n  eventBase.runInLoop([]() {\n    /* sleep override */\n    std::this_thread::sleep_for(std::chrono::microseconds(10000));\n  });\n  t.scheduleTimeout(&t2, milliseconds(5));\n\n  ASSERT_EQ(t.count(), 1);\n\n  TimePoint start2;\n  eventBase.loop();\n  TimePoint end2;\n\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  // Check that the timeout was NOT delayed by sleep\n  T_CHECK_TIMEOUT(start2, t2.timestamps[0], milliseconds(10), milliseconds(1));\n  T_CHECK_TIMEOUT(start2, end2, milliseconds(10), milliseconds(1));\n}\n\n/*\n * Test upper timer levels.  Slow by necessity :/\n */\n\nTEST_F(HHWheelTimerTest, Level1) {\n  HHWheelTimer& t = eventBase.timer();\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, milliseconds(605));\n  t.scheduleTimeout(&t2, milliseconds(300));\n\n  ASSERT_EQ(t.count(), 2);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  // Check that the timeout was delayed by sleep\n  T_CHECK_TIMEOUT(\n      start, t1.timestamps[0], milliseconds(605), milliseconds(256));\n  T_CHECK_TIMEOUT(\n      start, t2.timestamps[0], milliseconds(300), milliseconds(256));\n}\n\nTEST_F(HHWheelTimerTest, Stress) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n\n  long timeoutcount = 10000;\n  TestTimeout timeouts[10000];\n  long runtimeouts = 0;\n  for (long i = 0; i < timeoutcount; i++) {\n    long timeout = Random::rand32(1, 10000);\n    if (Random::rand32(3)) {\n      // NOTE: hhwheel timer runs before eventbase runAfterDelay,\n      // so runAfterDelay cancelTimeout() must run  at least one timerwheel\n      // before scheduleTimeout, to ensure it runs first.\n      timeout += 256;\n      t.scheduleTimeout(&timeouts[i], std::chrono::milliseconds(timeout));\n      eventBase.runAfterDelay(\n          [&, i]() {\n            timeouts[i].fn = nullptr;\n            timeouts[i].cancelTimeout();\n            runtimeouts++;\n            LOG(INFO) << \"Ran \" << runtimeouts << \" timeouts, cancelled\";\n          },\n          timeout - 256);\n      timeouts[i].fn = [&, i, timeout]() {\n        LOG(INFO) << \"FAIL:timer \" << i << \" still fired in \" << timeout;\n        ADD_FAILURE();\n      };\n    } else {\n      t.scheduleTimeout(&timeouts[i], std::chrono::milliseconds(timeout));\n      timeouts[i].fn = [&, i]() {\n        timeoutcount++;\n        long newtimeout = Random::rand32(1, 10000);\n        t.scheduleTimeout(&timeouts[i], std::chrono::milliseconds(newtimeout));\n        runtimeouts++;\n        /* sleep override */ usleep(1000);\n        LOG(INFO) << \"Ran \" << runtimeouts << \" timeouts of \" << timeoutcount;\n        timeouts[i].fn = [&]() {\n          runtimeouts++;\n          LOG(INFO) << \"Ran \" << runtimeouts << \" timeouts of \" << timeoutcount;\n        };\n      };\n    }\n  }\n\n  LOG(INFO) << \"RUNNING TEST\";\n  eventBase.loop();\n\n  EXPECT_EQ(runtimeouts, timeoutcount);\n}\n"
  },
  {
    "path": "folly/io/async/test/HHWheelTimerTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/HHWheelTimer.h>\n\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/test/UndelayedDestruction.h>\n#include <folly/io/async/test/Util.h>\n#include <folly/portability/GTest.h>\n#include <folly/portability/Unistd.h>\n\nusing namespace folly;\nusing std::chrono::milliseconds;\n\nusing StackWheelTimer = UndelayedDestruction<HHWheelTimer>;\n\nclass TestTimeout : public HHWheelTimer::Callback {\n public:\n  TestTimeout() {}\n  TestTimeout(HHWheelTimer* t, milliseconds timeout) {\n    t->scheduleTimeout(this, timeout);\n  }\n\n  void timeoutExpired() noexcept override {\n    timestamps.emplace_back();\n    if (fn) {\n      fn();\n    }\n  }\n\n  void callbackCanceled() noexcept override {\n    canceledTimestamps.emplace_back();\n    if (fn) {\n      fn();\n    }\n  }\n\n  std::deque<TimePoint> timestamps;\n  std::deque<TimePoint> canceledTimestamps;\n  std::function<void()> fn;\n};\n\nstruct HHWheelTimerTest : public ::testing::Test {\n  EventBase eventBase;\n};\n\n/*\n * Test firing some simple timeouts that are fired once and never rescheduled\n */\nTEST_F(HHWheelTimerTest, FireOnce) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n\n  TestTimeout t1;\n  TestTimeout t2;\n  TestTimeout t3;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, milliseconds(20));\n  t.scheduleTimeout(&t2, milliseconds(20));\n  // Verify scheduling it twice cancels, then schedules.\n  // Should only get one callback.\n  t.scheduleTimeout(&t2, milliseconds(20));\n  t.scheduleTimeout(&t3, milliseconds(40));\n\n  ASSERT_EQ(t.count(), 3);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t3.timestamps.size(), 1);\n\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(20));\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], milliseconds(20));\n  T_CHECK_TIMEOUT(start, t3.timestamps[0], milliseconds(40));\n  T_CHECK_TIMEOUT(start, end, milliseconds(40));\n}\n\nTEST_F(HHWheelTimerTest, NoRequestContextLeak) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n  std::set<int> destructed;\n\n  class TestData : public RequestData {\n   public:\n    TestData(int data, std::set<int>& destructed)\n        : data_(data), destructed_(destructed) {}\n    ~TestData() override { destructed_.insert(data_); }\n\n    bool hasCallback() override { return false; }\n\n   private:\n    int data_;\n    std::set<int>& destructed_;\n  };\n\n  folly::Optional<TestTimeout> t1 = TestTimeout{};\n  folly::Optional<TestTimeout> t2 = TestTimeout{};\n\n  {\n    RequestContextScopeGuard g;\n    RequestContext::get()->setContextData(\n        \"k\", std::make_unique<TestData>(1, destructed));\n    t.scheduleTimeout(&*t1, milliseconds(5));\n  }\n\n  {\n    RequestContextScopeGuard g;\n    RequestContext::get()->setContextData(\n        \"k\", std::make_unique<TestData>(2, destructed));\n    t.scheduleTimeout(&*t2, milliseconds(5));\n  }\n\n  EXPECT_EQ(0, destructed.size());\n  t1.reset();\n  EXPECT_TRUE(destructed.contains(1));\n  EXPECT_FALSE(destructed.contains(2));\n}\n\n/*\n * Test scheduling a timeout from another timeout callback.\n */\nTEST_F(HHWheelTimerTest, TestSchedulingWithinCallback) {\n  HHWheelTimer& t = eventBase.timer();\n\n  TestTimeout t1, t2;\n\n  t.scheduleTimeout(&t1, milliseconds(500));\n  t1.fn = [&] {\n    t.scheduleTimeout(&t2, milliseconds(1));\n    std::this_thread::sleep_for(std::chrono::milliseconds(5));\n  };\n  // If t is in an inconsistent state, detachEventBase should fail.\n  t2.fn = [&] { t.detachEventBase(); };\n\n  ASSERT_EQ(t.count(), 1);\n\n  eventBase.loop();\n\n  ASSERT_EQ(t.count(), 0);\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n}\n\n/*\n * Test changing default-timeout in timer.\n */\nTEST_F(HHWheelTimerTest, TestSetDefaultTimeout) {\n  HHWheelTimer& t = eventBase.timer();\n\n  t.setDefaultTimeout(milliseconds(1000));\n  // verify: default-time has been modified\n  ASSERT_EQ(t.getDefaultTimeout(), milliseconds(1000));\n}\n\n/*\n * Test cancelling a timeout when it is scheduled to be fired right away.\n */\n\nTEST_F(HHWheelTimerTest, CancelTimeout) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n\n  // Create several timeouts that will all fire in 5ms.\n  TestTimeout t5_1(&t, milliseconds(5));\n  TestTimeout t5_2(&t, milliseconds(5));\n  TestTimeout t5_3(&t, milliseconds(5));\n  TestTimeout t5_4(&t, milliseconds(5));\n  TestTimeout t5_5(&t, milliseconds(5));\n\n  // Also create a few timeouts to fire in 10ms\n  TestTimeout t10_1(&t, milliseconds(10));\n  TestTimeout t10_2(&t, milliseconds(10));\n  TestTimeout t10_3(&t, milliseconds(10));\n\n  TestTimeout t20_1(&t, milliseconds(20));\n  TestTimeout t20_2(&t, milliseconds(20));\n\n  // Have t5_1 cancel t5_2 and t5_4.\n  //\n  // Cancelling t5_2 will test cancelling a timeout that is at the head of the\n  // list and ready to be fired.\n  //\n  // Cancelling t5_4 will test cancelling a timeout in the middle of the list\n  t5_1.fn = [&] {\n    t5_2.cancelTimeout();\n    t5_4.cancelTimeout();\n  };\n\n  // Have t5_3 cancel t5_5.\n  // This will test cancelling the last remaining timeout.\n  //\n  // Then have t5_3 reschedule itself.\n  t5_3.fn = [&] {\n    t5_5.cancelTimeout();\n    // Reset our function so we won't continually reschedule ourself\n    std::function<void()> fnDtorGuard;\n    t5_3.fn.swap(fnDtorGuard);\n    t.scheduleTimeout(&t5_3, milliseconds(5));\n\n    // Also test cancelling timeouts in another timeset that isn't ready to\n    // fire yet.\n    //\n    // Cancel the middle timeout in ts10.\n    t10_2.cancelTimeout();\n    // Cancel both the timeouts in ts20.\n    t20_1.cancelTimeout();\n    t20_2.cancelTimeout();\n  };\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t5_1.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t5_1.timestamps[0], milliseconds(5));\n\n  ASSERT_EQ(t5_3.timestamps.size(), 2);\n  T_CHECK_TIMEOUT(start, t5_3.timestamps[0], milliseconds(5));\n  T_CHECK_TIMEOUT(t5_3.timestamps[0], t5_3.timestamps[1], milliseconds(5));\n\n  ASSERT_EQ(t10_1.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t10_1.timestamps[0], milliseconds(10));\n  ASSERT_EQ(t10_3.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t10_3.timestamps[0], milliseconds(10));\n\n  // Cancelled timeouts\n  ASSERT_EQ(t5_2.timestamps.size(), 0);\n  ASSERT_EQ(t5_4.timestamps.size(), 0);\n  ASSERT_EQ(t5_5.timestamps.size(), 0);\n  ASSERT_EQ(t10_2.timestamps.size(), 0);\n  ASSERT_EQ(t20_1.timestamps.size(), 0);\n  ASSERT_EQ(t20_2.timestamps.size(), 0);\n\n  T_CHECK_TIMEOUT(start, end, milliseconds(10));\n}\n\n/*\n * Test destroying a HHWheelTimer with timeouts outstanding\n */\n\nTEST_F(HHWheelTimerTest, DestroyTimeoutSet) {\n  HHWheelTimer::UniquePtr t(\n      HHWheelTimer::newTimer(&eventBase, milliseconds(1)));\n\n  TestTimeout t5_1(t.get(), milliseconds(5));\n  TestTimeout t5_2(t.get(), milliseconds(5));\n  TestTimeout t5_3(t.get(), milliseconds(5));\n\n  TestTimeout t10_1(t.get(), milliseconds(10));\n  TestTimeout t10_2(t.get(), milliseconds(10));\n\n  // Have t5_2 destroy t\n  // Note that this will call destroy() inside t's timeoutExpired()\n  // method.\n  t5_2.fn = [&] {\n    t5_3.cancelTimeout();\n    t5_1.cancelTimeout();\n    t10_1.cancelTimeout();\n    t10_2.cancelTimeout();\n    t.reset();\n  };\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t5_1.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t5_1.timestamps[0], milliseconds(5));\n  ASSERT_EQ(t5_2.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, t5_2.timestamps[0], milliseconds(5));\n\n  ASSERT_EQ(t5_3.timestamps.size(), 0);\n  ASSERT_EQ(t10_1.timestamps.size(), 0);\n  ASSERT_EQ(t10_2.timestamps.size(), 0);\n\n  T_CHECK_TIMEOUT(start, end, milliseconds(5));\n}\n\n/*\n * Test an event scheduled before the last event fires on time\n */\nTEST_F(HHWheelTimerTest, SlowFast) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, milliseconds(10));\n  t.scheduleTimeout(&t2, milliseconds(5));\n\n  ASSERT_EQ(t.count(), 2);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(10));\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], milliseconds(5));\n}\n\nTEST_F(HHWheelTimerTest, ReschedTest) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n\n  t.scheduleTimeout(&t1, milliseconds(128));\n  TimePoint start2;\n  t1.fn = [&]() {\n    t.scheduleTimeout(&t2, milliseconds(255)); // WHEEL_SIZE - 1\n    start2.reset();\n    ASSERT_EQ(t.count(), 1);\n  };\n\n  ASSERT_EQ(t.count(), 1);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(128));\n  T_CHECK_TIMEOUT(start2, t2.timestamps[0], milliseconds(255));\n}\n\nTEST_F(HHWheelTimerTest, DeleteWheelInTimeout) {\n  auto t = HHWheelTimer::newTimer(&eventBase, milliseconds(1));\n\n  TestTimeout t1;\n  TestTimeout t2;\n  TestTimeout t3;\n\n  ASSERT_EQ(t->count(), 0);\n\n  t->scheduleTimeout(&t1, milliseconds(128));\n  t->scheduleTimeout(&t2, milliseconds(128));\n  t->scheduleTimeout(&t3, milliseconds(128));\n  t1.fn = [&]() { t2.cancelTimeout(); };\n  t3.fn = [&]() { t.reset(); };\n\n  ASSERT_EQ(t->count(), 3);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(128));\n}\n\n/*\n * Test scheduling a mix of timers with default timeout and variable timeout.\n */\nTEST_F(HHWheelTimerTest, DefaultTimeout) {\n  milliseconds defaultTimeout(milliseconds(5));\n  StackWheelTimer t(\n      &eventBase,\n      milliseconds(1),\n      AsyncTimeout::InternalEnum::NORMAL,\n      defaultTimeout);\n\n  TestTimeout t1;\n  TestTimeout t2;\n\n  ASSERT_EQ(t.count(), 0);\n  ASSERT_EQ(t.getDefaultTimeout(), defaultTimeout);\n\n  t.scheduleTimeout(&t1);\n  t.scheduleTimeout(&t2, milliseconds(10));\n\n  ASSERT_EQ(t.count(), 2);\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n\n  ASSERT_EQ(t.count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], defaultTimeout);\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], milliseconds(10));\n  T_CHECK_TIMEOUT(start, end, milliseconds(10));\n}\n\nTEST_F(HHWheelTimerTest, lambda) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n  size_t count = 0;\n  t.scheduleTimeoutFn([&] { count++; }, milliseconds(1));\n  eventBase.loop();\n  EXPECT_EQ(1, count);\n}\n\n// shouldn't crash because we swallow and log the error (you'll have to look\n// at the console to confirm logging)\nTEST_F(HHWheelTimerTest, lambdaThrows) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n  t.scheduleTimeoutFn(\n      [&] { throw std::runtime_error(\"expected\"); }, milliseconds(1));\n  eventBase.loop();\n}\n\nTEST_F(HHWheelTimerTest, cancelAll) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n  TestTimeout t1;\n  TestTimeout t2;\n  t.scheduleTimeout(&t1, std::chrono::milliseconds(1));\n  t.scheduleTimeout(&t2, std::chrono::milliseconds(1));\n  size_t canceled = 0;\n  t1.fn = [&] { canceled += t.cancelAll(); };\n  t2.fn = [&] { canceled += t.cancelAll(); };\n  // Sleep 20ms to ensure both timeouts will fire in a single event (in case\n  // they ended up in different slots)\n  ::usleep(20000);\n  eventBase.loop();\n  EXPECT_EQ(1, t1.canceledTimestamps.size() + t2.canceledTimestamps.size());\n  EXPECT_EQ(1, canceled);\n}\n\nTEST_F(HHWheelTimerTest, IntrusivePtr) {\n  HHWheelTimer::UniquePtr t(\n      HHWheelTimer::newTimer(&eventBase, milliseconds(1)));\n\n  TestTimeout t1;\n  TestTimeout t2;\n  TestTimeout t3;\n\n  ASSERT_EQ(t->count(), 0);\n\n  t->scheduleTimeout(&t1, milliseconds(5));\n  t->scheduleTimeout(&t2, milliseconds(5));\n\n  DelayedDestruction::IntrusivePtr<HHWheelTimer> s(t);\n\n  s->scheduleTimeout(&t3, milliseconds(10));\n\n  ASSERT_EQ(t->count(), 3);\n\n  // Kill the UniquePtr, but the SharedPtr keeps it alive\n  t.reset();\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  ASSERT_EQ(t1.timestamps.size(), 1);\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  ASSERT_EQ(t3.timestamps.size(), 1);\n\n  ASSERT_EQ(s->count(), 0);\n\n  T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(5));\n  T_CHECK_TIMEOUT(start, t2.timestamps[0], milliseconds(5));\n  T_CHECK_TIMEOUT(start, t3.timestamps[0], milliseconds(10));\n  T_CHECK_TIMEOUT(start, end, milliseconds(10));\n}\n\nTEST_F(HHWheelTimerTest, GetTimeRemaining) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n  TestTimeout t1;\n\n  // Not scheduled yet, time remaining should be zero\n  ASSERT_EQ(t1.getTimeRemaining(), milliseconds(0));\n  ASSERT_EQ(t.count(), 0);\n\n  // Scheduled, time remaining should be less than or equal to the scheduled\n  // timeout\n  t.scheduleTimeout(&t1, milliseconds(10));\n  ASSERT_LE(t1.getTimeRemaining(), milliseconds(10));\n\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n\n  // Expired and time remaining should be zero\n  ASSERT_EQ(t1.getTimeRemaining(), milliseconds(0));\n\n  ASSERT_EQ(t.count(), 0);\n  T_CHECK_TIMEOUT(start, end, milliseconds(10));\n}\n\nTEST_F(HHWheelTimerTest, prematureTimeout) {\n  StackWheelTimer t(&eventBase, milliseconds(10));\n  TestTimeout t1;\n  TestTimeout t2;\n  // Schedule the timeout for the nextTick of timer\n  t.scheduleTimeout(&t1, std::chrono::milliseconds(1));\n  // Make sure that time is past that tick.\n  ::usleep(10000);\n  // Schedule the timeout for the +255 tick, due to sleep above it will overlap\n  // with what would be ran on the next timeoutExpired of the timer.\n  auto timeout = std::chrono::milliseconds(2555);\n  t.scheduleTimeout(&t2, std::chrono::milliseconds(2555));\n  auto start = std::chrono::steady_clock::now();\n  eventBase.loop();\n  ASSERT_EQ(t2.timestamps.size(), 1);\n  auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(\n      t2.timestamps[0].getTime() - start);\n  EXPECT_GE(elapsedMs.count(), timeout.count());\n}\n\nTEST_F(HHWheelTimerTest, Level1) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n  TestTimeout tt;\n  // Schedule the timeout for the tick in a next epoch.\n  t.scheduleTimeout(&tt, std::chrono::milliseconds(500));\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n  ASSERT_EQ(tt.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, end, milliseconds(500));\n}\n\n// Test that we handle negative timeouts properly (i.e. treat them as 0)\nTEST_F(HHWheelTimerTest, NegativeTimeout) {\n  StackWheelTimer t(&eventBase, milliseconds(1));\n  std::this_thread::sleep_for(std::chrono::milliseconds(10));\n  TestTimeout tt1;\n  TestTimeout tt2;\n  // Make sure we have event scheduled.\n  t.scheduleTimeout(&tt1, std::chrono::milliseconds(1));\n  // Schedule another timeout that would appear to be earlier than\n  // the already scheduled one.\n  t.scheduleTimeout(&tt2, std::chrono::milliseconds(-500000000));\n  TimePoint start;\n  eventBase.loop();\n  TimePoint end;\n  ASSERT_EQ(tt2.timestamps.size(), 1);\n  T_CHECK_TIMEOUT(start, end, milliseconds(1));\n}\n\nTEST(HHWheelTimerDetailsTest, Divider) {\n  auto no_overflow_add = [](uint64_t& base, int offset) -> bool {\n    if (offset >= 0 || static_cast<unsigned int>(-offset) < base) {\n      base += offset;\n      return true;\n    }\n    return false;\n  };\n\n  for (uint64_t denShift = 1; denShift <= 60; denShift++) {\n    for (int denOffset = -10; denOffset <= 10; denOffset++) {\n      uint64_t den = uint64_t(1) << denShift;\n\n      if (!no_overflow_add(den, denOffset)) {\n        continue;\n      }\n\n      folly::detail::HHWheelTimerDurationInterval<\n          std::chrono::milliseconds>::Divider divider{den};\n      for (uint64_t numShift = 3; numShift <= 58; numShift++) {\n        for (int numOffset = -10; numOffset <= 10; numOffset++) {\n          uint64_t num = (1LLU << numShift);\n          if (!no_overflow_add(num, numOffset)) {\n            continue;\n          }\n          int allowedError = 0;\n          if (numShift >= 32 || denShift >= 32) {\n            allowedError = 1;\n          }\n          auto expected = num / den;\n          auto calc = divider.divide(num);\n\n          // either it is accurate or the result is overstated by allowedError\n          EXPECT_TRUE(\n              expected == calc ||\n              (calc >= expected && calc <= expected + allowedError))\n              << \"expected=\" << expected << \" calc=\" << calc << \" allowedError=\"\n              << allowedError << \": \" << num << \"/\" << den << \" ns=\" << numShift\n              << \" + \" << numOffset << \" ds=\" << denShift << \" + \" << denOffset;\n        }\n      }\n    }\n  }\n\n  for (uint64_t den = 1; den <= 10000; den++) {\n    folly::detail::HHWheelTimerDurationInterval<\n        std::chrono::milliseconds>::Divider divider{den};\n    for (uint64_t num = 0; num <= 10000; num++) {\n      EXPECT_EQ(num / den, divider.divide(num)) << num << \"/\" << den;\n    }\n  }\n}\n"
  },
  {
    "path": "folly/io/async/test/IOBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/Benchmark.h>\n#include <folly/FileUtil.h>\n#include <folly/io/async/AsyncIO.h>\n#include <folly/io/async/IoUring.h>\n#include <folly/io/async/test/AsyncBaseTestLib.h>\n#include <folly/io/async/test/IoTestTempFileUtil.h>\n#include <folly/portability/GFlags.h>\n\nnamespace {\n\nstatic constexpr size_t kBlockSize = 4096;\n// we cannot register more than UIO_MAXIOV iovs\n// we can create bigger buffers and split them\nstatic constexpr size_t kNumBlocks = UIO_MAXIOV;\n\nstatic folly::test::TemporaryFile& getTempFile(size_t num) {\n  CHECK_LE(num, kNumBlocks);\n\n  static auto sTempFile =\n      folly::test::TempFileUtil::getTempFile(kNumBlocks * kBlockSize);\n\n  return sTempFile;\n}\n\ntemplate <typename OP>\nstruct BenchmarkData {\n  BenchmarkData(size_t num, size_t& c) : numEntries(num), completed(c) {\n    fd = folly::fileops::open(\n        getTempFile(num).path().c_str(), O_DIRECT | O_RDONLY);\n    if (fd == -1) {\n      fd = folly::fileops::open(getTempFile(num).path().c_str(), O_RDONLY);\n    }\n    CHECK_GE(fd, 0);\n    ops.reserve(numEntries);\n    bufs.reserve(numEntries);\n    for (size_t i = 0; i < numEntries; i++) {\n      bufs.push_back(\n          folly::test::async_base_test_lib_detail::TestUtil::allocateAligned(\n              kBlockSize));\n    }\n  }\n\n  ~BenchmarkData() { folly::fileops::close(fd); }\n\n  void reset(bool useRegisteredBuffers) {\n    ops.clear();\n    for (size_t i = 0; i < numEntries; i++) {\n      ops.push_back(std::make_unique<OP>());\n      auto& op = *ops.back();\n      op.setNotificationCallback([&](folly::AsyncBaseOp*) { ++completed; });\n      if (useRegisteredBuffers) {\n        op.pread(fd, bufs[i].get(), kBlockSize, i * kBlockSize, i);\n      } else {\n        op.pread(fd, bufs[i].get(), kBlockSize, i * kBlockSize);\n      }\n    }\n  }\n\n  std::vector<std::unique_ptr<folly::AsyncBase::Op>> ops;\n  std::vector<folly::test::async_base_test_lib_detail::TestUtil::ManagedBuffer>\n      bufs;\n  size_t numEntries;\n  size_t& completed;\n  int fd{-1};\n};\n\ntemplate <typename TAsync>\nvoid runTAsyncIOTest(\n    unsigned int iters,\n    size_t numEntries,\n    size_t batchSize,\n    bool persist,\n    bool useRegisteredBuffers) {\n  folly::BenchmarkSuspender suspender;\n  std::vector<folly::AsyncBase::Op*> ops;\n  ops.reserve(batchSize);\n  size_t completed = 0;\n  BenchmarkData<typename TAsync::Op> bmData(numEntries, completed);\n  std::unique_ptr<TAsync> aio(\n      persist\n          ? new TAsync(numEntries, folly::AsyncBase::NOT_POLLABLE, batchSize)\n          : nullptr);\n  if (aio) {\n    aio->register_buffers(bmData.bufs);\n  }\n  suspender.dismiss();\n  for (unsigned iter = 0; iter < iters; iter++) {\n    if (!persist) {\n      aio.reset(\n          new TAsync(numEntries, folly::AsyncBase::NOT_POLLABLE, batchSize));\n      if (useRegisteredBuffers) {\n        aio->register_buffers(bmData.bufs);\n      }\n    }\n    completed = 0;\n    bmData.reset(useRegisteredBuffers);\n    size_t num = 0;\n    for (size_t i = 0; i < numEntries; i++) {\n      ops.push_back(bmData.ops[i].get());\n      if (++num == batchSize) {\n        num = 0;\n        aio->submit(folly::Range(ops.data(), ops.data() + ops.size()));\n        ops.clear();\n      }\n    }\n    if (num) {\n      aio->submit(folly::Range(ops.data(), ops.data() + ops.size()));\n      ops.clear();\n    }\n    aio->wait(numEntries);\n    CHECK_EQ(completed, numEntries);\n    for (size_t i = 0; i < numEntries; i++) {\n      CHECK_EQ(bmData.ops[i]->result(), kBlockSize);\n    }\n    if (!persist) {\n      aio.reset();\n    }\n  }\n  aio.reset();\n  suspender.rehire();\n}\n\nvoid runAsyncIOTest(\n    unsigned int iters, size_t numEntries, size_t batchSize, bool persist) {\n  class BatchAsyncIO : public folly::AsyncIO {\n   public:\n    BatchAsyncIO(size_t capacity, PollMode pollMode, size_t /*unused*/)\n        : folly::AsyncIO(capacity, pollMode) {}\n    void register_buffers(\n        const std::vector<folly::test::async_base_test_lib_detail::TestUtil::\n                              ManagedBuffer>&) {}\n  };\n  runTAsyncIOTest<BatchAsyncIO>(iters, numEntries, batchSize, persist, false);\n}\n\nvoid runIOUringTest(\n    unsigned int iters,\n    size_t numEntries,\n    size_t batchSize,\n    bool persist,\n    bool useRegisteredBuffers = false) {\n  class BatchIoUring : public folly::IoUring {\n   public:\n    BatchIoUring(size_t capacity, PollMode pollMode, size_t batchSize)\n        : folly::IoUring(capacity, pollMode, batchSize) {}\n    void register_buffers(\n        const std::vector<\n            folly::test::async_base_test_lib_detail::TestUtil::ManagedBuffer>&\n            bufs) {\n      std::vector<struct iovec> iovs(bufs.size());\n      for (size_t i = 0; i < bufs.size(); i++) {\n        iovs[i].iov_base = bufs[i].get();\n        iovs[i].iov_len = kBlockSize;\n      }\n\n      auto ret = folly::IoUring::register_buffers(iovs.data(), iovs.size());\n      CHECK_EQ(ret, 0);\n    }\n  };\n\n  runTAsyncIOTest<BatchIoUring>(\n      iters, numEntries, batchSize, persist, useRegisteredBuffers);\n}\n\n} // namespace\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runAsyncIOTest, async_io_no_batching_no_per, 1024, 1, false)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runAsyncIOTest, async_io_batching_64_no_per, 1024, 64, false)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runAsyncIOTest, async_io_batching_256_no_per, 1024, 256, false)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runAsyncIOTest, async_io_no_batching_per, 1024, 1, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runAsyncIOTest, async_io_batching_64_per, 1024, 64, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runAsyncIOTest, async_io_batching_256_per, 1024, 256, true)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_no_batching_no_per, 1024, 1, false)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_64_no_per, 1024, 64, false)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_256_no_per, 1024, 256, false)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_no_batching_per, 1024, 1, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_64_per, 1024, 64, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_256_per, 1024, 256, true)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_no_batching_no_per_reg, 1024, 1, false, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_64_no_per_reg, 1024, 64, false, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_256_no_per_reg, 1024, 256, false, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_no_batching_per_reg, 1024, 1, true, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_64_per_reg, 1024, 64, true, true)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runIOUringTest, io_uring_batching_256_per_reg, 1024, 256, true, true)\nBENCHMARK_DRAW_LINE();\n\nint main(int argc, char** argv) {\n  getTempFile(kNumBlocks);\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n}\n\n/*\n./io_benchmark --bm_min_iters=100\n============================================================================\nfolly/experimental/io/test/IOBenchmark.cpp      relative  time/iter  iters/s\n============================================================================\n----------------------------------------------------------------------------\nrunAsyncIOTest(async_io_no_batching_no_per)                 45.33ms    22.06\nrunAsyncIOTest(async_io_batching_64_no_per)      102.48%    44.24ms    22.61\nrunAsyncIOTest(async_io_batching_256_no_per)      94.30%    48.08ms    20.80\nrunAsyncIOTest(async_io_no_batching_per)         173.66%    26.11ms    38.31\nrunAsyncIOTest(async_io_batching_64_per)         179.94%    25.19ms    39.69\nrunAsyncIOTest(async_io_batching_256_per)        171.69%    26.40ms    37.87\n----------------------------------------------------------------------------\nrunIOUringTest(io_uring_no_batching_no_per)      180.66%    25.09ms    39.85\nrunIOUringTest(io_uring_batching_64_no_per)      176.16%    25.74ms    38.86\nrunIOUringTest(io_uring_batching_256_no_per)     178.45%    25.40ms    39.36\nrunIOUringTest(io_uring_no_batching_per)         177.59%    25.53ms    39.17\nrunIOUringTest(io_uring_batching_64_per)         178.06%    25.46ms    39.28\nrunIOUringTest(io_uring_batching_256_per)        178.81%    25.35ms    39.44\n----------------------------------------------------------------------------\nrunIOUringTest(io_uring_no_batching_no_per_reg)  121.76%    37.23ms    26.86\nrunIOUringTest(io_uring_batching_64_no_per_reg)  119.86%    37.82ms    26.44\nrunIOUringTest(io_uring_batching_256_no_per_reg  127.17%    35.65ms    28.05\nrunIOUringTest(io_uring_no_batching_per_reg)     178.60%    25.38ms    39.39\nrunIOUringTest(io_uring_batching_64_per_reg)     179.33%    25.28ms    39.56\nrunIOUringTest(io_uring_batching_256_per_reg)    178.69%    25.37ms    39.42\n----------------------------------------------------------------------------\n============================================================================\n*/\n"
  },
  {
    "path": "folly/io/async/test/IoTestTempFileUtil.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/test/IoTestTempFileUtil.h>\n\n#include <random>\n\n#include <folly/FileUtil.h>\n#include <folly/String.h>\n\nnamespace folly {\nnamespace test {\n\nTemporaryFile TempFileUtil::getTempFile(size_t size) {\n  CHECK_EQ(size % sizeof(uint32_t), 0);\n  size /= sizeof(uint32_t);\n\n  TemporaryFile tmpFile;\n  int fd = tmpFile.fd();\n  CHECK_GE(fd, 0);\n\n  // fill the file the file with random data\n  const uint32_t seed = 42;\n  std::mt19937 rnd(seed);\n\n  constexpr size_t bufferSize = 1U << 16;\n  uint32_t buffer[bufferSize];\n\n  while (size) {\n    size_t n = std::min(size, bufferSize);\n    for (size_t i = 0; i < n; ++i) {\n      buffer[i] = rnd();\n    }\n    size_t written = folly::writeFull(fd, buffer, sizeof(uint32_t) * n);\n    CHECK_EQ(written, sizeof(uint32_t) * n);\n    size -= n;\n  }\n\n  CHECK_EQ(::fdatasync(fd), 0);\n\n  // the file was opened with O_EXCL so we need to close to be able\n  // to open it again\n  tmpFile.close();\n\n  return tmpFile;\n}\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/IoTestTempFileUtil.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <glog/logging.h>\n\n#include <folly/io/FsUtil.h>\n#include <folly/testing/TestUtil.h>\n\nnamespace folly {\nnamespace test {\n\nclass TempFileUtil {\n public:\n  // Returns a temporary file that is NOT kept open\n  // but is deleted on destruction\n  // Generate random-looking but reproduceable data.\n  static TemporaryFile getTempFile(size_t size);\n\n private:\n  TempFileUtil() = delete;\n};\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/IoUringBackendBench.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/eventfd.h>\n\n#include <folly/Benchmark.h>\n#include <folly/FileUtil.h>\n#include <folly/init/Init.h>\n#include <folly/io/async/EpollBackend.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventHandler.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/io/async/ScopedEventBaseThread.h>\n#include <folly/portability/GFlags.h>\n\nDEFINE_bool(async_refill, true, \"async refill\");\n\nusing namespace folly;\n\nnamespace {\n\nclass EventFD;\nstruct EventFDRefillInfo {\n  size_t num{0};\n  size_t curr{0};\n  size_t refillNum{0};\n  EventBase* evb_{nullptr};\n  std::vector<std::unique_ptr<EventFD>>* events{nullptr};\n};\n\nclass EventFD : public EventHandler {\n public:\n  EventFD(\n      uint64_t num,\n      uint64_t& total,\n      bool persist,\n      EventBase* eventBase,\n      EventFDRefillInfo* refillInfo)\n      : EventFD(total, createFd(num), persist, eventBase, refillInfo) {}\n\n  ~EventFD() override {\n    unregisterHandler();\n\n    if (fd_ > 0) {\n      unregisterHandler();\n      fileops::close(fd_);\n      fd_ = -1;\n    }\n  }\n\n  void setNumReadPerLoop(size_t numReadPerLoop) {\n    numReadPerLoop_ = numReadPerLoop;\n  }\n\n  ssize_t write(uint64_t val) {\n    uint64_t data = val;\n\n    return fileops::write(fd_, &data, sizeof(data));\n  }\n\n  // from folly::EventHandler\n  void handlerReady(uint16_t /*events*/) noexcept override {\n    for (size_t i = 0; i < numReadPerLoop_; ++i) {\n      uint64_t data = 0;\n      auto ret = fileops::read(fd_, &data, sizeof(data));\n\n      CHECK_EQ(ret, sizeof(data));\n      CHECK_EQ(data, 1);\n    }\n\n    if (!persist_) {\n      registerHandler(folly::EventHandler::READ);\n    }\n    if (total_ > 0) {\n      total_ -= std::min(numReadPerLoop_, total_);\n      if (total_ == 0) {\n        evb_->terminateLoopSoon();\n      }\n    }\n    if (refillInfo_ && refillInfo_->events) {\n      processOne();\n    }\n  }\n\n  void processOne() {\n    ++refillInfo_->curr;\n    if (refillInfo_->curr >= refillInfo_->num) {\n      refillInfo_->curr = 0;\n      refillInfo_->evb_->runInEventBaseThreadAlwaysEnqueue([&]() {\n        for (auto& ev : *refillInfo_->events) {\n          ev->write(refillInfo_->refillNum);\n        }\n      });\n    }\n  }\n\n private:\n  static int createFd(uint64_t num) {\n    // we want it a semaphore\n    int fd = ::eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE);\n    CHECK_GT(fd, 0);\n    CHECK_EQ(writeNoInt(fd, &num, sizeof(num)), sizeof(num));\n    CHECK_EQ(fcntl(fd, F_SETFL, O_NONBLOCK), 0);\n\n    return fd;\n  }\n\n  EventFD(\n      uint64_t& total,\n      int fd,\n      bool persist,\n      EventBase* eventBase,\n      EventFDRefillInfo* refillInfo)\n      : EventHandler(eventBase, NetworkSocket::fromFd(fd)),\n        total_(total),\n        fd_(fd),\n        persist_(persist),\n        evb_(eventBase),\n        refillInfo_(refillInfo) {\n    if (persist_) {\n      registerHandler(folly::EventHandler::READ | folly::EventHandler::PERSIST);\n    } else {\n      registerHandler(folly::EventHandler::READ);\n    }\n  }\n  uint64_t& total_;\n  size_t numReadPerLoop_{1};\n  int fd_{-1};\n  bool persist_;\n  EventBase* evb_;\n  EventFDRefillInfo* refillInfo_;\n};\n\nclass EventBaseProvider {\n public:\n  enum Type {\n    DEFAULT,\n    IO_URING,\n    EPOLL,\n  };\n\n  static std::unique_ptr<folly::EventBase> getEventBase(\n      Type type, size_t capacity = 1024) {\n    switch (type) {\n      case DEFAULT: {\n        return std::make_unique<folly::EventBase>();\n      }\n      case IO_URING: {\n        try {\n          folly::IoUringOptions opts;\n          opts.setCapacity(capacity).setMaxSubmit(256);\n\n          auto optsPtr =\n              std::make_shared<folly::IoUringOptions>(std::move(opts));\n          auto factory = [optsPtr]() mutable {\n            return std::make_unique<folly::IoUringBackend>(std::move(*optsPtr));\n          };\n          return std::make_unique<folly::EventBase>(\n              folly::EventBase::Options().setBackendFactory(\n                  std::move(factory)));\n        } catch (const folly::IoUringBackend::NotAvailable&) {\n          return nullptr;\n        }\n      }\n      case EPOLL: {\n        folly::EpollBackend::Options opts;\n        opts.setNumLoopEvents(256);\n\n        auto factory = [opts] {\n          return std::make_unique<folly::EpollBackend>(opts);\n        };\n        return std::make_unique<folly::EventBase>(\n            folly::EventBase::Options().setBackendFactory(std::move(factory)));\n      }\n    }\n\n    LOG(FATAL) << \"invalid backend type\";\n  }\n};\n\nvoid runBM(\n    unsigned int iters,\n    EventBaseProvider::Type type,\n    bool persist,\n    size_t numEvents,\n    size_t numReadPerLoop = 1,\n    size_t refillNum = 0) {\n  BenchmarkSuspender suspender;\n  static constexpr uint64_t kNum = 2000000000;\n  if (iters > kNum) {\n    iters = kNum;\n  }\n  uint64_t total = numEvents;\n  auto evb = EventBaseProvider::getEventBase(type);\n  evb->runAfterDelay([]() {}, 1000 * 1000); // just a long timeout\n  std::unique_ptr<ScopedEventBaseThread> refillThread;\n  EventFDRefillInfo refillInfo;\n  std::vector<std::unique_ptr<EventFD>> eventsVec;\n  eventsVec.reserve(numEvents);\n  uint64_t num = refillNum ? refillNum : kNum;\n  if (refillNum) {\n    refillInfo.events = &eventsVec;\n    refillInfo.num = eventsVec.size();\n    refillInfo.refillNum = refillNum;\n    if (FLAGS_async_refill) {\n      refillThread = std::make_unique<ScopedEventBaseThread>();\n      refillInfo.evb_ = refillThread->getEventBase();\n    } else {\n      refillInfo.evb_ = evb.get();\n    }\n  }\n  for (size_t i = 0; i < numEvents; i++) {\n    auto ev =\n        std::make_unique<EventFD>(num, total, persist, evb.get(), &refillInfo);\n    ev->setNumReadPerLoop(numReadPerLoop);\n    eventsVec.emplace_back(std::move(ev));\n  }\n  // reset the total\n  total = iters * numEvents;\n  suspender.dismiss();\n  evb->loopForever();\n  suspender.rehire();\n  refillThread.reset();\n  eventsVec.clear();\n  evb.reset();\n}\n\n} // namespace\n\n// refill\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM,\n    default_persist_1_refill,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    1,\n    1,\n    1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    default_persist_1_read_4_refill,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    1,\n    4,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    epoll_persist_1_refill,\n    EventBaseProvider::Type::EPOLL,\n    true,\n    1,\n    1,\n    1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    epoll_persist_1_read_4_refill,\n    EventBaseProvider::Type::EPOLL,\n    true,\n    1,\n    4,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    io_uring_persist_1_refill,\n    EventBaseProvider::Type::IO_URING,\n    true,\n    1,\n    1,\n    1)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM,\n    default_persist_16_refill,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    16,\n    1,\n    1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    default_persist_16_read_4_refill,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    16,\n    4,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    epoll_persist_16_refill,\n    EventBaseProvider::Type::EPOLL,\n    true,\n    16,\n    1,\n    1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    epoll_persist_16_read_4_refill,\n    EventBaseProvider::Type::EPOLL,\n    true,\n    16,\n    4,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    io_uring_persist_16_refill,\n    EventBaseProvider::Type::IO_URING,\n    true,\n    16,\n    1,\n    1)\nBENCHMARK_DRAW_LINE();\n// no refill\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_persist_1, EventBaseProvider::Type::DEFAULT, true, 1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    default_persist_1_read_4,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    1,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_1, EventBaseProvider::Type::EPOLL, true, 1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_1_read_4, EventBaseProvider::Type::EPOLL, true, 1, 4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_persist_1, EventBaseProvider::Type::IO_URING, true, 1)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_persist_16, EventBaseProvider::Type::DEFAULT, true, 16)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    default_persist_16_read_4,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    16,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_16, EventBaseProvider::Type::EPOLL, true, 16)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_16_read_4, EventBaseProvider::Type::EPOLL, true, 16, 4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_persist_16, EventBaseProvider::Type::IO_URING, true, 16)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_persist_64, EventBaseProvider::Type::DEFAULT, true, 64)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    default_persist_64_read_4,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    64,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_64, EventBaseProvider::Type::EPOLL, true, 64)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_64_read_4, EventBaseProvider::Type::EPOLL, true, 64, 4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_persist_64, EventBaseProvider::Type::IO_URING, true, 64)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_persist_128, EventBaseProvider::Type::DEFAULT, true, 128)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    default_persist_128_read_4,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    128,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_128, EventBaseProvider::Type::EPOLL, true, 128)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    epoll_persist_128_read_4,\n    EventBaseProvider::Type::EPOLL,\n    true,\n    128,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_persist_128, EventBaseProvider::Type::IO_URING, true, 128)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_persist_256, EventBaseProvider::Type::DEFAULT, true, 256)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    default_persist_256_read_4,\n    EventBaseProvider::Type::DEFAULT,\n    true,\n    256,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_persist_256, EventBaseProvider::Type::EPOLL, true, 256)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    epoll_persist_256_read_4,\n    EventBaseProvider::Type::EPOLL,\n    true,\n    256,\n    4)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_persist_256, EventBaseProvider::Type::IO_URING, true, 256)\nBENCHMARK_DRAW_LINE();\n\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_no_persist_1, EventBaseProvider::Type::DEFAULT, false, 1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_no_persist_1, EventBaseProvider::Type::EPOLL, false, 1)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_no_persist_1, EventBaseProvider::Type::IO_URING, false, 1)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_no_persist_16, EventBaseProvider::Type::DEFAULT, false, 16)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_no_persist_16, EventBaseProvider::Type::EPOLL, false, 16)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_no_persist_16, EventBaseProvider::Type::IO_URING, false, 16)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_no_persist_64, EventBaseProvider::Type::DEFAULT, false, 64)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_no_persist_64, EventBaseProvider::Type::EPOLL, false, 64)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, io_uring_no_persist_64, EventBaseProvider::Type::IO_URING, false, 64)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_no_persist_128, EventBaseProvider::Type::DEFAULT, false, 128)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_no_persist_128, EventBaseProvider::Type::EPOLL, false, 128)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    io_uring_no_persist_128,\n    EventBaseProvider::Type::IO_URING,\n    false,\n    128)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(\n    runBM, default_no_persist_256, EventBaseProvider::Type::DEFAULT, false, 256)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM, epoll_no_persist_256, EventBaseProvider::Type::EPOLL, false, 256)\nBENCHMARK_RELATIVE_NAMED_PARAM(\n    runBM,\n    io_uring_no_persist_256,\n    EventBaseProvider::Type::IO_URING,\n    false,\n    256)\nBENCHMARK_DRAW_LINE();\n\nint main(int argc, char** argv) {\n  folly::Init init(&argc, &argv, true);\n  runBenchmarks();\n}\n\n/*\n$ buck run @mode/opt //folly/io/async/test:io_uring_backend_bench -- \\\n--bm_min_iters=100000\n============================================================================\n[...]io/async/test/IoUringBackendBench.cpp     relative  time/iter   iters/s\n============================================================================\nrunBM(default_persist_1_refill)                             9.51us   105.10K\nrunBM(default_persist_1_read_4_refill)          395.25%     2.41us   415.41K\nrunBM(epoll_persist_1_refill)                   103.49%     9.19us   108.77K\nrunBM(epoll_persist_1_read_4_refill)            385.35%     2.47us   405.01K\nrunBM(io_uring_persist_1_refill)                100.60%     9.46us   105.73K\n----------------------------------------------------------------------------\nrunBM(default_persist_16_refill)                           17.97us    55.65K\nrunBM(default_persist_16_read_4_refill)         237.58%     7.56us   132.21K\nrunBM(epoll_persist_16_refill)                  116.06%    15.48us    64.59K\nrunBM(epoll_persist_16_read_4_refill)           256.37%     7.01us   142.67K\nrunBM(io_uring_persist_16_refill)               98.152%    18.31us    54.62K\n----------------------------------------------------------------------------\nrunBM(default_persist_1)                                  544.64ns     1.84M\nrunBM(default_persist_1_read_4)                 224.31%   242.80ns     4.12M\nrunBM(epoll_persist_1)                          137.28%   396.73ns     2.52M\nrunBM(epoll_persist_1_read_4)                   265.08%   205.46ns     4.87M\nrunBM(io_uring_persist_1)                       109.05%   499.44ns     2.00M\n----------------------------------------------------------------------------\nrunBM(default_persist_16)                                   3.44us   290.54K\nrunBM(default_persist_16_read_4)                133.56%     2.58us   388.05K\nrunBM(epoll_persist_16)                         107.09%     3.21us   311.13K\nrunBM(epoll_persist_16_read_4)                  136.62%     2.52us   396.93K\nrunBM(io_uring_persist_16)                      78.378%     4.39us   227.72K\n----------------------------------------------------------------------------\nrunBM(default_persist_64)                                  12.74us    78.48K\nrunBM(default_persist_64_read_4)                126.81%    10.05us    99.51K\nrunBM(epoll_persist_64)                         104.11%    12.24us    81.70K\nrunBM(epoll_persist_64_read_4)                  128.57%     9.91us   100.89K\nrunBM(io_uring_persist_64)                      75.697%    16.83us    59.40K\n----------------------------------------------------------------------------\nrunBM(default_persist_128)                                 25.66us    38.96K\nrunBM(default_persist_128_read_4)               127.24%    20.17us    49.58K\nrunBM(epoll_persist_128)                        103.57%    24.78us    40.36K\nrunBM(epoll_persist_128_read_4)                 128.75%    19.93us    50.17K\nrunBM(io_uring_persist_128)                     76.656%    33.48us    29.87K\n----------------------------------------------------------------------------\nrunBM(default_persist_256)                                 50.93us    19.63K\nrunBM(default_persist_256_read_4)               126.92%    40.13us    24.92K\nrunBM(epoll_persist_256)                        103.13%    49.39us    20.25K\nrunBM(epoll_persist_256_read_4)                 128.29%    39.70us    25.19K\nrunBM(io_uring_persist_256)                     75.215%    67.72us    14.77K\n----------------------------------------------------------------------------\nrunBM(default_no_persist_1)                                 1.06us   944.53K\nrunBM(epoll_no_persist_1)                       118.53%   893.20ns     1.12M\nrunBM(io_uring_no_persist_1)                    208.61%   507.51ns     1.97M\n----------------------------------------------------------------------------\nrunBM(default_no_persist_16)                               12.46us    80.28K\nrunBM(epoll_no_persist_16)                      106.36%    11.71us    85.38K\nrunBM(io_uring_no_persist_16)                   267.24%     4.66us   214.53K\n----------------------------------------------------------------------------\nrunBM(default_no_persist_64)                               51.91us    19.26K\nrunBM(epoll_no_persist_64)                      110.21%    47.11us    21.23K\nrunBM(io_uring_no_persist_64)                   296.14%    17.53us    57.04K\n----------------------------------------------------------------------------\nrunBM(default_no_persist_128)                             102.67us     9.74K\nrunBM(epoll_no_persist_128)                     107.42%    95.57us    10.46K\nrunBM(io_uring_no_persist_128)                  291.41%    35.23us    28.38K\n----------------------------------------------------------------------------\nrunBM(default_no_persist_256)                             218.32us     4.58K\nrunBM(epoll_no_persist_256)                     111.42%   195.95us     5.10K\nrunBM(io_uring_no_persist_256)                  312.72%    69.81us    14.32K\n----------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "folly/io/async/test/IoUringBackendSetupTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly::test {\n\nTEST(IoUringBackendSetupTest, SetupPollNoGroup) {\n  IoUringBackend::Options options;\n  options.setFlags(IoUringBackend::Options::Flags::POLL_SQ);\n\n  IoUringBackend io(std::move(options));\n\n  EXPECT_TRUE(io.params().flags & IORING_SETUP_CQSIZE);\n  EXPECT_TRUE(io.params().flags & IORING_SETUP_SQPOLL);\n  EXPECT_FALSE(io.params().flags & IORING_SETUP_SQ_AFF);\n  EXPECT_FALSE(io.params().flags & IORING_SETUP_ATTACH_WQ);\n}\n\nTEST(IoUringBackendSetupTest, SetupPollWithGroup) {\n  auto makeOptions = []() {\n    IoUringBackend::Options options;\n    options.setFlags(IoUringBackend::Options::Flags::POLL_SQ)\n        .setSQGroupName(\"test group\")\n        .setSQGroupNumThreads(1);\n    return options;\n  };\n\n  IoUringBackend io1(makeOptions());\n  IoUringBackend io2(makeOptions());\n\n  // We set up one thread for the group, so the first call should be normal...\n  EXPECT_TRUE(io1.params().flags & IORING_SETUP_SQPOLL);\n  EXPECT_FALSE(io1.params().flags & IORING_SETUP_SQ_AFF);\n  EXPECT_FALSE(io1.params().flags & IORING_SETUP_ATTACH_WQ);\n\n  // second call should have attached to existing fd 1.\n  EXPECT_TRUE(io2.params().flags & IORING_SETUP_SQPOLL);\n  EXPECT_FALSE(io2.params().flags & IORING_SETUP_SQ_AFF);\n  EXPECT_TRUE(io2.params().flags & IORING_SETUP_ATTACH_WQ);\n  EXPECT_EQ(io2.params().wq_fd, io1.ioRingPtr()->ring_fd);\n}\n\nTEST(IoUringBackendSetupTest, SetupPollWithGroupAndCpu) {\n  auto makeOptions = []() {\n    IoUringBackend::Options options;\n    options.setFlags(IoUringBackend::Options::Flags::POLL_SQ)\n        .setSQGroupName(\"test group\")\n        .setSQGroupNumThreads(2)\n        .setSQCpu(1)\n        .setSQCpu(0);\n    return options;\n  };\n\n  IoUringBackend io1(makeOptions());\n  IoUringBackend io2(makeOptions());\n  IoUringBackend io3(makeOptions());\n\n  // The first call should create a thread with CPU affinity set.\n  EXPECT_TRUE(io1.params().flags & IORING_SETUP_SQPOLL);\n  EXPECT_TRUE(io1.params().flags & IORING_SETUP_SQ_AFF);\n  EXPECT_FALSE(io1.params().flags & IORING_SETUP_ATTACH_WQ);\n  // We don't know which CPU code will choose, but it better be one or the\n  // other.\n  EXPECT_TRUE(\n      io1.params().sq_thread_cpu == 1 || io1.params().sq_thread_cpu == 0);\n\n  // We set two threads, so second call should create a thread with other CPU.\n  EXPECT_TRUE(io2.params().flags & IORING_SETUP_CQSIZE);\n  EXPECT_TRUE(io2.params().flags & IORING_SETUP_SQPOLL);\n  EXPECT_TRUE(io2.params().flags & IORING_SETUP_SQ_AFF);\n  EXPECT_FALSE(io2.params().flags & IORING_SETUP_ATTACH_WQ);\n  // This one better choose the other CPU.\n  EXPECT_TRUE(\n      io2.params().sq_thread_cpu == 1 || io2.params().sq_thread_cpu == 0);\n  EXPECT_NE(io1.params().sq_thread_cpu, io2.params().sq_thread_cpu);\n\n  // And the third thread should have attached to an existing SQ and not\n  // specified an affinity.\n  EXPECT_TRUE(io3.params().flags & IORING_SETUP_SQPOLL);\n  EXPECT_FALSE(io3.params().flags & IORING_SETUP_SQ_AFF);\n  EXPECT_TRUE(io3.params().flags & IORING_SETUP_ATTACH_WQ);\n}\n\n} // namespace folly::test\n"
  },
  {
    "path": "folly/io/async/test/IoUringEventBaseLocalTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/futures/Future.h>\n#include <folly/futures/Promise.h>\n#include <folly/io/async/IoUringBackend.h>\n#include <folly/io/async/IoUringEventBaseLocal.h>\n#include <folly/portability/GTest.h>\n\nnamespace folly {\n\nstruct NopSqe : IoSqeBase {\n  void processSubmit(struct io_uring_sqe* sqe) noexcept override {\n    io_uring_prep_nop(sqe);\n  }\n  void callback(const io_uring_cqe* cqe) noexcept override {\n    prom.setValue(cqe->res);\n  }\n  void callbackCancelled(const io_uring_cqe*) noexcept override {\n    prom.setException(FutureCancellation{});\n  }\n\n  Promise<int> prom;\n};\n\nTEST(IoUringEventBaseLocalTest, Basic) {\n  folly::EventBase eb;\n  IoUringEventBaseLocal::attach(&eb, {});\n  IoUringBackend* backend = IoUringEventBaseLocal::try_get(&eb);\n  ASSERT_NE(nullptr, backend);\n  NopSqe nop;\n  backend->submit(nop);\n  EXPECT_EQ(0, nop.prom.getSemiFuture().via(&eb).getVia(&eb));\n}\n\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/IoUringTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/init/Init.h>\n#include <folly/io/async/IoUring.h>\n#include <folly/io/async/test/AsyncBaseTestLib.h>\n\nusing folly::IoUring;\n\nnamespace folly {\nnamespace test {\nnamespace async_base_test_lib_detail {\nREGISTER_TYPED_TEST_SUITE_P(\n    AsyncTest,\n    ZeroAsyncDataNotPollable,\n    ZeroAsyncDataPollable,\n    SingleAsyncDataNotPollable,\n    SingleAsyncDataPollable,\n    MultipleAsyncDataNotPollable,\n    MultipleAsyncDataPollable,\n    ManyAsyncDataNotPollable,\n    ManyAsyncDataPollable,\n    NonBlockingWait,\n    Cancel);\n\nREGISTER_TYPED_TEST_SUITE_P(AsyncBatchTest, BatchRead);\n\nINSTANTIATE_TYPED_TEST_SUITE_P(AsyncTest, AsyncTest, IoUring);\n\nclass BatchIoUring : public IoUring {\n public:\n  static constexpr size_t kMaxSubmit = 64;\n  BatchIoUring()\n      : IoUring(kBatchNumEntries, folly::AsyncBase::NOT_POLLABLE, kMaxSubmit) {}\n};\nINSTANTIATE_TYPED_TEST_SUITE_P(AsyncBatchTest, AsyncBatchTest, BatchIoUring);\n\nTEST(IoUringTest, RegisteredBuffers) {\n  constexpr size_t kNumEntries = 2;\n  constexpr size_t kBufSize = 4096;\n  auto ioUring = getAIO<IoUring>(kNumEntries, folly::AsyncBase::NOT_POLLABLE);\n  SKIP_IF(!ioUring) << \"IOUring not available\";\n\n  auto tempFile = folly::test::TempFileUtil::getTempFile(kDefaultFileSize);\n  int fd = ::open(tempFile.path().c_str(), O_DIRECT | O_RDWR);\n  if (fd == -1) {\n    fd = ::open(tempFile.path().c_str(), O_RDWR);\n  }\n  SKIP_IF(fd == -1) << \"Tempfile can't be opened: \" << folly::errnoStr(errno);\n  SCOPE_EXIT {\n    fileops::close(fd);\n  };\n\n  folly::test::async_base_test_lib_detail::TestUtil::ManagedBuffer\n      regFdWriteBuf =\n          folly::test::async_base_test_lib_detail::TestUtil::allocateAligned(\n              kBufSize),\n      readBuf =\n          folly::test::async_base_test_lib_detail::TestUtil::allocateAligned(\n              kBufSize),\n      regFdReadBuf =\n          folly::test::async_base_test_lib_detail::TestUtil::allocateAligned(\n              kBufSize);\n\n  ::memset(regFdWriteBuf.get(), '0', kBufSize);\n  ::memset(readBuf.get(), 'A', kBufSize);\n  ::memset(regFdReadBuf.get(), 'Z', kBufSize);\n\n  struct iovec iov[2];\n  iov[0].iov_base = regFdWriteBuf.get();\n  iov[0].iov_len = kBufSize;\n  iov[1].iov_base = regFdReadBuf.get();\n  iov[1].iov_len = kBufSize;\n\n  CHECK_EQ(ioUring->register_buffers(iov, 2), 0);\n\n  IoUring::Op regFdWriteOp, readOp, regFdReadOp;\n  size_t completed = 0;\n\n  regFdWriteOp.setNotificationCallback([&](folly::AsyncBaseOp*) {\n    ++completed;\n  });\n  regFdWriteOp.pwrite(fd, regFdWriteBuf.get(), kBufSize, 0, 0 /*buf_index*/);\n\n  readOp.setNotificationCallback([&](folly::AsyncBaseOp*) { ++completed; });\n  readOp.pread(fd, readBuf.get(), kBufSize, 0);\n  regFdReadOp.setNotificationCallback([&](folly::AsyncBaseOp*) {\n    ++completed;\n  });\n  regFdReadOp.pread(fd, regFdReadBuf.get(), kBufSize, 0, 1 /*buf_index*/);\n\n  // write\n  ioUring->submit(&regFdWriteOp);\n  ioUring->wait(1);\n  CHECK_EQ(completed, 1);\n  CHECK_EQ(regFdWriteOp.result(), kBufSize);\n\n  // read - both via regular and registered buffers\n  completed = 0;\n  ioUring->submit(&readOp);\n  ioUring->submit(&regFdReadOp);\n\n  ioUring->wait(kNumEntries);\n\n  CHECK_EQ(completed, kNumEntries);\n  CHECK_EQ(readOp.result(), kBufSize);\n  CHECK_EQ(regFdReadOp.result(), kBufSize);\n\n  // make sure we read the same data\n  CHECK_EQ(::memcmp(readBuf.get(), regFdWriteBuf.get(), kBufSize), 0);\n  CHECK_EQ(::memcmp(regFdReadBuf.get(), regFdWriteBuf.get(), kBufSize), 0);\n}\n\n} // namespace async_base_test_lib_detail\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/MockAsyncSSLSocket.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <folly/io/async/AsyncSSLSocket.h>\n#include <folly/portability/GMock.h>\n\nnamespace folly {\nnamespace test {\n\nclass MockAsyncSSLSocket : public AsyncSSLSocket {\n public:\n  MockAsyncSSLSocket(\n      const std::shared_ptr<SSLContext>& ctx,\n      EventBase* base,\n      bool deferSecurityNegotiation = false)\n      : AsyncSSLSocket(ctx, base, deferSecurityNegotiation) {}\n\n  MOCK_METHOD(\n      void,\n      connect_,\n      (AsyncSocket::ConnectCallback*,\n       const folly::SocketAddress&,\n       int,\n       const folly::SocketOptionMap&,\n       const BindOptions&,\n       const std::string&));\n  void connect(\n      AsyncSocket::ConnectCallback* callback,\n      const folly::SocketAddress& address,\n      int timeout,\n      const folly::SocketOptionMap& options,\n      const BindOptions& bindOptions,\n      const std::string& ifName) noexcept override {\n    connect_(callback, address, timeout, options, bindOptions, ifName);\n  }\n\n  MOCK_METHOD(void, getLocalAddress, (folly::SocketAddress*), (const));\n  MOCK_METHOD(void, getPeerAddress, (folly::SocketAddress*), (const));\n  MOCK_METHOD(void, closeNow, ());\n  MOCK_METHOD(bool, good, (), (const));\n  MOCK_METHOD(bool, readable, (), (const));\n  MOCK_METHOD(bool, hangup, (), (const));\n  MOCK_METHOD(\n      void,\n      getSelectedNextProtocol,\n      (const unsigned char**, unsigned*),\n      (const));\n  MOCK_METHOD(\n      bool,\n      getSelectedNextProtocolNoThrow,\n      (const unsigned char**, unsigned*),\n      (const));\n  MOCK_METHOD(void, setReadCB, (ReadCallback*));\n\n  void sslConn(\n      AsyncSSLSocket::HandshakeCB* cb,\n      std::chrono::milliseconds timeout,\n      const SSLContext::SSLVerifyPeerEnum& verify) override {\n    if (timeout > std::chrono::milliseconds::zero()) {\n      handshakeTimeout_.scheduleTimeout(timeout);\n    }\n\n    state_ = StateEnum::ESTABLISHED;\n    sslState_ = STATE_CONNECTING;\n    handshakeCallback_ = cb;\n\n    sslConnectMockable(cb, timeout, verify);\n  }\n\n  void sslAccept(\n      AsyncSSLSocket::HandshakeCB* cb,\n      std::chrono::milliseconds timeout,\n      const SSLContext::SSLVerifyPeerEnum& verify) override {\n    if (timeout > std::chrono::milliseconds::zero()) {\n      handshakeTimeout_.scheduleTimeout(timeout);\n    }\n\n    state_ = StateEnum::ESTABLISHED;\n    sslState_ = STATE_ACCEPTING;\n    handshakeCallback_ = cb;\n\n    sslAcceptMockable(cb, timeout, verify);\n  }\n\n  MOCK_METHOD(\n      void,\n      sslConnectMockable,\n      (AsyncSSLSocket::HandshakeCB*,\n       std::chrono::milliseconds,\n       const SSLContext::SSLVerifyPeerEnum&));\n\n  MOCK_METHOD(\n      void,\n      sslAcceptMockable,\n      (AsyncSSLSocket::HandshakeCB*,\n       std::chrono::milliseconds,\n       const SSLContext::SSLVerifyPeerEnum&));\n};\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/MockAsyncSocketLegacyObserver.h",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// TODO(lume): remove this file in future diff once we replace LegacyObserver\n// with AsyncSocket::ManagedObserver\n\n#pragma once\n\n#include <folly/io/async/AsyncSocket.h>\n#include <folly/io/async/AsyncSocketException.h>\n#include <folly/portability/GMock.h>\n\nnamespace folly {\nnamespace test {\n\nclass MockAsyncSocketLegacyLifecycleObserver\n    : public AsyncSocket::LegacyLifecycleObserver {\n public:\n  using AsyncSocket::LegacyLifecycleObserver::LegacyLifecycleObserver;\n  MOCK_METHOD(void, observerAttachMock, (AsyncSocket*));\n  MOCK_METHOD(void, observerDetachMock, (AsyncSocket*));\n  MOCK_METHOD(void, destroyMock, (AsyncSocket*));\n  MOCK_METHOD(void, closeMock, (AsyncSocket*));\n  MOCK_METHOD(void, connectAttemptMock, (AsyncSocket*));\n  MOCK_METHOD(void, connectSuccessMock, (AsyncSocket*));\n  MOCK_METHOD(\n      void, connectErrorMock, (AsyncSocket*, const AsyncSocketException&));\n  MOCK_METHOD(void, evbAttachMock, (AsyncSocket*, EventBase*));\n  MOCK_METHOD(void, evbDetachMock, (AsyncSocket*, EventBase*));\n  MOCK_METHOD(\n      void, byteEventMock, (AsyncSocket*, const AsyncSocket::ByteEvent&));\n  MOCK_METHOD(void, byteEventsEnabledMock, (AsyncSocket*));\n  MOCK_METHOD(\n      void,\n      byteEventsUnavailableMock,\n      (AsyncSocket*, const AsyncSocketException&));\n  MOCK_METHOD(\n      void,\n      prewriteMock,\n      (AsyncSocket*, const PrewriteState&, PrewriteRequestContainer&));\n\n private:\n  void observerAttach(AsyncSocket* socket) noexcept override {\n    observerAttachMock(socket);\n  }\n  void observerDetach(AsyncSocket* socket) noexcept override {\n    observerDetachMock(socket);\n  }\n  void destroy(AsyncSocket* socket) noexcept override { destroyMock(socket); }\n  void close(AsyncSocket* socket) noexcept override { closeMock(socket); }\n  void connectAttempt(AsyncSocket* socket) noexcept override {\n    connectAttemptMock(socket);\n  }\n  void connectSuccess(AsyncSocket* socket) noexcept override {\n    connectSuccessMock(socket);\n  }\n  void connectError(\n      AsyncSocket* socket, const AsyncSocketException& ex) noexcept override {\n    connectErrorMock(socket, ex);\n  }\n  void evbAttach(AsyncSocket* socket, EventBase* eb) noexcept override {\n    evbAttachMock(socket, eb);\n  }\n  void evbDetach(AsyncSocket* socket, EventBase* eb) noexcept override {\n    evbDetachMock(socket, eb);\n  }\n  void byteEvent(\n      AsyncSocket* socket, const AsyncSocket::ByteEvent& ev) noexcept override {\n    byteEventMock(socket, ev);\n  }\n  void byteEventsEnabled(AsyncSocket* socket) noexcept override {\n    byteEventsEnabledMock(socket);\n  }\n  void byteEventsUnavailable(\n      AsyncSocket* socket, const AsyncSocketException& ex) noexcept override {\n    byteEventsUnavailableMock(socket, ex);\n  }\n  void prewrite(\n      AsyncSocket* socket,\n      const PrewriteState& state,\n      PrewriteRequestContainer& container) noexcept override {\n    prewriteMock(socket, state, container);\n  }\n};\n\n/**\n * Extends mock class to simplify ByteEvents tests.\n */\nclass MockAsyncSocketLegacyLifecycleObserverForByteEvents\n    : public MockAsyncSocketLegacyLifecycleObserver {\n public:\n  MockAsyncSocketLegacyLifecycleObserverForByteEvents(\n      AsyncSocket* socket,\n      const MockAsyncSocketLegacyLifecycleObserverForByteEvents::Config&\n          observerConfig)\n      : MockAsyncSocketLegacyLifecycleObserver(observerConfig),\n        socket_(socket) {\n    ON_CALL(*this, byteEventMock(testing::_, testing::_))\n        .WillByDefault(\n            testing::Invoke([this](\n                                AsyncSocket* socketport,\n                                const AsyncSocket::ByteEvent& event) {\n              CHECK_EQ(this->socket_, socketport);\n              byteEvents_.emplace_back(event);\n            }));\n    ON_CALL(*this, byteEventsEnabledMock(testing::_))\n        .WillByDefault(testing::Invoke([this](AsyncSocket* socketport) {\n          CHECK_EQ(this->socket_, socketport);\n          byteEventsEnabledCalled_++;\n        }));\n\n    ON_CALL(*this, byteEventsUnavailableMock(testing::_, testing::_))\n        .WillByDefault(\n            testing::Invoke(\n                [this](\n                    AsyncSocket* socketport, const AsyncSocketException& ex) {\n                  CHECK_EQ(this->socket_, socketport);\n                  byteEventsUnavailableCalled_++;\n                  byteEventsUnavailableCalledEx_.emplace(ex);\n                }));\n    socket_->addLifecycleObserver(this);\n  }\n\n  const std::vector<AsyncSocket::ByteEvent>& getByteEvents() {\n    return byteEvents_;\n  }\n\n  folly::Optional<AsyncSocket::ByteEvent> getByteEventReceivedWithOffset(\n      const uint64_t offset, const AsyncSocket::ByteEvent::Type type) {\n    for (const auto& byteEvent : byteEvents_) {\n      if (type == byteEvent.type && offset == byteEvent.offset) {\n        return byteEvent;\n      }\n    }\n    return folly::none;\n  }\n\n  folly::Optional<uint64_t> maxOffsetForByteEventReceived(\n      const AsyncSocket::ByteEvent::Type type) {\n    folly::Optional<uint64_t> maybeMaxOffset;\n    for (const auto& byteEvent : byteEvents_) {\n      if (type == byteEvent.type &&\n          (!maybeMaxOffset.has_value() ||\n           maybeMaxOffset.value() <= byteEvent.offset)) {\n        maybeMaxOffset = byteEvent.offset;\n      }\n    }\n    return maybeMaxOffset;\n  }\n\n  bool checkIfByteEventReceived(\n      const AsyncSocket::ByteEvent::Type type, const uint64_t offset) {\n    for (const auto& byteEvent : byteEvents_) {\n      if (type == byteEvent.type && offset == byteEvent.offset) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  void waitForByteEvent(\n      const AsyncSocket::ByteEvent::Type type, const uint64_t offset) {\n    while (!checkIfByteEventReceived(type, offset)) {\n      socket_->getEventBase()->loopOnce();\n    }\n  }\n\n  // Exposed ByteEvent helper fields with const\n  const uint32_t& byteEventsEnabledCalled{byteEventsEnabledCalled_};\n  const uint32_t& byteEventsUnavailableCalled{byteEventsUnavailableCalled_};\n  const folly::Optional<AsyncSocketException>& byteEventsUnavailableCalledEx{\n      byteEventsUnavailableCalledEx_};\n  const std::vector<AsyncSocket::ByteEvent>& byteEvents{byteEvents_};\n\n private:\n  AsyncSocket* socket_;\n\n  // ByteEvents helpers\n  uint32_t byteEventsEnabledCalled_{0};\n  uint32_t byteEventsUnavailableCalled_{0};\n  folly::Optional<AsyncSocketException> byteEventsUnavailableCalledEx_;\n  std::vector<AsyncSocket::ByteEvent> byteEvents_;\n};\n\n} // namespace test\n} // namespace folly\n"
  },
  {
    "path": "folly/io/async/test/MuxIOThreadPoolExecutorTest.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <folly/io/async/Epoll.h>\n\n#if FOLLY_HAS_EPOLL\n\n#include <thread>\n\n#include <folly/executors/test/IOThreadPoolExecutorBaseTestLib.h>\n#include <folly/io/async/MuxIOThreadPoolExecutor.h>\n#include <folly/portability/GTest.h>\n#include <folly/synchronization/Latch.h>\n\nnamespace folly {\nnamespace test {\n\nTEST(MuxIOThreadPoolExecutor, SingleEpollLoopCreateDestroy) {\n  static constexpr size_t kNumThreads = 16;\n  folly::MuxIOThreadPoolExecutor ex(kNumThreads);\n}\n\nTEST(MuxIOThreadPoolExecutor, SingleEpollLoopRun) {\n  static constexpr size_t kNumThreads = 16;\n  static constexpr size_t kNumEventBases = 64;\n  static constexpr size_t kLoops = 10;\n  folly::MuxIOThreadPoolExecutor::Options options;\n  options.setNumEventBases(kNumEventBases);\n  folly::MuxIOThreadPoolExecutor ex(kNumThreads, options);\n\n  const auto evbs = ex.getAllEventBases();\n  EXPECT_EQ(ex.numEventBases(), kNumEventBases);\n  EXPECT_EQ(evbs.size(), kNumEventBases);\n\n  const auto testEvbs = [&] {\n    // Ensure that the poller gets to the waiting state.\n    /* sleep override */ std::this_thread::sleep_for(\n        std::chrono::milliseconds{100});\n\n    folly::Latch latch(kNumEventBases * kLoops);\n    for (size_t k = 0; k < kLoops; ++k) {\n      for (auto evb : evbs) {\n        evb->runInEventBaseThread([&]() { latch.count_down(); });\n      }\n    }\n    latch.wait();\n  };\n\n  testEvbs();\n\n  ex.setNumThreads(1);\n  EXPECT_EQ(ex.numThreads(), 1);\n  EXPECT_EQ(ex.numActiveThreads(), 1);\n  testEvbs();\n\n  ex.setNumThreads(kNumEventBases);\n  EXPECT_EQ(ex.numThreads(), kNumEventBases);\n  testEvbs();\n}\n\nTEST(MuxIOThreadPoolExecutor, SingleEpollLoopTimers) {\n  static constexpr size_t kNumThreads = 16;\n  static constexpr uint32_t kMilliseconds = 500;\n  folly::MuxIOThreadPoolExecutor ex(kNumThreads);\n\n  // Ensure that the poller gets to the waiting state.\n  /* sleep override */ std::this_thread::sleep_for(\n      std::chrono::milliseconds{100});\n\n  folly::Latch latch(kNumThreads);\n  for (auto evb : ex.getAllEventBases()) {\n    evb->runInEventBaseThread([evb, &latch]() {\n      evb->runAfterDelay([&latch]() { latch.count_down(); }, kMilliseconds);\n    });\n  }\n  latch.wait();\n}\n\nTEST(MuxIOThreadPoolExecutor, InvalidSetNumThreads) {\n  folly::MuxIOThreadPoolExecutor ex(16);\n  EXPECT_EQ(ex.numEventBases(), 16);\n  ex.setNumThreads(16); // No-op.\n  EXPECT_THROW(ex.setNumThreads(0), std::invalid_argument);\n  EXPECT_THROW(ex.setNumThreads(17), std::invalid_argument);\n\n  EXPECT_THROW(folly::MuxIOThreadPoolExecutor(0), std::invalid_argument);\n  folly::MuxIOThreadPoolExecutor::Options options;\n  options.setNumEventBases(1);\n  EXPECT_THROW(\n      folly::MuxIOThreadPoolExecutor(2, options), std::invalid_argument);\n}\n\nINSTANTIATE_TYPED_TEST_SUITE_P(\n    MuxIOThreadPoolExecutorTest,\n    IOThreadPoolExecutorBaseTest,\n    MuxIOThreadPoolExecutor);\n\n} // namespace test\n} // namespace folly\n#endif\n"
  },
  {
    "path": "folly/io/async/test/NotificationQueueBenchmark.cpp",
    "content": "/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <thread>\n\n#include <folly/Benchmark.h>\n#include <folly/io/async/EventBase.h>\n#include <folly/io/async/EventBaseAtomicNotificationQueue.h>\n#include <folly/io/async/NotificationQueue.h>\n#include <folly/synchronization/Baton.h>\n\nusing namespace folly;\n\nstatic size_t constexpr kMaxRead = 20;\nstatic size_t constexpr kProducerWarmup = 1000;\nstatic size_t constexpr kBusyLoopSize = 0;\n\nstruct FuncRunner {\n  void operator()(Func&& message) noexcept {\n    message();\n    message = nullptr;\n  }\n};\n\nclass MockConsumer : public NotificationQueue<Func>::Consumer {\n public:\n  void messageAvailable(Func&& message) noexcept override {\n    funcRunner_(std::move(message));\n  }\n\n private:\n  FuncRunner funcRunner_;\n};\n\nstruct AtomicNotificationQueueConsumerAdaptor {\n  void startConsuming(\n      EventBase* evb,\n      EventBaseAtomicNotificationQueue<Func, FuncRunner>* queue) {\n    queue->startConsuming(evb);\n  }\n};\n\nstatic void burn(size_t n) {\n  for (size_t i = 0; i < n; ++i) {\n    folly::doNotOptimizeAway(i);\n  }\n}\n\ntemplate <typename Queue, typename Consumer>\nvoid multiProducerMultiConsumer(\n    int iters, size_t numProducers, size_t numConsumers) {\n  BenchmarkSuspender susp;\n  Queue queue;\n  std::vector<std::unique_ptr<EventBase>> consumerEventBases;\n  std::vector<std::thread> consumerThreads;\n  // Initialize consumers\n  for (size_t i = 0; i < numConsumers; ++i) {\n    // construct base without time measurements\n    consumerEventBases.emplace_back(std::make_unique<EventBase>(false));\n    EventBase& base = *consumerEventBases.back();\n    consumerThreads.emplace_back([&base, &queue]() mutable {\n      base.setMaxReadAtOnce(kMaxRead);\n      Consumer consumer;\n      consumer.startConsuming(&base, &queue);\n      base.loopForever();\n    });\n  }\n\n  std::vector<std::thread> producerThreads;\n  std::atomic<size_t> producersWarmedUp{0};\n  // Needs to be less than 0 so that consumers don't reach 0 during warm up\n  std::atomic<ssize_t> itemsToProcess{-1};\n  std::atomic<bool> stop_producing{false};\n  Baton warmUpBaton;\n  Baton finishedBaton;\n  // Initialize producers and warm up both producers and consumers\n  // during warm up producers produce kProducerWarmup tasks each and consumers\n  // try to consume as much as possible\n  for (size_t i = 0; i < numProducers; ++i) {\n    producerThreads.emplace_back(\n        std::thread([numProducers,\n                     &warmUpBaton,\n                     &queue,\n                     &producersWarmedUp,\n                     &itemsToProcess,\n                     &stop_producing,\n                     &finishedBaton]() mutable {\n          size_t num_produced{0};\n          while (!stop_producing.load(std::memory_order_relaxed)) {\n            burn(kBusyLoopSize);\n            if (num_produced++ == kProducerWarmup &&\n                numProducers ==\n                    producersWarmedUp.fetch_add(1, std::memory_order_relaxed) +\n                        1) {\n              warmUpBaton.post();\n            }\n            queue.putMessage([&itemsToProcess, &finishedBaton]() {\n              burn(kBusyLoopSize);\n              if (itemsToProcess.fetch_sub(1, std::memory_order_relaxed) == 0) {\n                finishedBaton.post();\n              }\n            });\n          }\n        }));\n  }\n  warmUpBaton.wait();\n  susp.dismiss();\n  // This sets itemsToProcess to desired iterations. Consumers reduce it with\n  // every task. One which reduces it to 0 notifies via finishedBaton.\n  itemsToProcess.store(iters, std::memory_order_relaxed);\n  finishedBaton.wait();\n  susp.rehire();\n  // Stop producers\n  stop_producing.store(true, std::memory_order_relaxed);\n  for (auto& producerThread : producerThreads) {\n    producerThread.join();\n  }\n  // Stop consumers\n  for (auto& consumerEventBase : consumerEventBases) {\n    consumerEventBase->terminateLoopSoon();\n  }\n  for (auto& consumerThread : consumerThreads) {\n    consumerThread.join();\n  }\n}\n\nvoid multiProducerMultiConsumerNQ(\n    int iters, size_t numProducers, size_t numConsumers) {\n  multiProducerMultiConsumer<NotificationQueue<Func>, MockConsumer>(\n      iters, numProducers, numConsumers);\n}\n\nvoid multiProducerMultiConsumerANQ(\n    int iters, size_t numProducers, size_t numConsumers) {\n  CHECK(numConsumers == 1);\n  multiProducerMultiConsumer<\n      EventBaseAtomicNotificationQueue<Func, FuncRunner>,\n      AtomicNotificationQueueConsumerAdaptor>(\n      iters, numProducers, numConsumers);\n}\n\nBENCHMARK(EnqueueBenchmark, n) {\n  BenchmarkSuspender suspender;\n  NotificationQueue<Func> queue;\n  suspender.dismiss();\n  for (unsigned int i = 0; i < n; ++i) {\n    queue.putMessage(Func());\n  }\n  suspender.rehire();\n}\n\nBENCHMARK(DequeueBenchmark, n) {\n  BenchmarkSuspender suspender;\n  NotificationQueue<Func> queue;\n  EventBase base;\n  MockConsumer consumer;\n  consumer.setMaxReadAtOnce(kMaxRead);\n  consumer.startConsumingInternal(&base, &queue);\n  for (unsigned int i = 0; i < n; ++i) {\n    queue.putMessage([]() {});\n  }\n  suspender.dismiss();\n  for (unsigned int i = 0; i <= (n / kMaxRead); ++i) {\n    consumer.handlerReady(0);\n  }\n  suspender.rehire();\n  if (queue.size() > 0) {\n    throw std::logic_error(\"This should not happen batching might be broken\");\n  }\n  consumer.stopConsuming();\n}\n\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerANQ, _1p__1c, 1, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerANQ, _2p__1c, 2, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerANQ, _4p__1c, 4, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerANQ, _8p__1c, 8, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerANQ, 16p__1c, 16, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerANQ, 32p__1c, 32, 1)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _1p__1c, 1, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _2p__1c, 2, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _4p__1c, 4, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _8p__1c, 8, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 16p__1c, 16, 1)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 32p__1c, 32, 1)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _1p__2c, 1, 2)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _2p__2c, 2, 2)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _4p__2c, 4, 2)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _8p__2c, 8, 2)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 16p__2c, 16, 2)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 32p__2c, 32, 2)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _1p__4c, 1, 4)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _2p__4c, 2, 4)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _4p__4c, 4, 4)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _8p__4c, 8, 4)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 16p__4c, 16, 4)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 32p__4c, 32, 4)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _1p__8c, 1, 8)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _2p__8c, 2, 8)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _4p__8c, 4, 8)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _8p__8c, 8, 8)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 16p__8c, 16, 8)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 32p__8c, 32, 8)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _1p_16c, 1, 16)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _2p_16c, 2, 16)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _4p_16c, 4, 16)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _8p_16c, 8, 16)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 16p_16c, 16, 16)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 32p_16c, 32, 16)\nBENCHMARK_DRAW_LINE();\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _1p_32c, 1, 32)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _2p_32c, 2, 32)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _4p_32c, 4, 32)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, _8p_32c, 8, 32)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 16p_32c, 16, 32)\nBENCHMARK_NAMED_PARAM(multiProducerMultiConsumerNQ, 32p_32c, 32, 32)\nBENCHMARK_DRAW_LINE();\n\nint main(int argc, char* argv[]) {\n  folly::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  folly::runBenchmarks();\n\n  return 0;\n}\n"
  }
]